pax_global_header00006660000000000000000000000064147662355370014534gustar00rootroot0000000000000052 comment=67b7d63dc333c5d4fce5d92f125c1e0ce0e3514a casacore-3.7.1/000077500000000000000000000000001476623553700133245ustar00rootroot00000000000000casacore-3.7.1/.dockerignore000066400000000000000000000000331476623553700157740ustar00rootroot00000000000000cmake-build-*/ build/ .git casacore-3.7.1/.gitattributes000066400000000000000000000002361476623553700162200ustar00rootroot00000000000000# Explicitly declare text files you want to always be normalized and converted # to native line endings on checkout. *.txt text *.cc text *.tcc text *.h text casacore-3.7.1/.github/000077500000000000000000000000001476623553700146645ustar00rootroot00000000000000casacore-3.7.1/.github/workflows/000077500000000000000000000000001476623553700167215ustar00rootroot00000000000000casacore-3.7.1/.github/workflows/docker.yml000066400000000000000000000035521476623553700207200ustar00rootroot00000000000000name: Publish Docker images on: push: branches: - 'master' tags: - 'v*' pull_request: branches: - 'master' env: REGISTRY: quay.io ORGANIZATION: casacore IMAGE: ${{ github.repository }} jobs: build_images: runs-on: ubuntu-latest strategy: matrix: python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] steps: - name: Checkout repository uses: actions/checkout@v4 - name: Determine build-args id: args run: | py_major=$(echo ${{ matrix.python-version }} | cut -d'.' -f1) py_minor=$(echo ${{ matrix.python-version }} | cut -d'.' -f2) echo "PYMAJOR=${py_major}" >> "$GITHUB_OUTPUT" echo "PYMINOR=${py_minor}" >> "$GITHUB_OUTPUT" - name: Extract metadata (tags, labels) for Docker id: meta uses: docker/metadata-action@v5 with: flavor: prefix=py${{ steps.args.outputs.PYMAJOR }}${{ steps.args.outputs.PYMINOR }}_ images: ${{ env.REGISTRY }}/${{ env.IMAGE }} - name: Login to registry id: login uses: docker/login-action@v3 if: github.event_name != 'pull_request' with: # Credentials for robot account "github" on Quay for organization "casacore" registry: ${{ env.REGISTRY }} username: ${{ secrets.QUAY_USERNAME }} password: ${{ secrets.QUAY_PASSWORD }} - name: Build and push docker image id: build-push uses: docker/build-push-action@v5 with: build-args: | PYMAJOR=${{ steps.args.outputs.PYMAJOR }} PYMINOR=${{ steps.args.outputs.PYMINOR }} file: docker/py_wheel.docker push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} casacore-3.7.1/.github/workflows/linux.yml000066400000000000000000000012171476623553700206040ustar00rootroot00000000000000name: Linux on: push: branches: [ master ] pull_request: branches: [ master ] jobs: Linux: runs-on: ubuntu-20.04 strategy: matrix: dist: - ubuntu2004_clang - ubuntu2004_gcc - ubuntu2204_clang - ubuntu2204_gcc - ubuntu2404_clang - ubuntu2404_gcc steps: - name: Checkout uses: actions/checkout@v2 - name: Build container run: docker build . -t ${{ matrix.dist }} -f docker/${{ matrix.dist }}.docker - name: Test run: docker run --rm ${{ matrix.dist }} /bin/bash -c 'cd /code/build && ctest --output-on-failure' casacore-3.7.1/.github/workflows/osx.yml000066400000000000000000000042741476623553700202640ustar00rootroot00000000000000name: macOS on: push: branches: [master] tags: ["*"] pull_request: branches: [master] jobs: osx: runs-on: ${{matrix.os}} strategy: fail-fast: false matrix: os: - macos-13 # x68 - macos-14 # arm64 steps: - name: checkout uses: actions/checkout@v4 - name: install dependencies, set python environment run: | # Delete GitHub Python, See https://github.com/orgs/Homebrew/discussions/3895#discussioncomment-4130560 find /usr/local/bin -lname '*/Library/Frameworks/Python.framework/*' -print -delete brew install fftw hdf5 boost-python3 numpy cfitsio wcslib gsl ninja gcc # ensure we use homebrew python and gcc export PATH=$(brew --prefix python)/bin:${PATH} export PATH=$(brew --prefix gcc)/bin:${PATH} echo PATH=$PATH | tee -a $GITHUB_ENV - name: set up build and install dir with WSRT measures run: | wget ftp://ftp.astron.nl/outgoing/Measures/WSRT_Measures.ztar -O WSRT_Measures.ztar mkdir -p build install cd build tar zxvf ../WSRT_Measures.ztar rm ../WSRT_Measures.ztar - name: configure run: | cd build cmake \ -G Ninja \ -DCMAKE_Fortran_COMPILER=$(which gfortran) \ -DCMAKE_PREFIX_PATH=/usr/local/ \ -DBoost_NO_BOOST_CMAKE=True \ -DBUILD_TESTING=ON \ -DBUILD_PYTHON=OFF \ -DBUILD_PYTHON3=ON \ -DPython3_EXECUTABLE=$(which python3) \ -DUSE_HDF5=ON \ -DUSE_OPENMP=OFF \ -DDATA_DIR=. \ -DCMAKE_INSTALL_PREFIX=../install \ .. - name: make run: cmake --build build - name: run tests run: | cd build export ctest_args="" # disable tests on amd64 https://github.com/casacore/casacore/issues/1352 if [[ "$(uname -m)" == "arm64" ]]; then export ctest_args="-E tLSQaips|tLSQFit" fi CTEST_OUTPUT_ON_FAILURE=1 ctest $ctest_args - name: install run: | cmake --build build --target install casacore-3.7.1/.gitignore000066400000000000000000000002031476623553700153070ustar00rootroot00000000000000doc/html/ build/ *~ *table.lock *CMakeCache.txt *CMakeFiles .project .cproject .settings .vscode WSRT_Measures.ztar sofa.tgz sofa/ casacore-3.7.1/CHANGES.md000066400000000000000000000272501476623553700147240ustar00rootroot00000000000000# 3.7.1 - Fix an issue in 'Stokes I Storage manager' when used on boolean columns (flag column): #1407 - Fix a bug in Dysco when antenna indices are swapped: #1406 # 3.7.0 ## Highlights - Add new storage managers which losslessly compress data #1385, #1394, #1395, #1398, #1399, #1396, #1397, #1400 - Improvements to FITS-IDI / MSFits conversions #1370, #1373, #1384, #1386 - Switch to C++17 and use some of its features #1346 # # General - Solved various issues with newer compilers and boost versions #1365, #1374, #1377, #1379, #1401 - Fixed memory issues #1391, #1402 - Build system improvements #1368, #1357, #1375, #1376 # 3.6.1 - Use the latest numpy for dockers; this should make python-casacore compatible with numpy 2.0. # 3.6.0 ## General - Improvements on FITS import and export (#1215, #1229, #1251, #1267, #1268, #1304, #1318, #1341) - Small improvements to Dysco (#1305, #1313, #1337, #1343) - C++ modernizations (#1185, #1199, #1271, #1280, #1284, #1288, #1289, #1294, #1296, #1298, #1309, #1316, #1353, #1355) - Build system and CI improvements (#1219, #1236, #1252, #1315, #1328, #1330, #1334, #1335, #1349) - Code cleanup (#1204, #1245, #1269, #1277, #1279, #1300, #1301, #1302) - TaQL improvements (#1221, #1232, #1244, #1247, #1259, #1260, #1263, #1270, #1276, #1299) - Add new SI prefixes quetta, ronna, ronto and quecto (#1257) - Fix several issues that arose in CASA use (#1200, #1227, #1246, #1262, #1290, #1291, #1292, #1329, #1323) - Fix data race in images toWord / toPixel (#1235) - Update e-mail address (#1339) # 3.5.0 ## Highlights Apart from many bug fixes and modernizations, a few new features were introduced: - TaQL now supports copying columns to new columns (e.g. to make a backup of the `FLAG` column) - Non-zero coordinates in JPL frames, e.g. the `SUN` frame, are now interpreted as an offset in right ascension and declination. Previously, coordinates in a JPL coordinate frame were ignored. - The Dysco storage manager is now built by default as part of casacore. ## General - Improve continuous integration (#1180, #1182), move from Travis CI to Github Actions (#1086, #1097, #1098, #1161) - Modernizations: remove Mutex (#1095, #1127, #1128), typing system (#1172, #1176) - Allow building with newer compilers (#1134, #1137, #1145, #1206, #1208, #1210, #1211) - Build python3 by default, not python2 (#1209) ## MeasurementSet - Improve reading of DataDescriptionId (#1103) - Compute feed information (#1104) and field attributes (#1109) on demand - UVFits improvements: support X-Y mounts (#1115), allow large antenna numbers (#1144), fix handling FITS-IDI `GAIN_CURVE` (#1151) - Fix precision in `T+dT` syntax (#1118) ## Measures - Allow offsets to JPL coordinates (#1160) ## Tables - Cache iteration boundaries while sorting tables (#1106) - Use RefTable for iteration (#1108) - Adios2 related changes: #1110, #1116, #1121, #1148 - Make Dysco part of casacore (#1117, #1125, #1146, #1166) - Add TaQL commands `LIKE`, `COPYCOLUMN` and `DROPTABLE` (#1154, #1169) - Fix one bug in `rownr_t` migration for TiledDataStMan (#1156) - Improve documentation (#1192) ## Images - Support CARTA opening images with a custom handler (#1158) - Allow easier python handling of beams (#1184) # 3.4.0 ## General - Major modernization of Arrays, bringing it more in line with C++11 (#1012) - Use std::regex (#1072) - Remove many warnings (#1024, #1036, #1062) - Use FFTW as default FFT provider (#1029, #1047, #1049) - UVFITS improvements (#1033, #1040, #1064, #1066, #1067) - Support for 64-bit row numbers. # 3.3.0 ## General - Replace implementation of complex trig functions by std:: (#1010) - Fix bug SciMath / StatsFramework where parameters were passed incorrectly (#984) ## Tables - Pass sorting algorithm optio nthrough to table iterator (#992) - Support for ADIOS2 (#1006) - Add .casarc option to disable table locking (#1002) ## Measurement Set / UVFits - Fix antenna swapping in MSConcat (#977) - Merge PROCESSOR table in MSConcat (#1004) - Fix antenna positions in importing old VLA UVFITS (#1005) ## Images - More efficient WCS batch coordinate lookups (#932) # 3.2.1 - Increment the SO version, otherwise identical to 3.2 (#981) # 3.2 ## General - Remove some unused functionality (#854, #947) - Prevent setting OpenMP num_threads to zero (#962) ## Tables - Fix locking issue with IncrementalStorageManager (#970, #974) - Various improvements to TaQL - Various ADIOS2 improvements (#943, #966) ## Measures - Update hardcoded URLs for fetching measures data (#975) ## FITS - Correctly convert Nasmyth mount types (#946, #954) # 3.1.2 - Bump version in version.h (since in 3.1.1 it was still at '3.1.0') # 3.1.1 ## General - Only use DataManager.get/putSlice if possible (#901) - User docker for Linux build on Travis for CI (#856) - improve unicode support (#853) - Changed MadfmFunc so that it inherits from ArrayFunctorBase (#905) - Fix memory leak in TableProxy::getColumnDescription (#900) - Allow colon and fix recursive bison parsing (#894) - ObjectID's hash function behavior fix (#897) - Add a public getter for the mask array to LCRegionFixed (#895) - Replace STL-like containers by their STL counterparts (#890) ## Python support - Boost 1.67 Python components require a Python version suffix (#844) - Handle numpy Unicode arrays (#912) - Fix memory leak when converting Python unicode to casa string (#910) - Possible leak bug (#908) - Always use TpInt64 for PyLong (#916) - Improve CMake FindPython (#922) ## Build system - Travis macOS fails on Python3 OSX build system (#778) - Switch travis to docker /xenial (#847) # 3.1 ## General - Removed many warnings issued by newer compiler versions (#798, #809, #819, #820, #866, #883) - Further improved thread-safety (#817, #869, #877, #886) - Added variance and standard deviation of complex numbers (#851) ## Tables - Improved parallel storage manager based on ADIOS2 - Support of Int64 table columns (#859) - Added O_DIRECT support when using MultiFile (#885) - TaQL improvements (#813, #851, #867) - Small CTDS fixes (#840, #868) ## Measurement Set - Small improvements (#858, #864, #872) - Better handling of FITS-IDI data bit sampling (#836) ## Images - Support for non-chunked HDF5 data sets (#879, #880) - Fixed count overflow on very large images (#849) # 3.0 ## General - Building with C++11 is now required - Improved thread-safety of statics (#775) - Improve statistics code (#785, #776, #662, #663) ## Tables - Avoid growing the size of tables when overwriting data (#768) - TaQL support for radial velocity, earth magnetic field and Doppler (#750) - Added performance tests for tables (#658) ## Measurement Set - Improvements to MSConcat (#779, #701) - Better handling of FITS-IDI flag history (#748) ## Images - Better support for double precision images (#752, #751) ## Other - A full list of changes can be found on the [issue tracker](https://github.com/casacore/casacore/milestone/8?closed=1). # 2.4.1 ## General - This release fixes a bug where TableIterator would skip a row # 2.4.0 ## General - Bug fixes and improvements - This version can be used to compile CASA 5.0 - Improved error checking when parsing dates/times (#619) - Statistics: allow data provider to specify number of threads (#645) ## MS - Disable caching of MS main table columns (#597) - Properly copy the `SIGMA_SPECTRUM` column, if present (#599) - Implement getTimesForSpws (#600) ## FITS and FITS-IDI - Implement digital corrections for DiFX/VLBA (#602) - Several improvements for WEIGHTS when reading FITS-IDI (#590, #608) - Parallactic angle calculation for Nasmyth mounts (#627) - Modernised matrix syntax in FITS files now follows standard 3.0 (#606) # 2.3.0 ## General - Bug fixes and improvements - Improved installation documentation in README - Add an option to use Ccache (`-DUseCcache`) (#549) - Add some statistics functionality (#569) ## Python - Make some of the TableProxy functionality publicly available (#559) - Make version checking from python (or plain C) possible (#583) ## TaQL - Fix transpose in TaQL (#563) - Added functions `delay` and `uvwapp` to `mscal` (#562) ## FITS - MSFitsOutput now writes ant diams (#536) - Improvements to FITS-IDI conversion (#538, #590, #579) # 2.2.0 ## General - Lots of bug fixes and improvements - Tests are not built by default anymore, use `-DBUILD_TESTING=True` to build them - Building with C++11 is now default, use `-DCXX11=False` if you do not have a recent compiler (#533) - Added JSON support (#506) ## TaQL - Major improvements in TaQL, such as masked arrays, new commands `ALTER TABLE`, `SHOW TABLE`, `HELP` (#388) ## Images - ImageConcat and ImageExpr now use JSON export (#517) # 2.1.0 ## General - Lots of code improvement, optimization and added tests ## Measures - The default search path for measures data is now smaller. The path can be set at cmake time by specifying `-DDATA_DIR=/path/to/data`. This path can contain `%CASAROOT%` which is expanded at run time, to support relocatable installations. The measures path can also still be set in `.casarc`. (#277) - Inserted fix to leap second handling problem (#290) - Various coordinate performance improvements (#258) ## Tables - Renamed showtable to showtableinfo (#320) - Added several TaQL functions (#229) ## MS - Improve testing of MSSummary (#330) - Many improvements for the MS related functions (#318, #291, #228, #208, ...) - Fix multithreaded MS creation (#298) ## Python - Build python bindings by default (not for CASA build) - Add experimental Python3 support. Read the README for instructions (#280) - Changed behavior from 2.0.3: Use PYTHON2_* configuration options to set your python2 interpreter. (#280) # 2.0.3 ## General - Merge of CASA work into casacore, CASA should compile with casacore now - Under-the-hood optimizations, like using allocator features (#132) - Building with gcc 5 is now possible (#166) - Compatibility with more versions of wcslib and cfitsio - SOFA is now an external dependency (#105) ## Tables - Arrays can now sometimes be reshaped while keeping allocated memory (#113) ## MS - Selection of baselines with a regexp between stations is now possible (#99) - Several new methods to MSMetaData (#138) ## Bug fixes - Fix a bug which caused an error with LOFAR measurement sets with the LOFAR tool msoverview (#140) - Fix a bug where TaQL would not write output when an expression was used (#184) # 2.0.2 This version was not released # 2.0.1 changes since 2.0.0 ## Bug fixes - Does not build if checkout root folder is not named casacore (#79) # 2.0.0 changes since 1.7.0 ## General - Fixed all build problems for GCC and clang on various platforms (up to OS-X 10.10) - Fixed all valgrind issues found in test programs - Made the statics in all of casacore thread-safe. Similar to libstdc++ the user is responsible for thread-safety when using casacore ojbects. - Added casacore/ to the #include path of all header files - Changed namespace from casa to casacore (but still #defined as casa for time being) - Split Tables, MeasurementSets and Lattices into smaller parts - The 3 changes above give rise to backward incompatibilities. Scripts in casacore/changescripts (notably updateall) can be used to fix client code. ## Tables - Added GROUPBY/HAVING/aggregation function to TaQL - Optimized TaQL's IN operator (linear time for integer sets) - Added table tracing (class TableTrace) - Fixed TiledStMan issue where BucketCache was not shrinked - Added MultiFile option to combine table files in a single one ## Components - This module has been removed (too CASA specific) ## Images - Added support for beam per frequency channel and Stokes - Added persistency for ImageExpr and ImageCocat objects ## Python - Moved converters from pyrap to casacore/Python casacore-3.7.1/CMakeLists.txt000066400000000000000000000626501476623553700160750ustar00rootroot00000000000000cmake_minimum_required (VERSION 3.14.0) project(casacore VERSION 3.7.1) include(CheckCXXCompilerFlag) include(CheckCCompilerFlag) include(CheckFunctionExists) include(CheckCXXSourceCompiles) include(cmake/PCHSupport.cmake) # Fixes warnings on cmake 3.x+ about missing extensions if (POLICY CMP0115) cmake_policy (SET CMP0115 NEW) endif() # Enable _ROOT variables for recent versions of CMake if (POLICY CMP0074) cmake_policy (SET CMP0074 NEW) endif() # Use LOCATION as Python find strategy. if (POLICY CMP0094) cmake_policy (SET CMP0094 NEW) endif() # Increment the PROJECT_SOVERSION every time you update VERSION_MINOR! SET(PROJECT_SOVERSION 8) SET(NO_SOVERSION FALSE CACHE BOOL "do not add version information to shared libraries") set (CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") set(CPACK_GENERATOR "DEB") set(CPACK_PACKAGE_NAME "casacore") set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}") set(CPACK_PACKAGE_CONTACT "Malte Marquarding") #required set(CPACK_PACKAGE_VENDOR "https://github.com/casacore/casacore") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Astronomical data processing library") set (CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") # By default build only Python3 bindings option (BUILD_PYTHON "Build the python2 bindings" NO) option (BUILD_PYTHON3 "Build the python3 bindings" YES) option (BUILD_DEPRECATED "build and install deprecated classes (such as Map)" NO) option (BUILD_FFTPACK_DEPRECATED "build FFTPack" NO) option (BUILD_DYSCO "Build the Dysco compression storage manager" YES) # By default build shared libraries option (ENABLE_SHARED "Build shared libraries" YES) option (ENABLE_RPATH "Include rpath in executables and shared libraries" YES) # Check if O_DIRECT is available. # Note this does not work well with check_c_source_compiles. check_cxx_source_compiles(" #include main() { int i = O_DIRECT; } " HAVE_O_DIRECT) if (HAVE_O_DIRECT) add_definitions(-DHAVE_O_DIRECT) endif() # By default do not use ADIOS2, HDF5 option (ENABLE_TABLELOCKING "Make locking for concurrent table access possible" YES) option (USE_READLINE "Build readline support" YES) option (USE_ADIOS2 "Build ADIOS2 " NO) option (USE_HDF5 "Build HDF5 " NO) option (USE_THREADS "Use Mutex thread synchronization" YES) option (USE_OPENMP "Use OpenMP threading" NO) option (USE_MPI "Use MPI for parallel IO" NO) option (USE_STACKTRACE "Show stacktrace in case of exception" NO) option (CASA_BUILD "Building in the CASA (http://casa.nrao.edu) environment" NO) set(CASA_DEFAULT_ALIGNMENT "32" CACHE STRING "Default alignment of casa::AlignedAllocator") # ccache use is optional option( UseCcache OFF ) if (UseCcache) message (STATUS "Searching for ccache.") find_program(CCACHE_FOUND ccache) if(CCACHE_FOUND) message (STATUS "Ccache found.") set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache) endif(CCACHE_FOUND) endif() # basic setup for building within CASA environment if( CASA_BUILD ) if( NOT DATA_DIR ) set(DATA_DIR "%CASAROOT%/data") endif( ) set(USE_OPENMP ON) set(USE_THREADS ON) set(BUILD_FFTPACK_DEPRECATED ON) set(BUILD_PYTHON OFF) set(Boost_NO_BOOST_CMAKE 1) if (EXISTS "/opt/casa/02/include/python2.7") ### RHEL7 set(PYTHON_LIBRARY "/opt/casa/02/lib/libpython2.7.so") set(PYTHON_INCLUDE_DIR "/opt/casa/02/include/python2.7") set(NUMPY_INCLUDE_DIRS "/opt/casa/02/lib/python2.7/site-packages/numpy/core/include") set(PYTHON_EXECUTABLE:FILEPATH "/opt/casa/02/bin/python") if (EXISTS "/usr/include/boost") set(BOOST_ROOT "/usr") endif( ) set(WCSLIB_INCLUDE_DIR "/opt/casa/02/include") set(WCSLIB_LIBRARY "/opt/casa/02/lib/libwcs.so") set(PYTHON2_NUMPY_INCLUDE_DIRS "/opt/casa/02/lib/python2.7/site-packages/numpy/core/include") elseif (EXISTS "/opt/casa/01/include/python2.7") ### RHEL7 set(PYTHON_LIBRARY "/opt/casa/01/lib/libpython2.7.so") set(PYTHON_INCLUDE_DIR "/opt/casa/01/include/python2.7") set(PYTHON_EXECUTABLE:FILEPATH "/opt/casa/01/bin/python") if (EXISTS "/usr/include/boost") set(BOOST_ROOT "/usr") endif( ) elseif(EXISTS "/usr/lib64/casa/01/include/python2.7") ### RHEL5/RHEL6 set(PYTHON_LIBRARY "/usr/lib64/casa/01/lib/libpython2.7.so") set(PYTHON_INCLUDE_DIR "/usr/lib64/casa/01/include/python2.7") set(PYTHON_EXECUTABLE:FILEPATH "/usr/lib64/casa/01/bin/python") if (EXISTS "/usr/lib64/casa/01/include/boost") ### RHEL5 set(BOOST_ROOT "/usr/lib64/casa/01") elseif (EXISTS "/usr/include/boost") ### RHEL6 set(BOOST_ROOT="/usr") endif( ) endif( ) endif( ) # Test if shared libraries have to be built. if (ENABLE_SHARED) option (BUILD_SHARED_LIBS "" YES) if (ENABLE_RPATH) # Set RPATH to use for installed targets; append linker search path set(CMAKE_INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/lib" ) set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) set(CMAKE_MACOSX_RPATH TRUE) endif (ENABLE_RPATH) if( CASA_BUILD ) option(NO_SOVERSION "Build shared libraries without version information" NO) if( NOT NO_SOVERSION ) set( epochdelta 1385614800 ) execute_process( COMMAND perl -e "$t=time( )-${epochdelta};$z=$t & 0xff; $y=($t>>8)&0xff; $x=($t>>16)&0xffff; print \"$x.$y.$z\"" OUTPUT_VARIABLE __casa_soversion ) set(casa_soversion ${__casa_soversion} CACHE STRING "version for shared objects") message( STATUS "Shared object version number ${casa_soversion}" ) file( WRITE ${CMAKE_INSTALL_PREFIX}/casa_sover.txt "# generated by casacore/CMakeList.txt... Do not edit\n" "${casa_soversion}\n" ) else( ) message( STATUS "User disabled shared library versioning" ) endif( ) endif( ) else() option (BUILD_SHARED_LIBS "" NO) endif (ENABLE_SHARED) if (UseCasaNamespace) add_definitions (-DUseCasaNamespace) message (STATUS "Using namespace casa. This will be deprecated at some point.") else () message (STATUS "Using namespace casacore.") endif () # Define the compiler flags to be used. # Note: -Wshadow and -Wunreachable-code give (too) many warnings. # Casacore uses longlong, so no warnings for it. # Clang gives warning on bison generated code; disable unneeded-internal-declaration. if (NOT CMAKE_CXX_FLAGS) set (CMAKE_CXX_FLAGS "-Wextra -Wall -W -Wpointer-arith -Woverloaded-virtual -Wwrite-strings -pedantic -Wno-long-long") #SET(CMAKE_CXX_FLAGS="-g -O0 -Wall -Wextra -Wshadow -Wunused-variable # -Wunused-parameter -Wunused-function -Wunused -Wno-system-headers # -Wno-deprecated -Woverloaded-virtual -Wwrite-strings -fprofile-arcs # -ftest-coverage -Wold-style-cast -Weffc++ -Wconversion") if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unneeded-internal-declaration") endif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") endif (NOT CMAKE_CXX_FLAGS) # Set build type if not given. if (NOT CMAKE_BUILD_TYPE) # Use debug mode if building in dbg or debug directory. get_filename_component(_cmpvar ${CMAKE_BINARY_DIR} NAME) if(_cmpvar STREQUAL "dbg" OR _cmpvar STREQUAL "debug") set (CMAKE_BUILD_TYPE Debug) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DAIPS_DEBUG") else() if(_cmpvar STREQUAL "cov") set (CMAKE_BUILD_TYPE Debug) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 --coverage") set(CMAKE_LD_FLAGS "${CMAKE_LD_FLAGS} --coverage") else() set (CMAKE_BUILD_TYPE Release) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNDEBUG") endif(_cmpvar STREQUAL "cov") endif(_cmpvar STREQUAL "dbg" OR _cmpvar STREQUAL "debug") endif (NOT CMAKE_BUILD_TYPE) # Require a C++17 compatible compiler set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # Ensure clang is not complaining about unused arguments. if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") set(CMAKE_CXX_FLAGS "-Qunused-arguments ${CMAKE_CXX_FLAGS}") set(CMAKE_C_FLAGS "-Qunused-arguments ${CMAKE_C_FLAGS}") endif() # Since 2015, we need pread and pwrite (POSIX 2001) check_function_exists(pread HAVE_PREAD) check_function_exists(pwrite HAVE_PWRITE) if (NOT (HAVE_PREAD AND HAVE_PWRITE)) message(FATAL_ERROR "Casacore requires pread and pwrite functionality" ) endif (NOT (HAVE_PREAD AND HAVE_PWRITE)) # Since 2020, we need GCC 4.9 for regex support # Additionally we need cmake 2.8.10 for CMAKE_CXX_COMPILER_VERSION if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9) message(FATAL_ERROR "Casacore requires at least gcc-4.9") endif() # FindHDF5 uses environment variables, so set it if needed. if (HDF5_ROOT_DIR) set (ENV{HDF5_ROOT} ${HDF5_ROOT_DIR}) endif (HDF5_ROOT_DIR) # Find out which modules to build. if (NOT MODULE) set (MODULE all) endif (NOT MODULE) set (_usebison NO) set (_uselapack NO) set (_usefortran NO) set (_usefits NO) set (_usewcs NO) set (_modules casa) set (_modules2 ) if (NOT ${MODULE} STREQUAL "casa") set (_modules ${_modules} tables) set (_usebison YES) if (NOT ${MODULE} STREQUAL "tables") set (_modules ${_modules} scimath_f scimath measures meas) set (_uselapack YES) set (_usefortran YES) if (NOT ${MODULE} STREQUAL "measures") if (${MODULE} STREQUAL "ms") set (_modules ${_modules} ms derivedmscal) endif() if (${MODULE} STREQUAL "msfits" OR ${MODULE} STREQUAL "all") set (_modules2 ${_modules2} ms msfits derivedmscal) set (_usefits YES) endif() if (${MODULE} STREQUAL "images" OR ${MODULE} STREQUAL "all") set (_modules2 ${_modules2} lattices mirlib coordinates images) set (_usewcs YES) set (_usefits YES) endif() endif() endif() endif() if (_usefits) set (_modules ${_modules} fits) endif() set (_modules ${_modules} ${_modules2}) if (BUILD_PYTHON) set (_modules ${_modules} python) endif (BUILD_PYTHON) if (BUILD_PYTHON3) set (_modules ${_modules} python3) endif (BUILD_PYTHON3) # Determine which external packages to use. include (CTest) if (_usefortran) enable_language (Fortran) # use faster fortran rules for complex operations, removes restoring complex # infinities if naive computation results in NAN + NAN * I #Handling complex multiplication and division with correct treating of complex #infinities (one element Inf regardless of the other) according to the C is #complicated. #E.g. a = NaN + 1e30 i; a * a is not NaN but a complex infinity (-Inf - NaN). # #Treating this situation correctly has large performance impact. In GCC's #implementation it is about 4 times slower than the naive implementation, with #vectorization enabled the impact is even larger. #As correct treatment of complex infinities when NaN appear in results is seldom #accounted for, or not required and most other languages do not have these #rules, the correct treatmeant can be disabled with the -fcx-fortran-rules flag. #This changes the semantics to those of the FORTRAN language which is removes #the need for rescuing the result when NaN appear. Python also follows FORTRAN #rules. #Additionally the correct behavior is not implemented in all compilers, #most notably clang which is the default compiler on MacOS. So turning off #correct treatment with GCC does not only make our code faster but also behave #the same on more compilers. # #This has measurable impact on e.g. applycal where the cpu performance improves #by about 20%. # For one reason or another the check on the compiler flag has no # effect; it still adds the option which fails for clang. if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") check_cxx_compiler_flag(-fcx-fortran-rules HAS_GXX_FORTRAN_RULES) check_c_compiler_flag(-fcx-fortran-rules HAS_GCC_FORTRAN_RULES) # added before cmake flags so it can be disabled with # -fno-cx-fortran-rules for testing if (HAS_GXX_FORTRAN_RULES) set(CMAKE_CXX_FLAGS "-fcx-fortran-rules ${CMAKE_CXX_FLAGS}") endif() if (HAS_GCC_FORTRAN_RULES) set(CMAKE_C_FLAGS "-fcx-fortran-rules ${CMAKE_C_FLAGS}") endif() endif() endif() find_package (DL) if (USE_READLINE) find_package (Readline REQUIRED) endif (USE_READLINE) if (BUILD_TESTING) find_package (SOFA) endif() if (USE_ADIOS2) find_package (ADIOS2 2.6.0 CONFIG REQUIRED) if(NOT USE_MPI) set(CASACORE_ADIOS_LIBRARY adios2::cxx11) elseif (NOT ADIOS2_HAVE_MPI) message(FATAL_ERROR "ADIOS2 found without MPI support but MPI support requested") else() set(CASACORE_ADIOS_LIBRARY adios2::cxx11_mpi) endif() endif (USE_ADIOS2) if (USE_HDF5) find_package (HDF5 REQUIRED) endif (USE_HDF5) if (_usebison STREQUAL YES) find_package (FLEX REQUIRED) find_package (BISON REQUIRED) endif (_usebison STREQUAL YES) if (_uselapack STREQUAL YES) find_package (BLAS REQUIRED) find_package (LAPACK REQUIRED) if (FFTW3_DISABLE_THREADS) find_package (FFTW3 COMPONENTS single double REQUIRED) else() find_package (FFTW3 COMPONENTS single double threads REQUIRED) endif (FFTW3_DISABLE_THREADS) endif (_uselapack STREQUAL YES) if (_usefits STREQUAL YES) find_package (CFITSIO 3.030 REQUIRED) # Should pad to three decimal digits endif (_usefits STREQUAL YES) if (_usewcs STREQUAL YES) # find_package (WCSLIB 4.20 REQUIRED) find_package (WCSLIB 4.7 REQUIRED) # needed for CASA if (WCSLIB_VERSION_STRING STREQUAL "5.14") # WCSlib 5.14 throws segfaults on lots of tests, e.g. tTempImage message(WARNING "Casacore is not compatible with wcslib 5.14, see issue gh-384.") endif (WCSLIB_VERSION_STRING STREQUAL "5.14") endif (_usewcs STREQUAL YES) # Set the include directories and HAVE compiler variables include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR}) if (BUILD_DYSCO) add_definitions(-DHAVE_DYSCO) endif (BUILD_DYSCO) if (WCSLIB_FOUND) include_directories (${WCSLIB_INCLUDE_DIRS}) add_definitions (-DWCSLIB_VERSION_MAJOR=${WCSLIB_VERSION_MAJOR} -DWCSLIB_VERSION_MINOR=${WCSLIB_VERSION_MINOR}) endif (WCSLIB_FOUND) if (CFITSIO_FOUND) include_directories (${CFITSIO_INCLUDE_DIRS}) add_definitions (-DCFITSIO_VERSION_MAJOR=${CFITSIO_VERSION_MAJOR} -DCFITSIO_VERSION_MINOR=${CFITSIO_VERSION_MINOR}) endif (CFITSIO_FOUND) if (Boost_FOUND) include_directories (${Boost_INCLUDE_DIRS}) add_definitions(-DHAVE_BOOST) endif (Boost_FOUND) if (ADIOS2_FOUND) include_directories (${ADIOS2_INCLUDE_DIRS}) add_definitions(-DHAVE_ADIOS2) endif (ADIOS2_FOUND) if (HDF5_FOUND) # Use BEFORE to achieve that HDF5 is not taken from /usr/local/include ##include_directories (BEFORE ${HDF5_INCLUDE_DIRS}) include_directories (${HDF5_INCLUDE_DIRS}) add_definitions(-DHAVE_HDF5) endif (HDF5_FOUND) include_directories (${FFTW3_INCLUDE_DIRS}) add_definitions(-DHAVE_FFTW3) if (NOT FFTW3_DISABLE_THREADS) add_definitions(-DHAVE_FFTW3_THREADS) endif (NOT FFTW3_DISABLE_THREADS) if (DL_FOUND) add_definitions(-DHAVE_DL) endif (DL_FOUND) if (READLINE_FOUND) add_definitions(-DHAVE_READLINE) endif (READLINE_FOUND) if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel") ## setting intel libraries with e.g. ## ## find_library( INTEL_IRNG irng HINTS ${INTEL_PATH} ) ## ## causes CMAKE to substitute fully qualified paths which makes ## python shared object unrelocatable in the case of libirng.so ## get_filename_component(INTEL_PATH ${CMAKE_CXX_COMPILER} DIRECTORY) set(INTEL_LIB_PATH ${INTEL_PATH}/../lib/intel64) set(CASACORE_ARCH_LIBS ${CASACORE_ARCH_LIBS} -L${INTEL_LIB_PATH} -limf -lsvml -lirng -lintlc -lifport -lifcore -liomp5) endif() if(USE_MPI) find_package(MPI COMPONENTS C REQUIRED) include_directories (${MPI_C_INCLUDE_PATH}) add_definitions(-DOMPI_SKIP_MPICXX -DMPICH_SKIP_MPICXX) add_definitions(-DHAVE_MPI) set(ENABLE_TABLELOCKING OFF) set(CASACORE_MPI_LIBRARY ${MPI_C_LIBRARIES}) message (STATUS "ENABLE_TABLELOCKING is turned off as required by MPI") endif(USE_MPI) if(USE_OPENMP) set (USE_THREADS YES) find_package (OpenMP) if (OPENMP_FOUND) if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -qopenmp") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -qopenmp") else( ) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") endif( ) else (OPENMP_FOUND) message(WARNING "Cannot fullfill USE_OPENMP, compiler does not support it") endif (OPENMP_FOUND) endif() # Thread support? if(USE_THREADS) set(_errmsg "FIXME: Don't know how to enable thread support for ") find_package(Pthreads REQUIRED) add_definitions(-DUSE_THREADS) if(CMAKE_COMPILER_IS_GNUCC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread") elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "Intel") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread") else() if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread") else() message(FATAL_ERROR "${_errmsg} (${CMAKE_C_COMPILER_ID}): ${CMAKE_C_COMPILER}") endif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") endif(CMAKE_COMPILER_IS_GNUCC) if(CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") else() if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") else() message(FATAL_ERROR "${_errmsg} (${CMAKE_C_COMPILER_ID}): ${CMAKE_CXX_COMPILER}") endif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") endif(CMAKE_COMPILER_IS_GNUCXX) else() # Compile with -fno-threadsafe-statics if supp to reduce some thread safety overhead. check_cxx_compiler_flag(-fno-threadsafe-statics HAS_CXX_NO_THREADSAFE_STATICS) if (HAS_CXX_NO_THREADSAFE_STATICS) set(CMAKE_CXX_FLAGS "-fno-threadsafe-statics ${CMAKE_CXX_FLAGS}") endif() check_c_compiler_flag(-fno-threadsafe-statics HAS_C_NO_THREADSAFE_STATICS) if (HAS_C_NO_THREADSAFE_STATICS) set(CMAKE_C_FLAGS "-fno-threadsafe-statics ${CMAKE_C_FLAGS}") endif() endif(USE_THREADS) # Set default DATA_DIR if undefined. if (NOT DATA_DIR) set (DATA_DIR ${CMAKE_INSTALL_PREFIX}/share/casacore/data) endif (NOT DATA_DIR) # Let cmake cache DATA_DIR. set (DATA_DIR "${DATA_DIR}" CACHE PATH "Measures tables root") # Set compiler flag if deprecated Casacore header files are allowed. if (BUILD_DEPRECATED) add_definitions(-DAIPS_USE_DEPRECATED) endif () # Set compiler flag if no table locking. if (NOT ENABLE_TABLELOCKING) add_definitions(-DAIPS_TABLE_NOLOCKING) endif (NOT ENABLE_TABLELOCKING) # Set compiler flag if stack tracing. if(USE_STACKTRACE) add_definitions(-DUSE_STACKTRACE) endif(USE_STACKTRACE) # Enable cmake testing and add make check target that builds and runs the test enable_testing() add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND}) # This legacy flag always builds the tests and runs them with make test. There # seems to be no good way to make test executable depend on the test target if (NOT BUILD_TESTING) set(EXCL_ALL EXCLUDE_FROM_ALL) endif (NOT BUILD_TESTING) # Determine the SOVERSION and define as compile variable. if (casa_soversion) set (LIB_VERSION "${casa_soversion}") set (LIB_SOVERSION "${casa_soversion}") set (CMAKE_INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/lib" ) else( ) set (LIB_VERSION "${PROJECT_SOVERSION}") set (LIB_SOVERSION "${PROJECT_SOVERSION}") endif( ) # Add the modules to be built. add_subdirectory (build-tools) foreach (module ${_modules}) add_subdirectory (${module}) set_target_properties( casa_${module} PROPERTIES VERSION "${LIB_VERSION}" SOVERSION "${LIB_SOVERSION}" ) if (CASA_BUILD) if (PYTHON_SHARED_LINKER_FLAGS AND ${module} STREQUAL python) set_target_properties(casa_python PROPERTIES LINK_FLAGS ${PYTHON_SHARED_LINKER_FLAGS}) endif (PYTHON_SHARED_LINKER_FLAGS AND ${module} STREQUAL python) endif (CASA_BUILD) if (APPLE) if (${module} STREQUAL scimath_f OR ${module} STREQUAL fits OR ${module} STREQUAL mirlib OR ${module} STREQUAL coordinates) set_target_properties(casa_${module} PROPERTIES LINK_FLAGS -single_module) endif (${module} STREQUAL scimath_f OR ${module} STREQUAL fits OR ${module} STREQUAL mirlib OR ${module} STREQUAL coordinates) endif (APPLE) set(PRIVATE_LIBS "${PRIVATE_LIBS} -lcasa_${module}") endforeach (module) # Install pkg-config support file CONFIGURE_FILE("casacore.pc.in" "casacore.pc" @ONLY) set(CASA_PKGCONFIG_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/lib/pkgconfig") INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}/casacore.pc" DESTINATION "${CASA_PKGCONFIG_INSTALL_PREFIX}") # Show summary. message (STATUS "CMAKE_SYSTEM .......... = ${CMAKE_SYSTEM}") message (STATUS "CMAKE_BUILD_TYPE ...... = ${CMAKE_BUILD_TYPE}") message (STATUS "Modules to be built ... = ${_modules}") message (STATUS "BUILD_SHARED_LIBS ..... = ${BUILD_SHARED_LIBS}") message (STATUS "ENABLE_RPATH .......... = ${ENABLE_RPATH}") message (STATUS "CMAKE_INSTALL_NAME_DIR = ${CMAKE_INSTALL_NAME_DIR}") message (STATUS "ENABLE_TABLELOCKING ... = ${ENABLE_TABLELOCKING}") message (STATUS "USE_PCH ............... = ${USE_PCH}") message (STATUS "USE_THREADS ........... = ${USE_THREADS}") message (STATUS "USE_OPENMP ............ = ${USE_OPENMP}") message (STATUS "USE_MPI ............... = ${USE_MPI}") message (STATUS "USE_STACKTRACE ........ = ${USE_STACKTRACE}") message (STATUS "HAVE_O_DIRECT ......... = ${HAVE_O_DIRECT}") message (STATUS "CMAKE_CXX_COMPILER .... = ${CMAKE_CXX_COMPILER}") message (STATUS "CMAKE_CXX_FLAGS ....... = ${CMAKE_CXX_FLAGS}") message (STATUS "DATA directory ........ = ${DATA_DIR}") message (STATUS "DL library? ........... = ${DL_LIBRARIES}") message (STATUS "Pthreads library? ..... = ${PTHREADS_LIBRARIES}") message (STATUS "Readline library? ..... = ${READLINE_LIBRARIES}") message (STATUS "BLAS library? ......... = ${BLAS_LIBRARIES}") message (STATUS "LAPACK library? ....... = ${LAPACK_LIBRARIES}") message (STATUS "WCS library? .......... = ${WCSLIB_LIBRARIES}") message (STATUS "SOFA library? ......... = ${SOFA_LIBRARIES}") message (STATUS "CFitsio library? ...... = ${CFITSIO_LIBRARIES}") message (STATUS "ADIOS2 library? ....... = ${CASACORE_ADIOS_LIBRARY}") message (STATUS "HDF5 library? ......... = ${HDF5_hdf5_LIBRARY}") message (STATUS "FFTW3 library? ........ = ${FFTW3_LIBRARIES}") message (STATUS "BUILD_FFTPACK_DEPRECATED= ${BUILD_FFTPACK_DEPRECATED}") message (STATUS "BUILD_DEPRECATED ...... = ${BUILD_DEPRECATED}") message (STATUS "BUILD_DYSCO ........... = ${BUILD_DYSCO}") message (STATUS "BUILD_PYTHON .......... = ${BUILD_PYTHON}") message (STATUS "BUILD_PYTHON3 ......... = ${BUILD_PYTHON3}") if (BUILD_PYTHON) message (STATUS "PYTHON2_EXECUTABLE ......... = ${PYTHON2_EXECUTABLE}") message (STATUS "PYTHON2_LIBRARIES........... = ${PYTHON2_LIBRARIES}") message (STATUS "PYTHON2_NUMPY_INCLUDE_DIRS . = ${PYTHON2_NUMPY_INCLUDE_DIRS}") message (STATUS "PYTHON2_Boost_LIBRARIES .... = ${PYTHON2_Boost_LIBRARIES}") message (STATUS "PYTHON2_Boost_INCLUDE_DIRS . = ${PYTHON2_Boost_INCLUDE_DIRS}") endif (BUILD_PYTHON) if (BUILD_PYTHON3) message (STATUS "PYTHON3_EXECUTABLE ......... = ${PYTHON3_EXECUTABLE}") message (STATUS "PYTHON3_LIBRARIES .......... = ${PYTHON3_LIBRARIES}") message (STATUS "PYTHON3_NUMPY_INCLUDE_DIRS . = ${PYTHON3_NUMPY_INCLUDE_DIRS}") message (STATUS "PYTHON3_Boost_LIBRARIES .... = ${PYTHON3_Boost_LIBRARIES}") message (STATUS "PYTHON3_Boost_INCLUDE_DIRS . = ${PYTHON3_Boost_INCLUDE_DIRS}") endif (BUILD_PYTHON3) # List of build variables and defaults. # BUILD_PYTHON YES # BUILD_PYTHON3 NO # BUILD_DEPRECATED NO # ENABLE_SHARED YES # ENABLE_RPATH YES # ENABLE_TABLELOCKING YES # USE_ADIOS2 NO # USE_HDF5 NO # USE_THREADS YES # USE_OPENMP NO # USE_MPI NO # USE_STACKTRACE NO # DATA_DIR ${CMAKE_INSTALL_PREFIX}/share/casacore/data # MODULE all # Possible value for MODULE (previous built too): # - casa (casa) # - tables (tables) # - measures (scimath,scimath_f,measures,meas) # - ms (ms,derivedmscal) # or msfits (fits,ms,msfits,derivedmscal) # or images (fits,lattices,mirlib,coordinates,images) # - all # # List of possibly used external packages and where # CFITSIO fits # WCSLIB coordinates # SOFA measures (optional, only for testing) # DL casa (optional) # READLINE casa (optional) # HDF5 casa (optional) # BISON casa,tables,images # FLEX casa,tables,images # ADIOS2 tables (optional) # LAPACK scimath # BLAS scimath # FFTW scimath # BOOST python (Boost-Python only) # PYTHON python # NUMPY python casacore-3.7.1/CONTRIBUTING.md000066400000000000000000000057701476623553700155660ustar00rootroot00000000000000# Casacore contribution policy Casacore is an open source project and everybody is encouraged to help improve the quality of the code. You can help by reporting issues or even better fix issues yourself. We use github as a central communication and development platform. Issues can be reported there. If you have a patch for casacore we use the github pull request system. Also to keep the casacore code quality high we have written down some guidelines for contributing, see below. ## General considerations * If you have problems or questions with/about git or github, first check [1]. * If you modify any code, make sure the test suite still runs (`make test`). If it fails, fix the code or the relevant test. * If you change a function/method signature, update the doxygen documentation accordingly. * If you add a function or method, add a test for it add it to the doxygen documentation. * The tested code coverage line count should increase, not decrease. * Follow the google style code as much as possible [2]. ## Contribution procedure 1. Create a github account, setup SSH keys. 2. Fork the casacore repository on github [3]. 3. Create a branch and commit your changes to this branch. 4. Push your branch to your github fork (not the original casacore, you probably don't have permission). 5. Issue a pull request [4]. 6. When the pull request is reviewed and there are no problems it will be accepted. Merging a pull request should always happen by someone else. 7. It can happen there are some mistakes here and there, we use the github commenting system to discuss these issues. 8. If there is a problem with the commit, you need to fix it. You can commit to the same branch, the PR will be updated automatically. ## General notes about Pull requests * Please create descriptive commits containing atomic changes. Use short commit messages (try 50 characters, max is 70 characters); longer commit messages are possible in the body of the commit message (separated from the subject by an empty line). See [5]. * If you fix an existing github issue, reference it in the commit message body, e.g. 'Fixes #41'. * If your Pull request does not refer to an existing github issue, it is not necessary to create one (because github will present the PR just like an issue). * You can rewrite the history of the commits in your branch using rebase, but don't rewrite the history before the first commit of your new branch. * We like to keep the history clean, and prevent a lot of 'fix typo' messages. * If you rewrite your history of your branch you can force push those changes to your branch. The PR will be updated. See e.g. [6]. ## Links * [1] http://help.github.com * [2] https://google-styleguide.googlecode.com/svn/trunk/cppguide.html * [3] https://help.github.com/articles/fork-a-repo/ * [4] https://help.github.com/articles/creating-a-pull-request/ * [5] https://chris.beams.io/posts/git-commit * [6] https://developer.atlassian.com/blog/2015/04/force-with-lease/ casacore-3.7.1/COPYING000066400000000000000000000614501476623553700143650ustar00rootroot00000000000000 GNU LIBRARY GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library, or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide complete object files to the recipients so that they can relink them with the library, after making changes to the library and recompiling it. And you must show them these terms so they know their rights. Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library. Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license. The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such. Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better. However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library. Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one. GNU LIBRARY GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. c) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. d) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Library General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! casacore-3.7.1/CTDS-64bit.md000066400000000000000000000132521476623553700153340ustar00rootroot00000000000000# CTDS support of very large tables A major change has been made to the Casacore Table Data System (CTDS). So far, tables larger than 2^31 rows could not be supported, but this change makes it possible to have tables up to 2^63 rows. ## Data format change The change had quite some impact on the Tables data format, but the changes have been done in a compatible way. * The new software can still read and update existing tables. It means it is fully backward compatible. * A new table will be written in the old format if it contains less than 2^31 rows. It means that an older Casacore version can still access most tables created with the new version. ## API change The API has changed considerably, both at the Tables and the DataManager level. The main changes at the Tables level are: * `rownr_t` (typedef-ed to `uInt64`) instead of `uInt` is now used to represent a row number. * class `RowNumbers` instead of `Vector` is used to represent a vector of row numbers. It can convert automatically from and to a `Vector` of `rownr_t` and optionally to `uInt` (giving backward compatibility). * The functions `getColumnXXX` in class `TableExprNode` are available for `RowNumbers` and optionally for `Vector`. To assure that software is using 64-bit row numbers, conversion of `RowNumbers` from and to `Vector` is made explicit. However, if `IMPLICIT_CTDS_32BIT` is defined, the conversion is implicit (thus automatic). Similarly, the `Vector` versions of `TableExprNode::getColumnXXX` are only public if `IMPLICIT_CTDS_32BIT` is defined. It means that with this `#define` existing software can be built against the new and an old Casacore version without any change. The DataManager interface has been changed more drastically, but still in a backward compatible way. Specialized storage managers such as LofarStMan, ASDMStMan and DyscoStMan should build fine against the new and an old Casacore version without any change. However, in this way such storage managers can only support tables with less than 2^31 rows. To extend it to 2^63 rows, they should use the new interface as depicted in DataManagerColumn.h. The main changes are: * Several virtual functions in class `DataManager` got the suffix 64 (e.g. `create64`) to differentiate them from the old function names which still exist for backward compatibility. * `DataManager` column classes (such as `ISMColumn`) should be derived from class `StManColumnBase` which implements some common functionality. * The `canAccess` functions are obsolete and can be removed. Formerly the Tables classes `ScalarColumn` and `ArrayColumn` contained the default implementations for `getColumn`, `getSlice`, etc.. This has been moved to class `DataManagerColumn` making the `canAccess` functions obsolete in the new interface. Of course, these functions should be kept if backward compatibily is needed. * The interface of get/put functions handling arrays has been changed from `void*` to `ArrayBase&`, mainly to have it more clear. The native storage manager classes now often can use Array functionality to handle most array data types instead of having to handle these data types explicitly. Although the DataManager interface has changed considerably, it is still fully backward compatible. The default implementations of the new functions call the old functions in the `StManColumn` class and convert `rownr_t` to `uInt` (while checking if its value is < 2^31). The test programs tExternalStMan.cc and tExternalStManNew.cc show how the old and the new interface are used in a data manager. They reside in tables/DataMan/test. Note that the build of tExternalStMan.cc and other unchanged data manager classes give hidden virtual function warnings. The reason is that in the new interface functions such as `shape` are overloaded for `rownr_t` and `uInt`, while only the `uInt` version is used in these classes using the old interface. Note that a nice side-effect of the DataManager change is that `libcasa_tables.so` shrunk about 10%. It will even be more once the backward compatibility functionality can be removed. ## ABI change As can be expected, the ABI has been changed considerably, so software needs to be rebuilt between using the new and old Casacore libraries. It results in the new major Casacore version 3.4.0. ## Changes in other software All other software in the Casacore package, notably the MS package, has been changed to use the new CTDS interface. A few external packages (LOFAR, CASA) have been built against the new interface. With one minor (backward compatible) change LOFAR built fine. CASA built fine after defining `-DIMPLICIT_CTDS_32BIT` in its main CMakeLists.txt. It means that both packages can be built against the old and new interface without any problem. Of course, over time packages such as CASA should change to using `rownr_t` instead of `uInt` where applicable. ## Backward compatibility functions Several functions have been added to make the API change backward compatible. They are intended to be temporary, so they should be removed once all software using Casacore uses 64-bit row numbers. Tables backward compatibility to be removed consists of the following functions: * In class `RowNumbers` the `Vector` and `std::vector` constructor and the `Vector` operator. * In class `RefRows` the functions using `Vector`. * In class `TableExprNode` the functions getColumnXXX using Vector. Once all external data managers use 64-bit row numbers, the following DataMan classes and functions can be removed. * In class `DataManager` the functions `create`, `open`, `open1`, `resync`, `resync1`, `addRow` and `removeRow`. * The entire class `StManColumn`. casacore-3.7.1/README.md000066400000000000000000000071171476623553700146110ustar00rootroot00000000000000 # Casacore A suite of C++ libraries for radio astronomy data processing. # Installation ## Debian / Ubuntu Casacore is now part of Debian and Ubuntu, use apt to search for the package you need: ```bash $ apt-get update $ apt-get search casacore ``` ## MacOS Use Homebrew with the `ska-sa` tap to install casacore: ```bash $ brew tap ska-sa/tap $ brew install casacore ``` ## Docker We now also publish docker images for each github branch on [quay.io](https://quay.io/repository/casacore/casacore): ``` $ docker pull quay.io/casacore/casacore:master ``` ## Building from source ### Getting the source code The casacore source code is maintained on github. You can obtain it using: ``` $ git clone https://github.com/casacore/casacore ``` ### Requirements To compile casacore you need to meet the following requirements: * cmake * gfortran * g++ * flex * bison * blas * lapack * cfitsio (3.181 or later) * wcslib (4.20 or later) * sofa (optional, only for testing casacore measures) * fftw3 * hdf5 (optional) * numpy (optional) * boost-python (optional) * ncurses (optional) On Debian / Ubuntu you can install these with: ``` $ sudo apt-get install build-essential cmake gfortran g++ libncurses5-dev \ libreadline-dev flex bison libblas-dev liblapacke-dev libcfitsio-dev \ wcslib-dev libfftw3-dev ``` and the optional libraries: ``` $ sudo apt-get install libhdf5-serial-dev python-numpy \ libboost-python-dev libpython3.4-dev libpython2.7-dev ``` On CentOS7 you can install these with: ```bash $ sudo yum install cmake cmake-gui gcc-gfortran gcc-c++ flex bison \ blas blas-devel lapack lapack-devel cfitsio cfitsio-devel \ wcslib wcslib-devel ncurses ncurses-devel readline readline-devel\ python-devel boost boost-devel fftw fftw-devel hdf5 hdf5-devel\ numpy boost-python ``` ## Obtaining measures data Various parts of casacore require measures data, which requires regular updating. You can obtain the WSRT measures archive from the ASTRON FTP server: ftp://ftp.astron.nl/outgoing/Measures/ Extract this somewhere on a permanent location on your filesystem. ## Compilation In the casacore source folder run: ``` mkdir build cd build cmake .. make make install ``` there are various flags available to cmake to enable and disable options: ``` $ cmake -DDATA_DIR=/usr/share/casacore/data -DUSE_OPENMP=ON \ -DUSE_HDF5=ON -DBUILD_PYTHON3=ON -DUSE_THREADS=ON ``` The `DATA_DIR` should point to the location where you extracted the measures data. Special variables `%CASAROOT%` and `%CASAHOME%` can be used here, which can be set at run time through the `.casarc` file. Older versions of CMake may have problems detecting the correct python libraries and headers, in which case you may need to set them manually. For example: ``` -DPYTHON3_LIBRARY=/usr/lib/x86_64-linux-gnu/libpython3.4m.so -DPYTHON3_INCLUDE_DIR=/usr/include/python3.4 ``` To configure Python3 specific settings use: ``` PYTHON3_EXECUTABLE PYTHON3_LIBRARY PYTHON3_INCLUDE_DIR ``` If you run into problems with boost libraries, try setting `-DBoost_NO_BOOST_CMAKE=True`. This may be necessary if you have the libraries from NRAO casa in your `PATH` or `LD_LIBRARY_PATH`. # Documentation http://casacore.github.io/casacore # Problems & bugs If you have any issues compiling or using casacore, please open an issue on the issue tracker on github. If you have patches please open a pull request. Your contributions are more than welcome! But to maintain a high code quality we have written a [contribution manual](https://github.com/casacore/casacore/blob/master/CONTRIBUTING.md), please read that first. casacore-3.7.1/build-tools/000077500000000000000000000000001476623553700155615ustar00rootroot00000000000000casacore-3.7.1/build-tools/CMakeLists.txt000066400000000000000000000000541476623553700203200ustar00rootroot00000000000000# # CASA build-tools # # nothing to install casacore-3.7.1/build-tools/casacore_assay000077500000000000000000000125451476623553700204760ustar00rootroot00000000000000#!/bin/sh #----------------------------------------------------------------------------- # assay: Invoke an AIPS++ test program and verify its output #----------------------------------------------------------------------------- # # Copyright (C) 1995,1996,1998,1999,2001,2003 # Associated Universities, Inc. Washington DC, USA. # # 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., 675 Mass Ave, Cambridge, MA 02139, USA. # # Correspondence concerning AIPS++ should be addressed as follows: # Internet email: casa-feedback@nrao.edu. # Postal address: AIPS++ Project Office # National Radio Astronomy Observatory # 520 Edgemont Road # Charlottesville, VA 22903-2475 USA # #----------------------------------------------------------------------------- # Usage: assay #----------------------------------------------------------------------------- # assay invokes a casacore test program. If the test program has an associated # .run file then it simply invokes it. Otherwise assay invokes the test # executable directly, and, if there is a corresponding .out file, compares # its output with that. # # The env.var. CASACORE_CHECK can be defined to do checking using tools # like valgrind. It defines a script that will execute the command. The # script can invoke the checking tool. If set to 1, yes, or YES, the script # casacore_memcheck will be used that uses valgrind's memcheck tool. # # If there is a .in file associated with the test program then assay will # redirect stdin from it. # # Options: # none # # Status returns: # 0: success # 1: test execution failed # 2: test output disagreement # 3: untested (usually returned from a .run) # 130: interrupt # # Original: 1995/11/01 by Mark Calabretta, ATNF #============================================================================= # Find the path used to start the script. It is used for other scripts. pgmpath=`dirname $0` pgmpath=`cd $pgmpath > /dev/null 2>&1 && pwd` # Also set PYTHONPATH. if [ "$PYTHONPATH" = "" ] then PYTHONPATH=. else PYTHONPATH=".:$PYTHONPATH" fi CLEANUP= # Set testsrcdir to . if undefined. if [ "$testsrcdir" = "" ] then testsrcdir=. fi # Determine if a program checktool (like valgrind) has to be used. casa_checktool=$CASACORE_CHECK # Empty or no means no checktool. if test "$casa_checktool" = "0" -o "$casa_checktool" = "no" -o "$casa_checktool" = "NO"; then casa_checktool= fi # Default checktool is casacore_memcheck (using valgrind). if test "$casa_checktool" = "1" -o "$casa_checktool" = "yes" -o "$casa_checktool" = "YES"; then casa_checktool=$pgmpath/casacore_memcheck fi # Export, so it can be used in possible .run files. export casa_checktool # Delete possible already existing checktool output. rm -rf $1.checktool* # Define exit and interrupt handler. trap 'rm -rf $CLEANUP core ${1}_tmp* ; \ trap - 0 ; \ exit $STATUS' 0 1 2 3 15 # Get the command. COMMAND="$casa_checktool $@" set $@ # Get the current directory (resolving symlinks). curdir=`pwd -P` # If there is a .run file then use it without checktool. # Start it using sh if not executable. if [ -f "$1.run" ] then COMMAND="sh $1.run" if [ -x "$1.run" ] then COMMAND="$1.run" fi fi # If there is a .in file then use it as input. if [ -f "$1.in" ] then COMMAND="$COMMAND < $1.in" fi # Remove possible old output files. rm -rf ${1}_tmp* eval "$COMMAND" > ${1}_tmp.out STATUS=$? if [ $STATUS != 0 ] then if [ $STATUS = 3 ] then echo "UNTESTED: $*" else echo "FAIL (execution failure): $*" fi exit fi if [ -f $1.out ] then CAT="$1.out" else echo "PASS (execution succeeded): $*" STATUS=0 exit fi # Strip out demarked text. # First text on a single line enclosed in >>> and <<<. # Then lines enclosed in lines starting with >>> and <<<. # Remove the current directory from the output (to avoid system dependencies). sed -e 's/>>>.*<<>>/,/^<< ${1}_tmp.out2 mv -f ${1}_tmp.out2 ${1}_tmp.out # Compare with the expected output. cat "$CAT" | sed -e 's/>>>.*<<>>/,/^<< ${1}_tmpo.out if diff ${1}_tmp.out ${1}_tmpo.out then echo "PASS (output verified): $*" STATUS=0 exit fi # Not fully equal. Check with floating point accuracy. $pgmpath/casacore_floatcheck ${1}_tmp.out ${1}_tmpo.out 1e-5 stat=$? case $stat in 0) echo "PASS (floating point discrepancies <= 1e-5): $1" ;; 1) echo "FAIL (output not verified): $*" ;; *) echo "FAIL (floating point discrepancies > 1e-5): $1" ;; esac STATUS=$stat exit casacore-3.7.1/build-tools/casacore_cov000077500000000000000000000042641476623553700201440ustar00rootroot00000000000000#!/bin/sh # Get directory of module or package to test. # It can be run in something like build, build/ or # build///test. # If run in the test directory, the directory containing the object files # should be included. Its name is ../../CMakeFiles/casa_.dir/. cwd=`pwd` cwd1=`echo "$cwd" | sed -e "s%/test$"%%` srcdir= if [ "$cwd" != "$cwd1" ]; then part=`echo $cwd1 | sed -e "s%.*/%%"` # remove till last slash module=`echo $cwd1 | sed -e "s%/${part}$%%" -e "s%.*/%%"` srcdir="--directory ../../CMakeFiles/casa_${module}.dir/${part}" fi mod=`pwd | sed -e "s%.*/build/[^/]*/%/%" -e "s%.*/build/[^/]*$%%" -e "s%/test$%%"` # Clear coverage counters. echo "Initializing coverage info for module $mod ..." lcov --zerocounters --directory . $srcdir > testcov.log 2>&1 # Create baseline coverage data. lcov --rc lcov_function_coverage=0 --capture --initial --directory . $srcdir --output-file testcov.bl >> testcov.log 2>&1 #lcov --capture --initial --directory $cwd --output-file testcov # Run the tests and extract line and branch coverage information. echo "Running tests ..." ctest >> testcov.log 2>&1 echo "Collecting coverage info ..." lcov --rc lcov_function_coverage=0 --rc lcov_branch_coverage=1 --no-checksum --capture --directory . $srcdir --output-file testcov.data >> testcov.log 2>&1 #lcov --no-checksum --capture --directory $cwd --output-file testcov.info # Combine with baseline. lcov --rc lcov_function_coverage=0 --rc lcov_branch_coverage=1 -a testcov.bl -a testcov.data -o testcov.info >> testcov.log 2>&1 # Get information for this module only. echo "Filtering coverage info ..." lcov --rc lcov_function_coverage=0 --rc lcov_branch_coverage=1 --output-file testcov.full --extract testcov.info "*casacore$mod/*" >> testcov.log 2>&1 # Get it without the test programs. lcov --rc lcov_function_coverage=0 --rc lcov_branch_coverage=1 --output-file testcov.module --remove testcov.full "*/test/*" >> testcov.log 2>&1 # Convert to html. echo "Converting coverage info to ./cov/index.html ..." rm -rf cov genhtml --rc lcov_function_coverage=0 --rc lcov_branch_coverage=1 --output-directory=cov testcov.module >> testcov.log 2>&1 echo "Ready; log output in ./testcov.log"casacore-3.7.1/build-tools/casacore_floatcheck000077500000000000000000000064561476623553700214650ustar00rootroot00000000000000#!/usr/bin/env python #----------------------------------------------------------------------------- # floatcheck: Check files within floating point accuracy #----------------------------------------------------------------------------- # # Copyright (C) 2009 # Associated Universities, Inc. Washington DC, USA. # # 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., 675 Mass Ave, Cambridge, MA 02139, USA. # # Correspondence concerning AIPS++ should be addressed as follows: # Internet email: casa-feedback@nrao.edu. # Postal address: AIPS++ Project Office # National Radio Astronomy Observatory # 520 Edgemont Road # Charlottesville, VA 22903-2475 USA # # This script compares two text files for equality. # If numbers mismatch, it is checked if they are near each other. # The tolerance can be given as the third argument; it defaults to 1e-5. import re import sys def splitfile (filename, regexp): f = open(filename, 'r') res = [] for line in f: # Make sure numbers are separated by blanks. line = regexp.sub (r' \g ', line) res.extend (line.split()) f.close() return res; def near (val1, val2, tol): # ok if both close to zero. if abs(val1) <= tol*1e-10 and abs(val2) <= tol*1e-10: return True; if (val1>0) != (val2>0): return False return abs(val1-val2) <= tol*max(abs(val1),abs(val2)) def main(argv=None): if argv is None: argv = sys.argv if len(argv) < 3: sys.stderr.write ('run as: floatcheck.py file1 file2 [tolerance]\n') return 1 tol = 1e-5; if len(argv) > 3: tol = float(argv[3]) # Regular expression for an integer or float number. numre = re.compile (r'(?P[+-]?([0-9]+\.[0-9]*|[0-9]*\.[0-9]+|[0-9]+)([ed][+-][0-9]+)?)', re.IGNORECASE) # Split file in separate values. res1 = splitfile (argv[1], numre); res2 = splitfile (argv[2], numre); # Keep list of possible numbers to compare. num1 = [] num2 = [] # Compare res1 and res2 for equality. # Mismatching numbers are kept and compared later. if len(res1) != len(res2): return 1 # mismatch for i in range(len(res1)): if res1[i] != res2[i]: if numre.sub('', res1[i]) != '' or numre.sub('', res2[i]) != '': return 1 num1.append (res1[i]) num2.append (res2[i]) # Now compare if numbers are more or less equal. for i in range(len(num1)): val1 = float(num1[i]) val2 = float(num2[i]) if not near(val1, val2, tol): return 2 return 0 if __name__ == "__main__": sys.exit(main()) casacore-3.7.1/build-tools/casacore_memcheck000077500000000000000000000027431476623553700211310ustar00rootroot00000000000000#!/bin/sh # Find the path used to start the script. It is used for other scripts. pgmpath=`dirname $0` pgmpath=`cd $pgmpath > /dev/null 2>&1 && pwd` # Get name of program to test. pgm=$1 shift # Exit if valgrind does not exist. which valgrind > /dev/null 2>&1 if [ $? != 0 ]; then echo "*** memcheck of $pgm cannot be done; valgrind cannot be found ***" 1>&2 exit 1 fi # Use valgrind's memcheck tool on a program. # Filter out possible errors to give an overview. rm -f ${pgm}_tmp.valgrind valgrind --tool=memcheck --num-callers=50 --ignore-range-below-sp=1024-1 --leak-check=yes --track-fds=yes --suppressions=$pgmpath/casacore_memcheck.supp --log-file=${pgm}_tmp.valgrind $pgm "$@" # Check if any error, definite or possible leak, or open fd is found. # If so, list the file and rename to keep it. # Note that fd 0,1,2 are always open (stdin,stdout,stderr). # Furthermore 3 and/or 4 can be open for the valgrind log file and # the ctest output. errors=`(grep "ERROR SUMMARY: " ${pgm}_tmp.valgrind | grep -v " 0 errors ") || echo ""` deflost=`(grep "definitely lost: " ${pgm}_tmp.valgrind | grep -v " 0 bytes ") || echo ""` poslost=`(grep "possibly lost: " ${pgm}_tmp.valgrind | grep -v " 0 bytes ") || echo ""` openfds=`(grep " Open file descriptor " ${pgm}_tmp.valgrind | grep -v "descriptor [01234]:") || echo ""` if test "$errors" != "" -o "$deflost" != "" -o "$poslost" != "" -o "$openfds" != ""; then cat ${pgm}_tmp.valgrind >> $pgm.checktool.valgrind fi rm -f ${pgm}_tmp.valgrind casacore-3.7.1/build-tools/casacore_memcheck.supp000066400000000000000000000052711476623553700221130ustar00rootroot00000000000000# This file contains the valgrind errors to be suppressed for casacore. # Where needed, it has lines for namespace casa and casacore. # Very often libc gives an invalid free error at exit. { Ubuntu-gcc-freeres-error-in-libc Memcheck:Free fun:free obj:/lib/libc-2.11.1.so obj:/lib/libc-2.11.1.so fun:_vgnU_freeres } # putenv takes over the pointer, but valgrind does not notice. { Class-EnvVar-putenv-takes-over-pointer-4 Memcheck:Leak fun:_Znam fun:_ZN4casa19EnvironmentVariable3setERKNS_6StringES3_ } { Class-EnvVar-putenv-takes-over-pointer-8 Memcheck:Leak fun:_Znam fun:_ZN8casacore19EnvironmentVariable3setERKNS_6StringES3_ } # Casacore's IO sometimes uses uninitialized fill characters. { Casacore-write-not-fully-initialized-buffer-LargeFilebufIO-4 Memcheck:Param write(buf) fun:__write_nocancel fun:_ZN4casa9FiledesIO5writeExPKv } { Casacore-write-not-fully-initialized-buffer-LargeFilebufIO-8 Memcheck:Param write(buf) fun:__write_nocancel fun:_ZN8casacore9FiledesIO5writeExPKv } { Casacore-write-not-fully-initialized-buffer-FilebufIO-dbg4 Memcheck:Param write(buf) fun:__write_nocancel fun:_ZN4casa9FilebufIO11writeBufferExPKcx } { Casacore-write-not-fully-initialized-buffer-FilebufIO-dbg8 Memcheck:Param write(buf) fun:__write_nocancel fun:_ZN8casacore9FilebufIO11writeBufferExPKcx } { Casacore-write-not-fully-initialized-buffer-FilebufIO-opt4 Memcheck:Param write(buf) fun:??? fun:_ZN4casa9FilebufIO11writeBufferExPKcx } { Casacore-write-not-fully-initialized-buffer-FilebufIO-opt8 Memcheck:Param write(buf) fun:??? fun:_ZN8casacore9FilebufIO11writeBufferExPKcx } { Casacore-write-not-fully-initialized-buffer-FiledesIO-dbg8 Memcheck:Param pwrite64(buf) fun:__pwrite_nocancel fun:_ZN8casacore9FiledesIO6pwriteExxPKv } { Casacore-write-not-fully-initialized-buffer-FiledesIO-opt8 Memcheck:Param pwrite64(buf) fun:??? fun:_ZN8casacore9FiledesIO6pwriteExxPKv } # The function strlen is optimized by testing multiple byte simultaneously. # This gives an uninitialized condition error for the bytes beyond the end. { Optimized-strlen-uses-multiple-bytes Memcheck:Cond fun:__GI_strlen fun:_ZNSsC1EPKcRKSaIcE } { Optimized-strlen-uses-multiple-bytes-2 Memcheck:Cond fun:strlen fun:_ZNSsC1EPKcRKSaIcE } # On lhd002 the nss functions (called by getpwnam) leak { getpwnam-nss-leak Memcheck:Leak fun:malloc fun:nss_parse_service-list fun:__nss_database_lookup } # OpenMP with glibc-2.2.5 seems to give a pthread leak { openmp-pthread-leak Memcheck:Leak fun:calloc fun:_dl_allocate_tls fun:pthread_create@@GLIBC_2.2.5 } casacore-3.7.1/build-tools/doxygen_pp000077500000000000000000000534541476623553700176760ustar00rootroot00000000000000#!/usr/bin/perl # # Copyright (C) 2006 # ASTRON (Netherlands Foundation for Research in Astronomy) # P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, diepen@astron.nl # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Convert multiple C++ "slash-slash" style comment lines # into one spanning C-style comment (/* ... */), ignoring # lines that start with "slash-slash-hash style comments. # Lines starting with //# are blanked except for the ones at the beginning # showing the copyright. # # The cxx2html tags are converted to doxygen or html tags. # Code from cxx2html (Html.pm) has been copied for this purpose. # When converting, the line numbers should stay the same, so doxygen # errors/warnings refer to the correct line. Also the line numbers and # source file shown by doxygen will be correct. $NON_COMMENT_BLOCK = 0; $COMMENT_BLOCK = 1; $FIRST_SUMMARY = 1; # If the caller supplied precisely one command line argument, then this script # can proceed. ###open OUT, ">> doc/doxygen.done"; $arguments = @ARGV; if ($arguments == 1) { # The argument represents the name of the file to process. $filename = $ARGV[0]; # Get current directory without trailing slash. $cwd = `pwd`; chop($cwd); $cwd =~ s|/$||; # Append file name to it (which may contain a path as well). $fullnm = $filename; if ($filename !~ m|^/|) { $fullnm = $cwd . "/" . $filename; } $filnm = $filename; $filnm =~ s|$cwd||; # remove current directory (thus keep past casacore) $filnm =~ s|^/||; # remove leading slash # Get the name of the header. $hdrnm = $filnm; $hdrnm =~ s|.*/||; # remove directory (until last slash) $hdrnm =~ s|\..*||; # remove extension # Get the name of the package and module. $isModHdr = 0; $modnm = ""; $pkgnm = $filnm; $pkgnm =~ s|/.*||; # remove first slash and beyond giving package name $modna = $filnm; $modna =~ s|$pkgnm/||; # remove package name # The old situation has an extra layer like casa/casa/OS or casa/implement/OS. # Remove that too. $modna =~ s%$pkgnm/|implement/%%; # remove package name $modnm = $modna; $modnm =~ s|/.*||; # again remove first slash and beyond giving module if ($modnm eq $modna) { # Nothing removed, so we have a .h file outside a module. $modnm =~ s|\..*||; # remove extension # It is a module header if a directory with the same name exists. $tmpnm = $fullnm; $tmpnm =~ s|\.h$||; if (-d $tmpnm) { $isModHdr = 1; } } # On OS-X package tables and module Tables get the same (casa-insensitive) # file name which is solved by appending '_module' to a module name. $modnm .= "_module"; ### print OUT "$fullnm $filnm $hdrnm $modnm $pkgnm $isModHdr\n"; # Open the file. $status = open(FILEHANDLE, $filename); ### open DOXOUT, ">> doc/" . $hdrnm . $isModHdr . ".dout"; # If the file could be opened then read the contents. if ($status) { # Read the contents of the file. @text = ; # Close the file. close(FILEHANDLE); # Process the contents of the file. change_comment_style(@text); } # Otherwise report an error condition. else { print "ERROR: file \"$filename\" could not be opened.\n"; } } # Otherwise, display the proper invocation for this script. else { print "usage: $0 filename\n"; } # Handle changing a linkto tag. # Replace linkto by the class linked to; remove possible ':description' # Similarly replace module link; link ':classes' to the same. # The closing has already been removed. sub handleLinkto { my $linkstr = shift(@_); # Remove :description or :classes. $linkstr =~ s%:(description|classes)(['"]?\s*>)%$2%i; my $endlink = ""; if ($linkstr =~ m||i) { # A link to a class. $linkstr =~ s|| \\link casacore::$1 |i; $endlink = "\\endlink"; } elsif ($linkstr =~ m%%i) { # A link to a module. $linkstr =~ s%% \\ref $1_module_anchor "%i; $endlink = '"'; } else { # A link to an anchor in a group or file. if ($linkstr =~ m%%i) { my $grpnm = $2; my $ref = $3; $grpnm =~ s|\..*||; $grpnm =~ s|\s*['".]\s*||g; $grpnm =~ s|\s+|_|g; $ref =~ s|\s*['".]\s*||g; $ref =~ s|\s+|_|g; $ref =~ s|:description||; $ref =~ s|[:]|_|g; $linkstr =~ s%% \\ref ${grpnm}_$ref "%i; $endlink = '"'; } elsif ($linkstr =~ m%%i) { # A link to a class member function. Remove the signature. my $classnm = $1; my $funcnm = $2; $linkstr =~ s%% \\link ${classnm}::$funcnm() %i; $endlink = '\\endlink'; } elsif ($linkstr =~ m%%) { # A link to a class or module anchor. my $grpnm = $2; my $ref = $3; $grpnm =~ s|\s*['".]\s*||g; $grpnm =~ s|\s+|_|g; $ref =~ s|\s*['".]\s*||g; $ref =~ s|\s+|_|g; $ref =~ s|:description||; $ref =~ s|[:]|_|g; $linkstr =~ s%%\\ref ${grpnm}_$ref "%i; $endlink = '"'; } else { # For the time being cannot really handle other linkto commands. $linkstr =~ s|(.*?)|(see $2 ($1))|i; } } $linkstr .= $endlink; return $linkstr; } # Handle changing a reviewed tag. sub handleReviewed { # Get argument and remove tags delimiters. my $attr = shift(@_); $attr =~ s|^\s*<\s*reviewed\s*||; $attr =~ s|\s*>\s*$||; # Organize review info nicely. @attr = split(/(?:=["'][^'"]*['"]|=[^\s>]*)\s*/,$attr); $attr =~ s/.*?=//; my @val = split(/\s*\w+=/,$attr); my %attr = (); my $attrcnt = 0; while ( ($_ = shift(@attr)) && ($val = shift(@val)) ) { $val =~ s/^["']\s*//; $val =~ s/\s*["']$//; $val =~ s/@/\\@/g; # doxygen treats @ as special, so escape $val =~ s/\.cc//g; # doxygen cannot deal with . in
if ( $val ) { $attr{$_} = $val; $attrcnt++; } } my $str = ""; if ( $attrcnt ) { $str = "

Review Status

"; $str .= "
Reviewed By:
" . $attr{'reviewer'} if defined $attr{'reviewer'}; $str .= "
Date Reviewed:
" . $attr{'date'} if defined $attr{'date'}; $str .= "
Test programs:
" . $attr{'tests'} if defined $attr{'tests'}; $str .= "
Demo programs:
" . $attr{'demos'} if defined $attr{'demos'}; $str .= "
"; } return $str; } # Loop through all lines and change comments as needed. sub change_comment_style { $keephash = 1; $state = $NON_COMMENT_BLOCK; $newstate = $NON_COMMENT_BLOCK; @spanning_comment = (); $indent = ""; $comment = ""; $extraline = ""; $grouplevel = 0; $namedgrouplevel = 0; $nexamples = 0; $code = 0; # no block found $link = 0; # no block found $linkfr = 0; # no found $linkstr = ""; $todo = 0; # no found $prereq = 0; # no found $thrown = 0; # no found $emnote = 0; # no found $review = 0; # no block found $reviewstr = ""; $nadded = 0; # nr of added lines for which blank lines are removed $summinx = -1; # index of summary line in spanning_comment foreach $line (@_) { $skip = 0; $newstate = $NON_COMMENT_BLOCK; # slash-slash-hash is blanked if not at beginning of file. if ($keephash == 0 && $line =~ m|^\s*//\#|) { $line = "\n"; } $comment = $line; # If this line contains only a slash-slash comment, then # the comment block might need to be converted. if ($line =~ m|(^\s*)(//)(\s*)(.*)|) { # Remove indentation, because doxygen treats a line with 4 leading spaces # (after an empty line) as an example code block. $indent = $1; $indent2 = $3; $comment = $4; if ($indent2 =~ m|\s.*|) { $comment = " $comment"; } # slash-slash-slash/hash are kept as such (is doxygen already). # Four slashes is comment in code examples, thus is normal comment. if ($comment !~ m|^[/#][^/]| && $comment !~ m|^[/#]$|) { $keephash = 0; $newstate = $COMMENT_BLOCK; # Doxygen treats #name as a request to link to name. # So escape the #. Don't do that for #include and if. $comment =~ s|([ (])#([a-z]+)|$1\\#$2|ig; $comment =~ s| \\#include| #include|g; $comment =~ s| \\#if| #if|g; $comment =~ s| \\#endif| #endif|g; # Remove possible prototype text. $comment =~ s|You should have at least a preliminary understanding of these classes:||; $comment =~ s|
  • ||; # A module header is turned into a defgroup and added to the package. if ($isModHdr == 1) { if ($comment =~ m|^\s*|i) { $comment =~ s|| \\anchor ${modnm}_anchor \\ingroup $pkgnm\n\\defgroup $modnm $modnm|i; $nadded += 2; } $comment =~ s||*/\n/** \\ingroup $modnm\n\\defgroup ${modnm}_internal_classes ${modnm}_internal_classes\n\\brief Internal $modnm classes and functions|i; $comment =~ s|^\s*| \\brief |ig; $comment =~ s||

    See below for an overview of the classes in this module.|i; } else { # A class or group header's summary is added to the module's defgroup. if ($comment =~ m|^\s*

    |i) { if ($FIRST_SUMMARY == 1) { $comment =~ s|| \\anchor ${hdrnm}_summary \\ingroup $modnm \n\\brief |i; $FIRST_SUMMARY = 0; } else { $comment =~ s|| \\ingroup $modnm \n\\brief |i; } $nadded += 1; $summinx = @spanning_comment + 1; # index of ingroup line } $comment =~ s|||i; } # Handle group tags (also the old fashioned +grp/-grp). # Turn a named group with a summary into a struct, so doxygen # puts it into the module header at the right place. # Keep track of the group level, so such a named group is ended # like a struct with };. # Named groups are always preceeded by an anchor for linkto references. # Unnamed groups are removed because doxygen handles them in a strange way. # Sometimes they do not appear in the summary output. $comment =~ s|^\s*\+grp||; $comment =~ s|^\s*-grp||; if ($comment =~ m||i) { $named = 1; $grpnm = $1; $grpnm =~ s|\s*['".]\s*||g; $grpnm =~ s|\s+|_|g; $grpnm =~ s|:|_|g; $comment = "\\anchor ${hdrnm}_$grpnm "; # Only a named group at the highest level is turned into a struct. # Do this only if it has a summary, which indicates it describes # a group of functions outside a class. if ($grouplevel == 0 && $summinx >= 0) { $namedgrouplevel += 1; $extraline = "struct ${hdrnm}_global_functions_${grpnm} {\n"; $nadded += 1; } else { # Otherwise open a doxygen group. $extraline = "//@\{\n"; $nadded += 1; } $grouplevel += 1; } elsif ($comment =~ m||) { $named = 0; ## $comment =~ s|| \\name='' @\{|i; ## $comment =~ s||@\{|i; $comment =~ s|||i; $grouplevel += 1; } elsif ($comment =~ m||) { if ($namedgrouplevel == $grouplevel) { $line = "\};\n"; $newstate = $NON_COMMENT_BLOCK; $namedgrouplevel -= 1; } else { if ($named == 1) { $comment =~ s||@\}|i; } else { $comment =~ s|||i; } } $grouplevel -= 1; } # Change an anchor tag. # Replace it by a doxygen \anchor tag and an tag. # Prepend the doxygen name with the header name. # The latter one is used for internal references. if ( $comment =~ m||i ) { my $nm = $1; my $rem = $2; my $dnm = $nm; $dnm =~ s|\s*['".]\s*||g; $dnm =~ s|\s+|_|g; $dnm =~ s|:|_|g; $comment =~ s|| \\anchor $dnm |i; } $comment =~ s|||ig; # Handle linkto tags. There can be multiple of them and they can be # spread over multiple lines. if ( $link==1 ) { if ( $comment =~ m|(.*?)(.*)|i ) { my $b = $2; $linkstr = handleLinkto ($linkstr . " $1"); $comment = "$linkstr $b"; $link = 0; } else { $linkstr .= " $comment"; $comment = ""; } } while ( $link==0 && $comment =~ m|(.*)|i ) { my $a = $1; my $c = $3; my $linkstr = handleLinkto ("(.*)|i ) { $comment = $2; $linkfr = 0; } else { $comment = ""; } } while ( $linkfr==0 && $comment =~ m|(.*)|i ) { $comment = "$1 $3"; } elsif ( $comment =~ m|(.*?)| \\code |ig; $comment =~ s|| \\endcode |ig; $comment =~ s||

    Motivation

    |ig; $comment =~ s|
    ||ig; $comment =~ s||

    Synopsis

    |ig; $comment =~ s|
    ||ig; if ( $comment =~ m||i ) { $nexamples += 1; $comment =~ s||

    Example

    \\anchor ${hdrnm}_example${nexamples}|ig; } $comment =~ s|
    ||ig; $comment =~ s||

    Etymology

    |ig; $comment =~ s|
    ||ig; $comment =~ s||

    Motivation

    |ig; $comment =~ s|
    ||ig; # Remove obsolete category tag (used in e.g. Map.h>). $comment =~ s|||i; # Use current date in todo if needed. if ( $comment =~ //i ) { my $date = $1; if ( $date ) { $comment =~ s||

    To Do ($date)

      |i; } else { $comment =~ s||

      To Do

        |i; } $todo = 1; $todostr = $comment; $comment = ""; } # Do not insert todo if empty (i.e. no
      • lines). if ( $todo ) { if ( $comment =~ m||i ) { $comment = ""; $todo = 0; } elsif ( $comment =~ m|
      • |i ) { $comment = $todostr . $comment; $todo = 0; } else { $todostr .= $comment; $comment = "" } } $comment =~ s||
      |i; # Do not use
        for an empty prerequisite. if ( $comment =~ /^\s*/i ) { $comment = ""; $prereq = 1; } elsif ( $prereq ) { if ( $comment =~ m|^\s*|i ) { # No lines, so do not insert
      . $comment = ""; $prereq = 0; } elsif ( $comment !~ m|^\s*$| ) { # First non-empty line, so prepend with
        . $comment = "

        Prerequisite

          " . $comment; $prereq = 0; } } $comment =~ s|^\s*|
        |i; # Do not use
          for an empty thrown. if ( $comment =~ /^\s*/i ) { $comment = ""; $thrown = 1; } elsif ( $thrown ) { if ( $comment =~ m|^\s*|i ) { # No lines, so do not insert
        . $comment = ""; $thrown = 0; } elsif ( $comment !~ m|^\s*$| ) { # First non-empty line, so prepend with
          . $comment = "

          Thrown Exceptions

            " . $comment; $thrown = 0; } } $comment =~ s|^\s*|
          |i; # Fill in templating tags. # Use % to prevent doxygen from making a link to class Template. if ( $comment =~ // || $comment =~ // ) { my $arg = $1; if ( $arg ) { $comment =~ s||

          %Template Type Argument Requirements ($arg)

            |ig; } else { $comment =~ s||

            %Template Type Argument Requirements

              |ig; } } $comment =~ s||
            |ig; # Fill in the visibility. # Use that in the ingroup used in the summary. if ( $comment =~ // ) { my $arg = $1; if ( $arg =~ /export/i ) { $comment =~ s||

            Intended use:

            Public interface|ig; } elsif ( $arg =~ /local/i ) { $comment =~ s||

            Intended use:

            Internal|ig; # Change group in summary. if ($summinx > 0) { $modnmi = $modnm . "_internal_classes"; $last = @spanning_comment[$suminx]; $last =~ s|ingroup $modnm|ingroup $modnmi|; @spanning_comment[$suminx] = $last; } } else { $comment =~ s||

            Intended use:

            $1|ig; } } $comment =~ s|||ig; # Organize review info nicely. if ( $review==1 ) { if ( $comment =~ m|(.*?)>(.*)|i ) { my $b = $2; $reviewstr = handleReviewed ($reviewstr . " $1"); $comment = "$reviewstr $b"; $review = 0; } else { $reviewstr .= " $comment"; $comment = ""; } } while ( $review==0 && $comment =~ m|(.*)|i ) { my $a = $1; my $c = $3; my $reviewstr = handleReviewed ("||i; # Turn note to . # Capitalize the type. my $outtype = "Note"; if ( $comment =~ //i ) { my $type = lc $1; $outtype = ucfirst $type; } } # Doxygen complains about a . inside an block. # So for the time being replace a . by a ; # Note that this is done for the full line with is not entirely correct. if ( $emnote ) { $comment =~ s|\.|;|g; } $comment =~ s||
            $outtype: |i; if ( $comment =~ m|
            |i ) { $emnote = 0; } $comment =~ s||
            |i; # Replace . Inside the block the special characters have to # be replaced. Note that multiple can be on a line, but it # is also possible that the closing is on another line. if ( $code==1 ) { if ( $comment =~ m|(.*?)(.*)|i ) { my $a = $1; my $b = $2; $a =~ s/&/&/g; $a =~ s/"/"/g; $a =~ s//>/g; $comment = "$a$b"; $code = 0; } else { $comment =~ s/&/&/g; $comment =~ s/"/"/g; $comment =~ s//>/g; } } while ( $code==0 && $comment =~ m||i ) { if ( $comment =~ m|(.*?)(.*?)(.*)|i ) { my $a = $1; my $b = $2; my $c = $3; $b =~ s/&/&/g; $b =~ s/"/"/g; $b =~ s//>/g; $comment = "$a$b$c"; } elsif ( $comment =~ m|(.*?)(.*)|i ) { my $a = $1; my $b = $2; $b =~ s/&/&/g; $b =~ s/"/"/g; $b =~ s//>/g; $comment = "$a$b"; $code = 1; } } # Doxygen treats a trailing .. as end of list. So escape second one. $comment =~ s|\.\.\s*$|.\\.|g; # Skip an empty line if needed. if ($nadded > 0) { if ($comment =~ /^\s*$/) { $nadded -= 1; $skip = 1; } } } # doxygen group tags need slashes. if ($comment =~ /^\s*@[\{\}]\s*$/) { $comment =~ s/\s+//g; $line = "//$comment\n"; $newstate = $NON_COMMENT_BLOCK; } } elsif ($line =~ /^\s*$/) { # A blank line does not change anything. $newstate = $state; if ($nadded > 0) { $nadded -= 1; $skip = 1; } $comment = ""; } else { $keephash = 0; } # Remove stray spaces left before ) or after ( . and ,. $comment =~ s/\(\s+/(/g; $comment =~ s/\s+\)/)/g; $comment =~ s/\s+\././g; $comment =~ s/\s+,/,/g; # Act depending on new and old state. if ($skip == 0) { if ($newstate == $COMMENT_BLOCK) { if ($state == $COMMENT_BLOCK) { # Add comment to block. push @spanning_comment, "\n$comment"; } else { # Begin the spanning comment block with given indentation. push @spanning_comment, "/**$comment"; } if ($extraline ne "") { $line = $extraline; $extraline = ""; $state = $newstate; $newstate = $NON_COMMENT_BLOCK; } } if ($newstate == $NON_COMMENT_BLOCK) { if ($state == $COMMENT_BLOCK) { # End comment block and write the block. push @spanning_comment, " */\n"; print @spanning_comment; ### print DOXOUT @spanning_comment; @spanning_comment = (); $summinx = -1; } # Write the line. print $line; ### print DOXOUT $line; if ($extraline ne "") { print $extraline; ### print DOXOUT $extraline; $extraline = ""; } } $state = $newstate; } } # Output remaining comments if there. if ($state == $COMMENT_BLOCK) { push @spanning_comment, " */\n"; print @spanning_comment; ### print DOXOUT @spanning_comment; } } casacore-3.7.1/build-tools/floatcheck.sh000077500000000000000000000032241476623553700202240ustar00rootroot00000000000000#! /bin/sh # # This script checks if two files containing floating point numbers # are approximately equal. # All non-numeric characters are ignored. # The differences are written to stdout. # # run as: floatcheck.sh file1 file2 [precision] # default precision is 1e-5 if test $# -lt 2; then echo "usage: floatcheck.sh file1 file2 [precision]" exit 1 fi PREC=$3 if test $# -lt 3; then PREC=1e-5 fi pid=$$ # Check if all numbers are approximately equal. # First replace all non-numeric characters and single e.+- by a blank. # Insert a blank if a - or + is preceeded by a digit. # Replace blanks by a newline. sed -e 's/[^-+.e0-9]/ /g' $1 | sed -e 's/\(^\| \)[e.+-]\+\( \|$\)//g' | sed -e 's/^ \+//' -e 's/ \+$//' -e 's/\([0-9.]\)\([+-]\)/\1 \2/g' -e 's/ \+/\n/g' > /tmp/$pid.floatcheck_tmp.1 sed -e 's/[^-+.e0-9]/ /g' $2 | sed -e 's/\(^\| \)[e.+-]\+\( \|$\)//g' | sed -e 's/^ \+//' -e 's/ \+$//' -e 's/\([0-9.]\)\([+-]\)/\1 \2/g' -e 's/ \+/\n/g' > /tmp/$pid.floatcheck_tmp.2 # Show the differences column-wise. diff -y --suppress-common-lines /tmp/$pid.floatcheck_tmp.1 /tmp/$pid.floatcheck_tmp.2 > /tmp/$pid.floatcheck_tmp.diff # Now loop through all differences and see if almost equal. awk '{ a1=$1; if (a1<0) a1=-a1; a2=$3; if (a2<0) a2=-a2; if (2*a1>'"$PREC"' && 2*a2>'"$PREC"') if ((a1>a2 && a1-a2>'"$PREC"'*a1) || (a2>a1 && a2-a1>'"$PREC"'*a2)) print "float diff>'"$PREC"':",$1,$3 }' /tmp/$pid.floatcheck_tmp.diff > /tmp/$pid.floatcheck_tmp.res STATUS=0; if [ -s /tmp/$pid.floatcheck_tmp.res ] then cat /tmp/$pid.floatcheck_tmp.res STATUS=2 fi \rm -f /tmp/$pid.floatcheck_tmp.* exit $STATUS casacore-3.7.1/build-tools/make-drawio-links000077500000000000000000000147401476623553700210330ustar00rootroot00000000000000#! /bin/sh # This script inserts clickable
            tags in the drawio.svg files # to have links in the class diagrams created by drawio. # The script should be run in the casacore root directory. CURWD=`pwd` CURWD=`basename $CURWD` if [ "$CURWD" != "casacore" ]; then echo "Error: run make-drawio-links in casacore root directory" exit 1 fi cd doc/html > /dev/null || exit 1 # Make a sed file to adapt the drawio.svg files. # In general a class entry in the diagram file looks like: # Classname # Fist remove all output drawio files, otherwise they are found as well. rm -f *.drawio.svg # Handle each input drawio file. for INFILE in `find ../.. -name "*.drawio.svg"` do # Extract file name and class name from the input file name. OUTFILE=`basename $INFILE` echo "Processing $OUTFILE" DIAGRAMNAME=`echo $OUTFILE | sed -e "s/\..*//"` # Start with a clean slate by removing the sed file. rm -f tmpsed # Find the class names used in the diagram; they are enclosed in tags. # They might be followed by a template type which gets <...> in html. # If preceded by a - or +, they are class members. Keep the template parameter, # because it might be the relevant class (as in PtrBlock). Note that # a member can contain spaces and asterisks which are replaced by special strings. # Thus find the parts containing class names as described above. # Tag them with and add a newline (because drawio is one long line). # Remove the part before and including . Finally only take the # lines containing no spaces because the class name is a single word. # Interface classes are different; they are in boldface, thus check on CLASSES=`sed -e 's#]*>\([a-zA-Z_][a-zA-Z_0-9]*\)\(&[^<]*\)*#\1\n#g' $INFILE | grep '' | sed -e 's#.*##' | sort | uniq` MEMBERS=`sed -e 's#]*>[-+] *\([a-zA-Z_][a-zA-Z_0-9 \*,&;:]*\)#\1\n#g' $INFILE | grep '' | sed -e 's#.*##' -e 's#\*##g' -e 's# ##g' | sort | uniq` INTERFACES=`sed -e 's#\([a-zA-Z_][a-zA-Z_0-9]*\)\(&[^<]*\)*#\1\n#g' $INFILE | grep '' | sed -e 's#.*##' | sort | uniq` # Try to find the html file belonging to each class name. for CLASS in $CLASSES do FILENAME=`ls class*_[0-9]$CLASS.html 2>& 1` FILENAMX=`echo "$FILENAME" | sed -e 's# ##'` if [ "$FILENAME" != "" -a "$FILENAME" = "$FILENAMX" ]; then # A single file name was found, so the class name can be linked to it. # Note that an error also results in multiple words. # Generate sed commands to insert an tag to link to this class. # Insert a tag around the tag as described # on https://www.w3.org/wiki/SVG_Links. Note that xlink:href is # replaced by href in the newest standards and newer browsers. # Also note that in the diagram a class name might be followed by # template parameters such as translating to <V,S> in html. # Show the referenced file in a new tab (security requires the rel part). # Insert the placeholder xrgbx to change the color later. echo 's#\(]*rgb([0-9 ,]*)\)\([^>]*\)\(pointer-events="none"\)\([^>]*>\)\(]*>'$CLASS'\(&[^<]*\)*\)#\1xrgbbx\2pointer-events="auto"\4\5/#g' >> tmpsed echo 's#\(]*>'$CLASS'\(&[^<]*\)*\)#\1#g' >> tmpsed fi done # Do the same for the members (indicated by leading - or +). Make them less blue. # Take care of possible template parameters. It's unfortunately impossible # to insert a link for both templated class and template parameter due to the # nature of the svg file. Therefore the last template parameter is preferred if # an html file exists for it. # Remove a possible field name followed by a colon. # Restore the substituted space and asterisk (but escape the latter). for CLASS in $MEMBERS do PARTS=`echo "$CLASS" | sed -e 's#.*:##' -e 's#<# #' -e 's#>##' -e 's#,# #g' -e 's###' -e 's## #g'` ESCNAME=`echo "$CLASS" | sed -e 's##\\\\*#g' -e 's## #g'` FILNAM= for PART in $PARTS do FILENAME=`ls class*_[0-9]$PART.html 2>& 1` FILENAMX=`echo "$FILENAME" | sed -e 's# ##'` if [ "$FILENAME" != "" -a "$FILENAME" = "$FILENAMX" ]; then FILNAM="$FILENAME" fi done if [ "$FILNAM" != "" ]; then echo 's#\(]*rgb([0-9 ,]*)\)\([^>]*\)\(pointer-events="none"\)\([^>]*>\)\(]*>[-+] *'"$ESCNAME"'\)#\1xrgbx\2pointer-events="auto"\4\5#g' >> tmpsed fi done # Again, the same for the interfaces. for CLASS in $INTERFACES do FILENAME=`ls class*_[0-9]$CLASS.html 2>& 1` FILENAMX=`echo "$FILENAME" | sed -e 's# ##'` if [ "$FILENAME" != "" -a "$FILENAME" = "$FILENAMX" ]; then echo 's#\('$CLASS'\(&[^<]*\)*\)#\1#g' >> tmpsed fi done # Change the black colors to blue where the placeholder was inserted. # Remove the possible remaining placeholders. echo 's#rgb(0, *0, *0)xrgbx#rgb(0, 0, 100)#g' >> tmpsed echo 's#rgb(0, *0, *0)xrgbbx#rgb(0, 0, 200)#g' >> tmpsed echo 's#xrgb*x##g' >> tmpsed # Finally split the very long drawio line on the tag. echo 's##\\n#g' >> tmpsed # Apply the sed commands for the classes in the drawio file. sed -f tmpsed $INFILE > $OUTFILE # Enclose it in an html file. Use object (not img), otherwise links do not work. # If there is a .drawio.svg.extrahtml file, add it before the tag. cat > $OUTFILE.html <

            Casacore $DIAGRAMNAME class diagram

            EOF if [ -e "$INFILE.extrahtml" ]; then cat $INFILE.extrahtml >> $OUTFILE.html fi cat >> $OUTFILE.html < EOF done # Cleanup rm -f tmpsed casacore-3.7.1/casa/000077500000000000000000000000001476623553700142335ustar00rootroot00000000000000casacore-3.7.1/casa/Arrays.h000066400000000000000000000242451476623553700156540ustar00rootroot00000000000000//# Arrays.h: A module implementing multidimensional arrays and operations //# Copyright (C) 1995,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_ARRAYS_H #define CASA_ARRAYS_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // // // A module implementing multidimensional arrays and operations. // // // // // This module provides classes and global functions for multidimensional // arrays. // // // // Arrays have traditionally played an important role in scientific // computation. While it is certainly true that some of the reliance on // arrays was due to the paucity of other data structures in FORTRAN, it // is also true that computation on arrays reflects the common occurrence // of regularly sampled multi-dimensioned data in science. // // The Lattices are a generalization // of Arrays. They can handle memory- and disk-based arrays as well // as other types of arrays (eg. expressions). // // The module consists of various parts: //
              //
            • // Array is the basic array class. It is // only templated on data type, not on dimensionality like the array // classes in Blitz and boost. // It has a non-templated base class ArrayBase. // // Vector, // Matrix, and // Cube // are the one, two, and three dimensional specializations respectively of // Array. // //
            • // MaskedArray is the class used to mask // an Array for operations on that Array. // //
            • // ArrayError is the base class for all // Array exception classes. // //
            • // There are several ways o iterate through an array: //
                //
              • The STL-style Array iterators can be used to iterate // element by element through an array. This is the fastest way. // They also make it possible to virtually extend an array (called // shape broadcasting in numpy) and to reorder the iteration axes. //
              • ArrayIterator can be used to // iterate line by line, plane by plane, etc. through an array. // Each subset is an array in itself, thus can be iterated again. //
              • The Array function operators () can be used to get a subset from // an array. They can be used for iteration, but that is slower than // the ways mentioned above. //
              • The array operator[] can be used to get the i-th subset. It can // be used for iteration, but ArrayIterator does the same and is faster. //
              • ArrayAccessor is useful when neighbours of an array element have // to be visited. //
              • LatticeIterator can be used on // a ArrayLattice object for more // advanced iteration. However, they are part of the lattices packages. //
              // //
            • // Mathematical, // logical, // chunked mathematical and logical, // IO, // and other useful operations are provided for // Arrays and MaskedArrays. // // ArrayMath also defines various STL-style transform functions that use the // Array iterators and functors like Plus to apply the mathematical and logical // operations. They can, however, also be used directly on arrays of // different types making it possible to, say, add a Complex and double array // with a DComplex result. //
              It also has a transformInPlace to avoid needless incrementing // of iterators which have to be done when using std::transform // for in-place operations. // //
            • // Orthogonal n-space descriptors - useful when a shape of an Array is // needed or when a sub-region within an Array is required. //
                //
              • The IPosition class name is a // concatenation of "Integer Position." IPosition objects are normally // used to index into, and define the shapes of, Arrays and Lattices. For // example, if you have a 5-dimensional array, you need an IPosition of // length 5 to index into the array (or to define its shape, etc.). It is // essentially a vector of integers. The IPosition vector may point to // the "top right corner" of some shape, or it may be an indicator of a // specific position in n-space. The interpretation is context dependent. // The constructor consists of an initial argument which specifies the // number of axes, followed by the appropriate number of respective axis // lengths. Thus the constructor needs N+1 arguments for an IPosition // of length N. IPositions have the standard integer math relationships // defined. The dimensionality of the operator arguments must be the // same. // // // Make a shape with three axes, x = 24, y = 48, z = 16; // IPosition threeSpace(3, 24, 48, 16); // // // get the value of the ith axis (note: C++ is zero based!) // Int xShape = threeSpace(0); // Int zShape = threeSpace(2); // // // construct another with all three axes values equal to 666; // IPosition threeSpaceAlso(3,666); // // // do math with the IPositions... // threeSpace += threeSpaceAlso; // AlwaysAssert(threeSpace(1) == 714, AipsError); // // //
              • The Slicer class name may be // thought of as a short form of "n-Dimensional Slice Specifier." // This object is used to bundle into one place all the information // necessary to specify a regular subregion within an Array or Lattice. // In other words, Slicer holds the location of a "slice" of a // greater whole. Construction is with up to 3 IPositions: the start // location of the subspace within the greater space; the shape or end // location of the subspace within the greater space; and the stride, // or multiplier to be used for each axis. The stride gives the user // the chance to use every i-th piece of data, rather than every // position on the axis. //
                // It is possible to leave some values in the given start or end/length // unspecified. Such unspecified values default to the boundaries of the // array to which the slicer will be applied. // It is also possible to use a non-zero origin when applying the slicer // to an array. // // // // Define the shape of an array. // IPosition shape(2,20,30); // // // Also define an origin. // IPosition origin(2,-5,15); // // // Now define some Slicers, initially only specify the start // // Its length and stride will be 1. // Slicer ns0(IPosition(2,0,24)); // // // make some IPositions as holders for the rest of the information // IPosition blc,trc,inc; // // // Use the shape and origin to fill our holders assuming we want to use // // as much of the Array as possible. // ns0.inferShapeFromSource (shape, origin, blc,trc,inc); // // // print out the new info ie. blc=[5,9],trc=[5,9],inc=[1,1] // cout << blc << trc << inc << endl; // // // Build a slicer with temporaries for arguments. The arguments are: // // start position, end position and step increment. The Slicer::endIsLast // // argument specifies that the end position is the trc. The alternative // // is Slicer::endIsLength which specifies that the end argument is the // // shape of the resulting subregion. // // // Slicer ns1(IPosition(2,3,5), IPosition(2,13,21), IPosition(2,3,2), // Slicer::endIsLast); // IPosition shp = ns1.inferShapeFromSource (shape, blc,trc,inc); // // // // print out the new info ie. shp=[4,9],blc=[3,5],trc=[12,21],inc=[3,2] // cout << shp << blc << trc << inc << endl; // //
              //
            // The detailed discussions for the // classes and global functions will describe how to use them. //
            // //
            } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Arrays/000077500000000000000000000000001476623553700154745ustar00rootroot00000000000000casacore-3.7.1/casa/Arrays/Array.h000066400000000000000000001225061476623553700167310ustar00rootroot00000000000000//# Array.h: A templated N-D Array class with zero origin //# Copyright (C) 1993,1994,1995,1996,1997,1998,1999,2000,2001,2002,2003,2015 //# Associated Universities, Inc. Washington DC, USA, //# National Astronomical Observatory of Japan //# 2-21-1, Osawa, Mitaka, Tokyo, 181-8588, Japan. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_ARRAY_2_H #define CASA_ARRAY_2_H //# Includes #include "ArrayBase.h" #include "ArrayError.h" #include "IPosition.h" #include "MaskLogiArrFwd.h" #include "Storage.h" #include #include #include #include namespace casacore { //#Begin casa namespace // A templated N-D %Array class with zero origin. // Array is a templated, N-dimensional, %Array class. The origin is zero, // but by default indices are zero-based. This Array class is the // base class for the Vector, Matrix, and Cube subclasses. // // Indexing into the array, and positions in general, are given with IPosition // (essentially a vector of integers) objects. That is, an N-dimensional // array requires a length-N IPosition to define a position within the array. // Unlike C, indexing is done with (), not []. Also, the storage order // is the same as in FORTRAN, i.e. memory varies most rapidly with the first // index. // // // axisLengths = [1,2,3,4,5] // IPosition axisLengths(5, 1, 2, 3, 4, 5); // Array ai(axisLengths); // ai is a 5 dimensional array of // // integers; indices are 0-based // // => ai.nelements() == 120 // Array ai2(axisLengths); // The first element is at index 0 // IPosition zero(5); zero = 0; // [0,0,0,0,0] // //... // // Indexing into an N-dimensional array is relatively expensive. Normally // you will index into a Vector, Matrix, or Cube. These may be obtained from // an N-dimensional array by creating a reference, or by using an // ArrayIterator. The "shape" of the array is an IPosition which gives the // length of each axis. // // An Array may be standalone, or it may refer to another array, or to // part of another array (by refer we mean that if you change a pixel in // the current array, a pixel in the referred to array also changes, i.e. // they share underlying storage). // // One way one array can reference another is through the copy // constructor. While this might be what you want, you should // probably use the reference() member function to make it explicit. // The copy constructor is used when arguments are passed by value; // normally functions should not pass Arrays by value, rather they // should pass a reference or a const reference. On the positive // side, returning an array from a function is efficient since no // copying need be done. // // // Aside from the explicit reference() member function, a user will // most commonly encounter an array which references another array // when he takes an array slice (or section). A slice is a sub-region of // an array (which might also have a stride: every nth row, every mth column, // ...). // // IPosition lengths(3,10,20,30); // Array ai(lengths); // A 10x20x30 cube // Cube ci; // //... // ci.reference(ai1); // ci and ai now reference the same // // storage // ci(0,0,0) = 123; // Can use Cube indexing // ci.xyPlane(2) = 0; // and other member functions // IPosition zero(3,0,0,0); // assert(ai(zero) == 123); // true because ai, ci are references // //... // Array subArray; // IPosition blc(3,0,0,0), trc(3,5,5,5); // subArray.reference(ai(blc, trc)); // subArray = 10; // All of subArray, which is the // // subcube from 0,0,0 to 5,5,5 in // // ai, has the value 10. // // While the last example has an array slice referenced explicitly by another // array variable, normally the user will often only use the slice as // a temporary in an expresion, for example: // // Array array; // IPosition blc, trc, offset; // //... // // Copy from one region of the array into another // array(blc, trc) = array(blc+offset, trc+offset); // // // The Array classes are intended to operate on relatively large // amounts of data. While they haven't been extensively tuned yet, // they are relatively efficient in terms of speed. Presently they // are not space efficient -- the overhead is about 15 words. While // this will be improved (probably to about 1/2 that), these array // classes are not appropriate for very large numbers of very small // arrays. The Block class may be what you want in this circumstance. // // Element by element mathematical and logical operations are available // for arrays (defined in aips/ArrayMath.h and aips/ArrayLogical.h). // Because arithmetic and logical functions are split out, it is possible // to create an Array (and hence Vector etc) for any type T that has // a default constructor, assignment operator, and copy constructor. In // particular, Array works. // // If compiled with the preprocessor symbol AIPS_DEBUG symbol, array // consistency ("invariants") will be checked in most member // functions, and indexing will be range-checked. This should not be // defined for production runs. // // // Most of the data members and functions which are "protected" should // likely become "private". // // // //
          • Integrate into the Lattice hierarchy //
          • Factor out the common functions (shape etc) into a type-independent // base class. // template class Array : public ArrayBase { public: // Result has dimensionality of zero, and nelements is zero. Array(); // Create an array of the given shape, i.e. after construction // array.ndim() == shape.nelements() and array.shape() == shape. // The origin of the Array is zero. // Storage is allocated by DefaultAllocator. // Without initPolicy parameter, the initialization of elements depends on type T. // When T is a fundamental type like int, elements are NOT initialized. // When T is a class type like casacore::Complex or std::string, elements are initialized. // This inconsistent behavior confuses programmers and make it hard to write efficient and generic code using template. // Especially when T is of type Complex or DComplex and it is unnecessary to initialize, // provide initPolicy with value NO_INIT to skip the initialization. // Therefore, it is strongly recommended to explicitly provide initPolicy parameter, explicit Array(const IPosition &shape); // Create an array of the given shape and initialize it with the // initial value. // Storage is allocated by DefaultAllocator. Array(const IPosition &shape, const T &initialValue); // This is a tag for the constructor that may be used to construct an uninitialized Array. static struct uninitializedType{} uninitialized; // Constructor to create an uninitialized array. This constructor can for example // be called with: // // Array a(shape, Array::uninitialized); // Array(const IPosition& shape, uninitializedType); // Construct a one-dimensional array from an initializer list. // Example: // // Array a({5, 6, 7, 8}); // Array(std::initializer_list list); // After construction, this and other reference the same storage. Array(const Array &other); // Source will be empty after this call. Array(Array&& source) noexcept; // Create an Array of a given shape from a pointer. // If policy is COPY, storage of a new copy is allocated by DefaultAllocator. // If policy is TAKE_OVER, storage will be destructed and released by the specified allocator. // // FILE *fp = ...; // typedef DefaultAllocator Alloc; // Alloc::type alloc; // IPosition shape(1, 10); // int *ptr = alloc.allocate(shape.product()); // size_t nread = fread(ptr, sizeof(int), shape.product(), fp); // Array ai(shape, ptr, TAKE_OVER); // Array(const IPosition &shape, T *storage, StorageInitPolicy policy = COPY); // Create an Array of a given shape from a pointer. Because the pointer // is const, a copy is always made. // The copy is allocated by DefaultAllocator. Array(const IPosition &shape, const T *storage); // Construct an array from an iterator and a shape. template Array(const IPosition &shape, InputIterator startIter); // Frees up storage only if this array was the last reference to it. virtual ~Array() noexcept; // Make an empty array of the same template type. virtual std::unique_ptr makeArray() const override; // Assign the other array to this array. // If the shapes mismatch, this array is resized. // void assign (const Array& other); void assignBase (const ArrayBase& other, bool checkType=true) override; // // Set every element of the array to "value." Also could use the // assignment operator which assigns an array from a scalar. void set(const T &value); // Apply the function to every element of the array. This modifies // the array in place. // (TODO this version made the other versions of apply() redundant) template void apply(Callable function); // After invocation, this array and other reference the same storage. That // is, modifying an element through one will show up in the other. The // arrays appear to be identical; they have the same shape. //
            Please note that this function makes it possible to reference a // const Array, thus effectively it makes a const Array non-const. // Although this may seem undesirable at first sight, it is necessary to // be able to make references to temporary Array objects, in particular to // Array slices. Otherwise one first needs to use the copy constructor. //# The const has been introduced on 2005-Mar-31 because of the hassle //# involved in calling the copy ctor before reference. virtual void reference(const Array &other); // Copy the values in other to this. If the array on the left hand // side has no elements, then it is resized to be the same size as // as the array on the right hand side. Otherwise, the arrays must // conform (same shapes). // // IPosition shape(2,10,10); // some shape // Array ad(shape); // //... // Array ad2; // N.B. ad2.nelements() == 0 // ad2 = ad; // ad2 resizes, then elements // // are copied. // shape = 20; // Array ad3(shape); // ad3 = ad; // Error: arrays do not conform // // Note that the assign function can be used to assign a // non-conforming array. Array& assign_conforming(const Array& other) { return assign_conforming_implementation(other, std::is_copy_assignable()); } // Copy to this those values in marray whose corresponding elements // in marray's mask are true. // //
          • ArrayConformanceError // // Array& assign_conforming (const MaskedArray& marray); // TODO we should change the semantics Array& operator=(const Array& other) { return assign_conforming(other); } // Calls assign_conforming(). Array& operator=(const MaskedArray& marray) { return assign_conforming(marray); } // The move operator takes the storage from the given array. After moving an // Array, the source Array will be left empty. Array& operator=(Array&& other); // Set every element of this array to "value". In other words, a scalar // behaves as if it were a constant conformant array. Array& operator=(const T& value); // This makes a copy of the array and returns it. This can be // useful for, e.g. making working copies of function arguments // that you can write into. // // void someFunction(const Array &arg) // { // Array tmp(arg.copy()); // // ... // } // // Note that since the copy constructor makes a reference, if we just // created used to copy constructor, modifying "tmp" would also // modify "arg". Clearly another alternative would simply be: // // void someFunction(const Array &arg) // { // Array tmp; // tmp = arg; // // ... // } // // which likely would be simpler to understand. (Should copy() // be deprecated and removed?) // // TODO deprecate Array copy() const; // This function copies the matching part of from array to this array. // The matching part is the part with the minimum size for each axis. // E.g. if this array has shape [4,5,6] and from array has shape [7,3], // the matching part has shape [4,3]. //
            Note it is used by the resize function if // copyValues==true. void copyMatchingPart (const Array& from); // This ensures that this array does not reference any other storage. // // When a section is taken of an array with non-unity strides, // storage can be wasted if the array, which originally contained // all the data, goes away. unique() also reclaims storage. This // is an optimization users don't normally need to understand. // // // IPosition shape(...), blc(...), trc(...), inc(...); // Array af(shape); // inc = 2; // or anything > 1 // Array aSection.reference(af(blc, trc, inc)); // af.reference(anotherArray); // // aSection now references storage that has a stride // // in it, but nothing else is. Storage is wasted. // aSection.unique(); // // void unique(); // Create an STL vector from an Array. The created vector is a linear // representation of the Array memory. See // Vector for // details of the operation and its reverse (i.e. creating a // Vector from a vector), and for details of // definition and instantiation. // template void tovector(std::vector& out) const; std::vector tovector() const; // // It is occasionally useful to have an array which access the same // storage appear to have a different shape. For example, // turning an N-dimensional array into a Vector. //
            When the array data are contiguous, the array can be reshaped // to any form as long as the number of elements stays the same. // When not contiguous, it is only possible to remove or add axes // with length 1. // // IPosition squareShape(2,5,5); // Array square(squareShape); // IPosition lineShape(1,25); // Vector line(square.reform(lineShape)); // // "square"'s storage may now be accessed through Vector "line" // Array reform(const IPosition& shape) const; // Having an array that can be reused without requiring reallocation can // be useful for large arrays. The method reformOrResize permits this // usage. // // The reformOrResize method first attempts to reform the matrix so that // it reuses the existing storage for an array with a new shape. If the // existing storage will not hold the new shape, then the method will // resize the array when resizeIfNeeded is true; if a resize is needed and // resizeIfNeeded is false, then an ArrayConformanceError is thrown. The // copyDataIfNeeded parameter is passed to resize if resizing is performed. // resizePercentage is the percent of additional storage to be addeed when // a resize is performed; this allows the allocations to be amortized when // the caller expects to be calling this method again in the future. The // parameter is used to define an allocation shape which is larger than // the newShape by increasing the last dimension by resizePercentage percent // (i.e., lastDim = (lastDim * (100 + resizePercentage)) / 100). If // resizePercentage <= 0 then resizing uses newShape as-is. Returns true // if resizing (allocation) was performed. // // To truncate the array so that it no longer holds additional storage, // use the resize method. // // Array may not be shared with another Array object during this call. // Exception thrown if it is shared. bool reformOrResize (const IPosition & newShape, size_t resizePercentage = 0, bool resizeIfNeeded = true); // Use this method to extend or reduce the last dimension of an array. If // sufficient excess capacity exists then the bookkeeping is adjusted to // support the new shape. If insufficient storage exists then a new array // is allocated (unless resizeIfNeeded is false; then an exception is thrown). // If resizing is not required then the data remains untouched; if resizing // is required then the data is copied into the new storage. The resizePercentage // works the same as for reformOrResize (see above). This method never releases // extra storage; use "resize" to do this. Array may not be sharing storage // with another array at call time; an exception will be thrown if the array is shared. // Returns true if the array was extension required a Array::resize operation. bool adjustLastAxis (const IPosition & newShape, size_t resizePercentage = 0, bool resizeIfNeeded = true); // Returns the number of elements allocated. This value is >= to the value returned // by size(). size_t capacity () const; // These member functions remove degenerate (ie. length==1) axes from // Arrays. Only axes greater than startingAxis are considered (normally // one wants to remove trailing axes). The first two of these functions // return an Array reference with axes removed. The latter two functions // let this Array object reference the 'other' array with degenerated axes // removed. //
            // Unless throwIfError is false, an exception will be thrown if // startingAxis exceeds the array's dimensionality. //
            // The functions with argument ignoreAxes do // not consider the axes given in that argument. In this way it can be // achieved that degenerate axes are kept. // When the two functions returning void // are invoked on a derived object (e.g. Matrix), an exception is // thrown if removing the degenerate axes from other does not result // in a correct number of axes. // // Array nonDegenerate(size_t startingAxis=0, bool throwIfError=true) const; Array nonDegenerate(const IPosition& ignoreAxes) const; void nonDegenerate(const Array &other, size_t startingAxis=0, bool throwIfError=true); void nonDegenerate(const Array &other, const IPosition &ignoreAxes) { doNonDegenerate (other, ignoreAxes); } // // Remove degenerate axes from this Array object. // Note it does not make sense to use these functions on a derived object // like Matrix, because it is not possible to remove axes from them. // void removeDegenerate(size_t startingAxis=0, bool throwIfError=true); void removeDegenerate(const IPosition &ignoreAxes); // // This member function returns an Array reference with the specified // number of extra axes, all of length one, appended to the end of the // Array. Note that the reform function can also be // used to add extra axes. // const Array addDegenerate(size_t numAxes) const; Array addDegenerate(size_t numAxes); // // Make this array a different shape. If copyValues==true // the old values are copied over to the new array. // Copying is done on a per axis basis, thus a subsection with the // minimum of the old and new shape is copied. //
            Resize without argument is equal to resize(IPosition()). //
            It is important to note that if multiple Array objects // reference the same data storage, this Array object still references // the same data storage as the other Array objects if the shape does // not change. Otherwise this Array object references newly allocated // storage, while the other Array objects still reference the existing // data storage. //
            If you want to be sure that the data storage of this Array object // is not referenced by other Array objects, the function unique should // be called first. // void resize(); void resize(const IPosition &newShape, bool copyValues=false) override; // // Access a single element of the array. This is relatively // expensive. Extensive indexing should be done through one // of the Array specializations (Vector, Matrix, Cube). // T &operator()(const IPosition &); const T &operator()(const IPosition &) const; // // Get a reference to an array section extending // from start to end (inclusive). // Array operator()(const IPosition &start, const IPosition &end); const Array operator()(const IPosition &start, const IPosition &end) const; // Along the ith axis, every inc[i]'th element is chosen. Array operator()(const IPosition &start, const IPosition &end, const IPosition &inc); const Array operator()(const IPosition &start, const IPosition &end, const IPosition &inc) const; // // Get a reference to an array section using a Slicer. // Array operator()(const Slicer&); const Array operator()(const Slicer&) const; // // Get a reference to a section of an array. // This is the same as operator(), but can be used in a type-agnostic way. std::unique_ptr getSection (const Slicer&) const override; // Get the subset given by the i-th value of the last axis. So for a cube // it returns the i-th xy plane. For a Matrix it returns the i-th row. // The returned array references the original array data; its dimensionality // is one less. For a 1-dim array it still returns a 1-dim array. // This function should not be used in tight loops as it is (much) // slower than iterating using begin() and end(), ArrayIter, or // ArrayAccessor. Array operator[] (size_t i) const; // Get the diagonal of each matrix part in the full array. // The matrices are taken using axes firstAxes and firstAxis+1. // diag==0 is main diagonal; diag>0 above the main diagonal; diag<0 below. Array diagonals (size_t firstAxis=0, long long diag=0) const; // The array is masked by the input LogicalArray. // This mask must conform to the array. // const MaskedArray operator() (const LogicalArray &mask) const; MaskedArray operator() (const LogicalArray &mask); // // The array is masked by the input MaskedLogicalArray. // The mask is effectively the AND of the internal LogicalArray // and the internal mask of the MaskedLogicalArray. // The MaskedLogicalArray must conform to the array. // const MaskedArray operator() (const MaskedLogicalArray &mask) const; MaskedArray operator() (const MaskedLogicalArray &mask); // // The number of references the underlying storage has assigned to it. // It is 1 unless there are outstanding references to the storage (e.g., // through a slice). Normally you have no need to do this since the // arrays handle all of the references for you. // NB: Even when nrefs()==1, the array might be shared, because the // the storage itself might be shared. Therefore, this function should // not be used outside debugging. // TODO make protected. size_t nrefs() const; // Check to see if the Array is consistent. This is about the same thing // as checking for invariants. If AIPS_DEBUG is defined, this is invoked // after construction and on entry to most member functions. virtual bool ok() const override; // Are the shapes identical? // bool conform (const Array &other) const { return conform2(other); } bool conform (const MaskedArray &other) const; // // Get a pointer to the beginning of the array. // Note that the array may not be contiguous. // T* data() { return begin_p; } const T* data() const { return begin_p; } // // Generally use of this should be shunned, except to use a FORTRAN routine // or something similar. Because you can't know the state of the underlying // data layout (in particular, if there are increments) sometimes the // pointer returned will be to a copy, but often this won't be necessary. // A boolean is returned which tells you if this is a copy (and hence the // storage must be deleted). Note that if you don't do anything unusual, // getStorage followed by freeStorage or putStorage will do the deletion // for you (if required). e.g.: // // Array a(shape); ... // bool deleteIt; int *storage = a.getStorage(deleteIt); // foo(storage, a.nelements()); a.puStorage(storage, deleteIt); // // or a.freeStorage(storage, deleteIt) if a is const. // // NB: However, if you only use getStorage, you will have to delete the // pointer yourself using freeStorage(). // // It would probably be useful to have corresponding "copyin" "copyout" // functions that used a user supplied buffer. // Note that deleteIt is set in this function. // T *getStorage(bool& deleteIt); const T *getStorage(bool& deleteIt) const { // The cast is OK because the return pointer will be cast to const return const_cast*>(this)->getStorage(deleteIt); } void *getVStorage(bool &deleteIt) override; const void *getVStorage(bool &deleteIt) const override; // // putStorage() is normally called after a call to getStorage() (cf). // The "storage" pointer is set to zero. void putStorage(T *&storage, bool deleteAndCopy); void putVStorage(void *&storage, bool deleteAndCopy) override; // If deleteIt is set, delete "storage". Normally freeStorage calls // will follow calls to getStorage. The reason the pointer is "const" // is because only const pointers are released from const arrays. // The "storage" pointer is set to zero. // TODO this function can not be const for stateful allocators void freeStorage(const T *&storage, bool deleteIt) const; void freeVStorage(const void *&storage, bool deleteIt) const override; // Replace the data values with those in the pointer storage. // The results are undefined if storage does not point at nelements() or // more data elements. After takeStorage() is called, nrefs() // is 1. // // If policy is COPY, storage of a new copy is allocated by allocator. // If policy is TAKE_OVER, storage will be destructed and released by allocator. virtual void takeStorage(const IPosition &shape, T *storage, StorageInitPolicy policy = COPY); // Since the pointer is const, a copy is always taken. // Storage of a new copy is allocated by the specified allocator. virtual void takeStorage(const IPosition &shape, const T *storage); // // Used to iterate through Arrays. Derived classes VectorIterator and // MatrixIterator are probably more useful. friend class ArrayIterator; // Create an ArrayIterator object of the correct type. std::unique_ptr makeIterator(size_t byDim) const override; // // See the function begin() and end() for a detailed description // of the STL iterator capability. class BaseIteratorSTL { public: // Create the begin const_iterator object for an Array. explicit BaseIteratorSTL (const Array&); // Create the end const_iterator object for an Array. // It also acts as the default constructor. explicit BaseIteratorSTL (const T* end = 0) : itsPos(end), itsLineEnd(0), itsLineIncr(0), itsLineAxis(0), itsArray(0), itsContig(false) {} void nextElem() { itsPos++; if (!itsContig) { itsPos += itsLineIncr; if (itsPos > itsLineEnd) increment(); } } void nextLine() { itsPos = itsLineEnd; increment(); } bool operator== (const BaseIteratorSTL& other) const { return itsPos == other.itsPos; } bool operator!= (const BaseIteratorSTL& other) const { return itsPos != other.itsPos; } T* getPos() { return const_cast(itsPos); } friend std::ostream& operator<< (std::ostream& os, const BaseIteratorSTL& iter) { os << iter.itsPos; return os; } protected: // Increment iterator for a non-contiguous array. void increment(); const T* itsPos; const T* itsLineEnd; size_t itsLineIncr; size_t itsLineAxis; IPosition itsCurPos; IPosition itsLastPos; const Array* itsArray; bool itsContig; }; class IteratorSTL: public BaseIteratorSTL { public: // typedef T value_type; typedef value_type* pointer; typedef value_type& reference; typedef std::size_t size_type; typedef ptrdiff_t difference_type; typedef std::forward_iterator_tag iterator_category; // // Create the begin iterator object for an Array. explicit IteratorSTL (Array& arr) : BaseIteratorSTL (arr) {} // Create the end iterator object for an Array. // It also acts as the default constructor. explicit IteratorSTL (const T* end = 0) : BaseIteratorSTL (end) {} const IteratorSTL& operator++() { this->nextElem(); return *this; } IteratorSTL operator++(int) { IteratorSTL old(*this); this->nextElem(); return old; } T& operator*() { return *this->getPos(); } T* operator->() { return this->getPos(); } }; class ConstIteratorSTL: public BaseIteratorSTL { public: // typedef T value_type; typedef const value_type* pointer; typedef const value_type& reference; typedef std::size_t size_type; typedef ptrdiff_t difference_type; typedef std::forward_iterator_tag iterator_category; // // Create the begin const_iterator object for an Array. explicit ConstIteratorSTL (const Array& arr) : BaseIteratorSTL (arr) {} // Create the end const_iterator object for an Array. // It also acts as the default constructor. explicit ConstIteratorSTL (const T* end = 0) : BaseIteratorSTL (end) {} // Create from a non-const iterator. ConstIteratorSTL (const IteratorSTL& iter) : BaseIteratorSTL (iter) {} const ConstIteratorSTL& operator++() { this->nextElem(); return *this; } ConstIteratorSTL operator++(int) { ConstIteratorSTL old(*this); this->nextElem(); return old; } const T& operator*() const { return *this->itsPos; } const T* operator->() { return this->itsPos; } const T* pos() const { return this->itsPos; } }; // // Define the STL-style iterator functions (only forward iterator). // It makes it possible to iterate through all data elements of an array // and to use it common STL functions. // The end() function is relatively expensive, so it should not be // used inside a for statement. It is much better to call it beforehand // as shown in the example below. Furthermore it is very important to // use ++iter, because iter++ is 4 times slower. // // Array arr(shape); // Array::iterator iterend(arr.end()); // for (Array::iterator iter=arr.begin(); iter!=iterend; ++iter) { // *iter += 1; // } // // The Array class supports random access, so in principle a random // iterator could be implemented, but its performance would not be great, // especially for non-contiguous arrays. //
            Some other STL like functions exist for performance reasons. // If the array is contiguous, it is possible to use the // cbegin and cend functions which are // about 10% faster. // // STL-style typedefs. // // Element type typedef T value_type; // TODO This is how std containers define a reference type, but // the name 'reference' is already taken by a method. // typedef T& reference; typedef const T& const_reference; // Pointer to an element type typedef T* pointer; // Constant pointer to the element type typedef const T* const_pointer; typedef IteratorSTL iterator; typedef ConstIteratorSTL const_iterator; typedef T* contiter; typedef const T* const_contiter; // // Get the begin iterator object for any array. // iterator begin() { return iterator (*this); } const_iterator begin() const { return const_iterator (*this); } iterator end() { return iterator(end_p); } const_iterator end() const { return const_iterator(end_p); } // // Get the begin iterator object for a contiguous array. // contiter cbegin() { return begin_p; } const_contiter cbegin() const { return begin_p; } contiter cend() { return end_p; } const_contiter cend() const { return end_p; } // // private: // Implementation of constructor taking a Shape, a Templated parameter and an allocator. // This method implements it for when T is integral, in which case the templated parameter // is the initial value. template Array(const IPosition &shape, Integral startIter, std::true_type /*is_integral*/ ); // Implementation of constructor taking a Shape, a Templated parameter and an allocator. // This method implements it for when T is NOT integral, in which case the templated parameter // is an iterator. template Array(const IPosition &shape, InputIterator startIter, std::false_type /*is_integral*/ ); // Implementation for assign for copyable types Array& assign_conforming_implementation (const Array& other, std::true_type); // Implementation for assign for non-copyable types: can not be assigned Array& assign_conforming_implementation (const Array&, std::false_type) { throw ArrayError("Can not assign from non-copyable object"); } static void copyToContiguousStorage(T *dst, Array const& src, std::true_type); static void copyToContiguousStorage(T*, Array const&, std::false_type) { throw ArrayError("Can not coy from non-copyable object"); } // An Array is unique when the container is shared and when nrefs==1. bool isUnique() const { return !data_p->is_shared() && nrefs()==1; } protected: // Source will be empty with given shape after this call. Array(Array&& source, const IPosition& shapeForSource) noexcept; template friend void swap(Array& left, Array& right); // Swap this array with another array. // Normally, casacore::swap() should be used instead. void swap(Array& other); // pre/post processing hook of takeStorage() for subclasses. virtual void preTakeStorage(const IPosition &) {} virtual void postTakeStorage() {} // This function is called when this array is about to be resized, before // any work is done. Subclasses can throw an exception if the size doesn't // match, e.g. if a Matrix is resized to have 3 dimensions. // Before this function existed, assign-like functions had to be virtual. // However, for non-copyable types, assign can't exist. This is fine for // non-virtual methods (they just can't be called), but virtual methods // cause the who class to fail to be instantiatable. void checkBeforeResize(const IPosition &newShape) { if(fixedDimensionality() !=0 && newShape.size() != fixedDimensionality()) throw(ArrayNDimError(fixedDimensionality(), newShape.size(), std::string("Invalid size given to ") + typeid(*this).name() + ": should have dimensionality of " + std::to_string(fixedDimensionality()))); } // Subclasses can return their dimensionality. The Array class will make sure // that whenever it is resized, the dimensionality is checked. // Array's constructors do not check the dimensionality, because the subclass // hasn't been created yet at that point. Subclasses should therefore make // sure to call the constructors appropriately. // For classes that return 0, any resize will be allowed. virtual size_t fixedDimensionality() const { return 0; } virtual void checkAssignableType(ArrayBase& arrayBase) const { const Array* pa = dynamic_cast*>(&arrayBase); if (pa == nullptr) { throw ArrayError("ArrayBase& has incorrect template type"); } } static void copyToContiguousStorage(T *dst, Array const& src) { copyToContiguousStorage(dst, src, std::is_copy_assignable()); } // Remove the degenerate axes from the Array object. // This is the implementation of the nonDegenerate functions. // It has a different name to be able to make it virtual without having // the "hide virtual function" message when compiling derived classes. virtual void doNonDegenerate(const Array &other, const IPosition &ignoreAxes); // Shared pointer to a Storage that contains the data. std::shared_ptr> data_p; // This pointer is adjusted to point to the first element of the array. // It is not necessarily the same thing as data->storage() since // this array might be a section, e.g. have a blc which shifts us forward // into the block. T *begin_p; // The end for an STL-style iteration. T* end_p; // Fill the steps and the end for a derived class. void makeSteps() { baseMakeSteps(); this->setEndIter(); } // Set the end iterator. void setEndIter() { end_p = (nels_p==0 ? 0 : (contiguous_p ? begin_p + nels_p : begin_p + size_t(length_p(ndim()-1)) * steps_p(ndim()-1))); } }; // Swap the first array with the second. // This is more efficient than std::swap() template void swap(Array& first, Array& second) { first.swap(second); } //# Declare extern templates for often used types. extern template class Array; extern template class Array; extern template class Array; extern template class Array; extern template class Array; extern template class Array; extern template class Array; extern template class Array; extern template class Array; }//#End casa namespace #include "ArrayStr.h" #include "Array.tcc" #endif casacore-3.7.1/casa/Arrays/Array.tcc000066400000000000000000000770141476623553700172560ustar00rootroot00000000000000//# Array.cc: A templated N-D Array class with zero origin //# Copyright (C) 1993,1994,1995,1996,1997,1998,1999,2000,2001,2002,2003,2015 //# Associated Universities, Inc. Washington DC, USA. //# National Astronomical Observatory of Japan //# 2-21-1, Osawa, Mitaka, Tokyo, 181-8588, Japan. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_ARRAY_2_TCC #define CASA_ARRAY_2_TCC #include "Array.h" #include "ArrayError.h" #include "ArrayIter.h" #include "ArrayPosIter.h" #include "Memory.h" #include "MaskedArray.h" #include "Slicer.h" #include #include namespace casacore {//#Begin casa namespace template Array::Array() : data_p(new arrays_internal::Storage(0)), begin_p(nullptr), end_p(nullptr) { assert(ok()); } // // ArrayShapeError // template Array::Array(const IPosition &shape) : ArrayBase(shape), data_p(new arrays_internal::Storage(nelements())), begin_p(data_p->data()) { setEndIter(); assert(ok()); } // // ArrayShapeError // template Array::Array(const IPosition &shape, const T &initialValue) : ArrayBase(shape), data_p(new arrays_internal::Storage(nelements(), initialValue)), begin_p(data_p->data()) { setEndIter(); assert(ok()); } template Array::Array(const IPosition& shape, uninitializedType) : ArrayBase(shape), data_p(arrays_internal::Storage::MakeUninitialized(nelements())), begin_p(data_p->data()) { setEndIter(); assert(ok()); } template Array::Array(std::initializer_list list) : ArrayBase (IPosition(1, list.size())), data_p(new arrays_internal::Storage(list.begin(), list.end())), begin_p(data_p->data()) { setEndIter(); assert(ok()); } template Array::Array(const Array &other) : ArrayBase (other), data_p(other.data_p), begin_p (other.begin_p), end_p (other.end_p) { assert(ok()); } template Array::Array(Array&& source) noexcept : ArrayBase (std::move(source)), data_p(source.data_p), begin_p(source.begin_p), end_p(source.end_p) { // We can't free the storage of the source object yet, because this would require either // to allow data_p be nullptr, which requires check everywhere, or would require allocating // an empty storage, which would forbid noexcept. // Empty source source.begin_p = nullptr; source.end_p = nullptr; assert(ok()); } template Array::Array(Array&& source, const IPosition& shapeForSource) noexcept : ArrayBase (std::move(source), shapeForSource), data_p(source.data_p), begin_p(source.begin_p), end_p(source.end_p) { source.begin_p = nullptr; source.end_p = nullptr; assert(ok()); } template Array::Array(const IPosition &shape, T *storage, StorageInitPolicy policy) : ArrayBase(shape), data_p(), begin_p(nullptr), end_p(nullptr) { takeStorage(shape, storage, policy); assert(ok()); } template Array::Array(const IPosition &shape, const T *storage) : ArrayBase(shape), data_p(), begin_p(nullptr), end_p(nullptr) { takeStorage(shape, storage); assert(ok()); } template template Array::Array(const IPosition &shape, InputIterator startIter) : Array(shape, startIter, std::is_integral()) { } template template Array::Array(const IPosition &shape, InputIterator startIter, std::false_type) : ArrayBase(shape), data_p(new arrays_internal::Storage(startIter, std::next(startIter, nelements()))), begin_p(data_p->data()) { setEndIter(); assert(ok()); } template template Array::Array(const IPosition &shape, Integral initialValue, std::true_type) : ArrayBase(shape), data_p(new arrays_internal::Storage(nelements(), initialValue)), begin_p(data_p->data()) { setEndIter(); assert(ok()); } template Array::~Array() noexcept { } template std::unique_ptr Array::makeArray() const { return std::unique_ptr(new Array()); } // It is better for a move assignment to be noexcept, but that would allow // assigning Matrix to Vector (etc) without being able to throw. Hence, I // think it is not possible to do this without changing such semantics. template Array& Array::operator= (Array&& source) { bool swapWouldClashRequirements = source.fixedDimensionality() != 0 && fixedDimensionality() != 0 && source.fixedDimensionality() != fixedDimensionality(); // If this or the source is a shared array, we can't // just replace the storage. Non-moveable types will cause // this to throw :-(. TODO should be solved. if(!isUnique() || !source.isUnique() || swapWouldClashRequirements) { assign_conforming(source); } else { // There's no good reason to require conformance here, but some of the software seems // to assume that assignment always checks for the shape to fit. :-( if (!conform(source) && nelements() != 0) { validateConformance(source); } else { if(source.fixedDimensionality() != 0 && ndim() != source.ndim()) { // We can't directly swap the two, because the lhs doesn't match in rhs requirements resize(IPosition(source.fixedDimensionality(), 0)); } swap(source); } } return *this; } template void Array::swap(Array& other) { checkBeforeResize(other.shape()); other.checkBeforeResize(shape()); ArrayBase::swap(other); // Take storage std::swap(begin_p, other.begin_p); std::swap(end_p, other.end_p); std::swap(data_p, other.data_p); } template void Array::assign (const Array& other) { assert(ok()); if (! shape().isEqual (other.shape())) { checkBeforeResize(other.shape()); resize (other.shape()); } assign_conforming (other); } template void Array::assignBase (const ArrayBase& other, bool checkType) { assert(ok()); // Checking the type can be expensive, so only do if needed or in debug mode. if (checkType /*|| aips_debug*/) { const Array* pa = dynamic_cast*>(&other); if (pa == nullptr) { throw ArrayError("assign(ArrayBase&) has incorrect template type"); } } assign (static_cast&>(other)); } template void Array::reference(const Array &other) { assert(ok()); // It is allowed to reference from a higher dimensional Array to a lower dimensional one, // e.g. reference an Array from a Matrix. if(other.ndim() < fixedDimensionality()) { IPosition newShape(fixedDimensionality()); for(size_t i=0; i!=other.ndim(); ++i) newShape[i] = other.shape()[i]; const int newValue = (other.nelements() == 0) ? 0 : 1; for(size_t i=other.ndim(); i!=fixedDimensionality(); ++i) newShape[i] = newValue; Array tmp; tmp.reference(other); other.baseReform(tmp, newShape); reference( tmp ); } else { // First copy data, then meta data. // This is better in case of multi-threading because it makes it possible // to test the size and be sure that the data is there. checkBeforeResize(other.shape()); data_p = other.data_p; begin_p = other.begin_p; end_p = other.end_p; ArrayBase::assign (other); } } template void Array::copyToContiguousStorage(T *storage, Array const& src, std::true_type) { if (src.contiguousStorage()) { std::copy_n(src.begin_p, src.nels_p, storage); } else if (src.ndim() == 1) { copy_n_with_stride(src.begin_p, src.length_p(0), storage, 1U, src.inc_p(0)); } else if (src.length_p(0) == 1 && src.ndim() == 2) { // Special case which can be quite common (e.g. row in a matrix). copy_n_with_stride(src.begin_p, src.length_p(1), storage, 1U, src.originalLength_p(0) * src.inc_p(1)); } else if (src.length_p(0) <= 25) { // If not many elements on a line, it's better to use this loop. T* ptr = storage; const_iterator iterend = src.end(); for (const_iterator iter = src.begin(); iter != iterend; ++iter) { *ptr++ = *iter; } } else { // Step through Vector by Vector // The output is guaranteed to have all incs set to 1 ArrayPositionIterator ai(src.shape(), 1); IPosition index(src.ndim()); size_t count = 0; size_t const size = src.length_p(0); while (!ai.pastEnd()) { index = ai.pos(); size_t offset = ArrayIndexOffset(src.ndim(), src.originalLength_p.storage(), src.inc_p.storage(), index); copy_n_with_stride(src.begin_p + offset, size, storage + count * size, 1U, src.inc_p(0)); ai.next(); count++; } } } template Array Array::copy() const { assert(ok()); Array vp(shape()); if (ndim() != 0) copyToContiguousStorage(vp.begin_p, *this); return vp; } template Array& Array::assign_conforming_implementation(const Array& other, std::true_type) { assert(ok()); if (this == &other) { return *this; } bool Conform = conform(other); if (!Conform && nelements() != 0) { validateConformance(other); // We can't overwrite, so throw exception } size_t offset, offset2; IPosition index(other.ndim()); if (Conform == true) { // Copy in place if (ndim() == 0) { return *this; } else if (contiguousStorage() && other.contiguousStorage()) { std::copy_n(other.begin_p, nels_p, begin_p); } else if (ndim() == 1) { copy_n_with_stride (other.begin_p, length_p(0), begin_p, inc_p(0), other.inc_p(0)); } else if (length_p(0) == 1 && ndim() == 2) { // Special case which can be quite common (e.g. row in a matrix). copy_n_with_stride(other.begin_p, length_p(1), begin_p, originalLength_p(0)*inc_p(1), other.originalLength_p(0)*other.inc_p(1)); } else if (length_p(0) <= 25) { // If not many elements on a line, it's better to use this loop. const_iterator from(other.begin()); iterator iterend=end(); for (iterator iter=begin(); iter!=iterend; ++iter) { *iter = *from; ++from; } } else { ArrayPositionIterator ai(other.shape(), 1); // Step through Vector by Vector while (! ai.pastEnd()) { index = ai.pos(); offset = ArrayIndexOffset(ndim(), originalLength_p.storage(), inc_p.storage(), index); offset2 = ArrayIndexOffset(other.ndim(), other.originalLength_p.storage(), other.inc_p.storage(), index); copy_n_with_stride(other.begin_p+offset2, length_p(0), begin_p+offset, inc_p(0), other.inc_p(0)); ai.next(); } } } else { // Array was empty; make a new copy and reference it. Array tmp (other.copy()); reference (tmp); } return *this; } template Array &Array::operator=(const T &val) { assert(ok()); set (val); return *this; } template Array& Array::assign_conforming(const MaskedArray& marray) { assert(ok()); if (!conform(marray)) { throw(ArrayConformanceError( "Array & Array::assign_conforming (const MaskedArray& marray)" "- Conformance error.")); } bool deleteThis; T *thisStorage = getStorage(deleteThis); T *thisS = thisStorage; bool deleteArr; const T *arrStorage = marray.getArrayStorage(deleteArr); const T *arrS = arrStorage; bool deleteMask; const LogicalArrayElem *maskStorage = marray.getMaskStorage(deleteMask); const LogicalArrayElem *maskS = maskStorage; size_t ntotal = nelements(); while (ntotal--) { if (*maskS) { *thisS = *arrS; } thisS++; maskS++; arrS++; } putStorage(thisStorage, deleteThis); marray.freeArrayStorage(arrStorage, deleteArr); marray.freeMaskStorage(maskStorage, deleteMask); return *this; } template void Array::set(const T &Value) { assert(ok()); // Ultimately we should go to RawFillAll functions // RawFillAll(ndim(), begin_p, inc_p.storage(), length_p.storage(), Value); // Step through Vector by Vector size_t offset; if (ndim() == 0) { return; } else if (contiguousStorage()) { std::fill_n(begin_p, nels_p, Value); } else if (ndim() == 1) { fill_n_with_stride (begin_p, length_p(0), Value, inc_p(0)); } else if (length_p(0) == 1 && ndim() == 2) { // Special case which can be quite common (e.g. row in a matrix). fill_n_with_stride (begin_p, length_p(1), Value, originalLength_p(0)*inc_p(1)); } else if (length_p(0) <= 25) { // If not many elements on a line, it's better to use this loop. iterator iterend=end(); for (iterator iter=begin(); iter!=iterend; ++iter) { *iter = Value; } } else { // Step through Vector by Vector ArrayPositionIterator ai(shape(), 1); IPosition index(ndim()); while (! ai.pastEnd()) { index = ai.pos(); offset = ArrayIndexOffset(ndim(), originalLength_p.storage(), inc_p.storage(), index); fill_n_with_stride(begin_p+offset, length_p(0), Value, inc_p(0)); ai.next(); } } } template template void Array::apply(Callable function) { assert(ok()); if (nelements() == 0) { return; // short-circuit } if (contiguousStorage()) { for (size_t i=0; i void Array::unique() { assert(ok()); if (!contiguousStorage() || !isUnique()) { // OK, we know we are going to need to copy. Array tmp(copy()); reference (tmp); } } // // ArrayConformanceError // template Array Array::reform(const IPosition& len) const { assert(ok()); // Check if reform is possible and needed. // If not needed, simply return a copy. Array tmp(*this); baseReform (tmp, len); tmp.setEndIter(); return tmp; } template bool Array::adjustLastAxis (const IPosition& newShape, size_t resizePercentage, bool resizeIfNeeded) { assert(ok()); IPosition currentShape = shape(); if (newShape.size() == currentShape.size()){ // Let base method handle attempt dimensionality changes for (size_t i = 0; i < newShape.size() - 1; i++){ if (currentShape (i) != newShape (i)){ std::string message = "Array::extend - New shape can only change last dimension:" " current=" + currentShape.toString() + ", new=" + newShape.toString(); throw ArrayConformanceError (message); } } } long long originalElements = data_p->size(); bool resetEnd = ArrayBase::reformOrResize (newShape, resizeIfNeeded, data_p.use_count(), data_p->size(), true, resizePercentage); if (resetEnd){ setEndIter(); } return originalElements != (long long) data_p->size(); } template bool Array::reformOrResize (const IPosition & newShape, size_t resizePercentage, bool resizeIfNeeded) { assert(ok()); checkBeforeResize(newShape); long long originalElements = data_p->size(); bool resetEnd = ArrayBase::reformOrResize (newShape, resizeIfNeeded, data_p.use_count(), data_p->size(), false, resizePercentage); if (resetEnd){ setEndIter(); } return originalElements != (long long) data_p->size(); } template inline size_t Array::capacity () const { return data_p->size(); // returns the number of elements allocated. } template Array Array::nonDegenerate (size_t startingAxis, bool throwIfError) const { Array tmp; assert(ok()); tmp.nonDegenerate (*this, startingAxis, throwIfError); return tmp; } template void Array::nonDegenerate (const Array &other, size_t startingAxis, bool throwIfError) { if (startingAxis < other.ndim()) { IPosition ignoreAxes(startingAxis); for (size_t i=0; i= other.ndim()) throw ArrayError(); reference (other); } } template Array Array::nonDegenerate (const IPosition &ignoreAxes) const { Array tmp; assert(ok()); tmp.nonDegenerate(*this, ignoreAxes); return tmp; } template void Array::removeDegenerate (size_t startingAxis, bool throwIfError) { Array tmp; assert(ok()); tmp.nonDegenerate (*this, startingAxis, throwIfError); reference (tmp); } template void Array::removeDegenerate (const IPosition &ignoreAxes) { Array tmp; assert(ok()); tmp.nonDegenerate(*this, ignoreAxes); reference (tmp); } template void Array::doNonDegenerate (const Array &other, const IPosition &ignoreAxes) { assert(ok()); baseNonDegenerate (other, ignoreAxes); begin_p = other.begin_p; data_p = other.data_p; setEndIter(); } template const Array Array::addDegenerate(size_t numAxes) const { Array * This = const_cast*>(this); const Array tmp(This->addDegenerate(numAxes)); return tmp; } template Array Array::addDegenerate(size_t numAxes) { assert(ok()); Array tmp(*this); if (numAxes > 0) { baseAddDegenerate (tmp, numAxes); tmp.setEndIter(); } return tmp; } template bool Array::conform(const MaskedArray &other) const { return conform (other.getArray()); } // // ArrayConformanceError // template void Array::resize() { IPosition emptyShape(fixedDimensionality(), 0); resize (emptyShape); } template void Array::resize(const IPosition& len, bool copyValues) { assert(ok()); // Maybe we don't need to resize; let's see if we can short circuit if (len.isEqual (shape())) { return; } // OK we differ, so we really have to resize ourselves. Array tmp(len); // Copy the contents if needed. if (copyValues) { tmp.copyMatchingPart (*this); } this->reference(tmp); } template void Array::copyMatchingPart (const Array& from) { if (nelements() > 0 && from.nelements() > 0) { // Create IPositions of the correct length. IPosition endto (ndim(), 0); IPosition endfr (from.ndim(), 0); // Put the minimum length in each axis. size_t nd = from.ndim(); if (ndim() < nd) { nd = ndim(); } const IPosition& lento = shape(); const IPosition& lenfr = from.shape(); for (size_t i=0; i subto = (*this)(IPosition(ndim(), 0), endto); Array fromc(from); // make non-const Array subfr = fromc(IPosition(from.ndim(), 0), endfr); // Reform to if the dimensionalities differ. if (subto.ndim() != subfr.ndim()) { Array tmp = subto.reform (endfr+1); subto.reference (tmp); } subto.assign_conforming(subfr); } } template T &Array::operator()(const IPosition &index) { assert(ok()); /*if (aips_debug) { validateIndex(index); }*/ size_t offs=0; for (size_t i=0; i const T &Array::operator()(const IPosition &index) const { assert(ok()); size_t offs=0; for (size_t i=0; i // ArrayError // template Array Array::operator()(const IPosition& b, const IPosition& e, const IPosition& i) { assert(ok()); Array tmp(*this); size_t offs = makeSubset (tmp, b, e, i); tmp.begin_p += offs; tmp.setEndIter(); assert(tmp.ok()); return tmp; } template const Array Array::operator()( const IPosition &b, const IPosition &e, const IPosition &i) const { return const_cast*>(this)->operator() (b,e,i); } template Array Array::operator()(const IPosition &b, const IPosition &e) { IPosition i(e.nelements()); i = 1; return (*this)(b,e,i); } template const Array Array::operator()(const IPosition &b, const IPosition &e) const { return const_cast*>(this)->operator() (b,e); } template Array Array::operator()(const Slicer& slicer) { if (slicer.isFixed()) { return operator() (slicer.start(), slicer.end(), slicer.stride()); } IPosition blc, trc, inc; slicer.inferShapeFromSource (shape(), blc, trc, inc); return operator() (blc, trc, inc); } template const Array Array::operator()(const Slicer& slicer) const { return const_cast*>(this)->operator() (slicer); } template std::unique_ptr Array::getSection(const Slicer& slicer) const { return std::unique_ptr(new Array(operator()(slicer))); } template Array Array::operator[](size_t i) const { assert(ok()); size_t nd = ndim(); IPosition s(nd, 0); IPosition e(shape() - 1); if (nd > 0) { nd--; s[nd] = i; e[nd] = i; } Array tmp(*this); tmp.reference (tmp(s,e)); return nd == 0 ? tmp : tmp.nonDegenerate(nd); } template const MaskedArray Array::operator() (const LogicalArray &mask) const { MaskedArray ret (*this, mask, true); return ret; } template MaskedArray Array::operator() (const LogicalArray &mask) { MaskedArray ret (*this, mask); return ret; } template const MaskedArray Array::operator() (const MaskedLogicalArray &mask) const { MaskedArray ret (*this, mask, true); return ret; } template MaskedArray Array::operator() (const MaskedLogicalArray &mask) { MaskedArray ret (*this, mask); return ret; } template Array Array::diagonals (size_t firstAxis, long long diag) const { assert(ok()); Array tmp(*this); tmp.begin_p += tmp.makeDiagonal (firstAxis, diag); tmp.makeSteps(); return tmp; } template size_t Array::nrefs() const { assert(ok()); return data_p.use_count(); } // This is relatively expensive template bool Array::ok() const { assert(ArrayBase::ok()); assert(data_p != nullptr); assert(!(nelements() > 0 && (begin_p == nullptr || data_p==nullptr))); assert(!(begin_p != nullptr && data_p->data() > begin_p)); assert(!(begin_p != nullptr && begin_p > data_p->data() + data_p->size())); if (! ArrayBase::ok()) { return false; } if(data_p == nullptr) return false; if (nelements() > 0 && (begin_p == nullptr || data_p==nullptr)) return false; // This test may not be portable. if (begin_p != nullptr && data_p->data() > begin_p) { return false; } // This test may not be portable. if (begin_p != nullptr && begin_p > data_p->data() + data_p->size()) { return false; } return true; } // // ArrayError // template T* Array::getStorage(bool& deleteIt) { assert(ok()); deleteIt = false; if (ndim() == 0) { return nullptr; } if (contiguousStorage()) { return begin_p; } // We need to do a copy size_t n = nelements(); T* storage = std::allocator().allocate(n); try { for(size_t i=0; i!=n; ++i) new (&storage[i]) T(); copyToContiguousStorage(storage, *this); } catch (...) { // TODO To be correct, the destructors of the already // constructed object should be called, but this is // a border case so ignored for now. std::allocator().deallocate(storage, nelements()); throw; } deleteIt = true; return storage; } template void Array::putStorage(T *&storage, bool deleteAndCopy) { assert(ok()); if (deleteAndCopy == false) { storage = nullptr; return; } if (ndim() == 1) { move_n_with_stride(storage, length_p(0), begin_p, inc_p(0), 1U); } else if (length_p(0) == 1 && ndim() == 2) { // Special case which can be quite common (e.g. row in a matrix). move_n_with_stride(storage, length_p(1), begin_p, originalLength_p(0)*inc_p(1), 1U); } else if (length_p(0) <= 25) { // If not many elements on a line, it's better to use this loop. T* ptr = storage; iterator iterend=end(); for (iterator iter=begin(); iter!=iterend; ++iter) { *iter = std::move(*ptr); ++ptr; } } else { ArrayPositionIterator ai(this->shape(), 1); size_t offset; IPosition index(ndim()); size_t count=0; while (! ai.pastEnd()) { index = ai.pos(); offset = ArrayIndexOffset(ndim(), originalLength_p.storage(), inc_p.storage(), index); move_n_with_stride(storage+count*length_p(0), length_p(0), begin_p+offset, inc_p(0), 1U); ai.next(); count++; } } T const * &fakeStorage = const_cast(storage); freeStorage(fakeStorage, deleteAndCopy); } template void Array::freeStorage(const T*&storage, bool deleteIt) const { assert(ok()); if (deleteIt) { // The cast is required since you can't delete a const array; however // if deleteIt is set the array came from new. T* ptr = const_cast(storage); size_t n = nelements(); for(size_t i=0; i!=n; ++i) ptr[i].~T(); // TODO this is only allowed when allocator is always equal, but is done for // now to keep the method const. // see e.g. std::allocator_traits::is_always_equal std::allocator().deallocate(ptr, n); } storage = nullptr; } template void *Array::getVStorage(bool &deleteIt) { return getStorage (deleteIt); } template const void *Array::getVStorage(bool &deleteIt) const { return getStorage (deleteIt); } template void Array::putVStorage(void *&storage, bool deleteAndCopy) { T* &ptr = reinterpret_cast(storage); putStorage (ptr, deleteAndCopy); } template void Array::freeVStorage(const void *&storage, bool deleteAndCopy) const { const T* &ptr = reinterpret_cast(storage); freeStorage (ptr, deleteAndCopy); } template void Array::takeStorage(const IPosition &shape, T *storage, StorageInitPolicy policy) { preTakeStorage(shape); size_t new_nels = shape.product(); if(policy == SHARE) { data_p = arrays_internal::Storage::MakeFromSharedData(storage, new_nels); } else { if (data_p==nullptr || !isUnique() || data_p->size() != new_nels) { data_p = arrays_internal::Storage::MakeFromMove(storage, storage+new_nels); } else { std::move(storage, storage+new_nels, data_p->data()); } } ArrayBase::assign(ArrayBase(shape)); begin_p = data_p->data(); setEndIter(); if(policy == TAKE_OVER) { // TODO this is not consistent with old behaviour for(size_t i=0; i!=new_nels; ++i) storage[new_nels-i-1].~T(); std::allocator().deallocate(storage, new_nels); } // Call OK at the end rather than the beginning since this might // be called from a constructor. assert(ok()); postTakeStorage(); } template void Array::takeStorage(const IPosition &shape, const T *storage) { // This cast is safe since a copy will be made T *storagefake = const_cast(storage); takeStorage(shape, storagefake, COPY); } template std::unique_ptr Array::makeIterator (size_t byDim) const { return std::unique_ptr( new ArrayIterator (*this, byDim) ); } template Array::BaseIteratorSTL::BaseIteratorSTL (const Array& arr) : itsLineIncr (0), itsCurPos (arr.ndim(), 0), itsArray (&arr), itsContig (arr.contiguousStorage()) { // An empty array has to be handled. if (arr.nelements() == 0) { itsPos = 0; itsContig = true; } else { // Set the last cursor position. // Handle the case for the end iterator. itsLastPos = arr.shape() - 1; // If the array is not contiguous, we iterate "line by line" in // the increment function. Optimize for the case where the length // of the lower dimensions is 1. All such dimensions can be included // in the "line". // At the end itsLineAxis gives the axis where the next "line" starts. itsPos = &((*itsArray)(itsCurPos)); if (!itsContig) { itsLineAxis = 0; while (itsLineAxis < arr.ndim()-1 && itsLastPos(itsLineAxis) == 0) { itsLineAxis++; } itsCurPos(itsLineAxis) = 1; itsLineIncr = itsArray->steps()(itsLineAxis) - 1; itsLineEnd = itsPos + itsLastPos(itsLineAxis) * (itsLineIncr+1); itsCurPos(itsLineAxis) = 0; } } } template void Array::BaseIteratorSTL::increment() { size_t axis; for (axis=itsLineAxis+1; axissteps()(axis); break; } itsCurPos(axis) = 0; itsLineEnd -= itsLastPos(axis) * itsArray->steps()(axis); } if (axis == itsCurPos.nelements()) { itsPos = itsArray->cend(); } else { itsPos = itsLineEnd - itsLastPos(itsLineAxis) * (itsLineIncr+1); } } template std::vector Array::tovector() const { bool deleteIt; const T *stor = getStorage(deleteIt); std::vector out; out.assign(stor, stor+nelements()); // TODO this is formally not allowed: the allocator is not const, so // might cause the object to change. This tovector() method can obviously be implemented // in a const manner, so this needs to be rewritten. const_cast*>(this)->freeStorage(stor, deleteIt); return out; } template template void Array::tovector(std::vector &out) const { bool deleteIt; const T *stor = getStorage(deleteIt); out.assign(stor, stor+nelements()); /// See note above for @ref tovector() const_cast*>(this)->freeStorage(stor, deleteIt); } // Define static member template typename Array::uninitializedType Array::uninitialized; } //#End casa namespace #endif casacore-3.7.1/casa/Arrays/Array2.cc000066400000000000000000000062471476623553700171540ustar00rootroot00000000000000//# Array2.cc: Template Arrays with slices, logical operations, and arithmetic //# Copyright (C) 1993,1994,1995,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include "Array.h" #include "IPosition.h" #include "ArrayError.h" namespace casacore { //# NAMESPACE CASACORE - BEGIN // This probably isn't of interest to normal users. It returns the "volume" of // an array (i.e. "nelements"). size_t ArrayVolume (size_t Ndim, const ssize_t *Shape) { size_t i; /*if (aips_debug) { for (i=0; i < Ndim; i++) if (Shape[i] < 0) throw(ArrayError("::ArrayVolume - negative shape")); }*/ if (Ndim == 0) return 0; size_t total=1; for(i=0; i < Ndim; i++) total *= Shape[i]; return total; } // This probably isn't of interest to normal users. Given a decimated // array with a non-zero origin, what is the linear index into storage. // Here we assume that the Shape is the original length, i.e. has INC // in it. size_t ArrayIndexOffset (size_t Ndim, const ssize_t *Shape, const ssize_t *Origin, const ssize_t *Inc, const IPosition &Index) { size_t i; /*if (aips_debug) { for (i=0; i < Ndim; i++) if (Index(i) < Origin[i] || Index(i) > (Origin[i] + Shape[i] - 1) || Shape[i] < 0 || Inc[i] < 1) throw(ArrayError("::ArrayIndexOffset - negative shape or inc" "<1 or out-of-bounds index")); }*/ size_t offset = (Index(0) - Origin[0])*Inc[0]; for (i=1; i < Ndim; i++) offset += (Index(i) - Origin[i])*Inc[i]*ArrayVolume(i, Shape); return offset; } size_t ArrayIndexOffset (size_t Ndim, const ssize_t *Shape, const ssize_t *Inc, const IPosition &Index) { size_t i; /*if (aips_debug) { for (i=0; i < Ndim; i++) if (Index(i) < 0 || Index(i) >= Shape[i] || Shape[i] < 0 || Inc[i] < 1) throw(ArrayError("::ArrayIndexOffset - negative shape or inc" "<1 or out-of-bounds index")); }*/ size_t offset = Index(0)*Inc[0]; for (i=1; i < Ndim; i++) offset += Index(i)*Inc[i]*ArrayVolume(i, Shape); return offset; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Arrays/Array2Math.cc000066400000000000000000000267131476623553700177660ustar00rootroot00000000000000//# Array2Math.cc: Arithmetic functions defined on Arrays //# Copyright (C) 1993,1994,1995,1996,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include "ArrayMath.h" #include "ArrayError.h" #include "Matrix.h" #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# We could use macros to considerably reduce the number of lines, however //# that makes it harder to debug, understand, etc. Array> conj(const Array> &carray) { return arrayTransformResult (carray, [](std::complex v) { return std::conj(v); }); } Array> conj(const Array> &carray) { return arrayTransformResult (carray, [](std::complex v) { return std::conj(v); }); } Matrix> conj(const Matrix> &carray) { return Matrix>(conj ((const Array>&)carray)); } Matrix> conj(const Matrix> &carray) { return Matrix>(conj ((const Array>&)carray)); } void conj(Array> &rarray, const Array> &carray) { checkArrayShapes (carray, rarray, "conj"); arrayTransform (carray, rarray, [](std::complex v) { return std::conj(v); }); } void conj(Array> &rarray, const Array> &carray) { checkArrayShapes (carray, rarray, "conj"); arrayTransform (carray, rarray, [](std::complex v) { return std::conj(v); }); } void real(Array &rarray, const Array> &carray) { checkArrayShapes (carray, rarray, "real"); // std::real is only a template since c++14 :( arrayTransform (carray, rarray, [](std::complex v) { return std::real(v); }); } void real(Array &rarray, const Array> &carray) { checkArrayShapes (carray, rarray, "real"); arrayTransform (carray, rarray, [](std::complex v) { return std::real(v); }); } void imag(Array &rarray, const Array> &carray) { checkArrayShapes (carray, rarray, "imag"); arrayTransform (carray, rarray, [](std::complex v) { return std::imag(v); }); } void imag(Array &rarray, const Array> &carray) { checkArrayShapes (carray, rarray, "imag"); arrayTransform (carray, rarray, [](std::complex v) { return std::imag(v); }); } void amplitude(Array &rarray, const Array> &carray) { checkArrayShapes (carray, rarray, "amplitude"); arrayTransform (carray, rarray, std::abs); } void amplitude(Array &rarray, const Array> &carray) { checkArrayShapes (carray, rarray, "amplitude"); arrayTransform (carray, rarray, std::abs); } void phase(Array &rarray, const Array> &carray) { checkArrayShapes (carray, rarray, "pahse"); arrayTransform (carray, rarray, [](std::complex v) { return std::arg(v); }); } void phase(Array &rarray, const Array> &carray) { checkArrayShapes (carray, rarray, "phase"); arrayTransform (carray, rarray, [](std::complex v) { return std::arg(v); }); } Array real(const Array> &carray) { Array rarray(carray.shape()); real(rarray, carray); return rarray; } Array real(const Array> &carray) { Array rarray(carray.shape()); real(rarray, carray); return rarray; } Array imag(const Array> &carray) { Array rarray(carray.shape()); imag(rarray, carray); return rarray; } Array imag(const Array> &carray) { Array rarray(carray.shape()); imag(rarray, carray); return rarray; } Array amplitude(const Array> &carray) { Array rarray(carray.shape()); amplitude(rarray, carray); return rarray; } Array amplitude(const Array> &carray) { Array rarray(carray.shape()); amplitude(rarray, carray); return rarray; } Array phase(const Array> &carray) { Array rarray(carray.shape()); phase(rarray, carray); return rarray; } Array phase(const Array> &carray) { Array rarray(carray.shape()); phase(rarray, carray); return rarray; } // // ArrayError // void ComplexToReal(Array &rarray, const Array> &carray) { if (rarray.nelements() != 2*carray.nelements()) { throw(ArrayError("::ComplexToReal(Array &rarray, const " "Array> &carray) - rarray.nelements() != " "2*carray.nelements()")); } if (rarray.contiguousStorage() && carray.contiguousStorage()) { memcpy (const_cast(rarray.data()), carray.data(), rarray.nelements() * sizeof(float)); } else { Array>::const_iterator citer=carray.begin(); Array::iterator rend = rarray.end(); for (Array::iterator riter = rarray.begin(); riter!=rend; ++riter, ++citer) { *riter = real(*citer); ++riter; *riter = imag(*citer); } } } // // ArrayError // void ComplexToReal(Array &rarray, const Array> &carray) { if (rarray.nelements() != 2*carray.nelements()) { throw(ArrayError("::ComplexToReal(Array &rarray, const " "Array> &carray) - rarray.nelements() != " "2*carray.nelements()")); } if (rarray.contiguousStorage() && carray.contiguousStorage()) { memcpy (const_cast(rarray.data()), carray.data(), rarray.nelements() * sizeof(double)); } else { Array>::const_iterator citer=carray.begin(); Array::iterator rend = rarray.end(); for (Array::iterator riter = rarray.begin(); riter!=rend; ++riter, ++citer) { *riter = real(*citer); ++riter; *riter = imag(*citer); } } } Array ComplexToReal(const Array> &carray) { IPosition shape = carray.shape(); shape(0) *= 2; Array retval(shape); ComplexToReal(retval, carray); return retval; } Array ComplexToReal(const Array> &carray) { IPosition shape = carray.shape(); shape(0) *= 2; Array retval(shape); ComplexToReal(retval, carray); return retval; } // // ArrayError // void RealToComplex(Array> &carray, const Array &rarray) { if (rarray.nelements() != 2*carray.nelements()) { throw(ArrayError("::RealToComplex(Array> &carray, const " "Array &rarray) - rarray.nelements() != " "2*carray.nelements()")); } if (rarray.contiguousStorage() && carray.contiguousStorage()) { std::copy_n(rarray.data(), rarray.nelements(), reinterpret_cast(const_cast*>(carray.data()))); } else { Array>::iterator citer=carray.begin(); Array::const_iterator rend = rarray.end(); for (Array::const_iterator riter = rarray.begin(); riter!=rend; ++riter, ++citer) { float r = *riter; ++riter; *citer = std::complex(r, *riter); } } } // // ArrayError // void RealToComplex(Array> &carray, const Array &rarray) { if (rarray.nelements() != 2*carray.nelements()) { throw(ArrayError("::RealToComplex(Array> &carray, const " "Array &rarray) - rarray.nelements() != " "2*carray.nelements()")); } if (rarray.contiguousStorage() && carray.contiguousStorage()) { std::copy_n(rarray.data(), rarray.nelements(), reinterpret_cast(const_cast*>(carray.data()))); } else { Array>::iterator citer=carray.begin(); Array::const_iterator rend = rarray.end(); for (Array::const_iterator riter = rarray.begin(); riter!=rend; ++riter, ++citer) { double r = *riter; ++riter; *citer = std::complex(r, *riter); } } } // // ArrayError // Array> RealToComplex(const Array &rarray) { IPosition shape = rarray.shape(); if (shape(0) %2 == 1) { // Odd size throw(ArrayError("Array> RealToComplex(const Array &" "rarray) - rarray.shape()(0) not even")); } shape(0) /= 2; Array> retval(shape); RealToComplex(retval, rarray); return retval; } // // ArrayError // Array> RealToComplex(const Array &rarray) { IPosition shape = rarray.shape(); if (shape(0) %2 == 1) { // Odd size throw(ArrayError("Array> RealToComplex(const Array &" "rarray) - rarray.shape()(0) not even")); } shape(0) /= 2; Array> retval(shape); RealToComplex(retval, rarray); return retval; } IPosition checkExpandArray (IPosition& mult, IPosition& inshp, const IPosition& inShape, const IPosition& outShape, const IPosition& alternate) { if (inShape.size() == 0) { throw ArrayError("expandArray: input array cannot be empty"); } mult.resize (outShape.size()); inshp.resize (outShape.size()); inshp = 1; // missing axes have length 1 IPosition alt(outShape.size(), 0); for (size_t i=0; i outShape[i] || outShape[i] % inshp[i] != 0) { throw ArrayError("expandArray: length of each input array axis must " "be <= output axis and divide evenly"); } // Note that for an input length 1, linear and alternate come to the same. // Linear is faster, so use that if possible. if (i < alternate.size() && inshp[i] > 1) { alt[i] = alternate[i]; } mult[i] = outShape[i] / inshp[i]; } return alt; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Arrays/ArrayAccessor.h000066400000000000000000000556721476623553700204250ustar00rootroot00000000000000//# ArrayAccessor.h: Fast 1D accessor/iterator for nD array classes //# Copyright (C) 2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_ARRAYACCESSOR_2_H #define CASA_ARRAYACCESSOR_2_H //# Includes #include "Array.h" namespace casacore { //#Begin casa namespace //# Hide simple Axis classes names from outside module namespace { // Class to enumerate compile-time axis numeration template struct Axis { enum { // Specify the constant axis N=AX }; }; // Class to specify run-time axis values struct AxisN { // Construct the run-time axis number explicit AxisN(const size_t n) : N(n) {} // Axis number size_t N; }; } // Axis independent base for the ArrayAccessor classes // // // The ArrayBaseAccessor class implements the axis independent parts of the // ArrayAccessor class. It can only be used from the ArrayAccessor class. // template class ArrayBaseAccessor { protected: //# Constructors // // Default constructor (for use in e.g. containers) ArrayBaseAccessor() : arrayPtr_p(0), axis_p(0), ptr_p(0), step_p(0), begin_p(0), end_p(0) {;} // Construct from an Array // explicit ArrayBaseAccessor(const Array &arr) : arrayPtr_p(&arr), axis_p(0), ptr_p(const_cast(arrayPtr_p->data())), step_p(0), begin_p(0), end_p(0) {;} ArrayBaseAccessor(const Array &arr, const size_t ax) : arrayPtr_p(&arr), axis_p(ax), ptr_p(const_cast(arrayPtr_p->data())), step_p(0), begin_p(0), end_p(0) {;} // // Copy constructor (copy semantics) // ArrayBaseAccessor(const ArrayBaseAccessor &other) : arrayPtr_p(other.arrayPtr_p), axis_p(other.axis_p), ptr_p(other.ptr_p), step_p(other.step_p), begin_p(other.begin_p), end_p(other.end_p) {;} ArrayBaseAccessor(const ArrayBaseAccessor &other, const size_t ax) : arrayPtr_p(other.arrayPtr_p), axis_p(ax), ptr_p(other.ptr_p), step_p(other.step_p), begin_p(other.begin_p), end_p(other.end_p) {;} // //# Destructor // Destructor ~ArrayBaseAccessor() {;} // // Assignment (copy semantics) ArrayBaseAccessor &operator=(const ArrayBaseAccessor &other) { if (&other != this) { arrayPtr_p = other.arrayPtr_p; ptr_p = other.ptr_p; }; return *this; } // (Re-)initialize from Array // void init(const Array &arr) { arrayPtr_p = &arr; ptr_p = const_cast(arrayPtr_p->data()); } void init(const Array &arr, const size_t ax) { arrayPtr_p = &arr; axis_p = ax; ptr_p = const_cast(arrayPtr_p->data()); } void init(const size_t ax) { arrayPtr_p = 0; axis_p = ax; ptr_p = 0; } // public: //# Operators // Iterator-like operations. // void operator+=(const size_t ix) { ptr_p += ix*step_p; } void operator-=(const size_t ix) { ptr_p -= ix*step_p; } void operator++() { ptr_p += step_p; } void operator++(int) { ptr_p += step_p; } void operator--() { ptr_p -= step_p; } void operator--(int) { ptr_p -= step_p; } // // Dereferencing. // const T &operator*() const { return *ptr_p; } T &operator*() { return *ptr_p; } T *data() { return ptr_p; } const Array &baseArray() { return *arrayPtr_p; } size_t step() { return step_p; } // // Index along current axis // const T &operator[](const int ix) const { return *(ptr_p + ix*step_p); }; T &operator[](const int ix) { return *(ptr_p + ix*step_p); } // // End of index on line // const T *end() { return end_p; } const T *end(const int n) { return end_p + n*step_p; } // // Start of index on line // const T *begin() { return begin_p; } const T *begin(const int n) { return begin_p + n*step_p; } // // End when reverse indexing // const T *rend() { return begin_p-step_p; } const T *rend(const int n) { return begin_p + (n-1)*step_p; } // // Begin when reverse indexing // const T *rbegin() { return end_p-step_p; } const T *rbegin(const int n) { return end_p + (n-1)*step_p; } // protected: //# Data // The pointer to belonging array const Array *arrayPtr_p; // Current run-time axis size_t axis_p; // Current access pointer T *ptr_p; // The increment to go from one point along an axis, to the next. int step_p; // The start element of array const T *begin_p; // The one element beyond last on line const T *end_p; }; // Fast 1D accessor/iterator for nD array classes // // // // //
          • Array indexing and access methods // (Array) // // // // Array and access, rather than Iterator, which would suggest more // standard-like interfaces // // // // Accessing a large multi-dimensional array by varying the indices of the // array can be a slow process. Timing indications are that for a cube // indexing with 3 indices was about seven times slower than using a // standard 1D C-like index into an array of basic int types. // Improvements have made this less, partly due to some pre-calculation // necessary for this class, but can still be a factor of more than 3 // slower. There are a variety of ways to access elements // cube(i,j,k): //
              //
            • Complete random access in all dimensions will need the // use of the indexing: cube(i,j,k); or // cube(IPosition(3)) as described in the // Array and // Cube classes //
            • Ordered access of all (or most) elements in an Array // (in memory order) can be best achieved by the use of Array's // STLIterator classes. // This is the fastest way for non-contiguous arrays, and only slightly // slower than the use of getStorage for contiguous arrays. //
            • Ordered access along memory order can also be achieved by the use // of the // // getStorage() method. // For contiguous arrays this could be slightly faster than the use of // the STLIterator (about 10% faster), but slower for // non-contiguous arrays. In addition it needs additional memory // resources, which will lead to extra overhead. The general use of // getStorage is discouraged with the introduction of the STLIterator. // It should only be used when an interface to routines in // other languages is needed (like Fortran), or when a large Array is // known to be contiguous, and the data have to be referenced many times. //
            • Access along one or more axes of a (large) multi-dimensional array // is best achieved using the ArrayAccessor class. Its total // access time is about 2 times faster than indexing (for cubes, // more for more indices), //
            • Special iteration (like in chunks) are catered for by the // ArrayIterator, // MatrixIterator, // VectorIterator classes. //
            // The ArrayAccessor class is an iterator like pointer to the data // in the array. It is a 1-dimensional accessor. It is created with either // a constant (at compile time) axis indicator, or with a run-time // axis selector. ArrayAccessor constructor accepts a const Array<>. // However, the underlying Array class can be modified at this moment. In // future a ConstArrayAccessor class is foreseen. // // Matrix mat(1000,500); // A 1000*500 matrix // // Fill Matrix ... // // Loop over index 1, than index 0: // for (ArrayAccessor > i(mat); i != i.end(); ++i) { // for (ArrayAccessor > j(i); j |= j.end(); ++j) { // // Actions on *j (which points to mat(j,i)) or j[n] // // (which points to mat(j+n,i)) // }} // // For run-time indices it would look like: // // Matrix mat(1000,500); // A 1000*500 matrix // // Fill Matrix ... // // Loop over index 1, than index 0: // for (ArrayAccessor i(mat, AxisN(1)); // i != i.end(); ++i) { // for (ArrayAccessor j(i,AxisN(0)); j |= j.end(); ++j) { // // Actions on *j (which points to mat(j,i)) or j[n] // // (which points to mat(j+n,i)) // }} // // Compile-time and run-time axes can be mixed in constructors and assignments. // // Like in all comparable situations, memory allocation // within a loop can slow down processes. For that reason the example above // can be better written (about 25% faster) as: // // Matrix mat(1000,500); // A 1000*500 matrix // ArrayAccessor > j; // accessor pre-allocated // // Fill Matrix ... // // Loop over index 1, than index 0: // for (ArrayAccessor > i(mat); i != i.end(); ++i) { // for (j=i; j |= j.end(); ++j) { // // Actions on *j (which points to mat(j,i)) or j[n] // // (which points to mat(j+n,i)) // }} // // // The underlying Array classes are structured with the // first index varying fastest. This means that in general (due to caching and // swapping) operations are fastest when Axis<0> > is in the // innermost loop (if possible of course). // // The demonstrator and test programs have more examples. // // The accessors can be dereferenced by the dereference operator (*) // and by the index operator ([int]), which can handle negative // values. // Points around the accessor in any axis direction can be addressed // along any axis by the templated methods next(), // prev() and index(int). Either run-time or // compile-time axes can be used (see example). // // An accessor can be re-initialized with the init() function. It can also // be reset() to any pointer value. Mthods end(), // begin(), rbegin() and rend() are available // for loop control (like in the STL iterators). In addition each of these // can have an optional integer argument, specifying an offset (in points // along the current axis). // // Operations ++ -- += -= are available. // // This class is available for Axis and AxisN // specializations only. //
            // // // // // get a cube and fill it // Cube cub(5,2,4); // indgen(cub); // // Loop over axes 2-0 and use index() over axis 1 // for (ArrayAccessor > i(cub); i != i.end() ; ++i) { // for (ArrayAccessor > j(i); // j != j.end(); ++j) { // // show result // cout << *j << ", " << j.index >(1) << endl; // }; // }; // // See the demonstrator program in // aips/implement/Arrays/test/dArrayAccessor.cc and the // test program tArrayAccessor for more examples. // // // // To speed up especially interpolation code // // // //
          • Any valid Array templating argument // // //
          • A class Axis //
          • Class AxisN // // // //
          • Exceptions created in the Array class //
          • Addressing errors // // // //
          • add a ConstArrayAccessor class // // // \see ArrayAccessor > // \see ArrayAccessor //# Next one suffices as declaration: only (part) specialisations allowed template class ArrayAccessor; // Specialization for compile-time axes. \see ArrayAccessor template class ArrayAccessor > : public ArrayBaseAccessor { public: // Constructors // // Default ctor. Note only available to accommodate containers of // ArrayAccessors. Use init() to initialize. ArrayAccessor() : ArrayBaseAccessor() {;} // Construct an accessor from specified Array along the selected axis. // The accessor will point to the first element along the axis (i.e. // at (0,0,...)). explicit ArrayAccessor(const Array &arr) : ArrayBaseAccessor(arr) { initStep(); } // Construct from an ArrayAccessor along same axis. The accessor will point // at the same element as the originator. ArrayAccessor(const ArrayAccessor > &other) : ArrayBaseAccessor(other) {;} // Construct from accessor along another (or run-time) axis. // The accessor will point to the same element (but will be oriented // along another axis). // template explicit ArrayAccessor(const ArrayAccessor > &other) : ArrayBaseAccessor(other) { initStep(); } explicit ArrayAccessor(const ArrayAccessor &other) : ArrayBaseAccessor(other) { initStep(); } // // Destructor ~ArrayAccessor() {;} // // Assignment (copy semantics) // // Assign from other compile-time accessor along same axis ArrayAccessor &operator=(const ArrayAccessor > &other) { if (&other != this) { ArrayBaseAccessor::operator=(other); this->step_p = other.step_p; this->begin_p = other.begin_p; this->end_p = other.end_p; }; return *this; } // Assign from other compile-time accessor along another axis template ArrayAccessor &operator=(const ArrayAccessor > &other) { ArrayBaseAccessor::operator=(other); initStep(); return *this; } // Assign from run-time accessor along any axis ArrayAccessor &operator=(const ArrayAccessor &other) { ArrayBaseAccessor::operator=(other); initStep(); return *this; } // // (Re-)initialization to start of array (i.e. element (0,0,0,...)) void init(const Array &arr) { ArrayBaseAccessor::init(arr); initStep(); } // Reset to start of dimension or to specified pointer // void reset() { this->ptr_p = const_cast(this->begin_p); } void reset(const T * p) { this->ptr_p = const_cast(p); initStep(); } // // Indexing operations along another axis than the one of the current // object. See for the indexing and iterator operations along the // object's axis ArrayBaseAccessor // // Get the value 'next' along the specified axis (e.g. with // a.next >()) // template const T &next() const { return *(this->ptr_p + this->arrayPtr_p->steps()[X::N]); } template T &next() { return *(this->ptr_p + this->arrayPtr_p->steps()[X::N]); } // // Get the value 'previous' along the specified axis (e.g. with // a.prev >()) // template const T &prev() const { return *(this->ptr_p - this->arrayPtr_p->steps()[X::N]); } template T &prev() { return *(this->ptr_p - this->arrayPtr_p->steps()[X::N]); } // // Get the next or previous along the specified run-time axis. E.g. // a.prev(AxisN(2)). // const T &next(const AxisN ax) const { return *(this->ptr_p + this->arrayPtr_p->steps()[ax.N]); } T &next(const AxisN ax) { return *(this->ptr_p + this->arrayPtr_p->steps()[ax.N]); } const T &prev(const AxisN ax) const { return *(this->ptr_p - this->arrayPtr_p->steps()[ax.N]); } T &prev(const AxisN ax) { return *(this->ptr_p - this->arrayPtr_p->steps()[ax.N]); } // // Give the value indexed with respect to the current accessor value // along the axis specified as either a compile-time or a run-time // axis. E.g. a.index >(5) or // a.index(5, AxisN(3)). // template const T &index(const int ix) const { return *(this->ptr_p + ix*this->arrayPtr_p->steps()[X::N]); } template T &index(const int ix) { return *(this->ptr_p + ix*this->arrayPtr_p->steps()[X::N]); } const T &index(const int ix, const AxisN ax) const { return *(this->ptr_p + ix*this->arrayPtr_p->steps()[ax.N]); } T &index(const int ix, const AxisN ax) { return *(this->ptr_p + ix*this->arrayPtr_p->steps()[ax.N]); } // // // Comparison. The comparisons are done for the accessor pointer // value. They can be used to control loops. // bool operator==(const ArrayAccessor > &other) const { return this->ptr_p == other.ptr_p; } bool operator!=(const ArrayAccessor > &other) const { return this->ptr_p != other.ptr_p; } bool operator==(const T *other) const { return this->ptr_p == other; } bool operator!=(const T *other) const { return this->ptr_p != other; } // private: // Get proper offset int initOff(int x, size_t ax) { size_t st = this->arrayPtr_p->steps()[ax]; return ((st) ? (ax == Axis::N ? x/st : initOff(x%st, ax-1)) : 0); } // Initialize some internal values void initStep() { this->step_p = this->arrayPtr_p->steps()[Axis::N]; this->begin_p = this->end_p = this->ptr_p - initOff(this->ptr_p - this->arrayPtr_p->data(), this->arrayPtr_p->ndim()-1)*this->step_p; this->end_p += this->arrayPtr_p->shape()[Axis::N]*this->step_p; } }; // Specialization for run-time axes // // // This class is a specialization for run-time axis selection within the // array accessor. The axis is specified in the constructors and in the // special indexing operators (prev, next, index) with // a parameter AxisN(n) in stead of a template parameter // >. // \see ArrayAccessor // // template class ArrayAccessor : public ArrayBaseAccessor { public: // Constructors // explicit ArrayAccessor(const AxisN ax=AxisN(0)) : ArrayBaseAccessor() { this->axis_p = ax.N; } explicit ArrayAccessor(Array &arr, const AxisN ax=AxisN(0)) : ArrayBaseAccessor(arr, ax.N) { initStep(); } ArrayAccessor(ArrayAccessor &other) : ArrayBaseAccessor(other) {;} explicit ArrayAccessor(ArrayAccessor &other, const AxisN ax) : ArrayBaseAccessor(other, ax.N) { initStep(); } template explicit ArrayAccessor(ArrayAccessor > &other, const AxisN ax=AxisN(0)) : ArrayBaseAccessor(other, ax.N) { initStep(); } ArrayAccessor &operator=(const ArrayAccessor &other) { if (&other != this) { ArrayBaseAccessor::operator=(other); initStep(); }; return *this; } template ArrayAccessor &operator=(const ArrayAccessor > &other) { ArrayBaseAccessor::operator=(other); initStep(); return *this; } // // Destructor ~ArrayAccessor() {;} // (Re-)initialization to start of array (i.e. element (0,0,0,...)) or // re-initialize to an axis. // void init(const Array &arr, const AxisN ax) { ArrayBaseAccessor::init(arr, ax.N); initStep(); } void init(const AxisN ax) { ArrayBaseAccessor::init(ax.N); } // // Reset to start of dimension or to specified pointer // void reset() { this->ptr_p = const_cast(this->begin_p); } void reset(const T *p) { this->ptr_p = const_cast(p); initStep(); } // // Indexing operations along another axis than the one of the current // object. See for the indexing and iterator operations along the // object's axis ArrayBaseAccessor // template const T &next() const { return *(this->ptr_p + this->arrayPtr_p->steps()[X::N]); } template T &next() { return *(this->ptr_p + this->arrayPtr_p->steps()[X::N]); } template const T &prev() const { return *(this->ptr_p - this->arrayPtr_p->steps()[X::N]); } template T &prev() { return *(this->ptr_p - this->arrayPtr_p->steps()[X::N]); } const T &next(const AxisN ax) const { return *(this->ptr_p + this->arrayPtr_p->steps()[ax.N]); } T &next(const AxisN ax) { return *(this->ptr_p + this->arrayPtr_p->steps()[ax.N]); } const T &prev(const AxisN ax) const { return *(this->ptr_p - this->arrayPtr_p->steps()[ax.N]); } T &prev(const AxisN ax) { return *(this->ptr_p - this->arrayPtr_p->steps()[ax.N]); } template const T &index(const int ix) const { return *(this->ptr_p + ix*this->arrayPtr_p->steps()[X::N]); } template T &index(const int ix) { return *(this->ptr_p + ix*this->arrayPtr_p->steps()[X::N]); } const T &index(const int ix, const AxisN(ax)) const { return *(this->ptr_p + ix*this->arrayPtr_p->steps()[ax.N]); } T &index(const int ix, const AxisN(ax)) { return *(this->ptr_p + ix*this->arrayPtr_p->steps()[ax.N]); } // // Comparisons // bool operator==(const ArrayAccessor &other) const { return this->ptr_p == other.ptr_p; } bool operator!=(const ArrayAccessor &other) const { return this->ptr_p != other.ptr_p; } bool operator==(const T *other) const { return this->ptr_p == other; } bool operator!=(const T *other) const { return this->ptr_p != other; } // private: // Get proper offset int initOff(int x, size_t ax) { size_t st = this->arrayPtr_p->steps()[ax]; return ((st) ? (ax == this->axis_p ? x/st : initOff(x%st, ax-1)) : 0); } // Initialize some internal values void initStep() { this->step_p = this->arrayPtr_p->steps()[this->axis_p]; this->begin_p = this->end_p = this->ptr_p - initOff(this->ptr_p - this->arrayPtr_p->data(), this->arrayPtr_p->ndim()-1)*this->step_p; this->end_p += this->arrayPtr_p->shape()[this->axis_p]*this->step_p; } }; } //#End casa namespace #endif casacore-3.7.1/casa/Arrays/ArrayBase.cc000066400000000000000000000615451476623553700176670ustar00rootroot00000000000000//# ArrayBase.cc: Non-templated base class for templated Array class //# Copyright (C) 1993,1994,1995,1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include "ArrayBase.h" #include "ArrayError.h" #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ArrayBase::ArrayBase() noexcept : nels_p (0), ndimen_p (0), contiguous_p (true) {} // // ArrayShapeError // ArrayBase::ArrayBase (const IPosition& Shape) : nels_p (Shape.product()), ndimen_p (Shape.nelements()), contiguous_p (true), length_p (Shape), inc_p (Shape.nelements(), 1), originalLength_p (Shape) { for (size_t i = 0; i < ndimen_p; i++) { if (Shape(i) < 0) { throw(ArrayShapeError(shape(), Shape, "ArrayBase::Array(const IPosition&)" " - Negative shape")); } } baseMakeSteps(); } ArrayBase::ArrayBase (const ArrayBase& other) : nels_p (other.nels_p), ndimen_p (other.ndimen_p), contiguous_p (other.contiguous_p), length_p (other.length_p), inc_p (other.inc_p), originalLength_p (other.originalLength_p), steps_p (other.steps_p) {} ArrayBase::ArrayBase (ArrayBase&& source) noexcept : nels_p (source.nels_p), ndimen_p (source.ndimen_p), contiguous_p (source.contiguous_p), length_p (source.length_p), inc_p (source.inc_p), originalLength_p (source.originalLength_p), steps_p (source.steps_p) { // Make source empty (self-assignment of move object is not allowed) source.nels_p = 0; source.ndimen_p = 0; source.contiguous_p = true; source.length_p = IPosition(); source.inc_p = IPosition(); source.originalLength_p = IPosition(); source.steps_p = IPosition(); } ArrayBase::ArrayBase (ArrayBase&& source, const IPosition& shapeForSource) noexcept : nels_p (source.nels_p), ndimen_p (source.ndimen_p), contiguous_p (source.contiguous_p), length_p (source.length_p), inc_p (source.inc_p), originalLength_p (source.originalLength_p), steps_p (source.steps_p) { // Set source to have given shape source.nels_p = shapeForSource.product(); source.ndimen_p = shapeForSource.nelements(); source.contiguous_p = true; source.length_p = shapeForSource; source.inc_p = IPosition(shapeForSource.nelements(), 1); source.originalLength_p = shapeForSource; source.steps_p = IPosition(); } ArrayBase& ArrayBase::assign (const ArrayBase& other) { if (this != &other) { nels_p = other.nels_p; ndimen_p = other.ndimen_p; contiguous_p = other.contiguous_p; if (ndimen_p != length_p.nelements()) { length_p.resize (ndimen_p); inc_p.resize (ndimen_p); originalLength_p.resize (ndimen_p); steps_p.resize (ndimen_p); } length_p = other.length_p; inc_p = other.inc_p; originalLength_p = other.originalLength_p; steps_p = other.steps_p; } return *this; } ArrayBase& ArrayBase::operator=(ArrayBase&& source) noexcept { nels_p = source.nels_p; ndimen_p = source.ndimen_p; contiguous_p = source.contiguous_p; length_p = std::move(source.length_p); inc_p = std::move(source.inc_p); originalLength_p = std::move(source.originalLength_p); steps_p = std::move(source.steps_p); // Make source empty (self-assignment of move object is not allowed) source.nels_p = 0; source.ndimen_p = 0; source.contiguous_p = true; source.length_p = IPosition(); source.inc_p = IPosition(); source.originalLength_p = IPosition(); source.steps_p = IPosition(); return *this; } ArrayBase::~ArrayBase() noexcept {} void ArrayBase::swap(ArrayBase& source) noexcept { std::swap(nels_p, source.nels_p); std::swap(ndimen_p, source.ndimen_p); std::swap(contiguous_p, source.contiguous_p); std::swap(length_p, source.length_p); std::swap(inc_p, source.inc_p); std::swap(originalLength_p, source.originalLength_p); std::swap(steps_p, source.steps_p); } void ArrayBase::baseReform (ArrayBase& tmp, const IPosition& len, bool strict) const { // Check if reform can be done. long long prod = len.nelements()==0 ? 0 : len.product(); if (strict && prod != (long long)(nelements())) { throw(ArrayConformanceError("ArrayBase::reform() - " "total elements differ: " + to_string(len) + " vs " + to_string(shape()))); } // Return if the new shape equals the current one. if (len.isEqual(length_p)) { return; } size_t newNdim = len.nelements(); // If the data is contiguous, a reform can simply be done // by inserting the new shape. if (contiguousStorage()) { tmp.ndimen_p = newNdim; tmp.length_p.resize (newNdim); tmp.length_p = len; tmp.inc_p.resize (newNdim); tmp.inc_p = 1; tmp.originalLength_p.resize (newNdim); tmp.originalLength_p = tmp.length_p; tmp.nels_p = len.product(); tmp.baseMakeSteps(); return; } // A reform of a non-contiguous array has to be done. // This is only possible if axes with length 1 are left out and/or added. bool valid = true; size_t oldPos=0; size_t newPos=0; int oldLen = length_p(0); int newLen = len(0); // Find the axes corresponding to the old shape. // copyAxes(i)<0 indicates that an axis with length 1 has been added. // When a shape array array is exhausted, its length variable is set // to 0. In that way trailing dimensions are handled without problem. IPosition copyAxes(newNdim, -1); while (valid && (oldLen>0 || newLen>0)) { if (oldLen == newLen) { copyAxes(newPos) = oldPos; oldPos++; newPos++; } else if (oldLen == 1) { oldPos++; } else if (newLen == 1) { newPos++; } else { // A new axis with length>1 has no corresponding original axis. valid = false; } oldLen = (oldPos >= length_p.nelements() ? 0 : length_p(oldPos)); newLen = (newPos >= len.nelements() ? 0 : len(newPos)); } if (!valid) { throw(ArrayConformanceError("ArrayBase::reform() - " "data not contiguous nor similarly shaped")); } // Great, the shapes match. Adjust the IPositions. // Set inc and originalLength initially to 1 (caters for added axes). tmp.ndimen_p = newNdim; tmp.length_p.resize (newNdim); tmp.length_p = len; tmp.inc_p.resize (newNdim); tmp.inc_p = 1; tmp.originalLength_p.resize (newNdim); tmp.originalLength_p = 1; // When an axis has been removed Inc and originalLength have to be adjusted // by multiplying them with the originalLength of the removed axes. size_t startAxis = 0; for (size_t i=0; i= 0) { tmp.inc_p(i) = inc_p(copyAxes(i)); tmp.originalLength_p(i) = originalLength_p(copyAxes(i)); for (int j=startAxis; j 0); // These data members are the same irrespective of the degenerate axes. nels_p = other.nels_p; contiguous_p = other.contiguous_p; // To remove degenerate axes use two passes - first find out how many axes // have to be kept. size_t i; size_t nd = other.ndim(); // First determine which axes have to be ignored, thus always be kept. // Do not count here, because in theory ignoreAxes can contain the // same axis more than once. IPosition keepAxes(nd, 0); for (i=0; i nElementsAllocated); if (resizeNeeded && ! resizeIfNeeded){ // User did not permit resizing but it is required so throw and exception. std::string message = "ArrayBase::reformOrResize() - insufficient storage for reform: " "nElementInAllocation=" + std::to_string(nElementsAllocated) + ", nElementsRequested=" + std::to_string(newShape.product()); throw ArrayConformanceError(message); } // The operation is legal, so perform it // ===================================== bool resetEnd = true; // Caller will need to reset the end iterator if (resizeNeeded){ // Insufficient storage so resize required, with or without padding if (resizePercentage <= 0){ // Perform an exact resize resize (newShape, copyDataIfNeeded); resetEnd = false; } else { // Padding was requested so resize to match the padded shape // and then reform it to use the desired shape. IPosition paddedShape; paddedShape = newShape; paddedShape.last() = (paddedShape.last() * (100 + resizePercentage)) / 100; resize (paddedShape, copyDataIfNeeded); // Reform it baseReform (* this, newShape, false); } } else { baseReform (* this, newShape, false); } return resetEnd; } // // ArrayError // size_t ArrayBase::makeSubset (ArrayBase& out, const IPosition& b, const IPosition& e, const IPosition& i) { if (b.nelements() != ndim() || e.nelements() != ndim() || i.nelements() != ndim()) { std::ostringstream os; os << "ArrayBase::operator()(b,e,i) - ndim() b: " << b.nelements() << " e: " << e.nelements() << " i: " << i.nelements() << " differs from the array ndim " << ndim(); throw(ArrayError(os.str())); } size_t j; for (j=0; j < ndim(); j++) { if (b(j) < 0 || b(j) > e(j)+1 || e(j) >= length_p(j) || i(j) < 1) { std::ostringstream os; os << "ArrayBase::operator()(b,e,i) - incorrectly specified\n"; os << "begin: " << b << '\n'; os << "end: " << e << '\n'; os << "incr: " << i << '\n'; os << '\n'; os << "array shape: " << length_p << '\n'; os << "required: b >= 0; b <= e; e < shape; i >= 0" << '\n'; throw(ArrayError(os.str())); } } size_t offs=0; for (j=0; j= length_p[firstAxis]) throw ArrayConformanceError("ArrayBase::diagonal() - " "diagonal out of range"); /// cout<= 0) { offs = diag * steps_p[firstAxis+1]; } else { offs = (-diag) * steps_p[firstAxis]; } baseMakeSteps(); /// cout< // ArrayNDimErrror // ArrayShapeError // void ArrayBase::validateConformance (const ArrayBase& other) const { assert(ok()); if (! conform2(other)) { if (ndim() != other.ndim()) { throw(ArrayNDimError(ndim(), other.ndim(), "ArrayBase::validateConformance")); } else { throw(ArrayShapeError(shape(), other.shape(), "ArrayBase::validateConformance")); } } } // // ArrayNDimErrror // ArrayIndexError // void ArrayBase::validateIndex (const IPosition& i) const { assert(ok()); if (ndim() != i.nelements()) { throw(ArrayNDimError(ndim(), i.nelements(), "ArrayBase::validateIndex - ndims of index" " and array differ")); } for (size_t j=0; j < ndim(); j++) { if (i(j) < 0 || i(j) >= length_p(j)) { throw(ArrayIndexError(i, length_p)); } } // OK - normal return } void ArrayBase::validateIndex (size_t index) const { validateIndex (IPosition(1, index)); } void ArrayBase::validateIndex (size_t index1, size_t index2) const { IPosition inx(2); inx[0] = index1; inx[1] = index2; validateIndex (inx); } void ArrayBase::validateIndex (size_t index1, size_t index2, size_t index3) const { IPosition inx(3); inx[0] = index1; inx[1] = index2; inx[2] = index3; validateIndex (inx); } bool ArrayBase::copyVectorHelper (const ArrayBase& other) { bool Conform = conform2(other); if (!Conform && length_p(0) != 0) { validateConformance(other); // We can't overwrite, so throw exception } if (!Conform) { // copy in place length_p = other.length_p; nels_p = other.nels_p; originalLength_p = length_p; baseMakeSteps(); } return Conform; } bool ArrayBase::isStorageContiguous() const { int i; int nd = ndim(); if (nd == 0) { return true; } // If we have increments, we're definitely not contiguous (unless the axis // length is one!) for (i=0; i < nd; i++) { if ((inc_p(i) != 1) && (length_p(i) != 1)) { return false; } } // If we don't fill up the region (except for the last dimension), then // we're also not contiguous // // ------------------------- // | | // | | // | | // | +---+ | // | | | | // | | | | // | +---+ | // ------------------------- // // Here, even though the increment is one, we need to make a copy since // all the elements in the sub-region aren't contiguous. Note, though, that // the lengths don't need to be identical in the last axis. // Trailing lengths equal to 1 can be skipped. while (nd > 1 && length_p(nd-1) == 1) { nd--; } for (i=0; i < nd - 1; i++) { if (length_p(i) != originalLength_p(i)) { return false; } } // If we've made it here, we are contiguous! return true; } void ArrayBase::baseMakeSteps() { // No Assert since the Array may not be constructed yet when // calling this. steps_p.resize (ndimen_p); int size = 1; for (size_t i=0; i= 0); assert(inc_p(i) >= 1); assert(originalLength_p(i) >= length_p(i)); if (length_p(i) < 0 || inc_p(i) < 1 || originalLength_p(i) < length_p(i)) { return false; } count *= length_p(i); if (length_p(i) > 1) { pos(i) = 1; size_t off = ArrayIndexOffset(ndim(), originalLength_p.storage(), inc_p.storage(), pos); pos(i) = 0; assert(size_t(steps_p(i)) == off); if (size_t(steps_p(i)) != off) { return false; } } } if (ndim() == 0) { count = 0; } assert(count == nelements()); if (count != nelements()) { return false; } assert(contiguous_p == isStorageContiguous()); if (contiguous_p != isStorageContiguous()) { return false; } return true; } void ArrayBase::checkVectorShape() { if (ndim() != 1) { // Check if all elements are 1 or nels_p. In this way we are sure that // only one axis remains (i.e. at most one axis has length > 1). // Keep original increment and length of the remaining axis. int inc = 1; int orLen = 1; int skippedVolume = 1; for (size_t i=0; i: ndim of other array > 1")); } inc = inc_p(i) * skippedVolume; orLen = originalLength_p(i) * skippedVolume; break; } } ndimen_p = 1; length_p.resize(1); inc_p.resize(1); originalLength_p.resize(1); steps_p.resize(1); length_p(0) = nels_p; inc_p(0) = inc; originalLength_p(0) = orLen; steps_p(0) = inc; } } void ArrayBase::checkMatrixShape() { if (ndim() > 2) { throw(ArrayNDimError(2, ndim(), "Matrix: ndim of other array > 2")); } if (ndim() < 2) { // We need to fiddle a bit if ndim < 2. length_p.resize(2); inc_p.resize(2); originalLength_p.resize(2); if (ndim() == 0) { length_p(0) = 0; length_p(1) = 0; inc_p(0) = 1; inc_p(1) = 1; originalLength_p(0) = 0; originalLength_p(1) = 0; } else { length_p(1) = (nelements() == 0) ? 0 : 1; originalLength_p(1) = length_p(1); inc_p(1) = 1; } ndimen_p = 2; baseMakeSteps(); } } void ArrayBase::checkCubeShape() { if (ndim() > 3) { throw(ArrayNDimError(3, ndim(), "Cube: ndim of other array > 3")); } // We need to fiddle a bit if ndim < 3. if (ndim() < 3) { length_p.resize(3); inc_p.resize(3); originalLength_p.resize(3); int len = (nelements()==0) ? 0 : 1; if (ndim() == 0) { len = 0; length_p(0) = 0; inc_p(0) = 1; originalLength_p(0) = 0; } if (ndim() < 2) { length_p(1) = len; inc_p(1) = 1; originalLength_p(1) = len; } length_p(2) = len; inc_p(2) = 1; originalLength_p(2) = len; ndimen_p = 3; baseMakeSteps(); } } std::unique_ptr ArrayBase::makeArray() const { throw ArrayError ("ArrayBase::makeArray cannot be used"); } void ArrayBase::resize(const IPosition&, bool) { throw ArrayError ("ArrayBase::resize cannot be used"); } std::unique_ptr ArrayBase::makeIterator (size_t) const { throw ArrayError ("ArrayBase::makeIterator cannot be used"); } std::unique_ptr ArrayBase::getSection (const Slicer&) const { throw ArrayError ("ArrayBase::getSection cannot be used"); } void ArrayBase::assignBase (const ArrayBase&, bool) { throw ArrayError ("ArrayBase::assign cannot be used"); } void* ArrayBase::getVStorage (bool&) { throw ArrayError ("ArrayBase::getVStorage cannot be used"); } const void* ArrayBase::getVStorage (bool&) const { throw ArrayError ("ArrayBase::getVStorage cannot be used"); } void ArrayBase::putVStorage(void*&, bool) { throw ArrayError ("ArrayBase::putVStorage cannot be used"); } void ArrayBase::freeVStorage(const void*&, bool) const { throw ArrayError ("ArrayBase::freeVStorage cannot be used"); } void throwArrayShapes (const IPosition& shape1, const IPosition& shape2, const char* name) { throw ArrayConformanceError ("ArrayMath/Logical function " + std::string(name) + ": array shapes " + shape1.toString() + " and " + shape2.toString() + " differ"); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Arrays/ArrayBase.h000066400000000000000000000302211476623553700175140ustar00rootroot00000000000000//# ArrayBase.h: Non-templated base class for templated Array class //# Copyright (C) 1993,1994,1995,1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA, //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_ARRAYBASE_2_H #define CASA_ARRAYBASE_2_H //# Includes #include "IPosition.h" #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations. class ArrayPositionIterator; class Slicer; // // A global enum used by some Array constructors. // // // StorageInitPolicy is used in functions where an array is formed from // a shape and an ordinary pointer. This enum should be in Array but that // causes gcc to be unhappy. // enum StorageInitPolicy { // COPY is used when an internal copy of the storage is to be made. // The array is NOT responsible for deleting the external storage. COPY, // TAKE_OVER is used to indicate that the Array should just use the // external storage (i.e., no copy is made). The Array class is now // responsible for deleting the storage (hence it must have come from // a call to new[]). TAKE_OVER, // Share means that the Array will just use the pointer (no copy), however // the Array will NOT delete it upon destruction. SHARE}; // // Non-templated base class for templated Array class. // // ArrayBase is only used to factor out common code from the templated // Array class. class ArrayBase { public: ArrayBase() noexcept; // Create an array of the given shape, i.e. after construction // array.ndim() == shape.nelements() and array.shape() == shape. // The origin of the Array is zero. explicit ArrayBase (const IPosition& shape); // Copy constructor. ArrayBase (const ArrayBase& other); ArrayBase (ArrayBase&& source) noexcept; // Assignment. ArrayBase& assign (const ArrayBase&); ArrayBase& operator=(const ArrayBase&) = delete; ArrayBase& operator=(ArrayBase&&) noexcept; // Destructor. virtual ~ArrayBase() noexcept; // The dimensionality of this array. size_t ndim() const { return ndimen_p; } // How many elements does this array have? Product of all axis lengths. // size_t nelements() const { return nels_p; } size_t size() const { return nels_p; } // // Is the array empty (i.e. no elements)? bool empty() const { return nels_p == 0; } // Are the array data contiguous? // If they are not contiguous, getStorage (see below) // needs to make a copy. bool contiguousStorage() const { return contiguous_p; } // Check to see if the Array is consistent. This is about the same thing // as checking for invariants. If AIPS_DEBUG is defined, this is invoked // after construction and on entry to most member functions. virtual bool ok() const; // The length of each axis. const IPosition& shape() const { return length_p; } // A convenience function: endPosition(i) = shape(i) - 1; i.e. this // is the IPosition of the last element of the Array. IPosition endPosition() const; // Return steps to be made if stepping one element in a dimension. // This is the 'physical' step, thus it also works correctly for // non-contiguous arrays. E.g. data() + steps(0) gives // the second element of the first axis. const IPosition& steps() const { return steps_p; } // Array version for major change (used by ArrayIO). // enum did not work properly with cfront 3.0.1), so replaced // by a static inline function. Users won't normally use this. static unsigned arrayVersion() {return 3;} // Make an empty array of the same type. //
            The default implementation in ArrayBase throws an exception. virtual std::unique_ptr makeArray() const; // Resize the array and optionally copy the values. //
            The default implementation in ArrayBase throws an exception. virtual void resize(const IPosition &newShape, bool copyValues=false); // Resize the array and optionally copy the values. //
            The default implementation in ArrayBase throws an exception. //virtual void resize(const IPosition &newShape, bool copyValues, ArrayInitPolicy policy); // Create an ArrayIterator object of the correct type. // This is implemented in the derived Array classes. //
            The default implementation in ArrayBase throws an exception. virtual std::unique_ptr makeIterator (size_t byDim) const; // Get a reference to a section of an array. // This is the same as Array::operator(), but without having to know // the exact template type. //
            The default implementation in ArrayBase throws an exception. virtual std::unique_ptr getSection (const Slicer&) const; // Assign the source array to this array. // If checkType==true, it is checked if the underlying template // types match. Otherwise, it is only checked in debug mode (for performance). //
            The default implementation in ArrayBase throws an exception. virtual void assignBase (const ArrayBase& source, bool checkType=true); // The following functions behave the same as the corresponding getStorage // functions in the derived templated Array class. // They handle a pointer to a contiguous block of array data. // If the array is not contiguous, a copy is used to make it contiguous. // virtual void* getVStorage (bool& deleteIt); virtual const void* getVStorage (bool& deleteIt) const; virtual void putVStorage(void*& storage, bool deleteAndCopy); virtual void freeVStorage(const void*& storage, bool deleteIt) const; // protected: // For subclasses, this move constructor allows the moved-from object to // obtain a given shape after resizing. This way, e.g. a source Matrix can // still kee a dimensionality of 2. ArrayBase(ArrayBase&& source, const IPosition& shapeForSource) noexcept; void swap(ArrayBase& source) noexcept; // Either reforms the array if size permits or resizes it to the new shape. // Implementation of Array::reformOrResize (slightly different signature). bool reformOrResize (const IPosition & newShape, bool resizeIfNeeded, size_t nReferences, long long nElementsAllocated, bool copyDataIfNeeded, size_t resizePercentage); // Determine if the storage of a subset is contiguous. bool isStorageContiguous() const; // Check if the shape of a vector is correct. If possible, adjust if not. // It is possible if at most one axis has length > 1. void checkVectorShape(); // Check if the shape of a matrix is correct. Adjust it if smaller. void checkMatrixShape(); // Check if the shape of a cube is correct. Adjust it if smaller. void checkCubeShape(); // Reform the array to a shape with the same nr of elements. If nonStrict then // caller assumes responsibility for not overrunning storage (avoid or use with extreme care). void baseReform (ArrayBase& tmp, const IPosition& shape, bool strict=true) const; // Remove the degenerate axes from the Array object. // This is the implementation of the nonDegenerate functions. // It has a different name to be able to make it virtual without having // the "hide virtual function" message when compiling derived classes. void baseNonDegenerate (const ArrayBase& other, const IPosition& ignoreAxes); // These member functions return an Array reference with the specified // number of extra axes, all of length one, appended to the end of the // Array. Note that the reform function can also be // used to add extra axes. void baseAddDegenerate (ArrayBase&, size_t numAxes); // Make a subset of an array. // It checks if start,end,incr are within the array limits. // It returns the offset of the subset in the (original) array. size_t makeSubset (ArrayBase& out, const IPosition& b, const IPosition& e, const IPosition& i); // Set the length and stride such that the diagonal of the matrices // defined by two consecutive axes is formed. // diag == 0 indicates the main diagonal, >0 above, <0 below. // It returns the offset of the diagonal in the (original) array. size_t makeDiagonal (size_t firstAxis, long long diag); // Are the shapes identical? bool conform2 (const ArrayBase& other) const { return length_p.isEqual (other.length_p); } // Make the indexing step sizes. void baseMakeSteps(); // Helper function for templated Vector class. // It returns if this and other are conformant. bool copyVectorHelper (const ArrayBase& other); public: // Various helper functions. // void validateConformance (const ArrayBase&) const; void validateIndex (const IPosition&) const; void validateIndex (size_t index) const; void validateIndex (size_t index1, size_t index2) const; void validateIndex (size_t index1, size_t index2, size_t index3) const; // protected: // Number of elements in the array. Cached rather than computed. size_t nels_p; // Dimensionality of the array. size_t ndimen_p; // Are the data contiguous? bool contiguous_p; // Used to hold the shape, increment into the underlying storage // and originalLength of the array. IPosition length_p, inc_p, originalLength_p; // Used to hold the step to next element in each dimension. IPosition steps_p; }; // General global functions for Arrays. // // // //
          • Array // // // // These are generally useful global functions which operate on all // Arrays. // // // // Array general global functions -- General global functions // for Arrays. // // // // // What is the volume of an N-dimensional array. // Shape[0]*Shape[1]*...*Shape[N-1]. An Array helper function. //# Implemented in Array2.cc. size_t ArrayVolume (size_t Ndim, const int* Shape); // // What is the linear index into an "Ndim" dimensional array of the given // "Shape", "Origin", and "Increment" for a given IPosition Index. // An Array helper function. // //# Implemented in Array2.cc. size_t ArrayIndexOffset (size_t Ndim, const ssize_t* Shape, const ssize_t* Origin, const ssize_t* Inc, const IPosition& Index); size_t ArrayIndexOffset (size_t Ndim, const ssize_t* Shape, const ssize_t* Inc, const IPosition& Index); // // Function to check the shapes. It throws an exception if not equal. // void throwArrayShapes (const IPosition& shape1, const IPosition& shape2, const char* name); inline void checkArrayShapes (const ArrayBase& left, const ArrayBase& right, const char* name) { if (! left.shape().isEqual (right.shape())) { throwArrayShapes (left.shape(), right.shape(), name); } } // // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Arrays/ArrayError.cc000066400000000000000000000100631476623553700200730ustar00rootroot00000000000000//# ArrayError.cc: Exception classes thrown by Array and related classes/functions //# Copyright (C) 1993,1994,1995,1997,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // .SUMMARY General, Indexing, and Conformace errors thrown by Array classes. #include "ArrayError.h" namespace casacore { //# NAMESPACE CASACORE - BEGIN ArrayError::ArrayError() : std::runtime_error("ArrayError") {} ArrayError::ArrayError(const char *m) : std::runtime_error(m) {} ArrayError::ArrayError(const std::string &m) : std::runtime_error(m) {} ArrayError::~ArrayError() noexcept{} ArrayIndexError::ArrayIndexError() : ArrayError("ArrayIndexError") {} ArrayIndexError::ArrayIndexError(const char* m) : ArrayError(m) {} ArrayIndexError::ArrayIndexError(const std::string &m) : ArrayError(m) {} ArrayIndexError::ArrayIndexError(const IPosition &in, const IPosition &sh, const char* m) : ArrayError(m), i(in), l(sh) { // Nothing } ArrayIndexError::~ArrayIndexError() noexcept{} IPosition ArrayIndexError::index() const { return i; } IPosition ArrayIndexError::shape() const { return l; } ArrayConformanceError::ArrayConformanceError() : ArrayError("ArrayConformanceError") { // Nothing } ArrayConformanceError::ArrayConformanceError(const char* m) : ArrayError(m) {} ArrayConformanceError::ArrayConformanceError(const std::string &m) : ArrayError(m) {} ArrayConformanceError::~ArrayConformanceError() noexcept{} ArrayNDimError::ArrayNDimError(int ndim1, int ndim2, const char* m) : ArrayConformanceError(m + std::string(" -- ndim ") + std::to_string(ndim1) + " differs from " + std::to_string(ndim2)), r1(ndim1), r2(ndim2) {} ArrayNDimError::ArrayNDimError(int ndim1, int ndim2, const std::string& m) : ArrayConformanceError(m + std::string(" -- ndim ") + std::to_string(ndim1) + " differs from " + std::to_string(ndim2)), r1(ndim1), r2(ndim2) {} ArrayNDimError::~ArrayNDimError() noexcept{} void ArrayNDimError::ndims(int &ndim1, int &ndim2) const { ndim1 = r1; ndim2 = r2; } ArrayShapeError::ArrayShapeError(const IPosition &s1, const IPosition & s2, const char* m) : ArrayConformanceError(m + std::string(" shape ") + s1.toString() + " differs from " + s2.toString()), sh1(s1), sh2(s2) { // Nothing } ArrayShapeError::~ArrayShapeError() noexcept{} void ArrayShapeError::shapes(IPosition &shape1, IPosition &shape2) const { shape1 = sh1; shape2 = sh2; } ArrayIteratorError::ArrayIteratorError() : ArrayError("ArrayIteratorError") {} ArrayIteratorError::ArrayIteratorError(const char* m) : ArrayError(m) {} ArrayIteratorError::ArrayIteratorError(const std::string &m) : ArrayError(m) {} ArrayIteratorError::~ArrayIteratorError() noexcept{} ArraySlicerError::ArraySlicerError() : ArrayError("Slicer error") {} ArraySlicerError::ArraySlicerError(const std::string &m) : ArrayError("Slicer error:" + m) {} ArraySlicerError::~ArraySlicerError() noexcept{} } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Arrays/ArrayError.h000066400000000000000000000164711476623553700177460ustar00rootroot00000000000000//# ArrayError.h: Exception classes thrown by Array and related classes/functions //# Copyright (C) 1993,1994,1995,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_ARRAYERROR_2_H #define CASA_ARRAYERROR_2_H //# Includes #include #include #include "IPosition.h" namespace casacore { //# NAMESPACE CASACORE - BEGIN // The base class for all Array exception classes. // // // // ArrayError is the base class for all the Array-specific exception classes, // i.e. if it is caught you will catch (through inheritance) all Array-specific // exceptions. Note that (presently, anyway) the Array classes will throw // a few non-Array exceptions. // // try { // // Some lines, functions, ... // } catch (ArrayError x) { // // Array specific errors // } catch (std::exception x) { // // All other errors caught here. // } // // //# There are too many Array related error classes. Some should be deleted. class ArrayError : public std::runtime_error { public: // Initialize with the message "ArrayError" ArrayError(); // Initialize with the supplied message. ArrayError(const char *m); // Initialize with the supplied message. ArrayError(const std::string& m); ~ArrayError() noexcept; }; // An error thrown when an index is out of range // // // // The ArrayIndexError class, which is derived from ArrayError, is intended // to be thrown when an index is out-of-bounds. It contains within it // the offending index, as well as the shape of the array which // is being indexed. This should be multiply-derived from // indexError defined in Error.h. class ArrayIndexError : public ArrayError { public: // Initialize with the message "ArrayIndexError". ArrayIndexError(); // Initialize with the supplied message, the index and shape are null. ArrayIndexError(const char *m); // Initialize with the supplied message, the index and shape are null. ArrayIndexError(const std::string &m); // Initialize with a given out-of-bounds index, as well as the shape // of the array and a supplied message. ArrayIndexError(const IPosition &index, const IPosition &shape, const char *m="ArrayIndexError"); ~ArrayIndexError() noexcept; // The out-of-bounds index. IPosition index() const; // The shape of the violated array. IPosition shape() const; private: //# index, offset, length IPosition i,l; }; // An error thrown when two arrays do not conform // // // // The ArrayConformanceError class is the base class for all errors thrown // because two arrays are not conformant. See also the ArrayShapeError and // ArrayNDimError classes which are derived from it. This error, or one derived // from it, os normally thrown from a binary operation (arithmetic, logical, // assignment, etc). class ArrayConformanceError : public ArrayError { public: // Initialize the message with "ArrayConformanceError". ArrayConformanceError(); // Initialize with a supplied message. ArrayConformanceError(const char *m); // Initialize with a supplied message. ArrayConformanceError(const std::string& m); ~ArrayConformanceError() noexcept; }; // Thrown when two arrays have different dimensionality // // // // An ArrayNDimError is derived from ArrayConformanceError. It is thrown when // two arrays are non-conformant by virtue of having different dimensionality. // It holds within it the two dimensions. class ArrayNDimError : public ArrayConformanceError { public: // Define the two (presumably different) messages and optionally // supply a message. ArrayNDimError(int dim1, int dim2, const char *m="ArrayNDimError"); ArrayNDimError(int dim1, int dim2, const std::string& m); ~ArrayNDimError() noexcept; // Return the stored dimensions. NB modifies arguments. void ndims(int &dim1, int &dim2) const; // modifies arguments private: int r1, r2; }; // An error thrown when two arrays have different shapes // // // // An ArrayShapeError is derived from ArrayConformanceError. It is thrown when // two arrays are non-conformant by virtue of having different shapes. // It holds within it the two different two shapes. class ArrayShapeError : public ArrayConformanceError { public: // Define an ArrayShapeError with the two (presumably different) shapes // and an optional supplied message. ArrayShapeError(const IPosition &shape1, const IPosition &shape2, const char *m="ArrayShapeError"); ~ArrayShapeError() noexcept; // Get back the stored shapes. NB modifies arguments. void shapes(IPosition &, IPosition &) const; // modifies arguments private: IPosition sh1, sh2; }; // An error thrown by an ArrayIterator // // // // An ArrayIteratorError is thrown by an array iterator or related class // (e.g. VectorIterator). class ArrayIteratorError : public ArrayError { public: // Initialize with the message "ArrayIteratorError. ArrayIteratorError(); // Initialize with the supplied message ArrayIteratorError(const char *m); // Initialize with the supplied message ArrayIteratorError(const std::string &m); ~ArrayIteratorError() noexcept; }; // An error thrown by an Slicer member function // // // // An ArraySlicerError is thrown by an Slicer member function. class ArraySlicerError : public ArrayError { public: // Initialize with the message "Slicer error." ArraySlicerError(); // Initialize with ArraySlicerError plus the supplied message ArraySlicerError(const std::string &m); ~ArraySlicerError() noexcept; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Arrays/ArrayFwd.h000066400000000000000000000010001476623553700173530ustar00rootroot00000000000000#ifndef ARRAY2_ARRAY_FWD_H #define ARRAY2_ARRAY_FWD_H #include namespace casacore { //#Begin casa namespace template class Array; template class Vector; template class Matrix; template class Cube; typedef bool LogicalArrayElem; typedef Array LogicalArray; template class MaskedArray; typedef MaskedArray MaskedLogicalArray; class Slice; class Slicer; template class ArrayIterator; } #endif casacore-3.7.1/casa/Arrays/ArrayIter.h000066400000000000000000000203271476623553700175530ustar00rootroot00000000000000//# ArrayIter.h: Iterate an Array cursor through another Array. //# Copyright (C) 1993,1994,1995,1996,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_ARRAYITER2_H #define CASA_ARRAYITER2_H #include "ArrayPosIter.h" #include "Array.h" namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Iterate an Array cursor through another Array. // // // // ArrayIterator steps an array section (the "cursor") through an array. // The cursor "refers" to storage in the array, so that changing the // values in the cursor changes values in the original array. Like with // ArrayPositionIterator, the cursor presently only moves through the array from // bottom to top in the obvious way; however one may of course iterate // through a slice ("array section"). This class is derived from // ArrayPositionIterator since it also has a position (the blc of the cursor) // which moves through the array volume. // // The origin of the cursor, i.e. the subarray that moves // through the larger array, is always zero. // // // // Array to, from; // //... set to and from, check that they are conformant // ArrayIterator toiter(to,1); // ArrayIterator fromiter(from,1); // while (! toiter.pastEnd() ) { // toiter.array() = fromiter.array(); // copy vector by vector // toiter.next(); fromiter.next(); // } // // // // // ArrayIterator -- Iterate an Array cursor through another Array. // // template class ArrayIterator : public ArrayPositionIterator { public: // Step through array "arr" over the first byDim axes // (using a cursor of dimensionality "byDim"). explicit ArrayIterator(const Array &arr, size_t byDim=1); // Step through an array using the given axes. // The axes can be given in two ways: //
              //
            1. axesAreCursor=true means that the axes form the cursor axes. // The remaining axes will form the iteration axes. // This is the default. //
            2. axesAreCursor=false means the opposite. // In this case the iteration axes can be given in any order. //
            // E.g. when using iteration axes 2,0 for an array with shape [5,3,7], each // iteration step returns a cursor (containing the data of axis 1). // During the iteration axis 2 will vary most rapidly (as it was // given first). ArrayIterator(const Array &arr, const IPosition &axes, bool axesAreCursor = true); // Move the cursor to the next position. virtual void next() override; // Set the cursor to the given position. // The position can only contain the iteration axes or it can be the full // position. //
            In the first case the position must to be given in the order // of the iteration axes as given in the constructor. // In the latter case the position must be given in natural order // (as given by function pos and only the cursor axes are taken // into account. virtual void set (const IPosition& cursorPos) override; // Reset the cursor to the beginning. // virtual void reset() override; // // Return the cursor. (Perhaps we should have a fn() that returns a // reference to the original array as well?) // Array &array() {return *ap_p;} virtual ArrayBase& getArray() override; // protected: // The cursor std::unique_ptr> ap_p; private: // helper function to centralize construction work void init(const Array &); // helper function to set the pointer to the new data position in ap // after a step in the given dimension. -1 resets it to the beginning. void apSetPointer(int stepDim); Array pOriginalArray_p; IPosition offset_p; T* dataPtr_p; //# Presently the following are not defined. ArrayIterator(const ArrayIterator &); ArrayIterator &operator=(const ArrayIterator &); }; // // Iterate a const Array cursor through a const Array. // // // // This class behaves exactly like an ArrayIterator, only it iterates through // const Arrays. // // // void CopyArray(Array &to, const Array &from) // { // //... check that they are conformant // ArrayIterator toiter(to,1); // ReadOnlyArrayIterator fromiter(from,1); // while (! toiter.pastEnd() ) { // toiter.array() = fromiter.array(); // copy vector by vector // toiter.next(); fromiter.next(); // } // } // // This class is not derived from ArrayPositionIterator. For simplicity // it merely contains an ArrayIterator to which it forwards requests // and returns (const) results. The iterator classes should be // rethought and reimplemented. // // // // ReadOnlyArrayIterator -- Iterate a const Array cursor through // a const Array. // // template class ReadOnlyArrayIterator { public: // Step through array "arr" using a cursor of dimensionality "byDim". explicit ReadOnlyArrayIterator(const Array &arr, size_t byDim=1) : ai(const_cast&>(arr),byDim) {} // Step through an array for the given iteration axes. ReadOnlyArrayIterator(const Array &arr, const IPosition &axes, bool axesAreCursor = true) : ai(const_cast&>(arr),axes,axesAreCursor) {} // Move the cursor to the next position. void next() {ai.next();} // Set the cursor to the given position. // The position can only contain the iteration axes or it can be the full // position. //
            In the first case the position must to be given in the order // of the iteration axes as given in the constructor. // In the latter case the position must be given in natural order // (as given by function pos and only the cursor axes are taken // into account. void set (const IPosition& cursorPos) {ai.set(cursorPos);} // Reset the cursor to the beginning. // void reset() {ai.origin();} void origin() {ai.origin();} // // Return the cursor. (Perhaps we should have a fn() that returns a // reference to the original array as well?) const Array &array() {return ai.array();} // The same as the functions in ArrayPositionIterator. // bool atStart() const {return ai.atStart();} bool pastEnd() const {return ai.pastEnd();} const IPosition &pos() const {return ai.pos();} IPosition endPos() const {return ai.endPos();} size_t ndim() const {return ai.ndim();} // private: // Not implemented. // ReadOnlyArrayIterator (const ReadOnlyArrayIterator &); ReadOnlyArrayIterator &operator=(const ReadOnlyArrayIterator &); // ArrayIterator ai; }; } //# NAMESPACE CASACORE - END #include "ArrayIter.tcc" #endif casacore-3.7.1/casa/Arrays/ArrayIter.tcc000066400000000000000000000112431476623553700200720ustar00rootroot00000000000000//# ArrayIter.cc: Iterate an Array cursor through another Array //# Copyright (C) 1993,1994,1995,1997,1999,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_ARRAYITER_2_TCC #define CASA_ARRAYITER_2_TCC #include "ArrayIter.h" #include "ArrayError.h" namespace casacore { //# NAMESPACE CASACORE - BEGIN template ArrayIterator::ArrayIterator(const Array &a, size_t byDim) : ArrayPositionIterator(a.shape(), byDim), ap_p(), pOriginalArray_p() { init(a); } template ArrayIterator::ArrayIterator(const Array &a, const IPosition &axes, bool axesAreCursor) : ArrayPositionIterator(a.shape(), axes, axesAreCursor), ap_p(), pOriginalArray_p() { init(a); } // // ArrayIteratorError // template void ArrayIterator::init(const Array &a) { pOriginalArray_p.reference (a); dataPtr_p = pOriginalArray_p.begin_p; if (dimIter() < 1) throw(ArrayIteratorError("ArrayIterator::ArrayIterator - " " at the moment cannot iterate by scalars")); IPosition blc(pOriginalArray_p.ndim(), 0); IPosition trc(pOriginalArray_p.endPosition()); // Calculate what the offset for ap_p->begin is for each step. // The offset is the value that has to be added to dataPtr_p to go // to the next chunk. // The offset calculation must match the way nextStep is iterating. // The iteration is such that shape(i)-1 steps are made for axis i. const IPosition& iAxes = iterAxes(); const IPosition& steps = pOriginalArray_p.steps(); const IPosition& shape = pOriginalArray_p.shape(); offset_p.resize (a.ndim()); offset_p = 0; int lastoff = 0; for (size_t i=0; i 0) trc(axis) = 0; offset_p(axis) = steps(axis) - lastoff; lastoff += (shape(axis)-1)*steps(axis); } // Now diddle with the internal array to ensure that it is the // correct shape. We only want to remove the iteration axes, not the // possible degenerate axes in the cursor). if (dimIter() < pOriginalArray_p.ndim()) { ap_p.reset( new Array(pOriginalArray_p(blc,trc).nonDegenerate(cursorAxes())) ); } else { // Same dimensionality, so no degenerate axes ap_p.reset( new Array(pOriginalArray_p) ); } } // // ArrayIteratorError // template void ArrayIterator::apSetPointer(int stepDim) { if (ap_p == 0) throw(ArrayIteratorError("ArrayIterator::apSetPointer()" " - no iteration array!")); if (pastEnd()) { ap_p->begin_p = 0; // Mark it "invalid" } else { if (stepDim < 0) { dataPtr_p = pOriginalArray_p.begin_p; } else { dataPtr_p += offset_p(stepDim); } ap_p->begin_p = dataPtr_p; ap_p->setEndIter(); } } template void ArrayIterator::reset() { ArrayPositionIterator::reset(); apSetPointer(-1); } template void ArrayIterator::next() { int stepDim = ArrayPositionIterator::nextStep(); apSetPointer(stepDim); } template void ArrayIterator::set (const IPosition& cursorPos) { ArrayPositionIterator::set (cursorPos); if (ap_p == nullptr) throw(ArrayIteratorError("ArrayIterator::apSetPointer()" " - no iteration array!")); if (pastEnd()) { ap_p->begin_p = 0; // Mark it "invalid" } else { dataPtr_p = &(pOriginalArray_p(pos())); ap_p->begin_p = dataPtr_p; ap_p->setEndIter(); } } template ArrayBase& ArrayIterator::getArray() { return array(); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Arrays/ArrayLogical.h000066400000000000000000000435041476623553700202240ustar00rootroot00000000000000//# ArrayLogical.h: Element by element logical operations on arrays. //# Copyright (C) 1993,1994,1995,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_ARRAYLOGICAL_2_H #define CASA_ARRAYLOGICAL_2_H //# Includes #include "ArrayFwd.h" #include "IPosition.h" namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Logical operations for Arrays. // // // // //
          • Array // // // // This file contains global functions which perform element by element logical // operations on arrays. // // // // These functions perform element by element logical operations on // arrays. The two arrays must conform, except for allEQ which returns // false if the arrays do not conform. // // There are two classes of functions. One class returns a LogicalArray. // In these functions, the value of an element of the LogicalArray is // the value of the logical operation applied to the corresponding elements // of the input Arrays. The other class of functions returns a single // bool. The return value is true if the logical operation returns true for // all elements of the input arrays for the "all" functions // (e.g. allLE()), and returns true if the logical operation returns true for // any elements of the input arrays for the "any" functions // (e.g. anyLE()). // // For instance allLE (a, b) implies that every element of a is // less than or equal to every element of b. Note that with this definition // allLE (a, b) and allGE (a, b) can both be false (e.g. a = [1,0] b = [0,1]). // // Comparison between two zero-sized arrays is not defined // (should it throw an exception?). // // // // // // // Vector a(10); // Vector b(10); // LogicalVector l(10); // . . . // l = a < b; // // This example sets the elements of l (a // // // // Vector a(10); // Vector b(10); // bool result; // . . . // result = allLT (a, b); // // This example sets result to true if, for all elements, a // // // One wants to be able to perform logical operations on arrays. // // // // Array logical operations -- Logical operations for Arrays. // // // // Determine if the comparisons between corresponding array elements yield true. // template bool arrayCompareAll (const Array& left, const Array& right, CompareOperator op); template bool arrayCompareAll (const Array& left, T right, CompareOperator op); template bool arrayCompareAll (T left, const Array& right, CompareOperator op); // // Determine if the comparisons between corresponding array elements yield true. // template bool arrayCompareAny (const Array& left, const Array& right, CompareOperator op); template bool arrayCompareAny (const Array& left, T right, CompareOperator op); template bool arrayCompareAny (T left, const Array& right, CompareOperator op); // // // Element by element comparisons between the "l" and "r" arrays. The result // is true only if the comparison is true for every element of the arrays. // // The operator forms of array logical operations which return a single bool // have been replaced by these "all" functions. // The operator forms of array logical operations now return a LogicalArray. // // The arrays must conform except for allEQ, which will return false if the // arrays have different shapes. // // //
          • ArrayConformanceError // // // template bool allLE (const Array &l, const Array &r); template bool allLT (const Array &l, const Array &r); template bool allGE (const Array &l, const Array &r); template bool allGT (const Array &l, const Array &r); template bool allEQ (const Array &l, const Array &r); template bool allNE (const Array &l, const Array &r); template bool allNear (const Array &l, const Array &r, double tol); template bool allNearAbs (const Array &l, const Array &r, double tol); // // This only makes sense if the array element type is logical valued. // template bool allAND (const Array &l, const Array &r); template bool allOR (const Array &l, const Array &r); // // // // // Element by element comparisons between the "l" and "r" arrays. The result // is a LogicalArray. // The arrays must conform or an exception is thrown. // // The Vector, Matrix and Cube version are present to bypass the problems // due to the existence of automatic comparison inline templates in standard // algorithm library, producing a single bool value. // // template LogicalArray operator <= (const Array &l, const Array &r); template LogicalArray operator < (const Array &l, const Array &r); template LogicalArray operator >= (const Array &l, const Array &r); template LogicalArray operator > (const Array &l, const Array &r); template LogicalArray operator == (const Array &l, const Array &r); template LogicalArray operator != (const Array &l, const Array &r); template LogicalArray near(const Array &l, const Array &r, double tol); template LogicalArray nearAbs(const Array &l, const Array &r, double tol); // // This only makes sense if the array element type is logical valued. // template LogicalArray operator && (const Array &l, const Array &r); template LogicalArray operator || (const Array &l, const Array &r); // // // // // Logical negation of an array. This only makes sense if the array // element type is logical valued. template LogicalArray operator ! (const Array &l); // // Element by element comparisons between an array and a scalar, which // behaves as if it were a conformant array filled with the value "val." // The result is true only if the comparison is true for every element // of the array. // template bool allLE (const Array &array, const T &val); template bool allLE (const T &val, const Array &array); template bool allLT (const Array &array, const T &val); template bool allLT (const T &val, const Array &array); template bool allGE (const Array &array, const T &val); template bool allGE (const T &val, const Array &array); template bool allGT (const Array &array, const T &val); template bool allGT (const T &val, const Array &array); template bool allEQ (const Array &array, const T &val); template bool allEQ (const T &val, const Array &array); template bool allNE (const Array &array, const T &val); template bool allNE (const T &val, const Array &array); template bool allNear (const Array &array, const T &val, double tol); template bool allNear (const T &val, const Array &array, double tol); template bool allNearAbs (const Array &array, const T &val, double tol); template bool allNearAbs (const T &val, const Array &array, double tol); // // This only makes sense if the array element type is logical valued. // template bool allAND (const Array &array, const T &val); template bool allAND (const T &val, const Array &array); template bool allOR (const Array &array, const T &val); template bool allOR (const T &val, const Array &array); // // // // Test if all elements in an array are the same. template bool allSame (const Array &a) { return a.size() <= 1 || allEQ(*a.data(), a); } // Element by element test for NaN or (In)finity. // template LogicalArray isNaN (const Array &array); template LogicalArray isInf (const Array &array); template LogicalArray isFinite (const Array &array); // // // Element by element comparisons between an array and a scalar, which // behaves as if it were a conformant array filled with the value "val." // The result is a LogicalArray. // // //
          • ArrayConformanceError // // // template LogicalArray operator <= (const Array &array, const T &val); template LogicalArray operator <= (const T &val, const Array &array); template LogicalArray operator < (const Array &array, const T &val); template LogicalArray operator < (const T &val, const Array &array); template LogicalArray operator >= (const Array &array, const T &val); template LogicalArray operator >= (const T &val, const Array &array); template LogicalArray operator > (const Array &array, const T &val); template LogicalArray operator > (const T &val, const Array &array); template LogicalArray operator == (const Array &array, const T &val); template LogicalArray operator == (const T &val, const Array &array); template LogicalArray operator != (const Array &array, const T &val); template LogicalArray operator != (const T &val, const Array &array); template LogicalArray near (const Array &array, const T &val, double tol); template LogicalArray near (const T &val, const Array &array, double tol); template LogicalArray nearAbs (const Array &array, const T &val, double tol); template LogicalArray nearAbs (const T &val, const Array &array, double tol); // // This only makes sense if the array element type is logical valued. // template LogicalArray operator && (const Array &array, const T &val); template LogicalArray operator && (const T &val, const Array &array); template LogicalArray operator || (const Array &array, const T &val); template LogicalArray operator || (const T &val, const Array &array); // // // //# With two arrays, they must both conform, and the result is done element //# by element. For instance anyLE (a, b) implies that some element of a is //# less than or equal to the corresponding element of b. //# NB comparison between two zero-sized arrays is not defined (should it //# throw an exception?). // // Element by element comparisons between the "l" and "r" arrays. The result // is true if the comparison is true for some element of the arrays. // // //
          • ArrayConformanceError // // // template bool anyLE (const Array &l, const Array &r); template bool anyLT (const Array &l, const Array &r); template bool anyGE (const Array &l, const Array &r); template bool anyGT (const Array &l, const Array &r); template bool anyEQ (const Array &l, const Array &r); template bool anyNE (const Array &l, const Array &r); template bool anyNear (const Array &l, const Array &r, double tol); template bool anyNearAbs (const Array &l, const Array &r, double tol); // // This only makes sense if the array element type is logical valued. // template bool anyAND (const Array &l, const Array &r); template bool anyOR (const Array &l, const Array &r); // // // // // Element by element comparisons between an array and a scalar, which // behaves as if it were a conformant array filled with the value "val." // The result is true if the comparison is true for some element of the array. // At some point operators will be available that return masks where the // comparison is true. // template bool anyLE (const Array &array, const T &val); template bool anyLE (const T &val, const Array &array); template bool anyLT (const Array &array, const T &val); template bool anyLT (const T &val, const Array &array); template bool anyGE (const Array &array, const T &val); template bool anyGE (const T &val, const Array &array); template bool anyGT (const Array &array, const T &val); template bool anyGT (const T &val, const Array &array); template bool anyEQ (const Array &array, const T &val); template bool anyEQ (const T &val, const Array &array); template bool anyNE (const Array &array, const T &val); template bool anyNE (const T &val, const Array &array); template bool anyNear (const Array &array, const T &val, double tol); template bool anyNear (const T &val, const Array &array, double tol); template bool anyNearAbs (const Array &array, const T &val, double tol); template bool anyNearAbs (const T &val, const Array &array, double tol); // // This only makes sense if the array element type is logical valued. // template bool anyAND (const Array &array, const T &val); template bool anyAND (const T &val, const Array &array); template bool anyOR (const Array &array, const T &val); template bool anyOR (const T &val, const Array &array); // // // // Are all elements true? inline bool allTrue (const Array& array) { return allEQ (array, true); } // Is any element true? inline bool anyTrue (const Array& array) { return anyEQ (array, true); } // The same functions as above, but for selected axes. Array partialAllTrue (const Array& array, const IPosition& collapseAxes); Array partialAnyTrue (const Array& array, const IPosition& collapseAxes); // Determine the number of true or false elements. // Note: it is meant for bool arrays, but can also be used for // e.g. int arrays. // // Determine it for the full array. // template size_t nfalse (const Array &array); template size_t ntrue (const Array &array) { return array.nelements() - nfalse(array); } // // The same functions as above, but determine ntrue and nfalse for the // given axes only. The result is an array with a shape formed by the // remaining axes. // For example, for an array with shape [3,4,5], collapsing axis 0 // results in an array with shape [4,5] containing ntrue or nfalse for // each X line. // Summing for axes 0 and 2 results in an array with shape [4] containing // ntrue or nfalse for each XZ plane. // template Array partialNTrue (const Array& array, const IPosition& collapseAxes); template Array partialNFalse (const Array& array, const IPosition& collapseAxes); // // // } //# end of casacore namespace #include "ArrayMathBase.h" namespace casacore { // Logical functor to test if all elements are true template class AllFunc final : public ArrayFunctorBase { public: virtual bool operator() (const Array& arr) const override { return allTrue(arr); } }; // Logical functor to test if any elements are true template class AnyFunc final : public ArrayFunctorBase { public: virtual bool operator() (const Array& arr) const override { return anyTrue(arr); } }; // Logical functor to count the number of true elements template class NTrueFunc final : public ArrayFunctorBase { public: virtual RES operator() (const Array& arr) const override { return ntrue(arr); } }; // Logical functor to count the number of false elements template class NFalseFunc final : public ArrayFunctorBase { public: virtual RES operator() (const Array& arr) const override { return nfalse(arr); } }; } //# NAMESPACE CASACORE - END #include "ArrayLogical.tcc" #endif casacore-3.7.1/casa/Arrays/ArrayLogical.tcc000066400000000000000000000532511476623553700205460ustar00rootroot00000000000000//# ArrayLogical.cc: Element by element logical operations on arrays. //# Copyright (C) 1993,1994,1995,1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_ARRAYLOGICAL_2_TCC #define CASA_ARRAYLOGICAL_2_TCC #include "ArrayLogical.h" #include "ArrayMath.h" #include "ArrayUtil.h" #include "ArrayError.h" #include "ElementFunctions.h" #include #include #include #include #include "ElementFunctions.h" namespace casacore { //# NAMESPACE CASACORE - BEGIN template bool arrayCompareAll (const Array& left, const Array& right, CompareOperator op) { if (! left.conform(right)) return false; if (left.contiguousStorage() && right.contiguousStorage()) { return arrays_internal::compareAll (left.cbegin(), left.cend(), right.cbegin(), op); } else { return arrays_internal::compareAll (left.begin(), left.end(), right.begin(), op); } } template bool arrayCompareAll (const Array& left, T right, CompareOperator op) { if (left.contiguousStorage()) { return arrays_internal::compareAllRight (left.cbegin(), left.cend(), right, op); } else { return arrays_internal::compareAllRight (left.begin(), left.end(), right, op); } } template bool arrayCompareAll (T left, const Array& right, CompareOperator op) { if (right.contiguousStorage()) { return arrays_internal::compareAllLeft (right.cbegin(), right.cend(), left, op); } else { return arrays_internal::compareAllLeft (right.begin(), right.end(), left, op); } } template bool arrayCompareAny (const Array& left, const Array& right, CompareOperator op) { if (! left.conform(right)) return false; if (left.contiguousStorage() && right.contiguousStorage()) { return arrays_internal::compareAny (left.cbegin(), left.cend(), right.cbegin(), op); } else { return arrays_internal::compareAny (left.begin(), left.end(), right.begin(), op); } } template bool arrayCompareAny (const Array& left, T right, CompareOperator op) { if (left.contiguousStorage()) { return arrays_internal::compareAnyRight (left.cbegin(), left.cend(), right, op); } else { return arrays_internal::compareAnyRight (left.begin(), left.end(), right, op); } } template bool arrayCompareAny (T left, const Array& right, CompareOperator op) { if (right.contiguousStorage()) { return arrays_internal::compareAnyLeft (right.cbegin(), right.cend(), left, op); } else { return arrays_internal::compareAnyLeft (right.begin(), right.end(), left, op); } } template bool allEQ (const Array &l, const Array &r) { return arrayCompareAll (l, r, std::equal_to()); } template bool allNE (const Array &l, const Array &r) { return arrayCompareAll (l, r, std::not_equal_to()); } template bool allLT (const Array &l, const Array &r) { return arrayCompareAll (l, r, std::less()); } template bool allLE (const Array &l, const Array &r) { return arrayCompareAll (l, r, std::less_equal()); } template bool allGT (const Array &l, const Array &r) { return arrayCompareAll (l, r, std::greater()); } template bool allGE (const Array &l, const Array &r) { return arrayCompareAll (l, r, std::greater_equal()); } template bool allOR (const Array &l, const Array &r) { return arrayCompareAll (l, r, std::logical_or()); } template bool allAND (const Array &l, const Array &r) { return arrayCompareAll (l, r, std::logical_and()); } template bool allEQ (const Array &l, const T &r) { return arrayCompareAll (l, r, std::equal_to()); } template bool allNE (const Array &l, const T &r) { return arrayCompareAll (l, r, std::not_equal_to()); } template bool allLT (const Array &l, const T &r) { return arrayCompareAll (l, r, std::less()); } template bool allLE (const Array &l, const T &r) { return arrayCompareAll (l, r, std::less_equal()); } template bool allGT (const Array &l, const T &r) { return arrayCompareAll (l, r, std::greater()); } template bool allGE (const Array &l, const T &r) { return arrayCompareAll (l, r, std::greater_equal()); } template bool allOR (const Array &l, const T &r) { return arrayCompareAll (l, r, std::logical_or()); } template bool allAND (const Array &l, const T &r) { return arrayCompareAll (l, r, std::logical_and()); } template bool allEQ (const T &l, const Array &r) { return arrayCompareAll (l, r, std::equal_to()); } template bool allNE (const T &l, const Array &r) { return arrayCompareAll (l, r, std::not_equal_to()); } template bool allLT (const T &l, const Array &r) { return arrayCompareAll (l, r, std::less()); } template bool allLE (const T &l, const Array &r) { return arrayCompareAll (l, r, std::less_equal()); } template bool allGT (const T &l, const Array &r) { return arrayCompareAll (l, r, std::greater()); } template bool allGE (const T &l, const Array &r) { return arrayCompareAll (l, r, std::greater_equal()); } template bool allOR (const T &l, const Array &r) { return arrayCompareAll (l, r, std::logical_or()); } template bool allAND (const T &l, const Array &r) { return arrayCompareAll (l, r, std::logical_and()); } template bool anyEQ (const Array &l, const Array &r) { return arrayCompareAny (l, r, std::equal_to()); } template bool anyNE (const Array &l, const Array &r) { return arrayCompareAny (l, r, std::not_equal_to()); } template bool anyLT (const Array &l, const Array &r) { return arrayCompareAny (l, r, std::less()); } template bool anyLE (const Array &l, const Array &r) { return arrayCompareAny (l, r, std::less_equal()); } template bool anyGT (const Array &l, const Array &r) { return arrayCompareAny (l, r, std::greater()); } template bool anyGE (const Array &l, const Array &r) { return arrayCompareAny (l, r, std::greater_equal()); } template bool anyOR (const Array &l, const Array &r) { return arrayCompareAny (l, r, std::logical_or()); } template bool anyAND (const Array &l, const Array &r) { return arrayCompareAny (l, r, std::logical_and()); } template bool anyEQ (const Array &l, const T &r) { return arrayCompareAny (l, r, std::equal_to()); } template bool anyNE (const Array &l, const T &r) { return arrayCompareAny (l, r, std::not_equal_to()); } template bool anyLT (const Array &l, const T &r) { return arrayCompareAny (l, r, std::less()); } template bool anyLE (const Array &l, const T &r) { return arrayCompareAny (l, r, std::less_equal()); } template bool anyGT (const Array &l, const T &r) { return arrayCompareAny (l, r, std::greater()); } template bool anyGE (const Array &l, const T &r) { return arrayCompareAny (l, r, std::greater_equal()); } template bool anyOR (const Array &l, const T &r) { return arrayCompareAny (l, r, std::logical_or()); } template bool anyAND (const Array &l, const T &r) { return arrayCompareAny (l, r, std::logical_and()); } template bool anyEQ (const T &l, const Array &r) { return arrayCompareAny (l, r, std::equal_to()); } template bool anyNE (const T &l, const Array &r) { return arrayCompareAny (l, r, std::not_equal_to()); } template bool anyLT (const T &l, const Array &r) { return arrayCompareAny (l, r, std::less()); } template bool anyLE (const T &l, const Array &r) { return arrayCompareAny (l, r, std::less_equal()); } template bool anyGT (const T &l, const Array &r) { return arrayCompareAny (l, r, std::greater()); } template bool anyGE (const T &l, const Array &r) { return arrayCompareAny (l, r, std::greater_equal()); } template bool anyOR (const T &l, const Array &r) { return arrayCompareAny (l, r, std::logical_or()); } template bool anyAND (const T &l, const Array &r) { return arrayCompareAny (l, r, std::logical_and()); } template LogicalArray operator== (const Array &l, const Array &r) { checkArrayShapes (l, r, "=="); LogicalArray result(l.shape()); arrayContTransform (l, r, result, std::equal_to()); return result; } template LogicalArray operator!= (const Array &l, const Array &r) { checkArrayShapes (l, r, "!="); LogicalArray result(l.shape()); arrayContTransform (l, r, result, std::not_equal_to()); return result; } template LogicalArray operator< (const Array &l, const Array &r) { checkArrayShapes (l, r, "<"); LogicalArray result(l.shape()); arrayContTransform (l, r, result, std::less()); return result; } template LogicalArray operator<= (const Array &l, const Array &r) { checkArrayShapes (l, r, "<="); LogicalArray result(l.shape()); arrayContTransform (l, r, result, std::less_equal()); return result; } template LogicalArray operator> (const Array &l, const Array &r) { checkArrayShapes (l, r, ">"); LogicalArray result(l.shape()); arrayContTransform (l, r, result, std::greater()); return result; } template LogicalArray operator>= (const Array &l, const Array &r) { checkArrayShapes (l, r, ">="); LogicalArray result(l.shape()); arrayContTransform (l, r, result, std::greater_equal()); return result; } template LogicalArray operator|| (const Array &l, const Array &r) { checkArrayShapes (l, r, "||"); LogicalArray result(l.shape()); arrayContTransform (l, r, result, std::logical_or()); return result; } template LogicalArray operator&& (const Array &l, const Array &r) { checkArrayShapes (l, r, "&&"); LogicalArray result(l.shape()); arrayContTransform (l, r, result, std::logical_and()); return result; } template LogicalArray operator== (const Array &l, const T &r) { LogicalArray result(l.shape()); arrayContTransform (l, r, result, std::equal_to()); return result; } template LogicalArray operator!= (const Array &l, const T &r) { LogicalArray result(l.shape()); arrayContTransform (l, r, result, std::not_equal_to()); return result; } template LogicalArray operator< (const Array &l, const T &r) { LogicalArray result(l.shape()); arrayContTransform (l, r, result, std::less()); return result; } template LogicalArray operator<= (const Array &l, const T &r) { LogicalArray result(l.shape()); arrayContTransform (l, r, result, std::less_equal()); return result; } template LogicalArray operator> (const Array &l, const T &r) { LogicalArray result(l.shape()); arrayContTransform (l, r, result, std::greater()); return result; } template LogicalArray operator>= (const Array &l, const T &r) { LogicalArray result(l.shape()); arrayContTransform (l, r, result, std::greater_equal()); return result; } template LogicalArray operator|| (const Array &l, const T &r) { LogicalArray result(l.shape()); arrayContTransform (l, r, result, std::logical_or()); return result; } template LogicalArray operator&& (const Array &l, const T &r) { LogicalArray result(l.shape()); arrayContTransform (l, r, result, std::logical_and()); return result; } template LogicalArray operator== (const T &l, const Array &r) { LogicalArray result(r.shape()); arrayContTransform (l, r, result, std::equal_to()); return result; } template LogicalArray operator!= (const T &l, const Array &r) { LogicalArray result(r.shape()); arrayContTransform (l, r, result, std::not_equal_to()); return result; } template LogicalArray operator< (const T &l, const Array &r) { LogicalArray result(r.shape()); arrayContTransform (l, r, result, std::less()); return result; } template LogicalArray operator<= (const T &l, const Array &r) { LogicalArray result(r.shape()); arrayContTransform (l, r, result, std::less_equal()); return result; } template LogicalArray operator> (const T &l, const Array &r) { LogicalArray result(r.shape()); arrayContTransform (l, r, result, std::greater()); return result; } template LogicalArray operator>= (const T &l, const Array &r) { LogicalArray result(r.shape()); arrayContTransform (l, r, result, std::greater_equal()); return result; } template LogicalArray operator|| (const T &l, const Array &r) { LogicalArray result(r.shape()); arrayContTransform (l, r, result, std::logical_or()); return result; } template LogicalArray operator&& (const T &l, const Array &r) { LogicalArray result(r.shape()); arrayContTransform (l, r, result, std::logical_and()); return result; } template LogicalArray operator! (const Array &array) { LogicalArray result(array.shape()); arrayContTransform (array, result, std::logical_not()); return result; } template LogicalArray isNaN (const Array &array) { LogicalArray result(array.shape()); using std::isnan; using arrays_internal::isnan; arrayContTransform (array, result, [](T val){ return isnan(val);} ); return result; } template LogicalArray isInf (const Array &array) { LogicalArray result(array.shape()); using std::isinf; using arrays_internal::isinf; arrayContTransform (array, result, [](T val){ return isinf(val);} ); return result; } template LogicalArray isFinite (const Array &array) { LogicalArray result(array.shape()); using std::isfinite; using arrays_internal::isfinite; arrayContTransform (array, result, [](T val){ return isfinite(val);} ); return result; } template LogicalArray near (const Array &l, const Array& r, double tol) { checkArrayShapes (l, r, "near"); LogicalArray result(l.shape()); arrayContTransform (l, r, result, [tol](T left, T right){ return arrays_internal::near(left, right, tol); }); return result; } template LogicalArray nearAbs(const Array &l, const Array &r, double tol) { checkArrayShapes (l, r, "nearAbs"); LogicalArray result(l.shape()); arrayContTransform (l, r, result, [tol](T left, T right){ return arrays_internal::nearAbs(left, right, tol); }); return result; } template LogicalArray near (const Array &array, const T &val, double tol) { LogicalArray result(array.shape()); arrayContTransform (array, val, result, [tol](T left, T right){ return arrays_internal::near(left, right, tol); }); return result; } template LogicalArray near (const T &val, const Array &array, double tol) { LogicalArray result(array.shape()); arrayContTransform (val, array, result, [tol](T left, T right){ return arrays_internal::near(left, right, tol); }); return result; } template LogicalArray nearAbs (const Array &array, const T &val, double tol) { LogicalArray result(array.shape()); arrayContTransform (array, val, result, [tol](T left, T right){ return arrays_internal::nearAbs(left, right, tol); }); return result; } template LogicalArray nearAbs (const T &val, const Array &array, double tol) { LogicalArray result(array.shape()); arrayContTransform (val, array, result, [tol](T left, T right){ return arrays_internal::nearAbs(left, right, tol); }); return result; } template bool allNear (const Array &l, const Array &r, double tol) { return arrayCompareAll (l, r, [tol](T left, T right){ return arrays_internal::near(left, right, tol); }); } template bool allNear (const Array &array, const T &val, double tol) { return arrayCompareAll (array, val, [tol](T left, T right){ return arrays_internal::near(left, right, tol); }); } template bool allNear (const T &val, const Array &array, double tol) { return arrayCompareAll (val, array, [tol](T left, T right){ return arrays_internal::near(left, right, tol); }); } template bool allNearAbs (const Array &l, const Array &r, double tol) { return arrayCompareAll (l, r, [tol](T left, T right){ return arrays_internal::nearAbs(left, right, tol); }); } template bool allNearAbs (const Array &array, const T &val, double tol) { return arrayCompareAll (array, val, [tol](T left, T right){ return arrays_internal::nearAbs(left, right, tol); }); } template bool allNearAbs (const T &val, const Array &array, double tol) { return arrayCompareAll (val, array, [tol](T left, T right){ return arrays_internal::nearAbs(left, right, tol); }); } template bool anyNear (const Array &l, const Array &r, double tol) { return arrayCompareAny (l, r, [tol](T left, T right){ return arrays_internal::near(left, right, tol); }); } template bool anyNear (const Array &array, const T &val, double tol) { return arrayCompareAny (array, val, [tol](T left, T right){ return arrays_internal::near(left, right, tol); }); } template bool anyNear (const T &val, const Array &array, double tol) { return arrayCompareAny (val, array, [tol](T left, T right){ return arrays_internal::near(left, right, tol); }); } template bool anyNearAbs (const Array &l, const Array &r, double tol) { return arrayCompareAny (l, r, [tol](T left, T right){ return arrays_internal::nearAbs(left, right, tol); }); } template bool anyNearAbs (const Array &array, const T &val, double tol) { return arrayCompareAny (array, val, [tol](T left, T right){ return arrays_internal::nearAbs(left, right, tol); }); } template bool anyNearAbs (const T &val, const Array &array, double tol) { return arrayCompareAny (val, array, [tol](T left, T right){ return arrays_internal::nearAbs(left, right, tol); }); } template size_t nfalse (const Array &array) { return (array.contiguousStorage() ? std::count (array.cbegin(), array.cend(), T()) : std::count (array.begin(), array.end(), T())); } template Array partialNTrue (const Array& array, const IPosition& collapseAxes) { const IPosition& shape = array.shape(); size_t ndim = shape.nelements(); if (ndim == 0) { return Array(); } IPosition resShape, incr; int nelemCont = 0; size_t stax = partialFuncHelper (nelemCont, resShape, incr, shape, collapseAxes); Array result (resShape); result = 0; bool deleteData, deleteRes; const T* arrData = array.getStorage (deleteData); const T* data = arrData; size_t* resData = result.getStorage (deleteRes); size_t* res = resData; // Find out how contiguous the data is, i.e. if some contiguous data // end up in the same output element. // const tells if any data are contiguous. // stax gives the first non-contiguous axis. // no gives the number of contiguous elements. bool cont = true; unsigned n0 = nelemCont; int incr0 = incr(0); if (nelemCont <= 1) { cont = false; n0 = shape(0); stax = 1; } // Loop through all data and assemble as needed. IPosition pos(ndim, 0); while (true) { if (cont) { size_t tmp = *res; for (size_t i=0; i Array partialNFalse (const Array& array, const IPosition& collapseAxes) { Array result = partialNTrue (array, collapseAxes); size_t nr = result.nelements(); if (nr > 0) { size_t factor = array.nelements() / nr; bool deleteRes; size_t* res = result.getStorage (deleteRes); for (size_t i=0; i #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Mathematical operations for Arrays. // // // // //
          • Array // // // // This file contains global functions which perform element by element // mathematical operations on arrays. // // // // These functions perform element by element mathematical operations on // arrays. The two arrays must conform. // // Furthermore it defines functions a la std::transform to transform one or // two arrays by means of a unary or binary operator. All math and logical // operations on arrays can be expressed by means of these transform functions. //
            It also defines an in-place transform function because for non-trivial // iterators it works faster than a transform where the result is an iterator // on the same data object as the left operand. //
            The transform functions distinguish between contiguous and non-contiguous // arrays because iterating through a contiguous array can be done in a faster // way. //
            Similar to the standard transform function these functions do not check // if the shapes match. The user is responsible for that. //
            // // // // Vector a(10); // Vector b(10); // Vector c(10); // . . . // c = a + b; // // This example sets the elements of c to (a+b). It checks if a and b have the // same shape. // The result of this operation is an Array. // // // // // c = arrayTransformResult (a, b, std::plus()); // // This example does the same as the previous example, but expressed using // the transform function (which, in fact, is used by the + operator above). // However, it is not checked if the shapes match. // // // // arrayContTransform (a, b, c, std::plus()); // // This example does the same as the previous example, but is faster because // the result array already exists and does not need to be allocated. // Note that the caller must be sure that c is contiguous. // // // // Vector a(10); // Vector b(10); // Vector c(10); // . . . // c = atan2 (a, b); // // This example sets the elements of c to atan2 (a,b). // The result of this operation is an Array. // // // // // Vector a(10); // int result; // . . . // result = sum (a); // // This example sums a. // // // // One wants to be able to perform mathematical operations on arrays. // // // // Array mathematical operations -- Mathematical operations for // Arrays. // // // // The myxtransform functions are defined to avoid a bug in g++-4.3. // That compiler generates incorrect code when only -g is used for // a std::transform with a bind1st or bind2nd for a complex. // So, for example, the multiplication of a std::complex array and std::complex scalar // would fail (see g++ bug 39678). // // sequence = scalar OP sequence template void myltransform(_InputIterator1 __first1, _InputIterator1 __last1, _OutputIterator __result, T left, _BinaryOperation __binary_op) { for ( ; __first1 != __last1; ++__first1, ++__result) *__result = __binary_op(left, *__first1); } // sequence = sequence OP scalar template void myrtransform(_InputIterator1 __first1, _InputIterator1 __last1, _OutputIterator __result, T right, _BinaryOperation __binary_op) { for ( ; __first1 != __last1; ++__first1, ++__result) *__result = __binary_op(*__first1, right); } // sequence OP= scalar template void myiptransform(_InputIterator1 __first1, _InputIterator1 __last1, T right, _BinaryOperation __binary_op) { for ( ; __first1 != __last1; ++__first1) *__first1 = __binary_op(*__first1, right); } // // Functions to apply a binary or unary operator to arrays. // They are modeled after std::transform. // They do not check if the shapes conform; as in std::transform the // user must take care that the operands conform. // // Transform left and right to a result using the binary operator. // Result MUST be a contiguous array. template inline void arrayContTransform (const Array& left, const Array& right, Array& result, BinaryOperator op) { assert (result.contiguousStorage()); if (left.contiguousStorage() && right.contiguousStorage()) { std::transform (left.cbegin(), left.cend(), right.cbegin(), result.cbegin(), op); } else { std::transform (left.begin(), left.end(), right.begin(), result.cbegin(), op); } } // Transform left and right to a result using the binary operator. // Result MUST be a contiguous array. template inline void arrayContTransform (const Array& left, R right, Array& result, BinaryOperator op) { assert (result.contiguousStorage()); if (left.contiguousStorage()) { myrtransform (left.cbegin(), left.cend(), result.cbegin(), right, op); //// std::transform (left.cbegin(), left.cend(), //// result.cbegin(), bind2nd(op, right)); } else { myrtransform (left.begin(), left.end(), result.cbegin(), right, op); //// std::transform (left.begin(), left.end(), //// result.cbegin(), bind2nd(op, right)); } } // Transform left and right to a result using the binary operator. // Result MUST be a contiguous array. template inline void arrayContTransform (L left, const Array& right, Array& result, BinaryOperator op) { assert (result.contiguousStorage()); if (right.contiguousStorage()) { myltransform (right.cbegin(), right.cend(), result.cbegin(), left, op); //// std::transform (right.cbegin(), right.cend(), //// result.cbegin(), bind1st(op, left)); } else { myltransform (right.begin(), right.end(), result.cbegin(), left, op); //// std::transform (right.begin(), right.end(), //// result.cbegin(), bind1st(op, left)); } } // Transform array to a result using the unary operator. // Result MUST be a contiguous array. template inline void arrayContTransform (const Array& arr, Array& result, UnaryOperator op) { assert (result.contiguousStorage()); if (arr.contiguousStorage()) { std::transform (arr.cbegin(), arr.cend(), result.cbegin(), op); } else { std::transform (arr.begin(), arr.end(), result.cbegin(), op); } } // Transform left and right to a result using the binary operator. // Result need not be a contiguous array. template void arrayTransform (const Array& left, const Array& right, Array& result, BinaryOperator op); // Transform left and right to a result using the binary operator. // Result need not be a contiguous array. template void arrayTransform (const Array& left, R right, Array& result, BinaryOperator op); // Transform left and right to a result using the binary operator. // Result need not be a contiguous array. template void arrayTransform (L left, const Array& right, Array& result, BinaryOperator op); // Transform array to a result using the unary operator. // Result need not be a contiguous array. template void arrayTransform (const Array& arr, Array& result, UnaryOperator op); // Transform left and right to a result using the binary operator. // The created and returned result array is contiguous. template Array arrayTransformResult (const Array& left, const Array& right, BinaryOperator op); // Transform left and right to a result using the binary operator. // The created and returned result array is contiguous. template Array arrayTransformResult (const Array& left, T right, BinaryOperator op); // Transform left and right to a result using the binary operator. // The created and returned result array is contiguous. template Array arrayTransformResult (T left, const Array& right, BinaryOperator op); // Transform array to a result using the unary operator. // The created and returned result array is contiguous. template Array arrayTransformResult (const Array& arr, UnaryOperator op); // Transform left and right in place using the binary operator. // The result is stored in the left array (useful for e.g. the += operation). template inline void arrayTransformInPlace (Array& left, const Array& right, BinaryOperator op) { if (left.contiguousStorage() && right.contiguousStorage()) { std::transform(left.cbegin(), left.cend(), right.cbegin(), left.cbegin(), op); } else { std::transform(left.begin(), left.end(), right.begin(), left.begin(), op); } } // Transform left and right in place using the binary operator. // The result is stored in the left array (useful for e.g. the += operation). template inline void arrayTransformInPlace (Array& left, R right, BinaryOperator op) { if (left.contiguousStorage()) { myiptransform (left.cbegin(), left.cend(), right, op); //// transformInPlace (left.cbegin(), left.cend(), bind2nd(op, right)); } else { myiptransform (left.begin(), left.end(), right, op); //// transformInPlace (left.begin(), left.end(), bind2nd(op, right)); } } // Transform the array in place using the unary operator. // E.g. doing arrayTransformInPlace(array, Sin()) is faster than // array=sin(array) as it does not need to create a temporary array. template inline void arrayTransformInPlace (Array& arr, UnaryOperator op) { if (arr.contiguousStorage()) { std::transform(arr.cbegin(), arr.cend(), arr.cbegin(), op); } else { std::transform(arr.begin(), arr.end(), arr.begin(), op); } } // // // Element by element arithmetic modifying left in-place. left and other // must be conformant. // template void operator+= (Array &left, const Array &other); template void operator-= (Array &left, const Array &other); template void operator*= (Array &left, const Array &other) { checkArrayShapes (left, other, "*="); arrayTransformInPlace (left, other, std::multiplies()); } template void operator/= (Array &left, const Array &other) { checkArrayShapes (left, other, "/="); arrayTransformInPlace (left, other, std::divides()); } template void operator%= (Array &left, const Array &other); template void operator&= (Array &left, const Array &other); template void operator|= (Array &left, const Array &other); template void operator^= (Array &left, const Array &other); // // // Element by element arithmetic modifying left in-place. The scalar "other" // behaves as if it were a conformant Array to left filled with constant values. // template void operator+= (Array &left, const T &other); template void operator-= (Array &left, const T &other); template void operator*= (Array &left, const T &other) { arrayTransformInPlace (left, other, std::multiplies()); } template void operator/= (Array &left, const T &other) { arrayTransformInPlace (left, other, std::divides()); } template void operator%= (Array &left, const T &other); template void operator&= (Array &left, const T &other); template void operator|= (Array &left, const T &other); template void operator^= (Array &left, const T &other); // // Unary arithmetic operation. // // template Array operator+(const Array &a); template Array operator-(const Array &a); template Array operator~(const Array &a); // // // Element by element arithmetic on two arrays, returning an array. // template Array operator+ (const Array &left, const Array &right); template Array operator- (const Array &left, const Array &right); template Array operator*(const Array &left, const Array &right) { checkArrayShapes (left, right, "*"); return arrayTransformResult (left, right, std::multiplies()); } template Array operator/ (const Array &left, const Array &right); template Array operator% (const Array &left, const Array &right); template Array operator| (const Array &left, const Array &right); template Array operator& (const Array &left, const Array &right); template Array operator^ (const Array &left, const Array &right); // // // Element by element arithmetic between an array and a scalar, returning // an array. // template Array operator+ (const Array &left, const T &right); template Array operator- (const Array &left, const T &right); template Array operator* (const Array &left, const T &right) { return arrayTransformResult (left, right, std::multiplies()); } template Array operator/ (const Array &left, const T &right); template Array operator% (const Array &left, const T &right); template Array operator| (const Array &left, const T &right); template Array operator& (const Array &left, const T &right); template Array operator^ (const Array &left, const T &right); // // // Element by element arithmetic between a scalar and an array, returning // an array. // template Array operator+ (const T &left, const Array &right); template Array operator- (const T &left, const Array &right); template Array operator* (const T &left, const Array &right) { return arrayTransformResult (left, right, std::multiplies()); } template Array operator/ (const T &left, const Array &right); template Array operator% (const T &left, const Array &right); template Array operator| (const T &left, const Array &right); template Array operator& (const T &left, const Array &right); template Array operator^ (const T &left, const Array &right); // // // Transcendental function that can be applied to essentially all numeric // types. Works on an element-by-element basis. // template Array cos(const Array &a); template Array cosh(const Array &a); template Array exp(const Array &a); template Array log(const Array &a); template Array log10(const Array &a); template Array pow(const Array &a, const Array &b); template Array pow(const T &a, const Array &b); template Array sin(const Array &a); template Array sinh(const Array &a); template Array sqrt(const Array &a); // // // Transcendental function applied to the array on an element-by-element // basis. Although a template function, this does not make sense for all // numeric types. // template Array acos(const Array &a); template Array asin(const Array &a); template Array atan(const Array &a); template Array atan2(const Array &y, const Array &x); template Array atan2(const T &y, const Array &x); template Array atan2(const Array &y, const T &x); template Array ceil(const Array &a); template Array fabs(const Array &a); template Array abs(const Array &a); template Array floor(const Array &a); template Array round(const Array &a); template Array sign(const Array &a); template Array fmod(const Array &a, const Array &b); template Array fmod(const T &a, const Array &b); template Array fmod(const Array &a, const T &b); template Array floormod(const Array &a, const Array &b); template Array floormod(const T &a, const Array &b); template Array floormod(const Array &a, const T &b); template Array pow(const Array &a, const T &b); template Array> pow(const Array> &a, const T &b); template Array tan(const Array &a); template Array tanh(const Array &a); // N.B. fabs is deprecated. Use abs. template Array fabs(const Array &a); // // // // Find the minimum and maximum values of an array, including their locations. template void minMax(ScalarType &minVal, ScalarType &maxVal, IPosition &minPos, IPosition &maxPos, const Array &array); // The array is searched at locations where the mask equals valid. // (at least one such position must exist or an exception will be thrown). // MaskType should be an Array of bool. template void minMax(ScalarType &minVal, ScalarType &maxVal, IPosition &minPos, IPosition &maxPos, const Array &array, const Array &mask, bool valid=true); // The array * weight is searched template void minMaxMasked(ScalarType &minVal, ScalarType &maxVal, IPosition &minPos, IPosition &maxPos, const Array &array, const Array &weight); // // // The "min" and "max" functions require that the type "T" have comparison // operators. // // // This sets min and max to the minimum and maximum of the array to // avoid having to do two passes with max() and min() separately. template void minMax(T &min, T &max, const Array &a); // // The minimum element of the array. // Requires that the type "T" has comparison operators. template T min(const Array &a); // The maximum element of the array. // Requires that the type "T" has comparison operators. template T max(const Array &a); // "result" contains the maximum of "a" and "b" at each position. "result", // "a", and "b" must be conformant. template void max(Array &result, const Array &a, const Array &b); // "result" contains the minimum of "a" and "b" at each position. "result", // "a", and "b" must be conformant. template void min(Array &result, const Array &a, const Array &b); // Return an array that contains the maximum of "a" and "b" at each position. // "a" and "b" must be conformant. template Array max(const Array &a, const Array &b); template Array max(const T &a, const Array &b); // Return an array that contains the minimum of "a" and "b" at each position. // "a" and "b" must be conformant. template Array min(const Array &a, const Array &b); // "result" contains the maximum of "a" and "b" at each position. "result", // and "a" must be conformant. template void max(Array &result, const Array &a, const T &b); template inline void max(Array &result, const T &a, const Array &b) { max (result, b, a); } // "result" contains the minimum of "a" and "b" at each position. "result", // and "a" must be conformant. template void min(Array &result, const Array &a, const T &b); template inline void min(Array &result, const T &a, const Array &b) { min (result, b, a); } // Return an array that contains the maximum of "a" and "b" at each position. template Array max(const Array &a, const T &b); template inline Array max(const T &a, const Array &b) { return max(b, a); } // Return an array that contains the minimum of "a" and "b" at each position. template Array min(const Array &a, const T &b); template inline Array min(const T &a, const Array &b) { return min(b, a); } // // // Fills all elements of "array" with a sequence starting with "start" // and incrementing by "inc" for each element. The first axis varies // most rapidly. template void indgen(Array &a, T start, T inc); // // Fills all elements of "array" with a sequence starting with 0 // and ending with nelements() - 1. The first axis varies // most rapidly. template inline void indgen(Array &a) { indgen(a, T(0), T(1)); } // // Fills all elements of "array" with a sequence starting with start // incremented by one for each position in the array. The first axis varies // most rapidly. template inline void indgen(Array &a, T start) { indgen(a, start, T(1)); } // Create a Vector of the given length and fill it with the start value // incremented with inc for each element. template inline Vector indgen(size_t length, T start, T inc) { Vector x(length); indgen(x, start, inc); return x; } // Sum of every element of the array. template T sum(const Array &a); // // Sum the square of every element of the array. template T sumsqr(const Array &a); // // Product of every element of the array. This could of course easily // overflow. template T product(const Array &a); // // The mean of "a" is the sum of all elements of "a" divided by the number // of elements of "a". template T mean(const Array &a); // The variance of "a" is the sum of (a(i) - mean(a))**2/(a.nelements() - ddof). // Similar to numpy the argument ddof (delta degrees of freedom) tells if the // population variance (ddof=0) or the sample variance (ddof=1) is taken. // The variance functions proper use ddof=1. //
            Note that for a complex valued T the absolute values are used; in that way // the variance is equal to the sum of the variances of the real and imaginary parts. // Hence the imaginary part in the return value is 0. template T variance(const Array &a); template T pvariance(const Array &a, size_t ddof=0); // Rather than using a computed mean, use the supplied value. template T variance(const Array &a, T mean); template T pvariance(const Array &a, T mean, size_t ddof=0); // The standard deviation of "a" is the square root of its variance. template T stddev(const Array &a); template T pstddev(const Array &a, size_t ddof=0); template T stddev(const Array &a, T mean); template T pstddev(const Array &a, T mean, size_t ddof=0); // // The average deviation of "a" is the sum of abs(a(i) - mean(a))/N. (N.B. // N, not N-1 in the denominator). template T avdev(const Array &a); // // The average deviation of "a" is the sum of abs(a(i) - mean(a))/N. (N.B. // N, not N-1 in the denominator). // Rather than using a computed mean, use the supplied value. template T avdev(const Array &a,T mean); // // The root-mean-square of "a" is the sqrt of sum(a*a)/N. template T rms(const Array &a); // The median of "a" is a(n/2). // If a has an even number of elements and the switch takeEvenMean is set, // the median is 0.5*(a(n/2) + a((n+1)/2)). // According to Numerical Recipes (2nd edition) it makes little sense to take // the mean if the array is large enough (> 100 elements). Therefore // the default for takeEvenMean is false if the array has > 100 elements, // otherwise it is true. //
            If "sorted"==true we assume the data is already sorted and we // compute the median directly. Otherwise the function GenSort::kthLargest // is used to find the median (kthLargest is about 6 times faster // than a full quicksort). //
            Finding the median means that the array has to be (partially) // sorted. By default a copy will be made, but if "inPlace" is in effect, // the data themselves will be sorted. That should only be used if the // data are used not thereafter. // The function kthLargest in class GenSortIndirect can be used to // obtain the index of the median in an array. // // TODO shouldn't take a const Array for in place sorting template T median(const Array &a, std::vector &scratch, bool sorted, bool takeEvenMean, bool inPlace=false); // TODO shouldn't take a const Array for in place sorting template T median(const Array &a, bool sorted, bool takeEvenMean, bool inPlace=false) { std::vector scratch; return median (a, scratch, sorted, takeEvenMean, inPlace); } template inline T median(const Array &a, bool sorted) { return median (a, sorted, (a.nelements() <= 100), false); } template inline T median(const Array &a) { return median (a, false, (a.nelements() <= 100), false); } // TODO shouldn't take a const Array for in place sorting template inline T medianInPlace(const Array &a, bool sorted=false) { return median (a, sorted, (a.nelements() <= 100), true); } // // The median absolute deviation from the median. Interface is as for // the median functions // // TODO shouldn't take a const Array for in place sorting template T madfm(const Array &a, std::vector &tmp, bool sorted, bool takeEvenMean, bool inPlace = false); // TODO shouldn't take a const Array for in place sorting template T madfm(const Array &a, bool sorted, bool takeEvenMean, bool inPlace=false) { std::vector tmp; return madfm(a, tmp, sorted, takeEvenMean, inPlace); } template inline T madfm(const Array &a, bool sorted) { return madfm(a, sorted, (a.nelements() <= 100), false); } template inline T madfm(const Array &a) { return madfm(a, false, (a.nelements() <= 100), false); } // TODO shouldn't take a const Array for in place sorting template inline T madfmInPlace(const Array &a, bool sorted=false) { return madfm(a, sorted, (a.nelements() <= 100), true); } // // Return the fractile of an array. // It returns the value at the given fraction of the array. // A fraction of 0.5 is the same as the median, be it that no mean of // the two middle elements is taken if the array has an even nr of elements. // It uses kthLargest if the array is not sorted yet. // The function kthLargest in class GenSortIndirect can be used to // obtain the index of the fractile in an array. // TODO shouldn't take a const Array for in place sorting template T fractile(const Array &a, std::vector &tmp, float fraction, bool sorted=false, bool inPlace=false); // TODO shouldn't take a const Array for in place sorting template T fractile(const Array &a, float fraction, bool sorted=false, bool inPlace=false) { std::vector tmp; return fractile (a, tmp, fraction, sorted, inPlace); } // Return the inter-fractile range of an array. // This is the full range between the bottom and the top fraction. // // TODO shouldn't take a const Array for in place sorting template T interFractileRange(const Array &a, std::vector &tmp, float fraction, bool sorted=false, bool inPlace=false); // TODO shouldn't take a const Array for in place sorting template T interFractileRange(const Array &a, float fraction, bool sorted=false, bool inPlace=false) { std::vector tmp; return interFractileRange(a, tmp, fraction, sorted, inPlace); } // // Return the inter-hexile range of an array. // This is the full range between the bottom sixth and the top sixth // of ordered array values. "The semi-interhexile range is very nearly // equal to the rms for a Gaussian distribution, but it is much less // sensitive to the tails of extended distributions." (Condon et al // 1998) // // TODO shouldn't take a const Array for in place sorting template T interHexileRange(const Array &a, std::vector &tmp, bool sorted=false, bool inPlace=false) { return interFractileRange(a, tmp, 1./6., sorted, inPlace); } // TODO shouldn't take a const Array for in place sorting template T interHexileRange(const Array &a, bool sorted=false, bool inPlace=false) { return interFractileRange(a, 1./6., sorted, inPlace); } // // Return the inter-quartile range of an array. // This is the full range between the bottom quarter and the top // quarter of ordered array values. // // TODO shouldn't take a const Array for in place sorting template T interQuartileRange(const Array &a, std::vector &tmp, bool sorted=false, bool inPlace=false) { return interFractileRange(a, tmp, 0.25, sorted, inPlace); } // TODO shouldn't take a const Array for in place sorting template T interQuartileRange(const Array &a, bool sorted=false, bool inPlace=false) { return interFractileRange(a, 0.25, sorted, inPlace); } // // Methods for element-by-element scaling of complex and real. // Note that std::complex and std::complex are typedefs for std::complex. // template void operator*= (Array> &left, const Array &other) { checkArrayShapes (left, other, "*="); arrayTransformInPlace (left, other, [](std::complex left, T right) { return left*right; }); } template void operator*= (Array> &left, const T &other) { arrayTransformInPlace (left, other, [](std::complex left, T right) { return left*right; }); } template void operator/= (Array> &left, const Array &other) { checkArrayShapes (left, other, "/="); arrayTransformInPlace (left, other, [](std::complex left, T right) { return left/right; }); } template void operator/= (Array> &left, const T &other) { arrayTransformInPlace (left, other, [](std::complex left, T right) { return left/right; }); } template Array> operator* (const Array> &left, const Array &right) { checkArrayShapes (left, right, "*"); Array> result(left.shape()); arrayContTransform (left, right, result, [](std::complex left, T right) { return left*right; }); return result; } template Array > operator* (const Array> &left, const T &other) { Array > result(left.shape()); arrayContTransform (left, other, result, [](std::complex left, T right) { return left*right; }); return result; } template Array > operator*(const std::complex &left, const Array &other) { Array > result(other.shape()); arrayContTransform (left, other, result, [](std::complex left, T right) { return left*right; }); return result; } template Array> operator/ (const Array> &left, const Array &right) { checkArrayShapes (left, right, "/"); Array> result(left.shape()); arrayContTransform (left, right, result, [](std::complex l, T r) { return l/r; }); return result; } template Array> operator/ (const Array> &left, const T &other) { Array> result(left.shape()); arrayContTransform (left, other, result, [](std::complex left, T right) { return left/right; }); return result; } template Array> operator/(const std::complex &left, const Array &other) { Array> result(other.shape()); arrayContTransform (left, other, result, [](std::complex left, T right) { return left/right; }); return result; } // // Returns the complex conjugate of a complex array. // Array> conj(const Array> &carray); Array> conj(const Array> &carray); // Modifies rarray in place. rarray must be conformant. void conj(Array> &rarray, const Array> &carray); void conj(Array> &rarray, const Array> &carray); //# The following are implemented to make the compiler find the right conversion //# more often. Matrix> conj(const Matrix> &carray); Matrix> conj(const Matrix> &carray); // // Form an array of complex numbers from the given real arrays. // Note that std::complex and std::complex are simply typedefs for std::complex // and std::complex, so the result is in fact one of these types. // template Array > makeComplex(const Array &real, const Array& imag); template Array > makeComplex(const T &real, const Array& imag); template Array > makeComplex(const Array &real, const T& imag); // // Set the real part of the left complex array to the right real array. template void setReal(Array &carray, const Array &rarray); // Set the imaginary part of the left complex array to right real array. template void setImag(Array &carray, const Array &rarray); // Extracts the real part of a complex array into an array of floats. // Array real(const Array> &carray); Array real(const Array> &carray); // Modifies rarray in place. rarray must be conformant. void real(Array &rarray, const Array> &carray); void real(Array &rarray, const Array> &carray); // // // Extracts the imaginary part of a complex array into an array of floats. // Array imag(const Array> &carray); Array imag(const Array> &carray); // Modifies rarray in place. rarray must be conformant. void imag(Array &rarray, const Array> &carray); void imag(Array &rarray, const Array> &carray); // // // Extracts the amplitude (i.e. sqrt(re*re + im*im)) from an array // of complex numbers. N.B. this is presently called "fabs" for a single // complex number. // Array amplitude(const Array> &carray); Array amplitude(const Array> &carray); // Modifies rarray in place. rarray must be conformant. void amplitude(Array &rarray, const Array> &carray); void amplitude(Array &rarray, const Array> &carray); // // // Extracts the phase (i.e. atan2(im, re)) from an array // of complex numbers. N.B. this is presently called "arg" // for a single complex number. // Array phase(const Array> &carray); Array phase(const Array> &carray); // Modifies rarray in place. rarray must be conformant. void phase(Array &rarray, const Array> &carray); void phase(Array &rarray, const Array> &carray); // // Copy an array of complex into an array of real,imaginary pairs. The // first axis of the real array becomes twice as long as the complex array. // In the future versions which work by reference will be available; presently // a copy is made. Array ComplexToReal(const Array> &carray); Array ComplexToReal(const Array> &carray); // Modify the array "rarray" in place. "rarray" must be the correct shape. // void ComplexToReal(Array &rarray, const Array> &carray); void ComplexToReal(Array &rarray, const Array> &carray); // // Copy an array of real,imaginary pairs into a complex array. The first axis // must have an even length. // In the future versions which work by reference will be available; presently // a copy is made. Array> RealToComplex(const Array &rarray); Array> RealToComplex(const Array &rarray); // Modify the array "carray" in place. "carray" must be the correct shape. // void RealToComplex(Array> &carray, const Array &rarray); void RealToComplex(Array> &carray, const Array &rarray); // // Make a copy of an array of a different type; for example make an array // of doubles from an array of floats. Arrays to and from must be conformant // (same shape). Also, it must be possible to convert a scalar of type U // to type T. template void convertArray(Array &to, const Array &from); // Returns an array where every element is squared. template Array square(const Array &val); // Returns an array where every element is cubed. template Array cube(const Array &val); // Helper function for expandArray using recursion for each axis. template T* expandRecursive (int axis, const IPosition& shp, const IPosition& mult, const IPosition& inSteps, const T* in, T* out, const IPosition& alternate) { if (axis == 0) { if (alternate[0]) { // Copy as 1,2,3 1,2,3, etc. for (ssize_t j=0; jmult is set to output/input. //
            The alternate argument determines how the values are expanded. // If a row contains values '1 2 3', they can be expanded "linearly" // as '1 1 2 2 3 3' or alternately as '1 2 3 1 2 3' // This choice can be made for each axis; a value 0 means linearly, // another value means alternately. If the length of alternate is less than // the dimensionality of the output array, the missing ones default to 0. template void expandArray (Array& out, const Array& in, const IPosition& alternate=IPosition()) { IPosition mult, inshp, outshp; IPosition alt = checkExpandArray (mult, inshp, in.shape(), out.shape(), alternate); Array incp(in); if (in.ndim() < inshp.size()) { incp.reference (in.reform(inshp)); } // Make sure output is contiguous. bool deleteIt; T* outPtr = out.getStorage (deleteIt); expandRecursive (out.ndim()-1, inshp, mult, incp.steps(), incp.data(), outPtr, alt); out.putStorage (outPtr, deleteIt); } // Check array shapes for expandArray. It returns the alternate argument, // where possibly missing values are appended (as 0). // It fills in mult and inshp (with possibly missing axes of length 1). //
            inShape defines the shape of the input array. //
            outShape defines the shape of the output array. //
            alternate tells per axis if value expansion uses alternation. //
            newInShape is the input shape with new axes (of length 1) added as needed //
            mult is the multiplication (expansion) factor per output axis // Returned is the alternation per output axis; new axes have value 0 (linear expansion) IPosition checkExpandArray (IPosition& mult, IPosition& newInShape, const IPosition& inShape, const IPosition& outShape, const IPosition& alternate); //
            } //# NAMESPACE CASACORE - END #include "ArrayMath.tcc" #endif casacore-3.7.1/casa/Arrays/ArrayMath.tcc000066400000000000000000001063441476623553700200670ustar00rootroot00000000000000//# ArrayMath.cc: Arithmetic functions defined on Arrays //# Copyright (C) 1993,1994,1995,1996,1997,1998,1999,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_ARRAYMATH_2_TCC #define CASA_ARRAYMATH_2_TCC #include "ArrayMath.h" #include "Array.h" #include "ArrayIter.h" #include "ArrayUtil.h" #include "VectorIter.h" #include "ArrayError.h" #include "ElementFunctions.h" #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template void arrayTransform (const Array& left, const Array& right, Array& result, BinaryOperator op) { if (result.contiguousStorage()) { arrayContTransform (left, right, result, op); } else { if (left.contiguousStorage() && right.contiguousStorage()) { std::transform (left.cbegin(), left.cend(), right.cbegin(), result.begin(), op); } else { std::transform (left.begin(), left.end(), right.begin(), result.begin(), op); } } } template void arrayTransform (const Array& left, R right, Array& result, BinaryOperator op) { if (result.contiguousStorage()) { arrayContTransform (left, right, result, op); } else { if (left.contiguousStorage()) { myrtransform (left.cbegin(), left.cend(), result.begin(), right, op); } else { myrtransform (left.begin(), left.end(), result.begin(), right, op); } } } template void arrayTransform (L left, const Array& right, Array& result, BinaryOperator op) { if (result.contiguousStorage()) { arrayContTransform (left, right, result, op); } else { if (right.contiguousStorage()) { myltransform (right.cbegin(), right.cend(), result.begin(), left, op); } else { myltransform (right.begin(), right.end(), result.begin(), left, op); } } } template void arrayTransform (const Array& arr, Array& result, UnaryOperator op) { if (result.contiguousStorage()) { arrayContTransform (arr, result, op); } else { if (arr.contiguousStorage()) { std::transform (arr.cbegin(), arr.cend(), result.begin(), op); } else { std::transform (arr.begin(), arr.end(), result.begin(), op); } } } template Array arrayTransformResult (const Array& left, const Array& right, BinaryOperator op) { Array res(left.shape()); arrayContTransform (left, right, res, op); return res; } template Array arrayTransformResult (const Array& left, T right, BinaryOperator op) { Array res(left.shape()); arrayContTransform (left, right, res, op); return res; } template Array arrayTransformResult (T left, const Array& right, BinaryOperator op) { Array res(right.shape()); arrayContTransform (left, right, res, op); return res; } template Array arrayTransformResult (const Array& arr, UnaryOperator op) { Array res(arr.shape()); arrayContTransform (arr, res, op); return res; } // // ArrayError // template void minMax(T &minVal, T &maxVal, IPosition &minPos, IPosition &maxPos, const Array &array) { size_t n = array.nelements(); if (n == 0) { throw(ArrayError("void minMax(T &min, T &max, IPosition &minPos," "IPosition &maxPos, const Array &array) - " "Array has no elements")); } size_t minp = 0; size_t maxp = 0; T minv = array.data()[0]; T maxv = minv; if (array.contiguousStorage()) { typename Array::const_contiter iter = array.cbegin(); for (size_t i=0; i maxv) { maxv = *iter; maxp = i; } } } else { typename Array::const_iterator iter = array.begin(); for (size_t i=0; i maxv) { maxv = *iter; maxp = i; } } } minPos.resize (array.ndim()); maxPos.resize (array.ndim()); minPos = toIPositionInArray (minp, array.shape()); maxPos = toIPositionInArray (maxp, array.shape()); minVal = minv; maxVal = maxv; } // // ArrayError // template void minMaxMasked(T &minVal, T &maxVal, IPosition &minPos, IPosition &maxPos, const Array &array, const Array &weight) { size_t n = array.nelements(); if (n == 0) { throw(ArrayError("void minMax(T &min, T &max, IPosition &minPos," "IPosition &maxPos, const Array &array) - " "const Array &weight) - " "Array has no elements")); } if (! array.shape().isEqual (weight.shape())) { throw(ArrayConformanceError("void minMaxMasked(T &min, T &max," "IPosition &minPos, IPosition &maxPos, " "const Array &array, " "const Array &weight) - array " "and weight do not have the same shape()")); } size_t minp = 0; size_t maxp = 0; T minv = array.data()[0] * weight.data()[0]; T maxv = minv; if (array.contiguousStorage() && weight.contiguousStorage()) { typename Array::const_contiter iter = array.cbegin(); typename Array::const_contiter witer = weight.cbegin(); for (size_t i=0; i maxv) { maxv = tmp; maxp = i; } } } else { typename Array::const_iterator iter = array.begin(); typename Array::const_iterator witer = weight.begin(); for (size_t i=0; i maxv) { maxv = tmp; maxp = i; } } } minPos.resize (array.ndim()); maxPos.resize (array.ndim()); minPos = toIPositionInArray (minp, array.shape()); maxPos = toIPositionInArray (maxp, array.shape()); minVal = minv; maxVal = maxv; } // // ArrayError // AipsError // template void minMax(T &minVal, T &maxVal, IPosition &minPos, IPosition &maxPos, const Array &array, const Array &mask, bool valid) { size_t n = array.nelements(); if (n == 0) { throw(ArrayError("void minMax(T &min, T &max, IPosition &minPos," "IPosition &maxPos, const Array &array, " "const Array &mask) - " "Array has no elements")); } if (! array.shape().isEqual (mask.shape())) { throw(ArrayConformanceError("void minMax(T &min, T &max," "IPosition &minPos, IPosition &maxPos," "const Array &array, " "const Array &mask) - " "array and mask do not have the same shape()")); } size_t minp; size_t maxp; T minv = T(); T maxv = T(); if (array.contiguousStorage() && mask.contiguousStorage()) { typename Array::const_contiter iter = array.cbegin(); typename Array::const_contiter miter = mask.cbegin(); size_t i=0; for (; i maxv) { maxv = *iter; maxp = i; } } } } else { typename Array::const_iterator iter = array.begin(); typename Array::const_iterator miter = mask.begin(); size_t i=0; for (; i maxv) { maxv = *iter; maxp = i; } } } } if (minp ==n) { throw(std::runtime_error("void minMax(T &min, T &max," "IPosition &minPos, IPosition &maxPos," "const Array &array, " "const Array &mask) - no valid array elements")); } minPos.resize (array.ndim()); maxPos.resize (array.ndim()); minPos = toIPositionInArray (minp, array.shape()); maxPos = toIPositionInArray (maxp, array.shape()); minVal = minv; maxVal = maxv; } // // ArrayConformanceError // template void operator+= (Array &left, const Array &other) { checkArrayShapes (left, other, "+="); arrayTransformInPlace (left, other, std::plus()); } template T min(const Array &a) { T Min, Max; minMax(Min, Max, a); return Min; } template T max(const Array &a) { T Min, Max; minMax(Min, Max, a); return Max; } template void operator+= (Array &left, const T &other) { arrayTransformInPlace (left, other, std::plus()); } // // ArrayConformanceError // template void operator-= (Array &left, const Array &other) { checkArrayShapes (left, other, "-="); arrayTransformInPlace (left, other, std::minus()); } template void operator-= (Array &left, const T &other) { arrayTransformInPlace (left, other, std::minus()); } // // ArrayConformanceError // template void operator%= (Array &left, const Array &other) { checkArrayShapes (left, other, "%="); arrayTransformInPlace (left, other, std::modulus()); } template void operator%= (Array &left, const T &other) { arrayTransformInPlace (left, other, std::modulus()); } // // ArrayConformanceError // template void operator&= (Array &left, const Array &other) { checkArrayShapes (left, other, "&="); arrayTransformInPlace (left, other, [](T a, T b){ return a&b; }); } template void operator&= (Array &left, const T &other) { arrayTransformInPlace (left, other, [](T a, T b){ return a&b; }); } // // ArrayConformanceError // template void operator|= (Array &left, const Array &other) { checkArrayShapes (left, other, "|="); arrayTransformInPlace (left, other, [](T a, T b){ return a|b; }); } template void operator|= (Array &left, const T &other) { arrayTransformInPlace (left, other, [](T a, T b){ return a|b; }); } // // ArrayConformanceError // template void operator^= (Array &left, const Array &other) { checkArrayShapes (left, other, "^="); arrayTransformInPlace (left, other, [](T a, T b){ return a^b; }); } template void operator^= (Array &left, const T &other) { arrayTransformInPlace (left, other, [](T a, T b){ return a^b; }); } template Array operator+(const Array &a) { return a.copy(); } template Array operator-(const Array &a) { return arrayTransformResult (a, std::negate()); } template Array operator~(const Array &a) { return arrayTransformResult (a, [](T val) { return ~val; }); // bit_not would be nicer, but is C++14 } // // ArrayConformanceError // template Array operator+(const Array &left, const Array &right) { checkArrayShapes (left, right, "+"); return arrayTransformResult (left, right, std::plus()); } // // ArrayConformanceError // template Array operator-(const Array &left, const Array &right) { checkArrayShapes (left, right, "-"); return arrayTransformResult (left, right, std::minus()); } // // ArrayConformanceError // template Array operator/(const Array &left, const Array &right) { checkArrayShapes (left, right, "/"); return arrayTransformResult (left, right, std::divides()); } template Array operator%(const Array &left, const Array &right) { checkArrayShapes (left, right, "%"); return arrayTransformResult (left, right, std::modulus()); } template Array operator&(const Array &left, const Array &right) { checkArrayShapes (left, right, "%"); return arrayTransformResult (left, right, [](T a, T b){ return a&b; }); } template Array operator|(const Array &left, const Array &right) { checkArrayShapes (left, right, "%"); return arrayTransformResult (left, right, [](T a, T b){ return a|b; }); } template Array operator^(const Array &left, const Array &right) { checkArrayShapes (left, right, "%"); return arrayTransformResult (left, right, [](T a, T b){ return a^b; }); } template Array operator+ (const Array &left, const T &right) { return arrayTransformResult (left, right, std::plus()); } template Array operator- (const Array &left, const T &right) { return arrayTransformResult (left, right, std::minus()); } template Array operator/ (const Array &left, const T &right) { return arrayTransformResult (left, right, std::divides()); } template Array operator% (const Array &left, const T &right) { return arrayTransformResult (left, right, std::modulus()); } template Array operator& (const Array &left, const T &right) { return arrayTransformResult (left, right, [](T a, T b){ return a&b; }); } template Array operator| (const Array &left, const T &right) { return arrayTransformResult (left, right, [](T a, T b){ return a|b; }); } template Array operator^ (const Array &left, const T &right) { return arrayTransformResult (left, right, [](T a, T b){ return a^b; }); } template Array operator+ (const T &left, const Array &right) { return arrayTransformResult (left, right, std::plus()); } template Array operator- (const T &left, const Array &right) { return arrayTransformResult (left, right, std::minus()); } template Array operator/ (const T &left, const Array &right) { return arrayTransformResult (left, right, std::divides()); } template Array operator% (const T &left, const Array &right) { return arrayTransformResult (left, right, std::modulus()); } template Array operator& (const T &left, const Array &right) { return arrayTransformResult (left, right, [](T a, T b){ return a&b; }); } template Array operator| (const T &left, const Array &right) { return arrayTransformResult (left, right, [](T a, T b){ return a|b; }); } template Array operator^ (const T &left, const Array &right) { return arrayTransformResult (left, right, [](T a, T b){ return a^b; }); } // // ArrayError // template void minMax(T &minVal, T &maxVal, const Array &array) { if (array.nelements() == 0) { throw(ArrayError("void minMax(T &min, T &max, const Array &array) - " "Array has no elements")); } if (array.contiguousStorage()) { // minimal scope as some compilers may spill onto stack otherwise T minv = array.data()[0]; T maxv = minv; typename Array::const_contiter iterEnd = array.cend(); for (typename Array::const_contiter iter = array.cbegin(); iter!=iterEnd; ++iter) { if (*iter < minv) { minv = *iter; } // no else allows compiler to use branchless instructions if (*iter > maxv) { maxv = *iter; } } maxVal = maxv; minVal = minv; } else { T minv = array.data()[0]; T maxv = minv; typename Array::const_iterator iterEnd = array.end(); for (typename Array::const_iterator iter = array.begin(); iter!=iterEnd; ++iter) { if (*iter < minv) { minv = *iter; } if (*iter > maxv) { maxv = *iter; } } maxVal = maxv; minVal = minv; } } // // ArrayConformanceError // template void max(Array &result, const Array &a, const Array &b) { checkArrayShapes (a, b, "max"); checkArrayShapes (a, result, "max"); arrayTransform (a, b, result, [](T l, T r){ return std::max(l, r); }); } // // ArrayConformanceError // template void min(Array &result, const Array &a, const Array &b) { checkArrayShapes (a, b, "min"); checkArrayShapes (a, result, "min"); arrayTransform (a, b, result, [](T l, T r){ return std::min(l, r); }); } template Array max(const Array &a, const Array &b) { Array result(a.shape()); max(result, a, b); return result; } template Array min(const Array &a, const Array &b) { Array result(a.shape()); min(result, a, b); return result; } // // ArrayConformanceError // template void max(Array &result, const Array &a, const T &b) { checkArrayShapes (a, result, "max"); arrayTransform (a, b, result, [](T a, T b){ return std::max(a, b); }); } // // ArrayConformanceError // template void min(Array &result, const Array &a, const T &b) { checkArrayShapes (a, result, "min"); arrayTransform (a, b, result, [](T a, T b){ return std::min(a, b); }); } template Array max(const Array &a, const T &b) { Array result(a.shape()); max(result, a, b); return result; } template Array min(const Array &a, const T &b) { Array result(a.shape()); min(result, a, b); return result; } template void indgen(Array &a, T start, T inc) { if (a.contiguousStorage()) { typename Array::contiter aend = a.cend(); for (typename Array::contiter iter=a.cbegin(); iter!=aend; ++iter) { *iter = start; start += inc; } } else { typename Array::iterator aend = a.end(); for (typename Array::iterator iter=a.begin(); iter!=aend; ++iter) { *iter = start; start += inc; } } } template Array cos(const Array &a) { return arrayTransformResult (a, [](T v){ return std::cos(v); }); } template Array cosh(const Array &a) { return arrayTransformResult (a, [](T v){ return std::cosh(v); }); } template Array exp(const Array &a) { return arrayTransformResult (a, [](T v){ return std::exp(v); }); } template Array log(const Array &a) { return arrayTransformResult (a, [](T v){ return std::log(v); }); } template Array log10(const Array &a) { return arrayTransformResult (a, [](T v){ return std::log10(v); }); } template Array sin(const Array &a) { return arrayTransformResult (a, [](T v){ return std::sin(v); }); } template Array sinh(const Array &a) { return arrayTransformResult (a, [](T v){ return std::sinh(v); }); } template Array sqrt(const Array &a) { return arrayTransformResult (a, [](T a){ return std::sqrt(a);}); } template Array square(const Array &a) { return arrayTransformResult (a, [](T a){ return a*a; }); } template Array cube(const Array &a) { return arrayTransformResult (a, [](T a){ return a*a*a; }); } template Array acos(const Array &a) { return arrayTransformResult (a, [](T a){ return std::acos(a); }); } template Array asin(const Array &a) { return arrayTransformResult (a, [](T a){ return std::asin(a); }); } template Array atan(const Array &a) { return arrayTransformResult (a, [](T a){ return std::atan(a); }); } template Array ceil(const Array &a) { return arrayTransformResult (a, [](T val) { return std::ceil(val);}); } template Array fabs(const Array &a) { return arrayTransformResult (a, [](T t){ return std::abs(t); }); } template Array abs(const Array &a) { return arrayTransformResult (a, [](T t){ return std::abs(t); }); } template Array floor(const Array &a) { return arrayTransformResult (a, [](T t){ return std::floor(t); }); } template Array round(const Array &a) { return arrayTransformResult (a, [](T t){ return std::round(t); }); } template Array sign(const Array &a) { return arrayTransformResult (a, [](T value) { return (value<0 ? -1 : (value>0 ? 1:0));}); } template Array tan(const Array &a) { return arrayTransformResult (a, [](T t){ return std::tan(t); }); } template Array tanh(const Array &a) { return arrayTransformResult (a, [](T t){ return std::tanh(t); }); } // // ArrayConformanceError // template Array pow(const Array &a, const Array &b) { checkArrayShapes (a, b, "pow"); return arrayTransformResult (a, b, [](T l, T r) { return std::pow(l, r); }); } template Array pow(const T &a, const Array &b) { return arrayTransformResult (a, b, [](T l, T r) { return std::pow(l, r); }); } template Array pow(const Array &a, const T &b) { return arrayTransformResult (a, b, [](T l, T r) { return std::pow(l, r); }); } template Array> pow(const Array> &a, const T &b) { Array> result(a.shape()); arrayContTransform (a, b, result, [](std::complex l, T r) { return std::pow(l, r); }); return result; } // // ArrayConformanceError // template Array atan2(const Array &a, const Array &b) { checkArrayShapes (a, b, "atan2"); return arrayTransformResult (a, b, [](T l, T r) { return std::atan2(l,r);}); } template Array atan2(const T &a, const Array &b) { return arrayTransformResult (a, b, [](T l, T r) { return std::atan2(l,r);}); } template Array atan2(const Array &a, const T &b) { return arrayTransformResult (a, b, [](T l, T r) { return std::atan2(l,r);}); } // // ArrayConformanceError // template Array fmod(const Array &a, const Array &b) { checkArrayShapes (a, b, "fmod"); return arrayTransformResult (a, b, [](T l, T r) { return std::fmod(l,r);}); } template Array fmod(const T &a, const Array &b) { return arrayTransformResult (a, b, [](T l, T r) { return std::fmod(l,r);}); } template Array fmod(const Array &a, const T &b) { return arrayTransformResult (a, b, [](T l, T r) { return std::fmod(l,r);}); } // // ArrayConformanceError // template Array floormod(const Array &a, const Array &b) { checkArrayShapes (a, b, "floormod"); return arrayTransformResult (a, b, static_cast(arrays_internal::floormod)); } template Array floormod(const T &a, const Array &b) { return arrayTransformResult (a, b, static_cast(arrays_internal::floormod)); } template Array floormod(const Array &a, const T &b) { return arrayTransformResult (a, b, static_cast(arrays_internal::floormod)); } // // ArrayError // template T sum(const Array &a) { return a.contiguousStorage() ? std::accumulate(a.cbegin(), a.cend(), T(), std::plus()) : std::accumulate(a.begin(), a.end(), T(), std::plus()); } template T sumsqr(const Array &a) { auto sumsqr = [](T left, T right) { return left + right*right;}; return a.contiguousStorage() ? std::accumulate(a.cbegin(), a.cend(), T(), sumsqr) : std::accumulate(a.begin(), a.end(), T(), sumsqr); } // // ArrayError // template T product(const Array &a) { if (a.empty()) { return T(); } // Get first element, because T(1) may not work for all types. T prod = *a.data(); if (a.contiguousStorage()) { typename Array::const_contiter iter(a.cbegin()); ++iter; return std::accumulate(iter, a.cend(), prod, std::multiplies()); } else { typename Array::const_iterator iter(a.begin()); ++iter; return std::accumulate(iter, a.end(), prod, std::multiplies()); } } // // ArrayError // template T mean(const Array &a) { if (a.empty()) { throw(ArrayError("::mean(const Array &) - 0 element array")); } return T(sum(a)/T(1.0*a.nelements())); } // // ArrayError // // Similar to numpy the ddof argument can be used to get the population // variance (ddof=0) or the sample variance (ddof=1). template T pvariance(const Array &a, T mean, size_t ddof) { if (a.nelements() < ddof+1) { throw(ArrayError("::variance(const Array &) - Need at least " + std::to_string(ddof+1) + " elements")); } T sum = a.contiguousStorage() ? std::accumulate(a.cbegin(), a.cend(), T(), arrays_internal::SumSqrDiff(mean)) : std::accumulate(a.begin(), a.end(), T(), arrays_internal::SumSqrDiff(mean)); return T(sum/T(1.0*a.nelements() - ddof)); } template T variance(const Array &a, T mean) { return pvariance (a, mean, 1); } template T pvariance(const Array &a, size_t ddof) { return pvariance(a, mean(a), ddof); } template T variance(const Array &a) { return pvariance(a, mean(a), 1); } // // ArrayError // template T pstddev(const Array &a, T mean, size_t ddof) { if (a.nelements() < ddof+1) { throw(ArrayError("::stddev(const Array &) - Need at least " + std::to_string(ddof+1) + " elements")); } return std::sqrt(pvariance(a, mean, ddof)); } template T stddev(const Array &a, T mean) { return pstddev (a, mean, 1); } template T pstddev(const Array &a, size_t ddof) { return pstddev (a, mean(a), ddof); } template T stddev(const Array &a) { return pstddev (a, mean(a), 1); } // // ArrayError // template T avdev(const Array &a) { if (a.nelements() < 1) { throw(ArrayError("::avdev(const Array &,) - Need at least 1 " "element")); } return avdev(a, mean(a)); } // // ArrayError // template T avdev(const Array &a, T mean) { if (a.nelements() < 1) { throw(ArrayError("::avdev(const Array &,T) - Need at least 1 " "element")); } auto sumabsdiff = [mean](T left, T right) { return left + std::abs(right-mean); }; T sum = a.contiguousStorage() ? std::accumulate(a.cbegin(), a.cend(), T(), sumabsdiff) : std::accumulate(a.begin(), a.end(), T(), sumabsdiff); return T(sum/T(1.0*a.nelements())); } // // ArrayError // template T rms(const Array &a) { if (a.nelements() < 1) { throw(ArrayError("::rms(const Array &) - Need at least 1 " "element")); } auto sumsqr = [](T left, T right) { return left + right*right; }; T sum = a.contiguousStorage() ? std::accumulate(a.cbegin(), a.cend(), T(), sumsqr) : std::accumulate(a.begin(), a.end(), T(), sumsqr); return T(std::sqrt(sum/T(1.0*a.nelements()))); } // // ArrayError // template T median(const Array &a, std::vector& scratch, bool sorted, bool takeEvenMean, bool inPlace) { T medval=T(); size_t nelem = a.nelements(); if (nelem < 1) { throw(ArrayError("::median(T*) - array needs at least 1 element")); } //# Mean does not have to be taken for odd number of elements. if (nelem%2 != 0) { takeEvenMean = false; } // A copy is needed if not contiguous or if not in place. const T* storage; if (!a.contiguousStorage() || !inPlace) { a.tovector(scratch); storage = scratch.data(); } else { storage = a.data(); } T* data = const_cast(storage); size_t n2 = (nelem - 1)/2; if (!sorted) { std::nth_element(data, data+n2, data+nelem); medval = data[n2]; if (takeEvenMean) { std::nth_element(data, data+n2+1, data+nelem); medval = T(0.5 * (medval + data[n2+1])); } } else { if (takeEvenMean) { medval = T(0.5 * (data[n2] + data[n2+1])); } else { medval = data[n2]; } } return medval; } // // ArrayError // template T madfm(const Array &a, std::vector& scratch, bool sorted, bool takeEvenMean, bool inPlace) { T med = median(a, scratch, sorted, takeEvenMean, inPlace); Array atmp; if (inPlace && a.contiguousStorage()) { atmp.reference (a); // remove constness } else { // A copy of a has been made to scratch. // Using it saves computing. // (this was changed for array2: sharing storage is no longer possible) assert(a.size() == scratch.size()); atmp.resize(a.shape()); atmp.assign_conforming (Array(a.shape(), scratch.data())); } T* aptr = atmp.data(); for (size_t i=0; i // ArrayError // template T fractile(const Array &a, std::vector& scratch, float fraction, bool sorted, bool inPlace) { if (fraction < 0 || fraction > 1) { throw(ArrayError("::fractile(const Array&) - fraction <0 or >1 ")); } size_t nelem = a.nelements(); if (nelem < 1) { throw(ArrayError("::fractile(const Array&) - Need at least 1 " "elements")); } // A copy is needed if not contiguous or if not in place. const T* storage = a.data(); if (!a.contiguousStorage() || !inPlace) { a.tovector(scratch); storage = scratch.data(); } T* data = const_cast(storage); size_t n2 = size_t((nelem - 1) * double(fraction) + 0.01); if (!sorted) { std::nth_element(data, data+n2, data+nelem); } return data[n2]; } // // ArrayError // template T interFractileRange(const Array &a, std::vector& scratch, float fraction, bool sorted, bool inPlace) { if (!(fraction>0 && fraction<0.5)) throw std::runtime_error("interFractileRange: invalid parameter"); T hex1, hex2; hex1 = fractile(a, scratch, fraction, sorted, inPlace); if (inPlace && a.contiguousStorage()) { hex2 = fractile(a, scratch, 1-fraction, sorted, inPlace); } else { // In this case a copy of a has been made to scratch. // Using it saves making another copy. if (a.size() != scratch.size()) throw std::runtime_error("interFractileRange: array sizes don't match"); Array atmp(a.shape(), scratch.data(), SHARE); hex2 = fractile(atmp, scratch, 1-fraction, sorted, inPlace); } return (hex2 - hex1); } template Array > makeComplex(const Array &left, const Array& right) { checkArrayShapes (left, right, "makeComplex"); Array > res(left.shape()); arrayContTransform (left, right, res, [](T r, T i) { return std::complex(r, i);}); return res; } template Array > makeComplex(const T &left, const Array& right) { Array > res(right.shape()); arrayContTransform (left, right, res, [](T r, T i) { return std::complex(r, i);}); return res; } template Array > makeComplex(const Array &left, const T& right) { Array > res(left.shape()); arrayContTransform (left, right, res, [](T r, T i) { return std::complex(r, i);}); return res; } template void setReal(Array &carray, const Array &rarray) { checkArrayShapes (carray, rarray, "setReal"); // Cannot be done in place, because imag is taken from second operand. arrayTransform (rarray, carray, carray, [](R l, C r)->C { return C(l, std::imag(r)); }); } template void setImag(Array &carray, const Array &rarray) { checkArrayShapes (carray, rarray, "setImag"); arrayTransformInPlace (carray, rarray, [](C l, R r)->C { return C(std::real(l), r); }); } template void convertArray(Array &to, const Array &from) { if (to.nelements() == 0 && from.nelements() == 0) { return; } if (to.shape() != from.shape()) { throw(ArrayConformanceError("void ::convertArray(Array &to, " "const Array &from)" " - arrays do not conform")); } if (to.contiguousStorage() && from.contiguousStorage()) { typename Array::const_contiter endFrom = from.cend(); typename Array::const_contiter iterFrom = from.cbegin(); for (typename Array::contiter iterTo = to.cbegin(); iterFrom != endFrom; ++iterFrom, ++iterTo) { arrays_internal::convertScalar (*iterTo, *iterFrom); } } else { typename Array::const_iterator endFrom = from.end(); typename Array::const_iterator iterFrom = from.begin(); for (typename Array::iterator iterTo = to.begin(); iterFrom != endFrom; ++iterFrom, ++iterTo) { arrays_internal::convertScalar (*iterTo, *iterFrom); } } } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Arrays/ArrayMathBase.h000066400000000000000000000042531476623553700203340ustar00rootroot00000000000000//# ArrayMathBase.h: Basic functions and classes for math on Array objects //# Copyright (C) 2012 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_ARRAYMATHBASE_2_H #define CASA_ARRAYMATHBASE_2_H #include "ArrayFwd.h" namespace casacore { // // Basic class for math on Array objects // // // // // //
          • Array // // // // The abstract base class ArrayFunctorBase is defined for functors to // be used in functions like slidingXXX. // Virtual functions instead of templated functions are used to avoid // code bloat when used in functions like partialArrayMath. Because a // reduction operation usually takes much more time than the call, using // virtual functions hardly imposes a performance penalty. // template class ArrayFunctorBase { public: virtual ~ArrayFunctorBase() {} virtual RES operator() (const Array&) const = 0; }; } //# end namespace #endif casacore-3.7.1/casa/Arrays/ArrayOpsDiffShapes.cc000066400000000000000000000032421476623553700215010ustar00rootroot00000000000000//# ArrayOpsDiffShapes.cc: Operations for 2 Arrays with possibly different shapes. //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., 675 Mass Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include "ArrayOpsDiffShapes.h" #include "ArrayMath.h" #include "IPosition.h" namespace casacore { bool rightExpandableToLeft(const IPosition& leftShape, const IPosition& rightShape) { size_t n_desired_dim = rightShape.nelements(); bool expandable = (leftShape.nelements() > n_desired_dim); for(size_t axnum = 0; expandable && axnum < n_desired_dim; ++axnum) expandable = (leftShape[axnum] == rightShape[axnum]); return expandable; } } //#End casa namespace casacore-3.7.1/casa/Arrays/ArrayOpsDiffShapes.h000066400000000000000000000107701476623553700213470ustar00rootroot00000000000000//# ArrayOpsDiffShapes.h: Operations for 2 Arrays with possibly different shapes. //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., 675 Mass Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_ARRAYOPSDIFFSHAPES_2_H #define CASA_ARRAYOPSDIFFSHAPES_2_H #include "ArrayMath.h" #include "ArrayLogical.h" #include "IPosition.h" //# Don't forget a .tcc file is included at the end! namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Operations for 2 Arrays with possibly different shapes. // // // // //
          • Array //
          • ArrayMath // // // // This file contains global functions that attempt binary operations with // arrays that have possibly differing shapes. // // // // These functions perform operations on two arrays, left and right, which go // chunk by chunk in left and element by element in right, as long as right // spans a subspace of left. If left's shape has more dimensions than right's, // each entry in right will effectively be replicated as necessary to act on // each entry in the corresponding chunk of left. // e.g. if left's shape is (256, 256, 1, 4), and right's is (256, 256), // left(i, j, 1, l) will be operated on with right(i, j) for i & j from 0 to // 255 and l from 0 to 3. Note that right must be either reformable to left's // shape (same # of elements) or its shape must equal left's shape "as far as // it goes", i.e. where right's dimensions are defined. // // // // // Array a(10, 6); // Vector b(10); // Array c(10, 6); // // c = binOpExpandR(a, b, std::plus()); // // This example sets c(i, j) to a(i, j) + b(i). It checks that either b's // shape can be reformed to a's (same # of elements) or that a's shape is the // same as b's where b's dimensions are defined. // The result of this operation is an Array. // // // Returns a LogicalArray with elements (at pos) set to (data(pos) == // truthvalue). data is effectively collapsed using anyEQ if necessary to // fit desiredform. Throws an exception if that does not work. template LogicalArray reformedMask(const Array& data, const T truthvalue, const IPosition& desiredform); // Can arrays left and right with respective shapes leftShape and rightShape be // used in function(left, right, ...) for the other functions declared here? bool rightExpandableToLeft(const IPosition& leftShape, const IPosition& rightShape); // Apply op elementwise to left and right, replicating elements of right as // necessary (see example above). Throws an ArrayConformanceError exception if // that cannot be done. // // Currently assumes that it is the trailing axes of left that right will be // replicated along. e.g. if left's shape is (1, 2, 4) and right's is (1, 2), // the result will be left(i, j, k) op right(i, j) for all (i, j, k). // //# template //# Array binOpExpandR(const Array& left, const Array& right, //# BinaryOperator op); // Like binOpExpandR(left, right, res, op), but work on left in place. template void binOpExpandInPlace(Array& left, const Array& right, BinaryOperator op); // } //#End casa namespace #include "ArrayOpsDiffShapes.tcc" #endif casacore-3.7.1/casa/Arrays/ArrayOpsDiffShapes.tcc000066400000000000000000000111601476623553700216630ustar00rootroot00000000000000//# ArrayOpsDiffShapes.tcc: Operations for 2 Arrays with possibly different shapes. //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., 675 Mass Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_ARRAYOPSDIFFSHAPES_2_TCC #define CASA_ARRAYOPSDIFFSHAPES_2_TCC #include "ArrayMath.h" #include "ArrayLogical.h" #include "IPosition.h" #include "ArrayError.h" namespace casacore { template LogicalArray reformedMask(const Array& data, const T truthvalue, const IPosition& desiredform) { if(data.shape().nelements() == desiredform.nelements() && data.shape() == desiredform){ return (data == truthvalue); } else if(static_cast(data.nelements()) == desiredform.product()){ return (data == truthvalue).reform(desiredform); } else{ if(rightExpandableToLeft(data.shape(), desiredform)){ size_t n_data_dim = data.shape().nelements(); size_t n_desired_dim = desiredform.nelements(); // Create an array with desiredform's shape, LogicalArray collapseddata(desiredform); ReadOnlyArrayIterator data_cursor(data, IPosition::otherAxes(n_data_dim, IPosition::makeAxisPath(n_desired_dim))); IPosition collapsedPos; // Go through each position in the new array, for(ArrayPositionIterator positer(desiredform, 0); !positer.pastEnd(); positer.next()){ collapsedPos = positer.pos(); data_cursor.set(collapsedPos); collapseddata(collapsedPos) = anyEQ(data_cursor.array(), truthvalue); } //// Apparently it would be expensive to put this inside the for statement. //LogicalArray::iterator iterend(collapseddata.end()); // for(LogicalArray::iterator it = collapseddata.begin(); it != iterend; // ++it) // *it = data(it.itsCurPos).anyEQ(truthvalue); return collapseddata; } else{ std::ostringstream os; os << "reformedMask(): Could not reconcile the input shape (" << data.shape() << ")\n" << "with the output shape (" << desiredform << ")."; throw(ArrayConformanceError(os.str())); } } } template void binOpExpandInPlace(Array& leftarr, const Array& rightarr, BinaryOperator op) { const IPosition leftShape = leftarr.shape(); const IPosition rightShape = rightarr.shape(); // leftarr.conform(rightarr) fails if e.g. L == double and R == float. if(leftShape.nelements() == rightShape.nelements() && leftShape == rightShape){ arrayTransformInPlace(leftarr, rightarr, op); // Autochecks contiguity. } else if(leftarr.nelements() == rightarr.nelements()){ arrayTransformInPlace(leftarr, rightarr.reform(leftShape), op); } else{ size_t n_right_dim = rightShape.nelements(); if(rightExpandableToLeft(leftShape, rightShape)){ IPosition iteraxes(IPosition::otherAxes(leftShape.nelements(), IPosition::makeAxisPath(n_right_dim))); ArrayIterator left_cursor(leftarr, iteraxes); IPosition rightPos; // Go through each position in the new array, for(ArrayPositionIterator positer(rightShape, 0); !positer.pastEnd(); positer.next()){ rightPos = positer.pos(); left_cursor.set(rightPos); // "leftChunk op= rightEntry" arrayTransformInPlace(left_cursor.array(), rightarr(rightPos), op); } } else{ std::ostringstream os; // String(rightShape) is not supported. os << "binOpExpandInPlace(): rightarr's shape (" << rightShape << ")\n" << " has more dimensions than leftarr's (" << leftShape << ")!"; throw(ArrayConformanceError(os.str())); } } } } //#End casa namespace #endif casacore-3.7.1/casa/Arrays/ArrayPartMath.cc000066400000000000000000000054671476623553700205360ustar00rootroot00000000000000//# ArrayPartMath.h: mathematics done on an array parts. //# Copyright (C) 1993,1994,1995,1996,1998,1999,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include "ArrayPartMath.h" namespace casacore { void fillBoxedShape (const IPosition& shape, const IPosition& boxSize, IPosition& fullBoxSize, IPosition& resultShape) { size_t ndim = shape.size(); // Set missing axes to 1. fullBoxSize.resize (ndim); fullBoxSize = 1; for (size_t i=0; i shape[i]) { fullBoxSize[i] = shape[i]; } else { fullBoxSize[i] = boxSize[i]; } } // Determine the output shape. resultShape.resize (ndim); for (size_t i=0; i namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Mathematical and logical operations for Array parts. // // // // //
          • Array // // // // This file contains global functions which perform part by part // mathematical or logical operations on arrays. // // // // These functions perform chunk by chunk mathematical operations on // arrays. // In particular boxed and sliding operations are possible. E.g. to calculate // the median in sliding windows making it possible to subtract the background // in an image. // // The operations to be performed are defined by means of functors that // reduce an array subset to a scalar. Those functors are wrappers for // ArrayMath and ArrayLogical functions like sum, median, and ntrue. // // The partialXX functions are a special case of the // BoxedArrayMath function. // They reduce one or more entire axes which can be done in a faster way than // the more general boxedArrayMath function. // // // // // Array data(...); // Array means = partialMeans (data, IPosition(2,0,1)); // // This example calculates the mean of each plane in the data array. // // // // // IPosition shp = data.shape(); // Array means = boxedArrayMath (data, IPosition(2,shp[0],shp[1]), // SumFunc()); // // does the same as the first example. // Note that in this example the box is formed by the entire axes, but it // could also be a subset of it to average, say, boxes of 5*5 elements. // // // // Array mathematical operations -- Mathematical operations for // Arrays. // // // // Determine the sum, product, etc. for the given axes only. // The result is an array with a shape formed by the remaining axes. // For example, for an array with shape [3,4,5], collapsing axis 0 // results in an array with shape [4,5] containing, say, the sum for // each X line. // Summing for axes 0 and 2 results in an array with shape [4] containing, // say, the sum for each XZ plane. // // ArrayLogical.h contains the functions ntrue, nfalse, partialNTrue and // partialNFalse to count the number of true or false elements in an array. // // template Array partialSums (const Array& array, const IPosition& collapseAxes); template Array partialSumSqrs (const Array& array, const IPosition& collapseAxes); template Array partialProducts (const Array& array, const IPosition& collapseAxes); template Array partialMins (const Array& array, const IPosition& collapseAxes); template Array partialMaxs (const Array& array, const IPosition& collapseAxes); template Array partialMeans (const Array& array, const IPosition& collapseAxes); template inline Array partialVariances (const Array& array, const IPosition& collapseAxes, size_t ddof=1) { return partialVariances (array, collapseAxes, partialMeans (array, collapseAxes), ddof); } template Array partialVariances (const Array& array, const IPosition& collapseAxes, const Array& means); template Array partialVariances (const Array& array, const IPosition& collapseAxes, const Array& means, size_t ddof); template Array> partialVariances (const Array>& array, const IPosition& collapseAxes, const Array>& means, size_t ddof); template inline Array partialStddevs (const Array& array, const IPosition& collapseAxes, size_t ddof=1) { return sqrt (partialVariances (array, collapseAxes, partialMeans (array, collapseAxes), ddof)); } template inline Array partialStddevs (const Array& array, const IPosition& collapseAxes, const Array& means, size_t ddof=1) { return sqrt (partialVariances (array, collapseAxes, means, ddof)); } template inline Array partialAvdevs (const Array& array, const IPosition& collapseAxes) { return partialAvdevs (array, collapseAxes, partialMeans (array, collapseAxes)); } template Array partialAvdevs (const Array& array, const IPosition& collapseAxes, const Array& means); template Array partialRmss (const Array& array, const IPosition& collapseAxes); template Array partialMedians (const Array& array, const IPosition& collapseAxes, bool takeEvenMean=false, bool inPlace=false); template Array partialMadfms (const Array& array, const IPosition& collapseAxes, bool takeEvenMean=false, bool inPlace=false); template Array partialFractiles (const Array& array, const IPosition& collapseAxes, float fraction, bool inPlace=false); template Array partialInterFractileRanges (const Array& array, const IPosition& collapseAxes, float fraction, bool inPlace=false); template Array partialInterHexileRanges (const Array& array, const IPosition& collapseAxes, bool inPlace=false) { return partialInterFractileRanges (array, collapseAxes, 1./6., inPlace); } template Array partialInterQuartileRanges (const Array& array, const IPosition& collapseAxes, bool inPlace=false) { return partialInterFractileRanges (array, collapseAxes, 0.25, inPlace); } // // Define functors to perform a reduction function on an Array object. // Use virtual functions instead of templates to avoid code bloat // in partialArrayMath, etc. template class SumFunc : public ArrayFunctorBase { public: virtual ~SumFunc() {} virtual T operator() (const Array& arr) const final override { return sum(arr); } }; template class SumSqrFunc : public ArrayFunctorBase { public: virtual ~SumSqrFunc() {} virtual T operator() (const Array& arr) const final override { return sumsqr(arr); } }; template class ProductFunc : public ArrayFunctorBase { public: virtual ~ProductFunc() {} virtual T operator() (const Array& arr) const final override { return product(arr); } }; template class MinFunc : public ArrayFunctorBase { public: virtual ~MinFunc() {} virtual T operator() (const Array& arr) const final override { return min(arr); } }; template class MaxFunc : public ArrayFunctorBase { public: virtual ~MaxFunc() {} virtual T operator() (const Array& arr) const final override { return max(arr); } }; template class MeanFunc : public ArrayFunctorBase { public: virtual ~MeanFunc() {} virtual T operator() (const Array& arr) const final override { return mean(arr); } }; template class VarianceFunc : public ArrayFunctorBase { public: explicit VarianceFunc (size_t ddof) : itsDdof(ddof) {} virtual ~VarianceFunc() {} virtual T operator() (const Array& arr) const final override { return pvariance(arr, itsDdof); } private: size_t itsDdof; }; template class StddevFunc : public ArrayFunctorBase { public: explicit StddevFunc (size_t ddof) : itsDdof(ddof) {} virtual ~StddevFunc() {} virtual T operator() (const Array& arr) const final override { return pstddev(arr, itsDdof); } private: size_t itsDdof; }; template class AvdevFunc : public ArrayFunctorBase { public: virtual ~AvdevFunc() {} virtual T operator() (const Array& arr) const final override { return avdev(arr); } }; template class RmsFunc : public ArrayFunctorBase { public: virtual ~RmsFunc() {} virtual T operator() (const Array& arr) const final override { return rms(arr); } }; template class MedianFunc : public ArrayFunctorBase { public: explicit MedianFunc (bool sorted=false, bool takeEvenMean=true, bool inPlace = false) : itsSorted(sorted), itsTakeEvenMean(takeEvenMean), itsInPlace(inPlace) {} virtual ~MedianFunc() {} virtual T operator() (const Array& arr) const final override { return median(arr, itsTmp, itsSorted, itsTakeEvenMean, itsInPlace); } private: bool itsSorted; bool itsTakeEvenMean; bool itsInPlace; mutable std::vector itsTmp; }; template class MadfmFunc : public ArrayFunctorBase { public: explicit MadfmFunc(bool sorted = false, bool takeEvenMean = true, bool inPlace = false) : itsSorted(sorted), itsTakeEvenMean(takeEvenMean), itsInPlace(inPlace) {} virtual ~MadfmFunc() {} virtual T operator()(const Array& arr) const final override { return madfm(arr, itsTmp, itsSorted, itsTakeEvenMean, itsInPlace); } private: bool itsSorted; bool itsTakeEvenMean; bool itsInPlace; mutable std::vector itsTmp; }; template class FractileFunc : public ArrayFunctorBase { public: explicit FractileFunc (float fraction, bool sorted = false, bool inPlace = false) : itsFraction(fraction), itsSorted(sorted), itsInPlace(inPlace) {} virtual ~FractileFunc() {} virtual T operator() (const Array& arr) const final override { return fractile(arr, itsTmp, itsFraction, itsSorted, itsInPlace); } private: float itsFraction; bool itsSorted; bool itsInPlace; mutable std::vector itsTmp; }; template class InterFractileRangeFunc { public: explicit InterFractileRangeFunc(float fraction, bool sorted = false, bool inPlace = false) : itsFraction(fraction), itsSorted(sorted), itsInPlace(inPlace) {} virtual ~InterFractileRangeFunc() {} virtual T operator()(const Array& arr) const final override { return interFractileRange(arr, itsTmp, itsFraction, itsSorted, itsInPlace); } private: float itsFraction; bool itsSorted; bool itsInPlace; mutable std::vector itsTmp; }; template class InterHexileRangeFunc: public InterFractileRangeFunc { public: explicit InterHexileRangeFunc(bool sorted = false, bool inPlace = false) : InterFractileRangeFunc (1./6., sorted, inPlace) {} virtual ~InterHexileRangeFunc() {} }; template class InterQuartileRangeFunc: public InterFractileRangeFunc { public: explicit InterQuartileRangeFunc(bool sorted = false, bool inPlace = false) : InterFractileRangeFunc (0.25, sorted, inPlace) {} virtual ~InterQuartileRangeFunc() {} }; // Do partial reduction of an Array object. I.e., perform the operation // on a subset of the array axes (the collapse axes). template inline Array partialArrayMath (const Array& a, const IPosition& collapseAxes, const ArrayFunctorBase& funcObj) { Array res; partialArrayMath (res, a, collapseAxes, funcObj); return res; } template void partialArrayMath (Array& res, const Array& a, const IPosition& collapseAxes, const ArrayFunctorBase& funcObj); // Apply the given ArrayMath reduction function objects // to each box in the array. // // Downsample an array by taking the median of every [25,25] elements. // // Array downArr = boxedArrayMath(in, IPosition(2,25,25), // MedianFunc()); // // // The dimensionality of the array can be larger than the box; in that // case the missing axes of the box are assumed to have length 1. // A box axis length <= 0 means the full array axis. template inline Array boxedArrayMath (const Array& a, const IPosition& boxSize, const ArrayFunctorBase& funcObj) { Array res; boxedArrayMath (res, a, boxSize, funcObj); return res; } template void boxedArrayMath (Array&, const Array& array, const IPosition& boxSize, const ArrayFunctorBase& funcObj); // Apply for each element in the array the given ArrayMath reduction function // object to the box around that element. The full box is 2*halfBoxSize + 1. // It can be used for arrays and boxes of any dimensionality; missing // halfBoxSize values are set to 0. // // Determine for each element in the array the median of a box // with size [51,51] around that element: // // Array medians = slidingArrayMath(in, IPosition(2,25,25), // MedianFunc()); // // This is a potentially expensive operation. On a high-end PC it took // appr. 27 seconds to get the medians for an array of [1000,1000] using // a halfBoxSize of [50,50]. // //
            The fillEdge argument determines how the edge is filled where // no full boxes can be made. true means it is set to zero; false means // that the edge is removed, thus the output array is smaller than the // input array. // This brute-force method of determining the medians outperforms // all kinds of smart implementations. For a vector it is about as fast // as casacore class MedianSlider, for a 2D array // it is much, much faster. // template inline Array slidingArrayMath (const Array& a, const IPosition& halfBoxSize, const ArrayFunctorBase& funcObj, bool fillEdge=true) { Array res; slidingArrayMath (res, a, halfBoxSize, funcObj, fillEdge); return res; } template void slidingArrayMath (Array& res, const Array& array, const IPosition& halfBoxSize, const ArrayFunctorBase& funcObj, bool fillEdge=true); //
            // // Helper functions for boxed and sliding functions. // Determine full box shape and shape of result for a boxed operation. void fillBoxedShape (const IPosition& shape, const IPosition& boxShape, IPosition& fullBoxShape, IPosition& resultShape); // Determine the box end and shape of result for a sliding operation. // It returns false if the result is empty. bool fillSlidingShape (const IPosition& shape, const IPosition& halfBoxSize, IPosition& boxEnd, IPosition& resultShape); // } //# NAMESPACE CASACORE - END #include "ArrayPartMath.tcc" #endif casacore-3.7.1/casa/Arrays/ArrayPartMath.tcc000066400000000000000000000727221476623553700207200ustar00rootroot00000000000000//# ArrayMath.cc: Arithmetic functions defined on Arrays //# Copyright (C) 1993,1994,1995,1996,1997,1998,1999,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include "ArrayPartMath.h" #include "ArrayIter.h" #include "ArrayError.h" #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template Array partialSums (const Array& array, const IPosition& collapseAxes) { if (collapseAxes.nelements() == 0) { return array.copy(); } const IPosition& shape = array.shape(); size_t ndim = shape.nelements(); if (ndim == 0) { return Array(); } IPosition resShape, incr; int nelemCont = 0; size_t stax = partialFuncHelper (nelemCont, resShape, incr, shape, collapseAxes); Array result (resShape); result = 0; bool deleteData, deleteRes; const T* arrData = array.getStorage (deleteData); const T* data = arrData; T* resData = result.getStorage (deleteRes); T* res = resData; // Find out how contiguous the data is, i.e. if some contiguous data // end up in the same output element. // cont tells if any data are contiguous. // stax gives the first non-contiguous axis. // n0 gives the number of contiguous elements. bool cont = true; size_t n0 = nelemCont; int incr0 = incr(0); if (nelemCont <= 1) { cont = false; n0 = shape(0); stax = 1; } // Loop through all data and assemble as needed. IPosition pos(ndim, 0); while (true) { if (cont) { T tmp = *res; for (size_t i=0; i Array partialSumSqrs (const Array& array, const IPosition& collapseAxes) { if (collapseAxes.nelements() == 0) { return array.copy(); } const IPosition& shape = array.shape(); size_t ndim = shape.nelements(); if (ndim == 0) { return Array(); } IPosition resShape, incr; int nelemCont = 0; size_t stax = partialFuncHelper (nelemCont, resShape, incr, shape, collapseAxes); Array result (resShape); result = 0; bool deleteData, deleteRes; const T* arrData = array.getStorage (deleteData); const T* data = arrData; T* resData = result.getStorage (deleteRes); T* res = resData; // Find out how contiguous the data is, i.e. if some contiguous data // end up in the same output element. // cont tells if any data are contiguous. // stax gives the first non-contiguous axis. // n0 gives the number of contiguous elements. bool cont = true; size_t n0 = nelemCont; int incr0 = incr(0); if (nelemCont <= 1) { cont = false; n0 = shape(0); stax = 1; } // Loop through all data and assemble as needed. IPosition pos(ndim, 0); while (true) { if (cont) { T tmp = *res; for (size_t i=0; i Array partialProducts (const Array& array, const IPosition& collapseAxes) { if (collapseAxes.nelements() == 0) { return array.copy(); } const IPosition& shape = array.shape(); size_t ndim = shape.nelements(); if (ndim == 0) { return Array(); } IPosition resShape, incr; int nelemCont = 0; size_t stax = partialFuncHelper (nelemCont, resShape, incr, shape, collapseAxes); Array result (resShape); result = T(1); bool deleteData, deleteRes; const T* arrData = array.getStorage (deleteData); const T* data = arrData; T* resData = result.getStorage (deleteRes); T* res = resData; // Find out how contiguous the data is, i.e. if some contiguous data // end up in the same output element. // cont tells if any data are contiguous. // stax gives the first non-contiguous axis. // n0 gives the number of contiguous elements. bool cont = true; size_t n0 = nelemCont; int incr0 = incr(0); if (nelemCont <= 1) { cont = false; n0 = shape(0); stax = 1; } // Loop through all data and assemble as needed. IPosition pos(ndim, 0); while (true) { if (cont) { T tmp = *res; for (size_t i=0; i Array partialMins (const Array& array, const IPosition& collapseAxes) { if (collapseAxes.nelements() == 0) { return array.copy(); } const IPosition& shape = array.shape(); size_t ndim = shape.nelements(); if (ndim == 0) { return Array(); } IPosition resShape, incr; int nelemCont = 0; size_t stax = partialFuncHelper (nelemCont, resShape, incr, shape, collapseAxes); Array result (resShape); result = 0; bool deleteData, deleteRes; const T* arrData = array.getStorage (deleteData); const T* data = arrData; T* resData = result.getStorage (deleteRes); T* res = resData; // Initialize the minima with the first value of collapsed axes. IPosition end(shape-1); for (size_t i=0; i tmp(array); // to get a non-const array for operator() Array scratch(result); result.assign_conforming( tmp(IPosition(ndim,0), end).reform (resShape) ); // Find out how contiguous the data is, i.e. if some contiguous data // end up in the same output element. // cont tells if any data are contiguous. // stax gives the first non-contiguous axis. // n0 gives the number of contiguous elements. bool cont = true; size_t n0 = nelemCont; int incr0 = incr(0); if (nelemCont <= 1) { cont = false; n0 = shape(0); stax = 1; } // Loop through all data and assemble as needed. IPosition pos(ndim, 0); while (true) { if (cont) { T tmp = *res; for (size_t i=0; i Array partialMaxs (const Array& array, const IPosition& collapseAxes) { if (collapseAxes.nelements() == 0) { return array.copy(); } const IPosition& shape = array.shape(); size_t ndim = shape.nelements(); if (ndim == 0) { return Array(); } IPosition resShape, incr; int nelemCont = 0; size_t stax = partialFuncHelper (nelemCont, resShape, incr, shape, collapseAxes); Array result (resShape); result = 0; bool deleteData, deleteRes; const T* arrData = array.getStorage (deleteData); const T* data = arrData; T* resData = result.getStorage (deleteRes); T* res = resData; // Initialize the maxima with the first value of collapsed axes. IPosition end(shape-1); for (size_t i=0; i tmp(array); // to get a non-const array for operator() result.assign_conforming( tmp(IPosition(ndim,0), end).reform (resShape) ); // Find out how contiguous the data is, i.e. if some contiguous data // end up in the same output element. // cont tells if any data are contiguous. // stax gives the first non-contiguous axis. // n0 gives the number of contiguous elements. bool cont = true; size_t n0 = nelemCont; int incr0 = incr(0); if (nelemCont <= 1) { cont = false; n0 = shape(0); stax = 1; } // Loop through all data and assemble as needed. IPosition pos(ndim, 0); while (true) { if (cont) { T tmp = *res; for (size_t i=0; i tmp) { tmp = *data; } data++; } *res = tmp; } else { for (size_t i=0; i *res) { *res = *data; } data++; res += incr0; } } size_t ax; for (ax=stax; ax Array partialMeans (const Array& array, const IPosition& collapseAxes) { if (collapseAxes.nelements() == 0) { return array.copy(); } Array result = partialSums (array, collapseAxes); size_t nr = result.nelements(); if (nr > 0) { size_t factor = array.nelements() / nr; bool deleteRes; T* res = result.getStorage (deleteRes); for (size_t i=0; i Array partialVariances (const Array& array, const IPosition& collapseAxes, const Array& means) { return partialVariances (array, collapseAxes, means, 1); } template Array partialVariances (const Array& array, const IPosition& collapseAxes, const Array& means, size_t ddof) { const IPosition& shape = array.shape(); size_t ndim = shape.nelements(); if (ndim == 0) { return Array(); } IPosition resShape, incr; int nelemCont = 0; size_t stax = partialFuncHelper (nelemCont, resShape, incr, shape, collapseAxes); if (! resShape.isEqual (means.shape())) { throw ArrayError ("partialVariances: shape of means array mismatches " "shape of result array"); } Array result (resShape); result = 0; size_t nr = result.nelements(); int factor = int(array.nelements() / nr) - ddof; if (factor <= 0) { return result; } bool deleteData, deleteRes, deleteMean; const T* arrData = array.getStorage (deleteData); const T* data = arrData; const T* meanData = means.getStorage (deleteMean); const T* mean = meanData; T* resData = result.getStorage (deleteRes); T* res = resData; // Find out how contiguous the data is, i.e. if some contiguous data // end up in the same output element. // cont tells if any data are contiguous. // stax gives the first non-contiguous axis. // n0 gives the number of contiguous elements. bool cont = true; size_t n0 = nelemCont; int incr0 = incr(0); if (nelemCont <= 1) { cont = false; n0 = shape(0); stax = 1; } // Loop through all data and assemble as needed. IPosition pos(ndim, 0); while (true) { if (cont) { T tmp = *res; T tmpm = *mean; for (size_t i=0; i Array> partialVariances (const Array>& array, const IPosition& collapseAxes, const Array>& means, size_t ddof) { const IPosition& shape = array.shape(); size_t ndim = shape.nelements(); if (ndim == 0) { return Array>(); } IPosition resShape, incr; int nelemCont = 0; size_t stax = partialFuncHelper (nelemCont, resShape, incr, shape, collapseAxes); if (! resShape.isEqual (means.shape())) { throw ArrayError ("partialVariances: shape of means array mismatches " "shape of result array"); } Array> result (resShape); result = 0; size_t nr = result.nelements(); int factor = int(array.nelements() / nr) - ddof; if (factor <= 0) { return result; } bool deleteData, deleteRes, deleteMean; const std::complex* arrData = array.getStorage (deleteData); const std::complex* data = arrData; const std::complex* meanData = means.getStorage (deleteMean); const std::complex* mean = meanData; std::complex* resData = result.getStorage (deleteRes); std::complex* res = resData; // Find out how contiguous the data is, i.e. if some contiguous data // end up in the same output element. // cont tells if any data are contiguous. // stax gives the first non-contiguous axis. // n0 gives the number of contiguous elements. bool cont = true; size_t n0 = nelemCont; int incr0 = incr(0); if (nelemCont <= 1) { cont = false; n0 = shape(0); stax = 1; } // Loop through all data and assemble as needed. IPosition pos(ndim, 0); while (true) { if (cont) { std::complex tmp = *res; std::complex tmpm = *mean; for (size_t i=0; i var = *data++ - tmpm; tmp += var.real()*var.real() + var.imag()*var.imag(); } *res = tmp; } else { for (size_t i=0; i var = *data++ - *mean; *res += var.real()*var.real() + var.imag()*var.imag(); res += incr0; mean += incr0; } } size_t ax; for (ax=stax; ax Array partialAvdevs (const Array& array, const IPosition& collapseAxes, const Array& means) { const IPosition& shape = array.shape(); size_t ndim = shape.nelements(); if (ndim == 0) { return Array(); } IPosition resShape, incr; int nelemCont = 0; size_t stax = partialFuncHelper (nelemCont, resShape, incr, shape, collapseAxes); if (! resShape.isEqual (means.shape())) { throw ArrayError ("partialAvdevs: shape of means array mismatches " "shape of result array"); } Array result (resShape); result = 0; size_t nr = result.nelements(); size_t factor = array.nelements() / nr; bool deleteData, deleteRes, deleteMean; const T* arrData = array.getStorage (deleteData); const T* data = arrData; const T* meanData = means.getStorage (deleteMean); const T* mean = meanData; T* resData = result.getStorage (deleteRes); T* res = resData; // Find out how contiguous the data is, i.e. if some contiguous data // end up in the same output element. // cont tells if any data are contiguous. // stax gives the first non-contiguous axis. // n0 gives the number of contiguous elements. bool cont = true; size_t n0 = nelemCont; int incr0 = incr(0); if (nelemCont <= 1) { cont = false; n0 = shape(0); stax = 1; } // Loop through all data and assemble as needed. IPosition pos(ndim, 0); while (true) { if (cont) { T tmp = *res; T tmpm = *mean; for (size_t i=0; i Array partialRmss (const Array& array, const IPosition& collapseAxes) { if (collapseAxes.nelements() == 0) { return array.copy(); } const IPosition& shape = array.shape(); size_t ndim = shape.nelements(); if (ndim == 0) { return Array(); } IPosition resShape, incr; int nelemCont = 0; size_t stax = partialFuncHelper (nelemCont, resShape, incr, shape, collapseAxes); Array result (resShape); result = 0; size_t nr = result.nelements(); size_t factor = array.nelements() / nr; bool deleteData, deleteRes; const T* arrData = array.getStorage (deleteData); const T* data = arrData; T* resData = result.getStorage (deleteRes); T* res = resData; // Find out how contiguous the data is, i.e. if some contiguous data // end up in the same output element. // cont tells if any data are contiguous. // stax gives the first non-contiguous axis. // n0 gives the number of contiguous elements. bool cont = true; size_t n0 = nelemCont; int incr0 = incr(0); if (nelemCont <= 1) { cont = false; n0 = shape(0); stax = 1; } // Loop through all data and assemble as needed. IPosition pos(ndim, 0); while (true) { if (cont) { T tmp = *res; for (size_t i=0; i Array partialMedians (const Array& array, const IPosition& collapseAxes, bool takeEvenMean, bool inPlace) { // Need to make shallow copy because operator() is non-const. Array arr = array; // Is there anything to collapse? if (collapseAxes.nelements() == 0) { return (inPlace ? array : array.copy()); } const IPosition& shape = array.shape(); size_t ndim = shape.nelements(); if (ndim == 0) { return Array(); } // Get the remaining axes. // It also checks if axes are specified correctly. IPosition resAxes = IPosition::otherAxes (ndim, collapseAxes); size_t ndimRes = resAxes.nelements(); // Create the result shape. // Create blc and trc to step through the input array. IPosition resShape(ndimRes); IPosition blc(ndim, 0); IPosition trc(shape-1); for (size_t i=0; i result (resShape); bool deleteRes; T* resData = result.getStorage (deleteRes); T* res = resData; std::vector tmp; // Loop through all data and assemble as needed. IPosition pos(ndimRes, 0); while (true) { *res++ = median(arr(blc,trc), tmp, false, takeEvenMean, inPlace); size_t ax; for (ax=0; ax Array partialMadfms (const Array& array, const IPosition& collapseAxes, bool takeEvenMean, bool inPlace) { // Need to make shallow copy because operator() is non-const. Array arr = array; // Is there anything to collapse? if (collapseAxes.nelements() == 0) { return (inPlace ? array : array.copy()); } const IPosition& shape = array.shape(); size_t ndim = shape.nelements(); if (ndim == 0) { return Array(); } // Get the remaining axes. // It also checks if axes are specified correctly. IPosition resAxes = IPosition::otherAxes (ndim, collapseAxes); size_t ndimRes = resAxes.nelements(); // Create the result shape. // Create blc and trc to step through the input array. IPosition resShape(ndimRes); IPosition blc(ndim, 0); IPosition trc(shape-1); for (size_t i=0; i result (resShape); bool deleteRes; T* resData = result.getStorage (deleteRes); T* res = resData; std::vector tmp; // Loop through all data and assemble as needed. IPosition pos(ndimRes, 0); while (true) { *res++ = madfm(arr(blc,trc), tmp, false, takeEvenMean, inPlace); size_t ax; for (ax=0; ax Array partialFractiles (const Array& array, const IPosition& collapseAxes, float fraction, bool inPlace) { if (fraction < 0 || fraction > 1) { throw(ArrayError("::fractile(const Array&) - fraction <0 or >1 ")); } // Need to make shallow copy because operator() is non-const. Array arr = array; // Is there anything to collapse? if (collapseAxes.nelements() == 0) { return (inPlace ? array : array.copy()); } const IPosition& shape = array.shape(); size_t ndim = shape.nelements(); if (ndim == 0) { return Array(); } // Get the remaining axes. // It also checks if axes are specified correctly. IPosition resAxes = IPosition::otherAxes (ndim, collapseAxes); size_t ndimRes = resAxes.nelements(); // Create the result shape. // Create blc and trc to step through the input array. IPosition resShape(ndimRes); IPosition blc(ndim, 0); IPosition trc(shape-1); for (size_t i=0; i result (resShape); bool deleteRes; T* resData = result.getStorage (deleteRes); T* res = resData; std::vector tmp; // Loop through all data and assemble as needed. IPosition pos(ndimRes, 0); while (true) { *res++ = fractile(arr(blc,trc), tmp, fraction, false, inPlace); size_t ax; for (ax=0; ax Array partialInterFractileRanges (const Array& array, const IPosition& collapseAxes, float fraction, bool inPlace) { // Need to make shallow copy because operator() is non-const. Array arr = array; // Is there anything to collapse? if (collapseAxes.nelements() == 0) { return (inPlace ? array : array.copy()); } const IPosition& shape = array.shape(); size_t ndim = shape.nelements(); if (ndim == 0) { return Array(); } // Get the remaining axes. // It also checks if axes are specified correctly. IPosition resAxes = IPosition::otherAxes (ndim, collapseAxes); size_t ndimRes = resAxes.nelements(); // Create the result shape. // Create blc and trc to step through the input array. IPosition resShape(ndimRes); IPosition blc(ndim, 0); IPosition trc(shape-1); for (size_t i=0; i result (resShape); bool deleteRes; T* resData = result.getStorage (deleteRes); T* res = resData; std::vector tmp; // Loop through all data and assemble as needed. IPosition pos(ndimRes, 0); while (true) { *res++ = interFractileRange(arr(blc,trc), tmp, fraction, false, inPlace); size_t ax; for (ax=0; ax void partialArrayMath (Array& res, const Array& a, const IPosition& collapseAxes, const ArrayFunctorBase& funcObj) { ReadOnlyArrayIterator aiter(a, collapseAxes); IPosition shape(a.shape().removeAxes (collapseAxes)); res.resize (shape); RES* data = res.data(); while (!aiter.pastEnd()) { *data++ = funcObj(aiter.array()); aiter.next(); } } template void boxedArrayMath (Array& result, const Array& array, const IPosition& boxShape, const ArrayFunctorBase& funcObj) { const IPosition& shape = array.shape(); size_t ndim = shape.size(); IPosition fullBoxShape, resShape; fillBoxedShape (shape, boxShape, fullBoxShape, resShape); result.resize (resShape); assert(result.contiguousStorage()); RES* res = result.data(); // Loop through all data and assemble as needed. IPosition blc(ndim, 0); IPosition trc(fullBoxShape-1); while (true) { *res++ = funcObj (array(blc,trc)); size_t ax; for (ax=0; ax= shape[ax]) { trc[ax] = shape[ax]-1; } break; } blc[ax] = 0; trc[ax] = fullBoxShape[ax]-1; } if (ax == ndim) { break; } } } template void slidingArrayMath (Array& result, const Array& array, const IPosition& halfBoxShape, const ArrayFunctorBase& funcObj, bool fillEdge) { const IPosition& shape = array.shape(); size_t ndim = shape.size(); IPosition boxEnd, resShape; bool empty = fillSlidingShape (shape, halfBoxShape, boxEnd, resShape); if (fillEdge) { result.resize (shape); result = RES(); } else { result.resize (resShape); } if (!empty) { Array resa (result); if (fillEdge) { IPosition boxEnd2 (boxEnd/2); resa.reference (resa(boxEnd2, resShape+boxEnd2-1)); } typename Array::iterator iterarr(resa.begin()); // Loop through all data and assemble as needed. IPosition blc(ndim, 0); IPosition trc(boxEnd); IPosition pos(ndim, 0); while (true) { *iterarr = funcObj (array(blc,trc)); ++iterarr; size_t ax; for (ax=0; ax // ArrayIteratorError // void ArrayPositionIterator::setup(size_t byDim) { if (byDim > ndim()) { throw(ArrayIteratorError("ArrayPositionIterator::ArrayPositionIterator" " - Stepping by dimension > Array dimension")); } IPosition cursorAxes(byDim); for (size_t i=0; i 0) { int ax = iterationAxes[0]; atOrBeyondEnd = End[ax] < Start[ax]; } else { atOrBeyondEnd = Shape.nelements() == 0 || Shape[0] == 0; } } bool ArrayPositionIterator::atStart() const { // Too expensive - we should set variables in next/previous return Cursor == Start; } // // ArrayIteratorError // void ArrayPositionIterator::next() { nextStep(); } void ArrayPositionIterator::set (const IPosition& cursorPos) { bool all = false; if (cursorPos.nelements() != iterationAxes.nelements()) { all = true; if (cursorPos.nelements() != ndim()) { throw ArrayIteratorError ("ArrayPositionIterator::set - " "length of cursorPos is invalid"); } } atOrBeyondEnd = false; for (size_t i=0; i= 0) { Cursor[axis] = cursorPos[i]; if (Cursor[axis] > End[axis]) { atOrBeyondEnd = true; } } } } size_t ArrayPositionIterator::nextStep() { // This could and should be made more efficient. // next will step past the end (as it needs to for pastEnd to trigger). // Short circuit if we are iterating by the same dimensionality // as the array. if (iterationAxes.nelements() == 0){ atOrBeyondEnd = true; Cursor = End; return ndim(); } // Increment the cursor. int axis = 0; for (size_t i=0; i Iterate an IPosition through the shape of an Array
        // // // // ArrayPositionIterator manipulates an IPosition "cursor" through some // volume defined by an origin and shape. This position can in turn be // used to index into, or otherwise define a position in, an Array. Normally // users won't use this class directly, rather they will use an ArrayIterator, // VectorIterator or MatrixIterator object, which in turn uses this class. // ArrayPositionIterator is also used in the implementation of Array. // // // template void verySlowArrayCopy(Array &to, const Array &from) // { // if (! to.conform(from)) { // // throw some error // } // ArrayPositionIterator toiter(to.shape(), to.origin(),0); // ArrayPositionIterator fromiter(from.shape(), from.origin(),0); // // If to.origin() == from.origin() we only need one iterator // // or we could offset positions by the difference in origins. // // The "0" means we are stepping by scalars. // while (! toiter.pastEnd()) { // we know arrays conform // to(toiter.pos()) = fromiter(fromiter.pos()); // toiter.next(); fromiter.next(); // } // } // // // Iteration can be done by any combination of axes, but it can only be // done for full axes. //
        The iteration step always "fills up" its dimensionality. // E.g., if we are stepping through a cube by matrices, the matrix completely // fills up the plane. // Casacore class ArrayLattice in the lattices // package can be used to iterate with partial volumes. // //

        // ArrayPositionIterator also serves as the base class of ArrayIterator. // Function makeIterator in class ArrayBase can be used to make an // ArrayIterator without having to know the template type. Function // getArray in this class can be used to obtain the current // contents of the cursor as an ArrayBase object. // class ArrayPositionIterator { public: // Define the shape and origin of the volume the cursor will step // through. Also define the dimensionality of the step. byDim==0 implies // we are stepping by scalars (i.e. every element), byDim==1 implies that // we are stepping by vector, ==2 by matrices, and so on. // If uses the first byDim axes as the cursor volume and it steps // through the remaining axes. // ArrayPositionIterator(const IPosition &shape, const IPosition &origin, size_t byDim); ArrayPositionIterator(const IPosition &shape, size_t byDim); // // Step through an array using the given axes. // The axes can be given in two ways: //

          //
        1. axesAreCursor=true means that the axes form the cursor axes. // The remaining axes will form the iteration axes. // This is the default. //
        2. axesAreCursor=false means the opposite. // In this case the iteration axes can be given in any order. //
        // E.g. when using iteration axes 2,0 for an array with shape [5,3,7], each // iteration step returns a cursor (containing the data of axis 1). // During the iteration axis 2 will vary most rapidly (as it was // given first). //
        E.g. for a shape of [3,4,5,6] and cursor axes [2,0], the cursor size // is [3,5] (axes 0 and 2), while the iteration is done over axes 1 and 3 // (1 the fastest varying one). ArrayPositionIterator(const IPosition &shape, const IPosition &axes, bool axesAreCursor=true); virtual ~ArrayPositionIterator() {}; // Reset the cursor to the beginning of the volume. // virtual void reset(); void origin() { reset(); } // // Returns true of the cursor is at the origin. bool atStart() const; // Returns true if the cursor has moved past the end of its volume. bool pastEnd() const; // Return the position of the cursor. // This include all axes const IPosition &pos() const {return Cursor;} // Return the end position of the cursor. IPosition endPos() const; // Advance the cursor to its next position. virtual void next(); // Set the cursor to the given position. // The position can only contain the iteration axes or it can be the full // position. //
        In the first case the position must to be given in the order // of the iteration axes as given in the constructor. // In the latter case the position must be given in natural order // (as given by function pos and only the cursor axes are taken // into account. virtual void set (const IPosition& cursorPos); // What is the dimensionality of the volume we are iterating through? size_t ndim() const; // Return the iteration axes. const IPosition &iterAxes() const {return iterationAxes;} // Return the cursor axes. const IPosition &cursorAxes() const {return cursAxes;} // Get the array in the cursor. // This is only implemented in the derived ArrayIterator class. // By default it throws an exception. virtual ArrayBase& getArray(); protected: // Advance cursor to its next position and tell which dimension stepped. size_t nextStep(); // What is the dimensionality of the "step" the cursor takes, i.e. // 0 for scalars, 1 for vector, .... size_t dimIter() const {return cursAxes.nelements();} private: // Setup the object for the constructor. // void setup(size_t byDim); void setup(const IPosition &axes, bool axesAreCursor); // //# We should probably have mf's for getting at Start,Shape and End. IPosition Start, Shape, End, Cursor; bool atOrBeyondEnd; IPosition cursAxes, iterationAxes; }; // Dimensionality of the array we are iterating through. inline size_t ArrayPositionIterator::ndim() const { return Start.nelements(); } // We are at the "end" if we cannot advance any more. inline bool ArrayPositionIterator::pastEnd() const { return atOrBeyondEnd; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Arrays/ArrayStr.h000066400000000000000000000123561476623553700174230ustar00rootroot00000000000000#ifndef CASACORE_ARRAYSTR_H #define CASACORE_ARRAYSTR_H #include "Array.h" #include #include namespace casacore { // Write out an ascii representation of an array of any dimensionality. // Arrays of dimensionality 3 or greater are written out vector by vector, // preceeded by the position of the start of the vector. If the origin of // the array isn't zero it is printed. The shape of the array is always // printed. template std::ostream &operator << (std::ostream &, const Array &); // Read an ascii representation of an array. All types with an << // operator can be handled. The basic format of the input should be: // // [element element element ....] // // Elements are separated by whitespace, or a comma, optionally surrounded // by white space.
        // Some input routines read fields between blank spaces. This // is (at the moment) especially true for Quantities and Strings. // In those cases // the separator should be blank (or a comma following a blank), and the // end ']' should have a blank in front. // A crude fix for String arrays having separators , and ] // without blanks preceding has been made; but slows routines down // The default input is a vector of unspecified length. The input shape // can be changed by pre-pending the input with: // // {[shape]} // // where shape is an unsigned integer vector. The shape will be used to check // the input length; and, depending on the possibility, to resize/reshape the // result. However, reshaping of e.g. a Vector to a Matrix cannot be done, and // the result will stay in the form asked.
        // Input order is row major, however by preceding the input with: // // {T[shape]} // // the order will be reversed.
        // Reshaping of the Array provided will depend on the type of Array and its // state. If a general Array, the shape will be // as defined by user. If fixed Array (e.g. Matrix, Vector, Cube) the number // of dimesnsions will be kept. If the user specified more dimensions // then supported (e.g. 3 for Matrix), the last dimesions will be collapsed. // If less dimensions are specified, the missing ones will be set to 1. // will be kept.
        // The read() version can be used to force a shape (ip), or an input // transpose (it) (which can be undone by the user specifying transpose). // // template std::istream &operator>> (std::istream &s, Array &x); template bool read(std::istream &s, Array &x, const IPosition *ip=0, bool it=false); // // General read support function for matrices. // In principle these functions will not be // used by general user, but could be. They can be used by Array type // classes (like Slice, Lattice) to do the work of comparable input // functions as the one for Arrays. // In these functions p is the shape // of the returned Block x. This shape is either deduced from the user // specification; made equal to (1, nelements) if no user shape is // given; is set to ip if specified. The function will return false (and // p = (0)) in the case of an invalid input element; a number of elements // input not equal to ip (if specified); the shape given by user as input // does not conform to ip (if given) or the number of elements input.
        // trans will be true if transpose asked by user; or if forced by it. template bool readArrayBlock(std::istream &s, bool &trans, IPosition &p, std::vector &x, const IPosition *ip=0, bool it=false); // // Global functions for Matrix/Vector input/output using ASCII format. // // // //
      • Matrix //
      • Vector // // // These global functions support file I/O between ASCII files and // Matrices or Vectors. // // // // Matrix picture(256, 256); picture = 0.0; // String fileName="picture.data"; // // // operations to populate picture // // ... // // writeAsciiMatrix (picture, fileName); // // // // Array Ascii IO -- Simple Ascii input/output for Arrays. // // // These routines read and write a Matrix of data. The first line of // input will be examined to determine the number of columns in the matrix. // The maximum number of columns provided for is 100. Each item may be up // to 50 characters long. // // Each item must be separated from others by one (or more) blank column. // The "line" may be up to 1024 characters long. Each subsequent line must // contain the SAME number of items as the first line but may be any length // (up to 1024 characters). // // The matrix need NOT be square. // // The matrix should be declared but NOT dimensioned in the calling program. // template void readAsciiMatrix (Matrix& mat, const char* fileName); template void writeAsciiMatrix (const Matrix& mat, const char* fileName); // template std::string to_string(const Array array); } #include "ArrayStr.tcc" #endif casacore-3.7.1/casa/Arrays/ArrayStr.tcc000066400000000000000000000352231476623553700177430ustar00rootroot00000000000000#ifndef CASACORE_ARRAYSTR_TCC #define CASACORE_ARRAYSTR_TCC #include "Array.h" #include "ArrayStr.h" #include "ArrayPosIter.h" #include #include #include #include namespace casacore { template std::string to_string(const Array array) { std::ostringstream str; str << array; return str.str(); } // Take care that a uChar is printed numerically, not as a letter. inline void ArrayIO_printValue (std::ostream& s, const unsigned char& v) { s << int(v); } template inline void ArrayIO_printValue (std::ostream& s, const T& v) { s << v; } template std::ostream &operator<<(std::ostream &s, const Array &a) { // Print any "header" information if (a.ndim() > 2) { s << "Ndim=" << a.ndim() << " "; } if (a.ndim() > 1) { s << "Axis Lengths: " << a.shape() << " "; } if (a.nelements() == 0) { s << "[]"; return s; } // Then print the values - if (a.ndim() == 1) { // Vector IPosition ipos(1); s << "["; long long iend = a.shape()(0) - 1; for (long long i=0; i < iend; i++) { ipos(0) = i; casacore::ArrayIO_printValue (s, a(ipos)); s << ", "; } ipos(0) = iend; casacore::ArrayIO_printValue (s, a(ipos)); s << "]"; } else if (a.ndim() == 2) { // Matrix s << " (NB: Matrix in Row/Column order)\n"; IPosition index(2); long long row_end = a.shape()(0) - 1; long long col_end = a.shape()(1) - 1; for (long long i=0; i <= row_end; i++) { index(0) = i; if (i == 0) { s << "["; } else { s << " "; } for (long long j=0; j <= col_end; j++) { index(1) = j; casacore::ArrayIO_printValue (s, a(index)); if (j != col_end) { s << ", "; } } if (i != row_end) { s << '\n'; } else { s << "]\n"; } } } else { // Any dimension - print by vectors, preceed each vector by IPosition // of start. s << '\n'; IPosition ashape = a.shape(); int andim = a.ndim(); ArrayPositionIterator ai(ashape, 1); int i; IPosition index(andim); // Print vector by vector while(! ai.pastEnd()) { index = ai.pos(); s << index; s << "["; for(i=0; i < ashape(0); i++) { index(0) = i; if (i > 0) s << ", "; casacore::ArrayIO_printValue (s, a(index)); } s << "]\n"; ai.next(); } } return s; } // These functions allow the user to read /write raw binary data files // created with the Array class to / from disk, so that they can be // viewed, for example, with SAOimage. // // They should eventually be replaced with something more sophisticated. template void write_array (const Array& the_array, const std::string& fileName) { size_t nbytes = the_array.nelements() * sizeof(T); std::ofstream outfile(fileName, std::ios::out); if(!outfile) { throw (ArrayError ("write_array error: could not open file " + fileName)); } std::vector storage = the_array.tovector (); outfile.write ((char*)storage.data(), nbytes); outfile.close(); } template void read_array(Array& the_array, const std::string& fileName) { size_t nbytes = the_array.nelements() * sizeof(T); std::ifstream infile(fileName, std::ios::in); if(!infile) { throw (ArrayError ("read_array error: could not open file " + fileName)); } bool delete_storage; T *storage = the_array.getStorage (delete_storage); infile.read ((char*)storage, nbytes); infile.close(); the_array.putStorage (storage, delete_storage); } template void readAsciiMatrix (Matrix& mat, const char* filein) { const size_t bufSize = 1024; char buf[bufSize]; std::fill_n(buf, bufSize, 0); const size_t numberSize = 50; char buf2[numberSize]; std::fill_n(buf2, numberSize, 0); size_t blockSize = 100; std::vector temp(blockSize); std::ifstream iFile; iFile.open (filein, std::ios::in); if (! iFile) { throw (ArrayError ("readAsciiFile: cannot open " + std::string(filein))); } size_t rows = 0, cols = 0, saveCols = 0; size_t havePoint = 0; while (iFile.getline(buf, bufSize)) { size_t blankLine = 1; for (size_t j1=0;j1 (int(blockSize) - int(saveCols) - 10)) { blockSize *= 2; temp.resize(blockSize); } rows += 1; cols = 0; size_t ch = 0; bool startedNew = false; for (size_t i2=0; i2 0) { buf2[ch] = ' '; // istringstream(buf2,sizeof(buf2)) >> temp[havePoint]; std::istringstream(buf2) >> temp[havePoint]; havePoint += 1; ch = 0; } startedNew = false; if (buf[i2] == '\0') break; } if (buf[i2] != ' ' && !startedNew) { cols += 1; startedNew = true; } if (startedNew) buf2[ch++] = buf[i2]; } if (rows == 1) saveCols = cols; else if (cols != saveCols) { throw ArrayError("Array is not regular. Number of elements was " + std::to_string(saveCols) + " at row 1" + " but is " + std::to_string(cols) + " at row " + std::to_string(rows)); } } } iFile.close(); mat.resize(rows, cols); size_t k3 = 0; for (size_t i3=0;i3 void writeAsciiMatrix (const Matrix& mat, const char* fileout) { std::ofstream oFile; oFile.precision(12); oFile.open (fileout, std::ios::out); if (! oFile) { throw (ArrayError ("writeAsciiFile: cannot open " + std::string(fileout))); } for (size_t i1=0;i1 void readAsciiVector (Vector& vect, const char* filein) { const size_t bufSize = 1024; char buf[bufSize]; std::fill_n(buf, bufSize, 0); const size_t numberSize = 50; char buf2[numberSize]; std::fill_n(buf2, numberSize, 0); size_t blockSize = 100; std::vector temp(blockSize); std::ifstream iFile; iFile.open (filein, std::ios::in); if (! iFile) { throw (ArrayError ("readAsciiFile: cannot open " + std::string(filein))); } size_t havePoint = 0; while (iFile.getline(buf, bufSize)) { size_t blankLine = 1; for (size_t j1=0;j1 0) { buf2[ch] = ' '; if (int(havePoint) > (int(blockSize) - 2)) { blockSize *= 2; temp.resize(blockSize); } std::istringstream(buf2) >> temp[havePoint]; havePoint +=1; if (buf[i2] == ' ') ch = 0; } if (buf[i2] == '\0') break; else startedNew = false; } if (buf[i2] != ' ' && !startedNew) startedNew = true; if (startedNew) buf2[ch++] = buf[i2]; } } } iFile.close(); vect.resize(havePoint); size_t k3 = 0; for (size_t i3=0;i3 void writeAsciiVector (const Vector& vect, const char* fileout) { size_t rows = vect.size(); std::ofstream oFile; oFile.precision(12); oFile.open (fileout, std::ios::out); if (! oFile) { throw (ArrayError ("writeAsciiFile: cannot open " + std::string(fileout))); } for (size_t i1=0;i1 std::istream &operator >> (std::istream &s, Array &x) { if (!read(s, x, 0, false)) { s.clear(std::ios::failbit | s.rdstate()); } return s; } template bool read(std::istream &s, Array &x, const IPosition *ip, bool it) { /// Array *to; PCAST(to, Array, &x); /// if (to) { // If an empty array, it can get any dimension. if (x.ndim() == 0) { std::vector tmp; bool tr; IPosition p; if (!readArrayBlock(s, tr, p, tmp, ip, it)) return false; x.resize(p); size_t iptr = p.nelements() - 1; IPosition iter(p); iter = int(0); for (size_t i=0; i < x.nelements(); i++) { x(iter) = tmp[i]; if (!tr) { for (int j=iptr; j >=0; j--) { iter(j) += 1; if (iter(j) < p(j)) break; iter(j) = 0; } } else { for (size_t j=0; j <=iptr; j++) { iter(j) += 1; if (iter(j) < p(j)) break; iter(j) = 0; } } } } else { // Otherwise try if we can resize. // This will always be possible for an Array, // but e.g. not for Vector if the Array is not 1D. Array tx; if (!read(s, tx, ip, it)) return false; // TODO this can be done without trial and error: // reading a matrix in always throws and catches an exception // in the current implementation, which should not be the // normal flow. try { x.resize (tx.shape()); x.assign_conforming(tx); } catch (std::runtime_error&) { IPosition first; IPosition last; if (x.ndim() >= tx.ndim()) { first = tx.shape(); last = IPosition(x.ndim() - tx.ndim()); last = int(1); } else { first = tx.shape().getFirst(x.ndim() - 1); last = IPosition(1, tx.shape(). getLast(tx.ndim() - x.ndim() + 1).product()); } IPosition tot(x.ndim()); tot.setFirst(first); tot.setLast(last); x.resize(tot); IPosition p(x.shape()); size_t iptr = p.nelements() - 1; IPosition iter(p); iter = int(0); bool deleteIt; const T *tmp = tx.getStorage(deleteIt); for (size_t i=0; i < tx.nelements(); i++) { x(iter) = tmp[i]; for (size_t j=0; j<=iptr; j++) { iter(j) += 1; if (iter(j) < p(j)) break; iter(j) = 0; } } tx.freeStorage(tmp, deleteIt); } } return true; } template bool readArrayBlock(std::istream &s, bool &trans, IPosition &p, std::vector &x, const IPosition *ip, bool it) { if (!s.good()) { s.clear(std::ios::failbit|s.rdstate()); // Redundant if using GNU iostreams return false; } bool how = true; T r; char ch; size_t cnt = 0; p.resize(0); if (ip == 0) { p = IPosition(0); } else { p = *ip; } trans = it; s >> std::ws; s.get(ch); if (ch == '{') { s >> std::ws; s.get(ch); if (ch == 't' || ch == 'T') { trans = (!trans); s >> std::ws; s.get(ch); } if (ch != '}') { s.putback(ch); bool lpt; IPosition lpp, lpq; std::vector lpx; if (!readArrayBlock(s, lpt, lpp, lpx, 0, false) || lpp.nelements() != 1) { how = false; } else { lpq.resize(lpp(0)); for (int i=0; i> std::ws; s.get(ch); if (ch != '}') { how = false; } else { s >> std::ws; s.get(ch); } } } // end start shape // The following is done to circumvent the problem arising from the fact that // the output of a string array is given as [ abc, def], but the string >> // will read all characters between blanks. Proper string I/O handling // would solve this. Lines with /// were added/deleted if (how && ch != '[') { s.putback(ch); s >> r; if (!s.good()) { s.clear(std::ios::failbit|s.rdstate()); // Redundant if using GNU iostreams how = false; } else { if (x.size() <= cnt) { x.resize(2*x.size() + 1); } x[cnt] = r; cnt++; } } else { std::string st; /// std::string sts; /// while (how) { s >> std::ws; s.get(ch); if (ch == ',') { s >> std::ws; } else if (ch == ']' || (int)ch == EOF) { break; } else { s.putback(ch); } if (std::is_base_of::value) { /// all of this extra s >> st; /// Read string bool hasEither = st.find_first_of(",]")!=std::string::npos; if (hasEither) { size_t ix; while ((ix = st.find(',')) != std::string::npos) { sts = st.substr(0, ix); std::istringstream ins(sts); ins >> r; st = st.substr(ix+1); if (x.size() <= cnt) { x.resize(2*x.size() + 1); } x[cnt] = r; cnt++; } if ((ix = st.find(']')) != std::string::npos) { sts = st.substr(0, ix); std::istringstream ins(sts); ins >> r; st = st.substr(ix); if (x.size() <= cnt) { x.resize(2*x.size() + 1); } x[cnt] = r; cnt++; for (int i1=st.length()-1; i1>=0; i1--) { /// set back s.putback(st[i1]); /// } /// break; } for (int i1=st.length()-1; i1>=0; i1--) { /// set back s.putback(st[i1]); /// } /// } else { std::istringstream ins(st); /// Necessary for template ins >> r; /// expansion if (x.size() <= cnt) { x.resize(2*x.size() + 1); } x[cnt] = r; cnt++; } if (!s.good()) { s.clear(std::ios::failbit|s.rdstate()); // Redundant if using GNU iostreams how = false; } } else { s >> r; if (!s.good()) { s.clear(std::ios::failbit|s.rdstate()); // Redundant if using GNU iostreams how = false; } else { if (x.size() <= cnt) { x.resize(2*x.size() + 1); } x[cnt] = r; cnt++; } } } } if (how) { if (p.nelements() == 0) { p = IPosition(1, cnt); } else if ((long long)(cnt) != p.product()) { how = false; } } if (!how) { s.clear(std::ios::failbit); p.resize(0); } return how; } } #endif casacore-3.7.1/casa/Arrays/ArrayUtil.h000066400000000000000000000232131476623553700175620ustar00rootroot00000000000000//# ArrayUtil.h: Utility functions for arrays //# Copyright (C) 1995,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_ARRAYUTIL_2_H #define CASA_ARRAYUTIL_2_H //# Includes #include "Vector.h" #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Split a std::string into its elements. // // // //
      • Vector //
      • std::string // // // stringToVector converts a std::string to a Vector of Strings. // // // The function stringToVector splits a string into its elements // using the given delimiter and returns them in a Vector. // The default delimiter is a comma (,). // It is very useful when using a function taking a vector of strings // as shown in the example. //

        // A more advanced way of splitting a string is by using a // regular expression as delimiter. // It makes it, for example, possible to treat whitespace around a comma // as part of the delimiter (as shown in an example below). //

        // A string with length 0 results in a zero-length vector. // // // As shown in the example, the function stringToVector makes // passing a Vector of Strings far easier. // // // // someFunction (stringToVector ("abc,def ,,gh")); // // This results in a vector with 4 elements containing the values // "abc", "def ", "", and "gh". The vector is passed to someFunction. // This is far easier than having to do it as: // // Vector vector(4); // vector(0) = "abc"; // vector(1) = "def "; // vector(2) = ""; // vector(3) = "gh"; // someFunction (vector); // // // The following example shows how to use a delimiter consisting of a comma // surrounded by possible whitespace. // // Vector result = stringToVector (source, Regex(" *, *")); // // // Vector strToVector (const std::string& string, char delim = ','); Vector strToVector (const std::string& string, const std::regex& delim); // //

        // Concatenate two Arrays. // // // //
      • Array // // // concatenateArray concatenates two Arrays into a new Array. // // // The function concatenates two Arrays into a new Array. // The shape of both arrays must match except for the last dimension. // The shape of the resulting array is equal to that of the input // arrays with its last dimension as the sum of both last dimensions. //

        // An exception ArrayConformanceError is thrown when the shapes // do not match. // // // The table system needed this function. // // // // Vector vector1(5); // Vector vector2(10); // indgen (vector1); // fill with values 0..4 // indgen (vector2); // fill with values 0..9 // Vector result = concatenateVector (vector1, vector2); // // The example above results in a vector with length 15 and values // 0,1,2,3,4,0,1,2,3,4,5,6,7,8,9. //

        // It can also be used with matrices or arrays with higher dimensionality // as long as all dimensions but the last one have equal length. // // Matrix matrix1 (3,4); // Matrix matrix2 (3,5); // Matrix matrix3 (4,4); // // Concatenation of matrix1 and matrix 2 will succeed and result // // in a 3x9 matrix. // Matrix matrixConc = concatenateArray (matrix1, matrix2); // if (matrixConc.shape() != IPosition(2,3,9)) { // cout << "Error in shape of concatenated matrices" << endl; // } // // Concatenation of matrix1 and matrix3 will fail, because the // // first dimensions have a different length (3 vs. 4). // try { // concatenateArray (matrix1, matrix2); // } catch (ArrayConformanceError x) { // cout << x.what() << endl; // } // // // template Array concatenateArray (const Array& left, const Array& right); // //

        Helper function for partialX functions // // // This is a specialized helper function for functions like partialSums. // It determines the shape of the resulting array and calculates the // result increments when iterating linearly through the source array. // It returns the first result axis which indicates the number of the first // contiguous collapse axes. The number of contiguous data points is // returned in nelemCont. // // size_t partialFuncHelper (int& nelemCont, IPosition& resultShape, IPosition& incr, const IPosition& sourceShape, const IPosition& collapseAxes); // // // Reverse the order of one or more axes of an array. // // // // // This function makes it possible to reverse one or more axes of an array by // swapping around the elements of each axis. // The resulting array is a copy of the input array with its data // moved around according to the new order. // If the order does not change, a copy is returned if the // alwaysCopy is true. Otherwise a reference of the // input array is returned. // // // Reversing axis 0 of a Vector means that the Vector is reversed. // Reversing axis 1 of a Matrix means that its rows are reversed. // Reversing axis 0 of an N-dim array means that the elements of each Vector // in that array are reversed. // // template Array reverseArray (const Array& array, const IPosition& reversedAxes, bool alwaysCopy = true); template Array reverseArray (const Array& array, size_t axis, bool alwaysCopy = true); // // // Reorder the axes of an array. // // // // // This function makes it possible to reorder the axes of an array. // The resulting array is a copy of the input array with its data // moved around according to the new array order. // If the order does not change, a copy is returned if the // alwaysCopy is true. Otherwise a reference of the // input array is returned. //

        // The newAxisOrder defines the new axes order. // Its length can be less than the dimensionality of the input array. // It is appended with the non-specified axes in their natural order. // newAxisOrder(i) gives the axis in the original array // which will now get axis i. // // // // Array result = reorderArray (someArray, IPosition(2,1,3)); // // Say that someArray is a 4D array with shape [3,4,5,6]. // The non-specified axes get appended to the axis order // specification [1,3] resulting in [1,3,0,2]. //
        This means that axis 1 gets axis 0, axis 3 gets axis 1, axis 0 gets // axis 2, and axis 2 gets axis 3. // Thus the resulting shape is [4,6,3,5] and the data are moved accordingly. //
        // // This function was needed for an efficient implementation of the // functions partialMedians and partialFractiles. // // template Array reorderArray (const Array& array, const IPosition& newAxisOrder, bool alwaysCopy = true); // //

        // Helper function for function reorderArray. // // // // // This is a specialized helper function for function reorderArray. // It determines the shape of the resulting array and calculates the // result increments when iterating linearly through the source array. // It returns the number of the first non-reordered axes. // // // Split off common non-templated code. // // size_t reorderArrayHelper (IPosition& newShape, IPosition& incr, const IPosition& shape, const IPosition& newAxisOrder); // } //# NAMESPACE CASACORE - END #include "ArrayUtil.tcc" #endif casacore-3.7.1/casa/Arrays/ArrayUtil.tcc000066400000000000000000000133421476623553700201060ustar00rootroot00000000000000//# ArrayUtil.cc: Utility functions for arrays (templated) //# Copyright (C) 1995,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_ARRAYUTIL_2_TCC #define CASA_ARRAYUTIL_2_TCC #include "ArrayUtil.h" #include "ArrayError.h" #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template Array concatenateArray (const Array& left, const Array& right) { if (left.nelements() == 0) { return right.copy(); } if (right.nelements() == 0) { return left.copy(); } IPosition shape = right.shape(); IPosition leftShape = left.shape(); std::size_t ndim = shape.nelements(); if (! shape.isEqual (leftShape, ndim-1)) { throw (ArrayConformanceError ("concatenateArray(left,right)")); } shape(ndim-1) += leftShape(ndim-1); Array result (shape); IPosition start(ndim, 0); result (start, leftShape-1).assign_conforming( left ); start(ndim-1) = leftShape(ndim-1); result (start, shape-1).assign_conforming( right ); return result; } template Array reorderArray (const Array& array, const IPosition& newAxisOrder, bool alwaysCopy) { const IPosition& shape = array.shape(); IPosition newShape, incr; size_t contAxes = reorderArrayHelper (newShape, incr, shape, newAxisOrder); // If not reordered, we can simply return the array (or a copy if needed). size_t ndim = shape.nelements(); if (contAxes == ndim) { if (alwaysCopy) { return array.copy(); } return array; } Array result(newShape); bool deleteData, deleteRes; const T* arrData = array.getStorage (deleteData); const T* data = arrData; T* resData = result.getStorage (deleteRes); T* res = resData; // Find out the nr of contiguous elements. size_t nrcont = 1; if (contAxes == 0) { contAxes = 1; } else { for (size_t i=0; i 1) { std::copy_n(data, nrcont, res); data += nrcont; res += nrcont; } else { for (size_t i=0; i Array reverseArray (const Array& array, size_t axis, bool alwaysCopy) { const IPosition& shape = array.shape(); if (axis >= shape.size()) { throw ArrayError( std::string(__FUNCTION__) + ": axis number is higher than number of axes in the array" ); } bool nothingToDo = shape[axis] == 1; if (nothingToDo) { if (alwaysCopy) { return array.copy(); } return array; } bool deletein, deleteout; const T *indata = array.getStorage(deletein); Array result(shape); T *outdata = result.getStorage(deleteout); size_t outerProduct = 1; size_t innerProduct = 1; for (size_t i=0; iaxis) { outerProduct *= shape[i]; } } for (size_t j=0; j Array reverseArray (const Array& array, const IPosition& reversedAxes, bool alwaysCopy) { const IPosition& shape = array.shape(); bool nothingToDo = true; for (size_t i=0; i= int(shape.size())) { throw ArrayError(std::string(__FUNCTION__) + ": axis number " + std::to_string(reversedAxes[i]) + " is higher than number of axes in the array" ); } if (shape[reversedAxes[i]] > 1) { nothingToDo = false; break; } } if (nothingToDo) { if (alwaysCopy) { return array.copy(); } return array; } Array result = array.copy(); for (size_t i=0; i strToVector (const std::string& str, char delim) { if (str.empty()) { return Vector(0); } size_t nr = std::count(str.begin(), str.end(), delim); Vector vec(nr+1); size_t st = 0; nr = 0; size_t i; for (i=0; i strToVector (const std::string& string, const std::regex& delim) { std::vector stdvec; const char* s = string.c_str(); int sl = string.length(); if (sl != 0) { const char* pos = s; std::cmatch results; bool match = std::regex_search (s, s+sl, results, delim); while (match) { stdvec.emplace_back(std::string (pos, results[0].first)); pos = results[0].first + results[0].length(); match = std::regex_search (pos, s+sl, results, delim); } stdvec.emplace_back( std::string (pos, s + sl - pos) ); } return Vector(stdvec); } size_t partialFuncHelper (int& nelemCont, IPosition& resultShape, IPosition& incr, const IPosition& sourceShape, const IPosition& collapseAxes) { int ndim = sourceShape.nelements(); // Get the remaining axes. // It also checks if axes are specified correctly. IPosition resultAxes = IPosition::otherAxes (ndim, collapseAxes); size_t nres = resultAxes.nelements(); // Create an array which determines how to increment in the result // when incrementing an axis of the input array. incr.resize (ndim); incr = 0; // Find out how many contiguous elements are available. size_t stax = ndim; nelemCont = 1; // Create the resulting shape and array. if (nres == 0) { resultShape.resize (1); resultShape = 1; nelemCont = sourceShape.product(); } else { resultShape.resize (nres); int nr = 1; int lastAxis = -2; for (size_t i=0; i; template class Array; template class Array; template class Array; template class Array; template class Array; template class Array; template class Array; template class Array; template class Array; } casacore-3.7.1/casa/Arrays/AxesMapping.cc000066400000000000000000000074551476623553700202320ustar00rootroot00000000000000//# AxesMapping.cc: Info about mapping array axes to another order //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include "AxesMapping.h" #include "Slicer.h" #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN AxesMapping::AxesMapping() : itsRemoved (false), itsReordered (false) {} AxesMapping::AxesMapping (const IPosition& oldToNew) : itsToNew (oldToNew), itsToOld (oldToNew.nelements(), -1), itsRemoved (false), itsReordered (false) { int naxes = itsToNew.nelements(); size_t nnew = 0; for (int i=0; i= naxes) throw std::runtime_error("itsToNew(i) >= naxes"); itsToOld(itsToNew(i)) = i; nnew++; } } for (size_t i=0; i 0 && itsToOld(i) < itsToOld(i-1)) { itsReordered = true; } } itsToOld.resize (nnew); } IPosition AxesMapping::posToNew (const IPosition& pos) const { size_t naxes = itsToNew.nelements(); assert (pos.nelements()==naxes); IPosition newpos(itsToOld.nelements()); for (size_t i=0; i // Info about mapping array axes to another order. //
      • // // // // //
      • IPosition //
      • AxesSpecifier // // // AxesMapping holds the information about mapping axes to another order. // It can be constructed by AxesSpecifier // by applying a shape to the axes specification. //
        AxesMapping is thereafter used to map positions, shapes, and // slices to the new axes or backwards. // // Shapes and positions are both represented by class IPosition. // However, they have to be treated differently in this class, // because removed axes for a position have value 0, while for a // shape they have value 1. Hence there are different functions for them // and the user has to take care that the correct function is called. // //
        // // // // // // // The class encapsulates the mapping functionality. // It is meant as a helper class for casacore's SubLattice. // //# //# class AxesMapping { public: // The default constructor creates empty maps. AxesMapping(); // Construct it with the mapping from old to new axes order. // A value of -1 means that the old axes is ignored in the new one. // Another value gives the new axis number. //
        It determines if axes are removed and/or reordered. explicit AxesMapping (const IPosition& oldToNew); // Are axes removed? bool isRemoved() const { return itsRemoved; } // Is the axes order reordered? bool isReordered() const { return itsReordered; } // Get the mapping of old->new. // The length of the resulting IPosition is the dimensionality of // the original lattice. A value of -1 indicates that the corresponding // axis in the original lattice is removed. // Another value is the axis number in the new lattice, const IPosition& getToNew() const { return itsToNew; } // Get the mapping of new->old. // The length of the resulting IPosition is the dimensionality of // the new lattice. Its values give the axes in the original lattice. const IPosition& getToOld() const { return itsToOld; } // Map an old position to the new one. // In debug-mode it checks if the removed axes have position 0 // in the input position. IPosition posToNew (const IPosition& pos) const; // Map a new position or shape to the old one. IPosition posToOld (const IPosition& pos) const; // Map an old shape to the new one. // In debug-mode it checks if the removed axes have length 1 // in the input shape. IPosition shapeToNew (const IPosition& shape) const; // Map a new position or shape to the old one. IPosition shapeToOld (const IPosition& shape) const; // Map an old shape to the new one. // In debug-mode it checks if the removed axes have length 1 // in the input slicer. Slicer slicerToNew (const Slicer& slicer) const; // Map a new position or shape to the old one. Slicer slicerToOld (const Slicer& slicer) const; private: IPosition itsToNew; IPosition itsToOld; bool itsRemoved; bool itsReordered; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Arrays/AxesSpecifier.cc000066400000000000000000000062171476623553700205430ustar00rootroot00000000000000//# AxesSpecifier.cc: Specification of axes to keep or remove //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include "AxesSpecifier.h" #include namespace casacore { //# NAMESPACE CASACORE - BEGIN AxesSpecifier::AxesSpecifier() : itsKeep (true) {} AxesSpecifier::AxesSpecifier (bool keepDegenerate) : itsKeep (keepDegenerate) {} AxesSpecifier::AxesSpecifier (bool keepDegenerate, const IPosition& axisPath) : itsPath (axisPath), itsKeep (keepDegenerate) {} AxesSpecifier::AxesSpecifier (const IPosition& keepAxes) : itsAxes (keepAxes), itsKeep (false) {} AxesSpecifier::AxesSpecifier (const IPosition& keepAxes, const IPosition& axisPath) : itsAxes (keepAxes), itsPath (axisPath), itsKeep (false) {} AxesMapping AxesSpecifier::apply (const IPosition& shape) const { // Find the axes to be kept. // It also checks the keepAxes specification. IPosition keepAxes; size_t nrnew; if (itsKeep) { nrnew = shape.nelements(); keepAxes = IPosition::otherAxes (nrnew, IPosition()); } else { // First determine which axes have to be always kept. // To remove degenerate axes we use two passes // First find out how many axes have to be kept. int naxes = shape.nelements(); keepAxes.resize (naxes, false); keepAxes = 0; for (size_t i=0; i= naxes) throw std::runtime_error("itsAxes(i) >= naxes"); keepAxes(itsAxes(i)) = 1; } // Now remove degenerate axes. nrnew = 0; for (int i=0; inew. // -1 means that the axis is not used. IPosition axisToNew(shape.nelements(), -1); for (size_t i=0; i // Specification of axes to keep or remove // // // // // //
      • IPosition // // // AxesSpecifier makes it possible to specify which axes should // be used in a shape. Degenerate axes (i.e. axes with length 0) // can be thrown away which makes it possible to reduce the // dimensionality of an array. All degenerate axes can be thrown // away, but one can also specify which ones should be kept. //

        // Another option of this class is to reorder the axes, thus to // make the axes of a lattice appear in a different order. // This can be useful when two images with diferent axes orders // have to be combined. //

        // When an AxesSpecifier has to be used for a lattice, the lattice's // shape has to be applied to the AxesSpecifier. The result is // a AxesMapping object. // This object is (for example) used internally in the // casacore SubLattice class to know how // to map the axes form the original lattice to the sublattice. // // Reordering axes is not supported (yet) by the other Casacore classes // like Lattices and Images. // // // // This example tells that all degenerate axes have to be kept. // The axes are reordered to 1,0,2. Thus the first and second axes are // swapped. // // AxesSpecifier spec(true, IPosition(3,1,0,2)); // AxesMapping map = spec.apply (IPosition(3,4,1,5)); // AlwaysAssertExit (map.posToNew (IPosition(3,2,0,3)) == IPosition(3,0,2,3)); // AlwaysAssertExit (map.posToOld (IPosition(3,0,2,3)) == IPosition(3,2,0,3)); // // The following specification would have the same effect, because the // unspecified axes are kept in their natural order. // AxesSpecifier spec(true, IPosition(1,1)); // // // The same example as above, but now degenerated axes are removed. // Note that because the second axis is removed, the third axis now // get the second axis, thus gets swapped with the first axis. //
        Also note the difference between the functions posToOld // and shapeToOld. // // AxesSpecifier spec(false, IPosition(1,1)); // AxesMapping map = spec.apply (IPosition(3,4,1,5)); // AlwaysAssertExit (map.posToNew (IPosition(3,2,0,3)) == IPosition(2,3,2)); // AlwaysAssertExit (map.posToOld (IPosition(3,3,2)) == IPosition(3,2,0,3); // AlwaysAssertExit (map.shapeToOld (IPosition(3,3,2)) == IPosition(3,2,1,3); // //
        //# //# class AxesSpecifier { public: // The default constructor keeps all axes. AxesSpecifier(); // Tell if no or all degenerate axes have to be removed. explicit AxesSpecifier (bool keepDegenerate); // Tell if no or all degenerate axes have to be removed. //
        The argument axisPath makes it possible to specify in // which order the KEPT axes have to be used. Unspecified axes are // appended to the end. It gives a means to reorder the axes of a lattice. //
        E.g. for a 4-dim lattice axisPath [2,0] means axis order [2,0,1,3]. explicit AxesSpecifier (bool keepDegenerate, const IPosition& axisPath); // Tell which (degenerate) axes have to be kept. // Non-degenerate axes will always be kept. explicit AxesSpecifier (const IPosition& keepAxes); // The argument keepAxes tells which degenerate axes have // to be kept. Non-degenerate axes will always be kept. //
        The argument axisPath makes it possible to specify in // which order the KEPT axes have to be used. Unspecified axes are // appended to the end. It gives a means to reorder the axes of a lattice. //
        E.g. for a 4-dim lattice axisPath [2,0] means axis order [2,0,1,3]. AxesSpecifier (const IPosition& keepAxes, const IPosition& axisPath); // Apply the specification to a shape. // It returns an AxesMapping // object which takes care of mapping old to new axes order. AxesMapping apply (const IPosition& shape) const; // Are we keeping all degenerate axes ? bool keep() const {return itsKeep;}; private: IPosition itsAxes; IPosition itsPath; bool itsKeep; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Arrays/Changes-May-2020.txt000066400000000000000000000157511476623553700207230ustar00rootroot00000000000000Casacore Arrays underwent a large refactoring operation around May 2020. (Andre Offringa) This file contains a short summary of the changes and the changes that might be required for Casacore 'users', in particular for Casa. While the changes were done with as little implications as possible, they do require changes. == A summary of the changes == * The Array directory is now completely independent of all other casa headers: it can be compiled and tested by itself, and doesn't include any other casacore header. * An Array now supports moving its elements. Therefore, it can now contain uncopyable classes like std::unique_ptr * Move constructors and move assignments were added to the most relevant types * Removed the use of CountedPtr, used unique_ptr instead. * I've added iterator constructors (i.e. Array(begin, end)), and removed constructors taken a 'Block'. * I've for now removed the option to skip initialization. I doubt the way it is done was standard C++ compliant due to rules of type aliasing -- and it is in any case not always allowed. This also didn't seem used anywhere in casacore. * I've removed the allocators from Array. They were not used anywhere in casacore, and at least to me highly confusing. Instead, I've added the possibility for C++STL-like allocators, i.e. you can define something like an Array. It wasn't hard to add, but not sure if ever useful. * I've added support for initializer lists, like Vector a{1,2,3} and IPosition ip{2,5}. * Added fast swap operators for some of the classes like IPosition and Array. * Added to_string() implementations for Array, IPosition. * I rewrote the Array tests using Boost unit testing platform. This makes the tests more debuggable. * Array-exceptions are now based on std::runtime_error instead of AipsError. * Casacore's "Uppercase-types" like Int, Char, Bool etc. were replaced by their lower-case C++ counterparts. Array indices now use size_t instead of uInt. Use of String was replaced by std::string, use of Regex was replaced by std::regex. * There were many other small things like sorting and medium calculation that had manual implementation, which I replaced by calls to equivalent std calls. * Add final and override to methods/classes where appropriate The corresponding pull request is https://github.com/casacore/casacore/pull/1012. A new testset was added (tCpp11Features.cc), which is also a demonstration of the new features available. == Changes required to CASA == === Headers === * By far the most significant change is that Arrays does not include files outside the Arrays directory anymore. Previously, headers from e.g. BasicMath, BasicSL and Utilities were immediately included when (almost) any header from Arrays was used. In particular missing math headers that define operators can cause confusing compilations errors that seem to state that parameters don't match, where the actual cause is just that the correct overload is not included. * Also internally inside the Arrays directory less headers are included, and e.g. Arrays/Array.h no longer includes Arrays/Vector.h. * Include header 'casa/Arrays/ArrayIO.h' no longer exist. Array string operations are now in Arrays/ArrayStr.h, which gets automatically included from Arrays/Array.h. The logging and AipsIO operations (like '<<') are now in casa/IO/ArrayIO.h , as well as stringToVector() (latter should be deprecated over strToVector() ). * Include header 'casa/Arrays/LogiArrayFwd.h' does not exist anymore. Include ArrayFwd.h instead. * Generally, fewer std:: items are included in the casacore namespace because fewer of the headers like casa/vector.h are included. While these can be included, I recommend adding 'std::' where possible instead. === Type changes === * Because the template parameters for several Arrays classes (Array, Vector, Matrix, ..) changed, forward declarations such as `template class Vector;` are not correct. Instead, header casa/Arrays/ArrayFwd.h can be included instead. NB: despite that template parameters changed, declarations like 'Array a;' still continue to work of course. * Array classes now throw exceptions of type std::runtime_error or derived types. This means that a catch(AipsError) won't catch such errors, and should instead be written as catch(std::exception). I suggest a search-replace for these catch statements, or the removal of AipsError altogether. * Sizes of arrays etc. are now of type 'size_t' instead of UInt. This can at times cause overload mismatch and therefore compilation error when e.g. the result of Array::nelements() is a parameter to a function. NB: The format of Arrays on disk did of course not change. === Function changes === * `ArrayBase::makeIterator` returns a std::unique_ptr instead of a CountedPtr. Since a unique_ptr can be converted to a shared_ptr, which in turn converts to a CountedPtr, statements like Countedtr<..> a = arr.makeIterator() can be changed to CountedPtr<..> a(arr.makeIterator()), although I would advise to use unique_ptr over CountedPtr if possible. * String returning functions such as `IPosition::toString()` now return a std::string instead of a String. In some cases this requires explicit casts or changing casts. * Because a new overload of the Array was added (taking two iterators), compilation errors can arise for constructors calls that specify two parameters that did not match exactly. For example: Array goodplane(IPosition(4, nx,ny,1,1), 0.0); causes a compilation error, because 0.0 is a double. The compiler then has to chose between two overloads that don't match exactly, Array(IPosition, float) and Array(template, template), and refuses to do this. This can therefore be rewritten as Array goodplane(IPosition(4, nx,ny,1,1), 0.0f); (Despite the triviality of the mismatch, this unfortunately can cause long, highly confusing compilation errors). * Constructor 'Array(const IPosition &shape, ArrayInitPolicy initPolicy)' was removed. To skip initialization, a new constructor was added, which can be called like this: Array arr(shape, Array::uninitialized); or Vector arr(length, Array::uninitialized); This constructor is only available for trivial types, as it is undefined behaviour to not initialize a non-trivial type. The constructor optionally takes an allocator as third parameter, like many of the Array constructors have now. At the time of writing Casacore itself does not make use of the non-initializing constructor, but Casa may. === Related to Block === * There is no longer a Vector constructor that takes a Block. There is however a begin/end iterator constructor now, and statements such as `Vector a(block);` can be rewritten as `Vector a(block.begin(), block.end());` * Method `Array::toBlock(block)` was removed. Instead, the global method `makeBlock(array)` can be used instead. Therefore, this: Vector namlst = ...; Block nl; namlst.toBlock(nl); Can be rewritten as: #include ... Vector namlst = ...; Block nl = makeBlock(namlst); casacore-3.7.1/casa/Arrays/Cube.h000066400000000000000000000275451476623553700165400ustar00rootroot00000000000000//# Cube.h: A 3-D Specialization of the Array Class //# Copyright (C) 1993,1994,1995,1996,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_CUBE_2_H #define CASA_CUBE_2_H #include "Array.h" namespace casacore { //#Begin casa namespace //

        A 3-D Specialization of the Array class // // // // Cube objects are three-dimensional specializations (e.g., more convenient // and efficient indexing) of the general Array class. You might also want // to look at the Array documentation to see inherited functionality. // // Generally the member functions of Array are also available in // Cube versions which take a pair of integers where the array // needs an IPosition. Since the Cube // is three-dimensional, the IPositions are overkill, although you may // use those versions if you want to. // // Cube ci(100,100,100); // Shape is 100x100 // ci.resize(50,50,50); // Shape now 50x50 // // // Slices may be taken with the Slice class. To take a slice, one "indexes" // with one Slice(start, length, inc) for each axis, where end and inc // are optional. Additionally, there is an xyPlane() // member function which return a Matrix which corresponds to some plane: // // Cube cube(10,20,30); // for(size_t i=0; i < 30; i++) { // cube.xyPlane(i) = i; // Set every 10x20 plane to its "height" // } // // // Element-by-element arithmetic and logical operations are available (in // aips/ArrayMath.h and aips/ArrayLogical.h). // // As with the Arrays, if the preprocessor symbol AIPS_DEBUG is // defined at compile time invariants will be checked on entry to most // member functions. Additionally, if AIPS_ARRAY_INDEX_CHECK is defined // index operations will be bounds-checked. Neither of these should // be defined for production code. template class Cube : public Array { public: // A Cube of length zero in each dimension; zero origin. Cube(); // A l1xl2xl3 sized cube. // Fill it with the initial value. Cube(size_t l1, size_t l2, size_t l3, const T &initialValue=T()); // An uninitialized l1xl2xl3 sized cube. Cube(size_t l1, size_t l2, size_t l3, typename Array::uninitializedType); // A Cube where the shape ("len") is defined with IPositions. // Fill it with the initial value. Cube(const IPosition &length, const T &initialValue = T()); // An uninitialized Cube where the shape ("len") is defined with IPositions. Cube(const IPosition& length, typename Array::uninitializedType); // The copy constructor uses reference semantics. Cube(const Cube &); Cube(Cube &&); // Construct a cube by reference from "other". "other must have // ndim() of 3 or less. The warning which applies to the copy constructor // is also valid here. Cube(const Array &); Cube(Array &&); // Create an Cube of a given shape from a pointer. Cube(const IPosition &shape, T *storage, StorageInitPolicy policy = COPY); // Create an Cube of a given shape from a pointer. Because the pointer // is const, a copy is always made. Cube(const IPosition &shape, const T *storage); // Resize to the given shape. // Resize without argument is equal to resize(0,0,0). // using Array::resize; void resize(size_t nx, size_t ny, size_t nz, bool copyValues=false); // // Copy the values from other to this cube. If this cube has zero // elements then it will resize to be the same shape as other; otherwise // other must conform to this. // Note that the assign function can be used to assign a // non-conforming cube. // Cube &operator=(const Cube& source) { Array::operator=(source); return *this; } Cube &operator=(Cube&& source) { Array::operator=(std::move(source)); return *this; } Cube& operator=(const Array& source) { // TODO is it highly confusing that operator= is specialized for Cube, e.g. // this is allowed: // Cube cube(5,1,1); // Vector v(5,0); // cube = v; // But this is not: // Array arr(IPosition{5,1,1}); // Vector v(5,0); // arr = v; // If it should be allowed to assign from dim(5,1,1) to dim(5), this should // be supported already by the Array class so that the semantics are the // same! if (source.ndim() == 3) { Array::operator=(source); } else { // This might work if a.ndim == 1 or 2 (*this) = Cube(source); } return *this; } Cube& operator=(Array&& source) { if (source.ndim() == 3) { Array::operator=(std::move(source)); } else { (*this) = Cube(std::move(source)); } return *this; } // // Copy val into every element of this cube; i.e. behaves as if // val were a constant conformant cube. Array &operator=(const T &val) { return Array::operator=(val); } // Copy to this those values in marray whose corresponding elements // in marray's mask are true. // TODO //Cube &operator= (const MaskedArray &marray) // { Array (*this) = marray; return *this; } // Single-pixel addressing. If AIPS_ARRAY_INDEX_CHECK is defined, // bounds checking is performed. // T &operator()(const IPosition &i) { return Array::operator()(i); } const T &operator()(const IPosition &i) const { return Array::operator()(i); } T &operator()(size_t i1, size_t i2, size_t i3) { return this->begin_p[index(i1, i2, i3)]; } const T &operator()(size_t i1, size_t i2, size_t i3) const { return this->begin_p[index(i1, i2, i3)]; } // // Take a slice of this cube. Slices are always indexed starting // at zero. This uses reference semantics, i.e. changing a value // in the slice changes the original. // // Cube vd(100,100,100); // //... // vd(Slice(0,10),Slice(10,10,Slice(0,10))) = -1.0; // sub-cube set to -1.0 // // Cube operator()(const Slice &sliceX, const Slice &sliceY, const Slice &sliceZ); const Cube operator()(const Slice &sliceX, const Slice &sliceY, const Slice &sliceZ) const; // // Slice using IPositions. Required to be defined, otherwise the base // class versions are hidden. // Array operator()(const IPosition &blc, const IPosition &trc, const IPosition &incr) { return Array::operator()(blc,trc,incr); } const Array operator()(const IPosition &blc, const IPosition &trc, const IPosition &incr) const { return Array::operator()(blc,trc,incr); } Array operator()(const IPosition &blc, const IPosition &trc) { return Array::operator()(blc,trc); } const Array operator()(const IPosition &blc, const IPosition &trc) const { return Array::operator()(blc,trc); } Array operator()(const Slicer& slicer) { return Array::operator()(slicer); } const Array operator()(const Slicer& slicer) const { return Array::operator()(slicer); } // // The array is masked by the input LogicalArray. // This mask must conform to the array. // // Return a MaskedArray. const MaskedArray operator() (const LogicalArray &mask) const { return Array::operator() (mask); } // Return a MaskedArray. MaskedArray operator() (const LogicalArray &mask) { return Array::operator() (mask); } // // The array is masked by the input MaskedLogicalArray. // The mask is effectively the AND of the internal LogicalArray // and the internal mask of the MaskedLogicalArray. // The MaskedLogicalArray must conform to the array. // // Return a MaskedArray. const MaskedArray operator() (const MaskedLogicalArray &mask) const { return Array::operator() (mask); } // Return a MaskedArray. MaskedArray operator() (const MaskedLogicalArray &mask) { return Array::operator() (mask); } // // Extract a plane as a matrix referencing the original data. // Of course you could also use a Matrix // iterator on the cube. // Matrix xyPlane(size_t zplane); const Matrix xyPlane(size_t zplane) const; Matrix xzPlane(size_t yplane); const Matrix xzPlane(size_t yplane) const; Matrix yzPlane(size_t xplane); const Matrix yzPlane(size_t xplane) const; // // The length of each axis of the cube. const IPosition &shape() const { return this->length_p; } void shape(int &s1, int &s2, int &s3) const { s1 = this->length_p(0); s2=this->length_p(1); s3=this->length_p(2); } // The number of rows in the Cube, i.e. the length of the first axis. size_t nrow() const { return this->length_p(0); } // The number of columns in the Cube, i.e. the length of the 2nd axis. size_t ncolumn() const { return this->length_p(1); } // The number of planes in the Cube, i.e. the length of the 3rd axis. size_t nplane() const { return this->length_p(2); } // Checks that the cube is consistent (invariants check out). virtual bool ok() const override; protected: virtual void preTakeStorage(const IPosition &shape) override; // Remove the degenerate axes from other and store result in this cube. // An exception is thrown if removing degenerate axes does not result // in a cube. virtual void doNonDegenerate(const Array &other, const IPosition &ignoreAxes) override; size_t fixedDimensionality() const override { return 3; } private: // Cached constants to improve indexing. //size_t xinc_p, yinc_p, zinc_p; // Helper fn to calculate the indexing constants. //void makeIndexingConstants(); size_t xinc() const { return this->inc_p(0); } size_t yinc() const { return this->inc_p(1)*this->originalLength_p(0); } size_t zinc() const { return this->inc_p(2)*this->originalLength_p(0)*this->originalLength_p(1); } size_t index(size_t i1, size_t i2, size_t i3) const { return xinc()*i1 + this->originalLength_p(0)*(this->inc_p(1)*i2 + this->inc_p(2)*this->originalLength_p(1)*i3); } size_t index_continuous(size_t i1, size_t i2, size_t i3) const { return i1 + this->originalLength_p(0)*(this->inc_p(1)*i2 + this->inc_p(2)*this->originalLength_p(1)*i3); } }; } //#End casa namespace #include "Cube.tcc" #endif casacore-3.7.1/casa/Arrays/Cube.tcc000066400000000000000000000202141476623553700170440ustar00rootroot00000000000000//# Cube.cc: A 3-D Specialization of the Array Class //# Copyright (C) 1993,1994,1995,1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_CUBE_2_TCC #define CASA_CUBE_2_TCC #include "Cube.h" #include "Matrix.h" #include "Slice.h" #include "MaskedArray.h" #include "ArrayError.h" namespace casacore { template Cube::Cube() : Array(IPosition(3, 0)) { assert(ok()); } template Cube::Cube(size_t l1, size_t l2, size_t l3, const T &initialValue) : Array(IPosition(3, l1, l2, l3), initialValue) { assert(ok()); } template Cube::Cube(size_t l1, size_t l2, size_t l3, typename Array::uninitializedType) : Array(IPosition(3, l1, l2, l3), Array::uninitialized) { assert(ok()); } template Cube::Cube(const IPosition &length, const T &initialValue) : Array(length, initialValue) { this->checkBeforeResize(length); } template Cube::Cube(const IPosition &length, typename Array::uninitializedType) : Array(length, Array::uninitialized) { this->checkBeforeResize(length); } template Cube::Cube(const Cube &source) : Array(source) { assert(ok()); } template Cube::Cube(Cube&& source) : Array(std::move(source), IPosition(3, 0)) { assert(ok()); } // // ArrayNDimError // template Cube::Cube(const Array &source) : Array(source) { this->checkCubeShape(); assert(ok()); } template Cube::Cube(Array&& source) : Array(std::move(source)) { this->checkCubeShape(); assert(ok()); } template void Cube::resize(size_t nx, size_t ny, size_t nz, bool copyValues) { assert(ok()); IPosition l(3); l(0) = nx; l(1) = ny; l(2) = nz; Cube::resize(l, copyValues); } // // ArrayError // template Cube Cube::operator()(const Slice &sliceX, const Slice &sliceY, const Slice &sliceZ) { assert(ok()); long long b1, l1, s1, b2, l2, s2, b3,s3,l3; // begin length step if (sliceX.all()) { b1 = 0; l1 = this->length_p(0); s1 = 1; } else { b1 = sliceX.start(); l1 = sliceX.length(); s1 = sliceX.inc(); } if (sliceY.all()) { b2 = 0; l2 = this->length_p(1); s2 = 1; } else { b2 = sliceY.start(); l2 = sliceY.length(); s2 = sliceY.inc(); } if (sliceZ.all()) { b3 = 0; l3 = this->length_p(2); s3 = 1; } else { b3 = sliceZ.start(); l3 = sliceZ.length(); s3 = sliceZ.inc(); } // Check that the selected slice is valid if (s1 < 1 || s2 < 1 || s3 < 1) { throw(ArrayError("Cube::operator()(Slice,Slice,Slice) : step < 1")); } else if (l1 < 0 || l2 < 0 || l3 < 0) { throw(ArrayError("Cube::operator()(Slice,Slice,Slice): length < 0")); } else if ((b1+(l1-1)*s1 >= this->length_p(0)) || (b2+(l2-1)*s2 >= this->length_p(1)) || (b3+(l3-1)*s3 >= this->length_p(2))) { throw(ArrayError("Cube::operator()(Slice,Slice,Slice) : " "Desired slice extends beyond the end of the array")); } else if (b1 < 0 || b2 < 0 || b3 < 0) { throw(ArrayError("Cube::operator()(Slice,Slice,Slice) : " "start of slice before beginning of cube")); } // For simplicity, just use the Array slicing. If this is found to be // a performance drag, we could special case this as we do for Vector. IPosition blc(3,b1,b2,b3); IPosition trc(3,b1+(l1-1)*s1,b2+(l2-1)*s2,b3+(l3-1)*s3); IPosition incr(3,s1,s2,s3); return this->operator()(blc,trc,incr); } template const Cube Cube::operator() (const Slice &sliceX, const Slice &sliceY, const Slice &sliceZ) const { return const_cast*>(this)->operator() (sliceX, sliceY, sliceZ); } template void Cube::doNonDegenerate (const Array &other, const IPosition &ignoreAxes) { Array tmp(*this); tmp.nonDegenerate (other, ignoreAxes); if (tmp.ndim() != 3) { throw (ArrayError ("Cube::nonDegenerate (other, ignoreAxes) - " "removing degenerate axes from other " "does not result in cube")); } this->reference (tmp); } // // ArrayConformanceError // template Matrix Cube::xyPlane(size_t which) { assert(ok()); if ((long long)(which) >= this->length_p(2)) { throw(ArrayConformanceError("Cube::xyPlane - plane > end")); } Cube tmp((*this)(Slice(), Slice(), which)); tmp.ndimen_p = 2; tmp.length_p.resize (2); tmp.inc_p.resize (2); tmp.originalLength_p.resize (2); tmp.makeSteps(); return Matrix(tmp); // should match Matrix(const Array &) } template const Matrix Cube::xyPlane(size_t which) const { Cube *This = const_cast*>(this); // Cast away constness, but the return type is a const Matrix, so // this should still be safe. return This->xyPlane(which); } template Matrix Cube::xzPlane(size_t which) { assert(ok()); if ((long long)(which) >= this->length_p(1)) { throw(ArrayConformanceError("Cube::xzPlane - plane > end")); } Cube tmp((*this)(Slice(), which, Slice())); // Keep axes 0 and 2, even if they have length 1. return tmp.nonDegenerate(IPosition(2,0,2)); } template const Matrix Cube::xzPlane(size_t which) const { Cube *This = const_cast*>(this); // Cast away constness, but the return type is a const Matrix, so // this should still be safe. return This->xzPlane(which); } template Matrix Cube::yzPlane(size_t which) { assert(ok()); if ((long long)(which) >= this->length_p(0)) { throw(ArrayConformanceError("Cube::yzPlane - plane > end")); } Cube tmp((*this)(which, Slice(), Slice())); // Keep axes 1 and 2, even if they have length 1. return tmp.nonDegenerate(IPosition(2,1,2)); } template const Matrix Cube::yzPlane(size_t which) const { Cube *This = const_cast*>(this); // Cast away constness, but the return type is a const Matrix, so // this should still be safe. return This->yzPlane(which); } template bool Cube::ok() const { #ifndef NDEBUG if(this->ndim() != 3) throw std::runtime_error("ndim() == " + std::to_string(this->ndim())); #endif return this->ndim() == 3 && Array::ok(); } template Cube::Cube(const IPosition &shape, T *storage, StorageInitPolicy policy) : Array(shape, storage, policy) { if(shape.nelements() != 3) throw ArrayError("len.nelements() != 3"); } template Cube::Cube(const IPosition &shape, const T *storage) : Array(shape, storage) { if(shape.nelements() != 3) throw ArrayError("len.nelements() != 3"); } template void Cube::preTakeStorage(const IPosition &shape) { Array::preTakeStorage(shape); if(shape.nelements() != 3) throw ArrayError("len.nelements() != 3"); } } //#End casa namespace #endif casacore-3.7.1/casa/Arrays/ElementFunctions.h000066400000000000000000000217311476623553700211330ustar00rootroot00000000000000#ifndef ELEMENT_FUNCTIONS_H #define ELEMENT_FUNCTIONS_H #include #include #include namespace casacore { namespace arrays_internal { inline bool near(unsigned val1, unsigned val2, double tol) { if (tol <= 0) { return (val1 == val2); } if (val1 == val2) { return true; } else if (val1 > val2) { return (double(val1-val2) <= tol*std::max(val1,val2)); } else { return (double(val2-val1) <= tol*std::max(val1,val2)); } } inline bool near(int val1, int val2, double tol) { if (tol <=0) { return (val1 == val2); } if (val1 == val2) { return true; } if ((0::min()); } else if (val2 == 0) { return (std::abs(val1) <= (1+tol)*std::numeric_limits::min()); } if ((0::min()); } else if (val2 == 0) { return (std::abs(val1) <= (1+tol)*std::numeric_limits::min()); } if ((0 &val1, const std::complex &val2, double tol=1.0e-5) { if (tol <= 0) return val1 == val2; if (val1 == val2) return true; if (near(val1.real(), val2.real(), tol) && near(val1.imag(), val2.imag(), tol)) return true; float aval1(std::abs(val1)), aval2(std::abs(val2)); if (aval1 == 0) return aval2 <= (1+tol)*std::numeric_limits::min(); else if (aval2 == 0) return aval1 <= (1+tol)*std::numeric_limits::min(); std::complex dval(val1); dval -= std::complex(val2); return std::abs(dval) <= tol * (aval1 < aval2 ? aval2 : aval1); } inline bool near(const std::complex &val1, const std::complex &val2, double tol=1.0e-13) { if (tol <= 0) return val1 == val2; if (val1 == val2) return true; if (std::abs(val1) == 0) return std::abs(val2) <= (1+tol)*std::numeric_limits::min(); else if (std::abs(val2) == 0) return std::abs(val1) <= (1+tol)*std::numeric_limits::min(); double aval1(std::abs(val1)), aval2(std::abs(val2)); return std::abs(val1-val2) <= tol * (aval1 < aval2 ? aval2 : aval1); } inline bool nearAbs(const std::complex &val1, const std::complex &val2, double tol=1.0e-5) { return std::abs(val2 - val1) <= tol; } inline bool nearAbs(const std::complex &val1, const std::complex &val2, double tol=1.0e-13) { return std::abs(val2 - val1) <= tol; } inline bool nearAbs(unsigned val1, unsigned val2, double tol) { if (val1 == val2) { return true; } else if (val1 > val2) { return (tol >= double(val1 - val2)); } else { return (tol >= double(val2 - val1)); } } inline bool nearAbs(int val1, int val2, double tol) { return (tol >= double(std::abs(val2 - val1))); } inline bool nearAbs(float val1, float val2, double tol) { return (tol >= double(std::abs(val2 - val1))); } inline bool nearAbs(double val1, double val2, double tol) { return (tol >= std::abs(val2 - val1)); } // Define a function to compare all elements of two sequences. // It returns true if all elements compare true. // An example compare operator is std::equal_to. // template inline bool compareAll (InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, CompareOperator op) { for (; first1!=last1; ++first1, ++first2) { if (!op(*first1, *first2)) return false; } return true; } // For use with a constant left value. // This avoids use of bind1st or bind2nd which can fail for gcc-4.3. // (see ArrayMath.h). template inline bool compareAllLeft (InputIterator1 first1, InputIterator1 last1, T left, CompareOperator op) { for (; first1!=last1; ++first1) { if (!op(left, *first1)) return false; } return true; } // For use with a constant right value. // This avoids use of bind1st or bind2nd which can fail for gcc-4.3. // (see ArrayMath.h). template inline bool compareAllRight (InputIterator1 first1, InputIterator1 last1, T right, CompareOperator op) { for (; first1!=last1; ++first1) { if (!op(*first1, right)) return false; } return true; } // // Define a function to compare all elements of two sequences. // It returns true if any element compares true. // An example compare operator is std::equal_to. // template inline bool compareAny (InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, CompareOperator op) { for (; first1!=last1; ++first1, ++first2) { if (op(*first1, *first2)) return true; } return false; } // For use with a constant left value. // This avoids use of bind1st or bind2nd which can fail for gcc-4.3. // (see ArrayMath.h). template inline bool compareAnyLeft (InputIterator1 first1, InputIterator1 last1, T left, CompareOperator op) { for (; first1!=last1; ++first1) { if (op(left, *first1)) return true; } return false; } // For use with a constant right value. // This avoids use of bind1st or bind2nd which can fail for gcc-4.3. // (see ArrayMath.h). template inline bool compareAnyRight (InputIterator1 first1, InputIterator1 last1, T right, CompareOperator op) { for (; first1!=last1; ++first1) { if (op(*first1, right)) return true; } return false; } // // Functor to add squared diff of right and base value to left. // It can be used to calculate the variance. // Note: it is specialized for complex values to handle real and imag separately. template struct SumSqrDiff { explicit SumSqrDiff(T base) : itsBase(base) {} Accum operator() (Accum left, T right) const { return left + (right-itsBase)*(right-itsBase); } private: Accum itsBase; // store as Accum, so subtraction results in Accum }; // Specialize for complex values. // Variance has to be taken for the absolute value of a complex value. thus // sum(abs((a[i] - mean)**2 // where the sqrt used in abs and the **2 cancel each other, thus can be left out. // See also https://en.wikipedia.org/wiki/Complex_random_variable#Variance // Note that although the sum is real, a complex value is used to have equal template types. template struct SumSqrDiff> { explicit SumSqrDiff(std::complex base) : itsBase(base) {} std::complex operator() (std::complex left, std::complex right) const { return left + ((right.real() - itsBase.real()) * (right.real() - itsBase.real()) + (right.imag() - itsBase.imag()) * (right.imag() - itsBase.imag())); } private: std::complex itsBase; }; template bool isnan(const std::complex &val) { return std::isnan(val.real()) || std::isnan(val.imag()); } template bool isinf(const std::complex &val) { return std::isinf(val.real()) || std::isinf(val.imag()); } template bool isfinite(const std::complex &val) { return std::isfinite(val.real()) || std::isfinite(val.imag()); } inline int floormod (int x, int y) { int r = x%y; if (r != 0 && (x<0) != (y<0)) r+=y; return r; } inline long long floormod (long long x, long long y) { long long r = x%y; if (r != 0 && (x<0) != (y<0)) r+=y; return r; } inline float floormod (float x, float y) { float r = std::fmod(x,y); if (r != 0 && (x<0) != (y<0)) r+=y; return r; } inline double floormod (double x, double y) { double r = std::fmod(x,y); if (r != 0 && (x<0) != (y<0)) r+=y; return r; } template inline void convertScalar (T& out, F in) { out = static_cast(in); } inline void convertScalar (std::complex& out, std::complex in) { out = std::complex(in.real(), in.imag()); } } } #endif casacore-3.7.1/casa/Arrays/ExtendSpecifier.cc000066400000000000000000000115471476623553700210740ustar00rootroot00000000000000//# ExtendSpecifier.cc: Specification of new and stretched lattice axes //# Copyright (C) 2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include "ExtendSpecifier.h" #include "Slicer.h" #include "ArrayError.h" #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ExtendSpecifier::ExtendSpecifier() {} ExtendSpecifier::ExtendSpecifier (const IPosition& oldShape, const IPosition& newShape, const IPosition& newAxes, const IPosition& stretchAxes) : itsOldShape (oldShape), itsNewShape (newShape), itsNewAxes (newAxes), itsStretchAxes (stretchAxes) { size_t nrdim = newShape.nelements(); // Check if axes are given correctly. std::unique_ptr flags(new bool[nrdim]); std::fill_n(flags.get(), nrdim, false); fill (flags.get(), nrdim, newAxes); // Make the mapping of new axes to old axes. IPosition newToOld(nrdim, -1); size_t nrold = 0; for (size_t i=0; i= nrdim) { throw ArrayError ("ExtendSpecifier - no axes remaining"); } itsExtendAxes.resize (nrext); itsOldOldAxes.resize (nrdim - nrext); itsOldNewAxes.resize (nrdim - nrext); nrext = 0; nrold = 0; // Fill the old axes (i.e. new nor stretched) in the old and new shape. // Check if those axes have the same length. // Check if stretched axes have length 1 in the old shape. for (size_t i=0; i= 0 && oldShape(newToOld(i)) != 1) { throw ArrayError ("ExtendSpecifier - length of stretched axis > 1"); } itsExtendAxes(nrext++) = i; } else { itsOldOldAxes(nrold) = newToOld(i); itsOldNewAxes(nrold++) = i; if (newShape(i) != oldShape(newToOld(i))) { throw ArrayError ("ExtendSpecifier - lengths of old axis mismatch"); } } } } void ExtendSpecifier::fill (bool* flags, size_t nrdim, const IPosition& axes) const { for (size_t i=0; i= ssize_t(nrdim)) { throw ArrayError ("ExtendSpecifier - invalid axis given (<0 or >=nrdim)"); } if (flags[axis]) { throw ArrayError ("ExtendSpecifier - axis multiply specified"); } flags[axis] = true; } } Slicer ExtendSpecifier::convert (IPosition& shape, const Slicer& section) const { size_t nrdim = itsNewShape.nelements(); assert (nrdim == section.ndim()); size_t nrr = nrdim - itsNewAxes.nelements(); size_t nrold = itsOldOldAxes.nelements(); // Create a Slicer for the section without the new axes. // Copy the blc,trc,stride for the old axes. // This means we have to create a Slicer for those axes only. IPosition blc(nrr, 0); IPosition len(nrr, 1); IPosition inc(nrr, 1); shape.resize (nrdim); shape = 1; for (size_t j=0; j // Specification of new and stretched lattice axes // // // // // //
      • IPosition // // // ExtendSpecifier is a class internally used by casacore class // ExtendLattice. // It holds the information which axes are stretched and which axes // are new. Note that a stretched axis has to have length 1 in the // original shape. //

        // The class only contains the functionality needed by ExtendLattice. // which are (mainly) 2 conversion functions. One function converts // a slicer from the extended lattice to the original lattice, so // ExtendLattice can read the correct data. // The other function converts a shape in the original lattice to the // shape in the extended lattice. //
        Some data is precalculated for more efficient processing // of the conversion of slicers and shapes. // // // // IPosition oldShape(4,10,1,3,1); // IPosition newShape(5,10,1,5,3,8); // ExtendSpecifier spec (oldShape, newShape, IPosition(1,2), IPosition(1,4)); // // This example extends the old shape to the new shape. //
        The 3rd argument tells that axes 2 is new. The newShape tells that // its length will be 5. Note that adding this axis means that axes 2 // in the old shape will get axes 3 in the new shape. //
        The 4th argument tells that axes 4 (in the new shape!!) is stretched // (to 8 according to newShape). //
        //# //# class ExtendSpecifier { public: // Default constructor generates empty IPositions. ExtendSpecifier(); // Tell if no or all degenerate axes have to be removed. ExtendSpecifier (const IPosition& oldShape, const IPosition& newShape, const IPosition& newAxes, const IPosition& stretchAxes); // Return the new shape. const IPosition& newShape() const { return itsNewShape; } // Return the new axes. const IPosition& newAxes() const { return itsNewAxes; } // Return the axes to be stretched. const IPosition& stretchAxes() const { return itsStretchAxes; } // Return the old shape. const IPosition& oldShape() const { return itsOldShape; } // Return the axes to be extended (i.e. new and stretch axes). const IPosition& extendAxes() const { return itsExtendAxes; } // Return the old axes (i.e. axes new nor stretched) as in old shape. const IPosition& oldOldAxes() const { return itsOldOldAxes; } // Return the old axes as in new shape. const IPosition& oldNewAxes() const { return itsOldNewAxes; } // Convert the slicer to the specification for the old shape. // It fills shape with the shape to reform the section // length such that it contains the new axes. Slicer convert (IPosition& shape, const Slicer& section) const; // Convert a shape to the specification for the new shape. IPosition convertNew (const IPosition& oldShape) const; private: // Fill the flags for the given axes. // It throws an exception if the axis is invalid or multiply given. void fill (bool* flags, size_t nrdim, const IPosition& axes) const; IPosition itsOldShape; IPosition itsNewShape; IPosition itsNewAxes; IPosition itsStretchAxes; IPosition itsExtendAxes; IPosition itsOldOldAxes; IPosition itsOldNewAxes; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Arrays/IPosition.cc000066400000000000000000000675721476623553700177410ustar00rootroot00000000000000//# IPosition.cc: A vector of integers, used to index into arrays. //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include "IPosition.h" #include "ArrayError.h" #include "Array.h" #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN IPosition::IPosition (size_t length) : size_p (length), data_p (buffer_p) { if (length > BufferLength) { allocateBuffer(); } } IPosition::IPosition(std::initializer_list list) : size_p (list.size()), data_p (buffer_p) { if (list.size() > BufferLength) { allocateBuffer(); } std::initializer_list::const_iterator list_iter = list.begin(); size_t i = 0; while(list_iter != list.end()) { data_p[i] = *list_iter; ++list_iter; ++i; } } IPosition::IPosition (const Array &other) : size_p (0), data_p (buffer_p) { if (other.size() > 0) { if (other.ndim() != 1) { throw(ArrayError("IPosition::IPosition(const Array &other) - " "other is not one-dimensional")); } fill (other.size(), other.begin()); } assert(ok()); } IPosition::IPosition (const Array &other) : size_p (0), data_p (buffer_p) { if (other.size() > 0) { if (other.ndim() != 1) { throw(ArrayError("IPosition::IPosition(const Array &other) - " "other is not one-dimensional")); } fill (other.size(), other.begin()); } assert(ok()); } IPosition::~IPosition() { if (data_p != &buffer_p[0]) { delete [] data_p; } } void IPosition::allocateBuffer() { if (size_p <= BufferLength) { data_p = buffer_p; } else { data_p = new ssize_t[size_p]; } assert(ok()); } IPosition::IPosition (size_t length, ssize_t val) : size_p (length), data_p (buffer_p) { if (size_p > BufferLength) { allocateBuffer(); } std::fill_n(data_p, size_p, val); } IPosition::IPosition (const IPosition& other) : size_p (other.size_p), data_p (buffer_p) { if (size_p > BufferLength) { allocateBuffer(); } std::copy_n(other.data_p, size_p, data_p); assert(ok()); } IPosition::IPosition (IPosition&& source) noexcept : size_p (source.size_p), data_p (size_p > BufferLength ? source.data_p : buffer_p) { std::copy_n(source.data_p, size_p, data_p); source.size_p = 0; source.data_p = source.buffer_p; } Vector IPosition::asVector() const { assert(ok()); Vector retval(nelements()); copy (retval.begin()); return retval; } Vector IPosition::asVector64() const { assert(ok()); Vector retval(nelements()); copy (retval.begin()); return retval; } IPosition::IPosition (const std::vector &other) : size_p (0), data_p (buffer_p) { fill (other.size(), other.begin()); assert(ok()); } IPosition::IPosition (const std::vector &other) : size_p (0), data_p (buffer_p) { fill (other.size(), other.begin()); assert(ok()); } std::vector IPosition::asStdVector() const { assert(ok()); std::vector retval(nelements()); copy (retval.begin()); return retval; } std::vector IPosition::asStdVector64() const { assert(ok()); std::vector retval(nelements()); copy (retval.begin()); return retval; } IPosition IPosition::nonDegenerate (size_t startingAxis) const { if (startingAxis >= size_p) { return *this; } IPosition ignoreAxes(startingAxis); for (size_t i=0; i= ssize_t(size_p)) throw std::runtime_error("ignoreAxes(i) >= ssize_t(size_p)"); keepAxes(ignoreAxes(i)) = 1; } // Now count all axes to keep. size_t count=0; for (i=0; i // ArrayConformanceError // IPosition& IPosition::operator= (const IPosition& other) { assert(ok()); if (&other == this) { return *this; } if (size_p != other.size_p) { resize (other.nelements(), false); } std::copy_n(other.data_p, size_p, data_p); assert(ok()); return *this; } IPosition& IPosition::operator=(IPosition&& source) { size_p = source.size_p; if (data_p != &buffer_p[0]) delete [] data_p; data_p = size_p > BufferLength ? source.data_p : buffer_p; std::copy_n(source.data_p, size_p, data_p); source.size_p = 0; source.data_p = source.buffer_p; return *this; } IPosition& IPosition::operator= (ssize_t value) { assert(ok()); std::fill_n(data_p, size_p, value); return *this; } IPosition IPosition::operator() (const IPosition& axes) const { IPosition ipos(axes.nelements()); size_t i = 0; for (IPosition::const_iterator iter=axes.begin(); iter!=axes.end(); ++iter, ++i) { if (*iter >= int(size_p)) { throw std::runtime_error("IPosition::operator()(const IPosition&): " "Axis number must be less than size of current object"); } ipos[i] = data_p[*iter]; } return ipos; } void IPosition::append (const IPosition& other) { const size_t pos = size_p; resize (size_p + other.size_p); std::copy_n(other.data_p, other.size_p, data_p + pos); } void IPosition::prepend (const IPosition& other) { const size_t old_size = size_p; resize (size_p + other.size_p); std::move_backward(data_p, data_p + old_size, data_p + size_p); std::copy_n(other.data_p, other.size_p, data_p); } IPosition IPosition::concatenate (const IPosition& other) const { IPosition tmp(size_p + other.size_p); std::copy_n(data_p, size_p, tmp.data_p); std::copy_n(other.data_p, other.size_p, tmp.data_p + size_p); return tmp; } void IPosition::setFirst (const IPosition& other) { if (size_p < other.size_p) { throw (std::runtime_error ("IPosition::setFirst(other); other is too long")); } std::copy_n(other.data_p, other.size_p, data_p); } void IPosition::setLast (const IPosition& other) { if (size_p < other.size_p) { throw (std::runtime_error ("IPosition::setLast(other); other is too long")); } std::copy_n(other.data_p, other.size_p, data_p + size_p - other.size_p); } IPosition IPosition::getFirst (size_t n) const { if (size_p < n) { throw (std::runtime_error ("IPosition::getFirst(n); n is too high")); } IPosition tmp(n); std::copy_n(data_p, n, tmp.data_p); return tmp; } IPosition IPosition::getLast (size_t n) const { if (size_p < n) { throw (std::runtime_error ("IPosition::getLast(n); n is too high")); } IPosition tmp(n); std::copy_n(data_p + size_p - n, n, tmp.data_p); return tmp; } IPosition IPosition::removeAxes (const IPosition& axes) const { // Get the axes to keep. // It also checks if axes are specified correctly. IPosition resAxes = IPosition::otherAxes (size_p, axes); size_t ndimRes = resAxes.nelements(); // Create the result shape. IPosition resShape(ndimRes); if (ndimRes == 0) { resShape.resize(1); resShape[0] = 1; } else { for (size_t i=0; i // ArrayConformanceError // void IPosition::operator += (const IPosition& other) { assert(ok()); if (! conform(other)) { throw(ArrayConformanceError("IPosition::operator += " "(const IPosition&) - " "this and other differ in length")); } for (size_t i=0; i // ArrayConformanceError // void IPosition::operator -= (const IPosition& other) { assert(ok()); if (! conform(other)) { throw(ArrayConformanceError("IPosition::operator -= " "(const IPosition&) - " "this and other differ in length")); } for (size_t i=0; i // ArrayConformanceError // void IPosition::operator *= (const IPosition& other) { assert(ok()); if (! conform(other)) { throw(ArrayConformanceError("IPosition::operator *= " "(const IPosition&) - " "this and other differ in length")); } for (size_t i=0; i // ArrayConformanceError // void IPosition::operator /= (const IPosition& other) { assert(ok()); if (! conform(other)) { throw(ArrayConformanceError("IPosition::operator /= " "(const IPosition&) - " "this and other differ in length")); } for (size_t i=0; i= nrthat) { break; } if (data_p[i] != other(j)) { return false; } j++; } } for (; i nelements()) { nrCompare = nelements(); } for (size_t i=0; i // ArrayConformanceError // IPosition operator + (const IPosition& left, const IPosition& right) { if (! left.conform(right)) { throw(ArrayConformanceError("::operator + " "(const IPosition&, const IPosition&) - " "left and right operand do not conform ")); } IPosition result(left); result += right; return result; } // // ArrayConformanceError // IPosition operator - (const IPosition& left, const IPosition& right) { if (! left.conform(right)) { throw(ArrayConformanceError("::operator - " "(const IPosition&, const IPosition&) - " "left and right operand do not conform ")); } IPosition result(left); result -= right; return result; } // // ArrayConformanceError // IPosition operator * (const IPosition& left, const IPosition& right) { if (! left.conform(right)) { throw(ArrayConformanceError("::operator * " "(const IPosition&, const IPosition&) - " "left and right operand do not conform ")); } IPosition result(left); result *= right; return result; } // // ArrayConformanceError // IPosition operator / (const IPosition& left, const IPosition& right) { if (! left.conform(right)) { throw(ArrayConformanceError("::operator / " "(const IPosition&, const IPosition&) - " "left and right operand do not conform ")); } IPosition result(left); result /= right; return result; } IPosition operator + (const IPosition& left, ssize_t val) { IPosition result(left); result += val; return result; } IPosition operator - (const IPosition& left, ssize_t val) { IPosition result(left); result -= val; return result; } IPosition operator * (const IPosition& left, ssize_t val) { IPosition result(left); result *= val; return result; } IPosition operator / (const IPosition& left, ssize_t val) { IPosition result(left); result /= val; return result; } IPosition operator + (ssize_t val, const IPosition& right) { IPosition result(right.nelements()); result = val; result += right; return result; } IPosition operator - (ssize_t val, const IPosition& right) { IPosition result(right.nelements()); result = val; result -= right; return result; } IPosition operator * (ssize_t val, const IPosition& right) { IPosition result(right.nelements()); result = val; result *= right; return result; } IPosition operator / (ssize_t val, const IPosition& right) { IPosition result(right.nelements()); result = val; result /= right; return result; } IPosition max (const IPosition& left, const IPosition& right) { if (! left.conform(right)) { throw(ArrayConformanceError("::max " "(const IPosition&, const IPosition&) - " "left and right operand do not conform ")); } IPosition result(left); const size_t ndim = result.nelements(); ssize_t max; for (size_t i = 0; i < ndim; i++) { if (result(i) < (max = right(i))) { result(i) = max; } } return result; } // // ArrayConformanceError // IPosition min (const IPosition& left, const IPosition& right) { if (! left.conform(right)) { throw(ArrayConformanceError("::min " "(const IPosition&, const IPosition&) - " "left and right operand do not conform ")); } IPosition result(left); const size_t ndim = result.nelements(); ssize_t min; for (size_t i = 0; i < ndim; i++) { if (result(i) > (min = right(i))) { result(i) = min; } } return result; } long long IPosition::product() const { if (nelements() == 0) { return 0; } long long total = 1; for (size_t i=0; i // ArrayConformanceError // bool operator == (const IPosition& left, const IPosition& right) { if (! left.conform(right)) { throw(ArrayConformanceError("::operator== " "(const IPosition&, const IPosition&) - " "left and right operand do not conform ")); } size_t n=left.nelements(); bool result = true; for (size_t i=0; i // ArrayConformanceError // bool operator != (const IPosition& left, const IPosition& right) { if (! left.conform(right)) { throw(ArrayConformanceError("::operator!= " "(const IPosition&, const IPosition&) - " "left and right operand do not conform (left.shape()=" + to_string(left) + ", right.shape()=" + to_string(right) + ")")); } size_t n=left.nelements(); bool result = false; for (size_t i=0; i // ArrayConformanceError // bool operator < (const IPosition& left, const IPosition& right) { if (! left.conform(right)) { throw(ArrayConformanceError("::operator< " "(const IPosition&, const IPosition&) - " "left and right operand do not conform ")); } size_t n=left.nelements(); bool result = true; for (size_t i=0; i // ArrayConformanceError // bool operator <= (const IPosition& left, const IPosition& right) { if (! left.conform(right)) { throw(ArrayConformanceError("::operator<= " "(const IPosition&, const IPosition&) - " "left and right operand do not conform ")); } size_t n=left.nelements(); bool result = true; for (size_t i=0; i // ArrayConformanceError // bool operator > (const IPosition& left, const IPosition& right) { if (! left.conform(right)) { throw(ArrayConformanceError("::operator> " "(const IPosition&, const IPosition&) - " "left and right operand do not conform ")); } size_t n=left.nelements(); bool result = true; for (size_t i=0; i right(i)) { // Nothing - written to make cut and paste easier } else { result = false; break; } } return result; } // // ArrayConformanceError // bool operator >= (const IPosition& left, const IPosition& right) { if (! left.conform(right)) { throw(ArrayConformanceError("::operator>= " "(const IPosition&, const IPosition&) - " "left and right operand do not conform ")); } size_t n=left.nelements(); bool result = true; for (size_t i=0; i= right(i)) { // Nothing - written to make cut and paste easier } else { result = false; break; } } return result; } bool operator == (const IPosition& left, ssize_t val) { bool result = true; size_t n = left.nelements(); for (size_t i=0; i (const IPosition& left, ssize_t val) { bool result = true; size_t n = left.nelements(); for (size_t i=0; i val) { // Nothing } else { result = false; break; } } return result; } bool operator >= (const IPosition& left, ssize_t val) { bool result = true; size_t n = left.nelements(); for (size_t i=0; i= val) { // Nothing } else { result = false; break; } } return result; } bool operator == (ssize_t val, const IPosition& right) { bool result = true; size_t n = right.nelements(); for (size_t i=0; i (ssize_t val, const IPosition& right) { bool result = true; size_t n = right.nelements(); for (size_t i=0; i right(i)) { // Nothing } else { result = false; break; } } return result; } bool operator >= (ssize_t val, const IPosition& right) { bool result = true; size_t n = right.nelements(); for (size_t i=0; i= right(i)) { // Nothing } else { result = false; break; } } return result; } std::string to_string(const IPosition& ip) { std::ostringstream oss; oss << ip; return oss.str(); } std::string IPosition::toString() const { return to_string(*this); } std::ostream& operator<< (std::ostream& os, const IPosition& ip) { os << "["; for (size_t i=0; i 0) { os << ", "; } os << ip(i); } os << "]"; return os; } bool IPosition::ok() const { assert(size_p > BufferLength || data_p == &buffer_p[0]); assert(data_p != nullptr); bool retval = true; if (size_p <= BufferLength && data_p != &buffer_p[0]) { retval = false; } if (data_p == nullptr) { retval = false; } return retval; } IPosition toIPositionInArray (long long offset, const IPosition& shape) { if (! isInsideArray (offset, shape) ) { throw (ArrayIndexError( "IPosition ::toIPositionInArray (long long offset," " const IPosition& shape)" " - Invalid offset.")); } IPosition iposition (shape.nelements()); long long divisor = 1; size_t ndim = shape.nelements(); for (size_t idim = 0; idim < ndim; idim++) { iposition(idim) = ((offset / divisor) % shape(idim)); divisor *= shape(idim); } return iposition; } long long toOffsetInArray (const IPosition& iposition, const IPosition& shape) { if (! (iposition.conform(shape)) ) { throw (ArrayConformanceError( "long long ::toOffsetInArray (const IPosition& iposition," " const IPosition& shape)" " - IPositions do not conform")); } if (! isInsideArray (iposition, shape) ) { throw (ArrayIndexError( "long long ::toOffsetInArray (const IPosition& iposition," " const IPosition& shape)" " - Invalid iposition.")); } long long offset = 0; long long multiplier = 1; size_t ndim = shape.nelements(); for (size_t idim = 0; idim < ndim; idim++) { offset += (iposition(idim) * multiplier); multiplier *= shape(idim); } return offset; } bool isInsideArray (long long offset, const IPosition& shape) { return (offset < shape.product()) ? true : false; } bool isInsideArray (const IPosition& iposition, const IPosition& shape) { if (! (iposition.conform(shape)) ) { throw (ArrayConformanceError( "bool ::isInsideArray (const IPosition& iposition," " const IPosition& shape)" " - IPositions do not conform")); } bool result = true; ssize_t ioff; size_t ndim = shape.nelements(); for (size_t idim = 0; idim < ndim; idim++) { ioff = iposition(idim); if ( (ioff < 0) || (ioff >= shape(idim)) ) { result = false; break; } } return result; } IPosition IPosition::makeAxisPath (size_t nrdim, const IPosition& partialPath) { // Check if the specified traversal axes are correct and unique. if (partialPath.nelements() > nrdim) std::runtime_error("partialPath.nelements() > nrdim"); IPosition path(nrdim); IPosition done(nrdim, 0); size_t i,j; for (i=0; i= int(nrdim) || done(path(i)) != 0) { throw (std::runtime_error ("IPosition::makeAxisPath: invalid defined axes")); } done(path(i)) = 1; } // Fill unspecified axes with the natural order. for (j=0; j - but that causes multiply // defined symbols with the current objectcenter. throw(std::runtime_error("IPosition::operator() - index error")); } bool IPositionComparator::operator ()(const IPosition& lhs, const IPosition& rhs) const { size_t lhsSize = lhs.size_p; size_t rhsSize = rhs.size_p; if (lhsSize == rhsSize) { ssize_t *lp = lhs.data_p; ssize_t *rp = rhs.data_p; for (size_t i=0; i #include // for ptrdiff_t #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class AipsIO; class LogIO; //

        A Vector of integers, for indexing into Array objects. // // // //# //# Classes you should understand before using this one. //# // // IPosition is an Index Position in an n-dimensional array. // // // IPosition is "logically" a Vector constrained so that its origin // is zero-based, and in fact that used to be the way it was implemented. // It was split out into a separate class to make the inheritance from // Arrays simpler (since Arrays use IPositions). The // template instantiation mechanism is complicated enough that this // simplification was felt to be a good idea. //

        // IPosition objects are normally used to index into, and define the shapes // of, multi-dimensional arrays. For example, if you have a 5 dimensional // array, you need an IPosition of length 5 to index into the array (or // to define its shape, etc.). //

        // Unlike Vectors, IPositions always use copy semantics. // // IPosition ip1(5); // An IPosition of length 5 // ip1(0) = 11; ip1(1) = 5; ... ip1(4) = 6; // Indices 0-based // IPosition ip2(ip1); // Copy constructor; a COPY // // // Binary operations must take place either with a conformnat (same size) // IPosition or with an integer, which behaves as if it was an IPosition // of the same size (i.e., length). All the usual binary arithmetic // operations are available, as well as logical operations, which return // Booleans. These all operate "element-by-element". //

        // All non-inlined member functions of IPosition check invariants if the // preprocessor symbol AIPS_DEBUG is defined. // That is, the member functions check that ok() is true (constructors // check after construction, other functions on entry to the function). // If these tests fail, an AipsError exception is thrown; its message // contains the line number and source file of the failure (it is thrown // by the lAssert macro defined in aips/Assert.h). // // Constructors and functions exist to construct an IPosition directly from // a Vector or std::vector object or to convert to it. Furthermore the // fill and copy can be used to fill from or copy to // any STL-type iterator. // // // // IPosition blc(5), trc(5,1,2,3,4,5); // blc = 0; // OR IPosition blc(5,0); // //... // if (blc > trc) { // IPosition tmp; // tmp = trc; // Swap // trc = blc; // blc = tmp; // } // //... // trc += 5; // make the box 5 larger in all dimensions // std::vector vec(trc.toStdVector()); // // class IPosition { friend class IPositionComparator; public: // A zero-length IPosition. IPosition() noexcept; // An IPosition of size "length." The values in the object are uninitialized. explicit IPosition(size_t length); // An IPosition initialized from the given list IPosition(std::initializer_list list); // An IPosition of size "length." All values in the object are // initialized to val. IPosition(size_t length, ssize_t val); // An IPosition initialized from a variable number of parameters. // The first parameter should specify the size, but the actual // size of the resulting IPosition is determined from the number // of parameters (the first argument is ignored). // // This constructor should be disfavoured, because i) of the // dummy parameter and ii) because it may narrow the // specified parameter without a warning. // // Instead, use an initializer list constructor whenever possible. // If an IPosition is created inside a macro, an initializer list // is not possible. In those cases, use the Make(Vals...) factory // method. Both of those methods do not have the above issues. template //[[ deprecated("Use the initializer list constructor or Make() method") ]] IPosition (size_t /*dummy*/, ssize_t val1, ssize_t val2, Vals... vals) : IPosition{val1, val2, static_cast(vals)...} { } // Makes a copy (copy, NOT reference, semantics) of source. IPosition(const IPosition& source); IPosition(IPosition&& source) noexcept; ~IPosition(); // Construct an IPosition that is initialized from a variable number of parameter. // The resulting size of the IPosition will equal the number of parameters specified. // // In general, using the initializer list constructor should be preferred. Defining // an initializer list inside macros is however not possible. In those cases, this // method can be used to construct the IPosition. // // Example: IPosition::Make(3, 5) creates an IPosition of size 2, with values 3 and 5. // It is identical to IPosition{3, 5}. A program is ill-formed when narrowing of // a parameter is required, causing a compiler warning or error. template static IPosition Make (Vals... vals) { return IPosition{vals...}; } // Makes this a copy of other. When the dest is not of the same // size, it will resize itself to be the same length as the source. IPosition& operator=(const IPosition& source); IPosition& operator=(IPosition&& source); // Copy "value" into every position of this IPosition. IPosition& operator=(ssize_t value); // Construct a default axis path consisting of the values 0 .. nrdim-1. static IPosition makeAxisPath (size_t nrdim); // Construct a full axis path from a (partially) given axis path. // It fills the path with the non-given axis. // It is checked if the given axis path is valid (i.e. if the axis are // < nrdim and if they are not doubly defined). // E.g.: in the 4D case an axis path [0,2] is returned as [0,2,1,3]. static IPosition makeAxisPath (size_t nrdim, const IPosition& partialPath); // Make a list of axes which are the axes not given in axes // up to the given dimension static IPosition otherAxes (size_t nrdim, const IPosition& axes); // Convert an IPosition to and from an Array. In either case, the // array must be one dimensional. // IPosition(const Array& other); IPosition(const Array& other); Vector asVector() const; Vector asVector64() const; // // Convert an IPosition to and from an Array. In either case, the // array must be one dimensional. // IPosition(const std::vector& other); IPosition(const std::vector& other); std::vector asStdVector() const; std::vector asStdVector64() const; // // Resize and fill this IPosition object. template void fill (size_t size, InputIterator iter) { resize (size); for (size_t i=0; i void copy (OutputIterator iter) const { for (size_t i=0; i // The functions with argument ignoreAxes do // not consider the axes given in that argument. // IPosition nonDegenerate(size_t startingAxis=0) const; IPosition nonDegenerate(const IPosition& ignoreAxes) const; // // Old values are copied on resize if copy==true. // If the size increases, values beyond the former size are undefined. void resize(size_t newSize, bool copy=true); // Index into the IPosition. Indices are zero-based. If the preprocessor // symbol AIPS_ARRAY_INDEX_CHECK is defined, operator() will check // "index" to ensure it is not out of bounds. If this check fails, an // AipsError will be thrown. // ssize_t& operator[] (size_t index); ssize_t operator[] (size_t index) const; ssize_t& operator() (size_t index); ssize_t operator() (size_t index) const; // // Make an IPosition by using only the specified elements of the current // IPosition. All values of axes must be less than // the number of elements of the current object. // // IPosition ipos(4, 11, 12, 13, 14); // // ex1 is IPosition(3, 11, 12, 13); // IPosition ex1 = ipos(IPosition(3,0,1,2); // // ex2 is IPosition(3, 12, 11) // IPosition ex2 = ipos(IPosition(2,2,1); // // ex3 is IPosition(4,14,14,14,14) // IPosition ex3 = ipos(IPosition(4,3,3,3,3); // IPosition operator() (const IPosition& axes) const; // Index into the IPosition from the end. // By default the last value is returned. // If the preprocessor symbol AIPS_ARRAY_INDEX_CHECK is defined, it will // check if the index is not out of bounds. // ssize_t& last (size_t index=0); ssize_t last (size_t index=0) const; // // Get the storage. const ssize_t *storage() const; // Append this IPosition with another one (causing a resize). void append (const IPosition& other); // Prepend this IPosition with another one (causing a resize). void prepend (const IPosition& other); // Return an IPosition as the concetanation of this and another IPosition. IPosition concatenate (const IPosition& other) const; // Set the first values of this IPosition to another IPosition. // An exception is thrown if the other IPosition is too long. void setFirst (const IPosition& other); // Set the last values of this IPosition to another IPosition. // An exception is thrown if the other IPosition is too long. void setLast (const IPosition& other); // Construct an IPosition from the first n values of this // IPosition. // An exception is thrown if n is too high. IPosition getFirst (size_t n) const; // Construct an IPosition from the last n values of this // IPosition. // An exception is thrown if n is too high. IPosition getLast (size_t n) const; // Return an IPosition where the given axes are reoved. IPosition removeAxes (const IPosition& axes) const; // Return an IPosition containing the given axes only. IPosition keepAxes (const IPosition& axes) const; // The number of elements in this IPosition. Since IPosition // objects use zero-based indexing, the maximum available index is // nelements() - 1. // size_t nelements() const; size_t size() const; // // Is the IPosition empty (i.e. no elements)? bool empty() const; // conform returns true if nelements() == other.nelements(). bool conform(const IPosition& other) const; // Element-by-element arithmetic. // void operator += (const IPosition& other); void operator -= (const IPosition& other); void operator *= (const IPosition& other); void operator /= (const IPosition& other); void operator += (ssize_t val); void operator -= (ssize_t val); void operator *= (ssize_t val); void operator /= (ssize_t val); // // Returns 0 if nelements() == 0, otherwise it returns the product of // its elements. long long product() const; // Are all elements equal to 1? // Useful to check if a given stride is really a stride. bool allOne() const; // Element-by-element comparison for equality. // It returns true if the lengths and all elements are equal. //
        // Note that an important difference between this function and operator==() // is that if the two IPositions have different lengths, this function // returns false, instead of throwing an exception as operator==() does. bool isEqual (const IPosition& other) const; // Element-by-element comparison for equality. // It returns true if all elements are equal. // When skipDegeneratedAxes is true, axes with // length 1 are skipped in both operands. bool isEqual (const IPosition& other, bool skipDegeneratedAxes) const; // Element-by-element comparison for (partial) equality. // It returns true if the lengths and the first nrCompare // elements are equal. bool isEqual (const IPosition& other, size_t nrCompare) const; // Is the other IPosition a subset of (or equal to) this IPosition? // It is a subset if zero or more axes of this IPosition do not occur // or are degenerated in the other and if the remaining axes are // in the same order. bool isSubSet (const IPosition& other) const; // Write the IPosition into a string. // TODO deprecate in favour of to_string(const IPosition&) std::string toString() const; // Write an IPosition to an ostream in a simple text form. friend std::ostream& operator<<(std::ostream& os, const IPosition& ip); // Is this IPosition consistent? bool ok() const; // Define the STL-style iterators. // It makes it possible to iterate through all data elements. // // IPosition shp(2,0); // for (IPosition::iterator iter=shp.begin(); iter!=shp.end(); iter++) { // *iter += 1; // } // // // STL-style typedefs. // typedef ssize_t value_type; typedef ssize_t* iterator; typedef const ssize_t* const_iterator; typedef value_type* pointer; typedef const value_type* const_pointer; typedef value_type& reference; typedef const value_type& const_reference; typedef size_t size_type; typedef ptrdiff_t difference_type; // // Get the begin and end iterator object for this object. // iterator begin() { return data_p; } const_iterator begin() const { return data_p; } iterator end() { return data_p + size_p; } const_iterator end() const { return data_p + size_p; } // // private: // Allocate a buffer with length size_p. void allocateBuffer(); // Throw an index error exception. void throwIndexError() const; enum { BufferLength = 4 }; size_t size_p; ssize_t buffer_p[BufferLength]; // When the iposition is length BufferSize or less data is just buffer_p, // avoiding calls to new and delete. ssize_t *data_p; }; // Allows a way for IPosition to be used as keys in a std::map class IPositionComparator { public: // if sizes aren't equal, returns true if lhs.size() < rhs.size(), false // otherwise. If sizes are equal, does an element by element comparison. The first // corresponding elements that are not equal, returns true if the rhs element is // less than the lhs element, false otherwise. Returns false if all elements are // equal. bool operator()(const IPosition& lhs, const IPosition& rhs) const; }; //

        Arithmetic Operations for IPosition's // Element by element arithmetic on IPositions. // // Each operation is done on corresponding elements of the IPositions. The // two IPositions must have the same number of elements otherwise an // exception (ArrayConformanceError) will be thrown. // IPosition operator + (const IPosition& left, const IPosition& right); IPosition operator - (const IPosition& left, const IPosition& right); IPosition operator * (const IPosition& left, const IPosition& right); IPosition operator / (const IPosition& left, const IPosition& right); // // Each operation is done by appliying the integer argument to all elements // of the IPosition argument. // IPosition operator + (const IPosition& left, ssize_t val); IPosition operator - (const IPosition& left, ssize_t val); IPosition operator * (const IPosition& left, ssize_t val); IPosition operator / (const IPosition& left, ssize_t val); // // Same functions as above but with with the int argument on the left side. // IPosition operator + (ssize_t val, const IPosition& right); IPosition operator - (ssize_t val, const IPosition& right); IPosition operator * (ssize_t val, const IPosition& right); IPosition operator / (ssize_t val, const IPosition& right); // // Returns the element by element minimum or maximum. // IPosition max (const IPosition& left, const IPosition& right); IPosition min (const IPosition& left, const IPosition& right); // // // Logical operations for IPosition's // Element by element boolean operations on IPositions. The result is true // only if the operation yields true for every element of the IPosition. // // Each operation is done on corresponding elements of the IPositions. The // two IPositions must have the same number of elements otherwise an // exception (ArrayConformanceError) will be thrown. // bool operator == (const IPosition& left, const IPosition& right); bool operator != (const IPosition& left, const IPosition& right); bool operator < (const IPosition& left, const IPosition& right); bool operator <= (const IPosition& left, const IPosition& right); bool operator > (const IPosition& left, const IPosition& right); bool operator >= (const IPosition& left, const IPosition& right); // // Each operation is done by appliying the integer argument to all elements // bool operator == (const IPosition& left, ssize_t val); bool operator != (const IPosition& left, ssize_t val); bool operator < (const IPosition& left, ssize_t val); bool operator <= (const IPosition& left, ssize_t val); bool operator > (const IPosition& left, ssize_t val); bool operator >= (const IPosition& left, ssize_t val); // // Same functions as above but with with the int argument on the left side. // bool operator == (ssize_t val, const IPosition& right); bool operator != (ssize_t val, const IPosition& right); bool operator < (ssize_t val, const IPosition& right); bool operator <= (ssize_t val, const IPosition& right); bool operator > (ssize_t val, const IPosition& right); bool operator >= (ssize_t val, const IPosition& right); // // // Indexing functions for IPosition's // Convert between IPosition and offset in an array. // // The offset of an element in an array is the number of elements from the // origin that the element would be if the array were arranged linearly. // The origin of the array has an offset equal to 0, while the // "top right corner" of the array has an offset equal to one less than the // total number of elements in the array. // // Two examples of offset would be the index in a carray and the seek position // in a file. // // Convert from offset to IPosition in an array. IPosition toIPositionInArray (long long offset, const IPosition& shape); // Convert from IPosition to offset in an array. long long toOffsetInArray (const IPosition& iposition, const IPosition& shape); // Determine if the given offset or IPosition is inside the array. Returns // true if it is inside the Array. // //
      • ArrayConformanceError: If all the IPositions are not the same length // // bool isInsideArray (const long long offset, const IPosition& shape); bool isInsideArray (const IPosition& iposition, const IPosition& shape); // // std::string to_string(const IPosition& ip); //# Inlined member functions for IPosition inline IPosition::IPosition() noexcept : size_p (0), data_p (buffer_p) {} inline IPosition IPosition::makeAxisPath (size_t nrdim) { return makeAxisPath (nrdim, IPosition()); } inline size_t IPosition::nelements() const { return size_p; } inline size_t IPosition::size() const { return size_p; } inline bool IPosition::empty() const { return size_p == 0; } inline ssize_t& IPosition::operator[](size_t index) { return data_p[index]; } inline ssize_t IPosition::operator[](size_t index) const { return data_p[index]; } inline ssize_t& IPosition::operator()(size_t index) { #if defined(AIPS_ARRAY_INDEX_CHECK) if (index >= nelements()) { throwIndexError(); } #endif return data_p[index]; } inline ssize_t IPosition::operator()(size_t index) const { #if defined(AIPS_ARRAY_INDEX_CHECK) if (index >= nelements()) { throwIndexError(); } #endif return data_p[index]; } inline ssize_t& IPosition::last (size_t index) { #if defined(AIPS_ARRAY_INDEX_CHECK) if (size_p - index <= 0) { throwIndexError(); } #endif return data_p[size_p-index-1]; } inline ssize_t IPosition::last (size_t index) const { #if defined(AIPS_ARRAY_INDEX_CHECK) if (size_p - index <= 0) { throwIndexError(); } #endif return data_p[size_p-index-1]; } inline const ssize_t *IPosition::storage() const { return data_p; } inline bool IPosition::conform(const IPosition& other) const { return (size_p == other.size_p); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Arrays/LogiArray.h000066400000000000000000000054771476623553700175530ustar00rootroot00000000000000//# LogiArray.h: Logical valued Arrays. //# Copyright (C) 1994,1995,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_LOGIARRAY_2_H #define CASA_LOGIARRAY_2_H #include "Array.h" namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Logical valued Arrays. // // // // //
      • Array // // // // LogicalArray declares logical valued Arrays. // // // // This file contains the declarations for LogicalArrays. // // // // One needs to have logical valued Arrays. They are the result of // logical operations on Arrays. They can also be created in other ways. // They are used as masks for MaskedArrays. // // Array would have served the purpose. However, it is very space // inefficient. Instead, the concept has been abstracted. Currently, // the implementation of LogicalArray is Array, done // with typedefs. The type of LogicalArrayElem can be changed at any time. // Later, if desired, LogicalArray can be made to be a true class, without // requiring more than a recompile of code which uses it. // // // // LogicalArray -- Logical valued Arrays. // // // //# This is empty. Everything is done by the include files. // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Arrays/LogiCube.h000066400000000000000000000046151476623553700173440ustar00rootroot00000000000000//# LogiCube.h: Logical valued Cubes. //# Copyright (C) 1994,1995,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_LOGICUBE_2_H #define CASA_LOGICUBE_2_H #include "LogiArray.h" #include "Cube.h" namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Logical valued Cubes. // // // // //
      • Array //
      • Cube //
      • LogicalArray // // // // LogicalCube declares logical valued Cubes. // // // // This file contains the declarations for LogicalCubes. // // // // These are the Cube specialization of LogicalArrays. // // // // LogicalCube -- Logical valued Cubes. // // // // Define LogicalCube. // typedef Cube LogicalCube; // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Arrays/LogiMatrix.h000066400000000000000000000046721476623553700177350ustar00rootroot00000000000000//# LogiMatrix.h: Logical valued Matrices. //# Copyright (C) 1994,1995,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_LOGIMATRIX_2_H #define CASA_LOGIMATRIX_2_H #include "LogiArray.h" #include "Matrix.h" namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Logical valued Matrices. // // // // //
      • Array //
      • Matrix //
      • LogicalArray // // // // LogicalMatrix declares logical valued Matrices. // // // // This file contains the declarations for LogicalMatrixs. // // // // These are the Matrix specialization of LogicalArrays. // // // // LogicalMatrix -- Logical valued Matrices. // // // // Define LogicalMatrix. // typedef Matrix LogicalMatrix; // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Arrays/LogiVector.h000066400000000000000000000046601476623553700177300ustar00rootroot00000000000000//# LogiVector.h: Logical valued Vectors. //# Copyright (C) 1994,1995,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_LOGIVECTOR_2_H #define CASA_LOGIVECTOR_2_H #include "LogiArray.h" #include "Vector.h" namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Logical valued Vectors. // // // // //
      • Array //
      • Vector //
      • LogicalArray // // // // LogicalVector declares logical valued Vectors. // // // // This file contains the declarations for LogicalVectors. // // // // These are the Vector specialization of LogicalArrays. // // // // LogicalVector -- Logical valued Vectors. // // // // Define LogicalVector. // typedef Vector LogicalVector; // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Arrays/MaskArrIO.h000066400000000000000000000064231476623553700174420ustar00rootroot00000000000000//# MaskArrIO.h: Write out an ascii representation of a MaskedArray. //# Copyright (C) 1993,1994,1995,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MASKARRIO_2_H #define CASA_MASKARRIO_2_H namespace casacore { //# NAMESPACE CASACORE - BEGIN template class MaskedArray; // // Ascii input/output operations for MaskedArrays. // // // // //
      • Array //
      • MaskedArray //
      • ArrayIO // // // // MaskArrIO is short for MaskedArrayIO, which is too long by the old // AIPS++ file naming conventions. This file contains global functions // for writing out ascii representations of masked arrays. // // // // These functions write out masked arrays in ascii representation. // They simply write out the Array and the LogicalArray which is the mask // using the ascii output functions for these objects. // // // // // Vector a(10); // LogicalVector b(10); // MaskedArray m (a,b); // . . . // cout << m; // // This example writes out m in ascii. It writes first a and then // the mask constructed from b. // // // // These are primarily for debugging, so that one can examine the MaskedArray. // Since MaskedArrays are manipulators of Arrays, it was not thought to // be necessary, or even a good idea, to have other kinds of IO defined // for them. // // // // MaskedArray IO -- Ascii input/output operations // for MaskedArrays. // // // // // Write out an ascii representation of a MaskedArray. // The component Array and LogicalArray are written out sequentially. template std::ostream & operator<< (std::ostream &, const MaskedArray &); // template std::string to_string(const MaskedArray &); } //# NAMESPACE CASACORE - END #include "MaskArrIO.tcc" #endif casacore-3.7.1/casa/Arrays/MaskArrIO.tcc000066400000000000000000000035021476623553700177570ustar00rootroot00000000000000//# MaskArrIO.cc: Write out an ascii reporesentation of a MaskedArray. //# Copyright (C) 1993,1994,1995,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MASKARRIO_2_TCC #define CASA_MASKARRIO_2_TCC #include "MaskArrIO.h" #include "MaskedArray.h" //#include "ArrayIO.h" namespace casacore { //# NAMESPACE CASACORE - BEGIN template std::ostream & operator<< (std::ostream &s, const MaskedArray &a) { // Print the Array. s << "Array: " << a.getArray(); s << "\n"; // Print the Mask. s << "Mask: " << a.getMask(); return s; } template std::string to_string(const MaskedArray &maskedArray) { std::ostringstream str; str << maskedArray; return str.str(); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Arrays/MaskArrLogi.h000066400000000000000000000447551476623553700200370ustar00rootroot00000000000000//# MaskArrLogi.h: Element by element logical operations on masked arrays. //# Copyright (C) 1993,1994,1995,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MASKARRLOGI_2_H #define CASA_MASKARRLOGI_2_H #include "Array.h" #include "MaskedArray.h" #include "MaskLogiArr.h" namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Logical operations for MaskedArrays, and between MaskedArrays and Arrays. // // // // //
      • Array //
      • LogicalArray //
      • MaskedArray // // // // MaskArrLogi is short for MaskedArrayLogical, which is too long by the // old AIPS++ file naming conventions. This file contains global functions // which perform element by element logical operations on masked arrays. // // // // These functions perform element by element logical operations on // masked arrays. With two arrays, they must both conform, and the result // is done element by element, for those locations where the mask of the // MaskedArray is true. For two MaskedArrays, the "and" of the masks is used. // // There are two classes of functions. One class returns a MaskedLogicalArray. // In these functions, the value of an element of the MaskedLogicalArray is // the value of the logical operation applied to the corresponding elements // of the input MaskedArrays. The other class of functions returns a single // bool. The return value is true if the logical operation returns true for // all elements of the input masked arrays for the "all" functions // (e.g. allLE()), and returns true if the logical operation returns true for // any elements of the input masked arrays for the "any" functions // (e.g. anyLE()). The functions which return a single bool throw an exception // if the AND of the masks of the input masked arrays has no true elements. // // For instance allLE (a, b) imples that every element of a is // less than or equal to every element of b. Note that with this definition // allLE (a, b) and allGE (a, b) can both be false (e.g. a = [1,0] b = [0,1]). // // NB comparison between two zero-sized arrays is not defined (should it // throw an exception?). // // // // // Vector a(10); // Vector b(10); // LogicalVector l(10); // . . . // l = a(a>0) < b(b>0); // // This example sets those elements of l where ((a>0) && (b>0)) to (a0) && (b>0)) are unchanged. The result of // the comparison is a MaskedLogicalArray. The assignment from this // MaskedLogicalArray to the LogicalArray l only assigns those elements // where the mask is true. // // // // // Vector a(10); // Vector b(10); // bool result; // . . . // result = allLT (a(a>0), b(b>0)); // // This example sets result to true if, for all elements where // ((a>0) && (b>0)), a // // // One wants to be able to mask arrays and perform logical operations on // those masked arrays. Since the masked arrays are only defined where // the masks are true, the result must be a MaskedLogicalArray, or a single // bool. // // // // MaskedArray logical operations -- Logical operations // for MaskedArrays, and between MaskedArrays and Arrays. // // // // // Element by element comparisons between the "l" and "r" arrays. The result // is true only if the comparison is true for every element of the arrays // for which the mask of the MaskedArray is true. For two MaskedArrays, // the "and" of the masks is used. // // //
      • ArrayConformanceError //
      • ArrayError // // // template bool allLE (const MaskedArray &l, const Array &r); template bool allLT (const MaskedArray &l, const Array &r); template bool allGE (const MaskedArray &l, const Array &r); template bool allGT (const MaskedArray &l, const Array &r); template bool allEQ (const MaskedArray &l, const Array &r); template bool allNE (const MaskedArray &l, const Array &r); // // This only makes sense if the array element type is logical valued. // template bool allAND (const MaskedArray &l, const Array &r); template bool allOR (const MaskedArray &l, const Array &r); // template bool allLE (const Array &l, const MaskedArray &r); template bool allLT (const Array &l, const MaskedArray &r); template bool allGE (const Array &l, const MaskedArray &r); template bool allGT (const Array &l, const MaskedArray &r); template bool allEQ (const Array &l, const MaskedArray &r); template bool allNE (const Array &l, const MaskedArray &r); // // This only makes sense if the array element type is logical valued. // template bool allAND (const Array &l, const MaskedArray &r); template bool allOR (const Array &l, const MaskedArray &r); // template bool allLE (const MaskedArray &l, const MaskedArray &r); template bool allLT (const MaskedArray &l, const MaskedArray &r); template bool allGE (const MaskedArray &l, const MaskedArray &r); template bool allGT (const MaskedArray &l, const MaskedArray &r); template bool allEQ (const MaskedArray &l, const MaskedArray &r); template bool allNE (const MaskedArray &l, const MaskedArray &r); // // This only makes sense if the array element type is logical valued. // template bool allAND (const MaskedArray &l, const MaskedArray &r); template bool allOR (const MaskedArray &l, const MaskedArray &r); // // // // Element by element comparisons between the "l" and "r" arrays. The result // is a MaskedLogicalArray. // // The arrays must conform or an exception is thrown. // // //
      • ArrayConformanceError // // // template MaskedLogicalArray operator <= (const MaskedArray &l, const Array &r); template MaskedLogicalArray operator < (const MaskedArray &l, const Array &r); template MaskedLogicalArray operator >= (const MaskedArray &l, const Array &r); template MaskedLogicalArray operator > (const MaskedArray &l, const Array &r); template MaskedLogicalArray operator == (const MaskedArray &l, const Array &r); template MaskedLogicalArray operator != (const MaskedArray &l, const Array &r); // // This only makes sense if the array element type is logical valued. // template MaskedLogicalArray operator && (const MaskedArray &l, const Array &r); template MaskedLogicalArray operator || (const MaskedArray &l, const Array &r); // template MaskedLogicalArray operator <= (const Array &l, const MaskedArray &r); template MaskedLogicalArray operator < (const Array &l, const MaskedArray &r); template MaskedLogicalArray operator >= (const Array &l, const MaskedArray &r); template MaskedLogicalArray operator > (const Array &l, const MaskedArray &r); template MaskedLogicalArray operator == (const Array &l, const MaskedArray &r); template MaskedLogicalArray operator != (const Array &l, const MaskedArray &r); // // This only makes sense if the array element type is logical valued. // template MaskedLogicalArray operator && (const Array &l, const MaskedArray &r); template MaskedLogicalArray operator || (const Array &l, const MaskedArray &r); // template MaskedLogicalArray operator <= (const MaskedArray &l, const MaskedArray &r); template MaskedLogicalArray operator < (const MaskedArray &l, const MaskedArray &r); template MaskedLogicalArray operator >= (const MaskedArray &l, const MaskedArray &r); template MaskedLogicalArray operator > (const MaskedArray &l, const MaskedArray &r); template MaskedLogicalArray operator == (const MaskedArray &l, const MaskedArray &r); template MaskedLogicalArray operator != (const MaskedArray &l, const MaskedArray &r); // // This only makes sense if the array element type is logical valued. // template MaskedLogicalArray operator && (const MaskedArray &l, const MaskedArray &r); template MaskedLogicalArray operator || (const MaskedArray &l, const MaskedArray &r); // // // // Logical negation of a MaskedArray. This only makes sense if the array // element type is logical valued. template MaskedLogicalArray operator ! (const MaskedArray &marray); // // Element by element comparisons between an array and a scalar, which // behaves as if it were a conformant array filled with the value "val." // The result is true only if the comparison is true for every element // for which the mask of the MaskedArray is true. // //
      • ArrayError // // // template bool allLE (const MaskedArray &array, const T &val); template bool allLE (const T &val, const MaskedArray &array); template bool allLT (const MaskedArray &array, const T &val); template bool allLT (const T &val, const MaskedArray &array); template bool allGE (const MaskedArray &array, const T &val); template bool allGE (const T &val, const MaskedArray &array); template bool allGT (const MaskedArray &array, const T &val); template bool allGT (const T &val, const MaskedArray &array); template bool allEQ (const MaskedArray &array, const T &val); template bool allEQ (const T &val, const MaskedArray &array); template bool allNE (const MaskedArray &array, const T &val); template bool allNE (const T &val, const MaskedArray &array); // // This only makes sense if the array element type is logical valued. // template bool allAND (const MaskedArray &array, const T &val); template bool allAND (const T &val, const MaskedArray &array); template bool allOR (const MaskedArray &array, const T &val); template bool allOR (const T &val, const MaskedArray &array); // // // // // Element by element comparisons between an array and a scalar, which // behaves as if it were a conformant array filled with the value "val." // The result is an MaskedLogicalArray. // // template MaskedLogicalArray operator <= (const MaskedArray &array, const T &val); template MaskedLogicalArray operator <= (const T &val, const MaskedArray &array); template MaskedLogicalArray operator < (const MaskedArray &array, const T &val); template MaskedLogicalArray operator < (const T &val, const MaskedArray &array); template MaskedLogicalArray operator >= (const MaskedArray &array, const T &val); template MaskedLogicalArray operator >= (const T &val, const MaskedArray &array); template MaskedLogicalArray operator > (const MaskedArray &array, const T &val); template MaskedLogicalArray operator > (const T &val, const MaskedArray &array); template MaskedLogicalArray operator == (const MaskedArray &array, const T &val); template MaskedLogicalArray operator == (const T &val, const MaskedArray &array); template MaskedLogicalArray operator != (const MaskedArray &array, const T &val); template MaskedLogicalArray operator != (const T &val, const MaskedArray &array); // // This only makes sense if the array element type is logical valued. // template MaskedLogicalArray operator && (const MaskedArray &array, const T &val); template MaskedLogicalArray operator && (const T &val, const MaskedArray &array); template MaskedLogicalArray operator || (const MaskedArray &array, const T &val); template MaskedLogicalArray operator || (const T &val, const MaskedArray &array); // // // //# With two arrays, they must both conform, and the result is done element //# by element. For instance anyLE (a, b) imples that some element of a is //# less than or equal to every element of b. //# NB comparison between two zero-sized arrays is not defined (should it //# throw an exception?). // // Element by element comparisons between the "l" and "r" arrays. The result // is true only if the comparison is true for some element of the arrays // for which the mask of the MaskedArray is true. For two MaskedArrays, // the "and" of the masks is used. // // //
      • ArrayConformanceError //
      • ArrayError // // // // template bool anyLE (const MaskedArray &l, const Array &r); template bool anyLT (const MaskedArray &l, const Array &r); template bool anyGE (const MaskedArray &l, const Array &r); template bool anyGT (const MaskedArray &l, const Array &r); template bool anyEQ (const MaskedArray &l, const Array &r); template bool anyNE (const MaskedArray &l, const Array &r); // // This only makes sense if the array element type is logical valued. // template bool anyAND (const MaskedArray &l, const Array &r); template bool anyOR (const MaskedArray &l, const Array &r); // template bool anyLE (const Array &l, const MaskedArray &r); template bool anyLT (const Array &l, const MaskedArray &r); template bool anyGE (const Array &l, const MaskedArray &r); template bool anyGT (const Array &l, const MaskedArray &r); template bool anyEQ (const Array &l, const MaskedArray &r); template bool anyNE (const Array &l, const MaskedArray &r); // // This only makes sense if the array element type is logical valued. // template bool anyAND (const Array &l, const MaskedArray &r); template bool anyOR (const Array &l, const MaskedArray &r); // template bool anyLE (const MaskedArray &l, const MaskedArray &r); template bool anyLT (const MaskedArray &l, const MaskedArray &r); template bool anyGE (const MaskedArray &l, const MaskedArray &r); template bool anyGT (const MaskedArray &l, const MaskedArray &r); template bool anyEQ (const MaskedArray &l, const MaskedArray &r); template bool anyNE (const MaskedArray &l, const MaskedArray &r); // // This only makes sense if the array element type is logical valued. // template bool anyAND (const MaskedArray &l, const MaskedArray &r); template bool anyOR (const MaskedArray &l, const MaskedArray &r); // // // // Element by element comparisons between an array and a scalar, which // behaves as if it were a conformant array filled with the value "val." // The result is true only if the comparison is true for some element // for which the mask of the MaskedArray is true. // // //
      • ArrayError // // // // template bool anyLE (const MaskedArray &array, const T &val); template bool anyLE (const T &val, const MaskedArray &array); template bool anyLT (const MaskedArray &array, const T &val); template bool anyLT (const T &val, const MaskedArray &array); template bool anyGE (const MaskedArray &array, const T &val); template bool anyGE (const T &val, const MaskedArray &array); template bool anyGT (const MaskedArray &array, const T &val); template bool anyGT (const T &val, const MaskedArray &array); template bool anyEQ (const MaskedArray &array, const T &val); template bool anyEQ (const T &val, const MaskedArray &array); template bool anyNE (const MaskedArray &array, const T &val); template bool anyNE (const T &val, const MaskedArray &array); // // This only makes sense if the array element type is logical valued. // template bool anyAND (const MaskedArray &array, const T &val); template bool anyAND (const T &val, const MaskedArray &array); template bool anyOR (const MaskedArray &array, const T &val); template bool anyOR (const T &val, const MaskedArray &array); // // // // } //# NAMESPACE CASACORE - END #include "MaskArrLogi.tcc" #endif casacore-3.7.1/casa/Arrays/MaskArrLogi.tcc000066400000000000000000001276641476623553700203620ustar00rootroot00000000000000//# MaskArrLogi.cc: Element by element logical operations on arrays. //# Copyright (C) 1993,1994,1995,1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MASKARRLOGI_2_TCC #define CASA_MASKARRLOGI_2_TCC #include "MaskArrLogi.h" #include "ArrayError.h" #include "ArrayLogical.h" namespace casacore { //# NAMESPACE CASACORE - BEGIN #define MARRLOGI_B_ALLFUNC_MA(ALLFUNC,OP,STRALLFUNC) \ template \ bool ALLFUNC (const MaskedArray &left, const Array &right) \ { \ if (left.conform(right) == false) { \ throw (ArrayConformanceError \ ("bool ::" STRALLFUNC "(const MaskedArray &, const Array &)" \ " - arrays do not conform")); \ } \ \ bool leftarrDelete; \ const T *leftarrStorage = left.getArrayStorage(leftarrDelete); \ const T *leftarrS = leftarrStorage; \ \ bool leftmaskDelete; \ const LogicalArrayElem *leftmaskStorage = \ left.getMaskStorage(leftmaskDelete); \ const LogicalArrayElem *leftmaskS = leftmaskStorage; \ \ bool rightDelete; \ const T *rightStorage = right.getStorage(rightDelete); \ const T *rightS = rightStorage; \ \ size_t ntotal = left.nelements(); \ bool retval = true; \ bool foundOne = false; \ while (ntotal--) { \ if (*leftmaskS) { \ foundOne = true; \ if (! (*leftarrS OP *rightS) ) { \ retval = false; \ break; \ } \ } \ leftarrS++; \ leftmaskS++; \ rightS++; \ } \ \ left.freeArrayStorage(leftarrStorage, leftarrDelete); \ left.freeMaskStorage(leftmaskStorage, leftmaskDelete); \ right.freeStorage(rightStorage, rightDelete); \ \ if (!foundOne) { \ throw (ArrayError( \ "bool ::" STRALLFUNC "(const MaskedArray &, const Array &)" \ " - Need at least 1 unmasked element")); \ } \ \ return retval; \ } #define MARRLOGI_B_ALLFUNC_AM(ALLFUNC,OP,STRALLFUNC) \ template \ bool ALLFUNC (const Array &left, const MaskedArray &right) \ { \ if (left.conform(right) == false) { \ throw (ArrayConformanceError \ ("bool ::" STRALLFUNC "(Array &, const MaskedArray &)" \ " - arrays do not conform")); \ } \ \ bool leftDelete; \ const T *leftStorage = left.getStorage(leftDelete); \ const T *leftS = leftStorage; \ \ bool rightarrDelete; \ const T *rightarrStorage = right.getArrayStorage(rightarrDelete); \ const T *rightarrS = rightarrStorage; \ \ bool rightmaskDelete; \ const LogicalArrayElem *rightmaskStorage = \ right.getMaskStorage(rightmaskDelete); \ const LogicalArrayElem *rightmaskS = rightmaskStorage; \ \ size_t ntotal = left.nelements(); \ bool retval = true; \ bool foundOne = false; \ while (ntotal--) { \ if (*rightmaskS) { \ foundOne = true; \ if (! (*leftS OP *rightarrS) ) { \ retval = false; \ break; \ } \ } \ leftS++; \ rightarrS++; \ rightmaskS++; \ } \ \ left.freeStorage(leftStorage, leftDelete); \ right.freeArrayStorage(rightarrStorage, rightarrDelete); \ right.freeMaskStorage(rightmaskStorage, rightmaskDelete); \ \ if (!foundOne) { \ throw (ArrayError( \ "bool ::" STRALLFUNC "(const Array &, const MaskedArray &)" \ " - Need at least 1 unmasked element")); \ } \ \ return retval; \ } #define MARRLOGI_B_ALLFUNC_MM(ALLFUNC,OP,STRALLFUNC) \ template \ bool ALLFUNC (const MaskedArray &left, const MaskedArray &right) \ { \ if (left.conform(right) == false) { \ throw (ArrayConformanceError \ ("bool ::" STRALLFUNC "(const MaskedArray &," \ " const MaskedArray &)" \ " - arrays do not conform")); \ } \ \ bool leftarrDelete; \ const T *leftarrStorage = left.getArrayStorage(leftarrDelete); \ const T *leftarrS = leftarrStorage; \ \ bool leftmaskDelete; \ const LogicalArrayElem *leftmaskStorage \ = left.getMaskStorage(leftmaskDelete); \ const LogicalArrayElem *leftmaskS = leftmaskStorage; \ \ bool rightarrDelete; \ const T *rightarrStorage = right.getArrayStorage(rightarrDelete); \ const T *rightarrS = rightarrStorage; \ \ bool rightmaskDelete; \ const LogicalArrayElem *rightmaskStorage \ = right.getMaskStorage(rightmaskDelete); \ const LogicalArrayElem *rightmaskS = rightmaskStorage; \ \ size_t ntotal = left.nelements(); \ bool retval = true; \ bool foundOne = false; \ while (ntotal--) { \ if (*leftmaskS && *rightmaskS) { \ foundOne = true; \ if (! (*leftarrS OP *rightarrS) ) { \ retval = false; \ break; \ } \ } \ leftarrS++; \ leftmaskS++; \ rightarrS++; \ rightmaskS++; \ } \ \ left.freeArrayStorage(leftarrStorage, leftarrDelete); \ left.freeMaskStorage(leftmaskStorage, leftmaskDelete); \ right.freeArrayStorage(rightarrStorage, rightarrDelete); \ right.freeMaskStorage(rightmaskStorage, rightmaskDelete); \ \ if (!foundOne) { \ throw (ArrayError( \ "bool ::" STRALLFUNC "(const MaskedArray &," \ " const MaskedArray &)" \ " - AND of MaskedArray masks must have at least 1 element")); \ } \ \ return retval; \ } MARRLOGI_B_ALLFUNC_MA ( allLE, <=, "allLE" ) MARRLOGI_B_ALLFUNC_MA ( allLT, <, "allLT" ) MARRLOGI_B_ALLFUNC_MA ( allGE, >=, "allGE" ) MARRLOGI_B_ALLFUNC_MA ( allGT, >, "allGT" ) MARRLOGI_B_ALLFUNC_MA ( allEQ, ==, "allEQ" ) MARRLOGI_B_ALLFUNC_MA ( allNE, !=, "allNE" ) MARRLOGI_B_ALLFUNC_MA ( allAND, &&, "allAND" ) MARRLOGI_B_ALLFUNC_MA ( allOR, ||, "allOR" ) MARRLOGI_B_ALLFUNC_AM ( allLE, <=, "allLE" ) MARRLOGI_B_ALLFUNC_AM ( allLT, <, "allLT" ) MARRLOGI_B_ALLFUNC_AM ( allGE, >=, "allGE" ) MARRLOGI_B_ALLFUNC_AM ( allGT, >, "allGT" ) MARRLOGI_B_ALLFUNC_AM ( allEQ, ==, "allEQ" ) MARRLOGI_B_ALLFUNC_AM ( allNE, !=, "allNE" ) MARRLOGI_B_ALLFUNC_AM ( allAND, &&, "allAND" ) MARRLOGI_B_ALLFUNC_AM ( allOR, ||, "allOR" ) MARRLOGI_B_ALLFUNC_MM ( allLE, <=, "allLE" ) MARRLOGI_B_ALLFUNC_MM ( allLT, <, "allLT" ) MARRLOGI_B_ALLFUNC_MM ( allGE, >=, "allGE" ) MARRLOGI_B_ALLFUNC_MM ( allGT, >, "allGT" ) MARRLOGI_B_ALLFUNC_MM ( allEQ, ==, "allEQ" ) MARRLOGI_B_ALLFUNC_MM ( allNE, !=, "allNE" ) MARRLOGI_B_ALLFUNC_MM ( allAND, &&, "allAND" ) MARRLOGI_B_ALLFUNC_MM ( allOR, ||, "allOR" ) #define MARRLOGI_B_ANYFUNC_MA(ANYFUNC,OP,STRANYFUNC) \ template \ bool ANYFUNC (const MaskedArray &left, const Array &right) \ { \ if (left.conform(right) == false) { \ throw (ArrayConformanceError \ ("bool ::" STRANYFUNC "(const MaskedArray &, const Array &)" \ " - arrays do not conform")); \ } \ \ bool leftarrDelete; \ const T *leftarrStorage = left.getArrayStorage(leftarrDelete); \ const T *leftarrS = leftarrStorage; \ \ bool leftmaskDelete; \ const LogicalArrayElem *leftmaskStorage = \ left.getMaskStorage(leftmaskDelete); \ const LogicalArrayElem *leftmaskS = leftmaskStorage; \ \ bool rightDelete; \ const T *rightStorage = right.getStorage(rightDelete); \ const T *rightS = rightStorage; \ \ size_t ntotal = left.nelements(); \ bool retval = false; \ bool foundOne = false; \ while (ntotal--) { \ if (*leftmaskS) { \ foundOne = true; \ if (*leftarrS OP *rightS) { \ retval = true; \ break; \ } \ } \ leftarrS++; \ leftmaskS++; \ rightS++; \ } \ \ left.freeArrayStorage(leftarrStorage, leftarrDelete); \ left.freeMaskStorage(leftmaskStorage, leftmaskDelete); \ right.freeStorage(rightStorage, rightDelete); \ \ if (!foundOne) { \ throw (ArrayError( \ "bool ::" STRANYFUNC "(const MaskedArray &, const Array &)" \ " - Need at least 1 unmasked element")); \ } \ \ return retval; \ } #define MARRLOGI_B_ANYFUNC_AM(ANYFUNC,OP,STRANYFUNC) \ template \ bool ANYFUNC (const Array &left, const MaskedArray &right) \ { \ if (left.conform(right) == false) { \ throw (ArrayConformanceError \ ("bool ::" STRANYFUNC "(Array &, const MaskedArray &)" \ " - arrays do not conform")); \ } \ \ bool leftDelete; \ const T *leftStorage = left.getStorage(leftDelete); \ const T *leftS = leftStorage; \ \ bool rightarrDelete; \ const T *rightarrStorage = right.getArrayStorage(rightarrDelete); \ const T *rightarrS = rightarrStorage; \ \ bool rightmaskDelete; \ const LogicalArrayElem *rightmaskStorage = \ right.getMaskStorage(rightmaskDelete); \ const LogicalArrayElem *rightmaskS = rightmaskStorage; \ \ size_t ntotal = left.nelements(); \ bool retval = false; \ bool foundOne = false; \ while (ntotal--) { \ if (*rightmaskS) { \ foundOne = true; \ if (*leftS OP *rightarrS) { \ retval = true; \ break; \ } \ } \ leftS++; \ rightarrS++; \ rightmaskS++; \ } \ \ left.freeStorage(leftStorage, leftDelete); \ right.freeArrayStorage(rightarrStorage, rightarrDelete); \ right.freeMaskStorage(rightmaskStorage, rightmaskDelete); \ \ if (!foundOne) { \ throw (ArrayError( \ "bool ::" STRANYFUNC "(const Array &, const MaskedArray &)" \ " - Need at least 1 unmasked element")); \ } \ \ return retval; \ } #define MARRLOGI_B_ANYFUNC_MM(ANYFUNC,OP,STRANYFUNC) \ template \ bool ANYFUNC (const MaskedArray &left, const MaskedArray &right) \ { \ if (left.conform(right) == false) { \ throw (ArrayConformanceError \ ("bool ::" STRANYFUNC "(const MaskedArray &," \ " const MaskedArray &)" \ " - arrays do not conform")); \ } \ \ bool leftarrDelete; \ const T *leftarrStorage = left.getArrayStorage(leftarrDelete); \ const T *leftarrS = leftarrStorage; \ \ bool leftmaskDelete; \ const LogicalArrayElem *leftmaskStorage \ = left.getMaskStorage(leftmaskDelete); \ const LogicalArrayElem *leftmaskS = leftmaskStorage; \ \ bool rightarrDelete; \ const T *rightarrStorage = right.getArrayStorage(rightarrDelete); \ const T *rightarrS = rightarrStorage; \ \ bool rightmaskDelete; \ const LogicalArrayElem *rightmaskStorage \ = right.getMaskStorage(rightmaskDelete); \ const LogicalArrayElem *rightmaskS = rightmaskStorage; \ \ size_t ntotal = left.nelements(); \ bool retval = false; \ bool foundOne = false; \ while (ntotal--) { \ if (*leftmaskS && *rightmaskS) { \ foundOne = true; \ if (*leftarrS OP *rightarrS) { \ retval = true; \ break; \ } \ } \ leftarrS++; \ leftmaskS++; \ rightarrS++; \ rightmaskS++; \ } \ \ left.freeArrayStorage(leftarrStorage, leftarrDelete); \ left.freeMaskStorage(leftmaskStorage, leftmaskDelete); \ right.freeArrayStorage(rightarrStorage, rightarrDelete); \ right.freeMaskStorage(rightmaskStorage, rightmaskDelete); \ \ if (!foundOne) { \ throw (ArrayError( \ "bool ::" STRANYFUNC "(const MaskedArray &," \ " const MaskedArray &)" \ " - AND of MaskedArray masks must have at least 1 element")); \ } \ \ return retval; \ } MARRLOGI_B_ANYFUNC_MA ( anyLE, <=, "anyLE" ) MARRLOGI_B_ANYFUNC_MA ( anyLT, <, "anyLT" ) MARRLOGI_B_ANYFUNC_MA ( anyGE, >=, "anyGE" ) MARRLOGI_B_ANYFUNC_MA ( anyGT, >, "anyGT" ) MARRLOGI_B_ANYFUNC_MA ( anyEQ, ==, "anyEQ" ) MARRLOGI_B_ANYFUNC_MA ( anyNE, !=, "anyNE" ) MARRLOGI_B_ANYFUNC_MA ( anyAND, &&, "anyAND" ) MARRLOGI_B_ANYFUNC_MA ( anyOR, ||, "anyOR" ) MARRLOGI_B_ANYFUNC_AM ( anyLE, <=, "anyLE" ) MARRLOGI_B_ANYFUNC_AM ( anyLT, <, "anyLT" ) MARRLOGI_B_ANYFUNC_AM ( anyGE, >=, "anyGE" ) MARRLOGI_B_ANYFUNC_AM ( anyGT, >, "anyGT" ) MARRLOGI_B_ANYFUNC_AM ( anyEQ, ==, "anyEQ" ) MARRLOGI_B_ANYFUNC_AM ( anyNE, !=, "anyNE" ) MARRLOGI_B_ANYFUNC_AM ( anyAND, &&, "anyAND" ) MARRLOGI_B_ANYFUNC_AM ( anyOR, ||, "anyOR" ) MARRLOGI_B_ANYFUNC_MM ( anyLE, <=, "anyLE" ) MARRLOGI_B_ANYFUNC_MM ( anyLT, <, "anyLT" ) MARRLOGI_B_ANYFUNC_MM ( anyGE, >=, "anyGE" ) MARRLOGI_B_ANYFUNC_MM ( anyGT, >, "anyGT" ) MARRLOGI_B_ANYFUNC_MM ( anyEQ, ==, "anyEQ" ) MARRLOGI_B_ANYFUNC_MM ( anyNE, !=, "anyNE" ) MARRLOGI_B_ANYFUNC_MM ( anyAND, &&, "anyAND" ) MARRLOGI_B_ANYFUNC_MM ( anyOR, ||, "anyOR" ) #define MARRLOGI_MLA_OP_MA(OP,STROP) \ template \ MaskedLogicalArray operator OP (const MaskedArray &left, \ const Array &right) \ { \ if (left.conform(right) == false) { \ throw (ArrayConformanceError \ ("MaskedLogicalArray ::" STROP \ "(const MaskedArray &, const Array &)" \ " - arrays do not conform")); \ } \ \ LogicalArray resultarr (left.shape()); \ resultarr = false; \ MaskedLogicalArray result (resultarr, left.getMask()); \ \ bool resultarrDelete; \ LogicalArrayElem *resultarrStorage = \ result.getRWArrayStorage(resultarrDelete); \ LogicalArrayElem *resultarrS = resultarrStorage; \ \ bool resultmaskDelete; \ const LogicalArrayElem *resultmaskStorage = \ result.getMaskStorage(resultmaskDelete); \ const LogicalArrayElem *resultmaskS = resultmaskStorage; \ \ bool leftarrDelete; \ const T *leftarrStorage = left.getArrayStorage(leftarrDelete); \ const T *leftarrS = leftarrStorage; \ \ bool rightDelete; \ const T *rightStorage = right.getStorage(rightDelete); \ const T *rightS = rightStorage; \ \ size_t ntotal = result.nelements(); \ while (ntotal--) { \ if (*resultmaskS) { \ *resultarrS = (LogicalArrayElem) (*leftarrS OP *rightS); \ } \ resultarrS++; \ resultmaskS++; \ leftarrS++; \ rightS++; \ } \ \ result.putArrayStorage(resultarrStorage, resultarrDelete); \ result.freeMaskStorage(resultmaskStorage, resultmaskDelete); \ left.freeArrayStorage(leftarrStorage, leftarrDelete); \ right.freeStorage(rightStorage, rightDelete); \ \ return result; \ } #define MARRLOGI_MLA_OP_AM(OP,STROP) \ template \ MaskedLogicalArray operator OP (const Array &left, \ const MaskedArray &right) \ { \ if (left.conform(right) == false) { \ throw (ArrayConformanceError \ ("MaskLogicalArray ::" STROP \ "(const Array &, const MaskedArray &)" \ " - arrays do not conform")); \ } \ \ LogicalArray resultarr (left.shape()); \ resultarr = false; \ MaskedLogicalArray result (resultarr, right.getMask()); \ \ bool resultarrDelete; \ LogicalArrayElem *resultarrStorage = \ result.getRWArrayStorage(resultarrDelete); \ LogicalArrayElem *resultarrS = resultarrStorage; \ \ bool resultmaskDelete; \ const LogicalArrayElem *resultmaskStorage = \ result.getMaskStorage(resultmaskDelete); \ const LogicalArrayElem *resultmaskS = resultmaskStorage; \ \ bool leftDelete; \ const T *leftStorage = left.getStorage(leftDelete); \ const T *leftS = leftStorage; \ \ bool rightarrDelete; \ const T *rightarrStorage = right.getArrayStorage(rightarrDelete); \ const T *rightarrS = rightarrStorage; \ \ size_t ntotal = result.nelements(); \ while (ntotal--) { \ if (*resultmaskS) { \ *resultarrS = (LogicalArrayElem) (*leftS OP *rightarrS); \ } \ resultarrS++; \ resultmaskS++; \ leftS++; \ rightarrS++; \ } \ \ result.putArrayStorage(resultarrStorage, resultarrDelete); \ result.freeMaskStorage(resultmaskStorage, resultmaskDelete); \ left.freeStorage(leftStorage, leftDelete); \ right.freeArrayStorage(rightarrStorage, rightarrDelete); \ \ return result; \ } #define MARRLOGI_MLA_OP_MM(OP,STROP) \ template \ MaskedLogicalArray operator OP (const MaskedArray &left, \ const MaskedArray &right) \ { \ if (left.conform(right) == false) { \ throw (ArrayConformanceError \ ("MaskLogicalArray ::" STROP \ "(const MaskedArray &, const MaskedArray &)" \ " - arrays do not conform")); \ } \ \ LogicalArray resultarr (left.shape()); \ resultarr = false; \ MaskedLogicalArray result (resultarr, \ (left.getMask() && right.getMask())); \ \ bool resultarrDelete; \ LogicalArrayElem *resultarrStorage = \ result.getRWArrayStorage(resultarrDelete); \ LogicalArrayElem *resultarrS = resultarrStorage; \ \ bool resultmaskDelete; \ const LogicalArrayElem *resultmaskStorage = \ result.getMaskStorage(resultmaskDelete); \ const LogicalArrayElem *resultmaskS = resultmaskStorage; \ \ bool leftarrDelete; \ const T *leftarrStorage = left.getArrayStorage(leftarrDelete); \ const T *leftarrS = leftarrStorage; \ \ bool rightarrDelete; \ const T *rightarrStorage = right.getArrayStorage(rightarrDelete); \ const T *rightarrS = rightarrStorage; \ \ size_t ntotal = result.nelements(); \ while (ntotal--) { \ if (*resultmaskS) { \ *resultarrS = (LogicalArrayElem) (*leftarrS OP *rightarrS); \ } \ resultarrS++; \ resultmaskS++; \ leftarrS++; \ rightarrS++; \ } \ \ result.putArrayStorage(resultarrStorage, resultarrDelete); \ result.freeMaskStorage(resultmaskStorage, resultmaskDelete); \ left.freeArrayStorage(leftarrStorage, leftarrDelete); \ right.freeArrayStorage(rightarrStorage, rightarrDelete); \ \ return result; \ } MARRLOGI_MLA_OP_MA ( <=, "<=" ) MARRLOGI_MLA_OP_MA ( <, "<" ) MARRLOGI_MLA_OP_MA ( >=, ">=" ) MARRLOGI_MLA_OP_MA ( >, ">" ) MARRLOGI_MLA_OP_MA ( ==, "==" ) MARRLOGI_MLA_OP_MA ( !=, "!=" ) MARRLOGI_MLA_OP_MA ( &&, "&&" ) MARRLOGI_MLA_OP_MA ( ||, "||" ) MARRLOGI_MLA_OP_AM ( <=, "<=" ) MARRLOGI_MLA_OP_AM ( <, "<" ) MARRLOGI_MLA_OP_AM ( >=, ">=" ) MARRLOGI_MLA_OP_AM ( >, ">" ) MARRLOGI_MLA_OP_AM ( ==, "==" ) MARRLOGI_MLA_OP_AM ( !=, "!=" ) MARRLOGI_MLA_OP_AM ( &&, "&&" ) MARRLOGI_MLA_OP_AM ( ||, "||" ) MARRLOGI_MLA_OP_MM ( <=, "<=" ) MARRLOGI_MLA_OP_MM ( <, "<" ) MARRLOGI_MLA_OP_MM ( >=, ">=" ) MARRLOGI_MLA_OP_MM ( >, ">" ) MARRLOGI_MLA_OP_MM ( ==, "==" ) MARRLOGI_MLA_OP_MM ( !=, "!=" ) MARRLOGI_MLA_OP_MM ( &&, "&&" ) MARRLOGI_MLA_OP_MM ( ||, "||" ) template MaskedLogicalArray operator ! (const MaskedArray &marray) { MaskedLogicalArray result (marray.copy()); bool resultarrDelete; LogicalArrayElem *resultarrStorage = result.getRWArrayStorage(resultarrDelete); LogicalArrayElem *resultarrS = resultarrStorage; bool resultmaskDelete; const LogicalArrayElem *resultmaskStorage = result.getMaskStorage(resultmaskDelete); const LogicalArrayElem *resultmaskS = resultmaskStorage; bool marrayarrDelete; const T *marrayarrStorage = marray.getArrayStorage(marrayarrDelete); const T *marrayarrS = marrayarrStorage; size_t ntotal = result.nelements(); while (ntotal--) { if (*resultmaskS) { *resultarrS = ((*marrayarrS) ? false : true); } resultarrS++; resultmaskS++; marrayarrS++; } result.putArrayStorage(resultarrStorage, resultarrDelete); result.freeMaskStorage(resultmaskStorage, resultmaskDelete); marray.freeArrayStorage(marrayarrStorage, marrayarrDelete); return result; } #define MARRLOGI_B_ALLFUNC_MS(ALLFUNC,OP,STRALLFUNC) \ template \ bool ALLFUNC (const MaskedArray &left, const T &right) \ { \ bool leftarrDelete; \ const T *leftarrStorage = left.getArrayStorage(leftarrDelete); \ const T *leftarrS = leftarrStorage; \ \ bool leftmaskDelete; \ const LogicalArrayElem *leftmaskStorage = \ left.getMaskStorage(leftmaskDelete); \ const LogicalArrayElem *leftmaskS = leftmaskStorage; \ \ size_t ntotal = left.nelements(); \ bool retval = true; \ bool foundOne = false; \ while (ntotal--) { \ if (*leftmaskS) { \ foundOne = true; \ if (! (*leftarrS OP right) ) { \ retval = false; \ break; \ } \ } \ leftarrS++; \ leftmaskS++; \ } \ \ left.freeArrayStorage(leftarrStorage, leftarrDelete); \ left.freeMaskStorage(leftmaskStorage, leftmaskDelete); \ \ if (!foundOne) { \ throw (ArrayError( \ "bool ::" STRALLFUNC "(const MaskedArray &, const T)" \ " - Need at least 1 unmasked element")); \ } \ \ return retval; \ } #define MARRLOGI_B_ALLFUNC_SM(ALLFUNC,OP,STRALLFUNC) \ template \ bool ALLFUNC (const T &left, const MaskedArray &right) \ { \ bool rightarrDelete; \ const T *rightarrStorage = right.getArrayStorage(rightarrDelete); \ const T *rightarrS = rightarrStorage; \ \ bool rightmaskDelete; \ const LogicalArrayElem *rightmaskStorage = \ right.getMaskStorage(rightmaskDelete); \ const LogicalArrayElem *rightmaskS = rightmaskStorage; \ \ size_t ntotal = right.nelements(); \ bool retval = true; \ bool foundOne = false; \ while (ntotal--) { \ if (*rightmaskS) { \ foundOne = true; \ if (! (left OP *rightarrS) ) { \ retval = false; \ break; \ } \ } \ rightarrS++; \ rightmaskS++; \ } \ \ right.freeArrayStorage(rightarrStorage, rightarrDelete); \ right.freeMaskStorage(rightmaskStorage, rightmaskDelete); \ \ if (!foundOne) { \ throw (ArrayError( \ "bool ::" STRALLFUNC "(const T, const MaskedArray &)" \ " - Need at least 1 unmasked element")); \ } \ \ return retval; \ } MARRLOGI_B_ALLFUNC_MS ( allLE, <=, "allLE" ) MARRLOGI_B_ALLFUNC_SM ( allLE, <=, "allLE" ) MARRLOGI_B_ALLFUNC_MS ( allLT, <, "allLT" ) MARRLOGI_B_ALLFUNC_SM ( allLT, <, "allLT" ) MARRLOGI_B_ALLFUNC_MS ( allGE, >=, "allGE" ) MARRLOGI_B_ALLFUNC_SM ( allGE, >=, "allGE" ) MARRLOGI_B_ALLFUNC_MS ( allGT, >, "allGT" ) MARRLOGI_B_ALLFUNC_SM ( allGT, >, "allGT" ) MARRLOGI_B_ALLFUNC_MS ( allEQ, ==, "allEQ" ) MARRLOGI_B_ALLFUNC_SM ( allEQ, ==, "allEQ" ) MARRLOGI_B_ALLFUNC_MS ( allNE, !=, "allNE" ) MARRLOGI_B_ALLFUNC_SM ( allNE, !=, "allNE" ) template bool allAND (const MaskedArray &marray, const T &val) { if (!val) { return false; } else { bool marrayarrDelete; const T *marrayarrStorage = marray.getArrayStorage(marrayarrDelete); const T *marrayarrS = marrayarrStorage; bool marraymaskDelete; const LogicalArrayElem *marraymaskStorage = marray.getMaskStorage(marraymaskDelete); const LogicalArrayElem *marraymaskS = marraymaskStorage; size_t ntotal = marray.nelements(); bool retval = true; bool foundOne = false; while (ntotal--) { if (*marraymaskS) { foundOne = true; if (! *marrayarrS) { retval = false; break; } } marrayarrS++; marraymaskS++; } marray.freeArrayStorage(marrayarrStorage, marrayarrDelete); marray.freeMaskStorage(marraymaskStorage, marraymaskDelete); if (!foundOne) { throw (ArrayError( "bool ::allAND(const MaskedArray &, const T)" " - Need at least 1 unmasked element")); } return retval; } } template bool allAND (const T &val, const MaskedArray &marray) { if (!val) { return false; } else { bool marrayarrDelete; const T *marrayarrStorage = marray.getArrayStorage(marrayarrDelete); const T *marrayarrS = marrayarrStorage; bool marraymaskDelete; const LogicalArrayElem *marraymaskStorage = marray.getMaskStorage(marraymaskDelete); const LogicalArrayElem *marraymaskS = marraymaskStorage; size_t ntotal = marray.nelements(); bool retval = true; bool foundOne = false; while (ntotal--) { if (*marraymaskS) { foundOne = true; if (! *marrayarrS) { retval = false; break; } } marrayarrS++; marraymaskS++; } marray.freeArrayStorage(marrayarrStorage, marrayarrDelete); marray.freeMaskStorage(marraymaskStorage, marraymaskDelete); if (!foundOne) { throw (ArrayError( "bool ::allAND(const T, const MaskedArray &)" " - Need at least 1 unmasked element")); } return retval; } } template bool allOR (const MaskedArray &marray, const T &val) { if (val) { return true; } else { bool marrayarrDelete; const T *marrayarrStorage = marray.getArrayStorage(marrayarrDelete); const T *marrayarrS = marrayarrStorage; bool marraymaskDelete; const LogicalArrayElem *marraymaskStorage = marray.getMaskStorage(marraymaskDelete); const LogicalArrayElem *marraymaskS = marraymaskStorage; size_t ntotal = marray.nelements(); bool retval = true; bool foundOne = false; while (ntotal--) { if (*marraymaskS) { foundOne = true; if (! *marrayarrS) { retval = false; break; } } marrayarrS++; marraymaskS++; } marray.freeArrayStorage(marrayarrStorage, marrayarrDelete); marray.freeMaskStorage(marraymaskStorage, marraymaskDelete); if (!foundOne) { throw (ArrayError( "bool ::allOR(const MaskedArray &, const T)" " - Need at least 1 unmasked element")); } return retval; } } template bool allOR (const T &val, const MaskedArray &marray) { if (val) { return true; } else { bool marrayarrDelete; const T *marrayarrStorage = marray.getArrayStorage(marrayarrDelete); const T *marrayarrS = marrayarrStorage; bool marraymaskDelete; const LogicalArrayElem *marraymaskStorage = marray.getMaskStorage(marraymaskDelete); const LogicalArrayElem *marraymaskS = marraymaskStorage; size_t ntotal = marray.nelements(); bool retval = true; bool foundOne = false; while (ntotal--) { if (*marraymaskS) { foundOne = true; if (! *marrayarrS) { retval = false; break; } } marrayarrS++; marraymaskS++; } marray.freeArrayStorage(marrayarrStorage, marrayarrDelete); marray.freeMaskStorage(marraymaskStorage, marraymaskDelete); if (!foundOne) { throw (ArrayError( "bool ::allOR(const T, const MaskedArray &)" " - Need at least 1 unmasked element")); } return retval; } } #define MARRLOGI_B_ANYFUNC_MS(ANYFUNC,OP,STRANYFUNC) \ template \ bool ANYFUNC (const MaskedArray &left, const T &right) \ { \ bool leftarrDelete; \ const T *leftarrStorage = left.getArrayStorage(leftarrDelete); \ const T *leftarrS = leftarrStorage; \ \ bool leftmaskDelete; \ const LogicalArrayElem *leftmaskStorage = \ left.getMaskStorage(leftmaskDelete); \ const LogicalArrayElem *leftmaskS = leftmaskStorage; \ \ size_t ntotal = left.nelements(); \ bool retval = false; \ bool foundOne = false; \ while (ntotal--) { \ if (*leftmaskS) { \ foundOne = true; \ if (*leftarrS OP right) { \ retval = true; \ break; \ } \ } \ leftarrS++; \ leftmaskS++; \ } \ \ left.freeArrayStorage(leftarrStorage, leftarrDelete); \ left.freeMaskStorage(leftmaskStorage, leftmaskDelete); \ \ if (!foundOne) { \ throw (ArrayError( \ "bool ::" STRANYFUNC "(const MaskedArray &, const T)" \ " - Need at least 1 unmasked element")); \ } \ \ return retval; \ } #define MARRLOGI_B_ANYFUNC_SM(ANYFUNC,OP,STRANYFUNC) \ template \ bool ANYFUNC (const T &left, const MaskedArray &right) \ { \ bool rightarrDelete; \ const T *rightarrStorage = right.getArrayStorage(rightarrDelete); \ const T *rightarrS = rightarrStorage; \ \ bool rightmaskDelete; \ const LogicalArrayElem *rightmaskStorage = \ right.getMaskStorage(rightmaskDelete); \ const LogicalArrayElem *rightmaskS = rightmaskStorage; \ \ size_t ntotal = right.nelements(); \ bool retval = false; \ bool foundOne = false; \ while (ntotal--) { \ if (*rightmaskS) { \ foundOne = true; \ if (left OP *rightarrS) { \ retval = true; \ break; \ } \ } \ rightarrS++; \ rightmaskS++; \ } \ \ right.freeArrayStorage(rightarrStorage, rightarrDelete); \ right.freeMaskStorage(rightmaskStorage, rightmaskDelete); \ \ if (!foundOne) { \ throw (ArrayError( \ "bool ::" STRANYFUNC "(const T, const MaskedArray &)" \ " - Need at least 1 unmasked element")); \ } \ \ return retval; \ } MARRLOGI_B_ANYFUNC_MS ( anyLE, <=, "anyLE" ) MARRLOGI_B_ANYFUNC_SM ( anyLE, <=, "anyLE" ) MARRLOGI_B_ANYFUNC_MS ( anyLT, <, "anyLT" ) MARRLOGI_B_ANYFUNC_SM ( anyLT, <, "anyLT" ) MARRLOGI_B_ANYFUNC_MS ( anyGE, >=, "anyGE" ) MARRLOGI_B_ANYFUNC_SM ( anyGE, >=, "anyGE" ) MARRLOGI_B_ANYFUNC_MS ( anyGT, >, "anyGT" ) MARRLOGI_B_ANYFUNC_SM ( anyGT, >, "anyGT" ) MARRLOGI_B_ANYFUNC_MS ( anyEQ, ==, "anyEQ" ) MARRLOGI_B_ANYFUNC_SM ( anyEQ, ==, "anyEQ" ) MARRLOGI_B_ANYFUNC_MS ( anyNE, !=, "anyNE" ) MARRLOGI_B_ANYFUNC_SM ( anyNE, !=, "anyNE" ) template bool anyAND (const MaskedArray &marray, const T &val) { if (!val) { return false; } else { bool marrayarrDelete; const T *marrayarrStorage = marray.getArrayStorage(marrayarrDelete); const T *marrayarrS = marrayarrStorage; bool marraymaskDelete; const LogicalArrayElem *marraymaskStorage = marray.getMaskStorage(marraymaskDelete); const LogicalArrayElem *marraymaskS = marraymaskStorage; size_t ntotal = marray.nelements(); bool retval = false; bool foundOne = false; while (ntotal--) { if (*marraymaskS) { foundOne = true; if (*marrayarrS) { retval = true; break; } } marrayarrS++; marraymaskS++; } marray.freeArrayStorage(marrayarrStorage, marrayarrDelete); marray.freeMaskStorage(marraymaskStorage, marraymaskDelete); if (!foundOne) { throw (ArrayError( "bool ::anyAND(const MaskedArray &, const T)" " - Need at least 1 unmasked element")); } return retval; } } template bool anyAND (const T &val, const MaskedArray &marray) { if (!val) { return false; } else { bool marrayarrDelete; const T *marrayarrStorage = marray.getArrayStorage(marrayarrDelete); const T *marrayarrS = marrayarrStorage; bool marraymaskDelete; const LogicalArrayElem *marraymaskStorage = marray.getMaskStorage(marraymaskDelete); const LogicalArrayElem *marraymaskS = marraymaskStorage; size_t ntotal = marray.nelements(); bool retval = false; bool foundOne = false; while (ntotal--) { if (*marraymaskS) { foundOne = true; if (*marrayarrS) { retval = true; break; } } marrayarrS++; marraymaskS++; } marray.freeArrayStorage(marrayarrStorage, marrayarrDelete); marray.freeMaskStorage(marraymaskStorage, marraymaskDelete); if (!foundOne) { throw (ArrayError( "bool ::anyAND(const T, const MaskedArray &)" " - Need at least 1 unmasked element")); } return retval; } } template bool anyOR (const MaskedArray &marray, const T &val) { if (val) { return true; } else { bool marrayarrDelete; const T *marrayarrStorage = marray.getArrayStorage(marrayarrDelete); const T *marrayarrS = marrayarrStorage; bool marraymaskDelete; const LogicalArrayElem *marraymaskStorage = marray.getMaskStorage(marraymaskDelete); const LogicalArrayElem *marraymaskS = marraymaskStorage; size_t ntotal = marray.nelements(); bool retval = false; bool foundOne = false; while (ntotal--) { if (*marraymaskS) { foundOne = true; if (*marrayarrS) { retval = true; break; } } marrayarrS++; marraymaskS++; } marray.freeArrayStorage(marrayarrStorage, marrayarrDelete); marray.freeMaskStorage(marraymaskStorage, marraymaskDelete); if (!foundOne) { throw (ArrayError( "bool ::anyOR(const MaskedArray &, const T)" " - Need at least 1 unmasked element")); } return retval; } } template bool anyOR (const T &val, const MaskedArray &marray) { if (val) { return true; } else { bool marrayarrDelete; const T *marrayarrStorage = marray.getArrayStorage(marrayarrDelete); const T *marrayarrS = marrayarrStorage; bool marraymaskDelete; const LogicalArrayElem *marraymaskStorage = marray.getMaskStorage(marraymaskDelete); const LogicalArrayElem *marraymaskS = marraymaskStorage; size_t ntotal = marray.nelements(); bool retval = false; bool foundOne = false; while (ntotal--) { if (*marraymaskS) { foundOne = true; if (*marrayarrS) { retval = true; break; } } marrayarrS++; marraymaskS++; } marray.freeArrayStorage(marrayarrStorage, marrayarrDelete); marray.freeMaskStorage(marraymaskStorage, marraymaskDelete); if (!foundOne) { throw (ArrayError( "bool ::anyOR(const T, const MaskedArray &)" " - Need at least 1 unmasked element")); } return retval; } } #define MARRLOGI_MLA_OP_MS(OP) \ template \ MaskedLogicalArray operator OP (const MaskedArray &left, \ const T &right) \ { \ LogicalArray resultarr (left.shape()); \ resultarr = false; \ MaskedLogicalArray result (resultarr, left.getMask()); \ \ bool resultarrDelete; \ LogicalArrayElem *resultarrStorage = \ result.getRWArrayStorage(resultarrDelete); \ LogicalArrayElem *resultarrS = resultarrStorage; \ \ bool resultmaskDelete; \ const LogicalArrayElem *resultmaskStorage = \ result.getMaskStorage(resultmaskDelete); \ const LogicalArrayElem *resultmaskS = resultmaskStorage; \ \ bool leftarrDelete; \ const T *leftarrStorage = left.getArrayStorage(leftarrDelete); \ const T *leftarrS = leftarrStorage; \ \ size_t ntotal = result.nelements(); \ while (ntotal--) { \ if (*resultmaskS) { \ *resultarrS = (LogicalArrayElem) (*leftarrS OP right); \ } \ resultarrS++; \ resultmaskS++; \ leftarrS++; \ } \ \ result.putArrayStorage(resultarrStorage, resultarrDelete); \ result.freeMaskStorage(resultmaskStorage, resultmaskDelete); \ left.freeArrayStorage(leftarrStorage, leftarrDelete); \ \ return result; \ } #define MARRLOGI_MLA_OP_SM(OP) \ template \ MaskedLogicalArray operator OP (const T &left, \ const MaskedArray &right) \ { \ LogicalArray resultarr (right.shape()); \ resultarr = false; \ MaskedLogicalArray result (resultarr, right.getMask()); \ \ bool resultarrDelete; \ LogicalArrayElem *resultarrStorage = \ result.getRWArrayStorage(resultarrDelete); \ LogicalArrayElem *resultarrS = resultarrStorage; \ \ bool resultmaskDelete; \ const LogicalArrayElem *resultmaskStorage = \ result.getMaskStorage(resultmaskDelete); \ const LogicalArrayElem *resultmaskS = resultmaskStorage; \ \ bool rightarrDelete; \ const T *rightarrStorage = right.getArrayStorage(rightarrDelete); \ const T *rightarrS = rightarrStorage; \ \ size_t ntotal = result.nelements(); \ while (ntotal--) { \ if (*resultmaskS) { \ *resultarrS = (LogicalArrayElem) (left OP *rightarrS); \ } \ resultarrS++; \ resultmaskS++; \ rightarrS++; \ } \ \ result.putArrayStorage(resultarrStorage, resultarrDelete); \ result.freeMaskStorage(resultmaskStorage, resultmaskDelete); \ right.freeArrayStorage(rightarrStorage, rightarrDelete); \ \ return result; \ } MARRLOGI_MLA_OP_MS ( <= ) MARRLOGI_MLA_OP_SM ( <= ) MARRLOGI_MLA_OP_MS ( < ) MARRLOGI_MLA_OP_SM ( < ) MARRLOGI_MLA_OP_MS ( >= ) MARRLOGI_MLA_OP_SM ( >= ) MARRLOGI_MLA_OP_MS ( > ) MARRLOGI_MLA_OP_SM ( > ) MARRLOGI_MLA_OP_MS ( == ) MARRLOGI_MLA_OP_SM ( == ) MARRLOGI_MLA_OP_MS ( != ) MARRLOGI_MLA_OP_SM ( != ) template MaskedLogicalArray operator && (const MaskedArray &marray, const T &val) { LogicalArray resultarr (marray.shape()); resultarr = false; MaskedLogicalArray result (resultarr, marray.getMask()); if (val) { bool resultarrDelete; LogicalArrayElem *resultarrStorage = result.getRWArrayStorage(resultarrDelete); LogicalArrayElem *resultarrS = resultarrStorage; bool resultmaskDelete; const LogicalArrayElem *resultmaskStorage = result.getMaskStorage(resultmaskDelete); const LogicalArrayElem *resultmaskS = resultmaskStorage; bool marrayarrDelete; const T *marrayarrStorage = marray.getArrayStorage(marrayarrDelete); const T *marrayarrS = marrayarrStorage; size_t ntotal = result.nelements(); while (ntotal--) { if (*resultmaskS) { *resultarrS = ((*marrayarrS) ? true : false); } resultarrS++; resultmaskS++; marrayarrS++; } result.putArrayStorage(resultarrStorage, resultarrDelete); result.freeMaskStorage(resultmaskStorage, resultmaskDelete); marray.freeArrayStorage(marrayarrStorage, marrayarrDelete); } return result; } template MaskedLogicalArray operator && (const T &val, const MaskedArray &marray) { LogicalArray resultarr (marray.shape()); resultarr = false; MaskedLogicalArray result (resultarr, marray.getMask()); if (val) { bool resultarrDelete; LogicalArrayElem *resultarrStorage = result.getRWArrayStorage(resultarrDelete); LogicalArrayElem *resultarrS = resultarrStorage; bool resultmaskDelete; const LogicalArrayElem *resultmaskStorage = result.getMaskStorage(resultmaskDelete); const LogicalArrayElem *resultmaskS = resultmaskStorage; bool marrayarrDelete; const T *marrayarrStorage = marray.getArrayStorage(marrayarrDelete); const T *marrayarrS = marrayarrStorage; size_t ntotal = result.nelements(); while (ntotal--) { if (*resultmaskS) { *resultarrS = ((*marrayarrS) ? true : false); } resultarrS++; resultmaskS++; marrayarrS++; } result.putArrayStorage(resultarrStorage, resultarrDelete); result.freeMaskStorage(resultmaskStorage, resultmaskDelete); marray.freeArrayStorage(marrayarrStorage, marrayarrDelete); } return result; } template MaskedLogicalArray operator || (const MaskedArray &marray, const T &val) { LogicalArray resultarr (marray.shape()); resultarr = false; MaskedLogicalArray result (resultarr, marray.getMask()); if (val) { result = true; } else { bool resultarrDelete; LogicalArrayElem *resultarrStorage = result.getRWArrayStorage(resultarrDelete); LogicalArrayElem *resultarrS = resultarrStorage; bool resultmaskDelete; const LogicalArrayElem *resultmaskStorage = result.getMaskStorage(resultmaskDelete); const LogicalArrayElem *resultmaskS = resultmaskStorage; bool marrayarrDelete; const T *marrayarrStorage = marray.getArrayStorage(marrayarrDelete); const T *marrayarrS = marrayarrStorage; size_t ntotal = result.nelements(); while (ntotal--) { if (*resultmaskS) { *resultarrS = ((*marrayarrS) ? true : false); } resultarrS++; resultmaskS++; marrayarrS++; } result.putArrayStorage(resultarrStorage, resultarrDelete); result.freeMaskStorage(resultmaskStorage, resultmaskDelete); marray.freeArrayStorage(marrayarrStorage, marrayarrDelete); } return result; } template MaskedLogicalArray operator || (const T &val, const MaskedArray &marray) { LogicalArray resultarr (marray.shape()); resultarr = false; MaskedLogicalArray result (resultarr, marray.getMask()); if (val) { result = true; } else { bool resultarrDelete; LogicalArrayElem *resultarrStorage = result.getRWArrayStorage(resultarrDelete); LogicalArrayElem *resultarrS = resultarrStorage; bool resultmaskDelete; const LogicalArrayElem *resultmaskStorage = result.getMaskStorage(resultmaskDelete); const LogicalArrayElem *resultmaskS = resultmaskStorage; bool marrayarrDelete; const T *marrayarrStorage = marray.getArrayStorage(marrayarrDelete); const T *marrayarrS = marrayarrStorage; size_t ntotal = result.nelements(); while (ntotal--) { if (*resultmaskS) { *resultarrS = ((*marrayarrS) ? true : false); } resultarrS++; resultmaskS++; marrayarrS++; } result.putArrayStorage(resultarrStorage, resultarrDelete); result.freeMaskStorage(resultmaskStorage, resultmaskDelete); marray.freeArrayStorage(marrayarrStorage, marrayarrDelete); } return result; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Arrays/MaskArrMath.h000066400000000000000000000557311476623553700200320ustar00rootroot00000000000000//# MaskArrMath.h: Simple mathematics done with MaskedArray's. //# Copyright (C) 1993,1994,1995,1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MASKARRMATH_2_H #define CASA_MASKARRMATH_2_H #include "Array.h" #include "MaskedArray.h" #include "IPosition.h" namespace casacore { //# NAMESPACE CASACORE - BEGIN // Mathematical operations for MaskedArrays (and with Arrays) // // // //
      • Array //
      • MaskedArray // // // // MaskArrMath is short for MaskedArrayMath, which is too long by the old // AIPS++ file naming conventions. This file contains global functions // which perform element by element mathematical operations on masked arrays. // // // // These functions perform element by element mathematical operations on // masked arrays. With two arrays, they must both conform, and the result // is done element by element, for those locations where the mask of the // MaskedArray is true. For two MaskedArrays, the "and" of the masks is used. // // // // // Vector a(10); // Vector b(10); // Vector c(10); // . . . // c = a(a>0) + b(b>0); // // This example sets those elements of c where ((a>0) && (b>0)) to (a+b). // Elements of c where !((a>0) && (b>0)) are unchanged. The result of // this operation is a MaskedArray. The assignment from this // MaskedArray to the Vector c only assigns those elements // where the mask is true. // // // // // Vector a(10); // Vector b(10); // Vector c(10); // . . . // c = atan2 (a, b(b>0); // // This example sets those elements of c where (b>0) to atan2 (a,b). // Elements of c where !(b>0) are unchanged. The result of // this operation is a MaskedArray. The assignment from this // MaskedArray to the Vector c only assigns those elements // where the mask is true. // // // // // Vector a(10); // int result; // . . . // result = sum (a(a>0)); // // This example sums a, for those elements of a which are greater than 0. // // // // One wants to be able to mask arrays and perform mathematical operations on // those masked arrays. Since the masked arrays are only defined where // the masks are true, the result must be a MaskedArray, or a simple number. // // // // MaskedArray mathematical operations -- Mathematical // operations for MaskedArrays, and between MaskedArrays and Arrays. // // // // Element by element arithmetic modifying left in-place. left and other // must be conformant. // // //
      • ArrayConformanceError // // // template const MaskedArray & operator+= (const MaskedArray &left, const Array &other); template const MaskedArray & operator-= (const MaskedArray &left, const Array &other); template const MaskedArray & operator*= (const MaskedArray &left, const Array &other); template const MaskedArray & operator/= (const MaskedArray &left, const Array &other); template Array & operator+= (Array &left, const MaskedArray &other); template Array & operator-= (Array &left, const MaskedArray &other); template Array & operator*= (Array &left, const MaskedArray &other); template Array & operator/= (Array &left, const MaskedArray &other); template const MaskedArray & operator+= (const MaskedArray &left, const MaskedArray &other); template const MaskedArray & operator-= (const MaskedArray &left, const MaskedArray &other); template const MaskedArray & operator*= (const MaskedArray &left, const MaskedArray &other); template const MaskedArray & operator/= (const MaskedArray &left,const MaskedArray &other); template const MaskedArray & operator/= (const MaskedArray &left,const MaskedArray &other); // // // Element by element arithmetic modifying left in-place. The scalar "other" // behaves as if it were a conformant Array to left filled with constant values. // template const MaskedArray & operator+= (const MaskedArray &left,const T &other); template const MaskedArray & operator-= (const MaskedArray &left,const T &other); template const MaskedArray & operator*= (const MaskedArray &left,const T &other); template const MaskedArray & operator/= (const MaskedArray &left,const T &other); // // Unary arithmetic operation. // // template MaskedArray operator+(const MaskedArray &a); template MaskedArray operator-(const MaskedArray &a); // // // Element by element arithmetic on MaskedArrays, returns a MaskedArray. // // //
      • ArrayConformanceError // // // template MaskedArray operator+ (const MaskedArray &left, const Array &right); template MaskedArray operator- (const MaskedArray &left, const Array &right); template MaskedArray operator* (const MaskedArray &left, const Array &right); template MaskedArray operator/ (const MaskedArray &left, const Array &right); template MaskedArray operator+ (const Array &left, const MaskedArray &right); template MaskedArray operator- (const Array &left, const MaskedArray &right); template MaskedArray operator* (const Array &left, const MaskedArray &right); template MaskedArray operator/ (const Array &left, const MaskedArray &right); template MaskedArray operator+ (const MaskedArray &left,const MaskedArray &right); template MaskedArray operator- (const MaskedArray &left,const MaskedArray &right); template MaskedArray operator* (const MaskedArray &left,const MaskedArray &right); template MaskedArray operator/ (const MaskedArray &left,const MaskedArray &right); // // // Element by element arithmetic between a MaskedArray and a scalar, returning // a MaskedArray. // template MaskedArray operator+ (const MaskedArray &left, const T &right); template MaskedArray operator- (const MaskedArray &left, const T &right); template MaskedArray operator* (const MaskedArray &left, const T &right); template MaskedArray operator/ (const MaskedArray &left, const T &right); MaskedArray> operator* (const MaskedArray> &left, const float &right); // // // Element by element arithmetic between a scalar and a MaskedArray, returning // a MaskedArray. // template MaskedArray operator+ (const T &left, const MaskedArray &right); template MaskedArray operator- (const T &left, const MaskedArray &right); template MaskedArray operator* (const T &left, const MaskedArray &right); template MaskedArray operator/ (const T &left, const MaskedArray &right); MaskedArray> operator* (const float &left, const MaskedArray> &right); // // // Transcendental function applied to the array on an element-by-element // basis. Although a template function, this may not make sense for all // numeric types. // template MaskedArray sin(const MaskedArray &left); template MaskedArray cos(const MaskedArray &left); template MaskedArray tan(const MaskedArray &left); template MaskedArray asin(const MaskedArray &left); template MaskedArray acos(const MaskedArray &left); template MaskedArray atan(const MaskedArray &left); template MaskedArray sinh(const MaskedArray &left); template MaskedArray cosh(const MaskedArray &left); template MaskedArray tanh(const MaskedArray &left); template MaskedArray exp(const MaskedArray &left); template MaskedArray log(const MaskedArray &left); template MaskedArray log10(const MaskedArray &left); template MaskedArray sqrt(const MaskedArray &left); template MaskedArray abs(const MaskedArray &left); template MaskedArray fabs(const MaskedArray &left); template MaskedArray ceil(const MaskedArray &left); template MaskedArray floor(const MaskedArray &left); // // Transcendental functions requiring two arguments applied on an element-by-element // basis. Although a template function, this may not make sense for all // numeric types. // //
      • ArrayConformanceError // // // template MaskedArray atan2(const MaskedArray &left, const Array &right); template MaskedArray fmod(const MaskedArray &left, const Array &right); template MaskedArray atan2(const Array &left, const MaskedArray &right); template MaskedArray fmod(const Array &left, const MaskedArray &right); template MaskedArray atan2(const MaskedArray &left,const MaskedArray &right); template MaskedArray fmod(const MaskedArray &left,const MaskedArray &right); template MaskedArray atan2(const MaskedArray &left, const T &right); template MaskedArray fmod(const MaskedArray &left, const T &right); template MaskedArray atan2(const T &left, const MaskedArray &right); template MaskedArray fmod(const T &left, const MaskedArray &right); template MaskedArray pow(const MaskedArray &left, const Array &right); template MaskedArray pow(const Array &left, const MaskedArray &right); template MaskedArray pow(const MaskedArray &left,const MaskedArray &right); template MaskedArray pow(const MaskedArray &left, const double &right); // // Extracts the real part of a complex array into an array of floats. template MaskedArray real(const MaskedArray> &carray) { return MaskedArray (real(carray.getArray()), carray.getMask()); } // // Extracts the imaginary part of a complex array into an array of floats. template MaskedArray imag(const MaskedArray> &carray) { return MaskedArray (imag(carray.getArray()), carray.getMask()); } // // Find the minimum and maximum values of a MaskedArray. // Also find the IPositions of the minimum and maximum values. // // //
      • ArrayError // // // template void minMax(T &minVal, T &maxVal, IPosition &minPos, IPosition &maxPos,const MaskedArray &marray); template void minMax(T &minVal, T &maxVal,const MaskedArray &marray); // // // The "min" and "max" functions require that the type "T" have comparison // operators. // The minimum element of the array. template T min(const MaskedArray &left); // Return an array that contains the minimum of "left" and "right" at each // position. // // "left" and "right" must be conformant. // // //
      • ArrayError // // template MaskedArray min(const MaskedArray &left, const Array &right); template MaskedArray min(const Array &left, const MaskedArray &right); template MaskedArray min(const MaskedArray &left, const MaskedArray &right); template MaskedArray min(const T &left, const MaskedArray &right); template MaskedArray min(const MaskedArray &left, const T &right); // // "result" contains the minimum of "left" and "right" at each position. // "result", "left", and "right" must be conformant. // // //
      • ArrayConformanceError // // template void min(const MaskedArray &result, const Array &left, const Array &right); // The maximum element of the array. template T max(const MaskedArray &left); // Return an array that contains the maximum of "left" and "right" at each // position. // // "left" and "right" must be conformant. // //
      • ArrayError // // // template MaskedArray max(const MaskedArray &left, const Array &right); template MaskedArray max(const Array &left, const MaskedArray &right); template MaskedArray max(const MaskedArray &left, const MaskedArray &right); template MaskedArray max(const T &left, const MaskedArray &right); template MaskedArray max(const MaskedArray &left, const T &right); // // "result" contains the maximum of "left" and "right" at each position. // "result", "left", and "right" must be conformant. // // //
      • ArrayConformanceError // // template void max(const MaskedArray &result,const Array &left, const Array &right); // // Fills all elements of "array" where the mask is true with a sequence // starting with "start" and incrementing by "inc" for each element // where the mask is true. // The first axis varies most rapidly. template void indgen(MaskedArray &a, T start, T inc); // // Fills all elements of "array" where the mask is true with a sequence // starting with 0 and incremented by one for each element // where the mask is true. // The first axis varies most rapidly. template void indgen(MaskedArray &a); // // Fills all elements of "array" where the mask is true with a sequence // starting with "start" and incremented by one for each element // where the mask is true. // The first axis varies most rapidly. template void indgen(MaskedArray &a, T start); // //
      • ArrayError // // // Sum of every element of the MaskedArray where the Mask is true. template T sum(const MaskedArray &a); // // Sum of the squares of every element of the MaskedArray where the Mask is true. template T sumsquares(const MaskedArray &a); // // Product of every element of the MaskedArray where the Mask is true. // This could of course easily overflow. template T product(const MaskedArray &a); // // The mean of "a" is the sum of all elements of "a" divided by the number // of elements of "a". template T mean(const MaskedArray &a); // // The variance of "a" is the sum of (a(i) - mean(a))**2/(a.nelements() - ddof). // Similar to numpy the argument ddof tells if the population variance (ddof=0) // or the sample variance (ddof=1) is taken. // The variance functions proper use ddof=1. //
        Note that for a complex valued T the absolute values are used; in that way // the variance is equal to the sum of the variances of the real and imaginary parts. // Hence the imaginary part in the return value is 0. template T variance(const MaskedArray &a); template T pvariance(const MaskedArray &a, size_t ddof=0); // Rather than using a computed mean, use the supplied value. template T variance(const MaskedArray &a, T mean); template T pvariance(const MaskedArray &a, T mean, size_t ddof=0); // The standard deviation of "a" is the square root of its variance. template T stddev(const MaskedArray &a); template T pstddev(const MaskedArray &a, size_t ddof=0); template T stddev(const MaskedArray &a, T mean); template T pstddev(const MaskedArray &a, T mean, size_t ddof=0); // // The average deviation of "a" is the sum of abs(a(i) - mean(a))/N. (N.B. // N, not N-1 in the denominator). template T avdev(const MaskedArray &a); // // The average deviation of "a" is the sum of abs(a(i) - mean(a))/N. (N.B. // N, not N-1 in the denominator). // Rather than using a computed mean, use the supplied value. template T avdev(const MaskedArray &a,T mean); // // The root-mean-square of "a" is the sqrt of sum(a*a)/N. template T rms(const MaskedArray &a); // // The median of "a" is a(n/2). // When a has an even number of elements and the switch takeEvenMean is set, // the median is 0.5*(a(n/2) + a((n+1)/2)). // According to Numerical Recipes (2nd edition) it makes little sense to take // the mean when the array is large enough (> 100 elements). Therefore // the default for takeEvenMean is false when the array has > 100 elements, // otherwise it is true. //
        If "sorted"==true we assume the data is already sorted and we // compute the median directly. Otherwise the function GenSort::kthLargest // is used to find the median (kthLargest is about 6 times faster // than a full quicksort). // template inline T median(const MaskedArray &a, bool sorted=false) { return median (a, sorted, (a.nelements() <= 100)); } template T median(const MaskedArray &a, bool sorted, bool takeEvenMean); // // The median absolute deviation from the median. Interface is as for // the median functions // template inline T madfm(const MaskedArray &a, bool sorted=false) { return madfm (a, sorted, (a.nelements() <= 100)); } template T madfm(const MaskedArray &a, bool sorted, bool takeEvenMean); // // Returns a MaskedArray where every element is squared. template MaskedArray square(const MaskedArray &val); // Returns a MaskedArray where every element is cubed. template MaskedArray cube(const MaskedArray &val); // template class MaskedSumFunc { public: T operator() (const MaskedArray& arr) const { return sum(arr); } }; template class MaskedProductFunc { public: T operator() (const MaskedArray& arr) const { return product(arr); } }; template class MaskedMinFunc { public: T operator() (const MaskedArray& arr) const { return min(arr); } }; template class MaskedMaxFunc { public: T operator() (const MaskedArray& arr) const { return max(arr); } }; template class MaskedMeanFunc { public: T operator() (const MaskedArray& arr) const { return mean(arr); } }; template class MaskedVarianceFunc { public: T operator() (const MaskedArray& arr) const { return variance(arr); } }; template class MaskedStddevFunc { public: T operator() (const MaskedArray& arr) const { return stddev(arr); } }; template class MaskedAvdevFunc { public: T operator() (const MaskedArray& arr) const { return avdev(arr); } }; template class MaskedRmsFunc { public: T operator() (const MaskedArray& arr) const { return rms(arr); } }; template class MaskedMedianFunc { public: explicit MaskedMedianFunc (bool sorted=false, bool takeEvenMean=true) : itsSorted(sorted), itsTakeEvenMean(takeEvenMean) {} T operator() (const MaskedArray& arr) const { return median(arr, itsSorted, itsTakeEvenMean); } private: bool itsSorted; bool itsTakeEvenMean; bool itsInPlace; }; template class MaskedMadfmFunc { public: explicit MaskedMadfmFunc(bool sorted=false, bool takeEvenMean=true) : itsSorted(sorted), itsTakeEvenMean(takeEvenMean) {} float operator()(const MaskedArray& arr) const { return madfm(arr, itsSorted, itsTakeEvenMean); } private: bool itsSorted; bool itsTakeEvenMean; bool itsInPlace; }; // Apply the given ArrayMath reduction function objects // to each box in the array. // // Downsample an array by taking the mean of every [25,25] elements. // // Array downArr = boxedArrayMath(in, IPosition(2,25,25), // MaskedMeanFunc()); // // // The dimensionality of the array can be larger than the box; in that // case the missing axes of the box are assumed to have length 1. // A box axis length <= 0 means the full array axis. template MaskedArray boxedArrayMath (const MaskedArray& array, const IPosition& boxSize, const FuncType& funcObj); // Apply for each element in the array the given ArrayMath reduction function // object to the box around that element. The full box is 2*halfBoxSize + 1. // It can be used for arrays and boxes of any dimensionality; missing // halfBoxSize values are set to 1. // // Determine for each element in the array the median of a box // with size [51,51] around that element: // // Array medians = slidingArrayMath(in, IPosition(2,25,25), // MaskedMedianFunc()); // // This is a potentially expensive operation. On a high-end PC it took // appr. 27 seconds to get the medians for an array of [1000,1000] using // a halfBoxSize of [50,50]. // //
        The fillEdge argument determines how the edge is filled where // no full boxes can be made. true means it is set to zero; false means // that the edge is removed, thus the output array is smaller than the // input array. // This brute-force method of determining the medians outperforms // all kinds of smart implementations. For a vector it is about as fast // as the casacore class MedianSlider, for a 2D array // it is much, much faster. // template Array slidingArrayMath (const MaskedArray& array, const IPosition& halfBoxSize, const FuncType& funcObj, bool fillEdge=true); } //# NAMESPACE CASACORE - END #include "MaskArrMath.tcc" #endif casacore-3.7.1/casa/Arrays/MaskArrMath.tcc000066400000000000000000001432321476623553700203460ustar00rootroot00000000000000//# MaskArrMath.cc: Simple mathematics done with MaskedArray's. //# Copyright (C) 1993,1994,1995,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MASKARRMATH_2_TCC #define CASA_MASKARRMATH_2_TCC #include "ArrayLogical.h" #include "MaskArrMath.h" #include "Array.h" #include "ArrayError.h" #include "ArrayIter.h" #include "VectorIter.h" #include namespace casacore { //# NAMESPACE CASACORE - BEGIN #define MARRM_IOP_MA(IOP,STRIOP) \ template \ const MaskedArray & operator IOP (const MaskedArray &left, \ const Array &right) \ { \ if (left.conform(right) == false) { \ throw (ArrayConformanceError \ ("::operator" STRIOP "(const MaskedArray &, const Array &)" \ " - arrays do not conform")); \ } \ \ bool leftarrDelete; \ T *leftarrStorage = left.getRWArrayStorage(leftarrDelete); \ T *leftarrS = leftarrStorage; \ \ bool leftmaskDelete; \ const LogicalArrayElem *leftmaskStorage = \ left.getMaskStorage(leftmaskDelete); \ const LogicalArrayElem *leftmaskS = leftmaskStorage; \ \ bool rightDelete; \ const T *rightStorage = right.getStorage(rightDelete); \ const T *rightS = rightStorage; \ \ size_t ntotal = left.nelements(); \ while (ntotal--) { \ if (*leftmaskS) { \ *leftarrS IOP *rightS; \ } \ leftarrS++; \ leftmaskS++; \ rightS++; \ } \ \ left.putArrayStorage(leftarrStorage, leftarrDelete); \ left.freeMaskStorage(leftmaskStorage, leftmaskDelete); \ right.freeStorage(rightStorage, rightDelete); \ \ return left; \ } #define MARRM_IOP_AM(IOP,STRIOP) \ template \ Array & operator IOP (Array &left, const MaskedArray &right) \ { \ if (left.conform(right) == false) { \ throw (ArrayConformanceError \ ("::operator" STRIOP "(Array &, const MaskedArray &)" \ " - arrays do not conform")); \ } \ \ bool leftDelete; \ T *leftStorage = left.getStorage(leftDelete); \ T *leftS = leftStorage; \ \ bool rightarrDelete; \ const T *rightarrStorage = right.getArrayStorage(rightarrDelete); \ const T *rightarrS = rightarrStorage; \ \ bool rightmaskDelete; \ const LogicalArrayElem *rightmaskStorage = \ right.getMaskStorage(rightmaskDelete); \ const LogicalArrayElem *rightmaskS = rightmaskStorage; \ \ size_t ntotal = left.nelements(); \ while (ntotal--) { \ if (*rightmaskS) { \ *leftS IOP *rightarrS; \ } \ leftS++; \ rightarrS++; \ rightmaskS++; \ } \ \ left.putStorage(leftStorage, leftDelete); \ right.freeArrayStorage(rightarrStorage, rightarrDelete); \ right.freeMaskStorage(rightmaskStorage, rightmaskDelete); \ \ return left; \ } #define MARRM_IOP_MM(IOP,STRIOP) \ template \ const MaskedArray & operator IOP (const MaskedArray &left, \ const MaskedArray &right) \ { \ if (left.conform(right) == false) { \ throw (ArrayConformanceError \ ("::operator" STRIOP "(const MaskedArray &, const MaskedArray &)" \ " - arrays do not conform")); \ } \ \ bool leftarrDelete; \ T *leftarrStorage = left.getRWArrayStorage(leftarrDelete); \ T *leftarrS = leftarrStorage; \ \ bool leftmaskDelete; \ const LogicalArrayElem *leftmaskStorage \ = left.getMaskStorage(leftmaskDelete); \ const LogicalArrayElem *leftmaskS = leftmaskStorage; \ \ bool rightarrDelete; \ const T *rightarrStorage = right.getArrayStorage(rightarrDelete); \ const T *rightarrS = rightarrStorage; \ \ bool rightmaskDelete; \ const LogicalArrayElem *rightmaskStorage \ = right.getMaskStorage(rightmaskDelete); \ const LogicalArrayElem *rightmaskS = rightmaskStorage; \ \ size_t ntotal = left.nelements(); \ while (ntotal--) { \ if (*leftmaskS && *rightmaskS) { \ *leftarrS IOP *rightarrS; \ } \ leftarrS++; \ leftmaskS++; \ rightarrS++; \ rightmaskS++; \ } \ \ left.putArrayStorage(leftarrStorage, leftarrDelete); \ left.freeMaskStorage(leftmaskStorage, leftmaskDelete); \ right.freeArrayStorage(rightarrStorage, rightarrDelete); \ right.freeMaskStorage(rightmaskStorage, rightmaskDelete); \ \ return left; \ } #define MARRM_IOP_MM2(IOP,STRIOP) \ template \ const MaskedArray & operator IOP (const MaskedArray &left, \ const MaskedArray &right) \ { \ if (left.shape()!=right.shape()) { \ throw (ArrayConformanceError \ ("::operator" STRIOP "(const MaskedArray &, const MaskedArray &)" \ " - arrays do not conform")); \ } \ \ bool leftarrDelete; \ T *leftarrStorage = left.getRWArrayStorage(leftarrDelete); \ T *leftarrS = leftarrStorage; \ \ bool leftmaskDelete; \ const LogicalArrayElem *leftmaskStorage \ = left.getMaskStorage(leftmaskDelete); \ const LogicalArrayElem *leftmaskS = leftmaskStorage; \ \ bool rightarrDelete; \ const S *rightarrStorage = right.getArrayStorage(rightarrDelete); \ const S *rightarrS = rightarrStorage; \ \ bool rightmaskDelete; \ const LogicalArrayElem *rightmaskStorage \ = right.getMaskStorage(rightmaskDelete); \ const LogicalArrayElem *rightmaskS = rightmaskStorage; \ \ size_t ntotal = left.nelements(); \ while (ntotal--) { \ if (*leftmaskS && *rightmaskS) { \ *leftarrS IOP *rightarrS; \ } \ leftarrS++; \ leftmaskS++; \ rightarrS++; \ rightmaskS++; \ } \ \ left.putArrayStorage(leftarrStorage, leftarrDelete); \ left.freeMaskStorage(leftmaskStorage, leftmaskDelete); \ right.freeArrayStorage(rightarrStorage, rightarrDelete); \ right.freeMaskStorage(rightmaskStorage, rightmaskDelete); \ \ return left; \ } #define MARRM_IOP_MS(IOP) \ template \ const MaskedArray & operator IOP (const MaskedArray &left, \ const T &right) \ { \ bool leftarrDelete; \ T *leftarrStorage = left.getRWArrayStorage(leftarrDelete); \ T *leftarrS = leftarrStorage; \ \ bool leftmaskDelete; \ const LogicalArrayElem *leftmaskStorage \ = left.getMaskStorage(leftmaskDelete); \ const LogicalArrayElem *leftmaskS = leftmaskStorage; \ \ size_t ntotal = left.nelements(); \ while (ntotal--) { \ if (*leftmaskS) { \ *leftarrS IOP right; \ } \ leftarrS++; \ leftmaskS++; \ } \ \ left.putArrayStorage(leftarrStorage, leftarrDelete); \ left.freeMaskStorage(leftmaskStorage, leftmaskDelete); \ \ return left; \ } MARRM_IOP_MA ( += , "+=" ) MARRM_IOP_MA ( -= , "-=" ) MARRM_IOP_MA ( *= , "*=" ) MARRM_IOP_MA ( /= , "/=" ) MARRM_IOP_AM ( += , "+=" ) MARRM_IOP_AM ( -= , "-=" ) MARRM_IOP_AM ( *= , "*=" ) MARRM_IOP_AM ( /= , "/=" ) MARRM_IOP_MM ( += , "+=" ) MARRM_IOP_MM ( -= , "-=" ) MARRM_IOP_MM ( *= , "*=" ) MARRM_IOP_MM ( /= , "/=" ) MARRM_IOP_MM2 ( /= , "/=" ) MARRM_IOP_MS ( += ) MARRM_IOP_MS ( -= ) MARRM_IOP_MS ( *= ) MARRM_IOP_MS ( /= ) template MaskedArray operator+ (const MaskedArray &left) { MaskedArray result (left.copy()); return result; } template MaskedArray operator- (const MaskedArray &left) { MaskedArray result (left.copy()); bool resultarrDelete; T *resultarrStorage = result.getRWArrayStorage(resultarrDelete); T *resultarrS = resultarrStorage; bool resultmaskDelete; const LogicalArrayElem *resultmaskStorage = result.getMaskStorage(resultmaskDelete); const LogicalArrayElem *resultmaskS = resultmaskStorage; size_t ntotal = result.nelements(); while (ntotal--) { if (*resultmaskS) { *resultarrS = -(*resultarrS); } resultarrS++; resultmaskS++; } result.putArrayStorage(resultarrStorage, resultarrDelete); result.freeMaskStorage(resultmaskStorage, resultmaskDelete); return result; } #define MARRM_OP_MA(OP,IOP,STROP) \ template \ MaskedArray operator OP (const MaskedArray &left, \ const Array &right) \ { \ if (left.conform(right) == false) { \ throw (ArrayConformanceError \ ("::operator" STROP "(const MaskedArray &, const Array &)" \ " - arrays do not conform")); \ } \ \ MaskedArray result (left.copy()); \ \ result IOP right; \ \ return result; \ } #define MARRM_OP_AM(OP,IOP,STROP) \ template \ MaskedArray operator OP (const Array &left, \ const MaskedArray &right) \ { \ if (left.conform(right) == false) { \ throw (ArrayConformanceError \ ("::operator" STROP "(const Array &, const MaskedArray &)" \ " - arrays do not conform")); \ } \ \ MaskedArray result (left.copy(), right.getMask()); \ \ result IOP right; \ \ return result; \ } #define MARRM_OP_MM(OP,IOP,STROP) \ template \ MaskedArray operator OP (const MaskedArray &left, \ const MaskedArray &right) \ { \ if (left.conform(right) == false) { \ throw (ArrayConformanceError \ ("::operator" STROP "(const MaskedArray &," \ " const MaskedArray &)" \ " - arrays do not conform")); \ } \ \ MaskedArray result ( left.getArray().copy(), \ (left.getMask() && right.getMask()) ); \ \ result IOP right; \ \ return result; \ } #define MARRM_OP_MS(OP,IOP) \ template \ MaskedArray operator OP (const MaskedArray &left, const T &right) \ { \ MaskedArray result (left.copy()); \ \ result IOP right; \ \ return result; \ } #define MARRM_OP_SM(OP,IOP) \ template \ MaskedArray operator OP (const T &left, const MaskedArray &right) \ { \ Array resultarray (right.shape()); \ resultarray = left; \ \ MaskedArray result (resultarray, right.getMask()); \ \ result IOP right; \ \ return result; \ } MARRM_OP_MA ( +, += , "+" ) MARRM_OP_MA ( -, -= , "-" ) MARRM_OP_MA ( *, *= , "*" ) MARRM_OP_MA ( /, /= , "/" ) MARRM_OP_AM ( +, += , "+" ) MARRM_OP_AM ( -, -= , "-" ) MARRM_OP_AM ( *, *= , "*" ) MARRM_OP_AM ( /, /= , "/" ) MARRM_OP_MM ( +, += , "+" ) MARRM_OP_MM ( -, -= , "-" ) MARRM_OP_MM ( *, *= , "*" ) MARRM_OP_MM ( /, /= , "/" ) MARRM_OP_MS ( +, += ) MARRM_OP_MS ( -, -= ) MARRM_OP_MS ( *, *= ) MARRM_OP_MS ( /, /= ) MARRM_OP_SM ( +, += ) MARRM_OP_SM ( -, -= ) MARRM_OP_SM ( *, *= ) MARRM_OP_SM ( /, /= ) template void indgen(MaskedArray &left, T start, T inc) { bool leftarrDelete; T *leftarrStorage = left.getRWArrayStorage(leftarrDelete); T *leftarrS = leftarrStorage; bool leftmaskDelete; const LogicalArrayElem *leftmaskStorage = left.getMaskStorage(leftmaskDelete); const LogicalArrayElem *leftmaskS = leftmaskStorage; size_t ntotal = left.nelements(); T ind = start; while (ntotal--) { if (*leftmaskS) { *leftarrS = ind; ind += inc; } leftarrS++; leftmaskS++; } left.putArrayStorage(leftarrStorage, leftarrDelete); left.freeMaskStorage(leftmaskStorage, leftmaskDelete); } template void indgen(MaskedArray &a) { indgen(a, T(0), T(1)); } template void indgen(MaskedArray &a, T start) { indgen(a, start, T(1)); } template MaskedArray pow (const MaskedArray &left, const Array &right) { // if (conform2(left, right) == false) { if (left.shape() != right.shape()) { throw (ArrayConformanceError ("::" "pow" "(const MaskedArray &, const Array &)" " - arrays do not conform")); } MaskedArray result (left.copy()); bool resultarrDelete; T *resultarrStorage = result.getRWArrayStorage(resultarrDelete); T *resultarrS = resultarrStorage; bool resultmaskDelete; const LogicalArrayElem *resultmaskStorage = result.getMaskStorage(resultmaskDelete); const LogicalArrayElem *resultmaskS = resultmaskStorage; bool rightDelete; const U *rightStorage = right.getStorage(rightDelete); const U *rightS = rightStorage; size_t ntotal = result.nelements(); while (ntotal--) { if (*resultmaskS) { *resultarrS = std::pow (*resultarrS, *rightS); } resultarrS++; resultmaskS++; rightS++; } result.putArrayStorage(resultarrStorage, resultarrDelete); result.freeMaskStorage(resultmaskStorage, resultmaskDelete); right.freeStorage(rightStorage, rightDelete); return result; } template MaskedArray pow (const Array &left, const MaskedArray &right) { // if (conform2(left, right) == false) { if (left.shape() != right.shape()) { throw (ArrayConformanceError ("::" "pow" "(const Array &, const MaskedArray &)" " - arrays do not conform")); } MaskedArray result (left.copy(), right.getMask()); bool resultarrDelete; T *resultarrStorage = result.getRWArrayStorage(resultarrDelete); T *resultarrS = resultarrStorage; bool resultmaskDelete; const LogicalArrayElem *resultmaskStorage = result.getMaskStorage(resultmaskDelete); const LogicalArrayElem *resultmaskS = resultmaskStorage; bool rightarrDelete; const U *rightarrStorage = right.getArrayStorage(rightarrDelete); const U *rightarrS = rightarrStorage; size_t ntotal = result.nelements(); while (ntotal--) { if (*resultmaskS) { *resultarrS = std::pow (*resultarrS, *rightarrS); } resultarrS++; resultmaskS++; rightarrS++; } result.putArrayStorage(resultarrStorage, resultarrDelete); result.freeMaskStorage(resultmaskStorage, resultmaskDelete); right.freeArrayStorage(rightarrStorage, rightarrDelete); return result; } template MaskedArray pow (const MaskedArray &left, const MaskedArray &right) { // if (conform2(left, right) == false) { if (left.shape() != right.shape()) { throw (ArrayConformanceError ("::" "pow" "(const MaskedArray &, const MaskedArray &)" " - arrays do not conform")); } MaskedArray result ( left.getArray().copy(), (left.getMask() && right.getMask()) ); bool resultarrDelete; T *resultarrStorage = result.getRWArrayStorage(resultarrDelete); T *resultarrS = resultarrStorage; bool resultmaskDelete; const LogicalArrayElem *resultmaskStorage = result.getMaskStorage(resultmaskDelete); const LogicalArrayElem *resultmaskS = resultmaskStorage; bool rightarrDelete; const U *rightarrStorage = right.getArrayStorage(rightarrDelete); const U *rightarrS = rightarrStorage; size_t ntotal = result.nelements(); while (ntotal--) { if (*resultmaskS) { *resultarrS = std::pow (*resultarrS, *rightarrS); } resultarrS++; resultmaskS++; rightarrS++; } result.putArrayStorage(resultarrStorage, resultarrDelete); result.freeMaskStorage(resultmaskStorage, resultmaskDelete); right.freeArrayStorage(rightarrStorage, rightarrDelete); return result; } template MaskedArray pow (const MaskedArray &left, const double &right) { MaskedArray result (left.copy()); bool resultarrDelete; T *resultarrStorage = result.getRWArrayStorage(resultarrDelete); T *resultarrS = resultarrStorage; bool resultmaskDelete; const LogicalArrayElem *resultmaskStorage = result.getMaskStorage(resultmaskDelete); const LogicalArrayElem *resultmaskS = resultmaskStorage; size_t ntotal = result.nelements(); while (ntotal--) { if (*resultmaskS) { *resultarrS = std::pow (*resultarrS, right); } resultarrS++; resultmaskS++; } result.putArrayStorage(resultarrStorage, resultarrDelete); result.freeMaskStorage(resultmaskStorage, resultmaskDelete); return result; } #define MARRM_FUNC_M(DEFNAME, FUNC) \ template \ MaskedArray DEFNAME (const MaskedArray &left) \ { \ MaskedArray result (left.copy()); \ \ bool resultarrDelete; \ T *resultarrStorage = result.getRWArrayStorage(resultarrDelete); \ T *resultarrS = resultarrStorage; \ \ bool resultmaskDelete; \ const LogicalArrayElem *resultmaskStorage \ = result.getMaskStorage(resultmaskDelete); \ const LogicalArrayElem *resultmaskS = resultmaskStorage; \ \ size_t ntotal = result.nelements(); \ while (ntotal--) { \ if (*resultmaskS) { \ *resultarrS = FUNC (*resultarrS); \ } \ resultarrS++; \ resultmaskS++; \ } \ \ result.putArrayStorage(resultarrStorage, resultarrDelete); \ result.freeMaskStorage(resultmaskStorage, resultmaskDelete); \ \ return result; \ } #define MARRM_FUNC_MA(DEFNAME,FUNC,STRFUNC) \ template \ MaskedArray DEFNAME (const MaskedArray &left, const Array &right) \ { \ if (left.conform(right) == false) { \ throw (ArrayConformanceError \ ("::" STRFUNC \ "(const MaskedArray &, const Array &)" \ " - arrays do not conform")); \ } \ \ MaskedArray result (left.copy()); \ \ bool resultarrDelete; \ T *resultarrStorage = result.getRWArrayStorage(resultarrDelete); \ T *resultarrS = resultarrStorage; \ \ bool resultmaskDelete; \ const LogicalArrayElem *resultmaskStorage = \ result.getMaskStorage(resultmaskDelete); \ const LogicalArrayElem *resultmaskS = resultmaskStorage; \ \ bool rightDelete; \ const T *rightStorage = right.getStorage(rightDelete); \ const T *rightS = rightStorage; \ \ size_t ntotal = result.nelements(); \ while (ntotal--) { \ if (*resultmaskS) { \ *resultarrS = T(FUNC (*resultarrS, *rightS)); \ } \ resultarrS++; \ resultmaskS++; \ rightS++; \ } \ \ result.putArrayStorage(resultarrStorage, resultarrDelete); \ result.freeMaskStorage(resultmaskStorage, resultmaskDelete); \ right.freeStorage(rightStorage, rightDelete); \ \ return result; \ } #define MARRM_FUNC_AM(DEFNAME, FUNC,STRFUNC) \ template \ MaskedArray DEFNAME (const Array &left, const MaskedArray &right) \ { \ if (left.conform(right) == false) { \ throw (ArrayConformanceError \ ("::" STRFUNC \ "(const Array &, const MaskedArray &)" \ " - arrays do not conform")); \ } \ \ MaskedArray result (left.copy(), right.getMask()); \ \ bool resultarrDelete; \ T *resultarrStorage = result.getRWArrayStorage(resultarrDelete); \ T *resultarrS = resultarrStorage; \ \ bool resultmaskDelete; \ const LogicalArrayElem *resultmaskStorage = \ result.getMaskStorage(resultmaskDelete); \ const LogicalArrayElem *resultmaskS = resultmaskStorage; \ \ bool rightarrDelete; \ const T *rightarrStorage = right.getArrayStorage(rightarrDelete); \ const T *rightarrS = rightarrStorage; \ \ size_t ntotal = result.nelements(); \ while (ntotal--) { \ if (*resultmaskS) { \ *resultarrS = T(FUNC (*resultarrS, *rightarrS)); \ } \ resultarrS++; \ resultmaskS++; \ rightarrS++; \ } \ \ result.putArrayStorage(resultarrStorage, resultarrDelete); \ result.freeMaskStorage(resultmaskStorage, resultmaskDelete); \ right.freeArrayStorage(rightarrStorage, rightarrDelete); \ \ return result; \ } #define MARRM_FUNC_MM(DEFNAME, FUNC,STRFUNC) \ template \ MaskedArray DEFNAME (const MaskedArray &left, \ const MaskedArray &right) \ { \ if (left.conform(right) == false) { \ throw (ArrayConformanceError \ ("::" STRFUNC\ "(const MaskedArray &, const MaskedArray &)" \ " - arrays do not conform")); \ } \ \ MaskedArray result ( left.getArray().copy(), \ (left.getMask() && right.getMask()) ); \ \ bool resultarrDelete; \ T *resultarrStorage = result.getRWArrayStorage(resultarrDelete); \ T *resultarrS = resultarrStorage; \ \ bool resultmaskDelete; \ const LogicalArrayElem *resultmaskStorage \ = result.getMaskStorage(resultmaskDelete); \ const LogicalArrayElem *resultmaskS = resultmaskStorage; \ \ bool rightarrDelete; \ const T *rightarrStorage = right.getArrayStorage(rightarrDelete); \ const T *rightarrS = rightarrStorage; \ \ size_t ntotal = result.nelements(); \ while (ntotal--) { \ if (*resultmaskS) { \ *resultarrS = T(FUNC (*resultarrS, *rightarrS)); \ } \ resultarrS++; \ resultmaskS++; \ rightarrS++; \ } \ \ result.putArrayStorage(resultarrStorage, resultarrDelete); \ result.freeMaskStorage(resultmaskStorage, resultmaskDelete); \ right.freeArrayStorage(rightarrStorage, rightarrDelete); \ \ return result; \ } #define MARRM_FUNC_MS(DEFNAME, FUNC) \ template \ MaskedArray DEFNAME (const MaskedArray &left, const T &right) \ { \ MaskedArray result (left.copy()); \ \ bool resultarrDelete; \ T *resultarrStorage = result.getRWArrayStorage(resultarrDelete); \ T *resultarrS = resultarrStorage; \ \ bool resultmaskDelete; \ const LogicalArrayElem *resultmaskStorage \ = result.getMaskStorage(resultmaskDelete); \ const LogicalArrayElem *resultmaskS = resultmaskStorage; \ \ size_t ntotal = result.nelements(); \ while (ntotal--) { \ if (*resultmaskS) { \ *resultarrS = FUNC (*resultarrS, right); \ } \ resultarrS++; \ resultmaskS++; \ } \ \ result.putArrayStorage(resultarrStorage, resultarrDelete); \ result.freeMaskStorage(resultmaskStorage, resultmaskDelete); \ \ return result; \ } #define MARRM_FUNC_SM(DEFNAME, FUNC) \ template \ MaskedArray DEFNAME (const T &left, const MaskedArray &right) \ { \ Array resultarray (right.shape()); \ resultarray = left; \ \ MaskedArray result (resultarray, right.getMask()); \ \ bool resultarrDelete; \ T *resultarrStorage = result.getRWArrayStorage(resultarrDelete); \ T *resultarrS = resultarrStorage; \ \ bool resultmaskDelete; \ const LogicalArrayElem *resultmaskStorage \ = result.getMaskStorage(resultmaskDelete); \ const LogicalArrayElem *resultmaskS = resultmaskStorage; \ \ bool rightarrDelete; \ const T *rightarrStorage = right.getArrayStorage(rightarrDelete); \ const T *rightarrS = rightarrStorage; \ \ size_t ntotal = result.nelements(); \ while (ntotal--) { \ if (*resultmaskS) { \ *resultarrS = FUNC (*resultarrS, *rightarrS); \ } \ resultarrS++; \ resultmaskS++; \ rightarrS++; \ } \ \ result.putArrayStorage(resultarrStorage, resultarrDelete); \ result.freeMaskStorage(resultmaskStorage, resultmaskDelete); \ right.freeArrayStorage(rightarrStorage, rightarrDelete); \ \ return result; \ } MARRM_FUNC_M ( sin, std::sin ) MARRM_FUNC_M ( cos, std::cos ) MARRM_FUNC_M ( tan, std::tan ) MARRM_FUNC_M ( asin, std::sin ) MARRM_FUNC_M ( acos, std::acos ) MARRM_FUNC_M ( atan, std::atan ) MARRM_FUNC_M ( sinh, std::sinh ) MARRM_FUNC_M ( cosh, std::cosh ) MARRM_FUNC_M ( tanh, std::tanh ) MARRM_FUNC_M ( exp, std::exp ) MARRM_FUNC_M ( log, std::log ) MARRM_FUNC_M ( log10, std::log10 ) MARRM_FUNC_M ( sqrt, std::sqrt ) MARRM_FUNC_M ( abs, std::abs ) MARRM_FUNC_M ( fabs, std::abs ) MARRM_FUNC_M ( ceil, std::ceil ) MARRM_FUNC_M ( floor, std::floor ) MARRM_FUNC_MA ( atan2, std::atan2, "atan2" ) MARRM_FUNC_MA ( fmod, std::fmod, "fmod" ) MARRM_FUNC_AM ( atan2, std::atan2, "atan2" ) MARRM_FUNC_AM ( fmod, std::fmod, "fmod" ) MARRM_FUNC_MM ( atan2, std::atan2, "atan2" ) MARRM_FUNC_MM ( fmod, std::fmod, "fmod" ) MARRM_FUNC_MS ( atan2, std::atan2 ) MARRM_FUNC_MS ( fmod, std::fmod ) MARRM_FUNC_SM ( atan2, std::atan2 ) MARRM_FUNC_SM ( fmod, std::fmod ) template void minMax(T &minVal, T &maxVal, IPosition &minPos, IPosition &maxPos, const MaskedArray &marray) { if ((minPos.nelements() != marray.ndim()) || (maxPos.nelements() != marray.ndim())) { throw (ArrayError( "void ::minMax(" "T &minVal, T &maxVal, IPosition &minPos, IPosition &maxPos," " const MaskedArray &marray)" " - minPos, maxPos dimensionality inconsistent with marray")); } bool marrayarrDelete; const T *marrayarrStorage = marray.getArrayStorage(marrayarrDelete); const T *marrayarrS = marrayarrStorage; bool marraymaskDelete; const LogicalArrayElem *marraymaskStorage = marray.getMaskStorage(marraymaskDelete); const LogicalArrayElem *marraymaskS = marraymaskStorage; size_t ntotal = marray.nelements(); bool foundOne = false; T minLocal = T(); T maxLocal = T(); size_t minNtotal=0; size_t maxNtotal=0; while (ntotal--) { if (*marraymaskS) { minLocal = *marrayarrS; maxLocal = minLocal; minNtotal = ntotal + 1; maxNtotal = minNtotal; marrayarrS++; marraymaskS++; foundOne = true; break; } else { marrayarrS++; marraymaskS++; } } if (!foundOne) { marray.freeArrayStorage(marrayarrStorage, marrayarrDelete); marray.freeMaskStorage(marraymaskStorage, marraymaskDelete); throw (ArrayError( "void ::minMax(" "T &minVal, T &maxVal, IPosition &minPos, IPosition &maxPos," " const MaskedArray &marray)" " - Need at least 1 unmasked element")); } while (ntotal--) { if (*marraymaskS) { if (*marrayarrS < minLocal) { \ minLocal = *marrayarrS; minNtotal = ntotal + 1; } if (*marrayarrS > maxLocal) { \ maxLocal = *marrayarrS; maxNtotal = ntotal + 1; } } marrayarrS++; marraymaskS++; } marray.freeArrayStorage(marrayarrStorage, marrayarrDelete); marray.freeMaskStorage(marraymaskStorage, marraymaskDelete); minVal = minLocal; maxVal = maxLocal; minPos = toIPositionInArray (marray.nelements() - minNtotal, marray.shape()); maxPos = toIPositionInArray (marray.nelements() - maxNtotal, marray.shape()); return; } template void minMax(T &minVal, T &maxVal, const MaskedArray &marray) { IPosition minPos (marray.ndim(), 0); IPosition maxPos (minPos); minMax (minVal, maxVal, minPos, maxPos, marray); return; } #define MARRM_MINORMAX_M(FUNC,OP,STRFUNC) \ template T FUNC (const MaskedArray &left) \ { \ bool leftarrDelete; \ const T *leftarrStorage = left.getArrayStorage(leftarrDelete); \ const T *leftarrS = leftarrStorage; \ \ bool leftmaskDelete; \ const LogicalArrayElem *leftmaskStorage \ = left.getMaskStorage(leftmaskDelete); \ const LogicalArrayElem *leftmaskS = leftmaskStorage; \ \ T result = *leftarrS; \ size_t ntotal = left.nelements(); \ bool foundOne = false; \ \ while (ntotal--) { \ if (*leftmaskS) { \ result = *leftarrS; \ leftarrS++; \ leftmaskS++; \ foundOne = true; \ break; \ } else { \ leftarrS++; \ leftmaskS++; \ } \ } \ \ if (!foundOne) { \ left.freeArrayStorage(leftarrStorage, leftarrDelete); \ left.freeMaskStorage(leftmaskStorage, leftmaskDelete); \ \ throw (ArrayError("T ::" STRFUNC "(const MaskedArray &left) - " \ "Need at least 1 unmasked element")); \ } \ \ while (ntotal--) { \ if (*leftmaskS) { \ if (*leftarrS OP result) { \ result = *leftarrS; \ } \ } \ leftarrS++; \ leftmaskS++; \ } \ \ left.freeArrayStorage(leftarrStorage, leftarrDelete); \ left.freeMaskStorage(leftmaskStorage, leftmaskDelete); \ \ return result; \ } #define MARRM_MINORMAX_MA(FUNC,OP,STRFUNC) \ template \ MaskedArray FUNC (const MaskedArray &left, const Array &right) \ { \ if (left.conform(right) == false) { \ throw (ArrayConformanceError \ ("::" STRFUNC \ "(const MaskedArray &, const Array &)" \ " - arrays do not conform")); \ } \ \ MaskedArray result (left.copy()); \ \ bool resultarrDelete; \ T *resultarrStorage = result.getRWArrayStorage(resultarrDelete); \ T *resultarrS = resultarrStorage; \ \ bool resultmaskDelete; \ const LogicalArrayElem *resultmaskStorage = \ result.getMaskStorage(resultmaskDelete); \ const LogicalArrayElem *resultmaskS = resultmaskStorage; \ \ bool rightDelete; \ const T *rightStorage = right.getStorage(rightDelete); \ const T *rightS = rightStorage; \ \ size_t ntotal = result.nelements(); \ while (ntotal--) { \ if (*resultmaskS) { \ if (*rightS OP *resultarrS) { \ *resultarrS = *rightS; \ } \ } \ resultarrS++; \ resultmaskS++; \ rightS++; \ } \ \ result.putArrayStorage(resultarrStorage, resultarrDelete); \ result.freeMaskStorage(resultmaskStorage, resultmaskDelete); \ right.freeStorage(rightStorage, rightDelete); \ \ return result; \ } #define MARRM_MINORMAX_AM(FUNC,OP,STRFUNC) \ template \ MaskedArray FUNC (const Array &left, const MaskedArray &right) \ { \ if (left.conform(right) == false) { \ throw (ArrayConformanceError \ ("::" STRFUNC \ "(const Array &, const MaskedArray &)" \ " - arrays do not conform")); \ } \ \ MaskedArray result (left.copy(), right.getMask()); \ \ bool resultarrDelete; \ T *resultarrStorage = result.getRWArrayStorage(resultarrDelete); \ T *resultarrS = resultarrStorage; \ \ bool resultmaskDelete; \ const LogicalArrayElem *resultmaskStorage = \ result.getMaskStorage(resultmaskDelete); \ const LogicalArrayElem *resultmaskS = resultmaskStorage; \ \ bool rightarrDelete; \ const T *rightarrStorage = right.getArrayStorage(rightarrDelete); \ const T *rightarrS = rightarrStorage; \ \ size_t ntotal = result.nelements(); \ while (ntotal--) { \ if (*resultmaskS) { \ if (*rightarrS OP *resultarrS) { \ *resultarrS = *rightarrS; \ } \ } \ resultarrS++; \ resultmaskS++; \ rightarrS++; \ } \ \ result.putArrayStorage(resultarrStorage, resultarrDelete); \ result.freeMaskStorage(resultmaskStorage, resultmaskDelete); \ right.freeArrayStorage(rightarrStorage, rightarrDelete); \ \ return result; \ } #define MARRM_MINORMAX_MM(FUNC,OP,STRFUNC) \ template \ MaskedArray FUNC (const MaskedArray &left, \ const MaskedArray &right) \ { \ if (left.conform(right) == false) { \ throw (ArrayConformanceError \ ("MaskedArray ::" STRFUNC\ "(const MaskedArray &, const MaskedArray &)" \ " - arrays do not conform")); \ } \ \ MaskedArray result ( left.getArray().copy(), \ (left.getMask() && right.getMask()) ); \ \ bool resultarrDelete; \ T *resultarrStorage = result.getRWArrayStorage(resultarrDelete); \ T *resultarrS = resultarrStorage; \ \ bool resultmaskDelete; \ const LogicalArrayElem *resultmaskStorage \ = result.getMaskStorage(resultmaskDelete); \ const LogicalArrayElem *resultmaskS = resultmaskStorage; \ \ bool rightarrDelete; \ const T *rightarrStorage = right.getArrayStorage(rightarrDelete); \ const T *rightarrS = rightarrStorage; \ \ size_t ntotal = result.nelements(); \ while (ntotal--) { \ if (*resultmaskS) { \ if (*rightarrS OP *resultarrS) { \ *resultarrS = *rightarrS; \ } \ } \ resultarrS++; \ resultmaskS++; \ rightarrS++; \ } \ \ result.putArrayStorage(resultarrStorage, resultarrDelete); \ result.freeMaskStorage(resultmaskStorage, resultmaskDelete); \ right.freeArrayStorage(rightarrStorage, rightarrDelete); \ \ return result; \ } #define MARRM_MINORMAX_MS(FUNC,OP) \ template \ MaskedArray FUNC (const MaskedArray &left, const T &right) \ { \ MaskedArray result (left.copy()); \ \ bool resultarrDelete; \ T *resultarrStorage = result.getRWArrayStorage(resultarrDelete); \ T *resultarrS = resultarrStorage; \ \ bool resultmaskDelete; \ const LogicalArrayElem *resultmaskStorage \ = result.getMaskStorage(resultmaskDelete); \ const LogicalArrayElem *resultmaskS = resultmaskStorage; \ \ size_t ntotal = result.nelements(); \ while (ntotal--) { \ if (*resultmaskS) { \ if (right OP *resultarrS) { \ *resultarrS = right; \ } \ } \ resultarrS++; \ resultmaskS++; \ } \ \ result.putArrayStorage(resultarrStorage, resultarrDelete); \ result.freeMaskStorage(resultmaskStorage, resultmaskDelete); \ \ return result; \ } #define MARRM_MINORMAX_SM(FUNC,OP) \ template \ MaskedArray FUNC (const T &left, const MaskedArray &right) \ { \ Array resultarray (right.shape()); \ resultarray = left; \ \ MaskedArray result (resultarray, right.getMask()); \ \ bool resultarrDelete; \ T *resultarrStorage = result.getRWArrayStorage(resultarrDelete); \ T *resultarrS = resultarrStorage; \ \ bool resultmaskDelete; \ const LogicalArrayElem *resultmaskStorage \ = result.getMaskStorage(resultmaskDelete); \ const LogicalArrayElem *resultmaskS = resultmaskStorage; \ \ bool rightarrDelete; \ const T *rightarrStorage = right.getArrayStorage(rightarrDelete); \ const T *rightarrS = rightarrStorage; \ \ size_t ntotal = result.nelements(); \ while (ntotal--) { \ if (*resultmaskS) { \ if (*rightarrS OP *resultarrS) { \ *resultarrS = *rightarrS; \ } \ } \ resultarrS++; \ resultmaskS++; \ rightarrS++; \ } \ \ result.putArrayStorage(resultarrStorage, resultarrDelete); \ result.freeMaskStorage(resultmaskStorage, resultmaskDelete); \ right.freeArrayStorage(rightarrStorage, rightarrDelete); \ \ return result; \ } #define MARRM_MINORMAX_AAM(FUNC,OP,STRFUNC) \ template \ void FUNC (const MaskedArray &result, \ const Array &left, const Array &right) \ { \ if ( ! (result.conform(left) && result.conform(right)) ) { \ throw (ArrayConformanceError \ ("void ::" STRFUNC \ "(const MaskedArray &, const Array &, const Array &)" \ " - arrays do not conform")); \ } \ \ bool resultarrDelete; \ T *resultarrStorage = result.getRWArrayStorage(resultarrDelete); \ T *resultarrS = resultarrStorage; \ \ bool resultmaskDelete; \ const LogicalArrayElem *resultmaskStorage = \ result.getMaskStorage(resultmaskDelete); \ const LogicalArrayElem *resultmaskS = resultmaskStorage; \ \ bool leftarrDelete; \ const T *leftarrStorage = left.getStorage(leftarrDelete); \ const T *leftarrS = leftarrStorage; \ \ bool rightarrDelete; \ const T *rightarrStorage = right.getStorage(rightarrDelete); \ const T *rightarrS = rightarrStorage; \ \ size_t ntotal = result.nelements(); \ while (ntotal--) { \ if (*resultmaskS) { \ *resultarrS = (*leftarrS OP *rightarrS) ? *leftarrS : *rightarrS; \ } \ resultarrS++; \ resultmaskS++; \ leftarrS++; \ rightarrS++; \ } \ \ result.putArrayStorage(resultarrStorage, resultarrDelete); \ result.freeMaskStorage(resultmaskStorage, resultmaskDelete); \ left.freeStorage(leftarrStorage, leftarrDelete); \ right.freeStorage(rightarrStorage, rightarrDelete); \ \ return; \ } MARRM_MINORMAX_M ( min, <, "min" ) MARRM_MINORMAX_M ( max, >, "max" ) MARRM_MINORMAX_MA ( min, < , "min" ) MARRM_MINORMAX_MA ( max, > , "max" ) MARRM_MINORMAX_AM ( min, < , "min" ) MARRM_MINORMAX_AM ( max, > , "max" ) MARRM_MINORMAX_MM ( min, < , "min" ) MARRM_MINORMAX_MM ( max, > , "max" ) MARRM_MINORMAX_MS ( min, < ) MARRM_MINORMAX_MS ( max, > ) MARRM_MINORMAX_SM ( min, < ) MARRM_MINORMAX_SM ( max, > ) MARRM_MINORMAX_AAM ( min, < , "min" ) MARRM_MINORMAX_AAM ( max, > , "max" ) template T sum(const MaskedArray &left) { if (left.nelementsValid() < 1) { throw (ArrayError("T ::sum(const MaskedArray &left) - " "Need at least 1 unmasked element")); } bool leftarrDelete; const T *leftarrStorage = left.getArrayStorage(leftarrDelete); const T *leftarrS = leftarrStorage; bool leftmaskDelete; const LogicalArrayElem *leftmaskStorage = left.getMaskStorage(leftmaskDelete); const LogicalArrayElem *leftmaskS = leftmaskStorage; T sum = 0; size_t ntotal = left.nelements(); while (ntotal--) { if (*leftmaskS) { sum += *leftarrS; } leftarrS++; leftmaskS++; } left.freeArrayStorage(leftarrStorage, leftarrDelete); left.freeMaskStorage(leftmaskStorage, leftmaskDelete); return sum; } template T sumsquares(const MaskedArray &left) { if (left.nelementsValid() < 1) { throw (ArrayError("T ::sumsquares(const MaskedArray &left) - " "Need at least 1 unmasked element")); } bool leftarrDelete; const T *leftarrStorage = left.getArrayStorage(leftarrDelete); const T *leftarrS = leftarrStorage; bool leftmaskDelete; const LogicalArrayElem *leftmaskStorage = left.getMaskStorage(leftmaskDelete); const LogicalArrayElem *leftmaskS = leftmaskStorage; T sumsquares = 0; size_t ntotal = left.nelements(); while (ntotal--) { if (*leftmaskS) { sumsquares += (*leftarrS * *leftarrS); } leftarrS++; leftmaskS++; } left.freeArrayStorage(leftarrStorage, leftarrDelete); left.freeMaskStorage(leftmaskStorage, leftmaskDelete); return sumsquares; } template T product(const MaskedArray &left) { if (left.nelementsValid() < 1) { throw (ArrayError("T ::product(const MaskedArray &left) - " "Need at least 1 unmasked element")); } bool leftarrDelete; const T *leftarrStorage = left.getArrayStorage(leftarrDelete); const T *leftarrS = leftarrStorage; bool leftmaskDelete; const LogicalArrayElem *leftmaskStorage = left.getMaskStorage(leftmaskDelete); const LogicalArrayElem *leftmaskS = leftmaskStorage; T product = 1; size_t ntotal = left.nelements(); while (ntotal--) { if (*leftmaskS) { product *= *leftarrS; } leftarrS++; leftmaskS++; } left.freeArrayStorage(leftarrStorage, leftarrDelete); left.freeMaskStorage(leftmaskStorage, leftmaskDelete); return product; } template T mean(const MaskedArray &left) { if (left.nelementsValid() < 1) { throw (ArrayError("T ::mean(const MaskedArray &left) - " "Need at least 1 unmasked element")); } return sum(left)/T(left.nelementsValid()); } // // ArrayError // // Similar to numpy the ddof argument can be used to get the population // variance (ddof=0) or the sample variance (ddof=1). template T pvariance(const MaskedArray &a, T mean, size_t ddof) { size_t nr = a.nelementsValid(); if (nr < ddof+1) { throw(ArrayError("::variance(const MaskedArray &) - Need at least " + std::to_string(ddof+1) + " unmasked elements")); } MaskedArray deviations (abs(a - mean)); // abs is needed for Complex deviations *= deviations; return sum(deviations) / T(nr - ddof); } template T variance(const MaskedArray &a, T mean) { return pvariance (a, mean, 1); } template T pvariance(const MaskedArray &a, size_t ddof) { return pvariance(a, mean(a), ddof); } template T variance(const MaskedArray &a) { return pvariance(a, mean(a), 1); } // // ArrayError // template T pstddev(const MaskedArray &a, T mean, size_t ddof) { if (a.nelements() < ddof+1) { throw(ArrayError("::stddev(const Array &) - Need at least " + std::to_string(ddof+1) + " unmasked elements")); } return std::sqrt(pvariance(a, mean, ddof)); } template T stddev(const MaskedArray &a, T mean) { return pstddev (a, mean, 1); } template T pstddev(const MaskedArray &a, size_t ddof) { return pstddev (a, mean(a), ddof); } template T stddev(const MaskedArray &a) { return pstddev (a, mean(a), 1); } template T avdev(const MaskedArray &left) { return avdev(left, mean(left)); } template T avdev(const MaskedArray &left, T mean) { if (left.nelementsValid() < 1) { throw (ArrayError("T ::avdev(const MaskedArray &, T) - " "Need at least 1 unmasked element")); } MaskedArray avdeviations (abs(left - mean)); return sum(avdeviations)/T(left.nelementsValid()); } template T rms(const MaskedArray &left) { if (left.nelementsValid() < 1) { throw (ArrayError("T ::rms(const MaskedArray &left) - " "Need at least 1 unmasked element")); } return T(std::sqrt(sumsquares(left)/(1.0*left.nelementsValid()))); } template T median(const MaskedArray &left, bool sorted, bool takeEvenMean) { size_t nelem = left.nelementsValid(); if (nelem < 1) { throw (ArrayError("T ::median(const MaskedArray &) - " "Need at least 1 unmasked element")); } //# Mean does not have to be taken for odd number of elements. if (nelem%2 != 0) { takeEvenMean = false; } T medval; bool leftarrDelete; const T *leftarrStorage = left.getArrayStorage(leftarrDelete); const T *leftarrS = leftarrStorage; bool leftmaskDelete; const LogicalArrayElem *leftmaskStorage = left.getMaskStorage(leftmaskDelete); const LogicalArrayElem *leftmaskS = leftmaskStorage; size_t n2 = (nelem - 1)/2; if (! sorted) { // Make a copy of the masked elements. std::unique_ptr copy(new T[nelem]); T *copyS = copy.get(); size_t ntotal = nelem; while (ntotal) { if (*leftmaskS) { *copyS = *leftarrS; copyS++; ntotal--; } leftarrS++; leftmaskS++; } std::nth_element(©[0], ©[n2], ©[nelem]); if (takeEvenMean) { T a = copy[n2]; std::nth_element(©[0], ©[n2+1], ©[nelem]); medval = T(0.5 * (a + copy[n2+1])); } else { medval = copy[n2]; } copy.reset(); } else { // Sorted. // When mean has to be taken, we need one more element. if (takeEvenMean) { n2++; } const T* prev = 0; for (;;) { if (*leftmaskS) { if (n2 == 0) break; prev = leftarrS; n2--; } leftarrS++; leftmaskS++; } if (takeEvenMean) { medval = T(0.5 * (*prev + *leftarrS)); } else { medval = *leftarrS; } } left.freeArrayStorage(leftarrStorage, leftarrDelete); left.freeMaskStorage(leftmaskStorage, leftmaskDelete); return medval; } template T madfm(const MaskedArray &a, bool sorted, bool takeEvenMean) { T med = median(a, sorted, takeEvenMean); MaskedArray absdiff = abs(a - med); return median(absdiff, false, takeEvenMean); } template MaskedArray square(const MaskedArray &left) { MaskedArray result (left.copy()); bool resultarrDelete; T *resultarrStorage = result.getRWArrayStorage(resultarrDelete); T *resultarrS = resultarrStorage; bool resultmaskDelete; const LogicalArrayElem *resultmaskStorage = result.getMaskStorage(resultmaskDelete); const LogicalArrayElem *resultmaskS = resultmaskStorage; size_t ntotal = result.nelements(); while (ntotal--) { if (*resultmaskS) { *resultarrS *= *resultarrS; } resultarrS++; resultmaskS++; } result.putArrayStorage(resultarrStorage, resultarrDelete); result.freeMaskStorage(resultmaskStorage, resultmaskDelete); return result; } template MaskedArray cube(const MaskedArray &left) { MaskedArray result (left.copy()); bool resultarrDelete; T *resultarrStorage = result.getRWArrayStorage(resultarrDelete); T *resultarrS = resultarrStorage; bool resultmaskDelete; const LogicalArrayElem *resultmaskStorage = result.getMaskStorage(resultmaskDelete); const LogicalArrayElem *resultmaskS = resultmaskStorage; size_t ntotal = result.nelements(); while (ntotal--) { if (*resultmaskS) { *resultarrS *= (*resultarrS * *resultarrS); } resultarrS++; resultmaskS++; } result.putArrayStorage(resultarrStorage, resultarrDelete); result.freeMaskStorage(resultmaskStorage, resultmaskDelete); return result; } template MaskedArray boxedArrayMath (const MaskedArray& array, const IPosition& boxSize, const FuncType& funcObj) { size_t ndim = array.ndim(); const IPosition& shape = array.shape(); // Set missing axes to 1. IPosition boxsz (boxSize); if (boxsz.size() != ndim) { size_t sz = boxsz.size(); boxsz.resize (ndim); for (size_t i=sz; i shape[i]) { boxsz[i] = shape[i]; } resShape[i] = (shape[i] + boxsz[i] - 1) / boxsz[i]; } // Need to make shallow copy because operator() is non-const. MaskedArray arr (array); Array result (resShape); Array resultMask(resShape); T* res = result.data(); bool* resMask = resultMask.data(); // Loop through all data and assemble as needed. IPosition blc(ndim, 0); IPosition trc(boxsz-1); while (true) { MaskedArray subarr (arr(blc,trc)); if (subarr.nelementsValid() == 0) { *resMask++ = false; *res++ = T(); } else { *resMask++ = true; *res++ = funcObj (arr(blc,trc)); } size_t ax; for (ax=0; ax= shape[ax]) { trc[ax] = shape[ax]-1; } break; } blc[ax] = 0; trc[ax] = boxsz[ax]-1; } if (ax == ndim) { break; } } return MaskedArray (result, resultMask); } template Array slidingArrayMath (const MaskedArray& array, const IPosition& halfBoxSize, const FuncType& funcObj, bool fillEdge) { size_t ndim = array.ndim(); const IPosition& shape = array.shape(); // Set full box size (-1) and resize/fill as needed. IPosition hboxsz (2*halfBoxSize); if (hboxsz.size() != array.ndim()) { size_t sz = hboxsz.size(); hboxsz.resize (array.ndim()); for (size_t i=sz; i(); } Array res(shape); res = T(); return res; } } // Need to make shallow copy because operator() is non-const. MaskedArray arr (array); Array result (resShape); assert (result.contiguousStorage() ); T* res = result.data(); // Loop through all data and assemble as needed. IPosition blc(ndim, 0); IPosition trc(hboxsz); IPosition pos(ndim, 0); while (true) { *res++ = funcObj (arr(blc,trc)); size_t ax; for (ax=0; ax fullResult(shape); fullResult = T(); hboxsz /= 2; fullResult(hboxsz, resShape+hboxsz-1).assign_conforming( result ); return fullResult; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Arrays/MaskArrMath2.cc000066400000000000000000000041631476623553700202430ustar00rootroot00000000000000//# MaskArrMath2.cc: Arithmetic functions defined on MaskedArrays //# Copyright (C) 1993,1994,1995,1996 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include "MaskArrMath.h" #include "ArrayError.h" namespace casacore { //# NAMESPACE CASACORE - BEGIN MaskedArray> operator * (const MaskedArray> &left, const float &right) { MaskedArray> retval; retval = left; bool zapIt; std::complex* storage = retval.getRWArrayStorage(zapIt); int ntotal = retval.nelements(); bool leftmaskDelete; const LogicalArrayElem *leftmaskStorage = left.getMaskStorage(leftmaskDelete); for (int i=0; i< ntotal; i++) { if (leftmaskStorage[i]) storage[i] *= right; } retval.putArrayStorage(storage, zapIt); left.freeMaskStorage(leftmaskStorage, leftmaskDelete); return retval; } MaskedArray> operator*(const float& left, const MaskedArray> &right) { return operator*(right,left); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Arrays/MaskLogiArr.h000066400000000000000000000052441476623553700200250ustar00rootroot00000000000000//# MaskLogiArr.h: Masked logical arrays. //# Copyright (C) 1994,1995,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MASKLOGIARR_2_H #define CASA_MASKLOGIARR_2_H //# There is no source file, so this pragma is not needed. #if 0 #endif #include "MaskLogiArrFwd.h" #include "LogiArray.h" #include "MaskedArray.h" namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Masked LogicalArrays. // // // // //
      • Array //
      • LogicalArray //
      • MaskedArray //
      • MaskLogiArrFwd // // // // MaskLogiArr is short for MaskedLogicalArray, which is too long by // the old AIPS++ file naming conventions. This file contains typedefs // for MaskedLogicalArrays. // // // // These classes are analogous to MaskedArrays, where the type of the // Array is a LogicalArray. // // // // One wants to be able to mask and use LogicalArrays the same way one // can mask and use general Arrays. // // // // MaskedLogicalArray -- Masked LogicalArrays. // // // //# This is empty. Everything is done by the include files. // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Arrays/MaskLogiArrFwd.h000066400000000000000000000065571476623553700204760ustar00rootroot00000000000000//# MaskLogiArrFwd.h: Forwards for MaskedLogicalArrays. //# Copyright (C) 1994,1995,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MASKLOGIARRFWD_2_H #define CASA_MASKLOGIARRFWD_2_H //# There is no source file, so this pragma is not needed. #if 0 #endif #include "ArrayFwd.h" namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Forward declarations for MaskedLogicalArrays. // // // // //
      • Array //
      • MaskedArray //
      • LogicalArray // // // // MaskLogiArrayFwd is short for MaskedLogicalArrayForwards, which is // too long by the old AIPS++ file naming conventions. // It contains forwards for MaskedLogicalArrays. // // // // This file contains forward definitions for MaskedLogicalArrays. // // // // There are places where MaskedLogicalArrays cannot be defined, i.e. where // MaskLogiArr.h cannot be included. In those places, one must provide // forward declarations for those objects which one is using. // This is particularly tricky for MaskedLogicalArrays, since they are not // classes, but are rather typedefs. In order to make these forwards // easier to get correct, and easier to maintain, this file was created. // If MaskedLogicalArrays should ever be change to a class, only this file would // need to be changed, instead of every file where the current typedefs // would have to be changed to class forwards. // // // // MaskedLogicalArray forwards -- Forward declarations for // MaskedLogicalArrays. // // // //# Forwards template class MaskedArray; // Define MaskedLogicalArray. // typedef MaskedArray MaskedLogicalArray; // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Arrays/MaskedArray.h000066400000000000000000000627031476623553700200600ustar00rootroot00000000000000//# MaskedArray.h: A templated N-D masked array class with zero origin. //# Copyright (C) 1993,1994,1995,1996,1997,1999,2000,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MASKEDARRAY2_H #define CASA_MASKEDARRAY2_H //# Includes #include "ArrayLogical.h" #include "ArrayFwd.h" #include "IPosition.h" #include "MaskLogiArrFwd.h" namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations //# //# Array.h cannot be included in this header file. Anything needed //# from it must be forwarded. This is why LogicalArrayFwd.h is //# included instead of LogicalArray.h . //# class Slicer; // Class for masking an Array for operations on that Array. // // // // //
      • Array //
      • LogicalArray // // // // MaskedArray is a class for masking elements of an Array while performing // operations on that Array. // // // // A MaskedArray is an association between an Array and a mask. The mask // selects elements of the Array. Only elements of the Array where the // corresponding element of the mask is true are defined. Thus, operations // on a MaskedArray only operate on those elements of the Array where the // corresponding element of the mask is true. // // A MaskedArray should be thought of as a manipulator for an Array, analogous // to an iterator. It allows one to perform whole Array operations on selected // elements of the Array. // // The mask used in the constructor for the MaskedArray must conform to // the Array, thus have the same shape. // The internal mask is (will be) copy constructed with reference semantics // from the input mask. Therefore, it is (will be) possible to change the // internal mask by changing values in the input mask *after* the MaskedArray // has been constructed. To ensure that the internal mask is independent of // the input mask after construction, use mask.copy() as the input argument. // // One can explicitly construct a MaskedArray from an Array and a mask or // a MaskedArray and a mask. One can also use operator() on an Array or // a MaskedArray to implicitly create a MaskedArray. // // One can create a MaskedArray from a MaskedArray and a mask. The resulting // MaskedArray has as its Array the Array from the original MaskedArray. // The mask for the resulting MaskedArray is the AND of the mask from the // original MaskedArray and the input mask. // // Any operation involving a MaskedArray or a set of MaskedArrays is only // performed for those elements where the AND of the masks is true. // // Any operation involving a MaskedArray or a set of MaskedArrays results // in a MaskedArray whose mask is the AND of the masks of the original // MaskedArrays. The only exception to this is assignment, where the // mask determines which elements of the underlying Array are assigned. // // Masks, which are LogicalArrays, can be constructed by logical operations // involving Arrays. They can also, of course, be constructed by individually // setting individual elements of an LogicalArray. // // MaskedArrays constructed directly from Arrays are by default writeable. // MaskedArrays constructed indirectly from Arrays by operator() // are writeable if the Array is non-const and are readonly if the // Array is const. // MaskedArrays constructed from other MaskedArrays, either directly by // constructors or indirectly by operator(), are by default // writeable if the input MaskedArray is writeable, and readonly if the // input MaskedArray is readonly. // // A given MaskedArray can be set to be readonly. One specifies // this in the constructor with the bool argument isreadonly, // or calls the setReadOnly() member function. // A MaskedArray which would default to be readonly cannot be forced to // be writeable. It will remain readonly even if the bool argument // isreadonly is set to be false. // // The isReadOnly(), member function is used to test whether // the MaskedArray is readonly. // // Member functions which change the MaskedArray test to see whether // the MaskedArray is readonly, and throw an ArrayError exception if // it is. These member functions are: //
          //
        • operator=() //
        • getRWArray() //
        • getRWArrayStorage() //
        • putArrayStorage() //
        // // The copy() member function makes a deep copy of a MaskedArray. // By default it returns a writeable MaskedArray, but the MaskedArray // returned can be made readonly by using the bool argument "isreadonly" // to copy() (or by calling setReadOnly() on the new MaskedArray). // // The valid elements of the MaskedArray can be manipulated as a // "compressed" Array which contains only the valid elements. // The number of elements in this "compressed" Array is the number of valid // elements in the MaskedArray, nelementsValid(). // The "compressed" Array can have any shape which meets this requirement. // The MaskedArray can have any shape. // // The getCompressedArray() member functions get a compressed // Array from the valid members of the MaskedArray, while the // setCompressedArray() member function sets the valid members // of the MaskedArray from the input compressed Array. // // Many mathematical and logical global operators and functions which operate // on MaskedArrays are defined. Typically, they are defined for all sensible // combinations of MaskedArrays, Arrays, and scalars. // // Mathematical global operators and functions are defined in // Arrays/MaskArrMath.h . // The list is: //
          //
        • operator+= () //
        • operator-= () //
        • operator*= () //
        • operator/= () //
        • operator+ () //
        • operator- () //
        • operator* () //
        • operator/ () //
        • sin () //
        • cos () //
        • tan () //
        • asin () //
        • acos () //
        • atan () //
        • sinh () //
        • cosh () //
        • tanh () //
        • exp () //
        • log () //
        • log10 () //
        • sqrt () //
        • abs () //
        • fabs () //
        • ceil () //
        • floor () //
        • atan2 () //
        • fmod () //
        • pow () //
        • minMax () //
        • min () //
        • max () //
        • indgen () //
        • sum () //
        • sumsquares () //
        • product () //
        • mean () //
        • variance () //
        • stddev () //
        • avdev () //
        • median () //
        • square () //
        • cube () //
        // // Logical global operators and functions are defined in // Arrays/MaskArrLogi.h . // The list is: //
          //
        • allLE () //
        • allLT () //
        • allGE () //
        • allGT () //
        • allEQ () //
        • allNE () //
        • allAND () //
        • allOR () //
        • anyLE () //
        • anyLT () //
        • anyGE () //
        • anyGT () //
        • anyEQ () //
        • anyNE () //
        • anyAND () //
        • anyOR () //
        • operator<= () //
        • operator< () //
        • operator>= () //
        • operator< () //
        • operator== () //
        • operator!= () //
        • operator&& () //
        • operator|| () //
        //
        // // // Use an explicit MaskedArray to limit the maximum value of an Array. // // // Vector arr (20); // . . . // MaskedArray marr (arr, (arr > 5)); // marr = 5; // // // This sets all elements of arr which are greater than 5 to 5. // // // // Use an implicit MaskedArray to limit the minimum value of an Array. // // // Vector arr (20); // . . . // arr (arr < 0) = 0; // // // This sets all elements of arr which are less than 0 to 0. // // // // It does not matter where in an expression the MaskedArrays are located. // The operation is only performed on those elements where the AND of the // masks is true. // // The following expressions are all equivalent. // The first (and second) expressions are the most efficient, since the sum // is only performed for those elements where ((a > 0) && (b > 0)). // The third example is less efficient, since the sum is performed for // all elements of a and b, and then the assignment is only performed for // those elements where ((a > 0) && (b > 0)). // // // Vector arr (20); // Vector a (20); // Vector b (20); // . . . // arr = a(a > 0) + b(b > 0); // // arr = a(b > 0) + b(a > 0); // // arr ((a > 0) && (b > 0)) = a + b; // // arr = (a + b) ((a > 0) && (b > 0)); // // arr (a > 0) = a + b(b > 0); // // // // All of these expressions set those elements of arr where // ((a > 0) && (b > 0)) to a + b. Those elements of arr where the condition // is false are unchanged. // // // // This example extracts the valid elements of the MaskedArray as a // "compressed" Vector, manipulates this Vector, and then puts the result // back into the MaskedArray. // // // Matrix arr (20,5); // . . . // MaskedArray marr (arr, (arr>0) && (arr<10)); // Vector vec (marr.getCompressedArray()); // . . . // marr.setCompressedArray (vec); // // // // // // A MaskedArray is an association between an Array and a LogicalArray which // masks the Array. It allows one to perform whole Array manipulations // with a single expression, selecting those elements of the Array to modify // based either on a logical expression, typically involving some of the // Arrays involved in the expression, or based on a specifically set mask. // // // masked_array_type operator()(const IPosition &start, const IPosition &end); // Along the ith axis, every inc[i]'th element is chosen. masked_array_type operator()(const IPosition &start, const IPosition &end, const IPosition &inc); // // Get a reference to an array using a Slicer. masked_array_type operator()(const Slicer&); // Make a copy of the masked array. // // This is a deep copy. The Array and mask components of the returned // MaskedArray are deep copies of the Array and mask in the input // MaskedArray pointed to by this. In other words, the Array and mask // in the output MaskedArray are completely independent of those in // the input MaskedArray. // // By default, the MaskedArray returned is writeable. If // isreadonly is true, then the MaskedArray // returned is readonly. // // masked_array_type copy(bool isreadonly) const; masked_array_type copy() const; // // Return the internal Array. const array_type & getArray() const; // Return the internal Array, writeable. // // //
      • ArrayError // // array_type & getRWArray() const; // Return the (const) internal Mask. const mask_type & getMask() const; // The dimensionality of this masked array. size_t ndim() const; // The number of elements of this masked array. // This is the number of elements in the underlying Array. // size_t nelements() const; size_t size() const { return nelements(); } // // The number of valid elements of this masked array. // This is the number of elements of the mask which are TRUE. size_t nelementsValid() const; // Check to see if the masked array is consistent. This is about the same // thing as checking for invariants. If AIPS_DEBUG is defined, this is // invoked after construction and on entry to most member functions. bool ok() const; // Are the shapes identical? // bool conform(const array_type &other) const; bool conform(const masked_array_type &other) const; // // The length of each axis. const IPosition& shape() const { return pArray->shape(); } // Is the array read only? bool isReadOnly() const { return isRO; } // Set the array to be read only. void setReadOnly() const; // Copy the values in inarray to this, only copying those elements // for which the corresponding mask element is true. // //
      • ArrayConformanceError //
      • ArrayError // // TODO rename, see copy assignment operator. masked_array_type &operator=(const array_type &inarray); masked_array_type &operator=(array_type&& inarray); // Copies/moves the values in other to this, only copying those elements // for which the logical AND of the corresponding mask elements // of both MaskedArrays is true. // // //
      • ArrayConformanceError //
      • ArrayError // // // // TODO this should be renamed: assignment operator should make // obervable state equal, which should thus include getArray(). masked_array_type &operator=(const masked_array_type &other); masked_array_type &operator=(masked_array_type&& other); // // Set every element of this array to "value", only setting those elements // for which the corresponding mask element is true. // In other words, a scalar behaves as if it were a constant conformant // array. // // //
      • ArrayError // // masked_array_type &operator=(const T &value); // Return a "compressed" Array containing only the valid // elements of the MaskedArray. The number of elements in the // Array will be nelementsValid() for the // MaskedArray. The MaskedArray can have any shape. // // The returned Array will have dimension one. Array getCompressedArray () const; // The returned Array will have the input shape. This shape must // give the returned Array the required number of elements. // // //
      • ArrayError // // Array getCompressedArray (const IPosition & shape) const; // // Fill the argument "compressed" Array with only the // valid elements of the MaskedArray. The size of the // Array must be nelementsValid() for the MaskedArray. // The Array can have any shape which meets this requirement. // The MaskedArray can have any shape. // // //
      • ArrayError // // void getCompressedArray (array_type & inarr) const; // Set only the valid elements of the MaskedArray from the argument // "compressed" Array. The size of the // Array must be nelementsValid() for the MaskedArray. // The Array can have any shape which meets this requirement. // The MaskedArray can have any shape. // // //
      • ArrayError // // void setCompressedArray (const array_type& inarr); // Manipulate the storage for the underlying Array. // See the description of the corresponding Array functions // for more information. // const T * getArrayStorage (bool &deleteIt) const; // // //
      • ArrayError // // T * getRWArrayStorage (bool &deleteIt) const; // void freeArrayStorage(const T *&storage, bool deleteIt) const; // // //
      • ArrayError // // void putArrayStorage(T *&storage, bool deleteAndCopy) const; // // Manipulate the storage for the underlying Mask. // See the description of the corresponding Array functions // for more information. // const LogicalArrayElem *getMaskStorage (bool &deleteIt) const; // void freeMaskStorage(const LogicalArrayElem *&storage, bool deleteIt) const; // protected: // The array. std::unique_ptr pArray; // The mask. std::unique_ptr pMask; // Cache the number of valid elements. size_t nelemValid; // Is the number of valid elements cache OK? // i.e. has it been calculated? bool nelemValidIsOK; // Is the array read only? bool isRO; }; // General global functions for MaskedArrays, and MaskedArrays and Arrays. // // // //
      • Array //
      • LogicalArray //
      • MaskedArray // // // // These are generally useful global functions which operate on all // MaskedArrays, or on MaskedArrays and Arrays. // // // // MaskedArray general global functions -- General global // functions for MaskedArrays, and between MaskedArrays and Arrays. // // // // Test conformance for masked arrays and arrays of different types. // Are the shapes identical? // // // template bool conform2 (const MaskedArray &left, const Array &right); template bool conform2 (const Array &left, const MaskedArray &right); template bool conform2 (const MaskedArray &left, const MaskedArray &right); // // // } //# NAMESPACE CASACORE - END #include "MaskedArray.tcc" #endif casacore-3.7.1/casa/Arrays/MaskedArray.tcc000066400000000000000000000543171476623553700204040ustar00rootroot00000000000000//# MaskedArray.cc: A templated N-D masked array class with variable origin. //# Copyright (C) 1993,1994,1995,1996,1997,1999,2001,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MASKEDARRAY_2_TCC #define CASA_MASKEDARRAY_2_TCC #include "MaskedArray.h" #include "Array.h" #include "ArrayLogical.h" #include "Slicer.h" #include "ArrayError.h" #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template MaskedArray::MaskedArray () : pArray (), pMask (), nelemValid (0), nelemValidIsOK (false), isRO (false) {} template MaskedArray::MaskedArray (const array_type &inarray, const LogicalArray &inmask, bool isreadonly) : pArray (), pMask (), nelemValid (0), nelemValidIsOK (false), isRO (isreadonly) { // if (! conform2 (inarray, inmask)) { if (inarray.shape() != inmask.shape()) { throw (ArrayConformanceError( "MaskedArray::MaskedArray(const array_type &," " const LogicalArray &, bool)" " - arrays do not conform")); } pArray.reset( new array_type (inarray) ); pMask.reset( new LogicalArray (inmask.shape()) ); pMask->assign_conforming(inmask); assert(ok()); } template MaskedArray::MaskedArray (const array_type &inarray, const LogicalArray &inmask) : pArray(), pMask (), nelemValid (0), nelemValidIsOK (false), isRO (false) { // if (! conform2 (inarray, inmask)) { if (inarray.shape() != inmask.shape()) { throw (ArrayConformanceError( "MaskedArray::MaskedArray(const array_type &," " const LogicalArray &)" " - arrays do not conform")); } pArray.reset( new array_type (inarray) ); pMask.reset( new LogicalArray (inmask.shape()) ); pMask->assign_conforming(inmask); assert(ok()); } template MaskedArray::MaskedArray (const MaskedArray &inarray, const LogicalArray &inmask, bool isreadonly) : pArray (), pMask (), nelemValid (0), nelemValidIsOK (false), isRO ( (inarray.isRO || isreadonly)) { // if (! conform2 (inarray, inmask)) { if (inarray.shape() != inmask.shape()) { throw (ArrayConformanceError( "MaskedArray::MaskedArray (const MaskedArray &," " const LogicalArray &, bool)" " - arrays do not conform")); } pArray.reset( new array_type (inarray.getArray()) ); pMask.reset( new LogicalArray (inmask.shape()) ); *pMask = (inmask && inarray.getMask()); assert(ok()); } template MaskedArray::MaskedArray (const MaskedArray &inarray, const LogicalArray &inmask) : pArray (), pMask (), nelemValid (0), nelemValidIsOK (false), isRO (inarray.isRO) { // if (! conform2 (inarray, inmask)) { if (inarray.shape() != inmask.shape()) { throw (ArrayConformanceError( "MaskedArray::MaskedArray (const MaskedArray &," " const LogicalArray &)" " - arrays do not conform")); } pArray.reset( new array_type (inarray.getArray()) ); pMask.reset( new LogicalArray (inmask.shape()) ); *pMask = (inmask && inarray.getMask()); assert(ok()); } template MaskedArray::MaskedArray (const array_type &inarray, const MaskedLogicalArray &inmask, bool isreadonly) : pArray (), pMask (), nelemValid (0), nelemValidIsOK (false), isRO (isreadonly) { // if (! conform2 (inarray, inmask)) { if (inarray.shape() != inmask.shape()) { throw (ArrayConformanceError( "MaskedArray::MaskedArray(const array_type &inarray," " const MaskedLogicalArray &inmask, bool isreadonly)" " - arrays do not conform")); } pArray.reset( new array_type (inarray) ); pMask.reset( new LogicalArray (inarray.shape()) ); pMask->assign_conforming(inmask.getArray() && inmask.getMask()); assert(ok()); } template MaskedArray::MaskedArray (const array_type &inarray, const MaskedLogicalArray &inmask) : pArray (), pMask (), nelemValid (0), nelemValidIsOK (false), isRO (false) { // if (! conform2 (inarray, inmask)) { if (inarray.shape() != inmask.shape()) { throw (ArrayConformanceError( "MaskedArray::MaskedArray(const array_type &inarray," " const MaskedLogicalArray &inmask)" " - arrays do not conform")); } pArray.reset( new array_type (inarray) ); pMask.reset( new LogicalArray (inarray.shape()) ); pMask->assign_conforming(inmask.getArray() && inmask.getMask()); assert(ok()); } template MaskedArray::MaskedArray (const MaskedArray &inarray, const MaskedLogicalArray &inmask) : pArray (), pMask (), nelemValid (0), nelemValidIsOK (false), isRO (inarray.isRO) { // if (! conform2 (inarray, inmask)) { if (inarray.shape() != inmask.shape()) { throw (ArrayConformanceError( "MaskedArray::MaskedArray (const MaskedArray &inarray," " const MaskedLogicalArray &inmask)" " - arrays do not conform")); } pArray.reset( new array_type (inarray.getArray()) ); pMask.reset( new LogicalArray (inarray.shape()) ); *pMask = (inmask.getArray() && inmask.getMask() && inarray.getMask()); assert(ok()); } template MaskedArray::MaskedArray (const MaskedArray &inarray, const MaskedLogicalArray &inmask, bool isreadonly) : pArray (), pMask (), nelemValid (0), nelemValidIsOK (false), isRO ( (inarray.isRO || isreadonly)) { // if (! conform2 (inarray, inmask)) { if (inarray.shape() != inmask.shape()) { throw (ArrayConformanceError( "MaskedArray::MaskedArray (const MaskedArray &inarray," " const MaskedLogicalArray &inmask, bool isreadonly)" " - arrays do not conform")); } pArray.reset( new array_type (inarray.getArray()) ); pMask.reset( new LogicalArray (inarray.shape()) ); *pMask = (inmask.getArray() && inmask.getMask() && inarray.getMask()); assert(ok()); } template MaskedArray::MaskedArray(const MaskedArray &other, bool isreadonly) : pArray (), pMask (), nelemValid (other.nelemValid), nelemValidIsOK (other.nelemValidIsOK), isRO ( (other.isRO || isreadonly)) { pArray.reset( new array_type (*(other.pArray)) ); pMask.reset( new LogicalArray (*(other.pMask)) ); assert(ok()); } template MaskedArray::MaskedArray(const MaskedArray &other) : pArray (), pMask (), nelemValid (other.nelemValid), nelemValidIsOK (other.nelemValidIsOK), isRO (other.isRO) { pArray.reset( new array_type (*(other.pArray)) ); pMask.reset( new LogicalArray (*(other.pMask)) ); assert(ok()); } template MaskedArray::MaskedArray(MaskedArray&& source) : pArray (std::move(source.pArray)), pMask (std::move(source.pMask)), nelemValid (source.nelemValid), nelemValidIsOK (source.nelemValidIsOK), isRO (source.isRO) { source.nelemValid = 0; source.nelemValidIsOK = false; source.isRO = false; assert(ok()); assert(source.ok()); } template void MaskedArray::setData (const array_type &data, const mask_type &mask, bool isReadOnly) { if (data.shape() != mask.shape()) { throw (ArrayConformanceError( "MaskedArray::setData(const array_type &," " const LogicalArray &, bool)" " - arrays do not conform")); } pArray.reset( new array_type(data) ); pMask.reset( new mask_type (mask.copy()) ); nelemValid = 0; nelemValidIsOK = false; isRO = isReadOnly; assert(ok()); } template void MaskedArray::setData (const MaskedArray & array, bool isReadOnly){ pArray.reset( new array_type(array.getArray()) ); pMask.reset( new LogicalArray(array.getMask().copy()) ); nelemValid = 0; nelemValidIsOK = false; isRO = isReadOnly; assert(ok()); } template MaskedArray MaskedArray::copy(bool isreadonly) const { assert(ok()); MaskedArray retval (pArray->copy(), *pMask, isreadonly); retval.nelemValid = nelemValid; retval.nelemValidIsOK = nelemValidIsOK; return retval; } template MaskedArray MaskedArray::copy() const { assert(ok()); MaskedArray retval (pArray->copy(), *pMask); retval.nelemValid = nelemValid; retval.nelemValidIsOK = nelemValidIsOK; return retval; } template MaskedArray MaskedArray::operator() (const LogicalArray &mask) const { assert(ok()); MaskedArray ret (*this, mask); return ret; } template MaskedArray MaskedArray::operator() (const MaskedLogicalArray &mask) const { assert(ok()); MaskedArray ret (*this, mask); return ret; } template MaskedArray MaskedArray::operator() (const IPosition &start, const IPosition &end) { assert(ok()); return MaskedArray ((*pArray)(start,end), (*pMask)(start,end), isRO); } template MaskedArray MaskedArray::operator() (const IPosition &start, const IPosition &end, const IPosition &inc) { assert(ok()); return MaskedArray ((*pArray)(start,end,inc), (*pMask)(start,end,inc), isRO); } template MaskedArray MaskedArray::operator() (const Slicer &slicer) { assert(ok()); return MaskedArray ((*pArray)(slicer), (*pMask)(slicer), isRO); } template const Array &MaskedArray::getArray() const { assert(ok()); return *pArray; } template const Array & MaskedArray::getMask() const { assert(ok()); return *pMask; } template size_t MaskedArray::ndim() const { assert(ok()); return pArray->ndim(); } template size_t MaskedArray::nelementsValid() const { assert(ok()); if (!nelemValidIsOK) { // Calculate nelemValid; bool maskDelete; const LogicalArrayElem *maskStorage = getMaskStorage(maskDelete); const LogicalArrayElem *maskS = maskStorage; size_t nelemValidTmp = 0; size_t ntotal = nelements(); while (ntotal--) { if (*maskS) { nelemValidTmp++; } maskS++; } freeMaskStorage(maskStorage, maskDelete); MaskedArray *nonconstThis = (MaskedArray *) this; nonconstThis->nelemValid = nelemValidTmp; nonconstThis->nelemValidIsOK = true; } return nelemValid; } template size_t MaskedArray::nelements() const { assert(ok()); return pArray->nelements(); } template bool MaskedArray::ok() const { if (!pArray && !pMask) return true; // default constructed is ok if (!pArray || !pMask) return false; // not both set is not ok return (pArray->ok() && pMask->ok()) ? true : false; } template bool MaskedArray::conform(const array_type &other) const { assert(ok()); return pArray->conform(other); } template bool MaskedArray::conform(const MaskedArray &other) const { assert(ok()); return pArray->conform(*(other.pArray)); } template void MaskedArray::setReadOnly() const { assert(ok()); MaskedArray *nonconstThis = (MaskedArray *) this; nonconstThis->isRO = true; } template Array MaskedArray::getCompressedArray () const { array_type result (IPosition (1,nelementsValid())); bool deleteResult; T *resultStorage = result.getStorage (deleteResult); T *resultS = resultStorage; bool deleteArr; const T *arrStorage = getArrayStorage (deleteArr); const T *arrS = arrStorage; bool deleteMask; const LogicalArrayElem *maskStorage = getMaskStorage (deleteMask); const LogicalArrayElem *maskS = maskStorage; size_t ntotal = nelementsValid(); while (ntotal) { if (*maskS) { *resultS = *arrS; resultS++; ntotal--; } maskS++; arrS++; } result.putStorage (resultStorage, deleteResult); freeArrayStorage (arrStorage, deleteArr); freeMaskStorage (maskStorage, deleteMask); return result; } template Array MaskedArray::getCompressedArray (const IPosition & shape) const { if (int(nelementsValid()) != shape.product()) { throw (ArrayError ("void MaskedArray::getCompressedArray (const IPosition & shape)" " - input shape will create Array with incorrect number of elements")); } array_type result (shape); bool deleteResult; T *resultStorage = result.getStorage (deleteResult); T *resultS = resultStorage; bool deleteArr; const T *arrStorage = getArrayStorage (deleteArr); const T *arrS = arrStorage; bool deleteMask; const LogicalArrayElem *maskStorage = getMaskStorage (deleteMask); const LogicalArrayElem *maskS = maskStorage; size_t ntotal = nelementsValid(); while (ntotal) { if (*maskS) { *resultS = *arrS; resultS++; ntotal--; } maskS++; arrS++; } result.putStorage (resultStorage, deleteResult); freeArrayStorage (arrStorage, deleteArr); freeMaskStorage (maskStorage, deleteMask); return result; } template void MaskedArray::getCompressedArray (array_type & inarr) const { if (nelementsValid() != inarr.nelements()) { throw (ArrayError ("void MaskedArray::getCompressedArray (array_type & inarr)" " - input Array number of elements is incorrect")); } bool deleteInarr; T *inarrStorage = inarr.getStorage (deleteInarr); T *inarrS = inarrStorage; bool deleteArr; const T *arrStorage = getArrayStorage (deleteArr); const T *arrS = arrStorage; bool deleteMask; const LogicalArrayElem *maskStorage = getMaskStorage (deleteMask); const LogicalArrayElem *maskS = maskStorage; size_t ntotal = nelementsValid(); while (ntotal) { if (*maskS) { *inarrS = *arrS; inarrS++; ntotal--; } maskS++; arrS++; } inarr.putStorage (inarrStorage, deleteInarr); freeArrayStorage (arrStorage, deleteArr); freeMaskStorage (maskStorage, deleteMask); } template void MaskedArray::setCompressedArray (const array_type & inarr) { if (nelementsValid() != inarr.nelements()) { throw (ArrayError ("void MaskedArray::setCompressedArray (const array_type & inarr)" " - input array number of elements is incorrect")); } bool deleteInarr; const T *inarrStorage = inarr.getStorage (deleteInarr); const T *inarrS = inarrStorage; bool deleteArr; T *arrStorage = getRWArrayStorage (deleteArr); T *arrS = arrStorage; bool deleteMask; const LogicalArrayElem *maskStorage = getMaskStorage (deleteMask); const LogicalArrayElem *maskS = maskStorage; size_t ntotal = nelementsValid(); while (ntotal) { if (*maskS) { *arrS = *inarrS; inarrS++; ntotal--; } maskS++; arrS++; } inarr.freeStorage (inarrStorage, deleteInarr); putArrayStorage (arrStorage, deleteArr); freeMaskStorage (maskStorage, deleteMask); } template const T * MaskedArray::getArrayStorage (bool &deleteIt) const { assert(ok()); return pArray->getStorage (deleteIt); } template void MaskedArray::freeArrayStorage(const T *&storage, bool deleteIt) const { assert(ok()); pArray->freeStorage (storage, deleteIt); } template const LogicalArrayElem * MaskedArray::getMaskStorage (bool &deleteIt) const { assert(ok()); return pMask->getStorage (deleteIt); } template void MaskedArray::freeMaskStorage (const LogicalArrayElem *&storage, bool deleteIt) const { assert(ok()); pMask->freeStorage (storage, deleteIt); } template MaskedArray& MaskedArray::operator= (const array_type &inarray) { assert(ok()); if (!pArray) { pArray.reset( new array_type(inarray) ); pMask.reset( new mask_type(inarray.shape(), true) ); nelemValid = 0; nelemValidIsOK = false; isRO = false; return *this; } if (!conform(inarray)) { throw(ArrayConformanceError( "MaskedArray & MaskedArray::operator= " "(const array_type &inarray)" "- Conformance error.")); } if (isRO) { throw(ArrayError( "MaskedArray & MaskedArray::operator= " "(const array_type &inarray)" "- this is read only.")); } bool deleteArr; T *arrStorage = getRWArrayStorage(deleteArr); T *arrS = arrStorage; bool deleteMask; const LogicalArrayElem *maskStorage = getMaskStorage(deleteMask); const LogicalArrayElem *maskS = maskStorage; bool deleteInarr; const T *inarrStorage = inarray.getStorage(deleteInarr); const T *inarrS = inarrStorage; size_t ntotal = pArray->nelements(); while (ntotal--) { if (*maskS) { *arrS = *inarrS; } arrS++; maskS++; inarrS++; } putArrayStorage(arrStorage, deleteArr); freeMaskStorage(maskStorage, deleteMask); inarray.freeStorage(inarrStorage, deleteInarr); return *this; } template MaskedArray& MaskedArray::operator= (array_type&& inarray) { assert(ok()); if (!pArray) { pMask.reset( new mask_type(inarray.shape(), true) ); pArray.reset( new array_type(inarray) ); nelemValid = 0; nelemValidIsOK = false; isRO = false; return *this; } else { return operator=(inarray); // do ordinary copy assignment } } template MaskedArray& MaskedArray::operator= (const MaskedArray &other) { assert(ok()); if (this == &other) return *this; if (!pArray) { setData(other.copy()); return *this; } if (!conform(other)) { throw(ArrayConformanceError( "MaskedArray & MaskedArray::operator= " "(const MaskedArray &other)" "- Conformance error.")); } if (isRO) { throw(ArrayError( "MaskedArray & MaskedArray::operator= " "(const MaskedArray &other)" "- this is read only.")); } bool deleteArr; T *arrStorage = getRWArrayStorage(deleteArr); T *arrS = arrStorage; bool deleteMask; const LogicalArrayElem *maskStorage = getMaskStorage(deleteMask); const LogicalArrayElem *maskS = maskStorage; bool deleteOarr; const T *oarrStorage = other.getArrayStorage(deleteOarr); const T *oarrS = oarrStorage; bool deleteOmask; const LogicalArrayElem *omaskStorage = other.getMaskStorage(deleteOmask); const LogicalArrayElem *omaskS = omaskStorage; size_t ntotal = pArray->nelements(); while (ntotal--) { if (*maskS && *omaskS) { *arrS = *oarrS; } arrS++; maskS++; oarrS++; omaskS++; } putArrayStorage(arrStorage, deleteArr); freeMaskStorage(maskStorage, deleteMask); other.freeArrayStorage(oarrStorage, deleteOarr); other.freeMaskStorage(omaskStorage, deleteOmask); return *this; } template MaskedArray& MaskedArray::operator= (MaskedArray&& other) { assert(ok()); if (other.isReadOnly()) return operator=(other); if (this == &other) return *this; if (!pArray) { pArray = std::move(other.pArray); pMask = std::move(other.pMask); nelemValid = 0; nelemValidIsOK = false; isRO = false; other.nelemValid = 0; other.nelemValidIsOK = false; // other.isRO = false; // we already know this is false return *this; } else { return operator=(other); } } template MaskedArray &MaskedArray::operator=(const T &val) { assert(ok()); if (!pArray) return *this; if (isRO) { throw(ArrayError( "MaskedArray & MaskedArray::operator= (const T &val)" "- this is read only.")); } bool deleteArr; T *arrStorage = getRWArrayStorage(deleteArr); T *arrS = arrStorage; bool deleteMask; const LogicalArrayElem *maskStorage = getMaskStorage(deleteMask); const LogicalArrayElem *maskS = maskStorage; size_t ntotal = pArray->nelements(); while (ntotal--) { if (*maskS) { *arrS = val; } arrS++; maskS++; } putArrayStorage(arrStorage, deleteArr); freeMaskStorage(maskStorage, deleteMask); return *this; } template T * MaskedArray::getRWArrayStorage (bool &deleteIt) const { assert(ok()); if (isRO) { throw(ArrayError( "MaskedArray::getRWArrayStorage (bool &deleteIt) const" "- this is read only.")); } return pArray->getStorage (deleteIt); } template void MaskedArray::putArrayStorage(T *&storage, bool deleteAndCopy) const { assert(ok()); if (isRO) { throw(ArrayError( "MaskedArray::putArrayStorage (bool deleteAndCopy) const" "- this is read only.")); } pArray->putStorage (storage, deleteAndCopy); } template Array& MaskedArray::getRWArray() const { assert(ok()); if (isRO) { throw(ArrayError( "array_type & MaskedArray::getRWArray () const" "- this is read only.")); } return *pArray; } //# Global functions. template bool conform2 (const MaskedArray &left, const Array &right) { IPosition leftShape (left.shape()); IPosition rightShape (right.shape()); return ( (leftShape.conform (rightShape)) && (leftShape == rightShape) ) ? true : false; } template bool conform2 (const Array &left, const MaskedArray &right) { IPosition leftShape (left.shape()); IPosition rightShape (right.shape()); return ( (leftShape.conform (rightShape)) && (leftShape == rightShape) ) ? true : false; } template bool conform2 (const MaskedArray &left, const MaskedArray &right) { IPosition leftShape (left.shape()); IPosition rightShape (right.shape()); return ( (leftShape.conform (rightShape)) && (leftShape == rightShape) ) ? true : false; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Arrays/Matrix.h000066400000000000000000000300471476623553700171150ustar00rootroot00000000000000//# Matrix.h: A 2-D Specialization of the Array Class //# Copyright (C) 1993,1994,1995,1996,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MATRIX_2_H #define CASA_MATRIX_2_H //# Includes #include "Array.h" namespace casacore { //#Begin casa namespace // A 2-D Specialization of the Array class // // // // Matrix objects are two-dimensional specializations (e.g., more convenient // and efficient indexing) of the general Array class. You might also want // to look at the Array documentation to see inherited functionality. A // tutorial on using the array classes in general is available in the // "AIPS++ Programming Manual". // // Generally the member functions of Array are also available in // Matrix versions which take a pair of integers where the array // needs an IPosition. Since the Matrix // is two-dimensional, the IPositions are overkill, although you may // use those versions if you want to. // // Matrix mi(100,100); // Shape is 100x100 // mi.resize(50,50); // Shape now 50x50 // // // Slices may be taken with the Slice class. To take a slice, one "indexes" // with one Slice(start, length, inc) for each axis, // where end and inc are optional. // Additionally, there are row(), column() and diagonal() // member functions which return Vector's which refer to the storage back // in the Matrix: // // Matrix mf(100, 100); // mf.diagonal() = 1; // // // Correct indexing order of a matrix is: // // Matrix mi(n1,n2) // [nrow, ncolumn] // for (size_t j=0; j // // // Element-by-element arithmetic and logical operations are available (in // aips/ArrayMath.h and aips/ArrayLogical.h). Other Matrix operations (e.g. // LU decomposition) are available, and more appear periodically. // // As with the Arrays, if the preprocessor symbol AIPS_DEBUG is // defined at compile time invariants will be checked on entry to most // member functions. Additionally, if AIPS_ARRAY_INDEX_CHECK is defined // index operations will be bounds-checked. Neither of these should // be defined for production code. template class Matrix : public Array { public: // A Matrix of length zero in each dimension; zero origin. Matrix(); // A Matrix with "l1" rows and "l2" columns. // Fill it with the initial value. Matrix(size_t l1, size_t l2, const T &initialValue = T()); // An uninitialized Matrix with "l1" rows and "l2" columns. Matrix(size_t l1, size_t l2, typename Array::uninitializedType); // A matrix of shape with shape "len". // Fill it with the initial value. Matrix(const IPosition &len, const T &initialValue = T()); // An uninitialized matrix of shape with shape "len". Matrix(const IPosition &len, typename Array::uninitializedType); // The copy/move constructor uses reference semantics. Matrix(const Matrix& source); Matrix(Matrix&& source); // Construct a Matrix by reference from "source". "source must have // ndim() of 2 or less. Matrix(const Array& source); Matrix(Array&& source); // Create an Matrix of a given shape from a pointer. Matrix(const IPosition &shape, T *storage, StorageInitPolicy policy = COPY); // Create an Matrix of a given shape from a pointer. Because the pointer // is const, a copy is always made. Matrix(const IPosition &shape, const T *storage); // Create an identity matrix of side length n. (Could not do this as a constructor // because of ambiguities with other constructors). static Matrix identity (size_t n); // Resize to the given shape // using Array::resize; void resize(size_t nx, size_t ny, bool copyValues=false); // Matrix& operator=(const Matrix& source) { return assign_conforming(source); } Matrix& operator=(Matrix&& source) { return assign_conforming(std::move(source)); } Matrix& operator=(const Array& source) { return assign_conforming(source); } Matrix& operator=(Array&& source) { return assign_conforming(std::move(source)); } // Copy the values from other to this Matrix. If this matrix has zero // elements then it will resize to be the same shape as other; otherwise // other must conform to this. // Note that the assign function can be used to assign a // non-conforming matrix. // Matrix& assign_conforming(const Matrix& source) { Array::assign_conforming(source); return *this; } Matrix& assign_conforming(Matrix&& source) { Array::assign_conforming(std::move(source)); return *this; } Matrix& assign_conforming(const Array& source) { // TODO Should be supported by the Array class, // see Cube::operator=(const Array&) if (source.ndim() == 2) { Array::assign_conforming(source); } else { // This might work if a.ndim == 1 or 2 (*this) = Matrix(source); } return *this; } Matrix& assign_conforming(Array&& source) { if (source.ndim() == 2) { Array::assign_conforming(std::move(source)); } else { (*this) = Matrix(std::move(source)); } return *this; } // // Copy val into every element of this Matrix; i.e. behaves as if // val were a constant conformant matrix. Array& operator=(const T &val) { return Array::operator=(val); } // Copy to this those values in marray whose corresponding elements // in marray's mask are true. Matrix& assign_conforming (const MaskedArray &marray) { Array::assign_conforming(marray); return *this; } // Single-pixel addressing. If AIPS_ARRAY_INDEX_CHECK is defined, // bounds checking is performed. // T &operator()(const IPosition &i) { return Array::operator()(i); } const T &operator()(const IPosition &i) const { return Array::operator()(i); } T &operator()(size_t i1, size_t i2) { return this->contiguous_p ? this->begin_p[i1 + i2*yinc()] : this->begin_p[i1*xinc() + i2*yinc()]; } const T &operator()(size_t i1, size_t i2) const { return this->contiguous_p ? this->begin_p[i1 + i2*yinc()] : this->begin_p[i1*xinc() + i2*yinc()]; } // // The array is masked by the input LogicalArray. // This mask must conform to the array. // // Return a MaskedArray. MaskedArray operator() (const LogicalArray &mask) const { return Array::operator() (mask); } // Return a MaskedArray. MaskedArray operator() (const LogicalArray &mask) { return Array::operator() (mask); } // // The array is masked by the input MaskedLogicalArray. // The mask is effectively the AND of the internal LogicalArray // and the internal mask of the MaskedLogicalArray. // The MaskedLogicalArray must conform to the array. // // Return a MaskedArray. MaskedArray operator() (const MaskedLogicalArray &mask) const { return Array::operator() (mask); } // Return a MaskedArray. MaskedArray operator() (const MaskedLogicalArray &mask) { return Array::operator() (mask); } // // Returns a reference to the i'th row. // Vector row(size_t i); const Vector row(size_t i) const; // // Returns a reference to the j'th column // Vector column(size_t j); const Vector column(size_t j) const; // // Returns a diagonal from the Matrix. The Matrix must be square. // n==0 is the main diagonal. n>0 is above the main diagonal, n<0 // is below it. // Vector diagonal(long long n=0); const Vector diagonal(long long n=0) const; // // Take a slice of this matrix. Slices are always indexed starting // at zero. This uses reference semantics, i.e. changing a value // in the slice changes the original. // // Matrix vd(100,100); // //... // vd(Slice(0,10),Slice(10,10)) = -1.0; // 10x10 sub-matrix set to -1.0 // // Matrix operator()(const Slice &sliceX, const Slice &sliceY); const Matrix operator()(const Slice &sliceX, const Slice &sliceY) const; // // Slice using IPositions. Required to be defined, otherwise the base // class versions are hidden. // Array operator()(const IPosition &blc, const IPosition &trc, const IPosition &incr) { return Array::operator()(blc,trc,incr); } const Array operator()(const IPosition &blc, const IPosition &trc, const IPosition &incr) const { return Array::operator()(blc,trc,incr); } Array operator()(const IPosition &blc, const IPosition &trc) { return Array::operator()(blc,trc); } const Array operator()(const IPosition &blc, const IPosition &trc) const { return Array::operator()(blc,trc); } Array operator()(const Slicer& slicer) { return Array::operator()(slicer); } const Array operator()(const Slicer& slicer) const { return Array::operator()(slicer); } // // The length of each axis of the Matrix. const IPosition &shape() const { return this->length_p; } void shape(int &s1, int &s2) const { s1 = this->length_p(0); s2=this->length_p(1); } // The number of rows in the Matrix, i.e. the length of the first axis. size_t nrow() const { return this->length_p(0); } // The number of columns in the Matrix, i.e. the length of the 2nd axis. size_t ncolumn() const { return this->length_p(1); } // Checks that the Matrix is consistent (invariants check out). virtual bool ok() const override; protected: virtual void preTakeStorage(const IPosition &shape) override; // Remove the degenerate axes from other and store result in this matrix. // An exception is thrown if removing degenerate axes does not result // in a matrix. virtual void doNonDegenerate(const Array &other, const IPosition &ignoreAxes) override; virtual size_t fixedDimensionality() const override { return 2; } private: // Cached constants to improve indexing. // size_t xinc_p, yinc_p; size_t xinc() const { return this->inc_p(0); } size_t yinc() const { return this->inc_p(1)*this->originalLength_p(0); } }; //# Declare extern templates for often used types. extern template class Matrix; extern template class Matrix; extern template class Matrix; } //#End casa namespace #include "Matrix.tcc" #endif casacore-3.7.1/casa/Arrays/Matrix.tcc000066400000000000000000000211511476623553700174330ustar00rootroot00000000000000//# Matrix.cc: A 2-D Specialization of the Array Class //# Copyright (C) 1993,1994,1995,1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MATRIX_2_TCC #define CASA_MATRIX_2_TCC #include "Matrix.h" #include "Vector.h" #include "Slice.h" #include "MaskedArray.h" #include "ArrayError.h" #include namespace casacore { //#Begin casa namespace template Matrix::Matrix() : Array(IPosition(2, 0)) { assert(ok()); } template Matrix::Matrix(const IPosition &length, const T &initialValue) : Array(length, initialValue) { Array::checkBeforeResize(length); } template Matrix::Matrix(const IPosition &length, typename Array::uninitializedType) : Array(length, Array::uninitialized) { Array::checkBeforeResize(length); } template Matrix::Matrix(size_t l1, size_t l2, const T &initialValue) : Array(IPosition(2, l1, l2), initialValue) { assert(ok()); } template Matrix::Matrix(size_t l1, size_t l2, typename Array::uninitializedType) : Array(IPosition(2, l1, l2), Array::uninitialized) { assert(ok()); } template Matrix::Matrix(const Matrix& source) : Array(source) { assert(ok()); } template Matrix::Matrix(Matrix&& source) : Array(std::move(source), IPosition(2, 0)) { assert(ok()); } // // ArrayNDimError // template Matrix::Matrix(const Array& source) : Array(source) { this->checkMatrixShape(); assert(ok()); } template Matrix::Matrix(Array&& source) : Array(std::move(source)) { this->checkMatrixShape(); assert(ok()); } template Matrix::Matrix(const IPosition &shape, T *storage, StorageInitPolicy policy) : Array(shape, storage, policy) { Array::checkBeforeResize(shape); } template Matrix::Matrix(const IPosition &shape, const T *storage) : Array(shape, storage) { Array::checkBeforeResize(shape); } template void Matrix::resize(size_t nx, size_t ny, bool copyValues) { Array::resize(IPosition{ssize_t(nx), ssize_t(ny)}, copyValues); assert(ok()); } // // ArrayError // template Matrix Matrix::operator()(const Slice &sliceX, const Slice &sliceY) { assert(ok()); long long b1, l1, s1, b2, l2, s2; // begin length step if (sliceX.all()) { b1 = 0; l1 = this->length_p(0); s1 = 1; } else { b1 = sliceX.start(); l1 = sliceX.length(); s1 = sliceX.inc(); } if (sliceY.all()) { b2 = 0; l2 = this->length_p(1); s2 = 1; } else { b2 = sliceY.start(); l2 = sliceY.length(); s2 = sliceY.inc(); } // Check that the selected slice is valid if (s1 < 1 || s2<1) { throw(ArrayError("Matrix::operator()(Slice,Slice) : step < 1")); } else if (l1 < 0 || l2 < 0) { throw(ArrayError("Matrix::operator()(Slice,Slice) : length < 0")); } else if ((b1+(l1-1)*s1 >= this->length_p(0)) || (b2+(l2-1)*s2 >= this->length_p(1))) { throw(ArrayError("Matrix::operator()(Slice,Slice): desired slice" " extends beyond the end of the array")); } else if (b1 < 0 || b2 < 0) { throw(ArrayError("Matrix::operator()(Slice,Slice) : start of slice " "before beginning of matrix")); } // For simplicity, just use the Array slicing. If this is found to be // a performance drag, we could special case this as we do for Vector. IPosition blc(2,b1,b2); IPosition trc(2,b1+(l1-1)*s1,b2+(l2-1)*s2); IPosition incr(2,s1,s2); return this->operator()(blc,trc,incr); } template const Matrix Matrix::operator() (const Slice &sliceX, const Slice &sliceY) const { return const_cast*>(this)->operator() (sliceX, sliceY); } // // ArrayConformanceError // template Vector Matrix::row(size_t n) { assert(ok()); if ((long long)(n) >= this->length_p(0)) { throw(ArrayConformanceError("Matrix::row - row < 0 or > end")); } Matrix tmp((*this)(n, Slice())); // A reference tmp.ndimen_p = 1; tmp.length_p(0) = tmp.length_p(1); tmp.inc_p(0) = this->steps_p(1); // "Lie" about the original length so that ok() doesn't spuriously fail // the test length[i] < originalLength (basically we've "swapped" axes). tmp.originalLength_p(0) = tmp.originalLength_p(1); tmp.length_p.resize (1); tmp.inc_p.resize (1); tmp.originalLength_p.resize (1); tmp.nels_p = tmp.length_p(0); tmp.contiguous_p = tmp.isStorageContiguous(); tmp.makeSteps(); return tmp; // should match Vector(const Array &) } // // ArrayConformanceError // template Vector Matrix::column(size_t n) { assert(ok()); if ((long long)(n) >= this->length_p(1)) { throw(ArrayConformanceError("Matrix::column - column < 0 or > end")); } Matrix tmp((*this)(Slice(), n)); // A reference tmp.ndimen_p = 1; tmp.length_p.resize (1); tmp.inc_p.resize (1); tmp.originalLength_p.resize (1); tmp.nels_p = tmp.length_p(0); tmp.contiguous_p = tmp.isStorageContiguous(); tmp.makeSteps(); return tmp; // should match Vector(const Array &) } // // ArrayConformanceError // template Vector Matrix::diagonal(long long n) { assert(ok()); Matrix tmp(*this); tmp.begin_p += tmp.makeDiagonal (0, n); tmp.makeSteps(); return tmp; // should match Vector(const Array &) } template const Vector Matrix::row(size_t n) const { assert(ok()); // Cast away constness of this so we do not have to duplicate code. // Because the return type is const we are not actually violating // constness. Matrix *This = const_cast*>(this); return This->row(n); } template const Vector Matrix::column(size_t n) const { assert(ok()); // Cast away constness of this so we do not have to duplicate code. // Because the return type is const we are not actually violating // constness. Matrix *This = const_cast*>(this); return This->column(n); } // If the matrix isn't square, this will throw an exception. template const Vector Matrix::diagonal(long long n) const { assert(ok()); // Cast away constness of this so we do not have to duplicate code. // Because the return type is const we are not actually violating // constness. Matrix *This = const_cast*>(this); return This->diagonal(n); } template Matrix Matrix::identity(size_t n) { Matrix m(n, n, T(0)); T* ptr = m.data(); for (size_t i=0; i void Matrix::doNonDegenerate (const Array &other, const IPosition &ignoreAxes) { Array tmp(*this); tmp.nonDegenerate (other, ignoreAxes); if (tmp.ndim() != 2) { throw (ArrayError ("Matrix::nonDegenerate (other, ignoreAxes) - " "removing degenerate axes from other " "does not result in matrix")); } this->reference (tmp); } template bool Matrix::ok() const { return ( (this->ndim() == 2) ? (Array::ok()) : false ); } template void Matrix::preTakeStorage(const IPosition &shape) { Array::preTakeStorage(shape); if(shape.nelements() != 2) throw ArrayError("shape.nelements() != 2"); } } //#End casa namespace #endif casacore-3.7.1/casa/Arrays/Matrix2Math.cc000066400000000000000000000220571476623553700201510ustar00rootroot00000000000000//# Matrix2Math.cc: The Casacore linear algebra functions (non-templated) //# Copyright (C) 1993,1994,1995,1996,1999,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include "Vector.h" #include "Matrix.h" #include "MatrixMath.h" #include "ArrayLogical.h" #include "ArrayMath.h" #include "ArrayError.h" #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Matrix Rot3D(int axis, double angle) { if (axis<0 || axis>2) throw (ArrayError("Rot3D(axis, angle): axis has to be 0 (x)," " 1 (y) or 2 (z).")); Matrix Rot(3,3); Rot=0; double cosa=std::cos(angle); double sina=std::sin(angle); Rot(axis,axis)=1; Rot((axis+1)%3,(axis+1)%3)=cosa; Rot((axis+2)%3,(axis+1)%3)=sina; Rot((axis+1)%3,(axis+2)%3)=-sina; Rot((axis+2)%3,(axis+2)%3)=cosa; return Rot; } Matrix Rot3D(int axis, float angle) { Matrix tmp = Rot3D(axis, double(angle)); Matrix retval(tmp.shape()); convertArray(retval, tmp); return retval; } // In order to create a specific std::complex and Dcomplex function (with the // little nuances associated with complex space) this file carries some // non-template copies of the templated Matrix math stuff. // the vector dot/scalar/inner product std::complex innerProduct (const Vector> &A, const Vector> &B) { // check for correct dimensions if (!(A.conform(B))) throw (ArrayConformanceError("innerProduct - conform() error.")); std::complex scalar = 0; for (size_t i = 0; i < A.nelements(); i++) scalar += A(i)*conj(B(i)); return scalar; } Matrix> conjugate(const Matrix> &A) { return conj(A); } Matrix> adjoint(const Matrix> &A) { return transpose(Matrix>(conj(A))); } // ----------------std::complex subroutines--------------------------- // the vector dot/scalar/inner product std::complex innerProduct (const Vector> &A, const Vector> &B) { // check for correct dimensions if (!(A.conform(B))) throw (ArrayConformanceError("innerProduct - conform() error.")); std::complex scalar = 0; for (size_t i = 0; i < A.nelements(); i++) scalar += A(i)*conj(B(i)); return scalar; } Matrix> conjugate(const Matrix> &A) { return conj(A); } Matrix> adjoint (const Matrix> &A) { return transpose(Matrix>(conj(A))); } // int Vector magnitude/norm int norm (const Vector &A) { return int(std::sqrt(double(innerProduct(A,A)))); } // float Vector magnitude/norm float norm (const Vector &A) { return std::sqrt(innerProduct(A,A)); } // double Vector magnitude/norm double norm (const Vector &A) { return std::sqrt(innerProduct(A,A)); } // std::complex vector magnitude/norm float norm (const Vector> &A) { return std::sqrt(real(innerProduct(A,A))); } // std::complex vector magnitude/norm double norm (const Vector> &A) { return std::sqrt(real(innerProduct(A,A))); } // The infinity norm of a matrix int normI (const Matrix &A) { int output = 0; if( A.nelements()!=0) { int hold = 0; size_t M = A.nrow(); for (size_t I = 0; I < M; I++) { hold = sum(abs(A.row(I))); output = (output >= hold) ? output : hold; } } return output; } // The infinity norm of a matrix float normI (const Matrix &A) { float output = 0; if( A.nelements()!=0) { float hold = 0; size_t M = A.nrow(); for (size_t I = 0; I < M; I++) { hold = sum(abs(A.row(I))); output = (output >= hold) ? output : hold; } } return output; } // The infinity norm of a matrix double normI (const Matrix &A) { double output = 0; if( A.nelements()!=0) { double hold = 0; size_t M = A.nrow(); for (size_t I = 0; I < M; I++) { hold = sum(abs(A.row(I))); output = (output >= hold) ? output : hold; } } return output; } // the infinite norm of a matrix float normI (const Matrix> &A) { float output = 0; if( A.nelements()!=0) { float hold = 0; size_t M = A.nrow(); for (size_t I = 0; I < M; I++) { hold = sum(amplitude(A.row(I))); output = (output >= hold) ? output : hold; } } return output; } // the infinite norm of a matrix double normI (const Matrix> &A) { double output = 0; if( A.nelements()!=0) { double hold = 0; size_t M = A.nrow(); for (size_t I = 0; I < M; I++) { hold = sum(amplitude(A.row(I))); output = (output >= hold) ? output : hold; } } return output; } // The one norm of a matrix int norm1 (const Matrix &A) { int output = 0; if (A.nelements()!=0) { int hold = 0; size_t N = A.ncolumn(); for (size_t I = 0; I < N; I++) { hold = sum(abs(A.column(I))); output = (output >= hold) ? output : hold; } } return output; } // The one norm of a matrix float norm1 (const Matrix &A) { float output = 0; if (A.nelements()!=0) { float hold = 0; size_t N = A.ncolumn(); for (size_t I = 0; I < N; I++) { hold = sum(abs(A.column(I))); output = (output >= hold) ? output : hold; } } return output; } // The one norm of a matrix double norm1 (const Matrix &A) { double output = 0; if (A.nelements()!=0) { double hold = 0; size_t N = A.ncolumn(); for (size_t I = 0; I < N; I++) { hold = sum(abs(A.column(I))); output = (output >= hold) ? output : hold; } } return output; } // The one norm of a matrix float norm1 (const Matrix> &A) { float output = 0; if( A.nelements()!=0) { float hold = 0; size_t N = A.ncolumn(); for (size_t I = 0; I < N; I++) { hold = sum(amplitude(A.column(I))); output = (output >= hold) ? output : hold; } } return output; } // The one norm of a matrix double norm1 (const Matrix> &A) { double output = 0; if( A.nelements()!=0) { double hold = 0; size_t N = A.ncolumn(); for (size_t I = 0; I < N; I++) { hold = sum(amplitude(A.column(I))); output = (output >= hold) ? output : hold; } } return output; } Matrix rproduct (const Matrix> &A, const Matrix> &B) { if (A.ncolumn() != B.nrow()) throw (ArrayError("product - multiplication of" " these matrices shapes is undefined")); Matrix result(A.nrow(), B.ncolumn()); for (size_t i = 0; i < A.nrow(); i++) for (size_t j = 0; j < B.ncolumn(); j++) { result(i,j) = 0.0; for (size_t k = 0; k < A.ncolumn(); k++) result(i,j) += real(A(i, k) * B(k, j)); } return result; } Vector rproduct(const Matrix> &A, const Vector> &x) { if (A.ncolumn() != x.nelements()) throw (ArrayError("product - multiplication of" " these matrices shapes is undefined")); Vector result(A.nrow()); for (size_t i = 0; i < A.nrow(); i++) { result(i) = 0.0; for (size_t k = 0; k < A.ncolumn(); k++) result(i) += real(A(i, k) * x(k)); } return result; } Vector> product(const Matrix> &A, const Vector &x) { if (A.ncolumn() != x.nelements()) throw (ArrayError("product - multiplication of" " these matrices shapes is undefined")); Vector> result(A.nrow()); for (size_t i = 0; i < A.nrow(); i++) { result(i) = std::complex(0); for (size_t k = 0; k < A.ncolumn(); k++) result(i) += A(i, k) * x(k); } return result; } Matrix adjoint (const Matrix &A){ return transpose(A);} Matrix adjoint (const Matrix &A){ return transpose(A);} Matrix adjoint (const Matrix &A){ return transpose(A);} } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Arrays/MatrixIter.h000066400000000000000000000112131476623553700177330ustar00rootroot00000000000000//# MatrixIter.h: Iterate a matrix cursor through another array //# Copyright (C) 1993,1994,1995,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MATRIXITER_2_H #define CASA_MATRIXITER_2_H #include "ArrayIter.h" #include "Matrix.h" namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Iterate a Matrix cursor through another Array. // // // // MatrixIterator steps a Matrix (the "cursor") through an array. // The cursor "refers" to storage in the array, so that changing the // values in the cursor changes values in the original array. // // This class is derived from ArrayIterator; basically it only adds the // matrix() member function which allows you to access the cursor as a Matrix. // // // The origin of the cursor, i.e. the subarray that moves through the // larger array, is always zero. // // // In this example we want to make a "moment" map of a cube, i.e. collapse // the "Z" axis by averaging it. // // Cube cube; // MatrixIterator planeIter(cube); // Matrix average(planeIter.matrix().copy()); // init with first plane // planeIter.next(); // advance the iterator // while (! planeIter.pastEnd()) { // average += planeIter.matrix(); // Sum the next plane // planeIter.next(); // } // average /= float(cube.shape()(2)); // divide by the number of planes // template class MatrixIterator : public ArrayIterator { public: // Iterate by matrices through array "a". // The first 2 axes form the cursor axes. explicit MatrixIterator(Array &a); // Iterate by matrices through array "a". // The given axes form the cursor axes. MatrixIterator(Array &a, size_t cursorAxis1, size_t cursorAxis2); // Return the matrix at the current position. Matrix &matrix() {return *(Matrix *)(this->ap_p.get());} private: // Not implemented. MatrixIterator(const MatrixIterator &) = delete; // Not implemented. MatrixIterator &operator=(const MatrixIterator &) = delete; }; // // Iterate a Matrix cursor through a R/O Array. // // // // ReadOnlyMatrixIterator behaves exactly like MatrixIterator (cf.) only // it should be used on const Arrays. // // Note that the R/O MatrixIterator is not derived from R/O // ArrayIterator. // // template class ReadOnlyMatrixIterator { public: // ReadOnlyMatrixIterator(const Array &a) : mi(const_cast&>(a)) {} ReadOnlyMatrixIterator(const Array &a, size_t cursorAxis1, size_t cursorAxis2) : mi(const_cast&>(a), cursorAxis1, cursorAxis2) {} void next() {mi.next();} void reset() {mi.origin();} void origin() {mi.origin();} const Array &array() {return mi.array();} const Matrix &matrix() {return mi.matrix();} bool atStart() const {return mi.atStart();} bool pastEnd() const {return mi.pastEnd();} const IPosition &pos() const {return mi.pos();} IPosition endPos() const {return mi.endPos();} size_t ndim() const {return mi.ndim();} // private: // Not implemented. ReadOnlyMatrixIterator(const ReadOnlyMatrixIterator &) = delete; // Not implemented. ReadOnlyMatrixIterator &operator=(const ReadOnlyMatrixIterator &) = delete; MatrixIterator mi; }; } //# NAMESPACE CASACORE - END #include "MatrixIter.tcc" #endif casacore-3.7.1/casa/Arrays/MatrixIter.tcc000066400000000000000000000036441476623553700202660ustar00rootroot00000000000000//# MatrixIter.cc: Iterate a matrix cursor through another array //# Copyright (C) 1993,1994,1995 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MATRIXITER_2_TCC #define CASA_MATRIXITER_2_TCC #include "MatrixIter.h" namespace casacore { //# NAMESPACE CASACORE - BEGIN template MatrixIterator::MatrixIterator(Array &a) : ArrayIterator(a, 2) { // We need to ensure that ap points at a Matrix this->ap_p.reset( new Matrix(*this->ap_p) ); // reference } template MatrixIterator::MatrixIterator(Array &a, size_t cursorAxis1, size_t cursorAxis2) : ArrayIterator(a, IPosition(2, cursorAxis1, cursorAxis2), true) { // We need to ensure that ap points at a Matrix this->ap_p.reset( new Matrix(*this->ap_p) ); // reference } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Arrays/MatrixMath.h000066400000000000000000000141051476623553700177240ustar00rootroot00000000000000//# MatrixMath.h: The Casacore linear algebra functions //# Copyright (C) 1994,1995,1996,1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MATRIXMATH_2_H #define CASA_MATRIXMATH_2_H #include "Vector.h" #include "Matrix.h" namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Linear algebra functions on Vectors and Matrices. // // // // // // // Linear Algebra -- Linear algebra functions // on Vectors and Matrices. // // // // // The scalar/dot/inner product of two equal length vectors. // // template T innerProduct (const Vector &x, const Vector &y); std::complex innerProduct (const Vector> &x, const Vector> &y); std::complex innerProduct (const Vector> &x, const Vector> &y); // // // The magnitude/norm of a vector. // int norm (const Vector &x); float norm (const Vector &x); double norm (const Vector &x); float norm (const Vector> &x); double norm (const Vector> &x); // // // The vector/cross product of two 3-space vectors. // template Vector crossProduct (const Vector &x, const Vector &y); // Magnitude of cross product of two 2-space vectors, x[0]*y[1] - x[1]*y[0]. template T crossProduct2D(const Vector &x, const Vector &y); // // The matrix/outer product of a vector and a transposed vector. // The function's second argument is actually a transposed vector // stored as the only row in a 1xN matrix. // template Matrix product (const Vector &x, const Matrix &yT); // // The vector/outer product of an MxN matrix and an N-length vector. // template Vector product (const Matrix &A, const Vector &x); // // The direct product of two vectors. // The resulting vector contains for every element of x, the product of // that element and Vector y. Thus the length of the output vector is // the product of the input lengths. // template Vector directProduct(const Vector& x, const Vector& y); // // The matrix multiplication or cayley product of an MxN matrix and // an NxP matrix. // template Matrix product (const Matrix &A, const Matrix &B); // // The infinity norm (or maximum value of the sum of the absolute values // of the rows members of a matrix) // int normI(const Matrix &A); float normI(const Matrix &A); double normI(const Matrix &A); float normI(const Matrix> &A); double normI(const Matrix> &A); // // // The one norm (or maximum value of the sum of the absolute values // of the column members of a matrix) // int norm1(const Matrix &A); float norm1(const Matrix &A); double norm1(const Matrix &A); float norm1(const Matrix> &A); double norm1(const Matrix> &A); // // // The NxM transpose of an MxN matrix. // template Matrix transpose (const Matrix &A); // Create a 3D rotation matrix (3x3). // Axis is 0,1,2 for x,y,z; angle is in radians. // template Matrix Rot3D(int axis, T angle); Matrix Rot3D(int axis, double angle); Matrix Rot3D(int axis, float angle); // // // The direct product of two matrices. // The resulting matrix contains for every element of A, the product of // that element and Matrix B. Thus the shape of the output matrix is // the (element by element) product of the input shapes. // template Matrix directProduct(const Matrix& A, const Matrix& B); // // The conjugate/transpose or adjoint of the complex matrix A. // Matrix> adjoint (const Matrix> &A); Matrix> adjoint (const Matrix> &A); // define the adjoint operator as a plain old transpose when the Matrix is // not complex valued. (for templating purposes) Matrix adjoint (const Matrix &A); Matrix adjoint (const Matrix &A); Matrix adjoint (const Matrix &A); // // The product of a std::complex Matrix and a Real Vector // Vector> product(const Matrix>&, const Vector&); // // The real part of a product of a std::complex Matrix and a std::complex Vector // Vector rproduct(const Matrix>&, const Vector>&); // // The real part of a product of a std::complex Matrix and a std::complex Matrix // Matrix rproduct (const Matrix>&, const Matrix>&); // } //# NAMESPACE CASACORE - END #include "MatrixMath.tcc" #endif casacore-3.7.1/casa/Arrays/MatrixMath.tcc000066400000000000000000000130761476623553700202540ustar00rootroot00000000000000//# MatrixMath.tcc: The Casacore linear algebra functions //# Copyright (C) 1994,1995,1996,1998,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MATRIXMATH_2_TCC #define CASA_MATRIXMATH_2_TCC #include "MatrixMath.h" #include "Vector.h" #include "Matrix.h" #include "ArrayError.h" namespace casacore { //# NAMESPACE CASACORE - BEGIN // the vector dot/scalar/inner product template T innerProduct (const Vector &A, const Vector &B) { // check for correct dimensions if (A.conform(B) == false){ throw(ArrayConformanceError("innerProduct - conform() error.")); } T scalar = 0; for (size_t i=0; i < A.nelements(); i++) scalar += A(i)*B(i); return scalar; } // Uncomment this if we ever want a templated Rot3D again. // template Matrix Rot3D(int axis, T angle) { // if (axis<0 || axis>2) // throw (ArrayError("Rot3D(axis, angle): axis has to be 0 (x)," // " 1 (y) or 2 (z).")); // // Matrix Rot(3,3); // Rot=0; // T cosa=cos(angle); // T sina=sin(angle); // // Rot(axis,axis)=1; // Rot((axis+1)%3,(axis+1)%3)=cosa; // Rot((axis+2)%3,(axis+1)%3)=sina; // Rot((axis+1)%3,(axis+2)%3)=-sina; // Rot((axis+2)%3,(axis+2)%3)=cosa; // return Rot; // } // the 3-space cross/vector product template Vector crossProduct (const Vector &A, const Vector &B) { // check for correct dimensions if (!A.conform(B)){ throw (ArrayConformanceError("crossProduct - conform() error.")); } else { if (A.nelements() != 3) throw (ArrayConformanceError("crossProduct - Vector not in 3-space")); } Vector result(3); result(0) = A(1)*B(2) - A(2)*B(1); result(1) = A(2)*B(0) - A(0)*B(2); result(2) = A(0)*B(1) - A(1)*B(0); return result; } template T crossProduct2D (const Vector &A, const Vector &B) { // check for correct dimensions if (!A.conform(B)){ throw (ArrayConformanceError("crossProduct2D - conform() error.")); } else { if (A.nelements() != 2) throw (ArrayConformanceError("crossProduct2D - Vector not in 2-space")); } return A[0]* B[1] - A[1]*B[0]; } // matrix multiplication or cayley product template Vector product (const Matrix &A, const Vector &x) { if (A.ncolumn() != x.nelements()) throw (ArrayError("product - multiplication of" " these matrices shapes is undefined")); Vector result(A.nrow()); for (size_t i = 0; i < A.nrow(); i++) { result(i) = T(0); for (size_t k = 0; k < A.ncolumn(); k++) result(i) += A(i,k) * x(k); } return result; } template Vector directProduct(const Vector& x, const Vector& y) { size_t nx=x.nelements(), ny=y.nelements(); Vector res(nx*ny); for (size_t i=0; i Matrix product (const Vector &x, const Matrix &yT) { if (yT.nrow()!= 1) throw (ArrayError("product - multiplication of" " these matrices shapes is undefined")); Matrix A(x.nelements(),1u); A.column(0).assign_conforming( x ); return product(A,yT); } // matrix multiplication or cayley product template Matrix product (const Matrix &A, const Matrix &B) { if (A.ncolumn() != B.nrow()) throw (ArrayError("product - multiplication of" " these matrices shapes is undefined")); Matrix result(A.nrow(), B.ncolumn()); for (size_t i = 0; i < A.nrow(); i++) for (size_t j = 0; j < B.ncolumn(); j++) { result(i,j) = T(0); for (size_t k = 0; k < A.ncolumn(); k++) result(i,j) += A(i,k) * B(k,j); } return result; } template Matrix transpose (const Matrix &A) { Matrix aT(A.ncolumn(), A.nrow()); for (size_t i=0; i Matrix directProduct(const Matrix &A, const Matrix &B) { int ncB = B.ncolumn(), nrB = B.nrow(); Matrix dpAB(A.ncolumn()*B.ncolumn(),A.nrow()*B.nrow()); for (size_t i=0; i; template class Matrix; template class Matrix; } casacore-3.7.1/casa/Arrays/Memory.h000066400000000000000000000013561476623553700171220ustar00rootroot00000000000000#ifndef CASACORE_COPY_2_H #define CASACORE_COPY_2_H #include namespace casacore { //#Begin casa namespace template void copy_n_with_stride(const T* from, std::size_t n, T* to, std::size_t toStride, std::size_t fromStride) { while (n--) { *to = *from; to += toStride; from += fromStride; } } template void move_n_with_stride(T* from, std::size_t n, T* to, std::size_t toStride, std::size_t fromStride) { while (n--) { *to = std::move(*from); to += toStride; from += fromStride; } } template void fill_n_with_stride(T* dest, size_t n, const T& value, size_t stride) { while (n--) { *dest = value; dest += stride; }; } } // namespaces #endif casacore-3.7.1/casa/Arrays/Slice.cc000066400000000000000000000053411476623553700170450ustar00rootroot00000000000000//# Slice.c: Define a (start,length,increment) along an axis //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include "Slice.h" #include "Slicer.h" #include "Vector.h" namespace casacore { //# NAMESPACE CASACORE - BEGIN IPosition Slice::checkSlices (Vector >& slices, Slicer& first, const IPosition& shape) { // In principle the shape defines the dimensionality, but it can be empty. // In that case use the slices definition. size_t ndim = shape.size(); if (ndim == 0) { ndim = slices.size(); } if (slices.size() > ndim) { throw ArrayError ("Slice::checkSlices - " "slices size exceeds dimensionality"); } // Extend slices if needed. slices.resize (ndim, true); // Initialize the result shape and first Slicer start,end,incr. IPosition result(ndim, 0); IPosition start(ndim); IPosition length(ndim); IPosition incr(ndim); for (size_t i=0; i& sliceVec = slices[i]; if (sliceVec.size() == 0) { sliceVec.resize (1); sliceVec[0] = Slice(0, shape[i]); } // Get total slices length if slices are given. // Check if slice does not exceed shape. for (size_t j=0; j= size_t(shape[i])) { throw ArrayError("Slice::checkSlices - " "slice exceeds array shape"); } result[i] += sliceVec[j].length(); } start[i] = sliceVec[0].start(); length[i] = sliceVec[0].length(); incr[i] = sliceVec[0].inc(); } first = Slicer(start, length, incr); return result; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Arrays/Slice.h000066400000000000000000000153301476623553700167060ustar00rootroot00000000000000//# Slice.h: Define a (start,length,increment) along an axis //# Copyright (C) 1993,1994,1995,1997 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_SLICE_2_H #define CASA_SLICE_2_H #include #include //# for ssize_t #include "ArrayFwd.h" namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations. class Slicer; class IPosition; // define a (start,length,increment) along an axis // // // // // A "slice" (aka Section) is a a regular sub-Array (and ultimately sub-Image) // that is defined by defining a (start,length,increment) for each axis in // the array. That is, the output array's axis is of size "length", and the // elements are sampled by stepping along the input array in strides of // "increment". // // The "length" is the length of the OUTPUT array, the output length // is NOT divided by the increment/stride. // // If increment is not defined, then it defaults to one. // (Increment, if defined, must be >= 1). If length // is not defined, then it defaults to a length of one also (i.e. just the pixel // "start"). If start is also undefined, then all pixels along this axis are // chosen. This class deprecates the "_" (IndexRange) class, which had a failed // syntax and used (start,end,increment) which is generally less convenient. // Some simple examples follow: // // Vector vi(100); // Vector of length 100; // //... // // Copy odd values onto even values // vi(Slice(0,50,2)) = vi(Slice(1,50,2)); // // Matrix mf(100,50), smallMf; // smallMf.reference(mf(Slice(0,10,10), Slice(0,5,10))); // // smallMF is now a "dezoomed" (every 10th pix) // // refference to mf. Of course we could also // // make it a copy by using assignment; e.g: // // smallMf.resize(0,0); // Make it so it will "size to fit" // smallMf = mf(Slice(0,10,10), Slice(0,5,10)); // // As shown above, normally Slices will normally be used as temporaries, // but they may also be put into variables if desired (the default // copy constructors and assignment operators suffice for this class). // // While it will be unusual for a user to want this, a zero-length slice // is allowable. // // Another way to produce a slice from any of the Array classes is to use // SomeArray(blc,trc,inc) where blc,trc,inc are IPositions. This is described // in the documentation for Array. // class Slice { public: // The entire range of indices on the axis is desired. Slice(); // Create a Slice with a given start, length, and increment. The latter // two default to one if not given. Slice(size_t Start, size_t Length=1, size_t Inc=1); // Create a Slice with a given start, end or length, and increment. // If endIsLength=false, end is interpreted as length. Slice(size_t Start, size_t End, size_t Inc, bool endIsLength); // Was the entire range of indices on this axis selected? bool all() const; // Report the selected starting position. If all() is true, // start=len=inc=0 is set. size_t start() const; // Report the defined length. If all() is true, start=len=inc=0 is set. size_t length() const; // Report the defined increment. If all() is true, start=len=inc=0 is set. size_t inc() const; // Attempt to report the last element of the slice. If all() is // true, end() returns -1 (which is less than start(), which returns // zero in that case). size_t end() const; // Check a vector of slices. // If a vector of an axis is empty or missing, it is replaced by a Slice // representing the entire axis. // It checks if the Slices do not exceed the array shape. // It returns the shape of the combined slices and fills the Slicer // for the first array part defined by the slices. static IPosition checkSlices (Vector >& slices, Slicer& first, const IPosition& shape); private: //# Inc of <0 is used as a private flag to mean that the whole axis is //# selected. Users are given a size_t in their interface, so they cannot //# set it to this. Chose Inc rather than length since it's more likely //# that we'd need all bits of length than of inc. The "p" in the names //# stands for private to avoid it colliding with the accessor names. //# incp < 0 is chosen as the flag since the user can set inc to be zero //# although that is an error that can be caught if AIPS_DEBUG is defined). size_t startp; ssize_t incp; size_t lengthp; }; inline Slice::Slice() : startp(0), incp(-1), lengthp(0) { // Nothing } inline Slice::Slice(size_t Start, size_t Length, size_t Inc) : startp(Start), incp(Inc), lengthp(Length) { #if defined(AIPS_DEBUG) assert(incp > 0); #endif } inline Slice::Slice(size_t Start, size_t End, size_t Inc, bool endIsLength) : startp(Start), incp(Inc), lengthp(endIsLength ? End : 1+(End-Start)/Inc) { #if defined(AIPS_DEBUG) if (! endIsLength) { assert(End >= Start); } assert(incp > 0); #endif } inline bool Slice::all() const { return incp<0; } inline size_t Slice::start() const { return startp; } inline size_t Slice::length() const { return lengthp; } inline size_t Slice::inc() const { if (all()) { return 0; } else { return incp; } } inline size_t Slice::end() const { // return -1 if all() return startp + (lengthp-1)*incp; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Arrays/Slicer.cc000066400000000000000000000164701476623553700172340ustar00rootroot00000000000000//# Slicer.cc: Class to specify which elements to extract from an n-dimensional array //# Copyright (C) 1994,1995,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include "Slicer.h" #include "Slice.h" #include "ArrayError.h" #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Slicer::Slicer() : asEnd_p (endIsLength), start_p (1,MimicSource), end_p (1,MimicSource), stride_p(1,1), len_p (1,MimicSource), fixed_p (false) {} Slicer::Slicer (const IPosition& bl, const IPosition& tr, const IPosition& in, LengthOrLast lol) : asEnd_p (lol), start_p (bl), end_p (tr), stride_p(in), len_p (tr) { fillEndLen(); } Slicer::Slicer (const IPosition& bl, const IPosition& tr, LengthOrLast lol) : asEnd_p (lol), start_p (bl), end_p (tr), stride_p(bl.nelements(), 1), len_p (tr) { fillEndLen(); } Slicer::Slicer (const IPosition& bl) : asEnd_p (endIsLength), start_p (bl), end_p (bl), stride_p(bl.nelements(), 1), len_p (bl.nelements(), 1) { fillFixed(); } Slicer::Slicer (const Slice& x, const Slice& y, const Slice& z, LengthOrLast lol) : asEnd_p (lol), start_p (3, MimicSource), end_p (3, MimicSource), stride_p(3, 1), len_p (3, MimicSource) { fillSlice (x, start_p(0), len_p(0), stride_p(0)); fillSlice (y, start_p(1), len_p(1), stride_p(1)); fillSlice (z, start_p(2), len_p(2), stride_p(2)); fillEndLen(); } Slicer::Slicer (const Slice& x, const Slice& y, LengthOrLast lol) : asEnd_p (lol), start_p (2, MimicSource), end_p (2, MimicSource), stride_p(2, 1), len_p (2, MimicSource) { fillSlice (x, start_p(0), len_p(0), stride_p(0)); fillSlice (y, start_p(1), len_p(1), stride_p(1)); fillEndLen(); } Slicer::Slicer (const Slice& x, LengthOrLast lol) : asEnd_p (lol), start_p (1, MimicSource), end_p (1, MimicSource), stride_p(1, 1), len_p (1, MimicSource) { fillSlice (x, start_p(0), len_p(0), stride_p(0)); fillEndLen(); } bool Slicer::operator==(const Slicer& that) const { return this->len_p.isEqual(that.len_p) && this->start_p.isEqual(that.start_p) && this->end_p.isEqual(that.end_p) && this->stride_p.isEqual(that.stride_p) && this->asEnd_p==that.asEnd_p && this->fixed_p==that.fixed_p; } void Slicer::fillEndLen() { //# First check if all positions have same length. if (start_p.nelements() != end_p.nelements() || start_p.nelements() != stride_p.nelements()) { throw (ArraySlicerError ("IPosition-lengths differ")); } //# Reversed strides are not allowed yet (thus stride>0). for (size_t i=0; i= 0 && end_p(i) >= 0) { len_p(i) = end_p(i) - start_p(i); // input-length if (len_p(i) < 0) { throw (ArraySlicerError ("end= 0. //# Fill in end if start and length are given. end_p(i) = MimicSource; if (len_p(i) != MimicSource) { if (len_p(i) < 0) { throw (ArraySlicerError ("length<0")); } end_p(i) = start_p(i) + (len_p(i) - 1) * stride_p(i); } } } } //# Set the fixed_p flag. fillFixed(); } void Slicer::fillFixed() { fixed_p = true; for (size_t i=0; i= shp(i)) { throw (ArraySlicerError ("infer: startResult>=shape")); } //# Fill end value. //# If given as end, unspecified is end of axis. //# If given as length, unspecified is also end of axis. if (asEnd_p == endIsLast) { if (end_p(i) != MimicSource) { end(i) = end_p(i); if (end(i) < 0) end(i) += shp(i); } }else{ if (len_p(i) != MimicSource) { end(i) = start(i) + len_p(i) * stride_p(i) - 1; } } //# Get resulting shape and adjust and check end value. //# Length 0 is handled correctly. if (end(i) < start(i)) { if (end(i) < start(i) - 1) { throw (ArraySlicerError ("infer: endResult= shp(i)) { throw (ArraySlicerError ("infer: endResult>=shape")); } } return res; } std::ostream &operator << (std::ostream &stream, const Slicer &slicer) { stream << slicer.start () << " to " << slicer.end () << " with stride " << slicer.stride () << ", length " << slicer.length (); return stream; } std::string to_string(const Slicer& slicer) { std::ostringstream str; str << slicer; return str.str(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Arrays/Slicer.h000066400000000000000000000445471476623553700171040ustar00rootroot00000000000000//# Slicer.h: specify which elements to extract from an n-dimensional array //# Copyright (C) 1994,1995,1997,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_SLICER_2_H #define CASA_SLICER_2_H //# Includes #include "IPosition.h" namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class Slice; // // Specify which elements to extract from an n-dimensional array // // // The review and modification of this class were undertaken, in part, // with the aim of making this class header an example -- this is what // the Casacore project thinks a class header should look like. // // // You should have at least a preliminary understanding of these classes: //
      • IPosition //
      • Array //
      • Slice // // // The class name "Slicer" may be thought of as a short form // of "n-Dimensional Slice Specifier." Some confusion is possible // between class "Slice" and this class. // // // // If you need to extract or operate upon a portion of an array, // the Slicer class is the best way to specify the subarray you are // interested in. // // Slicer has many constructors. Of these, some require that the // programmer supply a full specification of the array elements he // wants to extract; other constructors make do with partial information. // In the latter case, the constructor will assume sensible default values or, // when directed, infer missing information from the array that's getting // sliced (hereafter, the "source" array). // //

        Constructing With Full Information

        // // To fully specify a subarray, you must supply three pieces of information // for each axis of the subarray: // //
          //
        1. where to start //
        2. how many elements to extract //
        3. what stride (or "increment" or "interval") to use: a stride of // "n" means pick extract only every "nth" element along an axis //
        // // The most basic constructor for Slicer illustrates this. To create // an Slicer for getting selected elements from a 3D array: // // // IPosition start (3,0,0,0), length (3,10,10,10), stride (3,3,3,3); // Slicer slicer (start, length, stride); // // assume proper declarations, and meaningful values in the source array // subArray = sourceArray (slicer); // // It gets elements 0,3,6,9,12,15,18,21,24,27 for each dimension. // // If you wish to extract elements from the array // at intervals, these intervals must be regular. The interval is one // constant integer for each dimension of the array: it cannot be a function. // // // "length", the second parameter to the Slicer // constructor above, may actually be used in two ways. In normal // (and default) use, it specifies how many elements to select from the // source. In the alternative use, it specifies the index of the last element // to extract from the source array. This ambiguity (does "end" mean // "length" or does it mean "last index"?) is handled by a default // fourth parameter to the constructor. This code fragment will // extract the same subarray as the example above: // // IPosition start (3,0,0,0), end (3,27,27,27), stride (3,3,3,3); // Slicer slicer (start, end, stride, Slicer::endIsLast); // subArray = sourceArray (slicer); // // Note that in this example end(3,28,29,28) gives the same result. // (We use "end" as the name of the formal parameter because it supports // both meanings -- "last index" or "length." You may wish to use a // clarifying name for the actual parameter in your code, as we have // above when we used "length".) // // Similar to Python it is possible to address the start and/or end value // from the end by giving a negative value (-1 means the last value). // However, a length and stride cannot be negative. // Unlike Python the end value is inclusive (as discussed above). // For example, // // Slicer slicer (IPosition(1,-4), IPosition(1,-2), Slicer::endIsLast) // Slicer slicer (IPosition(1,6), IPosition(1,8), Slicer::endIsLast) // // Both Slicers give the same result when used on a Vector with length 10. // //

        Constructing with Partial Information

        // // Some of the constructors don't require complete information: Slicer // either calculates sensible default values or deduces them from the // source array. If you do not specify a "stride" argument, for example, // a value of 1 will be used for all dimensions. If you specify a "start" // but nothing else, a stride of 1, and (perhaps against expectation) // a length of 1 will be used. // // Note that using a negative start or end is also partial information. // The actual array shape is needed to derive the exact start or end value. // // To instruct the Slicer to get otherwise unspecified information // from the source array, you can create an IPosition like "end" // as shown here: // // // IPosition start (3,0,0,0), stride (3,3,3,3); // IPosition end (3,Slicer::MimicSource, Slicer::MimicSource, // Slicer::MimicSource); // Slicer smartSlicer (start, end, stride); // // assume proper declarations... // subArray = sourceArray (smartSlicer) // // // If you are a library programmer, and write a class that can be sliced // by the Slicer class, you need to understand the mechanism for // completing the information which the application programmer, in using // your class, specified incompletely. (If you are an application // programmer, who wants to slice a library class, this explanation will // be only of academic interest.) // // When the source array (the library class you provide) gets the Slicer -- // which typically comes when the source array is asked to return a // reference to a subarray -- the source does a callback to the Slicer // object. The source array passes its own shape as one of the arguments // to the Slicer callback and asks the Slicer to fill in the missing // values from that shape. // // In use, and with an imagined class "MyVector", code would look // like this: // // // first, a fragment from the application program: // IPosition start (1,10), end (1, Slicer::MimicSource); // Slicer slicer (start, end); // MyVector v0 (100); // MyVector v1 = v0 (slicer); // //.... // // second, a fragment from a constructor of the library class "MyVector": // // the MyVector class will construct v1 as a reference to // // selected elements of v0, using (among other things) a // // callback to the slicer it was passed (above, in the // // construction of v1. // // // IPosition start, end, stride; // fullSliceInformation = // slicer.inferShapeFromSource (MyVector::shape(), start, end, stride); // // now the MyVector instance knows everything it needs to // // construct the instance. // // Please note that v1 will have a length of 90, and refer to elements // 10-99 of v0. // // An exception will be thrown if the positions // defined in the Slicer exceed the source array's shape. // //
        // // // Given a large image, 4k on a side, extract (by sampling) an image // 1k on a side, but covering the same region as the original. // // // Image image ("N5364.fits"); // a 4-d VLA map, 4096 x 4096 x 3 x 1 // IPosition start (4,0,0,0,0), stride (4,4,4,1,1); // IPosition end (4, Slicer::MimicSource, Slicer::MimicSource, // Slicer::MimicSource, Slicer::MimicSource); // Slicer smartSlicer (start, end, stride); // // assume proper declarations... // Image subImage = image (smartSlicer); // // // // // Slicer is particularly convenient for designers of other library // classes: Array and Image, for example. (In fact, this convenience // was the original motivation for the class.) The benefit // is this: the application programmer, who needs a slice of an Array, // may provide slicing specifications in many different ways, but the // Array class author needs to provide only one member function to // return the slice. The Slicer class, in effect, and with its // many constructors, provides a way to funnel all of the variety // into a single member function call to the array or image class. // // For example, imagine a 100 x 100 x 100 array from which you want to // extract various subarrays. Here are some of the ways you might // specify the the subarray in the -absence- of Slicer. // // // // preliminaries: create a cube and assign values to all elements -- // // this will be "source" array // Cube bigCube (IPosition (3, 100, 100, 100)); // assignValues (bigCube); // // declare a smaller cube, the destination array. // Cube smallCube (IPosition (3, 10, 10, 10)); // // // example 1: use Slice objects to extract a subcube -- the first // // ten elements along each axis // Slice xIndices (0,10,1), yIndices (0,10,1), zIndices (0,10,1); // smallCube = bigCube (xIndices, yIndices, zIndices); // // // example 2: get the same subcube using three IPosition arguments // IPosition start (3,0,0,0), end (3,10,10,10), stride (3,1,1,1); // smallCube = bigCube (start, end, stride); // // // example 3: use 2 IPositions, letting the 3rd (stride) default to // // IPosition (3,1,1,1) // smallCube = bigCube (start, end); // // // So the Cube class (together with its base class) must define three separate // member functions for the essentially identical operation of // extracting a subcube. The same replication is also required of // Image, Array, and the other Array subclasses (Matrix and Vector). // // The Slicer class collapses all of this into a single member // function per class: // // // Slicer slicer = (call the constructor that best suits your problem) // smallCube = bigCube (slicer); // // // Since there are many constructors available for Slicer, you // can still specify the subarray that you may want in a number of // different ways, by constructing the Slicer in the way most natural // to your circumstances. You then pass the Slicer to the array, and // you will get back the slice you want. // // This class also offers the application programmer considerable // flexibility by allowing the shape of the source array to determine // some of the slice specification. This benefit is explained and // demonstrated above. // // //
      • This class, and the TableArray, Array and Image classes, // could allow for the extraction of a subarray with fewer axes than the // source array. At present, for example, you cannot, directly slice // a matrix from a cube. // class Slicer { public: // Define the "MimicSource" value which defines the open start or end. // This value should be different from MIN_INT in IPosition.h. // It should also not be the lowest possible value, since that // will probably be used as an undefined value. // It must be a negative number. enum {MimicSource= -2147483646}; // Define the possible interpretations of the end-value. enum LengthOrLast { // The end-values given in the constructor define the lengths. endIsLength, // The end-values given in the constructor define the trc. endIsLast }; // Construct a 1-dimensional Slicer. // Start and end are inferred from the source; stride=1. // "endIsLength" and "endIsLast" are identical here, so there's // no need to discriminate between them by using a default parameter. Slicer(); // The member function inferShapeFromSource // (invoked as a callback by the // source array) will use the shape of the source array for the // unspecified values: IPosition elements with the value // Slicer::MimicSource // //
      • ArraySlicerError // // Create a Slicer with a given start, end (or length), and stride. // An exception will be thrown if a negative length or non-positive // stride is given or if the IPositions start, end, and stride // do not have the same dimensionality. // If length or stride is not given, they default to 1. //
        It is possible to leave values in start and end undefined // by giving the value MimicSource. They can be filled // in later with the actual array shape using function // inferShapeFromSource. // Slicer (const IPosition& start, const IPosition& end, const IPosition& stride, LengthOrLast endInterpretation = endIsLength); Slicer (const IPosition& start, const IPosition& end, LengthOrLast endInterpretation = endIsLength); explicit Slicer (const IPosition& start); // // Create a Slicer object from Slice objects. // In a Slice object one defines the start, length, and stride for // one axis. // The default Slice constructor (called with no arguments) creates // a Slice with start and length equal to zero, and an undefined stride. // // Create a Slicer for a 1-dimensional array. Slicer (const Slice& x, LengthOrLast endInterpretation = endIsLength); // Create a Slicer for a 2-dim array. Slicer (const Slice& x, const Slice& y, LengthOrLast endInterpretation = endIsLength); // Create a Slicer for a 3-dim array. Slicer (const Slice& x, const Slice& y, const Slice& z, LengthOrLast endInterpretation = endIsLength); // // Equality bool operator==(const Slicer&) const; // Return the number of dimensions of the Slicer. size_t ndim() const; // This function checks all of the start, length (or end), // and stride IPositions, and fills in missing values by // getting the corresponding values from the shape of the // source array. // These will first be resized, if necessary. // If, for a given axis, (end < start) , it means that a // length of zero was specified. // An exception is thrown if the // start, end, or length exceeds the array shape or if the // dimensionality of the array and Slicer do not conform. // //
      • ArraySlicerError // IPosition inferShapeFromSource (const IPosition& shape, IPosition& startResult, IPosition& endResult, IPosition& strideResult) const; // Report the defined starting position. const IPosition& start() const; // Report the defined ending position. const IPosition& end() const; // Report the defined stride. const IPosition& stride() const; // Report the length of the resulting axes. const IPosition& length() const; // Are all values fixed (i.e., no MimicSource given)? bool isFixed() const; // Set the start and end positions. No explicit checking is done that // the input parameters make sense, so you must be certain if you // call these. These are useful if you have a loop with many iterations // and you do not wish the overhead of creating a new Slicer object // for each iteration if the only thing you are doing is adjusting // the start and end positions. Other than for performance reasons, // these methods should not be called and you should prefer the // error checking provided by constructing a new Slicer object. // Note that the length is not updated, so in principle care should // be taken that the length does not change. // void setStart (const IPosition& start) { start_p = start; } void setEnd (const IPosition& end) { end_p = end; } // private: LengthOrLast asEnd_p; IPosition start_p; IPosition end_p; IPosition stride_p; IPosition len_p; // Length of input bool fixed_p; // no MimicSource used // Define a private constructor taking an ssize_t. // This is to prevent the user from the unexpected and meaningless // Slicer that would result when the ssize_t argument is promoted to // an IPosition. // Slicer (ssize_t); // Check the given start, end/length and stride. // Fill in the length or end. // It also calls fillFixed to fill the fixed flag. void fillEndLen(); // Fill in start, len and stride from a Slice. void fillSlice (const Slice&, ssize_t& start, ssize_t& length, ssize_t& stride); // Fill the fixed flag. void fillFixed(); }; // IO functions for Slicer's // // Print the contents of the specified Slicer to the specified stream. std::ostream& operator << (std::ostream& stream, const Slicer& slicer); // std::string to_string(const Slicer& slicer); inline size_t Slicer::ndim() const { return start_p.nelements(); } inline const IPosition& Slicer::start() const { return start_p; } inline const IPosition& Slicer::end() const { return end_p; } inline const IPosition& Slicer::stride() const { return stride_p; } inline const IPosition& Slicer::length() const { return len_p; } inline bool Slicer::isFixed() const { return fixed_p; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Arrays/Storage.h000066400000000000000000000172151476623553700172570ustar00rootroot00000000000000#ifndef CASACORE_STORAGE_2_H #define CASACORE_STORAGE_2_H #include #include namespace casacore { namespace arrays_internal { // This class emplements a static (but run-time) sized array. It is used in the // Array class, and is necessary because std::vector specializes for bool. // It holds the same functionality as a normal array, and enables allocation // through different allocators similar to std::vector. template class Storage { public: // Construct an empty Storage Storage() : _data(nullptr), _end(nullptr), _isShared(false) { } // Construct Storage with a given size. // The elements will be default constructed Storage(std::size_t n) : _data(construct(n)), _end(_data + n), _isShared(false) { } // Construct Storage with a given size. // The elements will be copy constructed from the given value Storage(std::size_t n, const T& val) : _data(construct(n, val)), _end(_data + n), _isShared(false) { } // Construct Storage from a range. // The elements will be copy constructed from the given values. // Note that this constructor can be chosen over // of Storage(std::size_t, const T&, const Alloc) when T=size_t. Therefore, // this constructor forwards to the appropriate constructor based on // whether T is an integral. template Storage(InputIterator startIter, InputIterator endIter) : Storage(startIter, endIter, disjunction< std::is_integral, conjunction< std::is_same, std::is_base_of > >()) { } // Construct Storage from a range by moving. // The elements will be move constructed from the given values. static std::unique_ptr> MakeFromMove(T* startIter, T* endIter) { return std::unique_ptr>(new Storage(startIter, endIter, std::false_type(), std::true_type())); } // Construct a Storage from existing data. // The given pointer will not be owned by this class. static std::unique_ptr> MakeFromSharedData(T* existingData, size_t n) { std::unique_ptr> newStorage = std::unique_ptr(new Storage()); newStorage->_data = existingData; newStorage->_end = existingData + n; newStorage->_isShared = true; return newStorage; } // Construct a Storage with uninitialized data. // This will skip the constructor of the elements. This is only allowed for // trivial types. static std::unique_ptr> MakeUninitialized(size_t n) { static_assert(std::is_trivial::value, "Only trivial types can be constructed uninitialized"); std::unique_ptr> newStorage = std::unique_ptr(new Storage()); if(n == 0) newStorage->_data = nullptr; else newStorage->_data = std::allocator().allocate(n); newStorage->_end = newStorage->_data + n; return newStorage; } // Destructs the elements and deallocates the data ~Storage() noexcept { if(size() && !_isShared) { for(size_t i=0; i!=size(); ++i) _data[size()-i-1].~T(); std::allocator().deallocate(_data, size()); } } // Return a pointer to the storage data. // @{ T* data() { return _data; } const T* data() const { return _data; } // @} // Size of the data, zero if empty. size_t size() const { return _end - _data; } // Whether this Storage owns its data. // Returns @c true when this Storage was constructed with MakeFromSharedData(). bool is_shared() const { return _isShared; } Storage(const Storage&) = delete; Storage(Storage&&) = delete; Storage& operator=(const Storage&) = delete; Storage& operator=(Storage&&) = delete; private: // Moving range constructor implementation. Parameter integral is only a place-holder. Storage(T* startIter, T* endIter, std::false_type /*integral*/, std::true_type /*move*/) : _data(construct_move(startIter, endIter)), _end(_data + (endIter-startIter)), _isShared(false) { } // Copying range constructor implementation for non-integral types template Storage(InputIterator startIter, InputIterator endIter, std::false_type /*integral*/) : _data(construct_range(startIter, endIter)), _end(_data + std::distance(startIter, endIter)), _isShared(false) { } // Copying range constructor implementation for integral types template Storage(Integral n, Integral val, std::true_type /*integral*/) : _data(construct(n, val)), _end(_data + n), _isShared(false) { } // These methods allocate the storage and construct the elements. // When any element constructor throws, the already constructed elements are destructed in reverse // and the allocated storage is deallocated. // @{ T* construct(size_t n) { if(n == 0) return nullptr; else { T* data = std::allocator().allocate(n); T* current = data; try { for (; current != data+n; ++current) { new (current) T(); } } catch(...) { while(current != data) { --current; current->~T(); } std::allocator().deallocate(data, n); throw; } return data; } } T* construct(size_t n, const T& val) { if(n == 0) return nullptr; else { T* data = std::allocator().allocate(n); T* current = data; try { for (; current != data+n; ++current) { new (current) T(val); } } catch(...) { while(current != data) { --current; current->~T(); } std::allocator().deallocate(data, n); throw; } return data; } } template T* construct_range(InputIterator startIter, InputIterator endIter) { if(startIter == endIter) return nullptr; else { size_t n = std::distance(startIter, endIter); T* data = std::allocator().allocate(n); T* current = data; try { for (; current != data+n; ++current) { new (current) T(*startIter); ++startIter; } } catch(...) { while(current != data) { --current; current->~T(); } std::allocator().deallocate(data, n); throw; } return data; } } T* construct_move(T* startIter, T* endIter) { if(startIter == endIter) return nullptr; else { size_t n = endIter - startIter; T* data = std::allocator().allocate(n); T* current = data; try { for (; current != data+n; ++current) { new (current) T(std::move(*startIter)); ++startIter; } } catch(...) { while(current != data) { --current; current->~T(); } std::allocator().deallocate(data, n); throw; } return data; } } // @} // Used by template code above // These are already in C++17, but currently only using C++11... template struct disjunction : std::false_type { }; template struct disjunction : B1 { }; template struct disjunction : std::conditional>::type { }; template struct conjunction : std::true_type { }; template struct conjunction : B1 { }; template struct conjunction : std::conditional, B1>::type {}; T* _data; T* _end; bool _isShared; }; } } #endif casacore-3.7.1/casa/Arrays/Vector.h000066400000000000000000000324121476623553700171110ustar00rootroot00000000000000//# Vector.h: A 1-D Specialization of the Array Class //# Copyright (C) 1993,1994,1995,1996,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_VECTOR_2_H #define CASA_VECTOR_2_H //# Includes #include "Array.h" namespace casacore { //#Begin namespace casacore // A 1-D Specialization of the Array class // // // // Vector objects are one-dimensional specializations (e.g., more convenient // and efficient indexing) of the general Array class. You might also want // to look at the Array documentation to see inherited functionality. // // Generally the member functions of Array are also available in // Vector versions // which take an integer where the array needs an IPosition. Since the Vector // is one-dimensional, the IPositions are overkill, although you may // use those versions if you want to. // // Vector vi(100); // Vector 100 elements long. // vi.resize(50); // Now only 50 long. // // // Slices may be taken with the Slice class. To take a slice, one "indexes" // with Slice(start, length, inc) where end and inc are optional. // // Vector vf(100); // //... // vf(Slice(0,50,2)) = vf(Slice(1,50,2)); // Copy values from odd onto even // Vector firstHalf, secondHalf; // firstHalf.reference(vf(Slice(0,50))); // secondHalf.reference(vf(Slice(50,50))); // // Now we have aliases for two slices into the Vector // // // Element-by-element arithmetic and logical operations are available (in // aips/ArrayMath.h and aips/ArrayLogical.h) as well as dot and cross // products (in aips/MatrixMath.h). // // A Vector can be constructed from an STL vector. The reverse // operation (Array::tovector()) can construct an STL // vector from any Array. // To create any other STL container from an Array (or // the reverse), always create from/to a vector, and use the // range constructor to create from/to others (like set, list, deque). // // As with the Arrays, if the preprocessor symbol AIPS_DEBUG is // defined at compile time invariants will be checked on entry to most // member functions. Additionally, if AIPS_ARRAY_INDEX_CHECK is defined // index operations will be bounds-checked. Neither of these should // be defined for production code. template class Vector : public Array { public: // A zero-length Vector. Vector(); // A Vector with a defined length and origin of zero. // explicit Vector(size_t Length); explicit Vector(const IPosition& Length); // // A Vector with a defined length and origin of zero. // Fill it with the initial value. // Vector(size_t Length, const T &initialValue); Vector(const IPosition& Length, const T &initialValue); // // An uninitialized Vector with a defined length. // Vector(size_t Length, typename Array::uninitializedType); Vector(const IPosition& Length, typename Array::uninitializedType); // // Create a Vector from the given std::vector "other." Make it length "nr" // and copy over that many elements. // This used to take a 'Block' Vector(const std::vector &other, long long nr); // Create a Vector of length other.size() and copy over its values. // This used to take a 'Block' explicit Vector(const std::vector &other); // Create a Vector from an initializer list. Vector(std::initializer_list list); // Create a reference to other. Vector(const Vector &other); // Move Vector(Vector&& source) noexcept; // Create a reference to the other array. // It is always possible if the array has zero or one axes. // If it has > 1 axes, it is only possible if the array has at most // one axis with length > 1. In that case the degenerated axes are removed. Vector(const Array &other); // Create an Vector of a given shape from a pointer. Vector(const IPosition &shape, T *storage, StorageInitPolicy policy = COPY); // Create an Vector of a given shape from a pointer. Because the pointer // is const, a copy is always made. Vector(const IPosition &shape, const T *storage); template Vector(InputIterator startIter, InputIterator endIter); // Create a Vector from an STL vector (see tovector() in // Array for the reverse operation). // Both this constructor and the tovector() are // defined in Vector2.cc. // It does implicit promotion/demotion of the type U if different from T. template explicit Vector(const std::vector &other); // Create a Vector from a container iterator and its length. // The length is used instead of last, because the distance // function needed to calculate the length can be expensive. //
        A third dummy argument is unfortunately needed to avoid ambiguity // with another Vector constructor (taking two uInts). //
        template Vector(Iterator first, size_t size, int dummy); // Resize this Vector to the given length. // The default copyValues flag is false. //# Note that the 3rd resize function is necessary, because that //# function is virtual in the base class (otherwise it would //# be hidden). // Resize without argument is equal to resize(0, false). // using Array::resize; void resize(size_t len, bool copyValues=false) { if (len != this->nelements()) resize(IPosition(1,len), copyValues); } virtual void resize(const IPosition &len, bool copyValues=false) final override; // // Assign to this Vector. If this Vector is zero-length, then resize // to be the same size as other. Otherwise this and other have to be // conformant (same size). //
        Note that the assign function can be used to assign a // non-conforming vector. // // TODO unlike Array, a Vector assign to an empty Vector does // not create a reference but does a value copy of the source. // This should be made consistent. using Array::assign_conforming; Vector& assign_conforming(const Vector& source) { return assign_conforming_implementation(source, std::is_copy_assignable()); } Vector& assign_conforming(Vector&& source); // source must be a 1-dimensional array. Vector& assign_conforming(const Array& source); Vector& assign_conforming(Array&& source); // using Array::operator=; Vector& operator=(const Vector& source) { return assign_conforming(source); } Vector& operator=(Vector&& source) { return assign_conforming(std::move(source)); } Vector& operator=(const Array& source) { assign_conforming(source); return *this; } Vector& operator=(Array&& source) { assign_conforming(std::move(source)); return *this; } // Convert a Vector to a Block, resizing the block and copying values. // This is done this way to avoid having the simpler Block class // containing dependencies on the Vector class. //void toBlock(Block &other) const; // Single-pixel addressing. If AIPS_ARRAY_INDEX_CHECK is defined, // bounds checking is performed (not for []).. // T &operator[](size_t index) { return (this->contiguous_p ? this->begin_p[index] : this->begin_p[index*this->inc_p(0)]); } const T &operator[](size_t index) const { return (this->contiguous_p ? this->begin_p[index] : this->begin_p[index*this->inc_p(0)]); } T &operator()(const IPosition &i) { return Array::operator()(i); } const T &operator()(const IPosition &i) const { return Array::operator()(i); } T &operator()(size_t index) { #if defined(AIPS_ARRAY_INDEX_CHECK) this->validateIndex(index); //# Throws an exception on failure #endif return *(this->begin_p + index*this->inc_p(0)); } const T &operator()(size_t index) const { #if defined(AIPS_ARRAY_INDEX_CHECK) this->validateIndex(index); //# Throws an exception on failure #endif return *(this->begin_p + index*this->inc_p(0)); } // // Take a slice of this vector. Slices are always indexed starting // at zero. This uses reference semantics, i.e. changing a value // in the slice changes the original. // // Vector vd(100); // //... // vd(Slice(0,10)) = -1.0; // First 10 elements of vd set to -1 // // Vector operator()(const Slice &slice); const Vector operator()(const Slice &slice) const; // // Slice using IPositions. Required to be defined, otherwise the base // class versions are hidden. // Array operator()(const IPosition &blc, const IPosition &trc, const IPosition &incr) { return Array::operator()(blc,trc,incr); } const Array operator()(const IPosition &blc, const IPosition &trc, const IPosition &incr) const { return Array::operator()(blc,trc,incr); } Array operator()(const IPosition &blc, const IPosition &trc) { return Array::operator()(blc,trc); } const Array operator()(const IPosition &blc, const IPosition &trc) const { return Array::operator()(blc,trc); } Array operator()(const Slicer& slicer) { return Array::operator()(slicer); } const Array operator()(const Slicer& slicer) const { return Array::operator()(slicer); } // // The array is masked by the input LogicalArray. // This mask must conform to the array. // // Return a MaskedArray. MaskedArray operator() (const LogicalArray &mask) const { return Array::operator() (mask); } // Return a MaskedArray. MaskedArray operator() (const LogicalArray &mask) { return Array::operator() (mask); } // // The array is masked by the input MaskedLogicalArray. // The mask is effectively the AND of the internal LogicalArray // and the internal mask of the MaskedLogicalArray. // The MaskedLogicalArray must conform to the array. // // Return a MaskedArray. MaskedArray operator() (const MaskedLogicalArray &mask) const { return Array::operator() (mask); } // Return a MaskedArray. MaskedArray operator() (const MaskedLogicalArray &mask) { return Array::operator() (mask); } // // The length of the Vector. const IPosition &shape() const { return this->length_p; } void shape(int &Shape) const { Shape = this->length_p(0); } // Verify that dimensionality is 1 and then call Array::ok() virtual bool ok() const final override; protected: // Remove the degenerate axes from other and store result in this vector. // An exception is thrown if removing degenerate axes does not result // in a vector. virtual void doNonDegenerate(const Array &other, const IPosition &ignoreAxes) final override; virtual size_t fixedDimensionality() const final override { return 1; } private: Vector& assign_conforming_implementation(const Vector &v, std::false_type isCopyable); Vector& assign_conforming_implementation(const Vector &v, std::true_type isCopyable); // Helper functions for constructors. void initVector(const std::vector&, long long nr); // copy semantics // The following two constructors are used to make distinguish between // Vector(literal size, literal value) // and // Vector(iterator, iterator) template Vector(InputIterator startIter, InputIterator endIter, std::false_type /* T is not a literal */); template Vector(InputIterator startIter, InputIterator endIter, std::true_type /* T is literal */); }; } //#End namespace casacore #include "Vector.tcc" #include "Vector2.tcc" #endif casacore-3.7.1/casa/Arrays/Vector.tcc000066400000000000000000000224571476623553700174430ustar00rootroot00000000000000//# Vector.cc: A 1-D Specialization of the Array Class //# Copyright (C) 1993,1994,1995,1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_VECTOR_2_TCC #define CASA_VECTOR_2_TCC #include "Vector.h" #include "ArrayError.h" #include "Slice.h" #include "MaskedArray.h" #include #include namespace casacore { //#Begin casa namespace template Vector::Vector() : Array(IPosition(1,0)) { assert(ok()); } template Vector::Vector(size_t Length) : Array(IPosition(1, Length)) { assert(ok()); } template Vector::Vector(const IPosition& len) : Array(len) { Array::checkBeforeResize(len); } template Vector::Vector(size_t length, const T &initialValue) : Array(IPosition(1, length), initialValue) { assert(ok()); } template Vector::Vector(const IPosition& length, const T &initialValue) : Array(length, initialValue) { Array::checkBeforeResize(length); } template Vector::Vector(size_t length, typename Array::uninitializedType) : Array(IPosition(1, length), Array::uninitialized) { } template Vector::Vector(const IPosition& length, typename Array::uninitializedType) : Array(length, Array::uninitialized) { Array::checkBeforeResize(length); } template Vector::Vector(const std::vector &other, long long nr) : Array(IPosition(1, other.size())) { initVector (other, nr); assert(ok()); } template Vector::Vector(const std::vector &other) : Array(IPosition(1, other.size()), const_cast(other.data())) { assert(ok()); } template<> inline Vector::Vector(const std::vector& input) : Array(IPosition(1, input.size()), input.begin()) { assert(ok()); } template template Vector::Vector(InputIterator startIter, InputIterator endIter) : Vector(startIter, endIter, std::is_integral()) { } // Constructor for Vector(nonintegral, nonintegral) template template Vector::Vector(InputIterator startIter, InputIterator endIter, std::false_type) : Array(IPosition(1, std::distance(startIter, endIter)), startIter) { assert(ok()); } // Constructor for Vector(integral, integral) template template Vector::Vector(Integral length, Integral initialValue, std::true_type) : Array(IPosition(1, length), initialValue) { assert(ok()); } template Vector::Vector(std::initializer_list list) : Array(list) { assert(ok()); } template Vector::Vector(const Vector &other) : Array(other) { assert(ok()); } template Vector::Vector(Vector&& source) noexcept : Array(std::move(source), IPosition(1, 0)) { assert(ok()); } // // ArrayNDimError // template Vector::Vector(const Array &other) : Array(other) { // If not 1 dimension, adjust shape if possible. if (this->ndim() != 1) { this->checkVectorShape(); } assert(ok()); } template Vector::Vector(const IPosition &shape, T *storage, StorageInitPolicy policy) : Array(shape, storage, policy) { } template Vector::Vector(const IPosition &shape, const T *storage) : Array(shape, storage) { } // Copy from the block. Copy the number of elements specified or // the number of elements in the block if nr <= 0. // // ArrayError // template void Vector::initVector(const std::vector &other, long long nr) { size_t n = nr; if (nr <= 0) { n = other.size(); } if (n > other.size()) throw(ArrayError("Vector::initVector(const Block &other" ", long long nr) - nr > other.nelements()")); if (this->nelements() != n) { this->resize(n); } for (size_t i=0; i < n; i++) { this->begin_p[i] = other[i]; } return; } // // ArrayConformanceError // template void Vector::resize(const IPosition& l, bool copyValues) { assert(ok()); if (copyValues) { Vector oldref(*this); Array::resize(l, false); size_t minNels = std::min(this->nelements(), oldref.nelements()); move_n_with_stride(oldref.begin_p, minNels, this->begin_p, this->inc_p(0), oldref.inc_p(0)); } else { Array::resize(l, false); } assert(ok()); } template Vector& Vector::assign_conforming(const Array& a) { assert(ok()); Vector tmp(a); assign_conforming(tmp); return *this; } template Vector& Vector::assign_conforming(Array&& a) { assert(ok()); Vector tmp(std::move(a)); assign_conforming(tmp); return *this; } template Vector& Vector::assign_conforming_implementation(const Vector &, std::false_type /*movable?*/) { throw std::runtime_error("assign called for which a copy is required, while element type is not copyable"); } template Vector& Vector::assign_conforming_implementation(const Vector &other, std::true_type /*movable?*/) { assert(ok()); if (this != &other) { if (! this->copyVectorHelper (other)) { // Block was empty, so allocate new block. // TODO think about semantics of allocator! this->data_p.reset( new arrays_internal::Storage(this->length_p(0)) ); this->begin_p = this->data_p->data(); } this->setEndIter(); copy_n_with_stride (other.begin_p, this->nels_p, this->begin_p, this->inc_p(0), other.inc_p(0)); } return *this; } template Vector& Vector::assign_conforming(Vector&& source) { assert(ok()); if(this->nrefs() > 1 || source.nrefs() > 1 || this->data_p->is_shared() || source.data_p->is_shared()) assign_conforming(source); else if(source.ndim() == 0) { Vector empty; casacore::swap(empty, *this); } else casacore::swap(source, *this); return *this; } // // ArrayError // template Vector Vector::operator()(const Slice &slice) { assert(ok()); long long b, l, s; // begin length step if (slice.all()) { b = 0; l = this->length_p(0); s = 1; } else { b = slice.start(); l = slice.length(); s = slice.inc(); } // Check that the selected slice is valid if (s < 1) { throw(ArrayError("Vector::operator()(Slice) : step < 1")); } else if (l < 0) { throw(ArrayError("Vector::operator()(Slice) : length < 0")); } else if (b+(l-1)*s >= this->length_p(0)) { throw(ArrayError("Vector::operator()(Slice) : Desired slice extends" " beyond the end of the array")); } else if (b < 0) { throw(ArrayError("Vector::operator()(Slice) : start of slice before " "beginning of vector")); } // Create the slice. This could also be done with the Array::operator() // slice functions, however it's simple for vectors, and this will be // more efficient. // Create the vector that will be the slice into this Vector vp(*this); // Increment vp's begin so that it is at the selected position vp.begin_p += b*this->steps()[0]; vp.inc_p(0) *= s; vp.length_p(0) = l; vp.nels_p = l; vp.contiguous_p = vp.isStorageContiguous(); vp.makeSteps(); return vp; } template const Vector Vector::operator() (const Slice &slice) const { return const_cast*>(this)->operator() (slice); } template void Vector::doNonDegenerate (const Array &other, const IPosition &ignoreAxes) { Array tmp(*this); tmp.nonDegenerate (other, ignoreAxes); Array::reference (tmp); } template bool Vector::ok() const { return this->ndim() == 1 && Array::ok(); } //# Declare extern templates for often used types. extern template class Vector; extern template class Vector; extern template class Vector; extern template class Vector; extern template class Vector; extern template class Vector; extern template class Vector; extern template class Vector; extern template class Vector; } //#End casa namespace #endif casacore-3.7.1/casa/Arrays/Vector2.tcc000066400000000000000000000035461476623553700175230ustar00rootroot00000000000000//# Vector2.cc: Definitions for STL vector related methods //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_VECTOR2_2_TCC #define CASA_VECTOR2_2_TCC #include "Vector.h" namespace casacore { //# NAMESPACE CASACORE - BEGIN template template Vector::Vector(const std::vector &other) : Array(IPosition(1, other.size())) { size_t i=0; for (typename std::vector::const_iterator pos=other.begin(); pos != other.end(); ++pos) { (*this)[i++] = (T)*pos; } } template template Vector::Vector(Iterator first, size_t size, int) : Array(IPosition(1, size)) { for (size_t i=0; i Iterate an Vector cursor through another Array. // // // // VectorIterator steps a Vector (the "cursor") through an array for the // given axis. // The cursor "refers" to storage in the array, so that changing the // values in the cursor changes values in the original array. // // This class is derived from ArrayIterator; basically it only adds // the vector() member function which allows you to access the cursor // as a Vector. // // // The origin of the cursor, i.e. the subarray that moves through the // larger array, is always zero. // // // In this example we sum all the elements of an array; of course we already // have the "sum" function in ArrayMath.h that we should use instead. // // // Array af; // // set af // VectorIterator vi(af); // float sum = 0.0; // size_t n = vi.vector().nelements(); // while (! vi.pastEnd()) { // for (int i=0; i < n; i++) { // N.B.; cursor always 0 based. // sum += vi.vector()(i); // } // vi.next(); // } // template class VectorIterator : public ArrayIterator { public: // Iterate by vector cursors through array "a". // The vector cursor is taken for the given axis. explicit VectorIterator(Array &a, size_t axis=0); // Return a Vector at the current position. Vector &vector() {return *(Vector *)this->ap_p.get();} private: // Not implemented. VectorIterator(const VectorIterator &) = delete; // Not implemented. VectorIterator &operator=(const VectorIterator &) = delete; }; // // Iterate a Vector cursor through another Array. // // // // ReadOnlyVectorIterator behaves exactly like VectorIterator (cf.) only // it should be used on const Arrays. // // Note that the R/O VectorIterator is not derived from R/O // ArrayIterator. // template class ReadOnlyVectorIterator { public: // explicit ReadOnlyVectorIterator(const Array &a, size_t axis=0) : vi(const_cast&>(a), axis) {} void next() {vi.next();} void reset() {vi.origin();} void origin() {vi.origin();} const Array &array() {return vi.array();} const Vector &vector() {return vi.vector();} bool atStart() const {return vi.atStart();} bool pastEnd() const {return vi.pastEnd();} const IPosition &pos() const {return vi.pos();} IPosition endPos() const {return vi.endPos();} size_t ndim() const {return vi.ndim();} // private: // Not implemented. ReadOnlyVectorIterator(const ReadOnlyVectorIterator &) = delete; // Not implemented. ReadOnlyVectorIterator &operator=(const ReadOnlyVectorIterator &) = delete; VectorIterator vi; }; } //# NAMESPACE CASACORE - END #include "VectorIter.tcc" #endif casacore-3.7.1/casa/Arrays/VectorIter.tcc000066400000000000000000000032061476623553700202560ustar00rootroot00000000000000//# VectorIter.cc: Iterate a vector cursor through another array //# Copyright (C) 1993,1994,1995 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_VECTORITER_TCC #define CASA_VECTORITER_TCC #include "VectorIter.h" namespace casacore { //# NAMESPACE CASACORE - BEGIN template VectorIterator::VectorIterator(Array &a, size_t axis) : ArrayIterator(a, IPosition(1,axis), true) { // We need to ensure that ap points at a vector this->ap_p.reset( new Vector(*this->ap_p) ); // reference } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Arrays/VectorSTLIterator.h000066400000000000000000000135501476623553700212100ustar00rootroot00000000000000//# VectorSTLIterator.h: Random access iterator for Casacore Vectors //# Copyright (C) 2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_VECTORSTLITERATOR_2_H #define CASA_VECTORSTLITERATOR_2_H //# Includes #include "Vector.h" #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // Casacore Vector iterator // // // // This class creates a random access STL iterator for an Casacore Vector. All // the STL functionality is present (or if something missing can be easily // added).
        // The following comments hold: //
          //
        • A VectorSTLIterator can be created from a Vector (non-STL) // It is the same as using Vector.begin() //
        • No contiguous iterator is provided. Its construction is not necessary, // since Vector.data() is a fully functional STL iterator already. //
        • This iterator is non-intrusive. Since it needs state (the 'step') // nothing substantial is gained by having it included in the Vector class. // The major gain would be to be able to use the standard nomenclature: // Vector::iterator() rather than VectorSTLiterator //
        • It would be advisable, and easy to implement, to add a template argument // to the Array classes: . The // default is contiguous, since creation is contiguous. In that case correct // iterators for e.g. contiguous arrays are supplied automatically. //
        • needs probably some 'const' fine tuning; and the -> operator //
        //
        template class VectorSTLIterator : public std::iterator { public: typedef T value_type; typedef value_type* pointer; typedef const value_type* const_pointer; typedef VectorSTLIterator iterator; typedef const VectorSTLIterator const_iterator; typedef value_type& reference; typedef const value_type& const_reference; typedef std::size_t size_type; typedef ptrdiff_t difference_type; // Constructors. The iterator constructor from a Vector is // the same as if created from Vector.begin(). Copy // constructor and assignment can be the default ones. // explicit VectorSTLIterator(const Vector &c) : start_p(const_cast(c.data())), step_p (std::max(ssize_t(1), c.steps()(0))), iter_p (const_cast(c.data())) {} VectorSTLIterator() : start_p(0), step_p(1), iter_p(0) {} VectorSTLIterator(const typename Array::IteratorSTL &c) : start_p(c.pos()), step_p (std::max(ssize_t(1), c.steps()(0))), iter_p (start_p) {} // // Access // reference operator[](size_t i) { return *(start_p+i*step_p); }; const_reference operator[](size_t i) const { return *(start_p+i*step_p); }; reference operator*() { return *iter_p; }; const_reference operator*() const { return *iter_p; }; pointer pos() const {return iter_p; } // // Manipulation // const iterator &operator++() { iter_p+=step_p; return *this; }; iterator operator++(int) { iterator t = *this; iter_p+=step_p; return t; }; iterator &operator--() { iter_p-=step_p; return *this; }; iterator operator--(int) { VectorSTLIterator t = *this;iter_p-=step_p; return t; }; iterator &operator+=(difference_type i) { iter_p+=i*step_p; return *this; }; iterator &operator-=(difference_type i) { iter_p-=i*step_p; return *this; }; iterator operator+(difference_type i) const { VectorSTLIterator t = *this; return t+=i; }; iterator operator-(difference_type i) const { VectorSTLIterator t = *this; return t-=i; }; // // Size related // difference_type operator-(const VectorSTLIterator &x) const { return (iter_p-x.iter_p)/step_p; }; // // Comparisons // bool operator== (const iterator &other) const { return iter_p == other.iter_p; }; bool operator!= (const iterator other) const { return iter_p != other.iter_p; }; bool operator< (const iterator &other) const { return iter_p < other.iter_p; }; bool operator== (const_pointer const pos) const { return iter_p == pos; }; bool operator!= (const_pointer const pos) const { return iter_p != pos; }; bool operator< (const_pointer const pos) const { return iter_p < pos; }; // protected: // Start (for random indexing) pointer const start_p; // Distance between elements difference_type step_p; // Current element pointer pointer iter_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Arrays/Vector_tmpl.cc000066400000000000000000000031561476623553700203060ustar00rootroot00000000000000//# Vector_tmpl.cc: Explicit Vector template instantiations //# Copyright (C) 2015 //# Associated Universities, Inc. Washington DC, USA, //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include "Vector.h" //# Instantiate extern templates for often used types. namespace casacore { template class Vector; template class Vector; template class Vector; template class Vector; template class Vector; template class Vector; template class Vector; template class Vector; template class Vector; } casacore-3.7.1/casa/Arrays/test/000077500000000000000000000000001476623553700164535ustar00rootroot00000000000000casacore-3.7.1/casa/Arrays/test/CMakeLists.txt000066400000000000000000000025541476623553700212210ustar00rootroot00000000000000set (testfiles tAllocator.cc tArray.cc tArrayAccessor.cc tArrayBase.cc #tArrayIO2.cc #tArrayIO3.cc #tArrayIO.cc tArrayExceptionHandling.cc tArrayIter.cc tArrayIter1.cc tArrayIteratorSTL.cc tArrayLogical.cc tArrayMath.cc #tArrayMathPerf.cc tArrayMathTransform.cc tArrayOperations.cc tArrayOpsDiffShapes.cc tArrayPartMath.cc tArrayPosIter.cc tArrayStr.cc tArrayUtil.cc #tArrayUtilPerf.cc tAxesSpecifier.cc tBoxedArrayMath.cc #tCompareBoxedPartial.cc tConvertArray.cc tCpp11Features.cc tCube.cc tDiagonal.cc tExtendSpecifier.cc tIPosition.cc tLinAlgebra.cc tMaskArrExcp.cc tMaskArrIO.cc tMaskArrLogi.cc tMaskArrMath0.cc tMaskArrMath1.cc tMaskArrMath2.cc tMaskedArray.cc tMatrix.cc tMatrixMath.cc tMedianSmooth.cc # tPerfTransform.cc tSlice.cc tSlicer.cc tSlidingArrayMath.cc tStringArray.cc # tSumPerformance.cc tVector.cc tVectorSTLIterator.cc ) find_package(Boost COMPONENTS filesystem unit_test_framework system) if(Boost_FOUND) include_directories(${Boost_INCLUDE_DIR}) add_executable (arraytest ${testfiles}) add_pch_support(arraytest) target_link_libraries(arraytest casa_casa ${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) add_test (arraytest ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./arraytest) add_dependencies(check arraytest) endif(Boost_FOUND) casacore-3.7.1/casa/Arrays/test/TestUtilities.h000066400000000000000000000017551476623553700214470ustar00rootroot00000000000000#ifndef TEST_UTILITIES_H #define TEST_UTILITIES_H #include "../Array.h" #include "../IPosition.h" #include #include inline void check(const casacore::Array& v, std::initializer_list list) { std::vector a; a.reserve(v.nelements()); for(bool b : v) a.push_back(b ? 1 : 0); BOOST_CHECK_EQUAL_COLLECTIONS(a.begin(), a.end(), list.begin(), list.end()); } template inline void check(const casacore::Array& v, std::initializer_list list) { BOOST_CHECK_EQUAL_COLLECTIONS(v.begin(), v.end(), list.begin(), list.end()); } template inline void check(const casacore::MaskedArray& v, std::initializer_list arr, std::initializer_list mask) { check(v.getArray(), arr); check(v.getMask(), mask); } inline void check(const casacore::IPosition& ip, std::initializer_list reference) { BOOST_CHECK_EQUAL_COLLECTIONS(ip.begin(), ip.end(), reference.begin(), reference.end()); } #endif casacore-3.7.1/casa/Arrays/test/dArrayAccessor.cc000066400000000000000000000232611476623553700216730ustar00rootroot00000000000000//# dArrayAccessor.cc: Demonstrator for the ArrayAccessor 1D access class //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include int main() { // Loop number const size_t Ncnt=100; try { Cube cub(5,2,4); for (size_t i=0; i<5; i++) { for (size_t j=0; j<2; j++) { for (size_t k=0; k<4; k++) { cub(i,j,k) = 100*i + 10*j + k + 10000; } } } cout << "--------- Test ArrayAccessor ---------------------" << endl; cout << "------------------ Loop in cube ------------------" << endl; cout << "Cube: " << cub << endl; cout << "With accessor over axes 2-0-1: " << endl; for (ArrayAccessor > i(cub); i != i.end() ; ++i) { for (ArrayAccessor > j(i); j != j.end(); ++j) { cout << *j << ", " << j.index >(1) << endl; } } ArrayAccessor > aa(cub); ArrayAccessor > ab(cub); cout << "t1: " << *aa << ", " << *aa.begin() << ", " << *aa.begin(3) << endl;;; ab.reset(aa.begin(3)); cout << "t2: " << *ab << endl;; ab++; cout << "t3: " << *ab << endl;; } catch (std::exception& x) { cout << x.what() << endl; } try { Cube cube(10,6,16); for (size_t i=0; i<10; i++) { for (size_t j=0; j<6; j++) { for (size_t k=0; k<16; k++) { cube(i,j,k) = 100*i + 10*j + k + 10000; } } } Cube cub = cube(Slice(1,5,2), Slice(0,2,3), Slice(2,4,4)); cout << "-------------------- Loop in slice of cube --------" << endl; cout << "Cube: " << cub << endl; cout << "With accessor over axes 2-0-1: " << endl; for (ArrayAccessor > i(cub); i != i.end() ; ++i) { for (ArrayAccessor > j(i); j != j.end(); ++j) { cout << *j << ", " << j.index >(1) << endl; } } } catch (std::exception& x) { cout << x.what() << endl; } try { Cube cub(100,100,100); indgen (cub); cout << "-- Various timings (*" << Ncnt << "(Cube: *" << Ncnt/10 << ")) -------------" << endl; Timer timer; for (size_t cnt=0; cnt > i(cub); i != i.end() ; ++i) { for (ArrayAccessor > j(i); j != j.end() ; ++j) { for (ArrayAccessor > k(j); k != k.end() ; ++k) { if (*k != inx) { cout << inx << ' ' << *k << endl; } inx++; } } } if (inx != int(cub.nelements())) { cout << "Inx: " << inx << ' ' << cub.nelements() << endl; } } timer.show("ArrayAccessor full "); } { timer.mark(); for (size_t cnt=0; cnt > i(cub); i != i.end() ; ++i) { for (ArrayAccessor j(i, AxisN(1)); j != j.end() ; ++j) { for (ArrayAccessor > k(j); k != k.end() ; ++k) { if (*k != inx) { cout << inx << ' ' << *k << endl; } inx++; } } } if (inx != int(cub.nelements())) { cout << "Inx: " << inx << ' ' << cub.nelements() << endl; } } timer.show("ArrayAccessor run "); } { timer.mark(); for (size_t cnt=0; cnt > i(cub); i != i.end() ; ++i) { for (ArrayAccessor j(i, AxisN(1)); j != j.end() ; ++j) { for (ArrayAccessor k(j, AxisN(0)); k != k.end() ; ++k) { if (*k != inx) { cout << inx << ' ' << *k << endl; } inx++; } } } if (inx != int(cub.nelements())) { cout << "Inx: " << inx << ' ' << cub.nelements() << endl; } } timer.show("ArrayAccessor run2 "); } { timer.mark(); for (size_t cnt=0; cnt > i; ArrayAccessor > j; ArrayAccessor > k; for (i = ArrayAccessor >(cub); i != i.end() ; ++i) { for (j = i; j != j.end() ; ++j) { for (k = j; k != k.end() ; ++k) { if (*k != inx) { cout << inx << ' ' << *k << endl; } inx++; } } } if (inx != int(cub.nelements())) { cout << "Inx: " << inx << ' ' << cub.nelements() << endl; } } timer.show("ArrayAccessor nome "); } { timer.mark(); for (size_t cnt=0; cnt > i; ArrayAccessor > j; ArrayAccessor > k; for (i = ArrayAccessor >(cub); i != i.end() ; ++i) { for (j = i; j != j.end() ; ++j) { for (k = j; k != k.end() ; ++k) { *k = inx; inx++; } } } if (inx != int(cub.nelements())) { cout << "Inx: " << inx << ' ' << cub.nelements() << endl; } } timer.show("Accessor writenome "); } { timer.mark(); for (size_t cnt=0; cnt > i(cub); ArrayAccessor > j; ArrayAccessor > k; for (; i != i.end() ; ++i) { for (j = i; j != j.end() ; ++j) { for (k = j; k != k.end() ; ++k) { inx = *k; inx++; } } } if (inx != int(cub.nelements())) { cout << "Inx: " << inx << ' ' << cub.nelements() << endl; } } timer.show("Accessor read nome "); } timer.mark(); for (size_t cnt=0; cnt cubs = cub(Slice(0,50,2), Slice(0,100,1), Slice(0,100,1)); timer.mark(); for (size_t cnt=0; cnt > i(cubs); i != i.end() ; ++i) { for (ArrayAccessor > j(i); j != j.end() ; ++j) { for (ArrayAccessor > k(j); k != k.end() ; ++k) { if (*k != inx) { cout << inx << ' ' << *k << endl; } inx+=2; } } } if (inx != int(cub.nelements())) { cout << "Inx: " << inx << ' ' << cub.nelements() << endl; } } timer.show("ArrayAccessor part "); timer.mark(); for (size_t cnt=0; cnt using namespace casacore; class Pool { public: Pool(size_t poolSize=1024) : _size(poolSize), _position(0), _nAlloc(0), _nDealloc(0) { _pool = new char[poolSize]; } Pool(const Pool& source) = delete; ~Pool() noexcept(false) { if(_nAlloc != _nDealloc) throw std::runtime_error("Memory leak: nalloc=" + std::to_string(_nAlloc) + ", ndealloc=" + std::to_string(_nDealloc)); delete[] _pool; } Pool& operator=(const Pool& source) = delete; char* allocate(size_t n) { char* data = &_pool[_position]; size_t allocsize = n==0 ? 1 : n; // We much return a unique ptr to be able to distinguish it _position += allocsize; if(_position > _size) throw std::runtime_error("Pool is full"); ++_nAlloc; _chunks.emplace(reinterpret_cast(data), std::make_pair(n, true)); return data; } void deallocate(char* data, size_t n) { // Generally, deallocate(nullptr) is not allowed for all allocators, so this function throws when doing so auto iter = _chunks.find(reinterpret_cast(data)); if(iter == _chunks.end()) throw std::runtime_error("Can't deallocate chunk at " + std::to_string((size_t)data) + ": not an allocated chunk"); if(!iter->second.second) throw std::runtime_error("Can't deallocate chunk at " + std::to_string((size_t)data) + ": was already deallocated"); if(iter->second.first != n) throw std::runtime_error("Can't deallocate chunk at " + std::to_string((size_t)data) + ": invalid size specified, expected " + std::to_string(iter->second.first) + ", was " + std::to_string(n)); iter->second.second = false; ++_nDealloc; } private: char* _pool; size_t _size, _position; size_t _nAlloc, _nDealloc; std::map> _chunks; }; // Type that satisfies the minimum requirements of an Allocator template class PoolAllocator { public: // No default constructor is required. PoolAllocator(size_t mem) : _pool(new Pool(mem)) { } // Copy and move constructors are required: default ones are okay. PoolAllocator(const PoolAllocator& source) = default; PoolAllocator& operator=(const PoolAllocator& source) = default; // Rebind constructor template PoolAllocator(const PoolAllocator& source) : _pool(source._pool) { } // Rebind assignment template PoolAllocator& operator=(const PoolAllocator& source) { _pool = source._pool; return *this; } T* allocate(size_t n) { return reinterpret_cast(_pool->allocate(n*sizeof(T))); } void deallocate(T* data, size_t n) { _pool->deallocate(reinterpret_cast(data), n*sizeof(T)); } private: std::shared_ptr _pool; }; BOOST_AUTO_TEST_SUITE(allocator) BOOST_AUTO_TEST_CASE(poolallocator) { // Do some basic tests to check if the custom allocator works properly PoolAllocator alloc(1024); int* a = alloc.allocate(1); int* b = alloc.allocate(5); int* c = alloc.allocate(1); int* n = alloc.allocate(0); BOOST_CHECK(a != nullptr); BOOST_CHECK(b != nullptr); BOOST_CHECK(c != nullptr); BOOST_CHECK_NE(a, b); BOOST_CHECK_NE(b, c); BOOST_CHECK_NE(a, c); a[0] = 1982; for(size_t i=0; i!=5; ++i) b[i] = 2020+i; c[0] = 37; BOOST_CHECK_EQUAL(a[0], 1982); for(size_t i=0; i!=5; ++i) BOOST_CHECK_EQUAL(b[i], 2020+i); BOOST_CHECK_EQUAL(c[0], 37); alloc.deallocate(b, 5); int* d = alloc.allocate(3); BOOST_CHECK(d != nullptr); BOOST_CHECK_NE(a, d); BOOST_CHECK_NE(c, d); d[0] = 8; d[1] = 9; d[2] = 10; BOOST_CHECK_EQUAL(a[0], 1982); BOOST_CHECK_EQUAL(c[0], 37); for(size_t i=0; i!=3; ++i) BOOST_CHECK_EQUAL(d[i], 8+i); alloc.deallocate(n, 0); alloc.deallocate(d, 3); alloc.deallocate(a, 1); alloc.deallocate(c, 1); } BOOST_AUTO_TEST_CASE(array_shared_storage) { int storage = 42; Array a(IPosition{1}, &storage, StorageInitPolicy::SHARE); BOOST_CHECK_EQUAL(storage, 42); std::vector ref={42}; BOOST_CHECK_EQUAL_COLLECTIONS(a.begin(), a.end(), ref.begin(), ref.end()); a.assign_conforming(Array(IPosition{1}, 37)); BOOST_CHECK_EQUAL(storage, 37); ref[0] = 37; BOOST_CHECK_EQUAL_COLLECTIONS(a.begin(), a.end(), ref.begin(), ref.end()); } BOOST_AUTO_TEST_CASE(array_take_shared_storage) { IPosition shape{3, 4, 2, 3}; std::vector v(shape.product(), 1982); Array a; a.takeStorage(shape, v.data(), StorageInitPolicy::SHARE); BOOST_CHECK_EQUAL(v.data()[0], 1982); std::vector ref1(shape.product(), 1982); BOOST_CHECK_EQUAL_COLLECTIONS(a.begin(), a.end(), ref1.begin(), ref1.end()); a.assign_conforming(Array(shape, 1988)); std::vector ref2(shape.product(), 1988); BOOST_CHECK_EQUAL_COLLECTIONS(v.begin(), v.end(), ref2.begin(), ref2.end()); BOOST_CHECK_EQUAL_COLLECTIONS(a.begin(), a.end(), ref2.begin(), ref2.end()); } BOOST_AUTO_TEST_CASE(array_uninitialized) { IPosition shape{1, 2, 3, 4}; Array a(shape, Array::uninitialized); BOOST_CHECK_EQUAL(a.shape(), shape); a = 3; BOOST_CHECK(allEQ(a, 3)); Array b(shape, Array::uninitialized); b.assign_conforming(a); BOOST_CHECK(allEQ(b, 3)); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tArray.cc000066400000000000000000000701011476623553700202230ustar00rootroot00000000000000//# tArray.cc: Test program for the Array class //# Copyright (C) 1993,1994,1995,1996,1997,1998,1999,2000,2001,2002,2003,2015 //# Associated Universities, Inc. Washington DC, USA. //# National Astronomical Observatory of Japan //# 2-21-1, Osawa, Mitaka, Tokyo, 181-8588, Japan. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include "../IPosition.h" #include "../Array.h" #include "../Vector.h" #include "../Slice.h" #include "../Slicer.h" #include "../ArrayError.h" #include #include #include using namespace casacore; BOOST_AUTO_TEST_SUITE(array) static int zero(int) { return 0; } static int minusone(const int &) { return -1; } BOOST_AUTO_TEST_CASE( multidim_construct ) { IPosition shape(2, 5, 5); Array x(shape); BOOST_CHECK_EQUAL(x.ndim(), 2); BOOST_CHECK_EQUAL(x.shape(), shape); } BOOST_AUTO_TEST_CASE( string_construct ) { class CasaStringLike : public std::string { public: CasaStringLike() : std::string() { } CasaStringLike(char c) : std::string(1, c) { } CasaStringLike(const char* c) : std::string(c) { } }; IPosition shape(2, 2, 2); Array x(shape, CasaStringLike("some_string")); //Array x(shape, "some_string"); // <-- This should not compile BOOST_CHECK_EQUAL(x.ndim(), 2); BOOST_CHECK_EQUAL(x.shape(), shape); std::vector ref(2*2, CasaStringLike("some_string")); BOOST_CHECK_EQUAL_COLLECTIONS(ref.begin(), ref.end(), x.begin(), x.end()); } BOOST_AUTO_TEST_CASE( index ) { Array x(IPosition(2, 5, 5)); IPosition index(2); index = 2; x(index) = 6; BOOST_CHECK_EQUAL(x(index), 6); } BOOST_AUTO_TEST_CASE( multidim_set ) { Array x(IPosition(2, 5, 5)); x.set(-1); for(size_t i=0; i!=5; ++i) { for(size_t j=0; j!=5; ++j) { BOOST_CHECK_EQUAL(x(IPosition(2,i,j)), -1); } } } BOOST_AUTO_TEST_CASE( multidim_shape1 ) { IPosition shape(2, 5, 5); Array x(shape), y(shape+1); y.resize(x.shape()); BOOST_CHECK_EQUAL(y.shape(), x.shape()); } BOOST_AUTO_TEST_CASE( multidim_shape2 ) { IPosition shape(2, 5, 5); Array x(shape), y(shape+1); y.resize(x.shape()); y.assign_conforming(x); BOOST_CHECK_EQUAL(y.shape(), x.shape()); } BOOST_AUTO_TEST_CASE( multidim_shape3 ) { IPosition shape(2, 5, 5); Array y(shape); y.resize(shape + 3); BOOST_CHECK_EQUAL( y.shape(), shape + 3); } BOOST_AUTO_TEST_CASE( multidim_initialize ) { Array y1(IPosition(2, 5, 5), 4); BOOST_CHECK (allEQ(y1, 4)); } BOOST_AUTO_TEST_CASE( slices1 ) { IPosition i1(3), i2(3); i1 = 0; i2 = 3; Array a1(i2); a1 = 0; i2 = 1; a1(i1, i2) = 1; BOOST_CHECK(allEQ (a1(i1, i2), 1)); BOOST_CHECK_EQUAL(a1(i1), 1); BOOST_CHECK_EQUAL(a1(i2), 1); } BOOST_AUTO_TEST_CASE( slices2 ) { IPosition i1(3), i2(3); i1 = 0; i2 = 3; Array a1(i2); a1 = 0; i2 = 1; a1(i1, i2) = 1; i2 = 2; BOOST_CHECK_EQUAL(a1(i2), 0); } BOOST_AUTO_TEST_CASE( apply1 ) { Array a1(IPosition(3, 3, 3, 3)); a1.apply(zero); BOOST_CHECK(allEQ (a1, 0)); } BOOST_AUTO_TEST_CASE( apply2 ) { Array a1(IPosition(3, 3, 3, 3)); a1.apply(zero); a1.apply(minusone); BOOST_CHECK(allEQ (a1, -1)); } BOOST_AUTO_TEST_CASE( apply_function ) { Vector vi(10); indgen(vi); vi.apply([](float x)->float { return x * x; }); for (int i=0; i < 10; i++) { BOOST_CHECK_CLOSE_FRACTION(vi(i), i*i, 1e-5); } } BOOST_AUTO_TEST_CASE( copy ) { Array a1(IPosition(3, 3, 3, 3)); a1.apply(minusone); BOOST_CHECK(allEQ (a1, a1.copy())); } BOOST_AUTO_TEST_CASE( assign ) { Array a1(IPosition(3, 3, 3, 3)); a1.apply(minusone); Array a2(a1); a1.assign_conforming(a1); BOOST_CHECK(allEQ (a1, a2)); } BOOST_AUTO_TEST_CASE( resize1 ) { IPosition i2(3); i2 = 0; Array a1(IPosition(3, 3, 3, 3)); Array a2(a1); a1.resize(i2); a1.reference(a2); BOOST_CHECK(allEQ (a1, a2)); } BOOST_AUTO_TEST_CASE( resize2 ) { IPosition i2(3); i2.resize(1); i2 = 10; Array a2(IPosition(3, 3, 3, 3)), a1(a2); a2.resize(i2); a2 = 10; i2 = 0; a1.resize(i2); a1.assign_conforming(a2); BOOST_CHECK(allEQ (a1, 10)); } BOOST_AUTO_TEST_CASE( unique ) { IPosition i2(1, 100); Array *a3 = new Array(i2); *a3 = 11.0; BOOST_CHECK_EQUAL(11.0, (*a3)(IPosition(3, 0, 0, 0))); Array a4(a3->operator()(IPosition(a3->ndim(), 0), a3->shape()-1, IPosition(1,2))); BOOST_CHECK(allEQ (a4, 11.0F)); delete a3; a4.unique(); BOOST_CHECK(allEQ (a4, 11.0F)); } BOOST_AUTO_TEST_CASE( reform ) { Array ab1(IPosition(4,5,6,7,8)); indgen(ab1); Array ab2 (ab1(IPosition(4,1,2,1,3), IPosition(4,2,2,5,7), IPosition(4,1,1,2,3)).reform (IPosition(3,2,3,2))); for (size_t i=0; i<2; i++) { for (size_t j=0; j<3; j++) { for (size_t k=0; k<2; k++) { BOOST_CHECK_EQUAL (&(ab2(IPosition(3,i,j,k))), &(ab1(IPosition(4,1+i,2,1+j*2,3+k*3)))); } } } } BOOST_AUTO_TEST_CASE( slicer1 ) { Array ab1(IPosition(4,5,6,7,8)); indgen(ab1); Slicer sl(IPosition(4,1,2,1,3), IPosition(4,2,2,5,5), IPosition(4,1,1,2,3), Slicer::endIsLast); Array absl = ab1(sl); BOOST_CHECK (absl.shape() == IPosition(4,2,1,3,1)); for (size_t i=0; i<2; i++) { for (size_t j=0; j<3; j++) { for (size_t k=0; k<1; k++) { BOOST_CHECK_EQUAL (&(absl(IPosition(4,i,0,j,k))), &(ab1(IPosition(4,1+i,2,1+j*2,3+k*3)))); } } } } BOOST_AUTO_TEST_CASE( slicer2 ) { Array ab1(IPosition(4,5,6,7,8)); indgen(ab1); Slicer sl(IPosition(4,1,2,1,3), IPosition(4,2,2,Slicer::MimicSource,7), IPosition(4,1,1,2,3), Slicer::endIsLast); Array absl = ab1(sl); BOOST_CHECK (absl.shape() == IPosition(4,2,1,3,2)); for (size_t i=0; i<2; i++) { for (size_t j=0; j<3; j++) { for (size_t k=0; k<2; k++) { BOOST_CHECK_EQUAL (&(absl(IPosition(4,i,0,j,k))), &(ab1(IPosition(4,1+i,2,1+j*2,3+k*3)))); } } } } BOOST_AUTO_TEST_CASE( empty_slice ) { Array a1(IPosition(3,2,3,4)); Array a2 = a1(IPosition(3,0,0,2), IPosition(3,0,0,1)); BOOST_CHECK_EQUAL (a2.shape(), IPosition(3,1,1,0)); BOOST_CHECK_EQUAL (a2.size(), 0); } // reformOrResize, adjustLastAxis static Array reformArray() { IPosition shape (2, 3, 4); Array a0 (shape); for (int r = 0; r < 3; r++) for (int c = 0; c < 4; c++) a0 (IPosition (2, r, c)) = r * 10 + c; return a0; } BOOST_AUTO_TEST_CASE( array_reform_or_resize ) { Array a1 = reformArray(); // Test a no-op for adjustLastAxis. a1.reformOrResize (IPosition (2, 3, 4)); BOOST_CHECK_EQUAL (a1.shape(), reformArray().shape()); } BOOST_AUTO_TEST_CASE( array_reform ) { Array a1 = reformArray(); // Simple reform which shouldn't involve resizing. IPosition newShape (2, 4, 3); bool resized = a1.reformOrResize (newShape); BOOST_CHECK_EQUAL (a1.shape(), newShape); BOOST_CHECK_EQUAL ((size_t) a1.capacity(), newShape.product()); BOOST_CHECK (!resized); } BOOST_AUTO_TEST_CASE( array_reform_with_resize ) { Array a1 = reformArray(); // Simple reform which should involve resizing. IPosition newShape (2, 3, 10); bool resized = a1.reformOrResize (newShape); BOOST_CHECK_EQUAL (a1.shape(), newShape); BOOST_CHECK_EQUAL ((size_t) a1.capacity(), newShape.product()); BOOST_CHECK (resized); } BOOST_AUTO_TEST_CASE( array_reform_smaller ) { // Simple reform to make it smaller Array a1 = reformArray(); IPosition newShape (2, 3, 3); bool resized = a1.reformOrResize (newShape); BOOST_CHECK_EQUAL (a1.shape(), newShape); BOOST_CHECK_EQUAL (a1.capacity(), reformArray().capacity()); // same allocation size BOOST_CHECK (!resized); } BOOST_AUTO_TEST_CASE( array_reform_exception ) { // See that when resizing is required but forbidden that exception thrown. Array a1 = reformArray(); try { a1.reformOrResize (IPosition (2, 3, 10), 0, false); // forbid resize BOOST_CHECK (false); // shouldn't get here } catch (ArrayConformanceError & e) { // Everything's fine if we get here. BOOST_CHECK (true); } } BOOST_AUTO_TEST_CASE( array_dimensionality_exception ) { // Attempt to change dimensionality should throw exception. Array a1 = reformArray(); try { a1.reformOrResize (IPosition (1, 12)); // attempt to change dimensionality BOOST_CHECK (false); // shouldn't get here } catch (ArrayConformanceError & e){ // Everything's fine if we get here. BOOST_CHECK (true); } } BOOST_AUTO_TEST_CASE( array_shared_exception ) { // Arrays that share storage must cause exception if method is called. Array a1 = reformArray(); Array a2(a1); // copy construction --> sharing try { a1.reformOrResize (IPosition (2, 3, 3)); // would work except for sharing BOOST_CHECK (false); // shouldn't get here } catch (ArrayConformanceError & e) { // Everything's fine if we get here. BOOST_CHECK (true); } } BOOST_AUTO_TEST_CASE( array_padding ) { Array a1 = reformArray(); // See if padding functionality works. Capacity ought to be 50% larger // than actually used. IPosition newShape (IPosition (2, 3, 6)); bool resized = a1.reformOrResize (newShape, 50); BOOST_CHECK_EQUAL (a1.shape(), newShape); BOOST_CHECK (resized); BOOST_CHECK_EQUAL ((size_t) a1.capacity(), newShape.product() * 3 / 2); } BOOST_AUTO_TEST_CASE( array_adjustlastaxis1 ) { Array a0 = reformArray(), a1 = reformArray(); // AdjustLastAxis the last dimension by minus one and check that the data is preserved. IPosition newShape (IPosition (2, 3, 3)); bool resized = a1.adjustLastAxis (newShape); BOOST_CHECK_EQUAL (a1.shape(), newShape); BOOST_CHECK (! resized); // should just reform for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { BOOST_CHECK_EQUAL (a1 (IPosition (2, i, j)), a0 (IPosition (2, i, j))); } } } BOOST_AUTO_TEST_CASE( array_adjustlastaxis2 ) { Array a0 = reformArray(), a1 = reformArray(); // AdjustLastAxis the last dimension by one and check that the data is preserved. IPosition newShape (IPosition (2, 3, 5)); bool resized = a1.adjustLastAxis (newShape); BOOST_CHECK_EQUAL (a1.shape(), newShape); BOOST_CHECK (resized); // should have been resized for (int i = 0; i < 3; i++) { for (int j = 0; j < 4; j++) { BOOST_CHECK_EQUAL (a1 (IPosition (2, i, j)), a0 (IPosition (2, i, j))); } } } BOOST_AUTO_TEST_CASE( array_resize_exception ) { Array a1 = reformArray(); // See that when resizing is required but forbidden that exception thrown. try { a1.adjustLastAxis (IPosition (2, 3, 10), 0, false); // forbid resize BOOST_CHECK (false); // shouldn't get here } catch (ArrayConformanceError & e){ // Everything's fine if we get here. BOOST_CHECK (true); // shouldn't get here } } BOOST_AUTO_TEST_CASE( multi_dimensional_copy ) { Array arr; Array arr2(arr); BOOST_CHECK (arr2.ndim()==0 && arr2.nelements()==0); BOOST_CHECK (arr2.shape() == IPosition()); } BOOST_AUTO_TEST_CASE( resize_copy ) { Array arr1(IPosition(3,4,5,6)); indgen (arr1); Array arr2; arr2.assign_conforming( arr1 ); arr1.resize (IPosition(3,4,5,8), true); BOOST_CHECK (allEQ (arr2, arr1(IPosition(3,0), IPosition(3,3,4,5)))); arr1.resize (IPosition(3,6,4,2), true); BOOST_CHECK (allEQ (arr2(IPosition(3,0), IPosition(3,3,3,1)), arr1(IPosition(3,0), IPosition(3,3,3,1)))); arr1.resize(); arr1.assign_conforming( arr2 ); arr1.resize (IPosition(2,6,4), true); Array arr1ca = arr1.reform(IPosition(3,6,4,1)); BOOST_CHECK (allEQ (arr2(IPosition(3,0), IPosition(3,3,3,0)), arr1ca(IPosition(3,0), IPosition(3,3,3,0)))); arr1.resize (IPosition(4,8,3,2,4), true); Array arr1cb = arr1.reform(IPosition(3,8,3,8)); BOOST_CHECK (allEQ (arr2(IPosition(3,0), IPosition(3,3,2,0)), arr1cb(IPosition(3,0), IPosition(3,3,2,0)))); } BOOST_AUTO_TEST_CASE( new_interface1 ) { IPosition const shape(2, 2, 3); size_t const nelems = shape.product(); Array ai(shape, int(0)); bool deleteIt; int const *ptr = ai.getStorage(deleteIt); for (size_t i = 0; i < nelems; ++i) { BOOST_CHECK(ptr[i] == 0); } ai.freeStorage(ptr, deleteIt); BOOST_CHECK(ptr == nullptr); } BOOST_AUTO_TEST_CASE( new_interface2 ) { IPosition const shape(2, 2, 3); size_t const nelems = shape.product(); Array ai(shape, int(0)); bool deleteIt; int *ptr = ai.getStorage(deleteIt); for (size_t i = 0; i < nelems; ++i) { BOOST_CHECK(ptr[i] == 0); ptr[i] = int(i); } ai.putStorage(ptr, deleteIt); BOOST_CHECK(ptr == 0); for (size_t i = 0; i < nelems; ++i) { BOOST_CHECK(ai.data()[i] == int(i)); } } BOOST_AUTO_TEST_CASE( new_interface3 ) { IPosition const shape(2, 2, 3); Array ai(shape, int(0)); bool deleteIt; void const *ptr = ai.getVStorage(deleteIt); ai.freeVStorage(ptr, deleteIt); BOOST_CHECK(ptr == nullptr); } BOOST_AUTO_TEST_CASE( new_interface4 ) { IPosition const shape(2, 2, 3); Array ai(shape, int(0)); bool deleteIt; void *ptr = ai.getStorage(deleteIt); ai.putVStorage(ptr, deleteIt); BOOST_CHECK(ptr == nullptr); } namespace { struct LifecycleChecker { LifecycleChecker() { if (ctor_count >= ctor_error_trigger) { throw 0; } ++ctor_count; } LifecycleChecker(LifecycleChecker const &) { if (ctor_count >= ctor_error_trigger) { throw 0; } ++ctor_count; } LifecycleChecker(LifecycleChecker&& src) : LifecycleChecker(src) { } ~LifecycleChecker() { ++dtor_count; } LifecycleChecker & operator =(LifecycleChecker const&) { if (assign_count >= assign_error_trigger) { throw 0; } ++assign_count; return *this; } LifecycleChecker & operator =(LifecycleChecker&& rhs) { return operator=(rhs); } static void clear() { assign_count = ctor_count = dtor_count = 0; assign_error_trigger = ctor_error_trigger = std::numeric_limits::max(); } static size_t assign_count; static size_t ctor_count; static size_t dtor_count; static size_t ctor_error_trigger; static size_t assign_error_trigger; }; size_t LifecycleChecker::assign_count = 0; size_t LifecycleChecker::ctor_count = 0; size_t LifecycleChecker::dtor_count = 0; size_t LifecycleChecker::ctor_error_trigger = std::numeric_limits::max(); size_t LifecycleChecker::assign_error_trigger = std::numeric_limits::max(); BOOST_AUTO_TEST_CASE( life_cycle_1 ) { IPosition const shape(2, 2, 3); LifecycleChecker::clear(); LifecycleChecker *ptr = new LifecycleChecker[shape.product()]; { Array a; a.takeStorage(shape, ptr, SHARE); a.resize(IPosition(2, 3, 3), false); } delete[] ptr; BOOST_CHECK_EQUAL(LifecycleChecker::ctor_count, LifecycleChecker::dtor_count); } // This test-case is no longer valid: deallocation of an array that's // allocated with new[] is not supported in Array2. /* BOOST_AUTO_TEST_CASE( life_cycle_2 ) { IPosition const shape(2, 2, 3); LifecycleChecker::clear(); LifecycleChecker *ptr = new LifecycleChecker[shape.product()]; { Array a; a.takeStorage(shape, ptr, TAKE_OVER); a.resize(IPosition(2, 3, 3), true); } BOOST_CHECK_EQUAL(LifecycleChecker::ctor_count, LifecycleChecker::dtor_count); } */ BOOST_AUTO_TEST_CASE( life_cycle_3 ) { IPosition const shape(2, 2, 3); LifecycleChecker::clear(); LifecycleChecker *ptr = new LifecycleChecker[shape.product()]; { Array a; a.takeStorage(shape, ptr, COPY); } delete[] ptr; BOOST_CHECK_EQUAL(LifecycleChecker::ctor_count, LifecycleChecker::dtor_count); } BOOST_AUTO_TEST_CASE( life_cycle_4 ) { IPosition const shape(2, 2, 3); LifecycleChecker::clear(); LifecycleChecker *ptr = new LifecycleChecker[shape.product()]; { Array a(shape, ptr, SHARE); } delete[] ptr; BOOST_CHECK_EQUAL(LifecycleChecker::ctor_count, LifecycleChecker::dtor_count); } /* BOOST_AUTO_TEST_CASE( life_cycle_5 ) { IPosition const shape(2, 2, 3); LifecycleChecker::clear(); LifecycleChecker *ptr = new LifecycleChecker[shape.product()]; { Array a(shape, ptr, TAKE_OVER); } BOOST_CHECK_EQUAL(LifecycleChecker::ctor_count, LifecycleChecker::dtor_count); } */ BOOST_AUTO_TEST_CASE( life_cycle_6 ) { IPosition const shape(2, 2, 3); LifecycleChecker::clear(); LifecycleChecker *ptr = new LifecycleChecker[shape.product()]; { Array a(shape, ptr, COPY); } delete[] ptr; BOOST_CHECK_EQUAL(LifecycleChecker::ctor_count, LifecycleChecker::dtor_count); } BOOST_AUTO_TEST_CASE( life_cycle_7 ) { IPosition const shape(2, 2, 3); size_t const nelems = shape.product(); LifecycleChecker::clear(); LifecycleChecker *ptr = std::allocator().allocate(shape.product()); for (size_t i = 0; i < nelems; ++i) { std::allocator().construct(&ptr[i]); } { Array a; a.takeStorage(shape, ptr, SHARE); a.resize(IPosition(2, 3, 3), false); } for (size_t i = 0; i < nelems; ++i) { std::allocator().destroy(&ptr[i]); } std::allocator().deallocate(ptr, nelems); BOOST_CHECK_EQUAL(LifecycleChecker::ctor_count, LifecycleChecker::dtor_count); } BOOST_AUTO_TEST_CASE( life_cycle_8 ) { IPosition const shape(2, 2, 3); size_t const nelems = shape.product(); LifecycleChecker::clear(); std::allocator allocator; LifecycleChecker *ptr = allocator.allocate(shape.product()); for (size_t i = 0; i < nelems; ++i) { allocator.construct(&ptr[i]); } { Array a; a.takeStorage(shape, ptr, TAKE_OVER); a.resize(IPosition(2, 3, 3), true); } BOOST_CHECK_EQUAL(LifecycleChecker::ctor_count, LifecycleChecker::dtor_count); } BOOST_AUTO_TEST_CASE( life_cycle_9 ) { IPosition const shape(2, 2, 3); size_t const nelems = shape.product(); LifecycleChecker::clear(); std::allocator allocator; LifecycleChecker *ptr = allocator.allocate(shape.product()); { Array a; a.takeStorage(shape, ptr, COPY); } allocator.deallocate(ptr, nelems); BOOST_CHECK_EQUAL(LifecycleChecker::ctor_count, LifecycleChecker::dtor_count); } BOOST_AUTO_TEST_CASE( life_cycle_10 ) { IPosition const shape(2, 2, 3); size_t const nelems = shape.product(); LifecycleChecker::clear(); std::allocator allocator; LifecycleChecker *ptr = allocator.allocate(shape.product()); for (size_t i = 0; i < nelems; ++i) { allocator.construct(&ptr[i]); } { Array a(shape, ptr, SHARE); } for (size_t i = 0; i < nelems; ++i) { allocator.destroy(&ptr[i]); } allocator.deallocate(ptr, nelems); BOOST_CHECK_EQUAL(LifecycleChecker::ctor_count, LifecycleChecker::dtor_count); } BOOST_AUTO_TEST_CASE( life_cycle_11 ) { IPosition const shape(2, 2, 3); size_t const nelems = shape.product(); LifecycleChecker::clear(); std::allocator allocator; LifecycleChecker *ptr = allocator.allocate(shape.product()); for (size_t i = 0; i < nelems; ++i) { allocator.construct(&ptr[i]); } { Array a(shape, ptr, TAKE_OVER); } BOOST_CHECK_EQUAL(LifecycleChecker::ctor_count, LifecycleChecker::dtor_count); } BOOST_AUTO_TEST_CASE( life_cycle_12 ) { IPosition const shape(2, 2, 3); size_t const nelems = shape.product(); LifecycleChecker::clear(); std::allocator allocator; LifecycleChecker *ptr = allocator.allocate(shape.product()); { Array a(shape, ptr, COPY); } allocator.deallocate(ptr, nelems); BOOST_CHECK_EQUAL(LifecycleChecker::ctor_count, LifecycleChecker::dtor_count); } /* BOOST_AUTO_TEST_CASE( life_cycle_13 ) { IPosition const shape(2, 2, 3); LifecycleChecker::clear(); { // no longer supported in Array2 // Array a(shape, ArrayInitPolicies::NO_INIT); } BOOST_CHECK(LifecycleChecker::ctor_count == 0); BOOST_CHECK(LifecycleChecker::dtor_count == (size_t)shape.product()); }*/ BOOST_AUTO_TEST_CASE( life_cycle_14 ) { IPosition const shape(2, 2, 3); LifecycleChecker::clear(); { Array a(shape); } BOOST_CHECK_EQUAL(LifecycleChecker::ctor_count, (size_t) shape.product()); BOOST_CHECK_EQUAL(LifecycleChecker::dtor_count, (size_t) shape.product()); } /* BOOST_AUTO_TEST_CASE( life_cycle_15 ) { IPosition const shape(2, 2, 3); { Array a; LifecycleChecker::clear(); // no longer supported in Array2 // a.resize(shape, false, ArrayInitPolicies::NO_INIT); } BOOST_CHECK(LifecycleChecker::ctor_count == 0); BOOST_CHECK(LifecycleChecker::dtor_count == (size_t )shape.product()); }*/ BOOST_AUTO_TEST_CASE( life_cycle_16 ) { IPosition const shape(2, 2, 3); { Array a; LifecycleChecker::clear(); a.resize(shape, false); } BOOST_CHECK_EQUAL(LifecycleChecker::ctor_count, (size_t )shape.product()); BOOST_CHECK_EQUAL(LifecycleChecker::dtor_count, (size_t )shape.product()); } BOOST_AUTO_TEST_CASE( array_pos_iterator ) { IPosition const shape(2, 2, 3); Array ai(shape); BOOST_CHECK_EQUAL(ai.capacity(), 2 * 3); for (ssize_t c = 0; c < 3; ++c) { for (ssize_t r = 0; r < 2; ++r) { IPosition pos(2, r, c); ai(pos) = r*100 + c; } } int *p = ai.data(); int order[] = {0, 100, 1, 101, 2, 102}; for (ssize_t i = 0; i < (int)ai.capacity(); ++i) { BOOST_CHECK_EQUAL(p[i], order[i]); } size_t count1 = 0; for (ArrayPositionIterator iter1(shape, 1); !iter1.pastEnd(); iter1.next()) ++count1; BOOST_CHECK_EQUAL(count1, 3); size_t count2 = 0; for (ArrayPositionIterator iter2(shape, 0); !iter2.pastEnd(); iter2.next()) ++count2; BOOST_CHECK_EQUAL(count2, 6); } } // anonymous namespace // Various further tests (coming from 'main()') BOOST_AUTO_TEST_CASE( array_empty ) { Array ai1; BOOST_CHECK_EQUAL(ai1.ndim(), 0); BOOST_CHECK_EQUAL(ai1.nelements(), 0); } BOOST_AUTO_TEST_CASE( array_init1 ) { IPosition ip1(5,1,2,3,4,5); Array ai2(ip1); BOOST_CHECK_EQUAL(ai2.ndim(), 5); BOOST_CHECK_EQUAL(ai2.nelements(), 120); BOOST_CHECK_EQUAL(ai2.shape(), ip1); } BOOST_AUTO_TEST_CASE( array_init2 ) { IPosition ip3(1,11); Array ai4(ip3); ai4.set(10); BOOST_CHECK(allEQ(ai4, 10)); } BOOST_AUTO_TEST_CASE( array_iposition_index ) { IPosition ip3(1,11); Array ai4(ip3); ai4.set(10); IPosition ip5(1); for(int i=0; i <11; i++) { ip5(0) = i; BOOST_CHECK_EQUAL(ai4(ip5), 10); // T operator()(IPosition) } } BOOST_AUTO_TEST_CASE( array_reference ) { IPosition ip1(5,1,2,3,4,5); Array ai3(ip1); IPosition ip3(1,11); Array ai4(ip3); ai4.set(10); ai3.reference(ai4); BOOST_CHECK(ai4.nrefs() == 2 && ai3.nrefs() == 2); BOOST_CHECK(ai3.ndim() == 1 && ai3.shape() == 11); BOOST_CHECK(&ai3(IPosition{0}) == &ai4(IPosition{0})); // Eventually should carry on with all member functions. Still, // The test coverage isn't terrible. } /* BOOST_AUTO_TEST_CASE( pointer_array ) { // Tests of the pointer->Array functions std::vector ip(100); IPosition shape(2, 5, 20); Array ai(shape, ip.data(), SHARE); // sharing no longer possible in Array2 indgen(ai); for (int i=0; i < 100; i++) { BOOST_CHECK(ip[i] == i); } Array ai2(shape, ip.data(), COPY); BOOST_CHECK(allEQ(ai2, ai)); ai2 = 11; BOOST_CHECK(ip[0] == 0 && ip[99] == 99 && ai(IPosition(2,4,19)) == 99 && allEQ(ai2, 11)); Vector vi(IPosition(1, 100), ip.data(), SHARE); Matrix mi(IPosition(2, 10, 10), ip.data(), SHARE); Cube ci(IPosition(3, 4, 5, 5), ip.data(), SHARE); vi(99) = 66; BOOST_CHECK(vi(99) == 66 && mi(9,9) == 66 && ci(3,4,4) == 66 && ai(IPosition(2,4,19)) == 66); }*/ BOOST_AUTO_TEST_CASE( non_degenerate1 ) { // Test the nonDegenerate() function Array a1(IPosition(5,1,2,1,3,1)); indgen(a1); BOOST_CHECK(a1.nonDegenerate().shape() == IPosition(2,2,3)); BOOST_CHECK(a1.nonDegenerate(1).shape() == IPosition(3,1,2,3)); Array c = a1.nonDegenerate(1); BOOST_CHECK(c(IPosition(3,0,1,2)) == 5); c(IPosition{0,1,2}) = 99; BOOST_CHECK(a1(IPosition(5, 0, 1, 0, 2, 0)) == 99); BOOST_CHECK(a1.nonDegenerate(4).shape() == IPosition(4,1,2,1,3)); Array a2(IPosition(3,1,1,1)); BOOST_CHECK(a2.nonDegenerate().shape() == IPosition(1,1)); } BOOST_AUTO_TEST_CASE( non_degenerate2 ) { Array a1(IPosition(5,1,2,1,3,1)); indgen(a1); const Array a3(a1); Array c = a1.nonDegenerate(1); c(IPosition(3, 0,1,2)) = 99; BOOST_CHECK(a3.nonDegenerate().shape() == IPosition(2,2,3)); BOOST_CHECK(a3.nonDegenerate(1).shape() == IPosition(3,1,2,3)); BOOST_CHECK(a3.nonDegenerate()(IPosition(2,0,2)) == 4); BOOST_CHECK(a3.nonDegenerate()(IPosition(2,1,2)) == 99); } BOOST_AUTO_TEST_CASE( non_degenerate3 ) { Array a1(IPosition(5,1,2,1,3,1)); indgen(a1); const Array a3(a1); Array c = a1.nonDegenerate(1); c(IPosition(3, 0,1,2)) = 99; Array a4; a4.nonDegenerate(a1); BOOST_CHECK(a4.shape() == IPosition(2,2,3)); BOOST_CHECK(a4(IPosition(2,0,2)) == 4); BOOST_CHECK(a4(IPosition(2,1,2)) == 99); a4.nonDegenerate(a1, 1); BOOST_CHECK(a4.shape() == IPosition(3,1,2,3)); BOOST_CHECK(a4(IPosition(3,0,0,0)) == 0); BOOST_CHECK(a4(IPosition(3,0,1,2)) == 99); } BOOST_AUTO_TEST_CASE( add_generate ) { Array a1(IPosition(2,10,10)); indgen(a1); BOOST_CHECK(a1.addDegenerate(1u).shape()==IPosition(3,10,10,1)); Array m = a1(IPosition(2,1),IPosition(2,3),IPosition(2,2)); BOOST_CHECK(m(IPosition(2, 0,0)) == 11); BOOST_CHECK(m(IPosition(2, 1,1)) == 33); Array md(m.addDegenerate(2u)); BOOST_CHECK(md.shape() == IPosition(4,2,2,1,1)); BOOST_CHECK(md(IPosition(4,0)) == 11); BOOST_CHECK(md(IPosition(4,1,1,0,0)) == 33); md(IPosition(4,0)) = 100; BOOST_CHECK(m(IPosition(2, 0,0)) == 100); const Array a2(m); BOOST_CHECK(a2.addDegenerate(1u).shape() == IPosition(3,2,2,1)); } BOOST_AUTO_TEST_CASE( zero_dimensional_arrays ) { // Test 0-dimensioned (not sized) arrays IPosition shape(0); Array ai(shape); Array ai2(ai); ai2.assign_conforming( ai ); ai = 999; BOOST_CHECK(ai.ndim() == 0 && ai2.ndim() == 0 && ai.nelements() == 0); } BOOST_AUTO_TEST_CASE( nondegenerate_on_subsection ) { // Test nonDegenerate on an Array subsection. IPosition shape0(5,2,3,4,5,6); Array data(shape0); indgen(data, float(0.0)); IPosition blc(5, 0); IPosition trc = shape0 - 1; for (int i=0; i data2 = data(blc, trc); IPosition shape1(3, shape0(1), shape0(2), shape0(4)); Array data3 = data2.nonDegenerate(); Array data4 = data2.reform(shape1); BOOST_CHECK (allEQ(data3, data4)); bool deleteIt; const float* dataPtr = data2.getStorage (deleteIt); Array data5 (shape1, dataPtr); BOOST_CHECK (allEQ(data3, data5)); data2.freeStorage (dataPtr, deleteIt); } } } BOOST_AUTO_TEST_CASE( assign_empty ) { Array zeroArr; zeroArr = Array(); BOOST_CHECK_EQUAL( zeroArr.shape().size(), 0); BOOST_CHECK( zeroArr.shape() == IPosition() ); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tArrayAccessor.cc000066400000000000000000000254101476623553700217110ustar00rootroot00000000000000//# tArrayAccessor.cc: Test program for the ArrayAccessor class //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include "../ArrayAccessor.h" #include "../ArrayMath.h" #include "../Cube.h" #include using namespace casacore; BOOST_AUTO_TEST_SUITE(array_accessor) BOOST_AUTO_TEST_CASE( cube_access ) { const size_t N0=10, N1=20, N2=30; Cube cub(N0, N1, N2); indgen(cub); int inx(0); for (size_t i=0; i!=N2; ++i) { for (size_t j=0; j!=N1; ++j) { for (size_t k=0; k!=N0; ++k) { BOOST_CHECK_EQUAL(cub(k,j,i), inx); ++inx; } } } } BOOST_AUTO_TEST_CASE( axis_constructors ) { const size_t N0=10, N1=20, N2=30; Cube cub(N0, N1, N2); indgen(cub); int inx(0); for (ArrayAccessor > i(cub); i!=i.end(); ++i) { for (ArrayAccessor > j(i); j!=j.end(); ++j) { for (ArrayAccessor > k(j); k!=k.end(); ++k) { BOOST_CHECK_EQUAL(*k, inx); ++inx; } } } } BOOST_AUTO_TEST_CASE( axisn_constructors ) { const size_t N0=10, N1=20, N2=30; Cube cub(N0, N1, N2); indgen(cub); int inx(0); for (ArrayAccessor i(cub, AxisN(2)); i!=i.end(); ++i) { for (ArrayAccessor j(i, AxisN(1)); j!=j.end(); ++j) { for (ArrayAccessor k(j, AxisN(0)); k!=k.end(); ++k) { BOOST_CHECK_EQUAL(*k, inx); ++inx; } } } } BOOST_AUTO_TEST_CASE( axis_axisn_constructors1 ) { const size_t N0=10, N1=20, N2=30; Cube cub(N0, N1, N2); indgen(cub); int inx(0); for (ArrayAccessor > i(cub); i!=i.end(); ++i) { for (ArrayAccessor j(i, AxisN(1)); j!=j.end(); ++j) { for (ArrayAccessor > k(j); k!=k.end(); ++k) { BOOST_CHECK_EQUAL(*k, inx); ++inx; } } } } BOOST_AUTO_TEST_CASE( axis_axisn_constructors2 ) { const size_t N0=10, N1=20, N2=30; Cube cub(N0, N1, N2); indgen(cub); int inx = 0; for (ArrayAccessor i(cub, AxisN(2)); i!=i.end(); ++i) { for (ArrayAccessor > j(i); j!=j.end(); ++j) { for (ArrayAccessor k(j, AxisN(0)); k!=k.end(); ++k) { BOOST_CHECK_EQUAL(*k, inx); ++inx; } } } } BOOST_AUTO_TEST_CASE( axis_assignments ) { const size_t N0=10, N1=20, N2=30; Cube cub(N0, N1, N2); indgen(cub); int inx = 0; ArrayAccessor > i(cub); ArrayAccessor > j; ArrayAccessor > k; ArrayAccessor > l; for (; i!=i.end(); ++i) { for (j = i; j!=j.end(); ++j) { for (k =j; k!=k.end(); ++k) { BOOST_CHECK_EQUAL(*k, inx); l = k; BOOST_CHECK_EQUAL(*l, inx); ++inx; } } } } BOOST_AUTO_TEST_CASE( axisn_assignments ) { const size_t N0=10, N1=20, N2=30; Cube cub(N0, N1, N2); indgen(cub); int inx = 0; ArrayAccessor i(cub, AxisN(2)); ArrayAccessor j(AxisN(1)); ArrayAccessor k(AxisN(0)); ArrayAccessor l(AxisN(0)); for (; i!=i.end(); ++i) { for (j = i; j!=j.end(); ++j) { for (k =j; k!=k.end(); ++k) { BOOST_CHECK_EQUAL(*k, inx); l = k; BOOST_CHECK_EQUAL(*l, inx); ++inx; } } } } BOOST_AUTO_TEST_CASE( axis_axisn_assignments1 ) { Cube cub(10, 20, 30); indgen(cub); int inx = 0; ArrayAccessor > i(cub); ArrayAccessor j(AxisN(1)); ArrayAccessor > k; ArrayAccessor l(AxisN(0)); for (; i!=i.end(); ++i) { for (j = i; j!=j.end(); ++j) { for (k =j; k!=k.end(); ++k) { BOOST_CHECK_EQUAL(*k, inx); l = k; BOOST_CHECK_EQUAL(*l, inx); ++inx; } } } } BOOST_AUTO_TEST_CASE( axis_axisn_assignments2 ) { Cube cub(10, 20, 30); indgen(cub); int inx = 0; ArrayAccessor i(cub, AxisN(2)); ArrayAccessor > j; ArrayAccessor k(AxisN(0)); ArrayAccessor > l; for (; i!=i.end(); ++i) { for (j = i; j!=j.end(); ++j) { for (k =j; k!=k.end(); ++k) { BOOST_CHECK_EQUAL(*k, inx); l = k; BOOST_CHECK_EQUAL(*l, inx); ++inx; } } } } BOOST_AUTO_TEST_CASE( axis_init1 ) { Cube cub(10, 20, 30); indgen(cub); int inx = 0; ArrayAccessor > i(cub); for (; i!=i.end(); ++i) { for (ArrayAccessor > j(i); j!=j.end(); ++j) { for (ArrayAccessor > k(j); k!=k.end(); ++k) { BOOST_CHECK_EQUAL(*k, inx); ++inx; } } } } BOOST_AUTO_TEST_CASE( axis_init2 ) { Cube cub(10, 20, 30); indgen(cub); int inx = 0; ArrayAccessor > i(cub); while(i!=i.end()) ++i; for (i.init(cub); i!=i.end(); ++i) { for (ArrayAccessor > j(i); j!=j.end(); ++j) { for (ArrayAccessor > k(j); k!=k.end(); ++k) { BOOST_CHECK_EQUAL(*k, inx); ++inx; } } } } BOOST_AUTO_TEST_CASE( axisn_init ) { Cube cub(10, 20, 30); indgen(cub); int inx = 0; ArrayAccessor i(cub, AxisN(2)); ArrayAccessor j(AxisN(1)); ArrayAccessor k(AxisN(0)); for (; i!=i.end(); ++i) { for (j = i; j!=j.end(); ++j) { for (k = j; k!=k.end(); ++k) { BOOST_CHECK_EQUAL(*k, inx); ++inx; } } } inx = 0; j.init(AxisN(0)); k.init(AxisN(1)); for (i.init(cub, AxisN(2)); i!=i.end(); ++i) { for (k = i; k!=k.end(); ++k) { for (j = k; j!=j.end(); ++j) { BOOST_CHECK_EQUAL(*j, inx); ++inx; } } } } BOOST_AUTO_TEST_CASE( axis_access ) { const size_t N0=10; Cube cub(N0, 20, 30); indgen(cub); int inx = 0; for (ArrayAccessor > i(cub); i!=i.end(); ++i) { for (ArrayAccessor > j(i); j!=j.end(); ++j) { BOOST_CHECK_EQUAL(*j, inx); BOOST_CHECK_EQUAL(j.next >(), inx+1); BOOST_CHECK_EQUAL(j.index >(2), inx+2); BOOST_CHECK_EQUAL(j.next(AxisN(0)), inx+1); BOOST_CHECK_EQUAL(j.index(2, AxisN(0)), inx+2); if (j == j.end()-1) { BOOST_CHECK_EQUAL(j.prev >(), inx-1); BOOST_CHECK_EQUAL(j.prev(AxisN(0)), inx-1); } inx += N0; } } } BOOST_AUTO_TEST_CASE( operations ) { const size_t N0=10, N1=20, N2=30; Cube cub(N0, N1, N2); indgen(cub); // Test += -= -- ++ --- int inx = 0; int ln = N0*N1; for (ArrayAccessor > i(cub); i!=i.end(-4); ++i) { BOOST_CHECK_EQUAL(*i, inx); ++i; BOOST_CHECK_EQUAL(*i, inx+1*ln); i++; BOOST_CHECK_EQUAL(*i, inx+2*ln); --i; BOOST_CHECK_EQUAL(*i, inx+1*ln); i--; BOOST_CHECK_EQUAL(*i, inx); i += 3; BOOST_CHECK_EQUAL(*i, inx+3*ln); i -= 1; BOOST_CHECK_EQUAL(*i, inx+2*ln); i -= 2; inx += ln; } } BOOST_AUTO_TEST_CASE( dereferencing ) { const size_t N0=10, N1=20, N2=30; Cube cub(N0, N1, N2); indgen(cub); int inx = 0; int ln = N0*N1; size_t cnt = 0; for (ArrayAccessor > i(cub); i!=i.end(-2); ++i) { BOOST_CHECK_EQUAL(*i, inx); BOOST_CHECK(i.data() == &cub(0,0,cnt)); BOOST_CHECK(&(i.baseArray()) == &cub); BOOST_CHECK(i[2] == inx+2*ln); BOOST_CHECK(i.step() == size_t(ln)); inx += ln; ++cnt; } } BOOST_AUTO_TEST_CASE( iterators ) { // test end(), begin(), rbegin(), rend() const size_t N0=10, N1=20, N2=30; Cube cub(N0, N1, N2); indgen(cub); int inx = 0; int ln = N0*N1; for (ArrayAccessor > i(cub); i!=i.end(); ++i) { BOOST_CHECK_EQUAL(*i, inx); BOOST_CHECK_EQUAL(i.begin(), &cub(0,0,0)); BOOST_CHECK_EQUAL(i.end(), i.begin()+N2*ln); BOOST_CHECK_EQUAL(i.rbegin(), i.end()-ln); BOOST_CHECK_EQUAL(i.rend(), i.begin()-ln); BOOST_CHECK_EQUAL(i.begin(-2), i.begin()-2*ln); BOOST_CHECK_EQUAL(i.end(-1), i.begin()+N2*ln-ln); BOOST_CHECK_EQUAL(i.rbegin(+5), i.end()-ln+5*ln); BOOST_CHECK_EQUAL(i.rend(1), i.begin()); inx += ln; } } BOOST_AUTO_TEST_CASE( reset1 ) { const size_t N0=10, N1=20, N2=30; Cube cub(N0, N1, N2); indgen(cub); int inx = 0; int ln = N0*N1; ArrayAccessor > i(cub); for (; i!=i.end(); ++i) { BOOST_CHECK_EQUAL(*i, inx); inx += ln; } inx = 0; for (i.reset(); i!=i.end(); ++i) { BOOST_CHECK_EQUAL(*i, inx); inx += ln; } inx = 2*ln; for (i.reset(i.begin(2)); i!=i.end(); ++i) { BOOST_CHECK_EQUAL(*i, inx); inx += ln; } } BOOST_AUTO_TEST_CASE( reset2 ) { const size_t N0=10, N1=20, N2=30; Cube cub(N0, N1, N2); indgen(cub); int inx = 0; int ln = N0*N1; ArrayAccessor i(cub, AxisN(2)); for (; i!=i.end(); ++i) { BOOST_CHECK_EQUAL(*i, inx); inx += ln; } inx = 0; for (i.reset(); i!=i.end(); ++i) { BOOST_CHECK_EQUAL(*i, inx); inx += ln; } inx = 2*ln; for (i.reset(i.begin(2)); i!=i.end(); ++i) { BOOST_CHECK_EQUAL(*i, inx); inx += ln; } } BOOST_AUTO_TEST_CASE( split_loops1 ) { const size_t N0=10, N1=20, N2=30; Cube cub(N0, N1, N2); indgen(cub); int inx = 0; int ln = N0*N1; ArrayAccessor > i(cub); ArrayAccessor > j; for (; i!=i.end(-2); ++i) { BOOST_CHECK_EQUAL(*i, inx); inx += ln; } for (j = i; j!=j.end(); ++j) { BOOST_CHECK_EQUAL(*j, inx); inx += ln; } } BOOST_AUTO_TEST_CASE( split_loops2 ) { const size_t N0=10, N1=20, N2=30; Cube cub(N0, N1, N2); indgen(cub); int inx = 0; int ln = N0*N1; ArrayAccessor i(cub, AxisN(2)); ArrayAccessor j(AxisN(2)); for (; i!=i.end(-2); ++i) { BOOST_CHECK_EQUAL(*i, inx); inx += ln; } for (j = i; j!=j.end(); ++j) { BOOST_CHECK_EQUAL(*j, inx); inx += ln; } } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tArrayBase.cc000066400000000000000000000212201476623553700210140ustar00rootroot00000000000000//# tArrayBase.cc: Test program for the Array class //# Copyright (C) 2013 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #define BOOST_TEST_MODULE array2 #define BOOST_TEST_DYN_LINK #include #include "../Array.h" #include "../ArrayBase.h" #include "../ArrayMath.h" #include "../ArrayPosIter.h" #include "../Slicer.h" using namespace casacore; BOOST_AUTO_TEST_SUITE(array_base) // Check that an array is empty. void checkEmpty (const ArrayBase& arr) { BOOST_CHECK_EQUAL (arr.ndim(), 0); BOOST_CHECK_EQUAL (arr.shape(), IPosition()); BOOST_CHECK_EQUAL (arr.size(), 0); BOOST_CHECK_EQUAL (arr.nelements(), 0); BOOST_CHECK (arr.empty()); BOOST_CHECK_EQUAL (arr.endPosition(), IPosition()); BOOST_CHECK_EQUAL (arr.steps(), IPosition()); BOOST_CHECK (arr.contiguousStorage()); } // Check that an array contains data. void checkFilled (const ArrayBase& arr) { BOOST_CHECK_EQUAL (arr.ndim(), 3); BOOST_CHECK_EQUAL (arr.shape(), IPosition(3,5,6,10)); BOOST_CHECK_EQUAL (arr.size(), 5*6*10); BOOST_CHECK_EQUAL (arr.nelements(), 5*6*10); BOOST_CHECK (!arr.empty()); BOOST_CHECK_EQUAL (arr.endPosition(), IPosition(3,4,5,9)); BOOST_CHECK_EQUAL (arr.steps(), IPosition(3,1,5,30)); BOOST_CHECK (arr.contiguousStorage()); } // Check that the virtual functions throw an exception for a base object. void checkBaseVF (const ArrayBase& arr) { bool ok = true; /*try { arr.makeIterator(1); ok = false; } catch (const std::exception&) { }*/ BOOST_CHECK (ok); BOOST_CHECK_THROW(arr.makeArray(), std::exception); BOOST_CHECK (ok); /*try { arr.getSection(Slicer()); ok = false; } catch (const std::exception&) { }*/ BOOST_CHECK (ok); ArrayBase arr2(arr); BOOST_CHECK_THROW(arr2.resize(IPosition()), std::exception); BOOST_CHECK (ok); BOOST_CHECK_THROW(arr2.assignBase(arr), std::exception); BOOST_CHECK (ok); } // Check if the values in the array are correct. void checkValues (const ArrayBase& arr, int st, size_t nr) { const Array& arri = dynamic_cast&>(arr); BOOST_CHECK_EQUAL (arri.size(), nr); for (size_t i=0; i arr0(new Array()); checkEmpty (*arr0); } BOOST_AUTO_TEST_CASE( array_resize_empty ) { // Make a similar array and resize it. std::unique_ptr arr0(new Array()); std::unique_ptr arr1 = arr0->makeArray(); arr1->resize (IPosition(3,5,6,10)); checkFilled (*arr1); } BOOST_AUTO_TEST_CASE( array_values1 ) { // Store values and check them. std::unique_ptr arr0(new Array()); std::unique_ptr arr1 = arr0->makeArray(); arr1->resize (IPosition(3,5,6,10)); indgen (dynamic_cast&>(*arr1)); checkValues (*arr1, 0, 300); } BOOST_AUTO_TEST_CASE( array_values2 ) { // Create and check another array. std::unique_ptr arr2( new Array(IPosition(3,5,6,10)) ); indgen (dynamic_cast&>(*arr2),10); checkValues (*arr2, 10, 300); } BOOST_AUTO_TEST_CASE( make_array ) { std::unique_ptr arr2( new Array(IPosition(3,5,6,10)) ); indgen (dynamic_cast&>(*arr2),10); checkEmpty (*arr2->makeArray()); } BOOST_AUTO_TEST_CASE( array_resize_filled ) { std::unique_ptr arr2( new Array(IPosition(3,5,6,10)) ); indgen (dynamic_cast&>(*arr2),10); arr2->resize (IPosition()); checkEmpty (*arr2); } BOOST_AUTO_TEST_CASE( array_resize_assign_base ) { std::unique_ptr arr0(new Array()); std::unique_ptr arr2( new Array(IPosition(3,5,6,10)) ); std::unique_ptr arr1 = arr0->makeArray(); arr1->resize (IPosition(3,5,6,10)); indgen (dynamic_cast&>(*arr1)); indgen (dynamic_cast&>(*arr2),10); arr2->resize (IPosition()); arr2->assignBase (*arr1); checkFilled (*arr2); checkValues (*arr2, 0, 300); } BOOST_AUTO_TEST_CASE( array_section ) { std::unique_ptr arr2( new Array(IPosition(3,5,6,10)) ); indgen (dynamic_cast&>(*arr2)); // Check if getting a section is fine. checkFilled (*arr2->getSection(Slicer(IPosition(3,0), IPosition(3,5,6,10)))); checkValues (*arr2->getSection(Slicer(IPosition(3,0), IPosition(3,5,6,10))), 0, 300); checkValues (*arr2->getSection(Slicer(IPosition(3,0,0,2), IPosition(3,5,6,4))), 60, 120); } BOOST_AUTO_TEST_CASE( array_assign_base ) { std::unique_ptr arr0( new Array(IPosition(3,5,6,10)) ); std::unique_ptr arr1 = arr0->makeArray(); arr1->resize (IPosition(3,5,6,10)); indgen (dynamic_cast&>(*arr1)); // Check assign in various ways. arr1->assignBase (Array()); checkEmpty (*arr1); } BOOST_AUTO_TEST_CASE( array_assign_reference ) { std::unique_ptr arr1( new Array(IPosition(3,5,6,10)) ); arr1->assignBase (Array()); std::unique_ptr arr2( new Array(IPosition(3,5,6,10)) ); indgen (dynamic_cast&>(*arr2)); arr1->assignBase (*arr2); checkFilled (*arr1); // Make sure that after the assign arr1 and arr2 reference different data. // TODO this I want to change! dynamic_cast&>(*arr1) += 100; checkValues (*arr1, 100, 300); //checkValues (*arr2, 0, 300); } BOOST_AUTO_TEST_CASE( array_iteration ) { std::unique_ptr arr2( new Array(IPosition(3,5,6,10)) ); indgen (dynamic_cast&>(*arr2)); std::unique_ptr iter = arr2->makeIterator(2); int nr = 0; while (!iter->pastEnd()) { checkValues (iter->getArray(), nr*30, 30); iter->next(); nr++; } BOOST_CHECK_EQUAL (nr, 10); } BOOST_AUTO_TEST_CASE( array_assign_wrong_type ) { std::unique_ptr arr2( new Array(IPosition(3,5,6,10)) ); indgen (dynamic_cast&>(*arr2)); // Check that assign of a different array type fails. try { arr2->assignBase (Array()); BOOST_CHECK(false); } catch (const std::exception&) { BOOST_CHECK(true); } } // TODO move construct & move assign BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tArrayExceptionHandling.cc000066400000000000000000000041711476623553700235530ustar00rootroot00000000000000#include "../Array.h" #include BOOST_AUTO_TEST_SUITE(array_exception_handling) using namespace casacore; class ExceptionThrower { public: explicit ExceptionThrower(bool someValue = false) { if(someValue || _count == _throwIndex) throw std::exception(); ++_count; _maxCount=std::max(_count, _maxCount); } ExceptionThrower(const ExceptionThrower&) { if(_count == _throwIndex) throw std::exception(); ++_count; _maxCount=std::max(_count, _maxCount); } ~ExceptionThrower() { --_count; } ExceptionThrower& operator=(const ExceptionThrower&) { return *this; } static void SetThrowIndex(size_t index) { _throwIndex = index; } static size_t Count() { return _count; } static size_t MaxCount() { return _maxCount; } static void Reset(size_t throwIndex) { _count = 0; _maxCount = 0; _throwIndex=throwIndex; } private: static size_t _count, _maxCount, _throwIndex; }; size_t ExceptionThrower::_count = 0; size_t ExceptionThrower::_maxCount = 0; size_t ExceptionThrower::_throwIndex = 0; BOOST_AUTO_TEST_CASE( default_construct ) { ExceptionThrower::Reset(1); std::unique_ptr> a; BOOST_CHECK_THROW( a.reset(new Array(IPosition(1, 3))), std::exception ); BOOST_CHECK_EQUAL(ExceptionThrower::Count(), 0); BOOST_CHECK_EQUAL(ExceptionThrower::MaxCount(), 1); } BOOST_AUTO_TEST_CASE( value_construct ) { ExceptionThrower::Reset(2); std::unique_ptr> a; ExceptionThrower value; BOOST_CHECK_THROW( a.reset(new Array(IPosition(1, 3), value)), std::exception ); BOOST_CHECK_EQUAL(ExceptionThrower::Count(), 1); BOOST_CHECK_EQUAL(ExceptionThrower::MaxCount(), 2); } BOOST_AUTO_TEST_CASE( range_construct ) { ExceptionThrower::Reset(4); std::unique_ptr> a; ExceptionThrower values[3]; BOOST_CHECK_THROW( a.reset(new Array(IPosition(1, 3), values[0])), std::exception ); BOOST_CHECK_EQUAL(ExceptionThrower::Count(), 3); BOOST_CHECK_EQUAL(ExceptionThrower::MaxCount(), 4); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tArrayIter.cc000066400000000000000000000245221476623553700210550ustar00rootroot00000000000000//# tArrayIter.cc: This program tests Array iteration //# Copyright (C) 1993,1994,1995,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include "../Array.h" #include "../ArrayIter.h" #include "../ArrayMath.h" #include "../ArrayLogical.h" //#include "../ArrayIO.h" #include "../IPosition.h" #include "../Vector.h" #include "../Matrix.h" #include "../Cube.h" #include using namespace casacore; BOOST_AUTO_TEST_SUITE(array_iter1) BOOST_AUTO_TEST_CASE( arraypositer_zero_dimension ) { IPosition shape{5, 5}; ArrayPositionIterator ai (shape, 0); IPosition index(2); for (int iloop = 0; ! ai.pastEnd(); ai.next(), iloop++ ) { index = ai.pos(); BOOST_CHECK_EQUAL(index (0), iloop%5); BOOST_CHECK_EQUAL(index (1), iloop/5); } } BOOST_AUTO_TEST_CASE( arraypositer_one_dimension ) { IPosition shape{5, 5}; Array arr (shape); ArrayPositionIterator ai (shape, 1); IPosition index (2); for (int iloop = 0; ! ai.pastEnd(); ai.next(), iloop++ ) { index = ai.pos(); arr (index) = 4 * iloop; BOOST_CHECK_EQUAL(index(0), 0); BOOST_CHECK_EQUAL(index(1), iloop); BOOST_CHECK_EQUAL(arr (index), iloop*4); } ai.set (IPosition(1,4)); BOOST_CHECK_EQUAL (ai.pos(), IPosition(2,0,4)); ai.set (IPosition(2,2,3)); BOOST_CHECK_EQUAL (ai.pos(), IPosition(2,0,3)); BOOST_CHECK(!ai.pastEnd()); BOOST_CHECK_EQUAL(ai.pos(), IPosition(2,0,3)); ai.next(); BOOST_CHECK(!ai.pastEnd()); BOOST_CHECK_EQUAL(ai.pos(), IPosition(2,0,4)); ai.next(); BOOST_CHECK(ai.pastEnd()); } BOOST_AUTO_TEST_CASE( arrayiter_one_dimension ) { IPosition shape{5, 5}; Array arr (shape); ArrayIterator arri (arr, 1); IPosition arriindex (1, 0); ArrayPositionIterator ai (shape, 1); for (int iloop = 0; ! ai.pastEnd(); ai.next(), iloop++ ) arr (ai.pos()) = 4 * iloop; for (int iloop = 0; ! arri.pastEnd(); arri.next(), iloop++ ) { BOOST_CHECK_EQUAL((arri.array ()) (arriindex), iloop*4); } } BOOST_AUTO_TEST_CASE( double_arraypositer ) { IPosition shape{5, 5}; ArrayPositionIterator ai (shape, 1); Array arr (shape); IPosition index (2); for (int iloop = 0; ! ai.pastEnd(); ai.next(), iloop++ ) { index = ai.pos(); arr (index) = double (4 * iloop) + 0.5; BOOST_CHECK_EQUAL(index (0), 0); BOOST_CHECK_EQUAL(index (1), iloop); BOOST_CHECK_CLOSE_FRACTION(arr (index), double (4 * iloop) + 0.5, 1e-6); } } BOOST_AUTO_TEST_CASE( double_arrayiter ) { IPosition shape{5, 5}; ArrayPositionIterator ai (shape, 1); Array arr (shape); for (int iloop = 0; ! ai.pastEnd(); ai.next(), iloop++ ) arr (ai.pos()) = double (4 * iloop) + 0.5; ArrayIterator arri (arr, 1); IPosition arriindex (1, 0); for (int iloop = 0; ! arri.pastEnd(); arri.next(), iloop++ ) { BOOST_CHECK_CLOSE_FRACTION((arri.array ()) (arriindex), double (4 * iloop) + 0.5, 1e-6); } } BOOST_AUTO_TEST_CASE( int_arrayiter ) { // Test that leading degenerate axes do not go away. // Check if each chunk matches. IPosition shape{2,3,4,5,6}; Array ai(shape); indgen(ai); // Test a regular iterator. ArrayIterator iter(ai, 2); BOOST_CHECK(iter.array().shape() == IPosition(2,shape(0),shape(1))); while (!iter.pastEnd()) { Array tmparr (ai(iter.pos(), iter.endPos()).nonDegenerate()); BOOST_CHECK(iter.array().data() == tmparr.data()); BOOST_CHECK(allEQ (iter.array(), tmparr)); iter.next(); } { // Test that a cursor as large as the array works ArrayIterator aiter(ai, 5); BOOST_CHECK(allEQ(aiter.array(), ai)); aiter.next(); BOOST_CHECK(aiter.pastEnd()); } { // Test iterator with arbitrary axes. ReadOnlyArrayIterator iter(ai, IPosition(3,4,1,3), false); BOOST_CHECK(iter.array().shape() == IPosition(2,shape(0),shape(2))); while (!iter.pastEnd()) { Array tmparr (ai(iter.pos(), iter.endPos()).nonDegenerate()); BOOST_CHECK(allEQ (iter.array(), tmparr)); iter.next(); } iter.reset(); // Check if iteration is in correct order. IPosition blc(5,0); IPosition trc(shape-1); for (int ax3=0; ax3 ai1(shape1); indgen(ai1); // Take a chunk from it. Array ai(ai1(IPosition(5,1,2,1,4,3), IPosition(5,7,12,13,16,13), IPosition(5,6,5,4,3,2))); IPosition shape(5, 2,3,4,5,6); BOOST_CHECK (ai.shape() == shape); { // Test a regular iterator. ArrayIterator iter(ai, 2); BOOST_CHECK(iter.array().shape() == IPosition(2,shape(0),shape(1))); while (!iter.pastEnd()) { Array tmparr (ai(iter.pos(), iter.endPos()).nonDegenerate()); BOOST_CHECK(iter.array().data() == tmparr.data()); BOOST_CHECK(allEQ (iter.array(), tmparr)); iter.next(); } } { // Test that a cursor as large as the array works ArrayIterator aiter(ai, 5); BOOST_CHECK(allEQ(aiter.array(), ai)); aiter.next(); BOOST_CHECK(aiter.pastEnd()); } { // Test iterator with arbitrary axes. ReadOnlyArrayIterator iter(ai, IPosition(3,4,1,3), false); BOOST_CHECK(iter.array().shape() == IPosition(2,shape(0),shape(2))); while (!iter.pastEnd()) { Array tmparr (ai(iter.pos(), iter.endPos()).nonDegenerate()); BOOST_CHECK(allEQ (iter.array(), tmparr)); iter.next(); } iter.reset(); // Check if iteration is in correct order. IPosition blc(5,0); IPosition trc(shape-1); for (int ax3=0; ax3 tmparr1 (ai(iter.pos(), iter.endPos()).nonDegenerate()); BOOST_CHECK(allEQ (iter.array(), tmparr1)); iter.next(); BOOST_CHECK(iter.pos() == IPosition(5,0,1,0,2,4)); Array tmparr2 (ai(iter.pos(), iter.endPos()).nonDegenerate()); BOOST_CHECK(allEQ (iter.array(), tmparr2)); } { ReadOnlyArrayIterator iter(ai, IPosition(3,4,1,3)); BOOST_CHECK(iter.array().shape() == IPosition(3,shape(1),shape(3), shape(4))); while (!iter.pastEnd()) { Array tmparr (ai(iter.pos(), iter.endPos()).nonDegenerate()); BOOST_CHECK(allEQ (iter.array(), tmparr)); iter.next(); } iter.reset(); // Check if iteration is in correct order. IPosition blc(5,0); IPosition trc(shape-1); for (int ax2=0; ax2 vec(0); { Array arr(IPosition(2,0,5)); ArrayIterator iter(arr, 1); int nstep=0; while (!iter.pastEnd()) { Array& darr = iter.array(); darr.assign_conforming( vec ); iter.next(); nstep++; } BOOST_CHECK (nstep==5); } { Array arr(IPosition(2,5,0)); ArrayIterator iter(arr, 1); int nstep=0; while (!iter.pastEnd()) { Array& darr = iter.array(); darr.assign_conforming( vec ); iter.next(); nstep++; } BOOST_CHECK (nstep==0); } { Array arr(IPosition(1,2)); ArrayIterator iter(arr, 1); int nstep=0; while (!iter.pastEnd()) { Array& darr = iter.array(); BOOST_CHECK (darr.shape() == IPosition(1,2)); iter.next(); nstep++; } BOOST_CHECK (nstep==1); } { Array arr(IPosition(1,0)); ArrayIterator iter(arr, 1); int nstep=0; while (!iter.pastEnd()) { iter.next(); nstep++; } BOOST_CHECK (nstep==0); } } BOOST_AUTO_TEST_CASE( virtual_iteration_function ) { Array arr(IPosition(2,4,5)); indgen (arr); std::unique_ptr iter = arr.makeIterator(1); int count = 0; while (!iter->pastEnd()) { Array& subarr = dynamic_cast&>(iter->getArray()); int ref[4] = {count*4, count*4+1, count*4+2, count*4+3}; BOOST_CHECK_EQUAL_COLLECTIONS(subarr.begin(), subarr.end(), std::begin(ref), std::end(ref)); iter->next(); ++count; } BOOST_CHECK_EQUAL(count, 5); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tArrayIter1.cc000066400000000000000000000246571476623553700211470ustar00rootroot00000000000000//# tArrayIter1.cc: This program test the Array-based iterators //# Copyright (C) 1993,1994,1995,1996,1998,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include "../Array.h" #include "../ArrayMath.h" #include "../ArrayLogical.h" #include "../Vector.h" #include "../Matrix.h" #include "../Cube.h" #include "../ArrayPosIter.h" #include "../ArrayIter.h" #include "../MatrixIter.h" #include "../VectorIter.h" #include using namespace casacore; BOOST_AUTO_TEST_SUITE(array_iter2) // Test iterating through a subset of a 5D array. void checkIter (const Array& array, const IPosition& blc, const IPosition& trc, const IPosition& inc) { Array a(array); Array a1 = a(blc, trc, inc); { IPosition st(blc); IPosition end(blc); end(0) = trc(0); IPosition shp = a1.shape(); shp.resize (1); ReadOnlyArrayIterator ai(a1,1); for (int i5=blc(4); i5<=trc(4); i5+=inc(4)) { for (int i4=blc(3); i4<=trc(3); i4+=inc(3)) { for (int i3=blc(2); i3<=trc(2); i3+=inc(2)) { for (int i2=blc(1); i2<=trc(1); i2+=inc(1)) { BOOST_CHECK(allEQ (ai.array(), a(st,end,inc).reform(shp))); ai.next(); st(1)+=inc(1); end(1)+=inc(1); } st(1) = blc(1); end(1) = blc(1); st(2)+=inc(2); end(2)+=inc(2); } st(2) = blc(2); end(2) = blc(2); st(3)+=inc(3); end(3)+=inc(3); } st(3) = blc(3); end(3) = blc(3); st(4)+=inc(4); end(4)+=inc(4); } BOOST_CHECK (ai.pastEnd()); } { IPosition st(blc); IPosition end(blc); end(0) = trc(0); end(1) = trc(1); IPosition shp = a1.shape(); shp.resize (2); ReadOnlyArrayIterator ai(a1,2); for (int i5=blc(4); i5<=trc(4); i5+=inc(4)) { for (int i4=blc(3); i4<=trc(3); i4+=inc(3)) { for (int i3=blc(2); i3<=trc(2); i3+=inc(2)) { BOOST_CHECK(allEQ (ai.array(), a(st,end,inc).reform(shp))); ai.next(); st(2)+=inc(2); end(2)+=inc(2); } st(2) = blc(2); end(2) = blc(2); st(3)+=inc(3); end(3)+=inc(3); } st(3) = blc(3); end(3) = blc(3); st(4)+=inc(4); end(4)+=inc(4); } BOOST_CHECK (ai.pastEnd()); } { IPosition st(blc); IPosition end(blc); end(0) = trc(0); end(1) = trc(1); end(2) = trc(2); IPosition shp = a1.shape(); shp.resize (3); ReadOnlyArrayIterator ai(a1,3); for (int i5=blc(4); i5<=trc(4); i5+=inc(4)) { for (int i4=blc(3); i4<=trc(3); i4+=inc(3)) { BOOST_CHECK(allEQ (ai.array(), a(st,end,inc).reform(shp))); ai.next(); st(3)+=inc(3); end(3)+=inc(3); } st(3) = blc(3); end(3) = blc(3); st(4)+=inc(4); end(4)+=inc(4); } BOOST_CHECK (ai.pastEnd()); } { IPosition st(blc); IPosition end(blc); end(0) = trc(0); end(1) = trc(1); end(2) = trc(2); end(3) = trc(3); IPosition shp = a1.shape(); shp.resize (4); ReadOnlyArrayIterator ai(a1,4); for (int i5=blc(4); i5<=trc(4); i5+=inc(4)) { BOOST_CHECK(allEQ (ai.array(), a(st,end,inc).reform(shp))); ai.next(); st(4)+=inc(4); end(4)+=inc(4); } BOOST_CHECK (ai.pastEnd()); } { IPosition st(blc); IPosition end(blc); end(0) = trc(0); end(1) = trc(1); end(2) = trc(2); end(3) = trc(3); end(4) = trc(4); IPosition shp = a1.shape(); shp.resize (5); ReadOnlyArrayIterator ai(a1,5); BOOST_CHECK(allEQ (ai.array(), a(st,end,inc).reform(shp))); ai.next(); BOOST_CHECK (ai.pastEnd()); } } BOOST_AUTO_TEST_CASE( iterate_5d_array ) { IPosition shape(3); shape=3; ArrayPositionIterator api(shape, 1); int count = 0; while (!api.pastEnd()) { count++; api.next(); } BOOST_CHECK(count == 9); Cube a(3,3,3); MatrixIterator ai(a); int i; for (i = 0; i < 3; i++) a.xyPlane(i) = i; count = 0; while (!ai.pastEnd()) { BOOST_CHECK(allEQ (ai.array(), count)); BOOST_CHECK(allEQ (ai.matrix(), count)); count++; ai.next(); } ReadOnlyMatrixIterator roai(a); for (i = 0; i < 3; i++) a.xyPlane(i) = i; count = 0; while (!roai.pastEnd()) { BOOST_CHECK(allEQ (roai.array(), count)); BOOST_CHECK(allEQ (roai.matrix(), count)); count++; roai.next(); } VectorIterator ai2(a); count = 0; while (!ai2.pastEnd()) { ai2.vector().set(count); count++; ai2.next(); } ai.origin(); BOOST_CHECK(ai.atStart()); count = 0; while (!ai.pastEnd()) { for (i=0; i<3; i++) { BOOST_CHECK(allEQ (ai.matrix().column(i), count)); count++; } ai.next(); } { // Test iterating by the same dimensionality as the array Vector theArray(100); theArray = 0; ArrayPositionIterator api(theArray.shape(), 1); size_t count = 0; while (! api.pastEnd()) { count++; api.next(); } BOOST_CHECK(count == 1); VectorIterator vi(theArray); count = 0; while (! vi.pastEnd()) { count++; vi.next(); } BOOST_CHECK(count == 1); } { Cube acube(16,16,16); indgen(acube); { Vector b(16); indgen(b); VectorIterator ai(acube); while (!ai.pastEnd()) { BOOST_CHECK(allEQ (ai.array(), b)); b += 16; ai.next(); } } // Test iterating through a subset (bottomleft) of the cube. { Cube a1 = acube(IPosition(3,0,0,0), IPosition(3,7,7,7)); Vector b(8); indgen(b); int count = 0; VectorIterator ai(a1); while (!ai.pastEnd()) { BOOST_CHECK(allEQ (ai.array(), b)); count++; b += 16; if (count%8 == 0) { b += 128; // next plane in cube subset } ai.next(); } } // Test iterating through a strided subset (bottomleft) of the cube. { Cube a1 = acube(IPosition(3,0,0,0), IPosition(3,7,7,7), IPosition(3,2,2,2)); Vector b(4); indgen(b, 0, 2); int count = 0; VectorIterator ai(a1); while (!ai.pastEnd()) { BOOST_CHECK(allEQ (ai.array(), b)); count++; b += 32; if (count%4 == 0) { b += 128 + 256; // next plane in cube subset } ai.next(); } } // Test iterating through a subset (middle) of the cube. { Cube a1 = acube(IPosition(3,5,4,3), IPosition(3,12,11,13)); Vector b(8); indgen(b, 3*256+4*16+5); int count = 0; VectorIterator ai(a1); while (!ai.pastEnd()) { BOOST_CHECK(allEQ (ai.array(), b)); count++; b += 16; if (count%8 == 0) { b += 128; // next plane in cube subset } ai.next(); } } // Test iterating through a strided subset (middle) of the cube. { Cube a1 = acube(IPosition(3,4,4,4), IPosition(3,11,11,11), IPosition(3,2,2,2)); Vector b(4); indgen(b, 4*256+4*16+4, 2); int count = 0; VectorIterator ai(a1); while (!ai.pastEnd()) { BOOST_CHECK(allEQ (ai.array(), b)); count++; b += 32; if (count%4 == 0) { b += 128 + 256; // next plane in cube subset } ai.next(); } } // Test iterating through a strided subset (middle) of the cube // with an axis of length 1. { Cube a1 = acube(IPosition(3,4,4,4), IPosition(3,11,4,11), IPosition(3,2,2,2)); Vector b(4); indgen(b, 4*256+4*16+4, 2); int count = 0; VectorIterator ai(a1); while (!ai.pastEnd()) { BOOST_CHECK(allEQ (ai.array(), b)); count++; b += 512; ai.next(); } } } // Now test a 5D array. { Array array(IPosition(5,8,10,12,14,16)); indgen(array); { Array b(IPosition(1,8)); indgen(b); ArrayIterator ai(array,1); while (!ai.pastEnd()) { BOOST_CHECK(allEQ (ai.array(), b)); b += 8; ai.next(); } } { Array b(IPosition(2,8,10)); indgen(b); ArrayIterator ai(array,2); while (!ai.pastEnd()) { BOOST_CHECK(allEQ (ai.array(), b)); b += 8*10; ai.next(); } } { Array b(IPosition(3,8,10,12)); indgen(b); ArrayIterator ai(array,3); while (!ai.pastEnd()) { BOOST_CHECK(allEQ (ai.array(), b)); b += 8*10*12; ai.next(); } } { Array b(IPosition(4,8,10,12,14)); indgen(b); ArrayIterator ai(array,4); while (!ai.pastEnd()) { BOOST_CHECK(allEQ (ai.array(), b)); b += 8*10*12*14; ai.next(); } } { Array b(IPosition(5,8,10,12,14,16)); indgen(b); ArrayIterator ai(array,5); while (!ai.pastEnd()) { BOOST_CHECK(allEQ (ai.array(), b)); ai.next(); } } // Test iterating through various subsets of the array. checkIter (array, IPosition(5,1,2,3,4,5), IPosition(5,5,7,9,11,13), IPosition(5,1,1,1,1,1)); checkIter (array, IPosition(5,1,2,3,4,5), IPosition(5,5,7,9,11,13), IPosition(5,1,1,1,1,1)); checkIter (array, IPosition(5,1,2,3,4,5), IPosition(5,5,7,9,11,13), IPosition(5,2,2,2,2,2)); checkIter (array, IPosition(5,1,2,3,4,5), IPosition(5,5,7,9,11,13), IPosition(5,2,1,3,1,2)); checkIter (array, IPosition(5,1,2,3,4,5), IPosition(5,5,7,3,11,13), IPosition(5,1,2,1,1,1)); checkIter (array, IPosition(5,1,2,3,4,5), IPosition(5,5,7,3,4,13), IPosition(5,1,1,1,1,1)); checkIter (array, IPosition(5,1,2,3,4,5), IPosition(5,5,7,3,11,5), IPosition(5,1,1,1,1,1)); checkIter (array, IPosition(5,1,2,3,4,5), IPosition(5,5,2,9,11,13), IPosition(5,1,1,1,1,1)); checkIter (array, IPosition(5,5,2,3,4,5), IPosition(5,5,2,9,11,13), IPosition(5,1,1,1,1,1)); } } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tArrayIteratorSTL.cc000066400000000000000000000074341476623553700223310ustar00rootroot00000000000000//# tArrayIteratorSTL.cc: Test program for the Array Iterator member class //# Copyright (C) 2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include "../Array.h" #include "../ArrayMath.h" #include #include #include using namespace casacore; BOOST_AUTO_TEST_SUITE(array_iterator_stl) void testSub (Array& arr1, const IPosition& blc, const IPosition& trc, const IPosition& inc) { Array arr = arr1(blc,trc,inc); Array arrs; arrs.assign_conforming(arr); std::vector vec(arr.begin(), arr.end()); Array::const_iterator iters = arrs.begin(); size_t i=0; Array::const_iterator enditer=arr.end(); for (Array::const_iterator iter=arr.begin(); iter!=enditer; ++iter) { BOOST_CHECK_EQUAL(*iter, *iters); BOOST_CHECK_EQUAL(vec[i], *iters); BOOST_CHECK_EQUAL(arrs.data()[i], *iters); iters++; i++; } BOOST_CHECK (i == arr.size()); BOOST_CHECK (std::distance(arr.begin(), arr.end()) == int(arr.size())); } BOOST_AUTO_TEST_CASE( iterator ) { Array arr(IPosition(3,4,5,6)); indgen(arr); { int i=0; Array::const_iterator enditer=arr.end(); for (Array::const_iterator iter=arr.begin(); iter!=enditer; ++iter) { BOOST_CHECK_EQUAL (*iter, i); ++i; } BOOST_CHECK_EQUAL (size_t(i), arr.nelements()); } testSub (arr, IPosition(3,0,0,0), IPosition(3,3,4,5), IPosition(3,1,1,1)); testSub (arr, IPosition(3,0,0,0), IPosition(3,3,4,5), IPosition(3,2,1,1)); testSub (arr, IPosition(3,0,0,0), IPosition(3,3,4,5), IPosition(3,1,1,2)); testSub (arr, IPosition(3,3,0,0), IPosition(3,3,4,5), IPosition(3,1,1,1)); testSub (arr, IPosition(3,3,4,1), IPosition(3,3,4,4), IPosition(3,1,1,1)); testSub (arr, IPosition(3,1,2,1), IPosition(3,3,3,4), IPosition(3,2,1,1)); } void checkEmpty(Array earr) { for (const int& i : earr) { (void) i; // prevent warning for unused i BOOST_CHECK(false); // should be empty } for (Array::iterator itera1=earr.begin(); itera1!=earr.end(); itera1++) { BOOST_CHECK(false); // should be empty } for (Array::iterator itera2=earr.begin(); itera2!=earr.end(); itera2++) { BOOST_CHECK(false); // should be empty } for (Array::contiter itera3=earr.cbegin(); itera3!=earr.cend(); itera3++) { BOOST_CHECK(false); // should be empty } for (Array::contiter itera4=earr.cbegin(); itera4!=earr.cend(); itera4++) { BOOST_CHECK(false); // should be empty } BOOST_CHECK(true); } BOOST_AUTO_TEST_CASE( uninitialized_array ) { Array uninitialized; checkEmpty(uninitialized); } BOOST_AUTO_TEST_CASE( empty_array ) { IPosition eshp; Array empty(eshp); checkEmpty(empty); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tArrayLogical.cc000066400000000000000000000074121476623553700215230ustar00rootroot00000000000000//# tArrayLogical.cc: Test program for Array logical operators //# Copyright (C) 1993,1994,1995,1996,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include "../IPosition.h" #include "../Array.h" #include "../ArrayMath.h" #include "../ArrayLogical.h" //#include "../ArrayIO.h" #include "../Vector.h" #include "../Matrix.h" #include "../Cube.h" #include "../ArrayError.h" #include "../LogiVector.h" #include using namespace casacore; BOOST_AUTO_TEST_SUITE(array_logical) template void Compare(const T& l, const std::string& str) { BOOST_CHECK_EQUAL(to_string(l), str); } BOOST_AUTO_TEST_CASE( array_position_iterator ) { Vector x(5, 1), y(5); LogicalVector b(5), c(5); indgen (y); BOOST_CHECK (allSame(x)); BOOST_CHECK (!allSame(y)); b.assign_conforming (x <= y); Compare(b, "[0, 1, 1, 1, 1]"); b.assign_conforming (x < y); Compare(b, "[0, 0, 1, 1, 1]"); b.assign_conforming (x >= y); Compare(b, "[1, 1, 0, 0, 0]"); b.assign_conforming (x > y); Compare(b, "[1, 0, 0, 0, 0]"); b.assign_conforming (x == y); Compare(b, "[0, 1, 0, 0, 0]"); b.assign_conforming (x != y); Compare(b, "[1, 0, 1, 1, 1]"); b.assign_conforming ((const Array &)y <= 1); Compare(b, "[1, 1, 0, 0, 0]"); b.assign_conforming ((const Array &)y < 1); Compare(b, "[1, 0, 0, 0, 0]"); b.assign_conforming ((const Array &)y >= 1); Compare(b, "[0, 1, 1, 1, 1]"); b.assign_conforming ((const Array &)y > 1); Compare(b, "[0, 0, 1, 1, 1]"); b.assign_conforming ((const Array &)y == 1); Compare(b, "[0, 1, 0, 0, 0]"); b.assign_conforming ((const Array &)y != 1); Compare(b, "[1, 0, 1, 1, 1]"); b.assign_conforming (1 <= (const Array &)y); Compare(b, "[0, 1, 1, 1, 1]"); b.assign_conforming (1 < (const Array &)y); Compare(b, "[0, 0, 1, 1, 1]"); b.assign_conforming (1 >= (const Array &)y); Compare(b, "[1, 1, 0, 0, 0]"); b.assign_conforming (1 > (const Array &)y); Compare(b, "[1, 0, 0, 0, 0]"); b.assign_conforming (1 == (const Array &)y); Compare(b, "[0, 1, 0, 0, 0]"); b.assign_conforming (1 != (const Array &)y); Compare(b, "[1, 0, 1, 1, 1]"); b.assign_conforming (! ((const Array &)y >= 3)); c.assign_conforming ((const Array &)y < 3); Compare(b, "[1, 1, 1, 0, 0]"); Compare(c, "[1, 1, 1, 0, 0]"); Vector x1(2, 10000), x2(2); x2(0) = 10001; x2(1) = 10002; Compare(near(x1, x2, 0.99e-4), "[0, 0]"); Compare(near(x1, x2, 1.01e-4), "[1, 0]"); Compare(near(x1, x2, 2.01e-4), "[1, 1]"); Compare(nearAbs(x1, x2, 0.99), "[0, 0]"); Compare(nearAbs(x1, x2, 1.01), "[1, 0]"); Compare(nearAbs(x1, x2, 2.01), "[1, 1]"); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tArrayMath.cc000066400000000000000000000550361476623553700210470ustar00rootroot00000000000000//# tArrayMath2.cc: This program tests the ArrayMath class //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include "../Cube.h" #include "../ArrayMath.h" #include "../ArrayLogical.h" //#include "../ArrayIO.h" #include "../ElementFunctions.h" #include #include using namespace casacore; using namespace std; BOOST_AUTO_TEST_SUITE(array_math) template T square(T x) { return x*x; } template T cube(T x) { return x*x*x; } //template //T amplitude(T x) { return std::abs(x); } #define TestBinary(OPER,NAME,T,U) \ void NAME()\ {\ Array a(IPosition(3,4,5,6));\ Array b(IPosition(3,4,5,6));\ Array res;\ Array e1(IPosition(3,4,5,6));\ Array e2(IPosition(3,4,5,6));\ Array e3(IPosition(3,4,5,6));\ U s1 = 3;\ T s2 = -5;\ T* ap = a.data();\ U* bp = b.data();\ T* e1p = e1.data();\ T* e2p = e2.data();\ T* e3p = e3.data();\ for (size_t i=0; i sa(a(start,end));\ Array sb(b(start,end));\ res.resize();\ res.assign_conforming( sa OPER sb);\ BOOST_CHECK (allEQ(res, e1(start,end)));\ res.assign_conforming( sa OPER s1);\ BOOST_CHECK (allEQ(res, e2(start,end)));\ res.assign_conforming( s2 OPER sb);\ BOOST_CHECK (allEQ(res, e3(start,end)));\ res.assign_conforming( sa OPER sb);\ sa OPER##= sb;\ BOOST_CHECK (allEQ(sa, res));\ res.assign_conforming( sa OPER s1);\ sa OPER##=s1;\ BOOST_CHECK (allEQ(sa, res)); \ } #define TestUnary(OPER, NAME, T)\ void NAME()\ {\ Array a(IPosition(3,4,5,6));\ Array e(IPosition(3,4,5,6));\ T* ap = a.data();\ T* ep = e.data();\ for (size_t i=0; i ((i+1)/120.); \ ep[i] = OPER ap[i];\ }\ BOOST_CHECK (allEQ(OPER a, e));\ IPosition start(3,0,1,2);\ IPosition end(3,2,3,4);\ Array sa(a(start,end));\ BOOST_CHECK (allEQ(OPER sa, e(start,end))); \ } #define TestReduce(FUNC, OPER, NAME, T) \ void NAME()\ {\ Array a(IPosition(3,4,5,6));\ int res=0;\ int* ap = a.data();\ for (size_t i=0; i sa(a(start,end));\ Array sb;\ sb.assign_conforming( sa ); /* copy to make contiguous (that's tested above) */\ BOOST_CHECK (FUNC(sa) == FUNC(sb));\ } #define TestMinMax(FUNC, NAME, T) \ void NAME()\ {\ Array a(IPosition(3,4,5,6));\ int res=10;\ int* ap = a.data();\ for (size_t i=0; i sa(a(start,end));\ Array sb;\ sb.assign_conforming( sa ); /* copy to make contiguous (that's tested above) */\ BOOST_CHECK (FUNC(sa) == FUNC(sb));\ } void testMeanFloat() { Array a(IPosition(3,4,5,6)); float res1=0; float res2=0; float* ap = a.data(); for (size_t i=0; i sa(a(start,end)); Array sb; sb.assign_conforming(sa); /* copy to make contiguous (that's tested above) */ BOOST_CHECK_CLOSE(mean(sa), mean(sb), 1e-6); BOOST_CHECK_CLOSE(variance(sa), variance(sb), 1e-6); BOOST_CHECK_CLOSE(stddev(sa), stddev(sb), 1e-6); BOOST_CHECK_CLOSE(avdev(sa), avdev(sb), 1e-6); BOOST_CHECK_CLOSE(rms(sa), rms(sb), 1e-6); } void check_close(const std::complex& a, const std::complex& b) { BOOST_CHECK_CLOSE_FRACTION(a.real(), b.real(), 1e-5); BOOST_CHECK_CLOSE_FRACTION(a.imag(), b.imag(), 1e-5); } void testMeanComplex() { Array> a(IPosition(3,4,5,6)); std::complex res1; std::complex res2; std::complex* ap = a.data(); for (int i=0; i(i+1, 2*i-3); res1 += ap[i]; res2 += ap[i] * ap[i]; } std::complex m = mean(a); check_close(m, res1/float(a.size())); check_close(rms(a), std::sqrt(res2/float(a.size()))); res1 = std::complex(); res2 = std::complex(); for (size_t i=0; i v = variance(a); BOOST_CHECK_EQUAL (v.imag(), 0); BOOST_CHECK_CLOSE_FRACTION( v.real(), variance(real(a)) + variance(imag(a)), 1e-5); std::complex pv = pvariance(a); BOOST_CHECK (pv.imag() == 0); BOOST_CHECK_CLOSE_FRACTION( pv.real(), pvariance(real(a)) + pvariance(imag(a)), 1e-5); std::complex av = avdev(a); BOOST_CHECK (av.imag() == 0); check_close(v, res1/float(a.size()-1)); check_close(pv, res1/float(a.size())); check_close(variance(a, m), v); check_close(pvariance(a, m, 1), v); check_close(pvariance(a, m, 0), pv); check_close(pvariance(a, m), pv); check_close(av, res2/float(a.size())); check_close(avdev(a, m), av); check_close(stddev(a), std::sqrt(v)); check_close(pstddev(a), std::sqrt(pv)); check_close(stddev(a, m), std::sqrt(v)); check_close(pstddev(a, m), std::sqrt(pv)); check_close(pstddev(a, m, 0), std::sqrt(pv)); check_close(pstddev(a, m, 1), std::sqrt(v)); IPosition start(3,0,1,2); IPosition end(3,2,3,4); Array> sa(a(start,end)); Array> sb; sb.assign_conforming( sa ); /* copy to make contiguous (that's tested above) */ check_close(mean(sa), mean(sb)); check_close(variance(sa), variance(sb)); check_close(stddev(sa), stddev(sb)); check_close(avdev(sa), avdev(sb)); check_close(rms(sa), rms(sb)); } #define TestFunc1(FUNC, NAME, T, TOL) \ void NAME()\ {\ Array a(IPosition(3,4,5,6));\ Array e(IPosition(3,4,5,6));\ T* ap = a.data();\ T* ep = e.data();\ for (size_t i=0; i sa(a(start,end));\ BOOST_CHECK (allNear(FUNC(sa), e(start,end), TOL));\ } #define TestFunc2(FUNC,NAME,T) \ void NAME()\ {\ Array a(IPosition(3,4,5,6));\ Array b(IPosition(3,4,5,6));\ Array res;\ Array e1(IPosition(3,4,5,6));\ Array e2(IPosition(3,4,5,6));\ Array e3(IPosition(3,4,5,6));\ T s1 = 3;\ T s2 = -5;\ T* ap = a.data();\ T* bp = b.data();\ T* e1p = e1.data();\ T* e2p = e2.data();\ T* e3p = e3.data();\ for (size_t i=0; i sa(a(start,end));\ Array sb(b(start,end));\ res.resize();\ res.assign_conforming( FUNC(sa, sb) ); \ BOOST_CHECK (allEQ(res, e1(start,end)));\ res.assign_conforming( FUNC(sb, s1) ); \ BOOST_CHECK (allEQ(res, e2(start,end)));\ res.assign_conforming( FUNC(s2, sa) ); \ BOOST_CHECK (allEQ(res, e3(start,end)));\ } #define TestComplex(FUNC, SFUNC, NAME, T, U)\ void NAME()\ {\ Array a(IPosition(3,4,5,6));\ Array e(IPosition(3,4,5,6));\ T* ap = a.data();\ U* ep = e.data();\ for (size_t i=0; i z = FUNC(a); \ for (size_t i=0; i sa(a(start,end));\ BOOST_CHECK (allNear(FUNC(sa), e(start,end), 1e-13));\ Array res(a.shape());\ FUNC(res,a);\ BOOST_CHECK (allNear(res, e, 1e-13)); \ e=0;\ Array se(e(start,end));\ Array sres(res(start,end));\ se.assign_conforming( sres );\ res=0; \ FUNC(sres,sa);\ BOOST_CHECK (allNear(res, e, 1e-13)); \ } template void testSetReal() { Array a(IPosition(3,4,5,6)); Array e(IPosition(3,4,5,6)); T* ap = a.data(); T* ep = e.data(); for (size_t i=0; i b; b.assign_conforming( a ); setReal(b, real(a+T(10,5))); BOOST_CHECK (allNear(b, e, 1e-13)); IPosition start(3,0,1,2); IPosition end(3,2,3,4); Array sa(a(start,end)); setReal(sa, real(sa+T(10,5))); BOOST_CHECK (allNear(sa, e(start,end), 1e-13)); } template void testSetImag() { Array a(IPosition(3,4,5,6)); Array e(IPosition(3,4,5,6)); T* ap = a.data(); T* ep = e.data(); for (size_t i=0; i b; b.assign_conforming( a ); setImag(b, imag(a+T(10,5))); BOOST_CHECK (allNear(b, e, 1e-13)); IPosition start(3,0,1,2); IPosition end(3,2,3,4); Array sa(a(start,end)); setImag(sa, imag(sa+T(10,5))); BOOST_CHECK (allNear(sa, e(start,end), 1e-13)); } template void testMakeComplex() { Array a(IPosition(3,4,5,6)); Array b(IPosition(3,4,5,6)); Array e(IPosition(3,4,5,6)); T* ap = a.data(); T* bp = b.data(); U* ep = e.data(); for (size_t i=0; i a(IPosition(3,4,5,6)); indgen (a); a.data()[11] = -3; a.data()[66] = 1000; int minval, maxval; IPosition minpos, maxpos; // Unmasked minmax. minMax(minval, maxval, minpos, maxpos, a); BOOST_CHECK (minval == -3); BOOST_CHECK (maxval == 1000); BOOST_CHECK (minpos == IPosition(3,3,2,0)); BOOST_CHECK (maxpos == IPosition(3,2,1,3)); IPosition start(3,0,1,2); IPosition end(3,2,3,4); Array sa(a(start, end)); minMax(minval, maxval, minpos, maxpos, sa); BOOST_CHECK (minval == 44); BOOST_CHECK (maxval == 1000); BOOST_CHECK (minpos == IPosition(3,0,0,0)); BOOST_CHECK (maxpos == IPosition(3,2,0,1)); // Masked minmax without any flag set. Array mask(a.shape()); Array smask(mask(start, end)); mask = true; minMax(minval, maxval, minpos, maxpos, a, mask); BOOST_CHECK (minval == -3); BOOST_CHECK (maxval == 1000); BOOST_CHECK (minpos == IPosition(3,3,2,0)); BOOST_CHECK (maxpos == IPosition(3,2,1,3)); // Try without a valid element. bool ok=false; try { minMax(minval, maxval, minpos, maxpos, a, mask, false); } catch (std::exception&) { ok = true; } BOOST_CHECK (ok); // Masked minmax with some flags set. mask.data()[11] = false; mask.data()[66] = false; a.data()[0] = 10; minMax(minval, maxval, minpos, maxpos, a, mask); BOOST_CHECK (minval == 1); BOOST_CHECK (maxval == 119); BOOST_CHECK (minpos == IPosition(3,1,0,0)); BOOST_CHECK (maxpos == IPosition(3,3,4,5)); // Non-contiguous subset. minMax(minval, maxval, minpos, maxpos, sa, smask); BOOST_CHECK (minval == 44); BOOST_CHECK (maxval == 94); BOOST_CHECK (minpos == IPosition(3,0,0,0)); BOOST_CHECK (maxpos == IPosition(3,2,2,2)); // Masked minmax with some opposite mask. minMax(minval, maxval, minpos, maxpos, a, mask, false); BOOST_CHECK (minval == -3); BOOST_CHECK (maxval == 1000); BOOST_CHECK (minpos == IPosition(3,3,2,0)); BOOST_CHECK (maxpos == IPosition(3,2,1,3)); // Non-contiguous subset with a single valid element. minMax(minval, maxval, minpos, maxpos, sa, smask, false); BOOST_CHECK (minval == 1000); BOOST_CHECK (maxval == 1000); BOOST_CHECK (minpos == IPosition(3,2,0,1)); BOOST_CHECK (maxpos == IPosition(3,2,0,1)); // Test weighted minmax. Array weight(a.shape()); Array sweight(weight(start, end)); weight = 1; weight.data()[11] = 0; weight.data()[66] = 0; minMaxMasked(minval, maxval, minpos, maxpos, a, weight); BOOST_CHECK (minval == 0); BOOST_CHECK (maxval == 119); BOOST_CHECK (minpos == IPosition(3,3,2,0)); BOOST_CHECK (maxpos == IPosition(3,3,4,5)); // Non-contiguous subset. minMaxMasked(minval, maxval, minpos, maxpos, sa, sweight); BOOST_CHECK (minval == 0); BOOST_CHECK (maxval == 94); BOOST_CHECK (minpos == IPosition(3,2,0,1)); BOOST_CHECK (maxpos == IPosition(3,2,2,2)); } void testExpand() { // Test linear expansion. Cube mat1(IPosition(3,2,3,1)); indgen(mat1); Cube mat2(IPosition(3,8,6,2)); for (int i=0; i<2; ++i) { for (int j=0; j<6; ++j) { for (int k=0; k<8; ++k) { mat2(k,j,i) = mat1(k/4, j/2, 0); } } } Array out(IPosition(3,8,6,2)); expandArray (out, mat1); BOOST_CHECK (allEQ (out, mat2)); // Test alternate expansion. for (int i=0; i<2; ++i) { for (int j=0; j<6; ++j) { for (int k=0; k<8; ++k) { mat2(k,j,i) = mat1(k%2, j%3, 0); } } } out=-1; expandArray (out, mat1, IPosition(3,1)); BOOST_CHECK (allEQ (out, mat2)); // Test mixed expansion. for (int i=0; i<2; ++i) { for (int j=0; j<6; ++j) { for (int k=0; k<8; ++k) { mat2(k,j,i) = mat1(k/4, j%3, 0); } } } out=-1; expandArray (out, mat1, IPosition(3,0,1,1)); BOOST_CHECK (allEQ (out, mat2)); // Test another mixed expansion. for (int i=0; i<2; ++i) { for (int j=0; j<6; ++j) { for (int k=0; k<8; ++k) { mat2(k,j,i) = mat1(k%2, j/2, 0); } } } out=-1; expandArray (out, mat1, IPosition(3,1,0,0)); BOOST_CHECK (allEQ (out, mat2)); // Test input ndim > output ndim. { Array out1(IPosition(1,8)); expandArray (out1, mat1, IPosition(3,1,0,0)); VectorIterator iter(mat2); BOOST_CHECK (allEQ (out1, iter.vector())); } // Test output ndim > output ndim. { Array out1(IPosition(5,8,6,2,3,2)); expandArray (out1, mat1, IPosition(1,1)); ArrayIterator iter(out1, 3); int niter=0; while (! iter.pastEnd()) { BOOST_CHECK (allEQ (iter.array(), mat2)); iter.next(); niter++; } BOOST_CHECK (niter==6); } } // Instantiate the macro-ed functions. TestBinary(+, testPlusInt, int, int) TestBinary(-, testMinusInt, int, int) TestBinary(*, testTimesInt, int, int) TestBinary(/, testDivideInt, int, int) TestBinary(%, testModuloInt, int, int) TestBinary(|, testOrInt, int, int) TestBinary(&, testAndInt, int, int) TestBinary(^, testXorInt, int, int) TestBinary(*, testTimesDComplex, std::complex, std::complex) TestBinary(*, testTimesComplex, std::complex, std::complex) TestBinary(/, testDivideCF, std::complex, float) TestBinary(*, testTimesCF, std::complex, float) TestBinary(/, testDivideCD, std::complex, double) TestBinary(*, testTimesCD, std::complex, double) TestUnary(+, testUPlusInt, int) TestUnary(-, testUMinusInt, int) TestUnary(~, testUNegateInt, int) TestReduce(sum, +=, testSumInt, int) TestReduce(product, *=, testProductInt, int) TestMinMax(min, testMinInt, int) TestMinMax(max, testMaxInt, int) TestFunc1(sin, testSinDouble, double, 1e-13) TestFunc1(sinh, testSinhDouble, double, 1e-13) TestFunc1(cos, testCosfloat, float, 1e-6) TestFunc1(cosh, testCoshfloat, float, 1e-6) TestFunc1(square, testSqrDouble, double, 1e-13) TestFunc1(cube, testCubeDouble, double, 1e-13) TestFunc1(sqrt, testSqrtDouble, double, 1e-13) TestFunc1(exp, testExpDouble, double, 1e-13) TestFunc1(log, testLogDouble, double, 1e-13) TestFunc1(log10, testLog10Double, double, 1e-13) TestFunc1(sin, testSinComplex, std::complex, 1e-6) TestFunc1(sinh, testSinhComplex, std::complex, 1e-6) TestFunc1(cos, testCosComplex, std::complex, 1e-13) TestFunc1(cosh, testCoshComplex, std::complex, 1e-13) TestFunc1(square, testSqrComplex, std::complex, 1e-6) TestFunc1(cube, testCubeComplex, std::complex, 1e-6) TestFunc1(sqrt, testSqrtComplex, std::complex, 1e-6) TestFunc1(exp, testExpComplex, std::complex, 1e-6) TestFunc1(log, testLogComplex, std::complex, 1e-6) TestFunc1(log10, testLog10Complex, std::complex, 1e-6) TestFunc1(tan, testTanDouble, double, 1e-13) TestFunc1(tanh, testTanhDouble, double, 1e-13) TestFunc1(asin, testAsinDouble, double, 1e-13) TestFunc1(acos, testAcosDouble, double, 1e-13) TestFunc1(atan, testAtanDouble, double, 1e-13) TestFunc1(ceil, testCeilDouble, double, 1e-13) TestFunc1(fabs, testFabsDouble, double, 1e-13) TestFunc1(abs, testAbsDouble, double, 1e-13) TestFunc1(floor, testFloorDouble, double, 1e-13) TestFunc2(atan2, testAtan2Double, double) TestFunc2(pow, testPowDouble, double) TestFunc2(fmod, testFmodDouble, double) TestFunc2(min, testMin2Double, double) TestFunc2(max, testMax2Int, int) TestComplex(amplitude, abs, testComplexAbs, std::complex, float) TestComplex(phase, arg, testComplexPhase, std::complex, float) TestComplex(real, real, testComplexReal, std::complex, float) TestComplex(imag, imag, testComplexImag, std::complex, float) TestComplex(conj, conj, testComplexConj, std::complex, std::complex) TestComplex(amplitude, abs, testDComplexAbs, std::complex, double) TestComplex(phase, arg, testDComplexPhase, std::complex, double) TestComplex(real, real, testDComplexReal, std::complex, double) TestComplex(imag, imag, testDComplexImag, std::complex, double) TestComplex(conj, conj, testDComplexConj, std::complex, std::complex) BOOST_AUTO_TEST_CASE( all ) { testPlusInt(); testMinusInt(); testTimesInt(); testDivideInt(); testModuloInt(); testOrInt(); testAndInt(); testXorInt(); testTimesDComplex(); testTimesComplex(); testDivideCF(); testTimesCF(); testDivideCD(); testTimesCD(); testUPlusInt(); testUMinusInt(); testUNegateInt(); testSumInt(); testProductInt(); testMinInt(); testMaxInt(); testMeanFloat(); testMeanComplex(); testSinDouble(); testSinhDouble(); testCosfloat(); testCoshfloat(); testSqrDouble(); testSqrtDouble(); testExpDouble(); testLogDouble(); testLog10Double(); testSinComplex(); testSinhComplex(); testCosComplex(); testCoshComplex(); testSqrComplex(); testSqrtComplex(); testExpComplex(); testLogComplex(); testLog10Complex(); testTanDouble(); testTanhDouble(); testAsinDouble(); testAcosDouble(); testAtanDouble(); testCeilDouble(); testFabsDouble(); testAbsDouble(); testFloorDouble(); testAtan2Double(); testPowDouble(); testFmodDouble(); testMin2Double(); testMax2Int(); testComplexAbs(); testComplexPhase(); testComplexReal(); testComplexImag(); testComplexConj(); testDComplexAbs(); testDComplexPhase(); testDComplexReal(); testDComplexImag(); testDComplexConj(); testSetReal,float>(); testSetReal,double>(); testSetImag,float>(); testSetImag,double>(); testMakeComplex>(); testMakeComplex>(); testMinMax1(); testExpand(); } BOOST_AUTO_TEST_CASE( convert_array ) { Array a(IPosition{3, 2}, 7); Array b(IPosition{3, 2}, 1), c(IPosition{3, 2}, 1); std::vector ref(6, 1); convertArray(a, b); BOOST_CHECK_EQUAL_COLLECTIONS(b.begin(), b.end(), ref.begin(), ref.end()); convertArray(a, c); BOOST_CHECK_EQUAL_COLLECTIONS(b.begin(), b.end(), ref.begin(), ref.end()); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tArrayMathTransform.cc000066400000000000000000000124601476623553700227350ustar00rootroot00000000000000//# tArrayMath.cc: This program tests the itrators in the ArrayMath class //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include "../Array.h" #include "../ArrayMath.h" #include "../ArrayLogical.h" #include #include using namespace casacore; BOOST_AUTO_TEST_SUITE(array_math_transform) BOOST_AUTO_TEST_CASE(test) { IPosition shape(3,10,11,12); Array arr1(shape); Array arr2(shape); Array exp1(shape); Array exp2(shape); Array expa(shape); Array res(shape); indgen (arr1, -100); indgen (arr2); for (size_t i=0; i()); BOOST_CHECK (allEQ (res, exp1)); arrayContTransform (arr1, 20, res, std::plus()); BOOST_CHECK (allEQ (res, exp2)); arrayContTransform (20, arr1, res, std::plus()); BOOST_CHECK (allEQ (res, exp2)); arrayContTransform (arr1, res, [](int a){return std::abs(a);}); BOOST_CHECK (allEQ (res, expa)); arrayTransform (arr1, arr2, res, std::plus()); BOOST_CHECK (allEQ (res, exp1)); arrayTransform (arr1, 20, res, std::plus()); BOOST_CHECK (allEQ (res, exp2)); arrayTransform (20, arr1, res, std::plus()); BOOST_CHECK (allEQ (res, exp2)); arrayTransform (arr1, res, [](int a){return std::abs(a);}); BOOST_CHECK (allEQ (res, expa)); BOOST_CHECK (allEQ (exp1, arrayTransformResult (arr1, arr2, std::plus()))); BOOST_CHECK (allEQ (exp2, arrayTransformResult (arr1, 20, std::plus()))); BOOST_CHECK (allEQ (exp2, arrayTransformResult (20, arr1, std::plus()))); BOOST_CHECK (allEQ (expa, arrayTransformResult (arr1, [](int a){return std::abs(a);}))); res.assign_conforming(arr1); arrayTransformInPlace (res, arr2, std::plus()); BOOST_CHECK (allEQ (res, exp1)); res.assign_conforming(arr1); arrayTransformInPlace (res, 20, std::plus()); BOOST_CHECK (allEQ (res, exp2)); res.assign_conforming(arr1); arrayTransformInPlace (res, [](int a){return std::abs(a);}); BOOST_CHECK (allEQ (res, expa)); // Now test non-contiguous arrays. Slicer slicer(IPosition(3,1,2,3), IPosition(3,7,8,9), IPosition(3,2), Slicer::endIsLast); Array arr1sl (arr1(slicer)); Array arr2sl (arr2(slicer)); Array ressl (res(slicer)); Array exp1sl (exp1(slicer)); Array exp2sl (exp2(slicer)); Array expasl (expa(slicer)); res.assign_conforming(exp1); arrayTransform (arr1sl, arr2sl, ressl, std::plus()); BOOST_CHECK (allEQ (res, exp1)); res.assign_conforming(exp2); arrayTransform (arr1sl, 20, ressl, std::plus()); BOOST_CHECK (allEQ (res, exp2)); arrayTransform (20, arr1sl, ressl, std::plus()); BOOST_CHECK (allEQ (res, exp2)); res.assign_conforming(expa); arrayTransform (arr1sl, ressl, [](int a){return std::abs(a);}); BOOST_CHECK (allEQ (res, expa)); BOOST_CHECK (allEQ (exp1sl, arrayTransformResult (arr1sl, arr2sl, std::plus()))); BOOST_CHECK (allEQ (exp2sl, arrayTransformResult (arr1sl, 20, std::plus()))); BOOST_CHECK (allEQ (exp2sl, arrayTransformResult (20, arr1sl, std::plus()))); BOOST_CHECK (allEQ (expasl, arrayTransformResult (arr1sl, [](int a){return std::abs(a);}))); res.assign_conforming(exp1); ressl.assign_conforming(arr1sl); arrayTransformInPlace (ressl, arr2sl, std::plus()); BOOST_CHECK (allEQ (res, exp1)); res.assign_conforming(exp2); ressl.assign_conforming(arr1sl); arrayTransformInPlace (ressl, 20, std::plus()); BOOST_CHECK (allEQ (res, exp2)); res.assign_conforming(expa); ressl.assign_conforming(arr1sl); arrayTransformInPlace (res, [](int a){return std::abs(a);}); BOOST_CHECK (allEQ (res, expa)); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tArrayOperations.cc000066400000000000000000000217241476623553700222760ustar00rootroot00000000000000#include #include #include #include "../Array.h" #include "../ArrayMath.h" #include "../ArrayLogical.h" #include "../Matrix.h" using namespace casacore; BOOST_AUTO_TEST_SUITE(array_operations) // Math functions BOOST_AUTO_TEST_CASE( math_floor ) { Vector x({0.1, 0.2, 0.3, 0.4, 0.5}); Vector y = floor(x); for (int i = 0; i < 5; i++) BOOST_CHECK_EQUAL(y(i), 0.0); } BOOST_AUTO_TEST_CASE( math_pow ) { Vector x({0.1, 0.2, 0.3, 0.4, 0.5}); Vector z = pow(x, 1.0); BOOST_CHECK(allNear (z, x, 1.0e-10)); z.assign_conforming(pow(x, 2.0)); BOOST_CHECK(allNear (z, x*x, 1.0e-10)); Vector y(5, 0.0); z.assign_conforming(pow(x, y)); for (int i = 0; i < 5; i++) BOOST_CHECK_CLOSE_FRACTION(z(i), 1.0, 1e-6); } BOOST_AUTO_TEST_CASE( math_minmax1 ) { Vector z(5, 1.0); BOOST_CHECK_EQUAL(min(z), 1.0); BOOST_CHECK_EQUAL(max(z), 1.0); z(4) = -1.0; z(3) = 22.0; BOOST_CHECK_EQUAL(min(z), -1.0); BOOST_CHECK_EQUAL(max(z), 22.0); } BOOST_AUTO_TEST_CASE( math_minmax2 ) { Vector vi1(5), vi2(5), vi3; indgen(vi1); vi2 = 0; vi2(3) = -3; vi3.assign_conforming( casacore::min(vi1, vi2) ); BOOST_CHECK_EQUAL(vi3(0), 0); BOOST_CHECK_EQUAL(vi3(1), 0); BOOST_CHECK_EQUAL(vi3(2), 0); BOOST_CHECK_EQUAL(vi3(3), -3); BOOST_CHECK_EQUAL(vi3(4), 0); vi2(3) = 9; vi3.assign_conforming( casacore::max(-vi1, vi2) ); BOOST_CHECK_EQUAL(vi3(0), 0); BOOST_CHECK_EQUAL(vi3(1), 0); BOOST_CHECK_EQUAL(vi3(2), 0); BOOST_CHECK_EQUAL(vi3(3), 9); BOOST_CHECK_EQUAL(vi3(4), 0); } BOOST_AUTO_TEST_CASE( math_masked_minmax ) { Matrix a(10u, 3u); Matrix mask(10u, 3u); float val; for (int j=0; j<3; j++) { for (int i=0; i<10; i++) { a(i, j) = sin( (10*j + i) * 0.6); val = a(i, j); mask(i, j) = (val > 0) && (val < 0.5); } } for (int i=0; i<10; i++) { mask(i, 0)= false; } float min,max; IPosition minPos(2),maxPos(2); minMax (min, max, minPos, maxPos, a); BOOST_CHECK_EQUAL(minPos, IPosition(2, 8, 0)); BOOST_CHECK_EQUAL(maxPos, IPosition(2, 3, 1)); BOOST_CHECK_EQUAL(min, a(minPos)); BOOST_CHECK_EQUAL(max, a(maxPos)); minMax(min, max,a); BOOST_CHECK_EQUAL(min, a(minPos)); BOOST_CHECK_EQUAL(max, a(maxPos)); minMax (min, max, minPos, maxPos, a, mask); BOOST_CHECK_EQUAL(minPos, IPosition(2, 1, 2)); BOOST_CHECK_EQUAL(maxPos, IPosition(2, 5, 1)); BOOST_CHECK_EQUAL(min, a(minPos)); BOOST_CHECK_EQUAL(max, a(maxPos)); } // Testing Vector math and logicals BOOST_AUTO_TEST_CASE( logical ) { Vector x(5, 1), y(5, 2); BOOST_CHECK(allEQ (x, 1)); BOOST_CHECK(allEQ (y, 2)); BOOST_CHECK(allLE (x, y)); BOOST_CHECK(allGE (y, x)); BOOST_CHECK(allNE (x, 2)); BOOST_CHECK(allNE (x, y)); BOOST_CHECK(allEQ (x, x)); BOOST_CHECK(allLT (x, y)); BOOST_CHECK(allGT (y, x)); } BOOST_AUTO_TEST_CASE( add_scalar ) { Vector x(5, 1), y(5, 2); x += 1; BOOST_CHECK(allEQ (x, y)); } BOOST_AUTO_TEST_CASE( add_vector ) { Vector x(5, 2), y(5, 2); x += y; BOOST_CHECK(allEQ (x, 2*y)); } BOOST_AUTO_TEST_CASE( mul_scalar ) { Vector x(5, 4); x.assign_conforming( -1 * x ); BOOST_CHECK(allEQ (x, -4)); } BOOST_AUTO_TEST_CASE( vector_indgen ) { Vector x(5), y(5); indgen(x); indgen(y, 1); BOOST_CHECK(allEQ (x - y, -1)); for (int i = 0; i < 5; i++) BOOST_CHECK_EQUAL(x(i), i); } BOOST_AUTO_TEST_CASE( vector_operations ) { Vector x(5), y(5); indgen(x); indgen(y, 1); BOOST_CHECK(sum(x) == 10); BOOST_CHECK(product(y) == 120); BOOST_CHECK(mean(y) == median(y) && mean(y) == 3); } BOOST_AUTO_TEST_CASE( vector_comparison ) { Vector x(5), y(5); indgen(x); indgen(y, 1); BOOST_CHECK(anyLE(x, y) && anyLE(x, y-1) && !anyLE(x, y-2)); BOOST_CHECK(anyLT(x, y) && !anyLT(x, y-1) && !anyLT (x, y-2)); BOOST_CHECK(!anyGE(x, y) && anyGE (x, y-1) && anyGE (x, y-2)); BOOST_CHECK(!anyGT(x, y) && !anyGT (x, y-1) && anyGT (x, y-2)); BOOST_CHECK(!anyEQ(x, y) && anyEQ(x, y-1) && !anyEQ(x, y-2)); BOOST_CHECK(anyNE(x, y) && !anyNE (x, y-1) && anyNE(x, y-2)); BOOST_CHECK(anyLE(x,1) && anyLE(x,0)&& !anyLE(x,-1)); BOOST_CHECK(anyLT(x,1) && !anyLT(x,0)&&!anyLT(x,-1)); BOOST_CHECK(anyGE(x,3)&&anyGE(x,4) && !anyGE(x, 5)); BOOST_CHECK(anyGT(x,3) && !anyGT(x,4)&&!anyGT(x, 5)); BOOST_CHECK(anyEQ(x,3) && anyEQ(x,4) && !anyEQ(x,5)); BOOST_CHECK(anyNE(x,3) && anyNE(x,4) && anyNE(x, 5)); BOOST_CHECK(anyLE(3,x) && anyLE(4,x) && !anyLE(5,x)); BOOST_CHECK(anyLT(3,x) && !anyLT(4,x) &&!anyLT(5,x)); BOOST_CHECK(!anyGE(-1,x) && anyGE(0,x) &&anyGE(1,x)); BOOST_CHECK(!anyGT(-1,x) && !anyGT(0,x)&&anyGT(1,x)); BOOST_CHECK(!anyEQ(-1,x)&& anyEQ(0,x) && anyEQ(1,x)); BOOST_CHECK(anyNE(-1,x) && anyNE(0,x) &&anyNE (1,x)); } BOOST_AUTO_TEST_CASE( vector_statistics ) { Vector vd(5); indgen(vd,1.0); BOOST_CHECK(std::fabs(variance(vd) - 2.5) < 0.0001); BOOST_CHECK(std::fabs(stddev(vd) - std::sqrt(2.5)) < 0.0001); BOOST_CHECK(std::fabs(avdev(vd) - 1.2) < 0.0001); } BOOST_AUTO_TEST_CASE( vector_complex ) { Vector> vc(2); vc(0) = std::complex(1.0, 2.0); vc(1) = std::complex(0.0, 1.0); Vector vr = real(vc); BOOST_CHECK_EQUAL(vr(1), 0.0f); vr.assign_conforming(imag(vc)); BOOST_CHECK_EQUAL(vr(0), 2.0f); float pi2 = 3.1415927/2.0; vr.assign_conforming( phase(vc)); float pi2out = vr(1); BOOST_CHECK_LT(fabs(pi2 - pi2out), 0.00001); vr.assign_conforming( amplitude(vc) ); BOOST_CHECK_EQUAL(vr(1), 1.0f); } BOOST_AUTO_TEST_CASE( square_cube ) { Vector vf(3); vf = -3; BOOST_CHECK(allEQ (square(vf), 9)); BOOST_CHECK(allEQ (cube(vf), -27)); } BOOST_AUTO_TEST_CASE( near_tests ) { Vector x1(2), x2(2); x1 = 10000; x2(0) = 10001; x2(1) = 10002; // Array,Array BOOST_CHECK(allNear(x1, x2, 2.01e-4)); BOOST_CHECK(!allNear(x1, x2, 1.01e-4)); BOOST_CHECK(anyNear(x1, x2, 1.01e-4)); BOOST_CHECK(!anyNear(x1, x2, 0.99e-4)); BOOST_CHECK(allNearAbs(x1, x2, 2.01)); BOOST_CHECK(!allNearAbs(x1, x2, 1.01)); BOOST_CHECK(anyNearAbs(x1, x2, 1.01)); BOOST_CHECK(!anyNearAbs(x1, x2, 0.99)); // Constant,Array BOOST_CHECK(allNear(10000.0f, x2, 2.01e-4)); BOOST_CHECK(!allNear(10000.0f, x2, 1.01e-4)); BOOST_CHECK(anyNear(10000.0f, x2, 1.01e-4)); BOOST_CHECK(!anyNear(10000.0f, x2, 0.99e-4)); BOOST_CHECK(allNearAbs(10000.0f, x2, 2.01)); BOOST_CHECK(!allNearAbs(10000.0f, x2, 1.01)); BOOST_CHECK(anyNearAbs(10000.0f, x2, 1.01)); BOOST_CHECK(!anyNearAbs(10000.0f, x2, 0.99)); // Array,Constant BOOST_CHECK(allNear(x1, 10002.0f, 2.01e-4)); BOOST_CHECK(!allNear(x1, 10002.0f, 1.01e-4)); BOOST_CHECK(!anyNear(x1, 10002.0f, 1.01e-4)); BOOST_CHECK(!anyNear(x1, 10002.0f, 0.99e-4)); BOOST_CHECK(allNearAbs(x1, 10002.0f, 2.01)); BOOST_CHECK(!allNearAbs(x1, 10002.0f, 1.01)); BOOST_CHECK(anyNearAbs(x1, 10001.0f, 1.01)); BOOST_CHECK(!anyNearAbs(x1, 10002.0f, 0.99)); } BOOST_AUTO_TEST_CASE( median_and_fractile ) { Vector x1(5), x2(10), x3(100), x4(101), x5(101); indgen(x1); indgen(x2); indgen(x3); indgen(x4); indgen(x5); BOOST_CHECK_EQUAL (median(x1) , 2.); BOOST_CHECK_EQUAL (median(x1, true) , 2.); BOOST_CHECK_EQUAL (median(x1, true, false, false) , 2.); BOOST_CHECK_EQUAL (median(Vector{0., 2.}) , 1.); BOOST_CHECK_EQUAL (median(x1(Slice(0,2,2))) , 1.); BOOST_CHECK_EQUAL (median(x2) , 4.5); BOOST_CHECK_EQUAL (median(x2, true) , 4.5); BOOST_CHECK_EQUAL (median(x2, true, false, false) , 4.); BOOST_CHECK_EQUAL (median(x3) , 49.5); BOOST_CHECK_EQUAL (median(x3, true) , 49.5); BOOST_CHECK_EQUAL (medianInPlace(x3) , 49.5); BOOST_CHECK_EQUAL (median(x3, true, false, true) , 49.); BOOST_CHECK_EQUAL (median(x4) , 50.); BOOST_CHECK_EQUAL (median(x4, true) , 50.); BOOST_CHECK_EQUAL (median(x4, true, false, false) , 50.); BOOST_CHECK_EQUAL (madfm(x1) , 1.); BOOST_CHECK_EQUAL (madfm(x1, true) , 1.); BOOST_CHECK_EQUAL (madfm(x1, true, false, false) , 1.); BOOST_CHECK_EQUAL (madfm(x1(Slice(0,2,2))) , 1.); BOOST_CHECK_EQUAL (madfm(x4) , 25.); // Make sure x4 is not changed. BOOST_CHECK (allEQ (x4, x5)); BOOST_CHECK_EQUAL (fractile(x1, 0.0) , 0.); BOOST_CHECK_EQUAL (fractile(x1, 0.25) , 1.); BOOST_CHECK_EQUAL (fractile(x1, 0.5) , 2.); BOOST_CHECK_EQUAL (fractile(x1, 0.75) , 3.); BOOST_CHECK_EQUAL (fractile(x1, 1.0) , 4.); BOOST_CHECK_EQUAL (fractile(x1, 0.75, true) , 3.); BOOST_CHECK_EQUAL (fractile(x2, 0.5) , 4.); BOOST_CHECK_EQUAL (fractile(x3, 0.5, false, true) , 49.); BOOST_CHECK_EQUAL (fractile(x4, 0.0) , 0.); BOOST_CHECK_EQUAL (fractile(x4, 0.5) , 50.); BOOST_CHECK_EQUAL (fractile(x4, 0.05) , 5.); BOOST_CHECK_EQUAL (fractile(x4, 0.951) , 95.); // Make sure x4 is not changed. BOOST_CHECK (allEQ (x4, x5)); BOOST_CHECK_EQUAL (interQuartileRange(x1) , 2.); BOOST_CHECK_EQUAL (interFractileRange(x1, 0.25) , 2.); BOOST_CHECK_EQUAL (interHexileRange(x4), fractile(x4, 5./6.) - fractile(x4, 1./6.)); // Make sure x4 is not changed. BOOST_CHECK (allEQ (x4, x5)); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tArrayOpsDiffShapes.cc000066400000000000000000000071021476623553700226430ustar00rootroot00000000000000//# tArrayOpsDiffShapes.cc: This program tests the ArrayMath class //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include "../Array.h" #include "../ArrayOpsDiffShapes.h" #include "../ArrayLogical.h" #include "../IPosition.h" #include using namespace casacore; BOOST_AUTO_TEST_SUITE(array_ops_diff_shapes) void test_reformedMask(bool truthval, size_t ni = 3, size_t nj = 4, size_t nk = 5, size_t nl = 6) { Array data(IPosition(4, ni, nj, nk, nl)); const IPosition expectedShape(2, ni, nj); Array expected(expectedShape); for(size_t i = 0; i < ni; ++i){ for(size_t j = 0; j < nj; ++j){ bool expected_val = (i + j) % 2 ? true : false; expected(IPosition(2, i, j)) = expected_val; for(size_t k = 0; k < nk; ++k){ for(size_t l = 0; l < nl; ++l){ if(expected_val) // Make sure [0, 0] gets truthval in case nk == // nl == 1. data(IPosition(4, i, j, k, l)) = (k + l) % 2 ? !truthval : truthval; else data(IPosition(4, i, j, k, l)) = !truthval; } } } } bool allequal = allEQ(reformedMask(data, truthval, expectedShape), expected); BOOST_CHECK (allequal); } void test_binOpExpanders(size_t ni = 2, size_t nj = 3, size_t nk = 4) { const IPosition leftShape(3, ni, nj, nk); Array left(leftShape); Array right(IPosition(2, ni, nj)); Array expected(leftShape); for(size_t i = 0; i < ni; ++i){ for(size_t j = 0; j < nj; ++j){ double rightval = 2.0 + 10.0 * (i + 10.0 * j); // [ 2 102 202 ] // [ 12 112 212 ] right(IPosition(2, i, j)) = rightval; // [ 22 122 222 ] for(size_t k = 0; k < nk; ++k){ const IPosition ijk(3, i, j, k); double leftval = 1.0 + 0.1 * (i + 0.1 * (j + 0.1 * k)); left(ijk) = leftval; expected(ijk) = leftval + rightval; } } } //const Array constleft(left); //BOOST_CHECK(allEQ(binOpExpandR(left, right, std::plus()), // expected)); binOpExpandInPlace(left, right, std::plus()); bool allequal = allEQ(left, expected); BOOST_CHECK (allequal); } BOOST_AUTO_TEST_CASE(test) { for(size_t i = 0; i <= 1; ++i){ for(size_t j = 0; j <= 1; ++j){ test_reformedMask(true, 3, 4, 1 + 3 * i, 1 + 4 * i); test_reformedMask(false, 1 + 4 * j, 1 + 2 * j, 1 + 4 * i, 1 + 3 * i); test_binOpExpanders(1 + i, 2 + j, 4); } } } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tArrayPartMath.cc000066400000000000000000000435521476623553700216760ustar00rootroot00000000000000//# tArrayMath2.cc: This program tests the ArrayPartMath class //# Copyright (C) 1993,1994,1995,1996,1997,1999,2000,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include "../Array.h" #include "../Vector.h" #include "../ArrayPartMath.h" #include "../ArrayLogical.h" #include "../ArrayStr.h" #include using namespace casacore; BOOST_AUTO_TEST_SUITE(array_part_math) typedef Array PartFunc (const Array&, const IPosition& axes); typedef double FullFunc (const Array&); Array myPartialVariances (const Array& array, const IPosition& axes) { return partialVariances (array, axes, 1); } double myVariance (const Array& array) { return pvariance (array, 1); } Array myPartialStddevs (const Array& array, const IPosition& axes) { return partialStddevs (array, axes, 0); } double myStddev (const Array& array) { return pstddev (array, 0); } Array myMeanPartialMedians (const Array& array, const IPosition& axes) { return partialMedians (array, axes, true, false); } double myMeanMedian (const Array& array) { return median (array, false, true, false); } Array myNomeanPartialMedians (const Array& array, const IPosition& axes) { return partialMedians (array, axes, false, false); } double myNomeanMedian (const Array& array) { return median (array, false, false, false); } Array myMeanPartialMadfms (const Array& array, const IPosition& axes) { return partialMadfms (array, axes, true, false); } double myMeanMadfm (const Array& array) { return madfm (array, false, true, false); } Array myNomeanPartialMadfms (const Array& array, const IPosition& axes) { return partialMadfms (array, axes, false, false); } double myNomeanMadfm (const Array& array) { return madfm (array, false, false, false); } Array myPartialFractiles (const Array& array, const IPosition& axes) { return partialFractiles (array, axes, 0.3, false); } double myFractile (const Array& array) { return fractile (array, 0.3, false, false); } Array myPartialHexiles (const Array& array, const IPosition& axes) { return partialInterHexileRanges (array, axes, false); } double myHexile (const Array& array) { return interHexileRange (array, false, false); } Array myPartialQuartiles (const Array& array, const IPosition& axes) { return partialInterQuartileRanges (array, axes, false); } double myQuartile (const Array& array) { return interQuartileRange (array, false, false); } bool doIt1 (PartFunc* partFunc, FullFunc* fullFunc, bool doExtra) { bool errFlag = false; IPosition shape(2,3,4); Array arr(shape); indgen(arr); for (int j=0; j<2; j++) { Vector res(shape(j)); IPosition st(2,0); IPosition end(shape-1); for (int i=0; i res2 = partFunc (arr, IPosition(1,1-j)); if (! allNear (res, res2, 1.e-5)) { errFlag = true; } } if (doExtra) { { Array res2 = partFunc (arr, IPosition()); if (! allNear (arr, res2, 1.e-5)) { errFlag = true; } } { Array res2 = partFunc (arr, IPosition(2,0,1)); Vector res(1, fullFunc(arr)); if (! allEQ (res, res2)) { errFlag = true; } } } return !errFlag; } bool doIt2 (PartFunc* partFunc, FullFunc* fullFunc) { bool errFlag = false; IPosition shape(3,3,4,5); Array arr(shape); indgen(arr); for (int j=0; j<3; j++) { Vector res(shape(j)); IPosition st(3,0); IPosition end(shape-1); for (int i=0; i res2 = partFunc (arr, IPosition::otherAxes(3, IPosition(1,j))); if (! allNear (res, res2, 1.e-5)) { errFlag = true; } } for (int j=0; j<3; j++) { for (int k=j+1; k<3; k++) { IPosition resshape(2,shape(j),shape(k)); Array res(resshape); IPosition st(3,0); IPosition end(shape-1); for (int i0=0; i0 res2 = partFunc (arr, IPosition::otherAxes(3, IPosition(2,j,k))); if (! allNear (res, res2, 1.e-5)) { errFlag = true; } } } return !errFlag; } bool doIt3 (PartFunc* partFunc, FullFunc* fullFunc) { bool errFlag = false; IPosition shape(4,3,4,5,6); Array arr(shape); indgen(arr); for (int j=0; j<4; j++) { Vector res(shape(j)); IPosition st(4,0); IPosition end(shape-1); for (int i=0; i res2 = partFunc (arr, IPosition::otherAxes(4, IPosition(1,j))); if (! allNear (res, res2, 1.e-5)) { errFlag = true; } } for (int j=0; j<4; j++) { for (int k=j+1; k<4; k++) { IPosition resshape(2,shape(j),shape(k)); Array res(resshape); IPosition st(4,0); IPosition end(shape-1); for (int i0=0; i0 res2 = partFunc (arr, IPosition::otherAxes(4, IPosition(2,j,k))); if (! allNear (res, res2, 1.e-5)) { errFlag = true; } } } for (int j0=0; j0<4; j0++) { for (int j1=j0+1; j1<4; j1++) { for (int j2=j1+1; j2<4; j2++) { IPosition resshape(3,shape(j0),shape(j1),shape(j2)); Array res(resshape); IPosition st(4,0); IPosition end(shape-1); for (int i0=0; i0 res2 = partFunc (arr, IPosition::otherAxes(4, IPosition(3,j0,j1,j2))); if (! allNear (res, res2, 1.e-5)) { errFlag = true; } } } } return !errFlag; } bool doIt4 (PartFunc* partFunc, FullFunc* fullFunc) { bool errFlag = false; IPosition shape(5,2,2,2,2,2); Array arr(shape); indgen(arr); for (int j=0; j<5; j++) { Vector res(shape(j)); IPosition st(5,0); IPosition end(shape-1); for (int i=0; i res2 = partFunc (arr, IPosition::otherAxes(5, IPosition(1,j))); if (! allNear (res, res2, 1.e-5)) { errFlag = true; } } for (int j=0; j<5; j++) { for (int k=j+1; k<5; k++) { IPosition resshape(2,shape(j),shape(k)); Array res(resshape); IPosition st(5,0); IPosition end(shape-1); for (int i0=0; i0 res2 = partFunc (arr, IPosition::otherAxes(5, IPosition(2,j,k))); if (! allNear (res, res2, 1.e-5)) { errFlag = true; } } } for (int j0=0; j0<5; j0++) { for (int j1=j0+1; j1<5; j1++) { for (int j2=j1+1; j2<5; j2++) { IPosition resshape(3,shape(j0),shape(j1),shape(j2)); Array res(resshape); IPosition st(5,0); IPosition end(shape-1); for (int i0=0; i0 res2 = partFunc (arr, IPosition::otherAxes(5, IPosition(3,j0,j1,j2))); if (! allNear (res, res2, 1.e-5)) { errFlag = true; } } } } for (int j0=0; j0<5; j0++) { for (int j1=j0+1; j1<5; j1++) { for (int j2=j1+1; j2<5; j2++) { for (int j3=j2+1; j3<5; j3++) { IPosition resshape(4,shape(j0),shape(j1),shape(j2),shape(j3)); Array res(resshape); IPosition st(5,0); IPosition end(shape-1); for (int i0=0; i0 res2 = partFunc (arr, IPosition::otherAxes(5, IPosition(4,j0,j1,j2,j3))); if (! allNear (res, res2, 1.e-5)) { errFlag = true; } } } } } return !errFlag; } bool doIt5 (PartFunc* partFunc, FullFunc* fullFunc) { bool errFlag = false; IPosition shape(6,2,2,2,2,2,2); Array arr(shape); indgen(arr); for (int j=0; j<6; j++) { Vector res(shape(j)); IPosition st(6,0); IPosition end(shape-1); for (int i=0; i res2 = partFunc (arr, IPosition::otherAxes(6, IPosition(1,j))); if (! allNear (res, res2, 5.e-5)) { errFlag = true; } } for (int j=0; j<6; j++) { for (int k=j+1; k<6; k++) { IPosition resshape(2,shape(j),shape(k)); Array res(resshape); IPosition st(6,0); IPosition end(shape-1); for (int i0=0; i0 res2 = partFunc (arr, IPosition::otherAxes(6, IPosition(2,j,k))); if (! allNear (res, res2, 1.e-5)) { errFlag = true; } } } for (int j0=0; j0<6; j0++) { for (int j1=j0+1; j1<6; j1++) { for (int j2=j1+1; j2<6; j2++) { IPosition resshape(3,shape(j0),shape(j1),shape(j2)); Array res(resshape); IPosition st(6,0); IPosition end(shape-1); for (int i0=0; i0 res2 = partFunc (arr, IPosition::otherAxes(6, IPosition(3,j0,j1,j2))); if (! allNear (res, res2, 1.e-5)) { errFlag = true; } } } } for (int j0=0; j0<6; j0++) { for (int j1=j0+1; j1<6; j1++) { for (int j2=j1+1; j2<6; j2++) { for (int j3=j2+1; j3<6; j3++) { IPosition resshape(4,shape(j0),shape(j1),shape(j2),shape(j3)); Array res(resshape); IPosition st(6,0); IPosition end(shape-1); for (int i0=0; i0 res2 = partFunc (arr, IPosition::otherAxes(6, IPosition(4,j0,j1,j2,j3))); if (! allNear (res, res2, 1.e-5)) { errFlag = true; } } } } } for (int j0=0; j0<6; j0++) { for (int j1=j0+1; j1<6; j1++) { for (int j2=j1+1; j2<6; j2++) { for (int j3=j2+1; j3<6; j3++) { for (int j4=j3+1; j4<6; j4++) { IPosition resshape(5,shape(j0),shape(j1),shape(j2),shape(j3), shape(j4)); Array res(resshape); IPosition st(6,0); IPosition end(shape-1); for (int i0=0; i0 res2 = partFunc (arr, IPosition::otherAxes(6, IPosition(5,j0,j1,j2,j3,j4))); if (! allNear (res, res2, 1.e-5)) { errFlag = true; } } } } } } return !errFlag; } bool doIt (PartFunc* partFunc, FullFunc* fullFunc, bool extra) { bool success = true; if(!doIt1(partFunc, fullFunc, extra)) success = false; if(!doIt2(partFunc, fullFunc)) success = false; if(!doIt3(partFunc, fullFunc)) success = false; if(!doIt4(partFunc, fullFunc)) success = false; if(!doIt5(partFunc, fullFunc)) success = false; return success; } BOOST_AUTO_TEST_CASE(partial_sums) { BOOST_CHECK(doIt (&partialSums, &sum, true)); } BOOST_AUTO_TEST_CASE(partial_means) { BOOST_CHECK(doIt (&partialMeans, &mean, true)); } BOOST_AUTO_TEST_CASE(partial_variances) { BOOST_CHECK(doIt (&myPartialVariances, &myVariance, false)); } BOOST_AUTO_TEST_CASE(partial_avdevs) { BOOST_CHECK(doIt (&partialAvdevs, &avdev, false)); } BOOST_AUTO_TEST_CASE(partial_stddevs) { BOOST_CHECK(doIt (&myPartialStddevs, &myStddev, false)); } BOOST_AUTO_TEST_CASE(partial_rmss) { BOOST_CHECK(doIt (&partialRmss, &rms, false)); } BOOST_AUTO_TEST_CASE(partial_mins) { BOOST_CHECK(doIt (&partialMins, &min, true)); } BOOST_AUTO_TEST_CASE(partial_max) { BOOST_CHECK(doIt (&partialMaxs, &max, true)); } BOOST_AUTO_TEST_CASE(partial_medians_even) { BOOST_CHECK(doIt (&myMeanPartialMedians, &myMeanMedian, true)); } BOOST_AUTO_TEST_CASE(partial_medians_noteven) { BOOST_CHECK(doIt (&myNomeanPartialMedians, &myNomeanMedian, true)); } BOOST_AUTO_TEST_CASE(partial_madfms_even) { BOOST_CHECK(doIt (&myMeanPartialMadfms, &myMeanMadfm, true)); } BOOST_AUTO_TEST_CASE(partial_madfms_noteven) { BOOST_CHECK(doIt (&myNomeanPartialMadfms, &myNomeanMadfm, true)); } BOOST_AUTO_TEST_CASE(partial_fractiles) { BOOST_CHECK(doIt (&myPartialFractiles, &myFractile, true)); } BOOST_AUTO_TEST_CASE(partial_hexile) { BOOST_CHECK(doIt (&myPartialHexiles, &myHexile, true)); } BOOST_AUTO_TEST_CASE(partial_quartile) { BOOST_CHECK(doIt (&myPartialQuartiles, &myQuartile, true)); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tArrayPosIter.cc000066400000000000000000000132641476623553700215400ustar00rootroot00000000000000//# tArrayPosIter.cc: This program tests the class ArrayPosIter //# Copyright (C) 2004 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include "../ArrayPosIter.h" #include using namespace casacore; BOOST_AUTO_TEST_SUITE(array_pop_iter) BOOST_AUTO_TEST_CASE( zero_dim ) { IPosition shape(2); shape(0) = shape(1) = 5; ArrayPositionIterator ai (shape, 0); BOOST_CHECK (ai.atStart()); BOOST_CHECK (!ai.pastEnd()); BOOST_CHECK (ai.ndim() == 2); IPosition index (2); size_t iloop; for ( iloop = 0; ! ai.pastEnd(); ai.next(), iloop++ ) { index = ai.pos(); BOOST_CHECK_EQUAL(index(0), iloop%5); BOOST_CHECK_EQUAL(index(1), iloop/5); } BOOST_CHECK (iloop == 25); BOOST_CHECK (!ai.atStart()); BOOST_CHECK (ai.pastEnd()); BOOST_CHECK (ai.ndim() == 2); ai.origin(); BOOST_CHECK (ai.atStart()); BOOST_CHECK (!ai.pastEnd()); BOOST_CHECK (ai.ndim() == 2); } BOOST_AUTO_TEST_CASE( one_dim ) { IPosition shape(2); shape(0) = shape(1) = 5; ArrayPositionIterator ai (shape, 1); BOOST_CHECK (ai.atStart()); BOOST_CHECK (!ai.pastEnd()); BOOST_CHECK (ai.ndim() == 2); IPosition index (2); size_t iloop; for ( iloop = 0; ! ai.pastEnd(); ai.next(), iloop++ ) { index = ai.pos(); BOOST_CHECK_EQUAL(index(0), 0); BOOST_CHECK_EQUAL(index(1), iloop); } BOOST_CHECK (iloop == 5); BOOST_CHECK (!ai.atStart()); BOOST_CHECK (ai.pastEnd()); BOOST_CHECK (ai.ndim() == 2); ai.origin(); BOOST_CHECK (ai.atStart()); BOOST_CHECK (!ai.pastEnd()); BOOST_CHECK (ai.ndim() == 2); } BOOST_AUTO_TEST_CASE( two_dim ) { IPosition shape(2); shape(0) = shape(1) = 5; ArrayPositionIterator ai (shape, 2); BOOST_CHECK (ai.atStart()); BOOST_CHECK (!ai.pastEnd()); BOOST_CHECK (ai.ndim() == 2); IPosition index (2); size_t iloop; for ( iloop = 0; ! ai.pastEnd(); ai.next(), iloop++ ) { index = ai.pos(); BOOST_CHECK_EQUAL(index(0), 0); BOOST_CHECK_EQUAL(index(1), 0); } BOOST_CHECK (iloop == 1); BOOST_CHECK (!ai.atStart()); BOOST_CHECK (ai.pastEnd()); BOOST_CHECK (ai.ndim() == 2); ai.origin(); BOOST_CHECK (ai.atStart()); BOOST_CHECK (!ai.pastEnd()); BOOST_CHECK (ai.ndim() == 2); } BOOST_AUTO_TEST_CASE( dim_2 ) { IPosition shape(3); shape(0) = 5; shape(1) = 3; shape(2) = 7; ArrayPositionIterator ai (shape, 2); BOOST_CHECK (ai.atStart()); BOOST_CHECK (!ai.pastEnd()); BOOST_CHECK (ai.ndim() == 3); IPosition index (3); size_t iloop; for ( iloop = 0; ! ai.pastEnd(); ai.next(), iloop++ ) { index = ai.pos(); BOOST_CHECK_EQUAL(index(0), 0); BOOST_CHECK_EQUAL(index(1), 0); BOOST_CHECK_EQUAL(index(2), iloop); } BOOST_CHECK (iloop == 7); BOOST_CHECK (!ai.atStart()); BOOST_CHECK (ai.pastEnd()); BOOST_CHECK (ai.ndim() == 3); ai.origin(); BOOST_CHECK (ai.atStart()); BOOST_CHECK (!ai.pastEnd()); BOOST_CHECK (ai.ndim() == 3); } BOOST_AUTO_TEST_CASE( dim_0_1 ) { IPosition shape(3); shape(0) = 5; shape(1) = 3; shape(2) = 7; ArrayPositionIterator ai (shape, IPosition(1,2)); BOOST_CHECK (ai.atStart()); BOOST_CHECK (!ai.pastEnd()); BOOST_CHECK (ai.ndim() == 3); IPosition index (3); size_t iloop; for ( iloop = 0; ! ai.pastEnd(); ai.next(), iloop++ ) { index = ai.pos(); BOOST_CHECK_EQUAL(index(0), iloop%5); BOOST_CHECK_EQUAL(index(1), iloop/5); BOOST_CHECK_EQUAL(index(2), 0); } BOOST_CHECK (iloop == 15); BOOST_CHECK (!ai.atStart()); BOOST_CHECK (ai.pastEnd()); BOOST_CHECK (ai.ndim() == 3); ai.origin(); BOOST_CHECK (ai.atStart()); BOOST_CHECK (!ai.pastEnd()); BOOST_CHECK (ai.ndim() == 3); } BOOST_AUTO_TEST_CASE( dim_2_0 ) { IPosition shape(3); shape(0) = 5; shape(1) = 3; shape(2) = 7; ArrayPositionIterator ai (shape, IPosition(2,2,0), false); BOOST_CHECK (ai.atStart()); BOOST_CHECK (!ai.pastEnd()); BOOST_CHECK (ai.ndim() == 3); IPosition index (3); size_t iloop; for ( iloop = 0; ! ai.pastEnd(); ai.next(), iloop++ ) { index = ai.pos(); BOOST_CHECK_EQUAL(index(0), iloop/7); BOOST_CHECK_EQUAL(index(1), 0); BOOST_CHECK_EQUAL(index(2), iloop%7); } BOOST_CHECK (iloop == 35); BOOST_CHECK (!ai.atStart()); BOOST_CHECK (ai.pastEnd()); BOOST_CHECK (ai.ndim() == 3); ai.origin(); BOOST_CHECK (ai.atStart()); BOOST_CHECK (!ai.pastEnd()); BOOST_CHECK (ai.ndim() == 3); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tArrayStr.cc000066400000000000000000000137531476623553700207260ustar00rootroot00000000000000//# tArrayIO2.cc: This program tests the tArrayIO2 functions //# Copyright (C) 1993,1994,1995,1996,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include "../ArrayStr.h" #include "../Vector.h" #include "../Matrix.h" #include #include // This test program tests the ArrayIO2 functions. // It writes all kind of stuff, reads it back and writes it to stdout. // A script compares this output with a reference output file. using namespace casacore; BOOST_AUTO_TEST_SUITE(array_IO_2) BOOST_AUTO_TEST_CASE( bin ) { Vector ip(1000); for (size_t i=0; i<1000; i++) { ip(i) = i*2; } write_array (ip, "tArrayIO2_tmp.data"); Vector ipi(1000); read_array (ipi, "tArrayIO2_tmp.data"); for (size_t i=0; i<1000; i++) { BOOST_CHECK_EQUAL(ipi(i), i*2); } boost::filesystem::remove("tArrayIO2_tmp.data"); } BOOST_AUTO_TEST_CASE( matrix_read ) { const char* str = "1 2 3 4\n" "2 3 4 5\n" "1 4 5 6\n" "4 5 6 7\n"; std::ofstream ofile("tArrayIO2.in_mat"); ofile << str << '\n'; ofile.close(); Matrix mat; readAsciiMatrix (mat, "tArrayIO2.in_mat"); std::ostringstream ostr; ostr << mat; BOOST_CHECK_EQUAL(ostr.str(), "Axis Lengths: [4, 4] (NB: Matrix in Row/Column order)\n" "[1, 2, 3, 4\n" " 2, 3, 4, 5\n" " 1, 4, 5, 6\n" " 4, 5, 6, 7]\n"); boost::filesystem::remove("tArrayIO2.in_mat"); } BOOST_AUTO_TEST_CASE( matrix_write ) { Matrix mat(4, 4); for(int y=0; y!=4; ++y) { for(int x=0; x!=4; ++x) { mat(IPosition{x, y}) = x + y + 1; } } writeAsciiMatrix (mat, "tArrayIO2_tmp.mat"); std::ifstream ifile("tArrayIO2_tmp.mat"); std::stringstream sstr; sstr << ifile.rdbuf(); BOOST_CHECK_EQUAL(sstr.str(), "1 2 3 4 \n" "2 3 4 5 \n" "3 4 5 6 \n" "4 5 6 7 \n"); boost::filesystem::remove("tArrayIO2_tmp.mat"); } BOOST_AUTO_TEST_CASE( vector_read ) { std::ofstream ofile("tArrayIO2.in_vec"); const char* str = "1.23\n" "2.34 3.45\n" " 4.56\n" " 5.67 6.78 9\n"; ofile << str << '\n'; ofile.close(); Vector vec; readAsciiVector (vec, "tArrayIO2.in_vec"); Vector ref{1.23, 2.34, 3.45, 4.56, 5.67, 6.78, 9}; BOOST_CHECK_EQUAL(vec.shape(), ref.shape()); for(size_t i=0; i!=ref.nelements(); ++i) BOOST_CHECK_EQUAL(vec[i], ref[i]); boost::filesystem::remove("tArrayIO2.in_vec"); } BOOST_AUTO_TEST_CASE( vector_write ) { Vector vec{1.23, 2.34, 3.45, 4.56, 5.67, 6.78, 9}; writeAsciiVector (vec, "tArrayIO2_tmp.vec"); std::ifstream ifile("tArrayIO2_tmp.vec"); std::stringstream sstr; sstr << ifile.rdbuf(); BOOST_CHECK_EQUAL(sstr.str(), "1.23 2.34 3.45 4.56 5.67 6.78 9 \n"); boost::filesystem::remove("tArrayIO2_tmp.vec"); } template void checkExtract(const std::string& str, const Arr& ref, const std::string& outputStr) { std::stringstream istr(str); Arr a; istr >> a; BOOST_CHECK(istr); BOOST_CHECK_EQUAL(a.shape().size(), ref.shape().size()); BOOST_CHECK_EQUAL(a.shape(), ref.shape()); BOOST_CHECK_EQUAL(a.shape(), ref.shape()); std::ostringstream astr, refstr; astr << a; refstr << ref; BOOST_CHECK_EQUAL(astr.str(), refstr.str()); BOOST_CHECK_EQUAL(astr.str(), outputStr); } BOOST_AUTO_TEST_CASE( double_istream_extract_arr1 ) { checkExtract( "[1 2 3 4 5 6]", Array{1,2,3,4,5,6}, "[1, 2, 3, 4, 5, 6]" ); } BOOST_AUTO_TEST_CASE( double_istream_extract_matrix1 ) { const double data[] = {1,2,3,4,5,6}; checkExtract( "[1 2 3 4 5 6]\n", Matrix(IPosition{6, 1}, data), "Axis Lengths: [6, 1] (NB: Matrix in Row/Column order)\n" "[1\n" " 2\n" " 3\n" " 4\n" " 5\n" " 6]\n" ); } BOOST_AUTO_TEST_CASE( double_istream_extract_arr2 ) { const double data[] = {1,4,2,5,3,6}; checkExtract( "{[2 3]}[1 2 3 4 5 6]\n", Array(IPosition{2, 3}, data), "Axis Lengths: [2, 3] (NB: Matrix in Row/Column order)\n" "[1, 2, 3\n" " 4, 5, 6]\n" ); } BOOST_AUTO_TEST_CASE( double_istream_extract_matrix2 ) { const double data[] = {1,4,2,5,3,6}; checkExtract( "{[2 3]}[1 2 3 4 5 6]\n", Matrix(IPosition{2, 3}, data), "Axis Lengths: [2, 3] (NB: Matrix in Row/Column order)\n" "[1, 2, 3\n" " 4, 5, 6]\n" ); } BOOST_AUTO_TEST_CASE( double_istream_extract_arr3 ) { const double data[] = {1,2,3,4,5,6}; checkExtract( "{t[2 3]}[1 2 3 4 5 6]\n", Array(IPosition{2, 3}, data), "Axis Lengths: [2, 3] (NB: Matrix in Row/Column order)\n" "[1, 3, 5\n" " 2, 4, 6]\n" ); } BOOST_AUTO_TEST_CASE( double_istream_extract_string1 ) { checkExtract( "[ x, y , x]\n", Array{"x","y","x"}, "[x, y, x]" ); } BOOST_AUTO_TEST_CASE( double_istream_extract_string2 ) { checkExtract( "[x, y]\n", Array{"x","y"}, "[x, y]" ); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tArrayUtil.cc000066400000000000000000000307061476623553700210700ustar00rootroot00000000000000//# tArrayUtil.cc: Test program for functions in ArrayUtil.h //# Copyright (C) 1995,1996,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //#include "../ArrayIO.h" #include "../ArrayMath.h" #include "../ArrayError.h" #include "../ArrayUtil.h" #include "../Matrix.h" #include "../Vector.h" // // Test of functions in ArrayUtil.h. // // This program tests the various functions in ArrayUtil.h. // When an argument is given to the program, it will not invoke any // function resulting in an exception. This mode can be used to do // proper detection of memory leaks using tools like TestCenter. #include using namespace casacore; BOOST_AUTO_TEST_SUITE(array_util) BOOST_AUTO_TEST_CASE( string_to_vector ) { Vector vec1 = strToVector (""); BOOST_CHECK_EQUAL (vec1.nelements(), 0); Vector vec2 = strToVector ("abc"); BOOST_CHECK_EQUAL(vec2.nelements(), 1); BOOST_CHECK_EQUAL(vec2(0), "abc"); Vector vec3 = strToVector (","); BOOST_CHECK_EQUAL(vec3.nelements(), 2); BOOST_CHECK_EQUAL(vec3(0), ""); BOOST_CHECK_EQUAL(vec3(1), ""); Vector vec4 = strToVector ("abc,defg,,h"); BOOST_CHECK_EQUAL(vec4.nelements(), 4); BOOST_CHECK_EQUAL(vec4(0), "abc"); BOOST_CHECK_EQUAL(vec4(1), "defg"); BOOST_CHECK_EQUAL(vec4(2), ""); BOOST_CHECK_EQUAL(vec4(3), "h"); Vector vec5 = strToVector (",abc,defg,"); BOOST_CHECK_EQUAL(vec5.nelements(), 4); BOOST_CHECK_EQUAL(vec5(0), ""); BOOST_CHECK_EQUAL(vec5(1), "abc"); BOOST_CHECK_EQUAL(vec5(2), "defg"); BOOST_CHECK_EQUAL(vec5(3), ""); } BOOST_AUTO_TEST_CASE( string_to_vector_regex ) { // Test using multiple spaces and a single comma as delimiter. std::regex delim(" *, *"); Vector vec1 = strToVector ("", delim); BOOST_CHECK_EQUAL (vec1.nelements(), 0); Vector vec2 = strToVector ("abc", delim); BOOST_CHECK_EQUAL(vec2.nelements(), 1); BOOST_CHECK_EQUAL(vec2(0), "abc" ); Vector vec3 = strToVector (",", delim); BOOST_CHECK_EQUAL(vec3.nelements(), 2 ); BOOST_CHECK_EQUAL(vec3(0), ""); BOOST_CHECK_EQUAL(vec3(1), ""); Vector vec4 = strToVector ("abc,defg,,h", delim); BOOST_CHECK_EQUAL(vec4.nelements(), 4); BOOST_CHECK_EQUAL(vec4(0), "abc"); BOOST_CHECK_EQUAL(vec4(1), "defg"); BOOST_CHECK_EQUAL(vec4(2), ""); BOOST_CHECK_EQUAL(vec4(3), "h"); Vector vec5 = strToVector (",abc,defg,", delim); BOOST_CHECK_EQUAL(vec5.nelements(), 4); BOOST_CHECK_EQUAL(vec5(0), ""); BOOST_CHECK_EQUAL(vec5(1), "abc"); BOOST_CHECK_EQUAL(vec5(2), "defg"); BOOST_CHECK_EQUAL(vec5(3), ""); Vector vec6 = strToVector (" , ", delim); BOOST_CHECK_EQUAL(vec6.nelements(), 2); BOOST_CHECK_EQUAL(vec6(0), ""); BOOST_CHECK_EQUAL(vec6(1), ""); Vector vec7 = strToVector ("abc , defg , , h", delim); BOOST_CHECK_EQUAL(vec7.nelements(), 4); BOOST_CHECK_EQUAL(vec7(0), "abc"); BOOST_CHECK_EQUAL(vec7(1), "defg"); BOOST_CHECK_EQUAL(vec7(2), ""); BOOST_CHECK_EQUAL(vec7(3), "h"); Vector vec8 = strToVector (" abc ", delim); BOOST_CHECK_EQUAL(vec8.nelements(), 1); BOOST_CHECK_EQUAL(vec8(0), " abc "); } BOOST_AUTO_TEST_CASE( concatenate_array ) { bool doExcp = true; Matrix matrix1 (3u,4u); Matrix matrix2 (3u,5u); Matrix matrix3 (4u,4u); Vector vector1 (4); Vector vector2 (6); indgen (matrix1); indgen (matrix2, int(matrix1.nelements())); indgen (matrix3); indgen (vector1); indgen (vector2, int(vector1.nelements())); Matrix matrixConc(concatenateArray (matrix1, matrix2)); BOOST_CHECK_EQUAL (matrixConc.shape(), IPosition(2,3,9)); size_t i, j; int value = 0; for (j=0; j<9; j++) { for (i=0; i<3; i++) { BOOST_CHECK_EQUAL(matrixConc(i,j), value); ++value; } } Vector vectorConc = concatenateArray (vector1, vector2); BOOST_CHECK_EQUAL(vectorConc.shape(), IPosition(1,10)); value = 0; for (i=0; i<10; i++) { BOOST_CHECK_EQUAL(vectorConc(i), value); ++value; } if (doExcp) { BOOST_CHECK_THROW(concatenateArray (matrix1, matrix3), ArrayConformanceError); BOOST_CHECK_THROW(concatenateArray (matrix1, vector1), ArrayConformanceError); } } BOOST_AUTO_TEST_CASE( reorder_array_2d ) { IPosition shape(2,3,4); Array arr(shape); indgen(arr); for (int j0=0; j0<2; j0++) { for (int j1=0; j1<2; j1++) { if (j1 != j0) { IPosition axisOrder(2, j0, j1); Array res = reorderArray (arr, axisOrder); const IPosition& resShape = res.shape(); IPosition posOld(2); IPosition posNew(2); for (int i1=0; i1 arr(shape); indgen(arr); for (int j0=0; j0<3; j0++) { for (int j1=0; j1<3; j1++) { if (j1 != j0) { for (int j2=0; j2<3; j2++) { if (j2 != j0 && j2 != j1) { IPosition axisOrder(3, j0, j1, j2); Array res = reorderArray (arr, axisOrder); const IPosition& resShape = res.shape(); IPosition posOld(3); IPosition posNew(3); for (int i2=0; i2 arr(shape); indgen(arr); for (int j0=0; j0<4; j0++) { for (int j1=0; j1<4; j1++) { if (j1 != j0) { for (int j2=0; j2<4; j2++) { if (j2 != j0 && j2 != j1) { for (int j3=0; j3<4; j3++) { if (j3 != j0 && j3 != j1 && j3 != j2) { IPosition axisOrder(4, j0, j1, j2, j3); Array res = reorderArray (arr, axisOrder); const IPosition& resShape = res.shape(); IPosition posOld(4); IPosition posNew(4); for (int i3=0; i3(IPosition(2,3,4)), IPosition(2,1,1)), std::runtime_error); BOOST_CHECK_THROW( reorderArray (Array(IPosition(2,3,4)), IPosition(2,1,2)), std::runtime_error); } BOOST_AUTO_TEST_CASE( reverse_array ) { IPosition shape(3, 2, 3, 4); Array arr(shape); indgen(arr); // Test if no reversal is fine. IPosition axes(0); Array rev = reverseArray(arr, axes); BOOST_CHECK( allEQ(arr, rev) ); // Test if reversal of axis 0 is fine. rev = reverseArray(arr, 0); for (int i=0; i using namespace casacore; BOOST_AUTO_TEST_SUITE(axis_specifier) BOOST_AUTO_TEST_CASE( tests ) { AxesMapping map; { AxesSpecifier as; map = as.apply (IPosition(3,1,10,1)); BOOST_CHECK (map.getToNew() == IPosition(3,0,1,2)); BOOST_CHECK (map.getToOld() == IPosition(3,0,1,2)); BOOST_CHECK (!map.isRemoved()); BOOST_CHECK (!map.isReordered()); } { AxesSpecifier as(true); map = as.apply (IPosition(3,1,10,1)); BOOST_CHECK (map.getToNew() == IPosition(3,0,1,2)); BOOST_CHECK (map.getToOld() == IPosition(3,0,1,2)); BOOST_CHECK (!map.isRemoved()); BOOST_CHECK (!map.isReordered()); } { AxesSpecifier as(false); map = as.apply (IPosition(3,1,10,1)); BOOST_CHECK (map.getToNew() == IPosition(3,-1,0,-1)); BOOST_CHECK (map.getToOld() == IPosition(1,1)); BOOST_CHECK (map.isRemoved()); BOOST_CHECK (!map.isReordered()); } { AxesSpecifier as(IPosition(1,0)); map = as.apply (IPosition(3,1,10,1)); BOOST_CHECK (map.getToNew() == IPosition(3,0,1,-1)); BOOST_CHECK (map.getToOld() == IPosition(2,0,1)); BOOST_CHECK (map.isRemoved()); BOOST_CHECK (!map.isReordered()); } { AxesSpecifier as(IPosition(4,1,2,1,2)); map = as.apply (IPosition(3,1,10,1)); BOOST_CHECK (map.getToNew() == IPosition(3,-1,0,1)); BOOST_CHECK (map.getToOld() == IPosition(2,1,2)); BOOST_CHECK (map.isRemoved()); BOOST_CHECK (!map.isReordered()); } { AxesSpecifier as(IPosition(4,1,0,1,2), IPosition(2,2,0)); AxesSpecifier as1(IPosition(2,0,2)); as = as1; map = as.apply (IPosition(3,1,10,1)); BOOST_CHECK (map.getToNew() == IPosition(3,0,1,2)); BOOST_CHECK (map.getToOld() == IPosition(3,0,1,2)); BOOST_CHECK (!map.isRemoved()); BOOST_CHECK (!map.isReordered()); } { AxesSpecifier as1(IPosition(4,1,0,1,2), IPosition(2,2,0)); AxesSpecifier as(as1); map = as.apply (IPosition(3,1,10,1)); BOOST_CHECK (map.getToNew() == IPosition(3,1,2,0)); BOOST_CHECK (map.getToOld() == IPosition(3,2,0,1)); BOOST_CHECK (!map.isRemoved()); BOOST_CHECK (map.isReordered()); } { AxesSpecifier as(IPosition(4,1,2,1,2), IPosition(2,1,0)); map = as.apply (IPosition(3,1,10,1)); AxesMapping map1(map); BOOST_CHECK (map1.getToNew() == IPosition(3,-1,1,0)); BOOST_CHECK (map1.getToOld() == IPosition(2,2,1)); BOOST_CHECK (map1.isRemoved()); BOOST_CHECK (map1.isReordered()); BOOST_CHECK (map1.shapeToNew(IPosition(3,1,3,4)) == IPosition(2,4,3)); BOOST_CHECK (map1.posToNew(IPosition(3,0,3,4)) == IPosition(2,4,3)); BOOST_CHECK (map1.shapeToOld(IPosition(2,4,3)) == IPosition(3,1,3,4)); BOOST_CHECK (map1.posToOld(IPosition(2,4,3)) == IPosition(3,0,3,4)); Slicer slin(IPosition(3,0,1,2), IPosition(3,1,3,2)); Slicer slout = map1.slicerToNew (slin); BOOST_CHECK (slout.start() == IPosition(2,2,1)); BOOST_CHECK (slout.length() == IPosition(2,2,3)); BOOST_CHECK (slout.stride() == IPosition(2,1,1)); Slicer slout2 = map1.slicerToOld (slout); BOOST_CHECK (slout2.start() == slin.start()); BOOST_CHECK (slout2.length() == slin.length()); BOOST_CHECK (slout2.stride() == slin.stride()); } } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tBoxedArrayMath.cc000066400000000000000000000170771476623553700220340ustar00rootroot00000000000000//# tBoxedArrayMath.cc: Test program for function boxedArrayMath //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include "../MaskArrMath.h" #include "../ArrayLogical.h" #include "../ArrayPartMath.h" //#include "../ArrayIO.h" #include "../MaskArrIO.h" #include #include #include using namespace casacore; using namespace std; BOOST_AUTO_TEST_SUITE(boxed_array_math) template void check(const Array& a, const IPosition& expectedShape, std::initializer_list expectedValues) { BOOST_CHECK_EQUAL(a.shape(), expectedShape); if(a.shape() == expectedShape) { std::vector v = a.tovector(); auto iterE = expectedValues.begin(); auto iterV = v.begin(); while(iterE != expectedValues.end()) { BOOST_CHECK_CLOSE_FRACTION(*iterE, *iterV, 1e-6); ++iterV; ++iterE; } } } void check(const Array& a, const IPosition& expectedShape, std::initializer_list expectedValues) { BOOST_CHECK_EQUAL(a.shape(), expectedShape); if(a.shape() == expectedShape) { std::vector v = a.tovector(); auto iterE = expectedValues.begin(); auto iterV = v.begin(); while(iterE != expectedValues.end()) { BOOST_CHECK_EQUAL(*iterE, *iterV); ++iterV; ++iterE; } } } template void check(const MaskedArray& a, const IPosition& expectedShape, std::initializer_list expectedValues, std::initializer_list expectedMask) { check(a.getArray(), expectedShape, expectedValues); check(a.getMask(), expectedShape, expectedMask); } BOOST_AUTO_TEST_CASE( sumfunc1 ) { IPosition shape(2,5,5); Array arr(shape); indgen (arr); check( boxedArrayMath(arr, IPosition(2,2), SumFunc()), IPosition{3,3}, {12.f, 20.f, 13.f, 52.f, 60.f, 33.f, 41.f, 45.f, 24.f} ); check( boxedArrayMath(arr, IPosition(2,1), SumFunc()), IPosition{5, 5}, {0.f, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f, 10.f, 11.f, 12.f, 13.f, 14.f, 15.f, 16.f, 17.f, 18.f, 19.f, 20.f, 21.f, 22.f, 23.f, 24.f} ); check( boxedArrayMath(arr, IPosition(1,0), SumFunc()), IPosition{1, 5}, { 10.f, 35.f, 60.f, 85.f, 110.f } ); } BOOST_AUTO_TEST_CASE( sumfunc2 ) { IPosition shape(2,5,5); Array arr(shape); indgen (arr); check( boxedArrayMath(arr, IPosition(4,2), SumFunc()), IPosition{3, 3}, { 12.f, 20.f, 13.f, 52.f, 60.f, 33.f, 41.f, 45.f, 24.f } ); check( boxedArrayMath(arr, IPosition(3,1), SumFunc()), IPosition{5, 5}, {0.f, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f, 10.f, 11.f, 12.f, 13.f, 14.f, 15.f, 16.f, 17.f, 18.f, 19.f, 20.f, 21.f, 22.f, 23.f, 24.f} ); check( boxedArrayMath(arr, IPosition(), SumFunc()), IPosition{5, 5}, {0.f, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f, 10.f, 11.f, 12.f, 13.f, 14.f, 15.f, 16.f, 17.f, 18.f, 19.f, 20.f, 21.f, 22.f, 23.f, 24.f} ); } BOOST_AUTO_TEST_CASE( medianfunc ) { IPosition shape(2,5,5); Array arr(shape); indgen (arr); check( boxedArrayMath(arr, IPosition(2,2), MedianFunc()), IPosition{3, 3}, { 3.f, 5.f, 6.5f, 13.f, 15.f, 16.5f, 20.5f, 22.5f, 24.f } ); check( boxedArrayMath(arr, IPosition(2,1), MedianFunc()), IPosition{5, 5}, {0.f, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f, 10.f, 11.f, 12.f, 13.f, 14.f, 15.f, 16.f, 17.f, 18.f, 19.f, 20.f, 21.f, 22.f, 23.f, 24.f} ); check( boxedArrayMath(arr, IPosition(2,0), MedianFunc()), IPosition{1, 1}, {12.f} ); } BOOST_AUTO_TEST_CASE( masked_init ) { Array darr(IPosition{5, 5}); indgen(darr); MaskedArray arr = darr(darr<=float(10) || darr>float(14)); check(arr.getMask(), IPosition{5, 5}, { true, true, true, true, true, true, true, true, true, true, true, false, false, false, false, true, true, true, true, true, true, true, true, true, true }); BOOST_CHECK_CLOSE_FRACTION(sum(arr), 250, 1e-6); } BOOST_AUTO_TEST_CASE( masked_sum1 ) { Array darr(IPosition{5, 5}); indgen(darr); MaskedArray arr = darr(darr<=float(10) || darr>float(14)); check( boxedArrayMath(arr, IPosition(2,2), MaskedSumFunc()), IPosition{3, 3}, { 12.f, 20.f, 13.f, 41.f, 35.f, 19.f, 41.f, 45.f, 24.f }, { true, true, true, true, true, true, true, true, true } ); check( boxedArrayMath(arr, IPosition(2,1), MaskedSumFunc()), IPosition{5, 5}, {0.f, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f, 10.f, 0.f, 0.f, 0.f, 0.f, 15.f, 16.f, 17.f, 18.f, 19.f, 20.f, 21.f, 22.f, 23.f, 24.f}, { true, true, true, true, true, true, true, true, true, true, true, false, false, false, false, true, true, true, true, true, true, true, true, true, true }); } BOOST_AUTO_TEST_CASE( masked_sum2 ) { Array darr(IPosition{5, 5}); indgen(darr); MaskedArray arr = darr(darr<=float(10) || darr>float(14)); check( boxedArrayMath(arr, IPosition(4,2), MaskedSumFunc()), IPosition{3, 3}, { 12.f, 20.f, 13.f, 41.f, 35.f, 19.f, 41.f, 45.f, 24.f }, { true, true, true, true, true, true, true, true, true } ); check( boxedArrayMath(arr, IPosition(3,1), MaskedSumFunc()), IPosition{5, 5}, {0.f, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f, 10.f, 0.f, 0.f, 0.f, 0.f, 15.f, 16.f, 17.f, 18.f, 19.f, 20.f, 21.f, 22.f, 23.f, 24.f}, { true, true, true, true, true, true, true, true, true, true, true, false, false, false, false, true, true, true, true, true, true, true, true, true, true }); } BOOST_AUTO_TEST_CASE( masked_median ) { Array darr(IPosition{5, 5}); indgen(darr); MaskedArray arr = darr(darr<=float(10) || darr>float(14)); check( boxedArrayMath(arr, IPosition(2,2), MaskedMedianFunc()), IPosition{3, 3}, {3.f, 5.f, 6.5f, 15.f, 17.5f, 19.f, 20.5f, 22.5f, 24.f}, { true, true, true, true, true, true, true, true, true } ); check( boxedArrayMath(arr, IPosition(2,1), MaskedMedianFunc()), IPosition{5, 5}, {0.f, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f, 10.f, 0.f, 0.f, 0.f, 0.f, 15.f, 16.f, 17.f, 18.f, 19.f, 20.f, 21.f, 22.f, 23.f, 24.f}, { true, true, true, true, true, true, true, true, true, true, true, false, false, false, false, true, true, true, true, true, true, true, true, true, true }); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tConvertArray.cc000066400000000000000000000056451476623553700215770ustar00rootroot00000000000000//# tConvertArray.cc: This program tests the convertArray functions //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include "../Array.h" #include "../ArrayMath.h" #include "../ArrayLogical.h" #include using namespace casacore; BOOST_AUTO_TEST_SUITE(axis_specifier) template void tConvertEQ (const IPosition& shape) { Array arr(shape); Array res(shape); Array exp(shape); indgen (arr, F(0), F(1)); indgen (exp, T(0), T(1)); convertArray (res, arr); BOOST_CHECK (allEQ(res, exp)); // Test on non-contiguous array. IPosition st(shape.size(), 1); IPosition end(shape-2); Array arr1 = arr(st,end); Array exp1 = exp(st,end); Array res1 = res(st,end); res1 = 0; convertArray (res1, arr1); BOOST_CHECK (allEQ(res1, exp1)); BOOST_CHECK (allEQ(res, exp)); } template void tConvertNear (const IPosition& shape) { Array arr(shape); Array res(shape); Array exp(shape); indgen (arr, F(0), F(1)); indgen (exp, T(0), T(1)); convertArray (res, arr); BOOST_CHECK (allNear(res, exp, 1e-5)); // Test on non-contiguous array. IPosition st(shape.size(), 1); IPosition end(shape-2); Array arr1 = arr(st,end); Array exp1 = exp(st,end); Array res1 = res(st,end); res1 = 0; convertArray (res1, arr1); BOOST_CHECK (allNear(res1, exp1, 1e-5)); BOOST_CHECK (allNear(res, exp, 1e-5)); } BOOST_AUTO_TEST_CASE( converteq1 ) { tConvertEQ (IPosition{40,50,6}); // size should fit in Short } BOOST_AUTO_TEST_CASE( converteq2 ) { tConvertEQ (IPosition{40,50,6}); } BOOST_AUTO_TEST_CASE( convertnear1 ) { tConvertNear (IPosition{40,50,600}); } BOOST_AUTO_TEST_CASE( convertnear2 ) { tConvertNear,float> (IPosition{40,50,600}); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tCpp11Features.cc000066400000000000000000000306541476623553700215410ustar00rootroot00000000000000#include "../Array.h" #include "../Cube.h" #include "../Slice.h" #include "../Slicer.h" #include "../Vector.h" #include using namespace casacore; BOOST_AUTO_TEST_SUITE(cpp11_features) void checkEmpty(const IPosition& ip) { BOOST_CHECK_EQUAL(ip.empty(), true); BOOST_CHECK_EQUAL(ip.size(), 0); } void checkSmall(const IPosition& ip) { BOOST_CHECK_EQUAL(ip.empty(), false); BOOST_CHECK_EQUAL(ip.size(), 2); BOOST_CHECK_EQUAL(ip[0], 2); BOOST_CHECK_EQUAL(ip[1], 3); } void checkLarge(const IPosition& ip) { BOOST_CHECK_EQUAL(ip.empty(), false); BOOST_CHECK_EQUAL(ip.size(), 6); for(size_t i=0; i!=6; ++i) BOOST_CHECK_EQUAL(ip[i], i+2); } BOOST_AUTO_TEST_CASE( ip_initializerlist ) { IPosition ip{}; checkEmpty(ip); IPosition ip1{2, 3}; checkSmall(ip1); IPosition ip2{2, 3, 4, 5, 6, 7}; checkLarge(ip2); } BOOST_AUTO_TEST_CASE( ip_move_constructor ) { IPosition ip1{2, 3}; IPosition ip2(std::move(ip1)); checkSmall(ip2); checkEmpty(ip1); IPosition ip3{2, 3, 4, 5, 6, 7}; IPosition ip4(std::move(ip3)); checkLarge(ip4); checkEmpty(ip3); } BOOST_AUTO_TEST_CASE( ip_move_assignment ) { IPosition ip1{2, 3}, ip2; ip2 = std::move(ip1); checkSmall(ip2); checkEmpty(ip1); IPosition ip3{2, 3, 4, 5, 6, 7}, ip4; ip4 = std::move(ip3); checkLarge(ip4); checkEmpty(ip3); ip4 = std::move(ip2); checkSmall(ip4); checkEmpty(ip2); } BOOST_AUTO_TEST_CASE( ip_non_conforming_copy_assignment ) { // Since the 2020 changes, this should no longer throw. IPosition ip1{2, 3}; IPosition ip2{2, 3, 4, 5, 6, 7}; ip1 = ip2; // normal copy assignment checkLarge(ip1); checkLarge(ip2); } BOOST_AUTO_TEST_CASE( array_move_constructor ) { IPosition ip{2, 3}; Array a(ip, 42); Array b(std::move(a)); Array ref(ip, 42); BOOST_CHECK_EQUAL(b.shape(), ip); BOOST_CHECK_EQUAL_COLLECTIONS(b.begin(), b.end(), ref.begin(), ref.end()); Array c(std::move(a)); Array empty; BOOST_CHECK_EQUAL_COLLECTIONS(c.begin(), c.end(), empty.begin(), empty.end()); } BOOST_AUTO_TEST_CASE( array_iterate_empty ) { Array empty1, empty2; BOOST_CHECK_EQUAL_COLLECTIONS(empty1.begin(), empty1.end(), empty2.begin(), empty2.end()); } BOOST_AUTO_TEST_CASE( array_move_assignment ) { IPosition ip{2, 3}; Array a(ip, 42); Array b; b = std::move(a); Array ref(ip, 42); BOOST_CHECK_EQUAL(b.shape(), ip); BOOST_CHECK_EQUAL_COLLECTIONS(b.begin(), b.end(), ref.begin(), ref.end()); Array c(IPosition{1,1}, 7); // incorrect shape BOOST_CHECK_THROW(c = std::move(b), std::runtime_error); // TODO it is questionable whether we would want this to throw! BOOST_CHECK_EQUAL(c(IPosition(2, 0, 0)), 7); Array d(IPosition(0)); Vector e(IPosition{3}, 1982); d = std::move(e); // Move assigning to empty should not validate shape BOOST_CHECK_EQUAL(d.shape(), IPosition(1, 3)); } BOOST_AUTO_TEST_CASE( array_move_case1 ) { Array a(IPosition{2, 3}, 0); Array b = a[1]; b = 5; std::vector refa{0, 0, 5, 5, 0, 0}; BOOST_CHECK_EQUAL_COLLECTIONS(a.begin(), a.end(), refa.begin(), refa.end()); Slicer slicer(IPosition(1,0)); Array sliced = b(slicer); sliced = 1982; std::vector refsliced1{0, 0, 1982, 5, 0, 0}; BOOST_CHECK_EQUAL_COLLECTIONS(a.begin(), a.end(), refsliced1.begin(), refsliced1.end()); Array d(IPosition{3}, 42); sliced = d(IPosition{0}, IPosition{0}, IPosition{1}); std::vector refsliced2{0, 0, 42, 5, 0, 0}; BOOST_CHECK_EQUAL_COLLECTIONS(a.begin(), a.end(), refsliced2.begin(), refsliced2.end()); Array e(IPosition{1}, 16); e = d(IPosition{0}, IPosition{0}, IPosition{1}); BOOST_CHECK_EQUAL(e(IPosition(1, 0)), 42); } BOOST_AUTO_TEST_CASE( array_move_case2 ) { Array a(IPosition{2, 3}, 2); Array b(a); Array c(IPosition{2, 3}, 3); c = std::move(a); IPosition el{1, 2}; BOOST_CHECK_EQUAL(c(el), 2); BOOST_CHECK_EQUAL(b(el), 2); // Since the semantics are that operator= does copy value assignment, // b should not change when we change c c(el) = 4; BOOST_CHECK_EQUAL(b(el), 2); BOOST_CHECK_EQUAL(c(el), 4); } BOOST_AUTO_TEST_CASE( array_move_case3 ) { int storage = 1988; Array a(IPosition{1, 1}, &storage, SHARE); Array b(IPosition{1, 1}, 3); b = std::move(a); IPosition el{0,0}; BOOST_CHECK_EQUAL(storage, 1988); BOOST_CHECK_EQUAL(a(el), 1988); BOOST_CHECK_EQUAL(b(el), 1988); b(el) = 4; BOOST_CHECK_EQUAL(storage, 1988); BOOST_CHECK_EQUAL(a(el), 1988); BOOST_CHECK_EQUAL(b(el), 4); storage = 0; BOOST_CHECK_EQUAL(storage, 0); BOOST_CHECK_EQUAL(a(el), 0); BOOST_CHECK_EQUAL(b(el), 4); } BOOST_AUTO_TEST_CASE( vector_move_case2 ) { Vector a(IPosition{6}, 2); Vector b(a); Vector c(IPosition{6}, 3); c = std::move(a); BOOST_CHECK_EQUAL(c[5], 2); BOOST_CHECK_EQUAL(b[5], 2); // Since the semantics are that operator= does copy value assignment, // b should not change when we change c c[5] = 4; BOOST_CHECK_EQUAL(b[5], 2); BOOST_CHECK_EQUAL(c[5], 4); } BOOST_AUTO_TEST_CASE( array_swap ) { IPosition sa{2,3}, sb{4,4,4}; Array a(sa, 8), b(sb, 1982), c, d(sa, 37); casacore::swap(a, b); casacore::swap(b, c); BOOST_CHECK_EQUAL(a.shape(), sb); BOOST_CHECK(allEQ(a, 1982)); BOOST_CHECK(b.empty()); BOOST_CHECK_EQUAL(c.shape(), sa); BOOST_CHECK(allEQ(c, 8)); std::swap(c, d); // Will use moves & temporary, but should still work. BOOST_CHECK_EQUAL(c.shape(), sa); BOOST_CHECK(allEQ(c, 37)); } BOOST_AUTO_TEST_CASE( array_can_hold_noncopyable_objects ) { // Because Array now moves objects when possible, it can also store // non-copyable objects such as std::unique_ptr Array> a, b, c; //a = Array>(IPosition{2,3}, nullptr); // TODO this could be made possible but isn't yet a = Array>(IPosition{2,3}); IPosition pos{1, 2}; a(pos) = std::unique_ptr(new int(42)); b = std::move(a); BOOST_CHECK_EQUAL(*b(pos), 42); c.reference(b); BOOST_CHECK_EQUAL(*c(pos), 42); BOOST_CHECK_THROW(a.assign(b), ArrayError); } BOOST_AUTO_TEST_CASE( vector_can_hold_noncopyable_objects ) { Vector> a, b, c; a = Vector>(2); a[1] = std::unique_ptr(new int(42)); b = std::move(a); BOOST_CHECK_EQUAL(*b[1], 42); c.reference(b); BOOST_CHECK_EQUAL(*c[1], 42); BOOST_CHECK_THROW(a.assign(b), ArrayError); } BOOST_AUTO_TEST_CASE( vector_range_constructor ) { std::vector stdvec1{1, 2, 3, 4, 5}; Vector v1(stdvec1.begin(), stdvec1.end()); BOOST_CHECK_EQUAL_COLLECTIONS(v1.begin(), v1.end(), stdvec1.begin(), stdvec1.end()); std::vector stdvec2{"a", "b"}; Vector v2(stdvec2.begin(), stdvec2.end()); BOOST_CHECK_EQUAL_COLLECTIONS(v2.begin(), v2.end(), stdvec2.begin(), stdvec2.end()); std::vector stdvec3{37, 38, 39, 40, 41}; v1 = Vector(stdvec3.begin(), stdvec3.end()); BOOST_CHECK_EQUAL_COLLECTIONS(v1.begin(), v1.end(), stdvec3.begin(), stdvec3.end()); Vector v3(v2.begin(), v2.end()); BOOST_CHECK_EQUAL_COLLECTIONS(v2.begin(), v2.end(), v3.begin(), v3.end()); Vector v4; int *a=nullptr, *b=nullptr; v4 = Vector(a, b); BOOST_CHECK_EQUAL_COLLECTIONS(v4.begin(), v4.end(), a, b); v1.resize(); } BOOST_AUTO_TEST_CASE( array_to_string ) { Array a(IPosition{2, 3}, 2); a(IPosition{0, 1}) = 1; BOOST_CHECK_EQUAL(to_string(a), "Axis Lengths: [2, 3] (NB: Matrix in Row/Column order)\n" "[2, 1, 2\n" " 2, 2, 2]\n"); } BOOST_AUTO_TEST_CASE( masked_array_move_constructor ) { Array arr(IPosition{2, 3}, 37); LogicalArray mask(IPosition{2, 3}, false); mask(IPosition{0, 1}) = true; MaskedArray ma1(arr, mask); MaskedArray ma2(std::move(ma1)); std::vector arrReference{37, 37, 37, 37, 37, 37}; BOOST_CHECK_EQUAL_COLLECTIONS(ma2.getArray().begin(), ma2.getArray().end(), arrReference.begin(), arrReference.end()); std::vector maskReference{false, false, true, false, false, false}; BOOST_CHECK_EQUAL_COLLECTIONS(ma2.getMask().begin(), ma2.getMask().end(), maskReference.begin(), maskReference.end()); } BOOST_AUTO_TEST_CASE( masked_array_move_assignment ) { Array arr(IPosition{2, 3}, 37); LogicalArray mask(IPosition{2, 3}, false); mask(IPosition{0, 1}) = true; MaskedArray ma1(arr, mask), ma2, ma3; ma2 = ma1; ma3 = std::move(ma1); BOOST_CHECK_EQUAL_COLLECTIONS(ma2.getArray().begin(), ma2.getArray().end(), ma3.getArray().begin(), ma3.getArray().end()); BOOST_CHECK_EQUAL_COLLECTIONS(ma2.getMask().begin(), ma2.getMask().end(), ma3.getMask().begin(), ma3.getMask().end()); } BOOST_AUTO_TEST_CASE( masked_array_assign_rvalue_array ) { MaskedArray ma; IPosition ip{2, 3}; ma = Array(ip, 37); BOOST_CHECK_EQUAL(ma.shape(), ip); for(size_t i=0; i!=6; ++i) { BOOST_CHECK_EQUAL(ma.getArray()(IPosition(2, i/3, i%3)), 37); BOOST_CHECK_EQUAL(ma.getMask()(IPosition(2, i/3, i%3)), true); } } BOOST_AUTO_TEST_CASE( pointer_vector ) { // Testing this because it gave compiling errors at some point // Vector(3, 0) was allowed, but is no longer, because 0 is not implicitly convertable to a pointer // Hence now we need this: Vector*> ptrArray(3, nullptr); for(auto iter=ptrArray.begin(); iter!=ptrArray.end(); ++iter) BOOST_CHECK(*iter == nullptr); } BOOST_AUTO_TEST_CASE( bool_vector_constructor ) { // The bool Vector constructor that takes a std::vector is specialized because // std::vector is different than other std::vectors. This is tested here. std::vector stdv{true, false, true}; Vector v(stdv); BOOST_CHECK_EQUAL_COLLECTIONS(stdv.begin(), stdv.end(), v.begin(), v.end()); } BOOST_AUTO_TEST_CASE( cube_move_constructor ) { IPosition ip{1, 2, 3}; Cube a(ip, 42); Cube b(std::move(a)); Cube ref(ip, 42); BOOST_CHECK_EQUAL(b.shape(), ip); BOOST_CHECK_EQUAL_COLLECTIONS(b.begin(), b.end(), ref.begin(), ref.end()); Cube c(std::move(a)); Cube empty; BOOST_CHECK_EQUAL_COLLECTIONS(c.begin(), c.end(), empty.begin(), empty.end()); BOOST_CHECK_THROW(Cube(Array(IPosition(5, 0))), std::exception); Cube d(Vector(3, 17)); BOOST_CHECK_EQUAL(d.shape(), IPosition(3, 3, 1, 1)); std::vector dref{17, 17, 17}; BOOST_CHECK_EQUAL_COLLECTIONS(d.begin(), d.end(), dref.begin(), dref.end()); } BOOST_AUTO_TEST_CASE( cube_move_assign ) { IPosition ip{1, 2, 3}; Cube a(ip, 42); Cube b; b = std::move(a); Cube ref(ip, 42); BOOST_CHECK_EQUAL(b.shape(), ip); BOOST_CHECK_EQUAL_COLLECTIONS(b.begin(), b.end(), ref.begin(), ref.end()); Cube c(IPosition{3,2,1}, 7); // incorrect shape BOOST_CHECK_THROW(c = std::move(b), std::runtime_error); BOOST_CHECK_EQUAL(b.shape(), ip); BOOST_CHECK_EQUAL(b(0,0,0), 42); BOOST_CHECK_EQUAL(c.shape(), IPosition(3,3,2,1)); BOOST_CHECK_EQUAL(c(0,0,0), 7); Cube d; Vector e(IPosition{3}, 1982); d = std::move(e); BOOST_CHECK_EQUAL(d.shape(), IPosition(3, 3, 1, 1)); BOOST_CHECK_EQUAL(d(0,0,0), 1982); } BOOST_AUTO_TEST_CASE( matrix_move_constructor ) { IPosition ip{2, 3}; Matrix a(ip, 42); Matrix b(std::move(a)); Matrix ref(ip, 42); BOOST_CHECK_EQUAL(b.shape(), ip); BOOST_CHECK_EQUAL_COLLECTIONS(b.begin(), b.end(), ref.begin(), ref.end()); Matrix c(std::move(a)); Matrix empty; BOOST_CHECK_EQUAL_COLLECTIONS(c.begin(), c.end(), empty.begin(), empty.end()); BOOST_CHECK_THROW(Matrix(Array(IPosition(3))), std::exception); Matrix d(Vector(3, 17)); BOOST_CHECK_EQUAL(d.shape(), IPosition(2, 3, 1)); std::vector dref{17, 17, 17}; BOOST_CHECK_EQUAL_COLLECTIONS(d.begin(), d.end(), dref.begin(), dref.end()); } BOOST_AUTO_TEST_CASE( matrix_move_assign ) { IPosition ip{2, 3}; Matrix a(ip, 42); Matrix b; b = std::move(a); Matrix ref(ip, 42); BOOST_CHECK_EQUAL(b.shape(), ip); BOOST_CHECK_EQUAL_COLLECTIONS(b.begin(), b.end(), ref.begin(), ref.end()); Matrix c(IPosition{3,2}, 7); // incorrect shape BOOST_CHECK_THROW(c = std::move(b), std::runtime_error); BOOST_CHECK_EQUAL(b.shape(), ip); BOOST_CHECK_EQUAL(b(0,0), 42); BOOST_CHECK_EQUAL(c.shape(), IPosition(2,3,2)); BOOST_CHECK_EQUAL(c(0,0), 7); Matrix d; Vector e(IPosition{3}, 1982); d = std::move(e); BOOST_CHECK_EQUAL(d.shape(), IPosition(2, 3, 1)); BOOST_CHECK_EQUAL(d(0,0), 1982); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tCube.cc000066400000000000000000000107031476623553700200250ustar00rootroot00000000000000#include #include #include #include "../Cube.h" using namespace casacore; BOOST_AUTO_TEST_SUITE(cube_class) // Cube tests BOOST_AUTO_TEST_CASE( initialize ) { Cube c(3,3,3); c = 3; for (int k=0; k <= 2; k++) for (int j=0; j <= 2; j++) for (int i=0; i <= 2; i++) BOOST_CHECK(c(i,j,k) == 3); for (int k=0; k <= 2; k++) BOOST_CHECK(allEQ (c.xyPlane(k), 3)); } BOOST_AUTO_TEST_CASE( copy_constructor ) { Cube c(3,3,3); // Check copy ctor Cube c2(c); c(1,1,1) = -3; BOOST_CHECK_EQUAL(c2(1,1,1), -3); } BOOST_AUTO_TEST_CASE( assignment ) { Cube c(3,3,3), c3; c3.assign_conforming( c ); BOOST_CHECK(allEQ (c3, c)); Cube y1(5, 6, 7, 4); BOOST_CHECK (allEQ(y1, 4)); } BOOST_AUTO_TEST_CASE( slice1 ) { Cube c(3,3,3), c3 = c; // slice BOOST_CHECK(allEQ (c3 (Slice(0,2), Slice(1,2), 1), c (Slice(0,2), Slice(1,2), 1))); } BOOST_AUTO_TEST_CASE( slice2 ) { Cube c(3,3,3), c3 = c; Cube c4(c3(Slice(0,2), Slice(1,2), 1)); IPosition c4shape(c4.Array::shape()); BOOST_CHECK_EQUAL(c4.nelements(), 4); BOOST_CHECK_EQUAL(c4shape(2), 1); } BOOST_AUTO_TEST_CASE( plane ) { Cube c(3,3,3,3); // middle plane IPosition blc({0, 0, 1}), trc({2, 2, 1}); c(blc, trc) = 11; BOOST_CHECK(allEQ (c.xyPlane(1), 11)); BOOST_CHECK(allEQ (c.xyPlane(0), 3)); BOOST_CHECK(allEQ (c.xyPlane(2), 3)); } BOOST_AUTO_TEST_CASE( index ) { Cube c(3,3,3,3); IPosition blc({0, 0, 1}), trc({2, 2, 1}); c(blc, trc) = 11; Array cinx (c[1]); BOOST_CHECK (allEQ (cinx, c.xyPlane(1))); cinx.reference (cinx[0]); BOOST_CHECK_EQUAL (cinx.shape(), c.shape().getFirst(1)); cinx.reference (cinx[0]); BOOST_CHECK_EQUAL (cinx.shape(), IPosition(1,1)); cinx.reference (cinx[0]); BOOST_CHECK_EQUAL (cinx.shape(), IPosition(1,1)); BOOST_CHECK (allEQ (cinx, 11)); } BOOST_AUTO_TEST_CASE( init_from_data ) { IPosition shape(3, 2, 2, 2); std::unique_ptr> values(new std::vector(shape.product())); Cube c(shape, values->data(), COPY); values.reset(); c.resize(IPosition(3, 2, 3, 2), false); c.resize(2, 4, 4, false); BOOST_CHECK(true); } BOOST_AUTO_TEST_CASE( multi_dimensional_copy ) { // Test the copy ctor for arrays with !1 dimension. Array arr; Cube cub(arr); BOOST_CHECK (cub.ndim()==3 && cub.nelements()==0); BOOST_CHECK (cub.shape() == IPosition(3,0)); } BOOST_AUTO_TEST_CASE( non_degenerate ) { // Test if a non-degerate Cube throws an exception. Cube c1(IPosition(3,1,2,3)); Cube cr; BOOST_CHECK_THROW(cr.nonDegenerate(c1), std::exception); cr.nonDegenerate(c1, 1); BOOST_CHECK (cr.shape() == IPosition(3,1,2,3)); } BOOST_AUTO_TEST_CASE( uninitialized_constructor_a ) { Cube y1(5, 4, 3, Cube::uninitialized); BOOST_CHECK_EQUAL (y1.shape()[0], 5); BOOST_CHECK_EQUAL (y1.shape()[1], 4); BOOST_CHECK_EQUAL (y1.shape()[2], 3); y1 = 2; BOOST_CHECK (allEQ(y1, 2)); } BOOST_AUTO_TEST_CASE( uninitialized_constructor_b ) { Cube y1(IPosition{5, 4, 3}, Cube::uninitialized); BOOST_CHECK_EQUAL (y1.shape()[0], 5); BOOST_CHECK_EQUAL (y1.shape()[1], 4); BOOST_CHECK_EQUAL (y1.shape()[2], 3); y1 = 2; BOOST_CHECK (allEQ(y1, 2)); } BOOST_AUTO_TEST_CASE( assign_unmatched ) { Cube lhs(IPosition{10, 1, 1}, 1.0f); Vector rhs(10, 13.0f); lhs = rhs; std::vector ref(10, 13.0f); BOOST_CHECK_EQUAL_COLLECTIONS (lhs.begin(), lhs.end(), ref.begin(), ref.end()); BOOST_CHECK_EQUAL_COLLECTIONS (lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); } BOOST_AUTO_TEST_CASE( move_assign_unmatched ) { Cube lhs(IPosition{10, 1, 1}, 1.0f); Vector rhs(10, 27.0f); lhs = std::move(rhs); std::vector ref(10, 27.0f); BOOST_CHECK_EQUAL_COLLECTIONS (lhs.begin(), lhs.end(), ref.begin(), ref.end()); } BOOST_AUTO_TEST_CASE( assign_from_empty_array ) { Cube cube; cube = Array(); BOOST_CHECK_EQUAL( cube.shape().size(), 3); BOOST_CHECK_EQUAL( cube.shape(), (IPosition{0,0,0}) ); } BOOST_AUTO_TEST_CASE( assign_from_empty_vector ) { Cube cube; cube = Vector(); BOOST_CHECK_EQUAL( cube.shape().size(), 3); BOOST_CHECK_EQUAL( cube.shape(), (IPosition{0,0,0}) ); } BOOST_AUTO_TEST_CASE( assign_from_empty_matrix ) { Cube cube; cube = Matrix(); BOOST_CHECK_EQUAL( cube.shape().size(), 3); BOOST_CHECK_EQUAL( cube.shape(), (IPosition{0,0,0}) ); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tDiagonal.cc000066400000000000000000000237501476623553700206730ustar00rootroot00000000000000//# tDiagonal.cc: Test program for the Array diagonals function //# Copyright (C) 2012 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include "../IPosition.h" #include "../Array.h" #include "../ArrayMath.h" //#include "../ArrayIO.h" #include "../MatrixIter.h" #include #include #include using namespace casacore; BOOST_AUTO_TEST_SUITE(diagonal) // Get the diagonals directly from the 4-dim array. // The order of the values is the same as when using Array::diagonals. Vector getDiag (const Array& arr, int axis, int off) { const IPosition& shp = arr.shape(); int dlen = shp[axis] - abs(off); Vector vec(arr.size() / shp[axis] / shp[axis] * dlen); int* vecp = vec.data(); int i[4]; for (i[3]=0; i[3]& arr, size_t axis) { const IPosition& shp = arr.shape(); // Determine the size of the axes before the first diagonals axis. // This is the step size to be used in the MatrixIterator loop. int step = 1; for (size_t i=0; i darr = arr.diagonals(axis, off); // Copy them to a consecutive array (to see if copy works fine). Array carr (darr.copy()); // Also get them directly for comparison. Vector diags = getDiag(arr, axis, off); ///cout <<"diag steps="<< darr.steps()< diter(darr, axis); ReadOnlyVectorIterator citer(carr, axis); ReadOnlyMatrixIterator miter(arr, axis, axis+1); for (; !miter.pastEnd(); miter.next(), diter.next(), citer.next()) { Matrix marr(miter.matrix()); ///cout << marr; Vector dmarr(marr.diagonal(off)); ///cout << dmarr << endl; ///cout << "slice="<& darr, const Array& arr, const std::vector& values) { // Compare the diagonals of axis 1. // Both directly and using Array::iterator to see if that works fine. std::vector::const_iterator valIter = values.begin(); for (auto darrVal : darr) { BOOST_CHECK_EQUAL( darrVal , *valIter); ++valIter; } // Also print the diagonals using getDiag and directly. // Both for main diagonal and for other diagonals. Vector getDiagRes = getDiag(arr, 1, 0); BOOST_CHECK_EQUAL_COLLECTIONS(getDiagRes.begin(), getDiagRes.end(), darr.begin(), darr.end()); std::vector arrDiagValues{ arr(IPosition(4,0,0,1,0)), arr(IPosition(4,1,0,1,0)), arr(IPosition(4,0,1,2,0)), arr(IPosition(4,1,1,2,0)), arr(IPosition(4,0,0,1,1)), arr(IPosition(4,1,0,1,1)), arr(IPosition(4,0,1,2,1)), arr(IPosition(4,1,1,2,1)) }; getDiagRes.assign( getDiag(arr, 1, 1) ); BOOST_CHECK_EQUAL_COLLECTIONS(getDiagRes.begin(), getDiagRes.end(), arrDiagValues.begin(), arrDiagValues.end()); BOOST_CHECK_EQUAL_COLLECTIONS(getDiagRes.begin(), getDiagRes.end(), valIter, valIter+8); valIter += 8; arrDiagValues = std::vector{ arr(IPosition(4,0,1,0,0)), arr(IPosition(4,1,1,0,0)), arr(IPosition(4,0,2,1,0)), arr(IPosition(4,1,2,1,0)), arr(IPosition(4,0,1,0,1)), arr(IPosition(4,1,1,0,1)), arr(IPosition(4,0,2,1,1)), arr(IPosition(4,1,2,1,1)) }; getDiagRes.assign( getDiag(arr, 1, -1) ); BOOST_CHECK_EQUAL_COLLECTIONS(getDiagRes.begin(), getDiagRes.end(), arrDiagValues.begin(), arrDiagValues.end()); BOOST_CHECK_EQUAL_COLLECTIONS(getDiagRes.begin(), getDiagRes.end(), valIter, valIter+8); valIter += 8; arrDiagValues = std::vector{ arr(IPosition(4,0,0,2,0)), arr(IPosition(4,1,0,2,0)), arr(IPosition(4,0,0,2,1)), arr(IPosition(4,1,0,2,1)) }; getDiagRes.assign( getDiag(arr, 1, 2) ); BOOST_CHECK_EQUAL_COLLECTIONS(getDiagRes.begin(), getDiagRes.end(), arrDiagValues.begin(), arrDiagValues.end()); BOOST_CHECK_EQUAL_COLLECTIONS(getDiagRes.begin(), getDiagRes.end(), valIter, valIter+4); valIter += 4; arrDiagValues = std::vector{ arr(IPosition(4,0,2,0,0)), arr(IPosition(4,1,2,0,0)), arr(IPosition(4,0,2,0,1)), arr(IPosition(4,1,2,0,1)) }; getDiagRes.assign( getDiag(arr, 1, -2) ); BOOST_CHECK_EQUAL_COLLECTIONS(getDiagRes.begin(), getDiagRes.end(), arrDiagValues.begin(), arrDiagValues.end()); BOOST_CHECK_EQUAL_COLLECTIONS(getDiagRes.begin(), getDiagRes.end(), valIter, valIter+4); valIter += 4; BOOST_CHECK(valIter == values.end()); } BOOST_AUTO_TEST_CASE( full ) { Array arr(IPosition(4,2,3,3,2)); BOOST_CHECK_EQUAL (arr.steps(), IPosition(4,1,2,6,18)); indgen (arr); // Get the diagonals and check if shape and steps are correct. Array darr(arr.diagonals(1)); BOOST_CHECK_EQUAL (darr.shape(), IPosition(3,2,3,2)); BOOST_CHECK_EQUAL (darr.steps(), IPosition(3,1,8,18)); fullCompare (darr, arr, std::vector{ 0, 1, 8, 9, 16, 17, 18, 19, 26, 27, 34, 35, 6, 7, 14, 15, 24, 25, 32, 33, 2, 3, 10, 11, 20, 21, 28, 29, 12, 13, 30, 31, 4, 5, 22, 23 }); checkDiagonals (arr, 1); // Now test if for an array where all axes have the same length, so // we can get the diagonals for all axes. Array arr2(IPosition(4,5,5,5,5)); indgen (arr2); for (int ax=0; ax<3; ++ax) { checkDiagonals (arr2, ax); } } BOOST_AUTO_TEST_CASE( subset ) { // Do the same for a subset (with same shape as testFull) of a larger array. Array farr(IPosition(4,6,6,12,8)); indgen (farr); Array arr (farr(IPosition(4,1,1,1,3), IPosition(4,4,3,7,5), IPosition(4,2,1,3,2))); BOOST_CHECK (arr.steps() == IPosition(4,2,6,36*3,36*12*2)); Array darr(arr.diagonals(1)); BOOST_CHECK (darr.shape() == IPosition(3,2,3,2)); /// BOOST_CHECK (darr.steps() == IPosition(3,1,8,18)); fullCompare (darr, arr, std::vector{ 1339, 1341, 1453, 1455, 1567, 1569, 2203, 2205, 2317, 2319, 2431, 2433, 1447, 1449, 1561, 1563, 2311, 2313, 2425, 2427, 1345, 1347, 1459, 1461, 2209, 2211, 2323, 2325, 1555, 1557, 2419, 2421, 1351, 1353, 2215, 2217 }); checkDiagonals (arr, 1); // Now test if for a subset where all axes have the same length, so // we can get the diagonals for all axes. Array arr2 (farr(IPosition(4,1,0,2,3), IPosition(4,5,4,10,7), IPosition(4,1,1,2,1))); indgen (arr2); for (int ax=0; ax<3; ++ax) { checkDiagonals (arr2, ax); } } BOOST_AUTO_TEST_CASE( diag_subset ) { // Do the same for the diagonals of a subset (with same shape as testFull) // of a larger array. Array farr(IPosition(5,12,12,12,12,12)); indgen (farr); Array sarr (farr(IPosition(5,1,2,3,4,5), IPosition(5,7,8,9,10,11), IPosition(5,2,2,2,2,2))); Array arr (sarr.diagonals(2)); Array darr(arr.diagonals(1)); BOOST_CHECK (darr.shape() == IPosition(3,4,4,4)); // Also get main diagonal in another way. BOOST_CHECK (allEQ(darr, getDiag(arr, 1, 0).reform(darr.shape()))); checkDiagonals (arr, 1); // Do the same to check if a contiguous copy gives the same result. Array arr2(arr.copy()); Array darr2(arr2.diagonals(1)); BOOST_CHECK (allEQ(darr2, darr)); BOOST_CHECK (allEQ(darr2, getDiag(arr2, 1, 0).reform(darr2.shape()))); checkDiagonals (arr2, 1); // Test if all give the same result. for (int ax1=0; ax1<4; ++ax1) { Array arr3 (sarr.diagonals(ax1)); Array arr4 (arr3(IPosition(4,1,1,1,1), IPosition(4,3,3,3,3), IPosition(4,2,2,2,2))); for (int ax=0; ax<3; ++ax) { checkDiagonals (arr3, ax); // Also for a subset of the diagonals. checkDiagonals (arr4, ax); } } } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tExtendSpecifier.cc000066400000000000000000000124361476623553700222350ustar00rootroot00000000000000//# tExtendSpecifier.cc: Test program for class ExtendSpecifier //# Copyright (C) 2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include "../ExtendSpecifier.h" #include "../Slicer.h" #include #include using namespace casacore; BOOST_AUTO_TEST_SUITE(extend_specifier) BOOST_AUTO_TEST_CASE( construct1 ) { IPosition oldShape(4,10,1,3,1); IPosition newShape(5,10,1,5,3,8); ExtendSpecifier spec (oldShape, newShape, IPosition(1,2), IPosition(1,4)); BOOST_CHECK_EQUAL( spec.oldShape(), IPosition(4, 10, 1, 3, 1) ); BOOST_CHECK_EQUAL( spec.newShape(), IPosition(5, 10, 1, 5, 3, 8) ); BOOST_CHECK_EQUAL( spec.newAxes(), IPosition(1, 2) ); BOOST_CHECK_EQUAL( spec.stretchAxes(), IPosition(1, 4) ); BOOST_CHECK_EQUAL( spec.extendAxes(), IPosition(2, 2, 4) ); BOOST_CHECK_EQUAL( spec.oldOldAxes(), IPosition(3, 0, 1, 2) ); BOOST_CHECK_EQUAL( spec.oldNewAxes(), IPosition(3, 0, 1, 3) ); IPosition sh(4,3,4,5,6); IPosition newsh = spec.convertNew(sh); BOOST_CHECK_EQUAL( newsh, IPosition(5, 3, 4, 1, 5, 1) ); Slicer sl(IPosition(5,0,1,2,3,4), IPosition(5,1,2,3,4,5) ); BOOST_CHECK_EQUAL( sl.start(), IPosition(5, 0, 1, 2, 3, 4) ); BOOST_CHECK_EQUAL( sl.length(), IPosition(5, 1, 2, 3, 4, 5) ); BOOST_CHECK_EQUAL( sl.stride(), IPosition(5, 1, 1, 1, 1, 1) ); IPosition shp; Slicer sln = spec.convert (shp, sl); BOOST_CHECK_EQUAL( sln.start(), IPosition(4, 0, 1, 3, 0)); BOOST_CHECK_EQUAL( sln.length(), IPosition(4, 1, 2, 4, 1)); BOOST_CHECK_EQUAL( sln.stride(), IPosition(4, 1, 1, 1, 1)); BOOST_CHECK_EQUAL( shp, IPosition(5, 1, 2, 1, 4, 1)); } BOOST_AUTO_TEST_CASE( construct2 ) { IPosition oldShape(4,10,1,1,3); IPosition newShape(5,10,1,5,3,8); ExtendSpecifier spec (oldShape, newShape, IPosition(1,4), IPosition(1,2)); BOOST_CHECK_EQUAL( spec.oldShape(), IPosition(4, 10, 1, 1, 3) ); BOOST_CHECK_EQUAL( spec.newShape(), IPosition(5, 10, 1, 5, 3, 8) ); BOOST_CHECK_EQUAL( spec.newAxes(), IPosition(1, 4) ); BOOST_CHECK_EQUAL( spec.stretchAxes(), IPosition(1, 2) ); BOOST_CHECK_EQUAL( spec.extendAxes(), IPosition(2, 2, 4) ); BOOST_CHECK_EQUAL( spec.oldOldAxes(), IPosition(3, 0, 1, 3) ); BOOST_CHECK_EQUAL( spec.oldNewAxes(), IPosition(3, 0, 1, 3) ); IPosition sh(4,3,4,5,6); IPosition newsh = spec.convertNew(sh); BOOST_CHECK_EQUAL( newsh, IPosition(5, 3, 4, 1, 6, 1) ); Slicer sl(IPosition(5,0,1,2,3,4), IPosition(5,1,2,3,4,5)); BOOST_CHECK_EQUAL( sl.start(), IPosition(5, 0, 1, 2, 3, 4) ); BOOST_CHECK_EQUAL( sl.length(), IPosition(5, 1, 2, 3, 4, 5) ); BOOST_CHECK_EQUAL( sl.stride(), IPosition(5, 1, 1, 1, 1, 1) ); IPosition shp; Slicer sln = spec.convert (shp, sl); BOOST_CHECK_EQUAL( sln.start(), IPosition(4, 0, 1, 0, 3)); BOOST_CHECK_EQUAL( sln.length(), IPosition(4, 1, 2, 1, 4)); BOOST_CHECK_EQUAL( sln.stride(), IPosition(4, 1, 1, 1, 1)); BOOST_CHECK_EQUAL( shp, IPosition(5, 1, 2, 1, 4, 1)); } BOOST_AUTO_TEST_CASE(erroneous_constructions) { BOOST_CHECK_THROW( ExtendSpecifier(IPosition(1,1), IPosition(1,1), IPosition(), IPosition()), std::runtime_error ); BOOST_CHECK_THROW( ExtendSpecifier(IPosition(1,1), IPosition(1,1), IPosition(), IPosition(1,0)), std::runtime_error ); BOOST_CHECK_THROW( ExtendSpecifier(IPosition(2,2,1), IPosition(2,1,1), IPosition(), IPosition(1,0)), std::runtime_error ); BOOST_CHECK_THROW( ExtendSpecifier(IPosition(2,1,2), IPosition(2,2,3), IPosition(), IPosition(1,0)), std::runtime_error ); BOOST_CHECK_THROW( ExtendSpecifier(IPosition(2,1,2), IPosition(2,2,2), IPosition(), IPosition(2,0,0)), std::runtime_error ); BOOST_CHECK_THROW( ExtendSpecifier spec (IPosition(2,1,2), IPosition(2,2,2), IPosition(1,0), IPosition(1,0)), std::runtime_error ); BOOST_CHECK_THROW( ExtendSpecifier spec (IPosition(2,1,2), IPosition(2,2,2), IPosition(1,0), IPosition()), std::runtime_error ); BOOST_CHECK_THROW( ExtendSpecifier spec (IPosition(2,1,2), IPosition(2,2,2), IPosition(), IPosition(1,-1)), std::runtime_error ); BOOST_CHECK_THROW( ExtendSpecifier spec (IPosition(2,1,2), IPosition(2,2,2), IPosition(), IPosition(1,2)), std::runtime_error ); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tIPosition.cc000066400000000000000000000361671476623553700211000ustar00rootroot00000000000000//# tIPosition.cc: This program tests the IPosition class //# Copyright (C) 1994,1995,1996,1997,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include "../IPosition.h" #include "../Vector.h" #include #include #include using namespace casacore; BOOST_AUTO_TEST_SUITE(iposition) BOOST_AUTO_TEST_CASE( keep_remove ) { IPosition p1(4,2,3,4,5); IPosition p2 (p1.removeAxes(IPosition(2,0,3))); BOOST_CHECK (p2.size()==2 && p2[0]==3 && p2[1]==4); IPosition p3 (p1.keepAxes(IPosition(2,2,1))); BOOST_CHECK (p2.size()==2 && p2[0]==3 && p2[1]==4); } BOOST_AUTO_TEST_CASE( common_operations ) { IPosition ip(3, 0, 1, 2); int nrit = 0; for (IPosition::const_iterator iter=ip.begin(); iter!=ip.end(); iter++) { BOOST_CHECK(*iter == ip[nrit++]); } BOOST_CHECK(nrit == 3); BOOST_CHECK(ip.nelements() == 3); BOOST_CHECK(ip.size() == 3); BOOST_CHECK(!ip.empty()); BOOST_CHECK(ip(0) == 0 && ip(1) == 1 && ip(2) == 2); BOOST_CHECK(ip[0] == 0 && ip[1] == 1 && ip[2] == 2); BOOST_CHECK (ip.last() == 2); BOOST_CHECK (ip.last(1) == 1); BOOST_CHECK (ip.last(2) == 0); std::vector vec(ip.begin(), ip.end()); BOOST_CHECK(vec.size() == 3); BOOST_CHECK(vec[0] == 0 && vec[1] == 1 && vec[2] == 2); ip[2] = 21; BOOST_CHECK(ip(2) == 21); ip(2) = 23; BOOST_CHECK(ip(2) == 23); BOOST_CHECK (ip.last() == 23); ip.last() = 22; BOOST_CHECK(ip(2) == 22); IPosition ip2; BOOST_CHECK(ip2.nelements() == 0); BOOST_CHECK(ip2.size() == 0); BOOST_CHECK(ip2.empty()); ip2 = ip; BOOST_CHECK(ip2 == ip); BOOST_CHECK(ip2(0) == 0 && ip2(1) == 1 && ip2(2) == 22); ip2 += 2; ip2 = ip2 - 1; ip2 -= 1; BOOST_CHECK(ip2(0) == 0 && ip2(1) == 1 && ip2(2) == 22); ip2 = 5; BOOST_CHECK(ip2 == 5); BOOST_CHECK(ip2 == ip2); BOOST_CHECK(ip2 != ip); ip2.resize(10); ip2 = 10; ip.resize(0); ip = ip2; BOOST_CHECK (ip == 10); BOOST_CHECK(ip.nelements() == 10); ip = ip * ip2; BOOST_CHECK(ip == 100); BOOST_CHECK (ip > ip2); BOOST_CHECK (ip >= ip2); BOOST_CHECK (ip2 < ip); BOOST_CHECK (ip2 <= ip); BOOST_CHECK (ip2 != ip); IPosition ip3(5,0,1,2,3,4); nrit = 0; for (IPosition::const_iterator iter=ip3.begin(); iter!=ip3.end(); iter++) { BOOST_CHECK(*iter == ip3[nrit++]); } } BOOST_AUTO_TEST_CASE( construct_from_int_array ) { Vector emptyVec; IPosition emptyPos(emptyVec); BOOST_CHECK(emptyPos.nelements() == 0); Vector filledVec{ 19, 82 }; IPosition filledPos(filledVec); BOOST_CHECK(filledPos.nelements() == 2); BOOST_CHECK(filledPos[0] == 19); BOOST_CHECK(filledPos[1] == 82); Vector bigVec{ 9, 8, 7, 6, 5, 4, 3 }; IPosition bigPos(bigVec); BOOST_CHECK(bigPos.nelements() == 7); for(int i=0; i!=7; ++i) BOOST_CHECK(bigPos[i] == (9-i)); } BOOST_AUTO_TEST_CASE( construct_from_std_vector ) { std::vector emptyVec; IPosition emptyPos(emptyVec); BOOST_CHECK(emptyPos.nelements() == 0); std::vector filledVec{ 19, 82 }; IPosition filledPos(filledVec); BOOST_CHECK(filledPos.nelements() == 2); BOOST_CHECK(filledPos[0] == 19); BOOST_CHECK(filledPos[1] == 82); std::vector bigVec{ 9, 8, 7, 6, 5, 4, 3 }; IPosition bigPos(bigVec); BOOST_CHECK(bigPos.nelements() == 7); for(int i=0; i!=7; ++i) BOOST_CHECK(bigPos[i] == (9-i)); } BOOST_AUTO_TEST_CASE( construct_from_ll_array ) { Vector emptyVec; IPosition emptyPos(emptyVec); BOOST_CHECK(emptyPos.nelements() == 0); Vector filledVec{ 19, 82 }; IPosition filledPos(filledVec); BOOST_CHECK(filledPos.nelements() == 2); BOOST_CHECK(filledPos[0] == 19); BOOST_CHECK(filledPos[1] == 82); Vector bigVec{ 9, 8, 7, 6, 5, 4, 3 }; IPosition bigPos(bigVec); BOOST_CHECK(bigPos.nelements() == 7); for(int i=0; i!=7; ++i) BOOST_CHECK(bigPos[i] == (9-i)); } BOOST_AUTO_TEST_CASE(make) { IPosition d = IPosition::Make(8, 5, 19, 82); BOOST_CHECK_EQUAL(d.nelements(), 4); BOOST_CHECK_EQUAL(d[0], 8); BOOST_CHECK_EQUAL(d[1], 5); BOOST_CHECK_EQUAL(d[2], 19); BOOST_CHECK_EQUAL(d[3], 82); IPosition l = IPosition::Make(9, 8, 7, 6, 5, 4, 3); BOOST_CHECK_EQUAL(l.nelements(), 7); for(int i=0; i!=7; ++i) BOOST_CHECK_EQUAL(l[i], 9-i); } BOOST_AUTO_TEST_CASE( move_exhaustively ) { int i; IPosition ip1; // IPosition() BOOST_CHECK(ip1.nelements() == 0); // nelements() IPosition ip2(5); // IPosition(size_t); BOOST_CHECK(ip2.nelements() == 5); IPosition ip3(5, 0, 1, 2, 3, 4); // IPosition(size_t, int, ...) BOOST_CHECK(ip3.nelements() == 5); const IPosition &rip3 = ip3; for (i=0; i<5; i++) { BOOST_CHECK(ip3(i) == i); // operator()(size_t) BOOST_CHECK(rip3(i) == i); // operator()(size_t) const } IPosition ip4(ip3); // IPosition(const IPosition &) BOOST_CHECK(ip4.nelements() == 5); for (i=0; i<5; i++) { BOOST_CHECK(ip4(i) == i); } ip1 = ip4; // operator=(const IPosition &); BOOST_CHECK(ip1.nelements() == 5); for (i=0; i<5; i++) { BOOST_CHECK(ip1(i) == i); } ip1 = ip1; for (i=0; i<5; i++) { BOOST_CHECK(ip1(i) == i); } ip1 = 1; // operator=(int); BOOST_CHECK(ip1.nelements() == 5); for (i=0; i<5; i++) { BOOST_CHECK(ip1(i) == 1); } ip1 = ip4; BOOST_CHECK(ip1.nelements() == 5); for (i=0; i<5; i++) { BOOST_CHECK(ip1(i) == i); } ip1.resize(10); // resize(size_t) BOOST_CHECK(ip1.nelements() == 10); BOOST_CHECK(ip1.conform(ip4) == false); // conform BOOST_CHECK(ip4.conform(ip3) == true); IPosition ipf (2,3,4); IPosition ipl (3,14,15,16); IPosition ipr (5,3,4,14,15,16); IPosition ipp0 (ipf); ipp0.append (ipl); BOOST_CHECK (ipp0 == ipr); IPosition ipp1 (ipl); ipp1.prepend (ipf); BOOST_CHECK (ipp1 == ipr); BOOST_CHECK (ipf.concatenate (ipl) == ipr); IPosition ipgf = ipr.getFirst (2); BOOST_CHECK (ipgf == ipf); IPosition ipgl = ipr.getLast (3); BOOST_CHECK (ipgl == ipl); ipp0.setFirst (ipl); BOOST_CHECK (ipp0 == IPosition(5,14,15,16,15,16)); ipp1.setLast (ipf); BOOST_CHECK (ipp1 == IPosition(5,3,4,14,3,4)); // Member fn arithmetic ip1 = 1; ip1 += 5; BOOST_CHECK(ip1 == 6); ip1 -= 1; BOOST_CHECK(ip1 == 5); ip1 *= 2; BOOST_CHECK(ip1 == 10); ip1 /= 2; BOOST_CHECK(ip1 == 5); ip2.resize(10); ip2 = 1; ip1 += ip2; BOOST_CHECK(ip1 == 6); ip1 -= ip2; BOOST_CHECK(ip1 == 5); ip2 = 2; ip1 *= ip2; BOOST_CHECK(ip1 == 10); ip1 /= ip2; BOOST_CHECK(ip1 == 5); // Global fn arithmetic ip1 = ip1 + ip2; BOOST_CHECK(ip1 == 7); ip1 = ip1 - ip2; BOOST_CHECK(ip1 == 5); ip1 = ip2 * ip1; BOOST_CHECK(ip1 == 10); ip1 = ip1 / ip2; BOOST_CHECK(ip1 == 5); ip1 = ip1 + 1; BOOST_CHECK(ip1 == 6); ip1 = ip1 - 1; BOOST_CHECK(ip1 == 5); ip1 = ip1 * 2; BOOST_CHECK(ip1 == 10); ip1 = ip1 / 2; BOOST_CHECK(ip1 == 5); ip1 = 1 + ip1; BOOST_CHECK(ip1 == 6); ip1 = 1 - ip1; BOOST_CHECK(ip1 == -5); ip1 = -2 * ip1; BOOST_CHECK(ip1 == 10); ip1 = 50 / ip1; BOOST_CHECK(ip1 == 5); // Global fn logicals ip2.resize(ip1.nelements()); ip2 = 6; BOOST_CHECK(ip1 == ip1); BOOST_CHECK((ip1 == ip2) == false); BOOST_CHECK (ip1.isEqual(ip1)); BOOST_CHECK (ip1.isEqual(ip2) == false); BOOST_CHECK(ip1 != ip2); BOOST_CHECK((ip1 != ip1) == false); BOOST_CHECK(ip1 < ip2); BOOST_CHECK((ip1 < ip1) == false); BOOST_CHECK(ip1 <= ip2); BOOST_CHECK((ip1 <= ip1)); ip2(0) = 0; BOOST_CHECK((ip1 <= ip2) == false); BOOST_CHECK(ip1 + 1 > ip1); BOOST_CHECK((ip1 > ip1) == false); BOOST_CHECK((ip2 > ip1) == false); BOOST_CHECK(ip1 + 1 >= ip1); BOOST_CHECK(ip1 >= ip1); BOOST_CHECK((ip2 >= ip1) == false); BOOST_CHECK(ip1 == 5); BOOST_CHECK((ip1 == 6) == false); BOOST_CHECK(5 == ip1); BOOST_CHECK((6 == ip1) == false); BOOST_CHECK(ip1 != 6); BOOST_CHECK((ip1 != 5) == false); BOOST_CHECK(6 != ip1); BOOST_CHECK((5 != ip1) == false); BOOST_CHECK(ip1 < 6); BOOST_CHECK((ip1 < 5) == false); BOOST_CHECK(4 < ip1); BOOST_CHECK((5 < ip1) == false); BOOST_CHECK(ip1 <= 6); BOOST_CHECK(ip1 <= 5); BOOST_CHECK((ip1 <= 4) == false); BOOST_CHECK(4 <= ip1); BOOST_CHECK(5 <= ip1); BOOST_CHECK((6 <= ip1) == false); BOOST_CHECK(ip1 > 4); BOOST_CHECK((ip1 > 5) == false); BOOST_CHECK(ip1 >= 4); BOOST_CHECK((ip1 >= 5)); BOOST_CHECK((ip1 >= 6) == false); BOOST_CHECK(6 > ip1); BOOST_CHECK(( 5 > ip1) == false); BOOST_CHECK(5 >= ip1); BOOST_CHECK((4 >= ip1) == false); IPosition ip7 (ip1.nelements()); IPosition ip8 (ip1.nelements() + 1); ip7 = 0; ip8 = 0; for (size_t ipindex=0; ipindex < ip1.nelements(); ipindex++) { ip7(ipindex) = ip1(ipindex); ip8(ipindex) = ip1(ipindex); } BOOST_CHECK (ip7.isEqual(ip1)); BOOST_CHECK (ip7.isEqual(ip8) == false); BOOST_CHECK (! IPosition(1,1).isEqual (IPosition(), false)); BOOST_CHECK (IPosition(1,1).isEqual (IPosition(), true)); BOOST_CHECK (IPosition(1,1).isEqual (IPosition(1,1), true)); BOOST_CHECK (IPosition(1,1).isEqual (IPosition(2,1,1), true)); BOOST_CHECK (IPosition(2,1,1).isEqual (IPosition(2,1,1), true)); BOOST_CHECK (IPosition(2,1,1).isEqual (IPosition(1,1), true)); BOOST_CHECK (IPosition(6,2,1,1,3,1,4).isEqual (IPosition(3,2,3,4), true)); BOOST_CHECK (IPosition(6,2,1,1,3,1,4).isEqual (IPosition(5,1,1,2,3,4), true)); BOOST_CHECK (IPosition(6,2,1,1,3,1,4).isEqual (IPosition(5,2,3,4,1,1), true)); BOOST_CHECK (IPosition(3,2,3,4).isEqual (IPosition(5,2,1,3,1,4), true)); BOOST_CHECK (IPosition(3,2,3,4).nonDegenerate().isEqual (IPosition(3,2,3,4))); BOOST_CHECK (IPosition(4,1,2,3,4).nonDegenerate().isEqual (IPosition(3,2,3,4))); BOOST_CHECK (IPosition(5,1,1,2,3,4).nonDegenerate().isEqual (IPosition(3,2,3,4))); BOOST_CHECK (IPosition(6,1,1,2,3,1,4).nonDegenerate().isEqual (IPosition(3,2,3,4))); BOOST_CHECK (IPosition(6,1,1,2,3,1,4).nonDegenerate(2).isEqual (IPosition(5,1,1,2,3,4))); } BOOST_AUTO_TEST_CASE(strio) { IPosition ip1{5, 5, 5, 5, 5, 5, 5, 5, 5, 5}; // Use an ostringstream to convert to ASCII and reading it back. // Note that function str "freezes" the buffer in ostringstream, // which means we have to delete it ourselves. // Also note that no terminating 0 is stored by operator<<. std::ostringstream os; // ostream &operator<< os << ip1; std::string string (os.str()); BOOST_CHECK(string == "[5, 5, 5, 5, 5, 5, 5, 5, 5, 5]"); BOOST_CHECK(to_string(ip1) == "[5, 5, 5, 5, 5, 5, 5, 5, 5, 5]"); } BOOST_AUTO_TEST_CASE(exceptions) { // Check out exceptions IPosition ip1(2); IPosition ip2(3); // ip1.conform(ip2) == false ip1 = 5; ip2 = 6; BOOST_CHECK_THROW(ip1+=ip2, std::runtime_error); BOOST_CHECK_THROW(ip1-=ip2, std::runtime_error); BOOST_CHECK_THROW(ip1*=ip2, std::runtime_error); BOOST_CHECK_THROW(ip1/=ip2, std::runtime_error); BOOST_CHECK_THROW((void)(ip1+ip2), std::runtime_error); BOOST_CHECK_THROW((void)(ip1-ip2), std::runtime_error); BOOST_CHECK_THROW((void)(ip1*ip2), std::runtime_error); BOOST_CHECK_THROW((void)(ip1/ip2), std::runtime_error); BOOST_CHECK_THROW((void)(ip1==ip2), std::runtime_error); BOOST_CHECK_THROW((void)(ip1!=ip2), std::runtime_error); BOOST_CHECK_THROW((void)(ip1ip2), std::runtime_error); BOOST_CHECK_THROW((void)(ip1>=ip2), std::runtime_error); // Assignment no longer requires conformance // BOOST_CHECK_THROW((void)(ip1=ip2), std::runtime_error); } // ~IPosition tested implicitly // at end of block BOOST_AUTO_TEST_CASE( product ) { IPosition x; IPosition y(5,1,2,3,4,5); BOOST_CHECK(x.product() == 0 && y.product() == 120); } BOOST_AUTO_TEST_CASE( as_vector ) { Vector vi; IPosition ip(3, 1, 2, 3); IPosition ip2(6, 1, 2, 3, 4, 5, 6); vi = ip.asVector(); BOOST_CHECK(vi(0) == 1 && vi(1) == 2 && vi(2) == 3); vi.resize(6); vi = ip2.asVector(); BOOST_CHECK(vi(0) == 1 && vi(1) == 2 && vi(2) == 3 && vi(3) == 4 && vi(4) == 5 && vi(5) == 6); IPosition ip3(vi); BOOST_CHECK(ip3(0) == 1 && ip3(1) == 2 && ip3(2) == 3 && ip3(3) == 4 && ip3(4) == 5 && ip3(5) == 6); BOOST_CHECK(IPosition(3,1).allOne()); BOOST_CHECK(! IPosition(3,0).allOne()); } BOOST_AUTO_TEST_CASE( as_std_vector ) { std::vector vi; IPosition ip(3, 1, 2, 3); IPosition ip2(6, 1, 2, 3, 4, 5, 6); vi = ip.asStdVector(); BOOST_CHECK(vi[0] == 1 && vi[1] == 2 && vi[2] == 3); vi.resize(6); vi = ip2.asStdVector(); BOOST_CHECK(vi[0] == 1 && vi[1] == 2 && vi[2] == 3 && vi[3] == 4 && vi[4] == 5 && vi[5] == 6); IPosition ip3(vi); BOOST_CHECK(ip3[0] == 1 && ip3[1] == 2 && ip3[2] == 3 && ip3[3] == 4 && ip3[4] == 5 && ip3[5] == 6); } BOOST_AUTO_TEST_CASE( index_operator ) { // operator()(IPostion) tests IPosition ipos(4,11,12,13,14); BOOST_CHECK(ipos(IPosition(4, 0,2,1,3)) == IPosition(4, 11,13,12,14)); BOOST_CHECK(ipos(IPosition(3, 2,2,1)) == IPosition(3, 13,13,12)); // test IPOsitionComparator IPosition ip0(2, 0); IPosition ip1(2, 1); IPosition ip2(3, 1); IPosition ip3(2, 0, 1); IPosition ip4(2, 1, 0); // creating a map with IPosition keys without the comparator // doesn't work as we would like std::map mymap; mymap[ip0] = 0; mymap[ip1] = 1; bool thrown = false; try { // throws exception because size not equal mymap[ip2] = 2; } catch (const std::runtime_error& x) { thrown = true; } BOOST_CHECK(thrown); mymap[ip3] = 3; mymap[ip4] = 4; // one would think the map now has four elements, but no, it only has two BOOST_CHECK(mymap.size() != 4); // so use the comparator and things make sense std::map goodmap; goodmap[ip0] = 0; goodmap[ip1] = 1; goodmap[ip2] = 2; goodmap[ip3] = 3; goodmap[ip4] = 4; BOOST_CHECK(goodmap.size() == 5); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tLinAlgebra.cc000066400000000000000000000300151476623553700211450ustar00rootroot00000000000000//# tLinAlgebra.cc: This program tests the linear algebra routines //# Copyright (C) 1994,1995,1996,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include "../Vector.h" #include "../Matrix.h" #include "../MatrixMath.h" #include "../ArrayLogical.h" #include "../ArrayMath.h" #include #include using namespace casacore; BOOST_AUTO_TEST_SUITE(lin_algebra) BOOST_AUTO_TEST_CASE( all ) { Vector foo_int1(3); foo_int1(0)=1; foo_int1(1)=2; foo_int1(2)=3; Vector foo_int2(3); foo_int2(0)=3; foo_int2(1)=2; foo_int2(2)=1; BOOST_CHECK(10==innerProduct(foo_int1,foo_int2)); BOOST_CHECK(3==norm(foo_int1)); Vector foo_int3(3); foo_int3(0)=-4; foo_int3(1)=8; foo_int3(2)=-4; BOOST_CHECK(allEQ(foo_int3,crossProduct(foo_int1,foo_int2))); Matrix Foo_int(3u,3u); Foo_int(0,0)=3; Foo_int(1,0)=6; Foo_int(2,0)=9; Foo_int(0,1)=2; Foo_int(1,1)=4; Foo_int(2,1)=6; Foo_int(0,2)=1; Foo_int(1,2)=2; Foo_int(2,2)=3; BOOST_CHECK(18==normI(Foo_int)); BOOST_CHECK(18==norm1(Foo_int)); Matrix Foo_int_T(3u,3u); Foo_int_T(0,0)=3; Foo_int_T(1,0)=2; Foo_int_T(2,0)=1; Foo_int_T(0,1)=6; Foo_int_T(1,1)=4; Foo_int_T(2,1)=2; Foo_int_T(0,2)=9; Foo_int_T(1,2)=6; Foo_int_T(2,2)=3; BOOST_CHECK(allEQ(Foo_int,transpose(Foo_int_T))); Matrix itemp(1u,3u); itemp.row(0).assign_conforming( foo_int2 ); BOOST_CHECK(allEQ(Foo_int,product(foo_int1,itemp))); foo_int3(0)=10; foo_int3(1)=20; foo_int3(2)=30; BOOST_CHECK(allEQ(foo_int3,product(Foo_int, foo_int1))); Vector foo_float1(3); foo_float1(0)=1.0; foo_float1(1)=2.0; foo_float1(2)=3.0; Vector foo_float2(3); foo_float2(0)=3.0; foo_float2(1)=2.0; foo_float2(2)=1.0; BOOST_CHECK(10.0==innerProduct(foo_float1,foo_float2)); BOOST_CHECK(sqrt(13.999)norm(foo_float1)); Vector foo_float3(3); foo_float3(0)=-4.0; foo_float3(1)=8.0; foo_float3(2)=-4.0; BOOST_CHECK(allEQ(foo_float3, crossProduct(foo_float1,foo_float2))); Matrix Foo_float(3u,3u); Foo_float(0,0)=3.0; Foo_float(1,0)=6.0; Foo_float(2,0)=9.0; Foo_float(0,1)=2.0; Foo_float(1,1)=4.0; Foo_float(2,1)=6.0; Foo_float(0,2)=1.0; Foo_float(1,2)=2.0; Foo_float(2,2)=3.0; BOOST_CHECK(18==normI(Foo_float)); BOOST_CHECK(18==norm1(Foo_float)); Matrix Foo_float_T(3u,3u); Foo_float_T(0,0)=3.0; Foo_float_T(1,0)=2.0; Foo_float_T(2,0)=1.0; Foo_float_T(0,1)=6.0; Foo_float_T(1,1)=4.0; Foo_float_T(2,1)=2.0; Foo_float_T(0,2)=9.0; Foo_float_T(1,2)=6.0; Foo_float_T(2,2)=3.0; BOOST_CHECK(allEQ(Foo_float,transpose(Foo_float_T))); Matrix ftemp(1u,3u); ftemp.row(0).assign_conforming( foo_float2 ); BOOST_CHECK(allEQ(Foo_float,product(foo_float1,ftemp))); foo_float3(0)=10.0; foo_float3(1)=20.0; foo_float3(2)=30.0; BOOST_CHECK(allEQ(foo_float3,product(Foo_float, foo_float1))); Vector foo_double1(3); foo_double1(0)=1.0; foo_double1(1)=2.0; foo_double1(2)=3.0; Vector foo_double2(3); foo_double2(0)=3.0; foo_double2(1)=2.0; foo_double2(2)=1.0; BOOST_CHECK(10.0==innerProduct(foo_double1,foo_double2)); BOOST_CHECK(sqrt(13.99999999)norm(foo_double1)); Vector foo_double3(3); foo_double3(0)=-4.0; foo_double3(1)=8.0; foo_double3(2)=-4.0; BOOST_CHECK(allEQ(foo_double3, crossProduct(foo_double1,foo_double2))); Matrix Foo_double(3u,3u); Foo_double(0,0)=3.0; Foo_double(1,0)=6.0; Foo_double(2,0)=9.0; Foo_double(0,1)=2.0; Foo_double(1,1)=4.0; Foo_double(2,1)=6.0; Foo_double(0,2)=1.0; Foo_double(1,2)=2.0; Foo_double(2,2)=3.0; BOOST_CHECK(18.0==normI(Foo_double)); BOOST_CHECK(18.0==norm1(Foo_double)); Matrix Foo_double_T(3u,3u); Foo_double_T(0,0)=3.0; Foo_double_T(1,0)=2.0; Foo_double_T(2,0)=1.0; Foo_double_T(0,1)=6.0; Foo_double_T(1,1)=4.0; Foo_double_T(2,1)=2.0; Foo_double_T(0,2)=9.0; Foo_double_T(1,2)=6.0; Foo_double_T(2,2)=3.0; BOOST_CHECK(allEQ(Foo_double,transpose(Foo_double_T))); Matrix dtemp(1u,3u); dtemp.row(0).assign_conforming( foo_double2 ); BOOST_CHECK(allEQ(Foo_double,product(foo_double1,dtemp))); foo_double3(0)=10.0; foo_double3(1)=20.0; foo_double3(2)=30.0; BOOST_CHECK(allEQ(foo_double3, product(Foo_double, foo_double1))); Vector> foo_Complex1(3); foo_Complex1(0)=std::complex(1.0,3.0); foo_Complex1(1)=std::complex(2.0,2.0); foo_Complex1(2)=std::complex(3.0,1.0); Vector> foo_Complex2(3); foo_Complex2(0)=std::complex(3.0,1.0); foo_Complex2(1)=std::complex(2.0,2.0); foo_Complex2(2)=std::complex(1.0,3.0); BOOST_CHECK(std::complex(20.0,0)==innerProduct(foo_Complex1,foo_Complex2)); BOOST_CHECK(sqrt(27.99999) < norm(foo_Complex1)); BOOST_CHECK(sqrt(28.00001) > norm(foo_Complex1)); Vector> foo_Complex3(3); foo_Complex3(0) = std::complex(-8.0,0); foo_Complex3(1) = std::complex(16.0,0); foo_Complex3(2) = std::complex(-8.0,0); BOOST_CHECK(allEQ(foo_Complex3, crossProduct(foo_Complex1,foo_Complex2))); Matrix> Foo_Complex(3u,3u); Foo_Complex(0,0)=std::complex(0,10.0); Foo_Complex(1,0)=std::complex(4.0,8.0); Foo_Complex(2,0)=std::complex(8.0,6.0); Foo_Complex(0,1)=std::complex(-4.0,8.0); Foo_Complex(1,1)=std::complex(0,8.0); Foo_Complex(2,1)=std::complex(4.0,8.0); Foo_Complex(0,2)=std::complex(-8.0,6.0); Foo_Complex(1,2)=std::complex(-4.0,8.0); Foo_Complex(2,2)=std::complex(0,10.0); BOOST_CHECK(28.944271 < normI(Foo_Complex)); BOOST_CHECK(28.944272 > normI(Foo_Complex)); BOOST_CHECK(28.944271 < norm1(Foo_Complex)); BOOST_CHECK(28.944272 > norm1(Foo_Complex)); Matrix> Foo_Complex_bar(3u,3u); Foo_Complex_bar(0,0)=std::complex(0,-10.0); Foo_Complex_bar(1,0)=std::complex(-4.0,-8.0); Foo_Complex_bar(2,0)=std::complex(-8.0,-6.0); Foo_Complex_bar(0,1)=std::complex(4.0,-8.0); Foo_Complex_bar(1,1)=std::complex(0,-8.0); Foo_Complex_bar(2,1)=std::complex(-4.0,-8.0); Foo_Complex_bar(0,2)=std::complex(8.0,-6.0); Foo_Complex_bar(1,2)=std::complex(4.0,-8.0); Foo_Complex_bar(2,2)=std::complex(0,-10.0); BOOST_CHECK(allEQ(Foo_Complex, conj(transpose(Foo_Complex_bar)))); BOOST_CHECK(allEQ(Foo_Complex,adjoint(Foo_Complex_bar))); Matrix> Ctemp(1u,3u); Ctemp.row(0).assign_conforming(foo_Complex2); BOOST_CHECK(allEQ(Foo_Complex,product(foo_Complex1,Ctemp))); foo_Complex3(0)=std::complex(-84.0,28.0); foo_Complex3(1)=std::complex(-56.0,56.0); foo_Complex3(2)=std::complex(-28.0,84.0); BOOST_CHECK(allEQ(foo_Complex3, product(Foo_Complex, foo_Complex1))); Vector> foo_DComplex1(3); foo_DComplex1(0)=std::complex(1.0,3.0); foo_DComplex1(1)=std::complex(2.0,2.0); foo_DComplex1(2)=std::complex(3.0,1.0); Vector> foo_DComplex2(3); foo_DComplex2(0)=std::complex(3.0,1.0); foo_DComplex2(1)=std::complex(2.0,2.0); foo_DComplex2(2)=std::complex(1.0,3.0); BOOST_CHECK(std::complex(20.0,0)==innerProduct(foo_DComplex1,foo_DComplex2)); BOOST_CHECK(sqrt(27.99999) < norm(foo_DComplex1)); BOOST_CHECK(sqrt(28.00001) > norm(foo_DComplex1)); Vector> foo_DComplex3(3); foo_DComplex3(0) = std::complex(-8.0,0); foo_DComplex3(1) = std::complex(16.0,0); foo_DComplex3(2) = std::complex(-8.0,0); BOOST_CHECK(allEQ(foo_DComplex3, crossProduct(foo_DComplex1,foo_DComplex2))); Matrix> Foo_DComplex(3u,3u); Foo_DComplex(0,0)=std::complex(0,10.0); Foo_DComplex(1,0)=std::complex(4.0,8.0); Foo_DComplex(2,0)=std::complex(8.0,6.0); Foo_DComplex(0,1)=std::complex(-4.0,8.0); Foo_DComplex(1,1)=std::complex(0,8.0); Foo_DComplex(2,1)=std::complex(4.0,8.0); Foo_DComplex(0,2)=std::complex(-8.0,6.0); Foo_DComplex(1,2)=std::complex(-4.0,8.0); Foo_DComplex(2,2)=std::complex(0,10.0); BOOST_CHECK(28.9442719099 < normI(Foo_DComplex)); BOOST_CHECK(28.94427191 > normI(Foo_DComplex)); BOOST_CHECK(28.9442719099 < norm1(Foo_DComplex)); BOOST_CHECK(28.94427191 > norm1(Foo_DComplex)); Matrix> Foo_DComplex_bar(3u,3u); Foo_DComplex_bar(0,0)=std::complex(0,-10.0); Foo_DComplex_bar(1,0)=std::complex(-4.0,-8.0); Foo_DComplex_bar(2,0)=std::complex(-8.0,-6.0); Foo_DComplex_bar(0,1)=std::complex(4.0,-8.0); Foo_DComplex_bar(1,1)=std::complex(0,-8.0); Foo_DComplex_bar(2,1)=std::complex(-4.0,-8.0); Foo_DComplex_bar(0,2)=std::complex(8.0,-6.0); Foo_DComplex_bar(1,2)=std::complex(4.0,-8.0); Foo_DComplex_bar(2,2)=std::complex(0,-10.0); BOOST_CHECK(allEQ(Foo_DComplex, conj(transpose(Foo_DComplex_bar)))); BOOST_CHECK(allEQ(Foo_DComplex,adjoint(Foo_DComplex_bar))); Matrix> DCtemp(1u,3u); DCtemp.row(0).assign_conforming(foo_DComplex2); BOOST_CHECK(allEQ(Foo_DComplex,product(foo_DComplex1,DCtemp))); foo_DComplex3(0)=std::complex(-84.0,28.0); foo_DComplex3(1)=std::complex(-56.0,56.0); foo_DComplex3(2)=std::complex(-28.0,84.0); BOOST_CHECK(allEQ(foo_DComplex3, product(Foo_DComplex, foo_DComplex1))); Matrix foo_a(2u,2u),foo_b(2u,2u),foo_dPab(4u,4u); foo_a(0,0)=1; foo_a(0,1)=2; foo_a(1,0)=3; foo_a(1,1)=4; foo_b(0,0)=4; foo_b(0,1)=3; foo_b(1,0)=2; foo_b(1,1)=1; foo_dPab(0,0)=4; foo_dPab(0,1)=3; foo_dPab(0,2)=8; foo_dPab(0,3)=6; foo_dPab(1,0)=2; foo_dPab(1,1)=1; foo_dPab(1,2)=4; foo_dPab(1,3)=2; foo_dPab(2,0)=12; foo_dPab(2,1)=9; foo_dPab(2,2)=16; foo_dPab(2,3)=12; foo_dPab(3,0)=6; foo_dPab(3,1)=3; foo_dPab(3,2)=8; foo_dPab(3,3)=4; BOOST_CHECK(allEQ(foo_dPab, directProduct(foo_a, foo_b))); double alpha=1, cosa=cos(alpha), sina=sin(alpha); Matrix Rx(3u,3u); Rx(0,0) = 1; Rx(0,1) = 0; Rx(0,2) = 0; Rx(1,0) = 0; Rx(1,1) = cosa; Rx(1,2) =-sina; Rx(2,0) = 0; Rx(2,1) = sina; Rx(2,2) = cosa; BOOST_CHECK(allEQ(Rx, Rot3D(0,alpha))); Matrix Ry(3u,3u); Ry(0,0) = cosa; Ry(0,1) = 0; Ry(0,2) = sina; Ry(1,0) = 0; Ry(1,1) = 1; Ry(1,2) = 0; Ry(2,0) =-sina; Ry(2,1) = 0; Ry(2,2) = cosa; BOOST_CHECK(allEQ(Ry, Rot3D(1,alpha))); Matrix Rz(3u,3u); Rz(0,0) = cosa; Rz(0,1) =-sina; Rz(0,2) = 0; Rz(1,0) = sina; Rz(1,1) = cosa; Rz(1,2) = 0; Rz(2,0) = 0; Rz(2,1) = 0; Rz(2,2) = 1; BOOST_CHECK(allEQ(Rz, Rot3D(2,alpha))); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tMaskArrExcp.cc000066400000000000000000000536231476623553700213370ustar00rootroot00000000000000//# tMaskArrExcp.cc: Test program for MaskedArray Exceptions //# Copyright (C) 1994,1995,1996,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include "../MaskedArray.h" #include "../MaskArrIO.h" #include "../MaskArrLogi.h" #include "../MaskArrMath.h" #include "../Array.h" #include "../ArrayError.h" #include "../ArrayLogical.h" #include "../ArrayMath.h" #include "../Vector.h" #include "../Matrix.h" #include "../Cube.h" #include "../LogiArray.h" #include "../LogiVector.h" #include using namespace casacore; BOOST_AUTO_TEST_SUITE(masked_array_exceptions) BOOST_AUTO_TEST_CASE( conformance_a_la ) { Vector a(10); LogicalArray b(IPosition(1,11)); a = 0; b = true; BOOST_CHECK_THROW(MaskedArray(a,b), ArrayConformanceError); } BOOST_AUTO_TEST_CASE( conformance_la_la ) { Vector a(10); LogicalArray ab(IPosition(1,10)); LogicalArray b(IPosition(1,11)); a = 0; ab = true; b = true; MaskedArray mab (a,ab); BOOST_CHECK_THROW(MaskedArray(mab,b), ArrayConformanceError); } BOOST_AUTO_TEST_CASE( conformance_assign_array ) { Vector a(10); Vector ba(11); LogicalArray b(IPosition(1,11)); a = 0; ba = 1; b = true; BOOST_CHECK_THROW(ba(b) = a, ArrayConformanceError); } BOOST_AUTO_TEST_CASE( conformance_assign_masked_array1 ) { Vector a(10); LogicalArray ab(IPosition(1,10)); Vector ba(11); LogicalArray b(IPosition(1,11)); a = 0; ab = true; ba = 1; b = true; MaskedArray mbab (ba,b); BOOST_CHECK_THROW(a(ab) = mbab, ArrayConformanceError); } BOOST_AUTO_TEST_CASE( conformance_assign_masked_array2 ) { Vector a(10); Vector ba(11); LogicalArray b(IPosition(1,11)); a = 0; ba = 1; b = true; MaskedArray mbab (ba,b); BOOST_CHECK_THROW(a.assign_conforming( mbab ), ArrayConformanceError); } BOOST_AUTO_TEST_CASE( read_only_assign ) { Vector a(10); LogicalArray b(IPosition(1,10)); a = 0; b = true; MaskedArray ma (a,b,true); BOOST_CHECK_THROW(ma = 1, ArrayError); } BOOST_AUTO_TEST_CASE( read_only_assign1 ) { Vector aa(10); aa = 0; const Vector a(aa); LogicalArray b(IPosition(1,10)); b = true; MaskedArray ma (a,b,true); BOOST_CHECK_THROW(a(b) = a, ArrayError); } BOOST_AUTO_TEST_CASE( read_only_assign2 ) { Vector a(10); LogicalArray b(IPosition(1,10)); a = 0; b = true; MaskedArray ma (a,b); MaskedArray mma (a,b); ma.setReadOnly(); BOOST_CHECK_THROW(ma = mma, ArrayError); } BOOST_AUTO_TEST_CASE( read_only_getrwarray ) { Vector a(10); LogicalArray b(IPosition(1,10)); a = 0; b = true; MaskedArray ma (a,b,true); BOOST_CHECK_THROW(ma.getRWArray(), ArrayError); } BOOST_AUTO_TEST_CASE( read_only_getrwstorage ) { Vector a(10); LogicalArray b(IPosition(1,10)); a = 0; b = true; MaskedArray ma (a,b,true); bool deleteIt; BOOST_CHECK_THROW(ma.getRWArrayStorage(deleteIt), ArrayError); } BOOST_AUTO_TEST_CASE( read_only_putarraystorage ) { Vector a(10); LogicalArray b(IPosition(1,10)); a = 0; b = true; MaskedArray ma (a,b,true); bool deleteIt; const int *arrS (ma.getArrayStorage(deleteIt)); int *arrRWS ((int *) arrS); BOOST_CHECK_THROW(ma.putArrayStorage (arrRWS, deleteIt), ArrayError); } BOOST_AUTO_TEST_CASE( get_compressed_array1 ) { Vector a(10); LogicalVector b(10); a = 0; b = true; b(5) = false; MaskedArray ma (a,b,true); IPosition shape (2,2,5); BOOST_CHECK_THROW(ma.getCompressedArray(shape), ArrayError); } BOOST_AUTO_TEST_CASE( get_compressed_array2 ) { Vector a(10); LogicalVector b(10); a = 0; b = true; b(5) = false; MaskedArray ma (a,b,true); Matrix c (IPosition (2,2,5)); BOOST_CHECK_THROW(ma.getCompressedArray(c), ArrayError); } BOOST_AUTO_TEST_CASE( set_compressed_array ) { Vector a(10); LogicalVector b(10); a = 0; b = true; b(5) = false; MaskedArray ma (a,b,true); Matrix c (IPosition (2,2,5)); c = 1; BOOST_CHECK_THROW(ma.setCompressedArray(c), ArrayError); } BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE(mask_arr_math_exceptions) BOOST_AUTO_TEST_CASE( conformance_add_assign1 ) { Vector a(10); Vector ba(11); LogicalArray b(IPosition(1,11)); a = 0; ba = 1; b = true; BOOST_CHECK_THROW(ba(b) += a, ArrayConformanceError); } BOOST_AUTO_TEST_CASE( conformance_add_assign2 ) { Vector a(10); Vector ba(10); LogicalArray b(IPosition(1,10)); a = 0; ba = 1; b = true; MaskedArray bab (ba,b,true); BOOST_CHECK_THROW(bab += a, ArrayError); } BOOST_AUTO_TEST_CASE( conformance_add_assign3 ) { Vector a(10); Vector ba(11); LogicalArray b(IPosition(1,11)); a = 0; ba = 1; b = true; MaskedArray mbab (ba,b); BOOST_CHECK_THROW(a += mbab, ArrayConformanceError); } BOOST_AUTO_TEST_CASE( conformance_add_assign4 ) { Vector a(10); LogicalArray ab(IPosition(1,10)); Vector ba(11); LogicalArray b(IPosition(1,11)); a = 0; ab = true; ba = 1; b = true; MaskedArray mbab (ba,b); BOOST_CHECK_THROW(a(ab) += mbab, ArrayConformanceError); } BOOST_AUTO_TEST_CASE(readonly_add_assign) { Vector a(10); Vector ba(10); LogicalArray b(IPosition(1,10)); a = 0; ba = 1; b = true; MaskedArray bab (ba,b,true); BOOST_CHECK_THROW(bab += bab, ArrayError); } BOOST_AUTO_TEST_CASE(conformance_add) { Vector a(10); Vector ba(11); LogicalArray b(IPosition(1,11)); Vector c(10); a = 0; ba = 1; b = true; c = 0; MaskedArray mbab (ba,b); BOOST_CHECK_THROW(c.assign_conforming( mbab + a ), ArrayConformanceError); } BOOST_AUTO_TEST_CASE(conformance_add1) { Vector a(10); Vector ba(11); LogicalArray b(IPosition(1,11)); Vector c(10); a = 0; ba = 1; b = true; c = 0; MaskedArray mbab (ba,b); BOOST_CHECK_THROW(c.assign_conforming(a + mbab), ArrayConformanceError); } BOOST_AUTO_TEST_CASE(conformance_add2) { Vector a(10); LogicalArray ab(IPosition(1,10)); Vector ba(11); LogicalArray b(IPosition(1,11)); Vector c(10); a = 0; ab = true; ba = 1; b = true; c = 0; MaskedArray maab (a,ab); MaskedArray mbab (ba,b); BOOST_CHECK_THROW(c.assign_conforming( maab + mbab ), ArrayConformanceError); } BOOST_AUTO_TEST_CASE(conformance_pow1) { Vector a(10); Vector ba(11); LogicalArray b(IPosition(1,11)); Vector c(10); a = 0; ba = 1; b = true; c = 0; MaskedArray mbab (ba,b); BOOST_CHECK_THROW(c.assign_conforming( pow (mbab, a) ), ArrayConformanceError); } BOOST_AUTO_TEST_CASE(conformance_pow2) { Vector a(10); Vector ba(11); LogicalArray b(IPosition(1,11)); Vector c(10); a = 0; ba = 1; b = true; c = 0; MaskedArray mbab (ba,b); BOOST_CHECK_THROW(c.assign_conforming(pow (a, mbab)), ArrayConformanceError); } BOOST_AUTO_TEST_CASE(conformance_pow3) { Vector a(10); LogicalArray ab(IPosition(1,10)); Vector ba(11); LogicalArray b(IPosition(1,11)); Vector c(10); a = 0; ab = true; ba = 1; b = true; c = 0; MaskedArray maab (a,ab); MaskedArray mbab (ba,b); BOOST_CHECK_THROW(c.assign_conforming( pow (maab, mbab)), ArrayConformanceError); } BOOST_AUTO_TEST_CASE(conformance_atan2_1) { Vector a(10); Vector ba(11); LogicalArray b(IPosition(1,11)); Vector c(10); a = 0; ba = 1; b = true; c = 0; MaskedArray mbab (ba,b); BOOST_CHECK_THROW(c.assign_conforming( atan2 (mbab, a)), ArrayConformanceError); } BOOST_AUTO_TEST_CASE(conformance_atan2_2) { Vector a(10); Vector ba(11); LogicalArray b(IPosition(1,11)); Vector c(10); a = 0; ba = 1; b = true; c = 0; MaskedArray mbab (ba,b); BOOST_CHECK_THROW(c .assign_conforming( atan2 (a, mbab)), ArrayConformanceError); } BOOST_AUTO_TEST_CASE(conformance_atan2_3) { Vector a(10); LogicalArray ab(IPosition(1,10)); Vector ba(11); LogicalArray b(IPosition(1,11)); Vector c(10); a = 0; ab = true; ba = 1; b = true; c = 0; MaskedArray maab (a,ab); MaskedArray mbab (ba,b); BOOST_CHECK_THROW(c.assign_conforming( atan2 (maab, mbab)), ArrayConformanceError); } BOOST_AUTO_TEST_CASE(insufficient_elements_minmax) { Vector a(10); LogicalArray b(IPosition(1,10)); indgen (a); b = false; MaskedArray ma (a,b); int minVal, maxVal; IPosition minPos (1,1), maxPos (1,1); BOOST_CHECK_THROW(minMax (minVal, maxVal, minPos, maxPos, ma), ArrayError); } BOOST_AUTO_TEST_CASE(insufficient_elements_min) { Vector ba(11); LogicalArray b(IPosition(1,11)); indgen (ba); b = false; MaskedArray mbab (ba,b); BOOST_CHECK_THROW(min (mbab), ArrayError); } BOOST_AUTO_TEST_CASE(conformance_min1) { Vector a(10); Vector ba(11); LogicalArray b(IPosition(1,11)); Vector c(10); a = 0; ba = 1; b = true; c = 0; MaskedArray mbab (ba,b); BOOST_CHECK_THROW(c.assign_conforming( min (mbab, a)), ArrayConformanceError); } BOOST_AUTO_TEST_CASE(conformance_min2) { Vector a(10); Vector ba(11); LogicalArray b(IPosition(1,11)); Vector c(10); a = 0; ba = 1; b = true; c = 0; MaskedArray mbab (ba,b); BOOST_CHECK_THROW(c.assign_conforming( min (a, mbab)), ArrayConformanceError); } BOOST_AUTO_TEST_CASE(conformance_min3) { Vector a(10); LogicalArray ab(IPosition(1,10)); Vector ba(11); LogicalArray b(IPosition(1,11)); Vector c(10); a = 0; ab = true; ba = 1; b = true; c = 0; MaskedArray maab (a,ab); MaskedArray mbab (ba,b); BOOST_CHECK_THROW(c.assign_conforming( min (maab, mbab)), ArrayConformanceError); } BOOST_AUTO_TEST_CASE(conformance_min4) { Vector a(10); Vector ba(11); Vector c(10); LogicalArray cb(IPosition(1,10)); a = 0; ba = 1; c = 0; cb = true; BOOST_CHECK_THROW(::min (a, ba, c), ArrayConformanceError); } BOOST_AUTO_TEST_CASE(conformance_min5) { Vector a(10); Vector ba(11); Vector c(10); LogicalArray cb(IPosition(1,10)); a = 0; ba = 1; c = 0; cb = true; BOOST_CHECK_THROW(::min (ba, a, c), ArrayConformanceError); } BOOST_AUTO_TEST_CASE(insufficient_elements_sum) { Vector a(10); LogicalArray b(IPosition(1,10)); indgen (a); b = false; MaskedArray ma (a,b); BOOST_CHECK_THROW(sum (ma), ArrayError); } BOOST_AUTO_TEST_CASE(insufficient_elements_sumsquares) { Vector a(10); LogicalArray b(IPosition(1,10)); indgen (a); b = false; MaskedArray ma (a,b); BOOST_CHECK_THROW(sumsquares (ma), ArrayError); } BOOST_AUTO_TEST_CASE(insufficient_elements_product) { Vector a(10); LogicalArray b(IPosition(1,10)); indgen (a); b = false; MaskedArray ma (a,b); BOOST_CHECK_THROW(product (ma), ArrayError); } BOOST_AUTO_TEST_CASE(insufficient_elements_mean) { Vector a(10); LogicalArray b(IPosition(1,10)); indgen (a); b = false; MaskedArray ma (a,b); BOOST_CHECK_THROW(mean (ma), ArrayError); } BOOST_AUTO_TEST_CASE(insufficient_elements_variance) { Vector a(10); LogicalArray b(IPosition(1,10)); int mean (0); indgen (a); b = false; b(IPosition(1,0)) = true; MaskedArray ma (a,b); BOOST_CHECK_THROW(variance (ma, mean), ArrayError); } BOOST_AUTO_TEST_CASE(insufficient_elements_avdev) { Vector a(10); LogicalArray b(IPosition(1,10)); int mean (0); indgen (a); b = false; MaskedArray ma (a,b); BOOST_CHECK_THROW(avdev (ma, mean), ArrayError); } BOOST_AUTO_TEST_CASE(insufficient_elements_median) { Vector a(10); LogicalArray b(IPosition(1,10)); indgen (a); b = false; MaskedArray ma (a,b); BOOST_CHECK_THROW(median (ma, false), ArrayError); } BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE(mask_arr_logi_exceptions) BOOST_AUTO_TEST_CASE(conformance_allle1 ) { Vector a(10); Vector ba(11); LogicalArray b(IPosition(1,11)); a = 0; ba = 1; b = true; MaskedArray mbab (ba,b); BOOST_CHECK_THROW(allLE (mbab, a), ArrayConformanceError); } BOOST_AUTO_TEST_CASE(insufficient_elements_alle1) { Vector a(10); Vector ba(10); LogicalArray b(IPosition(1,10)); a = 0; ba = 1; b = false; MaskedArray mbab (ba,b); BOOST_CHECK_THROW(allLE (mbab, a), ArrayError); } BOOST_AUTO_TEST_CASE(conformance_allle2) { Vector a(10); Vector ba(11); LogicalArray b(IPosition(1,11)); a = 0; ba = 1; b = true; MaskedArray mbab (ba,b); BOOST_CHECK_THROW(allLE (a, mbab), ArrayConformanceError); } BOOST_AUTO_TEST_CASE(insufficient_elements_alle2) { Vector a(10); Vector ba(10); LogicalArray b(IPosition(1,10)); a = 0; ba = 1; b = false; MaskedArray mbab (ba,b); BOOST_CHECK_THROW(allLE (a, mbab), ArrayError); } BOOST_AUTO_TEST_CASE(conformance_allle3) { Vector a(10); LogicalArray ab(IPosition(1,10)); Vector ba(11); LogicalArray b(IPosition(1,11)); a = 0; ab = true; ba = 1; b = true; MaskedArray maab (a,ab); MaskedArray mbab (ba,b); BOOST_CHECK_THROW(allLE (maab, mbab), ArrayConformanceError); } BOOST_AUTO_TEST_CASE(insufficient_elements_alle3) { Vector a(10); LogicalArray ab(IPosition(1,10)); Vector ba(10); LogicalArray b(IPosition(1,10)); a = 0; ab = false; ba = 1; b = true; MaskedArray maab (a,ab); MaskedArray mbab (ba,b); BOOST_CHECK_THROW(allLE (maab, mbab), ArrayError); } BOOST_AUTO_TEST_CASE(conformance_anyand1) { LogicalArray a(IPosition(1,10)); LogicalArray c(IPosition(1,11)); LogicalArray b(IPosition(1,11)); a = true; c = false; b = true; MaskedLogicalArray mcb (c,b); BOOST_CHECK_THROW(anyAND (mcb, a), ArrayConformanceError); } BOOST_AUTO_TEST_CASE(insufficient_elements_anyand1) { LogicalArray a(IPosition(1,10)); LogicalArray c(IPosition(1,10)); LogicalArray b(IPosition(1,10)); a = true; c = false; b = false; MaskedLogicalArray mcb (c,b); BOOST_CHECK_THROW(anyAND (mcb, a), ArrayError); } BOOST_AUTO_TEST_CASE(conformance_anyand2) { LogicalArray a(IPosition(1,10)); LogicalArray c(IPosition(1,11)); LogicalArray b(IPosition(1,11)); a = true; c = false; b = true; MaskedLogicalArray mcb (c,b); BOOST_CHECK_THROW(anyAND (a, mcb), ArrayConformanceError); } BOOST_AUTO_TEST_CASE(insufficient_elements_anyand2) { LogicalArray a(IPosition(1,10)); LogicalArray c(IPosition(1,10)); LogicalArray b(IPosition(1,10)); a = false; c = true; b = false; MaskedLogicalArray mcb (c,b); BOOST_CHECK_THROW(anyAND (a, mcb), ArrayError); } BOOST_AUTO_TEST_CASE(conformance_anyand3) { LogicalArray a(IPosition(1,10)); LogicalArray ab(IPosition(1,10)); LogicalArray c(IPosition(1,11)); LogicalArray b(IPosition(1,11)); a = false; ab = true; c = true; b = true; MaskedLogicalArray maab (a,ab); MaskedLogicalArray mcb (c,b); BOOST_CHECK_THROW(anyAND (maab, mcb), ArrayConformanceError); } BOOST_AUTO_TEST_CASE(insufficient_elements_anyand3) { LogicalArray a(IPosition(1,10)); LogicalArray ab(IPosition(1,10)); LogicalArray c(IPosition(1,10)); LogicalArray b(IPosition(1,10)); a = false; ab = true; c = true; b = false; MaskedLogicalArray maab (a,ab); MaskedLogicalArray mcb (c,b); BOOST_CHECK_THROW(anyAND (maab, mcb), ArrayError); } BOOST_AUTO_TEST_CASE(conformance_anyor1) { LogicalArray a(IPosition(1,10)); LogicalArray c(IPosition(1,11)); LogicalArray b(IPosition(1,11)); a = false; c = true; b = true; MaskedLogicalArray mcb (c,b); BOOST_CHECK_THROW(anyOR (mcb, a), ArrayConformanceError); } BOOST_AUTO_TEST_CASE(insufficient_elements_anyor1) { LogicalArray a(IPosition(1,10)); LogicalArray c(IPosition(1,10)); LogicalArray b(IPosition(1,10)); a = false; c = true; b = false; MaskedLogicalArray mcb (c,b); BOOST_CHECK_THROW(anyOR (mcb, a), ArrayError); } BOOST_AUTO_TEST_CASE(conformance_anyor2) { LogicalArray a(IPosition(1,10)); LogicalArray c(IPosition(1,11)); LogicalArray b(IPosition(1,11)); a = false; c = true; b = true; MaskedLogicalArray mcb (c,b); BOOST_CHECK_THROW(anyOR (a, mcb), ArrayConformanceError); } BOOST_AUTO_TEST_CASE(insufficient_elements_anyor2) { LogicalArray a(IPosition(1,10)); LogicalArray c(IPosition(1,10)); LogicalArray b(IPosition(1,10)); a = false; c = true; b = false; MaskedLogicalArray mcb (c,b); BOOST_CHECK_THROW(anyOR (a, mcb), ArrayError); } BOOST_AUTO_TEST_CASE(conformance_anyor3) { LogicalArray a(IPosition(1,10)); LogicalArray ab(IPosition(1,10)); LogicalArray c(IPosition(1,11)); LogicalArray b(IPosition(1,11)); a = false; ab = true; c = true; b = true; MaskedLogicalArray maab (a,ab); MaskedLogicalArray mcb (c,b); BOOST_CHECK_THROW(anyOR (maab, mcb), ArrayConformanceError); } BOOST_AUTO_TEST_CASE(insufficient_elements_anyor3) { LogicalArray a(IPosition(1,10)); LogicalArray ab(IPosition(1,10)); LogicalArray c(IPosition(1,10)); LogicalArray b(IPosition(1,10)); a = false; ab = false; c = true; b = true; MaskedLogicalArray maab (a,ab); MaskedLogicalArray mcb (c,b); BOOST_CHECK_THROW(anyOR (maab, mcb), ArrayError); } BOOST_AUTO_TEST_CASE(conformance_le1) { Vector a(10); Vector ba(11); LogicalArray b(IPosition(1,11)); LogicalArray cb(IPosition(1,10)); a = 0; ba = 1; b = true; cb = false; MaskedArray mbab (ba,b); BOOST_CHECK_THROW(cb.assign_conforming( (mbab <= a)), ArrayConformanceError); } BOOST_AUTO_TEST_CASE(conformance_le2) { Vector a(10); Vector ba(11); LogicalArray b(IPosition(1,11)); LogicalArray cb(IPosition(1,10)); a = 0; ba = 1; b = true; cb = false; MaskedArray mbab (ba,b); BOOST_CHECK_THROW(cb.assign_conforming( (a <= mbab)), ArrayConformanceError); } BOOST_AUTO_TEST_CASE(conformance_le3) { Vector a(10); LogicalArray ab(IPosition(1,10)); Vector ba(11); LogicalArray b(IPosition(1,11)); LogicalArray cb(IPosition(1,10)); a = 0; ab = true; ba = 1; b = true; cb = false; MaskedArray maab (a,ab); MaskedArray mbab (ba,b); BOOST_CHECK_THROW(cb.assign_conforming( (maab <= mbab)), ArrayConformanceError); } BOOST_AUTO_TEST_CASE(insufficient_elements_alland1) { Vector a(10); LogicalArray c(IPosition(1,10)); LogicalArray b(IPosition(1,10)); indgen (a); c = (a<5); b = false; MaskedLogicalArray mcb (c,b); BOOST_CHECK_THROW(allAND (mcb, true), ArrayError); } BOOST_AUTO_TEST_CASE(insufficient_elements_alland2) { Vector a(10); LogicalArray c(IPosition(1,10)); LogicalArray b(IPosition(1,10)); indgen (a); c = (a<5); b = false; MaskedLogicalArray mcb (c,b); BOOST_CHECK_THROW(allAND (true, mcb), ArrayError); } BOOST_AUTO_TEST_CASE(insufficient_elements_allor1) { Vector a(10); LogicalArray c(IPosition(1,10)); LogicalArray b(IPosition(1,10)); indgen (a); c = (a<5); b = false; MaskedLogicalArray mcb (c,b); BOOST_CHECK_THROW(allOR (mcb, false), ArrayError); } BOOST_AUTO_TEST_CASE(insufficient_elements_allor2) { Vector a(10); LogicalArray c(IPosition(1,10)); LogicalArray b(IPosition(1,10)); indgen (a); c = (a<5); b = false; MaskedLogicalArray mcb (c,b); BOOST_CHECK_THROW(allOR (false, mcb), ArrayError); } BOOST_AUTO_TEST_CASE(insufficient_elements_allle1) { Vector a(10); Vector ba(10); LogicalArray b(IPosition(1,10)); a = 0; ba = 1; b = false; MaskedArray mbab (ba,b); BOOST_CHECK_THROW(allLE (mbab, 7), ArrayError); } BOOST_AUTO_TEST_CASE(insufficient_elements_allle2) { Vector a(10); Vector ba(10); LogicalArray b(IPosition(1,10)); a = 0; ba = 1; b = false; MaskedArray mbab (ba,b); BOOST_CHECK_THROW(allLE (7, mbab), ArrayError); } BOOST_AUTO_TEST_CASE(insufficient_elements_anyand4) { Vector a(10); LogicalArray c(IPosition(1,10)); LogicalArray b(IPosition(1,10)); indgen (a); c = (a<5); b = false; MaskedLogicalArray mcb (c,b); BOOST_CHECK_THROW(anyAND (mcb, true), ArrayError); } BOOST_AUTO_TEST_CASE(insufficient_elements_anyand5) { Vector a(10); LogicalArray c(IPosition(1,10)); LogicalArray b(IPosition(1,10)); indgen (a); c = (a<5); b = false; MaskedLogicalArray mcb (c,b); BOOST_CHECK_THROW(anyAND (true, mcb), ArrayError); } BOOST_AUTO_TEST_CASE(insufficient_elements_anyor4) { Vector a(10); LogicalArray c(IPosition(1,10)); LogicalArray b(IPosition(1,10)); indgen (a); c = (a<5); b = false; MaskedLogicalArray mcb (c,b); BOOST_CHECK_THROW(anyOR (mcb, false), ArrayError); } BOOST_AUTO_TEST_CASE(insufficient_elements_anyor5) { Vector a(10); LogicalArray c(IPosition(1,10)); LogicalArray b(IPosition(1,10)); indgen (a); c = (a<5); b = false; MaskedLogicalArray mcb (c,b); BOOST_CHECK_THROW(anyOR (false, mcb), ArrayError); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tMaskArrIO.cc000066400000000000000000000107531476623553700207440ustar00rootroot00000000000000//# tMaskArrIO.cc: Test program for MaskedArray IO //# Copyright (C) 1994,1995,1996,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include "../MaskedArray.h" #include "../MaskArrMath.h" #include "../MaskArrIO.h" #include "../Array.h" #include "../ArrayLogical.h" #include "../ArrayMath.h" //#include "../ArrayIO.h" #include "../Vector.h" #include "../Matrix.h" #include "../Cube.h" #include "../ArrayError.h" #include using namespace casacore; BOOST_AUTO_TEST_SUITE(mask_array_io) BOOST_AUTO_TEST_CASE(masked_vector) { const char* reference[5] = { "Array: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n" "Mask: [1, 1, 1, 1, 1, 1, 1, 0, 0, 0]", "Array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n" "Mask: [1, 1, 1, 1, 1, 1, 0, 0, 0, 0]", "Array: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]\n" "Mask: [1, 1, 1, 1, 1, 0, 0, 0, 0, 0]", "Array: [3, 4, 5, 6, 7, 8, 9, 10, 11, 12]\n" "Mask: [1, 1, 1, 1, 0, 0, 0, 0, 0, 0]", "Array: [4, 5, 6, 7, 8, 9, 10, 11, 12, 13]\n" "Mask: [1, 1, 1, 0, 0, 0, 0, 0, 0, 0]" }; Vector a(10); for (int orig0=0; orig0 < 5; orig0++) { indgen (a, orig0); std::ostringstream str; str << a(a<7); BOOST_CHECK_EQUAL(str.str(), reference[orig0]); } } BOOST_AUTO_TEST_CASE(masked_matrix) { Matrix a(10u,3u); indgen (a, 0); std::ostringstream str; str << a(a<7); BOOST_CHECK_EQUAL(str.str(), "Array: Axis Lengths: [10, 3] (NB: Matrix in Row/Column order)\n" "[0, 10, 20\n" " 1, 11, 21\n" " 2, 12, 22\n" " 3, 13, 23\n" " 4, 14, 24\n" " 5, 15, 25\n" " 6, 16, 26\n" " 7, 17, 27\n" " 8, 18, 28\n" " 9, 19, 29]\n" "\n" "Mask: Axis Lengths: [10, 3] (NB: Matrix in Row/Column order)\n" "[1, 0, 0\n" " 1, 0, 0\n" " 1, 0, 0\n" " 1, 0, 0\n" " 1, 0, 0\n" " 1, 0, 0\n" " 1, 0, 0\n" " 0, 0, 0\n" " 0, 0, 0\n" " 0, 0, 0]\n"); } BOOST_AUTO_TEST_CASE(masked_cube) { Cube a(10,3,4); indgen (a, 0); std::ostringstream str; str << a(a<7); BOOST_CHECK_EQUAL(str.str(), "Array: Ndim=3 Axis Lengths: [10, 3, 4] \n" "[0, 0, 0][0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n" "[0, 1, 0][10, 11, 12, 13, 14, 15, 16, 17, 18, 19]\n" "[0, 2, 0][20, 21, 22, 23, 24, 25, 26, 27, 28, 29]\n" "[0, 0, 1][30, 31, 32, 33, 34, 35, 36, 37, 38, 39]\n" "[0, 1, 1][40, 41, 42, 43, 44, 45, 46, 47, 48, 49]\n" "[0, 2, 1][50, 51, 52, 53, 54, 55, 56, 57, 58, 59]\n" "[0, 0, 2][60, 61, 62, 63, 64, 65, 66, 67, 68, 69]\n" "[0, 1, 2][70, 71, 72, 73, 74, 75, 76, 77, 78, 79]\n" "[0, 2, 2][80, 81, 82, 83, 84, 85, 86, 87, 88, 89]\n" "[0, 0, 3][90, 91, 92, 93, 94, 95, 96, 97, 98, 99]\n" "[0, 1, 3][100, 101, 102, 103, 104, 105, 106, 107, 108, 109]\n" "[0, 2, 3][110, 111, 112, 113, 114, 115, 116, 117, 118, 119]\n" "\n" "Mask: Ndim=3 Axis Lengths: [10, 3, 4] \n" "[0, 0, 0][1, 1, 1, 1, 1, 1, 1, 0, 0, 0]\n" "[0, 1, 0][0, 0, 0, 0, 0, 0, 0, 0, 0, 0]\n" "[0, 2, 0][0, 0, 0, 0, 0, 0, 0, 0, 0, 0]\n" "[0, 0, 1][0, 0, 0, 0, 0, 0, 0, 0, 0, 0]\n" "[0, 1, 1][0, 0, 0, 0, 0, 0, 0, 0, 0, 0]\n" "[0, 2, 1][0, 0, 0, 0, 0, 0, 0, 0, 0, 0]\n" "[0, 0, 2][0, 0, 0, 0, 0, 0, 0, 0, 0, 0]\n" "[0, 1, 2][0, 0, 0, 0, 0, 0, 0, 0, 0, 0]\n" "[0, 2, 2][0, 0, 0, 0, 0, 0, 0, 0, 0, 0]\n" "[0, 0, 3][0, 0, 0, 0, 0, 0, 0, 0, 0, 0]\n" "[0, 1, 3][0, 0, 0, 0, 0, 0, 0, 0, 0, 0]\n" "[0, 2, 3][0, 0, 0, 0, 0, 0, 0, 0, 0, 0]\n" ); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tMaskArrLogi.cc000066400000000000000000000204501476623553700213220ustar00rootroot00000000000000//# tMaskArrLogi.cc: Test program for MaskedArray logical operations //# Copyright (C) 1993,1994,1995,1996,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include "../IPosition.h" #include "../Array.h" #include "../ArrayError.h" //#include "../ArrayIO.h" #include "../ArrayLogical.h" #include "../ArrayMath.h" #include "../Vector.h" #include "../Matrix.h" #include "../Cube.h" #include "../LogiVector.h" #include "../MaskedArray.h" #include "../MaskArrIO.h" #include "../MaskArrLogi.h" #include "../MaskArrMath.h" #include "TestUtilities.h" #include #include using namespace casacore; struct Fixture { std::vector ref; Vector u, v, w, x, y, z; LogicalArray b; Fixture() : u(10), v(10), w(10), x(10), y(10), z(10), b(IPosition(1,10)) { indgen (u, -2); u(0) = 8; u(1) = 9; v=-1; w=11; x=1; indgen (y); z=5; b = ((y > 3) && (y < 8)); } }; BOOST_AUTO_TEST_SUITE(mask_array_logical_operators) BOOST_FIXTURE_TEST_CASE(prologue, Fixture) { check(u, {8, 9, 0, 1, 2, 3, 4, 5, 6, 7}); check(v, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}); check(w, {11, 11, 11, 11, 11, 11, 11, 11, 11, 11}); check(x, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}); check(y, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); check(z, {5, 5, 5, 5, 5, 5, 5, 5, 5, 5}); check(b, {false, false, false, false, true, true, true, true, false, false}); } BOOST_FIXTURE_TEST_CASE(constructor, Fixture) { Vector a(x.copy()); MaskedArray ma (a, b); MaskedArray mma (ma, y>5); mma = 75; check(a, {1, 1, 1, 1, 1, 1, 75, 75, 1, 1}); } BOOST_FIXTURE_TEST_CASE(brackets_operator, Fixture) { Vector a(x.copy()); MaskedArray ma (a, b); ma (y>5) = 75; check(a, {1, 1, 1, 1, 1, 1, 75, 75, 1, 1}); } BOOST_FIXTURE_TEST_CASE(le_operator1, Fixture) { check((y(b) <= z).getArray(), {0, 0, 0, 0, 1, 1, 0, 0, 0, 0}); } BOOST_FIXTURE_TEST_CASE(le_operator2, Fixture) { check((y <= z(b)).getArray(), {0, 0, 0, 0, 1, 1, 0, 0, 0, 0}); } BOOST_FIXTURE_TEST_CASE(le_operator3, Fixture) { check((y(b) <= z(y>4)).getArray(), {0, 0, 0, 0, 0, 1, 0, 0, 0, 0}); } BOOST_FIXTURE_TEST_CASE(le_operator4, Fixture) { check((y(b) <= 5).getArray(), {0, 0, 0, 0, 1, 1, 0, 0, 0, 0}); } BOOST_FIXTURE_TEST_CASE(le_operator5, Fixture) { check((5 > y(b)).getArray(), {0, 0, 0, 0, 1, 0, 0, 0, 0, 0}); } BOOST_FIXTURE_TEST_CASE(logical_and1, Fixture) { check(((5 > y(b)) && true).getArray(), {0, 0, 0, 0, 1, 0, 0, 0, 0, 0}); } BOOST_FIXTURE_TEST_CASE(logical_and2, Fixture) { check(((5 > y(b)) && false).getArray(), {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); } BOOST_FIXTURE_TEST_CASE(logical_or1, Fixture) { check(((5 > y(b)) || true).getArray(), {0, 0, 0, 0, 1, 1, 1, 1, 0, 0}); } BOOST_FIXTURE_TEST_CASE(logical_or2, Fixture) { check(((5 > y(b)) || false).getArray(), {0, 0, 0, 0, 1, 0, 0, 0, 0, 0}); } BOOST_FIXTURE_TEST_CASE(logical_and3, Fixture) { check((true && (5 > y(b))).getArray(), {0, 0, 0, 0, 1, 0, 0, 0, 0, 0}); } BOOST_FIXTURE_TEST_CASE(logical_and4, Fixture) { check((false && (5 > y(b))).getArray(), {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); } BOOST_FIXTURE_TEST_CASE(logical_or3, Fixture) { check((true || (5 > y(b))).getArray(), {0, 0, 0, 0, 1, 1, 1, 1, 0, 0}); } BOOST_FIXTURE_TEST_CASE(logical_or4, Fixture) { check( (false || (5 > y(b))).getArray(), {0, 0, 0, 0, 1, 0, 0, 0, 0, 0}); } BOOST_FIXTURE_TEST_CASE(negate, Fixture) { Vector a_log (10); indgen (a_log); LogicalVector b_log ((a_log > 1) && (a_log < 6)); check(a_log, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); check(b_log, {0, 0, 1, 1, 1, 1, 0, 0, 0, 0}); MaskedLogicalArray mla_log ((a_log >= 3), b_log); check(mla_log.getArray(), {0, 0, 0, 1, 1, 1, 1, 1, 1, 1}); check(mla_log.getMask(), {0, 0, 1, 1, 1, 1, 0, 0, 0, 0}); check((!mla_log).getArray(), {0, 0, 1, 0, 0, 0, 1, 1, 1, 1}); check((!mla_log).getMask(), {0, 0, 1, 1, 1, 1, 0, 0, 0, 0}); check(((a_log < 3)(b_log)).getArray(), {1, 1, 1, 0, 0, 0, 0, 0, 0, 0}); check(((a_log < 3)(b_log)).getMask(), {0, 0, 1, 1, 1, 1, 0, 0, 0, 0}); BOOST_CHECK_EQUAL(allEQ (!mla_log, (a_log < 3)(b_log)), true); } BOOST_FIXTURE_TEST_CASE(all_le, Fixture) { BOOST_CHECK(!allLE (y(b), z)); BOOST_CHECK(allLE (y(b), w)); } BOOST_FIXTURE_TEST_CASE(all_gt, Fixture) { BOOST_CHECK(!allGT (z, y(b))); BOOST_CHECK(allGT (w, y(b))); } BOOST_FIXTURE_TEST_CASE(all_lt1, Fixture) { BOOST_CHECK(!allLT (u(u<9), y(y<7))); BOOST_CHECK(allLT (u(u<8), y(y<7))); } BOOST_FIXTURE_TEST_CASE(any_le, Fixture) { BOOST_CHECK(anyLE (y(b), z)); BOOST_CHECK(!anyLE (y(b), v)); } BOOST_FIXTURE_TEST_CASE(any_gt, Fixture) { BOOST_CHECK(anyGT (z, y(b))); BOOST_CHECK(!anyGT (v, y(b))); } BOOST_FIXTURE_TEST_CASE(any_lt1, Fixture) { BOOST_CHECK(anyLT (u(u<9), y(y<7))); BOOST_CHECK(!anyLT (u(u>8), y(y<7))); } BOOST_FIXTURE_TEST_CASE(any_and1, Fixture) { BOOST_CHECK(anyAND (b(y>5), y>4)); BOOST_CHECK(!anyAND (b(y==4), y>4)); } BOOST_FIXTURE_TEST_CASE(any_and2, Fixture) { BOOST_CHECK(anyAND (y>4, b(y>5))); BOOST_CHECK(!anyAND (y>4, b(y==4))); } BOOST_FIXTURE_TEST_CASE(any_and3, Fixture) { BOOST_CHECK(anyAND (b(y>4), b(y>5))); BOOST_CHECK(!anyAND (b(y<3), b(y<=3))); } BOOST_FIXTURE_TEST_CASE(any_or1, Fixture) { BOOST_CHECK(anyOR (b(y>5), y>4)); BOOST_CHECK(!anyOR (b(y==3), y>4)); } BOOST_FIXTURE_TEST_CASE(any_or2, Fixture) { BOOST_CHECK(anyOR (y>4, b(y>5))); BOOST_CHECK(!anyOR (y>4, b(y==3))); } BOOST_FIXTURE_TEST_CASE(any_or3, Fixture) { BOOST_CHECK(anyOR (b(y>4), b(y>5))); BOOST_CHECK(!anyOR (b(y<3), b(y<=3))); } BOOST_FIXTURE_TEST_CASE(all_lt2, Fixture) { BOOST_CHECK(!allLT (y(y>5), 7)); BOOST_CHECK(allLT (y(y>5), 11)); } BOOST_FIXTURE_TEST_CASE(all_ge, Fixture) { BOOST_CHECK(!allGE (7, y(y>5))); BOOST_CHECK(allGE (11, y(y>5))); } BOOST_FIXTURE_TEST_CASE(any_lt2, Fixture) { BOOST_CHECK(anyLT (y(y>5), 7)); BOOST_CHECK(!anyLT (y(y>5), 5)); } BOOST_FIXTURE_TEST_CASE(any_ge, Fixture) { BOOST_CHECK(anyGE (7, y(y>5))); BOOST_CHECK(!anyGE (5, y(y>5))); } BOOST_FIXTURE_TEST_CASE(all_and1, Fixture) { BOOST_CHECK(!allAND (b(y>5), true)); BOOST_CHECK(allAND (b(b), true)); BOOST_CHECK(!allAND (b(b), false)); } BOOST_FIXTURE_TEST_CASE(all_and2, Fixture) { BOOST_CHECK(!allAND (true, b(y>5))); BOOST_CHECK(allAND (true, b(b))); BOOST_CHECK(!allAND (false, b(b))); } BOOST_FIXTURE_TEST_CASE(all_or1, Fixture) { BOOST_CHECK(!allOR (b(y>5), false)); BOOST_CHECK(allOR (b(b), false)); BOOST_CHECK(allOR (b(y>5), true)); } BOOST_FIXTURE_TEST_CASE(all_or2, Fixture) { BOOST_CHECK(!allOR (false, b(y>5))); BOOST_CHECK(allOR (false, b(b))); BOOST_CHECK(allOR (true, b(y>5))); } BOOST_FIXTURE_TEST_CASE(any_and4, Fixture) { BOOST_CHECK(anyAND (b(y>5), true)); BOOST_CHECK(!anyAND (b(y<3), true)); BOOST_CHECK(!anyAND (b(y>5), false)); } BOOST_FIXTURE_TEST_CASE(any_and5, Fixture) { BOOST_CHECK(anyAND (true, b(y>5))); BOOST_CHECK(!anyAND (true, b(y<3))); BOOST_CHECK(!anyAND (false, b(y>5))); } BOOST_FIXTURE_TEST_CASE(any_or4, Fixture) { BOOST_CHECK(anyOR (b(y>5), false)); BOOST_CHECK(!anyOR (b(y<3), false)); BOOST_CHECK(anyOR (b(y<3), true)); } BOOST_FIXTURE_TEST_CASE(any_or5, Fixture) { BOOST_CHECK(anyOR (false, b(y>5))); BOOST_CHECK(!anyOR (false, b(y<3))); BOOST_CHECK(anyOR (true, b(y<3))); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tMaskArrMath0.cc000066400000000000000000000075261476623553700214120ustar00rootroot00000000000000//# tMaskArrMath0.cc: Test program for MaskedArrays mathematical operations. //# Copyright (C) 1993,1994,1995,1996,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include "../IPosition.h" #include "../Array.h" #include "../ArrayMath.h" #include "../ArrayLogical.h" //#include "../ArrayIO.h" #include "../Vector.h" #include "../Matrix.h" #include "../Cube.h" #include "../ArrayError.h" #include "../MaskedArray.h" #include "../MaskArrMath.h" #include "TestUtilities.h" #include #include using namespace casacore; BOOST_AUTO_TEST_SUITE(masked_array_math0) struct Fixture { Vector f, g, h; Vector b; Fixture() : f(10), g(10), h(10), b(10) { indgen (f); } }; BOOST_FIXTURE_TEST_CASE(less_than, Fixture) { b = (f<3); check(b, {1, 1, 1, 0, 0, 0, 0, 0, 0, 0}); MaskedArray m(h,b); h = 0; indgen (m); check(h, {0, 1, 2, 0, 0, 0, 0, 0, 0, 0}); } BOOST_FIXTURE_TEST_CASE(larger_and_less, Fixture) { check(((f>3) && (f<7)), {0, 0, 0, 0, 1, 1, 1, 0, 0, 0}); MaskedArray m( h( ((f>3) && (f<7)) ) ); h = 0; indgen (m, 10); check(h, {0, 0, 0, 0, 10, 11, 12, 0, 0, 0}); } BOOST_FIXTURE_TEST_CASE(sum_assign1, Fixture) { b = (f>2); check(b, {0, 0, 0, 1, 1, 1, 1, 1, 1, 1}); MaskedArray m(h,b); h = 1; m += 5; check(h, {1, 1, 1, 6, 6, 6, 6, 6, 6, 6}); } BOOST_FIXTURE_TEST_CASE(sum_assign2, Fixture) { b = (f>2); check(b, {0, 0, 0, 1, 1, 1, 1, 1, 1, 1}); h = 1; h(b) += 5; check(h, {1, 1, 1, 6, 6, 6, 6, 6, 6, 6}); } BOOST_FIXTURE_TEST_CASE(sum_assign3, Fixture) { b = (f>2); check(b, {0, 0, 0, 1, 1, 1, 1, 1, 1, 1}); MaskedArray m(h,b); h = -1; m += f; check(h, {-1, -1, -1, 2, 3, 4, 5, 6, 7, 8}); } BOOST_FIXTURE_TEST_CASE(sum_assign4, Fixture) { b = (f>2); check(b, {0, 0, 0, 1, 1, 1, 1, 1, 1, 1}); h = -1; h(b) += f; check(h, {-1, -1, -1, 2, 3, 4, 5, 6, 7, 8}); } BOOST_FIXTURE_TEST_CASE(sum_assign5, Fixture) { b = (f>4); check(b, {0, 0, 0, 0, 0, 1, 1, 1, 1, 1}); MaskedArray m(f,b); indgen (h); check(h, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); h += m; check(h, {0, 1, 2, 3, 4, 10, 12, 14, 16, 18}); } BOOST_FIXTURE_TEST_CASE(sum_assign6, Fixture) { b = (f>3); check(b, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1}); Vector c(10); c = (f<8); check(c, {1, 1, 1, 1, 1, 1, 1, 1, 0, 0}); MaskedArray m(h,b), n(f,c); h = -1; m += n; check(h, {-1, -1, -1, -1, 3, 4, 5, 6, -1, -1}); } BOOST_FIXTURE_TEST_CASE(sum_assign7, Fixture) { b = (f>3); check(b, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1}); Vector c(10); c = (f<8); check(c, {1, 1, 1, 1, 1, 1, 1, 1, 0, 0}); MaskedArray n(f,c); h = -1; h(b) += n; check(h, {-1, -1, -1, -1, 3, 4, 5, 6, -1, -1}); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tMaskArrMath1.cc000066400000000000000000000166521476623553700214130ustar00rootroot00000000000000//# tMaskArrMath1.cc: Test program for MaskedArrays mathematical operations. //# Copyright (C) 1993,1994,1995,1996,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include "../IPosition.h" #include "../Array.h" #include "../ArrayMath.h" #include "../ArrayLogical.h" //#include "../ArrayIO.h" #include "../Vector.h" #include "../Matrix.h" #include "../Cube.h" #include "../ArrayError.h" #include "../MaskedArray.h" #include "../MaskArrMath.h" #include #include using namespace casacore; BOOST_AUTO_TEST_SUITE(masked_array_math1) struct Fixture { Vector df, dg, dh; Vector b; Fixture() : df(10), dg(10), dh(10), b(10) { indgen (df); indgen (dg, 2.0); dh = 2.0; } }; BOOST_FIXTURE_TEST_CASE(cos_func, Fixture) { // Math dh.assign_conforming( cos (df ((df > 2.5) && (df < 6.5))) ); Vector ref{2, 2, 2, -0.989992, -0.653644, 0.283662, 0.96017, 2, 2, 2}; BOOST_CHECK(allNear(dh, ref, 1e-4)); } BOOST_FIXTURE_TEST_CASE(atan2_func1, Fixture) { dh.assign_conforming(atan2 (df ((df > 2.5) && (df < 6.5)), dg) ); Vector ref{2, 2, 2, 0.54042, 0.588003, 0.620249, 0.643501, 2, 2, 2}; BOOST_CHECK(allNear(dh, ref, 1e-4)); } BOOST_FIXTURE_TEST_CASE(atan2_func2, Fixture) { dh.assign_conforming(atan2 (dg, df ((df > 2.5) && (df < 6.5))) ); Vector ref{2, 2, 2, 1.03038, 0.982794, 0.950547, 0.927295, 2, 2, 2}; BOOST_CHECK(allNear(dh, ref, 1e-4)); } BOOST_FIXTURE_TEST_CASE(atan2_func3, Fixture) { dh.assign_conforming(atan2 (dg ((df > 3.5) && (df < 7.5)), df ((df > 2.5) && (df < 6.5))) ); Vector ref{2, 2, 2, 2, 0.982794, 0.950547, 0.927295, 2, 2, 2}; BOOST_CHECK(allNear(dh, ref, 1e-4)); } BOOST_FIXTURE_TEST_CASE(atan2_func4, Fixture) { dh.assign_conforming(atan2 (df ((df > 2.5) && (df < 6.5)), 2.0) ); Vector ref{2, 2, 2, 0.982794, 1.10715, 1.19029, 1.24905, 2, 2, 2}; BOOST_CHECK(allNear(dh, ref, 1e-4)); } BOOST_FIXTURE_TEST_CASE(atan2_func5, Fixture) { dh.assign_conforming(atan2 (2.0, df ((df > 2.5) && (df < 6.5))) ); Vector ref{2, 2, 2, 0.588003, 0.463648, 0.380506, 0.321751, 2, 2, 2}; BOOST_CHECK(allNear(dh, ref, 1e-4)); } BOOST_FIXTURE_TEST_CASE(pow_func1, Fixture) { dh.assign_conforming(pow (df ((df > 2.5) && (df < 6.5)), dg) ); Vector ref{2, 2, 2, 243, 4096, 78125, 1.67962e+06, 2, 2, 2}; BOOST_CHECK(allNear(dh, ref, 1e-4)); } BOOST_FIXTURE_TEST_CASE(pow_func2, Fixture) { dh.assign_conforming(pow (dg, df ((df > 2.5) && (df < 6.5))) ); Vector ref{2, 2, 2, 125, 1296, 16807, 262144, 2, 2, 2}; BOOST_CHECK(allNear(dh, ref, 1e-4)); } BOOST_FIXTURE_TEST_CASE(pow_func3, Fixture) { dh.assign_conforming(pow (dg ((df > 3.5) && (df < 7.5)), df ((df > 2.5) && (df < 6.5))) ); Vector ref{2, 2, 2, 2, 1296, 16807, 262144, 2, 2, 2}; BOOST_CHECK(allNear(dh, ref, 1e-4)); } BOOST_FIXTURE_TEST_CASE(pow_func4, Fixture) { dh.assign_conforming(pow (df ((df > 2.5) && (df < 6.5)), 2.0) ); Vector ref{2, 2, 2, 9, 16, 25, 36, 2, 2, 2}; BOOST_CHECK(allNear(dh, ref, 1e-4)); } BOOST_FIXTURE_TEST_CASE(sum_func, Fixture) { BOOST_CHECK_CLOSE_FRACTION( sum (df ((df > 2.5) && (df < 6.5))), 18, 1e-4); } BOOST_FIXTURE_TEST_CASE(sumsquares_func, Fixture) { BOOST_CHECK_CLOSE_FRACTION( sumsquares (df ((df > 2.5) && (df < 6.5))), 86, 1e-4); } BOOST_FIXTURE_TEST_CASE(product_func, Fixture) { BOOST_CHECK_CLOSE_FRACTION( product (df ((df > 2.5) && (df < 6.5))), 360, 1e-4); } BOOST_FIXTURE_TEST_CASE(mean_func, Fixture) { BOOST_CHECK_CLOSE_FRACTION( mean (df ((df > 2.5) && (df < 6.5))), 4.5, 1e-4); } BOOST_FIXTURE_TEST_CASE(variance_func, Fixture) { BOOST_CHECK_CLOSE_FRACTION( variance (df ((df > 2.5) && (df < 6.5))), 1.66667, 1e-4); } BOOST_FIXTURE_TEST_CASE(pvariance_func, Fixture) { BOOST_CHECK_CLOSE_FRACTION( pvariance (df ((df > 2.5) && (df < 6.5)), 1),1.66667, 1e-4); } BOOST_FIXTURE_TEST_CASE(stddev_func, Fixture) { BOOST_CHECK_CLOSE_FRACTION( stddev (df ((df > 2.5) && (df < 6.5))), 1.29099, 1e-4); } BOOST_FIXTURE_TEST_CASE(pstddev_func, Fixture) { BOOST_CHECK_CLOSE_FRACTION( pstddev (df ((df > 2.5) && (df < 6.5)), 1), 1.29099, 1e-4); } BOOST_FIXTURE_TEST_CASE(avdev_func, Fixture) { BOOST_CHECK_CLOSE_FRACTION( avdev (df ((df > 2.5) && (df < 6.5))), 1, 1e-4); } BOOST_FIXTURE_TEST_CASE(rms_func, Fixture) { BOOST_CHECK_CLOSE_FRACTION( rms (df ((df > 2.5) && (df < 6.5))), 4.63681, 1e-4); } BOOST_FIXTURE_TEST_CASE(median_even1_func, Fixture) { BOOST_CHECK_CLOSE_FRACTION( median (df ((df > 2.5) && (df < 6.5)), true), 4.5, 1e-4); } BOOST_FIXTURE_TEST_CASE(median_even2_func, Fixture) { Vector dfunsort(10); dfunsort.assign_conforming( df ); double tmp; tmp = dfunsort (9); dfunsort (9) = dfunsort (5); dfunsort (5) = tmp; double result (-1.0); result = median (dfunsort ((dfunsort > 2.5) && (dfunsort < 6.5))); BOOST_CHECK_CLOSE_FRACTION( result, 4.5, 1e-4); Vector ref{0, 1, 2, 3, 4, 9, 6, 7, 8, 5}; BOOST_CHECK(allNear(dfunsort, ref, 1e-4)); } BOOST_FIXTURE_TEST_CASE(median_odd1_func, Fixture) { BOOST_CHECK_CLOSE_FRACTION( median (df ((df > 2.5) && (df < 7.5)), true), 5, 1e-4); } BOOST_FIXTURE_TEST_CASE(median_odd2_func, Fixture) { Vector dfunsort(10); dfunsort.assign_conforming( df ); double tmp; tmp = dfunsort (9); dfunsort (9) = dfunsort (5); dfunsort (5) = tmp; double result (-1.0); result = median (dfunsort ((dfunsort > 2.5) && (dfunsort < 7.5))); BOOST_CHECK_CLOSE_FRACTION( result, 5, 1e-4); Vector ref{0, 1, 2, 3, 4, 9, 6, 7, 8, 5}; BOOST_CHECK(allNear(dfunsort, ref, 1e-4)); } BOOST_FIXTURE_TEST_CASE(square_func, Fixture) { dh.assign_conforming(square (df ((df > 2.5) && (df < 6.5))) ); Vector ref{2, 2, 2, 9, 16, 25, 36, 2, 2, 2}; BOOST_CHECK(allNear(dh, ref, 1e-4)); } BOOST_FIXTURE_TEST_CASE(cube_func, Fixture) { dh.assign_conforming(cube (df ((df > 2.5) && (df < 6.5))) ); Vector ref{2, 2, 2, 27, 64, 125, 216, 2, 2, 2}; BOOST_CHECK(allNear(dh, ref, 1e-4)); } BOOST_AUTO_TEST_CASE(complex_variance_func) { // Test Complex variance. Vector> vecc(10); indgen (vecc, std::complex(1.5,-3.3)); Vector vecb(10, false); vecb[2] = vecb[5] = true; BOOST_CHECK (arrays_internal::near (pvariance(vecc(vecb)), (pvariance(real(vecc(vecb))) + pvariance(imag(vecc(vecb)))))); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tMaskArrMath2.cc000066400000000000000000000126501476623553700214060ustar00rootroot00000000000000//# tMaskArrMath2.cc: Test program for MaskedArrays mathematical operations. //# Copyright (C) 1993,1994,1995,1996,1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include "../IPosition.h" #include "../Array.h" #include "../ArrayMath.h" #include "../ArrayLogical.h" //#include "../ArrayIO.h" #include "../Vector.h" #include "../Matrix.h" #include "../Cube.h" #include "../ArrayError.h" #include "../MaskedArray.h" #include "../MaskArrMath.h" #include #include "TestUtilities.h" using namespace casacore; BOOST_AUTO_TEST_SUITE(masked_array_math2) struct Fixture { Vector df, dh; Vector b; Vector dk; Fixture() : df(10), dh(10), b(10), dk({0., 1., 5., 3., 9., 2., 7., 7., 4., 3.}) { indgen (df); } }; BOOST_FIXTURE_TEST_CASE(fixture, Fixture) { check(dk, {0., 1., 5., 3., 9., 2., 7., 7., 4., 3.}); } BOOST_FIXTURE_TEST_CASE(min_ma, Fixture) { BOOST_CHECK_CLOSE_FRACTION( min (dk ((dk > 2.5) && (dk < 7.5))), 3, 1e-6); } BOOST_FIXTURE_TEST_CASE(max_ma, Fixture) { BOOST_CHECK_CLOSE_FRACTION( max (dk ((dk > 2.5) && (dk < 7.5))), 7, 1e-6); } BOOST_FIXTURE_TEST_CASE(minmax_ma1, Fixture) { double minVal; double maxVal; IPosition minPos (1); IPosition maxPos (1); minMax (minVal, maxVal, minPos, maxPos, dk((dk > 2.5) && (dk < 7.5))); BOOST_CHECK_CLOSE_FRACTION(minVal, 3, 1e-6); BOOST_CHECK_CLOSE_FRACTION(maxVal, 7, 1e-6); BOOST_CHECK_EQUAL(minPos, IPosition(1, 3)); BOOST_CHECK_EQUAL(maxPos, IPosition(1, 6)); } BOOST_FIXTURE_TEST_CASE(minmax_ma2, Fixture) { double minVal; double maxVal; minMax (minVal, maxVal, dk((dk > 2.5) && (dk < 7.5))); BOOST_CHECK_CLOSE_FRACTION(minVal, 3, 1e-6); BOOST_CHECK_CLOSE_FRACTION(maxVal, 7, 1e-6); } BOOST_FIXTURE_TEST_CASE(min_ma_3pars, Fixture) { // min (MaskedArray, Array, Array) dh = 2.0; min (dh((dk > 2.5) && (dk < 7.5)), dk, df); // Input: 0., 1., 5., 3., 9., 2., 7., 7., 4., 3. check( dh, {2., 2., 2., 3., 2., 2., 6., 7., 4., 3.}); // Output: 2., 2., 0., 0., 2., 2., 0., 0., 0., 0. } BOOST_FIXTURE_TEST_CASE(max_ma_3pars, Fixture) { dh = 2.0; max (dh((dk > 2.5) && (dk < 7.5)), dk, df); check( dh, {2., 2., 5., 3., 2., 2., 7., 7., 8., 9.}); } BOOST_FIXTURE_TEST_CASE(min_ma_2pars_a, Fixture) { dh = 2.0; dh.assign_conforming( min (dk((dk > 2.5) && (dk < 7.5)), df) ); check( dh, {2., 2., 2., 3., 2., 2., 6., 7., 4., 3.}); } BOOST_FIXTURE_TEST_CASE(max_ma_2pars, Fixture) { dh = 2.0; dh.assign_conforming( max (dk((dk > 2.5) && (dk < 7.5)), df) ); check( dh, {2., 2., 5., 3., 2., 2., 7., 7., 8., 9.}); } BOOST_FIXTURE_TEST_CASE(min_ma_2pars_b, Fixture) { dh = 2.0; // min(Array, MaskedArray) dh.assign_conforming( min (dk((dk > 2.5) && (dk < 7.5)), df) ); check( dh, {2., 2., 2., 3., 2., 2., 6., 7., 4., 3.}); } BOOST_FIXTURE_TEST_CASE(max_ma_2pars_b, Fixture) { dh = 2.0; dh.assign_conforming( max (df, dk((dk > 2.5) && (dk < 7.5))) ); check( dh, {2., 2., 5., 3., 2., 2., 7., 7., 8., 9.}); } BOOST_FIXTURE_TEST_CASE(min_ma_2pars_c, Fixture) { // min(MaskedArray, MaskedArray) dh = 2.0; dh.assign_conforming(min (dk((dk > 2.5) && (dk < 7.5)), df((df > 2.5) && (df < 7.5))) ); check( dh, {2., 2., 2., 3., 2., 2., 6., 7., 2., 2.}); } BOOST_FIXTURE_TEST_CASE(max_ma_2pars_c, Fixture) { dh = 2.0; dh.assign_conforming(max (dk((dk > 2.5) && (dk < 7.5)), df((df > 2.5) && (df < 7.5))) ); check( dh, {2., 2., 2., 3., 2., 2., 7., 7., 2., 2.}); } BOOST_FIXTURE_TEST_CASE(min_ma_2pars_d, Fixture) { // min (MaskedArray, double) dh = 2.0; dh.assign_conforming( min (dk((dk > 2.5) && (dk < 7.5)), 5.0) ); check( dh, {2., 2., 5., 3., 2., 2., 5., 5., 4., 3.}); } BOOST_FIXTURE_TEST_CASE(max_ma_2pars_d, Fixture) { dh = 2.0; dh.assign_conforming( max (dk((dk > 2.5) && (dk < 7.5)), 5.0) ); check( dh, {2., 2., 5., 5., 2., 2., 7., 7., 5., 5.}); } BOOST_FIXTURE_TEST_CASE(min_ma_2pars_e, Fixture) { dh = 2.0; dh.assign_conforming( min (5.0, dk((dk > 2.5) && (dk < 7.5))) ); check( dh, {2., 2., 5., 3., 2., 2., 5., 5., 4., 3.}); } BOOST_FIXTURE_TEST_CASE(max_ma_2pars_e, Fixture) { dh = 2.0; dh.assign_conforming( max (5.0, dk((dk > 2.5) && (dk < 7.5))) ); check( dh, {2., 2., 5., 5., 2., 2., 7., 7., 5., 5.}); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tMaskedArray.cc000066400000000000000000000430551476623553700213600ustar00rootroot00000000000000//# tMaskedArray.cc: Test program for MaskedArrays //# Copyright (C) 1993,1994,1995,1996,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include "../IPosition.h" #include "../Array.h" #include "../ArrayMath.h" #include "../ArrayLogical.h" //#include "../ArrayIO.h" #include "../Vector.h" #include "../Matrix.h" #include "../Cube.h" #include "../ArrayError.h" #include "../MaskedArray.h" #include "../MaskArrIO.h" #include "../LogiCube.h" #include "../LogiVector.h" #include "TestUtilities.h" #include #include using namespace casacore; BOOST_AUTO_TEST_SUITE(masked_array) BOOST_AUTO_TEST_CASE(assignment) { const IPosition ip(1,5); Vector x(ip), y(ip); LogicalArray b(ip); Array z(ip); bool xOK = x.ok(); BOOST_CHECK(xOK); size_t xNdim = x.ndim(); BOOST_CHECK_EQUAL(xNdim, 1); size_t xNelements = x.nelements(); BOOST_CHECK_EQUAL(xNelements, 5); IPosition xShape = x.shape(); BOOST_CHECK_EQUAL(xShape, IPosition(1,5)); x=1; indgen(y); b = (x <= y); check(x, {1, 1, 1, 1, 1}); check(y, {0, 1, 2, 3, 4}); check(b, {0, 1, 1, 1, 1}); MaskedArray m(z, b); z=0; check(z, {0, 0, 0, 0, 0}); m = 5; check(z, {0, 5, 5, 5, 5}); MaskedArray n(m); n = 6; check(z, {0, 6, 6, 6, 6}); z(b) = 7; check(z, {0, 7, 7, 7, 7}); z (x <= y) = 8; check(z, {0, 8, 8, 8, 8}); } BOOST_AUTO_TEST_CASE(assignment_to_vector) { Vector vec1{1, 2, 3, 4, 5}; Vector vec2{6, 7, 8, 9, 10}; LogicalVector lvec{true, true, false, true, true}; vec1 = vec2(lvec); Vector ref{6, 7, 3, 9, 10}; BOOST_CHECK_EQUAL_COLLECTIONS(vec1.begin(), vec1.end(), ref.begin(), ref.end()); } BOOST_AUTO_TEST_CASE(array_operations1) { Array yc (IPosition(1,24)); indgen (yc); const Array ycc (yc); check(ycc(ycc<18).getArray(), {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}); check(ycc(ycc<18).getMask(), {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}); check(yc((yc<18))(yc>3), {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}); (yc(yc<18))(yc>3) = 8; check(yc, {0, 1, 2, 3, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 18, 19, 20, 21, 22, 23}); } BOOST_AUTO_TEST_CASE(array_operations2) { Array yc (IPosition(1,24)); indgen (yc); check(yc((yc<18)(yc>3)), {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}); yc((yc<18)(yc>3)) = 8; check(yc, {0, 1, 2, 3, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 18, 19, 20, 21, 22, 23}); } BOOST_AUTO_TEST_CASE(array_operations3) { Array yc (IPosition(1,24)); indgen (yc); check((yc(yc>7)) ((yc<18)(yc>3)), {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}); (yc(yc>7)) ((yc<18)(yc>3)) = 8; check(yc, {0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 18, 19, 20, 21, 22, 23}); } BOOST_AUTO_TEST_CASE(array_operations4) { Array yc (IPosition(1,24)); indgen (yc); const Array ycc (yc); const MaskedArray ycc1 (yc, yc<18); MaskedArray ycc2 (yc, yc<18); const MaskedArray ycc3 (yc, yc<18); MaskedArray ycc4 (yc, yc<18); check(ycc1(ycc>7), {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}); check(ycc2(ycc>7), {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}); check(ycc3(ycc>7), {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}); check(ycc4(ycc>7), {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}); check(yc(yc>7), {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}); check(ycc(ycc>7), {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}); } BOOST_AUTO_TEST_CASE(array_operations5) { Array yc (IPosition(2,20,30)); indgen (yc); Array ycc (yc); MaskedArray ycc1 (yc, yc<318||yc/2*2==yc); MaskedArray ycc2 (ycc1(IPosition(2,15,14), IPosition(2,18,19))); check(ycc2, { 295, 296, 297, 298, 315, 316, 317, 318, 335, 336, 337, 338, 355, 356, 357, 358, 375, 376, 377, 378, 395, 396, 397, 398 }, { 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1 }); check(ycc2(IPosition(2,1,0), IPosition(2,3,4), IPosition(2,1,2)), { 296, 297, 298, 336, 337, 338, 376, 377, 378 }, { 1, 1, 1, 1, 0, 1, 1, 0, 1 }); } BOOST_AUTO_TEST_CASE(vector_operations1) { Vector w(5), z({0, 8, 8, 8, 8}); indgen (w, 6); w.assign_conforming( z(w yc (24); indgen (yc); const Vector ycc (yc); check(ycc(ycc<18), {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}); check((yc(yc<18))(yc>3), {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}); (yc(yc<18))(yc>3) = 8; check(yc, {0, 1, 2, 3, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 18, 19, 20, 21, 22, 23}); } BOOST_AUTO_TEST_CASE(vector_operations3) { Vector yc (24); indgen (yc); check(yc((yc<18)(yc>3)), {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}); yc((yc<18)(yc>3)) = 8; check(yc, {0, 1, 2, 3, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 18, 19, 20, 21, 22, 23}); } BOOST_AUTO_TEST_CASE(vector_operations4) { Vector yc (24); indgen (yc); check( (yc(yc>7)) ((yc<18)(yc>3)), {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}); (yc(yc>7)) ((yc<18)(yc>3)) = 8; check(yc, {0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 18, 19, 20, 21, 22, 23}); } BOOST_AUTO_TEST_CASE(matrix_operations1) { Matrix v8(5u,3u); Matrix v(5u,3u); v8 = 8; indgen (v); check(v8, { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 }); check(v, { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 } ); v.assign_conforming( v8(v yc (4u,6u); indgen (yc); const Matrix ycc (yc); check(ycc(ycc<18), {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}); check((yc(yc<18))(yc>3), {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}); (yc(yc<18))(yc>3) = 8; check(yc, {0, 1, 2, 3, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 18, 19, 20, 21, 22, 23}); } BOOST_AUTO_TEST_CASE(matrix_operations3) { Matrix yc (4u,6u); indgen (yc); check( yc((yc<18)(yc>3)), {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}); yc((yc<18)(yc>3)) = 8; check(yc, {0, 1, 2, 3, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 18, 19, 20, 21, 22, 23}); } BOOST_AUTO_TEST_CASE(matrix_operations4) { Matrix yc (4u,6u); indgen (yc); check( (yc(yc>7)) ((yc<18)(yc>3)), {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}); (yc(yc>7)) ((yc<18)(yc>3)) = 8; check(yc, {0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 18, 19, 20, 21, 22, 23}); } BOOST_AUTO_TEST_CASE(cube_operations1) { Cube u15(5,3,2); Cube u(5,3,2); u15 = 15; indgen (u); BOOST_CHECK_EQUAL(to_string(u15), "Ndim=3 Axis Lengths: [5, 3, 2] \n" "[0, 0, 0][15, 15, 15, 15, 15]\n" "[0, 1, 0][15, 15, 15, 15, 15]\n" "[0, 2, 0][15, 15, 15, 15, 15]\n" "[0, 0, 1][15, 15, 15, 15, 15]\n" "[0, 1, 1][15, 15, 15, 15, 15]\n" "[0, 2, 1][15, 15, 15, 15, 15]\n"); BOOST_CHECK_EQUAL(to_string(u), "Ndim=3 Axis Lengths: [5, 3, 2] \n" "[0, 0, 0][0, 1, 2, 3, 4]\n" "[0, 1, 0][5, 6, 7, 8, 9]\n" "[0, 2, 0][10, 11, 12, 13, 14]\n" "[0, 0, 1][15, 16, 17, 18, 19]\n" "[0, 1, 1][20, 21, 22, 23, 24]\n" "[0, 2, 1][25, 26, 27, 28, 29]\n"); u.assign_conforming( u15(u yc (4,3,2); indgen (yc); const Cube ycc (yc); check(ycc(ycc<18), {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}); check((yc(yc<18))(yc>3), {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}); (yc(yc<18))(yc>3) = 8; check(yc, {0, 1, 2, 3, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 18, 19, 20, 21, 22, 23}); } BOOST_AUTO_TEST_CASE(cube_operations3) { Cube yc (4,3,2); indgen (yc); check((yc(yc<18))(yc>3), {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}); yc((yc<18)(yc>3)) = 8; check(yc, {0, 1, 2, 3, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 18, 19, 20, 21, 22, 23}); } BOOST_AUTO_TEST_CASE(cube_operations4) { Cube yc (4,3,2); indgen (yc); check((yc(yc>7)) ((yc<18)(yc>3)), {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}); (yc(yc>7)) ((yc<18)(yc>3)) = 8; check(yc, {0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 18, 19, 20, 21, 22, 23}); } struct MCUFixture { Cube cu; LogicalCube lcu; MCUFixture() : cu (5, 3, 2), lcu (5, 3, 2) { indgen (cu); lcu.assign_conforming( (cu > 10) && (cu <= 20) ); } }; BOOST_FIXTURE_TEST_CASE(compressed_array1, MCUFixture) { MaskedArray mcu (cu, lcu); BOOST_CHECK_EQUAL(to_string(mcu), "Array: Ndim=3 Axis Lengths: [5, 3, 2] \n" "[0, 0, 0][0, 1, 2, 3, 4]\n" "[0, 1, 0][5, 6, 7, 8, 9]\n" "[0, 2, 0][10, 11, 12, 13, 14]\n" "[0, 0, 1][15, 16, 17, 18, 19]\n" "[0, 1, 1][20, 21, 22, 23, 24]\n" "[0, 2, 1][25, 26, 27, 28, 29]\n" "\n" "Mask: Ndim=3 Axis Lengths: [5, 3, 2] \n" "[0, 0, 0][0, 0, 0, 0, 0]\n" "[0, 1, 0][0, 0, 0, 0, 0]\n" "[0, 2, 0][0, 1, 1, 1, 1]\n" "[0, 0, 1][1, 1, 1, 1, 1]\n" "[0, 1, 1][1, 0, 0, 0, 0]\n" "[0, 2, 1][0, 0, 0, 0, 0]\n"); } BOOST_FIXTURE_TEST_CASE(compressed_array2, MCUFixture) { MaskedArray mcu (cu, lcu); Vector vec (mcu.getCompressedArray()); check(vec, {11, 12, 13, 14, 15, 16, 17, 18, 19, 20}); } BOOST_FIXTURE_TEST_CASE(compressed_array3, MCUFixture) { MaskedArray mcu (cu, lcu); Matrix mat (mcu.getCompressedArray (IPosition (2,5,2))); check(mat, {11, 12, 13, 14, 15, 16, 17, 18, 19, 20}); } BOOST_FIXTURE_TEST_CASE(compressed_array4, MCUFixture) { MaskedArray mcu (cu, lcu); Matrix mat (5u,2u); mcu.getCompressedArray (mat); check(mat, {11, 12, 13, 14, 15, 16, 17, 18, 19, 20}); mcu.setCompressedArray (-mat); BOOST_CHECK_EQUAL(to_string(mcu), "Array: Ndim=3 Axis Lengths: [5, 3, 2] \n" "[0, 0, 0][0, 1, 2, 3, 4]\n" "[0, 1, 0][5, 6, 7, 8, 9]\n" "[0, 2, 0][10, -11, -12, -13, -14]\n" "[0, 0, 1][-15, -16, -17, -18, -19]\n" "[0, 1, 1][-20, 21, 22, 23, 24]\n" "[0, 2, 1][25, 26, 27, 28, 29]\n" "\n" "Mask: Ndim=3 Axis Lengths: [5, 3, 2] \n" "[0, 0, 0][0, 0, 0, 0, 0]\n" "[0, 1, 0][0, 0, 0, 0, 0]\n" "[0, 2, 0][0, 1, 1, 1, 1]\n" "[0, 0, 1][1, 1, 1, 1, 1]\n" "[0, 1, 1][1, 0, 0, 0, 0]\n" "[0, 2, 1][0, 0, 0, 0, 0]\n"); } struct MCU2 { Cube cu; LogicalCube lcu; MCU2() : cu (5u, 3u, 2u), lcu(5u, 3u, 2u) { indgen (cu); lcu.assign_conforming( (cu > 10) && (cu <= 20) ); } }; BOOST_FIXTURE_TEST_CASE(read_only1, MCU2) { MaskedArray mcu (cu, lcu); BOOST_CHECK(!mcu.isReadOnly()); } BOOST_FIXTURE_TEST_CASE(read_only2, MCU2) { MaskedArray mcu (cu, lcu, false); BOOST_CHECK(!mcu.isReadOnly()); } BOOST_FIXTURE_TEST_CASE(read_only3, MCU2) { MaskedArray mcu (cu, lcu, true); BOOST_CHECK(mcu.isReadOnly()); } BOOST_FIXTURE_TEST_CASE(read_only4, MCU2) { MaskedArray mcu (cu, lcu); mcu.setReadOnly(); BOOST_CHECK(mcu.isReadOnly()); } BOOST_FIXTURE_TEST_CASE(read_only5, MCU2) { MaskedLogicalArray mlcu (lcu, lcu); MaskedArray mcu (cu, mlcu, true); BOOST_CHECK(mcu.isReadOnly()); } BOOST_FIXTURE_TEST_CASE(read_only6, MCU2) { MaskedArray mmcu (cu, lcu); MaskedArray mcu (mmcu, lcu, true); BOOST_CHECK(mcu.isReadOnly()); } BOOST_FIXTURE_TEST_CASE(read_only7, MCU2) { MaskedArray mmcu (cu, lcu); MaskedLogicalArray mlcu (lcu, lcu); MaskedArray mcu (mmcu, mlcu, true); BOOST_CHECK(mcu.isReadOnly()); } BOOST_FIXTURE_TEST_CASE(read_only8, MCU2) { MaskedArray mmcu (cu, lcu, true); MaskedArray mcu (mmcu, lcu, false); BOOST_CHECK(mcu.isReadOnly()); } BOOST_FIXTURE_TEST_CASE(read_only9, MCU2) { MaskedArray mmcu (cu, lcu, true); MaskedLogicalArray mlcu (lcu, lcu); MaskedArray mcu (mmcu, mlcu, false); BOOST_CHECK(mcu.isReadOnly()); } BOOST_FIXTURE_TEST_CASE(read_only10, MCU2) { const Array ccu (cu); BOOST_CHECK(ccu(lcu).isReadOnly()); } BOOST_FIXTURE_TEST_CASE(read_only11, MCU2) { MaskedArray mcu (cu, lcu, true); BOOST_CHECK(mcu(lcu).isReadOnly()); } BOOST_FIXTURE_TEST_CASE(read_only12, MCU2) { MaskedArray mmcu (cu, lcu, true); MaskedArray mcu (mmcu); BOOST_CHECK(mcu.isReadOnly()); } BOOST_FIXTURE_TEST_CASE(read_only13, MCU2) { MaskedArray mmcu (cu, lcu, true); MaskedArray mcu (mmcu, false); BOOST_CHECK(mcu.isReadOnly()); } BOOST_FIXTURE_TEST_CASE(read_only14, MCU2) { MaskedArray mmcu (cu, lcu, true); MaskedArray mcu (mmcu, true); BOOST_CHECK(mcu.isReadOnly()); } BOOST_FIXTURE_TEST_CASE(read_only15, MCU2) { MaskedArray mcu (cu, lcu, true); BOOST_CHECK(!mcu.copy().isReadOnly()); } BOOST_FIXTURE_TEST_CASE(read_only16, MCU2) { MaskedArray mcu (cu, lcu, true); BOOST_CHECK(!mcu.copy(false).isReadOnly()); } BOOST_FIXTURE_TEST_CASE(read_only17, MCU2) { MaskedArray mcu (cu, lcu); BOOST_CHECK(mcu.copy(true).isReadOnly()); } BOOST_FIXTURE_TEST_CASE(read_only18, MCU2) { MaskedArray mcu (cu, lcu); BOOST_CHECK(!mcu.copy(false).isReadOnly()); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tMatrix.cc000066400000000000000000000175271476623553700204260ustar00rootroot00000000000000#include #include #include #include "../ArrayLogical.h" #include "../Matrix.h" using namespace casacore; BOOST_AUTO_TEST_SUITE(matrix_class) // Simple matrix tests BOOST_AUTO_TEST_CASE( matrix_initialize ) { Matrix a(5u, 5u); a = 3; BOOST_CHECK(a.nrow() == a.ncolumn() && a.nrow() == 5); BOOST_CHECK(allEQ(a, 3)); BOOST_CHECK(allLE(a, 3)); BOOST_CHECK(allEQ(a, a)); BOOST_CHECK(allNE(a, 1)); BOOST_CHECK (allEQ(Matrix(5u,6u, 4u), 4)); } BOOST_AUTO_TEST_CASE( matrix_assign ) { Matrix a(5u, 5u, 3), b; b.assign_conforming( 2*a ); BOOST_CHECK(allEQ (b, 6)); a.row(3) = 6; BOOST_CHECK(allEQ (a.row(3), 6)); a.column(3) = 1; BOOST_CHECK(allEQ (a.column(3), 1)); a.diagonal(-1) = 7; BOOST_CHECK(allEQ (a.diagonal(-1), 7)); } BOOST_AUTO_TEST_CASE( matrix_slice ) { Matrix a(5u, 5u, 3); Matrix c = a(Slice(2,1), Slice(3,1)); BOOST_CHECK_EQUAL(c.size(), 1); BOOST_CHECK_EQUAL(c(0, 0), 3); } BOOST_AUTO_TEST_CASE( matrix_reform ) { Matrix a(5u, 5u, 3); IPosition l(1); l(0) = a.nelements(); Vector d(a.reform(l)); for (int i = 0; i < 5; i++) for (int j = 0; j < 5; j++) BOOST_CHECK(a(i,j) == d(i + j*5)); } BOOST_AUTO_TEST_CASE( matrix_from_vector ) { Vector v(10); indgen(v); Matrix vm(v); BOOST_CHECK_EQUAL(vm.ndim(), 2); BOOST_CHECK_EQUAL(vm.nelements(), v.nelements()); for (int i = 0; i < int(v.nelements()); i++) { BOOST_CHECK_EQUAL(vm(i,0), v(i)); BOOST_CHECK_EQUAL(v(i), i); } } BOOST_AUTO_TEST_CASE( matrix_storage1 ) { Matrix m(8u, 8u, -1); m = -1; bool deleteIt; int *storage = m.getStorage(deleteIt); BOOST_CHECK_EQUAL(deleteIt, false); BOOST_CHECK_EQUAL(storage[0], -1); BOOST_CHECK_EQUAL(storage[8*8-1], -1); for (size_t i = 0; i < m.nelements(); i++) storage[i] = 1; BOOST_CHECK(allEQ (m, 1)); m.putStorage(storage, deleteIt); } BOOST_AUTO_TEST_CASE( matrix_storage2 ) { Matrix m(8u, 8u, 1); bool deleteIt; int *storage = m(Slice(0,2,3), Slice(2,2,4)).getStorage(deleteIt); BOOST_CHECK_EQUAL(deleteIt, true); for (int i=0; i < 4; i++) storage[i] = 0; BOOST_CHECK_EQUAL(m(0,2), 1); BOOST_CHECK_EQUAL(m(0,6), 1); BOOST_CHECK_EQUAL(m(3,2), 1); BOOST_CHECK_EQUAL(m(3,6), 1); m(Slice(0,2,3), Slice(2,2,4)).putStorage(storage, deleteIt); BOOST_CHECK_EQUAL(m(0,2), 0); BOOST_CHECK_EQUAL(m(0,6), 0); BOOST_CHECK_EQUAL(m(3,2), 0); BOOST_CHECK_EQUAL(m(3,6), 0); } void checkRCDVec (const Vector& v1, const Vector& v2) { BOOST_CHECK (allEQ(v1,v2)); Array::const_iterator iter1 = v1.begin(); Array::const_iterator iter2 = v2.begin(); for (size_t i=0; i& vn, const Vector& vc) { Slice sl(1,3,2); // start=1,n=3,inc=2 checkRCDVec (vn, vc); checkRCDVec (vn(sl), vc(sl)); checkRCDVec (vn(IPosition(1,1), IPosition(1,5), IPosition(1,2)), vn(sl)); checkRCDVec (vc(IPosition(1,1), IPosition(1,5), IPosition(1,2)), vc(sl)); } void doRowColDiag (const Matrix& m) { // Make contiguous copy of matrix. Matrix cm(m.copy()); BOOST_CHECK (cm.contiguousStorage()); // Check row selection and subsetting. Vector r0(m.row(1)); Vector cr0(cm.row(1)); BOOST_CHECK (!r0.contiguousStorage() && !cr0.contiguousStorage()); checkRCD (r0, cr0); // Check column selection and subsetting. Vector c0(m.column(1)); Vector cc0(cm.column(1)); BOOST_CHECK (cc0.contiguousStorage()); checkRCD (c0, cc0); // Check diagonal selection and subsetting. Vector d0(m.diagonal()); Vector cd0(cm.diagonal()); BOOST_CHECK (!d0.contiguousStorage() && !cd0.contiguousStorage()); checkRCD (d0, cd0); } BOOST_AUTO_TEST_CASE( row_col_diag ) { Matrix m(18,18); indgen (m); doRowColDiag (m); doRowColDiag (m(IPosition(2,1,1), IPosition(2,12,12), IPosition(2,1,1))); doRowColDiag (m(IPosition(2,1,1), IPosition(2,12,12), IPosition(2,2,2))); doRowColDiag (m(IPosition(2,1,2), IPosition(2,17,12), IPosition(2,3,2))); } BOOST_AUTO_TEST_CASE( init_from_data ) { IPosition shape(2, 2, 2); std::unique_ptr> values(new std::vector(shape.product())); Matrix c(shape, values->data(), COPY); values.reset(); c.resize(IPosition(2, 2, 3), false); c.resize(4, 4, false); BOOST_CHECK(true); } BOOST_AUTO_TEST_CASE( reference_1d_array ) { // Matrix.reference(1-d array) Array ai(IPosition(1,10)); Matrix mi; mi.reference(ai); BOOST_CHECK(mi.shape() == IPosition(2,10,1)); ai = 11; BOOST_CHECK(allEQ(mi, 11)); } BOOST_AUTO_TEST_CASE( matrix_identity ) { for (size_t i=0; i<20; i++) { Matrix x = Matrix::identity(i); BOOST_CHECK(x.ncolumn() == i); BOOST_CHECK(x.nrow() == i); for (size_t j=0; j arr; Matrix mat(arr); BOOST_CHECK (mat.ndim()==2 && mat.nelements()==0); BOOST_CHECK (mat.shape() == IPosition(2,0)); } BOOST_AUTO_TEST_CASE( non_degenerate ) { // Test if a non-degerate Matrix throws an exception. Matrix m1(IPosition(2,1,2)); Matrix mr; BOOST_CHECK_THROW(mr.nonDegenerate(m1), std::exception); mr.nonDegenerate(m1, 1); BOOST_CHECK (mr.shape() == IPosition(2,1,2)); } BOOST_AUTO_TEST_CASE( array_assign ) { // Array assign Array ai(IPosition(1,10)); ai = 1; Matrix mi(5,3); mi = 2; bool exc = false; try { mi.assign (ai); } catch (std::exception&) { exc = true; } BOOST_CHECK (exc); BOOST_CHECK(mi.shape() == IPosition(2,5,3)); BOOST_CHECK(allEQ(mi, 2)); ai.assign (mi); BOOST_CHECK(ai.shape() == IPosition(2,5,3)); BOOST_CHECK(allEQ(ai, 2)); } BOOST_AUTO_TEST_CASE( uninitialized_constructor_a ) { Matrix y1(5, 4, Matrix::uninitialized); BOOST_CHECK_EQUAL (y1.shape()[0], 5); BOOST_CHECK_EQUAL (y1.shape()[1], 4); y1 = 7; BOOST_CHECK (allEQ(y1, 7)); } BOOST_AUTO_TEST_CASE( uninitialized_constructor_b ) { Matrix y1(IPosition{5, 4}, Matrix::uninitialized); BOOST_CHECK_EQUAL (y1.shape()[0], 5); BOOST_CHECK_EQUAL (y1.shape()[1], 4); y1 = 7; BOOST_CHECK (allEQ(y1, 7)); } BOOST_AUTO_TEST_CASE( assign_from_vector ) { Matrix lhs(IPosition{10, 1}, 1.0f); Vector rhs(10, 13.0f); lhs = rhs; std::vector ref(10, 13.0f); BOOST_CHECK_EQUAL_COLLECTIONS (lhs.begin(), lhs.end(), ref.begin(), ref.end()); BOOST_CHECK_EQUAL_COLLECTIONS (lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); } BOOST_AUTO_TEST_CASE( move_assign_from_vector ) { Matrix lhs(IPosition{10, 1}, 1.0f); Vector rhs(10, 27.0f); lhs = std::move(rhs); std::vector ref(10, 27.0f); BOOST_CHECK_EQUAL_COLLECTIONS (lhs.begin(), lhs.end(), ref.begin(), ref.end()); } BOOST_AUTO_TEST_CASE( assign_from_empty_array ) { Matrix matrix; matrix = Array(); BOOST_CHECK_EQUAL( matrix.shape().size(), 2); BOOST_CHECK_EQUAL( matrix.shape(), (IPosition{0,0}) ); } BOOST_AUTO_TEST_CASE( assign_from_empty_vector ) { Matrix matrix; matrix = Vector(); BOOST_CHECK_EQUAL( matrix.shape().size(), 2); BOOST_CHECK_EQUAL( matrix.shape(), (IPosition{0,0}) ); } BOOST_AUTO_TEST_CASE( reference_empty_vector ) { Matrix matrix; matrix.reference(Vector()); BOOST_CHECK_EQUAL( matrix.shape().size(), 2); BOOST_CHECK_EQUAL( matrix.shape(), (IPosition{0,0}) ); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tMatrixMath.cc000066400000000000000000000051241476623553700212260ustar00rootroot00000000000000//# tMatrixMath.cc: Test functions in MatrixMath.h //# Copyright (C) 1995,1996,1999,2001, 2004 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include "../MatrixMath.h" #include "../ArrayMath.h" #include "../ArrayLogical.h" #include using namespace casacore; BOOST_AUTO_TEST_SUITE(matrix_math) BOOST_AUTO_TEST_CASE( all ) { Matrix ind(3,3); ind(0,0) = 2; ind(0,1) = 8; ind(0,2) = 6; ind(1,0) = 4; ind(1,1) = 2; ind(1,2) = -2; ind(2,0) = 3; ind(2,1) = -1; ind(2,2) = 1; Matrix outd(3,3); outd(0,0) = 2; outd(0,1) = 4; outd(0,2) = 3; outd(1,0) = 8; outd(1,1) = 2; outd(1,2) = -1; outd(2,0) = 6; outd(2,1) = -2; outd(2,2) = 1; BOOST_CHECK(allNearAbs(transpose(ind), outd, 0.00001)); // Now test the other types - float/std::complex/std::complex Matrix inf(3,3), outf(3,3); convertArray(inf, ind); convertArray(outf, outd); BOOST_CHECK(allNearAbs(transpose(inf), outf, 0.00001)); Matrix> inc(3,3), outc(3,3); convertArray(inc, ind); convertArray(outc, outd); BOOST_CHECK(allNearAbs(transpose(inc), outc, 0.00001)); Matrix> indc(3,3), outdc(3,3); convertArray(indc, ind); convertArray(outdc, outd); BOOST_CHECK(allNearAbs(transpose(indc), outdc, 0.00001)); Vector a(2, 2); Vector b(2, 3); BOOST_CHECK_EQUAL(crossProduct2D(a, b), 0); a[0] = -2; BOOST_CHECK_EQUAL(crossProduct2D(a, b),-12); b[1] = -5; BOOST_CHECK_EQUAL(crossProduct2D(a, b), 4); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tMedianSmooth.cc000066400000000000000000000043121476623553700215350ustar00rootroot00000000000000//# ArrayMath.cc: Arithmetic functions defined on Arrays //# Copyright (C) 1993,1994,1995,1996,1997,1998,1999,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include "../Array.h" #include "../ArrayMath.h" #include "../ArrayPartMath.h" #include using namespace casacore; BOOST_AUTO_TEST_SUITE(median_smooth) BOOST_AUTO_TEST_CASE(test) { const size_t specsize=50; const int width=1; IPosition box(1,width); Vector input(specsize); for(size_t i=0;i medians = slidingArrayMath(input, box, MedianFunc(false,true,false)); Vector ref{ 0, 1, 2, 3, 3, 1.01, 1.01, 2.01, 3.01, 3.01, 1.02, 1.02, 2.02, 3.02, 3.02, 1.03, 1.03, 2.03, 3.03, 3.03, 1.04, 1.04, 2.04, 3.04, 3.04, 1.05, 1.05, 2.05, 3.05, 3.05, 1.06, 1.06, 2.06, 3.06, 3.06, 1.07, 1.07, 2.07, 3.07, 3.07, 1.08, 1.08, 2.08, 3.08, 3.08, 1.09, 1.09, 2.09, 3.09, 0 }; BOOST_CHECK_EQUAL(medians.shape(), ref.shape()); for(size_t i=0; i!=ref.nelements(); ++i) BOOST_CHECK_CLOSE_FRACTION(medians[i], ref[i], 1e-4); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tSlice.cc000066400000000000000000000070751476623553700202160ustar00rootroot00000000000000//# tSlice.cc: Test program for class Slice. //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include "../Slice.h" #include "../Slicer.h" #include "../Vector.h" #include using namespace casacore; BOOST_AUTO_TEST_SUITE(slice) BOOST_AUTO_TEST_CASE( check_slices_uninitialized ) { Slicer first; IPosition shape(3,100,110,120); Vector > slices; IPosition shp = Slice::checkSlices (slices, first, shape); BOOST_CHECK_EQUAL (slices.size(), 3); BOOST_CHECK_EQUAL (shp, shape); BOOST_CHECK_EQUAL (first.start(), IPosition(3,0)); BOOST_CHECK_EQUAL (first.length(), shape); BOOST_CHECK_EQUAL (first.stride(), IPosition(3,1)); } BOOST_AUTO_TEST_CASE( check_slices_sized ) { Slicer first; IPosition shape(3,100,110,120); Vector > slices(3); IPosition shp = Slice::checkSlices (slices, first, shape); BOOST_CHECK_EQUAL (slices.size(), 3); BOOST_CHECK_EQUAL (shp, shape); BOOST_CHECK_EQUAL (first.start(), IPosition(3,0)); BOOST_CHECK_EQUAL (first.length(), shape); BOOST_CHECK_EQUAL (first.stride(), IPosition(3,1)); } BOOST_AUTO_TEST_CASE( check_slices_initialized ) { Slicer first; IPosition shape(3,100,110,120); Vector > slices(2); slices[0].resize(2); slices[0][0] = Slice(20,10); slices[0][1] = Slice(25,20); slices[1].resize(3); slices[1][0] = Slice(22,12,2); slices[1][1] = Slice(20,10); slices[1][2] = Slice(34,10,2); IPosition shp = Slice::checkSlices (slices, first, shape); BOOST_CHECK_EQUAL (slices.size(), 3); BOOST_CHECK_EQUAL (shp, IPosition(3,30,32,shape[2])); BOOST_CHECK_EQUAL (first.start(), IPosition(3,20,22,0)); BOOST_CHECK_EQUAL (first.length(), IPosition(3,10,12,shape[2])); BOOST_CHECK_EQUAL (first.stride(), IPosition(3,1,2,1)); } BOOST_AUTO_TEST_CASE( constructor ) { Slice slice(3,10,5); BOOST_CHECK_EQUAL (slice.start(), 3); BOOST_CHECK_EQUAL (slice.length(), 10); BOOST_CHECK_EQUAL (slice.end(), 48); BOOST_CHECK_EQUAL (slice.inc(), 5); } BOOST_AUTO_TEST_CASE( constructor_endislength1 ) { Slice slice(3,48,5, false); BOOST_CHECK_EQUAL (slice.start(), 3); BOOST_CHECK_EQUAL (slice.length(), 10); BOOST_CHECK_EQUAL (slice.end(), 48); BOOST_CHECK_EQUAL (slice.inc(), 5); } BOOST_AUTO_TEST_CASE( constructor_endislength2 ) { Slice slice(2,10,5, false); BOOST_CHECK_EQUAL (slice.start(), 2); BOOST_CHECK_EQUAL (slice.length(), 2); BOOST_CHECK_EQUAL (slice.end(), 7); BOOST_CHECK_EQUAL (slice.inc(), 5); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tSlicer.cc000066400000000000000000000231461476623553700203750ustar00rootroot00000000000000//# tSlicer.cc: This program tests the Slicer class //# Copyright (C) 1994,1995,1996,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include "../Slicer.h" #include "../Slice.h" #include "../IPosition.h" #include "../ArrayError.h" #include #include "TestUtilities.h" // Test the Slicer class using namespace casacore; BOOST_AUTO_TEST_SUITE(slicer) struct Fixture { // Define the shape of an array. // Also define an origin. IPosition shape; IPosition blc,trc,inc; Fixture() : shape (2,20,30) { } }; BOOST_FIXTURE_TEST_CASE( define, Fixture ) { // The following (outcommented) constructor results in a compile // error, because this private constructor prevents an automatic // conversion of int to IPosition. //# Slicer xxx(0); // Now define some Slicer's and apply the shape. Slicer ns0 (IPosition(2,0,24)); check(ns0.inferShapeFromSource (shape, blc, trc, inc), {1, 1}); check(blc, {0, 24}); check(trc, {0, 24}); check(inc, {1, 1}); Slicer ns1 (IPosition(2,3,5), IPosition(2,13,21), Slicer::endIsLast); check(ns1.inferShapeFromSource (shape,blc,trc,inc), {11, 17}); check(blc, {3, 5}); check(trc, {13, 21}); check(inc, {1, 1}); Slicer ns2 (IPosition(2,3,5), IPosition(2,13,21), IPosition(2,3,2), Slicer::endIsLast); check(ns2.inferShapeFromSource (shape, blc,trc,inc), {4, 9}); check(blc, {3, 5}); check(trc, {12, 21}); check(inc, {3, 2}); BOOST_CHECK_EQUAL(ns2.ndim(), 2); check(ns2.start(), {3, 5}); check(ns2.end(), {13, 21}); check(ns2.stride(), {3, 2}); check(ns2.length(), {4, 9}); BOOST_CHECK_EQUAL(to_string(ns2), "[3, 5] to [13, 21] with stride [3, 2], length [4, 9]"); } BOOST_FIXTURE_TEST_CASE( from_output_length, Fixture ) { IPosition blc,trc,inc; // Define some Slicer's via an output length. Slicer ns10 (IPosition(2,0,24)); check(ns10.inferShapeFromSource (shape, blc, trc, inc), {1, 1}); check(blc, {0, 24}); check(trc, {0, 24}); check(inc, {1, 1}); Slicer ns11 (IPosition(2,3,5), IPosition(2,13,21)); check(ns11.inferShapeFromSource (shape, blc,trc,inc), {13, 21}); check(blc, {3, 5}); check(trc, {15, 25}); check(inc, {1, 1}); Slicer ns12 (IPosition(2,3,5), IPosition(2,4,11), IPosition(2,3,2)); check(ns12.inferShapeFromSource (shape, blc,trc,inc), {4, 11}); check(blc, {3, 5}); check(trc, {12, 25}); check(inc, {3, 2}); BOOST_CHECK_EQUAL(ns12.ndim(), 2); check(ns12.start(), {3, 5}); check(ns12.end(), {12, 25}); check(ns12.stride(), {3, 2}); check(ns12.length(), {4, 11}); } BOOST_FIXTURE_TEST_CASE( undetermined_blc_trc, Fixture ) { // Define some Slicer's with an undetermined blc and/or trc. Slicer ns20 (IPosition(2,Slicer::MimicSource,24)); check(ns20.inferShapeFromSource (shape, blc,trc,inc), {1, 1}); check(blc, {0, 24}); check(trc, {0, 24}); check(inc, {1, 1}); Slicer ns21 (IPosition(2,3,5), IPosition(2,13,Slicer::MimicSource), Slicer::endIsLast); check(ns21.inferShapeFromSource (shape, blc,trc,inc), {11, 25}); check(blc, {3, 5}); check(trc, {13, 29}); check(inc, {1, 1}); Slicer ns22 (IPosition(2,Slicer::MimicSource,5), IPosition(2,13,21), IPosition(2,3,2), Slicer::endIsLast); check(ns22.inferShapeFromSource (shape, blc,trc,inc), {5, 9}); check(blc, {0, 5}); check(trc, {12, 21}); check(inc, {3, 2}); BOOST_CHECK_EQUAL(ns22.ndim(), 2); check(ns22.start(), {-2147483646, 5}); check(ns22.end(), {13, 21}); check(ns22.stride(), {3, 2}); check(ns22.length(), {-2147483646, 9}); } BOOST_FIXTURE_TEST_CASE( undetermined_blc_length, Fixture ) { // Define some Slicer's with an undetermined blc and/or length. Slicer ns30 (IPosition(2,Slicer::MimicSource,24)); check(ns30.inferShapeFromSource (shape, blc,trc,inc), {1, 1}); check(blc, {0, 24}); check(trc, {0, 24}); check(inc, {1, 1}); Slicer ns31 (IPosition(2,3,5), IPosition(2,13,Slicer::MimicSource)); check(ns31.inferShapeFromSource (shape, blc,trc,inc), {13, 25}); check(blc, {3, 5}); check(trc, {15, 29}); check(inc, {1, 1}); Slicer ns32 (IPosition(2,Slicer::MimicSource,5), IPosition(2,5,11), IPosition(2,3,2)); check(ns32.inferShapeFromSource (shape, blc,trc,inc), {5, 11}); check(blc, {0, 5}); check(trc, {12, 25}); check(inc, {3, 2}); BOOST_CHECK_EQUAL(ns32.ndim(), 2); check(ns32.start(), {-2147483646, 5}); check(ns32.end(), {-2147483634, 25}); check(ns32.stride(), {3, 2}); check(ns32.length(), {5, 11}); Slicer ns40 (Slice(0), Slice(24)); check(ns40.inferShapeFromSource (shape, blc,trc,inc), {1, 1}); check(blc, {0, 24}); check(trc, {0, 24}); check(inc, {1, 1}); Slicer ns41 (Slice(3,13), Slice(5,21)); check(ns41.inferShapeFromSource (shape, blc,trc,inc), {13, 21}); check(blc, {3, 5}); check(trc, {15, 25}); check(inc, {1, 1}); Slicer ns42 (Slice(3,5,3), Slice(5,11,2)); check(ns42.inferShapeFromSource (shape, blc,trc,inc), {5, 11}); check(blc, {3, 5}); check(trc, {15, 25}); check(inc, {3, 2}); BOOST_CHECK_EQUAL(ns42.ndim(), 2); check(ns42.start(), {3, 5}); check(ns42.end(), {15, 25}); check(ns42.stride(), {3, 2}); check(ns42.length(), {5, 11}); Slice tmp1, tmp2(5,11,2); Slicer ns43(tmp1, tmp2); check(ns43.inferShapeFromSource (shape, blc,trc,inc), {20, 11}); check(blc, {0, 5}); check(trc, {19, 25}); check(inc, {1, 2}); BOOST_CHECK_EQUAL(ns43.ndim(), 2); check(ns43.start(), {-2147483646, 5}); check(ns43.end(), {-2147483646, 25}); check(ns43.stride(), {1, 2}); check(ns43.length(), {-2147483646, 11}); } BOOST_FIXTURE_TEST_CASE( zero_length, Fixture ) { // Try length 0. Slicer ns50 (Slice(0,5,2), Slice(24,0,3)); check(ns50.inferShapeFromSource (shape, blc,trc,inc), {5, 0}); check(blc, {0, 24}); check(trc, {8, 23}); check(inc, {2, 3}); BOOST_CHECK_EQUAL(ns50.ndim(), 2); check(ns50.start(), {0, 24}); check(ns50.end(), {8, 21}); check(ns50.stride(), {2, 3}); check(ns50.length(), {5, 0}); } BOOST_FIXTURE_TEST_CASE( negative_start_or_end, Fixture ) { // Define some Slicers with a negative start or end. Slicer ns60 (IPosition(2,-13,10), IPosition(2,15,-3), Slicer::endIsLast); check(ns60.inferShapeFromSource (shape, blc,trc,inc), {9, 18}); check(blc, {7, 10}); check(trc, {15, 27}); check(inc, {1, 1}); } BOOST_FIXTURE_TEST_CASE( copy_constructor, Fixture ) { // Try copy constructor. Slicer ns2 (IPosition(2,3,5), IPosition(2,13,21), IPosition(2,3,2), Slicer::endIsLast); ns2.inferShapeFromSource (shape, blc,trc,inc); Slicer ns3(ns2); BOOST_CHECK_EQUAL(ns3.ndim(), 2); check(ns3.start(), {3, 5}); check(ns3.end(), {13, 21}); check(ns3.stride(), {3, 2}); check(ns3.length(), {4, 9}); // Try assignment (the difference in ndim should work fine). Slicer ns4(IPosition(1,0)); BOOST_CHECK_EQUAL(ns4.ndim(), 1); ns4 = ns3; BOOST_CHECK_EQUAL(ns4.ndim(), 2); check(ns4.start(), {3, 5}); check(ns4.end(), {13, 21}); check(ns4.stride(), {3, 2}); check(ns4.length(), {4, 9}); } BOOST_FIXTURE_TEST_CASE( equality, Fixture ) { Slicer ns2 (IPosition(2,3,5), IPosition(2,13,21), IPosition(2,3,2), Slicer::endIsLast); ns2.inferShapeFromSource (shape, blc,trc,inc); Slicer ns3(ns2); Slicer ns50 (Slice(0,5,2), Slice(24,0,3)); check(ns50.inferShapeFromSource (shape, blc,trc,inc), {5, 0}); // Try equality BOOST_CHECK(ns2==ns3); BOOST_CHECK(!(ns2==ns50)); } BOOST_AUTO_TEST_CASE( errors ) { // Do some erroneous constructions. // different lengths BOOST_CHECK_THROW(Slicer(IPosition(2,0,0), IPosition(3,0,0,0)), ArrayError); BOOST_CHECK_THROW(Slicer(IPosition(2,0,0), IPosition(3,0,0,0), Slicer::endIsLast), ArrayError); // trc < blc BOOST_CHECK_THROW(Slicer(IPosition(2,0,1), IPosition(2,0,0), Slicer::endIsLast), ArrayError); // length < 0 BOOST_CHECK_THROW(Slicer(IPosition(2,0,1), IPosition(2,0,-1), Slicer::endIsLength), ArrayError); // inc <= 0 BOOST_CHECK_THROW(Slicer(IPosition(2,0,1), IPosition(2,1,1), IPosition(2,-1,0)), ArrayError); BOOST_CHECK_THROW(Slicer(IPosition(2,0,1), IPosition(2,1,1), IPosition(2,0,0)), ArrayError); } BOOST_FIXTURE_TEST_CASE( change_length, Fixture ) { // Check if changing length of trc,blc,inc works fine. Slicer ns90; check(ns90.inferShapeFromSource (IPosition(1,10), blc, trc, inc), {10}); check(blc, {0}); check(trc, {9}); check(inc, {1}); BOOST_CHECK_EQUAL(ns90.ndim(), 1); check(ns90.start(), {-2147483646}); check(ns90.end(), {-2147483646}); check(ns90.stride(), {1}); check(ns90.length(), {-2147483646}); // shape length invalid BOOST_CHECK_THROW(ns90.inferShapeFromSource (shape, blc, trc, inc), ArrayError); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tSlidingArrayMath.cc000066400000000000000000000120361476623553700223520ustar00rootroot00000000000000//# tSlidingArrayMath.cc: Test program for function slidingArrayMath //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include "../MaskArrMath.h" #include "../ArrayLogical.h" #include "../ArrayPartMath.h" //#include "../ArrayIO.h" #include #include using namespace casacore; BOOST_AUTO_TEST_SUITE(sliding_array_math) template void check(const Array& arr, std::initializer_list ref) { BOOST_CHECK_EQUAL(arr.nelements(), ref.size()); Array::const_iterator arrIter = arr.begin(); typename std::initializer_list::const_iterator refIter = ref.begin(); while(arrIter!=arr.end() && refIter!=ref.end()) { BOOST_CHECK_CLOSE_FRACTION(*arrIter, *refIter, 1e-6); ++arrIter; ++refIter; } } float smartMedian (const Array& arr) { std::vector bbuf(arr.size()); float* buf = bbuf.data(); int nl = 0; int nr = arr.size(); float pivot = arr(arr.shape()/2); Array::const_iterator iterEnd=arr.end(); for (Array::const_iterator iter=arr.begin(); iter!=iterEnd; ++iter) { float val = *iter; if (val <= pivot) { buf[nl++] = val; } else { buf[--nr] = val; } } if (nl >= nr/2) { std::nth_element(buf, buf+nr/2, buf+nl); return buf[nr/2]; } else { std::nth_element(buf+nl, buf+nr/2-nl, buf+nr-nl); return buf[nr/2-nl]; } } BOOST_AUTO_TEST_CASE( standard ) { IPosition shape(2,5,5); Array arr(shape); indgen (arr); check(slidingArrayMath(arr, IPosition(2,2), SumFunc(), false), {300}); check(slidingArrayMath(arr, IPosition(2,1), SumFunc(), false), {54, 63, 72, 99, 108, 117, 144, 153, 162}); check(slidingArrayMath(arr, IPosition(1,0), SumFunc(), false), {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24}); check(slidingArrayMath(arr, IPosition(4,2), SumFunc(), true), { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 300, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); check(slidingArrayMath(arr, IPosition(3,1), SumFunc(), true), { 0, 0, 0, 0, 0, 0, 54, 63, 72, 0, 0, 99, 108, 117, 0, 0, 144, 153, 162, 0, 0, 0, 0, 0, 0 }); check(slidingArrayMath(arr, IPosition(), SumFunc(), true), {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24}); check(slidingArrayMath(arr, IPosition(2,2), MedianFunc(), false), { 12 }); check(slidingArrayMath(arr, IPosition(2,1), MedianFunc(), false), { 6, 7, 8, 11, 12, 13, 16, 17, 18 }); check(slidingArrayMath(arr, IPosition(2,0), MedianFunc(), false), {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24}); } BOOST_AUTO_TEST_CASE( masked ) { IPosition shape(2,5,5); Array darr(shape); indgen(darr); MaskedArray arr = darr(darr<=float(10) || darr>float(14)); BOOST_CHECK_CLOSE_FRACTION( sum(arr), 250, 1e-6); check(slidingArrayMath(arr, IPosition(2,2), MaskedSumFunc(), false), { 250 }); check(slidingArrayMath(arr, IPosition(2,1), MaskedSumFunc(), false), {31, 27, 33, 76, 72, 78, 121, 117, 123 }); check(slidingArrayMath(arr, IPosition(4,2), MaskedSumFunc(), true), { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}); check(slidingArrayMath(arr, IPosition(3,1), MaskedSumFunc(), true), { 0, 0, 0, 0, 0, 0, 31, 27, 33, 0, 0, 76, 72, 78, 0, 0, 121, 117, 123, 0, 0, 0, 0, 0, 0 }); check(slidingArrayMath(arr, IPosition(2,2), MaskedMedianFunc(), false), { 10 }); check(slidingArrayMath(arr, IPosition(2,1), MaskedMedianFunc(), false), {5.0, 4.5, 5.5, 10.0, 12.0, 13.0, 17.0, 19.5, 20.5 }); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tStringArray.cc000066400000000000000000000035061476623553700214170ustar00rootroot00000000000000//# tStringArray.cc: This program tests Array and related classes //# Copyright (C) 1993,1994,1995,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include "../Array.h" #include "../Vector.h" #include "../Matrix.h" #include "../Cube.h" #include "../ArrayError.h" #include #include using namespace casacore; BOOST_AUTO_TEST_SUITE(string_array) BOOST_AUTO_TEST_CASE( vector_string ) { Vector vs(5); vs(0) = "Every";vs(1) = "Good";vs(2) = "Boy";vs(3) = "Deserves"; vs(4) = "Fudge"; BOOST_CHECK_EQUAL(vs(0), "Every"); BOOST_CHECK_EQUAL(vs(1), "Good"); BOOST_CHECK_EQUAL(vs(2), "Boy"); BOOST_CHECK_EQUAL(vs(3), "Deserves"); BOOST_CHECK_EQUAL(vs(4), "Fudge"); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tVector.cc000066400000000000000000000263371476623553700204230ustar00rootroot00000000000000//# tVector.cc: Test program for the Vector class //# Copyright (C) 2015 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //#include "../ArrayIO.h" #include "../Vector.h" #include #include using namespace std; using namespace casacore; BOOST_AUTO_TEST_SUITE(vector_class) BOOST_AUTO_TEST_CASE( int_io ) { Vector vvec1{1,2,4,-2}; std::ostringstream str; str << vvec1; BOOST_CHECK_EQUAL(str.str(), "[1, 2, 4, -2]"); } BOOST_AUTO_TEST_CASE( double_io ) { Vector vvec2(std::vector{1,2,4,-2}); std::ostringstream str; str << vvec2; BOOST_CHECK_EQUAL(str.str(), "[1, 2, 4, -2]"); } BOOST_AUTO_TEST_CASE( vector_init ) { Vector x(10); x = 5; for (int i=0; i < 10; i++) BOOST_CHECK_EQUAL(x(i), 5); } BOOST_AUTO_TEST_CASE( copy_constructor ) { Vector x(10); x = 5; Vector y(x); BOOST_CHECK(x.nrefs() == y.nrefs() && x.nrefs() > 1); for (int i=0; i < 10; i++) BOOST_CHECK_EQUAL(y(i), 5); } BOOST_AUTO_TEST_CASE( copy ) { Vector x(10); x = 5; Vector z(x.copy()); z = 11; for (int i=0; i < 10; i++) { BOOST_CHECK_EQUAL(z(i), 11); BOOST_CHECK_EQUAL(x(i), 5); } } BOOST_AUTO_TEST_CASE( slice1 ) { Vector x(10); x = 5; Vector z(x.copy()); z = 11; // The semantics of this changed; before, this could be done with the assignment operator x(Slice(0,5,2)).assign_conforming(z(Slice(0,5,2))); for(int i=0; i < 10; i += 2) { BOOST_CHECK_EQUAL(x(i), 11); BOOST_CHECK_EQUAL(x(i+1), 5); } } BOOST_AUTO_TEST_CASE( slice2 ) { Vector x1(5); indgen(x1); BOOST_CHECK(allEQ(x1(Slice(0,2,2)), Vector{0, 2})); } BOOST_AUTO_TEST_CASE( default_constructor ) { Vector zz; BOOST_CHECK_EQUAL(zz.nelements(),0); BOOST_CHECK_EQUAL(zz.size(), 0); BOOST_CHECK(zz.empty()); } BOOST_AUTO_TEST_CASE( init_constructor ) { Vector y1(5, 4); BOOST_CHECK (allEQ(y1, 4)); } BOOST_AUTO_TEST_CASE( uninitialized_constructor_a ) { Vector y1(7, Vector::uninitialized); BOOST_CHECK_EQUAL (*y1.shape().begin(), 7); y1 = 3; BOOST_CHECK (allEQ(y1, 3)); } BOOST_AUTO_TEST_CASE( uninitialized_constructor_b ) { Vector y1(IPosition{7}, Vector::uninitialized); BOOST_CHECK_EQUAL (*y1.shape().begin(), 7); y1 = 3; BOOST_CHECK (allEQ(y1, 3)); } BOOST_AUTO_TEST_CASE( slice_constructor ) { Vector x(10, 5); Vector z(10, 11); // The semantics of this changed; before, this could be done with the assignment operator x(Slice(0,5,2)).assign_conforming(z(Slice(0,5,2))); Vector zzz(x(Slice(0,5,2))); zzz.unique(); BOOST_CHECK_EQUAL(zzz.nrefs(), 1); BOOST_CHECK(allEQ(zzz, 11)); BOOST_CHECK_EQUAL(zzz.nelements(), 5); BOOST_CHECK_EQUAL(zzz.size(), 5); BOOST_CHECK(!zzz.empty()); } BOOST_AUTO_TEST_CASE( string ) { Vector vs(5); vs(0) = "Every";vs(1) = "Good";vs(2) = "Boy";vs(3) = "Deserves"; vs(4) = "Fudge"; BOOST_CHECK_EQUAL(vs(0), "Every"); BOOST_CHECK_EQUAL(vs(1), "Good"); BOOST_CHECK_EQUAL(vs(2), "Boy"); BOOST_CHECK_EQUAL(vs(3), "Deserves"); BOOST_CHECK_EQUAL(vs(4), "Fudge"); } BOOST_AUTO_TEST_CASE( resize ) { Vector x(10, 5); Vector z(10, 11); // The semantics of this changed; before, this could be done with the assignment operator x(Slice(0,5,2)).assign_conforming(z(Slice(0,5,2))); Vector zzz(x(Slice(0,5,2))); zzz.unique(); zzz.resize(10); zzz = 13; BOOST_CHECK_EQUAL(zzz.nelements(), 10); } BOOST_AUTO_TEST_CASE( assign_conforming ) { Vector yyy(10), zzz(10, 13); yyy = -13; zzz.assign_conforming( yyy ); BOOST_CHECK(allEQ(zzz, yyy)); BOOST_CHECK(allEQ(zzz, -13)); } BOOST_AUTO_TEST_CASE( tostdvector ) { Vector zzz(10, -13); std::vector stdvec = zzz.tovector(); BOOST_CHECK_EQUAL(stdvec.size(), 10); for(size_t i=0; i!=10; ++i) BOOST_CHECK_EQUAL(stdvec[i], -13); } BOOST_AUTO_TEST_CASE( from_stdvector ) { Vector aaa(std::vector(10, -13)); BOOST_CHECK(allEQ (aaa, Vector(10, -13))); BOOST_CHECK(allEQ (aaa, -13)); } // Various vector tests BOOST_AUTO_TEST_CASE( take_part ) { IPosition shape(4,20,21,22,23); Array arr(shape); indgen(arr); Array arr2; // Take a part of the original array. arr2.reference(arr(IPosition(4,9,0,9,9), IPosition(4,9,shape(1)-1,9,9))); Vector vec(arr2); BOOST_CHECK (vec.ndim()==1 && vec.nelements()==size_t(shape(1))); BOOST_CHECK (vec.shape() == IPosition(1,shape(1))); BOOST_CHECK (!vec.contiguousStorage()); for (size_t i=0; i arr(shape); indgen(arr); Array arr2; Array arr3(arr(IPosition(4,1,2,3,4), IPosition(4,19,18,20,22), IPosition(4,2))); IPosition shp3 = arr3.shape(); arr2.reference(arr3(IPosition(4,1,3,1,2), IPosition(4,1,3,shp3(2)-1,2), IPosition(4,1,1,2,1))); // Note that elements 1..shp3(2)-1 with step 2 gives shp3(2)/2 elements. Vector vec(arr2); BOOST_CHECK (vec.ndim()==1 && vec.size()==size_t(shp3(2))/2); BOOST_CHECK (vec.shape() == IPosition(1,shp3(2)/2)); BOOST_CHECK (!vec.contiguousStorage()); for (size_t i=0; i> values(new std::vector(shape.product())); Vector c(shape, values->data(), COPY); values.reset(); c.resize(IPosition(1, 3), false); c.resize(4, false); BOOST_CHECK(true); } BOOST_AUTO_TEST_CASE( copy_resize ) { // Test the copying Vector::resize functions Vector vi(10); indgen(vi); vi.resize(20, true); BOOST_CHECK(vi(0) == 0 && vi(9) == 9); vi.resize(IPosition(1,5), true); BOOST_CHECK(vi(0) == 0 && vi(4) == 4); vi.resize(IPosition(1,10)); // All bets are off, nothing to test } BOOST_AUTO_TEST_CASE( tovector ) { // tovector tests Vector x(3); x[0] = 20; x[1] = 33; x[2] = -20; std::vector tx; x.tovector(tx); Vector xx(x.tovector()); BOOST_CHECK(tx.size() == x.size()); BOOST_CHECK(tx.size() == xx.size()); for (size_t i=0; i vs1(3, 2); BOOST_CHECK (allEQ(vs1, size_t(2))); Vector vs2(3, 2); BOOST_CHECK (allEQ(vs2, size_t(2))); // Construct from iterator. std::vector v(5); v[0] = 2; v[1] = 3; v[2] = 4; v[3] = 5; v[4] = 6; Vector myvec(v.begin(), v.size(), 0); BOOST_CHECK(v.size() == myvec.size()); for (size_t i=0; i<5; i++) { BOOST_CHECK(v[i] == myvec[i]); } // Construct from std::vector. std::vector v2(2); v2[0] = 5; v2[1] = -2; Vector myvec2(v2); BOOST_CHECK(v2.size() == myvec2.size()); for (size_t i=0; i<2; i++) { BOOST_CHECK(v2[i] == myvec2[i]); } // Construct and convert type. Vector myvec3(v2.begin(), v2.size(), 0); BOOST_CHECK(v2.size() == myvec3.size()); for (size_t i=0; i<2; i++) { BOOST_CHECK(v2[i] == myvec3[i]); } } BOOST_AUTO_TEST_CASE( multi_dimensional ) { IPosition shape(4,20,21,22,23); Array arr(shape); indgen(arr); Array arr2; // Test use of a Vector from an Array with > 1 dimensions. arr2.reference(arr(IPosition(4,0), IPosition(4,shape(0)-1,0,0,0))); Vector vec(arr2); BOOST_CHECK (vec.ndim()==1 && vec.nelements()==size_t(shape(0))); BOOST_CHECK (vec.shape() == IPosition(1,shape(0))); BOOST_CHECK (vec.contiguousStorage()); for (size_t i=0; i arr(shape); indgen(arr); Array arr2; // Test it for a slice. arr2.reference(arr(IPosition(4,0,9,9,9), IPosition(4,shape(0)-1,9,9,9))); Vector vec(arr2); BOOST_CHECK (vec.ndim()==1 && vec.nelements()==size_t(shape(0))); BOOST_CHECK (vec.shape() == IPosition(1,shape(0))); BOOST_CHECK (vec.contiguousStorage()); for (size_t i=0; i arr(shape); indgen(arr); Array arr2; arr2.reference(arr(IPosition(4,9,9,9,9), IPosition(4,9,9,9,9))); Vector vec(arr2); BOOST_CHECK (vec.ndim()==1 && vec.nelements()==1); BOOST_CHECK (vec.shape() == IPosition(1,1)); BOOST_CHECK (vec.contiguousStorage()); BOOST_CHECK (vec(0) == arr(IPosition(4,9,9,9,9))); BOOST_CHECK (vec(0) == arr2(IPosition(4,0,0,0,0))); } BOOST_AUTO_TEST_CASE( multi_dimensional_copy ) { // Test the Vector copy ctor for arrays with !1 dimension. Array arr; Vector vec(arr); BOOST_CHECK (vec.ndim()==1 && vec.nelements()==0); BOOST_CHECK (vec.shape() == IPosition(1,0)); } BOOST_AUTO_TEST_CASE( assign_empty_to_zero_len ) { Vector vec; vec = Array(); BOOST_CHECK_EQUAL( vec.shape().size(), 1); BOOST_CHECK( vec.shape() == IPosition{0} ); } BOOST_AUTO_TEST_CASE( assign_dimensional ) { Vector vec(16, 1); Array arr(IPosition{1, 1, 1, 16}, 2); vec = arr; BOOST_CHECK_EQUAL( vec.shape().size(), 1); BOOST_CHECK( vec.shape() == IPosition{16} ); std::vector ref(16, 2); BOOST_CHECK_EQUAL_COLLECTIONS(ref.begin(), ref.end(), vec.begin(), vec.end()); } BOOST_AUTO_TEST_CASE( move_assign_dimensional ) { Vector vec(16, 1); vec = Array(IPosition{1, 1, 1, 16}, 2); BOOST_CHECK_EQUAL( vec.shape().size(), 1); BOOST_CHECK( vec.shape() == IPosition{16} ); std::vector ref(16, 2); BOOST_CHECK_EQUAL_COLLECTIONS(ref.begin(), ref.end(), vec.begin(), vec.end()); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/Arrays/test/tVectorSTLIterator.cc000066400000000000000000000046031476623553700225100ustar00rootroot00000000000000//# tVectorSTLIterator.cc: Test program for the VectroSTLIterator class //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include "../VectorSTLIterator.h" #include "../ArrayMath.h" #include using namespace casacore; BOOST_AUTO_TEST_SUITE(vector_stl_iterator) BOOST_AUTO_TEST_CASE(vector_forward) { Vector vec(10); indgen (vec); { VectorSTLIterator iter(vec); for (int i=0; i(vec) == 4); } } BOOST_AUTO_TEST_CASE(vector_backward) { Vector vec(10); indgen (vec); VectorSTLIterator iter(vec); iter += vec.size(); for (int i=0; i vec(10); indgen (vec); // Uses ArraySTLIterator, not VectorSTLIterator. int i=0; for (Vector::const_iterator iter=vec.begin(); iter!=vec.end(); ++iter){ BOOST_CHECK (*iter == i); ++i; } } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/casa/BasicMath.h000066400000000000000000000057671476623553700162560ustar00rootroot00000000000000//# BasicMath.h: Basic math related classes. //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_BASICMATH_H #define CASA_BASICMATH_H #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // // Classes and global functions for basic math use // // // // // This module is a bag of related basic math classes and // global functions. // // The following functionality is available: //
          //
        • Templated functors that can be used with std::transform // to apply functions like sin, near, sqrt, etc. to iterators on // sequences like Array, Block, std::vector, etc. //
        • Templated functions // ConvertScalar // to convert scalars from one type to another. //
        • Class // Functional // to map a domain object into a range object. //
        • Functions Prime numbers //
        // // You may want to look at the individual header files // to see whether you might not prefer to include only the header // files you really need; it may be more efficient to do so. // // //
        //# //#
      • //# // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/BasicMath/000077500000000000000000000000001476623553700160665ustar00rootroot00000000000000casacore-3.7.1/casa/BasicMath/ConvertScalar.h000066400000000000000000000043121476623553700210050ustar00rootroot00000000000000//# ConvertScalar.h: Templated functions to convert scalars //# Copyright (C) 2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_CONVERTSCALAR_H #define CASA_CONVERTSCALAR_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Templated functions to convert scalars from one type to another. // // // // // Templated functions to convert scalars from one type to another. // They are useful to be able to convert a DComplex to a Complex // in templated classes. // A Complex,DComplex specialisation is necessary because the complex // class in the standard C++ library does not have such an automatic // conversion. // // template inline void convertScalar (T& out, F in) { out = static_cast(in); } inline void convertScalar (Complex& out, DComplex in) { out = Complex(in.real(), in.imag()); } // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/BasicMath/Functional.h000066400000000000000000000120571476623553700203460ustar00rootroot00000000000000//# Functional.h: Map a domain object into a range object via operator(). //# Copyright (C) 1995,1996,1999-2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_FUNCTIONAL_H #define CASA_FUNCTIONAL_H //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declaration template class Lattice; // Map a domain object into a range object via operator(). // // // // // The term ``Functional'' was chosen to follow the usage // in Barton and Nackman's ``Scientific and Engineering C++.'' // // // // A Functional is an abstract base class which // encapsulates the mapping of an object of type Domain into an // object of type Range. // This operation is invoked via operator() to make it look like // a function call. // // While these functions are function-like, there is no guarantee // that evaluations of the same parameter will yield the same result // (the implementor of a particular class could, for example, merely choose // to emit a random number). // However implementors of Functional classes are strongly // encouraged to implement (visible) side-effect free semantics in their // classes. // // A Functional object is used in circumstances similar to those // in which a function pointer could be used. An advantage of the // Functional objects is that it is possible to have more than // one of them at the same time. // Another potential advantage (not yet // implemented) is that it will be possible to perform functional // composition at run time, e.g. a=b+c where a,b, and c are // Functionals. // Another advantage is that since the Functional implementations // will in general be templated, the same source code would yield // instantiations for all the numeric types and for specializations like // automatic derivatives. // // To be of greatest utility, a library of functions that do mathematics, // plotting, etc. on Functional objects needs to be developed. // // // // The following simple example shows how you can write a function that uses a // Functional object. // // Double integrate1D(const Functional &f, // Double x1, Double x2, Double dx) { // uInt n = (xend - xstart) / dx; // Double sum = 0.0; // for (uInt i=0; i < n; i++) sum += f(x1 + i*dx) * dx; // return sum; // } // // Obviously this isn't a very serious algorithm! // // // // The specific application that caused the implementation of these // Functional // classes was the creation of the Fitting // module, which needed classes to represent the fitting functions. // // // //
      • Accessible default and copy constructors, assignment operators, // and destructors will almost always also be required. // // // //
      • A copy constructor is absolutely required for Range objects because // operator() returns Range objects by value. //
      • Accessible default constructors, assignment operators, // and destructors will almost always also be required. // // // //
      • For polymorphic access it could be that a clone() function // is needed at this level. // template class Functional { public: //# Constructors // Destructor virtual ~Functional(); //# Operators // Map a Domain x into a Range y value. virtual Range operator()(const Domain &x) const = 0; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/casa/BasicMath/Functional.tcc000066400000000000000000000030321476623553700206610ustar00rootroot00000000000000//# Functional.cc: Map a domain object into a range object via operator(). //# Copyright (C) 1995,1996,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_FUNCTIONAL_TCC #define CASA_FUNCTIONAL_TCC //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors template Functional::~Functional() {} } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/BasicMath/Functors.h000066400000000000000000000463711476623553700200550ustar00rootroot00000000000000//# Functors.h: Define STL functors for basic math functions. //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_FUNCTORS_H #define CASA_FUNCTORS_H #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Define a function to do a binary transform in place. // It is functionally equivalent to std::transform where the first and result // iterator are the same, but it is faster for non-trivial iterators. template inline void transformInPlace (InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, BinaryOperator op) { for (; first1!=last1; ++first1, ++first2) { *first1 = op(*first1, *first2); } } // Define a function to do a unary transform in place. // It is functionally equivalent to std::transform where the first and result // iterator are the same, but it is faster for non-trivial iterators. template inline void transformInPlace (InputIterator1 first1, InputIterator1 last1, UnaryOperator op) { for (; first1!=last1; ++first1) { *first1 = op(*first1); } } // Define a function (similar to std::accumulate) to do accumulation of // elements for which the corresponding mask value is true. // The default accumulation is addition. template inline Accum accumulateTrue (InputIterator first, InputIterator last, MaskIterator mask, Accum acc, BinaryOperator op = std::plus()) { for (; first!=last; ++first, ++mask) { if (*mask) acc = op(acc, *first); } return acc; } // Define a function (similar to std::accumulate) to do accumulation of // elements for which the corresponding mask value is false. // The default accumulation is addition. template inline Accum accumulateFalse (InputIterator first, InputIterator last, MaskIterator mask, Accum acc, BinaryOperator op = std::plus()) { for (; first!=last; ++first, ++mask) { if (!*mask) acc = op(acc, *first); } return acc; } // Define a function to compare all elements of two sequences. // It returns true if all elements compare true. // An example compare operator is std::equal_to. // template inline bool compareAll (InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, CompareOperator op) { for (; first1!=last1; ++first1, ++first2) { if (!op(*first1, *first2)) return false; } return true; } // For use with a constant left value. // This avoids use of bind1st or bind2nd which can fail for gcc-4.3. // (see ArrayMath.h). template inline bool compareAllLeft (InputIterator1 first1, InputIterator1 last1, T left, CompareOperator op) { for (; first1!=last1; ++first1) { if (!op(left, *first1)) return false; } return true; } // For use with a constant right value. // This avoids use of bind1st or bind2nd which can fail for gcc-4.3. // (see ArrayMath.h). template inline bool compareAllRight (InputIterator1 first1, InputIterator1 last1, T right, CompareOperator op) { for (; first1!=last1; ++first1) { if (!op(*first1, right)) return false; } return true; } // // Define a function to compare all elements of two sequences. // It returns true if any element compares true. // An example compare operator is std::equal_to. // template inline bool compareAny (InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, CompareOperator op) { for (; first1!=last1; ++first1, ++first2) { if (op(*first1, *first2)) return true; } return false; } // For use with a constant left value. // This avoids use of bind1st or bind2nd which can fail for gcc-4.3. // (see ArrayMath.h). template inline bool compareAnyLeft (InputIterator1 first1, InputIterator1 last1, T left, CompareOperator op) { for (; first1!=last1; ++first1) { if (op(left, *first1)) return true; } return false; } // For use with a constant right value. // This avoids use of bind1st or bind2nd which can fail for gcc-4.3. // (see ArrayMath.h). template inline bool compareAnyRight (InputIterator1 first1, InputIterator1 last1, T right, CompareOperator op) { for (; first1!=last1; ++first1) { if (op(*first1, right)) return true; } return false; } // // Functor to add variables of possibly different types. // This is unlike std::plus which requires equal types. template struct Plus { RES operator() (const L& x, const R& y) const { return RES(x)+y; } }; // Functor to subtract variables of possibly different types. // This is unlike std::minus which requires equal types. template struct Minus { RES operator() (const L& x, const R& y) const { return RES(x)-y; } }; // Functor to multiply variables of possibly different types. // This is unlike std::multiplies which requires equal types. template struct Multiplies { RES operator() (const L& x, const R& y) const { return RES(x)*y; } }; // Functor to divide variables of possibly different types. // This is unlike std::divides which requires equal types. template struct Divides { RES operator() (const L& x, const R& y) const { return RES(x)/y; } }; // Functor to take modulo of (integer) variables of possibly different types // in the C way. // This is unlike std::modulo which requires equal types. template struct Modulo { RES operator() (const L& x, const R& y) const { return RES(x)%y; } }; // Functor to take modulo of variables of possibly different types // using the floor modulo (% as used in Python). template struct FloorMod { RES operator() (const L& x, const R& y) const { return floormod (RES(x), RES(y)); } }; // Functor for bitwise and of (integer) values. template struct BitAnd { T operator() (const T& x, const T& y) const { return x&y; } }; // Functor for bitwise or of (integer) values. template struct BitOr { T operator() (const T& x, const T& y) const { return x|y; } }; // Functor for bitwise xor of (integer) values. template struct BitXor { T operator() (const T& x, const T& y) const { return x^y; } }; // Functor for bitwise negate of (integer) values. template struct BitNegate { T operator() (const T& x) const { return ~x; } }; // Functor to test for NaN. // It can be used in something like: // // std::transform (array.begin(), array.end(), // result.begin(), IsNaN()); // template struct IsNaN { bool operator() (T value) const { return isNaN (value); } }; // Functor to test for infinity. template struct IsInf { bool operator() (T value) const { return isInf (value); } }; // Functor to test for finiteness. template struct IsFinite { bool operator() (T value) const { return isFinite (value); } }; // Functor to test if two values are relatively near each other. // It can be used in something like: // // std::transform (left.begin(), left.cend(), right.begin(), // result.cbegin(), Near(tolerance)); // template struct Near { explicit Near (double tolerance=1e-5) : itsTolerance (tolerance) {} bool operator() (L left, R right) const { return near (left, L(right), itsTolerance); } private: double itsTolerance; }; // Functor to test for if two values are absolutely near each other. template struct NearAbs { explicit NearAbs (double tolerance=1e-13) : itsTolerance (tolerance) {} bool operator() (L left, R right) const { return nearAbs (left, L(right), itsTolerance); } private: double itsTolerance; }; // Functor to apply sin. template struct Sin { RES operator() (T value) const { return RES(sin (value)); } }; // Functor to apply sinh. template struct Sinh { RES operator() (T value) const { return RES(sinh (value)); } }; // Functor to apply asin. template struct Asin { RES operator() (T value) const { return RES(asin (value)); } }; // Functor to apply cos. template struct Cos { RES operator() (T value) const { return RES(cos (value)); } }; // Functor to apply cosh. template struct Cosh { RES operator() (T value) const { return RES(cosh (value)); } }; // Functor to apply acos. template struct Acos { RES operator() (T value) const { return RES(acos (value)); } }; // Functor to apply tan. template struct Tan { RES operator() (T value) const { return RES(tan (value)); } }; // Functor to apply tanh. template struct Tanh { RES operator() (T value) const { return RES(tanh (value)); } }; // Functor to apply atan. template struct Atan { RES operator() (T value) const { return RES(atan (value)); } }; // Functor to apply atan2. template struct Atan2 { RES operator() (L left, R right) const { return RES(atan2 (left, L(right))); } }; // Functor to apply sqr (power of 2). template struct Sqr { RES operator() (T value) const { return RES(value*value); } }; // Functor to apply a power of 3. template struct Pow3 { RES operator() (T value) const { return RES(value*value*value); } }; // Functor to apply sqrt. template struct Sqrt { RES operator() (T value) const { return RES(sqrt (value)); } }; // Functor to apply exp. template struct Exp { RES operator() (T value) const { return RES(exp (value)); } }; // Functor to apply log. template struct Log { RES operator() (T value) const { return RES(log (value)); } }; // Functor to apply log10. template struct Log10 { RES operator() (T value) const { return RES(log10 (value)); } }; // Functor to apply abs. template struct Abs { RES operator() (T value) const { return RES(abs (value)); } }; // Functor to apply floor. template struct Floor { RES operator() (T value) const { return RES(floor (value)); } }; // Functor to apply ceil. template struct Ceil { RES operator() (T value) const { return RES(ceil (value)); } }; // Functor to apply round (e.g. -3.7 gets -4). template struct Round { RES operator() (T value) const { return RES(value<0 ? ceil(value-0.5) : floor(value+0.5)); } }; // Functor to apply sign (result is -1, 0, or 1). template struct Sign { RES operator() (T value) const { return (value<0 ? -1 : (value>0 ? 1:0)); } }; // Functor to form a complex number from the left and right value. template struct MakeComplex { RES operator() (L l, R r) const { return RES(l, r); } }; // Functor to form a complex number from the real part of the // left value and the right value. template struct MakeComplexReal { RES operator() (L l, R r) const { return RES(real(l), r); } }; // Functor to form a complex number from the left value and the // imaginary part of the right value. template struct MakeComplexImag { RES operator() (L l, R r) const { return RES(l, imag(r)); } }; // Functor to form a complex number from the real part of the // left value and the imaginary part of the right value. template struct MakeComplexRealImag { RES operator() (L l, R r) const { return RES(real(l), imag(r)); } }; // Functor to apply complex function conj. template struct Conj { RES operator() (T value) const { return RES(conj (value)); } }; // Functor to apply complex function real. template struct Real { RES operator() (T value) const { return RES(real (value)); } }; // Functor to apply complex function imag. template struct Imag { RES operator() (T value) const { return RES(imag (value)); } }; // Functor to apply complex function arg. template struct CArg { RES operator() (T value) const { return RES(arg (value)); } }; // Functor to apply complex function fabs. template struct CAbs { RES operator() (T value) const { return RES(fabs (value)); } }; // Functor to apply pow. template struct Pow { RES operator() (T left, E exponent) const { return RES(pow (left, exponent)); } }; // Functor to apply fmod. template struct Fmod { RES operator() (R left, L right) const { return RES(fmod (left, L(right))); } }; // Functor to get minimum of two values. template struct Min { RES operator() (L left, R right) const { return RES(left struct Max { RES operator() (L left, R right) const { return RES(left struct SumSqr { Accum operator() (Accum left, T right) const { return left + Accum(right)*Accum(right); } }; // Functor to add squared diff of right and base value to left. // It can be used to calculate the variance. // Note: it is specialized for complex values to handle real and imag separately. template struct SumSqrDiff { explicit SumSqrDiff(T base) : itsBase(base) {} Accum operator() (Accum left, T right) const { return left + (right-itsBase)*(right-itsBase); } private: Accum itsBase; // store as Accum, so subtraction results in Accum }; // Specialize for complex values. // Variance has to be taken for the absolute value of a complex value. thus // sum(abs((a[i] - mean)**2 // where the sqrt used in abs and the **2 cancel each other, thus can be left out. // See also https://en.wikipedia.org/wiki/Complex_random_variable#Variance // Note that although the sum is real, a complex value is used to have equal template types. template struct SumSqrDiff> { explicit SumSqrDiff(std::complex base) : itsBase(base) {} std::complex operator() (std::complex left, std::complex right) const { return left + ((right.real() - itsBase.real()) * (right.real() - itsBase.real()) + (right.imag() - itsBase.imag()) * (right.imag() - itsBase.imag())); } private: std::complex itsBase; }; // Functor to add absolute diff of right and base value to left. // It can be used to calculate the average deviation. template struct SumAbsDiff { explicit SumAbsDiff(T base) : itsBase(base) {} Accum operator() (Accum left, T right) const { return left + abs((right-itsBase)); } private: Accum itsBase; // store as Accum, so subtraction results in Accum }; // Functor to downcase a std::string. The result is a casacore::String. struct Downcase { String operator() (const std::string& value) const { return downcase(value); } }; // Functor to upcase a std::string. The result is a casacore::String. struct Upcase { String operator() (const std::string& value) const { return upcase(value); } }; // Functor to capitalize a std::string. The result is a casacore::String. struct Capitalize { String operator() (const std::string& value) const { return capitalize(value); } }; // Functor to trim a std::string. The result is a casacore::String. // Leading and trailing whitespace is removed. struct Trim { String operator() (const std::string& value) const { return trim(value); } }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/BasicMath/Math.cc000066400000000000000000000172371476623553700173000ustar00rootroot00000000000000//# Math.cc: Implementation of miscellaneous functions in Math.h //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include // Changes for SUN CC port - abs changed to fabs for double and float args. // the following is needed to get the finite function used in isInf #if defined (AIPS_SOLARIS) || defined(AIPS_IRIX) #include #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN Bool near(uInt val1, uInt val2, Double tol) { if (tol <= 0) { return (val1 == val2); } if (val1 == val2) { return True; } else if (val1 > val2) { return (Double(val1-val2) <= tol*max(val1,val2)); } else { return (Double(val2-val1) <= tol*max(val1,val2)); } } Bool near(Int val1, Int val2, Double tol) { if (tol <=0) { return (val1 == val2); } if (val1 == val2) { return True; } if ((0 val2) { return (tol >= Double(val1 - val2)); } else { return (tol >= Double(val2 - val1)); } } Bool nearAbs(Int val1, Int val2, Double tol) { return (tol >= Double(std::abs(val2 - val1))); } Bool nearAbs(Float val1, Float val2, Double tol) { return (tol >= Double(fabs(val2 - val1))); } Bool nearAbs(Double val1, Double val2, Double tol) { return (tol >= fabs(val2 - val1)); } Float floatNaN() { static Float nanval; static Bool init = False; if (!init) { init = True; // All bits on is a NaN uChar *uptr = (uChar *)&nanval; for (uInt i=0; i= 0 ? lgr + 1.000001 : lgr - 0.000001); Double temp = val * pow(10.0, -i); using std::round; return sign*round(temp)*pow(10.0, i); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/BasicMath/Math.h000066400000000000000000000342251476623553700171360ustar00rootroot00000000000000//# Math.h: Casacore interface to and other scalar math functions //# Copyright (C) 1993,1994,1995,1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MATH_H #define CASA_MATH_H #include //# The following is to get abs(int) and (is)finite. #include #include // On some systems the following is needed to get the finite function #if defined (AIPS_SOLARIS) || defined(AIPS_IRIX) #include #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Casacore interface to math.h and other scalar math functions // // // // // Casacore interface to . You should include this file // rather than directly. It will be used to cover up any // deficiencies in the system . // This file does not include things like element-by-element // array operations. See the // ArrayMath // functions for these functions. // This file includes the standard math library. Hence besides the functions // defined here the following functions are also available. // // Double sin(Double x) Sine function // Double cos(Double x) Cosine function // Double tan(Double x) Tangent function // Double asin(Double x) Inverse sine function // Double acos(Double x) Inverse cosine function // Double atan(Double x) Inverse tangent function // Double atan2(Double y, Double x) Four quandrant inverse tangent function // Double hypot(Double y, Double x) Euclidean distance sqrt(x*x+y*y) // Double sinh(Double x) Hyperbolic sine // Double cosh(Double x) Hyperbolic cosine // Double tanh(Double x) Hyperbolic tangent // Double acosh(Double x) Inverse hyperbolic sine // Double asinh(Double x) Inverse hyperbolic cosine // Double atanh(Double x) Inverse hyperbolic tangent // Double sqrt(Double x) Square root // Double cbrt(Double x) Cube root // Double pow(Double x, Double y) x raised to the power of y // Double exp(Double x) Exponental function // Double expm1(Double x) exp(x)-1. Use when x is small. // Double log(Double x) Natural logarithm // Double log10(Double x) Base ten logarithm // Double log1p(Double x) log(x+1). Use when x is small // Double j0(Double x) Bessel function of the first kind, zeroth order // Double j1(Double x) Bessel function of the first kind, first order // Double jn(Int n, Double x) Bessel function of the first kind nth order // Double y0(Double x) Bessel function of the second kind, zeroth order // Double y1(Double x) Bessel function of the second kind, first order // Double yn(Int n, Double x) Bessel function of the second kind, nth order // // Double lgamma(Double x) Natural Log of the absolute value of the gamma // function // Double lgamma_r(Double x, Int* sign) Same as lgamma. The sign of the gamma // function is returned in the second argument. // Double erf(Double x) Error function // Double erfc(Double x) Complementary error function (1 - erf(x)). // Use for large x. // Double ceil(Double x) Returns the least integral value greater than or // equal to x // Double floor(Double x) Returns the least integral value than than or // equal to x // Double rint(Double x) Round to an integer using the current direction. // Double fabs(Double x) Absolute value of x // Double remainder(Double x, Double y) the remainder. x - y*Int(x/y) // Double fmod(Double x, Double y) As above. May differ by +/- y // Int isNaN(Double x) Returns 1 if x is a NaN, zero otherwise // Int ilogb(Double x) Unbiased exponent of x // Double logb(Double x) As above but returns floating point result // Double scalbn(Double x, Int n) x*2**n. Uses exponent manipulation. // Double scalb(Double x, Double n) x*2**n. As above but n is a Double // Double significand(Double x) Returns the fractional part of x // (between 1 and 2) // Double copysign(Double x, Double y) returns a value with the magnitude of // x and the sign bit of y. // Double nextafter(Double x, Double y) Returns the next machine representable // number after x in the direction specified by y // // // This file also includes the standard C library (stdlib.h). This is to obtain // a definition of the following functions. // // Int abs(Int x) absolute value function // // // // Returns f1**f2. The Double precision version is defined in the standard // library. But many compilers are not good enough to automatically do the type // promotion. Hence these functions are explicitly defined. // inline Float pow(Float f1, Double f2) {return Float(std::pow(Double(f1), f2));} inline Float pow(Double f1, Float f2) {return Float(std::pow(f1, Double(f2)));} inline Int pow(Int f1, Int f2) {return Int(std::pow(Double(f1), Double(f2)));} // // Return the integer "less than" point (i.e. the one further from zero if // "point" is negative. // inline Int ifloor(Float point) { if (point >= 0.0) return Int (point); else return Int(point - 1.0); } inline Int ifloor(Double point) { if (point >= 0.0) return Int(point); else return Int(point - 1.0); } // // Functions to get the max or min of two numbers. // inline Int max(Int a, Int b) { if (a > b) return a; else return b; } inline Int min(Int a, Int b) { if (a > b) return b; else return a; } inline uInt max(uInt a, uInt b){ if (a>b) return a; else return b; } inline uInt min(uInt a, uInt b){ if (a>b) return b; else return a; } inline uInt64 max(uInt64 a, uInt64 b){ if (a>b) return a; else return b; } inline uInt64 min(uInt64 a, uInt64 b){ if (a>b) return b; else return a; } inline Double max(Double a, Double b) { if (a > b) return a; else return b; } inline Double min(Double a, Double b) { if (a > b) return b; else return a; } inline Double max(Double a, Float b) { if (a > b) return a; else return b; } inline Double min(Double a, Float b) { if (a > b) return b; else return a; } inline Double max(Float a, Double b) { if (a > b) return a; else return b; } inline Double min(Float a, Double b) { if (a > b) return b; else return a; } inline Float max(Float a, Float b) { if (a > b) return a; else return b; } inline Float min(Float a, Float b) { if (a > b) return b; else return a; } // // Return the square of a value. // inline Int square(Int val) {return val*val;} inline Int64 square(Int64 val) {return val*val;} inline Float square(Float val) {return val*val;} inline Double square(Double val) {return val*val;} // // Return the cube of a value. // inline Int cube(Int val) {return val*val*val;} inline Int64 cube(Int64 val) {return val*val*val;} inline Float cube(Float val) {return val*val*val;} inline Double cube(Double val) {return val*val*val;} // // Return the sign of a value. // inline Int sign(Int val) {return val<0 ? -1 : (val>0 ? 1:0);} inline Int64 sign(Int64 val) {return val<0 ? -1 : (val>0 ? 1:0);} inline Float sign(Float val) {return val<0 ? -1 : (val>0 ? 1:0);} inline Double sign(Double val) {return val<0 ? -1 : (val>0 ? 1:0);} // // Return the floor modulo as used by Python (unlike C); divisor sign is used. // Note that function fmod can be used for C behaviour; dividend sign is used. // In Python: 5%3=2 -5%3=1 5%-3=-1 -5%-3=-2 // In C: 5%3=2 -5%3=-2 5%-3=2 -5%-3=-2 // inline Int floormod (Int x, Int y) { Int r = x%y; if (r != 0 && (x<0) != (y<0)) r+=y; return r; } inline Int64 floormod (Int64 x, Int64 y) { Int64 r = x%y; if (r != 0 && (x<0) != (y<0)) r+=y; return r; } inline Float floormod (Float x, Float y) { Float r = fmod(x,y); if (r != 0 && (x<0) != (y<0)) r+=y; return r; } inline Double floormod (Double x, Double y) { Double r = fmod(x,y); if (r != 0 && (x<0) != (y<0)) r+=y; return r; } // // Functions to return whether a value is "relatively" near another. Returns // tol > abs(val2 - val1)/max(abs(val1),(val2)). // If tol <= 0, returns val1 == val2. If either val is 0.0, take care of area // around the minimum number that can be represented. // Bool near(uInt val1, uInt val2, Double tol = 1.0e-5); Bool near(Int val1, Int val2, Double tol = 1.0e-5); Bool near(Float val1, Float val2, Double tol = 1.0e-5); Bool near(Float val1, Double val2, Double tol = 1.0e-5); Bool near(Double val1, Float val2, Double tol = 1.0e-5); Bool near(Double val1, Double val2, Double tol = 1.0e-13); // // The "allNear" versions are aliases for the normal "near" versions. They // exist to make template functions that work for both arrays and scalars // easier to write. These functions should be moved to ArrayMath.h // inline Bool allNear(uInt val1, uInt val2, Double tol = 1.0e-5) { return near(val1, val2, tol); } inline Bool allNear(Int val1, Int val2, Double tol = 1.0e-5) { return near(val1, val2, tol); } inline Bool allNear(Float val1, Double val2, Double tol = 1.0e-5) { return near(val1, val2, tol); } inline Bool allNear(Double val1, Float val2, Double tol = 1.0e-5) { return near(val1, val2, tol); } inline Bool allNear(Float val1, Float val2, Double tol = 1.0e-5) { return near(val1, val2, tol); } inline Bool allNear(Double val1, Double val2, Double tol = 1.0e-13) { return near(val1, val2, tol); } // // Functions to return whether a value is "absolutely" near another. Returns // tol > abs(val2 - val1) // Bool nearAbs(uInt val1, uInt val2, Double tol = 1.0e-5); Bool nearAbs(Int val1, Int val2, Double tol = 1.0e-5); Bool nearAbs(Float val1, Float val2, Double tol = 1.0e-5); Bool nearAbs(Float val1, Double val2, Double tol = 1.0e-5); Bool nearAbs(Double val1, Float val2, Double tol = 1.0e-5); Bool nearAbs(Double val1, Double val2, Double tol = 1.0e-13); // // The "allNearAbs" versions are aliases for the normal "nearAbs" // versions. They exist to make template functions that work for both arrays // and scalars easier to write. These functions should be in ArrayMath.h // inline Bool allNearAbs(uInt val1, uInt val2, uInt tol = 1) { return nearAbs(val1, val2, tol); } inline Bool allNearAbs(Int val1, Int val2, Int tol = 1) { return nearAbs(val1, val2, tol); } inline Bool allNearAbs(Float val1, Float val2, Double tol = 1.0e-5) { return nearAbs(val1, val2, tol); } inline Bool allNearAbs(Float val1, Double val2, Double tol = 1.0e-5) { return nearAbs(val1, val2, tol); } inline Bool allNearAbs(Double val1, Float val2, Double tol = 1.0e-5) { return nearAbs(val1, val2, tol); } inline Bool allNearAbs(Double val1, Double val2, Double tol = 1.0e-13) { return nearAbs(val1, val2, tol); } // // Functions to test if a floating point number is finite. // It is if it is NaN nor infinity. // inline Bool isFinite (const Float& val) { #if defined(AIPS_DARWIN) return std::isfinite(val); #else return finite(val); #endif } inline Bool isFinite (const Double& val) { #if defined(AIPS_DARWIN) return std::isfinite(val); #else return finite(val); #endif } // // Functions to test for IEEE NaN's. The Float variant uses an in-line // Macro examining the bit pattern (for portability and efficiency). The // Double version invokes the IEEE function isnan found in ieeefp.h or math.h // inline Bool isNaN (const Float& val) { return (((*(Int *)&(val) & 0x7f800000) == 0x7f800000) && ((*(Int *)&(val) & 0x007fffff) != 0x00000000)); } inline Bool isNaN(Double val) { return ( std::isnan(val) ); } // // Round a number to ndigit significant digits, usually used // for formatting for printing. //
        A non-integer ndigit=N+F, with integer N and fraction F, // is interpreted as follows. // For x = A*10^B, where B is an integer, A is rounded to N digits // if A > 10^F, otherwise N+1 digits. //
        For the default 2.5, a value of 32157 is rounded to 32000, // while 22157 is rounded to 22200. Double roundDouble(Double val, Double ndigit=2.5); // Functions that return IEEE NaN's. The specific NaN returned has all bits // set. This is 'quiet' NaN, and because the sign bit is set it may be // considered a negative number (but NaN's are not numbers!). // Float floatNaN(); Double doubleNaN(); void setNaN(Float& val); void setNaN(Double& val); // // Functions to test for IEEE Infinity's. Should work for positive or negative // infinity. // Bool isInf(Float val); Bool isInf(Double val); // // Functions that return an IEEE Infinity, (positive infinity). // Float floatInf(); Double doubleInf(); void setInf(Float& val); void setInf(Double& val); // //
        } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/BasicMath/Primes.cc000066400000000000000000000131351476623553700176370ustar00rootroot00000000000000//# Primes.cc: This class provides some prime number operations using a cached table //# Copyright (C) 1994,1995,1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include // For sqrt only namespace casacore { //# NAMESPACE CASACORE - BEGIN //The minimum size for the cacheTable const uInt MINSIZE = 31; Block Primes::cacheTable; std::mutex Primes::theirMutex; Bool Primes::isPrime(uInt number) { if (number < 2) return False; return(smallestPrimeFactor(number)==number ? True : False); } uInt Primes::aLargerPrimeThan( uInt number ) { std::lock_guard lock(theirMutex); // If number is equal to or larger than the last (and largest) element in // the table of primes, this function returns zero; otherwise, this // function returns the next higher prime in the table. if ( cacheTable.nelements() < MINSIZE ) initializeCache(); if ( number >= cacheTable[cacheTable.nelements() - 1] ) return 0; Int index = -1; for ( uInt i = cacheTable.nelements(); i > 0; i-- ) { if ( cacheTable[(i-1)] > number ) { index =(i-1); } } return cacheTable[ index ]; } uInt Primes::nextLargerPrimeThan( uInt number ) { std::lock_guard lock(theirMutex); uInt i; // This function increments number until it is prime. It finds the next // entry in the table of primes which is larger, and stores this entry's // index number. The table is resized to accomodate another entry, and // every entry after the stored index is moved over by one. The new prime // number is inserted to the spot marked by the stored index. if ( cacheTable.nelements() < MINSIZE ) { initializeCache(); } while( !isPrime( ++number ) ) {} uInt index = cacheTable.nelements(); for( i = cacheTable.nelements(); i > 0; i-- ) { if ( cacheTable[(i-1)] == number ) { return number; } if ( cacheTable[(i-1)] > number ) { index =(i-1); } } cacheTable.resize(cacheTable.nelements() + 1); for( i = ( cacheTable.nelements()-1 ); i > index; i-- ) { cacheTable[i] = cacheTable[i-1]; } cacheTable[ index ] = number; return number; } uInt Primes::smallestPrimeFactor( uInt number ) { // This function checks for factors: if found, the first (smallest) one is // returned, otherwise the original value is returned. // This algorithm is not the best, but checks for divisability by 6n +/- 1 if (number == 0) return 0; if ((number % 2) == 0) return 2; if ((number % 3) == 0) return 3; for (uInt i=5,k=7,sq=(uInt)(sqrt(Double(number))+1); i Primes::factor( uInt number ) { //If number is zero or one, this function returns a one-cell block //containing number; otherwise this fuction continues to resize the //block by one and store the next smallest factor of number in the //block until number equals the product of all the factors stored //in the block. Block multiples; if (number < 2) { multiples.resize(1); multiples[0] = number; } else { for (uInt index=0; number > 1; index++) { multiples.resize( index+1 ); multiples[index] = smallestPrimeFactor(number); number = number / multiples[index]; } } return multiples; } void Primes::initializeCache() { // This function resets the cache to a block of 30, which // contains the next prime greater than each power of two. cacheTable.resize( 30, True, False ); cacheTable[0] = 3; cacheTable[1] = 5; cacheTable[2] = 11; cacheTable[3] = 17; cacheTable[4] = 37; cacheTable[5] = 67; cacheTable[6] = 131; cacheTable[7] = 257; cacheTable[8] = 521; cacheTable[9] = 1031; cacheTable[10] = 2053; cacheTable[11] = 4099; cacheTable[12] = 8209; cacheTable[13] = 16411; cacheTable[14] = 32771; cacheTable[15] = 65537; cacheTable[16] = 131101; cacheTable[17] = 262147; cacheTable[18] = 524309; cacheTable[19] = 1048583; cacheTable[20] = 2097169; cacheTable[21] = 4194319; cacheTable[22] = 8388617; cacheTable[23] = 16777259; cacheTable[24] = 33554467; cacheTable[25] = 67108879; cacheTable[26] = 134217757; cacheTable[27] = 268435459; cacheTable[28] = 536870923; cacheTable[29] = 1073741827; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/BasicMath/Primes.h000066400000000000000000000156741476623553700175130ustar00rootroot00000000000000//# Primes.h: This class provides some prime number operations using a cached table //# Copyright (C) 1994,1995,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_PRIMES_H #define SCIMATH_PRIMES_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Creates a reference table of prime numbers, and some functions // // // // //
      • Understanding Block is only peripherally important. // // // // Prime has its usual definition (a number whose only factors are // itself and one.) Zero and one are not considered to be prime. // // // // The primary function of the Primes class is to create and maintain a list of // prime numbers. This class has no constructors; all member functions are // therefore declared static. The class maintains an internal cache table of // prime numbers. There are also several member functions of more general use, // which do not access the cached table. // // The table is initialized to contain the next prime greater than each power // of two. The function "aLargerPrimeThan" accepts a number and returns a // larger prime number from the table of prime numbers. The function // "nextLargerPrimeThan" finds the next greater integer that is a prime, // returns it, and inserts it into the table. There are also functions to // initialize and examine the table. // // The basic prime number operations are done in three functions: "isPrime" // determines whether a given number is a prime; "smallestPrimeFactor" finds // the smallest factor of a given number; and "factor" returns a Block // containing a number's factors. // // // // // #include // #include // #include // // // Refer also to tPrimes.cc // // int main() { // Block BB, DD; // uInt C, i; // if(! Primes::isPrime(4)) { //if this number // cout<<"Four is not a prime number"< 4 ) { //if difference is more // //than five, then // C = Primes::nextLargerPrimeThan(4); //find next lprime // } // DebugAssertExit( C == Primes::aLargerPrimeThan(4)); //now the prime resides // } //in the cache table // if( Primes::nCachedPrimes() > 50 ) { // Primes::initializeCache(); // } // DD = Primes::cachedPrimes(); // cout<<"The Table of Primes"< // // // // This class was conceived during the coding of a class that creates hash // tables. The hash table class works best with a table whose size is prime. // It uses the Primes class's function "nextLargerPrimeThan" to find a prime // number that is close to an appropriate size. This prime number becomes the // size of the hash table. // // // //
      • This class should not be used to generate large sets of prime // numbers - it is not designed for efficiency at this. // The algorithm checks 2, 3, and (6n +/- 1) up to the square // root of the candidate prime. //
      • The size of the prime numbers are restricted by the size of an // unsigned integer (2^31-1 on 32 bit machines). // class Primes { public: //This function takes number and returns "True" if number is prime, "False" //if it is not. static Bool isPrime(uInt number); //This function returns the closest integer larger than number from the //table of primes. If there is no entry in the table of primes which is //larger than number, a zero is returned. static uInt aLargerPrimeThan(uInt number); //This function finds the next largest prime than number, returns that //value and stores it in the table of primes. static uInt nextLargerPrimeThan(uInt number); // adds to cache //This function returns the smallest factor of number. static uInt smallestPrimeFactor(uInt number); //This function returns a block, of variable length, with each factor //indexed. For example, if number equaled 4, then the return block would //have a length of two, and have a two stored in each cell. One and zero //are special cases; this function returns a one-cell block which holds //one or zero, respectively. static Block factor(uInt number); // This function returns the number of primes stored in the primes table. // static uInt nCachedPrimes() // { return cacheTable.nelements(); } //This function returns the table of prime numbers. //static Block cachedPrimes() // { return cacheTable; } private: //This function resets the table of prime numbers to contain 31 prime //numbers to avoid consuming too much memory. static void initializeCache(); //This is the table which stores the prime numbers. static Block cacheTable; static std::mutex theirMutex; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/BasicMath/Random.cc000066400000000000000000000645531476623553700176320ustar00rootroot00000000000000//# Random.cc: Random number classes //# Copyright (C) 1992,1993,1994,1995,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN RNG::~RNG() { } Float RNG::asFloat() { // used to access floats as unsigned Int's union PrivateRNGSingleType { Float flt; uInt intgr; }; PrivateRNGSingleType result; result.flt = 1.0f; result.intgr |= (asuInt() & 0x7fffff); result.flt -= 1.0; AlwaysAssert(result.flt < 1.0f && result.flt >= 0.0f, AipsError); return result.flt; } Double RNG::asDouble() { // used to access Doubles as two unsigned integers union PrivateRNGDoubleType { Double dbl; uInt intgr[2]; }; PrivateRNGDoubleType result; result.dbl = 1.0; uInt iMsb = asuInt() & 0xfffff; uInt iLsb = asuInt(); #if defined(AIPS_LITTLE_ENDIAN) result.intgr[0] |= iLsb; result.intgr[1] |= iMsb; # else result.intgr[1] |= iLsb; result.intgr[0] |= iMsb; #endif result.dbl -= 1.0; AlwaysAssert(result.dbl < 1.0f && result.dbl >= 0.0f, AipsError); return result.dbl; } // // This is an extension of the older implementation of Algorithm M // which I previously supplied. The main difference between this // version and the old code are: // // + Andres searched high & low for good constants for // the LCG. // // + theres more bit chopping going on. // // The following contains his comments. // // agn@UNH.CS.CMU.EDU sez.. // // The generator below is based on 2 well known // methods: Linear Congruential (LCGs) and Additive // Congruential generators (ACGs). // // The LCG produces the longest possible sequence // of 32 bit random numbers, each being unique in // that sequence (it has only 32 bits of state). // It suffers from 2 problems: a) Independence // isnt great, that is the (n+1)th number is // somewhat related to the preceding one, unlike // flipping a coin where knowing the past outcomes // dont help to predict the next result. b) // Taking parts of a LCG generated number can be // quite non-random: for example, looking at only // the least significant byte gives a permuted // 8-bit counter (that has a period length of only // 256). The advantage of an LCA is that it is // perfectly uniform when run for the entire period // length (and very uniform for smaller sequences // too, if the parameters are chosen carefully). // // ACGs have extremly long period lengths and // provide good independence. Unfortunately, // uniformity isnt not too great. Furthermore, I // didnt find any theoretically analysis of ACGs // that addresses uniformity. // // The RNG given below will return numbers // generated by an LCA that are permuted under // control of a ACG. 2 permutations take place: the // 4 bytes of one LCG generated number are // subjected to one of 16 permutations selected by // 4 bits of the ACG. The permutation a such that // byte of the result may come from each byte of // the LCG number. This effectively destroys the // structure within a word. Finally, the sequence // of such numbers is permuted within a range of // 256 numbers. This greatly improves independence. // // // Algorithm M as describes in Knuths "Art of Computer Programming", // Vol 2. 1969 // is used with a linear congruential generator (to get a good uniform // distribution) that is permuted with a Fibonacci additive congruential // generator to get good independence. // // Bit, byte, and word distributions were extensively tested and pass // Chi-squared test near perfect scores (>7E8 numbers tested, Uniformity // assumption holds with probability > 0.999) // // Run-up tests for on 7E8 numbers confirm independence with // probability > 0.97. // // Plotting random points in 2d reveals no apparent structure. // // Autocorrelation on sequences of 5E5 numbers (A(i) = SUM X(n)*X(n-i), // i=1..512) // results in no obvious structure (A(i) ~ const). // // Except for speed and memory requirements, this generator outperforms // random() for all tests. (random() scored rather low on uniformity tests, // while independence test differences were less dramatic). // // AGN would like to.. // thanks to M.Mauldin, H.Walker, J.Saxe and M.Molloy for inspiration & help. // // And I would (DGC) would like to thank Donald Kunth for AGN for letting me // use his extensions in this implementation. // // Part of the table on page 28 of Knuth, vol II. This allows us // to adjust the size of the table at the expense of shorter sequences. static Int randomStateTable[][3] = { {3,7,16}, {4,9, 32}, {3,10, 32}, {1,11, 32}, {1,15,64}, {3,17,128}, {7,18,128}, {3,20,128}, {2,21, 128}, {1,22, 128}, {5,23, 128}, {3,25, 128}, {2,29, 128}, {3,31, 128}, {13,33, 256}, {2,35, 256}, {11,36, 256}, {14,39,256}, {3,41,256}, {9,49,256}, {3,52,256}, {24,55,256}, {7,57, 256}, {19,58,256}, {38,89,512}, {17,95,512}, {6,97,512}, {11,98,512}, {-1,-1,-1} }; // spatial permutation table // RANDOM_PERM_SIZE must be a power of two #define RANDOM_PERM_SIZE 64 uInt randomPermutations[RANDOM_PERM_SIZE] = { 0xffffffff, 0x00000000, 0x00000000, 0x00000000, // 3210 0x0000ffff, 0x00ff0000, 0x00000000, 0xff000000, // 2310 0xff0000ff, 0x0000ff00, 0x00000000, 0x00ff0000, // 3120 0x00ff00ff, 0x00000000, 0xff00ff00, 0x00000000, // 1230 0xffff0000, 0x000000ff, 0x00000000, 0x0000ff00, // 3201 0x00000000, 0x00ff00ff, 0x00000000, 0xff00ff00, // 2301 0xff000000, 0x00000000, 0x000000ff, 0x00ffff00, // 3102 0x00000000, 0x00000000, 0x00000000, 0xffffffff, // 2103 0xff00ff00, 0x00000000, 0x00ff00ff, 0x00000000, // 3012 0x0000ff00, 0x00000000, 0x00ff0000, 0xff0000ff, // 2013 0x00000000, 0x00000000, 0xffffffff, 0x00000000, // 1032 0x00000000, 0x0000ff00, 0xffff0000, 0x000000ff, // 1023 0x00000000, 0xffffffff, 0x00000000, 0x00000000, // 0321 0x00ffff00, 0xff000000, 0x00000000, 0x000000ff, // 0213 0x00000000, 0xff000000, 0x0000ffff, 0x00ff0000, // 0132 0x00000000, 0xff00ff00, 0x00000000, 0x00ff00ff // 0123 }; // SEED_TABLE_SIZE must be a power of 2 #define SEED_TABLE_SIZE 32 static uInt seedTable[SEED_TABLE_SIZE] = { 0xbdcc47e5, 0x54aea45d, 0xec0df859, 0xda84637b, 0xc8c6cb4f, 0x35574b01, 0x28260b7d, 0x0d07fdbf, 0x9faaeeb0, 0x613dd169, 0x5ce2d818, 0x85b9e706, 0xab2469db, 0xda02b0dc, 0x45c60d6e, 0xffe49d10, 0x7224fea3, 0xf9684fc9, 0xfc7ee074, 0x326ce92a, 0x366d13b5, 0x17aaa731, 0xeb83a675, 0x7781cb32, 0x4ec7c92d, 0x7f187521, 0x2cf346b4, 0xad13310f, 0xb89cff2b, 0x12164de1, 0xa865168d, 0x32b56cdf }; // The LCG used to scramble the ACG // // LC-parameter selection follows recommendations in // "Handbook of Mathematical Functions" by Abramowitz & Stegun 10th, edi. // // LC_A = 251^2, ~= sqrt(2^32) = 66049 // LC_C = result of a long trial & error series = 3907864577 static const uInt LC_A = 66049; static const uInt LC_C = 3907864577u; static inline uInt LCG(uInt x) { return x * LC_A + LC_C; } ACG::ACG(uInt seed, Int size) :itsInitSeed(seed), itsInitTblEntry(0), itsStatePtr(0), itsAuxStatePtr(0), itsStateSize(0), itsAuxSize(0), lcgRecurr(0), itsJ(0), itsK(0) { // Determine the size of the state table Int l; for (l = 0; randomStateTable[l][0] != -1 && randomStateTable[l][1] < size; l++) {} if (randomStateTable[l][1] == -1) { l--; } itsInitTblEntry = l; itsStateSize = randomStateTable[ itsInitTblEntry ][ 1 ]; itsAuxSize = randomStateTable[ itsInitTblEntry ][ 2 ]; // Allocate the state table & the auxillary table in a single malloc itsStatePtr = new uInt[itsStateSize + itsAuxSize]; AlwaysAssert(itsStatePtr != 0, AipsError); itsAuxStatePtr = &itsStatePtr[itsStateSize]; reset(); } void ACG::reset() { uInt u; if (itsInitSeed < SEED_TABLE_SIZE) { u = seedTable[ itsInitSeed ]; } else { u = itsInitSeed ^ seedTable[ itsInitSeed & (SEED_TABLE_SIZE-1) ]; } itsJ = randomStateTable[ itsInitTblEntry ][ 0 ] - 1; itsK = randomStateTable[ itsInitTblEntry ][ 1 ] - 1; for (Int i = 0; i < itsStateSize; i++) { itsStatePtr[i] = u = LCG(u); } for (Int i = 0; i < itsAuxSize; i++) { itsAuxStatePtr[i] = u = LCG(u); } // Get rid of compiler warning - hopefully the authors of this class knew // what they were doing itsK = static_cast(u % itsStateSize); Int tailBehind = (itsStateSize - randomStateTable[ itsInitTblEntry ][ 0 ]); itsJ = itsK - tailBehind; if (itsJ < 0) { itsJ += itsStateSize; } lcgRecurr = u; } ACG::~ACG() { delete[] itsStatePtr; // don't delete itsAuxStatePtr, it's really an alias for itsStatePtr. itsAuxStatePtr = itsStatePtr = 0; } uInt ACG::asuInt() { uInt result = itsStatePtr[itsK] + itsStatePtr[itsJ]; itsStatePtr[itsK] = result; itsJ = (itsJ <= 0) ? (itsStateSize-1) : (itsJ-1); itsK = (itsK <= 0) ? (itsStateSize-1) : (itsK-1); // Get rid of compiler warning - hopefully the authors of this class knew // what they were doing Short auxIndex = static_cast((result >> 24) & (itsAuxSize - 1)); uInt auxACG = itsAuxStatePtr[auxIndex]; itsAuxStatePtr[auxIndex] = lcgRecurr = LCG(lcgRecurr); // 3c is a magic number. We are doing four masks here, so we // do not want to run off the end of the permutation table. // This insures that we have always got four entries left. uInt* perm = &randomPermutations[result & 0x3c]; result = *(perm++) & auxACG; result |= *(perm++) & ((auxACG << 24) | ((auxACG >> 8) & 0xffffff)); result |= *(perm++) & ((auxACG << 16) | ((auxACG >> 16) & 0xffff)); result |= *(perm++) & ((auxACG << 8) | ((auxACG >> 24) & 0xff)); return result; } MLCG::MLCG(Int seed1, Int seed2): itsInitSeedOne(seed1), itsInitSeedTwo(seed2) { reset(); } MLCG::~MLCG() { } void MLCG::reset() { Int seed1 = itsInitSeedOne; Int seed2 = itsInitSeedTwo; // Most people pick stupid seed numbers that do not have enough // bits. In this case, if they pick a small seed number, we // map that to a specific seed. // if (seed1 < 0) { seed1 = (seed1 + 2147483561); seed1 = (seed1 < 0) ? -seed1 : seed1; } if (seed2 < 0) { seed2 = (seed2 + 2147483561); seed2 = (seed2 < 0) ? -seed2 : seed2; } if (seed1 > -1 && seed1 < SEED_TABLE_SIZE) { itsSeedOne = seedTable[seed1]; } else { itsSeedOne = seed1 ^ seedTable[seed1 & (SEED_TABLE_SIZE-1)]; } if (seed2 > -1 && seed2 < SEED_TABLE_SIZE) { itsSeedTwo = seedTable[seed2]; } else { itsSeedTwo = seed2 ^ seedTable[ seed2 & (SEED_TABLE_SIZE-1) ]; } itsSeedOne = (itsSeedOne % 2147483561) + 1; itsSeedTwo = (itsSeedTwo % 2147483397) + 1; } uInt MLCG::asuInt() { Int k = itsSeedOne % 53668; itsSeedOne = 40014 * (itsSeedOne-k * 53668) - k * 12211; if (itsSeedOne < 0) { itsSeedOne += 2147483563; } k = itsSeedTwo % 52774; itsSeedTwo = 40692 * (itsSeedTwo - k * 52774) - k * 3791; if (itsSeedTwo < 0) { itsSeedTwo += 2147483399; } Int z = itsSeedOne - itsSeedTwo; if (z < 1) { z += 2147483562; } return static_cast(z); } Random::~Random() { } String Random::asString(Random::Types type) { switch (type) { case BINOMIAL: return String("BINOMIAL"); case DISCRETEUNIFORM: return String("DISCRETEUNIFORM"); case ERLANG: return String("ERLANG"); case GEOMETRIC: return String("GEOMETRIC"); case HYPERGEOMETRIC: return String("HYPERGEOMETRIC"); case NORMAL: return String("NORMAL"); case LOGNORMAL: return String("LOGNORMAL"); case NEGATIVEEXPONENTIAL: return String("NEGATIVEEXPONENTIAL"); case POISSON: return String("POISSON"); case UNIFORM: return String("UNIFORM"); case WEIBULL: return String("WEIBULL"); case UNKNOWN: return String("UNKNOWN"); case NUMBER_TYPES: throw(AipsError("NUMBER_TYPES has no string equivalent")); default: throw(AipsError("Unknown Random::Types enumerator")); } } Random::Types Random::asType(const String& str) { String canonicalCase(str); canonicalCase.upcase(); Random::Types t; String s2; for (uInt i = 0; i < NUMBER_TYPES; i++) { t = static_cast(i); s2 = Random::asString(t); if (s2.matches(canonicalCase)) { return t; } } return Random::UNKNOWN; } Random* Random::construct(Random::Types type, RNG* gen) { switch (type) { case BINOMIAL: return new Binomial(gen); case DISCRETEUNIFORM: return new DiscreteUniform(gen); case ERLANG: return new Erlang(gen); case GEOMETRIC: return new Geometric(gen); case HYPERGEOMETRIC: return new HyperGeometric(gen); case NORMAL: return new Normal(gen); case LOGNORMAL: return new LogNormal(gen); case NEGATIVEEXPONENTIAL: return new NegativeExpntl(gen); case POISSON: return new Poisson(gen); case UNIFORM: return new Uniform(gen); case WEIBULL: return new Weibull(gen); case UNKNOWN: case NUMBER_TYPES: default: return 0; } } Vector Random::defaultParameters (Random::Types type) { MLCG gen; const std::unique_ptr ranPtr(construct(type, &gen)); if (!ranPtr) { return Vector(); } else { return ranPtr->parameters(); } } Binomial::~Binomial() { } Binomial::Binomial(RNG* gen, uInt n, Double p) :Random(gen), itsN(n), itsP(p) { AlwaysAssert( p >= 0.0 && p <= 1.0 && n > 0, AipsError); } Double Binomial::operator()() { return static_cast(asInt()); } uInt Binomial::asInt() { uInt result = 0; for (uInt i = 0; i < itsN; i++) { if (itsRNG->asDouble() < itsP) { result++; } } return result; } void Binomial::n(uInt newN) { AlwaysAssert(newN > 0, AipsError); itsN = newN; } void Binomial::n(Double newN) { AlwaysAssert(newN >= 0.5, AipsError); n(static_cast(newN)); } void Binomial::p(Double newP) { AlwaysAssert(newP >= 0.0 && newP <= 1.0, AipsError); itsP = newP; } void Binomial::setParameters(const Vector& pars) { AlwaysAssert(checkParameters(pars), AipsError); n(pars(0)); p(pars(1)); } Vector Binomial::parameters() const { Vector retVal(2); retVal(0) = n(); retVal(1) = p(); return retVal; } Bool Binomial::checkParameters(const Vector& pars) const { return pars.nelements() == 2 && pars(0) >= 0.5 && pars(1) >= 0.0 && pars(1) <= 1.0; } DiscreteUniform::DiscreteUniform(RNG* gen, Int low, Int high) :Random(gen), itsLow(low), itsHigh(high), itsDelta(calcDelta(itsLow, itsHigh)) { AlwaysAssert(itsLow <= itsHigh, AipsError); } DiscreteUniform::~DiscreteUniform() { } Double DiscreteUniform::operator()() { return static_cast(asInt()); } Int DiscreteUniform::asInt() { return itsLow + static_cast(std::floor(itsDelta * itsRNG->asDouble())); } void DiscreteUniform::low(Int x) { AlwaysAssert(x <= itsHigh, AipsError); itsLow = x; itsDelta = calcDelta(itsLow, itsHigh); } void DiscreteUniform::high(Int x) { AlwaysAssert(itsLow <= x, AipsError); itsHigh = x; itsDelta = calcDelta(itsLow, itsHigh); } void DiscreteUniform::range(Int low, Int high) { AlwaysAssert(low <= high, AipsError); itsLow = low; itsHigh = high; itsDelta = calcDelta(itsLow, itsHigh); } void DiscreteUniform::setParameters(const Vector& pars) { AlwaysAssert(checkParameters(pars), AipsError); range(static_cast(pars(0)), static_cast(pars(1))); } Vector DiscreteUniform::parameters() const { Vector retVal(2); retVal(0) = low(); retVal(1) = high(); return retVal; } Bool DiscreteUniform::checkParameters(const Vector& pars) const { return pars.nelements() == 2 && pars(0) <= pars(1); } Double DiscreteUniform::calcDelta(Int low, Int high) { return static_cast((high - low) + 1); } Erlang::~Erlang() { } void Erlang::setState() { AlwaysAssert(!nearAbs(itsMean, 0.0), AipsError); AlwaysAssert(itsVariance > 0, AipsError); itsK = static_cast((itsMean * itsMean ) / itsVariance + 0.5 ); itsK = (itsK > 0) ? itsK : 1; itsA = itsK / itsMean; } Double Erlang::operator()() { Double prod = 1.0; for (Int i = 0; i < itsK; i++) { prod *= itsRNG->asDouble(); } return -log(prod)/itsA; } void Erlang::setParameters(const Vector& pars) { AlwaysAssert(checkParameters(pars), AipsError); mean(pars(0)); variance(pars(1)); } Vector Erlang::parameters() const { Vector retVal(2); retVal(0) = mean(); retVal(1) = variance(); return retVal; } Bool Erlang::checkParameters(const Vector& pars) const { return pars.nelements() == 2 && !nearAbs(pars(0), 0.0) && pars(1) > 0.0; } Geometric::Geometric(RNG* gen, Double probability) :Random(gen), itsProbability(probability) { AlwaysAssert(itsProbability >= 0.0 && itsProbability < 1.0, AipsError); } Geometric::~Geometric() { } Double Geometric::operator()() { return static_cast(asInt()); } uInt Geometric::asInt() { uInt samples; for (samples = 0; itsRNG->asDouble() > itsProbability; samples++) {} return samples; } void Geometric::probability(Double x) { itsProbability = x; AlwaysAssert(itsProbability >= 0.0 && itsProbability < 1.0, AipsError); } void Geometric::setParameters(const Vector& pars) { AlwaysAssert(checkParameters(pars), AipsError); probability(pars(0)); } Vector Geometric::parameters() const { return Vector(1, probability()); } Bool Geometric::checkParameters(const Vector& pars) const { return pars.nelements() == 1 && pars(0) >= 0.0 && pars(0) < 1.0; } HyperGeometric::~HyperGeometric() { } Double HyperGeometric::operator()() { const Double d = (itsRNG->asDouble() > itsP) ? (1.0 - itsP) : itsP; return -itsMean * log(itsRNG->asDouble()) / (2.0 * d); } void HyperGeometric::setParameters(const Vector& pars) { AlwaysAssert(checkParameters(pars), AipsError); mean(pars(0)); variance(pars(1)); } Vector HyperGeometric::parameters() const { Vector retVal(2); retVal(0) = mean(); retVal(1) = variance(); return retVal; } Bool HyperGeometric::checkParameters(const Vector& pars) const { return pars.nelements() == 2 && !nearAbs(pars(0), 0.0) && pars(1) > 0.0 && square(pars(0)) <= pars(1); } void HyperGeometric::setState() { AlwaysAssert(itsVariance > 0.0, AipsError); AlwaysAssert(!near(itsMean, 0.0), AipsError); AlwaysAssert(itsMean*itsMean <= itsVariance, AipsError); const Double z = itsVariance / (itsMean * itsMean); itsP = 0.5 * (1.0 - sqrt((z - 1.0) / ( z + 1.0 ))); } Normal::Normal(RNG* gen, Double mean, Double variance) :Random(gen), itsMean(mean), itsVariance(variance), itsCached(False), itsCachedValue(0) { AlwaysAssert(itsVariance > 0.0, AipsError); itsStdDev = sqrt(itsVariance); } Normal::~Normal() { } // See Simulation, Modelling & Analysis by Law & Kelton, pp259 // This is the ``polar'' method. // We actually generate two IID normal distribution variables. // We cache the one & return the other. Double Normal::operator()() { if (itsCached) { itsCached = False; return itsCachedValue * itsStdDev + itsMean; } for(;;) { const Double u1 = itsRNG->asDouble(); const Double u2 = itsRNG->asDouble(); const Double v1 = 2 * u1 - 1; const Double v2 = 2 * u2 - 1; const Double w = (v1 * v1) + (v2 * v2); if (w <= 1) { const Double y = sqrt( (-2 * log(w)) / w); const Double x1 = v1 * y; itsCachedValue = v2 * y; itsCached = True; return x1 * itsStdDev + itsMean; } } } void Normal::mean(Double x) { itsMean = x; } void Normal::variance(Double x) { itsVariance = x; AlwaysAssert(itsVariance > 0.0, AipsError); itsStdDev = sqrt(itsVariance); } void Normal::setParameters(const Vector& pars) { AlwaysAssert(checkParameters(pars), AipsError); mean(pars(0)); variance(pars(1)); } Vector Normal::parameters() const { Vector retVal(2); retVal(0) = mean(); retVal(1) = variance(); return retVal; } Bool Normal::checkParameters(const Vector& pars) const { return pars.nelements() == 2 && pars(1) > 0.0; } LogNormal::LogNormal(RNG* gen, Double mean, Double variance) :Normal(gen), itsLogMean(mean), itsLogVar(variance) { setState(); } LogNormal::~LogNormal() { } // See Simulation, Modelling & Analysis by Law & Kelton, pp260 Double LogNormal::operator()() { return std::pow(M_E, this->Normal::operator()() ); } void LogNormal::mean(Double x) { itsLogMean = x; setState(); } void LogNormal::variance(Double x) { itsLogVar = x; setState(); } void LogNormal::setState() { const Double m2 = itsLogMean * itsLogMean; AlwaysAssert(!near(m2, 0.0), AipsError); this->Normal::mean(log(m2 / sqrt(itsLogVar + m2) )); AlwaysAssert(!near(m2+itsLogVar, 0.0), AipsError); this->Normal::variance(log((itsLogVar + m2)/m2 )); } void LogNormal::setParameters(const Vector& pars) { AlwaysAssert(checkParameters(pars), AipsError); mean(pars(0)); variance(pars(1)); } Vector LogNormal::parameters() const { Vector retVal(2); retVal(0) = mean(); retVal(1) = variance(); return retVal; } Bool LogNormal::checkParameters(const Vector& pars) const { return pars.nelements() == 2 && !nearAbs(pars(0), 0.0) && pars(1) > 0.0; } NegativeExpntl::NegativeExpntl(RNG* gen, Double mean) :Random(gen) { itsMean = mean; } NegativeExpntl::~NegativeExpntl() { } Double NegativeExpntl::operator()() { return -itsMean * log(itsRNG->asDouble()); } void NegativeExpntl::mean(Double x) { itsMean = x; } void NegativeExpntl::setParameters(const Vector& pars) { AlwaysAssert(checkParameters(pars), AipsError); mean(pars(0)); } Vector NegativeExpntl::parameters() const { return Vector(1, mean()); } Bool NegativeExpntl::checkParameters(const Vector& pars) const { return pars.nelements() == 1; } Poisson::Poisson(RNG* gen, Double mean) :Random(gen) { AlwaysAssert(mean >= 0.0, AipsError); itsMean = mean; } Poisson::~Poisson() { } Double Poisson::operator()() { return static_cast(asInt()); } uInt Poisson::asInt() { const Double bound = exp(-1.0 * itsMean); uInt count = 0; for (Double product = 1.0; product >= bound; product *= itsRNG->asDouble()) { count++; } return count - 1; } void Poisson::mean(Double x) { AlwaysAssert(x >= 0.0, AipsError); itsMean = x; } void Poisson::setParameters(const Vector& pars) { AlwaysAssert(checkParameters(pars), AipsError); mean(pars(0)); } Vector Poisson::parameters() const { return Vector(1, mean()); } Bool Poisson::checkParameters(const Vector& pars) const { return pars.nelements() == 1 && pars(0) >= 0.0; } Uniform::Uniform(RNG* gen, Double low, Double high) :Random(gen), itsLow(low), itsHigh(high), itsDelta(calcDelta(itsLow, itsHigh)) { AlwaysAssert(itsLow < itsHigh, AipsError); } Uniform::~Uniform() { } Double Uniform::operator()() { return itsLow + itsDelta * itsRNG->asDouble(); } void Uniform::low(Double x) { AlwaysAssert(x < itsHigh, AipsError); itsLow = x; itsDelta = calcDelta(itsLow, itsHigh); } void Uniform::high(Double x) { AlwaysAssert(itsLow < x, AipsError); itsHigh = x; itsDelta = calcDelta(itsLow, itsHigh); } void Uniform::range(Double low, Double high) { AlwaysAssert(low < high, AipsError); itsHigh = high; itsLow = low; itsDelta = calcDelta(itsLow, itsHigh); } void Uniform::setParameters(const Vector& pars) { AlwaysAssert(checkParameters(pars), AipsError); range(pars(0), pars(1)); } Vector Uniform::parameters() const { Vector retVal(2); retVal(0) = low(); retVal(1) = high(); return retVal; } Bool Uniform::checkParameters(const Vector& pars) const { return pars.nelements() == 2 && pars(0) < pars(1); } Double Uniform::calcDelta(Double low, Double high) { return static_cast(high - low); } Weibull::~Weibull() { } // See Simulation, Modelling & Analysis by Law & Kelton, pp259 // This is the ``polar'' method. Weibull::Weibull(RNG* gen, Double alpha, Double beta) :Random(gen), itsAlpha(alpha), itsBeta(beta), itsInvAlpha(0) { setState(); } Double Weibull::operator()() { return std::pow(itsBeta * ( - log(1.0 - itsRNG->asDouble()) ), itsInvAlpha); } void Weibull::alpha(Double x) { itsAlpha = x; setState(); } void Weibull::beta(Double x) { itsBeta = x; } void Weibull::setParameters(const Vector& pars) { AlwaysAssert(checkParameters(pars), AipsError); alpha(pars(0)); beta(pars(1)); } Vector Weibull::parameters() const { Vector retVal(2); retVal(0) = alpha(); retVal(1) = beta(); return retVal; } Bool Weibull::checkParameters(const Vector& pars) const { return pars.nelements() == 2 && !nearAbs(pars(0), 0.0) && pars(1) > 0.0; } void Weibull::setState() { AlwaysAssert(!near(itsAlpha, 0.0), AipsError); itsInvAlpha = 1.0 / itsAlpha; } // Local Variables: // compile-command: "gmake XLIBLIST=0 Random" // End: } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/BasicMath/Random.h000066400000000000000000001266501476623553700174710ustar00rootroot00000000000000//# Random.h: Random number classes //# Copyright (C) 1992,1993,1994,1995,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_RANDOM_H #define CASA_RANDOM_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class String; // Base class for random number generators // // // // // // //
      • A knowledge of C++, in particular inheritance //
      • College level mathematics // // // // RNG stands for "Random Number Generator" // // // //

        General Structure of the Classes

        // // The two base classes RNG and // Random are used together to generate a variety // of random number distributions. A distinction must be made between // random number generators, implemented by class derived from // RNG, and random number distributions. A random number // generator produces a series of randomly ordered bits. These bits can be // used directly, or cast to another representation, such as a floating point // value. A random number generator should produce a uniform // distribution. A random number distribution, on the other hand, uses the // randomly generated bits of a generator to produce numbers from a // distribution with specific properties. Each instance of Random // uses an instance of class RNG to provide the raw, uniform // distribution used to produce the specific distribution. Several instances // of Random classes can share the same instance of RNG, // or each instance can use its own copy. //

        RNG

        // // Random distributions are constructed from classes derived from // RNG, the actual random number generators. The RNG // class contains no data; it only serves to define the interface to random // number generators. The RNG::asuInt member returns a 32-bit // unsigned integer of random bits. Applications that require a number of // random bits can use this directly. More often, these random bits are // transformed to a uniformly distributed floating point number using either // asFloat or asDouble. These functions return differing // precisions and the asDouble function will use two different // random 32-bit integers to get a legal double, while // asFloat will use a single integer. These members are used by // classes derived fro the Random base class to implement a variety // of random number distributions. // // Currently, the following subclasses are provided: //
          //
        • MLCG: // Multiplicative Linear Congruential Generator. // A reasonable generator for most purposes. //
        • ACG: Additive Number Generator. // A high quality generator that uses more memory and computation time. //
        // // This class assumes that IEEE floating point // representation is used for the floating point numbers and that the integer // and unsigned integer type is exactly 32 bits long. // //
        // // // // // // Random numbers are used everywhere, particularly in simulations. // // // //
      • AipsError: If a programming error or unexpected numeric size is // detected. Should not occur in normal usage. // // // //
      • Nothing I hope! // class RNG { public: // A virtual destructor is needed to ensure that the destructor of derived // classes gets used. virtual ~RNG(); // Resets the random number generator. After calling this function the random // numbers generated will be the same as if the object had just been // constructed. virtual void reset() = 0; // Return the 32-random bits as an unsigned integer virtual uInt asuInt() = 0; // Return random bits converted to either a Float or a Double. The returned // value x is in the range 1.0 > x >= 0.0 // Float asFloat(); Double asDouble(); // }; // Additive number generator // // // // // // //
      • A knowledge of C++, in particular inheritance //
      • College level mathematics // // // // ACG stands for "Additive Congruential Generator" // // // // This class implements the additive number generator as presented in Volume // II of The Art of Computer Programming by Knuth. I have coded the algorithm // and have added the extensions by Andres Nowatzyk of CMU to randomize the // result of algorithm M a bit by using an LCG & a spatial permutation table. // // The version presented uses the same constants for the LCG that Andres uses // (chosen by trial & error). The spatial permutation table is the same size // (it is based on word size). This is for 32-bit words. // // The auxillary table used by the LCG table varies in size, and is // chosen to be the the smallest power of two which is larger than twice the // size of the state table. // // Class ACG is a variant of a Linear Congruential Generator // (Algorithm M) described in Knuth, "Art of Computer Programming, Vol III". // This result is permuted with a Fibonacci Additive Congruential Generator to // get good independence between samples. This is a very high quality random // number generator, although it requires a fair amount of memory for each // instance of the generator. // // The constructor takes two parameters: the seed and the size. The seed can // be any number. The performance of the generator depends on having a // distribution of bits through the seed. If you choose a number in the range // of 0 to 31, a seed with more bits is chosen. Other values are // deterministically modified to give a better distribution of bits. This // provides a good random number generator while still allowing a sequence to // be repeated given the same initial seed. // // The size parameter determines the size of two tables used in the // generator. The first table is used in the Additive Generator; see the // algorithm in Knuth for more information. In general, this table contains // size integers. The default value, used in the algorithm in Knuth, // gives a table of 55 integers (220 bytes). The table size affects the period // of the generators; smaller values give shorter periods and larger tables // give longer periods. The smallest table size is 7 integers, and the longest // is 98. The size parameter also determines the size of the table // used for the Linear Congruential Generator. This value is chosen implicitly // based on the size of the Additive Congruential Generator table. It is two // powers of two larger than the power of two that is larger than // size. For example, if size is 7, the ACG table // contains 7 integers and the LCG table contains 128 integers. Thus, the // default size (55) requires 55 + 256 integers, or 1244 bytes. The largest // table requires 2440 bytes and the smallest table requires 100 bytes. // Applications that require a large number of generators or applications that // are not so fussy about the quality of the generator may elect to use the // MLCG generator. // // This class assumes that the integer and unsigned integer // type is exactly 32 bits long. // // // // // // // //
      • AipsError: If a programming error or unexpected numeric size is // detected. Should not occur in normal usage. // // // //
      • Nothing I hope! // class ACG : public RNG { public: // The constructor allows you to specify seeds. The seed should be a big // random number and size must be between 7 and 98. See the synopsis for more // details. explicit ACG(uInt seed = 0, Int size = 55); // The destructor cleans up memory allocated by this class virtual ~ACG(); // Resets the random number generator. After calling this function the random // numbers generated will be the same as if the object had just been // constructed. virtual void reset(); // Return the 32-random bits as an unsigned integer virtual uInt asuInt(); private: uInt itsInitSeed; //# used to reset the generator Int itsInitTblEntry; uInt* itsStatePtr; uInt* itsAuxStatePtr; Short itsStateSize; Short itsAuxSize; uInt lcgRecurr; Short itsJ; Short itsK; }; // Multiplicative linear congruential generator // // // // // //
      • A knowledge of C++, in particular inheritance //
      • College level mathematics // // // // MLCG stands for "Multiplicative Linear Congruential Generator" // // // // The MLCG class implements a Multiplicative Linear // Congruential Generator. In particular, it is an implementation of the // double MLCG described in Efficient and Portable Combined Random Number // Generators by Pierre L'Ecuyer, appearing in Communications of the // ACM, Vol. 31. No. 6. This generator has a fairly long period, and has // been statistically analyzed to show that it gives good inter-sample // independence. // // The constructor has two parameters, both of which are seeds for the // generator. As in the ACG generator, both seeds are modified to // give a "better" distribution of seed digits. Thus, you can safely use values // such as 0 or 1 for the seeds. The MLCG // generator used much less state than the ACG generator; only two // integers (8 bytes) are needed for each generator. // This class assumes that the integer and unsigned integer // type is exactly 32 bits long. // // // // // // //
      • AipsError: If a programming error or unexpected numeric size is // detected. Should not occur in normal usage. // // // //
      • Nothing I hope! // class MLCG : public RNG { public: // The constructor allows you to specify seeds. explicit MLCG(Int seed1 = 0, Int seed2 = 1); // The destructor is trivial virtual ~MLCG(); // Return the 32-random bits as an unsigned integer virtual uInt asuInt(); // Resets the random number generator. After calling this function the random // numbers generated will be the same as if the object had just been // constructed. virtual void reset(); // Functions that allow the user to retrieve or change the seed integers. The // seeds returned are not the user supplied values but the values obtained // after some deterministic modification to produce a more uniform bit // distribution. // Int seed1() const; void seed1(Int s); Int seed2() const; void seed2(Int s); void reseed(Int s1, Int s2); // private: Int itsInitSeedOne; Int itsInitSeedTwo; Int itsSeedOne; Int itsSeedTwo; }; inline Int MLCG::seed1() const { return itsSeedOne; } inline void MLCG::seed1(Int s) { itsInitSeedOne = s; reset(); } inline Int MLCG::seed2() const { return itsSeedTwo; } inline void MLCG::seed2(Int s) { itsInitSeedTwo = s; reset(); } inline void MLCG::reseed(Int s1, Int s2) { itsInitSeedOne = s1; itsInitSeedTwo = s2; reset(); } // Base class for random number distributions // // // // // //
      • A knowledge of C++, in particular inheritance //
      • College level mathematics // // // // A random number generator may be declared by first constructing a // RNG object and then a Random. For example, // // ACG gen(10, 20); // NegativeExpntl rnd (1.0, &gen); // // declares an additive congruential generator with seed 10 and table size 20, // that is used to generate exponentially distributed values with mean of 1.0. // // The virtual member Random::operator() is the common way of // extracting a random number from a particular distribution. The base class, // Random does not implement operator(). This is // performed by each of the derived classes. Thus, given the above declaration // of rnd, new random values may be obtained via, for example, // Double nextExpRand = rnd(); // // Currently, the following subclasses are provided: // //
          //
        • Binomial //
        • Erlang //
        • Geometric //
        • HyperGeometric //
        • NegativeExpntl //
        • Normal //
        • LogNormal //
        • Poisson //
        • DiscreteUniform //
        • Uniform //
        • Weibull //
        //
        // // // // // //
      • No exceptions are thrown directly from this class. // // // //
      • Nothing I hope! // class Random { public: // This enumerator lists all the predefined random number distributions. enum Types { // 2 parameters. The binomial distribution models successfully drawing // items from a pool. Specify n and p. n is the number of items in the // pool, and p, is the probability of each item being successfully drawn. // It is required that n > 0 and 0 <= p <= 1 BINOMIAL, // 2 parameters. Model a uniform random variable over the closed // interval. Specify the values low and high. The low parameter is the // lowest possible return value and the high parameter is the highest. It // is required that low < high. DISCRETEUNIFORM, // 2 parameters, mean and variance. It is required that the mean is // non-zero and the variance is positive. ERLANG, // 1 parameters, the mean. It is required that 0 <= probability < 1 GEOMETRIC, // 2 parameters, mean and variance. It is required that the variance is // positive and that the mean is non-zero and not bigger than the // square-root of the variance. HYPERGEOMETRIC, // 2 parameters, the mean and variance. It is required that the variance is // positive. NORMAL, // 2 parameters, mean and variance. It is required that the supplied // variance is positive and that the mean is non-zero LOGNORMAL, // 1 parameter, the mean. NEGATIVEEXPONENTIAL, // 1 parameter, the mean. It is required that the mean is non-negative POISSON, // 2 parameters, low and high. Model a uniform random variable over the // closed interval. The low parameter is the lowest possible return value // and the high parameter can never be returned. It is required that low < // high. UNIFORM, // 2 parameters, alpha and beta. It is required that the alpha parameter is // not zero. WEIBULL, // An non-predefined random number distribution UNKNOWN, // Number of distributions NUMBER_TYPES}; // A virtual destructor is needed to ensure that the destructor of derived // classes gets used. Not that this destructor does NOT delete the pointer to // the RNG object virtual ~Random(); // This function returns a random number from the appropriate distribution. virtual Double operator()() = 0; // Functions that allow you to access and change the class that generates the // random bits. // RNG* generator(); void generator(RNG* p); // // Convert the enumerator to a lower-case string. static String asString(Random::Types type); // Convert the string to enumerator. The parsing of the string is case // insensitive. Returns the Random::UNKNOWN value if the string does not // cotrtrespond to any of the enumerators. static Random::Types asType(const String& str); // Convert the Random::Type enumerator to a specific object (derived from // Random but upcast to a Random object). Returns a null pointer if the // object could not be constructed. This will occur is the enumerator is // UNKNOWN or NUMBER_TYPES or there is insufficient memory. The caller of // this function is responsible for deleting the pointer. static Random* construct(Random::Types type, RNG* gen); // These function allow you to manipulate the parameters (mean variance etc.) // of random number distribution. The parameters() function returns the // current value, the setParameters function allows you to change the // parameters and the checkParameters function will return False if the // supplied parameters are not appropriate for the distribution. // virtual void setParameters(const Vector& parms) = 0; virtual Vector parameters() const = 0; virtual Bool checkParameters(const Vector& parms) const = 0; // // returns the default parameters for the specified distribution. Returns an // empty Vector if a non-predifined distribution is used. static Vector defaultParameters (Random::Types type); protected: //# This class contains pure virtual functions hence the constructor can only //# sensibly be used by derived classes. Random(RNG* generator); //# The RNG class provides the random bits. RNG* itsRNG; }; inline Random::Random(RNG* gen) { itsRNG = gen; } inline RNG* Random::generator() { return itsRNG; } inline void Random::generator(RNG* p) { itsRNG = p; } // Binomial distribution // // The binomial distribution models successfully drawing items from a pool. // n is the number of items in the pool, and p, is the // probability of each item being successfully drawn. The // operator() functions returns an integral value indicating the // number of items actually drawn from the pool. It is possible to get this // same value as an integer using the asInt function. // It is assumed that n > 0 and 0 <= p <= 1 an AipsError // exception thrown if it is not true. The remaining members allow you to read // and set the parameters. // // // // // //
      • AipsError: if bad values for the arguments are given, as specified // above. // // // //
      • Nothing I hope! // class Binomial: public Random { public: // Construct a random number generator for a binomial distribution. The first // argument is a class that produces random bits. This pointer is NOT taken // over by this class and the user is responsible for deleting it. The second // and third arguments are the parameters are the Binomial distribution as // described in the synopsis. Binomial(RNG* gen, uInt n=1, Double p=0.5); // The destructor is trivial virtual ~Binomial(); // Returns a value from the Binomial distribution. The returned value is a // non-negative integer and using the asInt function bypasses the conversion // to a floating point number. // virtual Double operator()(); uInt asInt(); // // Functions that allow you to query and change the parameters of the // binomial distribution. // uInt n() const; void n(uInt newN); void n(Double newN); Double p() const; void p(Double newP); // // These function allow you to manipulate the parameters (n & p) described // above through the base class. The Vectors must always be of length two. // virtual void setParameters(const Vector& parms); virtual Vector parameters() const; virtual Bool checkParameters(const Vector& parms) const; // private: uInt itsN; Double itsP; }; inline uInt Binomial::n() const { return itsN; } inline Double Binomial::p() const { return itsP; } // Discrete uniform distribution // // The DiscreteUniform class implements a quantized uniform random // variable over the closed interval ranging from [low..high]. The // low parameter is the lowest possible return value and the // high parameter is the highest. The operator() // functions returns a value from this distribution. It is possible to get this // same value as an integer using the asInt function. // It is assumed that low limit is less than the high limit and an AipsError // exception thrown if this is not true. The remaining members allow you to // read and set the parameters. // // // // // //
      • AipsError: if bad values for the arguments are given, as specified // above. // // // //
      • Nothing I hope! // class DiscreteUniform: public Random { public: // Construct a random number generator for a discrete uniform // distribution. The first argument is a class that produces random // bits. This pointer is NOT taken over by this class and the user is // responsible for deleting it. The second and third arguments define the // range of possible return values for this distribution as described in the // synopsis. DiscreteUniform(RNG* gen, Int low=-1, Int high=1); // The destructor is trivial virtual ~DiscreteUniform(); // Returns a value from the discrete uniform distribution. The returned // value is a integer and using the asInt function bypasses the conversion to // a floating point number. // virtual Double operator()(); Int asInt(); // // Functions that allow you to query and change the parameters of the // discrete uniform distribution. // Int low() const; void low(Int x); Int high() const; void high(Int x); void range(Int low, Int high); // // These function allow you to manipulate the parameters (low & high) // described above through the base class. The Vectors must always be of // length two. // virtual void setParameters(const Vector& parms); virtual Vector parameters() const; virtual Bool checkParameters(const Vector& parms) const; // private: static Double calcDelta(Int low, Int high); Int itsLow; Int itsHigh; Double itsDelta; }; inline Int DiscreteUniform::low() const { return itsLow; } inline Int DiscreteUniform::high() const { return itsHigh; } // Erlang distribution // // The Erlang class implements an Erlang distribution with mean // mean and variance variance. // It is assumed that the mean is non-zero and the variance is positive an // AipsError exception thrown if this is not true. The remaining members allow // you to read and set the parameters. // // // // // //
      • AipsError: if bad values for the arguments are given, as specified // above. // // // //
      • Nothing I hope! // class Erlang: public Random { public: // Construct a random number generator for an Erlang distribution. The first // argument is a class that produces random bits. This pointer is NOT taken // over by this class and the user is responsible for deleting it. The second // and third arguments define the parameters for this distribution as // described in the synopsis. Erlang(RNG* gen, Double mean=1.0, Double variance=1.0); // The destructor is trivial virtual ~Erlang(); // Returns a value from the Erlang distribution. virtual Double operator()(); // Functions that allow you to query and change the parameters of the // discrete uniform distribution. // Double mean() const; void mean(Double x); Double variance() const; void variance(Double x); // // These function allow you to manipulate the parameters (mean & variance) // described above through the base class. The Vectors must always be of // length two. // virtual void setParameters(const Vector& parms); virtual Vector parameters() const; virtual Bool checkParameters(const Vector& parms) const; // private: void setState(); Double itsMean; Double itsVariance; Int itsK; Double itsA; }; inline Erlang::Erlang(RNG* gen, Double mean, Double variance) :Random(gen), itsMean(mean), itsVariance(variance) { setState(); } inline Double Erlang::mean() const { return itsMean; } inline void Erlang::mean(Double x) { itsMean = x; setState(); } inline Double Erlang::variance() const { return itsVariance; } inline void Erlang::variance(Double x) { itsVariance = x; setState(); } // Discrete geometric distribution // // The Geometric class implements a discrete geometric distribution. // The probability is the only parameter. The operator() // functions returns an non-negative integral value indicating the number of // uniform random samples actually drawn before one is obtained that is larger // than the given probability. To get this same value as an integer use the // asInt function. // // It is assumed that the probability is between zero and one // (0 <= probability < 1) and and AipsError exception thrown if this // is not true. The remaining function allow you to read and set the // parameters. // // // // // //
      • AipsError: if bad values for the arguments are given, as specified // above. // // // //
      • Nothing I hope! // class Geometric: public Random { public: // Construct a random number generator for a geometric uniform // distribution. The first argument is a class that produces random // bits. This pointer is NOT taken over by this class and the user is // responsible for deleting it. The second argument defines the range of // possible return values for this distribution as described in the synopsis. Geometric(RNG* gen, Double probability=0.5); // The destructor is trivial virtual ~Geometric(); // Returns a value from the geometric uniform distribution. The returned // value is a non-negative integer and using the asInt function bypasses the // conversion to a floating point number. // virtual Double operator()(); uInt asInt(); // // Functions that allow you to query and change the parameters of the // geometric uniform distribution. // Double probability() const; void probability(Double x); // // These function allow you to manipulate the parameter (probability) // described above through the base class. The Vectors must always be of // length one. // virtual void setParameters(const Vector& parms); virtual Vector parameters() const; virtual Bool checkParameters(const Vector& parms) const; // private: Double itsProbability; }; inline Double Geometric::probability() const { return itsProbability; } // Hypergeometric distribution // // The HyperGeometric class implements the hypergeometric // distribution. The mean and variance are the // parameters of the distribution. The operator() functions returns // a value from this distribution // It is assumed the variance is positive and that the mean is non-zero and not // bigger than the square-root of the variance. An AipsError exception is // thrown if this is not true. The remaining members allow you to read and set // the parameters. // // // // // //
      • AipsError: if bad values for the arguments are given, as specified // above. // // // //
      • Nothing I hope! // class HyperGeometric: public Random { public: // Construct a random number generator for an hypergeometric // distribution. The first argument is a class that produces random // bits. This pointer is NOT taken over by this class and the user is // responsible for deleting it. The second and third arguments define the // parameters for this distribution as described in the synopsis. HyperGeometric(RNG* gen, Double mean=0.5, Double variance=1.0); // The destructor is trivial virtual ~HyperGeometric(); // Returns a value from the hypergeometric distribution. virtual Double operator()(); // Functions that allow you to query and change the parameters of the // hypergeometric distribution. // Double mean() const; void mean(Double x); Double variance() const; void variance(Double x); // // These function allow you to manipulate the parameters (mean & variance) // described above through the base class. The Vectors must always be of // length two. // virtual void setParameters(const Vector& parms); virtual Vector parameters() const; virtual Bool checkParameters(const Vector& parms) const; // private: void setState(); Double itsMean; Double itsVariance; Double itsP; }; inline HyperGeometric::HyperGeometric(RNG* gen, Double mean, Double variance) :Random(gen), itsMean(mean), itsVariance(variance) { setState(); } inline Double HyperGeometric::mean() const { return itsMean; } inline void HyperGeometric::mean(Double x) { itsMean = x; setState(); } inline Double HyperGeometric::variance() const { return itsVariance; } inline void HyperGeometric::variance(Double x) { itsVariance = x; setState(); } // Normal or Gaussian distribution // // The Normal class implements the normal or Gaussian distribution. // The mean and variance are the parameters of the // distribution. The operator() functions returns a value from this // distribution // It is assumed that the supplied variance is positive and an AipsError // exception is thrown if this is not true. The remaining members allow you to // read and set the parameters. The LogNormal class is derived from // this one. // // // // // //
      • AipsError: if bad values for the arguments are given, as specified // above. // // // //
      • Nothing I hope! // class Normal: public Random { public: // Construct a random number generator for a normal distribution. The first // argument is a class that produces random bits. This pointer is NOT taken // over by this class and the user is responsible for deleting it. The second // and third arguments define the parameters for this distribution as // described in the synopsis. Normal(RNG* gen, Double mean=0.0, Double variance=1.0); // The destructor is trivial virtual ~Normal(); // Returns a value from the normal distribution. virtual Double operator()(); // Functions that allow you to query and change the parameters of the // normal distribution. // virtual Double mean() const; virtual void mean(Double x); virtual Double variance() const; virtual void variance(Double x); // // These function allow you to manipulate the parameters (mean & variance) // described above through the base class. The Vectors must always be of // length two. // virtual void setParameters(const Vector& parms); virtual Vector parameters() const; virtual Bool checkParameters(const Vector& parms) const; // private: Double itsMean; Double itsVariance; Double itsStdDev; Bool itsCached; Double itsCachedValue; }; inline Double Normal::mean() const { return itsMean; } inline Double Normal::variance() const { return itsVariance; } // Logarithmic normal distribution // // The LogNormal class implements the logaraithmic normal // distribution. The mean and variance are the // parameters of the distribution. The operator() functions returns // a value from this distribution // It is assumed that the supplied variance is positive and an AipsError // exception is thrown if this is not true. The remaining members allow you to // read and set the parameters. // // // // // //
      • AipsError: if bad values for the arguments are given, as specified // above. // // // //
      • Nothing I hope! // class LogNormal: public Normal { public: // Construct a random number generator for a log-normal distribution. The // first argument is a class that produces random bits. This pointer is NOT // taken over by this class and the user is responsible for deleting it. The // second and third arguments define the parameters for this distribution as // described in the synopsis. LogNormal(RNG* gen, Double mean=1.0, Double variance=1.0); // The destructor is trivial virtual ~LogNormal(); // Returns a value from the log-normal distribution. virtual Double operator()(); // Functions that allow you to query and change the parameters of the // log-normal distribution. // virtual Double mean() const; virtual void mean(Double x); virtual Double variance() const; virtual void variance(Double x); // // These function allow you to manipulate the parameters (mean & variance) // described above through the base class. The Vectors must always be of // length two. // virtual void setParameters(const Vector& parms); virtual Vector parameters() const; virtual Bool checkParameters(const Vector& parms) const; // private: void setState(); Double itsLogMean; Double itsLogVar; }; inline Double LogNormal::mean() const { return itsLogMean; } inline Double LogNormal::variance() const { return itsLogVar; } // Negative exponential distribution // // The NegativeExpntl class implements a negative exponential // distribution. The mean parameter, is the only parameter of this // distribution. The operator() functions returns a value from this // distribution. The remaining members allow you to inspect and change the // mean. // // // // // //
      • No exceptions are thrown by this class. // // // //
      • Nothing I hope! // class NegativeExpntl: public Random { public: // Construct a random number generator for a negative exponential // distribution. The first argument is a class that produces random // bits. This pointer is NOT taken over by this class and the user is // responsible for deleting it. The second argument defines the parameters // for this distribution as described in the synopsis. NegativeExpntl(RNG* gen, Double mean=1.0); // The destructor is trivial virtual ~NegativeExpntl(); // Returns a value from the negative exponential distribution. virtual Double operator()(); // Functions that allow you to query and change the parameters of the // negative exponential distribution. // Double mean() const; void mean(Double x); // // These function allow you to manipulate the parameters (mean) // described above through the base class. The Vectors must always be of // length one. // virtual void setParameters(const Vector& parms); virtual Vector parameters() const; virtual Bool checkParameters(const Vector& parms) const; // private: Double itsMean; }; inline Double NegativeExpntl::mean() const { return itsMean; } // Poisson distribution // // The Poisson class implements a Poisson distribution. The // mean parameter, is the only parameter of this distribution. The // operator() functions returns a value from this distribution. The // remaining members allow you to inspect and change the mean. // It is assumed that the supplied mean is non-negative and an AipsError // exception is thrown if this is not true. The remaining members allow you to // read and set the parameters. // // // // // //
      • No exceptions are thrown by this class. // // // //
      • Nothing I hope! // class Poisson: public Random { public: // Construct a random number generator for a Poisson distribution. The first // argument is a class that produces random bits. This pointer is NOT taken // over by this class and the user is responsible for deleting it. The second // argument defines the parameters for this distribution as described in the // synopsis. Poisson(RNG* gen, Double mean=0.0); // The destructor is trivial virtual ~Poisson(); // Returns a value from the Poisson distribution. The returned value is a // non-negative integer and using the asInt function bypasses the conversion // to a floating point number. // virtual Double operator()(); uInt asInt(); // // Functions that allow you to query and change the parameters of the // Poisson distribution. // Double mean() const; void mean(Double x); // // These function allow you to manipulate the parameters (mean) // described above through the base class. The Vectors must always be of // length one. // virtual void setParameters(const Vector& parms); virtual Vector parameters() const; virtual Bool checkParameters(const Vector& parms) const; // private: Double itsMean; }; inline Double Poisson::mean() const { return itsMean; } // Uniform distribution // // The Uniform class implements a uniform random variable over the // copen interval ranging from [low..high). The low // parameter is the lowest possible return value and the high // parameter can never be returned. The operator() functions // returns a value from this distribution. // It is assumed that low limit is less than the high limit and an AipsError // exception is thrown if this is not true. The remaining members allow you to // read and set the parameters. // // // // // //
      • AipsError: if bad values for the arguments are given, as specified // above. // // // //
      • Nothing I hope! // class Uniform: public Random { public: // Construct a random number generator for a uniform distribution. The first // argument is a class that produces random bits. This pointer is NOT taken // over by this class and the user is responsible for deleting it. The // remaining arguments define the parameters for this distribution as // described in the synopsis. Uniform(RNG* gen, Double low=-1.0, Double high=1.0); // The destructor is trivial virtual ~Uniform(); // Returns a value from the uniform distribution. virtual Double operator()(); // Functions that allow you to query and change the parameters of the // uniform distribution. // Double low() const; void low(Double x); Double high() const; void high(Double x); void range(Double low, Double high); // // These function allow you to manipulate the parameters (low & high) // described above through the base class. The Vectors must always be of // length two. // virtual void setParameters(const Vector& parms); virtual Vector parameters() const; virtual Bool checkParameters(const Vector& parms) const; // private: static Double calcDelta(Double low, Double high); Double itsLow; Double itsHigh; Double itsDelta; }; inline Double Uniform::low() const { return itsLow; } inline Double Uniform::high() const { return itsHigh; } // Weibull distribution // // The Weibull class implements a weibull distribution with // parameters alpha and beta. The first parameter to the // class constructor is alpha, and the second parameter is // beta. It is assumed that the alpha parameter is not zero and an // AipsError exception is thrown if this is not true. The remaining members // allow you to read and set the parameters. // // // // // //
      • AipsError: if bad values for the arguments are given, as specified // above. // // // //
      • Nothing I hope! // class Weibull: public Random { public: // Construct a random number generator for a uniform distribution. The first // argument is a class that produces random bits. This pointer is NOT taken // over by this class and the user is responsible for deleting it. The // remaining arguments define the parameters for this distribution as // described in the synopsis. Weibull(RNG* gen, Double alpha=1.0, Double beta=1.0); // The destructor is trivial virtual ~Weibull(); // Returns a value from the Weiball distribution. virtual Double operator()(); // Functions that allow you to query and change the parameters of the // Weiball distribution. // Double alpha() const; void alpha(Double x); Double beta() const; void beta(Double x); // // These function allow you to manipulate the parameters (alpha & beta) // described above through the base class. The Vectors must always be of // length two. // virtual void setParameters(const Vector& parms); virtual Vector parameters() const; virtual Bool checkParameters(const Vector& parms) const; // private: void setState(); Double itsAlpha; Double itsBeta; Double itsInvAlpha; }; inline Double Weibull::alpha() const { return itsAlpha; } inline Double Weibull::beta() const { return itsBeta; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/BasicMath/StdLogical.h000066400000000000000000000045711476623553700202730ustar00rootroot00000000000000//# StdLogical.h: Logical Operations on STL-style containers //# Copyright (C) 2012 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_STDLOGICAL_H #define CASA_STDLOGICAL_H #include #include namespace casacore { // Arbitrary compare operation on two STL-style containers. // It returns True if containers have equal size and // all elements compare True. template bool compareAll (const C1& l, const C2& r, CompareOperator op) { if (l.size() != r.size()) return false; return compareAll (l.begin(), l.end(), r.begin(), op); } // Test if all elements of the containers are relatively near each other. template bool allNear (const C1& l, const C2& r, U tolerance) { return compareAll (l, r, Near(tolerance)); } // Test if all elements of the containers are absolutely near each other. template bool allNearAbs (const C1& l, const C2& r, U tolerance) { return compareAll (l, r, NearAbs(tolerance)); } } //# end namespace casacore //# #ifndef CASACORE_NO_AUTO_TEMPLATES //# #include //# #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/casa/BasicMath/test/000077500000000000000000000000001476623553700170455ustar00rootroot00000000000000casacore-3.7.1/casa/BasicMath/test/CMakeLists.txt000066400000000000000000000005101476623553700216010ustar00rootroot00000000000000set (tests tFunctors tMath tMathNaN tPrimes tRNG tStdLogical ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_casa) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/casa/BasicMath/test/tFunctors.cc000066400000000000000000000131001476623553700213360ustar00rootroot00000000000000//# tFunctors.cc: //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include using namespace casacore; using namespace std; #define TESTFUNCTOR1(NAME, FUNC) \ { \ std::transform (v1.begin(), v1.end(), res.begin(), NAME()); \ for (uInt i=0; i()); \ for (uInt i=0; i()); \ for (uInt i=0; i()); \ for (uInt i=0; i()); \ for (uInt i=0; i()); \ for (uInt i=0; i arr1(shp), arr2(shp); Array arr1s(arr1(IPosition(3,0,0,0), IPosition(3,3,7,3))); Array arr2s(arr2(IPosition(3,0,0,0), IPosition(3,3,7,3))); indgen (arr1); indgen (arr2); AlwaysAssertExit (compareAll(arr1.begin(), arr1.end(), arr2.begin(), Near())); AlwaysAssertExit (compareAny(arr1.begin(), arr1.end(), arr2.begin(), Near())); AlwaysAssertExit (compareAll(arr1s.begin(), arr1s.end(), arr2s.begin(), Near())); AlwaysAssertExit (compareAny(arr1s.begin(), arr1s.end(), arr2s.begin(), Near())); arr1(IPosition(3,1,0,0)) -= 1; AlwaysAssertExit (! compareAll(arr1.begin(), arr1.end(), arr2.begin(), Near())); AlwaysAssertExit (compareAny(arr1.begin(), arr1.end(), arr2.begin(), Near())); AlwaysAssertExit (! compareAll(arr1s.begin(), arr1s.end(), arr2s.begin(), Near())); AlwaysAssertExit (compareAny(arr1s.begin(), arr1s.end(), arr2s.begin(), Near())); } int main() { try { vector v1(10); vector v2(10); vector res(10); vector reb(10); for (uInt i=0; i(0.25)); for (uInt i=0; i #include #include #include #include #include #include int main() { try { { Float x; setNaN(x); AlwaysAssert(isNaN(x), AipsError); AlwaysAssert(!isFinite(x), AipsError); } { Double x = floatNaN(); AlwaysAssert(isNaN(x), AipsError); AlwaysAssert(!isFinite(x), AipsError); } { Float x = doubleNaN(); AlwaysAssert(isNaN(x), AipsError); AlwaysAssert(!isFinite(x), AipsError); } { Double x; setNaN(x); AlwaysAssert(isNaN(x), AipsError); AlwaysAssert(!isFinite(x), AipsError); } { Float x; setInf(x); AlwaysAssert(isInf(x), AipsError); AlwaysAssert(!isFinite(x), AipsError); } { Double x = floatInf(); AlwaysAssert(isInf(x), AipsError); AlwaysAssert(!isFinite(x), AipsError); } { Float x = doubleInf(); AlwaysAssert(isInf(x), AipsError); AlwaysAssert(!isFinite(x), AipsError); } { Double x; setInf(x); AlwaysAssert(isInf(x), AipsError); AlwaysAssert(!isFinite(x), AipsError); } { Double x = 321.544; AlwaysAssert(roundDouble(x) == 320, AipsError); AlwaysAssert(roundDouble(x,3) == 322, AipsError); x = 21.45554; AlwaysAssert(roundDouble(x,2) == 21, AipsError); AlwaysAssert(roundDouble(x) == 21.5, AipsError); x = -11.324; AlwaysAssert(roundDouble(x,2) == -11, AipsError); x = -4502034; AlwaysAssert(roundDouble(x) == -4500000, AipsError); x = -0.012345; AlwaysAssert(nearAbs(roundDouble(x,4), -0.01235, 1e-8), AipsError); x = 0; AlwaysAssert(roundDouble(x) == 0, AipsError); AlwaysAssert(floormod(5,3) == 2, AipsError); AlwaysAssert(floormod(-5,3) == 1, AipsError); AlwaysAssert(floormod(5,-3) == -1, AipsError); AlwaysAssert(floormod(-5,-3) == -2, AipsError); AlwaysAssert(floormod(5.,3.) == 2., AipsError); AlwaysAssert(floormod(-5.,3.) == 1., AipsError); AlwaysAssert(floormod(5.,-3.) == -1., AipsError); AlwaysAssert(floormod(-5.,-3.) == -2., AipsError); AlwaysAssert(floormod(3.,3.) == 0., AipsError); AlwaysAssert(floormod(-3.,3.) == 0., AipsError); AlwaysAssert(floormod(3.,-3.) == 0., AipsError); AlwaysAssert(floormod(-3.,-3.) == 0., AipsError); } { // min/max uInt64 uInt64 a = 9876543210; uInt64 b = 9876543211; AlwaysAssert(min(a,b) == a, AipsError); AlwaysAssert(max(a,b) == b, AipsError); } } catch (std::exception& x) { cerr << x.what() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } // Local Variables: // compile-command: "gmake OPTLIB=1 tMath" // End: casacore-3.7.1/casa/BasicMath/test/tMathNaN.cc000066400000000000000000000132151476623553700210300ustar00rootroot00000000000000//# tMathNaN.cc: Test program for NaN //# Copyright (C) 1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #if defined(AIPS_SOLARIS) || defined(AIPS_IRIX) #include #endif #include #include #include #define isnanfmacro(x) (((*(Int *)(x) & 0x7f800000) == 0x7f800000) && \ ((*(Int *)(x) & 0x007fffff) != 0x00000000)) inline Bool isNaN_isnan(Float val) { return (std::isnan(Double(val))); } inline Bool isNaN_isnanf(const Float& val) { #if defined(AIPS_SOLARIS) || defined(AIPS_IRIX) return (isnanf(val)); #else return (isnanfmacro(&val)); #endif } inline Bool isNaN_ref(const Float &x) { return (((*(Int *)&(x) & 0x7f800000) == 0x7f800000) && \ ((*(Int *)&(x) & 0x007fffff) != 0x00000000)); } inline Bool isNaN_val(Float x) { Float* xp=&x; return (((*(Int *)xp & 0x7f800000) == 0x7f800000) && \ ((*(Int *)xp & 0x007fffff) != 0x00000000)); } Bool doIt (Int n, Float x, Bool nan) { Bool ok = True; const Int narr = 100000; // Determine the expected nr of NaN's. uInt nrnan = 0; if (nan) { nrnan = n*narr; } // Initialize the array. Float arr[narr]; for (Int i=0; i #include #include #include // Test the non-cache functions. void primesNoncacheTests (uInt number, Bool shouldBePrime, uInt numberOfFactors) { AlwaysAssertExit(Primes::isPrime(number) == shouldBePrime); Block factors=Primes::factor(number); AlwaysAssertExit(factors.nelements() == numberOfFactors); AlwaysAssertExit(Primes::smallestPrimeFactor(number) == factors[0]); } void largerPrimesTest (uInt number, uInt next, uInt closest) { AlwaysAssertExit(Primes::aLargerPrimeThan(number) == next); AlwaysAssertExit(Primes::nextLargerPrimeThan(number) == closest); } int main() { // First test the non-cache functions with some large numbers (for 32 bits) primesNoncacheTests (1610612736, False, 30); // 3 x 2^29 primesNoncacheTests (5*7*11*13*17*19*23*29, False, 8); primesNoncacheTests (46337*46337, False, 2); // Largest prime square primesNoncacheTests (46307*46309, False, 2); // Largest prime pair primesNoncacheTests (2147483647, True, 1); // 2^31 - 1 primesNoncacheTests (2147483629, True, 1); // Next smaller prime primesNoncacheTests (0, False, 1); largerPrimesTest (4098, 4099, 4099); //immediately followed //by cached prime largerPrimesTest (1073741828, 0, 1073741831); //larger than largest //cached prime largerPrimesTest (0, 3, 2); largerPrimesTest (0, 2, 2); //"2" is now in cache cout << "OK" << endl; return 0; } casacore-3.7.1/casa/BasicMath/test/tRNG.cc000066400000000000000000000111071476623553700201660ustar00rootroot00000000000000//# tRNG.cc: A test program for the RNG, ACG & MLCG classes //# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include int main() { try { uInt i; Float f; Double d; cout << "testing the MLCG generator" << endl; { cout << "random integers, floats & doubles" << endl; MLCG g; for (uInt k = 0; k < 4; k++) { // Note the values are calculated here rather than in the print // statemement because of the problem discussed in // http://aips2.nrao.edu/mail/aips2-lib/1391 i = g.asuInt(); f = g.asFloat(); d = g.asDouble(); cout << k << ": " << setbase(16) << i << ": " << setprecision(6) << f << ": " << setprecision(12) << d << endl; } cout << "resetting the generator. Should get the same numbers" << endl; g.reset(); for (uInt k = 0; k < 4; k++) { i = g.asuInt(); f = g.asFloat(); d = g.asDouble(); cout << k << ": " << setbase(16) << i << ": " << setprecision(6) << f << ": " << setprecision(12) << d << endl; } } { cout << "Using a different seed" << endl; MLCG g(1, 0); for (uInt k = 0; k < 4; k++) { i = g.asuInt(); f = g.asFloat(); d = g.asDouble(); cout << k << ": " << setbase(16) << i << ": " << setprecision(6) << f << ": " << setprecision(12) << d << endl; } cout << "resetting the generator. Should get the same numbers" << endl; g.reset(); for (uInt k = 0; k < 4; k++) { i = g.asuInt(); f = g.asFloat(); d = g.asDouble(); cout << k << ": " << setbase(16) << i << ": " << setprecision(6) << f << ": " << setprecision(12) << d << endl; } } cout << "testing the ACG generator" << endl; { cout << "random integers, floats & doubles" << endl; ACG g; for (uInt k = 0; k < 4; k++) { i = g.asuInt(); f = g.asFloat(); d = g.asDouble(); cout << k << ": " << setbase(16) << i << ": " << setprecision(6) << f << ": " << setprecision(12) << d << endl; } cout << "resetting the generator. Should get the same numbers" << endl; g.reset(); for (uInt k = 0; k < 4; k++) { i = g.asuInt(); f = g.asFloat(); d = g.asDouble(); cout << k << ": " << setbase(16) << i << ": " << setprecision(6) << f << ": " << setprecision(12) << d << endl; } } { cout << "Using a different seed" << endl; ACG g(7326458, 98); for (uInt k = 0; k < 4; k++) { i = g.asuInt(); f = g.asFloat(); d = g.asDouble(); cout << k << ": " << setbase(16) << i << ": " << setprecision(6) << f << ": " << setprecision(12) << d << endl; } cout << "resetting the generator. Should get the same numbers" << endl; g.reset(); for (uInt k = 0; k < 4; k++) { i = g.asuInt(); f = g.asFloat(); d = g.asDouble(); cout << k << ": " << setbase(16) << i << ": " << setprecision(6) << f << ": " << setprecision(12) << d << endl; } } } catch (std::exception& x) { cerr << x.what() << endl; cout << "FAIL" << endl; return 1; } catch (...) { cerr << "Exception not derived from AipsError" << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } // Local Variables: // compile-command: "gmake tRNG" // End: casacore-3.7.1/casa/BasicMath/test/tRNG.out000066400000000000000000000031021476623553700204040ustar00rootroot00000000000000testing the MLCG generator random integers, floats & doubles 0: 4ffb0fe8: 0.951057: 0.224307257582 1: 7d0a8f71: 0.990539: 0.124021621998 2: 615c0703: 0.988474: 0.990468198826 3: 1d66a4ae: 0.0601412: 0.514878396855 resetting the generator. Should get the same numbers 0: 4ffb0fe8: 0.951057: 0.224307257582 1: 7d0a8f71: 0.990539: 0.124021621998 2: 615c0703: 0.988474: 0.990468198826 3: 1d66a4ae: 0.0601412: 0.514878396855 Using a different seed 0: 3e84478b: 0.916736: 0.368252914916 1: 3d4139c3: 0.114195: 0.816914884857 2: 6636898c: 0.175281: 0.467684062113 3: 319ee548: 0.559797: 0.0895876175065 resetting the generator. Should get the same numbers 0: 3e84478b: 0.916736: 0.368252914916 1: 3d4139c3: 0.114195: 0.816914884857 2: 6636898c: 0.175281: 0.467684062113 3: 319ee548: 0.559797: 0.0895876175065 testing the ACG generator random integers, floats & doubles 0: 5ae6c126: 0.941752: 0.223244717431 1: f4cc99d2: 0.956651: 0.43620916494 2: 5c3b00ed: 0.418954: 0.0735634721186 3: 627137fe: 0.913327: 0.64354605158 resetting the generator. Should get the same numbers 0: 5ae6c126: 0.941752: 0.223244717431 1: f4cc99d2: 0.956651: 0.43620916494 2: 5c3b00ed: 0.418954: 0.0735634721186 3: 627137fe: 0.913327: 0.64354605158 Using a different seed 0: cbda4069: 0.913194: 0.718020789668 1: ea25a3d6: 0.915639: 0.949607629571 2: 60bd8523: 0.0316063: 0.39812996443 3: 16a769f9: 0.0873321: 0.804342518522 resetting the generator. Should get the same numbers 0: cbda4069: 0.913194: 0.718020789668 1: ea25a3d6: 0.915639: 0.949607629571 2: 60bd8523: 0.0316063: 0.39812996443 3: 16a769f9: 0.0873321: 0.804342518522 OK casacore-3.7.1/casa/BasicMath/test/tStdLogical.cc000066400000000000000000000037331476623553700215730ustar00rootroot00000000000000//# tStdLogical.cc: Test program for functions in StdLogical.h //# Copyright (C) 2012 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include using namespace casacore; int main() { try { std::set a; std::set b; Float a1[] = {0.1, 5.2, 6.3}; Float b1[] = {0.5}; a.insert(a1, a1+3); b.insert(b1, b1+1); AlwaysAssert(! allNearAbs(a, b, 0.1), AipsError); b.clear(); Float b2[] = {0.2, 5.1, 6.4}; b.insert(b2, b2+3); AlwaysAssert(! allNearAbs(a, b, 0.05), AipsError); AlwaysAssert(allNearAbs(a, b, 0.11), AipsError); AlwaysAssert(compareAll(a.begin(), a.end(), a1, NearAbs(0.1)), AipsError); cout << "OK" << endl; return 0; } catch (const AipsError& exc) { cout << "FAIL" << endl; return 1; } } casacore-3.7.1/casa/BasicSL.h000066400000000000000000000061031476623553700156640ustar00rootroot00000000000000//# BasicSL.h: Basic standard library related classes. //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_BASICSL_H #define CASA_BASICSL_H #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // // Classes and global functions for standard library use // // // // // This module is a bag of related standard library classes and // global functions. // // The following functionality is available: //
          //
        • Forward declarations // // Complexfwd // for complex numbers. //
        • Class // Complex // to offer single and double precision complex numbers. //
        • Class // IComplex // for integer complex numbers. //
        • Value // Constants // to offer mathematical and numerical constants. //
        • Class // String // for handling character strings. //
        • STLMath.h has some functions doing math on std::vector objects. //
        • STLIO.h has some functions showing a container on std::ostream or LogIO. //
        // // You may want to look at the individual header files // to see whether you might not prefer to include only the header // files you really need; it may be more efficient to do so. // // //
        //# //#
      • //# // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/BasicSL/000077500000000000000000000000001476623553700155135ustar00rootroot00000000000000casacore-3.7.1/casa/BasicSL/Complex.cc000066400000000000000000000122331476623553700174320ustar00rootroot00000000000000//# Complex.cc: Implement Complex, DComplex //# Copyright (C) 2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Math functions // Near functions // Note: max() cannot be used from Math.h until it is derived from // Note: abs() not defined in SGI // Bool near(const Complex &val1, const Complex &val2, Double tol) { if (tol <= 0) return val1 == val2; if (val1 == val2) return True; if (near(val1.real(), val2.real(), tol) && near(val1.imag(), val2.imag(), tol)) return True; Float aval1(std::abs(val1)), aval2(std::abs(val2)); if (aval1 == 0) return aval2 <= (1+tol)*FLT_MIN; else if (aval2 == 0) return aval1 <= (1+tol)*FLT_MIN; DComplex dval(val1); dval -= DComplex(val2); return std::abs(dval) <= tol * (aval1 < aval2 ? aval2 : aval1); } Bool near(const DComplex &val1, const DComplex &val2, Double tol) { if (tol <= 0) return val1 == val2; if (val1 == val2) return True; if (std::abs(val1) == 0) return std::abs(val2) <= (1+tol)*DBL_MIN; else if (std::abs(val2) == 0) return std::abs(val1) <= (1+tol)*DBL_MIN; Double aval1(std::abs(val1)), aval2(std::abs(val2)); return std::abs(val1-val2) <= tol * (aval1 < aval2 ? aval2 : aval1); } Bool nearAbs(const Complex &val1, const Complex &val2, Double tol) { return std::abs(val2 - val1) <= tol; } Bool nearAbs(const DComplex &val1, const DComplex &val2, Double tol) { return std::abs(val2 - val1) <= tol; } // NaN functions Bool isNaN(const Complex &val) { return isNaN(val.real()) || isNaN(val.imag()); } Bool isNaN(const DComplex &val) { return isNaN(val.real()) || isNaN(val.imag()); } void setNaN(Complex &val) { Float x; setNaN(x); Float y; setNaN(y); val = Complex(x, y); } void setNaN(DComplex &val) { Double x; setNaN(x); Double y; setNaN(y); val = DComplex(x, y); } // Inf functions Bool isInf(const Complex &val) { return isInf(val.real()) || isInf(val.imag()); } Bool isInf(const DComplex &val) { return isInf(val.real()) || isInf(val.imag()); } void setInf(Complex &val) { Float x; setInf(x); Float y; setInf(y); val = Complex(x, y); } void setInf(DComplex &val) { Double x; setInf(x); Double y; setInf(y); val = DComplex(x, y); } // Finite functions Bool isFinite(const Complex &val) { return isFinite(val.real()) || isFinite(val.imag()); } Bool isFinite(const DComplex &val) { return isFinite(val.real()) || isFinite(val.imag()); } // fmod functions DComplex fmod(const DComplex &in, const DComplex &f) { return DComplex(std::fmod(real(in), real(f)), imag(in)); } Complex fmod(const Complex &in, const Complex &f) { return Complex(std::fmod(real(in), real(f)), imag(in)); } // Inverse trigonometry (see Abromowitz) DComplex atan(const DComplex &in) { const Double n = norm(in); return DComplex(0.5*std::atan(2.0*real(in)/(1.0-n)), 0.25*std::log((1.0+n+2*imag(in))/(1.0+n-2*imag(in)))); } Complex atan(const Complex &in) { const Float n = norm(in); return Complex(0.5*std::atan(2.0*real(in)/(1.0-n)), 0.25*std::log((1.0+n+2*imag(in))/(1.0+n-2*imag(in)))); } DComplex asin(const DComplex &in) { return std::asin(in); } Complex asin(const Complex &in) { return std::asin(in); } DComplex acos(const DComplex &in) { return std::acos(in); } Complex acos(const Complex &in) { return std::acos(in); } DComplex atan2(const DComplex &in, const DComplex &t2) { if (norm(t2) == 0) return DComplex(M_PI_2); const DComplex z = atan(in/t2); if (real(t2) > 0) return z; return (z + DComplex(M_PI)); } Complex atan2(const Complex &in, const Complex &t2) { if (norm(t2) == 0) return Complex(M_PI_2); const Complex z = atan(in/t2); if (real(t2) > 0) return z; return (z + Complex(M_PI)); } /// Temporary solutions only DComplex erf(const DComplex &in) { return ::erf(in.real()); } Complex erf(const Complex &in) { return ::erf(in.real()); } DComplex erfc(const DComplex &in) { return ::erfc(in.real()); } Complex erfc(const Complex &in) { return ::erfc(in.real()); } } //# NAMESPACE CASACORE - END ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������casacore-3.7.1/casa/BasicSL/Complex.h���������������������������������������������������������������0000664�0000000�0000000�00000031434�14766235537�0017300�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������//# Complex.h: Single and double precision complex numbers //# Copyright (C) 2000,2001,2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_COMPLEX_H #define CASA_COMPLEX_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Single and double precision complex numbers // // // // // The class Complex is a straight typedef as the // standard library complex. // // In a similar way DComplex is typedef-ed as // complex. // // IComplex is defined as a specific class. // It is only used by the FITS classes. // // lDComplex has not been defined: long double is not // part of the standard Casacore data suite (yet) // // A set of global functions are added for historic reasons (they were present // in the original Casacore/gcc complex implementation). // // See the standard library documentation for the expected behaviour of // the Complex and DComplex classes. // // In the following all references to Complex // can be replaced with DComplex. with simultaneous // replacement of Float with Double. // // Complex numbers may be constructed and used in the following ways: //
        //
        Complex x;
        //
        Declares an uninitialized Complex.
        // //
        Complex x = 2; Complex y(2.0);
        //
        Set x and y to the Complex value (2.0, 0.0);
        // //
        Complex x(2, 3);
        //
        Sets x to the Complex value (2, 3);
        // //
        Complex u(x); Complex v = x;
        //
        Set u and v to the same value as x.
        // //
        Float real(Complex& x);
        //
        returns the real part of x.
        // //
        Float imag(Complex& x);
        //
        returns the imaginary part of x.
        // //
        Float abs(Complex& x);
        //
        returns the magnitude of x.
        // //
        Float norm(Complex& x);
        //
        returns the square of the magnitude of x.
        // //
        Float arg(Complex& x);
        //
        returns the argument (amplitude) of x.
        // //
        Complex polar(Float r, Float t = 0.0);
        //
        returns a Complex with abs of r and arg of t.
        // //
        Complex conj(Complex& x);
        //
        returns the complex conjugate of x
        // //
        Complex cos(Complex& x);
        //
        returns the complex cosine of x.
        // //
        Complex sin(Complex& x);
        //
        returns the complex sine of x.
        // //
        Complex cosh(Complex& x);
        //
        returns the complex hyperbolic cosine of x.
        // //
        Complex sinh(Complex& x);
        //
        returns the complex hyperbolic sine of x.
        // //
        Complex exp(Complex& x);
        //
        returns the exponential of x.
        // //
        Complex log(Complex& x);
        //
        returns the natural log of x.
        // //
        Complex pow(Complex& x, long p);
        //
        returns x raised to the p power.
        // //
        Complex pow(Complex& x, Complex& p);
        //
        returns x raised to the p power.
        // //
        Complex sqrt(Complex& x);
        //
        returns the square root of x.
        // //
        Complex min(Complex x,Complex y); //
        Returns the minumum of x,y (using operator<=, i.e. the norm). // //
        Complex max(Complex x,Complex y); //
        Returns the maximum of x,y (using operator>=, i.e. the norm). // //
        Bool near(Complex val1, Complex val2, Double tol = 1.0e-5);
        //
        returns whether val1 is relatively near val2 (see Math.h). // (Note the Double tolerance)
        // //
        Bool nearAbs(Complex val1, Complex val2, Double tol = 1.0e-5);
        //
        returns whether val1 is absolutely near val2 (see Math.h). // (Note the Double tolerance)
        // //
        ostream << x;
        //
        prints x in the form (re, im).
        // //
        istream >> x;
        //
        reads x in the form (re, im), or just (re) or re in which case the // imaginary part is set to zero.
        //
        // //# //# // // Complex NaN and Infinity // // // Bool isNaN (const Complex& val); void setNaN(Complex& val); Bool isInf (const Complex& val); void setInf(Complex& val); Bool isFinite(const Complex& val); // // Complex comparisons // // // //# On Linux comparing the norm does not work well in debug mode //# for equal values. Therefore they are compared for equality first. inline Bool operator>= (const Complex& left, const Complex& right) { return left==right ? True : norm(left) >= norm(right); } inline Bool operator> (const Complex& left, const Complex& right) { return left==right ? False : norm(left) > norm(right); } inline Bool operator<= (const Complex& left, const Complex& right) { return left==right ? True : norm(left) <= norm(right); } inline Bool operator< (const Complex& left, const Complex& right) { return left==right ? False : norm(left) < norm(right); } // // DComplex NaN and Infinity // // // Bool isNaN (const DComplex& val); void setNaN(DComplex& val); Bool isInf (const DComplex& val); void setInf(DComplex& val); Bool isFinite(const DComplex& val); // // DComplex comparisons // // // inline Bool operator>= (const DComplex& left, const DComplex& right) { return norm(left) >= norm(right); } inline Bool operator> (const DComplex& left, const DComplex& right) { return norm(left) > norm(right); } inline Bool operator<= (const DComplex& left, const DComplex& right) { return norm(left) <= norm(right); } inline Bool operator< (const DComplex& left, const DComplex& right) { return norm(left) < norm(right); } // //# Global functions // Additional complex mathematical functions // // // inline Double fabs(const DComplex &val) { return std::abs(val); } inline Float fabs(const Complex &val) { return std::abs(val); } inline DComplex square(const DComplex &val) { return val*val; } inline Complex square(const Complex &val) { return val*val; } inline DComplex cube(const DComplex &val) { return val*val*val; } inline Complex cube(const Complex &val) { return val*val*val; } // ArrayMath::pow needs this pow function. ///inline Complex pow(const Complex& val, Double p) { return std::pow(val,Float(p)); } // We have to explicitly implement these for different type operands inline DComplex operator+(const DComplex& d, const Complex& c) { return (DComplex)c + d; } inline DComplex operator+(const Complex& c, const DComplex& d) { return (DComplex)c + d; } inline DComplex operator-(const DComplex& d, const Complex& c) { return d - (DComplex)c; } inline DComplex operator-(const Complex& c, const DComplex& d) { return (DComplex)c - d; } // QMath and scimath need these operators * and / // inline Complex operator*(const Complex& val, Double f) { return val*Float(f); } inline Complex operator*(Double f, const Complex& val) { return val*Float(f); } inline Complex operator/(const Complex& val, Double f) { return val/Float(f); } inline Complex operator/(Double f, const Complex& val) { return Float(f)/val; } // // These operators are useful, otherwise both Float and Double are applicable // for Ints. // inline Complex operator*(const Complex& val, Int f) { return val*Float(f); } inline Complex operator*(Int f, const Complex& val) { return val*Float(f); } inline Complex operator/(const Complex& val, Int f) { return val/Float(f); } inline Complex operator/(Int f, const Complex& val) { return Float(f)/val; } // // // The near functions // // // Bool near(const Complex &val1, const Complex &val2, Double tol=1.0e-5); Bool near(const DComplex &val1, const DComplex &val2, Double tol=1.0e-13); Bool nearAbs(const Complex &val1, const Complex &val2, Double tol=1.0e-5); Bool nearAbs(const DComplex &val1, const DComplex &val2, Double tol=1.0e-13); inline Bool allNear(const Complex &val1, const Complex &val2, Double tol=1.0e-5) { return near(val1, val2, tol); } inline Bool allNear(const DComplex &val1, const DComplex &val2, Double tol=1.0e-13) { return near(val1, val2, tol); } inline Bool allNearAbs(const Complex &val1, const Complex &val2, Double tol=1.0e-5) { return nearAbs(val1, val2, tol); } inline Bool allNearAbs(const DComplex &val1, const DComplex &val2, Double tol=1.0e-13) { return nearAbs(val1, val2, tol); } // // Max and min, floor and ceil functions // // // inline Complex max(const Complex &x, const Complex &y) { return x >= y ? x : y; } inline DComplex max(const DComplex &x, const DComplex &y) { return x >= y ? x : y; } inline Complex min(const Complex &x, const Complex &y) { return x <= y ? x : y; } inline DComplex min(const DComplex &x, const DComplex &y) { return x <= y ? x : y; } inline Complex floor(const Complex &x) { return Complex(std::floor(x.real()), std::floor(x.imag())); } inline DComplex floor(const DComplex &x) { return DComplex(std::floor(x.real()), std::floor(x.imag())); } inline Complex ceil(const Complex &x) { return Complex(std::ceil(x.real()), std::ceil(x.imag())); } inline DComplex ceil(const DComplex &x) { return DComplex(std::ceil(x.real()), std::ceil(x.imag())); } // // fmod // // // DComplex fmod(const DComplex &in, const DComplex &f); Complex fmod(const Complex &in, const Complex &f); // // Inverse trigonometry // // // // atan not valid for z == -1 DComplex atan(const DComplex &in); Complex atan(const Complex &in); DComplex asin(const DComplex &in); Complex asin(const Complex &in); DComplex acos(const DComplex &in); Complex acos(const Complex &in); DComplex atan2(const DComplex &in, const DComplex &t2); Complex atan2(const Complex &in, const Complex &t2); // // Error function // // // // Preliminary to get Functionals working. erf(z) will return erf(real(z)) // only for now. DComplex erf(const DComplex &in); Complex erf(const Complex &in); DComplex erfc(const DComplex &in); Complex erfc(const Complex &in); // // } //# NAMESPACE CASACORE - END // Define real & complex conjugation for non-complex types // and put comparisons into std namespace. namespace std { inline float conj(float x) { return x; } inline double conj(double x) { return x; } using casacore::operator>; using casacore::operator>=; using casacore::operator<; using casacore::operator<=; } #endif casacore-3.7.1/casa/BasicSL/Complexfwd.h000066400000000000000000000035631476623553700200030ustar00rootroot00000000000000//# Complexfwd.h: Forward declaration complex classes //# Copyright (C) 2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_COMPLEXFWD_H #define CASA_COMPLEXFWD_H //# Includes #include // Forward declaration complex classes // // // // The Complexfwd.h include file can be used where a forward declaration // of the Casacore complex classes could suffice (cf the system's iosfwd). // // #include namespace casacore { //# NAMESPACE CASACORE - BEGIN typedef std::complex Complex; typedef std::complex DComplex; class IComplex; // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/BasicSL/Constants.h000066400000000000000000000476421476623553700176550ustar00rootroot00000000000000//# Constants.h: Mathematical and numerical constants //# Copyright (C) 1993,1994,1995,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_CONSTANTS_H #define CASA_CONSTANTS_H #include #if defined (sun) && ! defined (AIPS_SOLARIS) # include #else # include #endif #include #if !defined(AIPS_DARWIN) && !defined(AIPS_BSD) #include #endif #if defined (AIPS_OSF) # define LN_MAXFLOAT (M_LN2 * FMAXEXP) # define LN_MINFLOAT (M_LN2 * (FMINEXP -1)) #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN // Mathematical and numerical constants. // // // // The constants and conversion factors are defined here as double precision // values. Where single precision calculations are done in a situation where // processing speed is of concern, for example within the inner loop of an // expensive algorithm, a separate single precision variable should be defined // for use within the loop. // // // // The following list is generated by hand, and may be incomplete. // After future revision of ccx2html the real data will be displayed //

        Floating point limits

        // // flt_min the minimum single precision floating point number, // excluding denormalised numbers // minfloat the minimum single precision floating point number, // including denormalised numbers // dbl_min the minimum double precision floating point number, // excluding denormalised numbers // mindouble the minimum double precision floating point number, // including denormalised numbers // flt_max the maximum single precision floating point number // dbl_max the maximum double precision floating point number // flt_epsilon Minimum single precision floating point number X // such that 1+X does not equal X // dbl_epsilon Minimum double precision floating point number X // such that 1+X does not equal X // //

        Irrationals

        // // sqrt2 sqrt(2) // sqrt3 sqrt(3) // _1_sqrt2 1/sqrt(2) // _1_sqrt3 1/sqrt(3) // //

        Pi and functions thereof

        // // pi pi // _2pi 2*pi // pi_2 pi/2 // pi_4 pi/4 // _1_pi 1/pi // _2_pi 2/pi // _1_sqrtpi 1/sqrt(pi) // _2_sqrtpi 2/sqrt(pi) // //

        e and functions thereof

        // // e e // ln2 ln(2) // ln10 ln(10) // log2e log2(e) // log10e log10(e) // //

        gamma and functions thereof

        // // gamma gamma // lngamma ln(gamma) // etogamma e**gamma // //

        Fundamental physical constants (SI units)

        // Preserved for legacy reasons only. // See QC class for other physical constants. // // c velocity of light (m/s) // //

        Numerical conversion factors

        // // quetta e+30 (Q) // ronna e+27 (R) // yotta e+24 (Y) // zetta e+21 (Z) // exa e+18 (E) // peta e+15 (P) // tera e+12 (T) // giga e+09 (G) // mega e+06 (M) // kilo e+03 (k) // hecto e+02 (h) // deka e+01 (da) // deci e-01 (d) // centi e-02 (c) // milli e-03 (m) // micro e-06 (u) // nano e-09 (n) // pico e-12 (p) // femto e-15 (f) // atto e-18 (a) // zepto e-21 (z) // yocto e-24 (y) // ronto e-27 (r) // quecto e-30 (q) // //

        Angular measure

        // // radian radian // circle circle // degree degree // arcmin arcminute // arcsec arcsecond // //

        Solid angular measure

        // // steradian steradian // sphere sphere // square_degree square degree // square_arcmin square arcminute // square_arcsec square arcsecond // //

        Time interval

        // // second second // minute minute // hour hour // day day // //

        Machine constants

        // // Implementation-defined limits usually defined in , // , and as preprocessor // defines. They are // Inclusion of is // sufficient to ensure that they are defined for any particular // implementation, and the correct functioning of the tConstants // test program guarantees this. // // In future use will be made of the (standard) numeric_limits // template from the include file. // // // Refer to Section 3.2c, pp28-30 of // "The Annotated C++ Reference Manual", // Ellis, M.A., and Stroustrup, B., // Addison-Wesley Publishing Company, 1990. // IBSN 0-201-51459-1. // // and // // Appendix B11, pp257-8 of // "The C Programming Language", 2nd ed., // Kernighan, B.W., and Ritchie, D.M., // Prentice Hall Software Series, 1988. // IBSN 0-13-110362-8. // // //

        Constants defined in limits.h

        // (these are part of the ANSI C and hence POSIX standards). // Acceptable limits defined by the standard are quoted. // // // CHAR_BIT 8 Maximum bits in a byte. // CHAR_MIN 0 or Minimum value of 'char'. // SCHAR_MIN // CHAR_MAX UCHAR_MAX or Maximum value of 'char'. // SCHAR_MAX // SCHAR_MIN -127 Minimum value of 'signed char'. // SCHAR_MAX +127 Maximum value of 'signed char'. // UCHAR_MAX 255 Maximum value of 'unsigned char'. // MB_LEN_MAX Maximum bytes in multibyte character. // // SHRT_MIN -32767 Minimum value of 'short'. // SHRT_MAX +32767 Maximum value of 'short'. // USHRT_MAX 65535 Maximum value of 'unsigned short'. // // INT_MIN -32767 Minimum value of 'int'. // INT_MAX +32767 Maximum value of 'int'. // UINT_MAX 65535 Maximum value of 'unsigned int'. // // LONG_MIN -2147483647 Minimum value of 'long'. // LONG_MAX +2147483647 Maximum value of 'long'. // ULONG_MAX 4294967295 Maximum value of 'unsigned long'. // // //

        Constants defined in float.h

        // (these are part of the ANSI C and hence POSIX standards). // Acceptable limits defined by the standard are quoted. // // // FLT_RADIX 2 Radix of exponent representation. // FLT_ROUNDS Floating point rounding mode for addition // -1: indeterminate // 0: towards zero // 1: to nearest // 2: toward +infinity // 3: toward -infinity // // FLT_MIN_EXP Minimum negative integer N such that FLT_RADIX // DBL_MIN_EXP raised to the Nth minus 1 is a normalized // LDBL_MIN_EXP floating point number. // // FLT_MAX_EXP Maximum integer N such that FLT_RADIX raised to // DBL_MAX_EXP the Nth minus 1 is representable. // LDBL_MAX_EXP // // FLT_MIN_10_EXP -37 Minimum negative integer N such that 10 raised // DBL_MIN_10_EXP -37 to the Nth is in the range of normalized // LDBL_MIN_10_EXP -37 floating point numbers. // // FLT_MAX_10_EXP 37 Maximum integer N such that 10 raised to the // DBL_MAX_10_EXP 37 Nth minus 1 is representable. // LDBL_MAX_10_EXP 37 // // FLT_MANT_DIG Number of base FLT_RADIX digits in mantissa. // DBL_MANT_DIG // LDBL_MANT_DIG // // FLT_DIG 6 Decimal digits of precision. // DBL_DIG 10 // LDBL_DIG 10 // // FLT_EPSILON 1E-5 Minimum floating point number X such that // (use C::flt_epsilon in preference to this) // DBL_EPSILON 1E-9 1.0 + X does not equal 1.0. // (use C::dbl_epsilon in preference to this) // LDBL_EPSILON 1E-9 // // FLT_MIN 1E-37 Minimum normalized positive floating point // (use C::flt_min in preference to this) // DBL_MIN 1E-37 number // (use C::dbl_min in preference to this) // LDBL_MIN 1E-37 // // FLT_MAX 1E+37 Maximum representable floating point number. // (use C::flt_max in preference to this) // DBL_MAX 1E+37 // (use C::dbl_max in preference to this) // LDBL_MAX 1E+37 // // //

        Constants defined in values.h

        // (not part of the POSIX standard). // These constants will disappear in the near future. // Do not use them in new code. // // // HIBITS Value of a short integer with only the high-order // bit set (in most implementations, 0x8000). // // HIBITL Value of a long integer with only the high-order // bit set (in most implementations, 0x80000000). // // MAXSHORT Maximum value of a signed short integer (in most // implementations, 0x7FFF = 32767). // // MAXLONG Maximum value of a signed long integer (in most // implementations, 0x7FFFFFFF = 2147483647). // // MAXINT Maximum value of a signed regular integer (usually // the same as MAXSHORT or MAXLONG). // // MINFLOAT Minimum positive value of a single-precision // floating-point number (use C::minfloat in preference // to this) // // MINDOUBLE Minimum positive value of a double-precision // floating-point number (use C::mindouble in preference // to this) // // MAXFLOAT Maximum value of a single-precision floating-point number // // MAXDOUBLE Maximum value of a double-precision floating-point number // // FSIGNIF Number of significant bits in the mantissa of a // single-precision floating-point number. // // DSIGNIF Number of significant bits in the mantissa of a // double-precision floating-point number. // namespace C { //#-------------------------------------------------------------------- // Mathematical constants //#-------------------------------------------------------------------- //# // Irrationals: // // sqrt(2) [[deprecated("use M_SQRT2")]] inline constexpr double sqrt2 = 1.4142135623730950488; // sqrt(3) inline constexpr double sqrt3 = 1.7320508075688772935; // 1/sqrt(2) [[deprecated("use M_SQRT1_2")]] inline constexpr double _1_sqrt2 = 0.70710678118654752440; // 1/sqrt(3) inline constexpr double _1_sqrt3 = 0.57735026918962576451; // // Pi and functions thereof: // // pi [[deprecated("use M_PI")]] inline constexpr double pi = 3.141592653589793238462643; // 2*pi [[deprecated("use (2.0*M_PI)")]] inline constexpr double _2pi = 6.283185307179586476925286; // pi/2 [[deprecated("use M_PI_2")]] inline constexpr double pi_2 = 1.570796326794896619231322; // pi/4 [[deprecated("use M_PI_4")]] inline constexpr double pi_4 = 0.7853981633974483096156608; // 1/pi [[deprecated("use M_1_PI")]] inline constexpr double _1_pi = 0.3183098861837906715377675; // 2/pi [[deprecated("use M_2_PI")]] inline constexpr double _2_pi = 0.6366197723675813430755350; // 1/sqrt(pi) [[deprecated("use (0.5 * M_2_SQRTPI)")]] inline constexpr double _1_sqrtpi = 0.5641895835477562869480795; // 2/sqrt(pi) [[deprecated("use M_2_SQRTPI")]] inline constexpr double _2_sqrtpi = 1.1283791670955125738961590; // // e and functions thereof: // // e [[deprecated("use M_E")]] inline constexpr double e = 2.718281828459045235360287; // ln(2) [[deprecated("use M_LN2")]] inline constexpr double ln2 = 0.6931471805599453094172321; // ln(10) [[deprecated("use M_LN10")]] inline constexpr double ln10 = 2.3025850929940456840179915; // log2(e) [[deprecated("use M_LOG2E")]] inline constexpr double log2e = 1.4426950408889634074; // log10(e) [[deprecated("use M_LOG10E")]] inline constexpr double log10e = 0.4342944819032518276511289; // // gamma and functions thereof: // // gamma // With C++20, could be deprecated and use std::numbers::gamma instead inline constexpr double gamma = 0.577215664901532860606512; // ln(gamma) // With C++20, could be deprecated and use std::log(std::numbers::gamma) instead inline constexpr double lngamma = -0.549539312981644822337662; // e**gamma // With C++20, could be deprecated and use std::exp(std::numbers::gamma) instead inline constexpr double etogamma = 1.7810724179901979852; // // statistics related // 1/(Phi^(-1)(3/4), see https://en.wikipedia.org/wiki/Median_absolute_deviation#Relation_to_standard_deviation inline constexpr double probit_3_4 = 1.482602218505602; //#-------------------------------------------------------------------- //# Mathematical constants //#-------------------------------------------------------------------- //# //#-------------------------------------------------------------------- // Machine constants //#-------------------------------------------------------------------- //# // floating point limits // // the minimum single precision floating point number, // excluding denormalised numbers [[deprecated("Use FLT_MIN")]] inline constexpr double flt_min = FLT_MIN; // the minimum single precision floating point number, // including denormalised numbers [[deprecated("Use FLT_MIN")]] inline constexpr double minfloat = FLT_MIN; // the minimum double precision floating point number, // excluding denormalised numbers [[deprecated("Use DBL_MIN")]] inline constexpr double dbl_min = DBL_MIN; // the minimum double precision floating point number, // including denormalised numbers [[deprecated("Use DBL_MIN")]] inline constexpr double mindouble = DBL_MIN; // the maximum single precision floating point number [[deprecated("Use FLT_MAX")]] inline constexpr double flt_max = FLT_MAX; // the maximum double precision floating point number [[deprecated("Use DBL_MAX")]] inline constexpr double dbl_max = DBL_MAX; // Minimum single precision floating point number X such that 1+X does not // equal X [[deprecated("Use FLT_EPSILON")]] inline constexpr double flt_epsilon = FLT_EPSILON; // Minimum double precision floating point number X such that 1+X does not // equal X [[deprecated("Use DBL_EPSILON")]] inline constexpr double dbl_epsilon = DBL_EPSILON; // //#-------------------------------------------------------------------- //# Machine constants //#-------------------------------------------------------------------- //# //#-------------------------------------------------------------------- //# Physical constants, and quantities //#-------------------------------------------------------------------- //# // Fundamental physical constants (SI units): // // velocity of light -- for legacy reasons only -- refer to PC inline constexpr double c = 2.99792458e+08; // //#-------------------------------------------------------------------- //# Physical constants, and quantities //#-------------------------------------------------------------------- //# //#-------------------------------------------------------------------- //# Physical units //#-------------------------------------------------------------------- //# //#----------------------------- //# Numerical conversion factors //#----------------------------- //# // Numerical conversion factors // // e+30 (Q) inline constexpr double quetta = 1.0e+30; // e+27 (R) inline constexpr double ronna = 1.0e+27; // e+24 (Y) inline constexpr double yotta = 1.0e+24; // e+21 (Z) inline constexpr double zetta = 1.0e+21; // e+18 (E) inline constexpr double exa = 1.0e+18; // e+15 (P) inline constexpr double peta = 1.0e+15; // e+12 (T) inline constexpr double tera = 1.0e+12; // e+09 (G) inline constexpr double giga = 1.0e+09; // e+06 (M) inline constexpr double mega = 1.0e+06; // e+03 (k) inline constexpr double kilo = 1.0e+03; // e+02 (h) inline constexpr double hecto = 1.0e+02; // e+01 (da) inline constexpr double deka = 1.0e+01; // e-01 (d) inline constexpr double deci = 1.0e-01; // e-02 (c) inline constexpr double centi = 1.0e-02; // e-03 (m) inline constexpr double milli = 1.0e-03; // e-06 (u) inline constexpr double micro = 1.0e-06; // e-09 (n) inline constexpr double nano = 1.0e-09; // e-12 (p) inline constexpr double pico = 1.0e-12; // e-15 (f) inline constexpr double femto = 1.0e-15; // e-18 (a) inline constexpr double atto = 1.0e-18; // e-21 (z) inline constexpr double zepto = 1.0e-21; // e-24 (y) inline constexpr double yocto = 1.0e-24; // e-27 (r) inline constexpr double ronto = 1.0e-27; // e-30 (q) inline constexpr double quecto = 1.0e-30; // // Angular measure: // // radian inline constexpr double radian = 1.0; // circle inline constexpr double circle = 6.2831853071795864769252867; // degree inline constexpr double degree = 0.0174532925199432957692369; // arcminute inline constexpr double arcmin = 0.000290888208665721596153948459; // arcsecond inline constexpr double arcsec = 0.00000484813681109535993589914098765; // // Solid angular measure: // // steradian inline constexpr double steradian = 1.0; // sphere inline constexpr double sphere = 12.56637061435917295385057344; // square degree inline constexpr double square_degree = 0.30461741978670859934674354486e-3; // square arcminute inline constexpr double square_arcmin = 0.8461594994075238870742876246233e-7; // square arcsecond inline constexpr double square_arcsec= 0.235044305390978857520635451284e-10; // //#----------------------------- //# Numerical conversion factors //#----------------------------- //# //#---------------------------- //# Physical conversion factors //#---------------------------- //# // Time interval [T]: // // second inline constexpr double second = 1.0; // minute inline constexpr double minute = 60.0; // hour inline constexpr double hour = 3600.0; // day inline constexpr double day = 86400.0; // Difference between Julian Day and Modified Julian Day inline constexpr double MJD0 = 2400000.5; // } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/BasicSL/IComplex.cc000066400000000000000000000027621476623553700175510ustar00rootroot00000000000000//# IComplex.cc: Integer complex numbers //# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ostream& operator<< (ostream& os, const IComplex& val) { os << "(" << val.real() << "," << val.imag() << ")"; return os; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/BasicSL/IComplex.h000066400000000000000000000042021476623553700174020ustar00rootroot00000000000000//# IComplex.h: Integer complex number //# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_ICOMPLEX_H #define CASA_ICOMPLEX_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Integer complex numbers. // // // // // The class IComplex is only a container for FITS usage. // class IComplex { public: //# Constructors // From one or two ints (note for gnu use) // IComplex() : re(0), im(0) {;}; IComplex(Int r) : re(r), im(0) {;}; IComplex(Int r, Int i) : re(r), im(i) {;}; // //# Member functions // For use in FITS classes only // Int real() const { return re; }; Int imag() const { return im; }; // private: Int re; Int im; }; // Show on ostream. ostream &operator<< (ostream &os, const IComplex&); } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/BasicSL/STLIO.h000066400000000000000000000155451476623553700165700ustar00rootroot00000000000000//# STLIO.h: text output IO for any STL-like container //# Copyright (C) 2011 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_STLIO_H #define CASA_STLIO_H //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class AipsIO; template inline ostream& operator<< (ostream& os, const std::pair& p); template inline ostream& operator<<(ostream& os, const std::vector& v); template inline ostream& operator<<(ostream& os, const std::set& v); template inline ostream& operator<<(ostream& os, const std::list& v); template inline ostream& operator<<(ostream& os, const std::map& m); // // Input/output operators for STL-like containers. // // // // // //
      • STL container concept // // // The function showContainer makes it possible to show // any STL-like container (having forward iterators) on an ostream. // This include casacore classes like Array, IPosition, and Block, but // also STL classes like vector. The separator, prefix, and postfix // can be defined at will (they default to , [ ]). // // The function showDataIter is similar to // showContainer, but uses iterators directly. // // // // IPosition shape (3,10,10,3); // showContainer (cout, shape); // // // // Effortless input/output is clearly a big win. // // // // Write out an ascii representation of any container using the // given begin and end iterator. // An arbitrary separator, prefix, and postfix can be given. // E.g. for separator ', ' the output looks like [1, 2, 3]. template void showDataIter (ostream&, ITER begin, const ITER& end, const char* separator=",", const char* prefix="[", const char* postfix="]"); // Write out an ascii representation of any container having a // forward iterator. // Note that a multi-dimensional Array object is linearized. // An arbitrary separator, prefix, and postfix can be given. // E.g. for separator ', ' the output looks like [1, 2, 3]. template void showContainer (ostream& os, const CONTAINER& c, const char* separator=",", const char* prefix="[", const char* postfix="]") { showDataIter (os, c.begin(), c.end(), separator, prefix, postfix); } // Write a std::pair. template inline ostream& operator<< (ostream& os, const std::pair& p) { os << '<' << p.first << ',' << p.second << '>'; return os; } // Write the contents of a vector enclosed in square brackets, using a comma // as separator. template inline ostream& operator<<(ostream& os, const std::vector& v) { showContainer (os, v, ",", "[", "]"); return os; } // Write the contents of a set enclosed in square brackets, using a comma // as separator. template inline ostream& operator<<(ostream& os, const std::set& v) { showContainer (os, v, ",", "[", "]"); return os; } // Write the contents of a list enclosed in square brackets, using a comma // as separator. template inline ostream& operator<<(ostream& os, const std::list& v) { showContainer (os, v, ",", "[", "]"); return os; } // Print the contents of a map enclosed in braces, using a comma // as separator. template inline ostream& operator<<(ostream& os, const std::map& m) { showContainer (os, m, ", ", "{", "}"); return os; } // Print the contents of a container on LogIO. // template inline LogIO& operator<<(LogIO &os, const std::vector &a) { os.output() << a; return os; } template inline LogIO& operator<<(LogIO &os, const std::set &a) { os.output() << a; return os; } template inline LogIO& operator<<(LogIO &os, const std::list &a) { os.output() << a; return os; } template inline LogIO& operator<<(LogIO& os, const std::map& a) { os.output() << a; return os; } // // Read or write the contents of an STL vector from/to AipsIO. // The container is written in the same way as Block, // thus can be read back that way and vice-versa. // template AipsIO& operator>> (AipsIO& ios, std::vector&); template AipsIO& operator<< (AipsIO& ios, const std::vector&); // // Read and write the contents of a map object from/to AipsIO. // It is done in the same way as the old SimpleOrderedMap class, so // persistent SimpleOrderedMap objects in CTDS can be read as std::map // and vice-versa. template AipsIO& operator>> (AipsIO& ios, std::map&); template AipsIO& operator<< (AipsIO& ios, const std::map&); // } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/casa/BasicSL/STLIO.tcc000066400000000000000000000063341476623553700171060ustar00rootroot00000000000000//# STLIO.tcc: text output IO for any STL-like container //# Copyright (C) 2011 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_STLIO_TCC #define CASA_STLIO_TCC //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template void showDataIter (ostream& os, ITER begin, const ITER& end, const char* sep, const char* prefix, const char* postfix) { // Note that the begin iterator is passed by value, so it can be used // directly. os << prefix; if (begin != end) { os << *begin; ++begin; } for (; begin!=end; ++begin) { os << sep << *begin; } os << postfix; } template AipsIO& operator>> (AipsIO& ios, std::vector& v) { ios.getstart ("Block"); uInt nr; ios >> nr; v.resize(nr); getAipsIO(ios, nr, &(v[0])); ios.getend(); return ios; } template AipsIO& operator<< (AipsIO& ios, const std::vector& v) { ios.putstart ("Block", 1); putAipsIO (ios, (uInt)v.size(), &(v[0])); ios.putend(); return ios; } template AipsIO& operator>> (AipsIO& ios, std::map& m) { K key; V val; // Start reading the object. // Delete the current keys and values. ios.getstart ("SimpleOrderedMap"); m.clear(); // Now read in the values and store them into the map. ios >> val; // old default value; ignored uInt nr,ni; ios >> nr; ios >> ni; // old incr; ignored for (uInt i=0; i> key; ios >> val; m.insert (std::make_pair(key,val)); } ios.getend(); return ios; } template AipsIO& operator<< (AipsIO& ios, const std::map& m) { ios.putstart ("SimpleOrderedMap", 1); ios << V(); // old default value; ignored ios << uInt(m.size()); ios << uInt(1); // old incr; ignored for (const auto& x : m) { ios << x.first; ios << x.second; } ios.putend(); return ios; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/BasicSL/STLMath.cc000066400000000000000000000033231476623553700172770ustar00rootroot00000000000000//# STLMath.cc: Math operations on STL-like containers //# Copyright (C) 2014 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN void throwContainerSizes (const char* name, size_t l1, size_t l2) { throw AipsError ("STLMath function " + String(name) + ": container sizes differ ( " + String::toString(l1) + " and " + String::toString(l2) + ')'); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/BasicSL/STLMath.h000066400000000000000000000065641476623553700171530ustar00rootroot00000000000000//# STLMath.h: Math operations on STL-like containers //# Copyright (C) 2014 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_STLMATH_H #define CASA_STLMATH_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Math operations on STL-like containers // // // // // //
      • STL container concept // // // This file defines a few functions with a math operation on a vector. // Others can be added later. // // // Throw an exception that two container sizes mismatch. void throwContainerSizes (const char* name, size_t l1, size_t l2); // Check if the sizes of both containers are the same. template inline void checkContainerSizes (const CONTAINER& left, const CONTAINER& right, const char* name) { if (left.size() != right.size()) { throwContainerSizes (name, left.size(), right.size()); } } // Reverse a Casacore container like IPosition, Block, or Vector. template inline CONTAINER reversedCasaContainer (const CONTAINER& in) { size_t sz = in.size(); CONTAINER out(sz); for (size_t i=0; i std::vector operator+ (const std::vector &left, const std::vector &right) { checkContainerSizes(left, right, "+"); std::vector result(left.size()); std::transform (left.begin(), left.end(), right.begin(), result.begin(), std::plus()); return result; } // Divide a vector by a scalar. template std::vector operator/ (const std::vector &left, const T &right) { std::vector result(left.size()); std::transform (left.begin(), left.end(), result.begin(), [right](T x) { return x / right; }); return result; } // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/BasicSL/String.cc000066400000000000000000000361441476623553700173000ustar00rootroot00000000000000//# String.cc: String class //# Copyright (C) 2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include // for vsnprintf( ) #include // for va_start/end namespace casacore { //# NAMESPACE CASACORE - BEGIN // Special constructors String::String(ostringstream &os) { *this = os.str(); } // Count occurrences Int String::freq(Char c) const { size_type p(0); Int found(0); while (p < length()) { if ((p = find(c, p)) == npos) break; found++; p++; } return found; } Int String::freq(const string &str) const { size_type p(0); Int found(0); while (p < length()) { if ((p = find(str, p)) == npos) break; found++; p++; } return found; } Int String::freq(const Char *s) const { size_type p(0); Int found(0); while (p < length()) { if ((p = find(s, p)) == npos) break; found++; p++; } return found; } Int String::toInt (const String& s, Bool chk) { Int v=0; s.fromString(v, chk); return v; } Float String::toFloat (const String& s, Bool chk) { Float v=0; s.fromString(v, chk); return v; } Double String::toDouble (const String& s, Bool chk) { Double v=0; s.fromString(v, chk); return v; } void String::throwFromStringError() const { throw AipsError ("fromString failure for string '" + *this + "'"); } String String::format (const char* picture, ...) { const int BufferSize = 16384; char buffer [BufferSize]; va_list vaList; va_start (vaList, picture); int nUsed = vsnprintf (buffer, BufferSize, picture, vaList); va_end (vaList); String result = buffer; if (nUsed >= BufferSize){ result += "*TRUNCATED*"; } return result; } void String::trim() { char ws[4]; ws[0] = ' '; ws[1] = '\t'; ws[2] = '\n'; ws[3] = '\r'; trim(ws, 4); } void String::trim(char c[], uInt n) { iterator iter = begin(); while (iter != end() && std::find(c, c+n, *iter) != c+n) { ++iter; } erase (begin(), iter); if (! empty()) { iter = end() - 1; while (iter != begin() && std::find(c, c+n, *iter) != c+n) { --iter; } ++iter; erase (iter, end()); } } void String::ltrim(char c) { iterator iter = begin(); while (iter != end() && *iter ==c) { ++iter; } erase (begin(), iter); } void String::rtrim(char c) { if (! empty()) { iterator iter = end() - 1; while (iter != begin() && *iter == c) { --iter; } ++iter; erase (iter, end()); } } // Obtain a (separate) 'sub'-string SubString String::at(size_type pos, size_type len) { return _substr(pos, len); } SubString String::at(const string &str, Int startpos) { return _substr(index(str, startpos), str.length()); } SubString String::at(const Char *s, Int startpos) { return _substr(index(s, startpos), traits_type::length(s)); } SubString String::at(Char c, Int startpos) { return _substr(index(c, startpos), 1); } SubString String::before(size_type pos) const { return _substr(0, pos); } SubString String::before(const string &str, size_type startpos) const { return _substr(0, index(str, startpos)); } SubString String::before(const Char *s, size_type startpos) const { return _substr(0, index(s, startpos)); } SubString String::before(Char c, size_type startpos) const { return _substr(0, index(c, startpos)); } SubString String::through(size_type pos) { return _substr(0, pos+1); } SubString String::through(const string &str, size_type startpos) { size_type last(index(str, startpos)); if (last != npos) last += str.length(); return _substr(0, last); } SubString String::through(const Char *s, size_type startpos) { size_type last(index(s, startpos)); if (last != npos) last += traits_type::length(s); return _substr(0, last); } SubString String::through(Char c, size_type startpos) { size_type last(index(c, startpos)); if (last != npos) last += 1; return _substr(0, last); } SubString String::from(size_type pos) { return _substr(pos, length()-pos); } SubString String::from(const string &str, size_type startpos) { size_type first(index(str, startpos)); return _substr(first, length()-first); } SubString String::from(const Char *s, size_type startpos) { size_type first(index(s, startpos)); return _substr(first, length()-first); } SubString String::from(Char c, size_type startpos) { size_type first(index(c, startpos)); return _substr(first, length()-first); } SubString String::after(size_type pos) { return _substr(pos+1, length()-(pos+1)); } SubString String::after(const string &str, size_type startpos) { size_type first(index(str, startpos)); if (first != npos) first += str.length(); return _substr(first, length()-first); } SubString String::after(const Char *s, size_type startpos) { size_type first(index(s, startpos)); if (first != npos) first += traits_type::length(s); return _substr(first, length()-first); } SubString String::after(Char c, size_type startpos) { size_type first(index(c, startpos)); if (first != npos) first += 1; return _substr(first, length()-first); } // Prepend string void String::prepend(const string &str) { insert(size_type(0), str); } void String::prepend(const Char *s) { insert(size_type(0), s); } void String::prepend(Char c) { insert(size_type(0), c); } // Delete void String::del(size_type pos, size_type len) { erase(pos, len); } void String::del(const string &str, size_type startpos) { erase(index(str, startpos), str.length()); } void String::del(const Char *s, size_type startpos) { erase(index(s, startpos), traits_type::length(s)); } void String::del(Char c, size_type startpos) { erase(index(c, startpos), 1); } // Global substitution Int String::gsub(const string &pat, const string &repl) { Int nmatches(0); if (length() == 0 || pat.length() == 0 || length() < pat.length()) return nmatches; size_type si(0); Int rl(repl.length()); while (length()-si >= pat.length()) { size_type pos = find(pat, si); if (pos == npos) break; else { nmatches++; replace(pos, pat.length(), repl); si = pos + rl; } } return nmatches; } Int String::gsub(const Char *pat, const string &repl) { return gsub(String(pat), repl); } Int String::gsub(const Char *pat, const Char *repl) { return gsub(String(pat), String(repl)); } // Member utilities void String::reverse() { std::reverse(begin(), end()); } #if defined(AIPS_SUN_NATIVE) Int ToUpper(Int a){return toupper(a);} Int ToLower(Int a){return tolower(a);} #else #define ToUpper toupper #define ToLower tolower #endif void String::upcase() { std::transform(begin(), end(), begin(), ToUpper); } void String::downcase() { std::transform(begin(), end(), begin(), ToLower); } void String::capitalize() { for (iterator p=begin(); p < end(); p++) { Bool at_word; if ((at_word = islower(*p))) *p = ToUpper(*p); else at_word = isupper(*p) || isdigit(*p); if (at_word) { while (++p < end()) { if (isupper(*p)) *p = ToLower(*p); else if (!islower(*p) && !isdigit(*p)) break; } } } } // Regex related functions String::size_type String::find(const Regex &r, size_type pos) const { Int unused; return r.find(c_str(), length(), unused, pos); } Bool String::matches(const string &str, Int pos) const { Bool rstat(False); if (pos < 0) { if (this->index(str,pos) == 0) { rstat = True; /// } else { /// cerr << "No Match " << this->index(str, pos) << endl; } } else { if (length() != 0 && str.length() != 0 && length() == pos+str.length() && static_cast(pos) < length() && index(str, pos) == static_cast(pos)) { rstat = True; } } return rstat; } Bool String::contains(const Regex &r) const { Int unused; return (r.find(c_str(), length(), unused, 0)) != npos; } Bool String::matches(const Regex &r, Int pos) const { String::size_type l = (pos < 0) ? -pos : length() - pos; if (l>length()) return False; if (pos<0) return r.fullMatch(c_str(), l); return r.fullMatch(c_str()+pos, l); } String::size_type String::index(const Regex &r, Int startpos) const { Int unused; return r.search(c_str(), length(), unused, startpos); } SubString String::at(const Regex &r, Int startpos) { Int mlen; size_type first = r.search(c_str(), length(), mlen, startpos); return _substr(first, mlen); } SubString String::before(const Regex &r, size_type startpos) const { Int mlen; size_type first = r.search(c_str(), length(), mlen, startpos); return _substr(0, first); } SubString String::through(const Regex &r, size_type startpos) { Int mlen; size_type first = r.search(c_str(), length(), mlen, startpos); if (first != npos) first += mlen; return _substr(0, first); } SubString String::from(const Regex &r, size_type startpos) { Int mlen; size_type first = r.search(c_str(), length(), mlen, startpos); return _substr(first, length()-first); } SubString String::after(const Regex &r, size_type startpos) { Int mlen; size_type first = r.search(c_str(), length(), mlen, startpos); if (first != npos) first += mlen; return _substr(first, length()-first); } void String::del(const Regex &r, size_type startpos) { Int mlen; size_type first = r.find(c_str(), length(), mlen, startpos); if (mlen > 0) { erase(first, mlen); } } Int String::gsub(const Regex &pat, const string &repl) { Int nmatches(0); if (length() == 0) return nmatches; Int pl; size_type si(0); Int rl(repl.length()); while (length() > si) { size_type pos = pat.find(c_str(), length(), pl, si); if (pos >= npos-1 || pl <= 0) break; else { nmatches++; si = pos + rl; if (pos == 0 && si == 0) { // could be problem with anchor at begin Int pls; size_type ps = pat.find(c_str(), length(), pls, pl); // try for begin if (ps >= npos-1 || pls <= 0) { replace(pos, pl, repl); // finish off if no more (anchored) match break; } } // Continue global substitution replace(pos, pl, repl); } } return nmatches; } // Global functions String reverse(const string& str) { String s(str); std::reverse(s.begin(), s.end()); return s; } String upcase(const string& str) { String s(str); std::transform(s.begin(), s.end(), s.begin(), ToUpper); return s; } String downcase(const string& str) { String s(str); std::transform(s.begin(), s.end(), s.begin(), ToLower); return s; } String capitalize(const string& str) { String s(str); s.capitalize(); return s; } String trim(const string& str) { String s(str); s.trim(); return s; } String replicate(Char c, String::size_type n) { return String(n, c); } String replicate(const string &str, String::size_type n) { String t(str); t.reserve(n*str.length()); while (--n > 0) t += str; return t; } Int split(const string &str, string res[], Int maxn, const string &sep) { Int i(0); String::size_type pos(0); while (i < maxn && pos < str.length()) { String::size_type p = str.find(sep, pos); if (p == String::npos) p = str.length(); res[i] = String(str, pos, p-pos); i++; pos = p + sep.length(); } return i; } Int split(const string &str, string res[], Int maxn, const Regex &sep) { Int i(0); String::size_type pos(0); Int matchlen; while (i < maxn && pos < str.length()) { String::size_type p = sep.find(str.c_str(), str.length(), matchlen, pos); if (p == String::npos) p = str.length(); res[i] = String(str, pos, p-pos); i++; pos = p + matchlen; } return i; } Int split(const string &str, string res[], Int maxn, const Char sep) { return split(str, res, maxn, String(sep)); } String common_prefix(const string &x, const string &y, Int startpos) { if (static_cast(startpos) == String::npos || static_cast(startpos) >= x.length() || static_cast(startpos) >= y.length()) return String(); String::const_iterator xs(x.begin() + startpos); String::const_iterator ys(y.begin() + startpos); String::size_type l(0); while (xs != x.end() && ys != y.end() && *xs++ == *ys++) l++; return String(x, startpos, l); } String common_suffix(const string &x, const string &y, Int startpos) { if (startpos >= 0 || startpos + Int(x.length()) < 0 || startpos + Int(y.length()) < 0) return String(); String::const_iterator xs(x.end() + startpos+1); String::const_iterator ys(y.end() + startpos+1); String::size_type l(0); while (xs != x.begin() && ys != y.begin() && *--xs == *--ys) l++; return String(x, x.length()+startpos+1-l, l); } String join(string src[], Int n, const string& sep) { String x; for (Int i=0; i y.size()) { res = 1; sz = y.size(); } for (string::size_type i=0; i yc) { return 1; } } return res; /// x.downcase(); /// y.downcase(); /// return x.compare(y); } // SubString SubString &SubString::operator=(const SubString &str) { if (this != &str) *this = String(str); return *this; } SubString &SubString::operator=(const String &str) { const_cast(ref_p).replace(pos_p, len_p, str); return *this; } SubString &SubString::operator=(const Char *s) { const_cast(ref_p).replace(pos_p, len_p, s); return *this; } SubString &SubString::operator=(const Char c) { const_cast(ref_p).replace(pos_p, len_p, 1, c); return *this; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/BasicSL/String.h000066400000000000000000001171721476623553700171430ustar00rootroot00000000000000//# String.h: String class //# Copyright (C) 2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_STRING_H #define CASA_STRING_H //# Includes #include //# Includes #include using std::string; #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class String; class Regex; // SubString help class to be used in at, before, ... // // The SubString class can only be used by the String class to be able to // operate the Casacore defined replacement operators at, before, after, // through, from. The class is used transparently in operations like: // // string.at(2,3) = "five"; // // If the SubString starts at a position outside the length of the // original string (like e.g. in after(1000000)), a zero length string is // created (not an exception thrown like in standard string operations). // class SubString { public: //# Friends friend class String; // Make a string operator const string() const { return string(ref_p, pos_p, len_p); } // Default copy constructor. SubString (const SubString&) = default; // Assignment // SubString &operator=(const SubString &str); SubString &operator=(const String &str); SubString &operator=(const Char *s); SubString &operator=(const Char c); // // Get as (const) C array const Char *chars() const; // Obtain length string::size_type length() const { return len_p; } private: //# Constructors // Constructor (there are no public constructors) SubString(const string &str, string::size_type pos, string::size_type len); //# Data // Referenced string const string &ref_p; // Start of sub-string string::size_type pos_p; // Length of sub-string string::size_type len_p; }; // // String: the storage and methods of handling collections of characters. // // // // // //
      • Regex - the regular expressions class //
      • the std string class // // // // The String class name is a continuation of the "C" language custom of // refering to collections of characters as "strings of characters". // // // // The String class is the Casacore implementation of a string class. It is // from the standard library string class, and all operations // and behaviour of strings as defined in the standard are available for // a String. The only difference is the extension with additional functions // in the Casacore String class as compared to the standard string class. // // The String class may be instantiated in many ways: //
          //
        1. A single character - String myChar('C'); //
        2. A Char* argument - String myWord("Yowza"); //
        3. The first n chararcters of a pre-existing string - // String myFoo("fooey", 3); //
        As well as the copy and default constructors and iterator based ones. // // A String may be concatinated with another object (String, or // char*) with either prepending or postpending. A search for the position // of a character within a String may return its position, a Bool that it // is contained within or a Bool confirming your guess at the character's // position is correct. A check of the frequency of occurance of a string // within a String will return the number of occurances. // // Strings may be extracted from Strings at, before, through, from and // after a starting position within the String. Deletion of characters is // possible after a given position within the String. Global substitution // of characters within a String is provided, as well. Splitting of Strings // into a carray of Strings is possible, based upon a given separator // character, with a return value of the number of elements split. The joining // together of the elements of an array of Strings into one String is possible. // // Finally, transformations of case and conversions of type are provided. // // The standard string class provides the following functionality: //
          //
        1. Construction from (part of) String, (part of) Char*, // (repeating) Char, iterator pair. //
        2. Assignment from String, Char*, Char //
        3. Iterators: begin() and end(); rbegin() and rend() (Note: gcc reverse // iterators still weak) //
        4. Capacity: size, length, max_size, resize, capacity, reserve, clear, // empty //
        5. Special size: String::size_type, with indicator: String::npos //
        6. Element access: [pos] and at(pos) (both const and non-const) //
        7. Modifiers: += of String, Char*, Char; append of (part of) String, // Char*, Char and iterator defined; assign() of (part of) // String, Char* and (repeating) Char and iterator; // insertion of same; replacing of same; erase of part of // String; a copy and a swap. //
        8. C-string: get Char* with c_str() or data() and get the relevant // Allocator used (Note: not fully supported in gcc) //
        9. Operations: find, rfind, find_first_of, find_last_of, find_first_not_of, // find_last_not_of; substr (Note only readable substring); // compare with (part of) String, Char* //
        10. Globals: Addition operators for String, Char*, Char; all comparison // operators for String and Char*; getline; input and output // stream operators //
        11. Typedef: All relevant typedefs for standard containers and iterator // handling //
        // The Casacore additions are: //
          //
        1. To standard: some Char function arguments where appropriate; Regex // arguments in search like methods. //
        2. Substring additions: at, before, after, from, through functions taking // search String, Char* as arguments can give (hidden) substrings // which can be assigned (as in at(1,2) = ";") //
        3. Methods: prepend (in addition to standard append); del (as erase); // global substitution of String and patterns; // freq (count of occurance); split/join of strings at separator // or pattern; upcase, downcase, reverse; // common_suffix and _prefix; replicate; case insensitive // compare; creation from stream //
        //
        // // // // // Let's start with a simple string. // String myString("the time"); // // add some more on the end... // myString += " for all good men"; // // prepend some on the front... // myString.prepend("Now is "); // // do some concatination... // String evenMore; // evenMore += myString + " to come to"; // // do some three way concatination // String allKeys, finishIt(" their country."); // allKeys = evenMore + "the aid of" + finishIt; // // find the spot where we put something earlier // String::size_type position = allKeys.index(finishIt); // // find if the word is in the String... // Bool query = myString.contains("good men"); // // ask if the position we think is true is correct... // Bool answer = allKeys.matches(finishIt, position); // // How many spaces are in our phrase? // Int spacesCount = allKeys.freq(" "); // // // // // The String class eases the handling of characters within the Casacore // environment. // // // //
      • if old string disappeared; remove the alloc() call. //
      • add more tests (for string methods) when old String disappears // class String : public string { public: //# Basic container typedefs typedef string::traits_type traits_type; typedef string::value_type value_type; typedef string::allocator_type allocator_type; typedef string::size_type size_type; typedef string::difference_type difference_type; typedef string::reference reference; typedef string::const_reference const_reference; typedef string::pointer pointer; typedef string::const_pointer const_pointer; typedef string::iterator iterator; typedef string::const_iterator const_iterator; typedef string::reverse_iterator reverse_iterator; typedef string::const_reverse_iterator const_reverse_iterator; //# Next cast necessary to stop warning in gcc static const size_type npos = static_cast(-1); //# Constructors // Default constructor String() : string("") {} // Construct from std string // Construct from (part of) other string: acts as copy constructor // //
      • out_of_range if pos > str.size() // String(const string& str, size_type pos=0, size_type n=npos) : string(str, pos, n) {} // Construct from char* with given length // //
      • length_error if n == npos // String(const Char* s, size_type n) : string(s, n) {} // Construct from char array String(const Char* s) : string(s) {} // Construct from a single char (repeated n times) // //
      • length_error if n == npos // String(size_type n, Char c) : string(n, c) {} // Construct from iterator template String(InputIterator begin, InputIterator end) : string(begin, end) {} // From single char (** Casacore addition). // Note that there is no automatic Char-to-String // conversion available. This stops inadvertent conversions of // integer to string. explicit String(Char c) : string(1, c) {} // Construct from a SubString String(const SubString &str) : string(str.ref_p, str.pos_p, str.len_p) {} // Construct from a stream. String(ostringstream &os); //# Destructor // Destructor ~String() {} //# Operators // Assignments (they are all deep copies according to standard) // String& operator=(const string& str) { return static_cast(string::operator=(str)); } String& operator=(const SubString &str) { return (*this = String(str)); } String& operator=(const Char* s) { return static_cast(string::operator=(s)); } String& operator=(Char c) { return static_cast(string::operator=(c)); } // // ** Casacore addition: synonym for at(pos, len) SubString operator()(size_type pos, size_type len); // Concatenate // String& operator+=(const string& str) { return static_cast(string::operator+=(str)); } String& operator+=(const Char* s) { return static_cast(string::operator+=(s)); } String& operator+=(Char c) { return static_cast(string::operator+=(c)); } // // Indexing. The standard version is undefined if pos > size(), or // pos >= size() for non-const version. // The const_reference version needs the at() version // for the gcc compiler: no const [] exists. // const_reference operator[](size_type pos) const { return string::at(pos); } reference operator[](size_type pos) { return string::operator[](pos); } // *** Casacore addition // const_reference elem(size_type pos) const { return string::at(pos); } Char firstchar() const { return at(static_cast(0)); } Char lastchar() const { return at(length()-1); } // // //# Member functions // Iterators // iterator begin() { return string::begin(); } const_iterator begin() const { return string::begin(); } iterator end() { return string::end(); } const_iterator end() const { return string::end(); } reverse_iterator rbegin() { return string::rbegin(); } const_reverse_iterator rbegin() const { return string::rbegin(); } reverse_iterator rend() { return string::rend(); } const_reverse_iterator rend() const { return string::rend(); } // // Capacity, size // size_type size() const { return string::size(); } size_type length() const { return string::length(); } size_type max_size() const { return string::max_size(); } size_type capacity() const { return string::capacity(); } // ** Casacore addition -- works as a capacity(n) -- Note Int Int allocation() const { return string::capacity(); } // // Resize by truncating or extending with copies of c (default // Char()) // //
      • length_error if n > max_size() //
      • length_error if res_arg > max_size() // // // The reserve length given is non-binding on the // implementation String& resize(size_type n) { string::resize(n); return *this; } String& resize(size_type n, Char c) { string::resize(n, c); return *this; } String& reserve(size_type res_arg = 0) { string::reserve(res_arg); return *this; } // ** Casacore addition -- works as a resize(n) void alloc(size_type n) { string::resize(n); } // // Clear the string // clear() executed as erase() due to missing clear() in // gcc void clear() { string::erase(begin(), end()); } // Test for empty Bool empty() const { return string::empty(); } // Addressing // //
      • out_of_range if pos >= size() // // const_reference at(size_type n) const { return string::at(n); } reference at(size_type n) { return string::at(n); } // // Append // //
      • out_of_range if pos > str.size() //
      • length_error if new size() >= npos // // The standard has a // void push_back(const Char) which is completely undefined. It // probably is a remnant of the full list of container functions pop/push // back/front. // String& append(const string& str) { return static_cast(string::append(str)); } String& append(const string& str, size_type pos, size_type n) { return static_cast(string::append(str, pos, n)); } String& append(const Char* s, size_type n) { return static_cast(string::append(s, n)); } String& append(const Char* s) { return static_cast(string::append(s)); } String& append(size_type n, Char c) { return static_cast(string::append(n, c)); } template String& append(InputIterator first, InputIterator last) { return static_cast(string::append(first, last)); } // ** Casacore addition String& append(Char c) { return static_cast(string::append(1, c)); } // // Assign // //
      • out_of_range if pos > str.size() // // String& assign(const string& str) { return static_cast(string::assign(str)); } String& assign(const string& str, size_type pos, size_type n) { return static_cast(string::assign(str, pos, n)); } String& assign(const Char* s, size_type n) { return static_cast(string::assign(s, n)); } String& assign(const Char* s) { return static_cast(string::assign(s)); } String& assign(size_type n, Char c) { return static_cast(string::assign(n, c)); } template String& assign(InputIterator first, InputIterator last) { return static_cast(string::assign(first, last)); } // ** Casacore addition String& assign(Char c) { return static_cast(string::assign(1, c)); } // // Insert // //
      • out_of_range if pos1 > str.size() or pos2 > str.size() //
      • length_error if new size() >= npos // // String& insert(size_type pos1, const string& str) { return static_cast(string::insert(pos1, str)); } String& insert(size_type pos1, const string& str, size_type pos2, size_type n) { return static_cast(string::insert(pos1, str, pos2, n)); } String& insert(size_type pos, const Char* s, size_type n) { return static_cast(string::insert(pos, s, n)); } String& insert(size_type pos, const Char* s) { return static_cast(string::insert(pos, s)); } String& insert(size_type pos, size_type n, Char c) { return static_cast(string::insert(pos, n, c)); } // ** Casacore addition String& insert(size_type pos, Char c) { return static_cast(string::insert(pos, 1, c)); } iterator insert(iterator p, Char c) { return string::insert(p, c); } void insert(iterator p, size_type n, Char c) { string::insert(p, n, c); } template void insert(iterator p, InputIterator first, InputIterator last) { string::insert(p, first, last); } // ** Casacore additions // String& insert(iterator p, const string& str) { return static_cast(string::insert(p-begin(), str)); } String& insert(iterator p, const Char* s, size_type n) { return static_cast(string::insert(p-begin(), s, n)); } String& insert(iterator p, const Char* s) { return static_cast(string::insert(p-begin(), s)); } // // // Compare. Returns 0 if strings equal and of equal size; else positive if // str larger or longer; else negative. // The gcc compiler does not have the proper standard // compare functions. Hence they are locally implemented. // Int compare(const string& str) const { return string::compare(str); } Int compare(size_type pos1, size_type n1, const string& str) const { return String(*this, pos1, n1).compare(str); } Int compare(size_type pos1, size_type n1, const string& str, size_type pos2, size_type n2) const { return String(*this, pos1, n1).compare(String(str, pos2, n2)); } Int compare(const Char* s) const { return string::compare(s); } Int compare(size_type pos1, size_type n1, const Char* s, size_type n2=npos) const { return String(*this, pos1, n1).compare(String(s, n2)); } // // Erase // String& erase(size_type pos, size_type n = npos) { return static_cast(string::erase(pos, n)); } iterator erase(iterator position) { return string::erase(position); } iterator erase(iterator first, iterator last) { return string::erase(first, last); } // // Replace // //
      • out_of_range if pos1 > str.size() or pos2 > str.size() //
      • length_error if new size() > npos // // String& replace(size_type pos1, size_type n1, const string& str) { return static_cast(string::replace(pos1, n1, str)); } String& replace(size_type pos1, size_type n1, const string& str, size_type pos2, size_type n2) { return static_cast(string::replace(pos1, n1, str, pos2, n2)); } String& replace(size_type pos, size_type n1, const Char* s, size_type n2) { return static_cast(string::replace(pos, n1, s, n2)); } String& replace(size_type pos, size_type n1, const Char* s) { return static_cast(string::replace(pos, n1, s)); } String& replace(size_type pos, size_type n1, size_type n2, Char c) { return static_cast(string::replace(pos, n1, n2, c)); } // ** Casacore addition String& replace(size_type pos, size_type n1, Char c) { return static_cast(string::replace(pos, n1, 1, c)); } String& replace(iterator i1, iterator i2, const string& str) { return static_cast(string::replace(i1, i2, str)); } String& replace(iterator i1, iterator i2, const Char* s, size_type n) { return static_cast(string::replace(i1, i2, s, n)); } String& replace(iterator i1, iterator i2, const Char* s) { return static_cast(string::replace(i1, i2, s)); } String& replace(iterator i1, iterator i2, size_type n, Char c) { return static_cast(string::replace(i1, i2, n, c)); } // ** Casacore addition String& replace(iterator i1, iterator i2, Char c) { return static_cast(string::replace(i1, i2, 1, c)); } template String& replace(iterator i1, iterator i2, InputIterator j1, InputIterator j2) { return static_cast(string::replace(i1, i2, j1, j2)); } // // Copy // //
      • out_of_range if pos > size() // size_type copy(Char* s, size_type n, size_type pos = 0) const { return string::copy(s, n, pos); } // Swap void swap(string& s) { string::swap(s); } // Get char array // // As a proper null terminated C-string const Char *c_str() const { return string::c_str(); } // As pointer to char array const Char *data() const { return string::data(); } // ** Casacore synonym const Char *chars() const { return string::c_str(); } // // Get allocator used // gcc has no get_allocator() allocator_type get_allocator() const { return string::allocator_type(); } // Get a sub string // //
      • out_of_range if pos > size() // String substr(size_type pos=0, size_type n=npos) const { return String(*this, pos, n); } // Create a formatted string using the given printf format string. static String format (const char* picture, ...); // Convert a String to a value. All characters in the string must be used. // It uses a shift from an ostringstream, so that operator must exist // for the data type used. //
        In case of an error, an exception is thrown if chk is set. // Otherwise it returns False and value contains the value read // so far. // template inline Bool fromString (T& value, Bool chk=True) const { std::istringstream os(*this); os >> value; if (os.fail() || !os.eof()) { if (chk) throwFromStringError(); return False; } return True; } template inline T fromString() const { T value; fromString(value); return value; } // // Convert a string to an Int, Float or Double. //
        In case of an error, an exception is thrown if chk is set. // Otherwise the value read so far is returned (0 if nothing read). // static Int toInt (const String& s, Bool chk=False); static Float toFloat (const String& s, Bool chk=False); static Double toDouble (const String& s, Bool chk=False); // // Convert a value to a String. // It uses a shift into an ostringstream, so that operator must be // defined for the data type used. template static String toString(const T& value) { std::ostringstream os; os << value; return os.str(); } // Remove beginning and ending whitespace. void trim(); // Remove specified chars from beginning and end of string. void trim(char c[], uInt n); // Remove specified character from beginning of string. // If the character is repeated more than once on the left, all instances // will be removed; e.g. ltrim(',') results in ",,xy" becoming "xy". void ltrim(char c); // Remove specified character from end of string. // If the character is repeated more than once on the right, all instances // will be removed; e.g. rtrim(',') results in "xy,," becoming "xy". void rtrim(char c); // Does the string start with the specified string? Bool startsWith(const string& beginString) const { return find(beginString) == 0; } // Search functions. Returns either npos (if not found); else position. // The Regex ones are ** Casacore additions // size_type find(const string &str, size_type pos=0) const { return string::find(str, pos); } size_type find(const Char *s, size_type pos=0) const { return string::find(s, pos); } size_type find(const Char *s, size_type pos, size_type n) const { return string::find(s, pos, n); } size_type find(Char c, size_type pos=0) const { return string::find(c, pos); } size_type find(const Regex &r, size_type pos=0) const; size_type rfind(const string &str, size_type pos=npos) const { return string::rfind(str, pos); } size_type rfind(const Char *s, size_type pos=npos) const { return string::rfind(s, pos); } size_type rfind(const Char *s, size_type pos, size_type n) const { return string::rfind(s, pos, n); } size_type rfind(Char c, size_type pos=npos) const { return string::rfind(c, pos); } size_type find_first_of(const string &str, size_type pos=0) const { return string::find_first_of(str, pos); } size_type find_first_of(const Char *s, size_type pos=0) const { return string::find_first_of(s, pos); } size_type find_first_of(const Char *s, size_type pos, size_type n) const { return string::find_first_of(s, pos, n); } size_type find_first_of(Char c, size_type pos=0) const { return string::find_first_of(c, pos); } size_type find_last_of(const string &str, size_type pos=npos) const { return string::find_last_of(str, pos); } size_type find_last_of(const Char *s, size_type pos=npos) const { return string::find_last_of(s, pos); } size_type find_last_of(const Char *s, size_type pos, size_type n) const { return string::find_last_of(s, pos, n); } size_type find_last_of(Char c, size_type pos=npos) const { return string::find_last_of(c, pos); } size_type find_first_not_of(const string &str, size_type pos=0) const { return string::find_first_not_of(str, pos); } size_type find_first_not_of(const Char *s, size_type pos=0) const { return string::find_first_not_of(s, pos); } size_type find_first_not_of(const Char *s, size_type pos, size_type n) const { return string::find_first_not_of(s, pos, n); } size_type find_first_not_of(Char c, size_type pos=0) const { return string::find_first_not_of(c, pos); } size_type find_last_not_of(const string &str, size_type pos=npos) const { return string::find_last_not_of(str, pos); } size_type find_last_not_of(const Char *s, size_type pos=npos) const { return string::find_last_not_of(s, pos); } size_type find_last_not_of(const Char *s, size_type pos, size_type n) const { return string::find_last_not_of(s, pos, n); } size_type find_last_not_of(Char c, size_type pos=npos) const { return string::find_last_not_of(c, pos); } // // Containment. ** Casacore addition // Bool contains(Char c) const { return (find(c) != npos); } Bool contains(const string &str) const { return (find(str) != npos); } Bool contains(const Char *s) const { return (find(s) != npos); } Bool contains(const Regex &r) const; // // Does the string starting at the given position contain the given substring? // If the position is negative, it is counted from the end. // ** Casacore addition // Bool contains(Char c, Int pos) const; Bool contains(const string &str, Int pos) const; Bool contains(const Char *s, Int pos) const; Bool contains(const Regex &r, Int pos) const; // // Matches entire string from pos // (or till pos if negative pos). ** Casacore addition // Bool matches(const string &str, Int pos = 0) const; Bool matches(Char c, Int pos = 0) const { return matches(String(c), pos); }; Bool matches(const Char *s, Int pos = 0) const { return matches(String(s), pos); }; Bool matches(const Regex &r, Int pos = 0) const; // // Concatenate by prepending the argument onto String. ** Casacore addition // void prepend(const string &str); void prepend(const Char *s); void prepend(Char c); // // Return the position of the target in the string or npos for failure. // ** Casacore addition // size_type index(Char c, Int startpos = 0) const { return ((startpos >= 0) ? find(c, startpos) : rfind(c, length() + startpos - 1)); } size_type index(const string &str, Int startpos = 0) const { return ((startpos >= 0) ? find(str, startpos) : rfind(str, length() + startpos - str.length())); } size_type index(const Char *s, Int startpos = 0) const { return ((startpos >= 0) ? find(s, startpos) : rfind(s, length() + startpos - traits_type::length(s))); } size_type index(const Regex &r, Int startpos = 0) const; // // Return the number of occurences of target in String. ** Casacore addition // Int freq(Char c) const; Int freq(const string &str) const; Int freq(const Char *s) const; // // Extract the string "at" the argument's position. ** Casacore addition // SubString at(size_type pos, size_type len); String at(size_type pos, size_type len) const { return String(*this, pos, len); } SubString at(const string &str, Int startpos = 0); String at(const string &str, Int startpos = 0) const; SubString at(const Char *s, Int startpos = 0); String at(const Char *s, Int startpos = 0) const; SubString at(Char c, Int startpos = 0); String at(Char c, Int startpos = 0) const; SubString at(const Regex &r, Int startpos = 0); String at(const Regex &r, Int startpos = 0) const; // Next ones for overloading reasons. // It is better to use the substr() method // in stead. // SubString at(Int pos, Int len) { return at(static_cast(pos), static_cast(len)); }; String at(Int pos, Int len) const { return at(static_cast(pos), static_cast(len)); }; SubString at(Int pos, size_type len) { return at(static_cast(pos), len); }; String at(Int pos, size_type len) const { return at(static_cast(pos), len); }; // // // Start at startpos and extract the string "before" the argument's // position, exclusive. ** Casacore addition // SubString before(size_type pos) const; SubString before(const string &str, size_type startpos = 0) const; SubString before(const Char *s, size_type startpos = 0) const; SubString before(Char c, size_type startpos = 0) const; SubString before(const Regex &r, size_type startpos = 0) const; // Next one for overloading reasons SubString before(Int pos) const { return before(static_cast(pos)); }; // // Start at startpos and extract the SubString "through" to the argument's // position, inclusive. ** Casacore addition // SubString through(size_type pos); SubString through(const string &str, size_type startpos = 0); SubString through(const Char *s, size_type startpos = 0); SubString through(Char c, size_type startpos = 0); SubString through(const Regex &r, size_type startpos = 0); // Next one for overloading reasons SubString through(Int pos) { return through(static_cast(pos)); } // // Start at startpos and extract the SubString "from" the argument's // position, inclusive, to the String's end. ** Casacore addition // SubString from(size_type pos); SubString from(const string &str, size_type startpos = 0); SubString from(const Char *s, size_type startpos = 0); SubString from(Char c, size_type startpos = 0); SubString from(const Regex &r, size_type startpos = 0); // Next one for overloading reasons SubString from(Int pos) { return from(static_cast(pos)); }; // // Start at startpos and extract the SubString "after" the argument's // position, exclusive, to the String's end. ** Casacore addition // SubString after(size_type pos); SubString after(const string &str, size_type startpos = 0); SubString after(const Char *s, size_type startpos = 0); SubString after(Char c, size_type startpos = 0); SubString after(const Regex &r, size_type startpos = 0); // Next one for overloading reasons SubString after(Int pos) { return after(static_cast(pos)); }; // // Maybe forget some. ** Casacore addition // // internal transformation to reverse order of String. void reverse(); // internal transformation to capitalization of String. void capitalize(); // internal transformation to uppercase of String void upcase(); // internal transformation to lowercase of String void downcase(); // // Delete len chars starting at pos. ** Casacore addition void del(size_type pos, size_type len); // Delete the first occurrence of target after startpos. ** Casacore addition // void del(const string &str, size_type startpos = 0); void del(const Char *s, size_type startpos = 0); void del(Char c, size_type startpos = 0); void del(const Regex &r, size_type startpos = 0); // Overload problem void del(Int pos, Int len) { del(static_cast(pos), static_cast(len)); } // // Global substitution: substitute all occurrences of pat with repl, and // return the number of replacements. // ** Casacore addition // Int gsub(const string &pat, const string &repl); Int gsub(const Char *pat, const string &repl); Int gsub(const Char *pat, const Char *repl); Int gsub(const Regex &pat, const string &repl); // private: // Helper functions for at, before etc // SubString _substr(size_type first, size_type l) const { return SubString(*this, first, l); } // // Helper function for fromString. void throwFromStringError() const; }; // // Global concatenation operators // // The global concatenation operators // inline String operator+(const String &lhs, const String &rhs) { String str(lhs); str.append(rhs); return str; } inline String operator+(const Char *lhs, const String &rhs) { String str(lhs); str.append(rhs); return str; } inline String operator+(Char lhs, const String &rhs) { String str(lhs); str.append(rhs); return str; } inline String operator+(const String &lhs, const Char *rhs) { String str(lhs); str.append(rhs); return str; } inline String operator+(const String &lhs, Char rhs) { String str(lhs); str.append(rhs); return str; } // // // Global comparison operators // // The global comparison operators // inline Bool operator==(const String &x, const String &y) { return x.compare(y) == 0; } inline Bool operator!=(const String &x, const String &y) { return x.compare(y) != 0; } inline Bool operator>(const String &x, const String &y) { return x.compare(y) > 0; } inline Bool operator>=(const String &x, const String &y) { return x.compare(y) >= 0; } inline Bool operator<(const String &x, const String &y) { return x.compare(y) < 0; } inline Bool operator<=(const String &x, const String &y) { return x.compare(y) <= 0; } inline Bool operator==(const String &x, const Char *t) { return x.compare(t) == 0; } inline Bool operator!=(const String &x, const Char *t) { return x.compare(t) != 0; } inline Bool operator>(const String &x, const Char *t) { return x.compare(t) > 0; } inline Bool operator>=(const String &x, const Char *t) { return x.compare(t) >= 0; } inline Bool operator<(const String &x, const Char *t) { return x.compare(t) < 0; } inline Bool operator<=(const String &x, const Char *t) { return x.compare(t) <= 0; } inline Bool operator==(const String &x, const Char t) { return x.compare(String(t)) == 0; } inline Bool operator!=(const String &x, const Char t) { return x.compare(String(t)) != 0; } inline Bool operator>(const String &x, const Char t) { return x.compare(String(t)) > 0; } inline Bool operator>=(const String &x, const Char t) { return x.compare(String(t)) >= 0; } inline Bool operator<(const String &x, const Char t) { return x.compare(String(t)) < 0; } inline Bool operator<=(const String &x, const Char t) { return x.compare(String(t)) <= 0; } // ** Casacore additions of global compares. Returns 0 if equal; lt or gt 0 if // strings unequal or of unequal lengths. // inline Int compare(const string &x, const string &y) { return x.compare(y); } inline Int compare(const string &x, const Char *y) { return x.compare(y); } inline Int compare(const string &x, const Char y) { return x.compare(String(y)); } // this version ignores case. ** Casacore addition. Result is 0 if equal // strings of equal lengths; else lt or gt 0 to indicate differences. Int fcompare(const String& x, const String& y); // // // Splitting // Global function which splits the String into string array res at separator // and returns the number of elements. ** Casacore addition // Int split(const string &str, string res[], Int maxn, const string &sep); Int split(const string &str, string res[], Int maxn, const Char sep); Int split(const string &str, string res[], Int maxn, const Regex &sep); // // Some general functions // Functions to find special patterns, join and replicate // String common_prefix(const string &x, const string &y, Int startpos = 0); String common_suffix(const string &x, const string &y, Int startpos = -1); String replicate(Char c, String::size_type n); String replicate(const string &str, String::size_type n); String join(string src[], Int n, const string &sep); // // Casing and related functions // Case conversion and rearrangement functions // // Global function which returns a transformation to reverse order of String. String reverse(const string& str); // Global function which returns a transformation to uppercase of String. String upcase(const string& str); // Global function which returns a transformation to lowercase of String. String downcase(const string& str); // Global function which returns a transformation to capitalization of // String. String capitalize(const string& str); // Global function which removes leading and trailing whitespace. String trim(const string& str); // // IO // // Output ostream &operator<<(ostream &s, const String &x); // //# Inlines inline SubString::SubString(const string &str, string::size_type pos, string::size_type len) : ref_p(str), pos_p((pos > str.length()) ? str.length() : pos), len_p((len == string::npos || pos_p+len > str.length()) ? str.length()-pos_p : len) {} inline SubString String::operator()(size_type pos, size_type len) { return at(pos, len); } inline const Char *SubString::chars() const { return String(*this).c_str(); } inline Bool String::contains(Char c, Int pos) const { return (index(c, pos) != npos); } inline Bool String::contains(const string &str, Int pos) const { return (index(str, pos) != npos); } inline Bool String::contains(const Char *s, Int pos) const { return (index(s, pos) != npos); } inline Bool String::contains(const Regex &r, Int pos) const { return (index(r, pos) != npos); } inline ostream &operator<<(ostream &s, const String &x) { s << x.c_str(); return s; } } //# NAMESPACE CASACORE - END // Define the hash function for String, so unordered_set can be used. namespace std { template<> struct hash { std::size_t operator()(casacore::String const& k) const noexcept { return std::hash()(k); } }; } #endif casacore-3.7.1/casa/BasicSL/test/000077500000000000000000000000001476623553700164725ustar00rootroot00000000000000casacore-3.7.1/casa/BasicSL/test/CMakeLists.txt000066400000000000000000000005021476623553700212270ustar00rootroot00000000000000set (tests tComplex tConstants tSTLIO tSTLMath tString ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_casa) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/casa/BasicSL/test/tComplex.cc000066400000000000000000000301751476623553700206020ustar00rootroot00000000000000//# tComplex.cc: This program tests the Complex class //# Copyright (C) 1993,1994,1995,1996,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #define TESTOP(NAME, Z, FAILVAR) \ if(!near(casacore::NAME(Z), std::NAME(Z), 1e-5)) { \ cout << "for z=" << Z << ", casacore::" #NAME "(z)=" << casacore::NAME(Z) << ", std::" #NAME "(z)=" << std::NAME(Z) << '\n'; \ FAILVAR = true; \ } int main() { { DComplex z(0.7, -0.3); bool fail = false; TESTOP(tan, z, fail); TESTOP(sin, z, fail); TESTOP(cos, z, fail); TESTOP(atan, z, fail); TESTOP(asin, z, fail); TESTOP(acos, z, fail); TESTOP(tanh, z, fail); TESTOP(sinh, z, fail); TESTOP(cosh, z, fail); TESTOP(sqrt, z, fail); AlwaysAssert(!fail, AipsError); } Complex f1(23.9,1.8), f2(9.2,8.2), f3(2.7,1.8), fo(237.561,0.9312), fi; IComplex i1(5,2), i3(Int(f1.real()),Int(f1.imag())); DComplex d1, d2(f1.real(),f1.imag()), d3(0.921,7.812); fstream fio("tComplex_tmp.data", ios::out | ios::trunc); cout << "Initial value for complex: " << d1 << endl; cout << d1 << " :" << endl; cout << " real part: " << d1.real() << " == " << real(d1) << endl; cout << " imaginary part: " << d1.imag() << " == " << imag(d1) << endl; cout << f1 << " == " << d2 << " ~= " << i3 << endl; cout << "-" << d3 << " == " << -d3 << endl; cout << "conj(" << d3 << ") == " << conj(d3) << endl; cout << "norm(" << d3 << ") == " << norm(d3) << endl; cout << "arg(" << d3 << ") == " << arg(d3) << endl; d1 = DComplex(18.9, -2.31); cout << "fabs(" << d1 << ") == " << fabs(d1) << endl; fio << fo << endl; fio.close(); fio.open("tComplex_tmp.data", ios::in); fio >> fi; fio.close(); unlink("tComplex_tmp.data"); cout << "out: " << fo << " in: " << fi << endl; d1 = DComplex(i1.real(), i1.imag()); cout << d1 << " == " << i1 << endl; cout << "- - - - - - - - - - - - - - - - - - - -" << endl; cout << f1 << " + " << f2 << " = " << f1 + f2 << endl; cout << f1 << " * " << f2 << " = " << f1 * f2 << endl; cout << f1 << " - " << f2 << " = " << f1 - f2 << endl; cout << f1 << " / " << f2 << " = " << f1 / f2 << endl; cout << endl; cout << "- - - - - - - - - - - - - - - - - - - -" << endl; cout << f1 << " + " << 7.561 << " = " << f1 + 7.561f << endl; cout << f1 << " * " << 7.561 << " = " << f1 * 7.561 << endl; cout << f1 << " - " << 7.561 << " = " << f1 - 7.561f << endl; cout << f1 << " / " << 7.561 << " = " << f1 / 7.561 << endl; cout << endl; cout << "- - - - - - - - - - - - - - - - - - - -" << endl; cout << f1 << " + " << 13 << " = " << f1 + 13.0f << endl; cout << f1 << " * " << "13.0f" << " = " << f1 * 13.0f << endl; cout << f1 << " * " << "13" << " = " << f1 * 13 << endl; cout << f1 << " * " << "13." << " = " << f1 * 13. << endl; cout << "13.0f" << " * " << f1 << " = " << 13.0f * f1 << endl; cout << "13" << " * " << f1 << " = " << 13 * f1 << endl; cout << "13." << " * " << f1 << " = " << 13. * f1 << endl; cout << f1 << " - " << 13 << " = " << f1 - 13.0f << endl; cout << f1 << " / " << "13.0f" << " = " << f1 / 13.0f << endl; cout << f1 << " / " << "13" << " = " << f1 / 13 << endl; cout << f1 << " / " << "13." << " = " << f1 / 13. << endl; cout << "13.0f" << " / " << f1 << " = " << 13.0f / f1 << endl; cout << "13" << " / " << f1 << " = " << 13 / f1 << endl; cout << "13." << " / " << f1 << " = " << 13. / f1 << endl; cout << endl; cout << "- - - - - - - - - - - - - - - - - - - -" << endl; cout << f1 << " += 4 -> "; cout << (f1 += 4) << endl; cout << f1 << " *= 4 -> "; cout << (f1 *= 4) << endl; cout << f1 << " -= 4 -> "; cout << (f1 -= 4) << endl; cout << f1 << " /= 4 -> "; cout << (f1 /= 4) << endl; cout << endl; cout << "- - - - - - - - - - - - - - - - - - - -" << endl; cout << f1 << " += 0.896 -> "; cout << (f1 += 0.896) << endl; cout << f1 << " *= 0.896 -> "; cout << (f1 *= 0.896) << endl; cout << f1 << " -= 0.896 -> "; cout << (f1 -= 0.896) << endl; cout << f1 << " /= 0.896 -> "; cout << (f1 /= 0.896) << endl; cout << endl; char simChar = 2; cout << "- - - - - - - - - - - - - - - - - - - -" << endl; cout << f1 << " += simChar -> "; cout << (f1 += simChar) << endl; cout << f1 << " *= simChar -> "; cout << (f1 *= simChar) << endl; cout << f1 << " -= simChar -> "; cout << (f1 -= simChar) << endl; cout << f1 << " /= simChar -> "; cout << (f1 /= simChar) << endl; cout << endl; cout << "- - - - - - - - - - - - - - - - - - - -" << endl; cout << f1 << " += " << f3 << " -> "; cout << (f1 += f3) << endl; cout << f1 << " *= " << f3 << " -> "; cout << (f1 *= f3) << endl; cout << f1 << " -= " << f3 << " -> "; cout << (f1 -= f3) << endl; cout << f1 << " /= " << f3 << " -> "; cout << (f1 /= f3) << endl; cout << endl; cout << "- - - - - - - - - - - - - - - - - - - -" << endl; Complex d3c = Complex(d3.real(), d3.imag()); cout << f1 << " += " << d3 << " -> "; cout << (f1 += d3c) << endl; cout << f1 << " *= " << d3 << " -> "; cout << (f1 *= d3c) << endl; cout << f1 << " -= " << d3 << " -> "; cout << (f1 -= d3c) << endl; cout << f1 << " /= " << d3 << " -> "; cout << (f1 /= d3c) << endl; cout << endl; i1 = IComplex(36, 14); cout << "- - - - - - - - - - - - - - - - - - - -" << endl; // reset f1 to nominal values at it will have been corrupted (due to // roundoff) by all the earlier calculations. f1 = Complex(33.417, 13.412); cout << "sin(" << f1 << ") = " << sin(f1) << endl; cout << "cos(" << f1 << ") = " << cos(f1) << endl; cout << "sinh(" << f1 << ") = " << sinh(f1) << endl; cout << "cosh(" << f1 << ") = " << cosh(f1) << endl; cout << endl; cout << "- - - - - - - - - - - - - - - - - - - -" << endl; cout << "log(" << f1 << ") = " << log(f1) << endl; cout << "log10(" << f1 << ") = " << log10(f1) << endl; cout << "exp(" << f1 << ") = " << exp(f1) << endl; cout << endl; cout << "- - - - - - - - - - - - - - - - - - - -" << endl; if (! near (pow(f1,3.0f), Complex(19283.3,42518.8))) { cout << "pow(" << f1 << ",3) = " << pow(f1,3.0f) << endl; } if (! near (pow(f1,-8.0f), Complex(-3.52463e-13,-3.11743e-14))) { cout << "pow(" << f1 << ",-8) = " << pow(f1,-8.0f) << endl; } if (! near (pow(f1,0.214f), Complex(2.14595,0.175667))) { cout << "pow(" << f1 << ",0.214) = " << pow(f1,0.214f) << endl; } cout << "pow(" << f1 << "," << i1 << " = " << pow(f1,Complex(i1.real(), i1.imag())) << endl; if (! near (pow(f1,f2), Complex(8.06899e+11,9.07712e+12))) { cout << "pow(" << f1 << "," << f2 << " = " << pow(f1,f2) << endl; } cout << endl; cout << "- - - - - - - - - - - - - - - - - - - -" << endl; cout << "sqrt(" << f1 << ") = " << sqrt(f1) << endl; cout << endl; cout << "- - - - - - - - - - - - - - - - - - - -" << endl; cout << f1 << " == " << f2 << " -> " << (f1 == f2) << endl; cout << f1 << " != " << f2 << " -> " << (f1 != f2) << endl; cout << f1 << " >= " << f2 << " -> " << (f1 >= f2) << endl; cout << f1 << " > " << f2 << " -> " << (f1 > f2) << endl; cout << f1 << " <= " << f2 << " -> " << (f1 <= f2) << endl; cout << f1 << " < " << f2 << " -> " << (f1 < f2) << endl; cout << endl; cout << "- - - - - - - - - - - - - - - - - - - -" << endl; cout << f2 << " == " << f1 << " -> " << (f2 == f1) << endl; cout << f2 << " != " << f1 << " -> " << (f2 != f1) << endl; cout << f2 << " >= " << f1 << " -> " << (f2 >= f1) << endl; cout << f2 << " > " << f1 << " -> " << (f2 > f1) << endl; cout << f2 << " <= " << f1 << " -> " << (f2 <= f1) << endl; cout << f2 << " < " << f1 << " -> " << (f2 < f1) << endl; cout << endl; cout << "- - - - - - - - - - - - - - - - - - - -" << endl; cout << f1 << " == " << f1 << " -> " << (f1 == f1) << endl; cout << f1 << " != " << f1 << " -> " << (f1 != f1) << endl; cout << f1 << " >= " << f1 << " -> " << (f1 >= f1) << endl; cout << f1 << " > " << f1 << " -> " << (f1 > f1) << endl; cout << f1 << " <= " << f1 << " -> " << (f1 <= f1) << endl; cout << f1 << " < " << f1 << " -> " << (f1 < f1) << endl; cout << endl; f1 = Complex(33.0, 6.0); f1 = 33.021; f1 = 0; cout << "- - - - - - - - - - - - - - - - - - - -" << endl; AlwaysAssertExit(near(Complex(0,10000), Complex(0,10001), 1.01e-4)); AlwaysAssertExit(!near(Complex(0,10000), Complex(0,10001), 0.99e-4)); AlwaysAssertExit(!near(Complex(10000,0), Complex(0,10001), 1.01e-4)); AlwaysAssertExit(nearAbs(Complex(0,10000), Complex(0,10001), 1.01)); AlwaysAssertExit(!nearAbs(Complex(0,10000), Complex(0,10001), 0.99)); AlwaysAssertExit(!nearAbs(Complex(10000,0), Complex(0,10001), 1.01)); AlwaysAssertExit(allNear(Complex(0,10000), Complex(0,10001), 1.01e-4)); AlwaysAssertExit(!allNear(Complex(0,10000), Complex(0,10001), 0.99e-4)); AlwaysAssertExit(!allNear(Complex(10000,0), Complex(0,10001), 1.01e-4)); AlwaysAssertExit(allNearAbs(Complex(0,10000), Complex(0,10001), 1.01)); AlwaysAssertExit(!allNearAbs(Complex(0,10000), Complex(0,10001), 0.99)); AlwaysAssertExit(!allNearAbs(Complex(10000,0), Complex(0,10001), 1.01)); AlwaysAssertExit(near(DComplex(0,10000), DComplex(0,10001), 1.01e-4)); AlwaysAssertExit(!near(DComplex(0,10000), DComplex(0,10001), 0.99e-4)); AlwaysAssertExit(!near(DComplex(10000,0), DComplex(0,10001), 1.01e-4)); AlwaysAssertExit(nearAbs(DComplex(0,10000), DComplex(0,10001), 1.01)); AlwaysAssertExit(!nearAbs(DComplex(0,10000), DComplex(0,10001), 0.99)); AlwaysAssertExit(!nearAbs(DComplex(10000,0), DComplex(0,10001), 1.01)); AlwaysAssertExit(allNear(DComplex(0,10000), DComplex(0,10001), 1.01e-4)); AlwaysAssertExit(!allNear(DComplex(0,10000), DComplex(0,10001), 0.99e-4)); AlwaysAssertExit(!allNear(DComplex(10000,0), DComplex(0,10001), 1.01e-4)); AlwaysAssertExit(allNearAbs(DComplex(0,10000), DComplex(0,10001), 1.01)); AlwaysAssertExit(!allNearAbs(DComplex(0,10000), DComplex(0,10001), 0.99)); AlwaysAssertExit(!allNearAbs(DComplex(10000,0), DComplex(0,10001), 1.01)); Complex c1; DComplex c2; setNaN(c1); setNaN(c2); AlwaysAssertExit(isNaN(c1) && isNaN(c2)); c1 = Complex(0.0, c1.imag()); c2 = DComplex(c2.real(), 0.0); AlwaysAssertExit(isNaN(c1) && isNaN(c2)); c1 = Complex(0.0); c2 = DComplex(0.0); AlwaysAssertExit((!isNaN(c1)) && (!isNaN(c2))); { // Test min/max Complex c1(0,1), c2(2,0); Complex c3 = min(c1,c2); AlwaysAssertExit(near(c1,c3)); Complex c4 = max(c1,c2); AlwaysAssertExit(near(c2,c4)); } { // test mixed types in addition and subtraction DComplex dd(4.0, 5.0); Complex cc(7.0, 8.0); DComplex r = dd + cc; AlwaysAssertExit(near(r, DComplex(11, 13))); r = cc + dd; AlwaysAssertExit(near(r, DComplex(11, 13))); r = dd - cc; AlwaysAssertExit(near(r, DComplex(-3, -3))); r = cc - dd; AlwaysAssertExit(near(r, DComplex(3, 3))); } return(0); } casacore-3.7.1/casa/BasicSL/test/tComplex.out000066400000000000000000000072141476623553700210220ustar00rootroot00000000000000Initial value for complex: (0,0) (0,0) : real part: 0 == 0 imaginary part: 0 == 0 (23.9,1.8) == (23.9,1.8) ~= (23,1) -(0.921,7.812) == (-0.921,-7.812) conj((0.921,7.812)) == (0.921,-7.812) norm((0.921,7.812)) == 61.8756 arg((0.921,7.812)) == 1.45344 fabs((18.9,-2.31)) == 19.0406 out: (237.561,0.9312) in: (237.561,0.9312) (5,2) == (5,2) - - - - - - - - - - - - - - - - - - - - (23.9,1.8) + (9.2,8.2) = (33.1,10) (23.9,1.8) * (9.2,8.2) = (205.12,212.54) (23.9,1.8) - (9.2,8.2) = (14.7,-6.4) (23.9,1.8) / (9.2,8.2) = (1.5449,-1.18133) - - - - - - - - - - - - - - - - - - - - (23.9,1.8) + 7.561 = (31.461,1.8) (23.9,1.8) * 7.561 = (180.708,13.6098) (23.9,1.8) - 7.561 = (16.339,1.8) (23.9,1.8) / 7.561 = (3.16096,0.238064) - - - - - - - - - - - - - - - - - - - - (23.9,1.8) + 13 = (36.9,1.8) (23.9,1.8) * 13.0f = (310.7,23.4) (23.9,1.8) * 13 = (310.7,23.4) (23.9,1.8) * 13. = (310.7,23.4) 13.0f * (23.9,1.8) = (310.7,23.4) 13 * (23.9,1.8) = (310.7,23.4) 13. * (23.9,1.8) = (310.7,23.4) (23.9,1.8) - 13 = (10.9,1.8) (23.9,1.8) / 13.0f = (1.83846,0.138462) (23.9,1.8) / 13 = (1.83846,0.138462) (23.9,1.8) / 13. = (1.83846,0.138462) 13.0f / (23.9,1.8) = (0.540865,-0.0407346) 13 / (23.9,1.8) = (0.540865,-0.0407346) 13. / (23.9,1.8) = (0.540865,-0.0407346) - - - - - - - - - - - - - - - - - - - - (23.9,1.8) += 4 -> (27.9,1.8) (27.9,1.8) *= 4 -> (111.6,7.2) (111.6,7.2) -= 4 -> (107.6,7.2) (107.6,7.2) /= 4 -> (26.9,1.8) - - - - - - - - - - - - - - - - - - - - (26.9,1.8) += 0.896 -> (27.796,1.8) (27.796,1.8) *= 0.896 -> (24.9052,1.6128) (24.9052,1.6128) -= 0.896 -> (24.0092,1.6128) (24.0092,1.6128) /= 0.896 -> (26.796,1.8) - - - - - - - - - - - - - - - - - - - - (26.796,1.8) += simChar -> (28.796,1.8) (28.796,1.8) *= simChar -> (57.592,3.6) (57.592,3.6) -= simChar -> (55.592,3.6) (55.592,3.6) /= simChar -> (27.796,1.8) - - - - - - - - - - - - - - - - - - - - (27.796,1.8) += (2.7,1.8) -> (30.496,3.6) (30.496,3.6) *= (2.7,1.8) -> (75.8592,64.6128) (75.8592,64.6128) -= (2.7,1.8) -> (73.1592,62.8128) (73.1592,62.8128) /= (2.7,1.8) -> (29.496,3.6) - - - - - - - - - - - - - - - - - - - - (29.496,3.6) += (0.921,7.812) -> (30.417,11.412) (30.417,11.412) *= (0.921,7.812) -> (-61.1365,248.128) (-61.1365,248.128) -= (0.921,7.812) -> (-62.0575,240.316) (-62.0575,240.316) /= (0.921,7.812) -> (29.417,11.412) - - - - - - - - - - - - - - - - - - - - sin((33.417,13.412)) = (303543,-139313) cos((33.417,13.412)) = (-139313,-303543) sinh((33.417,13.412)) = (1.08012e+14,1.21875e+14) cosh((33.417,13.412)) = (1.08012e+14,1.21875e+14) - - - - - - - - - - - - - - - - - - - - log((33.417,13.412)) = (3.58374,0.381672) log10((33.417,13.412)) = (1.5564,0.165758) exp((33.417,13.412)) = (2.16024e+14,2.43751e+14) - - - - - - - - - - - - - - - - - - - - pow((33.417,13.412),(36,14) = (Inf,Inf) - - - - - - - - - - - - - - - - - - - - sqrt((33.417,13.412)) = (5.89173,1.13821) - - - - - - - - - - - - - - - - - - - - (33.417,13.412) == (9.2,8.2) -> 0 (33.417,13.412) != (9.2,8.2) -> 1 (33.417,13.412) >= (9.2,8.2) -> 1 (33.417,13.412) > (9.2,8.2) -> 1 (33.417,13.412) <= (9.2,8.2) -> 0 (33.417,13.412) < (9.2,8.2) -> 0 - - - - - - - - - - - - - - - - - - - - (9.2,8.2) == (33.417,13.412) -> 0 (9.2,8.2) != (33.417,13.412) -> 1 (9.2,8.2) >= (33.417,13.412) -> 0 (9.2,8.2) > (33.417,13.412) -> 0 (9.2,8.2) <= (33.417,13.412) -> 1 (9.2,8.2) < (33.417,13.412) -> 1 - - - - - - - - - - - - - - - - - - - - (33.417,13.412) == (33.417,13.412) -> 1 (33.417,13.412) != (33.417,13.412) -> 0 (33.417,13.412) >= (33.417,13.412) -> 1 (33.417,13.412) > (33.417,13.412) -> 0 (33.417,13.412) <= (33.417,13.412) -> 1 (33.417,13.412) < (33.417,13.412) -> 0 - - - - - - - - - - - - - - - - - - - - casacore-3.7.1/casa/BasicSL/test/tComplex.run000066400000000000000000000002621476623553700210130ustar00rootroot00000000000000#!/bin/sh # Change Infinity, infinity or inf to Inf and remove all leading/trailing whitespace $casa_checktool ./tComplex | sed -e 's/nfinity/nf/g;s/inf/Inf/g;s/ *Inf */Inf/g' casacore-3.7.1/casa/BasicSL/test/tConstants.cc000066400000000000000000000254151476623553700211500ustar00rootroot00000000000000//# tConstants.cc: This program tests the Casacore C (constants) class //# Copyright (C) 1993,1994,1995,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include int main() { cout << ">>> List of machine constants." << endl << endl << "Machine constants from limits.h:" << endl << "CHAR_BIT.............. " << CHAR_BIT << endl << "CHAR_MIN.............. " << CHAR_MIN << endl << "CHAR_MAX.............. " << CHAR_MAX << endl << "SCHAR_MIN............. " << SCHAR_MIN << endl << "SCHAR_MAX............. " << SCHAR_MAX << endl << "UCHAR_MAX............. " << UCHAR_MAX << endl << "MB_LEN_MAX............ " << MB_LEN_MAX << endl << "SHRT_MIN.............. " << SHRT_MIN << endl << "SHRT_MAX.............. " << SHRT_MAX << endl << "USHRT_MAX............. " << USHRT_MAX << endl << "INT_MIN............... " << INT_MIN << endl << "INT_MAX............... " << INT_MAX << endl << "UINT_MAX.............. " << UINT_MAX << endl << "LONG_MIN.............. " << LONG_MIN << endl << "LONG_MAX.............. " << LONG_MAX << endl << "ULONG_MAX............. " << ULONG_MAX << endl; cout << endl << "Machine constants from float.h:" << endl << "FLT_RADIX............. " << FLT_RADIX << endl << "FLT_ROUNDS............ " << FLT_ROUNDS << endl << "FLT_MIN_EXP........... " << FLT_MIN_EXP << endl << "DBL_MIN_EXP........... " << DBL_MIN_EXP << endl << "LDBL_MIN_EXP.......... " << LDBL_MIN_EXP << endl << "FLT_MAX_EXP........... " << FLT_MAX_EXP << endl << "DBL_MAX_EXP........... " << DBL_MAX_EXP << endl << "LDBL_MAX_EXP.......... " << LDBL_MAX_EXP << endl << "FLT_MIN_10_EXP........ " << FLT_MIN_10_EXP << endl << "DBL_MIN_10_EXP........ " << DBL_MIN_10_EXP << endl << "LDBL_MIN_10_EXP....... " << LDBL_MIN_10_EXP << endl << "FLT_MAX_10_EXP........ " << FLT_MAX_10_EXP << endl << "DBL_MAX_10_EXP........ " << DBL_MAX_10_EXP << endl << "LDBL_MAX_10_EXP....... " << LDBL_MAX_10_EXP << endl << "FLT_MANT_DIG.......... " << FLT_MANT_DIG << endl << "DBL_MANT_DIG.......... " << DBL_MANT_DIG << endl << "LDBL_MANT_DIG......... " << LDBL_MANT_DIG << endl << "FLT_DIG............... " << FLT_DIG << endl << "DBL_DIG............... " << DBL_DIG << endl << "LDBL_DIG.............. " << LDBL_DIG << endl << "FLT_EPSILON........... " << FLT_EPSILON << endl << "DBL_EPSILON........... " << DBL_EPSILON << endl << "LDBL_EPSILON.......... "; if (LDBL_MAX_EXP == DBL_MAX_EXP) { cout << LDBL_EPSILON << endl; } else { cout << "(unprintable)" << endl; } cout << "FLT_MIN............... " << FLT_MIN << endl << "DBL_MIN............... " << DBL_MIN << endl << "LDBL_MIN.............. "; if (LDBL_MAX_EXP == DBL_MAX_EXP) { cout << LDBL_MIN << endl; } else { cout << "(unprintable)" << endl; } cout << "FLT_MAX............... " << FLT_MAX << endl << "DBL_MAX............... " << DBL_MAX << endl << "LDBL_MAX.............. "; if (LDBL_MAX_EXP == DBL_MAX_EXP) { cout << LDBL_MAX << endl; } else { cout << "(unprintable)" << endl; } #if !defined(AIPS_DARWIN) && !defined(AIPS_BSD) cout << endl << "Machine constants from values.h:" << endl << "HIBITS................ " << HIBITS << endl << "HIBITL................ " << HIBITL << endl << "MAXSHORT.............. " << MAXSHORT << endl << "MAXLONG............... " << MAXLONG << endl << "MAXINT................ " << MAXINT << endl << "MINFLOAT.............. " << MINFLOAT << endl << "MINDOUBLE............. " << MINDOUBLE << endl << "MAXFLOAT.............. " << MAXFLOAT << endl << "MAXDOUBLE............. " << MAXDOUBLE << endl; #endif cout << endl << "<<< End of machine constants." << endl; cout << endl << "Irrationals:" << endl << "C::sqrt2.............. " << M_SQRT2 << endl << "C::sqrt3.............. " << C::sqrt3 << endl << "C::_1_sqrt2........... " << M_SQRT1_2 << endl << "C::_1_sqrt3........... " << C::_1_sqrt3 << endl; cout << endl << "Pi and functions thereof:" << endl << "C::pi................. " << M_PI << endl << "C::_2pi............... " << (2.0*M_PI) << endl << "C::pi_2............... " << M_PI_2 << endl << "C::pi_4............... " << M_PI_4 << endl << "C::_1_pi.............. " << M_1_PI << endl << "C::_2_pi.............. " << M_2_PI << endl << "C::_1_sqrtpi.......... " << (0.5 * M_2_SQRTPI) << endl << "C::_2_sqrtpi.......... " << M_2_SQRTPI << endl; cout << endl << "e and functions thereof:" << endl << "C::e.................. " << M_E << endl << "C::ln2................ " << M_LN2 << endl << "C::ln10............... " << M_LN10 << endl << "C::log2e.............. " << M_LOG2E << endl << "C::log10e............. " << M_LOG10E << endl; cout << endl << "gamma and functions thereof:" << endl << "C::gamma.............. " << C::gamma << endl << "C::lngamma............ " << C::lngamma << endl << "C::etogamma........... " << C::etogamma << endl; cout << endl << "Fundamental physical constants (SI units):" << endl << "C::c.................. " << C::c << endl; cout << endl << "Metric prefixes:" << endl << "C::quetta............. " << C::quetta << endl << "C::ronna.............. " << C::ronna << endl << "C::yotta.............. " << C::yotta << endl << "C::zetta.............. " << C::zetta << endl << "C::exa................ " << C::exa << endl << "C::peta............... " << C::peta << endl << "C::tera............... " << C::tera << endl << "C::giga............... " << C::giga << endl << "C::mega............... " << C::mega << endl << "C::kilo............... " << C::kilo << endl << "C::hecto.............. " << C::hecto << endl << "C::deka............... " << C::deka << endl << "C::deci............... " << C::deci << endl << "C::centi.............. " << C::centi << endl << "C::milli.............. " << C::milli << endl << "C::micro.............. " << C::micro << endl << "C::nano............... " << C::nano << endl << "C::pico............... " << C::pico << endl << "C::femto.............. " << C::femto << endl << "C::atto............... " << C::atto << endl << "C::zepto.............. " << C::zepto << endl << "C::yocto.............. " << C::yocto << endl << "C::ronto.............. " << C::ronto << endl << "C::quecto............. " << C::quecto << endl; cout << endl << "Angular measure:" << endl << "C::radian............. " << C::radian << endl << "C::circle............. " << C::circle << endl << "C::degree............. " << C::degree << endl << "C::arcmin............. " << C::arcmin << endl << "C::arcsec............. " << C::arcsec << endl; cout << endl << "Solid angular measure:" << endl << "C::steradian.......... " << C::steradian << endl << "C::sphere............. " << C::sphere << endl << "C::square_degree...... " << C::square_degree << endl << "C::square_arcmin...... " << C::square_arcmin << endl << "C::square_arcsec...... " << C::square_arcsec << endl; cout << endl << "Time interval:" << endl << "C::second............. " << C::second << endl << "C::minute............. " << C::minute << endl << "C::hour............... " << C::hour << endl << "C::day................ " << C::day << endl; return 0; } casacore-3.7.1/casa/BasicSL/test/tConstants.out000066400000000000000000000075141476623553700213720ustar00rootroot00000000000000>>> List of machine constants. Machine constants from limits.h: CHAR_BIT.............. 8 CHAR_MIN.............. -128 CHAR_MAX.............. 127 SCHAR_MIN............. -128 SCHAR_MAX............. 127 UCHAR_MAX............. 255 MB_LEN_MAX............ 5 SHRT_MIN.............. -32768 SHRT_MAX.............. 32767 USHRT_MAX............. 65535 INT_MIN............... -2147483648 INT_MAX............... 2147483647 UINT_MAX.............. 4294967295 LONG_MIN.............. -2147483648 LONG_MAX.............. 2147483647 ULONG_MAX............. 4294967295 Machine constants from float.h: FLT_RADIX............. 2 FLT_ROUNDS............ 1 FLT_MIN_EXP........... -125 DBL_MIN_EXP........... -1021 LDBL_MIN_EXP.......... -16381 FLT_MAX_EXP........... 128 DBL_MAX_EXP........... 1024 LDBL_MAX_EXP.......... 16384 FLT_MIN_10_EXP........ -37 DBL_MIN_10_EXP........ -307 LDBL_MIN_10_EXP....... -4931 FLT_MAX_10_EXP........ 38 DBL_MAX_10_EXP........ 308 LDBL_MAX_10_EXP....... 4932 FLT_MANT_DIG.......... 24 DBL_MANT_DIG.......... 53 LDBL_MANT_DIG......... 113 FLT_DIG............... 6 DBL_DIG............... 15 LDBL_DIG.............. 33 FLT_EPSILON........... 1.19209e-07 DBL_EPSILON........... 2.22045e-16 LDBL_EPSILON.......... (unprintable) FLT_MIN............... 1.17549e-38 DBL_MIN............... 2.22507e-308 LDBL_MIN.............. (unprintable) FLT_MAX............... 3.40282e+38 DBL_MAX............... 1.79769e+308 LDBL_MAX.............. (unprintable) Machine constants from values.h: HIBITS................ -32768 HIBITL................ 2147483648 MAXSHORT.............. 32767 MAXLONG............... 2147483647 MAXINT................ 2147483647 MINFLOAT.............. 1.4013e-45 MINDOUBLE............. 4.94066e-324 MAXFLOAT.............. 3.40282e+38 MAXDOUBLE............. 1.79769e+308 <<< End of machine constants. Irrationals: C::sqrt2.............. 1.41421 C::sqrt3.............. 1.73205 C::_1_sqrt2........... 0.707107 C::_1_sqrt3........... 0.57735 Pi and functions thereof: C::pi................. 3.14159 C::_2pi............... 6.28319 C::pi_2............... 1.5708 C::pi_4............... 0.785398 C::_1_pi.............. 0.31831 C::_2_pi.............. 0.63662 C::_1_sqrtpi.......... 0.56419 C::_2_sqrtpi.......... 1.12838 e and functions thereof: C::e.................. 2.71828 C::ln2................ 0.693147 C::ln10............... 2.30259 C::log2e.............. 1.4427 C::log10e............. 0.434294 gamma and functions thereof: C::gamma.............. 0.577216 C::lngamma............ -0.549539 C::etogamma........... 1.78107 Fundamental physical constants (SI units): C::c.................. 2.99792e+08 Metric prefixes: C::quetta............. 1e+30 C::ronna.............. 1e+27 C::yotta.............. 1e+24 C::zetta.............. 1e+21 C::exa................ 1e+18 C::peta............... 1e+15 C::tera............... 1e+12 C::giga............... 1e+09 C::mega............... 1e+06 C::kilo............... 1000 C::hecto.............. 100 C::deka............... 10 C::deci............... 0.1 C::centi.............. 0.01 C::milli.............. 0.001 C::micro.............. 1e-06 C::nano............... 1e-09 C::pico............... 1e-12 C::femto.............. 1e-15 C::atto............... 1e-18 C::zepto.............. 1e-21 C::yocto.............. 1e-24 C::ronto.............. 1e-27 C::quecto............. 1e-30 Angular measure: C::radian............. 1 C::circle............. 6.28319 C::degree............. 0.0174533 C::arcmin............. 0.000290888 C::arcsec............. 4.84814e-06 Solid angular measure: C::steradian.......... 1 C::sphere............. 12.5664 C::square_degree...... 0.000304617 C::square_arcmin...... 8.46159e-08 C::square_arcsec...... 2.35044e-11 Time interval: C::second............. 1 C::minute............. 60 C::hour............... 3600 C::day................ 86400 casacore-3.7.1/casa/BasicSL/test/tSTLIO.cc000066400000000000000000000052341476623553700200630ustar00rootroot00000000000000//# tSTLIO.cc: This program tests STL IO //# Copyright (C) 2011 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include using namespace casacore; int main() { // Test with various delimiters. Vector vec2(3); indgen(vec2, 1); { ostringstream oss; showContainer (oss, vec2); AlwaysAssertExit (oss.str() == "[1,2,3]"); } { ostringstream oss; showContainer (oss, vec2, ", "); AlwaysAssertExit (oss.str() == "[1, 2, 3]"); } { ostringstream oss; showDataIter (oss, vec2.data(), vec2.data()+vec2.size(), " ", "(", ")"); AlwaysAssertExit (oss.str() == "(1 2 3)"); } // Test a map (and pair). map map1; map1[-1] = "str-1"; map1[3] = "str3"; { ostringstream oss; oss << map1; AlwaysAssertExit (oss.str() == "{<-1,str-1>, <3,str3>}"); } // Test empty container and vector. { ostringstream oss; oss << vector(); AlwaysAssertExit (oss.str() == "[]"); } { ostringstream oss; oss << vector(1,3); AlwaysAssertExit (oss.str() == "[3]"); } { ostringstream oss; oss << vector(3,4); AlwaysAssertExit (oss.str() == "[4,4,4]"); } // Test a map of integers to list of ints std::map> map2; map2[0] = {1, 2}; map2[3] = {-1, -2}; { ostringstream oss; oss << map2; AlwaysAssertExit (oss.str() == "{<0,[1,2]>, <3,[-1,-2]>}"); } cout << "OK\n"; return 0; } casacore-3.7.1/casa/BasicSL/test/tSTLMath.cc000066400000000000000000000045431476623553700204470ustar00rootroot00000000000000//# tComplex.cc: This program tests the Complex class //# Copyright (C) 1993,1994,1995,1996,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include void testStdVectorPlus() { std::vector a(3); std::vector b(3); a[0] = 0; a[1] = 1; a[2] = 2; b[0] = 5; b[1] = 6; b[2] = 7; std::vector c = a + b; AlwaysAssertExit(c.size() == 3 && c[0] == 5 && c[1] == 7 && c[2] == 9); std::vector d(2); Bool caught = False; try { std::vector e = a + d; // exception should be thrown, shouldn't get here. AlwaysAssertExit(False); } catch (const AipsError& exc) { caught = True; } AlwaysAssertExit(caught); } void testStdVectorDivide() { std::vector a(3); a[0] = 0; a[1] = 2; a[2] = 4; std::vector b = a/2; AlwaysAssertExit(b.size() == 3 && b[0] == 0 && b[1] == 1 && b[2] == 2); } int main() { try { testStdVectorPlus(); testStdVectorDivide(); } catch (const std::exception& x) { cout << "Unexpected exception: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/casa/BasicSL/test/tString.cc000066400000000000000000000310561476623553700204400ustar00rootroot00000000000000//# tString.cc: This program tests Strings //# Copyright (C) 1993-1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include // Next one for atoi and atof #include #include #include #include #include // Generally used variables String X = "Hello"; String Y = "world"; String N = "123"; String c; const Char* s = ","; Regex r ("e[a-z]*o"); void decltest() { String x; cout << "an empty String:" << x << endl; AlwaysAssertExit(x == ""); String y = "Hello"; cout << "A string initialized to Hello:" << y << endl; AlwaysAssertExit(y == "Hello"); if (y[y.length()-1] == 'o') y = y + '\n'; AlwaysAssertExit(y == "Hello\n"); y = "Hello"; String a = y; cout << "A string initialized to previous string:" << a << endl; AlwaysAssertExit(a == "Hello"); AlwaysAssertExit(a == y); String b (a.at(1, 2)); cout << "A string initialized to previous string.at(1, 2):" << b << endl; AlwaysAssertExit(b == "el"); Char ch = '@'; String z(ch); cout << "A string initialized to @:" << z << endl; AlwaysAssertExit(z == "@"); String n = "20"; cout << "A string initialized to dec(20):" << n << endl; AlwaysAssertExit(n == "20"); Int i = atoi(n.chars()); Double f = atof(n.chars()); cout << "n = " << n << " atoi(n) = " << i << " atof(n) = " << f << endl; AlwaysAssertExit(i == 20); AlwaysAssertExit(f == 20); /* AlwaysAssertExit(X.OK()); AlwaysAssertExit(Y.OK()); AlwaysAssertExit(x.OK()); AlwaysAssertExit(y.OK()); AlwaysAssertExit(z.OK()); AlwaysAssertExit(n.OK()); AlwaysAssertExit(r.OK()); */ } void cattest() { String x = X; String y = Y; String z = x + y; cout << "z = x + y = " << z << endl; AlwaysAssertExit(z == "Helloworld"); x += y; cout << "x += y; x = " << x << endl; AlwaysAssertExit(x == "Helloworld"); y = Y; x = X; y.prepend(x); cout << "y.prepend(x); y = " << y << endl; AlwaysAssertExit(y == "Helloworld"); x = X; y = Y; z = x + s + ' ' + y.at("w") + y.after("w") + "."; cout << "z = x + s + + y.at(w) + y.after(w) + . = " << z << endl; AlwaysAssertExit(z == "Hello, world."); } void comparetest() { String x = X; String y = Y; String n = N; String z = x + y; AlwaysAssertExit(x != y); AlwaysAssertExit(x == "Hello"); AlwaysAssertExit(x != z.at(0, 4)); AlwaysAssertExit (x < y); AlwaysAssertExit(!(x >= z.at(0, 6))); AlwaysAssertExit(x.contains("He")); AlwaysAssertExit (z.contains(x)); AlwaysAssertExit(x.contains(r)); AlwaysAssertExit(!(x.matches(r))); AlwaysAssertExit(x.matches(RXalpha)); AlwaysAssertExit(!(n.matches(RXalpha))); AlwaysAssertExit(n.matches(RXint)); AlwaysAssertExit(n.matches(RXdouble)); AlwaysAssertExit(x.index("lo") == 3); AlwaysAssertExit(x.index("l", 2) == 2); AlwaysAssertExit(x.index("l", -1) == 3); // negative pos! AlwaysAssertExit(x.index(r) == 1); AlwaysAssertExit(x.index(r, -2) == 1); AlwaysAssertExit(x.contains("el", 1)); AlwaysAssertExit(x.contains("el")); AlwaysAssertExit(common_prefix(x, "Help") == "Hel"); AlwaysAssertExit(common_suffix(x, "to") == "o"); AlwaysAssertExit(fcompare(x, "hello") == 0); AlwaysAssertExit(fcompare(x, "hellox") < 0); AlwaysAssertExit(fcompare(x, "hell") > 0); AlwaysAssertExit(fcompare(x, "hELlo") == 0); AlwaysAssertExit(fcompare(x, "hElp") < 0); } void substrtest() { String x = X; Char ch = x[0]; cout << "ch = x[0] = " << ch << endl; AlwaysAssertExit(ch == 'H'); String z = x.at(2, 3); cout << "z = x.at(2, 3) = " << z << endl; AlwaysAssertExit(z.length() == 3); AlwaysAssertExit(z == "llo"); String z1 = x.at(2, 4); cout << "z1 = x.at(2, 4) = " << z1 << endl; AlwaysAssertExit(z1.length() == 3); AlwaysAssertExit(z1 == "llo"); String z2 = x.at(5, 3); cout << "z2 = x.at(5, 3) = " << z2 << endl; AlwaysAssertExit(z2.length() == 0); AlwaysAssertExit(z2 == ""); x.at(2, 2) = "r"; cout << "x.at(2, 2) = r; x = " << x << endl; AlwaysAssertExit(x == "Hero"); x = X; x.at(0, 1) = "j"; cout << "x.at(0, 1) = j; x = " << x << endl; AlwaysAssertExit(x == "jello"); x = X; x.at("He") = "je"; cout << "x.at(He) = je; x = " << x << endl; AlwaysAssertExit(x == "jello"); x = X; x.at("l", -1) = "i"; cout << "x.at(l, -1) = i; x = " << x << endl; AlwaysAssertExit(x == "Helio"); x = X; z = x.at(r); cout << "z = x.at(r) = " << z << endl; AlwaysAssertExit(z == "ello"); z = x.before("o"); cout << "z = x.before(o) = " << z << endl; AlwaysAssertExit(z == "Hell"); x.before("ll") = "Bri"; cout << "x.before(ll) = Bri; x = " << x << endl; AlwaysAssertExit(x == "Brillo"); x = X; z = x.before(2); cout << "z = x.before(2) = " << z << endl; AlwaysAssertExit(z == "He"); z = x.after("Hel"); cout << "z = x.after(Hel) = " << z << endl; AlwaysAssertExit(z == "lo"); x.after("Hel") = "p"; cout << "x.after(Hel) = p; x = " << x << endl; AlwaysAssertExit(x == "Help"); x = X; z = x.after(3); cout << "z = x.after(3) = " << z << endl; AlwaysAssertExit(z == "o"); z = " a bc"; z = z.after(RXwhite); cout << "z = a bc; z = z.after(RXwhite); z =" << z << endl; AlwaysAssertExit(z == "a bc"); } void utiltest() { String x = X; Int matches = x.gsub("l", "ll"); cout << "x.gsub(l, ll); x = " << x << endl; AlwaysAssertExit(matches == 2); AlwaysAssertExit(x == "Hellllo"); x = X; matches = x.gsub(r, "ello should have been replaced by this string"); cout << "x.gsub(r, ...); x = " << x << endl; AlwaysAssertExit(matches == 1); AlwaysAssertExit(x == "Hello should have been replaced by this string"); matches = x.gsub(RXwhite, "#"); cout << "x.gsub(RXwhite, #); x = " << x << endl; AlwaysAssertExit(matches == 7); String z = X + Y; z.del("loworl"); cout << "z = x+y; z.del(loworl); z = " << z << endl; AlwaysAssertExit(z == "Held"); x = X; z = reverse(x); cout << "reverse(x) = " << z << endl; AlwaysAssertExit(z == "olleH"); x.reverse(); cout << "x.reverse() = " << x << endl; AlwaysAssertExit(x == z); x = X; z = upcase(x); cout << "upcase(x) = " << z << endl; AlwaysAssertExit(z == "HELLO"); z = downcase(x); cout << "downcase(x) = " << z << endl; AlwaysAssertExit(z == "hello"); z = capitalize(x); cout << "capitalize(x) = " << z << endl; AlwaysAssertExit(z == "Hello"); z = replicate('*', 10); cout << "z = replicate(*, 10) = " << z << endl; AlwaysAssertExit(z == "**********"); AlwaysAssertExit(z.length() == 10); } void splittest() { String z = "This string\thas\nfive words"; cout << "z = " << z << endl; String w[10]; Int nw = split(z, w, 10, RXwhite); AlwaysAssertExit(nw == 5); cout << "from split(z, RXwhite, w, 10), n words = " << nw << ":\n"; for (Int i = 0; i < nw; ++i) { cout << w[i] << endl; } AlwaysAssertExit(w[0] == "This"); AlwaysAssertExit(w[1] == "string"); AlwaysAssertExit(w[2] == "has"); AlwaysAssertExit(w[3] == "five"); AlwaysAssertExit(w[4] == "words"); AlwaysAssertExit(w[5] == ""); z = join(w, nw, "/"); cout << "z = join(w, nw, /); z =" << z << endl; AlwaysAssertExit(z == "This/string/has/five/words"); } void iotest() { String z = "Della and the Dealer\n and a dog named Jake,\n and a cat named Kalamazoo"; cout << "word =" << z << " "; cout << "length = " << z.length() << endl; ostringstream os; os << "Test stream: " << "length = " << z.length(); String zx(os); cout << zx << endl; } void identitytest(String a, String b) { String x = a; String y = b; x += b; y.prepend(a); AlwaysAssertExit((a + b) == x); AlwaysAssertExit((a + b) == y); AlwaysAssertExit(x == y); AlwaysAssertExit(x.after(a) == b); AlwaysAssertExit(x.before(b, 4) == a); AlwaysAssertExit(x.from(a) == x); AlwaysAssertExit(x.through(b, x.size()) == x); AlwaysAssertExit(x.at(a) == a); AlwaysAssertExit(x.at(b) == b); AlwaysAssertExit(reverse(x) == reverse(b) + reverse(a)); AlwaysAssertExit((a + b + a) == (a + (b + a))); /// x.del(b, -1); x.del(b); AlwaysAssertExit(x == a); y.before(b, 2) = b; AlwaysAssertExit(y == (b + b)); y.at(b) = a; AlwaysAssertExit(y == (a + b)); x = a + reverse(a); for (Int i = 0; i < 7; ++i) { y = x; x += x; AlwaysAssertExit(x == reverse(x)); AlwaysAssertExit(x.index(y) == 0); } } void freqtest() { String x = "Hello World"; String y = x.at(0,5); AlwaysAssertExit(x.freq('l') == 3); // Char AlwaysAssertExit(x.freq("lo") == 1); // Char* AlwaysAssertExit(x.freq(x) == 1); // String AlwaysAssertExit(x.freq(y) == 1); // SubString } void toDouble() { String x = "1.5"; Double y = String::toDouble(x); AlwaysAssertExit(y == 1.5); x = "frodo"; AlwaysAssertExit (String::toDouble(x) == 0); bool ok = false; try { y = String::toDouble(x, True); } catch (const AipsError&) { ok = true; } AlwaysAssertExit(ok); } void toFloat() { String x = "1.5"; Float y = String::toFloat(x); AlwaysAssertExit(y == 1.5); x = "1.5 aa"; AlwaysAssertExit(String::toFloat(x) == 1.5); bool ok = false; try { y = String::toFloat(x, True); } catch (const AipsError&) { ok = true; } AlwaysAssertExit(ok); } void toInt() { String x = "4"; Int y = String::toInt(x); AlwaysAssertExit(y == 4); x = "-12"; y = String::toInt(x); AlwaysAssertExit(y == -12); x = "6.9999"; AlwaysAssertExit (String::toInt(x) == 6); bool ok = false; try { y = String::toInt(x, True); } catch (const AipsError&) { ok = true; } AlwaysAssertExit(ok); } void trim() { String myString = "\t \t \n\r my string \n\r \t "; myString.trim(); AlwaysAssertExit(myString == "my string"); myString = "\t \t \n\r my string"; myString.trim(); AlwaysAssertExit(myString == "my string"); myString = "my string \n\r \t "; myString.trim(); AlwaysAssertExit(myString == "my string"); myString = "\n \t\t\r "; myString.trim(); AlwaysAssertExit(myString.empty()); myString = " "; myString.trim(); AlwaysAssertExit(myString.empty()); } void startsWith() { String myString = "Gozer the Destroyer"; AlwaysAssertExit(myString.startsWith("G")); AlwaysAssertExit(myString.startsWith("Gozer t")); AlwaysAssertExit(! myString.startsWith("oz")); } /* void hashtest() { String *xp, a, x[] = { "Hello World", "Me and you and the dog named boo", "a", "b", "aa", "bb", "A", "" }; //# remove cout's of hash values because they can differ between arch's for (xp=x; xp->length() > 0; xp++) xp->hash(); // cout << "hash(" << *xp << ") = " << xp->hash() << endl; a = ""; a.hash(); // cout << "hash() = " << a.hash() << endl; } */ int main() { decltest(); cattest(); comparetest(); substrtest(); utiltest(); splittest(); freqtest(); identitytest(X, X); identitytest(X, Y); identitytest(X+Y+N+X+Y+N, "A string that will be used in identitytest but is otherwise " "just another useless string."); /// hashtest(); iotest(); toDouble(); toFloat(); toInt(); trim(); startsWith(); { // Test to see if String hash works. std::unordered_set sset; sset.insert ("abc"); AlwaysAssertExit (sset.size() == 1); AlwaysAssertExit (sset.find("abc") != sset.end()); AlwaysAssertExit (sset.find("abd") == sset.end()); } cout << "\nEnd of test\n"; return(0); } casacore-3.7.1/casa/BasicSL/test/tString.out000066400000000000000000000025331476623553700206600ustar00rootroot00000000000000an empty String: A string initialized to Hello:Hello A string initialized to previous string:Hello A string initialized to previous string.at(1, 2):el A string initialized to @:@ A string initialized to dec(20):20 n = 20 atoi(n) = 20 atof(n) = 20 z = x + y = Helloworld x += y; x = Helloworld y.prepend(x); y = Helloworld z = x + s + + y.at(w) + y.after(w) + . = Hello, world. ch = x[0] = H z = x.at(2, 3) = llo z1 = x.at(2, 4) = llo z2 = x.at(5, 3) = x.at(2, 2) = r; x = Hero x.at(0, 1) = j; x = jello x.at(He) = je; x = jello x.at(l, -1) = i; x = Helio z = x.at(r) = ello z = x.before(o) = Hell x.before(ll) = Bri; x = Brillo z = x.before(2) = He z = x.after(Hel) = lo x.after(Hel) = p; x = Help z = x.after(3) = o z = a bc; z = z.after(RXwhite); z =a bc x.gsub(l, ll); x = Hellllo x.gsub(r, ...); x = Hello should have been replaced by this string x.gsub(RXwhite, #); x = Hello#should#have#been#replaced#by#this#string z = x+y; z.del(loworl); z = Held reverse(x) = olleH x.reverse() = olleH upcase(x) = HELLO downcase(x) = hello capitalize(x) = Hello z = replicate(*, 10) = ********** z = This string has five words from split(z, RXwhite, w, 10), n words = 5: This string has five words z = join(w, nw, /); z =This/string/has/five/words word =Della and the Dealer and a dog named Jake, and a cat named Kalamazoo length = 70 Test stream: length = 70 End of test casacore-3.7.1/casa/CMakeLists.txt000066400000000000000000000306031476623553700167750ustar00rootroot00000000000000# # CASA casa # configure_file ( config.h.in "${PROJECT_BINARY_DIR}/casacore/casa/config.h" @ONLY) configure_file ( version.h.in "${PROJECT_BINARY_DIR}/casacore/casa/version.h" @ONLY) set ( parser_inputs JsonGram ) foreach (src ${parser_inputs}) if (BISON_VERSION VERSION_LESS 3.0) BISON_TARGET (${src} Json/${src}.yy ${CMAKE_CURRENT_BINARY_DIR}/${src}.ycc COMPILE_FLAGS "-y -p ${src}") else() BISON_TARGET (${src} Json/${src}.yy ${CMAKE_CURRENT_BINARY_DIR}/${src}.ycc COMPILE_FLAGS "-y -Dapi.prefix={${src}} --warnings=no-yacc") endif() FLEX_TARGET (${src} Json/${src}.ll ${CMAKE_CURRENT_BINARY_DIR}/${src}.lcc COMPILE_FLAGS "-P${src}") endforeach (src) include_directories (${CMAKE_CURRENT_BINARY_DIR}) # Define the bfiles to build. set (buildfiles Arrays/ArrayBase.cc Arrays/ArrayError.cc Arrays/ArrayOpsDiffShapes.cc Arrays/ArrayPartMath.cc Arrays/ArrayPosIter.cc Arrays/ArrayUtil2.cc Arrays/Array2.cc Arrays/Array2Math.cc Arrays/Array_tmpl.cc Arrays/AxesMapping.cc Arrays/AxesSpecifier.cc Arrays/ExtendSpecifier.cc Arrays/IPosition.cc #Arrays/IPosition2.cc Arrays/MaskArrMath2.cc Arrays/Matrix2Math.cc Arrays/Matrix_tmpl.cc Arrays/Slice.cc Arrays/Slicer.cc Arrays/Vector_tmpl.cc BasicMath/Math.cc BasicMath/Primes.cc BasicMath/Random.cc BasicSL/Complex.cc BasicSL/IComplex.cc BasicSL/STLMath.cc BasicSL/String.cc Containers/Allocator.cc Containers/Block.cc Containers/Block_tmpl.cc Containers/IterError.cc Containers/Record.cc Containers/RecordDesc.cc Containers/RecordDescRep.cc Containers/RecordFieldId.cc Containers/RecordField2Writer.cc Containers/RecordInterface.cc Containers/RecordRep.cc Containers/Record2.cc Containers/Record2Interface.cc Containers/ValueHolder.cc Containers/ValueHolderRep.cc Exceptions/Error2.cc Exceptions/CasaErrorTools.cc HDF5/HDF5DataSet.cc HDF5/HDF5DataType.cc HDF5/HDF5Error.cc HDF5/HDF5File.cc HDF5/HDF5Group.cc HDF5/HDF5HidMeta.cc HDF5/HDF5Object.cc HDF5/HDF5Record.cc Inputs/Input.cc Inputs/Param.cc IO/AipsIO.cc IO/BaseSinkSource.cc IO/BucketBase.cc IO/BucketBuffered.cc IO/BucketCache.cc IO/BucketFile.cc IO/BucketMapped.cc IO/ByteIO.cc IO/ByteSink.cc IO/ByteSinkSource.cc IO/ByteSource.cc IO/CanonicalIO.cc IO/ConversionIO.cc IO/FilebufIO.cc IO/FiledesIO.cc IO/FileLocker.cc IO/FileUnbufferedIO.cc IO/IPositionIO.cc IO/LECanonicalIO.cc IO/LockFile.cc IO/MemoryIO.cc IO/MFFileIO.cc IO/MMapfdIO.cc IO/MMapIO.cc IO/MultiFile.cc IO/MultiFileBase.cc IO/MultiHDF5.cc IO/RawIO.cc IO/RegularFileIO.cc IO/StreamIO.cc IO/TapeIO.cc IO/TypeIO.cc Json/JsonError.cc Json/JsonKVMap.cc Json/JsonOut.cc Json/JsonParser.cc Json/JsonValue.cc Logging/LogFilter.cc Logging/LogFilterInterface.cc Logging/LogIO.cc Logging/LogMessage.cc Logging/LogOrigin.cc Logging/LogSink.cc Logging/LogSinkInterface.cc Logging/MemoryLogSink.cc Logging/NullLogSink.cc Logging/StreamLogSink.cc OS/CanonicalConversion.cc OS/CanonicalDataConversion.cc OS/Conversion.cc OS/DataConversion.cc OS/Directory.cc OS/DirectoryIterator.cc OS/DOos.cc OS/DynLib.cc OS/EnvVar.cc OS/File.cc OS/HostInfo.cc OS/IBMConversion.cc OS/IBMDataConversion.cc OS/MemoryTrace.cc OS/LECanonicalConversion.cc OS/LECanonicalDataConversion.cc OS/LittleEndianConversion.cc OS/malloc.cc OS/Memory.cc OS/MemoryTrace.cc OS/ModcompConversion.cc OS/ModcompDataConversion.cc OS/OMP.cc OS/Path.cc OS/PrecTimer.cc OS/RawDataConversion.cc OS/RegularFile.cc OS/SymLink.cc OS/Time.cc OS/Timer.cc OS/VAXConversion.cc OS/VAXDataConversion.cc Quanta/Euler.cc Quanta/MeasValue.cc Quanta/MVAngle.cc Quanta/MVBaseline.cc Quanta/MVDirection.cc Quanta/MVDoppler.cc Quanta/MVDouble.cc Quanta/MVEarthMagnetic.cc Quanta/MVEpoch.cc Quanta/MVFrequency.cc Quanta/MVPosition.cc Quanta/MVRadialVelocity.cc Quanta/MVTime.cc Quanta/MVuvw.cc Quanta/QBase.cc Quanta/QC.cc Quanta/QLogical2.cc Quanta/QMath2.cc Quanta/QuantumHolder.cc Quanta/Quantum2.cc Quanta/RotMatrix.cc Quanta/Unit.cc Quanta/UnitDim.cc Quanta/UnitMap.cc Quanta/UnitMap2.cc Quanta/UnitMap3.cc Quanta/UnitMap4.cc Quanta/UnitMap5.cc Quanta/UnitMap6.cc Quanta/UnitMap7.cc Quanta/UnitName.cc Quanta/UnitVal.cc System/Aipsrc.cc System/AipsrcBool.cc System/AipsrcValue2.cc System/AipsrcVBool.cc System/AipsrcVString.cc System/AppInfo.cc System/AppState.cc System/Casarc.cc System/Choice.cc System/ObjectID.cc System/ObjectID2.cc System/PGPlotter.cc System/PGPlotterInterface.cc System/PGPlotterNull.cc System/ProgressMeter.cc Utilities/BitVector.cc Utilities/CountedPtr2.cc Utilities/Compare.cc Utilities/CompositeNumber.cc Utilities/Copy2.cc Utilities/DataType.cc Utilities/DynBuffer.cc Utilities/Fallible2.cc Utilities/MUString.cc Utilities/Precision.cc Utilities/RecordTransformable.cc Utilities/Regex.cc Utilities/Sequence2.cc Utilities/Sort.cc Utilities/SortError.cc Utilities/StringDistance.cc Utilities/ValType.cc aips.cc version.cc ${BISON_JsonGram_OUTPUTS} ${FLEX_JsonGram_OUTPUTS} ) set(top_level_headers ${PROJECT_BINARY_DIR}/casacore/casa/config.h aipsdef.h aipsenv.h aips.h aipstype.h aipsxtype.h Arrays.h BasicMath.h BasicSL.h complex.h Containers.h Exceptions.h fstream.h HDF5.h Inputs.h IO.h iomanip.h iosfwd.h iosstrfwd.h iostream.h istream.h Json.h Logging.h math.h namespace.h OS.h ostream.h Quanta.h sstream.h stdexcept.h stdio.h stdlib.h stdmap.h stdvector.h string.h System.h typeinfo.h Utilities.h vector.h ${PROJECT_BINARY_DIR}/casacore/casa/version.h ) add_library (casa_casa ${buildfiles}) init_pch_support(casa_casa ${top_level_headers}) if (HDF5_FOUND) list (APPEND de_libraries ${HDF5_LIBRARIES}) endif (HDF5_FOUND) if (READLINE_FOUND) list (APPEND de_libraries ${READLINE_LIBRARIES}) endif (READLINE_FOUND) find_library(libm m) target_link_libraries ( casa_casa ${de_libraries} ${libm} dl ${CASACORE_ARCH_LIBS} ) add_subdirectory (apps) install ( TARGETS casa_casa LIBRARY DESTINATION lib${LIB_SUFFIX} ARCHIVE DESTINATION lib${LIB_SUFFIX} LIBRARY PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) install (FILES Arrays/ArrayAccessor.h Arrays/ArrayBase.h Arrays/ArrayError.h Arrays/Array.h Arrays/Array.tcc Arrays/ArrayFwd.h Arrays/ArrayIter.h Arrays/ArrayIter.tcc Arrays/ArrayLogical.h Arrays/ArrayLogical.tcc Arrays/ArrayMathBase.h Arrays/ArrayMath.h Arrays/ArrayMath.tcc Arrays/ArrayOpsDiffShapes.h Arrays/ArrayOpsDiffShapes.tcc Arrays/ArrayPartMath.h Arrays/ArrayPartMath.tcc Arrays/ArrayPosIter.h Arrays/ArrayStr.h Arrays/ArrayStr.tcc Arrays/ArrayUtil.h Arrays/ArrayUtil.tcc Arrays/AxesMapping.h Arrays/AxesSpecifier.h Arrays/Cube.h Arrays/Cube.tcc Arrays/ElementFunctions.h Arrays/ExtendSpecifier.h Arrays/IPosition.h Arrays/LogiArray.h Arrays/LogiCube.h Arrays/LogiMatrix.h Arrays/LogiVector.h Arrays/MaskArrIO.h Arrays/MaskArrIO.tcc Arrays/MaskArrLogi.h Arrays/MaskArrLogi.tcc Arrays/MaskArrMath.h Arrays/MaskArrMath.tcc Arrays/MaskedArray.h Arrays/MaskedArray.tcc Arrays/MaskLogiArrFwd.h Arrays/MaskLogiArr.h Arrays/Matrix.h Arrays/Matrix.tcc Arrays/MatrixIter.h Arrays/MatrixIter.tcc Arrays/MatrixMath.h Arrays/MatrixMath.tcc Arrays/Memory.h Arrays/Slice.h Arrays/Slicer.h Arrays/Storage.h Arrays/Vector.h Arrays/Vector.tcc Arrays/Vector2.tcc Arrays/VectorIter.h Arrays/VectorIter.tcc Arrays/VectorSTLIterator.h DESTINATION include/casacore/casa/Arrays ) install (FILES BasicMath/ConvertScalar.h BasicMath/Functional.h BasicMath/Functional.tcc BasicMath/Functors.h BasicMath/Math.h BasicMath/Primes.h BasicMath/Random.h BasicMath/StdLogical.h DESTINATION include/casacore/casa/BasicMath ) install (FILES BasicSL/Complexfwd.h BasicSL/Complex.h BasicSL/Constants.h BasicSL/IComplex.h BasicSL/String.h BasicSL/STLMath.h BasicSL/STLIO.h BasicSL/STLIO.tcc DESTINATION include/casacore/casa/BasicSL ) install (FILES Containers/Allocator.h Containers/Block.h Containers/BlockIO.h Containers/BlockIO.tcc Containers/IterError.h Containers/ObjectStack.h Containers/ObjectStack.tcc Containers/RecordDesc.h Containers/RecordDescRep.h Containers/RecordField.h Containers/RecordField.tcc Containers/RecordFieldId.h Containers/RecordFieldWriter.h Containers/RecordFieldWriter.tcc Containers/Record.h Containers/RecordInterface.h Containers/RecordRep.h Containers/ValueHolder.h Containers/ValueHolderRep.h DESTINATION include/casacore/casa/Containers ) install (FILES Exceptions/CasaErrorTools.h Exceptions/Error.h Exceptions/Error.tcc DESTINATION include/casacore/casa/Exceptions ) install (FILES HDF5/HDF5DataSet.h HDF5/HDF5DataType.h HDF5/HDF5Error.h HDF5/HDF5File.h HDF5/HDF5Group.h HDF5/HDF5HidMeta.h HDF5/HDF5Object.h HDF5/HDF5Record.h DESTINATION include/casacore/casa/HDF5 ) install (FILES Inputs/Input.h Inputs/Param.h DESTINATION include/casacore/casa/Inputs ) install (FILES IO/AipsIOCarray.h IO/AipsIOCarray.tcc IO/AipsIO.h IO/ArrayIO.h IO/ArrayIO.tcc IO/BaseSinkSource.h IO/BucketBase.h IO/BucketBuffered.h IO/BucketCache.h IO/BucketFile.h IO/BucketMapped.h IO/ByteIO.h IO/ByteSink.h IO/ByteSinkSource.h IO/ByteSource.h IO/CanonicalIO.h IO/ConversionIO.h IO/FilebufIO.h IO/FiledesIO.h IO/FileLocker.h IO/FileUnbufferedIO.h IO/LargeIOFuncDef.h IO/LECanonicalIO.h IO/LockFile.h IO/MemoryIO.h IO/MFFileIO.h IO/MMapfdIO.h IO/MMapIO.h IO/MultiFile.h IO/MultiFileBase.h IO/MultiHDF5.h IO/RawIO.h IO/RegularFileIO.h IO/StreamIO.h IO/TapeIO.h IO/TypeIO.h DESTINATION include/casacore/casa/IO ) install (FILES Json/JsonError.h Json/JsonKVMap.h Json/JsonOut.h Json/JsonOut.tcc Json/JsonParser.h Json/JsonValue.h DESTINATION include/casacore/casa/Json ) install (FILES Logging/LogFilter.h Logging/LogFilterInterface.h Logging/LogIO.h Logging/LogMessage.h Logging/LogOrigin.h Logging/LogSink.h Logging/LogSinkInterface.h Logging/MemoryLogSink.h Logging/NullLogSink.h Logging/StreamLogSink.h DESTINATION include/casacore/casa/Logging ) install (FILES OS/CanonicalConversion.h OS/CanonicalDataConversion.h OS/Conversion.h OS/DataConversion.h OS/Directory.h OS/DirectoryIterator.h OS/DOos.h OS/DynLib.h OS/EnvVar.h OS/File.h OS/HostInfoBsd.h OS/HostInfoDarwin.h OS/HostInfo.h OS/HostInfoHpux.h OS/HostInfoIrix.h OS/HostInfoLinux.h OS/HostInfoOsf1.h OS/HostInfoSolaris.h OS/IBMConversion.h OS/IBMDataConversion.h OS/LECanonicalConversion.h OS/LECanonicalDataConversion.h OS/LittleEndianConversion.h OS/malloc.h OS/Memory.h OS/MemoryTrace.h OS/ModcompConversion.h OS/ModcompDataConversion.h OS/Mutex.h OS/OMP.h OS/Path.h OS/PrecTimer.h OS/RawDataConversion.h OS/RegularFile.h OS/SymLink.h OS/Time.h OS/Timer.h OS/VAXConversion.h OS/VAXDataConversion.h DESTINATION include/casacore/casa/OS ) install (FILES Quanta/Euler.h Quanta/MeasValue.h Quanta/MVAngle.h Quanta/MVBaseline.h Quanta/MVDirection.h Quanta/MVDoppler.h Quanta/MVDouble.h Quanta/MVEarthMagnetic.h Quanta/MVEpoch.h Quanta/MVFrequency.h Quanta/MVPosition.h Quanta/MVRadialVelocity.h Quanta/MVTime.h Quanta/MVuvw.h Quanta/QBase.h Quanta/QC.h Quanta/QLogical.h Quanta/QLogical.tcc Quanta/QMath.h Quanta/QMath.tcc Quanta/Quantum.h Quanta/Quantum.tcc Quanta/QVector.h Quanta/QVector.tcc Quanta/QuantumHolder.h Quanta/QuantumType.h Quanta/RotMatrix.h Quanta/UnitDim.h Quanta/Unit.h Quanta/UnitMap.h Quanta/UnitName.h Quanta/UnitVal.h DESTINATION include/casacore/casa/Quanta ) install (FILES System/Aipsrc.h System/AipsrcValue.h System/AipsrcValue.tcc System/AipsrcVector.h System/AipsrcVector.tcc System/AppInfo.h System/AppState.h System/Casarc.h System/Choice.h System/ObjectID.h System/PGPlotter.h System/PGPlotterInterface.h System/PGPlotterNull.h System/ProgressMeter.h DESTINATION include/casacore/casa/System ) install (FILES Utilities/Assert.h Utilities/Assert.tcc Utilities/BinarySearch.h Utilities/BinarySearch.tcc Utilities/BitVector.h Utilities/CASATask.h Utilities/Compare.h Utilities/Compare.tcc Utilities/CompositeNumber.h Utilities/Copy.h Utilities/Copy.tcc Utilities/CountedPtr.h Utilities/CountedPtr.tcc Utilities/COWPtr.h Utilities/COWPtr.tcc Utilities/DataType.h Utilities/DefaultValue.h Utilities/DynBuffer.h Utilities/Fallible.h Utilities/generic.h Utilities/GenSort.h Utilities/GenSort.tcc Utilities/LinearSearch.h Utilities/LinearSearch.tcc Utilities/MUString.h Utilities/Precision.h Utilities/PtrHolder.h Utilities/PtrHolder.tcc Utilities/RecordTransformable.h Utilities/Regex.h Utilities/Sequence.h Utilities/Sequence.tcc Utilities/SortError.h Utilities/Sort.h Utilities/StringDistance.h Utilities/Template.h Utilities/Template.tcc Utilities/ValType.h Utilities/ValTypeId.h DESTINATION include/casacore/casa/Utilities ) install (FILES ${top_level_headers} DESTINATION include/casacore/casa ) add_subdirectory (test ${EXCL_ALL}) foreach (casa_module Arrays BasicMath BasicSL Containers Exceptions HDF5 Inputs IO Json Logging OS Quanta System Utilities) add_subdirectory (${casa_module}/test ${EXCL_ALL}) endforeach (casa_module) casacore-3.7.1/casa/Containers.h000066400000000000000000000061541476623553700165170ustar00rootroot00000000000000//# : a module for non-mathematical containers //# Copyright (C) 1995,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_CONTAINERS_H #define CASA_CONTAINERS_H #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // // // Non-mathematical Containers // // // // // // // This module provides non-mathematical containers. These containers are the // prototypical computer science types of containers -- // records and simple // arrays. These classes are useful for all of the various types of low // level data management. In general, these classes will have familiar semantics // and an unsurprising interface. // Note that Casacore used to have classes such as Map and List, but they // became obsolete when the Standard C++ Library was introduced. Therefore these // classes have been removed. // // Most of the important classes in this module also have IO shift operators, // e.g. for writing out a Block (simple // array). These operators typically allow the container (and the objects it // contains) to be written out to both AipsIO and // the standard ostream. // // The class Block has the option to trace (de)allocations for Blocks with // a size above a given threshold. It uses class MemoryTrace to log the // trace messages. Unlike MemoryTrace, it also works on non-Linux systems. // Since class Array uses Block underneath, it makes it possible to trace // Array usage. // // // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Containers/000077500000000000000000000000001476623553700163405ustar00rootroot00000000000000casacore-3.7.1/casa/Containers/Allocator.cc000066400000000000000000000026061476623553700205730ustar00rootroot00000000000000//# Allocator.cc: //# Copyright (C) 2015 //# National Astronomical Observatory of Japan //# 2-21-1, Osawa, Mitaka, Tokyo, 181-8588, Japan. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include namespace casacore { constexpr ArrayInitPolicy ArrayInitPolicies::NO_INIT; constexpr ArrayInitPolicy ArrayInitPolicies::INIT; } casacore-3.7.1/casa/Containers/Allocator.h000066400000000000000000000316061476623553700204370ustar00rootroot00000000000000//# Allocator.h: //# Copyright (C) 2015 //# National Astronomical Observatory of Japan //# 2-21-1, Osawa, Mitaka, Tokyo, 181-8588, Japan. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_CONTAINERS_ALLOCATOR_H_ #define CASA_CONTAINERS_ALLOCATOR_H_ #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN #ifndef CASA_DEFAULT_ALIGNMENT # define CASA_DEFAULT_ALIGNMENT (32UL) // AVX/AVX2 alignment #endif // // A global enum used by some Array/Block constructors. // // // ArrayInitPolicy is used in functions where an array is allocated/resized. // class ArrayInitPolicy { public: Bool operator ==(ArrayInitPolicy const &other) { return init == other.init; } Bool operator !=(ArrayInitPolicy const &other) { return init != other.init; } private: Bool init; explicit constexpr ArrayInitPolicy(bool v): init(v) {} friend struct ArrayInitPolicies; }; struct ArrayInitPolicies { // Don't initialize elements in the array. (The array will be explicitly filled with values other than the default value.) static constexpr ArrayInitPolicy NO_INIT = ArrayInitPolicy(false); // Initialize all elements in the array with the default value. static constexpr ArrayInitPolicy INIT = ArrayInitPolicy(true); }; template using std11_allocator = std::allocator; template struct casacore_allocator: public std11_allocator { typedef std11_allocator Super; using size_type = typename Super::size_type; using difference_type = typename Super::difference_type; using pointer = T*; using const_pointer = const T*; using reference = T&; using const_reference = const T&; using value_type = typename Super::value_type; static constexpr size_t alignment = ALIGNMENT; template struct rebind { typedef casacore_allocator other; }; casacore_allocator() throw () { } casacore_allocator(const casacore_allocator&other) noexcept :Super(other) { } template casacore_allocator(const casacore_allocator&) noexcept { } ~casacore_allocator() noexcept { } pointer allocate(size_type elements, const void* = 0) { if (elements > std::allocator_traits::max_size(*this)) { throw std::bad_alloc(); } void *memptr = 0; int result = posix_memalign(&memptr, ALIGNMENT, sizeof(T) * elements); if (result != 0) { throw std::bad_alloc(); } return static_cast(memptr); } void deallocate(pointer ptr, size_type) { free(ptr); } }; template inline bool operator==(const casacore_allocator&, const casacore_allocator&) { return true; } template inline bool operator!=(const casacore_allocator&, const casacore_allocator&) { return false; } template struct new_del_allocator: public std11_allocator { using Super = std11_allocator; using size_type = typename Super::size_type; using difference_type = typename Super::difference_type; using pointer = T*; using const_pointer = const T*; using reference = T&; using const_reference = const T&; using value_type = typename Super::value_type; template struct rebind { typedef new_del_allocator other; }; new_del_allocator() noexcept { } new_del_allocator(const new_del_allocator&other) noexcept :Super(other) { } template new_del_allocator(const new_del_allocator&) noexcept { } ~new_del_allocator() noexcept { } pointer allocate(size_type elements, const void* = 0) { if (elements > std::allocator_traits::max_size(*this)) { throw std::bad_alloc(); } return new T[elements]; } void deallocate(pointer ptr, size_type) { delete[] ptr; } template void construct(U *, Args&&... ) {} // do nothing because new T[] does template void construct(U *ptr, U &&value) { *ptr = value; // because *ptr was already contructed by new[]. } template void construct(U *ptr, U &value) { *ptr = value; // because *ptr was already contructed by new[]. } template void construct(U *ptr, U const &value) { *ptr = value; // because *ptr was already contructed by new[]. } template void destroy(U *) {} // do nothing because delete[] will do. }; template inline bool operator==(const new_del_allocator&, const new_del_allocator&) { return true; } template inline bool operator!=(const new_del_allocator&, const new_del_allocator&) { return false; } template class Block; class Allocator_private { template friend class AbstractAllocator; template friend class BaseAllocator; template friend class Block; template struct BulkAllocator { using size_type = typename std::allocator::size_type; using value_type = typename std::allocator::value_type; using pointer = T2*; using const_pointer = const T2*; virtual pointer allocate(size_type elements, const void*ptr = 0) = 0; virtual void deallocate(pointer ptr, size_type size) = 0; virtual void construct(pointer ptr, size_type n, const_pointer src) = 0; virtual void construct(pointer ptr, size_type n, value_type const &initial_value) = 0; virtual void construct(pointer ptr, size_type n) = 0; virtual void destroy(pointer ptr, size_type n) = 0; virtual std::type_info const &allocator_typeid() const = 0; virtual ~BulkAllocator() {} }; template struct BulkAllocatorImpl: public BulkAllocator { typedef typename Allocator::size_type size_type; typedef typename Allocator::pointer pointer; typedef typename Allocator::const_pointer const_pointer; typedef typename Allocator::value_type value_type; virtual pointer allocate(size_type elements, const void *ptr = 0) override { return allocator.allocate(elements, ptr); } virtual void deallocate(pointer ptr, size_type size) override { allocator.deallocate(ptr, size); } virtual void construct(pointer ptr, size_type n, const_pointer src) override { size_type i = 0; try { for (i = 0; i < n; ++i) { std::allocator_traits::construct(allocator, &ptr[i], src[i]); } } catch (...) { destroy(ptr, i); // rollback constructions throw; } } virtual void construct(pointer ptr, size_type n, value_type const &initial_value) override { size_type i = 0; try { for (i = 0; i < n; ++i) { std::allocator_traits::construct(allocator, &ptr[i], initial_value); } } catch (...) { destroy(ptr, i); // rollback constructions throw; } } virtual void construct(pointer ptr, size_type n) override { size_type i = 0; try { for (i = 0; i < n; ++i) { std::allocator_traits::construct(allocator, &ptr[i]); } } catch (...) { destroy(ptr, i); // rollback constructions throw; } } virtual void destroy(pointer ptr, size_type n) override { for (size_type i = n; i > 0;) { --i; try { std::allocator_traits::destroy(allocator, &ptr[i]); } catch (...) { // Destructor should not raise any exception. } } } virtual std::type_info const &allocator_typeid() const override { return typeid(Allocator); } virtual ~BulkAllocatorImpl() override {} private: static Allocator allocator; }; template static BulkAllocator *get_allocator() { return get_allocator_raw(); } template static BulkAllocatorImpl *get_allocator_raw() { // Because this function gets called from destructors of statically allocated objects that get destructed // after the program finishes, the allocator is constructed in a static storage space and is never // destructed. static typename std::aligned_storage), alignof(BulkAllocatorImpl)>::type storage; static BulkAllocatorImpl* ptr = new (reinterpret_cast*>(&storage)) BulkAllocatorImpl(); return ptr; } // Allocator specifier // // This class is just used to avoid ambiguity between overloaded functions. // template struct AllocSpec { BulkAllocator *allocator; explicit AllocSpec(BulkAllocator *alloc) : allocator(alloc) {} }; }; template Allocator Allocator_private::BulkAllocatorImpl::allocator; template class AbstractAllocator { public: typedef T value_type; virtual ~AbstractAllocator(){} protected: AbstractAllocator(){} friend class Array; friend class Block; virtual Allocator_private::BulkAllocator *getAllocator() const = 0; }; template class BaseAllocator: public AbstractAllocator { public: typedef T value_type; typedef Sub facade_type; virtual ~BaseAllocator() {} protected: BaseAllocator() {} virtual typename Allocator_private::BulkAllocator *getAllocator() const override { return Allocator_private::get_allocator(); } }; // An allocator behaves like operator new[]/delete[]. // Because it is impossible to decouple construction/destruction from allocation/deallocation with this allocator, // it is discouraged to use this allocator. // Use DefaultAllocator or AlignedAllocator as possible. // This allocator is provided only for compatibility for calling // Array::takeStorage(), Block::replaceStorage(), Block(size_t, T *&, Bool) etc. // with a storage allocated by operator new[]. template class NewDelAllocator: public BaseAllocator > { public: typedef new_del_allocator type; // an instance of this allocator. static NewDelAllocator value; protected: NewDelAllocator(){} }; template NewDelAllocator NewDelAllocator::value; // An allocator which allocates aligned memory. template class AlignedAllocator: public BaseAllocator > { public: typedef casacore_allocator type; // an instance of this allocator. static AlignedAllocator value; protected: AlignedAllocator(){} }; template AlignedAllocator AlignedAllocator::value; // An aligned allocator with the default alignment. template class DefaultAllocator: public AlignedAllocator { public: typedef typename AlignedAllocator::type type; // an instance of this allocator. static DefaultAllocator value; protected: DefaultAllocator(){} }; template DefaultAllocator DefaultAllocator::value; // Allocator specifier // // This class is just used to avoid ambiguity between overloaded functions. // template struct AllocSpec { typedef T type; static AllocSpec const value; }; template AllocSpec const AllocSpec::value = AllocSpec(); } //# NAMESPACE CASACORE - END #endif /* CASA_CONTAINERS_ALLOCATOR_H_ */ casacore-3.7.1/casa/Containers/Block.cc000066400000000000000000000040031476623553700176760ustar00rootroot00000000000000//# Block.cc: Simple templated array classes //# Copyright (C) 1993-1997,2000,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Initialize static members. size_t BlockTrace::itsTraceSize = 0; void BlockTrace::setTraceSize (size_t sz) { itsTraceSize = sz; if (sz > 0) { MemoryTrace::open(); } } void BlockTrace::doTraceAlloc (const void* addr, size_t nelem, DataType type, size_t sz) { traceMemoryAlloc (addr, nelem*sz, "Block " << type << ' ' << sz); } void BlockTrace::doTraceFree (const void* addr, size_t nelem, DataType type, size_t sz) { traceMemoryFree (addr, "Block " << type << ' ' << nelem*sz); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Containers/Block.h000066400000000000000000000766671476623553700175710ustar00rootroot00000000000000//# Block.h: Simple templated array classes //# Copyright (C) 1993-1997,2000,2002,2005,2015 //# Associated Universities, Inc. Washington DC, USA. //# National Astronomical Observatory of Japan //# 2-21-1, Osawa, Mitaka, Tokyo, 181-8588, Japan. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_BLOCK_H #define CASA_BLOCK_H #include #include #include #include #include #include // for ptrdiff_t #include // for std:min/max #include //# For index checking #if defined(AIPS_ARRAY_INDEX_CHECK) #include #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN // simple 1-D array // // // // // // // This should be viewed as a block of memory without sophisticated // manipulation functions. Thus it is called Block. // // // // Block is a simple templated 1-D array class. Indices are always // 0-based. For efficiency reasons, no index checking is done unless the // preprocessor symbol AIPS_ARRAY_INDEX_CHECK is defined. // Block's may be assigned to and constructed from other // Block's. // As no reference counting is done this can be an expensive operation, however. // // The net effect of this class is meant to be unsurprising to users who think // of arrays as first class objects. The name "Block" is intended to convey // the concept of a solid "chunk" of things without any intervening "fancy" // memory management, etc. This class was written to be // used in the implementations of more functional Vector, Matrix, etc. classes, // although it is expected Block will be useful on its own. // // The Block class should be efficient. You should normally use Block. // // If you use the assignment operator on an element of this // class, you may leave dangling references to pointers released from // storage(). // Resizing the array will also have this effect if the underlying storage // is actually affected. // // // If index checking is turned on, an out-of-bounds index will // generate an indexError exception. // // // // // Block a(100,0); // 100 ints initialized to 0 // Block b; // 0-length Block // // ... // b = a; // resize b and copy a into it // for (size_t i=0; i < a.nelements(); i++) { // a[i] = i; // Generate a sequence // // with Vectors, could simply say "indgen(myVector);" // } // b.set(-1); // All positions in b have the value -1 // b.resize(b.nelements()*2); // Make b twice as long, by default the old // // elements are copied over, although this can // // be defeated. // some_c_function(b.storage()); // Use a fn that takes an // // Int * pointer // // // class BlockTrace { public: // Set the trace size. The (de)allocation of Blocks with >= sz elements // will be traced using the MemoryTrace class. // A value 0 means no tracing. static void setTraceSize (size_t sz); protected: // Write alloc and free trace messages. static void doTraceAlloc (const void* addr, size_t nelem, DataType type, size_t sz); static void doTraceFree (const void* addr, size_t nelem, DataType type, size_t sz); protected: static size_t itsTraceSize; }; template class Block; template class Block_internal_IsFundamental { template friend class Block; static constexpr int value = static_cast(std::is_fundamental::value); }; template class Block_internal_IsPointer { template friend class Block; static constexpr int value = static_cast(std::is_pointer::value); }; // simple 1-D array // // // // // // // This should be viewed as a block of memory without sophisticated // manipulation functions. Thus it is called Block. // // // // Block is a simple templated 1-D array class. Indices are always // 0-based. For efficiency reasons, no index checking is done unless the // preprocessor symbol AIPS_ARRAY_INDEX_CHECK is defined. // Block's may be assigned to and constructed from other // Block's. // As no reference counting is done this can be an expensive operation, however. // // The net effect of this class is meant to be unsurprising to users who think // of arrays as first class objects. The name "Block" is intended to convey // the concept of a solid "chunk" of things without any intervening "fancy" // memory management, etc. This class was written to be // used in the implementations of more functional Vector, Matrix, etc. classes, // although it is expected Block will be useful on its own. // // The Block class should be efficient. You should normally use Block. // // If you use the assignment operator on an element of this // class, you may leave dangling references to pointers released from // storage(). // Resizing the array will also have this effect if the underlying storage // is actually affected. // // // If index checking is turned on, an out-of-bounds index will // generate an indexError exception. // // // // // Block a(100,0); // 100 ints initialized to 0 // Block b; // 0-length Block // // ... // b = a; // resize b and copy a into it // for (size_t i=0; i < a.nelements(); i++) { // a[i] = i; // Generate a sequence // // with Vectors, could simply say "indgen(myVector);" // } // b.set(-1); // All positions in b have the value -1 // b.resize(b.nelements()*2); // Make b twice as long, by default the old // // elements are copied over, although this can // // be defeated. // some_c_function(b.storage()); // Use a fn that takes an // // Int * pointer // // // template class Block: public BlockTrace { public: // Create a zero-length Block. Note that any index into this Block // is an error. // DefaultAllocator is used as an allocator. Block() : allocator_p(get_allocator::type>()), capacity_p( 0), used_p(0), array(0), destroyPointer(True), keep_allocator_p(False) { } // Create a zero-length Block. Note that any index into this Block // is an error. template explicit Block(AllocSpec const &) : allocator_p(get_allocator()), capacity_p(0), used_p( 0), array(0), destroyPointer(True), keep_allocator_p(False) { } // Create a Block with the given number of points. The values in Block // are initialized. Note that indices range between 0 and n-1. // DefaultAllocator is used as an allocator. explicit Block(size_t n) : allocator_p(get_allocator::type>()), used_p( n), destroyPointer(True), keep_allocator_p(False) { init(init_anyway() ? ArrayInitPolicies::INIT : ArrayInitPolicies::NO_INIT); } // Create a Block with the given number of points. The values in Block // are initialized. Note that indices range between 0 and n-1. template Block(size_t n, AllocSpec const &) : allocator_p(get_allocator()), used_p(n), destroyPointer( True), keep_allocator_p(False) { init(init_anyway() ? ArrayInitPolicies::INIT : ArrayInitPolicies::NO_INIT); } // Create a Block with the given number of points. The values in Block // are uninitialized. Note that indices range between 0 and n-1. // DefaultAllocator is used as an allocator. Block(size_t n, ArrayInitPolicy initPolicy) : allocator_p(get_allocator::type>()), used_p( n), destroyPointer(True), keep_allocator_p(False) { init(initPolicy); } // Create a Block with the given number of points. // Note that indices range between 0 and n-1. template Block(size_t n, ArrayInitPolicy initPolicy, AllocSpec const &) : allocator_p(get_allocator()), used_p(n), destroyPointer( True), keep_allocator_p(False) { init(initPolicy); } // Create a Block of the given length, and initialize (via copy constructor for // objects of type T) with the provided value. // DefaultAllocator is used as an allocator. Block(size_t n, T const &val) : allocator_p(get_allocator::type>()), used_p( n), destroyPointer(True), keep_allocator_p(False) { init(ArrayInitPolicies::NO_INIT); try { allocator_p->construct(array, get_size(), val); } catch (...) { dealloc(); throw; } } // Create a Block of the given length, and initialize (via copy constructor for // objects of type T) with the provided value. template Block(size_t n, T const &val, AllocSpec const &) : allocator_p(get_allocator()), used_p(n), destroyPointer( True), keep_allocator_p(False) { init(ArrayInitPolicies::NO_INIT); try { allocator_p->construct(array, get_size(), val); } catch (...) { dealloc(); throw; } } // Create a Block from a C-array (i.e. pointer). If // takeOverStorage is True, The Block assumes that // it owns the pointer, i.e. that it is safe to release it via allocator when // the Block is destructed, otherwise the actual storage is not destroyed. // If true, storagePointer is set to 0. // It is strongly recommended to supply an appropriate allocator argument explicitly // whenever takeOverStorage == True // to let Block to know how to release the storagePointer. // The default allocator set by this constructor will be changed from NewDelAllocator::value // to DefaultAllocator::value in future. Block(size_t n, T *&storagePointer, Bool takeOverStorage = True) : allocator_p(get_allocator::type>()), capacity_p( n), used_p(n), array(storagePointer), destroyPointer(takeOverStorage), keep_allocator_p( False) { if (destroyPointer) storagePointer = 0; } // Create a Block from a C-array (i.e. pointer). If // takeOverStorage is True, The Block assumes that // it owns the pointer, i.e. that it is safe to release it via allocator when // the Block is destructed, otherwise the actual storage is not destroyed. // If true, storagePointer is set to 0. template Block(size_t n, T *&storagePointer, Bool takeOverStorage, AllocSpec const &) : allocator_p(get_allocator()), capacity_p(n), used_p( n), array(storagePointer), destroyPointer(takeOverStorage), keep_allocator_p( False) { if (destroyPointer) storagePointer = 0; } // Copy the other block into this one. Uses copy, not reference, semantics. Block(const Block &other) : allocator_p(other.allocator_p), used_p(other.size()), destroyPointer( True), keep_allocator_p(False) { init(ArrayInitPolicies::NO_INIT); try { //objcopy(array, other.array, get_size()); objthrowcp1(array, other.array, get_size()); allocator_p->construct(array, get_size(), other.array); } catch (...) { dealloc(); throw; } } // Assign other to this. this resizes itself to the size of other, so after // the assignment, this->nelements() == other.nelements() always. Block &operator=(const Block &other) { if (&other != this) { T *old = array; this->resize(other.size(), True, False, ArrayInitPolicies::NO_INIT); if (array == old) { objcopy(array, other.array, get_size()); } else { objthrowcp1(array, other.array, get_size()); allocator_p->construct(array, get_size(), other.array); } } return *this; } // Frees up the storage pointed contained in the Block. ~Block() { deinit(); } // Resizes the Block. If n == nelements() resize just returns. If // a larger size is requested (n > nelements()) the Block always // resizes. If the requested size is smaller (n < nelements()), // by default the Block does not resize smaller, although it can be // forced to with forceSmaller. The reasoning behind this is that // often the user will just want a buffer of at least a certain size, // and won't want to pay the cost of multiple resizings. // // Block bf(100, 0.0); // bf.resize(10); // bf.nelements() == 100 // bf.resize(10, True) // bf.nelements() == 10 // bf.resize(200) // bf.nelements() == 200 // // Normally the old elements are copied over (although if the // Block is lengthened the trailing elements will have undefined // values), however this can be turned off by setting copyElements to // False. // // This is written as three functions because default parameters do // not always work properly with templates. // // initPolicy makes sense to determine whether extended elements // should be initialized or not when you enlarge Block. // void resize(size_t n, Bool forceSmaller = False, Bool copyElements = True) { resize(n, forceSmaller, copyElements, init_anyway() ? ArrayInitPolicies::INIT : ArrayInitPolicies::NO_INIT); } void resize(size_t n, Bool forceSmaller, Bool copyElements, ArrayInitPolicy initPolicy) { if (n == get_size()) { return; } if (n < get_size() && forceSmaller == False) { if (false) { // to keep get_size() == get_capacity() allocator_p->destroy(&array[n], get_size() - n); set_size(n); } return; } if (get_size() < n && n <= get_capacity()) { allocator_p->construct(&array[get_size()], n - get_size()); set_size(n); return; } T *tp = n > 0 ? allocator_p->allocate(n) : 0; traceAlloc(tp, n); if (n > 0) { size_t start = 0; if (copyElements) { size_t nmin = std::min(get_size(), n); // Don't copy too much! if (nmin > 0) { try { allocator_p->construct(tp, nmin, array); } catch (...) { traceFree(tp, n); allocator_p->deallocate(tp, n); throw; } } start = nmin; } if (initPolicy == ArrayInitPolicies::INIT) { try { allocator_p->construct(&tp[start], n - start); } catch (...) { allocator_p->destroy(tp, start); traceFree(tp, n); allocator_p->deallocate(tp, n); throw; } } } deinit(); destroyPointer = True; array = tp; // ... and update pointer set_capacity(n); set_size(n); } // // Remove a single element from the Block. If forceSmaller is True this // will resize the Block and hence involve new memory allocations. This is // relatively expensive so setting forceSmaller to False is preferred. When // forceSmaller is False the Block is not resized but the elements with an // index above the removed element are shuffled down by one. For backward // compatibility forceSmaller is True by default. // // initPolicy makes sense to determine whether new storage // should be initialized or not before copying when forceSmaller is True. // void remove(size_t whichOne, Bool forceSmaller = True) { remove(whichOne, forceSmaller, init_anyway() ? ArrayInitPolicies::INIT : ArrayInitPolicies::NO_INIT); } void remove(size_t whichOne, Bool forceSmaller, ArrayInitPolicy initPolicy) { if (whichOne >= get_size()) { #if defined(AIPS_ARRAY_INDEX_CHECK) throw(indexError(whichOne, "Block::remove() - " "index out of range")); #else return; #endif } size_t n = get_size() - 1; if (forceSmaller == True) { T *tp = n > 0 ? allocator_p->allocate(n) : 0; traceAlloc(array, n); if (initPolicy == ArrayInitPolicies::INIT && n > 0) { try { allocator_p->construct(tp, n); } catch (...) { traceFree(tp, n); allocator_p->deallocate(tp, n); throw; } } try { objcopy(tp, array, whichOne); } catch (...) { traceFree(tp, n); allocator_p->deallocate(tp, n); throw; } try { objcopy(tp + whichOne, array + whichOne + 1, get_size() - whichOne - 1); } catch (...) { allocator_p->destroy(tp, whichOne); traceFree(tp, n); allocator_p->deallocate(tp, n); throw; } if (array && destroyPointer) { traceFree(array, get_capacity()); allocator_p->destroy(array, get_size()); allocator_p->deallocate(array, get_capacity()); array = 0; }; set_capacity(n); set_size(n); array = tp; destroyPointer = True; } else { objmove(&array[whichOne], &array[whichOne + 1], get_size() - whichOne - 1); if (false) { // to keep get_size() == get_capacity() allocator_p->destroy(&array[n], 1); set_size(n); } } } // // Prohibit changing allocator for this instance. // void prohibitChangingAllocator() { keep_allocator_p = True; } // Permit changing allocator for this instance. void permitChangingAllocator() { keep_allocator_p = False; } // // Replace the internal storage with a C-array (i.e. pointer). // If takeOverStorage is True, The Block assumes that it // owns the pointer, i.e. that it is safe to release it via allocator when the // Blockis destructed, otherwise the actual storage is not destroyed. // If true, storagePointer is set to NULL. // It is strongly recommended to supply an appropriate allocator argument explicitly // whenever takeOverStorage == True // to let Block to know how to release the storagePointer. // The default parameter of allocator will be changed from AllocSpec >::value // to AllocSpec >::value in future. // AipsError is thrown if allocator is incompatible with the current allocator of the instance and changing allocator is prohibited, // even if takeOverStorage == False. // void replaceStorage(size_t n, T *&storagePointer, Bool takeOverStorage=True) { replaceStorage(n, storagePointer, takeOverStorage, AllocSpec >::value); } template void replaceStorage(size_t n, T *&storagePointer, Bool takeOverStorage, AllocSpec const &) { if (keep_allocator_p && ! isCompatibleAllocator()) { throw AipsError("Block::replaceStorage - Attemption to change allocator of Block"); } if (array && destroyPointer) { traceFree (array, get_capacity()); allocator_p->destroy(array, get_size()); allocator_p->deallocate(array, get_capacity()); array = 0; }; set_capacity(n); set_size(n); allocator_p = get_allocator(); array = storagePointer; destroyPointer = takeOverStorage; if (destroyPointer) storagePointer = 0; } // // Index into the block (0-based). If the preprocessor symbol // AIPS_ARRAY_INDEX_CHECK is defined, index checking will be done // and an out-of-bounds index will cause an indexError to be // thrown. Note that valid indices range between 0 and nelements()-1. // //
      • indexError // // T &operator[](size_t index) { #if defined(AIPS_ARRAY_INDEX_CHECK) // Write it this way to avoid casts; remember index and get_size() are // unsigned. if ((get_size() == 0) || (index > get_size() - 1)) { throw(indexError(index, "Block::operator[] - " "index out of range")); }; #endif return array[index]; } const T &operator[](size_t index) const { #if defined(AIPS_ARRAY_INDEX_CHECK) if ((get_size() == 0) || (index > get_size() - 1)) { throw(indexError(index, "Block::operator[] const - " "index out of range")); }; #endif return array[index]; } // // Set all values in the block to "val". // Block &operator=(const T &val) { T tmp=val; objset(array, tmp, get_size()); return *this;} void set(const T &val) { *this = val; } // // If you really, really, need a "raw" pointer to the beginning of the // storage area this will give it to you. This may leave dangling pointers // if the block is destructed or if the assignment operator or resize // is used. Returns a null pointer if nelements() == 0. // It is best to only use this if you completely control the extent and // lifetime of the Block. //

        Examples of misuse

        // Block *bp = new Block(100); // Int *ip = bp->storage(); // DefaultAllocator::value.deallocate(bp, bp->capacity()); // Oops, ip is now dangling // Block a(100),b(100); // Int *ip = a.storage(); // a = b; // Likewise // // T *storage() {return array;} const T *storage() const {return array;} // // The number of elements contained in this Block. // size_t nelements() const {return size();} size_t size() const {return get_capacity();} // // The capacity in this Block. // size() <= capacity() is always true. size_t capacity() const {return get_capacity();} // Is the block empty (i.e. no elements)? Bool empty() const {return size() == 0;} // Define the STL-style iterators. // It makes it possible to iterate through all data elements. // // Block bl(100,0); // for (Block::iterator iter=bl.begin(); iter!=bl.end(); iter++) { // *iter += 1; // } // // // STL-style typedefs. // typedef T value_type; typedef T* iterator; typedef const T* const_iterator; typedef value_type* pointer; typedef const value_type* const_pointer; typedef value_type& reference; typedef const value_type& const_reference; typedef size_t size_type; typedef ptrdiff_t difference_type; // // Get the begin and end iterator object for this block. // iterator begin() { return array; } const_iterator begin() const { return array; } iterator end() { return array + size(); } const_iterator end() const { return array + size(); } // // inline void traceAlloc (const void* addr, size_t sz) const { if (itsTraceSize>0 && sz>=itsTraceSize) { doTraceAlloc (addr, sz, whatType(), sizeof(T)); } } inline void traceFree (const void* addr, size_t sz) const { if (itsTraceSize>0 && sz>=itsTraceSize) { doTraceFree (addr, sz, whatType(), sizeof(T)); } } private: friend class Array; // to allow access to following constructors. Block(size_t n, ArrayInitPolicy initPolicy, Allocator_private::BulkAllocator *allocator) : allocator_p(allocator), used_p(n), destroyPointer( True), keep_allocator_p(False) { init(initPolicy); } Block(size_t n, Allocator_private::AllocSpec allocator) : allocator_p(allocator.allocator), used_p(n), destroyPointer( True), keep_allocator_p(False) { init(init_anyway() ? ArrayInitPolicies::INIT : ArrayInitPolicies::NO_INIT); } Block(size_t n, T *&storagePointer, Bool takeOverStorage, Allocator_private::BulkAllocator *allocator) : allocator_p(allocator), capacity_p(n), used_p( n), array(storagePointer), destroyPointer(takeOverStorage), keep_allocator_p( False) { if (destroyPointer) storagePointer = 0; } void construct(size_t pos, size_t n, T const *src) { allocator_p->construct(&array[pos], n, src); } void construct(size_t pos, size_t n, T const &initial_value) { allocator_p->construct(&array[pos], n, initial_value); } void construct(size_t pos, size_type n) { allocator_p->construct(&array[pos], n); } void destroy(size_t pos, size_type n) { allocator_p->destroy(&array[pos], n); } Allocator_private::BulkAllocator *get_allocator(){ return allocator_p; } static bool init_anyway() { return !(Block_internal_IsFundamental::value || Block_internal_IsPointer::value); } // end of friend void init(ArrayInitPolicy initPolicy) { set_capacity(get_size()); if (get_capacity() > 0) { array = allocator_p->allocate(get_capacity()); traceAlloc(array, get_capacity()); if (initPolicy == ArrayInitPolicies::INIT) { try { allocator_p->construct(array, get_size()); } catch (...) { dealloc(); throw; } } } else { array = 0; } } void deinit() { if (array && destroyPointer) { allocator_p->destroy(array, get_size()); dealloc(); } } void dealloc() { if (array && destroyPointer) { traceFree(array, get_capacity()); allocator_p->deallocate(array, get_capacity()); array = 0; } } template static typename Allocator_private::BulkAllocator< typename Allocator::value_type> *get_allocator() { return Allocator_private::get_allocator< Allocator>(); } template Bool isCompatibleAllocator() { typename Allocator_private::BulkAllocator< typename Allocator::type::value_type> *other_allocator = Allocator_private::get_allocator(); return other_allocator == allocator_p; } // The number of used elements in the vector size_t get_size() const { return used_p;} // Set the number of used elements in the vector void set_size(size_t new_value) { AlwaysAssert(new_value <= get_capacity(), AipsError); used_p = new_value; } // The capacity of the vector size_t get_capacity() const { return capacity_p;} // Set the capacity of the vector void set_capacity(size_t new_value) { capacity_p = new_value; set_size(std::min(get_size(), capacity_p)); } // The allocator typename Allocator_private::BulkAllocator *allocator_p; // The capacity of the vector size_t capacity_p; // The number of used elements in the vector size_t used_p; // The actual storage T *array; // Can we delete the storage upon destruction? Bool destroyPointer; // Can we change allocator or not? Bool keep_allocator_p; }; // // A drop-in replacement for Block. // // // //
      • Block // // // PtrBlock has exactly the same interface as Block // and should be used in preference to the latter. It's purpose is solely to // reduce the number of template instantiations. // // //
      • Partial template specialization is another implementation choice that // will be possible eventually. //
      • It might be useful to have functions that know the template parameter // is a pointer, e.g. that delete all the non-null pointers. // template class PtrBlock { public: PtrBlock() : block_p() {} explicit PtrBlock(size_t n) : block_p(n) {} PtrBlock(size_t n, T val) : block_p(n, (void *)val) {} PtrBlock(size_t n, T *&storagePointer, Bool takeOverStorage = True) : block_p(n, (void **&)storagePointer, takeOverStorage) {} PtrBlock(const PtrBlock &other) : block_p(other.block_p) {} PtrBlock &operator=(const PtrBlock &other) { block_p = other.block_p; return *this;} ~PtrBlock() {} void resize(size_t n, Bool forceSmaller, Bool copyElements) { block_p.resize(n,forceSmaller, copyElements); } void resize(size_t n) {block_p.resize(n);} void resize(size_t n, Bool forceSmaller) {block_p.resize(n, forceSmaller);} void remove(size_t whichOne, Bool forceSmaller) { block_p.remove(whichOne, forceSmaller);} void remove(size_t whichOne) {block_p.remove(whichOne);} void replaceStorage(size_t n, T *&storagePointer, Bool takeOverStorage=True) {block_p.replaceStorage(n, (void **&)storagePointer, takeOverStorage);} T &operator[](size_t index) {return (T &)block_p[index];} const T &operator[](size_t index) const {return (const T &)block_p[index];} void set(const T &val) {block_p.set((void *const &)val);} PtrBlock &operator=(const T &val) {set(val); return *this;} T *storage() {return (T *)block_p.storage();} const T *storage() const {return (const T *)block_p.storage();} size_t nelements() const {return block_p.nelements();} size_t size() const {return block_p.size();} Bool empty() const {return block_p.empty();} private: Block block_p; }; //# Instantiate extern templates for often used types. extern template class Block; extern template class Block; extern template class Block; extern template class Block; extern template class Block; extern template class Block; extern template class Block; extern template class Block; extern template class Block; extern template class Block; extern template class Block; extern template class Block; extern template class Block; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Containers/BlockIO.h000066400000000000000000000117371476623553700200040ustar00rootroot00000000000000//# BlockIO.h: Functions to perform IO for the Block class //# Copyright (C) 1993,1994,1995,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_BLOCKIO_H #define CASA_BLOCKIO_H #include //# Forward declarations. #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template class Block; class AipsIO; // IO functions for Block // // // // // // These functions allow the user to write either an entire or a // partial Block out to an ostream or to // AipsIO. These functions provide simple storage and // display capabilities for Block. // // // // Global Block IO functions // // // //# It appears that (at least for the SUN compiler) the 3rd argument //# cannot be an uInt (otherwise the compiler says no match when //# called as e.g. putBlock(ios,blk,10);). //# //# Note that as of 29-Dec-2008 the size of Block is a size_t, so nr should //# also be a size_t. However, because AipsIO cannot handle sizes larger than //# an uInt, it makes no sense to make that change. // // These functions allow the user to read and write Blocks // from the AipsIO stream. // // putBlock writes the Block to the stream. If // a number, nr, of elements is specified, only the first // nr elements will be written out to AipsI0. // // getBlock reads a Block in from an // AipsIO stream. // // template void putBlock (AipsIO&, const Block&, Int nr); template void putBlock (AipsIO& ios, const Block& blk) { putBlock (ios, blk, (Int)(blk.nelements())); } template void getBlock (AipsIO&, Block&); // // These functions allow the user to write Blocks out to // a standard ostream. The user can either write the entire // Block out to the stream, or if a number of elements, // nr, is specified, only the first nr elements // of the Block will be written out. // // template void showBlock (std::ostream&, const Block&, Int nr); template void showBlock (std::ostream& ios, const Block& blk) { showBlock (ios, blk, (Int)(blk.nelements())); } // // These are the standard shift operators for writing an entire // Block out to a stream. Shift operators are provided // to write the block out to either AipsIO or // ostream. A shift operator is also provided for // reading a Block in from AipsIO. // STL containers like vector and list are written in the same way as // a Block, so they can be written one way and read back the other. // // // template AipsIO& operator<< (AipsIO& ios, const Block& blk) { putBlock (ios, blk, (Int)(blk.nelements())); return ios; } template AipsIO& operator>> (AipsIO& ios, Block& blk) { getBlock (ios, blk); return ios; } template std::ostream& operator<< (std::ostream& ios, const Block& blk) { showBlock (ios, blk, (Int)(blk.nelements())); return ios; } // // //# Implement the specialization for the void* data type. //# This will not do anything at all. //# This specialization is needed for StColMirAIO.cc. inline void putBlock (AipsIO&, const Block&, Int) {} inline void getBlock (AipsIO&, Block&) {} inline void showBlock (AipsIO&, const Block&, Int) {} } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/casa/Containers/BlockIO.tcc000066400000000000000000000044711476623553700203230ustar00rootroot00000000000000//# BlockIO.cc: Functions to perform IO for the Block class //# Copyright (C) 1993,1994,1995,1999,2001,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_BLOCKIO_TCC #define CASA_BLOCKIO_TCC #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template void putBlock (AipsIO& ios, const Block& blk, Int nr) { if (nr < 0) { nr = 0; } else if (nr > Int(blk.nelements())) { nr = blk.nelements(); } ios.putstart("Block", 1); putAipsIO(ios, (uInt)nr, blk.storage()); ios.putend(); } template void getBlock (AipsIO& ios, Block& blk) { ios.getstart("Block"); uInt nr; ios >> nr; blk.resize(nr,True); getAipsIO(ios, (uInt)nr, blk.storage()); ios.getend(); } template void showBlock (ostream& ios, const Block& blk, Int nr) { if (nr < 0) { nr = 0; } else if (nr > Int(blk.nelements())) { nr = blk.nelements(); } ios << "["; for (Int i=0; i 0) { ios << ", "; } ios << blk[i]; } ios << "]"; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Containers/Block_tmpl.cc000066400000000000000000000033511476623553700207370ustar00rootroot00000000000000//# Block_tmpl.cc: Explicit Block template instantiations //# Copyright (C) 2015 //# Associated Universities, Inc. Washington DC, USA, //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include //# Instantiate extern templates for often used types. namespace casacore { template class Block; template class Block; template class Block; template class Block; template class Block; template class Block; template class Block; template class Block; template class Block; template class Block; template class Block; template class Block; template class Block; } casacore-3.7.1/casa/Containers/IterError.cc000066400000000000000000000043441476623553700205710ustar00rootroot00000000000000//# IterError.cc: //# Copyright (C) 1993,1994,1995,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // The normal constructor when throwing the exception. IterError::IterError (const char *msg,Category c) : AipsError(msg ? msg : "Iterator Error.",c) {} IterError::~IterError () noexcept { ; } // The normal constructor when throwing the exception. IterBoundaryError::IterBoundaryError (const char *msg,Category c) : IterError(msg ? msg : "Iterator boundaries exceeded.",c) {} IterBoundaryError::~IterBoundaryError () noexcept { ; } // The normal constructor when throwing the exception. IterInitError::IterInitError (const char *msg,Category c) : IterError(msg ? msg : "Iterator initialization error.",c) {} IterInitError::~IterInitError () noexcept { ; } // The normal constructor when throwing the exception. InvalidIterError::InvalidIterError (const char *msg,Category c) : IterError(msg ? msg : "Use of invalid iterator.",c) {} InvalidIterError::~InvalidIterError () noexcept { ; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Containers/IterError.h000066400000000000000000000052051476623553700204300ustar00rootroot00000000000000//# IterError.h: //# Copyright (C) 1993,1994,1995,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_ITERERROR_H #define CASA_ITERERROR_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Iteration error class // // class IterError : public AipsError { public: IterError(const char *msg = 0, Category c=BOUNDARY); // normal constructor ~IterError () noexcept; }; // Iteration Boundary error class // // class IterBoundaryError : public IterError { public: IterBoundaryError(const char *msg = 0, Category c=BOUNDARY); // normal constructor ~IterBoundaryError () noexcept; }; // Iteration initialization error // // class IterInitError : public IterError { public: IterInitError(const char *msg = 0, Category c=INITIALIZATION); // normal constructor ~IterInitError () noexcept; }; // Invalide iteration error class // // class InvalidIterError : public IterError { public: InvalidIterError(const char *msg = 0, Category c=GENERAL); // normal constructor ~InvalidIterError () noexcept; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Containers/ObjectStack.h000066400000000000000000000102071476623553700207050ustar00rootroot00000000000000//# ObjectStack.h: A stack of re-usable objects //# Copyright (C) 2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_OBJECTSTACK_H #define CASA_OBJECTSTACK_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // // A stack of re-usable objects // // // // // // // // //
      • // // // // An ObjectStack contains a set of pre-allocated Objects of the type // T. // The stack is a very simple stack, without the // linking/unlinking of a normal Stack implementation. // This lightweight implementation was especially designed for use // with the AutoDiff // classes, but can be used independently. The stack works best with small // object sizes, or letter/envelope classes. // // The class is fully thread-safe, thus the same object can be used safely // in multiple threads. // // // // // // { // // Get an element (and create stack!) // SparseDiff elem; // // Use it // elem.value() = 27; // // Release it (automatic by dtor on elem) // } // // // // // To improve the speed for the auto differentiating classes. // // // //
      • the class T must have a constructor(T::FULLREFRESH) // for creating new entries and destructor; // and must possess a clear() method to enable element re-use. // // // // template class ObjectStack { public: //# Member functions // Destructor ~ObjectStack(); // Create a singleton stack static ObjectStack &stack(); // Get a pointer to an object in the stack. The stack will be extended if // no objects left. T *get(); // Return an object to the stack for re-use void put(T *obj) { obj->clear(); stack_p.push_back(obj); }; // Decimate the stack by getting rid of all unused elements in it void clear(); // Test if stack empty Bool empty() { return stack_p.empty(); }; // return the stack extend (for debugging use and checking mainly) uInt nelements() const { return stack_p.size(); }; private: //# Data // The Stack std::vector stack_p; std::mutex mutex_p; //# Constructors // All ctor and assignment constructors and assignment (not implemented) // ObjectStack() : stack_p() {}; ObjectStack(const ObjectStack &other); ObjectStack &operator=(const ObjectStack &other); // //# Member functions }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/casa/Containers/ObjectStack.tcc000066400000000000000000000041271476623553700212330ustar00rootroot00000000000000//# ObjectStack.cc: A stack of re-usable objects //# Copyright (C) 2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_OBJECTSTACK_TCC #define CASA_OBJECTSTACK_TCC //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template ObjectStack &ObjectStack::stack() { static ObjectStack theStack; return theStack; } //# Destructor template ObjectStack::~ObjectStack() { for (uInt i=0; i T *ObjectStack::get() { std::lock_guard lock(mutex_p); if (stack_p.empty()) stack_p.push_back(new T); T* object = stack_p.back(); stack_p.pop_back(); return object; } template void ObjectStack::clear() { std::lock_guard lock(mutex_p); std::vector(stack_p).swap(stack_p); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Containers/Record.cc000066400000000000000000000200041476623553700200610ustar00rootroot00000000000000//# Record.cc: A hierarchical collection of named fields of various types //# Copyright (C) 1995,1996,1997,1998,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Record::Record() : RecordInterface (), rep_p (new RecordRep), parent_p (0) {} Record::Record (RecordType type, CheckFieldFunction* func, const void* checkArgument) : RecordInterface (type, func, checkArgument), rep_p (new RecordRep), parent_p (0) {} Record::Record (const RecordDesc& description, RecordType type, CheckFieldFunction* func, const void* checkArgument) : RecordInterface (type, func, checkArgument), rep_p (new RecordRep (description)), parent_p (0) {} // When description is empty, Record structure is variable. Record::Record (RecordRep* parent, const RecordDesc& description) : RecordInterface (description.nfields()==0 ? Variable : Fixed, 0, 0), rep_p (new RecordRep (description)), parent_p (parent) {} Record::Record (RecordRep* parent, RecordType type) : RecordInterface (type, 0, 0), rep_p (new RecordRep), parent_p (parent) {} Record::Record (const Record& other) : RecordInterface (other), rep_p (other.rep_p), parent_p (other.parent_p) {} Record::Record (const RecordInterface& other) : RecordInterface (other), rep_p (new RecordRep (other.description())), parent_p (0) { uInt n = other.nfields(); const RecordDesc& desc = description(); for (uInt i=0; icopyDataField (dtype, i, other.get_pointer (i, dtype)); } } } Record& Record::operator= (const Record& other) { // Assignment is only possible when the Record is empty or // when their layout match or when the Record is non-fixed. // When non-fixed or empty, we simply replace the representation. // Otherwise we replace all values (in which case we do not need // to replace the pointers in RecordFieldPtr's). if (this != &other) { if (! isFixed() || nfields() == 0) { rep_p = other.rep_p; }else{ AlwaysAssert (conform (other), AipsError); rwRef().copyData (other.ref()); } } return *this; } Record::~Record() {} RecordInterface* Record::clone() const { return new Record (*this); } void Record::assign (const RecordInterface& that) { *this = that; } void Record::print (ostream& os, Int maxNrValues, const String& indent) const { rep_p.ref().print (os, maxNrValues, indent); } void Record::makeUnique() { rwRef(); } RecordRep& Record::rwRef() { return rep_p.rwRef(); } const String& Record::comment (const RecordFieldId& id) const { Int whichField = idToNumber (id); return ref().comment (whichField); } void Record::setComment (const RecordFieldId& id, const String& comment) { Int whichField = idToNumber (id); rwRef().setComment (whichField, comment); } RecordDesc Record::getDescription() const { return ref().description(); } void Record::restructure (const RecordDesc& newDescription, Bool recursive) { // Restructure is not possible for fixed records. throwIfFixed(); // Restructuring means that all RecordFieldPtr's get invalid. rwRef().restructure (newDescription, recursive); } uInt Record::nfields() const { return description().nfields(); } Int Record::fieldNumber (const String& fieldName) const { return description().fieldNumber (fieldName); } DataType Record::type (Int whichField) const { return description().type (whichField); } void Record::removeField (const RecordFieldId& id) { throwIfFixed(); Int whichField = idToNumber (id); rwRef().removeField (whichField); } void Record::renameField (const String& newName, const RecordFieldId& id) { rwRef().renameField (newName, idToNumber(id)); } void Record::addDataField (const String& name, DataType type, const IPosition& shape, Bool fixedShape, const void* value) { rwRef().addDataField (name, type, shape, fixedShape, value); } void Record::defineDataField (Int whichField, DataType type, const void* value) { rwRef().defineDataField (whichField, type, value); } void* Record::get_pointer (Int whichField, DataType type) const { return ref().get_pointer (whichField, type); } void* Record::get_pointer (Int whichField, DataType type, const String& recordType) const { return ref().get_pointer (whichField, type, recordType); } void Record::defineRecord (const RecordFieldId& id, const RecordInterface& value, RecordType type) { defineRecord (id, Record (value), type); } void Record::defineRecord (const RecordFieldId& id, const Record& value, RecordType type) { Int whichField = newIdToNumber (id); if (whichField < 0) { throwIfFixed(); String name; if (id.byName()) { name = id.fieldName(); }else{ name = description().makeName (id.fieldNumber()); } checkName (name, TpRecord); rwRef().addField (name, value, type); }else{ rwRef().defineDataField (whichField, TpRecord, &value); Record& subrec = *(Record*)get_pointer (whichField, TpRecord); subrec.recordType() = type; } } const RecordInterface& Record::asRecord (const RecordFieldId& id) const { return subRecord (id); } RecordInterface& Record::asrwRecord (const RecordFieldId& id) { return rwSubRecord (id); } const Record& Record::subRecord (const RecordFieldId& id) const { Int whichField = idToNumber (id); return *(const Record*)get_pointer (whichField, TpRecord); } Record& Record::rwSubRecord (const RecordFieldId& id) { Int whichField = idToNumber (id); rwRef(); return *(Record*)get_pointer (whichField, TpRecord); } void Record::mergeField (const Record& other, const RecordFieldId& id, DuplicatesFlag flag) { throwIfFixed(); Int whichField = other.idToNumber (id); rwRef().mergeField (other.ref(), whichField, flag); } void Record::merge (const Record& other, DuplicatesFlag flag) { AlwaysAssert (this != &other, AipsError); throwIfFixed(); rwRef().merge (other.ref(), flag); } void Record::putRecord (AipsIO& os) const { ref().putRecord (os, recordType()); } void Record::getRecord (AipsIO& os) { // Get is only possible when the Record is empty or when // the Record is non-fixed. AlwaysAssert ((! isFixed() || nfields() == 0), AipsError); // Reading the record type back means casting it from an int // to the correct type. Int type; rwRef().getRecord (os, type); recordType() = (RecordInterface::RecordType)type; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Containers/Record.h000066400000000000000000000446701476623553700177420ustar00rootroot00000000000000//# Record.h: A hierarchical collection of named fields of various types //# Copyright (C) 1995,1996,1997,1998,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_RECORD_H #define CASA_RECORD_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class IPosition; class AipsIO; // // A hierarchical collection of named fields of various types // // // // // //
      • RecordDesc. //
      • RecordInterface. //
      • RecordFieldPtr. // // // ``Record'' is a widely used term in both programming languages and data // structures to denote an imhogeneous set of fields. An alternative would // have been to name it structure, which would have perhaps been // a clearer name for C++ programmers. // // // Class RecordInterface decribes // the fundamental properties of records. //
        // The Record class is a particular type of a record class. // The fields in Record may be of scalar type, array type, or a Record. // The types are chosen to be compatible with the native // types of the Table system, viz: Bool, uChar, Short, Int, uInt, float, // double, Complex, DComplex, String. // Arrays of all these types are also available. // Note that a Record is not a space-efficient way of storing small objects. //

        // The structure of a Record is defined by the // RecordDesc class. The structure of the Record can be defined at // construction time. It can thereafter be restructured. This has the // effect, however, that any existing RecordFieldPtr objects become // invalid. //
        // It is possible to add or remove fields once a Record is constructed. // However, this is not possible when the Record is constructed with a // fixed structure (i.e. with the fixedStructure flag set). //

        // A Record is a hierarchical structure, because it can have fields // containing Record's (as layed out in the RecordDesc). A subrecord // has a variable structure, when its RecordDesc is empty (i.e. contains // no fields). It is fixed when its RecordDesc contains fields. //

        // A Record may be assigned to another only if they conform; that is if their // fields have the identical type in the identical order. // The field names do not need to be identical however, only the types. // That is, the structure needs to be identical, but // not the labels. Note that field order is significant, // [ifield(type=Int),ffield(type=float)] // is not the same as [ffield(type=float),ifield(type=Int)] //
        // Conformance is checked recursively for fixed subrecords. That is, a // variable structured subrecord is not checked, because any record // can be assigned to it. A fixed structured subrecord has to // conform the corresponding subrecord in the source. //

        // Record uses copy-on-write semantics. This means that when a Record // is copied, only the pointer to the underlying RecordRep object is copied. // Only when the Record gets changed (i.e. when a non-const Record member // function is called), the RecordRep object is copied. // This results in a cheap copy behaviour. // // // Suppose we wanted to create a records that describe the favorite example // of the OO world - an employee: // // RecordDesc employeeDesc; // employeeDesc.addField ("name", TpString); // employeeDesc.addField ("salary", TpDouble); // // The above creates the description (structure) for some record objects. // // Record employeeA(employeeDesc); // Record employeeB(employeeDesc, False); // // And these two lines create Record objects which share this common structure. // The first Record has a fixed structure, the 2nd variable. // // RecordFieldPtr nameA(employeeA, 0); // RecordFieldPtr nameB(employeeB, 0); // RecordFieldPtr salaryA(employeeA, 1); // RecordFieldPtr salaryB(employeeB, "salary"); // // This shows how we can get access to the individual fields. The fields are // fundamentally identified by number, but the number can be looked up through // the use of the fieldNumber member function. // // nameA.define ("Tim"); // nameB.define ("Brian"); // salaryA.define (1.0e+8); // salaryB.define (1.0 / *salaryA); // // Once obtained, the fields are readily manipulated, as shown above. Note // that the field values are obtained through the dereference (*) // operator. This is to identify that the field objects are pointers // to the values in the underlying Record; that is // // salaryA = salaryB; // *salaryA = *salaryB; // // Do very different things; the first line is a pointer copy; salaryA and // salaryB now point to the same field in salaryB. The second line is a value // copy. // // Whole records can be copied as long as their structures are compatible, so // that employeeA = employeeB is a legal statement. However, if // the structure is changed, assignment is no longer possible, and all of the // field pointers are invalidated: // // employeeB.define ("age", (Int)40); // employeeA = employeeB; // exception - no longer conformant // // // // Collections of data with different types are frequently needed. // Record makes it possible to hold such data in a flexible way. // // //

      • A record reference class, which contains some fields from another // record, would likely be useful. This would be analagous to a // subarray sliced from an existing array. // class Record : public RecordInterface { friend class RecordRep; public: // Create a record with no fields. // The record has a variable structure. Record(); // Create a record with no fields. // The type determines if the record has a fixed or variable structure. // The callback function is called when a field is added to the Record. // That function can check the name and of data type of the new field // (for instance, the Table system uses it to ensure that table columns // and keywords have different names). explicit Record (RecordType type, CheckFieldFunction* = 0, const void* checkArgument = 0); // Create a record with the given description. If it is not possible to // create all fields (for example, if a field with an unsupported data // type is requested), an exception is thrown. // The type determines if the record has a fixed or variable structure. // All fields are checked by the field checking function (if defined) // (for instance, the Table system uses it to ensure that table columns // and keywords have different names). explicit Record (const RecordDesc& description, RecordType type = Fixed, CheckFieldFunction* = 0, const void* checkArgument = 0); // Create a copy of other using copy semantics. Record (const Record& other); // Create a Record from another type of record using copy semantics. // Subrecords are also converted to a Record. Record (const RecordInterface& other); // Copy the data in the other record to this record. // It can operate in 2 ways depending on the Record structure flag. //
          //
        • For variable structured records the existing fields are // thrown away and replaced by the new fields. // This means that RecordFieldPtr's using this record get invalidated. // Because copy-on-write semantics are used, this kind of // assignment is a very efficient operation. //
        • For fixed structured records the existing values are replaced // by the new values. This means that RecordFieldPtr's using this // record remain valid. // The structure of the other record has to conform this record // or this record has to be empty, otherwise an exception is thrown. // This assignment is less efficient, because it has to check the // conformance and because each value has to be copied. //
        // // Attributes like fixed structure flag and check function will not // be copied. // Record& operator= (const Record& other); // Release resources associated with this object. ~Record(); // Make a copy of this object. RecordInterface* clone() const override; // Assign that RecordInterface object to this one. // Unlike operator= it copies all data in the derived // class. void assign (const RecordInterface& that) override; // Get the comment for this field. const String& comment (const RecordFieldId&) const override; // Set the comment for this field. void setComment (const RecordFieldId&, const String& comment) override; // Describes the current structure of this Record. const RecordDesc& description() const; // Change the structure of this Record to contain the fields in // newDescription. After calling restructure, description() == // newDescription. Any existing RecordFieldPtr objects are // invalidated (their isAttached() members return False) after // this call. //
        When the new description contains subrecords, those subrecords // will be restructured if recursive=True is given. // Otherwise the subrecord is a variable empty record. // Subrecords will be variable if their description is empty (i.e. does // not contain any field), otherwise they are fixed. The 2nd form of // the restructure function will overwrite those implicit // record types with the given record type. The new type will also // be given to this top record. //
        Restructuring is not possible and an exception is thrown // if the Record has a fixed structure. void restructure (const RecordDesc& newDescription, Bool recursive = True) override; // Returns True if this and other have the same RecordDesc, other // than different names for the fields. That is, the number, type and the // order of the fields must be identical (recursively for fixed // structured sub-Records in this). // // thisRecord.conform(thatRecord) == True does not imply //
        thatRecord.conform(thisRecord) == True, because // a variable record in one conforms a fixed record in that, but // not vice-versa. //
        Bool conform (const Record& other) const; // How many fields does this structure have? A convenient synonym for // description().nfields(). uInt nfields() const override; // Get the field number from the field name. // -1 is returned if the field name is unknown. Int fieldNumber (const String& fieldName) const override; // Get the data type of this field. DataType type (Int whichField) const override; // Remove a field from the record. // // Removing a field means that the field number of the fields following // it will be decremented. Only the RecordFieldPtr's // pointing to the removed field will be invalidated. // void removeField (const RecordFieldId&) override; // Rename the given field. void renameField (const String& newName, const RecordFieldId&); // Define a value for the given field containing a subrecord. // When the field is unknown, it will be added to the record. // The second version is meant for any type of record (e.g. Record, // TableRecord, GlishRecord). It is converted to a Record using the // Record constructor taking a RecordInterface object. // void defineRecord (const RecordFieldId&, const Record& value, RecordType type = Variable); void defineRecord (const RecordFieldId&, const RecordInterface& value, RecordType = Variable) override; // // Get the subrecord from the given field. // // The non-const version has a different name to prevent that the // copy-on-write mechanism makes a copy when not necessary. // // const Record& subRecord (const RecordFieldId&) const; Record& rwSubRecord (const RecordFieldId&); const RecordInterface& asRecord (const RecordFieldId&) const override; RecordInterface& asrwRecord (const RecordFieldId&) override; // // Get or define the value as a ValueHolder. // This is useful to pass around a value of any supported type. // ValueHolder asValueHolder (const RecordFieldId&) const override; void defineFromValueHolder (const RecordFieldId&, const ValueHolder&) override; // // Merge a field from another record into this record. // The DuplicatesFlag (as described in // RecordInterface) determines // what will be done in case the field name already exists. void mergeField (const Record& other, const RecordFieldId&, DuplicatesFlag = ThrowOnDuplicates); // Merge all fields from the other record into this record. // The DuplicatesFlag (as described in // RecordInterface) determines // what will be done in case a field name already exists. // An exception will be thrown if other is the same as this // (i.e. if merging the record itself). void merge (const Record& other, DuplicatesFlag = ThrowOnDuplicates); // Write the Record to an output stream. friend AipsIO& operator<< (AipsIO& os, const Record& rec); // Read the Record from an input stream. friend AipsIO& operator>> (AipsIO& os, Record& rec); // Write the Record to an output stream. // This is used to write a subrecord, whose description has // not been written. void putRecord (AipsIO& os) const; // Read the Record from an input stream. // This is used to read a subrecord, whose description has // not been read. void getRecord (AipsIO& os); // Put the data of a record. // This is used to write a subrecord, whose description has // already been written. void putData (AipsIO& os) const; // Read the data of a record. // This is used to read a subrecord, whose description has // already been read. void getData (AipsIO& os, uInt version); // Make a unique record representation // (to do copy-on-write in RecordFieldPtr). void makeUnique() override; // Print the contents of the record. // Only the first maxNrValues of an array will be printed. // A value < 0 means the entire array. void print (std::ostream&, Int maxNrValues = 25, const String& indent="") const override; protected: // Used by the RecordField classes to attach in a type-safe way to the // correct field. // void* get_pointer (Int whichField, DataType type) const override; void* get_pointer (Int whichField, DataType type, const String& recordType) const override; // // Return a const reference to the underlying RecordRep. const RecordRep& ref() const; // Return a non-const reference to the underlying RecordRep. // When needed, the RecordRep will be copied and all RecordField // objects will be notified. RecordRep& rwRef(); // Add a field to the record. void addDataField (const String& name, DataType type, const IPosition& shape, Bool fixedShape, const void* value) override; // Define a value in the given field. void defineDataField (Int whichField, DataType type, const void* value) override; private: // Get the description of this record. RecordDesc getDescription() const override; // Create Record as a subrecord. // When the description is empty, the record has a variable structure. // Otherwise it is fixed. // Record (RecordRep* parent, const RecordDesc& description); Record (RecordRep* parent, RecordType type); // // The Record representation. COWPtr rep_p; // The parent Record. RecordRep* parent_p; }; inline const RecordRep& Record::ref() const { return rep_p.ref(); } inline const RecordDesc& Record::description() const { return ref().description(); } inline Bool Record::conform (const Record& other) const { return ref().conform (other.ref()); } inline AipsIO& operator<< (AipsIO& os, const Record& rec) { rec.putRecord (os); return os; } inline void Record::putData (AipsIO& os) const { ref().putData (os); } inline AipsIO& operator>> (AipsIO& os, Record& rec) { rec.getRecord (os); return os; } inline void Record::getData (AipsIO& os, uInt version) { rwRef().getData (os, version); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Containers/Record2.cc000066400000000000000000000036061476623553700201540ustar00rootroot00000000000000//# Record2.cc: A hierarchical collection of named fields of various types //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ValueHolder Record::asValueHolder (const RecordFieldId& id) const { if (dataType(id) == TpRecord) { return ValueHolder(subRecord(id)); } else { return RecordInterface::asValueHolder (id); } } void Record::defineFromValueHolder (const RecordFieldId& id, const ValueHolder& value) { if (value.dataType() == TpRecord) { defineRecord (id, value.asRecord()); } else { RecordInterface::defineFromValueHolder (id, value); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Containers/Record2Interface.cc000066400000000000000000000315441476623553700217770ustar00rootroot00000000000000//# Record2Interface.cc: Implementation of toArrayX functions //# Copyright (C) 2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Array RecordInterface::toArrayBool (const RecordFieldId& id) const { Array arr; Int whichField = idToNumber (id); switch (type(whichField)) { case TpInt: case TpArrayInt: { Array tmp = asArrayInt (id); arr.resize (tmp.shape()); convertArray (arr, tmp); break; } default: arr = asArrayBool(id).copy(); } return arr; } Array RecordInterface::toArrayuChar (const RecordFieldId& id) const { return asArrayuChar(id).copy(); } Array RecordInterface::toArrayShort (const RecordFieldId& id) const { Array arr; Int whichField = idToNumber (id); switch (type(whichField)) { case TpUChar: case TpArrayUChar: { Array tmp = asArrayuChar (id); arr.resize (tmp.shape()); convertArray (arr, tmp); break; } default: arr = asArrayShort (id); } return arr; } Array RecordInterface::toArrayInt (const RecordFieldId& id) const { Array arr; Int whichField = idToNumber (id); switch (type(whichField)) { case TpUChar: case TpArrayUChar: { Array tmp = asArrayuChar (id); arr.resize (tmp.shape()); convertArray (arr, tmp); break; } case TpShort: case TpArrayShort: { Array tmp = asArrayShort (id); arr.resize (tmp.shape()); convertArray (arr, tmp); break; } case TpUInt: case TpArrayUInt: { Array tmp = asArrayuInt (id); arr.resize (tmp.shape()); convertArray (arr, tmp); break; } case TpInt64: case TpArrayInt64: { Array tmp = asArrayInt64 (id); arr.resize (tmp.shape()); convertArray (arr, tmp); break; } default: arr = asArrayInt (id); } return arr; } Array RecordInterface::toArrayuInt (const RecordFieldId& id) const { Array arr; Int whichField = idToNumber (id); switch (type(whichField)) { case TpUChar: case TpArrayUChar: { Array tmp = asArrayuChar (id); arr.resize (tmp.shape()); convertArray (arr, tmp); break; } case TpShort: case TpArrayShort: { Array tmp = asArrayShort (id); arr.resize (tmp.shape()); convertArray (arr, tmp); break; } case TpInt: case TpArrayInt: { Array tmp = asArrayInt (id); arr.resize (tmp.shape()); convertArray (arr, tmp); break; } case TpInt64: case TpArrayInt64: { Array tmp = asArrayInt64 (id); arr.resize (tmp.shape()); convertArray (arr, tmp); break; } default: arr = asArrayuInt (id); } return arr; } Array RecordInterface::toArrayInt64 (const RecordFieldId& id) const { Array arr; Int whichField = idToNumber (id); switch (type(whichField)) { case TpUChar: case TpArrayUChar: { Array tmp = asArrayuChar (id); arr.resize (tmp.shape()); convertArray (arr, tmp); break; } case TpShort: case TpArrayShort: { Array tmp = asArrayShort (id); arr.resize (tmp.shape()); convertArray (arr, tmp); break; } case TpInt: case TpArrayInt: { Array tmp = asArrayInt (id); arr.resize (tmp.shape()); convertArray (arr, tmp); break; } case TpUInt: case TpArrayUInt: { Array tmp = asArrayuInt (id); arr.resize (tmp.shape()); convertArray (arr, tmp); break; } default: arr = asArrayInt64 (id); } return arr; } Array RecordInterface::toArrayFloat (const RecordFieldId& id) const { Array arr; Int whichField = idToNumber (id); switch (type(whichField)) { case TpUChar: case TpArrayUChar: { Array tmp = asArrayuChar (id); arr.resize (tmp.shape()); convertArray (arr, tmp); break; } case TpShort: case TpArrayShort: { Array tmp = asArrayShort (id); arr.resize (tmp.shape()); convertArray (arr, tmp); break; } case TpInt: case TpArrayInt: { Array tmp = asArrayInt (id); arr.resize (tmp.shape()); convertArray (arr, tmp); break; } case TpUInt: case TpArrayUInt: { Array tmp = asArrayuInt (id); arr.resize (tmp.shape()); convertArray (arr, tmp); break; } case TpInt64: case TpArrayInt64: { Array tmp = asArrayInt64 (id); arr.resize (tmp.shape()); convertArray (arr, tmp); break; } case TpDouble: case TpArrayDouble: { Array tmp = asArrayDouble (id); arr.resize (tmp.shape()); convertArray (arr, tmp); break; } default: arr = asArrayFloat (id); } return arr; } Array RecordInterface::toArrayDouble (const RecordFieldId& id) const { Array arr; Int whichField = idToNumber (id); switch (type(whichField)) { case TpUChar: case TpArrayUChar: { Array tmp = asArrayuChar (id); arr.resize (tmp.shape()); convertArray (arr, tmp); break; } case TpShort: case TpArrayShort: { Array tmp = asArrayShort (id); arr.resize (tmp.shape()); convertArray (arr, tmp); break; } case TpInt: case TpArrayInt: { Array tmp = asArrayInt (id); arr.resize (tmp.shape()); convertArray (arr, tmp); break; } case TpUInt: case TpArrayUInt: { Array tmp = asArrayuInt (id); arr.resize (tmp.shape()); convertArray (arr, tmp); break; } case TpInt64: case TpArrayInt64: { Array tmp = asArrayInt64 (id); arr.resize (tmp.shape()); convertArray (arr, tmp); break; } case TpFloat: case TpArrayFloat: { Array tmp = asArrayFloat (id); arr.resize (tmp.shape()); convertArray (arr, tmp); break; } default: arr = asArrayDouble (id); } return arr; } Array RecordInterface::toArrayDComplex (const RecordFieldId& id) const { Array arr; Int whichField = idToNumber (id); switch (type(whichField)) { case TpUChar: case TpArrayUChar: case TpShort: case TpArrayShort: case TpInt: case TpArrayInt: case TpUInt: case TpArrayUInt: case TpInt64: case TpArrayInt64: case TpFloat: case TpArrayFloat: { Array tmp = toArrayDouble (id); arr.resize (tmp.shape()); convertArray (arr, tmp); break; } case TpDouble: case TpArrayDouble: { Array tmp = asArrayDouble (id); arr.resize (tmp.shape()); convertArray (arr, tmp); break; } case TpComplex: case TpArrayComplex: { Array tmp = asArrayComplex (id); arr.resize (tmp.shape()); convertArray (arr, tmp); break; } default: arr = asArrayDComplex (id); } return arr; } Array RecordInterface::toArrayComplex (const RecordFieldId& id) const { Array arr; Int whichField = idToNumber (id); switch (type(whichField)) { case TpUChar: case TpArrayUChar: case TpShort: case TpArrayShort: case TpInt: case TpArrayInt: case TpUInt: case TpArrayUInt: case TpInt64: case TpArrayInt64: case TpDouble: case TpArrayDouble: { Array tmp = toArrayFloat (id); arr.resize (tmp.shape()); convertArray (arr, tmp); break; } case TpFloat: case TpArrayFloat: { Array tmp = asArrayFloat (id); arr.resize (tmp.shape()); convertArray (arr, tmp); break; } case TpDComplex: case TpArrayDComplex: { Array tmp = asArrayDComplex (id); arr.resize (tmp.shape()); convertArray (arr, tmp); break; } default: arr = asArrayComplex (id); } return arr; } Array RecordInterface::toArrayString (const RecordFieldId& id) const { return asArrayString(id).copy(); } ValueHolder RecordInterface::asValueHolder (const RecordFieldId& id) const { switch (dataType(id)) { case TpBool: return ValueHolder(asBool(id)); case TpUChar: return ValueHolder(asuChar(id)); case TpShort: return ValueHolder(asShort(id)); case TpInt: return ValueHolder(asInt(id)); case TpUInt: return ValueHolder(asuInt(id)); case TpInt64: return ValueHolder(asInt64(id)); case TpFloat: return ValueHolder(asFloat(id)); case TpDouble: return ValueHolder(asDouble(id)); case TpComplex: return ValueHolder(asComplex(id)); case TpDComplex: return ValueHolder(asDComplex(id)); case TpString: return ValueHolder(asString(id)); case TpArrayBool: return ValueHolder(asArrayBool(id)); case TpArrayUChar: return ValueHolder(asArrayuChar(id)); case TpArrayShort: return ValueHolder(asArrayShort(id)); case TpArrayInt: return ValueHolder(asArrayInt(id)); case TpArrayUInt: return ValueHolder(asArrayuInt(id)); case TpArrayInt64: return ValueHolder(asArrayInt64(id)); case TpArrayFloat: return ValueHolder(asArrayFloat(id)); case TpArrayDouble: return ValueHolder(asArrayDouble(id)); case TpArrayComplex: return ValueHolder(asArrayComplex(id)); case TpArrayDComplex: return ValueHolder(asArrayDComplex(id)); case TpArrayString: return ValueHolder(asArrayString(id)); default: break; } throw AipsError ("RecordInterface::asValueHolder - unknown data type"); } void RecordInterface::defineFromValueHolder (const RecordFieldId& id, const ValueHolder& value) { switch (value.dataType()) { case TpBool: define (id, value.asBool()); break; case TpUChar: define (id, value.asuChar()); break; case TpShort: define (id, value.asShort()); break; case TpUShort: case TpInt: define (id, value.asInt()); break; case TpUInt: define (id, value.asuInt()); break; case TpInt64: define (id, value.asInt64()); break; case TpFloat: define (id, value.asFloat()); break; case TpDouble: define (id, value.asDouble()); break; case TpComplex: define (id, value.asComplex()); break; case TpDComplex: define (id, value.asDComplex()); break; case TpString: define (id, value.asString()); break; case TpArrayBool: define (id, value.asArrayBool()); break; case TpArrayUChar: define (id, value.asArrayuChar()); break; case TpArrayShort: define (id, value.asArrayShort()); break; case TpArrayUShort: case TpArrayInt: define (id, value.asArrayInt()); break; case TpArrayUInt: define (id, value.asArrayuInt()); break; case TpArrayInt64: define (id, value.asArrayInt64()); break; case TpArrayFloat: define (id, value.asArrayFloat()); break; case TpArrayDouble: define (id, value.asArrayDouble()); break; case TpArrayComplex: define (id, value.asArrayComplex()); break; case TpArrayDComplex: define (id, value.asArrayDComplex()); break; case TpArrayString: define (id, value.asArrayString()); break; case TpOther: // An untyped array is handled as an Int array. define (id, value.asArrayInt()); break; default: throw AipsError ("RecordInterface::defineFromValueHolder - " "unknown data type"); break; } } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Containers/RecordDesc.cc000066400000000000000000000072341476623553700206720ustar00rootroot00000000000000//# RecordDesc.cc: Description of the fields in a Record object //# Copyright (C) 1995,1996,1998,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ostream& RecordDesc::put (ostream &os) const { Int i; // There must be a better way to handle indentation, e.g. a manipulator. // If control leaves abnormally the indentation might be wrong. static Int indentLevel = -1; indentLevel++; String indentation; for (i=0; i < indentLevel*4; i++) { indentation += " "; } Int n = nfields(); for (i=0; i < n; i++) { os << indentation << i << " " << name(i) << " : "; if (isSubRecord(i)) { os << "SUBRECORD" << endl; os << subRecord(i); } else if (isTable(i)) { os << "TableDesc " << tableDescName(i) << endl; } else { os << type(i); if (isArray(i)) { os << shape(i); } os << endl; } if (! comment(i).empty()) { os << indentation << " " << comment(i) << endl; } } indentLevel--; return os; } AipsIO& RecordDesc::put (AipsIO& os) const { os.putstart ("RecordDesc", 2); // version 2 Int n = nfields(); os << n; for (Int i=0; i> n; for (Int i=0; i> name; os >> type; switch (type) { case TpRecord: os >> sub; addField (name, sub); break; case TpTable: os >> descName; addTable (name, descName); break; case TpArrayBool: case TpArrayChar: case TpArrayUChar: case TpArrayShort: case TpArrayUShort: case TpArrayInt: case TpArrayUInt: case TpArrayInt64: case TpArrayFloat: case TpArrayDouble: case TpArrayComplex: case TpArrayDComplex: case TpArrayString: os >> shape; addField (name, DataType(type), shape); break; default: addField (name, DataType(type)); } if (version > 1) { os >> comment; setComment (i, comment); } } os.getend(); return os; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Containers/RecordDesc.h000066400000000000000000000413021476623553700205260ustar00rootroot00000000000000//# RecordDesc.h: Description of the fields in a record object //# Copyright (C) 1995,1996,1998,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_RECORDDESC_H #define CASA_RECORDDESC_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class AipsIO; // // Description of the fields in a record object // // // // // //
      • DataType //
      • Record // // // // RecordStructure would perhaps have been the clearest possible name. However // it was decided to name it ``RecordDesc'' to use a compatible naming // convention with other classes in the system, such as TableDesc. This class // Describes the structure of a Record. // // // // RecordDesc describes the structure of Record // objects. A Record consists of a number of fields. A RecordDesc describes // those fields by assigning to each one: //
          //
        • A name for the field. //
        • A type from the DataType // enum. //
        • A shape if the field is an array. //
        • A RecordDesc if the field is itself a Record (the Record is an // hierarchical structure). //
        // Only one field with a given name is allowed (although fields in subrecords // may have the same name as a field in a parent or child Record). // // Field indices are zero relative, i.e. they range from 0 to // nfields()-1. //
        // // // See the example in the description of the // Record class. // // // // It is useful to be able to create many new objects with the same structure // as some other, without necessarily cloning it by copying all the values. // A ``Description'' type is necessary to do this (e.g., shape for an Array). // // // // //
      • Should the strategy wrt. field names be changed (not used in // field description equality, must be unique at a given level?). //
      • Perhaps we should be able to more conveniently change the description // of an existing field. // class RecordDesc { public: // Writes/reads the RecordDesc to/from an output stream. // friend ostream& operator<< (ostream& os, const RecordDesc& desc); friend AipsIO& operator<< (AipsIO& os, const RecordDesc& desc); friend AipsIO& operator>> (AipsIO& os, RecordDesc& desc); // // Create a description with no fields. RecordDesc(); // Create a description which is a copy of other. RecordDesc (const RecordDesc& other); // Replace this description with other. RecordDesc& operator= (const RecordDesc& other); ~RecordDesc(); // Add scalar, array, sub-record, or table field. // If of array type, the shape is set to [-1], which indicates a // variable sized array. // If of sub-record type, the sub-record is free format. // Returns the number of fields in the description. uInt addField (const String& fieldName, DataType dataType); // Add an array field of the indicated type. The DataType is promoted // from a scalar type to an array type if necessary, e.g., // TpInt ->TpArrayInt. Returns the number of fields in // the description. // A shape of [-1] indicates a variable shape. uInt addField (const String& fieldName, DataType scalarOrArrayType, const IPosition& shape); // Add a Record field to the description. This allows hierarchical // descriptions to be developed. Returns the number of fields in the // description. uInt addField (const String& fieldName, const RecordDesc& subDesc); // Add a Table field to the description. The Table description has the // given name. Returns the number of fields in the description. //
        // When a table is put in a record field, it is checked if the name // of its description matches this name. If this name is empty, it // matches any table description. // // Note that not all record types are able to instantiate a table field. // E.g. TableRecord can instantiate // it, while Record cannot and throws an // exception when a record description containing a table field is used. // uInt addTable (const String& fieldName, const String& tableDescName); // Get the comment for this field. const String& comment (Int whichField) const; // Set the comment for this field. void setComment (Int whichField, const String& comment); // Set the shape for this field. // An exception will be thrown if the field is no array. void setShape (Int whichField, const IPosition& shape); // Merge a single field from other. If allowDuplicates is True, silently // throw away fields if one with the same name and type already exists, // otherwise an exception is thrown. Conflicting types always cause an // exception. Returns the number of fields in the description. uInt mergeField (const RecordDesc& other, Int whichFieldFromOther, RecordInterface::DuplicatesFlag DuplicateAction = RecordInterface::ThrowOnDuplicates); // Add all the fields from another RecordDesc to the current objects. // It returns the new number of fields. uInt merge (const RecordDesc& other, RecordInterface::DuplicatesFlag DuplicateAction = RecordInterface::ThrowOnDuplicates); // Remove the given field from the description. // It returns the new number of fields. uInt removeField (Int whichField); // Rename the given field. void renameField (const String& newName, Int whichField); // Returns the index of the field named fieldName. Returns -1 if fieldName // does not exist. Int fieldNumber (const String& fieldName) const; // Number of fields in the description. uInt nfields() const; // What is the type of the given field. Returns TpRecord if the field is // a sub-Record. DataType type (Int whichField) const; // What is the name of the given field. const String& name (Int whichField) const; // Create a name for a field defined by index as *i (similar to glish). // It takes care that the resulting name is unique by adding a suffix _j // when needed. String makeName (Int whichField) const; // Make the given name unique by adding a suffix _j when needed. // j is the minimal number needed to make it unique. String uniqueName (const String& name) const; // Returns True if whichField is an array. Bool isArray (Int whichField) const; // Returns True if whichField is a scalar. Bool isScalar (Int whichField) const; // Returns True if whichField is a sub-record. Bool isSubRecord (Int whichField) const; // Returns True if whichField is a table. Bool isTable (Int whichField) const; // What is the shape of the given field. Returns [1] if the field is a // scalar, table or, sub-record, [-1] if it is a variable length array, // and the actual shape for a fixed length array. const IPosition& shape (Int whichField) const; // What is the name of the table description. // Returns an empty string when the field is no table. const String& tableDescName (Int whichField) const; // If whichField is a sub-record return its description. // Otherwise an exception is thrown. // The non-const version is named differently to prevent accidental // use of the non-const version. // const RecordDesc& subRecord (Int whichField) const; RecordDesc& rwSubRecord (Int whichField); // // This and other compare equal if the field types and shapes are identical // (recursively if there are described sub-records). // The field names are not used. //
        Use function isEqual if names are important, but order is not. // Bool operator== (const RecordDesc& other) const; Bool operator!= (const RecordDesc& other) const; // // Test if this description conforms the other. // It is NOT doing it recursively, thus is does not check if // sub-records are conforming. //
        This is used by Record, to see if another record can be assigned // to this record. Bool conform (const RecordDesc& other) const; // Test if this description equals another one. // It is equal if the number of fields is equal and all field names in // this description occur in the other too. The order of the fields // is not important. //
        The flag equalDataTypes is set to True if the data types // of all fields match. //
        Use function operator== if order and types are important, // but names are not. Bool isEqual (const RecordDesc& other, Bool& equalDataTypes) const; // Test if this description is a subset of another one. // It is similar to isEqual above. Bool isSubset (const RecordDesc& other, Bool& equalDataTypes) const; // Test if this description is a strict subset of another one, thus // if it is a subset and not equal. Bool isStrictSubset (const RecordDesc& other, Bool& equalDataTypes) const; // Test if this description is a superset of another one. Bool isSuperset (const RecordDesc& other, Bool& equalDataTypes) const; // Test if this description is a strict superset of another one, thus // if it is a superset and not equal. Bool isStrictSuperset (const RecordDesc& other, Bool& equalDataTypes) const; // Test if the set of field names in this and other record description // is disjoint (i.e. if they do not share names). Bool isDisjoint (const RecordDesc& other) const; private: // Writes/reads the RecordDesc to/from an output stream. // ostream& put (ostream& os) const; AipsIO& put (AipsIO& os) const; AipsIO& get (AipsIO& os); // // Use a copy-on-write pointer to the RecordDescRep. COWPtr desc_p; }; inline RecordDesc::RecordDesc() : desc_p (new RecordDescRep) {} inline RecordDesc::RecordDesc (const RecordDesc& other) : desc_p (other.desc_p) {} inline RecordDesc& RecordDesc::operator= (const RecordDesc& other) { if (this != &other) { desc_p = other.desc_p; } return *this; } inline RecordDesc::~RecordDesc() {} inline uInt RecordDesc::addField (const String& fieldName, DataType dataType) { return desc_p.rwRef().addField (fieldName, dataType); } inline uInt RecordDesc::addField (const String& fieldName, DataType scalarOrArrayType, const IPosition& shape) { return desc_p.rwRef().addArray (fieldName, scalarOrArrayType, shape); } inline uInt RecordDesc::addField (const String& fieldName, const RecordDesc& subDesc) { return desc_p.rwRef().addRecord (fieldName, subDesc); } inline uInt RecordDesc::addTable (const String& fieldName, const String& tableDescName) { return desc_p.rwRef().addTable (fieldName, tableDescName); } inline const String& RecordDesc::comment (Int whichField) const { return desc_p.ref().comment (whichField); } inline void RecordDesc::setComment (Int whichField, const String& comment) { desc_p.rwRef().setComment (whichField, comment); } inline void RecordDesc::setShape (Int whichField, const IPosition& shape) { desc_p.rwRef().setShape (whichField, shape); } inline uInt RecordDesc::mergeField (const RecordDesc& other, Int whichFieldFromOther, RecordInterface::DuplicatesFlag duplicateAction) { return desc_p.rwRef().mergeField (other.desc_p.ref(), whichFieldFromOther, duplicateAction); } inline uInt RecordDesc::merge (const RecordDesc& other, RecordInterface::DuplicatesFlag duplicateAction) { return desc_p.rwRef().merge (other.desc_p.ref(), duplicateAction); } inline uInt RecordDesc::removeField (Int whichField) { return desc_p.rwRef().removeField (whichField); } inline void RecordDesc::renameField (const String& newName, Int whichField) { desc_p.rwRef().renameField (newName, whichField); } inline Int RecordDesc::fieldNumber (const String& fieldName) const { return desc_p.ref().fieldNumber (fieldName); } inline uInt RecordDesc::nfields() const { return desc_p.ref().nfields(); } inline DataType RecordDesc::type (Int whichField) const { return desc_p.ref().type (whichField); } inline String RecordDesc::uniqueName (const String& name) const { return desc_p.ref().uniqueName (name); } inline String RecordDesc::makeName (Int whichField) const { return desc_p.ref().makeName (whichField); } inline const String& RecordDesc::name (Int whichField) const { return desc_p.ref().name (whichField); } inline Bool RecordDesc::isArray (Int whichField) const { return desc_p.ref().isArray (whichField); } inline Bool RecordDesc::isScalar (Int whichField) const { return desc_p.ref().isScalar (whichField); } inline Bool RecordDesc::isSubRecord (Int whichField) const { return desc_p.ref().isSubRecord (whichField); } inline Bool RecordDesc::isTable (Int whichField) const { return desc_p.ref().isTable (whichField); } inline const IPosition& RecordDesc::shape (Int whichField) const { return desc_p.ref().shape (whichField); } inline const String& RecordDesc::tableDescName (Int whichField) const { return desc_p.ref().tableDescName (whichField); } inline const RecordDesc& RecordDesc::subRecord (Int whichField) const { return desc_p.ref().subRecord (whichField); } inline RecordDesc& RecordDesc::rwSubRecord (Int whichField) { return desc_p.rwRef().subRecord (whichField); } inline Bool RecordDesc::operator== (const RecordDesc& other) const { return desc_p.ref() == other.desc_p.ref(); } inline Bool RecordDesc::operator!= (const RecordDesc& other) const { return desc_p.ref() != other.desc_p.ref(); } inline Bool RecordDesc::conform (const RecordDesc& other) const { return desc_p.ref().conform (other.desc_p.ref()); } inline Bool RecordDesc::isEqual (const RecordDesc& other, Bool& equalDataTypes) const { return desc_p.ref().isEqual (other.desc_p.ref(), equalDataTypes); } inline Bool RecordDesc::isSubset (const RecordDesc& other, Bool& equalDataTypes) const { return desc_p.ref().isSubset (other.desc_p.ref(), equalDataTypes); } inline Bool RecordDesc::isStrictSubset (const RecordDesc& other, Bool& equalDataTypes) const { return desc_p.ref().isStrictSubset (other.desc_p.ref(), equalDataTypes); } inline Bool RecordDesc::isSuperset (const RecordDesc& other, Bool& equalDataTypes) const { return other.desc_p.ref().isSubset (desc_p.ref(), equalDataTypes); } inline Bool RecordDesc::isStrictSuperset (const RecordDesc& other, Bool& equalDataTypes) const { return other.desc_p.ref().isStrictSubset (desc_p.ref(), equalDataTypes); } inline Bool RecordDesc::isDisjoint (const RecordDesc& other) const { return desc_p.ref().isDisjoint (other.desc_p.ref()); } inline ostream& operator<< (ostream& os, const RecordDesc& desc) { return desc.put (os); } inline AipsIO& operator<< (AipsIO& os, const RecordDesc& desc) { return desc.put (os); } inline AipsIO& operator>> (AipsIO& os, RecordDesc& desc) { return desc.get (os); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Containers/RecordDescRep.cc000066400000000000000000000345141476623553700213420ustar00rootroot00000000000000//# RecordDescRep.cc: Description of the fields in a Record object //# Copyright (C) 1996,1997,1998,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN RecordDescRep::RecordDescRep() : n_p(0), types_p(0), names_p(0), sub_records_p(0), shapes_p(0), is_array_p(0), tableDescNames_p(0), comments_p(0) { // Nothing } RecordDescRep::RecordDescRep (const RecordDescRep& other) : n_p(0), types_p(0), names_p(0), sub_records_p(0), shapes_p(0), is_array_p(0), tableDescNames_p(0), comments_p(0) { copy_other (other); } RecordDescRep& RecordDescRep::operator= (const RecordDescRep& other) { if (this != &other) { copy_other (other); } return *this; } RecordDescRep::~RecordDescRep() { for (uInt i=0; i= 0) { throw (AipsError ("RecordDesc::addField() - field " + fieldName + " already has been defined")); } increment_length(); uInt n = n_p - 1; types_p[n] = type; names_p[n] = fieldName; name_map_p.insert (std::make_pair(fieldName, n)); sub_records_p[n] = 0; is_array_p[n] = False; shapes_p[n].resize(1); shapes_p[n] = IPosition(1,1); } uInt RecordDescRep::addField (const String& fieldName, DataType type) { addFieldName (fieldName, type); if (type == TpRecord) { sub_records_p[n_p - 1] = new RecordDesc; } else if (type != TpTable) { addFieldAny (type); } return n_p; } void RecordDescRep::addFieldAny (DataType type) { uInt n = n_p - 1; switch(type) { case TpBool: case TpChar: case TpUChar: case TpShort: case TpUShort: case TpInt: case TpUInt: case TpInt64: case TpFloat: case TpDouble: case TpComplex: case TpDComplex: case TpString: break; case TpArrayBool: case TpArrayChar: case TpArrayUChar: case TpArrayShort: case TpArrayUShort: case TpArrayInt: case TpArrayUInt: case TpArrayInt64: case TpArrayFloat: case TpArrayDouble: case TpArrayComplex: case TpArrayDComplex: case TpArrayString: shapes_p[n] = IPosition(1,-1); is_array_p[n] = True; break; default: removeField (n); throw (AipsError ("RecordDesc::addField(const String& fieldName, " "DataType type) - unknown datatype")); } } uInt RecordDescRep::addArray (const String& fieldName, DataType type, const IPosition& shape) { addFieldName (fieldName, type); addFieldArray (type, shape); return n_p; } void RecordDescRep::addFieldArray (DataType type, const IPosition& shape) { uInt n = n_p - 1; shapes_p[n].resize(shape.nelements()); shapes_p[n] = shape; is_array_p[n] = True; switch(type) { case TpBool: case TpArrayBool: types_p[n] = TpArrayBool; break; case TpChar: case TpArrayChar: types_p[n] = TpArrayChar; break; case TpUChar: case TpArrayUChar: types_p[n] = TpArrayUChar; break; case TpShort: case TpArrayShort: types_p[n] = TpArrayShort; break; case TpUShort: case TpArrayUShort: types_p[n] = TpArrayUShort; break; case TpInt: case TpArrayInt: types_p[n] = TpArrayInt; break; case TpUInt: case TpArrayUInt: types_p[n] = TpArrayUInt; break; case TpInt64: case TpArrayInt64: types_p[n] = TpArrayInt64; break; case TpFloat: case TpArrayFloat: types_p[n] = TpArrayFloat; break; case TpDouble: case TpArrayDouble: types_p[n] = TpArrayDouble; break; case TpComplex: case TpArrayComplex: types_p[n] = TpArrayComplex; break; case TpDComplex: case TpArrayDComplex: types_p[n] = TpArrayDComplex; break; case TpString: case TpArrayString: types_p[n] = TpArrayString; break; default: removeField (n); throw (AipsError ("RecordDesc::addField(const String& fieldName, " "DataType type) - unknown datatype (must be array" "or ordinary scalar type)")); } } uInt RecordDescRep::addRecord (const String& fieldName, const RecordDesc& subDesc) { addFieldName (fieldName, TpRecord); sub_records_p[n_p - 1] = new RecordDesc(subDesc); AlwaysAssert (sub_records_p[n_p - 1] != 0, AipsError); return n_p; } uInt RecordDescRep::addTable (const String& fieldName, const String& tableDescName) { addFieldName (fieldName, TpTable); tableDescNames_p[n_p - 1] = tableDescName; return n_p; } const String& RecordDescRep::comment (Int whichField) const { AlwaysAssert (whichField>=0 && whichField < Int(n_p), AipsError); return comments_p[whichField]; } void RecordDescRep::setComment (Int whichField, const String& comment) { AlwaysAssert (whichField>=0 && whichField < Int(n_p), AipsError); comments_p[whichField] = comment; } void RecordDescRep::setShape (Int whichField, const IPosition& shape) { AlwaysAssert (whichField>=0 && whichField < Int(n_p), AipsError); AlwaysAssert (isArray(whichField), AipsError); shapes_p[whichField] = shape; } uInt RecordDescRep::mergeField (const RecordDescRep& other, Int whichField, int duplicateAction) { AlwaysAssert (whichField>=0 && whichField < Int(other.nfields()), AipsError); String newName = other.name (whichField); Int duplicateNumber = fieldNumber (newName); if (duplicateNumber >= 0) { switch (duplicateAction) { case RecordInterface::SkipDuplicates: return nfields(); case RecordInterface::ThrowOnDuplicates: throw (AipsError ("RecordDesc::mergeField - duplicate in other")); case RecordInterface::OverwriteDuplicates: removeField (duplicateNumber); // We'll add it back below break; case RecordInterface::RenameDuplicates: newName = uniqueName (newName); break; default: AlwaysAssert ((0), AipsError); // NOTREACHED } } addRepField (other, newName, whichField); return nfields(); } void RecordDescRep::addRepField (const RecordDescRep& other, const String& newName, Int whichField) { if (other.isScalar (whichField)) { addField (newName, other.type(whichField)); } else if (other.isArray (whichField)) { addArray (newName, other.type(whichField), other.shape(whichField)); } else if (other.isTable (whichField)) { addTable (newName, other.tableDescName(whichField)); } else if (other.isSubRecord (whichField)) { addRecord (newName, other.subRecord(whichField)); } else { AlwaysAssert (0, AipsError); // NOTREACHED } comments_p[n_p-1] = other.comment (whichField); } uInt RecordDescRep::merge (const RecordDescRep& other, int duplicateAction) { for (uInt i=0; i < other.nfields(); i++) { mergeField (other, i, duplicateAction); } return nfields(); } uInt RecordDescRep::removeField (Int whichField) { AlwaysAssert (whichField>=0 && whichField < Int(n_p), AipsError); if (sub_records_p[whichField]) { delete sub_records_p[whichField]; sub_records_p[whichField] = 0; } n_p--; // Remove the field from the name map. // Decrement the field number of all fields following it. name_map_p.erase (names_p[whichField]); for (auto& x : name_map_p) { if (x.second > whichField) { x.second -= 1; } } types_p.remove (whichField); names_p.remove (whichField); sub_records_p.remove (whichField); shapes_p.remove (whichField); is_array_p.remove (whichField); tableDescNames_p.remove (whichField); comments_p.remove (whichField); return n_p; } void RecordDescRep::renameField (const String& newName, Int whichField) { AlwaysAssert (whichField>=0 && whichField < Int(n_p), AipsError); Int inx = name_map_p[names_p[whichField]]; name_map_p.erase (names_p[whichField]); name_map_p.insert (std::make_pair(newName, inx)); names_p[whichField] = newName; } void RecordDescRep::setShape (const IPosition& shape, Int whichField) { AlwaysAssert (whichField>=0 && whichField < Int(n_p), AipsError); shapes_p[whichField].resize (shape.nelements()); shapes_p[whichField] = shape; } Int RecordDescRep::fieldNumber (const String& fieldName) const { std::map::const_iterator iter = name_map_p.find (fieldName); return (iter == name_map_p.end() ? -1 : iter->second); } String RecordDescRep::makeName (Int whichField) const { char strc[13]; snprintf(strc, sizeof(strc), "*%i", whichField+1); return uniqueName (strc); } String RecordDescRep::uniqueName (const String& name) const { String newName = name; char strc[16]; // should be plenty large... int n = 0; while (fieldNumber(newName) >= 0) { ++n; snprintf(strc, sizeof(strc), "_%i", n); newName = name + strc; } return newName; } RecordDesc& RecordDescRep::subRecord (Int whichField) { AlwaysAssert (isSubRecord(whichField), AipsError); return *sub_records_p[whichField]; } Bool RecordDescRep::conform (const RecordDescRep& other) const { if (this == &other) { return True; } uInt n = nfields(); if (n != other.nfields()) { return False; } for (uInt i=0; i < n; i++) { if (type(i) != other.type(i)) { return False; } if (! shapes_p[i].isEqual (other.shapes_p[i])) { return False; } if (tableDescNames_p[i] != tableDescNames_p[i]) { return False; } if (sub_records_p[i]) { if (! other.sub_records_p[i]) { return False; } } } return True; } Bool RecordDescRep::operator== (const RecordDescRep& other) const { if (this == &other) { return True; } if (!conform (other)) { return False; } // Now check recursively if the sub-records conform. uInt n = nfields(); for (uInt i=0; i other.nfields()) { return False; } return allExist (other, equalDataTypes); } Bool RecordDescRep::isStrictSubset (const RecordDescRep& other, Bool& equalDataTypes) const { equalDataTypes = False; if (nfields() >= other.nfields()) { return False; } return allExist (other, equalDataTypes); } Bool RecordDescRep::allExist (const RecordDescRep& other, Bool& equalDataTypes) const { equalDataTypes = True; uInt n = nfields(); for (uInt i=0; i= 0) { return False; // name exists in other } } return True; } void RecordDescRep::copy_other (const RecordDescRep& other) { uInt i; // First, we need to free up the storage of any extant sub records for (i=0; i < n_p ; i++) { if (sub_records_p[i]) { delete sub_records_p[i]; sub_records_p[i] = 0; } } // Then copy n_p = other.n_p; types_p = other.types_p; names_p = other.names_p; name_map_p = other.name_map_p; shapes_p = other.shapes_p; is_array_p = other.is_array_p; tableDescNames_p = other.tableDescNames_p; comments_p = other.comments_p; sub_records_p = other.sub_records_p; // Now clone them. As an alternative, we could have used a counted pointer. for (i=0; i < n_p; i++) { if (sub_records_p[i]) { sub_records_p[i] = new RecordDesc(*sub_records_p[i]); AlwaysAssert(sub_records_p[i] != 0, AipsError); } } } void RecordDescRep::increment_length() { n_p++; if (n_p > types_p.nelements()) { uInt newSize = 2*n_p; types_p.resize (newSize); names_p.resize (newSize); shapes_p.resize (newSize); sub_records_p.resize (newSize); is_array_p.resize (newSize); tableDescNames_p.resize (newSize); comments_p.resize (newSize); // This is to shut up tools that note when you read an unset // value. IPosition scalarShape(1,1); for (uInt i=n_p; i < types_p.nelements(); i++) { types_p[i] = 0; sub_records_p[i] = 0; is_array_p[i] = False; shapes_p[i].resize (scalarShape.nelements()); shapes_p[i] = scalarShape; // names_p, etc. is already set since the default ctor is called // for all its elements when new String[] is called. The // joys of types with constructors vs. built-in types... } } } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Containers/RecordDescRep.h000066400000000000000000000301271476623553700212000ustar00rootroot00000000000000//# RecordDescRep.h: Representation of a RecordDesc //# Copyright (C) 1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_RECORDDESCREP_H #define CASA_RECORDDESCREP_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class RecordDesc; class AipsIO; // // Representation of a RecordDesc // // // // // //
      • DataType //
      • RecordDesc // // // Rep is an often used abbreviation for representation. // Thus RecordDescRep is the representation of a RecordDesc. // // // RecordDescRep is used by RecordDesc // to implement its copy-on-write semantics. RecordDesc is the interface // to the user, while RecordDescRep contains the actual implementation. // See RecordDesc for a more detailed // description of a record description. // // // See the example in the description of the // Record class. // // // RecordDescRep is needed to make copy-on-write semantics possible in // class RecordDesc. // // //
      • Should the strategy wrt. field names be changed (not used in // field description equality, must be unique at a given level?). //
      • Perhaps we should be able to more conveniently change the description // of an existing field. // class RecordDescRep { public: // Create a description with no fields. RecordDescRep(); // Create a description which is a copy of other. RecordDescRep (const RecordDescRep& other); // Replace this description with other. RecordDescRep& operator= (const RecordDescRep& other); virtual ~RecordDescRep(); // Add scalar or array field. If of array type, the shape is set to [-1], // which indicates a variable sized array. Returns the number of fields in // the description. uInt addField (const String& fieldName, DataType scalarOrArrayType); // Add an array field of the indicated type. The DataType is promoted // from a scalar type to an array type if necessary, e.g., // TpInt ->TpArrayInt. Returns the number of fields in // the description. uInt addArray (const String& fieldName, DataType scalarOrArrayType, const IPosition& shape); // Add a Record field to the description. This allows hierarchical // descriptions to be developed. Returns the number of fields in the // description. uInt addRecord (const String& fieldName, const RecordDesc& subDesc); // Add a Table field to the description. The Table description has the // given name. Returns the number of fields in the description. uInt addTable (const String& fieldName, const String& tableDescName); // Get the comment for this field. const String& comment (Int whichField) const; // Set the comment for this field. void setComment (Int whichField, const String& comment); // Set the shape for this field. // An exception will be thrown if the field is no array. void setShape (Int whichField, const IPosition& shape); // Merge a single field from other. If allowDuplicates is True, silently // throw away fields if one with the same name and type already exists, // otherwise an exception is thrown. Conflicting types always cause an // exception. Returns the number of fields in the description. uInt mergeField (const RecordDescRep& other, Int whichFieldFromOther, int duplicateAction); // Add all the fields from another RecordDescRep to the current objects. uInt merge (const RecordDescRep& other, int duplicateAction); // Remove the given field from the description. virtual uInt removeField (Int whichField); // Rename the given field. virtual void renameField (const String& newName, Int whichField); // Returns the index of the field named fieldName. Returns -1 if fieldName // does not exist. Int fieldNumber (const String& fieldName) const; // Number of fields in the description. uInt nfields() const; // What is the type of the given field. Returns TpRecord if the field is // a sub-Record. DataType type (Int whichField) const; // What is the name of the given field. const String& name (Int whichField) const; // Create a name for a field defined by index as *i (similar to glish). // It takes care that the resulting name is unique by adding a suffix _j // when needed. String makeName (Int whichField) const; // Make the given name unique by adding a suffix _j when needed. // j is the minimal number needed to make it unique. String uniqueName (const String& name) const; // Returns True if whichField is an array. Bool isArray (Int whichField) const; // Returns True if whichField is a scalar. Bool isScalar (Int whichField) const; // Returns True if whichField is a sub-record. Bool isSubRecord (Int whichField) const; // Returns True if whichField is a table. Bool isTable (Int whichField) const; // What is the shape of the given field. Returns [1] if the field is a // scalar, table or, sub-record, [-1] if it is a variable length array, // and the actual shape for a fixed length array. const IPosition& shape (Int whichField) const; // What is the name of the table description associated with a table. const String& tableDescName (Int whichField) const; // If whichField is a sub-record with a description, // return its description. Otherwise an exception is thrown. // const RecordDesc& subRecord (Int whichField) const; RecordDesc& subRecord (Int whichField); // // // This and other compare equal if the field types and shapes are identical // (recursively if there are described sub-records or tables). // The field names are not used. Bool operator== (const RecordDescRep& other) const; Bool operator!= (const RecordDescRep& other) const; // // Test if this description conforms the other. // It is similar to operator==. However, a subrecord in that description // always conforms an arbitrary (i.e. empty) subrecord in this // description. //
        This is used by Record, to see if another record can be assigned // to this record. Bool conform (const RecordDescRep& other) const; // Test if this description equals another one. // It is equal if the number of fields is equal and all field names in // this description occur in the other too. The order of the fields // is not important. //
        The flag equalDataTypes is set to True if the data types // of all fields match. //
        Use function operator== if order and types are important, // but names are not. Bool isEqual (const RecordDescRep& other, Bool& equalDataTypes) const; // Test if this description is a subset of another one. // It is similar to isEqual above. Bool isSubset (const RecordDescRep& other, Bool& equalDataTypes) const; // Test if this description is a strict subset of another one, thus // if it is a subset and not equal. Bool isStrictSubset (const RecordDescRep& other, Bool& equalDataTypes) const; // Test if the set of field names in this and other record description // is disjoint (i.e. if they do not share names). Bool isDisjoint (const RecordDescRep& other) const; protected: // Add a field name and its type. // It checks if the name is unique and it extends the various blocks // using increment_length. void addFieldName (const String& fieldName, DataType type); // Add a field from another Record description. // This is used by the merge functions. virtual void addRepField (const RecordDescRep& other, const String& newName, Int whichField); // Add the field info. These are helper functions for the add functions // and can be used in derived classes too. // void addFieldAny (DataType scalarOrArrayType); void addFieldArray (DataType scalarOrArrayType, const IPosition& shape); // // Set the shape (for a derived class). void setShape (const IPosition& shape, Int whichField); // Helper functions // virtual void increment_length(); void copy_other (const RecordDescRep& other); // private: // Test if all fields are part of the other description. // The flag equalDataTypes is set to True if the data types of the // fields in both descriptions are the same. Bool allExist (const RecordDescRep&, Bool& equalDataTypes) const; // Number of fields in the description. uInt n_p; // The DataType of each field. Block types_p; // The name of each field. Block names_p; // The description of the subrecords. Null if the field is not a subrecord. // This isn't the most efficient representation. If this is ever an issue // we could calculate these, or store them in one Block, or implement // copy-on-write semantics. PtrBlock sub_records_p; // The shape of the field [1] for scalars and sub-records. Block shapes_p; // True if the corresponding field is an array. Block is_array_p; // Table description name for table fields. Block tableDescNames_p; // Comments for each field. Block comments_p; // Mapping of field name to field number. std::map name_map_p; }; inline uInt RecordDescRep::nfields() const { return n_p; } inline DataType RecordDescRep::type (Int whichField) const { return DataType(types_p[whichField]); } inline const String& RecordDescRep::name (Int whichField) const { return names_p[whichField]; } inline const IPosition& RecordDescRep::shape (Int whichField) const { return shapes_p[whichField]; } inline Bool RecordDescRep::isArray (Int whichField) const { return is_array_p[whichField]; } inline Bool RecordDescRep::isScalar (Int whichField) const { return isScalarFun (DataType(types_p[whichField])); } inline Bool RecordDescRep::isSubRecord (Int whichField) const { return (types_p[whichField] == TpRecord); } inline Bool RecordDescRep::isTable (Int whichField) const { return (types_p[whichField] == TpTable); } inline const RecordDesc& RecordDescRep::subRecord (Int whichField) const { //# The cast to non-const is completely safe. return ((RecordDescRep*)this)->subRecord (whichField); } inline const String& RecordDescRep::tableDescName (Int whichField) const { return tableDescNames_p[whichField]; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Containers/RecordField.h000066400000000000000000000213641476623553700207010ustar00rootroot00000000000000//# RecordField.h: Access to an individual field in a record //# Copyright (C) 1995,1996,1997 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_RECORDFIELD_H #define CASA_RECORDFIELD_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableRecord; class Table; // // Access to an individual field in a record. // // // // // //
      • RecordInterface. // // // RecordFieldPtr indicates that an object of this type is // pointing to a field in a record. // // // RecordFieldPtr allows access to the fields in a record object. // A record object is an object of a class derived from // RecordInterface. // RecordFieldPtr objects can only be instantiated for types `T' // which are valid fields of a record object (e.g. Int, float, String, // Record, TableRecord). It can, however, NOT be instantiated for // a Table field, because Table fields are accessed indirectly via a // TableKeyword object. Table fields have to be accessed directly // through the TableRecord interface. //

        // Internally, a RecordFieldPtr stores a Record pointer and // field number. Therefore, if the order of fields in a Record is modified, // or if the Record is restructured, a RecordFieldPtr is invalidated and should // no longer be used. //

        // The RecordFieldPtr is pointer-like in the sense that it points to an // object that is physically inside of another object (the enclosing // record object). // Access to the value is obtained via the dereference operator // (operator*()) to emphasize the pointer like nature of these // classes. //
        // An alternative way to get access to the values is using the // functions define and get. Note that in // // RecordFieldPtr > field (record, fieldNumber); // Array value; // *field = value; // field.define (value); // // the assignment (in line 3) and define (in line 4) are not equivalent. // The assignment uses the normal Array assignment, thus it takes the // Array conformance rules into account (an assign is only possible when // the new array value conforms the current array value or when the current // array value is empty). // On the other hand, define does not take the current array value into // account. Thus an array value can always be redefined. //
        // However, note that if the field is defined with a non-fixed shape in // the record description, a value must always conform that shape (in // case of assignment as well as in case of define). // // // See the example in the Record class. // // // RecordFieldPtr provides a fast way to access the data in a record. // template class RecordFieldPtr { public: // This object does not point to any field, i.e. // this->isAttached() == False; RecordFieldPtr(); // Attach this field pointer to the given field. If it does not exist // an exception is thrown. // RecordFieldPtr (RecordInterface& record, Int whichField); RecordFieldPtr (RecordInterface& record, const RecordFieldId&); // // Change our pointer to the supplied field. If it doesn't exist an // exception is thrown. // void attachToRecord (RecordInterface& record, Int whichField); void attachToRecord (RecordInterface& record, const RecordFieldId&); // // Point to no field in any Record. void detach(); // Provide access to the field's value. // // To be sure a const function is called, it is best to use get(). // For a non-const object, a non-const function is called, even if // used as an rvalue. // // T& operator*(); const T& operator*() const { return get(); } const T& get() const { return *get_typed_ptr(parent_p, fieldNumber_p); } // // Store a value in the field using redefinition. // Define differs from assignment w.r.t. arrays. // For define a variable shaped array is deleted first with the // effect that array conformance rules are not applied for them. void define (const T& value); // Get the comment of this field. const String& comment() const; // Set the comment for this field. void setComment (const String& comment); // Return the fieldnumber of this field. Int fieldNumber() const {return fieldNumber_p;} // Return the name of the field. String name() const {return parent_p->name (fieldNumber_p);} // Is this field pointer attached to a valid record? Operations which // might cause it to become detached are: //

          //
        1. Destruction of the Record //
        2. Restructuring of the record. //
        3. Explicit call of the detach() member. //
        //# This inherited function is shown for documentation purposes. Bool isAttached() const {return parent_p;} private: static const T* get_typed_ptr(RecordInterface* record, Int fieldNumber); RecordInterface* parent_p; Int fieldNumber_p; }; // // Read-Only access to an individual field from a Record. // // // // // //
      • RecordRecordFieldPtr. // // // // This class is entirely like // RecordFieldPtr, except that it only allows Read-Only // access to fields in a Record. The documentation for that class should // be consulted. //

        // Note that RecordFieldPtr is not inherited from RORecordFieldPtr, // because that would give problems with the function attachToRecord. // It would allow RecordFieldPtr to attach to a const RecordInterface object. // template class RORecordFieldPtr { public: RORecordFieldPtr() {} RORecordFieldPtr (const RecordInterface& record, Int whichField) : fieldPtr_p((RecordInterface&)record, whichField) {} RORecordFieldPtr (const RecordInterface& record, const RecordFieldId& id) : fieldPtr_p((RecordInterface&)record, id) {} RORecordFieldPtr (const RecordFieldPtr& other) : fieldPtr_p(other) {} RORecordFieldPtr (const RORecordFieldPtr& other) : fieldPtr_p(other.fieldPtr_p) {} RORecordFieldPtr& operator= (const RORecordFieldPtr& other) { fieldPtr_p = other.fieldPtr_p; return *this;} ~RORecordFieldPtr() {} void attachToRecord (const RecordInterface& record, Int whichField) { fieldPtr_p.attachToRecord ((RecordInterface&)record, whichField); } void attachToRecord (const RecordInterface& record, const RecordFieldId& id) { fieldPtr_p.attachToRecord ((RecordInterface&)record, id); } const T& operator*() const {return *fieldPtr_p;} const T& get() const {return fieldPtr_p.get();} const String& comment() const {return fieldPtr_p.comment();} Int fieldNumber() const {return fieldPtr_p.fieldNumber();} void detach() {fieldPtr_p.detach(); } Bool isAttached() const {return fieldPtr_p.isAttached(); } private: RecordFieldPtr fieldPtr_p; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/casa/Containers/RecordField.tcc000066400000000000000000000074711476623553700212260ustar00rootroot00000000000000//# RecordField.cc: Access to an individual field from a record //# Copyright (C) 1995,1996,1997,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_RECORDFIELD_TCC #define CASA_RECORDFIELD_TCC #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template RecordFieldPtr::RecordFieldPtr() : parent_p (nullptr), fieldNumber_p(-1) { // Nothing } template RecordFieldPtr::RecordFieldPtr (RecordInterface& record, Int whichField) { attachToRecord (record, whichField); } template RecordFieldPtr::RecordFieldPtr (RecordInterface& record, const RecordFieldId& id) { attachToRecord (record, record.idToNumber (id)); } template void RecordFieldPtr::attachToRecord (RecordInterface& record, const RecordFieldId& id) { attachToRecord (record, record.idToNumber (id)); } template void RecordFieldPtr::attachToRecord (RecordInterface& record, Int whichField) { parent_p = &record; fieldNumber_p = whichField; get(); // check type } template void RecordFieldPtr::detach() { parent_p = nullptr; fieldNumber_p = -1; } template T& RecordFieldPtr::operator*() { parent_p->makeUnique(); return const_cast(get()); } template<> inline const Table* RecordFieldPtr::get_typed_ptr(RecordInterface* record, Int fieldNumber) { return static_cast(record->get_pointer(fieldNumber, TpOther)); } template<> inline const Record* RecordFieldPtr::get_typed_ptr(RecordInterface* record, Int fieldNumber) { return static_cast(record->get_pointer(fieldNumber, TpRecord, "Record")); } template<> inline const TableRecord* RecordFieldPtr::get_typed_ptr(RecordInterface* record, Int fieldNumber) { return static_cast(record->get_pointer(fieldNumber, TpRecord, "TableRecord")); } template inline const T* RecordFieldPtr::get_typed_ptr(RecordInterface* record, Int fieldNumber) { return static_cast(record->get_pointer(fieldNumber, whatType())); } template inline void RecordFieldPtr::define (const T& value) { parent_p->defineDataField (fieldNumber_p, whatType(), &value); } template<> inline void RecordFieldPtr::define (const TableRecord& value) { parent_p->defineDataField (fieldNumber_p, TpRecord, &value); } template const String& RecordFieldPtr::comment() const { return parent_p->comment (fieldNumber_p); } template void RecordFieldPtr::setComment (const String& comment) { parent_p->setComment (fieldNumber_p, comment); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Containers/RecordField2Writer.cc000066400000000000000000000037561476623553700223230ustar00rootroot00000000000000//# RecordField2Writer.cc: non-templated implementation of RecordFieldWriter.h //# Copyright (C) 1996,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN RecordFieldWriter::~RecordFieldWriter() { // Nothing } void MultiRecordFieldWriter::addWriter(RecordFieldWriter *fromNew) { AlwaysAssert(fromNew, AipsError); uInt which = writers_p.nelements(); writers_p.resize(which + 1); writers_p[which] = fromNew; } void MultiRecordFieldWriter::copy() { uInt n = writers_p.nelements(); for (uInt i=0; iwriteField(); } } MultiRecordFieldWriter::~MultiRecordFieldWriter() { for (uInt i=0; i namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Everything is inlined } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Containers/RecordFieldId.h000066400000000000000000000101641476623553700211520ustar00rootroot00000000000000//# RecordFieldId.h: The identification of a record field //# Copyright (C) 1995,1996 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_RECORDFIELDID_H #define CASA_RECORDFIELDID_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class RecordInterface; // // The identification of a record field. // // // // // //
      • RecordInterface. // // // RecordFieldId gives the identification of a field in a record. // // // This class provides the user to identify a field in a record. // Identification can be done by means of the field name or by means // of its field number. //
        For the programmer the most convenient way is probably the name, // because that is the natural identification. // However, identification by means of field number is much faster // and could be used when it is known. //
        // // // void someFunc (const Record& record) // { // float value1 = record.asfloat ("name"); // identify by name // float value2 = record.asfloat (0); // identify by number // } // // // // This class makes it possible that many functions in Record classes // have to be defined only once. The constructors of RecordFieldId // make it possible that a number and a string are automatically // converted, so the user does not have to instantiate a RecordFieldId // object explicitly. // //# //# class RecordFieldId { public: // Construct it from a field number. RecordFieldId (Int fieldNumber); // Construct it from a field name. // RecordFieldId (const String& name); RecordFieldId (const std::string& name); RecordFieldId (const Char* name); // // Get the field number. Int fieldNumber() const; // Get the field name. const String& fieldName() const; // Is the id given by name? Bool byName() const; private: Bool byName_p; Int number_p; String name_p; }; inline RecordFieldId::RecordFieldId (Int fieldNumber) : byName_p (False), number_p (fieldNumber) {} inline RecordFieldId::RecordFieldId (const String& fieldName) : byName_p (True), number_p (-1), name_p (fieldName) {} inline RecordFieldId::RecordFieldId (const std::string& fieldName) : byName_p (True), number_p (-1), name_p (fieldName) {} inline RecordFieldId::RecordFieldId (const Char* fieldName) : byName_p (True), number_p (-1), name_p (fieldName) {} inline Int RecordFieldId::fieldNumber() const { return number_p; } inline const String& RecordFieldId::fieldName() const { return name_p; } inline Bool RecordFieldId::byName() const { return byName_p; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Containers/RecordFieldWriter.h000066400000000000000000000115311476623553700220710ustar00rootroot00000000000000//# RecordFieldWriter.h: Various copiers to move fields between records. //# Copyright (C) 1996,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_RECORDFIELDWRITER_H #define CASA_RECORDFIELDWRITER_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Record field writer. Base class for the copiers. // // // // // // These classes write values to a field or fields in a record. // // // // These classes are used in the ms2sdfits conversion code. // It might be better if they were moved there. // // // // It was useful to set up a number of copiers and invoke them as appropriate // via a single function call. Some copiers may be more complicate than a // direct field to field copy. // // // //
      • Either make this generally useful here or move them out of containers //
      • fully document this // class RecordFieldWriter { public: virtual ~RecordFieldWriter(); virtual void writeField() = 0; }; // Record field copier. Copies field to field as is. // // // // // // Copies a field from a record to another record with a field // of the same type. // // // This type of copy can be inlined. // template class RecordFieldCopier : public RecordFieldWriter { public: RecordFieldCopier(RecordInterface &outRecord, RecordFieldId whichOutField, const RecordInterface &inRecord, RecordFieldId whichInField); void copy() {*out_p = outType(*in_p);} virtual void writeField(); private: RecordFieldPtr out_p; RORecordFieldPtr in_p; }; // Unequal shape copier. // // // // // // Copy fields where the two fields fields do not have the same shape, // however, the number of elements must match. Copying is done element // by element in vector order. // // // // Sometimes the shapes need to change even though the number of elements // stays the same. // // template class UnequalShapeCopier : public RecordFieldWriter { public: UnequalShapeCopier(RecordInterface &outRecord, RecordFieldId whichOutField, const RecordInterface &inRecord, RecordFieldId whichInField); virtual void writeField(); private: RecordFieldPtr > out_p; RORecordFieldPtr > in_p; }; // Multi field writer. Copy many fields with a single call. // // // // // // This class contains other copiers and copies multiple fields at a time. // // // // It was useful to set up a number of copiers and invoke them as appropriate // via a single function call. // // class MultiRecordFieldWriter { public: void addWriter(RecordFieldWriter *fromNew); void copy(); ~MultiRecordFieldWriter(); private: // Make faster by having the RecordFieldCopiers split out so straight copying // is inline. PtrBlock writers_p; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/casa/Containers/RecordFieldWriter.tcc000066400000000000000000000051571476623553700224220ustar00rootroot00000000000000//# RecordFieldWriter.cc: templated implementation of RecordFieldWriter.h //# Copyright (C) 1996,1997,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_RECORDFIELDWRITER_TCC #define CASA_RECORDFIELDWRITER_TCC #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template RecordFieldCopier::RecordFieldCopier(RecordInterface &outRecord, RecordFieldId whichOutField, const RecordInterface &inRecord, RecordFieldId whichInField) : out_p(outRecord, whichOutField), in_p(inRecord, whichInField) { // Nothing } template void RecordFieldCopier::writeField() { copy(); } template UnequalShapeCopier::UnequalShapeCopier(RecordInterface &outRecord, RecordFieldId whichOutField, const RecordInterface &inRecord, RecordFieldId whichInField) : out_p(outRecord, whichOutField), in_p(inRecord, whichInField) { // Nothing } template void UnequalShapeCopier::writeField() { uInt n = (*out_p).nelements(); AlwaysAssert(n == (*in_p).nelements(), AipsError); Bool deleteOut, deleteIn; T *out = (*out_p).getStorage(deleteOut); const T *in = (*in_p).getStorage(deleteIn); objcopy(out, in, n); (*out_p).putStorage(out, deleteOut); (*in_p).freeStorage(in, deleteIn); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Containers/RecordInterface.cc000066400000000000000000000516311476623553700217140ustar00rootroot00000000000000//# RecordInterface.cc: Abstract base class for a hierarchical collection of named fields of various types //# Copyright (C) 1996,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN RecordInterface::RecordInterface () : checkFunction_p (0), checkArgument_p (0), type_p (Variable) {} RecordInterface::RecordInterface (RecordType type, CheckFieldFunction* funcPtr, const void* checkArgument) : checkFunction_p (funcPtr), checkArgument_p (checkArgument), type_p (type) {} RecordInterface::RecordInterface (const RecordInterface& other) : checkFunction_p (other.checkFunction_p), checkArgument_p (other.checkArgument_p), type_p (other.type_p) {} RecordInterface& RecordInterface::operator= (const RecordInterface& other) { checkFunction_p = other.checkFunction_p; checkArgument_p = other.checkArgument_p; type_p = other.type_p; return *this; } RecordInterface::~RecordInterface() { } void RecordInterface::throwIfFixed() const { if (isFixed()) { throw (AipsError ("Record cannot be changed (fixed structure)")); } } void RecordInterface::checkName (const String& fieldName, DataType type) const { if (checkFunction_p != 0) { String message; if (! checkFunction_p (fieldName, type, checkArgument_p, message)) { throw (AipsError ("Record field " + fieldName + " cannot be added: " + message)); } } } RecordDesc RecordInterface::description() const { return getDescription(); } Int RecordInterface::newIdToNumber (const RecordFieldId& id) const { if (id.byName()) { return fieldNumber (id.fieldName()); } Int nfield = nfields(); if (id.fieldNumber() > nfield) { throw (AipsError ("RecordInterface::define - " "new fieldNumber exceeds #fields")); } if (id.fieldNumber() == nfield) { return -1; } return id.fieldNumber(); } Int RecordInterface::idToNumber (const RecordFieldId& id) const { if (! id.byName()) { return id.fieldNumber(); } Int whichField = fieldNumber (id.fieldName()); if (whichField < 0) { throw (AipsError ("RecordInterface: field " + id.fieldName() + " is unknown")); } return whichField; } String RecordInterface::name (const RecordFieldId& id) const { return description().name (idToNumber (id)); } IPosition RecordInterface::shape (const RecordFieldId& id) const { Int whichField = idToNumber (id); switch (type (whichField)) { case TpArrayBool: return asArrayBool(whichField).shape(); case TpArrayUChar: return asArrayuChar(whichField).shape(); case TpArrayShort: return asArrayShort(whichField).shape(); case TpArrayInt: return asArrayInt(whichField).shape(); case TpArrayUInt: return asArrayuInt(whichField).shape(); case TpArrayInt64: return asArrayInt64(whichField).shape(); case TpArrayFloat: return asArrayFloat(whichField).shape(); case TpArrayDouble: return asArrayDouble(whichField).shape(); case TpArrayComplex: return asArrayComplex(whichField).shape(); case TpArrayDComplex: return asArrayDComplex(whichField).shape(); case TpArrayString: return asArrayString(whichField).shape(); default: break; } return IPosition (1,1); } void RecordInterface::defineField (const RecordFieldId& id, DataType type, const void* value) { defineField (id, type, IPosition(), False, value); } void RecordInterface::defineField (const RecordFieldId& id, DataType type, const IPosition& shape, Bool fixedShape, const void* value) { Int whichField = newIdToNumber (id); if (whichField < 0) { throwIfFixed(); String name; if (id.byName()) { name = id.fieldName(); }else{ name = description().makeName (id.fieldNumber()); } checkName (name, type); addDataField (name, type, shape, fixedShape, value); }else{ defineDataField (whichField, type, value); } } void RecordInterface::define (const RecordFieldId& id, Bool value) { defineField (id, TpBool, &value); } void RecordInterface::define (const RecordFieldId& id, uChar value) { defineField (id, TpUChar, &value); } void RecordInterface::define (const RecordFieldId& id, Short value) { defineField (id, TpShort, &value); } void RecordInterface::define (const RecordFieldId& id, Int value) { defineField (id, TpInt, &value); } void RecordInterface::define (const RecordFieldId& id, uInt value) { defineField (id, TpUInt, &value); } void RecordInterface::define (const RecordFieldId& id, Int64 value) { defineField (id, TpInt64, &value); } void RecordInterface::define (const RecordFieldId& id, float value) { defineField (id, TpFloat, &value); } void RecordInterface::define (const RecordFieldId& id, double value) { defineField (id, TpDouble, &value); } void RecordInterface::define (const RecordFieldId& id, const Complex& value) { defineField (id, TpComplex, &value); } void RecordInterface::define (const RecordFieldId& id, const DComplex& value) { defineField (id, TpDComplex, &value); } void RecordInterface::define (const RecordFieldId& id, const String& value) { defineField (id, TpString, &value); } void RecordInterface::define (const RecordFieldId& id, const Array& value, Bool fixedShape) { defineField (id, TpArrayBool, value.shape(), fixedShape, &value); } void RecordInterface::define (const RecordFieldId& id, const Array& value, Bool fixedShape) { defineField (id, TpArrayUChar, value.shape(), fixedShape, &value); } void RecordInterface::define (const RecordFieldId& id, const Array& value, Bool fixedShape) { defineField (id, TpArrayShort, value.shape(), fixedShape, &value); } void RecordInterface::define (const RecordFieldId& id, const Array& value, Bool fixedShape) { defineField (id, TpArrayInt, value.shape(), fixedShape, &value); } void RecordInterface::define (const RecordFieldId& id, const Array& value, Bool fixedShape) { defineField (id, TpArrayUInt, value.shape(), fixedShape, &value); } void RecordInterface::define (const RecordFieldId& id, const Array& value, Bool fixedShape) { defineField (id, TpArrayInt64, value.shape(), fixedShape, &value); } void RecordInterface::define (const RecordFieldId& id, const Array& value, Bool fixedShape) { defineField (id, TpArrayFloat, value.shape(), fixedShape, &value); } void RecordInterface::define (const RecordFieldId& id, const Array& value, Bool fixedShape) { defineField (id, TpArrayDouble, value.shape(), fixedShape, &value); } void RecordInterface::define (const RecordFieldId& id, const Array& value, Bool fixedShape) { defineField (id, TpArrayComplex, value.shape(), fixedShape, &value); } void RecordInterface::define (const RecordFieldId& id, const Array& value, Bool fixedShape) { defineField (id, TpArrayDComplex, value.shape(), fixedShape, &value); } void RecordInterface::define (const RecordFieldId& id, const Array& value, Bool fixedShape) { defineField (id, TpArrayString, value.shape(), fixedShape, &value); } void RecordInterface::get (const RecordFieldId& id, Bool& value) const { value = asBool (id); } void RecordInterface::get (const RecordFieldId& id, uChar& value) const { value = asuChar (id); } void RecordInterface::get (const RecordFieldId& id, Short& value) const { value = asShort (id); } void RecordInterface::get (const RecordFieldId& id, Int& value) const { value = asInt (id); } void RecordInterface::get (const RecordFieldId& id, uInt& value) const { value = asuInt (id); } void RecordInterface::get (const RecordFieldId& id, Int64& value) const { value = asInt64 (id); } void RecordInterface::get (const RecordFieldId& id, float& value) const { value = asFloat (id); } void RecordInterface::get (const RecordFieldId& id, double& value) const { value = asDouble (id); } void RecordInterface::get (const RecordFieldId& id, Complex& value) const { value = asComplex (id); } void RecordInterface::get (const RecordFieldId& id, DComplex& value) const { value = asDComplex (id); } void RecordInterface::get (const RecordFieldId& id, String& value) const { value = asString (id); } void RecordInterface::get (const RecordFieldId& id, Array& value) const { Array array = toArrayBool (id); value.resize (array.shape()); value = array; } void RecordInterface::get (const RecordFieldId& id, Array& value) const { Array array = toArrayuChar (id); value.resize (array.shape()); value = array; } void RecordInterface::get (const RecordFieldId& id, Array& value) const { Array array = toArrayShort (id); value.resize (array.shape()); value = array; } void RecordInterface::get (const RecordFieldId& id, Array& value) const { Array array = toArrayInt (id); value.resize (array.shape()); value = array; } void RecordInterface::get (const RecordFieldId& id, Array& value) const { Array array = toArrayuInt (id); value.resize (array.shape()); value = array; } void RecordInterface::get (const RecordFieldId& id, Array& value) const { Array array = toArrayInt64 (id); value.resize (array.shape()); value = array; } void RecordInterface::get (const RecordFieldId& id, Array& value) const { Array array = toArrayFloat (id); value.resize (array.shape()); value = array; } void RecordInterface::get (const RecordFieldId& id, Array& value) const { Array array = toArrayDouble (id); value.resize (array.shape()); value = array; } void RecordInterface::get (const RecordFieldId& id, Array& value) const { Array array = toArrayComplex (id); value.resize (array.shape()); value = array; } void RecordInterface::get (const RecordFieldId& id, Array& value) const { Array array = toArrayDComplex (id); value.resize (array.shape()); value = array; } void RecordInterface::get (const RecordFieldId& id, Array& value) const { Array array = toArrayString (id); value.resize (array.shape()); value = array; } Bool RecordInterface::asBool (const RecordFieldId& id) const { Int whichField = idToNumber (id); DataType dataType = type (whichField); switch (dataType) { case TpBool: break; case TpInt: return *(const Int*)get_pointer (whichField, TpInt); default: throw (AipsError ("RecordInterface::asBool - invalid data type")); } return *(const Bool*)get_pointer (whichField, TpBool); } uChar RecordInterface::asuChar (const RecordFieldId& id) const { Int whichField = idToNumber (id); DataType dataType = type (whichField); switch (dataType) { case TpUChar: break; case TpShort: return *(const Short*)get_pointer (whichField, TpShort); case TpInt: return *(const Int*)get_pointer (whichField, TpInt); case TpUInt: return *(const uInt*)get_pointer (whichField, TpUInt); default: throw (AipsError ("RecordInterface::asuChar - invalid data type")); } return *(const uChar*)get_pointer (whichField, TpUChar); } Short RecordInterface::asShort (const RecordFieldId& id) const { Int whichField = idToNumber (id); DataType dataType = type (whichField); switch (dataType) { case TpUChar: return *(const uChar*)get_pointer (whichField, TpUChar); case TpShort: break; case TpInt: return *(const Int*)get_pointer (whichField, TpInt); case TpUInt: return *(const uInt*)get_pointer (whichField, TpUInt); default: throw (AipsError ("RecordInterface::asShort - invalid data type")); } return *(const Short*)get_pointer (whichField, TpShort); } Int RecordInterface::asInt (const RecordFieldId& id) const { Int whichField = idToNumber (id); DataType dataType = type (whichField); switch (dataType) { case TpUChar: return *(const uChar*)get_pointer (whichField, TpUChar); case TpShort: return *(const Short*)get_pointer (whichField, TpShort); case TpInt: break; case TpUInt: return *(const uInt*)get_pointer (whichField, TpUInt); case TpInt64: return *(const Int64*)get_pointer (whichField, TpInt64); default: throw (AipsError ("RecordInterface::asInt - invalid data type")); } return *(const Int*)get_pointer (whichField, TpInt); } uInt RecordInterface::asuInt (const RecordFieldId& id) const { Int whichField = idToNumber (id); DataType dataType = type (whichField); switch (dataType) { case TpUChar: return *(const uChar*)get_pointer (whichField, TpUChar); case TpShort: return *(const Short*)get_pointer (whichField, TpShort); case TpInt: return *(const Int*)get_pointer (whichField, TpInt); case TpUInt: break; case TpInt64: return *(const Int64*)get_pointer (whichField, TpInt64); default: throw (AipsError ("RecordInterface::asuInt - invalid data type")); } return *(const uInt*)get_pointer (whichField, TpUInt); } Int64 RecordInterface::asInt64 (const RecordFieldId& id) const { Int whichField = idToNumber (id); DataType dataType = type (whichField); switch (dataType) { case TpUChar: return *(const uChar*)get_pointer (whichField, TpUChar); case TpShort: return *(const Short*)get_pointer (whichField, TpShort); case TpInt: return *(const Int*)get_pointer (whichField, TpInt); case TpUInt: return *(const uInt*)get_pointer (whichField, TpUInt); case TpInt64: break; default: throw (AipsError ("RecordInterface::asInt64 - invalid data type")); } return *(const Int64*)get_pointer (whichField, TpInt64); } float RecordInterface::asFloat (const RecordFieldId& id) const { Int whichField = idToNumber (id); DataType dataType = type (whichField); switch (dataType) { case TpUChar: return *(const uChar*)get_pointer (whichField, TpUChar); case TpShort: return *(const Short*)get_pointer (whichField, TpShort); case TpInt: return *(const Int*)get_pointer (whichField, TpInt); case TpUInt: return *(const uInt*)get_pointer (whichField, TpUInt); case TpInt64: return *(const Int64*)get_pointer (whichField, TpInt64); case TpFloat: break; case TpDouble: return *(const double*)get_pointer (whichField, TpDouble); default: throw (AipsError ("RecordInterface::asFloat - invalid data type")); } return *(const float*)get_pointer (whichField, TpFloat); } double RecordInterface::asDouble (const RecordFieldId& id) const { Int whichField = idToNumber (id); DataType dataType = type (whichField); switch (dataType) { case TpUChar: return *(const uChar*)get_pointer (whichField, TpUChar); case TpShort: return *(const Short*)get_pointer (whichField, TpShort); case TpInt: return *(const Int*)get_pointer (whichField, TpInt); case TpUInt: return *(const uInt*)get_pointer (whichField, TpUInt); case TpInt64: return *(const Int64*)get_pointer (whichField, TpInt64); case TpFloat: return *(const float*)get_pointer (whichField, TpFloat); case TpDouble: break; default: throw (AipsError ("RecordInterface::asDouble - invalid data type")); } return *(const double*)get_pointer (whichField, TpDouble); } Complex RecordInterface::asComplex (const RecordFieldId& id) const { Int whichField = idToNumber (id); DataType dataType = type (whichField); switch (dataType) { case TpUChar: return *(const uChar*)get_pointer (whichField, TpUChar); case TpShort: return *(const Short*)get_pointer (whichField, TpShort); case TpInt: return *(const Int*)get_pointer (whichField, TpInt); case TpUInt: return *(const uInt*)get_pointer (whichField, TpUInt); case TpInt64: return *(const Int64*)get_pointer (whichField, TpInt64); case TpFloat: return *(const float*)get_pointer (whichField, TpFloat); case TpDouble: return *(const double*)get_pointer (whichField, TpDouble); case TpComplex: break; case TpDComplex: { DComplex dc = *(const DComplex*)get_pointer (whichField, TpDComplex); return Complex(dc.real(), dc.imag()); } default: throw (AipsError ("RecordInterface::asComplex - invalid data type")); } return *(const Complex*)get_pointer (whichField, TpComplex); } DComplex RecordInterface::asDComplex (const RecordFieldId& id) const { Int whichField = idToNumber (id); DataType dataType = type (whichField); switch (dataType) { case TpUChar: return *(const uChar*)get_pointer (whichField, TpUChar); case TpShort: return *(const Short*)get_pointer (whichField, TpShort); case TpInt: return *(const Int*)get_pointer (whichField, TpInt); case TpUInt: return *(const uInt*)get_pointer (whichField, TpUInt); case TpInt64: return *(const Int64*)get_pointer (whichField, TpInt64); case TpFloat: return *(const float*)get_pointer (whichField, TpFloat); case TpDouble: return *(const double*)get_pointer (whichField, TpDouble); case TpComplex: { const Complex* val = (const Complex*)get_pointer (whichField, TpComplex); return DComplex (val->real(), val->imag()); } case TpDComplex: break; default: throw (AipsError ("RecordInterface::asDComplex - invalid data type")); } return *(const DComplex*)get_pointer (whichField, TpDComplex); } const String& RecordInterface::asString (const RecordFieldId& id) const { Int whichField = idToNumber (id); return *(const String*)get_pointer (whichField, TpString); } const Array& RecordInterface::asArrayBool (const RecordFieldId& id) const { Int whichField = idToNumber (id); return *(const Array*)get_pointer (whichField, TpArrayBool); } const Array& RecordInterface::asArrayuChar (const RecordFieldId& id) const { Int whichField = idToNumber (id); return *(const Array*)get_pointer (whichField, TpArrayUChar); } const Array& RecordInterface::asArrayShort (const RecordFieldId& id) const { Int whichField = idToNumber (id); return *(const Array*)get_pointer (whichField, TpArrayShort); } const Array& RecordInterface::asArrayInt (const RecordFieldId& id) const { Int whichField = idToNumber (id); return *(const Array*)get_pointer (whichField, TpArrayInt); } const Array& RecordInterface::asArrayuInt (const RecordFieldId& id) const { Int whichField = idToNumber (id); return *(const Array*)get_pointer (whichField, TpArrayUInt); } const Array& RecordInterface::asArrayInt64 (const RecordFieldId& id) const { Int whichField = idToNumber (id); return *(const Array*)get_pointer (whichField, TpArrayInt64); } const Array& RecordInterface::asArrayFloat (const RecordFieldId& id) const { Int whichField = idToNumber (id); return *(const Array*)get_pointer (whichField, TpArrayFloat); } const Array& RecordInterface::asArrayDouble (const RecordFieldId& id) const { Int whichField = idToNumber (id); return *(const Array*)get_pointer (whichField, TpArrayDouble); } const Array& RecordInterface::asArrayComplex (const RecordFieldId& id) const { Int whichField = idToNumber (id); return *(const Array*)get_pointer (whichField, TpArrayComplex); } const Array& RecordInterface::asArrayDComplex (const RecordFieldId& id) const { Int whichField = idToNumber (id); return *(const Array*)get_pointer (whichField, TpArrayDComplex); } const Array& RecordInterface::asArrayString (const RecordFieldId& id) const { Int whichField = idToNumber (id); return *(const Array*)get_pointer (whichField, TpArrayString); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Containers/RecordInterface.h000066400000000000000000000607351476623553700215630ustar00rootroot00000000000000//# RecordInterface.h: Abstract base class for Record classes //# Copyright (C) 1996,1997,1998,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_RECORDINTERFACE_H #define CASA_RECORDINTERFACE_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class RecordDesc; class ValueHolder; class IPosition; // // Abstract base class for Record classes // // // // //# //# // // ``Record'' is a widely used term in both programming languages and data // structures to denote an imhogeneous set of fields. An alternative would // have been to name it structure, which would have perhaps been // a clearer name for C++ programmers. //
        // RecordInterface denotes that this class defines the common interface to // possible Record classes. //
        // // A Record is an heterogeneous, hierarchical, collection of named fields. The // fields may be of scalar type, array type, a Table or a Record. This latter // feature is what makes the Record a (potentially) hierarchical type. //

        // RecordInterface is the abstract base class for various Record classes. // At the moment three Record classes exist: //

          //
        • Record //
        • TableRecord //
        // Presently, the scalar types are chosen to be compatible with the native // types of the Table system, viz: Bool, uChar, Short, Int, uInt, Int64, // Float, Double, Complex, DComplex, String. // Arrays of all these types are also available. // It is fairly straightforward to extend this set if necessary, although it // will result in more template instantiations with the current implementation. //

        // Each field has an integral index, which ranges between 0 and // nfields() - 1. The values of a field can be manipulated // in two ways: //

          //
        1. Through the get and put functions in this class. // They are easy to use and support type promotion. // However, they are a bit less efficient than the second way. //
        2. Through the class // RecordFieldPtr. // This is a bit less convenient. However, it is more efficient if // the same field is accessed multiple times. //
        // The structure of a record can be fixed or variable. // If fixed, it is not possible to change the structure once the // record has been instantiated. If variable, the record can be // restructured or fields can be added/removed. //
        // When a field gets added, it is possible to check if its name and // type are valid by means of the CheckFunction callback. This is // for instance used by the table system to assure that keywords // and columns in a table do not have the same name. //

        // Arrays in a record description can be fixed or variable shaped. // If fixed shaped, only arrays with that shape can be stored // in that field in the record. If variable shaped, any array // can be stored. //
        However, note there is a difference between assign and define. // Assign invokes the array assignment operator which checks for // conformance. Thus even for variable shaped arrays, the new array // must conform the exisitng one when using assign. Define simply replaces // the array, thus for variable shaped arrays ay array shape will do. //

        // RecordFieldPtr objects attached to a Record have to be notified when // the Record is deleted or changed. // The RecordInterface class provides the hooks for this via the // Notice system. It is derived from // NoticeSource. The class // RecordNotice is for the messages. // // // This common base class provides a common interface to the various // Record classes. // Furthermore it is needed for the class RecordFieldPtr. // Finally it provides the hooks for the notification in case the // record structure changes. // // // //

      • A record reference class, which contains some fields from another // record, would likely be useful. This would be analagous to a // subarray sliced from an existing array. // class RecordInterface { public: // Define the flag telling if a Record has a fixed or // variable structure. enum RecordType { // Record has a fixed structure; that is, no fields can // be added or removed once the Record is created. Fixed, // Record has a variable structure; after Record creation // fields can be added or removed at will. Variable}; // Define the Duplicates flag for the function merge in the various // record classes. // This function merges the fields from that record (description) // into this one. // DuplicatesFlag determines what to do if a field already exists. enum DuplicatesFlag { // Rename a name from the other set to name_n, // where n is the first positive number making the name unique. RenameDuplicates, // Skip duplicate names from the other set. SkipDuplicates, // Overwrite the value of a duplicate keyword // This will also happen if their types differ. OverwriteDuplicates, // Throw an exception. ThrowOnDuplicates}; // Define the signature of the add callback function. // This function is called when a field is added to the record // (thus also when a Record is constructed from a RecordDesc). // The function can check if the name and/or data type are valid. // The extra argument is the argument given to the Record constructor // which can be used to pass non-Record information. // The function should return False if name or data type is invalid. // In that case it can fill the message string, which will be added // to the message in the thrown exception. typedef Bool CheckFieldFunction (const String& fieldName, DataType dataType, const void* extraArgument, String& message); // The default constructor creates an empty record with a variable // structure. RecordInterface(); // Create a record with no fields. // The callback function is called when a field is added to the Record. // That function can check the name and of data type of the new field // (for instance, the Table system uses it to ensure that table columns // and keywords have different names). RecordInterface (RecordType type, CheckFieldFunction* funcPtr, const void* checkArgument); // Copy constructor (copy semantics). RecordInterface (const RecordInterface& other); // Assignment (copy semantics). // This only assigns the RecordInterface object itself, // thus not the data in a derived class. // To do that the function assign below can be used. RecordInterface& operator= (const RecordInterface& other); // Destruct the record. // All attached RecordFieldPtr objects are notified to detach themselves. virtual ~RecordInterface(); // Make a copy of this object. virtual RecordInterface* clone() const = 0; // Assign that RecordInterface object to this one. // Unlike operator= it copies all data in the derived // class. virtual void assign (const RecordInterface& that) = 0; // Is the Record structure fixed (i.e. impossible to restructure or // to add or remove fields)? Bool isFixed() const; // How many fields does this structure have? // virtual uInt nfields() const = 0; uInt size() const { return nfields(); } // // Is the record empty? bool empty() const { return size() == 0; } // Get the field number from the field name. // -1 is returned if the field name is unknown. virtual Int fieldNumber (const String& fieldName) const = 0; // Get the field number for the given field id. // It throws an exception if id is unrecognized (e.g. an unknown name). Int idToNumber (const RecordFieldId&) const; // Test if a field name exists. //# Is here for backward compatibility with KeywordSet. Bool isDefined (const String& fieldName) const; // Get the data type of this field (as defined in DataType.h). // virtual DataType type (Int whichField) const = 0; DataType dataType (const RecordFieldId&) const; // // Get the name of this field. String name (const RecordFieldId&) const; // Get the comment for this field. virtual const String& comment (const RecordFieldId&) const = 0; // Set the comment for this field. virtual void setComment (const RecordFieldId&, const String& comment) = 0; // Get the actual shape of this field. // It returns [1] for non-array fields. IPosition shape (const RecordFieldId&) const; // Get the description of this record. RecordDesc description() const; // Change the structure of this Record to contain the fields in // newDescription. After calling restructure, description() == // newDescription. Any existing RecordFieldPtr objects are // invalidated (their isAttached() members return False) after // this call. //
        If the new description contains subrecords, those subrecords // will be restructured if recursive=True is given. // Otherwise the subrecord is a variable empty record. // Subrecords will be variable if their description is empty (i.e. does // not contain any field), otherwise they are fixed. //
        Restructuring is not possible and an exception is thrown // if the Record has a fixed structure. virtual void restructure (const RecordDesc& newDescription, Bool recursive=True) = 0; // Remove a field from the record. // // Removing a field means that the field number of the fields following // it will be decremented. It will invalidate RecordFieldPtr's // pointing to the removed field, but no other RecordFieldPtr's. // virtual void removeField (const RecordFieldId&) = 0; // Get or define the value as a ValueHolder. // This is useful to pass around a value of any supported type. // virtual ValueHolder asValueHolder (const RecordFieldId&) const; virtual void defineFromValueHolder (const RecordFieldId&, const ValueHolder&); // // Define a value for the given field. // Array conformance rules will not be applied for variable shaped arrays. // If the field and value data type mismatch, type promotion // of scalars will be done if possible. If not possible, an exception // is thrown. //
        // If the field does not exist, it will be added to the record. // This results in an exception for fixed structured records. // The field is checked by a possible field checking function // before it gets added. // void define (const RecordFieldId&, Bool value); void define (const RecordFieldId&, uChar value); void define (const RecordFieldId&, Short value); void define (const RecordFieldId&, Int value); void define (const RecordFieldId&, uInt value); void define (const RecordFieldId&, Int64 value); void define (const RecordFieldId&, Float value); void define (const RecordFieldId&, Double value); void define (const RecordFieldId&, const Complex& value); void define (const RecordFieldId&, const DComplex& value); void define (const RecordFieldId&, const Char* value); void define (const RecordFieldId&, const String& value); void define (const RecordFieldId&, const Array& value, Bool FixedShape = False); void define (const RecordFieldId&, const Array& value, Bool FixedShape = False); void define (const RecordFieldId&, const Array& value, Bool FixedShape = False); void define (const RecordFieldId&, const Array& value, Bool FixedShape = False); void define (const RecordFieldId&, const Array& value, Bool FixedShape = False); void define (const RecordFieldId&, const Array& value, Bool FixedShape = False); void define (const RecordFieldId&, const Array& value, Bool FixedShape = False); void define (const RecordFieldId&, const Array& value, Bool FixedShape = False); void define (const RecordFieldId&, const Array& value, Bool FixedShape = False); void define (const RecordFieldId&, const Array& value, Bool FixedShape = False); void define (const RecordFieldId&, const Array& value, Bool FixedShape = False); virtual void defineRecord (const RecordFieldId&, const RecordInterface& value, RecordType = Variable) = 0; // // Get the value of the given field. // If the field and value data type mismatch, type promotion // will be done if possible. If not possible, an exception // is thrown. // If the value argument is an array, it will be reshaped if needed. // void get (const RecordFieldId&, Bool& value) const; void get (const RecordFieldId&, uChar& value) const; void get (const RecordFieldId&, Short& value) const; void get (const RecordFieldId&, Int& value) const; void get (const RecordFieldId&, uInt& value) const; void get (const RecordFieldId&, Int64& value) const; void get (const RecordFieldId&, Float& value) const; void get (const RecordFieldId&, Double& value) const; void get (const RecordFieldId&, Complex& value) const; void get (const RecordFieldId&, DComplex& value) const; void get (const RecordFieldId&, String& value) const; void get (const RecordFieldId&, Array& value) const; void get (const RecordFieldId&, Array& value) const; void get (const RecordFieldId&, Array& value) const; void get (const RecordFieldId&, Array& value) const; void get (const RecordFieldId&, Array& value) const; void get (const RecordFieldId&, Array& value) const; void get (const RecordFieldId&, Array& value) const; void get (const RecordFieldId&, Array& value) const; void get (const RecordFieldId&, Array& value) const; void get (const RecordFieldId&, Array& value) const; void get (const RecordFieldId&, Array& value) const; // // The following functions get the value based on field name or number. // The scalar functions promote the data type if needed. It also supports // conversion of Int to Bool. //
        The array functions throw an exception if the data type mismatches. // The toArrayX function can be used for array type promotion. // Bool asBool (const RecordFieldId&) const; uChar asuChar (const RecordFieldId&) const; Short asShort (const RecordFieldId&) const; Int asInt (const RecordFieldId&) const; uInt asuInt (const RecordFieldId&) const; Int64 asInt64 (const RecordFieldId&) const; Float asFloat (const RecordFieldId&) const; Double asDouble (const RecordFieldId&) const; Complex asComplex (const RecordFieldId&) const; DComplex asDComplex(const RecordFieldId&) const; const String& asString (const RecordFieldId&) const; const Array& asArrayBool (const RecordFieldId&) const; const Array& asArrayuChar (const RecordFieldId&) const; const Array& asArrayShort (const RecordFieldId&) const; const Array& asArrayInt (const RecordFieldId&) const; const Array& asArrayuInt (const RecordFieldId&) const; const Array& asArrayInt64 (const RecordFieldId&) const; const Array& asArrayFloat (const RecordFieldId&) const; const Array& asArrayDouble (const RecordFieldId&) const; const Array& asArrayComplex (const RecordFieldId&) const; const Array& asArrayDComplex(const RecordFieldId&) const; const Array& asArrayString (const RecordFieldId&) const; virtual const RecordInterface& asRecord (const RecordFieldId&) const = 0; virtual RecordInterface& asrwRecord (const RecordFieldId&) = 0; // // Get an array while promoting the data as needed. // Int values can be converted to Bool. // A scalar value is also converted to an array. // These functions are slower than asX, but more general. // Array toArrayBool (const RecordFieldId&) const; Array toArrayuChar (const RecordFieldId&) const; Array toArrayShort (const RecordFieldId&) const; Array toArrayInt (const RecordFieldId&) const; Array toArrayuInt (const RecordFieldId&) const; Array toArrayInt64 (const RecordFieldId&) const; Array toArrayFloat (const RecordFieldId&) const; Array toArrayDouble (const RecordFieldId&) const; Array toArrayComplex (const RecordFieldId&) const; Array toArrayDComplex(const RecordFieldId&) const; Array toArrayString (const RecordFieldId&) const; void toArray (const RecordFieldId& id, Array& array) const { array.reference (toArrayBool (id)); } void toArray (const RecordFieldId& id, Array& array) const { array.reference (toArrayuChar (id)); } void toArray (const RecordFieldId& id, Array& array) const { array.reference (toArrayShort (id)); } void toArray (const RecordFieldId& id, Array& array) const { array.reference (toArrayInt (id)); } void toArray (const RecordFieldId& id, Array& array) const { array.reference (toArrayuInt (id)); } void toArray (const RecordFieldId& id, Array& array) const { array.reference (toArrayInt64 (id)); } void toArray (const RecordFieldId& id, Array& array) const { array.reference (toArrayFloat (id)); } void toArray (const RecordFieldId& id, Array& array) const { array.reference (toArrayDouble (id)); } void toArray (const RecordFieldId& id, Array& array) const { array.reference (toArrayComplex (id)); } void toArray (const RecordFieldId& id, Array& array) const { array.reference (toArrayDComplex (id)); } void toArray (const RecordFieldId& id, Array& array) const { array.reference (toArrayString (id)); } // // Get value based on field name or number. // They are here for backward compatibility with the old KeywordSet // classes and will be removed in the future. // Float asfloat (const RecordFieldId&) const; Double asdouble (const RecordFieldId&) const; const Array& asArrayfloat (const RecordFieldId&) const; const Array& asArraydouble (const RecordFieldId&) const; // // Make a unique record representation // (for copy-on-write in RecordFieldPtr). virtual void makeUnique() = 0; // Define a data field (for RecordFieldPtr). //# This function has to be public for the global defineRecordFieldPtr //# functions in RecordField.h. virtual void defineDataField (Int whichField, DataType type, const void* value) = 0; // Used by the RecordFieldPtr classes to attach to the correct field. //# This function has to be public for the global attachRecordFieldPtr //# functions in RecordField.h. // The latter function is used to attach to a Record-type field // checking if the correct Record type is used. // virtual void* get_pointer (Int whichField, DataType type) const = 0; virtual void* get_pointer (Int whichField, DataType type, const String& recordType) const = 0; // // Print the contents of the record. // Only the first maxNrValues of an array will be printed. // A value < 0 means the entire array. // friend inline std::ostream& operator<< (std::ostream& os, const RecordInterface& rec) { rec.print (os, 25, " "); return os; } virtual void print (std::ostream&, Int maxNrValues = 25, const String& indent="") const = 0; // protected: // Let the derived class add an array field with the given type, shape, // and value. virtual void addDataField (const String& name, DataType type, const IPosition& shape, Bool fixedShape, const void* value) = 0; // Check if the Record has a non-fixed structure. // If it is fixed, it throws an exception. // This can be used by other functions (like define). void throwIfFixed() const; // Check if the new field name is correct. // This is done by calling the checkFunction (if defined). // If incorrect, an exception is thrown. void checkName (const String& fieldName, DataType type) const; // Give access to the RecordType flag (write-access is needed when // a record is read back). // RecordType& recordType(); RecordType recordType() const; // // Get the field number for the given field id. // It returns -1 if an unknown name was given. Int newIdToNumber (const RecordFieldId&) const; // Add a scalar field with the given type and value. // An exception is thrown if the record structure is fixed // or if the name is invalid. void defineField (const RecordFieldId&, DataType type, const void* value); // Add an array field with the given type, shape and value. // An exception is thrown if the record structure is fixed // or if the name is invalid. void defineField (const RecordFieldId&, DataType type, const IPosition& shape, Bool fixedShape, const void* value); private: // Get the description of this record. virtual RecordDesc getDescription() const = 0; // Holds the callback function plus argument. CheckFieldFunction* checkFunction_p; const void* checkArgument_p; // Defines if the Record has a fixed structure. RecordType type_p; }; inline Bool RecordInterface::isFixed() const { return (type_p == Fixed); } inline Bool RecordInterface::isDefined (const String& fieldName) const { return (fieldNumber(fieldName) >= 0); } inline RecordInterface::RecordType& RecordInterface::recordType() { return type_p; } inline RecordInterface::RecordType RecordInterface::recordType() const { return type_p; } inline DataType RecordInterface::dataType (const RecordFieldId& id) const { return type (idToNumber(id)); } inline void RecordInterface::define (const RecordFieldId& id, const Char* value) { define (id, String(value)); } inline Float RecordInterface::asfloat (const RecordFieldId& id) const { return asFloat (id); } inline Double RecordInterface::asdouble (const RecordFieldId& id) const { return asDouble (id); } inline const Array& RecordInterface::asArrayfloat (const RecordFieldId& id) const { return asArrayFloat (id); } inline const Array& RecordInterface::asArraydouble (const RecordFieldId& id) const { return asArrayDouble (id); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Containers/RecordRep.cc000066400000000000000000001143311476623553700205370ustar00rootroot00000000000000//# RecordRep.cc: A hierarchical collection of named fields of various types //# Copyright (C) 1996,1997,1999,2000,2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include //# for memmove with gcc-4.3 namespace casacore { //# NAMESPACE CASACORE - BEGIN // Tweaked for SUN NTV compiler. Added the const_cast to give a hint to the Solaris compiler. RecordRep::RecordRep () : nused_p (0) {} RecordRep::RecordRep (const RecordDesc& description) : desc_p (description), nused_p (0) { restructure (desc_p, True); } RecordRep::RecordRep (const RecordRep& other) : desc_p (other.desc_p), nused_p (0) { restructure (desc_p, False); copy_other (other); } RecordRep& RecordRep::operator= (const RecordRep& other) { if (this != &other) { restructure (other.desc_p, False); copy_other (other); } return *this; } RecordRep::~RecordRep() { delete_myself (desc_p.nfields()); } void RecordRep::restructure (const RecordDesc& newDescription, Bool recursive) { delete_myself (desc_p.nfields()); desc_p = newDescription; nused_p = desc_p.nfields(); datavec_p.resize (nused_p); datavec_p = static_cast(0); data_p.resize (nused_p); for (uInt i=0; i= data_p.nelements()) { datavec_p.resize (nused_p + 16); data_p.resize (nused_p + 16); } datavec_p[nused_p] = 0; data_p[nused_p++] = ptr; } void RecordRep::removeDataPtr (Int index) { nused_p--; if (index < Int(nused_p)) { memmove (&datavec_p[index], &datavec_p[index+1], (nused_p-index) * sizeof(void*)); memmove (&data_p[index], &data_p[index+1], (nused_p-index) * sizeof(void*)); } } void RecordRep::removeData (Int whichField, void* ptr, void* vecptr) { DataType type = desc_p.type(whichField); if (type == TpRecord) { delete static_cast(ptr); }else{ deleteDataField (type, ptr, vecptr); } } void RecordRep::removeField (Int whichField) { removeData (whichField, data_p[whichField], datavec_p[whichField]); removeDataPtr (whichField); removeFieldFromDesc (whichField); } void RecordRep::addFieldToDesc (const String& name, DataType type, const IPosition& shape, Bool fixedShape) { if (fixedShape) { desc_p.addField (name, type, shape); }else{ desc_p.addField (name, type); } } void RecordRep::removeFieldFromDesc (Int whichField) { desc_p.removeField (whichField); } void RecordRep::addDataField (const String& name, DataType type, const IPosition& shape, Bool fixedShape, const void* data) { AlwaysAssert (type == TpBool || type == TpArrayBool || type == TpUChar || type == TpArrayUChar || type == TpShort || type == TpArrayShort || type == TpInt || type == TpArrayInt || type == TpUInt || type == TpArrayUInt || type == TpInt64 || type == TpArrayInt64 || type == TpFloat || type == TpArrayFloat || type == TpDouble || type == TpArrayDouble || type == TpComplex || type == TpArrayComplex || type == TpDComplex || type == TpArrayDComplex || type == TpString || type == TpArrayString , AipsError); addFieldToDesc (name, type, shape, fixedShape); void* ptr = createDataField (type, shape); copyDataField (type, ptr, data); addDataPtr (ptr); } void RecordRep::addField (const String& name, const Record& rec, RecordInterface::RecordType type) { // When the record is empty, it is variable structured. if (rec.nfields() == 0) { type = RecordInterface::Variable; } // When the new field is fixed, add its description too. if (type == RecordInterface::Fixed) { desc_p.addField (name, rec.description()); }else{ desc_p.addField (name, TpRecord); } // Use default ctor and assignment to be sure that the // new record gets the correct record type. Record* ptr = new Record (this, type); *ptr = rec; addDataPtr (ptr); } void RecordRep::checkShape (DataType type, const IPosition& shape, const void* value, const String& fieldName) { IPosition arrShape; switch (type) { case TpArrayBool: arrShape = static_cast*>(value)->shape(); break; case TpArrayUChar: arrShape = static_cast*>(value)->shape(); break; case TpArrayShort: arrShape = static_cast*>(value)->shape(); break; case TpArrayInt: arrShape = static_cast*>(value)->shape(); break; case TpArrayUInt: arrShape = static_cast*>(value)->shape(); break; case TpArrayInt64: arrShape = static_cast*>(value)->shape(); break; case TpArrayFloat: arrShape = static_cast*>(value)->shape(); break; case TpArrayDouble: arrShape = static_cast*>(value)->shape(); break; case TpArrayComplex: arrShape = static_cast*>(value)->shape(); break; case TpArrayDComplex: arrShape = static_cast*>(value)->shape(); break; case TpArrayString: arrShape = static_cast*>(value)->shape(); break; default: throw (AipsError ("RecordRep::checkShape")); } if (! shape.isEqual (arrShape)) { throw (ArrayConformanceError ("Record::define - fixed array conformance error for field " + fieldName)); } } void RecordRep::defineDataField (Int whichField, DataType type, const void* value) { AlwaysAssert (whichField >= 0 && whichField < Int(nused_p), AipsError); DataType descDtype = desc_p.type(whichField); if (type == descDtype) { if (type == TpRecord) { *static_cast(data_p[whichField]) = *static_cast(value); }else{ if (desc_p.isArray(whichField)) { const IPosition& shape = desc_p.shape(whichField); if (shape.nelements() > 0 && shape(0) > 0) { checkShape (type, shape, value, desc_p.name(whichField)); } } copyDataField (type, data_p[whichField], value); } } else if (isArray(type) && asScalar(type) == descDtype) { // A scalar can be defined using a single element vector. checkShape (type, IPosition(1,1), value, desc_p.name(whichField)); // Make sure there is a datavec entry. get_pointer (whichField, type); copyDataField (type, datavec_p[whichField], value); } else { throw (AipsError ("RecordRep::defineDataField - " "incorrect data type used for field " + desc_p.name(whichField))); } } void* RecordRep::createDataField (DataType type, const IPosition& shape) { IPosition arrayShape; if (shape.nelements() > 0 && shape(0) > 0) { arrayShape = shape; } switch (type) { case TpBool: { Bool* ptr = new Bool; *ptr = False; return ptr; } case TpUChar: { uChar* ptr = new uChar; *ptr = 0; return ptr; } case TpShort: { Short* ptr = new Short; *ptr = 0; return ptr; } case TpInt: { Int* ptr = new Int; *ptr = 0; return ptr; } case TpUInt: { uInt* ptr = new uInt; *ptr = 0; return ptr; } case TpInt64: { Int64* ptr = new Int64; *ptr = 0; return ptr; } case TpFloat: { float* ptr = new float; *ptr = 0.0; return ptr; } case TpDouble: { double* ptr = new double; *ptr = 0.0; return ptr; } case TpComplex: return new Complex; case TpDComplex: return new DComplex; case TpString: return new String; case TpArrayBool: { Array* ptr = new Array (arrayShape); *ptr = False; return ptr; } case TpArrayUChar: { Array* ptr = new Array (arrayShape); *ptr = 0; return ptr; } case TpArrayShort: { Array* ptr = new Array (arrayShape); *ptr = 0; return ptr; } case TpArrayInt: { Array* ptr = new Array (arrayShape); *ptr = 0; return ptr; } case TpArrayUInt: { Array* ptr = new Array (arrayShape); *ptr = 0; return ptr; } case TpArrayInt64: { Array* ptr = new Array (arrayShape); *ptr = 0; return ptr; } case TpArrayFloat: { Array* ptr = new Array (arrayShape); *ptr = 0.0; return ptr; } case TpArrayDouble: { Array* ptr = new Array (arrayShape); *ptr = 0.0; return ptr; } case TpArrayComplex: return new Array (arrayShape); case TpArrayDComplex: return new Array (arrayShape); case TpArrayString: return new Array (arrayShape); default: throw (AipsError ("RecordRep::createDataField: unknown data type " + String::toString(int(type)))); } } void RecordRep::makeDataVec (Int whichField, DataType type) { IPosition shape(1,1); switch (type) { case TpBool: datavec_p[whichField] = new Array (shape, static_cast(data_p[whichField]), SHARE); break; case TpUChar: datavec_p[whichField] = new Array (shape, static_cast(data_p[whichField]), SHARE); break; case TpShort: datavec_p[whichField] = new Array (shape, static_cast(data_p[whichField]), SHARE); break; case TpInt: datavec_p[whichField] = new Array (shape, static_cast(data_p[whichField]), SHARE); break; case TpUInt: datavec_p[whichField] = new Array (shape, static_cast(data_p[whichField]), SHARE); break; case TpInt64: datavec_p[whichField] = new Array (shape, static_cast(data_p[whichField]), SHARE); break; case TpFloat: datavec_p[whichField] = new Array (shape, static_cast(data_p[whichField]), SHARE); break; case TpDouble: datavec_p[whichField] = new Array (shape, static_cast(data_p[whichField]), SHARE); break; case TpComplex: datavec_p[whichField] = new Array (shape, static_cast(data_p[whichField]), SHARE); break; case TpDComplex: datavec_p[whichField] = new Array (shape, static_cast(data_p[whichField]), SHARE); break; case TpString: datavec_p[whichField] = new Array (shape, static_cast(data_p[whichField]), SHARE); break; default: throw (AipsError ("RecordRep::makeDataVec: unknown data type")); } } void RecordRep::delete_myself (uInt nfields) { if (nfields > nused_p) { nfields = nused_p; } for (uInt i=0; i(ptr); delete static_cast*>(vecptr); break; case TpUChar: delete static_cast(ptr); delete static_cast*>(vecptr); break; case TpShort: delete static_cast(ptr); delete static_cast*>(vecptr); break; case TpInt: delete static_cast(ptr); delete static_cast*>(vecptr); break; case TpUInt: delete static_cast(ptr); delete static_cast*>(vecptr); break; case TpInt64: delete static_cast(ptr); delete static_cast*>(vecptr); break; case TpFloat: delete static_cast(ptr); delete static_cast*>(vecptr); break; case TpDouble: delete static_cast(ptr); delete static_cast*>(vecptr); break; case TpComplex: delete static_cast(ptr); delete static_cast*>(vecptr); break; case TpDComplex: delete static_cast(ptr); delete static_cast*>(vecptr); break; case TpString: delete static_cast(ptr); delete static_cast*>(vecptr); break; case TpArrayBool: delete static_cast*>(ptr); break; case TpArrayUChar: delete static_cast*>(ptr); break; case TpArrayShort: delete static_cast*>(ptr); break; case TpArrayInt: delete static_cast*>(ptr); break; case TpArrayUInt: delete static_cast*>(ptr); break; case TpArrayInt64: delete static_cast*>(ptr); break; case TpArrayFloat: delete static_cast*>(ptr); break; case TpArrayDouble: delete static_cast*>(ptr); break; case TpArrayComplex: delete static_cast*>(ptr); break; case TpArrayDComplex: delete static_cast*>(ptr); break; case TpArrayString: delete static_cast*>(ptr); break; default: throw (AipsError ("RecordRep::deleteDataField")); } } Bool RecordRep::conform (const RecordRep& other) const { // First check (non-recursively) if the descriptions conform. if (! desc_p.conform (other.desc_p)) { return False; } // Now check for each fixed sub-record if it conforms. for (uInt i=0; i(const_cast(data_p[i])); if (thisRecord.isFixed()) { const Record& thatRecord = *static_cast(const_cast(other.data_p[i])); if (! thisRecord.conform (thatRecord)) { return False; } } } } return True; } void RecordRep::copyData (const RecordRep& other) { // Assume conform has already been called DebugAssert (conform (other), AipsError); copy_other (other); } void RecordRep::copy_other (const RecordRep& other) { for (uInt i=0; i(data_p[i]) = *static_cast(const_cast(other.data_p[i])); }else{ copyDataField (desc_p.type(i), data_p[i], other.data_p[i]); } } } void RecordRep::copyDataField (DataType type, Int whichField, const void* that) const { copyDataField (type, data_p[whichField], that); } void RecordRep::copyDataField (DataType type, void* ptr, const void* that) const { switch (type) { case TpBool: *static_cast(ptr) = *static_cast(that); break; case TpUChar: *static_cast(ptr) = *static_cast(that); break; case TpShort: *static_cast(ptr) = *static_cast(that); break; case TpInt: *static_cast(ptr) = *static_cast(that); break; case TpUInt: *static_cast(ptr) = *static_cast(that); break; case TpInt64: *static_cast(ptr) = *static_cast(that); break; case TpFloat: *static_cast(ptr) = *static_cast(that); break; case TpDouble: *static_cast(ptr) = *static_cast(that); break; case TpComplex: *static_cast(ptr) = *static_cast(that); break; case TpDComplex: *static_cast(ptr) = *static_cast(that); break; case TpString: *static_cast(ptr) = *static_cast(that); break; case TpArrayBool: static_cast*>(ptr)->resize (static_cast*>(that)->shape()); *static_cast*>(ptr) = *static_cast*>(that); break; case TpArrayUChar: static_cast*>(ptr)->resize (static_cast*>(that)->shape()); *static_cast*>(ptr) = *static_cast*>(that); break; case TpArrayShort: static_cast*>(ptr)->resize (static_cast*>(that)->shape()); *static_cast*>(ptr) = *static_cast*>(that); break; case TpArrayInt: static_cast*>(ptr)->resize (static_cast*>(that)->shape()); *static_cast*>(ptr) = *static_cast*>(that); break; case TpArrayUInt: static_cast*>(ptr)->resize (static_cast*>(that)->shape()); *static_cast*>(ptr) = *static_cast*>(that); break; case TpArrayInt64: static_cast*>(ptr)->resize (static_cast*>(that)->shape()); *static_cast*>(ptr) = *static_cast*>(that); break; case TpArrayFloat: static_cast*>(ptr)->resize (static_cast*>(that)->shape()); *static_cast*>(ptr) = *static_cast*>(that); break; case TpArrayDouble: static_cast*>(ptr)->resize (static_cast*>(that)->shape()); *static_cast*>(ptr) = *static_cast*>(that); break; case TpArrayComplex: static_cast*>(ptr)->resize (static_cast*>(that)->shape()); *static_cast*>(ptr) = *static_cast*>(that); break; case TpArrayDComplex: static_cast*>(ptr)->resize (static_cast*>(that)->shape()); *static_cast*>(ptr) = *static_cast*>(that); break; case TpArrayString: static_cast*>(ptr)->resize (static_cast*>(that)->shape()); *static_cast*>(ptr) = *static_cast*>(that); break; default: throw (AipsError ("RecordRep::copyDataField")); } } void* RecordRep::get_pointer (Int whichField, DataType type, const String& recordType) const { AlwaysAssert (recordType == "Record", AipsError); return get_pointer (whichField, type); } void* RecordRep::get_pointer (Int whichField, DataType type) const { AlwaysAssert (whichField >= 0 && whichField < Int(nused_p), AipsError); DataType descDtype = desc_p.type(whichField); if (type == descDtype) { return data_p[whichField]; } // A scalar can be returned as an array. if (! (isArray(type) && asScalar(type) == descDtype)) { throw (AipsError ("RecordRep::get_pointer - " "incorrect data type " + ValType::getTypeStr(type) + " used for field " + desc_p.name(whichField) + " with type " + ValType::getTypeStr(descDtype))); } if (datavec_p[whichField] == 0) { const_cast(this)->makeDataVec (whichField, descDtype); } return datavec_p[whichField]; } void RecordRep::mergeField (const RecordRep& other, Int whichFieldFromOther, RecordInterface::DuplicatesFlag flag) { // If the field exists and if flag tells to overwrite, // the field is removed first. if (flag == RecordInterface::OverwriteDuplicates) { Int fld = desc_p.fieldNumber (other.desc_p.name(whichFieldFromOther)); if (fld >= 0) { removeField (fld); } } // Try to add the field to the description. Int nr = desc_p.nfields(); Int nrnew = desc_p.mergeField (other.desc_p, whichFieldFromOther, flag); // It succeeded if nfields increased. // Then the value can be defined. if (nrnew > nr) { DataType type = desc_p.type (nr); void* otherPtr = other.get_pointer (whichFieldFromOther, type); void* ptr; if (type == TpRecord) { ptr = new Record (*static_cast(otherPtr)); }else{ ptr = createDataField (type, desc_p.shape(nr)); copyDataField (type, ptr, otherPtr); } addDataPtr (ptr); } } void RecordRep::merge (const RecordRep& other, RecordInterface::DuplicatesFlag flag) { Int n = other.desc_p.nfields(); for (Int i=0; i(ptr); break; case TpUChar: os << "uChar " << Int(*static_cast(ptr)); break; case TpShort: os << "Short " << *static_cast(ptr); break; case TpInt: os << "Int " << *static_cast(ptr); break; case TpUInt: os << "uInt " << *static_cast(ptr); break; case TpInt64: os << "Int64 " << *static_cast(ptr); break; case TpFloat: os << "Float " << *static_cast(ptr); break; case TpDouble: os << "Double " << *static_cast(ptr); break; case TpComplex: os << "Complex " << *static_cast(ptr); break; case TpDComplex: os << "DComplex " << *static_cast(ptr); break; case TpString: os << "String " << '"' << *static_cast(ptr) << '"'; break; case TpArrayBool: os << "Bool array with shape " << static_cast*>(ptr)->shape(); if (maxNrValues != 0) { const Array& arr = *static_cast*>(ptr); if (maxNrValues < 0) { os << endl << arr; } else { Vector vec = arr.reform (IPosition(1, arr.nelements())); if (uInt(maxNrValues+1) >= vec.nelements()) { os << endl << indent << " " << vec; } else { os << ", first values:" << endl << indent << " " << vec(Slice(0,maxNrValues-1)); } } } break; case TpArrayUChar: os << "uChar array with shape " << static_cast*>(ptr)->shape(); if (maxNrValues != 0) { const Array& arr = *static_cast*>(ptr); if (maxNrValues < 0) { os << endl << arr; } else { Vector vec = arr.reform (IPosition(1, arr.nelements())); if (uInt(maxNrValues+1) >= vec.nelements()) { os << endl << indent << " " << vec; } else { os << ", first values:" << endl << indent << " " << vec(Slice(0,maxNrValues-1)); } } } break; case TpArrayShort: os << "Short array with shape " << static_cast*>(ptr)->shape(); if (maxNrValues != 0) { const Array& arr = *static_cast*>(ptr); if (maxNrValues < 0) { os << endl << arr; } else { Vector vec = arr.reform (IPosition(1, arr.nelements())); if (uInt(maxNrValues+1) >= vec.nelements()) { os << endl << indent << " " << vec; } else { os << ", first values:" << endl << indent << " " << vec(Slice(0,maxNrValues-1)); } } } break; case TpArrayInt: os << "Int array with shape " << static_cast*>(ptr)->shape(); if (maxNrValues != 0) { const Array& arr = *static_cast*>(ptr); if (maxNrValues < 0) { os << endl << arr; } else { Vector vec = arr.reform (IPosition(1, arr.nelements())); if (uInt(maxNrValues+1) >= vec.nelements()) { os << endl << indent << " " << vec; } else { os << ", first values:" << endl << indent << " " << vec(Slice(0,maxNrValues-1)); } } } break; case TpArrayUInt: os << "uInt array with shape " << static_cast*>(ptr)->shape(); if (maxNrValues != 0) { const Array& arr = *static_cast*>(ptr); if (maxNrValues < 0) { os << endl << arr; } else { Vector vec = arr.reform (IPosition(1, arr.nelements())); if (uInt(maxNrValues+1) >= vec.nelements()) { os << endl << indent << " " << vec; } else { os << ", first values:" << endl << indent << " " << vec(Slice(0,maxNrValues-1)); } } } break; case TpArrayInt64: os << "Int64 array with shape " << static_cast*>(ptr)->shape(); if (maxNrValues != 0) { const Array& arr = *static_cast*>(ptr); if (maxNrValues < 0) { os << endl << arr; } else { Vector vec = arr.reform (IPosition(1, arr.nelements())); if (uInt(maxNrValues+1) >= vec.nelements()) { os << endl << indent << " " << vec; } else { os << ", first values:" << endl << indent << " " << vec(Slice(0,maxNrValues-1)); } } } break; case TpArrayFloat: os << "Float array with shape " << static_cast*>(ptr)->shape(); if (maxNrValues != 0) { const Array& arr = *static_cast*>(ptr); if (maxNrValues < 0) { os << endl << arr; } else { Vector vec = arr.reform (IPosition(1, arr.nelements())); if (uInt(maxNrValues+1) >= vec.nelements()) { os << endl << indent << " " << vec; } else { os << ", first values:" << endl << indent << " " << vec(Slice(0,maxNrValues-1)); } } } break; case TpArrayDouble: os << "Double array with shape " << static_cast*>(ptr)->shape(); if (maxNrValues != 0) { const Array& arr = *static_cast*>(ptr); if (maxNrValues < 0) { os << endl << arr; } else { Vector vec = arr.reform (IPosition(1, arr.nelements())); if (uInt(maxNrValues+1) >= vec.nelements()) { os << endl << indent << " " << vec; } else { os << ", first values:" << endl << indent << " " << vec(Slice(0,maxNrValues-1)); } } } break; case TpArrayComplex: os << "Complex array with shape " << static_cast*>(ptr)->shape(); if (maxNrValues != 0) { const Array& arr = *static_cast*>(ptr); if (maxNrValues < 0) { os << endl << arr; } else { Vector vec = arr.reform (IPosition(1, arr.nelements())); if (uInt(maxNrValues+1) >= vec.nelements()) { os << endl << indent << " " << vec; } else { os << ", first values:" << endl << indent << " " << vec(Slice(0,maxNrValues-1)); } } } break; case TpArrayDComplex: os << "DComplex array with shape " << static_cast*>(ptr)->shape(); if (maxNrValues != 0) { const Array& arr = *static_cast*>(ptr); if (maxNrValues < 0) { os << endl << arr; } else { Vector vec = arr.reform (IPosition(1, arr.nelements())); if (uInt(maxNrValues+1) >= vec.nelements()) { os << endl << indent << " " << vec; } else { os << ", first values:" << endl << indent << " " << vec(Slice(0,maxNrValues-1)); } } } break; case TpArrayString: os << "String array with shape " << static_cast*>(ptr)->shape(); if (maxNrValues != 0) { const Array& arr = *static_cast*>(ptr); if (maxNrValues < 0) { os << endl << arr; } else { Vector vec = arr.reform (IPosition(1, arr.nelements())); if (uInt(maxNrValues+1) >= vec.nelements()) { os << endl << indent << " " << vec; } else { os << ", first values:" << endl << indent << " " << vec(Slice(0,maxNrValues-1)); } } } break; default: throw (AipsError ("RecordRep::printDataField")); } } void RecordRep::print (std::ostream& os, Int maxNrValues, const String& indent) const { for (uInt i=0; i(data_p[i])->print (os, maxNrValues, indent+" "); os << indent << '}' << endl; } else { printDataField (os, desc_p.type(i), indent, maxNrValues, data_p[i]); os << endl; } } } void RecordRep::putDataField (AipsIO& os, DataType type, const void* ptr) const { switch (type) { case TpBool: os << *static_cast(ptr); break; case TpUChar: os << *static_cast(ptr); break; case TpShort: os << *static_cast(ptr); break; case TpInt: os << *static_cast(ptr); break; case TpUInt: os << *static_cast(ptr); break; case TpInt64: os << *static_cast(ptr); break; case TpFloat: os << *static_cast(ptr); break; case TpDouble: os << *static_cast(ptr); break; case TpComplex: os << *static_cast(ptr); break; case TpDComplex: os << *static_cast(ptr); break; case TpString: os << *static_cast(ptr); break; case TpArrayBool: putArray (os, *static_cast*>(ptr), "Array"); break; case TpArrayUChar: putArray (os, *static_cast*>(ptr), "Array"); break; case TpArrayShort: putArray (os, *static_cast*>(ptr), "Array"); break; case TpArrayInt: putArray (os, *static_cast*>(ptr), "Array"); break; case TpArrayUInt: putArray (os, *static_cast*>(ptr), "Array"); break; case TpArrayInt64: putArray (os, *static_cast*>(ptr), "Array"); break; case TpArrayFloat: putArray (os, *static_cast*>(ptr), "Array"); break; case TpArrayDouble: putArray (os, *static_cast*>(ptr), "Array"); break; case TpArrayComplex: putArray (os, *static_cast*>(ptr), "Array"); break; case TpArrayDComplex: putArray (os, *static_cast*>(ptr), "Array"); break; case TpArrayString: putArray (os, *static_cast*>(ptr), "Array"); break; default: throw (AipsError ("RecordRep::putDataField")); } } void RecordRep::getDataField (AipsIO& os, DataType type, void* ptr) { switch (type) { case TpBool: os >> *static_cast(ptr); break; case TpUChar: os >> *static_cast(ptr); break; case TpShort: os >> *static_cast(ptr); break; case TpInt: os >> *static_cast(ptr); break; case TpUInt: os >> *static_cast(ptr); break; case TpInt64: os >> *static_cast(ptr); break; case TpFloat: os >> *static_cast(ptr); break; case TpDouble: os >> *static_cast(ptr); break; case TpComplex: os >> *static_cast(ptr); break; case TpDComplex: os >> *static_cast(ptr); break; case TpString: os >> *static_cast(ptr); break; case TpArrayBool: os >> *static_cast*>(ptr); break; case TpArrayUChar: os >> *static_cast*>(ptr); break; case TpArrayShort: os >> *static_cast*>(ptr); break; case TpArrayInt: os >> *static_cast*>(ptr); break; case TpArrayUInt: os >> *static_cast*>(ptr); break; case TpArrayInt64: os >> *static_cast*>(ptr); break; case TpArrayFloat: os >> *static_cast*>(ptr); break; case TpArrayDouble: os >> *static_cast*>(ptr); break; case TpArrayComplex: os >> *static_cast*>(ptr); break; case TpArrayDComplex: os >> *static_cast*>(ptr); break; case TpArrayString: os >> *static_cast*>(ptr); break; default: throw (AipsError ("RecordRep::getDataField")); } } void RecordRep::putRecord (AipsIO& os, int recordType) const { os.putstart ("Record", 1); // version 1 os << desc_p; os << recordType; putData (os); os.putend(); } void RecordRep::putData (AipsIO& os) const { for (uInt i=0; i(const_cast(data_p[i])); }else{ static_cast(const_cast(data_p[i]))->putData (os); } }else{ putDataField (os, desc_p.type(i), data_p[i]); } } } void RecordRep::getRecord (AipsIO& os, Int& recordType) { // Support reading scalar and array keyword sets as records. // They are the very old way of storing keywords, since long replaced // by Record. The code does not exist anymore, but theoretically such data // can exist in a very old table. Therefore it is still supported here. uInt version; String type = os.getNextType(); if (type == "ScalarKeywordSet") { version = os.getstart ("ScalarKeywordSet"); getKeySet (os, version, 0); } else if (type == "ArrayKeywordSet") { version = os.getstart ("ArrayKeywordSet"); getKeySet (os, version, 1); }else{ uInt version = os.getstart ("Record"); // Get the description and restructure the record. RecordDesc desc; os >> desc; os >> recordType; restructure (desc, True); // Read the data. getData (os, version); } os.getend(); } void RecordRep::getData (AipsIO& os, uInt version) { for (uInt i=0; i> *static_cast(data_p[i]); }else{ static_cast(data_p[i])->getData (os, version); } }else{ getDataField (os, desc_p.type(i), data_p[i]); } } } void RecordRep::getKeySet (AipsIO& os, uInt version, uInt type) { // First build the description from the map of keyword names and // attributes. RecordDesc desc; getKeyDesc (os, desc); // Define the record from the description. // Read the keyword values and define the corresponding record value. restructure (desc, True); getScalarKeys (os); if (type == 1) { getArrayKeys (os); } // Newer keyword sets may contain nested keyword sets. // We do not support reading those, so throw an exception when they exist. if (version > 1) { uInt n; os >> n; AlwaysAssert (n==0, AipsError); } } void RecordRep::getKeyDesc (AipsIO& os, RecordDesc& desc) { // Start reading the Map of keyword names and attributes. os.getstart ("Map"); int dt; String name, comment; // Get #names and the default attribute (datatype + comment). uInt i, n; os >> n; os >> dt; os >> comment; // Get each keyword name and attribute. // Add them to the record description. for (i=0; i> name; os >> dt; os >> comment; desc.addField (name, DataType(dt)); } os.getend(); // Get the excluded data types and names. // Note that exNames was written as a Block, but can be // read as a Block. This is a template instantiation less. Block exDtype; Block exNames; os >> exDtype; os >> exNames; } void RecordRep::getScalarKeys (AipsIO& os) { uInt i, n; String name; // Read the values per type. os >> n; for (i=0; i> name; getDataField (os, TpBool, data_p[fieldNumber (name)]); } os >> n; for (i=0; i> name; getDataField (os, TpInt, data_p[fieldNumber (name)]); } os >> n; for (i=0; i> name; getDataField (os, TpUInt, data_p[fieldNumber (name)]); } os >> n; for (i=0; i> name; getDataField (os, TpFloat, data_p[fieldNumber (name)]); } os >> n; for (i=0; i> name; getDataField (os, TpDouble, data_p[fieldNumber (name)]); } os >> n; for (i=0; i> name; getDataField (os, TpComplex, data_p[fieldNumber (name)]); } os >> n; for (i=0; i> name; getDataField (os, TpDComplex, data_p[fieldNumber (name)]); } os >> n; for (i=0; i> name; getDataField (os, TpString, data_p[fieldNumber (name)]); } } void RecordRep::getArrayKeys (AipsIO& os) { uInt i, n; String name; // Read the values per type. os >> n; for (i=0; i> name; getDataField (os, TpArrayBool, data_p[fieldNumber (name)]); } os >> n; for (i=0; i> name; getDataField (os, TpArrayInt, data_p[fieldNumber (name)]); } os >> n; for (i=0; i> name; getDataField (os, TpArrayUInt, data_p[fieldNumber (name)]); } os >> n; for (i=0; i> name; getDataField (os, TpArrayFloat, data_p[fieldNumber (name)]); } os >> n; for (i=0; i> name; getDataField (os, TpArrayDouble, data_p[fieldNumber (name)]); } os >> n; for (i=0; i> name; getDataField (os, TpArrayComplex, data_p[fieldNumber (name)]); } os >> n; for (i=0; i> name; getDataField (os, TpArrayDComplex, data_p[fieldNumber (name)]); } os >> n; for (i=0; i> name; getDataField (os, TpArrayString, data_p[fieldNumber (name)]); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Containers/RecordRep.h000066400000000000000000000265171476623553700204110ustar00rootroot00000000000000//# RecordRep.h: The representation of a Record //# Copyright (C) 1996,1997,2000,2001,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_RECORDREP_H #define CASA_RECORDREP_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class AipsIO; class IPosition; class String; // // The representation of a Record // // // // // //
      • Record. // // // // RecordRep is the REPresentation of a Record. // // // // RecordRep is the actual implementation of a Record object. // It contains the description and the data. The data is stored as // a collection of void* pointers to the actual data. By storing // it in this indirect way, it is easier to extend the data block. // It also means that RecordFieldPtr objects always have the correct // pointer and do not need to be adjusted when the data block is extended. //

        // Despite the fact that the data pointers have type void*, the // functions are completely type safe. This is done by passing the // type around using the DataType enumeration. The downpart is that // only types from that enumeration are supported (but that is also // required by the RecordDesc mechanics). //

        // Note that RecordRep does not know anything about RecordFieldPtr // objects pointing to its data. Only its mother class Record // knows about them and handles all cases where the RecordFieldPtr's // have to be notified. // // // // RecordRep mirrors all functions in Record. // // // // Having a separate RecordRep class makes copy-on-write possible. // It also allows derivation of other RecordRep classes (like TableRecordRep), // while their mother classes are not derived from each other. // // // //

      • An implementation where arrays are stored as T*'s would cut down on // instantiations (the Arrayness would come back through the creation // of the RecordFieldPtr >). // class RecordRep { public: // Create a record with no fields. RecordRep(); // Create a record with the given description. If it is not possible to // create all fields (for example, if a field of an unsupported type is // requested), an exception is thrown. // All fields are checked by the field checking function (if defined). RecordRep (const RecordDesc& description); // Create a copy of other using copy semantics. RecordRep (const RecordRep& other); // Copy all the data over. RecordRep& operator= (const RecordRep& other); // Delete all data. virtual ~RecordRep(); // Get the comment for this field. const String& comment (Int whichField) const; // Set the comment for this field. void setComment (Int whichField, const String& comment); // Describes the current structure of this Record. const RecordDesc& description() const; // Change the structure of this Record to contain the fields in // newDescription. After calling restructure, description() == // newDescription. void restructure (const RecordDesc& newDescription, Bool recursive); // Returns True if this and other have the same RecordDesc, other // than different names for the fields. That is, the number, type and the // order of the fields must be identical (recursively for fixed // structured sub-Records in this). // // thisRecord.conform(thatRecord) == True does not imply //
        thatRecord.conform(thisRecord) == True, because // a variable record in one conforms a fixed record in that, but // not vice-versa. //
        Bool conform (const RecordRep& other) const; // Copy all data of the Record. void copyData (const RecordRep& other); // Copy a data field. // This can only handle scalars and arrays. void copyDataField (DataType type, Int whichField, const void* that) const; // Remove a field from the record. void removeField (Int whichField); // Rename the given field. void renameField (const String& newName, Int whichField); // Add a field with the given name and value to the record. // The data type of the field is determined by the data type of the value. // For arrays it is possible to define if the shape is fixed. // void addDataField (const String& name, DataType type, const IPosition& shape, Bool fixedShape, const void* data); void addField (const String& name, const Record& value, RecordInterface::RecordType type); // // Define a value for the given field. // Array conformance rules will not be applied for variable shaped arrays. // When the field and value data type mismatch, type promotion // of scalars will be done if possible. If not possible, an exception // is thrown. void defineDataField (Int whichField, DataType type, const void* value); // Put the description and data of the Record. // It also puts the fixedFlag attribute (of the mother object). void putRecord (AipsIO& os, int recordType) const; // Get the description and data of the Record. // It also gets the fixedFlag attribute (of the mother object). void getRecord (AipsIO& os, Int& recordType); // Put the data of a record. // This is used to write a subrecord, whose description has // already been written. void putData (AipsIO& os) const; // Read the data of a record. // This is used to read a subrecord, whose description has // already been read. void getData (AipsIO& os, uInt version); // Used by the RecordFieldPtr classes to attach in a type-safe way to the // correct field. // void* get_pointer (Int whichField, DataType type) const; void* get_pointer (Int whichField, DataType type, const String& recordType) const; // // Merge a field from another record into this record. void mergeField (const RecordRep& other, Int whichFieldFromOther, RecordInterface::DuplicatesFlag); // Merge all fields from the other record into this record. void merge (const RecordRep& other, RecordInterface::DuplicatesFlag); // Print a record. // Print the contents of the record. // Only the first maxNrValues of an array will be printed. // A value < 0 means the entire array. void print (std::ostream&, Int maxNrValues = 25, const String& indent="") const; protected: // Utility functions to avoid code duplication in the public member // functions. // void delete_myself (uInt nfields); void copy_other (const RecordRep& other); // // Get the field number for a given name. virtual Int fieldNumber (const String& name) const; // Add the data pointer to the data block. // The block is extended when needed. void addDataPtr (void* ptr); // Remove a data pointer add the given index. void removeDataPtr (Int index); // Check if the shape of the data array matches the shape of a // fixed-shaped array in the description. void checkShape (DataType type, const IPosition& shape, const void* value, const String& fieldName); // Add a field to the description. virtual void addFieldToDesc (const String& name, DataType type, const IPosition& shape, Bool fixedShape); // Remove a data field. virtual void removeData (Int whichField, void* ptr, void* vecptr); // Remove a field from the description. virtual void removeFieldFromDesc (Int whichField); // Create a data field for the given type and shape. // This can only handle scalars and arrays. void* createDataField (DataType type, const IPosition& shape); // Delete a data field. // This can only handle scalars and arrays. void deleteDataField (DataType type, void* ptr, void* vecptr); // Copy a data field. // This can only handle scalars and arrays. void copyDataField (DataType type, void* ptr, const void* that) const; // Print a data field. // This can only handle scalars and arrays. void printDataField (std::ostream& os, DataType type, const String& indent, Int maxNrValues, const void* ptr) const; // Put a data field. // This can only handle scalars and arrays. void putDataField (AipsIO& os, DataType type, const void* ptr) const; // Get a data field. // This can only handle scalars and arrays. void getDataField (AipsIO& os, DataType type, void* ptr); // Make an array for a scalar data field. // It shares the data, so a change in the data is reflected in the array. // It is used to be able to access a scalar as an 1D array. void makeDataVec (Int whichField, DataType type); // Get a Scalar/ArrayKeywordSet object as a Record. // (type 0 = ScalarKeywordSet; type 1 = ArrayKeywordSet). void getKeySet (AipsIO& os, uInt version, uInt type); // Get the description of a keyword set as a RecordDesc. void getKeyDesc (AipsIO& os, RecordDesc& desc); // Get the scalar values of a keyword set. void getScalarKeys (AipsIO& os); // Get the array values of a keyword set. void getArrayKeys (AipsIO& os); // Holds the structure of this Record. RecordDesc desc_p; // Pointers to data values. Block data_p; // Pointers to a vector of a scalar (to access a scalar as an array). Block datavec_p; // #Entries used in data_p. uInt nused_p; }; inline const RecordDesc& RecordRep::description() const { return desc_p; } inline const String& RecordRep::comment (Int whichField) const { return desc_p.comment (whichField); } inline void RecordRep::setComment (Int whichField, const String& comment) { desc_p.setComment (whichField, comment); } inline void RecordRep::renameField (const String& newName, Int whichField) { desc_p.renameField (newName, whichField); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Containers/ValueHolder.cc000066400000000000000000000102061476623553700210600ustar00rootroot00000000000000//# ValueHolder.cc: A holder object for the standard Casacore data //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ValueHolder::ValueHolder (Bool value) : itsRep (new ValueHolderRep(value)) {} ValueHolder::ValueHolder (uChar value) : itsRep (new ValueHolderRep(value)) {} ValueHolder::ValueHolder (Short value) : itsRep (new ValueHolderRep(value)) {} ValueHolder::ValueHolder (uShort value) : itsRep (new ValueHolderRep(value)) {} ValueHolder::ValueHolder (Int value) : itsRep (new ValueHolderRep(value)) {} ValueHolder::ValueHolder (uInt value) : itsRep (new ValueHolderRep(value)) {} ValueHolder::ValueHolder (Int64 value) : itsRep (new ValueHolderRep(value)) {} ValueHolder::ValueHolder (Float value) : itsRep (new ValueHolderRep(value)) {} ValueHolder::ValueHolder (Double value) : itsRep (new ValueHolderRep(value)) {} ValueHolder::ValueHolder (const Complex& value) : itsRep (new ValueHolderRep(value)) {} ValueHolder::ValueHolder (const DComplex& value) : itsRep (new ValueHolderRep(value)) {} ValueHolder::ValueHolder (const Char* value) : itsRep (new ValueHolderRep(String(value))) {} ValueHolder::ValueHolder (const String& value) : itsRep (new ValueHolderRep(value)) {} ValueHolder::ValueHolder (const Array& value) : itsRep (new ValueHolderRep(value)) {} ValueHolder::ValueHolder (const Array& value) : itsRep (new ValueHolderRep(value)) {} ValueHolder::ValueHolder (const Array& value) : itsRep (new ValueHolderRep(value)) {} ValueHolder::ValueHolder (const Array& value) : itsRep (new ValueHolderRep(value)) {} ValueHolder::ValueHolder (const Array& value) : itsRep (new ValueHolderRep(value)) {} ValueHolder::ValueHolder (const Array& value) : itsRep (new ValueHolderRep(value)) {} ValueHolder::ValueHolder (const Array& value) : itsRep (new ValueHolderRep(value)) {} ValueHolder::ValueHolder (const Array& value) : itsRep (new ValueHolderRep(value)) {} ValueHolder::ValueHolder (const Array& value) : itsRep (new ValueHolderRep(value)) {} ValueHolder::ValueHolder (const Array& value) : itsRep (new ValueHolderRep(value)) {} ValueHolder::ValueHolder (const Array& value) : itsRep (new ValueHolderRep(value)) {} ValueHolder::ValueHolder (const Array& value) : itsRep (new ValueHolderRep(value)) {} ValueHolder::ValueHolder (const Record& value) : itsRep (new ValueHolderRep(value)) {} ValueHolder::ValueHolder (uInt ndim, Bool dummy) : itsRep (new ValueHolderRep(ndim, dummy)) {} ValueHolder::ValueHolder (const ValueHolder& that) : itsRep (that.itsRep) {} ValueHolder& ValueHolder::operator= (const ValueHolder& that) { if (this != &that) { itsRep = that.itsRep; } return *this; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Containers/ValueHolder.h000066400000000000000000000254361476623553700207350ustar00rootroot00000000000000//# ValueHolder.h: A holder object for the standard Casacore data types //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_VALUEHOLDER_H #define CASA_VALUEHOLDER_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A holder for a value of any basic Casacore data type. // // // // // // Class ValueHolder is meant to be used for holding a single Casacore value. // The value can be scalar or an array of any basic type (including complex // and string). Also a Record value is possible. // In this way varying typed data (e.g. the result of getCell in the table DO) // can be packed in a strongly typed variable. //
        All unsigned integer type values are kept as signed 32-bit integers // because scripting languages usually only support those types. // // ValueHolder is an envelope class that holds a counted-referenced letter // object ValueHolderRep. //
        // // This class comes handy in passing arbitrary values from a DO to // its environment. // class ValueHolder { public: // Construct a null object. ValueHolder() {} // Create the object for the given value. // explicit ValueHolder (Bool value); explicit ValueHolder (uChar value); explicit ValueHolder (Short value); explicit ValueHolder (uShort value); explicit ValueHolder (Int value); explicit ValueHolder (uInt value); explicit ValueHolder (Int64 value); explicit ValueHolder (Float value); explicit ValueHolder (Double value); explicit ValueHolder (const Complex& value); explicit ValueHolder (const DComplex& value); explicit ValueHolder (const Char* value); explicit ValueHolder (const String& value); explicit ValueHolder (const Array& value); explicit ValueHolder (const Array& value); explicit ValueHolder (const Array& value); explicit ValueHolder (const Array& value); explicit ValueHolder (const Array& value); explicit ValueHolder (const Array& value); explicit ValueHolder (const Array& value); explicit ValueHolder (const Array& value); explicit ValueHolder (const Array& value); explicit ValueHolder (const Array& value); explicit ValueHolder (const Array& value); explicit ValueHolder (const Array& value); explicit ValueHolder (const Record& value); // // Create an empty N-dim array (gets type TpOther). ValueHolder (uInt ndim, Bool dummy); // Create a ValueHolder from a ValueHolderRep. // It takes over the pointer and deletes it in the destructor. explicit ValueHolder (ValueHolderRep* rep) : itsRep (rep) {} // Copy constructor (reference semantics). ValueHolder (const ValueHolder&); // Destructor. ~ValueHolder() {} // Assignment (reference semantics). ValueHolder& operator= (const ValueHolder&); // Is this a null object? Bool isNull() const { return !itsRep; } // Get the data type (as defined in DataType.h). // Note that TpOther is returned for an empty untyped array. DataType dataType() const; // Get the value. // If possible, it converts the data as needed. // Bool asBool () const; uChar asuChar () const; Short asShort () const; uShort asuShort () const; Int asInt () const; uInt asuInt () const; Int64 asInt64 () const; Float asFloat () const; Double asDouble () const; Complex asComplex () const; DComplex asDComplex() const; const String& asString () const; const Array asArrayBool () const; const Array asArrayuChar () const; const Array asArrayShort () const; const Array asArrayuShort () const; const Array asArrayInt () const; const Array asArrayuInt () const; const Array asArrayInt64 () const; const Array asArrayFloat () const; const Array asArrayDouble () const; const Array asArrayComplex () const; const Array asArrayDComplex() const; const Array asArrayString () const; const Record& asRecord () const; // // Get the data in a way useful for templates. // If possible, it converts the the data as needed. // void getValue (Bool& value) const { value = asBool(); } void getValue (uChar& value) const { value = asuChar(); } void getValue (Short& value) const { value = asShort(); } void getValue (uShort& value) const { value = asuShort(); } void getValue (Int& value) const { value = asInt(); } void getValue (uInt& value) const { value = asuInt(); } void getValue (Int64& value) const { value = asInt64(); } void getValue (Float& value) const { value = asFloat(); } void getValue (Double& value) const { value = asDouble(); } void getValue (Complex& value) const { value = asComplex(); } void getValue (DComplex& value) const { value = asDComplex(); } void getValue (String& value) const { value = asString(); } void getValue (Array& value) const { value.reference(asArrayBool()); } void getValue (Array& value) const { value.reference(asArrayuChar()); } void getValue (Array& value) const { value.reference(asArrayShort()); } void getValue (Array& value) const { value.reference(asArrayuShort()); } void getValue (Array& value) const { value.reference(asArrayInt()); } void getValue (Array& value) const { value.reference(asArrayuInt()); } void getValue (Array& value) const { value.reference(asArrayInt64()); } void getValue (Array& value) const { value.reference(asArrayFloat()); } void getValue (Array& value) const { value.reference(asArrayDouble()); } void getValue (Array& value) const { value.reference(asArrayComplex()); } void getValue (Array& value) const { value.reference(asArrayDComplex()); } void getValue (Array& value) const { value.reference(asArrayString()); } // // Put the value as a field in a record. void toRecord (Record&, const RecordFieldId&) const; // Construct the object from the value in a record. static ValueHolder fromRecord (const Record&, const RecordFieldId&); // Compare two ValueHolder objects. // They must have the same data type. bool operator< (const ValueHolder& right) const { return itsRep->operator< (*right.itsRep); } // Write the ValueHolder to an output stream. // Arrays are written as normal arrays using ArrayIO.h. friend std::ostream& operator<< (std::ostream& os, const ValueHolder& vh) { return vh.itsRep->write (os); } private: std::shared_ptr itsRep; }; inline DataType ValueHolder::dataType() const { return itsRep->dataType(); } inline void ValueHolder::toRecord (Record& rec, const RecordFieldId& id) const { return itsRep->toRecord (rec, id); } inline ValueHolder ValueHolder::fromRecord (const Record& rec, const RecordFieldId& id) { return ValueHolder (ValueHolderRep::fromRecord (rec, id)); } inline Bool ValueHolder::asBool() const { return itsRep->asBool(); } inline uChar ValueHolder::asuChar() const { return itsRep->asuChar(); } inline Short ValueHolder::asShort() const { return itsRep->asShort(); } inline uShort ValueHolder::asuShort() const { return itsRep->asuShort(); } inline Int ValueHolder::asInt() const { return itsRep->asInt(); } inline uInt ValueHolder::asuInt() const { return itsRep->asuInt(); } inline Int64 ValueHolder::asInt64() const { return itsRep->asInt64(); } inline Float ValueHolder::asFloat() const { return itsRep->asFloat(); } inline Double ValueHolder::asDouble() const { return itsRep->asDouble(); } inline Complex ValueHolder::asComplex() const { return itsRep->asComplex(); } inline DComplex ValueHolder::asDComplex() const { return itsRep->asDComplex(); } inline const String& ValueHolder::asString() const { return itsRep->asString(); } inline const Array ValueHolder::asArrayBool() const { return itsRep->asArrayBool(); } inline const Array ValueHolder::asArrayuChar() const { return itsRep->asArrayuChar(); } inline const Array ValueHolder::asArrayShort() const { return itsRep->asArrayShort(); } inline const Array ValueHolder::asArrayuShort() const { return itsRep->asArrayuShort(); } inline const Array ValueHolder::asArrayInt() const { return itsRep->asArrayInt(); } inline const Array ValueHolder::asArrayuInt() const { return itsRep->asArrayuInt(); } inline const Array ValueHolder::asArrayInt64() const { return itsRep->asArrayInt64(); } inline const Array ValueHolder::asArrayFloat() const { return itsRep->asArrayFloat(); } inline const Array ValueHolder::asArrayDouble() const { return itsRep->asArrayDouble(); } inline const Array ValueHolder::asArrayComplex() const { return itsRep->asArrayComplex(); } inline const Array ValueHolder::asArrayDComplex() const { return itsRep->asArrayDComplex(); } inline const Array ValueHolder::asArrayString() const { return itsRep->asArrayString(); } inline const Record& ValueHolder::asRecord() const { return itsRep->asRecord(); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Containers/ValueHolderRep.cc000066400000000000000000000722751476623553700215450ustar00rootroot00000000000000//# ValueHolderRep.cc: A holder object for the standard Casacore data //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ValueHolderRep::ValueHolderRep (Bool value) : itsNdim (0), itsType (TpBool), itsBool (value) {} ValueHolderRep::ValueHolderRep (uChar value) : itsNdim (0), itsType (TpUChar), itsInt64(value) {} ValueHolderRep::ValueHolderRep (Short value) : itsNdim (0), itsType (TpShort), itsInt64(value) {} ValueHolderRep::ValueHolderRep (uShort value) : itsNdim (0), itsType (TpUShort), itsInt64(value) {} ValueHolderRep::ValueHolderRep (Int value) : itsNdim (0), itsType (TpInt), itsInt64(value) {} ValueHolderRep::ValueHolderRep (uInt value) : itsNdim (0), itsType (TpUInt), itsInt64(value) {} ValueHolderRep::ValueHolderRep (Int64 value) : itsNdim (0), itsType (TpInt64), itsInt64(value) {} ValueHolderRep::ValueHolderRep (Float value) : itsNdim (0), itsType (TpFloat), itsFloat (value) {} ValueHolderRep::ValueHolderRep (Double value) : itsNdim (0), itsType (TpDouble), itsDouble (value) {} ValueHolderRep::ValueHolderRep (const Complex& value) : itsNdim (0), itsType (TpComplex), itsPtr (new Complex(value)) {} ValueHolderRep::ValueHolderRep (const DComplex& value) : itsNdim (0), itsType (TpDComplex), itsPtr (new DComplex(value)) {} ValueHolderRep::ValueHolderRep (const String& value) : itsNdim (0), itsType (TpString), itsPtr (new String(value)) {} ValueHolderRep::ValueHolderRep (const Array& value) : itsNdim (value.ndim()), itsType (TpArrayBool), itsPtr (new Array(value)) {} ValueHolderRep::ValueHolderRep (const Array& value) : itsNdim (value.ndim()), itsType (TpArrayUChar), itsPtr (new Array(value.shape())) { convertArray(*(static_cast*>(itsPtr)), value); } ValueHolderRep::ValueHolderRep (const Array& value) : itsNdim (value.ndim()), itsType (TpArrayShort), itsPtr (new Array(value.shape())) { convertArray(*(static_cast*>(itsPtr)), value); } ValueHolderRep::ValueHolderRep (const Array& value) : itsNdim (value.ndim()), itsType (TpArrayUInt), itsPtr (new Array(value.shape())) { convertArray (*static_cast*>(itsPtr), value); } ValueHolderRep::ValueHolderRep (const Array& value) : itsNdim (value.ndim()), itsType (TpArrayInt), itsPtr (new Array(value)) {} ValueHolderRep::ValueHolderRep (const Array& value) : itsNdim (value.ndim()), itsType (TpArrayUInt), itsPtr (new Array(value)) {} ValueHolderRep::ValueHolderRep (const Array& value) : itsNdim (value.ndim()), itsType (TpArrayInt64), itsPtr (new Array(value)) {} ValueHolderRep::ValueHolderRep (const Array& value) : itsNdim (value.ndim()), itsType (TpArrayFloat), itsPtr (new Array(value)) {} ValueHolderRep::ValueHolderRep (const Array& value) : itsNdim (value.ndim()), itsType (TpArrayDouble), itsPtr (new Array(value)) {} ValueHolderRep::ValueHolderRep (const Array& value) : itsNdim (value.ndim()), itsType (TpArrayComplex), itsPtr (new Array(value)) {} ValueHolderRep::ValueHolderRep (const Array& value) : itsNdim (value.ndim()), itsType (TpArrayDComplex), itsPtr (new Array(value)) {} ValueHolderRep::ValueHolderRep (const Array& value) : itsNdim (value.ndim()), itsType (TpArrayString), itsPtr (new Array(value)) {} ValueHolderRep::ValueHolderRep (const Record& value) : itsNdim (0), itsType (TpRecord), itsPtr (new Record(value)) {} ValueHolderRep::ValueHolderRep (uInt ndim, Bool) : itsNdim (ndim), itsType (TpOther), itsPtr (0) {} ValueHolderRep::~ValueHolderRep() { switch (itsType) { case TpComplex: delete static_cast(itsPtr); break; case TpDComplex: delete static_cast(itsPtr); break; case TpString: delete static_cast(itsPtr); break; case TpArrayBool: delete static_cast*>(itsPtr); break; case TpArrayUChar: case TpArrayShort: case TpArrayUShort: case TpArrayInt: delete static_cast*>(itsPtr); break; case TpArrayUInt: delete static_cast*>(itsPtr); break; case TpArrayInt64: delete static_cast*>(itsPtr); break; case TpArrayFloat: delete static_cast*>(itsPtr); break; case TpArrayDouble: delete static_cast*>(itsPtr); break; case TpArrayComplex: delete static_cast*>(itsPtr); break; case TpArrayDComplex: delete static_cast*>(itsPtr); break; case TpArrayString: delete static_cast*>(itsPtr); break; case TpRecord: delete static_cast(itsPtr); break; default: break; } } Bool ValueHolderRep::asBool() const { switch (itsType) { case TpBool: return itsBool; case TpUChar: case TpShort: case TpUShort: case TpInt: case TpUInt: case TpInt64: return itsInt64; case TpFloat: return itsFloat; case TpDouble: return itsDouble; default: ; } throw AipsError ("ValueHolderRep::asBool - invalid data type " + String::toString(itsType)); } uChar ValueHolderRep::asuChar() const { switch (itsType) { case TpUChar: case TpShort: case TpUShort: case TpInt: case TpUInt: case TpInt64: AlwaysAssert (itsInt64 >= 0 && itsInt64 < 256, AipsError); return itsInt64; case TpFloat: AlwaysAssert (itsFloat >= 0 && itsFloat < 256, AipsError); return uChar(itsFloat); case TpDouble: AlwaysAssert (itsDouble >= 0 && itsDouble < 256, AipsError); return uChar(itsDouble); default: ; } throw AipsError ("ValueHolderRep::asuChar - invalid data type " + String::toString(itsType)); } Short ValueHolderRep::asShort() const { switch (itsType) { case TpUChar: case TpShort: case TpUShort: case TpInt: case TpUInt: case TpInt64: AlwaysAssert (itsInt64 >= -32768 && itsInt64 < 32768, AipsError); return itsInt64; case TpFloat: AlwaysAssert (itsFloat >= -32768 && itsFloat < 32768, AipsError); return Short(itsFloat); case TpDouble: AlwaysAssert (itsDouble >= -32768 && itsDouble < 32768, AipsError); return Short(itsDouble); default: ; } throw AipsError ("ValueHolderRep::asShort - invalid data type " + String::toString(itsType)); } uShort ValueHolderRep::asuShort() const { switch (itsType) { case TpUChar: case TpShort: case TpUShort: case TpInt: case TpUInt: case TpInt64: AlwaysAssert (itsInt64 >= 0 && itsInt64 < 65536, AipsError); return itsInt64; case TpFloat: AlwaysAssert (itsFloat >= 0 && itsFloat < 65536, AipsError); return uShort(itsFloat); case TpDouble: AlwaysAssert (itsDouble >= 0 && itsDouble < 65536, AipsError); return uShort(itsDouble); default: ; } throw AipsError ("ValueHolderRep::asuShort - invalid data type " + String::toString(itsType)); } Int ValueHolderRep::asInt() const { switch (itsType) { case TpUChar: case TpShort: case TpUShort: case TpInt: case TpUInt: case TpInt64: return Int(itsInt64); case TpFloat: return Int(itsFloat); case TpDouble: return Int(itsDouble); default: ; } throw AipsError ("ValueHolderRep::asInt - invalid data type " + String::toString(itsType)); } uInt ValueHolderRep::asuInt() const { switch (itsType) { case TpUChar: case TpShort: case TpUShort: case TpInt: case TpUInt: case TpInt64: AlwaysAssert (itsInt64 >= 0, AipsError); return itsInt64; case TpFloat: AlwaysAssert (itsFloat >= 0, AipsError); return uInt(itsFloat); case TpDouble: AlwaysAssert (itsDouble >= 0, AipsError); return uInt(itsDouble); default: ; } throw AipsError ("ValueHolderRep::asuInt - invalid data type " + String::toString(itsType)); } Int64 ValueHolderRep::asInt64() const { switch (itsType) { case TpUChar: case TpShort: case TpUShort: case TpInt: case TpUInt: case TpInt64: return itsInt64; case TpFloat: return static_cast (itsFloat); case TpDouble: return static_cast (itsDouble); default: ; } throw AipsError ("ValueHolderRep::asInt64 - invalid data type " + String::toString(itsType)); } Float ValueHolderRep::asFloat() const { switch (itsType) { case TpUChar: case TpShort: case TpUShort: case TpInt: case TpUInt: case TpInt64: return itsInt64; case TpFloat: return itsFloat; case TpDouble: return itsDouble; default: ; } throw AipsError ("ValueHolderRep::asFloat - invalid data type " + String::toString(itsType)); } Double ValueHolderRep::asDouble() const { switch (itsType) { case TpUChar: case TpShort: case TpUShort: case TpInt: case TpUInt: case TpInt64: return itsInt64; case TpFloat: return itsFloat; case TpDouble: return itsDouble; default: ; } throw AipsError ("ValueHolderRep::asDouble - invalid data type " + String::toString(itsType)); } Complex ValueHolderRep::asComplex() const { switch (itsType) { case TpUChar: case TpShort: case TpUShort: case TpInt: case TpUInt: case TpInt64: return itsInt64; case TpFloat: return itsFloat; case TpDouble: return itsDouble; case TpComplex: return *static_cast(itsPtr); case TpDComplex: return Complex (static_cast(itsPtr)->real(), static_cast(itsPtr)->imag()); default: ; } throw AipsError ("ValueHolderRep::asComplex - invalid data type " + String::toString(itsType)); } DComplex ValueHolderRep::asDComplex() const { switch (itsType) { case TpUChar: case TpShort: case TpUShort: case TpInt: case TpUInt: case TpInt64: return itsInt64; case TpFloat: return itsFloat; case TpDouble: return itsDouble; case TpComplex: return *static_cast(itsPtr); case TpDComplex: return *static_cast(itsPtr); default: ; } throw AipsError ("ValueHolderRep::asDComplex - invalid data type " + String::toString(itsType)); } const String& ValueHolderRep::asString() const { switch (itsType) { case TpString: return *static_cast(itsPtr); default: ; } throw AipsError ("ValueHolderRep::asString - invalid data type " + String::toString(itsType)); } const Array ValueHolderRep::asArrayBool() const { // Empty array from numpy (which is untyped). if (itsType == TpOther) { return Array(IPosition(itsNdim, 0)); } switch (itsType) { case TpArrayBool: return *static_cast*>(itsPtr); case TpArrayUChar: case TpArrayShort: case TpArrayUShort: case TpArrayInt: case TpArrayUInt: case TpArrayInt64: { const Array from = asArrayInt64(); Array to(from.shape()); convertArray (to, from); return to; } case TpArrayFloat: case TpArrayDouble: { const Array from = asArrayDouble(); Array to(from.shape()); convertArray (to, from); return to; } default: return Vector (1, asBool()); } } const Array ValueHolderRep::asArrayuChar() const { if (itsType == TpOther) { return Array(IPosition(itsNdim, 0)); } switch (itsType) { case TpArrayUChar: case TpArrayShort: case TpArrayUShort: case TpArrayInt: { const Array& from = *static_cast*>(itsPtr); Array to(from.shape()); convertArray (to, from); return to; } case TpArrayUInt: case TpArrayInt64: { const Array from = asArrayInt64(); Array to(from.shape()); convertArray (to, from); return to; } case TpArrayFloat: case TpArrayDouble: { const Array from = asArrayDouble(); Array to(from.shape()); convertArray (to, from); return to; } default: return Vector (1, asuChar()); } } const Array ValueHolderRep::asArrayShort() const { if (itsType == TpOther) { return Array(IPosition(itsNdim, 0)); } switch (itsType) { case TpArrayUChar: case TpArrayShort: case TpArrayUShort: case TpArrayInt: { const Array& from = *static_cast*>(itsPtr); Array to(from.shape()); convertArray (to, from); return to; } case TpArrayUInt: case TpArrayInt64: { const Array from = asArrayInt64(); Array to(from.shape()); convertArray (to, from); return to; } case TpArrayFloat: case TpArrayDouble: { const Array from = asArrayDouble(); Array to(from.shape()); convertArray (to, from); return to; } default: return Vector (1, asShort()); } } const Array ValueHolderRep::asArrayuShort() const { if (itsType == TpOther) { return Array(IPosition(itsNdim, 0)); } switch (itsType) { case TpArrayUChar: case TpArrayShort: case TpArrayUShort: case TpArrayInt: { const Array& from = *static_cast*>(itsPtr); Array to(from.shape()); convertArray (to, from); return to; } case TpArrayUInt: case TpArrayInt64: { const Array from = asArrayInt64(); Array to(from.shape()); convertArray (to, from); return to; } case TpArrayFloat: case TpArrayDouble: { const Array from = asArrayDouble(); Array to(from.shape()); convertArray (to, from); return to; } default: return Vector (1, asuShort()); } } const Array ValueHolderRep::asArrayInt() const { if (itsType == TpOther) { return Array(IPosition(itsNdim, 0)); } switch (itsType) { case TpArrayUChar: case TpArrayShort: case TpArrayUShort: case TpArrayInt: return *static_cast*>(itsPtr); case TpArrayUInt: case TpArrayInt64: { const Array from = asArrayInt64(); Array to(from.shape()); convertArray (to, from); return to; } case TpArrayFloat: case TpArrayDouble: { const Array from = asArrayDouble(); Array to(from.shape()); convertArray (to, from); return to; } default: return Vector (1, asInt()); } } const Array ValueHolderRep::asArrayuInt() const { if (itsType == TpOther) { return Array(IPosition(itsNdim, 0)); } switch (itsType) { case TpArrayUChar: case TpArrayShort: case TpArrayUShort: case TpArrayInt: { const Array& from = *static_cast*>(itsPtr); Array to(from.shape()); convertArray (to, from); return to; } case TpArrayUInt: return *static_cast*>(itsPtr); case TpArrayInt64: { const Array from = asArrayInt64(); Array to(from.shape()); convertArray (to, from); return to; } case TpArrayFloat: case TpArrayDouble: { const Array from = asArrayDouble(); Array to(from.shape()); convertArray (to, from); return to; } default: return Vector (1, asuInt()); } } const Array ValueHolderRep::asArrayInt64() const { if (itsType == TpOther) { return Array(IPosition(itsNdim, 0)); } switch (itsType) { case TpArrayUChar: case TpArrayShort: case TpArrayUShort: case TpArrayInt: { const Array& from = *static_cast*>(itsPtr); Array to(from.shape()); convertArray (to, from); return to; } case TpArrayUInt: { const Array from = asArrayuInt(); Array to(from.shape()); convertArray (to, from); return to; } case TpArrayInt64: return *static_cast*>(itsPtr); case TpArrayFloat: case TpArrayDouble: { const Array from = asArrayDouble(); Array to(from.shape()); convertArray (to, from); return to; } default: return Vector (1, asInt64()); } } const Array ValueHolderRep::asArrayFloat() const { if (itsType == TpOther) { return Array(IPosition(itsNdim, 0)); } switch (itsType) { case TpArrayUChar: case TpArrayShort: case TpArrayUShort: case TpArrayInt: { const Array& from = *static_cast*>(itsPtr); Array to(from.shape()); convertArray (to, from); return to; } case TpArrayUInt: { const Array& from = *static_cast*>(itsPtr); Array to(from.shape()); convertArray (to, from); return to; } case TpArrayInt64: { const Array& from = *static_cast*>(itsPtr); Array to(from.shape()); convertArray (to, from); return to; } case TpArrayFloat: return *static_cast*>(itsPtr); case TpArrayDouble: { const Array& from = *static_cast*>(itsPtr); Array to(from.shape()); convertArray (to, from); return to; } default: return Vector (1, asFloat()); } } const Array ValueHolderRep::asArrayDouble() const { if (itsType == TpOther) { return Array(IPosition(itsNdim, 0)); } switch (itsType) { case TpArrayUChar: case TpArrayShort: case TpArrayUShort: case TpArrayInt: { const Array& from = *static_cast*>(itsPtr); Array to(from.shape()); convertArray (to, from); return to; } case TpArrayUInt: { const Array& from = *static_cast*>(itsPtr); Array to(from.shape()); convertArray (to, from); return to; } case TpArrayInt64: { const Array& from = *static_cast*>(itsPtr); Array to(from.shape()); convertArray (to, from); return to; } case TpArrayFloat: { const Array& from = *static_cast*>(itsPtr); Array to(from.shape()); convertArray (to, from); return to; } case TpArrayDouble: return *static_cast*>(itsPtr); default: return Vector (1, asDouble()); } } const Array ValueHolderRep::asArrayComplex() const { if (itsType == TpOther) { return Array(IPosition(itsNdim, 0)); } switch (itsType) { case TpArrayUChar: case TpArrayShort: case TpArrayUShort: case TpArrayInt: { const Array& from = *static_cast*>(itsPtr); Array to(from.shape()); convertArray (to, from); return to; } case TpArrayUInt: { const Array& from = *static_cast*>(itsPtr); Array to(from.shape()); convertArray (to, from); return to; } case TpArrayInt64: { const Array& from = *static_cast*>(itsPtr); Array to(from.shape()); convertArray (to, from); return to; } case TpArrayFloat: { const Array& from = *static_cast*>(itsPtr); Array to(from.shape()); convertArray (to, from); return to; } case TpArrayDouble: { const Array& from = *static_cast*>(itsPtr); Array to(from.shape()); convertArray (to, from); return to; } case TpArrayComplex: return *static_cast*>(itsPtr); case TpArrayDComplex: { const Array& from = *static_cast*>(itsPtr); Array to(from.shape()); convertArray (to, from); return to; } default: return Vector (1, asComplex()); } } const Array ValueHolderRep::asArrayDComplex() const { if (itsType == TpOther) { return Array(IPosition(itsNdim, 0)); } switch (itsType) { case TpArrayUChar: case TpArrayShort: case TpArrayUShort: case TpArrayInt: { const Array& from = *static_cast*>(itsPtr); Array to(from.shape()); convertArray (to, from); return to; } case TpArrayUInt: { const Array& from = *static_cast*>(itsPtr); Array to(from.shape()); convertArray (to, from); return to; } case TpArrayInt64: { const Array& from = *static_cast*>(itsPtr); Array to(from.shape()); convertArray (to, from); return to; } case TpArrayFloat: { const Array& from = *static_cast*>(itsPtr); Array to(from.shape()); convertArray (to, from); return to; } case TpArrayDouble: { const Array& from = *static_cast*>(itsPtr); Array to(from.shape()); convertArray (to, from); return to; } case TpArrayComplex: { const Array& from = *static_cast*>(itsPtr); Array to(from.shape()); convertArray (to, from); return to; } case TpArrayDComplex: return *static_cast*>(itsPtr); default: return Vector (1, asDComplex()); } } const Array ValueHolderRep::asArrayString() const { if (itsType == TpOther) { return Array(IPosition(itsNdim, 0)); } switch (itsType) { case TpArrayString: return *static_cast*>(itsPtr); default: return Vector (1, asString()); } } const Record& ValueHolderRep::asRecord() const { switch (itsType) { case TpRecord: return *static_cast(itsPtr); default: ; } throw AipsError ("ValueHolderRep::asRecord - invalid data type " + String::toString(itsType)); } void ValueHolderRep::toRecord (Record& rec, const RecordFieldId& id) const { switch (itsType) { case TpBool: rec.define (id, itsBool); break; case TpUChar: rec.define (id, asuChar()); break; case TpShort: rec.define (id, asShort()); break; case TpUShort: case TpInt: rec.define (id, asInt()); break; case TpUInt: rec.define (id, asuInt()); break; case TpInt64: rec.define (id, itsInt64); break; case TpFloat: rec.define (id, itsFloat); break; case TpDouble: rec.define (id, itsDouble); break; case TpComplex: rec.define (id, *static_cast(itsPtr)); break; case TpDComplex: rec.define (id, *static_cast(itsPtr)); break; case TpString: rec.define (id, *static_cast(itsPtr)); break; case TpArrayBool: rec.define (id, *static_cast*>(itsPtr)); break; case TpArrayUChar: { const Array& from = *static_cast*>(itsPtr); Array to(from.shape()); convertArray (to, from); rec.define (id, to); break; } case TpArrayShort: { const Array& from = *static_cast*>(itsPtr); Array to(from.shape()); convertArray (to, from); rec.define (id, to); break; } case TpArrayUShort: case TpArrayInt: rec.define (id, *static_cast*>(itsPtr)); break; case TpArrayUInt: rec.define (id, *static_cast*>(itsPtr)); break; case TpArrayInt64: rec.define (id, *static_cast*>(itsPtr)); break; case TpArrayFloat: rec.define (id, *static_cast*>(itsPtr)); break; case TpArrayDouble: rec.define (id, *static_cast*>(itsPtr)); break; case TpArrayComplex: rec.define (id, *static_cast*>(itsPtr)); break; case TpArrayDComplex: rec.define (id, *static_cast*>(itsPtr)); break; case TpArrayString: rec.define (id, *static_cast*>(itsPtr)); break; case TpRecord: rec.defineRecord (id, *static_cast(itsPtr)); break; default: break; } } // Construct the object from the value in a record. ValueHolderRep* ValueHolderRep::fromRecord (const Record& rec, const RecordFieldId& id) { switch (rec.dataType(id)) { case TpBool: return new ValueHolderRep (rec.asBool(id)); case TpUChar: return new ValueHolderRep (rec.asuChar(id)); case TpShort: return new ValueHolderRep (rec.asShort(id)); case TpInt: return new ValueHolderRep (rec.asInt(id)); case TpUInt: return new ValueHolderRep (rec.asuInt(id)); case TpInt64: return new ValueHolderRep (rec.asInt64(id)); case TpFloat: return new ValueHolderRep (rec.asFloat(id)); case TpDouble: return new ValueHolderRep (rec.asDouble(id)); case TpComplex: return new ValueHolderRep (rec.asComplex(id)); case TpDComplex: return new ValueHolderRep (rec.asDComplex(id)); case TpString: return new ValueHolderRep (rec.asString(id)); case TpArrayBool: return new ValueHolderRep (rec.asArrayBool(id)); case TpArrayUChar: return new ValueHolderRep (rec.asArrayuChar(id)); case TpArrayShort: return new ValueHolderRep (rec.asArrayShort(id)); case TpArrayInt: return new ValueHolderRep (rec.asArrayInt(id)); case TpArrayUInt: return new ValueHolderRep (rec.asArrayuInt(id)); case TpArrayInt64: return new ValueHolderRep (rec.asArrayInt64(id)); case TpArrayFloat: return new ValueHolderRep (rec.asArrayFloat(id)); case TpArrayDouble: return new ValueHolderRep (rec.asArrayDouble(id)); case TpArrayComplex: return new ValueHolderRep (rec.asArrayComplex(id)); case TpArrayDComplex: return new ValueHolderRep (rec.asArrayDComplex(id)); case TpArrayString: return new ValueHolderRep (rec.asArrayString(id)); case TpRecord: return new ValueHolderRep (rec.subRecord(id)); default: break; } throw AipsError ("ValueHolder::fromRecord - unknown data type " + String::toString(rec.dataType(id))); } ostream& ValueHolderRep::write (ostream& os) const { switch (itsType) { case TpBool: os << itsBool; break; case TpUChar: case TpShort: case TpUShort: case TpInt: case TpUInt: case TpInt64: os << asInt64(); break; case TpFloat: case TpDouble: os << asDouble(); break; case TpComplex: case TpDComplex: os << asDComplex(); break; case TpString: os << asString(); break; case TpArrayBool: os << asArrayBool(); break; case TpArrayUChar: case TpArrayShort: case TpArrayUShort: case TpArrayInt: case TpArrayUInt: case TpArrayInt64: os << asArrayInt64(); break; case TpArrayFloat: case TpArrayDouble: os << asArrayDouble(); break; case TpArrayComplex: case TpArrayDComplex: os << asArrayDComplex(); break; case TpArrayString: os << asArrayString(); break; case TpRecord: os << asRecord(); break; case TpOther: os << "Empty untyped array"; break; default: throw AipsError ("ValueHolder::write - unknown data type " + String::toString(itsType)); break; } return os; } bool ValueHolderRep::operator< (const ValueHolderRep& right) const { AlwaysAssert (itsType == right.itsType, AipsError); switch (itsType) { case TpBool: return itsBool < right.itsBool; case TpUChar: case TpShort: case TpUShort: case TpInt: case TpUInt: case TpInt64: return itsInt64 < right.itsInt64; case TpFloat: return itsFloat < right.itsFloat; case TpDouble: return itsDouble < right.itsDouble; case TpString: return *static_cast(itsPtr) < *static_cast(right.itsPtr); default: throw AipsError ("ValueHolder::operator< - unsupported data type " + String::toString(itsType)); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Containers/ValueHolderRep.h000066400000000000000000000141351476623553700213760ustar00rootroot00000000000000//# ValueHolderRep.h: A holder object for the standard CASACORE data //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_VALUEHOLDERREP_H #define CASA_VALUEHOLDERREP_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class AipsIO; class Record; class RecordFieldId; // // A holder for a value of any basic type. // // // // // // Class ValueHolderRep is the letter class for the envelope class ValueHolder. // See that class for more information. // // // Copying ValueHolders should be as cheap as possible, so a counted // referenced letter class is used. // class ValueHolderRep { public: // Create the object for the given value. // explicit ValueHolderRep (Bool value); explicit ValueHolderRep (uChar value); explicit ValueHolderRep (Short value); explicit ValueHolderRep (uShort value); explicit ValueHolderRep (Int value); explicit ValueHolderRep (uInt value); explicit ValueHolderRep (Int64 value); explicit ValueHolderRep (Float value); explicit ValueHolderRep (Double value); explicit ValueHolderRep (const Complex& value); explicit ValueHolderRep (const DComplex& value); explicit ValueHolderRep (const Char* value); explicit ValueHolderRep (const String& value); explicit ValueHolderRep (const Array& value); explicit ValueHolderRep (const Array& value); explicit ValueHolderRep (const Array& value); explicit ValueHolderRep (const Array& value); explicit ValueHolderRep (const Array& value); explicit ValueHolderRep (const Array& value); explicit ValueHolderRep (const Array& value); explicit ValueHolderRep (const Array& value); explicit ValueHolderRep (const Array& value); explicit ValueHolderRep (const Array& value); explicit ValueHolderRep (const Array& value); explicit ValueHolderRep (const Array& value); explicit ValueHolderRep (const Record& value); // // Create an empty N-dim array. ValueHolderRep (uInt ndim, Bool dummy); // Destructor. ~ValueHolderRep(); // Forbid copy ctor and assignment. //# There is no fundamental reason to forbid them, but it saves //# implementation work as long as they are not needed. // ValueHolderRep (const ValueHolderRep&) = delete; ValueHolderRep& operator= (const ValueHolderRep&) = delete; // // Get the data type (as defined in DataType.h). DataType dataType() const; // Get the value. // If possible, it converts the data as needed. // Bool asBool () const; uChar asuChar () const; Short asShort () const; uShort asuShort () const; Int asInt () const; uInt asuInt () const; Int64 asInt64 () const; Float asFloat () const; Double asDouble () const; Complex asComplex () const; DComplex asDComplex() const; const String& asString () const; const Array asArrayBool () const; const Array asArrayuChar () const; const Array asArrayShort () const; const Array asArrayuShort () const; const Array asArrayInt () const; const Array asArrayuInt () const; const Array asArrayInt64 () const; const Array asArrayFloat () const; const Array asArrayDouble () const; const Array asArrayComplex () const; const Array asArrayDComplex() const; const Array asArrayString () const; const Record& asRecord () const; // // Put the value as a field in a record. void toRecord (Record&, const RecordFieldId&) const; // Construct the object from the value in a record. static ValueHolderRep* fromRecord (const Record& rec, const RecordFieldId&); // Write the ValueHolderRep to an output stream. // Arrays are written as normal arrays using ArrayIO.h. std::ostream& write (std::ostream& os) const; // Compare two ValueHolder objects. // They must have the same data type. bool operator< (const ValueHolderRep& right) const; /* bool operator== (const ValueHolderRep& right) const; bool near (const ValueHolderRep& right, tolerance=1e-5) const; bool nearAbs (const ValueHolderRep& right, double tolerance=1e-5) const; */ private: uInt itsNdim; DataType itsType; union { Bool itsBool; Int64 itsInt64; Float itsFloat; Double itsDouble; void* itsPtr; }; }; inline DataType ValueHolderRep::dataType() const { return itsType; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Containers/test/000077500000000000000000000000001476623553700173175ustar00rootroot00000000000000casacore-3.7.1/casa/Containers/test/CMakeLists.txt000066400000000000000000000005271476623553700220630ustar00rootroot00000000000000set (tests tBlock tBlockTrace tObjectStack tRecord tRecordDesc tValueHolder ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_casa) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/casa/Containers/test/tBlock.cc000066400000000000000000000336641476623553700210600ustar00rootroot00000000000000//# tBlock.cc: This program tests the Block class //# Copyright (C) 1993,1994,1995,1996,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes // Turn on array checking in the inline Block index operators #if !defined(AIPS_ARRAY_INDEX_CHECK) #define AIPS_ARRAY_INDEX_CHECK #endif #include #include #include #include #include #include #include #include #include #include namespace { struct LifecycleChecker { LifecycleChecker() { if (ctor_count >= ctor_error_trigger) { throw 0; } ++ctor_count; } LifecycleChecker(LifecycleChecker const &) { if (ctor_count >= ctor_error_trigger) { throw 0; } ++ctor_count; } ~LifecycleChecker() { ++dtor_count; } LifecycleChecker & operator =(LifecycleChecker const&) { if (assign_count >= assign_error_trigger) { throw 0; } ++assign_count; return *this; } static void clear() { assign_count = ctor_count = dtor_count = 0; assign_error_trigger = ctor_error_trigger = std::numeric_limits::max(); } static size_t assign_count; static size_t ctor_count; static size_t dtor_count; static size_t ctor_error_trigger; static size_t assign_error_trigger; }; size_t LifecycleChecker::assign_count = 0; size_t LifecycleChecker::ctor_count = 0; size_t LifecycleChecker::dtor_count = 0; size_t LifecycleChecker::ctor_error_trigger = std::numeric_limits::max(); size_t LifecycleChecker::assign_error_trigger = std::numeric_limits::max(); } void doit() { // We do the tests in another function to make it easier to check for // memory leaks, i.e. everything should be destructed at the end of this // block. uInt i; Block bi1; // Block::Block() AlwaysAssertExit(bi1.nelements() == 0);// Block::nelements() AlwaysAssertExit(bi1.size() == 0); AlwaysAssertExit(bi1.empty()); for (i = 0; i < 200; i++) { Block bi(AllocSpec >::value); AlwaysAssertExit(0 == bi.storage()); bi.resize(3); AlwaysAssertExit(0 == ((intptr_t)bi.storage()) % 32); } Block bi2(100); // Block::Block(uInt) AlwaysAssertExit(bi2.nelements() == 100); AlwaysAssertExit(bi2.size() == 100); AlwaysAssertExit(!bi2.empty()); for (i = 0; i < 200; i++) { Block bi(100UL, AllocSpec >::value); AlwaysAssertExit(bi.nelements() == 100); AlwaysAssertExit(bi.size() == 100); AlwaysAssertExit(bi.capacity() == 100); AlwaysAssertExit(!bi.empty()); AlwaysAssertExit(0 == ((intptr_t)bi.storage()) % 32); Int *p = bi.storage(); bi.resize(100UL); AlwaysAssertExit(p == bi.storage()); AlwaysAssertExit(0 == ((intptr_t)bi.storage()) % 32); bi.resize(95UL, True); AlwaysAssertExit(0 == ((intptr_t)bi.storage()) % 32); bi[44] = 9876; bi.resize(91UL, True, True); AlwaysAssertExit(9876 == bi[44]); AlwaysAssertExit(0 == ((intptr_t)bi.storage()) % 32); bi.resize(89UL, True, False, ArrayInitPolicies::INIT); AlwaysAssertExit(0 == bi[0] && 0 == bi[30]); AlwaysAssertExit(0 == ((intptr_t)bi.storage()) % 32); p = bi.storage(); bi.resize(87UL, False, True, ArrayInitPolicies::NO_INIT); AlwaysAssertExit(p == bi.storage()); AlwaysAssertExit(0 == ((intptr_t)bi.storage()) % 32); bi.resize(105UL); AlwaysAssertExit(0 == ((intptr_t)bi.storage()) % 32); } { LifecycleChecker::clear(); { Block b(200, ArrayInitPolicies::INIT); } AlwaysAssertExit(200 <= LifecycleChecker::ctor_count); AlwaysAssertExit(200 <= LifecycleChecker::dtor_count); } { LifecycleChecker::clear(); LifecycleChecker::ctor_error_trigger = 10; try { Block b(20, ArrayInitPolicies::INIT); AlwaysAssertExit(False); } catch (...) { AlwaysAssertExit(LifecycleChecker::ctor_count == LifecycleChecker::dtor_count); } } { LifecycleChecker::clear(); LifecycleChecker::ctor_error_trigger = 20 + 5; try { Block b(20, ArrayInitPolicies::INIT); b.resize(15, True, True, ArrayInitPolicies::NO_INIT); AlwaysAssertExit(False); } catch (...) { AlwaysAssertExit(LifecycleChecker::ctor_count == LifecycleChecker::dtor_count); } } { LifecycleChecker::clear(); LifecycleChecker::ctor_error_trigger = 20 + 5; try { Block b(20, ArrayInitPolicies::INIT); b.resize(15, True, True, ArrayInitPolicies::INIT); AlwaysAssertExit(False); } catch (...) { AlwaysAssertExit(LifecycleChecker::ctor_count == LifecycleChecker::dtor_count); } } { LifecycleChecker::clear(); LifecycleChecker::ctor_error_trigger = 10 + 5; try { Block b(10, ArrayInitPolicies::INIT); b.resize(15, True, True, ArrayInitPolicies::NO_INIT); AlwaysAssertExit(False); } catch (...) { AlwaysAssertExit(LifecycleChecker::ctor_count == LifecycleChecker::dtor_count); } } { LifecycleChecker::clear(); LifecycleChecker::ctor_error_trigger = 10 + 10 + 3; try { Block b(10, ArrayInitPolicies::INIT); b.resize(15, True, True, ArrayInitPolicies::NO_INIT); } catch (...) { AlwaysAssertExit(False); } AlwaysAssertExit(LifecycleChecker::ctor_count + 5 == LifecycleChecker::dtor_count); } { LifecycleChecker::clear(); LifecycleChecker::ctor_error_trigger = 10 + 10 + 3; try { Block b(10, ArrayInitPolicies::INIT); b.resize(15, True, True, ArrayInitPolicies::INIT); AlwaysAssertExit(False); } catch (...) { AlwaysAssertExit(LifecycleChecker::ctor_count == LifecycleChecker::dtor_count); } } { LifecycleChecker::clear(); { Block b(200, ArrayInitPolicies::NO_INIT, AllocSpec >::value); } AlwaysAssertExit(200 <= LifecycleChecker::ctor_count); AlwaysAssertExit(200 <= LifecycleChecker::dtor_count); AlwaysAssertExit(LifecycleChecker::ctor_count == LifecycleChecker::dtor_count); } { LifecycleChecker::clear(); { Block b(200, ArrayInitPolicies::NO_INIT, AllocSpec >::value); } AlwaysAssertExit(0 == LifecycleChecker::ctor_count); AlwaysAssertExit(200 <= LifecycleChecker::dtor_count); } { LifecycleChecker::clear(); { Block b(200, ArrayInitPolicies::INIT); } AlwaysAssertExit(200 <= LifecycleChecker::ctor_count); AlwaysAssertExit(200 <= LifecycleChecker::dtor_count); AlwaysAssertExit(LifecycleChecker::ctor_count == LifecycleChecker::dtor_count); } { LifecycleChecker::clear(); { Block bi(200); } AlwaysAssertExit(200 <= LifecycleChecker::ctor_count); AlwaysAssertExit(200 <= LifecycleChecker::dtor_count); AlwaysAssertExit(LifecycleChecker::ctor_count == LifecycleChecker::dtor_count); } { Block ba(10); ba = 10; Block bb(ba); AlwaysAssertExit(10 == bb[0] && 10 == bb[9]); Int *p = new Int[20]; ba.prohibitChangingAllocator(); try { ba.replaceStorage(20, p, True); AlwaysAssertExit(False); } catch (std::exception const &) { } try { ba.replaceStorage(20, p, False); AlwaysAssertExit(False); } catch (std::exception const &) { } ba.permitChangingAllocator(); try { ba.replaceStorage(20, p, True); } catch (std::exception const &) { AlwaysAssertExit(False); } AlwaysAssertExit(0 == p); bb.prohibitChangingAllocator(); p = DefaultAllocator::type().allocate(20); try { ba.replaceStorage(20, p, True, AllocSpec >::value); } catch (std::exception const &) { AlwaysAssertExit(False); } AlwaysAssertExit(0 == p); } Block bi7(0); AlwaysAssertExit(bi7.nelements() == 0); Block bi3(200,5); // Block::Block(uInt, T) AlwaysAssertExit(bi3.nelements() == 200); Block bi6(0, 5); AlwaysAssertExit(bi6.nelements() == 0); for (i=0; i<200; i++) { AlwaysAssertExit(5 == bi3[i]); // Block::operator[](uInt) } Block bi4(bi3); // Block::Block(const Block &) AlwaysAssertExit(bi4.nelements() == 200); for (i=0; i<200; i++) { AlwaysAssertExit(5 == bi4[i]); } Block bi5(bi1); AlwaysAssertExit(bi5.nelements() == 0); bi2 = bi3; // Block::operator=(const Block &) AlwaysAssertExit(bi2.nelements() == 200); for(i=0; i < 200; i++) { AlwaysAssertExit(bi2[i] == 5); } const Block& bi2ref(bi2); // Use ref in self-assigment bi2 = bi2ref; // to avoid compiler warning AlwaysAssertExit(bi2.nelements() == 200); for(i=0; i < 200; i++) { AlwaysAssertExit(bi2[i] == 5); } bi1.resize(150); // Block::resize(uInt); bi1.set(10); // Block::set(T); AlwaysAssertExit(bi1.nelements() == 150); for(i=0; i<150; i++) { AlwaysAssertExit(bi1[i] == 10); } bi1.resize(100); AlwaysAssertExit(bi1.nelements() == 150); for(i=0; i<150; i++) { AlwaysAssertExit(bi1[i] == 10); } bi1.resize(100, False); // Block::resize(uInt, Bool) AlwaysAssertExit(bi1.nelements() == 150); for(i=0; i<150; i++) { AlwaysAssertExit(bi1[i] == 10); } bi1.resize(100, True); AlwaysAssertExit(bi1.nelements() == 100); for(i=0; i<100; i++) { AlwaysAssertExit(bi1[i] == 10); } bi1.resize(150, True, True); // Block::resize(uInt, Bool, Bool) AlwaysAssertExit(bi1.nelements() == 150); for(i=0; i<100; i++) { AlwaysAssertExit(bi1[i] == 10); } { // Block::remove(uInt) Block bi(6); bi[0] = 0; bi[1] = 1; bi[2] = 2; bi[3] = 3; bi[4] = 4; bi[5] = 5; bi.remove(0); AlwaysAssertExit(bi[0] == 1 && bi[4] == 5); bi.remove(4); AlwaysAssertExit(bi[0] == 1 && bi[3] == 4); bi.remove(2); AlwaysAssertExit(bi[0] == 1 && bi[1] == 2 && bi[2] == 4); } // There's no portable way to test for CopyElFalse const Block &bi1ref = bi1; for(i=0; i<100; i++) { AlwaysAssertExit(bi1ref[i] == 10); // Block::operator[](uInt) const } AlwaysAssertExit(&bi1[0] == bi1.storage()); // Block::storage() AlwaysAssertExit(bi1.storage() == bi1ref.storage()); // Block::storage() const { Int *in1 = new int[100]; Int *inkeep = in1; Block bip(100, in1); AlwaysAssertExit(in1 == 0); AlwaysAssertExit(&bip[0] == inkeep && bip.nelements() == 100); Int *in2 = new int[50]; Int *inkeep2 = in2; bip.replaceStorage(50, in2); AlwaysAssertExit(in2 == 0); AlwaysAssertExit(&bip[0] == inkeep2 && bip.nelements() == 50); } { Int *stored = new Int[10]; Block aliased(10, stored, False); AlwaysAssertExit(stored != 0); stored[3] = 454; AlwaysAssertExit(aliased[3] == 454); Int *stored2 = new Int[10]; aliased.replaceStorage(10, stored2, False); stored2[3] = 999; AlwaysAssertExit(aliased[3] == 999); delete [] stored; delete [] stored2; } { // Block::iterator Block bi(6); bi[0] = 0; bi[1] = 1; bi[2] = 2; bi[3] = 3; bi[4] = 4; bi[5] = 5; Int nrit = 0; for (Block::const_iterator iter=bi.begin(); iter!=bi.end(); iter++) { AlwaysAssertExit(*iter == bi[nrit++]); } AlwaysAssertExit(nrit == 6); std::vector vec(bi.begin(), bi.end()); AlwaysAssertExit(vec.size() == 6); AlwaysAssertExit(vec[0] == 0 && vec[1] == 1 && vec[2] == 2 && vec[3] == 3 && vec[4] == 4 && vec[5] == 5); } { // Check that this no longer leaks (regression test) Block leaker(0); } // Block::~Block called at end of fn } void testIO() { Block bl(10000); for (uInt i=0; i bl2; aio >> bl2; AlwaysAssertExit (bl2.size() == bl.size()); for (uInt i=0; i #include #include void doit() { Block bl1(10); bl1.resize (20); Block bl2(3, 4.); } int main() { traceMemoryBlockBegin ("1st no"); MemoryTrace::open(); traceMemoryBlockBegin ("2nd yes"); MemoryTrace::open(); traceMemoryBlockBegin ("3rd yes"); MemoryTrace::close(); traceMemoryBlockBegin ("4th no"); // This should not do tracing. doit(); BlockTrace::setTraceSize (10); traceMemoryBlockBegin ("5th yes start"); // This should do tracing. doit(); { MemoryTraceBlock mtb("6th yes endxxxxx"); } // Now it should not trace. MemoryTrace::close(); traceMemoryBlockBegin ("7th no"); doit(); } casacore-3.7.1/casa/Containers/test/tBlockTrace.run000077500000000000000000000003271476623553700222470ustar00rootroot00000000000000#!/bin/sh # Give the output file a name that it is deleted automatically. CASACORE_MEMORYTRACE=tBlockTrace_tmp.log export CASACORE_MEMORYTRACE # valgrind is not needed, so do not use $casacore_check ./tBlockTrace casacore-3.7.1/casa/Containers/test/tObjectStack.cc000066400000000000000000000056011476623553700222100ustar00rootroot00000000000000//# tObjectPool.cc -- test ObjectStack //# Copyright (C) 2007 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include // Using using std::pair; #include int main() { Bool ok(True); try { cout << "Test ObjectStack" << endl; cout << "---------------------------------------------------" << endl; // Some poolobjectsList of pool objects; for (uInt i=0; i<10000; ++i) { vector*> list; vector*> listInt; for (uInt j=0; j<10; ++j) { // Get some objects list.push_back(ObjectStack >::stack().get()); listInt.push_back(ObjectStack >::stack().get()); // Test freshness if (!list[j]->empty()) { cout << "List not refreshed " << endl; ok = False; } if (!listInt[j]->empty()) { cout << "ListInt not refreshed " << endl; ok = False; } // Fill objects for (uInt k=0; k<7; ++k) { list[j]->push_back(13.*k); listInt[j]->push_back(k); } // Test objects if (list[j]->size() != 7) { cout << "Incorrect length list " << list[j]->size() << endl; ok = False; } if (listInt[j]->size() != 7) { cout << "Incorrect length listInt " << listInt[j]->size() << endl; ok = False; } } // Remove in different order for (uInt j=0; j<10; ++j) { ObjectStack >::stack().put(listInt[9-j]); ObjectStack >::stack().put(list[9-j]); } } } catch (std::exception& x) { cout << x.what() << endl; ok = False; } if (!ok) return 1; cout << "Tests ok" << endl; cout << "---------------------------------------------------" << endl; return 0; } casacore-3.7.1/casa/Containers/test/tRecord.cc000066400000000000000000001150751476623553700212410ustar00rootroot00000000000000//# tRecord.cc: Test the Record class //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include // This program tests the Record and RecordRep classes. // Its expected output is stored in tRecord.out and can be checked // using assay. This will also delete the temporary output file. //

        // It can throw several exceptions, which may result in memory leaks. // To check if no real memory leaks occur, the program can be run // with an argument (e.g. tRecord 1). In that case no statements // resulting in exceptions are executed, so no memory leaks should occur. //

        // The ability to read old KeywordSet objects from a file has been tested // in a separate program. That program is not checked into the system, // because the KeywordSet classes are removed from it. void check (const Record&, Int intValue, uInt nrField); void doIt (Bool doExcp); int main (int argc, const char*[]) { try { doIt ( (argc<2)); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; // exit with success status } // This function checks if a field name is correct. // A name has to be > 0 characters and start with an uppercase. // The extra argument should not be 10. Bool nameCallBack (const String& name, DataType, const void* extraArgument, String& message) { if (name.length() < 1) { message = "length<1"; return False; } if (name[0] < 'A' || name[0] > 'Z') { message = "no uppercase"; return False; } if (extraArgument != 0 && *(const Int*)extraArgument == 10) { message = "extra==10"; return False; } return True; } // This function is doing define's and assign's in several ways. // Many of them are incorrect and should result in an exception. // TpArrayString2 has a fixed shape, while TpArrayString3's shape is variable. // Note that assign always requires a matching shape, while a define // only requires a matching shape for a fixed shape. // It checks if the value is correct after each define/assign. void doDefineAssign (const Record& inrecord) { Record record (inrecord); RecordFieldPtr > rfstr2 (record, "TpArrayString2"); RecordFieldPtr > rfstr3 (record, "TpArrayString3"); Vector vec; record.get (record.fieldNumber ("TpArrayString2"), vec); AlwaysAssertExit (allEQ (vec, stringToVector ("abcd,ghi,jklmn"))); record.get (record.fieldNumber ("TpArrayString3"), vec); AlwaysAssertExit (allEQ (vec, stringToVector ("abc,dghij,klmn"))); try { *rfstr2 = stringToVector ("abc"); } catch (std::exception& x) { cout << x.what() << endl; // incorrect shape } try { *rfstr3 = stringToVector ("abc"); } catch (std::exception& x) { cout << x.what() << endl; // incorrect shape } record.get (record.fieldNumber ("TpArrayString2"), vec); AlwaysAssertExit (allEQ (vec, stringToVector ("abcd,ghi,jklmn"))); record.get (record.fieldNumber ("TpArrayString3"), vec); AlwaysAssertExit (allEQ (vec, stringToVector ("abc,dghij,klmn"))); try { *rfstr2 = stringToVector ("abc"); } catch (std::exception& x) { cout << x.what() << endl; // incorrect shape } try { *rfstr3 = stringToVector ("abc"); } catch (std::exception& x) { cout << x.what() << endl; // incorrect shape } record.get (record.fieldNumber ("TpArrayString2"), vec); AlwaysAssertExit (allEQ (vec, stringToVector ("abcd,ghi,jklmn"))); record.get (record.fieldNumber ("TpArrayString3"), vec); AlwaysAssertExit (allEQ (vec, stringToVector ("abc,dghij,klmn"))); try { rfstr2.define (stringToVector ("abc")); } catch (std::exception& x) { cout << x.what() << endl; // incorrect shape } rfstr3.define (stringToVector ("a")); record.get (record.fieldNumber ("TpArrayString2"), vec); AlwaysAssertExit (allEQ (vec, stringToVector ("abcd,ghi,jklmn"))); vec.resize (1); record.get (record.fieldNumber ("TpArrayString3"), vec); AlwaysAssertExit (allEQ (vec, stringToVector ("a"))); *rfstr2 = stringToVector ("a,b,c"); *rfstr3 = stringToVector ("d"); record.get (record.fieldNumber ("TpArrayString3"), vec); AlwaysAssertExit (allEQ (vec, stringToVector ("d"))); vec.resize (3); record.get (record.fieldNumber ("TpArrayString2"), vec); AlwaysAssertExit (allEQ (vec, stringToVector ("a,b,c"))); rfstr2.define (stringToVector ("g,h,i")); record.define (record.fieldNumber ("TpArrayString3"), stringToVector ("j,k,l")); record.get (record.fieldNumber ("TpArrayString2"), vec); AlwaysAssertExit (allEQ (vec, stringToVector ("g,h,i"))); record.get (record.fieldNumber ("TpArrayString3"), vec); AlwaysAssertExit (allEQ (vec, stringToVector ("j,k,l"))); try { record.define ("TpBool", Vector(2, False)); } catch (std::exception& x) { cout << x.what() << endl; } // Do an erroneous get. try { record.asString(0); // invalid data type } catch (const AipsError& x) { cout << x.what() << endl; } } void doSubRecord (Bool doExcp, const RecordDesc& desc) { Int subField = desc.fieldNumber ("SubRecord"); Int subField1 = desc.fieldNumber ("SubRecord1"); Record record(desc); RecordFieldPtr sub (record, subField); RecordFieldPtr sub1 (record, subField1); AlwaysAssertExit (! (*sub).conform (*sub1)); // Add 2 fields, so now they are conforming. (*sub1).define ("f1", float(3)); (*sub1).define ("i1", Int(2)); AlwaysAssertExit ((*sub).conform (*sub1)); // Create a copy of the record description and add the 2 fields // to SubRecord1. RecordDesc desc1(desc); RecordDesc& subDesc1 = desc1.rwSubRecord (subField1); subDesc1.addField ("fa", TpFloat); subDesc1.addField ("ia", TpInt); // Now create a record from that new description. // record conforms record1 and vice versa (because SubRecord1 // is variable in record, but fixed in record1). Record record1(desc1); AlwaysAssertExit (record.conform (record1)); AlwaysAssertExit (record1.conform (record)); // Test if the assignment works fine. record1 = record; AlwaysAssertExit (record1.subRecord(subField1).asFloat(0) == 3); AlwaysAssertExit (record1.subRecord(subField1).asInt(1) == 2); // Add another field to SubRecord1 in record. // This results in record1 not conforming record. // record still conforms record1, because its SubRecord1 is non-fixed. (*sub1).define ("i2", Int(2)); AlwaysAssertExit (record.conform (record1)); AlwaysAssertExit (! record1.conform (record)); if (doExcp) { try { record1 = record; } catch (std::exception& x) { // not conforming cout << ">>> Instance-specific assertion error message:" << endl << x.what() << endl << "<<<" << endl; } } (*sub1).define (0, float(4)); AlwaysAssertExit (record.subRecord(subField1).asFloat(0) == 4); record = record1; AlwaysAssertExit (record.subRecord(subField1).asFloat(0) == 3); record.print (cout, 0, " "); record.print (cout, -1, " "); cout << record; // Test conversion to ValueHolder. Record recin; recin.defineRecord ("rec", record); ValueHolder vh = recin.asValueHolder(0); Record recout; recout.defineFromValueHolder (0, vh); recout.print (cout, 1); } void doIt (Bool doExcp) { // Create a record description with all types. Int extraArgument=0; RecordDesc rd; rd.addField ("TpBool", TpBool); rd.setComment (0, "comment for field TpBool"); rd.addField ("TpUChar", TpUChar); rd.addField ("TpShort", TpShort); rd.addField ("TpInt", TpInt); rd.addField ("TpUInt", TpUInt); rd.addField ("TpInt64", TpInt64); rd.addField ("TpFloat", TpFloat); rd.addField ("TpDouble", TpDouble); rd.addField ("TpComplex", TpComplex); rd.addField ("TpDComplex", TpDComplex); rd.addField ("TpString", TpString); rd.addField ("TpArrayBool", TpArrayBool, IPosition(1,1)); rd.addField ("TpArrayUChar", TpArrayUChar, IPosition(1,1)); rd.addField ("TpArrayShort", TpArrayShort, IPosition(1,1)); rd.addField ("TpArrayInt", TpArrayInt, IPosition(1,1)); rd.addField ("TpArrayUInt", TpArrayUInt, IPosition(1,1)); rd.addField ("TpArrayInt64", TpArrayInt64, IPosition(1,1)); rd.addField ("TpArrayFloat", TpArrayFloat, IPosition(1,1)); rd.addField ("TpArrayDouble", TpArrayDouble, IPosition(1,1)); rd.addField ("TpArrayComplex", TpArrayComplex, IPosition(1,1)); rd.addField ("TpArrayDComplex", TpArrayDComplex, IPosition(1,1)); rd.addField ("TpArrayString", TpArrayString); RecordDesc subDesc; rd.addField ("SubRecord1", subDesc); // empty, thus arbitrary subrecord subDesc.addField ("SubFloat", TpFloat); subDesc.addField ("SubInt", TpInt); rd.addField ("SubRecord", subDesc); // not empty, thus fixed subrecord // Record(const RecordDesc &description, ...); // uInt nfields() const; // const RecordDesc &description() const Record record(rd, RecordInterface::Variable, nameCallBack, &extraArgument); AlwaysAssertExit (record.comment ("TpBool") == "comment for field TpBool"); record.setComment ("TpBool", "comment for TpBool"); AlwaysAssertExit (record.comment ("TpBool") == "comment for TpBool"); AlwaysAssertExit(record.nfields() == rd.nfields() && record.nfields() == 24); AlwaysAssertExit(record.description() == rd); // Test renameField. record.renameField ("newname", "TpInt"); AlwaysAssertExit (! record.isDefined ("TpInt")); AlwaysAssertExit (record.fieldNumber ("newname") == 3); record.renameField ("TpInt", "newname"); // Do some incorrect add's. if (doExcp) { try { record.define (record.nfields()+1, (Int)0); } catch (std::exception& x) { cout << x.what() << endl; // index too high } try { record.define ("", (Int)0); } catch (std::exception& x) { cout << x.what() << endl; // empty name } try { record.define ("aB", (Int)0); } catch (std::exception& x) { cout << x.what() << endl; // first no uppercase } extraArgument = 10; try { record.define ("A", (Int)0); } catch (std::exception& x) { cout << x.what() << endl; // extra argument = 10 } extraArgument = 0; try { record.define ("TpShort", (Int)0); } catch (std::exception& x) { cout << x.what() << endl; // invalid type } RecordDesc rd1; rd1.addField ("TpTable", TpTable); try { Record rec(rd1); } catch (std::exception& x) { cout << x.what() << endl; // invalid field type } } AlwaysAssertExit(record.nfields() == rd.nfields() && record.nfields() == 24); AlwaysAssertExit(record.description() == rd); // void define (const String& name, value); // void define (const String& name, value, Bool fixedShape); record.define ("TpBool2", False); rd.addField ("TpBool2a", TpBool); record.define ("TpUChar2", uChar(1)); rd.addField ("TpUChar2a", TpUChar); record.define ("TpShort2", Short(2)); rd.addField ("TpShort2a", TpShort); record.define ("TpInt2", Int(3)); rd.addField ("TpInt2a", TpInt); record.define ("TpUInt2", uInt(4)); rd.addField ("TpUInt2a", TpUInt); record.define ("TpInt642", Int64(2e10)); rd.addField ("TpInt642a", TpInt64); record.define ("TpFloat2", Float(5)); rd.addField ("TpFloat2a", TpFloat); record.define ("TpDouble2", Double(6)); rd.addField ("TpDouble2a", TpDouble); record.define ("TpComplex2", Complex(7,8)); rd.addField ("TpComplex2a", TpComplex); record.define ("TpDComplex2", DComplex(9,10)); rd.addField ("TpDComplex2a", TpDComplex); record.define ("TpArrayString2", stringToVector("abcd,ghi,jklmn"), True); rd.addField ("TpArrayString2a", TpArrayString, IPosition(1,3)); record.define ("TpArrayString3", stringToVector("abc,dghij,klmn")); rd.addField ("TpArrayString3a", TpArrayString); record.define ("TpString2", "abc"); rd.addField ("TpString2a", TpString); // Define a scalar using an array. AlwaysAssertExit (record.asInt("TpInt2") == 3); AlwaysAssertExit (record.asBool("TpInt2")); AlwaysAssertExit (allEQ (record.toArrayBool("TpInt2"), True)); AlwaysAssertExit (record.asuInt("TpUInt2") == 4); AlwaysAssertExit (allEQ (record.asArrayuInt("TpUInt2"), uInt(4))); record.define ("TpInt2", Vector(1,6)); AlwaysAssertExit (record.asInt("TpInt2") == 6); AlwaysAssertExit (allEQ (record.asArrayInt("TpInt2"), 6)); record.define ("TpUInt2", uInt(10)); AlwaysAssertExit (record.asuInt("TpUInt2") == 10); AlwaysAssertExit (allEQ (record.asArrayuInt("TpUInt2"), uInt(10))); record.define ("TpInt2", 3); record.define ("TpUInt2", Vector(1,4u)); AlwaysAssertExit (record.asInt("TpInt2") == 3); AlwaysAssertExit (allEQ (record.asArrayInt("TpInt2"), 3)); AlwaysAssertExit (record.asuInt("TpUInt2") == 4); AlwaysAssertExit (allEQ (record.asArrayuInt("TpUInt2"), uInt(4))); // Do some erroneous defines and assigns. if (doExcp) { doDefineAssign (record); } // Record(); // void restructure(const RecordDesc &newDescription); // Also check that a RecordFieldPtr gets detached. Record record2; record2.restructure(subDesc); // non-fixed -> possible RecordFieldPtr fld2(record2, 1); AlwaysAssertExit (fld2.isAttached()); record2.restructure(subDesc); // non-fixed -> possible and detaches // restructure and operator= fail on a non-empty, fixed record. Record record2a(RecordInterface::Fixed); if (doExcp) { try { record2a.restructure(subDesc); } catch (std::exception& x) { cout << x.what() << endl; // fixed, not empty ->impossible } } record2 = record; // non-fixed -> possible record2a = record; // same structure -> possible Record record2b (subDesc); if (doExcp) { try { record2a = record2b; } catch (std::exception& x) { // fixed; non-conforming cout << ">>> Instance-specific assertion error message:" << endl << x.what() << endl << "<<<" << endl; } } // Record(const Record &other); // Bool conform(const Record &other); Record record3(record2); Record record4(record2.description()); record4 = record3; AlwaysAssertExit(rd == record2.description() && rd == record3.description() && rd == record4.description()); AlwaysAssertExit(record.conform(record2)); AlwaysAssertExit(record.conform(record2a)); Record record5; AlwaysAssertExit(! record.conform(record5)); // Record(const RecordInterface& other) Record record3a ((RecordInterface&)record3); AlwaysAssertExit(rd == record3a.description()); AlwaysAssertExit(record3a.conform(record3)); // Scalar fields RecordFieldPtr boolField(record, 0); RecordFieldPtr ucharField(record, 1); RecordFieldPtr shortField(record, 2); RecordFieldPtr intField(record, 3); RecordFieldPtr uintField(record, 4); RecordFieldPtr int64Field(record, 5); RecordFieldPtr floatField(record, 6); RecordFieldPtr doubleField(record, 7); RecordFieldPtr complexField(record, 8); RecordFieldPtr dcomplexField(record, 9); RecordFieldPtr stringField(record, 10); // RecordFieldPtr(Record &record, uInt whichField); // T &operator*() // const T &operator*() const // define (const T& value) *boolField = True; *ucharField = 255; AlwaysAssertExit(*((const RecordFieldPtr &)ucharField) == 255); *shortField = 32767; *intField = -1234567; uintField.define (1234567); *int64Field = Int64(3e10); *floatField = 7.0f; *doubleField = 9.0; *complexField = Complex(1.0f, 11.0f); *dcomplexField = Complex(5.0, 1.0); *stringField = "Hello"; // Array fields RecordFieldPtr > arrayboolField(record, 11); RecordFieldPtr > arrayucharField(record, 12); RecordFieldPtr > arrayshortField(record, 13); RecordFieldPtr > arrayintField(record, 14); RecordFieldPtr > arrayuintField(record, 15); RecordFieldPtr > arrayint64Field(record, 16); RecordFieldPtr > arrayfloatField(record, 17); RecordFieldPtr > arraydoubleField(record, 18); RecordFieldPtr > arraycomplexField(record, 19); RecordFieldPtr > arraydcomplexField(record, 20); RecordFieldPtr > arraystringField(record, 21); arrayboolField.setComment ("comment for TpArrayBool"); *arrayboolField = True; *arrayucharField = 255; *arrayshortField = 32767; *arrayintField = -1234567; *arrayuintField = 1234567; *arrayint64Field = Int64(3e10); *arrayfloatField = 7.0f; *arraydoubleField = 9.0; *arraycomplexField = Complex(1.0f, 11.0f); *arraydcomplexField = DComplex(5.0, 1.0); *arraystringField = stringToVector ("Hello,Goodbye"); // Check if asComplex works okay. AlwaysAssertExit (record.asComplex ("TpComplex") == Complex(1.0f, 11.0f)); AlwaysAssertExit (record.asComplex ("TpInt") == Complex(-1234567.0f)); AlwaysAssertExit (record.asComplex ("TpDComplex") == Complex(5.0f, 1.0f)); AlwaysAssertExit (record.asDComplex ("TpComplex") == DComplex(1.0, 11.0)); AlwaysAssertExit (record.asDComplex ("TpInt") == DComplex(-1234567.0)); AlwaysAssertExit (record.asDComplex ("TpDComplex") == DComplex(5.0, 1.0)); if (doExcp) { try { record.asComplex ("TpBool"); } catch (std::exception& x) { cout << x.what() << endl; // fixed -> add not possible } } // Check if shape works okay. AlwaysAssertExit (record.shape("TpBool") == IPosition(1,1)); AlwaysAssertExit (record.shape(20) == IPosition(1,1)); AlwaysAssertExit (record.shape(21) == IPosition(1,2)); // Sub-record fields RecordFieldPtr recordField(record, "SubRecord"); Record& subrec = *recordField; AlwaysAssertExit(subrec.description() == subDesc); RecordFieldPtr subref (subrec, 0); *subref = 9.0; // Record& rwSubRecord (Int whichField); // defineRecord (const String& name, const Record&); // RecordFieldPtr::define (const Record&); // RecordFieldPtr::operator= (const Record&); Record& subrec1 = record.rwSubRecord (record.fieldNumber ("SubRecord1")); Record subrec1a; subrec1.defineRecord ("sub", subrec, RecordInterface::Fixed); subrec1.defineRecord ("sub1", subrec1a); subrec1.defineRecord ("sub2", subrec1a); RecordFieldPtr sub1 (subrec1, 1); RecordFieldPtr sub2 (subrec1, 2); *subref = 6.0; sub1.define (subrec); *subref = 8.0; *sub2 = subrec; // Check if the entire record is correct. check (record, -1234567, 37); // Now make a copy of the record and assign a value via RecordFieldPtr. // This has to result in a copy(-on-write) operation. Record savrec2(record); check (savrec2, -1234567, 37); *intField += 1; *arrayintField = -1234566; check (savrec2, -1234567, 37); check (record, -1234566, 37); savrec2 = record; check (savrec2, -1234566, 37); // Clone the record. RecordInterface* recClone = record.clone(); check (Record(*recClone), -1234566, 37); *intField += 11; *arrayintField = -1234555; Record reccp (record); check (reccp, -1234555, 37); check (Record(*recClone), -1234566, 37); reccp.assign (*recClone); check (reccp, -1234566, 37); check (Record(*recClone), -1234566, 37); delete recClone; *intField -= 11; *arrayintField = -1234566; check (record, -1234566, 37); // Change some more fields and check if the original is kept intact // (thus if copy-on-write works fine). This also checks if // reacquiring the RecordFieldPtr pointers after a copy works fine. Record savrec2a(savrec2); RecordFieldPtr savrf (savrec2, 3); RecordFieldPtr > savrfarray (savrec2, 14); savrf.define (savrf.get() + 11); *savrfarray = *savrf; check (savrec2, -1234555, 37); check (savrec2a, -1234566, 37); check (record, -1234566, 37); // Add some fields. // Check if removing a field results in deattaching and in // decrementing the field number. record.define ("TpString3", "abcd"); record.define ("TpString4", "efghij"); check (record, -1234566, 39); Record savrec3(record); check (savrec3, -1234566, 39); RecordFieldPtr tpstring2 (record, "TpString2"); RecordFieldPtr tpstring3 (record, "TpString3"); RecordFieldPtr tpstring4 (record, "TpString4"); record.removeField (record.fieldNumber("TpString3")); AlwaysAssertExit (tpstring2.isAttached()); AlwaysAssertExit (tpstring4.isAttached()); AlwaysAssertExit (tpstring2.fieldNumber() == 36); record.removeField (record.fieldNumber("TpString4")); check (savrec3, -1234566, 39); check (record, -1234566, 37); // OK, we've tested the Record members, now test the remaining // RecordFieldPtr members. // RecordFieldPtr(); // void attachToRecord(Record &record, uInt whichField); // virtual Bool isAttached() RecordFieldPtr ucharField2; AlwaysAssertExit(! ucharField2.isAttached()); ucharField2.attachToRecord(record, 1); AlwaysAssertExit(*ucharField2 == *ucharField && ucharField2.isAttached()); *ucharField = 99; AlwaysAssertExit(*ucharField2 == 99); // RecordFieldPtr(const RecordFieldPtr &other); RecordFieldPtr ucharField3(ucharField); AlwaysAssertExit(*ucharField3 == *ucharField2 && ucharField3.isAttached()); // RecordFieldPtr &operator=(const RecordFieldPtr &other); RecordFieldPtr ucharField4; ucharField4 = ucharField; AlwaysAssertExit(*ucharField4 == *ucharField3 && ucharField4.isAttached()); *ucharField4 = 44; AlwaysAssertExit(*ucharField == 44); // void detach(); ucharField4.detach(); AlwaysAssertExit(! ucharField4.isAttached()); RecordDesc rd2; rd2.addField("foo", TpInt); Record *record6 = new Record(rd2); RecordFieldPtr(*record6, 0); delete record6; // Check subRecord conformance. doSubRecord (doExcp, rd); *ucharField= 255; AipsIO aos ("tRecord_tmp.data", ByteIO::New); aos << record; aos.close(); aos.open ("tRecord_tmp.data"); aos >> record5; AlwaysAssertExit(record5.conform(record)); check (record5, -1234566, 37); // Check defining and removing a subrecord. record5.defineRecord (37, record); AlwaysAssertExit (record5.name(37) == "*38"); record5.renameField ("abcd", 37); record5.defineRecord ("abcd", record); check (record5, -1234566, 38); record5.removeField (record5.fieldNumber("abcd")); check (record5, -1234566, 37); // Check defining a field by number. record5.define (37, Int(2)); check (record5, -1234566, 38); AlwaysAssertExit (record5.asInt("*38") == 2); record5.removeField (37); check (record5, -1234566, 37); // Check field merge. Record recordm(record5); check (recordm, -1234566, 37); recordm.merge (record, RecordInterface::SkipDuplicates); check (recordm, -1234566, 37); recordm.merge (record, RecordInterface::OverwriteDuplicates); check (recordm, -1234566, 37); recordm.merge (record, RecordInterface::RenameDuplicates); check (recordm, -1234566, 74); recordm.define (3, -1234555); RecordFieldPtr > fldm (recordm, "TpArrayInt"); *fldm = -1234555; check (recordm, -1234555, 74); AlwaysAssertExit (recordm.isDefined ("TpInt_1")); AlwaysAssertExit (recordm.fieldNumber ("TpInt_1") == 40); recordm.merge (record, RecordInterface::OverwriteDuplicates); AlwaysAssertExit (recordm.isDefined ("TpInt_1")); AlwaysAssertExit (recordm.fieldNumber ("TpInt_1") == 3); AlwaysAssertExit (recordm.fieldNumber ("TpInt") == 40); AlwaysAssertExit (recordm.asInt(40) == -1234566); RecordFieldPtr > fldm2 (recordm, "TpArrayInt"); AlwaysAssertExit (allEQ (*fldm2, -1234566)); if (doExcp) { try { record.merge (record, RecordInterface::SkipDuplicates); } catch (std::exception& x) { // merge of itself cout << ">>> Instance-specific assertion error message:" << endl << x.what() << endl << "<<<" << endl; } try { record.mergeField (record5, "TpBool", RecordInterface::ThrowOnDuplicates); } catch (std::exception& x) { cout << x.what() << endl; // duplicate field } try { record2a.merge (record, RecordInterface::SkipDuplicates); } catch (std::exception& x) { cout << x.what() << endl; // fixed structure } try { record2a.mergeField (record5, "TpBool", RecordInterface::ThrowOnDuplicates); } catch (std::exception& x) { cout << x.what() << endl; // fixed structure } } // ~Record() // implicit // ~RecordFieldPtr(); // implicit } // Check if the values in the record and subrecord are correct. // The number of fields and the value of the Int fields can vary, // so they are given as arguments. void check (const Record& record, Int intValue, uInt nrField) { AlwaysAssertExit (record.nfields() == nrField); RORecordFieldPtr boolField(record, 0); RORecordFieldPtr ucharField(record, 1); RORecordFieldPtr shortField(record, 2); RORecordFieldPtr intField(record, 3); RORecordFieldPtr uintField(record, 4); RORecordFieldPtr int64Field(record, 5); RORecordFieldPtr floatField(record, 6); RORecordFieldPtr doubleField(record, 7); RORecordFieldPtr complexField(record, 8); RORecordFieldPtr dcomplexField(record, 9); RORecordFieldPtr stringField(record, 10); // RORecordFieldPtr(Record &record, uInt whichField); // const T &operator*() const {return *field_ptr_p;} AlwaysAssertExit(boolField.comment() == "comment for TpBool"); AlwaysAssertExit(*boolField == True); AlwaysAssertExit(*ucharField == 255); AlwaysAssertExit(*shortField == 32767); AlwaysAssertExit(intField.get() == intValue); AlwaysAssertExit(uintField.get() == 1234567); AlwaysAssertExit(*int64Field == Int64(3e10)); AlwaysAssertExit(*floatField == 7.0f); AlwaysAssertExit(*doubleField == 9.0); AlwaysAssertExit(*complexField == Complex(1.0f, 11.0f)); AlwaysAssertExit(*dcomplexField == DComplex(5.0, 1.0)); AlwaysAssertExit(*stringField == "Hello"); Bool bv; uChar ucv; Short sv; Int iv; uInt uiv; Int64 i64v; Float fv; Double dv; Complex cv; DComplex dcv; String strv; // Record::get (T& value) const; record.get (24, bv); record.get (25, ucv); record.get (26, sv); record.get (27, iv); record.get (28, uiv); record.get (29, i64v); record.get (30, fv); record.get (31, dv); record.get (32, cv); record.get (33, dcv); record.get (36, strv); AlwaysAssertExit(bv == False); AlwaysAssertExit(ucv == 1); AlwaysAssertExit(sv == 2); AlwaysAssertExit(iv == 3); AlwaysAssertExit(uiv == 4); AlwaysAssertExit(i64v == Int64(2e10)); AlwaysAssertExit(fv == 5); AlwaysAssertExit(dv == 6); AlwaysAssertExit(cv == Complex(7,8)); AlwaysAssertExit(dcv == DComplex(9,10)); AlwaysAssertExit(strv == "abc"); AlwaysAssertExit (allEQ (record.asArrayBool(24), bv)); AlwaysAssertExit (allEQ (record.asArrayuChar(25), ucv)); AlwaysAssertExit (allEQ (record.asArrayShort(26), sv)); AlwaysAssertExit (allEQ (record.asArrayInt(27), iv)); AlwaysAssertExit (allEQ (record.asArrayuInt(28), uiv)); AlwaysAssertExit (allEQ (record.asArrayInt64(29), i64v)); AlwaysAssertExit (allEQ (record.asArrayFloat(30), fv)); AlwaysAssertExit (allEQ (record.asArrayDouble(31), dv)); AlwaysAssertExit (allEQ (record.asArrayComplex(32), cv)); AlwaysAssertExit (allEQ (record.asArrayDComplex(33), dcv)); AlwaysAssertExit (allEQ (record.asArrayString(36), strv)); AlwaysAssertExit (allEQ (record.toArrayBool(24), bv)); AlwaysAssertExit (allEQ (record.toArrayuChar(25), ucv)); AlwaysAssertExit (allEQ (record.toArrayShort(26), sv)); AlwaysAssertExit (allEQ (record.toArrayInt(27), iv)); AlwaysAssertExit (allEQ (record.toArrayuInt(28), uiv)); AlwaysAssertExit (allEQ (record.toArrayInt64(29), i64v)); AlwaysAssertExit (allEQ (record.toArrayFloat(30), fv)); AlwaysAssertExit (allEQ (record.toArrayDouble(31), dv)); AlwaysAssertExit (allEQ (record.toArrayComplex(32), cv)); AlwaysAssertExit (allEQ (record.toArrayDComplex(33), dcv)); AlwaysAssertExit (allEQ (record.toArrayString(36), strv)); AlwaysAssertExit (allEQ (record.toArrayBool(11), *boolField)); AlwaysAssertExit (allEQ (record.toArrayuChar(12), *ucharField)); AlwaysAssertExit (allEQ (record.toArrayShort(12), Short(*ucharField))); AlwaysAssertExit (allEQ (record.toArrayInt(12), Int(*ucharField))); AlwaysAssertExit (allEQ (record.toArrayuInt(12), uInt(*ucharField))); AlwaysAssertExit (allEQ (record.toArrayInt64(12), Int64(*ucharField))); AlwaysAssertExit (allEQ (record.toArrayFloat(12), Float(*ucharField))); AlwaysAssertExit (allEQ (record.toArrayDouble(12), Double(*ucharField))); AlwaysAssertExit (allEQ (record.toArrayComplex(12), Complex(*ucharField,0))); AlwaysAssertExit (allEQ (record.toArrayDComplex(12), DComplex(*ucharField,0))); AlwaysAssertExit (allEQ (record.toArrayShort(13), *shortField)); AlwaysAssertExit (allEQ (record.toArrayInt(13), Int(*shortField))); AlwaysAssertExit (allEQ (record.toArrayuInt(13), uInt(*shortField))); AlwaysAssertExit (allEQ (record.toArrayInt64(13), Int64(*shortField))); AlwaysAssertExit (allEQ (record.toArrayFloat(13), Float(*shortField))); AlwaysAssertExit (allEQ (record.toArrayDouble(13), Double(*shortField))); AlwaysAssertExit (allEQ (record.toArrayComplex(13), Complex(*shortField,0))); AlwaysAssertExit (allEQ (record.toArrayDComplex(13), DComplex(*shortField,0))); AlwaysAssertExit (allEQ (record.toArrayInt(14), *intField)); AlwaysAssertExit (allEQ (record.toArrayInt64(14), Int64(*intField))); AlwaysAssertExit (allEQ (record.toArrayFloat(14), Float(*intField))); AlwaysAssertExit (allEQ (record.toArrayDouble(14), Double(*intField))); AlwaysAssertExit (allEQ (record.toArrayComplex(14), Complex(*intField,0))); AlwaysAssertExit (allEQ (record.toArrayDComplex(14), DComplex(*intField,0))); AlwaysAssertExit (allEQ (record.toArrayuInt(15), *uintField)); AlwaysAssertExit (allEQ (record.toArrayInt64(15), Int64(*uintField))); AlwaysAssertExit (allEQ (record.toArrayFloat(15), Float(*uintField))); AlwaysAssertExit (allEQ (record.toArrayDouble(15), Double(*uintField))); AlwaysAssertExit (allEQ (record.toArrayComplex(15), Complex(*uintField,0))); AlwaysAssertExit (allEQ (record.toArrayDComplex(15), DComplex(*uintField,0))); AlwaysAssertExit (allEQ (record.toArrayInt64(16), *int64Field)); AlwaysAssertExit (allEQ (record.toArrayFloat(16), Float(*int64Field))); AlwaysAssertExit (allEQ (record.toArrayDouble(16), Double(*int64Field))); AlwaysAssertExit (allEQ (record.toArrayComplex(16), Complex(*int64Field,0))); AlwaysAssertExit (allEQ (record.toArrayDComplex(16), DComplex(*int64Field,0))); AlwaysAssertExit (allEQ (record.toArrayFloat(17), *floatField)); AlwaysAssertExit (allEQ (record.toArrayDouble(17), Double(*floatField))); AlwaysAssertExit (allEQ (record.toArrayComplex(17), Complex(*floatField,0))); AlwaysAssertExit (allEQ (record.toArrayDComplex(17), DComplex(*floatField,0))); AlwaysAssertExit (allEQ (record.toArrayDouble(18), *doubleField)); AlwaysAssertExit (allEQ (record.toArrayFloat(18), Float(*doubleField))); AlwaysAssertExit (allEQ (record.toArrayComplex(18), Complex(*doubleField,0))); AlwaysAssertExit (allEQ (record.toArrayDComplex(18), DComplex(*doubleField,0))); AlwaysAssertExit (allEQ (record.toArrayComplex(19), *complexField)); AlwaysAssertExit (allEQ (record.toArrayDComplex(19), DComplex(*complexField))); AlwaysAssertExit (allEQ (record.toArrayDComplex(20), *dcomplexField)); AlwaysAssertExit (allEQ (record.toArrayComplex(20), Complex((*dcomplexField).real(), (*dcomplexField).imag()))); AlwaysAssertExit (allEQ (record.toArrayString(21), stringToVector("Hello,Goodbye"))); // Scalars as Arrays. RORecordFieldPtr > boolFieldA(record, 0); RORecordFieldPtr > ucharFieldA(record, 1); RORecordFieldPtr > shortFieldA(record, 2); RORecordFieldPtr > intFieldA(record, 3); RORecordFieldPtr > uintFieldA(record, 4); RORecordFieldPtr > int64FieldA(record, 5); RORecordFieldPtr > floatFieldA(record, 6); RORecordFieldPtr > doubleFieldA(record, 7); RORecordFieldPtr > complexFieldA(record, 8); RORecordFieldPtr > dcomplexFieldA(record, 9); RORecordFieldPtr > stringFieldA(record, 10); AlwaysAssertExit (allEQ (*boolFieldA, Vector(1, *boolField))); AlwaysAssertExit (allEQ (*ucharFieldA, Vector(1, *ucharField))); AlwaysAssertExit (allEQ (*shortFieldA, Vector(1, *shortField))); AlwaysAssertExit (allEQ (*intFieldA, Vector(1, *intField))); AlwaysAssertExit (allEQ (*uintFieldA, Vector(1, *uintField))); AlwaysAssertExit (allEQ (*int64FieldA, Vector(1, *int64Field))); AlwaysAssertExit (allEQ (*floatFieldA, Vector(1, *floatField))); AlwaysAssertExit (allEQ (*doubleFieldA, Vector(1, *doubleField))); AlwaysAssertExit (allEQ (*complexFieldA, Vector(1, *complexField))); AlwaysAssertExit (allEQ (*dcomplexFieldA, Vector(1, *dcomplexField))); AlwaysAssertExit (allEQ (*stringFieldA, Vector(1, *stringField))); // Array fields RORecordFieldPtr > arrayboolField(record, 11); RORecordFieldPtr > arrayucharField(record, 12); RORecordFieldPtr > arrayshortField(record, 13); RORecordFieldPtr > arrayintField(record, 14); RORecordFieldPtr > arrayuintField(record, 15); RORecordFieldPtr > arrayint64Field(record, 16); RORecordFieldPtr > arrayfloatField(record, 17); RORecordFieldPtr > arraydoubleField(record, 18); RORecordFieldPtr > arraycomplexField(record, 19); RORecordFieldPtr > arraydcomplexField(record, 20); RORecordFieldPtr > arraystringField(record, 21); AlwaysAssertExit(arrayboolField.comment() == "comment for TpArrayBool"); AlwaysAssertExit(allEQ(*arrayboolField, *boolField)); AlwaysAssertExit(allEQ(*arrayucharField, *ucharField)); AlwaysAssertExit(allEQ(*arrayshortField, *shortField)); AlwaysAssertExit(allEQ(*arrayintField, *intField)); AlwaysAssertExit(allEQ(*arrayuintField, *uintField)); AlwaysAssertExit(allEQ(*arrayint64Field, *int64Field)); AlwaysAssertExit(allEQ(*arrayfloatField, *floatField)); AlwaysAssertExit(allEQ(*arraydoubleField, *doubleField)); AlwaysAssertExit(allEQ(*arraycomplexField, *complexField)); AlwaysAssertExit(allEQ(*arraydcomplexField, *dcomplexField)); AlwaysAssertExit(allEQ(*arraystringField, stringToVector("Hello,Goodbye"))); // Sub(-sub)-record fields RORecordFieldPtr recordField(record, "SubRecord"); const Record& subrec = *recordField; AlwaysAssertExit(subrec.nfields() == 2); RORecordFieldPtr subref (subrec, 0); AlwaysAssertExit(*subref == 8.0); RORecordFieldPtr subref2 (record.subRecord (record.fieldNumber ("SubRecord")), 0); AlwaysAssertExit(*subref2 == 8.0); RORecordFieldPtr recordField1(record, "SubRecord1"); const Record& subrec1 = *recordField1; AlwaysAssertExit(! subrec1.isFixed()); AlwaysAssertExit(subrec1.nfields() == 3); RORecordFieldPtr sub(subrec1, "sub"); AlwaysAssertExit((*sub).isFixed()); AlwaysAssertExit((*sub).nfields() == 2); RORecordFieldPtr subrefa (*sub, 0); AlwaysAssertExit(*subrefa == 9.0); RORecordFieldPtr sub1(subrec1, "sub1"); AlwaysAssertExit(! (*sub1).isFixed()); AlwaysAssertExit((*sub1).nfields() == 2); AlwaysAssertExit((*sub1).asFloat(0) == 6.0); RORecordFieldPtr sub2(subrec1, "sub2"); AlwaysAssertExit(! (*sub2).isFixed()); AlwaysAssertExit((*sub2).nfields() == 2); AlwaysAssertExit((*sub2).asDouble("SubFloat") == 8.0); } casacore-3.7.1/casa/Containers/test/tRecord.out000066400000000000000000000141211476623553700214510ustar00rootroot00000000000000RecordInterface::define - new fieldNumber exceeds #fields Record field cannot be added: length<1 Record field aB cannot be added: no uppercase Record field A cannot be added: extra==10 RecordRep::defineDataField - incorrect data type used for field TpShort RecordRep::createDataField: unknown data type 12 ArrayBase::validateConformance shape [3] differs from [1] ArrayBase::validateConformance shape [3] differs from [1] ArrayBase::validateConformance shape [3] differs from [1] ArrayBase::validateConformance shape [3] differs from [1] Record::define - fixed array conformance error for field TpArrayString2 Record::define - fixed array conformance error for field TpBool RecordRep::get_pointer - incorrect data type String used for field TpBool with type Bool Record cannot be changed (fixed structure) >>> Instance-specific assertion error message: (Record.cc : 108) Failed AlwaysAssertExit conform (other) <<< RecordInterface::asComplex - invalid data type >>> Instance-specific assertion error message: (Record.cc : 108) Failed AlwaysAssertExit conform (other) <<< TpBool: Bool 0 TpUChar: uChar 0 TpShort: Short 0 TpInt: Int 0 TpUInt: uInt 0 TpInt64: Int64 0 TpFloat: Float 0 TpDouble: Double 0 TpComplex: Complex (0,0) TpDComplex: DComplex (0,0) TpString: String "" TpArrayBool: Bool array with shape [1] TpArrayUChar: uChar array with shape [1] TpArrayShort: Short array with shape [1] TpArrayInt: Int array with shape [1] TpArrayUInt: uInt array with shape [1] TpArrayInt64: Int64 array with shape [1] TpArrayFloat: Float array with shape [1] TpArrayDouble: Double array with shape [1] TpArrayComplex: Complex array with shape [1] TpArrayDComplex: DComplex array with shape [1] TpArrayString: String array with shape [] SubRecord1: { fa: Float 3 ia: Int 2 } SubRecord: { SubFloat: Float 0 SubInt: Int 0 } TpBool2a: Bool 0 TpUChar2a: uChar 0 TpShort2a: Short 0 TpInt2a: Int 0 TpUInt2a: uInt 0 TpInt642a: Int64 0 TpFloat2a: Float 0 TpDouble2a: Double 0 TpComplex2a: Complex (0,0) TpDComplex2a: DComplex (0,0) TpArrayString2a: String array with shape [3] TpArrayString3a: String array with shape [] TpString2a: String "" TpBool: Bool 0 TpUChar: uChar 0 TpShort: Short 0 TpInt: Int 0 TpUInt: uInt 0 TpInt64: Int64 0 TpFloat: Float 0 TpDouble: Double 0 TpComplex: Complex (0,0) TpDComplex: DComplex (0,0) TpString: String "" TpArrayBool: Bool array with shape [1] [0] TpArrayUChar: uChar array with shape [1] [0] TpArrayShort: Short array with shape [1] [0] TpArrayInt: Int array with shape [1] [0] TpArrayUInt: uInt array with shape [1] [0] TpArrayInt64: Int64 array with shape [1] [0] TpArrayFloat: Float array with shape [1] [0] TpArrayDouble: Double array with shape [1] [0] TpArrayComplex: Complex array with shape [1] [(0,0)] TpArrayDComplex: DComplex array with shape [1] [(0,0)] TpArrayString: String array with shape [] [] SubRecord1: { fa: Float 3 ia: Int 2 } SubRecord: { SubFloat: Float 0 SubInt: Int 0 } TpBool2a: Bool 0 TpUChar2a: uChar 0 TpShort2a: Short 0 TpInt2a: Int 0 TpUInt2a: uInt 0 TpInt642a: Int64 0 TpFloat2a: Float 0 TpDouble2a: Double 0 TpComplex2a: Complex (0,0) TpDComplex2a: DComplex (0,0) TpArrayString2a: String array with shape [3] [, , ] TpArrayString3a: String array with shape [] [] TpString2a: String "" TpBool: Bool 0 TpUChar: uChar 0 TpShort: Short 0 TpInt: Int 0 TpUInt: uInt 0 TpInt64: Int64 0 TpFloat: Float 0 TpDouble: Double 0 TpComplex: Complex (0,0) TpDComplex: DComplex (0,0) TpString: String "" TpArrayBool: Bool array with shape [1] [0] TpArrayUChar: uChar array with shape [1] [0] TpArrayShort: Short array with shape [1] [0] TpArrayInt: Int array with shape [1] [0] TpArrayUInt: uInt array with shape [1] [0] TpArrayInt64: Int64 array with shape [1] [0] TpArrayFloat: Float array with shape [1] [0] TpArrayDouble: Double array with shape [1] [0] TpArrayComplex: Complex array with shape [1] [(0,0)] TpArrayDComplex: DComplex array with shape [1] [(0,0)] TpArrayString: String array with shape [] [] SubRecord1: { fa: Float 3 ia: Int 2 } SubRecord: { SubFloat: Float 0 SubInt: Int 0 } TpBool2a: Bool 0 TpUChar2a: uChar 0 TpShort2a: Short 0 TpInt2a: Int 0 TpUInt2a: uInt 0 TpInt642a: Int64 0 TpFloat2a: Float 0 TpDouble2a: Double 0 TpComplex2a: Complex (0,0) TpDComplex2a: DComplex (0,0) TpArrayString2a: String array with shape [3] [, , ] TpArrayString3a: String array with shape [] [] TpString2a: String "" *1: { TpBool: Bool 0 TpUChar: uChar 0 TpShort: Short 0 TpInt: Int 0 TpUInt: uInt 0 TpInt64: Int64 0 TpFloat: Float 0 TpDouble: Double 0 TpComplex: Complex (0,0) TpDComplex: DComplex (0,0) TpString: String "" TpArrayBool: Bool array with shape [1] [0] TpArrayUChar: uChar array with shape [1] [0] TpArrayShort: Short array with shape [1] [0] TpArrayInt: Int array with shape [1] [0] TpArrayUInt: uInt array with shape [1] [0] TpArrayInt64: Int64 array with shape [1] [0] TpArrayFloat: Float array with shape [1] [0] TpArrayDouble: Double array with shape [1] [0] TpArrayComplex: Complex array with shape [1] [(0,0)] TpArrayDComplex: DComplex array with shape [1] [(0,0)] TpArrayString: String array with shape [] [] SubRecord1: { fa: Float 3 ia: Int 2 } SubRecord: { SubFloat: Float 0 SubInt: Int 0 } TpBool2a: Bool 0 TpUChar2a: uChar 0 TpShort2a: Short 0 TpInt2a: Int 0 TpUInt2a: uInt 0 TpInt642a: Int64 0 TpFloat2a: Float 0 TpDouble2a: Double 0 TpComplex2a: Complex (0,0) TpDComplex2a: DComplex (0,0) TpArrayString2a: String array with shape [3], first values: [] TpArrayString3a: String array with shape [] [] TpString2a: String "" } >>> Instance-specific assertion error message: (Record.cc : 281) Failed AlwaysAssertExit this != &other <<< RecordDesc::mergeField - duplicate in other Record cannot be changed (fixed structure) Record cannot be changed (fixed structure) OK casacore-3.7.1/casa/Containers/test/tRecordDesc.cc000066400000000000000000000252021476623553700220300ustar00rootroot00000000000000//# tRecordDesc.cc: Test the RecordDesc class //# Copyright (C) 1995,1996,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include void doIt (Bool doExcp); int main (int argc, const char*[]) { try { doIt ( (argc<2)); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } void doIt (Bool doExcp) { Bool equalDataTypes; // RecordDesc(); RecordDesc a, b; AlwaysAssertExit(a == b && a.nfields() == 0 && b.nfields() == 0); AlwaysAssertExit (a.isDisjoint (b)); AlwaysAssertExit (a.isEqual (b, equalDataTypes)); AlwaysAssertExit (equalDataTypes); AlwaysAssertExit (a.isSubset (b, equalDataTypes)); AlwaysAssertExit (a.isSuperset (b, equalDataTypes)); AlwaysAssertExit (! a.isStrictSuperset (b, equalDataTypes)); AlwaysAssertExit (! a.isStrictSubset (b, equalDataTypes)); // uInt addField(const String &fieldName, DataType type); // void setComment (uInt whichField, const String& comment); a.addField("a", TpString); a.addField("b", TpArrayDComplex); a.setComment (1, "comment for field b"); // Int fieldNumber(const String &fieldName); // uInt nfields() const; // DataType type(uInt whichField) const; // const String &comment (uInt whichField) const; // const String &name(uInt whichField) const; // const IPosition &shape(uInt whichField) const; // Bool isArray(uInt whichField) const; // Bool isScalar(uInt whichField) const; // Bool isSubRecord(uInt whichField) const; // Bool isTable(uInt whichField) const; // Bool operator==(const RecordDesc &other) const; // Bool operator!=(const RecordDesc &other) const; AlwaysAssertExit (a.isDisjoint (b)); AlwaysAssertExit (b.isDisjoint (a)); AlwaysAssertExit (! a.isEqual (b, equalDataTypes)); AlwaysAssertExit (a.comment (0) == ""); AlwaysAssertExit (a.comment (1) == "comment for field b"); AlwaysAssertExit(a.nfields() == 2 && a.name(0) == "a" && a.name(1) == "b" && a.type(0) == TpString && a.type(1) == TpArrayDComplex && !a.isArray(0) && a.isArray(1) && a.isScalar(0) && !a.isScalar(1) && !a.isSubRecord(0) && !a.isSubRecord(1) && !a.isTable(0) && !a.isTable(1) && a.tableDescName(0).empty() && a == a && !(a != a) && a.fieldNumber("a") == 0 && a.fieldNumber("b") == 1 && a.fieldNumber("c") == -1 && a.shape(0) == IPosition(1,1) && a.shape(1) == IPosition(1,-1)); // RecordDesc(const RecordDesc &other); RecordDesc c(a); AlwaysAssertExit(c == a && c.name(0) == "a" && c.name(1) == "b"); AlwaysAssertExit (c.comment (0) == ""); AlwaysAssertExit (c.comment (1) == "comment for field b"); RecordDesc c1(a); AlwaysAssertExit(c1 == c && c1.name(0) == "a" && c1.name(1) == "b"); // RecordDesc.addTable RecordDesc at(a); at.addTable("ctab","descName"); at.addField("ctab1",TpTable); AlwaysAssertExit (at.nfields()==4 && at.tableDescName(2) == "descName" && at.tableDescName(3) == ""); AlwaysAssertExit (at.isTable(2) && at.isTable(3)); AlwaysAssertExit (!at.isScalar(2) && !at.isArray(2) && !at.isSubRecord(2)); a.addField("c1", TpRecord); if (doExcp) { Bool caught = False; try { a.addField("a", TpDouble); // already exists } catch (std::exception& x) { caught = True; } AlwaysAssertExit(caught); caught = False; try { a.addField("a", TpDouble, IPosition(1,1)); // already exists } catch (std::exception& x) { caught = True; } AlwaysAssertExit(caught); caught = False; try { a.addField("a", TpDouble, IPosition(1,1)); // already exists } catch (std::exception& x) { caught = True; } AlwaysAssertExit(caught); caught = False; try { a.addField("aaa", TpOther); // invalid type } catch (std::exception& x) { caught = True; } AlwaysAssertExit(caught); } // RecordDesc &operator=(const RecordDesc &other); b = a; AlwaysAssertExit (b.comment (0) == ""); AlwaysAssertExit (b.comment (1) == "comment for field b"); AlwaysAssertExit(b == a && b.name(0) == "a" && b.name(1) == "b"); AlwaysAssertExit (! a.isDisjoint (b)); AlwaysAssertExit (a.isEqual (b, equalDataTypes)); AlwaysAssertExit (equalDataTypes); AlwaysAssertExit (! a.isStrictSubset (b, equalDataTypes)); // uInt addField(const String &fieldName, const RecordDesc &subDesc); b.addField("c", a); AlwaysAssertExit(b.nfields() == 4 && b.fieldNumber("c") == 3); // const RecordDesc &subRecord(uInt i) const; AlwaysAssertExit(b.subRecord(3) == a && b.isSubRecord(3) && !b.isScalar(3) && !b.isArray(3)); // uInt removeField(uInt whichField); b.removeField(2); AlwaysAssertExit(b.nfields() == 3); // uInt addField(const String &fieldName, DataType scalarOrArrayType, // const IPosition &shape); uInt whichField = b.addField("array", TpArrayInt, IPosition(2,3,4)); whichField--; AlwaysAssertExit(b.shape(whichField) == IPosition(2,3,4)); b.removeField(whichField); { // uInt mergeField(const RecordDesc &other, uInt whichFieldFromOther, // DuplicatesFlag DuplicateAction = ThrowOnDuplicates); // uInt merge(const RecordDesc &other, // DuplicatesFlag DuplicateAction = ThrowOnDuplicates); RecordDesc ab, cd; ab.addField("a", TpInt); ab.setComment (0, "a-comment"); ab.addField("b", TpFloat); cd.addField("c", TpArrayFloat); cd.setComment (0, "c-comment"); cd.addField("d", ab); ab.merge(cd); AlwaysAssertExit(ab.nfields() == 4 && ab.fieldNumber("c") == 2 && ab.fieldNumber("d") == 3); AlwaysAssertExit (ab.comment (2) == "c-comment"); ab.mergeField(ab, 0, RecordInterface::RenameDuplicates); AlwaysAssertExit(ab.nfields() == 5); AlwaysAssertExit(ab.fieldNumber("a_1") == 4); AlwaysAssertExit (ab.comment (2) == "c-comment"); AlwaysAssertExit (ab.comment (4) == "a-comment"); ab.removeField(4); ab.mergeField(ab, 0, RecordInterface::SkipDuplicates); AlwaysAssertExit(ab.nfields() == 4); cd.addField("a", TpString); cd.setComment (2, "a-comment-a"); ab.mergeField(cd, cd.fieldNumber("a"), RecordInterface::OverwriteDuplicates); AlwaysAssertExit (ab.comment (1) == "c-comment"); AlwaysAssertExit (ab.comment (3) == "a-comment-a"); AlwaysAssertExit(ab.type(ab.fieldNumber("a")) == TpString); if (doExcp) { Bool caught = False; try { ab.mergeField(cd, cd.fieldNumber("a"), RecordInterface::ThrowOnDuplicates); } catch (std::exception& x) { caught = True; } AlwaysAssertExit(caught); } } // Some other things to get the test coverage up RecordDesc d; AlwaysAssertExit(d != a); // Fails by nfields() differing d.addField("foo", a); d.addField("bar", a); AlwaysAssertExit(d != a); // fails by the type differing AlwaysAssertExit(d == d); RecordDesc e; e.addField("foo", d); AlwaysAssertExit(d != e); d = e; RecordDesc f; f.addField("bar", TpInt); AlwaysAssertExit(d != f); RecordDesc g; g.addField("bar", f); AlwaysAssertExit(d != g); g.addField("TpArrayBool", TpArrayBool, IPosition(1,1)); g.addField("TpArrayChar", TpArrayChar, IPosition(1,1)); g.addField("TpArrayUChar", TpArrayUChar, IPosition(1,1)); g.addField("TpArrayShort", TpArrayShort, IPosition(1,1)); g.addField("TpArrayUShort", TpArrayUShort, IPosition(1,1)); g.addField("TpArrayInt", TpArrayInt, IPosition(1,1)); g.addField("TpArrayUInt", TpArrayUInt, IPosition(1,1)); g.addField("TpArrayInt64", TpArrayInt64, IPosition(1,1)); g.addField("TpArrayFloat", TpArrayFloat, IPosition(1,1)); g.addField("TpArrayDouble", TpArrayDouble, IPosition(1,1)); g.addField("TpArrayComplex", TpArrayComplex, IPosition(1,1)); g.addField("TpArrayDComplex", TpArrayDComplex, IPosition(1,1)); g.addField("TpArrayString", TpArrayString, IPosition(1,1)); Int gn = g.nfields() - 1; AlwaysAssert (gn == g.fieldNumber("TpArrayString"), AipsError); AlwaysAssert (g.shape(gn) == IPosition(1,1), AipsError); // Test renameField. g.renameField ("newname", gn); AlwaysAssertExit (g.fieldNumber("TpArrayString") < 0); AlwaysAssertExit (g.fieldNumber("newname") == gn); g.renameField ("TpArrayString", gn); AlwaysAssertExit (g.fieldNumber("TpArrayString") == gn); AlwaysAssertExit (g.fieldNumber("newname") < 0); { // operator<<() RecordDesc rd; rd.addField("foo", TpInt); rd.setComment (0, "foo comment"); rd.addField("bar", TpDComplex, IPosition(2, 3, 4)); rd.addField("bar64", TpInt64); rd.addField("fubar", RecordDesc(rd)); rd.addTable("futab", "fuName"); cout << rd; AipsIO aos ("tRecordDesc_tmp.data", ByteIO::New); aos << rd; aos.close(); aos.open ("tRecordDesc_tmp.data"); RecordDesc rd1; rd1.addField ("should be removed by >>", TpShort); aos >> rd1; AlwaysAssertExit(rd == rd1); AlwaysAssertExit (rd1.comment (0) == "foo comment"); rd1.setComment (0, rd1.comment(0) + " bar"); AlwaysAssertExit (rd.comment (0) == "foo comment"); AlwaysAssertExit (rd1.comment (0) == "foo comment bar"); } if (doExcp) { Bool caught = False; try { g.addField("Other", TpRecord, IPosition(1,1)); } catch (std::exception& x) { caught = True; } AlwaysAssertExit(caught); caught = False; try { g.addField("Other", TpTable, IPosition(1,1)); } catch (std::exception& x) { caught = True; } AlwaysAssertExit(caught); } cout << "OK" << endl; // ~RecordDesc(); // implicit } casacore-3.7.1/casa/Containers/test/tRecordDesc.out000066400000000000000000000003361476623553700222530ustar00rootroot000000000000000 foo : Int foo comment 1 bar : Array[3, 4] 2 bar64 : Int64 3 fubar : SUBRECORD 0 foo : Int foo comment 1 bar : Array[3, 4] 2 bar64 : Int64 4 futab : TableDesc fuName OK casacore-3.7.1/casa/Containers/test/tValueHolder.cc000066400000000000000000000264031476623553700222310ustar00rootroot00000000000000//# tValueHolder.cc: Test the ValueHolder class //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include using namespace casacore; void doBool (Bool v) { ValueHolder vh(v); AlwaysAssertExit (!vh.isNull()); AlwaysAssertExit (vh.dataType() == TpBool); AlwaysAssertExit (vh.asBool() == v); Vector vec = vh.asArrayBool(); AlwaysAssertExit (vec.size() == 1 && vec.data()[0] == v); cout << vh << endl; Record rec; vh.toRecord (rec, "a"); ValueHolder vhc; AlwaysAssertExit (vhc.isNull()); vhc = ValueHolder::fromRecord (rec, "a"); AlwaysAssertExit (!vhc.isNull()); AlwaysAssertExit (vhc.dataType() == TpBool); Bool vc; vhc.getValue (vc); AlwaysAssertExit (vc == v); cout << vh << ' ' << vhc << endl; } template void doPos(T v, U, DataType dt) { ValueHolder vh(v); AlwaysAssertExit (!vh.isNull()); AlwaysAssertExit (vh.dataType() == dt); AlwaysAssertExit (vh.asBool()); AlwaysAssertExit (vh.asuChar() == uChar(v)); AlwaysAssertExit (vh.asShort() == Short(v)); AlwaysAssertExit (vh.asuShort() == uShort(v)); AlwaysAssertExit (vh.asInt() == Int(v)); AlwaysAssertExit (vh.asuInt() == uInt(v)); AlwaysAssertExit (vh.asInt64() == Int64(v)); AlwaysAssertExit (vh.asFloat() == float(v)); AlwaysAssertExit (vh.asDouble() == double(v)); AlwaysAssertExit (vh.asComplex() == Complex(float(v),0)); AlwaysAssertExit (vh.asDComplex() == DComplex(double(v),0)); Vector vecbool = vh.asArrayBool(); AlwaysAssertExit (vecbool.size() == 1 && vecbool.data()[0] == True); Vector vecuChar = vh.asArrayuChar(); AlwaysAssertExit (vecuChar.size() == 1 && vecuChar.data()[0] == uChar(v)); Vector vecShort = vh.asArrayShort(); AlwaysAssertExit (vecShort.size() == 1 && vecShort.data()[0] == Short(v)); Vector vecUShort = vh.asArrayuShort(); AlwaysAssertExit (vecUShort.size() == 1 && vecUShort.data()[0] == uShort(v)); Vector vecInt = vh.asArrayInt(); AlwaysAssertExit (vecInt.size() == 1 && vecInt.data()[0] == Int(v)); Vector vecuInt = vh.asArrayuInt(); AlwaysAssertExit (vecuInt.size() == 1 && vecuInt.data()[0] == uInt(v)); Vector vecInt64 = vh.asArrayInt64(); AlwaysAssertExit (vecInt64.size() == 1 && vecInt64.data()[0] == Int64(v)); Vector vecfloat = vh.asArrayFloat(); AlwaysAssertExit (vecfloat.size() == 1 && vecfloat.data()[0] == float(v)); Vector vecdouble = vh.asArrayDouble(); AlwaysAssertExit (vecdouble.size() == 1 && vecdouble.data()[0] == double(v)); Vector veccomplex = vh.asArrayComplex(); AlwaysAssertExit (veccomplex.size() == 1 && veccomplex.data()[0] == Complex(float(v), 0)); Vector vecdcomplex = vh.asArrayDComplex(); AlwaysAssertExit (vecdcomplex.size() == 1 && vecdcomplex.data()[0] == DComplex(double(v), 0)); Record rec; vh.toRecord (rec, "a"); ValueHolder vhc; AlwaysAssertExit (vhc.isNull()); vhc = ValueHolder::fromRecord (rec, "a"); AlwaysAssertExit (!vhc.isNull()); if (dt == TpUShort) { AlwaysAssertExit (vhc.dataType() == TpInt); } else { AlwaysAssertExit (vhc.dataType() == dt); } U vc; vhc.getValue (vc); AlwaysAssertExit (vc == U(v)); cout << vh << ' ' << vhc << endl; } template void doNeg(T v, U, DataType dt) { ValueHolder vh(v); AlwaysAssertExit (!vh.isNull()); AlwaysAssertExit (vh.dataType() == dt); AlwaysAssertExit (vh.asBool()); AlwaysAssertExit (vh.asShort() == Short(v)); AlwaysAssertExit (vh.asInt() == Int(v)); AlwaysAssertExit (vh.asInt64() == Int64(v)); AlwaysAssertExit (vh.asFloat() == float(v)); AlwaysAssertExit (vh.asDouble() == double(v)); AlwaysAssertExit (vh.asComplex() == Complex(float(v),0)); AlwaysAssertExit (vh.asDComplex() == DComplex(double(v),0)); Vector vecbool = vh.asArrayBool(); AlwaysAssertExit (vecbool.size() == 1 && vecbool.data()[0] == True); Vector vecShort = vh.asArrayShort(); AlwaysAssertExit (vecShort.size() == 1 && vecShort.data()[0] == Short(v)); Vector vecInt = vh.asArrayInt(); AlwaysAssertExit (vecInt.size() == 1 && vecInt.data()[0] == Int(v)); Vector vecInt64 = vh.asArrayInt64(); AlwaysAssertExit (vecInt64.size() == 1 && vecInt64.data()[0] == Int64(v)); Vector vecfloat = vh.asArrayFloat(); AlwaysAssertExit (vecfloat.size() == 1 && vecfloat.data()[0] == float(v)); Vector vecdouble = vh.asArrayDouble(); AlwaysAssertExit (vecdouble.size() == 1 && vecdouble.data()[0] == double(v)); Vector veccomplex = vh.asArrayComplex(); AlwaysAssertExit (veccomplex.size() == 1 && veccomplex.data()[0] == Complex(float(v), 0)); Vector vecdcomplex = vh.asArrayDComplex(); AlwaysAssertExit (vecdcomplex.size() == 1 && vecdcomplex.data()[0] == DComplex(double(v), 0)); Record rec; vh.toRecord (rec, "a"); ValueHolder vhc; AlwaysAssertExit (vhc.isNull()); vhc = ValueHolder::fromRecord (rec, "a"); AlwaysAssertExit (!vhc.isNull()); AlwaysAssertExit (vhc.dataType() == dt); U vc; vhc.getValue (vc); AlwaysAssertExit (vc == v); cout << vh << ' ' << vhc << endl; } void doComplex (const Complex& v) { ValueHolder vh(v); AlwaysAssertExit (!vh.isNull()); AlwaysAssertExit (vh.dataType() == TpComplex); AlwaysAssertExit (vh.asComplex() == v); AlwaysAssertExit (vh.asDComplex() == DComplex(v)); Vector vec = vh.asArrayComplex(); AlwaysAssertExit (vec.size() == 1 && vec.data()[0] == v); Vector vecd = vh.asArrayDComplex(); AlwaysAssertExit (vecd.size() == 1 && vecd.data()[0] == DComplex(v)); Record rec; vh.toRecord (rec, "a"); ValueHolder vhc; AlwaysAssertExit (vhc.isNull()); vhc = ValueHolder::fromRecord (rec, "a"); AlwaysAssertExit (!vhc.isNull()); AlwaysAssertExit (vhc.dataType() == TpComplex); Complex vc; vhc.getValue (vc); AlwaysAssertExit (vc == v); cout << vh << ' ' << vhc << endl; } void doDComplex (const DComplex& v) { ValueHolder vh(v); AlwaysAssertExit (!vh.isNull()); AlwaysAssertExit (vh.dataType() == TpDComplex); AlwaysAssertExit (vh.asComplex() == Complex(v)); AlwaysAssertExit (vh.asDComplex() == v); Vector vec = vh.asArrayComplex(); AlwaysAssertExit (vec.size() == 1 && vec.data()[0] == Complex(v)); Vector vecd = vh.asArrayDComplex(); AlwaysAssertExit (vecd.size() == 1 && vecd.data()[0] == v); Record rec; vh.toRecord (rec, "a"); ValueHolder vhc; AlwaysAssertExit (vhc.isNull()); vhc = ValueHolder::fromRecord (rec, "a"); AlwaysAssertExit (!vhc.isNull()); AlwaysAssertExit (vhc.dataType() == TpDComplex); DComplex vc; vhc.getValue (vc); AlwaysAssertExit (vc == v); cout << vh << ' ' << vhc << endl; } void doString (const String& v) { ValueHolder vh1(v); AlwaysAssertExit (!vh1.isNull()); AlwaysAssertExit (vh1.dataType() == TpString); AlwaysAssertExit (vh1.asString() == v); ValueHolder vh2(v.c_str()); AlwaysAssertExit (!vh2.isNull()); AlwaysAssertExit (vh2.dataType() == TpString); AlwaysAssertExit (vh2.asString() == v); Vector vec = vh1.asArrayString(); AlwaysAssertExit (vec.size() == 1 && vec.data()[0] == v); Record rec; vh1.toRecord (rec, "a"); ValueHolder vhc; AlwaysAssertExit (vhc.isNull()); vhc = ValueHolder::fromRecord (rec, "a"); AlwaysAssertExit (!vhc.isNull()); AlwaysAssertExit (vhc.dataType() == TpString); String vc; vhc.getValue (vc); AlwaysAssertExit (vc == v); ValueHolder vh3("123"); ValueHolder vh4(vhc); vh3 = vhc; cout << vh1 << ' ' << vh2 << ' ' << vhc << ' ' << vh3 << ' ' << vh4 << endl; } void doArrayDComplex (const Array& v) { ValueHolder vh(v); AlwaysAssertExit (!vh.isNull()); AlwaysAssertExit (vh.dataType() == TpArrayDComplex); Array ac(v.shape()); convertArray (ac, v); AlwaysAssertExit (allEQ (vh.asArrayComplex(), ac)); AlwaysAssertExit (allEQ (vh.asArrayDComplex(), v)); Record rec; vh.toRecord (rec, "a"); ValueHolder vhc; AlwaysAssertExit (vhc.isNull()); vhc = ValueHolder::fromRecord (rec, "a"); AlwaysAssertExit (!vhc.isNull()); AlwaysAssertExit (vhc.dataType() == TpArrayDComplex); Array vc; vhc.getValue (vc); AlwaysAssertExit (allEQ (vc, v)); cout << vh << ' ' << vhc << endl; } void doArrayString (const Array& v) { ValueHolder vh1(v); AlwaysAssertExit (!vh1.isNull()); AlwaysAssertExit (vh1.dataType() == TpArrayString); AlwaysAssertExit (allEQ(vh1.asArrayString(), v)); Record rec; vh1.toRecord (rec, "a"); ValueHolder vhc; AlwaysAssertExit (vhc.isNull()); vhc = ValueHolder::fromRecord (rec, "a"); AlwaysAssertExit (!vhc.isNull()); AlwaysAssertExit (vhc.dataType() == TpArrayString); Array vc; vhc.getValue (vc); AlwaysAssertExit (allEQ(vc, v)); cout << vh1 << ' ' << vhc << endl; } int main() { try { doBool(True); doBool(False); doNeg(Short(-4), Short(0), TpShort); doNeg(Int(-7), Int(0), TpInt); doNeg(Int64(-40), Int64(0), TpInt64); doNeg(float(-4.1), float(0), TpFloat); doNeg(double(-4.7), double(0), TpDouble); doPos(uChar(13), uChar(0), TpUChar); doPos(Short(4), Short(0), TpShort); doPos(uShort(14), uShort(0), TpUShort); doPos(Int(17), Int(0), TpInt); doPos(uInt(10), uInt(0), TpUInt); doPos(Int64(40), Int64(0), TpInt64); doPos(float(4.1), Float(0), TpFloat); doPos(double(4.7), Double(0), TpDouble); doString(String()); doString(""); doString("astring"); doComplex(Complex(3.1, -1.5)); doDComplex(DComplex(-3.2, 11.5)); Array dcomplexs(IPosition(2,1,2)); dcomplexs(IPosition(2,0,0)) = DComplex(-1.311, 1.422); dcomplexs(IPosition(2,0,1)) = DComplex(-11.311, 21.422); doArrayDComplex (dcomplexs); Array strings(IPosition(2,1,1)); strings = "abc"; doArrayString (strings); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; // exit with success status } casacore-3.7.1/casa/Containers/test/tValueHolder.out000066400000000000000000000007731476623553700224550ustar00rootroot000000000000001 1 1 0 0 0 -4 -4 -7 -7 -40 -40 -4.1 -4.1 -4.7 -4.7 13 13 4 4 14 14 17 17 10 10 40 40 4.1 4.1 4.7 4.7 astring astring astring astring astring (3.1,-1.5) (3.1,-1.5) (-3.2,11.5) (-3.2,11.5) Axis Lengths: [1, 2] (NB: Matrix in Row/Column order) [(-1.311,1.422), (-11.311,21.422)] Axis Lengths: [1, 2] (NB: Matrix in Row/Column order) [(-1.311,1.422), (-11.311,21.422)] Axis Lengths: [1, 1] (NB: Matrix in Row/Column order) [abc] Axis Lengths: [1, 1] (NB: Matrix in Row/Column order) [abc] OK casacore-3.7.1/casa/Containers/test/words.readme000066400000000000000000000011411476623553700216310ustar00rootroot00000000000000 words is a selection of 3916 from the web2 word list which is destributed as part of the GNU miscfiles package. Below is the copyright information # @(#)README 5.1 (Berkeley) 5/7/91 WEB ---- (introduction provided by jaw@riacs) ------------------------- Welcome to web2 (Webster's Second International) all 234,936 words worth. The 1934 copyright has elapsed, according to the supplier. The supplemental 'web2a' list contains hyphenated terms as well as assorted noun and adverbial phrases. The wordlist makes a dandy 'grep' victim. -- James A. Woods {ihnp4,hplabs}!ames!jaw (or jaw@riacs) casacore-3.7.1/casa/Exceptions.h000066400000000000000000000052461476623553700165340ustar00rootroot00000000000000//# Exceptions.h: a module for exception handling //# Copyright (C) 1995,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_EXCEPTIONS_H #define CASA_EXCEPTIONS_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // //

        // Exception handling // // // // // // // This module provides the exception handling mechanism used in Casacore. // It allows the user to define new exception types and to throw, // catch, and rethrow these exceptions. The interface // to this exception handling mechanism is very similary to the ANSI standard // exceptions. This will make it easy to switch when the time comes. // // This module supplies several common exception types . // These provide examples for creating new errors. // // At some point, this exception mechanism will probably be replaced with // compiler supported exceptions. // // // // This example shows how a more specific exception can be caught as // a more general exception: // // #include // #include // main() { // try { // throw(indexError(5,"Dummy error")); // } catch (IndexError xx) { // cout << "caught IndexError" << endl; // } // } // // // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Exceptions/000077500000000000000000000000001476623553700163545ustar00rootroot00000000000000casacore-3.7.1/casa/Exceptions/CasaErrorTools.cc000066400000000000000000000123071476623553700215700ustar00rootroot00000000000000//# CasaErrorTools.cc: Tools for error printing //# Copyright (C) 2012 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# Author: jjacobs #include #include #if !defined(USE_STACKTRACE) namespace casacore { // Stub out the related functions if functionality not enabled. void CasaErrorTools::generateSharedObjectMap() {} String CasaErrorTools::generateStackTrace() { return String(); } String CasaErrorTools::replaceStackAddresses (const String & stackTrace) { return "-->CasaErrorTools:: FYI: Could not replace stack addresses\n\n" + stackTrace; } } // end namespace casacore #else #include #include #include #include namespace { std::map sharedObjectMap; extern "C" int callback (struct dl_phdr_info *info, size_t, void *) { sharedObjectMap[info->dlpi_name] = info->dlpi_addr; return 0; } } // end namespace UNNAMED namespace casacore { void CasaErrorTools::generateSharedObjectMap () { sharedObjectMap.clear(); dl_iterate_phdr(callback, NULL); } String CasaErrorTools::generateStackTrace() { // Get the most recent 512 levels of the stack trace. // This ought to handle all cases but very deep recursion. void* stack[512]; // Allow for up to 512 levels int nLevels = backtrace (stack, 512); // Convert the internal stack representation into one string // per level. char ** trace = backtrace_symbols (stack, nLevels); // Put a header on the message and then append all of the // strings onto the message. String stackTrace; stackTrace += "\n||> Stack trace (use c++filt to demangle):\n"; for (int i = 0; i < nLevels; i++){ stackTrace += trace[i] + String ("\n"); } // Free up the array returned by backtrace_symbols. The // strings it points to must not be deleted by this method. free (trace); return stackTrace; } String CasaErrorTools::replaceStackAddresses (const String & stackTrace) { if (sharedObjectMap.empty()){ generateSharedObjectMap (); } String cleanedStackTrace; // Break the stack trace into lines by splitting at the '\n' string lines [500]; int nSplits = split (stackTrace, lines, 500, "\n"); for (int i = 0; i < nSplits; i++){ string & line = lines [i]; String cleanedLine; try { // Find the shared object name. if (line [0] != '/'){ throw True; } size_t openParenthesis = line.find ("("); if (openParenthesis == String::npos){ throw True; } String objectName = line.substr (0, openParenthesis); // Find the stack address. // This will be a hex number enclosed in square brackets // relative to the start of the shared object. size_t leftSquare = line.find ("["); size_t rightSquare = line.find ("]"); if (leftSquare == String::npos || rightSquare == String::npos || rightSquare <= leftSquare){ throw True; } // Extract the address and subtract the base of the shared object's // starting address to produce the offset within the shared object. String addressText = line.substr (leftSquare + 1, rightSquare - leftSquare - 1); uInt64 address = strtoll (addressText.c_str(), NULL, 16); uInt64 objectBase = sharedObjectMap [objectName]; uInt64 offset = address - objectBase; // Now rebuild the line replacing the original address ([0xHHHH...]) // with the offset (format [+0xHHHH...] char offsetInHex [128]; snprintf (offsetInHex, 127, "0x%llx", offset); cleanedLine = line.substr (0, leftSquare) + "[+" + offsetInHex + "]"; } catch (Bool){ // The line was not parseable so just copy it to the result. cleanedLine = line; } // Put the cleaned line into the result. cleanedStackTrace += "\n" + cleanedLine; } return cleanedStackTrace; } } // end namespace casacore #endif casacore-3.7.1/casa/Exceptions/CasaErrorTools.h000066400000000000000000000031641476623553700214330ustar00rootroot00000000000000//# CasaErrorTools.cc: Tools for error printing //# Copyright (C) 2012 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# Author: jjacobs #ifndef CASA_CASAERRORTOOLS_H #define CASA_CASAERRORTOOLS_H #include #include namespace casacore { class CasaErrorTools { public: static String generateStackTrace (); static String replaceStackAddresses (const String & stackTrace); private: static void generateSharedObjectMap (); }; } //# end namespace casacore #endif casacore-3.7.1/casa/Exceptions/Error.h000066400000000000000000000347141476623553700176270ustar00rootroot00000000000000//# Error.h: Base class for all Casacore errors //# Copyright (C) 1993,1994,1995,1999,2000,2001,2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_ERROR_H #define CASA_ERROR_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Throw the given exception with a string composed of various arguments. // E.g. // // CASATHROW (AipsError, "integer=" << myint << ", float=" << myfloat); // #define CASATHROW(exc, arg) do { \ std::ostringstream casa_log_oss; \ casa_log_oss << arg; \ throw exc(casa_log_oss.str()); \ } while (0) // The Assert macro is an alias to the standard assert macro when NDEBUG is defined. When // NDEBUG is not defined (release build) then a throw is used to report the error. #ifdef NDEBUG #define AssertCc(c) ((void)0) #else #define AssertCc(c) { if (AIPS_UNLIKELY(! (c))) {casacore::AipsError::throwIf (casacore::True, "Assertion failed: " #c, __FILE__, __LINE__, __PRETTY_FUNCTION__); }} #endif #define AssertAlways(c) { if (AIPS_UNLIKELY(! (c))) {casacore::AipsError::throwIf (casacore::True, "Assertion failed: " #c, __FILE__, __LINE__, __PRETTY_FUNCTION__); }} #define WarnCc(m)\ {\ LogIO os(LogOrigin("", __func__, __LINE__, WHERE));\ os << LogIO::WARN << m << LogIO::POST;\ } // Asserts when in debug build and issues a warning message to the log in release. #if defined (NDEBUG) #define AssertOrWarn(c,m) ((void)0) #else #define AssertOrWarn(c,m)\ { if (AIPS_UNLIKELY(! (c))) {\ WarnCc (m);\ }\ } #endif #if defined (NDEBUG) # define ThrowCc(m) \ { casacore::AipsError anAipsError ((m), __FILE__, __LINE__); \ throw anAipsError; } #else # define ThrowCc(m) throw casacore::AipsError ((m), __FILE__, __LINE__) #endif // Throw an AipsError exception if the condition is true. #define ThrowIf(c,m) {if (AIPS_UNLIKELY(c)) {casacore::AipsError::throwIf (casacore::True, (m), __FILE__, __LINE__, __PRETTY_FUNCTION__);}} // Throw an AipsError exception if the system error code is not 0. // It adds the message for that error code to the exception text. #define ThrowIfError(c,m) {if (AIPS_UNLIKELY(c)) {casacore::AipsError::throwIfError (casacore::True, (m), __FILE__, __LINE__, __PRETTY_FUNCTION__);}} // Repackage and rethrow an AipsError exception. #define Rethrow(e,m) {throw casacore::AipsError::repackageAipsError ((e),(m),__FILE__,__LINE__, __PRETTY_FUNCTION__);} // Base class for all Casacore library errors // // // // // // //
      • ExcpError // // // // This is the base class for all of the Casacore error classes. Because // all of the errors have a common base class, any error can be caught // with a single catch statement. // // This class has a string which allows error messages to be propagated. // // The string member must be handled very carefully because // string is also derived from cleanup, thus the // message.makePermanent() call in the implementation of // the constructors. This prevents the String from being cleaned up // in the middle of an exception. // // // // // // // throw(AipsError("SOME STRING")); // // // // // class AipsError: public std::exception { public: enum Category { BOUNDARY, INITIALIZATION, INVALID_ARGUMENT, CONFORMANCE, ENVIRONMENT, SYSTEM, PERMISSION, GENERAL }; // // Simply returns the stored error message. // virtual const char* what() const noexcept { return(message.c_str()); } const String &getMesg() const { return(message); } String getStackTrace() const; AipsError::Category getCategory( ) const { return(category); } // Append a message. This is used by LogIO when an exception is logged. // The message is const to be able to use it for a temporary exception. void setMessage (const String& msg) const { const_cast(this)->message = msg; } // Creates an AipsError and initializes the error message from // the parameter. // AipsError (const Char *str, Category c = GENERAL); AipsError (const String &str, Category c = GENERAL); AipsError (const String &msg, const String &filename, uInt lineNumber, Category c = GENERAL); AipsError (Category c = GENERAL); // // // Destructor which does nothing. // ~AipsError() noexcept; // Get or clear the stacktrace info. // static void getLastInfo (String & message, String & stackTrace); static String getLastMessage (); static String getLastStackTrace (); static void clearLastInfo (); // // Repackage an exception. static AipsError repackageAipsError (AipsError& error, const String& message, const char* file, Int line, const char* func); // Throw if the condition is true. static void throwIf (Bool condition, const String& message, const char* file, Int line, const char* func = ""); // Throw if the system error code is not 0. static void throwIfError (Int errorCode, const String& prefix, const char* file, Int line, const char* func = ""); protected: // Add the stack trace to the message (if USE_STACKTRACE is set). void addStackTrace (); String message; Category category; String stackTrace; }; // Allocation errors // // // // // // // // This class is used for allocation errors. It adds an extra // data item, the failed allocation size. Otherwise much the // same as AipsError. // // // // // // throw(AllocError("ANY STRING",1024)); // // // // // class AllocError : public AipsError { protected: size_t Size; public: // // This constructor takes the error message and the failed // allocation size. // // AllocError(const Char *str, uInt sze) : AipsError(str,SYSTEM), Size(sze) {} AllocError(const String &str, uInt sze) : AipsError(str,SYSTEM), Size(sze) {} // // // This function returns the failed allocation size. // size_t size() const {return(Size);} // // Destructor which does nothing. // ~AllocError() noexcept; }; // Base class for all indexing errors // // // // // // // This class is the base class of all IndexErrors. It is // defined to allow the user to catch any of the many kinds of IndexErrors // which may be thrown. It can also be thrown itself if returning // the illegal index value is unimportant. // // // // // throw(IndexError("ANY STRING")); // // // // // class IndexError : public AipsError { public: // // Creates an GeneralIndexError and initializes the error message from // the parameter // IndexError(const Char *str,Category c=BOUNDARY) : AipsError(str,c) {} IndexError(const String &str,Category c=BOUNDARY) : AipsError(str,c) {} IndexError(Category c=BOUNDARY) : AipsError(c) {} // // // Destructor which does nothing. // ~IndexError() noexcept; }; // Index errors returning the bad index // // // // // // // This class is templated to allow generalalized indexes to be returned // with the error message i.e. the class is templated on the index type. // // // // // // throw(indexError(3,"ANY STRING"));/ // // // // // template class indexError : public IndexError { protected: t oIndex; // Offending Index public: // // This constructor takes the error message and the index // which cause the error to occur. // // indexError(t oI, const Char *str, Category c=BOUNDARY); indexError(t oI, const String &str, Category c=BOUNDARY); indexError(t oI, Category c=BOUNDARY) : IndexError(c), oIndex(oI) {}; // // // Destructor which does nothing. // ~indexError() noexcept; }; // Duplicate key errors // // // // // // // This class is the base class of all duplicate key errors. It is // defined to allow the user to catch any of the many kinds of DuplErrors // which may be thrown. It can also be thrown itself if returning // the illegal key is unimportant. // // // // // throw(DuplError("ANY STRING")); // // // // // class DuplError : public AipsError { public: // // Creates an DuplError and initializes the error message from // the parameter // DuplError(Category c=BOUNDARY) : AipsError(c) {} DuplError(const Char *str,Category c=BOUNDARY) : AipsError(str,c) {} DuplError(const String &str,Category c=BOUNDARY) : AipsError(str,c) {} // // // Destructor which does nothing. // ~DuplError() noexcept; }; // Duplicate key errors where the bad key is returned // // // // // // // This template is for generalized duplicate key errors where the template // type parameter is the type of the key which caused the error. Because this // class is derived from DuplError // , the user to catch all duplicate key errors with one catch // statement. // // // // // throw(duplError(4,"ANY STRING")); // // // // template class duplError : public DuplError { protected: t oKey; // Offending Key public: // // This constructs a "duplError" for the offending key, and an // optional character string. // // duplError(t oI, const Char *str,Category c=BOUNDARY); duplError(t oI, const String &str,Category c=BOUNDARY); duplError(t oI,Category c=BOUNDARY) : DuplError(c), oKey(oI) {}; // // // Destructor which does nothing. // ~duplError() noexcept; }; // Exception for an error in a system call // // // // // // // This error is to be used for if a system call returns an error. // It uses strerror to get the system error message. // class SystemCallError : public AipsError { public: // This constructs a "SystemCallError" from the system call function name // and the errno. SystemCallError(const String &funcName, int error, Category c=GENERAL); SystemCallError (int error, const String &msg, const String &filename, uInt lineNumber, Category c=GENERAL); // Destructor which does nothing. ~SystemCallError() noexcept; // Get the errno. int error() const { return itsError; } // Get the message belonging to an error. static String errorMessage(int error); private: int itsError; }; // Exception which halts execution // // // // // // // This error causes an execution to halt regardless. It // causes execution to halt before the exception can be caught. // // // // // throw(AbortError("ANY STRING")); // // // // // class AbortError : public AipsError { public: // // This constructs a "AbortError" from the error message. // // AbortError(const Char *str,Category c=GENERAL); AbortError(const String &str,Category c=GENERAL); // // // Destructor which does nothing. // ~AbortError() noexcept; }; // Initialization error, typically of static data shared between objects // // // // // // // This error indicates that some initialization has failed. It is preferable // to throw this in an initX() function called by std::call_once() or similar // over returning a bool or other result variable. // // // // class InitError : public AipsError { }; } //# NAMESPACE CASACORE - END #ifdef AIPS_NEEDS_RETHROW #ifndef CASACORE_NEEDS_RETHROW #define CASACORE_NEEDS_RETHROW #endif #endif #ifdef CASACORE_NEEDS_RETHROW #define RETHROW(X) throw(X); #else #define RETHROW(X) #endif #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/casa/Exceptions/Error.tcc000066400000000000000000000040471476623553700201450ustar00rootroot00000000000000//# Error.cc: Base class for all Casacore errors (templated classes) //# Copyright (C) 1993,1994,1995,1996,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_ERROR_TCC #define CASA_ERROR_TCC #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template indexError::indexError( t oI, const Char *str, Category c ) : IndexError(str,c), oIndex (oI) {} template indexError::indexError( t oI, const String &str, Category c ) : IndexError(str,c), oIndex (oI) {} template indexError::~indexError() noexcept {} template duplError::duplError(t oI, const Char *str,Category c) : DuplError(str,c), oKey (oI) {} template duplError::duplError(t oI, const String &str,Category c) : DuplError(str,c), oKey (oI) {} template duplError::~duplError() noexcept {} } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Exceptions/Error2.cc000066400000000000000000000164671476623553700200540ustar00rootroot00000000000000//# Error2.cc: Base class for all Casacore errors (non-templated classes) //# Copyright (C) 1993,1994,1995,1996,1997,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include //# needed for strerror_r //# Stacktracing requires some extra includes. #ifdef USE_STACKTRACE # include # include # include # define AddStackTrace() addStackTrace() #else # define AddStackTrace() #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN AipsError::AipsError (Category c) : message(), category(c) { AddStackTrace(); } AipsError::AipsError(const Char *str,Category c) : message(str), category(c) { AddStackTrace(); } AipsError::AipsError(const String &str,Category c) : message(str), category(c) { AddStackTrace(); } AipsError::AipsError (const String &msg, const String& filename, uInt lineNumber, Category c) : category(c) { ostringstream os; os << msg << " at File: " << filename << ", line: " << lineNumber; message = os.str(); AddStackTrace (); } AipsError::~AipsError() noexcept {} #ifndef USE_STACKTRACE String AipsError::getStackTrace () const { return stackTrace; } void AipsError::addStackTrace() {} void AipsError::getLastInfo (String&, String&) {} String AipsError::getLastMessage () { return String(); } String AipsError::getLastStackTrace () { return String(); } void AipsError::clearLastInfo () {} #else // error message from last exception static String lastMessage = "*none*"; // stack trace from last exception static String lastStackTrace = "*no-stack-trace*"; // protects the lastMessage and lastStackTrace statics static std::mutex lastErrorMutex; void AipsError::getLastInfo (String & message, String & stackTrace) { std::lock_guard lock(lastErrorMutex); message = getLastMessage(); stackTrace = getLastStackTrace(); } String AipsError::getLastMessage () { return lastMessage; } String AipsError::getLastStackTrace () { return CasaErrorTools::replaceStackAddresses (lastStackTrace); } void AipsError::clearLastInfo () { std::lock_guard lock(lastErrorMutex); lastMessage = "*none*"; lastStackTrace = "*no-stack-trace*"; } void AipsError::addStackTrace() { // Always generate a stack trace and keep it around in a static // for later retrieval via casapy stackTrace = CasaErrorTools::generateStackTrace(); { std::lock_guard lock(lastErrorMutex); lastMessage = message; lastStackTrace = stackTrace; } // See if the default is to tack on the stack trace on the exception // message. N.B.: Turning this on will break some of the low-level tests // which simply compare expected to actual output. Bool enabled; AipsrcValue::find (enabled, "AipsError.enableStackTrace", False); if (enabled) { // If permitted, append to the error message. message += stackTrace; } } String AipsError::getStackTrace () const { return CasaErrorTools::replaceStackAddresses (stackTrace); } #endif void AipsError::throwIf (bool condition, const String& message, const char* file, Int line, const char* func) { // If the condition is met then throw an AipsError if (condition) { String m = String::format ("Exception: %s.\n... thrown by %s", message.c_str(), func); AipsError e (m.c_str(), file, line); throw e; } } void AipsError::throwIfError (int errorCode, const String& prefix, const char* file, Int line, const char* func) { // If the provided error code is not equal to success (0) then // throw an AipsError using the provided prefix and then details // of the error. if (errorCode != 0) { AipsError e (String::format ("Exception: %s.\n...Thrown by %s\n...(errno=%d): %s", prefix.c_str(), func, errorCode, strerror (errorCode)), file, line); throw e; } } AipsError AipsError::repackageAipsError (AipsError& error, const String& message, const char* file, Int line, const char* func) { ostringstream os; AipsError tmp (message, file, line); os << "+++Exception: " << tmp.getMesg() << ".\n...Thrown by " << func << ": " << "\n...Lower level exception: " << error.getMesg() << "\n--- end exception\n"; return AipsError (os.str()); } AllocError::~AllocError() noexcept {} IndexError::~IndexError() noexcept {} DuplError::~DuplError() noexcept {} SystemCallError::SystemCallError(const String& funcName, int error, Category c) : AipsError("Error in " + funcName + ": " + errorMessage(error), c), itsError (error) {} SystemCallError::SystemCallError (int error, const String &msg, const String &filename, uInt lineNumber, Category c) : AipsError (msg + String::format (": errno=%d: %s", error, errorMessage (error).c_str()), filename, lineNumber, c), itsError (error) {} SystemCallError::~SystemCallError() noexcept {} String strerror_overload(int err) { return "errno " + String::toString(err); } String strerror_overload(char *err) { return err; } String SystemCallError::errorMessage(int error) { // Use strerror_r for thread-safety. char buffer[128]; return strerror_overload(strerror_r(error, buffer, sizeof buffer)); } // Exception which causes an abort instead of continuing AbortError::AbortError(const Char *str,Category c) : AipsError(str,c) { cerr << "An unrecoverable error occurred: " << endl; cerr << str << endl; #ifndef CASACORE_NOEXIT exit(1); #endif } AbortError::AbortError(const String &str,Category c) : AipsError(str,c) { cerr << "An unrecoverable error occurred: " << endl; cerr << str << endl; #ifndef CASACORE_NOEXIT exit(1); #endif } AbortError::~AbortError() noexcept {} } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Exceptions/test/000077500000000000000000000000001476623553700173335ustar00rootroot00000000000000casacore-3.7.1/casa/Exceptions/test/CMakeLists.txt000066400000000000000000000004351476623553700220750ustar00rootroot00000000000000set (tests tError ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_casa) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/casa/Exceptions/test/tError.cc000066400000000000000000000036131476623553700211220ustar00rootroot00000000000000//# tError.cc: Test program for the Error class //# Copyright (C) 2014 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include using namespace casacore; int main() { try { CASATHROW (AipsError, "msg " << 31); } catch (const std::exception& x) { cout << x.what() << endl; AlwaysAssertExit (std::string(x.what()) == "msg 31"); } ThrowIf (false, "msg1"); bool caught = false; try { ThrowIf (true, "msg2"); } catch (const std::exception& x) { cout << x.what() << endl; caught = true; } AlwaysAssertExit (caught); ThrowIfError (0, "msg1"); caught = false; try { ThrowIfError (10, "msg2"); } catch (const std::exception& x) { cout << x.what() << endl; caught = true; } AlwaysAssertExit (caught); return 0; } casacore-3.7.1/casa/HDF5.h000066400000000000000000000132411476623553700150730ustar00rootroot00000000000000//# HDF5.h: Classes binding casacore to the HDF5 C API //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_HDF5_H #define CASA_HDF5_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // // // Classes binding casacore to the HDF5 C API // // //
      • HDF5 (see http://www.hdfgroup.uiuc.edu/HDF5/doc_dev_snapshot) // // // // // 'HDF5' is version 5 of the Hierarchical Data Format. // // // // This module's main purpose is to provide limited, but convenient // access to the HDF5 C API. // The classes offer the following services: //
          //
        • The burden of allocating and releasing the HDF5 resources // (the so-called hid-s) is handled in an automatic and // exception-safe way. //
        • The overwhelming and rather hard to use HDF5 C API is hidden. //
        • The standard casacore data types (scalars and arrays) are supported. // Because HDF5 does not support empty strings, they are transparantly // replaced by the value '__empty__'. // Also HDF5 does not support empty arrays, thus they are stored // in a special way using a special compound value. //
        • A Record is stored as a group. Its values (scalars and arrays) // are stored as attributes, while nested records are stored as // nested groups. //
        • Bool values are stored as chars. //
        • Complex values are stored as compounds of two real values. //
        • Large arrays can be stored in HDF5 DataSets with an optional tile size // (chunk size in HDF5 terminology). The array axes are reversed (fully // transparantly) because HDF5 uses C-order, while casacore Arrays use // Fortran-order. //
        • Automatic printing of HDF5 messages is disabled. All errors are // thrown as exceptions (AipsError or derived from it). //
        // // The following interface classes are available: //
          //
        • HDF5File opens or creates an HDF5 file and closes it automatically. // Furthermore it has a function to test if a file is in HDF5 format. //
        • HDF5DataType defines the HDF5 memory and file data type. It supports // the basic data types, complex, string, arrays and compounds. //
        • HDF5Record reads or writes a Record as a group in an HDF5 object. //
        • HDF5DataSet opens or creates a possible tiled data set in an HDF5 // object. The array can be read or written in parts. // It can be created of any HDF5DataType. //
        • HDF5Group opens, creates, or removes a group in an HDF5 object. //
        // Note that HDF5Object forms the base class of HDF5File, HDF5Group, and // HDF5DataSet. Most interfaces use HDF5Object, thus are applicable to // all these object types. //
        An HDF5Object has a conversion operator to hid_t, thus // can be used directly in any HDF5 function. // // Because of HDF5 resource management the objects (e.g. HDF5File) cannot be // copied. However, they can be used in shared pointers such as std::shared_ptr. //
        // Internally the classes use HDF5HidMeta.h which does the resource management // for HDF5 meta data like attributes, property lists, etc.. // // // All HDF5 classes and all their functions are compiled, but it depends on // the setting of HAVE_HDF5 how. If not set, all these functions are merely // stubs and the class constructors throw an exception when used. // The function HDF5Object::hasHDF5Support() tells if HDF5 is used. // See the casacore build instructions at github.com/casacore/casacore // for more information. // //
        // // See the various test programs. // // // // HDF5 offers a C++ interface. However, this interface is still quite complex // and is too much C-oriented. // Furthermore there was the need to support the casacore data types, // in particular complex. The reversal of array axes was also needed. // // //
      • Set the optimal data set chunk cache size from a given access pattern. // The current problem is that setting the cache size requires that // the file is closed first. // For the time being a fixed cache size of 16 MB is used instead of // the default 1 MB. // // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/HDF5/000077500000000000000000000000001476623553700147215ustar00rootroot00000000000000casacore-3.7.1/casa/HDF5/HDF5DataSet.cc000066400000000000000000000373461476623553700172010ustar00rootroot00000000000000//# HDF5DataSet.cc: An class representing an HDF5 data type //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN HDF5DataSet::HDF5DataSet (const HDF5Object& parentHid, const String& name, const IPosition& shape, const IPosition& tileShape, const Bool* type) : itsDataType (type) { create (parentHid, name, shape, tileShape); } HDF5DataSet::HDF5DataSet (const HDF5Object& parentHid, const String& name, const IPosition& shape, const IPosition& tileShape, const uChar* type) : itsDataType (type) { create (parentHid, name, shape, tileShape); } HDF5DataSet::HDF5DataSet (const HDF5Object& parentHid, const String& name, const IPosition& shape, const IPosition& tileShape, const Short* type) : itsDataType (type) { create (parentHid, name, shape, tileShape); } HDF5DataSet::HDF5DataSet (const HDF5Object& parentHid, const String& name, const IPosition& shape, const IPosition& tileShape, const Int* type) : itsDataType (type) { create (parentHid, name, shape, tileShape); } HDF5DataSet::HDF5DataSet (const HDF5Object& parentHid, const String& name, const IPosition& shape, const IPosition& tileShape, const Int64* type) : itsDataType (type) { create (parentHid, name, shape, tileShape); } HDF5DataSet::HDF5DataSet (const HDF5Object& parentHid, const String& name, const IPosition& shape, const IPosition& tileShape, const Float* type) : itsDataType (type) { create (parentHid, name, shape, tileShape); } HDF5DataSet::HDF5DataSet (const HDF5Object& parentHid, const String& name, const IPosition& shape, const IPosition& tileShape, const Double* type) : itsDataType (type) { create (parentHid, name, shape, tileShape); } HDF5DataSet::HDF5DataSet (const HDF5Object& parentHid, const String& name, const IPosition& shape, const IPosition& tileShape, const Complex* type) : itsDataType (type) { create (parentHid, name, shape, tileShape); } HDF5DataSet::HDF5DataSet (const HDF5Object& parentHid, const String& name, const IPosition& shape, const IPosition& tileShape, const DComplex* type) : itsDataType (type) { create (parentHid, name, shape, tileShape); } HDF5DataSet::HDF5DataSet (const HDF5Object& parentHid, const String& name, const IPosition& shape, const IPosition& tileShape, const HDF5DataType& type) : itsDataType (type) { create (parentHid, name, shape, tileShape); } HDF5DataSet::HDF5DataSet (const HDF5Object& parentHid, const String& name, const Bool* type) : itsDataType (type) { open (parentHid, name); } HDF5DataSet::HDF5DataSet (const HDF5Object& parentHid, const String& name, const uChar* type) : itsDataType (type) { open (parentHid, name); } HDF5DataSet::HDF5DataSet (const HDF5Object& parentHid, const String& name, const Short* type) : itsDataType (type) { open (parentHid, name); } HDF5DataSet::HDF5DataSet (const HDF5Object& parentHid, const String& name, const Int* type) : itsDataType (type) { open (parentHid, name); } HDF5DataSet::HDF5DataSet (const HDF5Object& parentHid, const String& name, const Int64* type) : itsDataType (type) { open (parentHid, name); } HDF5DataSet::HDF5DataSet (const HDF5Object& parentHid, const String& name, const Float* type) : itsDataType (type) { open (parentHid, name); } HDF5DataSet::HDF5DataSet (const HDF5Object& parentHid, const String& name, const Double* type) : itsDataType (type) { open (parentHid, name); } HDF5DataSet::HDF5DataSet (const HDF5Object& parentHid, const String& name, const Complex* type) : itsDataType (type) { open (parentHid, name); } HDF5DataSet::HDF5DataSet (const HDF5Object& parentHid, const String& name, const DComplex* type) : itsDataType (type) { open (parentHid, name); } HDF5DataSet::HDF5DataSet (const HDF5Object& parentHid, const String& name, const HDF5DataType& type) : itsDataType (type) { open (parentHid, name); } HDF5DataSet::~HDF5DataSet() { close(); } #ifdef HAVE_HDF5 void HDF5DataSet::create (const HDF5Object& parentHid, const String& name, const IPosition& shape, const IPosition& tileShape) { itsParent = &parentHid; setName (name); // Get the array shape and tile shape. Adjust as needed. AlwaysAssert (shape.nelements() >= tileShape.nelements(), AipsError); itsShape = shape; itsTileShape = IPosition(shape.nelements(), 1); // Trailing elements already have value 1; set the first elements. for (uInt i=0; i 0) { itsTileShape[i] = std::min(tileShape[i], shape[i]); } itsTileShape[i] = std::max(ssize_t(1), tileShape[i]); } // Create access property for later setting of cache size. itsDaplid = H5Pcreate (H5P_DATASET_ACCESS); AlwaysAssert (itsDaplid.getHid() >= 0, AipsError); // Create the data space for the array. int rank = itsShape.nelements(); Block ls = HDF5DataType::fromShape (itsShape); Block maxls(ls); for (uInt i=0; i= 0, AipsError); // Create the properties to hold the tile shape. itsPLid = H5Pcreate (H5P_DATASET_CREATE); AlwaysAssert (itsPLid.getHid() >= 0, AipsError); Block cs = HDF5DataType::fromShape (itsTileShape); H5Pset_chunk(itsPLid, rank, cs.storage()); // Create the data set. setHid (H5Dcreate2(parentHid, name.chars(), itsDataType.getHidFile(), itsDSid, 0, itsPLid, 0)); if (! isValid()) { throw HDF5Error("Data set array " + name + " could not be created"); } } void HDF5DataSet::open (const HDF5Object& parentHid, const String& name) { itsParent = &parentHid; setName (name); // Open the dataset. setHid (H5Dopen2(parentHid, name.chars(), 0)); if (! isValid()) { throw HDF5Error("Data set array " + name + " does not exist"); } // Get the (external) data type and check if it matches. HDF5HidDataType dsType (H5Dget_type(getHid())); if (H5Tget_class(dsType) != H5Tget_class(itsDataType.getHidFile())) { throw HDF5Error("Data set array " + name + " has an incorrect data type"); } if (H5Tget_size(dsType) != H5Tget_size(itsDataType.getHidFile())) { throw HDF5Error("Data set array " + name + " has an incorrect element size"); } // Create access property for later setting of cache size. itsDaplid = H5Pcreate (H5P_DATASET_ACCESS); AlwaysAssert (itsDaplid.getHid() >= 0, AipsError); // Get the data space (for the shape). itsDSid = H5Dget_space(getHid()); if (itsDSid.getHid() < 0) { throw HDF5Error("Data set array " + name + " does not have data space"); } // Get rank and shape. int rank = H5Sget_simple_extent_ndims(itsDSid); Block shp(rank); if (rank > 0) { rank = H5Sget_simple_extent_dims(itsDSid, shp.storage(), NULL); } if (rank < 0) { throw HDF5Error("Data set array " + name + " does not have dims"); } itsShape = HDF5DataType::toShape(shp); // Get the property list (for the tile shape). itsPLid = H5Dget_create_plist(getHid()); if (itsPLid.getHid() < 0) { throw HDF5Error("Data set array " + name + " does not have properties"); } H5D_layout_t layout = H5Pget_layout(itsPLid); if (layout != H5D_CONTIGUOUS) { // Get tile shape and check if its rank matches. if (H5Pget_chunk(itsPLid, rank, shp.storage()) != rank) { throw HDF5Error("Data set array " + name + " tile shape error"); } itsTileShape = HDF5DataType::toShape(shp); } } void HDF5DataSet::closeDataSet() { if (isValid()) { H5Dclose(getHid()); clearHid(); } } void HDF5DataSet::close() { // First close dataset. closeDataSet(); // Close extra hids. itsDSid.close(); itsPLid.close(); itsDaplid.close(); } void HDF5DataSet::setCacheSize (uInt nchunks) { // Setting the cache size takes only effect when opening the dataset. // So close it first. closeDataSet(); // Use LRU caching (4th argument is 0). // Hash size should be a prime according to the HDF5 documentation and // preferably 100 times the nr of chunks. This seems excessive, so use 20x. uInt nhash = 1; if (nchunks > 1) { nhash = Primes::nextLargerPrimeThan(nchunks*100); } // The cache size needs to be set in bytes. size_t sz = tileShape().product(); sz *= nchunks*itsDataType.size(); int err = H5Pset_chunk_cache (itsDaplid, nhash, sz, 0.); if (err < 0) { throw HDF5Error ("Could not set cache for HDF5 Dataset " + getName()); } // Reopen the dataset with cache size in itsDaplid. String name = getName(); setHid (H5Dopen2(*itsParent, name.chars(), itsDaplid)); if (! isValid()) { throw HDF5Error("Data set array " + name + " could not be reopened"); } } DataType HDF5DataSet::getDataType (hid_t parentHid, const String& name) { hid_t id = H5Dopen2(parentHid, name.chars(), 0); if (id < 0) { throw HDF5Error("Data set array " + name + " does not exist"); } // Get the (external) data type and check if it matches. HDF5HidDataType dtid (H5Dget_type(id)); H5Dclose(id); return HDF5DataType::getDataType (dtid); } void HDF5DataSet::get (const Slicer& section, ArrayBase& arr, Bool resize) { const IPosition& shp = section.length(); if (! shp.isEqual (arr.shape())) { if (resize || arr.nelements() == 0) { arr.resize (shp); } else { throw HDF5Error("Shape of slicer " + shp.toString() + " and array " + arr.shape().toString() + " mismatch in get of dataset " + std::string(getName())); } } Bool deleteIt; void* buf = arr.getVStorage (deleteIt); get (section, buf); arr.putVStorage (buf, deleteIt); } void HDF5DataSet::get (const Slicer& section, void* buf) { // Define the data set selection. Block offset = HDF5DataType::fromShape(section.start()); Block count = HDF5DataType::fromShape(section.length()); Block stride = HDF5DataType::fromShape(section.stride()); if (H5Sselect_hyperslab (itsDSid, H5S_SELECT_SET, offset.storage(), stride.storage(), count.storage(), NULL) < 0) { throw HDF5Error("invalid array get selection for dataset " + getName()); } // Define a data space for the memory buffer. HDF5HidDataSpace memspace (H5Screate_simple (count.size(), count.storage(), NULL)); // Define memory selection. offset = 0; if (H5Sselect_hyperslab (memspace, H5S_SELECT_SET, offset.storage(), NULL, count.storage(), NULL) < 0) { throw HDF5Error("setting slab of memory buffer for dataset " + getName()); } // Read the data. if (H5Dread (getHid(), itsDataType.getHidMem(), memspace, itsDSid, H5P_DEFAULT, buf) < 0) { throw HDF5Error("reading slab from data set array " + getName()); } } void HDF5DataSet::put (const Slicer& section, const ArrayBase& arr) { const IPosition& shp = section.length(); if (! shp.isEqual (arr.shape())) { throw HDF5Error("Shape of slicer " + shp.toString() + " and array " + arr.shape().toString() + " mismatch in put of dataset " + std::string(getName())); } Bool deleteIt; const void* buf = arr.getVStorage (deleteIt); put (section, buf); arr.freeVStorage (buf, deleteIt); } void HDF5DataSet::put (const Slicer& section, const void* buf) { // Define the data set selection. Block offset = HDF5DataType::fromShape(section.start()); Block count = HDF5DataType::fromShape(section.length()); Block stride = HDF5DataType::fromShape(section.stride()); if (H5Sselect_hyperslab (itsDSid, H5S_SELECT_SET, offset.storage(), stride.storage(), count.storage(), NULL) < 0) { throw HDF5Error("invalid array put selection for datset " + getName()); } // Define a data space for the memory buffer. HDF5HidDataSpace memspace (H5Screate_simple (count.size(), count.storage(), NULL)); // Define memory selection. offset = 0; if (H5Sselect_hyperslab (memspace, H5S_SELECT_SET, offset.storage(), NULL, count.storage(), NULL) < 0) { throw HDF5Error("setting slab of memory buffer for dataset " + getName()); } // Write the data. if (H5Dwrite (getHid(), itsDataType.getHidMem(), memspace, itsDSid, H5P_DEFAULT, buf) < 0) { throw HDF5Error("writing slab into data set array " + getName()); } } void HDF5DataSet::extend (const IPosition& shape) { AlwaysAssert (shape.size() == itsShape.size(), AipsError); // Extend the data set if one of the axes is larger than the current shape. IPosition newShape(itsShape); Bool ext = False; for (uInt i=0; i newShape[i]) { newShape[i] = shape[i]; ext = True; } } if (ext) { Block ls = HDF5DataType::fromShape (newShape); if (H5Dset_extent (getHid(), ls.storage()) < 0) { throw HDF5Error("Could not extend data set " + getName()); } itsShape = newShape; // The DataSpace has to be refreshed. itsDSid.close(); itsDSid = H5Dget_space(getHid()); } } #else void HDF5DataSet::create (const HDF5Object&, const String&, const IPosition&, const IPosition&) { HDF5Object::throwNoHDF5(); } void HDF5DataSet::open (const HDF5Object&, const String&) { HDF5Object::throwNoHDF5(); } void HDF5DataSet::close() {} void HDF5DataSet::setCacheSize (uInt) {} DataType HDF5DataSet::getDataType (hid_t, const String&) { return TpOther; } void HDF5DataSet::get (const Slicer&, ArrayBase&, Bool) {} void HDF5DataSet::get (const Slicer&, void*) {} void HDF5DataSet::put (const Slicer&, const ArrayBase&) {} void HDF5DataSet::put (const Slicer&, const void*) {} void HDF5DataSet::extend (const IPosition&) {} #endif } casacore-3.7.1/casa/HDF5/HDF5DataSet.h000066400000000000000000000202341476623553700170270ustar00rootroot00000000000000//# HDF5DataSet.h: An class representing an HDF5 data set //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_HDF5DATASET_H #define CASA_HDF5DATASET_H //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class ArrayBase; template class Block; // // A class representing an HDF5 data set. // // // // // //
      • HDF5 system // // // This class wraps the HDF5 functions to create and open a data set. // It is meant to be used in class HDF5Lattice, but can be used in itself // as well. // A dataset of any HDF5DataType (including compound types) can be created. // For a limited number of data types special constructors exist which // create the appropriate HDF5DataType themselves. //
        // A data set can be created extendible by defining the appropriate // axis with length 0, whereafter the extend function can be used to // extend the data set. // The data can be stored in a tiled (chunked) way by specifying the tile // shape when creating it. //
        // When opening an existing data set, it is checked if the given data type // matches the data set's data type. For a compound data type, it only // checks if its size matches; it does not check if the fields match. //
        // It is possible to read or write a section of the data set by using an // appropriate Slicer object. Note that the Slicer object must be fully // filled; it does not infer missing info from the array shape. //

        // Note that Casacore arrays are in Fortran order, while HDF5 uses C order. // Therefore array axes are reversed, thus axes in shapes, slicers, etc. // // // It was overkill to use the HDF5 C++ interface. Instead little wrappers // have been written. HDF5DataSet can be embedded in a shared pointer making // it possible to share an HDF5 data set amongst various HDF5Lattice objects // and close (i.e. destruct) the HDF5 data set object when needed. // class HDF5DataSet : public HDF5Object { public: // Create an HDF5 data set in the given hid (file or group). // It gets the given name, shape (also tile shape), and data type. // HDF5DataSet (const HDF5Object&, const String&, const IPosition& shape, const IPosition& tileShape, const Bool*); HDF5DataSet (const HDF5Object&, const String&, const IPosition& shape, const IPosition& tileShape, const uChar*); HDF5DataSet (const HDF5Object&, const String&, const IPosition& shape, const IPosition& tileShape, const Short*); HDF5DataSet (const HDF5Object&, const String&, const IPosition& shape, const IPosition& tileShape, const Int*); HDF5DataSet (const HDF5Object&, const String&, const IPosition& shape, const IPosition& tileShape, const Int64*); HDF5DataSet (const HDF5Object&, const String&, const IPosition& shape, const IPosition& tileShape, const Float*); HDF5DataSet (const HDF5Object&, const String&, const IPosition& shape, const IPosition& tileShape, const Double*); HDF5DataSet (const HDF5Object&, const String&, const IPosition& shape, const IPosition& tileShape, const Complex*); HDF5DataSet (const HDF5Object&, const String&, const IPosition& shape, const IPosition& tileShape, const DComplex*); HDF5DataSet (const HDF5Object&, const String&, const IPosition& shape, const IPosition& tileShape, const HDF5DataType&); // // Open an existing HDF5 data set in the given hid (file or group). // It checks if the internal type matches the given type. // HDF5DataSet (const HDF5Object&, const String&, const Bool*); HDF5DataSet (const HDF5Object&, const String&, const uChar*); HDF5DataSet (const HDF5Object&, const String&, const Short*); HDF5DataSet (const HDF5Object&, const String&, const Int*); HDF5DataSet (const HDF5Object&, const String&, const Int64*); HDF5DataSet (const HDF5Object&, const String&, const Float*); HDF5DataSet (const HDF5Object&, const String&, const Double*); HDF5DataSet (const HDF5Object&, const String&, const Complex*); HDF5DataSet (const HDF5Object&, const String&, const DComplex*); HDF5DataSet (const HDF5Object&, const String&, const HDF5DataType&); // // The destructor closes the HDF5 dataset object. virtual ~HDF5DataSet(); // Close the hid if valid. virtual void close(); // Set the cache size (in chunks) for the data set. // It needs to close and reopen the DataSet to take effect. void setCacheSize (uInt nchunks); // Get the data type for the data set with the given name. static DataType getDataType (hid_t, const String& name); // Get the shape. const IPosition& shape() const { return itsShape; } // Get the tile (chunk) shape. const IPosition& tileShape() const { return itsTileShape; } // Get a section of data into the array. // The array is resized if its shape does not match the slicer's shape. // This is only possible if the array is empty or if resize=True. // It is not checked if the data type of array and HDF5DataSet match. void get (const Slicer&, ArrayBase& buf, Bool resize=False); // Get a section of data. // The buffer must be large enough to hold the section. void get (const Slicer&, void* buf); // Put a section of data. // The shape of the array and slicer must match. // It is not checked if the data type of array and HDF5DataSet match. void put (const Slicer&, const ArrayBase& buf); // Put a section of data. // The buffer must be large enough to hold the section. void put (const Slicer&, const void* buf); // Extend the dataset if an axis in the new shape is larger. void extend (const IPosition& shape); protected: // Create the data set. void create (const HDF5Object&, const String&, const IPosition& shape, const IPosition& tileShape); // Open the data set and check if the external data type matches. void open (const HDF5Object&, const String&); // Close the dataset (but not other hids). void closeDataSet(); private: // Copy constructor cannot be used. HDF5DataSet (const HDF5DataSet& that); // Assignment cannot be used. HDF5DataSet& operator= (const HDF5DataSet& that); HDF5HidDataSpace itsDSid; //# data space id HDF5HidProperty itsPLid; //# create property list id HDF5HidProperty itsDaplid; //# access property list id IPosition itsShape; IPosition itsTileShape; HDF5DataType itsDataType; const HDF5Object* itsParent; }; } #endif casacore-3.7.1/casa/HDF5/HDF5DataType.cc000066400000000000000000000317121476623553700173560ustar00rootroot00000000000000//# HDF5DataType.cc: An class representing an HDF5 data type //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN #ifdef HAVE_HDF5 // H5free_memory was introduced in 1.8.13 // If the versions of hdf5 we depend on was formalized this could be simpler #if H5_VERS_MAJOR == 1 && (H5_VERS_MINOR < 8 || (H5_VERS_MINOR == 8 && H5_VERS_RELEASE < 13)) auto &H5free_memory = free; #endif HDF5DataType::HDF5DataType (const Bool*) : itsSize (sizeof(Bool)) { itsHidFile = H5Tcopy (H5T_STD_I8LE); itsHidMem = H5Tcopy (H5T_NATIVE_SCHAR); H5Tset_precision (itsHidMem, 8); } HDF5DataType::HDF5DataType (const uChar*) : itsSize (sizeof(uChar)) { itsHidFile = H5Tcopy (H5T_STD_U8LE); itsHidMem = H5Tcopy (H5T_NATIVE_UCHAR); H5Tset_precision (itsHidMem, 8); } HDF5DataType::HDF5DataType (const Short*) : itsSize (sizeof(Short)) { itsHidFile = H5Tcopy (H5T_STD_I16LE); itsHidMem = H5Tcopy (H5T_NATIVE_SHORT); H5Tset_precision (itsHidMem, 8*sizeof(Short)); } HDF5DataType::HDF5DataType (const uShort*) : itsSize (sizeof(uShort)) { itsHidFile = H5Tcopy (H5T_STD_U16LE); itsHidMem = H5Tcopy (H5T_NATIVE_USHORT); H5Tset_precision (itsHidMem, 8*sizeof(uShort)); } HDF5DataType::HDF5DataType (const Int*) : itsSize (sizeof(Int)) { itsHidFile = H5Tcopy (H5T_STD_I32LE); itsHidMem = H5Tcopy (H5T_NATIVE_INT); H5Tset_precision (itsHidMem, 8*sizeof(Int)); } HDF5DataType::HDF5DataType (const uInt*) : itsSize (sizeof(uInt)) { itsHidFile = H5Tcopy (H5T_STD_U32LE); itsHidMem = H5Tcopy (H5T_NATIVE_UINT); H5Tset_precision (itsHidMem, 8*sizeof(uInt)); } HDF5DataType::HDF5DataType (const Int64*) : itsSize (sizeof(Int64)) { itsHidFile = H5Tcopy (H5T_STD_I64LE); itsHidMem = H5Tcopy (H5T_NATIVE_INT); H5Tset_precision (itsHidMem, 8*sizeof(Int64)); } HDF5DataType::HDF5DataType (const Float*) : itsSize (sizeof(Float)) { itsHidFile = H5Tcopy (H5T_IEEE_F32LE); itsHidMem = H5Tcopy (H5T_NATIVE_FLOAT); } HDF5DataType::HDF5DataType (const Double*) : itsSize (sizeof(Double)) { itsHidFile = H5Tcopy (H5T_IEEE_F64LE); itsHidMem = H5Tcopy (H5T_NATIVE_DOUBLE); } HDF5DataType::HDF5DataType (const Complex*) : itsSize (sizeof(Complex)) { itsHidFile = H5Tcreate (H5T_COMPOUND, sizeof(Complex)); itsHidMem = H5Tcreate (H5T_COMPOUND, sizeof(Complex)); HDF5DataType dtype((Float*)0); addToCompound ("re", 0, dtype); addToCompound ("im", sizeof(Float), dtype); } HDF5DataType::HDF5DataType (const DComplex*) : itsSize (sizeof(DComplex)) { itsHidFile = H5Tcreate (H5T_COMPOUND, sizeof(DComplex)); itsHidMem = H5Tcreate (H5T_COMPOUND, sizeof(DComplex)); HDF5DataType dtype((Double*)0); addToCompound ("re", 0, dtype); addToCompound ("im", sizeof(Double), dtype); } HDF5DataType::HDF5DataType (const String& value) : itsSize (value.size()) { itsHidMem = H5Tcopy (H5T_C_S1); H5Tset_size (itsHidMem, value.size()); itsHidFile = H5Tcopy (itsHidMem); } HDF5DataType::HDF5DataType (const String*) : itsSize (0) { itsHidMem = H5Tcopy (H5T_C_S1); H5Tset_size (itsHidMem, H5T_VARIABLE); itsHidFile = H5Tcopy (itsHidMem); } HDF5DataType::HDF5DataType (Int, Int) { // An empty array is represented by its dimensionality and type. // Add an extra dummy field to make the compound different from (D)Complex // without having to test on field names. itsHidFile = H5Tcreate (H5T_COMPOUND, 3*sizeof(Int)); itsHidMem = H5Tcreate (H5T_COMPOUND, 3*sizeof(Int)); HDF5DataType dtype((Int*)0); addToCompound ("emptyarray", 0, dtype); addToCompound ("rank", sizeof(Int), dtype); addToCompound ("casatype", 2*sizeof(Int), dtype); } HDF5DataType::HDF5DataType (const std::vector& names, const std::vector& types) : itsSize (0) { AlwaysAssert (names.size() > 0, AipsError); AlwaysAssert (types.size() == names.size(), AipsError); for (uInt i=0; i 0, AipsError); itsSize += types[i].size(); } itsHidFile = H5Tcreate (H5T_COMPOUND, itsSize); itsHidMem = H5Tcreate (H5T_COMPOUND, itsSize); uInt offset = 0; for (uInt i=0; i 0, AipsError); Block shp = fromShape (shape); itsHidFile = H5Tarray_create (type.getHidFile(),shp.size(), shp.storage()); itsHidMem = H5Tarray_create (type.getHidMem(), shp.size(), shp.storage()); itsSize = type.size() * shape.product(); } HDF5DataType::HDF5DataType (const HDF5DataType& that) : itsHidMem (H5Tcopy (that.itsHidMem)), itsHidFile (H5Tcopy (that.itsHidFile)), itsSize (that.itsSize) {} HDF5DataType::~HDF5DataType() {} HDF5DataType& HDF5DataType::operator= (const HDF5DataType& that) { if (this != &that) { itsHidMem = that.itsHidMem; itsHidFile = that.itsHidFile; itsSize = that.itsSize; } return *this; } void HDF5DataType::addToCompound (const char* name, uInt offset, const HDF5DataType& dtype) { H5Tinsert (itsHidFile, name, offset, dtype.getHidFile()); H5Tinsert (itsHidMem, name, offset, dtype.getHidMem()); } DataType HDF5DataType::getDataType (hid_t dtid) { DataType dtype = TpOther; int sz = H5Tget_size(dtid); switch (H5Tget_class(dtid)) { case H5T_INTEGER: { int sgn = H5Tget_sign(dtid); if (sgn == H5T_SGN_2) { // A bool is stored as a signed char. if (sz == 1) { dtype = TpBool; } else if (sz == sizeof(Short)) { dtype = TpShort; } else if (sz == sizeof(Int)) { dtype = TpInt; } else { AlwaysAssert (sz==sizeof(Int64), AipsError); dtype = TpInt64; } } else { if (sz == 1) { dtype = TpUChar; } else if (sz == sizeof(uShort)) { dtype = TpUShort; } else { AlwaysAssert (sz==sizeof(uInt), AipsError); dtype = TpUInt; } } } break; case H5T_FLOAT: { if (sz == sizeof(Float)) { dtype = TpFloat; } else { AlwaysAssert (sz==sizeof(Double), AipsError); dtype = TpDouble; } } break; case H5T_COMPOUND: { if (HDF5DataType::isComplex (dtid)) { if (sz == sizeof(Complex)) { dtype = TpComplex; } else { AlwaysAssert (sz==sizeof(DComplex), AipsError); dtype = TpDComplex; } } else { dtype = TpRecord; } } break; case H5T_STRING: dtype = TpString; break; case H5T_ARRAY: { HDF5HidDataType hid(H5Tget_super (dtid)); switch (getDataType (hid)) { case TpBool: dtype = TpArrayBool; break; case TpUChar: dtype = TpArrayUChar; break; case TpShort: dtype = TpArrayShort; break; case TpUShort: dtype = TpArrayUShort; break; case TpInt: dtype = TpArrayInt; break; case TpUInt: dtype = TpArrayUInt; break; case TpInt64: dtype = TpArrayInt64; break; case TpFloat: dtype = TpArrayFloat; break; case TpDouble: dtype = TpArrayDouble; break; case TpComplex: dtype = TpArrayComplex; break; case TpDComplex: dtype = TpArrayDComplex; break; case TpString: dtype = TpArrayString; break; default: break; } } break; default: break; } return dtype; } Bool HDF5DataType::isComplex (hid_t dtid) { Bool res = False; if (H5Tget_class(dtid) == H5T_COMPOUND && H5Tget_nmembers(dtid) == 2) { char* f0 = H5Tget_member_name(dtid, 0); char* f1 = H5Tget_member_name(dtid, 1); res = (strcmp(f0, "re") == 0 && strcmp (f1, "im") == 0); H5free_memory(f0); H5free_memory(f1); } return res; } Bool HDF5DataType::isEmptyArray (hid_t dtid) { Bool res = False; if (H5Tget_class(dtid) == H5T_COMPOUND && H5Tget_nmembers(dtid) == 3) { char* f0 = H5Tget_member_name(dtid, 0); char* f1 = H5Tget_member_name(dtid, 1); char* f2 = H5Tget_member_name(dtid, 2); res = (strcmp(f0, "emptyarray") == 0 && strcmp (f1, "rank") == 0 && strcmp(f2, "casatype") == 0); H5free_memory(f0); H5free_memory(f1); H5free_memory(f2); } return res; } IPosition HDF5DataType::getShape (hid_t dtid) { Int ndim = H5Tget_array_ndims (dtid); AlwaysAssert (ndim >= 0, AipsError); Block dims(ndim); AlwaysAssert (H5Tget_array_dims(dtid, dims.storage()) == ndim, AipsError); return toShape (dims); } #else HDF5DataType::HDF5DataType (const Bool*) { HDF5Object::throwNoHDF5(); } HDF5DataType::HDF5DataType (const uChar*) { HDF5Object::throwNoHDF5(); } HDF5DataType::HDF5DataType (const Short*) { HDF5Object::throwNoHDF5(); } HDF5DataType::HDF5DataType (const uShort*) { HDF5Object::throwNoHDF5(); } HDF5DataType::HDF5DataType (const Int*) { HDF5Object::throwNoHDF5(); } HDF5DataType::HDF5DataType (const uInt*) { HDF5Object::throwNoHDF5(); } HDF5DataType::HDF5DataType (const Float*) { HDF5Object::throwNoHDF5(); } HDF5DataType::HDF5DataType (const Int64*) { HDF5Object::throwNoHDF5(); } HDF5DataType::HDF5DataType (const Double*) { HDF5Object::throwNoHDF5(); } HDF5DataType::HDF5DataType (const Complex*) { HDF5Object::throwNoHDF5(); } HDF5DataType::HDF5DataType (const DComplex*) { HDF5Object::throwNoHDF5(); } HDF5DataType::HDF5DataType (const String&) { HDF5Object::throwNoHDF5(); } HDF5DataType::HDF5DataType (const String*) { HDF5Object::throwNoHDF5(); } HDF5DataType::HDF5DataType (Int, Int) { HDF5Object::throwNoHDF5(); } HDF5DataType::HDF5DataType (const std::vector&, const std::vector&) { HDF5Object::throwNoHDF5(); } HDF5DataType::HDF5DataType (const HDF5DataType&, const IPosition&) { HDF5Object::throwNoHDF5(); } HDF5DataType::HDF5DataType (const HDF5DataType&) {} HDF5DataType::~HDF5DataType() {} HDF5DataType& HDF5DataType::operator= (const HDF5DataType&) { return *this; } void HDF5DataType::addToCompound (const char*, uInt, const HDF5DataType&) { HDF5Object::throwNoHDF5(); } DataType HDF5DataType::getDataType (hid_t) { return TpOther; } Bool HDF5DataType::isComplex (hid_t) { return False; } Bool HDF5DataType::isEmptyArray (hid_t) { return False; } IPosition HDF5DataType::getShape (hid_t) { return IPosition(); } #endif Block HDF5DataType::fromShape (const IPosition& shape) { // Reverse the axes (Fortran to C). Block b(shape.size()); std::reverse_copy (shape.begin(), shape.end(), b.begin()); return b; } IPosition HDF5DataType::toShape (const Block& b) { // Reverse the axes (C to Fortran). IPosition shape(b.size()); std::reverse_copy (b.begin(), b.end(), shape.begin()); return shape; } } casacore-3.7.1/casa/HDF5/HDF5DataType.h000066400000000000000000000155061476623553700172230ustar00rootroot00000000000000//# HDF5DataType.h: An class representing an HDF5 data type //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_HDF5DATATYPE_H #define CASA_HDF5DATATYPE_H //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class IPosition; template class Block; //

        // A class representing an HDF5 data type. // // // // // //
      • HDF5 system // // // This class wraps the HDF5 functions to create a data type // for the data in memory and for the file. // The HDF5 file data type order is set to LittleEndian. //
        // The basic constructors define a scalar of a basic data type or // data type Complex and DComplex. Strings are also supported. // However, it is also possible to define a fixed shaped array for any // HDF5DataType. Furthermore, it is possible to define a compound data // type consisting of named fields of any HDF5DataType with a fixed size. // Arrays and/or compounds can be nested at will. //
        // Variable length strings are supported, but cannot be used in compounds. //
        // Older HDF5 versions did not support empty arrays. Therefore they are // represented as a compound with 3 fields. Also they did not support // boolean values; they are represented as a signed char. // HDF5 does not support complex values either. They are represented // as a compound with fields 're' and 'im'. //
        // // The HDF5 C++ interface only supports the HDF5 C functionality and // resembles that to much. For instance, it does not use STL containers. // It does not support non-basic data types, in particular Complex. // class HDF5DataType { public: // The default constructor makes an invalid object. // It is needed to make a vector of objects. HDF5DataType() : itsSize(0) {} // Create an HDF5 datatype object for the given fixed length type. // It uses the corresponding native HDF5 data type. Only for Bool it // uses a uchar, because the HDF5 bool type is a uint. // For the complex types it makes a compound HDF5 data type. // The String type is meant for an array of strings. // explicit HDF5DataType (const Bool*); explicit HDF5DataType (const uChar*); explicit HDF5DataType (const Short*); explicit HDF5DataType (const uShort*); explicit HDF5DataType (const Int*); explicit HDF5DataType (const uInt*); explicit HDF5DataType (const Int64*); explicit HDF5DataType (const Float*); explicit HDF5DataType (const Double*); explicit HDF5DataType (const Complex*); explicit HDF5DataType (const DComplex*); explicit HDF5DataType (const String*); // // Create an HDF5 datatype object for a scalar string. // The length of the string is part of the type. explicit HDF5DataType (const String& value); // Create an HDF5 datatype object for an empty array. // Both arguments are dummy (needed to distinguish the constructor). // An empty array as represented as a compound data type with integer // field names emptyarray, rank and casatype. HDF5DataType (Int, Int); // Define a compound data type consisting of the given fields and types. // An exception is thrown if the vectors are empty or have mismatching // sizes. HDF5DataType (const std::vector& names, const std::vector& types); // Create an array of the given data type. // An exception is thrown if the shape is empty. HDF5DataType (const HDF5DataType&, const IPosition& shape); // The copy constructor makes a deep copy. HDF5DataType (const HDF5DataType& that); // The destructor closes the HDF5 data type object. ~HDF5DataType(); // Assignment makes a deep copy. HDF5DataType& operator= (const HDF5DataType& that); // Get the Casacore data type for the given HDF5 data type. // TpRecord is returned for a compound data type. static DataType getDataType (hid_t); // Get the HID for the data type in memory. hid_t getHidMem() const { return itsHidMem; } // Get the HID for the data type in the file. hid_t getHidFile() const { return itsHidFile; } // Get the size in bytes of the data type (in memory). // Note that the size of a string is variable, thus 0. uInt size() const { return itsSize; } // Test if the data type is Complex or DComplex. static Bool isComplex (hid_t dtid); // Test if the data type is an empty array. static Bool isEmptyArray (hid_t dtid); // Get the shape of an array data type. // It returns an empty IPosition for non-arrays. static IPosition getShape (hid_t dtid); // Helper functions to convert shapes. // It reverses the axes, because HDF5 uses C-order. // static Block fromShape (const IPosition& shape); static IPosition toShape (const Block& b); // private: // Add a field to a compound data type. // It does it for the memory and file data type. void addToCompound (const char* name, uInt offset, const HDF5DataType& dtype); //# Data members HDF5HidDataType itsHidMem; HDF5HidDataType itsHidFile; uInt itsSize; }; } #endif casacore-3.7.1/casa/HDF5/HDF5Error.cc000066400000000000000000000030221476623553700167250ustar00rootroot00000000000000//# HDF5Error.cc: Error classes for the HDF5 wrapper classes //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include namespace casacore { //# NAMESPACE CASACORE - BEGIN HDF5Error::HDF5Error (Category c) : AipsError("HDF5 error", c) {} HDF5Error::HDF5Error (const String& str, Category c) : AipsError("HDF5 error: " + str, c) {} HDF5Error::~HDF5Error () noexcept {} } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/HDF5/HDF5Error.h000066400000000000000000000042141476623553700165730ustar00rootroot00000000000000//# HDF5Error.h: HDF5 error classes //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_HDF5ERROR_H #define CASA_HDF5ERROR_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# This header file defines the error classes belonging to the table //# descriptor class and its associated classes. // // Base error class for HDF5 wrapper classes // // // // // // This is the generic HDF5 exception; catching this one means catching // all HDF5* exceptions. // Note that you have to catch AipsError to catch all possible exceptions. // class HDF5Error : public AipsError { public: // The default constructor generates the message "HDF5 error". HDF5Error (Category c=GENERAL); // Construct with given message. HDF5Error (const String& message, Category c=GENERAL); ~HDF5Error() noexcept; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/HDF5/HDF5File.cc000066400000000000000000000114361476623553700165230ustar00rootroot00000000000000//# HDF5File.h: An class representing an HDF5 file //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN #ifdef HAVE_HDF5 HDF5File::HDF5File (const String& name, ByteIO::OpenOption option) : itsOption (option), itsDelete (False) { // Disable automatic printing of errors. H5Eset_auto2(H5E_DEFAULT, NULL, NULL); // Use absolute expanded path name. setName (Path(name).absoluteName()); doOpen(); } HDF5File::~HDF5File() { close(); if (itsDelete) { RegularFile file(getName()); file.remove(); } } Bool HDF5File::isHDF5 (const String& name) { // Disable automatic printing of errors. H5Eset_auto2(H5E_DEFAULT, NULL, NULL); return H5Fis_hdf5 (name.chars()); } void HDF5File::reopenRW() { if (itsOption != ByteIO::Update) { close(); itsOption = ByteIO::Update; } reopen(); } void HDF5File::close() { if (isValid()) { // Do not check for errors. // If the same file is opened twice, HDF5 will close the file on // the first occasion and gives an error for the second close. H5Fflush (getHid(), H5F_SCOPE_LOCAL); H5Fclose (getHid()); clearHid(); } } void HDF5File::reopen() { if (isClosed()) { doOpen(); } } void HDF5File::flush() { if (isValid()) { if (H5Fflush (getHid(), H5F_SCOPE_LOCAL) < 0) { throw HDF5Error ("Failure when flushing file " + getName()); } } } void HDF5File::doOpen() { // Use 8 byte offets and blocks of 32768 bytes. HDF5HidProperty create_plist (H5Pcreate(H5P_FILE_CREATE)); H5Pset_sizes(create_plist, 8, 8); // Use unbuffered IO. HDF5HidProperty access_plist (H5Pcreate(H5P_FILE_ACCESS)); H5Pset_fapl_sec2(access_plist); // Set a cache size of 16 MB. // First get old values and use them for the other parameters. int mdn; size_t ccn, ccb; double policy; H5Pget_cache(access_plist, &mdn, &ccn, &ccb, &policy); ccb = 16*1024*1024; H5Pset_cache(access_plist, mdn, ccn, ccb, policy); // Open or create the file. switch (itsOption) { case ByteIO::Old: setHid (H5Fopen (getName().chars(), H5F_ACC_RDONLY, access_plist)); break; case ByteIO::Delete: itsDelete = True; // fall through case ByteIO::Update: case ByteIO::Append: setHid (H5Fopen (getName().chars(), H5F_ACC_RDWR, access_plist)); itsOption = ByteIO::Update; break; case ByteIO::Scratch: itsDelete = True; // fall through case ByteIO::New: setHid (H5Fcreate (getName().chars(), H5F_ACC_TRUNC, create_plist, access_plist)); itsOption = ByteIO::Update; break; case ByteIO::NewNoReplace: setHid (H5Fcreate (getName().chars(), H5F_ACC_EXCL, create_plist, access_plist)); itsOption = ByteIO::Update; break; } if (!isValid()) { throw HDF5Error ("Could not open or create HDF5 file " + getName()); } } #else HDF5File::HDF5File (const String& name, ByteIO::OpenOption option) : itsOption (option), itsDelete (False) { setName (name); doOpen(); } HDF5File::~HDF5File() {} Bool HDF5File::isHDF5 (const String&) { return False; } void HDF5File::reopenRW() {} void HDF5File::close() {} void HDF5File::reopen() {} void HDF5File::flush() {} void HDF5File::doOpen() { throw HDF5Error("HDF5 support is not compiled into this casacore version"); } #endif } casacore-3.7.1/casa/HDF5/HDF5File.h000066400000000000000000000110051476623553700163550ustar00rootroot00000000000000//# HDF5File.h: An class representing an HDF5 file //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_HDF5FILE_H #define CASA_HDF5FILE_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A class representing an HDF5 file. // // // // // //
      • HDF5 system // // // This class wraps the HDF5 functions to open, create, or close an // HDF5 file. // If the file is opened as readonly, it is possible to reopen it for // read/write (provided the user has the correct privileges). // It is also possible to temporarily close the file and reopen it later. // It is ensured that the class and the static function isHDF5 // are also defined if HDF5 is not compiled in. // // // It was overkill to use the HDF5 C++ interface. Instead little wrappers // have been written. HDF5File can be embedded in a shared pointer making // it possible to share an HDF5 file amongst various HDF5Array objects. // class HDF5File : public HDF5Object { public: // Create an HDF5 file object with the given file name (possible tilde // or environment variables in it will be expanded). // The ByteIO option determines if the file will be created, // opened for input and/or output, or possibly deleted by the destructor. explicit HDF5File (const String& name, ByteIO::OpenOption = ByteIO::Old); // The destructor closes the file and deletes it when it was opened // using ByteIO::Scratch or ByteIO::Delete. ~HDF5File(); // Test if the file with the given name is an HDF5 file. static Bool isHDF5 (const String& name); // Reopen the underlying file for read/write access. // Nothing will be done if the stream is writable already. // Otherwise it will be reopened and an exception will be thrown // if it is not possible to reopen it for read/write access. void reopenRW(); // Is the file writable? Bool isWritable() const { return itsOption == ByteIO::Update; } // Is the file opened for delete? Bool isOpenedForDelete() const { return itsDelete; } // Is the file temporarily closed? Bool isClosed() const { return getHid()<0; } // Close the file (temporarily). // Note it will not delete the file; that is only done by the destructor. virtual void close(); // Reopen the file if closed (which may change the HID). void reopen(); // Flush the data to disk. void flush(); // Get or set the chunk cache size (in bytes). // Note that all data sets in a file share the cache. // size_t getChunkCacheSize() const; void setChunkCacheSize (size_t nbytes); // private: // Open or create the file. void doOpen(); //# Data members ByteIO::OpenOption itsOption; String itsName; Bool itsDelete; private: // Copy constructor cannot be used. HDF5File (const HDF5File& that); // Assignment cannot be used. HDF5File& operator= (const HDF5File& that); }; } #endif casacore-3.7.1/casa/HDF5/HDF5Group.cc000066400000000000000000000074771476623553700167520ustar00rootroot00000000000000//# HDF5Group.cc: An class representing an HDF5 group //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN #ifdef HAVE_HDF5 void HDF5Group::init (hid_t parentHid, const String& parentName, const String& name, bool mustExist, bool mustNotExist) { String type = "create"; if (mustNotExist) { setHid (H5Gcreate2(parentHid, name.c_str(), H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)); } else { type = "open"; // Note that testing if the link exists does not work for /. if (name == "/" || H5Lexists (parentHid, name.c_str(), H5P_LINK_ACCESS_DEFAULT) == 1) { setHid (H5Gopen2(parentHid, name.c_str(), H5P_DEFAULT)); } if (!isValid() && !mustExist) { type = "open or create"; setHid (H5Gcreate2(parentHid, name.c_str(), H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)); } } if (! isValid()) { throw HDF5Error ("Could not " + type + " group " + name + " in parent " + parentName); } setName (name); } HDF5Group::~HDF5Group() { close(); } void HDF5Group::close() { if (isValid()) { H5Gclose(getHid()); clearHid(); } } // The callback function for function linkNames. herr_t doAddLinkName (hid_t, const char* name, const H5L_info_t*, void* arg) { static_cast*>(arg)->push_back (String(name)); return 0; } std::vector HDF5Group::linkNames (const HDF5Object& parentHid) { // Now read all subrecords. //# Use INDEX_NAME (using INDEX_CRT_ORDER results in a return of -1). std::vector names; names.reserve (16); hsize_t idx=0; H5Literate (parentHid, H5_INDEX_NAME, H5_ITER_NATIVE, &idx, &doAddLinkName, &names); return names; } bool HDF5Group::exists (const HDF5Object& parentHid, const String& name) { return H5Lexists (parentHid, name.c_str(), H5P_LINK_ACCESS_DEFAULT) == 1; } void HDF5Group::remove (const HDF5Object& parentHid, const String& name) { if (exists (parentHid, name)) { H5Ldelete (parentHid, name.c_str(), H5P_LINK_ACCESS_DEFAULT); } } #else void HDF5Group::init (hid_t, const String&, const String&, bool, bool) { HDF5Object::throwNoHDF5(); } HDF5Group::~HDF5Group() {} void HDF5Group::close() {} std::vector HDF5Group::linkNames (const HDF5Object&) { return std::vector(); } bool HDF5Group::exists (const HDF5Object&, const String&) { return false; } void HDF5Group::remove (const HDF5Object&, const String&) { HDF5Object::throwNoHDF5(); } #endif } casacore-3.7.1/casa/HDF5/HDF5Group.h000066400000000000000000000067471476623553700166130ustar00rootroot00000000000000//# HDF5Group.h: An class representing an HDF5 group //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_HDF5GROUP_H #define CASA_HDF5GROUP_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A class representing an HDF5 group. // // // // // // This class wraps an HDF5 group hid (hdf5 id). It offers two benefits: //
          //
        • The most important is resource management. In case of an exception, // the hid will automatically be closed by the destructor. //
        • A hid is a kind of pointer and should not be copied. These classes // make it possible to use them in a shared pointer. //
        //
        class HDF5Group : public HDF5Object { public: // Construct from given hid. HDF5Group() {} // Open or create a group at the given hid. // Default is that the group may exist; it is created if not existing. // HDF5Group (const HDF5Object& parentHid, const String& name, bool mustExist=false, bool mustNotExist=false) { init (parentHid, parentHid.getName(), name, mustExist, mustNotExist); } HDF5Group (hid_t parentHid, const String& name, bool mustExist=false, bool mustNotExist=false) { init (parentHid, String(), name, mustExist, mustNotExist); } // // The destructor closes the hid. virtual ~HDF5Group(); // Close the hid if valid. virtual void close(); // Get the names of all links at the given hid. static std::vector linkNames (const HDF5Object& parentHid); // Test if the group at the given hid exists. static bool exists (const HDF5Object& parentHid, const String& name); // Delete group at the given hid if it exists. static void remove (const HDF5Object& parentHid, const String& name); private: // Copy constructor cannot be used. HDF5Group (const HDF5Group& that); // Assignment cannot be used. HDF5Group& operator= (const HDF5Group& that); // Initialize (execute the constructor). void init (hid_t parentHid, const String& parentName, const String& name, bool mustExist=false, bool mustNotExist=false); }; } #endif casacore-3.7.1/casa/HDF5/HDF5HidMeta.cc000066400000000000000000000045301476623553700171540ustar00rootroot00000000000000//# HDF5HidMeta.cc: Classes representing an HDF5 hid of meta objects //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN #ifdef HAVE_HDF5 HDF5HidDataType::HDF5HidDataType (const HDF5HidDataType& that) : itsHid(H5Tcopy(that.itsHid)) {} HDF5HidDataType&HDF5HidDataType:: operator= (const HDF5HidDataType& that) { if (this != &that) { close(); itsHid = H5Tcopy(that.itsHid); } return *this; } void HDF5HidProperty::close() { if (itsHid>=0) H5Pclose(itsHid); itsHid=-1; } void HDF5HidDataType::close() { if (itsHid>=0) H5Tclose(itsHid); itsHid=-1; } void HDF5HidDataSpace::close() { if (itsHid>=0) H5Sclose(itsHid); itsHid=-1; } void HDF5HidAttribute::close() { if (itsHid>=0) H5Aclose(itsHid); itsHid=-1; } #else HDF5HidDataType::HDF5HidDataType (const HDF5HidDataType&) {} HDF5HidDataType& HDF5HidDataType::operator= (const HDF5HidDataType&) { return *this; } void HDF5HidProperty::close() {} void HDF5HidDataType::close() {} void HDF5HidDataSpace::close() {} void HDF5HidAttribute::close() {} #endif } casacore-3.7.1/casa/HDF5/HDF5HidMeta.h000066400000000000000000000165721476623553700170270ustar00rootroot00000000000000//# HDF5HidMeta.h: Classes representing an HDF5 hid of meta objects //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_HDF5HIDMETA_H #define CASA_HDF5HIDMETA_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A class representing an HDF5 property hid. // // // // // // This class wraps an HDF5 property hid (hdf5 id). It offers two benefits: //
          //
        • The most important is resource management. In case of an exception, // the hid will automatically be closed by the destructor. //
        • A hid is a kind of pointer and should not be copied. These classes // forbid making a copy, but make it possible to use them in a // shared pointer context. //
        //
        class HDF5HidProperty { public: // Default constructor sets hid to invalid. HDF5HidProperty() : itsHid(-1) {} // Construct from given hid. HDF5HidProperty (hid_t hid) : itsHid(hid) {} // The destructor closes the hid. ~HDF5HidProperty() { close(); } // Copy constructor cannot be used. HDF5HidProperty (const HDF5HidProperty&) = delete; // Assignment cannot be used. HDF5HidProperty& operator= (const HDF5HidProperty&) = delete; // Close the hid if valid. void close(); // Put hid in it. If it already contains a hid, it will be closed. void operator= (hid_t hid) { close(); itsHid = hid; } // Get the hid. hid_t getHid() const { return itsHid; } // Convert automatically to hid_t. operator hid_t() const { return itsHid; } private: hid_t itsHid; }; // // A class representing an HDF5 datatype hid. // // // // // // This class wraps an HDF5 datatype hid (hdf5 id). It offers two benefits: //
          //
        • The most important is resource management. In case of an exception, // the hid will automatically be closed by the destructor. //
        • A hid is a kind of pointer and should not be copied. These classes // forbid making a copy, but make it possible to use them in a // shared pointer context. //
        //
        class HDF5HidDataType { public: // Default constructor sets hid to invalid. HDF5HidDataType() : itsHid(-1) {} // Construct from given hid. HDF5HidDataType (hid_t hid) : itsHid(hid) {} // Copy constructor makes a deep copy. HDF5HidDataType (const HDF5HidDataType& that); // The destructor closes the hid. ~HDF5HidDataType() { close(); } // Assignment makes a deep copy. HDF5HidDataType& operator= (const HDF5HidDataType& that); // Close the hid if valid. void close(); // Put hid in it. If it already contains a hid, it will be closed. void operator= (hid_t hid) { close(); itsHid = hid; } // Get the hid. hid_t getHid() const { return itsHid; } // Convert automatically to hid_t. operator hid_t() const { return itsHid; } private: hid_t itsHid; }; // // A class representing an HDF5 dataspace hid. // // // // // // This class wraps an HDF5 dataspace hid (hdf5 id). It offers two benefits: //
          //
        • The most important is resource management. In case of an exception, // the hid will automatically be closed by the destructor. //
        • A hid is a kind of pointer and should not be copied. These classes // forbid making a copy, but make it possible to use them in a // shared pointer context. //
        //
        class HDF5HidDataSpace { public: // Default constructor sets hid to invalid. HDF5HidDataSpace() : itsHid(-1) {} // Construct from given hid. HDF5HidDataSpace (hid_t hid) : itsHid(hid) {} // The destructor closes the hid. ~HDF5HidDataSpace() { close(); } // Copy constructor cannot be used. HDF5HidDataSpace (const HDF5HidDataSpace&) = delete; // Assignment cannot be used. HDF5HidDataSpace& operator= (const HDF5HidDataSpace&) = delete; // Close the hid if valid. void close(); // Put hid in it. If it already contains a hid, it will be closed. void operator= (hid_t hid) { close(); itsHid = hid; } // Get the hid. hid_t getHid() const { return itsHid; } // Convert automatically to hid_t. operator hid_t() const { return itsHid; } private: hid_t itsHid; }; // // A class representing an HDF5 attribute hid. // // // // // // This class wraps an HDF5 attribute hid (hdf5 id). It offers two benefits: //
          //
        • The most important is resource management. In case of an exception, // the hid will automatically be closed by the destructor. //
        • A hid is a kind of pointer and should not be copied. These classes // forbid making a copy, but make it possible to use them in a // shared pointer context. //
        //
        class HDF5HidAttribute { public: // Default constructor sets hid to invalid. HDF5HidAttribute() : itsHid(-1) {} // Construct from given hid. HDF5HidAttribute (hid_t hid) : itsHid(hid) {} // The destructor closes the hid. ~HDF5HidAttribute() { close(); } // Copy constructor cannot be used. HDF5HidAttribute (const HDF5HidAttribute&) = delete; // Assignment cannot be used. HDF5HidAttribute& operator= (const HDF5HidAttribute&) = delete; // Close the hid if valid. void close(); // Put hid in it. If it already contains a hid, it will be closed. void operator= (hid_t hid) { close(); itsHid = hid; } // Get the hid. hid_t getHid() const { return itsHid; } // Convert automatically to hid_t. operator hid_t() const { return itsHid; } private: hid_t itsHid; }; } #endif casacore-3.7.1/casa/HDF5/HDF5Object.cc000066400000000000000000000034621476623553700170520ustar00rootroot00000000000000//# HDF5Object.cc: An abstract base class representing an HDF5 object //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGI HDF5Object::~HDF5Object() {} #ifdef HAVE_HDF5 Bool HDF5Object::hasHDF5Support() { return True; } void HDF5Object::throwNoHDF5() {} #else Bool HDF5Object::hasHDF5Support() { return False; } void HDF5Object::throwNoHDF5() { throw HDF5Error("HDF5 support is not compiled into this casacore version"); } #endif void throwInvHDF5() { throw HDF5Error("HDF5 hid_t or hsize_t have incorrect type in HDF5Object"); } } casacore-3.7.1/casa/HDF5/HDF5Object.h000066400000000000000000000077561476623553700167260ustar00rootroot00000000000000//# HDF5Object.h: An abstract base class representing an HDF5 object //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_HDF5OBJECT_H #define CASA_HDF5OBJECT_H //# Includes #include #include //# Define hid_t and hsize_t if not defined (thus if HDF5 disabled). //# They should be the same as used by HDF5. //# This is checked by functions check_hid_t and check_hsize_t. #ifdef HAVE_HDF5 # include #else typedef int64_t hid_t; typedef casacore::uInt64 hsize_t; #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN // // An abstract base class representing an HDF5 object // // // // // // This class wraps a basic HDF5 object. It offers several benefits: //
          //
        • The most important is resource management. In case of an exception, // the object's hid will automatically be closed by the destructor. //
        • It acts as the base class for the basic objects file, group, // and dataset. //
        • An HDF5 hid is a kind of pointer and should not be copied. // These classes forbid making a copy, but make it possible to use // them in a shared pointer context. //
        //
        class HDF5Object { public: // Default constructor sets to invalid hid. HDF5Object() : itsHid(-1) {} // The destructor in a derived class should close the hid appropriately. virtual ~HDF5Object(); // Copy constructor cannot be used because a HID cannot be copied. HDF5Object (const HDF5Object&) = delete; // Assignment cannot be used because a HID cannot be copied . HDF5Object& operator= (const HDF5Object&) = delete; // Check if there is HDF5 support compiled in. static Bool hasHDF5Support(); // Close the hid if valid. virtual void close() = 0; // Is it a valid hid? bool isValid() const { return itsHid >= 0; } // Get the hid. hid_t getHid() const { return itsHid; } // Convert automatically to hid_t. operator hid_t() const { return itsHid; } // Get or set the name. // void setName (const String& name) { itsName = name; } const String& getName() const { return itsName; } // // If no HDF5, throw an exception that HDF5 is not supported. static void throwNoHDF5(); protected: // Set the hid. void setHid (hid_t hid) { itsHid = hid; } // Clear the hid (set to invalid). void clearHid() { itsHid = -1; } private: //# Data members //# Define a union to ensure that the object always uses 64 bits, even //# for older HDF5 versions where hid_t is a 32 bit integer. union { hid_t itsHid; int64_t itsDummyHid; }; String itsName; }; } #endif casacore-3.7.1/casa/HDF5/HDF5Record.cc000066400000000000000000000427451476623553700170710ustar00rootroot00000000000000//# HDF5Record.cc: A class to write/read a record into HDF5 //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN #ifdef HAVE_HDF5 herr_t readSubRecord (hid_t groupHid, const char* name, const H5L_info_t*, void* voidRec) { HDF5Group gid(groupHid, name, true); Record& rec = *(static_cast(voidRec)); rec.defineRecord (name, HDF5Record::doReadRecord(gid)); return 0; } void HDF5Record::remove (const HDF5Object& parentHid, const String& recordName) { HDF5Group::remove (parentHid, recordName); } Record HDF5Record::readRecord (const HDF5Object& parentHid, const String& recordName) { try { HDF5Group gid(parentHid, recordName, true); return doReadRecord (gid); } catch (HDF5Error&) { return Record(); // no such group means empty record } } Record HDF5Record::doReadRecord (hid_t groupHid) { Record rec; char cname[512]; int nfields = H5Aget_num_attrs (groupHid); // Iterate through the attributes in order of index, so we're sure // they are read back in the same order as written. for (int index=0; index=0, AipsError); unsigned int namsz = H5Aget_name(id, sizeof(cname), cname); AlwaysAssert (namsz shp(rank); if (rank > 0) { rank = H5Sget_simple_extent_dims(dsid, shp.storage(), NULL); } if (rank < 0) { throw HDF5Error("Data set array " + name + " does not have dims"); } // Get data type and its size. HDF5HidDataType dtid(H5Aget_type(id)); if (rank == 0) { readScalar (id, dtid, name, rec); } else { readArray (id, dtid, HDF5DataType::toShape(shp), name, rec); } } // Now read all subrecords. //# Use INDEX_NAME (using INDEX_CRT_ORDER results in a return of -1). hsize_t idx=0; H5Literate (groupHid, H5_INDEX_NAME, H5_ITER_NATIVE, &idx, &readSubRecord, &rec); return rec; } void HDF5Record::readScalar (hid_t attrId, hid_t dtid, const String& name, RecordInterface& rec) { // Handle a scalar field. Int sz = H5Tget_size(dtid); switch (H5Tget_class(dtid)) { case H5T_INTEGER: { int sgn = H5Tget_sign(dtid); if (sgn == H5T_SGN_2) { if (sz == 1) { readSca (attrId, name, rec); } else if (sz == sizeof(Short)) { readSca (attrId, name, rec); } else if (sz == sizeof(Int)) { readSca (attrId, name, rec); } else { AlwaysAssert (sz==sizeof(Int64), AipsError); readSca (attrId, name, rec); } } else { if (sz == 1) { readSca (attrId, name, rec); } else { AlwaysAssert (sz==sizeof(uInt), AipsError); readSca (attrId, name, rec); } } } break; case H5T_FLOAT: { if (sz == sizeof(Float)) { readSca (attrId, name, rec); } else { AlwaysAssert (sz==sizeof(Double), AipsError); readSca (attrId, name, rec); } } break; case H5T_COMPOUND: { if (HDF5DataType::isComplex(dtid)) { if (sz == sizeof(Complex)) { readSca (attrId, name, rec); } else { AlwaysAssert (sz==sizeof(DComplex), AipsError); readSca (attrId, name, rec); } } else { AlwaysAssert (HDF5DataType::isEmptyArray(dtid), AipsError); readEmptyArray (attrId, name, rec); } } break; case H5T_STRING: readScaString (attrId, sz, name, rec); break; default: throw HDF5Error ("Unknown data type of scalar attribute " + name); } } void HDF5Record::readArray (hid_t attrId, hid_t dtid, const IPosition& shape, const String& name, RecordInterface& rec) { Int sz = H5Tget_size(dtid); // Handle an array field. switch (H5Tget_class(dtid)) { case H5T_INTEGER: { int sgn = H5Tget_sign(dtid); if (sgn == H5T_SGN_2) { if (sz == 1) { readArr (attrId, shape, name, rec); } else if (sz == sizeof(Short)) { readArr (attrId, shape, name, rec); } else if (sz == sizeof(Int)) { readArr (attrId, shape, name, rec); } else { AlwaysAssert (sz==sizeof(Int64), AipsError); readArr (attrId, shape, name, rec); } } else { if (sz == 1) { readArr (attrId, shape, name, rec); } else { AlwaysAssert (sz==sizeof(uInt), AipsError); readArr (attrId, shape, name, rec); } } } break; case H5T_FLOAT: { if (sz == sizeof(Float)) { readArr (attrId, shape, name, rec); } else { AlwaysAssert (sz==sizeof(Double), AipsError); readArr (attrId, shape, name, rec); } } break; case H5T_COMPOUND: { AlwaysAssert (HDF5DataType::isComplex(dtid), AipsError); if (sz == sizeof(Complex)) { readArr (attrId, shape, name, rec); } else { AlwaysAssert (sz==sizeof(DComplex), AipsError); readArr (attrId, shape, name, rec); } } break; case H5T_STRING: readArrString (attrId, shape, name, rec); break; default: throw HDF5Error ("Unknown data type of array attribute " + name); } } void HDF5Record::readEmptyArray (hid_t attrId, const String& name, RecordInterface& rec) { Int values[3]; HDF5DataType dtype(0, 0); read (attrId, values, dtype); Int rank = values[1]; Int dt = values[2]; switch (dt) { case TpBool: rec.define (name, Array(IPosition(rank, 0))); break; case TpUChar: rec.define (name, Array(IPosition(rank, 0))); break; case TpShort: rec.define (name, Array(IPosition(rank, 0))); break; case TpInt: rec.define (name, Array(IPosition(rank, 0))); break; case TpUInt: rec.define (name, Array(IPosition(rank, 0))); break; case TpInt64: rec.define (name, Array(IPosition(rank, 0))); break; case TpFloat: rec.define (name, Array(IPosition(rank, 0))); break; case TpDouble: rec.define (name, Array(IPosition(rank, 0))); break; case TpComplex: rec.define (name, Array(IPosition(rank, 0))); break; case TpDComplex: rec.define (name, Array(IPosition(rank, 0))); break; case TpString: rec.define (name, Array(IPosition(rank, 0))); break; default: throw HDF5Error ("Unknown data type of empty array attribute " + name); } } void HDF5Record::read (hid_t attrId, void* value, const HDF5DataType& dtype) { AlwaysAssert (H5Aread(attrId, dtype.getHidMem(), value)>=0, AipsError); } void HDF5Record::readScaString (hid_t attrId, Int sz, const String& name, RecordInterface& rec) { String value; if (sz > 0) { value.resize (sz); HDF5DataType dtype(value); AlwaysAssert (H5Aread(attrId, dtype.getHidMem(), const_cast(value.c_str()))>=0, AipsError); if (value == "__empty__") { value = String(); } } rec.define (name, value); } void HDF5Record::readArrString (hid_t attrId, const IPosition& shape, const String& name, RecordInterface& rec) { Array value(shape); std::vector ptrs(value.nelements()); HDF5DataType dtype((String*)0); AlwaysAssert (H5Aread(attrId, dtype.getHidMem(), &(ptrs[0])) >= 0, AipsError); // Copy the strings to the Array and delete the strings. Array::iterator aiter=value.begin(); for (std::vector::const_iterator viter = ptrs.begin(); viter!=ptrs.end(); ++aiter, ++viter) { *aiter = String(*viter); ::free(*viter); } rec.define (name, value); } void HDF5Record::writeRecord (const HDF5Object& parentHid, const String& recordName, const RecordInterface& rec) { // First delete the record in case it already exists. HDF5Group::remove (parentHid, recordName); // Create group. HDF5Group gid(parentHid, recordName, false, true); doWriteRecord (gid, rec); } void HDF5Record::doWriteRecord (const HDF5Object& groupHid, const RecordInterface& rec) { for (uInt i=0; i (groupHid, name, rec, i); break; case TpUChar: writeSca (groupHid, name, rec, i); break; case TpShort: writeSca (groupHid, name, rec, i); break; case TpInt: writeSca (groupHid, name, rec, i); break; case TpUInt: writeSca (groupHid, name, rec, i); break; case TpInt64: writeSca (groupHid, name, rec, i); break; case TpFloat: writeSca (groupHid, name, rec, i); break; case TpDouble: writeSca (groupHid, name, rec, i); break; case TpComplex: writeSca (groupHid, name, rec, i); break; case TpDComplex: writeSca (groupHid, name, rec, i); break; case TpString: writeScaString (groupHid, name, rec.asString(i)); break; case TpArrayBool: writeArr (groupHid, name, rec, i); break; case TpArrayUChar: writeArr (groupHid, name, rec, i); break; case TpArrayShort: writeArr (groupHid, name, rec, i); break; case TpArrayInt: writeArr (groupHid, name, rec, i); break; case TpArrayUInt: writeArr (groupHid, name, rec, i); break; case TpArrayInt64: writeArr (groupHid, name, rec, i); break; case TpArrayFloat: writeArr (groupHid, name, rec, i); break; case TpArrayDouble: writeArr (groupHid, name, rec, i); break; case TpArrayComplex: writeArr (groupHid, name, rec, i); break; case TpArrayDComplex: writeArr (groupHid, name, rec, i); break; case TpArrayString: writeArrString (groupHid, name, rec.asArrayString(i)); break; case TpRecord: { // Write a record in a new subgroup. HDF5Group gid(groupHid, name, false, true); doWriteRecord (gid, rec.asRecord(i)); } break; default: throw HDF5Error ("Unknown attribute data type"); } } } void HDF5Record::writeScalar (hid_t groupHid, const String& name, const void* value, const HDF5DataType& dtype) { // Create the data space for the scalar. HDF5HidDataSpace dsid (H5Screate_simple(0, NULL, NULL)); AlwaysAssert (dsid.getHid()>=0, AipsError); // Create the attribute. HDF5HidAttribute id (H5Acreate2(groupHid, name.c_str(), dtype.getHidFile(), dsid, H5P_DEFAULT, H5P_DEFAULT)); AlwaysAssert (id.getHid()>=0, AipsError); AlwaysAssert (H5Awrite(id, dtype.getHidMem(), value)>=0, AipsError); } void HDF5Record::writeScaString (hid_t groupHid, const String& name, const String& value) { // It seems that HDF5 cannot handle empty fixed length strings. String val(value); if (val.empty()) { val = "__empty__"; } // Create the data space for the scalar. HDF5HidDataSpace dsid (H5Screate_simple(0, NULL, NULL)); AlwaysAssert (dsid.getHid()>=0, AipsError); // Create the attribute. HDF5DataType dtype(val); HDF5HidAttribute id (H5Acreate2(groupHid, name.c_str(), dtype.getHidFile(), dsid, H5P_DEFAULT, H5P_DEFAULT)); AlwaysAssert (id.getHid()>=0, AipsError); AlwaysAssert (H5Awrite(id, dtype.getHidMem(), val.c_str())>=0, AipsError); } void HDF5Record::writeArray (hid_t groupHid, const String& name, const void* value, const IPosition& shape, const HDF5DataType& dtype) { int rank = shape.nelements(); if (shape.product() == 0) { writeEmptyArray (groupHid, name, rank, HDF5DataType::getDataType(dtype.getHidMem())); return; } // Create the data space for the array. Block ls = HDF5DataType::fromShape (shape); HDF5HidDataSpace dsid (H5Screate_simple(rank, ls.storage(), NULL)); AlwaysAssert (dsid.getHid()>=0, AipsError); // Create the attribute. HDF5HidAttribute id (H5Acreate2(groupHid, name.c_str(), dtype.getHidFile(), dsid, H5P_DEFAULT, H5P_DEFAULT)); AlwaysAssert (id.getHid()>=0, AipsError); AlwaysAssert (H5Awrite(id, dtype.getHidMem(), value)>=0, AipsError); } void HDF5Record::writeEmptyArray (hid_t groupHid, const String& name, Int rank, DataType dtype) { Int values[3]; values[0] = 1; values[1] = rank; values[2] = dtype; HDF5DataType dtid(values[1], values[2]); writeScalar (groupHid, name, values, dtid); } void HDF5Record::writeArrString (hid_t groupHid, const String& name, const Array& value) { if (value.nelements() == 0) { writeEmptyArray (groupHid, name, value.ndim(), TpString); return; } // Copy the string pointers to a vector. std::vector ptrs(value.nelements()); Array::const_iterator aiter=value.begin(); for (std::vector::iterator viter = ptrs.begin(); viter!=ptrs.end(); ++aiter, ++viter) { *viter = (char*)(aiter->c_str()); } // It appears that HDF5 cannot write an attribute with more than 4000 values. // So cut off if needed. IPosition shape = value.shape(); if (shape[0] > 4000) { LogIO os; os << "HDF5Record: Cut off size of attribute " + name + " from " + String::toString(shape[0]) + " to 4000 values" << LogIO::NORMAL << LogIO::POST; shape[0] = 4000; } // Create the data space for the array. int rank = shape.nelements(); Block ls = HDF5DataType::fromShape (shape); HDF5HidDataSpace dsid (H5Screate_simple(rank, ls.storage(), NULL)); AlwaysAssert (dsid.getHid()>=0, AipsError); // Create the attribute. HDF5DataType dtype((String*)0); HDF5HidAttribute id (H5Acreate2(groupHid, name.c_str(), dtype.getHidFile(), dsid, H5P_DEFAULT, H5P_DEFAULT)); AlwaysAssert (id.getHid()>=0, AipsError); AlwaysAssert (H5Awrite(id, dtype.getHidMem(), &(ptrs[0])) >= 0, AipsError); } #else void HDF5Record::remove (const HDF5Object&, const String&) { HDF5Object::throwNoHDF5(); } Record HDF5Record::readRecord (const HDF5Object&, const String&) { HDF5Object::throwNoHDF5(); return Record(); } Record HDF5Record::doReadRecord (hid_t) { HDF5Object::throwNoHDF5(); return Record(); } void HDF5Record::readScalar (hid_t, hid_t, const String&, RecordInterface&) { HDF5Object::throwNoHDF5(); } void HDF5Record::readArray (hid_t, hid_t, const IPosition&, const String&, RecordInterface&) { HDF5Object::throwNoHDF5(); } void HDF5Record::readEmptyArray (hid_t, const String&, RecordInterface&) { HDF5Object::throwNoHDF5(); } void HDF5Record::read (hid_t, void*, const HDF5DataType&) { HDF5Object::throwNoHDF5(); } void HDF5Record::readScaString (hid_t, Int, const String&, RecordInterface&) { HDF5Object::throwNoHDF5(); } void HDF5Record::readArrString (hid_t, const IPosition&, const String&, RecordInterface&) { HDF5Object::throwNoHDF5(); } void HDF5Record::writeRecord (const HDF5Object&, const String&, const RecordInterface&) { HDF5Object::throwNoHDF5(); } void HDF5Record::doWriteRecord (const HDF5Object&, const RecordInterface&) { HDF5Object::throwNoHDF5(); } void HDF5Record::writeScalar (hid_t, const String&, const void* , const HDF5DataType&) { HDF5Object::throwNoHDF5(); } void HDF5Record::writeScaString (hid_t, const String&, const String&) { HDF5Object::throwNoHDF5(); } void HDF5Record::writeArray (hid_t, const String&, const void*, const IPosition&, const HDF5DataType&) { HDF5Object::throwNoHDF5(); } void HDF5Record::writeEmptyArray (hid_t, const String&, Int, DataType) { HDF5Object::throwNoHDF5(); } void HDF5Record::writeArrString (hid_t, const String&, const Array&) { HDF5Object::throwNoHDF5(); } #endif } casacore-3.7.1/casa/HDF5/HDF5Record.h000066400000000000000000000164451476623553700167310ustar00rootroot00000000000000//# HDF5Record.h: A class to write/read a record into HDF5 //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_HDF5RECORD_H #define CASA_HDF5RECORD_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A class to write/read a record into HDF5. // // // // // //
      • HDF5 system //
      • class Record // // // This class has a static function to write a Record (or TableRecord) // into an HDF5 file by storing it as attributes for the given group. // Another static function can read back the Record. // It can handle all types of fields in a record. //
        // A few remarks: //
          //
        • When writing the record, it first deletes all attributes of the group // to be sure that the group's attributes only contain the record. //
        • A Casacore Record is a recursive structure, so it is written as // nested groups. The name of a subgroup is the name of the subrecord. //
        • HDF5 cannot deal with empty arrays. Therefore they are written as // a special compound type holding the rank and type of the empty array. //
        • HDF5 cannot hold empty fixed length strings. This is solved by // storing an empty string with the special value __empty__. //
        //
        // // Record is a very important class in Casacore images, so it has to be // possible to read and write them from/to HDF5. // class HDF5Record { public: // Read a record from the attributes of the given group. // Nested records are read back correctly. // An empty record is returned if the group does not exist. static Record readRecord (const HDF5Object& parentHid, const String& groupName); // Write the record as attributes of a group of the given parent. // Nested records are written as nested groups. // The group is deleted first if it already exists. static void writeRecord (const HDF5Object& parentHid, const String& recordName, const RecordInterface& rec); // Remove the record (i.e. group) from the given parent. // Nothing is done if the record does not exist. static void remove (const HDF5Object& parentHid, const String& recordName); // Read the (possibly nested) record values from the given group hid. static Record doReadRecord (hid_t parentHid); // Write the (possibly nested) record values into the given group hid. static void doWriteRecord (const HDF5Object& groupHid, const RecordInterface& rec); private: // Read a scalar value and add it to the record. static void readScalar (hid_t attrId, hid_t dtid, const String& name, RecordInterface& rec); // Read an array value and add it to the record. static void readArray (hid_t attrId, hid_t dtid, const IPosition&, const String& name, RecordInterface& rec); // Read a scalar string from an attribute and add it to the record. static void readScaString (hid_t attrId, Int sz, const String& name, RecordInterface& rec); // Read a array of strings from an atrribute and add it to the record. static void readArrString (hid_t attrId, const IPosition&, const String& name, RecordInterface& rec); // Read a field containing an empty array. static void readEmptyArray (hid_t attrId, const String& name, RecordInterface& rec); // Read a field containing a scalar of fixed length. template static void readSca (hid_t attrId, const String& name, RecordInterface& rec) { T value; HDF5DataType dtype((T*)0); read (attrId, &value, dtype); rec.define (name, value); } // Read a field containing an array of fixed length elements. template static void readArr (hid_t attrId, const IPosition& shape, const String& name, RecordInterface& rec) { Array value(shape); HDF5DataType dtype((T*)0); read (attrId, value.data(), dtype); rec.define (name, value); } // Read fixed length values from an attribute (scalar and array). static void read (hid_t attrId, void* value, const HDF5DataType& dtype); // Write a fixed length scalar value as attribute. static void writeScalar (hid_t parentHid, const String& name, const void* value, const HDF5DataType& dtype); // Write an array of fixed length values as attribute. static void writeArray (hid_t parentHid, const String& name, const void* value, const IPosition& shape, const HDF5DataType& dtype); // Write a scalar string as attribute. // HDF5 cannot handle empty strings, so for empty strings a special // value is written. static void writeScaString (hid_t parentHid, const String& name, const String& value); // Write an array of strings as attribute. // HDF5 cannot handle empty strings, so for empty strings a special // value is written. static void writeArrString (hid_t parentHid, const String& name, const Array& value); // Write a field containing an empty array. static void writeEmptyArray (hid_t groupHid, const String& name, Int rank, DataType dtype); // Write a field containing a fixed length scalar value. template static void writeSca (hid_t parentHid, const String& name, const RecordInterface& rec, Int i) { T value; rec.get (i, value); HDF5DataType dtype((T*)0); writeScalar (parentHid, name, &value, dtype); } // Write a field containing an array of fixed length elements. template static void writeArr (hid_t parentHid, const String& name, const RecordInterface& rec, Int i) { Array value; rec.get (i, value); HDF5DataType dtype((T*)0); writeArray (parentHid, name, value.data(), value.shape(), dtype); } }; } #endif casacore-3.7.1/casa/HDF5/test/000077500000000000000000000000001476623553700157005ustar00rootroot00000000000000casacore-3.7.1/casa/HDF5/test/CMakeLists.txt000066400000000000000000000005151476623553700204410ustar00rootroot00000000000000set (tests tHDF5 tHDF5DataSet tHDF5DataType tHDF5File tHDF5Record ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_casa) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/casa/HDF5/test/tHDF5.cc000066400000000000000000000327641476623553700170750ustar00rootroot00000000000000//# tHDF5.cc: Test program for HDF5 performance of tiling //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #ifndef HAVE_HDF5 int main() { std::cout << "HDF5 not configured in, so cannot run tHDF5" << std::endl; return 0; } #else #include #include #include #include using namespace std; // Quick and dirty. // Define some global hid variables. hid_t fileId=-1; // file hid_t setId=-1; // dataset hid_t dsId=-1; // dataspace hid_t plId=-1; // dataset property list hid_t daplId=-1; // dataset access hid_t typeIdMem=-1; hid_t typeIdFile=-1; template void check (T v, const char* message) { if (v < 0) { cerr << message << endl; exit(1); } } void createFile() { // Use 8 byte offets and blocks of 32768 bytes. hid_t create_plist = H5Pcreate(H5P_FILE_CREATE); check (create_plist, "Failed to create creation plist"); check (H5Pset_sizes(create_plist, 8, 8), "Failed to set offset size"); check (H5Pset_userblock(create_plist, 32768), "Failed to set block size"); // Use unbuffered IO. hid_t access_plist = H5Pcreate(H5P_FILE_ACCESS); check (access_plist, "Failed to create access plist"); check (H5Pset_fapl_sec2(access_plist), "Failed to set access mode"); // Create the file. fileId = H5Fcreate ("tHDF5_tmp.dat", H5F_ACC_TRUNC, create_plist, access_plist); check (fileId, "Failed to create the file"); H5Pclose (create_plist); H5Pclose (access_plist); } void openFile() { // Use unbuffered IO. hid_t access_plist = H5Pcreate(H5P_FILE_ACCESS); check (access_plist, "Failed to create access plist"); check (H5Pset_fapl_sec2(access_plist), "Failed to set access mode"); fileId = H5Fopen ("tHDF5_tmp.dat", H5F_ACC_RDONLY, access_plist); check (fileId, "Failed to create the file"); H5Pclose (access_plist); } void closeFile() { if (fileId >= 0) H5Fclose (fileId); fileId = -1; } void createDataSet (int nx, int ny, int nz, int ntx, int nty, int ntz) { // Create hids for the data type on disk and in memory. typeIdFile = H5Tcopy (H5T_NATIVE_FLOAT); check (typeIdFile, "Failed to create file datatype id"); typeIdMem = H5Tcopy (typeIdFile); check (typeIdMem, "Failed to create memory datatype id"); // Create access property for later setting of cache size. daplId = H5Pcreate (H5P_DATASET_ACCESS); check (daplId, "Failed to create dataset access property list"); // Create the data space for the array. int rank = 3; hsize_t shp[3]; shp[0] = nz; shp[1] = ny; shp[2] = nx; dsId = H5Screate_simple(rank, shp, NULL); check (dsId, "Failed to create dataspace"); // Create the properties to hold the chunk shape. plId = H5Pcreate (H5P_DATASET_CREATE); check (plId, "Failed to create dataset property list"); hsize_t chunkShp[3]; chunkShp[0] = ntz; chunkShp[1] = nty; chunkShp[2] = ntx; H5Pset_chunk (plId, rank, chunkShp); // Create the data set. setId = H5Dcreate2 (fileId, "dataset", typeIdFile, dsId, 0, plId, 0); check (setId, "Failed to create dataset"); } void openDataSet() { // Create hids for the data type on disk and in memory. typeIdFile = H5Tcopy (H5T_NATIVE_FLOAT); typeIdMem = H5Tcopy (typeIdFile); // Open the dataset. setId = H5Dopen2 (fileId, "dataset", 0); check (setId, "Failed to open dataset"); // Get the data space (for the shape). dsId = H5Dget_space(setId); check (dsId, "Failed to get dataspace"); // Create access property for later setting of cache size. daplId = H5Pcreate (H5P_DATASET_ACCESS); check (daplId, "Failed to create dataset access property list"); } void closeDataSet() { if (setId >= 0) H5Dclose (setId); setId = -1; if (plId >= 0) H5Pclose (plId); plId = -1; if (dsId >= 0) H5Sclose (dsId); dsId = -1; if (daplId >= 0) H5Pclose (daplId); daplId = -1; if (typeIdFile >= 0) H5Tclose (typeIdFile); typeIdFile = -1; if (typeIdMem >= 0) H5Tclose (typeIdMem); typeIdMem = -1; } bool isPrime (int v) { if (v%2 == 0) return false; // Very simple implementation, but suffices. int iend = int(sqrt(float(v))); for (int i=3; i<=iend; i+=2) { if (v%i == 0) return false; } return true; } void setCacheSize (int nchunks, int nbytes) { int nhash = 100*nchunks+1; while (!isPrime(nhash)) { nhash += 2; } cout << "setting cache to " << nchunks << " chunks (" << nbytes << " bytes) with " << nhash << " slots" << endl; // Setting the cache size takes only effect when opening the dataset. // So close it first. H5Dclose (setId); // Use LRU caching (4th argument is 0). check (H5Pset_chunk_cache (daplId, nhash, nbytes, 0.), "Failed to set chunk cache"); // Reopen the dataset with cache size in daplId. setId = H5Dopen2 (fileId, "dataset", daplId); check (setId, "Failed to reopen file"); } void get (int stx, int sty, int stz, int nx, int ny, int nz, void* buf) { // Define the data set selection. hsize_t offset[3], count[3], stride[3]; offset[0] = stz; count[0] = nz; stride[0] = 1; offset[1] = sty; count[1] = ny; stride[1] = 1; offset[2] = stx; count[2] = nx; stride[2] = 1; check (H5Sselect_hyperslab (dsId, H5S_SELECT_SET, offset, stride, count, NULL), "invalid data set array selection"); // Define a data space for the memory buffer. hid_t dsmemid = H5Screate_simple (3, count, NULL); check (dsmemid, "Failed to create memory data space"); // Define memory selection. offset[0]=0; offset[1]=0; offset[2] = 0; check (H5Sselect_hyperslab (dsmemid, H5S_SELECT_SET, offset, NULL, count, NULL), "Failed to set slab of memory buffer"); // Read the data. check (H5Dread (setId, typeIdMem, dsmemid, dsId, H5P_DEFAULT, buf), "Failed to read slab from data set array"); H5Sclose (dsmemid); } void put (int stx, int sty, int stz, int nx, int ny, int nz, const void* buf) { // Define the data set selection. hsize_t offset[3], count[3], stride[3]; offset[0] = stz; count[0] = nz; stride[0] = 1; offset[1] = sty; count[1] = ny; stride[1] = 1; offset[2] = stx; count[2] = nx; stride[2] = 1; check (H5Sselect_hyperslab (dsId, H5S_SELECT_SET, offset, stride, count, NULL), "invalid data set array selection"); // Define a data space for the memory buffer. hid_t dsmemid = H5Screate_simple (3, count, NULL); check (dsmemid, "Failed to create memory data space"); // Define memory selection. offset[0]=0; offset[1]=0; offset[2] = 0; check (H5Sselect_hyperslab (dsmemid, H5S_SELECT_SET, offset, NULL, count, NULL), "Failed to set slab of memory buffer"); // Write the data. check (H5Dwrite (setId, typeIdMem, dsmemid, dsId, H5P_DEFAULT, buf), "Failed to write slab into data set array"); H5Sclose (dsmemid); } void create (int nx, int ny, int nz, int ntx, int nty, int ntz) { // Create the file and data set. createFile(); createDataSet (nx, ny, nz, ntx, nty, ntz); // Fill the data set chunk by chunk. int sz = ntx*nty*ntz; vector buf(sz); for (int iz=0; iz buf(sz); for (int iz=0; iz buf(nx); for (int iz=0; iz buf(ny); for (int iz=0; iz buf(nz); for (int iy=0; iy 8 && argv[8][0] == '1'); readback (nx, ny, nz, ntx, nty, ntz, argv[7][0], checkv); } exit(0); } #endif casacore-3.7.1/casa/HDF5/test/tHDF5DataSet.cc000066400000000000000000000137361476623553700203410ustar00rootroot00000000000000//# tHDF5DataSet.cc: Test program for class HDF5DataSet //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include using namespace casacore; // Make a compound of a Complex, Int and Float[2]. struct Data { Complex f1; Int f2; Float f3[2]; }; void testCompound() { std::vector names(3); std::vector types(3); names[0] = "f1"; names[1] = "f2"; names[2] = "f3"; types[0] = HDF5DataType((Complex*)0); types[1] = HDF5DataType((Int*)0); types[2] = HDF5DataType(HDF5DataType((Float*)0), IPosition(1,2)); HDF5DataType dtcom(names, types); IPosition shape(1,3); { // Create the file. HDF5File file("tHDF5DataSet_tmp", ByteIO::New); // Create a data set in it. IPosition ts(1,3); HDF5DataSet dset(file, "array", shape, ts, dtcom); AlwaysAssertExit (dset.getName() == "array"); AlwaysAssertExit (dset.shape() == shape); AlwaysAssertExit (dset.tileShape() == shape); AlwaysAssertExit (HDF5DataSet::getDataType (file, "array") == TpRecord); // Put 3 rows. for (int i=0; i<3; ++i) { Vector data(1);; data[0].f1 = Complex(i-10., i+10.); data[0].f2 = i; data[0].f3[0] = i+100.; data[0].f3[1] = i-100.; dset.put (Slicer(IPosition(1,i), IPosition(1,1)), data); } } { // Open the file and data set. HDF5File file("tHDF5DataSet_tmp", ByteIO::Old); HDF5DataSet dset(file, "array", dtcom); AlwaysAssertExit (dset.getName() == "array"); AlwaysAssertExit (dset.shape() == shape); AlwaysAssertExit (dset.tileShape() == shape); // Set the cache size in chunks. dset.setCacheSize (10); Array ires(shape); dset.get (Slicer(IPosition(1,0), shape), ires); AlwaysAssertExit (Int(ires.size()) == shape.product()); for (uInt i=0; i iarr(shape); indgen(iarr); { // Create the file. HDF5File file("tHDF5DataSet_tmp", ByteIO::New); // Create a data set in it. HDF5DataSet dset(file, "array", IPosition(2,0,shape[1]), ts, (Int*)0); AlwaysAssertExit (dset.getName() == "array"); AlwaysAssertExit (dset.shape() == IPosition(2,0,shape[1])); AlwaysAssertExit (dset.tileShape() == shape); dset.extend (shape); AlwaysAssertExit (dset.shape() == shape); AlwaysAssertExit (dset.tileShape() == shape); dset.put (Slicer(IPosition(2,0), shape), iarr); AlwaysAssertExit (HDF5DataSet::getDataType (file, "array") == TpInt); } { // Open the file and data set. HDF5File file("tHDF5DataSet_tmp", ByteIO::Old); HDF5DataSet dset(file, "array", (Int*)0); AlwaysAssertExit (dset.getName() == "array"); AlwaysAssertExit (dset.shape() == shape); AlwaysAssertExit (dset.tileShape() == shape); // Set the cache size in chunks. dset.setCacheSize (10); Array ires(shape); dset.get (Slicer(IPosition(2,0), shape), ires); AlwaysAssertExit (allEQ(iarr, ires)); Slicer section(IPosition(2,1), IPosition(2,2), IPosition(2,2)); Array ires2(section.length()); dset.get (section, ires2.data()); cout<< ires2<< iarr(section)< #include #include #include using namespace casacore; template void testDT (DataType dt, DataType arrdt, uInt sz, Bool isC=False) { // Test scalar. HDF5DataType hdt((T*)0); AlwaysAssertExit (hdt.size() == sz); AlwaysAssertExit (HDF5DataType::getDataType(hdt.getHidMem()) == dt); AlwaysAssertExit (HDF5DataType::getDataType(hdt.getHidFile()) == dt); AlwaysAssertExit (HDF5DataType::isComplex(hdt.getHidMem()) == isC); AlwaysAssertExit (HDF5DataType::isComplex(hdt.getHidFile()) == isC); // Test array. HDF5DataType dtarr(hdt, IPosition(2,3,4)); AlwaysAssertExit (dtarr.size() == 3*4*sz); AlwaysAssertExit (HDF5DataType::getDataType(dtarr.getHidMem()) == arrdt); AlwaysAssertExit (HDF5DataType::getShape(dtarr.getHidMem()) == IPosition(2,3,4)); AlwaysAssertExit (HDF5DataType::getDataType(dtarr.getHidFile()) == arrdt); AlwaysAssertExit (HDF5DataType::getShape(dtarr.getHidFile()) == IPosition(2,3,4)); } void testCompound() { // First test with a few scalars. std::vector names(3); std::vector types(3); names[0] = "f1"; names[1] = "f2"; names[2] = "f3"; types[0] = HDF5DataType((Complex*)0); types[1] = HDF5DataType((Int*)0); types[2] = HDF5DataType((Float*)0); HDF5DataType dtcom1(names, types); AlwaysAssertExit (dtcom1.size() == 16); AlwaysAssertExit (HDF5DataType::getDataType(dtcom1.getHidMem()) == TpRecord); // Now add a few arrays and the previous compound. names.push_back ("fa1"); names.push_back ("fa2"); names.push_back ("fc"); types.push_back (HDF5DataType(HDF5DataType((Bool*)0), IPosition(1,8))); types.push_back (HDF5DataType(HDF5DataType((DComplex*)0), IPosition(2,1,2))); types.push_back (dtcom1); HDF5DataType dtcom2(names, types); AlwaysAssertExit (dtcom2.size() == 16 + 8 + 2*16 + 16); AlwaysAssertExit (HDF5DataType::getDataType(dtcom2.getHidMem()) == TpRecord); } int main() { // Exit with untested if no HDF5 support. if (! HDF5Object::hasHDF5Support()) { return 3; } try { testDT (TpBool, TpArrayBool, 1); testDT (TpUChar, TpArrayUChar, 1); testDT (TpShort, TpArrayShort, 2); testDT (TpInt, TpArrayInt, 4); testDT (TpUInt, TpArrayUInt, 4); testDT (TpInt64, TpArrayInt64, 8); testDT (TpFloat, TpArrayFloat, 4); testDT (TpDouble, TpArrayDouble, 8); testDT (TpComplex, TpArrayComplex, 8, True); testDT (TpDComplex, TpArrayDComplex, 16, True); testCompound(); } catch (std::exception& x) { cout << "Unexpected exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/casa/HDF5/test/tHDF5File.cc000066400000000000000000000101501476623553700176560ustar00rootroot00000000000000//# tHDF5File.cc: Test program for class HDF5File //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include using namespace casacore; int main() { // Exit with untested if no HDF5 support. if (! HDF5Object::hasHDF5Support()) { cout << "OK" << endl; return 3; } try { { // Create the file. HDF5File file("tHDF5File_tmpx", ByteIO::New); AlwaysAssertExit (Path(file.getName()).baseName() == "tHDF5File_tmpx"); AlwaysAssertExit (! file.isClosed()); AlwaysAssertExit (file.isWritable()); AlwaysAssertExit (! file.isOpenedForDelete()); // Close it temporarily. file.close(); AlwaysAssertExit (file.isClosed()); AlwaysAssertExit (file.isWritable()); AlwaysAssertExit (! file.isOpenedForDelete()); // Flush is a no-op if the file is closed. file.flush(); // Reopen it. file.reopen(); AlwaysAssertExit (! file.isClosed()); AlwaysAssertExit (file.isWritable()); AlwaysAssertExit (! file.isOpenedForDelete()); file.flush(); } { // Open readonly. HDF5File file("tHDF5File_tmpx", ByteIO::Old); AlwaysAssertExit (! file.isClosed()); AlwaysAssertExit (! file.isWritable()); AlwaysAssertExit (! file.isOpenedForDelete()); // Reopen for read/write. file.reopenRW(); AlwaysAssertExit (! file.isClosed()); AlwaysAssertExit (file.isWritable()); AlwaysAssertExit (! file.isOpenedForDelete()); } { // Open for delete. HDF5File file("tHDF5File_tmpx", ByteIO::Delete); AlwaysAssertExit (! file.isClosed()); AlwaysAssertExit (file.isWritable()); AlwaysAssertExit (file.isOpenedForDelete()); } { // File name gets absolute, so ignore for output comparison. cout << ">>>" << endl; Bool succ = True; try { HDF5File file("tHDF5File_tmpx", ByteIO::Old); } catch (std::exception& x) { succ = False; cout << x.what() << endl; } AlwaysAssertExit (!succ); cout << "<<<" << endl; } { // Create the file for scratch. HDF5File file("tHDF5File_tmp", ByteIO::Scratch); AlwaysAssertExit (! file.isClosed()); AlwaysAssertExit (file.isWritable()); AlwaysAssertExit (file.isOpenedForDelete()); } { // Create the file. Succeeds, because scratch file is deleted. HDF5File file("tHDF5File_tmp", ByteIO::NewNoReplace); } { // Create the file. Fails, because already exists. cout << ">>>" << endl; Bool succ = True; try { HDF5File file("tHDF5File_tmp", ByteIO::NewNoReplace); } catch (std::exception& x) { succ = False; cout << x.what() << endl; } AlwaysAssertExit (!succ); cout << "<<<" << endl; } } catch (std::exception& x) { cout << "Unexpected exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/casa/HDF5/test/tHDF5File.out000066400000000000000000000002161476623553700201020ustar00rootroot00000000000000>>> HDF5 error: Could not open or create HDF5 file tHDF5File_tmpx <<< >>> HDF5 error: Could not open or create HDF5 file tHDF5File_tmp <<< OK casacore-3.7.1/casa/HDF5/test/tHDF5Record.cc000066400000000000000000000227011476623553700202220ustar00rootroot00000000000000//# tHDF5Record.cc: Test program for class HDF5Record //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include using namespace casacore; Array arrb() { Array arrb(IPosition(4,1,1,4,1)); arrb = False; arrb(IPosition(4,0,0,2,0)) = True; return arrb; } Array arruc() { Array arruc(IPosition(1,1)); indgen(arruc); return arruc; } Array arrs() { Array arrs(IPosition(2,3,4)); indgen(arrs); return arrs; } Array arri() { Array arri(IPosition(3,2,2,2)); indgen(arri); return arri; } Array arrui() { Array arrui(IPosition(1,5)); indgen(arrui, 32768u*65536u); return arrui; } Array arri64() { Array arri(IPosition(3,2,2,2)); indgen(arri, Int64(2e10)); return arri; } Array arrf() { Array arrf(IPosition(1,3)); indgen(arrf); return arrf; } Array arrd() { Array arrd(IPosition(1,5)); indgen(arrd); return arrd; } Array arrc() { Array arrc(IPosition(1,10)); indgen(arrc); return arrc; } Array arrdc() { Array arrdc(IPosition(1,8)); indgen(arrdc); return arrdc; } Array arrstr() { Array arrstr(IPosition(3,2,1,2)); arrstr(IPosition(3,0,0,0)) = "str000"; arrstr(IPosition(3,1,0,0)) = "a horse without a name"; arrstr(IPosition(3,0,0,1)) = "stray"; arrstr(IPosition(3,1,0,1)) = ""; return arrstr; } Array arrstremp() { Array arrstr(IPosition(6,1,2,3,4,5,6)); return arrstr; } Array emparrb() { return Array (IPosition(4,0)); } Array emparruc() { return Array (IPosition(0,0)); } Array emparrs() { return Array (IPosition(0,0)); } Array emparri() { return Array (IPosition(0,0)); } Array emparrui() { return Array (IPosition(1,0)); } Array emparri64() { return Array (IPosition(2,0)); } Array emparrf() { return Array (IPosition(2,0)); } Array emparrd() { return Array (IPosition(1,0)); } Array emparrc() { return Array (IPosition(2,0,0)); } Array emparrdc() { return Array (IPosition(2,0,0)); } Array emparrstr() { return Array (IPosition(0,0)); } void checkRecord (const RecordInterface& rec) { AlwaysAssertExit (rec.nfields()==12); AlwaysAssertExit (rec.asBool("bool") == True); AlwaysAssertExit (rec.asuChar("uchar") == 1); AlwaysAssertExit (rec.asShort("short") == -2); AlwaysAssertExit (rec.asInt("int") == 2); AlwaysAssertExit (rec.asuInt("uint") == 21); AlwaysAssertExit (rec.asInt64("int64") == Int64(1e10)); AlwaysAssertExit (rec.asFloat("float") == 3.); AlwaysAssertExit (rec.asDouble("double") == -2.1); AlwaysAssertExit (rec.asComplex("complex") == Complex(-2.1,1.1)); AlwaysAssertExit (rec.asDComplex("dcomplex") == DComplex(-2.2,1.2)); AlwaysAssertExit (allEQ(rec.asArrayString("arrstring"), arrstr())); AlwaysAssertExit (rec.asString("string") == "abc"); } void check (HDF5File& file) { // Read the records back and check them. Record reca3 = HDF5Record::readRecord (file, "test"); AlwaysAssertExit (reca3.nfields()==25); AlwaysAssertExit (reca3.asString("string") == ""); AlwaysAssertExit (allEQ(reca3.asArrayString("arrstringemp"), arrstremp())); AlwaysAssertExit (allEQ(reca3.asArrayBool("arrbool"), arrb())); AlwaysAssertExit (allEQ(reca3.asArrayuChar("arruchar"), arruc())); AlwaysAssertExit (allEQ(reca3.asArrayShort("arrshort"), arrs())); AlwaysAssertExit (allEQ(reca3.asArrayInt("arrint"), arri())); AlwaysAssertExit (allEQ(reca3.asArrayuInt("arruint"), arrui())); AlwaysAssertExit (allEQ(reca3.asArrayInt64("arrint64"), arri64())); AlwaysAssertExit (allEQ(reca3.asArrayFloat("arrfloat"), arrf())); AlwaysAssertExit (allEQ(reca3.asArrayDouble("arrdouble"), arrd())); AlwaysAssertExit (allEQ(reca3.asArrayComplex("arrcomplex"), arrc())); AlwaysAssertExit (allEQ(reca3.asArrayDComplex("arrdcomplex"), arrdc())); AlwaysAssertExit (allEQ(reca3.asArrayString("arrstring"), arrstr())); AlwaysAssertExit (allEQ(reca3.asArrayBool("emparrbool"), emparrb())); AlwaysAssertExit (allEQ(reca3.asArrayuChar("emparruchar"), emparruc())); AlwaysAssertExit (allEQ(reca3.asArrayShort("emparrshort"), emparrs())); AlwaysAssertExit (allEQ(reca3.asArrayInt("emparrint"), emparri())); AlwaysAssertExit (allEQ(reca3.asArrayuInt("emparruint"), emparrui())); AlwaysAssertExit (allEQ(reca3.asArrayInt64("emparrint64"), emparri64())); AlwaysAssertExit (allEQ(reca3.asArrayFloat("emparrfloat"), emparrf())); AlwaysAssertExit (allEQ(reca3.asArrayDouble("emparrdouble"), emparrd())); AlwaysAssertExit (allEQ(reca3.asArrayComplex("emparrcomplex"), emparrc())); AlwaysAssertExit (allEQ(reca3.asArrayDComplex("emparrdcomplex"), emparrdc())); AlwaysAssertExit (allEQ(reca3.asArrayString("emparrstring"), emparrstr())); Record reca2 = reca3.asRecord("rec2"); AlwaysAssertExit (reca2.nfields()==3); AlwaysAssertExit (reca2.asDouble("double") == 3.14); checkRecord (reca2.asRecord("rec1")); checkRecord (reca2.asRecord("rec1a")); } int main() { // Exit with untested if no HDF5 support. if (! HDF5Object::hasHDF5Support()) { return 3; } try { // Create the file. HDF5File file("tHDF5Record_tmp", ByteIO::New); // Create a record and nested record. Record rec1; rec1.define ("bool", True); rec1.define ("uchar", (uChar)1); rec1.define ("short", (Short)-2); rec1.define ("int", (Int)2); rec1.define ("uint", (uInt)21); rec1.define ("int64", Int64(1e10)); rec1.define ("float", (Float)3.); rec1.define ("double", (Double)-2.1); rec1.define ("complex", Complex(-2.1,1.1)); rec1.define ("dcomplex", DComplex(-2.2,1.2)); rec1.define ("arrstring", arrstr()); rec1.define ("string", "abc"); Record rec2; rec2.defineRecord ("rec1", rec1); rec2.define ("double", (Double)3.14); rec2.defineRecord ("rec1a",rec1); Record rec3; rec3.defineRecord ("rec2",rec2); rec3.define ("string", ""); rec3.define ("arrstringemp", arrstremp()); rec3.define ("arrbool", arrb()); rec3.define ("arruchar", arruc()); rec3.define ("arrshort", arrs()); rec3.define ("arrint", arri()); rec3.define ("arruint", arrui()); rec3.define ("arrint64", arri64()); rec3.define ("arrfloat", arrf()); rec3.define ("arrdouble", arrd()); rec3.define ("arrcomplex", arrc()); rec3.define ("arrdcomplex", arrdc()); rec3.define ("arrstring", arrstr()); rec3.define ("emparrbool", emparrb()); rec3.define ("emparruchar", emparruc()); rec3.define ("emparrshort", emparrs()); rec3.define ("emparrint", emparri()); rec3.define ("emparruint", emparrui()); rec3.define ("emparrint64", emparri64()); rec3.define ("emparrfloat", emparrf()); rec3.define ("emparrdouble", emparrd()); rec3.define ("emparrcomplex", emparrc()); rec3.define ("emparrdcomplex", emparrdc()); rec3.define ("emparrstring", emparrstr()); HDF5Record::writeRecord (file, "test", rec3); file.flush(); // Read everything back and check. check (file); // Write again and check. HDF5Record::writeRecord (file, "test", rec3); check (file); // Check a group exists. AlwaysAssertExit (HDF5Group::exists (file, "test")); // Get the group names in the file. vector names = HDF5Group::linkNames (file); AlwaysAssertExit (names.size() == 1 && names[0] == "test"); // Delete the record. HDF5Record::remove (file, "test"); // Read back and check it is empty. Record reca3 = HDF5Record::readRecord (file, "test"); AlwaysAssertExit (reca3.nfields() == 0); // Do the same for a non-existing group. Record reca4 = HDF5Record::readRecord (file, "xxx"); AlwaysAssertExit (reca4.nfields() == 0); // Write again and check. HDF5Record::writeRecord (file, "test", rec3); check (file); } catch (std::exception& x) { cout << "Unexpected exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/casa/IO.h000066400000000000000000000142251476623553700147170ustar00rootroot00000000000000//# IO.h: Basic classes and global functions for IO and object persistency //# Copyright (C) 1995,1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_IO_H #define CASA_IO_H #include //# Includes for object persistency. #include #include //# Includes for general IO. #include //# Includes for underlying IO classes. #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // // Basic classes and global functions for IO and object persistency // // // // // This module provides the basic IO functionality for the Casacore classes. // There are two IO mechanisms: //
          //
        1. Class AipsIO // provides the object persistency mechanism. // The templated global functions in // AipsIOCarray.h // form a little layer upon AipsIO. They provide the means to put or // get a C-style array of any type. //
        2. Class ByteSinkSource // and its ancestors provide a general IO mechanism. //
        // // Both use the underlying IO framework which define where and how // the data are written. The how-part is defined by classes derived from // TypeIO as shown // in the UML diagram. // There are three such classes: //
          //
        1. CanonicalIO reads/writes // data in canonical (machine-independent) format. This should be // used when data are meant to be exportable. // It uses the conversion functions in class // CanonicalConversion // . //
        2. RawIO reads/writes // data in native (machine-dependent) format. This can be used when // data are not exported. //
        3. ConversionIO // reads/writes in an external format as defined at construction time. // This can be used when the external format can be one of several // (e.g. VAX or IBM for a WSRT archive tape). In this way the // format has to be defined only once and thereafter is it handled // correctly by the polymorphism mechanism. //
        // The where-part is defined by classes derived from // ByteIO as shown // in the UML diagram. // There are a few such classes: //
          //
        1. RegularFileIO uses a // regular file to hold the data. Internally it uses FilebufIO (see below). // It can handle files > 2 GB. //
        2. FilebufIO does the IO // in a buffered way similar to the stdio system. However, it // does not use stdio because that gave problems when doing concurrent // access from multiple processes. // It can handle files > 2 GB. //
        3. FiledesIO uses the // UNIX IO-functions like open, read to do IO directly. // It does not use an internal buffer. Instead it always does // physical IO. It is meant for IO operations where large chunks of // a file are accessed and for IO on sockets, pipes, etc.. // It can handle files > 2 GB. //
        4. StreamIO for IO // on a socket. //
        5. TapeIO for IO on a tape // device. //
        6. MemoryIO uses a // (possibly expandable) buffer in memory to hold the data. //
        7. MMapIO uses memory-mapped IO. // Be careful to use this on 32-bit machines, because its address space is // too small to handle a file of a few GBytes. //
        8. MFFileIO uses a virtual // file in a MultiFileBase // container file. The derived classes MultiFile and MultiHDF5 combine // multiple files in a single physical file to reduce the number of // files used by CTDS (the Casacore table data system). //
        // // The IO framework is easily expandable. One can for instance think of a // class AsciiIO derived from TypeIO // to hold data in ASCII format. // A class RemoteTapeIO could be developed for IO on a tape // device on another node. //
        //
        } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/IO/000077500000000000000000000000001476623553700145425ustar00rootroot00000000000000casacore-3.7.1/casa/IO/AipsIO.cc000066400000000000000000000573671476623553700162170ustar00rootroot00000000000000//# AipsIO.cc: AipsIO is the object persistency mechanism of Casacore //# Copyright (C) 1993,1994,1995,1996,1997,1998,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include //# for strcmp with gcc-4.3 #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // This is the implementation of the AipsIO class. // Operator << and >> for the built-in data types are inline functions // and defined in AipsIO.h. // Define the magic value used to check if the get of objects // is in synchronization with the file objetcs. const uInt AipsIO::magicval_p = 0xbebebebe; AipsIO::AipsIO() : opened_p (0), swput_p (-1), swget_p (-1), maxlev_p (10), objlen_p (10), objtln_p (10), objptr_p (10), hasCachedType_p(False) {} AipsIO::AipsIO (const String& fileName, ByteIO::OpenOption fop, uInt filebufSize, const std::shared_ptr& mfile) : opened_p (0), maxlev_p (10), objlen_p (10), objtln_p (10), objptr_p (10) { // Open the file. open (fileName, fop, filebufSize, mfile); } AipsIO::AipsIO (const std::shared_ptr& file) : opened_p (0), maxlev_p (10), objlen_p (10), objtln_p (10), objptr_p (10) { open (file); } AipsIO::AipsIO (const std::shared_ptr& file) : opened_p (0), maxlev_p (10), objlen_p (10), objtln_p (10), objptr_p (10) { open (file); } AipsIO::~AipsIO() { try { close(); } catch (...) {} } void AipsIO::open (const String& fileName, ByteIO::OpenOption fop, uInt filebufSize, const std::shared_ptr& mfile) { // Initialize everything for the open. openInit (fop); if (mfile) { file_p.reset (new MFFileIO (mfile, fileName, fopt_p)); } else { file_p.reset (new RegularFileIO (fileName, fopt_p, filebufSize)); } io_p.reset (new CanonicalIO (file_p)); seekable_p = True; opened_p = 1; } void AipsIO::open (const std::shared_ptr& file) { // Initialize everything for the open. openInit (ByteIO::New); file_p.reset(); io_p.reset (new CanonicalIO (file)); AlwaysAssert (io_p != 0, AipsError); seekable_p = io_p->isSeekable(); if (! io_p->isReadable()) { swget_p = -1; } if (! io_p->isWritable()) { swput_p = -1; } opened_p = 1; } void AipsIO::open (const std::shared_ptr& file) { // Initialize everything for the open. openInit (ByteIO::New); file_p.reset(); io_p = file; AlwaysAssert (io_p != 0, AipsError); seekable_p = io_p->isSeekable(); if (! io_p->isReadable()) { swget_p = -1; } if (! io_p->isWritable()) { swput_p = -1; } opened_p = -1; } // Only open if not already open. void AipsIO::openInit (ByteIO::OpenOption fop) { if (opened_p != 0) { throw (AipsError ("AipsIO: already open")); } hasCachedType_p = False; fopt_p = fop; swget_p = 0; swput_p = 0; level_p = 0; objtln_p[0] = 0xffffffff; // highest possible uInt value // Determine if put is possible. if (fopt_p == ByteIO::Old) { swput_p = -1; // put not possible } } // Close the file and delete filebuf buffer space. // Delete the file if required. void AipsIO::close() { io_p.reset(); file_p.reset();; opened_p = 0; swput_p = -1; swget_p = -1; hasCachedType_p = False; } // getpos allows you to get the position of an object in a file by // calling it before calling putstart. // setpos allows you to position on an object. // Note that these functions only return a valid result if a disk file // is used (it will not work for IPC). Int64 AipsIO::getpos() { return io_p->seek (0, ByteIO::Current); } Int64 AipsIO::setpos (Int64 pos) { if (level_p != 0) { throw (AipsError("AipsIO::setpos cannot be done while " "accessing objects")); } return io_p->seek (pos); } // The primitive put functions (operator <<) put one value at a time. // The value is converted to canonical format. // They test if a put is allowed; an exception is thrown if not. // The vector FromLocal functions are used to avoid align problems, // because the destination (the output buffer) can be non-aligned. AipsIO& AipsIO::operator<< (const Bool& var) { testput(); objlen_p[level_p] += io_p->write (1, &var); return (*this); } AipsIO& AipsIO::operator<< (const Char& var) { testput(); objlen_p[level_p] += io_p->write (1, &var); return (*this); } AipsIO& AipsIO::operator<< (const uChar& var) { testput(); objlen_p[level_p] += io_p->write (1, &var); return (*this); } AipsIO& AipsIO::operator<< (const short& var) { testput(); objlen_p[level_p] += io_p->write (1, &var); return (*this); } AipsIO& AipsIO::operator<< (const unsigned short& var) { testput(); objlen_p[level_p] += io_p->write (1, &var); return (*this); } AipsIO& AipsIO::operator<< (const int& var) { testput(); objlen_p[level_p] += io_p->write (1, &var); return (*this); } AipsIO& AipsIO::operator<< (const unsigned int& var) { testput(); objlen_p[level_p] += io_p->write (1, &var); return (*this); } AipsIO& AipsIO::operator<< (const Int64& var) { testput(); objlen_p[level_p] += io_p->write (1, &var); return (*this); } AipsIO& AipsIO::operator<< (const uInt64& var) { testput(); objlen_p[level_p] += io_p->write (1, &var); return (*this); } AipsIO& AipsIO::operator<< (const float& var) { testput(); objlen_p[level_p] += io_p->write (1, &var); return (*this); } AipsIO& AipsIO::operator<< (const double& var) { testput(); objlen_p[level_p] += io_p->write (1, &var); return (*this); } AipsIO& AipsIO::operator<< (const Complex& var) { testput(); objlen_p[level_p] += io_p->write (1, &var); return (*this); } AipsIO& AipsIO::operator<< (const DComplex& var) { testput(); objlen_p[level_p] += io_p->write (1, &var); return (*this); } AipsIO& AipsIO::operator<< (const String& var) { testput(); objlen_p[level_p] += io_p->write (1, &var); return (*this); } AipsIO& AipsIO::operator<< (const Char* var) { testput(); String str(var); objlen_p[level_p] += io_p->write (1, &str); return (*this); } // The following routines put an entire vector in one go. // They test if a put is allowed. // The data is stored in canonical format in an intermediate buffer AipsIO& AipsIO::put (uInt nrv, const Bool* var, Bool putNR) { testput(); if (putNR) { operator<< (nrv); // store #values } objlen_p[level_p] += io_p->write (nrv, var); return (*this); } AipsIO& AipsIO::put (uInt nrv, const Char* var, Bool putNR) { testput(); if (putNR) { operator<< (nrv); // store #values } objlen_p[level_p] += io_p->write (nrv, var); return (*this); } AipsIO& AipsIO::put (uInt nrv, const uChar* var, Bool putNR) { testput(); if (putNR) { operator<< (nrv); // store #values } objlen_p[level_p] += io_p->write (nrv, var); return (*this); } AipsIO& AipsIO::put (uInt nrv, const short* var, Bool putNR) { testput(); if (putNR) { operator<< (nrv); // store #values } objlen_p[level_p] += io_p->write (nrv, var); return (*this); } AipsIO& AipsIO::put (uInt nrv, const unsigned short* var, Bool putNR) { testput(); if (putNR) { operator<< (nrv); // store #values } objlen_p[level_p] += io_p->write (nrv, var); return (*this); } AipsIO& AipsIO::put (uInt nrv, const int* var, Bool putNR) { testput(); if (putNR) { operator<< (nrv); // store #values } objlen_p[level_p] += io_p->write (nrv, var); return (*this); } AipsIO& AipsIO::put (uInt nrv, const unsigned int* var, Bool putNR) { testput(); if (putNR) { operator<< (nrv); // store #values } objlen_p[level_p] += io_p->write (nrv, var); return (*this); } AipsIO& AipsIO::put (uInt nrv, const Int64* var, Bool putNR) { testput(); if (putNR) { operator<< (nrv); // store #values } objlen_p[level_p] += io_p->write (nrv, var); return (*this); } AipsIO& AipsIO::put (uInt nrv, const uInt64* var, Bool putNR) { testput(); if (putNR) { operator<< (nrv); // store #values } objlen_p[level_p] += io_p->write (nrv, var); return (*this); } AipsIO& AipsIO::put (uInt nrv, const float* var, Bool putNR) { testput(); if (putNR) { operator<< (nrv); // store #values } objlen_p[level_p] += io_p->write (nrv, var); return (*this); } AipsIO& AipsIO::put (uInt nrv, const double* var, Bool putNR) { testput(); if (putNR) { operator<< (nrv); // store #values } objlen_p[level_p] += io_p->write (nrv, var); return (*this); } AipsIO& AipsIO::put (uInt nrv, const Complex* var, Bool putNR) { testput(); if (putNR) { operator<< (nrv); // store #values } objlen_p[level_p] += io_p->write (nrv, var); return (*this); } AipsIO& AipsIO::put (uInt nrv, const DComplex* var, Bool putNR) { testput(); if (putNR) { operator<< (nrv); // store #values } objlen_p[level_p] += io_p->write (nrv, var); return (*this); } AipsIO& AipsIO::put (uInt nrv, const String* var, Bool putNR) { testput(); if (putNR) { operator<< (nrv); // store #values } for (uInt i=0; i& vec) { // std::vector uses bits instead of bytes. So copy first. Block var(vec.size()); std::copy (vec.begin(), vec.end(), var.begin()); put (var.size(), var.storage(), True); return *this; } // putstart starts writing an object. // This is not possible if there is no file, if the file is not opened // for output or if there is a get in operation. // If it is the root object, it initializes the dynamic buffers. // It puts the object type and version and reserves space for the length. // It increases the level for each object to hold the length. uInt AipsIO::putstart (const Char* type, uInt vers) { return (putstart (String(type), vers)); } uInt AipsIO::putstart (const String& type, uInt vers) { if (opened_p == 0 || swput_p < 0 || swget_p > 0) { throw (AipsError ("AipsIO::putstart: not open or not writable")); } if (level_p == 0) { swput_p = 1; // indicate putting is possible objlen_p[0] = 0; operator<< (magicval_p); // write magic value } level_p++; if (level_p >= maxlev_p) { maxlev_p += 10; // increase size of blocks objlen_p.resize (maxlev_p); objtln_p.resize (maxlev_p); objptr_p.resize (maxlev_p); } objlen_p[level_p] = 0; // initialize object length objptr_p[level_p] = getpos(); // remember where to put operator<< (magicval_p); operator<< (type); // write object type operator<< (vers); // write object version return level_p; } // putend ends putting an object. It decreases the level and writes // the object length if the file is seekable. uInt AipsIO::putend() { if (level_p == 0) { testputerr(); // no corresponding putstart } uInt len = objlen_p[level_p]; // object length if (seekable_p) { Int64 pos = getpos(); io_p->seek (objptr_p[level_p]); operator<< (objlen_p[level_p]); io_p->seek (pos); } level_p--; if (level_p == 0) { swput_p = 0; // putting is not possible anymore }else{ objlen_p[level_p] += len; // add length to parent object } return len; } // The primitive get functions (operator >>) get one value at a time. // They test if a get is allowed; an exception is thrown if not. // They convert from canonical to local format. // Note that the variables in the >> functions are always aligned, so // we can use the scalar ToLocal function which is doing a simple assign. AipsIO& AipsIO::operator>> (Bool& var) { testget(); objlen_p[level_p] += io_p->read (1, &var); testgetLength(); return (*this); } AipsIO& AipsIO::operator>> (Char& var) { testget(); objlen_p[level_p] += io_p->read (1, &var); testgetLength(); return (*this); } AipsIO& AipsIO::operator>> (uChar& var) { testget(); objlen_p[level_p] += io_p->read (1, &var); testgetLength(); return (*this); } AipsIO& AipsIO::operator>> (short& var) { testget(); objlen_p[level_p] += io_p->read (1, &var); testgetLength(); return (*this); } AipsIO& AipsIO::operator>> (unsigned short& var) { testget(); objlen_p[level_p] += io_p->read (1, &var); testgetLength(); return (*this); } AipsIO& AipsIO::operator>> (int& var) { testget(); objlen_p[level_p] += io_p->read (1, &var); testgetLength(); return (*this); } AipsIO& AipsIO::operator>> (unsigned int& var) { testget(); objlen_p[level_p] += io_p->read (1, &var); testgetLength(); return (*this); } AipsIO& AipsIO::operator>> (Int64& var) { testget(); objlen_p[level_p] += io_p->read (1, &var); testgetLength(); return (*this); } AipsIO& AipsIO::operator>> (uInt64& var) { testget(); objlen_p[level_p] += io_p->read (1, &var); testgetLength(); return (*this); } AipsIO& AipsIO::operator>> (float& var) { testget(); objlen_p[level_p] += io_p->read (1, &var); testgetLength(); return (*this); } AipsIO& AipsIO::operator>> (double& var) { testget(); objlen_p[level_p] += io_p->read (1, &var); testgetLength(); return (*this); } AipsIO& AipsIO::operator>> (Complex& var) { testget(); objlen_p[level_p] += io_p->read (1, &var); testgetLength(); return (*this); } AipsIO& AipsIO::operator>> (DComplex& var) { testget(); objlen_p[level_p] += io_p->read (1, &var); testgetLength(); return (*this); } AipsIO& AipsIO::operator>> (String& var) { testget(); objlen_p[level_p] += io_p->read (1, &var); testgetLength(); return (*this); } // The following routines get an entire vector in one go. // They convert from canonical to local format. // If the format sizes differ, some special has to be done. // The compiler will remove redundant code in that test. // They also test if a get is allowed. // The user has to supply the buffer and the given nr of values is read. AipsIO& AipsIO::get (uInt nrv, Bool* var) { testget(); objlen_p[level_p] += io_p->read (nrv, var); testgetLength(); return (*this); } AipsIO& AipsIO::get (uInt nrv, Char* var) { testget(); objlen_p[level_p] += io_p->read (nrv, var); testgetLength(); return (*this); } AipsIO& AipsIO::get (uInt nrv, uChar* var) { testget(); objlen_p[level_p] += io_p->read (nrv, var); testgetLength(); return (*this); } AipsIO& AipsIO::get (uInt nrv, short* var) { testget(); objlen_p[level_p] += io_p->read (nrv, var); testgetLength(); return (*this); } AipsIO& AipsIO::get (uInt nrv, unsigned short* var) { testget(); objlen_p[level_p] += io_p->read (nrv, var); testgetLength(); return (*this); } AipsIO& AipsIO::get (uInt nrv, int* var) { testget(); objlen_p[level_p] += io_p->read (nrv, var); testgetLength(); return (*this); } AipsIO& AipsIO::get (uInt nrv, unsigned int* var) { testget(); objlen_p[level_p] += io_p->read (nrv, var); testgetLength(); return (*this); } AipsIO& AipsIO::get (uInt nrv, Int64* var) { testget(); objlen_p[level_p] += io_p->read (nrv, var); testgetLength(); return (*this); } AipsIO& AipsIO::get (uInt nrv, uInt64* var) { testget(); objlen_p[level_p] += io_p->read (nrv, var); testgetLength(); return (*this); } AipsIO& AipsIO::get (uInt nrv, float* var) { testget(); objlen_p[level_p] += io_p->read (nrv, var); testgetLength(); return (*this); } AipsIO& AipsIO::get (uInt nrv, double* var) { testget(); objlen_p[level_p] += io_p->read (nrv, var); testgetLength(); return (*this); } AipsIO& AipsIO::get (uInt nrv, Complex* var) { testget(); objlen_p[level_p] += io_p->read (nrv, var); testgetLength(); return (*this); } AipsIO& AipsIO::get (uInt nrv, DComplex* var) { testget(); objlen_p[level_p] += io_p->read (nrv, var); testgetLength(); return (*this); } AipsIO& AipsIO::get (uInt nrv, String* var) { testget(); objlen_p[level_p] += io_p->read (nrv, var); testgetLength(); return (*this); } AipsIO& AipsIO::get (vector& vec) { uInt nrv; Bool* var; getnew (nrv, var); vec.resize (nrv); std::copy (var, var+nrv, vec.begin()); delete [] var; return *this; } // The following routines get an entire vector in one go. // The routine will allocate a buffer of the appropriate size. // It returns a pointer to that buffer and the nr of values read. AipsIO& AipsIO::getnew (uInt& nrv, Bool*& var) { operator>> (nrv); var = new Bool[nrv]; get (nrv, var); return (*this); } AipsIO& AipsIO::getnew (uInt& nrv, Char*& var) { operator>> (nrv); var = new Char[nrv]; get (nrv, var); return (*this); } AipsIO& AipsIO::getnew (uInt& nrv, uChar*& var) { operator>> (nrv); var = new uChar[nrv]; get (nrv, var); return (*this); } AipsIO& AipsIO::getnew (uInt& nrv, short*& var) { operator>> (nrv); var = new short[nrv]; get (nrv, var); return (*this); } AipsIO& AipsIO::getnew (uInt& nrv, unsigned short*& var) { operator>> (nrv); var = new unsigned short[nrv]; get (nrv, var); return (*this); } AipsIO& AipsIO::getnew (uInt& nrv, int*& var) { operator>> (nrv); var = new int[nrv]; get (nrv, var); return (*this); } AipsIO& AipsIO::getnew (uInt& nrv, unsigned int*& var) { operator>> (nrv); var = new unsigned int[nrv]; get (nrv, var); return (*this); } AipsIO& AipsIO::getnew (uInt& nrv, Int64*& var) { operator>> (nrv); var = new Int64[nrv]; get (nrv, var); return (*this); } AipsIO& AipsIO::getnew (uInt& nrv, uInt64*& var) { operator>> (nrv); var = new uInt64[nrv]; get (nrv, var); return (*this); } AipsIO& AipsIO::getnew (uInt& nrv, float*& var) { operator>> (nrv); var = new float[nrv]; get (nrv, var); return (*this); } AipsIO& AipsIO::getnew (uInt& nrv, double*& var) { operator>> (nrv); var = new double[nrv]; get (nrv, var); return (*this); } AipsIO& AipsIO::getnew (uInt& nrv, Complex*& var) { operator>> (nrv); var = new Complex[nrv]; get (nrv, var); return (*this); } AipsIO& AipsIO::getnew (uInt& nrv, DComplex*& var) { operator>> (nrv); var = new DComplex[nrv]; get (nrv, var); return (*this); } AipsIO& AipsIO::getnew (uInt& nrv, String*& var) { operator>> (nrv); var = new String[nrv]; get (nrv, var); return (*this); } // getNextType gets the object type of the next piece of // information to read. It can only be used if a file has been // opened and if no put is in operation. // It checks if it finds the correct magic value preceeding // the object type. const String& AipsIO::getNextType() { if (opened_p == 0 || swget_p < 0 || swput_p > 0) { String message; if (file_p) message = file_p->fileName() + " - "; throw (AipsError ("AipsIO::getNextType: " + message + "not opened or not readable")); } if (hasCachedType_p) { return objectType_p; } uInt swgetOld = swget_p; uInt mval; if (level_p == 0) { swget_p = 1; // getting is possible (temporarily) objlen_p[0] = 0; // length already read operator>> (mval); if (mval != magicval_p) { String message; if (file_p) message = file_p->fileName() + " - "; throw (AipsError ("AipsIO::getNextType: " + message + "no magic value found")); } } level_p++; if (level_p >= maxlev_p) { maxlev_p += 10; objlen_p.resize (maxlev_p); objtln_p.resize (maxlev_p); objptr_p.resize (maxlev_p); } objlen_p[level_p] = 0; // length already read objtln_p[level_p] = 16; // to satisfy test in read operator>> (objtln_p[level_p]); // total object length to read operator>> (objectType_p); // object type // Getting may not be possible till getstart has been done. swget_p = swgetOld; hasCachedType_p = True; return objectType_p; } // getstart starts reading an object. // It checks the object type and returns the version. // getend ends reading an object. It decreases the level and removes // possible DynBuffer buffers (although they should not be present). // It checks if the entire object has been read. uInt AipsIO::getstart (const String& type) { return (getstart (type.chars())); } uInt AipsIO::getstart (const Char* type) { uInt vers; if (strcmp (type, getNextType().chars()) != 0) { throw (AipsError ("AipsIO::getstart: found object type " + getNextType() + ", expected " + type)); } swget_p = 1; // getting is possible now hasCachedType_p = False; // type is not cached anymore operator>> (vers); // read object version return vers; } uInt AipsIO::getend() { if (level_p > 0) { uInt len = objlen_p[level_p]; // length of object read if (len != objtln_p[level_p] && objtln_p[level_p] != magicval_p) { throw (AipsError ("AipsIO::getend: part of object not read")); } if (--level_p == 0) { swget_p = 0; // reading not possible anymore }else{ objlen_p[level_p] += len; // increase length read of parent } return len; }else{ testgeterr(); // no corresponding getstart return 0; } } // Throw errors on behalf of testput and testget. // testget and testput are inline and it would be a bit expensive to // get these (expanded) statements in all instances. void AipsIO::testputerr() { throw (AipsError ("AipsIO: no putstart done")); } void AipsIO::testgeterr() { throw (AipsError ("AipsIO: no getstart done")); } void AipsIO::testgeterrLength() { throw (AipsError ("AipsIO: read beyond end of object")); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/IO/AipsIO.h000066400000000000000000000451611476623553700160460ustar00rootroot00000000000000//# AipsIO.h: AipsIO is the object persistency mechanism of Casacore //# Copyright (C) 1993,1994,1995,1996,1998,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_AIPSIO_H #define CASA_AIPSIO_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TypeIO; class ByteIO; class RegularFileIO; class MultiFileBase; // // AipsIO is the object persistency mechanism of Casacore // // // // // AipsIO is simply the conventional shorthand for "AIPS++ input/output". // Note that Casacore is the successor of the old AIPS++ project. // // // AipsIO is a class designed to do I/O for objects. // It reads/writes the data using a class derived from // TypeIO. For instance, class // CanonicalIO can be used // to read/write the data in canonical format. //

        // The TypeIO class in its turn uses a class derived from // ByteIO to determine where the data // has to be written. //

        // An object is written by writing all its data members. It will be // preceeded by a header containing type and version. // The I/O can be done via de overloaded << and >> operators to write or // read a single item (e.g., an int or an object). These operators are // already defined for all built-in data types and for Complex, DComplex, // String, and Bool. // Since each enumeration is a specific type, it is hard to handle them. // Casting to Bool (which is also an enumerated type) is a possibility, // but that assumes that each enumerated type has the same size (which // is probably true for all compilers). // Another possibility is to store it in an int when writing. Reading // can be done the opposite way, although the ARM says that an int // cannot be assigned to an enumerated type. //

        // There are also functions put, get and getnew to write or read an // array of values. These functions are defined for the same data types // as << and >> (so one can write, for example, an array of Strings). // AipsIO.put (nr, arr) writes nr values from the given array. // AipsIO.get (nr, arr) reads nr values into the given user-supplied array. // AipsIO.getnew (&nr, &arr) reads the number of values written into // a new array allocated on the heap. It returns the nr of values read // and a pointer to the array. // The data must be read back in the same order as it was written. //

        // The functions putstart(type,version) // and putend() must be called // before resp. after writing all values of the object. // It stores the given type and version of the object. // Similarly getstart(type) and getend() must be called. // getstart checks the type and returns the version. By using the version // the read function of the object can convert older versions of the // object (which may still reside on disk) to the latest version. // The function getNextType is available to get the type of the next // object stored. This can be used to check the type or to open (i.e. // issue a getstart) in the correct way. //

        // When implementing a class, one should also define the operators << and >> // for the class to allow users to write or read an object in this // simple way (e.g., as io >> obj; to read an object). // One has to define the friend functions: // // friend AipsIO& operator<< (AipsIO&, const YourClass&); // friend AipsIO& operator>> (AipsIO&, YourClass&); // // since they cannot be stored in the class itself. // The type of an object is usually passed as the class name. // // AipsIO& operator<< (AipsIO& ios, const YourClass& object) { // ios.putstart ("YourClass", version); // ios << ....; // ios.putend (); // } // // // The functions getpos() and setpos(offset) can be used to get and set // the offset in the file to a given point. They can be used to point // to a position in the file where an object must be written or read. // Obviously these functions are to be used by a storage manager and // are not for public use. Someday they should be made private with // a friend defined. // // // // MyClass myObject(...); // some object // AipsIO ios("file.name", ByteIO::New); // create new file // ios << myObject; // write object // MyClass myObject2; // ios >> myObject2; // read it back // // This example creates an object, writes it to AipsIO and reads it // back into another object. // The shift functions for MyClass could be defined as follows: // // AipsIO& operator<< (AipsIO& ios, const MyClass& myObject) // { // ios.putstart ("MyClass", 1); // MyClass version 1 // ios << ...; // write all data members // ios.putend(); // } // AipsIO& operator>> (AipsIO& ios, const MyClass& myObject) // { // // If needed, delete current data members first. // // Now read in the object. // uInt version = ios.getstart ("MyClass"); // ios >> ...; // read all data members // ios.getend(); // } // // In this example the version is not used. In more complex objects // it will probably be used when data members get added or changed // in future versions of a software system. // class AipsIO { public: // No file attached yet AipsIO(); // Construct and open/create a file with the given name. // The actual IO is done via a CanonicalIO object on a regular file // using buffered IO with a buffer of the given size. //
        If the MultiFileBase pointer is not null, a virtual file in the // MultiFileBase will be used instead of a regular file. explicit AipsIO (const String& fileName, ByteIO::OpenOption = ByteIO::Old, uInt filebufSize=65536, const std::shared_ptr& = std::shared_ptr()); // Construct from a stream object derived from ByteIO. // This can for instance by used to use AipsIO on a file descriptor // for which a FilebufIO // object has been created. // The actual IO is done via a CanonicalIO object on top of it. explicit AipsIO (const std::shared_ptr&); // Construct from a stream object derived from TypeIO, thus from // a stream on top of ByteIOn doing the possible conversions. explicit AipsIO (const std::shared_ptr&); // Close if not done yet ~AipsIO(); // Copy constructor and assignment are not allowed. AipsIO (const AipsIO&) = delete; AipsIO& operator= (const AipsIO&) = delete; // Open/create file (either a regular file or a MultiFileBase virtual file). // An exception is thrown if the object contains an already open file. void open (const String& fileName, ByteIO::OpenOption = ByteIO::Old, uInt filebufSize=65536, const std::shared_ptr& = std::shared_ptr()); // Open by connecting to the given byte stream. // This can for instance by used to use AipsIO on a file descriptor // for which a FilebufIO // object has been created. // The actual IO is done via a CanonicalIO object on top of it. // An exception is thrown if the object contains an already open file. void open (const std::shared_ptr&); // Open by connecting to the given typed byte stream. // An exception is thrown if the object contains an already open file. void open (const std::shared_ptr&); // Close file opened void close(); // Return the file option. ByteIO::OpenOption fileOption() const; // Start putting an object. // This writes the object type and version. When reading back getstart // calls have to be done in the same way. Getstart // checks the type and returns the version. The user can use that to // correctly read back objects with different versions. //
        // Data in the outermost object cannot be put before a putstart is done. // Data in nested objects can be put without an intermediate putstart. // However, for complex objects it is recommended to do a putstart // to have a better checking. //
        // After all values (inclusing nested objects) of the object have // been put, a call to putend has to be done. // uInt putstart (const String& objectType, uInt objectVersion); uInt putstart (const Char* objectType, uInt objectVersion); // // Put a single value. // AipsIO& operator<< (const Bool& value); AipsIO& operator<< (const Char& value); AipsIO& operator<< (const uChar& value); AipsIO& operator<< (const short& value); AipsIO& operator<< (const unsigned short& value); AipsIO& operator<< (const int& value); AipsIO& operator<< (const unsigned int& value); AipsIO& operator<< (const Int64& value); AipsIO& operator<< (const uInt64& value); AipsIO& operator<< (const float& value); AipsIO& operator<< (const double& value); AipsIO& operator<< (const Complex& value); AipsIO& operator<< (const DComplex& value); AipsIO& operator<< (const String& value); AipsIO& operator<< (const Char* value); // // Put an array of values with the given number of values. // If the flag putNr is set, the number of values is put first. // AipsIO& put (uInt nrval, const Bool* values, Bool putNR = True); AipsIO& put (uInt nrval, const Char* values, Bool putNR = True); AipsIO& put (uInt nrval, const uChar* values, Bool putNR = True); AipsIO& put (uInt nrval, const short* values, Bool putNR = True); AipsIO& put (uInt nrval, const unsigned short* values, Bool putNR = True); AipsIO& put (uInt nrval, const int* values, Bool putNR = True); AipsIO& put (uInt nrval, const unsigned int* values, Bool putNR = True); AipsIO& put (uInt nrval, const Int64* values, Bool putNR = True); AipsIO& put (uInt nrval, const uInt64* values, Bool putNR = True); AipsIO& put (uInt nrval, const float* values, Bool putNR = True); AipsIO& put (uInt nrval, const double* values, Bool putNR = True); AipsIO& put (uInt nrval, const Complex* values, Bool putNR = True); AipsIO& put (uInt nrval, const DComplex* values, Bool putNR = True); AipsIO& put (uInt nrval, const String* values, Bool putNR = True); // // Put a vector as an array of values // For standard types it has the same result as put with putNR=True. template AipsIO& put (const vector& vec) { *this << uInt(vec.size()); for (typename vector::const_iterator iter=vec.begin(); iter!=vec.end(); ++iter) { *this << *iter; } return *this; } //# Possibly specialize for standard types to make it faster. //# Specialize for a bool vector. AipsIO& put (const vector& vec); // End putting an object. It returns the object length (including // possible nested objects). uInt putend(); // Get and set file-offset. // Int64 getpos (); Int64 setpos (Int64 offset); // // Get the type of the next object stored. // This is not possible if a put is in progress. const String& getNextType(); // Start reading an object. It will check if the given type matches // the one stored by putstart. It returns the object version which // can be used to read in older version of the object correctly. //
        // After all values (inclusing nested objects) of the object have // been read, a call to getend has to be done. // uInt getstart (const String& objectType); uInt getstart (const Char* objectType); // // Get a single value. // AipsIO& operator>> (Bool& value); AipsIO& operator>> (Char& value); AipsIO& operator>> (uChar& value); AipsIO& operator>> (short& value); AipsIO& operator>> (unsigned short& value); AipsIO& operator>> (int& value); AipsIO& operator>> (unsigned int& value); AipsIO& operator>> (Int64& value); AipsIO& operator>> (uInt64& value); AipsIO& operator>> (float& value); AipsIO& operator>> (double& value); AipsIO& operator>> (Complex& value); AipsIO& operator>> (DComplex& value); AipsIO& operator>> (String& value); // // Read in nrval values into the user-supplied values buffer. // The buffer must be long enough. // AipsIO& get (uInt nrval, Bool* values); AipsIO& get (uInt nrval, Char* values); AipsIO& get (uInt nrval, uChar* values); AipsIO& get (uInt nrval, short* values); AipsIO& get (uInt nrval, unsigned short* values); AipsIO& get (uInt nrval, int* values); AipsIO& get (uInt nrval, unsigned int* values); AipsIO& get (uInt nrval, Int64* values); AipsIO& get (uInt nrval, uInt64* values); AipsIO& get (uInt nrval, float* values); AipsIO& get (uInt nrval, double* values); AipsIO& get (uInt nrval, Complex* values); AipsIO& get (uInt nrval, DComplex* values); AipsIO& get (uInt nrval, String* values); // // Get a vector as an array of values (similar to getnew). // It resizes the vector as needed. template AipsIO& get (vector& vec) { uInt sz; *this >> sz; vec.resize(sz); for (typename vector::iterator iter=vec.begin(); iter!=vec.end(); ++iter) { *this >> *iter; } return *this; } //# Specialize for a bool vector. AipsIO& get (vector& vec); // Read in values as written by the function put. // It will read the number of values (into nrval), allocate a // values buffer of that length and read the values into that buffer. // A pointer to the buffer is returned into values. // Although the buffer is allocated by this function, // the user has to delete it (using delete [] values;). // AipsIO& getnew (uInt& nrval, Bool*& values); AipsIO& getnew (uInt& nrval, Char*& values); AipsIO& getnew (uInt& nrval, uChar*& values); AipsIO& getnew (uInt& nrval, short*& values); AipsIO& getnew (uInt& nrval, unsigned short*& values); AipsIO& getnew (uInt& nrval, int*& values); AipsIO& getnew (uInt& nrval, unsigned int*& values); AipsIO& getnew (uInt& nrval, Int64*& values); AipsIO& getnew (uInt& nrval, uInt64*& values); AipsIO& getnew (uInt& nrval, float*& values); AipsIO& getnew (uInt& nrval, double*& values); AipsIO& getnew (uInt& nrval, Complex*& values); AipsIO& getnew (uInt& nrval, DComplex*& values); AipsIO& getnew (uInt& nrval, String*& values); // // End reading an object. It returns the object length (including // possible nested objects). // It checks if the entire object has been read (to keep the data // stream in sync). If not, an exception is thrown. uInt getend(); private: // Initialize everything for the open. // It checks if there is no outstanding open file. void openInit (ByteIO::OpenOption); // Test if put is possible (throw exception if not). void testput(); // Test if get is possible (throw exception if not). void testget(); // Test if get did not exceed object. void testgetLength(); // Throw exception for testput void testputerr(); // Throw exception for testget void testgeterr(); // Throw exception for testgetLength void testgeterrLength(); // 1 = file was opened by AipsIO // 0 = file not opened // -1 = file opened by user (=fd passed) Int opened_p; // File open option ByteIO::OpenOption fopt_p; // <0 = not opened for put // 0 = no putstart done // >0 = put is possible int swput_p; // <0 = not opened for get // 0 = no getstart done // >0 = get is possible int swget_p; // Nested object level uInt level_p; // Current size of objlen and objptr uInt maxlev_p; // Object length at each level Block objlen_p; // Object length to be read at each level Block objtln_p; // Offset of length at each level Block objptr_p; // True = the object type has already been read Bool hasCachedType_p; // The cached object type. String objectType_p; // The file object. std::shared_ptr file_p; // The actual IO object. std::shared_ptr io_p; // Is the file is seekable? Bool seekable_p; // magic value to check sync. static const uInt magicval_p; }; // Return the file option. inline ByteIO::OpenOption AipsIO::fileOption() const { return fopt_p; } // testput tests if a put can be done; ie. if putstart has been done. // It throws an exception if not. // testget is similar to test if a get can be done. inline void AipsIO::testput() { if (swput_p <= 0) { testputerr(); } } inline void AipsIO::testget() { if (swget_p <= 0) { testgeterr(); } } inline void AipsIO::testgetLength() { if (objlen_p[level_p] > objtln_p[level_p]) { testgeterrLength(); } } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/IO/AipsIOCarray.h000066400000000000000000000115701476623553700172050ustar00rootroot00000000000000//# AipsIOCarray.h: Templated functions to get/put a C-array from/into AipsIO. //# Copyright (C) 1993,1994,1995,1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_AIPSIOCARRAY_H #define CASA_AIPSIOCARRAY_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //

        // Templated functions to get/put a C-style array from/into AipsIO. // // // // //
      • AipsIO // // // AipsIOCarray is simply the conventional shorthand for "aips input/output for // C-style arrays". // // // This file declares templated functions to get or put a C-style array // of any data type from/into AipsIO. // These functions are similar to the AipsIO functions put, get and getnew, // but support any data type. // // Specializations (using these AipsIO functions) are made for // the standard data types. These are much more efficient. // // // // // Write an C-style array of type A into AipsIO. // // This will first write the number of elements. // { // A ap[1000]; // AipsIO io ("file.data", ByteIO::New); // io.putstart ("some",1); // putAipsIO (io, uInt(1000), ap); // io.putend(); // } // // Read the data back into a preallocated array. // // First the number of elements have to be read. // { // A api[1000]; // uInt n; // AipsIO io ("file.data"); // io.getstart ("some"); // io >> n; // getAipsIO (io, n, api); // } // // Read the data back into an automatically allocated array. // // This will also read the number of elements. // // Delete the allocated array at the end. // { // A* api; // uInt n; // AipsIO io ("file.data"); // io.getstart ("some"); // getnewAipsIO (io, n, &api); // delete [] api; // } // // // // Put a C-style array of n elements. // First the number of elements is put, thereafter all values. template void putAipsIO (AipsIO& aios, uInt n, const T* data); // Get n elements into an already available C-style array. // The data buffer must be large enough to hold n values. template void getAipsIO (AipsIO& aios, uInt n, T* data); // Get elements into a C-style array to be allocated on the heap. // First the number of elements will be read. The array will be allocated // by this function and must be freed by the user. Its pointer is returned // in data. The number of elements is returned in n. // // // Unfortunately the CFront compiler (and maybe others as well) fail to // overload on T*& data iso. T** data. // template void getnewAipsIO (AipsIO& aios, uInt& n, T** data); // //# Specializations for the builtin data types. #define AIPSIO_FUNC_SPEC(T) \ inline void putAipsIO (AipsIO& aios, uInt n, const T* data) \ { aios.put (n, data); } \ inline void getAipsIO (AipsIO& aios, uInt n, T* data) \ { aios.get (n, data); } \ inline void getnewAipsIO (AipsIO& aios, uInt& n, T** data) \ { aios.getnew (n, *data); } //# These macros expand to generate the appropriate inline functions //# for the built-in data types. AIPSIO_FUNC_SPEC(Bool) AIPSIO_FUNC_SPEC(Char) AIPSIO_FUNC_SPEC(uChar) AIPSIO_FUNC_SPEC(short) AIPSIO_FUNC_SPEC(unsigned short) AIPSIO_FUNC_SPEC(int) AIPSIO_FUNC_SPEC(unsigned int) AIPSIO_FUNC_SPEC(Int64) AIPSIO_FUNC_SPEC(uInt64) AIPSIO_FUNC_SPEC(float) AIPSIO_FUNC_SPEC(double) AIPSIO_FUNC_SPEC(Complex) AIPSIO_FUNC_SPEC(DComplex) AIPSIO_FUNC_SPEC(String) } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/casa/IO/AipsIOCarray.tcc000066400000000000000000000041051476623553700175230ustar00rootroot00000000000000//# AipsIOCarray.cc: Templated functions to get/put a C-array from/into AipsIO. //# Copyright (C) 1993,1994,1995,1996,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_AIPSIOCARRAY_TCC #define CASA_AIPSIOCARRAY_TCC #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Put a C-array of n elements. template void putAipsIO (AipsIO& ios, uInt n, const T* data) { ios << n; for (uInt i=0; i void getAipsIO (AipsIO& ios, uInt n, T* data) { for (uInt i=0; i> *data++; } } // Get elements into a C-array to be allocated on the heap. // The number of elements will also be returned. template void getnewAipsIO (AipsIO& ios, uInt& n, T** data) { ios >> n; *data = new T[n]; getAipsIO (ios, n, *data); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/IO/ArrayIO.h000066400000000000000000000161511476623553700162250ustar00rootroot00000000000000//# ArrayIO.h: text output and binary IO for an array of any dimensionality. //# Copyright (C) 1993,1994,1995,1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# charlottesville, VA 22903-2475 USA #ifndef CASA_ARRAYIO_2_H #define CASA_ARRAYIO_2_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class AipsIO; class LogIO; class IPosition; template class Block; class String; // // Input/output operators for Arrays. // // // // This header was reviewed and revised with the goal of making it an // example for those writing global function header files. // // //
      • Array //
      • ostream //
      • AipsIO // // // ArrayIO is simply the conventional shorthand for "array input/output". // // // These global functions provide easy input and output of (possibly) // large and (possibly) multi-dimensional arrays. Iteration through // entire arrays is done behind the scenes, with no effort required // of the client programmer. // These functions are global, rather than member functions of the // Array class, because of the well-known C++ requirement that the first // argument to an operator function (as it is declared) is the // left operand when the function is called. // // // // IPosition shape (3,10,10,3); // Array array (shape); // // ...initialize and manipulate the array... // cout << "result: " << array; // // // // Effortless input/output is clearly a big win. // // // // // // Array IO -- Input/output operators for Arrays. // // // // Write a formatted copy of the array to the LogIO output object. Merely calls // the ostream operator<< in turn. template LogIO &operator<<(LogIO &os, const Array &a); // Read or write a binary representation of an Array to a file. Very // useful for saving arrays and restoring them later. //
        The putArray function is put in for forwards compatibility // of images (so new images can be read with old release). // // template AipsIO &operator<< (AipsIO &, const Array &); template void putArray (AipsIO &, const Array &, const char* name); template AipsIO &operator>> (AipsIO &, Array &); // //
        // // Global functions to read/write binary arrays from/to a file. // // // // // // These global functions provide disk read/write functions for an Array of // binary numbers. The write operation is useful, for example, to dump an // image in binary form to disk so that it can be displayed with an external // utility such as SAOimage. // // // // Matrix picture(256, 256); picture = 0.0; // String fileName="picture.data"; // // // operations to populate picture // // ... // // write_array (picture, fileName); // // // //
      • These functions should eventually be replaced with something // more sophisticated. // // // Array binary IO -- Simple binary input/output for Arrays. // // // Write the values of an array in binary format into a file with // the given name. // The values are stored in local format, thus are not converted // to a canonical format as // AipsIO // does. // // This function is only suitable for built-in data types. // // template void write_array (const Array& the_array, const std::string& fileName); template inline void write_array (const Array& the_array, const char* fileName) { write_array (the_array, std::string(fileName)); } // // Read the values of an array in binary format from a file with // the given name. // The number of values read is the size of the Array, thus the file // should at least contain that number of values. // // This function is only suitable for built-in data types. // // template void read_array (Array& the_array, const std::string& fileName); template inline void read_array (Array& the_array, const char* fileName) { read_array (the_array, std::string(fileName)); } // // // These two functions read and write a Vector of data. The input // may be arranged in any format (i.e. It may be recorded as one value per // line or it may be recorded with all values on a single line). // Values must be separated by whitespace. // template void readAsciiVector (Vector& vec, const char* fileName); template void writeAsciiVector (const Vector& vec, const char* fileName); // // AipsIO& operator<< (AipsIO& aio, const IPosition& ip); AipsIO& operator>> (AipsIO& aio, IPosition& ip); LogIO& operator<< (LogIO& os, const IPosition& ip); template Block makeBlock(const Array& array); template Vector makeVector(const Block& block); Vector stringToVector (const String& string, char delim = ','); Vector stringToVector (const String& string, const std::regex& delim); } //# NAMESPACE CASACORE - END #include #endif casacore-3.7.1/casa/IO/ArrayIO.tcc000066400000000000000000000117721476623553700165530ustar00rootroot00000000000000//# ArrayIO.cc: text output and binary IO for an array of any dimensionality. //# Copyright (C) 1993,1994,1995,1996,1997,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_ARRAYIO_2_TCC #define CASA_ARRAYIO_2_TCC //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template LogIO &operator<<(LogIO &os, const Array &a) { os.output() << a; return os; } template AipsIO &operator<<(AipsIO &ios, const Array &a) { putArray (ios, a, "Array"); return ios; } template void putArray (AipsIO &ios, const Array &a, const Char* name) { if (a.size() * sizeof(T) > 2147483647) { throw AipsError("AipsIO putArray too large (exceeds 2**31 bytes)"); } ios.putstart(name, Array::arrayVersion()); // Write out dimensionality ios << uInt(a.ndim()); // Write out length for (size_t i=0; i < a.ndim(); i++) { ios << uInt(a.shape()(i)); } // Now write out the data bool deleteIt; const T *storage = a.getStorage(deleteIt); putAipsIO (ios, a.nelements(), storage); a.freeStorage(storage, deleteIt); ios.putend(); } // // ArrayError // template AipsIO &operator>>(AipsIO &ios, Array &a) { // Makes the argument unique, i.e. existing refs will be lost. At the moment // this is relatively inefficient as it makes a temporary copy. a.unique(); // On 20-Nov-2000 use of the home-brew rtti was removed. // It meant that a name 'Array' is now replaced by 'Array'. // In order to recognize those old names, we must do something special. String type = ios.getNextType(); int version; if (type.length() > 6 && type.index("Array<") == 0) { version = ios.getstart(type); } else { version = ios.getstart("Array"); } int ndim; ios >> ndim; IPosition shape(ndim); // Older versions contain an origin (which we discard). if (version < 3) { int orig; for (int i=0; i < ndim; i++) { ios >> orig; } } uInt v; for (int i=0; i < ndim; i++) { ios >> v; shape(i) = v; } a.resize(shape); // hopefully a no-op if unchanged // Now read in the data. bool deleteIt; T *storage = a.getStorage(deleteIt); uInt nwritten; ios >> nwritten; if (nwritten != a.nelements()) throw(ArrayError("AipsIO &operator>>(AipsIO, Array" " - nelements() differs from number in file")); getAipsIO (ios, nwritten, storage); a.putStorage(storage, deleteIt); ios.getend(); return ios; } inline LogIO& operator<< (LogIO& os, const IPosition& ip) { os.output() << ip; return os; } template Block makeBlock(const Array& array) { Block block(array.nelements()); if(array.contiguousStorage()) std::copy(array.cbegin(), array.cend(), block.storage()); else std::copy(array.begin(), array.end(), block.storage()); return block; } template Vector makeVector(const Block& block) { return Vector(block.begin(), block.end()); } inline Vector stringToVector (const String& string, char delim) { Vector vec = strToVector(string, delim); return Vector(vec.begin(), vec.end()); } inline Vector stringToVector (const String& string, const std::regex& delim) { Vector vec = strToVector(string, delim); return Vector(vec.begin(), vec.end()); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/IO/BaseSinkSource.cc000066400000000000000000000045611476623553700177370ustar00rootroot00000000000000//# BaseSinkSource.cc: Shared base class for ByteSink and ByteSource //# Copyright (C) 1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN BaseSinkSource::BaseSinkSource() : itsTypeIO () {} BaseSinkSource::BaseSinkSource (const std::shared_ptr& typeIO) : itsTypeIO (typeIO) {} BaseSinkSource::BaseSinkSource (const BaseSinkSource& sinkSource) : itsTypeIO (sinkSource.itsTypeIO) {} BaseSinkSource& BaseSinkSource::operator= (const BaseSinkSource& sinkSource) { if (this != &sinkSource) { itsTypeIO = sinkSource.itsTypeIO; } return *this; } BaseSinkSource::~BaseSinkSource() {} Int64 BaseSinkSource::seek (Int64 offset, ByteIO::SeekOption option) { return itsTypeIO->seek (offset, option); } Int64 BaseSinkSource::seek (Int offset, ByteIO::SeekOption option) { return itsTypeIO->seek (offset, option); } Bool BaseSinkSource::isReadable() const { return itsTypeIO->isReadable(); } Bool BaseSinkSource::isWritable() const { return itsTypeIO->isWritable(); } Bool BaseSinkSource::isSeekable() const { return itsTypeIO->isSeekable(); } Bool BaseSinkSource::isNull() const { return !itsTypeIO; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/IO/BaseSinkSource.h000066400000000000000000000113621476623553700175760ustar00rootroot00000000000000//# BaseSinkSource.h: Shared base class for ByteSink and ByteSource //# Copyright (C) 1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_BASESINKSOURCE_H #define CASA_BASESINKSOURCE_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Shared base class for ByteSink and ByteSource. // // //
      • TypeIO class and derived classes // // // // // This class provides the common functionality for the classes // ByteSink and // ByteSource. //

        // The object is constructed using a typed byte stream. This stream // is an instance of a class derived from class // TypeIO. This makes it possible to // store the data in any format (e.g. CanonicalIO or RawIO). // Class CanonicalIO makes it // possible to store the data in a canonical (machine-independent) format, // so it can be read on any machine and operating system. The canonical // format is big-endian IEEE, where a (unsigned) long is stored as 8 bytes. // This means that on common 32-bit big-endian machines like SUN and HP // only longs have to be converted and that CanonicalIO is as fast as RawIO. // Class RawIO stores the data in native // format, so the IO-process is faster on especially little-endian // machines (PC, DEC-alpha). Note that RawIO can also be used to read // bytes and interprete or convert them thereafter (e.g. using the // conversion functions in the Conversion // Conversion framework. //

        // In its turn TypeIO uses an instance of a class derived from class // ByteIO. This makes it possible to // use any output stream (e.g. file, memory). // // // The design of the ByteSink and ByteSource classes resembles the design of // the iostream classes in the standard library. A shared base class is needed // to allow multiple inheritance needed for class ByteSinkSource. // class BaseSinkSource { public: // This functions returns the shared pointer to itsTypeIO. const std::shared_ptr& typeIO() const { return itsTypeIO; } // This function sets the position on the given offset. // The seek option defines from which position the seek is done. // Int64 seek (Int64 offset, ByteIO::SeekOption = ByteIO::Begin); Int64 seek (Int offset, ByteIO::SeekOption = ByteIO::Begin); // // Is the SinkSource readable? Bool isReadable() const; // Is the SinkSource writable? Bool isWritable() const; // Is the SinkSource seekable? Bool isSeekable() const; // Is the BaseSinkSource unusable? Bool isNull() const; protected: BaseSinkSource(); // Construct using the given TypeIO. // The constructor does not copy the object, but only keeps a pointer to it. BaseSinkSource (const std::shared_ptr& typeIO); // The copy constructor uses reference semantics BaseSinkSource (const BaseSinkSource& BaseSinkSource); // The assignment operator uses reference semantics BaseSinkSource& operator= (const BaseSinkSource& BaseSinkSource); virtual ~BaseSinkSource(); // This variable keeps a pointer to a TypeIO. std::shared_ptr itsTypeIO; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/IO/BucketBase.cc000066400000000000000000000056611476623553700170710ustar00rootroot00000000000000//# BucketBase.cc: Abstract base class for Bucket classes //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN BucketBase::BucketBase (BucketFile* file, Int64 startOffset, uInt bucketSize, uInt nrOfBuckets) : itsFile (file), itsStartOffset (startOffset), itsBucketSize (bucketSize), itsCurNrOfBuckets (0), itsNewNrOfBuckets (nrOfBuckets), itsHasWritten (False) { // The bucketsize must be set. if (bucketSize == 0) { throw AipsError ("BucketBase::BucketBase; bucketsize=0"); } // Open the file if not open yet and get its physical size. // Use that to determine the number of buckets in the file. itsFile->open(); Int64 size = itsFile->fileSize(); if (size > startOffset) { itsCurNrOfBuckets = (size - startOffset) / bucketSize; if (itsCurNrOfBuckets > itsNewNrOfBuckets) { itsCurNrOfBuckets = itsNewNrOfBuckets; } } } BucketBase::~BucketBase() {} Bool BucketBase::flush() { if (itsNewNrOfBuckets > 0) { initializeBuckets (itsNewNrOfBuckets - 1); } if (itsHasWritten) { doFlush(); itsHasWritten = False; return True; } return False; } void BucketBase::resync (uInt nrBucket) { // Remap the file (if extended). if (nrBucket > itsNewNrOfBuckets) { doResync(); itsNewNrOfBuckets = nrBucket; } itsCurNrOfBuckets = nrBucket; } void BucketBase::extend (uInt nrBucket) { // Extend the file by writing the last byte. if (nrBucket > 0) { itsNewNrOfBuckets += nrBucket; doExtend (nrBucket); itsHasWritten = True; } } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/IO/BucketBase.h000066400000000000000000000105601476623553700167250ustar00rootroot00000000000000//# BucketBase.h: Abstract base class for Bucket classes //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_BUCKETBASE_H #define CASA_BUCKETBASE_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //

        // Abstract base class for Bucket classes. // // // // // //# Classes you should understand before using this one. //
      • BucketFile // // // BucketBase is the abstract baseclass for the various Bucket classes // like BucketMapped and BucketBuffered. // It is used by TSMCube to do the IO in the required way. // class BucketBase { public: // Create the bucket access for (part of) a file. // The file part starts at startOffset. Its length is // bucketSize*nrOfBuckets bytes. // If the file is smaller, the remainder is indicated as an extension // similarly to the behaviour of function extend. BucketBase (BucketFile* file, Int64 startOffset, uInt bucketSize, uInt nrOfBuckets); // Detach the file. The BucketFile is not closed. virtual ~BucketBase(); // Flush the cached buckets. // Possibly remaining uninitialized buckets will be initialized first. // A True status is returned if buckets had to be written. // The actual flushing is done using doFlush in the derived // class. Bool flush(); // Resynchronize the object (after another process updated the file). // It remaps the file if the nr of buckets has changed. // the new sizes. virtual void resync (uInt nrBucket); // Get the current nr of buckets in the file. uInt nBucket() const { return itsCurNrOfBuckets; } // Extend the file with the given number of buckets. // The buckets get initialized when they are acquired // (using getBucket) for the first time. void extend (uInt nrBucket); // Set that data has been written. void setWritten() { itsHasWritten = True; } protected: // Copy constructor is not possible. BucketBase (const BucketBase&); // Assignment is not possible. BucketBase& operator= (const BucketBase&); // Do the actual flushing. virtual void doFlush() = 0; // Do the actual resync-ing. virtual void doResync() = 0; // Do the actual extension of the file. // Note that itsNewNrOfBuckets has been increased before doExtend is called. virtual void doExtend (uInt nrBucket) = 0; // Initialize the bucket buffer. // The uninitialized buckets before this bucket are also initialized. virtual void initializeBuckets (uInt bucketNr) = 0; // The file used. BucketFile* itsFile; // The starting offsets of the buckets in the file. Int64 itsStartOffset; // The bucket size. uInt itsBucketSize; // The current nr of buckets in the file. uInt itsCurNrOfBuckets; // The new nr of buckets in the file (after extension). uInt itsNewNrOfBuckets; // Have data been written? Bool itsHasWritten; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/IO/BucketBuffered.cc000066400000000000000000000102571476623553700177360ustar00rootroot00000000000000//# BucketBuffered.cc: Use buffered file IO for buckets in a part of a file //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include //# for memset namespace casacore { //# NAMESPACE CASACORE - BEGIN BucketBuffered::BucketBuffered (BucketFile* file, Int64 startOffset, uInt bucketSize, uInt nrOfBuckets) : BucketBase (file, startOffset, bucketSize, nrOfBuckets), itsBuffer (0) { AlwaysAssert (itsFile->bufferedFile() != 0, AipsError); // Allocate a buffer that can hold a bucket. itsBuffer = new char[bucketSize]; } BucketBuffered::~BucketBuffered() { delete [] itsBuffer; } void BucketBuffered::read (uInt bucketNr, uInt bucketOffset, uInt nbytes, uInt bufferOffset) { if (bucketNr >= itsNewNrOfBuckets) { throw (indexError (bucketNr)); } itsFile->bufferedFile()->seek (itsStartOffset + Int64(bucketNr)*itsBucketSize + bucketOffset); // When doing read/write, it can happen that not all bytes are written yet. // So accept it if not all bytes could be read. uInt nread = itsFile->bufferedFile()->read (nbytes, itsBuffer+bufferOffset, False); if (nread < nbytes) { memset (itsBuffer+bufferOffset+nread, 0, nbytes-nread); } } void BucketBuffered::write (uInt bucketNr, uInt bucketOffset, uInt nbytes) { if (bucketNr >= itsCurNrOfBuckets) { if (bucketNr >= itsNewNrOfBuckets) { throw (indexError (bucketNr)); } itsCurNrOfBuckets = bucketNr+1; } itsFile->bufferedFile()->seek (itsStartOffset + Int64(bucketNr)*itsBucketSize + bucketOffset); itsFile->bufferedFile()->write (nbytes, itsBuffer); setWritten(); } void BucketBuffered::doFlush() { // Make sure the length is an integer nr of tiles. Int64 cubeLen = itsFile->bufferedFile()->length() - itsStartOffset; Int64 expLen = itsNewNrOfBuckets * itsBucketSize; if (expLen > cubeLen) { doExtend(0); } itsFile->bufferedFile()->flush(); } void BucketBuffered::doResync() {} void BucketBuffered::doExtend (uInt) { // Extend the file by writing the last byte. itsBuffer[0] = 0; write (itsNewNrOfBuckets-1, itsBucketSize-1, 1); } void BucketBuffered::initializeBuckets (uInt bucketNr) { // Initialize this bucket and all uninitialized ones before it. if (itsCurNrOfBuckets <= bucketNr) { memset (itsBuffer, 0, itsBucketSize); // Writing is sequentially, so seek needs to be done only once. itsFile->bufferedFile()->seek (itsStartOffset + Int64(itsCurNrOfBuckets)*itsBucketSize); while (itsCurNrOfBuckets <= bucketNr) { itsFile->bufferedFile()->write (itsBucketSize, itsBuffer); itsCurNrOfBuckets++; } setWritten(); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/IO/BucketBuffered.h000066400000000000000000000101231476623553700175700ustar00rootroot00000000000000//# BucketBuffered.h: Use buffered file IO for buckets in a part of a file //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_BUCKETBUFFERED_H #define CASA_BUCKETBUFFERED_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations. class FilebufIO; // // Use buffered file IO for buckets in a part of a file // // // // // //# Classes you should understand before using this one. //
      • BucketFile // // // BucketBuffered is similar to class // BucketCache and is meant to be used by // the storage managers of the Table System. // // It gives access to buckets in a file by means of memory-buffered files. // However, its functionality is a subset of BucketCache and is only meant // to be used by the Tiled Storage Managers. If The Standard and Incremental // Storage Manager also want to use it, functions like extend // needs to be added to this class. Also support for a free bucket list needs // to be added. // // // Use of BucketCache is sub-optimal when having large buckets and more or // less random IO. Memory-buffering behaves much better. // class BucketBuffered: public BucketBase { public: // Create the object for (part of) a file. // The file part buffered into memory starts at startOffset. Its length is // bucketSize*nrOfBuckets bytes. // If the file is smaller, the remainder is indicated as an extension // similarly to the behaviour of function extend. BucketBuffered (BucketFile* file, Int64 startOffset, uInt bucketSize, uInt nrOfBuckets); virtual ~BucketBuffered(); // Get a pointer to the buffer. char* getBuffer() { return itsBuffer; } // Read the given part into the internal buffer at the given offset. void read (uInt bucketNr, uInt bucketOffset, uInt nbytes, uInt bufferOffset=0); // Write the given part from the internal buffer. void write (uInt bucketNr, uInt bucketOffset, uInt nbytes); private: // Copy constructor is not possible. BucketBuffered (const BucketBuffered&); // Assignment is not possible. BucketBuffered& operator= (const BucketBuffered&); // Flush the file. virtual void doFlush(); // Do the actual resync-ing. virtual void doResync(); // Extend the file with the given number of buckets. virtual void doExtend (uInt nrBucket); // Initialize the bucket buffer. // The uninitialized buckets before this bucket are also initialized. virtual void initializeBuckets (uInt bucketNr); // Data buffer. char* itsBuffer; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/IO/BucketCache.cc000066400000000000000000000301401476623553700172100ustar00rootroot00000000000000//# BucketCache.cc: Cache for buckets in a file //# Copyright (C) 1994,1995,1996,1997,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN BucketCache::BucketCache (BucketFile* file, Int64 startOffset, uInt bucketSize, uInt nrOfBuckets, uInt cacheSize, void* ownerObject, BucketCacheToLocal readCallBack, BucketCacheFromLocal writeCallBack, BucketCacheAddBuffer initCallBack, BucketCacheDeleteBuffer deleteCallBack) : its_file (file), its_Owner (ownerObject), its_ReadCallBack (readCallBack), its_WriteCallBack (writeCallBack), its_InitCallBack (initCallBack), its_DeleteCallBack(deleteCallBack), its_StartOffset (startOffset), its_BucketSize (bucketSize), its_CurNrOfBuckets(0), its_NewNrOfBuckets(nrOfBuckets), its_CacheSize (cacheSize), its_CacheSizeUsed (0), its_Cache (cacheSize, static_cast(0)), its_ActualSlot (0), its_SlotNr (nrOfBuckets, Int(-1)), its_BucketNr (cacheSize, uInt(0)), its_Dirty (cacheSize, uInt(0)), its_LRU (cacheSize, uInt(0)), its_LRUCounter (0), its_Buffer (0), its_NrOfFree (0), its_FirstFree (-1) { initStatistics(); // The bucketsize must be set. if (bucketSize == 0) { throw (AipsError ("BucketCache::BucketCache; bucketsize=0")); } // A cache without slots is not possible; so give it a slot. if (its_CacheSize == 0) { resize (1); } // Allocate a buffer (for data in external format). // Initialize it to prevent "uninitialized memory errors" when writing. its_Buffer = new char[bucketSize]; for (uInt i=0; iopen(); Int64 size = its_file->fileSize(); if (size > startOffset) { its_CurNrOfBuckets = (size - startOffset) / bucketSize; if (its_CurNrOfBuckets > its_NewNrOfBuckets) { its_CurNrOfBuckets = its_NewNrOfBuckets; } } } BucketCache::~BucketCache() { // Clear the entire cache. // It is not flushed (that should have been done before). // In that way no needless flushes are done for a temporary table. clear (0, False); delete [] its_Buffer; } void BucketCache::clear (uInt fromSlot, Bool doFlush) { if (doFlush) { flush (fromSlot); } for (uInt i=fromSlot; i 0) { initializeBuckets (its_NewNrOfBuckets - 1); } Bool hasWritten = False; for (uInt i=fromSlot; i cacheSize) { its_CacheSizeUsed = cacheSize; } its_ActualSlot = 0; } void BucketCache::resync (uInt nrBucket, uInt nrOfFreeBucket, Int firstFreeBucket) { // Clear the entire cache, so data will be reread. // Set it to the new size. clear(); if (nrBucket > its_NewNrOfBuckets) { extend (nrBucket - its_NewNrOfBuckets); } its_CurNrOfBuckets = nrBucket; its_NrOfFree = nrOfFreeBucket; its_FirstFree = firstFreeBucket; } uInt BucketCache::nBucket() const { return its_NewNrOfBuckets; } void BucketCache::setDirty() { its_Dirty[its_ActualSlot] = 1; } void BucketCache::setLRU() { // When the LRU counter would wrap, clear all LRU info in the cache. if (its_LRUCounter == 4294967295u) { its_LRUCounter = 0; for (uInt i=0; i= its_NewNrOfBuckets) { throw (indexError (bucketNr)); } naccess_p++; // Test if it is already in the cache. if (its_SlotNr[bucketNr] >= 0) { its_ActualSlot = its_SlotNr[bucketNr]; setLRU(); return its_Cache[its_ActualSlot]; } // Not in cache, so get a slot. // Read the bucket when it is already in the file. // Otherwise get a new initialized bucket. if (bucketNr < its_CurNrOfBuckets) { getSlot (bucketNr); readBucket (its_ActualSlot); }else{ if (! its_file->isWritable()) { throw AipsError ("BucketCache::getBucket: bucket " + String::toString(bucketNr) + " exceeds nr of buckets"); } initializeBuckets (bucketNr); } return its_Cache[its_ActualSlot]; } void BucketCache::extend (uInt nrBucket) { its_NewNrOfBuckets += nrBucket; uInt oldSize = its_SlotNr.nelements(); if (oldSize < its_NewNrOfBuckets) { uInt newSize = oldSize*2; if (newSize < its_NewNrOfBuckets) { newSize = its_NewNrOfBuckets; } its_SlotNr.resize (newSize); for (uInt i=oldSize; i= 0) { // There is a free list, so get the first bucket from it. bucketNr = its_FirstFree; its_file->seek (its_StartOffset + Int64(bucketNr) * its_BucketSize); its_file->read (its_Buffer, CanonicalConversion::canonicalSize (static_cast(0))); CanonicalConversion::toLocal (its_FirstFree, its_Buffer); its_NrOfFree--; }else{ // No free buckets, so extend the file. // Initialize all uninitialized buckets before the newly added bucket. if (its_CurNrOfBuckets < its_NewNrOfBuckets) { initializeBuckets (its_NewNrOfBuckets - 1); } extend (1); its_CurNrOfBuckets++; bucketNr = its_NewNrOfBuckets - 1; } getSlot (bucketNr); its_Cache[its_ActualSlot] = data; its_Dirty[its_ActualSlot] = 1; return bucketNr; } void BucketCache::removeBucket() { // Removing a bucket means adding it to the beginning of the free list. // Thus store the bucket nr of the first free in this bucket // and make this bucket the first free. uInt bucketNr = its_BucketNr[its_ActualSlot]; CanonicalConversion::fromLocal (its_Buffer, its_FirstFree); its_file->seek (its_StartOffset + Int64(bucketNr) * its_BucketSize); its_file->write (its_Buffer, its_BucketSize); its_Dirty[its_ActualSlot] = 0; its_FirstFree = bucketNr; its_NrOfFree++; // Delete the stuff for this bucket. // Set the LRU to zero, so it will be reused first. its_DeleteCallBack (its_Owner, its_Cache[its_ActualSlot]); its_Cache[its_ActualSlot] = 0; its_SlotNr[bucketNr] = -1; its_LRU[its_ActualSlot] = 0; its_ActualSlot = 0; } void BucketCache::get (char* buf, uInt length, Int64 offset) { checkOffset (length, offset); its_file->seek (offset); its_file->read (buf, length); } void BucketCache::put (const char* buf, uInt length, Int64 offset) { checkOffset (length, offset); its_file->seek (offset); its_file->write (buf, length); } void BucketCache::checkOffset (uInt length, Int64 offset) const { // Check if not before or after cached area. if (offset + length > its_StartOffset && offset < its_StartOffset + Int64(its_CurNrOfBuckets)*its_BucketSize) { throw (indexError (offset)); } } void BucketCache::getSlot (uInt bucketNr) { if (its_CacheSizeUsed < its_CacheSize) { its_ActualSlot = its_CacheSizeUsed++; }else{ its_ActualSlot = 0; uInt least = its_LRU[0]; for (uInt i=1; iseek (its_StartOffset + Int64(its_BucketNr[slotNr]) * its_BucketSize); its_file->write (its_Buffer, its_BucketSize); its_Dirty[slotNr] = 0; nwrite_p++; } void BucketCache::readBucket (uInt slotNr) { /// cout << "read " << its_BucketNr[slotNr] << " " << slotNr; its_file->seek (its_StartOffset + Int64(its_BucketNr[slotNr]) * its_BucketSize); its_file->read (its_Buffer, its_BucketSize); its_Cache[slotNr] = its_ReadCallBack (its_Owner, its_Buffer); nread_p++; } void BucketCache::initializeBuckets (uInt bucketNr) { // Initialize this bucket and all uninitialized ones before it. while (its_CurNrOfBuckets <= bucketNr) { getSlot (its_CurNrOfBuckets); /// cout << "init " << its_CurNrOfBuckets << " " << its_ActualSlot; its_Cache[its_ActualSlot] = its_InitCallBack (its_Owner); its_Dirty[its_ActualSlot] = 1; its_CurNrOfBuckets++; ninit_p++; } } void BucketCache::showStatistics (ostream& os) const { os << "cacheSize: " << its_CacheSize << " (*" << its_BucketSize << ")" << endl; os << "#buckets: " << its_CurNrOfBuckets; if (nread_p+nwrite_p > its_CurNrOfBuckets) { os << " (< #reads + #writes!)"; } os << endl; if (its_NrOfFree > 0) { os << "#deleted: " << its_NrOfFree << endl; } if (nread_p > 0) { os << "#reads: " << nread_p << endl; } if (ninit_p > 0) { os << "#inits: " << ninit_p << endl; } if (nwrite_p > 0) { os << "#writes: " << nwrite_p << endl; } os << "#accesses: " << naccess_p; if (naccess_p > 0) { os << " hit-rate: " << 100 * float(naccess_p - nread_p - ninit_p) / float(naccess_p) << "%"; } cout << endl; } void BucketCache::initStatistics() { naccess_p = 0; nread_p = 0; ninit_p = 0; nwrite_p = 0; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/IO/BucketCache.h000066400000000000000000000347201476623553700170620ustar00rootroot00000000000000//# BucketCache.h: Cache for buckets in a part of a file //# Copyright (C) 1994,1995,1996,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_BUCKETCACHE_H #define CASA_BUCKETCACHE_H //# Includes #include #include #include #include //# Forward clarations #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Define the type of the static read and write function. // // // // // // The BucketCache class needs a way to convert its data from local // to canonical format and vice-versa. This is done by callback // functions defined at construction time. //

        // The ToLocal callback function has to allocate a buffer of the correct // size and to copy/convert the canonical data in the input buffer to // this buffer. The pointer this newly allocated buffer has to be returned. // The BucketCache class keeps this pointer in the cache block. //

        // The FromLocal callback function has to copy/convert the data from the // buffer in local format to the buffer in canonical format. It should // NOT delete the buffer; that has to be done by the DeleteBuffer function. //

        // The AddBuffer callback function has to create (and initialize) a // buffer to be added to the file and cache. // When the file gets extended, BucketCache only registers the new size, // but does not werite anything. When a bucket is read between the // actual file size and the new file size, the AddBuffer callback function // is called to create a buffer and possibly initialize it. //

        // The DeleteBuffer callback function has to delete the buffer // allocated by the ToLocal function. //

        // The functions get a pointer to the owner object, which was provided // at construction time. The callback function has to cast this to the // correct type and can use it thereafter. //
        // C++ supports pointers to members, but it is a bit hard. Therefore pointers // to static members are used (which are simple pointers to functions). // A pointer to the owner object is also passed to let the static function // call the correct member function (when needed). // // // // See class BucketCache. // // typedef char* (*BucketCacheToLocal) (void* ownerObject, const char* canonical); typedef void (*BucketCacheFromLocal) (void* ownerObject, char* canonical, const char* local); typedef char* (*BucketCacheAddBuffer) (void* ownerObject); typedef void (*BucketCacheDeleteBuffer) (void* ownerObject, char* buffer); // //

        // Cache for buckets in a part of a file // // // // // //# Classes you should understand before using this one. //
      • BucketFile // // // BucketCache implements a cache for buckets in (a part of) a file. // // // A cache may allow more efficient quasi-random IO. // It can, for instance, be used when a limited number of blocks // in a file have to be accessed again and again. //

        // The class BucketCache provides such a cache. It can be used on a // consecutive part of a file as long as that part is not simultaneously // accessed in another way (including another BucketCache object). //

        // BucketCache stores the data as given. // It uses callback functions // to allocate/delete buffers and to convert the data to/from local format. //

        // When a new bucket is needed and all slots in the cache are used, // BucketCache will remove the least recently used bucket from the // cache. When the dirty flag is set, it will first be written. //

        // BucketCache maintains a list of free buckets. Initially this list is // empty. When a bucket is removed, it is added to the free list. // AddBucket will take buckets from the free list before extending the file. //

        // Since it is possible to handle only a part of a file by a BucketCache // object, it is also possible to have multiple BucketCache objects on // the same file (as long as they access disjoint parts of the file). // Each BucketCache object can have its own bucket size. This can, // for example, be used to have tiled arrays with different tile shapes // in the same file. //

        // Statistics are kept to know how efficient the cache is working. // It is possible to initialize and show the statistics. // // // A cache may reduce IO traffix considerably. // Furthermore it is more efficient to keep a cache in local format. // In that way conversion to/from local only have to be done when // data gets read/written. It also allows for precalculations. // // // // // Define the callback function for reading a bucket. // char* bToLocal (void*, const char* data) // { // char* ptr = new char[32768]; // memcpy (ptr, data, 32768); // return ptr; // } // // Define the callback function for writing a bucket. // void bFromLocal (void*, char* data, const char* local) // { // memcpy (data, local, 32768); // } // // Define the callback function for initializing a new bucket. // char* bAddBuffer (void*) // { // char* ptr = new char[32768]; // for (uInt i=0; i++; i<32768) { // ptr[i] = 0; // } // return ptr; // } // // Define the callback function for deleting a bucket. // void bDeleteBuffer (void*, char* buffer) // { // delete [] buffer; // } // // void someFunc() // { // // Open the filebuf. // BucketFile file(...); // file.open(); // uInt i; // // Create a cache for the part of the file starting at offset 512 // // consisting of 1000 buckets. The cache consists of 10 buckets. // // Each bucket is 32768 bytes. // BucketCache cache (&file, 512, 32768, 1000, 10, 0, // bToLocal, bFromLocal, bAddBuffer, bDeleteBuffer); // // Write all buckets into the file. // for (i=0; i<100; i++) { // char* buf = new char[32768]; // cache.addBucket (buf); // } // Flush the cache to write all buckets in it. // cache.flush(); // // Read all buckets from the file. // for (i=0; i<1000; i++) { // char* buf = cache.getBucket(i); // ... // } // cout << cache.nBucket() << endl; // } // // // //

      • When ready, use HashMap for the internal maps. // class BucketCache { public: // Create the cache for (a part of) a file. // The file part used starts at startOffset. Its length is // bucketSize*nrOfBuckets bytes. // When the file is smaller, the remainder is indicated as an extension // similarly to the behaviour of function extend. BucketCache (BucketFile* file, Int64 startOffset, uInt bucketSize, uInt nrOfBuckets, uInt cacheSize, void* ownerObject, BucketCacheToLocal readCallBack, BucketCacheFromLocal writeCallBack, BucketCacheAddBuffer addCallBack, BucketCacheDeleteBuffer deleteCallBack); ~BucketCache(); // Flush the cache from the given slot on. // By default the entire cache is flushed. // When the entire cache is flushed, possible remaining uninitialized // buckets will be initialized first. // A True status is returned when buckets had to be written. Bool flush (uInt fromSlot = 0); // Clear the cache from the given slot on. // By default the entire cache is cleared. // It will remove the buckets in the cleared part. // If wanted and needed, the buckets are flushed to the file // before removing them. // It can be used to enforce rereading buckets from the file. void clear (uInt fromSlot = 0, Bool doFlush = True); // Resize the cache. // When the cache gets smaller, the latter buckets are cached out. // It does not take "least recently used" into account. void resize (uInt cacheSize); // Resynchronize the object (after another process updated the file). // It clears the cache (so all data will be reread) and sets // the new sizes. void resync (uInt nrBucket, uInt nrOfFreeBucket, Int firstFreeBucket); // Get the current nr of buckets in the file. uInt nBucket() const; // Get the current cache size (in buckets). uInt cacheSize() const; // Set the dirty bit for the current bucket. void setDirty(); // Make another bucket current. // When no more cache slots are available, the one least recently // used is flushed. // The data in the bucket is converted using the ToLocal callback // function. When the bucket does not exist yet in the file, it // gets added and initialized using the AddBuffer callback function. // A pointer to the data in converted format is returned. char* getBucket (uInt bucketNr); // Extend the file with the given number of buckets. // The buckets get initialized when they are acquired // (using getBucket) for the first time. void extend (uInt nrBucket); // Add a bucket to the file and make it the current one. // When no more cache slots are available, the one least recently // used is flushed. //
        When no free buckets are available, the file will be // extended with one bucket. It returns the new bucket number. // The buffer must have been allocated on the heap. // It will get part of the cache; its contents are not copied. // Thus the buffer should hereafter NOT be used for other purposes. // It will be deleted later via the DeleteBuffer callback function. // The data is copied into the bucket. A pointer to the data in // local format is returned. uInt addBucket (char* data); // Remove the current bucket; i.e. add it to the beginning of the // free bucket list. void removeBucket(); // Get a part from the file outside the cached area. // It is checked if that part is indeed outside the cached file area. void get (char* buf, uInt length, Int64 offset); // Put a part from the file outside the cached area. // It is checked if that part is indeed outside the cached file area. void put (const char* buf, uInt length, Int64 offset); // Get the bucket number of the first free bucket. // -1 = no free buckets. Int firstFreeBucket() const; // Get the number of free buckets. uInt nFreeBucket() const; // (Re)initialize the cache statistics. void initStatistics(); // Show the statistics. void showStatistics (ostream& os) const; private: // The file used. BucketFile* its_file; // The owner object. void* its_Owner; // The read callback function. BucketCacheToLocal its_ReadCallBack; // The write callback function. BucketCacheFromLocal its_WriteCallBack; // The add bucket callback function. BucketCacheAddBuffer its_InitCallBack; // The delete callback function. BucketCacheDeleteBuffer its_DeleteCallBack; // The starting offsets of the buckets in the file. Int64 its_StartOffset; // The bucket size. uInt its_BucketSize; // The current nr of buckets in the file. uInt its_CurNrOfBuckets; // The new nr of buckets in the file (after extension). uInt its_NewNrOfBuckets; // The size of the cache (i.e. #buckets fitting in it). uInt its_CacheSize; // The nr of slots used in the cache. uInt its_CacheSizeUsed; // The cache itself. PtrBlock its_Cache; // The cache slot actually used. uInt its_ActualSlot; // The slot numbers of the buckets in the cache (-1 = not in cache). Block its_SlotNr; // The buckets in the cache. Block its_BucketNr; // Determine if a block is dirty (i.e. changed) (1=dirty). Block its_Dirty; // Determine when a block is used for the last time. Block its_LRU; // The Least Recently Used counter. uInt its_LRUCounter; // The internal buffer. char* its_Buffer; // The number of free buckets. uInt its_NrOfFree; // The first free bucket (-1 = no free buckets). Int its_FirstFree; // The statistics. uInt naccess_p; uInt nread_p; uInt ninit_p; uInt nwrite_p; // Copy constructor is not possible. BucketCache (const BucketCache&); // Assignment is not possible. BucketCache& operator= (const BucketCache&); // Set the LRU information for the current slot. void setLRU(); // Get a cache slot for the bucket. void getSlot (uInt bucketNr); // Write a bucket. void writeBucket (uInt slotNr); // Read a bucket. void readBucket (uInt slotNr); // Initialize the bucket buffer. // The uninitialized buckets before this bucket are also initialized. // It returns a pointer to the buffer. void initializeBuckets (uInt bucketNr); // Check if the offset of a non-cached part is correct. void checkOffset (uInt length, Int64 offset) const; }; inline uInt BucketCache::cacheSize() const { return its_CacheSize; } inline Int BucketCache::firstFreeBucket() const { return its_FirstFree; } inline uInt BucketCache::nFreeBucket() const { return its_NrOfFree; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/IO/BucketFile.cc000066400000000000000000000140551476623553700170730ustar00rootroot00000000000000//# BucketFile.cc: Tiled Hypercube Storage Manager for tables //# Copyright (C) 1995,1996,1999,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include // needed for errno #include // needed for strerror #if defined(AIPS_DARWIN) || defined(AIPS_BSD) #undef trace3OPEN #define trace3OPEN open #undef trace2OPEN #define trace2OPEN open #undef traceLSEEK #define traceLSEEK lseek #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN BucketFile::BucketFile (const String& fileName, uInt bufSizeFile, Bool mappedFile, const std::shared_ptr& mfile) : name_p (Path(fileName).expandedName()), isWritable_p (True), isMapped_p (mappedFile), bufSize_p (bufSizeFile), fd_p (-1), file_p (), mappedFile_p (0), bufferedFile_p (0), mfile_p (mfile) { // Create the file. if (mfile_p) { file_p.reset (new MFFileIO (mfile_p, name_p, ByteIO::New)); isMapped_p = False; bufSize_p = 0; } else { fd_p = FiledesIO::create (name_p.chars()); file_p.reset (new FiledesIO (fd_p, name_p)); } createMapBuf(); } BucketFile::BucketFile (const String& fileName, Bool isWritable, uInt bufSizeFile, Bool mappedFile, const std::shared_ptr& mfile) : name_p (Path(fileName).expandedName()), isWritable_p (isWritable), isMapped_p (mappedFile), bufSize_p (bufSizeFile), fd_p (-1), file_p (), mappedFile_p (0), bufferedFile_p (0), mfile_p (mfile) { if (mfile_p) { isMapped_p = False; bufSize_p = 0; } } BucketFile::~BucketFile() { close(); } std::shared_ptr BucketFile::makeFilebufIO (uInt bufferSize) { if (mfile_p) { return file_p; } return std::make_shared(fd_p, bufferSize); } void BucketFile::close() { if (file_p) { deleteMapBuf(); file_p.reset(); FiledesIO::close (fd_p); fd_p = -1; } } void BucketFile::open() { if (! file_p) { if (mfile_p) { file_p.reset (new MFFileIO (mfile_p, name_p, isWritable_p ? ByteIO::Update : ByteIO::Old)); } else { fd_p = FiledesIO::open (name_p.chars(), isWritable_p); file_p.reset (new FiledesIO (fd_p, name_p)); } createMapBuf(); } } void BucketFile::createMapBuf() { deleteMapBuf(); if (isMapped_p) { AlwaysAssert (fd_p >= 0, AipsError); mappedFile_p = new MMapfdIO (fd_p, name_p); } if (bufSize_p > 0) { AlwaysAssert (fd_p >= 0, AipsError); bufferedFile_p = new FilebufIO (fd_p, bufSize_p); } } void BucketFile::deleteMapBuf() { delete mappedFile_p; mappedFile_p = 0; delete bufferedFile_p; bufferedFile_p = 0; } void BucketFile::remove() { close(); if (mfile_p) { // Remove the file from the MultiFileBase. Note it might not exist yet. Int id = mfile_p->fileId (name_p, False); if (id >= 0) { mfile_p->deleteFile (id); } } else { DOos::remove (name_p, False, False); } file_p.reset(); } void BucketFile::fsync() { file_p->fsync(); } void BucketFile::setRW() { // Exit if already writable. if (isWritable_p) { return; } isWritable_p = True; // Try to reopen the file as read/write. // Throw an exception if it fails. if (file_p) { if (mfile_p) { file_p->reopenRW(); } else { close(); open(); } } } uInt BucketFile::read (void* buffer, uInt length) { return file_p->read (length, buffer); } uInt BucketFile::write (const void* buffer, uInt length) { file_p->write (length, buffer); return length; } void BucketFile::seek (Int64 offset) { AlwaysAssert (bufferedFile_p == 0, AipsError); file_p->seek (offset, ByteIO::Begin); } Int64 BucketFile::fileSize () const { // If a buffered file is used, seek in there. Otherwise its internal // offset is wrong. Int64 size; if (bufferedFile_p) { size = bufferedFile_p->seek (0, ByteIO::End); } else { size = file_p->length(); } if (size < 0){ LogIO logIo (LogOrigin ("BucketFile", "fileSize")); logIo << LogIO::WARN; logIo << "lseek failed for " << name() << ": errno=" << errno << "'" << strerror(errno) << "'\n"; logIo << LogIO::POST; } return size; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/IO/BucketFile.h000066400000000000000000000170131476623553700167320ustar00rootroot00000000000000//# BucketFile.h: File object for BucketCache //# Copyright (C) 1995,1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_BUCKETFILE_H #define CASA_BUCKETFILE_H //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MultiFileBase; // // File object for BucketCache. // // // // //# //# Classes you should understand before using this one. //# // // BucketFile represents a data file for the BucketCache class. // // // A BucketFile object represents a data file. Currently it is used // by the Table system, but it can easily be turned into // a more general storage manager file class. //
        // Creation of a BucketFile object does not open the file yet. // An explicit open call has to be given before the file can be used. //

        // The file can be opened as an ordinary file (with a file descriptor) // or as a file in a MultiFileBase object. An ordinary file can be accessed // in 3 ways: //

          //
        • In an unbuffered way, where the parent BucketCache class accesses // a bucket at a time (and possibly keeps it in a cache). //
        • In a memory-mapped way, where the parent BucketMapped class does // the access using the MMapfdIO member. //
        • In a buffered way, where the parent BucketBuffered class does // the access using the FilebufIO member. //
        // A MultiFileBase file can only be accessed in the unbuffered way. //
        // // Encapsulate the file creation and access into a single class // to hide the file IO details. // // // // // Create the file for the given storage manager. // BucketFile file ("file.name"); // // Open the file and write into it. // file.open(); // file.write (someBuffer, someLength); // // Get the length of the file. // uInt size = file.fileSize(); // // // //
      • Use the ByteIO classes when they are ready. // class BucketFile { public: // Create a BucketFile object for a new file. // The file with the given name will be created as a normal file or // as part of a MultiFileBase (if mfile != 0). // It can be indicated if a MMapfdIO and/or FilebufIO object must be // created for the file. If a MultiFileBase is used, memory-mapped IO // cannot be used and mappedFile is ignored. explicit BucketFile (const String& fileName, uInt bufSizeFile=0, Bool mappedFile=False, const std::shared_ptr& mfile=std::shared_ptr()); // Create a BucketFile object for an existing file. // The file should be opened by the open. // Tell if the file must be opened writable. // It can be indicated if a MMapfdIO and/or FilebufIO object must be // created for the file. If a MultiFileBase is used, memory-mapped IO // cannot be used and mappedFile is ignored. BucketFile (const String& fileName, Bool writable, uInt bufSizeFile=0, Bool mappedFile=False, const std::shared_ptr& mfile=std::shared_ptr()); // The destructor closes the file (if open). virtual ~BucketFile(); // Forbid copy constructor. BucketFile (const BucketFile&) = delete; // Forbid assignment. BucketFile& operator= (const BucketFile&) = delete; // Make a (temporary) buffered IO object for this file. // That object should not close the file. virtual std::shared_ptr makeFilebufIO (uInt bufferSize); // Get the mapped file object. MMapfdIO* mappedFile() { return mappedFile_p; } // Get the buffered file object. FilebufIO* bufferedFile() { return bufferedFile_p; } // Open the file if not open yet. virtual void open(); // Close the file (if open). virtual void close(); // Remove the file (and close it if needed). virtual void remove(); // Fsync the file (i.e. force the data to be physically written). virtual void fsync(); // Set the file to read/write access. It is reopened if not writable. // It does nothing if the file is already writable. virtual void setRW(); // Get the file name. virtual const String& name() const; // Has the file logically been indicated as writable? Bool isWritable() const; // Read bytes from the file. virtual uInt read (void* buffer, uInt length); // Write bytes into the file. virtual uInt write (const void* buffer, uInt length); // Seek in the file. // virtual void seek (Int64 offset); void seek (Int offset); // // Get the (physical) size of the file. // This is doing a seek and sets the file pointer to end-of-file. virtual Int64 fileSize() const; // Is the file cached, mapped, or buffered? // Bool isCached() const; Bool isMapped() const; Bool isBuffered() const; // private: // The file name. String name_p; // The (logical) writability of the file. Bool isWritable_p; Bool isMapped_p; uInt bufSize_p; int fd_p; // fd (if used) of unbuffered file // The unbuffered file. std::shared_ptr file_p; // The optional mapped file. MMapfdIO* mappedFile_p; // The optional buffered file. FilebufIO* bufferedFile_p; // The possibly used MultiFileBase. std::shared_ptr mfile_p; // Create the mapped or buffered file object. void createMapBuf(); // Delete the possible mapped or buffered file object. void deleteMapBuf(); }; inline const String& BucketFile::name() const { return name_p; } inline Bool BucketFile::isWritable() const { return isWritable_p; } inline void BucketFile::seek (Int offset) { seek (Int64(offset)); } inline Bool BucketFile::isCached() const { return !isMapped_p && bufSize_p==0; } inline Bool BucketFile::isMapped() const { return isMapped_p; } inline Bool BucketFile::isBuffered() const { return bufSize_p>0; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/IO/BucketMapped.cc000066400000000000000000000060371476623553700174230ustar00rootroot00000000000000//# BucketCache.cc: File buckets by means of file mapping //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include //# for memset namespace casacore { //# NAMESPACE CASACORE - BEGIN BucketMapped::BucketMapped (BucketFile* file, Int64 startOffset, uInt bucketSize, uInt nrOfBuckets) : BucketBase (file, startOffset, bucketSize, nrOfBuckets) { AlwaysAssert (itsFile->mappedFile() != 0, AipsError); } BucketMapped::~BucketMapped() {} void BucketMapped::doResync() { ////itsFile->mappedFile()->resync(); } void BucketMapped::doFlush() { itsFile->mappedFile()->flush(); } void BucketMapped::doExtend (uInt) { // Extend the file by writing the last byte. char ch=0; itsFile->mappedFile()->seek (itsStartOffset + Int64(itsNewNrOfBuckets)*itsBucketSize - 1); itsFile->mappedFile()->write (1, &ch); } const char* BucketMapped::getBucket (uInt bucketNr) { if (bucketNr >= itsCurNrOfBuckets) { if (bucketNr >= itsNewNrOfBuckets) { throw (indexError (bucketNr)); } initializeBuckets (bucketNr); } return static_cast(itsFile->mappedFile()->getReadPointer (itsStartOffset + Int64(bucketNr)*itsBucketSize)); } void BucketMapped::initializeBuckets (uInt bucketNr) { if (itsCurNrOfBuckets <= bucketNr) { doExtend (0); // Initialize this bucket and all uninitialized ones before it. while (itsCurNrOfBuckets <= bucketNr) { char* data = static_cast(itsFile->mappedFile()->getWritePointer (itsStartOffset + Int64(itsCurNrOfBuckets)*itsBucketSize)); memset (data, 0, itsBucketSize); itsCurNrOfBuckets++; setWritten(); } } } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/IO/BucketMapped.h000066400000000000000000000100611476623553700172550ustar00rootroot00000000000000//# BucketMapped.h: File buckets by means of file mapping //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_BUCKETMAPPED_H #define CASA_BUCKETMAPPED_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Use file mapping for buckets in a part of a file // // // // // //# Classes you should understand before using this one. //
      • BucketFile // // // BucketMapped uses memory-mapped files for bucket access. // // // BucketMapped is similar to class // BucketCache and is meant to be used by // the storage managers of the Table System. // // It gives access to buckets in a file by means of memory-mapped files. // However, its functionality is a subset of BucketCache and is only meant // to be used by the Tiled Storage Managers. If The Standard and Incremental // Storage Manager also want to use it, functions like extend // needs to be added to this class. Also support for a free bucket list needs // to be added. // // // Use of BucketCache is sub-optimal when having large buckets and more or // less random IO. Memory-mapping behaves much better. // class BucketMapped: public BucketBase { public: // Create the cache for (part of) a file. // The file part mapped into memory starts at startOffset. Its length is // bucketSize*nrOfBuckets bytes. // If the file is smaller, the remainder is indicated as an extension // similarly to the behaviour of function extend. BucketMapped (BucketFile* file, Int64 startOffset, uInt bucketSize, uInt nrOfBuckets); // Unmap the file ~BucketMapped(); // Get a readonly pointer to the given bucket in memory. const char* getBucket (uInt bucketNr); // Get a writable pointer to the given bucket in memory. // It sets the hasWritten flag. char* getrwBucket (uInt bucketNr) { itsHasWritten = True; return const_cast(getBucket(bucketNr)); } private: // Copy constructor is not possible. BucketMapped (const BucketMapped&); // Assignment is not possible. BucketMapped& operator= (const BucketMapped&); // Flush the file. virtual void doFlush(); // Do the actual resync-ing. virtual void doResync(); // Extend the file with the given number of buckets. virtual void doExtend (uInt nrBucket); // Initialize the bucket buffer. // The uninitialized buckets before this bucket are also initialized. virtual void initializeBuckets (uInt bucketNr); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/IO/ByteIO.cc000066400000000000000000000045261476623553700162130ustar00rootroot00000000000000//# ByteIO.cc: Abstract base class for IO on a byte stream //# Copyright (C) 1996,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ByteIO::~ByteIO() {} void ByteIO::reopenRW() { if (! isWritable()) { throw (AipsError ("ByteIO: reopenRW is not possible")); } } void ByteIO::pwrite (Int64 size, Int64 offset, const void* buf) { Int64 cur = doSeek(0, ByteIO::Current); doSeek(offset, ByteIO::Begin); try { write(size, buf); } catch (...) { doSeek(cur, ByteIO::Begin); throw; } doSeek(cur, ByteIO::Begin); } Int64 ByteIO::pread (Int64 size, Int64 offset, void* buf, Bool throwException) { Int64 r = -1; Int64 cur = doSeek(0, ByteIO::Current); doSeek(offset, ByteIO::Begin); try { r = read(size, buf, throwException); } catch (...) { doSeek(cur, ByteIO::Begin); throw; } doSeek(cur, ByteIO::Begin); return r; } void ByteIO::flush() {} void ByteIO::fsync() {} void ByteIO::resync() {} void ByteIO::truncate (Int64) {} String ByteIO::fileName() const { return String(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/IO/ByteIO.h000066400000000000000000000137451476623553700160600ustar00rootroot00000000000000//# ByteIO.h: Abstract base class for IO on a byte stream //# Copyright (C) 1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_BYTEIO_H #define CASA_BYTEIO_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Abstract base class for IO on a byte stream. // // // // // ByteIO is the abstract base class for all classes doing IO on // byte streams. Examples of derived classes are // RegularFileIO and // MemoryIO. //

        // ByteIO contains two enumerations, which define the possible // open and seek options on byte streams. These enumerations // are used throughout the IO framework. // // // Make polymorphic operations on byte streams possible. // class ByteIO { public: // Define the possible ByteIO open options. enum OpenOption { Old=1, // read/write; file must exist. Update, // read/write; create file if not exist. Append, // read/write; create file if not exist. New, // read/write; file may not exist yet. NewNoReplace, // read/write; delete file at close. Scratch, // read/write; file must exist; delete at close. Delete }; // Define the possible seek options. enum SeekOption { // Seek from beginning of file. Begin=1, // Seek from current position. Current, // Seek from the end of the file. End }; // The constructor does nothing. ByteIO(); virtual ~ByteIO(); // Write size bytes to the byte stream. virtual void write (Int64 size, const void* buf) = 0; // Write size bytes to the byte stream at offset. // The file offset is not changed virtual void pwrite (Int64 size, Int64 offset, const void* buf); // Read size bytes from the byte stream. Returns the number of // bytes actually read, or a negative number if an error occurred. Will also // throw an Exception (AipsError) if the requested number of bytes could // not be read unless throwException is set to False. virtual Int64 read (Int64 size, void* buf, Bool throwException=True) = 0; // Like read but reads from offset of start of the file // The file offset is not changed virtual Int64 pread (Int64 size, Int64 offset, void* buf, Bool throwException=True); // Reopen the underlying IO stream for read/write access. // Nothing will be done if the stream is writable already. // Otherwise it will be reopened and an exception will be thrown // if it is not possible to reopen it for read/write access. // The default implementation in this base class throws a "not possible" // exception if a reopen has to be done. virtual void reopenRW(); // This function sets the position on the given offset. // The seek option defines from which file position the seek is done. // -1 is returned if not seekable. // Int64 seek (Int offset, ByteIO::SeekOption = ByteIO::Begin); Int64 seek (Int64 offset, ByteIO::SeekOption = ByteIO::Begin); // // Flush the data to the file. // The default implementation does nothing. virtual void flush(); // Fsync the file (i.e. force the data to be physically written). // The default implementation does nothing. virtual void fsync(); // Resync the file (i.e. empty the current buffer). // The default implementation does nothing. virtual void resync(); // Truncate the file to the given size. // The default implementation does nothing. virtual void truncate (Int64 size); // Get the file name of the file attached. // The default implementation returns an empty string. virtual String fileName() const; // Get the length of the byte stream. virtual Int64 length() = 0; // Is the byte stream readable? virtual Bool isReadable() const = 0; // Is the byte stream writable? virtual Bool isWritable() const = 0; // Is the byte stream seekable? virtual Bool isSeekable() const = 0; protected: // Make copy constructor and assignment protected, so a user cannot // use them (but a derived class can). // ByteIO (const ByteIO& byteIO); ByteIO& operator= (const ByteIO& byteIO); // virtual Int64 doSeek (Int64 offset, ByteIO::SeekOption) = 0; }; inline ByteIO::ByteIO() {} inline ByteIO::ByteIO (const ByteIO&) {} inline ByteIO& ByteIO::operator= (const ByteIO&) { return *this; } inline Int64 ByteIO::seek (Int64 offset, ByteIO::SeekOption option) { return doSeek (offset, option); } inline Int64 ByteIO::seek (Int offset, ByteIO::SeekOption option) { return doSeek (Int64(offset), option); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/IO/ByteSink.cc000066400000000000000000000114741476623553700166100ustar00rootroot00000000000000//# ByteSink.cc: Class for write-only access to data in a given format //# Copyright (C) 1996,1998,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ByteSink::ByteSink() {} ByteSink::ByteSink (const std::shared_ptr& typeIO) : BaseSinkSource (typeIO) { if (!isWritable()) { throw (AipsError ("ByteSink is not writable")); } } ByteSink::ByteSink (const ByteSink& sink) : BaseSinkSource (sink) {} ByteSink& ByteSink::operator= (const ByteSink& sink) { BaseSinkSource::operator= (sink); return *this; } ByteSink::~ByteSink() {} ByteSink& ByteSink::operator<< (Bool value) { itsTypeIO->write (1, &value); return *this; } ByteSink& ByteSink::operator<< (Char value) { itsTypeIO->write (1, &value); return *this; } ByteSink& ByteSink::operator<< (uChar value) { itsTypeIO->write (1, &value); return *this; } ByteSink& ByteSink::operator<< (Short value) { itsTypeIO->write (1, &value); return *this; } ByteSink& ByteSink::operator<< (uShort value) { itsTypeIO->write (1, &value); return *this; } ByteSink& ByteSink::operator<< (Int value) { itsTypeIO->write (1, &value); return *this; } ByteSink& ByteSink::operator<< (uInt value) { itsTypeIO->write (1, &value); return *this; } ByteSink& ByteSink::operator<< (Int64 value) { itsTypeIO->write (1, &value); return *this; } ByteSink& ByteSink::operator<< (uInt64 value) { itsTypeIO->write (1, &value); return *this; } ByteSink& ByteSink::operator<< (Float value) { itsTypeIO->write (1, &value); return *this; } ByteSink& ByteSink::operator<< (Double value) { itsTypeIO->write (1, &value); return *this; } ByteSink& ByteSink::operator<< (const Complex& value) { itsTypeIO->write (1, &value); return *this; } ByteSink& ByteSink::operator<< (const DComplex& value) { itsTypeIO->write (1, &value); return *this; } ByteSink& ByteSink::operator<< (const String& value) { itsTypeIO->write (1, &value); return *this; } ByteSink& ByteSink::operator<< (const Char* value) { String str(value); itsTypeIO->write (1, &str); return *this; } void ByteSink::write (size_t nvalues, const Bool* value) { itsTypeIO->write (nvalues, value); } void ByteSink::write (size_t nvalues, const Char* value) { itsTypeIO->write (nvalues, value); } void ByteSink::write (size_t nvalues, const uChar* value) { itsTypeIO->write (nvalues, value); } void ByteSink::write (size_t nvalues, const Short* value) { itsTypeIO->write (nvalues, value); } void ByteSink::write (size_t nvalues, const uShort* value) { itsTypeIO->write (nvalues, value); } void ByteSink::write (size_t nvalues, const Int* value) { itsTypeIO->write (nvalues, value); } void ByteSink::write (size_t nvalues, const uInt* value) { itsTypeIO->write (nvalues, value); } void ByteSink::write (size_t nvalues, const Int64* value) { itsTypeIO->write (nvalues, value); } void ByteSink::write (size_t nvalues, const uInt64* value) { itsTypeIO->write (nvalues, value); } void ByteSink::write (size_t nvalues, const Float* value) { itsTypeIO->write (nvalues, value); } void ByteSink::write (size_t nvalues, const Double* value) { itsTypeIO->write (nvalues, value); } void ByteSink::write (size_t nvalues, const Complex* value) { itsTypeIO->write (nvalues, value); } void ByteSink::write (size_t nvalues, const DComplex* value) { itsTypeIO->write (nvalues, value); } void ByteSink::write (size_t nvalues, const String* value) { itsTypeIO->write (nvalues, value); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/IO/ByteSink.h000066400000000000000000000132351476623553700164470ustar00rootroot00000000000000//# ByteSink.h: Class for write-only access to data in a given format //# Copyright (C) 1996,1998,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_BYTESINK_H #define CASA_BYTESINK_H #include #include //# The following should be a forward declaration. But our Complex & DComplex //# classes are a typedef hence this does not work. Replace the following with //# forward declarations when Complex and DComplex are no longer typedefs. #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class TypeIO; class String; //

        Class for write-only access to data in a given format. // // // // //
      • BaseSinkSource class //
      • TypeIO class and derived classes // // // A sink is the place where bytes are written to. // // // ByteSink provides write-only access to a typed byte stream in the // Casacore IO framework. The base class BaseSinkSource // contains common functions like seek. //

        // The object is constructed using a typed byte stream. This stream // is an instance of a class derived from class // TypeIO. This makes it possible to // store the data in any format (e.g. CanonicalIO or RawIO). //
        In its turn TypeIO uses an instance of a class derived from class // ByteIO. This makes it possible to // use any output stream (e.g. file, memory). //

        // Note that in general ByteSink will only be used // for write-only streams like sockets or pipes. // Class ByteSinkSource // is the obvious choice for read/write streams. // // // // // Construct the correct output stream. // MemoryIO memio; // CanonicalIO canio (&memio); // ByteSink sink (&canio); // // Write data. // Int vali; // sink << vali << True; // // // // This class makes it possible to deny read-access to an IO stream. // class ByteSink: virtual public BaseSinkSource { public: // Default constructor. // This creates an invalid object, but is present for convenience. ByteSink(); // Construct from given TypeIO object. // The constructor does not copy the object, but only keeps a pointer to it. ByteSink (const std::shared_ptr& typeIO); // The copy constructor uses reference semantics ByteSink (const ByteSink& sink); // The assignment operator uses reference semantics ByteSink& operator= (const ByteSink& sink); // destructor ~ByteSink(); // These functions write one value of the given type. // If this function does not succeed, an exception will be thrown. // ByteSink& operator<< (Bool value); ByteSink& operator<< (Char value); ByteSink& operator<< (uChar value); ByteSink& operator<< (Short value); ByteSink& operator<< (uShort value); ByteSink& operator<< (Int value); ByteSink& operator<< (uInt value); ByteSink& operator<< (Int64 value); ByteSink& operator<< (uInt64 value); ByteSink& operator<< (Float value); ByteSink& operator<< (Double value); ByteSink& operator<< (const Complex& value); ByteSink& operator<< (const DComplex& value); ByteSink& operator<< (const String& value); ByteSink& operator<< (const Char* value); // // These functions write multiple values of the given type. // If this function does not succeed, an exception will be thrown. // void write (size_t nvalues, const Bool* value); void write (size_t nvalues, const Char* value); void write (size_t nvalues, const uChar* value); void write (size_t nvalues, const Short* value); void write (size_t nvalues, const uShort* value); void write (size_t nvalues, const Int* value); void write (size_t nvalues, const uInt* value); void write (size_t nvalues, const Int64* value); void write (size_t nvalues, const uInt64* value); void write (size_t nvalues, const Float* value); void write (size_t nvalues, const Double* value); void write (size_t nvalues, const Complex* value); void write (size_t nvalues, const DComplex* value); void write (size_t nvalues, const String* value); // }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/IO/ByteSinkSource.cc000066400000000000000000000035321476623553700177650ustar00rootroot00000000000000//# SinkSource.cc: Class for read/write access to data in a given format //# Copyright (C) 1996,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ByteSinkSource::ByteSinkSource () {} ByteSinkSource::ByteSinkSource (const std::shared_ptr& typeIO) : BaseSinkSource (typeIO) {} ByteSinkSource::ByteSinkSource (const ByteSinkSource& sinkSource) : BaseSinkSource (sinkSource), ByteSink (), ByteSource () {} ByteSinkSource& ByteSinkSource::operator= (const ByteSinkSource& sinkSource) { BaseSinkSource::operator= (sinkSource); return *this; } ByteSinkSource::~ByteSinkSource() {} } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/IO/ByteSinkSource.h000066400000000000000000000076171476623553700176370ustar00rootroot00000000000000//# ByteSinkSource.h: Class for read/write access to data in a given format //# Copyright (C) 1996,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_BYTESINKSOURCE_H #define CASA_BYTESINKSOURCE_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class TypeIO; //

        Class for read/write access to data in a given format. // // // // //
      • ByteSink class //
      • ByteSource class //
      • TypeIO class and derived classes // // // This class combines ByteSink and ByteSource. // // // ByteSinkSource provides read/write access to a typed byte stream in the // Casacore IO framework. It is derived from the classes ByteSink // and ByteSource, so it combines their functionality. //

        // The object is constructed using a typed byte stream. This stream // is an instance of a class derived from class // TypeIO. This makes it possible to // store the data in any format (e.g. CanonicalIO or RawIO). //
        In its turn TypeIO uses an instance of a class derived from class // ByteIO. This makes it possible to // use any output stream (e.g. file, memory). // // // // main // { // Bool valb = True; // RegularFileIO regularFileIO ("test.dat", ByteIO::New); // CanonicalIO canonicalIO(®ularFileIO); // ByteSinkSource sinkSource(&canonicalIO); // sinkSource << valb; // Write a boolean // sinkSource.seek (0); // Reset to begin of IO stream // sinkSource >> valb; // Read a boolean // cout << valb << endl; // Print the boolean // } // // // // This class makes it transparant to do IO with different devices and // in different ways. // class ByteSinkSource: public ByteSink, public ByteSource { public: // Default constructor. // This creates an invalid object, but is present for convenience. ByteSinkSource(); // Construct from given TypeIO object. // The constructor does not copy the object, but only keeps a pointer to it. ByteSinkSource (const std::shared_ptr& typeIO); // The copy constructor uses reference semantics ByteSinkSource (const ByteSinkSource& sinkSource); // The assignment operator uses reference semantics ByteSinkSource& operator= (const ByteSinkSource& sinkSource); ~ByteSinkSource(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/IO/ByteSource.cc000066400000000000000000000112601476623553700171350ustar00rootroot00000000000000//# ByteSource.cc: Class for read-only access to data in a given format //# Copyright (C) 1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ByteSource::ByteSource() {} ByteSource::ByteSource (const std::shared_ptr& typeIO) : BaseSinkSource (typeIO) { if (!isReadable()) { throw (AipsError ("ByteSource is not readable")); } } ByteSource::ByteSource (const ByteSource& source) : BaseSinkSource (source) {} ByteSource& ByteSource::operator= (const ByteSource& source) { BaseSinkSource::operator= (source); return *this; } ByteSource::~ByteSource() {} ByteSource& ByteSource::operator>> (Bool& value) { itsTypeIO->read (1, &value); return *this; } ByteSource& ByteSource::operator>> (Char& value) { itsTypeIO->read (1, &value); return *this; } ByteSource& ByteSource::operator>> (uChar& value) { itsTypeIO->read (1, &value); return *this; } ByteSource& ByteSource::operator>> (Short& value) { itsTypeIO->read (1, &value); return *this; } ByteSource& ByteSource::operator>> (uShort& value) { itsTypeIO->read (1, &value); return *this; } ByteSource& ByteSource::operator>> (Int& value) { itsTypeIO->read (1, &value); return *this; } ByteSource& ByteSource::operator>> (uInt& value) { itsTypeIO->read (1, &value); return *this; } ByteSource& ByteSource::operator>> (Int64& value) { itsTypeIO->read (1, &value); return *this; } ByteSource& ByteSource::operator>> (uInt64& value) { itsTypeIO->read (1, &value); return *this; } ByteSource& ByteSource::operator>> (Float& value) { itsTypeIO->read (1, &value); return *this; } ByteSource& ByteSource::operator>> (Double& value) { itsTypeIO->read (1, &value); return *this; } ByteSource& ByteSource::operator>> (Complex& value) { itsTypeIO->read (1, &value); return *this; } ByteSource& ByteSource::operator>> (DComplex& value) { itsTypeIO->read (1, &value); return *this; } ByteSource& ByteSource::operator>> (String& value) { itsTypeIO->read (1, &value); return *this; } void ByteSource::read (size_t nvalues, Bool* value) { itsTypeIO->read (nvalues, value); } void ByteSource::read (size_t nvalues, Char* value) { itsTypeIO->read (nvalues, value); } void ByteSource::read (size_t nvalues, uChar* value) { itsTypeIO->read (nvalues, value); } void ByteSource::read (size_t nvalues, Short* value) { itsTypeIO->read (nvalues, value); } void ByteSource::read (size_t nvalues, uShort* value) { itsTypeIO->read (nvalues, value); } void ByteSource::read (size_t nvalues, Int* value) { itsTypeIO->read (nvalues, value); } void ByteSource::read (size_t nvalues, uInt* value) { itsTypeIO->read (nvalues, value); } void ByteSource::read (size_t nvalues, Int64* value) { itsTypeIO->read (nvalues, value); } void ByteSource::read (size_t nvalues, uInt64* value) { itsTypeIO->read (nvalues, value); } void ByteSource::read (size_t nvalues, Float* value) { itsTypeIO->read (nvalues, value); } void ByteSource::read (size_t nvalues, Double* value) { itsTypeIO->read (nvalues, value); } void ByteSource::read (size_t nvalues, Complex* value) { itsTypeIO->read (nvalues, value); } void ByteSource::read (size_t nvalues, DComplex* value) { itsTypeIO->read (nvalues, value); } void ByteSource::read (size_t nvalues, String* value) { itsTypeIO->read (nvalues, value); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/IO/ByteSource.h000066400000000000000000000126031476623553700170010ustar00rootroot00000000000000//# ByteSource.h: Class for read-only access to data in a given format //# Copyright (C) 1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_BYTESOURCE_H #define CASA_BYTESOURCE_H #include #include //# The following should be a forward declaration. But our Complex & DComplex //# classes are a typedef hence this does not work. Replace the following with //# forward declarations when Complex and DComplex are no longer typedefs. #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class TypeIO; class String; //

        Class for read-only access to data in a given format. // // // // //
      • BaseSinkSource class //
      • TypeIO class and derived classes // // // A source is the place where bytes are read from. // // // ByteSource provides read-only access to a typed byte stream in the // Casacore IO framework. The base class BaseSinkSource // contains common functions like seek. //

        // The object is constructed using a typed byte stream. This stream // is an instance of a class derived from class // TypeIO. This makes it possible to // read the data in any format (e.g. CanonicalIO or RawIO). //
        In its turn TypeIO uses an instance of a class derived from class // ByteIO. This makes it possible to // use any input stream (e.g. file, memory). // // // // // Construct the correct input stream. // RegularFileIO filio ("file.name"); // CanonicalIO canio (&filio); // ByteSource source (&canio); // // Read data. // Int vali; // Bool flag; // source >> vali >> flag; // // // // This class makes it possible to deny write-access to an IO stream. // class ByteSource: virtual public BaseSinkSource { public: // Default constructor. // This creates an invalid object, but is present for convenience. ByteSource(); // Construct from given TypeIO object. // The constructor does not copy the object, but only keeps a pointer to it. ByteSource (const std::shared_ptr& typeIO); // The copy constructor uses reference semantics ByteSource (const ByteSource& source); // The assignment operator uses reference semantics ByteSource& operator= (const ByteSource& source); // destructor ~ByteSource(); // These functions read one value of the given type. // If this function does not succeed, an exception will be thrown. // ByteSource& operator>> (Bool& value); ByteSource& operator>> (Char& value); ByteSource& operator>> (uChar& value); ByteSource& operator>> (Short& value); ByteSource& operator>> (uShort& value); ByteSource& operator>> (Int& value); ByteSource& operator>> (uInt& value); ByteSource& operator>> (Int64& value); ByteSource& operator>> (uInt64& value); ByteSource& operator>> (Float& value); ByteSource& operator>> (Double& value); ByteSource& operator>> (Complex& value); ByteSource& operator>> (DComplex& value); ByteSource& operator>> (String& value); // // These functions read multiple values of the given type. // If this function does not succeed, an exception will be thrown. // void read (size_t nvalues, Bool* value); void read (size_t nvalues, Char* value); void read (size_t nvalues, uChar* value); void read (size_t nvalues, Short* value); void read (size_t nvalues, uShort* value); void read (size_t nvalues, Int* value); void read (size_t nvalues, uInt* value); void read (size_t nvalues, Int64* value); void read (size_t nvalues, uInt64* value); void read (size_t nvalues, Float* value); void read (size_t nvalues, Double* value); void read (size_t nvalues, Complex* value); void read (size_t nvalues, DComplex* value); void read (size_t nvalues, String* value); // protected: }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/IO/CanonicalIO.cc000066400000000000000000000355311476623553700171770ustar00rootroot00000000000000//# CanonicalIO.cc: Class for IO in canonical format //# Copyright (C) 1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN CanonicalIO::CanonicalIO (const std::shared_ptr& byteIO, uInt bufferLength) : TypeIO (byteIO), itsBuffer (new char[bufferLength]), itsBufferLength (bufferLength) {} CanonicalIO::CanonicalIO (const CanonicalIO& that) : TypeIO (that), itsBuffer (new char[that.itsBufferLength]), itsBufferLength (that.itsBufferLength) {} CanonicalIO& CanonicalIO::operator= (const CanonicalIO& that) { if (this != &that) { TypeIO::operator= (that); if (itsBufferLength != that.itsBufferLength) { delete [] itsBuffer; itsBufferLength = that.itsBufferLength; itsBuffer = new char[itsBufferLength]; } } return *this; } CanonicalIO::~CanonicalIO() { delete [] itsBuffer; } size_t CanonicalIO::write (size_t nvalues, const Bool* value) { return TypeIO::write (nvalues, value); } size_t CanonicalIO::write (size_t nvalues, const Char* value) { if (CONVERT_CAN_CHAR) { if (nvalues * SIZE_CAN_CHAR <= itsBufferLength) { CanonicalConversion::fromLocal (itsBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_CAN_CHAR, itsBuffer); } else { char* tempBuffer = new char [nvalues * SIZE_CAN_CHAR]; CanonicalConversion::fromLocal (tempBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_CAN_CHAR, tempBuffer); delete [] tempBuffer; } } else { itsByteIO->write (nvalues * sizeof(Char), value); } return nvalues * SIZE_CAN_CHAR; } size_t CanonicalIO::write (size_t nvalues, const uChar* value) { if (CONVERT_CAN_UCHAR) { if (nvalues * SIZE_CAN_UCHAR <= itsBufferLength) { CanonicalConversion::fromLocal (itsBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_CAN_UCHAR, itsBuffer); } else { char* tempBuffer = new char [nvalues * SIZE_CAN_UCHAR]; CanonicalConversion::fromLocal (tempBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_CAN_UCHAR, tempBuffer); delete [] tempBuffer; } } else { itsByteIO->write (nvalues * sizeof(uChar), value); } return nvalues * SIZE_CAN_UCHAR; } size_t CanonicalIO::write (size_t nvalues, const Short* value) { if (CONVERT_CAN_SHORT) { if (nvalues * SIZE_CAN_SHORT <= itsBufferLength) { CanonicalConversion::fromLocal (itsBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_CAN_SHORT, itsBuffer); } else { char* tempBuffer = new char [nvalues * SIZE_CAN_SHORT]; CanonicalConversion::fromLocal (tempBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_CAN_SHORT, tempBuffer); delete [] tempBuffer; } } else { itsByteIO->write (nvalues * sizeof(Short), value); } return nvalues * SIZE_CAN_SHORT; } size_t CanonicalIO::write (size_t nvalues, const uShort* value) { if (CONVERT_CAN_USHORT) { if (nvalues * SIZE_CAN_USHORT <= itsBufferLength) { CanonicalConversion::fromLocal (itsBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_CAN_USHORT, itsBuffer); } else { char* tempBuffer = new char [nvalues * SIZE_CAN_USHORT]; CanonicalConversion::fromLocal (tempBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_CAN_USHORT, tempBuffer); delete [] tempBuffer; } } else { itsByteIO->write (nvalues * sizeof(uShort), value); } return nvalues * SIZE_CAN_USHORT; } size_t CanonicalIO::write(size_t nvalues, const Int* value) { if (CONVERT_CAN_INT) { if (nvalues * SIZE_CAN_INT <= itsBufferLength) { CanonicalConversion::fromLocal (itsBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_CAN_INT, itsBuffer); } else { char* tempBuffer = new char [nvalues * SIZE_CAN_INT]; CanonicalConversion::fromLocal (tempBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_CAN_INT, tempBuffer); delete [] tempBuffer; } } else { itsByteIO->write (nvalues * sizeof(Int), value); } return nvalues * SIZE_CAN_INT; } size_t CanonicalIO::write(size_t nvalues, const uInt* value) { if (CONVERT_CAN_UINT) { if (nvalues * SIZE_CAN_UINT <= itsBufferLength) { CanonicalConversion::fromLocal (itsBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_CAN_UINT, itsBuffer); } else { char* tempBuffer = new char [nvalues * SIZE_CAN_UINT]; CanonicalConversion::fromLocal (tempBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_CAN_UINT, tempBuffer); delete [] tempBuffer; } } else { itsByteIO->write (nvalues * sizeof(uInt), value); } return nvalues * SIZE_CAN_UINT; } size_t CanonicalIO::write(size_t nvalues, const Int64* value) { if (CONVERT_CAN_INT64) { if (nvalues * SIZE_CAN_INT64 <= itsBufferLength) { CanonicalConversion::fromLocal (itsBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_CAN_INT64, itsBuffer); } else { char* tempBuffer = new char [nvalues * SIZE_CAN_INT64]; CanonicalConversion::fromLocal (tempBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_CAN_INT64, tempBuffer); delete [] tempBuffer; } } else { itsByteIO->write (nvalues * sizeof(Int64), value); } return nvalues * SIZE_CAN_INT64; } size_t CanonicalIO::write(size_t nvalues, const uInt64* value) { if (CONVERT_CAN_UINT64) { if (nvalues * SIZE_CAN_UINT64 <= itsBufferLength) { CanonicalConversion::fromLocal (itsBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_CAN_UINT64, itsBuffer); } else { char* tempBuffer = new char [nvalues * SIZE_CAN_UINT64]; CanonicalConversion::fromLocal (tempBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_CAN_UINT64, tempBuffer); delete [] tempBuffer; } } else { itsByteIO->write (nvalues * sizeof(uInt64), value); } return nvalues * SIZE_CAN_UINT64; } size_t CanonicalIO::write(size_t nvalues, const float* value) { if (CONVERT_CAN_FLOAT) { if (nvalues * SIZE_CAN_FLOAT <= itsBufferLength) { CanonicalConversion::fromLocal (itsBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_CAN_FLOAT, itsBuffer); } else { char* tempBuffer = new char [nvalues * SIZE_CAN_FLOAT]; CanonicalConversion::fromLocal (tempBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_CAN_FLOAT, tempBuffer); delete [] tempBuffer; } } else { itsByteIO->write (nvalues * sizeof(float), value); } return nvalues * SIZE_CAN_FLOAT; } size_t CanonicalIO::write(size_t nvalues, const double* value) { if (CONVERT_CAN_DOUBLE) { if (nvalues * SIZE_CAN_DOUBLE <= itsBufferLength) { CanonicalConversion::fromLocal (itsBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_CAN_DOUBLE, itsBuffer); } else { char* tempBuffer = new char [nvalues * SIZE_CAN_DOUBLE]; CanonicalConversion::fromLocal (tempBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_CAN_DOUBLE, tempBuffer); delete [] tempBuffer; } } else { itsByteIO->write (nvalues * sizeof(double), value); } return nvalues * SIZE_CAN_DOUBLE; } size_t CanonicalIO::write (size_t nvalues, const Complex* value) { return TypeIO::write (nvalues, value); } size_t CanonicalIO::write (size_t nvalues, const DComplex* value) { return TypeIO::write (nvalues, value); } size_t CanonicalIO::write (size_t nvalues, const String* value) { return TypeIO::write (nvalues, value); } size_t CanonicalIO::read (size_t nvalues, Bool* value) { return TypeIO::read (nvalues, value); } size_t CanonicalIO::read (size_t nvalues, Char* value) { if (CONVERT_CAN_CHAR) { if (nvalues * SIZE_CAN_CHAR <= itsBufferLength) { itsByteIO->read (nvalues * SIZE_CAN_CHAR, itsBuffer); CanonicalConversion::toLocal (value, itsBuffer, nvalues); } else { char* tempBuffer = new char[nvalues * SIZE_CAN_CHAR]; itsByteIO->read (nvalues * SIZE_CAN_CHAR, tempBuffer); CanonicalConversion::toLocal (value, tempBuffer, nvalues); delete [] tempBuffer; } } else { itsByteIO->read (nvalues * sizeof(Char), value); } return nvalues * SIZE_CAN_CHAR; } size_t CanonicalIO::read (size_t nvalues, uChar* value) { if (CONVERT_CAN_UCHAR) { if (nvalues * SIZE_CAN_UCHAR <= itsBufferLength) { itsByteIO->read (nvalues * SIZE_CAN_UCHAR, itsBuffer); CanonicalConversion::toLocal (value, itsBuffer, nvalues); } else { char* tempBuffer = new char[nvalues * SIZE_CAN_UCHAR]; itsByteIO->read (nvalues * SIZE_CAN_UCHAR, tempBuffer); CanonicalConversion::toLocal (value, tempBuffer, nvalues); delete [] tempBuffer; } } else { itsByteIO->read (nvalues * sizeof(uChar), value); } return nvalues * SIZE_CAN_UCHAR; } size_t CanonicalIO::read (size_t nvalues, Short* value) { if (CONVERT_CAN_SHORT) { if (nvalues * SIZE_CAN_SHORT <= itsBufferLength) { itsByteIO->read (nvalues * SIZE_CAN_SHORT, itsBuffer); CanonicalConversion::toLocal (value, itsBuffer, nvalues); } else { char* tempBuffer = new char[nvalues * SIZE_CAN_SHORT]; itsByteIO->read (nvalues * SIZE_CAN_SHORT, tempBuffer); CanonicalConversion::toLocal (value, tempBuffer, nvalues); delete [] tempBuffer; } } else { itsByteIO->read (nvalues * sizeof(Short), value); } return nvalues * SIZE_CAN_SHORT; } size_t CanonicalIO::read (size_t nvalues, uShort* value) { if (CONVERT_CAN_USHORT) { if (nvalues * SIZE_CAN_USHORT <= itsBufferLength) { itsByteIO->read (nvalues * SIZE_CAN_USHORT, itsBuffer); CanonicalConversion::toLocal (value, itsBuffer, nvalues); } else { char* tempBuffer = new char[nvalues * SIZE_CAN_USHORT]; itsByteIO->read (nvalues * SIZE_CAN_USHORT, tempBuffer); CanonicalConversion::toLocal (value, tempBuffer, nvalues); delete [] tempBuffer; } } else { itsByteIO->read (nvalues * sizeof(uShort), value); } return nvalues * SIZE_CAN_USHORT; } size_t CanonicalIO::read (size_t nvalues, Int* value) { if (CONVERT_CAN_INT) { if (nvalues * SIZE_CAN_INT <= itsBufferLength) { itsByteIO->read (nvalues * SIZE_CAN_INT, itsBuffer); CanonicalConversion::toLocal (value, itsBuffer, nvalues); } else { char* tempBuffer = new char[nvalues * SIZE_CAN_INT]; itsByteIO->read (nvalues * SIZE_CAN_INT, tempBuffer); CanonicalConversion::toLocal (value, tempBuffer, nvalues); delete [] tempBuffer; } } else { itsByteIO->read (nvalues * sizeof(Int), value); } return nvalues * SIZE_CAN_INT; } size_t CanonicalIO::read (size_t nvalues, uInt* value) { if (CONVERT_CAN_UINT) { if (nvalues * SIZE_CAN_UINT <= itsBufferLength) { itsByteIO->read (nvalues * SIZE_CAN_UINT, itsBuffer); CanonicalConversion::toLocal (value, itsBuffer, nvalues); } else { char* tempBuffer = new char[nvalues * SIZE_CAN_UINT]; itsByteIO->read (nvalues * SIZE_CAN_UINT, tempBuffer); CanonicalConversion::toLocal (value, tempBuffer, nvalues); delete [] tempBuffer; } } else { itsByteIO->read (nvalues * sizeof(uInt), value); } return nvalues * SIZE_CAN_UINT; } size_t CanonicalIO::read (size_t nvalues, Int64* value) { if (CONVERT_CAN_INT64) { if (nvalues * SIZE_CAN_INT64 <= itsBufferLength) { itsByteIO->read (nvalues * SIZE_CAN_INT64, itsBuffer); CanonicalConversion::toLocal (value, itsBuffer, nvalues); } else { char* tempBuffer = new char[nvalues * SIZE_CAN_INT64]; itsByteIO->read (nvalues * SIZE_CAN_INT64, tempBuffer); CanonicalConversion::toLocal (value, tempBuffer, nvalues); delete [] tempBuffer; } } else { itsByteIO->read (nvalues * sizeof(Int64), value); } return nvalues * SIZE_CAN_INT64; } size_t CanonicalIO::read (size_t nvalues, uInt64* value) { if (CONVERT_CAN_UINT64) { if (nvalues * SIZE_CAN_UINT64 <= itsBufferLength) { itsByteIO->read (nvalues * SIZE_CAN_UINT64, itsBuffer); CanonicalConversion::toLocal(value, itsBuffer, nvalues); } else { char* tempBuffer = new char[nvalues * SIZE_CAN_UINT64]; itsByteIO->read (nvalues * SIZE_CAN_UINT64, tempBuffer); CanonicalConversion::toLocal (value, tempBuffer, nvalues); delete [] tempBuffer; } } else { itsByteIO->read (nvalues * sizeof(uInt64), value); } return nvalues * SIZE_CAN_UINT64; } size_t CanonicalIO::read (size_t nvalues, float* value) { if (CONVERT_CAN_FLOAT) { if (nvalues * SIZE_CAN_FLOAT <= itsBufferLength) { itsByteIO->read (nvalues * SIZE_CAN_FLOAT, itsBuffer); CanonicalConversion::toLocal (value, itsBuffer, nvalues); } else { char* tempBuffer = new char[nvalues * SIZE_CAN_FLOAT]; itsByteIO->read (nvalues * SIZE_CAN_FLOAT, tempBuffer); CanonicalConversion::toLocal (value, tempBuffer, nvalues); delete [] tempBuffer; } } else { itsByteIO->read (nvalues * sizeof(float), value); } return nvalues * SIZE_CAN_FLOAT; } size_t CanonicalIO::read (size_t nvalues, double* value) { if (CONVERT_CAN_DOUBLE) { if (nvalues * SIZE_CAN_DOUBLE <= itsBufferLength) { itsByteIO->read (nvalues * SIZE_CAN_DOUBLE, itsBuffer); CanonicalConversion::toLocal (value, itsBuffer, nvalues); } else { char* tempBuffer = new char[nvalues * SIZE_CAN_DOUBLE]; itsByteIO->read (nvalues * SIZE_CAN_DOUBLE, tempBuffer); CanonicalConversion::toLocal (value, tempBuffer, nvalues); delete [] tempBuffer; } } else { itsByteIO->read (nvalues * sizeof(double), value); } return nvalues * SIZE_CAN_DOUBLE; } size_t CanonicalIO::read (size_t nvalues, Complex* value) { return TypeIO::read (nvalues, value); } size_t CanonicalIO::read (size_t nvalues, DComplex* value) { return TypeIO::read (nvalues, value); } size_t CanonicalIO::read (size_t nvalues, String* value) { return TypeIO::read (nvalues, value); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/IO/CanonicalIO.h000066400000000000000000000132621476623553700170360ustar00rootroot00000000000000//# CanonicalIO.h: Class for IO in canonical format //# Copyright (C) 1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_CANONICALIO_H #define CASA_CANONICALIO_H #include #include //# The following should be a forward declaration. But our Complex & DComplex //# classes are a typedef hence this does not work. Replace the following with //# forward declarations when Complex and DComplex are no longer typedefs. #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class ByteIO; class String; //

        Class for IO in canonical format. // // // // //
      • ByteIO class and derived classes //
      • TypeIOclass //
      • CanonicalConversion // // // CanonicalIO is a specialization of class TypeIO to store // data in canonical format. //

        // The class converts the data to/from canonical data and reads/writes // them from/into the ByteIO object given at construction time. // Conversion is only done when really needed. If not needed, the // data is directly read or written. //

        // Canonical format is big-endian IEEE format, where longs are 8 bytes. // Bools are stored as bits to be as space-efficient as possible. // This means that on a 32-bit SUN or HP conversions only have to be done // for Bools and longs. For a DEC-alpha, however, the data will always // be converted because it is a little-endian machine. // class CanonicalIO: public TypeIO { public: // Constructor. // The read/write functions will use the given ByteIO object // as the data store. //

        // The read and write functions use an intermediate buffer to hold the data // in canonical format. For small arrays it uses a fixed buffer with // length bufferLength. For arrays not fitting in this buffer, // it uses a temporary buffer allocated on the heap. explicit CanonicalIO (const std::shared_ptr& byteIO, uInt bufferLength=4096); // The copy constructor uses reference semantics CanonicalIO (const CanonicalIO& canonicalIO); // The assignment operator uses reference semantics CanonicalIO& operator= (const CanonicalIO& canonicalIO); // Destructor, deletes allocated memory. ~CanonicalIO(); // Convert the values and write them to the ByteIO object. // Bool, complex and String values are handled by the base class. // virtual size_t write (size_t nvalues, const Bool* value); virtual size_t write (size_t nvalues, const Char* data); virtual size_t write (size_t nvalues, const uChar* data); virtual size_t write (size_t nvalues, const Short* data); virtual size_t write (size_t nvalues, const uShort* data); virtual size_t write (size_t nvalues, const Int* data); virtual size_t write (size_t nvalues, const uInt* data); virtual size_t write (size_t nvalues, const Int64* data); virtual size_t write (size_t nvalues, const uInt64* data); virtual size_t write (size_t nvalues, const Float* data); virtual size_t write (size_t nvalues, const Double* data); virtual size_t write (size_t nvalues, const Complex* value); virtual size_t write (size_t nvalues, const DComplex* value); virtual size_t write (size_t nvalues, const String* value); // // Read the values from the ByteIO object and convert them. // Bool, complex and String values are handled by the base class. // virtual size_t read (size_t nvalues, Bool* value); virtual size_t read (size_t nvalues, Char* data); virtual size_t read (size_t nvalues, uChar* data); virtual size_t read (size_t nvalues, Short* data); virtual size_t read (size_t nvalues, uShort* data); virtual size_t read (size_t nvalues, Int* data); virtual size_t read (size_t nvalues, uInt* data); virtual size_t read (size_t nvalues, Int64* data); virtual size_t read (size_t nvalues, uInt64* data); virtual size_t read (size_t nvalues, Float* data); virtual size_t read (size_t nvalues, Double* data); virtual size_t read (size_t nvalues, Complex* value); virtual size_t read (size_t nvalues, DComplex* value); virtual size_t read (size_t nvalues, String* value); // private: //# The buffer char* itsBuffer; uInt itsBufferLength; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/IO/ConversionIO.cc000066400000000000000000000140541476623553700174320ustar00rootroot00000000000000//# ConversionIO.cc: Class for IO in a converted format //# Copyright (C) 1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ConversionIO::ConversionIO (const std::shared_ptr& dataConversion, const std::shared_ptr& byteIO, uInt bufferLength) : TypeIO (byteIO), itsConversion (dataConversion), itsBuffer (new char[bufferLength]), itsBufferLength (bufferLength) { init(); } ConversionIO::ConversionIO (const ConversionIO& that) : TypeIO (that), itsConversion (that.itsConversion), itsBuffer (new char[that.itsBufferLength]), itsBufferLength (that.itsBufferLength) { init(); } ConversionIO& ConversionIO::operator= (const ConversionIO& that) { if (this != &that) { TypeIO::operator= (that); itsConversion = that.itsConversion; if (itsBufferLength != that.itsBufferLength) { delete [] itsBuffer; itsBufferLength = that.itsBufferLength; itsBuffer = new char[itsBufferLength]; } init(); } return *this; } ConversionIO::~ConversionIO() { delete [] itsBuffer; } void ConversionIO::init() { itsCopyChar = itsConversion->canCopy (static_cast(0)); itsCopyuChar = itsConversion->canCopy (static_cast(0)); itsCopyShort = itsConversion->canCopy (static_cast(0)); itsCopyuShort = itsConversion->canCopy (static_cast(0)); itsCopyInt = itsConversion->canCopy (static_cast(0)); itsCopyuInt = itsConversion->canCopy (static_cast(0)); itsCopyInt64 = itsConversion->canCopy (static_cast(0)); itsCopyuInt64 = itsConversion->canCopy (static_cast(0)); itsCopyFloat = itsConversion->canCopy (static_cast(0)); itsCopyDouble = itsConversion->canCopy (static_cast(0)); itsSizeChar = itsConversion->externalSize (static_cast(0)); itsSizeuChar = itsConversion->externalSize (static_cast(0)); itsSizeShort = itsConversion->externalSize (static_cast(0)); itsSizeuShort = itsConversion->externalSize (static_cast(0)); itsSizeInt = itsConversion->externalSize (static_cast(0)); itsSizeuInt = itsConversion->externalSize (static_cast(0)); itsSizeInt64 = itsConversion->externalSize (static_cast(0)); itsSizeuInt64 = itsConversion->externalSize (static_cast(0)); itsSizeFloat = itsConversion->externalSize (static_cast(0)); itsSizeDouble = itsConversion->externalSize (static_cast(0)); } size_t ConversionIO::write (size_t nvalues, const Bool* value) { return TypeIO::write (nvalues, value); } size_t ConversionIO::write (size_t nvalues, const Complex* value) { return TypeIO::write (nvalues, value); } size_t ConversionIO::write (size_t nvalues, const DComplex* value) { return TypeIO::write (nvalues, value); } size_t ConversionIO::write (size_t nvalues, const String* value) { return TypeIO::write (nvalues, value); } size_t ConversionIO::read (size_t nvalues, Bool* value) { return TypeIO::read (nvalues, value); } size_t ConversionIO::read (size_t nvalues, Complex* value) { return TypeIO::read (nvalues, value); } size_t ConversionIO::read (size_t nvalues, DComplex* value) { return TypeIO::read (nvalues, value); } size_t ConversionIO::read (size_t nvalues, String* value) { return TypeIO::read (nvalues, value); } #define CONVERSIONIO_DOIT(T) \ size_t ConversionIO::write (size_t nvalues, const T* value) \ { \ size_t size = nvalues * aips_name2(itsSize,T); \ if (aips_name2(itsCopy,T)) { \ itsByteIO->write (size, value); \ } else { \ if (size <= itsBufferLength) { \ itsConversion->fromLocal (itsBuffer, value, nvalues); \ itsByteIO->write (size, itsBuffer); \ } else { \ char* tempBuffer = new char [size]; \ itsConversion->fromLocal (tempBuffer, value, nvalues); \ itsByteIO->write (size, tempBuffer); \ delete [] tempBuffer; \ } \ } \ return size; \ } \ size_t ConversionIO::read (size_t nvalues, T* value) \ { \ size_t size = nvalues * aips_name2(itsSize,T); \ if (aips_name2(itsCopy,T)) { \ itsByteIO->read (size, value); \ } else { \ if (size <= itsBufferLength) { \ itsByteIO->read (size, itsBuffer); \ itsConversion->toLocal (value, itsBuffer, nvalues); \ } else { \ char* tempBuffer = new char[size]; \ itsByteIO->read (size, tempBuffer); \ itsConversion->toLocal (value, tempBuffer, nvalues); \ delete [] tempBuffer; \ } \ } \ return size; \ } CONVERSIONIO_DOIT(Char) CONVERSIONIO_DOIT(uChar) CONVERSIONIO_DOIT(Short) CONVERSIONIO_DOIT(uShort) CONVERSIONIO_DOIT(Int) CONVERSIONIO_DOIT(uInt) CONVERSIONIO_DOIT(Int64) CONVERSIONIO_DOIT(uInt64) CONVERSIONIO_DOIT(Float) CONVERSIONIO_DOIT(Double) } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/IO/ConversionIO.h000066400000000000000000000146371476623553700173030ustar00rootroot00000000000000//# ConversionIO.h: Class for IO in a converted format //# Copyright (C) 1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_CONVERSIONIO_H #define CASA_CONVERSIONIO_H #include #include #include //# The following should be a declaration. But our Complex & DComplex classes //# are a typedef hence this does not work. Replace the following with //# forward declarations when Complex and DComplex are no longer typedefs. #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class DataConversion; class ByteIO; class String; //

        Class for IO in a converted format. // // // // //
      • ByteIO class and derived classes //
      • TypeIOclass //
      • DataConversion // // // ConversionIO is a specialization of class TypeIO to store // data in a converted format. //

        // The class converts the data to/from external data and reads/writes // them from/into the ByteIO object given at construction time. // Conversion is only done when really needed. If not needed, the // data is directly read or written. //

        // This class is useful when data can be stored in one of multiple formats. // Only at construction time the correct // DataConversion class has to be given. Thereafter polymorphism // ensures that the correct conversion is done when reading or writing. // class ConversionIO: public TypeIO { public: // Constructor. // The read/write functions will use the given ByteIO object // as the data store and the given DataConversion object // as the conversion engine. //

        // The read and write functions use an intermediate buffer to hold the data // in canonical format. For small arrays it uses a fixed buffer with // length bufferLength. For arrays not fitting in this buffer, // it uses a temporary buffer allocated on the heap. ConversionIO (const std::shared_ptr& dataConversion, const std::shared_ptr& byteIO, uInt bufferLength=4096); // The copy constructor uses reference semantics ConversionIO (const ConversionIO& conversionIO); // The assignment operator uses reference semantics ConversionIO& operator= (const ConversionIO& conversionIO); // Destructor, deletes allocated memory. ~ConversionIO(); // Convert the values and write them to the ByteIO object. // Bool, complex and String values are handled by the base class. // virtual size_t write (size_t nvalues, const Bool* value); virtual size_t write (size_t nvalues, const Char* data); virtual size_t write (size_t nvalues, const uChar* data); virtual size_t write (size_t nvalues, const Short* data); virtual size_t write (size_t nvalues, const uShort* data); virtual size_t write (size_t nvalues, const Int* data); virtual size_t write (size_t nvalues, const uInt* data); virtual size_t write (size_t nvalues, const Int64* data); virtual size_t write (size_t nvalues, const uInt64* data); virtual size_t write (size_t nvalues, const Float* data); virtual size_t write (size_t nvalues, const Double* data); virtual size_t write (size_t nvalues, const Complex* value); virtual size_t write (size_t nvalues, const DComplex* value); virtual size_t write (size_t nvalues, const String* value); // // Read the values from the ByteIO object and convert them. // Bool, complex and String values are handled by the base class. // virtual size_t read (size_t nvalues, Bool* value); virtual size_t read (size_t nvalues, Char* data); virtual size_t read (size_t nvalues, uChar* data); virtual size_t read (size_t nvalues, Short* data); virtual size_t read (size_t nvalues, uShort* data); virtual size_t read (size_t nvalues, Int* data); virtual size_t read (size_t nvalues, uInt* data); virtual size_t read (size_t nvalues, Int64* data); virtual size_t read (size_t nvalues, uInt64* data); virtual size_t read (size_t nvalues, Float* data); virtual size_t read (size_t nvalues, Double* data); virtual size_t read (size_t nvalues, Complex* value); virtual size_t read (size_t nvalues, DComplex* value); virtual size_t read (size_t nvalues, String* value); // private: // Initialize the itsSize and itsCopy variables. void init(); //# The data. std::shared_ptr itsConversion; uInt itsSizeChar; uInt itsSizeuChar; uInt itsSizeShort; uInt itsSizeuShort; uInt itsSizeInt; uInt itsSizeuInt; uInt itsSizeInt64; uInt itsSizeuInt64; uInt itsSizeFloat; uInt itsSizeDouble; Bool itsCopyChar; Bool itsCopyuChar; Bool itsCopyShort; Bool itsCopyuShort; Bool itsCopyInt; Bool itsCopyuInt; Bool itsCopyInt64; Bool itsCopyuInt64; Bool itsCopyFloat; Bool itsCopyDouble; //# The buffer char* itsBuffer; uInt itsBufferLength; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/IO/FileLocker.cc000066400000000000000000000155571476623553700171050ustar00rootroot00000000000000//# FileLocker.cc: Class to handle file locking //# Copyright (C) 1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include //# Locking is not supported on Cray compute nodes. #if defined(AIPS_CRAY_PGI) && !defined(AIPS_NOFILELOCK) # define AIPS_NOFILELOCK 1 #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN FileLocker::FileLocker() : itsFD (-1), itsError (0), itsStart (0), itsLength (0), itsMsgShown (False), itsReadLocked (False), itsWriteLocked (False) {} FileLocker::FileLocker (int fd, uInt start, uInt length) : itsFD (fd), itsError (0), itsStart (start), itsLength (length), itsMsgShown (False), itsReadLocked (False), itsWriteLocked (False) {} FileLocker::~FileLocker() {} Bool FileLocker::acquire (LockType type, uInt nattempts) { itsError = 0; // Always success if locking is not supported. #if defined(AIPS_NOFILELOCK) itsReadLocked = True; if (!itsWriteLocked && type == Write) { itsWriteLocked = True; } return True; #else struct flock ls; ls.l_whence = SEEK_SET; ls.l_start = itsStart; ls.l_len = itsLength; ls.l_type = F_WRLCK; // When a read-lock is acquired, it may release an existing write-lock. // We do not want that to happen, so when it is write-locked, test // if the write-lock is still valid. if (type == Read) { if (itsWriteLocked) { if (fcntl (itsFD, F_SETLK, &ls) != -1) { /// cout << "kept " << itsReadLocked << ' ' < namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class String; //

        // Class to handle file locking. // // // // // //
      • man page of fcntl // // // This class handles file locking by means of the fcntl SETLK function. // Locking of files on NFS-mounted file systems works correctly // as long as the NFS lockd and statd deamons are configured correctly. // Otherwise lock requests may be granted incorrectly. //

        // Acquiring a lock can be done for a read or a write lock. // Multiple locks on a file can exist as long as they are all // read locks. When a write lock is involved, no other lock can exist. // It is possible to acquire a lock in 2 ways: //

          //
        • Wait until the lock request is granted; i.e. until the processes // holding a lock on the file release their lock. //
        • Do several attempts; between each attempt it sleeps 1 second. // Note that nattempts=1 means it returns immediately when the // lock request could not be granted. //
        //
        // // // int fd = open ("file.name"); // FileLocker lock(fd); // if (lock.acquire()) { // ... do something with the file ... // lock.release(); // }else{ // cout << lock.lastMessage() << endl; // } // // // // Make it possible to lock files in a standard way. // class FileLocker { public: // Define the possible lock types. enum LockType { // Acquire a read lock. Read, // Acquire a write lock. Write }; // Default constructor creates an invalid fd. FileLocker(); // Construct the FileLocker object for the given file descriptor. // This can be used to lock a segment of the given file. // The segment is given by start and length. Length=0 means till the // end of the file. explicit FileLocker (int fd, uInt start=0, uInt length=0); ~FileLocker(); // Acquire a write or read lock. // nattempts defines how often it tries to acquire the lock. // A zero value indicates an infinite number of times (i.e. wait until // the lock is acquired). // A positive value means it waits 1 second between each attempt. Bool acquire (LockType = Write, uInt nattempts = 0); // Release a lock. // The return status indicates if an error occurred. Bool release(); // Test if the file can be locked for read or write. // Optionally the PID of the process holding the lock is returned. // Bool canLock (LockType = Write); Bool canLock (uInt& pid, LockType = Write); // // Test if the process has a lock for read or write on the file. Bool hasLock (LockType = Write) const; // Get the fd in use. int fd() const; // Get the last error. int lastError() const; // Get the message belonging to the last error. String lastMessage() const; private: int itsFD; int itsError; int itsStart; int itsLength; Bool itsMsgShown; /// temporary for SUSE 6.1 Bool itsReadLocked; Bool itsWriteLocked; }; inline Bool FileLocker::hasLock (LockType type) const { return (type == Write ? itsWriteLocked : itsReadLocked); } inline int FileLocker::fd() const { return itsFD; } inline int FileLocker::lastError() const { return itsError; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/IO/FileUnbufferedIO.cc000066400000000000000000000043731476623553700201750ustar00rootroot00000000000000//# FileUnbufferedIO.cc: Class for unbuffered IO on a file //# Copyright (C) 2022 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN FileUnbufferedIO::FileUnbufferedIO (const RegularFile& fileName, ByteIO::OpenOption option, Bool useODirect) : itsUseODirect (useODirect) { attach (RegularFileIO::openCreate (fileName, option, useODirect), fileName.path().originalName()); } FileUnbufferedIO::~FileUnbufferedIO() { close (fd()); detach(); } void FileUnbufferedIO::reopenRW() { if (isWritable()) { return; } // First try if the file can be opened as read/write. // An exception is thrown if not possible. int newfd = RegularFileIO::openCreate (fileName(), ByteIO::Update, itsUseODirect); // Now close the readonly file and reset fd. close (fd()); detach(); attach (newfd, fileName()); setWritable(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/IO/FileUnbufferedIO.h000066400000000000000000000070261476623553700200350ustar00rootroot00000000000000//# FileUnbufferedIO.h: Class for unbuffered IO on a file //# Copyright (C) 2022 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_FILEUNBUFFEREDIO_H #define CASA_FILEUNBUFFEREDIO_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Class for unbuffered IO on a file. // // // // // //
      • ByteIO class //
      • file descriptors // // // This class is a specialization of class // FiledesIO to make it easier to open a file. // It reads/writes the data directly instead of using a buffer to reduce the // number of IO-operations. // It is meant for file access using large reads and writes as done by MultiFile. // // Optionally the file can be used with O_DIRECT to bypass the system's file cache // for more predictable IO behaviour which can be of importance in // real-time applications. // // // Make it possible to use the Casacore IO functionality on any file. // In this way any device can be hooked to the IO framework. // class FileUnbufferedIO: public FiledesIO { public: // The constructor opens or creates a file. // For option NewNoReplace it is checked if the file does not exist yet. // If useODirect=True and if supported by the OS, the file will be opened // with O_DIRECT which bypasses the kernel's file cache for more predictable // I/O behaviour. It requires the size and the alignment of the data read // or written to be a multiple of the the disk's logical block size. explicit FileUnbufferedIO (const RegularFile& fileName, ByteIO::OpenOption option, Bool useODirect=False); // The destructor closes the file. ~FileUnbufferedIO() override; // Copy constructor and assignment cannot be used. FileUnbufferedIO (const FileUnbufferedIO&) = delete; FileUnbufferedIO& operator= (const FileUnbufferedIO&) = delete; // Reopen the file for read/write. // Nothing is done if already opened for read/write. // An exception is thrown if the file is readonly. void reopenRW() override; private: Bool itsUseODirect; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/IO/FilebufIO.cc000066400000000000000000000262021476623553700166570ustar00rootroot00000000000000//# FilebufIO.cc: Class for buffered IO on a file. //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include // needed for errno #include // needed for strerror namespace casacore { //# NAMESPACE CASACORE - BEGIN FilebufIO::FilebufIO() : itsSeekable (False), itsReadable (False), itsWritable (False), itsFile (-1), itsBufSize (0), itsBufLen (0), itsBuffer (0), itsBufOffset (-1), itsOffset (-1), itsSeekOffset (-1), itsDirty (False) {} FilebufIO::FilebufIO (int fd, uInt bufferSize) : itsFile (-1), itsBufSize (0), itsBufLen (0), itsBuffer (0), itsBufOffset (-1), itsOffset (-1), itsSeekOffset (-1), itsDirty (False) { attach (fd, bufferSize); } FilebufIO::~FilebufIO() { detach(); } void FilebufIO::attach (int fd, uInt bufSize) { AlwaysAssert (itsFile == -1, AipsError); itsFile = fd; itsOffset = 0; itsSeekOffset = -1; itsDirty = False; fillRWFlags (fd); fillSeekable(); setBuffer (bufSize); } void FilebufIO::setBuffer (Int64 bufSize) { if (itsBuffer) { flush(); delete [] itsBuffer; itsBuffer = 0; itsBufSize = 0; itsBufLen = 0; itsBufOffset = -1; } if (bufSize > 0) { itsBuffer = new char[bufSize]; itsBufSize = bufSize; itsBufOffset = -Int(itsBufSize+1); } } void FilebufIO::detach (Bool closeFile) { setBuffer (0); if (closeFile && itsFile >= 0) { ::traceCLOSE (itsFile); } itsFile = -1; } void FilebufIO::fillRWFlags (int fd) { itsReadable = False; itsWritable = False; int flags = fcntl (fd, F_GETFL); if ((flags & O_RDWR) == O_RDWR) { itsReadable = True; itsWritable = True; } else if ((flags & O_WRONLY) == O_WRONLY) { itsWritable = True; } else { itsReadable = True; } } void FilebufIO::fillSeekable() { Int64 curOff = itsOffset; itsSeekable = (doSeek (0, ByteIO::End) >= 0); itsOffset = curOff; } String FilebufIO::fileName() const { return ""; } void FilebufIO::flush() { if (itsDirty) { writeBuffer (itsBufOffset, itsBuffer, itsBufLen); itsDirty = False; } } void FilebufIO::truncate (Int64 size) { ::ftruncate (itsFile, size); } void FilebufIO::resync() { AlwaysAssert (!itsDirty, AipsError); itsBufLen = 0; itsBufOffset = -Int(itsBufSize+1); itsOffset = 0; itsSeekOffset = -1; } void FilebufIO::writeBuffer (Int64 offset, const char* buf, Int64 size) { if (size > 0) { if (offset != itsSeekOffset) { ::traceLSEEK (itsFile, offset, SEEK_SET); itsSeekOffset = offset; } if (::traceWRITE (itsFile, const_cast(buf), size) != size) { int error = errno; itsSeekOffset = -1; throw AipsError (String("FilebufIO: write error for file ") + fileName() + ": " + strerror(error)); } itsSeekOffset += size; } } Int64 FilebufIO::readBuffer (Int64 offset, char* buf, Int64 size, Bool throwException) { if (offset != itsSeekOffset) { ::traceLSEEK (itsFile, offset, SEEK_SET); itsSeekOffset = offset; } Int64 bytesRead = ::traceREAD (itsFile, buf, size); int error = errno; if (bytesRead > Int(size)) { // Should never be executed itsSeekOffset = -1; throw AipsError ("FilebufIO::read - read returned a bad value" " for file " + fileName()); } if (bytesRead != Int(size) && throwException == True) { //# In case of a table reparation the remainder has to be filled with 0. #if defined(TABLEREPAIR) memset ((char*)buf + bytesRead, 0, size-bytesRead); bytesRead = size; #endif if (bytesRead < 0) { itsSeekOffset = -1; throw AipsError (String("FilebufIO::read error for file ") + fileName() + ": " + strerror(error)); } else if (bytesRead < Int(size)) { itsSeekOffset = -1; throw AipsError ("FilebufIO::read - incorrect number of bytes (" + String::toString(bytesRead) + " out of " + String::toString(size) + ") read for file " + fileName()); } } itsSeekOffset += bytesRead; return bytesRead; } void FilebufIO::write (Int64 size, const void* buf) { // Throw an exception if not writable. if (!itsWritable) { throw AipsError ("FilebufIO object (file " + fileName() + ") is not writable"); } const char* bufc = static_cast(buf); // Determine blocknr of first and last full block. Int64 st = (itsOffset + itsBufSize - 1) / itsBufSize; Int64 end = (itsOffset + size) / itsBufSize; Int64 blkst = st * itsBufSize - itsOffset; Int64 sz = 0; if (st < end) { sz = (end-st) * itsBufSize; // There are one or more full blocks. // Write them all. writeBuffer (st*itsBufSize, bufc+blkst, sz); // Discard the current buffer if within these full blocks. if (st*itsBufSize <= itsBufOffset && end*itsBufSize >= itsBufOffset+itsBufSize) { itsDirty = False; itsBufOffset = -Int(itsBufSize+1); itsBufLen = 0; } } // Write the start of the user buffer (if needed). // Note that by doing this after the full block write, we avoid a // possible flush of the current buffer if it is within the full blocks. if (blkst > 0) { if (blkst > size) { blkst = size; } writeBlock (blkst, bufc); } // Write the remainder of the user buffer (if needed). // First update the offset in the stream. blkst += sz; itsOffset += blkst; if (blkst < size) { size -= blkst; writeBlock (size, bufc+blkst); itsOffset += size; } } Int64 FilebufIO::read (Int64 size, void* buf, Bool throwException) { // Throw an exception if not readable. if (!itsReadable) { throw AipsError ("FilebufIO object (file " + fileName() + ") is not readable"); } char* bufc = static_cast(buf); // Determine blocknr of first and last full block. Int64 st = (itsOffset + itsBufSize - 1) / itsBufSize; Int64 end = (itsOffset + size) / itsBufSize; Int64 blkst = st * itsBufSize - itsOffset; Int64 sz = 0; if (st < end) { // There are one or more full blocks. // Read them all. // Handle the case that one of them is the current buffer. Int64 stoff = st*itsBufSize; Int64 endoff = end*itsBufSize; char* bufp = bufc+blkst; if (stoff <= itsBufOffset && endoff >= itsBufOffset+itsBufSize) { if (stoff < itsBufOffset) { sz = itsBufOffset-stoff; AlwaysAssert (readBuffer (stoff, bufp, sz, throwException) == sz, AipsError); bufp += sz; } // Do the get of the current block via readBlock to handle // the (hardly possible) case that itsBufSize!=itsBufLen. Int64 savoff = itsOffset; itsOffset = itsBufOffset; sz += readBlock (itsBufSize, bufp, throwException); itsOffset = savoff; stoff = itsBufOffset + itsBufSize; } // Read the remaining full blocks. sz += readBuffer (stoff, bufp, endoff-stoff, throwException); } // Read the start of the user buffer (if needed). Int64 total = sz; if (blkst > 0) { if (blkst > size) { blkst = size; } total += readBlock (blkst, bufc, throwException); } // Read the remainder of the user buffer (if needed). // First update the offset in the stream. blkst += sz; itsOffset += blkst; if (blkst < size) { sz = size - blkst; total += readBlock (sz, bufc+blkst, throwException); itsOffset += sz; } return total; } void FilebufIO::writeBlock (Int64 size, const char* buf) { // Write a part of a block. // It is ensured that the buffer fits in a single block and that it // is not a full block. // Flush current buffer if needed. if (itsOffset < itsBufOffset || itsOffset >= itsBufOffset + itsBufSize) { if (itsDirty) { flush(); } // Read the new buffer. itsBufOffset = itsOffset / itsBufSize * itsBufSize; itsBufLen = readBuffer (itsBufOffset, itsBuffer, itsBufSize, False); } Int64 st = itsOffset - itsBufOffset; memcpy (itsBuffer+st, buf, size); itsDirty = True; if (st+size > itsBufLen) { itsBufLen = st+size; } } Int64 FilebufIO::readBlock (Int64 size, char* buf, Bool throwException) { // Read a part of a block. // It is ensured that the buffer fits in a single block and that it // is not a full block. // Read current buffer if needed. Flush if current block is dirty. if (itsOffset < itsBufOffset || itsOffset >= itsBufOffset + itsBufSize) { if (itsDirty) { flush(); } // Read the new buffer. itsBufOffset = itsOffset / itsBufSize * itsBufSize; itsBufLen = readBuffer (itsBufOffset, itsBuffer, itsBufSize, False); } Int64 st = itsOffset - itsBufOffset; #if defined(TABLEREPAIR) if (st+size > itsBufLen) { memset (itsBuffer+itsBufLen, 0, st+size-itsBufLen); itsBufLen = st+size; } #endif if (st+size > itsBufLen) { if (throwException) { throw AipsError ("FilebufIO::readBlock - incorrect number of bytes" " read for file " + fileName()); } if (itsBufLen > st) { size = itsBufLen-st; } else { size = 0; } } memcpy (buf, itsBuffer+st, size); return size; } Int64 FilebufIO::doSeek (Int64 offset, ByteIO::SeekOption dir) { switch (dir) { case ByteIO::Begin: itsOffset = offset; break; case ByteIO::End: itsSeekOffset = ::traceLSEEK (itsFile, offset, SEEK_END); itsOffset = itsSeekOffset; break; default: itsOffset += offset; break; } return itsOffset; } Int64 FilebufIO::length() { itsSeekOffset = ::traceLSEEK (itsFile, 0, SEEK_END); Int64 len = itsBufOffset+itsBufLen; if (len < itsSeekOffset) { len = itsSeekOffset; } return len; } Bool FilebufIO::isReadable() const { return itsReadable; } Bool FilebufIO::isWritable() const { return itsWritable; } Bool FilebufIO::isSeekable() const { return itsSeekable; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/IO/FilebufIO.h000066400000000000000000000160101476623553700165150ustar00rootroot00000000000000//# FilebufIO.h: Class for buffered IO on a file //# Copyright (C) 1996,1997,1999,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_FILEBUFIO_H #define CASA_FILEBUFIO_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Class for buffered IO on a file. // // // // //
      • ByteIO // // // This class is a specialization of class // ByteIO. // This class is doing IO on a file in a buffered way to reduce the number // of file accesses as much as possible. // It is part of the entire IO framework. It can for // instance be used to store data in canonical format in a file // in an IO-efficient way //
        // The buffer size is dynamic, so any time it can be set as needed. //

        // It is also possible to construct a FilebufIO object // from a file descriptor (e.g. for a pipe or socket). // The constructor will determine automatically if the file is // readable, writable and seekable. // // // This example shows how FilebufIO can be used with an fd. // It uses the fd for a regular file, which could be done in an easier // way using class RegularFileIO. // However, when using pipes or sockets, this would be the only way. // // // Get a file descriptor for the file. // int fd = open ("file.name"); // // Use that as the source of AipsIO (which will also use CanonicalIO). // FilebufIO fio (fd); // AipsIO stream (&fio); // // Read the data. // Int vali; // Bool valb; // stream >> vali >> valb; // // // // The stdio package was used, but it proved to be very slow on SOlaris. // After a seek the buffer was refreshed, which increased the number // of file accesses enormously. // Also the interaction between reads and writes in stdio was poor. // class FilebufIO: public ByteIO { public: // Default constructor. // A stream can be attached using the attach function. FilebufIO(); // Construct from the given file descriptor. // Note that the destructor and the detach function implicitly close // the file descriptor. explicit FilebufIO (int fd, uInt bufferSize=16384); // Attach to the given file descriptor. // Note that the destructor and the detach function implicitly close // the file descriptor. void attach (int fd, uInt bufferSize=16384); // The destructor closes the file when it was owned and opened and not // closed yet. virtual ~FilebufIO(); // Write the number of bytes. virtual void write (Int64 size, const void* buf); // Read size bytes from the File. Returns the number of bytes // actually read. Will throw an exception (AipsError) if the requested // number of bytes could not be read unless throwException is set to // False. Will always throw an exception if the file is not readable or // the system call returns an undocumented value. virtual Int64 read (Int64 size, void* buf, Bool throwException=True); // Flush the current buffer. virtual void flush(); // Resync the file (i.e. empty the current buffer). virtual void resync(); // Truncate the file to the given size. virtual void truncate (Int64 size); // Get the length of the byte stream. virtual Int64 length(); // Is the IO stream readable? virtual Bool isReadable() const; // Is the IO stream writable? virtual Bool isWritable() const; // Is the IO stream seekable? virtual Bool isSeekable() const; // Get the file name of the file attached. virtual String fileName() const; // Get the buffer size. uInt bufferSize() const { return itsBufSize; } protected: // Detach the FILE. Close it when needed. void detach (Bool closeFile=False); // Determine if the file descriptor is readable and/or writable. void fillRWFlags (int fd); // Determine if the file is seekable. void fillSeekable(); // Reset the position pointer to the given value. It returns the // new position. virtual Int64 doSeek (Int64 offset, ByteIO::SeekOption); // Set a new buffer size. // If a buffer was already existing, flush and delete it. void setBuffer (Int64 bufSize); // Write a buffer of given length into the file at given offset. void writeBuffer (Int64 offset, const char* buf, Int64 size); // Read a buffer of given length from the file at given offset. Int64 readBuffer (Int64 offset, char* buf, Int64 size, Bool throwException); // Write a block into the stream at the current offset. // It is guaranteed that the block fits in a single buffer. void writeBlock (Int64 size, const char* buf); // Read a block from the stream at the current offset. // It is guaranteed that the block fits in a single buffer. Int64 readBlock (Int64 size, char* buf, Bool throwException); private: Bool itsSeekable; Bool itsReadable; Bool itsWritable; int itsFile; Int64 itsBufSize; // the buffer size Int64 itsBufLen; // the current buffer length used char* itsBuffer; Int64 itsBufOffset; // file offset of current buffer Int64 itsOffset; // current file offset Int64 itsSeekOffset; // offset last seeked Bool itsDirty; // data written into current buffer? // Copy constructor, should not be used. FilebufIO (const FilebufIO& that); // Assignment, should not be used. FilebufIO& operator= (const FilebufIO& that); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/IO/FiledesIO.cc000066400000000000000000000170241476623553700166600ustar00rootroot00000000000000//# FiledesIO.cc: Class for IO on a file descriptor //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include // needed for errno #include // needed for strerror namespace casacore { //# NAMESPACE CASACORE - BEGIN FiledesIO::FiledesIO() : itsSeekable (False), itsReadable (False), itsWritable (False), itsFile (-1) {} FiledesIO::FiledesIO (int fd, const String& fileName) : itsFile (-1) { attach (fd, fileName); } FiledesIO::~FiledesIO() { detach(); } void FiledesIO::attach (int fd, const String& fileName) { AlwaysAssert (itsFile == -1, AipsError); itsFile = fd; itsFileName = fileName; fillRWFlags (fd); fillSeekable(); } void FiledesIO::detach() { itsFile = -1; } void FiledesIO::fillRWFlags (int fd) { itsReadable = False; itsWritable = False; int flags = fcntl (fd, F_GETFL); if ((flags & O_RDWR) == O_RDWR) { itsReadable = True; itsWritable = True; } else if ((flags & O_WRONLY) == O_WRONLY) { itsWritable = True; } else { itsReadable = True; } } void FiledesIO::fillSeekable() { itsSeekable = (seek (0, ByteIO::Current) >= 0); } void FiledesIO::write (Int64 size, const void* buf) { // Throw an exception if not writable. if (!itsWritable) { throw AipsError ("FiledesIO::write - " + itsFileName + " is not writable"); } if (::traceWRITE(itsFile, (Char *)buf, size) != size) { int error = errno; throw AipsError ("FiledesIO::write - write error in " + itsFileName + ": " + strerror(error)); } } void FiledesIO::pwrite (Int64 size, Int64 offset, const void* buf) { // Throw an exception if not writable. if (!itsWritable) { throw AipsError ("FiledesIO::pwrite - " + itsFileName + " is not writable"); } if (::tracePWRITE(itsFile, (Char *)buf, size, offset) != size) { int error = errno; throw AipsError ("FiledesIO::pwrite - write error in " + itsFileName + ": " + strerror(error)); } } Int64 FiledesIO::read (Int64 size, void* buf, Bool throwException) { // Throw an exception if not readable. if (!itsReadable) { throw AipsError ("FiledesIO::read " + itsFileName + " - is not readable"); } Int64 bytesRead = ::traceREAD (itsFile, (Char *)buf, size); int error = errno; if (bytesRead > size) { // Should never be executed throw AipsError ("FiledesIO::read " + itsFileName + " - read returned a bad value"); } if (bytesRead != size && throwException == True) { if (bytesRead < 0) { throw AipsError ("FiledesIO::read " + itsFileName + " - error returned by system call: " + strerror(error)); } else if (bytesRead < size) { throw AipsError ("FiledesIO::read - incorrect number of bytes (" + String::toString(bytesRead) + " out of " + String::toString(size) + ") read for file " + itsFileName); } } return bytesRead; } Int64 FiledesIO::pread (Int64 size, Int64 offset, void* buf, Bool throwException) { // Throw an exception if not readable. if (!itsReadable) { throw AipsError ("FiledesIO::pread " + itsFileName + " - is not readable"); } Int64 bytesRead = ::tracePREAD (itsFile, (Char *)buf, size, offset); int error = errno; if (bytesRead > size) { // Should never be executed throw AipsError ("FiledesIO::pread " + itsFileName + " - read returned a bad value"); } if (bytesRead != size && throwException == True) { if (bytesRead < 0) { throw AipsError ("FiledesIO::pread " + itsFileName + " - error returned by system call: " + strerror(error)); } else if (bytesRead < size) { throw AipsError ("FiledesIO::pread - incorrect number of bytes (" + String::toString(bytesRead) + " out of " + String::toString(size) + ") read for file " + itsFileName); } } return bytesRead; } Int64 FiledesIO::doSeek (Int64 offset, ByteIO::SeekOption dir) { switch (dir) { case ByteIO::Begin: return ::traceLSEEK (itsFile, offset, SEEK_SET); case ByteIO::End: return ::traceLSEEK (itsFile, offset, SEEK_END); default: break; } return ::traceLSEEK (itsFile, offset, SEEK_CUR); } Int64 FiledesIO::length() { // Get current position to be able to reposition. Int64 pos = seek (0, ByteIO::Current); // Seek to the end of the stream. // If it fails, we cannot seek and the current position is the length. Int64 len = seek (0, ByteIO::End); if (len < 0) { return pos; } // Reposition and return the length. seek (pos, ByteIO::Begin); return len; } Bool FiledesIO::isReadable() const { return itsReadable; } Bool FiledesIO::isWritable() const { return itsWritable; } Bool FiledesIO::isSeekable() const { return itsSeekable; } String FiledesIO::fileName() const { return itsFileName; } void FiledesIO::fsync() { ::fsync (itsFile); } void FiledesIO::truncate (Int64 size) { ::ftruncate (itsFile, size); } int FiledesIO::create (const Char* name, int mode) { int fd = ::trace3OPEN ((Char *)name, O_RDWR | O_CREAT | O_TRUNC, mode); int error = errno; if (fd == -1) { throw AipsError ("FiledesIO: file " + String(name) + " could not be created: " + strerror(error)); } return fd; } int FiledesIO::open (const Char* name, Bool writable, Bool throwExcp) { int fd; if (writable) { fd = ::trace2OPEN ((Char *)name, O_RDWR); }else{ fd = ::trace2OPEN ((Char *)name, O_RDONLY); } int error = errno; if (throwExcp && fd == -1) { throw AipsError ("FiledesIO: file " + String(name) + " could not be opened: " + strerror(error)); } return fd; } void FiledesIO::close (int fd) { if (fd >= 0) { if (::traceCLOSE(fd) == -1) { int error = errno; throw AipsError (String("FiledesIO: file could not be closed: ") + strerror(error)); } } } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/IO/FiledesIO.h000066400000000000000000000144321476623553700165220ustar00rootroot00000000000000//# FiledesIO.h: Class for unbuffered IO on a file //# Copyright (C) 1996,1997,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_FILEDESIO_H #define CASA_FILEDESIO_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //

        // Class for unbuffered IO on a file. // // // // // //
      • ByteIO class //
      • file descriptors // // // This class is a specialization of class // ByteIO. It uses a file descriptor // to read/write data. //

        // The file associated with the file descriptor has to be opened // before hand. // The constructor will determine automatically if the file is // readable, writable and seekable. // Note that on destruction the file descriptor is NOT closed. // // // This example shows how FiledesIO can be used with an fd. // It uses the fd for a regular file, which could be done in an easier // way using class RegularFileIO. // However, when using pipes or sockets, this would be the only way. // // // Get a file descriptor for the file. // int fd = open ("file.name"); // // Use that as the source of AipsIO (which will also use CanonicalIO). // FiledesIO fio (fd); // AipsIO stream (&fio); // // Read the data. // Int vali; // Bool valb; // stream >> vali >> valb; // // // // Make it possible to use the Casacore IO functionality on any file. // In this way any device can be hooked to the IO framework. // class FiledesIO: public ByteIO { public: // Default constructor. // A stream can be attached using the attach function. FiledesIO(); // Construct from the given file descriptor. // The file name is only used in possible error messages. explicit FiledesIO (int fd, const String& fileName=String()); // Attach to the given file descriptor. // An exception is thrown if it is not in a detached state. // The file name is only used in error messages. void attach (int fd, const String& fileName); // Detach from the file descriptor. The file is not closed. void detach(); // The destructor detaches, but does not close the file. virtual ~FiledesIO(); // Write the number of bytes. virtual void write (Int64 size, const void* buf); // Write the number of bytes at offset from start of the file. // The file offset is not changed virtual void pwrite (Int64 size, Int64 offset, const void* buf); // Read size bytes from the descriptor. Returns the number of // bytes actually read or a negative number if an error occurred. Will throw // an Exception (AipsError) if the requested number of bytes could not be // read, or an error occured, unless throwException is set to False. Will // always throw an exception if the descriptor is not readable or the // system call returned an undocumented value. virtual Int64 read (Int64 size, void* buf, Bool throwException=True); // Like read except reads from offset of the start of the file. // The file offset is not changed virtual Int64 pread (Int64 size, Int64 offset, void* buf, Bool throwException=True); // Get the length of the byte stream. virtual Int64 length(); // Is the IO stream readable? virtual Bool isReadable() const; // Is the IO stream writable? virtual Bool isWritable() const; // Is the IO stream seekable? virtual Bool isSeekable() const; // Set that the IO stream is writable. void setWritable() { itsWritable = True; } // Get the file name of the file attached. virtual String fileName() const; // Fsync the file (i.e. force the data to be physically written). virtual void fsync(); // Truncate the file to the given size. virtual void truncate (Int64 size); // Some static convenience functions for file create/open/close. // Close is only done if the fd is non-negative. // static int create (const Char* name, int mode = 0666); static int open (const Char* name, Bool writable = False, Bool throwExcp = True); static void close (int fd); // protected: // Get the file descriptor. int fd() const { return itsFile; } // Determine if the file descriptor is readable and/or writable. void fillRWFlags (int fd); // Determine if the file is seekable. void fillSeekable(); // Reset the position pointer to the given value. It returns the // new position. virtual Int64 doSeek (Int64 offset, ByteIO::SeekOption); private: Bool itsSeekable; Bool itsReadable; Bool itsWritable; int itsFile; String itsFileName; // Copy constructor, should not be used. FiledesIO (const FiledesIO& that); // Assignment, should not be used. FiledesIO& operator= (const FiledesIO& that); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/IO/IO-framework.drawio.svg000066400000000000000000000624701476623553700210620ustar00rootroot00000000000000 AipsIO- level_p: uInt- objlen_p: Block<uInt>- objectType_p: StringTypeIOCanonicalIO- itsBuffer: char*- itsBufferLength: uIntConversionIO- itsBuffer: char*- itsBufferLength: uIntRawIOLECanonicalIO- itsBuffer: char*- itsBufferLength: uIntByteIOFilebufIO- itsFile: int- itsBuffer: char*- itsBufLen: Int64FiledesIO- itsFile: int- itsFileName: String- itsIsSeekable: BoolMemoryIO- itsBuffer: uChar*- itsUsed: Int64- itsPosition: Int64StreamIO- itsSockDesc: intMFFileIO- itsName: String- itsId: Int- itsPosition: Int64TapeIO- itsDevice: int- itsDeviceName: String
        1
        1
        «interface»
        DataConversion
        «interface»...
        MultiFileBase
        0..n
        0..n
        1
        1
        Text is not SVG - cannot display
        casacore-3.7.1/casa/IO/IO_1.gif000066400000000000000000000350771476623553700157740ustar00rootroot00000000000000GIF87a¿ùðÿÿÿ,¿ùþŒ©Ëí£œ´Ú‹³Þ¼û†âH–扦êʶî ÇòL×öçúÎ÷þ ‡Ä¢ñˆL*—̦ó J§ÔªõŠÍj·Ü®÷ ‹Çä²9 H«×ì¶û ËçôºýŽÏë÷ü~üÜá'8HXh¸øÈè³ØˆñY$9yRi™ ƒ©ùÀÙÉó ê!:j*Rzjª:ÃÚZñ ;K!;jK»‚›Û°Ëû‹à›) ¦x¶ðÆËdẕ̌¸^ÙÐ\H?1ý 5ÕK²B[¥ôõ&ÙXÓžbÛëí¹Kï¾ÝIp¾¿ /®{2rÇz=ºôéÔ«»ni=»öíy–÷¾¬9ñ ÈW1>z ë-µò>}¨ØòÕ¿výüþâŸæO‹;˜L Vû%¸‚É18ЂFŸƒê"á…±T¨á-v(…ÂqGb‰&žˆ¢ˆˆ¥Èb‹†p¢&*ºãH0Ö8ÉŒ2è¸Ú8:óáßåÇ£ÑidD>&©’LÂFä“€©ˆ”½,ieTº°¥oQÞCÑG äÔàTJ.ð˜9f¾ãš[lŠ©–'NA ¦zNz‚•/iFTYˆ˜YÑå© 'Ÿøi@è˜Cös&µÚ膇BŠæ¤¸ˆ(†Nú(œž:Z›£EfÚGqF%&™n}ÖX£}ÔÑ¡Œ¦ªÖIqyVk\ñeëD¯âs+¬ÀÖ+§¥JxþQªsB¹ªg‰Îú,l¶’éé¨K&Ê,¬Ùº)í¶ Ú”-­â~Kl·Â†+­›Í¦K®¶«®ËmL¢Žûé€ÈÊúë¼´«®¾§²«­¸³âv#Õv nÀ¨î»&¡¯*;ìÀó:¼0½÷백͊“1½ø=kfs¤¦æÅMé›oºSº/m¡[ï_ZúïÃÞÂk®¢Ëz¦ÄwìòÄ<L´©îš2¶)Ç,ò½Aí-œ6[¬óÒSÇkíÌAóœô[æâŒ4RýR iM-ÃlöÙE÷¬ð­cKM4ÙŽ8­¬ÀHCÖõÁ®âm·Þk­óÙÔê,dcK¸ËSíy.KJËÔØ²Ñ þþn˜Õò[,¨ó)gܯxmÝ8®XÑüôçR¯ –,3we Ò¼n¦wj^gÌ +à“Üù%G"»#³ÞËu†âލÏõ¸Èü¨dYÆð¾C©ŒÏS/†ô&hO÷›‚ÀJ[zþ÷É•žo>ŸÆóÓ&íè Äù8»o¼š¯pÒ'ÛÙ̯iy×é><é ÈÖ"E å°uðëËïºÚ‘ŽþªÞþVÇ;…jD˜éxµ©Ll(Z«¦•+‹@ ` Ýœµ–¾ÉK^™S!ËNˆ¶Æ- rÐúæ¼< ‚ÏUú3ÛØ8º¾‘RŒâÀ@¸DŠU°Þ³OþØÚ¶1Ðq c0³ÎLö±(†±‚3"R¾GÅÿÌhV3߈­Lc¹R›84“!®Šàã1ƒ·¨r‚[Œa¾ñArêŽ9#EØÈÛ ±?D¬ÙÓôÀŒMnhíŠd….=.2Bñ«—Íð7t™Šƒ—åS¹®µ’f@{†;5Ä+ZŠoK„ ,'÷¶O5ñ‹œÔaÔdÙB®Ð˜X$0eH˯A3m±t¤ßàÖ±õ Ф Ò`¨¸|ŸºqWCÝPɧ>eµ‹¥hjšš¾ 2±¦Hݦð"ŠÐ "‘K@­$Óõ/¯bð³”ÔQÍjRÉ´ôR󣓉Q{Q(‘Μ! ËJ#ªú-›íÜäÑÂö®r–ðgàD¡U(¹‹Î´¥O½âà‰œ%˜¨q( ÷þ*Ë„mU“øj­0ë(¬]åð™µÄ`Žö³FÚÑWÍÅæÉ> 2ÜUhWíc»ðh:ІV¯¯ÜYɸ8S½¨Œ›_é[¼¦±”í%o5_k_ÀÎò­ùÍYymTÚ°æ ¾ëuipÚYõ:ö™ô-W3§+ÆHúÒ…ü…d!™)]32³À;õ®')±)¦<×®š]ª‚ôº·Á%îœæHâå´›>¢V•ëL;'˜Ö–´î±”=Wf¨iÏ~â0Çr)± 2H ãBuÃΪƒÍËÑýÂó¥"Aܤ Um2–*Ó›èHØX.£Y¢6íÂb³ ½7[•Ím~IL…$ç‚b•—Q•ébþTÛ™“*bFS =Ž©ÅUWN¶àFq*^ ;™•Yv±£¬å‚T2ÆòWƒ:Õ?¿Ï€h$0E[83£§D¨[ã+UƬyÏ¢Z§­¹+ØÀú*½Âík1‰º[vN‰Mªx‰1ÍnW±*Èó™!l7ù ²Çå¬la[»¬ gÖý¦Ùî0j©m„¼Z,µØÜiÁ6e/mÑ´Ö€Kë\xØŒˆ>¤(·äïeד;¥WFìm5ì°ÍvÙÚ-]ªQ’+ŽcÁ¡Iñq¢ú¤önŠçŽß»Üàî´‰ üensÜÛ?yËØxáX1ä¦ueËï ËþŸ7ÀÒ nù'ÒŽGХĪ;ß“ÿ;å÷´q‹ò—«rÁä®$µzÙIx'×Â\Ô0×Ös–÷pãfº‰‰žÇÉRåy»wÔQÜô*ºÇÁ¼—Ú6œœ8®ŒŽ—Lc†¸ÅE¶ ½ìÛfpËUû$„5SäË2ësg±²áîT:Ï™.\`ÐÞÑñØs¶ç5¯gÎ÷O£|Võ˜E?ú4£ÔôVæxrçÑè¶¿= r£Ý_ºìŠ2XO!—ÔÓóxîJ=t£y_Hu}¥ ´í-mÝcÅ­»Kà` Atgß›ÊW½ÊÕJÓ¸Vø¡÷rùŸ¼ýŒþ^“‹ø7Ô¬â7ÚŸò¢ó w¬xç'd&^ü]®U¶–X¿dm˜—išçeÉvSDh©aYÇoÔÄuÇ[ô&[ÖsÚFu«ônk§u%æo.‡iÖSS÷uçpè#_“ç\¤GGX…—qã§Lœ3pâô]ŠôîR1J‡cѤsã!€!B×4s9Vèr87q^çv‚d`¶ubG·ƒI7^-·‚Ñç`Mè8`Ã`xnS”n&E:ˆ…ÂÇ~Äqv½b¸µowhJ;H^eè]õp¥#Oƒ¶wµÖw‹C>–ås<D½å~,Hz§röäOGQm‡7y†dþc™õ|Ú—yõvi‘z­ôW#I0‰’Kštˆzj¶§ ¢°gx­D™“«o¸KéDV"†²-k®4'­›°ñÚ!!ë©ÁÆ'Ô+-†N™Øw þ):ÿ´Ì†œé¤mRˆ|›h 6{Œ4 8ëª{«¢Ê@á˜é%P+SN{!R+Š9¨ûyFƒ’µ“Ú#^k%` ŒjË}˜Ú¨„¹#íY}‡†{\ËtŸ†s—€Xµ ·™j°\·Çʦi+·ZQ¸g°ƒò[nû·a;¶‹æžRŸiµ¼qd_Ê+Æ&»²‘±YKa|ÿÔŸKlKº€û¶f¹:ãóŸ²QR?¡˜H¡ªk†±£‹·—p»Áh›»¾›¸Lˆx:¬}%söqa7vÍTþI»ƒ«¹¦;¼•Hz\Jø»ÛC¼¹‚!YÆëºÏæm_sþ톔îä^ó¶J$ú‘¿Q»7 »Â›²€Q´8¹„‹½*v:Û[—yu¼ž6¯0x9o“¤Ôg÷†î5”Ö›©Å½Â«¿©Û+vò½®cÁ„ƒÁä;‹ x¾ÈK±IŠuO(i*sätlF½¯Q¿±Ó'꣪ˆ2h¹v›;w " âÒYõaOödÀ¨ ãT>Ë<ãRÍ‹Aªr²9Inõ÷¾æ¢öñ=9åeM÷þÜ܈ä9œÜ+élJaÐDmçzýåÐ`è®ö‚êâä^§d-ûHòËï´ÖH~å&Ý÷wó«"ùš>§E—¯ÿ^¥mͤxî›Oõs¦ þ|²:í¯Ø›|õ¾šS˜±hG.ïî0‹×ŸÿÙv¹³â‡L÷ßó¡}æmþàɑϴä^WrذòžM™ñY¼ý‘b®u€þ»…O¥£†’’_ä.æ+êøLxKûÒ$ ½J)GˆŽû›?¨ (øVYO…HÈ-xB&E{qÖ›wÿÁP³ŠŒLi‚TÈi©2)µ–ÙœÆùý]¿[ìTìøF¾‹²`W¼ÎYÍÁ,5ë4 ”j£FRD›Aè’xÌöºáqù<®þÌ7Ec¢÷ý†öbˆ„PmÉvœ‚–èÎØhòu˜ø ñÀdøö0;› »±þ"¯NìHZÛ’"_ÇJUmoqáf7R4w¾°žG¹V© x»‘Ws=v×.Ÿ¦|¡¬¶¦¹‡{йO—RU£CeŸÓÕ×ÙÛÑ'Ãþ_°/›ç!‰™ãíïÍq450™>a^êíËvîàFñÆÙx‰î0fÔ¸±ÈEgoˆ$JT-ØŠB'!ÌÒ¤O˜pša§Ñ£r/@Y+ ê$Μ;9ðùd!“·j¾c•ò#Ã<?óÈ‘jUi’,¦›ÊÐjE#[•=K°ëÌcßÄ ÓêWG@eáÆÅ:wÚ«rìrL0WÞ¼x½&8²çœãB6Äóv%-‹ÿÊ•ì6ðdË’÷^Ö¼.³`•YæyU |SÆ.þ[ÙC‚û;ïZc¼gmãöœ/ǹ“Xãx·ïe;üÙy†‹˜Ÿ\]Ý ÙµÄÜ«†ü¾dä ÍÛ­ë­ŸÓÒévŸ‡™wÝëJãñm••<`áÛξÜÑø…Ôz»÷¼ù*gƒžúCµ´ºïÂ'Õ5ÓÜ)±t¨mŽb_â!Ý¿«]ô/•/Íö9uŒ…-æ”ÿh@}íI|Ûèø@Nf:²à1˜A n°¼Ú±ÕA2p„S;_ %B p…ípà×.2‹ÿíjkˆþêôTØÂåéЃgÓÓ†¨à ²…RËa ˜?ßE¨0› •pj··}Ùïz ‰jÚ&>†i‹¹›‚¾%±[%Ov˜³bSâ´¹)v‡]\“1bºJ ¦7Ø FKÒˆˆ}+lÁŽO¢h»$¢pF\"ꄼâu£!ÀÞDB¿2Xp”—%]xH&&²[QZ#¿AC˜DTlÔ%AˆJÄ‘,%€|U'#–©ø5Ñ~ÄéŨ֢GU2 “§áà/9¸B.®Z»#%wI¤dúp„êÃ_ÖÚdÌcÂq™_Â!YÙËéi“‚!ª¦ªNÈÍmró›Ý Å©Ìtî0MÂÌþ&úP¦'xÊI”;±ápx&ÎrbÍ|èÔZùz$=€‚ƒN¦Ì'9¯YÂ/¶Òa¸LMuŒÕNTz3>jÈ®’ŒKpËgî ›ç£D­F‹šk~l©iê™Çˆ¾*‹ÔÚ'”"ö“œžÆ%<픚nZ¦aÍqVu,êíØR<2r©yC‰õÅ)åu€Í)ÄHÄ7Cˆ„¤`XWÄG€PõJšt$KóÈREª”©H%( ‰ “W²³Od}TcP1ž¬—âD_Y’‰Q”„ \(§h*ý]Ÿ$Üg½‚¯¦)´êO÷*¿­b5°µlf'ûWsØjï$Ìþƒ·VÖå¡×øÞSuê,ïMÎbøÜh:ãÚ×ú±³{•eo9»¨¸3œÿ4çóŠ M¹‘öL˜eV^wÜàê–º Ñë@SiZ”ŸÌ Ý9/;§6‘YÔ…ná„^Raw‚þ\g…˜»%h6¾—qï{qŠÛùZ³¤Ñ‹'r‰ßªêMõ½¤å `4 ˜¤úLh3—ùõ‹v¢ÁÔz,Mm“Ã$.‚ÝCÙ16Ì^5°e \¶{yP¤ØnÈÁVåeXý<ñw kVXv/Æ÷òßÙÅ«-Ç&cb%Š1îýÈ Þðƒ³›\ SQ­ïÑTÀ†l´3ÊN.l“þ›“eäˆÙ¸ýÔ.™©^.·7È`æÓ–¸bﺹp.œ‹Igør´Ã}֞Ѧç#©9Îýý  ýƒæ–A™ÍRFts\`@ÏÈÏ•¶ô¥1iúΆM ^I{º^ µG­ÐBÛíÔŒS´—»ê8¶:Öm @»VP“ÚIz5­mA·"ÊV°ö¤Õ¹k`;íx²¥BÅ:cVó:I¾^¶!]êìï¡ôsñúµ^díŽo_›ÅD•-ZWWYo#Û›¦&÷*³½½ÕNõËx¢v²§ýnæug>Fò‚Ý­êNç[ß/6·®è Ûv³‡N_À >6Û³»øÃ•þqŽH`!ظ3þi‚kü]ßùÈ÷‡7NÁ$÷À!ìîªw½ä;Où×Ïþæ§·7íq¿qÛ[<÷½·š­í{ᣘõ¨þñü«#ŸùŒîõò›þ}˜‹<óÒ·~ñ« ýëoŸ¿Øç=÷ÁOôØW?üåOôîl~õßuú­_ÿû¡£|Ïßþem¿ñëŸÿEßÿöú÷iùïûzMí°/èÿFÏûÒ¯ÔÀìä Nþd¯ÝÝïùæOühÌõ ÉÏ›Lßú2ñ#°û€ N¶ËiÎ;ðQüÎÍD«]°cP,àkGn?¯ 8!: p‰°pƒÐO,ŽÐÒ˜*À*Ìæäïnð¶¼æDz0åZÉ¡@ª–Æì«lp Suáéʳмz«§„*¤ÀPgÄð5fíγ>‹ y© •Èá®2€k–Žþ¥ ñ° »lñ -%¶î0™MÃä–tC„k—ÞP}Îô„pc0Ýp5ä%P3lA‘½,ñK1Ûk Y±I1[ÎÕh7‘G‘ Nìq‘Èi{±ö”Äqƒñ ä‚ªxÑ¾Ž±ä˜ñü&°©3ò ¥ñ÷žêVð±ñÀ&„Áq¹Ñ¦²qBŒqðÙÏÑ1½q·±ÝQqª1åÞ‘ëÎñe|”qùqOüQ Xð™¢æMÀ–‘Ò›î­ÂgøêËþ Wœ1 A‡éé³’Å©¼J®òr ©v* l02#þ“Q:>%×Jl_®ð˜Ê(”`Ëu¤­OR Õ‘ØN\X2"+C®Ò­%5ÊìRí&oê`A¦b’ ªKZì>èŨì"ò(÷çP|Ìb–²$ÿ $£jW†ò%­²*IPæÂâ©~ðaÌE »R’¤ŠÛ|R,‹’,“Є­ÿÂÎS®Jáâ*é².Áí.ñR¹ÊpšÈñ/× .þǾP „10d ‘!süRr2í±2ËR1ëQ-úè笭 ³0CS3?Q#µ± 6l‚…ÈfM3ûòÒrÅ1®Ì“Э¢€Ä¥ÖGp®GZÈ«c393ƒ'¢dL5HÜ$™ó*VrþÆNp8‰)ÍQ¼ÜÎ^2¡± v`ë‚Ð;É&+å2ϨsÏfS¾–3\¢È“@É]"‹­è:—Òéð<-=ÃhÒ…ÛÄ=aê8Ô"³McÊ3Ðð³3ÍÉî:I$3ë6ı"¤Þ®7SK8ô:éÑ8½låJ³C7=E”CA4$4'GÔD/35oqEó³DôEaÔC‹ÓEw±FôFYt.F+Èt͸f!¯±!q”2ÄîC›Í‡TGmÔ:Ô8ÈËʶ ŠjÅéèJÓe‘‚Sûœ4þTT=ň)é¡SȈ{êÃ9Ñ4g¢Ó>cðKÁT?…P—DA(VË=›ÊÅÔþPd²/É“4ON+IFõ1ÞluðÔ­R±ÜŽãþ´I•6[”EMo‚;T*¶'sºÃ@µÍ"ÕþUR#½À(^Ë•ˆ'UÕP;=µ1.T5{4E4¹HVçTNãôV¹ EPW¡W‡•2…GULKðXÁ)Y÷3Ð>ªU{†-¥âïè.fÌ„H¥ÑHk5š>óv‹ãšræøs:™5WuWYK¸Zò6½ªÂ0L ô†L'ÌN'¢ ‘¶àÓÏ5Xi5Jå £.`ð•fÖ´ÇLŒ'Iv¬ˆú5L¯r2o­' …vÐeL{RPæˆ#£rît0xs±~õaþÑõ_ u=TG©*UsÜ‘î¨#=QA6rDV5I–X35õ`/VÆhHJ×´#KfÝòc1‘_o6]fG't^×ò?+æi›64Öµ& `Ä*Ä>uk“VC%óH+ÎfÃ6qPl»–G'uFÍsláÎðÏVÏmôd33n•Ö.»¿´µ¹UmÒn³gg5knèyÊZ©õ@ßp#ön›Õ1eèhì"\ÉVnJçVROJkyŽ«ÔHUù^¯,Ä‚k½örsÖd5—Q{ò޶J93u¦¤3c¿cÿP7s1ÒZL+s Æ–ê¶“fS‹re6‡ööû–n“gÞ ´þyÇhgBén×rsWY–u, vôÅxj—ÉŠ×:üµz¿Vu•ÕYÂT«J3J–ØU"Ô+tE•|Ë7m•—FéwiYë6½´~ï7GýWwpñw€/oýVSGI”—6 wâ¨8u˜>?bQWk’0!–‚ïózŸµ@ Ö1.eÄÞwt©ì•ìutù„awGXx‡v®d7v·Í†Ë+|_†Íµq9tw#âîgY̱´:x8}øtXy½ò8ýÓŒÜa8Éi~›x0­w†geýSQy’uÃÎh x‹up™ø%Êhs§ t]ø-ëÈSâ·Žþ7“—T¹X„Õµù8Vý¸W9¯€{x ¹fÌ׋Gr»¾uƒ[6qM׉ymø‚1؇gÎ:xØdø’õ‰Wwx}*a«vxK7áä7*óõˆî¸ó{Õ…vÄX¢JÉ ‘¸’X”C€u×܆¤b7–f†8x{Ùb•Xzט¹‘ÿxy]VeÇg |9‹š•€)•Œ!‹9‡µ7¶¹[&Ø›•4“8CéØL××Ä¢¶Êœ–ŽWù™Ù¹…ù|yŸƒœ5ÙΚñR—µ š”ýùoz¡-øõö¡!úú€)ÚlÚ‘1:£Ývþ‹µ=ºlíWDÂñ¤Õn¤Ù¶‚‘I¥µ ‘Ý(–¡y–#Í¥Q2”ŸÌ¦oº‹J§ûñƒsÚ§qR…L¨ë ¨m̨‡¦‹Z©ÇR ßK¦™¦{Ú©£Nš“Zª/™ª³Úª¯º¤«Z« ™«[š(½º¡Ý¹ª%ù¬Ï¤cºÌØ:F±ú­á:®Ÿ4­“zˆìz #:¯ëm¯ùÚ¢·H\›iñ:ÍF³°õØ­‘È1;oû™®çé±û°i)ûqÁº©;³9š§uèG=­#{Qú´Q;µ3 üÈú4ñÑæÄÚ$‰Ú°—Úåb›WZ[_Û¶Y©];ùžî¶çq®{§°·gûþ®k›ä„û¥‰[·{èš{·7©[ã¦;º™ÚQúT;µçë»Å{¼aDâ’{£«Û—¯»®g/–s´A;»‡M¾ó0·ªy•¾G¿#·}§ã[Ô$÷Üû¿á{ù»5™¯ÀÏ»¯•6Á'Û÷ü¾Ñ;Ó tG–½áV d¿â׉Ë)‚Ë-0œDý2½çú;½›”*®?T< ¢ËJ\@ŽVÜžÐÄ‘ÀQ-󪺑¤5ü+´cb^ùÝ•Åí0\äsY5¬xÜÉEW®süÀ¼ÇAËÇ9ücòó¼Íkž¯kËöš< Ì¥ë#Ñ|Íwle›Â¯*þ’«"ôÇ;;È;¢ Ù<Í qºì|Q» yŠÏý*áË¥U¢ßü¹áºÑÑ=ÂAYÈqÙ µ|Jfü+ýß2¶.=Ó«“ø¤Ò!Ýl]üÐC]ºýºúxÒñË+ôÔYÍëœÊ§Åwœ>À¼ÂV½/$žYBC««ÆœÌ#(Äñ™Å'Å•|ýÐ1Œ¶ý»ÁÁœ“ ܼáüÚ-[Ø[ÖSoÂKýÛ<ô´öÈ×uüÛ!<ÐÕÛ˽´qúÝ—¼½åÝó´½›¼Ã1¼ý=àž w½Ý­œÞí=Ýñ]÷¬¥>Ûÿ:Þ¾Û¿Åý1þõÖÝàþûáÑ=âãõ½ã-Þ¬í;ã]oãžä+<áA^ãóãÍۓΗ¢õÏ_^wüíl¾Ãq^¯¹9´c~ågÞÝ'®ìÄ2r×9ˆÆs“1Ûƒ½n¸G¾è=¾fR±+A(fÄÊÜ¿ù­,ôœ³Þß|s¿3Ü&çýgø1…2(3VJ#Ú`9‘¬ukÔQxVúä§žÞÏ=&Ö’†I×]Úx7´:­Œ™×(c Ÿ„÷±E^æŸæ‰ÂÙŸ)—4”Ö¸dÀò‹s í+Z»×¾Â_Û°'‘æsÉŒ'ñÿéîÅ99åóûžÔÿ¾äÓû g²ÙKâ}D&”]°yþÚócÑWk‡ß€Tžå+ßè?èS~è_÷ëâ¥_õšŸê«ÿ]>ûÇú¹úEàvØM‹èûñ<ü'þôãü÷‹†$çÞ÷þa¾ýs_cÒ;s f?ym9†i&üjçÍ(ÙÍrY·Š“Ø‘_™¥¬ì¨qñûÆ7žë;ö?0(„ ‹)#ÒÓ‰XT§ÚÌØ|TåÕJDeyÐR7©½Tk[Ç9¬û†Ìâ7ü]ÓëG{oŽ_³œS’SE ››L™ ‡ DE#¡Kd  Ôå¢ÞÞJæâV £hä¥eKåi&Ìæ«âm­š-n’gî/ÎÒ™æ[þZ"òݦâ2ã ëŸlieTá5±.Hq5ew·,Êó3êèøõ÷®/ûgû;Ðz®<üd0ô58W÷á ¸|’¾tšfÅ,|>¢IÚ'TŸiL¥ 8Èà8L õQ 7¬W½;艄G²ÖÉve"v$DÑɱd·ô¨ï ±…-*¬èP"nÀ>.Û÷gâ }f+锯Ӎ1Ÿ¦d·ò_ˆ2{*µéñh1aØtrÝYjPe7Ö ›ÓíÀ®eI­»Í®Ô³vôÎKë€ï¿Sñc‘ìX–æW3 )*9w¹Tüøe!H‰¢; 25¼¢û®8Îi”¥ç¤Vþ½zòëÒ²‘´ž=¤6PÙ¸Q ¶«9öV߬lwWÜdÝÝu”K~ió¶ïgíßW{óí4‚zþüWø%èÅ~*8ø „J8!…úw .ÂR™*†…FT:Ø ‡!x ®g"$bW!‹-ºø"Œ1ÊHáw͆N[s¥b‰<¢G•N¡$}¤<²P(fý6T979Ý=B÷¤iT^‡eîUÔQ‡pýdå~` å dЙߙ´Õ¸ˆKÙãeSi¦'gÞÑ™áphøDaŸ‰Ò§3þ«0–çv„æ`&†Ò‚¨Š{*úè…¤¤t0J¢£•jZ¤¦—:·ém ÷©¨zzÜ©å•:jòMšLÒ§)špR¦«*š*q¼Öªk<Ép ’5ob•k¢ü()šÀæéëlоå,«èèDNaÖÈš˜+J”9–¸ò!IŽÆéá¡®ºJ*µjR+m³í¢EÊ_ê°YìM¶.RÒ4ö/XɲkÈ²Ñ + ¼FÀ[è” 3xѶjo~bí«Q0ƒ–cÁª’vJh¡¬"OKÂçðCí.Ü1Ê[µ”g=ËY`Ù¼‘W÷ê½ch¢o?±âŒQË «lgÑ¿vTþ¶=W ´c>YkD$³ü«?OôÐRßÚµXIý.Òb “|M4}Q"âh†ÙAo_̽‡®ùæ×Iy5ˆ ™=æÑ ÿÍìU¡¾ãI¹wÛ¶:‡MõÕƒÏ ìÊ+cXà_c8¢ÇXÎtÎpiœÆÄ™Î©ä“ëZyêÌ¢Œlµ‘·~åêeÏγذ×iÉíú‘-xïñ:¬{ðáYî›íÅåÎÅ#8vÎOk6ñÒS·<ðìYzÂÕoÿöÑgüŒå›>úé{ÿ½xÐSÊ>ûêË??ýõÛ¿üî ?üáóo÷ïÄ÷?ëùo€ª[ë (½*Ðu•J`ÉAë0Pþ$œàì*ˆA܉ê‚”œ?È=S%O„¹ ʺ¦g™b ¿á éš„:u…X Vßg¿î`ËÒ™-ò¦,7¸‰S>Ta™zQ›"g|;lÙPÉN|fˆ£SR\øÄ/¢YÌO1KÅì… oÑ„_­˜…8d—Äîï‰<\Ú[Šø“Ÿém‹Û}„f“Õjžq[–Å-|ŽÚs#õX1-")‹Ë‘x¹# £tN2Ä·”ɸmr<áKåAD¾Ž÷Xœ#»¶¥é•â‘]h¥Ì.ùÊœ!¥–}£¥ð”fH ’ò„°úåeéJÇÐe’h TYŒòžÔÌoþ ±˜ÕZÃÄÕ°—ú[Ú T™‚Üè%™Ë#D˜3œãét²XÍ­3uëÏ.}gÍRzÎxõŒ]_š÷0gržñÌe¯– ÏÍ!.7”«¦?k—P'‚ªŸ EàCxP†FtSÓÜàE­‚ЊvŠ£‡äçF=JB‘VéŸM$iQzÒ‰JT¥t©J®ÉK˜v 6½)NsªÓÉTž4ýé2ºC‡µ¨5j>[ŠÔ¥”©A ©S£ºF©. ªT½j ±ÚÔ6jµ«úôªá” Ö±‚’¬)«Y½*T5­X]«ÛêVªÂõƒrkTëŠQ«âu©zÅà]ûêWÁ†U‡„þ½ê_'ØÃ5±\,ciêØB6².ÝEœ^ÈBDVÖ²(¥G’Ê:XŠJg§¦=-ji¤À‚ú!³Wœ'iFÀò5  lû¶ ´ÅFb$ʹþ×YÝ<·¶µ¡“~ÙE=^K•´%­pîIJㆠ¹8«£ByG½Ùµ¶TI!u/GY݆Hfžä1o^ðyWH­bÇ‹Âle>-ÉL88ÀÊ¥Ö´Å•¯gÏš»÷ªÑ$)Œ ¸nļÕx$ƒ{W„`Þå+笮×Û¿ÛÀÛü“K:³µÒ¸ÁSUžnØÙ ¯˜Â¨*±‰Ï&ÁÕ°øy4.ªa†Å8”Lþ±…q (·éÆ=f,"YDz1ƒ,ãÞyX]#–›‚I,\KYºÃëðõvaçA¨Ë^îx¡$æ1“ùÀf†o ¼fûdØÍoî/žó,¡ÿî×Îoæp™ üÜ>ÏùÏV±P½ô¬èE«oµ64‡ÏüÄB¯ˆÒþ¨?-mØKOÓ×32§ Èçg1zÑ¡“§ü¬4¥Ò­f/¡<ýê?Ï:·«>S­Íœk ßIÖ§Ó®£e(_ÿšJ¥>6²a4lVÛØ£î5³›M¤`7Öùzô£Ñ¥'ÀP»ÇÝæ1´|alÓ ­ÒþÏ·çdí/ÙCX§#šì§ƒîŠ_=7þo»lvG’Éœ¹•[Ìd|à¿7¾¥”Ð\¼œþ6 ç .p‡ìßrÂôl:5üâÇõ¥6q<’Îxwªr.!:Pk覣¹³ØÉq“irŒ£<ßø4›_¶»ã†;Ñ3—Òs\óŒÐ·ÞKGrÏ? Ìr´’†Wv70ĨÍì›n¶.:°£~ (Æ•饙$½Â7ì–8`·RºCª7âß´$½v4©L‚æ\}»³Å®ñ8®’M>û¤r“Køšœ—×—¹à}Þ!o~…ëEäÊMÙ÷'W›ÔÉîo䟇Ê[=í*–X:‰˜±ÆݧÇuèþO¤§}:øI ‡|ì)¨]Ò>H·‡}îžé}£:øÂŸçï‹o|݇•åPw9ïáŒé×+ùªBp¿*ôÞúa·þõû†s #É´ò×7¦I>µ~úr"6øam$a’¿¼Ì4½âßi̶ {ÝÕ¿­YH DØl7å]þáã‰Ó×yÜ ü!Æ3]×ý ÚÚm^ûÍ”ç}jA ”é3¹Sˆ\žyŽ7UMø½î ÛíÝÖàXùMQÓYfIÑÛÔ_:`à¹`ÿiÎËÕÞúß÷ù`ç¡%!$_!¸!ñ5¡âEÜ!áªÛÂE!f¡ÆN‡þñßÇd›ÏhV–LY”0az!tÁFŽa޵ÜË$àÙR! RŤÁEÆøÍÕ)SÒyC¼%S7UÜJߨµ`N_1à'íËý‰Ìni`ùÍáú‹*!Üy`j="Ò ZˆýIÈÔ Ü¸ÛèÑÃáÈ\8'>’~ñ)Æ_]WI I ?à.*`,r—-Ñ¢ß!Ì-ZÙIâ*‘Þ0¦3fR&®Å0Í"ØL˜€%#–Áˆ¡×0S7æX#ñ¡Rä 8R‘:i:"“^T¡6ònì†<öáÐ;ncÊ áV a¡-Þcî¹c¤Ø ¤¹d轡þÑX—죟!ÑA"¤àÌ9ß­q Dß2¦"ûU‘6½ ¦ž"ÎÛú]Ænd@ÂQãPLm",5#LªÛ¨×"â‰Jr¤"ý pálU’ÆØ¤G&v½`NF^GÞDÄd,’P*ž1úåJåÊäÅÖæ}KTBcSRàR%Rþ8ŽdlÕëäÜ,º˜Lö¹ŸXöœAÆ\\fÜ\þ]]ÊåÆå¥]î%_â›Bòb,‘Ž»,$ÂÑå_6›áv™r~ã%gs¢fSž`~^¥q†e…Z(lb# BœÚì_PºÒm6Wnjሒ(0)¥5^¢v¢8§MPÆ(v‘øÁWÒV~å]£TN¥Î§uÖ 9rÄÁÑW"j§:¸å”ç’X®T–YšØ–RS—z[|†)a}iL‘©—Ž)šâ•™jÔšj©š¾iZµ)€È©g‰"žâ©î)Ÿö©Ÿþ) ª *¡ª¡*¢&ª¢.*£6ª£>*¤Fª¤N*¥VjT;casacore-3.7.1/casa/IO/IO_1.html000066400000000000000000000024721476623553700161640ustar00rootroot00000000000000


        Ger van Diepen (gvandiep@nfra.nl)
        Last modified: Thu Apr 20 16:33:43 CEST 2017 casacore-3.7.1/casa/IO/IO_1.omt000066400000000000000000000472301476623553700160200ustar00rootroot00000000000000(omt_version 5 0) # %W% %G% (omt_module (key 1) (anno target_file IO_1.cc) (name IO_1) (language C++) (fcode_max 1002) (omt_model (omt_class (key 3) (anno description "This is the base class for Sink and Source") (name BaseSinkSource) (method (key 169) (anno description "This functions returns a reference to itsTypeIO") (name getTypeIO) (type TypeIO)) (method (key 177) (anno description "This function sets the position on the given offset. The seek option defines from which file position the seek is done.") (name seek) (type Long) (arglist (arg (key 178) (name offset)) (arg (key 179) (name SeekOption))) (genheader "Long BaseSinkSource::seek (void* offset,void* seekOption) ")) (method (key 180) (anno description "This function checks if one can read a device") (anno const YES) (name isReadable) (type Bool) (genheader "Bool BaseSinkSource::isReadable () ")) (method (key 181) (anno const YES) (anno description "This function checks if one can write a device") (name isWritable) (type Bool)) (method (key 182) (anno const YES) (anno description "This function checks if one can seek in a device") (name isSeekable) (type Bool))) (omt_class (key 8) (anno description "This class is made to do the type dependant IO. This class is a baseclass which can be derived from. The functions in this class are virtual functions to read and to write bytes from or to a file. The derived classes represent a typical IO like RawIO, AsciiIO or CanonicalIO. In these derived classes there could be a translation, if needed, before the data is written to a file. If it is necessary there could be made more derived classes like IBMIO each for specific IO. ") (name TypeIO) (method (key 191) (anno description Constructor) (arglist (arg (key 223) (name ByteIO*)))) (method (key 193) (anno virtual_method YES) (anno description "These functions write a stream of bytes. ") (name write) (type uInt) (arglist (arg (key 194) (name nvalues)) (arg (key 195) (name " value*")))) (method (key 196) (anno virtual_method YES) (anno description "These functions read a stream of bytes.") (name read) (type uInt) (arglist (arg (key 197) (name nvalues)) (arg (key 198) (name value*)))) (method (key 199) (anno description "This function sets the position on the given offset. The seek option defines from which file position the seek is done.") (name seek) (type Long) (arglist (arg (key 200) (name offset)) (arg (key 201) (name SeekOption)))) (method (key 202) (anno const YES) (anno description "This function checks if one can read a device") (name isReadable) (type Bool)) (method (key 203) (anno const YES) (anno description "This function checks if one can write a device") (name isWritable) (type Bool)) (method (key 204) (anno const YES) (anno description "This function checks if one can seek in a device") (name isSeekable) (type Bool))) (omt_class (key 20) (anno description "A ByteSink is a place where bytes are written to") (name ByteSink) (method (key 170) (anno description "constructor, TypeIO should be given.") (arglist (arg (key 224) (name TypeIO*)))) (method (key 172) (anno description "These functions write one variable") (name operator<<) (type ByteSink) (arglist (arg (key 246) (name value)))) (method (key 174) (anno description "These functions write an array of variables") (name write) (type void) (arglist (arg (key 175) (name nvalues)) (arg (key 176) (name value*))))) (omt_class (key 21) (anno description "A source is a place where one can read bytes from.") (name ByteSource) (method (key 184) (anno description "constructor, TypeIO should be given.") (arglist (arg (key 333) (name TypeIO*)))) (method (key 186) (anno description "These functions read one variable. The buffer should contain enough space to read the value.") (name operator>>) (type ByteSource) (arglist (arg (key 259) (name value)))) (method (key 188) (anno description "These functions read an array of variables. the buffer should contain enough space to read the values.") (name read) (type void) (arglist (arg (key 189) (name nvalues)) (arg (key 190) (name value*))))) (omt_class (key 22) (anno description "This class is the base class for writing streams of bytes to a device. The derived class should be each for a particular device. This class also contains virtual functions for checking if the device is readable, writable or seekable. The derived classes could be regularFileIO, StringIO or TapeIO. RegularFileIO is implemented now, other derived classes can be added easy later.") (name ByteIO) (method (key 141) (anno virtual_method YES) (anno description "These functions read or write streams of bytes to a particular device") (name write) (type void) (arglist (arg (key 163) (name size)) (arg (key 164) (name buf*)))) (method (key 144) (anno virtual_method YES) (anno description "") (name read) (type void) (arglist (arg (key 165) (name size)) (arg (key 166) (name buf*)))) (method (key 147) (anno description "This function sets the position on the given offset. The seek option defines from which file position the seek is done.") (name seek) (type Long) (arglist (arg (key 148) (name offset)) (arg (key 149) (name SeekOption)))) (method (key 150) (anno virtual_method YES) (anno const YES) (anno description "This function checks if one can read a device") (name isReadable) (type Bool)) (method (key 151) (anno virtual_method YES) (anno const YES) (anno description "This function checks if one can write a device") (name isWritable) (type Bool)) (method (key 152) (anno virtual_method YES) (anno const YES) (anno description "This function checks if one can seek in a device") (name isSeekable) (type "Bool "))) (omt_class (key 19) (anno description "This class is made to do the OS- and device-independent IO . With this class it is possible to do IO without making specific translations for each kind or type of file. This class uses the class TypeIO to do the specific translations and the class TypeIO uses the class ByteIO to write it to a specific file or device. This class should be constructed with a TypeIO. The variable itsTypeIO is stored in the BaseSinkSource class. Reading or writing only should be done with the Sink or Source class. For a description of specific functions you should look in Sink, Source or the Base SinkSource. Example: // main // { // Bool* bool = new Bool(True); // RegularFileIO regularFileIO (Path(\"test.dat\")); // CanonicalIO canonicalIO(®ularFileIO); // SinkSource sinkSource(&canonicalIO); // sinkSource << *bool; // Write a boolean // sinkSource >> *bool; // Read a boolean // cout << bool << endl; // Print the boolean // } ") (name ByteSinkSource) (method (key 205) (anno description "constructor, TypeIO should be given.") (arglist (arg (key 226) (name TypeIO*))))) (omt_class (key 80) (anno description "This class is made to do canonical IO with a device. This class translates the data, when needed, so it is made possible to do IO with different devices.") (name CanonicalIO) (method (key 213) (anno description "Constructor. Sets itsByteIO and allocates memory for itsBuffer with a length of bufferLength. The buffer is initialy 100 bytes long but it can be made smaller or longer by giving another value for bufferLength.") (arglist (arg (key 231) (name ByteIO*)) (arg (key 232) (name bufferLength)))) (field (key 87) (anno cxx_field_access private) (anno description "") (name itsBuffer) (type char*)) (field (key 88) (anno cxx_field_access private) (anno description "") (name itsBufferLength) (type uInt))) (omt_class (key 89) (anno cxx_field_access private) (anno description "This class is made to do Raw IO with a device. This class translates the data, when needed, so it is made possible to do IO with different devices.") (name RawIO) (method (key 216) (anno description "Constructor, sets itsByteIO.") (arglist (arg (key 233) (name ByteIO*))))) (omt_class (key 237) (anno cxx_field_access private) (anno description "") (name AipsIO) (method (key 238) (anno description "Construct and open/create a file with the given name. The actual IO is done via a TypeIO object using a filebuf with a buffer of the given size.") (arglist (arg (key 308) (name fileName)) (arg (key 309) (name ByteIO) (type OpenOption)) (arg (key 310) (name filebufSize)))) (method (key 242) (anno description "Construct and open by connecting to the given file descriptor. The actual IO is done via a filebuf object with a buffer of the given size.") (arglist (arg (key 311) (name TypeIO*)) (arg (key 312) (name ByteIO) (type OpenOption)))) (method (key 313) (anno description "") (arglist (arg (key 314) (name ByteIO*)) (arg (key 315) (name ByteIO) (type OpenOption)))) (method (key 316) (anno description "") (name open) (arglist (arg (key 317) (name fileName)) (arg (key 318) (name ByteIO) (type OpenOption)) (arg (key 319) (name filebufSize)))) (method (key 320) (anno description "") (arglist (arg (key 321) (name TypeIO*)) (arg (key 322) (name ByteIO) (type OpenOption)))) (method (key 323) (anno description "") (arglist (arg (key 324) (name ByteIO*)) (arg (key 325) (name ByteIO) (type OpenOption)))) (method (key 326) (anno description "") (name close)) (method (key 256) (anno description "Return the file option.") (name fileOption) (type ByteIO::OpenOption)) (method (key 257) (anno description "Start putting an object. This writes the object type and version. When reading back getstart calls have to be done in the same way. Getstart checks the type and returns the version. The user can use that to correctly read back objects with different versions. Data in the outermost object cannot be put without an intermediate putstart. However, for complex objects it is recommended to do a putstart to have a better checking. After all values (inclusing nested objects) of the object have been put, a call to putend has to be done. ") (name putstart) (type uInt) (arglist (arg (key 263) (name objectType)) (arg (key 264) (name objectVersion)))) (method (key 265) (anno description "Put a single value.") (name operator<<) (type AipsIO) (arglist (arg (key 266) (name value)))) (method (key 267) (anno description "Put an array of values with the given number of values. If the flag putNr is set, the number of values is put first.") (name put) (type AipsIO) (arglist (arg (key 268) (name nrval)) (arg (key 269) (name values*)) (arg (key 270) (name putNR)))) (method (key 271) (anno description "End putting an object. It returns the object length (including possible nested objects).") (name putend) (type uInt)) (method (key 272) (anno description "Get and set file-offset.") (name getpos) (type long)) (method (key 327) (anno description "") (name setpos) (type Long) (arglist (arg (key 328) (name offset)))) (method (key 274) (anno description "Get the type of the next object stored. This is not possible if a put is in progress.") (name getNextType) (type String)) (method (key 275) (anno description "Start reading an object. It will check if the given type matches the one stored by putstart. It returns the object version which can be used to read in older version of the object correctly. After all values (inclusing nested objects) of the object have been read, a call to getend has to be done.") (name getstart) (type uInt) (arglist (arg (key 276) (name objectType)))) (method (key 277) (anno description "Get a single value.") (name operator>>) (type AipsIO) (arglist (arg (key 278) (name value)))) (method (key 279) (anno description "Read in nrval values into the user-supplied values buffer. The buffer must be long enough.") (name get) (type AipsIO) (arglist (arg (key 280) (name nrval)) (arg (key 281) (name values*)))) (method (key 282) (anno description "Read in values as written by the function put. It will read the number of values (into nrval), allocate a values buffer of that length and read the values into that buffer. A pointer to the buffer is returned into values. Although the buffer is allocated by this function, the user has to delete it (using delete [] values).") (name getnew) (type AipsIO) (arglist (arg (key 283) (name nrval)) (arg (key 284) (name values*)))) (method (key 285) (anno description "End reading an object. It returns the object length (including possible nested objects). It checks if the entire object has been read (to keep the data stream in sync). If not, an exception is thrown.") (name getend) (type uInt))) (omt_class (key 334) (anno description "Class to do conversion in a polymorphic way.") (name ConversionIO) (method (key 335) (anno description "") (arglist (arg (key 336) (name DataConversion*)) (arg (key 337) (name ByteIO*)) (arg (key 338) (name bufferLength)))) (field (key 339) (anno cxx_field_access private) (anno description "") (name itsBuffer) (type char*)) (field (key 340) (anno cxx_field_access private) (anno description "") (name itsBufferLength) (type uInt)) (field (key 341) (anno cxx_field_access private) (anno description "") (name itsSizeT) (type uInt)) (field (key 342) (anno cxx_field_access private) (anno description "") (name itsCopyT) (type Bool))) (omt_class (key 343) (anno description "") (name DataConversion)) (generalization_relation (key 24) (superclass 21) (subclasses 19)) (binary_association (key 9) (anno role1_is_class NO) (anno role2_is_class NO) (anno description "") (name itsTypeIO) (role (key 10) (primary 3) (mult 1 2)) (role (key 11) (primary 8) (is_arrow) (mult 1 2))) (generalization_relation (key 23) (superclass 20) (subclasses 19)) (binary_association (key 344) (anno role1_is_class NO) (anno role2_is_class NO) (anno description "") (name itsByteIO) (role (key 345) (primary 8) (mult 1 2)) (role (key 346) (primary 22) (is_arrow) (mult 1 2))) (binary_association (key 329) (anno role1_is_class NO) (anno role2_is_class NO) (anno description "") (name file_p) (role (key 330) (primary 237) (mult 1 2)) (role (key 331) (primary 22) (is_arrow) (mult 1 2))) (generalization_relation (key 347) (superclass 8) (subclasses 89 80 334)) (generalization_relation (key 118) (anno cxx_rel_virtual YES) (anno description "") (superclass 3) (subclasses 21 20)) (binary_association (key 300) (anno role1_is_class NO) (anno role2_is_class NO) (anno description "The actual IO object.") (name io_p) (role (key 301) (primary 237) (mult 1 2)) (role (key 302) (primary 8) (is_arrow) (mult 1 2))) (binary_association (key 348) (anno role1_is_class NO) (anno role2_is_class NO) (anno description "") (name itsConversion) (role (key 349) (primary 334) (mult 1 2)) (role (key 350) (primary 343) (is_arrow) (mult 1 2))) (generalization_order (subclass 19) (generalizations 23 24))) (omt_image (sheet (key 2) (anno description "") (name OM) (model_type Object) (width 720) (height 790) (background_color white) (foreground_color black) (ClassBox (key 245) (represents 237) (frame (loc 6 5) (dimensions 276 304))) (ClassBox (key 13) (represents 3) (frame (loc 413 5) (dimensions 181 109))) (ClassBox (key 121) (represents 20) (frame (loc 519 152) (dimensions 169 83))) (ClassBox (key 125) (represents 21) (frame (loc 308 152) (dimensions 185 83))) (ClassBox (key 126) (represents 19) (frame (loc 452 273) (dimensions 107 57))) (sheet_comment (key 25) (text "") (loc 235 504)) (ClassBox (key 29) (represents 22) (frame (loc 226 660) (dimensions 181 122))) (ClassBox (key 120) (represents 89) (frame (loc 210 531) (dimensions 69 57))) (ClassBox (key 119) (represents 80) (frame (loc 291 531) (dimensions 142 83))) (ClassBox (key 351) (represents 334) (frame (loc 447 531) (dimensions 241 109))) (ClassBox (key 12) (represents 8) (frame (loc 226 358) (dimensions 181 135))) (ClassBox (key 352) (represents 343) (frame (loc 518 686) (dimensions 104 44))) (GeneralizationImage (key 131) (represents 24) (supernode 125 (408 236) (408 262)) (node 126 (506 264) (506 272))) (GeneralizationImage (key 130) (represents 23) (supernode 121 (602 236) (602 262)) (node 126 (506 264) (506 272))) (GeneralizationImage (key 153) (represents 118) (supernode 13 (506 115) (506 141)) (node 121 (606 143) (606 151)) (node 125 (409 143) (409 151))) (AssociationImage (key 306) (represents 300) (nodes 245 12) (points (125 310) (125 368) (225 368)) (arc_name 125 329 1)) (AssociationImage (key 332) (represents 329) (nodes 245 29) (points (76 310) (76 679) (225 679)) (arc_name 76 329 5)) (AssociationImage (key 14) (represents 9) (nodes 13 12) (points (595 55) (713 55) (713 367) (408 367)) (arc_name 644 55 7)) (AssociationImage (key 353) (represents 344) (nodes 12 29) (points (225 384) (139 384) (139 667) (225 667)) (arc_name 139 402 1)) (GeneralizationImage (key 354) (represents 347) (supernode 12 (316 494) (316 521)) (node 120 (244 523) (244 530)) (node 119 (362 523) (362 530)) (node 351 (567 523) (567 530))) (AssociationImage (key 355) (represents 348) (nodes 351 352) (points (568 641) (568 685)) (arc_name 568 663 1)))) (savekey_max 355)) casacore-3.7.1/casa/IO/IO_2.gif000066400000000000000000000142721476623553700157670ustar00rootroot00000000000000GIF87a<®ðÿÿÿ,<®þŒ©Ëí£œ´Ú‹³Þ¼û†âH–扦êʶî ÇòL×öçúÎ÷þ ‡Ä¢ñˆL*—̦ó J§ÔªõŠÍj·Ü®÷ ‹Çä²ùŒN«×ì¶û ËçôºýŽÏë÷ü¾ÿ(8HXhxˆ˜¨¸ÈØèø)9IYiy‰™©¹É)ð *:JZjzŠšªºÊÚêúúÙ) 0KQk››…«ûÀÛ õ¬0L|œdŒ ¼ìÔ|ýL½3|]­]“#Ê»Î<ÎÔ½þr~^~àþŽ ´ž~ŸbÏ-_;ÿMÎ\¼&úðQpÆ?2GÏHƒ;LŒÑ®ŸÆþ†ßˆXäbÅ‘Dª ø0à@ˆ&m´$ ÂK?‚⸱&È1{’˜¹âŸÍ‡ÚJ©Ï¥J16å¹4ªE‚O¡J½Š¡ê#­X rmôµkº°‹ÈŠÕ+­ÚµlÛº…u6®Ìmfå:«‹¯]it÷úE ×P࿺2LxbA‹sj ²ãL’ýTžlé2͘'½ý :´èÑ;;÷䬵é­}WwU¶ë²­gK•m·íCºéôÞ}¸6ðÓä50v.ÛMã·ü>>œ)‡f"¯ñúU]Éóè†]ÿ¾³¢ ÇÕX0ÇðóÌ›žBçbœš£þ뮢˜á|jªá¢ª(«‹*-aj²füøéJaØdz·ª#³µJI'¶Ú>Ó—d;ÕºË<%æ»eÀ+oøn›/2ûÞÛ/_hLLKÈw-8â$ˆÜ…-&×¢ ÿ¬…Á†FÜÍÁå^ -µÜIÅÀÜÉÒ«Ó:éϵ\ºÑBYå¤0›[§“-S˜•Ƚ𺢀¶ÂJó¬ÊØì鱎â8*‹'V+my9뜋ªé1J5•@ƒËêÆÿ-Çõ©ÇújtÇÂKksPÛÂ2Î'¦x2–i“ m¯‘öÒcÓ½ÁÄgSÁsÑ=[}#¨|û÷´3ëªØbsšpI{+æ@Évþø$ÖKJIÞÍ'Û\²Ì€—ºrà8;þx'í"„út˜¤wéPÐBUç=$’®oÒº¾·ã~æî•p[ŒÃržZvqÁ×^dî¾ãùñŸ¶¯”ÆÅ„òË׳lËÌÊü-ç>&ZåÌ8 k!¹Ó[sý%<ïr±åltkáG }|Љ a}úVutù֡Ú.­ên¥ráð–<ýý.{m[㨸Ä9cüþH‰õ5P~t‚Ÿ‡ä÷+ú-K‚çÓAþ0 bU®€CÉI =x9óÐdr Kè Jâ„\à¡{àÊýAÜÅY3°#‚…X˜’­X–·úI@PET¢þKÌ–$ŽÅ(zžŒ¬H›&öÏgãBÏübxCø¯m™#ÚªF…ŒPBM2ÜáüÂÙ±†àGŒ8¨ƒ¤úàæH51ΰ\$¼ŸU)f‘mˆ³–×È6Á9B{ä&?wµ:Æy$Ÿ"1‰C²“-ÞûhÆÂ”()†”¢Ì'Ÿ\±28IÜå VÉ7_òRYÂdLïŠÙ ^"Ó2LtÚ™Êæ9y_\&3±è'…Q/Š’„f ­yÍjiÏ™44ã5>5†NQáËå#Á™šO¦±p#’–àŒuO@Ū‘Õ„gAªP• i®ŠÕ¬jµvUÕRWýIÕ¯Ž$¬bSY­IÖ³â#­jÕS[…ÉÖ·2U®qŒ+]ÙuWVÚ5¯|Ù+_GÍ¿Þ.X‚U Ý kX-"vyÒ[,c=æØÝQ'²¾›†_)‰lbvoÊÙìÙ æY‘6´³ÇeIIi¢v]õZ-‘¶ ÛØÊv¶´­í(~zÚƒäÖL<ÝíZq ÜÞ7§¾½GqÝþJÜá¢ô¸—Jn²l ÝWL5ºÔ­®u¯‹Ýµ$• ÿb®_&¶×îR¼kšêx·»ñF–¼Ü5ïzÑ«õ:–½éuï|á;ª9(«éWäïÈ—‰ ¯E††àÈ©„ ©«cX2à†Ñ HYéù˜²®±Tî„ÓéÖP–J-…A°Ïœ¨8Ä,nñ~Qlßg˜Å²LÉŠ ÊÕ=8¿ÝÊðWìâ*Æׄ~Lc—Ù»¤;ñž°d “Ç["²eÌÈàYËüõ¤•ßå$‹Ù#: Ó—7ó½1#ÂTc^ðK»{®¦‡#pƒ‘õàY¢¬Å{~•½ìþäúLæ0œÏÐáÂÚÐgØ »c4$Z°àÍ®¥»{éLkzÓœ¾ê¡ùõèÂFÑÎë¨Aíàû–z “þë©êž𠭿ë¬Y½jºžzpƒŒð¼¹º/óš +ñ݈¤EõÓ|Gµ¦ØxÅ:bݼQŸld'míZ>r«¤äDºyÞY](—«°<52’´Í! ,w¶£¸=J¤ ÃÜÇͼûoŸleЂ÷2!ÏÈÀ«_Ç ™´t)´”?±oÁù{nÑ)œŽ]<(k?Í'¥ñÇ~Û©aêPqœŠ£¸<•ýÏÇ[ã%åÂïÍèi›|ä”ì”É7éÇþçbü”q¾z{„“¶oµ"×4&oÚg7ÐglŽAz?ÓmÄö,‰Ô3ó7w°Wz²÷P­B{ØvKÖ·}$´ h8.¨yjã>n×pÃW3ŒÄN`37WøDg˜|l¸—Iׇ}ú'D_Çsxê¶("ÖJhnVhdF)9ƒé„þ2L§ˆFT€^€˜÷F®q€¢ŠøG÷hÕi®Ø°(j²ˆ€©XVfø) !.Sä‹°ã†"¸–5Dø{C£|rç‹øÇI¿w1èŒ:t‚X4)sxzva>ÈŒ¥XnÔ(Snšè-£‡åwsˆS#/u4 H‰ÈGˆNƒƒIs„…Š8—5@HtZ(…$wˆ…·soèQC=M¨n/ŒºpbôoÕrýRŽ4§×Îç„7sÀ‰™Äçö‰wð}´$‰IwpžHD؈½†F-T>YSîg‘¶ˆŠ]wA䈓]0Š@ÄŠYrŠ>‰‹bþ5”=T”_u”[à’etÊvÙö8K¹ˆþ·Ð¨SœÕ“H9|›8“‡GHþ޹¤ƒË÷:F]íø~ö‰t ç‡AÙT Š©{ã$Nís"ùr$(”[É”jiQ)Q9 U’’˜UI‹‚ˆÏ‡˜b8’…(€IY—¹`Á~Îby_™bžX™:I€¢Išô·˜—™~|#—syšXð“V7€äÒXà°…èSšÈv˜Œ„i›þ§€pæ@í±`-•x~É‚)E(€•/‘æD§Gˆ‡êPjÂHt#ZðÅ?æ‚z Y<ªá’^y…d2ø(tQ‰{À qßÙ›þ˜ù‘k„â“WZê©tÕ8žb'Gv—†×‡§W‡‘™˜¹‘›½‰‚çýé‘9A?SrÒWA÷¹jaMY„™ÈH Ù™kf¦J”~c4ù=JSz›9雺֚Wðš¸ù¡]Vo8' ö<Á¶¢geŒpxIÑÖ…ª˜…©‹Vœ6 )Ãé2ç×ðØD*„TèEÄG›eg)ιžû I-6}oRÇ(¤pgmÑH(Q¨pÇW…ª·x`H“Ó¹Œ)ž™™(…7S°Ä=h‚ø©}Êžòyeº›¯¥ŽÉoKèsº‹#§‘{j¤YÊz@—Ö††,'mþhþLÓ‰‘@6¨IÀw¥štE¨¤)I–{WÌ©Kbˆ§Ÿq…$¦ö™¢j¦2Paj‰ïXzöD¥{™ª¦›9j«#Ø (8KÝPd6FAç_pZtéš–YUÐ £Ò UÔj1ªVØZÚÊ£/š­È¤ƒI¥ c¥N·£\i•ÔBXšŒ„Emé9]h ]0h¬í†n·¬'ª ŽW: ª¯oÈ«•Ç–&ªÃ²i9ñó56THÂs{ëT SÂ@5·‘ZRp#¯¿d±ñ9‡‚X°´PèjL û0àת8ÁgËÚ¬8ªmÿ²”F³*«j8»°çU³}@±1A±þû³"r³) ‘É "¸›OY«!´=K ú£YØi–P«ce¬»¸ŽÁ©g÷ʯ2k$O«³ v/Å{£”¡õIc[´;[P*ùD7IyKü8dk´¬ºGr¸„6‡vyû«„Á·p«{ ¸¡ä)mÚ±ÁÇ ‡{±+Û(̪xTˆ.¶´æ7tNû¶“›³} º²ö¹ÝÁ³ekºï…µ)Gº [ CK#Áñº§ º‰I¯·‹»¹›»8k»´NÎê}¾ëPƒºlª›º@j’Â˺È[¼¡›µüi Æ;¼ªš¼Ò»¼Ô½ÐÁ¡ƒcªÓ¯ñúgµÏëŒü©¦0p¾M+þ݇w)µžÐ'œÙçw¢w°Ù¨9?˜bR(gZÓ{ù”¶)ú¥Š„'xlüK¨Õ{dË>¸¹‹:®lª™œúJ L¾BÚ¼”êÎמ*Ÿ¤ô°tè®á«cp±¼ºŸù4JvXJ︂„sˆ…‹\¢{¼K’¤„MZ9õÛ9íÇx¨ »ý„¸E ¾A1¥*0Äï4ºM|Ä€˜ÄPœ‡¥û»EŠºU\‹«›~¢¢ç±ú»Äs ¹7ü0ÏÓYCJTì³ÏÈ‚ùz£‰½Æû§Æþ++üo¼¸{<2ÇoÀ:º ȼ f×À*\K HN|XiLÈÀj<ÙÁ…éKþÞÛÈexÉVƒÇGŒ-ÌÇŸ…ƬùÈ*u3Ç׹ؘ¿Ä‹²%(ʤȼY9d |É;8˯<ʱ,Ë·ŒÉºœÅ¼Œ?Lû1Ö¡_ô€¢­ÙÇ¿UËÐàÅVu£çÌ –®R«·.«0Àc'Ëy•ÌÁ¬Ç•LÈzš_QÂúËŽÉ\=tê-SÇa6ƒ4XT¾L®ÕöÄÂÂGK2Ô§´ÃjœÏVÏʇ¬SJÆeñB<¼fîlÌl|Nn¬Gp.ô»†^É—UžSø?â˜È‹ÑßB°¨Êsb©ož'Ä‚dÎ ò™Ínm&g…ŒÐÒÚ7Ñ©±n–† )Á†êÂŽHo{Hª[“Ÿ1þ¼6,¶üÓ{(©ˆ8‰%ýZ˜8~jFÍK½Íw̦ûY>zË–R «U}Ó9¿#›GÛ+4•—ZÁìŠÅ o¢: ìÌÍt“³©œzãžÞW ÞàÛÉÒIÆ¿íÇ)ï¦ÎmcÎvÎÂ1LíÚèÎÅUàÂæùPó‘ðUþnŽ!Ùâ“H5ØF`9Ì~î2zËŒ®©ë~(Íò®$¡•žçEß·î§{LOV„÷ô_ë%¹ØmË`?Ý{ëæ"ëÁ’‡õj›=Œ*ðÁ{î μ³½‘[öM½H]p©—‚R=Žî÷‡ÞíŒ×NíŒæÞµñgâ¿níîï°L$KEŠã‚BMÀ`½ÉìŽOC9ÿç|\c²Â9¡îäâc(Äg©†½ùóÍŽ¯öŽˆL/ãU?™‰åRÏáOF÷&BLéTŽù#uó±;ïX1óOnh¤ÒÀNæÇºÖ‘}ÕîÅÇ?ê·ïõÓŸý=4üÄ_ô þòMýþMÅùDžì½¼ýŒû¹ÈúÚOê Î ží޼þ:^ÿfõ—ºåŒ?þûÛì(NˆÁ.ã¶ŸR%9Y³úé+pòÀ,‚ÐT]ÙÖ}áXžéÚ¾ÕñÖñÞÿAáÐÆ£é0ÇEÉÉ <”' ºtR:UU9JN™Dñ˜\6·ŒÇóšÝv“Ó2Œ¥bJv_lª*Úx±îìøº¸®CÞÅâä`>Y&éò^èz2)›fþr"?:9×&1 Gµû¶¬÷^EP]aqïwy{cLO]t Ñ@eÕ’‹kb™W†=K}öZ³jõ°¯bUµ_oki¿µˆ}Ïѧ©›çÀ—Àþ:ËÙ­ìJtíEEãù.Ã*Ú@+ÞgeÌAæ ʽ~`èD»(ùmšh­O:Û$TˆjÜFpÞ¸Üû'.Y@xSÆm+(ÜL|‡n™ÄùÌѺ w‰:”h0MT29«sm悎€–’œUPJk¬pjøi'A¬NŽÊRQ6ͦU+Íg1¥1w¢l9K*S·ó²6j‹®6†«²JÛvmaÇӎå™Ï/D›ˆbªbæR.Ë~âì¦âæ¤éßÍW—õ<Š˜tiÓ¼‡&éNŠz&"_Œ(±u`/9µˆâeÝ%ñ$K$µ×ÓÇ‘'RÜøpafâ0o®ìœ±t£?•g×¾ý×SÄÐFF{õ³ä¹§W¯œ¹õõïƒwŸ~ýÞíçW$ßRÿÿ PÀ ,ÐÀLPÁlÐÁÄO¿ աРõ›C 5ÜÐÃÑéÄüDÑÄ+DQÅ‘VlÑÅê ŒQÆi¬ÑÆ_ÌQÇyìÑÇ RÈ!‰,ÒÈ#‘LRÉ%™lÒÉ'¡ŒRÊ)©¬ÒÊ+±ÌRË-¹ìÒË/Á SÌ1ɤ¡;casacore-3.7.1/casa/IO/IO_2.html000066400000000000000000000012661476623553700161650ustar00rootroot00000000000000


        Ger van Diepen (gvandiep@nfra.nl)
        Last modified: Wed Nov 20 14:38:38 1996 casacore-3.7.1/casa/IO/IO_2.omt000066400000000000000000000164321476623553700160210ustar00rootroot00000000000000(omt_version 5 0) # %W% %G% (omt_module (key 1) (anno target_file IO_2.cc) (name IO_2) (language C++) (fcode_max 1002) (omt_model (omt_class (key 22) (anno description "This class is the base class for writing streams of bytes to a device. The derived class should be each for a particular device. This class also contains virtual functions for checking if the device is readable, writable or seekable. The derived classes could be regularFileIO, StringIO or TapeIO. RegularFileIO is implemented now, other derived classes can be added easy later.") (name ByteIO) (method (key 141) (anno virtual_method YES) (anno description "These functions read or write streams of bytes to a particular device") (name write) (type void) (arglist (arg (key 163) (name size)) (arg (key 164) (name buf*)))) (method (key 144) (anno virtual_method YES) (anno description "") (name read) (type void) (arglist (arg (key 165) (name size)) (arg (key 166) (name buf*)))) (method (key 147) (anno description "This function sets the position on the given offset. The seek option defines from which file position the seek is done.") (name seek) (type Long) (arglist (arg (key 148) (name offset)) (arg (key 149) (name SeekOption)))) (method (key 150) (anno virtual_method YES) (anno const YES) (anno description "This function checks if one can read a device") (name isReadable) (type Bool)) (method (key 151) (anno virtual_method YES) (anno const YES) (anno description "This function checks if one can write a device") (name isWritable) (type Bool)) (method (key 152) (anno virtual_method YES) (anno const YES) (anno description "This function checks if one can seek in a device") (name isSeekable) (type "Bool "))) (omt_class (key 51) (anno description "This class is made to do IO on a RegularFile. It contains functions for reading and writing to a file and also seeking in a file is possible. The class also contains functions for checking if a file can be read, checking if a file can be read, written or if one can seek in a file.") (name RegularFileIO) (method (key 207) (anno description "") (arglist (arg (key 260) (name RegularFile)) (arg (key 261) (name OpenOption)) (arg (key 262) (name filebufSize)))) (field (key 64) (anno cxx_field_access private) (anno description "") (name itsOption) (type OpenOption)) (field (key 65) (anno cxx_field_access private) (anno description "") (name itsRegularFile) (type String))) (omt_class (key 96) (anno description "This class is made to do IO on a filebuf. It contains functions for reading and writing to a file and also seeking in a file is possible. ") (name FilebufIO) (method (key 218) (anno description "Construct from a given filebuf.") (arglist (arg (key 234) (name filebuf*)))) (method (key 220) (anno description "Construct from a given file descriptor.") (arglist (arg (key 251) (name fd)) (arg (key 252) (name filebufSize)))) (field (key 109) (anno cxx_field_access private) (anno description "") (name itsOwner) (type Bool)) (field (key 110) (anno cxx_field_access private) (anno description "") (name itsSeekable) (type Bool)) (field (key 111) (anno cxx_field_access private) (anno description "") (name itsFile) (type filebuf*)) (field (key 112) (anno cxx_field_access private) (anno description "") (name itsBuffer) (type char*))) (omt_class (key 330) (anno description "IO in memory") (name MemoryIO) (method (key 331) (anno description "") (arglist (arg (key 332) (name initialSize)) (arg (key 333) (name expandSize)))) (method (key 334) (anno description "") (arglist (arg (key 335) (name buffer*)) (arg (key 336) (name size)))) (method (key 337) (anno description "") (arglist (arg (key 338) (name buffer*)) (arg (key 339) (name size)) (arg (key 340) (name ByteIO) (type OpenOption)) (arg (key 341) (name expandSize.canDelete)))) (method (key 342) (anno const YES) (anno description "") (name getBuffer) (type uChar*)) (method (key 343) (anno const YES) (anno description "") (name length) (type Long)) (method (key 344) (anno const YES) (anno description "") (name allocated) (type uLong)) (method (key 345) (anno const YES) (anno description "") (name expandSize) (type uLong)) (field (key 346) (anno cxx_field_access private) (anno description "") (name itsBuffer) (type uChar*)) (field (key 347) (anno cxx_field_access private) (anno description "") (name itsLength) (type uLong)) (field (key 348) (anno cxx_field_access private) (anno description "") (name itsExpandSize) (type uLong)) (field (key 349) (anno cxx_field_access private) (anno description "") (name itsUsed) (type uLong)) (field (key 350) (anno cxx_field_access private) (anno description "") (name itsPosition) (type uLong)) (field (key 351) (anno cxx_field_access private) (anno description "") (name itsOwner) (type Bool)) (field (key 352) (anno cxx_field_access private) (anno description "") (name itsReadable) (type Bool)) (field (key 353) (anno cxx_field_access private) (anno description "") (name itsWritable) (type Bool)) (field (key 354) (anno cxx_field_access private) (anno description "") (name itsCanDelete) (type Bool))) (generalization_relation (key 355) (superclass 96) (subclasses 51)) (generalization_relation (key 356) (superclass 22) (subclasses 96 330))) (omt_image (sheet (key 2) (anno description "") (name OM) (model_type Object) (width 600) (height 430) (background_color white) (foreground_color black) (sheet_comment (key 25) (text "") (loc 335 383)) (ClassBox (key 122) (represents 51) (frame (loc 361 341) (dimensions 223 83))) (ClassBox (key 360) (represents 330) (frame (loc 6 168) (dimensions 330 252))) (ClassBox (key 124) (represents 96) (frame (loc 417 167) (dimensions 111 122))) (ClassBox (key 29) (represents 22) (frame (loc 274 5) (dimensions 181 122))) (GeneralizationImage (key 362) (represents 356) (supernode 29 (364 128) (364 157)) (node 124 (472 159) (472 166))) (GeneralizationImage (key 363) (represents 355) (supernode 124 (472 290) (472 330)) (node 122 (472 332) (472 340))) (GeneralizationImage (key 364) (represents 356) (supernode 29 (364 128) (364 157)) (node 360 (171 159) (171 167))))) (savekey_max 365)) casacore-3.7.1/casa/IO/IPositionIO.cc000066400000000000000000000061001476623553700172130ustar00rootroot00000000000000//# IPosition2.cc: A vector of integers, used to index into arrays (for Array) //# Copyright (C) 1993,1994,1995,1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# This source file is not needed if you aren't interested in converting //# to and from Array, i.e. if you don't want IPosition's to depend //# on arrays. #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN AipsIO& operator<< (AipsIO& aio, const IPosition& ip) { bool use32 = true; if (sizeof(ssize_t) > 4) { for (size_t i=0; i 2147483647) { use32 = false; break; } } } if (use32) { // Write values as int. aio.putstart("IPosition", 1); aio << (uInt) ip.nelements(); for (size_t i=0; i // ArrayError // AipsIO& operator>> (AipsIO& aio, IPosition& ip) { int vers = aio.getstart("IPosition"); uInt nel; aio >> nel; ip.resize (nel, false); if (vers == 1) { int v; for (size_t i=0; i> v; ip[i] = v; } } else if (vers == 2) { long long v; if (sizeof(ssize_t) <= 4) { throw ArrayError ("AipsIO& operator>>(AipsIO& aio, IPosition& ip) - " "cannot read back in an ssize_t of 4 bytes"); } for (size_t i=0; i> v; ip[i] = v; } } else { throw(ArrayError("AipsIO& operator>>(AipsIO& aio, IPosition& ip) - " "version on disk and in class do not match")); } aio.getend(); assert (ip.ok()); return aio; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/IO/LECanonicalIO.cc000066400000000000000000000363631476623553700174240ustar00rootroot00000000000000//# LECanonicalIO.cc: Class for IO in little endian canonical format //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LECanonicalIO::LECanonicalIO (const std::shared_ptr& byteIO, uInt bufferLength) : TypeIO (byteIO), itsBuffer (new char[bufferLength]), itsBufferLength (bufferLength) {} LECanonicalIO::LECanonicalIO (const LECanonicalIO& that) : TypeIO (that), itsBuffer (new char[that.itsBufferLength]), itsBufferLength (that.itsBufferLength) {} LECanonicalIO& LECanonicalIO::operator= (const LECanonicalIO& that) { if (this != &that) { TypeIO::operator= (that); if (itsBufferLength != that.itsBufferLength) { delete [] itsBuffer; itsBufferLength = that.itsBufferLength; itsBuffer = new char[itsBufferLength]; } } return *this; } LECanonicalIO::~LECanonicalIO() { delete [] itsBuffer; } size_t LECanonicalIO::write (size_t nvalues, const Bool* value) { return TypeIO::write (nvalues, value); } size_t LECanonicalIO::write (size_t nvalues, const Char* value) { if (CONVERT_LECAN_CHAR) { if (nvalues * SIZE_LECAN_CHAR <= itsBufferLength) { LECanonicalConversion::fromLocal (itsBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_LECAN_CHAR, itsBuffer); } else { char* tempBuffer = new char [nvalues * SIZE_LECAN_CHAR]; LECanonicalConversion::fromLocal (tempBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_LECAN_CHAR, tempBuffer); delete [] tempBuffer; } } else { itsByteIO->write (nvalues * sizeof(Char), value); } return nvalues * SIZE_LECAN_CHAR; } size_t LECanonicalIO::write (size_t nvalues, const uChar* value) { if (CONVERT_LECAN_UCHAR) { if (nvalues * SIZE_LECAN_UCHAR <= itsBufferLength) { LECanonicalConversion::fromLocal (itsBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_LECAN_UCHAR, itsBuffer); } else { char* tempBuffer = new char [nvalues * SIZE_LECAN_UCHAR]; LECanonicalConversion::fromLocal (tempBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_LECAN_UCHAR, tempBuffer); delete [] tempBuffer; } } else { itsByteIO->write (nvalues * sizeof(uChar), value); } return nvalues * SIZE_LECAN_UCHAR; } size_t LECanonicalIO::write (size_t nvalues, const Short* value) { if (CONVERT_LECAN_SHORT) { if (nvalues * SIZE_LECAN_SHORT <= itsBufferLength) { LECanonicalConversion::fromLocal (itsBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_LECAN_SHORT, itsBuffer); } else { char* tempBuffer = new char [nvalues * SIZE_LECAN_SHORT]; LECanonicalConversion::fromLocal (tempBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_LECAN_SHORT, tempBuffer); delete [] tempBuffer; } } else { itsByteIO->write (nvalues * sizeof(Short), value); } return nvalues * SIZE_LECAN_SHORT; } size_t LECanonicalIO::write (size_t nvalues, const uShort* value) { if (CONVERT_LECAN_USHORT) { if (nvalues * SIZE_LECAN_USHORT <= itsBufferLength) { LECanonicalConversion::fromLocal (itsBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_LECAN_USHORT, itsBuffer); } else { char* tempBuffer = new char [nvalues * SIZE_LECAN_USHORT]; LECanonicalConversion::fromLocal (tempBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_LECAN_USHORT, tempBuffer); delete [] tempBuffer; } } else { itsByteIO->write (nvalues * sizeof(uShort), value); } return nvalues * SIZE_LECAN_USHORT; } size_t LECanonicalIO::write(size_t nvalues, const Int* value) { if (CONVERT_LECAN_INT) { if (nvalues * SIZE_LECAN_INT <= itsBufferLength) { LECanonicalConversion::fromLocal (itsBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_LECAN_INT, itsBuffer); } else { char* tempBuffer = new char [nvalues * SIZE_LECAN_INT]; LECanonicalConversion::fromLocal (tempBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_LECAN_INT, tempBuffer); delete [] tempBuffer; } } else { itsByteIO->write (nvalues * sizeof(Int), value); } return nvalues * SIZE_LECAN_INT; } size_t LECanonicalIO::write(size_t nvalues, const uInt* value) { if (CONVERT_LECAN_UINT) { if (nvalues * SIZE_LECAN_UINT <= itsBufferLength) { LECanonicalConversion::fromLocal (itsBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_LECAN_UINT, itsBuffer); } else { char* tempBuffer = new char [nvalues * SIZE_LECAN_UINT]; LECanonicalConversion::fromLocal (tempBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_LECAN_UINT, tempBuffer); delete [] tempBuffer; } } else { itsByteIO->write (nvalues * sizeof(uInt), value); } return nvalues * SIZE_LECAN_UINT; } size_t LECanonicalIO::write(size_t nvalues, const Int64* value) { if (CONVERT_LECAN_INT64) { if (nvalues * SIZE_LECAN_INT64 <= itsBufferLength) { LECanonicalConversion::fromLocal (itsBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_LECAN_INT64, itsBuffer); } else { char* tempBuffer = new char [nvalues * SIZE_LECAN_INT64]; LECanonicalConversion::fromLocal (tempBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_LECAN_INT64, tempBuffer); delete [] tempBuffer; } } else { itsByteIO->write (nvalues * sizeof(Int64), value); } return nvalues * SIZE_LECAN_INT64; } size_t LECanonicalIO::write(size_t nvalues, const uInt64* value) { if (CONVERT_LECAN_UINT64) { if (nvalues * SIZE_LECAN_UINT64 <= itsBufferLength) { LECanonicalConversion::fromLocal (itsBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_LECAN_UINT64, itsBuffer); } else { char* tempBuffer = new char [nvalues * SIZE_LECAN_UINT64]; LECanonicalConversion::fromLocal (tempBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_LECAN_UINT64, tempBuffer); delete [] tempBuffer; } } else { itsByteIO->write (nvalues * sizeof(uInt64), value); } return nvalues * SIZE_LECAN_UINT64; } size_t LECanonicalIO::write(size_t nvalues, const float* value) { if (CONVERT_LECAN_FLOAT) { if (nvalues * SIZE_LECAN_FLOAT <= itsBufferLength) { LECanonicalConversion::fromLocal (itsBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_LECAN_FLOAT, itsBuffer); } else { char* tempBuffer = new char [nvalues * SIZE_LECAN_FLOAT]; LECanonicalConversion::fromLocal (tempBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_LECAN_FLOAT, tempBuffer); delete [] tempBuffer; } } else { itsByteIO->write (nvalues * sizeof(float), value); } return nvalues * SIZE_LECAN_FLOAT; } size_t LECanonicalIO::write(size_t nvalues, const double* value) { if (CONVERT_LECAN_DOUBLE) { if (nvalues * SIZE_LECAN_DOUBLE <= itsBufferLength) { LECanonicalConversion::fromLocal (itsBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_LECAN_DOUBLE, itsBuffer); } else { char* tempBuffer = new char [nvalues * SIZE_LECAN_DOUBLE]; LECanonicalConversion::fromLocal (tempBuffer, value, nvalues); itsByteIO->write (nvalues * SIZE_LECAN_DOUBLE, tempBuffer); delete [] tempBuffer; } } else { itsByteIO->write (nvalues * sizeof(double), value); } return nvalues * SIZE_LECAN_DOUBLE; } size_t LECanonicalIO::write (size_t nvalues, const Complex* value) { return TypeIO::write (nvalues, value); } size_t LECanonicalIO::write (size_t nvalues, const DComplex* value) { return TypeIO::write (nvalues, value); } size_t LECanonicalIO::write (size_t nvalues, const String* value) { return TypeIO::write (nvalues, value); } size_t LECanonicalIO::read (size_t nvalues, Bool* value) { return TypeIO::read (nvalues, value); } size_t LECanonicalIO::read (size_t nvalues, Char* value) { if (CONVERT_LECAN_CHAR) { if (nvalues * SIZE_LECAN_CHAR <= itsBufferLength) { itsByteIO->read (nvalues * SIZE_LECAN_CHAR, itsBuffer); LECanonicalConversion::toLocal (value, itsBuffer, nvalues); } else { char* tempBuffer = new char[nvalues * SIZE_LECAN_CHAR]; itsByteIO->read (nvalues * SIZE_LECAN_CHAR, tempBuffer); LECanonicalConversion::toLocal (value, tempBuffer, nvalues); delete [] tempBuffer; } } else { itsByteIO->read (nvalues * sizeof(Char), value); } return nvalues * SIZE_LECAN_CHAR; } size_t LECanonicalIO::read (size_t nvalues, uChar* value) { if (CONVERT_LECAN_UCHAR) { if (nvalues * SIZE_LECAN_UCHAR <= itsBufferLength) { itsByteIO->read (nvalues * SIZE_LECAN_UCHAR, itsBuffer); LECanonicalConversion::toLocal (value, itsBuffer, nvalues); } else { char* tempBuffer = new char[nvalues * SIZE_LECAN_UCHAR]; itsByteIO->read (nvalues * SIZE_LECAN_UCHAR, tempBuffer); LECanonicalConversion::toLocal (value, tempBuffer, nvalues); delete [] tempBuffer; } } else { itsByteIO->read (nvalues * sizeof(uChar), value); } return nvalues * SIZE_LECAN_UCHAR; } size_t LECanonicalIO::read (size_t nvalues, Short* value) { if (CONVERT_LECAN_SHORT) { if (nvalues * SIZE_LECAN_SHORT <= itsBufferLength) { itsByteIO->read (nvalues * SIZE_LECAN_SHORT, itsBuffer); LECanonicalConversion::toLocal (value, itsBuffer, nvalues); } else { char* tempBuffer = new char[nvalues * SIZE_LECAN_SHORT]; itsByteIO->read (nvalues * SIZE_LECAN_SHORT, tempBuffer); LECanonicalConversion::toLocal (value, tempBuffer, nvalues); delete [] tempBuffer; } } else { itsByteIO->read (nvalues * sizeof(Short), value); } return nvalues * SIZE_LECAN_SHORT; } size_t LECanonicalIO::read (size_t nvalues, uShort* value) { if (CONVERT_LECAN_USHORT) { if (nvalues * SIZE_LECAN_USHORT <= itsBufferLength) { itsByteIO->read (nvalues * SIZE_LECAN_USHORT, itsBuffer); LECanonicalConversion::toLocal (value, itsBuffer, nvalues); } else { char* tempBuffer = new char[nvalues * SIZE_LECAN_USHORT]; itsByteIO->read (nvalues * SIZE_LECAN_USHORT, tempBuffer); LECanonicalConversion::toLocal (value, tempBuffer, nvalues); delete [] tempBuffer; } } else { itsByteIO->read (nvalues * sizeof(uShort), value); } return nvalues * SIZE_LECAN_USHORT; } size_t LECanonicalIO::read (size_t nvalues, Int* value) { if (CONVERT_LECAN_INT) { if (nvalues * SIZE_LECAN_INT <= itsBufferLength) { itsByteIO->read (nvalues * SIZE_LECAN_INT, itsBuffer); LECanonicalConversion::toLocal (value, itsBuffer, nvalues); } else { char* tempBuffer = new char[nvalues * SIZE_LECAN_INT]; itsByteIO->read (nvalues * SIZE_LECAN_INT, tempBuffer); LECanonicalConversion::toLocal (value, tempBuffer, nvalues); delete [] tempBuffer; } } else { itsByteIO->read (nvalues * sizeof(Int), value); } return nvalues * SIZE_LECAN_INT; } size_t LECanonicalIO::read (size_t nvalues, uInt* value) { if (CONVERT_LECAN_UINT) { if (nvalues * SIZE_LECAN_UINT <= itsBufferLength) { itsByteIO->read (nvalues * SIZE_LECAN_UINT, itsBuffer); LECanonicalConversion::toLocal (value, itsBuffer, nvalues); } else { char* tempBuffer = new char[nvalues * SIZE_LECAN_UINT]; itsByteIO->read (nvalues * SIZE_LECAN_UINT, tempBuffer); LECanonicalConversion::toLocal (value, tempBuffer, nvalues); delete [] tempBuffer; } } else { itsByteIO->read (nvalues * sizeof(uInt), value); } return nvalues * SIZE_LECAN_UINT; } size_t LECanonicalIO::read (size_t nvalues, Int64* value) { if (CONVERT_LECAN_INT64) { if (nvalues * SIZE_LECAN_INT64 <= itsBufferLength) { itsByteIO->read (nvalues * SIZE_LECAN_INT64, itsBuffer); LECanonicalConversion::toLocal (value, itsBuffer, nvalues); } else { char* tempBuffer = new char[nvalues * SIZE_LECAN_INT64]; itsByteIO->read (nvalues * SIZE_LECAN_INT64, tempBuffer); LECanonicalConversion::toLocal (value, tempBuffer, nvalues); delete [] tempBuffer; } } else { itsByteIO->read (nvalues * sizeof(Int64), value); } return nvalues * SIZE_LECAN_INT64; } size_t LECanonicalIO::read (size_t nvalues, uInt64* value) { if (CONVERT_LECAN_UINT64) { if (nvalues * SIZE_LECAN_UINT64 <= itsBufferLength) { itsByteIO->read (nvalues * SIZE_LECAN_UINT64, itsBuffer); LECanonicalConversion::toLocal(value, itsBuffer, nvalues); } else { char* tempBuffer = new char[nvalues * SIZE_LECAN_UINT64]; itsByteIO->read (nvalues * SIZE_LECAN_UINT64, tempBuffer); LECanonicalConversion::toLocal (value, tempBuffer, nvalues); delete [] tempBuffer; } } else { itsByteIO->read (nvalues * sizeof(uInt64), value); } return nvalues * SIZE_LECAN_UINT64; } size_t LECanonicalIO::read (size_t nvalues, float* value) { if (CONVERT_LECAN_FLOAT) { if (nvalues * SIZE_LECAN_FLOAT <= itsBufferLength) { itsByteIO->read (nvalues * SIZE_LECAN_FLOAT, itsBuffer); LECanonicalConversion::toLocal (value, itsBuffer, nvalues); } else { char* tempBuffer = new char[nvalues * SIZE_LECAN_FLOAT]; itsByteIO->read (nvalues * SIZE_LECAN_FLOAT, tempBuffer); LECanonicalConversion::toLocal (value, tempBuffer, nvalues); delete [] tempBuffer; } } else { itsByteIO->read (nvalues * sizeof(float), value); } return nvalues * SIZE_LECAN_FLOAT; } size_t LECanonicalIO::read (size_t nvalues, double* value) { if (CONVERT_LECAN_DOUBLE) { if (nvalues * SIZE_LECAN_DOUBLE <= itsBufferLength) { itsByteIO->read (nvalues * SIZE_LECAN_DOUBLE, itsBuffer); LECanonicalConversion::toLocal (value, itsBuffer, nvalues); } else { char* tempBuffer = new char[nvalues * SIZE_LECAN_DOUBLE]; itsByteIO->read (nvalues * SIZE_LECAN_DOUBLE, tempBuffer); LECanonicalConversion::toLocal (value, tempBuffer, nvalues); delete [] tempBuffer; } } else { itsByteIO->read (nvalues * sizeof(double), value); } return nvalues * SIZE_LECAN_DOUBLE; } size_t LECanonicalIO::read (size_t nvalues, Complex* value) { return TypeIO::read (nvalues, value); } size_t LECanonicalIO::read (size_t nvalues, DComplex* value) { return TypeIO::read (nvalues, value); } size_t LECanonicalIO::read (size_t nvalues, String* value) { return TypeIO::read (nvalues, value); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/IO/LECanonicalIO.h000066400000000000000000000130161476623553700172540ustar00rootroot00000000000000//# LECanonicalIO.h: Class for IO in little endian canonical format //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_LECANONICALIO_H #define CASA_LECANONICALIO_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class ByteIO; class String; // Class for IO in little endian canonical format. // // // // //
      • ByteIO class and derived classes //
      • TypeIOclass //
      • LECanonicalConversion // // // LECanonicalIO is a specialization of class TypeIO to store // data in little endian canonical format. //

        // The class converts the data to/from canonical data and reads/writes // them from/into the ByteIO object given at construction time. // Conversion is only done when really needed. If not needed, the // data is directly read or written. //

        // LECanonical format is little-endian IEEE format, where longs are 8 bytes. // Bools are stored as bits to be as space-efficient as possible. // This means that on a 32-bit SUN or HP conversions only have to be done // for Bools and longs. For a DEC-alpha, however, the data will always // be converted because it is a little-endian machine. // class LECanonicalIO: public TypeIO { public: // Constructor. // The read/write functions will use the given ByteIO object // as the data store. //

        // The read and write functions use an intermediate buffer to hold the data // in canonical format. For small arrays it uses a fixed buffer with // length bufferLength. For arrays not fitting in this buffer, // it uses a temporary buffer allocated on the heap. explicit LECanonicalIO (const std::shared_ptr& byteIO, uInt bufferLength=4096); // The copy constructor uses reference semantics LECanonicalIO (const LECanonicalIO& canonicalIO); // The assignment operator uses reference semantics LECanonicalIO& operator= (const LECanonicalIO& canonicalIO); // Destructor, deletes allocated memory. ~LECanonicalIO(); // Convert the values and write them to the ByteIO object. // Bool, complex and String values are handled by the base class. // virtual size_t write (size_t nvalues, const Bool* value); virtual size_t write (size_t nvalues, const Char* data); virtual size_t write (size_t nvalues, const uChar* data); virtual size_t write (size_t nvalues, const Short* data); virtual size_t write (size_t nvalues, const uShort* data); virtual size_t write (size_t nvalues, const Int* data); virtual size_t write (size_t nvalues, const uInt* data); virtual size_t write (size_t nvalues, const Int64* data); virtual size_t write (size_t nvalues, const uInt64* data); virtual size_t write (size_t nvalues, const Float* data); virtual size_t write (size_t nvalues, const Double* data); virtual size_t write (size_t nvalues, const Complex* value); virtual size_t write (size_t nvalues, const DComplex* value); virtual size_t write (size_t nvalues, const String* value); // // Read the values from the ByteIO object and convert them. // Bool, complex and String values are handled by the base class. // virtual size_t read (size_t nvalues, Bool* value); virtual size_t read (size_t nvalues, Char* data); virtual size_t read (size_t nvalues, uChar* data); virtual size_t read (size_t nvalues, Short* data); virtual size_t read (size_t nvalues, uShort* data); virtual size_t read (size_t nvalues, Int* data); virtual size_t read (size_t nvalues, uInt* data); virtual size_t read (size_t nvalues, Int64* data); virtual size_t read (size_t nvalues, uInt64* data); virtual size_t read (size_t nvalues, Float* data); virtual size_t read (size_t nvalues, Double* data); virtual size_t read (size_t nvalues, Complex* value); virtual size_t read (size_t nvalues, DComplex* value); virtual size_t read (size_t nvalues, String* value); // private: //# The buffer char* itsBuffer; uInt itsBufferLength; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/IO/LargeIOFuncDef.h000066400000000000000000000060531476623553700174340ustar00rootroot00000000000000//# LargeIOFuncDef.cc: Header to define possible large IO function names //# Copyright (C) 2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_LARGEIOFUNCDEF_H #define CASA_LARGEIOFUNCDEF_H //

        // Defines for correct name of functions to access large files. // // // // //
        // The defines in this file let us instrument the IO system using PABLO. // See www-pablo.cs.uiuc.edu for more about pablo. // // If AIPS_NOLARGEFILE is not defined, use the large file functions. // Define _LARGEFILE64_SOURCE for Linux systems. // #if !defined(AIPS_NOLARGEFILE) #if defined(AIPS_LINUX) # if !defined(_LARGEFILE64_SOURCE) # define _LARGEFILE64_SOURCE # endif # endif #if defined(PABLO_IO) # include "IOTrace.h" # define traceFOPEN fopen64 # define traceFSEEK fseeko64 # define traceFTELL ftello64 # define trace2OPEN open64 # define traceLSEEK lseek64 # define trace3OPEN open64 # else # define traceFOPEN fopen64 # define traceFCLOSE fclose # define traceFSEEK fseeko64 # define traceFTELL ftello64 # define traceFREAD fread # define traceFWRITE fwrite # define traceREAD read # define tracePREAD pread # define traceWRITE write # define tracePWRITE pwrite # define trace2OPEN open64 # define traceLSEEK lseek64 # define trace3OPEN open64 # define traceCLOSE close # endif #else # define traceFTELL ftell #if defined(PABLO_IO) # include "IOTrace.h" # else # define traceFOPEN fopen # define traceFCLOSE fclose # define traceFSEEK fseek # define traceFREAD fread # define traceFWRITE fwrite # define traceREAD read # define tracePREAD pread # define traceWRITE write # define tracePWRITE pwrite # define trace2OPEN open # define traceLSEEK lseek # define trace3OPEN open # define traceCLOSE close # endif #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/IO/LockFile.cc000066400000000000000000000326111476623553700165440ustar00rootroot00000000000000//# LockFile.cc: Class to handle file locking //# Copyright (C) 1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include // PABLO_IO is used to profile the IO performance of the Casacore (in // particular to help us locate bottlenecks associated with parallel // processing) // // Please see http://www-pablo.cs.uiuc.edu if you need more details // You'll need to set PABLO_IO variable in the makedefs as well as the // -I flag to find the header file and -L to resolve with the library. // #ifdef PABLO_IO #include "IOTrace.h" #else #define traceFCLOSE fclose #define traceFSEEK fseek #define traceFREAD fread #define traceFWRITE fwrite #define traceWRITE write #define tracePWRITE pwrite #define tracePREAD pread #define trace2OPEN open #define trace3OPEN open #define traceLSEEK lseek #define traceCLOSE close #endif // PABLO_IO //# canonical size of an Int (checked in constructor). #define SIZEINT 4u #define NRREQID 32u #define SIZEREQID ((1 + 2*NRREQID) * SIZEINT) namespace casacore { //# NAMESPACE CASACORE - BEGIN LockFile::LockFile (const String& fileName, double inspectInterval, Bool create, Bool setRequestFlag, Bool mustExist, uInt seqnr, Bool permLocking, Bool noLocking) : itsWritable (True), itsAddToList (setRequestFlag), itsInterval (inspectInterval), itsPid (getpid()), /// itsHostId (gethostid()), gethostid is not declared in unistd.h itsHostId (0), itsReqId (SIZEREQID/SIZEINT, (Int)0), itsInspectCount(0) { AlwaysAssert (SIZEINT == CanonicalConversion::canonicalSize (static_cast(0)), AipsError); itsName = Path(fileName).absoluteName(); //# If needed, create the file if it does not exist yet. //# If the flag is set, it is allowed that the file does not //# exist and cannot be created. In that case it is assumed that //# later on each locking request is successful (without doing actual //# locking). if (!noLocking && !create) { File f (itsName); if (! f.exists()) { if (!f.canCreate() && !mustExist) { return; // Acceptable that lock file does not exist } create = True; } } //# Open the lock file as read/write if it exists. //# If it did not succeed, open as readonly. //# For noLocking, it does not need to exist. int fd = -1; if (!create) { fd = FiledesIO::open (itsName.chars(), True, False); if (fd == -1) { fd = FiledesIO::open (itsName.chars(), False, !noLocking); itsWritable = False; itsAddToList = False; } } else if (!noLocking) { //# Create a new file with world write access. //# Initialize the values in it. fd = FiledesIO::create (itsName.chars(), 0666); putReqId (fd); } if (fd >= 0) { //# Create FileLocker objects for this lock file. //# The first one is for read/write locks. //# The second one is to set the file to "in use" and to tell if //# permanent locking is used. itsLocker = FileLocker (fd, 4*seqnr, 1); if (permLocking) { itsUseLocker = FileLocker (fd, 4*seqnr+1, 2); } else { itsUseLocker = FileLocker (fd, 4*seqnr+1, 1); } if (!noLocking) { itsFileIO.reset (new FiledesIO (fd, itsName)); // Set the file to in use by acquiring a read lock. itsUseLocker.acquire (FileLocker::Read, 1); } } } LockFile::~LockFile() { int fd = itsLocker.fd(); if (fd >= 0) { FiledesIO::close (fd); } } Bool LockFile::isMultiUsed() { //# If a write lock cannot be obtained, the file is in use. return ( (itsUseLocker.fd() >= 0 && !itsUseLocker.canLock (FileLocker::Write))); } Bool LockFile::acquire (MemoryIO* info, FileLocker::LockType type, uInt nattempts) { //# If no lock file, lock requests always succeed, //# but we cannot return any info. if (!itsFileIO) { if (info != 0) { info->clear(); } return True; } //# Try to set a lock without waiting. Bool succ = itsLocker.acquire (type, 1); Bool added = False; //# When unsuccessful and multiple attempts have to be done, //# add the process to the request list (if needed) and try to acquire. if (!succ && nattempts != 1) { if (itsAddToList) { addReqId(); added = True; } succ = itsLocker.acquire (type, nattempts); } //# Do not read info if we did not acquire the lock. if (!succ) { info = 0; } //# Read the info when needed. //# This also reads the request id's. //# If no info is needed, read req id's only when needed. //# Note that each IO-operation is quite expensive, so do as few //# IO's as possible. if (info != 0) { getInfo (*info); } else if (added) { getReqId(); } //# Remove this id from the request list (if added). if (added) { removeReqId(); } //# Reset the inspection time. itsLastTime.now(); itsInspectCount = 0; return succ; } Bool LockFile::release (const MemoryIO* info) { //# If no lock file, lock requests are not really handled. if (!itsFileIO) { return True; } if (info != 0) { putInfo (*info); } return itsLocker.release(); } Bool LockFile::inspect (Bool always) { //# If no lock file, lock requests are not really handled. if (!itsFileIO) { return False; } if (!always) { //# Only check elapsed time every n-th request (where n=25 at present), //# as the elapsed time calculation is computationally expensive if (itsInterval > 0 && itsInspectCount++ < 25) { return False; } itsInspectCount = 0; //# Only inspect if time interval has passed. if (itsInterval > 0 && itsLastTime.age() < itsInterval) { return False; } } //# Get the number of request id's and reset the time. uInt nr = getNrReqId(); itsLastTime.now(); return (nr > 0); } void LockFile::getInfo (MemoryIO& info) { // Do nothing if no locking. if (itsLocker.fd() < 0) { return; } // The lock file contains: // - the fixed length request list in the first bytes // - thereafter the length of the info (as a uInt) // - thereafter the entire info uChar buffer[2048]; // Read the first part of the file. traceLSEEK (itsLocker.fd(), 0, SEEK_SET); uInt leng = ::read (itsLocker.fd(), buffer, sizeof(buffer)); // Extract the request list from it. convReqId (buffer, leng); // Get the length of the info. uInt infoLeng = getInt (buffer, leng, SIZEREQID); // Clear the MemoryIO object. info.clear(); if (infoLeng == 0) { return; } // Get the length of the info in the part already read. // (subtract length of request list and the info-length uInt) leng -= SIZEREQID+SIZEINT; if (leng > infoLeng) { leng = infoLeng; } info.seek (Int64(0)); info.write (leng, buffer + SIZEREQID + SIZEINT); // Read the remaining info parts. if (infoLeng > leng) { infoLeng -= leng; uChar* buf = new uChar[infoLeng]; AlwaysAssert (::read (itsLocker.fd(), buf, infoLeng) == Int(infoLeng), AipsError); info.write (infoLeng, buf); delete [] buf; } info.seek (Int64(0)); } void LockFile::putInfo (const MemoryIO& info) const { uInt infoLeng = const_cast(info).length(); if (itsLocker.fd() < 0 || !itsWritable || infoLeng == 0) { return; } // Write the info into the lock file preceeded by its length. uChar buffer[1024]; uInt leng = CanonicalConversion::fromLocal (buffer, infoLeng); if (infoLeng > 1024 - leng) { // Too large for the buffer, so write length and info separately. traceLSEEK (itsLocker.fd(), SIZEREQID, SEEK_SET); AlwaysAssert (traceWRITE (itsLocker.fd(), (Char *)buffer, leng) == Int(leng), AipsError); AlwaysAssert (traceWRITE (itsLocker.fd(), (Char *)info.getBuffer(), infoLeng) == Int(infoLeng), AipsError); }else{ // Info fits in the buffer, so copy and do a single write. memcpy (buffer+leng, info.getBuffer(), infoLeng); AlwaysAssert (tracePWRITE (itsLocker.fd(), (Char *)buffer, leng+infoLeng, SIZEREQID) == Int(leng+infoLeng), AipsError); } // Do an fsync to achieve NFS synchronization. fsync (itsLocker.fd()); } Int LockFile::getNrReqId() const { uChar buffer[8]; uInt leng = tracePREAD (itsLocker.fd(), buffer, SIZEINT, 0); return getInt (buffer, leng, 0); } Int LockFile::getInt (const uChar* buffer, uInt leng, uInt offset) const { if (leng < offset + SIZEINT) { return 0; } Int value; CanonicalConversion::toLocal (value, buffer+offset); return value; } void LockFile::convReqId (const uChar* buffer, uInt leng) { if (leng >= SIZEREQID) { CanonicalConversion::toLocal (itsReqId.storage(), buffer, SIZEREQID/SIZEINT); } } void LockFile::addReqId() { //# Add the id at the last free place in the block. //# If full, use last element. This is better than ignoring it, //# because in this way the last request is always known. uInt inx = itsReqId[0]; if (inx >= NRREQID) { inx = NRREQID-1; } itsReqId[0] = inx+1; itsReqId[2*inx+1] = itsPid; itsReqId[2*inx+2] = itsHostId; putReqId (itsLocker.fd()); } void LockFile::removeReqId() { Int i; //# Remove the id and all previous id's from the block. //# In principle previous id's should not occur, but it //# can happen when a process with an outstanding request died. Int nr = itsReqId[0]; for (i=0; i 0) { CanonicalConversion::fromLocal (buffer, itsReqId.storage(), itsReqId.nelements()); } } uInt LockFile::showLock (uInt& pid, Bool& permLocked, const String& fileName) { pid = 0; permLocked = False; String fullName = Path(fileName).absoluteName(); File f (fullName); if (! f.exists()) { throw AipsError ("LockFile::showLock - File " + fileName + " does not exist"); } //# Open the lock file as readonly. int fd = FiledesIO::open (fullName.chars(), False); if (fd == -1) { throw AipsError ("LockFile::showLock - File " + fileName + " could not be opened"); } // The first byte is for read/write locking. // The second byte is to see if the file is used in another process. // The third one is to see if the file is permanently locked. FileLocker fileLocker (fd, 0, 1); FileLocker useLocker (fd, 1, 1); FileLocker permLocker (fd, 2, 1); // Determine if the file is opened in another process. // If not, we can exit immediately. uInt usePid; if (useLocker.canLock (usePid, FileLocker::Write)) { return 0; } uInt result; // If we cannot readlock, the file is writelocked elsewhere. // If we cannot writelock, the file is readlocked elsewhere. // Otherwise the file is simply in use. if (! fileLocker.canLock (pid, FileLocker::Read)) { result = 3; } else if (! fileLocker.canLock (pid, FileLocker::Write)) { result = 2; } else { pid = usePid; return 1; } if (! permLocker.canLock (usePid, FileLocker::Write)) { permLocked = True; } return result; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/IO/LockFile.h000066400000000000000000000362121476623553700164070ustar00rootroot00000000000000//# LockFile.h: Class to handle file locking and synchronization //# Copyright (C) 1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_LOCKFILE_H #define CASA_LOCKFILE_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations class FiledesIO; class MemoryIO; class CanonicalIO; // // Class to handle file locking and synchronization. // // // // // //
      • class FileLocker //
      • class MemoryIO // // // This class handles file locking by means of a special lock file // which serves as the locking mechanism for another file or // group of files. It is for instance used to lock a table in // the Casacore Table System. //

        // The lock file has in principle world read/write access, so every // process accessing the main file can write information in it. // The lock file contains the following information (in canonical format): //

          //
        • A request list indicating which processes want to acquire a lock. // The process holding the lock can inspect this list to decide if it // should release its lock. An interval can be defined to be sure // that the list is not inspected too often. // A user can choose not to add to this list, because it incurs some // overhead to write the list. However, that should only be done when // one is sure that another process cannot keep a lock forever. //
        • Some information telling if the state of the main file has changed. // The information can be used by a process to synchronize its // internal buffers with the new contents of the file(s). // E.g. a table could store one or more counters in it, which can be // used to determine if the table has to refresh its caches. // This information is passed as a MemoryIO object and is opaque // for the LockFile class. It is simply handled as a // stream of bytes. //
        //

        // Acquiring a lock works as follows: //

          //
        • Class FileLocker is used // to do one attempt to acquire a read or write lock. //
        • If it fails and multiple attempts have to be done, the // request is added to the request list in the lock file to tell // the process holding the lock that another process needs a lock. //
        • Other attempts (with 1 second intervals) will be done until the // lock is acquired or until the maximum number of attempts is reached. //
        • The lock request is removed from the request list. //
        • When the lock was acquired, the synchronization info is read // from the lock file. //
        // Releasing a lock writes the synchronization info into the lock file // and tells FileLocker to release the lock. //

        // When the lock file cannot be opened as read/write, it is opened as // readonly. It means that the request list cannot be stored in it, // so the process has no way to tell the other processes it wants // access to the file. It has to wait until the lock is released. //
        In principle a lock file should always be there. However, it // is possible (with a constructor option) that there is no lock file. // In that case each lock request succeeds without doing actual locking. // This mode is needed to be able to handle readonly tables containing // no lock file. //

        // After each write the fsync function is called to make // sure that the contents of the file are written to disk. This is // necessary for correct file synchronization in NFS. // However, at the moment this feature is switched off, because it // degraded performance severely. //

        // Apart from the read/write lock handling, the LockFile // also contains a mechanism to detect if a file is opened by another // process. This can be used to test if a process can safely delete the file. // For this purpose it sets another read lock when the file gets opened. // The function isMultiUsed tests this lock to see if the file is // used in other processes. //
        This lock is also used to tell if the file is permanently locked. // If that is the case, the locked block is 2 bytes instead of 1. //

        // When in the same process multiple LockFile objects are created for the same // file, deleting one object releases all locks on the file, thus also the // locks held by the other LockFile objects. This behaviour is due to the way // file locking is working on UNIX machines (certainly on Solaris 2.6). // One can use the test program tLockFile to test for this behaviour. // // // // // Create/open the lock file (with 1 sec inspection interval). // // Acquire the lock and get the synchronization info. // LockFile lock ("file.name", 1); // MemoryIO syncInfo; // if (! lock.acquire (syncInfo)) { // throw (AipsError ("Locking failed: " + lock.message())); // } // while (...) { // ... do something with the table files ... // // Test if another process needs the files. // // If so, synchronize files and release lock. // if (lock.inspect()) { // do fsync for all other files // syncInfo.seek (0); // syncInfo.write (...); // lock.release (syncInfo); // // At this point another process can grab the lock. // // Reacquire the lock // lock.acquire (syncInfo); // throw (AipsError ("Locking failed: " + lock.message())); // } // } // } // // // // Make it possible to lock and synchronize tables in an easy and // efficient way. // class LockFile { public: // Create or open the lock file with the given name. // It is created if create=True or if the file does not exist yet. // The interval (in seconds) defines how often function inspect // inspects the request list in the lock file. // An interval>0 means that it is only inspected if the last inspect // was at least inspectInterval seconds ago. // An interval<=0 means that inspect always inspects // the request list. //
        When addToRequestList=False, function acquire does not // add the request to the lock file when a lock cannot be acquired. // This may result in better performance, but should be used with care. //
        If create==True, a new lock file will always be created. // Otherwise it will be created if it does not exist yet. //
        If mustExist==False, it is allowed that the LockFile // does not exist and cannot be created either. //
        The seqnr is used to set the offset where LockFile will use 2 bytes // to set the locks on. Only in special cases it should be other than 0. // At the moment the offset is 2*seqnr. //
        The permLocking argument is used to indicate if // permanent locking will be used. If so, it'll indicate so. In that // way showLock() can find out if if table is permanently locked. //
        The noLocking argument is used to indicate that // no locking is needed. It means that acquiring a lock always succeeds. explicit LockFile (const String& fileName, double inspectInterval = 0, Bool create = False, Bool addToRequestList = True, Bool mustExist = True, uInt seqnr = 0, Bool permLocking = False, Bool noLocking = False); // The destructor does not delete the file, because it is not known // when the last process using the lock file will stop. // For the table system this is no problem, because the lock file // is contained in the directory of the table, thus deleted when // the table gets deleted. ~LockFile(); // Is the file associated with the LockFile object in use in // another process? Bool isMultiUsed(); // Acquire a read or write lock. // It reads the information (if the info argument is given) // from the lock file. The user is responsible for interpreting the // information (e.g. converting from canonical to local format). // The seek pointer in the MemoryIO object is set to 0, // so the user can simply start reading the pointer. //
        The argument nattempts tells how often it is // attempted (with 1 second intervals) to acquire the lock if // it does not succeed. // 0 means forever, while 1 means do not retry. // Bool acquire (FileLocker::LockType = FileLocker::Write, uInt nattempts = 0); Bool acquire (MemoryIO& info, FileLocker::LockType = FileLocker::Write, uInt nattempts = 0); Bool acquire (MemoryIO* info, FileLocker::LockType type, uInt nattempts); // // Release a lock and write the information (if given) into the lock file. // The user is responsible for making the information machine-independent // (e.g. converting from local to canonical format). // Bool release(); Bool release (const MemoryIO& info); Bool release (const MemoryIO* info); // // Inspect if another process wants to access the file (i.e. if the // request list is not empty). // It only inspects if the time passed since the last inspection // exceeds the inspection interval as given in the constructor. // If the time passed is too short, False is returned (indicating // that no access is needed). // If always==True, no test on inspection interval is done, // so the inspect is always done. Bool inspect (Bool always=False); // Test if the file can be locked for read or write. Bool canLock (FileLocker::LockType = FileLocker::Write); // Test if the process has a lock for read or write on the file. Bool hasLock (FileLocker::LockType = FileLocker::Write) const; // Get the last error. int lastError() const; // Get the message belonging to the last error. String lastMessage() const; // Get the name of the lock file. const String& name() const; // Get the block of request id's. const Block& reqIds() const; // Get the request id's and the info from the lock file. void getInfo (MemoryIO& info); // Put the info into the file (after the request id's). void putInfo (const MemoryIO& info) const; // Tell if another process holds a read or write lock on the given file // or has the file opened. It returns: //
        3 if write-locked elsewhere. //
        2 if read-locked elsewhere. //
        1 if opened elsewhere. //
        0 if locked nor opened. //
        It fills in the PID of the process having the file locked or opened. //
        If locked, it also tells if it is permanently locked. //
        An exception is thrown if the file does not exist or cannot // be opened. static uInt showLock (uInt& pid, Bool& permLocked, const String& fileName); private: // The copy constructor cannot be used (its semantics are too difficult). LockFile (const LockFile&); // Assignment cannot be used (its semantics are too difficult). LockFile& operator= (const LockFile&); // Get an Int from the buffer at the given offset and convert // it from canonical to local format. // If the buffer is too short (i.e. does not contain the value), // a zero value is returned. Int getInt (const uChar* buffer, uInt leng, uInt offset) const; // Add the request id of this process to the list. void addReqId(); // Remove the request id of this process from the list // (and all the ones before it). void removeReqId(); // Get the request list from the file. void getReqId(); // Put the request list into the file. void putReqId (int fd) const; // Convert the request id from canonical to local format. void convReqId (const uChar* buffer, uInt leng); // Get the number of request id's. Int getNrReqId() const; //# The member variables. FileLocker itsLocker; FileLocker itsUseLocker; std::shared_ptr itsFileIO; Bool itsWritable; //# lock file is writable? Bool itsAddToList; //# Should acquire add to request list? double itsInterval; //# interval between inspections Time itsLastTime; //# time of last inspection String itsName; //# Name of lock file uInt itsPid; uInt itsHostId; Block itsReqId; //# Id's of processes requesting lock //# First value contains #req id's //# Thereafter pid, hostid Int itsInspectCount; //# The number of times inspect() has //# been called since the last elapsed //# time check. }; inline Bool LockFile::acquire (FileLocker::LockType type, uInt nattempts) { return acquire (0, type, nattempts); } inline Bool LockFile::acquire (MemoryIO& info, FileLocker::LockType type, uInt nattempts) { return acquire (&info, type, nattempts); } inline Bool LockFile::release() { return release (0); } inline Bool LockFile::release (const MemoryIO& info) { return release (&info); } inline Bool LockFile::canLock (FileLocker::LockType type) { return (itsFileIO == 0 ? True : itsLocker.canLock (type)); } inline Bool LockFile::hasLock (FileLocker::LockType type) const { return (itsFileIO == 0 ? True : itsLocker.hasLock (type)); } inline int LockFile::lastError() const { return itsLocker.lastError(); } inline String LockFile::lastMessage() const { return itsLocker.lastMessage(); } inline const String& LockFile::name() const { return itsName; } inline const Block& LockFile::reqIds() const { return itsReqId; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/IO/MFFileIO.cc000066400000000000000000000102561476623553700164070ustar00rootroot00000000000000//# MFFileIO.cc: A single file in a MultiFileBase //# Copyright (C) 2014 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MFFileIO::MFFileIO (const std::shared_ptr& file, const String& name, ByteIO::OpenOption opt) : itsFile (file), itsPosition (0), itsName (name), itsIsWritable(True) { if (opt == ByteIO::New || opt == ByteIO::NewNoReplace) { itsId = itsFile->createFile (name, opt); } else { itsId = itsFile->openFile (name); itsIsWritable = (opt == ByteIO::Update); } } MFFileIO::~MFFileIO() { itsFile->closeFile (itsId); } void MFFileIO::remove() { itsFile->deleteFile (itsId); itsId = -1; } Int64 MFFileIO::read (Int64 size, void* buffer, Bool throwException) { Int64 n = itsFile->read (itsId, buffer, size, itsPosition); itsPosition += n; if (throwException && n < size) { throw AipsError ("MFFileIO::read - incorrect number of bytes (" + String::toString(n) + " out of " + String::toString(size) + ") read for logical file " + itsName + " in MultiFileBase " + itsFile->fileName()); } return n; } void MFFileIO::write (Int64 size, const void* buffer) { if (!itsIsWritable) { throw AipsError ("Logical file " + itsName + " is not writable " + "in MultiFileBase " + itsFile->fileName()); } Int64 n = itsFile->write (itsId, buffer, size, itsPosition); itsPosition += n; if (n != size) { throw AipsError ("MFFileIO: write error in logical file " + itsName + " in MultiFileBase " + itsFile->fileName()); } } void MFFileIO::reopenRW() { itsFile->reopenRW(); itsIsWritable = True; } void MFFileIO::flush() { itsFile->flushFile (itsId); } void MFFileIO::fsync() {} void MFFileIO::truncate (Int64 size) { itsFile->truncate (itsId, size); } String MFFileIO::fileName() const { return itsName; } Int64 MFFileIO::length() { return itsFile->fileSize (itsId); } Bool MFFileIO::isReadable() const { return True; } Bool MFFileIO::isWritable() const { return itsIsWritable && itsFile->isWritable(); } Bool MFFileIO::isSeekable() const { return True; } Int64 MFFileIO::doSeek (Int64 offset, ByteIO::SeekOption dir) { // Determine the new position. // Exit with error status if negative. Int64 newPos; switch (dir) { case ByteIO::Begin: newPos = offset; break; case ByteIO::End: newPos = length() + offset; break; default: newPos = itsPosition + offset; break; } if (newPos < 0) { throw (AipsError("MFFileIO::seek - cannot seek before start of file")); } itsPosition = newPos; return newPos; } const MultiFileInfo& MFFileIO::getInfo() const { return itsFile->info()[itsId]; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/IO/MFFileIO.h000066400000000000000000000120321476623553700162430ustar00rootroot00000000000000//# MFFileIO.h: Class for IO on a virtual file in a MultiFileBase //# Copyright (C) 2014 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MFFILEIO_H #define CASA_MFFILEIO_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //

        // Class for IO on a virtual file in a MultiFileBase // // // // // // This class is a specialization of class // ByteIO. It uses a // MultiFileBase as the data store. //

        // Similar to a regular file it is possible to read and write data and to // seek in the file. The object keeps track of the current file offset. // // // // // Create a new MultiFile using a block size of 1 MB. // shared_ptr mfile // (new MultiFile("file.mf', ByteIO::New, 1048576)); // // Create a virtual file in it. // MFFileIO mf1(mfile, "mf1", ByteIO::New); // // Use it (for example) as the sink of AipsIO. // AipsIO stream (&mf1); // // Write values. // stream << (Int)10; // stream << True; // // Seek to beginning of file and read data in. // stream.setpos (0); // Int vali; // Bool valb; // stream >> vali >> valb; // // class MFFileIO: public ByteIO { public: // Open or create a virtual file with the given name. Note that only the // basename of the file name is actually used. // It is created in the given MultiFileBase. MFFileIO (const std::shared_ptr&, const String& name, ByteIO::OpenOption = ByteIO::Old); // The destructor flushes and closes the file. ~MFFileIO() override; // Read size bytes from the byte stream. Returns the number of // bytes actually read, or a negative number if an error occurred. Will also // throw an Exception (AipsError) if the requested number of bytes could // not be read unless throwException is set to False. Int64 read (Int64 size, void* buf, Bool throwException=True) override; // Write a block at the current offset. void write (Int64 size, const void* buffer) override; // Reopen the file (and possibly underlying MultiFileBase) for read/write access. // Nothing will be done if the stream is writable already. // An exception will be thrown if it is not possible to reopen it for // read/write access. void reopenRW() override; // Remove the file from the MultiFileBase object. // It makes the object invalid by setting the fileId to -1. void remove(); // Flush the file by writing all dirty data and all header info. void flush() override; // Get the length of the file. Int64 length() override; // The file is always readable. Bool isReadable() const override; // Is the file writable? Bool isWritable() const override; // The file is always seekable. Bool isSeekable() const override; // Get the file name of the file attached. String fileName() const override; // Fsync the file (i.e. force the data to be physically written). void fsync() override; // Truncate the file to the given size. void truncate (Int64 size) override; // Reset the position pointer to the given value. It returns the // new position. Int64 doSeek (Int64 offset, ByteIO::SeekOption) override; // Get the MultiFileInfo object for this file. const MultiFileInfo& getInfo() const; private: //# Data members std::shared_ptr itsFile; Int64 itsPosition; String itsName; Int itsId; Bool itsIsWritable; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/IO/MMapIO.cc000066400000000000000000000032361476623553700161370ustar00rootroot00000000000000//# MMapIO.cc: Memory-mapped IO on a file //# //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include namespace casacore { MMapIO::MMapIO (const RegularFile& regularFile, ByteIO::OpenOption option) { int fdes = RegularFileIO::openCreate (regularFile, option); map (fdes, regularFile.path().originalName()); if (option == ByteIO::Append) { seek (0, ByteIO::End); } } MMapIO::~MMapIO() { unmapFile(); close (fd()); } } // end namespace casacore-3.7.1/casa/IO/MMapIO.h000066400000000000000000000055261476623553700160050ustar00rootroot00000000000000//# MMapIO.h: Memory-mapped IO on a file //# //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MMAPIO_H #define CASA_MMAPIO_H //# Includes #include #include #include namespace casacore { //

        // Memory-mapped IO on a file. // // // Memory-mapped IO lets the OS take care of caching file segments. // This is particularly useful for the Tiled Storage Manager which keeps a // cache of tiles. When using memory-mapped IO it does not need to do that // anymore. // // On 32-bit systems its use is limited because for large files the 4 GB // memory space is insufficient. However, for 64-bit systems the memory // space is large enough to make use of it. // // In the general case there is direct access to the mapped file space. // The read and write methods copies the data into/from a buffer. // However, to avoid the copying it is possible to get a direct pointer // to the mapped data. This should be used with care, because writing to // it will cause a segmentation if the file is readonly. If the file is // writable, writing into the mapped data segment means changing the file // contents. // class MMapIO: public MMapfdIO { public: // Open the given file and map it entirely into memory with read access. // The map has write access if the file is opened for write. explicit MMapIO (const RegularFile& regularFile, ByteIO::OpenOption = ByteIO::Old); // Destructor. // It will flush and unmap the file. ~MMapIO(); // Forbid copy constructor and assignment // MMapIO (const MMapIO&) = delete; MMapIO& operator= (const MMapIO&) = delete; // }; } // end namespace #endif casacore-3.7.1/casa/IO/MMapfdIO.cc000066400000000000000000000120671476623553700164530ustar00rootroot00000000000000//# MMapfdIO.cc: Memory-mapped IO on a file //# //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include namespace casacore { MMapfdIO::MMapfdIO() : itsFileSize (0), itsPosition (0), itsPtr (0), itsIsWritable (False) {} MMapfdIO::MMapfdIO (int fd, const String& fileName) : itsPtr (0) { map (fd, fileName); } void MMapfdIO::map (int fd, const String& fileName) { attach (fd, fileName); // Keep writable switch because it is used quite often. itsIsWritable = isWritable(); itsFileSize = length(); itsPosition = 0; if (itsFileSize > 0) { mapFile(); } } MMapfdIO::~MMapfdIO() { unmapFile(); } void MMapfdIO::mapFile() { // Unmap file if still mapped. if (itsPtr != 0) { unmapFile(); } int prot = PROT_READ; if (itsIsWritable) { prot = PROT_READ | PROT_WRITE; } // Do mmap of entire file. itsPtr = static_cast(::mmap (0, itsFileSize, prot, MAP_SHARED, fd(), 0)); if (itsPtr == MAP_FAILED) { throw AipsError ("MMapfdIO::MMapfdIO - mmap of " + fileName() + " failed: " + strerror(errno)); } // Optimize for sequential access. ::madvise (itsPtr, itsFileSize, MADV_SEQUENTIAL); } void MMapfdIO::unmapFile() { if (itsPtr != 0) { int res = ::munmap (itsPtr, itsFileSize); if (res != 0) { throw AipsError ("MMapfdIO::unmapFile - munmap of " + fileName() + " failed: " + strerror(errno)); } itsPtr = 0; } } void MMapfdIO::flush() { if (itsIsWritable && itsPtr != 0) { int res = ::msync (itsPtr, itsFileSize, MS_SYNC); if (res != 0) { throw AipsError ("MMapfdIO::flush - msync of " + fileName() + " failed: " + strerror(errno)); } } } void MMapfdIO::write (Int64 size, const void* buf) { if (!itsIsWritable) { throw AipsError ("MMapfdIO file " + fileName() + " is not writable"); } if (size > 0) { // If past end-of-file, write the last byte to extend the file. // Unmap and remap the file in that case. if (itsPosition + size > itsFileSize) { unmapFile(); itsFileSize = itsPosition + size; FiledesIO::doSeek (itsFileSize-1, ByteIO::Begin); char b=0; FiledesIO::write (1, &b); mapFile(); } memcpy (itsPtr+itsPosition, buf, size); itsPosition += size; } } Int64 MMapfdIO::read (Int64 size, void* buf, Bool throwException) { Int64 szrd = size; if (itsPosition >= itsFileSize) { szrd = 0; } else if (itsPosition+size > itsFileSize) { szrd = itsFileSize - itsPosition; } if (szrd > 0) { memcpy (buf, itsPtr+itsPosition, szrd); itsPosition += szrd; if (throwException && szrd < Int(size)) { throw AipsError ("MMapfdIO::read - " + fileName() + " incorrect number of bytes read"); } } return szrd; } Int64 MMapfdIO::doSeek (Int64 offset, ByteIO::SeekOption dir) { itsPosition = FiledesIO::doSeek (offset, dir); return itsPosition; } const void* MMapfdIO::getReadPointer (Int64 offset) const { if (offset >= itsFileSize) { throw AipsError ("MMapfdIO::getReadPointer: beyond EOF of " + fileName()); } return itsPtr+offset; } void* MMapfdIO::getWritePointer (Int64 offset) { if (!itsIsWritable) { throw AipsError ("MMapfdIO file " + fileName() + " is not writable"); } if (offset >= itsFileSize) { throw AipsError ("MMapfdIO::getWritePointer: beyond EOF of " + fileName()); } return itsPtr+offset; } } // end namespace casacore-3.7.1/casa/IO/MMapfdIO.h000066400000000000000000000123721476623553700163140ustar00rootroot00000000000000//# MMapfdIO.h: Memory-mapped IO on a file descriptor //# //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MMAPFDIO_H #define CASA_MMAPFDIO_H //# Includes #include #include #include namespace casacore { // // Memory-mapped IO on a file. // // // Memory-mapped IO lets the OS take care of caching file segments. // This is particularly useful for the Tiled Storage Manager which keeps a // cache of tiles. When using memory-mapped IO it does not need to do that // anymore. // // On 32-bit systems its use is limited because for large files the 4 GB // memory space is insufficient. However, for 64-bit systems the memory // space is large enough to make use of it. // // In the general case there is direct access to the mapped file space. // The read and write methods copies the data into/from a buffer. // However, to avoid the copying it is possible to get a direct pointer // to the mapped data. This should be used with care, because writing to // it will cause a segmentation if the file is readonly. If the file is // writable, writing into the mapped data segment means changing the file // contents. // class MMapfdIO: public FiledesIO { public: // Default constructor. // A file can be memory-mapped using the map function. MMapfdIO(); // Map the given file descriptor entirely into memory with read access. // The map has also write access if the file is opened for write. // The file name is only used in possible error messages. MMapfdIO (int fd, const String& fileName); // Destructor. // If needed, it will flush and unmap the file, but not close it. ~MMapfdIO(); // Forbid copy constructor and assignment // MMapfdIO (const MMapfdIO&) = delete; MMapfdIO& operator= (const MMapfdIO&) = delete; // // Map the given file descriptor entirely into memory with read access. // The map has also write access if the file is opened for write. // An exception is thrown if a file descriptor was already attached. // The file name is only used in possible error messages. void map (int fd, const String& fileName); // Map or remap the entire file. // Remapping is needed if the file has grown elsewhere. void mapFile(); // Flush changed mapped data to the file. // Nothing is done if the file is readonly. void flush(); // Write the number of bytes from the seek position on. // The file will be extended and remapped if writing beyond end-of-file. // In that case possible pointers obtained using getXXPointer // are not valid anymore. virtual void write (Int64 size, const void* buf); // Read size bytes from the File. Returns the number of bytes // actually read. Will throw an exception (AipsError) if the requested // number of bytes could not be read unless throwException is set to // False. Will always throw an exception if the file is not readable or // the system call returns an undocumented value. virtual Int64 read (Int64 size, void* buf, Bool throwException=True); // Get a read or write pointer to the given position in the mapped file. // An exception is thrown if beyond end-of-file or it not writable. // These functions should be used with care. If the pointer is used to // access data beyond the file size, a segmentation fault will occur. // So it means that the write pointer can only be used to update the file, // not to extend it. The seek and write functions // should be used to extend a file. // const void* getReadPointer (Int64 offset) const; void* getWritePointer (Int64 offset); // // Get the file size. Int64 getFileSize() const { return itsFileSize; } protected: // Reset the position pointer to the given value. It returns the // new position. virtual Int64 doSeek (Int64 offset, ByteIO::SeekOption); // Unmap the file. void unmapFile(); private: Int64 itsFileSize; //# File size Int64 itsPosition; //# Current seek position char* itsPtr; //# Pointer to memory map Bool itsIsWritable; }; } // end namespace #endif casacore-3.7.1/casa/IO/MemoryIO.cc000066400000000000000000000163611476623553700165600ustar00rootroot00000000000000//# MemoryIO.cc: Class for IO in memory //# Copyright (C) 1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Intgernet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include //# for memcpy with gcc-4.3 namespace casacore { //# NAMESPACE CASACORE - BEGIN MemoryIO::MemoryIO (uInt64 initialSize, uInt64 expandSize) : itsBuffer (0), itsAlloc (initialSize), itsExpandSize (expandSize), itsUsed (0), itsPosition (0), itsReadable (True), itsWritable (True), itsCanDelete (True) { if (itsAlloc > 0) { itsBuffer = new uChar[itsAlloc]; AlwaysAssert (itsBuffer != 0, AipsError); } } MemoryIO::MemoryIO (const void* buffer, uInt64 size) : itsBuffer ((uChar*)buffer), itsAlloc (size), itsExpandSize (0), itsUsed (size), itsPosition (0), itsReadable (True), itsWritable (False), itsCanDelete (False) {} MemoryIO::MemoryIO (void* buffer, uInt64 size, ByteIO::OpenOption option, uInt64 expandSize, Bool canDelete) : itsBuffer ((uChar*)buffer), itsAlloc (size), itsExpandSize (expandSize), itsUsed (size), itsPosition (0), itsReadable (True), itsWritable (True), itsCanDelete (canDelete) { // Make sure there is a buffer. if (itsAlloc > 0) { AlwaysAssert (itsBuffer != 0, AipsError); } // Adapt position, etc. from the option. switch (option) { case ByteIO::Old: itsWritable = False; break; case ByteIO::Append: itsPosition = itsUsed; break; default: itsUsed = 0; break; } } MemoryIO::~MemoryIO() { if (itsCanDelete) { delete [] itsBuffer; } } void MemoryIO::write (Int64 size, const void* buf) { // Throw an exception if not writable. if (!itsWritable) { throw (AipsError ("MemoryIO::write - MemoryIO object is not writable")); } // Expand the buffer when needed (and possible). Int64 minSize = itsPosition + size; if (minSize > itsAlloc) { if (! expand (minSize)) { throw (AipsError ("MemoryIO::write - buffer cannot be expanded")); } } // Copy the data and set new position and used. memcpy (itsBuffer + itsPosition, buf, size); itsPosition += size; if (itsPosition > itsUsed) { itsUsed = itsPosition; } } Int64 MemoryIO::read (Int64 size, void* buf, Bool throwException) { // Throw an exception if not readable. if (!itsReadable) { throw (AipsError ("MemoryIO::read - buffer is not readable")); } const Int64 bytesLeft = itsUsed - itsPosition; Int64 bytesRead = 0; if (size <= bytesLeft) { memcpy (buf, itsBuffer + itsPosition, size); itsPosition += size; bytesRead = size; } else { if (bytesLeft >= 0) { bytesRead = bytesLeft; memcpy (buf, itsBuffer + itsPosition, bytesRead); itsPosition += bytesLeft; if (throwException) { std::ostringstream oss; oss << "MemoryIO::read - incorrect number of bytes read: " << std::endl << " size=" << size << ", used=" << itsUsed << ", pos=" << itsPosition << ", left=" << bytesLeft; throw AipsError (oss.str()); } } else { std::ostringstream oss; oss << "MemoryIO::read - buffer position is invalid:" << std::endl << " size=" << size << ", used=" << itsUsed << ", pos=" << itsPosition << ", left=" << bytesLeft; throw AipsError (oss.str()); } } return bytesRead; } Int64 MemoryIO::doSeek (Int64 offset, ByteIO::SeekOption dir) { // Determine the new position. // Exit with error status if negative. Int64 newPos; switch (dir) { case ByteIO::Begin: newPos = offset; break; case ByteIO::End: newPos = itsUsed + offset; break; default: newPos = itsPosition + offset; break; } if (newPos < 0) { throw (AipsError("MemoryIO::seek - cannot seek before start of object")); } // It is possible to seek past the end of the buffer. // This means that the buffer usage increases. // Expand the buffer if needed. // Initialize the new buffer positions with zeroes. if (newPos > itsUsed) { // Throw an exception if not writable. if (!itsWritable) { throw (AipsError ("MemoryIO::seek - cannot seek past the end " "of a readonly object")); } if (newPos > itsAlloc) { if (! expand (newPos)) { throw (AipsError("MemoryIO::seek - buffer cannot be expanded")); } } for (; itsUsed itsAlloc) { throw(AipsError ("MemoryIO::setUsed - cannot use more than is allocated")); } itsUsed = bytesUsed; } uChar* MemoryIO::setBuffer (uInt64 length) { if (!itsWritable) { throw (AipsError ("MemoryIO::setBuffer - object is not writable")); } if (!expand (length)) { throw(AipsError ("MemoryIO::setBuffer - buffer cannot be expanded")); } itsUsed = length; return itsBuffer; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/IO/MemoryIO.h000066400000000000000000000242371476623553700164230ustar00rootroot00000000000000//# MemoryIO.h: Class for IO in memory //# Copyright (C) 1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MEMORYIO_H #define CASA_MEMORYIO_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Class for IO to a memory buffer. // // // // //
      • ByteIO // // // This class is doing IO in a buffer in memory. // It is part of the entire IO framework. It can for // instance be used to store data in canonical format in a // memory string and obtain it later. //

        // The memory buffer can be dynamic, so it will be expanded when needed. // This is done by allocating a larger buffer, copy the contents and // throw the old buffer away. //
        // The memory buffer can also be static to be sure that the pointer to // the buffer will not change. // The expand size determines if the memory buffer is static or dynamic. // An expand size zero indicates a static buffer. //

        // The memory buffer is seekable and readable. It depends on the // constructor whether it is writable. //

        // There are several ways in which the buffer can be created/passed: //

          //
        • Dynamic by passing an initial size and an expand size. // However, an expand size zero can be used to assure that no more // data is written than fits in the initial buffer (so once the // buffer is created it gets static). // In this way the buffer is readable and writable. //
        • Static by passing a const buffer and its length. // In this way the buffer is not writable. //
        • Dynamic or static by passing a non-const buffer, its length, // and an expand size (zero = static, >0 = dynamic) // . The OpenOption indicates whether the buffer will be writable. // Only for ByteIO::Old it will not be writable. // The OpenOption also determines the initial seek position. // Usually it is 0, but for ByteIO::Append it is the end of the buffer. //
        // The user can obtain a pointer to the buffer to extract the // stored data from it. The length of the data can also be obtained. //

        // Usually this class will be used in combination with, say, CanonicalIO // and AipsIO. // // // // Create dynamic (expandable) memory buffer of length 100. // // Use that as the sink of RawIO in AipsIO. // MemoryIO membuf (100); // RawIO rawio (&membuf); // AipsIO stream (&rawio); // // Write values. // stream << (Int)10; // stream << True; // // Seek to beginning of buffer and read data in. // stream.setpos (0); // Int vali; // Bool valb; // stream >> vali >> valb; // // // One can obtain the buffer and its length and use it later. // // (e.g. to write it in a non-AipsIO file). // uChar* bufptr = membuf.getBuffer(); // uInt64 length = membuf.length(); // // // It can also used to construct another MemoryIO object from it. // // The following memory buffer is static and readonly. // MemoryIO membuf2 (bufptr, length); // membuf2.read (sizeof(vali), vali); // membuf2.read (sizeof(valb), valb); // // // // Make it possible to do IO in a memory buffer. // The first implementation used strstreambuf from the iostream package. // However, that did not allow seeking and it was hard to get the length. // class MemoryIO: public ByteIO { public: // Construct a dynamic object with the given initial length. explicit MemoryIO (uInt64 initialSize=65536, uInt64 expandSize=32768); // Construct from a buffer with the given length. // The buffer is readonly and cannot be expanded. MemoryIO (const void* buffer, uInt64 size); // Construct from the given buffer with the given length. // The Byte::Option determines how the buffer will be used. // The seek pointer is set to the beginning of the buffer, unless // told otherwise below. //

        //
        New, NewNoReplace and Scratch //
        The buffer is empty and is read/write. //
        Old //
        The buffer contains size bytes and is readonly. //
        Update, Delete //
        The buffer contains size bytes and is read/write. //
        Append //
        The buffer contains size bytes and is read/write. // The seek pointer is set to the end of the buffer. //
        // When the buffer is writable, it will be expanded if needed. // This means that buffer does not point to the data // anymore. However, when expandSize==0, the buffer // cannot be expanded and the pointer is always valid. //
        When canDelete is True, buffer expansion means that the // old buffer gets deleted. MemoryIO (void* buffer, uInt64 size, ByteIO::OpenOption, uInt64 expandSize=0, Bool canDelete=False); // Delete the Memory object. // The data buffer is not deleted when constructed with the // constructor taking a buffer pointer. ~MemoryIO(); // Write the number of bytes. // When needed it expands the buffer. // An exception is thrown when the buffer is not writable or // when buffer expansion fails or is not possible. virtual void write (Int64 size, const void* buf); // Read size bytes from the memory buffer. Returns the number of // bytes actually read. Will throw an Exception (AipsError) if the // requested number of bytes could not be read unless throwException is set // to False. Will always throw an exception if the buffer is not readable // or the buffer pointer is at an invalid position. virtual Int64 read (Int64 size, void* buf, Bool throwException=True); // Clear the buffer; i.e. set the data length and seek pointer to zero. void clear(); // Get the buffer containing the data. //
        The length of the data in the buffer can be obtained using the // length() function. const uChar* getBuffer() const; // Get the length of the data in the buffer. virtual Int64 length(); // Get the allocated length of the buffer. uInt64 allocated() const; // Get the expand size (0 = not expandable). uInt64 expandSize() const; // Is the IO stream readable? virtual Bool isReadable() const; // Is the IO stream writable? virtual Bool isWritable() const; // Is the IO stream seekable? virtual Bool isSeekable() const; // resize the internal buffer (if necessary) so that it is big enough // to hold the specified number of bytes. Returns a non-const pointer // to the buffer that can be used to write up to the specified number // of bytes into the buffer. If less data is written into the buffer // then the setUsed member funtion should be used to indicate how much // of the buffer is valid. Throws an exception if the MemoryIO object // is not writable or if it needs to increase the size of the internal // buffer and the MemoryIO object is not expandable. // You should not use the supplied pointer to write // more than length data points to the buffer uChar* setBuffer(uInt64 length); // tell the MemoryIO object how much of its internal buffer is valid // data. You only need to use this function if you are directly writing to // the buffer using the pointer returned by the non-const getBuffer // function. This function throws an exception if the number of bytes used // is greater than the number allocated or if the MemoryIO object is not // writeable. void setUsed(uInt64 bytesUsed); private: //# Copy constructor, should not be used. MemoryIO (const MemoryIO& that); //# Assignment, should not be used. MemoryIO& operator= (const MemoryIO& that); // Reset the position pointer to the given value. It returns the // new position. // An exception is thrown when seeking before the start of the // buffer or when seeking past the end of a readonly buffer. // When seeking past the end of a writable buffer, the required // amount of bytes is added and initialized to zero. virtual Int64 doSeek (Int64 offset, ByteIO::SeekOption); //# Expand the buffer to at least the given size. The specified size //# will be used if it results in a larger buffer size. In this way the //# buffer does not get reallocated too often. It returns a false status //# when the buffer cannot be expanded. Bool expand (uInt64 minSize); uChar* itsBuffer; Int64 itsAlloc; Int64 itsExpandSize; Int64 itsUsed; Int64 itsPosition; Bool itsReadable; Bool itsWritable; Bool itsCanDelete; }; inline void MemoryIO::clear() { itsUsed = itsPosition = 0; } inline const uChar* MemoryIO::getBuffer() const { return itsBuffer; } inline uInt64 MemoryIO::allocated() const { return itsAlloc; } inline uInt64 MemoryIO::expandSize() const { return itsExpandSize; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/IO/MultiFile.cc000066400000000000000000000665011476623553700167530ustar00rootroot00000000000000//# MultiFile.cc: Class to combine multiple files in a single one //# Copyright (C) 2014 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // This function creates the CRC lookup table. // It is executed thread-safe on the first time called. static const std::array& CRCTable() { static std::array result = [] () -> std::array { std::array res; const uInt polynom = 0x4c11db7; const uInt highbit = (uInt)1<<(32-1); // make CRC lookup table used by table algorithms for (int i=0; i<256; i++) { uInt crc = i; crc<<= 32-8; for (int j=0; j<8; j++) { uInt bit = crc & highbit; crc<<= 1; if (bit) crc^= polynom; } res[i]= crc; } return res; }(); return result; } /* MultiFile keeps a map of blocks in each individual file to the blocks in the overall file. This map is kept in the MultiFile header. If too large, the remainder is written at the end of the file. A possible problem with this scheme is that when the MultiFile gets extended, the new blocks overwrite the remainder of the map if the blocks occupied by the remainder are not counted. This leads to file corruption in case the program or system crashes. In order to write the remainder in a robust way, the header keeps two lists of continuation blocks which are used alternately. The first header block (written at offset 0) tells which continuation block is used. It is written after the continuation blocks, so there is always a valid header. In case the header shrinks, it is possible less continuation blocks are needed. The superfluous blocks are kept and will be used when the header grows again. If not growing, they are 'lost' which is no problem as there are far less header blocks than data blocks. A potential problem is that if data blocks get removed at the end, the file cannot be truncated because a continuation block is stored after those data blocks. In the future it might be wise to move such a block upward. However, in practice this will hardly ever occur. Note that the header size implicitly tells how many continuation blocks are actually used. Another small problem is that new continuation blocks are put at the end of the file and are not taken from the free list to avoid that the free list needs to be serialized again. Again, in practice this is hardly a problem because data blocks will be removed very seldomly. */ MultiFile::MultiFile (const String& name, ByteIO::OpenOption option, Int blockSize, Bool useODirect, Bool useCRC) : MultiFileBase (name, blockSize, useODirect), itsNrContUsed {0,0}, itsHdrContInx (0), // Start using the first continuation block itsUseCRC (useCRC) { itsIO.reset (new FileUnbufferedIO (name, option, useODirect)); init (option); } MultiFile::MultiFile (const String& name, const std::shared_ptr& parent, ByteIO::OpenOption option, Int blockSize) // Use parent's block size if not specified. // A child MultiFile does not use CRC. : MultiFileBase (name, blockSize>0 ? blockSize:parent->blockSize(), False), itsNrContUsed {0,0}, itsHdrContInx (0), // Start using the first continuation block itsUseCRC (False) { itsIO.reset (new MFFileIO (parent, name, option)); init (option); } std::shared_ptr MultiFile::makeNested (const std::shared_ptr& parent, const String& name, ByteIO::OpenOption option, Int blockSize) const { return std::make_shared(name, parent, option, blockSize); } void MultiFile::init (ByteIO::OpenOption option) { if (option == ByteIO::New || option == ByteIO::NewNoReplace) { // New file; first block is for administration. setNewFile(); itsNrBlock = 1; } else { readHeader(); } itsWritable = itsIO->isWritable(); } MultiFile::~MultiFile() { close(); } void MultiFile::doFlushFile() { itsIO->flush(); } void MultiFile::close() { // Flush. flush(); // Clear all file info. itsInfo.clear(); // Delete the file object. itsIO.reset (0); } void MultiFile::reopenRW() { if (isWritable()) { return; } itsIO->reopenRW(); itsWritable = True; } void MultiFile::fsync() { itsIO->fsync(); } void MultiFile::writeHeader() { // Write all header info in canonical format into a memory buffer. // This buffer will be written into the file. // If too large, the remainder is written into continuation blocks. // There are 2 sets of continuation blocks to avoid that the header // gets corrupted in case of a crash while writing the header. auto mio = std::make_shared(itsBlockSize, itsBlockSize); auto cio = std::make_shared(mio); AipsIO aio(cio); itsHdrCounter++; Int64 zero64 = 0; uInt zero32 = 0; char char8[8] = {0,0,0,0,0,0,0,0}; Int version = 2; // Start with a zero to distinguish it from version 1. // The first value in version 1 is always > 0. cio->write (1, &zero64); cio->write (1, &zero64); // room for first cont.block (writeRemainder) cio->write (1, &itsHdrCounter); cio->write (1, &version); cio->write (1, &zero32); // headerCRC cio->write (1, &zero64); // header size cio->write (1, &itsBlockSize); cio->write (1, &itsNrBlock); if (itsUseCRC) char8[0] = 1; cio->write (8, char8); AlwaysAssert (mio->length() == 64, AipsError); // First write general info and file names, etc. aio.putstart ("MultiFile", version); aio << itsInfo; aio.putend(); // Write the used and free blocknrs in packed format but outside // the AipsIO object because that is limited to 4 GB. for (const MultiFileInfo& fileInfo : info()) { writeVector (*cio, packIndex(fileInfo.blockNrs)); } writeVector (*cio, packIndex(freeBlocks())); writeVector (*cio, itsCRC); // Calculate the size including the continuation blocknrs and number of // actually used blocknrs. // If continuation is needed, they might change and cannot // be written yet. Int64 todo = mio->length(); DebugAssert (static_cast(todo) <= mio->allocated(), AipsError); Int64 totalSize = todo + 2*sizeof(uInt) + (2 + itsHdrCont[0].blockNrs.size() + itsHdrCont[1].blockNrs.size()) * sizeof(Int64); // Allocate a temp buffer if header too large or if O_DIRECT. // This buffer is used by writeRemainder and Bool hasRemainder = (totalSize > itsBlockSize); MultiFileBuffer mfbuf(itsUseODirect || hasRemainder ? itsBlockSize:0, itsUseODirect); // First write the remainder and adjust the header as needed. if (hasRemainder) { writeRemainder (*mio, *cio, mfbuf); } else { // No remainder, so write the continuation blocknrs here. // None are used in the current set. writeVector (*cio, itsHdrCont[0].blockNrs); writeVector (*cio, itsHdrCont[1].blockNrs); itsNrContUsed[itsHdrContInx] = 0; cio->write (2, itsNrContUsed); } // Writing the first part of the header is done as the last step. uChar* buf = const_cast(mio->getBuffer()); todo = mio->length(); CanonicalConversion::fromLocal (buf+32, todo); // header size if (itsUseCRC) { uInt crc = calcCRC (buf, todo); crc = calcCRC (buf, todo); CanonicalConversion::fromLocal (buf+28, crc); } writeHeaderTest(); // hook for tMultiFileLarge.cc if (itsUseODirect) { char* iobuf = mfbuf.data(); memcpy (iobuf, buf, itsBlockSize); itsIO->pwrite (itsBlockSize, 0, iobuf); } else { itsIO->pwrite (itsBlockSize, 0, buf); } } void MultiFile::writeRemainder (MemoryIO& mio, CanonicalIO& cio, MultiFileBuffer& mfbuf) { char* iobuf = mfbuf.data(); // Get the size of the remainder. Int64 todo = mio.length() - itsBlockSize; // Use the other continuation set. int newContInx = 1 - itsHdrContInx; // Determine the nr of continuation blocks to be used. // The first 8 bytes per block are used to point to the next one. // Furthermore, the old and new cont-block vector have to be counted // as well (length and all entries). // So it can happen that an extra block is needed which in its turn // might require an extra block, etc. // To avoid that the free list is changed and needs to be rewritten, // new continuation blocks are created by adding a new block. Int64 contBlkSize = itsBlockSize - sizeof(Int64); Int64 ncontOther = itsHdrCont[itsHdrContInx].blockNrs.size(); Int64 ncontOld = itsHdrCont[newContInx].blockNrs.size(); Int64 ncont = (todo + contBlkSize - 1) / contBlkSize; Int64 ncontNew = std::max(ncont, ncontOld); while (True) { Int64 szCont = (todo + 2*sizeof(uInt) + (ncontOther + ncontNew + 2) * sizeof(Int64)); ncont = (szCont + contBlkSize - 1) / contBlkSize; if (ncont <= ncontNew) { break; } ncontNew = ncont; } // Extend the virtual file. if (ncontNew > ncontOld) { extendVF (itsHdrCont[newContInx], ncontNew, False); } // Write the continuation blocknrs. writeVector (cio, itsHdrCont[0].blockNrs); writeVector (cio, itsHdrCont[1].blockNrs); itsNrContUsed[newContInx] = ncontNew; cio.write (2, itsNrContUsed); // Write the continuation blocks. const std::vector& hdrContNrs = itsHdrCont[newContInx].blockNrs; const uChar* remHdr = mio.getBuffer() + itsBlockSize; todo = mio.length() - itsBlockSize; writeHeaderShow (ncont, todo); for (Int64 i=0; ipwrite (itsBlockSize, hdrContNrs[i]*itsBlockSize, iobuf); } // Store first continuation blocknr in header. uChar* buf = const_cast(mio.getBuffer()); CanonicalConversion::fromLocal (buf+8, hdrContNrs[0]); // Swap continuation sets. itsHdrContInx = newContInx; // Store possibly changed nr of blocks. CanonicalConversion::fromLocal (buf+48, itsNrBlock); } void MultiFile::writeVector (CanonicalIO& cio, const std::vector& index) { // Write in the same way as AipsIO is doing. Int64 sz = index.size(); cio.write (1, &sz); if (sz > 0) { cio.write (index.size(), index.data()); } } void MultiFile::writeVector (CanonicalIO& cio, const std::vector& index) { // Write in the same way as AipsIO is doing. Int64 sz = index.size(); cio.write (1, &sz); if (sz > 0) { cio.write (index.size(), index.data()); } } void MultiFile::readVector (CanonicalIO& cio, std::vector& index) { Int64 sz; cio.read (1, &sz); if (sz > 0) { index.resize (sz); cio.read (sz, index.data()); } else { index.clear(); } } void MultiFile::readVector (CanonicalIO& cio, std::vector& index) { Int64 sz; cio.read (1, &sz); if (sz > 0) { index.resize (sz); cio.read (sz, index.data()); } else { index.clear(); } } void MultiFile::readHeader (Bool always) { /* header layout is described in Casacore note 260, but repeated here. version 1 Int64 header size Int64 blockSize Int64 hdrCounter AipsIO 'MultiFile' version 1 Int64 nr of blocks used nfile*fileinfo String file name Int64[n] index (MultiFile block containing file block i) Int64 file size (bytes) Note that .hdrext is used if header does not fit in first block version 2 Int64 0 Int64 first block of header continuation (<0=none) Int64 hdrCounter Int32 version uInt32 headerCRC Int64 header size Int64 blockSize char useCRC char[7] spare AipsIO 'MultiFile' with same version as above Int64 nr of blocks used nfile*fileinfo String file name Int64 file size (bytes) Bool nested MultiFile? nfile*index Int64 size of packed index Int64[size] packed index (MultiFile block of file block i) index packed index of free blocks Int32[nblock] CRC value of each block (only if useCRC=1) Int64[ncont0] block numbers of header continuation buffer 0 Int64[ncont1] block numbers of header continuation buffer 1 Note that 'first block of header' tells if cont.block 0 or 1 is used by comparing it with the first entry. When writing index: - Determine nr of blocks needed. If > 1 perform next steps: - Re-determine nr of header blocks needed - Take the other cont.block and extend that as needed take new blocks from EOF (otherwise free list changes) - Make CRC of entire header (with 0 in headerCRC) - First write cont.blocks and finally first block (reset cont.blocknr) */ // Read the first 24 bytes (3x Int64) of the header. std::vector buf(3*sizeof(Int64)); itsIO->pread (buf.size(), 0, buf.data()); // First get the header change count. Int64 hdrCounter; CanonicalConversion::toLocal (hdrCounter, &(buf[16])); // Only if needed, read the rest of the header. if (always || hdrCounter != itsHdrCounter) { itsHdrCounter = hdrCounter; // In version 1 the headerSize is in the first 8 bytes. // For version 2 and higher it is 0. Int64 headerSize; CanonicalConversion::toLocal (headerSize, buf.data()); if (headerSize == 0) { readHeaderVersion2 (buf); } else { readHeaderVersion1 (headerSize, buf); } // Initialize remaining info fields. for (MultiFileInfo& info : itsInfo) { info.curBlock = -1; info.dirty = False; } } } void MultiFile::readHeaderVersion1 (Int64 headerSize, std::vector& buf) { Int64 leadSize = buf.size(); AlwaysAssert (leadSize==24, AipsError); CanonicalConversion::toLocal (itsBlockSize, &(buf[8])); buf.resize (headerSize); if (headerSize <= itsBlockSize) { // Only one header block; read only the part that is needed. itsIO->pread (headerSize - leadSize, leadSize, &(buf[leadSize])); } else { // Read the first header block and the remainder. itsIO->pread (itsBlockSize - leadSize, leadSize, &(buf[leadSize])); // For version 1 the remaining header info is in the .hdrext file. FileUnbufferedIO iohdr(itsName + "_hdrext", ByteIO::Old); iohdr.read (headerSize - itsBlockSize, &(buf[itsBlockSize])); } // Read all header info from the memory buffer. auto mio = std::make_shared(&(buf[leadSize]), headerSize - leadSize); auto cio = std::make_shared(mio); AipsIO aio(cio); Int version = aio.getstart ("MultiFile"); AlwaysAssert (version==1, AipsError); aio >> itsNrBlock; getInfoVersion1 (aio, itsInfo); aio >> itsFreeBlocks; aio.getend(); } void MultiFile::readHeaderVersion2 (std::vector& buf) { Int64 leadSize = buf.size(); AlwaysAssert (leadSize==24, AipsError); Int64 contBlockNr = 0; Int64 headerSize; uInt headerCRC = 0; Int version; // For version 2 and higher the next 40 bytes have to be read. buf.resize (leadSize + 40); itsIO->pread (40, leadSize, &(buf[leadSize])); leadSize += 40; CanonicalConversion::toLocal (contBlockNr, &(buf[8])); CanonicalConversion::toLocal (version, &(buf[24])); // This version of MultiFile can only handle version 2. // Future versions might use higher version numbers. if (version != 2) { throw AipsError("This version of Casacore supports up to MultiFile " "version 2, not version " + String::toString(version)); } CanonicalConversion::toLocal (headerCRC, &(buf[28])); CanonicalConversion::toLocal (headerSize, &(buf[32])); CanonicalConversion::toLocal (itsBlockSize, &(buf[40])); CanonicalConversion::toLocal (itsNrBlock, &(buf[48])); char tmpc; CanonicalConversion::toLocal (tmpc, &(buf[56])); itsUseCRC = (tmpc!=0); buf.resize (headerSize); if (headerSize <= itsBlockSize) { // Only one header block; read only the part that is needed. itsIO->pread (headerSize - leadSize, leadSize, &(buf[leadSize])); } else { // Read the first header block and the remainder. itsIO->pread (itsBlockSize - leadSize, leadSize, &(buf[leadSize])); readRemainder (headerSize, contBlockNr, buf); } // Check the CRC if needed. if (itsUseCRC) { // Set headerCRC in buffer to 0 to calculate the correct CRC. uInt zero32 = 0; CanonicalConversion::fromLocal (&(buf[28]), zero32); uInt crc = calcCRC (buf.data(), headerSize); if (crc != headerCRC) { throw AipsError("MultiFile header CRC mismatch in " + fileName()); } } // Read all header info from the memory buffer. auto mio = std::make_shared(&(buf[leadSize]), headerSize - leadSize); auto cio = std::make_shared(mio); AipsIO aio(cio); Int vers = aio.getstart ("MultiFile"); AlwaysAssert (vers==version, AipsError); aio >> itsInfo; aio.getend(); getInfoVersion2 (contBlockNr, *cio); } void MultiFile::getInfoVersion2 (Int64 contBlockNr, CanonicalIO& cio) { std::vector bl; for (MultiFileInfo& fileInfo: itsInfo) { readVector (cio, bl); fileInfo.blockNrs = unpackIndex(bl); } readVector (cio, bl); itsFreeBlocks = unpackIndex(bl); readVector (cio, itsCRC); if (! itsUseCRC) { AlwaysAssert (itsCRC.size() == 0, AipsError); } // Read the header continuation info. // Determine which of them is in use. readVector (cio, itsHdrCont[0].blockNrs); readVector (cio, itsHdrCont[1].blockNrs); cio.read (2, itsNrContUsed); itsHdrContInx = 0; if (! itsHdrCont[1].blockNrs.empty() && contBlockNr == itsHdrCont[1].blockNrs[0]) { itsHdrContInx = 1; } } void MultiFile::readRemainder (Int64 headerSize, Int64 blockNr, std::vector& buf) { MultiFileBuffer mfbuf(itsBlockSize, itsUseODirect); char* iobuf = mfbuf.data(); size_t off = itsBlockSize; Int64 blknr = blockNr; Int64 todo = headerSize - itsBlockSize; Int64 contBlkSize = itsBlockSize - sizeof(Int64); while (off < buf.size()) { AlwaysAssert (blknr!=0, AipsError); itsIO->pread (itsBlockSize, blknr*itsBlockSize, iobuf); memcpy (&(buf[off]), iobuf + sizeof(Int64), std::min(todo,contBlkSize)); off += contBlkSize; todo -= contBlkSize; CanonicalConversion::toLocal (blknr, iobuf); } // No continuation block should be given at the end. AlwaysAssert (blknr==0, AipsError); } void MultiFile::doOpenFile (MultiFileInfo&) {} void MultiFile::doCloseFile (MultiFileInfo&) {} void MultiFile::doAddFile (MultiFileInfo&) {} void MultiFile::doDeleteFile (MultiFileInfo& info) { // Add all file blocks to the free list. doTruncateFile (info, 0); } void MultiFile::doTruncateFile (MultiFileInfo& info, uInt64 nrblk) { if (nrblk < info.blockNrs.size()) { // Add the blocknrs to the free list. // Later we can merge them in order and leave out blocks past last block used. itsFreeBlocks.reserve (itsFreeBlocks.size() + info.blockNrs.size() - nrblk); for (size_t i=nrblk; i 0) { itsFreeBlocks.erase (itsFreeBlocks.begin(), itsFreeBlocks.begin() + i); itsNrBlock -= i; itsIO->truncate (itsNrBlock * itsBlockSize); if (itsUseCRC) { itsCRC.resize (itsNrBlock); } } } void MultiFile::extend (MultiFileInfo& info, Int64 lastblk) { extendVF (info, lastblk, True); } void MultiFile::extendVF (MultiFileInfo& info, Int64 lastblk, Bool useFreeBlocks) { Int64 curnrb = info.blockNrs.size(); info.blockNrs.resize (lastblk); Int64 nfree = itsFreeBlocks.size(); for (Int64 i=curnrb; ipread (itsBlockSize, info.blockNrs[blknr] * itsBlockSize, buffer); if (itsUseCRC) { checkCRC (buffer, info.blockNrs[blknr]); } } void MultiFile::writeBlock (MultiFileInfo& info, Int64 blknr, const void* buffer) { itsIO->pwrite (itsBlockSize, info.blockNrs[blknr] * itsBlockSize, buffer); if (itsUseCRC) { storeCRC (buffer, info.blockNrs[blknr]); } } void MultiFile::storeCRC (const void* buffer, Int64 blknr) { if (blknr >= Int64(itsCRC.size())) { itsCRC.resize (blknr+1); } itsCRC[blknr] = calcCRC (buffer, itsBlockSize); } void MultiFile::checkCRC (const void* buffer, Int64 blknr) const { AlwaysAssert (blknr < Int64(itsCRC.size()), AipsError); uInt crc = calcCRC (buffer, itsBlockSize); if (crc != itsCRC[blknr]) { throw AipsError ("Mismatch in CRC of MultiFile block " + String::toString(blknr)); } } uInt MultiFile::calcCRC (const void* buffer, Int64 size) const { // The algorithm is taken from crctester.c by Sven Reifegerste // (www.zorc/reflex) // See also https://www.lammertbies.nl/comm/info/crc-calculation const uChar* buf = static_cast(buffer); const uInt crcinit = 0x46af6449; //# The above value is calculated from: //# const uInt highbit = (uInt)1<<(32-1); //# crcinit = 0xffffffff; //# for (i=0; i<32; i++) { //# bit = crcinit & 1; //# if (bit) crcinit^= polynom; //# crcinit >>= 1; //# if (bit) crcinit|= highbit; //# } // Normal lookup table algorithm with augmented zero bytes. uInt crc = crcinit; for (Int64 i=0; i> (32-8)) & 0xff]; } // Augment zero bytes. for (size_t i=0; i<32/8; ++i) { crc = (crc << 8) ^ CRCTable()[ (crc >> (32-8)) & 0xff]; } crc^= 0xffffffff; return crc; } void MultiFile::show (std::ostream& os) const { os << fileName() << ": blocksize=" << blockSize() << " nfile=" << nfile() << " nblock=" << nblock() << " nCRC=" << itsCRC.size() << endl << " ncont=" << itsNrContUsed[0] << ',' << itsNrContUsed[1] << " cont=" << itsHdrContInx << ' ' << itsHdrCont[itsHdrContInx].blockNrs << " free=" << freeBlocks() << endl; } std::vector MultiFile::packIndex (const std::vector& blockNrs) { // See if subsequent block numbers are used, so the index can be // compressed. // Compression is done by counting the nr of subsequent blocknrs // and writing the negative count if > 0. // Note that the count does not include the first block number. std::vector buf; if (blockNrs.empty()) { return std::vector(); } buf.reserve (blockNrs.size()); buf.push_back (blockNrs[0]); Int64 next = blockNrs[0] + 1; for (size_t j=1; j 0) { buf.push_back (-nr); } next = blockNrs[j]; buf.push_back (next); } next++; } Int64 nr = next - buf.back() - 1; if (nr > 0) { buf.push_back (-nr); } return buf; } std::vector MultiFile::unpackIndex (const std::vector& blockNrs) { // Expand if subsequent block numbers are used indicated by a // negative value. std::vector buf; buf.reserve (blockNrs.size()); for (auto bl : blockNrs) { if (bl < 0) { Int64 next = buf.back() + 1; size_t nr = -bl; for (size_t i=0; i #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations. class ByteIO; class CanonicalIO; class MemoryIO; // // Class to combine multiple files in a single one. // // // // // // This class (derived from MultiFileBase) is a container file holding // multiple virtual files in a regular file. // It is primarily meant as a container file for the storage manager files // of a table to reduce the number of files used (especially for Lustre) and // to reduce the number of open files (especially when concatenating tables). //
        MultiFile has the following properties: //
          //
        • It can choose an IO buffer size that matches the file system well // (e.g., to support a large buffer size on ZFS or Lustre). //
        • O_DIRECT (if supported by the OS) can be used to tell the OS kernel // to bypass its file cache. It does not speed up the I/O, but it makes // I/O behaviour more predictable which a real-time system might need. //
        • Often the data to be read from MultiFile will not exactly match the // block size and offset. MultiFile will buffer the data and copy the // part that is needed (similar to stdio). However, when matching // block size and offset are used, data will directly be read into the // user's buffer to achieve zero-copy behaviour. //
        • It is possible to nest MultiFile's. Thus a MultiFile can be a file // in a parent MultiFile. In this way it is easily possible to store // a main table and its subtables (such as an MS) in a single file. //
        • Optionally each block is stored with a 32-bit CRC to check if the // data in a block are correctly read. The CRC values are stored as // part of the header, thus not in each individual block. This is done // to make the zero-copy behaviour possible (as described above). //
        • The header and the index are stored in the first block. If too large, // continuation blocks are used. There are two sets of continuation // blocks between which is alternated. This is done for robustness // purposes; there is always a valid one in case of a crash in the // middle of writing the continuation blocks. Note that the first // header block is written after the continuation blocks, so it always // points to a valid set of continuation blocks. //
        // // The SetupNewTable constructor has a StorageOption argument to define // if a MultiFile has to be used and if so, the buffer size to use. // It is also possible to specify that through aipsrc variables. // // A virtual file is spread over multiple (fixed size) data blocks in the // MultiFile. A data block is never shared by multiple files. // For each virtual file MultiFile keeps a MultiFileInfo object telling // the file size and the block numbers used for the file. When flushing // the MultiFile, this meta info is written into the header block. If it // does not fit in the header block, the rest is written in continuation blocks. // On open and resync, it is read back. There are two sets of continuation // blocks which are alternately used when the header is written. This is done // to have a valid header in case of a crash in the middle of writing the header. // // A virtual file is represented by an MFFileIO object, which is derived // from ByteIO and as such part of the casacore IO framework. It makes it // possible for applications to access a virtual file in the same way as // a regular file. // // It is possible to delete a virtual file. Its blocks will be added to // the free block list (which is also stored in the meta info). // The MultiFile is truncated when blocks are deleted at the end of the file. //
        // // In principle it is possible to use the MultiFile functions directly. // However, in general it is much easier to use an MFFileIO object // per virtual file as shown below. // // // Create a new MultiFile using a block size of 1 MB. // std::shared_ptr mfile // (new MultiFile("file.mf", ByteIO::New, 1048576)); // // Create a virtual file in it. // MFFileIO mf1(mfile, "mf1", ByteIO::New); // // Use it (for example) as the sink of AipsIO. // AipsIO stream (&mf1); // // Write values. // stream << (Int)10; // stream << True; // // Seek to beginning of file and read data in. // stream.setpos (0); // Int vali; // Bool valb; // stream >> vali >> valb; // // // //
      • MultiFile can be optimized how cont.blocks are used. In case of // file truncation, it could check if only cont.blocks are present // after the blocks to be removed. In such a case they can be moved // backwards. Also the nr of cont.blocks can shrink. In such a case // the unused blocks are not added to the free list. Only the nr of // actually used cont.blocks is decremented. They could be added to // the free list later. // The reason for above is that the free list is written into the // header blocks before the required nr of continuation blocks is known. //
      • Keep a journal file telling which files are created and which // blocks are allocated for a virtual file. // class MultiFile: public MultiFileBase { public: // Open or create a MultiFile with the given name. // Upon creation the block size can be given. If 0, it uses the block size // of the file system the file is on. //
        If useODirect=True, the O_DIRECT flag is used (if supported). // It tells the kernel to bypass its file cache to have more predictable // I/O behaviour. //
        If useCRC=True, 32-bit CRC values are calculated and stored for // each data block. Note that useCRC is only used for new files. explicit MultiFile (const String& name, ByteIO::OpenOption, Int blockSize=0, Bool useODirect=False, Bool useCRC=False); // Open or create a MultiFile with the given name which is nested in the // given parent. Thus data are read/written in the parent file. // Upon creation the block size can be given. If 0, it uses the block size // of the parent. explicit MultiFile (const String& name, const std::shared_ptr& parent, ByteIO::OpenOption, Int blockSize=0); // The destructor flushes and closes the file. ~MultiFile() override; // Copy constructor and assignment not possible. MultiFile (const MultiFile&) = delete; MultiFile& operator= (const MultiFile&) = delete; // Make a nested MultiFile. std::shared_ptr makeNested (const std::shared_ptr& parent, const String& name, ByteIO::OpenOption, Int blockSize) const override; // Reopen the underlying file for read/write access. // Nothing will be done if the file is writable already. // Otherwise it will be reopened and an exception will be thrown // if it is not possible to reopen it for read/write access. void reopenRW() override; // Fsync the file (i.e., force the data to be physically written). void fsync() override; // Show some info. void show (std::ostream&) const; // Compress a block index by looking for subsequent block numbers. static std::vector packIndex (const std::vector& blockNrs); // Decompress a block index by inserting subsequent block numbers. static std::vector unpackIndex (const std::vector& blockNrs); private: // Initialize the MultiFile object. void init (ByteIO::OpenOption option); // Read the file info for the new version 2. void getInfoVersion2 (Int64 contBlockNr, CanonicalIO& aio); // Write a vector of Int64. void writeVector (CanonicalIO& cio, const std::vector& index); void writeVector (CanonicalIO& cio, const std::vector& index); // Read a vector of Int64. void readVector (CanonicalIO& cio, std::vector& index); void readVector (CanonicalIO& cio, std::vector& index); // Write the remainder of the header (in case exceeding 1 block). // iobuf should be large enough void writeRemainder (MemoryIO& mio, CanonicalIO&, MultiFileBuffer& mfbuf); // Read the remainder of the header into the buffer. void readRemainder (Int64 headerSize, Int64 blockNr, std::vector& buf); // Truncate the file if blocks are freed at the end. void truncateIfNeeded(); // Header writing hooks (meant for derived test classes). virtual void writeHeaderShow (Int64 ncont, Int64 todo) const; virtual void writeHeaderTest(); // // Do the class-specific actions on opening a file. void doOpenFile (MultiFileInfo&) override; // Do the class-specific actions on closing a file. void doCloseFile (MultiFileInfo&) override; // Do the class-specific actions on adding a file. void doAddFile (MultiFileInfo&) override; // Do the class-specific actions on deleting a file. void doDeleteFile (MultiFileInfo&) override; // Truncate the file to nrblk blocks. void doTruncateFile (MultiFileInfo& info, uInt64 nrblk) override; // Flush the file itself. void doFlushFile() override; // Flush and close the file. void close() override; // Write the header info. void writeHeader() override; // Read the header info. If always==False, the info is only read if the // header counter has changed. void readHeader (Bool always=True) override; // Extend the virtual file to fit lastblk. void extend (MultiFileInfo& info, Int64 lastblk) override; protected: // Store the CRC of a data block in the index. void storeCRC (const void* buffer, Int64 blknr); // Check the CRC of a data block read. void checkCRC (const void* buffer, Int64 blknr) const; // Calculate the CRC of a data block. uInt calcCRC (const void* buffer, Int64 size) const; // Extend the virtual file to fit lastblk. // Optionally the free blocks are not used. virtual void extendVF (MultiFileInfo& info, Int64 lastblk, Bool useFreeBlocks); // Write a data block. void writeBlock (MultiFileInfo& info, Int64 blknr, const void* buffer) override; // Read a data block. void readBlock (MultiFileInfo& info, Int64 blknr, void* buffer) override; // Read the version 1 header. void readHeaderVersion1 (Int64 headerSize, std::vector& buf); // Read the version 2 and higher header. void readHeaderVersion2 (std::vector& buf); //# Data members // Define two continuation sets where the header overflow can be stored MultiFileInfo itsHdrCont[2]; uInt itsNrContUsed[2]; // nr of cont.blocks actually used uInt itsHdrContInx; // Continuation set last used (0 or 1) Bool itsUseCRC; std::vector itsCRC; // CRC value per block (empty if useCRC=False) std::unique_ptr itsIO; // A regular file or nested MFFileIO }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/IO/MultiFileBase.cc000066400000000000000000000346041476623553700175450ustar00rootroot00000000000000//# MultiFileBase.cc: Class to combine multiple files in a single one //# Copyright (C) 2014 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include // for fileSTAT #include // needed for stat or stat64 #include #include // for posix_memalign //# The alignment needed for O_DIRECT. #define mfb_od_align (size_t(4096)) namespace casacore { //# NAMESPACE CASACORE - BEGIN ostream& operator<< (ostream& ios, const MultiFileInfo& info) { ios << info.name << ' ' << info.blockNrs << endl << " fsize=" << info.fsize << " cur=" << info.curBlock << " nest=" << info.nested << " dirty=" << info.dirty << endl; return ios; } AipsIO& operator<< (AipsIO& ios, const MultiFileInfo& info) { ios << info.name << info.fsize << info.nested; return ios; } AipsIO& operator>> (AipsIO& ios, MultiFileInfo& info) { ios >> info.name >> info.fsize >> info.nested; return ios; } void getInfoVersion1 (AipsIO& ios, std::vector& info) { // Get the old MultiFileInfo not containing the 'nested' field. // The following code is a copy of STLIO.tcc. ios.getstart ("Block"); uInt nr; ios >> nr; info.resize(nr); for (uInt i=0; i> info[i].name >> info[i].blockNrs >> info[i].fsize; } ios.getend(); } MultiFileBase::MultiFileBase (const String& name, Int blockSize, Bool useODirect) : itsBlockSize (blockSize), itsNrBlock (0), itsHdrCounter (0), itsUseODirect (useODirect), itsWritable (False), // usually reset by derived class itsChanged (False) { // Unset itsUseODirect if the OS does not support it. #ifndef HAVE_O_DIRECT itsUseODirect = False; #endif itsName = Path(name).expandedName(); } std::shared_ptr MultiFileBase::openMF (const String& fileName) { if (HDF5File::isHDF5 (fileName)) { return std::make_shared(fileName, ByteIO::Old); } return std::make_shared(fileName, ByteIO::Old); } void MultiFileBase::setNewFile() { // The container file is new. itsChanged = True; // Use file system block size, but not less than given size. if (itsBlockSize <= 0) { struct fileSTAT sfs; fileSTAT (itsName.c_str(), &sfs); Int64 blksz = sfs.st_blksize; itsBlockSize = std::max (-itsBlockSize, blksz); } AlwaysAssert (itsBlockSize > 0, AipsError); } MultiFileBase::~MultiFileBase() { // Note: do not call flush() here, otherwise the virtual function // doFlushFile in the already destructed derived class is called // giving the error 'pure virtual function called'. itsInfo.clear(); } Int MultiFileBase::openFile (const String& name) { Int id = fileId (name, True); if (itsInfo[id].buffer) { throw AipsError ("MFFileIO: logical file " + name + " already opened in " + itsName); } itsInfo[id].allocBuffer (itsBlockSize, itsUseODirect); doOpenFile (itsInfo[id]); return id; } Int MultiFileBase::createFile (const String& name, ByteIO::OpenOption opt) { Int id = fileId (name, False); if (id >= 0) { if (opt == ByteIO::NewNoReplace) { throw AipsError ("MFFileIO: logical file " + name + " already exists in " + itsName); } deleteFile (id); } id = addFile(name); itsInfo[id].allocBuffer (itsBlockSize, itsUseODirect); return id; } uInt MultiFileBase::nfile() const { Int nf = 0; for (const MultiFileInfo& info : itsInfo) { if (! info.name.empty()) { nf++; } } return nf; } void MultiFileBase::flush() { // Flush all buffers if needed. for (MultiFileInfo& info : itsInfo) { if (info.dirty) { writeDirty (info); } } // Header only needs to be written if blocks were added since last flush. // If it does not need to be written, no further flush is needed. if (itsChanged) { writeHeader(); itsChanged = False; } doFlushFile(); } void MultiFileBase::flushFile (Int fileId) { if (fileId >= Int(itsInfo.size()) || itsInfo[fileId].name.empty()) { throw AipsError ("MultiFileBase::write - invalid fileId given"); } if (itsInfo[fileId].dirty) { writeDirty (itsInfo[fileId]); } } void MultiFileBase::closeFile (Int fileId) { // Flush the file (as needed) and delete the buffer. flushFile (fileId); itsInfo[fileId].buffer.reset(); itsInfo[fileId].curBlock = -1; doCloseFile (itsInfo[fileId]); } Int64 MultiFileBase::read (Int fileId, void* buf, Int64 size, Int64 offset) { if (fileId >= Int(itsInfo.size()) || itsInfo[fileId].name.empty()) { throw AipsError ("MultiFileBase::read - invalid fileId given"); } char* buffer = static_cast(buf); MultiFileInfo& info = itsInfo[fileId]; char* infoBuffer = info.buffer->data(); // Determine the logical block to read and the start offset in that block. Int64 nrblk = (info.fsize + itsBlockSize - 1) / itsBlockSize; Int64 blknr = offset/itsBlockSize; Int64 start = offset - blknr*itsBlockSize; Int64 done = 0; Int64 szdo = std::min(size, info.fsize - offset); // not past EOF // Read until done. while (done < szdo) { AlwaysAssert (blknr < nrblk, AipsError); Int64 todo = std::min(szdo-done, itsBlockSize-start); // If already in buffer, copy from there. if (blknr == info.curBlock) { memcpy (buffer, infoBuffer+start, todo); } else { // Read directly into buffer if it fits exactly and // no O_DIRECT or buffer aligned properly. if (todo == itsBlockSize && (!itsUseODirect || ((uintptr_t)buffer & (uintptr_t)(mfb_od_align - 1)) == 0)) { readBlock (info, blknr, buffer); } else { if (info.dirty) { writeDirty (info); } // Read into file buffer and copy correct part. readBlock (info, blknr, infoBuffer); info.curBlock = blknr; memcpy (buffer, infoBuffer+start, todo); } } // Increment counters. done += todo; buffer += todo; blknr++; start = 0; } return done; } Int64 MultiFileBase::write (Int fileId, const void* buf, Int64 size, Int64 offset) { if (fileId >= Int(itsInfo.size()) || itsInfo[fileId].name.empty()) { throw AipsError ("MultiFileBase::write - invalid fileId given"); } const char* buffer = static_cast(buf); AlwaysAssert (itsWritable, AipsError); MultiFileInfo& info = itsInfo[fileId]; char* infoBuffer = info.buffer->data(); // Determine the logical block to write and the start offset in that block. Int64 blknr = offset/itsBlockSize; Int64 start = offset - blknr*itsBlockSize; Int64 done = 0; // If beyond EOF, add blocks as needed. Int64 lastblk = blknr + (start+size+itsBlockSize-1) / itsBlockSize; Int64 curnrb = (info.fsize+itsBlockSize-1) / itsBlockSize; if (lastblk >= curnrb) { extend (info, lastblk); itsChanged = True; } // Write until all done. while (done < size) { Int64 todo = std::min(size-done, itsBlockSize-start); // Favor sequential writing, thus write current buffer first. if (blknr == info.curBlock) { memcpy (infoBuffer+start, buffer, todo); info.dirty = True; if (done+todo > size) { writeDirty (info); } // Write directly from buffer if it fits exactly and // no O_DIRECT or buffer aligned properly. } else if (todo == itsBlockSize && (!itsUseODirect || ((uintptr_t)buffer & (uintptr_t)(mfb_od_align - 1)) == 0)) { writeBlock (info, blknr, buffer); } else { // Write into temporary buffer and copy correct part. // First write possibly dirty buffer. if (info.dirty) { writeDirty (info); } if (blknr >= curnrb) { memset (infoBuffer, 0, itsBlockSize); } else { readBlock (info, blknr, infoBuffer); } info.curBlock = blknr; memcpy (infoBuffer+start, buffer, todo); info.dirty = True; } done += todo; buffer += todo; blknr++; start = 0; } if (offset+size > info.fsize) { info.fsize = offset+size; } return done; } void MultiFileBase::truncate (Int fileId, Int64 size) { if (fileId >= Int(itsInfo.size()) || itsInfo[fileId].name.empty()) { throw AipsError ("MultiFileBase::truncate - invalid fileId given"); } AlwaysAssert (itsWritable, AipsError); MultiFileInfo& info = itsInfo[fileId]; AlwaysAssert (size >= 0 && size <= info.fsize, AipsError); // Determine nr of remaining blocks. size_t nrblk = (size + itsBlockSize - 1) / itsBlockSize; if (nrblk < info.blockNrs.size()) { // Clear current block if it is one of the blocks to be freed. for (size_t i=nrblk; i::iterator iter=itsInfo.begin(); iter!=itsInfo.end(); ++iter, ++i) { if (iter->name.empty()) { inx = i; // free file slot } else if (bname == iter->name) { throw AipsError ("MultiFileBase::addFile - logical file name " + bname + " already exists in " + itsName); } } // Add a new file entry if needed. if (inx == itsInfo.size()) { itsInfo.resize (inx+1); } itsInfo[inx].name = bname; doAddFile (itsInfo[inx]); itsChanged = True; return inx; } Int MultiFileBase::fileId (const String& fname, Bool throwExcp) const { // Only use the basename part (to avoid directory rename problems). String bname = Path(fname).baseName(); for (size_t i=0; i= Int(itsInfo.size()) || itsInfo[fileId].name.empty()) { throw AipsError ("MultiFileBase::deleteFile - invalid fileId given"); } MultiFileInfo& info = itsInfo[fileId]; info.dirty = False; // no need to write when deleting closeFile (fileId); doDeleteFile (info); // Clear this slot. info = MultiFileInfo(); itsChanged = True; } Int64 MultiFileBase::fileSize (Int fileId) const { if (fileId >= Int(itsInfo.size()) || itsInfo[fileId].name.empty()) { throw AipsError ("MultiFileBase::fileSize - invalid fileId given"); } return itsInfo[fileId].fsize; } MultiFileInfo::MultiFileInfo() : curBlock (-1), fsize (0), nested (False), dirty (False) {} MultiFileBuffer::MultiFileBuffer (size_t bufSize, Bool useODirect) : itsData (0) { const size_t align = 4096; if (bufSize > 0) { if (useODirect && bufSize%align != 0) { throw AipsError("MultiFile bufsize " + String::toString(bufSize) + " must be a multiple of " + String::toString(mfb_od_align) + " when using O_DIRECT"); } // Note that the error messages do a malloc as well, but small // compared to the requested malloc, so they'll probably succeed. void* ptr; if (useODirect) { if (posix_memalign (&ptr, mfb_od_align, bufSize) != 0) { throw AllocError("MultiFileBuffer: failed to allocate aligned buffer", bufSize); } } else { ptr = malloc (bufSize); if (!ptr) { throw AllocError("MultiFileBuffer: failed to allocate buffer", bufSize); } } itsData = static_cast(ptr); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/IO/MultiFileBase.h000066400000000000000000000274011476623553700174040ustar00rootroot00000000000000//# MultiFileBase.h: Abstract base class to combine multiple files in a single one //# Copyright (C) 2014 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MULTIFILEBASE_H #define CASA_MULTIFILEBASE_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declaration. class AipsIO; class HDF5Group; class HDF5DataSet; // // Helper class for MultiFileInfo holding a data buffer // // // The buffer can be allocated with posix_memalign (for O_DIRECT support). // Hence the memory must be freed using free, which makes it impossible // to use a shared_ptr to that memory. Hence it is encapsulated in this class. // class MultiFileBuffer { public: MultiFileBuffer (size_t bufSize, Bool useODirect); ~MultiFileBuffer() { if (itsData) free(itsData); } // Forbid copy constructor. MultiFileBuffer (const MultiFileBuffer&) = delete; // Forbid assignment. MultiFileBuffer& operator= (const MultiFileBuffer&) = delete; char* data() { return itsData; } private: // Data members char* itsData; }; // // Helper class for MultiFileBase containing info per logical file. // // // This struct defines the basic fields describing a logical file in a // class derived from MultiFileBase (such as MultiFile or MultiHDF5). // // struct MultiFileInfo { // Initialize the object. The buffer is created when the file is opened. explicit MultiFileInfo(); // Allocate the buffer. void allocBuffer (size_t bufSize, Bool useODirect) { buffer = std::make_shared(bufSize, useODirect); } //# Data members. std::vector blockNrs; // physical blocknrs for this logical file Int64 curBlock; // the data block held in buffer (<0 is none) Int64 fsize; // file size (in bytes) String name; // the virtual file name Bool nested; // is the file a nested MultiFile? Bool dirty; // has data in buffer been changed? std::shared_ptr buffer; // buffer holding a data block std::shared_ptr group; std::shared_ptr dataSet; }; ostream& operator<< (ostream&, const MultiFileInfo&); AipsIO& operator<< (AipsIO&, const MultiFileInfo&); AipsIO& operator>> (AipsIO&, MultiFileInfo&); void getInfoVersion1 (AipsIO&, std::vector&); // // Abstract base class to combine multiple logical files in a single one. // // // // // // This class is the abstract base class for classes defining a container // file holding multiple logical files. These classes are meant as a // container files for the storage manager files of a table to reduce the // number of files used (especially for Lustre) and to reduce the number // of open files (especially when concatenating tables). // The derived classes MultiFile and MultiHDF5 implement such container // files using a regular file and HDF5, resp. // // MultiFileBase implements several functions with common functionality // for the derived classes. // // A logical file is represented by an MFFileIO object, which is derived // from ByteIO and as such part of the casacore IO framework. It makes it // possible for applications to access a logical file in the same way as // a regular file. // class MultiFileBase { public: // Create a MultiFileBase object with the given name. //
        Upon creation of the container file the block size can be given. // If <=0, it uses the block size of the file system the file is on, // but it will not be less than the absolute value of the given block size. //
        If useODIrect=True, it means that O_DIRECT is used. If the OS does not // support it (as determined at configure time), the flag will always be // set to False. If True, the data buffers will have a proper alignment // and size (as needed by O_DIRECT). MultiFileBase (const String& name, Int blockSize, Bool useODirect); // The destructor flushes dirty blocks and closes the container file. virtual ~MultiFileBase(); // Forbid copy constructor. MultiFileBase (const MultiFileBase&) = delete; // Forbid assignment. MultiFileBase& operator= (const MultiFileBase&) = delete; // Open the correct MultiFileBase (as plain or HDF5). static std::shared_ptr openMF (const String& fileName); // Make the correct MultiFileBase object for a nested file. virtual std::shared_ptr makeNested (const std::shared_ptr& parent, const String& name, ByteIO::OpenOption, Int blockSize) const = 0; // Get the file name of the MultiFileBase container file. String fileName() const { return itsName; } // Is the container file writable? Bool isWritable() const { return itsWritable; } // Open the given logical file and return its file id. // If the name is unknown, an exception is thrown. // It allocates the internal buffer of the logical file. Int openFile (const String& name); // Create a new logical file and return its file id. // Only the base name of the given file name is used. In this way the // MultiFileBase container file can be moved. // If the logical file already exists, it is deleted if ByteIO::New is // given. Otherwise an exception is thrown. // It allocates the internal buffer of the logical file. Int createFile (const String& name, ByteIO::OpenOption = ByteIO::New); // Flush the possible dirty buffer of the given logical file. void flushFile (Int fileId); // Close a logical file. // It flushes and deallocates its buffer. void closeFile (Int fileId); // Delete a logical file. It adds its blocks to the free block list. void deleteFile (Int fileId); // Get the size of a logical file. Int64 fileSize (Int fileId) const; // Read a block at the given offset in the logical file. // It returns the actual size read. Int64 read (Int fileId, void* buffer, Int64 size, Int64 offset); // Write a block at the given offset in the logical file. // It returns the actual size written. Int64 write (Int fileId, const void* buffer, Int64 size, Int64 offset); // Truncate the logical file to the given size. void truncate (Int fileId, Int64 size); // Reopen the underlying file for read/write access. // Nothing will be done if the file is writable already. // Otherwise it will be reopened and an exception will be thrown // if it is not possible to reopen it for read/write access. virtual void reopenRW() = 0; // Flush the file by writing all dirty data and all header info. void flush(); // Get the block size used. Int64 blockSize() const { return itsBlockSize; } // Get the nr of logical files. uInt nfile() const; // Get the total nr of data blocks used. Int64 nblock() const { return itsNrBlock; } // Get the info object (for test purposes mainly). const std::vector& info() const { return itsInfo; } // Get the free blocks (for test purposes mainly). const std::vector& freeBlocks() const { return itsFreeBlocks; } // Return the file id of a file in the MultiFileBase object. // If the name is unknown, an exception is thrown if throwExcp is set. // Otherwise it returns -1. Int fileId (const String& name, Bool throwExcp=True) const; // Is O_DIRECT used? Bool useODirect() const { return itsUseODirect; } protected: // Resync with another process by clearing the buffers and rereading // the header. The header is only read if its counter has changed. void resync(); // Fsync the file (i.e., force the data to be physically written). virtual void fsync() = 0; private: // Write the dirty block and clear dirty flag. void writeDirty (MultiFileInfo& info) { writeBlock (info, info.curBlock, info.buffer->data()); info.dirty = False; } // Add a file to the MultiFileBase object. It returns the file id. // Only the base name of the given file name is used. In this way the // MultiFileBase container file can be moved. Int addFile (const String& name); // Do the class-specific actions on opening a logical file. virtual void doOpenFile (MultiFileInfo&) = 0; // Do the class-specific actions on closing a logical file. virtual void doCloseFile (MultiFileInfo&) = 0; // Do the class-specific actions on adding a logical file. virtual void doAddFile (MultiFileInfo&) = 0; // Do the class-specific actions on deleting a logical file. virtual void doDeleteFile (MultiFileInfo&) = 0; // Truncate the container file to nrblk blocks. virtual void doTruncateFile (MultiFileInfo& info, uInt64 nrblk) = 0; // Flush the container file. virtual void doFlushFile() = 0; // Flush and close the container file. virtual void close() = 0; // Write the header info. virtual void writeHeader() = 0; // Read the header info. If always==False, the info is only read if the // header counter has changed. virtual void readHeader (Bool always=True) = 0; // Extend a logical file to fit lastblk. virtual void extend (MultiFileInfo& info, Int64 lastblk) = 0; // Write a data block of a logical file into the container file. virtual void writeBlock (MultiFileInfo& info, Int64 blknr, const void* buffer) = 0; // Read a data block of a logical file from the container file. virtual void readBlock (MultiFileInfo& info, Int64 blknr, void* buffer) = 0; protected: // Set the flags and blockSize for a new MultiFile/HDF5. void setNewFile(); //# Data members String itsName; Int64 itsBlockSize; // The blocksize used Int64 itsNrBlock; // The total nr of blocks actually used Int64 itsHdrCounter; // Counter of header changes std::vector itsInfo; std::shared_ptr itsBuffer; Bool itsUseODirect; // use O_DIRECT? Bool itsWritable; // Is the file writable? Bool itsChanged; // Has header info changed since last flush? std::vector itsFreeBlocks; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/IO/MultiHDF5.cc000066400000000000000000000152261476623553700165600ustar00rootroot00000000000000//# MultiHDF5.cc: Class to combine multiple files in a single HDF5 file //# Copyright (C) 2015 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MultiHDF5::MultiHDF5 (const String& name, ByteIO::OpenOption option, Int blockSize) : MultiFileBase (name, blockSize, False), //# no O_DIRECT in HDF5 itsFile (new HDF5File(itsName, option)), itsHDF5 (itsFile.get()) { init (option); } MultiHDF5::MultiHDF5 (const String& name, const std::shared_ptr& parent, ByteIO::OpenOption option, Int blockSize) // Use parent's block size if not specified. : MultiFileBase (name, blockSize>0 ? blockSize:parent->blockSize(), False) { // Get the overall HDF5 file object. MultiHDF5* parentHDF5 = dynamic_cast(parent.get()); if (! parentHDF5) { throw AipsError("No MultiHDF5 parent given to nested MultiHDF5 constructor"); } itsFile = parentHDF5->getHDF5File(); // Create or open the file. // Note that creation (in doAddFile) also creates a dataset which is // not used, but it does not harm. MFFileIO file(parent, name, option); itsGroup = file.getInfo().group; // make sure HDF5Group object is kept itsHDF5 = itsGroup.get(); init (option); } std::shared_ptr MultiHDF5::makeNested (const std::shared_ptr& parent, const String& name, ByteIO::OpenOption option, Int blockSize) const { return std::make_shared(name, parent, option, blockSize); } void MultiHDF5::init (ByteIO::OpenOption option) { if (option == ByteIO::New || option == ByteIO::NewNoReplace) { setNewFile(); } else { readHeader(); } itsWritable = itsFile->isWritable(); } MultiHDF5::~MultiHDF5() { close(); } void MultiHDF5::doOpenFile (MultiFileInfo& info) { DebugAssert (! info.group, AipsError); info.group.reset (new HDF5Group (*itsHDF5, info.name, true, false)); info.dataSet.reset (new HDF5DataSet (*info.group, "FileData", (const uChar*)0)); } void MultiHDF5::doCloseFile (MultiFileInfo& info) { DebugAssert (info.group->isValid(), AipsError); info.dataSet.reset(); info.group.reset(); } void MultiHDF5::doFlushFile() { itsFile->flush(); } void MultiHDF5::close() { flush(); // Close all datasets and groups. itsInfo.clear(); itsHDF5->close(); } void MultiHDF5::reopenRW() { // Close all datasets and groups. itsInfo.clear(); itsFile->reopenRW(); readHeader (True); itsWritable = True; } void MultiHDF5::fsync() {} void MultiHDF5::writeHeader() { Record rec; itsHdrCounter++; rec.define ("blockSize", itsBlockSize); rec.define ("hdrCounter", itsHdrCounter); Vector names(itsInfo.size()); Vector sizes(itsInfo.size()); for (uInt i=0; i names (rec.asArrayString("names")); Vector sizes(rec.asArrayInt64("sizes")); // Set info fields. itsInfo.reserve (names.size()); for (uInt i=0; iextend (IPosition(2, itsBlockSize, lastblk+1)); itsNrBlock = lastblk+1; } void MultiHDF5::readBlock (MultiFileInfo& info, Int64 blknr, void* buffer) { Slicer slicer(IPosition(2, 0, blknr), IPosition(2, itsBlockSize, 1)); info.dataSet->get (slicer, buffer); } void MultiHDF5::writeBlock (MultiFileInfo& info, Int64 blknr, const void* buffer) { Slicer slicer(IPosition(2, 0, blknr), IPosition(2, itsBlockSize, 1)); info.dataSet->put (slicer, buffer); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/IO/MultiHDF5.h000066400000000000000000000171661476623553700164270ustar00rootroot00000000000000 //# MultiHDF5.h: Class to combine multiple files in a single HDF5 file //# Copyright (C) 2015 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MULTIHDF5_H #define CASA_MULTIHDF5_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Class to combine multiple files in a single HDF5 file. // // // // // // This class is a container file holding multiple virtual files. It is // primarily meant as a container file for the storage manager files of a // table to reduce the number of files used (especially for Lustre) and to // reduce the number of open files (especially when concatenating tables). //
        A secondary goal is offering the ability to use an IO buffer size // that matches the file system well (large buffer size for e.g. ZFS). // // The SetupNewTable constructor has a StorageOption argument to define // if a MultiFile has to be used and if so, the buffer size to use. // It is also possible to specify that through aipsrc variables. // // A virtual file is spread over multiple (fixed size) data blocks in the // MultiFile. A data block is never shared by multiple files. // For each virtual file MultiFile keeps a MultiFileInfo object telling // the file size and the blocks numbers used for the file. When flushing // the MultiFile, this meta info is written into a header block and, // if needed, continuation blocks. On open and resync, it is read back. //
        // // A virtual file is represented by an MFFileIO object, which is derived // from ByteIO and as such part of the casacore IO framework. It makes it // possible for applications to access a virtual file in the same way as // a regular file. // // It is possible to delete a virtual file. Its blocks will be added to // the free block list (which is also stored in the meta info). //
        // // In principle it is possible to use the MultiFile functions directly. // However, in general it is much easier to use an MFFileIO object // per virtual file as shown below. // // // Create a new MultiFile using a block size of 1 MB. // MultiFile mfile("file.mf', ByteIO::New, 1048576); // // Create a virtual file in it. // MFFileIO mf1(mfile, "mf1", ByteIO::New); // // Use it (for example) as the sink of AipsIO. // AipsIO stream (&mf1); // // Write values. // stream << (Int)10; // stream << True; // // Seek to beginning of file and read data in. // stream.setpos (0); // Int vali; // Bool valb; // stream >> vali >> valb; // // class MultiHDF5 : public MultiFileBase { public: // Open or create a MultiHDF5 with the given name. // Upon creation the block size can be given. If 0, it uses the block size // of the file system the file is on. explicit MultiHDF5 (const String& name, ByteIO::OpenOption, Int blockSize=0); // Open or create a MultiHDF5 which is nested in the given parent. // The data are read/written in a group with the given name in the parent. // Upon creation the block size can be given. If 0, it uses the block size // of the parent. explicit MultiHDF5 (const String& name, const std::shared_ptr& parent, ByteIO::OpenOption, Int blockSize=0); // The destructor flushes and closes the file. ~MultiHDF5() override; // Copy constructor and assignment not possible. MultiHDF5 (const MultiHDF5&) = delete; MultiHDF5& operator= (const MultiHDF5&) = delete; // Make the correct MultiFileBase object for a nested file. // It creates a new group under which the virtual files are created. std::shared_ptr makeNested (const std::shared_ptr& parent, const String& name, ByteIO::OpenOption, Int blockSize) const override; // Open the given logical file and return its file id. // If the name is unknown, an exception is thrown. // It creates the HDF5 group and dataset opbject. // Reopen the underlying file for read/write access. // Nothing will be done if the file is writable already. // Otherwise it will be reopened and an exception will be thrown // if it is not possible to reopen it for read/write access. void reopenRW() override; // Fsync the file (i.e., force the data to be physically written). void fsync() override; // Get the HDF5File object. const std::shared_ptr& getHDF5File() const { return itsFile; } const HDF5Object& getHDF5Object() const { return *itsHDF5; } private: // Initialize the MultiFile object. void init (ByteIO::OpenOption option); // Do the class-specific actions on opening a file. void doOpenFile (MultiFileInfo&) override; // Do the class-specific actions on closing a file. void doCloseFile (MultiFileInfo&) override; // Do the class-specific actions on adding a file. void doAddFile (MultiFileInfo&) override; // Do the class-specific actions on deleting a file. void doDeleteFile (MultiFileInfo&) override; // Truncate the file to nrblk blocks (does nothing). void doTruncateFile (MultiFileInfo& info, uInt64 nrblk) override; // Flush the file itself. void doFlushFile() override; // Flush and close the file. void close() override; // Write the header info. void writeHeader() override; // Read the header info. If always==False, the info is only read if the // header counter has changed. void readHeader (Bool always=True) override; // Extend the virtual file to fit lastblk. void extend (MultiFileInfo& info, Int64 lastblk) override; // Read a data block. void readBlock (MultiFileInfo& info, Int64 blknr, void* buffer) override; // Write a data block. void writeBlock (MultiFileInfo& info, Int64 blknr, const void* buffer) override; //# Data members std::shared_ptr itsFile; std::shared_ptr itsGroup; // This points to the HDF5Group if present, otherwise to the HDF5File. // It tells where the HDF5 data are accessed. HDF5Object* itsHDF5; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/IO/RawIO.cc000066400000000000000000000126331476623553700160370ustar00rootroot00000000000000//# RawIO.cc: Class for IO in local format //# Copyright (C) 1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN RawIO::RawIO (const std::shared_ptr& byteIO) : TypeIO (byteIO) {} RawIO::RawIO (const RawIO& that) : TypeIO (that) {} RawIO& RawIO::operator= (const RawIO& that) { if (this != &that) { TypeIO::operator= (that); } return *this; } RawIO::~RawIO() {} size_t RawIO::write (size_t nvalues, const Bool* value) { return TypeIO::write (nvalues, value); } size_t RawIO::write (size_t nvalues, const Char* value) { itsByteIO->write (nvalues * sizeof(Char), (void*) value); return nvalues * sizeof(Char); } size_t RawIO::write (size_t nvalues, const uChar* value) { itsByteIO->write (nvalues * sizeof(uChar), (void*) value); return nvalues * sizeof(uChar); } size_t RawIO::write (size_t nvalues, const Short* value) { itsByteIO->write (nvalues * sizeof(Short), (void*) value); return nvalues * sizeof(Short); } size_t RawIO::write (size_t nvalues, const uShort* value) { itsByteIO->write (nvalues * sizeof(uShort), (void*) value); return nvalues * sizeof(uShort); } size_t RawIO::write (size_t nvalues, const Int* value) { itsByteIO->write (nvalues * sizeof(Int), (void*) value); return nvalues * sizeof(Int); } size_t RawIO::write (size_t nvalues, const uInt* value) { itsByteIO->write (nvalues * sizeof(uInt), (void*) value); return nvalues * sizeof(uInt); } size_t RawIO::write (size_t nvalues, const Int64* value) { itsByteIO->write (nvalues * sizeof(Int64), (void*) value); return nvalues * sizeof(Int64); } size_t RawIO::write (size_t nvalues, const uInt64* value) { itsByteIO->write (nvalues * sizeof(uInt64), (void*) value); return nvalues * sizeof(uInt64); } size_t RawIO::write (size_t nvalues, const Float* value) { itsByteIO->write (nvalues * sizeof(Float), (void*) value); return nvalues * sizeof(Float); } size_t RawIO::write (size_t nvalues, const Double* value) { itsByteIO->write (nvalues * sizeof(Double), (void*) value); return nvalues * sizeof(Double); } size_t RawIO::write (size_t nvalues, const Complex* value) { return TypeIO::write (nvalues, value); } size_t RawIO::write (size_t nvalues, const DComplex* value) { return TypeIO::write (nvalues, value); } size_t RawIO::write (size_t nvalues, const String* value) { return TypeIO::write (nvalues, value); } size_t RawIO::read (size_t nvalues, Bool* value) { return TypeIO::read (nvalues, value); } size_t RawIO::read (size_t nvalues, Char* value) { itsByteIO->read (nvalues * sizeof(Char), value); return nvalues * sizeof(Char); } size_t RawIO::read (size_t nvalues, uChar* value) { itsByteIO->read (nvalues * sizeof(uChar), value); return nvalues * sizeof(uChar); } size_t RawIO::read (size_t nvalues, Short* value) { itsByteIO->read (nvalues * sizeof(Short), value); return nvalues * sizeof(Short); } size_t RawIO::read (size_t nvalues, uShort* value) { itsByteIO->read (nvalues * sizeof(uShort), value); return nvalues * sizeof(uShort); } size_t RawIO::read (size_t nvalues, Int* value) { itsByteIO->read (nvalues * sizeof(Int), value); return nvalues * sizeof(Int); } size_t RawIO::read (size_t nvalues, uInt* value) { itsByteIO->read (nvalues * sizeof(uInt), value); return nvalues * sizeof(uInt); } size_t RawIO::read (size_t nvalues, Int64* value) { itsByteIO->read (nvalues * sizeof(Int64), value); return nvalues * sizeof(Int64); } size_t RawIO::read (size_t nvalues, uInt64* value) { itsByteIO->read (nvalues * sizeof(uInt64), value); return nvalues * sizeof(uInt64); } size_t RawIO::read (size_t nvalues, Float* value) { itsByteIO->read (nvalues * sizeof(Float), value); return nvalues * sizeof(Float); } size_t RawIO::read (size_t nvalues, Double* value) { itsByteIO->read (nvalues * sizeof(Double), value); return nvalues * sizeof(Double); } size_t RawIO::read (size_t nvalues, Complex* value) { return TypeIO::read (nvalues, value); } size_t RawIO::read (size_t nvalues, DComplex* value) { return TypeIO::read (nvalues, value); } size_t RawIO::read (size_t nvalues, String* value) { return TypeIO::read (nvalues, value); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/IO/RawIO.h000066400000000000000000000116211476623553700156750ustar00rootroot00000000000000//# RawIO.h: Class for IO in local format //# Copyright (C) 1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_RAWIO_H #define CASA_RAWIO_H #include #include //# The following should be a forward declaration. But our Complex & DComplex //# classes are a typedef hence this does not work. Replace the following with //# forward declarations when Complex and DComplex are no longer typedefs. #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class ByteIO; class String; // Class for IO in local format. // // // // //
      • ByteIO class and derived classes //
      • TypeIOclass // // // RawIO is a specialization of class TypeIO to store // data in local format. //

        // This class is intended for data that will only be used internally // and will not be exported to machines with a possible different // data format. //

        // To save storage Bools will be written as bits (using the // static functions in class Conversion. // // // Storing data in local format can improve performance on // little-endian machines like DEC-alpha and PC's. // class RawIO: public TypeIO { public: // Constructor. The read/write functions will use the given ByteIO object // as the data store. explicit RawIO (const std::shared_ptr& byteIO); // The copy constructor uses reference semantics RawIO (const RawIO& rawIO); // The assignment operator uses reference semantics RawIO& operator= (const RawIO& rawIO); // Destructor. ~RawIO(); // Write the values to the ByteIO object. // Bool, complex and String values are handled by the base class. // virtual size_t write (size_t nvalues, const Bool* value); virtual size_t write (size_t nvalues, const Char* data); virtual size_t write (size_t nvalues, const uChar* data); virtual size_t write (size_t nvalues, const Short* data); virtual size_t write (size_t nvalues, const uShort* data); virtual size_t write (size_t nvalues, const Int* data); virtual size_t write (size_t nvalues, const uInt* data); virtual size_t write (size_t nvalues, const Int64* data); virtual size_t write (size_t nvalues, const uInt64* data); virtual size_t write (size_t nvalues, const Float* data); virtual size_t write (size_t nvalues, const Double* data); virtual size_t write (size_t nvalues, const Complex* value); virtual size_t write (size_t nvalues, const DComplex* value); virtual size_t write (size_t nvalues, const String* value); // // Read the values from the ByteIO object. // Bool, complex and String values are handled by the base class. // virtual size_t read (size_t nvalues, Bool* value); virtual size_t read (size_t nvalues, Char* data); virtual size_t read (size_t nvalues, uChar* data); virtual size_t read (size_t nvalues, Short* data); virtual size_t read (size_t nvalues, uShort* data); virtual size_t read (size_t nvalues, Int* data); virtual size_t read (size_t nvalues, uInt* data); virtual size_t read (size_t nvalues, Int64* data); virtual size_t read (size_t nvalues, uInt64* data); virtual size_t read (size_t nvalues, Float* data); virtual size_t read (size_t nvalues, Double* data); virtual size_t read (size_t nvalues, Complex* value); virtual size_t read (size_t nvalues, DComplex* value); virtual size_t read (size_t nvalues, String* value); // }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/IO/RegularFileIO.cc000066400000000000000000000106461476623553700175110ustar00rootroot00000000000000//# RegularFileIO.cc: Class for IO on a regular file //# Copyright (C) 1996,1997,1998,1999,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include // needed for errno #include // needed for strerror namespace casacore { //# NAMESPACE CASACORE - BEGIN RegularFileIO::RegularFileIO (const RegularFile& regularFile, ByteIO::OpenOption option, uInt bufferSize) : itsOption (option), itsRegularFile (regularFile) { int file = openCreate (regularFile, option); attach (file, (bufferSize == 0 ? 16384 : bufferSize)); // If appending, set the stream offset to the file length. if (option == ByteIO::Append) { seek (length()); } } RegularFileIO::~RegularFileIO() { detach (True); if (itsOption == ByteIO::Scratch || itsOption == ByteIO::Delete) { itsRegularFile.remove(); } } int RegularFileIO::openCreate (const RegularFile& file, ByteIO::OpenOption option, Bool useODirect) { const String& name = file.path().expandedName(); Bool create = False; Int stropt; switch (option) { case ByteIO::Old: stropt = O_RDONLY; break; case ByteIO::NewNoReplace: if (file.exists()) { throw (AipsError ("RegularFileIO: new file " + name + " already exists")); } CASACORE_FALLTHROUGH; case ByteIO::New: case ByteIO::Scratch: create = True; stropt = O_RDWR | O_CREAT | O_TRUNC; break; case ByteIO::Append: stropt = O_RDWR; break; case ByteIO::Update: case ByteIO::Delete: stropt = O_RDWR; break; default: throw (AipsError ("RegularFileIO: unknown open option")); } Int stropt_orig = stropt; #ifdef HAVE_O_DIRECT if (useODirect) { stropt |= O_DIRECT; } #else useODirect = False; #endif // Open the file. Try it twice in case O_DIRECT fails. int fd; for (int i=0; i<2; ++i) { if (create) { fd = trace3OPEN ((char*)name.chars(), stropt, 0666); } else { fd = trace2OPEN ((char*)name.chars(), stropt); } if (fd < 0 && useODirect) { stropt = stropt_orig; } else { break; } } if (fd < 0) { throw (AipsError ("RegularFileIO: error in open or create of file " + name + ": " + strerror(errno))); } return fd; } void RegularFileIO::reopenRW() { if (isWritable()) { return; } // First try if the file can be opened as read/write. const String& name = itsRegularFile.path().expandedName(); int file = trace2OPEN ((char *)name.chars(), O_RDWR); if (file < 0) { throw (AipsError ("RegularFileIO::reopenRW " "not possible for file " + name + ": " + strerror(errno))); } uInt bufsize = bufferSize(); detach (True); attach (file, bufsize); // It can be reopened, so close and reopen. itsOption = ByteIO::Update; } String RegularFileIO::fileName() const { return itsRegularFile.path().expandedName(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/IO/RegularFileIO.h000066400000000000000000000107641476623553700173540ustar00rootroot00000000000000//# RegularFileIO.h: Class for IO on a regular file //# Copyright (C) 1996,1997,1999,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_REGULARFILEIO_H #define CASA_REGULARFILEIO_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //

        // Class for IO on a regular file. // // // // // //
      • FilebufIO class // // // This class is a specialization of class // ByteIO. It uses a // regular file as the data store. //

        // The class is derived from FilebufIO, // which contains all functions to access the file. The description of // this class explains the use of the filebufSize argument // in the constructor. // // // // // Create a file (which should not exist yet). // RegularFileIO regio (RegularFile("file.name"), ByteIO::NewNoReplace); // // Use that as the sink of AipsIO. // AipsIO stream (®io); // // Write values. // stream << (Int)10; // stream << True; // // Seek to beginning of file and read data in. // stream.setpos (0); // Int vali; // Bool valb; // stream >> vali >> valb; // // class RegularFileIO: public FilebufIO { public: // Create an IO stream object for a regular file with the given name. // The ByteIO option determines if the file will be created // or opened for input and/or output. //
        // The argument filebufSize defines the length of // the internal buffer in the underlying // FilebufIO object. A zero length uses an appropriate default. explicit RegularFileIO (const RegularFile& regularFile, ByteIO::OpenOption = ByteIO::Old, uInt filebufSize=0); ~RegularFileIO(); // Reopen the underlying file for read/write access. // Nothing will be done if the stream is writable already. // Otherwise it will be reopened and an exception will be thrown // if it is not possible to reopen it for read/write access. virtual void reopenRW(); // Get the file name of the file attached. virtual String fileName() const; // Convenience function to open or create a file. // Optionally it is checked if the file does not exist yet. // If useODirect=True and if supported by the OS, the file will be opened // with O_DIRECT which bypasses the kernel's file cache for more predictable // I/O behaviour. It requires the size and the alignment of the data to be // read/written to be a multiple of the the disk's logical block size. // It returns the file descriptor. static int openCreate (const RegularFile& file, ByteIO::OpenOption, Bool useODirect=False); private: OpenOption itsOption; RegularFile itsRegularFile; // Copy constructor, should not be used. RegularFileIO (const RegularFileIO& that); // Assignment, should not be used. RegularFileIO& operator= (const RegularFileIO& that); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/IO/StreamIO.cc000066400000000000000000000114771476623553700165460ustar00rootroot00000000000000//# StreamIO.cc: Class for IO to a connection oriented socket //# Copyright (C) 2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include //# No socket support on Cray XT3 Catamount (yet) #ifndef AIPS_CRAY_PGI #include #include // Definition of sockaddr_in #include // Definition of sockaddr & socket function #include #endif #include // needed for ::close #include //# for memcpy with gcc-4.3 namespace casacore { //# NAMESPACE CASACORE - BEGIN StreamIO::StreamIO(const String& hostname, uShort portNumber) :ByteIO(), itsSockDesc(-1) { #ifdef AIPS_CRAY_PGI throw AipsError("StreamIO is not supported on Cray XT3"); #else // Do hostname lookup! struct sockaddr_in serverInfo; memset(&serverInfo, 0, sizeof(serverInfo)); serverInfo.sin_family = AF_INET; Regex anyLetters("[A-Za-z]"); if(hostname.contains(anyLetters)){ struct hostent *hp = gethostbyname(hostname.chars()); memcpy ((char *) &serverInfo.sin_addr, (char *) hp->h_addr, hp->h_length); serverInfo.sin_family = hp->h_addrtype; } else { serverInfo.sin_addr.s_addr = inet_addr(hostname.chars());; } serverInfo.sin_port = htons(portNumber); itsSockDesc = socket(AF_INET, SOCK_STREAM, 0); if (itsSockDesc < 0) { throw(AipsError(String("StreamIO::StreamIO - cannot attach a socket to") + String(" host ") + hostname + String(" on port ") + String::toString(portNumber))); } if (connect(itsSockDesc, reinterpret_cast(&serverInfo), sizeof(serverInfo)) < 0) { throw(AipsError(String("StreamIO::StreamIO - cannot connect to") + String(" host ") + hostname + String(" on port ") + String::toString(portNumber))); } #endif } StreamIO::~StreamIO() { if (itsSockDesc > 0) { ::close(itsSockDesc); } } void StreamIO::write(Int64 size, const void* buf) { Int64 bytesToWrite = size; const Char* bytePtr = static_cast(buf); while (bytesToWrite > 0) { const Int64 bytesWritten = ::write(itsSockDesc, bytePtr, bytesToWrite); if (bytesWritten <= 0) { const String fString = "StreamIO::write - cannot write "; if (bytesToWrite == size) { throw(AipsError(fString + String("any data to the socket"))); } else { throw(AipsError(fString + String("all the data to the socket"))); } } bytePtr += bytesWritten; bytesToWrite -= bytesWritten; } } Int64 StreamIO::read(Int64 size, void* buf, Bool throwException) { Int64 bytesToRead = size; Char* bytePtr = static_cast(buf); while (bytesToRead > 0) { const Int64 bytesRead = ::read(itsSockDesc, bytePtr, bytesToRead); if (bytesRead < 0) { throw (AipsError(String("StreamIO::read - ") + String("Error returned by system call"))); } if (bytesRead > bytesToRead) { throw (AipsError ("StreamIO::read - read more bytes than requested")); } if (bytesRead == 0) { break; } bytesToRead -= bytesRead; bytePtr += bytesRead; } if (bytesToRead != 0 && throwException) { throw (AipsError ("StreamIO::read - incorrect number of bytes read")); } return size - bytesToRead; } Int64 StreamIO::doSeek (Int64, ByteIO::SeekOption) { throw(AipsError("StreamIO::doSeek - streams are not seekable.")); return -1; } Int64 StreamIO::length() { return -1; } Bool StreamIO::isReadable() const { return True; } Bool StreamIO::isWritable() const { return True; } Bool StreamIO::isSeekable() const { return False; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/IO/StreamIO.h000066400000000000000000000074431476623553700164060ustar00rootroot00000000000000//# StreamIO.h: Class for connection oriented IO to/from a socket //# Copyright (C) 2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_STREAMIO_H #define CASA_STREAMIO_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class String; //

        Class for IO on connection oriented socket // // // // //
      • ByteIO class //
      • Tape descriptors // // // This class is a specialization of class // ByteIO. It uses a file descriptor // to read/write data to a Internet (AF_INET) stream. //

        // // // // // This class was needed for the online version of the VLA filler. // class StreamIO: public ByteIO { public: // Construct a stream that is attached to the specified host on the specified // portnumber. Name lookup is not currently done so that the dotted quad // notation must be used. StreamIO(const String& hostname, uShort portNumber); // The destructor closes the file. virtual ~StreamIO(); // Write the specified number of bytes. virtual void write(Int64 size, const void* buf); // Read size bytes from the tape. Returns the number of bytes // actually read or a negative number if an error occured. Will throw an // exception (AipsError) if the requested number of bytes could not be read, // or an error occured, unless throwException is set to False. virtual Int64 read(Int64 size, void* buf, Bool throwException=True); // Get the length of the stream. Not a meaningful function for this // class and this function always returns -1. virtual Int64 length(); // Is the stream readable? This function always returns True. virtual Bool isReadable() const; // Is the stream writable? This function always returns True. virtual Bool isWritable() const; // Is the stream seekable? This function always returns False. virtual Bool isSeekable() const; protected: // Reset the position pointer to the given value. It returns the new // position. As stream devices are not seekable calling this function will // always throw an AipsError exception. virtual Int64 doSeek(Int64 offset, ByteIO::SeekOption); private: // The following functions are made private so that the compiler does not // generate default ones. They cannot be used and are not defined. StreamIO (const StreamIO& other); StreamIO& operator= (const StreamIO& other); int itsSockDesc; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/IO/TapeIO.cc000066400000000000000000000231071476623553700161750ustar00rootroot00000000000000//# TapeIO.cc: Class for IO on a tape device //# Copyright (C) 1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include // needed for ::close #include // needed for ::open #include // needed for errno #include // needed for strerror #ifndef CASA_NOTAPE // No tape support on Cray XT3 and OS-X 10.6 # if defined(AIPS_CRAY_PGI) # define CASA_NOTAPE 1 # endif # if defined(__APPLE__) // MAC_OS_X_VERSION_MAX_ALLOWED reflects the version of the SDK being used # include # if defined(MAC_OS_X_VERSION_10_6) # if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 # define CASA_NOTAPE 1 # endif # endif # endif #endif #ifndef CASA_NOTAPE # include // needed for ioctl # if defined(AIPS_SOLARIS) || defined(AIPS_DARWIN) # include // needed for ioctl # include // needed for ioctl # endif #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN TapeIO::TapeIO() :ByteIO(), itsDevice(-1), itsOwner(False), itsReadable(False), itsWritable(False), itsSeekable(False), itsDeviceName("") { } TapeIO::TapeIO (int fd) :ByteIO(), itsDevice(-1) { attach(fd); } TapeIO::TapeIO(const Path& device, Bool writable) :ByteIO(), itsDevice(-1) { attach(device, writable); } TapeIO::~TapeIO() { detach(); } void TapeIO::attach(int fd) { if (itsDevice >= 0) detach(); DebugAssert(itsDevice == -1, AipsError); itsOwner = False; itsDevice = fd; fillRWFlags(); fillSeekable(); itsDeviceName = String(""); } void TapeIO::attach(const Path& device, Bool writable) { if (itsDevice >= 0) detach(); DebugAssert(itsDevice == -1, AipsError); itsOwner = True; itsDevice = TapeIO::open(device, writable); fillRWFlags(); fillSeekable(); itsDeviceName = device.absoluteName(); } void TapeIO::write (Int64 size, const void* buf) { // Throw an exception if not writable. if (!itsWritable) { throw (AipsError ("TapeIO object is not writable")); } if (::write (itsDevice, buf, size) != static_cast(size)) { throw (AipsError (String("TapeIO: write error: ") + strerror(errno))); } } Int64 TapeIO::read(Int64 size, void* buf, Bool throwException) { if (!itsReadable) { throw (AipsError ("TapeIO::read - tape is not readable")); } Int64 bytesRead = ::read (itsDevice, buf, size); if (bytesRead < 0) { throw (AipsError (String("TapeIO::read - " " error returned by system call: ") + strerror(errno))); } if (bytesRead > size) { // Should never be executed throw (AipsError ("TapeIO::read - read more bytes than requested")); } if (bytesRead != size && throwException) { throw (AipsError ("TapeIO::read - incorrect number of bytes read")); } #if defined(AIPS_SOLARIS) // I assume a read of zero means we have hit the end of file. Solaris // requires an explicit skip to the next file UNLESS the bsd compatible // devices are used. The bsd compatible devices have a 'b' in the device // name. This test may fail if symlinks to the tape device are used. if (bytesRead == 0 && size != 0 && !Path(itsDeviceName).baseName().contains('b')) { skip(1); } #endif return bytesRead; } void TapeIO::rewind() { #ifndef CASA_NOTAPE struct mtop tapeCommand; tapeCommand.mt_op = MTREW; tapeCommand.mt_count = 1; Int error = ::ioctl(itsDevice, MTIOCTOP, &tapeCommand); if (error != 0) { throw(AipsError(String("TapeIO::rewind - error returned by ioctl: ") + strerror(errno))); } #endif } #ifndef CASA_NOTAPE void TapeIO::skip(uInt howMany) { if (howMany > 0) { struct mtop tapeCommand; tapeCommand.mt_op = MTFSF; tapeCommand.mt_count = howMany; Int error = ::ioctl(itsDevice, MTIOCTOP, &tapeCommand); if (error != 0) { throw(AipsError(String("TapeIO::skip - error returned by ioctl: ") + strerror(errno))); } } } #else void TapeIO::skip(uInt) { } #endif #ifndef CASA_NOTAPE void TapeIO::mark(uInt howMany) { DebugAssert(isWritable(), AipsError); if (howMany > 0) { struct mtop tapeCommand; tapeCommand.mt_op = MTWEOF; tapeCommand.mt_count = howMany; Int error = ::ioctl(itsDevice, MTIOCTOP, &tapeCommand); if (error != 0) { throw(AipsError(String("TapeIO::mark - error returned by ioctl: ") + strerror(errno))); } } } #else void TapeIO::mark(uInt) { } #endif Bool TapeIO::fixedBlocks() const { return (getBlockSize() != 0) ? True : False; } uInt TapeIO::fixedBlockSize() const { return getBlockSize(); } void TapeIO::setFixedBlockSize(uInt sizeInBytes) { DebugAssert(sizeInBytes > 0, AipsError); setBlockSize(sizeInBytes); } void TapeIO::setVariableBlockSize() { #if defined(AIPS_LINUX) setBlockSize(0); #endif } #if (defined(AIPS_SOLARIS) || defined(AIPS_LINUX)) && !defined(CASA_NOTAPE) void TapeIO::setBlockSize(uInt sizeInBytes) { struct mtop tapeCommand; #if defined(AIPS_LINUX) tapeCommand.mt_op = MTSETBLK; #else tapeCommand.mt_op = MTSRSZ; #endif tapeCommand.mt_count = sizeInBytes; Int error = ::ioctl(itsDevice, MTIOCTOP, &tapeCommand); if (error != 0) { throw(AipsError(String("TapeIO::setVariableBlockSize - ") + String("error returned by ioctl: ") + strerror(errno))); } #else void TapeIO::setBlockSize(uInt) { #endif } uInt TapeIO::getBlockSize() const { #if (defined(AIPS_SOLARIS) || defined(AIPS_LINUX)) && !defined(CASA_NOTAPE) #if defined(AIPS_LINUX) struct mtget tapeInquiry; Int error = ::ioctl(itsDevice, MTIOCGET, &tapeInquiry); if (error != 0) { throw(AipsError(String("TapeIO::setVariableBlockSize - ") + String("error returned by ioctl: ") + strerror(errno))); } return tapeInquiry.mt_dsreg & MT_ST_BLKSIZE_MASK; #else struct mtdrivetype tapeInfo; struct mtdrivetype_request tapeInquiry; tapeInquiry.size = sizeof(struct mtdrivetype); tapeInquiry.mtdtp = &tapeInfo; Int error = ::ioctl(itsDevice, MTIOCGETDRIVETYPE, &tapeInquiry); if (error != 0) { throw(AipsError(String("TapeIO::setVariableBlockSize - ") + String("error returned by ioctl: ") + strerror(errno))); } return tapeInfo.bsize; #endif #else return 0; #endif } Int64 TapeIO::doSeek (Int64 offset, ByteIO::SeekOption dir) { switch (dir) { case ByteIO::Begin: return ::lseek (itsDevice, offset, SEEK_SET); case ByteIO::End: return ::lseek (itsDevice, offset, SEEK_END); default: break; } return ::lseek (itsDevice, offset, SEEK_CUR); } Int64 TapeIO::length() { return -1; } Bool TapeIO::isReadable() const { return itsReadable; } Bool TapeIO::isWritable() const { return itsWritable; } Bool TapeIO::isSeekable() const { return itsSeekable; } String TapeIO::fileName() const { return itsDeviceName; } int TapeIO::open(const Path& device, Bool writable) { int fd; const String& deviceString = device.absoluteName(); char* devicePtr = (char*) deviceString.chars(); if (writable) { fd = ::open (devicePtr, O_RDWR); } else { fd = ::open (devicePtr, O_RDONLY); } if (fd == -1) { throw (AipsError ("TapeIO: device " + deviceString + " could not be opened: " + String(strerror(errno)))); } return fd; } void TapeIO::close(int fd) { DebugAssert(fd >= 0, AipsError); if (::close (fd) == -1) { throw (AipsError (String("TapeIO: file could not be closed: ") + strerror(errno))); } } void TapeIO::detach() { if (itsOwner) { if (isWritable()) mark(1); TapeIO::close(itsDevice); itsOwner = False; itsDeviceName = String(""); } itsDevice = -1; itsSeekable = itsReadable = itsWritable = False; } void TapeIO::fillRWFlags() { if (itsDevice < 0) { itsReadable = False; itsWritable = False; return; } int flags = fcntl (itsDevice, F_GETFL); if ((flags & O_RDWR) == O_RDWR) { itsReadable = True; itsWritable = True; } else if ((flags & O_RDONLY) == O_RDONLY) { itsReadable = True; itsWritable = False; } else if ((flags & O_WRONLY) == O_WRONLY) { itsReadable = False; itsWritable = True; } else { itsReadable = False; itsWritable = False; } } void TapeIO::fillSeekable() { if (itsDevice < 0) { itsSeekable = False; return; } itsSeekable = (seek (0, ByteIO::Current) >= 0); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/IO/TapeIO.h000066400000000000000000000173771476623553700160530ustar00rootroot00000000000000//# TapeIO.h: Class for IO on a tape device. //# Copyright (C) 1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_TAPEIO_H #define CASA_TAPEIO_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class Path; //

        Class for IO on a tape device // // // // //
      • ByteIO class //
      • Tape descriptors // // // This class is a specialization of class // ByteIO. It uses a file descriptor // to read/write data. //

        // The file associated with the file descriptor has to be opened // before hand. // The constructor will determine automatically if the file is // readable, writable and seekable. // Note that on destruction the file descriptor is NOT closed. // // // This example shows how FiledesIO can be used with an fd. // It uses the fd for a regular file, which could be done in an easier // way using class RegularFileIO. // However, when using pipes or sockets, this would be the only way. // // // Get a file descriptor for the file. // int fd = open ("file.name"); // // Use that as the source of AipsIO (which will also use CanonicalIO). // FiledesIO fio (fd); // AipsIO stream (&fio); // // Read the data. // Int vali; // Bool valb; // stream >> vali >> valb; // // // // Make it possible to use the Casacore IO functionality on any file. // In this way any device can be hooked to the IO framework. // class TapeIO: public ByteIO { public: // Default constructor. // A stream can be attached using the attach function. TapeIO(); // Construct from the given file descriptor. The file descriptor must have // been obtained using the TapeIO::open static function. When constructed // this way the class will not take over the file descriptor and hence not // close the Tape device when this class is destroyed. explicit TapeIO(int fd); // Construct from the given device. The device must point to a tape device // and if requested it is checked if the device is writeable. Throws an // exception if the device could not be opened correctly. When constructed // this way the class will close the Tape device when this class is destroyed // or the TapeIO object is attached to a new file descriptor. TapeIO(const Path& device, Bool writable = False); // The destructor will only close the file if the appropriate constructor, or // attach function, was used. virtual ~TapeIO(); // Attach to the given file descriptor. The file descriptor will not be // closed when this class is destroyed. void attach(int fd); // Attach to the given tape device. The tape will be closed when this class // is destroyed or the TapeIO object is attached to a new descriptor. void attach(const Path& device, Bool writable = False); // Write the specified number of bytes. virtual void write(Int64 size, const void* buf); // Read size bytes from the tape. Returns the number of bytes // actually read or a negative number if an error occured. Will throw an // exception (AipsError) if the requested number of bytes could not be read, // or an error occured, unless throwException is set to False. Will always // throw an exception if the tape is not readable or the system call returns // an undocumented value. Returns zero if the tape is at the end of the // current file (and size is non-zero and throwException is False). virtual Int64 read(Int64 size, void* buf, Bool throwException=True); // Rewind the tape device to the beginning. virtual void rewind(); // skip the specified number of files (ie tape marks) on the tape. Throws an // exception if you try to skip past the last filemark. virtual void skip(uInt howMany=1); // write the specified number of filemarks. virtual void mark(uInt howMany=1); // returns True if the tape device is configured to use a fixed block size Bool fixedBlocks() const; // returns the block size in bytes. Returns zero if the device is configured // to use variable length blocks. uInt fixedBlockSize() const; // Configure the tape device to use fixed length blocks of the specified // size. The size must be bigger than zero (dugh!). Values bigger than 64k // may cause problems on some systems. Currently this function only does // anything under Solaris and Linux systems. void setFixedBlockSize(uInt sizeInBytes); // Configure the tape device to use variable length blocks. Currently this // function only does anything under Solaris and Linux systems. void setVariableBlockSize(); // Get the length of the tape device. Not a meaningful function for this // class and this function always returns -1. virtual Int64 length(); // Is the tape device readable? virtual Bool isReadable() const; // Is the tape device writable? virtual Bool isWritable() const; // Is the tape device seekable? virtual Bool isSeekable() const; // Get the name of the attached device or return a zero length string if it // cannot be determined. virtual String fileName() const; // Some static convenience functions for file descriptor opening & // closing. The open function returns a file descriptor and the close // function requires a file descriptor as an argument. // static int open(const Path& device, Bool writable = False); static void close(int fd); // protected: // Detach the FILE. Close it when it is owned. void detach(); // Determine if the file is readable and/or writable. void fillRWFlags(); // Determine if the file is seekable. void fillSeekable(); // Reset the position pointer to the given value. It returns the new // position. May not work on all Tape devices use the isSeekable(0 member // function to see if this function is usuable. Otherwise an Exception // (AipsError) is thrown. virtual Int64 doSeek(Int64 offset, ByteIO::SeekOption); private: // The following functions are made private so that the compiler does not // generate default ones. They cannot be used and are not defined. TapeIO (const TapeIO& that); TapeIO& operator= (const TapeIO& that); void setBlockSize(uInt sizeInBytes); uInt getBlockSize() const; int itsDevice; Bool itsOwner; Bool itsReadable; Bool itsWritable; Bool itsSeekable; String itsDeviceName; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/IO/TypeIO.cc000066400000000000000000000113201476623553700162170ustar00rootroot00000000000000//# TypeIO.cc: Abstract base class for IO of data in a type-dependent format //# Copyright (C) 1996,1999,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TypeIO::TypeIO (const std::shared_ptr& byteIO) : itsByteIO(byteIO) {} TypeIO::TypeIO (const TypeIO& that) : itsByteIO (that.itsByteIO) {} TypeIO::~TypeIO() {} TypeIO& TypeIO::operator= (const TypeIO& that) { if (this != &that) { itsByteIO = that.itsByteIO; } return *this; } const ByteIO& TypeIO::byteIO() const { return *itsByteIO; } ByteIO& TypeIO::byteIO() { return *itsByteIO; } Int64 TypeIO::seek (Int64 offset, ByteIO::SeekOption option) { return itsByteIO->seek (offset, option); } Int64 TypeIO::seek (Int offset, ByteIO::SeekOption option) { return itsByteIO->seek (offset, option); } Bool TypeIO::isReadable() const { return itsByteIO->isReadable(); } Bool TypeIO::isWritable() const { return itsByteIO->isWritable(); } Bool TypeIO::isSeekable() const { return itsByteIO->isSeekable(); } size_t TypeIO::write (size_t nvalues, const Bool* value) { size_t nb = (nvalues+7) / 8; uChar* buf = new uChar[nb]; Conversion::boolToBit (buf, value, nvalues); write (nb, buf); delete [] buf; return nb; } size_t TypeIO::write (size_t nvalues, const Complex* value) { if (sizeof(Complex) == 2*sizeof(float)) { return write (2*nvalues, (const float*)value); } size_t n = 0; for (size_t i=0; i #include #include //# The following should be a forward declaration. But our Complex & DComplex //# classes are a typedef hence this does not work. Replace the following with //# forward declarations when Complex and DComplex are no longer typedefs. #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class String; //

        Abstract base class for IO of data in a type-dependent format // // // // //
      • ByteIO class and derived classes // // // This class is the abstract base class for doing IO in a type-dependent // way. Derived from it are classes like // CanonicalIO doing the actual formatting of the data. //

        // The TypeIO classes convert the data to/from the given format // using the static conversion functions in the classes like // CanonicalConversion. // The data is written to or read from the ByteIO // object given when constructing the TypeIO object. //

        // TypeIO declares the virtual functions read and write to read/write // one or more values of a given data type. Usually the derived classes // have to implement these functions. An exception are the functions // handling Bool, complex and String values. These functions have a // default implementation in this base class. However, if needed // they can be overwritten in derived classes. // // // The base class is needed for polymorphic type-dependent IO. // Furthermore the common functionality can be implemented here. // class TypeIO { public: // Constructor. // The read/write functions will use the given ByteIO object // as the data store. explicit TypeIO (const std::shared_ptr& byteIO); virtual ~TypeIO(); // Functions to return a reference to the ByteIO class. // const ByteIO& byteIO() const; ByteIO& byteIO(); // // Convert the values and write them to the ByteIO object. // By default Bools are stored as bits, Complex as 2 floats, // DComplex as 2 doubles and String as a length (uInt) and chars. // If it does not succeed an exception will be thrown. // virtual size_t write (size_t nvalues, const Bool* value); virtual size_t write (size_t nvalues, const Char* value) = 0; virtual size_t write (size_t nvalues, const uChar* value) = 0; virtual size_t write (size_t nvalues, const Short* value) = 0; virtual size_t write (size_t nvalues, const uShort* value) = 0; virtual size_t write (size_t nvalues, const Int* value) = 0; virtual size_t write (size_t nvalues, const uInt* value) = 0; virtual size_t write (size_t nvalues, const Int64* value) = 0; virtual size_t write (size_t nvalues, const uInt64* value) = 0; virtual size_t write (size_t nvalues, const Float* value) = 0; virtual size_t write (size_t nvalues, const Double* value) = 0; virtual size_t write (size_t nvalues, const Complex* value); virtual size_t write (size_t nvalues, const DComplex* value); virtual size_t write (size_t nvalues, const String* value); // // Read the values from the ByteIO object and convert them. // By default Bools are stored as bits, Complex as 2 floats, // DComplex as 2 doubles and String as a length (uInt) and chars. // If it does not succeed an exception will be thrown. // virtual size_t read (size_t nvalues, Bool* value); virtual size_t read (size_t nvalues, Char* value) = 0; virtual size_t read (size_t nvalues, uChar* value) = 0; virtual size_t read (size_t nvalues, Short* value) = 0; virtual size_t read (size_t nvalues, uShort* value) = 0; virtual size_t read (size_t nvalues, Int* value) = 0; virtual size_t read (size_t nvalues, uInt* value) = 0; virtual size_t read (size_t nvalues, Int64* value) = 0; virtual size_t read (size_t nvalues, uInt64* value) = 0; virtual size_t read (size_t nvalues, Float* value) = 0; virtual size_t read (size_t nvalues, Double* value) = 0; virtual size_t read (size_t nvalues, Complex* value); virtual size_t read (size_t nvalues, DComplex* value); virtual size_t read (size_t nvalues, String* value); // // This function sets the position on the given offset. // The seek option defines from which file position the seek is done. // -1 is returned if not seekable. // Int64 seek (Int64 offset, ByteIO::SeekOption = ByteIO::Begin); Int64 seek (Int offset, ByteIO::SeekOption = ByteIO::Begin); // // Is the TypeIO stream readable? Bool isReadable() const; // Is the TypeIO stream writable? Bool isWritable() const; // Is the TypeIO stream seekable? Bool isSeekable() const; protected: // This variable keeps a pointer to a ByteIO. std::shared_ptr itsByteIO; // The copy constructor uses reference semantics. TypeIO (const TypeIO& TypeIO); // The assignment operator uses reference semantics. TypeIO& operator= (const TypeIO& typeIO); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/IO/test/000077500000000000000000000000001476623553700155215ustar00rootroot00000000000000casacore-3.7.1/casa/IO/test/CMakeLists.txt000066400000000000000000000010231476623553700202550ustar00rootroot00000000000000set (tests tAipsIOCarray tAipsIO tBucketBuffered tBucketCache tBucketFile tBucketMapped tByteIO tByteSink tByteSinkSource tFilebufIO tFileIO tFileUnbufferedIO tLargeFileIO tLockFile tMFFileIO tMappedIO tMMapIO tMultiFile tMultiFileLarge tMultiHDF5 tTapeIO tTypeIO ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_casa) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/casa/IO/test/tAipsIO.cc000066400000000000000000000324571476623553700173530ustar00rootroot00000000000000//# tAipsIO.cc: This program tests the AipsIO class //# Copyright (C) 1993,1994,1995,1996,1997,1998,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include // This test program tests the AipsIO class. // It writes all kind of stuff, reads it back and writes it to stdout. // A script compares this output with a reference output file. // It also tries to open/create in all kind of combinations and // catches the exceptions thrown. // // If an argument is given to tAipsIO, no statements throwing exceptions // will be executed. In this way no memory leaks should occur and a // tool like TestCenter can be used to check for that. // On the other hand using TestCenter for test coverage should include // the exceptions. //# Forward declaration. void doit (Bool doExcp); void doIO (Bool doExcp, Bool out, AipsIO&); void doTry (AipsIO&); int main (int argc, const char*[]) { try { doit (argc<2); } catch (std::exception& x) { cout << "\nCaught an exception: " << x.what() << endl; return 1; } cout << "end" << endl; return 0; // successfully executed } void doit (Bool doExcp) { { cout << "Test using normal files ..." << endl; AipsIO io("tAipsIO_tmp.data", ByteIO::New); // open output file doIO (doExcp, True, io); io.close(); io.open("tAipsIO_tmp.data"); doIO (doExcp, False, io); // Now do some open calls; some of them are erroneous which are caught. // Delete the file in case it exists. if (doExcp) { doTry (io); } } { cout << endl << "Test using MultiFile files ..." << endl; auto mfile = std::make_shared("tAipsIO_tmp.mf", ByteIO::New); AipsIO io("tAipsIO_tmp.data", ByteIO::New, 1024, mfile); // open output file doIO (doExcp, True, io); io.close(); io.open("tAipsIO_tmp.data", ByteIO::Old, 1024, mfile); doIO (doExcp, False, io); // Now do some open calls; some of them are erroneous which are caught. // Delete the file in case it exists. if (doExcp) { doTry (io); } } cout << endl << "Test using MemoryIO ..." << endl; auto membuf = std::make_shared(); { auto rawio = std::make_shared(membuf); AipsIO io2(rawio); doIO (doExcp, True, io2); } const uChar* iobuf = membuf->getBuffer(); uInt bufleng = membuf->length(); auto membuf2 = std::make_shared(iobuf, bufleng); { auto rawio = std::make_shared(membuf2); AipsIO io2(rawio); doIO (doExcp, False, io2); } } void doIO (Bool doExcp, Bool out, AipsIO& io) { Bool tbi,tbo; tbi = True; Char tci,tco; tci = -1; uChar tuci,tuco; tuci = 2; short tsi,tso; tsi = -3; unsigned short tusi,tuso; tusi = 4; int tii,tio; tii = -5; unsigned int tuii,tuio; tuii = 6; Int64 tli,tlo; tli = -7; uInt64 tuli,tulo; tuli = 8; float tfi,tfo; tfi = 3.15; double tdi,tdo; tdi = 6.897; Complex toi (1.98,-1000.45); Complex too; DComplex tdoi (93.7,-11.5); DComplex tdoo; int i,j; unsigned int len; String str("aa van Diepenaa"); String a("bcdefg"); String cp; int* ip; Int* lp; Int lo; String* cptr; String* sptr; String sap[6]; String cap[6]; static char s0[10] = "string000"; static char s1[10] = "str1"; static char s2[10] = "strin2"; static char s3[10] = "stri3"; static char s4[10] = "string45"; static char s5[10] = "s"; String ca[6]; ca[0] = s0; ca[1] = s1; ca[2] = s2; ca[3] = s3; ca[4] = s4; ca[5] = s5; String sa[6]; sa[0] = s1; sa[1] = s2; sa[2] = s3; sa[3] = s4; sa[4] = s5; sa[5] = s5; sa[5] += "abc"; cout << sa[5] << endl; Bool barr[100]; for (i=0; i<100; i++) { barr[i] = False; if (i%5 == 1) { barr[i] = True; } } Bool barri[100]; Int arr[250001]; for (i=0; i<250001; i++) { arr[i] = i; } i=-32768; if (out) { cout << io.putstart ("abcdefghij",20) << endl; io << tbi << tci << tuci << tsi << tusi << tii << tuii; io << tli << tuli << tfi << tdi << toi << tdoi; io << 3 << i; io.put (1, &i); cout << io.putend() << endl; cout << io.putstart ("abcdefghij",20) << endl; io << "Ger"; io << str(2,11); // put a substring io.put (1, &i); cout << io.putend() << endl; cout << io.putstart ("abcdefghij",20) << endl; io << 1; cout << io.putstart ("klm",21) << endl; io << 2; cout << io.putstart ("nopq",22) << endl; io << 3; cout << io.putend() << endl; io << 4; cout << io.putstart ("r",23) << endl; io << 5; cout << io.putend() << endl; io << 6; cout << io.putend() << endl; io << 7; cout << io.putend() << endl; cout << io.putstart ("abcdefghij",20) << endl; io << 3 << i; io.put (1, &i); for (i=0; i<250000; i++) { io << (arr[i]); } io.put (250000, arr); io.put (100, barr); io.put (250000, arr); io.put (100, barr); io.put (5, ca); io.put (5, ca); io.put (5, sa); io.put (5, sa); cout << io.putend() << endl; cout << "Length=" << io.getpos() << endl; // total length return; } cout << io.getNextType() << " " << io.getNextType() << " "; cout << "Version=" << io.getstart ("abcdefghij") << endl; // Check if all data types are read back correctly. io>>tbo>>tco>>tuco>>tso>>tuso>>tio>>tuio; io>>tlo>>tulo; io>>tfo>>tdo; io>>too; io>>tdoo; if (tbo!=tbi) cout << "Bool "<> i >> j; cout << i << " " << j << endl; io >> len; io.get (len, &i); cout << len << " " << i << endl; cout << io.getend() << endl; cout << "Version=" << io.getstart ("abcdefghij") << endl; io >> a; io >> cp; cout << a << cp << endl; io.getnew (len, ip); cout << len << " " << ip[0] << endl; cout << io.getend() << endl; delete [] ip; cout << "Version=" << io.getstart ("abcdefghij") << endl; io >> i; cout << i << endl; cout << io.getNextType() << " " << io.getNextType() << " "; cout << "Version=" << io.getstart ("klm") << endl; io >> i; cout << i << endl; cout << "Version=" << io.getstart ("nopq") << endl; io >> i; cout << i << endl; cout << io.getend() << endl; io >> i; cout << i << endl; cout << io.getNextType() << " "; cout << "Version=" << io.getstart ("r") << endl; io >> i; cout << i << endl; cout << io.getend() << endl; io >> i; cout << i << endl; cout << io.getend() << endl; io >> i; cout << i << endl; cout << io.getend() << endl; cout << "Version=" << io.getstart ("abcdefghij") << endl; io >> i >> j; cout << i << " " << j << endl; io >> len; io.get (len, &i); cout << len << " " << i << endl; for (i=0; i<250000; i++) { io >> lo; if (lo != i) { cout << lo << " " << i << endl; } } io >> len; cout << len << endl; io.get (len, &arr[1]); for (i=0; i<250000; i++) { if (arr[i+1] != i) { cout << i << " not equal" << endl; } } io >> len; cout << len << endl; io.get (len, barri); for (i=0; i<100; i++) { if (barri[i] != barr[i]) { cout << i << " barri not equal" << endl; } } len = 0; io.getnew (len, lp); cout << len << endl; for (i=0; i<250000; i++) { if (lp[i] != i) { cout << i << " not equal" << endl; } } delete [] lp; Bool* barrp; io.getnew (len, barrp); cout << len << endl; for (i=0; i<100; i++) { if (barrp[i] != barr[i]) { cout << i << " barrp not equal" << endl; } } delete [] barrp; len = 0; io >> len; cout << len << endl; io.get (len, &cap[1]); len = 0; io >> len; cout << len << endl; io.get (len, &sap[1]); len = 0; io.getnew (len, sptr); cout << len << endl; len = 0; io.getnew (len, cptr); cout << len << endl; for (i=0; i> len; // read beyond object } catch (std::exception& x) { cout << x.what() << endl; } } cout << io.getend() << endl; if (doExcp) { try { io.getstart ("aa"); // read-error (past EOF) } catch (std::exception& x) { cout << x.what() << endl; } } // So, the entire file has been read back. cout << "Length=" << io.getpos() << endl; io.setpos(1); if (doExcp) { try { io.getstart ("aa"); // no magic number } catch (std::exception& x) { cout << x.what() << endl; } } io.setpos(0); if (doExcp) { try { io.getstart ("aa"); // invalid object type } catch (std::exception& x) { cout << x.what() << endl; } } } void doTry (AipsIO& io) { cout << "TryErrOpen" << endl; try { io.open ("tAipsIO_tmp.aa"); // still open } catch (std::exception& x) { cout << x.what() << endl; } io.close (); // now close the file cout << "TryAlrCls" << endl; io.close (); try { io.open ("tAipsIO_tmp.aa", ByteIO::Delete); io.close (); } catch (std::exception& x) { } cout << "TryErrIn" << endl; try { io.open ("tAipsIO_tmp.aa"); io.close (); } catch (std::exception& x) { cout << x.what() << endl; } cout << "TryScr" << endl; io.open ("tAipsIO_tmp.aa", ByteIO::Scratch); io.close (); cout << "TryErrDel" << endl; try { io.open ("tAipsIO_tmp.aa", ByteIO::Delete); io.close (); } catch (std::exception& x) { cout << x.what() << endl; } cout << "TryOut" << endl; io.open ("tAipsIO_tmp.aa", ByteIO::New); io.close(); cout << "TryApp" << endl; io.open ("tAipsIO_tmp.aa", ByteIO::Append); io.close(); cout << "TryUpd" << endl; io.open ("tAipsIO_tmp.aa", ByteIO::Update); io.close(); cout << "TryErrNorep" << endl; try { io.open ("tAipsIO_tmp.aa", ByteIO::NewNoReplace); io.close (); } catch (std::exception& x) { cout << x.what() << endl; } cout << "TryDel" << endl; io.open ("tAipsIO_tmp.aa", ByteIO::Delete); io.close(); cout << "TryErrUpd" << endl; try { io.open ("tAipsIO_tmp.aa", ByteIO::Update); io.close (); } catch (std::exception& x) { cout << x.what() << endl; } cout << "TryNew" << endl; io.open ("tAipsIO_tmp.aa", ByteIO::New); io.close(); cout << "TryIn" << endl; io.open ("tAipsIO_tmp.aa",ByteIO::Old); io.close(); cout << "TryScr" << endl; io.open ("tAipsIO_tmp.aa", ByteIO::Scratch); io.close(); cout << "TryOut" << endl; io.open ("tAipsIO_tmp.aa", ByteIO::New); io.close(); cout << "TryApp" << endl; io.open ("tAipsIO_tmp.aa", ByteIO::Append); io.close(); cout << "TryDel" << endl; io.open ("tAipsIO_tmp.aa", ByteIO::Delete); io.close(); cout << "TryNorep" << endl; io.open ("tAipsIO_tmp.aa", ByteIO::NewNoReplace); io.close(); cout << "TryUpd" << endl; io.open ("tAipsIO_tmp.aa", ByteIO::Update); io.close(); cout << "TryNew" << endl; io.open ("tAipsIO_tmp.aa", ByteIO::New); io.close(); } casacore-3.7.1/casa/IO/test/tAipsIO.out000066400000000000000000000065121476623553700175660ustar00rootroot00000000000000Test using normal files ... sabc 1 105 1 52 1 2 3 20 3 17 64 94 1 3000288 Length=3000555 sabc abcdefghij abcdefghij Version=20 3 -32768 1 -32768 105 Version=20 Ger van Diepen 1 -32768 52 Version=20 1 klm klm Version=21 2 Version=22 3 20 4 r Version=23 5 17 6 64 7 94 Version=20 3 -32768 1 -32768 250000 100 250000 100 5 5 5 5 string000 string000 str1 str1 str1 str1 strin2 strin2 strin2 strin2 stri3 stri3 stri3 stri3 string45 string45 string45 string45 s s FilebufIO::readBlock - incorrect number of bytes read for file tAipsIO_tmp.data 3000288 FilebufIO::readBlock - incorrect number of bytes read for file tAipsIO_tmp.data Length=3000555 AipsIO::getNextType: tAipsIO_tmp.data - no magic value found AipsIO::getstart: found object type abcdefghij, expected aa TryErrOpen AipsIO: already open TryAlrCls TryErrIn RegularFileIO: error in open or create of file tAipsIO_tmp.aa: No such file or directory TryScr TryErrDel RegularFileIO: error in open or create of file tAipsIO_tmp.aa: No such file or directory TryOut TryApp TryUpd TryErrNorep RegularFileIO: new file tAipsIO_tmp.aa already exists TryDel TryErrUpd RegularFileIO: error in open or create of file tAipsIO_tmp.aa: No such file or directory TryNew TryIn TryScr TryOut TryApp TryDel TryNorep TryUpd TryNew Test using MultiFile files ... sabc 1 105 1 52 1 2 3 20 3 17 64 94 1 3000288 Length=3000555 sabc abcdefghij abcdefghij Version=20 3 -32768 1 -32768 105 Version=20 Ger van Diepen 1 -32768 52 Version=20 1 klm klm Version=21 2 Version=22 3 20 4 r Version=23 5 17 6 64 7 94 Version=20 3 -32768 1 -32768 250000 100 250000 100 5 5 5 5 string000 string000 str1 str1 str1 str1 strin2 strin2 strin2 strin2 stri3 stri3 stri3 stri3 string45 string45 string45 string45 s s MFFileIO::read - incorrect number of bytes (0 out of 4) read for logical file tAipsIO_tmp.data in MultiFileBase tAipsIO_tmp.mf 3000288 MFFileIO::read - incorrect number of bytes (0 out of 4) read for logical file tAipsIO_tmp.data in MultiFileBase tAipsIO_tmp.mf Length=3000555 AipsIO::getNextType: tAipsIO_tmp.data - no magic value found AipsIO::getstart: found object type abcdefghij, expected aa TryErrOpen AipsIO: already open TryAlrCls TryErrIn RegularFileIO: error in open or create of file tAipsIO_tmp.aa: No such file or directory TryScr TryErrDel RegularFileIO: error in open or create of file tAipsIO_tmp.aa: No such file or directory TryOut TryApp TryUpd TryErrNorep RegularFileIO: new file tAipsIO_tmp.aa already exists TryDel TryErrUpd RegularFileIO: error in open or create of file tAipsIO_tmp.aa: No such file or directory TryNew TryIn TryScr TryOut TryApp TryDel TryNorep TryUpd TryNew Test using MemoryIO ... sabc 1 105 1 52 1 2 3 20 3 17 64 94 1 3000288 Length=3000555 sabc abcdefghij abcdefghij Version=20 3 -32768 1 -32768 105 Version=20 Ger van Diepen 1 -32768 52 Version=20 1 klm klm Version=21 2 Version=22 3 20 4 r Version=23 5 17 6 64 7 94 Version=20 3 -32768 1 -32768 250000 100 250000 100 5 5 5 5 string000 string000 str1 str1 str1 str1 strin2 strin2 strin2 strin2 stri3 stri3 stri3 stri3 string45 string45 string45 string45 s s MemoryIO::read - incorrect number of bytes read: size=4, used=3000555, pos=3000555, left=0 3000288 MemoryIO::read - incorrect number of bytes read: size=4, used=3000555, pos=3000555, left=0 Length=3000555 AipsIO::getNextType: no magic value found AipsIO::getstart: found object type abcdefghij, expected aa end casacore-3.7.1/casa/IO/test/tAipsIOCarray.cc000066400000000000000000000073751476623553700205160ustar00rootroot00000000000000//# tAipsIOCarray.cc: This program tests the AipsIOCarray functions //# Copyright (C) 1993,1994,1995,1996,1998,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include // This test program tests the AipsIOCarray functions. // It writes all kind of stuff, reads it back and writes it to stdout. // A script compares this output with a reference output file. //# Forward declaration. void doit (Bool doExcp); int main (int argc, const char*[]) { try { doit ( (argc<2)); } catch (std::exception& x) { cout << "\nCaught an exception: " << x.what() << endl; return 1; } cout << "end" << endl; return 0; // successfully executed } void doit (Bool) { { Complex cp[1000]; Int ip[1000]; AipsIOCarrayEx1 ap[1000]; for (uInt i=0; i<1000; i++) { cp[i] = Complex (float(i), float(i+4)); ip[i] = i*2; ap[i] = AipsIOCarrayEx1 (i+10, i+20); } AipsIO io ("tAipsIOCarray_tmp.data", ByteIO::New); io.putstart ("tAipsIOCarray",1); putAipsIO (io, uInt(1000), cp); putAipsIO (io, uInt(1000), ap); putAipsIO (io, uInt(1000), ip); io.putend(); } { Int i; Complex cpi[1000]; Int ipi[1000]; AipsIOCarrayEx1 api[1000]; uInt n; AipsIO io ("tAipsIOCarray_tmp.data"); io.getstart ("tAipsIOCarray"); io >> n; getAipsIO (io, n, cpi); for (i=0; i<1000; i++) { if (cpi[i] != Complex (float(i), float(i+4))) { cout << "Get error in cp[" << i << "]" << endl; } } io >> n; getAipsIO (io, n, api); for (i=0; i<1000; i++) { if (api[i].a() != i+10 || api[i].b() != i+20) { cout << "Get error in ap[" << i << "]" << endl; } } io >> n; getAipsIO (io, n, ipi); for (i=0; i<1000; i++) { if (ipi[i] != i*2) { cout << "Get error in ip[" << i << "]" << endl; } } io.getend(); } { Int i; Complex* cpi; Int* ipi; AipsIOCarrayEx1* api; uInt n; AipsIO io ("tAipsIOCarray_tmp.data"); io.getstart ("tAipsIOCarray"); getnewAipsIO (io, n, &cpi); for (i=0; i<1000; i++) { if (cpi[i] != Complex (float(i), float(i+4))) { cout << "Getnew error in cp[" << i << "]" << endl; } } getnewAipsIO (io, n, &api); for (i=0; i<1000; i++) { if (api[i].a() != i+10 || api[i].b() != i+20) { cout << "Getnew error in ap[" << i << "]" << endl; } } getnewAipsIO (io, n, &ipi); for (i=0; i<1000; i++) { if (ipi[i] != i*2) { cout << "Getnew error in ip[" << i << "]" << endl; } } io.getend(); delete [] cpi; delete [] ipi; delete [] api; } } casacore-3.7.1/casa/IO/test/tAipsIOCarray.h000066400000000000000000000044561476623553700203550ustar00rootroot00000000000000//# tAipsIOCarray.h: This program tests the AipsIOCarray functions //# Copyright (C) 1996 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_TAIPSIOCARRAY_H #define CASA_TAIPSIOCARRAY_H //# Includes #include #include //

        // Example class for use in AipsIOCarray functions // // // // // //# Classes you should understand before using this one. //
      • AipsIOCarray // // // Make a class AipsIOCarrayEx1 to construct an array of non-standard objects // to be used with AipsIOCarray functions. // // This file defines an example class used in tAipsIOCarray.cc class AipsIOCarrayEx1 { public: AipsIOCarrayEx1() : a_p(0), b_p(0) {;} AipsIOCarrayEx1 (Int a, double b) : a_p(a), b_p(b) {;} friend AipsIO& operator<< (AipsIO& ios, const AipsIOCarrayEx1& a) { return ios << a.a_p << a.b_p; } friend AipsIO& operator>> (AipsIO& ios, AipsIOCarrayEx1& a) { return ios >> a.a_p >> a.b_p; } Int a() const {return a_p;} double b() const {return b_p;} private: Int a_p; double b_p; }; #endif casacore-3.7.1/casa/IO/test/tAipsIOCarray.out000066400000000000000000000000041476623553700207160ustar00rootroot00000000000000end casacore-3.7.1/casa/IO/test/tBucketBuffered.cc000066400000000000000000000066211476623553700211010ustar00rootroot00000000000000//# tBucketBuffered.cc: Test program for the BucketBuffered class //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include // // Test program for the BucketBuffered class // void a (Bool); void b (Bool); int main (int argc, const char*[]) { try { a (argc<2); b (argc<2); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } // Build a file. void a (Bool) { // Create the file. BucketFile file ("tBucketBuffered_tmp.data", 4000, False); file.open(); BucketBuffered cache (&file, 512, 32768, 5); Int i; union { char buf[32768]; Int bufi[32768/4]; }; for (i=0; i<32768; i++) { buf[i] = 0; } cache.extend (100); for (i=0; i<100; i++) { bufi[0] = i+1; bufi[32760/4] = i+10; memcpy (cache.getBuffer(), buf, 32768); cache.write (i, 0, 32768); } for (i=0; i<100; i++) { cache.read (i, 0, 32768); const char* buf = cache.getBuffer(); if (*(const Int*)buf != i+1 || *(const Int*)(buf+32760) != i+10) { cout << "xError in bucket " << i << endl; cout << *(const Int*)buf <<' '<< *(const Int*)(buf+32760) < #include #include #include #include #include // // Test program for the BucketCache class // void a (Bool); void b (Bool); void c (uInt bufSize); void d (uInt bufSize); int main (int argc, const char*[]) { try { a (argc<2); b (argc<2); c (0); // c (1024); // c (32768); // c (327680); d (0); // d (1024); // d (32768); // d (327680); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } static uInt counter = 0; // The toLocal and fromLocal function. // These versions copy the buffer in a reversed way. char* aToLocal (void*, const char* data) { char* ptr = new char[32768]; char* out = ptr; char* last = out+32768; while (out < last) { out[3] = *data++; out[2] = *data++; out[1] = *data++; out[0] = *data++; out += 4; } return ptr; } void aFromLocal (void*, char* data, const char* local) { char* out = data; char* last = out+32768; while (out < last) { out[3] = *local++; out[2] = *local++; out[1] = *local++; out[0] = *local++; out += 4; } } void aDeleteBuffer (void*, char* buffer) { delete [] buffer; } char* aInitBuffer (void*) { char* ptr = new char[32768]; for (uInt i=0; i<32768; i++) { ptr[i] = 0; } *(Int*)ptr = ++counter; *(Int*)(ptr+32760) = counter; return ptr; } // The toLocal and fromLocal function. // These versions do a straightforward copy (much faster). char* bToLocal (void*, const char* data) { char* ptr = new char[32768]; memcpy (ptr, data, 32768); return ptr; } void bFromLocal (void*, char* data, const char* local) { memcpy (data, local, 32768); } // Build a file. void a (Bool) { // Create the file. BucketFile file ("tBucketCache_tmp.data"); file.open(); BucketCache cache (&file, 512, 32768, 5, 10, 0, aToLocal, aFromLocal, aInitBuffer, aDeleteBuffer); uInt i; union { char buf[32768]; Int bufi[32768/4]; }; for (i=0; i<32768; i++) { buf[i] = 0; } for (i=0; i<100; i++) { bufi[0] = i+1; bufi[32760/4] = i+10; char* ptr = new char[32768]; memcpy (ptr, buf, 32768); cache.addBucket (ptr); cache.getBucket(0); } cache.extend (10); char* data = cache.getBucket (110); *(Int*)data = 110; *(Int*)(data+32760) = 110; cache.setDirty(); Int rec[128]; for (i=0; i<128; i++) { rec[i] = i; } rec[0] = cache.nBucket(); cache.put ((char*)rec, 512, 0); cache.put ((char*)rec, 512, 512 + cache.nBucket()*32768); cout << "wrote " << cache.nBucket() << " buckets of 32768 bytes" << endl; cache.flush(); } void b (Bool) { // Open the file. BucketFile file("tBucketCache_tmp.data", False); file.open(); Int i; Int rec[128]; file.read ((char*)rec, 512); for (i=1; i<128; i++) { if (rec[i] != i) { cout << "Error in rec pos " << i << endl; } } BucketCache cache (&file, 512, 32768, rec[0], 10, 0, aToLocal, aFromLocal, aInitBuffer, aDeleteBuffer); cache.get ((char*)rec, 512, 512 + cache.nBucket()*32768); for (i=1; i<128; i++) { if (rec[i] != i) { cout << "Error in rec pos " << i << endl; } } for (i=0; i<5; i++) { char* buf = cache.getBucket(i); if (*(Int*)buf != i+1 || *(Int*)(buf+32760) != i+1) { cout << "Error in bucket " << i << endl; } } cache.resize (20); for (i=0; i<100; i++) { char* buf = cache.getBucket(i+5); if (*(Int*)buf != i+1 || *(Int*)(buf+32760) != i+10) { cout << "Error in bucket " << i+5 << endl; } } cache.resize (4); for (i=0; i<10; i++) { char* buf = cache.getBucket (i+105); if (i == 5) { if (*(Int*)buf != 110 || *(Int*)(buf+32760) != 110) { cout << "Error in bucket " << i << endl; } }else{ if (*(Int*)buf != i+6 || *(Int*)(buf+32760) != i+6) { cout << "Error in bucket " << i+105 << endl; } } } cout << "checked " << cache.nBucket() << " buckets" << endl; } void c (uInt) { Timer timer; // Open the file. BucketFile file("tBucketCache_tmp.data", False); file.open(); uInt i; Int rec[128]; file.read ((char*)rec, 512); BucketCache cache (&file, 512, 32768, rec[0], 10, 0, aToLocal, aFromLocal, aInitBuffer, aDeleteBuffer); cache.get ((char*)rec, 512, 512 + cache.nBucket()*32768); for (uInt j=0; j<25; j++) { for (i=0; i<100; i++) { cache.getBucket(i); } } cout << "read 25x" << cache.nBucket() << " buckets with swap" << endl; cout << ">>> "; timer.show(); cout << "<<<" << endl; } void d (uInt) { Timer timer; // Open the file. BucketFile file("tBucketCache_tmp.data", False); file.open(); uInt i; Int rec[128]; file.read ((char*)rec, 512); BucketCache cache (&file, 512, 32768, rec[0], 10, 0, bToLocal, bFromLocal, aInitBuffer, aDeleteBuffer); cache.get ((char*)rec, 512, 512 + cache.nBucket()*32768); for (uInt j=0; j<50; j++) { for (i=0; i<100; i++) { cache.getBucket(i); } } cout << "read 50x" << cache.nBucket() << " buckets without swap" << endl; cout << cache.nBucket() << endl; cout << ">>> "; timer.show(); cout << "<<<" << endl; } casacore-3.7.1/casa/IO/test/tBucketCache.out000066400000000000000000000003621476623553700206000ustar00rootroot00000000000000wrote 115 buckets of 32768 bytes checked 115 buckets read 25x115 buckets with swap >>> 21.34 real 18.16 user 2.72 system <<< read 50x115 buckets without swap 115 >>> 11.1 real 5.8 user 5.12 system <<< casacore-3.7.1/casa/IO/test/tBucketFile.cc000066400000000000000000000121711476623553700202330ustar00rootroot00000000000000//# tBucketFile.cc: Test program for the BucketFile class //# Copyright (C) 1995,1996,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include // // Test program for the BucketFile class // void a(const std::shared_ptr&); void b(const std::shared_ptr&); void c(const std::shared_ptr&); int main (int argc, const char*[]) { try { for (int i=0; i<2; ++i) { std::shared_ptr mfile; if (i == 1) { mfile.reset (new MultiFile("tBucketFile_tmp.mf", ByteIO::New, 512)); } a(mfile); b(mfile); // Do exceptional things only when needed. if (argc < 2) { cout << ">>>" << endl; c(mfile); cout << "<<<" << endl; } } } catch (const std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; // exit with success status } // Build a file. void a(const std::shared_ptr& mfile) { // Create the file. BucketFile file ("tBucketFile_tmp.data", 0, False, mfile); AlwaysAssertExit (file.isWritable()); AlwaysAssertExit (file.name() == "tBucketFile_tmp.data"); Int ival=10; float fval=20; file.write (&ival, sizeof(Int)); file.write (&fval, sizeof(fval)); Int ival2; float fval2; file.seek (0); file.read (&ival2, sizeof(Int)); file.read (&fval2, sizeof(fval)); AlwaysAssertExit (ival2 == ival); AlwaysAssertExit (fval2 == fval); } void b(const std::shared_ptr& mfile) { // Open the file. BucketFile file ("tBucketFile_tmp.data", False, 0, False, mfile); AlwaysAssertExit (! file.isWritable()); AlwaysAssertExit (file.name() == "tBucketFile_tmp.data"); file.open(); Int ival=10; float fval=20; Int ival2; float fval2; file.read (&ival2, sizeof(Int)); file.read (&fval2, sizeof(fval)); AlwaysAssertExit (ival2 == ival); AlwaysAssertExit (fval2 == fval); // Set the file to read/write access. file.setRW(); file.seek (0); file.read (&ival2, sizeof(Int)); file.read (&fval2, sizeof(fval)); AlwaysAssertExit (ival2 == ival); AlwaysAssertExit (fval2 == fval); file.write (&fval, sizeof(fval)); file.write (&ival, sizeof(Int)); file.seek (0); file.read (&ival2, sizeof(Int)); file.read (&fval2, sizeof(fval)); AlwaysAssertExit (ival2 == ival); AlwaysAssertExit (fval2 == fval); file.read (&fval2, sizeof(fval)); file.read (&ival2, sizeof(Int)); AlwaysAssertExit (ival2 == ival); AlwaysAssertExit (fval2 == fval); } void c(const std::shared_ptr& mfile) { // Do some erroneous calls. Bool flag = False; BucketFile file1 ("tBucketFile_tmp.data1", False, 0, False, mfile); try { file1.open(); } catch (const std::exception& x) { flag = True; cout << x.what() << endl; } AlwaysAssertExit (flag); // Make the file readonly to test on such errors. RegularFile rfile("tBucketFile_tmp.data"); rfile.setPermissions (0444); flag = False; BucketFile file2 ("tBucketFile_tmp.data", True); try { file2.open(); } catch (const std::exception& x) { flag = True; cout << x.what() << endl; } AlwaysAssertExit (flag); flag = False; BucketFile file3 ("tBucketFile_tmp.data", False); file3.setRW(); try { file3.open(); } catch (const std::exception& x) { flag = True; cout << x.what() << endl; } AlwaysAssertExit (flag); flag = False; BucketFile file4 ("tBucketFile_tmp.data", False); file4.open(); try { file4.setRW(); } catch (const std::exception& x) { flag = True; cout << x.what() << endl; } AlwaysAssertExit (flag); // Make it writable again. rfile.setPermissions (0644); } casacore-3.7.1/casa/IO/test/tBucketMapped.cc000066400000000000000000000064071476623553700205670ustar00rootroot00000000000000//# tBucketMapped.cc: Test program for the BucketMapped class //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include // // Test program for the BucketMapped class // void a (Bool); void b (Bool); int main (int argc, const char*[]) { try { a (argc<2); b (argc<2); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } // Build a file. void a (Bool) { // Create the file. BucketFile file ("tBucketMapped_tmp.data", 0, True); file.open(); BucketMapped cache (&file, 512, 32768, 5); Int i; union { char buf[32768]; Int bufi[32768/4]; }; for (i=0; i<32768; i++) { buf[i] = 0; } cache.extend (100); for (i=0; i<100; i++) { bufi[0] = i+1; bufi[32760/4] = i+10; memcpy (cache.getrwBucket(i), buf, 32768); } for (i=0; i<100; i++) { const char* buf = cache.getBucket(i); if (*(const Int*)buf != i+1 || *(const Int*)(buf+32760) != i+10) { cout << "xError in bucket " << i << endl; cout << *(const Int*)buf <<' '<< *(const Int*)(buf+32760) < #include #include #include #include #include #include #include #include #include #include void checkLength (ByteIO& fio, uInt& curLength, uInt addLength) { curLength += addLength; AlwaysAssertExit (fio.length() == curLength); } static Bool valb=True; static Short vals=-3; static uShort valus=2; static Int vali=1000; static uInt valui=32768; static Int64 vall=-14793; static uInt64 valul=17; static float valf=1.2; static double vald=-3.14; void checkValues (ByteIO& fio, uShort incr) { fio.seek (0); uInt curLength = fio.length(); Bool resb; AlwaysAssertExit (fio.read (sizeof(Bool), &resb) == sizeof(Bool)); AlwaysAssertExit (resb == valb); Short ress; AlwaysAssertExit (fio.read (sizeof(Short), &ress) == sizeof(Short)); AlwaysAssertExit (ress == vals - incr); uShort resus; AlwaysAssertExit (fio.read (sizeof(uShort), &resus) == sizeof(uShort)); AlwaysAssertExit (resus == valus + incr); Int resi; AlwaysAssertExit (fio.read (sizeof(Int), &resi) == sizeof(Int)); AlwaysAssertExit (resi == vali); uInt resui; AlwaysAssertExit (fio.read (sizeof(uInt), &resui) == sizeof(uInt)); AlwaysAssertExit (resui == valui); Int64 resl; AlwaysAssertExit (fio.read (sizeof(Int64), &resl) == sizeof(Int64)); AlwaysAssertExit (resl == vall); uInt64 resul; AlwaysAssertExit (fio.read (sizeof(uInt64), &resul) == sizeof(uInt64)); AlwaysAssertExit (resul == valul); float resf; AlwaysAssertExit (fio.read (sizeof(float), &resf) == sizeof(float)); AlwaysAssertExit (resf == valf); double resd; AlwaysAssertExit (fio.read (sizeof(double), &resd) == sizeof(double)); AlwaysAssertExit (resd == vald); AlwaysAssertExit (fio.length() == curLength); } void doIt (ByteIO& fio) { uInt length = 0; AlwaysAssertExit (fio.length() == 0); fio.write (sizeof(Bool), &valb); checkLength (fio, length, sizeof(Bool)); fio.write (sizeof(Short), &vals); checkLength (fio, length, sizeof(Short)); fio.write (sizeof(uShort), &valus); checkLength (fio, length, sizeof(uShort)); fio.write (sizeof(Int), &vali); checkLength (fio, length, sizeof(Int)); fio.write (sizeof(uInt), &valui); checkLength (fio, length, sizeof(uInt)); fio.write (sizeof(Int64), &vall); checkLength (fio, length, sizeof(Int64)); fio.write (sizeof(uInt64), &valul); checkLength (fio, length, sizeof(uInt64)); fio.write (sizeof(float), &valf); checkLength (fio, length, sizeof(float)); fio.write (sizeof(double), &vald); checkLength (fio, length, sizeof(double)); checkValues (fio, 0); fio.seek (Int(sizeof(Bool))); AlwaysAssertExit (fio.length() == length); uShort incr = 100; Short vals1 = vals - incr; Short ress; fio.write (sizeof(Short), &vals1); fio.seek (Int(sizeof(Bool))); AlwaysAssertExit (fio.read (sizeof(Short), &ress) == sizeof(Short)); AlwaysAssertExit (ress == vals1); uShort valus1 = valus + incr; uShort resus; fio.write (sizeof(uShort), &valus1); fio.seek (Int(-sizeof(uShort)), ByteIO::Current); AlwaysAssertExit (fio.read (sizeof(uShort), &resus) == sizeof(uShort)); AlwaysAssertExit (resus == valus1); Int resi; AlwaysAssertExit (fio.read (sizeof(Int), &resi) == sizeof(Int)); AlwaysAssertExit (resi == vali); AlwaysAssertExit (fio.length() == length); checkValues (fio, incr); AlwaysAssertExit (fio.length() == length); Int64 offset = sizeof(Bool); incr = 100; vals1 = vals - incr; fio.pwrite (sizeof(Short), offset, &vals1); AlwaysAssertExit (fio.pread(sizeof(Short), offset, &ress) == sizeof(Short)); AlwaysAssertExit (ress == vals1); fio.seek (offset); AlwaysAssertExit (fio.read(sizeof(Short), &ress) == sizeof(Short)); AlwaysAssertExit (ress == vals1); offset += sizeof(Short); valus1 = valus + incr; fio.pwrite (sizeof(uShort), offset, &valus1); AlwaysAssertExit (fio.pread(sizeof(uShort), offset, &resus) == sizeof(uShort)); AlwaysAssertExit (resus == valus1); AlwaysAssertExit (fio.read(sizeof(uShort), &ress) == sizeof(uShort)); AlwaysAssertExit (resus == valus1); checkValues (fio, incr); } void checkReopen() { RegularFile rfile("tByteIO_tmp.data"); { RegularFileIO fio(rfile); checkValues (fio, 100); fio.reopenRW(); fio.seek (Int64(sizeof(Bool))); Short vals; fio.read (sizeof(Short), &vals); vals -= 50; fio.seek (Int64(sizeof(Bool))); fio.write (sizeof(Short), &vals); uShort valus; fio.read (sizeof(uShort), &valus); valus += 50; fio.seek (Int(-sizeof(uShort)), ByteIO::Current); fio.write (sizeof(uShort), &valus); checkValues (fio, 150); } rfile.setPermissions (0444); RegularFileIO fio2(rfile); checkValues (fio2, 150); Bool flag = False; try { fio2.reopenRW(); } catch (std::exception& x) { flag = True; } AlwaysAssertExit (flag); checkValues (fio2, 150); rfile.setPermissions (0644); } void testMemoryIO() { { uChar buf[10]; MemoryIO membuf (buf, sizeof(buf), ByteIO::New, 6); doIt (membuf); AlwaysAssertExit (membuf.getBuffer() != (const uChar*)&buf); Int64 length = membuf.length(); Int incr = 20; membuf.seek (incr, ByteIO::End); AlwaysAssertExit (membuf.length() == length+incr); checkValues (membuf, 100); char val; Int64 lincr = incr; membuf.seek (-lincr, ByteIO::End); for (Int i=0; i #include #include #include #include #include #include #include #include const Int nrOfTests = 1; int main () { { Bool testBool = True; Short testShort = -30; uShort testuShort = 10; Int testInt = -20; uInt testuInt = 80; Int64 testInt64 = -100000; uInt64 testuInt64 = 100000; Float testFloat = 18.45; Double testDouble = 23.987; Complex testComplex(2,3); DComplex testDComplex(2.5,3.8); Char testChar = 'A'; uChar testuChar = 'B'; String testString("This is a teststring"); auto regularFileIO = std::make_shared(Path("tByteSink_tmp.dat"), ByteIO::New); auto canonicalIO = std::make_shared(regularFileIO); ByteSink sink(canonicalIO); cout << sink.isReadable() << endl; cout << sink.isWritable() << endl; cout << sink.isSeekable() << endl; sink << testBool; sink << testShort; sink << testuShort; sink << testInt; sink << testuInt; sink << testInt64; sink << testuInt64; sink << testFloat; sink << testDouble; sink << testComplex; sink << testDComplex; sink << testChar; sink << testuChar; sink << testString; sink.write (nrOfTests, &testBool); sink.write (nrOfTests, &testShort); sink.write (nrOfTests, &testuShort); sink.write (nrOfTests, &testInt); sink.write (nrOfTests, &testuInt); sink.write (nrOfTests, &testInt64); sink.write (nrOfTests, &testuInt64); sink.write (nrOfTests, &testFloat); sink.write (nrOfTests, &testDouble); sink.write (nrOfTests, &testComplex); sink.write (nrOfTests, &testDComplex); sink.write (nrOfTests, &testChar); sink.write (nrOfTests, &testuChar); sink.write (nrOfTests, &testString); } { Bool testBool; Short testShort; uShort testuShort; Int testInt; uInt testuInt; Int64 testInt64; uInt64 testuInt64; Float testFloat; Double testDouble; Complex testComplex; DComplex testDComplex; Char testChar; uChar testuChar; String testString; auto regularFileIO = std::make_shared(Path("tByteSink_tmp.dat")); auto canonicalIO = std::make_shared(regularFileIO); ByteSource source(canonicalIO); source.seek(0); cout << source.isReadable() << endl; cout << source.isWritable() << endl; cout << source.isSeekable() << endl; source >> testBool; source >> testShort; source >> testuShort; source >> testInt; source >> testuInt; source >> testInt64; source >> testuInt64; source >> testFloat; source >> testDouble; source >> testComplex; source >> testDComplex; source >> testChar; source >> testuChar; source >> testString; cout << testBool << endl; cout << testShort << endl; cout << testuShort << endl; cout << testInt << endl; cout << testuInt << endl; cout << testInt64 << endl; cout << testuInt64 << endl; cout << testFloat << endl; cout << testDouble << endl; cout << testComplex << endl; cout << testDComplex << endl; cout << testChar << endl; cout << testuChar << endl; cout << testString << endl; source.read (nrOfTests, &testBool); source.read (nrOfTests, &testShort); source.read (nrOfTests, &testuShort); source.read (nrOfTests, &testInt); source.read (nrOfTests, &testuInt); source.read (nrOfTests, &testInt64); source.read (nrOfTests, &testuInt64); source.read (nrOfTests, &testFloat); source.read (nrOfTests, &testDouble); source.read (nrOfTests, &testComplex); source.read (nrOfTests, &testDComplex); source.read (nrOfTests, &testChar); source.read (nrOfTests, &testuChar); source.read (nrOfTests, &testString); cout << testBool << endl; cout << testShort << endl; cout << testuShort << endl; cout << testInt << endl; cout << testuInt << endl; cout << testInt64 << endl; cout << testuInt64 << endl; cout << testFloat << endl; cout << testDouble << endl; cout << testComplex << endl; cout << testDComplex << endl; cout << testChar << endl; cout << testuChar << endl; cout << testString << endl; } return 0; } casacore-3.7.1/casa/IO/test/tByteSink.out000066400000000000000000000002661476623553700201720ustar00rootroot000000000000001 1 1 1 0 1 1 -30 10 -20 80 -100000 100000 18.45 23.987 (2,3) (2.5,3.8) A B This is a teststring 1 -30 10 -20 80 -100000 100000 18.45 23.987 (2,3) (2.5,3.8) A B This is a teststring casacore-3.7.1/casa/IO/test/tByteSinkSource.cc000066400000000000000000000143031476623553700211260ustar00rootroot00000000000000//# tByteSinkSource.cc: Test program for class ByteSinkSource //# Copyright (C) 1996,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include const Int nrOfTests = 1; int main () { { RegularFileIO regularFileIO (Path("tByteSinkSource_tmp.dat"), ByteIO::New); } { auto regularFileIO = std::make_shared(Path("tByteSinkSource_tmp.dat1"), ByteIO::New); auto canonicalIO = std::make_shared(regularFileIO); ByteSinkSource sinkSource(canonicalIO); ByteSinkSource sinkSource1(sinkSource); sinkSource1 = sinkSource; ByteSinkSource sinkSource2(sinkSource.typeIO()); } { Bool testBool = True; Short testShort = -30; uShort testuShort = 10; Int testInt = -20; uInt testuInt = 80; Int64 testInt64 = -100000; uInt64 testuInt64 = 100000; Float testFloat = 18.45; Double testDouble = 23.987; Complex testComplex(2,3); DComplex testDComplex(2.5,3.8); Char testChar = 'A'; uChar testuChar = 'B'; String testString("This is a teststring"); auto regularFileIO = std::make_shared(Path("tByteSinkSource_tmp.dat1"), ByteIO::New); auto canonicalIO = std::make_shared(regularFileIO); ByteSinkSource sinkSource(canonicalIO); cout << sinkSource.isReadable() << endl; cout << sinkSource.isWritable() << endl; cout << sinkSource.isSeekable() << endl; sinkSource << testBool; sinkSource << testShort; sinkSource << testuShort; sinkSource << testInt; sinkSource << testuInt; sinkSource << testInt64; sinkSource << testuInt64; sinkSource << testFloat; sinkSource << testDouble; sinkSource << testComplex; sinkSource << testDComplex; sinkSource << testChar; sinkSource << testuChar; sinkSource << testString; sinkSource.write (nrOfTests, &testBool); sinkSource.write (nrOfTests, &testShort); sinkSource.write (nrOfTests, &testuShort); sinkSource.write (nrOfTests, &testInt); sinkSource.write (nrOfTests, &testuInt); sinkSource.write (nrOfTests, &testInt64); sinkSource.write (nrOfTests, &testuInt64); sinkSource.write (nrOfTests, &testFloat); sinkSource.write (nrOfTests, &testDouble); sinkSource.write (nrOfTests, &testComplex); sinkSource.write (nrOfTests, &testDComplex); sinkSource.write (nrOfTests, &testChar); sinkSource.write (nrOfTests, &testuChar); sinkSource.write (nrOfTests, &testString); sinkSource.seek (0); sinkSource >> testBool; sinkSource >> testShort; sinkSource >> testuShort; sinkSource >> testInt; sinkSource >> testuInt; sinkSource >> testInt64; sinkSource >> testuInt64; sinkSource >> testFloat; sinkSource >> testDouble; sinkSource >> testComplex; sinkSource >> testDComplex; sinkSource >> testChar; sinkSource >> testuChar; sinkSource >> testString; cout << testBool << endl; cout << testShort << endl; cout << testuShort << endl; cout << testInt << endl; cout << testuInt << endl; cout << testInt64 << endl; cout << testuInt64 << endl; cout << testFloat << endl; cout << testDouble << endl; cout << testComplex << endl; cout << testDComplex << endl; cout << testChar << endl; cout << testuChar << endl; cout << testString << endl; sinkSource.read (nrOfTests, &testBool); sinkSource.read (nrOfTests, &testShort); sinkSource.read (nrOfTests, &testuShort); sinkSource.read (nrOfTests, &testInt); sinkSource.read (nrOfTests, &testuInt); sinkSource.read (nrOfTests, &testInt64); sinkSource.read (nrOfTests, &testuInt64); sinkSource.read (nrOfTests, &testFloat); sinkSource.read (nrOfTests, &testDouble); sinkSource.read (nrOfTests, &testComplex); sinkSource.read (nrOfTests, &testDComplex); sinkSource.read (nrOfTests, &testChar); sinkSource.read (nrOfTests, &testuChar); sinkSource.read (nrOfTests, &testString); cout << testBool << endl; cout << testShort << endl; cout << testuShort << endl; cout << testInt << endl; cout << testuInt << endl; cout << testInt64 << endl; cout << testuInt64 << endl; cout << testFloat << endl; cout << testDouble << endl; cout << testComplex << endl; cout << testDComplex << endl; cout << testChar << endl; cout << testuChar << endl; cout << testString << endl; } { auto regularFileIO = std::make_shared(Path("tByteSinkSource_tmp.dat1"), ByteIO::Update); auto canonicalIO = std::make_shared(regularFileIO, 5); ByteSinkSource sinkSource(canonicalIO); String testString; sinkSource << "This is a teststring"; sinkSource.seek (0); sinkSource >> testString; cout << testString << endl; } { try { RegularFileIO regularFileIO (Path("tByteSinkSource_tmp.dat"), ByteIO::NewNoReplace); } catch (std::exception& x) { cout << x.what () << endl; } } return 0; } casacore-3.7.1/casa/IO/test/tByteSinkSource.out000066400000000000000000000004041476623553700213450ustar00rootroot000000000000001 1 1 1 -30 10 -20 80 -100000 100000 18.45 23.987 (2,3) (2.5,3.8) A B This is a teststring 1 -30 10 -20 80 -100000 100000 18.45 23.987 (2,3) (2.5,3.8) A B This is a teststring This is a teststring RegularFileIO: new file tByteSinkSource_tmp.dat already exists casacore-3.7.1/casa/IO/test/tFileIO.cc000066400000000000000000000133701476623553700173270ustar00rootroot00000000000000//# tFileIO.cc: Test program for performance of file IO //# Copyright (C) 1997,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include int main (int argc, const char* argv[]) { int nr = 100; if (argc > 1) { istringstream istr(argv[1]); istr >> nr; } int leng = 1024; if (argc > 2) { istringstream istr(argv[2]); istr >> leng; } int size = 0; if (argc > 3) { istringstream istr(argv[3]); istr >> size; } int seek = 0; if (argc > 4) { istringstream istr(argv[4]); istr >> seek; } cout << "tFileIO nrrec=" << nr << " reclength=" << leng << " buffersize=" << size << " seek=" << seek << endl; char* buf = new char[leng]; int i; for (i=0; i #include #include #include #include #include #include void check (int bufSize, const char* buf) { FileUnbufferedIO fio("tFileUnbufferedIO_tmp.dat", ByteIO::Old); fio.seek (0); std::vector buf1(bufSize); for (int j=0; j<2000/bufSize; j++) { AlwaysAssertExit (fio.read (bufSize, buf1.data(), True) == bufSize); for (int i=0; i #include #include #include #include #include #include void check (int fd, int bufSize, const char* buf) { FilebufIO fio(fd, bufSize); fio.seek (0); char* buf1 = new char[bufSize]; for (int j=0; j<2000/bufSize; j++) { AlwaysAssertExit (fio.read (bufSize, buf1, True) == bufSize); for (int i=0; i #include #include #include #include #include #include #include int main (int argc, const char* argv[]) { try { int mode = 0; if (argc > 1) { if (String(argv[1]) == String("w")) { mode = 1; } else if (String(argv[1]) == String("r")) { mode = -1; } } int nr = 100; if (argc > 2) { istringstream istr(argv[2]); istr >> nr; } int leng = 1024; if (argc > 3) { istringstream istr(argv[3]); istr >> leng; } int incr = 0; Bool setpos = False; if (argc > 4) { istringstream istr(argv[4]); istr >> incr; setpos = True; } leng /= sizeof(int); int tleng = leng * sizeof(int); cout << "tLargeFileIO mode=" << mode << " nrrec=" << nr << " reclength=" << tleng << " incr=" << incr << " setpos=" << setpos << endl; int* buf = new int[leng]; int i; for (i=0; i= 0) { Timer timer; int fd = FiledesIO::create("tLargeFileIO_tmp.dat2"); FiledesIO file2 (fd, ""); timer.mark(); for (i=0; i #include #include #include #include #include #include void check (int fd, int bufSize, const char* buf) { LargeFilebufIO fio(fd, bufSize); fio.seek (0); char* buf1 = new char[bufSize]; for (int j=0; j<2000/bufSize; j++) { AlwaysAssertExit (fio.read (bufSize, buf1, True) == bufSize); for (int i=0; i #include #include #include #include #include #include #include #include #include #include #include void doIt (const String& name, double interval) { cout << "Choose between 2 locknrs (i.e. parts of file to lock)." << endl; cout << "The first one is a permanent LockFile object." << endl; cout << "The second one is deleted when leaving the inner loop" << endl; cout << "and can be used to check what happens if multiple LockFile" <> op; //# Create 2 lock objects with given inspection interval. //# Let them start at a different offset in the file. LockFile lock1(name, interval, False, True, True, 0, op==1, op==0); LockFile* lockp; while (True) { cout << "locknr (1,2 0=end): "; cin >> lnr; if (lnr <= 0) break; if (lnr == 1) { lockp = &lock1; } else { cout << "Locking type (0=no, 1=permanent, other=normal):"; cin >> op; lockp = new LockFile (name, interval, False, True, True, 1, op==1, op==0); } while (True) { cout << "1=rlock, 2=wlock, 3=rlockw, 4=wlockw, 5=unlock, " "6=status, 7=speed, 8=open/close 9=show, else=end: "; cin >> op; if (op == 1 || op == 3) { uInt nattempt = 1; if (op == 3) { cout << "nattempts :"; cin >> nattempt; } if (lockp->acquire (FileLocker::Read, nattempt)) { cout << " Read lock acquired" << endl; }else{ cout << " Already locked by another process" << endl; } } else if (op == 2 || op == 4) { uInt nattempt = 1; if (op == 4) { cout << "nattempts :"; cin >> nattempt; } if (lockp->acquire (FileLocker::Write, nattempt)) { cout << " Write lock acquired" << endl; }else{ cout << " Already locked by another process" << endl; } } else if (op == 5) { if (! lockp->release()) { cout << " Lock could not be released" << endl; } } else if (op == 6) { cout << " Last errno " << lockp->lastError() << ": " << lockp->lastMessage() << endl; cout << " Request flag is " << lockp->inspect() << endl; cout << " canReadLock=" << lockp->canLock (FileLocker::Read); cout << ", canWriteLock=" << lockp->canLock (FileLocker::Write); cout << ", hasReadLock=" << lockp->hasLock (FileLocker::Read); cout << ", hasWriteLock=" << lockp->hasLock (FileLocker::Write); cout << ", isMultiUsed = " << lockp->isMultiUsed(); cout << endl; } else if (op == 7) { Timer timer; for (int i=0; i<500; i++) { lockp->acquire (FileLocker::Write, 1); } timer.show ("Acquiring 500 locks:"); } else if (op == 8) { RegularFileIO tmp(name); } else if (op == 9) { uInt pid; Bool perm; uInt res = LockFile::showLock (pid, perm, name); cout << "result=" << res << ", pid=" << pid << ", permlocked=" << perm << endl; } else { break; } } if (lnr == 2) { delete lockp; } } } void doTest() { LockFile lock ("tLockFile_tmp.data", 0, True); AlwaysAssertExit (! lock.hasLock (FileLocker::Read)); AlwaysAssertExit (! lock.hasLock (FileLocker::Write)); MemoryIO memio; //# Acquire a lock and get information. No request is pending. AlwaysAssertExit (lock.acquire (memio)); AlwaysAssertExit (memio.length() == 0); AlwaysAssertExit (! lock.inspect()); AlwaysAssertExit (lock.hasLock (FileLocker::Read)); AlwaysAssertExit (lock.hasLock (FileLocker::Write)); cout << lock.canLock() << endl; //# Release the lock and reacquire it. AlwaysAssertExit (lock.release()); AlwaysAssertExit (! lock.hasLock (FileLocker::Read)); AlwaysAssertExit (! lock.hasLock (FileLocker::Write)); AlwaysAssertExit (lock.acquire()); AlwaysAssertExit (lock.acquire (memio)); AlwaysAssertExit (memio.length() == 0); //# Release the lock and reacquire it. AlwaysAssertExit (lock.release (memio)); AlwaysAssertExit (lock.acquire (memio)); AlwaysAssertExit (memio.length() == 0); //# Store information (<1024 bytes) in the memio object. uInt value = 10; memio.write (sizeof(uInt), &value); AlwaysAssertExit (lock.release (memio)); memio.seek (0); //# Store different value in memio to be sure acquire gets it right. value = 20; memio.write (sizeof(value), &value); //# Get lock and read info. lock.acquire (memio); AlwaysAssertExit (memio.length() == sizeof(value)); memio.read (sizeof(value), &value); AlwaysAssertExit (value == 10); //# Write very long info. Int val[10000]; Int i; for (i=0; i<10000; i++) { val[i] = i-5000; } value = 10000; memio.write (sizeof(value), &value); memio.write (sizeof(val), val); lock.release (memio); //# Acquire lock and get info. { MemoryIO memio2; uInt n; Int v[10000]; lock.acquire (memio2); memio2.read (sizeof(n), &n); AlwaysAssertExit (n == 10); memio2.read (sizeof(n), &n); AlwaysAssertExit (n == 10000); memio2.read (sizeof(v), v); for (i=0; i<10000; i++) { AlwaysAssertExit (v[i] == i-5000); } } } int main (int argc, const char* argv[]) { try { // If no argument given, tell how to run. // This option is needed to pass the standard runtests target. if (argc > 1) { double interval = 5; if (argc > 2) { istringstream istr(argv[2]); istr >> interval; } doIt (argv[1], interval); }else{ doTest(); cout << "Run as: tLockFile [inspectionInterval]" << endl; cout << "for a manual control of acquiring and releasing locks." << endl; cout << "Default inspection interval is 5 seconds." << endl; } } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; // exit with success status } casacore-3.7.1/casa/IO/test/tMFFileIO.cc000066400000000000000000000151511476623553700175510ustar00rootroot00000000000000//# tMFFileIO.cc: Test program for class MFFileIO //# Copyright (C) 2014 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include using namespace casacore; using namespace std; void showMultiFile (MultiFileBase& mfile) { cout << mfile.fileName() << ' ' << mfile.blockSize() << ' ' << mfile.nfile() << ' ' << mfile.nblock() << ' ' << mfile.freeBlocks() << endl; } void showMFFile (MFFileIO& mfile) { cout << mfile.fileName() << ' ' << mfile.length() << ' ' << mfile.seek(0, ByteIO::Current) << endl; } void makeFile (const std::shared_ptr& mfile) { MFFileIO mff(mfile, "mff1", ByteIO::New); showMultiFile(*mfile); showMFFile (mff); } void writeFiles1 (const std::shared_ptr& mfile) { MFFileIO mff(mfile, "mff1", ByteIO::Update); Vector buf(120); indgen(buf); mff.write (960, buf.data()); buf += Int64(120); mff.write (960, buf.data()); buf += Int64(120); mff.write (80, buf.data()); showMFFile (mff); cout << mfile->info() << endl; } void checkFiles1 (const std::shared_ptr& mfile) { MFFileIO mff(mfile, "mff1", ByteIO::Update); Vector bufcheck(250); Vector buf(250); indgen(bufcheck); mff.read (2000, buf.data()); AlwaysAssertExit (allEQ(buf, bufcheck)); showMFFile (mff); } void testWriteNested (const std::shared_ptr& parent) { cout << "testWriteNested ..." << endl; std::shared_ptr mfile1 (parent->makeNested (parent, "mfile1", ByteIO::New, 500)); MFFileIO mff11(mfile1, "mff11", ByteIO::New); Vector buf(300); indgen(buf); mff11.write (8*150, buf.data()); MFFileIO mff12(mfile1, "mff12", ByteIO::New); buf += Int64(150); mff12.write (8*300, buf.data()); std::shared_ptr mfile2 (parent->makeNested (parent, "mfile2", ByteIO::New, 500)); MFFileIO mff21(mfile2, "mff21", ByteIO::New); mff21.write (8*250, buf.data()); mff11.write (8*150, buf.data()); showMultiFile (*mfile1); showMultiFile (*mfile2); showMultiFile (*parent); showMFFile (mff11); showMFFile (mff12); showMFFile (mff21); } void testReadNested (const std::shared_ptr& parent) { cout<<"testReadNested ..."< mfile1 (parent->makeNested (parent, "mfile1", ByteIO::Old, 0)); MFFileIO mff11(mfile1, "mff11"); Vector bufcheck(300); Vector buf(300); indgen(bufcheck); mff11.read (8*300, buf.data()); AlwaysAssertExit (allEQ(buf, bufcheck)); MFFileIO mff12(mfile1, "mff12"); bufcheck += Int64(150); mff12.read (8*300, buf.data()); AlwaysAssertExit (allEQ(buf, bufcheck)); std::shared_ptr mfile2 (parent->makeNested (parent, "mfile2", ByteIO::Old, 0)); MFFileIO mff21(mfile2, "mff21"); Int64 nread = mff21.read (8*300, buf.data(), False); // only 250 were written AlwaysAssertExit (nread == 8*250); AlwaysAssertExit (allEQ(buf, bufcheck)); // last 50 elements not overwritten } void testTruncate (const std::shared_ptr& parent) { cout << "testTruncate ..." << endl; std::shared_ptr mfile1 (parent->makeNested (parent, "mfile1", ByteIO::Update, 0)); MFFileIO mff11(mfile1, "mff11", ByteIO::Update); mff11.truncate (8*120); AlwaysAssertExit (mff11.length() == 8*120); Vector bufcheck(120); Vector buf(300); indgen(bufcheck); Int64 nread = mff11.read (8*300, buf.data(), False); // only 120 are left AlwaysAssertExit (nread == 8*120); AlwaysAssertExit (allEQ(buf(Slice(0,120)), bufcheck)); mfile1->flush(); parent->flush(); showMultiFile (*mfile1); showMultiFile (*parent); showMFFile (mff11); } int main() { try { { cout << "Test using MultiFile ..." << endl; std::shared_ptr mfile (new MultiFile ("tMFFileIO_tmp.dat1", ByteIO::New, 512)); makeFile(mfile); writeFiles1(mfile); checkFiles1(mfile); // Test nested with parent still open and reopen of parent. { std::shared_ptr parent (new MultiFile ("tMFFileIO_tmp.dat3", ByteIO::New, 1024)); testWriteNested(parent); testReadNested(parent); } std::shared_ptr parent (new MultiFile ("tMFFileIO_tmp.dat3", ByteIO::Update, 1024)); testTruncate(parent); } if (HDF5Object::hasHDF5Support()) { cout << "Test using MultiHDF5 ..." << endl; std::shared_ptr mfile (new MultiHDF5 ("tMFFileIO_tmp.dat2", ByteIO::New, 512)); makeFile(mfile); writeFiles1(mfile); checkFiles1(mfile); // Test nested with parent still open and reopen of parent. { std::shared_ptr parent (new MultiHDF5 ("tMFFileIO_tmp.dat4", ByteIO::New, 1024)); testWriteNested(parent); showMultiFile (*parent); testReadNested(parent); } std::shared_ptr parent (new MultiHDF5 ("tMFFileIO_tmp.dat4", ByteIO::Update)); testTruncate(parent); } } catch (std::exception& x) { cout << "Unexpected exception: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/casa/IO/test/tMMapIO.cc000066400000000000000000000055231476623553700173030ustar00rootroot00000000000000//# tMMapIO.cc: Test program for class MMapIO //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include using namespace casacore; void check (const String& fileName, int bufSize, const char* buf) { MMapIO fio((RegularFile(fileName))); fio.seek (0); char* buf1 = new char[bufSize]; for (int j=0; j<2000/bufSize; j++) { AlwaysAssertExit (fio.read (bufSize, buf1, True) == bufSize); for (int i=0; i #include #include #include #include #include #include #include #include #include #include #include //# needed for memcpy #include int main (int argc, const char* argv[]) { try { int mode = 0; if (argc > 1) { if (String(argv[1]) == String("w")) { mode = 1; } else if (String(argv[1]) == String("r")) { mode = -1; } } int nrch = 1; if (argc > 2) { istringstream istr(argv[2]); istr >> nrch; } int nr = 100; if (argc > 3) { istringstream istr(argv[3]); istr >> nr; } int leng = 1024; if (argc > 4) { istringstream istr(argv[4]); istr >> leng; } leng /= sizeof(int); int tleng = leng * sizeof(int); cout << "tMappedIO mode=" << mode << " nrchunk=" << nrch << " nrrec=" << nr << " reclength=" << tleng << endl; int* buf = new int[leng]; int i; for (i=0; i 100*1024*1024) { cout << "Chunk length " << nr*tleng << " too large (> 100 MB)" << endl; return 1; } Int64 size = nr*tleng; Int64 pagesz = getpagesize(); if (mode >= 0) { Timer timer; int fd = FiledesIO::create("tMappedIO_tmp.dat2"); timer.mark(); ::traceLSEEK(fd, size*nrch-1, SEEK_SET); AlwaysAssert (::traceWRITE (fd, buf, 1) == 1, AipsError); timer.show("write"); // Map chunk by chunk. for (int k=0; k #include #include #include #include #include #include #include #include #include #include #include using namespace casacore; using namespace std; void makeFile (Int64 blockSize, Bool useODirect, Bool useCRC) { MultiFile mfile("tMultiFile_tmp.dat", ByteIO::New, blockSize, useODirect, useCRC); AlwaysAssertExit (mfile.isWritable()); mfile.show (cout); } void readFile (const String& name = "tMultiFile_tmp.dat") { MultiFile mfile(name, ByteIO::Old); AlwaysAssertExit (! mfile.isWritable()); mfile.show (cout); for (uInt i=0; i buf(128); indgen(buf); mfile.write (id0, buf.data(), 1024, 0); buf += Int64(128); mfile.write (id2, buf.data(), 1024, 0); buf += Int64(128); mfile.write (id0, buf.data(), 1024, 1024); buf += Int64(128); mfile.write (id0, buf.data(), 1024, 2048); buf += Int64(128); mfile.write (id1, buf.data(), 1024, 1024); // also creates block at offset 0 buf += Int64(128); mfile.write (id2, buf.data(), 1024, 1024); cout << mfile.info() << endl; mfile.closeFile (id0); mfile.closeFile (id1); mfile.closeFile (id2); } void checkFiles1 (Bool do1=True) { MultiFile mfile("tMultiFile_tmp.dat", ByteIO::Old); Int id0 = mfile.openFile ("file0"); Int id2 = mfile.openFile ("file2"); Vector buf1(128), buf(128),buff(3*128); indgen(buf1); mfile.read (id0, buf.data(), 1024, 0); if (do1) { AlwaysAssertExit (allEQ(buf, buf1)); } else { AlwaysAssertExit(buf[0]==buf1[0] && allEQ(buf(Slice(1,127)), buf1(Slice(0,127)))); } buf1 += Int64(128); mfile.read (id2, buf.data(), 1024, 0); AlwaysAssertExit (allEQ(buf, buf1)); buf1 += Int64(128); mfile.read (id0, buf.data(), 1024, 1024); AlwaysAssertExit (allEQ(buf, buf1)); buf1 += Int64(128); mfile.read (id0, buf.data(), 1024, 2048); AlwaysAssertExit (allEQ(buf, buf1)); buf1 += Int64(128); if (do1) { Int id1 = mfile.openFile ("file1"); mfile.read (id1, buf.data(), 1024, 1024); AlwaysAssertExit (allEQ(buf, buf1)); mfile.closeFile (id1); } buf1 += Int64(128); mfile.read (id2, buf.data(), 1024, 1024); AlwaysAssertExit (allEQ(buf, buf1)); // Check a single read. if (do1) { indgen(buf1); mfile.read (id0, buff.data(), 3072, 0); AlwaysAssertExit (allEQ(buff(Slice(0,128)), buf1)); AlwaysAssertExit (allEQ(buff(Slice(128,128)), buf1+Int64(256))); AlwaysAssertExit (allEQ(buff(Slice(256,128)), buf1+Int64(384))); // Read partial blocks. mfile.read (id0, buff.data(), 3072-24, 8); AlwaysAssertExit (allEQ(buff(Slice(0,127)), buf1(Slice(1,127)))); AlwaysAssertExit (allEQ(buff(Slice(127,128)), buf1+Int64(256))); AlwaysAssertExit (allEQ(buff(Slice(255,126)), buf1(Slice(0,126))+Int64(384))); // Check that remainder of receiving buffer is not overwritten by read AlwaysAssertExit (buff[380]==509 && buff[381]==509); } mfile.closeFile (id0); mfile.closeFile (id2); } void deleteFile() { cout <<"test deleteFile"< buf(128), buf1(128); indgen(buf); mfile.write (id0, buf.data(), 1016, 8); mfile.read (id0, buf1.data(), 1024, 0); AlwaysAssertExit(buf[0]==buf1[0] && allEQ(buf1(Slice(1,127)), buf(Slice(0,127)))); mfile.write (id2, buf.data(), 16, 2048); cout << mfile.info() << endl; mfile.closeFile (id0); mfile.closeFile (id2); } void checkFiles2 (const String& name = "tMultiFile_tmp.dat") { checkFiles1(False); MultiFile mfile(name, ByteIO::Old); Int id2 = mfile.openFile ("file2"); Vector buf1(2), buf(2); indgen(buf1); mfile.read (id2, buf.data(), 16, 2048); AlwaysAssertExit (allEQ(buf, buf1)); mfile.closeFile (id2); } void doTest (Int64 blockSize, Bool useODirect=False, Bool useCRC=False) { cout << "MultiFile test with blockSize=" << blockSize << ", useCRC=" << useCRC << ", useODirect=" << useODirect << endl; makeFile (blockSize, useODirect, useCRC); readFile(); addFiles(); readFile(); writeFiles1(); readFile(); checkFiles1(); deleteFile(); writeFiles2(); readFile(); checkFiles2(); cout << endl; } void timeExact() { MultiFile mfile("tMultiFile_tmp.dat", ByteIO::New, 32768); Int id = mfile.createFile ("file0"); Vector buf(32768/8, 0); for (Int j=0; j<2; ++j) { Timer timer; for (uInt i=0; i<1000; ++i) { mfile.write (id, buf.data(), 32768, i*32768); } mfile.fsync(); timer.show ("exact "); } mfile.closeFile (id); } void timeDouble() { MultiFile mfile("tMultiFile_tmp.dat", ByteIO::New, 16384); Int id = mfile.createFile ("file0"); Vector buf(32768/8, 0); for (Int j=0; j<2; ++j) { Timer timer; for (uInt i=0; i<1000; ++i) { mfile.write (id, buf.data(), 32768, i*32768); } mfile.fsync(); timer.show ("double"); } mfile.closeFile (id); } void timePartly() { MultiFile mfile("tMultiFile_tmp.dat", ByteIO::New, 32768); Int id = mfile.createFile ("file0"); Vector buf(16384/8, 0); for (Int j=0; j<2; ++j) { Timer timer; for (uInt i=0; i<2000; ++i) { mfile.write (id, buf.data(), 16384, i*16384); } mfile.fsync(); timer.show ("partly"); } mfile.closeFile (id); } void timePack (Int64 incr) { std::vector bl(1000000); for (size_t i=0; i pck = MultiFile::packIndex (bl); timer.show("pack "); Timer timer2; std::vector orig = MultiFile::packIndex (pck); timer2.show ("unpack"); } void timeMove1() { Vector buf1(4, 3); Vector buf2(4, 0); Timer timer; for (uInt i=0; i<5000000; ++i) { memcpy (buf2.data(), buf1.data(), 8*4); } timer.show ("move1 "); } typedef void* moveFunc(void*, const void*, size_t); void* mymemcpy (void* to, const void* from, size_t n) { return memcpy (to, from, n); } void timeMove2 (moveFunc func) { Vector buf1(4, 3); Vector buf2(4, 0); Timer timer; for (uInt i=0; i<5000000; ++i) { func (buf2.data(), buf1.data(), 8*4); } timer.show ("move2 "); } void timeMove3() { Vector buf1(4, 3); Vector buf2(4, 0); Timer timer; for (uInt i=0; i<5000000; ++i) { for (uInt j=0;j<4; ++j) { buf2.data()[j] = buf1.data()[j]; } } timer.show ("move3 "); } void testV1() { cout << "Test a version 1 MultiFile ..." << endl; readFile("tMultiFile.in"); checkFiles2("tMultiFile.in"); cout << endl; } void testNested (Int64 blockSizeParent, Int64 blockSizeChild) { cout << "Test nested with block sizes " << blockSizeParent << " and " << blockSizeChild << endl; { MultiFile* parentmf = new MultiFile ("tMultiFile_tmp.nest", ByteIO::New, blockSizeParent); std::shared_ptr parent (parentmf); MultiFile child("tnested", parent, ByteIO::New, blockSizeChild); Int fidParent = parent->createFile ("file0"); Int fidChild = child.createFile ("file0"); AlwaysAssertExit (fidParent == 1 && fidChild == 0); parentmf->show (cout); child.show (cout); child.closeFile (fidChild); parent->closeFile (fidParent); } { MultiFile* parentmf = new MultiFile ("tMultiFile_tmp.nest", ByteIO::Old); std::shared_ptr parent (parentmf); MultiFile child("tnested", parent, ByteIO::Old); Int fidParent = parent->openFile ("file0"); Int fidChild = child.openFile ("file0"); AlwaysAssertExit (fidParent == 1 && fidChild == 0); parentmf->show (cout); child.show (cout); child.closeFile (fidChild); parent->closeFile (fidParent); } cout << endl; } void testTruncate() { cout << "Test truncation of MultiFile (also nested) ..." << endl; MultiFile* mfilePtr = new MultiFile("tMultiFile_tmp.dat", ByteIO::New, 256); std::shared_ptr mfile(mfilePtr); AlwaysAssertExit (mfile->isWritable()); // Add a file. MFFileIO file1 (mfile, "file1", ByteIO::New); // Write some blocks; Vector vec(64); indgen (vec); for (uInt i=0; i<10; ++i) { file1.write (256, vec.data()); vec += 64; } mfilePtr->show (cout); AlwaysAssertExit (file1.length() == 2560); file1.truncate (1000); mfilePtr->show (cout); AlwaysAssertExit (file1.length() == 1000); cout<< mfile->info()[0].blockNrs.size() <info()[0].blockNrs.size() == 4); AlwaysAssertExit (mfile->freeBlocks().size() == 0); } void doPackTest (const std::vector& bl, const std::vector& exp) { std::vector pck = MultiFile::packIndex (bl); AlwaysAssertExit (pck.size() <= bl.size()); AlwaysAssertExit (pck.size() == exp.size()); for (size_t i=0; i orig = MultiFile::unpackIndex (pck); AlwaysAssertExit (orig.size() == bl.size()); for (size_t i=0; i(), std::vector()); doPackTest (std::vector({1,2,3,4,5}), std::vector({1,-4})); doPackTest (std::vector({1,3,5,7,9}), std::vector({1,3,5,7,9})); doPackTest (std::vector({1,3,4,7,8}), std::vector({1,3,-1,7,-1})); // Do some MultiFile tests. doTest (1024); // no extra header file doTest (128); // requires extra header file doTest (4096, True, True); // with O_DIRECT (if possible) and CRC // Test if a version 1 file can still be read. testV1(); // Test if nested MultiFiles work fine. testNested (512, 0); // Test file truncation. testTruncate(); // Do some timings. // Exclude timings from checked output. cout << ">>>" << endl; timePack(1); timePack(2); timeMove1(); timeMove2(memcpy); timeMove2(mymemcpy); timeMove3(); timeExact(); timeDouble(); timePartly(); cout << "<<<" << endl; } catch (std::exception& x) { cout << "Unexpected exception: " << x.what() << endl; return 1; } cout << "tMultiFile ended OK" << endl; return 0; } casacore-3.7.1/casa/IO/test/tMultiFile.in000066400000000000000000000162001476623553700201260ustar00rootroot00000000000000€¾¾¾¾t MultiFile9ÊBlockfile0ÕBlock  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿcasacore-3.7.1/casa/IO/test/tMultiFile.in_hdrext000066400000000000000000000010201476623553700214760ustar00rootroot00000000000000 Blockfile2Block 12345678!Block0/.-,+*)('&%$#"casacore-3.7.1/casa/IO/test/tMultiFile.out000066400000000000000000000123671476623553700203410ustar00rootroot00000000000000MultiFile test with blockSize=1024, useCRC=0, useODirect=0 tMultiFile_tmp.dat: blocksize=1024 nfile=0 nblock=1 nCRC=0 ncont=0,0 cont=0 [] free=[] tMultiFile_tmp.dat: blocksize=1024 nfile=0 nblock=1 nCRC=0 ncont=0,0 cont=0 [] free=[] tMultiFile_tmp.dat: blocksize=1024 nfile=3 nblock=1 nCRC=0 ncont=0,0 cont=0 [] free=[] tMultiFile_tmp.dat: blocksize=1024 nfile=3 nblock=1 nCRC=0 ncont=0,0 cont=0 [] free=[] file0 0 file1 1 file2 2 [file0 [1,3,4] fsize=3072 cur=-1 nest=0 dirty=0 ,file1 [5,6] fsize=2048 cur=-1 nest=0 dirty=0 ,file2 [2,7] fsize=2048 cur=-1 nest=0 dirty=0 ] tMultiFile_tmp.dat: blocksize=1024 nfile=3 nblock=8 nCRC=0 ncont=0,0 cont=0 [] free=[] file0 0 file1 1 file2 2 test deleteFile tMultiFile_tmp.dat: blocksize=1024 nfile=2 nblock=8 nCRC=0 ncont=0,0 cont=0 [] free=[6,5] file0 0 file1 -1 file2 2 [file0 [1,3,4] fsize=3072 cur=0 nest=0 dirty=1 , [] fsize=0 cur=-1 nest=0 dirty=0 ,file2 [2,7,5] fsize=2064 cur=2 nest=0 dirty=1 ] tMultiFile_tmp.dat: blocksize=1024 nfile=2 nblock=8 nCRC=0 ncont=0,0 cont=0 [] free=[6] file0 0 file1 -1 file2 2 MultiFile test with blockSize=128, useCRC=0, useODirect=0 tMultiFile_tmp.dat: blocksize=128 nfile=0 nblock=1 nCRC=0 ncont=0,0 cont=0 [] free=[] tMultiFile_tmp.dat: blocksize=128 nfile=0 nblock=2 nCRC=0 ncont=0,1 cont=1 [1] free=[] tMultiFile_tmp.dat: blocksize=128 nfile=3 nblock=2 nCRC=0 ncont=0,1 cont=1 [1] free=[] tMultiFile_tmp.dat: blocksize=128 nfile=3 nblock=3 nCRC=0 ncont=1,1 cont=0 [2] free=[] file0 0 file1 1 file2 2 [file0 [3,4,5,6,7,8,9,10,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34] fsize=3072 cur=-1 nest=0 dirty=0 ,file1 [35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50] fsize=2048 cur=-1 nest=0 dirty=0 ,file2 [11,12,13,14,15,16,17,18,51,52,53,54,55,56,57,58] fsize=2048 cur=-1 nest=0 dirty=0 ] tMultiFile_tmp.dat: blocksize=128 nfile=3 nblock=60 nCRC=0 ncont=1,2 cont=1 [1,59] free=[] file0 0 file1 1 file2 2 test deleteFile tMultiFile_tmp.dat: blocksize=128 nfile=2 nblock=62 nCRC=0 ncont=3,2 cont=0 [2,60,61] free=[50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35] file0 0 file1 -1 file2 2 [file0 [3,4,5,6,7,8,9,10,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34] fsize=3072 cur=0 nest=0 dirty=1 , [] fsize=0 cur=-1 nest=0 dirty=0 ,file2 [11,12,13,14,15,16,17,18,51,52,53,54,55,56,57,58,35] fsize=2064 cur=16 nest=0 dirty=1 ] tMultiFile_tmp.dat: blocksize=128 nfile=2 nblock=63 nCRC=0 ncont=3,3 cont=1 [1,59,62] free=[50,49,48,47,46,45,44,43,42,41,40,39,38,37,36] file0 0 file1 -1 file2 2 MultiFile test with blockSize=4096, useCRC=1, useODirect=1 tMultiFile_tmp.dat: blocksize=4096 nfile=0 nblock=1 nCRC=0 ncont=0,0 cont=0 [] free=[] tMultiFile_tmp.dat: blocksize=4096 nfile=0 nblock=1 nCRC=0 ncont=0,0 cont=0 [] free=[] tMultiFile_tmp.dat: blocksize=4096 nfile=3 nblock=1 nCRC=0 ncont=0,0 cont=0 [] free=[] tMultiFile_tmp.dat: blocksize=4096 nfile=3 nblock=1 nCRC=0 ncont=0,0 cont=0 [] free=[] file0 0 file1 1 file2 2 [file0 [1] fsize=3072 cur=0 nest=0 dirty=1 ,file1 [3] fsize=2048 cur=0 nest=0 dirty=1 ,file2 [2] fsize=2048 cur=0 nest=0 dirty=1 ] tMultiFile_tmp.dat: blocksize=4096 nfile=3 nblock=4 nCRC=4 ncont=0,0 cont=0 [] free=[] file0 0 file1 1 file2 2 test deleteFile tMultiFile_tmp.dat: blocksize=4096 nfile=2 nblock=3 nCRC=3 ncont=0,0 cont=0 [] free=[] file0 0 file1 -1 file2 2 [file0 [1] fsize=3072 cur=0 nest=0 dirty=1 , [] fsize=0 cur=-1 nest=0 dirty=0 ,file2 [2] fsize=2064 cur=0 nest=0 dirty=1 ] tMultiFile_tmp.dat: blocksize=4096 nfile=2 nblock=3 nCRC=3 ncont=0,0 cont=0 [] free=[] file0 0 file1 -1 file2 2 Test a version 1 MultiFile ... tMultiFile.in: blocksize=128 nfile=2 nblock=57 nCRC=0 ncont=0,0 cont=0 [] free=[48,47,46,45,44,43,42,41,40,39,38,37,36,35,34] file0 0 file1 -1 file2 2 Test nested with block sizes 512 and 0 tMultiFile_tmp.nest: blocksize=512 nfile=2 nblock=1 nCRC=0 ncont=0,0 cont=0 [] free=[] tnested: blocksize=512 nfile=1 nblock=1 nCRC=0 ncont=0,0 cont=0 [] free=[] tMultiFile_tmp.nest: blocksize=512 nfile=2 nblock=2 nCRC=0 ncont=0,0 cont=0 [] free=[] tnested: blocksize=512 nfile=1 nblock=1 nCRC=0 ncont=0,0 cont=0 [] free=[] Test truncation of MultiFile (also nested) ... tMultiFile_tmp.dat: blocksize=256 nfile=1 nblock=11 nCRC=0 ncont=0,0 cont=0 [] free=[] tMultiFile_tmp.dat: blocksize=256 nfile=1 nblock=5 nCRC=0 ncont=0,0 cont=0 [] free=[] 4 >>> pack 0 real 0 user 0 system unpack 0 real 0 user 0 system pack 0.05 real 0.04 user 0 system unpack 0.05 real 0.04 user 0.01 system move1 0.03 real 0.02 user 0 system move2 0.05 real 0.05 user 0 system move2 0.05 real 0.05 user 0 system move3 0.12 real 0.11 user 0 system exact 0.02 real 0 user 0.02 system exact 0.02 real 0 user 0 system double 0.02 real 0 user 0.01 system double 0.01 real 0.01 user 0.01 system partly 0.01 real 0 user 0.01 system partly 0.04 real 0 user 0.02 system <<< tMultiFile ended OK casacore-3.7.1/casa/IO/test/tMultiFileLarge.cc000066400000000000000000000156411476623553700210700ustar00rootroot00000000000000//# tMultiFileLarge.cc: Test program for class MultiFile getting aborted //# Copyright (C) 2014 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include using namespace casacore; namespace casacore { // Derive from MultiFile to make a special test mode possible // for extremely large files. Writing all data takes too long, but // it makes it possible to test such an index. class MultiFileLarge : public MultiFile { public: MultiFileLarge (const String& name, ByteIO::OpenOption option, Int blockSize, Bool useODirect, Bool useCRC, Int testMode) : MultiFile (name, option, blockSize, useODirect, useCRC), itsTestMode (testMode) {} ~MultiFileLarge() override { flush(); } void readBlock (MultiFileInfo&, Int64, void*) override; void writeBlock (MultiFileInfo&, Int64, const void*) override; void extendVF (MultiFileInfo& info, Int64 lastblk, Bool useFreeBlocks) override; void writeHeaderShow (Int64 ncont, Int64 todo) const override; void writeHeaderTest() override; private: Int itsTestMode; }; void MultiFileLarge::readBlock (MultiFileInfo& info, Int64 blknr, void* buffer) { if (itsTestMode != 0) { MultiFile::readBlock (info, blknr, buffer); } } void MultiFileLarge::writeBlock (MultiFileInfo& info, Int64 blknr, const void* buffer) { if (itsTestMode != 0) { MultiFile::writeBlock (info, blknr, buffer); } else if (itsUseCRC) { storeCRC (buffer, info.blockNrs[blknr]); } } void MultiFileLarge::extendVF (MultiFileInfo& info, Int64 lastblk, Bool useFreeBlocks) { Int64 nrb1 = 0; Int64 nrb2 = 0; if (!useFreeBlocks && itsTestMode != 0) { nrb1 = itsNrBlock; itsNrBlock = 1 + itsHdrCont[0].blockNrs.size() + itsHdrCont[1].blockNrs.size(); nrb2 = itsNrBlock; } MultiFile::extendVF (info, lastblk, useFreeBlocks); if (!useFreeBlocks && itsTestMode != 0) { // Reset the real size. Blocks might have been added for the header. itsNrBlock = nrb1 + itsNrBlock - nrb2; cout << "test mode: " << itsNrBlock << ' '<& hdrContNrs = itsHdrCont[newContInx].blockNrs; cout << ' ' << newContInx << itsHdrCont[0].blockNrs << itsHdrCont[1].blockNrs << hdrContNrs[0] << ' ' << ncont << ' ' << todo << endl; } } void MultiFileLarge::writeHeaderTest() { if (itsTestMode > 0 && itsNrBlock > itsTestMode) { // Mimic an abort by not writing the first header block. cout << "MultiFileLarge::writeHeader ended prematurely for nrBlock=" << itsNrBlock << endl; exit (0); // abort } } } void readFile (const String& name) { MultiFile mfile(name, ByteIO::Old); AlwaysAssertExit (! mfile.isWritable()); mfile.show (cout); for (uInt i=0; i buf(4096/sizeof(Int64)); indgen(buf); Int64 offs0 = 0; Int64 offs1 = 0; for (size_t i=0; i<1024; ++i) { for (size_t j=0; j<128; ++j) { mfile.write (fid0, buf.data(), 4096, offs0); offs0 += 4096; } for (size_t j=0; j<32; ++j) { mfile.write (fid1, buf.data(), 4096, offs1); offs1 += 4096; } // Do an occasional flush. if (i%200 == 5) { mfile.flush(); } } AlwaysAssertExit (mfile.info()[fid0].buffer.get()); mfile.closeFile (fid0); AlwaysAssertExit (! mfile.info()[fid0].buffer.get()); AlwaysAssertExit (mfile.info()[fid1].buffer.get()); mfile.closeFile (fid1); AlwaysAssertExit (! mfile.info()[fid1].buffer.get()); } cout << "read the large file" << endl; readFile ("tMultiFileLarge_tmp.dat"); cout << endl; } int main (int argc, char* argv[]) { // Run as: tMultiFileLarge testMode // testMode = 0: do several tests for large files without writing data // testMode > 0: create 1000000 blocks without writing data and exit prematurely // testMode < 0: read the file produced by testMode>0 try { Int testMode = 0; // normal if (argc > 1) { testMode = atoi(argv[1]); } if (testMode == 0) { // Test a large MultiFile without actually writing data. testLarge (False, False, -1); testLarge (False, True, -1); testLarge (True, False, -1); testLarge (True, True, -1); } else if (testMode < 0) { readFile ("tMultiFileLarge_tmp.dat"); } else { // Test a large MultiFile forcing an exception when itsNrBlock > 100000 testLarge (False, False, 100000); } } catch (std::exception& x) { cout << "Unexpected exception: " << x.what() << endl; return 1; } cout << "tMultiFileLarge ended OK" << endl; return 0; } casacore-3.7.1/casa/IO/test/tMultiFileLarge.out000066400000000000000000000274041476623553700213120ustar00rootroot00000000000000Test large MultiFiles ... Test with ODirect=0, useCRC=0 test mode: 32962 32961 1 1[][1]1 1 2706 test mode: 64965 64962 2 0[2,3,4][1]2 3 9130 test mode: 96968 96965 5 1[2,3,4][1,5,6,7]1 4 15554 test mode: 128971 128968 8 0[2,3,4,8,9,10][1,5,6,7]2 6 21978 test mode: 160974 160971 11 1[2,3,4,8,9,10][1,5,6,7,11,12,13]1 7 28402 test mode: 163856 163854 14 0[2,3,4,8,9,10,14,15][1,5,6,7,11,12,13]2 8 28994 read the large file tMultiFileLarge_tmp.dat: blocksize=4096 nfile=2 nblock=163856 nCRC=0 ncont=8,7 cont=0 [2,3,4,8,9,10,14,15] free=[] file0 0 file1 1 Test with ODirect=0, useCRC=1 test mode: 962 961 1 1[][1]1 1 150 test mode: 32995 32962 2 0[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][1]2 33 134818 test mode: 65061 64995 35 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][1,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,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100]1 67 269878 test mode: 97128 97061 101 0[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,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167][1,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,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100]2 100 405078 test mode: 129194 129128 168 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,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167][1,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,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233]1 133 540274 test mode: 161260 161194 234 0[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,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299][1,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,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233]2 166 675466 test mode: 164176 164140 300 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,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299][1,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,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335]1 169 688114 read the large file tMultiFileLarge_tmp.dat: blocksize=4096 nfile=2 nblock=164176 nCRC=164140 ncont=166,169 cont=1 [1,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,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335] free=[] file0 0 file1 1 Test with ODirect=1, useCRC=0 test mode: 32962 32961 1 1[][1]1 1 2706 test mode: 64965 64962 2 0[2,3,4][1]2 3 9130 test mode: 96968 96965 5 1[2,3,4][1,5,6,7]1 4 15554 test mode: 128971 128968 8 0[2,3,4,8,9,10][1,5,6,7]2 6 21978 test mode: 160974 160971 11 1[2,3,4,8,9,10][1,5,6,7,11,12,13]1 7 28402 test mode: 163856 163854 14 0[2,3,4,8,9,10,14,15][1,5,6,7,11,12,13]2 8 28994 read the large file tMultiFileLarge_tmp.dat: blocksize=4096 nfile=2 nblock=163856 nCRC=0 ncont=8,7 cont=0 [2,3,4,8,9,10,14,15] free=[] file0 0 file1 1 Test with ODirect=1, useCRC=1 test mode: 962 961 1 1[][1]1 1 150 test mode: 32995 32962 2 0[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][1]2 33 134818 test mode: 65061 64995 35 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][1,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,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100]1 67 269878 test mode: 97128 97061 101 0[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,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167][1,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,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100]2 100 405078 test mode: 129194 129128 168 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,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167][1,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,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233]1 133 540274 test mode: 161260 161194 234 0[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,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299][1,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,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233]2 166 675466 test mode: 164176 164140 300 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,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299][1,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,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335]1 169 688114 read the large file tMultiFileLarge_tmp.dat: blocksize=4096 nfile=2 nblock=164176 nCRC=164140 ncont=166,169 cont=1 [1,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,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335] free=[] file0 0 file1 1 tMultiFileLarge ended OK Test premature ending ... Test with ODirect=0, useCRC=0 test mode: 32962 32961 1 1[][1]1 1 2706 test mode: 64965 64962 2 0[2,3,4][1]2 3 9130 test mode: 96968 96965 5 1[2,3,4][1,5,6,7]1 4 15554 test mode: 128971 128968 8 0[2,3,4,8,9,10][1,5,6,7]2 6 21978 MultiFileLarge::writeHeader ended prematurely for nrBlock=128971 Read back prematurely ended data set ... tMultiFileLarge_tmp.dat: blocksize=4096 nfile=2 nblock=96968 nCRC=0 ncont=3,4 cont=1 [1,5,6,7] free=[] file0 0 file1 1 tMultiFileLarge ended OK casacore-3.7.1/casa/IO/test/tMultiFileLarge.run000066400000000000000000000003071476623553700213000ustar00rootroot00000000000000#!/bin/sh echo "Test large MultiFiles ..." ./tMultiFileLarge 0 echo echo "Test premature ending ..." ./tMultiFileLarge 1 echo echo "Read back prematurely ended data set ..." ./tMultiFileLarge -1 casacore-3.7.1/casa/IO/test/tMultiHDF5.cc000066400000000000000000000237401476623553700177230ustar00rootroot00000000000000//# tMultiHDF5.cc: Test program for class MultiHDF5 //# Copyright (C) 2014 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include using namespace casacore; using namespace std; void showMultiFile (MultiFileBase& mfile) { cout << mfile.fileName() << ' ' << mfile.blockSize() << ' ' << mfile.nfile() << ' ' << mfile.nblock() << ' ' << mfile.freeBlocks() << endl; } void makeFile (Int64 blockSize) { MultiHDF5 mfile("tMultiHDF5_tmp.dat", ByteIO::New, blockSize); AlwaysAssertExit (mfile.isWritable()); showMultiFile(mfile); } void readFile() { MultiHDF5 mfile("tMultiHDF5_tmp.dat", ByteIO::Old); AlwaysAssertExit (! mfile.isWritable()); showMultiFile(mfile); for (uInt i=0; i buf(128); indgen(buf); mfile.write (id0, buf.data(), 1024, 0); buf += Int64(128); mfile.write (id2, buf.data(), 1024, 0); buf += Int64(128); mfile.write (id0, buf.data(), 1024, 1024); buf += Int64(128); mfile.write (id0, buf.data(), 1024, 2048); buf += Int64(128); mfile.write (id1, buf.data(), 1024, 1024); buf += Int64(128); mfile.write (id2, buf.data(), 1024, 1024); cout << mfile.info() << endl; mfile.closeFile (id0); mfile.closeFile (id1); mfile.closeFile (id2); } void checkFiles1 (Bool do1=True) { MultiHDF5 mfile("tMultiHDF5_tmp.dat", ByteIO::Old); Int id0 = mfile.openFile ("file0"); Int id2 = mfile.openFile ("file2"); Vector buf1(128), buf(128),buff(3*128); indgen(buf1); mfile.read (id0, buf.data(), 1024, 0); AlwaysAssertExit (allEQ(buf, buf1)); buf1 += Int64(128); mfile.read (id2, buf.data(), 1024, 0); if (!allEQ(buf, buf1)) { cout << buf << endl; } AlwaysAssertExit (allEQ(buf, buf1)); buf1 += Int64(128); mfile.read (id0, buf.data(), 1024, 1024); AlwaysAssertExit (allEQ(buf, buf1)); buf1 += Int64(128); mfile.read (id0, buf.data(), 1024, 2048); AlwaysAssertExit (allEQ(buf, buf1)); buf1 += Int64(128); if (do1) { Int id1 = mfile.openFile ("file1"); mfile.read (id1, buf.data(), 1024, 1024); AlwaysAssertExit (allEQ(buf, buf1)); mfile.closeFile (id1); } buf1 += Int64(128); mfile.read (id2, buf.data(), 1024, 1024); AlwaysAssertExit (allEQ(buf, buf1)); // Check a single read. indgen(buf1); mfile.read (id0, buff.data(), 3072, 0); AlwaysAssertExit (allEQ(buff(Slice(0,128)), buf1)); AlwaysAssertExit (allEQ(buff(Slice(128,128)), buf1+Int64(256))); AlwaysAssertExit (allEQ(buff(Slice(256,128)), buf1+Int64(384))); mfile.read (id0, buff.data(), 3072-24, 8); AlwaysAssertExit (allEQ(buff(Slice(0,127)), buf1(Slice(1,127)))); AlwaysAssertExit (allEQ(buff(Slice(127,128)), buf1+Int64(256))); AlwaysAssertExit (allEQ(buff(Slice(255,126)), buf1(Slice(0,126))+Int64(384))); AlwaysAssertExit (buff[380]==509 && buff[381]==509); // check not overwritten mfile.closeFile (id0); mfile.closeFile (id2); } void deleteFile() { cout <<"test deleteFile"< buf(128), buf1(128); indgen(buf); mfile.write (id0, buf.data(), 1016, 8); mfile.read (id0, buf1.data(), 1024, 0); AlwaysAssertExit(buf[0]==buf1[0] && allEQ(buf1(Slice(1,127)), buf(Slice(0,127)))); mfile.write (id0, buf.data(), 1024, 0); mfile.write (id2, buf.data(), 16, 2048); cout << mfile.info() << endl; mfile.closeFile (id0); mfile.closeFile (id2); } void checkFiles2() { checkFiles1(False); MultiHDF5 mfile("tMultiHDF5_tmp.dat", ByteIO::Old); Int id2 = mfile.openFile ("file2"); Vector buf1(2), buf(2); indgen(buf1); mfile.read (id2, buf.data(), 16, 2048); AlwaysAssertExit (allEQ(buf, buf1)); mfile.closeFile (id2); } void timeExact() { MultiHDF5 mfile("tMultiHDF5_tmp.dat", ByteIO::New, 32768); Int id = mfile.createFile ("file0"); Vector buf(32768/8, 0); for (Int j=0; j<2; ++j) { Timer timer; for (uInt i=0; i<1000; ++i) { mfile.write (id, buf.data(), 32768, i*32768); } mfile.fsync(); timer.show ("exact "); } mfile.closeFile (id); } void timeDouble() { MultiHDF5 mfile("tMultiHDF5_tmp.dat", ByteIO::New, 16384); Int id = mfile.createFile ("file0"); Vector buf(32768/8, 0); for (Int j=0; j<2; ++j) { Timer timer; for (uInt i=0; i<1000; ++i) { mfile.write (id, buf.data(), 32768, i*32768); } mfile.fsync(); timer.show ("double"); } mfile.closeFile (id); } void timePartly() { MultiHDF5 mfile("tMultiHDF5_tmp.dat", ByteIO::New, 32768); Int id = mfile.createFile ("file0"); Vector buf(16384/8, 0); for (Int j=0; j<2; ++j) { Timer timer; for (uInt i=0; i<2000; ++i) { mfile.write (id, buf.data(), 16384, i*16384); } mfile.fsync(); timer.show ("partly"); } mfile.closeFile (id); } void timeMove1() { Vector buf1(4, 3); Vector buf2(4, 0); Timer timer; for (uInt i=0; i<5000000; ++i) { memcpy (buf2.data(), buf1.data(), 8*4); } timer.show ("move1 "); } typedef void* moveFunc(void*, const void*, size_t); void* mymemcpy (void* to, const void* from, size_t n) { return memcpy (to, from, n); } void timeMove2 (moveFunc func) { Vector buf1(4, 3); Vector buf2(4, 0); Timer timer; for (uInt i=0; i<5000000; ++i) { func (buf2.data(), buf1.data(), 8*4); } timer.show ("move2 "); } void timeMove3() { Vector buf1(4, 3); Vector buf2(4, 0); Timer timer; for (uInt i=0; i<5000000; ++i) { for (uInt j=0;j<4; ++j) { buf2.data()[j] = buf1.data()[j]; } } timer.show ("move3 "); } void doTest (Int64 blockSize) { cout << "MultiHDF5 test with blockSize=" << blockSize << endl; makeFile (blockSize); readFile(); addFiles(); readFile(); writeFiles1(); readFile(); checkFiles1(); deleteFile(); writeFiles2(); readFile(); checkFiles2(); cout << endl; } void testNested (Int64 blockSizeParent, Int64 blockSizeChild) { cout << "Test nested with block sizes " << blockSizeParent << " and " << blockSizeChild << endl; { MultiHDF5* parentmf = new MultiHDF5 ("tMultiHDF5_tmp.nest", ByteIO::New, blockSizeParent); std::shared_ptr parent (parentmf); MultiHDF5 child("tnested", parent, ByteIO::New, blockSizeChild); Int fidp = parent->createFile ("file0"); Int fidc = child.createFile ("file0"); AlwaysAssertExit (fidp == 1 && fidc == 0); showMultiFile (*parent); showMultiFile (child); child.closeFile (fidc); parent->closeFile (fidp); } { MultiHDF5* parentmf = new MultiHDF5 ("tMultiHDF5_tmp.nest", ByteIO::Old); std::shared_ptr parent (parentmf); MultiHDF5 child("tnested", parent, ByteIO::Old); Int fidp = parent->openFile ("file0"); Int fidc = child.openFile ("file0"); AlwaysAssertExit (fidp == 1 && fidc == 0); showMultiFile (*parent); showMultiFile (child); child.closeFile (fidc); parent->closeFile (fidp); } cout << endl; } int main() { if (! HDF5Object::hasHDF5Support()) { cout << "tMultiHDF5 not run; HDF5 is not supported in casacore build" << endl; return 3; } try { doTest (128); // requires extra header file doTest (1024); // no extra header file testNested (512, 0); timeExact(); timeDouble(); timePartly(); //timeMove1(); //timeMove2(memcpy); //timeMove2(mymemcpy); //timeMove3(); } catch (std::exception& x) { cout << "Unexpected exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/casa/IO/test/tTapeIO.cc000066400000000000000000000121041476623553700173330ustar00rootroot00000000000000//# tTapeIO.cc: Test program for TapeIO class //# Copyright (C) 1995,1996,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include int main(int argc, const char* argv[]) { try { Input inputs(1); inputs.create ("device", "", "Tape device name"); inputs.create ("bytes", "32768000", "Number of bytes to transfer", "Int", "1-10000000000"); inputs.create ("record", "32768", "Number of bytes in each record", "Int", "1-262144"); inputs.create ("check", "False", "Verify if the data read is correct", "Bool"); inputs.create ("interactive", "True", "Don't run this test automatically", "Bool"); inputs.readArguments (argc, argv); if (inputs.getBool("interactive") == False) { cout << "UNTESTED" << endl; return 3; } const Path device(inputs.getString("device")); const uInt recordSize = inputs.getInt("record"); uInt totalBytes = inputs.getInt("bytes"); const Bool checkContents = inputs.getBool("check");; const uInt nRecords = totalBytes/recordSize; totalBytes = nRecords * recordSize; // To account for roundoff. uChar* writeBuffer = new uChar[recordSize]; { // Do the write test TapeIO tape(device, True); AlwaysAssert(tape.isWritable(), AipsError); tape.rewind(); ACG g; DiscreteUniform rand(&g, 0, 255); for (uInt i = 0; i < recordSize; i++) { writeBuffer[i] = static_cast(rand.asInt()); } Timer clock; for (uInt i = 0; i < nRecords; i++) { tape.write(recordSize, writeBuffer); } Double elapsedTime = clock.real(); cout << "It took " << elapsedTime << " seconds to write " << totalBytes/1024 << " kbytes using a record size of " << recordSize << " bytes" << endl; cout << "Transfer rate is: " << totalBytes/elapsedTime/1024 << " kbytes/sec" << endl; } cout << "PASSED the write test" << endl; { // Do the skip test TapeIO tape(device); tape.rewind(); AlwaysAssert(tape.isReadable(), AipsError); uChar* readBuffer = new uChar[recordSize]; Int bytesRead = tape.read(recordSize, readBuffer, False); AlwaysAssert(bytesRead == Int(recordSize), AipsError); tape.skip(1); bytesRead = tape.read(recordSize, readBuffer, False); AlwaysAssert(bytesRead == 0, AipsError); delete [] readBuffer; } cout << "PASSED the skip test" << endl; { // Do the read test const int fd = TapeIO::open(device); TapeIO tape(fd); AlwaysAssert(tape.isReadable(), AipsError); tape.rewind(); uChar* readBuffer = new uChar[recordSize]; for (uInt i = 0; i < recordSize; i++) { *(readBuffer + i) = uChar(0xa5); } Timer clock; for (uInt l = 0; l < nRecords; l++) { tape.read(recordSize, readBuffer); if (checkContents) { for (uInt i = 0; i < recordSize; i++) { AlwaysAssert(readBuffer[i] == writeBuffer[i], AipsError); } } } Double elapsedTime = clock.real(); cout << "It took " << elapsedTime << " seconds to read " << totalBytes/1024 << " kbytes using a record size of " << recordSize << " bytes" << endl; cout << "Transfer rate is: " << totalBytes/elapsedTime/1024 << " kbytes/sec" << endl; tape.rewind(); TapeIO::close(fd); delete [] readBuffer; cout << "PASSED the read test" << endl; } delete [] writeBuffer; } catch (std::exception& x) { cerr << x.what() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; } // Local Variables: // compile-command: "gmake OPTLIB=1 tTapeIO" // End: casacore-3.7.1/casa/IO/test/tTapeIO.run000066400000000000000000000001521476623553700175520ustar00rootroot00000000000000#!/bin/sh echo "./tTapeIO interactive=False" ./tTapeIO interactive=False echo "tapeio status = $?" exit 0 casacore-3.7.1/casa/IO/test/tTypeIO.cc000066400000000000000000000125441476623553700173730ustar00rootroot00000000000000//# tTypeIO.cc: Test program for class TypeIO and derived classes //# Copyright (C) 1996,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void doIt (TypeIO* io) { // Save current file position. Int64 position = io->seek (0, ByteIO::Current); AlwaysAssertExit (io->isReadable()); AlwaysAssertExit (io->isWritable()); AlwaysAssertExit (io->isSeekable()); Bool testBool = True; Short testShort = -30; uShort testuShort = 10; Int testInt = -20; uInt testuInt = 80; Int64 testInt64 = -100000; uInt64 testuInt64 = 100000; Float testFloat = 18.5; Double testDouble = 23.5; Complex testComplex(2,3); DComplex testDComplex(2.5,3.8); Char testChar = 'A'; uChar testuChar = 'B'; String testString("This is a teststring"); io->write (1, &testBool); io->write (1, &testShort); io->write (1, &testuShort); io->write (1, &testInt); io->write (1, &testuInt); io->write (1, &testInt64); io->write (1, &testuInt64); io->write (1, &testFloat); io->write (1, &testDouble); io->write (1, &testComplex); io->write (1, &testDComplex); io->write (1, &testChar); io->write (1, &testuChar); io->write (1, &testString); io->seek (position); Bool tBool; Short tShort; uShort tuShort; Int tInt; uInt tuInt; Int64 tInt64; uInt64 tuInt64; Float tFloat; Double tDouble; Complex tComplex; DComplex tDComplex; Char tChar; uChar tuChar; String tString; io->read (1, &tBool); io->read (1, &tShort); io->read (1, &tuShort); io->read (1, &tInt); io->read (1, &tuInt); io->read (1, &tInt64); io->read (1, &tuInt64); io->read (1, &tFloat); io->read (1, &tDouble); io->read (1, &tComplex); io->read (1, &tDComplex); io->read (1, &tChar); io->read (1, &tuChar); io->read (1, &tString); AlwaysAssertExit (tBool == testBool); AlwaysAssertExit (tChar == testChar); AlwaysAssertExit (tuChar == testuChar); AlwaysAssertExit (tShort == testShort); AlwaysAssertExit (tuShort == testuShort); AlwaysAssertExit (tInt == testInt); AlwaysAssertExit (tuInt == testuInt); AlwaysAssertExit (tInt64 == testInt64); AlwaysAssertExit (tuInt64 == testuInt64); AlwaysAssertExit (tFloat == testFloat); AlwaysAssertExit (tDouble == testDouble); AlwaysAssertExit (tComplex == testComplex); AlwaysAssertExit (tDComplex == testDComplex); AlwaysAssertExit (tString == testString); } int main() { auto regularFileIO = std::make_shared(Path("tTypeIO_tmp.dat"), ByteIO::New); CanonicalIO canonicalIO (regularFileIO); doIt (&canonicalIO); LECanonicalIO lecanonicalIO (regularFileIO); doIt (&lecanonicalIO); RawIO rawIO (regularFileIO); doIt (&rawIO); auto canConv = std::make_shared(); ConversionIO canConvIO (canConv, regularFileIO); doIt (&canConvIO); auto lecanConv = std::make_shared(); ConversionIO lecanConvIO (lecanConv, regularFileIO); doIt (&lecanConvIO); auto ibmConv = std::make_shared(); ConversionIO ibmConvIO (ibmConv, regularFileIO); doIt (&ibmConvIO); auto vaxConv = std::make_shared(); ConversionIO vaxConvIO (vaxConv, regularFileIO); doIt (&vaxConvIO); auto rawConv = std::make_shared(); ConversionIO rawConvIO (rawConv, regularFileIO); doIt (&rawConvIO); cout << "OK" << endl; return 0; } casacore-3.7.1/casa/Inputs.h000066400000000000000000000262401476623553700156720ustar00rootroot00000000000000//# Inputs.h: a module for simple command line user interface classes //# Copyright (C) 1994,1995,1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_INPUTS_H #define CASA_INPUTS_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // // // A module for simple command line user interface classes // // //
      • String //
      • The C language int main(int argc, const char* argv[]) convention. // // // // // The Inputs module name reflects the Casacore convention of pluralizing // the name of the major class it contains. // The Input class name is a reflection of it's role as the early command // line user interface for Casacore applications. This class provides "inputs" // in the form "key=value" or "-key value." // // // // During the old AIPS++ prototyping stage a basic command line user // interface was developed. This attempt at easing the trouble of passing // information to an executable program resulted in a set of C++ classes // called Param and Input. The programmer may simply include the Input // class into their code and have immediate Command Line User Interface // (CLUI) capabilities. The programmer's Casacore application is run from // the unix level prompt by invoking its name and listing linearly on the // same command line the "keyword=values" or "-keyword values" associated // with proper execution. The Input and Param classes will successfully // parse the command line into the executable program and check for // appropriateness. // The CLUI capabilities are further extended to a Graphical User // Interface through the use of the Khoros Cantata environment. The user // starts up Cantata from the unix prompt and, by utilizing a series of // pull down windows, invisibly creates an X-based window for visual // display of all parameters associated with the Casacore application's // need for external input. // The basic command line user interface is an ordered series of // "keyword=value" pairs, which we call parameters. The names parameter // and keyword may correctly be used to refer to each other. // // The class Param (see Param.h) implements one single such parameter. // Values may be Int, Block, double, Block, Bool, or // Strings. In addition to a name and a value, a Param parameter has a // variety of other attributes, such as a one-line help string (useful // when being prompted for input or with hypertext identifiers, etc...), // a type, a range and optional units. All of these attributes are // character strings; parsing and error checking is done at a different // (hidden) level. The programmer, however, will never interact with a // parameter through it's Param class interface. Interaction is done // with the class Input, which is a container of Param's, with a variety // of user interface attributes (help-level, debug-level, etc...). // // Although the programmer must supply the user interface with a number // of predefined program parameters, the user interface itself will // create a small number of system parameters (help=, debug=). The // purpose of these is to tell the task how to communicate with the user // and it's environment, and give the user control over these items. For // example, the user may want to see (debug) messages above a certain // threshold level. The programmer simply adds debug levels to their // code and allows the user to specify how deeply they wish the debugging // to progress. // // For example, a interactive UNIX shell session may look like: // // // 1% MyProgram key1=val1 key3=val3 // 2% MyProgram key1=val1 key2=val3 debug=5 // 3% MyProgram help=prompt // 4% MyProgram help=pane > prog.pane // // // In command 1% the user has set several parameters for the program // MyProgram to applicable values. The 2% command line invokes the // executable and sets the level of displayed debugging to the programmer // specified 5th level. Command 3%: the user is prompted, and parameter // default values are restored. Command 4% gives an example of the // self-describing mode of programs, where a pane description file for // Khoros has been constructed. The latter is the first step toward // building a Khoros Graphic User Interface. // // The Input class is a means for building a linked list of parameters // and gaining access to them once created. Input takes care of // system/environment variables and assigns their values within the // programmer's code. The linked list of parameters is limited only by // the number of names the programmer can dream up. The programmer need // not think hard on the order of definition of parameters in Input. The // list of key=values given on the command line by the user need not be // in any specific order. // // The definition of parameters is by simply creating an Input and then // using the appropriate Input Create member function. Then the // programmer adds to the list of parameters as necessary. // // // // // 01 #include // need this if you want it to work // 02 #include // 03 int main(int argc, const char* argv[]) // 04 { // 05 Input inputs(1); // 06 // Define our input structure // 07 inputs.version("Id: xyPlot.C,v 1.1 1993/01/29 20:45:48 bglenden Exp"); // 08 inputs.create("xyfile", // 09 "/tmp/xy.aipsio", // 10 "File which contains xy vectors", // 11 "InFile"); // 12 inputs.create("overplot", "False", "Multiple plots?", "Bool"); // 13 inputs.create("lines", "True", "Plot lines or points?", "Bool"); // 14 // 15 // and Fill them from the command line // 16 inputs.readArguments(argc, argv); // 17 // 18 try { // 19 const Char *filename = inputs.getString("xyfile"); // 20 AipsIO xyfile(filename, ByteIO::Old); // 21 Vector x, y; // 22 Plot plot; // 23 // 24 xyfile >> x >> y; // initial vectors // 25 plot(x,y,inputs.getBool("lines")); // 26 // 27 for (;;) { // forever // 28 xyfile >> x >> y; // 29 if (inputs.getBool("overplot") == True) { // 30 plot(x,y,inputs.getBool("lines")); // 31 } else { // 32 plot.newPlot(); // 33 plot(x,y,inputs.getBool("lines")); // 34 } // 35 } // 36 } catch (AipsIOError x) { // 37 ; // nothing - no more data // 38 } catch (AllocError x) { // 39 cerr << "AllocError : " << x.what() << endl; // 40 cerr << "Size is : " << x.size() << endl; // 41 } catch (std::exception x) { // 42 cerr << "aipserror: error " << x.what() << endl; // 43 return 1; // 44 } // 45 // 46 cout << "Any key to exit:\n"; // 47 // 48 char ch; // 49 cin.get(ch); // 50 // 51 return 0; // 52 } // // Let us discuss this program line for line. // // 03 - This is the method of passing the command line through to the // main body of code. This obviously makes it mandatory. The inclusion // of the argc, argv is very well discussed in Stroustrup, The // C++ Programming Language, page 87. // // 05 - The instantiation of Input in the variable inputs(1) is done with // an integer argument of (1) to indicate the constructor should build // inputs with a pair of system parameters and read in values for them. // An argument of (0) would build an Input that was empty and would // obligate the programmer to build a list of Params explicitly. // // 07 - The version of the code is stored within the Input. Note the // optional use of RCS keyword substitution. See the "co" man page for // details. This allows the code to be automatically updated. // // 08-11 - The create member function of Input builds, in this case, a // parameter called xyfile, immediately filled with the String containing // the directory that holds the data. The help String is useful for new // users or prompting. The fourth argument of InFile is the optional // type of the parameter's value. Any suitable String may be used. // Missing from this example are the optional fifth and sixth arguments, // the parameter's value's range and units, respectively. // // 12 - This is another instantiation of a Param inside of Input. This // parameter will be referenced by the keyword "overplot". It is // initialized to False and is of type Bool. // // 13 - This line is the third and final Param placed in inputs and is // recognized by the code when accessed with keyword "lines". // // 16 - The call of readArguments(argc, argv) should be done after the // list of Params has been completed. This line of code fills the values // from the command line. A keyword that doesn't match will throw an // error. // // 19 - At this point the local variable filename is initialized to the // String value held within the parameter accessed through the key // "xyfile". Recall that the value of xyfile was originally set to // "/tmp/xy.aipsio" but would be replaced with the proper value at // execution. The getString member function returns either the default // value specified during the xyfile parameter's instantiation or the // value placed into it from the command line use of xyfile=myfile. // // 25 - Here the boolean value of the Param called lines is inserted into // the call to the function plot. // // 29 - Again the Input interface has its parameter called overplot // return a boolean to be used as a test for an "if". The getBool(key) // Input member function may be reading the default value of the // appropriate parameter called key or using the value passed from the // command line. // // 30 & 33 - Another call to plot that uses the boolean value stored in // the parameter called lines. // // // // This module fit the early needs of a a simple user interface. // // //
      • possibly replace the Param class with Keywords // // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Inputs/000077500000000000000000000000001476623553700155155ustar00rootroot00000000000000casacore-3.7.1/casa/Inputs/Input.cc000066400000000000000000000311571476623553700171320ustar00rootroot00000000000000//# Input.cc: A linked list of user input parameters //# Copyright (C) 1993,1994,1995,1996,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // Class Input: the user interface #include #include #include #include #include #include #include #if defined(TESTBED) #define DEBUG 1 #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN // // The constructor does nothing more but read the users environment // and add appropriate things to the linked list of Param`s // This can be turned off by overriding the default `init' argument. // Input::Input (Int createEnv) : is_closed(False), do_prompt(False), debug_level(0), p_count(0) { if (createEnv){ envCreate ("DEBUG", "debug", "0"); envCreate ("HELP" , "help", "0"); debug_level = getInt("debug"); if (debug(5)) { cout << "Input::Input: (debug=" << debug_level << ")\n"; } } else { create ("debug","0","Debug Level"); create ("help","0"); } } // Destructor Input::~Input() { if (debug(5)) { cout << "INPUT> Destructing " << count() << " parameters\n"; } } void Input::create (const String& key) { createPar (0, key, "", "", "", "", ""); } void Input::create (const String& key, const String& value) { createPar(0, key, value, "", "", "", ""); } void Input::create (const String& key, const String& value, const String& help) { createPar(0, key, value, help, "", "", ""); } void Input::create (const String& key, const String& value, const String& help, const String& type) { createPar(0, key, value, help, type, "", ""); } void Input::create (const String& key, const String& value, const String& help, const String& type, const String& range) { createPar(0, key, value, help, type, range, ""); } void Input::create (const String& key, const String& value, const String& help, const String& type, const String& range, const String& unit) { createPar(0, key, value, help, type, range, unit); } // CreatePar() is the workhorse function, it is a private member function void Input::createPar (Int system, const String& key, const String& value, const String& help, const String& type, const String& range, const String& unit) { if (is_closed) { String msg = "Input::createPar: " + key + ": Cannot create any more Parameters"; throw(AipsError(msg)); } int i = getParam(key); if (i>=0) { String msg = "Input:cCreatePar: " + key + ": Parameters already exists."; throw (AipsError(msg)); } if (key=="help") { if (value=="prompt") { do_prompt = True; } help_mode = value; } if (debug(5)) { cout << "Input::CreatePar: Creating new keyword " << key << "=" << value << "\n" << flush; } Param tmp(key,value,help,type,range,unit); if (system) { tmp.setSystem(True); } else { tmp.setIndex(++p_count); } parList_p.push_back (tmp); } void Input::close() { if (is_closed) { String msg = "Input::Close: parameter creation is already closed."; throw(AipsError(msg)); } else { is_closed = True; } if (debug(1)) { // Display Param as: 'key=val: help' cout << "INPUT> Closing parameter creation: \n"; cout << "INPUT> ----------------------------------------------------\n"; for (const auto& x : parList_p) { cout << "INPUT> " << x.keyVal() << ": " << x.getHelp() << "\n"; } cout << "INPUT>-----------------------------------------------------\n" << flush; } } // Query functions Double Input::getDouble (const String& key) { Int i = getParam(key); if (i<0) { String msg ="Input::GetDouble: Parameter " + key + " is unknown."; throw (AipsError(msg)); } Param& x = parList_p[i]; if (do_prompt && !x.isSystem()) { prompt(x); } return x.getDouble(); } Block Input::getDoubleArray (const String& key) { Int i = getParam(key); if (i<0) { String msg ="Input::GetDoubleArray: Parameter " + key + " is unknown."; throw (AipsError(msg)); } Param& x = parList_p[i]; if (do_prompt && !x.isSystem()) { prompt(x); } return x.getDoubleArray(); } int Input::getInt (const String& key) { Int i = getParam(key); if (i<0) { String msg ="Input::GetInt: Parameter " + key + " is unknown."; throw (AipsError(msg)); } Param& x = parList_p[i]; if (do_prompt && !x.isSystem()) { prompt(x); } return x.getInt(); } Block Input::getIntArray (const String& key) { Int i = getParam(key); if (i<0) { String msg ="Input::GetIntArray: Parameter " + key + " is unknown."; throw (AipsError(msg)); } Param& x = parList_p[i]; if (do_prompt && !x.isSystem()) { prompt(x); } return x.getIntArray(); } String Input::getString (const String& key) { Int i = getParam(key); if (i<0) { String msg ="Input::GetString: Parameter " + key + " is unknown."; throw (AipsError(msg)); } Param& x = parList_p[i]; if (do_prompt && !x.isSystem()) { prompt(x); } return x.getString(); } Bool Input::getBool (const String& key) { Int i = getParam(key); if (i<0) { String msg ="Input::GetBool: Parameter " + key + " is unknown."; throw (AipsError(msg)); } Param& x = parList_p[i]; if (do_prompt && !x.isSystem()) { prompt(x); } return x.getBool(); } // Modifiers Bool Input::put (const String& key, const String& value) { String akey, avalue; if (debug(5)) { cout << "PUT> " << key << "=" << value << "\n"; } Int i = getParam(key); if (i<0) { String msg = "Input::Put: parameter " + key + " is unknown."; throw (AipsError(msg)); } Param& x = parList_p[i]; x.put(value); return True; } Bool Input::put (const String& key) { String k = key; // Need non-const string String::size_type inx = key.index("="); if (inx == String::npos) { String msg = "Input::Put: " + key + " is not a valid parameter."; throw (AipsError(msg)); } return put (k.before(inx), k.after(inx)); } Int Input::count() const { return parList_p.size(); } void Input::version (const String& a_version) { version_id = a_version; } void Input::announce() { if (debug(5)) { cout << getString("argv0") << " "; for (const auto& x : parList_p) { if (x.getIndex() > 0) { cout << x << " "; } } cout << "\n"; } if (help_mode.contains("prompt")) { do_prompt = True; } if (help_mode.contains("keys")) { keys(); } if (help_mode.contains("exit")) { exit(0); } if (version_id.length() > 0) { // Always announce ??? cout << getString("argv0") << ": Version " << version_id << "\n"; } } // Some private member functions we need // // Move to the current named Parameter; return 0 if none found // A value >= 1 is the index (not used in K_ stuff) // Int Input::getParam (const String& name) const { for (uInt i=0; i " << p_count << " program parameters\n"; } close(); // close creation // Show version on screen if -v or --version is given. if (av[1] && (String(av[1]) == "-v" || String(av[1]) == "--version")) { cerr << av[0] << " version " << version_id << endl; exit (1); } // Show parameters on screen if -h or --help given. if (av[1] && (String(av[1]) == "-h" || String(av[1]) == "--help")) { cerr << av[0] << " version " << version_id << endl; for (const auto& x : parList_p) { if (x.getIndex() > 0) { cerr << x.getKey() << ", " << x.getType() << ", "; if (String(x.get()) != "") { cerr << '(' << x.get() << ')'; } cerr << endl; if (String(x.getHelp()) != "") { cerr << " " << x.getHelp() << endl; } } } exit (1); } // Turn command line style inputs into keyword=value inputs // ' -channels 64' becomes 'channels=64' String thisarg; String keyandval; // Pass through keyword=value and turn -keyword value into keyword=value for (i=1; i= ac - 1) { throw(AipsError("Input::ReadArguments:" "-keyword not followed by value")); } i++; // Advance keyandval = thisarg.after(0) + "=" + av[i]; } else { keyandval = thisarg; } // cout << "putting " << keyandval << endl; put (keyandval); // Insert command line parameters if (keyandval.size() > 5 && keyandval.before(6) == "debug=") { debug_level = atoi(keyandval.after(6).chars()); } else if (keyandval.size() > 4 && keyandval.before(5) == "help=") { help_mode = keyandval.after(4); } } announce(); // Announce and possibly die here } Vector Input::makeMaskFromRanges(const String& ranges, uInt length, Bool oneRelative) { Regex single("^[ \t]*[0-9]+[ \t]*$", 1); Regex range("^[ \t]*[0-9]+[ \t]*-[ \t]*[0-9]+[ \t]*$", 1); Vector mask(length); mask = False; // Step through the string, comma separated expression by comma separated // expression. Int numberOfCommas = ranges.freq(","); Block expressions(numberOfCommas + 1); split (ranges, expressions.storage(), numberOfCommas + 1, ","); for (uInt i=0; i < expressions.nelements(); i++) { // Validate if (expressions[i].contains(single) == False && expressions[i].contains(range) == False) { throw (AipsError(String("Input::makeMaskFromRanges - " "invalid range:") + expressions[i])); } Int left, right; String::size_type inx = expressions[i].index('-'); if (inx != String::npos) { left = atoi(expressions[i].before(inx).chars()); right = atoi(expressions[i].after(inx).chars()); } else { left = right = atoi(expressions[i].chars()); } if (oneRelative) { left -= 1; right -= 1; } if (left + 1 > Int(mask.nelements()) || right + 1 > Int(mask.nelements()) || left > right) { throw (AipsError(String("Input::makeMaskFromRanges - " "out of range or end #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Input.h: A simple command-line argument method for applications. // // // // // //
      • none noted // // // // The Input class name is a reflection of it's role as the early command // line user interface for Casacore applications. This class provides "inputs" // in the form "key=value" or "-key value." // // // // The Input class is a holder of parameters, either automatically assigned // values or altered at the execution of the program which utilizes them. The // parameters are associations of String "keys" to "values". The parameters // may be used as internal values during the program's run. The shell command // // shell% myexecutable limits=1000 happy=True // // would run "myexecutable" and set the internal parameter "limits" to a value // of 1000 and "happy" to True. // // The Input class is instantiated by a constructor with a single Int argument // which, when non-zero, switches on the filling of the keys "debug" and // "help" from environment variables. These two keys always exist in an // instance of Input. No argument to the Input constructor defaults to "debug" // and "help" being set to zero. // // The default existance of the help parameter allows the user to specify // predefined modes for the "help" key. The argument "help=prompt" turns // on prompting for parameter values not specified on the command-line. In // such an instance, the optional String arguments to Input::create become // important. The argument "help=keys" will print to standard output a list // of all the parameters. // // The default existance of the debug parameter allows the user to specify // levels of debugging, where 0 implies none and higher integers means more. // The usage would be as follows: // // Input inp; // // this will execute the block only for values higher than 5 // if(inp.debug(5)) // { // // do debugging stuff here // } // // // Additional parameters must be created inside the main block (or deeper) // of your application. The member function create() is overloaded to accept // from one to six String arguments. All but the first are optional. However, // should the user decide to call any of the get() functions which return a // String, that String will be empty. In this case it is assumed that all // values will be filled from the command line. // Some examples: // // int main(int argc,const char* argv[]) // { // Input inp; // // Create a parameter called "foo" which defaults to True. // inp.create("foo", "True"); // // Create a parameter called "iterbound" which defaults to 2000 and // // has a help String used in cases of prompting. // inp.create("iterbound", "2000", "The upper boundary of the iterator"); // // Create a normalising value with a range, type, and unit. // inp.create("dividend", "10000", The normalization factor of the chutspah", // "0-100000", "Double", "clean steps"); // // The parameters are "filled" from the command line arguments by the member // function ReadArguments(int argc, const char* argv[]). If an argument is not defined // within the main block but specified at the command line, an exception is // thrown. // // inp.readArguments(argc, argv); // // // Finally, the values of the various parameter's are utilized by calling the // Input::getWhatever(key) member functions. They return either a String or // are converted to the data type chosen by the "whatever" in the name of the // function. The value associated with the passed key is returned. // // // get a boolean // if(inp.getBool("foo") // // get an iteration boundary // for(Int i=0; i // // Optional items include: //
        1. specifying a version inp.version("$ID:"); // will print at run time the version of the program being run. //
        2. run time checking of ranges // inp.makeMaskFromRanges(const String &ranges, uInt length, // Bool oneRelative=False); //
        //
        // // // // #include // int main(int argc, const char* argv[]) // { // // instantiate an Input. The integer argument of 1 to the ctor builds // // the system parameters "debug" and "help" and sets their values to the // // shell environment variables DEBUG and HELP. // Input inp(1); // // set the version to be automatically expanded by the RCS. This will // // print the version at run time. // inp.version("$ID:$"); // // We will now create some parameters. // // Create a parameter with no default value i.e. it must be set by a // // command line argument. // inp.create("test"); // // Create a parameter with a default value. // inp.create("file", "$AIPSROOT/data.txt"); // // Create a parameter with a help String which will be displayed when in // // the prompted entry mode. // inp.create("ubound", "1000", "The number of iterations to clean."); // // Create a parameter with a range of acceptable values. Note: checking // // must be done by the user as this isn't coded in. // inp.create("gainstride", "0.5", "The factor by which the Clean strides.", // "Double", "0-1.0"); // // Create a parameter with a unit String. Note: checking must be done // // by the user as this test isn't coded in. // String help("The velocity of the Earth in the direction of the object."); // inp.create("velocity", "2.89e+05", help, "Double", "0-3.0e+06", "m/s"); // // Now we close parameter creation and get the values from the command line // // arguments. // inp.readArguments(argc, argv); // // Now we may utilize the values from the paramters we have created. // // Here we are getting a boolean from the parameter with the key "test". // if(inp.getBool("test") { // // Here we get a String from the parameter with the key "file". // Image myImage(inp.getString("file")); // // Here we set the boundary of the loop. // for(Int i=0;i // // // // In the earliest days of the old AIPS++ project, the desire to start coding // right away led to the need for a user interface. The preexistant C language // method of argc/argv was enclosed in an object for easier use. // class Input { public: // The default constructor enables the creation of parameters. // If the optional Int argument is non-zero, the parameters "help" and // "debug" are created from their shell environment values. // This puts the program in no-prompt mode unless environment variable HELP // is defined with value "prompt". The output debug level is set according // to the value of the environment variable DEBUG. Input (Int createEnv=0); // Destructor. ~Input(); // Create a new parameter, either from scratch or looking it // up from an internal list of templates. // The function also checks whether parameters can still be created, // and whether key is unique for the program. // The value, help and remaining arguments are all optional. // The multiple definitions are to allow default values // void create (const String& key); void create (const String& key, const String& value); void create (const String& key, const String& value, const String& help); void create (const String& key, const String& value, const String& help, const String& type); void create (const String& key, const String& value, const String& help, const String& type, const String& range); void create (const String& key, const String& value, const String& help, const String& type, const String& range, const String& unit); // // Disable the creation of parameters. Highly recommended, but // not required. readArguments calls it when filling the values from argv[]. void close(); // fill the parameter list from argc, argv command line args void readArguments (int argc, char const* const* argv); // Get the double value of the parameter (or 0.0 if unknown key). // If the program is in prompt mode, ask the user for the value. Double getDouble (const String& key); // Get the Block value of the parameter (or default Block if unknown // key). // If the program is in prompt mode, ask the user for the value. Block getDoubleArray (const String& key); // Get the int value of the parameter (or 0 if unknown key). // If the program is in prompt mode, ask the user for the value. Int getInt (const String& key); // Get the Block value of parameter (or default Block if unknown key) // If the program is in prompt mode, ask the user for the value. Block getIntArray (const String& key); // Get the String value of the parameter (or "" if unknown key). // If the program is in prompt mode, ask the user for the value. String getString (const String& key); // Get the boolean value of the parameter (or FALSE if unknown key). // If the program is in prompt mode, ask the user for the value. Bool getBool (const String& key); // Get the total number of parameters of this program Int count() const; // See if the current debug level is thresholded Bool debug (Int l) const { return (debug_level >= l) ? True : False; } // Set a new value for an existing named parameter // Returns FALSE if key is an unknown parameter name. // Bool put (const String& key, const String& value); // The single argument is of the form `key=value', where key is a valid // parameter name. Bool put (const String& keyval); // // Set version string for announcements void version (const String&); // Announce program and version. void announce(); // Turn a string in the form "5,7,9-11,13,2-4" into a Vector, where // each specified position or range, is set to True and every other position // is set to False. While the returned vector always has a zero origin, if // oneRelative is True, all the numbers in the supplied string are // decremented before use. Spaces in ranges are ignored, but otherwise // ill-formed strings, or numbers that would fill in beyond the length // of the Vector results in an exception being thrown. static Vector makeMaskFromRanges(const String& ranges, uInt length, Bool oneRelative=False); private: // Get the index of the named parameter (-1 if unknown key). // Anywhere from 0.. if a key is found. Int getParam (const String& key) const; // Prompt the user for a value for the parameter. // If he gives a non-empty answer, set that value. void prompt (Param& parameter) const; // Bind an environment variable to a parameter void envCreate (const Char *env, const String& key, const String& def); // The actual creation of a new (system/program) parameter void createPar (Int, const String&, const String&, const String&, const String&, const String&, const String&); // output to stdout a listing of all "key=value" pairs. void keys(); // container of parameters std::vector parList_p; // version id String version_id; // parameter creation allowed? Bool is_closed; // ask user for parameter value? Bool do_prompt; // threshold value for debug output Int debug_level; // "prompt" or "keys" indicates the various types of help. String help_mode; // count of program parameters Int p_count; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Inputs/Param.cc000066400000000000000000000167751476623553700171040ustar00rootroot00000000000000//# Param.cc: Helper class for key=value user interface //# Copyright (C) 1993,1994,1995,1999,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // Param.C: implementation of a parameter (key,value,help,type,range,....) // users typically do not use this class, it is merely // a helper class for Input. #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Param::Param() // default constructor; doesn't do anything {} // standard constructors Param::Param (const String& a_key, const String& a_value, const String& a_help, const String& a_type, const String& a_range, const String& a_unit) : key (a_key), value (a_value), help (a_help), type (a_type), range (a_range), unit (a_unit), hasvalue ((value.length() > 0) ? True : False), system (False), index (0) { #if defined(DEBUG) cout << "Creating parameter " << key << "\n" << " value: " << value << "\n" << " help: " << help << "\n" << " hasvalue: " << hasvalue << "\n" << " system: " << system << "\n" << " index: " << index << "\n"; #endif } Param::~Param() { #if defined(DEBUG) cout << "Destructing parameter " << key << "; but not really\n"; #endif } Param::Param (const Param& other) // copy : key (other.key), value (other.value), help (other.help), type (other.type), range (other.range), unit (other.unit), hasvalue (other.hasvalue), system (other.system), index (other.index) { #if defined(DEBUG) cout << "Copying parameter " << key << "\n" << " value: " << value << "\n" << " help: " << help << "\n" << " hasvalue: " << hasvalue << "\n" << " system: " << system << "\n" << " index: " << index << "\n"; #endif } // operator functions Param& Param::operator= (const Param& other) // assignment { // Make sure we don't assign ourselves if (this != &other ) { key = other.key; value = other.value; help = other.help; type = other.type; range = other.range; unit = other.unit; hasvalue = other.hasvalue; system = other.system; index = other.index; } return *this; } Bool // comparison, don't allow Param::operator== (const Param&) const { return False; } Double Param::getDouble (Bool prompt) const // Double value { #if defined(EVAL) Double d; Int n = eval_double((const char *)value, &d, 1, &iret); if (n==1) { return d; } else { return 0.0; } #else if (prompt) { cerr << "No prompting implemented yet" << endl; } return atof(value.chars()); #endif } Block Param::getDoubleArray (Bool prompt) const // Double value { Int i; Int idx=0; Int n = value.freq(",")+1; String z; String val(value); // need a non-const String Block x(n); if (prompt) { cerr << "No prompting implemented yet" << endl; } for (i=0; i Param::getIntArray (Bool prompt) const { Int i; Int idx=0; Int n = value.freq(",")+1; String z; String val(value); // need a non-const String Block x(n); if (prompt) { cerr << "No prompting implemented yet" << endl; } for (i=0; i Param::getStringArray (Bool prompt) const { Int i; Int idx=0; Int n = value.freq(",")+1; String z; String val(value); // need a non-const String Block x(n); if (prompt) { cerr << "No prompting implemented yet" << endl; } for (i=0; i Param::getBoolArray(Bool prompt) const { Int i; Int idx; Int n = value.freq(",")+1; String z; Block x(n); if (prompt) { cerr << "No prompting implemented yet" << endl; } for (i=0; i " << key << "=" << value << "\n"; value = other; // cout << "Param::Put> " << key << "=" << value << "\n"; return True; } ostream & operator<< (ostream &os, const Param& p) { os << p.key << "=" << p.value; return os; } istream & operator>> (istream &os, Param& p) { // needed because of Slist cerr << "==>Got to implement >> operator for Param now; " << p << endl; return os; } AipsIO & operator<< (AipsIO &os, const Param& p) { // cerr << "==>Got to implement << operator for Param now; " << p << endl; return os; } AipsIO & operator>> (AipsIO &os, Param& p) { // needed because of Slist cerr << "==>Got to implement >> operator for Param now; " << p << endl; return os; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Inputs/Param.h000066400000000000000000000167701476623553700167410ustar00rootroot00000000000000//# Param: A simple keyword/value pair with internal help Strings. //# Copyright (C) 1993,1994,1995,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_PARAM_H #define CASA_PARAM_H #include #include #include #include #include #include // need things like strlen() and such //# Forward declarations #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A simple keyword/value pair with internal help Strings. // // // // // //
      • none noted // // // // The Param class name is a shortening of "parameter" and is indicative of // the class being designed as a keyword/value pair relating to command line // arguments. The existing Keyword class does a much better job for most // other purposes. // // // // The Param is constructed with all arguments being Strings. This is a // reflection of the C-type command line argument method of passing // an integer (argc or argument count) and an array of pointers to characters // (argv or argument vector.) If "char* argv[]" is broken into its individual // arguments they may be used to fill a Param. The constructor pairs up a // "key" to a value. A help String argument is provided to assist in prompted // filling of Param values. The expected return type may be entered as well // as a range of potential values. Finally, the units of the value are also // specified. The intent is to provide a well documented value and a "key" // by which to "call" it. // // The "getWhatever" member functions of Param convert the internal Strings // into the desired output data type. The Strings themselves may also be // returned. // // // // // // we will create a Param which contains the boundary for an iteration loop. // String key("IterBound"); // // give "IterBound" a default value // String value("200"); // // a help String for prompting // String help("The Boundary value for the chutzpah iterator."); // // The expected return type is an integer // String type("Int"); // // The range of "legal" values // String range("10-10000"); // // the units of the value // String unit("unitless"): // // Now we may build our Param // Param PleaseDontTouchMeThere(key, value, help, type, range, unit); // // to retrieve the value we use the GetInt function // for (Int i=0, i // // // The Param class was an early attempt at keywords within Casacore. They have // become obsolete but hang on due to their relationship with the Input class. // // // //
      • fix the GetStringArray() function //
      • convert from Block to Array as return values. //
      • replace entirely with Casacore Keywords? // class Param { public: // constructors and destructor // default constructor Param(); // normal constructor with optional value and help strings Param (const String& key, const String& value, const String& help, const String& type, const String& range, const String& unit); // copy constructor Param (const Param&); // destructor ~Param(); // assignment operator Param& operator= (const Param&); // Equality comparitor. // This function ALWAYS returns // false. I have no idea why it was designed to do this. Bool operator== (const Param&) const; // I/O operators // friend ostream& operator<< (ostream&, const Param& p); friend istream& operator>> (istream&, Param& p); friend AipsIO& operator<< (AipsIO&, const Param& p); friend AipsIO& operator>> (AipsIO&, Param& p); // // get a double parameter value; prompt if switch is TRUE Double getDouble (Bool do_prompt=False) const; // get a Block parameter value; prompt if switch is TRUE Block getDoubleArray (Bool do_prompt=False) const; // get an Int parameter value; prompt if switch is TRUE Int getInt (Bool do_prompt=False) const; // get an Block parameter value; prompt if switch is TRUE Block getIntArray (Bool do_prompt=False) const; // get a String parameter value; prompt if switch is TRUE const String& getString (Bool do_prompt=False) const; // get a Block parameter value; prompt if switch is TRUE Block getStringArray (Bool do_prompt=False) const; // get a Boolean parameter value; prompt if switch is TRUE Bool getBool (Bool do_prompt=False) const; // get parameter value as a string const String& get() const { return value; } // get parameter help string const String& getHelp() const { return help; } // get parameter name const String& getKey() const { return key; } // get the string `key = value' for the parameter String keyVal() const { return key + "=" + value; } // get the type of a parameter const String& getType() const { return type; } // get the valid range of a parameter const String& getRange() const { return range; } // get the units of a parameter const String& getUnit() const { return unit; } // set new parameter value; return FALSE if invalid value Bool put (const String& a_value); // set a parameter as a system parameter void setSystem (Bool val) { system = val; } // check if a parameter is a system parameter Bool isSystem() const { return system; } // set an index for a program parameter void setIndex (Int inx) { index = inx; } // get the index of a parameter Int getIndex() const { return index; } private: // parameter name String key; // parameter value String value; // help string String help; // type of parameter String type; // range/validity/pre-check String range; // optional unit associated with value String unit; // boolean data member which indicates the Param's key has a value. Bool hasvalue; // boolean data member which indicates the Param is system wide. Bool system; // index for program keywords (>=1) Int index; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Inputs/test/000077500000000000000000000000001476623553700164745ustar00rootroot00000000000000casacore-3.7.1/casa/Inputs/test/CMakeLists.txt000066400000000000000000000004441476623553700212360ustar00rootroot00000000000000set (tests tInput tParam ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_casa) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/casa/Inputs/test/tInput.cc000066400000000000000000000122221476623553700202650ustar00rootroot00000000000000//# tInput.cc: this tests the simple argc, argv command line user interface. //# Copyright (C) 1994,1995,1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include int main(int argc, const char* argv[]) { try { // The default constructor enables the creation of parameters. // If the optional Bool argument is True, the parameters "help" and "debug" // are created from their shell environment values. // It puts the program in no-prompt mode unless environment variable HELP // is defined with value "prompt". The output debug level is set according // to the value of the environment variable DEBUG. Input sysInput(1); Input prgInput; // Destructor. //~Input(); // Create a new parameter, either from scratch or looking it // up from an internal list of templates. // The function also checks whether parameters can still be created, // and whether key is unique for the program. // The value, help and remaining arguments are all optional. // prgInput.create("zero"); prgInput.create("one", "1"); prgInput.create("two", "2.00", "the key named two"); prgInput.create("three", "three", "the key named three", "String"); prgInput.create("four", "True", "the key named four", "Bool", "N/A"); prgInput.create("five", "5,5,5,5,5", "a Block of fives", "Block", "3-5", "meters/second"); // // Set a new value for an existing named parameter // Returns FALSE if key is an unknown parameter name. // AlwaysAssertExit(prgInput.put("zero", "False")); // The single argument is of the // form `key=value', where key is a valid parameter name. AlwaysAssertExit(prgInput.put("zero=666.012")); // // fill the parameter list from argc, argv command line args prgInput.readArguments(argc, argv); // Disable the creation of parameters. Highly recommended, but // not required. ReadArguments calls it when filling the values from argv[] // prgInput.Close(); // Get the double value of the parameter (or 0.0 if unknown key). // If the program is in prompt mode, ask the user for the value. AlwaysAssertExit(666.012 == prgInput.getDouble("zero")); // Get the Block value of the parameter (or default Block if // unknown key). // If the program is in prompt mode, ask the user for the value. AlwaysAssertExit(1.0==prgInput.getDoubleArray("one")[0]); // Get the int value of the parameter (or 0 if unknown key). // If the program is in prompt mode, ask the user for the value. AlwaysAssertExit(2 == prgInput.getInt("two")); // Get the Block value of the parameter (or default Block if // unknown key) // If the program is in prompt mode, ask the user for the value. for (int i=0;i<5;i++) AlwaysAssertExit(5==prgInput.getIntArray("five")[i]); // Get the String value of the parameter (or "" if unknown key). // If the program is in prompt mode, ask the user for the value. AlwaysAssertExit(String("three")== prgInput.getString("three")); // Get the boolean value of the parameter (or FALSE if unknown key). // If the program is in prompt mode, ask the user for the value. AlwaysAssertExit(prgInput.getBool("four")); // Get the total number of parameters of this program AlwaysAssertExit (prgInput.count() == 9); // See if the current debug level is thresholded AlwaysAssertExit (!prgInput.debug(2)) // Set version string for announcements prgInput.version("OK"); // Announce program and version. prgInput.announce(); } catch(std::exception& x) { cout << "Caught exception" << endl; cout << "Message is: " << x.what() << endl; } } casacore-3.7.1/casa/Inputs/test/tParam.cc000066400000000000000000000107501476623553700202320ustar00rootroot00000000000000//# tParam.cc: the test code for the Param class. //# Copyright (C) 1994,1995,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include int main() { try { // default constructor Param deflt; // normal constructor with optional value and help strings Param normal("key", "value", "help", "type", "range", "unit"); // copy constructor Param copyofnormal(normal); // destructor //~Param(); // assignment operator deflt = normal; // Equality comparitor - this test is meaningless AlwaysAssertExit(!(deflt == normal)); // I/O operators // //friend ostream & operator<<(ostream &, const Param &p); //friend istream & operator>>(istream &, Param &p); //friend AipsIO & operator<<(AipsIO &, const Param &p); //friend AipsIO & operator>>(AipsIO &, Param &p); // // get a String parameter value; prompt if switch is TRUE AlwaysAssertExit(String("value")==deflt.getString()); // set new parameter value; return FALSE if invalid value AlwaysAssertExit(deflt.put("5.0")); // get a double parameter value; prompt if switch is TRUE AlwaysAssertExit(5.0 == deflt.getDouble()); // get an int parameter value; prompt if switch is TRUE AlwaysAssertExit(5 == deflt.getInt()); // get a Block parameter value; prompt if switch is TRUE AlwaysAssertExit(deflt.put("5.0, 5.0, 5.0")); int i; for (i=0;i<3;i++) AlwaysAssertExit(5.0 == deflt.getDoubleArray()[i]); // get an Block parameter value; prompt if switch is TRUE for (i=0;i<3;i++) AlwaysAssertExit(5 == deflt.getIntArray()[i]); // this function doesn't work // get a Block parameter value; prompt if switch is TRUE // for (i=0;i<3;i++) cout << deflt.getStringArray()[i] << endl; // for (i=0;i<3;i++) AlwaysAssertExit(String("5") == deflt.getStringArray()[i]); // get a Boolean parameter value; prompt if switch is TRUE deflt.put("True"); AlwaysAssertExit(deflt.getBool()); // get parameter value as a string AlwaysAssertExit(String("True")==deflt.get()); // get parameter help string AlwaysAssertExit(String("help")==deflt.getHelp()); // get parameter name AlwaysAssertExit(String("key")==deflt.getKey()); // get the string `key = value' for the parameter AlwaysAssertExit(String("key=True")==deflt.keyVal()); // get the type of a parameter AlwaysAssertExit(String("type")==deflt.getType()); // get the valid range of a parameter AlwaysAssertExit(String("range")==deflt.getRange()); // get the units of a parameter AlwaysAssertExit(String("unit")==deflt.getUnit()); // set a parameter as a system parameter deflt.setSystem(True); // check if a parameter is a system parameter AlwaysAssertExit(deflt.isSystem()); // set an index for a program parameter deflt.setIndex(2); // get the index of a parameter AlwaysAssertExit(2==deflt.getIndex()); cout << "OK" << endl; } catch(std::exception& x) { cout << "Caught exception" << endl; cout << "Message is: " << x.what() << endl; } } casacore-3.7.1/casa/Json.h000066400000000000000000000053161476623553700153220ustar00rootroot00000000000000//# Json.h: Classes to read or write a Json file //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_JSON_H #define CASA_JSON_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // // Classes to read or write a JSON file // // // // This module handles JSON input and output. It supports the full Json // syntax (including null values) with the addition of complex numbers // represented as a nested JSON struct with fields "r" and "i". //
        NaN and infinite floating point numbers are written as a null value. //
        It supports comments in the JSON file using C, C++ or Python style // delimiters. // // It consists of the following classes: //
          //
        • JsonOut // to create and populate a JSON file. Besides support of scalar values // and std::vector, it also supports Array, Record and ValueHolder objects. //
        • JsonParser // to parse a JSON file and store the results in a JsonKVMap object. // The parser only accepts the rather strict JSON number representation. //
        • JsonKVMap // to obtain the results from a parsed JSON file. It is possible to // obtain a (possible nested) sequence as an Array object. //
        //
        //# //#
      • //# // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Json/000077500000000000000000000000001476623553700151445ustar00rootroot00000000000000casacore-3.7.1/casa/Json/JsonError.cc000066400000000000000000000026251476623553700174030ustar00rootroot00000000000000//# JsonError.cc: Error class for Json //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include namespace casacore { JsonError::JsonError (const String& message) : AipsError ("Json error: " + message) {} JsonError::~JsonError() noexcept {} } // end namespace casacore-3.7.1/casa/Json/JsonError.h000066400000000000000000000036151476623553700172450ustar00rootroot00000000000000//# JsonError.h: Error class for Json //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_JSONERROR_H #define CASA_JSONERROR_H //# Includes #include #include namespace casacore { // // // // JsonError is used in case of parser errors. // An exception with this class is thrown. The object contains // the actual error message. // // One can put a try/catch block around JsonParser::parse to // catch this error object and, for example, to output a message. // class JsonError: public AipsError { public: // Construct the error object with the given message. JsonError (const String& message); ~JsonError() noexcept; }; } // end namespace #endif casacore-3.7.1/casa/Json/JsonGram.ll000066400000000000000000000107571476623553700172270ustar00rootroot00000000000000/* //# JsonGram.ll: Scanner for Json-style key=value lines //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA */ /* yy_unput is not used, so let flex not generate it, otherwise picky compilers will issue warnings. */ %option nounput noinput %{ #undef YY_INPUT #define YY_INPUT(buf,result,max_size) result=JsonParser::input(buf,max_size) #undef YY_DECL #define YY_DECL int JsonGramlex (YYSTYPE* lvalp) %} /* Json is very strict on number representation; see json.org */ WHITE [ \t\n\r\f]* DIGIT19 [1-9] DIGIT [0-9] DIGITS {DIGIT}+ INT -?({DIGIT}|{DIGIT19}{DIGITS}) DEXP [Ee][+-]?{DIGITS} FRAC "."{DIGITS} DOUBLE {INT}({FRAC}|{DEXP}|{FRAC}{DEXP}) DBINT {DOUBLE}|{INT} REAL \"r\"{WHITE}":"{WHITE}{DBINT} IMAG \"i\"{WHITE}":"{WHITE}{DBINT} COMPLEX "{"{WHITE}{REAL}{WHITE}","{WHITE}{IMAG}{WHITE}"}" TRUE true FALSE false NULL null STRING \"(([^\"\n\\])|(\\.))*\" USTRING \"(([^\"\n\\])|(\\.))*\n COMMENT1 "#".*\n COMMENT2 "//".*\n COMMENT3 "/*".*"*/" COMMENT {COMMENT1}|{COMMENT2}|{COMMENT3} %% /* quit on EOF */ <> { yyterminate(); } /* Literals */ {COMPLEX} { JsonParser::position() += yyleng; double valr,vali; sscanf(JsonGramtext, "{ \"r\" : %lf , \"i\" : %lf }", &valr, &vali); lvalp->val = new JsonValue (DComplex(valr, vali)); return LITERAL; } {DOUBLE} { JsonParser::position() += yyleng; double val; sscanf(JsonGramtext, "%lf", &val); lvalp->val = new JsonValue (val); return LITERAL; } {INT} { JsonParser::position() += yyleng; Int64 ival = atol(JsonGramtext); double dval = atof(JsonGramtext); /* Handle integers exceeding integer precision as doubles */ if (ival < dval-0.1 || ival > dval+0.1) { lvalp->val = new JsonValue (dval); } else { lvalp->val = new JsonValue (ival); } return LITERAL; } {TRUE} { JsonParser::position() += yyleng; lvalp->val = new JsonValue (True); return LITERAL; } {FALSE} { JsonParser::position() += yyleng; lvalp->val = new JsonValue (False); return LITERAL; } {NULL} { JsonParser::position() += yyleng; lvalp->val = new JsonValue(); return LITERAL; } /* Strings can have quotes which have to be removed. Names can have escape characters to be removed. */ {STRING} { JsonParser::position() += yyleng; lvalp->val = new JsonValue (JsonParser::removeEscapes (String(JsonGramtext+1, yyleng-2))); return STRING; } ":" { JsonParser::position() += yyleng; return COLON; } "," { JsonParser::position() += yyleng; return COMMA; } "[" { JsonParser::position() += yyleng; return LBRACKET; } "]" { JsonParser::position() += yyleng; return RBRACKET; } "\{" { JsonParser::position() += yyleng; return LBRACE; } "\}" { JsonParser::position() += yyleng; return RBRACE; } /* Whitespace is skipped */ {WHITE} { JsonParser::position() += yyleng; } /* Comments are skipped */ {COMMENT} { JsonParser::position() += yyleng; } /* An unterminated string is an error */ {USTRING} { throw JsonError("JsonGram: Unterminated string"); } /* Any other character is invalid */ . { return TOKENERROR; } %% casacore-3.7.1/casa/Json/JsonGram.yy000066400000000000000000000053301476623553700172500ustar00rootroot00000000000000/* //# JsonGram.yy: Parser for Json-style key:value lines //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA */ %{ using namespace casacore; %} %pure-parser /* make parser re-entrant */ %union { JsonValue* val; JsonKVMap* block; std::vector* vec; } %{ int JsonGramlex (YYSTYPE*); %} %token LITERAL %token STRING %token COLON %token COMMA %token LBRACKET %token RBRACKET %token LBRACE %token RBRACE %token TOKENERROR %type keyvals %type value %type valuesl %type values %% /* Grammar rules and actions follow */ json: LBRACE keyvals RBRACE { JsonParser::setMap ($2); } ; keyvals: keyvals COMMA STRING COLON value { $$ = $1; (*$$)[$3->getString()] = *$5; delete $3; delete $5; } | STRING COLON value { $$ = new JsonKVMap; (*$$)[$1->getString()] = *$3; delete $1; delete $3; } | { /* empty 'record' */ $$ = new JsonKVMap; } ; value: valuesl { $$ = $1; } | LBRACKET values RBRACKET { $$ = new JsonValue (*$2); delete $2; } | LBRACE keyvals RBRACE { $$ = new JsonValue (*$2); delete $2; } ; values: values COMMA value { $$ = $1; $$->push_back (*$3); delete $3; } | value { $$ = new std::vector(); $$->push_back (*$1); delete $1; } ; valuesl: LITERAL { $$ = $1; } | STRING { $$ = $1; } %% casacore-3.7.1/casa/Json/JsonKVMap.cc000066400000000000000000000074761476623553700173010ustar00rootroot00000000000000//# JsonKVMap.cc: Class to hold a collection of parameter name/value pairs. //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include using namespace std; namespace casacore { JsonKVMap::JsonKVMap() {} JsonKVMap::JsonKVMap (const JsonKVMap& that) : map (that) {} JsonKVMap::~JsonKVMap() {} JsonKVMap& JsonKVMap::operator= (const JsonKVMap& that) { if (this != &that) { map::operator= (that); } return *this; } const JsonValue& JsonKVMap::get (const String& name) const { const_iterator value = find(name); if (value == end()) { throw JsonError("JsonKVMap: unknown key " + name); } return value->second; } Bool JsonKVMap::getBool (const String& name, Bool defVal) const { const_iterator value = find(name); if (value == end()) { return defVal; } return value->second.getBool(); } Int64 JsonKVMap::getInt (const String& name, Int64 defVal) const { const_iterator value = find(name); if (value == end()) { return defVal; } return value->second.getInt(); } double JsonKVMap::getDouble (const String& name, double defVal) const { const_iterator value = find(name); if (value == end()) { return defVal; } return value->second.getDouble(); } DComplex JsonKVMap::getDComplex (const String& name, const DComplex& defVal) const { const_iterator value = find(name); if (value == end()) { return defVal; } return value->second.getDComplex(); } const String& JsonKVMap::getString (const String& name, const String& defVal) const { const_iterator value = find(name); if (value == end()) { return defVal; } return value->second.getString(); } Record JsonKVMap::toRecord() const { Record rec; for (const_iterator iter=begin(); iter!=end(); ++iter) { rec.defineFromValueHolder (iter->first, iter->second.getValueHolder()); } return rec; } void JsonKVMap::show (ostream& os) const { for (const_iterator iter=begin(); iter!=end(); ++iter) { os << iter->first << "\t= " << iter->second << endl; } } ostream& operator<< (ostream& os, const JsonKVMap& param) { for (JsonKVMap::const_iterator iter=param.begin(); iter!=param.end(); ++iter) { if (iter != param.begin()) { os << ", "; } os << '"' << iter->first << '"' << ':' << iter->second; } return os; } } // end namespace casacore-3.7.1/casa/Json/JsonKVMap.h000066400000000000000000000073651476623553700171400ustar00rootroot00000000000000//# JsonKVMap.h: Class to hold a collection of JSON key:value pairs //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_JSONKVMAP_H #define CASA_JSONKVMAP_H #include #include #include namespace casacore { //# Forward Declarations class ValueHolder; // // Class to hold a collection of JSON key:value pairs. // // // // //# //# // // A JsonKVMap object is the result of a JSON file parsed by JsonParser. // It is a map of name to a JsonValue object holding an arbitrary value // (including a JsonKVMap for nested structs). // // JsonKVMap has functions to test if a given field is present and to // get its JsonValue. It also has functions to get a scalar value // where a default value is used if the key is undefined. // // JsonKVMap is derived from std::map, so all its functions are available. // Iterators make standard iteration possible. // // // JSON is a commonly used interchange format. // //# //#
      • //# class JsonKVMap: public std::map { public: // Define the iterator types. typedef std::map::const_iterator const_iterator; typedef std::map::iterator iterator; // Construct an empty map. JsonKVMap(); // Copy constructor (copy semantics) JsonKVMap (const JsonKVMap& that); ~JsonKVMap(); // Assignment (copy semantics) JsonKVMap& operator= (const JsonKVMap& that); // Is a key defined? Bool isDefined (const String& name) const { return find(name) != end(); } // Get the value of a key. An exception is thrown if undefined. const JsonValue& get (const String& name) const; // \name Get the typed value of a key // Use the default if not existing. // Bool getBool (const String& name, Bool defVal) const; Int64 getInt (const String& name, Int64 defVal) const; double getDouble (const String& name, double defVal) const; DComplex getDComplex (const String& name, const DComplex& defVal) const; const String& getString (const String& name, const String& defVal) const; // // Convert the map to a Record. Record toRecord() const; // \name Show the contents of the object // void show (ostream&) const; friend ostream& operator<< (ostream&, const JsonKVMap&); // }; } //end namespace #endif casacore-3.7.1/casa/Json/JsonOut.cc000066400000000000000000000210061476623553700170530ustar00rootroot00000000000000//# JsonOut.cc: Fill a file or stream in Json format //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include //# for iscntrl namespace casacore { //# NAMESPACE CASACORE - BEGIN JsonOut::JsonOut() : itsStream (cout), itsLevel (0) {} JsonOut::JsonOut (const String& name) : itsFile (name.chars()), itsStream (itsFile), itsLevel (0) {} JsonOut::JsonOut (ostream& os) : itsStream (os), itsLevel (0) {} // Close the stream. JsonOut::~JsonOut() {} void JsonOut::start (const String& commentStart, const String& commentEnd, const String& indent) { AlwaysAssert (itsLevel==0, JsonError); itsStream << '{' << endl; itsIndent = indent; itsIndentStep = indent; itsCommentStart = commentStart; itsCommentEnd = commentEnd; itsLevel = 1; itsFirstName.resize (1); itsFirstName[0] = True; } void JsonOut::end() { itsLevel--; AlwaysAssert (itsLevel==0, JsonError); itsIndent.clear(); itsStream << '}' << endl; } void JsonOut::startNested (const String& name, const String& comment) { AlwaysAssert (itsLevel>0, JsonError); writeComment (comment); putName (name); itsStream << '{' << endl; itsIndent += itsIndentStep; itsLevel++; itsFirstName.resize (itsLevel); itsFirstName[itsLevel-1] = True; } void JsonOut::endNested() { itsLevel--; AlwaysAssert (itsLevel>0, JsonError); itsIndent = itsIndent.substr (0, itsIndent.size() - itsIndentStep.size()); itsStream << itsIndent << '}' << endl; } void JsonOut::writeKV (const String& name, const ValueHolder& vh) { if (vh.isNull()) { putNull(); } else { switch (vh.dataType()) { case TpBool: writeKV (name, vh.asBool()); break; case TpChar: case TpUChar: case TpShort: case TpUShort: case TpInt: case TpUInt: case TpInt64: writeKV (name, vh.asInt64()); break; case TpFloat: writeKV (name, vh.asFloat()); break; case TpDouble: writeKV (name, vh.asDouble()); break; case TpComplex: writeKV (name, vh.asComplex()); break; case TpDComplex: writeKV (name, vh.asDComplex()); break; case TpString: writeKV (name, vh.asString()); break; case TpArrayBool: writeKV (name, vh.asArrayBool()); break; case TpArrayChar: case TpArrayUChar: case TpArrayShort: case TpArrayUShort: case TpArrayInt: case TpArrayUInt: case TpArrayInt64: writeKV (name, vh.asArrayInt64()); break; case TpArrayFloat: writeKV (name, vh.asArrayFloat()); break; case TpArrayDouble: writeKV (name, vh.asArrayDouble()); break; case TpArrayComplex: writeKV (name, vh.asArrayComplex()); break; case TpArrayDComplex: writeKV (name, vh.asArrayDComplex()); break; case TpArrayString: writeKV (name, vh.asArrayString()); break; case TpRecord: writeKV (name, vh.asRecord()); break; default: throw JsonError("JsonOut: unknown ValueHolder datatype"); } } } void JsonOut::writeComment (const String& comment) { if (!itsCommentStart.empty() && !comment.empty()) { itsStream << itsIndent << ' ' << itsCommentStart << ' ' << comment; if (!itsCommentEnd.empty()) { itsStream << itsCommentEnd; } itsStream << endl; } } String JsonOut::indentValue (const String& indent, const String& name) const { String extra(" "); return indent + extra.substr(0, std::min(15,int(name.size())) + 5); } void JsonOut::putName (const String& name) { itsStream << itsIndent; if (itsFirstName[itsLevel-1]) { itsStream << ' '; itsFirstName[itsLevel-1] = False; } else { itsStream << ','; } itsStream << '"' << name << "\": "; } void JsonOut::putNull() { itsStream << "null"; } void JsonOut::put (Bool value) { itsStream << (value ? "true" : "false"); } void JsonOut::put (Float value) { if (! isFinite(value)) { putNull(); } else { char buf[16]; snprintf (buf, sizeof(buf), "%.7g", value); // Add a decimal point if needed, otherwise it is integer. unsigned i; for (i=0; i(in[i]); out.append (oss.str()); } else { out.append (in[i]); } } } return out; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Json/JsonOut.h000066400000000000000000000215161476623553700167230ustar00rootroot00000000000000//# JsonOut.h: Fill a file or stream in JSON format //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_JSONOUT_H #define CASA_JSONOUT_H //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class Record; class ValueHolder; // // Class to fill a file or stream in JSON format. // // // // //# //# // // JsonOut is a class to create a JSON file. JsonParser.h can be used // to interpret a JSON file whereafter JsonKVMap gets out the information. // // Besides the standard JSON types (bool, int, float, string), sequences // and nested structs, JsonOut also supports Casacore data type (D)Complex, // Array, Record, and ValueHolder. //
        - A complex number is written as a nested struct with fields // "r" and "i". //
        - An Array is written as a (possibly nested) sequence of values. //
        - A Record is written as a nested struct; subrecords are supported. //
        - A ValueHolder is written depending on the data type it contains. //
        Note that floating point values are written with high accuracy // (7 digits for single precision, 16 digits for double precision). // // Although standard JSON does not support comments, many parsers do support // C-style and C++-style comments. JsonOut has the possibility to define // arbitrary comment delimiters (e.g., / * and * / for C-style). // If no start delimiter is given, possible comments are ignored. // // The output of JsonOut can be any iostream. If a file name is given, an // ofstream will be opened in the constructor and closed in the destructor. // The output is formatted pretty nicely. Nested structs are indented with // 2 spaces. Arrays are written with a single axis per line; continuation // lines are indented properly. String arrays have one value per line. //
        // // The following example is read back by the example in class JsonParser. // // // Create the JSON file. // JsonOut jout(fullName + "/imageconcat.json"); // // Start the JSON struct; possible comments will be ignored. // jout.start(); // // Write some fields (one line per field). // jout.write ("Version", 1); // jout.write ("DataType", "float"); // jout.write ("Axis", latticeConcat_p.axis()); // jout.write ("Images", Array(latticeNames)); // // End the JSON struct. // jout.end(); // // See tJsonOut.cc for more elaborate examples. // // // JSON is a commonly used interchange format. // // //# //#
      • //# class JsonOut { public: // The default constructor creates the output on stdout. JsonOut(); // Create the file with the given name using an ofstream object. JsonOut (const String& name); // Create the object using the given ostream object. JsonOut (ostream& os); // Close the stream. It closes the ofstream object if created. ~JsonOut(); // Start a JSON structure by writing a { and setting the indentation. // It checks if not inside a JSON structure. // It is possible to define the comment delimiters // (e.g., / * and * / or // and empty). // If commentStart is empty, possible comments are ignored. void start (const String& commentStart=String(), const String& commentEnd=String(), const String& indent=" "); // End a structure by clearing the indentation and writing a }. // It checks if inside a JSON structure. void end(); // Start a nested structure; i.e., a field with a structured value. // It writes the name and opening brace and increments the indentation. // If supported, the comment is written on a line preceeding the key line. void startNested (const String& name, const String& comment=String()); // End a nested structure. // It decrements the indentation and writes the closing brace. void endNested(); // Write one or more lines defining a keyword-value pair, where value // can be of any type including Array, Record, and ValueHolder. // A non-finite floating point number and a null ValueHolder are // written as a null value. // If supported, the comment is written on a line preceeding the // 'key:value' line. template void write (const String& name, T value, const String& comment=String()); // Write a comment on a separate line. // If comments are not supported, an empty line is written. void writeComment (const String& comment); // Write a null value. void putNull(); // Put a scalar value with sufficient accuracy. // A Complex value is written as a nested JSON structure // with fields r and i. // A string is enclosed in quotes and escaped where necessary. // A NaN is written as a null value. //
        These functions are meant for internal use by the 'write' function. // template void put (T value); void put (Bool value); void put (Float value); void put (Double value); void put (const Complex& value); void put (const DComplex& value); void put (const char* value); void put (const String& value); // // Put a line defining an array value. Multi-dim arrays are written as // nested [] lines. // Normally the values of the first dimension are written on a single line, // but for string values a line per value is used. //
        These functions are meant for internal use by the 'write' function. template void putArray (const Array& value, const String& indent, Bool firstLine); void putArray (const Array& value, const String& indent, Bool firstLine); template void putArray (const Array& value, const String& indent, Bool firstLine, Bool valueEndl); // Escape special characters (including control characters) in a string. static String escapeString (const String& in); private: // Copy constructor cannot be used. JsonOut (const JsonOut& other); // Assignment cannot be used. JsonOut& operator= (const JsonOut& other); // Write the name. void putName (const String& name); // General function to write a key and value. // Specializations exist for particular data types. template void writeKV (const String& name, T value); // Write a key and array value. template void writeKV (const String& name, const Array& value); // Write a key and valueholder. void writeKV (const String& name, const ValueHolder& vh); // Put a Record which is written as a {} structure. // The Record can be nested. void put (const Record&); // Get the indentation after a name. // It indents with the length of the name (including quotes and colon) // with a maximum of 20 spaces. String indentValue (const String& indent, const String& name) const; //# Data fields. std::ofstream itsFile; std::ostream& itsStream; String itsIndent; String itsIndentStep; int itsLevel; String itsCommentStart; String itsCommentEnd; vector itsFirstName; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/casa/Json/JsonOut.tcc000066400000000000000000000070531476623553700172450ustar00rootroot00000000000000//# JsonOut.tcc: Fill a file or stream in Json format //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template inline void JsonOut::write (const String& name, T value, const String& comment) { writeComment (comment); putName (name); writeKV (name, value); itsStream << endl; } template inline void JsonOut::writeKV (const String&, T value) { put (value); } template inline void JsonOut::writeKV (const String& name, const Array& value) { // Use extra indentation for possible continuation lines. putArray (value, indentValue(itsIndent, name), True); } template inline void JsonOut::put (T value) { itsStream << value; } template inline void JsonOut::putArray (const Array& arr, const String& indent, Bool firstLine) { putArray (arr, indent, firstLine, False); } inline void JsonOut::putArray (const Array& arr, const String& indent, Bool firstLine) { putArray (arr, indent, firstLine, True); } template void JsonOut::putArray (const Array& arr, const String& indent, Bool firstLine, Bool valueEndl) { if (!firstLine) itsStream << indent; itsStream << '['; Bool first = True; if (arr.ndim() <= 1) { size_t todo = arr.size(); typename Array::const_iterator iterEnd = arr.end(); for (typename Array::const_iterator iter=arr.begin(); iter!=iterEnd; ++iter) { if (first) { first = False; } else if (!valueEndl) { itsStream << ", "; } else { itsStream << indent << ' '; } put (*iter); todo--; if (valueEndl && todo > 0) { itsStream << ',' << endl; } } } else { ArrayIterator iter(arr, IPosition(1, arr.ndim()-1), False); while (! iter.pastEnd()) { if (!first) { itsStream << ',' << endl; } putArray (iter.array(), indent+' ', first); first = False; iter.next(); } } itsStream << ']'; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Json/JsonParser.cc000066400000000000000000000135271476623553700175510ustar00rootroot00000000000000//# JsonParser.cc: Class for parsing Json key-value lines //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include //# stdlib.h is needed for bison 1.28 and needs to be included here //# (before the flex/bison files). //# Bison defines WHERE, which is also defined in LogOrigin.h (which //# is included in auto-template mode). //# So undefine WHERE first. #undef WHERE #include //# Let clang and gcc ignore some warnings in bison/flex code. //# Undef YY_NULL because flex can redefine it slightly differently (giving a warning). #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpragmas" #pragma GCC diagnostic ignored "-Wdeprecated-register" #pragma GCC diagnostic ignored "-Wsign-compare" #include "JsonGram.ycc" // bison output #ifdef YY_NULL # undef YY_NULL #endif #include "JsonGram.lcc" // flex output #pragma GCC diagnostic pop //# Define extern symbols in JsonGram.cc. //extern int yy_start; extern char* JsonGramtext; extern FILE* JsonGramin; extern int JsonGramparse(); extern void JsonGramrestart(FILE*); namespace casacore { // Initialize statics. int JsonParser::theirPosition = 0; const char* JsonParser::theirCommand = 0; JsonKVMap* JsonParser::theirJsonMap = 0; JsonKVMap JsonParser::parseFile (const String& fileName) { String command; char buf[4096]; std::ifstream ifs(fileName.c_str()); if (!ifs) { throw JsonError("Json file " + fileName + " could not be opened"); } while (ifs.getline(buf, sizeof(buf))) { command += buf; command += '\n'; } return parse(command); } JsonKVMap JsonParser::parse (const String& command) { // Return an empty map if the command is empty. Bool empty = true; for (size_t i=0; i= max_size) { break; // get max. max_size char. } buf[nr++] = *theirCommand++; } return nr; } String JsonParser::removeEscapes (const String& in) { String out; String::size_type leng = in.length(); for (String::size_type i=0; i #include extern "C" { int JsonGramwrap(); // yywrap } namespace casacore { //# Forward Declarations class JsonValue; class JSonKVMap; // // Class for parsing Json-style key:value lines. // // // // //# //# // // JsonParser is a class for parsing JSON files. Its function 'parse' // is the main function to do so. // It can handle any JSON file (not only those generated by JsonOut). // It supports (i.e., strips) possible comments in C, C++ and Python style. // It also supports complex numbers (structs with fields "r" and "i"). // Escaped characters in a string value are translated into their ASCII counterparts. // // The result of the parser is a JsonKVMap object containing all fields // and values (scalars, arrays and structs, possibly nested in any way). // The values in the map are stored as JsonValue objects, which have functions to // get the value with the proper type. // // // The following example is the opposite of the one given for class JsonOut. // // // Parse the given JSON file. // JsonKVMap jmap = JsonParser::parseFile (fileName); // // Check if the version is correct. // AlwaysAssert (jmap.getInt("Version", 1) == 1, AipsError); // uInt axis = jmap.get("Axis").getInt(); // // Get the vector of names from the map and JsonValue. // Vector names(jmap.get("Images").getArrayString()); // // // // JSON is a commonly used interchange format. // However, commonly available parsers do not support data type Complex. Also, comments // are often not supported (alas standard Json does not allow comments). // //# //#
      • //# class JsonParser { public: // Parse the command in the given string and return the resulting map. static JsonKVMap parse (const String& command); // Parse the given file and return the resulting map. // Comments are ignored; they can be indicated by // or # till eol // or be enclosed in / * and * /. static JsonKVMap parseFile (const String& fileName); // Give the next chunk of input for the scanner. static int input (char* buf, int max_size); // Give the current position (for read or update). static int& position() { return theirPosition; } // Remove all possible escape characters and convert as needed (including \uxxxx). static String removeEscapes (const String& in); // Let the parser set the final KeyValueMap. static void setMap (JsonKVMap* map) { theirJsonMap = map; } private: static int theirPosition; static const char* theirCommand; static JsonKVMap* theirJsonMap; }; // The global yyerror function for the parser. // It throws an exception with the current token. void JsonGramerror (const char*); // } // end namespace #endif casacore-3.7.1/casa/Json/JsonValue.cc000066400000000000000000000344461476623553700173740ustar00rootroot00000000000000//# JsonValue.cc: Class to hold a general Json value //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include using namespace std; namespace casacore { JsonValue::JsonValue() : itsDataType (TpNumberOfTypes), // use a non-existing type itsValuePtr (0) {} JsonValue::JsonValue (Bool value) : itsDataType (TpBool), itsValuePtr (new Bool(value)) {} JsonValue::JsonValue (int value) : itsDataType (TpInt64), itsValuePtr (new Int64(value)) {} JsonValue::JsonValue (Int64 value) : itsDataType (TpInt64), itsValuePtr (new Int64(value)) {} JsonValue::JsonValue (double value) : itsDataType (TpDouble), itsValuePtr (new double(value)) {} JsonValue::JsonValue (const DComplex& value) : itsDataType (TpDComplex), itsValuePtr (new DComplex(value)) {} JsonValue::JsonValue (const char* value) : itsDataType (TpString), itsValuePtr (new String(value)) {} JsonValue::JsonValue (const String& value) : itsDataType (TpString), itsValuePtr (new String(value)) {} JsonValue::JsonValue (const vector& value) : itsDataType (TpOther), itsValuePtr (new vector(value)) {} JsonValue::JsonValue (const JsonKVMap& value) : itsDataType (TpRecord), itsValuePtr (new JsonKVMap(value)) {} JsonValue::JsonValue (const JsonValue& that) : itsValuePtr (0) { copyValue (that); } JsonValue& JsonValue::operator= (const JsonValue& that) { if (this != &that) { clear(); copyValue (that); } return *this; } JsonValue::~JsonValue() { clear(); } void JsonValue::clear() { if (itsValuePtr) { switch (itsDataType) { case TpBool: delete (Bool*)itsValuePtr; break; case TpInt64: delete (Int64*)itsValuePtr; break; case TpDouble: delete (double*)itsValuePtr; break; case TpDComplex: delete (DComplex*)itsValuePtr; break; case TpString: delete (String*)itsValuePtr; break; case TpOther: delete (vector*)itsValuePtr; break; case TpRecord: delete (JsonKVMap*)itsValuePtr; break; default: throw JsonError("JsonValue::clear - invalid data type"); } } itsValuePtr = 0; } void JsonValue::copyValue (const JsonValue& that) { itsDataType = that.itsDataType; if (that.itsValuePtr) { switch (itsDataType) { case TpBool: itsValuePtr = new Bool (that.getBool()); break; case TpInt64: itsValuePtr = new Int64 (that.getInt()); break; case TpDouble: itsValuePtr = new double (that.getDouble()); break; case TpDComplex: itsValuePtr = new DComplex (that.getDComplex()); break; case TpString: itsValuePtr = new String (that.getString()); break; case TpOther: itsValuePtr = new vector (that.getVector()); break; case TpRecord: itsValuePtr = new JsonKVMap (that.getValueMap()); break; default: throw JsonError("JsonValue::copyValue - invalid data type"); } } } size_t JsonValue::size() const { if (isNull()) { return 0; } switch (itsDataType) { case TpOther: return ((vector*)itsValuePtr)->size(); break; case TpRecord: return ((JsonKVMap*)itsValuePtr)->size(); break; default: return 1; } } DataType JsonValue::arrayDataType() const { DataType vdt = dataType(); if (vdt != TpOther) { return vdt; } return vectorDataType (getVector()); } DataType JsonValue::vectorDataType (const vector& vec) const { DataType vdt = TpOther; // indicates first time for (vector::const_iterator iter=vec.begin(); iter!=vec.end(); ++iter) { DataType dt = iter->dataType(); if (dt == TpRecord) { return dt; } else if (vdt == TpOther) { if (dt == TpOther) { dt = vectorDataType (iter->getVector()); } vdt = dt; } else if (dt != vdt) { if (dt == TpBool || dt == TpString || vdt == TpBool || vdt == TpString) { return TpRecord; } if (dt == TpDComplex || vdt == TpDComplex) { vdt = TpDComplex; } else { vdt = TpDouble; } } } return vdt; } IPosition JsonValue::shape() const { if (dataType() == TpRecord) { throw JsonError("JsonValue::shape - vector contains a ValueMap"); } if (dataType() == TpOther) { return vectorShape (getVector()); } return IPosition(1,1); } IPosition JsonValue::vectorShape (const vector& vec) const { IPosition shp(1,0); IPosition nshp; Bool first = True; Bool nested = False; for (vector::const_iterator iter=vec.begin(); iter!=vec.end(); ++iter) { if (iter->dataType() == TpRecord) { throw JsonError("JsonValue::shape - vector contains a ValueMap"); } shp[0]++; if (first) { first = False; if (iter->isVector()) { nested = True; nshp = iter->shape(); } } else { if (nested != iter->isVector()) { throw JsonError("JsonValue::shape - vector contains scalars and vectors"); } if (nested && !nshp.isEqual (iter->shape())) { throw JsonError("JsonValue::shape - irregular nested vector sizes"); } } } return nshp.concatenate (shp); } ValueHolder JsonValue::getValueHolder() const { if (isNull()) { return ValueHolder(); } switch (itsDataType) { case TpBool: return ValueHolder(*(Bool*)itsValuePtr); case TpInt64: return ValueHolder(*(Int64*)itsValuePtr); case TpDouble: return ValueHolder(*(double*)itsValuePtr); case TpDComplex: return ValueHolder(*(DComplex*)itsValuePtr); case TpString: return ValueHolder(*(String*)itsValuePtr); case TpRecord: return ValueHolder(((JsonKVMap*)itsValuePtr)->toRecord()); case TpOther: break; // a vector which is handled below default: throw JsonError("JsonValue::getValueHolder - invalid data type"); } vector vec = getVector(); switch (vectorDataType(vec)) { case TpBool: return ValueHolder(Vector(getVecBool())); case TpInt64: return ValueHolder(Vector(getVecInt())); case TpDouble: return ValueHolder(Vector(getVecDouble())); case TpDComplex: return ValueHolder(Vector(getVecDComplex())); case TpString: return ValueHolder(Vector(getVecString())); case TpOther: return ValueHolder(1, True); // untyped array with 1 axis default: throw JsonError("JsonValue::getValueHolder - vector of mixed data types"); } } Bool JsonValue::getBool() const { switch (itsDataType) { case TpBool: return *(Bool*)itsValuePtr; case TpInt64: return (*(Int64*)itsValuePtr != 0); default: throw JsonError("JsonValue::getBool - invalid data type"); } } Int64 JsonValue::getInt() const { switch (itsDataType) { case TpInt64: return *(Int64*)itsValuePtr; default: throw JsonError("JsonValue::getInt - invalid data type"); } } double JsonValue::getDouble() const { if (isNull()) { return doubleNaN(); } switch (itsDataType) { case TpInt64: return *(Int64*)itsValuePtr; case TpDouble: return *(double*)itsValuePtr; default: throw JsonError("JsonValue::getDouble - invalid data type"); } } DComplex JsonValue::getDComplex() const { if (isNull()) { return DComplex(doubleNaN(), doubleNaN()); } switch (itsDataType) { case TpInt64: return DComplex(*(Int64*)itsValuePtr, 0.0); case TpDouble: return DComplex(*(double*)itsValuePtr, 0.0); case TpDComplex: return *(DComplex*)itsValuePtr; default: throw JsonError("JsonValue::getDComplex - invalid data type"); } } const String& JsonValue::getString() const { switch (itsDataType) { case TpString: return *(String*)itsValuePtr; default: throw JsonError("JsonValue::getString - invalid data type"); } } vector JsonValue::getVecBool() const { if (itsDataType == TpOther) { const vector& kvvec = *(const vector*)itsValuePtr; vector vec(kvvec.size()); for (size_t i=0; i vec(1); vec[0] = getBool(); return vec; } vector JsonValue::getVecInt() const { if (itsDataType == TpOther) { const vector& kvvec = *(const vector*)itsValuePtr; vector vec(kvvec.size()); for (size_t i=0; i vec(1); vec[0] = getInt(); return vec; } vector JsonValue::getVecDouble() const { if (itsDataType == TpOther) { const vector& kvvec = *(const vector*)itsValuePtr; vector vec(kvvec.size()); for (size_t i=0; i vec(1); vec[0] = getDouble(); return vec; } vector JsonValue::getVecDComplex() const { if (itsDataType == TpOther) { const vector& kvvec = *(const vector*)itsValuePtr; vector vec(kvvec.size()); for (size_t i=0; i vec(1); vec[0] = getDComplex(); return vec; } vector JsonValue::getVecString() const { if (itsDataType == TpOther) { const vector& kvvec = *(const vector*)itsValuePtr; vector vec(kvvec.size()); for (size_t i=0; i vec(1); vec[0] = getString(); return vec; } const vector& JsonValue::getVector() const { if (itsDataType == TpOther) { return *(vector*)itsValuePtr; } throw JsonError("JsonValue::getVector - invalid data type"); } const JsonKVMap& JsonValue::getValueMap() const { switch (itsDataType) { case TpRecord: return *(JsonKVMap*)itsValuePtr; default: throw JsonError("JsonValue::getValueMap - invalid data type"); } } void JsonValue::get (JsonKVMap& value) const { value = getValueMap(); } Array JsonValue::getArrayBool() const { Array arr(shape()); Bool* data = arr.data(); fillArray (data, data+arr.size(), getVector()); return arr; } Array JsonValue::getArrayInt() const { Array arr(shape()); Int64* data = arr.data(); fillArray (data, data+arr.size(), getVector()); return arr; } Array JsonValue::getArrayDouble() const { Array arr(shape()); double* data = arr.data(); fillArray (data, data+arr.size(), getVector()); return arr; } Array JsonValue::getArrayDComplex() const { Array arr(shape()); DComplex* data = arr.data(); fillArray (data, data+arr.size(), getVector()); return arr; } Array JsonValue::getArrayString() const { Array arr(shape()); String* data = arr.data(); fillArray (data, data+arr.size(), getVector()); return arr; } ostream& operator<< (ostream& os, const JsonValue& param) { JsonOut js(os); if (param.isNull()) { js.putNull(); } else { switch (param.itsDataType) { case TpBool: js.put (*(Bool*)(param.itsValuePtr)); break; case TpInt64: js.put (*(Int64*)(param.itsValuePtr)); break; case TpDouble: js.put (*(double*)(param.itsValuePtr)); break; case TpDComplex: js.put (*(DComplex*)(param.itsValuePtr)); break; case TpString: js.put (*(String*)(param.itsValuePtr)); break; case TpOther: { const vector& vec = param.getVector(); os << '['; for (size_t i=0; i 0) { os << ','; } os << vec[i]; } os << ']'; } break; case TpRecord: { const JsonKVMap& blk = param.getValueMap(); os << '['; if (blk.size() == 0) { os << '='; } else { os << blk; } os << ']'; } break; default: throw JsonError("JsonValue::operator<< - invalid data type"); } } return os; } } // end namespace casacore-3.7.1/casa/Json/JsonValue.h000066400000000000000000000211161476623553700172240ustar00rootroot00000000000000//# JsonValue.h: Class to hold any JSON value //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_JSONVALUE_H #define CASA_JSONVALUE_H //# Includes #include #include #include #include #include #include #include #include namespace casacore { //# Forward Declarations class JsonKVMap; class ValueHolder; class IPosition; // // Class to hold any JSON value // // // // //# //# // // Class JsonValue can hold an arbitrary JSON value which can be a scalar, // a JsonKVMap object, or a vector of JsonValue objects. In this way // JSON values can be nested in any way. // // Internally scalar values are kept as Bool, Int64, Double, DComplex or // String values. The functions to obtain the value convert if possible. // Note that conversion from Int64 to Bool is supported. // The value can also be obtained as a ValueHolder object making it easier // to use in other Casacore code. // Null is also a valid JsonValue. A null value can be obtained as a // floating point value resulting in a NaN. It can also be obtained as a // null ValueHolder. Getting it for other types results in an exception. // // It is possible to obtain the value as a multi-dimensional Array object // if the values are regular, thus if nested vectors have the same sizes. // The data type of an Array is the 'highest' data type of a value in it. // // Normally a JsonValue object is created by JsonParser and is the // interface to obtain a value of a field in a parsed JSON file. // However, users can create JsonValue objects as well. // // // JSON is a commonly used interchange format. // //# //#
      • //# class JsonValue { public: // The default constructor results in a null value. JsonValue(); // Construct value with given type. // JsonValue (Bool); JsonValue (int); JsonValue (Int64); JsonValue (double); JsonValue (const DComplex&); JsonValue (const char*); JsonValue (const String&); JsonValue (const std::vector&); JsonValue (const JsonKVMap&); // // Copy constructor (copy semantics). JsonValue (const JsonValue&); // Assignment (copy semantics). JsonValue& operator= (const JsonValue&); ~JsonValue(); // Is the value a null value? Bool isNull() const { return itsValuePtr == 0; } // Is the value a vector? Bool isVector() const { return itsDataType == TpOther; } // Is the value a value map? Bool isValueMap() const { return itsDataType == TpRecord; } // Return the size of a value vector or map (1 is returned for a scalar). size_t size() const; // Get the data type of the value. // A ValueMap is returned as TpRecord, a vector as TpOther. DataType dataType() const { return itsDataType; } // Get the most common data type of the value inside a possibly // nested vector. //
        - If the value is a single value, that type is returned. //
        - If any vector value is a ValueMap, TpRecord is returned. //
        - If any vector contains non-matching data types, TpOther is // returned. //
        - Otherwise the 'highest' data type is returned. // DataType arrayDataType() const; DataType vectorDataType (const std::vector& vec) const; // // Get the shape of an array (possibly nested vector). // An exception is thrown if a vector contains a ValueMap or if // the array shape is irregular (nested vectors have different sizes). IPosition shape() const; IPosition vectorShape (const std::vector& vec) const; // Get the value as a ValueHolder. // A null value results in a null (empty) ValueHolder. // An exception is thrown if the value cannot be represented as such, // because it is a vector of differently typed values or nested vectors. ValueHolder getValueHolder() const; // Get the value in the given data type. // Numeric data type promotion can be done as well as conversion of // integer to bool (0=False, other=True). An exception is thrown if // a mismatching data type is used. // Note that a null value can only be obtained as double (giving NaN). // Bool getBool() const; Int64 getInt() const; double getDouble() const; DComplex getDComplex() const; const String& getString() const; // // As above, but get the value as a vector. // If the value is a scalar, a vector with length 1 is returned. // std::vector getVecBool() const; std::vector getVecInt() const; std::vector getVecDouble() const; std::vector getVecDComplex() const; std::vector getVecString() const; const std::vector& getVector() const; // // Get the value as a JsonKVMap (no conversion is possible). const JsonKVMap& getValueMap() const; // Get the value as an Array. The value must be a scalar or a // regularly nested vector. // Array getArrayBool() const; Array getArrayInt() const; Array getArrayDouble() const; Array getArrayDComplex() const; Array getArrayString() const; // // Get functions for templated purposes // void get (Bool& value) const { value = getBool(); } void get (Int64& value) const { value = getInt(); } void get (double& value) const { value = getDouble(); } void get (DComplex& value) const { value = getDComplex(); } void get (String& value) const { value = getString(); } void get (std::vector& value) const { value = getVecBool(); } void get (std::vector& value) const { value = getVecInt(); } void get (std::vector& value) const { value = getVecDouble(); } void get (std::vector& value) const { value = getVecDComplex(); } void get (std::vector& value) const { value = getVecString(); } void get (std::vector& value) const { value = getVector(); } void get (JsonKVMap& value) const; // // Show value on given ostream. friend ostream& operator<< (ostream&, const JsonValue&); private: // Remove the value. void clear(); // Copy the value from another one. void copyValue (const JsonValue& that); // Fill an array from nested vector in a recursive way. template T* fillArray (T* data, const T* dataEnd, const std::vector& vec) const { for (std::vector::const_iterator iter=vec.begin(); iter!=vec.end(); ++iter) { if (iter->dataType() == TpOther) { data = fillArray (data, dataEnd, iter->getVector()); } else { AlwaysAssert (dataget (*data); data++; } } return data; } DataType itsDataType; void* itsValuePtr; }; } // end namespace #endif casacore-3.7.1/casa/Json/test/000077500000000000000000000000001476623553700161235ustar00rootroot00000000000000casacore-3.7.1/casa/Json/test/CMakeLists.txt000066400000000000000000000004651476623553700206700ustar00rootroot00000000000000set (tests tJsonKVMap tJsonOut tJsonValue ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_casa) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/casa/Json/test/tJsonKVMap.cc000066400000000000000000000132461476623553700204340ustar00rootroot00000000000000//# tJsonKVMap.cc: Program to test classes JsonKVMap and JsonValue //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include using namespace casacore; using namespace std; void showpar3 (JsonKVMap& par3) { JsonOut jout; par3.show (cout); cout << par3["b1"].getBool() << ' '; jout.putArray (Vector(par3["b1"].getVecBool()), String(), True); cout << endl; cout << par3["i1"].getInt() << ' '; jout.putArray (Vector(par3["i1"].getVecInt()), String(), True); cout << endl; cout << par3["i1"].getDouble() << ' '; jout.putArray (Vector(par3["i1"].getVecDouble()), String(), True); cout << endl; cout << par3["i1"].getDComplex() << ' '; jout.putArray (Vector(par3["i1"].getVecDComplex()), String(), True); cout << endl; cout << par3["d1"].getDouble() << ' '; jout.putArray (Vector(par3["d1"].getVecDouble()), String(), True); cout << endl; cout << par3["d1"].getDComplex() << ' '; jout.putArray (Vector(par3["d1"].getVecDComplex()), String(), True); cout << endl; cout << par3["dc1"].getDComplex() << ' '; jout.putArray (Vector(par3["dc1"].getVecDComplex()), String(), True); cout << endl; cout << par3["s1"].getString() << ' '; jout.putArray (Vector(par3["s1"].getVecString()), String(), True); cout << endl; cout << par3["b1"].dataType() << ' ' << par3["b1"].isVector() << ' ' << par3["b1"].size() << endl; cout << par3["i1"].dataType() << ' ' << par3["i1"].isVector() << ' ' << par3["i1"].size() << endl; cout << par3["d1"].dataType() << ' ' << par3["d1"].isVector() << ' ' << par3["d1"].size() << endl; cout << par3["dc1"].dataType() << ' ' << par3["dc1"].isVector() << ' ' << par3["dc1"].size() << endl; cout << par3["s1"].dataType() << ' ' << par3["s1"].isVector() << ' ' << par3["s1"].size() << endl; } void showpar2 (JsonKVMap& par2) { par2.show (cout); cout << par2["blk1"].dataType() << ' ' << par2["blk1"].isVector() << ' ' << par2["blk1"].size() << endl; cout << par2["blk2"].dataType() << ' ' << par2["blk2"].isVector() << ' ' << par2["blk2"].size() << endl; cout << par2["vec"].dataType() << ' ' << par2["vec"].isVector() << ' ' << par2["vec"].size() << endl; } void doIt() { JsonKVMap par; par["b1"] = true; par["i1"] = 10; par["d1"] = double(30); par["dc1"] = DComplex(60,70); par["s1"] = "abc"; par["s2"] = String("defg"); cout << "JsonKVMap par:" << endl; par.show (cout); JsonKVMap par1(par); cout << "JsonKVMap par1:" << endl; par1.show(cout); par["b2"] = false; par1 = par; cout << "JsonKVMap par1:" << endl; par1.show (cout); cout << endl <<"toRecord:" << endl << par1.toRecord() << endl; cout << "JsonKVMap par3:" << endl; JsonKVMap par3(par1); showpar3 (par3); JsonKVMap par2; par1 = par2; cout << "Empty JsonKVMap par1:" << endl; par1.show (cout); par1["blk1"] = par; par1["blk2"] = par2; vector vec; vec.push_back (true); vec.push_back (100); vec.push_back (double(100.02)); vec.push_back (DComplex (1.123456789, -3)); vec.push_back ("a"); vec.push_back (""); vec.push_back ("abc"); par1["vec"] = vec; par2 = par1; cout << "JsonKVMap par2:" << endl; showpar2 (par2); } void doItParse() { try { cout << JsonParser::parse ("{\"key1\":1}") << endl; cout << JsonParser::parse ("{\"key2\":\"abc\"}") << endl; cout << JsonParser::parse ("{\"key1\" : null, \"key2\":\"abc\"}") << endl; cout << JsonParser::parse ("{\"key1\":[1,1.3], \"key2\":\"/*abc*/\"}") << endl; cout << JsonParser::parse ("{\"key1\":{\"a\":1,\"b\":2}, \"key2\":\"abc\"}") << endl; cout << JsonParser::parse ("{\"key1\":{\"r\":1,\"i\":2}, \"key2\":\"abc\"}") << endl; cout << JsonParser::parse ("{\"key1\":1 /* ,,, */ ,\"Key2\":true } ") << endl; JsonKVMap par = JsonParser::parseFile ("tJsonKVMap.in"); cout << par << endl; Array exp(IPosition(3,4,3,2)); indgen(exp); AlwaysAssertExit (allNear(par["keyarr"].getArrayDouble(), exp, 1e-5)); } catch (std::exception& x) { cout << x.what() << endl; } } int main() { try { doIt(); doItParse(); } catch (...) { cout << "Unexpected exception" << endl; exit(1); } cout << "OK" << endl; exit(0); } casacore-3.7.1/casa/Json/test/tJsonKVMap.in000066400000000000000000000011741476623553700204520ustar00rootroot00000000000000# This defines the input for a file. { "keyx":"ab\"/cd\n", #comment "keyy" : {"aa":1, "bb":2, #cc:3 "dd":"'bc'" }, "keyb":true, "keyi":34, "keyf":24.3e1, "keyd":-14.35e2, "keyc":{"r":1,"i":2}, "keydc":{"r":3.5,"i":-4.6}, "keys":"string", "keys1":" str1 str2\u0020str3 ", #\u0020 is a space "keybv":[true,false], "keyiv":[2,3,4,-5], "keyfv":[2e2, 0.2, 2.0, 2.2, 2.0e2, 0.2e+2, -2.2e-2], "keydv":[3e3, 0.3, 3.0, 3.3, 3.0e3, 0.3e+3, -3.3e-3], "keymv":[ 24.3e0 , -14.35e2 , {"r":1,"i":2} , { "r" : 3.5 , "i" : -4.6e2 } , "string" ], "keyarr":[[[0,1,2,3],[4,5,6,7],[8,9,10,11]],[[12,13,14,15],[16,17,18,19],[20,21,22,23]]] } casacore-3.7.1/casa/Json/test/tJsonKVMap.out000066400000000000000000000032051476623553700206500ustar00rootroot00000000000000JsonKVMap par: b1 = true d1 = 30.0 dc1 = {"r":60.0, "i":70.0} i1 = 10 s1 = "abc" s2 = "defg" JsonKVMap par1: b1 = true d1 = 30.0 dc1 = {"r":60.0, "i":70.0} i1 = 10 s1 = "abc" s2 = "defg" JsonKVMap par1: b1 = true b2 = false d1 = 30.0 dc1 = {"r":60.0, "i":70.0} i1 = 10 s1 = "abc" s2 = "defg" toRecord: b1: Bool 1 b2: Bool 0 d1: Double 30 dc1: DComplex (60,70) i1: Int64 10 s1: String "abc" s2: String "defg" JsonKVMap par3: b1 = true b2 = false d1 = 30.0 dc1 = {"r":60.0, "i":70.0} i1 = 10 s1 = "abc" s2 = "defg" 1 [true] 10 [10] 10 [10.0] (10,0) [{"r":10.0, "i":0.0}] 30 [30.0] (30,0) [{"r":30.0, "i":0.0}] (60,70) [{"r":60.0, "i":70.0}] abc ["abc"] Bool 0 1 Int64 0 1 double 0 1 DComplex 0 1 String 0 1 Empty JsonKVMap par1: JsonKVMap par2: blk1 = ["b1":true, "b2":false, "d1":30.0, "dc1":{"r":60.0, "i":70.0}, "i1":10, "s1":"abc", "s2":"defg"] blk2 = [=] vec = [true,100,100.02,{"r":1.123456789, "i":-3.0},"a","","abc"] Record 0 7 Record 0 0 Other 1 7 "key1":1 "key2":"abc" "key1":null, "key2":"abc" "key1":[1,1.3], "key2":"/*abc*/" "key1":["a":1, "b":2], "key2":"abc" "key1":{"r":1.0, "i":2.0}, "key2":"abc" "Key2":true, "key1":1 "keyarr":[[[0,1,2,3],[4,5,6,7],[8,9,10,11]],[[12,13,14,15],[16,17,18,19],[20,21,22,23]]], "keyb":true, "keybv":[true,false], "keyc":{"r":1.0, "i":2.0}, "keyd":-1435.0, "keydc":{"r":3.5, "i":-4.6}, "keydv":[3000.0,0.3,3.0,3.3,3000.0,300.0,-0.0033], "keyf":243.0, "keyfv":[200.0,0.2,2.0,2.2,200.0,20.0,-0.022], "keyi":34, "keyiv":[2,3,4,-5], "keymv":[24.3,-1435.0,{"r":1.0, "i":2.0},{"r":3.5, "i":-460.0},"string"], "keys":"string", "keys1":" str1 str2 str3 ", "keyx":"ab\"/cd\n", "keyy":["aa":1, "bb":2, "dd":"'bc'"] OK casacore-3.7.1/casa/Json/test/tJsonOut.cc000066400000000000000000000061121476623553700202170ustar00rootroot00000000000000//# tJsonOut.cc: Test program for class JsonOut.cc //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include using namespace casacore; int main() { try { String indent(" "); JsonOut os("tJsonOut_tmp.txt"); os.start("#"); for (int ndim=0; ndim<4; ++ndim) { Array a(IPosition(ndim,3)); indgen(a); os.write ("arr", a); } Array as(IPosition(2,2,2)); as.data()[0] = "s0"; as.data()[1] = "s1"; as.data()[2] = "s\n2"; as.data()[3] = "s\"3"; os.write ("arrs", as, "comment1"); os.write ("key1", true); os.write ("key2", 1, "comment2"); os.write ("key3", -10); os.write ("key4", 11.5f); os.write ("keyfa", -1e20f); os.write ("keyfb", -1e-20f); os.write ("keyfc", -1.f); os.write ("key5", -13.2345); os.write ("keyda", -1e20); os.write ("keydb", -1e-20); os.write ("keydc", -1.); os.write ("keydd", -123456789012345.123456789012345); os.write ("key6", Complex(-1,2)); os.write ("key7", ValueHolder(DComplex(3,-4))); os.write ("key8", "string"); os.write ("key9", "\t str\\\"ing\n "); // str\"ing os.write ("key10", String("ab") + char(1) + "cd"); os.write ("null1", ValueHolder()); // null value os.write ("null2", floatNaN()); // null value os.write ("null3", doubleNaN()); // null value Array arr(IPosition(2,5,3)); indgen(arr); os.write ("arrf", ValueHolder(arr)); Record rec; rec.define ("recfld", 1); rec.define ("recarr", arr); Record subrec; subrec.define("subf1", 2); subrec.define("subf2", arr+float(15)); rec.defineRecord ("recsub", subrec); os.write ("rec", rec); os.end(); } catch (std::exception& x) { std::cout << x.what() << std::endl; return 1; } return 0; } casacore-3.7.1/casa/Json/test/tJsonOut.out000066400000000000000000000024761476623553700204520ustar00rootroot00000000000000{ "arr": [] ,"arr": [0, 1, 2] ,"arr": [[0, 1, 2], [3, 4, 5], [6, 7, 8]] ,"arr": [[[0, 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]]] # comment1 ,"arrs": [["s0", "s1"], ["s\n2", "s\"3"]] ,"key1": true # comment2 ,"key2": 1 ,"key3": -10 ,"key4": 11.5 ,"keyfa": -1e+20 ,"keyfb": -1e-20 ,"keyfc": -1.0 ,"key5": -13.2345 ,"keyda": -1e+20 ,"keydb": -9.999999999999999e-21 ,"keydc": -1.0 ,"keydd": -123456789012345.1 ,"key6": {"r":-1.0, "i":2.0} ,"key7": {"r":3.0, "i":-4.0} ,"key8": "string" ,"key9": "\t str\\\"ing\n " ,"key10": "ab\u0001cd" ,"null1": null ,"null2": null ,"null3": null ,"arrf": [[0.0, 1.0, 2.0, 3.0, 4.0], [5.0, 6.0, 7.0, 8.0, 9.0], [10.0, 11.0, 12.0, 13.0, 14.0]] ,"rec": { "recfld": 1 ,"recarr": [[0.0, 1.0, 2.0, 3.0, 4.0], [5.0, 6.0, 7.0, 8.0, 9.0], [10.0, 11.0, 12.0, 13.0, 14.0]] ,"recsub": { "subf1": 2 ,"subf2": [[15.0, 16.0, 17.0, 18.0, 19.0], [20.0, 21.0, 22.0, 23.0, 24.0], [25.0, 26.0, 27.0, 28.0, 29.0]] } } } casacore-3.7.1/casa/Json/test/tJsonOut.run000066400000000000000000000004241476623553700204360ustar00rootroot00000000000000#! /bin/sh #----------------------------------------------------------------------------- # Script to execute the tJsonOut program via assay. #============================================================================= $casa_checktool ./tJsonOut && cat tJsonOut_tmp.txt casacore-3.7.1/casa/Json/test/tJsonValue.cc000066400000000000000000000304271476623553700205320ustar00rootroot00000000000000//# tJsonKVMap.cc: Program to test class JsonValue //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include using namespace casacore; using namespace std; #define AssertException(cmd) \ { Bool tryFail = False; \ try { cmd ; } catch (const JsonError&) { tryFail = True; } \ AlwaysAssertExit (tryFail); \ } void doScalar() { AlwaysAssertExit (JsonValue().isNull()); AlwaysAssertExit (! JsonValue(True).isNull()); AlwaysAssertExit (! JsonValue(1).isNull()); AlwaysAssertExit (! JsonValue(1.).isNull()); AlwaysAssertExit (! JsonValue(DComplex()).isNull()); AlwaysAssertExit (! JsonValue(String()).isNull()); AlwaysAssertExit (JsonValue(True).dataType() == TpBool); AlwaysAssertExit (JsonValue(1).dataType() == TpInt64); AlwaysAssertExit (JsonValue(1.).dataType() == TpDouble); AlwaysAssertExit (JsonValue(Complex()).dataType() == TpDComplex); AlwaysAssertExit (JsonValue("").dataType() == TpString); AlwaysAssertExit (JsonValue(True).arrayDataType() == TpBool); AlwaysAssertExit (JsonValue(1).arrayDataType() == TpInt64); AlwaysAssertExit (JsonValue(1.).arrayDataType() == TpDouble); AlwaysAssertExit (JsonValue(Complex()).arrayDataType() == TpDComplex); AlwaysAssertExit (JsonValue("").arrayDataType() == TpString); AlwaysAssertExit (JsonValue(True).size() == 1); AlwaysAssertExit (JsonValue(1).size() == 1); AlwaysAssertExit (JsonValue(1.).size() == 1); AlwaysAssertExit (JsonValue(Complex()).size() == 1); AlwaysAssertExit (JsonValue("").size() == 1); AlwaysAssertExit (JsonValue(True).shape() == IPosition(1,1)); AlwaysAssertExit (JsonValue(1).shape() == IPosition(1,1)); AlwaysAssertExit (JsonValue(1.).shape() == IPosition(1,1)); AlwaysAssertExit (JsonValue(Complex()).shape() == IPosition(1,1)); AlwaysAssertExit (JsonValue("").shape() == IPosition(1,1)); AssertException (JsonValue().getBool()); AssertException (JsonValue().getInt()); AlwaysAssertExit (isNaN (JsonValue().getDouble())); AlwaysAssertExit (isNaN (JsonValue().getDComplex())); AssertException (JsonValue().getString()); AlwaysAssertExit (JsonValue(True).getBool() == True); AssertException (JsonValue(True).getInt()); AssertException (JsonValue(True).getDouble()); AssertException (JsonValue(True).getDComplex()); AssertException (JsonValue(True).getString()); AlwaysAssertExit (JsonValue(1).getBool() == True); AlwaysAssertExit (JsonValue(0).getBool() == False); AlwaysAssertExit (JsonValue(1).getInt() == 1); AlwaysAssertExit (JsonValue(1).getDouble() == 1.); AlwaysAssertExit (JsonValue(1).getDComplex() == DComplex(1,0)); AssertException (JsonValue(1).getString()); AssertException (JsonValue(-1.).getBool()); AssertException (JsonValue(-1.).getInt()); AlwaysAssertExit (JsonValue(-1.).getDouble() == -1.); AlwaysAssertExit (JsonValue(-1.).getDComplex() == DComplex(-1,0)); AssertException (JsonValue(-1.).getString()); AssertException (JsonValue(DComplex(1,2)).getBool()); AssertException (JsonValue(DComplex(1,2)).getInt()); AssertException (JsonValue(DComplex(1,2)).getDouble()); AlwaysAssertExit (JsonValue(DComplex(1,2)).getDComplex() == DComplex(1,2)); AssertException (JsonValue(DComplex(1,2)).getString()); AssertException (JsonValue("1,2").getBool()); AssertException (JsonValue("1,2").getInt()); AssertException (JsonValue("1,2").getDouble()); AssertException (JsonValue("1,2").getDComplex()); AlwaysAssertExit (JsonValue("1,2").getString() == "1,2"); } void doVector() { vector vec; AlwaysAssertExit (JsonValue(vec).dataType() == TpOther); AlwaysAssertExit (JsonValue(vec).arrayDataType() == TpOther); AlwaysAssertExit (JsonValue(vec).size() == 0); AlwaysAssertExit (JsonValue(vec).shape() == IPosition(1,0)); vec.push_back (JsonValue(10)); AlwaysAssertExit (JsonValue(vec).dataType() == TpOther); AlwaysAssertExit (JsonValue(vec).arrayDataType() == TpInt64); AlwaysAssertExit (JsonValue(vec).size() == 1); AlwaysAssertExit (JsonValue(vec).shape() == IPosition(1,1)); AlwaysAssertExit (JsonValue(vec).getVector().size() == 1); AlwaysAssertExit (JsonValue(vec).getVecBool().size() == 1); AlwaysAssertExit (JsonValue(vec).getVecInt().size() == 1); AlwaysAssertExit (JsonValue(vec).getVecDouble().size() == 1); AlwaysAssertExit (JsonValue(vec).getVecDComplex().size() == 1); AssertException (JsonValue(vec).getVecString()); AlwaysAssertExit (JsonValue(vec).getVecInt()[0] == 10); vec.push_back (JsonValue(2)); AlwaysAssertExit (JsonValue(vec).dataType() == TpOther); AlwaysAssertExit (JsonValue(vec).arrayDataType() == TpInt64); AlwaysAssertExit (JsonValue(vec).size() == 2); AlwaysAssertExit (JsonValue(vec).shape() == IPosition(1,2)); AlwaysAssertExit (JsonValue(vec).getVecBool().size() == 2); AlwaysAssertExit (JsonValue(vec).getVecInt().size() == 2); AlwaysAssertExit (JsonValue(vec).getVecDouble().size() == 2); AlwaysAssertExit (JsonValue(vec).getVecDComplex().size() == 2); AssertException (JsonValue(vec).getVecString()); AlwaysAssertExit (JsonValue(vec).getVecInt()[0] == 10); AlwaysAssertExit (JsonValue(vec).getVecInt()[1] == 2); vec.push_back (JsonValue(3.)); AlwaysAssertExit (JsonValue(vec).dataType() == TpOther); AlwaysAssertExit (JsonValue(vec).arrayDataType() == TpDouble); AlwaysAssertExit (JsonValue(vec).size() == 3); AlwaysAssertExit (JsonValue(vec).shape() == IPosition(1,3)); AssertException (JsonValue(vec).getVecBool()); AssertException (JsonValue(vec).getVecInt()); AlwaysAssertExit (JsonValue(vec).getVecDouble().size() == 3); AlwaysAssertExit (JsonValue(vec).getVecDComplex().size() == 3); AssertException (JsonValue(vec).getVecString()); AlwaysAssertExit (JsonValue(vec).getVecDouble()[0] == 10); AlwaysAssertExit (JsonValue(vec).getVecDouble()[1] == 2); AlwaysAssertExit (JsonValue(vec).getVecDouble()[2] == 3); vec.push_back (JsonValue(DComplex(-1,-2))); AlwaysAssertExit (JsonValue(vec).dataType() == TpOther); AlwaysAssertExit (JsonValue(vec).arrayDataType() == TpDComplex); AlwaysAssertExit (JsonValue(vec).size() == 4); AlwaysAssertExit (JsonValue(vec).shape() == IPosition(1,4)); AssertException (JsonValue(vec).getVecBool()); AssertException (JsonValue(vec).getVecInt()); AssertException (JsonValue(vec).getVecDouble()); AlwaysAssertExit (JsonValue(vec).getVecDComplex().size() == 4); AssertException (JsonValue(vec).getVecString()); AlwaysAssertExit (JsonValue(vec).getVecDComplex()[0] == DComplex(10,0)); AlwaysAssertExit (JsonValue(vec).getVecDComplex()[1] == DComplex(2,0)); AlwaysAssertExit (JsonValue(vec).getVecDComplex()[2] == DComplex(3,0)); AlwaysAssertExit (JsonValue(vec).getVecDComplex()[3] == DComplex(-1,-2)); vec.push_back (JsonValue(3.)); AlwaysAssertExit (JsonValue(vec).dataType() == TpOther); AlwaysAssertExit (JsonValue(vec).arrayDataType() == TpDComplex); AlwaysAssertExit (JsonValue(vec).size() == 5); AlwaysAssertExit (JsonValue(vec).shape() == IPosition(1,5)); AssertException (JsonValue(vec).getVecBool()); AssertException (JsonValue(vec).getVecInt()); AssertException (JsonValue(vec).getVecDouble()); AlwaysAssertExit (JsonValue(vec).getVecDComplex().size() == 5); AssertException (JsonValue(vec).getVecString()); AlwaysAssertExit (JsonValue(vec).getVecDComplex()[0] == DComplex(10,0)); AlwaysAssertExit (JsonValue(vec).getVecDComplex()[1] == DComplex(2,0)); AlwaysAssertExit (JsonValue(vec).getVecDComplex()[2] == DComplex(3,0)); AlwaysAssertExit (JsonValue(vec).getVecDComplex()[3] == DComplex(-1,-2)); AlwaysAssertExit (JsonValue(vec).getVecDComplex()[4] == DComplex(3,0)); AssertException (JsonValue(vec).getArrayDouble()); AlwaysAssertExit (JsonValue(vec).getArrayDComplex().shape() == IPosition(1,5)); AlwaysAssertExit (JsonValue(vec).getArrayDComplex().data()[0] == DComplex(10,0)); AlwaysAssertExit (JsonValue(vec).getArrayDComplex().data()[1] == DComplex(2,0)); AlwaysAssertExit (JsonValue(vec).getArrayDComplex().data()[2] == DComplex(3,0)); AlwaysAssertExit (JsonValue(vec).getArrayDComplex().data()[3] == DComplex(-1,-2)); AlwaysAssertExit (JsonValue(vec).getArrayDComplex().data()[4] == DComplex(3,0)); } void doArray() { vector zvec; Int v = 0; for (int i=0; i<4; ++i) { vector yvec; for (int j=0; j<3; ++j) { vector xvec; for (int k=0; k<5; ++k) { xvec.push_back (v); v++; } yvec.push_back (xvec); } zvec.push_back (yvec); } Array arr = JsonValue(zvec).getArrayInt(); Array exp(IPosition(3,5,3,4)); indgen(exp); AlwaysAssertExit (allEQ(arr,exp)); } void doValueHolder() { AlwaysAssertExit (JsonValue().getValueHolder().isNull()); AlwaysAssertExit (! JsonValue(1).getValueHolder().isNull()); AlwaysAssertExit (JsonValue(1).getValueHolder().dataType() == TpInt64); AlwaysAssertExit (JsonValue(1.).getValueHolder().dataType() == TpDouble); AlwaysAssertExit (JsonValue(Complex()).getValueHolder().dataType() == TpDComplex); AlwaysAssertExit (JsonValue("a").getValueHolder().dataType() == TpString); AlwaysAssertExit (JsonValue(1).getValueHolder().asInt() == 1); AlwaysAssertExit (JsonValue(1.).getValueHolder().asDouble() == 1.); AlwaysAssertExit (JsonValue(Complex()).getValueHolder().asDComplex() == DComplex()); AlwaysAssertExit (JsonValue("a").getValueHolder().asString() == "a"); AlwaysAssertExit (JsonValue(vector()).getValueHolder().dataType() == TpOther); AlwaysAssertExit (JsonValue(vector(1, JsonValue(True))).getValueHolder().dataType() == TpArrayBool); AlwaysAssertExit (JsonValue(vector(1, JsonValue(1))).getValueHolder().dataType() == TpArrayInt64); AlwaysAssertExit (JsonValue(vector(1, JsonValue(1.))).getValueHolder().dataType() == TpArrayDouble); AlwaysAssertExit (JsonValue(vector(1, JsonValue(Complex()))).getValueHolder().dataType() == TpArrayDComplex); AlwaysAssertExit (JsonValue(vector(1, JsonValue("a"))).getValueHolder().dataType() == TpArrayString); AlwaysAssertExit (JsonValue(vector(1, JsonValue(True))).getValueHolder().asArrayBool().size() == 1); AlwaysAssertExit (JsonValue(vector(1, JsonValue(True))).getValueHolder().asArrayBool().data()[0] == True); AlwaysAssertExit (JsonValue(vector(1, JsonValue(-2))).getValueHolder().asArrayInt().data()[0] == -2); AlwaysAssertExit (JsonValue(vector(1, JsonValue(2.5))).getValueHolder().asArrayDouble().data()[0] == 2.5); AlwaysAssertExit (JsonValue(vector(1, JsonValue(DComplex(1,2)))).getValueHolder().asArrayDComplex().data()[0] == DComplex(1,2)); AlwaysAssertExit (JsonValue(vector(1, JsonValue("bc"))).getValueHolder().asArrayString().data()[0] == "bc"); } int main() { try { doScalar(); doVector(); doArray(); doValueHolder(); } catch (const std::exception& x) { cout << "Unexpected exception: " << x.what() << endl; exit(1); } cout << "OK" << endl; exit(0); } casacore-3.7.1/casa/Logging.h000066400000000000000000000453351476623553700160040ustar00rootroot00000000000000//# Logging.h: Send, record, and filter informational messages //# Copyright (C) 1996,1997,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_LOGGING_H #define CASA_LOGGING_H #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // // // Send, record, and filter informational messages. // // //
      • General Casacore utility classes, such as String. // // // // // Logging, as in "log book", or "processing log." // // // // The classes in the logging module have two essential purposes: //
          //
        1. To attach processing logs to datasets to retain a permanent history of // informational messages that describe how the dataset arrived at its // present state; and //
        2. To inform the user about the progress and decisions made by various // algorithms, such as those used in the Measures system. //
        // // The two fundamental classes in the Logging module are the // LogMessage and // LogSink classes. // However, the class which is most of interest to application programmers is // the LogIO class since it forms the usual // interface to logging. // // A LogMessage consists of an informational message tagged with the // time, a priority (DEBUGGING, NORMAL,, WARN, or // SEVERE) and the source code location of the origin of the message // (for use in debugging primarily). // // The LogSink is used to send the LogMessage to its // destinations. Usually the message will be sent to both a global sink, // intended for user information (e.g., a GUI window), and to a sink associated // with the dataset(s) which are being modified. In practice, the application // programmer does not need to worry about where the global messages go, that // policy is implemented by the Tasking system. In practice, global messages // will be sent to Glish, where they will appear in a GUI, unless Glish is // not available, in which case SEVERE messsages (only) will be sent to // stdout. However, the principle is that "ordinary" application programmers // shouldn't worry about the details of the global sink - they should just // use it. // // A LogFilter can be used to filter // messages (based on priority only at the moment) before they are sent to // their appropriate sink(s). // // The LogIO class puts an ostream like // interface on top of the loggins system. Basically, the application // programmer just has to create messages using and os << items // type interface. // // The first issue that the application programmer has to decide is whether // to use logging at all, or if instead he should put his messages into a // String or an ostream. It is // never wrong to use log messages, however it is reasonable for low level // classes to use Strings or ostreams, since the // caller of that class will have the opportunity to put the text in a log // message if he decides that's the most appropriate thing to do. Note that // it is always wrong to write directly to cout or // cerr (other // then for debugging) - use an ostream, so the caller can replace // it with, for example, an ostringstream. // // Once you decide to use logging, the application programmer only has // to decide at every location he wants to log: //
          //
        1. What content do you want the message to have; and //
        2. what priority does the message have (DEBUGGING, NORMAL, WARN, SEVERE). //
        // Schematically, application programmers would use the logging system as // follows: // // #include // ... // void MyClass:myFunction(LogIO &os) // { // os << LogIO::NORMAL << LogOrigin("MyClass", "myFunction()", WHERE); // 1 // ... // os << WHERE << "An informative message") << LogIO::POST; // 2 // if (error()) { // os << WHERE << LogIO::SEVERE << "Error!" << LogIO::POST; // 3 // sink.post(msg); // ... // } // } // //
          //
        1. Set up the location where log messages come from. WHERE will expand // into the file name and line number (useful for debugging). Set the // priority to NORMAL (this is the default, but you don't know what // the state of os is when it is passed in to this function). //
        2. Set the message and the new line number (optional but encouraged) and // post it. //
        3. Change the priority to SEVERE and post an error message. //
        // // When a dataset is created from several other datasets, their input // "histories" should be merged if possible. This can be done if the // local log sink is in fact a Table. The way you do this is by interrogating // the local sink to find out if it is in fact a TableLogSink. If it is, you // can use a concatenate method of TableLogSink. Schematically this would be // implemented as follows in some DataSet class that has a logSink method that // returns a LogIO reference: // // void merge(DataSet &out, const DataSet &in1, const DataSet &in2) { // ... copy the data from in1 and in2 to out // if (out.logSink().localSink().isTableLogSink()) { // can write to out // if (in1.logSink().localSink().isTableLogSink()) { // out.logSink().localSink().castToTableLogSink().concatenate( // in1.logSink().localSink().castToTableLogSink()); // } // if (... the same for in2 ...) // } // // Of course, DataSet might provide some convenience function for merging // histories. However the point is that given a sink, you can safely determing // whether or not it is in fact a TableLogSink, and if it is you can call // its concatenate function, which takes another TableLogSink. //
        // // // The following example code is checked into the system as // dLogging.cc. It is found in the Logging test directory. // // // class DataClass // { // public: // DataClass(const IPosition &shape, const LogSink &sink); // 1 // void set(Int toWhat); // 2 // LogIO &sink() return os_p;} // 3 // Array &data() {return data_p;} // 4 // const Array &data() const {return data_p;} // 5 // private: // 6 // Vector data_p; // 7 // LogSink log_sink_p; // 8 // LogIO os_p; // 9 // }; // // // This toy class is meant to represent one which is to have "attached" logging // information. Generally, these classes would be fairly high level // astronomical classes, e.g. Image, not Array. Note that // only operations which change the data should be logged in the internal log. // Operations which only read the data should be logged either globally, or in // the class that is taking the results and modifying its own data. // //
        //
        1. //
        Depending on the application, the LogSink to be used might // either be handed in to the class, as is the case here, or it might // be created by the class. For example, a MeasurementSet // will have a processing log table with a known name. //
        2. //
        A sample function that changes the state of the class. Here, // it just sets all the elements of the internal array to // toWhat. //
        3. //
        Return the LogIO that is used by this object. A member function like this // should be provided for use by global functions which manipulate the object. // Note that it is non-const --- the internal sink should be modified only // by functions which CHANGE the object, otherwise the global sink should be // used. //
        4. //
        Return the internal data. Arguably this should be logged at at least // DEBUGGING level. //
        5. //
        Non-const version of the above. Note that it should not be logged since // the state cannot be changed with this function. //
        7. //
        The internal data member. //
        8. //
        The location to which log mesages are sent. //
        9. //
        The LogIO object that will be the actual interface to the logging // system. //
        // // // DataClass::DataClass(const IPosition &shape, const LogSink &sink) // : log_sink_p(sink), os_p(log_sink_p) // 1 // { // 2 // os_p << LogOrigin("DataClass", // 3 // "DataClass(const IPosition &shape, const LogSink &sink)"); // 4 // // 5 // if (shape.nelements() != 1) { // 6 // os_p << LogIO::SEVERE << WHERE << // 7 // "Illegal Shape! Must be one dimensional." << LogIO::EXCEPTION; // 8 // } // 9 // // 10 // data_p.resize(shape(0)); // 11 // os_p << "Inital shape " << shape << "and value 2" << // 12 // LogIO::NORMAL << LogIO::POST; // 13 // // 14 // set(2); // 15 // } // //
        //
        1. //
        The private LogSink data member is initialized with one that // the caller provides. Note that LogSink uses reference semantics, so // that if another "copy" of the sink is made then all the log messages // will go to the same place. For example: // // LogSink a("mylogtable"); // LogSink b(a); // LogSink c; // c = a; // ... // c.post(...); // ends up in mylogtable // ... // b.post(...); // as does this // // This can be useful if several classes might be modifying the same data, // or if a data is spread over several objects. // // Also, os_p is intialized from the sink. //
        3. //
        For a member function, the first argument to LogOrigin is the class name. //
        4. //
        The next argument is the function name. You should use the full name with // arguments so that you can use the argument name in your messages. Leave // off the return type. Cutting and pasting is easier than typing! //
        7. //
        WHERE is a predefined macro that gives the file name and line number. //
        8. //
        Create a SEVERE level error message, post it and throw an exception. //
        11. //
        This will post the message locally and globally, and then throw // an exception. Another possibility would be to call // postGloballyThenThrow() if you only wanted to send the // message to the global sink (for example, if the object is hopelessly // corrupted, or if the problem occurs in a read-only operation). The // thrown exception is an AipsError. The // post*Throw() functions will always set the priority to // SEVERE, however it doesn't hurt to show your intentions //
        12. //
        Create and send a NORMAL priority message. //
        15. //
        Call set() from the constructor to give the data values // an initial value. //
        // // // void DataClass::set(Int toWhat) // { // os_p << LogIO::NORMAL << LogOrigin("DataClass", "set(Int toWhat)"); // 1 // os_p << "Setting data values to " << toWhat << WHERE << LogIO::POST; // 2 // uInt n = data_p.nelements(); // 3 // for (uInt i=0; i < n; i++) { // 4 // #ifdef AIPS_DEBUG // 5 // os_p << LogIO::DEBUGGING << WHERE << // 6 // "Setting element " << i << " to " << toWhat << LogIO::POST; // 7 // #endif // 8 // data_p(i) = toWhat; // 9 // } // } // // //
        //
        2. //
        This and the previous line set up and send a normal priority log message // much as we did previously. //
        7. //
        LogMessages are relatively expensive to produces and consume. Use of // them in a very tight loop should either be ifdef'd out as // in this example, or like: // // if (aips_debug_on) { // ... set up and send log message ... // } // // The advantage of this code is that it's always available - so, for // example, you can turn it on and off by manipulating the global variable // aips_debug_on. However very tight loops cannot even afford // this extra if, and should prefer the ifdef. // // Normally the DEBUGGING messages are "boring but low-volume", // and you should just send them normally. //
        // // // void square(DataClass &object) // { // object.sink() << LogIO::NORMAL << WHERE << // 1 // LogOrigin("square(DataClass &object)") << "Squaring data elements" // 2 // << LogIO::POST; // 3 // object.data() *= object.data(); // 4 // } // // // This function shows how a global function that modifies an object can send // log messages to that objects LogSink using a function of that // object to get access to its sink. // // // float sum(const DataClass &object) // { // LogIO global(LogOrigin("sum(const DataClass &object)")); // 1 // float theSum = sum(object.data()); // 2 // global << WHERE << "Sum of object is: " << theSum; // 3 // return theSum; // 4 // } // // This is an example of a global function that only reads -- does not change -- // an object. //
        //
        3. //
        Since we are not changing the data object, we only post the message // globally, we don't write it to the data object's log sink. The caller // of sum() might log the message somewhere else if the return // value is used to modify data in some other object. Instead we send it // to the global sink. Here we don't POST the message ourselves, we rely // on the LogIO destructor to do it for us. //
        // // // int main() // { // LogSink::globalSink().filter(LogMessage::DEBUGGING); // 1 // LogSink logger(LogMessage::NORMAL, "dLogging_messages_tmp"); // 2 // // 3 // IPosition legalShape(1, 10); // 4 // DataClass dc(legalShape, logger); // 5 // // 6 // square(dc); // 7 // // 8 // Float total = sum(dc); // 9 // // 10 // return 0; // 11 // } // //
        //
        1. //
        Change the priority of messages to display on the global sink's // filter to // DEBUGGING from the default NORMAL. The default // global sink logs to cerr. The global sink can be replaced with // LogSink::globalSink(). //
        2. //
        Create the sink that we are going to use. This constructor will use // a Table. If the table doesn't exist // it will be created. If it does exist, new log messages will be appended // to the end. //
        5. //
        Create an object with the provided sink. The alternative strategy, which // will be used with classes like // MeasurementSet is for the object // to make it's own LogSink if it knows where it wants its // messages to go. //
        7. //
        Changes the data - log messages go to its local sink. //
        9. //
        Reads the data - log messages go only to the global sink. //
        //
        // // //
          //
        1. Attaching informational messages to datasets to describe their processing // history. //
        2. Informational messages to inform the user about the progress and // parameters of algorithms - for example those used for reference frame // conversions in the Measures module. //
        //
        // //
      • More filtering options? // // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Logging/000077500000000000000000000000001476623553700156215ustar00rootroot00000000000000casacore-3.7.1/casa/Logging/LogFilter.cc000066400000000000000000000035321476623553700200220ustar00rootroot00000000000000//# LogFilter.cc: Filter LogMessages on message priority //# Copyright (C) 1996,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LogFilter::LogFilter (LogMessage::Priority lowest) : lowest_p(lowest) {} LogFilter::LogFilter (const LogFilter& other) : LogFilterInterface(), lowest_p(other.lowest_p) {} LogFilter& LogFilter::operator= (const LogFilter& other) { if (this != &other) { lowest_p = other.lowest_p; } return *this; } LogFilter::~LogFilter() {} LogFilter* LogFilter::clone() const { return new LogFilter(*this); } Bool LogFilter::pass (const LogMessage& message) const { return message.priority() >= lowest_p; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Logging/LogFilter.h000066400000000000000000000073651476623553700176740ustar00rootroot00000000000000//# LogFilter.h: Filter LogMessages on message priority //# Copyright (C) 1996,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_LOGFILTER_H #define CASA_LOGFILTER_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Filter LogMessages on message priority. // // // // // //
      • LogMessage // // // // Log[Message] Filter. // // // // The LogFilter class is used by the various log sink classes, // typically accessed through LogSink, to // decide whether a particular LogMessage // should be accepted or rejected. // // Simple filtering is based on the messages priority. // In particular, you typically will choose to only pass messages greater // than or equal to NORMAL in priority, but you might choose // DEBUGGING to see all messages, or SEVERE to only see // messages that report serious problems. // // // // Suppose we wanted to change the global sink so that it prints all messages, // including debugging messages: // // LogFilter all(LogMessage::DEBUGGING); // LogSink::globalSink().filter(all); // // // // // // //# //# class LogFilter : public LogFilterInterface { public: // Construct a filter with the LOWEST priority that you want passed. Thus // DEBUGGING passes everything. Note that it is not possible to // block SEVERE level messages, although you can use a // NullLogSink which will have // this effect. LogFilter (LogMessage::Priority lowest=LogMessage::NORMAL); // Copy other to this. // LogFilter (const LogFilter& other); LogFilter& operator= (const LogFilter& other); // virtual ~LogFilter(); // Clone the object. virtual LogFilter* clone() const; // Return True if message passes this filter. virtual Bool pass (const LogMessage& message) const; // Return the lowest priority which will pass this filter. LogMessage::Priority lowestPriority() const; private: LogMessage::Priority lowest_p; }; inline LogMessage::Priority LogFilter::lowestPriority() const { return lowest_p; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Logging/LogFilterInterface.cc000066400000000000000000000026371476623553700216500ustar00rootroot00000000000000//# LogFilterInterface.cc: Abstract base class for filtering LogMessages //# Copyright (C) 1996,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LogFilterInterface::~LogFilterInterface() {} } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Logging/LogFilterInterface.h000066400000000000000000000071001476623553700215000ustar00rootroot00000000000000//# LogFilterInterface.h: Abstract base class for filtering LogMessages //# Copyright (C) 1996,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_LOGFILTERINTERFACE_H #define CASA_LOGFILTERINTERFACE_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Abstract base class for filtering LogMessages. // // // // // //
      • LogMessage // // // // Log[Message] Filter. // // // // The LogFilter class is used by the various log sink classes, // typically accessed through LogSink, to // decide whether a particular LogMessage // should be accepted or rejected. // // Simple filtering is based on the messages priority. // In particular, you typically will choose to only pass messages greater // than or equal to NORMAL in priority, but you might choose // DEBUGGING to see all messages, or SEVERE to only see // messages that report serious problems. // // // // Suppose we wanted to change the global sink so that it prints all messages, // including debugging messages: // // LogFilter all(LogMessage::DEBUGGING); // LogSink::globalSink().filter(all); // // // // // // //# //# class LogFilterInterface { public: // Construct a filter with the LOWEST priority that you want passed. Thus // DEBUGGING passes everything. Note that it is not possible to // block SEVERE level messages, although you can use a // NullLogSink which will have // this effect. LogFilterInterface() {} virtual ~LogFilterInterface(); // Clone the object. virtual LogFilterInterface* clone() const = 0; // Return True if message passes this filter. virtual Bool pass (const LogMessage& message) const = 0; private: // Copy constructor and assignment cannot be used. // LogFilterInterface (const LogFilterInterface& other); LogFilterInterface& operator= (const LogFilterInterface& other); // }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Logging/LogIO.cc000066400000000000000000000140431476623553700171030ustar00rootroot00000000000000//# LogIO.cc: this defines LogIO which provides a stream like interface for logging //# Copyright (C) 1997,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LogIO::LogIO() : sink_p(), text_p(0) {} LogIO::LogIO(LogSink &sink) : sink_p(sink), text_p(0) {} LogIO::LogIO(const LogOrigin &OR) : sink_p(), msg_p(OR), text_p(0) {} LogIO::LogIO(const LogOrigin &OR, LogSink &sink) : sink_p(sink), msg_p(OR), text_p(0) {} LogIO::LogIO(const LogIO &other) : sink_p(other.sink_p), msg_p(other.msg_p), text_p(0) {} LogIO& LogIO::operator=(const LogIO &other) { if (this != &other) { post(); sink_p = other.sink_p; msg_p = other.msg_p; } return *this; } LogIO::~LogIO() { if (text_p) { post(); } text_p = 0; } void LogIO::post(LogMessage &amess) { msg_p = amess; sink_p.post(msg_p); } void LogIO::post() { if (text_p) { msg_p.message(*text_p); delete text_p; text_p = 0; sink_p.post(msg_p); } // Reset priority. msg_p.priority(LogMessage::NORMAL); } void LogIO::postLocally() { if (text_p) { msg_p.message(*text_p); delete text_p; text_p = 0; sink_p.postLocally(msg_p); } // Reset priority. msg_p.priority(LogMessage::NORMAL); } void LogIO::preparePostThenThrow (const AipsError& x) { if (! String(x.what()).empty()) { output() << "; " << x.what(); } if (text_p == 0) { output() << "Unknown error!"; } msg_p.message(*text_p); // Reset priority before the post, because that'll make a copy and // thereafter throw an exception. msg_p.priority(LogMessage::NORMAL); delete text_p; text_p = 0; } void LogIO::priority(LogMessage::Priority which) { msg_p.priority(which); } LogMessage::Priority LogIO::priority() { return msg_p.priority(); } void LogIO::sourceLocation(const SourceLocation *where) { msg_p.sourceLocation(where); } void LogIO::origin(const LogOrigin &OR) { msg_p.origin(OR); } ostream &LogIO::output() { if (!text_p) { text_p = new ostringstream; AlwaysAssert(text_p != 0, AipsError); } return *text_p; } //--------------------------- << operators LogIO &operator<<(LogIO &os, LogIO::Command item) { switch (item) { case LogIO::POST: os.post(); break; case LogIO::EXCEPTION: os.postThenThrow(AipsError()); break; case LogIO::SEVERE: os.priority(LogMessage::SEVERE); break; case LogIO::WARN: os.priority(LogMessage::WARN); break; case LogIO::NORMAL: os.priority(LogMessage::NORMAL); break; case LogIO::NORMAL1: os.priority(LogMessage::NORMAL1); break; case LogIO::NORMAL2: os.priority(LogMessage::NORMAL2); break; case LogIO::NORMAL3: os.priority(LogMessage::NORMAL3); break; case LogIO::NORMAL4: os.priority(LogMessage::NORMAL4); break; case LogIO::NORMAL5: os.priority(LogMessage::NORMAL5); break; case LogIO::DEBUG1: os.priority(LogMessage::DEBUG1); break; case LogIO::DEBUG2: os.priority(LogMessage::DEBUG2); break; case LogIO::DEBUGGING: os.priority(LogMessage::DEBUGGING); break; default: AlwaysAssert(0 != 0, AipsError); // NOTREACHED } return os; } void operator<< (LogIO &os, const AipsError& x) { if (! String(x.what()).empty()) { os.output() << "; " << x.what(); } os.postThenThrow (x); } LogIO &operator<<(LogIO &os, const SourceLocation *item) { if(os.priority() < LogMessage::NORMAL1 || os.priority() > LogMessage::WARN ) os.sourceLocation(item); return os; } LogIO &operator<<(LogIO &os, const LogOrigin &OR) { os.origin(OR); return os; } LogIO &operator<<(LogIO &os, ostream &(*item)(ostream &)) { item(os.output()); return os; } LogIO &operator<<(LogIO &os, const String &item) { os.output() << item; return os; } LogIO &operator<<(LogIO &os, const char *item) { os.output() << item; return os; } LogIO &operator<<(LogIO &os, Double item) { os.output() << item; return os; } LogIO &operator<<(LogIO &os, Complex item) { os.output() << item; return os; } LogIO &operator<<(LogIO &os, DComplex item) { os.output() << item; return os; } LogIO &operator<<(LogIO &os, Int item) { os.output() << item; return os; } LogIO &operator<<(LogIO &os, uInt item) { os.output() << item; return os; } LogIO &operator<<(LogIO &os, Int64 item) { os.output() << item; return os; } LogIO &operator<<(LogIO &os, uInt64 item) { os.output() << item; return os; } LogIO &operator<<(LogIO &os, uLong item) { os.output() << item; return os; } LogIO &operator<<(LogIO &os, Long item) { os.output() << item; return os; } LogIO &operator<<(LogIO &os, Bool item) { os.output() << (item ? 1:0); return os; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Logging/LogIO.h000066400000000000000000000252361476623553700167530ustar00rootroot00000000000000//# LogIO.h: ostream-like interface to creating log messages. //# Copyright (C) 1997,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_LOGIO_H #define CASA_LOGIO_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class LogSink; class LogOrigin; // // ostream-like interface to creating log messages. // // // // // //
      • LogSink class //
      • LogMessage class //
      • LogOrigin class // // // // Log message, Input/Output. // // // // LogIO is intended to be used in a way similar to the ostream class. // However, rather than sending it's output to a file or stdout, it bundles // its output up into LogMessage objects // and posts them to a LogSink. // // When you use the "<<" operator on a LogIO, you are building up a log message // inside the LogIO object. The message is posted when: //
          //
        1. LogIO::POST() is called //
        2. You send the LogIO::POST or LogIO::EXCEPTION // commands to the LogIO with the shift ( << ) command. //
        3. The LogIO object is destructed. //
        // Note that log messages may span multiple lines, so sending the LogIO a // newline (via "\n" or endl) does not force the message to be emitted. //
        // // // A LogIO may be created in the following ways: // // LogIO os; // // Here, os is attached to the global log sink, and no origin // information is set. // // // TableLogSink tab(...); // LogIO os(tab); // // Here, os is attached to tab (and also to the global // log sink since every sink's post also calls the global sink's // post). // // // // LogIO os(LogOrigin("class", "func(args)", WHERE)); // // Here, os is attached to the global sink and the origin // information is set to class::func(args) and the line number and // source file information is set (with WHERE). // // // TableLogSink tab(...); // LogIO os(LogOrigin("class", "func(args)", WHERE), tab); // // Here all the above information is set. // // Once you have a LogIO, using it is pretty simple: // // os << "Every good boy deserves" << 5 << " pieces of fudge!"; // // // This accumulates the message but does not send it. If you want to force it // to be sent you can do so with either of the following methods: // // os << LogIO::POST; // From the Commands enum // os.post(); // Member function // // Note that after a post the priority is reset to NORMAL. // // If you want to change the level of the message you can also do so with the // shift operator: // // os << LogIO::DEBUGGING << "Boring message" << // LogIO::SEVERE << "Error!" << LogIO::POST; // // Note that changing the priority changes the priority of the entire // message. The message does not get posted until the POST is done. // So in the above example the DEBUGGING priority does not do anything // because the priority is overwritten by the SEVERE one. // // You can also change the origin information with the << operator: // // os << LogOrigin("class", "func(args)"); // os << WHERE; // // // A class which has an operator<< to std::ostream but not LogIO can be handled // as follows: // // os << LogIO::SEVERE << " at "; // os.output() << MEpoch::Convert(time_p, MEpoch::Ref(MEpoch::UTC))(); // os << LogIO::POST; // // // // // The earlier method of creating log messages solely through LogSink and // LogMessage required the programmer to type in more lines of code than // this solution. Also, this interface makes it easy to drop log messages // into existing code that uses ostreams. // // // //
      • Add << operators for all classes that have ostream<< defined. // (We could probably do it with a template, but might result // in ambiguity). //
      • Have a function for changing the LogSink only? (You can get // much the same effect with operator=). // them? // class LogIO { public: // Special commands to the LogIO object enum Command { // Post the accumulated message. Equivalent to calling LogIO::post(). POST, // Post the accumulated message then throw an exception. // Always posts the message at SEVERE priority. Equivalent to calling // LogIO::postThenThrow(). EXCEPTION, // Change the message priority to SEVERE. SEVERE, // Change the message priority to WARN. WARN, // Change the message priority to NORMAL. NORMAL, NORMAL1, NORMAL2, NORMAL3, NORMAL4, NORMAL5, // Change the message priority to DEBUGGING. DEBUG1, DEBUG2, DEBUGGING}; // Attach this LogIO object to the global sink with no origin information. LogIO(); // Attach this LogIO object to the supplied sink. A referencing copy of // the sink is made inside the LogIO object, so you do not need to worry // about memory management. LogIO(LogSink &sink); // Attach this LogIO object to the supplied origin and global sink. LogIO(const LogOrigin &OR); // Attach this LogIO object to the supplied origin and sink. LogIO(const LogOrigin &OR, LogSink &sink); // Copying uses reference semantics, i.e. the same sink will be shared // by both copies. // LogIO(const LogIO &other); LogIO &operator=(const LogIO &other); // // The destructor will post any accumulated message that has not already // been posted. ~LogIO(); // Post the accumulated message. If you wish, you can post the messages // only locally to the sink. // After the post the priority is reset to NORMAL. void post(); void post(LogMessage &amess); // Post the accumulated message locally. // After the post the priority is reset to NORMAL. void postLocally(); // Post the accumulated message at SEVERE priority and then throw an // exception. // After the post the priority is reset to NORMAL. template void postThenThrow (const EXC& exc) { preparePostThenThrow(exc); sink_p.postThenThrow (msg_p, exc); } // Change the priority of the message. It does NOT post the accumulated // message at the old priority first. void priority(LogMessage::Priority which); LogMessage::Priority priority(); // Change the location in the origin. Almost always this is called with the // macro WHERE as its argument. void sourceLocation(const SourceLocation *where); // Change the origin of the accumulated message. void origin(const LogOrigin &origin); // Acumulate output in this ostream. ostream& output(); // Occasionally it is useful to interrogate the local log sink. LogSinkInterface &localSink(); const LogSinkInterface &localSink() const; private: // Prepare message stream for postThenThrow function. void preparePostThenThrow (const AipsError& x); LogSink sink_p; LogMessage msg_p; ostringstream *text_p; }; // // Functions to send commands to a LogIO object. // // The following commands don't change the accumulated message, rather they // send commands to the LogIO object, either to: //
          //
        1. post the current message: os << "message" << LogIO::POST; //
        2. post the current message and then throw an exception: // os << "error" << LogIO::EXCEPTION; //
        3. Change the priority of the current message: // os << LogIO::DEBUGGING; //
        4. Change the origin of the message: // // os << LogOrigin(...); // os << WHERE; // Changes only source file/line number // //
        // LogIO &operator<<(LogIO &os, LogIO::Command item); LogIO &operator<<(LogIO &os, const SourceLocation *item); LogIO &operator<<(LogIO &os, const LogOrigin &OR); // // // Functions to accumulate text in the output message. // // Accumulate text in the output message. The last entry is for things like // endl. // LogIO &operator<<(LogIO &os, const String &item); LogIO &operator<<(LogIO &os, const char *item); LogIO &operator<<(LogIO &os, Double item); LogIO &operator<<(LogIO &os, Complex item); LogIO &operator<<(LogIO &os, DComplex item); LogIO &operator<<(LogIO &os, Int item); LogIO &operator<<(LogIO &os, uInt item); LogIO &operator<<(LogIO &os, Int64 item); LogIO &operator<<(LogIO &os, uInt64 item); LogIO &operator<<(LogIO &os, uLong item); LogIO &operator<<(LogIO &os, Long item); LogIO &operator<<(LogIO &os, Bool item); LogIO &operator<<(LogIO &os, ostream &(*item)(ostream &)); // inline LogSinkInterface &LogIO::localSink() { return sink_p.localSink(); } inline const LogSinkInterface &LogIO::localSink() const { return sink_p.localSink(); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Logging/LogMessage.cc000066400000000000000000000126131476623553700201610ustar00rootroot00000000000000//# LogMessage.cc: Informational log messages with with time,priority,and origin //# Copyright (C) 1996,1997,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LogMessage::LogMessage(Priority prio) : priority_p(prio) { // Nothing } LogMessage::LogMessage(const LogOrigin &sourceLocation, Priority priority) : origin_p(sourceLocation), priority_p(priority) { // Nothing } LogMessage::LogMessage(const String &message, const LogOrigin &sourceLocation, Priority priority) : origin_p(sourceLocation), priority_p(priority) { this->message(message); } void LogMessage::copy_other(const LogMessage &other) { priority_p = other.priority_p; origin_p = other.origin_p; time_p = other.time_p; message_p = other.message_p; } LogMessage::LogMessage(const LogMessage &other) { copy_other(other); } LogMessage &LogMessage::operator=(const LogMessage &other) { if (this != &other) { copy_other(other); } return *this; } LogMessage::~LogMessage() { // Nothing } const String &LogMessage::message() const { return message_p; } LogMessage &LogMessage::message(const String &message, Bool keepLastTime) { message_p = message; if (! keepLastTime) { time_p.now(); } //Remove everything after the final newline Int n = message_p.length(); while (--n >= 0 && message_p[n] == '\n') { ; // Nothing } if (n+1 < Int(message_p.length())) { message_p = message_p.before(n+1); } return *this; } uInt LogMessage::line() const { return origin_p.line(); } LogMessage &LogMessage::line(uInt which) { origin_p.line(which); return *this; } LogMessage &LogMessage::sourceLocation(const SourceLocation *where) { origin_p.sourceLocation(where); return *this; } const LogOrigin &LogMessage::origin() const { return origin_p; } LogMessage &LogMessage::origin(const LogOrigin &origin) { origin_p = origin; return *this; } LogMessage::Priority LogMessage::priority() const { return priority_p; } LogMessage &LogMessage::priority(LogMessage::Priority which) { priority_p = which; return *this; } const Time &LogMessage::messageTime() const { return time_p; } LogMessage &LogMessage::messageTime(const Time &theTime) { time_p = theTime; return *this; } const String &LogMessage::toString(Priority which) { static String names[11] = { "DEBUGGING", "DEBUG2", "DEBUG1", "INFO5", "INFO4", "INFO3", "INFO2", "INFO1", "INFO", "WARN", "SEVERE" }; AlwaysAssert(which >= DEBUGGING && which <= SEVERE, AipsError); return names[which]; } String LogMessage::toString() const { String header = messageTime().ISODate(); header += "\t"; header += toString(priority()); header += "\t"; if (! origin_p.isUnset()) { String daOrigin = origin().toString(); if (priority_p > NORMAL1 && priority_p < WARN) { // Remove file and line location from origin daOrigin.gsub(Regex(".file .*line .*"), ""); } header += daOrigin; } String continuationHeader = "\n" + header + "+\t"; String message = String(message_p); // copy message.gsub("\n", continuationHeader); ostringstream os; os << header << "\t" << message; return String(os); } String LogMessage::toTermString() const { String header = messageTime().ISODate(); header += "\t"; header += toString(priority()); header += "\t"; if (! origin_p.isUnset()) { String daOrigin = origin().toString(); if (priority_p > NORMAL1 && priority_p < WARN) { // Remove file and line location from origin daOrigin.gsub(Regex(".file .*line .*"), ""); } header += daOrigin; } String continuationHeader = "\n\t"; String message = String(message_p); // copy //message.gsub("\n", continuationHeader); ostringstream os; os << header << "\n" << message; //String pr = toString(priority()); //os << pr.resize(6, ' ') << " " << message; return String(os); } ostream &operator<<(ostream &os, const LogMessage &message) { os << message.toString() << endl; return os; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Logging/LogMessage.h000066400000000000000000000202171476623553700200220ustar00rootroot00000000000000//# LogMessage.h: Informational log messages with with time,priority, and origin //# Copyright (C) 1996,1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_LOGMESSAGE_H #define CASA_LOGMESSAGE_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Informational log messages with with time, priority, and origin. // // // // // //
      • LogOrigin // // // // A LogMessage is the unit of information in the Logging system. // A LogMessage consists of an informational text (String) message tagged with // the following: //
          //
        • The time at which the message was generated ("computer" time, not high // precision astronomical time). //
        • A priority - one of DEBUGGING, NORMAL, // WARN, or SEVERE. //
        • A LogOrigin, containing the source // code location where the message originated. It also contains the // ObjectID if the originator was a // distributed object. This is mostly of use in debugging. //
        //
        // // // // void globalFunction(Int arg) // { // LogMessage logMessage(LogOrigin("globalFunction(Int arg)", WHERE)); // ... // logMessage.message("my message").line(__LINE__); // ... // logMessage.message("my second message").line(__LINE__); // ... // } // // void MyClass::member(Int arg) // { // LogMessage logMessage(LogOrigin("myClass", "member(Int arg)", WHERE)); // ... // logMessage.message("my message").line(__LINE__); // ... // } // // A more complete example is available in the module file // Logging.h. // // // //
      • Formerly we had a MessageType enum to go along with // Priority. It was removed because the categories weren't // sufficiently orthogonal. However the idea is probably sound and // we might eventually want to put such a categorization back. //
      • toRecord() and fromRecord() functions will be needed when we integrate // logging with Glish. // // class LogMessage { public: //# If you change this enum, edit toString() // An "importance" which is assigned to each LogMessage. enum Priority { // Low priority - primarily used for findding problems or tracing // execution. DEBUGGING, DEBUG2, DEBUG1, // Most messages users see should have this priority. Use for // "interesting" informational messages from normally executing // software. NORMAL5, NORMAL4, NORMAL3, NORMAL2, NORMAL1, NORMAL, // Use messages of warning level to flag things that are unusual and // might well be errors. Normally the software should proceed anyway // rather than throw an exception. WARN, // Report on a problem detected by the software. Messages logged at // this priority will often be followed by a thrown exception. SEVERE}; // Create a message with the given priority and the current time, and an // empty origin and message. LogMessage(Priority priority=NORMAL); // Create a message with the given location and priority, the current time // and an empty message. This will likely be the most commonly used // constructor when a given message is to be used several times in the same // function. LogMessage(const LogOrigin &sourceLocation, Priority priority=NORMAL); // Create a completely filled out LogMessage. LogMessage(const String &message, const LogOrigin &sourceLocation, Priority=NORMAL); // Make this LogMessage a copy of other. Note that // the time is also copied over. // LogMessage(const LogMessage &other); LogMessage &operator=(const LogMessage &other); // ~LogMessage(); // Get the message text. const String &message() const; // Set the message text. If keepLastTime is True, the // previous time will be used, otherwise the current time is used. This is // intended for messages that come out at essentially identical times to // aid in, e.g., Table selections. LogMessage &message(const String &message, Bool keepLastTime = False); // Get and set the line number in the // LogOrigin. While in principle you can // get and set this information through the origin() functions, // in practice it is convenient to be able to directly get at the line // number since it and the message text are usually the only things you // change in a particular LogMessage object. Generally you will set the // line number with the __LINE__ macro. // uInt line() const; LogMessage &line(uInt which); // // Set the source location - usually this will be called with the // macro WHERE. LogMessage &sourceLocation(const SourceLocation *where); // Get and set the origin of this LogMessage. If you only need the line // number, use the line() or sourceOrigin() // functions instead. // const LogOrigin &origin() const; LogMessage &origin(const LogOrigin &origin); // // Get or change the priority of this LogMessage. // Priority priority() const; LogMessage &priority(Priority which); // // Returns the time at which the message text was created. This time is // presently "computer operating system" precision time, not high-precision // astronomical time. const Time &messageTime() const; // Normally you should not manually set the time, however there may be // rare circumstances where it is useful - for example if you have a single // static message that you want to send out at various times. LogMessage &messageTime(const Time &theTime); // Turn this entire LogMessage into a String. String toString() const; String toTermString() const; // Map the given priority into a String - so, for example, it can be stored // in a table. static const String &toString(Priority which); private: String message_p; LogOrigin origin_p; Priority priority_p; Time time_p; // Provide common implementation for copy constructor and assignment // operator void copy_other(const LogMessage &other); }; // // Write a LogMessage to an ostream. // // Write a LogMessage as a string to an ostream. Merely calls // LogMessage::toString() // ostream &operator<<(ostream &os, const LogMessage &message); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Logging/LogOrigin.cc000066400000000000000000000141631476623553700200260ustar00rootroot00000000000000//# LogOrigin.cc: The source code location of the originator of a LogMessage. //# Copyright (C) 1996,1997,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LogOrigin::LogOrigin() : task_p(""), function_p(""), class_p(""), id_p(True), line_p(0), file_p(""), node_p(getNode()) { // Nothing } LogOrigin::LogOrigin(const String &globalFunctionName, const SourceLocation *where) : task_p(""), function_p(globalFunctionName), class_p(""), id_p(True), line_p(where ? where->lineNumber : 0), file_p(where ? where->fileName : ""), node_p(getNode()) { // Nothing } LogOrigin::LogOrigin(const String &className, const String &memberFuncName, const SourceLocation *where) : task_p(""), function_p(memberFuncName), class_p(className), id_p(True), line_p(where ? where->lineNumber : 0), file_p(where ? where->fileName : ""), node_p(getNode()) { // Nothing } LogOrigin::LogOrigin(const String &className, const String &memberFuncName, const ObjectID &id, const SourceLocation *where) : task_p(""), function_p(memberFuncName), class_p(className), id_p(id), line_p(where ? where->lineNumber : 0), file_p(where ? where->fileName : ""), node_p(getNode()) { // Nothing } LogOrigin::LogOrigin(const LogOrigin &other) { copy_other(other); } LogOrigin &LogOrigin::operator=(const LogOrigin &other) { if (this != &other) { copy_other(other); } return *this; } void LogOrigin::copy_other(const LogOrigin &other) { task_p = other.task_p; function_p = other.function_p; class_p = other.class_p; id_p = other.id_p; line_p = other.line_p; file_p = other.file_p; node_p = other.node_p; } LogOrigin::~LogOrigin() { // Nothing } const String &LogOrigin::taskName() const { return task_p; } LogOrigin &LogOrigin::taskName(const String &funcName) { task_p = funcName; return *this; } const String &LogOrigin::functionName() const { return function_p; } LogOrigin &LogOrigin::functionName(const String &funcName) { function_p = funcName; return *this; } const String &LogOrigin::className() const { return class_p; } LogOrigin &LogOrigin::className(const String &cn) { class_p = cn; return *this; } const ObjectID &LogOrigin::objectID() const { return id_p; } LogOrigin &LogOrigin::objectID(const ObjectID &id) { id_p = id; return *this; } uInt LogOrigin::line() const { return line_p; } LogOrigin &LogOrigin::line(uInt which) { line_p = which; return *this; } const String &LogOrigin::fileName() const { return file_p; } LogOrigin &LogOrigin::fileName(const String &fn) { file_p = fn; return *this; } LogOrigin &LogOrigin::sourceLocation(const SourceLocation *where) { if (where) { line_p = where->lineNumber; if (file_p != where->fileName) { file_p = where->fileName; } } else { line_p = 0; file_p = ""; } return *this; } String LogOrigin::fullName() const { String nameTag(""); if(task_p.length()) nameTag = task_p + "::"; nameTag += className() + "::" + functionName(); if(node_p.length()) nameTag += "::" + node_p; return nameTag; } String LogOrigin::location() const { ostringstream os; String nullString; os << fullName(); if (fileName() != nullString) { os << " (file " << fileName(); if (line() > 0) { os << ", line " << line(); } os << ")"; } return String(os); } String LogOrigin::toString() const { String retval = location(); if (! objectID().isNull()) { ostringstream os; os << " ObjectID=" << objectID(); retval += String(os); } return retval; } Bool LogOrigin::isUnset() const { return (function_p == "" && class_p == "" && id_p.isNull() && line_p == 0 && file_p == "" && task_p == ""); } ostream &operator<<(ostream &os, const LogOrigin &origin) { os << origin.toString(); return os; } // Goes with the following function. Not static function level because // causes problems with our exception emulation. static SourceLocation permanent; const SourceLocation *SourceLocation::canonicalize(const char *file, Int line) { permanent.fileName = file; permanent.lineNumber = line; return &permanent; } // Get the OpenMPI rank from the current process. // This assumes that the MPI library implementation is openMPI. // If a program is started without mpirun, LogOrigin will print nothing // for node_p. If it is started with mpirun, each instance // running on an MPI server will print its rank at the LogOrigin String LogOrigin::getNode() { // Note, MPI rank 0 means the MPIClient from mpi4py is running, not a server, // therefore it should print nothing String rank = EnvironmentVariable::get("OMPI_COMM_WORLD_RANK"); if (! rank.empty()) { if (rank == "0") { rank = String(); } else { rank = "MPIServer-" + rank; } } return rank; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Logging/LogOrigin.h000066400000000000000000000161171476623553700176710ustar00rootroot00000000000000//# LogOrigin.h: The source code location of the originator of a LogMessageLogOrig //# Copyright (C) 1996,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_LOGORIGIN_H #define CASA_LOGORIGIN_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN struct SourceLocation; // // LogOrigin: The source code location of the originator of a LogMessage. // // // // // //
      • ObjectID if you are interested in // logging from Distributed Objects (don't worry if you don't know what // that means - in that case ignore it!). // // // // Log[message] Origin[ation point]. // // // // The LogOriging class is used to record the location at which a // LogMessage originates. It consists of: //
          //
        • A class name, which is blank in the case of a global function. //
        • A function name - global or member. You should "cut and paste" not only // the function name, but also the arguments (but not the return type) to // ensure that the function name is unique in the face of overloading. //
        • A source file name, usually filled in with the WHERE // predefined macro. //
        • A line number, usually filled in with the __LINE__ or // WHERE macros. //
        • An ObjectID if the log message comes // from a distributed object (if you don't know what this means, // you don't need to worry about it). //
        • Eventually we may want to canonicalize the path in the filename. //
        //
        // // // // See the examples for LogMessage and in // Logging.h. // // // It can be very useful for debugging if you know where a message is coming // from. // // // //
      • Nothing known // class LogOrigin { public: // The default constructor sets a null class name, function name, object id, // source file name, and sets the line number to zero. LogOrigin(); // Use this constructor if the log message origination is from a // global function. Normally where is provided using // the WHERE macro. LogOrigin(const String &globalFunctionName, const SourceLocation *where = 0); // Use this constructor if the log message origination is from a // class member function. Normally where is provided using // the WHERE macro. LogOrigin(const String &className, const String &memberFuncName, const SourceLocation *where = 0); // Use this constructor if the log message origination is from a // distributed object (don't worry if you don't know what this // means). Normally where is provided using the // WHERE macro. LogOrigin(const String &className, const String &memberFuncName, const ObjectID &id, const SourceLocation *where = 0); // Make this LogOrigin a copy of other. // LogOrigin(const LogOrigin &other); LogOrigin &operator=(const LogOrigin &other); // ~LogOrigin(); // Get or set the corresponding element of the source location. Note that // the "set" functions can be strung together: // // LogOrigin where; // ... // where.function("anotherFunc").line(__LINE__); // // const String &taskName() const; LogOrigin &taskName(const String &funcName); const String &functionName() const; LogOrigin &functionName(const String &funcName); const String &className() const; LogOrigin &className(const String &className); const ObjectID &objectID() const; LogOrigin &objectID(const ObjectID &id); uInt line() const; LogOrigin &line(uInt which); const String &fileName() const; LogOrigin &fileName(const String &fileName); // // Set the file name and line number at the same time. Normally // where will be defined with the WHERE macro. LogOrigin &sourceLocation(const SourceLocation *where); // Returns class\::function for a member function, or // \::function for a global function. String fullName() const; // Turn the entire origin into a String. String toString() const; // Turns the entire origin except for the ObjectID into a String. The // ObjectID can be turned into a string vie ObjectID::toString. String location() const; // Return true if the line number and file name are not set. Bool isUnset() const; private: String task_p; String function_p; String class_p; ObjectID id_p; uInt line_p; String file_p; String node_p; // Return a String with the MPI rank String getNode(); // Provide common implementation for copy constructor and // assignment operator. void copy_other(const LogOrigin &other); }; // // Write a LogOrigin to an ostream. // // Write a LogOrigin as a string to an ostream. Merely calls // LogOrigin::toString() // ostream &operator<<(ostream &os, const LogOrigin &origin); // // // Helper struct to get the source line. // // The user should only use the WHERE macro. // struct SourceLocation { const char *fileName; Int lineNumber; static const SourceLocation *canonicalize(const char *file, Int line); }; #define WHERE casacore::SourceLocation::canonicalize(__FILE__, __LINE__) // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Logging/LogSink.cc000066400000000000000000000206351476623553700175040ustar00rootroot00000000000000//# LogSink.cc: Distribute LogMessages to their destination(s) //# Copyright (C) 1996,1999,2001,2003,2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN std::shared_ptr LogSink::global_sink_p; std::once_flag LogSink::theirCallOnceFlag; String LogSink::localId( ) { return String("LogSink"); } String LogSink::id( ) const { return String("LogSink"); } LogSink::LogSink(LogMessage::Priority filter, Bool nullSink) : LogSinkInterface(LogFilter(filter)), useGlobalSink_p (True) { std::call_once(theirCallOnceFlag, createGlobalSink); local_ref_to_global_p = LogSink::global_sink_p; if (nullSink) { local_sink_p.reset (new NullLogSink(LogFilter(LogMessage::DEBUGGING))); } else { local_sink_p.reset (new MemoryLogSink(LogFilter(LogMessage::DEBUGGING))); } AlwaysAssert(static_cast(local_sink_p), AipsError); } LogSink::LogSink(const LogFilterInterface &filter, Bool nullSink) : LogSinkInterface(filter), useGlobalSink_p (True) { std::call_once(theirCallOnceFlag, createGlobalSink); local_ref_to_global_p = LogSink::global_sink_p; if (nullSink) { local_sink_p.reset (new NullLogSink(LogFilter(LogMessage::DEBUGGING))); } else { local_sink_p.reset (new MemoryLogSink(LogFilter(LogMessage::DEBUGGING))); } AlwaysAssert(static_cast(local_sink_p), AipsError); } LogSink::LogSink(LogMessage::Priority filter, ostream *os, Bool useGlobalSink) : LogSinkInterface(LogFilter(filter)), local_sink_p(new StreamLogSink(LogFilter(LogMessage::DEBUGGING), os)), useGlobalSink_p (useGlobalSink) { std::call_once(theirCallOnceFlag, createGlobalSink); local_ref_to_global_p = LogSink::global_sink_p; AlwaysAssert(static_cast(local_sink_p), AipsError); } LogSink::LogSink(const LogFilterInterface &filter, ostream *os, Bool useGlobalSink) : LogSinkInterface(filter), local_sink_p(new StreamLogSink(LogFilter(LogMessage::DEBUGGING), os)), useGlobalSink_p (useGlobalSink) { std::call_once(theirCallOnceFlag, createGlobalSink); local_ref_to_global_p = LogSink::global_sink_p; AlwaysAssert(static_cast(local_sink_p), AipsError); } LogSink::LogSink (const LogFilterInterface &filter, const std::shared_ptr& sink) : LogSinkInterface(filter), local_sink_p(sink), useGlobalSink_p (True) { std::call_once(theirCallOnceFlag, createGlobalSink); local_ref_to_global_p = LogSink::global_sink_p; } LogSink::LogSink(const LogSink &other) : LogSinkInterface(other), local_sink_p(other.local_sink_p), useGlobalSink_p (other.useGlobalSink_p) { std::call_once(theirCallOnceFlag, createGlobalSink); local_ref_to_global_p = LogSink::global_sink_p; } LogSink &LogSink::operator=(const LogSink &other) { if (this != &other) { local_ref_to_global_p = other.local_ref_to_global_p; local_sink_p = other.local_sink_p; useGlobalSink_p = other.useGlobalSink_p; LogSinkInterface &This = *this; This = other; } return *this; } LogSink::~LogSink() { flush(); } Bool LogSink::post(const LogMessage &message) { Bool postedLocally = postLocally(message); Bool postedGlobally = False; if (useGlobalSink_p) { postedGlobally = postGlobally(message); } return (postedLocally || postedGlobally); } Bool LogSink::postGlobally(const LogMessage &message) { Bool posted = False; AlwaysAssert(static_cast(global_sink_p), AipsError); if ((*global_sink_p)->filter().pass(message)) { posted = globalSink().postLocally(message); } return posted; } void LogSink::preparePostThenThrow(const LogMessage &message, const AipsError& x) { // Try not to copy message since a severe error might be caused by // out of memory if (message.priority() == LogMessage::SEVERE) { post(message); flush(); x.setMessage (message.toString()); } else { LogMessage messageCopy(message); messageCopy.priority(LogMessage::SEVERE); post(messageCopy); x.setMessage (messageCopy.toString()); } } void LogSink::postGloballyThenThrow(const LogMessage &message) { // Try not to copy message since a severe error might be caused by // out of memory if (message.priority() == LogMessage::SEVERE) { postGlobally(message); globalSink().flush(); throw(AipsError(message.toString())); } else { LogMessage messageCopy(message); messageCopy.priority(LogMessage::SEVERE); postGlobally(messageCopy); globalSink().flush(); throw(AipsError(messageCopy.toString())); } } uInt LogSink::nelements() const { return local_sink_p->nelements(); } Double LogSink::getTime (uInt i) const { return local_sink_p->getTime(i); } String LogSink::getPriority (uInt i) const { return local_sink_p->getPriority(i); } String LogSink::getMessage (uInt i) const { return local_sink_p->getMessage(i); } String LogSink::getLocation (uInt i) const { return local_sink_p->getLocation(i); } String LogSink::getObjectID (uInt i) const { return local_sink_p->getObjectID(i); } const LogFilterInterface &LogSink::filter() const { return this->LogSinkInterface::filter(); } LogSinkInterface &LogSink::filter(const LogFilterInterface &thefilter) { return this->LogSinkInterface::filter(thefilter); } const LogSinkInterface &LogSink::localSink() const { return *(local_sink_p); } LogSinkInterface &LogSink::localSink() { return *(local_sink_p); } LogSink &LogSink::localSink(LogSinkInterface *&fromNew) { local_sink_p.reset (fromNew); fromNew = 0; AlwaysAssert(static_cast(local_sink_p), AipsError); return *this; } Bool LogSink::nullGlobalSink( ) { return !global_sink_p; } LogSinkInterface &LogSink::globalSink() { std::call_once(theirCallOnceFlag, createGlobalSink); return **global_sink_p; } void LogSink::globalSink(LogSinkInterface *&fromNew) { std::call_once(theirCallOnceFlag, createGlobalSink); global_sink_p->replace(fromNew); // racy with use of global_sink_p as noted in .h fromNew = 0; AlwaysAssert(static_cast(global_sink_p), AipsError); } Bool LogSink::postLocally(const LogMessage &message) { if (filter().pass(message)) { return local_sink_p->postLocally(message); } else { return False; } } void LogSink::writeLocally (Double time, const String& message, const String& priority, const String& location, const String& objectID) { local_sink_p->writeLocally (time, message, priority, location, objectID); } void LogSink::clearLocally() { local_sink_p->clearLocally(); } void LogSink::flush (Bool global) { if (local_sink_p) { local_sink_p->flush(False); } if (global && global_sink_p) { (*global_sink_p)->flush(False); } } void LogSink::createGlobalSink() { global_sink_p = std::make_shared (new StreamLogSink(LogMessage::NORMAL, &cerr)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Logging/LogSink.h000066400000000000000000000306721476623553700173500ustar00rootroot00000000000000//# LogSink.h: Distribute LogMessages to their destination(s) //# Copyright (C) 1996,2000,2001,2003,2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_LOGSINK_H #define CASA_LOGSINK_H #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Distribute LogMessages to their destination(s) // // // // // //
      • LogMessage //
      • LogSinkInterface, if you are // interested in extending the set of destinations a LogMessage can // be sent. // // // // Log as in "Log Book." Sink from its common usage ("source/sink") as a thing // which can accept some substance or energy. // // // // The LogSink class supplies the destination for // LogMessages. There are two destinations // available through the LogSink //
          //
        1. A global destination, which is shared by all LogSinks. The global // destination will typically be a GUI window or standard output. //
        2. A local destination which is intended to log changes to // particular dataset(s). The local destination will typically be a // Casacore Table, but there is also // a local sink for temporary storage in memory. //
        // Normally the post() member function will be called which // sends the message to both the global and local destinations, however one or // the other may be chosen via LogSink::postGlobally() and // postLocally() member functions. // // The global sink will normally be set by system library code (it defaults to // using cerr. The type of local sink is defined at // construction time. Presently you can choose one of: //
          //
        1. a NullLogSink which merely // discards the logging messages. //
        2. a StreamLogSink which sends // the log messages to an ostream (typically cerr) //
        3. a TableLogSink which sends // the messages to a Casacore Table. //
        // // Every LogSink has an attached // LogFilterInterface // which is used to reject or pass messages. // The local and global sinks have their own filters, so they can // pass different message priorities (e.g., global DEBUGGING and // local NORMAL). Generally applications code shouldn't change the // global filter. // //
        // // // // LogMessage logMessage(...); // LogSink logger(LogMessage::NORMAL, "logtable"); // log locally to a 'logtable' // logMessage.message("this is a message").line(__LINE__); // logger.post(logMessage); // local and global // // More complete examples are in Logging.h. // // //

        Advanced topics

        // All possible sinks are derived from an abstract base class: // LogSinkInterface. If you want to // allow for logging to a different type of sink (i.e. different from // a stream or Table) , you first need to derive a new class from // LogSinkInterface, and then add a new constructor to // LogSink. // // LogSink itself contains a reference to the actual object that // disposes of the messages. Several LogSink's can share the same // actual sink via the copy constructor or assignment operator. // // LogSink logger1(LogMessage::NORMAL, "logtable"); // LogSink logger2(logger1); // logger2 references logger1 // logger2.post(message); // ends up in "logtable" // // You can even have different LogFilterInterface's // attached to the different LogSinks. // // // Logging changes to data and informing users what the software is doing in // detail. // // // //
      • More sink types - in particular to Glish. //
      • A "tee" Sink type might be useful. // class LogSink : public LogSinkInterface { public: //#If you add more sink types, modify the
          in the synopsis as well. // Create a null local sink that throws all messages away or create // a memory local sink that holds the messages in memory. // If a filter isn't defined, default to NORMAL. // explicit LogSink (LogMessage::Priority filter = LogMessage::NORMAL, Bool nullSink = True); explicit LogSink (const LogFilterInterface &filter, Bool nullSink = True); // // Log to an ostream. It is the responsiblity of the caller to ensure that // os will last as long as the LogSinks that use it. // Normally you would use &cerr as the argument. // LogSink (LogMessage::Priority filter, ostream *os, Bool useGlobalSink = True); LogSink (const LogFilterInterface &filter, ostream *os, Bool useGlobalSink = True); // // Log to the given sink. // It is primarily intended to log to a // TableLogSink. LogSink (const LogFilterInterface &filter, const std::shared_ptr&); // Make a referencing copy of other. That is, if you post a // message to the new object, it behaves as if you had posted it to the // old one (so long as their filters are the same). // LogSink (const LogSink &other); LogSink &operator= (const LogSink &other); // // Temporary to avoid problem that the bool constructor is taken // if a char* is passed. // They are not implemented, so compiler should give warning. // The 3rd argument is added to make it different from current // version which is still in the system library. LogSink (const LogFilterInterface &filter, const String &fileName, Int n=0); LogSink (const LogFilterInterface &filter, const Char* fileName, Int n=0); LogSink (LogMessage::Priority, const String &fileName, Int n=0); LogSink (LogMessage::Priority, const Char* fileName, Int n=0); ~LogSink(); // Send message to both the local and global sink. Return // True if it passes either of them. Bool post (const LogMessage &message); // Send message to the global sink only. Returns True // if it passes the filter. static Bool postGlobally (const LogMessage &message); // Send message to the local sink only. Returns True // if it passes the filter. virtual Bool postLocally (const LogMessage &message); // Post message and then throw an AipsError exception // containing message.toString(). It is always posted as a // SEVERE priority message, no matter what // message.priority() says. // template void postThenThrow (const LogMessage &message, const EXC& exc) { preparePostThenThrow(message, exc); throw exc; } static void postGloballyThenThrow (const LogMessage &message); // // Get number of messages in local sink. virtual uInt nelements() const; // Get given part of the i-th message from the local sink. // virtual Double getTime (uInt i) const; virtual String getPriority (uInt i) const; virtual String getMessage (uInt i) const; virtual String getLocation (uInt i) const; virtual String getObjectID (uInt i) const; // // Write a message (usually from another logsink) into the local one. // The default implementation does nothing. virtual void writeLocally (Double time, const String& message, const String& priority, const String& location, const String& objectID); // Clear the local sink (i.e. remove all messages from it). virtual void clearLocally(); //# Bring out of LogSinkInterface only for documentation purposes // Get or set the filter of this particular LogSink. // virtual const LogFilterInterface &filter() const; virtual LogSinkInterface &filter (const LogFilterInterface &filter); // // Change the sink that this LogSink actually uses. // const LogSinkInterface &localSink() const; LogSinkInterface &localSink(); LogSink &localSink (LogSinkInterface *&fromNew); // // Get/set the global sink or check if the global sink is null. The global // sink defaults to using cerr. Generally applications code // shouldn't change the global sink. More so, calling globalSink(fromNew) // while using the global sink is not thread-safe. And fromNew is set to 0. // static LogSinkInterface &globalSink(); static void globalSink (LogSinkInterface *&fromNew); static Bool nullGlobalSink(); // // Write any pending output (by default also the global sink). virtual void flush (Bool global=True); // Returns the id for this class... static String localId( ); // Returns the id of the LogSink in use... String id( ) const; private: // LsiIntermediate is a helper class to allow LogSinkInterface to implement // semantics that allow causing all classes accessing the log sink to be // aimed at a different sink object. This used to be done by using an // odd "replace" method in std::shared_ptr; however, this is functionality is // being removed to std::shared_ptr as it is modernized so this class was // created to serve this narrow purpose. class LsiIntermediate { public: LsiIntermediate () : logSinkInterface_p (0) {} LsiIntermediate (LogSinkInterface * lsi) : logSinkInterface_p (lsi) {} ~LsiIntermediate () { delete logSinkInterface_p;} LogSinkInterface & operator* () { return * logSinkInterface_p;} LogSinkInterface * operator-> () { return logSinkInterface_p;} Bool operator! () const { return ! logSinkInterface_p;} void replace (LogSinkInterface * newLsi) { delete logSinkInterface_p; logSinkInterface_p = newLsi;} private: // Copy ctor and op= are private and not defined to prevent double-delete. LsiIntermediate (const LsiIntermediate &); LsiIntermediate & operator= (const LsiIntermediate &); LogSinkInterface * logSinkInterface_p; }; // Prepare for postThenThrow function. void preparePostThenThrow(const LogMessage &message, const AipsError& x) ; // Create the global sink (attached to cerr). Always called using theirCallOnce. static void createGlobalSink(); //# Data members. std::shared_ptr local_sink_p; static std::shared_ptr global_sink_p; static std::once_flag theirCallOnceFlag; // The following is a reference to the global sink. It is created to // ensure that the global sink is not destroyed before the last // reference to it is destroyed. This can happen if you have a static // LogSink (or LogIO). std::shared_ptr local_ref_to_global_p; Bool useGlobalSink_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Logging/LogSinkInterface.cc000066400000000000000000000066461476623553700213330ustar00rootroot00000000000000//# LogSinkInterface.cc: Accepts LogMessages and posts them to some destination //# Copyright (C) 1996,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN String LogSinkInterface::localId( ) { return String( "LogSinkInterface" ); } LogSinkInterface::LogSinkInterface() : filter_p (new LogFilter()) { // Nothing } LogSinkInterface::LogSinkInterface(const LogFilterInterface &filter) : filter_p(filter.clone()) { // Nothing } LogSinkInterface::LogSinkInterface(const LogSinkInterface &other) : filter_p(other.filter_p->clone()) { // Nothing } LogSinkInterface &LogSinkInterface::operator=(const LogSinkInterface &other) { if (this != &other) { delete filter_p; filter_p = other.filter_p->clone(); } return *this; } LogSinkInterface::~LogSinkInterface() { flush(); delete filter_p; } uInt LogSinkInterface::nelements() const { return 0; } Double LogSinkInterface::getTime (uInt) const { throw AipsError ("LogSinkInterface::getTime - no such message"); return 0; } String LogSinkInterface::getPriority (uInt) const { throw AipsError ("LogSinkInterface::getPriority - no such message"); return ""; } String LogSinkInterface::getMessage (uInt) const { throw AipsError ("LogSinkInterface::getMessage - no such message"); return ""; } String LogSinkInterface::getLocation (uInt) const { throw AipsError ("LogSinkInterface::getLocation - no such message"); return ""; } String LogSinkInterface::getObjectID (uInt) const { throw AipsError ("LogSinkInterface::getObjectID - no such message"); return ""; } const LogFilterInterface &LogSinkInterface::filter() const { return *filter_p; } LogSinkInterface &LogSinkInterface::filter(const LogFilterInterface &filter) { delete filter_p; filter_p = filter.clone();; return *this; } void LogSinkInterface::flush(Bool) { // Defult implementation is to do nothing. } void LogSinkInterface::cerrToo(Bool) { // Defult implementation is to do nothing. } void LogSinkInterface::writeLocally (Double, const String&, const String&, const String&, const String&) {} void LogSinkInterface::clearLocally () {} } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Logging/LogSinkInterface.h000066400000000000000000000126141476623553700211650ustar00rootroot00000000000000//# LogSinkInterface.h: Accepts LogMessages and posts them to some destination //# Copyright (C) 1996,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_LOGSINKINTERFACE_H #define CASA_LOGSINKINTERFACE_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // //Accepts LogMessages and posts them to some destination // // // // // //
        1. LogMessage //
        2. LogFilterInterface // // // // Log as in "Log Book." Sink from its common usage ("source/sink") as a thing // which can accept some substance or energy. Interface because this is an // abstract, not concrete, class. // // // // This abstract base class is not intended for applications programmers. // Instead they should look at LogSink. // // This class defines a minimal "posting" interface for all objects which accept // log messages. The fundamental model of a LogSinkInterface is: //
            //
          1. That it contains a // LogFilterInterface that is // used to accept or reject messages; and //
          2. That it has a post message that takes a log message; and, if it passes // the filter, does something with it (prints it to a stream, saves it to // a table, ...). //
          // There is no notion of local vs global sinks - that is imposed by // LogSink. //
          // // // // LogSinkInterface &ref = ...; // LogMessage message(...); // ref.postLocally(message); // if (ref.filter().lowestPriority() != LogMessage::DEBUGGING) { // ref.filter(LogMessage::DEBUGGING); // } // // For a more complete example see Logging.h. // // // // Make it straightforward to extend the number of places a message may be // in the future through derivation. // // // //
        3. Nothing known. // class LogSinkInterface { public: // Create with a NORMAL filter. LogSinkInterface(); // Create with the supplied filter. LogSinkInterface(const LogFilterInterface &filter); // Copy semantics - copy the filter from other to this // LogSinkInterface(const LogSinkInterface &other); LogSinkInterface &operator=(const LogSinkInterface &); // virtual ~LogSinkInterface(); // Get/set the filter. // virtual const LogFilterInterface &filter() const; virtual LogSinkInterface &filter(const LogFilterInterface &filter); // // Get number of messages in sink. virtual uInt nelements() const; // Get given part of the i-th message from the sink. // virtual Double getTime (uInt i) const; virtual String getPriority (uInt i) const; virtual String getMessage (uInt i) const; virtual String getLocation (uInt i) const; virtual String getObjectID (uInt i) const; // // This function must be over-ridden in derived classes. If the filter // passes the message, do what is necessary with the message and return // True. virtual Bool postLocally(const LogMessage &message)= 0; // Write any pending output. virtual void flush (Bool global=True); // Write a message (usually from another logsink) into the local one. // The default implementation does nothing. virtual void writeLocally (Double time, const String& message, const String& priority, const String& location, const String& objectID); // Clear the local sink (i.e. remove all messages from it). // The default implementation does nothing. virtual void clearLocally(); // Returns the id for this class... static String localId( ); // Returns the id of the LogSink in use... virtual String id( ) const = 0; // Write to cerr too virtual void cerrToo(bool cerr2); void setTaskName(const String &theTask){taskName=theTask;} private: LogFilterInterface* filter_p; protected: String taskName; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Logging/MemoryLogSink.cc000066400000000000000000000111461476623553700206720ustar00rootroot00000000000000//# MemoryLogSink.h: save log messages in memory //# Copyright (C) 2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN String MemoryLogSink::localId( ) { return String("MemoryLogSink"); } String MemoryLogSink::id( ) const { return String("MemoryLogSink"); } MemoryLogSink::MemoryLogSink() : LogSinkInterface(), nmsg_p (0) {} MemoryLogSink::MemoryLogSink (LogMessage::Priority filter) : LogSinkInterface(LogFilter(filter)), nmsg_p (0) {} MemoryLogSink::MemoryLogSink (const LogFilterInterface& filter) : LogSinkInterface(filter), nmsg_p (0) {} MemoryLogSink::MemoryLogSink (const MemoryLogSink& other) : LogSinkInterface() { copy_other (other); } MemoryLogSink& MemoryLogSink::operator= (const MemoryLogSink& other) { if (this != &other) { copy_other (other); } return *this; } void MemoryLogSink::copy_other (const MemoryLogSink& other) { LogSinkInterface::operator= (other); nmsg_p = other.nmsg_p; time_p = other.time_p; priority_p = other.priority_p; message_p = other.message_p; location_p = other.location_p; objectID_p = other.objectID_p; } MemoryLogSink::~MemoryLogSink() {} uInt MemoryLogSink::nelements() const { return nmsg_p; } Double MemoryLogSink::getTime (uInt i) const { AlwaysAssert (i < nmsg_p, AipsError); return time_p[i]; } String MemoryLogSink::getPriority (uInt i) const { AlwaysAssert (i < nmsg_p, AipsError); return priority_p[i]; } String MemoryLogSink::getMessage (uInt i) const { AlwaysAssert (i < nmsg_p, AipsError); return message_p[i]; } String MemoryLogSink::getLocation (uInt i) const { AlwaysAssert (i < nmsg_p, AipsError); return location_p[i]; } String MemoryLogSink::getObjectID (uInt i) const { AlwaysAssert (i < nmsg_p, AipsError); return objectID_p[i]; } Bool MemoryLogSink::postLocally (const LogMessage& message) { Bool posted = False; if (filter().pass(message)) { posted = True; if (nmsg_p >= time_p.nelements()) { resize (nmsg_p+1); } time_p[nmsg_p] = message.messageTime().modifiedJulianDay()*24.0*3600.0; priority_p[nmsg_p] = LogMessage::toString(message.priority()); message_p[nmsg_p] = message.message(); location_p[nmsg_p] = message.origin().location(); String tmp; message.origin().objectID().toString(tmp); objectID_p[nmsg_p] = tmp; nmsg_p++; } return posted; } void MemoryLogSink::writeLocally (Double time, const String& message, const String& priority, const String& location, const String& objectID) { if (nmsg_p >= time_p.nelements()) { resize (nmsg_p+1); } time_p[nmsg_p] = time; message_p[nmsg_p] = message; priority_p[nmsg_p] = priority; location_p[nmsg_p] = location; objectID_p[nmsg_p] = objectID; nmsg_p++; } void MemoryLogSink::clearLocally() { // Resize the block to 0 elements. time_p.resize (0, True, True); priority_p.resize (0, True, True); message_p.resize (0, True, True); location_p.resize (0, True, True); objectID_p.resize (0, True, True); nmsg_p = 0; } void MemoryLogSink::resize (uInt nrnew) { // Increase with at least 64 elements. if (nrnew < time_p.nelements()+64) { nrnew = time_p.nelements()+64; } time_p.resize (nrnew); priority_p.resize (nrnew); message_p.resize (nrnew); location_p.resize (nrnew); objectID_p.resize (nrnew); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Logging/MemoryLogSink.h000066400000000000000000000100151476623553700205260ustar00rootroot00000000000000//# MemoryLogSink.h: Save log messages in memory //# Copyright (C) 2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MEMORYLOGSINK_H #define CASA_MEMORYLOGSINK_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // Save log messages in memory. // // // // // //
        4. LogSinkInterface // // // // This class posts messages which pass the filter to // memory. // // // // See Logging.h. // // // // For temporary images log messages must be held in memory temporarily. // // //# //# class MemoryLogSink : public LogSinkInterface { public: // Create an empty sink without a filter. MemoryLogSink(); // Create an empty sink with the given filter. // explicit MemoryLogSink (LogMessage::Priority filter); explicit MemoryLogSink (const LogFilterInterface& filter); // // Copy constructor (copy semantics). MemoryLogSink (const MemoryLogSink& other); // Assignment (copy semantics). MemoryLogSink& operator= (const MemoryLogSink& other); virtual ~MemoryLogSink(); // Get number of messages in sink. virtual uInt nelements() const; // Get given part of the i-th message from the sink. // virtual Double getTime (uInt i) const; virtual String getPriority (uInt i) const; virtual String getMessage (uInt i) const; virtual String getLocation (uInt i) const; virtual String getObjectID (uInt i) const; // // If the message passes the filter, write it to memory virtual Bool postLocally (const LogMessage& message); // Write a message (usually from another logsink) into the local one. virtual void writeLocally (Double time, const String& message, const String& priority, const String& location, const String& objectID); // Clear the local sink (i.e. remove all messages from it). virtual void clearLocally(); // Returns the id for this class... static String localId( ); // Returns the id of the LogSink in use... String id( ) const; private: // Avoid duplicating code in copy ctor and assignment operator void copy_other (const MemoryLogSink& other); // Rezize the blocks to the given size, but at least 64 elements // more than the current size. void resize (uInt nrnew); uInt nmsg_p; Block time_p; Block priority_p; Block message_p; Block location_p; Block objectID_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Logging/NullLogSink.cc000066400000000000000000000043171476623553700203360ustar00rootroot00000000000000//# NullLogSink.cc: Throw away all messages. //# Copyright (C) 1996,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN String NullLogSink::localId( ) { return String("NullLogSink"); } String NullLogSink::id( ) const { return String("NullLogSink"); } NullLogSink::NullLogSink() { // Nothing } NullLogSink::NullLogSink(LogMessage::Priority filter) : LogSinkInterface(LogFilter(filter)) { // Nothing } NullLogSink::NullLogSink(const LogFilterInterface &filter) : LogSinkInterface(filter) { // Nothing } NullLogSink::NullLogSink(const NullLogSink &other) : LogSinkInterface(other) { // Nothing } NullLogSink &NullLogSink::operator=(const NullLogSink &other) { if (this != &other) { // Copy the base class part LogSinkInterface &This = *this; This = other; } return *this; } NullLogSink::~NullLogSink() { // Nothing } Bool NullLogSink::postLocally(const LogMessage &message) { return filter().pass(message); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Logging/NullLogSink.h000066400000000000000000000060061476623553700201750ustar00rootroot00000000000000//# NullLogSink.h: Throw away all messages. //# Copyright (C) 1996,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_NULLLOGSINK_H #define CASA_NULLLOGSINK_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Throw away all messages. // // // // // //
        5. LogSinkInterface // // // // Null as in "empty" or "/dev/null". // // // // NullLogSink is a trivial // LogSinkInterface which merely throws // away all its messages. It is not intended to be used directly, rather it // should be used through LogSink. // // // // See Logging.h. // // // // For testing, or to prevent multiply logging (local and global) to // cerr (say). // // // //
        6. Nothing known. // class NullLogSink : public LogSinkInterface { public: NullLogSink(); explicit NullLogSink(LogMessage::Priority filter); explicit NullLogSink(const LogFilterInterface &filter); NullLogSink(const NullLogSink &other); NullLogSink &operator=(const NullLogSink &other); ~NullLogSink(); // Always throws the message away, but it does return True or // False depending on whether or not message passes // the filter. virtual Bool postLocally(const LogMessage &message); // Returns the id for this class... static String localId( ); // Returns the id of the LogSink in use... String id( ) const; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Logging/StreamLogSink.cc000066400000000000000000000061021476623553700206510ustar00rootroot00000000000000//# StreamLogSink.cc: Send log messages to an ostream. //# Copyright (C) 1996,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN String StreamLogSink::localId( ) { return String("StreamLogSink"); } String StreamLogSink::id( ) const { return String("StreamLogSink"); } StreamLogSink::StreamLogSink(ostream *theStream, bool del) : stream_p(theStream), deleteStream(del) { if (stream_p == 0) { stream_p = &cerr; } } StreamLogSink::StreamLogSink(LogMessage::Priority filter, ostream *theStream, bool del) : LogSinkInterface(LogFilter(filter)), stream_p(theStream), deleteStream(del) { if (stream_p == 0) { stream_p = &cerr; } } StreamLogSink::StreamLogSink(const LogFilterInterface &filter, ostream *theStream, bool del) : LogSinkInterface(filter), stream_p(theStream), deleteStream(del) { if (stream_p == 0) { stream_p = &cerr; } } StreamLogSink::StreamLogSink(const StreamLogSink &other) : LogSinkInterface(other), stream_p(other.stream_p), deleteStream(false) { // Nothing } StreamLogSink &StreamLogSink::operator=(const StreamLogSink &other) { if (this != &other) { LogSinkInterface &This = *this; This = other; stream_p = other.stream_p; deleteStream = false; } return *this; } StreamLogSink::~StreamLogSink() { if(deleteStream) delete stream_p; stream_p = 0; } Bool StreamLogSink::postLocally(const LogMessage &message) { Bool doPost = filter().pass(message); if (doPost) { LogOrigin theOrigin(message.origin()); theOrigin.taskName(LogSinkInterface::taskName); const_cast(message).origin(theOrigin); // Cast away const *stream_p << message; } return doPost; } void StreamLogSink::flush(Bool) { if(stream_p) stream_p->flush(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Logging/StreamLogSink.h000066400000000000000000000076241476623553700205250ustar00rootroot00000000000000//# StreamLogSink.h: Send log messages to an ostream. //# Copyright (C) 1996,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_STREAMLOGSINK_H #define CASA_STREAMLOGSINK_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Send log messages to an ostream. // // // // // //
        7. LogSinkInterface //
        8. ostream // // // // "Stream" from the family of standard C++ I/O classes. // // // // StreamLogSink is a straightforward // LogSinkInterface which sends its // messages to an ostream (typically cerr) which it is // given at construction time. It is not intended to be used directly, rather it // should be used through LogSink. // // // // See Logging.h. // // // // Writing to standard output or error will be a common way of displaying log // messages. // // // //
        9. Nothing known. // class StreamLogSink : public LogSinkInterface { public: // Defaults to cerr if no stream is supplied. The caller is // responsible for ensuring that the supplied ostream ostream // lives at least as long as this sink. If not filter is supplied, // NORMAL is used. // explicit StreamLogSink(ostream *theStream = 0, bool deleteStream = false); explicit StreamLogSink(LogMessage::Priority filter, ostream *theStream = 0, bool deleteStream = false); explicit StreamLogSink(const LogFilterInterface &filter, ostream *theStream = 0, bool deleteStream = false); // // Make a copy of other. After copying, both objects will post // to the same stream. // StreamLogSink(const StreamLogSink &other); StreamLogSink &operator=(const StreamLogSink &other); // ~StreamLogSink(); // Write message to the stream if it passes the filter. Works // by calling operator<<(ostream &,const LogMesssage&). virtual Bool postLocally(const LogMessage &message); // write any pending output. virtual void flush (Bool global=True); // Returns the id for this class... static String localId( ); // Returns the id of the LogSink in use... String id( ) const; private: ostream *stream_p; bool deleteStream; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Logging/test/000077500000000000000000000000001476623553700166005ustar00rootroot00000000000000casacore-3.7.1/casa/Logging/test/CMakeLists.txt000066400000000000000000000004371476623553700213440ustar00rootroot00000000000000set (tests tLogSink ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_casa) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/casa/Logging/test/tLogSink.cc000066400000000000000000000026631476623553700206500ustar00rootroot00000000000000//# Copyright (C) 1994,1995,1996,1998,1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include using namespace casacore; int main() { LogSink *sink = new LogSink(LogMessage::SEVERE); LogIO io_p(*sink); io_p << "Something happened"; delete sink; return 0; } casacore-3.7.1/casa/OS.h000066400000000000000000000131751476623553700147340ustar00rootroot00000000000000//# OS.h: Classes for operating system services, and assorted other things //# Copyright (C) 1995,1996,1998,1999,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_OS_H #define CASA_OS_H //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // // // Classes for operating system services, and assorted other things // // //
        10. Nothing special // // // // // // 'OS' is the standard abbreviation for 'Operating System'. // // // // This module's main purpose is to provide convenient and uniform // access to operating system features: environment variables, file // and directory access, time and date services, and uniform data // representation (which solves the variety of ways the fundamental // data types are represented in hardware). //

          // The following functionality is available: //

            //
          • Class // EnvironmentVariable // for access to environment variables. //
          • Class Path, // RegularFile, // SymLink, and // Directory // for dealing with the file system. // Note that module IO deals with // reading and writing data to files and other IO streams. //
          • Class Time // to get the system time. //
          • Class Timer // to measure elapsed, user, and system time of a piece of code. //
          • Framework Conversion // to convert data from one format to another. There are // classes to convert to/from // canonical // format, // VAX format, and // IBM/360 format. // The structure of the framework is shown in the // OMT diagram. //
          • A class to encapsulate Memory usage. // Class MemoryTrace makes it possible to trace all memory (de)allocations // through malloc and free (new and delete). // It only works on Linux systems though. //
          //
          // // See the various class header files. // // // // We want to provide a simple and uniform interface to OS services and // features for the application and library programmer. To pick a few // examples: //
            //
          1. Recursive deletion of a directory. //
          2. Access to time and date information. //
          3. Get and set environment variables. //
          //
          // //
        11. The OS module is a bit fuzzy: for example, canonical data // format conversion is more a matter of hardware than of operating // system differences. Perhaps these particular classes should be // moved to a new module. //
        12. Time and Date classes should be revised after studying // similar classes designed by others. Roel Martinez is tenatively // scheduled to do this in the late summer of 1995. //
        13. There was once some discussion of a 'VOS' (virtual operating system) // module. This seems like a good idea. Nested subdirectories for // specific operating systems would contain the implementations, but // a common interface would be used for all. And perhaps local // site makedefs could be used to select the proper implementation // to link against, with no special action required of the programmer. // // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/OS/000077500000000000000000000000001476623553700145545ustar00rootroot00000000000000casacore-3.7.1/casa/OS/CanonicalConversion.cc000066400000000000000000000154241476623553700210260ustar00rootroot00000000000000//# CanonicalConversion.cc: A class with static functions to convert canonical format //# Copyright (C) 1996,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN size_t CanonicalConversion::toLocalChar (void* to, const void* from, size_t nr) { assert (sizeof(char) == SIZE_CAN_CHAR); memcpy (to, from, nr); return nr * SIZE_CAN_CHAR; } size_t CanonicalConversion::fromLocalChar (void* to, const void* from, size_t nr) { assert (sizeof(char) == SIZE_CAN_CHAR); memcpy (to, from, nr); return nr * SIZE_CAN_CHAR; } void* CanonicalConversion::byteToLocalChar (void* to, const void* from, size_t nrbytes) { assert (sizeof(char) == SIZE_CAN_CHAR); memcpy (to, from, nrbytes); return to; } void* CanonicalConversion::byteFromLocalChar (void* to, const void* from, size_t nrbytes) { assert (sizeof(char) == SIZE_CAN_CHAR); memcpy (to, from, nrbytes); return to; } Conversion::ByteFunction* CanonicalConversion::getByteToLocal (const char*) { assert (sizeof(char) == SIZE_CAN_CHAR); return Conversion::getmemcpy(); } Conversion::ByteFunction* CanonicalConversion::getByteFromLocal (const char*) { assert (sizeof(char) == SIZE_CAN_CHAR); return Conversion::getmemcpy(); } size_t CanonicalConversion::toLocalUChar (void* to, const void* from, size_t nr) { assert (sizeof(unsigned char) == SIZE_CAN_UCHAR); memcpy (to, from, nr); return nr * SIZE_CAN_UCHAR; } size_t CanonicalConversion::fromLocalUChar (void* to, const void* from, size_t nr) { assert (sizeof(unsigned char) == SIZE_CAN_UCHAR); memcpy (to, from, nr); return nr * SIZE_CAN_UCHAR; } void* CanonicalConversion::byteToLocalUChar (void* to, const void* from, size_t nrbytes) { assert (sizeof(unsigned char) == SIZE_CAN_UCHAR); memcpy (to, from, nrbytes); return to; } void* CanonicalConversion::byteFromLocalUChar (void* to, const void* from, size_t nrbytes) { assert (sizeof(unsigned char) == SIZE_CAN_UCHAR); memcpy (to, from, nrbytes); return to; } Conversion::ByteFunction* CanonicalConversion::getByteToLocal (const unsigned char*) { assert (sizeof(unsigned char) == SIZE_CAN_UCHAR); return Conversion::getmemcpy(); } Conversion::ByteFunction* CanonicalConversion::getByteFromLocal (const unsigned char*) { assert (sizeof(unsigned char) == SIZE_CAN_UCHAR); return Conversion::getmemcpy(); } #define CANONICALCONVERSION_DO(CONVERT,SIZE,TOLOCAL,FROMLOCAL,BYTETO,BYTEFROM,T) \ size_t CanonicalConversion::TOLOCAL (void* to, const void* from, \ size_t nr) \ { \ /* Use memcpy if no conversion is needed. */ \ if (CONVERT == 0) { \ assert (sizeof(T) == SIZE); \ memcpy (to, from, nr*SIZE); \ }else{ \ const char* data = (const char*)from; \ T* dest = (T*)to; \ T* last = dest + nr; \ while (dest < last) { \ toLocal (*dest++, data); \ data += SIZE; \ } \ } \ return nr*SIZE; \ } \ size_t CanonicalConversion::FROMLOCAL (void* to, const void* from, \ size_t nr) \ { \ /* Use memcpy if no conversion is needed. */ \ if (CONVERT == 0) { \ assert (sizeof(T) == SIZE); \ memcpy (to, from, nr*SIZE); \ }else{ \ char* data = (char*)to; \ const T* src = (const T*)from; \ const T* last = src + nr; \ while (src < last) { \ fromLocal (data, *src++); \ data += SIZE; \ } \ } \ return nr*SIZE; \ } \ void* CanonicalConversion::BYTETO (void* to, const void* from, \ size_t nrbytes) \ { \ TOLOCAL (to, from, nrbytes / sizeof(T)); \ return to; \ } \ void* CanonicalConversion::BYTEFROM (void* to, const void* from, \ size_t nrbytes) \ { \ FROMLOCAL (to, from, nrbytes / sizeof(T)); \ return to; \ } \ Conversion::ByteFunction* CanonicalConversion::getByteToLocal (const T*) \ { \ if (CONVERT == 0) { \ assert (sizeof(T) == SIZE); \ return Conversion::getmemcpy(); \ } \ return BYTETO; \ } \ Conversion::ByteFunction* CanonicalConversion::getByteFromLocal (const T*) \ { \ if (CONVERT == 0) { \ assert (sizeof(T) == SIZE); \ return Conversion::getmemcpy(); \ } \ return BYTEFROM; \ } CANONICALCONVERSION_DO (CONVERT_CAN_SHORT, SIZE_CAN_SHORT, toLocalShort, fromLocalShort, byteToLocalShort, byteFromLocalShort, short) CANONICALCONVERSION_DO (CONVERT_CAN_USHORT, SIZE_CAN_USHORT, toLocalUShort, fromLocalUShort, byteToLocalUShort, byteFromLocalUShort, unsigned short) CANONICALCONVERSION_DO (CONVERT_CAN_INT, SIZE_CAN_INT, toLocalInt, fromLocalInt, byteToLocalInt, byteFromLocalInt, int) CANONICALCONVERSION_DO (CONVERT_CAN_UINT, SIZE_CAN_UINT, toLocalUInt, fromLocalUInt, byteToLocalUInt, byteFromLocalUInt, unsigned int) CANONICALCONVERSION_DO (CONVERT_CAN_INT64, SIZE_CAN_INT64, toLocalInt64, fromLocalInt64, byteToLocalInt64, byteFromLocalInt64, Int64) CANONICALCONVERSION_DO (CONVERT_CAN_UINT64, SIZE_CAN_UINT64, toLocalUInt64, fromLocalUInt64, byteToLocalUInt64, byteFromLocalUInt64, uInt64) CANONICALCONVERSION_DO (CONVERT_CAN_FLOAT, SIZE_CAN_FLOAT, toLocalFloat, fromLocalFloat, byteToLocalFloat, byteFromLocalFloat, float) CANONICALCONVERSION_DO (CONVERT_CAN_DOUBLE, SIZE_CAN_DOUBLE, toLocalDouble, fromLocalDouble, byteToLocalDouble, byteFromLocalDouble, double) } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/OS/CanonicalConversion.h000066400000000000000000001050171476623553700206660ustar00rootroot00000000000000//# CanonicalConversion.h: A class with static functions to convert canonical format //# Copyright (C) 1996,1997,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_CANONICALCONVERSION_H #define CASA_CANONICALCONVERSION_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Define the canonical sizes of the built-in data types. // These are the same for all machine architectures. // Also define the maximum size. #define SIZE_CAN_CHAR 1 #define SIZE_CAN_UCHAR 1 #define SIZE_CAN_SHORT 2 #define SIZE_CAN_USHORT 2 #define SIZE_CAN_INT 4 #define SIZE_CAN_UINT 4 #define SIZE_CAN_INT64 8 #define SIZE_CAN_UINT64 8 #define SIZE_CAN_FLOAT 4 #define SIZE_CAN_DOUBLE 8 //#//define SIZE_CAN_LDOUBLE 16 // Define for each data format if a conversion is needed from the // local format to the canonical format (or vice-versa). // This allows for optimizations in, for example, AipsIO. // The canonical format is ASCII for strings, IEEE for floating point // and 2-complement for integers (all most significant bit first) // with the lengths as shown above. // The function checkConvert() can be used to check if the flags are // set correctly. // Conversion is needed for little endian architectures (like DEC and Intel), // because the bytes have to be swapped (thus not for data with length 1). #define CONVERT_CAN_CHAR 0 #define CONVERT_CAN_UCHAR 0 #if defined(AIPS_LITTLE_ENDIAN) # define CONVERT_CAN_SHORT 1 # define CONVERT_CAN_USHORT 1 # define CONVERT_CAN_INT 1 # define CONVERT_CAN_UINT 1 # define CONVERT_CAN_INT64 1 # define CONVERT_CAN_UINT64 1 # define CONVERT_CAN_FLOAT 1 # define CONVERT_CAN_DOUBLE 1 //#//# define CONVERT_CAN_LDOUBLE 1 #else // Conversion is not needed for IEEE data. // Change the definitions below if new architectures are being used. # define CONVERT_CAN_SHORT 0 # define CONVERT_CAN_USHORT 0 # define CONVERT_CAN_INT 0 # define CONVERT_CAN_UINT 0 # define CONVERT_CAN_INT64 0 # define CONVERT_CAN_UINT64 0 # define CONVERT_CAN_FLOAT 0 # define CONVERT_CAN_DOUBLE 0 // LDOUBLE is 8 bytes on SUN, but 16 bytes canonical. //#//# define CONVERT_CAN_LDOUBLE 1 #endif // // A class with static functions to convert canonical format // // // // // // This class consists of several static functions to convert // data from local (=native) format to a canonical format. // The canonical length of each data type is: //
          - Bool: 1 bit //
          - char: 1 byte //
          - short: 2 bytes //
          - int: 4 bytes //
          - Int64: 8 bytes //
          - float: 4 bytes //
          - double: 8 bytes //
          The canonical format is big-endian IEEE format, so on many machines // the conversion is only a copy operation. On Alpha- or Intel-based // machines, however, it involves a byte swap to convert from little // endian to big endian. //

          // The class also contains conversion functions making it possible to // specify the number of bytes (in local format) instead of the number // of values. These functions are included to make it possible to have // the same signature as memcpy. //

          // The current implementation of this class works on big- and little-endian // machines using IEEE format. When using on other machines (e.g. VAX) // the toLocal and fromLocal functions have to be changed. //

          // Note that no functions are provided to handle Bools. Instead class // Conversion provides functions to // convert Bools to/from bits. // // // // void someFunction (const uInt* data, uInt nrval) // { // char* buffer = new char[nrval*CanonicalConversion::canonicalSize(data)]; // CanonicalConversion::fromLocal (buffer, data, nrval); // .... // delete [] buffer; // } // // // // Casacore data will often be stored in a canonical format. // To read these data conversion functions are needed. // However, these functions do not use any other Casacore classes, // so they can easily be used in any other software system. // // //

        14. Support data type long double. // class CanonicalConversion { public: // Convert one value from canonical format to local format. // The from and to buffer should not overlap. // static size_t toLocal (char& to, const void* from); static size_t toLocal (unsigned char& to, const void* from); static size_t toLocal (short& to, const void* from); static size_t toLocal (unsigned short& to, const void* from); static size_t toLocal (int& to, const void* from); static size_t toLocal (unsigned int& to, const void* from); static size_t toLocal (Int64& to, const void* from); static size_t toLocal (uInt64& to, const void* from); static size_t toLocal (float& to, const void* from); static size_t toLocal (double& to, const void* from); // // Convert one value from local format to canonical format. // The from and to buffer should not overlap. //# Note that the from value is passed by reference (and not by value), //# because the & operator applied to it is slowish if passed by value. // static size_t fromLocal (void* to, const char& from); static size_t fromLocal (void* to, const unsigned char& from); static size_t fromLocal (void* to, const short& from); static size_t fromLocal (void* to, const unsigned short& from); static size_t fromLocal (void* to, const int& from); static size_t fromLocal (void* to, const unsigned int& from); static size_t fromLocal (void* to, const Int64& from); static size_t fromLocal (void* to, const uInt64& from); static size_t fromLocal (void* to, const float& from); static size_t fromLocal (void* to, const double& from); // // Convert nr values from canonical format to local format. // The from and to buffer should not overlap. // static size_t toLocal (char* to, const void* from, size_t nr); static size_t toLocal (unsigned char* to, const void* from, size_t nr); static size_t toLocal (short* to, const void* from, size_t nr); static size_t toLocal (unsigned short* to, const void* from, size_t nr); static size_t toLocal (int* to, const void* from, size_t nr); static size_t toLocal (unsigned int* to, const void* from, size_t nr); static size_t toLocal (Int64* to, const void* from, size_t nr); static size_t toLocal (uInt64* to, const void* from, size_t nr); static size_t toLocal (float* to, const void* from, size_t nr); static size_t toLocal (double* to, const void* from, size_t nr); // // Convert nr values from local format to canonical format. // The from and to buffer should not overlap. // static size_t fromLocal (void* to, const char* from, size_t nr); static size_t fromLocal (void* to, const unsigned char* from, size_t nr); static size_t fromLocal (void* to, const short* from, size_t nr); static size_t fromLocal (void* to, const unsigned short* from, size_t nr); static size_t fromLocal (void* to, const int* from, size_t nr); static size_t fromLocal (void* to, const unsigned int* from, size_t nr); static size_t fromLocal (void* to, const Int64* from, size_t nr); static size_t fromLocal (void* to, const uInt64* from, size_t nr); static size_t fromLocal (void* to, const float* from, size_t nr); static size_t fromLocal (void* to, const double* from, size_t nr); // // Convert nr values from canonical format to local format. // The from and to buffer should not overlap. // static size_t toLocalChar (void* to, const void* from, size_t nr); static size_t toLocalUChar (void* to, const void* from, size_t nr); static size_t toLocalShort (void* to, const void* from, size_t nr); static size_t toLocalUShort (void* to, const void* from, size_t nr); static size_t toLocalInt (void* to, const void* from, size_t nr); static size_t toLocalUInt (void* to, const void* from, size_t nr); static size_t toLocalInt64 (void* to, const void* from, size_t nr); static size_t toLocalUInt64 (void* to, const void* from, size_t nr); static size_t toLocalFloat (void* to, const void* from, size_t nr); static size_t toLocalDouble (void* to, const void* from, size_t nr); // // Convert nr values from local format to canonical format. // The from and to buffer should not overlap. // static size_t fromLocalChar (void* to, const void* from, size_t nr); static size_t fromLocalUChar (void* to, const void* from, size_t nr); static size_t fromLocalShort (void* to, const void* from, size_t nr); static size_t fromLocalUShort (void* to, const void* from, size_t nr); static size_t fromLocalInt (void* to, const void* from, size_t nr); static size_t fromLocalUInt (void* to, const void* from, size_t nr); static size_t fromLocalInt64 (void* to, const void* from, size_t nr); static size_t fromLocalUInt64 (void* to, const void* from, size_t nr); static size_t fromLocalFloat (void* to, const void* from, size_t nr); static size_t fromLocalDouble (void* to, const void* from, size_t nr); // // Convert values from canonical format to local format. // The from and to buffer should not overlap. // The number of values involved is determined from the argument // nrbytes, which gives the number of bytes in local format. // The signature of this function is the same as memcpy, so // that memcpy can directly be used if no conversion is needed. // static void* byteToLocalChar (void* to, const void* from, size_t nrbytes); static void* byteToLocalUChar (void* to, const void* from, size_t nrbytes); static void* byteToLocalShort (void* to, const void* from, size_t nrbytes); static void* byteToLocalUShort (void* to, const void* from, size_t nrbytes); static void* byteToLocalInt (void* to, const void* from, size_t nrbytes); static void* byteToLocalUInt (void* to, const void* from, size_t nrbytes); static void* byteToLocalInt64 (void* to, const void* from, size_t nrbytes); static void* byteToLocalUInt64 (void* to, const void* from, size_t nrbytes); static void* byteToLocalFloat (void* to, const void* from, size_t nrbytes); static void* byteToLocalDouble (void* to, const void* from, size_t nrbytes); // // Convert values from local format to canonical format. // The from and to buffer should not overlap. // The number of values involved is determined from the argument // nrbytes, which gives the number of bytes in local format. // The signature of this function is the same as memcpy, so // that memcpy can directly be used if no conversion is needed. // static void* byteFromLocalChar (void* to, const void* from, size_t nrbytes); static void* byteFromLocalUChar (void* to, const void* from, size_t nrbytes); static void* byteFromLocalShort (void* to, const void* from, size_t nrbytes); static void* byteFromLocalUShort (void* to, const void* from, size_t nrbytes); static void* byteFromLocalInt (void* to, const void* from, size_t nrbytes); static void* byteFromLocalUInt (void* to, const void* from, size_t nrbytes); static void* byteFromLocalInt64 (void* to, const void* from, size_t nrbytes); static void* byteFromLocalUInt64 (void* to, const void* from, size_t nrbytes); static void* byteFromLocalFloat (void* to, const void* from, size_t nrbytes); static void* byteFromLocalDouble (void* to, const void* from, size_t nrbytes); // // Get the value conversion function for the given type. // static Conversion::ValueFunction* getToLocal (const char*); static Conversion::ValueFunction* getToLocal (const unsigned char*); static Conversion::ValueFunction* getToLocal (const short*); static Conversion::ValueFunction* getToLocal (const unsigned short*); static Conversion::ValueFunction* getToLocal (const int*); static Conversion::ValueFunction* getToLocal (const unsigned int*); static Conversion::ValueFunction* getToLocal (const Int64*); static Conversion::ValueFunction* getToLocal (const uInt64*); static Conversion::ValueFunction* getToLocal (const float*); static Conversion::ValueFunction* getToLocal (const double*); static Conversion::ValueFunction* getFromLocal (const char*); static Conversion::ValueFunction* getFromLocal (const unsigned char*); static Conversion::ValueFunction* getFromLocal (const short*); static Conversion::ValueFunction* getFromLocal (const unsigned short*); static Conversion::ValueFunction* getFromLocal (const int*); static Conversion::ValueFunction* getFromLocal (const unsigned int*); static Conversion::ValueFunction* getFromLocal (const Int64*); static Conversion::ValueFunction* getFromLocal (const uInt64*); static Conversion::ValueFunction* getFromLocal (const float*); static Conversion::ValueFunction* getFromLocal (const double*); // // Get the byte conversion function for the given type. // The function memcpy is returned when a conversion // is not needed. // static Conversion::ByteFunction* getByteToLocal (const char*); static Conversion::ByteFunction* getByteToLocal (const unsigned char*); static Conversion::ByteFunction* getByteToLocal (const short*); static Conversion::ByteFunction* getByteToLocal (const unsigned short*); static Conversion::ByteFunction* getByteToLocal (const int*); static Conversion::ByteFunction* getByteToLocal (const unsigned int*); static Conversion::ByteFunction* getByteToLocal (const Int64*); static Conversion::ByteFunction* getByteToLocal (const uInt64*); static Conversion::ByteFunction* getByteToLocal (const float*); static Conversion::ByteFunction* getByteToLocal (const double*); static Conversion::ByteFunction* getByteFromLocal (const char*); static Conversion::ByteFunction* getByteFromLocal (const unsigned char*); static Conversion::ByteFunction* getByteFromLocal (const short*); static Conversion::ByteFunction* getByteFromLocal (const unsigned short*); static Conversion::ByteFunction* getByteFromLocal (const int*); static Conversion::ByteFunction* getByteFromLocal (const unsigned int*); static Conversion::ByteFunction* getByteFromLocal (const Int64*); static Conversion::ByteFunction* getByteFromLocal (const uInt64*); static Conversion::ByteFunction* getByteFromLocal (const float*); static Conversion::ByteFunction* getByteFromLocal (const double*); // // Return the canonical length for the various data types. // static unsigned int canonicalSize (const char*); static unsigned int canonicalSize (const unsigned char*); static unsigned int canonicalSize (const short*); static unsigned int canonicalSize (const unsigned short*); static unsigned int canonicalSize (const int*); static unsigned int canonicalSize (const unsigned int*); static unsigned int canonicalSize (const Int64*); static unsigned int canonicalSize (const uInt64*); static unsigned int canonicalSize (const float*); static unsigned int canonicalSize (const double*); //#//static unsigned int canonicalSize (const long double*); // // Reverse 2 bytes. static void reverse2 (void* to, const void* from); // Reverse 4 bytes. static void reverse4 (void* to, const void* from); // Reverse 8 bytes. static void reverse8 (void* to, const void* from); // Move 2 bytes. static void move2 (void* to, const void* from); // Move 4 bytes. static void move4 (void* to, const void* from); // Move 8 bytes. static void move8 (void* to, const void* from); private: // This class should not be constructed // (so declare the constructor private). CanonicalConversion(); }; inline void CanonicalConversion::reverse2 (void* to, const void* from) { unsigned short x, xsw; memcpy(&x, from, 2); xsw = ((x & 0xffu) << 8u) | (x >> 8u); memcpy(to, &xsw, 2); } inline void CanonicalConversion::reverse4 (void* to, const void* from) { unsigned int x, xsw; memcpy(&x, from, 4); #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3) xsw = __builtin_bswap32(x); #else xsw = ((x & 0xffu) << 24u) | ((x & 0xff00u) << 8u) | ((x & 0xff0000u) >> 8u) | (x >> 24u); #endif memcpy(to, &xsw, 4); } inline void CanonicalConversion::reverse8 (void* to, const void* from) { uInt64 x, xsw; memcpy(&x, from, 8); #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3) xsw = __builtin_bswap64(x); #else xsw = ((x & 0xffULL) << 56ULL) | ((x & 0xff00ULL) << 40ULL) | ((x & 0xff0000ULL) << 24ULL) | ((x & 0xff000000ULL) << 8ULL) | ((x & 0xff00000000ULL) >> 8ULL) | ((x & 0xff0000000000ULL) >> 24ULL) | ((x & 0xff000000000000ULL) >> 40ULL) | ( x >> 56ULL); #endif memcpy(to, &xsw, 8); } inline void CanonicalConversion::move2 (void* to, const void* from) { memcpy(to, from, 2); } inline void CanonicalConversion::move4 (void* to, const void* from) { memcpy(to, from, 4); } inline void CanonicalConversion::move8 (void* to, const void* from) { /* memcpy is overlap save if size fits into a register */ if (sizeof(to) < 8) { memmove(to, from, 8); } else { memcpy(to, from, 8); } } inline size_t CanonicalConversion::toLocal (char& to, const void* from) { to = *(char*)from; return SIZE_CAN_CHAR; } inline size_t CanonicalConversion::toLocal (unsigned char& to, const void* from) { to = *(unsigned char*)from; return SIZE_CAN_UCHAR; } inline size_t CanonicalConversion::toLocal (short& to, const void* from) { if (sizeof(short) != 2) { if (((signed char*)from)[0] < 0) { to = -1; }else{ to = 0; } } #if defined(AIPS_LITTLE_ENDIAN) reverse2 (&to, from); #else move2 (((char*)&to)+sizeof(short)-2, from); #endif return SIZE_CAN_SHORT; } inline size_t CanonicalConversion::toLocal (unsigned short& to, const void* from) { if (sizeof(unsigned short) != 2) { to = 0; } #if defined(AIPS_LITTLE_ENDIAN) reverse2 (&to, from); #else move2 (((char*)&to)+sizeof(unsigned short)-2, from); #endif return SIZE_CAN_USHORT; } inline size_t CanonicalConversion::toLocal (int& to, const void* from) { if (sizeof(int) != 4) { if (((signed char*)from)[0] < 0) { to = -1; }else{ to = 0; } } #if defined(AIPS_LITTLE_ENDIAN) reverse4 (&to, from); #else move4 (((char*)&to)+sizeof(int)-4, from); #endif return SIZE_CAN_INT; } inline size_t CanonicalConversion::toLocal (unsigned int& to, const void* from) { if (sizeof(unsigned int) != 4) { to = 0; } #if defined(AIPS_LITTLE_ENDIAN) reverse4 (&to, from); #else move4 (((char*)&to)+sizeof(unsigned int)-4, from); #endif return SIZE_CAN_UINT; } inline size_t CanonicalConversion::toLocal (Int64& to, const void* from) { if (sizeof(Int64) != 8) { if (((signed char*)from)[0] < 0) { to = -1; }else{ to = 0; } } #if defined(AIPS_LITTLE_ENDIAN) reverse8 (&to, from); #else move8 (((char*)&to)+sizeof(Int64)-8, from); #endif return SIZE_CAN_INT64; } inline size_t CanonicalConversion::toLocal (uInt64& to, const void* from) { if (sizeof(uInt64) != 8) { to = 0; } #if defined(AIPS_LITTLE_ENDIAN) reverse8 (&to, from); #else move8 (((char*)&to)+sizeof(uInt64)-8, from); #endif return SIZE_CAN_UINT64; } inline size_t CanonicalConversion::toLocal (float& to, const void* from) { #if defined(AIPS_LITTLE_ENDIAN) reverse4 (((char*)&to)+sizeof(float)-4, from); #else move4 (&to, from); #endif return SIZE_CAN_FLOAT; } inline size_t CanonicalConversion::toLocal (double& to, const void* from) { #if defined(AIPS_LITTLE_ENDIAN) reverse8 (((char*)&to)+sizeof(double)-8, from); #else move8 (&to, from); #endif return SIZE_CAN_DOUBLE; } inline size_t CanonicalConversion::fromLocal (void* to, const char& from) { *(char*)to = from; return SIZE_CAN_CHAR; } inline size_t CanonicalConversion::fromLocal (void* to, const unsigned char& from) { *(unsigned char*)to = from; return SIZE_CAN_UCHAR; } inline size_t CanonicalConversion::fromLocal (void* to, const short& from) { #if defined(AIPS_LITTLE_ENDIAN) reverse2 (to, &from); #else move2 (to, ((char*)&from)+sizeof(short)-2); #endif return SIZE_CAN_SHORT; } inline size_t CanonicalConversion::fromLocal (void* to, const unsigned short& from) { #if defined(AIPS_LITTLE_ENDIAN) reverse2 (to, &from); #else move2 (to, ((char*)&from)+sizeof(unsigned short)-2); #endif return SIZE_CAN_USHORT; } inline size_t CanonicalConversion::fromLocal (void* to, const int& from) { #if defined(AIPS_LITTLE_ENDIAN) reverse4 (to, &from); #else move4 (to, ((char*)&from)+sizeof(int)-4); #endif return SIZE_CAN_INT; } inline size_t CanonicalConversion::fromLocal (void* to, const unsigned int& from) { #if defined(AIPS_LITTLE_ENDIAN) reverse4 (to, &from); #else move4 (to, ((char*)&from)+sizeof(unsigned int)-4); #endif return SIZE_CAN_UINT; } inline size_t CanonicalConversion::fromLocal (void* to, const Int64& from) { #if defined(AIPS_LITTLE_ENDIAN) reverse8 (to, &from); #else move8 (to, ((char*)&from)+sizeof(Int64)-8); #endif return SIZE_CAN_INT64; } inline size_t CanonicalConversion::fromLocal (void* to, const uInt64& from) { #if defined(AIPS_LITTLE_ENDIAN) reverse8 (to, &from); #else move8 (to, ((char*)&from)+sizeof(uInt64)-8); #endif return SIZE_CAN_UINT64; } inline size_t CanonicalConversion::fromLocal (void* to, const float& from) { #if defined(AIPS_LITTLE_ENDIAN) reverse4 (to, &from); #else move4 (to, &from); #endif return SIZE_CAN_FLOAT; } inline size_t CanonicalConversion::fromLocal (void* to, const double& from) { #if defined(AIPS_LITTLE_ENDIAN) reverse8 (to, &from); #else move8 (to, &from); #endif return SIZE_CAN_FLOAT; } inline size_t CanonicalConversion::toLocal (char* to, const void* from, size_t nr) { return toLocalChar (to, from, nr); } inline size_t CanonicalConversion::toLocal (unsigned char* to, const void* from, size_t nr) { return toLocalUChar (to, from, nr); } inline size_t CanonicalConversion::toLocal (short* to, const void* from, size_t nr) { return toLocalShort (to, from, nr); } inline size_t CanonicalConversion::toLocal (unsigned short* to, const void* from, size_t nr) { return toLocalUShort (to, from, nr); } inline size_t CanonicalConversion::toLocal (int* to, const void* from, size_t nr) { return toLocalInt (to, from, nr); } inline size_t CanonicalConversion::toLocal (unsigned int* to, const void* from, size_t nr) { return toLocalUInt (to, from, nr); } inline size_t CanonicalConversion::toLocal (Int64* to, const void* from, size_t nr) { return toLocalInt64 (to, from, nr); } inline size_t CanonicalConversion::toLocal (uInt64* to, const void* from, size_t nr) { return toLocalUInt64 (to, from, nr); } inline size_t CanonicalConversion::toLocal (float* to, const void* from, size_t nr) { return toLocalFloat (to, from, nr); } inline size_t CanonicalConversion::toLocal (double* to, const void* from, size_t nr) { return toLocalDouble (to, from, nr); } inline size_t CanonicalConversion::fromLocal (void* to, const char* from, size_t nr) { return fromLocalChar (to, from, nr); } inline size_t CanonicalConversion::fromLocal (void* to, const unsigned char* from, size_t nr) { return fromLocalUChar (to, from, nr); } inline size_t CanonicalConversion::fromLocal (void* to, const short* from, size_t nr) { return fromLocalShort (to, from, nr); } inline size_t CanonicalConversion::fromLocal (void* to, const unsigned short* from, size_t nr) { return fromLocalUShort (to, from, nr); } inline size_t CanonicalConversion::fromLocal (void* to, const int* from, size_t nr) { return fromLocalInt (to, from, nr); } inline size_t CanonicalConversion::fromLocal (void* to, const unsigned int* from, size_t nr) { return fromLocalUInt (to, from, nr); } inline size_t CanonicalConversion::fromLocal (void* to, const Int64* from, size_t nr) { return fromLocalInt64 (to, from, nr); } inline size_t CanonicalConversion::fromLocal (void* to, const uInt64* from, size_t nr) { return fromLocalUInt64 (to, from, nr); } inline size_t CanonicalConversion::fromLocal (void* to, const float* from, size_t nr) { return fromLocalFloat (to, from, nr); } inline size_t CanonicalConversion::fromLocal (void* to, const double* from, size_t nr) { return fromLocalDouble (to, from, nr); } inline Conversion::ValueFunction* CanonicalConversion::getToLocal (const char*) { return toLocalChar; } inline Conversion::ValueFunction* CanonicalConversion::getToLocal (const unsigned char*) { return toLocalUChar; } inline Conversion::ValueFunction* CanonicalConversion::getToLocal (const short*) { return toLocalShort; } inline Conversion::ValueFunction* CanonicalConversion::getToLocal (const unsigned short*) { return toLocalUShort; } inline Conversion::ValueFunction* CanonicalConversion::getToLocal (const int*) { return toLocalInt; } inline Conversion::ValueFunction* CanonicalConversion::getToLocal (const unsigned int*) { return toLocalUInt; } inline Conversion::ValueFunction* CanonicalConversion::getToLocal (const Int64*) { return toLocalInt64; } inline Conversion::ValueFunction* CanonicalConversion::getToLocal (const uInt64*) { return toLocalUInt64; } inline Conversion::ValueFunction* CanonicalConversion::getToLocal (const float*) { return toLocalFloat; } inline Conversion::ValueFunction* CanonicalConversion::getToLocal (const double*) { return toLocalDouble; } inline Conversion::ValueFunction* CanonicalConversion::getFromLocal (const char*) { return fromLocalChar; } inline Conversion::ValueFunction* CanonicalConversion::getFromLocal (const unsigned char*) { return fromLocalUChar; } inline Conversion::ValueFunction* CanonicalConversion::getFromLocal (const short*) { return fromLocalShort; } inline Conversion::ValueFunction* CanonicalConversion::getFromLocal (const unsigned short*) { return fromLocalUShort; } inline Conversion::ValueFunction* CanonicalConversion::getFromLocal (const int*) { return fromLocalInt; } inline Conversion::ValueFunction* CanonicalConversion::getFromLocal (const unsigned int*) { return fromLocalUInt; } inline Conversion::ValueFunction* CanonicalConversion::getFromLocal (const Int64*) { return fromLocalInt64; } inline Conversion::ValueFunction* CanonicalConversion::getFromLocal (const uInt64*) { return fromLocalUInt64; } inline Conversion::ValueFunction* CanonicalConversion::getFromLocal (const float*) { return fromLocalFloat; } inline Conversion::ValueFunction* CanonicalConversion::getFromLocal (const double*) { return fromLocalDouble; } inline unsigned int CanonicalConversion::canonicalSize (const char*) {return SIZE_CAN_CHAR;} inline unsigned int CanonicalConversion::canonicalSize (const unsigned char*) {return SIZE_CAN_UCHAR;} inline unsigned int CanonicalConversion::canonicalSize (const short*) {return SIZE_CAN_SHORT;} inline unsigned int CanonicalConversion::canonicalSize (const unsigned short*) {return SIZE_CAN_USHORT;} inline unsigned int CanonicalConversion::canonicalSize (const int*) {return SIZE_CAN_INT;} inline unsigned int CanonicalConversion::canonicalSize (const unsigned int*) {return SIZE_CAN_UINT;} inline unsigned int CanonicalConversion::canonicalSize (const Int64*) {return SIZE_CAN_INT64;} inline unsigned int CanonicalConversion::canonicalSize (const uInt64*) {return SIZE_CAN_UINT64;} inline unsigned int CanonicalConversion::canonicalSize (const float*) {return SIZE_CAN_FLOAT;} inline unsigned int CanonicalConversion::canonicalSize (const double*) {return SIZE_CAN_DOUBLE;} //#//inline unsigned int CanonicalConversion::canonicalSize (const long double*) //#// {return SIZE_CAN_LDOUBLE;} } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/OS/CanonicalDataConversion.cc000066400000000000000000000270261476623553700216210ustar00rootroot00000000000000//# CanonicalDataConversion.cc: A class with virtual functions to convert canonical format //# Copyright (C) 1996,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN CanonicalDataConversion::~CanonicalDataConversion() {} size_t CanonicalDataConversion::toLocal (char& to, const void* from) const { return CanonicalConversion::toLocal (to, from); } size_t CanonicalDataConversion::toLocal (unsigned char& to, const void* from) const { return CanonicalConversion::toLocal (to, from); } size_t CanonicalDataConversion::toLocal (short& to, const void* from) const { return CanonicalConversion::toLocal (to, from); } size_t CanonicalDataConversion::toLocal (unsigned short& to, const void* from) const { return CanonicalConversion::toLocal (to, from); } size_t CanonicalDataConversion::toLocal (int& to, const void* from) const { return CanonicalConversion::toLocal (to, from); } size_t CanonicalDataConversion::toLocal (unsigned int& to, const void* from) const { return CanonicalConversion::toLocal (to, from); } size_t CanonicalDataConversion::toLocal (Int64& to, const void* from) const { return CanonicalConversion::toLocal (to, from); } size_t CanonicalDataConversion::toLocal (uInt64& to, const void* from) const { return CanonicalConversion::toLocal (to, from); } size_t CanonicalDataConversion::toLocal (float& to, const void* from) const { return CanonicalConversion::toLocal (to, from); } size_t CanonicalDataConversion::toLocal (double& to, const void* from) const { return CanonicalConversion::toLocal (to, from); } size_t CanonicalDataConversion::toLocal (char* to, const void* from, size_t nr) const { return CanonicalConversion::toLocal (to, from, nr); } size_t CanonicalDataConversion::toLocal (unsigned char* to, const void* from, size_t nr) const { return CanonicalConversion::toLocal (to, from, nr); } size_t CanonicalDataConversion::toLocal (short* to, const void* from, size_t nr) const { return CanonicalConversion::toLocal (to, from, nr); } size_t CanonicalDataConversion::toLocal (unsigned short* to, const void* from, size_t nr) const { return CanonicalConversion::toLocal (to, from, nr); } size_t CanonicalDataConversion::toLocal (int* to, const void* from, size_t nr) const { return CanonicalConversion::toLocal (to, from, nr); } size_t CanonicalDataConversion::toLocal (unsigned int* to, const void* from, size_t nr) const { return CanonicalConversion::toLocal (to, from, nr); } size_t CanonicalDataConversion::toLocal (Int64* to, const void* from, size_t nr) const { return CanonicalConversion::toLocal (to, from, nr); } size_t CanonicalDataConversion::toLocal (uInt64* to, const void* from, size_t nr) const { return CanonicalConversion::toLocal (to, from, nr); } size_t CanonicalDataConversion::toLocal (float* to, const void* from, size_t nr) const { return CanonicalConversion::toLocal (to, from, nr); } size_t CanonicalDataConversion::toLocal (double* to, const void* from, size_t nr) const { return CanonicalConversion::toLocal (to, from, nr); } size_t CanonicalDataConversion::fromLocal (void* to, char from) const { return CanonicalConversion::fromLocal (to, from); } size_t CanonicalDataConversion::fromLocal (void* to, unsigned char from) const { return CanonicalConversion::fromLocal (to, from); } size_t CanonicalDataConversion::fromLocal (void* to, short from) const { return CanonicalConversion::fromLocal (to, from); } size_t CanonicalDataConversion::fromLocal (void* to, unsigned short from) const { return CanonicalConversion::fromLocal (to, from); } size_t CanonicalDataConversion::fromLocal (void* to, int from) const { return CanonicalConversion::fromLocal (to, from); } size_t CanonicalDataConversion::fromLocal (void* to, unsigned int from) const { return CanonicalConversion::fromLocal (to, from); } size_t CanonicalDataConversion::fromLocal (void* to, Int64 from) const { return CanonicalConversion::fromLocal (to, from); } size_t CanonicalDataConversion::fromLocal (void* to, uInt64 from) const { return CanonicalConversion::fromLocal (to, from); } size_t CanonicalDataConversion::fromLocal (void* to, float from) const { return CanonicalConversion::fromLocal (to, from); } size_t CanonicalDataConversion::fromLocal (void* to, double from) const { return CanonicalConversion::fromLocal (to, from); } size_t CanonicalDataConversion::fromLocal (void* to, const char* from, size_t nr) const { return CanonicalConversion::fromLocal (to, from, nr); } size_t CanonicalDataConversion::fromLocal (void* to, const unsigned char* from, size_t nr) const { return CanonicalConversion::fromLocal (to, from, nr); } size_t CanonicalDataConversion::fromLocal (void* to, const short* from, size_t nr) const { return CanonicalConversion::fromLocal (to, from, nr); } size_t CanonicalDataConversion::fromLocal (void* to, const unsigned short* from, size_t nr) const { return CanonicalConversion::fromLocal (to, from, nr); } size_t CanonicalDataConversion::fromLocal (void* to, const int* from, size_t nr) const { return CanonicalConversion::fromLocal (to, from, nr); } size_t CanonicalDataConversion::fromLocal (void* to, const unsigned int* from, size_t nr) const { return CanonicalConversion::fromLocal (to, from, nr); } size_t CanonicalDataConversion::fromLocal (void* to, const Int64* from, size_t nr) const { return CanonicalConversion::fromLocal (to, from, nr); } size_t CanonicalDataConversion::fromLocal (void* to, const uInt64* from, size_t nr) const { return CanonicalConversion::fromLocal (to, from, nr); } size_t CanonicalDataConversion::fromLocal (void* to, const float* from, size_t nr) const { return CanonicalConversion::fromLocal (to, from, nr); } size_t CanonicalDataConversion::fromLocal (void* to, const double* from, size_t nr) const { return CanonicalConversion::fromLocal (to, from, nr); } Bool CanonicalDataConversion::canCopy (const char*) const { return (CONVERT_CAN_CHAR == 0); } Bool CanonicalDataConversion::canCopy (const unsigned char*) const { return (CONVERT_CAN_UCHAR == 0); } Bool CanonicalDataConversion::canCopy (const short*) const { return (CONVERT_CAN_SHORT == 0); } Bool CanonicalDataConversion::canCopy (const unsigned short*) const { return (CONVERT_CAN_USHORT == 0); } Bool CanonicalDataConversion::canCopy (const int*) const { return (CONVERT_CAN_INT == 0); } Bool CanonicalDataConversion::canCopy (const unsigned int*) const { return (CONVERT_CAN_UINT == 0); } Bool CanonicalDataConversion::canCopy (const Int64*) const { return (CONVERT_CAN_INT64 == 0); } Bool CanonicalDataConversion::canCopy (const uInt64*) const { return (CONVERT_CAN_UINT64 == 0); } Bool CanonicalDataConversion::canCopy (const float*) const { return (CONVERT_CAN_FLOAT == 0); } Bool CanonicalDataConversion::canCopy (const double*) const { return (CONVERT_CAN_DOUBLE == 0); } unsigned int CanonicalDataConversion::externalSize (const char*) const { return SIZE_CAN_CHAR; } unsigned int CanonicalDataConversion::externalSize (const unsigned char*) const { return SIZE_CAN_UCHAR; } unsigned int CanonicalDataConversion::externalSize (const short*) const { return SIZE_CAN_SHORT; } unsigned int CanonicalDataConversion::externalSize (const unsigned short*) const { return SIZE_CAN_USHORT; } unsigned int CanonicalDataConversion::externalSize (const int*) const { return SIZE_CAN_INT; } unsigned int CanonicalDataConversion::externalSize (const unsigned int*) const { return SIZE_CAN_UINT; } unsigned int CanonicalDataConversion::externalSize (const Int64*) const { return SIZE_CAN_INT64; } unsigned int CanonicalDataConversion::externalSize (const uInt64*) const { return SIZE_CAN_UINT64; } unsigned int CanonicalDataConversion::externalSize (const float*) const { return SIZE_CAN_FLOAT; } unsigned int CanonicalDataConversion::externalSize (const double*) const { return SIZE_CAN_DOUBLE; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/OS/CanonicalDataConversion.h000066400000000000000000000200161476623553700214530ustar00rootroot00000000000000//# CanonicalDataConversion.h: A class with virtual functions to convert canonical format //# Copyright (C) 1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_CANONICALDATACONVERSION_H #define CASA_CANONICALDATACONVERSION_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A class with virtual functions to convert canonical format. // // // // // // This class is a specialization of the abstract base class // DataConversion. // It contains functions to convert data from/to the canonical format // using the static functions in class // CanonicalConversion. // // // See example in class DataConversion. // // // This class is an addition to // CanonicalConversion // to be able to use the conversion functions in a polymorphic way. // // //
        15. Support data type long double. // class CanonicalDataConversion : public DataConversion { public: // Construct the object. CanonicalDataConversion(); virtual ~CanonicalDataConversion(); // Convert one value from canonical format to local format. // The from and to buffer should not overlap. // size_t toLocal (char& to, const void* from) const override; size_t toLocal (unsigned char& to, const void* from) const override; size_t toLocal (short& to, const void* from) const override; size_t toLocal (unsigned short& to, const void* from) const override; size_t toLocal (int& to, const void* from) const override; size_t toLocal (unsigned int& to, const void* from) const override; size_t toLocal (Int64& to, const void* from) const override; size_t toLocal (uInt64& to, const void* from) const override; size_t toLocal (float& to, const void* from) const override; size_t toLocal (double& to, const void* from) const override; // // Convert nr values from canonical format to local format. // The from and to buffer should not overlap. // size_t toLocal (char* to, const void* from, size_t nr) const override; size_t toLocal (unsigned char* to, const void* from, size_t nr) const override; size_t toLocal (short* to, const void* from, size_t nr) const override; size_t toLocal (unsigned short* to, const void* from, size_t nr) const override; size_t toLocal (int* to, const void* from, size_t nr) const override; size_t toLocal (unsigned int* to, const void* from, size_t nr) const override; size_t toLocal (Int64* to, const void* from, size_t nr) const override; size_t toLocal (uInt64* to, const void* from, size_t nr) const override; size_t toLocal (float* to, const void* from, size_t nr) const override; size_t toLocal (double* to, const void* from, size_t nr) const override; // // Convert one value from local format to canonical format. // The from and to buffer should not overlap. // size_t fromLocal (void* to, char from) const override; size_t fromLocal (void* to, unsigned char from) const override; size_t fromLocal (void* to, short from) const override; size_t fromLocal (void* to, unsigned short from) const override; size_t fromLocal (void* to, int from) const override; size_t fromLocal (void* to, unsigned int from) const override; size_t fromLocal (void* to, Int64 from) const override; size_t fromLocal (void* to, uInt64 from) const override; size_t fromLocal (void* to, float from) const override; size_t fromLocal (void* to, double from) const override; // // Convert nr values from local format to canonical format. // The from and to buffer should not overlap. // size_t fromLocal (void* to, const char* from, size_t nr) const override; size_t fromLocal (void* to, const unsigned char* from, size_t nr) const override; size_t fromLocal (void* to, const short* from, size_t nr) const override; size_t fromLocal (void* to, const unsigned short* from, size_t nr) const override; size_t fromLocal (void* to, const int* from, size_t nr) const override; size_t fromLocal (void* to, const unsigned int* from, size_t nr) const override; size_t fromLocal (void* to, const Int64* from, size_t nr) const override; size_t fromLocal (void* to, const uInt64* from, size_t nr) const override; size_t fromLocal (void* to, const float* from, size_t nr) const override; size_t fromLocal (void* to, const double* from, size_t nr) const override; // // Determine if the data for a data type can be simply copied, thus // if no conversion is needed. // Bool canCopy (const char*) const override; Bool canCopy (const unsigned char*) const override; Bool canCopy (const short*) const override; Bool canCopy (const unsigned short*) const override; Bool canCopy (const int*) const override; Bool canCopy (const unsigned int*) const override; Bool canCopy (const Int64*) const override; Bool canCopy (const uInt64*) const override; Bool canCopy (const float*) const override; Bool canCopy (const double*) const override; // // Get the external size of the data type. // unsigned int externalSize (const char*) const override; unsigned int externalSize (const unsigned char*) const override; unsigned int externalSize (const short*) const override; unsigned int externalSize (const unsigned short*) const override; unsigned int externalSize (const int*) const override; unsigned int externalSize (const unsigned int*) const override; unsigned int externalSize (const Int64*) const override; unsigned int externalSize (const uInt64*) const override; unsigned int externalSize (const float*) const override; unsigned int externalSize (const double*) const override; // }; inline CanonicalDataConversion::CanonicalDataConversion() {} } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/OS/Conversion.cc000066400000000000000000000564321476623553700172220ustar00rootroot00000000000000//# Conversion.cc: A class with static functions to convert canonical format //# Copyright (C) 1996,2011 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #ifdef __SSE2__ #include #endif #ifdef _OPENMP #include #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN size_t Conversion::boolToBit (void* to, const void* from, size_t nvalues) { const Bool* data = (const Bool*)from; unsigned char* bits = (unsigned char*)to; size_t i = 0; #ifdef __SSE2__ __m128i zero = _mm_setzero_si128(); for (i = 0; i < nvalues - (nvalues & 0xF); i+=16) { __m128i v = _mm_loadu_si128((const __m128i*)&data[i]); /* compare to zero to convert false -> 0xFF and true -> 0x00 */ v = _mm_cmpeq_epi8(v, zero); /* extract most significant bit of each byte * not result to revert compare against zero */ unsigned int r = ~(unsigned int)_mm_movemask_epi8(v); /* store the 16 bits */ memcpy(&bits[i / 8], &r, 2); } data = &data[i]; #endif //# Fill as many full bytes as possible. //# Note: the compiler can optimize much better for j<8 than j 0) { unsigned char& ch = bits[nfbytes]; ch = 0; //# Take care of correct number of bits in last byte. for (size_t j=0; j 0 || endBit1 < 8) { unsigned char& ch = bits[startByte++]; unsigned char mask = (1 << startBit1); for (size_t j=startBit1; j 0) { unsigned char& ch = bits[endByte]; unsigned char mask = 1; for (size_t j=0; j 0) { int ch = bits[nfbytes]; for (size_t j=0; j= 32 * 1024) num_threads(nthr) #endif for (size_t i = 0; i < nwords; ++i) { data[i] = conv_tab[bits[i]].d; } return nwords * (bits_per_loop / 8) + bitToBool_ (&data[nwords], &bits[nwords], nvalues - (nwords * bits_per_loop)); } void Conversion::bitToBool (void* to, const void* from, size_t startBit, size_t nvalues) { Bool* data = (Bool*)to; const unsigned char* bits = (const unsigned char*)from; //# Determine the first and last byte to be read //# and the first and last bit in the first and last byte. size_t startByte = startBit / 8; size_t startBit1 = startBit - 8 * startByte; size_t endByte = (startBit + nvalues) / 8; size_t endBit1 = 8; size_t endBit2 = startBit + nvalues - 8 * endByte; //# Take care if only one byte has to be handled. if (startByte == endByte) { endBit1 = endBit2; endBit2 = 0; } //# Set the bits in the first byte (if needed). if (startBit1 > 0 || endBit1 < 8) { int ch = bits[startByte++]; for (size_t j=startBit1; j 0) { int ch = bits[endByte]; for (size_t j=0; j #include // needed for memcpy namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A class with general conversion definitions // // // // // // This class contains the general definitions for the Conversion classes. //
            //
          • // It defines the signature for the functions converting from input // to output format (e.g. from local to canonical format). There is // a version where the number of values is given and a version where // the number of bytes is given. The latter is there to be able to use // memcpy as a conversion function (which will often be the case). // Note that the signatures only differ in return value. //
          • // It defines functions to convert Bools to bits and vice-versa. // These are used elsewhere to store Bools as space efficient as possible. // Note that these functions are machine independent (they work on little // and big endian machines). //
          • // It defines a private version of memcpy for compilers having a // different signature for memcpy (e.g. ObjectCenter and DEC-Alpha). //
          // Static functions in the classes // CanonicalConversion, // VAXConversion, and // IBMConversion convert data // from/to canonical, VAX, and IBM/360 format, resp.. //
          Classes derived from // DataConversion // provide the same functionality in a polymorphic way. //
          // // This provides a common place for definitions used elsewhere. // It also provides a uniform interface to memcpy. // //# //# class Conversion { public: // Define the signature of a function converting nvalues // values from internal to external format or vice-versa. // These functions are used in the IO framework // , but are also used in the table system. // Examples of such conversions are: //
          - local <-> canonical (when storing in canonical format) //
          - local <-> local (when storing in local format) //
          - binary <-> ASCII //
          It returns the number of bytes in external format. // (For example the ToLocal/FromLocal functions in class // CanonicalConversion // return the number of bytes in canonical format). typedef size_t ValueFunction (void* to, const void* from, size_t nvalues); // Define the signature of a function converting from one // format to another providing the number of bytes. // It returns the to pointer (similar to memcpy). // (For example the byteTo/FromLocalXXX functions in class // CanonicalConversion. typedef void* ByteFunction (void* to, const void* from, size_t nbytes); // Convert a stream of Bools to output format (as bits). // The variable startBit (0-relative) indicates // where to start in the to buffer. // static size_t boolToBit (void* to, const void* from, size_t nvalues); static void boolToBit (void* to, const void* from, size_t startBit, size_t nvalues); // // Convert a stream of Bools to output format (as bits). // The variable startBit (0-relative) indicates // where to start in the from buffer. // static size_t bitToBool (void* to, const void* from, size_t nvalues); static void bitToBool (void* to, const void* from, size_t startBit, size_t nvalues); // // Copy a value using memcpy. // It differs from memcpy in the return value. // This version has the ValueFunction signature, // but it expects as input the number of bytes. // static size_t valueCopy (void* to, const void* from, size_t nbytes); // Get a pointer to the memcpy function. static ByteFunction* getmemcpy(); private: // Copy bits to Bool in an unoptimized way needed when 'to' is not // aligned properly. static size_t bitToBool_ (void* to, const void* from, size_t nvalues); }; inline Conversion::ByteFunction* Conversion::getmemcpy() { return memcpy; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/OS/DOos.cc000066400000000000000000000262451476623553700157400ustar00rootroot00000000000000//# DOos.cc: Functions used to implement the DO functionality //# Copyright (C) 1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Vector DOos::isValidPathName (const Vector& pathName) { Vector result(pathName.nelements()); for (uInt i=0; i DOos::fileExists (const Vector& pathName, Bool follow) { Vector result(pathName.nelements()); for (uInt i=0; i DOos::fileType (const Vector& pathName, Bool follow) { Vector result(pathName.nelements()); for (uInt i=0; i DOos::fileNames (const String& directoryName, const String& fileNamePattern, const String& fileTypes, Bool all, Bool follow) { // Determine if and how to select on file type. Bool takeRegular = (fileTypes.contains ('r')); Bool takeDirectory = (fileTypes.contains ('d')); Bool takeSymLink = (fileTypes.contains ('s')); Bool takeReadable = (fileTypes.contains ('R')); Bool takeWritable = (fileTypes.contains ('W')); Bool takeExecutable = (fileTypes.contains ('X')); Bool checkType = (takeRegular || takeDirectory || takeSymLink); Bool checkAcc = (takeReadable || takeWritable || takeExecutable); Bool check = (checkType || checkAcc); // Set up the iterator. Default pattern is all. Vector result; Directory dir (directoryName); uInt n = 0; DirectoryIterator iter (dir); if (! fileNamePattern.empty()) { iter = DirectoryIterator (dir, Regex(Regex::fromPattern (fileNamePattern))); } // Iterate through the directory and add matching name to result. // Skip names starting with . if all is False. for (; !iter.pastEnd(); iter++) { String name = iter.name(); if (name[0] != '.' || all) { if (check) { File file(directoryName + '/' + name); if (checkType) { if (!( (takeRegular && file.isRegular (follow)) || (takeDirectory && file.isDirectory (follow)) || (takeSymLink && file.isSymLink()))) { continue; } } if (checkAcc) { if (!( (takeReadable && file.isReadable()) || (takeWritable && file.isWritable()) || (takeExecutable && file.isExecutable()))) { continue; } } } if (n >= result.nelements()) { result.resize (result.nelements() + 100, True); } result(n++) = name; } } result.resize (n, True); return result; } void DOos::makeDirectory (const Vector& directoryName, Bool makeParent) { for (uInt i=0; i parName(1); parName(0) = parent; makeDirectory (parName, makeParent); } } Directory dir(file); dir.create (False); } } Vector DOos::fullName (const Vector& fileName) { Vector result(fileName.nelements()); for (uInt i=0; i DOos::dirName (const Vector& fileName) { Vector result(fileName.nelements()); for (uInt i=0; i DOos::baseName (const Vector& fileName) { Vector result(fileName.nelements()); for (uInt i=0; i DOos::fileTime (const Vector& fileName, Int whichTime, Bool follow) { Vector result(fileName.nelements()); for (uInt i=0; i DOos::totalSize (const Vector& fileName, Bool follow) { Vector result(fileName.nelements()); for (uInt i=0; i DOos::freeSpace (const Vector& fileName, Bool follow) { Vector result(fileName.nelements()); for (uInt i=0; i (1, fileName), recursive, mustExist, follow); } void DOos::remove (const Vector& fileNames, Bool recursive, Bool mustExist, Bool follow) { uInt i; if (mustExist) { for (i=0; i DOos::lockInfo (const String& tableName) { Vector result(3); uInt pid; Bool permLocked; result(0) = LockFile::showLock (pid, permLocked, tableName + "/table.lock"); result(1) = pid; result(2) = (permLocked ? 1 : 0); return result; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/OS/DOos.h000066400000000000000000000145121476623553700155740ustar00rootroot00000000000000//# DOos.h: Functions used to implement the DO functionality //# Copyright (C) 1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_DOOS_H #define CASA_DOOS_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class String; // // DO for accessing os-specific functions // // // // // //
        16. OS // // // // // This class serves as the connection between the OS module and a tasking // interface in Glish or Python. // It is meant for access to OS-specific functions, in // particular file handling. // // // // // // //
        17. AipsError if AIPSPATH or HOME is not defined // // //
        18. Check for feasable extensions // class DOos { public: // Are the given path names valid? // I.e. does a file with the given name exist or can it be created? static Vector isValidPathName (const Vector& pathName); // Do the given files exist? // If follow is False, symbolic links are not followed. static Vector fileExists (const Vector& fileName, Bool follow = True); // Give the type of the given files. static Vector fileType (const Vector& fileName, Bool follow = True); // Give all file names in the directory matching the given pattern // and file types. //
          The pattern can be a string like the filename pattern given in // a shell (e.g. '*.cc'). If the string is empty, all files are taken // into account. //
          Filetypes is a string determining which file types will be selected. // Each character in the string determines a file type. They are: //
          //
          r
          regular file //
          d
          directory //
          s
          symbolic link //
          R
          readable file //
          W
          writable file //
          X
          executable file //
          // The all flag determines if file names starting with a . will also // be selected. static Vector fileNames (const String& directoryName, const String& fileNamePattern, const String& fileTypes, Bool all = False, Bool follow = True); // Make directories. It throws an exception if a file with that // name already exists. static void makeDirectory (const Vector& directoryNames, Bool makeParent = False); // Return the full absolute names for the given names. static Vector fullName (const Vector& fileName); // Return the full directory names of the given files. static Vector dirName (const Vector& fileName); // Return the base names of the given files. static Vector baseName (const Vector& fileName); // Get the time of the given files. // whichTime determines which time to return: //
          1 = time of last access //
          2 = time of last modification //
          3 = time of last status change static Vector fileTime (const Vector& fileName, Int whichTime = 1, Bool follow = True); // Return the total size (in bytes) for each file or directory given. // For a directory the size of all files (recursively) in it is given. // If follow is False, symbolic links are not followed. // static Vector totalSize (const Vector& fileName, Bool follow = True); static Double totalSize (const String& fileName, Bool follow = True); // // Return the total size on the devices the given directories are on. // If follow is False, symbolic links are not followed. static Vector freeSpace (const Vector& fileName, Bool follow = True); // Copy the file (or directory recursively). // If from is a symbolic link and follow is False, only the // symbolic link is copied. static void copy (const String& to, const String& from, Bool overwrite = True, Bool follow = True); // Move the file or directory. // If from is a symbolic link and follow is False, only the // symbolic link is moved. static void move (const String& to, const String& from, Bool overwrite = True, Bool follow = True); // Remove the files (or directories recursively). // If fileName is a symbolic link and follow is False, only the // symbolic link is removed. // static void remove (const String& fileName, Bool recursive, Bool mustExist = True, Bool follow = True); static void remove (const Vector& fileNames, Bool recursive, Bool mustExist = True, Bool follow = True); // // Tell if a table is used or locked by another process. // It returns a vector containing 3 integers. // The first one tells if the table is in use or locked. // See LockFile\::showLock for details. // The second one gives the pid of the process using/locking the table. // The third one tells if the table is permanently locked (0 = not). static Vector lockInfo (const String& tableName); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/OS/DataConversion.cc000066400000000000000000000026141476623553700200050ustar00rootroot00000000000000//# DataConversion.cc: Abstract base class with functions to convert any format //# Copyright (C) 1996 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include namespace casacore { //# NAMESPACE CASACORE - BEGIN DataConversion::~DataConversion() {} } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/OS/DataConversion.h000066400000000000000000000230001476623553700176370ustar00rootroot00000000000000//# DataConversion.h: Abstract base class with functions to convert any format //# Copyright (C) 1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_DATACONVERSION_H #define CASA_DATACONVERSION_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Abstract base class with functions to convert any format // // // // // // This abstract base class contains pure virtual functions to convert // from any foreign data format to local format and vice-versa. // Classes derived from it implement the functions for a specific // foreign format (e.g. // CanonicalDataConversion // , // VAXDataConversion, // IBMDataConversion). // RawDataConversion). // // // // // Construct the correct conversion object. // DataConversion* conv = new IBMDataConversion(); // // Say that you read a block of 256 floats (in IBM-format). // char buffer[1024]; // read (fd, buffer, 1024); // // Convert the float to local format. // float values[256]; // conv->toLocal (values, buffer, 256); // // // // The abstract base class allows one to construct the correct conversion // object at the beginning. Thereafter this base class can be used and // polymorphism takes care of picking the correct functions. // // //
        19. Support data type long double. // class DataConversion { public: // Construct the object. DataConversion(); virtual ~DataConversion(); // Convert one value from foreign format to local format. // The from and to buffer should not overlap. // // The char version handles characters (thus may involve conversion // EBCDIC to ASCII), while the unsigned chars are simply bytes. // // virtual size_t toLocal (char& to, const void* from) const = 0; virtual size_t toLocal (unsigned char& to, const void* from) const = 0; virtual size_t toLocal (short& to, const void* from) const = 0; virtual size_t toLocal (unsigned short& to, const void* from) const = 0; virtual size_t toLocal (int& to, const void* from) const = 0; virtual size_t toLocal (unsigned int& to, const void* from) const = 0; virtual size_t toLocal (Int64& to, const void* from) const = 0; virtual size_t toLocal (uInt64& to, const void* from) const = 0; virtual size_t toLocal (float& to, const void* from) const = 0; virtual size_t toLocal (double& to, const void* from) const = 0; // // Convert nr values from foreign format to local format. // The from and to buffer should not overlap. // // The char version handles characters (thus may involve conversion // EBCDIC to ASCII), while the unsigned chars are simply bytes. // // virtual size_t toLocal (char* to, const void* from, size_t nr) const = 0; virtual size_t toLocal (unsigned char* to, const void* from, size_t nr) const = 0; virtual size_t toLocal (short* to, const void* from, size_t nr) const = 0; virtual size_t toLocal (unsigned short* to, const void* from, size_t nr) const = 0; virtual size_t toLocal (int* to, const void* from, size_t nr) const = 0; virtual size_t toLocal (unsigned int* to, const void* from, size_t nr) const = 0; virtual size_t toLocal (Int64* to, const void* from, size_t nr) const = 0; virtual size_t toLocal (uInt64* to, const void* from, size_t nr) const = 0; virtual size_t toLocal (float* to, const void* from, size_t nr) const = 0; virtual size_t toLocal (double* to, const void* from, size_t nr) const = 0; // // Convert one value from local format to foreign format. // The from and to buffer should not overlap. // // The char version handles characters (thus may involve conversion // ASCII to EBCDIC), while the unsigned chars are simply bytes. // // virtual size_t fromLocal (void* to, char from) const = 0; virtual size_t fromLocal (void* to, unsigned char from) const = 0; virtual size_t fromLocal (void* to, short from) const = 0; virtual size_t fromLocal (void* to, unsigned short from) const = 0; virtual size_t fromLocal (void* to, int from) const = 0; virtual size_t fromLocal (void* to, unsigned int from) const = 0; virtual size_t fromLocal (void* to, Int64 from) const = 0; virtual size_t fromLocal (void* to, uInt64 from) const = 0; virtual size_t fromLocal (void* to, float from) const = 0; virtual size_t fromLocal (void* to, double from) const = 0; // // Convert nr values from local format to foreign format. // The from and to buffer should not overlap. // // The char version handles characters (thus may involve conversion // ASCII to EBCDIC), while the unsigned chars are simply bytes. // // virtual size_t fromLocal (void* to, const char* from, size_t nr) const = 0; virtual size_t fromLocal (void* to, const unsigned char* from, size_t nr) const = 0; virtual size_t fromLocal (void* to, const short* from, size_t nr) const = 0; virtual size_t fromLocal (void* to, const unsigned short* from, size_t nr) const = 0; virtual size_t fromLocal (void* to, const int* from, size_t nr) const = 0; virtual size_t fromLocal (void* to, const unsigned int* from, size_t nr) const = 0; virtual size_t fromLocal (void* to, const Int64* from, size_t nr) const = 0; virtual size_t fromLocal (void* to, const uInt64* from, size_t nr) const = 0; virtual size_t fromLocal (void* to, const float* from, size_t nr) const = 0; virtual size_t fromLocal (void* to, const double* from, size_t nr) const = 0; // // Determine if the data for a data type can be simply copied, thus // if no conversion is needed. // virtual Bool canCopy (const char*) const = 0; virtual Bool canCopy (const unsigned char*) const = 0; virtual Bool canCopy (const short*) const = 0; virtual Bool canCopy (const unsigned short*) const = 0; virtual Bool canCopy (const int*) const = 0; virtual Bool canCopy (const unsigned int*) const = 0; virtual Bool canCopy (const Int64*) const = 0; virtual Bool canCopy (const uInt64*) const = 0; virtual Bool canCopy (const float*) const = 0; virtual Bool canCopy (const double*) const = 0; // // Get the external size of the data type. // virtual unsigned int externalSize (const char*) const = 0; virtual unsigned int externalSize (const unsigned char*) const = 0; virtual unsigned int externalSize (const short*) const = 0; virtual unsigned int externalSize (const unsigned short*) const = 0; virtual unsigned int externalSize (const int*) const = 0; virtual unsigned int externalSize (const unsigned int*) const = 0; virtual unsigned int externalSize (const Int64*) const = 0; virtual unsigned int externalSize (const uInt64*) const = 0; virtual unsigned int externalSize (const float*) const = 0; virtual unsigned int externalSize (const double*) const = 0; // }; inline DataConversion::DataConversion() {} } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/OS/Directory.cc000066400000000000000000000337451476623553700170430ustar00rootroot00000000000000//# Directory.cc: Class to define a Directory //# Copyright (C) 1996,1997,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // we NEED to include aips(env).h before using any AIPS_xyz defines #include #if defined(AIPS_SOLARIS) || defined(AIPS_OSF) # if defined(AIPS_OSF) extern "C" { // missing in system include file # endif # if defined(AIPS_SOLARIS) # undef _FILE_OFFSET_BITS # undef _LARGEFILE_SOURCE # endif # include // needed for statvfs # if defined(AIPS_OSF) } # endif # define statfs statvfs #elif defined(AIPS_DARWIN) || defined(AIPS_BSD) #include #include #else # include # if defined(AIPS_IRIX) # include # define f_bavail f_bfree # endif #endif #include #include #include #include #include #include #include #include #include #include // needed for rmdir, unlink #include // needed for mkdir #include // needed for errno #include // needed for strerror // Shouldn't be needed, but is needed to get rename under linux. The // man page claims it's in unistd.h. #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Directory::Directory() : File() // sets to working directory {} Directory::Directory (const Path& name) : File(name) { checkPath(); } Directory::Directory (const String& name) : File(name) { checkPath(); } Directory::Directory (const File& name) : File(name) { checkPath(); } Directory::Directory (const Directory& directory) : File (directory), itsFile (directory.itsFile) {} Directory::~Directory() {} Directory& Directory::operator= (const Directory& directory) { if (this != &directory) { File::operator= (directory); itsFile = directory.itsFile; } return *this; } void Directory::checkPath() { itsFile = *this; // If exists, check if it is a directory. // If the file is a symlink, resolve the entire symlink chain. // Otherwise check if it can be created. if (exists()) { if (isSymLink()) { itsFile = SymLink(*this).followSymLink(); // Error if no directory and exists or cannot be created. if (!itsFile.isDirectory()) { if (itsFile.exists() || !itsFile.canCreate()) { throw (AipsError ("Directory: " + path().expandedName() + " is a symbolic link not" " pointing to a valid directory")); } } } else if (!isDirectory()) { throw (AipsError ("Directory: " + path().expandedName() + " exists, but is no directory")); } } else { if (!canCreate()) { throw (AipsError ("Directory: " + path().expandedName() + " does not exist and cannot be created")); } } } uInt Directory::nEntries() const { uInt nentries = 0; DirectoryIterator iter(*this); while (! iter.pastEnd()) { nentries++; iter++; } return nentries; } Bool Directory::isEmpty() const { DirectoryIterator iter(*this); while (! iter.pastEnd()) { String nm (iter.name()); if (nm.size() < 5 || nm.before(4) != ".nfs") { /// cout <<"iter at "< 0) { bsize = buf.f_frsize; } #endif return bsize * buf.f_bavail; #endif } void Directory::create (Bool overwrite) { // If overwrite is False the directory will not be overwritten. if (exists()) { if (!itsFile.isDirectory()) { throw (AipsError ("Directory::create: " + itsFile.path().expandedName() + " already exists as a non-directory")); } if (!overwrite) { throw (AipsError ("Directory::create: " + itsFile.path().expandedName() + " already exists")); } // Keep the directory, so special allocation on Lustre is preserved. Directory(itsFile).removeRecursive(True); } else { if (mkdir (itsFile.path().expandedName().chars(), 0777) < 0) { throw (AipsError ("Directory::create error on " + itsFile.path().expandedName() + ": " + strerror(errno))); } } } void Directory::remove() { // If the directory is not empty it cannot be removed. if (! isEmpty()) { throw (AipsError ("Directory::remove: " + path().expandedName() + " is not empty")); } if (isSymLink()) { removeSymLinks(); } rmdir (itsFile.path().absoluteName().chars()); } void Directory::removeFiles() { DirectoryIterator iter(*this); while (! iter.pastEnd()) { File file = iter.file(); if (! file.isDirectory (False)) { unlink (file.path().originalName().chars()); } iter++; } } void Directory::removeRecursive (Bool keepDir) { DirectoryIterator iter(*this); while (! iter.pastEnd()) { File file = iter.file(); if (file.isDirectory (False)) { Directory(file).removeRecursive(); } else { unlink (file.path().originalName().chars()); } iter++; } if (!keepDir) { remove(); } } Int64 Directory::size() const { Int64 totSize=0; DirectoryIterator iter(*this); while (! iter.pastEnd()) { File file = iter.file(); if (file.isDirectory ()) { totSize+=Directory(file).size(); } else { totSize+=file.size(); } iter++; } return totSize; } void Directory::copy (const Path& target, Bool overwrite, Bool setUserWritePermission) const { Path targetName(target); checkTarget (targetName, overwrite, True); // Remove the target if it already exists. File targetFile(targetName); if (targetFile.isRegular (False)) { RegularFile(targetFile).remove(); } else if (targetFile.isDirectory (False)) { Directory(targetFile).removeRecursive(); } else { SymLink(targetFile).remove(); } // Copy the entire directory recursively using the system function cp. #if defined(AIPS_CRAY_PGI) // On the Cray XT3 the system call is not supported, so we have to // do it ourselves. copyRecursive (targetName.expandedName()); #else String command("cp -r '"); command += itsFile.path().expandedName() + "' '" + targetName.expandedName() + "'"; int result = system(command.chars()); if(result != 0) { throw AipsError("Executing cp command returned an error. Command was: " + command); } // Give write permission to user if needed. if (setUserWritePermission) { #if defined(__hpux__) || defined(AIPS_IRIX) command = "chmod -R u+w '"; #else command = "chmod -Rf u+w '"; #endif command += targetName.expandedName() + "'"; int result = system(command.chars()); if(result != 0) throw AipsError("Executing chmod command returned an error. Command was: " + command); } #endif } void Directory::copyRecursive (const String& target) const { // First create the directory. Directory dir(target); dir.create (True); // Now loop over all files and copy. DirectoryIterator iter(*this); while (! iter.pastEnd()) { File file = iter.file(); String outName = target + '/' + file.path().baseName(); if (file.isDirectory (False)) { Directory(file).copyRecursive (outName); } else { RegularFile::manualCopy (file.path().originalName(), outName); } iter++; } } void Directory::move (const Path& target, Bool overwrite) { Path targetPath(target); checkTarget (targetPath, overwrite, True); // Start trying to rename. // If source and target are the same directory, rename does nothing // and returns a success status. if (rename (path().expandedName().chars(), targetPath.expandedName().chars()) == 0) { return; } // The rename failed for one reason or another. // Remove the target if it already exists. Bool alrExist = False; if (errno == EEXIST) { alrExist = True; } #if defined(ENOTEMPTY) if (errno == ENOTEMPTY) { alrExist = True; } #endif #if defined(EBUSY) if (errno == EBUSY) { alrExist = True; } #endif if (alrExist) { Directory(targetPath).removeRecursive(); } else if (errno == ENOTDIR) { unlink (targetPath.expandedName().chars()); } // Try again. if (rename (path().expandedName().chars(), targetPath.expandedName().chars()) == 0) { return; } // Throw an exception if not "different file systems" error. if (errno != EXDEV) { throw (AipsError ("Directory::move error on " + path().expandedName() + " to " + targetPath.expandedName() + ": " + strerror(errno))); } // Copy the directory and remove it thereafter. copy (targetPath, overwrite, False); removeRecursive(); } // Vector Directory::find (const String& fileName, // Bool followSymLinks) const // { // //#// return find ('^' + Regex::fromString (fileName) + '$', followSymLinks); // return find (Regex (Regex::fromString (fileName)), followSymLinks); // } Vector Directory::find (const Regex& regexp, Bool followSymLinks, Bool recursive) const { DirectoryIterator iter(*this); Vector myentries(10); uInt count=0; while (!iter.pastEnd()) { //#// if (iter.name().contains (regexp)) { if (iter.name().matches (regexp)) { if (count + 1 >= myentries.nelements()) { // More entries have been added - have to resize myentries.resize (2*myentries.nelements(), True); } myentries(count) = iter.name(); count++; } iter++; } myentries.resize (count, True); // Trim the trailing entries // Now recursively add all the ones we find in subdirectories, prepending // the pathname. if (recursive) { iter.reset(); while (!iter.pastEnd()) { File file = iter.file(); if (file.isDirectory (followSymLinks)) { Directory subdir = file; Vector subentries = subdir.find (regexp); String basename = iter.name() + "/"; subentries = basename + subentries; uInt oldsize = myentries.nelements(); myentries.resize (oldsize + subentries.nelements(), True); myentries(Slice(oldsize, subentries.nelements())) = subentries; } iter++; } } return myentries; } Vector Directory::shellExpand (const Vector& files, Bool stripPath) // // Take a list of potentially wild-carded file names, and expand // them into (optional) absolute path and name. Some more development // needed to expand the path as well, if there are any wild cards in that. // { Vector expInNames; uInt nExpInNames = 0; uInt k = 0; Regex exp; // for (uInt i=0; i expFiles = dir.find(exp, True, False); nExpInNames += expFiles.nelements(); expInNames.resize(nExpInNames, True); // Add the path back on to each name if (stripPath) { for (uInt j=0; j #include #else #include #include #include #endif Bool Directory::isNFSMounted() const { struct statfs buf; if (statfs (itsFile.path().expandedName().chars(), &buf) < 0) { throw (AipsError ("Directory::isNFSMounted error on " + itsFile.path().expandedName() + ": " + strerror(errno))); } #ifndef __APPLE__ return buf.f_type == NFS_SUPER_MAGIC; #else return buf.f_type == VT_NFS; #endif } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/OS/Directory.h000066400000000000000000000232231476623553700166730ustar00rootroot00000000000000//# Directory.h: Get information about, and manipulate directories //# Copyright (C) 1996,1997,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_DIRECTORY_H #define CASA_DIRECTORY_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class Regex; class String; // // Get information about, and manipulate directories // // // // // //
        20. Basic knowledge of the UNIX file system //
        21. File // // // Directory provides functions to manipulate and to get information about // directories. The functions for getting information (like ownership, dates) // about directories are inherited from the File // class. // Directory itself provides functions to create, copy, move, or remove // a directory. The file name can be a symbolic link resolving // (eventually) to a directory. //

          // A separate class DirectoryIterator // allows one to traverse a directory to get the file names in it. // // // // Directory dir("someDir"); // // Create directory someDir in the working directory. // dir.create(); // cout << dir.nEntries(); // #entries // // Assign to another directory. // dir = Directory("otherDir"); // // Remove the directory and its contents. // dir.removeRecursive(); // // // // Provide functions for manipulating and getting information // about directories. // class Directory: public File { public: // Sets the path on the current working directory Directory(); // Create a directory object for a file with the given path name. // An exception is thrown if the directory is illegal, i.e. if it does // not exist as a directory or symbolic link or if cannot be created. // Note that the directory is not created if it does not exist yet. // This can be done using the function create. //
          // When the given path name is a symbolic link, the symbolic link // is resolved (recursively) and the resulting directory name is used // instead. // Directory (const Path& name); Directory (const String& name); Directory (const File& name); // // Copy constructor (copy semantics). Directory (const Directory& that); ~Directory(); // Assignment (copy semantics). Directory& operator= (const Directory& that); // Check if directory is empty. // If the directory does not exist, an exception will be thrown. Bool isEmpty() const; // Return the number of entries in the directory (not counting . and ..). // If the directory does not exist, an exception will be thrown. uInt nEntries() const; // Get the amount of free space (in bytes) on the file system this // directory is on. When the directory path is a symbolic link, that // link is resolved first. // Double freeSpace() const; uInt freeSpaceInMB() const; // // Create the directory. //
          If the directory exists and overwrite=True, it will be removed // (recursively). Otherwise an exception is thrown. void create (Bool overwrite = True); // Remove a directory. // An exception is thrown if the directory is not empty. // If a symbolic link is given, the link chain pointing to the directory // will also be removed. void remove(); // Remove all files in the directory except subdirectories. // The directory itself is not removed. void removeFiles(); // Remove the directory and its contents (recursively in all // subdirectories). // If keepDir==True, the directory itself is kept //(to keep properties like placement on Lustre). void removeRecursive (Bool keepDir = False); // Copy the directory and its contents (recursively) to the target // path using the system command cp -r. // If the target already exists (as a file, directory or symlink), // and overwrite=True, it will first be removed. // The target directory is created and the data in the source // directory is copied to the new directory. //
          An exception is thrown if: //
          - the target directory is not writable //
          - or the target already exists and overwrite!=True // // 1. The behavior of this copy function is different from cp when the // target directory already exists. Cp copies the source to a // subdirectory of the target, while copy recreates the target. //
          2. When a readonly file is copied, cp the resulting // file is also readonly. Therefore chmod is used to // set user write permission after the copy. // The flag setUserWritePermission can be set to False // when that should not be done. //
          // void copy (const Path& target, Bool overwrite = True, Bool setUserWritePermission = True) const; void copy (const String& target, Bool overwrite = True, Bool setUserWritePermission = True) const; // // Copy a directory recursively in a manual way. // This is used in a copy using the system command is not possible // (like on the Cray XT3). void copyRecursive (const String& target) const; // Move the directory to the target path using the system command mv. // If the target already exists (as a file, directory or symlink), // and overwrite=True, it will first be removed. // The source directory is moved (thus renamed) to the target. //
          An exception is thrown if: //
          - the target directory is not writable //
          - or the target already exists and overwrite!=True // // The behavior of this move function is different from mv when the // target directory already exists. Mv moves the source to a // subdirectory of the target, while move recreates the target. // // void move (const Path& target, Bool overwrite = True); void move (const String& target, Bool overwrite = True); // // Find all files which whose names match regex. You // can do this recursively (default) or not. Note that the // matching is a regular expression match, not a shell file-expansion // match. However, a shell file pattern can be converted to a regexp // using the function Regex::fromPattern. // Regex::fromString allows one to convert a file name // to a regexp and to use this function for eact file name matching. //
          To match the semantics of the unix find command, // symbolic links are not followed by default, but this behavior // can be over-ridden. Vector find (const Regex& regexp, Bool followSymLinks=False, Bool recursive=True) const; // For each element of files, find all file names matching // it using shell file-expansion rules. Return the list of all matched files // as absolute path + file names. You may optionally drop the path and just return // the file names. Note tha if files(i) contains a path as well as a file // name, no matching is done on the path, just the trailing file name. // Throws an AipsError if the shell pattern is illegal. static Vector shellExpand (const Vector& files, Bool stripPath=False); // Return the total size of everything in the Directory. If the Directory // does not exist, an exception will be thrown. virtual Int64 size() const; //Check if a directory is mounted via NFS or not. Bool isNFSMounted() const; private: // Check if the path defines a directory. // Also resolve possible symlinks. void checkPath(); // This variable is used when a symbolic link is given to be // a directory. File itsFile; }; inline void Directory::copy (const String& target, Bool overwrite, Bool setUserWritePermission) const { copy (Path(target), overwrite, setUserWritePermission); } inline void Directory::move (const String& target, Bool overwrite) { move (Path(target), overwrite); } inline uInt Directory::freeSpaceInMB() const { return uInt (0.5 + freeSpace() / (1024*1024)); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/OS/DirectoryIterator.cc000066400000000000000000000127551476623553700205530ustar00rootroot00000000000000//# DirectoryIterator.cc: Class to define a DirectoryIterator //# Copyright (C) 1996,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include // needed for errno #include // needed for strerror namespace casacore { //# NAMESPACE CASACORE - BEGIN DirectoryIterator::DirectoryIterator() : itsDirectoryDescriptor (0), itsDirectoryEntry (0), itsEnd (False), itsDirectory (), itsExpression (".*") { init(); } DirectoryIterator::DirectoryIterator (const Directory& dir) : itsDirectoryDescriptor (0), itsDirectoryEntry (0), itsEnd (False), itsDirectory (dir), itsExpression (".*") { init(); } DirectoryIterator::DirectoryIterator (const Directory& dir, const Regex& regExpression) : itsDirectoryDescriptor (0), itsDirectoryEntry (0), itsEnd (False), itsDirectory (dir), itsExpression (regExpression) { init(); } DirectoryIterator::DirectoryIterator (const DirectoryIterator& that) : itsDirectoryDescriptor (0), itsDirectoryEntry (0), itsEnd (False), itsDirectory (that.itsDirectory), itsExpression (that.itsExpression) { init(); } DirectoryIterator::~DirectoryIterator() { #if defined(AIPS_CRAY_PGI) if (itsNameList != 0) { for (int i=0; i= itsNrNames) { itsDirectoryEntry = 0; } else { itsDirectoryEntry = itsNameList[itsNameInx++]; } #else itsDirectoryEntry = readdir (itsDirectoryDescriptor); #endif if (itsDirectoryEntry == 0){ itsEnd = True; break; } name = itsDirectoryEntry->d_name; } while (name == "." || name == ".." || name.matches (itsExpression) == 0); } void DirectoryIterator::operator++(int) { operator++(); } String DirectoryIterator::name() const { if (itsEnd) { throw (AipsError ("DirectoryIterator::name past end on " + itsDirectory.path().expandedName())); } return itsDirectoryEntry->d_name; } File DirectoryIterator::file() const { return itsDirectory.path().expandedName() + "/" + name(); } void DirectoryIterator::reset() { // Reset the directory to the beginning of the stream // and get the first entry. #if defined(AIPS_CRAY_PGI) itsNameInx = 0; #else rewinddir (itsDirectoryDescriptor); #endif itsEnd = False; operator++(); } Bool DirectoryIterator::pastEnd() const { return (itsEnd == True); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/OS/DirectoryIterator.h000066400000000000000000000136251476623553700204120ustar00rootroot00000000000000//# DirectoryIterator.h: Traverse the contents of a directory //# Copyright (C) 1996 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_DIRECTORYITERATOR_H #define CASA_DIRECTORYITERATOR_H //# Includes #include #include #include #include #include // needed for DIR namespace casacore { //# NAMESPACE CASACORE - BEGIN //

          // Traverse the contents of a directory // // // // // //
        22. Basic knowledge of the UNIX file system //
        23. Directory //
        24. possibly Regex // // // DirectoryIterator allows to traverse a directory. In this way all // file names in a directory can be gotten. Files . and .. will // always be skipped. //

          // By means of a regular expression it is possible to traverse the // directory selectively. That is, only the file names matching the regular // expression will be returned. Note that the regular expression is // a true regular expression (as defined by class // Regex and not a file expression as used in shells. // Thus to get all .cc files, one has to specify ".*\.cc" and not "*.cc". //

          // The File class can be used to determine if // a file represents a symlink, directory or regular file. // // // // Directory dir("testdir"); // // Get all .cc files. // // Note that Regex is a true regular expression and not a // // simplified file expression (like *.cc) as used in shells. // DirectoryIterator dirIter(dir, ".*.\cc"); // while (!dirIter.pastEnd()){ // cout << dirIter.name() << endl; // dirIter++; // } // // // // With this class it is easy to iterate through a directory. // // //

        25. Allow file expressions like *.cc. // However, it's probably better to make that part of Regex. // class DirectoryIterator { public: // Construct the iterator for the working directory. // All entries (except . and ..) will be traversed. // It positions the iterator on the first entry. DirectoryIterator(); // Construct the iterator for the given directory. // All entries (except . and ..) will be traversed. // It positions the iterator on the first entry. DirectoryIterator (const Directory& dir); // Construct the iterator for the given directory. // All entries matching the regular expression will be traversed. // It positions the iterator on the first entry. DirectoryIterator (const Directory& dir, const Regex& regExpression); // Copy constructor (copy semantics). // The iterator will be positioned at the beginning. DirectoryIterator (const DirectoryIterator& that); // Assignment (copy semantics). // The iterator will be positioned at the beginning. DirectoryIterator& operator= (const DirectoryIterator& that); ~DirectoryIterator(); // Position on the next matching entry in the directory. //
          An exception is thrown if the iterator is already past the end. // void operator++(); void operator++(int); // // Returns the file name at the current position. //
          An exception is thrown if the iterator is already past the end. String name() const; // Returns a File object for the file at the current position. // Note that this adds the path of the directory to get the // correct path for the file. //
          An exception is thrown if the iterator is already past the end. File file() const; // Reposition the directory stream on the first entry. void reset(); // Checks if the iterator is past the end. Bool pastEnd() const; private: // Initialize the iterator. void init(); // This variable is used for seeking in the directory. // The directory is opened and closed once during the lifetime // of the class. DIR *itsDirectoryDescriptor; // This structure is used for information of the directory. dirent *itsDirectoryEntry; // Boolean to check if the directory stream has past the end Bool itsEnd; // class directory Directory itsDirectory; // Regular expression if given, with this variable it is possible // to compare files with regular expression. Regex itsExpression; #if defined(AIPS_CRAY_PGI) // Cray XT3 does not support readdir on compute nodes. // Use scandir instead. dirent** itsNameList; int itsNrNames; int itsNameInx; #endif }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/OS/DynLib.cc000066400000000000000000000160661476623553700162550ustar00rootroot00000000000000//# DynLib.cc: Class to handle loading of dynamic libraries //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# For the time being assume that all systems have dlopen. #ifndef HAVE_DLOPEN # define HAVE_DLOPEN #endif //# Includes #include #include #include #include #include #include #include #include #ifdef HAVE_DLOPEN #include #endif using namespace std; namespace casacore { //# NAMESPACE CASACORE - BEGIN DynLib::DynLib (const std::string& library, const std::string& prefix, const std::string& funcName, bool closeOnDestruction) : itsHandle (0), itsDoClose (closeOnDestruction) { attach (library, prefix, std::string(), funcName); } DynLib::DynLib (const std::string& library, const std::string& prefix, const std::string& version, const std::string& funcName, bool closeOnDestruction) : itsHandle (0), itsDoClose (closeOnDestruction) { // Add a dot to the version if needed. std::string vers(version); if (! vers.empty() && vers[0] != '.') { vers = '.' + vers; } attach (library, prefix, vers, funcName); } DynLib::DynLib (const std::string& library, Bool closeOnDestruction, const std::string& prefix, const std::string& suffix) : itsHandle (0), itsDoClose (closeOnDestruction) { open (prefix + library + suffix); } DynLib::~DynLib() { if (itsDoClose) { close(); } } void* DynLib::getFunc (const std::string& funcName) { itsError.clear(); #ifdef HAVE_DLOPEN if (itsHandle ) { void* fptr = dlsym (itsHandle, funcName.c_str()); if (fptr == 0) { itsError = dlerror(); } return fptr; } #endif return 0; } void DynLib::open (const std::string& name) { #ifdef HAVE_DLOPEN itsHandle = dlopen (name.c_str(), RTLD_NOW | RTLD_GLOBAL); if (itsHandle == 0) { itsError += string(dlerror()) + '\n'; } #endif } void DynLib::close() { if (itsHandle) { #ifdef HAVE_DLOPEN dlclose (itsHandle); #endif itsHandle= 0; } } std::string DynLib::tryOpen (const std::string& library, const std::string& dir, const std::string& prefix, const std::string& version) { std::string pref(prefix); std::string vers(version); std::string fullName; // Try a maximum 4 times (1 or 2 prefix, 1 or 2 version, 1 ext). int i=0; while (i<4 && itsHandle==0) { #ifdef __APPLE__ fullName = dir + pref + library + vers + ".dylib"; #else fullName = dir + pref + library + ".so" + vers; #endif open (fullName); i++; if (i == 2) { if (pref == "lib") i += 2; // no specific prefix given pref = "lib"; } if (i%2 == 1) { vers = std::string(); if (version.empty()) i++; // no version given } else { vers = version; } } return (itsHandle==0 ? std::string() : fullName); } void DynLib::attach (const std::string& library, const std::string& prefix, const std::string& version, const std::string& funcName) { std::string fullName = tryCasacorePath (library, prefix, version); if (fullName.empty()) { fullName = tryOpen (library, string(), prefix, version); } if (itsHandle == 0) { throw AipsError ("Shared library " + library + " not found in CASACORE_LDPATH or (DY)LD_LIBRARY_PATH\n" + itsError); } LogIO os(LogOrigin("DynLib")); os << LogIO::NORMAL3 << "Loaded shared library " << fullName << LogIO::POST; if (itsHandle && !funcName.empty()) { // Found the dynamic library. // Now find and execute the given function. // Because a compiler like g++ gives a warning when casting a pointer // to a function pointer, a union is used to achieve this. // Ensure the pointer sizes are the same. typedef void (*func_ptr)(); AlwaysAssert (sizeof(func_ptr) == sizeof(void*), AipsError); typedef union { func_ptr funcPtr; void* ptr; } ptrCastUnion; ptrCastUnion ptrCast; ptrCast.ptr = getFunc (funcName.c_str()); if (! ptrCast.ptr) { close(); throw AipsError("Found dynamic library " + fullName + ", but not its " + funcName + " function\n " + itsError); } /// Note: the following is a g++ specific way to avoid the warning. ///#ifdef __GNUC__ ///__extension__ ///#endif // Execute the function. ptrCast.funcPtr(); os << LogIO::NORMAL3 << "Executed " << funcName << " in shared library " << fullName << LogIO::POST; } } std::string DynLib::tryCasacorePath (const std::string& library, const std::string& prefix, const std::string& version) { // Check if CASACORE_LDPATH is defined. String casapath("CASACORE_LDPATH"); String path = EnvironmentVariable::get(casapath); if (! path.empty()) { // Split using : as delimiter. Vector parts = strToVector (path, ':'); for (uInt j=0; j #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Class to handle loading of dynamic libraries // // // // // //
        26. Basic knowledge of the dlopen function family // // // This class makes it possible to load a dynamic library and execute an // initialization function. Furthermore, one can get a pointer to any function // in the dynamic library and close the library. // // The search path of the shared library is as follows: //
            //
          • If the environment library CASACORE_LDPATH is defined, it is tried to // find the library using that path. //
          • If not defined or not found, the system's (DY)LD_LIBRARY_PATH is used. //
          • The library looked for has the name 'prefix'libname'suffix'. //
            As prefix first "lib" is used, thereafter the given one // (e.g., "libcasa_"). //
            As suffix first ".so" is used, thereafter ".dylib" (for OS-X). //
          // // It is a wrapper around functions dlopen, dlsym, and dlclose. // If dlopen and so are not supported on a platform, the class acts as if // the shared library could not be found. //
          // // // DynLib dl("derivedmscal", "libcasa_", "register_derivedmscal"); // AlwaysAssert (dl.getHandle()); // // Using this // loads the shared library libcasa_derivedmscal.so and // executes the given register initialization function. // // // dlopen is a standard UNIX system call, but some operating systems // do not support it or have different function names (notably Windows). // In this way use of dynamic libraries is centralized and can easily b // tailored as needed. // class DynLib { public: // Load the dynamic library. It is tried with prefixes prefix // and "lib" (in that order) and with suffix ".so" or ".dylib" (for Apple). // No library version number is used. // If not loaded successfully, an exception is thrown. //
          If a non-empty funcName is given, that function is looked up and // executed for initialization purposes. Its signature must be // void func(). // Note that the function name should not be mangled, thus declared // extern "C". // An exception is thrown if the library is loaded successfully, but // funcName could not be found. //
          If closeOnDestruction=True, the dynamic library is // closed on destruction of the DynLib object. DynLib (const std::string& library, const std::string& prefix=std::string(), const std::string& funcName=std::string(), bool closeOnDestruction=True); // The same as above, but it is tried with and without the given version // (in that order). DynLib (const std::string& library, const std::string& prefix, const std::string& version, const std::string& funcName, bool closeOnDestruction=True); // Load the dynamic library with the given name, prefix, and suffix. // If not loaded successfully, the internal handle is NULL. //
          If closeOnDestruction=True, the dynamic library is closed // when the DynLib object is destructed. DynLib (const std::string& library, Bool closeOnDestruction, const std::string& prefix="lib", #ifdef __APPLE__ const std::string& suffix=".dylib"); #else const std::string& suffix=".so"); #endif // Close the dynamic library if told so in the constructor. ~DynLib(); // Get a pointer to a function in the dynamic library. // The pointer has to be casted with a reinterpret_cast to a function // pointer with the correct signature. When compiling with -pedantic the // compiler will give a warning for such a cast, because on some systems // (in particular some micro-controllers) a data pointer differs from a // function pointer. However, that problem cannot be solved. // For example: // // typedef Int MyFunc(Int, Int); // void* initfunc = DynLib::getFunc (mod, ("register_"+name).c_str()); // if (initFunc) { // MyFunc* func = reinterpret_cast(initfunc); // Int result = func(1,2); // } // // casts to a function returning Int and taking two Ints. //
          A null pointer is returned if the function could not be found. void* getFunc (const std::string& funcName); // Get the dynamic library handle. void* getHandle() const { return itsHandle; } // Get the possible error. const std::string& getError() const { return itsError; } private: // Try to open the library with some prefixes, suffixes and versions // and to execute the initialization function. // If successful, itsHandle is filled. Otherwise an exception is thrown. void attach (const std::string& name, const std::string& prefix, const std::string& version, const std::string& funcName); // Try to open the library with some prefixes, suffixes and versions // If successful, itsHandle is filled and the full library name is // returned. Otherwise an empty name is returned. std::string tryOpen (const std::string& name, const std::string& libdir, const std::string& prefix, const std::string& version); // Open (load) the dynamic library. void open (const std::string& name); // Close (unload) the dynamic library (if opened). void close(); // Try if the library can be opened using CASACORE_LDPATH. std::string tryCasacorePath (const std::string& library, const std::string& prefix, const std::string& version); //# Handle to dynamic library; note that the pointer is not owned, so the //# generated copy ctor and assignment are fine. void* itsHandle; Bool itsDoClose; std::string itsError; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/OS/EnvVar.cc000066400000000000000000000036241476623553700162710ustar00rootroot00000000000000//# EnvVar.cc: Environment variables class //# Copyright (C) 1993,1994,1995,1998,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include //# for strcpy with gcc-4.3 namespace casacore { //# NAMESPACE CASACORE - BEGIN Bool EnvironmentVariable::isDefined (const String& name) { return getenv (name.chars()); } String EnvironmentVariable::get (const String& name) { Char* env = getenv (name.chars()); if (env) return String(env); return String(); } void EnvironmentVariable::set (const String& name, const String& value) { AlwaysAssert (setenv(name.chars(), value.chars(), 1) == 0, AipsError); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/OS/EnvVar.h000066400000000000000000000051631476623553700161330ustar00rootroot00000000000000//# EnvVar.h: Environment variables class //# Copyright (C) 1993,1994,1995,1999,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_ENVVAR_H #define CASA_ENVVAR_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // This class allows for getting enviroment variables // // // // // Environment variables are familiar to every Unix, MSDOS and VMS // computer user. This class makes it convenient to get and // enquire about environment variables from within a C++ program. // // // Check if an environment variable is defined. // If so, get its value. // // if (EnvironmentVariable::isDefined ("PATH")) { // cout << EnvironmentVariable::get ("PATH") << endl; // } // // // // class EnvironmentVariable { public: // Is environment variable with given name defined? static Bool isDefined (const String& name); // Get the value of environment variable with given name. // If not defined, return an empty String. static String get (const String& name); // Define environment variable. // If it already exists, its value will be overwritten. static void set (const String& name, const String& value); private: // This class is not meant to be constructed. EnvironmentVariable(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/OS/File.cc000066400000000000000000000271111476623553700157440ustar00rootroot00000000000000//# File.cc: Class to define a File //# Copyright (C) 1993,1994,1995,1996,1997,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include // needed for access, etc. #include // needed for lstat or lstat64 #include // needed for utimbuf #include // needed for errno #include // needed for strerror #include // needed for snprintf #include // needed for asctime/localtime on linux namespace casacore { //# NAMESPACE CASACORE - BEGIN std::atomic File::uniqueSeqnr_p(0); File::File () { // Sets itsPath on the current working directory } File::File (const Path& path) : itsPath (path) {} File::File (const String& string) : itsPath (string) {} File::File (const File& that) : itsPath (that.itsPath) {} File::~File() {} File& File::operator= (const File& that) { if (this != &that) { itsPath = that.itsPath; } return *this; } Bool File::isRegular (Bool followSymLink) const { // The struct is filled in by mylstat, and S_ISREG checks buf // if the file is a regularfile. Path testPath = itsPath; if (isSymLink()) { if (! followSymLink) { return False; } testPath = SymLink(itsPath).followSymLink(); } struct fileSTAT buf; if (mylstat (testPath.expandedName().chars(), &buf) < 0) { return False; } return (S_ISREG (buf.st_mode)); } Bool File::isDirectory (Bool followSymLink) const { // The struct is filled in by mylstat, and S_ISDIR checks buf // if the file is a directory. Path testPath = itsPath; if (isSymLink()) { if (! followSymLink) { return False; } testPath = SymLink(itsPath).followSymLink(); } struct fileSTAT buf; if (mylstat (testPath.expandedName().chars(), &buf) < 0) { return False; } return (S_ISDIR (buf.st_mode)); } Bool File::isSymLink() const { // The struct is filled in by mylstat, and S_ISLNK checks buf // if the file is a symbolic link. struct fileSTAT buf; if (mylstat (itsPath.expandedName().chars(), &buf) < 0) { return False; } return (S_ISLNK (buf.st_mode)); } Bool File::isPipe() const { // The struct is filled in by mylstat, and S_ISFIFO checks buf // if the file is a pipe. struct fileSTAT buf; getstat (&buf); return (S_ISFIFO (buf.st_mode)); } Bool File::isCharacterSpecial() const { // The struct is filled in by mylstat, and S_ISCHR checks buf // if the file is a characterspecialfile. struct fileSTAT buf; getstat (&buf); return (S_ISCHR (buf.st_mode)); } Bool File::isBlockSpecial() const { // The struct is filled in by mylstat, and S_ISBLK checks buf // if the file is a blokspecialfile. struct fileSTAT buf; getstat (&buf); return (S_ISBLK (buf.st_mode)); } Bool File::isSocket() const { // The struct is filled in by mylstat, and S_ISSOCK checks buf // if the file is a socket. struct fileSTAT buf; getstat (&buf); return (S_ISSOCK (buf.st_mode)); } Bool File::exists() const { // The function access always substitutes symlinks. // Therefore use lstat instead. struct fileSTAT buf; int status = mylstat((itsPath.expandedName()).chars(), &buf); if (status != 0 && errno != ENOENT){ LogIO logIo (LogOrigin ("File", "exists")); logIo << LogIO::WARN; logIo << "lstat failed for " << itsPath.expandedName() << ": errno=" << errno << "'" << strerror (errno) << "'\n"; logIo << LogIO::POST; } return status == 0; } Bool File::isReadable() const { // The function access checks if the file is readable. return (access ((itsPath.expandedName()).chars(), R_OK)==0); } Bool File::isWritable() const { // The function access checks if the file is writable. return (access ((itsPath.expandedName()).chars(), W_OK)==0); } Bool File::isExecutable() const { // The function access checks if the file is executable. return (access ((itsPath.expandedName()).chars(), X_OK)==0); } Bool File::canCreate() const { // Checks if the dirname of a file is a writable and executable // directory. File dir(itsPath.dirName()); if (dir.isDirectory() && dir.isWritable() && dir.isExecutable()) { return True; } return False; } long File::userID() const { // Returns the userid of a file which is extracted from the struct // buf. struct fileSTAT buf; getstat (&buf); return buf.st_uid; } long File::groupID() const { // Returns the groupid of a file which is extracted from the struct // buf. struct fileSTAT buf; getstat (&buf); return buf.st_gid; } Int64 File::size() const { // The struct buf is filled in by mylstat, and the size // of the file is extracted from buf. struct fileSTAT buf; getstat (&buf); return buf.st_size; } uInt File::readPermissions() const { // Returns the permissions as a decimal value. The value // is extracted from buf. struct fileSTAT buf; getstat (&buf); return (uInt (buf.st_mode & 07) + (uInt (buf.st_mode &070) >> 3) * 10 + (uInt (buf.st_mode &0700) >> 6) * 100 ); } void File::setPermissions(uInt permissions) { // Changes the permissions by using chmod, the value must be // an octal value. chmod ((itsPath.expandedName()).chars(),long (permissions)); } Path File::newUniqueName (const String& directory, const String& prefix) { // create an new unique name char str[32]; // fill str with the pid and the unique number uInt seqnr = uniqueSeqnr_p.fetch_add(1); snprintf (str, sizeof(str), "%i_%i", Int(getpid()), seqnr); if (directory.empty() || directory.lastchar() == '/') { return Path (directory + prefix + str); } // a slash is added when a directory is given and the last // character is a slash. return Path (directory + "/" + prefix + str); } Path File::newUniqueName (const String& directory) { // Uses the function newUniqueName with an empty prefix return newUniqueName (directory, ""); } void File::touch(uInt time) { // Uses the function utime to set the access time and the // modification time. utimbuf times; times.actime = time; times.modtime = time; if (isWritable()) { utime ((itsPath.expandedName()).chars(), ×); } } void File::touch() { // Uses the function utime to set the access time and the // modification time on the current time. if (isWritable()) { utime ((itsPath.expandedName()).chars(), 0); } } uInt File::accessTime () const { // The struct is filled in by mylstat, and the accesstime // is returned. struct fileSTAT buf; getstat (&buf); return buf.st_atime; } String File::accessTimeString () const { // The struct is filled in by mylstat, and the accesstime // is returned as a string. struct fileSTAT buf; getstat (&buf); return String (asctime (localtime (&buf.st_atime))); } uInt File::modifyTime () const { // The struct is filled in by mylstat, and the modificationtime // is returned. struct fileSTAT buf; getstat (&buf); return buf.st_mtime; } String File::modifyTimeString () const { // The struct is filled in by mylstat, and the modificationtime // is returned as a string. struct fileSTAT buf; getstat (&buf); return String (asctime (localtime (&buf.st_mtime))); } File::FileWriteStatus File::getWriteStatus() const { if (exists()) { return (isWritable() ? OVERWRITABLE : NOT_OVERWRITABLE); } return (canCreate() ? CREATABLE : NOT_CREATABLE); } uInt File::statusChangeTime () const { // The struct is filled in by mylstat, and the statusChangetime // is returned. struct fileSTAT buf; getstat (&buf); return buf.st_ctime; } String File::statusChangeTimeString() const { // The struct is filled in by mylstat, and the statusChangetime // is return from buf as a string. struct fileSTAT buf; getstat (&buf); return String (asctime (localtime (&buf.st_ctime))); } void File::removeSymLinks () { // Remove the chain of symlinks starting with this one. File next(*this); while (next.isSymLink()) { SymLink symLink(next); next = symLink.readSymLink(); symLink.remove(); } } int File::mylstat(const char* path, void* buf) const { return fileLSTAT (const_cast(path), static_cast(buf)); } void File::getstat (const File& file, void* buf) const { if (mylstat (file.path().expandedName().chars(), buf) < 0) { throw (AipsError ("File::getstat error on " + file.path().expandedName() + ": " + strerror(errno))); } } void File::checkTarget (Path& targetName, Bool overwrite, Bool forDirectory) const { // Determine the target directory and file. // When the target is a directory, the basename is copied from the source. // Otherwise the target directory is the dirname of the target. Path targetDir; File target(targetName); if (!forDirectory && target.isDirectory()) { targetDir = targetName; targetName.append (path().baseName()); } else { targetDir = targetName.dirName(); } // Check if the target directory is writable. target = targetDir; if (! target.isWritable()) { throw (AipsError ("RegularFile::copy/move: target directory " + targetDir.originalName() + " is not writable")); } // Check if the target file exists, is writable and should be overwritten. target = targetName; if (target.exists()) { if (! overwrite) { throw (AipsError ("RegularFile::copy/move: target file " + targetName.originalName() + " already exists")); } if (! target.isWritable()) { throw (AipsError ("RegularFile::copy/move: target file " + targetName.originalName() + " already exists and is not writable")); } } } #ifdef AIPS_DARWIN #include #include #else #include #endif String File::getFSType() const { String rstat("Normal"); struct fileSTATFS statbuf; fileSTATFS(itsPath.dirName().chars(), &statbuf); #ifdef AIPS_DARWIN rstat = String(statbuf.f_fstypename); #else if(statbuf.f_type == 0x0BD00BD0) rstat = "Lustre"; #endif return rstat; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/OS/File.h000066400000000000000000000263111476623553700156070ustar00rootroot00000000000000//# File.h: Class to get file information and a base for other file classes //# Copyright (C) 1993,1994,1995,1996,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_FILE_H #define CASA_FILE_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Class to get file information and a base for other file classes. // // // // // //
        27. Basic knowledge of the UNIX file system //
        28. Path // // // 'File' is used in a traditional sense. // // // The File class provides the primary functions needed by all kinds of // files (directories, regular files, symbolic links, named pipes etc.). // These shared functions serve mostly to return information about a // particular file -- for instance, its type, its ownership, read, write // and execute permissions, date of latest access and the path on secundary // storage associated with this file. Every file object has, by definition, // a Path object associated with it which // defines the file name. //

          // See also the derived classes // RegularFile, // Directory, and // SymLink. //
          // This class does not contain virtual functions, because a lot of functions // have different parameters, e.g. 'create' for RegularFile has one parameter // and 'create' for SymLink has two parameters. // // It handles large files correctly. // // // // File myFile("someFileName"); // if (myFile.exists()) { // myFile.setPermissions(0644); // if (myFile.isRegular()) { // cout << "this file is a regular file" << endl; // } // } // else if (!myFile.exists()) { // if (!myFile.canCreate()){ // cout << "cannot create this file" << endl; // } // } // // // // File systems operations are a notorious source of porting problems. // The file class provides a standard interface for programmers to use. // class File { public: enum FileWriteStatus { // file exists and can be overwritten OVERWRITABLE, // file exists but cannot be overwritten NOT_OVERWRITABLE, // file does not exist and is creatable CREATABLE, // file does not exist but cannot be created NOT_CREATABLE }; // Construct a File object whose Path is set to the current working // directory. File(); // Construct a File object whose Path is set to the given Path. // File (const Path& path); File (const String& path); // // Copy constructor (copy semantics). File (const File& that); virtual ~File(); // Assignment (copy semantics). File& operator= (const File& that); // Returns the pathname of the file. const Path& path() const; // Check if the file is a regular file. If the boolean followSymLink is // False a symbolic link will not be followed. Bool isRegular (Bool followSymLink = True) const; // Check if the file is a directory. If the boolean followSymLink is // False a symbolic link will not be followed. Bool isDirectory (Bool followSymLink = True) const; // Check if the file is a symbolic link. Bool isSymLink() const; // Check if the file is a pipe. Bool isPipe() const; // Check if the file is a character special file. Bool isCharacterSpecial() const; // Check if the file is a block special file. Bool isBlockSpecial() const; // Check if the file is a socket. Bool isSocket() const; // Check if the file exists. Bool exists() const; // Check if the file is readable. Bool isReadable() const; // Check if the file is writable. Bool isWritable() const; // Check if the file is executable. Bool isExecutable() const; // Check if a file can be created. Bool canCreate() const; // Return the userID of the file. long userID() const; // Return the groupID of the file. long groupID() const; // Return the size of the file. If the file // does not exist, an exception will be thrown. virtual Int64 size() const; // Return the permissions as a decimal value. uInt readPermissions() const; // Set permission with perm. Perm is an octal value. void setPermissions (uInt permissions); // Update access time and modification time of a file. void touch (uInt time); // Update access time and modification time of a file. This function // updates the file with the current time. void touch(); // Time related fucnctions: // Return the time when the file was last accessed in seconds since // 00:00:00 GMT Jan 1, 1970. uInt accessTime() const; // Return the time when the file was last accessed // as a 26-characters String of the form: // Thu Feb 3 13:40:11 1994 String accessTimeString() const; // Return the time when the file was last modified in seconds since // 00:00:00 GMT Jan 1, 1970. uInt modifyTime() const; // Return the time when the file was last modified // as a 26-characters String of the form: // Thu Feb 3 13:40:11 1994 String modifyTimeString() const; // Return the time when the file status was last changed in seconds since // 00:00:00 GMT Jan 1, 1970. // It is set both by writing and changing the file status information, // such as changes of owner, group, link count, or mode. uInt statusChangeTime() const; // return the time when the file status was last changed // as a 26-characters String of the form: // Thu Feb 3 13:40:11 1994 String statusChangeTimeString() const; // Create a new unique path name in the specified directory, with // the specified prefix and random trailing characters: // // p.newUniqueName ("./", "temp") --> "./tempAAA00xx32" // p.newUniqueName ("/home/me", "diary") --> "/home/me/diaryAAA00xxb0" // static Path newUniqueName (const String& directory, const String& prefix); // Create a new unique filename without a prefix. // As above, but all the characters in the filename are random: // // p.newUniqueName ("./") --> "./AAA00xx32" // p.newUniqueName ("/home/me") --> "/home/me/AAA00xxb0" // static Path newUniqueName (const String& directory); // get write status of the file. // OVERWRITABLE - file exists and can be overwritten // NOT_OVERWRITABLE - file exists but cannot be overwritten // CREATABLE - File does not exist and can be created // NOT_CREATABLE - file does not exist and cannot be created. FileWriteStatus getWriteStatus() const; // Return the filesystem type. // If the file doesn't exsist crawl up the directory tree to // find one that does. String getFSType() const; protected: // This function is used by RegularFile // and Directory to remove all the links // which, when followed, ultimately resolve to a Directory or a // RegularFile. // For example, A->B, B->C, C->D and D points to a regular file. // When remove() is called for a regular file A, // that function uses removeLinks() to remove A, B, C and D. void removeSymLinks(); // Check if the new path for a copy or move is valid. // An exception is thrown if: //
          - the target directory is not writable //
          - or the target file already exists and overwrite==False //
          - or the target file already exists and is not writable //
          When the targetName represents a directory, the basename // of the file is appended to it. This is done to cover the // case where the source is a symlink to a file. In that case // the target will get the basename of the symlink and not the // the basename of the file pointed to. This is not done when // forDirectory==True (which is used by class Directory). void checkTarget (Path& targetName, Bool overwrite, Bool forDirectory = False) const; private: // Define a function for lstat. // This is necessary since SunOS4.1.x prototypes lstat() with a first // argument of type (char*), while Solaris (and presumably all other // reasonable OS's) prototype it with a first argument of type // (const char*). Since lstat() does not change its first argument, // it is safe to convert our const variable to a non-const one so that // we can call lstat() successfully. //
          It is also useful to be able to pass the buffer as void*. In that // way the 32-bit or 64-bit file details are only needed in the cc file. int mylstat (const char* path, void* buf) const; // Get the lstat of this file. // Throw an exception when it fails. void getstat (void* buf) const; // Get the lstat of a file. // Throw an exception when it fails. void getstat (const File& file, void* buf) const; // Full pathname of the file. Path itsPath; // A sequence number to generate unique file names. static std::atomic uniqueSeqnr_p; }; inline const Path& File::path() const { return itsPath; } inline void File::getstat (void* buf) const { getstat (*this, buf); } //# The ifdef's below are similar to those in IO/LargeIOFuncDef.h. #if !defined(AIPS_NOLARGEFILE) # ifdef AIPS_LINUX # if !defined(_LARGEFILE64_SOURCE) # define _LARGEFILE64_SOURCE # endif # endif #if defined(AIPS_DARWIN) || defined(AIPS_BSD) # define fileFSTAT fstat # define fileLSTAT lstat # define fileSTAT stat # define fileSTATFS statfs #else # define fileFSTAT fstat64 # define fileLSTAT lstat64 # define fileSTAT stat64 # define fileSTATFS statfs64 #endif #else # define fileFSTAT fstat # define fileLSTAT lstat # define fileSTAT stat # define fileSTATFS statfs #endif } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/OS/HostInfo.cc000066400000000000000000000167771476623553700166360ustar00rootroot00000000000000//# HostInfo.h: Information about the host that this process is running on. //# Copyright (C) 1997-2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #define _XOPEN_SOURCE 600 //For clock_gettime #include #include #include #include #include #include // Time related includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN String HostInfo::hostName() { String retval; #if defined(AIPS_IRIX) // This is a kludge to get around a problem with // losing environment variable names on some IRIX machines // at NCSA in Urbana IL. Char buf[65]; if (gethostname(buf, 64) >= 0) { retval = String(buf); } #else struct utsname name; if (uname(&name) >= 0) { retval = name.nodename; } #endif return retval; } Int HostInfo::processID() { return getpid(); } Double HostInfo::secondsFrom1970() { struct timespec tp; AlwaysAssert(clock_gettime(CLOCK_REALTIME, &tp) == 0, AipsError); double total = tp.tv_sec; total += tp.tv_nsec * 1.e-9; return total; } #define HOSTINFO_IMPLEMENT_MEMBERS \ Int HostInfo::numCPUs(bool use_aipsrc) \ { \ static const String keyword("system.resources.cores"); \ if ( use_aipsrc ) { \ String value; \ if (resources_numCPUs > 0) { \ return resources_numCPUs; \ } else if ( Aipsrc::find(value, keyword) ) { \ int result; \ if ( sscanf( value.c_str( ), "%d", &result ) == 1 ) \ return (Int) result; \ } \ } \ \ if ( ! info ) info = new HostMachineInfo( ); \ return info->valid ? info->cpus : 0; \ } \ \ ptrdiff_t HostInfo::memoryTotal(bool use_aipsrc) \ { \ static const String memory("system.resources.memory"); \ static const String fraction("system.resources.memfrac"); \ int frac = 0; \ /** aipsrc memory is in megabytes whereas this **/ \ /** returns the memory in kilobytes... **/ \ if ( use_aipsrc ) { \ String value; \ if (resources_memory > 0) { \ return resources_memory; \ } else if ( Aipsrc::find(value, memory) ) { \ int result; \ if ( sscanf( value.c_str( ), "%d", &result ) == 1 ) \ return (ptrdiff_t) result * 1024; \ } else if (resources_memfrac > 0) { \ frac = resources_memfrac; \ } else if ( Aipsrc::find(value, fraction) ) { \ int result; \ if ( sscanf( value.c_str( ), "%d", &result ) == 1 ) \ frac = result; \ } \ } \ \ if ( ! info ) info = new HostMachineInfo( ); \ \ if ( ! info->valid ) \ return -1; \ else if ( frac == 0 ) \ return info->memory_total; \ else { \ double f = ((double) frac / 100.0); \ return (ptrdiff_t) ((double ) info->memory_total * f); \ } \ } \ \ ptrdiff_t HostInfo::memoryUsed( ) \ { \ if ( ! info ) info = new HostMachineInfo( ); \ info->update_info( ); \ return info->valid ? info->memory_used : -1; \ } \ \ ptrdiff_t HostInfo::memoryFree( ) \ { \ if ( ! info ) info = new HostMachineInfo( ); \ info->update_info( ); \ return info->valid ? info->memory_free : -1; \ } \ \ ptrdiff_t HostInfo::swapTotal( ) \ { \ if ( ! info ) info = new HostMachineInfo( ); \ info->update_info( ); \ return info->valid ? info->swap_total : -1; \ } \ \ ptrdiff_t HostInfo::swapUsed( ) \ { \ if ( ! info ) info = new HostMachineInfo( ); \ info->update_info( ); \ return info->valid ? info->swap_used : -1; \ } \ \ ptrdiff_t HostInfo::swapFree( ) \ { \ if ( ! info ) info = new HostMachineInfo( ); \ info->update_info( ); \ return info->valid ? info->swap_free : -1; \ } \ \ ptrdiff_t HostInfo::setMemoryTotal(ptrdiff_t memory) \ { \ ptrdiff_t old_memory = resources_memory; \ resources_memory = 1024*memory; \ return old_memory; \ } \ \ Int HostInfo::setMemoryFraction(Int memfrac) \ { \ Int old_memfrac = resources_memfrac; \ resources_memfrac = memfrac; \ return old_memfrac; \ } \ Int HostInfo::setNumCPUs(Int numCPUs) \ { \ Int old_numCPUs = resources_numCPUs; \ resources_numCPUs = numCPUs; \ return old_numCPUs; \ } } //# NAMESPACE CASACORE - END #define HOSTINFO_DO_IMPLEMENT #if defined(AIPS_LINUX) #include namespace casacore { //# NAMESPACE CASACORE - BEGIN HOSTINFO_IMPLEMENT_MEMBERS } //# NAMESPACE CASACORE - END #elif defined(AIPS_SOLARIS) #include namespace casacore { //# NAMESPACE CASACORE - BEGIN HOSTINFO_IMPLEMENT_MEMBERS } //# NAMESPACE CASACORE - END #elif defined(AIPS_IRIX) #include HOSTINFO_IMPLEMENT_MEMBERS } //# NAMESPACE CASACORE - END #elif defined(AIPS_OSF) #include namespace casacore { //# NAMESPACE CASACORE - BEGIN HOSTINFO_IMPLEMENT_MEMBERS } //# NAMESPACE CASACORE - END #elif defined(AIPS_HPUX) #include namespace casacore { //# NAMESPACE CASACORE - BEGIN HOSTINFO_IMPLEMENT_MEMBERS } //# NAMESPACE CASACORE - END #elif defined(__APPLE__) #include namespace casacore { //# NAMESPACE CASACORE - BEGIN HOSTINFO_IMPLEMENT_MEMBERS } //# NAMESPACE CASACORE - END #elif defined(AIPS_BSD) #include namespace casacore { //# NAMESPACE CASACORE - BEGIN HOSTINFO_IMPLEMENT_MEMBERS } //# NAMESPACE CASACORE - END #else namespace casacore { //# NAMESPACE CASACORE - BEGIN Int HostInfo::numCPUs(bool) { return 0; } ptrdiff_t HostInfo::memoryTotal(bool) { return -1; } ptrdiff_t HostInfo::memoryUsed( ) { return -1; } ptrdiff_t HostInfo::memoryFree( ) { return -1; } ptrdiff_t HostInfo::swapTotal( ) { return -1; } ptrdiff_t HostInfo::swapUsed( ) { return -1; } ptrdiff_t HostInfo::swapFree( ) { return -1; } } //# NAMESPACE CASACORE - END #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN HostMachineInfo *HostInfo::info = 0; ptrdiff_t HostInfo::resources_memory = 0; Int HostInfo::resources_memfrac = 0; Int HostInfo::resources_numCPUs = 0; } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/OS/HostInfo.h000066400000000000000000000130561476623553700164630ustar00rootroot00000000000000//# HostInfo.h: Miscellaneous information about this host and process. //# Copyright (C) 1997,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_HOSTINFO_H #define CASA_HOSTINFO_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class String; class HostMachineInfo; //

          // Miscellaneous information about this host and process. // // // // // //
        29. None // // // // This class is meant to encapsulate miscellaneous information about the // environment that the current process is running in. // // At present, the class can be used to obtain the name of the host on which // this process is running, the process id of this process, the amount of // physical memory (total, used, & free) on this host, the amount of swap // space (total, used, and free), the number of CPUs on this host, and a // count of the number of seconds from January 1, 1970. Generally you // should use the classes Time and // MVTime if your primary interest is related // to time and dates. If your interest is in timing, you would generally use // class Timer, which can also report CPU times // as well as "clock" times. // // Determination of the number of CPUs, swap space, and physical memory is // particularly OS dependent. In cases where this functionality has not yet // been implemented, the memory and swap functions will return -1 and the // CPU function will return 0. // // // // // cout << "I am process #" << HostInfo::processID() << " running on host " << // HostInfo::hostName() << ", which has " << HostInfor::numCPUs( ) << " CPUs." << endl; // cout << "This host has " << HostInfo::memoryTotal( ) << "K of memory [ " << // HostInfo::memoryUsed( ) << " used, " << // HostInfo::memoryFree( ) << " free ]." << endl; // cout << "This host has " << HostInfo::swapTotal( ) << "K of swap space [ " << // HostInfo::swapUsed( ) << " used, " << // HostInfo::swapFree( ) << " free ]." << endl; // Double now = HostInfo::secondsFrom1970(); // doSomething(); // cout << "Function doSomething() took " << // HostInfo::secondsFrom1970() - now << " seconds to execute." << endl; // // // // // The ObjectID class uses a combination of // hostname, process id, time, and a sequence number to ensure that ObjectID's // are distinct. This class encapsulates the way the first three of those items // are obtained. // // // //
        30. OS version? // class HostInfo { public: static String hostName(); static Int processID(); static Double secondsFrom1970(); // Returns True for big endian machines (like SUN). // Returns False for little endian machines (like PC). static Bool bigEndian(); // Returns 0 if unable to determine the number of CPUs. static Int numCPUs(bool use_aipsrc=false); // Get memory info (in KBytes). // Returns -1 if unable to determine memory info. // static ptrdiff_t memoryTotal(bool use_aipsrc=false); static ptrdiff_t memoryUsed(); static ptrdiff_t memoryFree(); // // Get swap space info (in KBytes). // Returns -1 if unable to determine swap info. // static ptrdiff_t swapTotal(); static ptrdiff_t swapUsed(); static ptrdiff_t swapFree(); // // Allows to set custom resource values overriding the // existing .apisrc setting and/or system defaults // Expected unit are KiB for memory total // and percentage (%) for memory fraction // Returns the value previously stored // static ptrdiff_t setMemoryTotal(ptrdiff_t memory); static Int setMemoryFraction(Int memfrac); static Int setNumCPUs(Int numCPUs); // private: // we don't want folks creating these... HostInfo( ); const HostInfo &operator=( const HostInfo & ); static HostMachineInfo *info; static ptrdiff_t resources_memory; static Int resources_memfrac; static Int resources_numCPUs; }; inline Bool HostInfo::bigEndian() { #if defined(AIPS_LITTLE_ENDIAN) return False; #else return True; #endif } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/OS/HostInfoBsd.h000066400000000000000000000071321476623553700171120ustar00rootroot00000000000000//# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA /* ** Author Tony Maher ** ** Based on HostInfoDarwin.h (just the scaffolding code). */ #ifndef CASA_HOSTINFOBSD_H #define CASA_HOSTINFOBSD_H # if defined(HOSTINFO_DO_IMPLEMENT) #include #include #include // struct vmtotal #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // HostInfo for BSD (FreeBSD) machines. // // // // // //
        31. HostInfo // // // This file provides the BSD (FreeBSD) specific functions for HostInfo. // It is selectively included by HostInfo.cc. // // // // these are for getting the memory statistics static int pagesize; static int page_kb; class HostMachineInfo { friend class HostInfo; HostMachineInfo( ); void update_info( ); int valid; int cpus; ptrdiff_t memory_total; ptrdiff_t memory_used; ptrdiff_t memory_free; ptrdiff_t swap_total; ptrdiff_t swap_used; ptrdiff_t swap_free; }; // HostMachineInfo::HostMachineInfo( ) : valid(1) { size_t len; pagesize = getpagesize(); // size of page in bytes. page_kb = pagesize / 1024; // in kilobytes. len = sizeof(cpus); if (sysctlbyname("hw.ncpu", &cpus, &len, NULL, 0) == -1) perror("sysctl"); len = sizeof(memory_total); if (sysctlbyname("hw.physmem", &memory_total, &len, NULL, 0) == -1) perror("sysctl"); else memory_total /= 1024; // in kilobytes } void HostMachineInfo::update_info( ) { size_t len; kvm_t *kd; struct vmtotal total; struct kvm_swap swapary[1]; // Memory and swap values are in pages. len = sizeof(total); if (sysctlbyname("vm.vmtotal", &total, &len, NULL, 0) == -1) perror("sysctl"); else memory_used = total.t_rm * page_kb; memory_free = total.t_free * page_kb; kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open"); if (kd != NULL) { kvm_getswapinfo(kd, swapary, 1, 0); swap_total = swapary[0].ksw_total * page_kb; swap_used = swapary[0].ksw_used * page_kb; swap_free = swap_total - swap_used; } } } //# NAMESPACE CASACORE - END # endif #endif casacore-3.7.1/casa/OS/HostInfoDarwin.h000066400000000000000000000124221476623553700176240ustar00rootroot00000000000000/* ** This is a greatly MODIFIED version of a "top" machine dependent file. ** The only resemblance it bears to the original is with respect to the ** mechanics of finding various system details. The copyright details ** follow. ** ** This is a modified version of the osf1 version. Initially, I tried ** to use the m_macosx.c version by Andrew S. Townley, but ran into ** problems... ** ** --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ** ** Top users/processes display for Unix ** Version 3 ** ** This program may be freely redistributed, ** but this entire comment MUST remain intact. ** ** Copyright (c) 1984, 1989, William LeFebvre, Rice University ** Copyright (c) 1989 - 1994, William LeFebvre, Northwestern University ** Copyright (c) 1994, 1995, William LeFebvre, Argonne National Laboratory ** Copyright (c) 1996, William LeFebvre, Group sys Consulting ** Copyright (c) 2002, Associated Universities Inc. */ /* ** LIBS: -lstdc++ ** ** AUTHOR: Darrell Schiebel ** ** ORIGINAL AUTHOR: Anthony Baxter ** ORIGINAL CONTRIBUTORS: David S. Comay ** Claus Kalle ** Pat Welch ** William LeFebvre ** Rainer Orth ** */ #ifndef CASA_HOSTINFODARWIN_H #define CASA_HOSTINFODARWIN_H # if defined(HOSTINFO_DO_IMPLEMENT) #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // HostInfo for Darwin machines. // // // // // //
        32. HostInfo // // // This file provides the Linux specific functions for HostInfo. // It is selectively included by HostInfo.cc. // // // /* Log base 2 of 1024 is 10 (2^10 == 1024) */ #define LOG1024 10 /* these are for getting the memory statistics */ static int pageshift; /* log base 2 of the pagesize */ static int pagesize_; /* define pagetok in terms of pageshift */ #define pagetok(size) ((size) << pageshift) class HostMachineInfo { friend class HostInfo; HostMachineInfo( ); void update_info( ); int valid; int cpus; ptrdiff_t memory_total; ptrdiff_t memory_used; ptrdiff_t memory_free; ptrdiff_t swap_total; ptrdiff_t swap_used; ptrdiff_t swap_free; }; // HostMachineInfo::HostMachineInfo( ) : valid(1) { int pagesize; kern_return_t ret; struct host_basic_info basic_info; unsigned int count = HOST_BASIC_INFO_COUNT; /* get the page size with portable sysconf and calculate pageshift from it */ pagesize_ = pagesize = sysconf(_SC_PAGESIZE); pageshift = 0; while (pagesize > 1) { pageshift++; pagesize >>= 1; } /* we only need the amount of log(2)1024 for our conversion */ pageshift -= LOG1024; #ifdef AIPS_64B ret = host_info( mach_host_self(), HOST_BASIC_INFO, (host_info64_t) &basic_info, &count ); #else ret = host_info( mach_host_self(), HOST_BASIC_INFO, (host_info_t) &basic_info, &count ); #endif if ( ret != KERN_SUCCESS ) { valid = 0; } else { #ifdef AIPS_64B memory_total = basic_info.max_mem / 1024; #else memory_total = basic_info.memory_size / 1024; #endif cpus = basic_info.avail_cpus; } } void HostMachineInfo::update_info( ) { #ifdef AIPS_64B struct vm_statistics64 vmstats; #else struct vm_statistics vmstats; #endif kern_return_t kr; unsigned int count; /* memory information */ /* this is possibly bogus - we work out total # pages by */ /* adding up the free, active, inactive, wired down, and */ /* zero filled. Anyone who knows a better way, TELL ME! */ /* Change: dont use zero filled. */ count = sizeof(vmstats)/sizeof(integer_t); #ifdef AIPS_64B kr = host_statistics64( mach_host_self(), HOST_VM_INFO64, (host_info64_t) &vmstats, &count ); #else kr = host_statistics( mach_host_self(), HOST_VM_INFO, (host_info_t) &vmstats, &count ); #endif if ( kr != KERN_SUCCESS ) { valid = 0; return; } // /* thanks DEC for the table() command. No thanks at all for */ // /* omitting the man page for it from OSF/1 1.2, and failing */ // /* to document SWAPINFO in the 1.3 man page. Lets hear it for */ // /* include files. */ // i=0; // while(table(TBL_SWAPINFO,i,&swbuf,1,sizeof(struct tbl_swapinfo))>0) { // swappages += swbuf.size; // swapfree += swbuf.free; // i++; // } // // swap_used = pagetok(swappages - swapfree); // swap_free = pagetok(swapfree); // swap_total = pagetok(swappages); memory_used = pagetok(vmstats.active_count + vmstats.wire_count); memory_free = memory_total - memory_used; swap_used = pagetok( vmstats.active_count + vmstats.inactive_count + vmstats.wire_count ); swap_free = pagetok( vmstats.free_count ); swap_total = pagetok( vmstats.active_count + vmstats.inactive_count + vmstats.wire_count + vmstats.free_count ); } } //# NAMESPACE CASACORE - END # endif #endif casacore-3.7.1/casa/OS/HostInfoHpux.h000066400000000000000000000066141476623553700173320ustar00rootroot00000000000000//# HostInfo_hpux.h: HP/UX specific memory, swap, and CPU code. /* ** This is a greatly MODIFIED version of a "top" machine dependent file. ** The only resemblance it bears to the original is with respect to the ** mechanics of finding various system details. The copyright details ** follow. ** ** --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ** ** Top users/processes display for Unix ** Version 3 ** ** This program may be freely redistributed, ** but this entire comment MUST remain intact. ** ** Copyright (c) 1984, 1989, William LeFebvre, Rice University ** Copyright (c) 1989 - 1994, William LeFebvre, Northwestern University ** Copyright (c) 1994, 1995, William LeFebvre, Argonne National Laboratory ** Copyright (c) 1996, William LeFebvre, Group sys Consulting ** Copyright (c) 2002, Associated Universities Inc. */ #ifndef CASA_HOSTINFOHPUX_H #define CASA_HOSTINFOHPUX_H # if defined(HOSTINFO_DO_IMPLEMENT) /* * AUTHOR: Darrell Schiebel * * ORIGINAL AUTHOR: John Haxby * ORIGINAL CONTRIBUTORS: Rich Holland * Kevin Schmidt */ #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // HostInfo for HP-UX machines. // // // // // //
        33. HostInfo // // // This file provides the HP-UX specific functions for HostInfo. // It is selectively included by HostInfo.cc. // // // /* these are for getting the memory statistics */ /* Log base 2 of 1024 is 10 (2^10 == 1024) */ #define LOG1024 10 /* define pagetok in terms of pageshift */ #define pagetok(size) ((size) << pageshift) class HostMachineInfo { friend class HostInfo; HostMachineInfo( ); void update_info( ); int valid; int cpus; ptrdiff_t swap_total; ptrdiff_t swap_used; ptrdiff_t swap_free; ptrdiff_t memory_total; ptrdiff_t memory_used; ptrdiff_t memory_free; int pageshift; /* log base 2 of the pagesize */ }; // HostMachineInfo::HostMachineInfo( ) :valid(1) { struct pst_static info; int pagesize; if (pstat_getstatic (&info, sizeof (info), 1, 0) < 0) { perror ("pstat_getstatic"); valid = 0; } /* * Calculate pageshift -- the value needed to convert pages to Kbytes. * This will usually be 2. */ pageshift = 0; for (pagesize = info.page_size; pagesize > 1; pagesize >>= 1) pageshift += 1; pageshift -= LOG1024; static struct pst_dynamic dynamic; pstat_getdynamic (&dynamic, sizeof (dynamic), 1, 0); cpus = dynamic.psd_proc_cnt; memory_total = pagetok (dynamic.psd_rm); } void HostMachineInfo::update_info( ) { static struct pst_dynamic dynamic; pstat_getdynamic (&dynamic, sizeof (dynamic), 1, 0); memory_used = pagetok (dynamic.psd_arm); memory_free = memory_total - memory_used; swap_total = pagetok (dynamic.psd_vm); swap_used = pagetok (dynamic.psd_avm); swap_free = swap_total - swap_used; } # endif } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/OS/HostInfoIrix.h000066400000000000000000000064441476623553700173220ustar00rootroot00000000000000//# HostInfo_irix.h: SGI Irix specific memory, swap, and CPU code. /* ** This is a greatly MODIFIED version of a "top" machine dependent file. ** The only resemblance it bears to the original is with respect to the ** mechanics of finding various system details. The copyright details ** follow. ** ** --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ** ** Top users/processes display for Unix ** Version 3 ** ** This program may be freely redistributed, ** but this entire comment MUST remain intact. ** ** Copyright (c) 1984, 1989, William LeFebvre, Rice University ** Copyright (c) 1989 - 1994, William LeFebvre, Northwestern University ** Copyright (c) 1994, 1995, William LeFebvre, Argonne National Laboratory ** Copyright (c) 1996, William LeFebvre, Group sys Consulting ** Copyright (c) 2002, Associated Universities Inc. */ #ifndef CASA_HOSTINFOIRIX_H #define CASA_HOSTINFOIRIX_H # if defined(HOSTINFO_DO_IMPLEMENT) /* * AUTHOR: Darrell Schiebel * * ORIGINAL AUTHORS: Sandeep Cariapa * Larry McVoy * John Schimmel * Ariel Faigon */ #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // HostInfo for IRIX machines. // // // // // //
        34. HostInfo // // // This file provides the IRIX specific functions for HostInfo. // It is selectively included by HostInfo.cc. // // // #define pagetok(pages) ((((uint64_t) pages) * pagesize) >> 10) class HostMachineInfo { friend class HostInfo; HostMachineInfo( ); void update_info( ); int valid; int cpus; ptrdiff_t swap_total; ptrdiff_t swap_used; ptrdiff_t swap_free; ptrdiff_t memory_total; ptrdiff_t memory_used; ptrdiff_t memory_free; ptrdiff_t pagesize; }; // HostMachineInfo::HostMachineInfo( ) : valid(1) { pagesize = getpagesize(); if ((cpus = sysmp(MP_NPROCS)) == -1) { perror("sysmp(MP_NPROCS)"); valid = 0; return; } struct rminfo realmem; if (sysmp(MP_SAGET, MPSA_RMINFO, &realmem, sizeof(realmem)) == -1) { perror("sysmp(MP_SAGET,MPSA_RMINFO, ...)"); valid = 0; return; } memory_total = pagetok(realmem.physmem); } void HostMachineInfo::update_info( ) { int i; struct rminfo realmem; struct sysinfo sysinfo; off_t fswap; /* current free swap in blocks */ off_t tswap; /* total swap in blocks */ swapctl(SC_GETFREESWAP, &fswap); swapctl(SC_GETSWAPTOT, &tswap); if (sysmp(MP_SAGET, MPSA_RMINFO, &realmem, sizeof(realmem)) == -1) { perror("sysmp(MP_SAGET,MPSA_RMINFO, ...)"); valid = 0; return; } memory_free = pagetok(realmem.freemem); memory_used = memory_total - memory_free; swap_total = tswap / 2; swap_free = fswap / 2; swap_used = swap_total - swap_free; } # endif } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/OS/HostInfoLinux.h000066400000000000000000000213001476623553700174720ustar00rootroot00000000000000//# HostInfo_linux.h: Linux specific memory, swap, and CPU code. /* ** This is a greatly MODIFIED version of a "top" machine dependent file. ** The only resemblance it bears to the original is with respect to the ** mechanics of finding various system details. The copyright details ** follow. ** ** --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ** ** Top users/processes display for Unix ** Version 3 ** ** This program may be freely redistributed, ** but this entire comment MUST remain intact. ** ** Copyright (c) 1984, 1989, William LeFebvre, Rice University ** Copyright (c) 1989 - 1994, William LeFebvre, Northwestern University ** Copyright (c) 1994, 1995, William LeFebvre, Argonne National Laboratory ** Copyright (c) 1996, William LeFebvre, Group sys Consulting ** Copyright (c) 2002, Associated Universities Inc. */ #ifndef CASA_HOSTINFOLINUX_H #define CASA_HOSTINFOLINUX_H # if defined(HOSTINFO_DO_IMPLEMENT) /* * AUTHOR: Darrell Schiebel * * ORIGINAL AUTHORS: Richard Henderson * Alexey Klimkin * * */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // HostInfo for Linux machines. // // // // // //
        35. HostInfo // // // This file provides the Linux specific functions for HostInfo. // It is selectively included by HostInfo.cc. // // // #if 0 #include /* for PROC_SUPER_MAGIC */ #else #define PROC_SUPER_MAGIC 0x9fa0 #endif #define PROCFS "/proc" #define CPUINFO "/proc/cpuinfo" #define MEMINFO "/proc/meminfo" #define bytetok(x) (((x) + 512) >> 10) namespace casacore { //# NAMESPACE CASACORE - BEGIN class HostMachineInfo { friend class HostInfo; HostMachineInfo( ); void update_info( ); int valid; int cpus; ptrdiff_t memory_total; ptrdiff_t memory_used; ptrdiff_t memory_free; ptrdiff_t swap_total; ptrdiff_t swap_used; ptrdiff_t swap_free; }; // static inline char * skip_ws(const char *p) { while (isspace(*p)) p++; return (char *)p; } static inline char * skip_token(const char *p) { while (isspace(*p)) p++; while (*p && !isspace(*p)) p++; return (char *)p; } // get integer value from v1 cgroup hierarchy of current processes, if // sub_value is set it returns the entry of a collection identified by value, // e.g. total_rss from memory.stat // returns std::numeric_limits::max() on error // note unset cgroup limits usually have intptr_t.max() // does not support v2 cgroups static inline uInt64 get_cgroup_limit(std::string group, std::string value, std::string sub_value="") { uInt64 result = std::numeric_limits::max(); // assume common location, technically one needs to search for mounts const std::string cgroup = std::string("/sys/fs/cgroup/") + group + "/"; // get hierarchy of current process, v1 format: id:controller:hierarchy std::string line; std::ifstream ifs("/proc/self/cgroup", std::ifstream::in); std::string hierarchy; while (getline(ifs, line)) { std::stringstream ss(line); std::string token; std::vector fields; while (getline(ss, token, ':')) { fields.push_back(token); } if (fields.size() % 3 != 0) { return result; } for (std::vector::size_type i=1; i < fields.size(); i+=3) { if (fields[i].find(group) != std::string::npos) { hierarchy = fields[i + 1] + "/"; } } } if (hierarchy.size() == 0) { return result; } std::ifstream flimit((cgroup + hierarchy + value).c_str(), std::ifstream::in); // if hierarchy does not exist we might be in a namespace, use the root group if (!flimit.is_open()) { flimit.open((cgroup + value).c_str(), std::ifstream::in); } if (flimit.is_open()) { if (!sub_value.empty()) { /* scan 'key value' entry like memory.stat for key == sub_value */ while (getline(flimit, line)) { std::stringstream ss(line); std::string token; ss >> token; if (token == sub_value) { ss >> result; break; } } } else { flimit >> result; } } return result; } HostMachineInfo::HostMachineInfo( ) : valid(1) { char buffer[4096+1]; char *p; /* get number of usable CPUs */ cpu_set_t cpuset; if (sched_getaffinity(0, sizeof(cpuset), &cpuset) == 0) { # ifdef CPU_COUNT /* glibc < 2.6 */ cpus = CPU_COUNT(&cpuset); # else for (int i = 0; i < CPU_SETSIZE; i++) { if (CPU_ISSET(i, &cpuset)) { cpus++; } } # endif } else { #ifndef AIPS_CRAY_PGI /* make sure the proc filesystem is mounted */ { struct statfs sb; if (statfs(PROCFS, &sb) < 0 || sb.f_type != PROC_SUPER_MAGIC) { fprintf( stderr, "proc filesystem not mounted on " PROCFS "\n" ); valid = 0; return; } } #endif /* get number of CPUs */ { cpus = 0; FILE *fptr = fopen(CPUINFO, "r"); while ( (p = fgets( buffer, sizeof(buffer), fptr )) ) { if ( ! strncmp( p, "processor", 9 ) ) ++cpus; } fclose(fptr); } } update_info(); } void HostMachineInfo::update_info( ) { char buffer[4096+1]; int fd, len; /* get system wide memory usage */ { char *p; unsigned long sys_mem_total, sys_mem_free, sys_mem_cached, sys_mem_avail, sys_swap_total, sys_swap_free; fd = open(MEMINFO, O_RDONLY); len = ::read(fd, buffer, sizeof(buffer)-1); close(fd); buffer[len] = '\0'; p = strstr(buffer, "MemTotal:"); if (sscanf (p,"MemTotal: %lu kB\nMemFree: %lu kB\n", &sys_mem_total, &sys_mem_free) != 2) cerr << "Error parsing MemTotal and MemFree in /proc/meminfo\n"; p = strstr (buffer, "Cached:"); if (sscanf (p,"Cached: %lu kB\n", &sys_mem_cached) != 1) cerr << "Error parsing Cached in /proc/meminfo\n"; /* available since 3.14, real available memory accounting for * unreclaimable page cache and slab cache */ p = strstr (buffer, "MemAvailable:"); if (!p || sscanf (p,"MemAvailable: %lu kB\n", &sys_mem_avail) != 1) { /* too old kernel, estimate via cache and free */ sys_mem_avail = sys_mem_cached + sys_mem_free; } /* check resource limits, note that these are not enforced by linux */ struct rlimit rlim; if (getrlimit(RLIMIT_RSS, &rlim) == 0 && rlim.rlim_cur > 0) { sys_mem_total = std::min(rlim.rlim_cur / 1024, (rlim_t)sys_mem_total); /* without cgroups we cannot determine available memory within the limit */ sys_mem_avail = std::min(sys_mem_total, sys_mem_avail); } /* can't use more memory than allowed by cgroups, enforced */ uInt64 proc_mem_max = get_cgroup_limit("memory", "memory.limit_in_bytes") / 1024; /* usage_in_bytes also includes cache so use memory.stat */ uInt64 proc_mem_used = get_cgroup_limit("memory", "memory.stat", "total_rss") / 1024; /* set HostInfo memoryTotal() */ memory_total = std::min((uInt64)sys_mem_total, proc_mem_max); /* if we have a valid cgroup limit we can determine memoryFree() exactly */ if (proc_mem_max <= sys_mem_total && proc_mem_used <= proc_mem_max) { memory_free = proc_mem_max - proc_mem_used; } else { /* no cgroups so we have to assume all memory of host is available */ memory_free = std::min((uInt64)sys_mem_avail, (uInt64)memory_total); } memory_used = memory_total - memory_free; p = strstr (buffer, "SwapTotal:"); if (sscanf (p, "SwapTotal: %lu kB\nSwapFree: %lu kB\n", &sys_swap_total, &sys_swap_free) != 2) cerr << "Error parsing SwapTotal and SwapFree in /proc/meminfo\n"; /* can't use more swap than allowed by cgroups */ uInt64 proc_swap_max = get_cgroup_limit("memory", "memory.memsw.limit_in_bytes") / 1024; uInt64 proc_swap_used = get_cgroup_limit("memory", "memory.stat", "total_swap") / 1024; /* limit is mem + swap */ if (proc_mem_max <= sys_mem_total && proc_mem_max <= proc_swap_max) { proc_swap_max = proc_swap_max - proc_mem_max; } /* set swapTotal() */ swap_total = std::min((uInt64)sys_swap_total, proc_swap_max); if (proc_swap_max <= (uInt64)swap_total && proc_swap_used <= proc_swap_max) { swap_free = proc_swap_max - proc_swap_used; } else { swap_free = sys_swap_free; } swap_used = swap_total - swap_free; } } } //# NAMESPACE CASACORE - END # endif #endif casacore-3.7.1/casa/OS/HostInfoOsf1.h000066400000000000000000000122701476623553700172110ustar00rootroot00000000000000//# HostInfo_osf1.h: DEC OSF/1 specific memory, swap, and CPU code. /* ** This is a greatly MODIFIED version of a "top" machine dependent file. ** The only resemblance it bears to the original is with respect to the ** mechanics of finding various system details. The copyright details ** follow. ** ** --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ** ** Top users/processes display for Unix ** Version 3 ** ** This program may be freely redistributed, ** but this entire comment MUST remain intact. ** ** Copyright (c) 1984, 1989, William LeFebvre, Rice University ** Copyright (c) 1989 - 1994, William LeFebvre, Northwestern University ** Copyright (c) 1994, 1995, William LeFebvre, Argonne National Laboratory ** Copyright (c) 1996, William LeFebvre, Group sys Consulting ** Copyright (c) 2002, Associated Universities Inc. */ /* * LIBS: -lmach -lpset * * AUTHOR: Darrell Schiebel * * ORIGINAL AUTHOR: Anthony Baxter * ORIGINAL CONTRIBUTORS: David S. Comay * Claus Kalle * Pat Welch * William LeFebvre * Rainer Orth */ #ifndef CASA_HOSTINFOOSF1_H #define CASA_HOSTINFOOSF1_H # if defined(HOSTINFO_DO_IMPLEMENT) // //--> /usr/include/mach/mach_interface.h:297: <- //--> previous declaration of `int vm_statistics(long int, struct vm_statistics *)' with C++ linkage <- // // so, we hack this by defining _mach to kick out of inclusion, // and then below we make this extern "C"... likely this is fixed in some version of // DEC OSF/1 which is newer than what we have... // #define _mach #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // HostInfo for OSF1 machines. // // // // // //
        36. HostInfo // // // This file provides the OSF1 specific functions for HostInfo. // It is selectively included by HostInfo.cc. // // // extern "C" kern_return_t host_info(int, int, host_info_t, unsigned int *); extern "C" int host_self( ); extern "C" int vm_statistics(long, struct vm_statistics *); /* Log base 2 of 1024 is 10 (2^10 == 1024) */ #define LOG1024 10 /* these are for getting the memory statistics */ /* define pagetok in terms of pageshift */ #define pagetok(size) ((size) << pageshift) class HostMachineInfo { friend class HostInfo; HostMachineInfo( ); void update_info( ); int valid; int cpus; ptrdiff_t swap_total; ptrdiff_t swap_used; ptrdiff_t swap_free; ptrdiff_t memory_total; ptrdiff_t memory_used; ptrdiff_t memory_free; int pageshift; /* log base 2 of the pagesize */ }; // HostMachineInfo::HostMachineInfo( ) : valid(1) { int i = 0; int pagesize; struct tbl_sysinfo sibuf; kern_return_t ret; struct host_basic_info basic_info; unsigned int count = HOST_BASIC_INFO_COUNT; /* get the page size with "getpagesize" and calculate pageshift from it */ pagesize = getpagesize(); pageshift = 0; while (pagesize > 1) { pageshift++; pagesize >>= 1; } /* we only need the amount of log(2)1024 for our conversion */ pageshift -= LOG1024; ret = host_info( host_self(), HOST_BASIC_INFO, (host_info_t) &basic_info, &count ); if ( ret != KERN_SUCCESS ) { valid = 0; } else { memory_total = basic_info.memory_size / 1024; cpus = basic_info.avail_cpus; } } void HostMachineInfo::update_info( ) { struct tbl_swapinfo swbuf; vm_statistics_data_t vmstats; int swappages=0,swapfree=0,i; /* memory information */ /* this is possibly bogus - we work out total # pages by */ /* adding up the free, active, inactive, wired down, and */ /* zero filled. Anyone who knows a better way, TELL ME! */ /* Change: dont use zero filled. */ (void) vm_statistics(task_self(),&vmstats); /* thanks DEC for the table() command. No thanks at all for */ /* omitting the man page for it from OSF/1 1.2, and failing */ /* to document SWAPINFO in the 1.3 man page. Lets hear it for */ /* include files. */ i=0; while(table(TBL_SWAPINFO,i,&swbuf,1,sizeof(struct tbl_swapinfo))>0) { swappages += swbuf.size; swapfree += swbuf.free; i++; } swap_total = pagetok(swappages); swap_used = pagetok(swappages - swapfree); swap_free = pagetok(swapfree); memory_free = pagetok(vmstats.free_count); memory_used = memory_total - memory_free; // some memory is left unaccounted for, using the following... // memory_used = pagetok(vmstats.active_count + vmstats.inactive_count + vmstats.wire_count); } # endif } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/OS/HostInfoSolaris.h000066400000000000000000000164251476623553700200230ustar00rootroot00000000000000//# HostInfo_solaris.h: Solaris specific memory, swap, and CPU code. /* ** This is a greatly MODIFIED version of a "top" machine dependent file. ** The only resemblance it bears to the original is with respect to the ** mechanics of finding various system details. The copyright details ** follow. ** ** --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ** ** Top users/processes display for Unix ** Version 3 ** ** This program may be freely redistributed, ** but this entire comment MUST remain intact. ** ** Copyright (c) 1984, 1989, William LeFebvre, Rice University ** Copyright (c) 1989 - 1994, William LeFebvre, Northwestern University ** Copyright (c) 1994, 1995, William LeFebvre, Argonne National Laboratory ** Copyright (c) 1996, William LeFebvre, Group sys Consulting ** Copyright (c) 2002, Associated Universities Inc. */ #ifndef CASA_HOSTINFOSOLARIS_H #define CASA_HOSTINFOSOLARIS_H # if defined(HOSTINFO_DO_IMPLEMENT) /* * * LIBS: -lkstat * * AUTHOR: Darrell Schiebel * * ORIGINAL AUTHORS: Torsten Kasch * Robert Boucher * ORIGINAL CONTRIBUTORS: Marc Cohen * Charles Hedrick * William L. Jones * Petri Kutvonen * Casper Dik * Tim Pugh */ #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // HostInfo for Solaris machines. // // // // // //
        37. HostInfo // // // This file provides the Solaris specific functions for HostInfo. // It is selectively included by HostInfo.cc. // // // /* * Some kstats are fixed at 32 bits, these will be specified as ui32; some * are "natural" size (32 bit on 32 bit Solaris, 64 on 64 bit Solaris * we'll make those unsigned long) * Older Solaris doesn't define KSTAT_DATA_UINT32, those are always 32 bit. */ # ifndef KSTAT_DATA_UINT32 # define ui32 ul # endif #ifdef SC_AINFO #undef USE_ANONINFO /* Use swapctl() instead */ #endif #define NO_NPROC /* pagetok function is really a pointer to an appropriate function */ #define pagetok(size) ((*p_pagetok)(size)) #ifndef USE_ANONINFO static void get_swapinfo(int *total, int *fr); #endif class HostMachineInfo { friend class HostInfo; void kupdate( ); HostMachineInfo( ); ~HostMachineInfo( ); void update_info( ); static int pageshift; int (*p_pagetok) (int); static inline int pagetok_none(int size) { return(size); } static inline int pagetok_left(int size) { return(size << pageshift); } static inline int pagetok_right(int size) { return(size >> pageshift); } int valid; kstat_ctl_t *kc; int cpus; ptrdiff_t memory_total; ptrdiff_t memory_used; ptrdiff_t memory_free; ptrdiff_t swap_total; ptrdiff_t swap_used; ptrdiff_t swap_free; }; // int HostMachineInfo::pageshift = 0; HostMachineInfo::~HostMachineInfo( ) { if ( kc ) kstat_close(kc); } HostMachineInfo::HostMachineInfo( ) : valid(1), kc(NULL) { int i; /* calculate pageshift value */ i = sysconf(_SC_PAGESIZE); pageshift = 0; while ((i >>= 1) > 0) pageshift++; /* calculate an amount to shift to K values */ /* remember that log base 2 of 1024 is 10 (i.e.: 2^10 = 1024) */ pageshift -= 10; /* now determine which pageshift function is appropriate for the result (have to because x << y is undefined for y < 0) */ if (pageshift > 0) { /* this is the most likely */ p_pagetok = pagetok_left; } else if (pageshift == 0) { p_pagetok = pagetok_none; } else { p_pagetok = pagetok_right; pageshift = -pageshift; } long maxmem = sysconf(_SC_PHYS_PAGES); memory_total = pagetok (maxmem); /* use kstat to update all processor information */ kupdate( ); kstat_t *ks = kstat_lookup(kc, "unix", 0, "system_misc"); if (kstat_read(kc, ks, 0) == -1) { perror("kstat_read"); valid = 0; } kstat_named_t *kn = (kstat_named_t*) kstat_data_lookup(ks, "ncpus"); cpus = kn->value.ui32; } void HostMachineInfo::kupdate( ) { kid_t nkcid; int i; static kid_t kcid = 0; /* * 0. kstat_open */ if (!kc) { kc = kstat_open(); if (!kc) { perror("kstat_open "); valid = 0; } kcid = kc->kc_chain_id; } /* keep doing it until no more changes */ kcid_changed: /* * 1. kstat_chain_update */ nkcid = kstat_chain_update(kc); if (nkcid) { /* UPDKCID will abort if nkcid is -1, so no need to check */ kcid = nkcid; } if (nkcid == -1) { perror("kstat_read "); valid = 0; } if (nkcid != 0) goto kcid_changed; } void HostMachineInfo::update_info( ) { static long freemem; static int swaptotal; static int swapfree; kstat_t *ks; kstat_named_t *kn; ks = kstat_lookup(kc, "unix", 0, "system_pages"); if (kstat_read(kc, ks, 0) == -1) { perror("kstat_read"); valid = 0; } kn = (kstat_named_t*) kstat_data_lookup(ks, "freemem"); if (kn) freemem = kn->value.ul; memory_free = pagetok (freemem); memory_used = memory_total - memory_free; get_swapinfo(&swaptotal, &swapfree); swap_total = pagetok(swaptotal); swap_used = pagetok(swaptotal - swapfree); swap_free = pagetok(swapfree); } #ifndef USE_ANONINFO void get_swapinfo(int *total, int *fr) { #ifdef SC_AINFO struct anoninfo anon; if (swapctl(SC_AINFO, &anon) == -1) { *total = *fr = 0; return; } *total = anon.ani_max; *fr = anon.ani_max - anon.ani_resv; #else int cnt, i; int t, f; struct swaptable *swt; struct swapent *ste; static char path[256]; /* get total number of swap entries */ cnt = swapctl(SC_GETNSWP, 0); /* allocate enough space to hold count + n swapents */ swt = (struct swaptable *)malloc(sizeof(int) + cnt * sizeof(struct swapent)); if (swt == NULL) { *total = 0; *fr = 0; return; } swt->swt_n = cnt; /* fill in ste_path pointers: we don't care about the paths, so we point them all to the same buffer */ ste = &(swt->swt_ent[0]); i = cnt; while (--i >= 0) { ste++->ste_path = path; } /* grab all swap info */ swapctl(SC_LIST, swt); /* walk thru the structs and sum up the fields */ t = f = 0; ste = &(swt->swt_ent[0]); i = cnt; while (--i >= 0) { /* dont count slots being deleted */ if (!(ste->ste_flags & ST_INDEL) && !(ste->ste_flags & ST_DOINGDEL)) { t += ste->ste_pages; f += ste->ste_free; } ste++; } /* fill in the results */ *total = t; *fr = f; free(swt); #endif /* SC_AINFO */ } #endif /* USE_ANONINFO */ } //# NAMESPACE CASACORE - END # endif #endif casacore-3.7.1/casa/OS/IBMConversion.cc000066400000000000000000000264061476623553700175500ustar00rootroot00000000000000//# IBMConversion.cc: A class with static functions to convert IBM format //# Copyright (C) 1996,1997,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN static char fromEBCDIC[256] = { 0 ,1 ,2 ,3 ,0 ,9 ,0 ,127,0 ,0 ,0 ,11,12,13,14,15, 16 ,17 ,18 ,0 ,0 ,0 ,8 ,0 ,24 ,25 ,0 ,0 ,28,29,30,31, 0 ,0 ,28 ,0 ,0 ,10 ,23 ,27 ,0 ,0 ,0 ,0 ,0 ,5 ,6 ,7 , 0 ,0 ,24 ,0 ,0 ,30 ,0 ,4 ,0 ,0 ,0 ,19,20,21,0 ,26, 32 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,46,60,40,43,124, 38 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,33 ,36,42,41,59,94, 45 ,47 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,124,44,37,95,62,63, 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,96 ,58 ,35,64,39,61,34, 0 ,97 ,98 ,99 ,100,101,102,103,104,105,0 ,0 ,0 ,0 ,0 ,0 , 0 ,106,107,108,109,110,111,112,113,114,0 ,0 ,0 ,0 ,0 ,0 , 0 ,126,115,116,117,118,119,120,121,122,0 ,0 ,0 ,0 ,0 ,0 , 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , 123,65 ,66 ,67 ,68 ,69 ,70 ,71 ,72 ,73 ,0 ,0 ,0 ,0 ,0 ,0 , 125,74 ,75 ,76 ,77 ,78 ,79 ,80 ,81 ,82 ,0 ,0 ,0 ,0 ,0 ,0 , 92 ,0 ,83 ,84 ,85 ,86 ,87 ,88 ,89 ,90 ,0 ,0 ,0 ,0 ,0 ,0 , 48 ,49 ,50 ,51 ,52 ,53 ,54 ,55 ,56 ,57 ,124,0 ,0 ,0 ,0 ,0 }; static signed char toEBCDIC[128] = { 0, 1, 2, 3, 55, 45, 46, 47, 22, 5, 37, 11, 12, 13, 14, 15, 16, 17, 18, 59, 60, 61, 50, 38, 24, 25, 63, 39, 28, 29, 30, 31, 64, 90, 127, 123, 91, 108, 80, 125, 77, 93, 92, 78, 107, 96, 75, 97, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, 122, 94, 76, 126, 110, 111, 124, -63, -62, -61, -60, -59, -58, -57, -56, -55, -47, -46, -45, -44, -43, -42, -41, -40, -39, -30, -29, -28, -27, -26, -25, -24, -23, -64, -32, -48, 95, 109, 121,-127,-126,-125,-124,-123,-122,-121, -120,-119,-111,-110,-109,-108,-107,-106, -105,-104,-103, -94, -93, -92, -91, -90, -89, -88, -87, -86, -6, -48, -95, 7 }; void IBMConversion::toLocal (char& to, const void* from) { assert (sizeof(char) == 1); to = fromEBCDIC[*(const unsigned char*)from]; } void IBMConversion::toLocal (char* to, const void* from, size_t nr) { assert (sizeof(char) == 1); const unsigned char* data = (const unsigned char*)from; char* last = to + nr; while (to < last) { *to++ = fromEBCDIC[*data++]; } } void IBMConversion::fromLocal (void* to, char from) { assert (sizeof(char) == 1); if (static_cast(from) < 0) { *(signed char*)to = 0; }else{ *(signed char*)to = toEBCDIC[int(from)]; } } void IBMConversion::fromLocal (void* to, const char* from, size_t nr) { assert (sizeof(char) == 1); signed char* data = (signed char*)to; const char* last = from + nr; while (from < last) { fromLocal (data++, *from++); } } void IBMConversion::toLocal (Int64* to, const void* from, size_t nr) { #if !defined(AIPS_LITTLE_ENDIAN) if (sizeof(Int64) == SIZE_IBM_INT64) { memcpy (to, from, nr*sizeof(Int64)); return; } #endif const char* data = (const char*)from; Int64* last = to + nr; while (to < last) { toLocal (*to++, data); data += 4; } } void IBMConversion::toLocal (uInt64* to, const void* from, size_t nr) { #if !defined(AIPS_LITTLE_ENDIAN) if (sizeof(uInt64) == SIZE_IBM_UINT64) { memcpy (to, from, nr*sizeof(uInt64)); return; } #endif const char* data = (const char*)from; uInt64* last = to + nr; while (to < last) { toLocal (*to++, data); data += 4; } } void IBMConversion::fromLocal (void* to, const Int64* from, size_t nr) { char* data = (char*)to; const Int64* last = from + nr; while (from < last) { fromLocal (data, *from++); data += 4; } } void IBMConversion::fromLocal (void* to, const uInt64* from, size_t nr) { char* data = (char*)to; const uInt64* last = from + nr; while (from < last) { fromLocal (data, *from++); data += 4; } } // IBM has format SEEEEEEE FFFFFFFF ... // The exponent has base 16. Fraction has no hidden bits. // IEEE has format SEEEEEEE EFFFFFFF ... // The exponent has base 2. Fraction has a hidden bit (i.e. a 1 before first F) void IBMConversion::toLocal (float* to, const void* from, size_t nr) { assert (sizeof(unsigned int) == 4); assert (sizeof(float) == 4); const char* data = (const char*)from; float* last = to + nr; while (to < last) { unsigned int value; #if defined(AIPS_LITTLE_ENDIAN) CanonicalConversion::reverse4 (&value, data); #else CanonicalConversion::move4 (&value, data); #endif if ((value & 0x00ffffff) == 0) { *to = 0; }else{ // Get exponent by shifting 24 bits to right. // IBM has base 16, IEEE has base 2. // So multiply by 4, thus shift 2 left. int exponent = ((value & 0x7f000000) >> (24-2)); exponent -= 256; unsigned int sign = (value & 0x80000000); // Shift to left until first bit of fraction is one. while ((value & 0x00800000) == 0) { value <<= 1; exponent--; } // Test for over/underflow. if (exponent > 128) { exponent = 128; value = 0xffffffff; } else if (exponent <= -126) { exponent = -126; sign = 0; value = 0; } // Mask off first bit of fraction (is hidden bit). *(unsigned int*)to = sign | ((exponent+126) << 23) | (value & 0x007fffff); } data += 4; to++; } } void IBMConversion::fromLocal (void* to, const float* from, size_t nr) { assert (sizeof(unsigned int) == 4); assert (sizeof(float) == 4); char* data = (char*)to; const float* last = from + nr; while (from < last) { unsigned int value; value = *(unsigned int*)from; int expo = (value & 0x7f800000) >> 23; if (expo == 0) { value = 0; }else{ // Keep exponent positive, but correct for multiple of 4 test. expo += (128-126); unsigned int sign = (value & 0x80000000); value &= 0x007fffff; // remove sign and exponent value |= 0x00800000; // add hidden bit // Make the exponent a multiple of 4 (by shifting 2 bits) // while shifting mantissa to right. int exponent = ((expo+3) >> 2); value >>= ((exponent << 2) - expo); exponent -= 32; // Under/overflow of exponent is not possible. value |= sign | ((exponent+64)<<24); } #if defined(AIPS_LITTLE_ENDIAN) CanonicalConversion::reverse4 (data, &value); #else CanonicalConversion::move4 (data, &value); #endif data += 4; from++; } } // IBM has format SEEEEEEE FFFFFFFF ... // The exponent has base 16. Fraction has no hidden bits. // IEEE has format SEEEEEEE EEEEFFFF ... // The exponent has base 2. Fraction has a hidden bit (i.e. a 1 before first F) void IBMConversion::toLocal (double* to, const void* from, size_t nr) { assert (sizeof(unsigned int) == 4); assert (sizeof(double) == 8); const char* data = (const char*)from; double* last = to + nr; while (to < last) { unsigned int value, rest; #if defined(AIPS_LITTLE_ENDIAN) CanonicalConversion::reverse4 (&value, data); CanonicalConversion::reverse4 (&rest, data+4); #else CanonicalConversion::move4 (&value, data); CanonicalConversion::move4 (&rest, data+4); #endif if ((value & 0x00ffffff) == 0) { *to = 0; }else{ // Get exponent by shifting 24 bits to right. // IBM has base 16, IEEE has base 2. // So multiply by 4, thus shift 2 left. int exponent = ((value & 0x7f000000) >> (24-2)); exponent -= 256; unsigned int sign = (value & 0x80000000); // Shift to left until first bit of fraction is one. while ((value & 0x00800000) == 0) { value <<= 1; if ((rest & 0x80000000) != 0) { value++; // shift first bit of rest } rest <<= 1; exponent--; } // Over/underflow is not possible. // Shift fraction to the right (4 bits minus hidden bit). // Mask off first bit of fraction (is hidden bit). rest = (value << 29) | (rest >> 3); value = sign | ((exponent+1022) << 20) | ((value >> 3) & 0x000fffff); #if defined(AIPS_LITTLE_ENDIAN) ((unsigned int*)to)[0] = rest; ((unsigned int*)to)[1] = value; #else ((unsigned int*)to)[0] = value; ((unsigned int*)to)[1] = rest; #endif } to++; data += 8; } } void IBMConversion::fromLocal (void* to, const double* from, size_t nr) { assert (sizeof(unsigned int) == 4); assert (sizeof(double) == 8); char* data = (char*)to; const double* last = from + nr; while (from < last) { unsigned int value, rest; #if defined(AIPS_LITTLE_ENDIAN) rest = ((unsigned int*)from)[0]; value = ((unsigned int*)from)[1]; #else value = ((unsigned int*)from)[0]; rest = ((unsigned int*)from)[1]; #endif unsigned int expo = (value & 0x7ff00000) >> 20; if (expo == 0) { value = 0; rest = 0; }else{ // Keep exponent positive, but correct for multiple of 4 test. expo += (1024-1022); unsigned int sign = (value & 0x80000000); value &= 0x000fffff; // remove sign and exponent value |= 0x00100000; // add hidden bit // The mantissa has to be shifted left 3 bits. // Make the exponent a multiple of 4 (by shifting 2 bits) // while shifting mantissa to right (will not be more than 3 bits). // So the net result is no shift or a shift left. int exponent = ((expo+3) >> 2); expo -= (exponent << 2) - 3; if (expo > 0) { value <<= expo; value |= (rest >> (32 - expo)); rest <<= expo; } exponent -= 256; // Test for over/underflow. if (exponent > 63) { value = 0x00ffffff; rest = 0xffffffff; exponent = 63; } else if (exponent <= -64 || (value == 0 && rest == 0)) { value = 0; rest = 0; exponent = -64; sign = 0; } value |= sign | ((exponent+64)<<24); } #if defined(AIPS_LITTLE_ENDIAN) CanonicalConversion::reverse4 (data, &value); CanonicalConversion::reverse4 (data+4, &rest); #else CanonicalConversion::move4 (data, &value); CanonicalConversion::move4 (data+4, &rest); #endif data += 8; from++; } } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/OS/IBMConversion.h000066400000000000000000000252341476623553700174100ustar00rootroot00000000000000//# IBMConversion.h: A class with static functions to convert IBM format //# Copyright (C) 1996,1997,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_IBMCONVERSION_H #define CASA_IBMCONVERSION_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Define the IBM sizes of the built-in data types. #define SIZE_IBM_CHAR 1 #define SIZE_IBM_UCHAR 1 #define SIZE_IBM_SHORT 2 #define SIZE_IBM_USHORT 2 #define SIZE_IBM_INT 4 #define SIZE_IBM_UINT 4 #define SIZE_IBM_INT64 4 #define SIZE_IBM_UINT64 4 #define SIZE_IBM_FLOAT 4 #define SIZE_IBM_DOUBLE 8 // // A class with static functions to convert IBM format // // // // // // This class contains static toLocal functions to convert data from IBM-360 // format to local format and vice-versa. It also handles the conversion // of the IBM EBCDIC characters to ASCII characters (for data type char). //

          // The functions work well on big-endian as well as little-endian machines. // // // Archived WSRT data can be stored in the old IBM format // (EBCDIC characters and floats with base 16). // Conversion functions are needed to read these data. // // //

        38. Support data type long double. // class IBMConversion { public: // Convert one value from IBM format to local format. // The from and to buffer should not overlap. // // The char version converts from EBCDIC to ASCII, while the // unsigned char version is a simple copy. // // static void toLocal (char& to, const void* from); static void toLocal (unsigned char& to, const void* from); static void toLocal (short& to, const void* from); static void toLocal (unsigned short& to, const void* from); static void toLocal (int& to, const void* from); static void toLocal (unsigned int& to, const void* from); static void toLocal (Int64& to, const void* from); static void toLocal (uInt64& to, const void* from); static void toLocal (float& to, const void* from); static void toLocal (double& to, const void* from); // // Convert nr values from IBM format to local format. // The from and to buffer should not overlap. // // The char version converts from EBCDIC to ASCII, while the // unsigned char version is a simple copy. // // static void toLocal (char* to, const void* from, size_t nr); static void toLocal (unsigned char* to, const void* from, size_t nr); static void toLocal (short* to, const void* from, size_t nr); static void toLocal (unsigned short* to, const void* from, size_t nr); static void toLocal (int* to, const void* from, size_t nr); static void toLocal (unsigned int* to, const void* from, size_t nr); static void toLocal (Int64* to, const void* from, size_t nr); static void toLocal (uInt64* to, const void* from, size_t nr); static void toLocal (float* to, const void* from, size_t nr); static void toLocal (double* to, const void* from, size_t nr); // // Convert one value from local format to IBM format. // The from and to buffer should not overlap. // // The char version converts from ASCII to EBCDIC, while the // unsigned char version is a simple copy. // // static void fromLocal (void* to, char from); static void fromLocal (void* to, unsigned char from); static void fromLocal (void* to, short from); static void fromLocal (void* to, unsigned short from); static void fromLocal (void* to, int from); static void fromLocal (void* to, unsigned int from); static void fromLocal (void* to, Int64 from); static void fromLocal (void* to, uInt64 from); static void fromLocal (void* to, float from); static void fromLocal (void* to, double from); // // Convert nr values from local format to IBM format. // The from and to buffer should not overlap. // // The char version converts from ASCII to EBCDIC, while the // unsigned char version is a simple copy. // // static void fromLocal (void* to, const char* from, size_t nr); static void fromLocal (void* to, const unsigned char* from, size_t nr); static void fromLocal (void* to, const short* from, size_t nr); static void fromLocal (void* to, const unsigned short* from, size_t nr); static void fromLocal (void* to, const int* from, size_t nr); static void fromLocal (void* to, const unsigned int* from, size_t nr); static void fromLocal (void* to, const Int64* from, size_t nr); static void fromLocal (void* to, const uInt64* from, size_t nr); static void fromLocal (void* to, const float* from, size_t nr); static void fromLocal (void* to, const double* from, size_t nr); // private: // This class should not be constructed // (so declare the constructor private). IBMConversion(); }; inline void IBMConversion::toLocal (unsigned char& to, const void* from) { CanonicalConversion::toLocal (to, from); } inline void IBMConversion::toLocal (short& to, const void* from) { CanonicalConversion::toLocal (to, from); } inline void IBMConversion::toLocal (unsigned short& to, const void* from) { CanonicalConversion::toLocal (to, from); } inline void IBMConversion::toLocal (int& to, const void* from) { CanonicalConversion::toLocal (to, from); } inline void IBMConversion::toLocal (unsigned int& to, const void* from) { CanonicalConversion::toLocal (to, from); } inline void IBMConversion::toLocal (Int64& to, const void* from) { if (sizeof(Int64) != 4) { if (((signed char*)from)[0] < 0) { to = -1; }else{ to = 0; } } #if defined(AIPS_LITTLE_ENDIAN) CanonicalConversion::reverse4 (&to, from); #else CanonicalConversion::move4 (((char*)&to)+sizeof(Int64)-4, from); #endif } inline void IBMConversion::toLocal (uInt64& to, const void* from) { if (sizeof(uInt64) != 4) { to = 0; } #if defined(AIPS_LITTLE_ENDIAN) CanonicalConversion::reverse4 (&to, from); #else CanonicalConversion::move4 (((char*)&to)+sizeof(uInt64)-4, from); #endif } inline void IBMConversion::toLocal (float& to, const void* from) { toLocal (&to, from, 1); } inline void IBMConversion::toLocal (double& to, const void* from) { toLocal (&to, from, 1); } inline void IBMConversion::toLocal (unsigned char* to, const void* from, size_t nr) { CanonicalConversion::toLocal (to, from, nr); } inline void IBMConversion::toLocal (short* to, const void* from, size_t nr) { CanonicalConversion::toLocal (to, from, nr); } inline void IBMConversion::toLocal (unsigned short* to, const void* from, size_t nr) { CanonicalConversion::toLocal (to, from, nr); } inline void IBMConversion::toLocal (int* to, const void* from, size_t nr) { CanonicalConversion::toLocal (to, from, nr); } inline void IBMConversion::toLocal (unsigned int* to, const void* from, size_t nr) { CanonicalConversion::toLocal (to, from, nr); } inline void IBMConversion::fromLocal (void* to, unsigned char from) { CanonicalConversion::fromLocal (to, from); } inline void IBMConversion::fromLocal (void* to, short from) { CanonicalConversion::fromLocal (to, from); } inline void IBMConversion::fromLocal (void* to, unsigned short from) { CanonicalConversion::fromLocal (to, from); } inline void IBMConversion::fromLocal (void* to, int from) { CanonicalConversion::fromLocal (to, from); } inline void IBMConversion::fromLocal (void* to, unsigned int from) { CanonicalConversion::fromLocal (to, from); } inline void IBMConversion::fromLocal (void* to, Int64 from) { #if defined(AIPS_LITTLE_ENDIAN) CanonicalConversion::reverse4 (to, &from); #else CanonicalConversion::move4 (to, ((char*)&from)+sizeof(Int64)-4); #endif } inline void IBMConversion::fromLocal (void* to, uInt64 from) { #if defined(AIPS_LITTLE_ENDIAN) CanonicalConversion::reverse4 (to, &from); #else CanonicalConversion::move4 (to,((char*)&from)+sizeof(uInt64)-4); #endif } inline void IBMConversion::fromLocal (void* to, float from) { fromLocal (to, &from, 1); } inline void IBMConversion::fromLocal (void* to, double from) { fromLocal (to, &from, 1); } inline void IBMConversion::fromLocal (void* to, const unsigned char* from, size_t nr) { CanonicalConversion::fromLocal (to, from, nr); } inline void IBMConversion::fromLocal (void* to, const short* from, size_t nr) { CanonicalConversion::fromLocal (to, from, nr); } inline void IBMConversion::fromLocal (void* to, const unsigned short* from, size_t nr) { CanonicalConversion::fromLocal (to, from, nr); } inline void IBMConversion::fromLocal (void* to, const int* from, size_t nr) { CanonicalConversion::fromLocal (to, from, nr); } inline void IBMConversion::fromLocal (void* to, const unsigned int* from, size_t nr) { CanonicalConversion::fromLocal (to, from, nr); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/OS/IBMDataConversion.cc000066400000000000000000000243041476623553700203350ustar00rootroot00000000000000//# IBMDataConversion.cc: A class with virtual functions to convert IBM format //# Copyright (C) 1996,1997,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN IBMDataConversion::~IBMDataConversion() {} size_t IBMDataConversion::toLocal (char& to, const void* from) const { IBMConversion::toLocal (to, from); return SIZE_IBM_CHAR; } size_t IBMDataConversion::toLocal (unsigned char& to, const void* from) const { IBMConversion::toLocal (to, from); return SIZE_IBM_UCHAR; } size_t IBMDataConversion::toLocal (short& to, const void* from) const { IBMConversion::toLocal (to, from); return SIZE_IBM_SHORT; } size_t IBMDataConversion::toLocal (unsigned short& to, const void* from) const { IBMConversion::toLocal (to, from); return SIZE_IBM_USHORT; } size_t IBMDataConversion::toLocal (int& to, const void* from) const { IBMConversion::toLocal (to, from); return SIZE_IBM_INT; } size_t IBMDataConversion::toLocal (unsigned int& to, const void* from) const { IBMConversion::toLocal (to, from); return SIZE_IBM_UINT; } size_t IBMDataConversion::toLocal (Int64& to, const void* from) const { IBMConversion::toLocal (to, from); return SIZE_IBM_INT64; } size_t IBMDataConversion::toLocal (uInt64& to, const void* from) const { IBMConversion::toLocal (to, from); return SIZE_IBM_UINT64; } size_t IBMDataConversion::toLocal (float& to, const void* from) const { IBMConversion::toLocal (to, from); return SIZE_IBM_FLOAT; } size_t IBMDataConversion::toLocal (double& to, const void* from) const { IBMConversion::toLocal (to, from); return SIZE_IBM_DOUBLE; } size_t IBMDataConversion::toLocal (char* to, const void* from, size_t nr) const { IBMConversion::toLocal (to, from, nr); return nr*SIZE_IBM_CHAR; } size_t IBMDataConversion::toLocal (unsigned char* to, const void* from, size_t nr) const { IBMConversion::toLocal (to, from, nr); return nr*SIZE_IBM_UCHAR; } size_t IBMDataConversion::toLocal (short* to, const void* from, size_t nr) const { IBMConversion::toLocal (to, from, nr); return nr*SIZE_IBM_SHORT; } size_t IBMDataConversion::toLocal (unsigned short* to, const void* from, size_t nr) const { IBMConversion::toLocal (to, from, nr); return nr*SIZE_IBM_USHORT; } size_t IBMDataConversion::toLocal (int* to, const void* from, size_t nr) const { IBMConversion::toLocal (to, from, nr); return nr*SIZE_IBM_INT; } size_t IBMDataConversion::toLocal (unsigned int* to, const void* from, size_t nr) const { IBMConversion::toLocal (to, from, nr); return nr*SIZE_IBM_UINT; } size_t IBMDataConversion::toLocal (Int64* to, const void* from, size_t nr) const { IBMConversion::toLocal (to, from, nr); return nr*SIZE_IBM_INT64; } size_t IBMDataConversion::toLocal (uInt64* to, const void* from, size_t nr) const { IBMConversion::toLocal (to, from, nr); return nr*SIZE_IBM_UINT64; } size_t IBMDataConversion::toLocal (float* to, const void* from, size_t nr) const { IBMConversion::toLocal (to, from, nr); return nr*SIZE_IBM_FLOAT; } size_t IBMDataConversion::toLocal (double* to, const void* from, size_t nr) const { IBMConversion::toLocal (to, from, nr); return nr*SIZE_IBM_DOUBLE; } size_t IBMDataConversion::fromLocal (void* to, char from) const { IBMConversion::fromLocal (to, from); return SIZE_IBM_CHAR; } size_t IBMDataConversion::fromLocal (void* to, unsigned char from) const { IBMConversion::fromLocal (to, from); return SIZE_IBM_UCHAR; } size_t IBMDataConversion::fromLocal (void* to, short from) const { IBMConversion::fromLocal (to, from); return SIZE_IBM_SHORT; } size_t IBMDataConversion::fromLocal (void* to, unsigned short from) const { IBMConversion::fromLocal (to, from); return SIZE_IBM_USHORT; } size_t IBMDataConversion::fromLocal (void* to, int from) const { IBMConversion::fromLocal (to, from); return SIZE_IBM_INT; } size_t IBMDataConversion::fromLocal (void* to, unsigned int from) const { IBMConversion::fromLocal (to, from); return SIZE_IBM_UINT; } size_t IBMDataConversion::fromLocal (void* to, Int64 from) const { IBMConversion::fromLocal (to, from); return SIZE_IBM_INT64; } size_t IBMDataConversion::fromLocal (void* to, uInt64 from) const { IBMConversion::fromLocal (to, from); return SIZE_IBM_UINT64; } size_t IBMDataConversion::fromLocal (void* to, float from) const { IBMConversion::fromLocal (to, from); return SIZE_IBM_FLOAT; } size_t IBMDataConversion::fromLocal (void* to, double from) const { IBMConversion::fromLocal (to, from); return SIZE_IBM_DOUBLE; } size_t IBMDataConversion::fromLocal (void* to, const char* from, size_t nr) const { IBMConversion::fromLocal (to, from, nr); return nr*SIZE_IBM_CHAR; } size_t IBMDataConversion::fromLocal (void* to, const unsigned char* from, size_t nr) const { IBMConversion::fromLocal (to, from, nr); return nr*SIZE_IBM_UCHAR; } size_t IBMDataConversion::fromLocal (void* to, const short* from, size_t nr) const { IBMConversion::fromLocal (to, from, nr); return nr*SIZE_IBM_SHORT; } size_t IBMDataConversion::fromLocal (void* to, const unsigned short* from, size_t nr) const { IBMConversion::fromLocal (to, from, nr); return nr*SIZE_IBM_USHORT; } size_t IBMDataConversion::fromLocal (void* to, const int* from, size_t nr) const { IBMConversion::fromLocal (to, from, nr); return nr*SIZE_IBM_INT; } size_t IBMDataConversion::fromLocal (void* to, const unsigned int* from, size_t nr) const { IBMConversion::fromLocal (to, from, nr); return nr*SIZE_IBM_UINT; } size_t IBMDataConversion::fromLocal (void* to, const Int64* from, size_t nr) const { IBMConversion::fromLocal (to, from, nr); return nr*SIZE_IBM_INT64; } size_t IBMDataConversion::fromLocal (void* to, const uInt64* from, size_t nr) const { IBMConversion::fromLocal (to, from, nr); return nr*SIZE_IBM_UINT64; } size_t IBMDataConversion::fromLocal (void* to, const float* from, size_t nr) const { IBMConversion::fromLocal (to, from, nr); return nr*SIZE_IBM_FLOAT; } size_t IBMDataConversion::fromLocal (void* to, const double* from, size_t nr) const { IBMConversion::fromLocal (to, from, nr); return nr*SIZE_IBM_DOUBLE; } Bool IBMDataConversion::canCopy (const char*) const { return False; } Bool IBMDataConversion::canCopy (const unsigned char*) const { if (sizeof(unsigned char) == SIZE_IBM_UCHAR) { return True; } return False; } Bool IBMDataConversion::canCopy (const short*) const { #if !defined(AIPS_LITTLE_ENDIAN) if (sizeof(short) == SIZE_IBM_SHORT) { return True; } #endif return False; } Bool IBMDataConversion::canCopy (const unsigned short*) const { #if !defined(AIPS_LITTLE_ENDIAN) if (sizeof(unsigned short) == SIZE_IBM_USHORT) { return True; } #endif return False; } Bool IBMDataConversion::canCopy (const int*) const { #if !defined(AIPS_LITTLE_ENDIAN) if (sizeof(int) == SIZE_IBM_INT) { return True; } #endif return False; } Bool IBMDataConversion::canCopy (const unsigned int*) const { #if !defined(AIPS_LITTLE_ENDIAN) if (sizeof(unsigned int) == SIZE_IBM_UINT) { return True; } #endif return False; } Bool IBMDataConversion::canCopy (const Int64*) const { #if !defined(AIPS_LITTLE_ENDIAN) if (sizeof(Int64) == SIZE_IBM_INT64) { return True; } #endif return False; } Bool IBMDataConversion::canCopy (const uInt64*) const { #if !defined(AIPS_LITTLE_ENDIAN) if (sizeof(uInt64) == SIZE_IBM_UINT64) { return True; } #endif return False; } Bool IBMDataConversion::canCopy (const float*) const { return False; } Bool IBMDataConversion::canCopy (const double*) const { return False; } unsigned int IBMDataConversion::externalSize (const char*) const { return SIZE_IBM_CHAR; } unsigned int IBMDataConversion::externalSize (const unsigned char*) const { return SIZE_IBM_UCHAR; } unsigned int IBMDataConversion::externalSize (const short*) const { return SIZE_IBM_SHORT; } unsigned int IBMDataConversion::externalSize (const unsigned short*) const { return SIZE_IBM_USHORT; } unsigned int IBMDataConversion::externalSize (const int*) const { return SIZE_IBM_INT; } unsigned int IBMDataConversion::externalSize (const unsigned int*) const { return SIZE_IBM_UINT; } unsigned int IBMDataConversion::externalSize (const Int64*) const { return SIZE_IBM_INT64; } unsigned int IBMDataConversion::externalSize (const uInt64*) const { return SIZE_IBM_UINT64; } unsigned int IBMDataConversion::externalSize (const float*) const { return SIZE_IBM_FLOAT; } unsigned int IBMDataConversion::externalSize (const double*) const { return SIZE_IBM_DOUBLE; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/OS/IBMDataConversion.h000066400000000000000000000207211476623553700201760ustar00rootroot00000000000000//# IBMDataConversion.h: A class with virtual functions to convert IBM format //# Copyright (C) 1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_IBMDATACONVERSION_H #define CASA_IBMDATACONVERSION_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A class with virtual functions to convert IBM format. // // // // // // This class is a specialization of the abstract base class // DataConversion. // It contains functions to convert data from/to the old IBM format // using the static functions in class // IBMConversion. // // // See example in class DataConversion. // // // This class is an addition to IBMConversion // to be able to use the conversion functions in a polymorphic way. // // //
        39. Support data type long double. // class IBMDataConversion : public DataConversion { public: // Construct the object. IBMDataConversion(); virtual ~IBMDataConversion(); // Convert one value from IBM format to local format. // The from and to buffer should not overlap. // // The char version converts from EBCDIC to ASCII, while the // unsigned char version is a simple copy. // // size_t toLocal (char& to, const void* from) const override; size_t toLocal (unsigned char& to, const void* from) const override; size_t toLocal (short& to, const void* from) const override; size_t toLocal (unsigned short& to, const void* from) const override; size_t toLocal (int& to, const void* from) const override; size_t toLocal (unsigned int& to, const void* from) const override; size_t toLocal (Int64& to, const void* from) const override; size_t toLocal (uInt64& to, const void* from) const override; size_t toLocal (float& to, const void* from) const override; size_t toLocal (double& to, const void* from) const override; // // Convert nr values from IBM format to local format. // The from and to buffer should not overlap. // // The char version converts from EBCDIC to ASCII, while the // unsigned char version is a simple copy. // // size_t toLocal (char* to, const void* from, size_t nr) const override; size_t toLocal (unsigned char* to, const void* from, size_t nr) const override; size_t toLocal (short* to, const void* from, size_t nr) const override; size_t toLocal (unsigned short* to, const void* from, size_t nr) const override; size_t toLocal (int* to, const void* from, size_t nr) const override; size_t toLocal (unsigned int* to, const void* from, size_t nr) const override; size_t toLocal (Int64* to, const void* from, size_t nr) const override; size_t toLocal (uInt64* to, const void* from, size_t nr) const override; size_t toLocal (float* to, const void* from, size_t nr) const override; size_t toLocal (double* to, const void* from, size_t nr) const override; // // Convert one value from local format to IBM format. // The from and to buffer should not overlap. // // The char version converts from ASCII to EBCDIC, while the // unsigned char version is a simple copy. // // size_t fromLocal (void* to, char from) const override; size_t fromLocal (void* to, unsigned char from) const override; size_t fromLocal (void* to, short from) const override; size_t fromLocal (void* to, unsigned short from) const override; size_t fromLocal (void* to, int from) const override; size_t fromLocal (void* to, unsigned int from) const override; size_t fromLocal (void* to, Int64 from) const override; size_t fromLocal (void* to, uInt64 from) const override; size_t fromLocal (void* to, float from) const override; size_t fromLocal (void* to, double from) const override; // // Convert nr values from local format to IBM format. // The from and to buffer should not overlap. // // The char version converts from ASCII to EBCDIC, while the // unsigned char version is a simple copy. // // size_t fromLocal (void* to, const char* from, size_t nr) const override; size_t fromLocal (void* to, const unsigned char* from, size_t nr) const override; size_t fromLocal (void* to, const short* from, size_t nr) const override; size_t fromLocal (void* to, const unsigned short* from, size_t nr) const override; size_t fromLocal (void* to, const int* from, size_t nr) const override; size_t fromLocal (void* to, const unsigned int* from, size_t nr) const override; size_t fromLocal (void* to, const Int64* from, size_t nr) const override; size_t fromLocal (void* to, const uInt64* from, size_t nr) const override; size_t fromLocal (void* to, const float* from, size_t nr) const override; size_t fromLocal (void* to, const double* from, size_t nr) const override; // // Determine if the data for a data type can be simply copied, thus // if no conversion is needed. // Bool canCopy (const char*) const override; Bool canCopy (const unsigned char*) const override; Bool canCopy (const short*) const override; Bool canCopy (const unsigned short*) const override; Bool canCopy (const int*) const override; Bool canCopy (const unsigned int*) const override; Bool canCopy (const Int64*) const override; Bool canCopy (const uInt64*) const override; Bool canCopy (const float*) const override; Bool canCopy (const double*) const override; // // Get the external size of the data type. // unsigned int externalSize (const char*) const override; unsigned int externalSize (const unsigned char*) const override; unsigned int externalSize (const short*) const override; unsigned int externalSize (const unsigned short*) const override; unsigned int externalSize (const int*) const override; unsigned int externalSize (const unsigned int*) const override; unsigned int externalSize (const Int64*) const override; unsigned int externalSize (const uInt64*) const override; unsigned int externalSize (const float*) const override; unsigned int externalSize (const double*) const override; // }; inline IBMDataConversion::IBMDataConversion() {} } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/OS/LECanonicalConversion.cc000066400000000000000000000161531476623553700212470ustar00rootroot00000000000000//# LECanonicalConversion.cc: A class with static functions to convert little endian canonical format //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN size_t LECanonicalConversion::toLocalChar (void* to, const void* from, size_t nr) { assert (sizeof(char) == SIZE_LECAN_CHAR); memcpy (to, from, nr); return nr * SIZE_LECAN_CHAR; } size_t LECanonicalConversion::fromLocalChar (void* to, const void* from, size_t nr) { assert (sizeof(char) == SIZE_LECAN_CHAR); memcpy (to, from, nr); return nr * SIZE_LECAN_CHAR; } void* LECanonicalConversion::byteToLocalChar (void* to, const void* from, size_t nrbytes) { assert (sizeof(char) == SIZE_LECAN_CHAR); memcpy (to, from, nrbytes); return to; } void* LECanonicalConversion::byteFromLocalChar (void* to, const void* from, size_t nrbytes) { assert (sizeof(char) == SIZE_LECAN_CHAR); memcpy (to, from, nrbytes); return to; } Conversion::ByteFunction* LECanonicalConversion::getByteToLocal (const char*) { assert (sizeof(char) == SIZE_LECAN_CHAR); return Conversion::getmemcpy(); } Conversion::ByteFunction* LECanonicalConversion::getByteFromLocal (const char*) { assert (sizeof(char) == SIZE_LECAN_CHAR); return Conversion::getmemcpy(); } size_t LECanonicalConversion::toLocalUChar (void* to, const void* from, size_t nr) { assert (sizeof(unsigned char) == SIZE_LECAN_UCHAR); memcpy (to, from, nr); return nr * SIZE_LECAN_UCHAR; } size_t LECanonicalConversion::fromLocalUChar (void* to, const void* from, size_t nr) { assert (sizeof(unsigned char) == SIZE_LECAN_UCHAR); memcpy (to, from, nr); return nr * SIZE_LECAN_UCHAR; } void* LECanonicalConversion::byteToLocalUChar (void* to, const void* from, size_t nrbytes) { assert (sizeof(unsigned char) == SIZE_LECAN_UCHAR); memcpy (to, from, nrbytes); return to; } void* LECanonicalConversion::byteFromLocalUChar (void* to, const void* from, size_t nrbytes) { assert (sizeof(unsigned char) == SIZE_LECAN_UCHAR); memcpy (to, from, nrbytes); return to; } Conversion::ByteFunction* LECanonicalConversion::getByteToLocal (const unsigned char*) { assert (sizeof(unsigned char) == SIZE_LECAN_UCHAR); return Conversion::getmemcpy(); } Conversion::ByteFunction* LECanonicalConversion::getByteFromLocal (const unsigned char*) { assert (sizeof(unsigned char) == SIZE_LECAN_UCHAR); return Conversion::getmemcpy(); } #define LECANONICALCONVERSION_DO(CONVERT,SIZE,TOLOCAL,FROMLOCAL,BYTETO,BYTEFROM,T) \ size_t LECanonicalConversion::TOLOCAL (void* to, const void* from, \ size_t nr) \ { \ /* Use memcpy if no conversion is needed. */ \ if (CONVERT == 0) { \ assert (sizeof(T) == SIZE); \ memcpy (to, from, nr*SIZE); \ }else{ \ const char* data = (const char*)from; \ T* dest = (T*)to; \ T* last = dest + nr; \ while (dest < last) { \ toLocal (*dest++, data); \ data += SIZE; \ } \ } \ return nr*SIZE; \ } \ size_t LECanonicalConversion::FROMLOCAL (void* to, const void* from, \ size_t nr) \ { \ /* Use memcpy if no conversion is needed. */ \ if (CONVERT == 0) { \ assert (sizeof(T) == SIZE); \ memcpy (to, from, nr*SIZE); \ }else{ \ char* data = (char*)to; \ const T* src = (const T*)from; \ const T* last = src + nr; \ while (src < last) { \ fromLocal (data, *src++); \ data += SIZE; \ } \ } \ return nr*SIZE; \ } \ void* LECanonicalConversion::BYTETO (void* to, const void* from, \ size_t nrbytes) \ { \ TOLOCAL (to, from, nrbytes / sizeof(T)); \ return to; \ } \ void* LECanonicalConversion::BYTEFROM (void* to, const void* from, \ size_t nrbytes) \ { \ FROMLOCAL (to, from, nrbytes / sizeof(T)); \ return to; \ } \ Conversion::ByteFunction* LECanonicalConversion::getByteToLocal (const T*) \ { \ if (CONVERT == 0) { \ assert (sizeof(T) == SIZE); \ return Conversion::getmemcpy(); \ } \ return BYTETO; \ } \ Conversion::ByteFunction* LECanonicalConversion::getByteFromLocal (const T*) \ { \ if (CONVERT == 0) { \ assert (sizeof(T) == SIZE); \ return Conversion::getmemcpy(); \ } \ return BYTEFROM; \ } LECANONICALCONVERSION_DO (CONVERT_LECAN_SHORT, SIZE_LECAN_SHORT, toLocalShort, fromLocalShort, byteToLocalShort, byteFromLocalShort, short) LECANONICALCONVERSION_DO (CONVERT_LECAN_USHORT, SIZE_LECAN_USHORT, toLocalUShort, fromLocalUShort, byteToLocalUShort, byteFromLocalUShort, unsigned short) LECANONICALCONVERSION_DO (CONVERT_LECAN_INT, SIZE_LECAN_INT, toLocalInt, fromLocalInt, byteToLocalInt, byteFromLocalInt, int) LECANONICALCONVERSION_DO (CONVERT_LECAN_UINT, SIZE_LECAN_UINT, toLocalUInt, fromLocalUInt, byteToLocalUInt, byteFromLocalUInt, unsigned int) LECANONICALCONVERSION_DO (CONVERT_LECAN_INT64, SIZE_LECAN_INT64, toLocalInt64, fromLocalInt64, byteToLocalInt64, byteFromLocalInt64, Int64) LECANONICALCONVERSION_DO (CONVERT_LECAN_UINT64, SIZE_LECAN_UINT64, toLocalUInt64, fromLocalUInt64, byteToLocalUInt64, byteFromLocalUInt64, uInt64) LECANONICALCONVERSION_DO (CONVERT_LECAN_FLOAT, SIZE_LECAN_FLOAT, toLocalFloat, fromLocalFloat, byteToLocalFloat, byteFromLocalFloat, float) LECANONICALCONVERSION_DO (CONVERT_LECAN_DOUBLE, SIZE_LECAN_DOUBLE, toLocalDouble, fromLocalDouble, byteToLocalDouble, byteFromLocalDouble, double) } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/OS/LECanonicalConversion.h000066400000000000000000001060741476623553700211130ustar00rootroot00000000000000//# LECanonicalConversion.h: A class with static functions to convert little endian canonical format //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_LECANONICALCONVERSION_H #define CASA_LECANONICALCONVERSION_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Define the little endian canonical sizes of the built-in data types. // These are the same for all machine architectures. #define SIZE_LECAN_CHAR 1 #define SIZE_LECAN_UCHAR 1 #define SIZE_LECAN_SHORT 2 #define SIZE_LECAN_USHORT 2 #define SIZE_LECAN_INT 4 #define SIZE_LECAN_UINT 4 #define SIZE_LECAN_INT64 8 #define SIZE_LECAN_UINT64 8 #define SIZE_LECAN_FLOAT 4 #define SIZE_LECAN_DOUBLE 8 //#//define SIZE_LECAN_LDOUBLE 16 // Define for each data format if a conversion is needed from the // local format to the little endian canonical format (or vice-versa). // This allows for optimizations in, for example, AipsIO. // The canonical format is ASCII for strings, IEEE for floating point // and 2-complement for integers (all most significant bit last) // with the lengths as shown above. // The function checkConvert() can be used to check if the flags are // set correctly. // Conversion is needed for big endian architectures (like SUN), // because the bytes have to be swapped (thus not for data with length 1). #define CONVERT_LECAN_CHAR 0 #define CONVERT_LECAN_UCHAR 0 #if !defined(AIPS_LITTLE_ENDIAN) # define CONVERT_LECAN_SHORT 1 # define CONVERT_LECAN_USHORT 1 # define CONVERT_LECAN_INT 1 # define CONVERT_LECAN_UINT 1 # define CONVERT_LECAN_INT64 1 # define CONVERT_LECAN_UINT64 1 # define CONVERT_LECAN_FLOAT 1 # define CONVERT_LECAN_DOUBLE 1 //#//# define CONVERT_LECAN_LDOUBLE 1 #else // Conversion is not needed for IEEE data. // Change the definitions below if new architectures are being used. # define CONVERT_LECAN_SHORT 0 # define CONVERT_LECAN_USHORT 0 # define CONVERT_LECAN_INT 0 # define CONVERT_LECAN_UINT 0 # define CONVERT_LECAN_INT64 0 # define CONVERT_LECAN_UINT64 0 # define CONVERT_LECAN_FLOAT 0 # define CONVERT_LECAN_DOUBLE 0 // LDOUBLE is 8 bytes on SUN, but 16 bytes canonical. //#//# define CONVERT_LECAN_LDOUBLE 1 #endif // // A class with static functions to convert little endian canonical format // // // // // // This class consists of several static functions to convert // data from local (=native) format to a little endian canonical format. // The canonical length of each data type is: //
          - Bool: 1 bit //
          - char: 1 byte //
          - short: 2 bytes //
          - int: 4 bytes //
          - Int64: 8 bytes //
          - float: 4 bytes //
          - double: 8 bytes //
          This canonical format is little-endian IEEE format, so on PCs // the conversion is only a copy operation. On e.g. SUNs // however, it involves a byte swap to convert from little // endian to big endian. //

          // The class also contains conversion functions making it possible to // specify the number of bytes (in local format) instead of the number // of values. These functions are included to make it possible to have // the same signature as memcpy. //

          // The current implementation of this class works on big- and little-endian // machines using IEEE format. When using on other machines (e.g. VAX) // the toLocal and fromLocal functions have to be changed. //

          // Note that no functions are provided to handle Bools. Instead class // Conversion provides functions to // convert Bools to/from bits. // // // // void someFunction (const uInt* data, uInt nrval) // { // char* buffer = new char[nrval*LECanonicalConversion::canonicalSize(data)]; // LECanonicalConversion::fromLocal (buffer, data, nrval); // .... // delete [] buffer; // } // // // // Casacore data will often be stored in a canonical format. // To read these data conversion functions are needed. // However, these functions do not use any other Casacore classes, // so they can easily be used in any other software system. // // //

        40. Support data type long double. // class LECanonicalConversion { public: // Convert one value from canonical format to local format. // The from and to buffer should not overlap. // static size_t toLocal (char& to, const void* from); static size_t toLocal (unsigned char& to, const void* from); static size_t toLocal (short& to, const void* from); static size_t toLocal (unsigned short& to, const void* from); static size_t toLocal (int& to, const void* from); static size_t toLocal (unsigned int& to, const void* from); static size_t toLocal (Int64& to, const void* from); static size_t toLocal (uInt64& to, const void* from); static size_t toLocal (float& to, const void* from); static size_t toLocal (double& to, const void* from); // // Convert one value from local format to canonical format. // The from and to buffer should not overlap. // static size_t fromLocal (void* to, const char& from); static size_t fromLocal (void* to, const unsigned char& from); static size_t fromLocal (void* to, const short& from); static size_t fromLocal (void* to, const unsigned short& from); static size_t fromLocal (void* to, const int& from); static size_t fromLocal (void* to, const unsigned int& from); static size_t fromLocal (void* to, const Int64& from); static size_t fromLocal (void* to, const uInt64& from); static size_t fromLocal (void* to, const float& from); static size_t fromLocal (void* to, const double& from); // // Convert nr values from canonical format to local format. // The from and to buffer should not overlap. // static size_t toLocal (char* to, const void* from, size_t nr); static size_t toLocal (unsigned char* to, const void* from, size_t nr); static size_t toLocal (short* to, const void* from, size_t nr); static size_t toLocal (unsigned short* to, const void* from, size_t nr); static size_t toLocal (int* to, const void* from, size_t nr); static size_t toLocal (unsigned int* to, const void* from, size_t nr); static size_t toLocal (Int64* to, const void* from, size_t nr); static size_t toLocal (uInt64* to, const void* from, size_t nr); static size_t toLocal (float* to, const void* from, size_t nr); static size_t toLocal (double* to, const void* from, size_t nr); // // Convert nr values from local format to canonical format. // The from and to buffer should not overlap. // static size_t fromLocal (void* to, const char* from, size_t nr); static size_t fromLocal (void* to, const unsigned char* from, size_t nr); static size_t fromLocal (void* to, const short* from, size_t nr); static size_t fromLocal (void* to, const unsigned short* from, size_t nr); static size_t fromLocal (void* to, const int* from, size_t nr); static size_t fromLocal (void* to, const unsigned int* from, size_t nr); static size_t fromLocal (void* to, const Int64* from, size_t nr); static size_t fromLocal (void* to, const uInt64* from, size_t nr); static size_t fromLocal (void* to, const float* from, size_t nr); static size_t fromLocal (void* to, const double* from, size_t nr); // // Convert nr values from canonical format to local format. // The from and to buffer should not overlap. // static size_t toLocalChar (void* to, const void* from, size_t nr); static size_t toLocalUChar (void* to, const void* from, size_t nr); static size_t toLocalShort (void* to, const void* from, size_t nr); static size_t toLocalUShort (void* to, const void* from, size_t nr); static size_t toLocalInt (void* to, const void* from, size_t nr); static size_t toLocalUInt (void* to, const void* from, size_t nr); static size_t toLocalInt64 (void* to, const void* from, size_t nr); static size_t toLocalUInt64 (void* to, const void* from, size_t nr); static size_t toLocalFloat (void* to, const void* from, size_t nr); static size_t toLocalDouble (void* to, const void* from, size_t nr); // // Convert nr values from local format to canonical format. // The from and to buffer should not overlap. // static size_t fromLocalChar (void* to, const void* from, size_t nr); static size_t fromLocalUChar (void* to, const void* from, size_t nr); static size_t fromLocalShort (void* to, const void* from, size_t nr); static size_t fromLocalUShort (void* to, const void* from, size_t nr); static size_t fromLocalInt (void* to, const void* from, size_t nr); static size_t fromLocalUInt (void* to, const void* from, size_t nr); static size_t fromLocalInt64 (void* to, const void* from, size_t nr); static size_t fromLocalUInt64 (void* to, const void* from, size_t nr); static size_t fromLocalFloat (void* to, const void* from, size_t nr); static size_t fromLocalDouble (void* to, const void* from, size_t nr); // // Convert values from canonical format to local format. // The from and to buffer should not overlap. // The number of values involved is determined from the argument // nrbytes, which gives the number of bytes in local format. // The signature of this function is the same as memcpy, so // that memcpy can directly be used if no conversion is needed. // static void* byteToLocalChar (void* to, const void* from, size_t nrbytes); static void* byteToLocalUChar (void* to, const void* from, size_t nrbytes); static void* byteToLocalShort (void* to, const void* from, size_t nrbytes); static void* byteToLocalUShort (void* to, const void* from, size_t nrbytes); static void* byteToLocalInt (void* to, const void* from, size_t nrbytes); static void* byteToLocalUInt (void* to, const void* from, size_t nrbytes); static void* byteToLocalInt64 (void* to, const void* from, size_t nrbytes); static void* byteToLocalUInt64 (void* to, const void* from, size_t nrbytes); static void* byteToLocalFloat (void* to, const void* from, size_t nrbytes); static void* byteToLocalDouble (void* to, const void* from, size_t nrbytes); // // Convert values from local format to canonical format. // The from and to buffer should not overlap. // The number of values involved is determined from the argument // nrbytes, which gives the number of bytes in local format. // The signature of this function is the same as memcpy, so // that memcpy can directly be used if no conversion is needed. // static void* byteFromLocalChar (void* to, const void* from, size_t nrbytes); static void* byteFromLocalUChar (void* to, const void* from, size_t nrbytes); static void* byteFromLocalShort (void* to, const void* from, size_t nrbytes); static void* byteFromLocalUShort (void* to, const void* from, size_t nrbytes); static void* byteFromLocalInt (void* to, const void* from, size_t nrbytes); static void* byteFromLocalUInt (void* to, const void* from, size_t nrbytes); static void* byteFromLocalInt64 (void* to, const void* from, size_t nrbytes); static void* byteFromLocalUInt64 (void* to, const void* from, size_t nrbytes); static void* byteFromLocalFloat (void* to, const void* from, size_t nrbytes); static void* byteFromLocalDouble (void* to, const void* from, size_t nrbytes); // // Get the value conversion function for the given type. // static Conversion::ValueFunction* getToLocal (const char*); static Conversion::ValueFunction* getToLocal (const unsigned char*); static Conversion::ValueFunction* getToLocal (const short*); static Conversion::ValueFunction* getToLocal (const unsigned short*); static Conversion::ValueFunction* getToLocal (const int*); static Conversion::ValueFunction* getToLocal (const unsigned int*); static Conversion::ValueFunction* getToLocal (const Int64*); static Conversion::ValueFunction* getToLocal (const uInt64*); static Conversion::ValueFunction* getToLocal (const float*); static Conversion::ValueFunction* getToLocal (const double*); static Conversion::ValueFunction* getFromLocal (const char*); static Conversion::ValueFunction* getFromLocal (const unsigned char*); static Conversion::ValueFunction* getFromLocal (const short*); static Conversion::ValueFunction* getFromLocal (const unsigned short*); static Conversion::ValueFunction* getFromLocal (const int*); static Conversion::ValueFunction* getFromLocal (const unsigned int*); static Conversion::ValueFunction* getFromLocal (const Int64*); static Conversion::ValueFunction* getFromLocal (const uInt64*); static Conversion::ValueFunction* getFromLocal (const float*); static Conversion::ValueFunction* getFromLocal (const double*); // // Get the byte conversion function for the given type. // The function memcpy is returned when a conversion // is not needed. // static Conversion::ByteFunction* getByteToLocal (const char*); static Conversion::ByteFunction* getByteToLocal (const unsigned char*); static Conversion::ByteFunction* getByteToLocal (const short*); static Conversion::ByteFunction* getByteToLocal (const unsigned short*); static Conversion::ByteFunction* getByteToLocal (const int*); static Conversion::ByteFunction* getByteToLocal (const unsigned int*); static Conversion::ByteFunction* getByteToLocal (const Int64*); static Conversion::ByteFunction* getByteToLocal (const uInt64*); static Conversion::ByteFunction* getByteToLocal (const float*); static Conversion::ByteFunction* getByteToLocal (const double*); static Conversion::ByteFunction* getByteFromLocal (const char*); static Conversion::ByteFunction* getByteFromLocal (const unsigned char*); static Conversion::ByteFunction* getByteFromLocal (const short*); static Conversion::ByteFunction* getByteFromLocal (const unsigned short*); static Conversion::ByteFunction* getByteFromLocal (const int*); static Conversion::ByteFunction* getByteFromLocal (const unsigned int*); static Conversion::ByteFunction* getByteFromLocal (const Int64*); static Conversion::ByteFunction* getByteFromLocal (const uInt64*); static Conversion::ByteFunction* getByteFromLocal (const float*); static Conversion::ByteFunction* getByteFromLocal (const double*); // // Return the canonical length for the various data types. // static unsigned int canonicalSize (const char*); static unsigned int canonicalSize (const unsigned char*); static unsigned int canonicalSize (const short*); static unsigned int canonicalSize (const unsigned short*); static unsigned int canonicalSize (const int*); static unsigned int canonicalSize (const unsigned int*); static unsigned int canonicalSize (const Int64*); static unsigned int canonicalSize (const uInt64*); static unsigned int canonicalSize (const float*); static unsigned int canonicalSize (const double*); //#//static unsigned int canonicalSize (const long double*); // // Reverse 2 bytes. static void reverse2 (void* to, const void* from); // Reverse 4 bytes. static void reverse4 (void* to, const void* from); // Reverse 8 bytes. static void reverse8 (void* to, const void* from); // Move 2 bytes. static void move2 (void* to, const void* from); // Move 4 bytes. static void move4 (void* to, const void* from); // Move 8 bytes. static void move8 (void* to, const void* from); private: // This class should not be constructed // (so declare the constructor private). LECanonicalConversion(); }; inline void LECanonicalConversion::reverse2 (void* to, const void* from) { unsigned short x, xsw; memcpy(&x, from, 2); xsw = ((x & 0xffu) << 8u) | (x >> 8u); memcpy(to, &xsw, 2); } inline void LECanonicalConversion::reverse4 (void* to, const void* from) { unsigned int x, xsw; memcpy(&x, from, 4); #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3) xsw = __builtin_bswap32(x); #else xsw = ((x & 0xffu) << 24u) | ((x & 0xff00u) << 8u) | ((x & 0xff0000u) >> 8u) | (x >> 24u); #endif memcpy(to, &xsw, 4); } inline void LECanonicalConversion::reverse8 (void* to, const void* from) { uInt64 x, xsw; memcpy(&x, from, 8); #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3) xsw = __builtin_bswap64(x); #else xsw = ((x & 0xffULL) << 56ULL) | ((x & 0xff00ULL) << 40ULL) | ((x & 0xff0000ULL) << 24ULL) | ((x & 0xff000000ULL) << 8ULL) | ((x & 0xff00000000ULL) >> 8ULL) | ((x & 0xff0000000000ULL) >> 24ULL) | ((x & 0xff000000000000ULL) >> 40ULL) | ( x >> 56ULL); #endif memcpy(to, &xsw, 8); } inline void LECanonicalConversion::move2 (void* to, const void* from) { memcpy(to, from, 2); } inline void LECanonicalConversion::move4 (void* to, const void* from) { memcpy(to, from, 4); } inline void LECanonicalConversion::move8 (void* to, const void* from) { /* memcpy is overlap save if size fits into a register */ if (sizeof(to) < 8) { memmove(to, from, 8); } else { memcpy(to, from, 8); } } inline size_t LECanonicalConversion::toLocal (char& to, const void* from) { to = *(char*)from; return SIZE_LECAN_CHAR; } inline size_t LECanonicalConversion::toLocal (unsigned char& to, const void* from) { to = *(unsigned char*)from; return SIZE_LECAN_UCHAR; } inline size_t LECanonicalConversion::toLocal (short& to, const void* from) { if (sizeof(short) != 2) { if (((signed char*)from)[0] < 0) { to = -1; }else{ to = 0; } } #if !defined(AIPS_LITTLE_ENDIAN) reverse2 (&to, from); #else move2 (((char*)&to)+sizeof(short)-2, from); #endif return SIZE_LECAN_SHORT; } inline size_t LECanonicalConversion::toLocal (unsigned short& to, const void* from) { if (sizeof(unsigned short) != 2) { to = 0; } #if !defined(AIPS_LITTLE_ENDIAN) reverse2 (&to, from); #else move2 (((char*)&to)+sizeof(unsigned short)-2, from); #endif return SIZE_LECAN_USHORT; } inline size_t LECanonicalConversion::toLocal (int& to, const void* from) { if (sizeof(int) != 4) { if (((signed char*)from)[0] < 0) { to = -1; }else{ to = 0; } } #if !defined(AIPS_LITTLE_ENDIAN) reverse4 (&to, from); #else move4 (((char*)&to)+sizeof(int)-4, from); #endif return SIZE_LECAN_INT; } inline size_t LECanonicalConversion::toLocal (unsigned int& to, const void* from) { if (sizeof(unsigned int) != 4) { to = 0; } #if !defined(AIPS_LITTLE_ENDIAN) reverse4 (&to, from); #else move4 (((char*)&to)+sizeof(unsigned int)-4, from); #endif return SIZE_LECAN_UINT; } inline size_t LECanonicalConversion::toLocal (Int64& to, const void* from) { if (sizeof(Int64) != 8) { if (((signed char*)from)[0] < 0) { to = -1; }else{ to = 0; } } #if !defined(AIPS_LITTLE_ENDIAN) reverse8 (&to, from); #else move8 (((char*)&to)+sizeof(Int64)-8, from); #endif return SIZE_LECAN_INT64; } inline size_t LECanonicalConversion::toLocal (uInt64& to, const void* from) { if (sizeof(uInt64) != 8) { to = 0; } #if !defined(AIPS_LITTLE_ENDIAN) reverse8 (&to, from); #else move8 (((char*)&to)+sizeof(uInt64)-8, from); #endif return SIZE_LECAN_UINT64; } inline size_t LECanonicalConversion::toLocal (float& to, const void* from) { #if !defined(AIPS_LITTLE_ENDIAN) reverse4 (((char*)&to)+sizeof(float)-4, from); #else move4 (&to, from); #endif return SIZE_LECAN_FLOAT; } inline size_t LECanonicalConversion::toLocal (double& to, const void* from) { #if !defined(AIPS_LITTLE_ENDIAN) reverse8 (((char*)&to)+sizeof(double)-8, from); #else move8 (&to, from); #endif return SIZE_LECAN_DOUBLE; } inline size_t LECanonicalConversion::fromLocal (void* to, const char& from) { *(char*)to = from; return SIZE_LECAN_CHAR; } inline size_t LECanonicalConversion::fromLocal (void* to, const unsigned char& from) { *(unsigned char*)to = from; return SIZE_LECAN_UCHAR; } inline size_t LECanonicalConversion::fromLocal (void* to, const short& from) { #if !defined(AIPS_LITTLE_ENDIAN) reverse2 (to, &from); #else move2 (to, ((char*)&from)+sizeof(short)-2); #endif return SIZE_LECAN_SHORT; } inline size_t LECanonicalConversion::fromLocal (void* to, const unsigned short& from) { #if !defined(AIPS_LITTLE_ENDIAN) reverse2 (to, &from); #else move2 (to, ((char*)&from)+sizeof(unsigned short)-2); #endif return SIZE_LECAN_USHORT; } inline size_t LECanonicalConversion::fromLocal (void* to, const int& from) { #if !defined(AIPS_LITTLE_ENDIAN) reverse4 (to, &from); #else move4 (to, ((char*)&from)+sizeof(int)-4); #endif return SIZE_LECAN_INT; } inline size_t LECanonicalConversion::fromLocal (void* to, const unsigned int& from) { #if !defined(AIPS_LITTLE_ENDIAN) reverse4 (to, &from); #else move4 (to, ((char*)&from)+sizeof(unsigned int)-4); #endif return SIZE_LECAN_UINT; } inline size_t LECanonicalConversion::fromLocal (void* to, const Int64& from) { #if !defined(AIPS_LITTLE_ENDIAN) reverse8 (to, &from); #else move8 (to, ((char*)&from)+sizeof(Int64)-8); #endif return SIZE_LECAN_INT64; } inline size_t LECanonicalConversion::fromLocal (void* to, const uInt64& from) { #if !defined(AIPS_LITTLE_ENDIAN) reverse8 (to, &from); #else move8 (to, ((char*)&from)+sizeof(uInt64)-8); #endif return SIZE_LECAN_UINT64; } inline size_t LECanonicalConversion::fromLocal (void* to, const float& from) { #if !defined(AIPS_LITTLE_ENDIAN) reverse4 (to, &from); #else move4 (to, &from); #endif return SIZE_LECAN_FLOAT; } inline size_t LECanonicalConversion::fromLocal (void* to, const double& from) { #if !defined(AIPS_LITTLE_ENDIAN) reverse8 (to, &from); #else move8 (to, &from); #endif return SIZE_LECAN_FLOAT; } inline size_t LECanonicalConversion::toLocal (char* to, const void* from, size_t nr) { return toLocalChar (to, from, nr); } inline size_t LECanonicalConversion::toLocal (unsigned char* to, const void* from, size_t nr) { return toLocalUChar (to, from, nr); } inline size_t LECanonicalConversion::toLocal (short* to, const void* from, size_t nr) { return toLocalShort (to, from, nr); } inline size_t LECanonicalConversion::toLocal (unsigned short* to, const void* from, size_t nr) { return toLocalUShort (to, from, nr); } inline size_t LECanonicalConversion::toLocal (int* to, const void* from, size_t nr) { return toLocalInt (to, from, nr); } inline size_t LECanonicalConversion::toLocal (unsigned int* to, const void* from, size_t nr) { return toLocalUInt (to, from, nr); } inline size_t LECanonicalConversion::toLocal (Int64* to, const void* from, size_t nr) { return toLocalInt64 (to, from, nr); } inline size_t LECanonicalConversion::toLocal (uInt64* to, const void* from, size_t nr) { return toLocalUInt64 (to, from, nr); } inline size_t LECanonicalConversion::toLocal (float* to, const void* from, size_t nr) { return toLocalFloat (to, from, nr); } inline size_t LECanonicalConversion::toLocal (double* to, const void* from, size_t nr) { return toLocalDouble (to, from, nr); } inline size_t LECanonicalConversion::fromLocal (void* to, const char* from, size_t nr) { return fromLocalChar (to, from, nr); } inline size_t LECanonicalConversion::fromLocal (void* to, const unsigned char* from, size_t nr) { return fromLocalUChar (to, from, nr); } inline size_t LECanonicalConversion::fromLocal (void* to, const short* from, size_t nr) { return fromLocalShort (to, from, nr); } inline size_t LECanonicalConversion::fromLocal (void* to, const unsigned short* from, size_t nr) { return fromLocalUShort (to, from, nr); } inline size_t LECanonicalConversion::fromLocal (void* to, const int* from, size_t nr) { return fromLocalInt (to, from, nr); } inline size_t LECanonicalConversion::fromLocal (void* to, const unsigned int* from, size_t nr) { return fromLocalUInt (to, from, nr); } inline size_t LECanonicalConversion::fromLocal (void* to, const Int64* from, size_t nr) { return fromLocalInt64 (to, from, nr); } inline size_t LECanonicalConversion::fromLocal (void* to, const uInt64* from, size_t nr) { return fromLocalUInt64 (to, from, nr); } inline size_t LECanonicalConversion::fromLocal (void* to, const float* from, size_t nr) { return fromLocalFloat (to, from, nr); } inline size_t LECanonicalConversion::fromLocal (void* to, const double* from, size_t nr) { return fromLocalDouble (to, from, nr); } inline Conversion::ValueFunction* LECanonicalConversion::getToLocal (const char*) { return toLocalChar; } inline Conversion::ValueFunction* LECanonicalConversion::getToLocal (const unsigned char*) { return toLocalUChar; } inline Conversion::ValueFunction* LECanonicalConversion::getToLocal (const short*) { return toLocalShort; } inline Conversion::ValueFunction* LECanonicalConversion::getToLocal (const unsigned short*) { return toLocalUShort; } inline Conversion::ValueFunction* LECanonicalConversion::getToLocal (const int*) { return toLocalInt; } inline Conversion::ValueFunction* LECanonicalConversion::getToLocal (const unsigned int*) { return toLocalUInt; } inline Conversion::ValueFunction* LECanonicalConversion::getToLocal (const Int64*) { return toLocalInt64; } inline Conversion::ValueFunction* LECanonicalConversion::getToLocal (const uInt64*) { return toLocalUInt64; } inline Conversion::ValueFunction* LECanonicalConversion::getToLocal (const float*) { return toLocalFloat; } inline Conversion::ValueFunction* LECanonicalConversion::getToLocal (const double*) { return toLocalDouble; } inline Conversion::ValueFunction* LECanonicalConversion::getFromLocal (const char*) { return fromLocalChar; } inline Conversion::ValueFunction* LECanonicalConversion::getFromLocal (const unsigned char*) { return fromLocalUChar; } inline Conversion::ValueFunction* LECanonicalConversion::getFromLocal (const short*) { return fromLocalShort; } inline Conversion::ValueFunction* LECanonicalConversion::getFromLocal (const unsigned short*) { return fromLocalUShort; } inline Conversion::ValueFunction* LECanonicalConversion::getFromLocal (const int*) { return fromLocalInt; } inline Conversion::ValueFunction* LECanonicalConversion::getFromLocal (const unsigned int*) { return fromLocalUInt; } inline Conversion::ValueFunction* LECanonicalConversion::getFromLocal (const Int64*) { return fromLocalInt64; } inline Conversion::ValueFunction* LECanonicalConversion::getFromLocal (const uInt64*) { return fromLocalUInt64; } inline Conversion::ValueFunction* LECanonicalConversion::getFromLocal (const float*) { return fromLocalFloat; } inline Conversion::ValueFunction* LECanonicalConversion::getFromLocal (const double*) { return fromLocalDouble; } inline unsigned int LECanonicalConversion::canonicalSize (const char*) {return SIZE_LECAN_CHAR;} inline unsigned int LECanonicalConversion::canonicalSize (const unsigned char*) {return SIZE_LECAN_UCHAR;} inline unsigned int LECanonicalConversion::canonicalSize (const short*) {return SIZE_LECAN_SHORT;} inline unsigned int LECanonicalConversion::canonicalSize (const unsigned short*) {return SIZE_LECAN_USHORT;} inline unsigned int LECanonicalConversion::canonicalSize (const int*) {return SIZE_LECAN_INT;} inline unsigned int LECanonicalConversion::canonicalSize (const unsigned int*) {return SIZE_LECAN_UINT;} inline unsigned int LECanonicalConversion::canonicalSize (const Int64*) {return SIZE_LECAN_INT64;} inline unsigned int LECanonicalConversion::canonicalSize (const uInt64*) {return SIZE_LECAN_UINT64;} inline unsigned int LECanonicalConversion::canonicalSize (const float*) {return SIZE_LECAN_FLOAT;} inline unsigned int LECanonicalConversion::canonicalSize (const double*) {return SIZE_LECAN_DOUBLE;} //#//inline unsigned int LECanonicalConversion::canonicalSize (const long double*) //#// {return SIZE_LECAN_LDOUBLE;} } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/OS/LECanonicalDataConversion.cc000066400000000000000000000276211476623553700220430ustar00rootroot00000000000000//# LECanonicalDataConversion.cc: A class with virtual functions to convert little endian canonical format //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LECanonicalDataConversion::~LECanonicalDataConversion() {} size_t LECanonicalDataConversion::toLocal (char& to, const void* from) const { return LECanonicalConversion::toLocal (to, from); } size_t LECanonicalDataConversion::toLocal (unsigned char& to, const void* from) const { return LECanonicalConversion::toLocal (to, from); } size_t LECanonicalDataConversion::toLocal (short& to, const void* from) const { return LECanonicalConversion::toLocal (to, from); } size_t LECanonicalDataConversion::toLocal (unsigned short& to, const void* from) const { return LECanonicalConversion::toLocal (to, from); } size_t LECanonicalDataConversion::toLocal (int& to, const void* from) const { return LECanonicalConversion::toLocal (to, from); } size_t LECanonicalDataConversion::toLocal (unsigned int& to, const void* from) const { return LECanonicalConversion::toLocal (to, from); } size_t LECanonicalDataConversion::toLocal (Int64& to, const void* from) const { return LECanonicalConversion::toLocal (to, from); } size_t LECanonicalDataConversion::toLocal (uInt64& to, const void* from) const { return LECanonicalConversion::toLocal (to, from); } size_t LECanonicalDataConversion::toLocal (float& to, const void* from) const { return LECanonicalConversion::toLocal (to, from); } size_t LECanonicalDataConversion::toLocal (double& to, const void* from) const { return LECanonicalConversion::toLocal (to, from); } size_t LECanonicalDataConversion::toLocal (char* to, const void* from, size_t nr) const { return LECanonicalConversion::toLocal (to, from, nr); } size_t LECanonicalDataConversion::toLocal (unsigned char* to, const void* from, size_t nr) const { return LECanonicalConversion::toLocal (to, from, nr); } size_t LECanonicalDataConversion::toLocal (short* to, const void* from, size_t nr) const { return LECanonicalConversion::toLocal (to, from, nr); } size_t LECanonicalDataConversion::toLocal (unsigned short* to, const void* from, size_t nr) const { return LECanonicalConversion::toLocal (to, from, nr); } size_t LECanonicalDataConversion::toLocal (int* to, const void* from, size_t nr) const { return LECanonicalConversion::toLocal (to, from, nr); } size_t LECanonicalDataConversion::toLocal (unsigned int* to, const void* from, size_t nr) const { return LECanonicalConversion::toLocal (to, from, nr); } size_t LECanonicalDataConversion::toLocal (Int64* to, const void* from, size_t nr) const { return LECanonicalConversion::toLocal (to, from, nr); } size_t LECanonicalDataConversion::toLocal (uInt64* to, const void* from, size_t nr) const { return LECanonicalConversion::toLocal (to, from, nr); } size_t LECanonicalDataConversion::toLocal (float* to, const void* from, size_t nr) const { return LECanonicalConversion::toLocal (to, from, nr); } size_t LECanonicalDataConversion::toLocal (double* to, const void* from, size_t nr) const { return LECanonicalConversion::toLocal (to, from, nr); } size_t LECanonicalDataConversion::fromLocal (void* to, char from) const { return LECanonicalConversion::fromLocal (to, from); } size_t LECanonicalDataConversion::fromLocal (void* to, unsigned char from) const { return LECanonicalConversion::fromLocal (to, from); } size_t LECanonicalDataConversion::fromLocal (void* to, short from) const { return LECanonicalConversion::fromLocal (to, from); } size_t LECanonicalDataConversion::fromLocal (void* to, unsigned short from) const { return LECanonicalConversion::fromLocal (to, from); } size_t LECanonicalDataConversion::fromLocal (void* to, int from) const { return LECanonicalConversion::fromLocal (to, from); } size_t LECanonicalDataConversion::fromLocal (void* to, unsigned int from) const { return LECanonicalConversion::fromLocal (to, from); } size_t LECanonicalDataConversion::fromLocal (void* to, Int64 from) const { return LECanonicalConversion::fromLocal (to, from); } size_t LECanonicalDataConversion::fromLocal (void* to, uInt64 from) const { return LECanonicalConversion::fromLocal (to, from); } size_t LECanonicalDataConversion::fromLocal (void* to, float from) const { return LECanonicalConversion::fromLocal (to, from); } size_t LECanonicalDataConversion::fromLocal (void* to, double from) const { return LECanonicalConversion::fromLocal (to, from); } size_t LECanonicalDataConversion::fromLocal (void* to, const char* from, size_t nr) const { return LECanonicalConversion::fromLocal (to, from, nr); } size_t LECanonicalDataConversion::fromLocal (void* to, const unsigned char* from, size_t nr) const { return LECanonicalConversion::fromLocal (to, from, nr); } size_t LECanonicalDataConversion::fromLocal (void* to, const short* from, size_t nr) const { return LECanonicalConversion::fromLocal (to, from, nr); } size_t LECanonicalDataConversion::fromLocal (void* to, const unsigned short* from, size_t nr) const { return LECanonicalConversion::fromLocal (to, from, nr); } size_t LECanonicalDataConversion::fromLocal (void* to, const int* from, size_t nr) const { return LECanonicalConversion::fromLocal (to, from, nr); } size_t LECanonicalDataConversion::fromLocal (void* to, const unsigned int* from, size_t nr) const { return LECanonicalConversion::fromLocal (to, from, nr); } size_t LECanonicalDataConversion::fromLocal (void* to, const Int64* from, size_t nr) const { return LECanonicalConversion::fromLocal (to, from, nr); } size_t LECanonicalDataConversion::fromLocal (void* to, const uInt64* from, size_t nr) const { return LECanonicalConversion::fromLocal (to, from, nr); } size_t LECanonicalDataConversion::fromLocal (void* to, const float* from, size_t nr) const { return LECanonicalConversion::fromLocal (to, from, nr); } size_t LECanonicalDataConversion::fromLocal (void* to, const double* from, size_t nr) const { return LECanonicalConversion::fromLocal (to, from, nr); } Bool LECanonicalDataConversion::canCopy (const char*) const { return (CONVERT_LECAN_CHAR == 0); } Bool LECanonicalDataConversion::canCopy (const unsigned char*) const { return (CONVERT_LECAN_UCHAR == 0); } Bool LECanonicalDataConversion::canCopy (const short*) const { return (CONVERT_LECAN_SHORT == 0); } Bool LECanonicalDataConversion::canCopy (const unsigned short*) const { return (CONVERT_LECAN_USHORT == 0); } Bool LECanonicalDataConversion::canCopy (const int*) const { return (CONVERT_LECAN_INT == 0); } Bool LECanonicalDataConversion::canCopy (const unsigned int*) const { return (CONVERT_LECAN_UINT == 0); } Bool LECanonicalDataConversion::canCopy (const Int64*) const { return (CONVERT_LECAN_INT64 == 0); } Bool LECanonicalDataConversion::canCopy (const uInt64*) const { return (CONVERT_LECAN_UINT64 == 0); } Bool LECanonicalDataConversion::canCopy (const float*) const { return (CONVERT_LECAN_FLOAT == 0); } Bool LECanonicalDataConversion::canCopy (const double*) const { return (CONVERT_LECAN_DOUBLE == 0); } unsigned int LECanonicalDataConversion::externalSize (const char*) const { return SIZE_LECAN_CHAR; } unsigned int LECanonicalDataConversion::externalSize (const unsigned char*) const { return SIZE_LECAN_UCHAR; } unsigned int LECanonicalDataConversion::externalSize (const short*) const { return SIZE_LECAN_SHORT; } unsigned int LECanonicalDataConversion::externalSize (const unsigned short*) const { return SIZE_LECAN_USHORT; } unsigned int LECanonicalDataConversion::externalSize (const int*) const { return SIZE_LECAN_INT; } unsigned int LECanonicalDataConversion::externalSize (const unsigned int*) const { return SIZE_LECAN_UINT; } unsigned int LECanonicalDataConversion::externalSize (const Int64*) const { return SIZE_LECAN_INT64; } unsigned int LECanonicalDataConversion::externalSize (const uInt64*) const { return SIZE_LECAN_UINT64; } unsigned int LECanonicalDataConversion::externalSize (const float*) const { return SIZE_LECAN_FLOAT; } unsigned int LECanonicalDataConversion::externalSize (const double*) const { return SIZE_LECAN_DOUBLE; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/OS/LECanonicalDataConversion.h000066400000000000000000000201051476623553700216730ustar00rootroot00000000000000//# LECanonicalDataConversion.h: A class with virtual functions to convert little endian canonical format //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_LECANONICALDATACONVERSION_H #define CASA_LECANONICALDATACONVERSION_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A class with virtual functions to convert little endian canonical format. // // // // // // This class is a specialization of the abstract base class // DataConversion. // It contains functions to convert data from/to the little endian // canonical format using the static functions in class // LECanonicalConversion. // // // See example in class DataConversion. // // // This class is an addition to // LECanonicalConversion // to be able to use the conversion functions in a polymorphic way. // // //
        41. Support data type long double. // class LECanonicalDataConversion : public DataConversion { public: // Construct the object. LECanonicalDataConversion(); virtual ~LECanonicalDataConversion(); // Convert one value from canonical format to local format. // The from and to buffer should not overlap. // size_t toLocal (char& to, const void* from) const override; size_t toLocal (unsigned char& to, const void* from) const override; size_t toLocal (short& to, const void* from) const override; size_t toLocal (unsigned short& to, const void* from) const override; size_t toLocal (int& to, const void* from) const override; size_t toLocal (unsigned int& to, const void* from) const override; size_t toLocal (Int64& to, const void* from) const override; size_t toLocal (uInt64& to, const void* from) const override; size_t toLocal (float& to, const void* from) const override; size_t toLocal (double& to, const void* from) const override; // // Convert nr values from canonical format to local format. // The from and to buffer should not overlap. // size_t toLocal (char* to, const void* from, size_t nr) const override; size_t toLocal (unsigned char* to, const void* from, size_t nr) const override; size_t toLocal (short* to, const void* from, size_t nr) const override; size_t toLocal (unsigned short* to, const void* from, size_t nr) const override; size_t toLocal (int* to, const void* from, size_t nr) const override; size_t toLocal (unsigned int* to, const void* from, size_t nr) const override; size_t toLocal (Int64* to, const void* from, size_t nr) const override; size_t toLocal (uInt64* to, const void* from, size_t nr) const override; size_t toLocal (float* to, const void* from, size_t nr) const override; size_t toLocal (double* to, const void* from, size_t nr) const override; // // Convert one value from local format to canonical format. // The from and to buffer should not overlap. // size_t fromLocal (void* to, char from) const override; size_t fromLocal (void* to, unsigned char from) const override; size_t fromLocal (void* to, short from) const override; size_t fromLocal (void* to, unsigned short from) const override; size_t fromLocal (void* to, int from) const override; size_t fromLocal (void* to, unsigned int from) const override; size_t fromLocal (void* to, Int64 from) const override; size_t fromLocal (void* to, uInt64 from) const override; size_t fromLocal (void* to, float from) const override; size_t fromLocal (void* to, double from) const override; // // Convert nr values from local format to canonical format. // The from and to buffer should not overlap. // size_t fromLocal (void* to, const char* from, size_t nr) const override; size_t fromLocal (void* to, const unsigned char* from, size_t nr) const override; size_t fromLocal (void* to, const short* from, size_t nr) const override; size_t fromLocal (void* to, const unsigned short* from, size_t nr) const override; size_t fromLocal (void* to, const int* from, size_t nr) const override; size_t fromLocal (void* to, const unsigned int* from, size_t nr) const override; size_t fromLocal (void* to, const Int64* from, size_t nr) const override; size_t fromLocal (void* to, const uInt64* from, size_t nr) const override; size_t fromLocal (void* to, const float* from, size_t nr) const override; size_t fromLocal (void* to, const double* from, size_t nr) const override; // // Determine if the data for a data type can be simply copied, thus // if no conversion is needed. // Bool canCopy (const char*) const override; Bool canCopy (const unsigned char*) const override; Bool canCopy (const short*) const override; Bool canCopy (const unsigned short*) const override; Bool canCopy (const int*) const override; Bool canCopy (const unsigned int*) const override; Bool canCopy (const Int64*) const override; Bool canCopy (const uInt64*) const override; Bool canCopy (const float*) const override; Bool canCopy (const double*) const override; // // Get the external size of the data type. // unsigned int externalSize (const char*) const override; unsigned int externalSize (const unsigned char*) const override; unsigned int externalSize (const short*) const override; unsigned int externalSize (const unsigned short*) const override; unsigned int externalSize (const int*) const override; unsigned int externalSize (const unsigned int*) const override; unsigned int externalSize (const Int64*) const override; unsigned int externalSize (const uInt64*) const override; unsigned int externalSize (const float*) const override; unsigned int externalSize (const double*) const override; // }; inline LECanonicalDataConversion::LECanonicalDataConversion() {} } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/OS/LittleEndianConversion.cc000066400000000000000000000131701476623553700215070ustar00rootroot00000000000000//# LittleEndianConversion.cc: A class with static functions to convert littleEndian format //# Copyright (C) 1996,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN void LittleEndianConversion::toLocal (char* to, const void* from, size_t nr) { assert (sizeof(char) == 1); memcpy (to, from, nr); } void LittleEndianConversion::toLocal (unsigned char* to, const void* from, size_t nr) { assert (sizeof(unsigned char) == 1); memcpy (to, from, nr); } void LittleEndianConversion::toLocal (short* to, const void* from, size_t nr) { const char* data = (const char*)from; short* last = to + nr; while (to < last) { toLocal (*to++, data); data += 2; } } void LittleEndianConversion::toLocal (unsigned short* to, const void* from, size_t nr) { const char* data = (const char*)from; unsigned short* last = to + nr; while (to < last) { toLocal (*to++, data); data += 2; } } void LittleEndianConversion::toLocal (int* to, const void* from, size_t nr) { const char* data = (const char*)from; int* last = to + nr; while (to < last) { toLocal (*to++, data); data += 4; } } void LittleEndianConversion::toLocal (unsigned int* to, const void* from, size_t nr) { const char* data = (const char*)from; unsigned int* last = to + nr; while (to < last) { toLocal (*to++, data); data += 4; } } void LittleEndianConversion::toLocal (Int64* to, const void* from, size_t nr) { const char* data = (const char*)from; Int64* last = to + nr; while (to < last) { toLocal (*to++, data); data += 4; } } void LittleEndianConversion::toLocal (uInt64* to, const void* from, size_t nr) { const char* data = (const char*)from; uInt64* last = to + nr; while (to < last) { toLocal (*to++, data); data += 4; } } void LittleEndianConversion::toLocal (float* to, const void* from, size_t nr) { const char* data = (const char*)from; float* last = to + nr; while (to < last) { toLocal (*to++, data); data += 4; } } void LittleEndianConversion::toLocal (double* to, const void* from, size_t nr) { const char* data = (const char*)from; double* last = to + nr; while (to < last) { toLocal (*to++, data); data += 4; } } void LittleEndianConversion::fromLocal (void* to, const char* from, size_t nr) { assert (sizeof(char) == 1); memcpy (to, from, nr); } void LittleEndianConversion::fromLocal (void* to, const unsigned char* from, size_t nr) { assert (sizeof(unsigned char) == 1); memcpy (to, from, nr); } void LittleEndianConversion::fromLocal (void* to, const short* from, size_t nr) { char* data = (char*)to; const short* last = from + nr; while (from < last) { fromLocal (data, *from++); data += 2; } } void LittleEndianConversion::fromLocal (void* to, const unsigned short* from, size_t nr) { char* data = (char*)to; const unsigned short* last = from + nr; while (from < last) { fromLocal (data, *from++); data += 2; } } void LittleEndianConversion::fromLocal (void* to, const int* from, size_t nr) { char* data = (char*)to; const int* last = from + nr; while (from < last) { fromLocal (data, *from++); data += 4; } } void LittleEndianConversion::fromLocal (void* to, const unsigned int* from, size_t nr) { char* data = (char*)to; const unsigned int* last = from + nr; while (from < last) { fromLocal (data, *from++); data += 4; } } void LittleEndianConversion::fromLocal (void* to, const Int64* from, size_t nr) { char* data = (char*)to; const Int64* last = from + nr; while (from < last) { fromLocal (data, *from++); data += 4; } } void LittleEndianConversion::fromLocal (void* to, const uInt64* from, size_t nr) { char* data = (char*)to; const uInt64* last = from + nr; while (from < last) { fromLocal (data, *from++); data += 4; } } void LittleEndianConversion::fromLocal (void* to, const float* from, size_t nr) { char* data = (char*)to; const float* last = from + nr; while (from < last) { fromLocal (data, *from++); data += 4; } } void LittleEndianConversion::fromLocal (void* to, const double* from, size_t nr) { char* data = (char*)to; const double* last = from + nr; while (from < last) { fromLocal (data, *from++); data += 8; } } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/OS/LittleEndianConversion.h000066400000000000000000000245001476623553700213500ustar00rootroot00000000000000//# LittleEndianConversion.h: A class with static functions to convert littleEndian format //# Copyright (C) 1996,1997,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_LITTLEENDIANCONVERSION_H #define CASA_LITTLEENDIANCONVERSION_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A class with static functions to convert littleEndian format // // // // // // This class is intended to be used as a common class for all // classes converting data to/from little-endian format. // // // Sometimes data are stored in little-endian format (e.g. old VAX-data). // Instead of putting all these conversion functions in all such classes, // it is better to keep them separate to be able to use them elsewhere. // However, note that this version handles a long as 4 bytes. // On several little-endian machines (e.g. DEC-alpha) a long is 8 bytes, // so a special function is needed for them. // // //
        42. Support data type long double. //
        43. Support a long as 4 or as 8 bytes. // class LittleEndianConversion { public: // Convert one value from littleEndian format to local format. // The from and to buffer should not overlap. // static void toLocal (char& to, const void* from); static void toLocal (unsigned char& to, const void* from); static void toLocal (short& to, const void* from); static void toLocal (unsigned short& to, const void* from); static void toLocal (int& to, const void* from); static void toLocal (unsigned int& to, const void* from); static void toLocal (Int64& to, const void* from); static void toLocal (uInt64& to, const void* from); static void toLocal (float& to, const void* from); static void toLocal (double& to, const void* from); // // Convert nr values from littleEndian format to local format. // The from and to buffer should not overlap. // static void toLocal (char* to, const void* from, size_t nr); static void toLocal (unsigned char* to, const void* from, size_t nr); static void toLocal (short* to, const void* from, size_t nr); static void toLocal (unsigned short* to, const void* from, size_t nr); static void toLocal (int* to, const void* from, size_t nr); static void toLocal (unsigned int* to, const void* from, size_t nr); static void toLocal (Int64* to, const void* from, size_t nr); static void toLocal (uInt64* to, const void* from, size_t nr); static void toLocal (float* to, const void* from, size_t nr); static void toLocal (double* to, const void* from, size_t nr); // // Convert one value from local format to littleEndian format. // The from and to buffer should not overlap. // static void fromLocal (void* to, char from); static void fromLocal (void* to, unsigned char from); static void fromLocal (void* to, short from); static void fromLocal (void* to, unsigned short from); static void fromLocal (void* to, int from); static void fromLocal (void* to, unsigned int from); static void fromLocal (void* to, Int64 from); static void fromLocal (void* to, uInt64 from); static void fromLocal (void* to, float from); static void fromLocal (void* to, double from); // // Convert nr values from local format to littleEndian format. // The from and to buffer should not overlap. // static void fromLocal (void* to, const char* from, size_t nr); static void fromLocal (void* to, const unsigned char* from, size_t nr); static void fromLocal (void* to, const short* from, size_t nr); static void fromLocal (void* to, const unsigned short* from, size_t nr); static void fromLocal (void* to, const int* from, size_t nr); static void fromLocal (void* to, const unsigned int* from, size_t nr); static void fromLocal (void* to, const Int64* from, size_t nr); static void fromLocal (void* to, const uInt64* from, size_t nr); static void fromLocal (void* to, const float* from, size_t nr); static void fromLocal (void* to, const double* from, size_t nr); // private: // This class should not be constructed // (so declare the constructor private). LittleEndianConversion(); }; inline void LittleEndianConversion::toLocal (char& to, const void* from) { to = *(char*)from; } inline void LittleEndianConversion::toLocal (unsigned char& to, const void* from) { to = *(unsigned char*)from; } inline void LittleEndianConversion::toLocal (short& to, const void* from) { if (sizeof(short) != 2) { if (((signed char*)from)[2] < 0) { to = -1; }else{ to = 0; } } #if !defined(AIPS_LITTLE_ENDIAN) CanonicalConversion::reverse2 (((char*)&to)+sizeof(short)-2, from); #else CanonicalConversion::move2 (&to, from); #endif } inline void LittleEndianConversion::toLocal (unsigned short& to, const void* from) { if (sizeof(unsigned short) != 2) { to = 0; } #if !defined(AIPS_LITTLE_ENDIAN) CanonicalConversion::reverse2 (((char*)&to)+sizeof(unsigned short)-2,from); #else CanonicalConversion::move2 (&to, from); #endif } inline void LittleEndianConversion::toLocal (int& to, const void* from) { if (sizeof(int) != 4) { if (((signed char*)from)[3] < 0) { to = -1; }else{ to = 0; } } #if !defined(AIPS_LITTLE_ENDIAN) CanonicalConversion::reverse4 (((char*)&to)+sizeof(int)-4, from); #else CanonicalConversion::move4 (&to, from); #endif } inline void LittleEndianConversion::toLocal (unsigned int& to, const void* from) { if (sizeof(unsigned int) != 4) { to = 0; } #if !defined(AIPS_LITTLE_ENDIAN) CanonicalConversion::reverse4 (((char*)&to)+sizeof(unsigned int)-4, from); #else CanonicalConversion::move4 (&to, from); #endif } inline void LittleEndianConversion::toLocal (Int64& to, const void* from) { int tmp; LittleEndianConversion::toLocal (tmp, from); to = tmp; } inline void LittleEndianConversion::toLocal (uInt64& to, const void* from) { unsigned int tmp; LittleEndianConversion::toLocal (tmp, from); to = tmp; } inline void LittleEndianConversion::toLocal (float& to, const void* from) { #if !defined(AIPS_LITTLE_ENDIAN) CanonicalConversion::reverse4 (&to, from); #else CanonicalConversion::move4 (&to, from); #endif } inline void LittleEndianConversion::toLocal (double& to, const void* from) { #if !defined(AIPS_LITTLE_ENDIAN) CanonicalConversion::reverse8 (&to, from); #else CanonicalConversion::move8 (&to, from); #endif } inline void LittleEndianConversion::fromLocal (void* to, char from) { *(char*)to = from; } inline void LittleEndianConversion::fromLocal (void* to, unsigned char from) { *(unsigned char*)to = from; } inline void LittleEndianConversion::fromLocal (void* to, short from) { #if !defined(AIPS_LITTLE_ENDIAN) CanonicalConversion::reverse2 (to, ((char*)&from)+sizeof(short)-2); #else CanonicalConversion::move2 (to, &from); #endif } inline void LittleEndianConversion::fromLocal (void* to, unsigned short from) { #if !defined(AIPS_LITTLE_ENDIAN) CanonicalConversion::reverse2 (to,((char*)&from)+sizeof(unsigned short)-2); #else CanonicalConversion::move2 (to, &from); #endif } inline void LittleEndianConversion::fromLocal (void* to, int from) { #if !defined(AIPS_LITTLE_ENDIAN) CanonicalConversion::reverse4 (to, ((char*)&from)+sizeof(int)-4); #else CanonicalConversion::move4 (to, &from); #endif } inline void LittleEndianConversion::fromLocal (void* to, unsigned int from) { #if !defined(AIPS_LITTLE_ENDIAN) CanonicalConversion::reverse4 (to, ((char*)&from)+sizeof(unsigned int)-4); #else CanonicalConversion::move4 (to, &from); #endif } inline void LittleEndianConversion::fromLocal (void* to, Int64 from) { #if !defined(AIPS_LITTLE_ENDIAN) CanonicalConversion::reverse4 (to, ((char*)&from)+sizeof(Int64)-4); #else CanonicalConversion::move4 (to, &from); #endif } inline void LittleEndianConversion::fromLocal (void* to, uInt64 from) { #if !defined(AIPS_LITTLE_ENDIAN) CanonicalConversion::reverse4 (to, ((char*)&from)+sizeof(uInt64)-4); #else CanonicalConversion::move4 (to, &from); #endif } inline void LittleEndianConversion::fromLocal (void* to, float from) { #if !defined(AIPS_LITTLE_ENDIAN) CanonicalConversion::reverse4 (to, ((char*)&from)+sizeof(float)-4); #else CanonicalConversion::move4 (to, &from); #endif } inline void LittleEndianConversion::fromLocal (void* to, double from) { #if !defined(AIPS_LITTLE_ENDIAN) CanonicalConversion::reverse8 (to, ((char*)&from)+sizeof(double)-8); #else CanonicalConversion::move8 (to, &from); #endif } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/OS/Memory.cc000066400000000000000000000057631476623553700163460ustar00rootroot00000000000000//# Memory.cc: Memory related information and utilities. //# Copyright (C) 1997,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# MH 97/11/24 Stop attempt to use mallinfo from stdlib.h for HPUX #include #include #include #if defined(AIPS_DARWIN) || defined(AIPS_CRAY_PGI) #include #include #if defined(AIPS_CRAY_PGI) #include #endif #else #include #endif namespace casacore { //#Begin namespace casa void Memory::releaseMemory() { #if defined(AIPS_RELEASEMEM) AIPS_RELEASEMEM; #elif !defined(AIPS_NO_LEA_MALLOC) malloc_trim(0); #endif } //setMemoryOptions uses compiler defines to set the // memory options if needed. It is intended to be // only called at the start of a program. Use // setMemoryOption to tweak the memory options elsewhere. void Memory::setMemoryOptions(){ #ifdef AIPS_MALLOC_M_MXFAST mallopt(M_MXFAST, AIPS_MALLOC_M_MXFAST); #endif #ifdef AIPS_MALLOC_M_NLBLKS mallopt(M_NLBLKS, AIPS_MALLOC_M_NLBLKS); #endif #ifdef AIPS_MALLOC_M_GRAIN mallopt(M_GRAIN, AIPS_MALLOC_M_GRAIN); #endif #ifdef AIPS_MALLOC_M_KEEP mallopt(M_KEEP, 1); #endif #ifdef AIPS_MALLOC_M_DEBUG mallopt(M_DEBUG, 1); #endif // Following options are from the SGI mallopt #ifdef AIPS_MALLOC_M_BLKSZ mallopt(M_BLKSZ, AIPS_MALLOC_M_BLKSZ); #endif #ifdef AIPS_MALLOC_M_MXCHK mallopt(M_MXCHK, AIPS_MALLOC_M_MXCHK); #endif #ifdef AIPS_MALLOC_M_FREEHD mallopt(M_FREEHD, 1); #endif #ifdef AIPS_MALLOC_M_CLRONFREE mallopt(M_CLRONFREE, AIPS_MALLOC_M_CLRONFREE); #endif } #if defined(AIPS_DARWIN) || defined(AIPS_CRAY_PGI) int Memory::setMemoryOption(int, int) { return 0; #else int Memory::setMemoryOption(int cmd, int value) { return(mallopt(cmd, value)); #endif } } //#End namespace casacore casacore-3.7.1/casa/OS/Memory.h000066400000000000000000000134261476623553700162030ustar00rootroot00000000000000//# Memory.h: Memory related information and utilities. //# Copyright (C) 1997,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MEMORY_H #define CASA_MEMORY_H #include //# The following is used to get size_t. #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Memory related information and utilities. // use visibility=export> // // // //
        44. General knowledge of C/C++ memory allocation issues. // // // // This class should generally not be used by general application programmers. // Instead you should use the memory information available in the // AppInfo class. // // This class reports on the dynamic ("heap") memory used by this process, and // it can attempt to release some of that memory back to the operating // system. The class reports on allocated memory, which is memory // that the process has actually requested, typically via new, but // also via malloc. The number might be somewhat larger than actually // requested by the programmer to account for overhead and alignment // considerations. The class also reports assigned memory, which is // the total memory that the process has been given, not all of which has been // allocated by the programmer. Typically this results from memory which has // been deleted by the programmer, but has not been released to the // OS on the assumption that it might be needed again. (Getting and releasing // memory to the OS can be expensive). // // At present, the values for allocated and assigned memory are obtained via the // mallinfo() call, usually defined in malloc.h. This call // seems to be adequately portable for Unix. Another alternative would be to // replace global operators new and delete for systems // that do not have mallinfo(). // // The member function releaseMemory() on some system will attempt // to return assigned but unallocated memory to the OS. If the compilation // system cannot automatically determine how to do this (at present it only // recognizes Linux systems), you can you this function by setting the // preprocessor symbol AIPS_RELEASEMEM to a C++ statement which // will release memory to the OS. For example, if you are using GNU malloc // you could set it to malloc_trim(0). Note that // releaseMemory() might be a no-op on many systems, and that // calling it might be expensive so it should not be called in tight-loops. // // Note that this class does not use any Casacore facilities and does not cause // any Casacore code to be linked in to your executable. // // // // We could attempt to return memory to the OS when we are wasting a lot // of memory as follows. // // if (Memory::assignedMemoryInBytes() - Memory::allocatedMemoryInBytes() > // 1024*1024) { // Memory::releaseMemory(); // Attempt to release if more than 1M "wasted" // } // // // // // At run time we need to be able to make decisions about whether we should // keep things in memory or about whether we should do I/O (e.g. use an // Array or a PagedArray). // // // //
        45. We might some day want to put actual allocation/deallocation // functions in here if the mallinfo() interface turns out to not // be portable. // class Memory { public: // Attempt to release memory which has been assigned but not allocated. // On many systems this will be a no-op, and even on systems in which it // does something the amount of reclaimed memory cannot be specified. // Since this function may be somewhat expensive to call it should not // be called too often. [[deprecated("This function calls malloc_trim(), which is is mostly an artefact and not effective on modern systems")]] static void releaseMemory(); // setMemoryOptions and setMemoryOption are typically front ends for mallopt // which lets the user control some memory allocation parameters. setMemoryOptions // is intended to be called only once at the start of a program while setMemoryOption // could be called where desired (but see mallopt man page for possible side effects). // Note: these two functions were added to address in a general way a memory // fragmentation problem encountered on by the MIPSpro C++ compiler on the SGI. static void setMemoryOptions(); static int setMemoryOption(int, int); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/OS/MemoryTrace.cc000066400000000000000000000217621476623553700173220ustar00rootroot00000000000000//# MemoryTrace.cc: Simple memory usage tracing mechanism //# Copyright (C) 2015 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Comment out for time being because __malloc_hook is deprecated. //# Do so by #ifdef on AIPS_LINUX_DEPR instead of AIPS_LINUX. #include #include #include #include #ifdef AIPS_LINUX_DEPR # include #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN // Initialize statics. Bool MemoryTrace::theirDoTrace = False; std::ofstream MemoryTrace::theirFile; Timer MemoryTrace::theirTimer; void* (*MemoryTrace::theirOldMallocHook)(size_t, const void*) = 0; void (*MemoryTrace::theirOldFreeHook)(void*, const void*) = 0; void MemoryTrace::open() { if (! theirFile.is_open()) { String name (EnvironmentVariable::get ("CASACORE_MEMORYTRACE")); if (name.empty()) { name = "casacore_memorytrace.log"; } theirFile.open (name.c_str()); if (!theirFile) { throw AipsError ("Could not create memorytrace file " + name); } } } void MemoryTrace::start() { if (! theirFile.is_open()) { open(); } if (!theirDoTrace) { #ifdef AIPS_LINUX_DEPR theirOldMallocHook = __malloc_hook; theirOldFreeHook = __free_hook; __malloc_hook = &mallocHook; __free_hook = &freeHook; #endif theirDoTrace = True; } } void MemoryTrace::stop() { if (theirDoTrace) { #ifdef AIPS_LINUX_DEPR __malloc_hook = theirOldMallocHook; __free_hook = theirOldFreeHook; #endif theirDoTrace = False; } } std::ofstream& MemoryTrace::writeAlloc (const void* ptr, size_t size) { theirFile << Int64(1000 * theirTimer.real()) << " a b-" << ptr << ' ' << size << ' '; return theirFile; } std::ofstream& MemoryTrace::writeFree (const void* ptr) { theirFile << Int64(1000 * theirTimer.real()) << " f b-" << ptr << ' '; return theirFile; } void MemoryTrace::close() { stop(); if (theirFile.is_open()) { theirFile.close(); } } #ifdef AIPS_LINUX_DEPR void* MemoryTrace::mallocHook (size_t size, const void* caller) { if (size == 0) { return NULL; } // Restore the old hooks. __malloc_hook = theirOldMallocHook; __free_hook = theirOldFreeHook; // Call recursively. void* ptr = malloc(size); // Save hooks (to be safe). theirOldMallocHook = __malloc_hook; theirOldFreeHook = __free_hook; // iostream might call malloc/free; it is protected too. theirFile << Int64(1000 * theirTimer.real()) << " a " << ptr << ' ' << size << ' ' << caller << std::endl; // Restore our own hooks. __malloc_hook = &mallocHook; __free_hook = &freeHook; return ptr; } #else void* MemoryTrace::mallocHook (size_t, const void*) { return NULL; } #endif #ifdef AIPS_LINUX_DEPR void MemoryTrace::freeHook (void* ptr, const void* caller) { if (ptr != NULL) { // Restore the old hooks. __malloc_hook = theirOldMallocHook; __free_hook = theirOldFreeHook; // Call recursively. free (ptr); // Save hooks (to be safe). theirOldMallocHook = __malloc_hook; theirOldFreeHook = __free_hook; // iostream might call malloc/free; it is protected too. theirFile << Int64(1000 * theirTimer.real()) << " f " << ptr << ' ' << caller << std::endl; // Restore our own hooks. __malloc_hook = &mallocHook; __free_hook = &freeHook; } } #else void MemoryTrace::freeHook (void*, const void*) {} #endif void MemoryTrace::writeBlock (const char* msg, const std::string& name) { if (isOpen()) { #ifdef AIPS_LINUX_DEPR if (theirDoTrace) { // Restore the old hooks. __malloc_hook = theirOldMallocHook; __free_hook = theirOldFreeHook; } #endif theirFile << Int64(1000 * theirTimer.real()) << msg << name << std::endl; #ifdef AIPS_LINUX_DEPR if (theirDoTrace) { // Restore our own hooks. __malloc_hook = &mallocHook; __free_hook = &freeHook; } #endif } } void MemoryTrace::writeBlock (const char* msg, const char* name) { if (isOpen()) { #ifdef AIPS_LINUX_DEPR if (theirDoTrace) { // Restore the old hooks. __malloc_hook = theirOldMallocHook; __free_hook = theirOldFreeHook; } #endif theirFile << Int64(1000 * theirTimer.real()) << msg << name << std::endl; #ifdef AIPS_LINUX_DEPR if (theirDoTrace) { // Restore our own hooks. __malloc_hook = &mallocHook; __free_hook = &freeHook; } #endif } } std::string MemoryTrace::makeString (const char* name) { #ifdef AIPS_LINUX_DEPR if (theirDoTrace) { // Restore the old hooks to avoid trace messages when making the string. __malloc_hook = theirOldMallocHook; __free_hook = theirOldFreeHook; } #endif std::string str(name); #ifdef AIPS_LINUX_DEPR if (theirDoTrace) { // Restore our own hooks. __malloc_hook = &mallocHook; __free_hook = &freeHook; } #endif return str; } MemoryTraceBlock::MemoryTraceBlock (const std::string& name) : itsName (name) { traceMemoryBlockBegin (itsName); } MemoryTraceBlock::MemoryTraceBlock (const char* name) : itsName (MemoryTrace::makeString(name)) { traceMemoryBlockBegin (itsName); } MemoryTraceBlock::~MemoryTraceBlock() { traceMemoryBlockEnd (itsName); } } //# NAMESPACE CASACORE - END // Note: another way to do memory tracing is by preloading a shared library // containing the overloaded malloc and free. This could be done in a // casacore package memorytrace or so. // The disadvantage of it is that it is less selective; furthermore it always // has to find the symbol which can be a bit expensive. // See the following code. /* //# See http://www.stev.org/post/2012/11/03/HowTo-Override-malloc-free-in-c.aspx #include #include #include void* malloc(size_t size) noexcept { if (size == 0) { return NULL; } // For the compiler data pointers and function pointers are different, which // can be the case for special hardware. Not here, so make them equal. typedef void* (*func_ptr)(size_t); typedef union { func_ptr funcPtr; void* ptr; } ptrCastUnion; ptrCastUnion ptrCast; void* handle = RTLD_NEXT; ptrCast.ptr = dlsym(handle, "malloc"); if (ptrCast.ptr == NULL) { printf("libcasa_malloclog cannot find malloc\n"); exit(1); } void* addr = (ptrCast.funcPtr)(size); printf("Alloc = %p Size: %ld\n", addr, size); return addr; } void free(void* addr) noexcept { if (addr == NULL) { return; } typedef void (*func_ptr)(void*); typedef union { func_ptr funcPtr; void* ptr; } ptrCastUnion; ptrCastUnion ptrCast; printf("free %p\n", addr); void* handle = RTLD_NEXT; ptrCast.ptr = dlsym(handle, "free"); if (ptrCast.ptr == NULL) { exit(1); } (ptrCast.funcPtr)(addr); } */ /* See http://stackoverflow.com/questions/17803456/an-alternative-for-the-deprecated-malloc-hook-functionality-of-glibc See also http://elinux.org/images/b/b5/Elc2013_Kobayashi.pdf My complete hooking function now looks like this: extern void *__libc_malloc(size_t size); int malloc_hook_active = 0; void* malloc (size_t size) { void *caller = __builtin_return_address(0); if (malloc_hook_active) return my_malloc_hook(size, caller); return __libc_malloc(size); } where my_malloc_hook looks like this: void* my_malloc_hook (size_t size, void *caller) { void *result; // deactivate hooks for logging malloc_hook_active = 0; result = malloc(size); // do logging [ ... ] // reactivate hooks malloc_hook_active = 1; return result; } Of course, the hooks for calloc, realloc and free work similarly. */ casacore-3.7.1/casa/OS/MemoryTrace.h000066400000000000000000000137431476623553700171640ustar00rootroot00000000000000//# MemoryTrace.h: Memory usage tracing mechanism //# Copyright (C) 2015 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MEMORYTRACE_H #define CASA_MEMORYTRACE_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // memory usage tracing mechanism // // // // // // // The MemoryTrace class provides some means to trace the // memory usage of a program. It logs malloc and free messages in // a file which can be examined by the python script memorytrace.py. //
          The tracing is done using hooks for malloc and free as explained // in 'man malloc_hook'. // // The tracing can be started and stopped at any time. On the first // start the trace file is created. The file can be closed at any time, // usually at the end of a program. Another start will recreate the file. // // The trace file consists of 3 types of lines: //
            //
          • An allocation line like "a
            " //
          • A deallocation line like "f
            " //
          • A line like "begin/end " telling the script the beginning // or end of a code block. It makes it possible to see how memory usage // develops. Such lines can be inserted using the class MemoryTraceBlock. //
          // All lines start with the number of milliseconds since the start of // the program. //

          // The script memorytrace.py can be used to interpret the log file and // to show the memory usage. // class MemoryTrace { public: // Start the tracing. Nothing is done if already started. // On the first time, it opens the trace file. The name of the // trace file can be given in the env.var. CASACORE_MEMORYTRACE. // If undefined, the name casacore_memorytrace.log will be used. static void start(); // Stop the tracing. static void stop(); // Open the trace file if not open yet. static void open(); // Close the tracing output file. static void close(); // Is tracing on? static Bool isOn() { return theirDoTrace; } // Is the tracing file opened? static Bool isOpen() { return theirFile.is_open(); } // Write a block line in the output file. static void writeBlock (const char* msg, const std::string& name); static void writeBlock (const char* msg, const char* name); // Write an alloc or free message. static std::ofstream& writeAlloc (const void* ptr, size_t); static std::ofstream& writeFree (const void* ptr); // The hooks for malloc and free writing the trace messages. static void* mallocHook (size_t, const void* caller); static void freeHook (void*, const void* caller); // Make a string from a char* without tracing a possible malloc in // the string constructor. static std::string makeString (const char*); private: static Bool theirDoTrace; static std::ofstream theirFile; static Timer theirTimer; //# Variables to save original hooks. static void* (*theirOldMallocHook)(size_t, const void*); static void (*theirOldFreeHook)(void*, const void*); }; //

          Class to write begin and end block message // // This class is meant to write memory trace messages indicating the // beginning and end of a code block. In this way it is known that the // (de)allocate messages between these messages belong to that code block. // // The constructor writes the begin message, while the destructor writes // the end message. Because the destructor is called automatically by the // compiler, the user does not have to worry about it; it will also // work fine in case of a premature exit from a function. // // It is possible to nest blocks as deeply as one likes. // class MemoryTraceBlock { public: // The constructor writes a block begin message. MemoryTraceBlock (const std::string& name); MemoryTraceBlock (const char* name); // The constructor writes a block end message. ~MemoryTraceBlock(); private: std::string itsName; }; } //# NAMESPACE CASACORE - END //# Trace memory (de)allocation. #define traceMemoryAlloc(ptr,size,msg) \ if (casacore::MemoryTrace::isOpen()) { \ casacore::MemoryTrace::writeAlloc (ptr, size) << msg << std::endl; \ } #define traceMemoryFree(ptr,msg) \ if (casacore::MemoryTrace::isOpen()) { \ casacore::MemoryTrace::writeFree (ptr) << msg << std::endl; \ } #define traceMemoryBlockBegin(name) \ if (casacore::MemoryTrace::isOpen()) { \ casacore::MemoryTrace::writeBlock(" begin ", name); \ } #define traceMemoryBlockEnd(name) \ if (casacore::MemoryTrace::isOpen()) { \ casacore::MemoryTrace::writeBlock(" end ", name); \ } #endif casacore-3.7.1/casa/OS/ModcompConversion.cc000066400000000000000000000346361476623553700205430ustar00rootroot00000000000000//# ModcompConversion.cc: Static functions to convert Modcomp numeric formats //# Copyright (C) 1998,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN size_t ModcompConversion::toLocal (Int64* to, const void* from, size_t nr) { const char* data = (const char*)from; Int64* last = to + nr; while (to < last) { toLocal (*to++, data); data += SIZE_MODCOMP_INT64; } return nr*SIZE_MODCOMP_INT64; } size_t ModcompConversion::toLocal (uInt64* to, const void* from, size_t nr) { const char* data = (const char*)from; uInt64* last = to + nr; while (to < last) { toLocal (*to++, data); data += SIZE_MODCOMP_UINT64; } return nr*SIZE_MODCOMP_UINT64; } size_t ModcompConversion::fromLocal (void* to, const Int64* from, size_t nr) { char* data = (char*)to; const Int64* last = from + nr; while (from < last) { fromLocal (data, *from++); data += SIZE_MODCOMP_INT64; } return nr*SIZE_MODCOMP_INT64; } size_t ModcompConversion::fromLocal (void* to, const uInt64* from, size_t nr) { char* data = (char*)to; const uInt64* last = from + nr; while (from < last) { fromLocal (data, *from++); data += SIZE_MODCOMP_UINT64; } return nr*SIZE_MODCOMP_UINT64; } // Modcomp has one more bit in the exponent than IEEE and because it does not // have an implicit bit two less bits in the Mantissa. It does not have any // special numbers like NaN or Infinity. The Modcomp is big-endian (like Sun's) size_t ModcompConversion::toLocal (Float* to, const void* from, size_t nr) { DebugAssert(sizeof(Short) >= 2, AipsError); uChar asByte[SIZE_MODCOMP_FLOAT]; size_t retval = 0; const uChar* data = (const uChar*) from; for (const Float* const last = to + nr; to < last; to++) { // Copy the data to the temporary buffer for (uShort i = 0; i < SIZE_MODCOMP_FLOAT; i++) { asByte[i] = *data; data++; } // If the number is negative then its positive value is the twos complement const Bool isNegative = ((asByte[0] & 0x80) > 0) ? True : False; if (isNegative) { // This code takes the twos complement uShort i = 0; while (i < SIZE_MODCOMP_FLOAT) { asByte[i] = ~asByte[i]; i++; } i--; asByte[i]++; while (asByte[i] == 0 && i > 0) { i--; asByte[i]++; } } Bool isZero = (asByte[1] & 0x3f) == 0x00; for (uShort i = 2; i < SIZE_MODCOMP_FLOAT; i++) { isZero = (asByte[i] == 0x00) && isZero; } if (isZero) { // Early exit if the number is zero. // I am told this is common in the data so it should speed things up. for (uShort k = 1; k < SIZE_MODCOMP_FLOAT; k++) { asByte[k] = 0x00; } if (isNegative) { // Return a signed zero asByte[0] = 0x80; } else { asByte[0] = 0x00; } } else { // Number is not zero Short exponent = ((asByte[0] & 0x7f) << 2) | ((asByte[1] & 0xc0) >> 6); while ((asByte[1] & 0x20) == 0) { // See if the number is unnormalised { // If so try to normalise it. // This code does a byte by byte left shift by one bit (painful). uShort i = SIZE_MODCOMP_FLOAT-1; Bool msbIsSet = (asByte[i] & (0x80)) > 0 ? True : False; asByte[i] <<= 1; i--; while (i > 1) { Bool prevMsbIsSet = msbIsSet; msbIsSet = (asByte[i] & (0x80)) > 0 ? True : False; asByte[i] <<= 1; if (prevMsbIsSet) asByte[i] |= 0x01; i--; } asByte[1] = ((asByte[1] & 0x3f) << 1) | (asByte[1] & 0xc0); if (msbIsSet) asByte[1] |= 0x01; } exponent--; // Because the exponent can go negative it must be signed } if (exponent > 384) {// exponent is too big. for (uShort i = 2; i < SIZE_MODCOMP_FLOAT; i++) { asByte[i] = 0x00; } asByte[1] = 0x80; if (isNegative) { asByte[0] = 0xff; // Return a negative infinity } else { asByte[0] = 0x7f; // Return a positive infinity } } else if (exponent < 108) {// exponent is too small. for (uShort i = 1; i < SIZE_MODCOMP_FLOAT; i++) { asByte[i] = 0x00; } if (isNegative) { asByte[0] = 0x80; // Return a negative zero } else { asByte[0] = 0x00; // Return a positive zero } } else if (exponent > 130) { // A normal number // The next 2 lines assumes mantissa is normalised (as is done above) exponent -= 130; { // This code does a byte by byte left shift by 2 bits (painful) uShort i = SIZE_MODCOMP_FLOAT-1; uChar msbits = asByte[i] >> 6; asByte[i] <<= 2; i--; while (i > 1) { uChar prevMsbits = msbits; msbits = asByte[i] >> 6; asByte[i] <<= 2; asByte[i] |= prevMsbits; i--; } asByte[1] = (asByte[1] << 2) | msbits; } if ((exponent & 0x0001) == 0) { asByte[1] &= 0x7f; } else { asByte[1] |= 0x80; } asByte[0] = (uChar) exponent >> 1; if (isNegative) asByte[0] |= 0x80; // The IEEE format effectively has two more bits in the mantissa than // the Modcomp format. The least significant bits are always set to // zero. Numerically it would be better to set them randomly to zero or // one but I do not think the performance degradation warrents this. } else { // A subnormal number if (exponent < 129) { // need to shift mantissa to the right Short shift = 129-exponent; // Do a series of 8 or less bit right shifts (the painful way). while (shift > 0) { // Exponent too small is already dealt with const Short thisShift = shift > 8 ? 8 : shift; const Short compShift = 8-thisShift; asByte[1] &= 0x3f; uChar lsbits = asByte[1] << compShift; asByte[1] >>= thisShift; uShort i = 2; while (i < SIZE_MODCOMP_FLOAT-1) { uChar prevLsbits = lsbits; lsbits = asByte[i] << compShift; asByte[i] >>= thisShift; asByte[i] |= prevLsbits; i++; } asByte[SIZE_MODCOMP_FLOAT-1] >>= thisShift; asByte[SIZE_MODCOMP_FLOAT-1] |= lsbits; shift -= 8; } } else if (exponent == 130) { // need to shift mantissa to the left // This code does a (painful) byte by byte left shift by one bit. uShort i = SIZE_MODCOMP_FLOAT-1; Bool msbIsSet = (asByte[i] & (0x80)) > 0 ? True : False; asByte[i] <<= 1; i--; while (i > 1) { Bool prevMsbIsSet = msbIsSet; msbIsSet = (asByte[i] & (0x80)) > 0 ? True : False; asByte[i] <<= 1; if (prevMsbIsSet) asByte[i] |= 0x01; i--; } asByte[1] = (asByte[1] & 0x3f) << 1; if (msbIsSet) asByte[1] |= 0x01; } else { // exponent == 129. No need to shift the mantissa asByte[1] &= 0x3f; } if (isNegative) {// the exponent is always zero, so just fixup the sign asByte[0] = 0x80; } else { asByte[0] = 0x00; } } } retval += CanonicalConversion::toLocal(*to, asByte); } return retval; } // Modcomp has one more bit in the exponent than IEEE and because it does not // have an implicit bit two less bits in the Mantissa. It does not have any // special numbers like NaN or Infinity. The Modcomp is big-endian (like Sun's) size_t ModcompConversion::toLocal (Double* to, const void* from, size_t nr) { DebugAssert(sizeof(Short) >= 2, AipsError); uChar asByte[SIZE_MODCOMP_DOUBLE]; size_t retval = 0; const uChar* data = (const uChar*) from; for (const Double* const last = to + nr; to < last; to++) { // Copy the data to temporary buffer for (uShort i = 0; i < SIZE_MODCOMP_DOUBLE; i++) { asByte[i] = *data; data++; } // If the number is negative then its positive value is the twos complement const Bool isNegative = ((asByte[0] & 0x80) > 0) ? True : False; if (isNegative) { // This code takes the twos complement uShort i = 0; while (i < SIZE_MODCOMP_DOUBLE) { asByte[i] = ~asByte[i]; i++; } i--; asByte[i]++; while (asByte[i] == 0 && i > 0) { i--; asByte[i]++; } } Bool isZero = (asByte[1] & 0x3f) == 0x00; for (uShort i = 2; i < SIZE_MODCOMP_DOUBLE; i++) { isZero = (asByte[i] == 0x00) && isZero; } if (isZero) { // Early exit if the number is zero. // I am told this is common in the data so it should speed things up. for (uShort k = 1; k < SIZE_MODCOMP_DOUBLE; k++) { asByte[k] = 0x00; } if (isNegative) { // Return a signed zero asByte[0] = 0x80; } else { asByte[0] = 0x00; } } else { // Number is not zero Short exponent = ((asByte[0] & 0x7f) << 2) | ((asByte[1] & 0xc0) >> 6); while ((asByte[1] & 0x20) == 0) { // See if the number is unnormalised { // If so try to normalise it. // This code does a byte by byte left shift by one bit (painful). uShort i = SIZE_MODCOMP_DOUBLE-1; Bool msbIsSet = (asByte[i] & (0x80)) > 0 ? True : False; asByte[i] <<= 1; i--; while (i > 1) { Bool prevMsbIsSet = msbIsSet; msbIsSet = (asByte[i] & (0x80)) > 0 ? True : False; asByte[i] <<= 1; if (prevMsbIsSet) asByte[i] |= 0x01; i--; } asByte[1] = ((asByte[1] & 0x3f) << 1) | (asByte[1] & 0xc0); if (msbIsSet) asByte[1] |= 0x01; } exponent--; // Because the exponent can go negative it must be signed } // In IEEE double precision the exponent is 11 bits, whereas the // Modcomp's is only 9 bits. So we never have to worry about: // * Numbers that are too big (overflow). Set to infinity. // * Numbers that are too small (underflow). Set to zero or generate // a subnormal number. // Hence, unlike the single precision code, every Modcomp double // precision number can be represented as a 'normal' IEEE double // precision number. // The mantissa of the Modcomp double precision number is 54 bits, // whereas it is 52 bits in the IEEE format. But only one bit of // precision is lost in the conversion because the IEEE format has an // implicit one at the beginning of the mantissa whereas the Modcomp has // an explicit one (that wastes a bit). // The next few lines assumes mantissa is normalised (as is done above) exponent += 766; { // This code does a byte by byte right shift by 1-bit (painful) for (uShort i = SIZE_MODCOMP_DOUBLE-1; i > 1; i--) { asByte[i] >>= 1; asByte[i] |= (asByte[i-1] & 0x01) << 7; } asByte[1] >>= 1; } asByte[1] &= 0x0f; asByte[1] |= uChar(exponent & 0x000f) << 4; asByte[0] = uChar(exponent >> 4); if (isNegative) { asByte[0] |= 0x80; } else { asByte[0] &= 0x7f; } // The IEEE format effectively has two more bits in the mantissa than // the Modcomp format. The least significant bits are always set to // zero. Numerically it would be better to set them randomly to zero or // one but I do not think the performance degradation warrents this. } retval += CanonicalConversion::toLocal(*to, asByte); } return retval; } size_t ModcompConversion::fromLocal(void* to, const Float* from, size_t nr) { // Dummy statements to suppress compiler warnings about unused variables if (nr == 0) {} if (from == 0) {} if (to != 0) {} throw(AipsError("ModcompConversion::fromLocal(Float&, const void*) - " "Cannot convert floating point numbers to Modcomp format")); return 0; } size_t ModcompConversion::fromLocal(void* to, const Double* from, size_t nr) { // Dummy statements to suppress compiler warnings about unused variables if (nr == 0) {} if (from == 0) {} if (to != 0) {} throw(AipsError("ModcompConversion::fromLocal(Double&, const void*) - " "Cannot convert floating point numbers to Modcomp format")); return 0; } // size_t ModcompConversion::fromLocal (void* to, const float* from, // size_t nr) // { // assert (sizeof(uInt) == 4); // assert (sizeof(float) == 4); // char* data = (char*)to; // const float* last = from + nr; // while (from < last) { // uInt value; // value = *(uInt*)from; // uInt exponent = (value & 0x7f800000) >> 23; // if (exponent == 0) { // value = 0; // }else{ // exponent += 2; // if (exponent > 255) { // value |= 0x7fffffff; // }else{ // value = (value & 0x807fffff) | (exponent << 23); // } // } // moveFloat (data, &value); // data += 4; // from++; // } // } // size_t ModcompConversion::fromLocal (void* to, const double* from, // size_t nr) // { // assert (sizeof(uInt) == 4); // assert (sizeof(double) == 8); // char* data = (char*)to; // const double* last = from + nr; // while (from < last) { // uInt value, rest; // #if defined(AIPS_LITTLE_ENDIAN) // rest = ((uInt*)from)[0]; // value = ((uInt*)from)[1]; // #else // value = ((uInt*)from)[0]; // rest = ((uInt*)from)[1]; // #endif // uInt exponent = (value & 0x7ff00000) >> 20; // exponent -= 1022-128; // if (exponent <= 0) { // exponent = 0; // value = 0; // rest = 0; // }else{ // if (exponent > 255) { // value |= 0x7fffffff; // rest = 0xffffffff; // }else{ // value = (value & 0x80000000) | (exponent << 23) // | ((value << 3) & 0x007fffff) | (rest >> 29); // rest <<= 3; // } // } // moveFloat (data, &value); // moveFloat (data+4, &rest); // data += 8; // from++; // } // } // Local Variables: // compile-command: "gmake OPTLIB=1 ModcompConversion; cd test; gmake tModcompConversion" // End: } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/OS/ModcompConversion.h000066400000000000000000000273211476623553700203760ustar00rootroot00000000000000//# ModCompConversion.h: A class with static functions to convert ModComp format //# Copyright (C) 1998,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MODCOMPCONVERSION_H #define CASA_MODCOMPCONVERSION_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Define the canonical sizes of the built-in data types. // These are the same for all machine architectures. #define SIZE_MODCOMP_CHAR 1 #define SIZE_MODCOMP_UCHAR 1 #define SIZE_MODCOMP_SHORT 2 #define SIZE_MODCOMP_USHORT 2 #define SIZE_MODCOMP_INT 4 #define SIZE_MODCOMP_UINT 4 #define SIZE_MODCOMP_INT64 4 #define SIZE_MODCOMP_UINT64 4 #define SIZE_MODCOMP_FLOAT 4 #define SIZE_MODCOMP_DOUBLE 8 // Define for each data format if a conversion is needed from the ModComp // format to the local format (or vice-versa). This allows for optimizations // in, for example, AipsIO. // The ModComp format is ASCII for strings, a proprietary floating point format // and 2-complement for integers (all most significant bit first) with the // lengths as shown above. // Change the definitions below if new architectures (whith different lengths // for these integer types) are being used. #define CONVERT_MODCOMP_CHAR 0 #define CONVERT_MODCOMP_UCHAR 0 // Conversion is needed for little endian architectures (like DEC and Intel), // because the bytes have to be swapped (thus not for data with length 1). #if defined(AIPS_LITTLE_ENDIAN) #define CONVERT_MODCOMP_SHORT 1 #define CONVERT_MODCOMP_USHORT 1 #define CONVERT_MODCOMP_INT 1 #define CONVERT_MODCOMP_UINT 1 #define CONVERT_MODCOMP_INT64 1 #define CONVERT_MODCOMP_UINT64 1 #else // Conversion is not needed for integers if the local lengths for Shorts and // Ints is 2,4 respectively. #define CONVERT_MODCOMP_SHORT 0 #define CONVERT_MODCOMP_USHORT 0 #define CONVERT_MODCOMP_INT 0 #define CONVERT_MODCOMP_UINT 0 #define CONVERT_MODCOMP_INT64 1 #define CONVERT_MODCOMP_UINT64 1 #endif // Conversion is always needed for floating point data. #define CONVERT_MODCOMP_FLOAT 1 #define CONVERT_MODCOMP_DOUBLE 1 // Static functions to convert Modcomp numeric formats // // // // // This class contains static toLocal functions to convert data from Modcomp // format to local format and vice-versa. // // The functions work on both big-endian and little-endian machines. They // convert between Modcomp 2-byte integers and Shorts (or uShorts) and Modcomp // 4-byte integers and Ints (or uInts, Int64s or uInt64s). // // It is currently not possible to convert floating point numbers from local // format to Modcomp. Attempting to do so will throw an exception (AipsError). // // // The VLA data is stored using Modcomp numeric data formats and needs to be // converted to the local format to be manipulated. // // //
        46. Support conversion of floating point data to Modcomp format //
        47. Support data type long double. // class ModcompConversion { public: // Convert one value from Modcomp format to local format. // The from and to buffer should not overlap. // static size_t toLocal(Char& to, const void* from); static size_t toLocal(uChar& to, const void* from); static size_t toLocal(Short& to, const void* from); static size_t toLocal(uShort& to, const void* from); static size_t toLocal(Int& to, const void* from); static size_t toLocal(uInt& to, const void* from); static size_t toLocal(Int64& to, const void* from); static size_t toLocal(uInt64& to, const void* from); static size_t toLocal(Float& to, const void* from); static size_t toLocal(Double& to, const void* from); // // Convert nr values from Modcomp format to local format. // The from and to buffer should not overlap. // static size_t toLocal(Char* to, const void* from, size_t nr); static size_t toLocal(uChar* to, const void* from, size_t nr); static size_t toLocal(Short* to, const void* from, size_t nr); static size_t toLocal(uShort* to, const void* from, size_t nr); static size_t toLocal(Int* to, const void* from, size_t nr); static size_t toLocal(uInt* to, const void* from, size_t nr); static size_t toLocal(Int64* to, const void* from, size_t nr); static size_t toLocal(uInt64* to, const void* from, size_t nr); static size_t toLocal(Float* to, const void* from, size_t nr); static size_t toLocal(Double* to, const void* from, size_t nr); // // Convert one value from local format to Modcomp format. The from and to // buffer should not overlap. The floating point functions will throw // exceptions as they are not implemented yet. // static size_t fromLocal(void* to, Char from); static size_t fromLocal(void* to, uChar from); static size_t fromLocal(void* to, Short from); static size_t fromLocal(void* to, uShort from); static size_t fromLocal(void* to, Int from); static size_t fromLocal(void* to, uInt from); static size_t fromLocal(void* to, Int64 from); static size_t fromLocal(void* to, uInt64 from); static size_t fromLocal(void* to, Float from); static size_t fromLocal(void* to, Double from); // // Convert nr values from local format to Modcomp format. The from and to // buffer should not overlap. The floating point functions will throw // exceptions as they are not implemented yet. // static size_t fromLocal(void* to, const Char* from, size_t nr); static size_t fromLocal(void* to, const uChar* from, size_t nr); static size_t fromLocal(void* to, const Short* from, size_t nr); static size_t fromLocal(void* to, const uShort* from, size_t nr); static size_t fromLocal(void* to, const Int* from, size_t nr); static size_t fromLocal(void* to, const uInt* from, size_t nr); static size_t fromLocal(void* to, const Int64* from, size_t nr); static size_t fromLocal(void* to, const uInt64* from, size_t nr); static size_t fromLocal(void* to, const Float* from, size_t nr); static size_t fromLocal(void* to, const Double* from, size_t nr); // private: // This class should not be constructed // (so declare the constructor private). ModcompConversion(); }; inline size_t ModcompConversion::toLocal(Char& to, const void* from) { return CanonicalConversion::toLocal(to, from); } inline size_t ModcompConversion::toLocal(uChar& to, const void* from) { return CanonicalConversion::toLocal(to, from); } inline size_t ModcompConversion::toLocal(Short& to, const void* from) { return CanonicalConversion::toLocal(to, from); } inline size_t ModcompConversion::toLocal(uShort& to, const void* from) { return CanonicalConversion::toLocal(to, from); } inline size_t ModcompConversion::toLocal(Int& to, const void* from) { return CanonicalConversion::toLocal(to, from); } inline size_t ModcompConversion::toLocal(uInt& to, const void* from) { return CanonicalConversion::toLocal(to, from); } inline size_t ModcompConversion::toLocal(Int64& to, const void* from) { Int tmp; size_t res = toLocal (tmp, from); to = tmp; return res; } inline size_t ModcompConversion::toLocal(uInt64& to, const void* from) { uInt tmp; size_t res = toLocal (tmp, from); to = tmp; return res; } inline size_t ModcompConversion::toLocal(Float& to, const void* from) { return ModcompConversion::toLocal(&to, from, 1u); } inline size_t ModcompConversion::toLocal(Double& to, const void* from) { return ModcompConversion::toLocal(&to, from, 1u); } inline size_t ModcompConversion::toLocal(Char* to, const void* from, size_t nr) { return CanonicalConversion::toLocalChar(to, from, nr); } inline size_t ModcompConversion::toLocal(uChar* to, const void* from, size_t nr) { return CanonicalConversion::toLocalUChar(to, from, nr); } inline size_t ModcompConversion::toLocal(Short* to, const void* from, size_t nr) { return CanonicalConversion::toLocalShort(to, from, nr); } inline size_t ModcompConversion::toLocal(uShort* to, const void* from, size_t nr) { return CanonicalConversion::toLocalUShort(to, from, nr); } inline size_t ModcompConversion::toLocal(Int* to, const void* from, size_t nr) { return CanonicalConversion::toLocalInt(to, from, nr); } inline size_t ModcompConversion::toLocal(uInt* to, const void* from, size_t nr) { return CanonicalConversion::toLocalUInt(to, from, nr); } inline size_t ModcompConversion::fromLocal(void* to, Char from) { return CanonicalConversion::fromLocal(to, from); } inline size_t ModcompConversion::fromLocal(void* to, uChar from) { return CanonicalConversion::fromLocal(to, from); } inline size_t ModcompConversion::fromLocal(void* to, Short from) { return CanonicalConversion::fromLocal (to, from); } inline size_t ModcompConversion::fromLocal(void* to, uShort from) { return CanonicalConversion::fromLocal(to, from); } inline size_t ModcompConversion::fromLocal(void* to, Int from) { return CanonicalConversion::fromLocal (to, from); } inline size_t ModcompConversion::fromLocal(void* to, uInt from) { return CanonicalConversion::fromLocal (to, from); } inline size_t ModcompConversion::fromLocal(void* to, Int64 from) { return CanonicalConversion::fromLocal (to, (Int) from); } inline size_t ModcompConversion::fromLocal(void* to, uInt64 from) { return CanonicalConversion::fromLocal (to, (uInt) from); } inline size_t ModcompConversion::fromLocal(void* to, Float from) { return ModcompConversion::fromLocal(to, &from, 1u); } inline size_t ModcompConversion::fromLocal(void* to, Double from) { return ModcompConversion::fromLocal(to, &from, 1u); } inline size_t ModcompConversion::fromLocal(void* to, const Char* from, size_t nr) { return CanonicalConversion::fromLocalChar(to, from, nr); } inline size_t ModcompConversion::fromLocal(void* to, const uChar* from, size_t nr){ return CanonicalConversion::fromLocalUChar(to, from, nr); } inline size_t ModcompConversion::fromLocal(void* to, const Short* from, size_t nr){ return CanonicalConversion::fromLocalShort(to, from, nr); } inline size_t ModcompConversion::fromLocal(void* to, const uShort* from,size_t nr){ return CanonicalConversion::fromLocalUShort(to, from, nr); } inline size_t ModcompConversion::fromLocal(void* to, const Int* from, size_t nr) { return CanonicalConversion::fromLocalInt(to, from, nr); } inline size_t ModcompConversion::fromLocal(void* to, const uInt* from, size_t nr) { return CanonicalConversion::fromLocalUInt(to, from, nr); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/OS/ModcompDataConversion.cc000066400000000000000000000230101476623553700213150ustar00rootroot00000000000000//# ModcompDataConversion.cc: A DataConversion class to convert Modcomp format. //# Copyright (C) 1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ModcompDataConversion::~ModcompDataConversion(){ } size_t ModcompDataConversion::toLocal (Char& to, const void* from) const { return ModcompConversion::toLocal (to, from); } size_t ModcompDataConversion::toLocal (uChar& to, const void* from) const { return ModcompConversion::toLocal (to, from); } size_t ModcompDataConversion::toLocal (Short& to, const void* from) const { return ModcompConversion::toLocal (to, from); } size_t ModcompDataConversion::toLocal (uShort& to, const void* from) const { return ModcompConversion::toLocal (to, from); } size_t ModcompDataConversion::toLocal (Int& to, const void* from) const { return ModcompConversion::toLocal (to, from); } size_t ModcompDataConversion::toLocal (uInt& to, const void* from) const { return ModcompConversion::toLocal (to, from); } size_t ModcompDataConversion::toLocal (Int64& to, const void* from) const { return ModcompConversion::toLocal (to, from); } size_t ModcompDataConversion::toLocal (uInt64& to, const void* from) const { return ModcompConversion::toLocal (to, from); } size_t ModcompDataConversion::toLocal (Float& to, const void* from) const { return ModcompConversion::toLocal (to, from); } size_t ModcompDataConversion::toLocal (Double& to, const void* from) const { return ModcompConversion::toLocal (to, from); } size_t ModcompDataConversion::toLocal (Char* to, const void* from, size_t nr) const { return ModcompConversion::toLocal (to, from, nr); } size_t ModcompDataConversion::toLocal (uChar* to, const void* from, size_t nr) const { return ModcompConversion::toLocal (to, from, nr); } size_t ModcompDataConversion::toLocal (Short* to, const void* from, size_t nr) const { return ModcompConversion::toLocal (to, from, nr); } size_t ModcompDataConversion::toLocal (uShort* to, const void* from, size_t nr) const { return ModcompConversion::toLocal (to, from, nr); } size_t ModcompDataConversion::toLocal (Int* to, const void* from, size_t nr) const { return ModcompConversion::toLocal (to, from, nr); } size_t ModcompDataConversion::toLocal (uInt* to, const void* from, size_t nr) const { return ModcompConversion::toLocal (to, from, nr); } size_t ModcompDataConversion::toLocal (Int64* to, const void* from, size_t nr) const { return ModcompConversion::toLocal (to, from, nr); } size_t ModcompDataConversion::toLocal (uInt64* to, const void* from, size_t nr) const { return ModcompConversion::toLocal (to, from, nr); } size_t ModcompDataConversion::toLocal (Float* to, const void* from, size_t nr) const { return ModcompConversion::toLocal (to, from, nr); } size_t ModcompDataConversion::toLocal (Double* to, const void* from, size_t nr) const { return ModcompConversion::toLocal (to, from, nr); } size_t ModcompDataConversion::fromLocal (void* to, Char from) const { return ModcompConversion::fromLocal (to, from); } size_t ModcompDataConversion::fromLocal (void* to, uChar from) const { return ModcompConversion::fromLocal (to, from); } size_t ModcompDataConversion::fromLocal (void* to, Short from) const { return ModcompConversion::fromLocal (to, from); } size_t ModcompDataConversion::fromLocal (void* to, uShort from) const { return ModcompConversion::fromLocal (to, from); } size_t ModcompDataConversion::fromLocal (void* to, Int from) const { return ModcompConversion::fromLocal (to, from); } size_t ModcompDataConversion::fromLocal (void* to, uInt from) const { return ModcompConversion::fromLocal (to, from); } size_t ModcompDataConversion::fromLocal (void* to, Int64 from) const { return ModcompConversion::fromLocal (to, from); } size_t ModcompDataConversion::fromLocal (void* to, uInt64 from) const { return ModcompConversion::fromLocal (to, from); } size_t ModcompDataConversion::fromLocal (void* to, Float from) const { return ModcompConversion::fromLocal (to, from); } size_t ModcompDataConversion::fromLocal (void* to, Double from) const { return ModcompConversion::fromLocal (to, from); } size_t ModcompDataConversion::fromLocal (void* to, const Char* from, size_t nr) const { return ModcompConversion::fromLocal (to, from, nr); } size_t ModcompDataConversion::fromLocal (void* to, const uChar* from, size_t nr) const { return ModcompConversion::fromLocal (to, from, nr); } size_t ModcompDataConversion::fromLocal (void* to, const Short* from, size_t nr) const { return ModcompConversion::fromLocal (to, from, nr); } size_t ModcompDataConversion::fromLocal (void* to, const uShort* from, size_t nr) const { return ModcompConversion::fromLocal (to, from, nr); } size_t ModcompDataConversion::fromLocal (void* to, const Int* from, size_t nr) const { return ModcompConversion::fromLocal (to, from, nr); } size_t ModcompDataConversion::fromLocal (void* to, const uInt* from, size_t nr) const { return ModcompConversion::fromLocal (to, from, nr); } size_t ModcompDataConversion::fromLocal (void* to, const Int64* from, size_t nr) const { return ModcompConversion::fromLocal (to, from, nr); } size_t ModcompDataConversion::fromLocal (void* to, const uInt64* from, size_t nr) const { return ModcompConversion::fromLocal (to, from, nr); } size_t ModcompDataConversion::fromLocal (void* to, const Float* from, size_t nr) const { return ModcompConversion::fromLocal (to, from, nr); } size_t ModcompDataConversion::fromLocal (void* to, const Double* from, size_t nr) const { return ModcompConversion::fromLocal (to, from, nr); } Bool ModcompDataConversion::canCopy (const Char*) const { return (CONVERT_MODCOMP_CHAR == 0); } Bool ModcompDataConversion::canCopy (const uChar*) const { return (CONVERT_MODCOMP_UCHAR == 0); } Bool ModcompDataConversion::canCopy (const Short*) const { return (CONVERT_MODCOMP_SHORT == 0); } Bool ModcompDataConversion::canCopy (const uShort*) const { return (CONVERT_MODCOMP_USHORT == 0); } Bool ModcompDataConversion::canCopy (const Int*) const { return (CONVERT_MODCOMP_INT == 0); } Bool ModcompDataConversion::canCopy (const uInt*) const { return (CONVERT_MODCOMP_UINT == 0); } Bool ModcompDataConversion::canCopy (const Int64*) const { return (CONVERT_MODCOMP_INT64 == 0); } Bool ModcompDataConversion::canCopy (const uInt64*) const { return (CONVERT_MODCOMP_UINT64 == 0); } Bool ModcompDataConversion::canCopy (const Float*) const { return (CONVERT_MODCOMP_FLOAT == 0); } Bool ModcompDataConversion::canCopy (const Double*) const { return (CONVERT_MODCOMP_DOUBLE == 0); } uInt ModcompDataConversion::externalSize (const Char*) const { return SIZE_MODCOMP_CHAR; } uInt ModcompDataConversion::externalSize (const uChar*) const { return SIZE_MODCOMP_UCHAR; } uInt ModcompDataConversion::externalSize (const Short*) const { return SIZE_MODCOMP_SHORT; } uInt ModcompDataConversion::externalSize (const uShort*) const { return SIZE_MODCOMP_USHORT; } uInt ModcompDataConversion::externalSize (const Int*) const { return SIZE_MODCOMP_INT; } uInt ModcompDataConversion::externalSize (const uInt*) const { return SIZE_MODCOMP_UINT; } uInt ModcompDataConversion::externalSize (const Int64*) const { return SIZE_MODCOMP_INT64; } uInt ModcompDataConversion::externalSize (const uInt64*) const { return SIZE_MODCOMP_UINT64; } uInt ModcompDataConversion::externalSize (const Float*) const { return SIZE_MODCOMP_FLOAT; } uInt ModcompDataConversion::externalSize (const Double*) const { return SIZE_MODCOMP_DOUBLE; } // Local Variables: // compile-command: "gmake OPTLIB=1 ModcompDataConversion" // End: } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/OS/ModcompDataConversion.h000066400000000000000000000160221476623553700211640ustar00rootroot00000000000000//# ModcompDataConversion.h: A DataConversion class to convert Modcomp format. //# Copyright (C) 1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MODCOMPDATACONVERSION_H #define CASA_MODCOMPDATACONVERSION_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // A DataConversion class to convert between Modcomp format. // // // // // This class is a specialization of the abstract base class // DataConversion. // It contains functions to convert data from/to the Modcomp format // using the static functions in class // ModcompConversion. // // // See example in class DataConversion. // // // This class is an addition to // ModcompConversion // to be able to use the conversion functions in a polymorphic way. // // //
        48. Support data type long Double. // class ModcompDataConversion :public DataConversion { public: // Construct the object. ModcompDataConversion(); virtual ~ModcompDataConversion(); // Convert one value from Modcomp format to local format. // The from and to buffer should not overlap. // virtual size_t toLocal (Char& to, const void* from) const; virtual size_t toLocal (uChar& to, const void* from) const; virtual size_t toLocal (Short& to, const void* from) const; virtual size_t toLocal (uShort& to, const void* from) const; virtual size_t toLocal (Int& to, const void* from) const; virtual size_t toLocal (uInt& to, const void* from) const; virtual size_t toLocal (Int64& to, const void* from) const; virtual size_t toLocal (uInt64& to, const void* from) const; virtual size_t toLocal (Float& to, const void* from) const; virtual size_t toLocal (Double& to, const void* from) const; // // Convert nr values from Modcomp format to local format. // The from and to buffer should not overlap. // virtual size_t toLocal (Char* to, const void* from, size_t nr) const; virtual size_t toLocal (uChar* to, const void* from, size_t nr) const; virtual size_t toLocal (Short* to, const void* from, size_t nr) const; virtual size_t toLocal (uShort* to, const void* from, size_t nr) const; virtual size_t toLocal (Int* to, const void* from, size_t nr) const; virtual size_t toLocal (uInt* to, const void* from, size_t nr) const; virtual size_t toLocal (Int64* to, const void* from, size_t nr) const; virtual size_t toLocal (uInt64* to, const void* from, size_t nr) const; virtual size_t toLocal (Float* to, const void* from, size_t nr) const; virtual size_t toLocal (Double* to, const void* from, size_t nr) const; // // Convert one value from local format to Modcomp format. // The from and to buffer should not overlap. // virtual size_t fromLocal (void* to, Char from) const; virtual size_t fromLocal (void* to, uChar from) const; virtual size_t fromLocal (void* to, Short from) const; virtual size_t fromLocal (void* to, uShort from) const; virtual size_t fromLocal (void* to, Int from) const; virtual size_t fromLocal (void* to, uInt from) const; virtual size_t fromLocal (void* to, Int64 from) const; virtual size_t fromLocal (void* to, uInt64 from) const; virtual size_t fromLocal (void* to, Float from) const; virtual size_t fromLocal (void* to, Double from) const; // // Convert nr values from local format to ModComp format. // The from and to buffer should not overlap. // virtual size_t fromLocal (void* to, const Char* from, size_t nr) const; virtual size_t fromLocal (void* to, const uChar* from, size_t nr) const; virtual size_t fromLocal (void* to, const Short* from, size_t nr) const; virtual size_t fromLocal (void* to, const uShort* from, size_t nr) const; virtual size_t fromLocal (void* to, const Int* from, size_t nr) const; virtual size_t fromLocal (void* to, const uInt* from, size_t nr) const; virtual size_t fromLocal (void* to, const Int64* from, size_t nr) const; virtual size_t fromLocal (void* to, const uInt64* from, size_t nr) const; virtual size_t fromLocal (void* to, const Float* from, size_t nr) const; virtual size_t fromLocal (void* to, const Double* from, size_t nr) const; // // Determine if the data for a data type can be simply copied, thus // if no conversion is needed. // virtual Bool canCopy (const Char*) const; virtual Bool canCopy (const uChar*) const; virtual Bool canCopy (const Short*) const; virtual Bool canCopy (const uShort*) const; virtual Bool canCopy (const Int*) const; virtual Bool canCopy (const uInt*) const; virtual Bool canCopy (const Int64*) const; virtual Bool canCopy (const uInt64*) const; virtual Bool canCopy (const Float*) const; virtual Bool canCopy (const Double*) const; // // Get the external size of the data type. // virtual uInt externalSize (const Char*) const; virtual uInt externalSize (const uChar*) const; virtual uInt externalSize (const Short*) const; virtual uInt externalSize (const uShort*) const; virtual uInt externalSize (const Int*) const; virtual uInt externalSize (const uInt*) const; virtual uInt externalSize (const Int64*) const; virtual uInt externalSize (const uInt64*) const; virtual uInt externalSize (const Float*) const; virtual uInt externalSize (const Double*) const; // }; inline ModcompDataConversion::ModcompDataConversion() { } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/OS/Mutex.h000066400000000000000000000025461476623553700160360ustar00rootroot00000000000000//# Mutex.h: Classes to handle mutexes and (un)locking //# Copyright (C) 2011,2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MUTEX_H #define CASA_MUTEX_H #error This header has been removed: use std::mutex and its related functionality for synchronisation #endif casacore-3.7.1/casa/OS/OMP.cc000066400000000000000000000026251476623553700155230ustar00rootroot00000000000000//# Copyright (C) 1993,1994,1995,1996,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include namespace casacore { namespace OMP { uInt nMaxThreads() { #ifdef _OPENMP return (omp_get_num_threads() > 1) ? 1 : omp_get_max_threads(); #else return 1; #endif } } } casacore-3.7.1/casa/OS/OMP.h000066400000000000000000000056671476623553700153760ustar00rootroot00000000000000//# Copyright (C) 1993,1994,1995,1996,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef CASA_OS_OMP_H #define CASA_OS_OMP_H #include #ifdef _OPENMP #include #endif namespace casacore { namespace OMP { // Get the maximum number of threads. // OpenMP sets it to the env.var. OMP_NUM_THREADS. If undefined, it is // the number of cores. // If OpenMP is not used, 1 is returned. inline uInt maxThreads() { #ifdef _OPENMP return omp_get_max_threads(); #else return 1; #endif } // Backward uInt nMaxThreads(); // Set the number of threads to use. Note it can be overridden // for a parallel section by 'omp parallel num_threads(n)'. // Nothing is done if OpenMP is not used. #ifdef _OPENMP inline void setNumThreads (uInt n) { omp_set_num_threads (n); } #else inline void setNumThreads (uInt) {} #endif // Get the number of threads used in a parallel piece of code. // If OpenMP is not used, 1 is returned. inline uInt numThreads() { #ifdef _OPENMP return omp_get_num_threads(); #else return 1; #endif } // Get the thread number (0 till numThreads). // If OpenMP is not used, 0 is returned. inline uInt threadNum() { #ifdef _OPENMP return omp_get_thread_num(); #else return 0; #endif } // Set if nested parallel sections are possible or not. // Nothing is done if OpenMP is not used. #ifdef _OPENMP inline void setNested (Bool nest) { omp_set_nested (nest); } #else inline void setNested (Bool) {} #endif // Test if nested parallel sections are possible. // If OpenMP is not used, false is returned. inline bool nested() { #ifdef _OPENMP return omp_get_nested(); #else return false; #endif } } // end namespace } // end namespace #endif casacore-3.7.1/casa/OS/OS_1.gif000066400000000000000000000342761476623553700160200ustar00rootroot00000000000000GIF87aìœðÿÿÿ,ìœþŒ©Ëí£œ´Ú‹³Þ¼û†âH–扦êʶî ÇòL×öçúÎ÷þ ‡Ä¢ñˆL*—̦ó J§ÔªõŠÍj·\ à ‹Çä²ùŒN«×ì¶û wçôºý~à÷y¾ÿØ£'HGXˆ˜¨¸ØqÈxåø(9I©Y u‰¹ÉÙ‰¥é™JZj*4zŠªÊÚêj“Ú"öðæRKûÊÛë»±»’›Ë@\LhŒüËÜ윰œB< fpp}y]K‡\­-Î î<ý¬¾Þš~2­ç->/ÿmßoo.7®ïÿ¯†;v nX4p êÝ{(/ßC€÷Iô7Q ÁþO!$¡P[8x##ñ›xQN²’ôþ‹ñ±£Ì™wbŠP‘aË…uŠ´‡à\µ”.m†0J3©R+H?äD‰Ï'µ“ó~B­z’êKM—zýº¤k£l?s$÷´OP–ßÌêÜúB,عtÈ­Ûà.Þ½|eèí«°àÁAþ 6L8±b/‹= n 9r^ÉŒÛŹŒ9³æÍœ;s¦,ͳèѤK›Nãê1hh«ßµ¶ ºNì׳)צ{›KnÛ¯ö¦°[KðÈÃWzœéoÉ7Ÿù|JtçËoV0=JvÇáXÀ•~Ävƒã”÷âóÕù¹ë×µWò¾‚P–õ¦2Ìç¶ç“øKþù7óo€Ø™tŸD88 FüVxN9耀ºìG ~;=U’EEíÔ„„2bˆ"ŽØFjò„Ñ…å E`~7!„xÈ …¸Ö‹‡t·Ò6ºXUƒ23ä6Ú•ÉŠ:¶ÔE*>$|EV6e`ª™W†oY´CäT´¥yUb€%+e2&•iNp¦G&®yA›îÁ)§u²s§3±Œ"—ËÀ8Y<#ì§,oÂ)¢lÒ2™cjcÔŽÖ1ZŒ¡í(š(¦¶D8••L~ZQ`Q…Ú%6¦Ze*2}¨ *ª v*ª5²ÂªBž7ˆk®ºîz†¦›²öh°Àž*é©ÄÊŠþl±A¡ô¨> °zl²¬N›¬°Ã +Ì¡¾Rºm¥ÃF n¬ÆYm¹ãš[,µäžk,´Ðb‹­ºáJ£m·ÞÚÛÐZ#Å /¿ìŠ$¯»ù¶km¿Â.‹.Pç¦ëïn¶Â‚o„ŒpÂì2cË|Óþ l„ß;ù¢—âM¹½©d˜2ª¢;=+­‚—Eì4àžžª¤‚wçj´Ú\:©–oιÝf®-ðÅ÷|èï,-ÓÞl³Ç8Ó¼t¦»sî³ç¼7Lñ»ß¹ÅÑZ_=¼Ü#oRÆSg »¥¹;®ïµ´‹ÿ/7Ôg¿zÒ›;ïúG{J²ä•ãŽþÜ+ÿ2ýåO{ÞšÙö¸õ®ïeÓùâ±ò¹é[ØkàûHÀu°yÝW±g½·\ðvÏ“˜ð6E†cÎOkHà¢g¸½}K#þ3ží4º¦ucuHÓ ÷TW¿ñ=MCÄJÔv¿(i®„*<áÞæ72ýå͉ƒÛŸþ §øµþÝŠ„UûU‡ ((UéˆVjQK'FÓ-l‡=4šëò¥:ÖåovY¬!Üñ—?íçdolÒhA./‰R_ê¤Æ)i%m{D‡·x£=òï‡\ —\– ¯w„" YAˆÙ1MÌ–Ðú¾–]2\ÊËàõ\F?Ã}–ö¥ÚõBGù­Rš Zæ"wE*:ïJÝs£%U J~R• b2ñ·Hî9r”˜ãbßœI7›è¥lÕ¼&1ÅçFM²ò™ËC^!i)MrŠŒ#¤á-9Æ’h­"]ëÚ¸ÆxÆŽ“9t†l·¾¢ô ‚û$£Ò¦È£LB¤IpYÚ þ)Jw3yw|¤÷˜¥H$þ/‰œ§‰‡HŽ6«œBTŸ\Ji²`Úr‰]T©(,*Șu2€±”Ù™‰ÎvÚ/„ì %0™ØMH²ôAU5ÄÉVnTšîÛ)NßçS§f$|ƒL'™ФHf¦—l&KÕž3¬µ¼)sZS5Mð|Ô¼¨L•ÚÁTJu¬5«\yÊβzs­û_TÈ’><.«P½|a:ôT¿ÀÔ¦edB ª‰¶¤P¬bRÇ(.#ÞOg¤ •5=¯§¢Åâ0%°µÄ¦­­ZêU«€Rzý-«bcã…Y~nV¥ '*w9Ø7.µZtœæ;‹þ ØF(dz}Äè,k¹L‘n£‡Db–YUìZ•+‹Íªw ÁGzÖõŒÒ²'ÅZ@´zÒ­hÅ&wY[7máP²Ugt£K×®7¦íÝîÝà‹"kª‰uÀÍÑák6—Ö  -¦tW ]»š5¿òC {ÉêÞÿr~`ZC©ï5†3¦2ë;ÞŠpœ´"=úJ²î‡”®Põ–%}:ŸõÌ-Áv›ªÞ¦î·¼eM=èãWõ°®Å…¡%ʰÉ8¦ ´®" ì Ê&в¡D_œž:n˜¼†t‘],Îù•Y?@îW Wæ}˜qtnœKÅìZåzѨcv«Wk þVÆš3½2¶pL‡[Vª‚¹Ë²“½Ú<ÿôÎGˆm(ˆhRõÒ4ˆM´ -ÚO"ºcTîh:-=#Žözª;±QBÆYæqŒÇ;ã³¶˜ªŠg‰%Ñ`轚±N*II[Rô~׆îê1=RÿšÜ´"›`ÇÂ¥´‰£7aä©òtìÐz¬c†j™’‹d,‹û™Yœìcj½$(¶÷\T¤l·Þjå°j9mH¼[4 w‚½½R¾ÂQÖ\Æ-•Å{–XùtCò.›[ÛŒëvv¶Íi“K{µV—øFs |×gÇ5Í+ H­{ íÖ:ÙrÎ0‰å&òþÖ²:¼·õ«„eùs»[¿Í~v {íbIKoMâ>8Àšûº}%Oêq¦²oMôPÏüé [ç¢nÚŸ1çÞdJ¡>Ò%_~<2Ô­žâªëv×äÎzáŒèÌnÝàÖóªŸÈ¾–^¸ä 7Åp_÷ÌË”£k¦“ýâ{)þˆ[vT /vãA6âM¬ÝÆ ,Yx{>ËyƒÚæyÐôË}v†‡ÚÑZtú•4pÉ‚†øœ©'{u]òÈ#ZÆß^æ‰6!øâ,j•“ «ÙYðßÃ>bÌKß7¬¥ñÐýæ(Ò•î^“ãýéo‚\±“K.ÚZ¶µ©i×éÏKm|¹þßOûh~¾ùįsŸÜÃ÷9åjbEyÐW?ž†Wõvý—¤¶{Jäo8Ögü…_ÎF|AFËv_±DÒVQ]uts÷€\bÐ׆`ZÓ]â…: eeRöhz¶‡[óÆuè¶v ^X`éOè§aà6‚¯%bäêÇ7ˆU ¥Ä€7è€Cèv6h­ÇnE{Eägµ…OU—['OGÖG¨ƒ±6~ AG`ÏwxÃUÅCQeyö`Rä{sHQ ˆ„~Ø…– ÔEyço§GsšôUÊ6_p—ˆI—]‹g'+!LxDç&:{–ˆÄ#uÓi€ÖþnY8u†DNg†_;z” oD09¨-t~ÞÇFïT?Æ3•b‡8w1htzÅr&'†ÖÖ7ÖWT~¦hy¨p²x< v^Â'‹¸8€y7y &t7æ?ˆdjF„Èo™Ô4aÕ)UPvo,„ÉX*R˜P`Öp38z~Xÿ;ÛvY®çŽŽ¸|Õ—xôæ\¿sŒÙWsH|­d®áY¿TÎ7Œ ÆU„æs»·JæÔ‹£&9üæNø‡9‚([‚KøHHåÍBu¤Šè…Tý¥S)ˆÍ‡U«‹ªXŠý|¸Xa׌„h_wtˆ˜MÖhxЃ‡L|ˆþ7rØ<LJ€rE‹ù]锨'„…Åm ù‘"xEbôoÛ‚K€¤FŸ÷Š`B’÷øOœåp48!5”Ô‹é#Ÿs•>8}k×YBØwBB„[0“¨H.Æy¡·cUØ:ñ6…`È$¾uq=8˜£çOKÖq`É€Z—\IÚ4{Ir—#p˜QôÅf S‘T4 bC5–“†htQ†™‡s™m7¨i‡g)€ƒ$yï'@Èöu‡)˜)ò¥PÈ'?›¨‹X·y»hjÞ×u+óg«œ‹0¯yr*–]vw“9€É©=™›o¥WzWdãiÕ™èù‡×…‹fwþ³6ßWwÛžk–r?©^ݦ‡U¢žy 8uhgYU0XzºGhÜÈh “{¤÷œƒg^i•¿I'̧t|‰PEH“¾‰•ªW“¤D¡VÉ¡d8—þ ¢% ŽSg)ª¢ºò’&*x|¸ $Šýé+4JlBi£*”ª)9ª(> ’¶£]3¤«‰sEêHº•K§¤zÙ¤JpOê¤RzJ+j¥WÊ8Tº€ZÊ¥]*I^ ¦a#bJ¦eJŠfЦiŠgjʦmjn §qú¢rJ§u ŒvЧy:™zʧr ¤} ¨z¨ƒj§J¨‡ •ˆª¨aj¨‹ê¨°ö¨‘ʨ’J©\Ú¨•Šþ©Ê©›Z¤—Ê©Ÿz6 *ª¿8ª¥Š(žjª©º\ªÊªÚª¯:&¨ «³úR´j«á!«·ª«Þµ«½Ê¾ ¬Ä¬Ãº¹J¬ÇzŽÈª¬ì±¬ÍŠÎ ­^a¬ÑJ­™Y­×JÓŠ­Û „Üê­-ú­áê Ú*®åJ€æŠ® ™®ëziìê®7ú®ñ: ä*¯ØJ¯õZ­÷Š¯Ñª¯ûê¬ýê¯Ë °‹¬K°Äj°¬ «°¾Ê° «« ±«kI±£‹±+•ɱûˆç ²#²J²'k„"‹²+›¬ªÈ²/Ë–P ³3{¢FJ³7ët{г;}2˳?[¡&þ ´CÛ‡*K´G볋´Ck~Kë´Kª³O{XJµUkµW‹µY«µ[»+ÔQ£×(¬‡A™G¶€ñ°È¶q¶sêªZ¶}±¶C·H0·o ·cë¶jëµ=š¶Q·“ö£}[¬{6\«µ¨`¸‰ÛG¡¸‡Ëg{¯­¶w°©¹àj\ÏʶèèkµL¹«¹Ú–¤[ mºù÷”Û ‘ºR’¨ùX¬;61˹cª‘¾8ºÖZº¼{º `š¥šø#€R‚ûŸ” öÑfšç6ÉÛŠ®H>ÇÛu*½2jºÔ¼ hû!וHOX²;çBª) a"¾þâ«v™ëwS8a¼ÛF¾åˡ܋— åW‘eJ’Šèû¿D¡nfÆ_"¢ Àlz@ò½bÉ!“å¿#Ù~) ÀÌ J r\Àê Á,Áé³$ýËfLB+`Îëzäh½üYÙ‹¡Ì!õ±Ž#œ=ÒÎbbÁ %ÊÛ€*¢Ã9¬ ¾¬¾!Ä)‚ÄO—òÃÓ«YZáÁ<ÃaæÃN"À)TÄD|À¶ûÀJœ17¾í›²t{¾Wì½+¬¿(‚ÃM ÇøP[ŒÅ2ÜPBÅL|¾4ìÆ>‚¾yÇ£’CP² Öz‚ú«ÅlÌ¿,ÜÇjŒ!‡ÇòëºiÌþÁîXÉ" Ç\Äù¡Ç†gu‚!ì¼ðˈ&*üÃy,Ævì#,²$OüÂG\ÊFtÊWñÈÔÛ#FüÀ \M4Lg±Â"yJÌËH| »Ë’Ì”<Ìçv ü;Ƹü#ûÀÃÁ¼Ä…ŒÆ/ ¾jlÃI\!›\Ã×[‘üÇ®ÜȬLÀé,á{ÃálÅU6¿åLÈÿ¸Ñü$™lÅïÌΦ|½ó1Ä›ì²wªÊbù%ÆœÀX|Ìû,ÈWfÎQœ¿‰i› ÎD,¼l˨¬Ñ€<Ð-ÛÀt¼ÌõìÍÜÎ LË|ÑϾLbøŒÒmLÒðkÒaŒÒ5ýÐ_\³ŒŒË‹yÉíþ|Ë"̰LO Ѱ›Æ²ãÐNHÏ“eË<¼néÍâgŠ•mâÝ ß~ãû}Û ^äîà>Þäë ãõ-ã1ŒáF>å{åºß:þäV¾ããÍÝ\NâJ¤ñ½åÍÞ<¾äÞãf>æ@þÚdÞæÿýã¸{å1™æb^ç1¾çNã‰ãç~ÝÖ-è‹óàGŽèD™ª¯6‚û©…®þ8Ïð·ù ²“î–.·$‹éóͤ»épΚžŽ·¡Î±Ÿæ_;²¦ŽæQšê£Þé¥îê¬^é±Në«ê8η­Žêy»±·Žä::ë»Nê½^ë¹ìÆ>ìëëŽì°.ì¯NìÏ.놆Ô{ý¯r©È:D¸Z×Íä¼íwwlŸ°Øî~'äN‹âžçàîuÛ¬Ýv€îNßÌ N¿Jíµ\¿Ê ï¬ÈwëEÏ<΃KíŠ Ù¶ZmÐY´9žƒ•ðqn|\Ë®ðu‹¹(ñ#Ÿ¯·¶0‰diÆ; ¯§4!ïñ÷(ðÇìÉnëžòÎÞìÓó//óÑó.±þÿâ8ß°:â<¯°>å@°Bç5¯ìD~óÒžôNõkô-õ;õȈô-_õËô4¯ô{õUÝôRÿõü©ée?%aÿjoJ¼þôcïõZ÷\¯òtÏòd¿õ§zöyß¶¢Î÷Š^÷zO¶3o÷Ðþöq?÷=öñÕõ…ŸõxŸø}Oø‚ïörø—Ÿó‹?$lOš«‡lùŽÿ› ßó¤_7¦O'- ö¨/#\\£ª¿úd—ö°ÏŸ´¿¯«¶ú$ÿ®¸_û²ª¼ï®ºÄ¿û•.ü´‘üÅ¿üë ü³ÏùXoëÍÏOù~ùKéÛÜÿ÷¦è§zÉýþã¿üäOþT¦åý6ë¢b+ýiªþÕ¯åßÞ‘nÿö¾â„1ë¿UÂ!ÿJñ1u¹ýa”“ÖlVXwÿÁPI+QñLÙÖ}·o…éÚÖæ:¿+Àß=¾‰ð𺈼ÔQ c6¡QÖB•^±+j‹%&•Éo7[.šÍ±±8³y`t€¼¨‡Þ'ñj/§ùÍîðæüà„ú8 Æ6~1 ''!+)Ã[:eÛ,öJ?ÕdÂøGIé^S=igQK3cuu%w}yeq99wƒ-$1mCm Õd×Ú„/G+•}7¯‘ŸƒVYQa³«±§]¿c5·‹Ukþ€ëá¹éïÝÒ‡µ¿Méó ´3d£vé–¿^ðŽKÆŽÄàZpGN™±~Ùk×M$Hy62¾0Ć»e9&žsøÊË}ü"š“Ò Ï!®u è²åL€¤ŠêT).MayâLšî(Q'uô8Ó*W9S£veØpe1°µªÆæ®Ö4Y­«Ì¬áâé™è¬T¿6£ú}{1^Ïu9¶{j¤ØvpCò;ë,í q‘ÿ æYoØÀˆ5ßÕE¨Ø¯‚Çn- ÚîŸÂ†© •­¯Oct|ÏÝ|ú·µO>)_fü…­m‘©‹zE‡éepú¿úøcþç—Ž c­¼ûëî=ÖŽ~kÓÆ3só–ÞP4Ž(Œùr\ãÔTàÝîÌ3×þ.þÁPÀÿðÐ@Ëô;PÁnä£)» 1Âß2ìÐŽæ<ôpCF ÑD$L,ñÄ@@\‘BíHFÆi¬ÑÆqÌQÇyìÑÇ 2È]ìpÂ"1„1$™ä0D!¡ŒRÊ)©¬#+±ÌRË-¼°É/¥P2 1ÁD©Å2$ó<4ÙÜ/B5ÛåÌ8»ƒÓ-:ñ\êÉ<ý;’Ïïì´óO$õrОæ<4¸m4!"MËÏHÑr“ÒK‚ÓÝtLC=Ý´PKCþ ³ÓRßÕHGetÕO |VWe¥´ÕìjõâÔ\ÛC‰Ë_ VØa¡Ô”×Tc=VU9•e5Eb¡VÚE™ÖÚk‡]¶Ù8o5s[mü\Å´Û^ËM#Ùtõ¤•Ý6Ïuí]yG›W]wí3Þ{ób×~×¥à2÷mwà&=x„@«¨/µÎØB©p)©×á? FQa„ÿݘ†I£”ôxä¦H>4cfI¼xIÝ’¸“ŠC _ Ã]óeê“Ù½ŠÝËסc y¡;?£Ki¤‰I. ;‚:b“…fZä¶Ì56Æ’F°œ† L™h¼úCˆï´ÂO±½¥µd>þ¨Nûå¯å¦šî»—$În³Æ›i»Ù£seGŽ™º¨·FOñ¸›¦Nq¨EÆ›î5õâš—›%`‚æÃÍ^ÜiÄM~&DÒŽœñº/®œr Û»®aŸ;î³ý¶ü<Ë¿žÜå¿y÷c­ÿ†»wÙ‡¼x¤o¯oÚO/Þ7ƒ­šuÔu?¾öæmßûåóÆþvíÇx/éŸÝûHôŽÝüò¯~}ÖÕÏÏÁ—ØtõS_ßáëßo¾n÷‘g=ã_ÑsÙý¾'=ý%6ãÝÿè†Bp,sÝÔr7AÊõ¯}t î:x¶žm}¿Û:Þ6Àêá=Ta÷Î@›°„èþ Ð5RÈ>¢®…/| Á¢‘Ðw6,`ð(ÈÃóép‰$â«vÀ ne~ÀK¡ •x¼' Ph4ÄbC·EùAl ”š—(F vQìŸýgD2&M•«ÍÒsÆ:îovt#o²º?ÂËŠôáù—Ä"ú‚@Øð©¦„!‹+\cüÖÈÇ Ñ~SœXýH5HŽc좹 1œD¨½ ^U’è¸/jÒ| »\ø>V6ëD :q\_ù9(úÑ‘}¤cg$©­QQÁA§d3IÀLS-šý™äÎB)Ê j\ÓÄe³ ¹0j*”Ü 1WÁsjdcþR8ÑÆ©tVÊbòü&;É©3tÖsz™zäÌ´¹ÏÌa3Cð„™;;FJ„òŒT¶$¨Å¨¿kæì ^'»ÆÐ0FãŸ?Ëå6õùÑzf”âœGcäQœ0¤+½çæD×R_ÎÔlC\ !—ÃöìŠÄ“%RNdP¦rŒê¼aDÝ8Ñ–Åó† M\Ù†ÔG&´¥Né7zHâ5P«F¥Iòn9²Üa²«Ï«P;Ó£ýóuž{ÛZèʪ豟1ë©‘T«6uˆ6í\+ó˜f•!9=éN#v×üåk,m]¯ÆÂ4«K…cñ É&2RD…$l"·ÈD.’Ro¥ kþ*Á§ÈMRÑ¥ŽEâ%· ÛÑöMy§M©iC(Z³žLã;£dш›ÍZ¶O`á ÛP¡u]míêb5÷\ãúÏ…Óff§¨ÜŒºÖ¹¢….qg˼ÕZØÍmÍ.tWÐNÖy™ì[q):9ä®V»b.?{©Z×8Ã%-|—Z^&½±ò­~)Ñmø÷½ã¯1Lß»ÍÞ -ÿ*û_X¾"l/kÏêYÃU’ƒY$k Åûaòv8®ïC¯zºßj½'^$ Uü`äÍמ§,çÚËÀ>.˜¿dãŽ#ËÉãBxiÈ ªˆ7gááÚ8Ž)&늙,Å~2ÀVUoþe‹àªf²º:֬ήìãÇz8›ý×}ge$]ÂÅŽ¬üéîË#ww–ap“õêZ¾¢Î(l[éªgMòŠ´dñ™mIº½ÆrÜ.Sj«‹ºy”¼-%E½9éÝͳÒëu7«IOUkú„]nó˜M}êW_ºÔ”v5¦i=k“.ÔEpôN$U¥²Ÿq^36¹œk–Öõ¤žtk_ü¦Mó:Æe¤öH-ýSlCTSݶ¨[-gPƒxœÍa6aañìrÚÚà®%@ãynò9»ÚÐæI;?d´(ß´ÊwftèàŠÓÒé4¤¸«©¼ÃmÑuÝ^ë6›Õýkw—›ÐFwØþ^ë‹{âåÜ ðJ+™'’Ä?¤]m%-æ-û×/j™¾ÉÇo“ûÛ­‰k\ñœ×ÜàÑÞx½ )W÷û—4lÀ?cØÊ炼wzkþÀ2ï—Æ{v¯•=©ÏÊÞxE,4b¿ª=I'˜‹$ÏYÈ)’] Žã ÿøþP®Ç K˜èpß^XKžæT3êÀ%oäÈœ¾«ãøÊ—:g‹äõjÿû²}æ¯ÚëNæóšW¿-·wÔßèà.zðGÎq-eËp‰k,3Þ0ÙQÏ`¬oÙð™·¸Â;kè¥S—õ”ï¡w­ëz¦ÅTõpß9õÔ£/ï°.áÔ©îzŸßºÓ_æ=òçÞþãÈòä—ù0ú]ø±5o†üùäxï^®°ïºo{ºÏÜó…#ùwýc?2v¬¼ì.0óì4«ÚÂdÂc/ߎûjï»Êªó-ë$Êwkô0T²Ïí:ÐØ‘… ébpðÍšt-kâ€#î †íõBm ™4PíÚ4dÚ¶Nè§ ƒÔÄ0”öaPÞ˜-ÙòM /îaМÈÐ)n‘þ0Ûª¹M”¼- …Ú¨ð ‘ Þ`F“  È@ÍþH ß]Ê;êQ"ÛŠÉRÔjßL‡ýŒ.êìêæÎe¬h‹„´Í¶úÄ [ÑNª°Ðkñ׺0·ð¨2î™ïçpê> Àjì¼O·¬z‡{P ·TîúìË ?1æBqŒF‘ÿJqèˆ _‹§*l±Z1å|1¬i”.ædjȰѯ ÿ´±íêY1ÒÎm ‘,ÃÒÏ üVOŠÅ¢ëø)ìˆQìänÊ ýìn]Ñ3¦AºÚ§9±Ùp v–gø±»(î´)}¨LÅ(rh€ïø 0´þnÏìÀKüº ›-$Ëð À4R¼øÑOR„\²ìên’fòôŽÑ&“1ûÌö$è9‰Â¶(s %×OôL2–t2˲ &„ A­$Ñ/'7ïT'ý±%©rÞ¬RÖ.1¿0,Ù«¿Â ,¡o*wlˆÖOã<.Óò-y²ú r+¥2( Q$+oÍùtð¨N//‹RÇo.ÇñÒ“&ƒò)“öúÒ(åRún‘ôˆJƒˆ ‡mÿO³‹•Ì3©’ó“#('2½Ê2%ÿRï6 _óó4’{¯ý.P,ãÒúªk$ƒÐ,=p;®5‡ ö:s˜Jþ(BóiŠÌ—ö'±Sóà²3Öfð3Åc: «:•â‹À'AnwTÓ8³m5; cÍøÝÓÖ~ñÍìóéò Á°¢rÓ?ó³2à‰9›3Iú ê ™ôóÓ&î •SUÄýÐå±NÖpŽl±Ø%@ §ËJJ-fÄ"[l(41C!ÝÒ„>'t?ÉqEáÓ‚FðCÑó°P Y”š°’@oÔE) öbEt„"Á£$Q$4èàªâh.59§…î§$G呺¸³RG“GOÅ:ËO>ó¡Ö”Œ 2¸¶5£´<Ò¸Æj!qSÚ*ÔE“ð@—×SGþl°æm<¹‘àð*pNëòýl-Ñô+“+1Sýî™TTN½Ð5-’RópO"-kM÷1ô4ð>9S+Á S½͆2*kS;[¬,—+R%Õ'1OS1rþœ¨-U-µÎS'9a”’žË)µ’Rò.“1yŒUóY°eY™KÌ - p§~´Kõ&o/ßÏ1ñˆvÓ’dUË~3ÕXÏ“KÏQÌô¶r÷ÐÓM+óù0µ15óA9PI×JÈîïI™ìh(+V»sɆs5%•NGvØ5Ž„uóÒZÅWv¼Œ4ŸˆÍD]ô`³´IØÔƾâ˜trÈþ¸Ç_¯Ó4V=«T”PlGÇ´cÓ•c_”jöb·5>1tgeÕøDc»N]6 sVg9ó8iÖ`“ö?Ù$G¹pç©ñgd$H­–3³?ÿÐJti[4O€öÉê¯ì5iL}¯>YQC½Vfç³mavcGÌ¡”讑±@OûéIe!"ÊÀq=QÔžàÖKÑ$l]µK»ñ"‹1!óòÏlµ!1ò!I4"3õà qã4a —W;ÍJ7'“F–!‰5Uƒ56ÃuC;wW±¯u —AEU^û’¿Jµ_§UçÕ63YÙ¶s5×h_UçVwk·êVQ#[ÉuþUÒBaWmŸ¤Y©·z…„x÷6›òR!bOwwU5ŠÞTB¢WzË7x‘e±S:—ÔOÁÕdwe;Q`!meõ°uÑWQò÷=Ý–?‡v9õå|Ë×òPEšÈcMtcWNØ„Ž…9”`x€ TVØc.R)8m…6+µ6D™v @¸OG4ÝÀCm´6n9¨`¿Ök3¸Š”åc½”¼\Öli©F;…c–…—7˜ƒ%º¤¦¦šŒtñÿ-o‘N4ëvq.±>Î_ɶéF“k qxøŠ/e]+×1K·7Ã7r•qrÅñ¶Â.$}“±ö9ö…9÷퇈ëu`½³þUŒ™ÒŽ^XWÍד5‹áp‹[P :èØ0ÙÊ%½7‘êa“‡—F ~ž·‚ÛmŽÍÈ$ï‹G7yñØx7¯ß(¹úä6i1¹iÁIa·*öK—‘OU1Á×!»1ËuåÄnWùp[yá ø€ôÌõÕsè¶_­4ÏsQ-Éîì†}0Ð,Ù…yC°…=×I¸hk¸¶pY‚cø3·l»9Wfœ_ñš§vU ‚å˜K“vÙùuËÙ{xõ³™¤R¸jµ˜bš„…”ÞrI‡oV»‡=}¹ŸÓ¹ƒÅ *¡ª—Aê(£K… W¡ÙxµY•þ!Ú›ðå䆈õ¶›ØÕo㪈Ño«1Hÿ6$l.š SPKú›µ4¤S¸€]Ùd"×ÄÂŒ–9Rr‡šMÁG—7r#“íž’§{ºTv(Ÿ2¿`S–¥ÙQÏÀþñRë(›š£ùYªºçLxäubZ/o7p¹ÒvÌTú{õ¬#>èÕwÁùÚ úŸKOµw~㺨Mås•—»ÑÕXÑ{ß§]ÖüÓ?œ˜qÛG1!3úÒùuÓ‹ÊÿïÃ7~ ‘›Ö±>×Õï{×ÁÖzé¾îsÄÅ·º¹9ÂEEîaxÖ›ð'ްØÛ^œUÆï£ý×±}iÿšæïלä±×#?Ó|ÙeOÙŽ=@ }è¶ñ?}^lj†éG™}ÛeT£O¿òYöã˜ÑÛ¥Ð>6O']ÝuÔᦎËç¤ckÞá¶íýþ^­þgC]ÊíÌ*霫“>ì! áRð¢ÑÓ÷º­P#„3ÈmóÃ"äõÝ Jd™"¬±z²Ÿ¿âý]å'þ7>¬55ñƒù‘v)>ff0ªä džá1tQ­×¢¼vËÇÔäu"V6'G¡äµfl$]Ûµvë;ßû?0(ŠrÄ#2É3ö*ˆý8ϪaQ‘N±,P÷Ôo³æ2U<%2•M$‹q¥<­Ý®—q^ÅúL[ÉÈÝÝÌ!ÞÜ Ÿ¡¡ÛÛc¤ä$e¤`%fæåÚSا”Y)Z”šêh)—iikÐfæa!£ËŠF®Lïž_ð®¢þðb°ÉF#àßÚr qâ-Ütµõõ‘-ö¶6Mgª³l™ÔéjNl(ú¹ºø¹ª»§wå&Ø•…—J³:z|øF©’“ç/.}Ä9(lÌ“J÷Ò’‰Ì~Õ⤶ã/ᮉûD̘-[^îN-—*¼‘É\¶9©]†ctüø4jÈ©WWdþ}Í´ëØ†i®Õ’“ ÉÙÔmð›Â¯8øíö¨3-lÙÊ/W’¼¹ëçY9a ›L'vç¼þ[äY8xíô’H‡žº)‡i–X$w;çáˆÞÙùSqRÂH&kþ€úi¡VÂG¤“\¥È§#B §¡F"ÔÏ€Ì0š§ˆýGÞ v:ا Ji}ºù{Fªä£)¢H*©¨Bú&ˆ:êê‹¡:8*j¼êZ¯Çø—Ï|ïR±ˆôg &KPªÝa×,¥ð«Å~SIÔh®¿¦ç­{à6,s×öY%„&Ú(î·ì–ént…â(ÍxËyS¹¥Á«\¾‰õ»/§„úU]ºñÎ{ðŒÿ|î /¬š¾KLÖÄçUìpÅÝÈ«®QÓ…Vîºu•j¸2š‡qÆK\œ2{‡27}'¡°°òÙ.Ëþ®œ³Î·í°²$+´³ÑÊÙK9rßþ²qè CÂÌsx y5ÖYk½5×]º\j:Z+«šJ+¢×‘ (ŽýàÔTÛ oÜrßÜZµN‹©›ER%à¬uªê‘´cx²ÝgÕ=eâf‘ÛÛD‡³9øÛ/IþßÕf{[Õ}?Šxã^-ÞXè¢]i€¸^ù¬ž5táL$/ú®ç‡ã\:ã.÷ã†FË7ðP«êú ³ÿSÛh;“ö®¸ µ{NÐóÜ;uMÏ'{Ò¶"xhK:+mÛïMìšR;”ô§ŸïóE[QxOaÓ½>麗,ˆiÀÜ{÷õÆ õ87Æ~ô‹ÞÎr£7“AÌvsÓXÍhôõuÊþ& ¤ÓÃÒÇ2 Fìggp¬g´Y«?ÑŠØ`´9 o<`Î8˜1ÒFEšÝêN…)'•[Ây4@ Ö0|ï‰dZªÿy †t— Gؤ°‰rm£"늼†ë‰ÏÚF',e2äÜM‚ƒ¢¡%¹31/pŸ1ÍÆ* þ[rtaûpó»üd …´¸·Ä ëñˆNËÔ Ï†Fñ8ñ… ó]ÙR'ÆÖíp‡·zWÿjÆÓ òCV\sH¯iõP‰ÌÚ"hü´È &0l¿s£­t¸£DÉ’ŽšÁÈÉ a1xsšc,MdÇ,þòU|_#Ù÷ÈêåEŠþ=!wÁGáðs'â AF1ö‡{ñ£™LðMbQ˜½´æ…†¹))­r™j,YÈÈ^íÒiÉÃ"'iÈ;þðœ¶K§?ˈÌdæ1Dܤüf1e¢ns‚ ¥=ÉÏ+Ö“€»U;FP<ºÓqñ¤'C±I¦ˆYÙ›òrù6ýSmÕ Q7úQ€et_C´hO ¸ÍÐÕt~-þ¶ƒO q<K—XPyÖ‚;m\O¡ØÊÞ-“, •J3ÿ-u¡!åLúÎ#2mšq!“ŒHþœpŸ¦$Zÿ¨(ż Õ‹aWTÙ•W{@җ焨êüVÊry²@åþLHƒfzÁyˆ¬_H¢61¹×nÝõ“ÕˆaXªÏ«žr–ã£$a-U8YF¶jb}eUÅŪ옳ie9JÓ͇8 M9ýÐÐÆŒ¨»--k-«ÇlHoߣ×çŠ$oúqŸre’)õÛ.þ´¡]š ·Îf¨ÓšQwãèÔ®‚­$‡­ƒêÄËC±Á”»ÁDlG§Ë×êæB»ˆ<¯(u[Qum(<}Ë%Æo`IÉ^F²–Pº3®šl:ëš;†Ý­$iÕÙSÓRÈIïv3½éeû™Û{ž­åj í_crÁr‚ÁûÛcÕÛÊv7ļìÊbã’Ü÷ý¤þ~­­§ð ”ÉÛ†wŒ<½.úê§ÄÎî{ƒ™ïtã Úìv$ÀßñFÖ™Tþï3ÑZêl¢:Í\ dýðöu­ä&?9ÊS®ò”OÜЧqî(nFôI·g-ç²ÀãÓ˜læ<0˜o^眻¤hñ(^Ü¥ =™D÷yÓ\A¦GêQ?àÓý»@ÏóÍqm¦mŽJi,7ØêZ1ûÕÏ—u™Ÿ´,V•v¹·šhM¯½*£ NÚ7Z÷žêƒm}5£ 9×™z¹?ä TðUA—pIÌóðžw§ÿ¹“.š€¯ï-Ê*ÿp°÷€/½1¾svJ}¥Ÿ< ÷ÞÚ={¿_-çþ)ªo:yø¯%^´N¿tÈ£>õ­1½©ù³ÚW½–¤fvjOäi“ýŒ"+Š•zÿèÊ/^ŽÔëcÏæt'ߥ‡­ãüüÈwSú¾'¸=gy}n^áV®7½¯6-›¶ÁA7ÿôEýáë¢2ú°4}-ñ_KE”÷5^óÉÙýáŸIî}ÛiÚ~›ýٞߌIÝÏéÖ’éŠô¨Þê’þFR^-àÒÒêµà¬AÜ â jà ÖaÔà :š¼1 žà®z`ïý  aé ¡’]ÝÊ5!ËY ò”aÚ)a9¡n!nM^! þ‚¡ÜBUžaó !ÕX¡¶¶áèÁ¡ªÅÎ!Ù!>O–àö!úa³¢ vDæa!¢|¢*""V#Êá#6¢LEbR¢$RÞ%z‹%f"r¢z"(vY(²Ó(’!ÇÚ­a)vÛ*ŠÊ•! ¶")ÊbJÊ 9Ñ&ža.a¬qN_`-&L0‚ŽºÅ¡£Ä!£ÁHö_*£+B#¿ÈN…u0Jc-b£6îâr£6öà7ʆ7†ãË‘£8š£4Žãª#:¦`;¢Ë;#;"á<Æã'Ú£Wáã*Öãò£>Ü?¶L@–¢?®`A¤ù äÕ*ä%ä:$CcD"ÝD6dE*Nf¤Fn$Gv¤G~$H†d*^$I–¤Iž$J¦¤J®$K¶¤K¾$LƤLÎ$MÖ¤MÞ$Næ¤Nî$Oö¤Oþ$P¥P%Q¥Q%R&¥R.%S6¥S2R] "D>%U6üYZUfå]Ï-j¥W&bPK*}%YNâOxÝT–¥Z®Íò9ãZ¾eRã÷Œ$\Ö¥]Þ%^æ¥^î%_ö¥_þ%`¦`&a¦a&b&¦b.&c6¦c>&dF¦dN&eVæ%;casacore-3.7.1/casa/OS/OS_1.html000066400000000000000000000025351476623553700162100ustar00rootroot00000000000000


          Ger van Diepen (gvandiep@nfra.nl)
          Last modified: Wed Nov 20 15:47:20 1996 casacore-3.7.1/casa/OS/OS_1.omt000066400000000000000000000460461476623553700160500ustar00rootroot00000000000000(omt_version 5 0) # %W% %G% (omt_module (key 1) (anno unique_methods NO) (anno unique_fields NO) (name OS_1) (language C++) (fcode_max 1001) (omt_model (omt_class (key 3) (anno description "A class with virtual functions to convert VAX format") (anno cxx_field_access private) (name VAXDataConversion)) (omt_class (key 7) (anno unique_fields YES) (anno virtual_method YES) (anno description "Abstract base class with functions to convert any format. This abstract base class contains pure virtual functions to convert from any foreign data format to local format and vice-versa. IEEEDataConversion). Classes derived from it implement the functions for a specific foreign format (e.g. IBMDataConversion, CanonicalDataConversion, Example: // Construct the correct conversion object. DataConversion* conv = new IBMDataConversion(); // Say that you read a block of 256 floats. char buffer[1024]; read (fd, buffer, 1024); float values[256]; // conv->toLocal (values, buffer, 256); ") (name DataConversion) (method (key 25) (anno description "Convert one value from foreign format to local format. The from and to buffer should not overlap. The char version handles characters (thus may involve convers EBCDIC to ASCII), while the unsigned chars are simply bytes.ion") (anno const YES) (name toLocal) (type uInt) (arglist (arg (key 169) (name to)) (arg (key 170) (name from*))) (genheader "unsigned int VAXDataConversion::toLocal (void* to&,void* from*) ")) (method (key 28) (anno description "Convert nr values from foreign format to local format. The from and to buffer should not overlap. The char version handles characters (thus may involve conversion EBCDIC to ASCII), while the unsigned chars are simply bytes.") (anno const YES) (name toLocal) (type uInt) (arglist (arg (key 171) (name to*)) (arg (key 172) (name from*)) (arg (key 173) (name nr)))) (method (key 32) (anno description "Convert one value from local format to foreign format. The from and to buffer should not overlap. The char version handles characters (thus may involve conversion ASCII to EBCDIC), while the unsigned chars are simply bytes.") (anno const YES) (name fromLocal) (type uInt) (arglist (arg (key 174) (name to*)) (arg (key 175) (name from)))) (method (key 35) (anno description "Convert nr values from local format to foreign format. The from and to buffer should not overlap. The char version handles characters (thus may involve convers ASCII to EBCDIC), while the unsigned chars are simply bytes.ion") (anno const YES) (name fromLocal) (type uInt) (arglist (arg (key 176) (name to*)) (arg (key 177) (name from*)) (arg (key 178) (name nr)))) (method (key 39) (anno const YES) (anno description "Determine if values of data type T can be copied directly.") (name canCopy) (type Bool) (arglist (arg (key 179) (name T*)))) (method (key 41) (anno const YES) (anno description "Get the size in bytes of a value with data type T in external format.") (name externalSize) (type T*) (arglist (arg (key 180) (name uInt))))) (omt_class (key 43) (anno unique_fields YES) (anno cxx_field_access private) (anno description "A class with static functions to convert IBM format. ") (name IBMConversion) (method (key 44) (anno description "Convert one value from IBM/360 format to local format. The from and to buffer should not overlap. The char version converts from EBCDIC to ASCII, while the unsigned char version is a simple copy. ") (name toLocal) (is_class) (type void) (arglist (arg (key 181) (name to)) (arg (key 182) (name from*))) (genheader "unsigned int VAXDataConversion::toLocal (void* to&,void* from*) ")) (method (key 47) (anno description "Convert nr values from IBM/360 format to local format. The from and to buffer should not overlap. The char version converts from EBCDIC to ASCII, while the unsigned char version is a simple copy.") (anno virtual_method YES) (name toLocal) (is_class) (type void) (arglist (arg (key 183) (name to*)) (arg (key 184) (name from*)) (arg (key 185) (name nr)))) (method (key 51) (anno description "Convert one value from local format to IBM/360 format. The from and to buffer should not overlap. The char version converts from ASCII to EBCDIC, while the unsigned char version is a simple copy. ") (anno virtual_method YES) (name fromLocal) (is_class) (type void) (arglist (arg (key 186) (name to*)) (arg (key 187) (name from)))) (method (key 54) (anno description "Convert nr values from local format to IBM/360 format. The from and to buffer should not overlap. The char version converts from ASCII to EBCDIC, while the unsigned char version is a simple copy.") (anno virtual_method YES) (name fromLocal) (is_class) (type void) (arglist (arg (key 188) (name to*)) (arg (key 189) (name from*)) (arg (key 190) (name nr))))) (omt_class (key 58) (anno unique_fields YES) (anno cxx_field_access private) (anno description "A class with static functions to convert canonical format.") (name Conversion) (method (key 191) (anno description "Conversion of boolean values to bits. There is also a version with an argument start.") (name boolToBit) (is_class) (type uInt) (arglist (arg (key 192) (name to*)) (arg (key 193) (name from*)) (arg (key 194) (name nr)))) (method (key 195) (anno description "Conversion of bits to boolean values. There is also a version with an argument start.") (name bitToBool) (is_class) (type uInt) (arglist (arg (key 196) (name to*)) (arg (key 197) (name from*)) (arg (key 198) (name nr)))) (method (key 199) (anno description "A function using memcpy, but returning nr of bytes to have the ValueFunction signature.") (name valueCopy) (is_class) (type uInt) (arglist (arg (key 200) (name to*)) (arg (key 201) (name from*)) (arg (key 202) (name nbytes)))) (method (key 203) (anno description "Get the memcpy function.") (name getmemcpy) (is_class) (type ByteFunction*)) (method (key 204) (anno description "Wrapper on systems with a non-standard memcpy signature.") (name mymemcpy) (is_class) (type void*) (arglist (arg (key 205) (name to*)) (arg (key 206) (name from*)) (arg (key 207) (name nbytes))))) (omt_class (key 60) (anno description "A class with static functions to convert VAX format.") (name VAXConversion) (method (key 61) (anno description "Convert one value from VAX format to local format. The from and to buffer should not overlap. ") (name toLocal) (is_class) (type void) (arglist (arg (key 62) (name to)) (arg (key 63) (name from*))) (genheader "unsigned int VAXDataConversion::toLocal (void* to&,void* from*) ")) (method (key 64) (anno description "Convert nr values from VAX format to local format. The from and to buffer should not overlap. ") (anno virtual_method YES) (name toLocal) (is_class) (type void) (arglist (arg (key 65) (name to*)) (arg (key 66) (name from*)) (arg (key 67) (name nr)))) (method (key 68) (anno description "Convert one value from local format to VAX format. The from and to buffer should not overlap. ") (anno virtual_method YES) (name fromLocal) (is_class) (type void) (arglist (arg (key 69) (name to*)) (arg (key 70) (name from)))) (method (key 71) (anno description "Convert nr values from local format to VAX format. The from and to buffer should not overlap. ") (anno virtual_method YES) (name fromLocal) (is_class) (type void) (arglist (arg (key 72) (name to*)) (arg (key 73) (name from*)) (arg (key 74) (name nr)))) (method (key 75) (anno description "Move a float value (by swapping bytes correctly).") (name moveFloat) (is_class) (type void) (arglist (arg (key 76) (name to*)) (arg (key 77) (name from*))))) (omt_class (key 78) (anno description " A class with static functions to convert littleEndian format ") (name LittleEndianConversion) (method (key 79) (anno description "Convert one value from littleEndian format to local format. The from and to buffer should not overlap. ") (name toLocal) (is_class) (type void) (arglist (arg (key 80) (name to)) (arg (key 81) (name from*))) (genheader "unsigned int VAXDataConversion::toLocal (void* to&,void* from*) ")) (method (key 82) (anno description "Convert nr values from littleEndian format to local format. The from and to buffer should not overlap. ") (anno virtual_method YES) (name toLocal) (is_class) (type void) (arglist (arg (key 83) (name to*)) (arg (key 84) (name from*)) (arg (key 85) (name nr)))) (method (key 86) (anno description "Convert one value from local format to littleEndian format. The from and to buffer should not overlap. ") (anno virtual_method YES) (name fromLocal) (is_class) (type void) (arglist (arg (key 87) (name to*)) (arg (key 88) (name from)))) (method (key 89) (anno description "Convert nr values from local format to littleEndian format. The from and to buffer should not overlap. ") (anno virtual_method YES) (name fromLocal) (is_class) (type void) (arglist (arg (key 90) (name to*)) (arg (key 91) (name from*)) (arg (key 92) (name nr))))) (omt_class (key 93) (anno description " A class with static functions to convert canonical format ") (name CanonicalConversion) (method (key 94) (anno description "Convert one value from canonical format to local format. The from and to buffer should not overlap. ") (name toLocal) (is_class) (type void) (arglist (arg (key 95) (name to)) (arg (key 96) (name from*))) (genheader "unsigned int VAXDataConversion::toLocal (void* to&,void* from*) ")) (method (key 97) (anno description "Convert nr values from canonical format to local format. The from and to buffer should not overlap. ") (anno virtual_method YES) (name toLocal) (is_class) (type void) (arglist (arg (key 98) (name to*)) (arg (key 99) (name from*)) (arg (key 100) (name nr)))) (method (key 101) (anno description "Convert one value from local format to canonical format. The from and to buffer should not overlap. ") (anno virtual_method YES) (name fromLocal) (is_class) (type void) (arglist (arg (key 102) (name to*)) (arg (key 103) (name from)))) (method (key 104) (anno description "Convert nr values from local format to canonical format. The from and to buffer should not overlap. ") (anno virtual_method YES) (name fromLocal) (is_class) (type void) (arglist (arg (key 105) (name to*)) (arg (key 106) (name from*)) (arg (key 107) (name nr)))) (method (key 108) (anno description "Convert data with the given data type to local format. Note that the length is given in bytes iso. values. In this way this function matches the ConversionFunction definition in class Conversion. ") (name toLocalT) (is_class) (type void*) (arglist (arg (key 208) (name to*)) (arg (key 209) (name from*)) (arg (key 210) (name nr)))) (method (key 211) (anno description "") (name fromLocalT) (is_class) (type void*) (arglist (arg (key 212) (name to*)) (arg (key 213) (name from*)) (arg (key 214) (name nr)))) (method (key 116) (anno description "Convert function with a ByteFunction signature.") (name byteToLocalT) (is_class) (type void*) (arglist (arg (key 215) (name to*)) (arg (key 216) (name from*)) (arg (key 217) (name nbytes)))) (method (key 218) (anno description "Convert function with a ByteFunction signature.") (name byteFromLocalT) (is_class) (type void*) (arglist (arg (key 219) (name to*)) (arg (key 220) (name from*)) (arg (key 221) (name nbytes)))) (method (key 222) (anno description "") (name getToLocal) (is_class) (type ValueFunction*) (arglist (arg (key 223) (name T*)))) (method (key 224) (anno description "") (name getFromLocal) (is_class) (type ValueFunction*) (arglist (arg (key 225) (name T*)))) (method (key 226) (anno description "") (name getByteToLocal) (is_class) (type ByteFunction*) (arglist (arg (key 227) (name T*)))) (method (key 228) (anno description "") (name getByteFromLocal) (is_class) (type ByteFunction*) (arglist (arg (key 229) (name T*)))) (method (key 230) (name canonicalSize) (is_class) (type uInt) (arglist (arg (key 231) (name Type*)))) (method (key 118) (anno description "Reverse N bytes, where N=2,4, or 8.") (name reverseN) (is_class) (type void) (arglist (arg (key 119) (name to*)) (arg (key 120) (name from*)))) (method (key 121) (anno description "Move N bytes, where N=2,4, or 8.") (name moveN) (is_class) (type void) (arglist (arg (key 122) (name to*)) (arg (key 123) (name from*))))) (omt_class (key 124) (anno description "A class with virtual functions to convert canonical format.") (name CanonicalDataConversion)) (omt_class (key 131) (anno description "A class with virtual functions to convert IBM format") (name IBMDataConversion)) (omt_class (key 232) (anno description "A DataConversion class for no-conversion operations.") (name RawDataConversion)) (generalization_relation (key 233) (superclass 7) (subclasses 131 3 124 232)) (binary_association (key 132) (anno role1_is_class NO) (anno role2_is_class NO) (anno description "") (name uses) (role (key 133) (primary 7) (mult 1 2)) (role (key 134) (primary 58) (is_arrow) (mult 1 2))) (binary_association (key 135) (anno role1_is_class NO) (anno role2_is_class NO) (anno description "") (name uses) (role (key 136) (primary 43) (mult 1 2)) (role (key 137) (primary 93) (is_arrow) (mult 1 2))) (binary_association (key 149) (anno role1_is_class NO) (anno role2_is_class NO) (anno description "") (name uses) (role (key 150) (primary 78) (mult 1 2)) (role (key 151) (primary 93) (is_arrow) (mult 1 2))) (binary_association (key 152) (anno role1_is_class NO) (anno role2_is_class NO) (anno description "") (name uses) (role (key 153) (primary 60) (mult 1 2)) (role (key 154) (primary 78) (is_arrow) (mult 1 2))) (binary_association (key 157) (name uses) (role (key 158) (primary 131) (mult 1 2)) (role (key 159) (primary 43) (is_arrow) (mult 1 2))) (binary_association (key 160) (name uses) (role (key 161) (primary 3) (mult 1 2)) (role (key 162) (primary 60) (is_arrow) (mult 1 2))) (binary_association (key 163) (name uses) (role (key 164) (primary 124) (mult 1 2)) (role (key 165) (primary 93) (is_arrow) (mult 1 2)))) (omt_image (sheet (key 2) (anno description "") (name OM) (model_type Object) (width 750) (height 670) (background_color white) (foreground_color black) (ClassBox (key 126) (represents 43) (frame (loc 5 336) (dimensions 175 96))) (ClassBox (key 144) (represents 131) (frame (loc 31 218) (dimensions 128 44))) (ClassBox (key 10) (represents 3) (frame (loc 232 215) (dimensions 128 44))) (ClassBox (key 130) (represents 124) (frame (loc 423 215) (dimensions 160 44))) (ClassBox (key 234) (represents 232) (frame (loc 613 215) (dimensions 131 44))) (ClassBox (key 125) (represents 58) (frame (loc 522 25) (dimensions 214 109))) (ClassBox (key 9) (represents 7) (frame (loc 199 5) (dimensions 195 122))) (ClassBox (key 128) (represents 60) (frame (loc 204 336) (dimensions 175 109))) (ClassBox (key 127) (represents 78) (frame (loc 201 500) (dimensions 175 96))) (ClassBox (key 129) (represents 93) (frame (loc 423 334) (dimensions 242 239))) (AssociationImage (key 156) (represents 149) (nodes 127 129) (points (377 594) (476 594) (476 574)) (arc_name 400 594 7)) (AssociationImage (key 145) (represents 132) (nodes 9 125) (points (395 86) (521 86)) (arc_name 498 86 7)) (GeneralizationImage (key 235) (represents 233) (supernode 9 (296 128) (296 205)) (node 144 (95 207) (95 217)) (node 10 (296 207) (296 214)) (node 130 (503 207) (503 214)) (node 234 (678 207) (678 214))) (AssociationImage (key 168) (represents 160) (nodes 10 128) (points (296 260) (296 335)) (arc_name 296 285 1)) (AssociationImage (key 166) (represents 157) (nodes 144 126) (points (95 263) (95 335)) (arc_name 95 285 1)) (AssociationImage (key 146) (represents 135) (nodes 126 129) (points (92 433) (92 631) (539 631) (539 574)) (arc_name 265 631 7)) (AssociationImage (key 155) (represents 152) (nodes 128 127) (points (293 446) (293 499)) (arc_name 293 473 1)) (AssociationImage (key 167) (represents 163) (nodes 130 129) (points (503 260) (503 333)) (arc_name 503 284 1)))) (savekey_max 235)) casacore-3.7.1/casa/OS/Path.cc000066400000000000000000000404431476623553700157640ustar00rootroot00000000000000//# Path.cc: Class to define a pathname //# Copyright (C) 1993,1994,1995,1996,1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include // needed for getpwnam #include // needed for pathconf #include // needed for PATH_MAX, etc. #include // needed for isprint #include // needed for realpath #include // needed for errno #include // needed for strerror namespace casacore { //# NAMESPACE CASACORE - BEGIN // The maximum number of bytes in a pathname is 255 (_POSIX_PATH_MAX) // Definition for POSIX systems #if defined (_POSIX_PATH_MAX) const uInt pathmax_posix = _POSIX_PATH_MAX; #else const uInt pathmax_posix = 255; #endif // The maximum number of bytes in a pathname is PATH_MAX #if defined (PATH_MAX) static uInt pathMax = PATH_MAX; #else static uInt pathMax = 0; #endif const uInt PATH_MAX_GUESS = 1024; // if PATH_MAX is indeterminate // we're not guaranteed this is adequate. // The maximum number of bytes in a filename is 14 (_POSIX_NAME_MAX) // Definition for POSIX systems #if defined (_POSIX_NAME_MAX) const uInt namemax_posix = _POSIX_NAME_MAX; #else const uInt namemax_posix = 14; #endif // The maximum number of bytes in a filename is NAME_MAX #if defined (NAME_MAX) static uInt nameMax = NAME_MAX; #else static uInt nameMax = 0; #endif const uInt NAME_MAX_GUESS = 255; // if NAME_MAX is indeterminate // we're not guaranteed this is adequate. ' Path::Path() : itsOriginalPathName (".") {} Path::Path (const String& pathName) : itsOriginalPathName (pathName) { if (itsOriginalPathName.empty()) { itsOriginalPathName = "."; } } Path::Path (const Path& that) : itsOriginalPathName (that.itsOriginalPathName) {} Path::~Path() {} Path& Path::operator= (const Path& that) { if (this != &that) { itsOriginalPathName = that.itsOriginalPathName; itsAbsolutePathName = that.itsAbsolutePathName; itsExpandedPathName = that.itsExpandedPathName; } return *this; } void Path::append (const String& string) { if (!string.empty()) { if (itsOriginalPathName.lastchar() != '/' && string.firstchar() != '/') { itsOriginalPathName += "/"; } itsOriginalPathName += string; itsAbsolutePathName = ""; itsExpandedPathName = ""; } } const String& Path::expandedName() const { if (itsExpandedPathName.empty()) { itsExpandedPathName = expandName (itsOriginalPathName); } return itsExpandedPathName; } const String& Path::absoluteName() const { if (itsAbsolutePathName.empty()) { itsAbsolutePathName = removeDots (makeAbsoluteName (expandedName())); } return itsAbsolutePathName; } String Path::resolvedName() const { char name[PATH_MAX+1]; char* ptr = realpath (absoluteName().c_str(), name); if (ptr == 0) { throw AipsError("resolvedName(" + absoluteName() + ") failed: " + strerror(errno)); } return String(name); } Bool Path::isValid() const { // Check if the length of the pathname is not too long if (itsOriginalPathName.length() > getMaxPathNameSize()) { return False; } // Check if pathname contains double slashes if (itsOriginalPathName.contains ("//")) { return False; } // Check if pathname contains non-printables uInt i; for (i=0; i getMaxPathNameSize()) { return False; } // Check if filenames are not too long String subPathname[30]; String sep = "/"; uInt nw = split (itsOriginalPathName, subPathname, 15, sep); uInt nameSize = getMaxNameSize(); for (i=0; i nameSize ) { return False; } } return True; } Bool Path::isStrictlyPosix() const { // Check if the length of the pathname is not too long, according POSIX // standard if(itsOriginalPathName.length() > pathmax_posix ) { return False; } // Check if pathname contains double slashes if (itsOriginalPathName.contains ("//")) { return False; } // Check if pathname contains non-printables uInt i; for (i=0; i namemax_posix ) return False; } return True; } uInt Path::length() const { return itsOriginalPathName.length(); } uInt Path::maxLength() const { return getMaxPathNameSize(); } String Path::baseName() const { // Determine from expanded path name. String name = expandedName(); // Search last slash. // Get rid of trailing slash. Int len=name.length(); if (len > 0 && name[len-1] == '/') { len--; } Int i=len; while (--i >= 0 && name[i] != '/') {} // The base name is the part from the slash till the end. return name(i+1, len-i-1); } String Path::dirName() const { String name = expandedName(); // Search last slash. // Get rid of trailing slash (except if name consists of a slash only). Int i=name.length(); if (i > 1 && name[i-1] == '/') { i--; } while (--i >= 0 && name[i] != '/') {} // If no slash, the dirname is the current directory. if (i < 0) { return "."; } // If the slash found is not the first character, skip it. if (i > 0) { i--; } return name.through(i); } uInt Path::getMaxPathNameSize() { // pathMax is not defined(<0) then pathconf sets pathMax, // if this doesn't work pathMax will get the value of PATH_MAX_GUESS if (pathMax == 0) { #if defined(AIPS_CRAY_PGI) pathMax = PATH_MAX_GUESS; #else pathMax = pathconf ("/",_PC_PATH_MAX) < 0 ? pathMax : PATH_MAX_GUESS; #endif } return pathMax; } uInt Path::getMaxNameSize() { // nameMax is not defined (<0) then pathconf sets nameMax, // if this doesn't work nameMax will get the value of PATH_MAX_GUESS if (nameMax == 0) { #if defined(AIPS_CRAY_PGI) pathMax = NAME_MAX_GUESS; #else nameMax = pathconf ("/",_PC_NAME_MAX) < 0 ? nameMax : NAME_MAX_GUESS; #endif } return nameMax; } String Path::expandName (const String& inString) const { String tempString (inString); uInt cursor = 0; uInt i = 0; Bool flag = True; uInt count = 0; // Flag is set True if an environment variable is detected. When this // happens more then 25 times, there is probably a recursive variable set. // In that case an exception will be thrown. while (flag && count<25) { // flag is False, if there is not an environment variable // the name will not be checked again flag = False; count++; // count is increased when the string is cursor = 0; // walked through // Replace tilde with the name of the home directory if (tempString.firstchar() == '~') { if (tempString.length() == 1 || tempString[1] == '/') { // To get the home directory, environment variable HOME is used. String name (EnvironmentVariable::get("HOME")); if (! name.empty()) { tempString.del ("~",0); tempString.prepend (name); } } else { tempString.del ("~",0); getNextName (tempString, cursor); String temp (tempString.before(Int(cursor))); // The password file is used to get the home directory // of "~name" // This cannot be done on the CRAY XT3 CATAMOUNT as it // does not support sockets. #ifdef AIPS_CRAY_PGI tempString.prepend ("~"); #else passwd* passWd = getpwnam(temp.chars()); if (passWd != 0) { tempString.del (tempString.before (Int(cursor))); tempString.prepend (passWd->pw_dir); cursor = 0; }else{ tempString.prepend ("~"); } #endif } } i = 0; if (tempString.size() > 0 && tempString[0] == '/') { i = 1; } while (i < tempString.length()) { cursor = i; getNextName (tempString, i); // i is set on the next name // See if an env.var is given String::size_type dpos = tempString.find ('$', cursor); if (dpos != String::npos) { String::size_type last = i; String dName (tempString.at (Int(dpos+1), Int((i-dpos)-1))); if (dName[0] == '{') { String::size_type bracePos = dName.find ('}'); if (bracePos != std::string::npos) { last = dpos+1+bracePos+1; dName = dName.substr(1, bracePos-1); } else { dpos = String::npos; } } if (dpos != String::npos) { String name (EnvironmentVariable::get(dName)); if (! name.empty()) { String res (name); res.prepend (tempString.before(Int(dpos))); res += tempString.after (Int(last-1)); // Update the index for the changed part. i = last + Int(res.size()) - Int(tempString.size()); tempString = res; // flag is set True, so the name will be checked again // for environment variables flag = True; } } } i++; // go past slash } } if (flag) { // An exception is thrown if the string is checked more then 25 times throw (AipsError ("Path::expandName: recursive environment variable")); } return tempString; } String Path::makeAbsoluteName (const String& inString) const { // If the first char is a slash the name is already absolute. if (inString.firstchar() == '/') { return inString; } // Otherwise we have a relative pathname. String workString (inString); if (workString == ".") { workString = ""; } else if (workString.startsWith("./")) { workString = workString.from(2); } // Get the working directory and prepend it. // getcwd returns a null pointer if it fails. char temp[1024]; AlwaysAssert (getcwd(temp, 1024), AipsError); String tempString (temp); // Return the working directory when no input string left. if (workString.empty()) { return tempString; } // Append a / if needed. if (tempString.lastchar() != '/') { tempString += '/'; } tempString += workString; return tempString; } String Path::removeDots (const String& inString) const { // Split the name at the slashes. Vector parts (strToVector (inString, '/')); Vector validParts (parts.nelements()); string dot("."); string dotdot(".."); uInt nvalid = 0; uInt i; // Count the number of valid parts and keep their index. // Ignore blanks and . parts. // A .. part removes an entry from the valid list (if possible). for (i=0; i 0) { nvalid--; } } else { validParts(nvalid++) = i; } } } // Combine the parts into the output string. // Start it with a slash if the input started with a slash. String outString; Bool doSlash = (parts(0).empty()); for (i=0; i 0) { Int aleng = aName.length(); if (aleng > leng && aName.before(leng) == dir) { return "././" + aName.from (leng); } else { // No match; see if name matches start of otherName. // If so, append /. to remainder of otherName. if (aleng+1 < leng && dir.before(aleng+1) == aName+'/') { return dir.from(aleng+1) + '.'; } // No match; now compare using the directory part only. dir = Path(dir).dirName() + '/'; while (dir.length() >= 2 && dir[0] == '.' && dir[1] == '/') { dir = dir.after(1); } leng = dir.length(); if (leng > 0) { if (aName.length() > uInt(leng) && aName.before(leng) == dir) { // The leading ./ indicates that directory is removed. return "./" + aName.from (leng); } } } } if (leng == 0) { // Also relative if the parent is this directory and the // subtable is relative. if (name[0] != '/' && name[0] != '$' && name[0] != '~') { return "./" + name; } } // Nothing could be stripped. // Return original name after removing possible leading ./ String tName(name); while (tName.length() >= 2 && tName[0] == '.' && tName[1] == '/') { tName = tName.after(1); } return tName; } String Path::addDirectory (const String& name, const String& otherName) { // If a directory was stripped off, add the directory of the parent. // Otherwise use the name as such. // Note that dirName will use the expanded name. // Start with removing possible leading ./ String tName(name); while (tName.length() >= 2 && tName[0] == '.' && tName[1] == '/') { tName = tName.after(1); } // If anything was removed, we have to add the directory. if (tName.length() < name.length()) { // If ./ was removed, add directory of otherName. // Otherwise add the full otherName. Path dir(otherName); if (tName.length() == name.length() - 2) { dir = Path(dir.dirName()); } dir.append (tName); return dir.originalName(); } else { // If name ends in /. and if it matches the end of otherName, // we have to return the remainder of otherName. Int leng = tName.length(); if (leng >= 3 && tName[leng-2] == '/' && tName[leng-1] == '.') { leng -= 2; String oName(otherName); Int oleng = oName.length(); if (oleng >= leng+2 && '/'+tName.before(leng) == oName.from(oleng-leng-1)) { return oName.before(oleng-leng-1); } } } return tName; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/OS/Path.h000066400000000000000000000245221476623553700156260ustar00rootroot00000000000000//# Path.h: Path name of a file //# Copyright (C) 1993,1994,1995,1996,1997,1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_PATH_H #define CASA_PATH_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Path name of a file // // // // //
        49. Basic knowledge of the UNIX file system // // // The term 'path' is the standard term for describing the location of a file // in a hierarchy of possibly nested directories. In order to find a // particular file you must travel a specific path strating from a known // point. We use the term in its standard sense in this class. // // // This class can be used to describe a pathname. One can also create, // validate, parse (get base or directory names or original, expanded or // absolute names), query and append strings. The client programmer can // give a string, at construction, which describes a path. This string can // be a relative or an absolute name. Environment variables and a tilde // (with or without user name) can also be used in the string and will // be expanded by the function expandedName. //
          The function // Once a Path has been constructed, you can query the object for its // original name, expanded name, absolute name, the name of the directory // where it is found or the name of only the file. Expanding the path name // means that possible environment variables and tilde get expanded. // There are also functions to get the length or maximum length of a path. // Pathnames can also be checked on correctness and they can be checked // if they conform the POSIX standard. //
          // // In this example a few pathnames are created. // // Path test1("~/test/$TEST1/.."); // absolute path // Path test2("/$HOME/./analyse"); // absolute path // Path test3("myFile"); // relative path // // cout << test1.originalName() << endl; // // // Test1 is according the POSIX standard // if (test1.isStrictlyPosix()){ // cout << "test1 is strictly POSIX << endl; // } // // // Test1 is valid // if (test1.isValid()){ // cout << test1.isValid() << endl; // } // // // if "TEST1=$TEST2 and TEST2=$TEST1"(recursive environment variables) // // an exception will be thrown. ~ is replaced by the homedirectory // cout << test1.expandedName() << endl; // // $HOME is expanded // cout << test2.expandedName() << endl; // cout << test1.absoluteName() << endl; // cout << test2.absoluteName() << endl; // cout << test2.baseName() << endl; // cout << test1.dirName() << endl; // cout << test3.originalName() << endl; // myFile is returned // cout << test3.expandedName() << endl; // Nothing is changed // cout << test3.absoluteName() << endl; // The current working directory // is placed before 'myFile' // cout << test3.baseName() << endl; // The current working directory // // is returned // cout << test3.dirName() << endl; // myFile is returned // // // // Programmer convenience and (eventually) OS independence. // // //
        50. To make the class OS independent some functions should be rebuild. // These functions could be expandedName or absoluteName. //
        51. The function expandedName or absoluteName could map the filename to // the native convention //
        52. A (maybe static) function contractName(const String& pathName) // could be implemented to remove . and .. from the file name. // class Path { public: // Default constructor, the path is set to . (working directory). Path(); // Construct a path with the given name. // When the name is empty, it is set to . (working directory). // It is not checked if the path name is valid. // Function isValid() can be used for that purpose. Path (const String& pathName); // Copy constructor, copy semantics. Path (const Path& that); // Destructor ~Path(); // Assignment, copy semantics. Path& operator= (const Path& that); // Append a string to the path name. // When the current path does not end with a / and the string to append // does not start with a /, an intermediate / is also added. void append (const String& string); // Returns the string as given at construction. const String& originalName () const; // Return a string giving the expanded pathname. // This means that the environment variables are expanded and the tilde // is replaced by the home directory. An expanded name can still // be a relative path. // An exception is thrown when converting a recursive environment // variable results in an endless loop (that is, more than 25 // substitutions). const String& expandedName () const; // Return the string which giving the absolute pathname. // It is generated from the expanded pathname by adding // the working directory when needed. const String& absoluteName () const; // Return the realpath which is the absolute pathname with possible // symlinks resolved. It also resolves //, /./, /../ and trailing /. //
          The path must be an existing file or directory. // It uses the system's realpath function. In case it fails, // an exception is thrown. String resolvedName() const; // Check if pathname is valid. This function checks for: double slashes, // non-printable characters, pathname length and filename lengths, this // function is more OS-specific. Bool isValid() const; // Check if pathname is valid according the POSIX standard. // This function checks for // double slashes, non-printable characters,pathname length and filename // lenghts, all according to the POSIX-standard. Bool isStrictlyPosix() const; // Return length of path name uInt length() const; // Return the maximum length a path name can have. uInt maxLength() const; // Return the basename of the path; this is only the name of the file. // It takes it from the expanded path name. String baseName() const; // Return the dirname of the path; this is the directory where the // filename is found. It takes it from the expanded path name. //
          To get the absolute dirname one could do: // // Path tmpPath (myPath.dirName()); // String absDir (tmpPath.absoluteName()); // // or // // Path tmpPath (myPath.absoluteName()); // String absDir (tmpPath.dirName()); // String dirName() const; // Strip otherName from this name. If stripped, the result gets a // leading ././ // If not stripped, it is tried if name can be stripped from otherName. // If stripped, the result gets a trailing /. // If still not stripped, it is tried to strip the directory of otherName. // If that succeeds, the result gets a leading ./ // This is used by RefTable and TableKeyword to ensure that the // name of a subtable or referenced table is always relative to // the main table. static String stripDirectory (const String& name, const String& otherName); // If the name starts with ././ add otherName to it. // If the name ends with /. strip name from otherName and return the // remainder. // If the name starts with ./ add the directory of otherName to it. // It is the opposite of stripDirectory. static String addDirectory (const String& name, const String& otherName); private: // Strings to describe the pathname in three different ways. String itsOriginalPathName; // These variables are pointer to strings because the functions which use // these variables are const functions. This means that they would not be // able to modify the string, now they can. mutable String itsAbsolutePathName; mutable String itsExpandedPathName; // Define the maximum number of bytes in a pathname // This definition does not use Posix values. static uInt getMaxPathNameSize (); // Define the maximum number of bytes in a filename // This definition does not use Posix values. static uInt getMaxNameSize (); // This function is used by expandedName to replace the tilde and to // expand the environment variables String expandName (const String& inString) const; // This function is used by absoluteName to make a name absolute, // this means that the name is described from the root String makeAbsoluteName (const String& inString) const; // Remove . and .. from the path name. // Also multiple slashes are replaced by a single. String removeDots (const String& inString) const; // This function is used by expandName and absoluteName. It sets the // integer "count" on the next slash or on the end of a string void getNextName (const String& inString, uInt& count) const; }; inline const String& Path::originalName() const { return itsOriginalPathName; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/OS/PrecTimer.cc000066400000000000000000000105471476623553700167640ustar00rootroot00000000000000//# PrecTimer.cc: Precision timer to measure elapsed times in a cumulative way //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #if defined __APPLE__ #include #include #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN double PrecTimer::CPU_speed_in_MHz = PrecTimer::get_CPU_speed_in_MHz(); double PrecTimer::get_CPU_speed_in_MHz() { // first a few sanity checks AlwaysAssert(sizeof(int) == 4, AipsError); AlwaysAssert(sizeof(long long) == 8, AipsError); #if (defined __linux__ || __APPLE__) && \ (defined __i386__ || defined __x86_64__ || defined __ia64__ || defined __PPC__) && \ (defined __GNUC__ || defined __INTEL_COMPILER || defined __PATHSCALE__ || defined __xlC__) ifstream infile("/proc/cpuinfo"); char buffer[256]; while (infile.good()) { infile.getline(buffer, 256); #if defined __PPC__ char* colon; if (strcmp("cpu\t\t: 450 Blue Gene/P DD2", buffer) == 0) { return 850.0; } if (strcmp("machine\t\t: Blue Gene", buffer) == 0) { return 700.0; } if (strncmp("timebase", buffer, 8) == 0 && (colon = strchr(buffer, ':')) != 0) { return atof(colon + 2) / 1e6; } #elif defined __APPLE__ // Macintosh int mib[2] = { CTL_HW, HW_CPU_FREQ }; double result = 0; size_t size = sizeof(result); if( sysctl(mib, 2, &result, &size, NULL, 0) != -1) { return result / 1e6; } #else char* colon; if (strncmp("cpu MHz", buffer, 7) == 0 && (colon = strchr(buffer, ':')) != 0) { return atof(colon + 2); } #endif } return 0.0; #else #warning partially supported architecture return 0.0; #endif } double PrecTimer::getReal() const { double time = u1.total_time / 1e6; if (CPU_speed_in_MHz > 0) { time /= CPU_speed_in_MHz; } return time; } void PrecTimer::show() const { show (cout); } void PrecTimer::show (ostream& os) const { if (u2.count == 0) { os << "not used\n"; } else { double total = static_cast(u1.total_time); if (CPU_speed_in_MHz == 0) { os << "avg = " << total / static_cast(u2.count); os << ", total = " << u1.total_time << " cycles"; } else { total /= 1e6 * CPU_speed_in_MHz; os << "avg = "; print_time(os, total / static_cast(u2.count)); os << ", total = "; print_time(os, total); } os << ", count = " << setw(9) << u2.count << endl; } } void PrecTimer::show (const String& s) const { show (cout, s); } void PrecTimer::show (ostream &os, const String& s) const { os << s << ": "; show (os); } void PrecTimer::print_time (ostream& os, double time) const { // Print the time in a suitable unit. static char units[] = { 'n', 'u', 'm', ' ', 'k' }; time = 1000.0 * time / CPU_speed_in_MHz; int i=0; while (time >= 999.5 && i<5) { time /= 1000.0; ++i; } os << setprecision(3) << setw(5) << time << ' ' << units[i] << 's'; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/OS/PrecTimer.h000066400000000000000000000220131476623553700166150ustar00rootroot00000000000000//# PrecTimer.h: Precision timer to measure elapsed times in a cumulative way //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_PRECTIMER_H #define CASA_PRECTIMER_H #include #include #include #if defined __ia64__ && defined __INTEL_COMPILER #include #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN // Forward Declaration. class String; // // Precision timer to measure elapsed times in a cumulative way // // // // // // The PrecTimer supplements the Timer class. // If offers a low-overhead and high-resolution interval timer for use // on i386, x86_64, ia64, and powerpc platforms, using the processor's // timestamp counter that is incremented each cycle. // Put timer.start() and timer.stop() calls around the piece of // code to be timed. Because the timer is cumulative, the total time of // a particular piece of code can be timed. // // Make sure that start() and stop() calls alternate, // otherwise very strange times will be the result. // // // A timer can be started and stopped multiple times; both the average and // total time, as well as the number of iterations are printed. // The measured time is real time (as opposed to user or system time). // The timer can be used to measure from 10 nanosecond to a century interval. // // Multiple timers can be used in a nested way as long as each of them // has independent (matching) start and stop calls. // // The class is more or less a copy of the original written by John Romein // at ASTRON, Dwingeloo, the Netherlands. // // // Here's how to create a timer, start it (the 'mark' member function) // and display a breakdown. // // PrecTimer ttimer; // the timer is reset at construction time // PrecTimer ctimer; // ttimer.reset(); // if you want to reset the timer (not needed here) // ttimer.start(); // start the total timer // for (int i=0; i // class PrecTimer { public: // Construct. PrecTimer(); // Destruct. ~PrecTimer(); // Restart the timer. void start(); // Stop the timer void stop(); // Reset the timer to zero. void reset(); // Show real time on cout or a user supplied stream. // void show() const; void show (std::ostream& os) const; // // Show real time on cout or a user supplied // stream preceeded by the string parameter. // void show (const String&) const; void show (std::ostream& os, const String& prefix) const; // // Get the real time (in seconds). double getReal() const; // Get the total number of times start/stop is done. unsigned long long getCount() const; private: void print_time (std::ostream&, double time) const; struct TimeStruct { #if defined __PPC__ int total_time_high, total_time_low; #else int total_time_low, total_time_high; #endif }; union Union1 { long long total_time; TimeStruct s1; }; #if defined __i386__ && defined __INTEL_COMPILER && defined _OPENMP struct CountStruct { int count_low, count_high; }; union Union2 { unsigned long long count; CountStruct s2; }; #else struct Union2 { unsigned long long count; }; #endif Union1 u1; Union2 u2; static double CPU_speed_in_MHz; static double get_CPU_speed_in_MHz(); }; inline void PrecTimer::reset() { u1.total_time = 0; u2.count = 0; } inline unsigned long long PrecTimer::getCount() const { return u2.count; } inline PrecTimer::PrecTimer() { reset(); } inline PrecTimer::~PrecTimer() {} inline void PrecTimer::start() { #if defined __x86_64__ && defined __INTEL_COMPILER && defined _OPENMP asm volatile ( "rdtsc\n\t" "shlq $32,%%rdx\n\t" "leaq (%%rax,%%rdx),%%rax\n\t" "lock;subq %%rax,%0" : "+m" (u1.total_time) : : "rax", "rdx" ); #elif defined __i386__ && defined __INTEL_COMPILER && defined _OPENMP asm volatile ( "rdtsc\n\t" "lock;subl %%eax,%0\n\t" "lock;sbbl %%edx,%1" : "+m" (u1.s1.total_time_low), "+m" (u1.s1total_time_high) : : "eax", "edx" ); #elif (defined __i386__ || defined __x86_64__) && (defined __PATHSCALE__ || (defined __APPLE__ && defined __APPLE_CC__ && __APPLE_CC__ == 5531)) unsigned eax, edx; asm volatile ("rdtsc" : "=a" (eax), "=d" (edx)); u1.total_time -= ((unsigned long long) edx << 32) + eax; #elif (defined __i386__ || defined __x86_64__) && (defined __GNUC__ || defined __INTEL_COMPILER) asm volatile ( "rdtsc\n\t" "subl %%eax, %0\n\t" "sbbl %%edx, %1" : "+m" (u1.s1.total_time_low), "+m" (u1.s1.total_time_high) : : "eax", "edx" ); #elif defined __ia64__ && defined __INTEL_COMPILER u1.total_time -= __getReg(_IA64_REG_AR_ITC); #elif defined __ia64__ && defined __GNUC__ long long time; asm volatile ("mov %0=ar.itc" : "=r" (time)); u1.total_time -= time; #elif defined __PPC__ && (defined __GNUC__ || defined __xlC__) int high, low, retry; asm ( "0:\n\t" "mftbu %0\n\t" "mftb %1\n\t" "mftbu %2\n\t" "cmpw %2,%0\n\t" "bne 0b\n\t" "subfc %3,%1,%3\n\t" "subfe %4,%0,%4" : "=r" (high), "=r" (low), "=r" (retry), "=r" (u1.s1.total_time_low), "=r" (u1.s1.total_time_high) : "3" (u1.s1.total_time_low), "4" (u1.s1.total_time_high) ); #endif } inline void PrecTimer::stop() { #if defined __x86_64__ && defined __INTEL_COMPILER && defined _OPENMP asm volatile ( "rdtsc\n\t" "shlq $32,%%rdx\n\t" "leaq (%%rax,%%rdx),%%rax\n\t" "lock;addq %%rax,%0" : "+m" (u1.total_time) : : "rax", "rdx" ); #elif defined __i386__ && defined __INTEL_COMPILER && defined _OPENMP asm volatile ( "rdtsc\n\t" "lock;addl %%eax, %0\n\t" "lock;adcl %%edx, %1" : "+m" (u1.s1.total_time_low), "+m" (u1.s1.total_time_high) : : "eax", "edx" ); #elif (defined __i386__ || defined __x86_64__) && (defined __PATHSCALE__ || (defined __APPLE__ && defined __APPLE_CC__ && __APPLE_CC__ == 5531)) unsigned eax, edx; asm volatile ("rdtsc\n\t" : "=a" (eax), "=d" (edx)); u1.total_time += ((unsigned long long) edx << 32) + eax; #elif (defined __i386__ || defined __x86_64__) && (defined __GNUC__ || defined __INTEL_COMPILER) asm volatile ( "rdtsc\n\t" "addl %%eax, %0\n\t" "adcl %%edx, %1" : "+m" (u1.s1.total_time_low), "+m" (u1.s1.total_time_high) : : "eax", "edx" ); #elif defined __ia64__ && defined __INTEL_COMPILER u1.total_time += __getReg(_IA64_REG_AR_ITC); #elif defined __ia64__ && defined __GNUC__ long long time; asm volatile ("mov %0=ar.itc" : "=r" (time)); u1.total_time += time; #elif defined __PPC__ && (defined __GNUC__ || defined __xlC__) int high, low, retry; asm ( "0:\n\t" "mftbu %0\n\t" "mftb %1\n\t" "mftbu %2\n\t" "cmpw %2,%0\n\t" "bne 0b\n\t" "addc %3,%3,%1\n\t" "adde %4,%4,%0" : "=r" (high), "=r" (low), "=r" (retry), "=r" (u1.s1.total_time_low), "=r" (u1.s1.total_time_high) : "3" (u1.s1.total_time_low), "4" (u1.s1.total_time_high) ); #endif #if defined __x86_64__ && defined __INTEL_COMPILER && defined _OPENMP asm volatile ("lock;addq $1,%0" : "+m" (u2.count)); #elif defined __i386__ && defined __INTEL_COMPILER && defined _OPENMP asm volatile ( "lock;addl $1,%0\n\t" "lock;adcl $0,%1" : "+m" (u2.s2.count_low), "+m" (u2.s2.count_high) ); #else ++u2.count; #endif } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/OS/RawDataConversion.cc000066400000000000000000000050321476623553700204540ustar00rootroot00000000000000//# RawDataConversion.cc: A class with virtual functions to copy without conversion //# Copyright (C) 1996,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include namespace casacore { //# NAMESPACE CASACORE - BEGIN RawDataConversion::~RawDataConversion() {} #define RAWDATACONVERSION_DOIT(T) \ size_t RawDataConversion::toLocal (T& to, \ const void* from) const \ { \ memcpy (&to, from, sizeof(T)); \ return sizeof(T); \ } \ size_t RawDataConversion::toLocal (T* to, const void* from, \ size_t nr) const \ { \ memcpy (to, from, nr * sizeof(T)); \ return nr * sizeof(T); \ } \ size_t RawDataConversion::fromLocal (void* to, T from) const \ { \ memcpy (to, &from, sizeof(T)); \ return sizeof(T); \ } \ size_t RawDataConversion::fromLocal (void* to, const T* from, \ size_t nr) const \ { \ memcpy (to, from, nr * sizeof(T)); \ return nr * sizeof(T); \ } \ Bool RawDataConversion::canCopy (const T*) const \ { \ return True; \ } \ unsigned int RawDataConversion::externalSize (const T*) const \ { \ return sizeof(T); \ } RAWDATACONVERSION_DOIT(char) RAWDATACONVERSION_DOIT(unsigned char) RAWDATACONVERSION_DOIT(short) RAWDATACONVERSION_DOIT(unsigned short) RAWDATACONVERSION_DOIT(int) RAWDATACONVERSION_DOIT(unsigned int) RAWDATACONVERSION_DOIT(Int64) RAWDATACONVERSION_DOIT(uInt64) RAWDATACONVERSION_DOIT(float) RAWDATACONVERSION_DOIT(double) } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/OS/RawDataConversion.h000066400000000000000000000200251476623553700203150ustar00rootroot00000000000000//# RawDataConversion.h: A class with virtual functions to copy without conversion //# Copyright (C) 1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_RAWDATACONVERSION_H #define CASA_RAWDATACONVERSION_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A class with virtual functions to copy without conversion // // // // // // This class is a specialization of the abstract base class // DataConversion. // It contains functions to copy data without conversion. //

          // This class exists to make it possible to use a DataConversion // object when no conversion is actually needed. // // // See example in class DataConversion. // // // This class makes it possible to use the conversion framework // for operations where a conversion is only a simple copy. // // //

        53. Support data type long double. // class RawDataConversion : public DataConversion { public: // Construct the object. RawDataConversion(); virtual ~RawDataConversion(); // Copy one value from external to local (is a simple memcpy). // The from and to buffer should not overlap. // size_t toLocal (char& to, const void* from) const override; size_t toLocal (unsigned char& to, const void* from) const override; size_t toLocal (short& to, const void* from) const override; size_t toLocal (unsigned short& to, const void* from) const override; size_t toLocal (int& to, const void* from) const override; size_t toLocal (unsigned int& to, const void* from) const override; size_t toLocal (Int64& to, const void* from) const override; size_t toLocal (uInt64& to, const void* from) const override; size_t toLocal (float& to, const void* from) const override; size_t toLocal (double& to, const void* from) const override; // // Copy multiple values from external to local (is a simple memcpy). // The from and to buffer should not overlap. // size_t toLocal (char* to, const void* from, size_t nr) const override; size_t toLocal (unsigned char* to, const void* from, size_t nr) const override; size_t toLocal (short* to, const void* from, size_t nr) const override; size_t toLocal (unsigned short* to, const void* from, size_t nr) const override; size_t toLocal (int* to, const void* from, size_t nr) const override; size_t toLocal (unsigned int* to, const void* from, size_t nr) const override; size_t toLocal (Int64* to, const void* from, size_t nr) const override; size_t toLocal (uInt64* to, const void* from, size_t nr) const override; size_t toLocal (float* to, const void* from, size_t nr) const override; size_t toLocal (double* to, const void* from, size_t nr) const override; // // Copy one value from local to external (is a simple memcpy). // The from and to buffer should not overlap. // size_t fromLocal (void* to, char from) const override; size_t fromLocal (void* to, unsigned char from) const override; size_t fromLocal (void* to, short from) const override; size_t fromLocal (void* to, unsigned short from) const override; size_t fromLocal (void* to, int from) const override; size_t fromLocal (void* to, unsigned int from) const override; size_t fromLocal (void* to, Int64 from) const override; size_t fromLocal (void* to, uInt64 from) const override; size_t fromLocal (void* to, float from) const override; size_t fromLocal (void* to, double from) const override; // // Copy multiple values from local to external (is a simple memcpy). // The from and to buffer should not overlap. // size_t fromLocal (void* to, const char* from, size_t nr) const override; size_t fromLocal (void* to, const unsigned char* from, size_t nr) const override; size_t fromLocal (void* to, const short* from, size_t nr) const override; size_t fromLocal (void* to, const unsigned short* from, size_t nr) const override; size_t fromLocal (void* to, const int* from, size_t nr) const override; size_t fromLocal (void* to, const unsigned int* from, size_t nr) const override; size_t fromLocal (void* to, const Int64* from, size_t nr) const override; size_t fromLocal (void* to, const uInt64* from, size_t nr) const override; size_t fromLocal (void* to, const float* from, size_t nr) const override; size_t fromLocal (void* to, const double* from, size_t nr) const override; // // Determine if the data for a data type can be simply copied, thus // if no conversion is needed. This is always True. // Bool canCopy (const char*) const override; Bool canCopy (const unsigned char*) const override; Bool canCopy (const short*) const override; Bool canCopy (const unsigned short*) const override; Bool canCopy (const int*) const override; Bool canCopy (const unsigned int*) const override; Bool canCopy (const Int64*) const override; Bool canCopy (const uInt64*) const override; Bool canCopy (const float*) const override; Bool canCopy (const double*) const override; // // Get the external size of the data type. // This returns the sizeof. // unsigned int externalSize (const char*) const override; unsigned int externalSize (const unsigned char*) const override; unsigned int externalSize (const short*) const override; unsigned int externalSize (const unsigned short*) const override; unsigned int externalSize (const int*) const override; unsigned int externalSize (const unsigned int*) const override; unsigned int externalSize (const Int64*) const override; unsigned int externalSize (const uInt64*) const override; unsigned int externalSize (const float*) const override; unsigned int externalSize (const double*) const override; // }; inline RawDataConversion::RawDataConversion() {} } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/OS/RegularFile.cc000066400000000000000000000152621476623553700172720ustar00rootroot00000000000000//# RegularFile.cc: Manipulate and get information about regular files //# Copyright (C) 1993,1994,1995,1996,1997,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include // needed for creat #include // needed for unlink, etc. #include // needed for errno #include // needed for strerror #include // needed for system namespace casacore { //# NAMESPACE CASACORE - BEGIN RegularFile::RegularFile () : File() {} RegularFile::RegularFile (const Path& path) : File(path) { checkPath(); } RegularFile::RegularFile (const String& path) : File(path) { checkPath(); } RegularFile::RegularFile (const char* path) : File(String(path)) { checkPath(); } RegularFile::RegularFile (const File& file) : File(file) { checkPath(); } RegularFile::RegularFile (const RegularFile& that) : File (that), itsFile (that.itsFile) {} RegularFile::~RegularFile() {} RegularFile& RegularFile::operator= (const RegularFile& that) { if (this != &that) { File::operator= (that); itsFile = that.itsFile; } return *this; } void RegularFile::checkPath() { itsFile = *this; // If exists, check if it is a regular file. // If the file is a symlink, resolve the entire symlink chain. // Otherwise check if it can be created. if (exists()) { if (isSymLink()) { itsFile = SymLink(*this).followSymLink(); // Error if no regular file and exists or cannot be created. if (!itsFile.isRegular()) { if (itsFile.exists() || !itsFile.canCreate()) { throw (AipsError ("RegularFile: " + path().expandedName() + " is a symbolic link not" " pointing to a valid regular file")); } } } else if (!isRegular()) { throw (AipsError ("RegularFile: " + path().expandedName() + " exists, but is no regular file")); } } else { if (!canCreate()) { throw (AipsError ("RegularFile: " + path().expandedName() + " does not exist and cannot be created")); } } } void RegularFile::create (Bool overwrite) { // If overwrite is False the file will not be overwritten. if (exists()) { if (!itsFile.isRegular (False)) { throw (AipsError ("RegularFile::create: " + itsFile.path().expandedName() + " already exists as a non-regular file")); } if (!overwrite) { throw (AipsError ("RegularFile::create: " + itsFile.path().expandedName() + " already exists")); } } int fd = ::creat (itsFile.path().expandedName().chars(), 0666); if (fd < 0) { throw (AipsError ("RegularFile::create error on " + itsFile.path().expandedName() + ": " + strerror(errno))); } ::close (fd); } void RegularFile::remove() { if (isSymLink()) { removeSymLinks(); } unlink (itsFile.path().expandedName().chars()); } void RegularFile::copy (const Path& target, Bool overwrite, Bool setUserWritePermission) const { Path targetName(target); checkTarget (targetName, overwrite); #if defined(AIPS_CRAY_PGI) manualCopy (itsFile.path().expandedName(), targetName.expandedName()); #else // This function uses the system function cp. String call("cp '"); call += itsFile.path().expandedName() + "' '" + targetName.expandedName() + "'"; AlwaysAssert (system(call.chars()) == 0, AipsError); if (setUserWritePermission) { File result(targetName.expandedName()); if (! result.isWritable()) { result.setPermissions (result.readPermissions() | 0200); } } #endif } void RegularFile::manualCopy (const String& source, const String& target) { int infd (FiledesIO::open (source.chars())); int outfd (FiledesIO::create (target.chars())); FiledesIO in (infd, source); FiledesIO out (outfd, target); char buf[32768]; int nrc = in.read (sizeof(buf), buf, False); while (true) { AlwaysAssert (nrc >= 0, AipsError); out.write (nrc, buf); if (nrc != sizeof(buf)) { break; } nrc = in.read (sizeof(buf), buf, False); } FiledesIO::close (infd); FiledesIO::close (outfd); } void RegularFile::move (const Path& target, Bool overwrite) { Path targetPath(target); checkTarget (targetPath, overwrite); // Start trying to rename. // If source and target are the same directory, rename does nothing // and returns a success status. if (rename (path().expandedName().chars(), targetPath.expandedName().chars()) == 0) { return; } // The rename failed for one reason or another. // Remove the target if it already exists. Bool alrExist = False; if (errno == EEXIST) { alrExist = True; } #if defined(EBUSY) if (errno == EBUSY) { alrExist = True; } #endif if (alrExist) { unlink (targetPath.expandedName().chars()); } // Try again. if (rename (path().expandedName().chars(), targetPath.expandedName().chars()) == 0) { return; } // Throw an exception if not "different file systems" error. if (errno != EXDEV) { throw (AipsError ("RegularFile::move error on " + path().expandedName() + " to " + targetPath.expandedName() + ": " + strerror(errno))); } // Copy the file and remove it thereafter. copy (targetPath, overwrite, False); remove(); } Int64 RegularFile::size() const { return itsFile.size(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/OS/RegularFile.h000066400000000000000000000161371476623553700171360ustar00rootroot00000000000000//# RegularFile.h: Manipulate and get information about regular files //# Copyright (C) 1993,1994,1995,1996,1997,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_REGULARFILE_H #define CASA_REGULARFILE_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Manipulate and get information about regular files // // // // // //
        54. Basic knowledge of the UNIX file system //
        55. File // // // The class RegularFile provides functions for manipulating and getting // information about regular files. Regular file are files which hold data. // // // This class provides functions to manipulate and to get information about // regular files. The functions for getting information (like ownership, dates) // about regular files are inherited from the File // class. //
          // The class RegularFile itself provides functions to rename, remove, copy, // and move regular files. The file name can be a symbolic link resolving // (eventually) to a regular file. //
          // // // // Create the object rFile // RegularFile rFile ("isFile"); // // // Create file; if the file exists it will be overwritten // rFile.create (True); // rFile.copy (newPath); // // cout << rFile.size() << endl; // Get the size of the file // cout << rFile.path() << endl; // Show the relative pathname // // rFile.remove(); // remove the file // // // // Provide functions for manipulating and getting information // about regular files. // class RegularFile: public File { public: // Default constructor sets path to . (working directory). RegularFile(); // Create a regular file object for a file with the given path name. // An exception is thrown if the file is illegal, i.e. if it does // not exist as a regular file or symbolic link or if cannot be created. // Note that the file is not created if it does not exist yet. // This can be done using the function create. //
          // When the given path name is a symbolic link, the symbolic link // is resolved (recursively) and the resulting file name is used // instead. // RegularFile (const Path& path); RegularFile (const String& path); RegularFile (const char* path); RegularFile (const File& file); // // Copy constructor (copy semantics). RegularFile (const RegularFile& regularFile); ~RegularFile(); // Assignment (copy semantics). RegularFile& operator= (const RegularFile& regularFile); // Create the regular file. //
          If the file exists and is not a regular file, an // exception is thrown. Otherwise if overwrite is true the regular file // will be overwritten. If overwrite is false then nothing will be done. // If the file does not exist, it is created. void create (Bool overwrite = True); // Remove the file. // If it does not exist, an exception will be thrown. // If a symbolic link is given, the link chain pointing to the file // will also be removed. void remove(); // Copy the file to the target path using the system command cp. // If the file is a symbolic link, the regular file pointed to // will be copied. // The target path can be a directory or a file (as in cp). // An exception is thrown if: //
          - the target directory is not writable //
          - or the target file already exists and overwrite==False //
          - or the target file already exists and is not writable // // When a readonly file is copied, the resulting // file is also readonly. Therefore chmod is used to // set user write permission after the copy. // The flag setUserWritePermission can be set to False // when that should not be done. // // void copy (const Path& target, Bool overwrite = True, Bool setUserWritePermission = True) const; void copy (const String& target, Bool overwrite = True, Bool setUserWritePermission = True) const; // // Copy the file manually in case the cp command cannot be used. // (like on the Cray XT3). static void manualCopy (const String& source, const String& target); // Move the file to the target path using the system command mv. // If the file is a symbolic link, the regular file pointed to // will be moved. // The target path can be a directory or a file (as in mv). // An exception is thrown if: //
          - the target directory is not writable //
          - or the target file already exists and overwrite==False //
          - or the target file already exists and is not writable // The system command mv is used instead of the // library function rename to be able to move across file systems. // // void move (const Path& target, Bool overwrite = True); void move (const String& target, Bool overwrite = True); // // Return the size of the file. If the file // does not exist, an exception will be thrown. virtual Int64 size() const; private: // Check if the path of the file is valid. // Also resolve possible symlinks. void checkPath(); // This variable is used when a symbolic link points to the file. File itsFile; }; inline void RegularFile::copy (const String& target, Bool overwrite, Bool setUserWritePermission) const { copy (Path(target), overwrite, setUserWritePermission); } inline void RegularFile::move (const String& target, Bool overwrite) { move (Path(target), overwrite); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/OS/SymLink.cc000066400000000000000000000121531476623553700164530ustar00rootroot00000000000000//# SymLink.cc: Class to define a Symbolic Link //# Copyright (C) 1996,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include // needed for unlink #include // needed for errno #include // needed for strerror namespace casacore { //# NAMESPACE CASACORE - BEGIN SymLink::SymLink() : File() {} SymLink::SymLink (const Path& name) : File(name) { checkPath(); } SymLink::SymLink (const String& name) : File(name) { checkPath(); } SymLink::SymLink (const File& name) : File(name) { checkPath(); } SymLink::SymLink (const SymLink& that) : File(that) {} SymLink::~SymLink() {} SymLink& SymLink::operator= (const SymLink& that) { if (this != &that){ File::operator = (that); } return *this; } void SymLink::checkPath() const { // If exists, check if it is a symlink. // Otherwise check if it can be created. if (exists()) { if (!isSymLink()) { throw (AipsError ("SymLink: " + path().expandedName() + " exists, but is no symbolic link")); } } else { if (!canCreate()) { throw (AipsError ("SymLink: " + path().expandedName() + " does not exist and cannot be created")); } } } void SymLink::create (const Path& target, Bool overwrite) { // If overwrite is False the file will not be overwritten. if (exists()) { if (!isSymLink()) { throw (AipsError ("SymLink::create: " + path().expandedName() + " already exists as a non-symlink")); } if (!overwrite) { throw (AipsError ("SymLink::create: " + path().expandedName() + " already exists")); } // Remove an existing symlink, otherwise symlink fails. remove(); } if (symlink (target.expandedName().chars(), path().expandedName().chars()) < 0) { throw (AipsError ("SymLink::create error on " + target.expandedName() + ": " + strerror(errno))); } } void SymLink::remove() { unlink (path().expandedName().chars()); } void SymLink::copy (const Path& target, Bool overwrite) const { Path targetName(target); checkTarget (targetName, overwrite); // This function cannot the system function cp, because // that copies the file the symlink is pointing to and // refuses to copy when it points to a directory. File targetFile(targetName); if (targetFile.isRegular (False)) { RegularFile(targetFile).remove(); } SymLink newLink(targetFile); newLink.create (getSymLink()); } void SymLink::move (const Path& target, Bool overwrite) { Path targetName(target); checkTarget (targetName, overwrite); File targetFile(targetName); if (targetFile.isRegular (False)) { RegularFile(targetFile).remove(); } SymLink newLink(targetFile); newLink.create (getSymLink()); remove(); } String SymLink::getSymLink() const { // Create a buffer for readlink. char buf[2048]; int length; // read the link, and place the result in buf, length is the number // of characters placed in buf by readlink length = readlink (path().expandedName().chars(), buf, 2048); if (length <= 0) { throw (AipsError ("SymLink: " + path().expandedName() + " does not exist")); } return String (buf, length); } Path SymLink::readSymLink() const { Path result (getSymLink()); // Prepend with dirname if no absolute name. if (result.originalName().firstchar() != '/') { result = path().dirName() + "/" + result.originalName(); } return result; } Path SymLink::followSymLink() const { // Do it max. 25 times to avoid endless loops. Path result; File file(*this); Int count = 0; do { if (++count > 25) { throw (AipsError ("SymLink: resolving " + path().expandedName() + " results in an endless loop")); } SymLink link(file); result = link.readSymLink(); file = File(result); } while (file.isSymLink()); return result; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/OS/SymLink.h000066400000000000000000000157331476623553700163240ustar00rootroot00000000000000//# SymLink.h: Get information about, and manipulate symbolic links //# Copyright (C) 1996 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_SYMLINK_H #define CASA_SYMLINK_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Get information about, and manipulate symbolic links // // // // // //
        56. Basic knowledge of the UNIX file system //
        57. File // // // The class SymLink handles SYMbolic LINKs in the file system. // // // SymLink provides functions to manipulate and to get information about // symbolic links. The functions for getting information (like ownership, // dates) about symbolic links are inherited from the // File class. //
          // The class SymLink itself provides functions to create, remove, copy, and // move symbolic links. There is a function readSymLink which reads a link and // then returns a path and there is a function followSymLink which reads a // link recursively. If the link eventually refers to itself (a loop), // an exception will be thrown. //
          // // // SymLink symLink1("isLink"); // SymLink symLink2("isLink2"); // SymLink symLinkA("A"); // SymLink symLinkB("B"); // // symLink1.create("~", True); // Create a symbolic link to the home // // directory. When it exists it will be // // overwritten. // symLink2.create("isLink", False); // Create a symbolic link to // // isLink. When it exists it will not // // be overwritten. // symLinkA.create(Path("B")); // Create a recursive link // symLinkB.create(Path("A")); // Create a recursive link // // cout << symLink1.readSymLink() << endl; // The homedirectory is printed // cout << symLink2.readSymLink() << endl; // isLink is printed // cout << symLink2.followSymLink() << endl;// The homedirectory is printed // cout << symLinkA.readSymLink() << endl; // B is printed // cout << symLinkA.followSymLink() << endl;// An exception is thrown (loop) // // // // Provide functions for manipulating and getting information // about symbolic links. // class SymLink: public File { public: // The default constructor creates a SymLink with path ".". SymLink(); // Create a SymLink with the given path. // An exception is thrown if the path exist and is no symbolic link // or if it does not exist, but cannot be created. // SymLink (const Path& name); SymLink (const String& name); SymLink (const File& name); // // Copy constructor (copy semantics). SymLink (const SymLink& that); ~SymLink(); // Assignment (copy semantics). SymLink& operator= (const SymLink& that); // Make a symbolic link to a file given by target. // An exception will be thrown if: //
          -target already exists and is no symlink //
          -or target already exists and overwrite==False // void create (const Path& target, Bool overwrite = True); void create (const String& target, Bool overwrite = True); // // Copy the symlink to the target path using the system command cp. // The target path can be a directory or a file (as in cp). // An exception is thrown if: //
          - the target directory is not writable //
          - or the target file already exists and overwrite==False //
          - or the target file already exists and is not writable // void copy (const Path& target, Bool overwrite = True) const; void copy (const String& target, Bool overwrite = True) const; // // Move the symlink to the target path using the system command mv. // The target path can be a directory or a file (as in mv). // An exception is thrown if: //
          - the target directory is not writable //
          - or the target file already exists and overwrite==False //
          - or the target file already exists and is not writable // void move (const Path& target, Bool overwrite = True); void move (const String& target, Bool overwrite = True); // // Remove a symbolic link. void remove(); // Read value of a symbolic link and return it as a Path. If // the symlink does not exist, an exception will be thrown. // When the symlink points to a file with a relative name, // the resulting file name gets prepended by the dirname of the symlink, // which is similar to the way a shell handles symlinks. // E.g. // // ls > subdir/a // ln -s a subdir/b // more subdir/b // // The more command shows the results of subdir/a. Path readSymLink() const; // As readSymLink, but the entire symlink chain is followed // when the symlinks points to other symlinks. // An exception is thrown if this results in a loop (that is, if more // than 25 links are encountered). Path followSymLink() const; private: // Check if the path of the file is valid. // Also resolve possible symlinks. void checkPath() const; // Get the value of the symlink. String getSymLink() const; }; inline void SymLink::create (const String& target, Bool overwrite) { create (Path(target), overwrite); } inline void SymLink::copy (const String& target, Bool overwrite) const { copy (Path(target), overwrite); } inline void SymLink::move (const String& target, Bool overwrite) { move (Path(target), overwrite); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/OS/Time.cc000066400000000000000000000345511476623553700157710ustar00rootroot00000000000000//# Time.cc: Class to handle dates and times //# Copyright (C) 1993,1994,1995,1996,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #if defined(AIPS_SOLARIS) || defined(_AIX) || defined(AIPS_IRIX) #include #elif defined(AIPS_OSF) #include #include #elif defined(AIPS_LINUX) #include #include #else #include #endif #if defined(AIPS_SOLARIS) || defined(AIPS_IRIX) extern time_t altzone; // Not declared in all files. #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN inline double daysFrom1970() { return HostInfo::secondsFrom1970() / C::day; } Time::Time() { now(); } Time::Time(double jdn) { mJulianDayfrac= jdn - (int)jdn; if( mJulianDayfrac >= 0.5) { mJulianDayfrac= mJulianDayfrac - 0.5; mJulianDay= (int)jdn - 2400000; } else { mJulianDayfrac= mJulianDayfrac + 0.5; mJulianDay= (int)jdn - 2400001; } } Time::Time(uInt year, uInt month, uInt day, uInt hour, uInt min, double sec) { setDate (year, month, day, hour, min, sec); } Time::Time( const Time& time ) { // Copy constructor mJulianDay= time.mJulianDay; mJulianDayfrac= time.mJulianDayfrac; } double Time::julianDay() const { // return Julian day return (double)mJulianDay + mJulianDayfrac + 2400000.5; } double Time::modifiedJulianDay() const { // return modify Julian day return (double)mJulianDay + mJulianDayfrac; } Time &Time::operator=(const Time &time) { mJulianDay= time.mJulianDay; mJulianDayfrac= time.mJulianDayfrac; return *this; } double Time::operator-(const Time& begin) { double d,s; s=mJulianDayfrac - begin.mJulianDayfrac; if( s < 0) { s +=1; d = (double)mJulianDay - (double)begin.mJulianDay - 1.0; } else d = (double)mJulianDay - (double)begin.mJulianDay; s=(d+s)*C::day; return s; } Time Time::operator+(const double plus) { Time result; double mjd; mjd = mJulianDayfrac + (plus/C::day - (int)(plus/C::day)); if(mjd >= 1.0) { result.mJulianDay= mJulianDay + (int)(plus/C::day) + 1; result.mJulianDayfrac= mjd - 1.0; } else { result.mJulianDay= mJulianDay + (int)(plus/C::day); result.mJulianDayfrac= mjd; } return result; } Bool Time::operator==(const Time &other) const { if( mJulianDay == other.mJulianDay ) { return mJulianDayfrac == other.mJulianDayfrac; } return False; } Bool Time::operator!=(const Time &other) const { if( mJulianDay == other.mJulianDay ) { return mJulianDayfrac != other.mJulianDayfrac; } return True; } Bool Time::operator>(const Time &other) const { if( mJulianDay > other.mJulianDay ) { return True; } else if (mJulianDay == other.mJulianDay ) { return mJulianDayfrac > other.mJulianDayfrac; } return False; } Bool Time::operator<(const Time &other) const { if( mJulianDay < other.mJulianDay ) { return True; } else if(mJulianDay == other.mJulianDay ) { return mJulianDayfrac < other.mJulianDayfrac; } return False; } String Time::toString(const Bool iso) const { // if iso is True, then use ISO 8601 format // otherwise, // Produce the string of the form // Tue Mar 22 16:40:24 1994 // with GMT time ostringstream out; int i,j,jd,l,n,day,month,year,dayweek,sec; double jdf,hour,min; // Julian day jdf=julianDay()+0.5; jd=(int)jdf; // Julian day fraction jdf=jdf-(int)jdf; l=jd+68569; n=(4*l)/146097; l=l-(146097*n+3)/4; i=(4000*(l+1))/1461001; l=l-(1461*i)/4+31; j=(80*l)/2447; day=l-(2447*j)/80; l=j/11; month=j+2-12*l; year=100*(n-49)+i+l; dayweek=jd-7*((jd+1)/7)+2; hour= jdf*24.0; min= (hour-(int)hour)*60.0; // seconds are rounded to nearest second sec= (int)((min-(int)min)*60.0 + 0.5); if (sec >= 60) { min += 1.0; sec -= 60; } if (min >= 60.0) { hour = hour + 1.0; min -= 60.0; } // I don't think it should be necessary to go past hour // since the day, dayweek, etc. calculation is all // separately if(iso) { out << year << '-'; if (month < 10) out << '0'; out << month << '-'; if (day < 10) out << '0'; out << day << ' '; // NOTE: ISO8601 requires a 'T' char, not space if (hour < 10) out << '0'; out << (int) hour << ':'; if (min < 10) out << '0'; out << (int) min <<':'; if (sec < 10) out << '0'; out << sec; } else { switch(dayweek) { case 1: out<<"Sun "; break; case 2: out<<"Mon "; break; case 3: out<<"Tue "; break; case 4: out<<"Wed "; break; case 5: out<<"Thu "; break; case 6: out<<"Fri "; break; case 7: out<<"Sat "; break; } switch(month) { case 1: out<<"Jan "; break; case 2: out<<"Feb "; break; case 3: out<<"Mar "; break; case 4: out<<"Apr "; break; case 5: out<<"May "; break; case 6: out<<"Jun "; break; case 7: out<<"Jul "; break; case 8: out<<"Aug "; break; case 9: out<<"Sep "; break; case 10: out<<"Oct "; break; case 11: out<<"Nov "; break; case 12: out<<"Dec "; break; } out<>(istream& in, Time& other) { // The operator>> read in date with the next format // month/day/year,hour:min:sec // Is importan this format uInt year=0, month=0, day=0, hour=0, min=0, sec=0; char ch=0; in>>ch; in.putback(ch); in>> month >>ch; if(ch == '/') { in>> day >>ch; } if(ch == '/') { in>> year>>ch; } if(ch == ',') { in>> hour >>ch; } if(ch == ':') { in>> min >>ch; } if(ch == ':') { in>> sec; } other.setDate (year, month, day, hour, min, sec); return in; } void Time::now() { // Return modify Julian day number and update datas double d = daysFrom1970(); // 40587 modify Julian day number = 00:00:00 January 1, 1970, GMT. mJulianDay= 40587 + (int)d; // tmv.tv_usec Microseconds after second. mJulianDayfrac= d - (int)d; } void Time::setDate(uInt year, uInt month, uInt day, uInt hour, uInt min, double sec) { // Converting between Julian calendar date and Julian date number // Valid for all values of year>=-4712 ( for all dates with Julian // Day >= 0). DebugAssert(sec < 60, AipsError); DebugAssert(min < 60, AipsError); DebugAssert(hour < 24, AipsError); DebugAssert(month <= 12, AipsError); if(month==1 || month==3 || month==5 || month==7 || month==8 || month==10 || month==12) { DebugAssert(day <= 31, AipsError); } else if(month==4 || month==6 || month==9 || month==11) { DebugAssert(day <= 30, AipsError); } else { // Check february days for leap years if (isLeapYear(year)) { DebugAssert(day <= 29, AipsError); } else { DebugAssert(day <= 28, AipsError); } } double jd; // the fraction of the day uInt md; // Modify Julian day number int y=year,m=month,d=day; md =(1461*(y+4800+(m-14)/12))/4+(367*(m-2-12*((m-14)/12)))/12-(3*((y+4900+(m-14)/12)/100))/4+d-32075; jd= ((double)hour/24) + ((double)min/1440) + ((double)sec/C::day); // with this formula (calculated md) // date --> Julian day number - 0.5 // then Julian day -1 (jd-1) and fraction +0.5 (md+0.5) mJulianDayfrac = jd; mJulianDay= md - 2400001; } double Time::age() { // time in seconds between some Time object and now double sn = HostInfo::secondsFrom1970(); if(mJulianDay >= 40587) { double jd; jd=(double)mJulianDay - 40587.0; double se = sn - ((jd + mJulianDayfrac)*C::day); return se; } else { sn = 3506716800.0 + sn; double se = sn - (((double)mJulianDay + mJulianDayfrac)*C::day); return se; } } uInt Time::seconds() { // return integral seconds after the minute [0,59] // accuracy of Time seconds is about 2e-5, so add a bit. return (uInt)(dseconds() + 2e-5); } double Time::dseconds() { // return seconds after the minute [0,59] double hour = mJulianDayfrac*24.0; double sec = (hour - hours()) * 3600. - minutes() * 60.; return sec; } uInt Time::minutes() { // return minutes after the hour [0,59] double hour = mJulianDayfrac*24.0; double min = (hour - hours()) * 60.0; // accuracy of Time second is about 2e-5, so add a bit. return uInt(min + 2e-5/60.); } uInt Time::hours() { // return hours after the day [0,23] // accuracy of Time second is 2e-5, so add a bit. return uInt(mJulianDayfrac*24.0 + 2e-5/3600.); } uInt Time::dayOfMonth() { // Return day of the month [1,31] with local time uInt jd,j,l,n,i; // Julian day jd= mJulianDay + 2400001; l=jd+68569; n=(4*l)/146097; l=l-(146097*n+3)/4; i=(4000*(l+1))/1461001; l=l-(1461*i)/4+31; j=(80*l)/2447; return l-(2447*j)/80; } uInt Time::month() { // Return month of the year [1,12] with local time uInt jd,j,l,n,i; // Julian day jd= mJulianDay + 2400001; l=jd+68569; n=(4*l)/146097; l=l-(146097*n+3)/4; i=(4000*(l+1))/1461001; l=l-(1461*i)/4+31; j=(80*l)/2447; l=j/11; return j+2-12*l; } uInt Time::year() { // Return year uInt jd,j,l,n,i; // Julian day jd= mJulianDay + 2400001; l=jd+68569; n=(4*l)/146097; l=l-(146097*n+3)/4; i=(4000*(l+1))/1461001; l=l-(1461*i)/4+31; j=(80*l)/2447; l=j/11; return 100*(n-49)+i+l; } uInt Time::dayOfWeek() { // Return day of the week for the Julian day number. // Where day runs from 1 though 7, with 1 being Sunday uInt jd; // Julian day jd= mJulianDay + 2400001; return jd-7*((jd+1)/7)+2; } uInt Time::dayOfYear() { // Return day of the year for the Julian day number. // Where day runs from 1 though 366. int i,j,jd,l,n,day,month,year; jd= mJulianDay + 2400001; l=jd+68569; n=(4*l)/146097; l=l-(146097*n+3)/4; i=(4000*(l+1))/1461001; l=l-(1461*i)/4+31; j=(80*l)/2447; day=l-(2447*j)/80; l=j/11; month=j+2-12*l; year=100*(n-49)+i+l; switch(month) { case 1: return day; case 2: return day+=31; case 3: day+=59; // jan + feb = 31 + 28 = 59 break; case 4: day+=90; // jan + feb + march = 31+28+31= 90 break; case 5: day+=120; // 31+28+31+30 = 120 break; case 6: day+=151; // 31+28+31+30+31 = 151 break; case 7: day+=181; // 31+28+31+30+31+30 = 181 break; case 8: day+=212; // 31+28+31+30+31+30+31 = 212 break; case 9: day+=243; // 31+28+31+30+31+30+31+31 = 243 break; case 10: day+=273; // 31+28+31+30+31+30+31+31+30 = 273 break; case 11: day+=304; // 31+28+31+30+31+30+31+31+30+31 = 304 break; case 12: day+=334; // 31+28+31+30+31+30+31+31+30+31+30 = 334 break; } if (isLeapYear(year)) { day++; } return day; } uInt Time::howManyDaysInMonth(uInt month, uInt year) { // Return how many days are in a month // Note: for february, always return 28 DebugAssert(month <= 12, AipsError); if(month==1 || month==3 || month==5 || month==7 || month==8 || month==10 || month==12) { return 31; } else if (month==4 || month==6 || month==9 || month==11) { return 30; } if (isLeapYear(year)) { return 29; } else { return 28; } } uInt Time::howManyDaysInMonth() { // Return how many days are in a months Time time; return howManyDaysInMonth (time.month(), time.year()); } Bool Time::isLeapYear() { return isLeapYear (Time().year()); } Bool Time::isLeapYear(uInt lyear) { if( lyear%100 == 0) { return lyear%400 == 0; } return lyear%4 == 0; } // Used internally here to determine if Daylight Savings Time (Summer // Time) is currently active. 1 is True, 0 False. static Int isDST () { time_t tim = time (NULL); struct tm *tm_info = localtime (&tim); return tm_info->tm_isdst; } // Returns the difference, in seconds, between UTC and local time. // Negative values are west of GMT, positive are east. #if defined(AIPS_SOLARIS) || defined(AIPS_IRIX) Int Time::timeZoneSeconds () { return isDST () ? -altzone : -timezone; } #elif defined(AIPS_OSF) || defined(AIPS_DARWIN) || defined(AIPS_BSD) Int Time::timeZoneSeconds () { time_t tim = time (NULL); struct tm *tm_info = localtime (&tim); return tm_info->tm_gmtoff; } #else Int Time::timeZoneSeconds () { // This will not be accurate unless the DST correction is +1 hour. // HP/UX and AIX do not have an altzone variable--at least not that I // can find--and this is also generic enough that it should work for // most other "reasonable" UNIX-like OS's. Note: Linux *had* an // altzone varialbe before the release of libc6 (glibc), but it's gone // now. Int dst = isDST (); return Int (-timezone + (C::hour * dst)); } #endif Double Time::timeZoneDays () { // Same as timeZoneSeconds(), but returns fractional days rather than // seconds. return (double)timeZoneSeconds () / C::day; // Turn seconds into days. } String Time::timeZoneName () { // Returns a string, e.g. "EST" or "MDT", describing the current local // time zone. return isDST () ? tzname[1] : tzname[0]; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/OS/Time.h000066400000000000000000000314371476623553700156330ustar00rootroot00000000000000//# Time.h: enquiry functions for calendar and clock time, with some operations //# Copyright (C) 1994,1995,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_TIME_H #define CASA_TIME_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // date and time enquiry functions, with some operations. // // // // // // This class might be better named a Date object, especially given that // more accurate Time classes are going to be required. // // //
        58. you should understand the difference between "Julian" and // "modified Julian" date // // // This class provides convenient date objects for the programmer. // Once constructed, they may be compared, read and written, and // queried for a wide variety of re-expressions. In a typical (?) use // you might create a Time object, and then query it to find out // the current month, day of the week, and whether it is a leap // year. You can also find out the number of seconds which have elapsed // since a specific Time. // // This class should not be used for very high precision // work. The time from epoch (1970.0) in seconds is // interconverted between computer "double" values, and // some loss of accuracy might result. // // // // // Time startTime; // Time moonLanding (1969,7,14); // cout << "date and time of moon landing: " << moonLanding << endl; // cout << "day of week: " << moonLanding.dayOfWeek () << endl; // cout << "day of year: " << moonLanding.dayOfYear () << endl; // cout << "seconds since moon landing: " << moonLanding.age () << endl; // cout << "weeks since moon landing: " << // moonLanding.age () / (60 * 60 * 24 * 7) << endl; // cout << "seconds elapsed since start: " << startTime.age () << endl; // // // // //
        59. member function 'age' might be renamed 'elapsedTime' //
        60. A reference to the source of each algorithm should be provided. // class Time { public: // the default constructor returns an object with the present date and time Time (); // Construct time with Julian day number Time (double jdn); // Construct Time with Gregorian calendar //
            //
          • seconds after the minute [0,59.999] (include milliseconds) //
          • minutes after the hour [0,59] //
          • hours after midnight [0,23] //
          • day of the month [1,31] //
          • month of the year [1,12] //
          • year. Beware, because '94' refers to the early Christian era, not // the 20th century. //
          Time (uInt year, uInt month, uInt day, uInt hour=0, uInt min=0, double sec=0.0); // Copy constructor Time (const Time& time); // return the Julian day (unit day) double julianDay () const; // return the modified Julian day (unit day) double modifiedJulianDay () const; // initialise the julian day data with Time class Time& operator = (const Time& time); double operator - (const Time& begin); Time operator + (const double plus); Bool operator == (const Time& other) const; Bool operator != (const Time& other) const; Bool operator > (const Time& other) const; Bool operator < (const Time& other) const; // if iso is True, then use ISO 8601 format // otherwise, produce the string of the form // Tue Mar 22 16:40:24 1994 // with GMT time String toString(const Bool iso=False) const; // returns a String in ISO 8601 format YYYY-MM-DDTHH:MM:SS in GMT // note: for dates beyond year 9999, use more digits for year const String ISODate() const { return toString(True); } // write the current time, GMT, in format // Tue Mar 22 16:40:24 1994 friend ostream& operator<<(ostream& out, const Time& other) { out << other.toString(False); return out; } // read in date, which must be in the following format // month/day/year,hour:min:sec // where month,day,year,hour,min and sec are uInt. friend istream& operator >> (istream&, Time&); // reset date to the present instant void now (); void setDate (uInt year, uInt month, uInt day, uInt hour=0, uInt min=0, double sec=0.0); // number of seconds which have elapsed since Time object was created // or reset double age (); // Return the seconds, minutes or hour part of the time. // uInt seconds (); double dseconds (); uInt minutes (); uInt hours (); // uInt dayOfMonth (); uInt month (); uInt year (); uInt dayOfWeek (); uInt dayOfYear (); static uInt howManyDaysInMonth (); static uInt howManyDaysInMonth (uInt month,uInt year); static Bool isLeapYear (); static Bool isLeapYear (uInt year); // Returns the difference, in seconds, between UTC and local time. // Negative values are west of GMT, positive are east. static Int timeZoneSeconds (); // Same as timeZoneSeconds(), but returns fractional days rather // than seconds. static Double timeZoneDays (); // Returns a string, e.g. "EST" or "MDT", describing the current // local time zone. static String timeZoneName (); protected: // Modified Julian day number // 40587 modified Julian day number = 00:00:00 January 1, 1970, GMT. uInt mJulianDay; // the fraction of the day double mJulianDayfrac; }; } //# NAMESPACE CASACORE - END #endif //# roel's original comments -- these may be useful in creating demo //# programs when we get some time... //# The function now () updated datas with the present time. // //# When create a object. The mJulianDay and mJulianDayfrac datas are //# initialise with actual modified Julian day. ( //# //# Time t; // The default constructor is at present time (now()) //# //# t.now(); //# //# //# //# When execute the setDate() function the actual mJulianDay and //# mJulianDayFrac datas are replace for the new date //# //# The function is invoked looks as follows //# //# //# //# Time t; // The default constructor is at present time (now()) //# //# t.setDate(1915,2,21); //# //# //# //# The function age() return the time in seconds between //# some Time object and now. //# //# The function is invoked looks as follows //# //# //# //# Time t; // The default constructor is at present time (now()) //# //# cout<<"time since x "<< t.age() <<"\n"; //# //# //# //# The function julianDay() return the julian day number. //# //# The function is invoked looks as follows //# //# //# //# Time t; //# //# cout<<"Julian day number "< //# //# The function modifiedJulianDay() return the modified julian day number. //# //# The function is invoked looks as follows //# //# //# //# Time t; //# //# cout<<"Modified Julian day number "< //# //# The function dayOfMonth() return day of the month [1,31] //# Note: This function doesn't modified the actual datas (mJulianDay and ' //# mJulianDayFrac). //# //# The function is invoked looks as follows //# //# //# //# Time t; //# //# //# cout<<"day of month "<< t.dayOfMonth() <<"\n"; //# //# //# //# The function month() return month of the year [1,12] //# Note: This function doesn't modified the actual datas (mJulianDay and ' //# mJulianDayFrac). //# //# The function is invoked looks as follows //# //# //# //# Time t; //# //# //# cout<<"month "<< t.month() <<"\n"; //# //# //# //# The function year() return the year. //# Note: This function doesn't modified the actual datas (mJulianDay and ' //# mJulianDayFrac). //# //# The function is invoked looks as follows //# //# //# //# Time t; //# //# //# cout<<"Year "<< t.year() <<"\n"; //# //# //# //# The function dayOfWeek() return days since sunday [1,7]. //# Note: This function doesn't modified the actual datas (mJulianDay and ' //# mJulianDayFrac). //# //# The function is invoked looks as follows //# //# //# //# Time t; //# //# //# cout<<"day of week "<< t.dayOfWeek() <<"\n"; //# //# //# //# The function dayOfYear() return day of the year [1,366] //# Note: This function doesn't modified the actual datas (mJulianDay and ' //# mJulianDayFrac). //# //# The function is invoked looks as follows //# //# //# //# Time t; //# //# //# cout<<"day of year "<< t.dayOfYear() <<"\n"; //# //# //# //# The function leapSeconds() return leap seconds. //# We have the next datas //# //# -Note: leapSeconds() removed 1997.10.07 by Jeff Uphoff, after //# -recommendation by Wim Brouw. //# //# leapsec=10; //# //# if(modified Julian Day>=41499) leapsec++; // 1 July 1972 //# if(modified Julian Day>=41683) leapsec++; // 1 January 1973 //# if(modified Julian Day>=42048) leapsec++; // 1 January 1974 //# if(modified Julian Day>=42413) leapsec++; // 1 January 1975 //# if(modified Julian Day>=42778) leapsec++; // 1 January 1976 //# if(modified Julian Day>=43144) leapsec++; // 1 January 1977 //# if(modified Julian Day>=43509) leapsec++; // 1 January 1978 //# if(modified Julian Day>=43874) leapsec++; // 1 January 1979 //# if(modified Julian Day>=44239) leapsec++; // 1 January 1980 //# if(modified Julian Day>=44786) leapsec++; // 1 July 1981 //# if(modified Julian Day>=45151) leapsec++; // 1 July 1982 //# if(modified Julian Day>=45516) leapsec++; // 1 July 1983 //# if(modified Julian Day>=46247) leapsec++; // 1 July 1985 //# if(modified Julian Day>=47161) leapsec++; // 1 January 1988 //# if(modified Julian Day>=47892) leapsec++; // 1 January 1990 //# if(modified Julian Day>=48257) leapsec++; // 1 January 1991 //# if(modified Julian Day>=48804) leapsec++; // 1 July 1992 //# if(modified Julian Day>=49169) leapsec++; // 1 July 1993 //# //# The function is invoked looks as follows //# //# //# //# Time t; //# //# cout<<"Leap seconds "<< t.leapSeconds() <<"\n"; //# //# //# //# The function howManyDaysInMonth() return how many days are in a month. //# //# The function is invoked looks as follows //# //# //# //# Time t; //# uInt month=1,month2=2,year=1992; //# //# cout<<"how many days are in this month "<< howManyDaysInMonth() <<"\n" //# cout<<"how many days are in January "<< howManyDaysInMonth(month) <<"\n"; //# cout<<"how many days are in february of 1992 "<< //# howManyDaysInMonth(month,year) <<"\n"; // 1992 is a leap year //# //# //# //# The function isLeapYear() return bool value. True if is a leap year //# and False in other case. //# //# The function is invoked looks as follows //# //# //# //# Time t; //# //# uInt year=1992; //# //# if(isLeapYear(year)) //# cout<<"Is a leap year"; //# //# if(isLeapYear()) //# cout<<"This year is a leap year"; //# //# casacore-3.7.1/casa/OS/Timer.cc000066400000000000000000000261771476623553700161600ustar00rootroot00000000000000//# Timer.cc: Timing facility //# Copyright (C) 1993,1994,1995,1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // User time: // time cpu spends in user mode on behalf of the program. // System time: // time cpu spends in system mode on behalf of the program. // Real time: // what you get from a stop watch timer. #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN void Timer::mark() { #if defined (DOS) || defined (MSDOS) usage0 = clock(); ftime(&real0); #elif defined (AIPS_SOLARIS) || defined(AIPS_IRIX) || defined(AIPS_OSF) || defined(__hpux__) || defined(AIPS_LINUX) || defined(AIPS_DARWIN) || defined(AIPS_BSD) || defined(__GLIBC__) # ifdef AIPS_CRAY_PGI struct timezone tz; getrusage(0, &usage0); timerclear(&real0); gettimeofday(&real0, &tz); # else real0 = times (&usage0); # endif #else getrusage(0, &usage0); ftime(&real0); #endif } double Timer::real() const { #if defined (DOS) || defined (MSDOS) double s, ms; timeb real1; // current elapsed real time int err; long ls; err = ftime(&real1); ls = real1.time - real0.time; s = ls; ls = real1.millitm - real0.millitm; ms = ls; ms = ms * 0.001 + s; return (ms); #elif defined (AIPS_SOLARIS) || defined(AIPS_IRIX) || defined(AIPS_OSF) || defined(__hpux__) || defined(AIPS_LINUX) || defined(AIPS_DARWIN) || defined(AIPS_BSD) || defined(__GLIBC__) # ifdef AIPS_CRAY_PGI struct timeval now; struct timezone tz; timerclear(&now); gettimeofday(&now, &tz); return ((double) (now.tv_sec - real0.tv_sec)); # else clock_t real1; // current time tms usage1; // current tms structure real1 = times (&usage1); return ((double) (real1 - real0)) / ((double) sysconf(_SC_CLK_TCK)); # endif #else double s, ms; timeb real1; // current elapsed real time int err; long ls; err = ftime(&real1); ls = real1.time - real0.time; s = ls; ls = real1.millitm - real0.millitm; ms = ls; ms = ms * 0.001 + s; return (ms); #endif } double Timer::user() const { #if defined (DOS) || defined (MSDOS) register clock_t usage1; if ((usage1 = clock()) != (clock_t) -1) { return (usage1 - usage0); } // error: Processor time not available return (0.0); #elif defined (AIPS_SOLARIS) || defined(AIPS_IRIX) || defined(AIPS_OSF) || defined(__hpux__) || defined(AIPS_LINUX) || defined(AIPS_DARWIN) || defined(AIPS_BSD) || defined(__GLIBC__) # ifdef AIPS_CRAY_PGI double dsec, dusec; rusage usage1; // current rusage structure getrusage(0, &usage1); dsec = (double)usage1.ru_utime.tv_sec - (double)usage0.ru_utime.tv_sec; dusec = (double)usage1.ru_utime.tv_usec - (double)usage0.ru_utime.tv_usec; return(dsec + dusec * 0.000001); # else tms usage1; // current tms structure times (&usage1); return ((double) (usage1.tms_utime - usage0.tms_utime)) / ((double) sysconf(_SC_CLK_TCK)); # endif #else double dsec, dusec; rusage usage1; // current rusage structure getrusage(0, &usage1); dsec = (double)usage1.ru_utime.tv_sec - (double)usage0.ru_utime.tv_sec; dusec = (double)usage1.ru_utime.tv_usec - (double)usage0.ru_utime.tv_usec; return(dsec + dusec * 0.000001); #endif } double Timer::system() const { #if defined (DOS) || defined (MSDOS) return(0L); #elif defined (AIPS_SOLARIS) || defined(AIPS_IRIX) || defined(AIPS_OSF) || defined(__hpux__) || defined(AIPS_LINUX) || defined(AIPS_DARWIN) || defined(AIPS_BSD) || defined(__GLIBC__) # ifdef AIPS_CRAY_PGI double dsec, dusec; rusage usage1; // current rusage structure getrusage(0, &usage1); dsec = usage1.ru_stime.tv_sec - usage0.ru_stime.tv_sec; dusec = usage1.ru_stime.tv_usec - usage0.ru_stime.tv_usec; return(dsec + dusec*0.000001); # else tms usage1; // current tms structure times (&usage1); return ((double) (usage1.tms_stime - usage0.tms_stime)) / ((double) sysconf(_SC_CLK_TCK)); # endif #else register double dsec, dusec; rusage usage1; // current rusage structure getrusage(0, &usage1); dsec = usage1.ru_stime.tv_sec - usage0.ru_stime.tv_sec; dusec = usage1.ru_stime.tv_usec - usage0.ru_stime.tv_usec; return(dsec + dusec*0.000001); #endif } double Timer::all() const { #if defined (DOS) || defined (MSDOS) register clock_t usage1; if ((usage1 = clock()) != (clock_t) -1) { return (usage1 - usage0); } // error: Processor time not available return (0.0); #elif defined (AIPS_SOLARIS) || defined(AIPS_IRIX) || defined(AIPS_OSF) || defined(__hpux__) || defined(AIPS_LINUX) || defined(AIPS_DARWIN) || defined(AIPS_BSD) || defined(__GLIBC__) # ifdef AIPS_CRAY_PGI double dsec, dusec; rusage usage1; // current rusage structure getrusage(0, &usage1); dsec = (usage1.ru_utime.tv_sec - usage0.ru_utime.tv_sec) + (usage1.ru_stime.tv_sec - usage0.ru_stime.tv_sec); dusec = (usage1.ru_utime.tv_usec - usage0.ru_utime.tv_usec) + (usage1.ru_stime.tv_usec - usage0.ru_stime.tv_usec); return(dsec + dusec*0.000001); # else tms usage1; // current tms structure times (&usage1); return ((double) ( (usage1.tms_utime - usage0.tms_utime) + (usage1.tms_stime - usage0.tms_stime) )) / ((double) sysconf(_SC_CLK_TCK)); # endif #else register double dsec, dusec; rusage usage1; // current rusage structure getrusage(0, &usage1); dsec = (usage1.ru_utime.tv_sec - usage0.ru_utime.tv_sec) + (usage1.ru_stime.tv_sec - usage0.ru_stime.tv_sec); dusec = (usage1.ru_utime.tv_usec - usage0.ru_utime.tv_usec) + (usage1.ru_stime.tv_usec - usage0.ru_stime.tv_usec); return(dsec + dusec*0.000001); #endif } double Timer::user_usec() const { #if defined (DOS) || defined (MSDOS) register clock_t usage1; if ((usage1 = clock()) != (clock_t) -1) { return (usage1 - usage0); } // error: Processor time not available return (0.0); #elif defined (AIPS_SOLARIS) || defined(AIPS_IRIX) || defined(AIPS_OSF) || defined(__hpux__) || defined(AIPS_LINUX) || defined(AIPS_DARWIN) || defined(AIPS_BSD) || defined(__GLIBC__) # ifdef AIPS_CRAY_PGI double dsec, dusec; rusage usage1; // current rusage structure getrusage(0, &usage1); dsec = usage1.ru_utime.tv_sec - usage0.ru_utime.tv_sec; dusec = usage1.ru_utime.tv_usec - usage0.ru_utime.tv_usec; return(dsec*1000000.0 + dusec); # else tms usage1; // current tms structure times (&usage1); return 1000000.0 * ((double) (usage1.tms_utime - usage0.tms_utime)) / ((double) sysconf(_SC_CLK_TCK)); # endif #else register double dsec, dusec; rusage usage1; // current rusage structure getrusage(0, &usage1); dsec = usage1.ru_utime.tv_sec - usage0.ru_utime.tv_sec; dusec = usage1.ru_utime.tv_usec - usage0.ru_utime.tv_usec; return(dsec*1000000.0 + dusec); #endif } double Timer::system_usec() const { #if defined (DOS) || defined (MSDOS) return(0.0); #elif defined (AIPS_SOLARIS) || defined(AIPS_IRIX) || defined(AIPS_OSF) || defined(__hpux__) || defined(AIPS_LINUX) || defined(AIPS_DARWIN) || defined(AIPS_BSD) || defined(__GLIBC__) # ifdef AIPS_CRAY_PGI double dsec, dusec; rusage usage1; // current rusage structure getrusage(0, &usage1); dsec = usage1.ru_stime.tv_sec - usage0.ru_stime.tv_sec; dusec = usage1.ru_stime.tv_usec - usage0.ru_stime.tv_usec; return(dsec*1000000.0 + dusec); # else tms usage1; // current tms structure times (&usage1); return 1000000.0 * ((double) (usage1.tms_stime - usage0.tms_stime)) / ((double) sysconf(_SC_CLK_TCK)); # endif #else register double dsec, dusec; rusage usage1; // current rusage structure getrusage(0, &usage1); dsec = usage1.ru_stime.tv_sec - usage0.ru_stime.tv_sec; dusec = usage1.ru_stime.tv_usec - usage0.ru_stime.tv_usec; return(dsec*1000000.0 + dusec); #endif } double Timer::all_usec() const { #if defined (DOS) || defined (MSDOS) register clock_t usage1; if ((usage1 = clock()) != (clock_t) -1) { return (usage1 - usage0); } // error: Processor time not available return (0.0); #elif defined (AIPS_SOLARIS) || defined(AIPS_IRIX) || defined(AIPS_OSF) || defined(__hpux__) || defined(AIPS_LINUX) || defined(AIPS_DARWIN) || defined(AIPS_BSD) || defined(__GLIBC__) # ifdef AIPS_CRAY_PGI double dsec, dusec; rusage usage1; // current rusage structure getrusage(0, &usage1); dsec = (usage1.ru_utime.tv_sec - usage0.ru_utime.tv_sec) + (usage1.ru_stime.tv_sec - usage0.ru_stime.tv_sec); dusec = (usage1.ru_utime.tv_usec - usage0.ru_utime.tv_usec) + (usage1.ru_stime.tv_usec - usage0.ru_stime.tv_usec); return(dsec*1000000.0 + dusec); # else tms usage1; // current tms structure times (&usage1); return 1000000.0 * ((double) ( (usage1.tms_utime - usage0.tms_utime) + (usage1.tms_stime - usage0.tms_stime) )) / ((double) sysconf(_SC_CLK_TCK)); # endif #else register double dsec, dusec; rusage usage1; // current rusage structure getrusage(0, &usage1); dsec = (usage1.ru_utime.tv_sec - usage0.ru_utime.tv_sec) + (usage1.ru_stime.tv_sec - usage0.ru_stime.tv_sec); dusec = (usage1.ru_utime.tv_usec - usage0.ru_utime.tv_usec) + (usage1.ru_stime.tv_usec - usage0.ru_stime.tv_usec); return(dsec*1000000.0 + dusec); #endif } void Timer::show() const { show(cout); } void Timer::show (const String& s) const { show(cout, s); } void Timer::show(ostream &os) const { os << setw(11) << real() << " real " << setw(11) << user() << " user " << setw(11) << system() << " system" << endl; } void Timer::show (ostream &os, const String& s) const { os << s; show(os); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/OS/Timer.h000066400000000000000000000145371476623553700160170ustar00rootroot00000000000000//# Timer.h: measure the time it takes to execute parts of a program //# Copyright (C) 1993,1994,1995,1996,1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_TIMER_H #define CASA_TIMER_H #include #include //# Forward declarations #include #if defined(DOS) || defined(MSDOS) #include extern "C" { #include } #elif defined(AIPS_SOLARIS) || defined(AIPS_IRIX) || defined(AIPS_OSF) || defined(__hpux__) || defined(AIPS_LINUX) || defined(AIPS_DARWIN) || defined(AIPS_BSD) || defined(__GLIBC__) #if defined(AIPS_CRAY_PGI) #include #include #include extern "C" int getrusage(int, struct rusage*); #else #include #include #endif #else #include #include extern "C" int getrusage(int, struct rusage*); extern "C" int ftime(struct timeb*); #include #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN // Class declaration. class String; // measure the time it takes to execute parts of a program // // // // // The Timer class provides an interface to system timing. It // allows a C++ program to record the time between a reference // point (mark) and now. This class uses the system time(2) // interface to provide time resolution at either millisecond or // microsecond granularity, depending upon operating system // support and features. Since the time duration is stored in // a 32-bit word, the maximum time period before rollover // occurs is about 71 minutes. // // Due to operating system dependencies, the accuracy of all // member function results may not be as documented. For example // some operating systems do not support timers with // microsecond resolution. In those cases, the values returned // are provided to the nearest millisecond or other unit of // time as appropriate. See the Timer header file for system- // specific notes. // // This Timer class is based on the TI COOL library // Timer class // // // // Here's how to create a timer, start it (the 'mark' member function) // and display a breakdown. Recall that // realtime = user time + system time // // // // Timer timer; // the mark is set at construction time // timer.mark(); // if you want to restart the clock // ...do some calculation... // cout << "user: " << timer.user () << endl; // cout << "system: " << timer.system () << endl; // cout << "real: " << timer.real () << endl; // // // // //
        61. it might be useful to have a stop () member function: for // really precise timing, the successive calls to user, system // and real all happen at measurably different times //
        62. provide an enquiry function that reports the resolution of // the timer //
        63. add 'start' member function, a synonym for 'mark' but more // comprehensible // // class Timer { public: // // Construct a timer and set the mark ("mark()"). // Timer() {mark();} // // Set the timer mark -- i.e., start the clock ticking // void mark(); // // Get the user time (in seconds) since last "mark()". // double user() const; // // Get the system time (in seconds) since last "mark()". // double system() const; // // Get the user+system time (in seconds) since last "mark()". // double all() const; // // Get the real time (in seconds) since last "mark()". // double real() const; // Show real, user, system time (in seconds) on cout or a user supplied // stream. // void show() const; void show(ostream &os) const; // // Show real, user, system time (in seconds) on cout or a user supplied // stream preceeded by the string parameter. // void show(const String&) const; void show(ostream &os, const String&prefix) const; // // // Get the user time (in microseconds) since last "mark()". // double user_usec() const; // // Get the system time (in microseconds) since last "mark()". // double system_usec() const; // // Get the user+system time (in microseconds) since last "mark()". // double all_usec() const; private: #if defined(DOS) || defined(MSDOS) clock_t usage0; timeb real0; //# elapsed real time at last mark #elif defined(AIPS_SOLARIS) || defined(AIPS_IRIX) || defined(AIPS_OSF) || defined(__hpux__) || defined(AIPS_LINUX) || defined(AIPS_DARWIN) || defined(AIPS_BSD) || defined(__GLIBC__) #if defined(AIPS_CRAY_PGI) //struct timeval usage0; rusage usage0; //# rusage structure at last mark struct timeval real0; //# elapsed real time at last mark #else tms usage0; //# tms structure at last mark clock_t real0; //# elapsed real time at last mark #endif #else rusage usage0; //# rusage structure at last mark timeb real0; //# elapsed real time at last mark #endif }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/OS/VAXConversion.cc000066400000000000000000000110101476623553700175600ustar00rootroot00000000000000//# VAXConversion.cc: A class with static functions to convert VAX format //# Copyright (C) 1996,1997 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // VAX has almost identical format to IEEE (but no NaN) // which is SEEEEEEE EFFFFFFF ... // The VAX exponent is 2 higher. // The VAX byte order is b1b0b3b2 (Big-endian IEEE has b0b1b2b3). void VAXConversion::toLocal (float* to, const void* from, size_t nr) { assert (sizeof(unsigned int) == 4); assert (sizeof(float) == 4); const char* data = (const char*)from; float* last = to + nr; while (to < last) { unsigned int value; moveFloat (&value, data); data += 4; unsigned int exponent = (value & 0x7f800000) >> 23; if (exponent <= 2) { *(unsigned int*)to = 0; }else{ exponent = (exponent-2) << 23; *(unsigned int*)to = (value & 0x807fffff) | exponent; } to++; } } // VAX D-float has format SEEEEEEE EFFFFFFF ... // IEEE double has format SEEEEEEE EEEEFFFF ... // The VAX exponent is 2 higher. // The VAX byte order is b1b0b3b2b5b4b7b6. void VAXConversion::toLocal (double* to, const void* from, size_t nr) { assert (sizeof(unsigned int) == 4); assert (sizeof(double) == 8); const char* data = (const char*)from; double* last = to + nr; unsigned int value, rest; while (to < last) { moveFloat (&value, data); moveFloat (&rest, data+4); data += 8; unsigned int exponent = (value & 0x7f800000) >> 23; if (exponent == 0) { value = 0; rest = 0; }else{ exponent = (exponent + (1022-128)) << 20; rest = (value << 29) | (rest >> 3); value = (value & 0x80000000) | exponent | ((value >> 3) & 0x000fffff); } #if defined(AIPS_LITTLE_ENDIAN) ((unsigned int*)to)[0] = rest; ((unsigned int*)to)[1] = value; #else ((unsigned int*)to)[0] = value; ((unsigned int*)to)[1] = rest; #endif to++; } } void VAXConversion::fromLocal (void* to, const float* from, size_t nr) { assert (sizeof(unsigned int) == 4); assert (sizeof(float) == 4); char* data = (char*)to; const float* last = from + nr; while (from < last) { unsigned int value; value = *(unsigned int*)from; unsigned int exponent = (value & 0x7f800000) >> 23; if (exponent == 0) { value = 0; }else{ exponent += 2; if (exponent > 255) { value |= 0x7fffffff; }else{ value = (value & 0x807fffff) | (exponent << 23); } } moveFloat (data, &value); data += 4; from++; } } void VAXConversion::fromLocal (void* to, const double* from, size_t nr) { assert (sizeof(unsigned int) == 4); assert (sizeof(double) == 8); char* data = (char*)to; const double* last = from + nr; while (from < last) { unsigned int value, rest; #if defined(AIPS_LITTLE_ENDIAN) rest = ((unsigned int*)from)[0]; value = ((unsigned int*)from)[1]; #else value = ((unsigned int*)from)[0]; rest = ((unsigned int*)from)[1]; #endif unsigned int exponent = (value & 0x7ff00000) >> 20; exponent -= 1022-128; if (exponent <= 0) { exponent = 0; value = 0; rest = 0; }else{ if (exponent > 255) { value |= 0x7fffffff; rest = 0xffffffff; }else{ value = (value & 0x80000000) | (exponent << 23) | ((value << 3) & 0x007fffff) | (rest >> 29); rest <<= 3; } } moveFloat (data, &value); moveFloat (data+4, &rest); data += 8; from++; } } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/OS/VAXConversion.h000066400000000000000000000264671476623553700174500ustar00rootroot00000000000000//# VAXConversion.h: A class with static functions to convert VAX format //# Copyright (C) 1996,1997,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_VAXCONVERSION_H #define CASA_VAXCONVERSION_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Define the canonical sizes of the built-in data types. // These are the same for all machine architectures. #define SIZE_VAX_CHAR 1 #define SIZE_VAX_UCHAR 1 #define SIZE_VAX_SHORT 2 #define SIZE_VAX_USHORT 2 #define SIZE_VAX_INT 4 #define SIZE_VAX_UINT 4 #define SIZE_VAX_INT64 4 #define SIZE_VAX_UINT64 4 #define SIZE_VAX_FLOAT 4 #define SIZE_VAX_DOUBLE 8 // // A class with static functions to convert VAX format // // // // // // This class contains static toLocal functions to convert data from VAX // format to local format and vice-versa. It only handles VAX D-float format. // Another class should be implemented to handle VAX G-float format. //

          // The functions work well on big-endian as well as little-endian machines. // // // Archived WSRT data can be stored in the old VAX format // (little-endian and VAX D-float floating point format). // Conversion functions are needed to read these data. // // //

        64. Support data type long double. // class VAXConversion { public: // Convert one value from VAX format to local format. // The from and to buffer should not overlap. // static void toLocal (char& to, const void* from); static void toLocal (unsigned char& to, const void* from); static void toLocal (short& to, const void* from); static void toLocal (unsigned short& to, const void* from); static void toLocal (int& to, const void* from); static void toLocal (unsigned int& to, const void* from); static void toLocal (Int64& to, const void* from); static void toLocal (uInt64& to, const void* from); static void toLocal (float& to, const void* from); static void toLocal (double& to, const void* from); // // Convert nr values from VAX format to local format. // The from and to buffer should not overlap. // static void toLocal (char* to, const void* from, size_t nr); static void toLocal (unsigned char* to, const void* from, size_t nr); static void toLocal (short* to, const void* from, size_t nr); static void toLocal (unsigned short* to, const void* from, size_t nr); static void toLocal (int* to, const void* from, size_t nr); static void toLocal (unsigned int* to, const void* from, size_t nr); static void toLocal (Int64* to, const void* from, size_t nr); static void toLocal (uInt64* to, const void* from, size_t nr); static void toLocal (float* to, const void* from, size_t nr); static void toLocal (double* to, const void* from, size_t nr); // // Convert one value from local format to VAX format. // The from and to buffer should not overlap. // static void fromLocal (void* to, char from); static void fromLocal (void* to, unsigned char from); static void fromLocal (void* to, short from); static void fromLocal (void* to, unsigned short from); static void fromLocal (void* to, int from); static void fromLocal (void* to, unsigned int from); static void fromLocal (void* to, Int64 from); static void fromLocal (void* to, uInt64 from); static void fromLocal (void* to, float from); static void fromLocal (void* to, double from); // // Convert nr values from local format to VAX format. // The from and to buffer should not overlap. // static void fromLocal (void* to, const char* from, size_t nr); static void fromLocal (void* to, const unsigned char* from, size_t nr); static void fromLocal (void* to, const short* from, size_t nr); static void fromLocal (void* to, const unsigned short* from, size_t nr); static void fromLocal (void* to, const int* from, size_t nr); static void fromLocal (void* to, const unsigned int* from, size_t nr); static void fromLocal (void* to, const Int64* from, size_t nr); static void fromLocal (void* to, const uInt64* from, size_t nr); static void fromLocal (void* to, const float* from, size_t nr); static void fromLocal (void* to, const double* from, size_t nr); // // Move a float value (by swapping bytes correctly). static void moveFloat (void* to, const void* from); private: // This class should not be constructed // (so declare the constructor private). VAXConversion(); }; inline void VAXConversion::toLocal (char& to, const void* from) { LittleEndianConversion::toLocal (to, from); } inline void VAXConversion::toLocal (unsigned char& to, const void* from) { LittleEndianConversion::toLocal (to, from); } inline void VAXConversion::toLocal (short& to, const void* from) { LittleEndianConversion::toLocal (to, from); } inline void VAXConversion::toLocal (unsigned short& to, const void* from) { LittleEndianConversion::toLocal (to, from); } inline void VAXConversion::toLocal (int& to, const void* from) { LittleEndianConversion::toLocal (to, from); } inline void VAXConversion::toLocal (unsigned int& to, const void* from) { LittleEndianConversion::toLocal (to, from); } inline void VAXConversion::toLocal (Int64& to, const void* from) { LittleEndianConversion::toLocal (to, from); } inline void VAXConversion::toLocal (uInt64& to, const void* from) { LittleEndianConversion::toLocal (to, from); } inline void VAXConversion::toLocal (float& to, const void* from) { toLocal (&to, from, 1); } inline void VAXConversion::toLocal (double& to, const void* from) { toLocal (&to, from, 1); } inline void VAXConversion::toLocal (char* to, const void* from, size_t nr) { LittleEndianConversion::toLocal (to, from, nr); } inline void VAXConversion::toLocal (unsigned char* to, const void* from, size_t nr) { LittleEndianConversion::toLocal (to, from, nr); } inline void VAXConversion::toLocal (short* to, const void* from, size_t nr) { LittleEndianConversion::toLocal (to, from, nr); } inline void VAXConversion::toLocal (unsigned short* to, const void* from, size_t nr) { LittleEndianConversion::toLocal (to, from, nr); } inline void VAXConversion::toLocal (int* to, const void* from, size_t nr) { LittleEndianConversion::toLocal (to, from, nr); } inline void VAXConversion::toLocal (unsigned int* to, const void* from, size_t nr) { LittleEndianConversion::toLocal (to, from, nr); } inline void VAXConversion::toLocal (Int64* to, const void* from, size_t nr) { LittleEndianConversion::toLocal (to, from, nr); } inline void VAXConversion::toLocal (uInt64* to, const void* from, size_t nr) { LittleEndianConversion::toLocal (to, from, nr); } inline void VAXConversion::fromLocal (void* to, char from) { LittleEndianConversion::fromLocal (to, from); } inline void VAXConversion::fromLocal (void* to, unsigned char from) { LittleEndianConversion::fromLocal (to, from); } inline void VAXConversion::fromLocal (void* to, short from) { LittleEndianConversion::fromLocal (to, from); } inline void VAXConversion::fromLocal (void* to, unsigned short from) { LittleEndianConversion::fromLocal (to, from); } inline void VAXConversion::fromLocal (void* to, int from) { LittleEndianConversion::fromLocal (to, from); } inline void VAXConversion::fromLocal (void* to, unsigned int from) { LittleEndianConversion::fromLocal (to, from); } inline void VAXConversion::fromLocal (void* to, Int64 from) { LittleEndianConversion::fromLocal (to, from); } inline void VAXConversion::fromLocal (void* to, uInt64 from) { LittleEndianConversion::fromLocal (to, from); } inline void VAXConversion::fromLocal (void* to, float from) { fromLocal (to, &from, 1); } inline void VAXConversion::fromLocal (void* to, double from) { fromLocal (to, &from, 1); } inline void VAXConversion::fromLocal (void* to, const char* from, size_t nr) { LittleEndianConversion::fromLocal (to, from, nr); } inline void VAXConversion::fromLocal (void* to, const unsigned char* from, size_t nr) { LittleEndianConversion::fromLocal (to, from, nr); } inline void VAXConversion::fromLocal (void* to, const short* from, size_t nr) { LittleEndianConversion::fromLocal (to, from, nr); } inline void VAXConversion::fromLocal (void* to, const unsigned short* from, size_t nr) { LittleEndianConversion::fromLocal (to, from, nr); } inline void VAXConversion::fromLocal (void* to, const int* from, size_t nr) { LittleEndianConversion::fromLocal (to, from, nr); } inline void VAXConversion::fromLocal (void* to, const unsigned int* from, size_t nr) { LittleEndianConversion::fromLocal (to, from, nr); } inline void VAXConversion::fromLocal (void* to, const Int64* from, size_t nr) { LittleEndianConversion::fromLocal (to, from, nr); } inline void VAXConversion::fromLocal (void* to, const uInt64* from, size_t nr) { LittleEndianConversion::fromLocal (to, from, nr); } inline void VAXConversion::moveFloat (void* to, const void* from) { #if defined(AIPS_LITTLE_ENDIAN) ((char*)to)[0] = ((const char*)from)[2]; ((char*)to)[1] = ((const char*)from)[3]; ((char*)to)[2] = ((const char*)from)[0]; ((char*)to)[3] = ((const char*)from)[1]; #else ((char*)to)[0] = ((const char*)from)[1]; ((char*)to)[1] = ((const char*)from)[0]; ((char*)to)[2] = ((const char*)from)[3]; ((char*)to)[3] = ((const char*)from)[2]; #endif } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/OS/VAXDataConversion.cc000066400000000000000000000261311476623553700203640ustar00rootroot00000000000000//# VAXDataConversion.cc: A class with virtual functions to convert VAX format //# Copyright (C) 1996,1997,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN VAXDataConversion::~VAXDataConversion() {} size_t VAXDataConversion::toLocal (char& to, const void* from) const { VAXConversion::toLocal (to, from); return SIZE_VAX_CHAR; } size_t VAXDataConversion::toLocal (unsigned char& to, const void* from) const { VAXConversion::toLocal (to, from); return SIZE_VAX_UCHAR; } size_t VAXDataConversion::toLocal (short& to, const void* from) const { VAXConversion::toLocal (to, from); return SIZE_VAX_SHORT; } size_t VAXDataConversion::toLocal (unsigned short& to, const void* from) const { VAXConversion::toLocal (to, from); return SIZE_VAX_USHORT; } size_t VAXDataConversion::toLocal (int& to, const void* from) const { VAXConversion::toLocal (to, from); return SIZE_VAX_INT; } size_t VAXDataConversion::toLocal (unsigned int& to, const void* from) const { VAXConversion::toLocal (to, from); return SIZE_VAX_UINT; } size_t VAXDataConversion::toLocal (Int64& to, const void* from) const { VAXConversion::toLocal (to, from); return SIZE_VAX_INT64; } size_t VAXDataConversion::toLocal (uInt64& to, const void* from) const { VAXConversion::toLocal (to, from); return SIZE_VAX_UINT64; } size_t VAXDataConversion::toLocal (float& to, const void* from) const { VAXConversion::toLocal (to, from); return SIZE_VAX_FLOAT; } size_t VAXDataConversion::toLocal (double& to, const void* from) const { VAXConversion::toLocal (to, from); return SIZE_VAX_DOUBLE; } size_t VAXDataConversion::toLocal (char* to, const void* from, size_t nr) const { VAXConversion::toLocal (to, from, nr); return nr*SIZE_VAX_CHAR; } size_t VAXDataConversion::toLocal (unsigned char* to, const void* from, size_t nr) const { VAXConversion::toLocal (to, from, nr); return nr*SIZE_VAX_UCHAR; } size_t VAXDataConversion::toLocal (short* to, const void* from, size_t nr) const { VAXConversion::toLocal (to, from, nr); return nr*SIZE_VAX_SHORT; } size_t VAXDataConversion::toLocal (unsigned short* to, const void* from, size_t nr) const { VAXConversion::toLocal (to, from, nr); return nr*SIZE_VAX_USHORT; } size_t VAXDataConversion::toLocal (int* to, const void* from, size_t nr) const { VAXConversion::toLocal (to, from, nr); return nr*SIZE_VAX_INT; } size_t VAXDataConversion::toLocal (unsigned int* to, const void* from, size_t nr) const { VAXConversion::toLocal (to, from, nr); return nr*SIZE_VAX_UINT; } size_t VAXDataConversion::toLocal (Int64* to, const void* from, size_t nr) const { VAXConversion::toLocal (to, from, nr); return nr*SIZE_VAX_INT64; } size_t VAXDataConversion::toLocal (uInt64* to, const void* from, size_t nr) const { VAXConversion::toLocal (to, from, nr); return nr*SIZE_VAX_UINT64; } size_t VAXDataConversion::toLocal (float* to, const void* from, size_t nr) const { VAXConversion::toLocal (to, from, nr); return nr*SIZE_VAX_FLOAT; } size_t VAXDataConversion::toLocal (double* to, const void* from, size_t nr) const { VAXConversion::toLocal (to, from, nr); return nr*SIZE_VAX_DOUBLE; } size_t VAXDataConversion::fromLocal (void* to, char from) const { VAXConversion::fromLocal (to, from); return SIZE_VAX_CHAR; } size_t VAXDataConversion::fromLocal (void* to, unsigned char from) const { VAXConversion::fromLocal (to, from); return SIZE_VAX_UCHAR; } size_t VAXDataConversion::fromLocal (void* to, short from) const { VAXConversion::fromLocal (to, from); return SIZE_VAX_SHORT; } size_t VAXDataConversion::fromLocal (void* to, unsigned short from) const { VAXConversion::fromLocal (to, from); return SIZE_VAX_USHORT; } size_t VAXDataConversion::fromLocal (void* to, int from) const { VAXConversion::fromLocal (to, from); return SIZE_VAX_INT; } size_t VAXDataConversion::fromLocal (void* to, unsigned int from) const { VAXConversion::fromLocal (to, from); return SIZE_VAX_UINT; } size_t VAXDataConversion::fromLocal (void* to, Int64 from) const { VAXConversion::fromLocal (to, from); return SIZE_VAX_INT64; } size_t VAXDataConversion::fromLocal (void* to, uInt64 from) const { VAXConversion::fromLocal (to, from); return SIZE_VAX_UINT64; } size_t VAXDataConversion::fromLocal (void* to, float from) const { VAXConversion::fromLocal (to, from); return SIZE_VAX_FLOAT; } size_t VAXDataConversion::fromLocal (void* to, double from) const { VAXConversion::fromLocal (to, from); return SIZE_VAX_DOUBLE; } size_t VAXDataConversion::fromLocal (void* to, const char* from, size_t nr) const { VAXConversion::fromLocal (to, from, nr); return nr*SIZE_VAX_CHAR; } size_t VAXDataConversion::fromLocal (void* to, const unsigned char* from, size_t nr) const { VAXConversion::fromLocal (to, from, nr); return nr*SIZE_VAX_UCHAR; } size_t VAXDataConversion::fromLocal (void* to, const short* from, size_t nr) const { VAXConversion::fromLocal (to, from, nr); return nr*SIZE_VAX_SHORT; } size_t VAXDataConversion::fromLocal (void* to, const unsigned short* from, size_t nr) const { VAXConversion::fromLocal (to, from, nr); return nr*SIZE_VAX_USHORT; } size_t VAXDataConversion::fromLocal (void* to, const int* from, size_t nr) const { VAXConversion::fromLocal (to, from, nr); return nr*SIZE_VAX_INT; } size_t VAXDataConversion::fromLocal (void* to, const unsigned int* from, size_t nr) const { VAXConversion::fromLocal (to, from, nr); return nr*SIZE_VAX_UINT; } size_t VAXDataConversion::fromLocal (void* to, const Int64* from, size_t nr) const { VAXConversion::fromLocal (to, from, nr); return nr*SIZE_VAX_INT64; } size_t VAXDataConversion::fromLocal (void* to, const uInt64* from, size_t nr) const { VAXConversion::fromLocal (to, from, nr); return nr*SIZE_VAX_UINT64; } size_t VAXDataConversion::fromLocal (void* to, const float* from, size_t nr) const { VAXConversion::fromLocal (to, from, nr); return nr*SIZE_VAX_FLOAT; } size_t VAXDataConversion::fromLocal (void* to, const double* from, size_t nr) const { VAXConversion::fromLocal (to, from, nr); return nr*SIZE_VAX_DOUBLE; } Bool VAXDataConversion::canCopy (const char*) const { if (sizeof(char) == SIZE_VAX_CHAR) { return True; } return False; } Bool VAXDataConversion::canCopy (const unsigned char*) const { if (sizeof(unsigned char) == SIZE_VAX_UCHAR) { return True; } return False; } Bool VAXDataConversion::canCopy (const short*) const { #if defined(AIPS_LITTLE_ENDIAN) if (sizeof(short) == SIZE_VAX_SHORT) { return True; } #endif return False; } Bool VAXDataConversion::canCopy (const unsigned short*) const { #if defined(AIPS_LITTLE_ENDIAN) if (sizeof(unsigned short) == SIZE_VAX_USHORT) { return True; } #endif return False; } Bool VAXDataConversion::canCopy (const int*) const { #if defined(AIPS_LITTLE_ENDIAN) if (sizeof(int) == SIZE_VAX_INT) { return True; } #endif return False; } Bool VAXDataConversion::canCopy (const unsigned int*) const { #if defined(AIPS_LITTLE_ENDIAN) if (sizeof(unsigned int) == SIZE_VAX_UINT) { return True; } #endif return False; } Bool VAXDataConversion::canCopy (const Int64*) const { #if defined(AIPS_LITTLE_ENDIAN) if (sizeof(Int64) == SIZE_VAX_INT64) { return True; } #endif return False; } Bool VAXDataConversion::canCopy (const uInt64*) const { #if defined(AIPS_LITTLE_ENDIAN) if (sizeof(uInt64) == SIZE_VAX_UINT64) { return True; } #endif return False; } Bool VAXDataConversion::canCopy (const float*) const { return False; } Bool VAXDataConversion::canCopy (const double*) const { return False; } unsigned int VAXDataConversion::externalSize (const char*) const { return SIZE_VAX_CHAR; } unsigned int VAXDataConversion::externalSize (const unsigned char*) const { return SIZE_VAX_UCHAR; } unsigned int VAXDataConversion::externalSize (const short*) const { return SIZE_VAX_SHORT; } unsigned int VAXDataConversion::externalSize (const unsigned short*) const { return SIZE_VAX_USHORT; } unsigned int VAXDataConversion::externalSize (const int*) const { return SIZE_VAX_INT; } unsigned int VAXDataConversion::externalSize (const unsigned int*) const { return SIZE_VAX_UINT; } unsigned int VAXDataConversion::externalSize (const Int64*) const { return SIZE_VAX_INT64; } unsigned int VAXDataConversion::externalSize (const uInt64*) const { return SIZE_VAX_UINT64; } unsigned int VAXDataConversion::externalSize (const float*) const { return SIZE_VAX_FLOAT; } unsigned int VAXDataConversion::externalSize (const double*) const { return SIZE_VAX_DOUBLE; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/OS/VAXDataConversion.h000066400000000000000000000176341476623553700202360ustar00rootroot00000000000000//# VAXDataConversion.h: A class with virtual functions to convert VAX format //# Copyright (C) 1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_VAXDATACONVERSION_H #define CASA_VAXDATACONVERSION_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A class with virtual functions to convert VAX format // // // // // // This class is a specialization of the abstract base class // DataConversion. // It contains functions to convert data from/to the old VAX format // using the static functions in class // VAXConversion. // // // See example in class DataConversion. // // // This class is an addition to VAXConversion // to be able to use the conversion functions in a polymorphic way. // // //
        65. Support data type long double. // class VAXDataConversion : public DataConversion { public: // Construct the object. VAXDataConversion(); ~VAXDataConversion() override; // Convert one value from VAX format to local format. // The from and to buffer should not overlap. // size_t toLocal (char& to, const void* from) const override; size_t toLocal (unsigned char& to, const void* from) const override; size_t toLocal (short& to, const void* from) const override; size_t toLocal (unsigned short& to, const void* from) const override; size_t toLocal (int& to, const void* from) const override; size_t toLocal (unsigned int& to, const void* from) const override; size_t toLocal (Int64& to, const void* from) const override; size_t toLocal (uInt64& to, const void* from) const override; size_t toLocal (float& to, const void* from) const override; size_t toLocal (double& to, const void* from) const override; // // Convert nr values from VAX format to local format. // The from and to buffer should not overlap. // size_t toLocal (char* to, const void* from, size_t nr) const override; size_t toLocal (unsigned char* to, const void* from, size_t nr) const override; size_t toLocal (short* to, const void* from, size_t nr) const override; size_t toLocal (unsigned short* to, const void* from, size_t nr) const override; size_t toLocal (int* to, const void* from, size_t nr) const override; size_t toLocal (unsigned int* to, const void* from, size_t nr) const override; size_t toLocal (Int64* to, const void* from, size_t nr) const override; size_t toLocal (uInt64* to, const void* from, size_t nr) const override; size_t toLocal (float* to, const void* from, size_t nr) const override; size_t toLocal (double* to, const void* from, size_t nr) const override; // // Convert one value from local format to VAX format. // The from and to buffer should not overlap. // size_t fromLocal (void* to, char from) const override; size_t fromLocal (void* to, unsigned char from) const override; size_t fromLocal (void* to, short from) const override; size_t fromLocal (void* to, unsigned short from) const override; size_t fromLocal (void* to, int from) const override; size_t fromLocal (void* to, unsigned int from) const override; size_t fromLocal (void* to, Int64 from) const override; size_t fromLocal (void* to, uInt64 from) const override; size_t fromLocal (void* to, float from) const override; size_t fromLocal (void* to, double from) const override; // // Convert nr values from local format to VAX format. // The from and to buffer should not overlap. // size_t fromLocal (void* to, const char* from, size_t nr) const override; size_t fromLocal (void* to, const unsigned char* from, size_t nr) const override; size_t fromLocal (void* to, const short* from, size_t nr) const override; size_t fromLocal (void* to, const unsigned short* from, size_t nr) const override; size_t fromLocal (void* to, const int* from, size_t nr) const override; size_t fromLocal (void* to, const unsigned int* from, size_t nr) const override; size_t fromLocal (void* to, const Int64* from, size_t nr) const override; size_t fromLocal (void* to, const uInt64* from, size_t nr) const override; size_t fromLocal (void* to, const float* from, size_t nr) const override; size_t fromLocal (void* to, const double* from, size_t nr) const override; // // Determine if the data for a data type can be simply copied, thus // if no conversion is needed. // Bool canCopy (const char*) const override; Bool canCopy (const unsigned char*) const override; Bool canCopy (const short*) const override; Bool canCopy (const unsigned short*) const override; Bool canCopy (const int*) const override; Bool canCopy (const unsigned int*) const override; Bool canCopy (const Int64*) const override; Bool canCopy (const uInt64*) const override; Bool canCopy (const float*) const override; Bool canCopy (const double*) const override; // // Get the external size of the data type. // unsigned int externalSize (const char*) const override; unsigned int externalSize (const unsigned char*) const override; unsigned int externalSize (const short*) const override; unsigned int externalSize (const unsigned short*) const override; unsigned int externalSize (const int*) const override; unsigned int externalSize (const unsigned int*) const override; unsigned int externalSize (const Int64*) const override; unsigned int externalSize (const uInt64*) const override; unsigned int externalSize (const float*) const override; unsigned int externalSize (const double*) const override; // }; inline VAXDataConversion::VAXDataConversion() {} } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/OS/malloc.cc000066400000000000000000006110541476623553700163410ustar00rootroot00000000000000//# malloc.cc: malloc functions from Doug Lea //# Copyright (C) 1996,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // #include #if !defined(AIPS_NO_LEA_MALLOC) #if !defined(AIPS_LINUX) /* Ignore for linux since it already uses gnu malloc! */ /* This is a version (aka dlmalloc) of malloc/free/realloc written by Doug Lea and released to the public domain, as explained at http://creativecommons.org/licenses/publicdomain. Send questions, comments, complaints, performance data, etc to dl@cs.oswego.edu * Version 2.8.4 Wed May 27 09:56:23 2009 Doug Lea (dl at gee) Note: There may be an updated version of this malloc obtainable at ftp://gee.cs.oswego.edu/pub/misc/malloc.c Check before installing! * Quickstart This library is all in one file to simplify the most common usage: ftp it, compile it (-O3), and link it into another program. All of the compile-time options default to reasonable values for use on most platforms. You might later want to step through various compile-time and dynamic tuning options. For convenience, an include file for code using this malloc is at: ftp://gee.cs.oswego.edu/pub/misc/malloc-2.8.4.h You don't really need this .h file unless you call functions not defined in your system include files. The .h file contains only the excerpts from this file needed for using this malloc on ANSI C/C++ systems, so long as you haven't changed compile-time options about naming and tuning parameters. If you do, then you can create your own malloc.h that does include all settings by cutting at the point indicated below. Note that you may already by default be using a C library containing a malloc that is based on some version of this malloc (for example in linux). You might still want to use the one in this file to customize settings or to avoid overheads associated with library versions. * Vital statistics: Supported pointer/size_t representation: 4 or 8 bytes size_t MUST be an unsigned type of the same width as pointers. (If you are using an ancient system that declares size_t as a signed type, or need it to be a different width than pointers, you can use a previous release of this malloc (e.g. 2.7.2) supporting these.) Alignment: 8 bytes (default) This suffices for nearly all current machines and C compilers. However, you can define MALLOC_ALIGNMENT to be wider than this if necessary (up to 128bytes), at the expense of using more space. Minimum overhead per allocated chunk: 4 or 8 bytes (if 4byte sizes) 8 or 16 bytes (if 8byte sizes) Each malloced chunk has a hidden word of overhead holding size and status information, and additional cross-check word if FOOTERS is defined. Minimum allocated size: 4-byte ptrs: 16 bytes (including overhead) 8-byte ptrs: 32 bytes (including overhead) Even a request for zero bytes (i.e., malloc(0)) returns a pointer to something of the minimum allocatable size. The maximum overhead wastage (i.e., number of extra bytes allocated than were requested in malloc) is less than or equal to the minimum size, except for requests >= mmap_threshold that are serviced via mmap(), where the worst case wastage is about 32 bytes plus the remainder from a system page (the minimal mmap unit); typically 4096 or 8192 bytes. Security: static-safe; optionally more or less The "security" of malloc refers to the ability of malicious code to accentuate the effects of errors (for example, freeing space that is not currently malloc'ed or overwriting past the ends of chunks) in code that calls malloc. This malloc guarantees not to modify any memory locations below the base of heap, i.e., static variables, even in the presence of usage errors. The routines additionally detect most improper frees and reallocs. All this holds as long as the static bookkeeping for malloc itself is not corrupted by some other means. This is only one aspect of security -- these checks do not, and cannot, detect all possible programming errors. If FOOTERS is defined nonzero, then each allocated chunk carries an additional check word to verify that it was malloced from its space. These check words are the same within each execution of a program using malloc, but differ across executions, so externally crafted fake chunks cannot be freed. This improves security by rejecting frees/reallocs that could corrupt heap memory, in addition to the checks preventing writes to statics that are always on. This may further improve security at the expense of time and space overhead. (Note that FOOTERS may also be worth using with MSPACES.) By default detected errors cause the program to abort (calling "abort()"). You can override this to instead proceed past errors by defining PROCEED_ON_ERROR. In this case, a bad free has no effect, and a malloc that encounters a bad address caused by user overwrites will ignore the bad address by dropping pointers and indices to all known memory. This may be appropriate for programs that should continue if at all possible in the face of programming errors, although they may run out of memory because dropped memory is never reclaimed. If you don't like either of these options, you can define CORRUPTION_ERROR_ACTION and USAGE_ERROR_ACTION to do anything else. And if if you are sure that your program using malloc has no errors or vulnerabilities, you can define INSECURE to 1, which might (or might not) provide a small performance improvement. Thread-safety: NOT thread-safe unless USE_LOCKS defined When USE_LOCKS is defined, each public call to malloc, free, etc is surrounded with either a pthread mutex or a win32 spinlock (depending on WIN32). This is not especially fast, and can be a major bottleneck. It is designed only to provide minimal protection in concurrent environments, and to provide a basis for extensions. If you are using malloc in a concurrent program, consider instead using nedmalloc (http://www.nedprod.com/programs/portable/nedmalloc/) or ptmalloc (See http://www.malloc.de), which are derived from versions of this malloc. System requirements: Any combination of MORECORE and/or MMAP/MUNMAP This malloc can use unix sbrk or any emulation (invoked using the CALL_MORECORE macro) and/or mmap/munmap or any emulation (invoked using CALL_MMAP/CALL_MUNMAP) to get and release system memory. On most unix systems, it tends to work best if both MORECORE and MMAP are enabled. On Win32, it uses emulations based on VirtualAlloc. It also uses common C library functions like memset. Compliance: I believe it is compliant with the Single Unix Specification (See http://www.unix.org). Also SVID/XPG, ANSI C, and probably others as well. * Overview of algorithms This is not the fastest, most space-conserving, most portable, or most tunable malloc ever written. However it is among the fastest while also being among the most space-conserving, portable and tunable. Consistent balance across these factors results in a good general-purpose allocator for malloc-intensive programs. In most ways, this malloc is a best-fit allocator. Generally, it chooses the best-fitting existing chunk for a request, with ties broken in approximately least-recently-used order. (This strategy normally maintains low fragmentation.) However, for requests less than 256bytes, it deviates from best-fit when there is not an exactly fitting available chunk by preferring to use space adjacent to that used for the previous small request, as well as by breaking ties in approximately most-recently-used order. (These enhance locality of series of small allocations.) And for very large requests (>= 256Kb by default), it relies on system memory mapping facilities, if supported. (This helps avoid carrying around and possibly fragmenting memory used only for large chunks.) All operations (except malloc_stats and mallinfo) have execution times that are bounded by a constant factor of the number of bits in a size_t, not counting any clearing in calloc or copying in realloc, or actions surrounding MORECORE and MMAP that have times proportional to the number of non-contiguous regions returned by system allocation routines, which is often just 1. In real-time applications, you can optionally suppress segment traversals using NO_SEGMENT_TRAVERSAL, which assures bounded execution even when system allocators return non-contiguous spaces, at the typical expense of carrying around more memory and increased fragmentation. The implementation is not very modular and seriously overuses macros. Perhaps someday all C compilers will do as good a job inlining modular code as can now be done by brute-force expansion, but now, enough of them seem not to. Some compilers issue a lot of warnings about code that is dead/unreachable only on some platforms, and also about intentional uses of negation on unsigned types. All known cases of each can be ignored. For a longer but out of date high-level description, see http://gee.cs.oswego.edu/dl/html/malloc.html * MSPACES If MSPACES is defined, then in addition to malloc, free, etc., this file also defines mspace_malloc, mspace_free, etc. These are versions of malloc routines that take an "mspace" argument obtained using create_mspace, to control all internal bookkeeping. If ONLY_MSPACES is defined, only these versions are compiled. So if you would like to use this allocator for only some allocations, and your system malloc for others, you can compile with ONLY_MSPACES and then do something like... static mspace mymspace = create_mspace(0,0); // for example #define mymalloc(bytes) mspace_malloc(mymspace, bytes) (Note: If you only need one instance of an mspace, you can instead use "USE_DL_PREFIX" to relabel the global malloc.) You can similarly create thread-local allocators by storing mspaces as thread-locals. For example: static __thread mspace tlms = 0; void* tlmalloc(size_t bytes) { if (tlms == 0) tlms = create_mspace(0, 0); return mspace_malloc(tlms, bytes); } void tlfree(void* mem) { mspace_free(tlms, mem); } Unless FOOTERS is defined, each mspace is completely independent. You cannot allocate from one and free to another (although conformance is only weakly checked, so usage errors are not always caught). If FOOTERS is defined, then each chunk carries around a tag indicating its originating mspace, and frees are directed to their originating spaces. ------------------------- Compile-time options --------------------------- Be careful in setting #define values for numerical constants of type size_t. On some systems, literal values are not automatically extended to size_t precision unless they are explicitly casted. You can also use the symbolic values MAX_SIZE_T, SIZE_T_ONE, etc below. WIN32 default: defined if _WIN32 defined Defining WIN32 sets up defaults for MS environment and compilers. Otherwise defaults are for unix. Beware that there seem to be some cases where this malloc might not be a pure drop-in replacement for Win32 malloc: Random-looking failures from Win32 GDI API's (eg; SetDIBits()) may be due to bugs in some video driver implementations when pixel buffers are malloc()ed, and the region spans more than one VirtualAlloc()ed region. Because dlmalloc uses a small (64Kb) default granularity, pixel buffers may straddle virtual allocation regions more often than when using the Microsoft allocator. You can avoid this by using VirtualAlloc() and VirtualFree() for all pixel buffers rather than using malloc(). If this is not possible, recompile this malloc with a larger DEFAULT_GRANULARITY. MALLOC_ALIGNMENT default: (size_t)8 Controls the minimum alignment for malloc'ed chunks. It must be a power of two and at least 8, even on machines for which smaller alignments would suffice. It may be defined as larger than this though. Note however that code and data structures are optimized for the case of 8-byte alignment. MSPACES default: 0 (false) If true, compile in support for independent allocation spaces. This is only supported if HAVE_MMAP is true. ONLY_MSPACES default: 0 (false) If true, only compile in mspace versions, not regular versions. USE_LOCKS default: 0 (false) Causes each call to each public routine to be surrounded with pthread or WIN32 mutex lock/unlock. (If set true, this can be overridden on a per-mspace basis for mspace versions.) If set to a non-zero value other than 1, locks are used, but their implementation is left out, so lock functions must be supplied manually, as described below. USE_SPIN_LOCKS default: 1 iff USE_LOCKS and on x86 using gcc or MSC If true, uses custom spin locks for locking. This is currently supported only for x86 platforms using gcc or recent MS compilers. Otherwise, posix locks or win32 critical sections are used. FOOTERS default: 0 If true, provide extra checking and dispatching by placing information in the footers of allocated chunks. This adds space and time overhead. INSECURE default: 0 If true, omit checks for usage errors and heap space overwrites. USE_DL_PREFIX default: NOT defined Causes compiler to prefix all public routines with the string 'dl'. This can be useful when you only want to use this malloc in one part of a program, using your regular system malloc elsewhere. ABORT default: defined as abort() Defines how to abort on failed checks. On most systems, a failed check cannot die with an "assert" or even print an informative message, because the underlying print routines in turn call malloc, which will fail again. Generally, the best policy is to simply call abort(). It's not very useful to do more than this because many errors due to overwriting will show up as address faults (null, odd addresses etc) rather than malloc-triggered checks, so will also abort. Also, most compilers know that abort() does not return, so can better optimize code conditionally calling it. PROCEED_ON_ERROR default: defined as 0 (false) Controls whether detected bad addresses cause them to bypassed rather than aborting. If set, detected bad arguments to free and realloc are ignored. And all bookkeeping information is zeroed out upon a detected overwrite of freed heap space, thus losing the ability to ever return it from malloc again, but enabling the application to proceed. If PROCEED_ON_ERROR is defined, the static variable malloc_corruption_error_count is compiled in and can be examined to see if errors have occurred. This option generates slower code than the default abort policy. DEBUG default: NOT defined The DEBUG setting is mainly intended for people trying to modify this code or diagnose problems when porting to new platforms. However, it may also be able to better isolate user errors than just using runtime checks. The assertions in the check routines spell out in more detail the assumptions and invariants underlying the algorithms. The checking is fairly extensive, and will slow down execution noticeably. Calling malloc_stats or mallinfo with DEBUG set will attempt to check every non-mmapped allocated and free chunk in the course of computing the summaries. ABORT_ON_ASSERT_FAILURE default: defined as 1 (true) Debugging assertion failures can be nearly impossible if your version of the assert macro causes malloc to be called, which will lead to a cascade of further failures, blowing the runtime stack. ABORT_ON_ASSERT_FAILURE cause assertions failures to call abort(), which will usually make debugging easier. MALLOC_FAILURE_ACTION default: sets errno to ENOMEM, or no-op on win32 The action to take before "return 0" when malloc fails to be able to return memory because there is none available. HAVE_MORECORE default: 1 (true) unless win32 or ONLY_MSPACES True if this system supports sbrk or an emulation of it. MORECORE default: sbrk The name of the sbrk-style system routine to call to obtain more memory. See below for guidance on writing custom MORECORE functions. The type of the argument to sbrk/MORECORE varies across systems. It cannot be size_t, because it supports negative arguments, so it is normally the signed type of the same width as size_t (sometimes declared as "intptr_t"). It doesn't much matter though. Internally, we only call it with arguments less than half the max value of a size_t, which should work across all reasonable possibilities, although sometimes generating compiler warnings. MORECORE_CONTIGUOUS default: 1 (true) if HAVE_MORECORE If true, take advantage of fact that consecutive calls to MORECORE with positive arguments always return contiguous increasing addresses. This is true of unix sbrk. It does not hurt too much to set it true anyway, since malloc copes with non-contiguities. Setting it false when definitely non-contiguous saves time and possibly wasted space it would take to discover this though. MORECORE_CANNOT_TRIM default: NOT defined True if MORECORE cannot release space back to the system when given negative arguments. This is generally necessary only if you are using a hand-crafted MORECORE function that cannot handle negative arguments. NO_SEGMENT_TRAVERSAL default: 0 If non-zero, suppresses traversals of memory segments returned by either MORECORE or CALL_MMAP. This disables merging of segments that are contiguous, and selectively releasing them to the OS if unused, but bounds execution times. HAVE_MMAP default: 1 (true) True if this system supports mmap or an emulation of it. If so, and HAVE_MORECORE is not true, MMAP is used for all system allocation. If set and HAVE_MORECORE is true as well, MMAP is primarily used to directly allocate very large blocks. It is also used as a backup strategy in cases where MORECORE fails to provide space from system. Note: A single call to MUNMAP is assumed to be able to unmap memory that may have be allocated using multiple calls to MMAP, so long as they are adjacent. HAVE_MREMAP default: 1 on linux, else 0 If true realloc() uses mremap() to re-allocate large blocks and extend or shrink allocation spaces. MMAP_CLEARS default: 1 except on WINCE. True if mmap clears memory so calloc doesn't need to. This is true for standard unix mmap using /dev/zero and on WIN32 except for WINCE. USE_BUILTIN_FFS default: 0 (i.e., not used) Causes malloc to use the builtin ffs() function to compute indices. Some compilers may recognize and intrinsify ffs to be faster than the supplied C version. Also, the case of x86 using gcc is special-cased to an asm instruction, so is already as fast as it can be, and so this setting has no effect. Similarly for Win32 under recent MS compilers. (On most x86s, the asm version is only slightly faster than the C version.) malloc_getpagesize default: derive from system includes, or 4096. The system page size. To the extent possible, this malloc manages memory from the system in page-size units. This may be (and usually is) a function rather than a constant. This is ignored if WIN32, where page size is determined using getSystemInfo during initialization. USE_DEV_RANDOM default: 0 (i.e., not used) Causes malloc to use /dev/random to initialize secure magic seed for stamping footers. Otherwise, the current time is used. NO_MALLINFO default: 0 If defined, don't compile "mallinfo". This can be a simple way of dealing with mismatches between system declarations and those in this file. MALLINFO_FIELD_TYPE default: size_t The type of the fields in the mallinfo struct. This was originally defined as "int" in SVID etc, but is more usefully defined as size_t. The value is used only if HAVE_USR_INCLUDE_MALLOC_H is not set REALLOC_ZERO_BYTES_FREES default: not defined This should be set if a call to realloc with zero bytes should be the same as a call to free. Some people think it should. Otherwise, since this malloc returns a unique pointer for malloc(0), so does realloc(p, 0). LACKS_UNISTD_H, LACKS_FCNTL_H, LACKS_SYS_PARAM_H, LACKS_SYS_MMAN_H LACKS_STRINGS_H, LACKS_STRING_H, LACKS_SYS_TYPES_H, LACKS_ERRNO_H LACKS_STDLIB_H default: NOT defined unless on WIN32 Define these if your system does not have these header files. You might need to manually insert some of the declarations they provide. DEFAULT_GRANULARITY default: page size if MORECORE_CONTIGUOUS, system_info.dwAllocationGranularity in WIN32, otherwise 64K. Also settable using mallopt(M_GRANULARITY, x) The unit for allocating and deallocating memory from the system. On most systems with contiguous MORECORE, there is no reason to make this more than a page. However, systems with MMAP tend to either require or encourage larger granularities. You can increase this value to prevent system allocation functions to be called so often, especially if they are slow. The value must be at least one page and must be a power of two. Setting to 0 causes initialization to either page size or win32 region size. (Note: In previous versions of malloc, the equivalent of this option was called "TOP_PAD") DEFAULT_TRIM_THRESHOLD default: 2MB Also settable using mallopt(M_TRIM_THRESHOLD, x) The maximum amount of unused top-most memory to keep before releasing via malloc_trim in free(). Automatic trimming is mainly useful in long-lived programs using contiguous MORECORE. Because trimming via sbrk can be slow on some systems, and can sometimes be wasteful (in cases where programs immediately afterward allocate more large chunks) the value should be high enough so that your overall system performance would improve by releasing this much memory. As a rough guide, you might set to a value close to the average size of a process (program) running on your system. Releasing this much memory would allow such a process to run in memory. Generally, it is worth tuning trim thresholds when a program undergoes phases where several large chunks are allocated and released in ways that can reuse each other's storage, perhaps mixed with phases where there are no such chunks at all. The trim value must be greater than page size to have any useful effect. To disable trimming completely, you can set to MAX_SIZE_T. Note that the trick some people use of mallocing a huge space and then freeing it at program startup, in an attempt to reserve system memory, doesn't have the intended effect under automatic trimming, since that memory will immediately be returned to the system. DEFAULT_MMAP_THRESHOLD default: 256K Also settable using mallopt(M_MMAP_THRESHOLD, x) The request size threshold for using MMAP to directly service a request. Requests of at least this size that cannot be allocated using already-existing space will be serviced via mmap. (If enough normal freed space already exists it is used instead.) Using mmap segregates relatively large chunks of memory so that they can be individually obtained and released from the host system. A request serviced through mmap is never reused by any other request (at least not directly; the system may just so happen to remap successive requests to the same locations). Segregating space in this way has the benefits that: Mmapped space can always be individually released back to the system, which helps keep the system level memory demands of a long-lived program low. Also, mapped memory doesn't become `locked' between other chunks, as can happen with normally allocated chunks, which means that even trimming via malloc_trim would not release them. However, it has the disadvantage that the space cannot be reclaimed, consolidated, and then used to service later requests, as happens with normal chunks. The advantages of mmap nearly always outweigh disadvantages for "large" chunks, but the value of "large" may vary across systems. The default is an empirically derived value that works well in most systems. You can disable mmap by setting to MAX_SIZE_T. MAX_RELEASE_CHECK_RATE default: 4095 unless not HAVE_MMAP The number of consolidated frees between checks to release unused segments when freeing. When using non-contiguous segments, especially with multiple mspaces, checking only for topmost space doesn't always suffice to trigger trimming. To compensate for this, free() will, with a period of MAX_RELEASE_CHECK_RATE (or the current number of segments, if greater) try to release unused segments to the OS when freeing chunks that result in consolidation. The best value for this parameter is a compromise between slowing down frees with relatively costly checks that rarely trigger versus holding on to unused memory. To effectively disable, set to MAX_SIZE_T. This may lead to a very slight speed improvement at the expense of carrying around more memory. */ /* Version identifier to allow people to support multiple versions */ #ifndef DLMALLOC_VERSION #define DLMALLOC_VERSION 20804 #endif /* DLMALLOC_VERSION */ #ifndef WIN32 #ifdef _WIN32 #define WIN32 1 #endif /* _WIN32 */ #ifdef _WIN32_WCE #define LACKS_FCNTL_H #define WIN32 1 #endif /* _WIN32_WCE */ #endif /* WIN32 */ #ifdef WIN32 #define WIN32_LEAN_AND_MEAN #include #define HAVE_MMAP 1 #define HAVE_MORECORE 0 #define LACKS_UNISTD_H #define LACKS_SYS_PARAM_H #define LACKS_SYS_MMAN_H #define LACKS_STRING_H #define LACKS_STRINGS_H #define LACKS_SYS_TYPES_H #define LACKS_ERRNO_H #ifndef MALLOC_FAILURE_ACTION #define MALLOC_FAILURE_ACTION #endif /* MALLOC_FAILURE_ACTION */ #ifdef _WIN32_WCE /* WINCE reportedly does not clear */ #define MMAP_CLEARS 0 #else #define MMAP_CLEARS 1 #endif /* _WIN32_WCE */ #endif /* WIN32 */ #if defined(DARWIN) || defined(_DARWIN) /* Mac OSX docs advise not to use sbrk; it seems better to use mmap */ #ifndef HAVE_MORECORE #define HAVE_MORECORE 0 #define HAVE_MMAP 1 /* OSX allocators provide 16 byte alignment */ #ifndef MALLOC_ALIGNMENT #define MALLOC_ALIGNMENT ((size_t)16U) #endif #endif /* HAVE_MORECORE */ #endif /* DARWIN */ #include /* for magic initialization */ #ifndef LACKS_SYS_TYPES_H #include /* For size_t */ #endif /* LACKS_SYS_TYPES_H */ #if (defined(__GNUC__) && ((defined(__i386__) || defined(__x86_64__)))) || (defined(_MSC_VER) && _MSC_VER>=1310) #define SPIN_LOCKS_AVAILABLE 1 #else #define SPIN_LOCKS_AVAILABLE 0 #endif /* The maximum possible size_t value has all bits set */ #define MAX_SIZE_T (~(size_t)0) #ifndef ONLY_MSPACES #define ONLY_MSPACES 0 /* define to a value */ #else #define ONLY_MSPACES 1 #endif /* ONLY_MSPACES */ #ifndef MSPACES #if ONLY_MSPACES #define MSPACES 1 #else /* ONLY_MSPACES */ #define MSPACES 0 #endif /* ONLY_MSPACES */ #endif /* MSPACES */ #ifndef MALLOC_ALIGNMENT #define MALLOC_ALIGNMENT ((size_t)8U) #endif /* MALLOC_ALIGNMENT */ #ifndef FOOTERS #define FOOTERS 0 #endif /* FOOTERS */ #ifndef ABORT #define ABORT abort() #endif /* ABORT */ #ifndef ABORT_ON_ASSERT_FAILURE #define ABORT_ON_ASSERT_FAILURE 1 #endif /* ABORT_ON_ASSERT_FAILURE */ #ifndef PROCEED_ON_ERROR #define PROCEED_ON_ERROR 0 #endif /* PROCEED_ON_ERROR */ #ifndef USE_LOCKS #define USE_LOCKS 0 #endif /* USE_LOCKS */ #ifndef USE_SPIN_LOCKS #if USE_LOCKS && SPIN_LOCKS_AVAILABLE #define USE_SPIN_LOCKS 1 #else #define USE_SPIN_LOCKS 0 #endif /* USE_LOCKS && SPIN_LOCKS_AVAILABLE. */ #endif /* USE_SPIN_LOCKS */ #ifndef INSECURE #define INSECURE 0 #endif /* INSECURE */ #ifndef HAVE_MMAP #define HAVE_MMAP 1 #endif /* HAVE_MMAP */ #ifndef MMAP_CLEARS #define MMAP_CLEARS 1 #endif /* MMAP_CLEARS */ #ifndef HAVE_MREMAP #ifdef linux #define HAVE_MREMAP 1 #else /* linux */ #define HAVE_MREMAP 0 #endif /* linux */ #endif /* HAVE_MREMAP */ #ifndef MALLOC_FAILURE_ACTION #define MALLOC_FAILURE_ACTION errno = ENOMEM; #endif /* MALLOC_FAILURE_ACTION */ #ifndef HAVE_MORECORE #if ONLY_MSPACES #define HAVE_MORECORE 0 #else /* ONLY_MSPACES */ #define HAVE_MORECORE 1 #endif /* ONLY_MSPACES */ #endif /* HAVE_MORECORE */ #if !HAVE_MORECORE #define MORECORE_CONTIGUOUS 0 #else /* !HAVE_MORECORE */ #define MORECORE_DEFAULT sbrk #ifndef MORECORE_CONTIGUOUS #define MORECORE_CONTIGUOUS 1 #endif /* MORECORE_CONTIGUOUS */ #endif /* HAVE_MORECORE */ #ifndef DEFAULT_GRANULARITY #if (MORECORE_CONTIGUOUS || defined(WIN32)) #define DEFAULT_GRANULARITY (0) /* 0 means to compute in init_mparams */ #else /* MORECORE_CONTIGUOUS */ #define DEFAULT_GRANULARITY ((size_t)64U * (size_t)1024U) #endif /* MORECORE_CONTIGUOUS */ #endif /* DEFAULT_GRANULARITY */ #ifndef DEFAULT_TRIM_THRESHOLD #ifndef MORECORE_CANNOT_TRIM #define DEFAULT_TRIM_THRESHOLD ((size_t)2U * (size_t)1024U * (size_t)1024U) #else /* MORECORE_CANNOT_TRIM */ #define DEFAULT_TRIM_THRESHOLD MAX_SIZE_T #endif /* MORECORE_CANNOT_TRIM */ #endif /* DEFAULT_TRIM_THRESHOLD */ #ifndef DEFAULT_MMAP_THRESHOLD #if HAVE_MMAP #define DEFAULT_MMAP_THRESHOLD ((size_t)256U * (size_t)1024U) #else /* HAVE_MMAP */ #define DEFAULT_MMAP_THRESHOLD MAX_SIZE_T #endif /* HAVE_MMAP */ #endif /* DEFAULT_MMAP_THRESHOLD */ #ifndef MAX_RELEASE_CHECK_RATE #if HAVE_MMAP #define MAX_RELEASE_CHECK_RATE 4095 #else #define MAX_RELEASE_CHECK_RATE MAX_SIZE_T #endif /* HAVE_MMAP */ #endif /* MAX_RELEASE_CHECK_RATE */ #ifndef USE_BUILTIN_FFS #define USE_BUILTIN_FFS 0 #endif /* USE_BUILTIN_FFS */ #ifndef USE_DEV_RANDOM #define USE_DEV_RANDOM 0 #endif /* USE_DEV_RANDOM */ #ifndef NO_MALLINFO #define NO_MALLINFO 0 #endif /* NO_MALLINFO */ #ifndef MALLINFO_FIELD_TYPE #define MALLINFO_FIELD_TYPE size_t #endif /* MALLINFO_FIELD_TYPE */ #ifndef NO_SEGMENT_TRAVERSAL #define NO_SEGMENT_TRAVERSAL 0 #endif /* NO_SEGMENT_TRAVERSAL */ /* mallopt tuning options. SVID/XPG defines four standard parameter numbers for mallopt, normally defined in malloc.h. None of these are used in this malloc, so setting them has no effect. But this malloc does support the following options. */ #define M_TRIM_THRESHOLD (-1) #define M_GRANULARITY (-2) #define M_MMAP_THRESHOLD (-3) /* ------------------------ Mallinfo declarations ------------------------ */ #if !NO_MALLINFO /* This version of malloc supports the standard SVID/XPG mallinfo routine that returns a struct containing usage properties and statistics. It should work on any system that has a /usr/include/malloc.h defining struct mallinfo. The main declaration needed is the mallinfo struct that is returned (by-copy) by mallinfo(). The malloinfo struct contains a bunch of fields that are not even meaningful in this version of malloc. These fields are are instead filled by mallinfo() with other numbers that might be of interest. HAVE_USR_INCLUDE_MALLOC_H should be set if you have a /usr/include/malloc.h file that includes a declaration of struct mallinfo. If so, it is included; else a compliant version is declared below. These must be precisely the same for mallinfo() to work. The original SVID version of this struct, defined on most systems with mallinfo, declares all fields as ints. But some others define as unsigned long. If your system defines the fields using a type of different width than listed here, you MUST #include your system version and #define HAVE_USR_INCLUDE_MALLOC_H. */ /* #define HAVE_USR_INCLUDE_MALLOC_H */ #ifdef HAVE_USR_INCLUDE_MALLOC_H #include "/usr/include/malloc.h" #else /* HAVE_USR_INCLUDE_MALLOC_H */ #ifndef STRUCT_MALLINFO_DECLARED #define STRUCT_MALLINFO_DECLARED 1 struct mallinfo { MALLINFO_FIELD_TYPE arena; /* non-mmapped space allocated from system */ MALLINFO_FIELD_TYPE ordblks; /* number of free chunks */ MALLINFO_FIELD_TYPE smblks; /* always 0 */ MALLINFO_FIELD_TYPE hblks; /* always 0 */ MALLINFO_FIELD_TYPE hblkhd; /* space in mmapped regions */ MALLINFO_FIELD_TYPE usmblks; /* maximum total allocated space */ MALLINFO_FIELD_TYPE fsmblks; /* always 0 */ MALLINFO_FIELD_TYPE uordblks; /* total allocated space */ MALLINFO_FIELD_TYPE fordblks; /* total free space */ MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */ }; #endif /* STRUCT_MALLINFO_DECLARED */ #endif /* HAVE_USR_INCLUDE_MALLOC_H */ #endif /* NO_MALLINFO */ /* Try to persuade compilers to inline. The most critical functions for inlining are defined as macros, so these aren't used for them. */ #ifndef FORCEINLINE #if defined(__GNUC__) #define FORCEINLINE __inline __attribute__ ((always_inline)) #elif defined(_MSC_VER) #define FORCEINLINE __forceinline #endif #endif #ifndef NOINLINE #if defined(__GNUC__) #define NOINLINE __attribute__ ((noinline)) #elif defined(_MSC_VER) #define NOINLINE __declspec(noinline) #else #define NOINLINE #endif #endif #ifdef __cplusplus extern "C" { #ifndef FORCEINLINE #define FORCEINLINE inline #endif #endif /* __cplusplus */ #ifndef FORCEINLINE #define FORCEINLINE #endif #if !ONLY_MSPACES /* ------------------- Declarations of public routines ------------------- */ #ifndef USE_DL_PREFIX #define dlcalloc calloc #define dlfree free #define dlmalloc malloc #define dlmemalign memalign #define dlrealloc realloc #define dlvalloc valloc #define dlpvalloc pvalloc #define dlmallinfo mallinfo #define dlmallopt mallopt #define dlmalloc_trim malloc_trim #define dlmalloc_stats malloc_stats #define dlmalloc_usable_size malloc_usable_size #define dlmalloc_footprint malloc_footprint #define dlmalloc_max_footprint malloc_max_footprint #define dlindependent_calloc independent_calloc #define dlindependent_comalloc independent_comalloc #endif /* USE_DL_PREFIX */ /* malloc(size_t n) Returns a pointer to a newly allocated chunk of at least n bytes, or null if no space is available, in which case errno is set to ENOMEM on ANSI C systems. If n is zero, malloc returns a minimum-sized chunk. (The minimum size is 16 bytes on most 32bit systems, and 32 bytes on 64bit systems.) Note that size_t is an unsigned type, so calls with arguments that would be negative if signed are interpreted as requests for huge amounts of space, which will often fail. The maximum supported value of n differs across systems, but is in all cases less than the maximum representable value of a size_t. */ void* dlmalloc(size_t); /* free(void* p) Releases the chunk of memory pointed to by p, that had been previously allocated using malloc or a related routine such as realloc. It has no effect if p is null. If p was not malloced or already freed, free(p) will by default cause the current program to abort. */ void dlfree(void*); /* calloc(size_t n_elements, size_t element_size); Returns a pointer to n_elements * element_size bytes, with all locations set to zero. */ void* dlcalloc(size_t, size_t); /* realloc(void* p, size_t n) Returns a pointer to a chunk of size n that contains the same data as does chunk p up to the minimum of (n, p's size) bytes, or null if no space is available. The returned pointer may or may not be the same as p. The algorithm prefers extending p in most cases when possible, otherwise it employs the equivalent of a malloc-copy-free sequence. If p is null, realloc is equivalent to malloc. If space is not available, realloc returns null, errno is set (if on ANSI) and p is NOT freed. if n is for fewer bytes than already held by p, the newly unused space is lopped off and freed if possible. realloc with a size argument of zero (re)allocates a minimum-sized chunk. The old unix realloc convention of allowing the last-free'd chunk to be used as an argument to realloc is not supported. */ void* dlrealloc(void*, size_t); /* memalign(size_t alignment, size_t n); Returns a pointer to a newly allocated chunk of n bytes, aligned in accord with the alignment argument. The alignment argument should be a power of two. If the argument is not a power of two, the nearest greater power is used. 8-byte alignment is guaranteed by normal malloc calls, so don't bother calling memalign with an argument of 8 or less. Overreliance on memalign is a sure way to fragment space. */ void* dlmemalign(size_t, size_t); /* valloc(size_t n); Equivalent to memalign(pagesize, n), where pagesize is the page size of the system. If the pagesize is unknown, 4096 is used. */ void* dlvalloc(size_t); /* mallopt(int parameter_number, int parameter_value) Sets tunable parameters The format is to provide a (parameter-number, parameter-value) pair. mallopt then sets the corresponding parameter to the argument value if it can (i.e., so long as the value is meaningful), and returns 1 if successful else 0. To workaround the fact that mallopt is specified to use int, not size_t parameters, the value -1 is specially treated as the maximum unsigned size_t value. SVID/XPG/ANSI defines four standard param numbers for mallopt, normally defined in malloc.h. None of these are use in this malloc, so setting them has no effect. But this malloc also supports other options in mallopt. See below for details. Briefly, supported parameters are as follows (listed defaults are for "typical" configurations). Symbol param # default allowed param values M_TRIM_THRESHOLD -1 2*1024*1024 any (-1 disables) M_GRANULARITY -2 page size any power of 2 >= page size M_MMAP_THRESHOLD -3 256*1024 any (or 0 if no MMAP support) */ int dlmallopt(int, int); /* malloc_footprint(); Returns the number of bytes obtained from the system. The total number of bytes allocated by malloc, realloc etc., is less than this value. Unlike mallinfo, this function returns only a precomputed result, so can be called frequently to monitor memory consumption. Even if locks are otherwise defined, this function does not use them, so results might not be up to date. */ size_t dlmalloc_footprint(void); /* malloc_max_footprint(); Returns the maximum number of bytes obtained from the system. This value will be greater than current footprint if deallocated space has been reclaimed by the system. The peak number of bytes allocated by malloc, realloc etc., is less than this value. Unlike mallinfo, this function returns only a precomputed result, so can be called frequently to monitor memory consumption. Even if locks are otherwise defined, this function does not use them, so results might not be up to date. */ size_t dlmalloc_max_footprint(void); #if !NO_MALLINFO /* mallinfo() Returns (by copy) a struct containing various summary statistics: arena: current total non-mmapped bytes allocated from system ordblks: the number of free chunks smblks: always zero. hblks: current number of mmapped regions hblkhd: total bytes held in mmapped regions usmblks: the maximum total allocated space. This will be greater than current total if trimming has occurred. fsmblks: always zero uordblks: current total allocated space (normal or mmapped) fordblks: total free space keepcost: the maximum number of bytes that could ideally be released back to system via malloc_trim. ("ideally" means that it ignores page restrictions etc.) Because these fields are ints, but internal bookkeeping may be kept as longs, the reported values may wrap around zero and thus be inaccurate. */ struct mallinfo dlmallinfo(void); #endif /* NO_MALLINFO */ /* independent_calloc(size_t n_elements, size_t element_size, void* chunks[]); independent_calloc is similar to calloc, but instead of returning a single cleared space, it returns an array of pointers to n_elements independent elements that can hold contents of size elem_size, each of which starts out cleared, and can be independently freed, realloc'ed etc. The elements are guaranteed to be adjacently allocated (this is not guaranteed to occur with multiple callocs or mallocs), which may also improve cache locality in some applications. The "chunks" argument is optional (i.e., may be null, which is probably the most typical usage). If it is null, the returned array is itself dynamically allocated and should also be freed when it is no longer needed. Otherwise, the chunks array must be of at least n_elements in length. It is filled in with the pointers to the chunks. In either case, independent_calloc returns this pointer array, or null if the allocation failed. If n_elements is zero and "chunks" is null, it returns a chunk representing an array with zero elements (which should be freed if not wanted). Each element must be individually freed when it is no longer needed. If you'd like to instead be able to free all at once, you should instead use regular calloc and assign pointers into this space to represent elements. (In this case though, you cannot independently free elements.) independent_calloc simplifies and speeds up implementations of many kinds of pools. It may also be useful when constructing large data structures that initially have a fixed number of fixed-sized nodes, but the number is not known at compile time, and some of the nodes may later need to be freed. For example: struct Node { int item; struct Node* next; }; struct Node* build_list() { struct Node** pool; int n = read_number_of_nodes_needed(); if (n <= 0) return 0; pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0); if (pool == 0) die(); // organize into a linked list... struct Node* first = pool[0]; for (i = 0; i < n-1; ++i) pool[i]->next = pool[i+1]; free(pool); // Can now free the array (or not, if it is needed later) return first; } */ void** dlindependent_calloc(size_t, size_t, void**); /* independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]); independent_comalloc allocates, all at once, a set of n_elements chunks with sizes indicated in the "sizes" array. It returns an array of pointers to these elements, each of which can be independently freed, realloc'ed etc. The elements are guaranteed to be adjacently allocated (this is not guaranteed to occur with multiple callocs or mallocs), which may also improve cache locality in some applications. The "chunks" argument is optional (i.e., may be null). If it is null the returned array is itself dynamically allocated and should also be freed when it is no longer needed. Otherwise, the chunks array must be of at least n_elements in length. It is filled in with the pointers to the chunks. In either case, independent_comalloc returns this pointer array, or null if the allocation failed. If n_elements is zero and chunks is null, it returns a chunk representing an array with zero elements (which should be freed if not wanted). Each element must be individually freed when it is no longer needed. If you'd like to instead be able to free all at once, you should instead use a single regular malloc, and assign pointers at particular offsets in the aggregate space. (In this case though, you cannot independently free elements.) independent_comallac differs from independent_calloc in that each element may have a different size, and also that it does not automatically clear elements. independent_comalloc can be used to speed up allocation in cases where several structs or objects must always be allocated at the same time. For example: struct Head { ... } struct Foot { ... } void send_message(char* msg) { int msglen = strlen(msg); size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) }; void* chunks[3]; if (independent_comalloc(3, sizes, chunks) == 0) die(); struct Head* head = (struct Head*)(chunks[0]); char* body = (char*)(chunks[1]); struct Foot* foot = (struct Foot*)(chunks[2]); // ... } In general though, independent_comalloc is worth using only for larger values of n_elements. For small values, you probably won't detect enough difference from series of malloc calls to bother. Overuse of independent_comalloc can increase overall memory usage, since it cannot reuse existing noncontiguous small chunks that might be available for some of the elements. */ void** dlindependent_comalloc(size_t, size_t*, void**); /* pvalloc(size_t n); Equivalent to valloc(minimum-page-that-holds(n)), that is, round up n to nearest pagesize. */ void* dlpvalloc(size_t); /* malloc_trim(size_t pad); If possible, gives memory back to the system (via negative arguments to sbrk) if there is unused memory at the `high' end of the malloc pool or in unused MMAP segments. You can call this after freeing large blocks of memory to potentially reduce the system-level memory requirements of a program. However, it cannot guarantee to reduce memory. Under some allocation patterns, some large free blocks of memory will be locked between two used chunks, so they cannot be given back to the system. The `pad' argument to malloc_trim represents the amount of free trailing space to leave untrimmed. If this argument is zero, only the minimum amount of memory to maintain internal data structures will be left. Non-zero arguments can be supplied to maintain enough trailing space to service future expected allocations without having to re-obtain memory from the system. Malloc_trim returns 1 if it actually released any memory, else 0. */ int dlmalloc_trim(size_t); /* malloc_stats(); Prints on stderr the amount of space obtained from the system (both via sbrk and mmap), the maximum amount (which may be more than current if malloc_trim and/or munmap got called), and the current number of bytes allocated via malloc (or realloc, etc) but not yet freed. Note that this is the number of bytes allocated, not the number requested. It will be larger than the number requested because of alignment and bookkeeping overhead. Because it includes alignment wastage as being in use, this figure may be greater than zero even when no user-level chunks are allocated. The reported current and maximum system memory can be inaccurate if a program makes other calls to system memory allocation functions (normally sbrk) outside of malloc. malloc_stats prints only the most commonly interesting statistics. More information can be obtained by calling mallinfo. */ void dlmalloc_stats(void); #endif /* ONLY_MSPACES */ /* malloc_usable_size(void* p); Returns the number of bytes you can actually use in an allocated chunk, which may be more than you requested (although often not) due to alignment and minimum size constraints. You can use this many bytes without worrying about overwriting other allocated objects. This is not a particularly great programming practice. malloc_usable_size can be more useful in debugging and assertions, for example: p = malloc(n); assert(malloc_usable_size(p) >= 256); */ size_t dlmalloc_usable_size(void*); #if MSPACES /* mspace is an opaque type representing an independent region of space that supports mspace_malloc, etc. */ typedef void* mspace; /* create_mspace creates and returns a new independent space with the given initial capacity, or, if 0, the default granularity size. It returns null if there is no system memory available to create the space. If argument locked is non-zero, the space uses a separate lock to control access. The capacity of the space will grow dynamically as needed to service mspace_malloc requests. You can control the sizes of incremental increases of this space by compiling with a different DEFAULT_GRANULARITY or dynamically setting with mallopt(M_GRANULARITY, value). */ mspace create_mspace(size_t capacity, int locked); /* destroy_mspace destroys the given space, and attempts to return all of its memory back to the system, returning the total number of bytes freed. After destruction, the results of access to all memory used by the space become undefined. */ size_t destroy_mspace(mspace msp); /* create_mspace_with_base uses the memory supplied as the initial base of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this space is used for bookkeeping, so the capacity must be at least this large. (Otherwise 0 is returned.) When this initial space is exhausted, additional memory will be obtained from the system. Destroying this space will deallocate all additionally allocated space (if possible) but not the initial base. */ mspace create_mspace_with_base(void* base, size_t capacity, int locked); /* mspace_track_large_chunks controls whether requests for large chunks are allocated in their own untracked mmapped regions, separate from others in this mspace. By default large chunks are not tracked, which reduces fragmentation. However, such chunks are not necessarily released to the system upon destroy_mspace. Enabling tracking by setting to true may increase fragmentation, but avoids leakage when relying on destroy_mspace to release all memory allocated using this space. The function returns the previous setting. */ int mspace_track_large_chunks(mspace msp, int enable); /* mspace_malloc behaves as malloc, but operates within the given space. */ void* mspace_malloc(mspace msp, size_t bytes); /* mspace_free behaves as free, but operates within the given space. If compiled with FOOTERS==1, mspace_free is not actually needed. free may be called instead of mspace_free because freed chunks from any space are handled by their originating spaces. */ void mspace_free(mspace msp, void* mem); /* mspace_realloc behaves as realloc, but operates within the given space. If compiled with FOOTERS==1, mspace_realloc is not actually needed. realloc may be called instead of mspace_realloc because realloced chunks from any space are handled by their originating spaces. */ void* mspace_realloc(mspace msp, void* mem, size_t newsize); /* mspace_calloc behaves as calloc, but operates within the given space. */ void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size); /* mspace_memalign behaves as memalign, but operates within the given space. */ void* mspace_memalign(mspace msp, size_t alignment, size_t bytes); /* mspace_independent_calloc behaves as independent_calloc, but operates within the given space. */ void** mspace_independent_calloc(mspace msp, size_t n_elements, size_t elem_size, void* chunks[]); /* mspace_independent_comalloc behaves as independent_comalloc, but operates within the given space. */ void** mspace_independent_comalloc(mspace msp, size_t n_elements, size_t sizes[], void* chunks[]); /* mspace_footprint() returns the number of bytes obtained from the system for this space. */ size_t mspace_footprint(mspace msp); /* mspace_max_footprint() returns the peak number of bytes obtained from the system for this space. */ size_t mspace_max_footprint(mspace msp); #if !NO_MALLINFO /* mspace_mallinfo behaves as mallinfo, but reports properties of the given space. */ struct mallinfo mspace_mallinfo(mspace msp); #endif /* NO_MALLINFO */ /* malloc_usable_size(void* p) behaves the same as malloc_usable_size; */ size_t mspace_usable_size(void* mem); /* mspace_malloc_stats behaves as malloc_stats, but reports properties of the given space. */ void mspace_malloc_stats(mspace msp); /* mspace_trim behaves as malloc_trim, but operates within the given space. */ int mspace_trim(mspace msp, size_t pad); /* An alias for mallopt. */ int mspace_mallopt(int, int); #endif /* MSPACES */ #ifdef __cplusplus }; /* end of extern "C" */ #endif /* __cplusplus */ /* ======================================================================== To make a fully customizable malloc.h header file, cut everything above this line, put into file malloc.h, edit to suit, and #include it on the next line, as well as in programs that use this malloc. ======================================================================== */ /* #include "malloc.h" */ /*------------------------------ internal #includes ---------------------- */ #ifdef WIN32 #pragma warning( disable : 4146 ) /* no "unsigned" warnings */ #endif /* WIN32 */ #include /* for printing in malloc_stats */ #ifndef LACKS_ERRNO_H #include /* for MALLOC_FAILURE_ACTION */ #endif /* LACKS_ERRNO_H */ #if FOOTERS || DEBUG #include /* for magic initialization */ #endif /* FOOTERS */ #ifndef LACKS_STDLIB_H #include /* for abort() */ #endif /* LACKS_STDLIB_H */ #ifdef DEBUG #if ABORT_ON_ASSERT_FAILURE #undef assert #define assert(x) if(!(x)) ABORT #else /* ABORT_ON_ASSERT_FAILURE */ #include #endif /* ABORT_ON_ASSERT_FAILURE */ #else /* DEBUG */ #ifndef assert #define assert(x) #endif #define DEBUG 0 #endif /* DEBUG */ #ifndef LACKS_STRING_H #include /* for memset etc */ #endif /* LACKS_STRING_H */ #if USE_BUILTIN_FFS #ifndef LACKS_STRINGS_H #include /* for ffs */ #endif /* LACKS_STRINGS_H */ #endif /* USE_BUILTIN_FFS */ #if HAVE_MMAP #ifndef LACKS_SYS_MMAN_H /* On some versions of linux, mremap decl in mman.h needs __USE_GNU set */ #if (defined(linux) && !defined(__USE_GNU)) #define __USE_GNU 1 #include /* for mmap */ #undef __USE_GNU #else #include /* for mmap */ #endif /* linux */ #endif /* LACKS_SYS_MMAN_H */ #ifndef LACKS_FCNTL_H #include #endif /* LACKS_FCNTL_H */ #endif /* HAVE_MMAP */ #ifndef LACKS_UNISTD_H #include /* for sbrk, sysconf */ #else /* LACKS_UNISTD_H */ #if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) extern void* sbrk(ptrdiff_t); #endif /* FreeBSD etc */ #endif /* LACKS_UNISTD_H */ /* Declarations for locking */ #if USE_LOCKS #ifndef WIN32 #include #if defined (__SVR4) && defined (__sun) /* solaris */ #include #endif /* solaris */ #else #ifndef _M_AMD64 /* These are already defined on AMD64 builds */ #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ LONG __cdecl _InterlockedCompareExchange(LONG volatile *Dest, LONG Exchange, LONG Comp); LONG __cdecl _InterlockedExchange(LONG volatile *Target, LONG Value); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* _M_AMD64 */ #pragma intrinsic (_InterlockedCompareExchange) #pragma intrinsic (_InterlockedExchange) #define interlockedcompareexchange _InterlockedCompareExchange #define interlockedexchange _InterlockedExchange #endif /* Win32 */ #endif /* USE_LOCKS */ /* Declarations for bit scanning on win32 */ #if defined(_MSC_VER) && _MSC_VER>=1300 #ifndef BitScanForward /* Try to avoid pulling in WinNT.h */ #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ unsigned char _BitScanForward(unsigned long *index, unsigned long mask); unsigned char _BitScanReverse(unsigned long *index, unsigned long mask); #ifdef __cplusplus } #endif /* __cplusplus */ #define BitScanForward _BitScanForward #define BitScanReverse _BitScanReverse #pragma intrinsic(_BitScanForward) #pragma intrinsic(_BitScanReverse) #endif /* BitScanForward */ #endif /* defined(_MSC_VER) && _MSC_VER>=1300 */ #ifndef WIN32 #ifndef malloc_getpagesize # ifdef _SC_PAGESIZE /* some SVR4 systems omit an underscore */ # ifndef _SC_PAGE_SIZE # define _SC_PAGE_SIZE _SC_PAGESIZE # endif # endif # ifdef _SC_PAGE_SIZE # define malloc_getpagesize sysconf(_SC_PAGE_SIZE) # else # if defined(BSD) || defined(DGUX) || defined(HAVE_GETPAGESIZE) extern size_t getpagesize(); # define malloc_getpagesize getpagesize() # else # ifdef WIN32 /* use supplied emulation of getpagesize */ # define malloc_getpagesize getpagesize() # else # ifndef LACKS_SYS_PARAM_H # include # endif # ifdef EXEC_PAGESIZE # define malloc_getpagesize EXEC_PAGESIZE # else # ifdef NBPG # ifndef CLSIZE # define malloc_getpagesize NBPG # else # define malloc_getpagesize (NBPG * CLSIZE) # endif # else # ifdef NBPC # define malloc_getpagesize NBPC # else # ifdef PAGESIZE # define malloc_getpagesize PAGESIZE # else /* just guess */ # define malloc_getpagesize ((size_t)4096U) # endif # endif # endif # endif # endif # endif # endif #endif #endif /* ------------------- size_t and alignment properties -------------------- */ /* The byte and bit size of a size_t */ #define SIZE_T_SIZE (sizeof(size_t)) #define SIZE_T_BITSIZE (sizeof(size_t) << 3) /* Some constants coerced to size_t */ /* Annoying but necessary to avoid errors on some platforms */ #define SIZE_T_ZERO ((size_t)0) #define SIZE_T_ONE ((size_t)1) #define SIZE_T_TWO ((size_t)2) #define SIZE_T_FOUR ((size_t)4) #define TWO_SIZE_T_SIZES (SIZE_T_SIZE<<1) #define FOUR_SIZE_T_SIZES (SIZE_T_SIZE<<2) #define SIX_SIZE_T_SIZES (FOUR_SIZE_T_SIZES+TWO_SIZE_T_SIZES) #define HALF_MAX_SIZE_T (MAX_SIZE_T / 2U) /* The bit mask value corresponding to MALLOC_ALIGNMENT */ #define CHUNK_ALIGN_MASK (MALLOC_ALIGNMENT - SIZE_T_ONE) /* True if address a has acceptable alignment */ #define is_aligned(A) (((size_t)((A)) & (CHUNK_ALIGN_MASK)) == 0) /* the number of bytes to offset an address to align it */ #define align_offset(A)\ ((((size_t)(A) & CHUNK_ALIGN_MASK) == 0)? 0 :\ ((MALLOC_ALIGNMENT - ((size_t)(A) & CHUNK_ALIGN_MASK)) & CHUNK_ALIGN_MASK)) /* -------------------------- MMAP preliminaries ------------------------- */ /* If HAVE_MORECORE or HAVE_MMAP are false, we just define calls and checks to fail so compiler optimizer can delete code rather than using so many "#if"s. */ /* MORECORE and MMAP must return MFAIL on failure */ #define MFAIL ((void*)(MAX_SIZE_T)) #define CMFAIL ((char*)(MFAIL)) /* defined for convenience */ #if HAVE_MMAP #ifndef WIN32 #define MUNMAP_DEFAULT(a, s) munmap((a), (s)) #define MMAP_PROT (PROT_READ|PROT_WRITE) #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) #define MAP_ANONYMOUS MAP_ANON #endif /* MAP_ANON */ #ifdef MAP_ANONYMOUS #define MMAP_FLAGS (MAP_PRIVATE|MAP_ANONYMOUS) #define MMAP_DEFAULT(s) mmap(0, (s), MMAP_PROT, MMAP_FLAGS, -1, 0) #else /* MAP_ANONYMOUS */ /* Nearly all versions of mmap support MAP_ANONYMOUS, so the following is unlikely to be needed, but is supplied just in case. */ #define MMAP_FLAGS (MAP_PRIVATE) static int dev_zero_fd = -1; /* Cached file descriptor for /dev/zero. */ #define MMAP_DEFAULT(s) ((dev_zero_fd < 0) ? \ (dev_zero_fd = open("/dev/zero", O_RDWR), \ mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) : \ mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) #endif /* MAP_ANONYMOUS */ #define DIRECT_MMAP_DEFAULT(s) MMAP_DEFAULT(s) #else /* WIN32 */ /* Win32 MMAP via VirtualAlloc */ static FORCEINLINE void* win32mmap(size_t size) { void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); return (ptr != 0)? ptr: MFAIL; } /* For direct MMAP, use MEM_TOP_DOWN to minimize interference */ static FORCEINLINE void* win32direct_mmap(size_t size) { void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, PAGE_READWRITE); return (ptr != 0)? ptr: MFAIL; } /* This function supports releasing coalesed segments */ static FORCEINLINE int win32munmap(void* ptr, size_t size) { MEMORY_BASIC_INFORMATION minfo; char* cptr = (char*)ptr; while (size) { if (VirtualQuery(cptr, &minfo, sizeof(minfo)) == 0) return -1; if (minfo.BaseAddress != cptr || minfo.AllocationBase != cptr || minfo.State != MEM_COMMIT || minfo.RegionSize > size) return -1; if (VirtualFree(cptr, 0, MEM_RELEASE) == 0) return -1; cptr += minfo.RegionSize; size -= minfo.RegionSize; } return 0; } #define MMAP_DEFAULT(s) win32mmap(s) #define MUNMAP_DEFAULT(a, s) win32munmap((a), (s)) #define DIRECT_MMAP_DEFAULT(s) win32direct_mmap(s) #endif /* WIN32 */ #endif /* HAVE_MMAP */ #if HAVE_MREMAP #ifndef WIN32 #define MREMAP_DEFAULT(addr, osz, nsz, mv) mremap((addr), (osz), (nsz), (mv)) #endif /* WIN32 */ #endif /* HAVE_MREMAP */ /** * Define CALL_MORECORE */ #if HAVE_MORECORE #ifdef MORECORE #define CALL_MORECORE(S) MORECORE(S) #else /* MORECORE */ #define CALL_MORECORE(S) MORECORE_DEFAULT(S) #endif /* MORECORE */ #else /* HAVE_MORECORE */ #define CALL_MORECORE(S) MFAIL #endif /* HAVE_MORECORE */ /** * Define CALL_MMAP/CALL_MUNMAP/CALL_DIRECT_MMAP */ #if HAVE_MMAP #define USE_MMAP_BIT (SIZE_T_ONE) #ifdef MMAP #define CALL_MMAP(s) MMAP(s) #else /* MMAP */ #define CALL_MMAP(s) MMAP_DEFAULT(s) #endif /* MMAP */ #ifdef MUNMAP #define CALL_MUNMAP(a, s) MUNMAP((a), (s)) #else /* MUNMAP */ #define CALL_MUNMAP(a, s) MUNMAP_DEFAULT((a), (s)) #endif /* MUNMAP */ #ifdef DIRECT_MMAP #define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s) #else /* DIRECT_MMAP */ #define CALL_DIRECT_MMAP(s) DIRECT_MMAP_DEFAULT(s) #endif /* DIRECT_MMAP */ #else /* HAVE_MMAP */ #define USE_MMAP_BIT (SIZE_T_ZERO) #define MMAP(s) MFAIL #define MUNMAP(a, s) (-1) #define DIRECT_MMAP(s) MFAIL #define CALL_DIRECT_MMAP(s) DIRECT_MMAP(s) #define CALL_MMAP(s) MMAP(s) #define CALL_MUNMAP(a, s) MUNMAP((a), (s)) #endif /* HAVE_MMAP */ /** * Define CALL_MREMAP */ #if HAVE_MMAP && HAVE_MREMAP #ifdef MREMAP #define CALL_MREMAP(addr, osz, nsz, mv) MREMAP((addr), (osz), (nsz), (mv)) #else /* MREMAP */ #define CALL_MREMAP(addr, osz, nsz, mv) MREMAP_DEFAULT((addr), (osz), (nsz), (mv)) #endif /* MREMAP */ #else /* HAVE_MMAP && HAVE_MREMAP */ #define CALL_MREMAP(addr, osz, nsz, mv) MFAIL #endif /* HAVE_MMAP && HAVE_MREMAP */ /* mstate bit set if continguous morecore disabled or failed */ #define USE_NONCONTIGUOUS_BIT (4U) /* segment bit set in create_mspace_with_base */ #define EXTERN_BIT (8U) /* --------------------------- Lock preliminaries ------------------------ */ /* When locks are defined, there is one global lock, plus one per-mspace lock. The global lock_ensures that mparams.magic and other unique mparams values are initialized only once. It also protects sequences of calls to MORECORE. In many cases sys_alloc requires two calls, that should not be interleaved with calls by other threads. This does not protect against direct calls to MORECORE by other threads not using this lock, so there is still code to cope the best we can on interference. Per-mspace locks surround calls to malloc, free, etc. To enable use in layered extensions, per-mspace locks are reentrant. Because lock-protected regions generally have bounded times, it is OK to use the supplied simple spinlocks in the custom versions for x86. Spinlocks are likely to improve performance for lightly contended applications, but worsen performance under heavy contention. If USE_LOCKS is > 1, the definitions of lock routines here are bypassed, in which case you will need to define the type MLOCK_T, and at least INITIAL_LOCK, ACQUIRE_LOCK, RELEASE_LOCK and possibly TRY_LOCK (which is not used in this malloc, but commonly needed in extensions.) You must also declare a static MLOCK_T malloc_global_mutex = { initialization values };. */ #if USE_LOCKS == 1 #if USE_SPIN_LOCKS && SPIN_LOCKS_AVAILABLE #ifndef WIN32 /* Custom pthread-style spin locks on x86 and x64 for gcc */ struct pthread_mlock_t { volatile unsigned int l; unsigned int c; pthread_t threadid; }; #define MLOCK_T struct pthread_mlock_t #define CURRENT_THREAD pthread_self() #define INITIAL_LOCK(sl) ((sl)->threadid = 0, (sl)->l = (sl)->c = 0, 0) #define ACQUIRE_LOCK(sl) pthread_acquire_lock(sl) #define RELEASE_LOCK(sl) pthread_release_lock(sl) #define TRY_LOCK(sl) pthread_try_lock(sl) #define SPINS_PER_YIELD 63 static MLOCK_T malloc_global_mutex = { 0, 0, 0}; static FORCEINLINE int pthread_acquire_lock (MLOCK_T *sl) { int spins = 0; volatile unsigned int* lp = &sl->l; for (;;) { if (*lp != 0) { if (sl->threadid == CURRENT_THREAD) { ++sl->c; return 0; } } else { /* place args to cmpxchgl in locals to evade oddities in some gccs */ int cmp = 0; int val = 1; int ret; __asm__ __volatile__ ("lock; cmpxchgl %1, %2" : "=a" (ret) : "r" (val), "m" (*(lp)), "0"(cmp) : "memory", "cc"); if (!ret) { assert(!sl->threadid); sl->threadid = CURRENT_THREAD; sl->c = 1; return 0; } } if ((++spins & SPINS_PER_YIELD) == 0) { #if defined (__SVR4) && defined (__sun) /* solaris */ thr_yield(); #else #if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) sched_yield(); #else /* no-op yield on unknown systems */ ; #endif /* __linux__ || __FreeBSD__ || __APPLE__ */ #endif /* solaris */ } } } static FORCEINLINE void pthread_release_lock (MLOCK_T *sl) { volatile unsigned int* lp = &sl->l; assert(*lp != 0); assert(sl->threadid == CURRENT_THREAD); if (--sl->c == 0) { sl->threadid = 0; int prev = 0; int ret; __asm__ __volatile__ ("lock; xchgl %0, %1" : "=r" (ret) : "m" (*(lp)), "0"(prev) : "memory"); } } static FORCEINLINE int pthread_try_lock (MLOCK_T *sl) { volatile unsigned int* lp = &sl->l; if (*lp != 0) { if (sl->threadid == CURRENT_THREAD) { ++sl->c; return 1; } } else { int cmp = 0; int val = 1; int ret; __asm__ __volatile__ ("lock; cmpxchgl %1, %2" : "=a" (ret) : "r" (val), "m" (*(lp)), "0"(cmp) : "memory", "cc"); if (!ret) { assert(!sl->threadid); sl->threadid = CURRENT_THREAD; sl->c = 1; return 1; } } return 0; } #else /* WIN32 */ /* Custom win32-style spin locks on x86 and x64 for MSC */ struct win32_mlock_t { volatile long l; unsigned int c; long threadid; }; #define MLOCK_T struct win32_mlock_t #define CURRENT_THREAD GetCurrentThreadId() #define INITIAL_LOCK(sl) ((sl)->threadid = 0, (sl)->l = (sl)->c = 0, 0) #define ACQUIRE_LOCK(sl) win32_acquire_lock(sl) #define RELEASE_LOCK(sl) win32_release_lock(sl) #define TRY_LOCK(sl) win32_try_lock(sl) #define SPINS_PER_YIELD 63 static MLOCK_T malloc_global_mutex = { 0, 0, 0}; static FORCEINLINE int win32_acquire_lock (MLOCK_T *sl) { int spins = 0; for (;;) { if (sl->l != 0) { if (sl->threadid == CURRENT_THREAD) { ++sl->c; return 0; } } else { if (!interlockedexchange(&sl->l, 1)) { assert(!sl->threadid); sl->threadid = CURRENT_THREAD; sl->c = 1; return 0; } } if ((++spins & SPINS_PER_YIELD) == 0) SleepEx(0, FALSE); } } static FORCEINLINE void win32_release_lock (MLOCK_T *sl) { assert(sl->threadid == CURRENT_THREAD); assert(sl->l != 0); if (--sl->c == 0) { sl->threadid = 0; interlockedexchange (&sl->l, 0); } } static FORCEINLINE int win32_try_lock (MLOCK_T *sl) { if (sl->l != 0) { if (sl->threadid == CURRENT_THREAD) { ++sl->c; return 1; } } else { if (!interlockedexchange(&sl->l, 1)){ assert(!sl->threadid); sl->threadid = CURRENT_THREAD; sl->c = 1; return 1; } } return 0; } #endif /* WIN32 */ #else /* USE_SPIN_LOCKS */ #ifndef WIN32 /* pthreads-based locks */ #define MLOCK_T pthread_mutex_t #define CURRENT_THREAD pthread_self() #define INITIAL_LOCK(sl) pthread_init_lock(sl) #define ACQUIRE_LOCK(sl) pthread_mutex_lock(sl) #define RELEASE_LOCK(sl) pthread_mutex_unlock(sl) #define TRY_LOCK(sl) (!pthread_mutex_trylock(sl)) static MLOCK_T malloc_global_mutex = PTHREAD_MUTEX_INITIALIZER; /* Cope with old-style linux recursive lock initialization by adding */ /* skipped internal declaration from pthread.h */ #ifdef linux #ifndef PTHREAD_MUTEX_RECURSIVE extern int pthread_mutexattr_setkind_np __P ((pthread_mutexattr_t *__attr, int __kind)); #define PTHREAD_MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE_NP #define pthread_mutexattr_settype(x,y) pthread_mutexattr_setkind_np(x,y) #endif #endif static int pthread_init_lock (MLOCK_T *sl) { pthread_mutexattr_t attr; if (pthread_mutexattr_init(&attr)) return 1; if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) return 1; if (pthread_mutex_init(sl, &attr)) return 1; if (pthread_mutexattr_destroy(&attr)) return 1; return 0; } #else /* WIN32 */ /* Win32 critical sections */ #define MLOCK_T CRITICAL_SECTION #define CURRENT_THREAD GetCurrentThreadId() #define INITIAL_LOCK(s) (!InitializeCriticalSectionAndSpinCount((s), 0x80000000|4000)) #define ACQUIRE_LOCK(s) (EnterCriticalSection(sl), 0) #define RELEASE_LOCK(s) LeaveCriticalSection(sl) #define TRY_LOCK(s) TryEnterCriticalSection(sl) #define NEED_GLOBAL_LOCK_INIT static MLOCK_T malloc_global_mutex; static volatile long malloc_global_mutex_status; /* Use spin loop to initialize global lock */ static void init_malloc_global_mutex() { for (;;) { long stat = malloc_global_mutex_status; if (stat > 0) return; /* transition to < 0 while initializing, then to > 0) */ if (stat == 0 && interlockedcompareexchange(&malloc_global_mutex_status, -1, 0) == 0) { InitializeCriticalSection(&malloc_global_mutex); interlockedexchange(&malloc_global_mutex_status,1); return; } SleepEx(0, FALSE); } } #endif /* WIN32 */ #endif /* USE_SPIN_LOCKS */ #endif /* USE_LOCKS == 1 */ /* ----------------------- User-defined locks ------------------------ */ #if USE_LOCKS > 1 /* Define your own lock implementation here */ /* #define INITIAL_LOCK(sl) ... */ /* #define ACQUIRE_LOCK(sl) ... */ /* #define RELEASE_LOCK(sl) ... */ /* #define TRY_LOCK(sl) ... */ /* static MLOCK_T malloc_global_mutex = ... */ #endif /* USE_LOCKS > 1 */ /* ----------------------- Lock-based state ------------------------ */ #if USE_LOCKS #define USE_LOCK_BIT (2U) #else /* USE_LOCKS */ #define USE_LOCK_BIT (0U) #define INITIAL_LOCK(l) #endif /* USE_LOCKS */ #if USE_LOCKS #ifndef ACQUIRE_MALLOC_GLOBAL_LOCK #define ACQUIRE_MALLOC_GLOBAL_LOCK() ACQUIRE_LOCK(&malloc_global_mutex); #endif #ifndef RELEASE_MALLOC_GLOBAL_LOCK #define RELEASE_MALLOC_GLOBAL_LOCK() RELEASE_LOCK(&malloc_global_mutex); #endif #else /* USE_LOCKS */ #define ACQUIRE_MALLOC_GLOBAL_LOCK() #define RELEASE_MALLOC_GLOBAL_LOCK() #endif /* USE_LOCKS */ /* ----------------------- Chunk representations ------------------------ */ /* (The following includes lightly edited explanations by Colin Plumb.) The malloc_chunk declaration below is misleading (but accurate and necessary). It declares a "view" into memory allowing access to necessary fields at known offsets from a given base. Chunks of memory are maintained using a `boundary tag' method as originally described by Knuth. (See the paper by Paul Wilson ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps for a survey of such techniques.) Sizes of free chunks are stored both in the front of each chunk and at the end. This makes consolidating fragmented chunks into bigger chunks fast. The head fields also hold bits representing whether chunks are free or in use. Here are some pictures to make it clearer. They are "exploded" to show that the state of a chunk can be thought of as extending from the high 31 bits of the head field of its header through the prev_foot and PINUSE_BIT bit of the following chunk header. A chunk that's in use looks like: chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Size of previous chunk (if P = 0) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P| | Size of this chunk 1| +-+ mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | +- -+ | | +- -+ | : +- size - sizeof(size_t) available payload bytes -+ : | chunk-> +- -+ | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1| | Size of next chunk (may or may not be in use) | +-+ mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ And if it's free, it looks like this: chunk-> +- -+ | User payload (must be in use, or we would have merged!) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P| | Size of this chunk 0| +-+ mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Next pointer | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Prev pointer | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | : +- size - sizeof(struct chunk) unused bytes -+ : | chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Size of this chunk | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |0| | Size of next chunk (must be in use, or we would have merged)| +-+ mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | : +- User payload -+ : | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |0| +-+ Note that since we always merge adjacent free chunks, the chunks adjacent to a free chunk must be in use. Given a pointer to a chunk (which can be derived trivially from the payload pointer) we can, in O(1) time, find out whether the adjacent chunks are free, and if so, unlink them from the lists that they are on and merge them with the current chunk. Chunks always begin on even word boundaries, so the mem portion (which is returned to the user) is also on an even word boundary, and thus at least double-word aligned. The P (PINUSE_BIT) bit, stored in the unused low-order bit of the chunk size (which is always a multiple of two words), is an in-use bit for the *previous* chunk. If that bit is *clear*, then the word before the current chunk size contains the previous chunk size, and can be used to find the front of the previous chunk. The very first chunk allocated always has this bit set, preventing access to non-existent (or non-owned) memory. If pinuse is set for any given chunk, then you CANNOT determine the size of the previous chunk, and might even get a memory addressing fault when trying to do so. The C (CINUSE_BIT) bit, stored in the unused second-lowest bit of the chunk size redundantly records whether the current chunk is inuse (unless the chunk is mmapped). This redundancy enables usage checks within free and realloc, and reduces indirection when freeing and consolidating chunks. Each freshly allocated chunk must have both cinuse and pinuse set. That is, each allocated chunk borders either a previously allocated and still in-use chunk, or the base of its memory arena. This is ensured by making all allocations from the the `lowest' part of any found chunk. Further, no free chunk physically borders another one, so each free chunk is known to be preceded and followed by either inuse chunks or the ends of memory. Note that the `foot' of the current chunk is actually represented as the prev_foot of the NEXT chunk. This makes it easier to deal with alignments etc but can be very confusing when trying to extend or adapt this code. The exceptions to all this are 1. The special chunk `top' is the top-most available chunk (i.e., the one bordering the end of available memory). It is treated specially. Top is never included in any bin, is used only if no other chunk is available, and is released back to the system if it is very large (see M_TRIM_THRESHOLD). In effect, the top chunk is treated as larger (and thus less well fitting) than any other available chunk. The top chunk doesn't update its trailing size field since there is no next contiguous chunk that would have to index off it. However, space is still allocated for it (TOP_FOOT_SIZE) to enable separation or merging when space is extended. 3. Chunks allocated via mmap, have both cinuse and pinuse bits cleared in their head fields. Because they are allocated one-by-one, each must carry its own prev_foot field, which is also used to hold the offset this chunk has within its mmapped region, which is needed to preserve alignment. Each mmapped chunk is trailed by the first two fields of a fake next-chunk for sake of usage checks. */ struct malloc_chunk { size_t prev_foot; /* Size of previous chunk (if free). */ size_t head; /* Size and inuse bits. */ struct malloc_chunk* fd; /* double links -- used only if free. */ struct malloc_chunk* bk; }; typedef struct malloc_chunk mchunk; typedef struct malloc_chunk* mchunkptr; typedef struct malloc_chunk* sbinptr; /* The type of bins of chunks */ typedef unsigned int bindex_t; /* Described below */ typedef unsigned int binmap_t; /* Described below */ typedef unsigned int flag_t; /* The type of various bit flag sets */ /* ------------------- Chunks sizes and alignments ----------------------- */ #define MCHUNK_SIZE (sizeof(mchunk)) #if FOOTERS #define CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) #else /* FOOTERS */ #define CHUNK_OVERHEAD (SIZE_T_SIZE) #endif /* FOOTERS */ /* MMapped chunks need a second word of overhead ... */ #define MMAP_CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) /* ... and additional padding for fake next-chunk at foot */ #define MMAP_FOOT_PAD (FOUR_SIZE_T_SIZES) /* The smallest size we can malloc is an aligned minimal chunk */ #define MIN_CHUNK_SIZE\ ((MCHUNK_SIZE + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) /* conversion from malloc headers to user pointers, and back */ #define chunk2mem(p) ((void*)((char*)(p) + TWO_SIZE_T_SIZES)) #define mem2chunk(mem) ((mchunkptr)((char*)(mem) - TWO_SIZE_T_SIZES)) /* chunk associated with aligned address A */ #define align_as_chunk(A) (mchunkptr)((A) + align_offset(chunk2mem(A))) /* Bounds on request (not chunk) sizes. */ #define MAX_REQUEST ((-MIN_CHUNK_SIZE) << 2) #define MIN_REQUEST (MIN_CHUNK_SIZE - CHUNK_OVERHEAD - SIZE_T_ONE) /* pad request bytes into a usable size */ #define pad_request(req) \ (((req) + CHUNK_OVERHEAD + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) /* pad request, checking for minimum (but not maximum) */ #define request2size(req) \ (((req) < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(req)) /* ------------------ Operations on head and foot fields ----------------- */ /* The head field of a chunk is or'ed with PINUSE_BIT when previous adjacent chunk in use, and or'ed with CINUSE_BIT if this chunk is in use, unless mmapped, in which case both bits are cleared. FLAG4_BIT is not used by this malloc, but might be useful in extensions. */ #define PINUSE_BIT (SIZE_T_ONE) #define CINUSE_BIT (SIZE_T_TWO) #define FLAG4_BIT (SIZE_T_FOUR) #define INUSE_BITS (PINUSE_BIT|CINUSE_BIT) #define FLAG_BITS (PINUSE_BIT|CINUSE_BIT|FLAG4_BIT) /* Head value for fenceposts */ #define FENCEPOST_HEAD (INUSE_BITS|SIZE_T_SIZE) /* extraction of fields from head words */ #define cinuse(p) ((p)->head & CINUSE_BIT) #define pinuse(p) ((p)->head & PINUSE_BIT) #define is_inuse(p) (((p)->head & INUSE_BITS) != PINUSE_BIT) #define is_mmapped(p) (((p)->head & INUSE_BITS) == 0) #define chunksize(p) ((p)->head & ~(FLAG_BITS)) #define clear_pinuse(p) ((p)->head &= ~PINUSE_BIT) /* Treat space at ptr +/- offset as a chunk */ #define chunk_plus_offset(p, s) ((mchunkptr)(((char*)(p)) + (s))) #define chunk_minus_offset(p, s) ((mchunkptr)(((char*)(p)) - (s))) /* Ptr to next or previous physical malloc_chunk. */ #define next_chunk(p) ((mchunkptr)( ((char*)(p)) + ((p)->head & ~FLAG_BITS))) #define prev_chunk(p) ((mchunkptr)( ((char*)(p)) - ((p)->prev_foot) )) /* extract next chunk's pinuse bit */ #define next_pinuse(p) ((next_chunk(p)->head) & PINUSE_BIT) /* Get/set size at footer */ #define get_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot) #define set_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot = (s)) /* Set size, pinuse bit, and foot */ #define set_size_and_pinuse_of_free_chunk(p, s)\ ((p)->head = (s|PINUSE_BIT), set_foot(p, s)) /* Set size, pinuse bit, foot, and clear next pinuse */ #define set_free_with_pinuse(p, s, n)\ (clear_pinuse(n), set_size_and_pinuse_of_free_chunk(p, s)) /* Get the internal overhead associated with chunk p */ #define overhead_for(p)\ (is_mmapped(p)? MMAP_CHUNK_OVERHEAD : CHUNK_OVERHEAD) /* Return true if malloced space is not necessarily cleared */ #if MMAP_CLEARS #define calloc_must_clear(p) (!is_mmapped(p)) #else /* MMAP_CLEARS */ #define calloc_must_clear(p) (1) #endif /* MMAP_CLEARS */ /* ---------------------- Overlaid data structures ----------------------- */ /* When chunks are not in use, they are treated as nodes of either lists or trees. "Small" chunks are stored in circular doubly-linked lists, and look like this: chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Size of previous chunk | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ `head:' | Size of chunk, in bytes |P| mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Forward pointer to next chunk in list | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Back pointer to previous chunk in list | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Unused space (may be 0 bytes long) . . . . | nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ `foot:' | Size of chunk, in bytes | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Larger chunks are kept in a form of bitwise digital trees (aka tries) keyed on chunksizes. Because malloc_tree_chunks are only for free chunks greater than 256 bytes, their size doesn't impose any constraints on user chunk sizes. Each node looks like: chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Size of previous chunk | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ `head:' | Size of chunk, in bytes |P| mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Forward pointer to next chunk of same size | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Back pointer to previous chunk of same size | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Pointer to left child (child[0]) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Pointer to right child (child[1]) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Pointer to parent | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | bin index of this chunk | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Unused space . . | nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ `foot:' | Size of chunk, in bytes | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Each tree holding treenodes is a tree of unique chunk sizes. Chunks of the same size are arranged in a circularly-linked list, with only the oldest chunk (the next to be used, in our FIFO ordering) actually in the tree. (Tree members are distinguished by a non-null parent pointer.) If a chunk with the same size an an existing node is inserted, it is linked off the existing node using pointers that work in the same way as fd/bk pointers of small chunks. Each tree contains a power of 2 sized range of chunk sizes (the smallest is 0x100 <= x < 0x180), which is is divided in half at each tree level, with the chunks in the smaller half of the range (0x100 <= x < 0x140 for the top nose) in the left subtree and the larger half (0x140 <= x < 0x180) in the right subtree. This is, of course, done by inspecting individual bits. Using these rules, each node's left subtree contains all smaller sizes than its right subtree. However, the node at the root of each subtree has no particular ordering relationship to either. (The dividing line between the subtree sizes is based on trie relation.) If we remove the last chunk of a given size from the interior of the tree, we need to replace it with a leaf node. The tree ordering rules permit a node to be replaced by any leaf below it. The smallest chunk in a tree (a common operation in a best-fit allocator) can be found by walking a path to the leftmost leaf in the tree. Unlike a usual binary tree, where we follow left child pointers until we reach a null, here we follow the right child pointer any time the left one is null, until we reach a leaf with both child pointers null. The smallest chunk in the tree will be somewhere along that path. The worst case number of steps to add, find, or remove a node is bounded by the number of bits differentiating chunks within bins. Under current bin calculations, this ranges from 6 up to 21 (for 32 bit sizes) or up to 53 (for 64 bit sizes). The typical case is of course much better. */ struct malloc_tree_chunk { /* The first four fields must be compatible with malloc_chunk */ size_t prev_foot; size_t head; struct malloc_tree_chunk* fd; struct malloc_tree_chunk* bk; struct malloc_tree_chunk* child[2]; struct malloc_tree_chunk* parent; bindex_t index; }; typedef struct malloc_tree_chunk tchunk; typedef struct malloc_tree_chunk* tchunkptr; typedef struct malloc_tree_chunk* tbinptr; /* The type of bins of trees */ /* A little helper macro for trees */ #define leftmost_child(t) ((t)->child[0] != 0? (t)->child[0] : (t)->child[1]) /* ----------------------------- Segments -------------------------------- */ /* Each malloc space may include non-contiguous segments, held in a list headed by an embedded malloc_segment record representing the top-most space. Segments also include flags holding properties of the space. Large chunks that are directly allocated by mmap are not included in this list. They are instead independently created and destroyed without otherwise keeping track of them. Segment management mainly comes into play for spaces allocated by MMAP. Any call to MMAP might or might not return memory that is adjacent to an existing segment. MORECORE normally contiguously extends the current space, so this space is almost always adjacent, which is simpler and faster to deal with. (This is why MORECORE is used preferentially to MMAP when both are available -- see sys_alloc.) When allocating using MMAP, we don't use any of the hinting mechanisms (inconsistently) supported in various implementations of unix mmap, or distinguish reserving from committing memory. Instead, we just ask for space, and exploit contiguity when we get it. It is probably possible to do better than this on some systems, but no general scheme seems to be significantly better. Management entails a simpler variant of the consolidation scheme used for chunks to reduce fragmentation -- new adjacent memory is normally prepended or appended to an existing segment. However, there are limitations compared to chunk consolidation that mostly reflect the fact that segment processing is relatively infrequent (occurring only when getting memory from system) and that we don't expect to have huge numbers of segments: * Segments are not indexed, so traversal requires linear scans. (It would be possible to index these, but is not worth the extra overhead and complexity for most programs on most platforms.) * New segments are only appended to old ones when holding top-most memory; if they cannot be prepended to others, they are held in different segments. Except for the top-most segment of an mstate, each segment record is kept at the tail of its segment. Segments are added by pushing segment records onto the list headed by &mstate.seg for the containing mstate. Segment flags control allocation/merge/deallocation policies: * If EXTERN_BIT set, then we did not allocate this segment, and so should not try to deallocate or merge with others. (This currently holds only for the initial segment passed into create_mspace_with_base.) * If USE_MMAP_BIT set, the segment may be merged with other surrounding mmapped segments and trimmed/de-allocated using munmap. * If neither bit is set, then the segment was obtained using MORECORE so can be merged with surrounding MORECORE'd segments and deallocated/trimmed using MORECORE with negative arguments. */ struct malloc_segment { char* base; /* base address */ size_t size; /* allocated size */ struct malloc_segment* next; /* ptr to next segment */ flag_t sflags; /* mmap and extern flag */ }; #define is_mmapped_segment(S) ((S)->sflags & USE_MMAP_BIT) #define is_extern_segment(S) ((S)->sflags & EXTERN_BIT) typedef struct malloc_segment msegment; typedef struct malloc_segment* msegmentptr; /* ---------------------------- malloc_state ----------------------------- */ /* A malloc_state holds all of the bookkeeping for a space. The main fields are: Top The topmost chunk of the currently active segment. Its size is cached in topsize. The actual size of topmost space is topsize+TOP_FOOT_SIZE, which includes space reserved for adding fenceposts and segment records if necessary when getting more space from the system. The size at which to autotrim top is cached from mparams in trim_check, except that it is disabled if an autotrim fails. Designated victim (dv) This is the preferred chunk for servicing small requests that don't have exact fits. It is normally the chunk split off most recently to service another small request. Its size is cached in dvsize. The link fields of this chunk are not maintained since it is not kept in a bin. SmallBins An array of bin headers for free chunks. These bins hold chunks with sizes less than MIN_LARGE_SIZE bytes. Each bin contains chunks of all the same size, spaced 8 bytes apart. To simplify use in double-linked lists, each bin header acts as a malloc_chunk pointing to the real first node, if it exists (else pointing to itself). This avoids special-casing for headers. But to avoid waste, we allocate only the fd/bk pointers of bins, and then use repositioning tricks to treat these as the fields of a chunk. TreeBins Treebins are pointers to the roots of trees holding a range of sizes. There are 2 equally spaced treebins for each power of two from TREE_SHIFT to TREE_SHIFT+16. The last bin holds anything larger. Bin maps There is one bit map for small bins ("smallmap") and one for treebins ("treemap). Each bin sets its bit when non-empty, and clears the bit when empty. Bit operations are then used to avoid bin-by-bin searching -- nearly all "search" is done without ever looking at bins that won't be selected. The bit maps conservatively use 32 bits per map word, even if on 64bit system. For a good description of some of the bit-based techniques used here, see Henry S. Warren Jr's book "Hacker's Delight" (and supplement at http://hackersdelight.org/). Many of these are intended to reduce the branchiness of paths through malloc etc, as well as to reduce the number of memory locations read or written. Segments A list of segments headed by an embedded malloc_segment record representing the initial space. Address check support The least_addr field is the least address ever obtained from MORECORE or MMAP. Attempted frees and reallocs of any address less than this are trapped (unless INSECURE is defined). Magic tag A cross-check field that should always hold same value as mparams.magic. Flags Bits recording whether to use MMAP, locks, or contiguous MORECORE Statistics Each space keeps track of current and maximum system memory obtained via MORECORE or MMAP. Trim support Fields holding the amount of unused topmost memory that should trigger timming, and a counter to force periodic scanning to release unused non-topmost segments. Locking If USE_LOCKS is defined, the "mutex" lock is acquired and released around every public call using this mspace. Extension support A void* pointer and a size_t field that can be used to help implement extensions to this malloc. */ /* Bin types, widths and sizes */ #define NSMALLBINS (32U) #define NTREEBINS (32U) #define SMALLBIN_SHIFT (3U) #define SMALLBIN_WIDTH (SIZE_T_ONE << SMALLBIN_SHIFT) #define TREEBIN_SHIFT (8U) #define MIN_LARGE_SIZE (SIZE_T_ONE << TREEBIN_SHIFT) #define MAX_SMALL_SIZE (MIN_LARGE_SIZE - SIZE_T_ONE) #define MAX_SMALL_REQUEST (MAX_SMALL_SIZE - CHUNK_ALIGN_MASK - CHUNK_OVERHEAD) struct malloc_state { binmap_t smallmap; binmap_t treemap; size_t dvsize; size_t topsize; char* least_addr; mchunkptr dv; mchunkptr top; size_t trim_check; size_t release_checks; size_t magic; mchunkptr smallbins[(NSMALLBINS+1)*2]; tbinptr treebins[NTREEBINS]; size_t footprint; size_t max_footprint; flag_t mflags; #if USE_LOCKS MLOCK_T mutex; /* locate lock among fields that rarely change */ #endif /* USE_LOCKS */ msegment seg; void* extp; /* Unused but available for extensions */ size_t exts; }; typedef struct malloc_state* mstate; /* ------------- Global malloc_state and malloc_params ------------------- */ /* malloc_params holds global properties, including those that can be dynamically set using mallopt. There is a single instance, mparams, initialized in init_mparams. Note that the non-zeroness of "magic" also serves as an initialization flag. */ struct malloc_params { volatile size_t magic; size_t page_size; size_t granularity; size_t mmap_threshold; size_t trim_threshold; flag_t default_mflags; }; static struct malloc_params mparams; /* Ensure mparams initialized */ #define ensure_initialization() (void)(mparams.magic != 0 || init_mparams()) #if !ONLY_MSPACES /* The global malloc_state used for all non-"mspace" calls */ static struct malloc_state _gm_; #define gm (&_gm_) #define is_global(M) ((M) == &_gm_) #endif /* !ONLY_MSPACES */ #define is_initialized(M) ((M)->top != 0) /* -------------------------- system alloc setup ------------------------- */ /* Operations on mflags */ #define use_lock(M) ((M)->mflags & USE_LOCK_BIT) #define enable_lock(M) ((M)->mflags |= USE_LOCK_BIT) #define disable_lock(M) ((M)->mflags &= ~USE_LOCK_BIT) #define use_mmap(M) ((M)->mflags & USE_MMAP_BIT) #define enable_mmap(M) ((M)->mflags |= USE_MMAP_BIT) #define disable_mmap(M) ((M)->mflags &= ~USE_MMAP_BIT) #define use_noncontiguous(M) ((M)->mflags & USE_NONCONTIGUOUS_BIT) #define disable_contiguous(M) ((M)->mflags |= USE_NONCONTIGUOUS_BIT) #define set_lock(M,L)\ ((M)->mflags = (L)?\ ((M)->mflags | USE_LOCK_BIT) :\ ((M)->mflags & ~USE_LOCK_BIT)) /* page-align a size */ #define page_align(S)\ (((S) + (mparams.page_size - SIZE_T_ONE)) & ~(mparams.page_size - SIZE_T_ONE)) /* granularity-align a size */ #define granularity_align(S)\ (((S) + (mparams.granularity - SIZE_T_ONE))\ & ~(mparams.granularity - SIZE_T_ONE)) /* For mmap, use granularity alignment on windows, else page-align */ #ifdef WIN32 #define mmap_align(S) granularity_align(S) #else #define mmap_align(S) page_align(S) #endif /* For sys_alloc, enough padding to ensure can malloc request on success */ #define SYS_ALLOC_PADDING (TOP_FOOT_SIZE + MALLOC_ALIGNMENT) #define is_page_aligned(S)\ (((size_t)(S) & (mparams.page_size - SIZE_T_ONE)) == 0) #define is_granularity_aligned(S)\ (((size_t)(S) & (mparams.granularity - SIZE_T_ONE)) == 0) /* True if segment S holds address A */ #define segment_holds(S, A)\ ((char*)(A) >= S->base && (char*)(A) < S->base + S->size) /* Return segment holding given address */ static msegmentptr segment_holding(mstate m, char* addr) { msegmentptr sp = &m->seg; for (;;) { if (addr >= sp->base && addr < sp->base + sp->size) return sp; if ((sp = sp->next) == 0) return 0; } } /* Return true if segment contains a segment link */ static int has_segment_link(mstate m, msegmentptr ss) { msegmentptr sp = &m->seg; for (;;) { if ((char*)sp >= ss->base && (char*)sp < ss->base + ss->size) return 1; if ((sp = sp->next) == 0) return 0; } } #ifndef MORECORE_CANNOT_TRIM #define should_trim(M,s) ((s) > (M)->trim_check) #else /* MORECORE_CANNOT_TRIM */ #define should_trim(M,s) (0) #endif /* MORECORE_CANNOT_TRIM */ /* TOP_FOOT_SIZE is padding at the end of a segment, including space that may be needed to place segment records and fenceposts when new noncontiguous segments are added. */ #define TOP_FOOT_SIZE\ (align_offset(chunk2mem(0))+pad_request(sizeof(struct malloc_segment))+MIN_CHUNK_SIZE) /* ------------------------------- Hooks -------------------------------- */ /* PREACTION should be defined to return 0 on success, and nonzero on failure. If you are not using locking, you can redefine these to do anything you like. */ #if USE_LOCKS #define PREACTION(M) ((use_lock(M))? ACQUIRE_LOCK(&(M)->mutex) : 0) #define POSTACTION(M) { if (use_lock(M)) RELEASE_LOCK(&(M)->mutex); } #else /* USE_LOCKS */ #ifndef PREACTION #define PREACTION(M) (0) #endif /* PREACTION */ #ifndef POSTACTION #define POSTACTION(M) #endif /* POSTACTION */ #endif /* USE_LOCKS */ /* CORRUPTION_ERROR_ACTION is triggered upon detected bad addresses. USAGE_ERROR_ACTION is triggered on detected bad frees and reallocs. The argument p is an address that might have triggered the fault. It is ignored by the two predefined actions, but might be useful in custom actions that try to help diagnose errors. */ #if PROCEED_ON_ERROR /* A count of the number of corruption errors causing resets */ int malloc_corruption_error_count; /* default corruption action */ static void reset_on_error(mstate m); #define CORRUPTION_ERROR_ACTION(m) reset_on_error(m) #define USAGE_ERROR_ACTION(m, p) #else /* PROCEED_ON_ERROR */ #ifndef CORRUPTION_ERROR_ACTION #define CORRUPTION_ERROR_ACTION(m) ABORT #endif /* CORRUPTION_ERROR_ACTION */ #ifndef USAGE_ERROR_ACTION #define USAGE_ERROR_ACTION(m,p) ABORT #endif /* USAGE_ERROR_ACTION */ #endif /* PROCEED_ON_ERROR */ /* -------------------------- Debugging setup ---------------------------- */ #if ! DEBUG #define check_free_chunk(M,P) #define check_inuse_chunk(M,P) #define check_malloced_chunk(M,P,N) #define check_mmapped_chunk(M,P) #define check_malloc_state(M) #define check_top_chunk(M,P) #else /* DEBUG */ #define check_free_chunk(M,P) do_check_free_chunk(M,P) #define check_inuse_chunk(M,P) do_check_inuse_chunk(M,P) #define check_top_chunk(M,P) do_check_top_chunk(M,P) #define check_malloced_chunk(M,P,N) do_check_malloced_chunk(M,P,N) #define check_mmapped_chunk(M,P) do_check_mmapped_chunk(M,P) #define check_malloc_state(M) do_check_malloc_state(M) static void do_check_any_chunk(mstate m, mchunkptr p); static void do_check_top_chunk(mstate m, mchunkptr p); static void do_check_mmapped_chunk(mstate m, mchunkptr p); static void do_check_inuse_chunk(mstate m, mchunkptr p); static void do_check_free_chunk(mstate m, mchunkptr p); static void do_check_malloced_chunk(mstate m, void* mem, size_t s); static void do_check_tree(mstate m, tchunkptr t); static void do_check_treebin(mstate m, bindex_t i); static void do_check_smallbin(mstate m, bindex_t i); static void do_check_malloc_state(mstate m); static int bin_find(mstate m, mchunkptr x); static size_t traverse_and_check(mstate m); #endif /* DEBUG */ /* ---------------------------- Indexing Bins ---------------------------- */ #define is_small(s) (((s) >> SMALLBIN_SHIFT) < NSMALLBINS) #define small_index(s) ((s) >> SMALLBIN_SHIFT) #define small_index2size(i) ((i) << SMALLBIN_SHIFT) #define MIN_SMALL_INDEX (small_index(MIN_CHUNK_SIZE)) /* addressing by index. See above about smallbin repositioning */ #define smallbin_at(M, i) ((sbinptr)((char*)&((M)->smallbins[(i)<<1]))) #define treebin_at(M,i) (&((M)->treebins[i])) /* assign tree index for size S to variable I. Use x86 asm if possible */ #if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) #define compute_tree_index(S, I)\ {\ unsigned int X = S >> TREEBIN_SHIFT;\ if (X == 0)\ I = 0;\ else if (X > 0xFFFF)\ I = NTREEBINS-1;\ else {\ unsigned int K;\ __asm__("bsrl\t%1, %0\n\t" : "=r" (K) : "g" (X));\ I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ }\ } #elif defined (__INTEL_COMPILER) #define compute_tree_index(S, I)\ {\ size_t X = S >> TREEBIN_SHIFT;\ if (X == 0)\ I = 0;\ else if (X > 0xFFFF)\ I = NTREEBINS-1;\ else {\ unsigned int K = _bit_scan_reverse (X); \ I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ }\ } #elif defined(_MSC_VER) && _MSC_VER>=1300 #define compute_tree_index(S, I)\ {\ size_t X = S >> TREEBIN_SHIFT;\ if (X == 0)\ I = 0;\ else if (X > 0xFFFF)\ I = NTREEBINS-1;\ else {\ unsigned int K;\ _BitScanReverse((DWORD *) &K, X);\ I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ }\ } #else /* GNUC */ #define compute_tree_index(S, I)\ {\ size_t X = S >> TREEBIN_SHIFT;\ if (X == 0)\ I = 0;\ else if (X > 0xFFFF)\ I = NTREEBINS-1;\ else {\ unsigned int Y = (unsigned int)X;\ unsigned int N = ((Y - 0x100) >> 16) & 8;\ unsigned int K = (((Y <<= N) - 0x1000) >> 16) & 4;\ N += K;\ N += K = (((Y <<= K) - 0x4000) >> 16) & 2;\ K = 14 - N + ((Y <<= K) >> 15);\ I = (K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1));\ }\ } #endif /* GNUC */ /* Bit representing maximum resolved size in a treebin at i */ #define bit_for_tree_index(i) \ (i == NTREEBINS-1)? (SIZE_T_BITSIZE-1) : (((i) >> 1) + TREEBIN_SHIFT - 2) /* Shift placing maximum resolved bit in a treebin at i as sign bit */ #define leftshift_for_tree_index(i) \ ((i == NTREEBINS-1)? 0 : \ ((SIZE_T_BITSIZE-SIZE_T_ONE) - (((i) >> 1) + TREEBIN_SHIFT - 2))) /* The size of the smallest chunk held in bin with index i */ #define minsize_for_tree_index(i) \ ((SIZE_T_ONE << (((i) >> 1) + TREEBIN_SHIFT)) | \ (((size_t)((i) & SIZE_T_ONE)) << (((i) >> 1) + TREEBIN_SHIFT - 1))) /* ------------------------ Operations on bin maps ----------------------- */ /* bit corresponding to given index */ #define idx2bit(i) ((binmap_t)(1) << (i)) /* Mark/Clear bits with given index */ #define mark_smallmap(M,i) ((M)->smallmap |= idx2bit(i)) #define clear_smallmap(M,i) ((M)->smallmap &= ~idx2bit(i)) #define smallmap_is_marked(M,i) ((M)->smallmap & idx2bit(i)) #define mark_treemap(M,i) ((M)->treemap |= idx2bit(i)) #define clear_treemap(M,i) ((M)->treemap &= ~idx2bit(i)) #define treemap_is_marked(M,i) ((M)->treemap & idx2bit(i)) /* isolate the least set bit of a bitmap */ #define least_bit(x) ((x) & -(x)) /* mask with all bits to left of least bit of x on */ #define left_bits(x) ((x<<1) | -(x<<1)) /* mask with all bits to left of or equal to least bit of x on */ #define same_or_left_bits(x) ((x) | -(x)) /* index corresponding to given bit. Use x86 asm if possible */ #if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) #define compute_bit2idx(X, I)\ {\ unsigned int J;\ __asm__("bsfl\t%1, %0\n\t" : "=r" (J) : "g" (X));\ I = (bindex_t)J;\ } #elif defined (__INTEL_COMPILER) #define compute_bit2idx(X, I)\ {\ unsigned int J;\ J = _bit_scan_forward (X); \ I = (bindex_t)J;\ } #elif defined(_MSC_VER) && _MSC_VER>=1300 #define compute_bit2idx(X, I)\ {\ unsigned int J;\ _BitScanForward((DWORD *) &J, X);\ I = (bindex_t)J;\ } #elif USE_BUILTIN_FFS #define compute_bit2idx(X, I) I = ffs(X)-1 #else #define compute_bit2idx(X, I)\ {\ unsigned int Y = X - 1;\ unsigned int K = Y >> (16-4) & 16;\ unsigned int N = K; Y >>= K;\ N += K = Y >> (8-3) & 8; Y >>= K;\ N += K = Y >> (4-2) & 4; Y >>= K;\ N += K = Y >> (2-1) & 2; Y >>= K;\ N += K = Y >> (1-0) & 1; Y >>= K;\ I = (bindex_t)(N + Y);\ } #endif /* GNUC */ /* ----------------------- Runtime Check Support ------------------------- */ /* For security, the main invariant is that malloc/free/etc never writes to a static address other than malloc_state, unless static malloc_state itself has been corrupted, which cannot occur via malloc (because of these checks). In essence this means that we believe all pointers, sizes, maps etc held in malloc_state, but check all of those linked or offsetted from other embedded data structures. These checks are interspersed with main code in a way that tends to minimize their run-time cost. When FOOTERS is defined, in addition to range checking, we also verify footer fields of inuse chunks, which can be used guarantee that the mstate controlling malloc/free is intact. This is a streamlined version of the approach described by William Robertson et al in "Run-time Detection of Heap-based Overflows" LISA'03 http://www.usenix.org/events/lisa03/tech/robertson.html The footer of an inuse chunk holds the xor of its mstate and a random seed, that is checked upon calls to free() and realloc(). This is (probablistically) unguessable from outside the program, but can be computed by any code successfully malloc'ing any chunk, so does not itself provide protection against code that has already broken security through some other means. Unlike Robertson et al, we always dynamically check addresses of all offset chunks (previous, next, etc). This turns out to be cheaper than relying on hashes. */ #if !INSECURE /* Check if address a is at least as high as any from MORECORE or MMAP */ #define ok_address(M, a) ((char*)(a) >= (M)->least_addr) /* Check if address of next chunk n is higher than base chunk p */ #define ok_next(p, n) ((char*)(p) < (char*)(n)) /* Check if p has inuse status */ #define ok_inuse(p) is_inuse(p) /* Check if p has its pinuse bit on */ #define ok_pinuse(p) pinuse(p) #else /* !INSECURE */ #define ok_address(M, a) (1) #define ok_next(b, n) (1) #define ok_inuse(p) (1) #define ok_pinuse(p) (1) #endif /* !INSECURE */ #if (FOOTERS && !INSECURE) /* Check if (alleged) mstate m has expected magic field */ #define ok_magic(M) ((M)->magic == mparams.magic) #else /* (FOOTERS && !INSECURE) */ #define ok_magic(M) (1) #endif /* (FOOTERS && !INSECURE) */ /* In gcc, use __builtin_expect to minimize impact of checks */ #if !INSECURE #if defined(__GNUC__) && __GNUC__ >= 3 #define RTCHECK(e) __builtin_expect(e, 1) #else /* GNUC */ #define RTCHECK(e) (e) #endif /* GNUC */ #else /* !INSECURE */ #define RTCHECK(e) (1) #endif /* !INSECURE */ /* macros to set up inuse chunks with or without footers */ #if !FOOTERS #define mark_inuse_foot(M,p,s) /* Macros for setting head/foot of non-mmapped chunks */ /* Set cinuse bit and pinuse bit of next chunk */ #define set_inuse(M,p,s)\ ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT) /* Set cinuse and pinuse of this chunk and pinuse of next chunk */ #define set_inuse_and_pinuse(M,p,s)\ ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT) /* Set size, cinuse and pinuse bit of this chunk */ #define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ ((p)->head = (s|PINUSE_BIT|CINUSE_BIT)) #else /* FOOTERS */ /* Set foot of inuse chunk to be xor of mstate and seed */ #define mark_inuse_foot(M,p,s)\ (((mchunkptr)((char*)(p) + (s)))->prev_foot = ((size_t)(M) ^ mparams.magic)) #define get_mstate_for(p)\ ((mstate)(((mchunkptr)((char*)(p) +\ (chunksize(p))))->prev_foot ^ mparams.magic)) #define set_inuse(M,p,s)\ ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT), \ mark_inuse_foot(M,p,s)) #define set_inuse_and_pinuse(M,p,s)\ ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT),\ mark_inuse_foot(M,p,s)) #define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ mark_inuse_foot(M, p, s)) #endif /* !FOOTERS */ /* ---------------------------- setting mparams -------------------------- */ /* Initialize mparams */ static int init_mparams(void) { #ifdef NEED_GLOBAL_LOCK_INIT if (malloc_global_mutex_status <= 0) init_malloc_global_mutex(); #endif ACQUIRE_MALLOC_GLOBAL_LOCK(); if (mparams.magic == 0) { size_t magic; size_t psize; size_t gsize; #ifndef WIN32 psize = malloc_getpagesize; gsize = ((DEFAULT_GRANULARITY != 0)? DEFAULT_GRANULARITY : psize); #else /* WIN32 */ { SYSTEM_INFO system_info; GetSystemInfo(&system_info); psize = system_info.dwPageSize; gsize = ((DEFAULT_GRANULARITY != 0)? DEFAULT_GRANULARITY : system_info.dwAllocationGranularity); } #endif /* WIN32 */ /* Sanity-check configuration: size_t must be unsigned and as wide as pointer type. ints must be at least 4 bytes. alignment must be at least 8. Alignment, min chunk size, and page size must all be powers of 2. */ if ((sizeof(size_t) != sizeof(char*)) || (MAX_SIZE_T < MIN_CHUNK_SIZE) || (sizeof(int) < 4) || (MALLOC_ALIGNMENT < (size_t)8U) || ((MALLOC_ALIGNMENT & (MALLOC_ALIGNMENT-SIZE_T_ONE)) != 0) || ((MCHUNK_SIZE & (MCHUNK_SIZE-SIZE_T_ONE)) != 0) || ((gsize & (gsize-SIZE_T_ONE)) != 0) || ((psize & (psize-SIZE_T_ONE)) != 0)) ABORT; mparams.granularity = gsize; mparams.page_size = psize; mparams.mmap_threshold = DEFAULT_MMAP_THRESHOLD; mparams.trim_threshold = DEFAULT_TRIM_THRESHOLD; #if MORECORE_CONTIGUOUS mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT; #else /* MORECORE_CONTIGUOUS */ mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT|USE_NONCONTIGUOUS_BIT; #endif /* MORECORE_CONTIGUOUS */ #if !ONLY_MSPACES /* Set up lock for main malloc area */ gm->mflags = mparams.default_mflags; INITIAL_LOCK(&gm->mutex); #endif { #if USE_DEV_RANDOM int fd; unsigned char buf[sizeof(size_t)]; /* Try to use /dev/urandom, else fall back on using time */ if ((fd = open("/dev/urandom", O_RDONLY)) >= 0 && read(fd, buf, sizeof(buf)) == sizeof(buf)) { magic = *((size_t *) buf); close(fd); } else #endif /* USE_DEV_RANDOM */ #ifdef WIN32 magic = (size_t)(GetTickCount() ^ (size_t)0x55555555U); #else magic = (size_t)(time(0) ^ (size_t)0x55555555U); #endif magic |= (size_t)8U; /* ensure nonzero */ magic &= ~(size_t)7U; /* improve chances of fault for bad values */ mparams.magic = magic; } } RELEASE_MALLOC_GLOBAL_LOCK(); return 1; } /* support for mallopt */ static int change_mparam(int param_number, int value) { size_t val; ensure_initialization(); val = (value == -1)? MAX_SIZE_T : (size_t)value; switch(param_number) { case M_TRIM_THRESHOLD: mparams.trim_threshold = val; return 1; case M_GRANULARITY: if (val >= mparams.page_size && ((val & (val-1)) == 0)) { mparams.granularity = val; return 1; } else return 0; case M_MMAP_THRESHOLD: mparams.mmap_threshold = val; return 1; default: return 0; } } #if DEBUG /* ------------------------- Debugging Support --------------------------- */ /* Check properties of any chunk, whether free, inuse, mmapped etc */ static void do_check_any_chunk(mstate m, mchunkptr p) { assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); assert(ok_address(m, p)); } /* Check properties of top chunk */ static void do_check_top_chunk(mstate m, mchunkptr p) { msegmentptr sp = segment_holding(m, (char*)p); size_t sz = p->head & ~INUSE_BITS; /* third-lowest bit can be set! */ assert(sp != 0); assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); assert(ok_address(m, p)); assert(sz == m->topsize); assert(sz > 0); assert(sz == ((sp->base + sp->size) - (char*)p) - TOP_FOOT_SIZE); assert(pinuse(p)); assert(!pinuse(chunk_plus_offset(p, sz))); } /* Check properties of (inuse) mmapped chunks */ static void do_check_mmapped_chunk(mstate m, mchunkptr p) { size_t sz = chunksize(p); size_t len = (sz + (p->prev_foot) + MMAP_FOOT_PAD); assert(is_mmapped(p)); assert(use_mmap(m)); assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); assert(ok_address(m, p)); assert(!is_small(sz)); assert((len & (mparams.page_size-SIZE_T_ONE)) == 0); assert(chunk_plus_offset(p, sz)->head == FENCEPOST_HEAD); assert(chunk_plus_offset(p, sz+SIZE_T_SIZE)->head == 0); } /* Check properties of inuse chunks */ static void do_check_inuse_chunk(mstate m, mchunkptr p) { do_check_any_chunk(m, p); assert(is_inuse(p)); assert(next_pinuse(p)); /* If not pinuse and not mmapped, previous chunk has OK offset */ assert(is_mmapped(p) || pinuse(p) || next_chunk(prev_chunk(p)) == p); if (is_mmapped(p)) do_check_mmapped_chunk(m, p); } /* Check properties of free chunks */ static void do_check_free_chunk(mstate m, mchunkptr p) { size_t sz = chunksize(p); mchunkptr next = chunk_plus_offset(p, sz); do_check_any_chunk(m, p); assert(!is_inuse(p)); assert(!next_pinuse(p)); assert (!is_mmapped(p)); if (p != m->dv && p != m->top) { if (sz >= MIN_CHUNK_SIZE) { assert((sz & CHUNK_ALIGN_MASK) == 0); assert(is_aligned(chunk2mem(p))); assert(next->prev_foot == sz); assert(pinuse(p)); assert (next == m->top || is_inuse(next)); assert(p->fd->bk == p); assert(p->bk->fd == p); } else /* markers are always of size SIZE_T_SIZE */ assert(sz == SIZE_T_SIZE); } } /* Check properties of malloced chunks at the point they are malloced */ static void do_check_malloced_chunk(mstate m, void* mem, size_t s) { if (mem != 0) { mchunkptr p = mem2chunk(mem); size_t sz = p->head & ~INUSE_BITS; do_check_inuse_chunk(m, p); assert((sz & CHUNK_ALIGN_MASK) == 0); assert(sz >= MIN_CHUNK_SIZE); assert(sz >= s); /* unless mmapped, size is less than MIN_CHUNK_SIZE more than request */ assert(is_mmapped(p) || sz < (s + MIN_CHUNK_SIZE)); } } /* Check a tree and its subtrees. */ static void do_check_tree(mstate m, tchunkptr t) { tchunkptr head = 0; tchunkptr u = t; bindex_t tindex = t->index; size_t tsize = chunksize(t); bindex_t idx; compute_tree_index(tsize, idx); assert(tindex == idx); assert(tsize >= MIN_LARGE_SIZE); assert(tsize >= minsize_for_tree_index(idx)); assert((idx == NTREEBINS-1) || (tsize < minsize_for_tree_index((idx+1)))); do { /* traverse through chain of same-sized nodes */ do_check_any_chunk(m, ((mchunkptr)u)); assert(u->index == tindex); assert(chunksize(u) == tsize); assert(!is_inuse(u)); assert(!next_pinuse(u)); assert(u->fd->bk == u); assert(u->bk->fd == u); if (u->parent == 0) { assert(u->child[0] == 0); assert(u->child[1] == 0); } else { assert(head == 0); /* only one node on chain has parent */ head = u; assert(u->parent != u); assert (u->parent->child[0] == u || u->parent->child[1] == u || *((tbinptr*)(u->parent)) == u); if (u->child[0] != 0) { assert(u->child[0]->parent == u); assert(u->child[0] != u); do_check_tree(m, u->child[0]); } if (u->child[1] != 0) { assert(u->child[1]->parent == u); assert(u->child[1] != u); do_check_tree(m, u->child[1]); } if (u->child[0] != 0 && u->child[1] != 0) { assert(chunksize(u->child[0]) < chunksize(u->child[1])); } } u = u->fd; } while (u != t); assert(head != 0); } /* Check all the chunks in a treebin. */ static void do_check_treebin(mstate m, bindex_t i) { tbinptr* tb = treebin_at(m, i); tchunkptr t = *tb; int empty = (m->treemap & (1U << i)) == 0; if (t == 0) assert(empty); if (!empty) do_check_tree(m, t); } /* Check all the chunks in a smallbin. */ static void do_check_smallbin(mstate m, bindex_t i) { sbinptr b = smallbin_at(m, i); mchunkptr p = b->bk; unsigned int empty = (m->smallmap & (1U << i)) == 0; if (p == b) assert(empty); if (!empty) { for (; p != b; p = p->bk) { size_t size = chunksize(p); mchunkptr q; /* each chunk claims to be free */ do_check_free_chunk(m, p); /* chunk belongs in bin */ assert(small_index(size) == i); assert(p->bk == b || chunksize(p->bk) == chunksize(p)); /* chunk is followed by an inuse chunk */ q = next_chunk(p); if (q->head != FENCEPOST_HEAD) do_check_inuse_chunk(m, q); } } } /* Find x in a bin. Used in other check functions. */ static int bin_find(mstate m, mchunkptr x) { size_t size = chunksize(x); if (is_small(size)) { bindex_t sidx = small_index(size); sbinptr b = smallbin_at(m, sidx); if (smallmap_is_marked(m, sidx)) { mchunkptr p = b; do { if (p == x) return 1; } while ((p = p->fd) != b); } } else { bindex_t tidx; compute_tree_index(size, tidx); if (treemap_is_marked(m, tidx)) { tchunkptr t = *treebin_at(m, tidx); size_t sizebits = size << leftshift_for_tree_index(tidx); while (t != 0 && chunksize(t) != size) { t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; sizebits <<= 1; } if (t != 0) { tchunkptr u = t; do { if (u == (tchunkptr)x) return 1; } while ((u = u->fd) != t); } } } return 0; } /* Traverse each chunk and check it; return total */ static size_t traverse_and_check(mstate m) { size_t sum = 0; if (is_initialized(m)) { msegmentptr s = &m->seg; sum += m->topsize + TOP_FOOT_SIZE; while (s != 0) { mchunkptr q = align_as_chunk(s->base); mchunkptr lastq = 0; assert(pinuse(q)); while (segment_holds(s, q) && q != m->top && q->head != FENCEPOST_HEAD) { sum += chunksize(q); if (is_inuse(q)) { assert(!bin_find(m, q)); do_check_inuse_chunk(m, q); } else { assert(q == m->dv || bin_find(m, q)); assert(lastq == 0 || is_inuse(lastq)); /* Not 2 consecutive free */ do_check_free_chunk(m, q); } lastq = q; q = next_chunk(q); } s = s->next; } } return sum; } /* Check all properties of malloc_state. */ static void do_check_malloc_state(mstate m) { bindex_t i; size_t total; /* check bins */ for (i = 0; i < NSMALLBINS; ++i) do_check_smallbin(m, i); for (i = 0; i < NTREEBINS; ++i) do_check_treebin(m, i); if (m->dvsize != 0) { /* check dv chunk */ do_check_any_chunk(m, m->dv); assert(m->dvsize == chunksize(m->dv)); assert(m->dvsize >= MIN_CHUNK_SIZE); assert(bin_find(m, m->dv) == 0); } if (m->top != 0) { /* check top chunk */ do_check_top_chunk(m, m->top); /*assert(m->topsize == chunksize(m->top)); redundant */ assert(m->topsize > 0); assert(bin_find(m, m->top) == 0); } total = traverse_and_check(m); assert(total <= m->footprint); assert(m->footprint <= m->max_footprint); } #endif /* DEBUG */ /* ----------------------------- statistics ------------------------------ */ #if !NO_MALLINFO static struct mallinfo internal_mallinfo(mstate m) { struct mallinfo nm = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; ensure_initialization(); if (!PREACTION(m)) { check_malloc_state(m); if (is_initialized(m)) { size_t nfree = SIZE_T_ONE; /* top always free */ size_t mfree = m->topsize + TOP_FOOT_SIZE; size_t sum = mfree; msegmentptr s = &m->seg; while (s != 0) { mchunkptr q = align_as_chunk(s->base); while (segment_holds(s, q) && q != m->top && q->head != FENCEPOST_HEAD) { size_t sz = chunksize(q); sum += sz; if (!is_inuse(q)) { mfree += sz; ++nfree; } q = next_chunk(q); } s = s->next; } nm.arena = sum; nm.ordblks = nfree; nm.hblkhd = m->footprint - sum; nm.usmblks = m->max_footprint; nm.uordblks = m->footprint - mfree; nm.fordblks = mfree; nm.keepcost = m->topsize; } POSTACTION(m); } return nm; } #endif /* !NO_MALLINFO */ static void internal_malloc_stats(mstate m) { ensure_initialization(); if (!PREACTION(m)) { size_t maxfp = 0; size_t fp = 0; size_t used = 0; check_malloc_state(m); if (is_initialized(m)) { msegmentptr s = &m->seg; maxfp = m->max_footprint; fp = m->footprint; used = fp - (m->topsize + TOP_FOOT_SIZE); while (s != 0) { mchunkptr q = align_as_chunk(s->base); while (segment_holds(s, q) && q != m->top && q->head != FENCEPOST_HEAD) { if (!is_inuse(q)) used -= chunksize(q); q = next_chunk(q); } s = s->next; } } fprintf(stderr, "max system bytes = %10lu\n", (unsigned long)(maxfp)); fprintf(stderr, "system bytes = %10lu\n", (unsigned long)(fp)); fprintf(stderr, "in use bytes = %10lu\n", (unsigned long)(used)); POSTACTION(m); } } /* ----------------------- Operations on smallbins ----------------------- */ /* Various forms of linking and unlinking are defined as macros. Even the ones for trees, which are very long but have very short typical paths. This is ugly but reduces reliance on inlining support of compilers. */ /* Link a free chunk into a smallbin */ #define insert_small_chunk(M, P, S) {\ bindex_t I = small_index(S);\ mchunkptr B = smallbin_at(M, I);\ mchunkptr F = B;\ assert(S >= MIN_CHUNK_SIZE);\ if (!smallmap_is_marked(M, I))\ mark_smallmap(M, I);\ else if (RTCHECK(ok_address(M, B->fd)))\ F = B->fd;\ else {\ CORRUPTION_ERROR_ACTION(M);\ }\ B->fd = P;\ F->bk = P;\ P->fd = F;\ P->bk = B;\ } /* Unlink a chunk from a smallbin */ #define unlink_small_chunk(M, P, S) {\ mchunkptr F = P->fd;\ mchunkptr B = P->bk;\ bindex_t I = small_index(S);\ assert(P != B);\ assert(P != F);\ assert(chunksize(P) == small_index2size(I));\ if (F == B)\ clear_smallmap(M, I);\ else if (RTCHECK((F == smallbin_at(M,I) || ok_address(M, F)) &&\ (B == smallbin_at(M,I) || ok_address(M, B)))) {\ F->bk = B;\ B->fd = F;\ }\ else {\ CORRUPTION_ERROR_ACTION(M);\ }\ } /* Unlink the first chunk from a smallbin */ #define unlink_first_small_chunk(M, B, P, I) {\ mchunkptr F = P->fd;\ assert(P != B);\ assert(P != F);\ assert(chunksize(P) == small_index2size(I));\ if (B == F)\ clear_smallmap(M, I);\ else if (RTCHECK(ok_address(M, F))) {\ B->fd = F;\ F->bk = B;\ }\ else {\ CORRUPTION_ERROR_ACTION(M);\ }\ } /* Replace dv node, binning the old one */ /* Used only when dvsize known to be small */ #define replace_dv(M, P, S) {\ size_t DVS = M->dvsize;\ if (DVS != 0) {\ mchunkptr DV = M->dv;\ assert(is_small(DVS));\ insert_small_chunk(M, DV, DVS);\ }\ M->dvsize = S;\ M->dv = P;\ } /* ------------------------- Operations on trees ------------------------- */ /* Insert chunk into tree */ #define insert_large_chunk(M, X, S) {\ tbinptr* H;\ bindex_t I;\ compute_tree_index(S, I);\ H = treebin_at(M, I);\ X->index = I;\ X->child[0] = X->child[1] = 0;\ if (!treemap_is_marked(M, I)) {\ mark_treemap(M, I);\ *H = X;\ X->parent = (tchunkptr)H;\ X->fd = X->bk = X;\ }\ else {\ tchunkptr T = *H;\ size_t K = S << leftshift_for_tree_index(I);\ for (;;) {\ if (chunksize(T) != S) {\ tchunkptr* C = &(T->child[(K >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]);\ K <<= 1;\ if (*C != 0)\ T = *C;\ else if (RTCHECK(ok_address(M, C))) {\ *C = X;\ X->parent = T;\ X->fd = X->bk = X;\ break;\ }\ else {\ CORRUPTION_ERROR_ACTION(M);\ break;\ }\ }\ else {\ tchunkptr F = T->fd;\ if (RTCHECK(ok_address(M, T) && ok_address(M, F))) {\ T->fd = F->bk = X;\ X->fd = F;\ X->bk = T;\ X->parent = 0;\ break;\ }\ else {\ CORRUPTION_ERROR_ACTION(M);\ break;\ }\ }\ }\ }\ } /* Unlink steps: 1. If x is a chained node, unlink it from its same-sized fd/bk links and choose its bk node as its replacement. 2. If x was the last node of its size, but not a leaf node, it must be replaced with a leaf node (not merely one with an open left or right), to make sure that lefts and rights of descendents correspond properly to bit masks. We use the rightmost descendent of x. We could use any other leaf, but this is easy to locate and tends to counteract removal of leftmosts elsewhere, and so keeps paths shorter than minimally guaranteed. This doesn't loop much because on average a node in a tree is near the bottom. 3. If x is the base of a chain (i.e., has parent links) relink x's parent and children to x's replacement (or null if none). */ #define unlink_large_chunk(M, X) {\ tchunkptr XP = X->parent;\ tchunkptr R;\ if (X->bk != X) {\ tchunkptr F = X->fd;\ R = X->bk;\ if (RTCHECK(ok_address(M, F))) {\ F->bk = R;\ R->fd = F;\ }\ else {\ CORRUPTION_ERROR_ACTION(M);\ }\ }\ else {\ tchunkptr* RP;\ if (((R = *(RP = &(X->child[1]))) != 0) ||\ ((R = *(RP = &(X->child[0]))) != 0)) {\ tchunkptr* CP;\ while ((*(CP = &(R->child[1])) != 0) ||\ (*(CP = &(R->child[0])) != 0)) {\ R = *(RP = CP);\ }\ if (RTCHECK(ok_address(M, RP)))\ *RP = 0;\ else {\ CORRUPTION_ERROR_ACTION(M);\ }\ }\ }\ if (XP != 0) {\ tbinptr* H = treebin_at(M, X->index);\ if (X == *H) {\ if ((*H = R) == 0) \ clear_treemap(M, X->index);\ }\ else if (RTCHECK(ok_address(M, XP))) {\ if (XP->child[0] == X) \ XP->child[0] = R;\ else \ XP->child[1] = R;\ }\ else\ CORRUPTION_ERROR_ACTION(M);\ if (R != 0) {\ if (RTCHECK(ok_address(M, R))) {\ tchunkptr C0, C1;\ R->parent = XP;\ if ((C0 = X->child[0]) != 0) {\ if (RTCHECK(ok_address(M, C0))) {\ R->child[0] = C0;\ C0->parent = R;\ }\ else\ CORRUPTION_ERROR_ACTION(M);\ }\ if ((C1 = X->child[1]) != 0) {\ if (RTCHECK(ok_address(M, C1))) {\ R->child[1] = C1;\ C1->parent = R;\ }\ else\ CORRUPTION_ERROR_ACTION(M);\ }\ }\ else\ CORRUPTION_ERROR_ACTION(M);\ }\ }\ } /* Relays to large vs small bin operations */ #define insert_chunk(M, P, S)\ if (is_small(S)) insert_small_chunk(M, P, S)\ else { tchunkptr TP = (tchunkptr)(P); insert_large_chunk(M, TP, S); } #define unlink_chunk(M, P, S)\ if (is_small(S)) unlink_small_chunk(M, P, S)\ else { tchunkptr TP = (tchunkptr)(P); unlink_large_chunk(M, TP); } /* Relays to internal calls to malloc/free from realloc, memalign etc */ #if ONLY_MSPACES #define internal_malloc(m, b) mspace_malloc(m, b) #define internal_free(m, mem) mspace_free(m,mem); #else /* ONLY_MSPACES */ #if MSPACES #define internal_malloc(m, b)\ (m == gm)? dlmalloc(b) : mspace_malloc(m, b) #define internal_free(m, mem)\ if (m == gm) dlfree(mem); else mspace_free(m,mem); #else /* MSPACES */ #define internal_malloc(m, b) dlmalloc(b) #define internal_free(m, mem) dlfree(mem) #endif /* MSPACES */ #endif /* ONLY_MSPACES */ /* ----------------------- Direct-mmapping chunks ----------------------- */ /* Directly mmapped chunks are set up with an offset to the start of the mmapped region stored in the prev_foot field of the chunk. This allows reconstruction of the required argument to MUNMAP when freed, and also allows adjustment of the returned chunk to meet alignment requirements (especially in memalign). */ /* Malloc using mmap */ static void* mmap_alloc(mstate m, size_t nb) { size_t mmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); if (mmsize > nb) { /* Check for wrap around 0 */ char* mm = (char*)(CALL_DIRECT_MMAP(mmsize)); if (mm != CMFAIL) { size_t offset = align_offset(chunk2mem(mm)); size_t psize = mmsize - offset - MMAP_FOOT_PAD; mchunkptr p = (mchunkptr)(mm + offset); p->prev_foot = offset; p->head = psize; mark_inuse_foot(m, p, psize); chunk_plus_offset(p, psize)->head = FENCEPOST_HEAD; chunk_plus_offset(p, psize+SIZE_T_SIZE)->head = 0; if (m->least_addr == 0 || mm < m->least_addr) m->least_addr = mm; if ((m->footprint += mmsize) > m->max_footprint) m->max_footprint = m->footprint; assert(is_aligned(chunk2mem(p))); check_mmapped_chunk(m, p); return chunk2mem(p); } } return 0; } /* Realloc using mmap */ static mchunkptr mmap_resize(mstate m, mchunkptr oldp, size_t nb) { size_t oldsize = chunksize(oldp); if (is_small(nb)) /* Can't shrink mmap regions below small size */ return 0; /* Keep old chunk if big enough but not too big */ if (oldsize >= nb + SIZE_T_SIZE && (oldsize - nb) <= (mparams.granularity << 1)) return oldp; else { size_t offset = oldp->prev_foot; size_t oldmmsize = oldsize + offset + MMAP_FOOT_PAD; size_t newmmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); char* cp = (char*)CALL_MREMAP((char*)oldp - offset, oldmmsize, newmmsize, 1); if (cp != CMFAIL) { mchunkptr newp = (mchunkptr)(cp + offset); size_t psize = newmmsize - offset - MMAP_FOOT_PAD; newp->head = psize; mark_inuse_foot(m, newp, psize); chunk_plus_offset(newp, psize)->head = FENCEPOST_HEAD; chunk_plus_offset(newp, psize+SIZE_T_SIZE)->head = 0; if (cp < m->least_addr) m->least_addr = cp; if ((m->footprint += newmmsize - oldmmsize) > m->max_footprint) m->max_footprint = m->footprint; check_mmapped_chunk(m, newp); return newp; } } return 0; } /* -------------------------- mspace management -------------------------- */ /* Initialize top chunk and its size */ static void init_top(mstate m, mchunkptr p, size_t psize) { /* Ensure alignment */ size_t offset = align_offset(chunk2mem(p)); p = (mchunkptr)((char*)p + offset); psize -= offset; m->top = p; m->topsize = psize; p->head = psize | PINUSE_BIT; /* set size of fake trailing chunk holding overhead space only once */ chunk_plus_offset(p, psize)->head = TOP_FOOT_SIZE; m->trim_check = mparams.trim_threshold; /* reset on each update */ } /* Initialize bins for a new mstate that is otherwise zeroed out */ static void init_bins(mstate m) { /* Establish circular links for smallbins */ bindex_t i; for (i = 0; i < NSMALLBINS; ++i) { sbinptr bin = smallbin_at(m,i); bin->fd = bin->bk = bin; } } #if PROCEED_ON_ERROR /* default corruption action */ static void reset_on_error(mstate m) { int i; ++malloc_corruption_error_count; /* Reinitialize fields to forget about all memory */ m->smallbins = m->treebins = 0; m->dvsize = m->topsize = 0; m->seg.base = 0; m->seg.size = 0; m->seg.next = 0; m->top = m->dv = 0; for (i = 0; i < NTREEBINS; ++i) *treebin_at(m, i) = 0; init_bins(m); } #endif /* PROCEED_ON_ERROR */ /* Allocate chunk and prepend remainder with chunk in successor base. */ static void* prepend_alloc(mstate m, char* newbase, char* oldbase, size_t nb) { mchunkptr p = align_as_chunk(newbase); mchunkptr oldfirst = align_as_chunk(oldbase); size_t psize = (char*)oldfirst - (char*)p; mchunkptr q = chunk_plus_offset(p, nb); size_t qsize = psize - nb; set_size_and_pinuse_of_inuse_chunk(m, p, nb); assert((char*)oldfirst > (char*)q); assert(pinuse(oldfirst)); assert(qsize >= MIN_CHUNK_SIZE); /* consolidate remainder with first chunk of old base */ if (oldfirst == m->top) { size_t tsize = m->topsize += qsize; m->top = q; q->head = tsize | PINUSE_BIT; check_top_chunk(m, q); } else if (oldfirst == m->dv) { size_t dsize = m->dvsize += qsize; m->dv = q; set_size_and_pinuse_of_free_chunk(q, dsize); } else { if (!is_inuse(oldfirst)) { size_t nsize = chunksize(oldfirst); unlink_chunk(m, oldfirst, nsize); oldfirst = chunk_plus_offset(oldfirst, nsize); qsize += nsize; } set_free_with_pinuse(q, qsize, oldfirst); insert_chunk(m, q, qsize); check_free_chunk(m, q); } check_malloced_chunk(m, chunk2mem(p), nb); return chunk2mem(p); } /* Add a segment to hold a new noncontiguous region */ static void add_segment(mstate m, char* tbase, size_t tsize, flag_t mmapped) { /* Determine locations and sizes of segment, fenceposts, old top */ char* old_top = (char*)m->top; msegmentptr oldsp = segment_holding(m, old_top); char* old_end = oldsp->base + oldsp->size; size_t ssize = pad_request(sizeof(struct malloc_segment)); char* rawsp = old_end - (ssize + FOUR_SIZE_T_SIZES + CHUNK_ALIGN_MASK); size_t offset = align_offset(chunk2mem(rawsp)); char* asp = rawsp + offset; char* csp = (asp < (old_top + MIN_CHUNK_SIZE))? old_top : asp; mchunkptr sp = (mchunkptr)csp; msegmentptr ss = (msegmentptr)(chunk2mem(sp)); mchunkptr tnext = chunk_plus_offset(sp, ssize); mchunkptr p = tnext; int nfences = 0; /* reset top to new space */ init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); /* Set up segment record */ assert(is_aligned(ss)); set_size_and_pinuse_of_inuse_chunk(m, sp, ssize); *ss = m->seg; /* Push current record */ m->seg.base = tbase; m->seg.size = tsize; m->seg.sflags = mmapped; m->seg.next = ss; /* Insert trailing fenceposts */ for (;;) { mchunkptr nextp = chunk_plus_offset(p, SIZE_T_SIZE); p->head = FENCEPOST_HEAD; ++nfences; if ((char*)(&(nextp->head)) < old_end) p = nextp; else break; } assert(nfences >= 2); /* Insert the rest of old top into a bin as an ordinary free chunk */ if (csp != old_top) { mchunkptr q = (mchunkptr)old_top; size_t psize = csp - old_top; mchunkptr tn = chunk_plus_offset(q, psize); set_free_with_pinuse(q, psize, tn); insert_chunk(m, q, psize); } check_top_chunk(m, m->top); } /* -------------------------- System allocation -------------------------- */ /* Get memory from system using MORECORE or MMAP */ static void* sys_alloc(mstate m, size_t nb) { char* tbase = CMFAIL; size_t tsize = 0; flag_t mmap_flag = 0; ensure_initialization(); /* Directly map large chunks, but only if already initialized */ if (use_mmap(m) && nb >= mparams.mmap_threshold && m->topsize != 0) { void* mem = mmap_alloc(m, nb); if (mem != 0) return mem; } /* Try getting memory in any of three ways (in most-preferred to least-preferred order): 1. A call to MORECORE that can normally contiguously extend memory. (disabled if not MORECORE_CONTIGUOUS or not HAVE_MORECORE or or main space is mmapped or a previous contiguous call failed) 2. A call to MMAP new space (disabled if not HAVE_MMAP). Note that under the default settings, if MORECORE is unable to fulfill a request, and HAVE_MMAP is true, then mmap is used as a noncontiguous system allocator. This is a useful backup strategy for systems with holes in address spaces -- in this case sbrk cannot contiguously expand the heap, but mmap may be able to find space. 3. A call to MORECORE that cannot usually contiguously extend memory. (disabled if not HAVE_MORECORE) In all cases, we need to request enough bytes from system to ensure we can malloc nb bytes upon success, so pad with enough space for top_foot, plus alignment-pad to make sure we don't lose bytes if not on boundary, and round this up to a granularity unit. */ if (MORECORE_CONTIGUOUS && !use_noncontiguous(m)) { char* br = CMFAIL; msegmentptr ss = (m->top == 0)? 0 : segment_holding(m, (char*)m->top); size_t asize = 0; ACQUIRE_MALLOC_GLOBAL_LOCK(); if (ss == 0) { /* First time through or recovery */ char* base = (char*)CALL_MORECORE(0); if (base != CMFAIL) { asize = granularity_align(nb + SYS_ALLOC_PADDING); /* Adjust to end on a page boundary */ if (!is_page_aligned(base)) asize += (page_align((size_t)base) - (size_t)base); /* Can't call MORECORE if size is negative when treated as signed */ if (asize < HALF_MAX_SIZE_T && (br = (char*)(CALL_MORECORE(asize))) == base) { tbase = base; tsize = asize; } } } else { /* Subtract out existing available top space from MORECORE request. */ asize = granularity_align(nb - m->topsize + SYS_ALLOC_PADDING); /* Use mem here only if it did continuously extend old space */ if (asize < HALF_MAX_SIZE_T && (br = (char*)(CALL_MORECORE(asize))) == ss->base+ss->size) { tbase = br; tsize = asize; } } if (tbase == CMFAIL) { /* Cope with partial failure */ if (br != CMFAIL) { /* Try to use/extend the space we did get */ if (asize < HALF_MAX_SIZE_T && asize < nb + SYS_ALLOC_PADDING) { size_t esize = granularity_align(nb + SYS_ALLOC_PADDING - asize); if (esize < HALF_MAX_SIZE_T) { char* end = (char*)CALL_MORECORE(esize); if (end != CMFAIL) asize += esize; else { /* Can't use; try to release */ (void) CALL_MORECORE(-asize); br = CMFAIL; } } } } if (br != CMFAIL) { /* Use the space we did get */ tbase = br; tsize = asize; } else disable_contiguous(m); /* Don't try contiguous path in the future */ } RELEASE_MALLOC_GLOBAL_LOCK(); } if (HAVE_MMAP && tbase == CMFAIL) { /* Try MMAP */ size_t rsize = granularity_align(nb + SYS_ALLOC_PADDING); if (rsize > nb) { /* Fail if wraps around zero */ char* mp = (char*)(CALL_MMAP(rsize)); if (mp != CMFAIL) { tbase = mp; tsize = rsize; mmap_flag = USE_MMAP_BIT; } } } if (HAVE_MORECORE && tbase == CMFAIL) { /* Try noncontiguous MORECORE */ size_t asize = granularity_align(nb + SYS_ALLOC_PADDING); if (asize < HALF_MAX_SIZE_T) { char* br = CMFAIL; char* end = CMFAIL; ACQUIRE_MALLOC_GLOBAL_LOCK(); br = (char*)(CALL_MORECORE(asize)); end = (char*)(CALL_MORECORE(0)); RELEASE_MALLOC_GLOBAL_LOCK(); if (br != CMFAIL && end != CMFAIL && br < end) { size_t ssize = end - br; if (ssize > nb + TOP_FOOT_SIZE) { tbase = br; tsize = ssize; } } } } if (tbase != CMFAIL) { if ((m->footprint += tsize) > m->max_footprint) m->max_footprint = m->footprint; if (!is_initialized(m)) { /* first-time initialization */ if (m->least_addr == 0 || tbase < m->least_addr) m->least_addr = tbase; m->seg.base = tbase; m->seg.size = tsize; m->seg.sflags = mmap_flag; m->magic = mparams.magic; m->release_checks = MAX_RELEASE_CHECK_RATE; init_bins(m); #if !ONLY_MSPACES if (is_global(m)) init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); else #endif { /* Offset top by embedded malloc_state */ mchunkptr mn = next_chunk(mem2chunk(m)); init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) -TOP_FOOT_SIZE); } } else { /* Try to merge with an existing segment */ msegmentptr sp = &m->seg; /* Only consider most recent segment if traversal suppressed */ while (sp != 0 && tbase != sp->base + sp->size) sp = (NO_SEGMENT_TRAVERSAL) ? 0 : sp->next; if (sp != 0 && !is_extern_segment(sp) && (sp->sflags & USE_MMAP_BIT) == mmap_flag && segment_holds(sp, m->top)) { /* append */ sp->size += tsize; init_top(m, m->top, m->topsize + tsize); } else { if (tbase < m->least_addr) m->least_addr = tbase; sp = &m->seg; while (sp != 0 && sp->base != tbase + tsize) sp = (NO_SEGMENT_TRAVERSAL) ? 0 : sp->next; if (sp != 0 && !is_extern_segment(sp) && (sp->sflags & USE_MMAP_BIT) == mmap_flag) { char* oldbase = sp->base; sp->base = tbase; sp->size += tsize; return prepend_alloc(m, tbase, oldbase, nb); } else add_segment(m, tbase, tsize, mmap_flag); } } if (nb < m->topsize) { /* Allocate from new or extended top space */ size_t rsize = m->topsize -= nb; mchunkptr p = m->top; mchunkptr r = m->top = chunk_plus_offset(p, nb); r->head = rsize | PINUSE_BIT; set_size_and_pinuse_of_inuse_chunk(m, p, nb); check_top_chunk(m, m->top); check_malloced_chunk(m, chunk2mem(p), nb); return chunk2mem(p); } } MALLOC_FAILURE_ACTION; return 0; } /* ----------------------- system deallocation -------------------------- */ /* Unmap and unlink any mmapped segments that don't contain used chunks */ static size_t release_unused_segments(mstate m) { size_t released = 0; int nsegs = 0; msegmentptr pred = &m->seg; msegmentptr sp = pred->next; while (sp != 0) { char* base = sp->base; size_t size = sp->size; msegmentptr next = sp->next; ++nsegs; if (is_mmapped_segment(sp) && !is_extern_segment(sp)) { mchunkptr p = align_as_chunk(base); size_t psize = chunksize(p); /* Can unmap if first chunk holds entire segment and not pinned */ if (!is_inuse(p) && (char*)p + psize >= base + size - TOP_FOOT_SIZE) { tchunkptr tp = (tchunkptr)p; assert(segment_holds(sp, (char*)sp)); if (p == m->dv) { m->dv = 0; m->dvsize = 0; } else { unlink_large_chunk(m, tp); } if (CALL_MUNMAP(base, size) == 0) { released += size; m->footprint -= size; /* unlink obsoleted record */ sp = pred; sp->next = next; } else { /* back out if cannot unmap */ insert_large_chunk(m, tp, psize); } } } if (NO_SEGMENT_TRAVERSAL) /* scan only first segment */ break; pred = sp; sp = next; } /* Reset check counter */ m->release_checks = ((nsegs > MAX_RELEASE_CHECK_RATE)? nsegs : MAX_RELEASE_CHECK_RATE); return released; } static int sys_trim(mstate m, size_t pad) { size_t released = 0; ensure_initialization(); if (pad < MAX_REQUEST && is_initialized(m)) { pad += TOP_FOOT_SIZE; /* ensure enough room for segment overhead */ if (m->topsize > pad) { /* Shrink top space in granularity-size units, keeping at least one */ size_t unit = mparams.granularity; size_t extra = ((m->topsize - pad + (unit - SIZE_T_ONE)) / unit - SIZE_T_ONE) * unit; msegmentptr sp = segment_holding(m, (char*)m->top); if (!is_extern_segment(sp)) { if (is_mmapped_segment(sp)) { if (HAVE_MMAP && sp->size >= extra && !has_segment_link(m, sp)) { /* can't shrink if pinned */ size_t newsize = sp->size - extra; /* Prefer mremap, fall back to munmap */ if ((CALL_MREMAP(sp->base, sp->size, newsize, 0) != MFAIL) || (CALL_MUNMAP(sp->base + newsize, extra) == 0)) { released = extra; } } } else if (HAVE_MORECORE) { if (extra >= HALF_MAX_SIZE_T) /* Avoid wrapping negative */ extra = (HALF_MAX_SIZE_T) + SIZE_T_ONE - unit; ACQUIRE_MALLOC_GLOBAL_LOCK(); { /* Make sure end of memory is where we last set it. */ char* old_br = (char*)(CALL_MORECORE(0)); if (old_br == sp->base + sp->size) { char* rel_br = (char*)(CALL_MORECORE(-extra)); char* new_br = (char*)(CALL_MORECORE(0)); if (rel_br != CMFAIL && new_br < old_br) released = old_br - new_br; } } RELEASE_MALLOC_GLOBAL_LOCK(); } } if (released != 0) { sp->size -= released; m->footprint -= released; init_top(m, m->top, m->topsize - released); check_top_chunk(m, m->top); } } /* Unmap any unused mmapped segments */ if (HAVE_MMAP) released += release_unused_segments(m); /* On failure, disable autotrim to avoid repeated failed future calls */ if (released == 0 && m->topsize > m->trim_check) m->trim_check = MAX_SIZE_T; } return (released != 0)? 1 : 0; } /* ---------------------------- malloc support --------------------------- */ /* allocate a large request from the best fitting chunk in a treebin */ static void* tmalloc_large(mstate m, size_t nb) { tchunkptr v = 0; size_t rsize = -nb; /* Unsigned negation */ tchunkptr t; bindex_t idx; compute_tree_index(nb, idx); if ((t = *treebin_at(m, idx)) != 0) { /* Traverse tree for this bin looking for node with size == nb */ size_t sizebits = nb << leftshift_for_tree_index(idx); tchunkptr rst = 0; /* The deepest untaken right subtree */ for (;;) { tchunkptr rt; size_t trem = chunksize(t) - nb; if (trem < rsize) { v = t; if ((rsize = trem) == 0) break; } rt = t->child[1]; t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; if (rt != 0 && rt != t) rst = rt; if (t == 0) { t = rst; /* set t to least subtree holding sizes > nb */ break; } sizebits <<= 1; } } if (t == 0 && v == 0) { /* set t to root of next non-empty treebin */ binmap_t leftbits = left_bits(idx2bit(idx)) & m->treemap; if (leftbits != 0) { bindex_t i; binmap_t leastbit = least_bit(leftbits); compute_bit2idx(leastbit, i); t = *treebin_at(m, i); } } while (t != 0) { /* find smallest of tree or subtree */ size_t trem = chunksize(t) - nb; if (trem < rsize) { rsize = trem; v = t; } t = leftmost_child(t); } /* If dv is a better fit, return 0 so malloc will use it */ if (v != 0 && rsize < (size_t)(m->dvsize - nb)) { if (RTCHECK(ok_address(m, v))) { /* split */ mchunkptr r = chunk_plus_offset(v, nb); assert(chunksize(v) == rsize + nb); if (RTCHECK(ok_next(v, r))) { unlink_large_chunk(m, v); if (rsize < MIN_CHUNK_SIZE) set_inuse_and_pinuse(m, v, (rsize + nb)); else { set_size_and_pinuse_of_inuse_chunk(m, v, nb); set_size_and_pinuse_of_free_chunk(r, rsize); insert_chunk(m, r, rsize); } return chunk2mem(v); } } CORRUPTION_ERROR_ACTION(m); } return 0; } /* allocate a small request from the best fitting chunk in a treebin */ static void* tmalloc_small(mstate m, size_t nb) { tchunkptr t, v; size_t rsize; bindex_t i; binmap_t leastbit = least_bit(m->treemap); compute_bit2idx(leastbit, i); v = t = *treebin_at(m, i); rsize = chunksize(t) - nb; while ((t = leftmost_child(t)) != 0) { size_t trem = chunksize(t) - nb; if (trem < rsize) { rsize = trem; v = t; } } if (RTCHECK(ok_address(m, v))) { mchunkptr r = chunk_plus_offset(v, nb); assert(chunksize(v) == rsize + nb); if (RTCHECK(ok_next(v, r))) { unlink_large_chunk(m, v); if (rsize < MIN_CHUNK_SIZE) set_inuse_and_pinuse(m, v, (rsize + nb)); else { set_size_and_pinuse_of_inuse_chunk(m, v, nb); set_size_and_pinuse_of_free_chunk(r, rsize); replace_dv(m, r, rsize); } return chunk2mem(v); } } CORRUPTION_ERROR_ACTION(m); return 0; } /* --------------------------- realloc support --------------------------- */ static void* internal_realloc(mstate m, void* oldmem, size_t bytes) { if (bytes >= MAX_REQUEST) { MALLOC_FAILURE_ACTION; return 0; } if (!PREACTION(m)) { mchunkptr oldp = mem2chunk(oldmem); size_t oldsize = chunksize(oldp); mchunkptr next = chunk_plus_offset(oldp, oldsize); mchunkptr newp = 0; void* extra = 0; /* Try to either shrink or extend into top. Else malloc-copy-free */ if (RTCHECK(ok_address(m, oldp) && ok_inuse(oldp) && ok_next(oldp, next) && ok_pinuse(next))) { size_t nb = request2size(bytes); if (is_mmapped(oldp)) newp = mmap_resize(m, oldp, nb); else if (oldsize >= nb) { /* already big enough */ size_t rsize = oldsize - nb; newp = oldp; if (rsize >= MIN_CHUNK_SIZE) { mchunkptr remainder = chunk_plus_offset(newp, nb); set_inuse(m, newp, nb); set_inuse_and_pinuse(m, remainder, rsize); extra = chunk2mem(remainder); } } else if (next == m->top && oldsize + m->topsize > nb) { /* Expand into top */ size_t newsize = oldsize + m->topsize; size_t newtopsize = newsize - nb; mchunkptr newtop = chunk_plus_offset(oldp, nb); set_inuse(m, oldp, nb); newtop->head = newtopsize |PINUSE_BIT; m->top = newtop; m->topsize = newtopsize; newp = oldp; } } else { USAGE_ERROR_ACTION(m, oldmem); POSTACTION(m); return 0; } #if DEBUG if (newp != 0) { check_inuse_chunk(m, newp); /* Check requires lock */ } #endif POSTACTION(m); if (newp != 0) { if (extra != 0) { internal_free(m, extra); } return chunk2mem(newp); } else { void* newmem = internal_malloc(m, bytes); if (newmem != 0) { size_t oc = oldsize - overhead_for(oldp); memcpy(newmem, oldmem, (oc < bytes)? oc : bytes); internal_free(m, oldmem); } return newmem; } } return 0; } /* --------------------------- memalign support -------------------------- */ static void* internal_memalign(mstate m, size_t alignment, size_t bytes) { if (alignment <= MALLOC_ALIGNMENT) /* Can just use malloc */ return internal_malloc(m, bytes); if (alignment < MIN_CHUNK_SIZE) /* must be at least a minimum chunk size */ alignment = MIN_CHUNK_SIZE; if ((alignment & (alignment-SIZE_T_ONE)) != 0) {/* Ensure a power of 2 */ size_t a = MALLOC_ALIGNMENT << 1; while (a < alignment) a <<= 1; alignment = a; } if (bytes >= MAX_REQUEST - alignment) { if (m != 0) { /* Test isn't needed but avoids compiler warning */ MALLOC_FAILURE_ACTION; } } else { size_t nb = request2size(bytes); size_t req = nb + alignment + MIN_CHUNK_SIZE - CHUNK_OVERHEAD; char* mem = (char*)internal_malloc(m, req); if (mem != 0) { void* leader = 0; void* trailer = 0; mchunkptr p = mem2chunk(mem); if (PREACTION(m)) return 0; if ((((size_t)(mem)) % alignment) != 0) { /* misaligned */ /* Find an aligned spot inside chunk. Since we need to give back leading space in a chunk of at least MIN_CHUNK_SIZE, if the first calculation places us at a spot with less than MIN_CHUNK_SIZE leader, we can move to the next aligned spot. We've allocated enough total room so that this is always possible. */ char* br = (char*)mem2chunk((size_t)(((size_t)(mem + alignment - SIZE_T_ONE)) & -alignment)); char* pos = ((size_t)(br - (char*)(p)) >= MIN_CHUNK_SIZE)? br : br+alignment; mchunkptr newp = (mchunkptr)pos; size_t leadsize = pos - (char*)(p); size_t newsize = chunksize(p) - leadsize; if (is_mmapped(p)) { /* For mmapped chunks, just adjust offset */ newp->prev_foot = p->prev_foot + leadsize; newp->head = newsize; } else { /* Otherwise, give back leader, use the rest */ set_inuse(m, newp, newsize); set_inuse(m, p, leadsize); leader = chunk2mem(p); } p = newp; } /* Give back spare room at the end */ if (!is_mmapped(p)) { size_t size = chunksize(p); if (size > nb + MIN_CHUNK_SIZE) { size_t remainder_size = size - nb; mchunkptr remainder = chunk_plus_offset(p, nb); set_inuse(m, p, nb); set_inuse(m, remainder, remainder_size); trailer = chunk2mem(remainder); } } assert (chunksize(p) >= nb); assert((((size_t)(chunk2mem(p))) % alignment) == 0); check_inuse_chunk(m, p); POSTACTION(m); if (leader != 0) { internal_free(m, leader); } if (trailer != 0) { internal_free(m, trailer); } return chunk2mem(p); } } return 0; } /* ------------------------ comalloc/coalloc support --------------------- */ static void** ialloc(mstate m, size_t n_elements, size_t* sizes, int opts, void* chunks[]) { /* This provides common support for independent_X routines, handling all of the combinations that can result. The opts arg has: bit 0 set if all elements are same size (using sizes[0]) bit 1 set if elements should be zeroed */ size_t element_size; /* chunksize of each element, if all same */ size_t contents_size; /* total size of elements */ size_t array_size; /* request size of pointer array */ void* mem; /* malloced aggregate space */ mchunkptr p; /* corresponding chunk */ size_t remainder_size; /* remaining bytes while splitting */ void** marray; /* either "chunks" or malloced ptr array */ mchunkptr array_chunk; /* chunk for malloced ptr array */ flag_t was_enabled; /* to disable mmap */ size_t size; size_t i; ensure_initialization(); /* compute array length, if needed */ if (chunks != 0) { if (n_elements == 0) return chunks; /* nothing to do */ marray = chunks; array_size = 0; } else { /* if empty req, must still return chunk representing empty array */ if (n_elements == 0) return (void**)internal_malloc(m, 0); marray = 0; array_size = request2size(n_elements * (sizeof(void*))); } /* compute total element size */ if (opts & 0x1) { /* all-same-size */ element_size = request2size(*sizes); contents_size = n_elements * element_size; } else { /* add up all the sizes */ element_size = 0; contents_size = 0; for (i = 0; i != n_elements; ++i) contents_size += request2size(sizes[i]); } size = contents_size + array_size; /* Allocate the aggregate chunk. First disable direct-mmapping so malloc won't use it, since we would not be able to later free/realloc space internal to a segregated mmap region. */ was_enabled = use_mmap(m); disable_mmap(m); mem = internal_malloc(m, size - CHUNK_OVERHEAD); if (was_enabled) enable_mmap(m); if (mem == 0) return 0; if (PREACTION(m)) return 0; p = mem2chunk(mem); remainder_size = chunksize(p); assert(!is_mmapped(p)); if (opts & 0x2) { /* optionally clear the elements */ memset((size_t*)mem, 0, remainder_size - SIZE_T_SIZE - array_size); } /* If not provided, allocate the pointer array as final part of chunk */ if (marray == 0) { size_t array_chunk_size; array_chunk = chunk_plus_offset(p, contents_size); array_chunk_size = remainder_size - contents_size; marray = (void**) (chunk2mem(array_chunk)); set_size_and_pinuse_of_inuse_chunk(m, array_chunk, array_chunk_size); remainder_size = contents_size; } /* split out elements */ for (i = 0; ; ++i) { marray[i] = chunk2mem(p); if (i != n_elements-1) { if (element_size != 0) size = element_size; else size = request2size(sizes[i]); remainder_size -= size; set_size_and_pinuse_of_inuse_chunk(m, p, size); p = chunk_plus_offset(p, size); } else { /* the final element absorbs any overallocation slop */ set_size_and_pinuse_of_inuse_chunk(m, p, remainder_size); break; } } #if DEBUG if (marray != chunks) { /* final element must have exactly exhausted chunk */ if (element_size != 0) { assert(remainder_size == element_size); } else { assert(remainder_size == request2size(sizes[i])); } check_inuse_chunk(m, mem2chunk(marray)); } for (i = 0; i != n_elements; ++i) check_inuse_chunk(m, mem2chunk(marray[i])); #endif /* DEBUG */ POSTACTION(m); return marray; } /* -------------------------- public routines ---------------------------- */ #if !ONLY_MSPACES void* dlmalloc(size_t bytes) { /* Basic algorithm: If a small request (< 256 bytes minus per-chunk overhead): 1. If one exists, use a remainderless chunk in associated smallbin. (Remainderless means that there are too few excess bytes to represent as a chunk.) 2. If it is big enough, use the dv chunk, which is normally the chunk adjacent to the one used for the most recent small request. 3. If one exists, split the smallest available chunk in a bin, saving remainder in dv. 4. If it is big enough, use the top chunk. 5. If available, get memory from system and use it Otherwise, for a large request: 1. Find the smallest available binned chunk that fits, and use it if it is better fitting than dv chunk, splitting if necessary. 2. If better fitting than any binned chunk, use the dv chunk. 3. If it is big enough, use the top chunk. 4. If request size >= mmap threshold, try to directly mmap this chunk. 5. If available, get memory from system and use it The ugly goto's here ensure that postaction occurs along all paths. */ #if USE_LOCKS ensure_initialization(); /* initialize in sys_alloc if not using locks */ #endif if (!PREACTION(gm)) { void* mem; size_t nb; if (bytes <= MAX_SMALL_REQUEST) { bindex_t idx; binmap_t smallbits; nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes); idx = small_index(nb); smallbits = gm->smallmap >> idx; if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ mchunkptr b, p; idx += ~smallbits & 1; /* Uses next bin if idx empty */ b = smallbin_at(gm, idx); p = b->fd; assert(chunksize(p) == small_index2size(idx)); unlink_first_small_chunk(gm, b, p, idx); set_inuse_and_pinuse(gm, p, small_index2size(idx)); mem = chunk2mem(p); check_malloced_chunk(gm, mem, nb); goto postaction; } else if (nb > gm->dvsize) { if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ mchunkptr b, p, r; size_t rsize; bindex_t i; binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); binmap_t leastbit = least_bit(leftbits); compute_bit2idx(leastbit, i); b = smallbin_at(gm, i); p = b->fd; assert(chunksize(p) == small_index2size(i)); unlink_first_small_chunk(gm, b, p, i); rsize = small_index2size(i) - nb; /* Fit here cannot be remainderless if 4byte sizes */ if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) set_inuse_and_pinuse(gm, p, small_index2size(i)); else { set_size_and_pinuse_of_inuse_chunk(gm, p, nb); r = chunk_plus_offset(p, nb); set_size_and_pinuse_of_free_chunk(r, rsize); replace_dv(gm, r, rsize); } mem = chunk2mem(p); check_malloced_chunk(gm, mem, nb); goto postaction; } else if (gm->treemap != 0 && (mem = tmalloc_small(gm, nb)) != 0) { check_malloced_chunk(gm, mem, nb); goto postaction; } } } else if (bytes >= MAX_REQUEST) nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ else { nb = pad_request(bytes); if (gm->treemap != 0 && (mem = tmalloc_large(gm, nb)) != 0) { check_malloced_chunk(gm, mem, nb); goto postaction; } } if (nb <= gm->dvsize) { size_t rsize = gm->dvsize - nb; mchunkptr p = gm->dv; if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ mchunkptr r = gm->dv = chunk_plus_offset(p, nb); gm->dvsize = rsize; set_size_and_pinuse_of_free_chunk(r, rsize); set_size_and_pinuse_of_inuse_chunk(gm, p, nb); } else { /* exhaust dv */ size_t dvs = gm->dvsize; gm->dvsize = 0; gm->dv = 0; set_inuse_and_pinuse(gm, p, dvs); } mem = chunk2mem(p); check_malloced_chunk(gm, mem, nb); goto postaction; } else if (nb < gm->topsize) { /* Split top */ size_t rsize = gm->topsize -= nb; mchunkptr p = gm->top; mchunkptr r = gm->top = chunk_plus_offset(p, nb); r->head = rsize | PINUSE_BIT; set_size_and_pinuse_of_inuse_chunk(gm, p, nb); mem = chunk2mem(p); check_top_chunk(gm, gm->top); check_malloced_chunk(gm, mem, nb); goto postaction; } mem = sys_alloc(gm, nb); postaction: POSTACTION(gm); return mem; } return 0; } void dlfree(void* mem) { /* Consolidate freed chunks with preceeding or succeeding bordering free chunks, if they exist, and then place in a bin. Intermixed with special cases for top, dv, mmapped chunks, and usage errors. */ if (mem != 0) { mchunkptr p = mem2chunk(mem); #if FOOTERS mstate fm = get_mstate_for(p); if (!ok_magic(fm)) { USAGE_ERROR_ACTION(fm, p); return; } #else /* FOOTERS */ #define fm gm #endif /* FOOTERS */ if (!PREACTION(fm)) { check_inuse_chunk(fm, p); if (RTCHECK(ok_address(fm, p) && ok_inuse(p))) { size_t psize = chunksize(p); mchunkptr next = chunk_plus_offset(p, psize); if (!pinuse(p)) { size_t prevsize = p->prev_foot; if (is_mmapped(p)) { psize += prevsize + MMAP_FOOT_PAD; if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) fm->footprint -= psize; goto postaction; } else { mchunkptr prev = chunk_minus_offset(p, prevsize); psize += prevsize; p = prev; if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ if (p != fm->dv) { unlink_chunk(fm, p, prevsize); } else if ((next->head & INUSE_BITS) == INUSE_BITS) { fm->dvsize = psize; set_free_with_pinuse(p, psize, next); goto postaction; } } else goto erroraction; } } if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { if (!cinuse(next)) { /* consolidate forward */ if (next == fm->top) { size_t tsize = fm->topsize += psize; fm->top = p; p->head = tsize | PINUSE_BIT; if (p == fm->dv) { fm->dv = 0; fm->dvsize = 0; } if (should_trim(fm, tsize)) sys_trim(fm, 0); goto postaction; } else if (next == fm->dv) { size_t dsize = fm->dvsize += psize; fm->dv = p; set_size_and_pinuse_of_free_chunk(p, dsize); goto postaction; } else { size_t nsize = chunksize(next); psize += nsize; unlink_chunk(fm, next, nsize); set_size_and_pinuse_of_free_chunk(p, psize); if (p == fm->dv) { fm->dvsize = psize; goto postaction; } } } else set_free_with_pinuse(p, psize, next); if (is_small(psize)) { insert_small_chunk(fm, p, psize); check_free_chunk(fm, p); } else { tchunkptr tp = (tchunkptr)p; insert_large_chunk(fm, tp, psize); check_free_chunk(fm, p); if (--fm->release_checks == 0) release_unused_segments(fm); } goto postaction; } } erroraction: USAGE_ERROR_ACTION(fm, p); postaction: POSTACTION(fm); } } #if !FOOTERS #undef fm #endif /* FOOTERS */ } void* dlcalloc(size_t n_elements, size_t elem_size) { void* mem; size_t req = 0; if (n_elements != 0) { req = n_elements * elem_size; if (((n_elements | elem_size) & ~(size_t)0xffff) && (req / n_elements != elem_size)) req = MAX_SIZE_T; /* force downstream failure on overflow */ } mem = dlmalloc(req); if (mem != 0 && calloc_must_clear(mem2chunk(mem))) memset(mem, 0, req); return mem; } void* dlrealloc(void* oldmem, size_t bytes) { if (oldmem == 0) return dlmalloc(bytes); #ifdef REALLOC_ZERO_BYTES_FREES if (bytes == 0) { dlfree(oldmem); return 0; } #endif /* REALLOC_ZERO_BYTES_FREES */ else { #if ! FOOTERS mstate m = gm; #else /* FOOTERS */ mstate m = get_mstate_for(mem2chunk(oldmem)); if (!ok_magic(m)) { USAGE_ERROR_ACTION(m, oldmem); return 0; } #endif /* FOOTERS */ return internal_realloc(m, oldmem, bytes); } } void* dlmemalign(size_t alignment, size_t bytes) { return internal_memalign(gm, alignment, bytes); } void** dlindependent_calloc(size_t n_elements, size_t elem_size, void* chunks[]) { size_t sz = elem_size; /* serves as 1-element array */ return ialloc(gm, n_elements, &sz, 3, chunks); } void** dlindependent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]) { return ialloc(gm, n_elements, sizes, 0, chunks); } void* dlvalloc(size_t bytes) { size_t pagesz; ensure_initialization(); pagesz = mparams.page_size; return dlmemalign(pagesz, bytes); } void* dlpvalloc(size_t bytes) { size_t pagesz; ensure_initialization(); pagesz = mparams.page_size; return dlmemalign(pagesz, (bytes + pagesz - SIZE_T_ONE) & ~(pagesz - SIZE_T_ONE)); } int dlmalloc_trim(size_t pad) { int result = 0; ensure_initialization(); if (!PREACTION(gm)) { result = sys_trim(gm, pad); POSTACTION(gm); } return result; } size_t dlmalloc_footprint(void) { return gm->footprint; } size_t dlmalloc_max_footprint(void) { return gm->max_footprint; } #if !NO_MALLINFO struct mallinfo dlmallinfo(void) { return internal_mallinfo(gm); } #endif /* NO_MALLINFO */ void dlmalloc_stats() { internal_malloc_stats(gm); } int dlmallopt(int param_number, int value) { return change_mparam(param_number, value); } #endif /* !ONLY_MSPACES */ size_t dlmalloc_usable_size(void* mem) { if (mem != 0) { mchunkptr p = mem2chunk(mem); if (is_inuse(p)) return chunksize(p) - overhead_for(p); } return 0; } /* ----------------------------- user mspaces ---------------------------- */ #if MSPACES static mstate init_user_mstate(char* tbase, size_t tsize) { size_t msize = pad_request(sizeof(struct malloc_state)); mchunkptr mn; mchunkptr msp = align_as_chunk(tbase); mstate m = (mstate)(chunk2mem(msp)); memset(m, 0, msize); INITIAL_LOCK(&m->mutex); msp->head = (msize|INUSE_BITS); m->seg.base = m->least_addr = tbase; m->seg.size = m->footprint = m->max_footprint = tsize; m->magic = mparams.magic; m->release_checks = MAX_RELEASE_CHECK_RATE; m->mflags = mparams.default_mflags; m->extp = 0; m->exts = 0; disable_contiguous(m); init_bins(m); mn = next_chunk(mem2chunk(m)); init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) - TOP_FOOT_SIZE); check_top_chunk(m, m->top); return m; } mspace create_mspace(size_t capacity, int locked) { mstate m = 0; size_t msize; ensure_initialization(); msize = pad_request(sizeof(struct malloc_state)); if (capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) { size_t rs = ((capacity == 0)? mparams.granularity : (capacity + TOP_FOOT_SIZE + msize)); size_t tsize = granularity_align(rs); char* tbase = (char*)(CALL_MMAP(tsize)); if (tbase != CMFAIL) { m = init_user_mstate(tbase, tsize); m->seg.sflags = USE_MMAP_BIT; set_lock(m, locked); } } return (mspace)m; } mspace create_mspace_with_base(void* base, size_t capacity, int locked) { mstate m = 0; size_t msize; ensure_initialization(); msize = pad_request(sizeof(struct malloc_state)); if (capacity > msize + TOP_FOOT_SIZE && capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) { m = init_user_mstate((char*)base, capacity); m->seg.sflags = EXTERN_BIT; set_lock(m, locked); } return (mspace)m; } int mspace_track_large_chunks(mspace msp, int enable) { int ret = 0; mstate ms = (mstate)msp; if (!PREACTION(ms)) { if (!use_mmap(ms)) ret = 1; if (!enable) enable_mmap(ms); else disable_mmap(ms); POSTACTION(ms); } return ret; } size_t destroy_mspace(mspace msp) { size_t freed = 0; mstate ms = (mstate)msp; if (ok_magic(ms)) { msegmentptr sp = &ms->seg; while (sp != 0) { char* base = sp->base; size_t size = sp->size; flag_t flag = sp->sflags; sp = sp->next; if ((flag & USE_MMAP_BIT) && !(flag & EXTERN_BIT) && CALL_MUNMAP(base, size) == 0) freed += size; } } else { USAGE_ERROR_ACTION(ms,ms); } return freed; } /* mspace versions of routines are near-clones of the global versions. This is not so nice but better than the alternatives. */ void* mspace_malloc(mspace msp, size_t bytes) { mstate ms = (mstate)msp; if (!ok_magic(ms)) { USAGE_ERROR_ACTION(ms,ms); return 0; } if (!PREACTION(ms)) { void* mem; size_t nb; if (bytes <= MAX_SMALL_REQUEST) { bindex_t idx; binmap_t smallbits; nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes); idx = small_index(nb); smallbits = ms->smallmap >> idx; if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ mchunkptr b, p; idx += ~smallbits & 1; /* Uses next bin if idx empty */ b = smallbin_at(ms, idx); p = b->fd; assert(chunksize(p) == small_index2size(idx)); unlink_first_small_chunk(ms, b, p, idx); set_inuse_and_pinuse(ms, p, small_index2size(idx)); mem = chunk2mem(p); check_malloced_chunk(ms, mem, nb); goto postaction; } else if (nb > ms->dvsize) { if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ mchunkptr b, p, r; size_t rsize; bindex_t i; binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); binmap_t leastbit = least_bit(leftbits); compute_bit2idx(leastbit, i); b = smallbin_at(ms, i); p = b->fd; assert(chunksize(p) == small_index2size(i)); unlink_first_small_chunk(ms, b, p, i); rsize = small_index2size(i) - nb; /* Fit here cannot be remainderless if 4byte sizes */ if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) set_inuse_and_pinuse(ms, p, small_index2size(i)); else { set_size_and_pinuse_of_inuse_chunk(ms, p, nb); r = chunk_plus_offset(p, nb); set_size_and_pinuse_of_free_chunk(r, rsize); replace_dv(ms, r, rsize); } mem = chunk2mem(p); check_malloced_chunk(ms, mem, nb); goto postaction; } else if (ms->treemap != 0 && (mem = tmalloc_small(ms, nb)) != 0) { check_malloced_chunk(ms, mem, nb); goto postaction; } } } else if (bytes >= MAX_REQUEST) nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ else { nb = pad_request(bytes); if (ms->treemap != 0 && (mem = tmalloc_large(ms, nb)) != 0) { check_malloced_chunk(ms, mem, nb); goto postaction; } } if (nb <= ms->dvsize) { size_t rsize = ms->dvsize - nb; mchunkptr p = ms->dv; if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ mchunkptr r = ms->dv = chunk_plus_offset(p, nb); ms->dvsize = rsize; set_size_and_pinuse_of_free_chunk(r, rsize); set_size_and_pinuse_of_inuse_chunk(ms, p, nb); } else { /* exhaust dv */ size_t dvs = ms->dvsize; ms->dvsize = 0; ms->dv = 0; set_inuse_and_pinuse(ms, p, dvs); } mem = chunk2mem(p); check_malloced_chunk(ms, mem, nb); goto postaction; } else if (nb < ms->topsize) { /* Split top */ size_t rsize = ms->topsize -= nb; mchunkptr p = ms->top; mchunkptr r = ms->top = chunk_plus_offset(p, nb); r->head = rsize | PINUSE_BIT; set_size_and_pinuse_of_inuse_chunk(ms, p, nb); mem = chunk2mem(p); check_top_chunk(ms, ms->top); check_malloced_chunk(ms, mem, nb); goto postaction; } mem = sys_alloc(ms, nb); postaction: POSTACTION(ms); return mem; } return 0; } void mspace_free(mspace msp, void* mem) { if (mem != 0) { mchunkptr p = mem2chunk(mem); #if FOOTERS mstate fm = get_mstate_for(p); msp = msp; /* placate people compiling -Wunused */ #else /* FOOTERS */ mstate fm = (mstate)msp; #endif /* FOOTERS */ if (!ok_magic(fm)) { USAGE_ERROR_ACTION(fm, p); return; } if (!PREACTION(fm)) { check_inuse_chunk(fm, p); if (RTCHECK(ok_address(fm, p) && ok_inuse(p))) { size_t psize = chunksize(p); mchunkptr next = chunk_plus_offset(p, psize); if (!pinuse(p)) { size_t prevsize = p->prev_foot; if (is_mmapped(p)) { psize += prevsize + MMAP_FOOT_PAD; if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) fm->footprint -= psize; goto postaction; } else { mchunkptr prev = chunk_minus_offset(p, prevsize); psize += prevsize; p = prev; if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ if (p != fm->dv) { unlink_chunk(fm, p, prevsize); } else if ((next->head & INUSE_BITS) == INUSE_BITS) { fm->dvsize = psize; set_free_with_pinuse(p, psize, next); goto postaction; } } else goto erroraction; } } if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { if (!cinuse(next)) { /* consolidate forward */ if (next == fm->top) { size_t tsize = fm->topsize += psize; fm->top = p; p->head = tsize | PINUSE_BIT; if (p == fm->dv) { fm->dv = 0; fm->dvsize = 0; } if (should_trim(fm, tsize)) sys_trim(fm, 0); goto postaction; } else if (next == fm->dv) { size_t dsize = fm->dvsize += psize; fm->dv = p; set_size_and_pinuse_of_free_chunk(p, dsize); goto postaction; } else { size_t nsize = chunksize(next); psize += nsize; unlink_chunk(fm, next, nsize); set_size_and_pinuse_of_free_chunk(p, psize); if (p == fm->dv) { fm->dvsize = psize; goto postaction; } } } else set_free_with_pinuse(p, psize, next); if (is_small(psize)) { insert_small_chunk(fm, p, psize); check_free_chunk(fm, p); } else { tchunkptr tp = (tchunkptr)p; insert_large_chunk(fm, tp, psize); check_free_chunk(fm, p); if (--fm->release_checks == 0) release_unused_segments(fm); } goto postaction; } } erroraction: USAGE_ERROR_ACTION(fm, p); postaction: POSTACTION(fm); } } } void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size) { void* mem; size_t req = 0; mstate ms = (mstate)msp; if (!ok_magic(ms)) { USAGE_ERROR_ACTION(ms,ms); return 0; } if (n_elements != 0) { req = n_elements * elem_size; if (((n_elements | elem_size) & ~(size_t)0xffff) && (req / n_elements != elem_size)) req = MAX_SIZE_T; /* force downstream failure on overflow */ } mem = internal_malloc(ms, req); if (mem != 0 && calloc_must_clear(mem2chunk(mem))) memset(mem, 0, req); return mem; } void* mspace_realloc(mspace msp, void* oldmem, size_t bytes) { if (oldmem == 0) return mspace_malloc(msp, bytes); #ifdef REALLOC_ZERO_BYTES_FREES if (bytes == 0) { mspace_free(msp, oldmem); return 0; } #endif /* REALLOC_ZERO_BYTES_FREES */ else { #if FOOTERS mchunkptr p = mem2chunk(oldmem); mstate ms = get_mstate_for(p); #else /* FOOTERS */ mstate ms = (mstate)msp; #endif /* FOOTERS */ if (!ok_magic(ms)) { USAGE_ERROR_ACTION(ms,ms); return 0; } return internal_realloc(ms, oldmem, bytes); } } void* mspace_memalign(mspace msp, size_t alignment, size_t bytes) { mstate ms = (mstate)msp; if (!ok_magic(ms)) { USAGE_ERROR_ACTION(ms,ms); return 0; } return internal_memalign(ms, alignment, bytes); } void** mspace_independent_calloc(mspace msp, size_t n_elements, size_t elem_size, void* chunks[]) { size_t sz = elem_size; /* serves as 1-element array */ mstate ms = (mstate)msp; if (!ok_magic(ms)) { USAGE_ERROR_ACTION(ms,ms); return 0; } return ialloc(ms, n_elements, &sz, 3, chunks); } void** mspace_independent_comalloc(mspace msp, size_t n_elements, size_t sizes[], void* chunks[]) { mstate ms = (mstate)msp; if (!ok_magic(ms)) { USAGE_ERROR_ACTION(ms,ms); return 0; } return ialloc(ms, n_elements, sizes, 0, chunks); } int mspace_trim(mspace msp, size_t pad) { int result = 0; mstate ms = (mstate)msp; if (ok_magic(ms)) { if (!PREACTION(ms)) { result = sys_trim(ms, pad); POSTACTION(ms); } } else { USAGE_ERROR_ACTION(ms,ms); } return result; } void mspace_malloc_stats(mspace msp) { mstate ms = (mstate)msp; if (ok_magic(ms)) { internal_malloc_stats(ms); } else { USAGE_ERROR_ACTION(ms,ms); } } size_t mspace_footprint(mspace msp) { size_t result = 0; mstate ms = (mstate)msp; if (ok_magic(ms)) { result = ms->footprint; } else { USAGE_ERROR_ACTION(ms,ms); } return result; } size_t mspace_max_footprint(mspace msp) { size_t result = 0; mstate ms = (mstate)msp; if (ok_magic(ms)) { result = ms->max_footprint; } else { USAGE_ERROR_ACTION(ms,ms); } return result; } #if !NO_MALLINFO struct mallinfo mspace_mallinfo(mspace msp) { mstate ms = (mstate)msp; if (!ok_magic(ms)) { USAGE_ERROR_ACTION(ms,ms); } return internal_mallinfo(ms); } #endif /* NO_MALLINFO */ size_t mspace_usable_size(void* mem) { if (mem != 0) { mchunkptr p = mem2chunk(mem); if (is_inuse(p)) return chunksize(p) - overhead_for(p); } return 0; } int mspace_mallopt(int param_number, int value) { return change_mparam(param_number, value); } #endif /* MSPACES */ /* -------------------- Alternative MORECORE functions ------------------- */ /* Guidelines for creating a custom version of MORECORE: * For best performance, MORECORE should allocate in multiples of pagesize. * MORECORE may allocate more memory than requested. (Or even less, but this will usually result in a malloc failure.) * MORECORE must not allocate memory when given argument zero, but instead return one past the end address of memory from previous nonzero call. * For best performance, consecutive calls to MORECORE with positive arguments should return increasing addresses, indicating that space has been contiguously extended. * Even though consecutive calls to MORECORE need not return contiguous addresses, it must be OK for malloc'ed chunks to span multiple regions in those cases where they do happen to be contiguous. * MORECORE need not handle negative arguments -- it may instead just return MFAIL when given negative arguments. Negative arguments are always multiples of pagesize. MORECORE must not misinterpret negative args as large positive unsigned args. You can suppress all such calls from even occurring by defining MORECORE_CANNOT_TRIM, As an example alternative MORECORE, here is a custom allocator kindly contributed for pre-OSX macOS. It uses virtually but not necessarily physically contiguous non-paged memory (locked in, present and won't get swapped out). You can use it by uncommenting this section, adding some #includes, and setting up the appropriate defines above: #define MORECORE osMoreCore There is also a shutdown routine that should somehow be called for cleanup upon program exit. #define MAX_POOL_ENTRIES 100 #define MINIMUM_MORECORE_SIZE (64 * 1024U) static int next_os_pool; void *our_os_pools[MAX_POOL_ENTRIES]; void *osMoreCore(int size) { void *ptr = 0; static void *sbrk_top = 0; if (size > 0) { if (size < MINIMUM_MORECORE_SIZE) size = MINIMUM_MORECORE_SIZE; if (CurrentExecutionLevel() == kTaskLevel) ptr = PoolAllocateResident(size + RM_PAGE_SIZE, 0); if (ptr == 0) { return (void *) MFAIL; } // save ptrs so they can be freed during cleanup our_os_pools[next_os_pool] = ptr; next_os_pool++; ptr = (void *) ((((size_t) ptr) + RM_PAGE_MASK) & ~RM_PAGE_MASK); sbrk_top = (char *) ptr + size; return ptr; } else if (size < 0) { // we don't currently support shrink behavior return (void *) MFAIL; } else { return sbrk_top; } } // cleanup any allocated memory pools // called as last thing before shutting down driver void osCleanupMem(void) { void **ptr; for (ptr = our_os_pools; ptr < &our_os_pools[MAX_POOL_ENTRIES]; ptr++) if (*ptr) { PoolDeallocate(*ptr); *ptr = 0; } } */ /* ----------------------------------------------------------------------- History: V2.8.4 Wed May 27 09:56:23 2009 Doug Lea (dl at gee) * Use zeros instead of prev foot for is_mmapped * Add mspace_track_large_chunks; thanks to Jean Brouwers * Fix set_inuse in internal_realloc; thanks to Jean Brouwers * Fix insufficient sys_alloc padding when using 16byte alignment * Fix bad error check in mspace_footprint * Adaptations for ptmalloc; thanks to Wolfram Gloger. * Reentrant spin locks; thanks to Earl Chew and others * Win32 improvements; thanks to Niall Douglas and Earl Chew * Add NO_SEGMENT_TRAVERSAL and MAX_RELEASE_CHECK_RATE options * Extension hook in malloc_state * Various small adjustments to reduce warnings on some compilers * Various configuration extensions/changes for more platforms. Thanks to all who contributed these. V2.8.3 Thu Sep 22 11:16:32 2005 Doug Lea (dl at gee) * Add max_footprint functions * Ensure all appropriate literals are size_t * Fix conditional compilation problem for some #define settings * Avoid concatenating segments with the one provided in create_mspace_with_base * Rename some variables to avoid compiler shadowing warnings * Use explicit lock initialization. * Better handling of sbrk interference. * Simplify and fix segment insertion, trimming and mspace_destroy * Reinstate REALLOC_ZERO_BYTES_FREES option from 2.7.x * Thanks especially to Dennis Flanagan for help on these. V2.8.2 Sun Jun 12 16:01:10 2005 Doug Lea (dl at gee) * Fix memalign brace error. V2.8.1 Wed Jun 8 16:11:46 2005 Doug Lea (dl at gee) * Fix improper #endif nesting in C++ * Add explicit casts needed for C++ V2.8.0 Mon May 30 14:09:02 2005 Doug Lea (dl at gee) * Use trees for large bins * Support mspaces * Use segments to unify sbrk-based and mmap-based system allocation, removing need for emulation on most platforms without sbrk. * Default safety checks * Optional footer checks. Thanks to William Robertson for the idea. * Internal code refactoring * Incorporate suggestions and platform-specific changes. Thanks to Dennis Flanagan, Colin Plumb, Niall Douglas, Aaron Bachmann, Emery Berger, and others. * Speed up non-fastbin processing enough to remove fastbins. * Remove useless cfree() to avoid conflicts with other apps. * Remove internal memcpy, memset. Compilers handle builtins better. * Remove some options that no one ever used and rename others. V2.7.2 Sat Aug 17 09:07:30 2002 Doug Lea (dl at gee) * Fix malloc_state bitmap array misdeclaration V2.7.1 Thu Jul 25 10:58:03 2002 Doug Lea (dl at gee) * Allow tuning of FIRST_SORTED_BIN_SIZE * Use PTR_UINT as type for all ptr->int casts. Thanks to John Belmonte. * Better detection and support for non-contiguousness of MORECORE. Thanks to Andreas Mueller, Conal Walsh, and Wolfram Gloger * Bypass most of malloc if no frees. Thanks To Emery Berger. * Fix freeing of old top non-contiguous chunk im sysmalloc. * Raised default trim and map thresholds to 256K. * Fix mmap-related #defines. Thanks to Lubos Lunak. * Fix copy macros; added LACKS_FCNTL_H. Thanks to Neal Walfield. * Branch-free bin calculation * Default trim and mmap thresholds now 256K. V2.7.0 Sun Mar 11 14:14:06 2001 Doug Lea (dl at gee) * Introduce independent_comalloc and independent_calloc. Thanks to Michael Pachos for motivation and help. * Make optional .h file available * Allow > 2GB requests on 32bit systems. * new WIN32 sbrk, mmap, munmap, lock code from . Thanks also to Andreas Mueller , and Anonymous. * Allow override of MALLOC_ALIGNMENT (Thanks to Ruud Waij for helping test this.) * memalign: check alignment arg * realloc: don't try to shift chunks backwards, since this leads to more fragmentation in some programs and doesn't seem to help in any others. * Collect all cases in malloc requiring system memory into sysmalloc * Use mmap as backup to sbrk * Place all internal state in malloc_state * Introduce fastbins (although similar to 2.5.1) * Many minor tunings and cosmetic improvements * Introduce USE_PUBLIC_MALLOC_WRAPPERS, USE_MALLOC_LOCK * Introduce MALLOC_FAILURE_ACTION, MORECORE_CONTIGUOUS Thanks to Tony E. Bennett and others. * Include errno.h to support default failure action. V2.6.6 Sun Dec 5 07:42:19 1999 Doug Lea (dl at gee) * return null for negative arguments * Added Several WIN32 cleanups from Martin C. Fong * Add 'LACKS_SYS_PARAM_H' for those systems without 'sys/param.h' (e.g. WIN32 platforms) * Cleanup header file inclusion for WIN32 platforms * Cleanup code to avoid Microsoft Visual C++ compiler complaints * Add 'USE_DL_PREFIX' to quickly allow co-existence with existing memory allocation routines * Set 'malloc_getpagesize' for WIN32 platforms (needs more work) * Use 'assert' rather than 'ASSERT' in WIN32 code to conform to usage of 'assert' in non-WIN32 code * Improve WIN32 'sbrk()' emulation's 'findRegion()' routine to avoid infinite loop * Always call 'fREe()' rather than 'free()' V2.6.5 Wed Jun 17 15:57:31 1998 Doug Lea (dl at gee) * Fixed ordering problem with boundary-stamping V2.6.3 Sun May 19 08:17:58 1996 Doug Lea (dl at gee) * Added pvalloc, as recommended by H.J. Liu * Added 64bit pointer support mainly from Wolfram Gloger * Added anonymously donated WIN32 sbrk emulation * Malloc, calloc, getpagesize: add optimizations from Raymond Nijssen * malloc_extend_top: fix mask error that caused wastage after foreign sbrks * Add linux mremap support code from HJ Liu V2.6.2 Tue Dec 5 06:52:55 1995 Doug Lea (dl at gee) * Integrated most documentation with the code. * Add support for mmap, with help from Wolfram Gloger (Gloger@lrz.uni-muenchen.de). * Use last_remainder in more cases. * Pack bins using idea from colin@nyx10.cs.du.edu * Use ordered bins instead of best-fit threshhold * Eliminate block-local decls to simplify tracing and debugging. * Support another case of realloc via move into top * Fix error occuring when initial sbrk_base not word-aligned. * Rely on page size for units instead of SBRK_UNIT to avoid surprises about sbrk alignment conventions. * Add mallinfo, mallopt. Thanks to Raymond Nijssen (raymond@es.ele.tue.nl) for the suggestion. * Add `pad' argument to malloc_trim and top_pad mallopt parameter. * More precautions for cases where other routines call sbrk, courtesy of Wolfram Gloger (Gloger@lrz.uni-muenchen.de). * Added macros etc., allowing use in linux libc from H.J. Lu (hjl@gnu.ai.mit.edu) * Inverted this history list V2.6.1 Sat Dec 2 14:10:57 1995 Doug Lea (dl at gee) * Re-tuned and fixed to behave more nicely with V2.6.0 changes. * Removed all preallocation code since under current scheme the work required to undo bad preallocations exceeds the work saved in good cases for most test programs. * No longer use return list or unconsolidated bins since no scheme using them consistently outperforms those that don't given above changes. * Use best fit for very large chunks to prevent some worst-cases. * Added some support for debugging V2.6.0 Sat Nov 4 07:05:23 1995 Doug Lea (dl at gee) * Removed footers when chunks are in use. Thanks to Paul Wilson (wilson@cs.texas.edu) for the suggestion. V2.5.4 Wed Nov 1 07:54:51 1995 Doug Lea (dl at gee) * Added malloc_trim, with help from Wolfram Gloger (wmglo@Dent.MED.Uni-Muenchen.DE). V2.5.3 Tue Apr 26 10:16:01 1994 Doug Lea (dl at g) V2.5.2 Tue Apr 5 16:20:40 1994 Doug Lea (dl at g) * realloc: try to expand in both directions * malloc: swap order of clean-bin strategy; * realloc: only conditionally expand backwards * Try not to scavenge used bins * Use bin counts as a guide to preallocation * Occasionally bin return list chunks in first scan * Add a few optimizations from colin@nyx10.cs.du.edu V2.5.1 Sat Aug 14 15:40:43 1993 Doug Lea (dl at g) * faster bin computation & slightly different binning * merged all consolidations to one part of malloc proper (eliminating old malloc_find_space & malloc_clean_bin) * Scan 2 returns chunks (not just 1) * Propagate failure in realloc if malloc returns 0 * Add stuff to allow compilation on non-ANSI compilers from kpv@research.att.com V2.5 Sat Aug 7 07:41:59 1993 Doug Lea (dl at g.oswego.edu) * removed potential for odd address access in prev_chunk * removed dependency on getpagesize.h * misc cosmetics and a bit more internal documentation * anticosmetics: mangled names in macros to evade debugger strangeness * tested on sparc, hp-700, dec-mips, rs6000 with gcc & native cc (hp, dec only) allowing Detlefs & Zorn comparison study (in SIGPLAN Notices.) Trial version Fri Aug 28 13:14:29 1992 Doug Lea (dl at g.oswego.edu) * Based loosely on libg++-1.2X malloc. (It retains some of the overall structure of old version, but most details differ.) */ #endif /* AIPS_LINUX */ #endif /* AIPS_NO_LEA_MALLOC */ casacore-3.7.1/casa/OS/malloc.h000066400000000000000000000537021476623553700162030ustar00rootroot00000000000000//# malloc.h: malloc functions from Doug Lea //# Copyright (C) 1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #if !defined(AIPS_NO_LEA_MALLOC) #ifndef CASA_MALLOC_H #define CASA_MALLOC_H /* A version of malloc/free/realloc written by Doug Lea and released to the public domain. Send questions/comments/complaints/performance data to dl@cs.oswego.edu * VERSION 2.6.5 Wed Jun 17 15:55:16 1998 Doug Lea (dl at gee) */ /* The only changes from the distribution are: 1. Added Casacore copyright notice and guard. 2. Compile to nothing for linux since we already get GNU malloc there. 3. If AIPS_DEBUG is set compile this malloc with DEBUG on. */ #if defined(AIPS_LINUX) /* IS linux. Include malloc.h so we only have to include casa/OS/malloc.h without an ifdef on OS. */ #include #else /* NOT linux */ #if defined(AIPS_DEBUG) /* Hopefully not too expensive. If so we can turn it off. */ #define DEBUG 1 #endif /* Default header file for malloc-2.8.x, written by Doug Lea and released to the public domain, as explained at http://creativecommons.org/licenses/publicdomain. last update: Wed May 27 14:25:17 2009 Doug Lea (dl at gee) This header is for ANSI C/C++ only. You can set any of the following #defines before including: * If USE_DL_PREFIX is defined, it is assumed that malloc.c was also compiled with this option, so all routines have names starting with "dl". * If HAVE_USR_INCLUDE_MALLOC_H is defined, it is assumed that this file will be #included AFTER . This is needed only if your system defines a struct mallinfo that is incompatible with the standard one declared here. Otherwise, you can include this file INSTEAD of your system system . At least on ANSI, all declarations should be compatible with system versions * If MSPACES is defined, declarations for mspace versions are included. */ #ifndef MALLOC_280_H #define MALLOC_280_H #ifdef __cplusplus extern "C" { #endif #include /* for size_t */ #ifndef ONLY_MSPACES #define ONLY_MSPACES 0 /* define to a value */ #endif /* ONLY_MSPACES */ #ifndef NO_MALLINFO #define NO_MALLINFO 0 #endif /* NO_MALLINFO */ #if !ONLY_MSPACES #ifndef USE_DL_PREFIX #define dlcalloc calloc #define dlfree free #define dlmalloc malloc #define dlmemalign memalign #define dlrealloc realloc #define dlvalloc valloc #define dlpvalloc pvalloc #define dlmallinfo mallinfo #define dlmallopt mallopt #define dlmalloc_trim malloc_trim #define dlmalloc_stats malloc_stats #define dlmalloc_usable_size malloc_usable_size #define dlmalloc_footprint malloc_footprint #define dlindependent_calloc independent_calloc #define dlindependent_comalloc independent_comalloc #endif /* USE_DL_PREFIX */ #if !NO_MALLINFO #ifndef HAVE_USR_INCLUDE_MALLOC_H #ifndef _MALLOC_H #ifndef MALLINFO_FIELD_TYPE #define MALLINFO_FIELD_TYPE size_t #endif /* MALLINFO_FIELD_TYPE */ #ifndef STRUCT_MALLINFO_DECLARED #define STRUCT_MALLINFO_DECLARED 1 struct mallinfo { MALLINFO_FIELD_TYPE arena; /* non-mmapped space allocated from system */ MALLINFO_FIELD_TYPE ordblks; /* number of free chunks */ MALLINFO_FIELD_TYPE smblks; /* always 0 */ MALLINFO_FIELD_TYPE hblks; /* always 0 */ MALLINFO_FIELD_TYPE hblkhd; /* space in mmapped regions */ MALLINFO_FIELD_TYPE usmblks; /* maximum total allocated space */ MALLINFO_FIELD_TYPE fsmblks; /* always 0 */ MALLINFO_FIELD_TYPE uordblks; /* total allocated space */ MALLINFO_FIELD_TYPE fordblks; /* total free space */ MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */ }; #endif /* STRUCT_MALLINFO_DECLARED */ #endif /* _MALLOC_H */ #endif /* HAVE_USR_INCLUDE_MALLOC_H */ #endif /* !NO_MALLINFO */ /* malloc(size_t n) Returns a pointer to a newly allocated chunk of at least n bytes, or null if no space is available, in which case errno is set to ENOMEM on ANSI C systems. If n is zero, malloc returns a minimum-sized chunk. (The minimum size is 16 bytes on most 32bit systems, and 32 bytes on 64bit systems.) Note that size_t is an unsigned type, so calls with arguments that would be negative if signed are interpreted as requests for huge amounts of space, which will often fail. The maximum supported value of n differs across systems, but is in all cases less than the maximum representable value of a size_t. */ void* dlmalloc(size_t); /* free(void* p) Releases the chunk of memory pointed to by p, that had been previously allocated using malloc or a related routine such as realloc. It has no effect if p is null. If p was not malloced or already freed, free(p) will by default cuase the current program to abort. */ void dlfree(void*); /* calloc(size_t n_elements, size_t element_size); Returns a pointer to n_elements * element_size bytes, with all locations set to zero. */ void* dlcalloc(size_t, size_t); /* realloc(void* p, size_t n) Returns a pointer to a chunk of size n that contains the same data as does chunk p up to the minimum of (n, p's size) bytes, or null if no space is available. The returned pointer may or may not be the same as p. The algorithm prefers extending p in most cases when possible, otherwise it employs the equivalent of a malloc-copy-free sequence. If p is null, realloc is equivalent to malloc. If space is not available, realloc returns null, errno is set (if on ANSI) and p is NOT freed. if n is for fewer bytes than already held by p, the newly unused space is lopped off and freed if possible. realloc with a size argument of zero (re)allocates a minimum-sized chunk. The old unix realloc convention of allowing the last-free'd chunk to be used as an argument to realloc is not supported. */ void* dlrealloc(void*, size_t); /* memalign(size_t alignment, size_t n); Returns a pointer to a newly allocated chunk of n bytes, aligned in accord with the alignment argument. The alignment argument should be a power of two. If the argument is not a power of two, the nearest greater power is used. 8-byte alignment is guaranteed by normal malloc calls, so don't bother calling memalign with an argument of 8 or less. Overreliance on memalign is a sure way to fragment space. */ void* dlmemalign(size_t, size_t); /* valloc(size_t n); Equivalent to memalign(pagesize, n), where pagesize is the page size of the system. If the pagesize is unknown, 4096 is used. */ void* dlvalloc(size_t); /* mallopt(int parameter_number, int parameter_value) Sets tunable parameters The format is to provide a (parameter-number, parameter-value) pair. mallopt then sets the corresponding parameter to the argument value if it can (i.e., so long as the value is meaningful), and returns 1 if successful else 0. SVID/XPG/ANSI defines four standard param numbers for mallopt, normally defined in malloc.h. None of these are use in this malloc, so setting them has no effect. But this malloc also supports other options in mallopt: Symbol param # default allowed param values M_TRIM_THRESHOLD -1 2*1024*1024 any (-1U disables trimming) M_GRANULARITY -2 page size any power of 2 >= page size M_MMAP_THRESHOLD -3 256*1024 any (or 0 if no MMAP support) */ int dlmallopt(int, int); #define M_TRIM_THRESHOLD (-1) #define M_GRANULARITY (-2) #define M_MMAP_THRESHOLD (-3) /* malloc_footprint(); Returns the number of bytes obtained from the system. The total number of bytes allocated by malloc, realloc etc., is less than this value. Unlike mallinfo, this function returns only a precomputed result, so can be called frequently to monitor memory consumption. Even if locks are otherwise defined, this function does not use them, so results might not be up to date. */ size_t dlmalloc_footprint(); #if !NO_MALLINFO /* mallinfo() Returns (by copy) a struct containing various summary statistics: arena: current total non-mmapped bytes allocated from system ordblks: the number of free chunks smblks: always zero. hblks: current number of mmapped regions hblkhd: total bytes held in mmapped regions usmblks: the maximum total allocated space. This will be greater than current total if trimming has occurred. fsmblks: always zero uordblks: current total allocated space (normal or mmapped) fordblks: total free space keepcost: the maximum number of bytes that could ideally be released back to system via malloc_trim. ("ideally" means that it ignores page restrictions etc.) Because these fields are ints, but internal bookkeeping may be kept as longs, the reported values may wrap around zero and thus be inaccurate. */ struct mallinfo dlmallinfo(void); #endif /* NO_MALLINFO */ /* independent_calloc(size_t n_elements, size_t element_size, void* chunks[]); independent_calloc is similar to calloc, but instead of returning a single cleared space, it returns an array of pointers to n_elements independent elements that can hold contents of size elem_size, each of which starts out cleared, and can be independently freed, realloc'ed etc. The elements are guaranteed to be adjacently allocated (this is not guaranteed to occur with multiple callocs or mallocs), which may also improve cache locality in some applications. The "chunks" argument is optional (i.e., may be null, which is probably the most typical usage). If it is null, the returned array is itself dynamically allocated and should also be freed when it is no longer needed. Otherwise, the chunks array must be of at least n_elements in length. It is filled in with the pointers to the chunks. In either case, independent_calloc returns this pointer array, or null if the allocation failed. If n_elements is zero and "chunks" is null, it returns a chunk representing an array with zero elements (which should be freed if not wanted). Each element must be individually freed when it is no longer needed. If you'd like to instead be able to free all at once, you should instead use regular calloc and assign pointers into this space to represent elements. (In this case though, you cannot independently free elements.) independent_calloc simplifies and speeds up implementations of many kinds of pools. It may also be useful when constructing large data structures that initially have a fixed number of fixed-sized nodes, but the number is not known at compile time, and some of the nodes may later need to be freed. For example: struct Node { int item; struct Node* next; }; struct Node* build_list() { struct Node** pool; int n = read_number_of_nodes_needed(); if (n <= 0) return 0; pool = (struct Node**)(independent_calloc(n, sizeof(struct Node), 0); if (pool == 0) die(); // organize into a linked list... struct Node* first = pool[0]; for (i = 0; i < n-1; ++i) pool[i]->next = pool[i+1]; free(pool); // Can now free the array (or not, if it is needed later) return first; } */ void** dlindependent_calloc(size_t, size_t, void**); /* independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]); independent_comalloc allocates, all at once, a set of n_elements chunks with sizes indicated in the "sizes" array. It returns an array of pointers to these elements, each of which can be independently freed, realloc'ed etc. The elements are guaranteed to be adjacently allocated (this is not guaranteed to occur with multiple callocs or mallocs), which may also improve cache locality in some applications. The "chunks" argument is optional (i.e., may be null). If it is null the returned array is itself dynamically allocated and should also be freed when it is no longer needed. Otherwise, the chunks array must be of at least n_elements in length. It is filled in with the pointers to the chunks. In either case, independent_comalloc returns this pointer array, or null if the allocation failed. If n_elements is zero and chunks is null, it returns a chunk representing an array with zero elements (which should be freed if not wanted). Each element must be individually freed when it is no longer needed. If you'd like to instead be able to free all at once, you should instead use a single regular malloc, and assign pointers at particular offsets in the aggregate space. (In this case though, you cannot independently free elements.) independent_comallac differs from independent_calloc in that each element may have a different size, and also that it does not automatically clear elements. independent_comalloc can be used to speed up allocation in cases where several structs or objects must always be allocated at the same time. For example: struct Head { ... } struct Foot { ... } void send_message(char* msg) { int msglen = strlen(msg); size_t sizes[3] = { sizeof(struct Head), msglen, sizeof(struct Foot) }; void* chunks[3]; if (independent_comalloc(3, sizes, chunks) == 0) die(); struct Head* head = (struct Head*)(chunks[0]); char* body = (char*)(chunks[1]); struct Foot* foot = (struct Foot*)(chunks[2]); // ... } In general though, independent_comalloc is worth using only for larger values of n_elements. For small values, you probably won't detect enough difference from series of malloc calls to bother. Overuse of independent_comalloc can increase overall memory usage, since it cannot reuse existing noncontiguous small chunks that might be available for some of the elements. */ void** dlindependent_comalloc(size_t, size_t*, void**); /* pvalloc(size_t n); Equivalent to valloc(minimum-page-that-holds(n)), that is, round up n to nearest pagesize. */ void* dlpvalloc(size_t); /* malloc_trim(size_t pad); If possible, gives memory back to the system (via negative arguments to sbrk) if there is unused memory at the `high' end of the malloc pool or in unused MMAP segments. You can call this after freeing large blocks of memory to potentially reduce the system-level memory requirements of a program. However, it cannot guarantee to reduce memory. Under some allocation patterns, some large free blocks of memory will be locked between two used chunks, so they cannot be given back to the system. The `pad' argument to malloc_trim represents the amount of free trailing space to leave untrimmed. If this argument is zero, only the minimum amount of memory to maintain internal data structures will be left. Non-zero arguments can be supplied to maintain enough trailing space to service future expected allocations without having to re-obtain memory from the system. Malloc_trim returns 1 if it actually released any memory, else 0. */ int dlmalloc_trim(size_t); /* malloc_stats(); Prints on stderr the amount of space obtained from the system (both via sbrk and mmap), the maximum amount (which may be more than current if malloc_trim and/or munmap got called), and the current number of bytes allocated via malloc (or realloc, etc) but not yet freed. Note that this is the number of bytes allocated, not the number requested. It will be larger than the number requested because of alignment and bookkeeping overhead. Because it includes alignment wastage as being in use, this figure may be greater than zero even when no user-level chunks are allocated. The reported current and maximum system memory can be inaccurate if a program makes other calls to system memory allocation functions (normally sbrk) outside of malloc. malloc_stats prints only the most commonly interesting statistics. More information can be obtained by calling mallinfo. */ void dlmalloc_stats(); #endif /* !ONLY_MSPACES */ /* malloc_usable_size(void* p); Returns the number of bytes you can actually use in an allocated chunk, which may be more than you requested (although often not) due to alignment and minimum size constraints. You can use this many bytes without worrying about overwriting other allocated objects. This is not a particularly great programming practice. malloc_usable_size can be more useful in debugging and assertions, for example: p = malloc(n); assert(malloc_usable_size(p) >= 256); */ size_t dlmalloc_usable_size(void*); #if MSPACES /* mspace is an opaque type representing an independent region of space that supports mspace_malloc, etc. */ typedef void* mspace; /* create_mspace creates and returns a new independent space with the given initial capacity, or, if 0, the default granularity size. It returns null if there is no system memory available to create the space. If argument locked is non-zero, the space uses a separate lock to control access. The capacity of the space will grow dynamically as needed to service mspace_malloc requests. You can control the sizes of incremental increases of this space by compiling with a different DEFAULT_GRANULARITY or dynamically setting with mallopt(M_GRANULARITY, value). */ mspace create_mspace(size_t capacity, int locked); /* destroy_mspace destroys the given space, and attempts to return all of its memory back to the system, returning the total number of bytes freed. After destruction, the results of access to all memory used by the space become undefined. */ size_t destroy_mspace(mspace msp); /* create_mspace_with_base uses the memory supplied as the initial base of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this space is used for bookkeeping, so the capacity must be at least this large. (Otherwise 0 is returned.) When this initial space is exhausted, additional memory will be obtained from the system. Destroying this space will deallocate all additionally allocated space (if possible) but not the initial base. */ mspace create_mspace_with_base(void* base, size_t capacity, int locked); /* mspace_track_large_chunks controls whether requests for large chunks are allocated in their own untracked mmapped regions, separate from others in this mspace. By default large chunks are not tracked, which reduces fragmentation. However, such chunks are not necessarily released to the system upon destroy_mspace. Enabling tracking by setting to true may increase fragmentation, but avoids leakage when relying on destroy_mspace to release all memory allocated using this space. The function returns the previous setting. */ int mspace_track_large_chunks(mspace msp, int enable); /* mspace_malloc behaves as malloc, but operates within the given space. */ void* mspace_malloc(mspace msp, size_t bytes); /* mspace_free behaves as free, but operates within the given space. If compiled with FOOTERS==1, mspace_free is not actually needed. free may be called instead of mspace_free because freed chunks from any space are handled by their originating spaces. */ void mspace_free(mspace msp, void* mem); /* mspace_realloc behaves as realloc, but operates within the given space. If compiled with FOOTERS==1, mspace_realloc is not actually needed. realloc may be called instead of mspace_realloc because realloced chunks from any space are handled by their originating spaces. */ void* mspace_realloc(mspace msp, void* mem, size_t newsize); /* mspace_calloc behaves as calloc, but operates within the given space. */ void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size); /* mspace_memalign behaves as memalign, but operates within the given space. */ void* mspace_memalign(mspace msp, size_t alignment, size_t bytes); /* mspace_independent_calloc behaves as independent_calloc, but operates within the given space. */ void** mspace_independent_calloc(mspace msp, size_t n_elements, size_t elem_size, void* chunks[]); /* mspace_independent_comalloc behaves as independent_comalloc, but operates within the given space. */ void** mspace_independent_comalloc(mspace msp, size_t n_elements, size_t sizes[], void* chunks[]); /* mspace_footprint() returns the number of bytes obtained from the system for this space. */ size_t mspace_footprint(mspace msp); #if !NO_MALLINFO /* mspace_mallinfo behaves as mallinfo, but reports properties of the given space. */ struct mallinfo mspace_mallinfo(mspace msp); #endif /* NO_MALLINFO */ /* malloc_usable_size(void* p) behaves the same as malloc_usable_size; */ size_t mspace_usable_size(void* mem); /* mspace_malloc_stats behaves as malloc_stats, but reports properties of the given space. */ void mspace_malloc_stats(mspace msp); /* mspace_trim behaves as malloc_trim, but operates within the given space. */ int mspace_trim(mspace msp, size_t pad); /* An alias for mallopt. */ int mspace_mallopt(int, int); #endif /* MSPACES */ #ifdef __cplusplus } /* end of extern "C" */ #endif #endif /* MALLOC_280_H */ #endif /* AIPS_LINUX */ #endif /* AIPS_MALLOC */ #endif /* AIPS_NO_LEA_MALLOC */ casacore-3.7.1/casa/OS/test/000077500000000000000000000000001476623553700155335ustar00rootroot00000000000000casacore-3.7.1/casa/OS/test/CMakeLists.txt000066400000000000000000000010261476623553700202720ustar00rootroot00000000000000set (tests tCanonicalConversion tConversion tConversionPerf tDataConversion tDirectory tDirectoryIterator tEnvVar tFile tHostInfo tIBMConversion tLECanonicalConversion tMemoryTrace tModcompConversion tPath tPrecTimer tRegularFile tSymLink tTime tTimer tVAXConversion ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_casa) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/casa/OS/test/tCanonicalConversion.cc000066400000000000000000000365641476623553700222010ustar00rootroot00000000000000//# tCanonicalConversion.h: Test program for class CanonicalConversion //# Copyright (C) 1996,1997,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include // Check if all conversion definitions are set correctly. // It write error messages to cout and exits (with status 1) // when errors are found. void checkFlags (int& error) { int flag; // Check if byte order is correctly defined. // In AIPS_LITTLE_ENDIAN the first byte of an int==1 should be 1. // Otherwise it should be 0. int val = 1; #if defined(AIPS_LITTLE_ENDIAN) if (((char*)(&val))[0] != 1) { cout << "AIPS_LITTLE_ENDIAN should not be defined" << endl; error = 1; } #else if (((char*)(&val))[0] != 0) { cout << "AIPS_LITTLE_ENDIAN should be defined" << endl; error = 1; } #endif // Check for (unsigned) char. flag = sizeof(char) != SIZE_CAN_CHAR; if ((flag^CONVERT_CAN_CHAR) != 0) { cout << "invalid CONVERT_CAN_CHAR definition" << endl; error = 1; } flag = sizeof(unsigned char) != SIZE_CAN_UCHAR; if ((flag^CONVERT_CAN_UCHAR) != 0) { cout << "invalid CONVERT_CAN_UCHAR definition" << endl; error = 1; } if (sizeof(char) != 1) { cout << "sizeof(char) must be 1" << endl; error = 1; } if (sizeof(unsigned char) != 1) { cout << "sizeof(char) must be 1" << endl; error = 1; } // When AIPS_LITTLE_ENDIAN is defined, conversion is always needed. #if defined(AIPS_LITTLE_ENDIAN) flag = 1; #endif // Check for (unsigned) short. #if !defined(AIPS_LITTLE_ENDIAN) flag = sizeof(short) != SIZE_CAN_SHORT; #endif if ((flag^CONVERT_CAN_SHORT) != 0) { cout << "invalid CONVERT_CAN_SHORT definition" << endl; error = 1; } #if !defined(AIPS_LITTLE_ENDIAN) flag = sizeof(unsigned short) != SIZE_CAN_USHORT; #endif if ((flag^CONVERT_CAN_USHORT) != 0) { cout << "invalid CONVERT_CAN_USHORT definition" << endl; error = 1; } if (sizeof(short) < 2) { cout << "sizeof(short) must be >=2" << endl; error = 1; } if (sizeof(unsigned short) < 2) { cout << "sizeof(unsigned short) must be >=2" << endl; error = 1; } // Check for (unsigned) int. #if !defined(AIPS_LITTLE_ENDIAN) flag = sizeof(int) != SIZE_CAN_INT; #endif if ((flag^CONVERT_CAN_INT) != 0) { cout << "invalid CONVERT_CAN_INT definition" << endl; error = 1; } #if !defined(AIPS_LITTLE_ENDIAN) flag = sizeof(unsigned int) != SIZE_CAN_UINT; #endif if ((flag^CONVERT_CAN_UINT) != 0) { cout << "invalid CONVERT_CAN_UINT definition" << endl; error = 1; } if (sizeof(int) < 4) { cout << "sizeof(int) must be >=4" << endl; error = 1; } if (sizeof(unsigned int) < 4) { cout << "sizeof(unsigned int) must be >=4" << endl; error = 1; } // Check for (unsigned) int64 #if !defined(AIPS_LITTLE_ENDIAN) flag = sizeof(Int64) != SIZE_CAN_INT64; #endif if ((flag^CONVERT_CAN_INT64) != 0) { cout << "invalid CONVERT_CAN_INT64 definition" << endl; error = 1; } #if !defined(AIPS_LITTLE_ENDIAN) flag = sizeof(uInt64) != SIZE_CAN_UINT64; #endif if ((flag^CONVERT_CAN_UINT64) != 0) { cout << "invalid CONVERT_CAN_UINT64 definition" << endl; error = 1; } if (sizeof(Int64) < 8) { cout << "sizeof(Int64) must be >=8" << endl; error = 1; } if (sizeof(uInt64) < 8) { cout << "sizeof(uInt64) must >=8" << endl; error = 1; } // Check for float and double. #if !defined(AIPS_LITTLE_ENDIAN) flag = sizeof(float) != SIZE_CAN_FLOAT; #endif if ((flag^CONVERT_CAN_FLOAT) != 0) { cout << "invalid CONVERT_CAN_FLOAT definition" << endl; error = 1; } #if !defined(AIPS_LITTLE_ENDIAN) flag = sizeof(double) != SIZE_CAN_DOUBLE; #endif if ((flag^CONVERT_CAN_DOUBLE) != 0) { cout << "invalid CONVERT_CAN_DOUBLE definition" << endl; error = 1; } if (sizeof(float) != 4) { cout << "sizeof(float) must be 4" << endl; error = 1; } if (sizeof(double) != 8) { cout << "sizeof(double) must be 8" << endl; error = 1; } } void checkConversion (int& error) { char val[9]; char out[9]; { val[1] = -1; char result; CanonicalConversion::toLocal (&result, val+1, 1); if (result != char(-1)) { cout << "invalid char to conversion " << result << endl; error = 1; } CanonicalConversion::fromLocal (out+1, &result, 1); if (out[1] != val[1]) { cout << "invalid char from conversion" << endl; error = 1; } } { val[1] = -2; unsigned char result; CanonicalConversion::toLocal (&result, val+1, 1); if (result != 254) { cout << "invalid unsigned char to conversion " << result << endl; error = 1; } CanonicalConversion::fromLocal (out+1, &result, 1); if (out[1] != val[1]) { cout << "invalid unsigned char from conversion" << endl; error = 1; } } { val[1] = 1; val[2] = 2; short result; CanonicalConversion::toLocal (&result, val+1, 1); if (result != 258) { cout << "invalid short to conversion 1 " << result << endl; error = 1; } CanonicalConversion::fromLocal (out+1, &result, 1); if (out[1] != val[1] || out[2] != val[2]) { cout << "invalid short from conversion 1" << endl; error = 1; } val[1] = -2; CanonicalConversion::toLocal (&result, val+1, 1); if (result != -(1*256 + 254)) { cout << "invalid short to conversion 2 " << result << endl; error = 1; } CanonicalConversion::fromLocal (out+1, &result, 1); if (out[1] != val[1] || out[2] != val[2]) { cout << "invalid short from conversion 2" << endl; error = 1; } } { val[1] = 7; val[2] = 111; unsigned short result; CanonicalConversion::toLocal (&result, val+1, 1); if (result != 7*256 + 111) { cout << "invalid unsigned short to conversion " << result << endl; error = 1; } CanonicalConversion::fromLocal (out+1, &result, 1); if (out[1] != val[1] || out[2] != val[2]) { cout << "invalid unsigned short from conversion" << endl; error = 1; } } { val[1] = 1; val[2] = 2; val[3] = 3; val[4] = 0; int result; CanonicalConversion::toLocal (&result, val+1, 1); if (result != 1*256*256*256 + 2*256*256 + 3*256 + 0) { cout << "invalid int to conversion 1 " << result << endl; error = 1; } CanonicalConversion::fromLocal (out+1, &result, 1); if (out[1] != val[1] || out[2] != val[2] || out[3] != val[3] || out[4] != val[4]) { cout << "invalid int from conversion 1" << endl; error = 1; } val[1] = -127; CanonicalConversion::toLocal (&result, val+1, 1); if (result != -(126*256*256*256 + 253*256*256 + 252*256 + 256)) { cout << "invalid int to conversion 2 " << result << endl; error = 1; } CanonicalConversion::fromLocal (out+1, &result, 1); if (out[1] != val[1] || out[2] != val[2] || out[3] != val[3] || out[4] != val[4]) { cout << "invalid int from conversion 2" << endl; error = 1; } } { val[1] = 11; val[2] = 23; val[3] = 35; val[4] = 7; unsigned int result; CanonicalConversion::toLocal (&result, val+1, 1); if (result != 11*256*256*256 + 23*256*256 + 35*256 + 07) { cout << "invalid unsigned int to conversion " << result << endl; error = 1; } CanonicalConversion::fromLocal (out+1, &result, 1); if (out[1] != val[1] || out[2] != val[2] || out[3] != val[3] || out[4] != val[4]) { cout << "invalid unsigned int from conversion" << endl; error = 1; } } { val[1] = 0; val[2] = 0; val[3] = 0; val[4] = 6; val[5] = 2; val[6] = 54; val[7] = 78; val[8] = 145-256; Int64 result; CanonicalConversion::toLocal (&result, val+1, 1); if (result != 2*256*256*256 + 54*256*256 + 78*256 + 145 + 6*(Int64)(256)*256*256*256) { cout << "invalid Int64 to conversion 1 " << result << endl; error = 1; } CanonicalConversion::fromLocal (out+1, &result, 1); if (out[1] != val[1] || out[2] != val[2] || out[3] != val[3] || out[4] != val[4] || out[5] != val[5] || out[6] != val[6] || out[7] != val[7] || out[8] != val[8]) { cout << "invalid Int64 from conversion 1" << endl; error = 1; } val[1] = -1; val[2] = -1; val[3] = -1; val[4] = -2; CanonicalConversion::toLocal (&result, val+1, 1); if (result != -((Int64)(256)*256*256*256 + (Int64)(253)*256*256*256 + 201*256*256 + 177*256 + 111)) { cout << "invalid Int64 to conversion 2 " << result << endl; error = 1; } CanonicalConversion::fromLocal (out+1, &result, 1); if (out[1] != val[1] || out[2] != val[2] || out[3] != val[3] || out[4] != val[4] || out[5] != val[5] || out[6] != val[6] || out[7] != val[7] || out[8] != val[8]) { cout << "invalid Int64 from conversion 2" << endl; error = 1; } } { val[1] = 0; val[2] = 0; val[3] = 0; val[4] = 5; val[5] = 128-256; val[6] = 54; val[7] = 78; val[8] = 100; uInt64 result; CanonicalConversion::toLocal (&result, val+1, 1); if (result != 128U*256U*256U*256U + 54U*256U*256U + 78U*256U + 100U + 5U*(uInt64)(256)*256U*256U*256U) { cout << "invalid uInt64 to conversion " << result << endl; error = 1; } CanonicalConversion::fromLocal (out+1, &result, 1); if (out[1] != val[1] || out[2] != val[2] || out[3] != val[3] || out[4] != val[4] || out[5] != val[5] || out[6] != val[6] || out[7] != val[7] || out[8] != val[8]) { cout << "invalid uInt64 from conversion" << endl; error = 1; } } { // Try a float with a positive exponent and sign. // An IEEE float looks like SEEEEEEE EFFFFFFF FFFFFFFF FFFFFFFF. // The first bit of the fraction is hidden (is 1 and not stored). // Exponent-126 is the true exponent (base 2). val[1] = 63; val[2] = 192-256; val[3] = 1; val[4] = 7; float result; CanonicalConversion::toLocal (&result, val+1, 1); double d = pow(double(2), double(127-126)); d *= double(192)/256 + double(1)/(256*256) + double(7)/(256*256*256); float v = d; if (result != float(v)) { cout << "invalid float to conversion " << result << " " <
        66. inlining //
        67. look at the problem of rad*rad (which is, in general, not sr) // // // //

          Known units on 960509

          // // // UnitMap::list() will produce the following list: //List all defined symbols // //Prefix table (20): // E (exa) 1e+18 // G (giga) 1000000000 // M (mega) 1000000 // P (peta) 1e+15 // T (tera) 1e+12 // Y (yotta) 1e+24 // Z (zetta) 1e+21 // a (atto) 1e-18 // c (centi) 0.01 // d (deci) 0.1 // da (deka) 10 // f (femto) 1e-15 // h (hecto) 100 // k (kilo) 1000 // m (milli) 0.001 // n (nano) 1e-09 // p (pico) 1e-12 // u (micro) 1e-06 // y (yocto) 1e-24 // z (zepto) 1e-21 //Defining unit table (10): // A (ampere) 1 A // K (kelvin) 1 K // _ (undimensioned) 1 _ // cd (candela) 1 cd // kg (kilogram) 1 kg // m (metre) 1 m // mol (mole) 1 mol // rad (radian) 1 rad // s (second) 1 s // sr (steradian) 1 sr //SI unit table (50): // $ (currency) 1 _ // % (percent) 0.01 // %% (permille) 0.001 // A (ampere) 1 A // AE (astronomical unit) 149597870659 m // AU (astronomical unit) 149597870659 m // Bq (becquerel) 1 s-1 // C (coulomb) 1 s.A // F (farad) 1 m-2.kg-1.s4.A2 // Gy (gray) 1 m2.s-2 // H (henry) 1 m2.kg.s-2.A-2 // Hz (hertz) 1 s-1 // J (joule) 1 m2.kg.s-2 // Jy (jansky) 1e-26 kg.s-2 // K (kelvin) 1 K // L (litre) 0.001 m3 // M0 (solar mass) 1.98891944407e+30 kg // N (newton) 1 m.kg.s-2 // Ohm (ohm) 1 m2.kg.s-3.A-2 // Pa (pascal) 1 m-1.kg.s-2 // S (siemens) 1 m-2.kg-1.s3.A2 // S0 (solar mass) 1.98891944407e+30 kg // Sv (sievert) 1 m2.s-2 // T (tesla) 1 kg.s-2.A-1 // UA (astronomical unit) 149597870659 m // V (volt) 1 m2.kg.s-3.A-1 // W (watt) 1 m2.kg.s-3 // Wb (weber) 1 m2.kg.s-2.A-1 // _ (undimensioned) 1 _ // a (year) 31557600 s // arcmin (arcmin) 0.000290888208666 rad // arcsec (arcsec) 4.8481368111e-06 rad // as (arcsec) 4.8481368111e-06 rad // cd (candela) 1 cd // cy (century) 3155760000 s // d (day) 86400 s // deg (degree) 0.0174532925199 rad // g (gram) 0.001 kg // h (hour) 3600 s // l (litre) 0.001 m3 // lm (lumen) 1 cd.sr // lx (lux) 1 m-2.cd.sr // m (metre) 1 m // min (minute) 60 s // mol (mole) 1 mol // pc (parsec) 3.08567758065e+16 m // rad (radian) 1 rad // s (second) 1 s // sr (steradian) 1 sr // t (tonne) 1000 kg //Customary unit table (74): // " (arcsec) 4.8481368111e-06 rad // "_2 (square arcsec) 2.35044305391e-11 sr // ' (arcmin) 0.000290888208666 rad // '' (arcsec) 4.8481368111e-06 rad // ''_2 (square arcsec) 2.35044305391e-11 sr // '_2 (square arcmin) 8.46159499408e-08 sr // : (hour) 3600 s // :: (minute) 60 s // ::: (second) 1 s // Ah (ampere hour) 3600 s.A // Angstrom (angstrom) 1e-10 m // Btu (British thermal unit (Int)) 1055.056 m2.kg.s-2 // CM (metric carat) 0.0002 kg // Cal (large calorie (Int)) 4186.8 m2.kg.s-2 // FU (flux unit) 1e-26 kg.s-2 // G (gauss) 0.0001 kg.s-2.A-1 // Gal (gal) 0.01 m.s-2 // Gb (gilbert) 0.795774715459 A // Mx (maxwell) 1e-08 m2.kg.s-2.A-1 // Oe (oersted) 79.5774715459 m-1.A // R (mile) 0.000258 kg-1.s.A // St (stokes) 0.0001 m2.s-1 // Torr (torr) 133.322368421 m-1.kg.s-2 // USfl_oz (fluid ounce (US)) 2.95735295625e-05 m3 // USgal (gallon (US)) 0.003785411784 m3 // WU (WSRT flux unit) 5e-29 kg.s-2 // abA (abampere) 10 A // abC (abcoulomb) 10 s.A // abF (abfarad) 1000000000 m-2.kg-1.s4.A2 // abH (abhenry) 1e-09 m2.kg.s-2.A-2 // abOhm (abohm) 1e-09 m2.kg.s-3.A-2 // abV (abvolt) 1e-08 m2.kg.s-3.A-1 // ac (acre) 4046.8564224 m2 // arcmin_2 (square arcmin) 8.46159499408e-08 sr // arcsec_2 (square arcsec) 2.35044305391e-11 sr // ata (technical atmosphere) 98066.5 m-1.kg.s-2 // atm (standard atmosphere) 101325 m-1.kg.s-2 // bar (bar) 100000 m-1.kg.s-2 // beam (undefined beam area) 1 _ // cal (calorie (Int)) 4.1868 m2.kg.s-2 // cwt (hundredweight) 50.80234544 kg // deg_2 (square degree) 0.000304617419787 sr // dyn (dyne) 1e-05 m.kg.s-2 // eV (electron volt) 1.60217733e-19 m2.kg.s-2 // erg (erg) 1e-07 m2.kg.s-2 // fl_oz (fluid ounce (Imp)) 2.84130488996e-05 m3 // ft (foot) 0.3048 m // fu (flux unit) 1e-26 kg.s-2 // fur (furlong) 201.168 m // gal (gallon (Imp)) 0.00454608782394 m3 // ha (hectare) 10000 m2 // hp (horsepower) 745.7 m2.kg.s-3 // in (inch) 0.0254 m // kn (knot (Imp)) 0.514773333333 m.s-1 // lb (pound (avoirdupois)) 0.45359237 kg // ly (light year) 9.46073047e+15 m // mHg (metre of mercury) 133322.387415 m-1.kg.s-2 // mile (mile) 1609.344 m // n_mile (nautical mile (Imp)) 1853.184 m // oz (ounce (avoirdupois)) 0.028349523125 kg // pixel (pixel) 1 _ // sb (stilb) 10000 m-2.cd // sq_arcmin (square arcmin) 8.46159499408e-08 sr // sq_arcsec (square arcsec) 2.35044305391e-11 sr // sq_deg (square degree) 0.000304617419787 sr // statA (statampere) 3.33564095198e-10 A // statC (statcoulomb) 3.33564095198e-10 s.A // statF (statfarad) 1.11188031733e-12 m-2.kg-1.s4.A2 // statH (stathenry) 899377374000 m2.kg.s-2.A-2 // statOhm (statohm) 899377374000 m2.kg.s-3.A-2 // statV (statvolt) 299.792458 m2.kg.s-3.A-1 // debye (electric dipole moment) 10-18 statC.cm // u (atomic mass unit) 1.661e-27 kg // yd (yard) 0.9144 m // yr (year) 31557600 s // // //
          // //# Dummy class definition for extractor //# class Quanta {}; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Quanta/000077500000000000000000000000001476623553700154645ustar00rootroot00000000000000casacore-3.7.1/casa/Quanta/Euler.cc000066400000000000000000000147571476623553700170650ustar00rootroot00000000000000//# Euler.cc: Vector of Euler rotation angles //# Copyright (C) 1995,1996,1997,1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Euler class //# Constructors Euler::Euler() : euler(3), axes(3) { euler = Double(0.0); indgen(axes,1,1); } Euler::Euler(const Euler &other) : euler(3), axes(3) { euler = other.euler; axes = other.axes; } Euler &Euler::operator=(const Euler &other) { if (this != &other) { euler = other.euler; axes = other.axes; } return *this; } Euler::Euler(Double in0, Double in1, Double in2) : euler(3), axes(3) { euler(0) = in0; euler(1) = in1; euler(2) = in2; indgen(axes,1,1); } Euler::Euler(Double in0, uInt ax0, Double in1, uInt ax1, Double in2, uInt ax2) : euler(3), axes(3) { DebugAssert(ax0 <= 3 && ax1 <=3 && ax2 <=3, AipsError); euler(0) = in0; euler(1) = in1; euler(2) = in2; axes(0) = ax0; axes(1) = ax1; axes(2) = ax2; } Euler::Euler(const Quantity &in0) : euler(3), axes(3) { euler(0) = Euler::makeRad(in0); euler(1) = 0; euler(2) = 0; indgen(axes,1,1); } Euler::Euler(const Quantity &in0, const Quantity &in1) : euler(3), axes(3) { euler(0) = Euler::makeRad(in0); euler(1) = Euler::makeRad(in1); euler(2) = 0; indgen(axes,1,1); } Euler::Euler(const Quantity &in0, const Quantity &in1, const Quantity &in2) : euler(3), axes(3) { euler(0) = Euler::makeRad(in0); euler(1) = Euler::makeRad(in1); euler(2) = Euler::makeRad(in2); indgen(axes,1,1); } Euler::Euler(const Quantity &in0, uInt ax0) : euler(3), axes(3) { DebugAssert(ax0 <= 3, AipsError); euler(0) = Euler::makeRad(in0); euler(1) = 0; euler(2) = 0; axes(0) = ax0; axes(1) = 0; axes(2) = 0; } Euler::Euler(const Quantity &in0, uInt ax0, const Quantity &in1, uInt ax1) : euler(3), axes(3) { DebugAssert(ax0 <= 3 && ax1 <=3, AipsError); euler(0) = Euler::makeRad(in0); euler(1) = Euler::makeRad(in1); euler(2) = 0; axes(0) = ax0; axes(1) = ax1; axes(2) = 0; } Euler::Euler(const Quantity &in0, uInt ax0, const Quantity &in1, uInt ax1, const Quantity &in2, uInt ax2) : euler(3), axes(3) { DebugAssert(ax0 <= 3 && ax1 <=3 && ax2 <=3, AipsError); euler(0) = Euler::makeRad(in0); euler(1) = Euler::makeRad(in1); euler(2) = Euler::makeRad(in2); axes(0) = ax0; axes(1) = ax1; axes(2) = ax2; } Euler::Euler(const Quantum > &in) : euler(3), axes(3) { Int i; Vector tmp = Euler::makeRad(in); Int j=tmp.size(); j=min(j,3); for (i=0; i > &in, const Vector &ax) : euler(3), axes(3) { Vector tmp = Euler::makeRad(in); Int j=tmp.size(); j=min(j,3); Int i=ax.size(); j=min(j,i); for (i=0; i Euler::makeRad(const Quantum > &in) { in.assure(UnitVal::ANGLE); return in.get().getValue(); } Quantum > Euler::getAngle() const { return Quantum >(euler,"rad"); } Quantum > Euler::getAngle(const Unit &unit) const { return Quantum >(euler,"rad").get(unit); } void Euler::set(uInt which, uInt ax) { DebugAssert(which < 3 && ax <=3, AipsError); axes(which) = ax; } void Euler::set(uInt ax0, uInt ax1, uInt ax2) { DebugAssert(ax0 <= 3 && ax1 <=3 && ax2 <= 3, AipsError); axes(0) = ax0; axes(1) = ax1; axes(2) = ax2; } Int Euler::get(uInt which) const{ DebugAssert(which < 3, AipsError); return axes(which); } ostream &operator<<(ostream &os, const Euler &eul) { os << eul.euler; return os; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Quanta/Euler.h000066400000000000000000000156421476623553700167210ustar00rootroot00000000000000//# Euler.h: Vector of Euler rotation angles //# Copyright (C) 1995,1996,1997,1998,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_EULER_H #define CASA_EULER_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // Vector of Euler rotation angles // // // // // //
        68. Vector class //
        69. Quantum class for units //
        70. RotMatrix class for usage // // // // Euler angles describe the rotation of a coordinate system // // // // The Euler class is a vector of three angles, together with a vector of // three signed integers. The angles describe the rotation around an axis of a // coordinate system, the integers the actual axis around which to rotate. // The integer can be 0 (do not use this angle) or 1,2,3 to indicate the // axis. // Given angles (a1,a2,a3) and axes (i1,i2,i3), the actual rotation matrix // constructed will be:
          // R = Ri3(a3).Ri2(a2).Ri1(a1)
          // It has the following constructors: //
            //
          • Euler() creates a zero filled vector of length 3. Axes: (1,2,3) //
          • Euler(Euler) creates a copy //
          • Euler(Double, uInt, Double=0, uInt=0, Double=0, uInt=0) creates an // Euler with specified values //
          • Euler(Double, Double=0, Double=0) creates an Euler with (1,2,3) //
          • Euler(Quantity, uInt, Quantity=0, uInt=0, Quantity=0, uInt=0) creates // an Euler with specified values //
          • Euler(Quantity, Quantity=0, Quantity=0) creates an Euler with // interpretation of angle units in the Quantities //
          • Euler(Quantum >) creates a zero expanded // Euler from at most the first three elements of Quantity // vector; with (1,2,3) //
          • Euler(Quantum >, Vector) creates a // zero expanded Euler with given values //
          // It has a unary minus operator, which reverses the sign and order of the // three angles, and the order of the axes, to produce the Euler angles // for a rotation with opposite signs, so that RotMatrix(-Euler) // will generate the inverse rotation matrix as compared with // RotMatrix(Euler).
          // getAngle() functions return the Euler angles as a Quantum vector.
          // Eulers have addition and subtraction (on the angles). Note that this // produces the correct angles for a combined rotation only if the // axes are identical.
          // A (which) operator returns the indicated angle. Set/get functions // manipulate the axes. //
          // // // // Quantity angle(25,"deg"); // 25 degrees // Euler eul(angle.get().getValue(),2); // rotate over axis 2 (radians) // RotMatrix rot(eul); // generates rotation matrix // // // // // To use generated precession and nutation results // // // // class Euler { public: //# Friends // Output Euler angles friend ostream &operator<<(ostream &os, const Euler &eul); //# Constructors // Default constructor generates zero filled Double vector of length 3, with // (1,2,3) axes Euler(); // Copy constructor Euler(const Euler &other); // Copy assignment Euler &operator=(const Euler &other); // Constructs an Euler with specified angles and (1,2,3) axes Euler(Double in0, Double in1 = 0, Double in2 = 0); // Constructs an Euler with specified angles and axes Euler(Double in0, uInt ax0, Double in1 = 0, uInt ax1=0, Double in2 = 0, uInt ax2=0); // //
        71. AipsError if non-angle units used // // Constructs an Euler from specified angle quantities // Euler(const Quantity &in0); Euler(const Quantity &in0, const Quantity &in1); Euler(const Quantity &in0, const Quantity &in1, const Quantity &in2); Euler(const Quantity &in0, uInt ax0); Euler(const Quantity &in0, uInt ax0, const Quantity &in1, uInt ax1=0); Euler(const Quantity &in0, uInt ax0, const Quantity &in1, uInt ax1, const Quantity &in2, uInt ax2=0); // Constructs an Euler (zero filled) from elements of Quantity vector // Euler(const Quantum > &in); Euler(const Quantum > &in, const Vector &ax); // // // Destructor ~Euler(); //# Operators // The unary minus reverses the sign and order of the Euler angles Euler operator-() const; // Addition and subtraction // Euler &operator+=(const Euler &right); Euler operator+(const Euler &right) const; Euler &operator-=(const Euler &right); Euler operator-(const Euler &right) const; // // Return the which' angle // Double &operator()(uInt which); const Double &operator()(uInt which) const; // //# General Member Functions // with the optional conversion units. // Quantum > getAngle() const; Quantum > getAngle(const Unit &unit) const; // // Set an axis void set(uInt which, uInt ax); // Set all axes void set(uInt ax0, uInt ax1, uInt ax2); // Get an axis Int get(uInt which) const; private: //# Data // vector with 3 Euler angles (data.first) Vector euler; // Axes (data.second) Vector axes; //# Private Member Functions // The makeRad functions check and convert the input Quantities to radians // static Double makeRad(const Quantity &in); static Vector makeRad(const Quantum > &in); // }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Quanta/MVAngle.cc000066400000000000000000000327661476623553700173020ustar00rootroot00000000000000//# MVAngle.cc: Class to handle angle type conversions and I/O //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include // #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // MVAngle class //# Static members MVAngle::Format MVAngle::defaultFormat = MVAngle::Format(); MVAngle::Format MVAngle::interimFormat = MVAngle::Format(); Bool MVAngle::interimSet = False; //# Constructors MVAngle::MVAngle() : val(0){} MVAngle::MVAngle(Double d) : val(d){} MVAngle::MVAngle(const MVAngle &other) : val(other.val) {} MVAngle::MVAngle(const Quantity &other) { static const Double factor = C::circle/C::day; val = other.getBaseValue(); if (other.check(UnitVal::ANGLE)) { } else { other.assure(UnitVal::TIME); val *= factor; } } MVAngle &MVAngle::operator=(const MVAngle &other) { if (this != &other) { val = other.val; } return *this; } // Destructor MVAngle::~MVAngle() {} // Operators MVAngle::operator Double() const { return val; } const MVAngle &MVAngle::operator()() { return (operator()(-0.5)); } const MVAngle &MVAngle::operator()(Double norm) { Double t = val/(2.0*M_PI) - norm; if (t < 0 || t >=1) { // Next statement necessary for Linux gnu; val -= expr; gives incorrect // result of order 2e-11 Double df = std::floor(t)*(2.0*M_PI); val -= df; /// val - = std::floor(t)*(2.0*M_PI); } return *this; } const MVAngle &MVAngle::operator()(const MVAngle &norm) { return (operator()(norm.circle() - 0.5)); } // Member functions MVAngle MVAngle::coAngle() const { MVAngle t = M_PI_2 - val; return (t()); } Double MVAngle::radian() const { return val; } Double MVAngle::degree() const { return val/C::degree; } Double MVAngle::circle() const { return val/C::circle; } Quantity MVAngle::get() const { return Quantity(val,"rad"); } Quantity MVAngle::get(const Unit &inunit) const { if (inunit.getValue() == UnitVal::TIME) { return Quantity(circle(), "d").get(inunit); } return Quantity(val,"rad").get(inunit); } MVAngle::Format MVAngle::setFormat(MVAngle::formatTypes intyp, uInt inprec) { Format tmp = MVAngle::defaultFormat; MVAngle::defaultFormat.typ = intyp; MVAngle::defaultFormat.prec = inprec; MVAngle::interimSet = False; return tmp; } MVAngle::Format MVAngle::setFormat(uInt intyp, uInt inprec) { return setFormat((MVAngle::formatTypes)intyp, inprec); } MVAngle::Format MVAngle::setFormat(uInt inprec) { return setFormat(MVAngle::ANGLE, inprec); } MVAngle::Format MVAngle::setFormat(const MVAngle::Format &form) { Format tmp = MVAngle::defaultFormat; MVAngle::defaultFormat = form; MVAngle::interimSet = False; return tmp; } MVAngle::Format MVAngle::getFormat() { return MVAngle::defaultFormat; } MVAngle::formatTypes MVAngle::giveMe(const String &in) { const Int N_name = 6; static const String tab[N_name] = { "ANGLE", "TIME", "CLEAN", "NO_D", "NO_DM", "DIG2" }; static const MVAngle::formatTypes nam[N_name] = { MVAngle::ANGLE, MVAngle::TIME, MVAngle::CLEAN, MVAngle::NO_D, MVAngle::NO_DM, MVAngle::DIG2 }; Int t = MUString::minimaxNC(in, N_name, tab); return (t 12) { t -= 24.0; } } if ((intyp & MVAngle::ALPHA) == MVAngle::ALPHA) { sep1 = 'h'; } else { sep1 = ':'; sep2 = ':'; } } if (inprec == 0) inprec = oss.precision(); Char sfill = oss.fill(); t1 = 1.0; if (inprec > 2) t1 /= 60.; if (inprec > 4) t1 /= 60.; // The next (Double)s necessary for wrong choice of pow if (inprec > 6) t1 /= std::pow(Double(10), Double(inprec-6)); if (i1 == MVAngle::ANGLE || ((intyp & MVAngle::DIG2) == MVAngle::DIG2)) { if (t < 0) { oss << '-'; } else if ((intyp & MVAngle::CLEAN) != MVAngle::CLEAN || ((intyp & MVAngle::DIG2) == MVAngle::DIG2)) { oss << '+'; } else { oss << ' '; } } // The next 0.1 necessary for some rounding errors t = std::abs((std::floor(std::abs(t)/t1+0.5)+0.1)*t1); Int h = ifloor(t); if ((intyp & MVAngle::NO_D) != MVAngle::NO_D) { if (i1 == MVAngle::ANGLE) { if ((intyp & MVAngle::DIG2) != MVAngle::DIG2) { if (h > 999) { oss << "***"; } else if ((intyp & MVAngle::CLEAN) != MVAngle::CLEAN) { oss << setfill('0') << setw(3) << h; } else { oss << setfill(' ') << setw(3) << h; } } else { if (h > 99) { oss << "**"; } else if ((intyp & MVAngle::CLEAN) != MVAngle::CLEAN) { oss << setfill('0') << setw(2) << h; } else { oss << setfill(' ') << setw(2) << h; } } } else { if (h > 99) { oss << "**"; } else { oss << setfill('0') << setw(2) << h; } } if ((inprec > 2) || ((intyp & MVAngle::CLEAN) != MVAngle::CLEAN)) { oss << sep1; } } else if ((intyp & MVAngle::CLEAN) != MVAngle::CLEAN) { oss << sep1; } if (inprec > 2) { t = std::fmod(t,1.0) *60.; h = ifloor(t); if ((intyp & MVAngle::NO_DM) != MVAngle::NO_DM) { oss << setfill('0') << setw(2) << h; if ((inprec > 4) || ((intyp & MVAngle::CLEAN) != MVAngle::CLEAN)) { oss << sep2; } } else if ((intyp & MVAngle::CLEAN) != MVAngle::CLEAN) { oss << sep2; } } else if ((intyp & MVAngle::CLEAN) != MVAngle::CLEAN) { oss << sep2; } if (inprec > 4 && inprec < 7) { t = std::fmod(t,1.0) *60.; h = ifloor(t); oss << setfill('0') << setw(2) << h; } if (inprec > 6) { t = std::abs((std::fmod(t, 1.0) - 6.0*t1)*60.); // The following was necessary since abs(0) becomes -0 (Solaris at least) if (t <= 0.0) t = 0; Int oprec = oss.precision(); ios::fmtflags oldb = oss.setf(ios::fixed,ios::floatfield); oss << setfill('0') << setprecision(inprec-6) << setw(inprec-3) << t << setprecision(oprec); oss.setf(oldb,ios::floatfield); } if ((intyp & MVAngle::FITS) == MVAngle::FITS) { if ((intyp & MVAngle::LOCAL) == MVAngle::LOCAL) { MVAngle my = MVAngle::timeZone() * C::circle; my.print(oss, MVAngle::Format(MVAngle::TIME_CLEAN | MVAngle::DIG2, 4), True); } } oss.fill(sfill); } const MVAngle &MVAngle::binorm(Double norm) { Double t = val/M_PI - norm; if (t < 0 || t >=1) { Double df = std::floor(t)*M_PI; val -= df; } return *this; } Bool MVAngle::unitString(UnitVal &uv, String &us, MUString &in) { in.skipBlank(); us = in.get(); return UnitVal::check(in.get(),uv); } Bool MVAngle::handleReadError(MUString& in, Bool throwExcp) { if (throwExcp) { throw AipsError("Invalid date/time '" + in.get(0) + "', invalid char about pos " + String::toString(in.getPtr())); } in.pop(); return False; } Bool MVAngle::read(Quantity &res, MUString &in, Bool chk) { return read (res, in, chk, False); } Bool MVAngle::read(Quantity &res, MUString &in, Bool chk, Bool throwExcp) { LogIO os(LogOrigin("MVAngle", "read()", WHERE)); res = Quantity(0.0, "rad"); in.skipBlank(); in.push(); // Save position Double s = in.getSign(); Double r = in.getuInt(); Int tp = 0; if (in.testChar('.')) { in.skipChar(); Double r1 = in.getuInt(); if (in.testChar('.')) { in.skipChar(); r += r1/60.0 + in.getDouble()/3600.0; r *= s; tp = 4; } }else if (in.tSkipOneCharNC('d')) { tp = 1; } else if (in.tSkipOneCharNC('h')) { tp = 2; } else if (in.tSkipOneChar(':')) { tp = 3; } switch (tp) { case 1: case 2: case 3: { if (in.testCharNC('m') || in.testCharNC(':')) { tp = 0; } else { Char tc = 'm'; if (tp == 3) tc = ':'; in.push(); Double r1 = in.getuInt(); // Test if : or m is given. // If not, something like 11:23 was given which cannot be followed by // a dot (otherwise decimal minutes would be given). // Also a slash and alpha are not possible after 11:23. // However, Z is accepted because it is the UTC time zone designator. // The reason is that Quantum::read first tries to read a string as an // MVAngle, thereafter as MVTime, and finally as value+unit. // So MVAngle::read has to be very strict in what it accepts to avoid // that it does not accept something that is meant to be something else. if (in.tSkipOneCharNC(tc)) { r += r1/60.0 + in.getDouble()/3600.; if (tp != 3) in.tSkipOneCharNC('s'); } else if (tp == 3 && (in.testChar('Z') || (!in.testChar('.') && !in.testChar('/') && !in.testAlpha()))) { r += r1/60.0; } else if ( !(tp == 1 && r1 == 0 && !in.testChar('.') && !in.testChar('/'))) { // Accept something like 10d (meaning 10deg). // Normally the d in 10d would be a unit (day), but in this way // it is accepted as 10deg (which is what people wanted). // Note: 10.d is 10 days. tp = 0; } in.unpush(); r *= s; } } break; default: break; } if (chk) { in.skipBlank(); if (!in.eos()) tp = 0; // incorrect } switch (tp) { case 1: case 4: res = Quantity(r,"deg"); break; case 2: case 3: res = Quantity(Quantity(r/240.,"h").getBaseValue(), "deg"); break; default: return handleReadError (in, throwExcp); break; } in.unpush(); return True; } Bool MVAngle::read(Quantity &res, const String &in, Bool chk) { return read (res, in, chk, False); } Bool MVAngle::read(Quantity &res, const String &in, Bool chk, Bool throwExcp) { MUString tmp(in); // Pointed non-const String if (!MVAngle::read(res, tmp, chk, throwExcp)) { Double r = tmp.getDouble(); UnitVal u; String us; if (!MVAngle::unitString(u,us,tmp)) { return handleReadError(tmp, throwExcp); } if (u == UnitVal::NODIM) { res = Quantity(r,"rad"); } else if (u == UnitVal::ANGLE) { res = Quantity(r,us); } else if (u == UnitVal::TIME) { res = Quantity(Quantity(r/240.,us).getBaseValue(), "deg"); } else { return handleReadError (tmp, throwExcp); } } return True; } ostream &operator<<(ostream &os, const MVAngle &meas) { if (MVAngle::interimSet) { MVAngle::interimSet = False; meas.print(os, MVAngle::interimFormat); } else { meas.print(os, MVAngle::defaultFormat); } return os; } istream &operator>>(istream &is, MVAngle &meas) { String str; is >> str; if (ios::failbit & is.rdstate()) return is; Quantity t; if (MVAngle::read(t, str)) { meas = MVAngle(t)(); } else { is.clear(ios::failbit | is.rdstate()); } return is; } ostream &operator<<(ostream &os, const MVAngle::Format &form) { MVAngle::interimFormat = form; MVAngle::interimSet = True; return os; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Quanta/MVAngle.h000066400000000000000000000401041476623553700171250ustar00rootroot00000000000000//# MVAngle.h: Class to handle angle type conversions and I/O //# Copyright (C) 1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MVANGLE_H #define CASA_MVANGLE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class String; class MUString; //# Constants (SUN compiler does not accept non-simple default arguments) // // Class to handle angle type conversions and I/O // // // // // //
        72. Quantum // // // // From Measure, Value and Angle // // // // An MVAngle is a simple Double, to be used for angle conversions and I/O. // It can be constructed from a Double (in which case radians are assumed), // or from a Quantity (Quantum). Quantities must be in // either angle or time units.
          // It has an automatic conversion to Double, so all standard mathematical // operations can operate on it.
          // The class has a number of special member operations: //
            //
          • MVAngle operator() will normalise the angle between // -180 and +180(inclusive) degrees and return the value //
          • MVAngle operator(Double) will normalise the angle // using the value specified (and return the value) // in fractions of a circle (this was chosen rather than radians to make // for easier and more precise programming) as a lower bound. I.e. // (-0.5) will normalise between -180 and +180 degrees, (0.) between // 0 and 360 degrees, (-0.25) between -90 and +270 degrees, // (5.) between 1800 and 2160 dgrees. //
          • MVAngle operator(MVAngle) will normalise (and // return the normalised value) to within 180 degrees of the // argument value. This is useful for making a range of angles // contiguous. //
          • MVAngle binorm(Double) will normalise the angle in // steps of 180 degrees. // using the value specified (and return the value) // in fractions of 180 degrees (this was chosen rather than radians to make // for easier and more precise programming) as a lower bound. I.e. // (-0.5) will normalise between -90 and +90 degrees, (0.) between // 0 and 180 degrees, (10.) between 1800 and 1980 dgrees. //
          • Double radian() will return value in radians //
          • Double degree() will return value in degrees //
          • Double circle() will return value in fraction of circles //
          • MVAngle coAngle() will return 90-angle (or rather // pi/2 - angle), with (0) normalisation. //
          • Quantity get() will return radians //
          • Quantity get(Unit) will return in specified units // (angle or time) //
          // Output formatting is done with the << statement, with the // following rules: //
            //
          • standard output is done in the following format: // +ddd.mm.ss.tt with a floating sign. The number of // digits presented will be based on the precision attached to the // current stream //
          • output can be formatted by using either the setFormat() // method for global angle format setting, or the output of // MVAngle::Format() data for a once off change (see later). // Formats have a first argument which // determines the type (default, if not given, MVAngle::ANGLE, other // possibility MVAngle::TIME (as hh:mm:ss.tt..), // the second the number of digits wanted (default stream precision), // with a value: //
              //
            • <3 : ddd.. only //
            • <5 : ddd.mm. //
            • <7 : ddd.mm.ss //
            • >6 : with precision-6 t's added //
            // comparable for time. The added periods are to enable input // checking of the format. Look at the 'clean' types to bypass them. // // The output format can be modified with modifiers (specify as // MVAngle::ANGLE | MVAngle::MOD (or + MVAngle::MOD)). // // For overloading/casting problems with some compilers, the // use of modifiers necessitates either the presence of a precision // (i.e. (A|B, prec)), or an explicit cast: // ((MVAngle::formatTypes)(A|B)), or make use of // the provided ANGLE[_CLEAN][_NO_D[M]] and // TIME[_CLEAN][_NO_H[M]]. // // // The modifiers can be: //
              //
            • MVAngle::CLEAN to suppress leading or trailing // periods (or colons for TIME), + and leading zeroes in degree // field for angle representation will be replaced with a space. // Note that the result can not be read automatically. //
            • MVAngle::NO_D (or NO_H) to suppress // the output of degrees (or hours): useful for offsets //
            • MVAngle::NO_DM (or NO_HM), to // suppress the degrees and minutes. //
            • MVAngle::DIG2 to allow only 2 digits for degrees, // or -12 - +12 range for hours //
            • MVAngle::LOCAL to indicate local time to FITS // formatting only //
            • MVAngle::FITS to produce, if // LOCAL set, the time zone (note that if local set // here, as opposed to in MVTime) the angle is supposed // to be in local time already). //
            • MVAngle::ALPHA to use d (or h) and m instead of // periods or colons. //
            // Output in formats like 20' can be done via the standard // Quantum output (e.g. stream << angle.get("'") ). //
          • Available formats: //
              //
            • MVAngle::ANGLE in +ddd.mm.ss.ttt format //
            • MVAngle::TIME in hh:mm:ss.ttt format //
            • MVAngle::[ANGLE|TIME]_CLEAN format without superfluous periods //
            • MVAngle::[ANGLE|TIME][_CLEAN]_NO_[D|H][M] in format with // leading zero fields left empty. //
            • MVAngle::CLEAN modifier for suppressing superfluous periods //
            • MVAngle::NO_[D|H][M] modifier to suppress first field(s) //
            • MVAngle::DIG2 modifier to output in +dd.mm.ss.ttt format or // in time format in range -12 to +12h //
            //
          // The default formatting can be overwritten by a // MVAngle::setFormat(); statement; which returns an // MVAngle::Format // structure, that can be used in a subsequent one to reset to previous. // The format set holds for all MVAngle output on all streams.
          // Temporary formats (i.e. for one MVAngle output only), can be set by // outputting a format (i.e. stream << MVAngle::Format() << ... ). // A setFormat() will also reset any lingering temporary format. // A setFormat(getFormat()) will reset without changing. Problems could // arise in parallel processors. // Input can be read if the values are in any of the above (non-clean) output // formats.
          // For other formatting practice, the output can be written to a String with // the string() member function.
          // Note that using a temporary format is inherently thread-unsafe because // the format is kept in a static variable. Another thread may overwrite // the format just set. The only thread-safe way to format an MVTime is using // a print or string that accepts a Format object. // // Strings and input can be converted to an MVAngle (or Quantity) by // Bool read(Quantity &out, const String &in) and // istream >> MVAngle &. In the latter case the actual // reading is done by the String read, which reads between white-spaces.
          // The following input formats (note no blanks allowed) are supported // (+stands for an optional + or -; v for an unsigned integer; dv for a // floating number. [] indicate optional values. Separating codes are // case insensitive): //
            //
          • +[v].[v].[dv] -- value in deg, arcmin, arcsec //
          • +[v]D[v[M[dv]]] -- value in deg, arcmin, arcsec //
          • +[v]:[v[:[dv]]] -- value in h, min, s //
          • +[v]H[v[M[dv]]] -- value in h, min, s //
          • +[v]{D|H|:}[dv] -- value in deg (or h), arcmin (or min) //
          • +dv[unit string] -- value in time or angle units. rad default //
          // Examples of valid strings: // // 5::2.59 5h + 0min + 2.59 s // 5..2.59 5deg + 0arcmin + 2.59arcsec // 5.259 5.259 rad // 5..259 5deg + 259arcsec // 5.259a 5.259 * pi * 2 *365.25 rad (normalised) // // In general the input will be read as a Quantity. // Reading of Quantities will always try to read special formats (like // MVAngle, MVTime) first. In // that case problems could arise converting strings like 5d, 5::, 5hm, 5dm. // In 'angle' mode they could have meant to be // 5d0m, 5:0:, 5h0m, 5d0m, but they could have // meant: days, min, hectometre, decimetre. In the same vain 5d2 could have // meant 5d2m or 5 d2. // To try to guess the general use, the following interpretation is made: //
            //
          • 5d, 5:: == 5deg, 5h0m; make float (like 5.d) to make it days/min //
          • 5dm, 5hm == decimetre, hectometre; use 5d0m 5h0m for // angle //
          • 5d2, 5h2, 5:2 == 5d2m, 5h2m, 5:2:; use float 5 or explicit () for // other interpretation //
          //
          //
          // // // See synopsis // // // // To be able to format angle-like values in user-required ways. // // // //
        73. Use AipsrcData once moved to aips from trial // class MVAngle { public: //# Enumerations (should mimic those in MVTime) // Format types enum formatTypes { ANGLE, TIME, CLEAN = 4, NO_D = 8, NO_DM = NO_D+16, DIG2 = 1024, FITS = TIME+2048, LOCAL = 4096, USE_SPACE = 8192, //# Only for MVTIme compatibility ALPHA = 16384, NO_H = NO_D, NO_HM = NO_DM, ANGLE_CLEAN = ANGLE + CLEAN, ANGLE_NO_D = ANGLE + NO_D, ANGLE_NO_DM = ANGLE + NO_DM, ANGLE_CLEAN_NO_D = ANGLE + CLEAN + NO_D, ANGLE_CLEAN_NO_DM = ANGLE + CLEAN + NO_DM, TIME_CLEAN = TIME + CLEAN, TIME_NO_H = TIME + NO_H, TIME_NO_HM = TIME + NO_HM, TIME_CLEAN_NO_H = TIME + CLEAN + NO_H, TIME_CLEAN_NO_HM = TIME + CLEAN + NO_HM , MOD_MASK = CLEAN + NO_DM + DIG2 + LOCAL + USE_SPACE + ALPHA}; //# Local structure // Format structure class Format { public: friend class MVAngle; Format(MVAngle::formatTypes intyp = MVAngle::ANGLE, uInt inprec = 0) : typ(intyp), prec(inprec) {;}; Format(uInt inprec) : typ(MVAngle::ANGLE), prec(inprec) {;}; // Construct from type and precision (present due to overlaoding problems) Format(uInt intyp, uInt inprec) : typ((MVAngle::formatTypes) intyp), prec(inprec) {;}; private: MVAngle::formatTypes typ; uInt prec; }; //# Friends // Output an angle friend ostream &operator<<(ostream &os, const MVAngle &meas); // Input an angle friend istream &operator>>(istream &is, MVAngle &meas); // Set a temporary format friend ostream &operator<<(ostream &os, const MVAngle::Format &form); //# Constructors // Default constructor: generate a zero value MVAngle(); // Copy constructor MVAngle(const MVAngle &other); // Copy assignment MVAngle &operator=(const MVAngle &other); // Constructor from Double MVAngle(Double d); // Constructor from Quantum : value can be an angle or time // //
        74. AipsError if not a time or angle // MVAngle(const Quantity &other); // Destructor ~MVAngle(); //# Operators // Conversion operator operator Double() const; // Normalisation between -180 and +180 degrees (-pi and +pi) const MVAngle &operator()(); // Normalisation between 2pi*norm and 2pi*norm + 2pi const MVAngle &operator()(Double norm); // Normalisation between norm-pi and norm+pi const MVAngle &operator()(const MVAngle &norm); //# General member functions // Normalisation between pi*norm and pi*norm + pi const MVAngle &binorm(Double norm); // Check if String unit static Bool unitString(UnitVal &uv, String &us, MUString &in); // Make res angle Quantity from string in angle/time-like format. In the // case of String input, also quantities are recognised. // chk=True means that the entire string should be consumed. // throwExcp=True means that an exception is thrown in case of an error. // static Bool read(Quantity &res, const String &in, Bool chk=True); static Bool read(Quantity &res, MUString &in, Bool chk=True); static Bool read(Quantity &res, const String &in, Bool chk, Bool throwExcp); static Bool read(Quantity &res, MUString &in, Bool chk, Bool throwExcp); // // Handle a read error. An exception is thrown if indicated so. // Otherwise in.pop() is called and False is returned. static Bool handleReadError(MUString& in, Bool throwExcp); // Make co-angle (e.g. zenith distance from elevation) MVAngle coAngle() const; // Get value in given unit // Double radian() const; Double degree() const; Double circle() const; Quantity get() const; Quantity get(const Unit &inunit) const; // // Output data // // The first function below is thread-unsafe because it uses the result of // the setFormat function which changes a static class member. // The other functions are thread-safe because the format is directly given. // // String string() const; String string(MVAngle::formatTypes intyp, uInt inprec = 0) const; String string(uInt intyp, uInt inprec) const; String string(uInt inprec) const; String string(const MVAngle::Format &form) const; void print(ostream &oss, const MVAngle::Format &form) const; void print(ostream &oss, const MVAngle::Format &form, Bool loc) const; // // Set default format // // It is thread-unsafe to print using the setFormat functions because they // change a static class member. The only thred-safe way to print a time is // to use the print function above. // // static Format setFormat(MVAngle::formatTypes intyp, uInt inprec = 0); static Format setFormat(uInt intyp, uInt inprec); static Format setFormat(uInt inprec = 0); static Format setFormat(const Format &form); // // Get default format static Format getFormat(); // Get code belonging to string. 0 if not known static MVAngle::formatTypes giveMe(const String &in); // Get time zone offset (in days) static Double timeZone(); private: //# Data // Value Double val; // Default format static MVAngle::Format defaultFormat; // Temporary format // static MVAngle::Format interimFormat; static Bool interimSet; // //# Member functions }; // Global functions // Global output/input functions // Output/Input // ostream &operator<<(ostream &os, const MVAngle &meas); istream &operator>>(istream &is, MVAngle &meas); // Set a temporary format (thread-unsafe). ostream &operator<<(ostream &os, const MVAngle::Format &form); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Quanta/MVBaseline.cc000066400000000000000000000236161476623553700177700ustar00rootroot00000000000000//# MVBaseline.cc: A 3D vector on Earth //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // MVBaseline class //# Constructors MVBaseline::MVBaseline() : MVPosition() {} MVBaseline::MVBaseline(Double in) : MVPosition(in) {} MVBaseline::MVBaseline(const Quantity &l) : MVPosition(l) {} MVBaseline::MVBaseline(Double in0, Double in1, Double in2) : MVPosition(in0, in1, in2) {} MVBaseline::MVBaseline(const Quantity &l, Double angle0, Double angle1) : MVPosition(l, angle0, angle1) {} MVBaseline::MVBaseline(const Quantity &l, const Quantity &angle0, const Quantity &angle1) : MVPosition(l, angle0, angle1) {} MVBaseline::MVBaseline(const Quantum > &angle) : MVPosition(angle) {} MVBaseline::MVBaseline(const Quantity &l, const Quantum > &angle) : MVPosition(l, angle) {} MVBaseline::MVBaseline(const Vector &other) : MVPosition(other) {} MVBaseline::MVBaseline(const Vector &other) : MVPosition() { if (!putValue(other)) { throw (AipsError("Illegal quantum vector in MVBaseline constructor")); } } MVBaseline::MVBaseline(const MVPosition &pos, const MVPosition &base) : MVPosition(pos) { xyz -= base.getValue(); } MVBaseline::MVBaseline(const MVPosition &other) : MVPosition(other) {} //# Operators Bool MVBaseline:: operator==(const MVBaseline &other) const { return (allEQ(xyz, other.xyz)); } Bool MVBaseline:: operator!=(const MVBaseline &other) const { return (!(*this == other)); } Bool MVBaseline:: near(const MVBaseline &other, Double tol) const { return (allNear(xyz, other.xyz, tol)); } Bool MVBaseline:: near(const MVBaseline &other, Quantity tol) const { return (separation(other,"rad") <= tol); } Bool MVBaseline:: nearAbs(const MVBaseline &other, Double tol) const { return (allNearAbs(xyz, other.xyz, tol)); } Double MVBaseline:: operator*(const MVBaseline &other) const { Double tmp = 0.0; for (Int i=0; i<3; i++) { tmp += xyz(i) * other.xyz(i); } return tmp; } MVBaseline MVBaseline::operator-() const { MVBaseline tmp; tmp = *this; tmp.xyz = -xyz; return tmp; } MVBaseline &MVBaseline::operator+=(const MVBaseline &right) { xyz += right.xyz; return *this; } MVBaseline MVBaseline::operator+(const MVBaseline &right) const { MVBaseline tmp = *this; tmp += right; return tmp; } MVBaseline &MVBaseline::operator-=(const MVBaseline &right) { xyz -= right.xyz; return *this; } MVBaseline MVBaseline::operator-(const MVBaseline &right) const{ MVBaseline tmp = *this; tmp -= right; return tmp; } //# Member functions void MVBaseline::assure(const MeasValue &in) { if (!dynamic_cast(&in)) { throw(AipsError("Illegal MeasValue type argument: MVBaseline")); } } void MVBaseline::adjust() {} void MVBaseline::adjust(Double &res) { res = std::sqrt(operator*(*this)); if (res != 0.0 && res != 1.0) { xyz /= res; } } void MVBaseline::readjust(Double res) { if (res == 0.0) { xyz *= 1e-12; } else { xyz *= res; } } Double MVBaseline::radius() { return (std::sqrt(operator*(*this))); } Vector MVBaseline::get() const{ Vector tmp(3); tmp(0) = std::sqrt(operator*(*this)); Double ln = (tmp(0) == 0.0 ? 1.0 : tmp(0)); Double loc = xyz(0)/ln; if (loc == 0) { tmp(1) = std::asin(xyz(1)/ln); } else { tmp(1) = std::atan2(xyz(1),xyz(0)); } tmp(2) = std::asin(xyz(2)/ln); return tmp; } const Vector &MVBaseline::getValue() const { return xyz; } Quantum > MVBaseline::getAngle() const{ Vector tp(3), tmp(2); tp = get(); tmp(0) = tp(1); tmp(1) = tp(2); return Quantum >(tmp,"rad"); } Quantum > MVBaseline::getAngle(const Unit &unit) const{ return getAngle().get(unit); } Quantity MVBaseline::getLength() const{ Double tmp = std::sqrt(operator*(*this)); return Quantity(tmp,"m"); } Quantity MVBaseline::getLength(const Unit &unit) const { return getLength().get(unit); } Double MVBaseline::BaselineAngle(const MVBaseline &other) const { Vector t1(3); Vector t2(3); t1 = get(); t2 = other.get(); Double s1,c1; c1 = std::cos(t1(2)) * std::sin(t2(2)) - std::sin(t1(2)) * std::cos(t2(2)) * std::cos(t1(1) - t2(1)); s1 = -std::cos(t2(2)) * std::sin(t1(1) - t2(1)); if (s1 != 0 || c1 != 0) { return std::atan2(s1, c1); } else { return Double(0.0); } } Quantity MVBaseline::BaselineAngle(const MVBaseline &other, const Unit &unit) const { return Quantity(BaselineAngle(other), "rad").get(unit); } Double MVBaseline::separation(const MVBaseline &other) const { MVBaseline t1(*this); MVBaseline t2(other); t1.adjust(); t2.adjust(); t1 -= t2; Double d1 = t1.radius()/2.0; d1 = (d1 < 1.0 ? d1 : 1.0); return (2*std::asin(d1)); } Quantity MVBaseline::separation(const MVBaseline &other, const Unit &unit) const { return Quantity(separation(other), "rad").get(unit); } MVBaseline MVBaseline::crossProduct(const MVBaseline &other) const { MVBaseline res; res(0) = xyz(1)*other(2) - xyz(2)*other(1); res(1) = xyz(2)*other(0) - xyz(0)*other(2); res(2) = xyz(0)*other(1) - xyz(1)*other(0); return res; } void MVBaseline::print(ostream &os) const { os << getValue(); } MeasValue *MVBaseline::clone() const { return (new MVBaseline(*this)); } Vector MVBaseline::getVector() const { return xyz; } void MVBaseline::putVector(const Vector &in) { if (in.nelements() == 3) { xyz = in; } else { xyz = 0.0; for (uInt i=0; i > MVBaseline::getRecordValue() const { Vector t(3); t = get(); Vector > tmp(3); tmp(2) = Quantity(t(0), "m"); tmp(0) = Quantity(t(1), "rad"); tmp(1) = Quantity(t(2), "rad"); return tmp; } Vector > MVBaseline::getXRecordValue() const { Vector > tmp(3); tmp(0) = Quantity(xyz(0), "m"); tmp(1) = Quantity(xyz(1), "m"); tmp(2) = Quantity(xyz(2), "m"); return tmp; } Bool MVBaseline::putValue(const Vector > &in) { uInt i = in.nelements(); if (i != 3 ) return False; if (in(0).check(UnitVal::LENGTH)) { if (in(1).check(UnitVal::LENGTH) && in(2).check(UnitVal::LENGTH)) { for (uInt j = 0; j tsin(2), tcos(2); for (uInt j=1; j < i; j++) { tsin(j-1) = (sin(in(j))).getValue(); tcos(j-1) = (cos(in(j))).getValue(); } xyz = Double(0.0); xyz(0) = tcos(0) * tcos(1); xyz(1) = tsin(0) * tcos(1); xyz(2) = tsin(1); readjust(in(0).getBaseValue()); } else { return False; } } else if (in(2).check(UnitVal::LENGTH)) { if (in(0).check(UnitVal::ANGLE) && in(1).check(UnitVal::ANGLE)) { Vector tsin(2), tcos(2); Int j; for (j=0; j < 2; j++) { tsin(j) = (sin(in(j))).getValue(); tcos(j) = (cos(in(j))).getValue(); } xyz = Double(0.0); xyz(0) = tcos(0) * tcos(1); xyz(1) = tsin(0) * tcos(1); xyz(2) = tsin(1); readjust(in(2).getBaseValue()); } else { return False; } } return True; } MVBaseline operator*(const RotMatrix &left, const MVBaseline &right) { MVBaseline result; for (Int i=0; i<3; i++) { result(i) = 0; for (Int j=0; j<3; j++) { result(i) += left(i,j) * right(j); } } return result; } MVBaseline operator*(const MVBaseline &left, const RotMatrix &right) { MVBaseline result(left); result *= right; return result; } MVBaseline operator*(Double left, const MVBaseline &right) { MVBaseline result(right); result *= left; return result; } MVBaseline operator*(const MVBaseline &left, Double right) { MVBaseline result(left); result *= right; return result; } Double operator*(const Vector &left, const MVBaseline &right) { MVBaseline tmp(left); return (tmp * right); } Double operator*(const MVBaseline &left, const Vector &right) { MVBaseline tmp(right); return (tmp * left); } Double operator*(const MVPosition &left, const MVBaseline &right) { MVBaseline tmp(left); return (tmp * right); } Double operator*(const MVBaseline &left, const MVPosition &right) { MVBaseline tmp(right); return (tmp * left); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Quanta/MVBaseline.h000066400000000000000000000223131476623553700176230ustar00rootroot00000000000000//# MVBaseline.h: A 3D vector on Earth //# Copyright (C) 1998,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MVBASELINE_H #define CASA_MVBASELINE_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // A 3D vector on Earth // // // // //
        75. MeasValue // // // // From Measure, Value and Baseline // // // // A MVBaseline is a 3-vector of Baselines in a rectangular frame with // internal units of m.
          // It can be constructed with: //
            //
          • MVBaseline() creates point at origin (0,0,0) //
          • MVBaseline(MVBaseline) creates a copy //
          • MVBaseline(MVPosition) creates (x,y,z) from the given position //
          • MVBaseline(Double, Double, Double) creates (x,y,z) with // specified values (assuming meters) //
          • MVBaseline(Quantity length,Double, Double) creates a MVBaseline assuming // that the two values are (in radians) angle along 'equator' // and towards 'pole'. //
          • MVBaseline(Quantity length, Quantity, Quantity) creates a MVBaseline // assuming angles as in previous, or Baselines //
          • MVBaseline(Quantity, Quantum >) creates a // MVBaseline from angle vector, using first two angles, and // assuming second as zero if not present. //
          • MVBaseline(Quantum > creates from // angles or Baselines, depending on the units in the // quantum vector. In the angle case, // the data derived can be scaled with the readjust() function. If // the unit of the quantum vector is length, Baseline is // assumed. //
          • MVBaseline(Vector creates from angles (less than // or equal to two elements) or x,y,z (3 elements). //
          • MVBaseline(Vector creates from length+angles, // angles, or x,y,z, depending on units. //
          • MVBaseline(MVPosition, MVPosition) creates a baseline // pointing from second to first MVPosition //
          • MVBaseline(MVPosition) creates a baseline as defined by the // position given (e.g. as derived from an offset MPosition) //
          // A void adjust(Double) function normalises the vector to a length of 1; // a get() returns as a // Double 3-vector the length and angles of the Baseline vector; // a getAngle() returns a Quantum 2-vector, (uInt) returns the indicated // element, and getValue returns the vector.
          // Baselines can be added and subtracted.
          // The multiplication of two Baselines produces the in-product.
          //
          // // // See MBaseline class. // // // // To do coordinate transformations // // // //
        76. Nothing I know of // class MVBaseline : public MVPosition { public: //# Friends //# Constructors // Default constructor generates a (0,0,0) Baseline MVBaseline(); // Creates from an MVPosition MVBaseline(const MVPosition &other); // Creates a specified vector MVBaseline(Double in0, Double in1, Double in2); // Creates a vector with specified length towards pole // explicit MVBaseline(Double in0); MVBaseline(const Quantity &l); // // Creates the Baseline from specified (azimuth,elevation) angles and length MVBaseline(const Quantity &l, Double angle0, Double angle1); // Creates the Baseline from specified angles and length. or Baselines // //
        77. AipsError if quantities not in angle format // // MVBaseline(const Quantity &l, const Quantity &angle0, const Quantity &angle1); // If not enough angles: pole assumed (if none), or elevation =0 (if 1) MVBaseline(const Quantum > &angle); MVBaseline(const Quantity &l, const Quantum > &angle); // // Create from specified length and/or angles and/or Baseline // MVBaseline(const Vector &other); MVBaseline(const Vector &other); // // Baseline as difference between positions (first - second (default(0,0,0)) // MVBaseline(const MVPosition &pos, const MVPosition &base); // //# Operators // Multiplication defined as in-product // Double operator*(const MVBaseline &other) const; // // Equality comparisons // Bool operator== (const MVBaseline &other) const; Bool operator!= (const MVBaseline &other) const; Bool near(const MVBaseline &other, Double tol=1e-13) const; Bool near(const MVBaseline &other, Quantity tol) const; Bool nearAbs(const MVBaseline &other, Double tol=1e-13) const; // // Addition and subtraction // MVBaseline operator-() const; MVBaseline &operator+=(const MVBaseline &right); MVBaseline operator+(const MVBaseline &right) const; MVBaseline &operator-=(const MVBaseline &right); MVBaseline operator-(const MVBaseline &right) const; // //# General Member Functions // Tell me your type // static void assure(const MeasValue &in); // // Normalise direction aspects by adjusting the length to 1 // virtual void adjust(); virtual void adjust(Double &res); virtual void readjust(Double res); // // Get radius of Baseline virtual Double radius(); // Generate a 3-vector of coordinates (length(m), angles(rad)) Vector get() const; // Generate a 3-vector of x,y,z in m const Vector &getValue() const; // Generate angle 2-vector (in rad) Quantum > getAngle() const; // and with specified units Quantum > getAngle(const Unit &unit) const; // Generate the length Quantity getLength() const; // and generate it with the specified units Quantity getLength(const Unit &unit) const; // Get the Baseline angle between the directions. I.e. the angle between // the direction from one to the pole, and from one to the other. // Double BaselineAngle(const MVBaseline &other) const; Quantity BaselineAngle(const MVBaseline &other, const Unit &unit) const; // // Get the angular separation between two directions. // Double separation(const MVBaseline &other) const; Quantity separation(const MVBaseline &other, const Unit &unit) const; // // Produce the cross product MVBaseline crossProduct(const MVBaseline &other) const; // Print data virtual void print(ostream &os) const; // Clone virtual MeasValue *clone() const; // Get the value in internal units virtual Vector getVector() const; // Set the value from internal units (set 0 for empty vector) virtual void putVector(const Vector &in); // Get the internal value as a Vector. Usable in // records. The getXRecordValue() gets additional information for records. // Note that the Vectors could be empty. // virtual Vector > getRecordValue() const; virtual Vector > getXRecordValue() const; virtual Vector > getTMRecordValue() const { return getXRecordValue(); } ; // // Set the internal value if correct values and dimensions virtual Bool putValue(const Vector > &in); }; //# Global functions // Rotate a Baseline vector with rotation matrix and other multiplications // MVBaseline operator*(const RotMatrix &left, const MVBaseline &right); MVBaseline operator*(const MVBaseline &left, const RotMatrix &right); MVBaseline operator*(Double left, const MVBaseline &right); MVBaseline operator*(const MVBaseline &left, Double right); Double operator*(const Vector &left, const MVBaseline &right); Double operator*(const MVBaseline &left, const Vector &right); Double operator*(const MVPosition &left, const MVBaseline &right); Double operator*(const MVBaseline &left, const MVPosition &right); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Quanta/MVDirection.cc000066400000000000000000000252171476623553700201650ustar00rootroot00000000000000//# MVDirection.cc: Vector of three direction cosines //# Copyright (C) 1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // MVDirection class //# Constructors MVDirection::MVDirection() : MVPosition() { xyz(2) = Double(1.0); } MVDirection::MVDirection(const MVPosition &other) : MVPosition(other) {} MVDirection::MVDirection(Double in0, Double in1, Double in2) : MVPosition(in0,in1,in2) { adjust(); } MVDirection::MVDirection(Double in0) : MVPosition() { xyz(0) = std::cos(in0); xyz(1) = std::sin(in0); } MVDirection::MVDirection(Double angle0, Double angle1) : MVPosition() { Double loc = std::cos(angle1); xyz(0) = std::cos(angle0)*loc; xyz(1) = std::sin(angle0)*loc; xyz(2) = std::sin(angle1); } MVDirection::MVDirection(const Quantity &angle0) : MVPosition() { xyz(0) = ((cos(angle0)).getValue()); xyz(1) = ((sin(angle0)).getValue()); xyz(2) = 0; } MVDirection::MVDirection(const Quantity &angle0, const Quantity &angle1) : MVPosition() { Double loc = (cos(angle1)).getValue(); xyz(0) = ((cos(angle0)).getValue()) * loc; xyz(1) = ((sin(angle0)).getValue()) * loc; xyz(2) = (sin(angle1)).getValue(); } MVDirection::MVDirection(const Quantum > &angle) : MVPosition(angle) { adjust(); } MVDirection::MVDirection(const Vector &angle) : MVPosition(angle) { adjust(); } MVDirection::MVDirection(const Vector &angle) : MVPosition() { if (!putValue(angle)) { throw (AipsError("Illegal quantum vector in MVDirection constructor")); } } //# Operators MVDirection &MVDirection::operator+=(const MVDirection &right) { xyz += right.xyz; adjust(); return *this; } MVDirection MVDirection::operator+(const MVDirection &right) const{ MVDirection tmp = *this; tmp += right; return tmp; } MVDirection &MVDirection::operator-=(const MVDirection &right) { xyz -= right.xyz; adjust(); return *this; } MVDirection MVDirection::operator-(const MVDirection &right) const{ MVDirection tmp = *this; tmp -= right; return tmp; } //# Member functions void MVDirection::assure(const MeasValue &in) { if (!dynamic_cast(&in)) { throw(AipsError("Illegal MeasValue type argument: MVDirection")); } } void MVDirection::adjust() { Double length = std::sqrt(operator*(*this)); if (length == 0) { xyz(2) = 1.0; } else if (length != 1.0) { xyz /= length; } } void MVDirection::adjust(Double &res) { res = std::sqrt(operator*(*this)); if (res == 0) { xyz(2) = 1.0; } else if (res != 1.0) { xyz /= res; } } Vector MVDirection::get() const { Vector tmp(2); if (xyz(0) != 0 || xyz(1) != 0) tmp(0) = std::atan2(xyz(1),xyz(0)); else tmp(0) = 0.0; tmp(1) = std::asin(xyz(2)); return tmp; } Double MVDirection::getLat() const { return MVPosition::getLat(1.0); } Quantity MVDirection::getLat(const Unit &unit) const { return (Quantity(getLat(), "rad").get(unit)); } Vector > MVDirection::getRecordValue() const { Vector t(2); t = get(); Vector > tmp(2); tmp(0) = Quantity(t(0), "rad"); tmp(1) = Quantity(t(1), "rad"); return tmp; } Vector > MVDirection::getXRecordValue() const { Vector > tmp(3); tmp(0) = Quantity(xyz(0), ""); tmp(1) = Quantity(xyz(1), ""); tmp(2) = Quantity(xyz(2), ""); return tmp; } Vector > MVDirection::getTMRecordValue() const { return getRecordValue(); } Bool MVDirection::putValue(const Vector > &in) { uInt i; i = in.nelements(); if (i > 3 ) return False; if (i == 3 && in(0).check(UnitVal::NODIM) && in(1).check(UnitVal::NODIM) && in(2).check(UnitVal::NODIM)) { for (uInt j = 0; j tsin(i), tcos(i); for (j=0; j < i; j++) { tsin(j) = (sin(in(j))).getValue(); tcos(j) = (cos(in(j))).getValue(); } xyz = Double(0.0); if (i > 1) { xyz(0) = tcos(0) * tcos(1); xyz(1) = tsin(0) * tcos(1); xyz(2) = tsin(1); } else if (i > 0) { xyz(0) = tcos(0); xyz(1) = tsin(0); } else { xyz(2)=1.0; } adjust(); } return True; } void MVDirection::setAngle(Double angle0, Double angle1) { Double loc = std::cos(angle1); xyz(0) = std::cos(angle0)*loc; xyz(1) = std::sin(angle0)*loc; xyz(2) = std::sin(angle1); } Double MVDirection::positionAngle(const MVPosition &other) const { Double longDiff(getLong() - other.getLong()); Double slat1(xyz(2)); Double ln(norm(other.getValue())); Double slat2(other.getValue()(2)/ln); Double clat2(std::sqrt(std::fabs(1.0 - slat2*slat2))); Double s1(-clat2 * std::sin(longDiff)); Double c1(std::sqrt(std::fabs(1.0 - slat1*slat1))*slat2 - slat1*clat2*std::cos(longDiff)); return ((s1 != 0 || c1 != 0) ? std::atan2(s1, c1): 0.0); } Double MVDirection::positionAngle(const MVDirection &other) const { const Double longDiff(getLong() - other.getLong()); const Double slat1(xyz(2)); const Double slat2(other.xyz(2)); const Double clat2(std::sqrt(std::fabs(1.0 - slat2*slat2))); const Double s1(-clat2 * std::sin(longDiff)); const Double c1(std::sqrt(std::fabs(1.0 - slat1*slat1))*slat2 - slat1*clat2*std::cos(longDiff)); return ((s1 != 0 || c1 != 0) ? std::atan2(s1, c1): 0.0); } Quantity MVDirection::positionAngle(const MVPosition &other, const Unit &unit) const { return Quantity(positionAngle(other), "rad").get(unit); } Quantity MVDirection::positionAngle(const MVDirection &other, const Unit &unit) const { return Quantity(positionAngle(other), "rad").get(unit); } Double MVDirection::separation(const MVPosition &other) const { const Vector &otherxyz = other.getValue(); Double l2(norm(otherxyz)); l2 = l2 > 0 ? l2 : 1.0; Double d1 = std::sqrt(square(xyz(0) - otherxyz(0)/l2) + square(xyz(1) - otherxyz(1)/l2) + square(xyz(2) - otherxyz(2)/l2))/2.0; return 2*std::asin(d1 < 1.0 ? d1 : 1.0); } Double MVDirection::separation(const MVDirection &other) const { Double d1 = std::sqrt(square(xyz(0) - other.xyz(0)) + square(xyz(1) - other.xyz(1)) + square(xyz(2) - other.xyz(2)))/2.0; return 2*std::asin(d1 < 1.0 ? d1 : 1.0); } Quantity MVDirection::separation(const MVPosition &other, const Unit &unit) const { return Quantity(separation(other), "rad").get(unit); } Quantity MVDirection::separation(const MVDirection &other, const Unit &unit) const { return Quantity(separation(other), "rad").get(unit); } MVDirection MVDirection::crossProduct(const MVDirection &other) const { MVDirection res; res(0) = xyz(1)*other(2) - xyz(2)*other(1); res(1) = xyz(2)*other(0) - xyz(0)*other(2); res(2) = xyz(0)*other(1) - xyz(1)*other(0); return res; } void MVDirection::shift(const Quantum &lng, const Quantum &lat, Bool trueAngle) { shift(lng.getBaseValue(), lat.getBaseValue(), trueAngle); } void MVDirection::shift(Double lng, Double lat, Bool trueAngle) { Vector x(2); x = get(); if (trueAngle) { // The following calculation could maybe done quicker, but for now this // is more transparent. RotMatrix rm=RotMatrix(Euler(-lng, 3, x(1)+lat, 2, -x(0), 3)); *this = MVDirection(1,0,0) * rm; } else { x(0) += lng; x(1) += lat; *this = MVDirection(x); } } void MVDirection::shiftLongitude(const Quantum &lng, Bool trueAngle) { shift(lng.getBaseValue(), 0, trueAngle); } void MVDirection::shiftLongitude(Double lng, Bool trueAngle) { shift(lng, 0, trueAngle); } void MVDirection::shiftLatitude(const Quantum &lat, Bool trueAngle) { shift(0, lat.getBaseValue(), trueAngle); } void MVDirection::shiftLatitude(Double lat, Bool trueAngle) { shift(0, lat, trueAngle); } void MVDirection::shift(const MVDirection &shft, Bool trueAngle) { Vector x(2); x = shft.get(); shift(x(0), x(1), trueAngle); } void MVDirection::shiftAngle(const Quantum &off, const Quantum &pa) { shiftAngle(off.getBaseValue(), pa.getBaseValue()); } void MVDirection::shiftAngle(Double off, Double pa) { Vector x(2); x = get(); Double nlat = std::asin(std::cos(off)*std::sin(x(1)) + std::sin(off)*std::cos(x(1))*std::cos(pa)); Double nlng = std::sin(off)*std::sin(pa); if (std::cos(nlat) != 0) { nlng = std::asin(nlng/std::cos(nlat)); } else nlng = 0; *this = MVDirection(x(0)+nlng, nlat); } MeasValue *MVDirection::clone() const { return (new MVDirection(*this)); } MVDirection operator*(const RotMatrix &left, const MVDirection &right) { MVDirection result; for (Int i=0; i<3; i++) { result(i) = 0; for (Int j=0; j<3; j++) { result(i) += left(i,j) * right(j); } } return result; } MVDirection operator*(const MVDirection &left, const RotMatrix &right) { MVDirection result(left); result *= right; return result; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Quanta/MVDirection.h000066400000000000000000000223241476623553700200230ustar00rootroot00000000000000//# MVDirection.h: Vector of three direction cosines //# Copyright (C) 1996,1997,1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MVDIRECTION_H #define CASA_MVDIRECTION_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward De // Vector of three direction cosines // // // // //
        78. MVPosition //
        79. Vector //
        80. Quantum // // // // From Measure, Value and Direction // // // // An MVDirection is a 3-vector of direction cosines. It is based on the // MVposition class. The main difference is that the length of the // vector will be adjusted (normalised) to a length of 1 in all operations. // It can be constructed with: //
            //
          • MVDirection() creates direction cosines for pole: (0,0,1) //
          • MVDirection(MVDirection) creates a copy //
          • MVDirection(MVPosition) creates (x,y,z) from the given position //
          • MVDirection(Double, Double, Double) creates with // specified values and adjust to length of 1. //
          • MVDirection(Double, Double) creates a MVDirection assuming that the two // values are (in radians) angle along 'equator' and towards 'pole'. //
          • MVDirection(Quantity, Quantity) creates a MVDirection assuming angles // as in previous //
          • MVDirection(Quantum >) creates an MVDirection // from angle vector, assuming // second as zero if not present, and pole if length 0. Assumes // a direction cosine if 3 elements //
          • MVDirection(Vector) creates an MVDirection with // the same restrictions as previous one //
          • MVDirection(Vector >) creates an // MVDirection with the same rstrictions as previous one; but // with unit check. //
          // A void adjust() function normalises the vector to a length of 1; // a get() returns as a // Double 2-vector the angles of the direction cosines; a getAngle() returns // a Quantum 2-vector, (uInt) returns the indicated element, and getValue // returns the direction cosine vector.
          // Direction cosines can be added and subtracted: the result will be // adjusted to a length of 1.
          // The multiplication of two direction cosines produces the inner product.
          // shift() methods are available to shift in angular coordinates. E.g. // shift(Quantity(5, "arcsec"), Quantity(-7, "arcsec")) will shift 5 arcsec // in longitude, and -7 arcsec in latitude. They have a trueAngle switch // to shift in latitude and perpendicular (along a great circle) to it. //
          // // // See MDirection // // // // To aid coordinate transformations // // // //
        81. check if true shifts can be done faster // class MVDirection : public MVPosition { public: //# Friends //# Constructors // Default constructor generates a direction to the pole (i.e. (0,0,1)) MVDirection(); // Creates from an MVPosition MVDirection(const MVPosition &other); // Constructs with elevation = 0. // MVDirection(Double in0); MVDirection(const Quantity &angle0); // // Creates a specified vector MVDirection(Double in0, Double in1, Double in2); // Creates the direction cosines from specified angles along equator (azimuth) // and towards pole (,elevation). MVDirection(Double angle0, Double angle1); // Creates the direction cosines from specified angles // //
        82. AipsError if quantities not in angle format // // MVDirection(const Quantity &angle0, const Quantity &angle1); // If not enough angles: pole (=(0,0,1)) assumed (if none), or elevation =0 (if 1); // direction cosines assumed (if 3). // //
        83. AipsError if more than 3 values or incorrect units // MVDirection(const Quantum > &angle); // // Create from Vector. Assumes angles if less than or equal than 2 elements. // Assumes direction cosines if 3 elements. // //
        84. AipsError if more than 3 elements // // MVDirection(const Vector &other); MVDirection(const Vector &other); // //# Operators // Addition and subtraction // MVDirection &operator+=(const MVDirection &right); MVDirection operator+(const MVDirection &right) const; MVDirection &operator-=(const MVDirection &right); MVDirection operator-(const MVDirection &right) const; // //# General Member Functions // Tell me your type // static void assure(const MeasValue &in); // // Adjust the direction cosines to a length of 1 virtual void adjust(); // Adjust the direction cosines to a length of 1 and return the length value virtual void adjust(Double &res); // Re-adjust : taken from MVPosition. // // Clone data virtual MeasValue *clone() const; // Generate a 2-vector of angles (in rad) Vector get() const; // Get the latitude angle (rad) Double getLat() const; // and with specified units Quantity getLat(const Unit &unit) const; // Get the position angle between the directions. I.e. the angle between // the direction from one to the pole, and from one to the other. // Double positionAngle(const MVPosition &other) const; Double positionAngle(const MVDirection &other) const; Quantity positionAngle(const MVPosition &other, const Unit &unit) const; Quantity positionAngle(const MVDirection &other, const Unit &unit) const; // // Get the angular separation between two directions. // Double separation(const MVPosition &other) const; Double separation(const MVDirection &other) const; Quantity separation(const MVPosition &other, const Unit &unit) const; Quantity separation(const MVDirection &other, const Unit &unit) const; // // Produce the cross product MVDirection crossProduct(const MVDirection &other) const; // Get the internal value as a Vector. Usable in // records. The getXRecordValue() gets additional information for records. // Note that the Vectors could be empty. // virtual Vector > getRecordValue() const; virtual Vector > getXRecordValue() const; virtual Vector > getTMRecordValue() const; // // Set the internal value if correct values and dimensions virtual Bool putValue(const Vector > &in); // Set the internal value, using the longitude and latitude (in rad) given void setAngle(Double angle0, Double angle1); // Shift the direction in longitude (radians if Double) and/or latitude. // If the trueAngle switch is True, the longitude shift will be in // angular units perpendicular to the direction to the pole at the shifted // latitude, along a great circle. // void shift(const Quantum &lng, const Quantum &lat, Bool trueAngle=False); void shift(Double lng, Double lat, Bool trueAngle=False); void shiftLongitude(const Quantity &lng, Bool trueAngle=False); void shiftLongitude(Double lng, Bool trueAngle=False); void shiftLatitude(const Quantum &lat, Bool trueAngle=False); void shiftLatitude(Double lat, Bool trueAngle=False); void shift(const MVDirection &shft, Bool trueAngle=False); // // Shift over an angle off in the direction pa. pa is measured from North, // in the direction of increasing longitude. // void shiftAngle(const Quantum &off, const Quantum &pa); void shiftAngle(Double off, Double pa); // protected: //# Data }; //# Global functions // Rotate a position vector MVDirection operator*(const RotMatrix &left, const MVDirection&right); MVDirection operator*(const MVDirection &left, const RotMatrix &right); } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Quanta/MVDoppler.cc000066400000000000000000000121001476623553700176350ustar00rootroot00000000000000//# MVDoppler.cc: Internal value for MDoppler //# Copyright (C) 1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // MVDoppler class //# Constructors MVDoppler::MVDoppler() : val(0.0){} MVDoppler::MVDoppler(Double d) : val(d){} MVDoppler::MVDoppler(const MVDoppler &other) : MeasValue(), val(other.val) {} MVDoppler::MVDoppler(const Quantity &other) { val = makeD(other.getValue(), other.getFullUnit()); } MVDoppler::MVDoppler(const Quantum > &other) { Vector tmp; tmp = other.getValue(); uInt i = tmp.nelements(); if (i == 0) { val = 0.0; } else if (i == 1) { val = makeD(tmp(0), other.getFullUnit()); } else { throw (AipsError("Illegal vector length in MVDoppler constructor")); } } MVDoppler::MVDoppler(const Vector &other) { uInt i = other.nelements(); if (i == 0) { val = 0.0; } else if (i == 1) { val = other(0); } else { throw (AipsError("Illegal vector length in MVDoppler constructor")); } } MVDoppler::MVDoppler(const Vector &other) { if (!putValue(other)) { throw (AipsError("Illegal quantity vector in MVDoppler constructor")); } } MVDoppler &MVDoppler::operator=(const MVDoppler &other) { if (this != &other) { val = other.val; } return *this; } // Destructor MVDoppler::~MVDoppler() {} // Operators MVDoppler::operator Double() const { return val; } MVDoppler &MVDoppler::operator+=(const MVDoppler &other) { val += other.val; return *this; } MVDoppler &MVDoppler::operator-=(const MVDoppler &other) { val -= other.val; return *this; } Bool MVDoppler::operator==(const MVDoppler &other) const { return (val == other.val); } Bool MVDoppler::operator!=(const MVDoppler &other) const { return (val != other.val); } Bool MVDoppler::near(const MVDoppler &other, Double tol) const { return ::casacore::near(val, other.val, tol); } Bool MVDoppler::nearAbs(const MVDoppler &other, Double tol) const { return ::casacore::nearAbs(val, other.val, tol); } // Member functions void MVDoppler::assure(const MeasValue &in) { if (!dynamic_cast(&in)) { throw(AipsError("Illegal MeasValue type argument: MVDoppler")); } } void MVDoppler::print(ostream &os) const { os << val; } MeasValue *MVDoppler::clone() const { return (new MVDoppler(*this)); } Double MVDoppler::getValue() const { return val; } Quantity MVDoppler::get() const { return Quantity(val*C::c,"m/s"); } Quantity MVDoppler::get(const Unit &unit) const { return Quantity(makeD(val, unit, True), unit); } Vector MVDoppler::getVector() const { Vector x(1); x(0) = val; return x; } void MVDoppler::putVector(const Vector &in) { if (in.nelements() < 1) { val = 0.0; } else { val = in(0); } } Vector > MVDoppler::getRecordValue() const { Vector > tmp(1); tmp(0) = get(); return tmp; } Bool MVDoppler::putValue(const Vector > &in) { static const UnitVal Velocity = UnitVal::LENGTH/UnitVal::TIME; uInt i = in.nelements(); if (i == 0) { val = 0.0; } else if (i == 1) { UnitVal dt = in(0).getFullUnit().getValue(); if (dt == UnitVal::NODIM || dt == Velocity) { val = makeD(in(0).getValue(), in(0).getFullUnit()); } else { return False; } } else { return False; } return True; } Double MVDoppler::makeD(Double v, const Unit &dt, Bool rev) const{ static const UnitVal Velocity = UnitVal::LENGTH/UnitVal::TIME; static const Double LVel = QC::c( ).getBaseValue(); Double x; if (dt.getValue() == UnitVal::NODIM) { x = dt.getValue().getFac(); } else { Quantity(1.0,dt).assure(Velocity); x = dt.getValue().getFac()/LVel; } if (rev) return (v/x); return (v*x); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Quanta/MVDoppler.h000066400000000000000000000133021476623553700175040ustar00rootroot00000000000000//# MVDoppler.h: Internal value for MDoppler //# Copyright (C) 1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MVDOPPLER_H #define CASA_MVDOPPLER_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // Internal value for MDoppler // // // // //
        85. MeasValue // // // // From Measure, Value and Doppler // // // // An MVDoppler is a simple Double, to be used in the MDoppler measure. // Requirements can be found in the // MeasValue base class.
          // The only reasonable constructor is (but all MeasValue constructors are // present) // MVDoppler(Double); and an operator Double takes // care of all other possibilities. Its external use is for // MeasConvert, to distinguish between // input in internal Measure units, and values which have to have // units applied.
          // The MVDoppler(Quantum) constructors recognise the type of representation // characteristics presented from its units. Recognised are: //
            //
          • no dimensions: value assumed to be dimensionless as is //
          • velocity: value will be divided by c (light velocity) //
          //
          The Doppler is returned dimensionless with getValue(); or as a Quantity // in m/s with get(); or in one of the related units with get(unit). //
          // // // See MDoppler // // // // To aid coordinate transformations possibilities // // // // class MVDoppler : public MeasValue { public: //# Constructors // Default constructor: generate a zero value MVDoppler(); // Copy constructor MVDoppler(const MVDoppler &other); // Copy assignment MVDoppler &operator=(const MVDoppler &other); // Constructor from Double MVDoppler(Double d); // Constructor from Quantum : value taken will be the canonical value // MVDoppler(const Quantity &other); MVDoppler(const Quantum > &other); // // Constructor from Vector. A zero value will be taken for an empty vector, // the canonical value for a quantum vector. // //
        86. AipsError if vector length > 1 // // MVDoppler(const Vector &other); MVDoppler(const Vector &other); // // Destructor ~MVDoppler(); //# Operators // Conversion operator operator Double() const; // Addition // MVDoppler &operator+=(const MVDoppler &other); MVDoppler &operator-=(const MVDoppler &other); // // Comparisons // Bool operator==(const MVDoppler &other) const; Bool operator!=(const MVDoppler &other) const; Bool near(const MVDoppler &other, Double tol = 1e-13) const; Bool nearAbs(const MVDoppler &other, Double tol = 1e-13) const; // //# General member functions // Tell me your type // static void assure(const MeasValue &in); // // Print data virtual void print(ostream &os) const; // Clone virtual MeasValue *clone() const; // Adjust value: taken from base class, a NOP. // Get value as ratio Double getValue() const; // Get quantity in m/s Quantity get() const; // Get the Doppler value in (recognised) specified units Quantity get(const Unit &unit) const; // Get the value in internal units virtual Vector getVector() const; // Set the value from internal units (set 0 for empty vector) virtual void putVector(const Vector &in); // Get the internal value as a Vector. Usable in // records. The getXRecordValue() gets additional information for records. // Note that the Vectors could be empty. // virtual Vector > getRecordValue() const; // // Set the internal value if correct values and dimensions virtual Bool putValue(const Vector > &in); private: //# Data // Value Double val; //# Member functions // Get correct data type conversion factor from input Quantum Double makeD(Double v, const Unit &dt, Bool rev=False) const; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Quanta/MVDouble.cc000066400000000000000000000103071476623553700174510ustar00rootroot00000000000000//# MVDouble.cc: to disticguish between internal and external Measure values //# Copyright (C) 1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // MVDouble class //# Constructors MVDouble::MVDouble(Double d) : val(d){} MVDouble::MVDouble(const MVDouble &other) : MeasValue(), val(other.val) {} MVDouble::MVDouble(const Quantity &other) { val = other.get().getValue(); } MVDouble::MVDouble(const Quantum > &other) { Vector tmp; tmp = other.get().getValue(); uInt i = tmp.nelements(); if (i == 0) { val = 0.0; } else if (i == 1) { val = tmp(0); } else { throw (AipsError("Illegal vector length in MVDouble constructor")); } } MVDouble::MVDouble(const Vector &other) { uInt i = other.nelements(); if (i == 0) { val = 0.0; } else if (i == 1) { val = other(0); } else { throw (AipsError("Illegal vector length in MVDouble constructor")); } } MVDouble::MVDouble(const Vector &other) { if (!putValue(other)) { throw (AipsError("Illegal quantity vector in MVDouble constructor")); } } MVDouble &MVDouble::operator=(const MVDouble &other) { if (this != &other) { val = other.val; } return *this; } // Destructor MVDouble::~MVDouble() {} // Operators MVDouble::operator Double() const { return val; } MVDouble &MVDouble::operator+=(const MVDouble &other) { val += other.val; return *this; } MVDouble &MVDouble::operator-=(const MVDouble &other) { val -= other.val; return *this; } Bool MVDouble::operator==(const MVDouble &other) const { return (val == other.val); } Bool MVDouble::operator!=(const MVDouble &other) const { return (val != other.val); } //# Member functions void MVDouble::assure(const MeasValue &in) { if (!dynamic_cast(&in)) { throw(AipsError("Illegal MeasValue type argument: MVDouble")); } } Bool MVDouble::near(const MVDouble &other, Double tol) const { return ::casacore::near(val, other.val, tol); } Bool MVDouble::nearAbs(const MVDouble &other, Double tol) const { return ::casacore::nearAbs(val, other.val, tol); } // Member functions void MVDouble::print(ostream &os) const { os << val; } MeasValue *MVDouble::clone() const { return (new MVDouble(*this)); } Vector MVDouble::getVector() const { Vector x(1); x(0) = val; return x; } void MVDouble::putVector(const Vector &in) { if (in.nelements() < 1) { val = 0.0; } else { val = in(0); } } Vector > MVDouble::getRecordValue() const { Vector > tmp(1); tmp(0) = Quantity(val, ""); return tmp; } Bool MVDouble::putValue(const Vector > &in) { uInt i = in.nelements(); if (i == 0) { val = 0.0; } else if (i == 1) { val = (in(0)).get().getValue(); } else { return False; } return True; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Quanta/MVDouble.h000066400000000000000000000117761476623553700173260ustar00rootroot00000000000000//# MVDouble.h: class to distinguish between internal and external Double //# Copyright (C) 1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MVDOUBLE_H #define CASA_MVDOUBLE_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations //# Constants (SUN compiler does not accept non-simple default arguments) // Class to distinguish external and Measure internal Double // // // // //
        87. MeasValue // // // // From Measure, Value and Double // // // // An MVDouble is a simple Double, to be used in simple, single value // Measures. Requirements can be found in the // MeasValue base class.
          // The only reasonable constructor is (but all MeasValue constructors are present) // MVDouble(Double); and an operator Double takes // care of all other possibilities. Its external use is for // MeasConvert, to distinguish between // input in internal Measure units, and values which have to have // units applied. //
          // // // See e.g. MFrequency // // // // To aid coordinate transformations possibilities // // // // class MVDouble : public MeasValue { public: //# Constructors // Default constructor: generate a zero value MVDouble(); // Copy constructor MVDouble(const MVDouble &other); // Copy assignment MVDouble &operator=(const MVDouble &other); // Constructor from Double MVDouble(Double d); // Constructor from Quantum : value taken will be the canonical value // MVDouble(const Quantity &other); MVDouble(const Quantum > &other); // // Constructor from Vector. A zero value will be taken for an empty vector, // the canonical value for a quantum vector. // //
        88. AipsError if vector length > 1 // // MVDouble(const Vector &other); MVDouble(const Vector &other); // // Destructor ~MVDouble(); //# Operators // Conversion operator operator Double() const; // Addition // MVDouble &operator+=(const MVDouble &other); MVDouble &operator-=(const MVDouble &other); // // Comparisons // Bool operator==(const MVDouble &other) const; Bool operator!=(const MVDouble &other) const; Bool near(const MVDouble &other, Double tol = 1e-13) const; Bool nearAbs(const MVDouble &other, Double tol = 1e-13) const; // //# General member functions // Tell me your type // static void assure(const MeasValue &in); // // Print data virtual void print(ostream &os) const; // Clone virtual MeasValue *clone() const; // Adjust value: taken from base class, a NOP. // Get the value in internal units virtual Vector getVector() const; // Set the value from internal units (set 0 for empty vector) virtual void putVector(const Vector &in); // Get the internal value as a Vector. Usable in // records. The getXRecordValue() gets additional information for records. // Note that the Vectors could be empty. // virtual Vector > getRecordValue() const; // // Set the internal value if correct values and dimensions virtual Bool putValue(const Vector > &in); private: //# Data // Value Double val; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Quanta/MVEarthMagnetic.cc000066400000000000000000000305361476623553700207600ustar00rootroot00000000000000//# MVEarthMagnetic.cc: A 3D Earth magnetic field vector //# Copyright (C) 1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // MVEarthMagnetic class //# Constructors MVEarthMagnetic::MVEarthMagnetic() : MVPosition() {} MVEarthMagnetic::MVEarthMagnetic(const MVPosition &other) : MVPosition(other) {} MVEarthMagnetic::MVEarthMagnetic(Double in) : MVPosition(in) {} MVEarthMagnetic::MVEarthMagnetic(const Quantity &l) : MVPosition() { static const UnitVal testUnit = UnitVal::MASS/UnitVal::TIME/UnitVal::TIME/ UnitVal::CURRENT; l.assure(testUnit); xyz(2) = l.getBaseValue(); } MVEarthMagnetic::MVEarthMagnetic(Double in0, Double in1, Double in2) : MVPosition(in0, in1, in2) {} MVEarthMagnetic::MVEarthMagnetic(const Quantity &l, Double angle0, Double angle1) : MVPosition() { static const UnitVal testUnit = UnitVal::MASS/UnitVal::TIME/UnitVal::TIME/ UnitVal::CURRENT; l.assure(testUnit); Double loc = std::cos(angle1); xyz(0) = std::cos(angle0)*loc; xyz(1) = std::sin(angle0)*loc; xyz(2) = std::sin(angle1); readjust(l.getBaseValue()); } MVEarthMagnetic::MVEarthMagnetic(const Quantity &l, const Quantity &angle0, const Quantity &angle1) : MVPosition() { static const UnitVal testUnit = UnitVal::MASS/UnitVal::TIME/UnitVal::TIME/ UnitVal::CURRENT; l.assure(testUnit); Double loc = (cos(angle1)).getValue(); xyz(0) = ((cos(angle0)).getValue()) * loc; xyz(1) = ((sin(angle0)).getValue()) * loc; xyz(2) = (sin(angle1)).getValue(); readjust(l.getBaseValue()); } MVEarthMagnetic::MVEarthMagnetic(const Quantum > &angle) : MVPosition() { static const UnitVal testUnit = UnitVal::MASS/UnitVal::TIME/UnitVal::TIME/ UnitVal::CURRENT; uInt i; i = angle.getValue().nelements(); if (i > 3 ) { throw (AipsError("Illegal vector length in MVEarthMagnetic constructor")); } else if (i == 3) { angle.assure(testUnit); xyz = angle.getBaseValue(); } else { Vector tsin = (sin(angle)).getValue(); Vector tcos = (cos(angle)).getValue(); xyz = Double(0.0); if (i > 1) { xyz(0) = tcos(0) * tcos(1); xyz(1) = tsin(0) * tcos(1); xyz(2) = tsin(1); } else if (i > 0) { xyz(0) = tcos(0); xyz(1) = tsin(0); } else { xyz(2)=1.0; } } } MVEarthMagnetic::MVEarthMagnetic(const Quantity &l, const Quantum > &angle) : MVPosition() { static const UnitVal testUnit = UnitVal::MASS/UnitVal::TIME/UnitVal::TIME/ UnitVal::CURRENT; uInt i; i = angle.getValue().nelements(); if (i > 3 ) { throw (AipsError("Illegal vector length in MVEarthMagnetic constructor")); } else if (i == 3) { angle.assure(UnitVal::NODIM); xyz = angle.getValue(); } else { Vector tsin = (sin(angle)).getValue(); Vector tcos = (cos(angle)).getValue(); xyz = Double(0.0); if (i > 1) { xyz(0) = tcos(0) * tcos(1); xyz(1) = tsin(0) * tcos(1); xyz(2) = tsin(1); } else if (i > 0) { xyz(0) = tcos(0); xyz(1) = tsin(0); } else { xyz(2)=1.0; } } l.assure(testUnit); readjust(l.getBaseValue()); } MVEarthMagnetic::MVEarthMagnetic(const Vector &other) : MVPosition(other) {} MVEarthMagnetic::MVEarthMagnetic(const Vector &other) : MVPosition() { if (!putValue(other)) { throw (AipsError("Illegal quantity vector in MVEarthMagnetic constructor")); } } //# Operators Bool MVEarthMagnetic:: operator==(const MVEarthMagnetic &other) const { return (allEQ(xyz, other.xyz)); } Bool MVEarthMagnetic:: operator!=(const MVEarthMagnetic &other) const { return (!(*this == other)); } Bool MVEarthMagnetic:: near(const MVEarthMagnetic &other, Double tol) const { return (allNear(xyz, other.xyz, tol)); } Bool MVEarthMagnetic:: near(const MVEarthMagnetic &other, Quantity tol) const { return (separation(other,"rad") <= tol); } Bool MVEarthMagnetic:: nearAbs(const MVEarthMagnetic &other, Double tol) const { return (allNearAbs(xyz, other.xyz, tol)); } Double MVEarthMagnetic:: operator*(const MVEarthMagnetic &other) const { Double tmp = 0.0; for (Int i=0; i<3; i++) { tmp += xyz(i) * other.xyz(i); } return tmp; } MVEarthMagnetic MVEarthMagnetic::operator-() const { MVEarthMagnetic tmp; tmp = *this; tmp.xyz = -xyz; return tmp; } MVEarthMagnetic &MVEarthMagnetic::operator+=(const MVEarthMagnetic &right) { xyz += right.xyz; return *this; } MVEarthMagnetic MVEarthMagnetic::operator+(const MVEarthMagnetic &right) const { MVEarthMagnetic tmp = *this; tmp += right; return tmp; } MVEarthMagnetic &MVEarthMagnetic::operator-=(const MVEarthMagnetic &right) { xyz -= right.xyz; return *this; } MVEarthMagnetic MVEarthMagnetic::operator-(const MVEarthMagnetic &right) const{ MVEarthMagnetic tmp = *this; tmp -= right; return tmp; } //# Member functions void MVEarthMagnetic::assure(const MeasValue &in) { if (!dynamic_cast(&in)) { throw(AipsError("Illegal MeasValue type argument: MVEarthMagnetic")); } } void MVEarthMagnetic::adjust() {} void MVEarthMagnetic::adjust(Double &res) { res = std::sqrt(operator*(*this)); if (res != 0.0 && res != 1.0) { xyz /= res; } } void MVEarthMagnetic::readjust(Double res) { if (res == 0.0) { xyz *= 1e-6; } else { xyz *= res; } } Double MVEarthMagnetic::radius() { return (std::sqrt(operator*(*this))); } Vector MVEarthMagnetic::get() const{ Vector tmp(3); tmp(0) = std::sqrt(operator*(*this)); Double ln = (tmp(0) == 0.0 ? 1.0 : tmp(0)); Double loc = xyz(0)/ln; if (loc == 0) { tmp(1) = std::asin(xyz(1)/ln); } else { tmp(1) = std::atan2(xyz(1),xyz(0)); } tmp(2) = std::asin(xyz(2)/ln); return tmp; } const Vector &MVEarthMagnetic::getValue() const { return xyz; } Quantum > MVEarthMagnetic::getAngle() const{ Vector tp(3), tmp(2); tp = get(); tmp(0) = tp(1); tmp(1) = tp(2); return Quantum >(tmp,"rad"); } Quantum > MVEarthMagnetic::getAngle(const Unit &unit) const{ return getAngle().get(unit); } Quantity MVEarthMagnetic::getLength() const{ Double tmp = std::sqrt(operator*(*this)); return Quantity(tmp,"nT"); } Quantity MVEarthMagnetic::getLength(const Unit &unit) const { return getLength().get(unit); } Double MVEarthMagnetic::earthMagneticAngle(const MVEarthMagnetic &other) const { Vector t1(3); Vector t2(3); t1 = get(); t2 = other.get(); Double s1,c1; c1 = std::cos(t1(2)) * std::sin(t2(2)) - std::sin(t1(2)) * std::cos(t2(2)) * std::cos(t1(1) - t2(1)); s1 = -std::cos(t2(2)) * std::sin(t1(1) - t2(1)); if (s1 != 0 || c1 != 0) { return std::atan2(s1, c1); } else { return Double(0.0); } } Quantity MVEarthMagnetic::earthMagneticAngle(const MVEarthMagnetic &other, const Unit &unit) const { return Quantity(earthMagneticAngle(other), "rad").get(unit); } Double MVEarthMagnetic::separation(const MVEarthMagnetic &other) const { MVEarthMagnetic t1(*this); MVEarthMagnetic t2(other); t1.adjust(); t2.adjust(); t1 -= t2; Double d1 = t1.radius()/2.0; d1 = (d1 < 1.0 ? d1 : 1.0); return (2*std::asin(d1)); } Quantity MVEarthMagnetic::separation(const MVEarthMagnetic &other, const Unit &unit) const { return Quantity(separation(other), "rad").get(unit); } MVEarthMagnetic MVEarthMagnetic::crossProduct(const MVEarthMagnetic &other) const { MVEarthMagnetic res; res(0) = xyz(1)*other(2) - xyz(2)*other(1); res(1) = xyz(2)*other(0) - xyz(0)*other(2); res(2) = xyz(0)*other(1) - xyz(1)*other(0); return res; } void MVEarthMagnetic::print(ostream &os) const { os << getValue(); } MeasValue *MVEarthMagnetic::clone() const { return (new MVEarthMagnetic(*this)); } Vector MVEarthMagnetic::getVector() const { return xyz; } void MVEarthMagnetic::putVector(const Vector &in) { if (in.nelements() == 3) { xyz = in; } else { xyz = 0.0; for (uInt i=0; i > MVEarthMagnetic::getRecordValue() const { Vector > tmp(3); tmp(0) = Quantity(xyz(0), "nT"); tmp(1) = Quantity(xyz(1), "nT"); tmp(2) = Quantity(xyz(2), "nT"); return tmp; } Bool MVEarthMagnetic::putValue(const Vector > &in) { static const UnitVal testUnit = UnitVal::MASS/UnitVal::TIME/UnitVal::TIME/ UnitVal::CURRENT; uInt i; i = in.nelements(); if (i != 3 ) return False; if (in(0).check(testUnit)) { if (in(1).check(testUnit) && in(2).check(testUnit)) { for (uInt j = 0; j tsin(2), tcos(2); for (uInt j=1; j < i; j++) { tsin(j-1) = (sin(in(j))).getValue(); tcos(j-1) = (cos(in(j))).getValue(); } xyz = Double(0.0); xyz(0) = tcos(0) * tcos(1); xyz(1) = tsin(0) * tcos(1); xyz(2) = tsin(1); readjust(in(0).getBaseValue()); } else { return False; } } else if (in(2).check(testUnit)) { if (in(0).check(UnitVal::ANGLE) && in(1).check(UnitVal::ANGLE)) { Vector tsin(2), tcos(2); Int j; for (j=0; j < 2; j++) { tsin(j) = (sin(in(j))).getValue(); tcos(j) = (cos(in(j))).getValue(); } xyz = Double(0.0); xyz(0) = tcos(0) * tcos(1); xyz(1) = tsin(0) * tcos(1); xyz(2) = tsin(1); readjust(in(2).getBaseValue()); } else { return False; } } else { return False; } return True; } MVEarthMagnetic operator*(const RotMatrix &left, const MVEarthMagnetic &right) { MVEarthMagnetic result; for (Int i=0; i<3; i++) { result(i) = 0; for (Int j=0; j<3; j++) { result(i) += left(i,j) * right(j); } } return result; } MVEarthMagnetic operator*(const MVEarthMagnetic &left, const RotMatrix &right) { MVEarthMagnetic result(left); result *= right; return result; } MVEarthMagnetic operator*(Double left, const MVEarthMagnetic &right) { MVEarthMagnetic result(right); result *= left; return result; } MVEarthMagnetic operator*(const MVEarthMagnetic &left, Double right) { MVEarthMagnetic result(left); result *= right; return result; } Double operator*(const Vector &left, const MVEarthMagnetic &right) { MVEarthMagnetic tmp(left); return (tmp * right); } Double operator*(const MVEarthMagnetic &left, const Vector &right) { MVEarthMagnetic tmp(right); return (tmp * left); } Double operator*(const MVPosition &left, const MVEarthMagnetic &right) { MVEarthMagnetic tmp(left); return (tmp * right); } Double operator*(const MVEarthMagnetic &left, const MVPosition &right) { MVEarthMagnetic tmp(right); return (tmp * left); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Quanta/MVEarthMagnetic.h000066400000000000000000000225041476623553700206160ustar00rootroot00000000000000//# MVEarthMagnetic.h: A 3D Earth magnetic field vector //# Copyright (C) 1996,1997,1998,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MVEARTHMAGNETIC_H #define CASA_MVEARTHMAGNETIC_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // A 3D Earth magnetic field vector // // // // //
        89. MeasValue //
        90. Vector //
        91. Quantum // // // // From Measure, Value and Earth Magnetic field // // // // A MVEarthMagnetic is a 3-vector of the Earth's magnetic flux density in a // rectangular frame with the z-axis to astronomical North pole, and x-axis // towards longitude zero, in internal Units of nano tesla (== 0.00001 G).
          // It can be constructed with: //
            //
          • MVEarthMagnetic() creates (0,0,0) //
          • MVEarthMagnetic(MVEarthMagnetic) creates a copy //
          • MVEarthMagnetic(MVPosition) creates (x,y,z) from the given position //
          • MVEarthMagnetic(Double, Double, Double) creates (x,y,z) with // specified values in tesla //
          • MVEarthMagnetic(Quantity length,Double, Double) creates an // MVEarthMagnetic assuming // that the two values are (in radians) angle along 'equator' // and towards 'pole'. //
          • MVEarthMagnetic(Quantity length, Quantity, Quantity) creates an // MVEarthMagnetic // assuming angles as in previous, or (x,y,z) fields //
          • MVEarthMagnetic(Quantity, Quantum >) creates a // MVEarthMagnetic from angle vector, using first two angles, and // assuming second as zero if not present, and pole if length 0. //
          • MVEarthMagnetic(Quantum > creates from // angles or fields, depending on the units in the // quantum vector. In the angle case, // the data derived can be scaled with the readjust() function. If // the unit of the quantum vector is magnetic flux density, // magnetic field components are assumed. //
          • MVEarthMagnetic(Vector creates from angles (less than // or equal to two elements) or x,y,z (3 elements). //
          • MVEarthMagnetic(Vector creates from length+angles, // angles, or x,y,z, depending on units. //
          // A void adjust(Double) function normalises the vector to a length of 1; // a get() returns as a // Double 3-vector the length and angles of the EarthMagnetic vector; // a getAngle() returns a Quantum 2-vector, (uInt) returns the indicated // element, and getValue returns the vector.
          // EarthMagnetics can be added and subtracted.
          // The multiplication of two EarthMagnetics produces the in-product.
          //
          // // // See MEarthMagnetic class. // // // // To use in ionospheric effect calculations // // // //
        92. nothing I know of // class MVEarthMagnetic : public MVPosition { public: //# Friends //# Constructors // Default constructor generates a (0,0,0) EarthMagnetic MVEarthMagnetic(); // Creates from an MVPosition MVEarthMagnetic(const MVPosition &other); // Creates a specified vector MVEarthMagnetic(Double in0, Double in1, Double in2); // Creates a vector with specified length towards pole // explicit MVEarthMagnetic(Double in0); MVEarthMagnetic(const Quantity &l); // // Creates the EarthMagnetic from specified (azimuth,elevation) angles and length MVEarthMagnetic(const Quantity &l, Double angle0, Double angle1); // Creates the EarthMagnetic from specified angles and length. or EarthMagnetics // //
        93. AipsError if quantities not in angle format // // MVEarthMagnetic(const Quantity &l, const Quantity &angle0, const Quantity &angle1); // If not enough angles: pole assumed (if none), or elevation =0 (if 1) MVEarthMagnetic(const Quantum > &angle); MVEarthMagnetic(const Quantity &l, const Quantum > &angle); // // Create from specified length and/or angles and/or EarthMagnetic // MVEarthMagnetic(const Vector &other); MVEarthMagnetic(const Vector &other); // //# Operators // Multiplication defined as in-product // Double operator*(const MVEarthMagnetic &other) const; // // Equality comparisons // Bool operator== (const MVEarthMagnetic &other) const; Bool operator!= (const MVEarthMagnetic &other) const; Bool near(const MVEarthMagnetic &other, Double tol=1e-13) const; Bool near(const MVEarthMagnetic &other, Quantity tol) const; Bool nearAbs(const MVEarthMagnetic &other, Double tol=1e-13) const; // // Addition and subtraction // MVEarthMagnetic operator-() const; MVEarthMagnetic &operator+=(const MVEarthMagnetic &right); MVEarthMagnetic operator+(const MVEarthMagnetic &right) const; MVEarthMagnetic &operator-=(const MVEarthMagnetic &right); MVEarthMagnetic operator-(const MVEarthMagnetic &right) const; // //# General Member Functions // Tell me your type // static void assure(const MeasValue &in); // // Normalise direction aspects by adjusting the length to 1 // virtual void adjust(); virtual void adjust(Double &res); virtual void readjust(Double res); // // Get modulus of EarthMagnetic virtual Double radius(); // Generate a 3-vector of coordinates (length(T), angles(rad)) Vector get() const; // Generate a 3-vector of x,y,z in tesla const Vector &getValue() const; // Generate angle 2-vector (in rad) Quantum > getAngle() const; // and with specified units Quantum > getAngle(const Unit &unit) const; // Generate the length Quantity getLength() const; // and generate it with the specified units Quantity getLength(const Unit &unit) const; // Get the EarthMagnetic angle between the directions. I.e. the angle between // the direction from one to the pole, and from one to the other. // Double earthMagneticAngle(const MVEarthMagnetic &other) const; Quantity earthMagneticAngle(const MVEarthMagnetic &other, const Unit &unit) const; // // Get the angular separation between two directions. // Double separation(const MVEarthMagnetic &other) const; Quantity separation(const MVEarthMagnetic &other, const Unit &unit) const; // // Produce the cross product MVEarthMagnetic crossProduct(const MVEarthMagnetic &other) const; // Print data virtual void print(ostream &os) const; // Clone virtual MeasValue *clone() const; // Get the value in internal units virtual Vector getVector() const; // Set the value from internal units (set 0 for empty vector) virtual void putVector(const Vector &in); // Get the internal value as a Vector. Usable in // records. The getXRecordValue() gets additional information for records. // Note that the Vectors could be empty. // virtual Vector > getRecordValue() const; // // Set the internal value if correct values and dimensions virtual Bool putValue(const Vector > &in); }; //# Global functions // Rotate a EarthMagnetic vector with rotation matrix and other multiplications // MVEarthMagnetic operator*(const RotMatrix &left, const MVEarthMagnetic &right); MVEarthMagnetic operator*(const MVEarthMagnetic &left, const RotMatrix &right); MVEarthMagnetic operator*(Double left, const MVEarthMagnetic &right); MVEarthMagnetic operator*(const MVEarthMagnetic &left, Double right); Double operator*(const Vector &left, const MVEarthMagnetic &right); Double operator*(const MVEarthMagnetic &left, const Vector &right); Double operator*(const MVPosition &left, const MVEarthMagnetic &right); Double operator*(const MVEarthMagnetic &left, const MVPosition &right); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Quanta/MVEpoch.cc000066400000000000000000000141011476623553700172710ustar00rootroot00000000000000//# MVEpoch.cc: a class for high precision time //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constants const Double MVEpoch::secInDay(3600*24); const Unit MVEpoch::unitDay("d"); //# Constructors MVEpoch::MVEpoch() : wday(0), frday(0) {} MVEpoch::MVEpoch(const MVEpoch &other) : MeasValue(), wday (other.wday), frday (other.frday) { adjust(); } MVEpoch::MVEpoch(Double inday, Double infrac) : wday(0), frday(0) { addTime(inday); addTime(infrac); adjust(); } MVEpoch::MVEpoch(const Quantity &in) : wday(0), frday(0) { addTime(makeDay(in)); } MVEpoch::MVEpoch(const Quantity &in1, const Quantity &in2) : wday(0), frday(0) { addTime(makeDay(in1)); addTime(makeDay(in2)); adjust(); } MVEpoch::MVEpoch(const Quantum > &in) : wday(0), frday(0) { for (uInt i=0; i &inday) : wday(0), frday(0) { for (uInt i=0; i &in) : wday(0), frday(0) { if (!putValue(in)) { throw(AipsError("Illegal Quantity type argument: MVEpoch")); } } //# Destructor MVEpoch::~MVEpoch() {} //# Operators MVEpoch &MVEpoch::operator=(const MVEpoch &other) { if (this != &other) { wday = other.wday; frday = other.frday; } return *this; } MVEpoch &MVEpoch::operator+=(const MVEpoch &other) { frday += other.frday; wday += other.wday; adjust(); return *this; } MVEpoch MVEpoch::operator+(const MVEpoch &other) const { MVEpoch tmp(*this); tmp += other; return tmp; } MVEpoch &MVEpoch::operator-=(const MVEpoch &other) { frday -= other.frday; wday -= other.wday; adjust(); return *this; } MVEpoch MVEpoch::operator-(const MVEpoch &other) const { MVEpoch tmp = *this; tmp -= other; return tmp; } Bool MVEpoch::operator==(const MVEpoch &other) const { return (wday == other.wday && frday == other.frday); } Bool MVEpoch::operator!=(const MVEpoch &other) const { return (!( *this == other)); } Bool MVEpoch::near(const MVEpoch &other, Double tol) const { return ::casacore::near(get(), other.get(), tol); } Bool MVEpoch::nearAbs(const MVEpoch &other, Double tol) const { return ::casacore::nearAbs(get(), other.get(), tol); } //# Member functions void MVEpoch::assure(const MeasValue &in) { if (!dynamic_cast(&in)) { throw(AipsError("Illegal MeasValue type argument: MVEpoch")); } } void MVEpoch::adjust() { while (frday < 0) { frday += 1; wday -= 1; } while (frday >= 1) { frday -= 1; wday += 1; } } void MVEpoch::adjust(Double &res) { adjust(); res = 1.0; } Double MVEpoch::get() const { return (wday + frday); } Quantity MVEpoch::getTime() const { return (Quantity(get(), unitDay)); } Quantity MVEpoch::getTime(const Unit &unit) const { return (getTime().get(unit)); } Double MVEpoch::getDay() const { return wday; } Double MVEpoch::getDayFraction() const { return frday; } void MVEpoch::print(ostream &os) const { Int h = ifloor(24.*frday); Int m = ifloor(60.*(24.*frday - h)); Double s = MVEpoch::secInDay*frday - m*60. - h*3600.; Int prec = os.precision(); Char fill = os.fill(); os << getDay() << "::"; os << setfill('0') << setw(2) << h << ":" << setw(2) << m << ':' << setprecision(max(prec-2,2)); ios::fmtflags oldb = os.setf(ios::fixed,ios::floatfield); os << setw(os.precision()+3) << s << setprecision(prec); os.setf(oldb,ios::floatfield); os.fill(fill); } MeasValue *MVEpoch::clone() const { return (new MVEpoch(*this)); } Vector MVEpoch::getVector() const { Vector x(2); x(0) = wday; x(1) = frday; return x; } void MVEpoch::putVector(const Vector &in) { if (in.nelements() < 2) { wday = 0.0; frday = 0.0; if (in.nelements() == 1) addTime(in(0)); } else { wday = in(0); frday = in(1); } } Vector > MVEpoch::getRecordValue() const { Vector > tmp(1); tmp(0) = getTime(); return tmp; } Bool MVEpoch::putValue(const Vector > &in) { for (uInt i=0; i #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class Unit; // A class for high precision time // // // // //
        94. MeasValue class // // // // MVEpoch from Measure, Value and Epoch // // // // MVEpoch is a class for high precision (10-16 s) epochs over a // period of 6*1010 a.
          // MVEpochs can be compared, a time interval can be added or subtracted, and // the time difference can be found. // The following constructors: //
            //
          • MVEpoch() default; assuming 0 //
          • MVEpoch(Double) with time given in days //
          • MVEpoch(Double, Double=0) with times given in days //
          • MVEpoch(Quantity, Quantity=0) with times given //
          • MVEpoch(Quantum >) with times given //
          • MVEpoch(Vector) with times in days //
          • MVEpoch(Vector) with times //
          //
          // // // See MEpoch // // // // To have high precision timing // // // //
        95. A proper high precision time, including multiplication etc should // be considered. E.g. a multi-byte number with 6 bytes day // and 8 bytes fractional day. // class MVEpoch : public MeasValue { public: //# Friends //# Constructors // Default constructor, generates default 0 epoch MVEpoch(); // Copy constructor MVEpoch(const MVEpoch &other); // Constructor with time in days // MVEpoch(Double inday, Double infrac=0); MVEpoch(const Vector &inday); // // Constructor with Quantities // MVEpoch(const Quantity &in); MVEpoch(const Quantity &in1, const Quantity &in2); MVEpoch(const Quantum > &in); MVEpoch(const Vector &in); // //# Destructor ~MVEpoch(); //# Operators // Copy assignment MVEpoch &operator=(const MVEpoch &other); // Add times // MVEpoch &operator+=(const MVEpoch &other); MVEpoch operator+(const MVEpoch &other) const; // // Difference times // MVEpoch &operator-=(const MVEpoch &other); MVEpoch operator-(const MVEpoch &other) const; // // Comparisons // Bool operator==(const MVEpoch &other) const; Bool operator!=(const MVEpoch &other) const; Bool near(const MVEpoch &other, Double tol = 1e-13) const; Bool nearAbs(const MVEpoch &other, Double tol = 1e-13) const; // //# General Member Functions // Constants static const Double secInDay; static const Unit unitDay; // Tell me your type // static void assure(const MeasValue &in); // // Adjust the time to its constituent parts. The returned result is always 1.0 // virtual void adjust(); virtual void adjust(Double &res); // // Get value in days Double get() const; // Get value with units // Quantity getTime() const; Quantity getTime(const Unit &unit) const; // // Get value of integer days Double getDay() const; // Get fraction of days Double getDayFraction() const; // Print a value virtual void print(ostream &os) const; // Clone a value virtual MeasValue *clone() const; // Get the value in internal units virtual Vector getVector() const; // Set the value from internal units (set 0 for empty vector) virtual void putVector(const Vector &in); // Get the internal value as a Vector. Usable in // records. The getXRecordValue() gets additional information for records. // Note that the Vectors could be empty. // virtual Vector > getRecordValue() const; // // Set the internal value if correct values and dimensions virtual Bool putValue(const Vector > &in); private: //# Data members // Whole days // Note that if higher precision is needed, the splitting could be in // 0.001 days and fractions thereof Double wday; // Fraction of days Double frday; //# Member functions // Make days from quantity Double makeDay(const Quantity &in) const; // Add time from days void addTime(Double in); }; //# Global functions } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Quanta/MVFrequency.cc000066400000000000000000000146551476623553700202120ustar00rootroot00000000000000//# MVFrequency.cc: Internal value for MFrequency //# Copyright (C) 1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // MVFrequency class //# Constructors MVFrequency::MVFrequency() : val(0.0){} MVFrequency::MVFrequency(Double d) : val(d){} MVFrequency::MVFrequency(const MVFrequency &other) : MeasValue(), val(other.val) {} MVFrequency::MVFrequency(const Quantity &other) { val = makeF(other.getValue(), other.getFullUnit()); } MVFrequency::MVFrequency(const Quantum > &other) { Vector tmp; tmp = other.getValue(); uInt i = tmp.nelements(); if (i == 0) { val = 0.0; } else if (i == 1) { val = makeF(tmp(0), other.getFullUnit()); } else { throw (AipsError("Illegal vector length in MVFrequency constructor")); } } MVFrequency::MVFrequency(const Vector &other) { uInt i = other.nelements(); if (i == 0) { val = 0.0; } else if (i == 1) { val = other(0); } else { throw (AipsError("Illegal vector length in MVFrequency constructor")); } } MVFrequency::MVFrequency(const Vector &other) { if (!putValue(other)) { throw (AipsError("Illegal quantity vector in MVFrequency constructor")); } } MVFrequency &MVFrequency::operator=(const MVFrequency &other) { if (this != &other) { val = other.val; } return *this; } // Destructor MVFrequency::~MVFrequency() {} // Operators MVFrequency::operator Double() const { return val; } MVFrequency &MVFrequency::operator+=(const MVFrequency &other) { val += other.val; return *this; } MVFrequency &MVFrequency::operator-=(const MVFrequency &other) { val -= other.val; return *this; } Bool MVFrequency::operator==(const MVFrequency &other) const { return (val == other.val); } Bool MVFrequency::operator!=(const MVFrequency &other) const { return (val != other.val); } Bool MVFrequency::near(const MVFrequency &other, Double tol) const { return ::casacore::near(val, other.val, tol); } Bool MVFrequency::nearAbs(const MVFrequency &other, Double tol) const { return ::casacore::nearAbs(val, other.val, tol); } // Member functions void MVFrequency::assure(const MeasValue &in) { if (!dynamic_cast(&in)) { throw(AipsError("Illegal MeasValue type argument: MVFrequency")); } } void MVFrequency::print(ostream &os) const { os << val; } MeasValue *MVFrequency::clone() const { return (new MVFrequency(*this)); } Double MVFrequency::getValue() const { return val; } Quantity MVFrequency::get() const { return Quantity(val,"Hz"); } Quantity MVFrequency::get(const Unit &unit) const { return Quantity(makeF(val, unit, True), unit); } Vector MVFrequency::getVector() const { Vector x(1); x(0) = val; return x; } void MVFrequency::putVector(const Vector &in) { if (in.nelements() < 1) { val = 0.0; } else { val = in(0); } } Vector > MVFrequency::getRecordValue() const { Vector > tmp(1); tmp(0) = get(); return tmp; } Bool MVFrequency::putValue(const Vector > &in) { static const UnitVal InvTime = UnitVal::NODIM/UnitVal::TIME; static const UnitVal AngleTime = UnitVal::ANGLE/UnitVal::TIME; static const UnitVal InvLength = UnitVal::NODIM/UnitVal::LENGTH; static const UnitVal Energy = UnitVal::MASS*UnitVal::LENGTH*UnitVal::LENGTH/ UnitVal::TIME/UnitVal::TIME; static const UnitVal Impuls = UnitVal::MASS*UnitVal::LENGTH; uInt i = in.nelements(); if (i == 0) { val = 0.0; } else if (i == 1) { UnitVal dt = in(0).getFullUnit().getValue(); if (dt == UnitVal::TIME || dt == InvTime || dt == AngleTime || dt == UnitVal::LENGTH || dt == InvLength || dt == Energy || dt == Impuls) { val = makeF(in(0).getValue(), in(0).getFullUnit()); } else { return False; } } else { return False; } return True; } Double MVFrequency::makeF(Double v, const Unit &dt, Bool rev) const{ static const UnitVal InvTime = UnitVal::NODIM/UnitVal::TIME; static const UnitVal AngleTime = UnitVal::ANGLE/UnitVal::TIME; static const UnitVal InvLength = UnitVal::NODIM/UnitVal::LENGTH; static const UnitVal Energy = UnitVal::MASS*UnitVal::LENGTH*UnitVal::LENGTH/ UnitVal::TIME/UnitVal::TIME; static const UnitVal Impuls = UnitVal::MASS*UnitVal::LENGTH; static const Double LVel = QC::c( ).getBaseValue(); static const Double Planck = QC::h( ).getBaseValue(); Double x; if (dt.getValue() == UnitVal::TIME) { return (1.0/dt.getValue().getFac()/v); } else if (dt.getValue() == InvTime) { x = dt.getValue().getFac(); } else if (dt.getValue() == AngleTime) { x = dt.getValue().getFac()/M_PI/2.0; } else if (dt.getValue() == UnitVal::LENGTH) { return (LVel/dt.getValue().getFac()/v); } else if (dt.getValue() == InvLength) { x = LVel*dt.getValue().getFac()/M_PI/2.0; } else if (dt.getValue() == Energy) { x = dt.getValue().getFac()/Planck; } else { Quantity(1.0,dt).assure(Impuls); x = dt.getValue().getFac()*LVel/Planck; } if (rev) return (v/x); return (v*x); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Quanta/MVFrequency.h000066400000000000000000000135011476623553700200410ustar00rootroot00000000000000//# MVFrequency.h: Internal value for MFrequency //# Copyright (C) 1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MVFREQUENCY_H #define CASA_MVFREQUENCY_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // Internal value for MFrequency // // // // //
        96. MeasValue // // // // From Measure, Value and Frequency // // // // An MVFrequency is a simple Double, to be used in the MFrequency measure. // Requirements can be found in the // MeasValue base class.
          // The only reasonable constructor is (but all MeasValue constructors are // present) // MVFrequency(Double) (with assumed Hz units); // and an operator Double takes // care of all other possibilities. Its external use is for // MeasConvert, to distinguish between // input in internal Measure units, and values which have to have // units applied.
          // The MVFrequency(Quantum) constructors recognise the type of wave // characteristics presented from its units. Recognised are: //
            //
          • frequency (1/time) //
          • time //
          • angle/time //
          • wavelength //
          • 1/wavelength (in 2pi units) //
          • energy (h.nu) //
          • impulse //
          //
          The frequency is returned in Hz with getValue(); or as a Quantity // in Hz with get(); or in one of the above units with get(unit). //
          // // // See MFrequency // // // // To aid coordinate transformations possibilities // // // // class MVFrequency : public MeasValue { public: //# Constructors // Default constructor: generate a zero value MVFrequency(); // Copy constructor MVFrequency(const MVFrequency &other); // Copy assignment MVFrequency &operator=(const MVFrequency &other); // Constructor from Double, assuming Hz MVFrequency(Double d); // Constructor from Quantum : value taken will be the canonical value // MVFrequency(const Quantity &other); MVFrequency(const Quantum > &other); // // Constructor from Vector. A zero value will be taken for an empty vector, // the canonical value for a quantum vector. // //
        97. AipsError if vector length > 1 // // MVFrequency(const Vector &other); MVFrequency(const Vector &other); // // Destructor ~MVFrequency(); //# Operators // Conversion operator operator Double() const; // Addition // MVFrequency &operator+=(const MVFrequency &other); MVFrequency &operator-=(const MVFrequency &other); // // Comparisons // Bool operator==(const MVFrequency &other) const; Bool operator!=(const MVFrequency &other) const; Bool near(const MVFrequency &other, Double tol = 1e-13) const; Bool nearAbs(const MVFrequency &other, Double tol = 1e-13) const; // //# General member functions // Tell me your type // static void assure(const MeasValue &in); // // Print data virtual void print(ostream &os) const; // Clone virtual MeasValue *clone() const; // Adjust value: taken from base class, a NOP. // Get value in Hz Double getValue() const; // Get quantity in Hz Quantity get() const; // Get the wave characteristics in (recognised) specified units Quantity get(const Unit &unit) const; // Get the value in internal units virtual Vector getVector() const; // Set the value from internal units (set 0 for empty vector) virtual void putVector(const Vector &in); // Get the internal value as a Vector. Usable in // records. The getXRecordValue() gets additional information for records. // Note that the Vectors could be empty. // virtual Vector > getRecordValue() const; // // Set the internal value if correct values and dimensions virtual Bool putValue(const Vector > &in); private: //# Data // Value Double val; //# Member functions // Get correct data type conversion factor from input Quantum Double makeF(Double v, const Unit &dt, Bool rev=False) const; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Quanta/MVPosition.cc000066400000000000000000000334061476623553700200500ustar00rootroot00000000000000//# MVPosition.cc: A 3D vector in space //# Copyright (C) 1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constants const Double MVPosition::loLimit = 743.568; const Double MVPosition::hiLimit = 743.569; //# Constructors MVPosition::MVPosition() : xyz(3) { xyz = Double(0.0); } MVPosition::MVPosition(const MVPosition &other) : MeasValue(), xyz(3) { xyz = other.xyz; } MVPosition &MVPosition::operator=(const MVPosition &other) { if (this != &other) { xyz = other.xyz; } return *this; } MVPosition::MVPosition(Double in) : xyz(3) { xyz = Double(0.0); xyz(2) = in; } MVPosition::MVPosition(const Quantity &l) : xyz(3) { xyz = Double(0.0); l.assure(UnitVal::LENGTH); xyz(2) = l.getBaseValue(); } MVPosition::MVPosition(Double in0, Double in1, Double in2) : xyz(3) { xyz(0) = in0; xyz(1) = in1; xyz(2) = in2; } MVPosition::MVPosition(const Quantity &l, Double angle0, Double angle1) : xyz(3) { Double loc = std::cos(angle1); xyz(0) = std::cos(angle0)*loc; xyz(1) = std::sin(angle0)*loc; xyz(2) = std::sin(angle1); Double t(l.getBaseValue()); if (t<0 && t>-7.0e6) t = t/1.0e7 + hiLimit; else if (t>loLimit && t-7.0e6) t = t/1.0e7 + hiLimit; else if (t>loLimit && t > &angle) : xyz(3) { uInt i; i = angle.getValue().nelements(); if (i > 3 ) { throw (AipsError("Illegeal vector length in MVPosition constructor")); } else if (i == 3) { angle.assure(UnitVal::LENGTH); xyz = angle.getBaseValue(); } else { Vector tsin = (sin(angle)).getValue(); Vector tcos = (cos(angle)).getValue(); xyz = Double(0.0); if (i > 1) { xyz(0) = tcos(0) * tcos(1); xyz(1) = tsin(0) * tcos(1); xyz(2) = tsin(1); } else if (i > 0) { xyz(0) = tcos(0); xyz(1) = tsin(0); } else { xyz(2)=1.0; } } } MVPosition::MVPosition(const Quantity &l, const Quantum > &angle) : xyz(3) { uInt i; i = angle.getValue().nelements(); if (i > 3 ) { throw (AipsError("Illegal vector length in MVPosition constructor")); } else if (i == 3) { angle.assure(UnitVal::NODIM); xyz = angle.getValue(); } else { Vector tsin = (sin(angle)).getValue(); Vector tcos = (cos(angle)).getValue(); xyz = Double(0.0); if (i > 1) { xyz(0) = tcos(0) * tcos(1); xyz(1) = tsin(0) * tcos(1); xyz(2) = tsin(1); } else if (i > 0) { xyz(0) = tcos(0); xyz(1) = tsin(0); } else { xyz(2)=1.0; } } l.assure(UnitVal::LENGTH); readjust(l.getBaseValue()); } MVPosition::MVPosition(const Vector &other) : xyz(3) { uInt i; i = other.nelements(); if (i > 3 ) { throw (AipsError("Illegal vector length in MVPosition constructor")); } else if (i == 3) { xyz = other; } else { Vector tsin = (sin(other)); Vector tcos = (cos(other)); xyz = Double(0.0); if (i > 1) { xyz(0) = tcos(0) * tcos(1); xyz(1) = tsin(0) * tcos(1); xyz(2) = tsin(1); } else if (i > 0) { xyz(0) = tcos(0); xyz(1) = tsin(0); } else { xyz(2)=1.0; } } } MVPosition::MVPosition(const Vector &other) : xyz(3) { if (!putValue(other)) { throw (AipsError("Illegal quantum vector in MVPosition constructor")); } } //# Destructor MVPosition::~MVPosition() { } //# Operators Bool MVPosition:: operator==(const MVPosition &other) const { return (allEQ(xyz, other.xyz)); } Bool MVPosition:: operator!=(const MVPosition &other) const { return (!(*this == other)); } Bool MVPosition:: near(const MVPosition &other, Double tol) const { return (allNear(xyz, other.xyz, tol)); } Bool MVPosition:: near(const MVPosition &other, Quantity tol) const { return (separation(other,"rad") <= tol); } Bool MVPosition:: nearAbs(const MVPosition &other, Double tol) const { return (allNearAbs(xyz, other.xyz, tol)); } Double MVPosition:: operator*(const MVPosition &other) const { Double tmp = 0.0; for (Int i=0; i<3; i++) { tmp += xyz(i) * other.xyz(i); } return tmp; } Double &MVPosition::operator()(uInt which) { DebugAssert(which < 3, AipsError); return (xyz(which)); } const Double &MVPosition::operator()(uInt which) const { DebugAssert(which < 3, AipsError); return (xyz(which)); } MVPosition MVPosition::operator-() const { MVPosition tmp; tmp = *this; tmp.xyz = -xyz; return tmp; } MVPosition &MVPosition::operator+=(const MVPosition &right) { xyz += right.xyz; return *this; } MVPosition MVPosition::operator+(const MVPosition &right) const { MVPosition tmp = *this; tmp += right; return tmp; } MVPosition &MVPosition::operator-=(const MVPosition &right) { xyz -= right.xyz; return *this; } MVPosition MVPosition::operator-(const MVPosition &right) const{ MVPosition tmp = *this; tmp -= right; return tmp; } MVPosition &MVPosition::operator*=(const RotMatrix &right) { Double x = xyz(0); Double y = xyz(1); Double z = xyz(2); xyz(0) = x * right(0, 0) + y * right(1, 0) + z * right(2, 0); xyz(1) = x * right(0, 1) + y * right(1, 1) + z * right(2, 1); xyz(2) = x * right(0, 2) + y * right(1, 2) + z * right(2, 2); return *this; } MVPosition &MVPosition::operator*=(Double right) { for (Int j=0; j<3; j++) { xyz(j) *= right; } return *this; } //# Member functions void MVPosition::assure(const MeasValue &in) { if (!dynamic_cast(&in)) { throw(AipsError("Illegal MeasValue type argument: MVPosition")); } } void MVPosition::adjust() {} void MVPosition::adjust(Double &res) { res = std::sqrt(operator*(*this)); if (res != 0.0 && res != 1.0) { xyz /= res; } } void MVPosition::readjust(Double res) { if (res == 0.0) { xyz *= 1e-6; } else { xyz *= res; } } Double MVPosition::radius() { return (std::sqrt(operator*(*this))); } Vector MVPosition::get() const{ Vector tmp(3); tmp(0) = norm(xyz); tmp(1) = getLong(); tmp(2) = getLat(tmp(0)); if (tmp(0)>loLimit && tmp(0) &MVPosition::getValue() const { return xyz; } Quantum > MVPosition::getAngle() const{ Vector tp(3), tmp(2); tp = get(); tmp(0) = tp(1); tmp(1) = tp(2); return Quantum >(tmp,"rad"); } Quantum > MVPosition::getAngle(const Unit &unit) const{ return getAngle().get(unit); } Double MVPosition::getLong() const { return ((xyz(0) != 0 || xyz(1) != 0) ? std::atan2(xyz(1),xyz(0)) : 0.0); } Quantity MVPosition::getLong(const Unit &unit) const { return (Quantity(getLong(), "rad").get(unit)); } Double MVPosition::getLat() const { return getLat(norm(xyz)); } Double MVPosition::getLat(Double ln) const { return std::asin(xyz(2)/((ln == 0) ? 1.0 : ln)); } Quantity MVPosition::getLat(const Unit &unit) const { return (Quantity(getLat(), "rad").get(unit)); } Quantity MVPosition::getLength() const{ Double tmp = std::sqrt(operator*(*this)); return Quantity(tmp, "m"); } Quantity MVPosition::getLength(const Unit &unit) const { return getLength().get(unit); } Double MVPosition::positionAngle(const MVPosition &other) const { Double longDiff(getLong() - other.getLong()); Double ln(norm(xyz)); Double slat1(xyz(2)/ln); ln = norm(other.xyz); Double slat2(other.xyz(2)/ln); Double clat2(std::sqrt(std::fabs(1.0 - slat2*slat2))); Double s1(-clat2 * std::sin(longDiff)); Double c1(std::sqrt(std::fabs(1.0 - slat1*slat1))*slat2 - slat1*clat2*std::cos(longDiff)); return ((s1 != 0 || c1 != 0) ? std::atan2(s1, c1): 0.0); } Quantity MVPosition::positionAngle(const MVPosition &other, const Unit &unit) const { return Quantity(positionAngle(other), "rad").get(unit); } Double MVPosition::separation(const MVPosition &other) const { Double l1(norm(xyz)); l1 = l1 > 0 ? l1 : 1.0; Double l2(norm(other.xyz)); l2 = l2 > 0 ? l2 : 1.0; Double d1 = std::sqrt(square(xyz(0)/l1 - other.xyz(0)/l2) + square(xyz(1)/l1 - other.xyz(1)/l2) + square(xyz(2)/l1 - other.xyz(2)/l2))/2.0; return 2*std::asin(d1 < 1.0 ? d1 : 1.0); } Quantity MVPosition::separation(const MVPosition &other, const Unit &unit) const { return Quantity(separation(other), "rad").get(unit); } MVPosition MVPosition::crossProduct(const MVPosition &other) const { MVPosition res; res(0) = xyz(1)*other(2) - xyz(2)*other(1); res(1) = xyz(2)*other(0) - xyz(0)*other(2); res(2) = xyz(0)*other(1) - xyz(1)*other(0); return res; } void MVPosition::print(ostream &os) const { os << getValue(); } MeasValue *MVPosition::clone() const { return (new MVPosition(*this)); } Vector MVPosition::getVector() const { return xyz; } void MVPosition::putVector(const Vector &in) { if (in.nelements() == 3) { xyz = in; } else { xyz = 0.0; for (uInt i=0; i > MVPosition::getRecordValue() const { Vector t(3); t = get(); Vector > tmp(3); tmp(2) = Quantity(t(0), "m"); tmp(0) = Quantity(t(1), "rad"); tmp(1) = Quantity(t(2), "rad"); return tmp; } Vector > MVPosition::getXRecordValue() const { Vector > tmp(3); tmp(0) = Quantity(xyz(0), "m"); tmp(1) = Quantity(xyz(1), "m"); tmp(2) = Quantity(xyz(2), "m"); return tmp; } Bool MVPosition::putValue(const Vector > &in) { uInt i; i = in.nelements(); if (i != 3 ) return False; if (in(0).check(UnitVal::LENGTH)) { if (in(1).check(UnitVal::LENGTH) && in(2).check(UnitVal::LENGTH)) { uInt j; for (j = 0; j tsin(2), tcos(2); uInt j; for (j=1; j < i; j++) { tsin(j-1) = (sin(in(j))).getValue(); tcos(j-1) = (cos(in(j))).getValue(); } xyz = Double(0.0); xyz(0) = tcos(0) * tcos(1); xyz(1) = tsin(0) * tcos(1); xyz(2) = tsin(1); Double t(in(0).getBaseValue()); if (t<0 && t>-7.0e6) t = t/1.0e7 + hiLimit; else if (t>loLimit && t tsin(2), tcos(2); Int j; for (j=0; j < 2; j++) { tsin(j) = (sin(in(j))).getValue(); tcos(j) = (cos(in(j))).getValue(); } xyz = Double(0.0); xyz(0) = tcos(0) * tcos(1); xyz(1) = tsin(0) * tcos(1); xyz(2) = tsin(1); Double t(in(2).getBaseValue()); if (t<0 && t>-7.0e6) t = t/1.0e7 + hiLimit; else if (t>loLimit && t &left, const MVPosition &right) { MVPosition tmp(left); return (tmp * right); } Double operator*(const MVPosition &left, const Vector &right) { MVPosition tmp(right); return (tmp * left); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Quanta/MVPosition.h000066400000000000000000000243051476623553700177100ustar00rootroot00000000000000//# MVPosition.h: A 3D vector in space //# Copyright (C) 1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MVPOSITION_H #define CASA_MVPOSITION_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class RotMatrix; //# Constants (SUN compiler does not accept non-simple default arguments) // A 3D vector in space // // // // //
        98. MeasValue //
        99. Vector //
        100. Quantum // // // // From Measure, Value and Position // // // // A MVPosition is a 3-vector of positions in a rectangular frame with // internal units of m.
          // It can be constructed with: //
            //
          • MVPosition() creates point at origin (0,0,0) //
          • MVPosition(MVPosition) creates a copy //
          • MVPosition(Double, Double, Double) creates (x,y,z) with // specified values //
          • MVPosition(Quantity length,Double, Double) creates a MVPosition assuming // that the two values are (in radians) angle along 'equator' // and towards 'pole'. A length of zero will be made 1um. //
          • MVPosition(Quantity length, Quantity, Quantity) creates a MVPosition // assuming angles as in previous, or positions //
          • MVPosition(Quantity, Quantum >) creates a // MVPosition from angle vector, using first two angles, and // assuming second as zero if not present, and pole if length 0. //
          • MVPosition(Quantum > creates from // angles or positions, depending on the units in the // quantum vector. In the angle case, // the data derived can be scaled with the readjust() function. If // the unit of the quantum vector is length, position is // assumed. //
          • MVPosition(Vector creates from angles (less than // or equal to two elements) or x,y,z (3 elements). //
          • MVPosition(Vector creates from length+angles, // angles, or x,y,z, depending on units. //
          // A void adjust(Double) function normalises the vector to a length of 1; // a get() returns as a // Double 3-vector the length and angles of the position vector; // a getAngle() returns a Quantum 2-vector, (uInt) returns the indicated // element, and getValue returns the vector.
          // Positions can be added and subtracted.
          // The multiplication of two positions produces the in-product.
          //
          // // // See Mposition class. // // // // To do coordinate transformations // // // //
        101. See if not better to have a direction + length // class MVPosition : public MeasValue { public: //# Constants // Internal limts codes for negative height // static const Double loLimit; static const Double hiLimit; // //# Friends //# Constructors // Default constructor generates a (0,0,0) position MVPosition(); // Copy constructor MVPosition(const MVPosition &other); // Creates a specified vector MVPosition(Double in0, Double in1, Double in2); // Creates a vector with specified length towards pole // explicit MVPosition(Double in0); MVPosition(const Quantity &l); // // Creates the position from specified (azimuth,elevation) angles and length MVPosition(const Quantity &l, Double angle0, Double angle1); // Creates the position from specified angles and length. or positions // //
        102. AipsError if quantities not in angle format // // MVPosition(const Quantity &l, const Quantity &angle0, const Quantity &angle1); // If not enough angles: pole assumed (if none), or elevation =0 (if 1) MVPosition(const Quantum > &angle); MVPosition(const Quantity &l, const Quantum > &angle); // // Create from specified length and/or angles and/or position // explicit MVPosition(const Vector &other); MVPosition(const Vector &other); // // Copy assignment MVPosition &operator=(const MVPosition &other); // Destructor virtual ~MVPosition(); //# Operators // Multiplication defined as in-product // Double operator*(const MVPosition &other) const; // // Equality comparisons // Bool operator== (const MVPosition &other) const; Bool operator!= (const MVPosition &other) const; Bool near(const MVPosition &other, Double tol=1e-13) const; Bool near(const MVPosition &other, Quantity tol) const; Bool nearAbs(const MVPosition &other, Double tol=1e-13) const; // // Addition and subtraction // MVPosition operator-() const; MVPosition &operator+=(const MVPosition &right); MVPosition operator+(const MVPosition &right) const; MVPosition &operator-=(const MVPosition &right); MVPosition operator-(const MVPosition &right) const; // // Multiplication with rotation matrix (see also global functions) // MVPosition &operator*=(const RotMatrix &right); // // Multiplication with constant // MVPosition &operator*=(Double right); // // Obtain an element // Double &operator()(uInt which); const Double &operator()(uInt which) const; // //# General Member Functions // Tell me your type // static void assure(const MeasValue &in); // // Normalise direction aspects by adjusting the length to 1 // // For position no adjustment; for direction adjustment virtual void adjust(); // Adjustment with returned factor virtual void adjust(Double &res); // Re-adjust using factor given virtual void readjust(Double res); // // Get radius of position virtual Double radius(); // Generate a 3-vector of coordinates (length(m), angles(rad)) Vector get() const; // Generate a 3-vector of x,y,z in m const Vector &getValue() const; // Generate angle 2-vector (in rad) Quantum > getAngle() const; // and with specified units Quantum > getAngle(const Unit &unit) const; // Get the longitudinal angle (in radians) Double getLong() const; // and with specified units Quantity getLong(const Unit &unit) const; // Get the latitude angle (rad) Double getLat() const; // and with specified units Quantity getLat(const Unit &unit) const; // Generate the length Quantity getLength() const; // and generate it with the specified units Quantity getLength(const Unit &unit) const; // Get the position angle between the directions. I.e. the angle between // the direction from one to the pole, and from one to the other. // Double positionAngle(const MVPosition &other) const; Quantity positionAngle(const MVPosition &other, const Unit &unit) const; // // Get the angular separation between two directions. // Double separation(const MVPosition &other) const; Quantity separation(const MVPosition &other, const Unit &unit) const; // // Produce the cross product MVPosition crossProduct(const MVPosition &other) const; // Print data virtual void print(ostream &os) const; // Clone virtual MeasValue *clone() const; // Get the value in internal units virtual Vector getVector() const; // Set the value from internal units (set 0 for empty vector) virtual void putVector(const Vector &in); // Get the internal value as a Vector. Usable in // records. The getXRecordValue() gets additional information for records. // Note that the Vectors could be empty. // virtual Vector > getRecordValue() const; virtual Vector > getXRecordValue() const; virtual Vector > getTMRecordValue() const { return getXRecordValue(); } ; // // Set the internal value if correct values and dimensions virtual Bool putValue(const Vector > &in); protected: //# Member functions // Get the latitude assuming length is given Double getLat(Double ln) const; //# Data // Position vector (in m) Vector xyz; }; //# Global functions // Rotate a position vector with rotation matrix and other multiplications // MVPosition operator*(const RotMatrix &left, const MVPosition &right); MVPosition operator*(const MVPosition &left, const RotMatrix &right); MVPosition operator*(Double left, const MVPosition &right); MVPosition operator*(const MVPosition &left, Double right); Double operator*(const Vector &left, const MVPosition &right); Double operator*(const MVPosition &left, const Vector &right); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Quanta/MVRadialVelocity.cc000066400000000000000000000142761476623553700211630ustar00rootroot00000000000000//# MVRadialVelocity.cc: Internal value for MRadialvelocity //# Copyright (C) 1996,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // MVRadialVelocity class //# Constructors MVRadialVelocity::MVRadialVelocity() : val(0.0){} MVRadialVelocity::MVRadialVelocity(Double d) : val(d){} MVRadialVelocity::MVRadialVelocity(const MVRadialVelocity &other) : MeasValue(), val(other.val) {} MVRadialVelocity::MVRadialVelocity(const Quantity &other) { val = other.getValue() * makeF(other.getFullUnit()); } MVRadialVelocity::MVRadialVelocity(const Quantum > &other) { Vector tmp; tmp = other.getValue(); uInt i = tmp.nelements(); if (i == 0) { val = 0.0; } else if (i == 1) { val = tmp(0) * makeF(other.getFullUnit()); } else { throw (AipsError("Illegal vector length in MVRadialVelocity constructor")); } } MVRadialVelocity::MVRadialVelocity(const Vector &other) { uInt i = other.nelements(); if (i == 0) { val = 0.0; } else if (i == 1) { val = other(0); } else { throw (AipsError("Illegal vector length in MVRadialVelocity constructor")); } } MVRadialVelocity::MVRadialVelocity(const Vector &other) { if (!putValue(other)) { throw (AipsError("Illegal quantity vector in MVRadialVelocity constructor")); } } MVRadialVelocity &MVRadialVelocity::operator=(const MVRadialVelocity &other) { if (this != &other) { val = other.val; } return *this; } // Destructor MVRadialVelocity::~MVRadialVelocity() {} // Operators MVRadialVelocity::operator Double() const { return val; } MVRadialVelocity &MVRadialVelocity::operator+=(const MVRadialVelocity &other) { val += other.val; return *this; } MVRadialVelocity &MVRadialVelocity::operator-=(const MVRadialVelocity &other) { val -= other.val; return *this; } Bool MVRadialVelocity::operator==(const MVRadialVelocity &other) const { return (val == other.val); } Bool MVRadialVelocity::operator!=(const MVRadialVelocity &other) const { return (val != other.val); } Bool MVRadialVelocity::near(const MVRadialVelocity &other, Double tol) const { return ::casacore::near(val, other.val, tol); } Bool MVRadialVelocity::nearAbs(const MVRadialVelocity &other, Double tol) const { return ::casacore::nearAbs(val, other.val, tol); } // Member functions void MVRadialVelocity::assure(const MeasValue &in) { if (!dynamic_cast(&in)) { throw(AipsError("Illegal MeasValue type argument: MVRadialVelocity")); } } void MVRadialVelocity::print(ostream &os) const { os << val; } MeasValue *MVRadialVelocity::clone() const { return (new MVRadialVelocity(*this)); } Double MVRadialVelocity::getValue() const { return val; } Quantity MVRadialVelocity::get() const { return Quantity(val,"m/s"); } Quantity MVRadialVelocity::get(const Unit &unit) const { return Quantity(val/makeF(unit), unit); } Vector MVRadialVelocity::getVector() const { Vector x(1); x(0) = val; return x; } void MVRadialVelocity::putVector(const Vector &in) { if (in.nelements() < 1) { val = 0.0; } else { val = in(0); } } Vector > MVRadialVelocity::getRecordValue() const { Vector > tmp(1); tmp(0) = get(); return tmp; } Bool MVRadialVelocity::putValue(const Vector > &in) { static const UnitVal Velocity = UnitVal::LENGTH/UnitVal::TIME; uInt i = in.nelements(); if (i == 0) { val = 0.0; } else if (i == 1) { UnitVal dt = in(0).getFullUnit().getValue(); if (dt == Velocity) { val = in(0).getValue() * makeF(in(0).getFullUnit()); } else { return False; } } else { return False; } return True; } Vector MVRadialVelocity::shiftFrequency(const Vector &freq) const { Vector tmp(freq.nelements()); Double factor = val/C::c; factor = sqrt((1-factor)/(1+factor)); for (uInt i=0; i > MVRadialVelocity::shiftFrequency(const Quantum > &freq) const { Vector tmp(freq.getValue().nelements()); tmp = freq.getValue(); Double factor = val/C::c; factor = sqrt((1-factor)/(1+factor)); for (uInt i=0; i >(tmp, freq.getFullUnit()); } Double MVRadialVelocity::makeF(const Unit &dt) const{ static const UnitVal Velocity = UnitVal::LENGTH/UnitVal::TIME; Quantity(1.0,dt).assure(Velocity); return (dt.getValue().getFac()); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Quanta/MVRadialVelocity.h000066400000000000000000000143461476623553700210230ustar00rootroot00000000000000//# MVRadialVelocity.h: Internal value for MRadialvelocity //# Copyright (C) 1996,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MVRADIALVELOCITY_H #define CASA_MVRADIALVELOCITY_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Quantum; // Internal value for MRadialVelocity // // // // //
        103. MeasValue // // // // From Measure, Value and Radial Velocity // // // // An MVRadialVelocity is a simple Double, to be used in the MRadialVelocity // measure. // Requirements can be found in the // MeasValue base class.
          // The only reasonable constructor is (but all MeasValue constructors are // present) // MVRadialVelocity(Double); and an operator Double takes // care of all other possibilities. Its external use is for // MeasConvert, to distinguish between // input in internal Measure units, and values which have to have // units applied.
          // The MVRadialVelocity(Quantum) constructors recognise the type of wave // characteristics presented from its units. Recognised are: //
            //
          • velocity (length/time) //
          //
          The velocity is returned in m/s with getValue(); or as a Quantity // in m/s with get(); or in the specified units with get(unit). // // A shiftFrequency() method can shift frequencies. //
          // // // See MRadialVelocity // // // // To aid coordinate transformations possibilities // // // // class MVRadialVelocity : public MeasValue { public: //# Constructors // Default constructor: generate a zero value MVRadialVelocity(); // Copy constructor MVRadialVelocity(const MVRadialVelocity &other); // Copy assignment MVRadialVelocity &operator=(const MVRadialVelocity &other); // Constructor from Double (assume m/s) MVRadialVelocity(Double d); // Constructor from Quantum // MVRadialVelocity(const Quantity &other); MVRadialVelocity(const Quantum > &other); // // Constructor from Vector. A zero value will be taken for an empty vector, // the first element for a quantum vector. // //
        104. AipsError if vector length > 1 // // MVRadialVelocity(const Vector &other); MVRadialVelocity(const Vector &other); // // Destructor ~MVRadialVelocity(); //# Operators // Conversion operator operator Double() const; // Addition // MVRadialVelocity &operator+=(const MVRadialVelocity &other); MVRadialVelocity &operator-=(const MVRadialVelocity &other); // // Comparisons // Bool operator==(const MVRadialVelocity &other) const; Bool operator!=(const MVRadialVelocity &other) const; Bool near(const MVRadialVelocity &other, Double tol = 1e-13) const; Bool nearAbs(const MVRadialVelocity &other, Double tol = 1e-13) const; // //# General member functions // Tell me your type // static void assure(const MeasValue &in); // // Print data virtual void print(ostream &os) const; // Clone virtual MeasValue *clone() const; // Adjust value: taken from base class, a NOP. // Get value in m/s Double getValue() const; // Get quantity in m/s Quantity get() const; // Get the wave characteristics in (recognised) specified units Quantity get(const Unit &unit) const; // Get the value in internal units virtual Vector getVector() const; // Set the value from internal units (set 0 for empty vector) virtual void putVector(const Vector &in); // Get the internal value as a Vector. Usable in // records. The getXRecordValue() gets additional information for records. // Note that the Vectors could be empty. // virtual Vector > getRecordValue() const; // // Set the internal value if correct values and dimensions virtual Bool putValue(const Vector > &in); // Shift the input frequencies to the output frequencies. In the case of // simple Double inputs, it is assumed that the values are linearly dependent // on frequency. I.e. frequencies given as wavelength or time cannot be used. // Vector shiftFrequency(const Vector &freq) const; Quantum > shiftFrequency(const Quantum > &freq) const; // private: //# Data // Value Double val; //# Member functions // Get correct data type conversion factor from input Quantum Double makeF(const Unit &dt) const; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Quanta/MVTime.cc000066400000000000000000000364361476623553700171500ustar00rootroot00000000000000//# MVTime.cc: Class to handle date/time type conversions and I/O //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003,2004,2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // MVTime class //# Static members MVTime::Format MVTime::defaultFormat = MVTime::Format(); MVTime::Format MVTime::interimFormat = MVTime::Format(); Bool MVTime::interimSet = False; //# Constructors MVTime::MVTime() : val(0){} MVTime::MVTime(Double d) : val(d){} MVTime::MVTime(const Time &other) : val(other.modifiedJulianDay()){} MVTime::MVTime(const MVEpoch &other) : val(other.get()){} MVTime::MVTime(Int yy, Int mm, Double dd, Double d) { if (mm < 3) { yy--; mm += 12; } dd += d; Int b = 0; if (yy>1582 || (yy==1582 && (mm>10 || (mm==10 && dd >= 15)))) { b = ifloor(yy/100.); b = 2 - b + (Int)(b/4); } val = ifloor(365.25*yy) + ifloor(30.6001*(mm+1)) + dd - 679006.0 +b; } MVTime::MVTime(const MVTime &other) : val(other.val) {} MVTime::MVTime(const Quantity &other) { val = other.getBaseValue(); if (other.check(UnitVal::ANGLE)) { val /= C::circle; } else { other.assure(UnitVal::TIME); val /= C::day; } } MVTime &MVTime::operator=(const MVTime &other) { if (this != &other) { val = other.val; } return *this; } // Destructor MVTime::~MVTime() {} // Operators MVTime::operator Double() const { return val; } // Member functions Double MVTime::day() const { return val; } Double MVTime::hour() const { return val*24.; } Double MVTime::minute() const { return val*24.*60.; } Double MVTime::second() const { return val*24.*3600.; } Quantity MVTime::get() const { return Quantity(val,"d"); } Quantity MVTime::get(const Unit &inunit) const { if (inunit.getValue() == UnitVal::TIME) { return Quantity(val, "d").get(inunit); } return Quantity(val*C::circle,"rad").get(inunit); } Time MVTime::getTime() const { return Time(val+2400000.5); } const String &MVTime::dayName(uInt which) { static const String weekDay[7] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; AlwaysAssert(which > 0 && which < 8, AipsError); return weekDay[which-1]; } const String &MVTime::dayName() const { return (dayName(weekday())); } const String &MVTime::monthName(uInt which) { static const String mon[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; return mon[which-1]; } const String &MVTime::monthName() const { return (monthName(month())); } uInt MVTime::weekday() const { return ((ifloor(val+2.)%7 + 7)%7 + 1); } uInt MVTime::month() const { Int c,e,a; ymd(c,e,a); return e; } uInt MVTime::monthday() const { Int c,e,a; ymd(c,e,a); return a; } Int MVTime::year() const { Int c,e,a; ymd(c,e,a); return c; } Int MVTime::ymd() const { Int c,e,a; ymd(c,e,a); if (c < 0) { return -(abs(c)*10000 + e*100 + a); } return (c*10000 + e*100 + a); } uInt MVTime::yearday() const { Int c,e,a; ymd(c,e,a); if (c%4 == 0 && (c%100 != 0 || c%400 == 0)) { c = (e+9)/12; } else { c = 2 * ((e+9)/12); } return ((275*e)/9 - c + a - 30); } uInt MVTime::yearweek() const { Int yd(yearday()-4); uInt yw((yd+7)/7); yd %= 7; // Check for other week if (yd >= 0) { if (yd >= (Int)weekday()) return yw+1; } else if (yd+7 >= (Int)weekday()) return yw+1; return yw; } void MVTime::ymd(Int &yyyy, Int &mm, Int &dd) const { Int z = ifloor(val + 2400001.0); dd = z; if (z >= 2299161) { Long al = ifloor(((Double) z - 1867216.25)/36524.25); dd = z + 1 + al - (Int)(al/4); } dd += 1524; // tmp introduced to circumvent optimization problem with gcc2.7.2.1 // on the DecAlpha Int tmp = ifloor((dd - 122.1)/365.25); yyyy = tmp; Int d = ifloor(365.25 * tmp); mm = tmp = ifloor((dd - d)/30.6001); dd -= d + ifloor(30.6001 * tmp); // day if (mm < 14) { // month mm--; } else { mm -= 13; } yyyy -= 4715; // year if (mm > 2) yyyy--; } MVTime::Format MVTime::setFormat(MVTime::formatTypes intyp, uInt inprec) { Format tmp = MVTime::defaultFormat; MVTime::defaultFormat.typ = intyp; MVTime::defaultFormat.prec = inprec; MVTime::interimSet = False; return tmp; } MVTime::Format MVTime::setFormat(uInt intyp, uInt inprec) { return setFormat((MVTime::formatTypes) intyp, inprec); } MVTime::Format MVTime::setFormat(uInt inprec) { return setFormat(MVTime::TIME, inprec); } MVTime::Format MVTime::setFormat(const MVTime::Format &form) { Format tmp = MVTime::defaultFormat; MVTime::defaultFormat = form; MVTime::interimSet = False; return tmp; } MVTime::Format MVTime::getFormat() { return MVTime::defaultFormat; } MVTime::formatTypes MVTime::giveMe(const String &in) { const Int N_name = 32; static const String tab[N_name] = { "ANGLE", "TIME", "CLEAN", "NO_D", "NO_DM", "YMD", "DMY", "MJD", "DAY", "NO_TIME", "DIG2", "FITS", "LOCAL", "USE_SPACE", "ALPHA", "USE_Z", "ISO", "BOOST", "NO_H", "NO_HM", "ANGLE_CLEAN", "ANGLE_NO_D", "ANGLE_NO_DM", "ANGLE_CLEAN_NO_D", "ANGLE_CLEAN_NO_DM", "TIME_CLEAN", "TIME_NO_H", "TIME_NO_HM", "TIME_CLEAN_NO_H", "TIME_CLEAN_NO_HM", "YMD_ONLY", "MOD_MASK" }; static const MVTime::formatTypes nam[N_name] = { MVTime::ANGLE, MVTime::TIME, MVTime::CLEAN, MVTime::NO_D, MVTime::NO_DM, MVTime::YMD, MVTime::DMY, MVTime::MJD, MVTime::DAY, MVTime::NO_TIME, MVTime::DIG2, MVTime::FITS, MVTime::LOCAL, MVTime::USE_SPACE, MVTime::ALPHA, MVTime::USE_Z, MVTime::ISO, MVTime::BOOST, MVTime::NO_H, MVTime::NO_HM, MVTime::ANGLE_CLEAN, MVTime::ANGLE_NO_D, MVTime::ANGLE_NO_DM, MVTime::ANGLE_CLEAN_NO_D, MVTime::ANGLE_CLEAN_NO_DM, MVTime::TIME_CLEAN, MVTime::TIME_NO_H, MVTime::TIME_NO_HM, MVTime::TIME_CLEAN_NO_H, MVTime::TIME_CLEAN_NO_HM, MVTime::YMD_ONLY, MVTime::MOD_MASK }; Int t = MUString::minimaxNC(in, N_name, tab); return (t 1000) { // New FITS format dd = dd2; } else { if (dd2 < 50) { dd2 += 2000; } else if (dd2 < 100) { dd2 += 1900; } dd = r; // Swap day/year r = dd2; } } else if (in.testChar('/')) { if (in.freqChar('/') > 1) { in.skipChar(); if (in.testChar('/')) { in.skipChar(); mm = 1; } else { mm = in.getuInt(); if (!in.tSkipChar('/')) { return MVAngle::handleReadError (in, throwExcp); } } dd = in.getDouble(); } else { tp = 1; } } else { return MVAngle::handleReadError (in, throwExcp); } if (in.tSkipChar('/') || in.tSkipChar('-') || in.tSkipChar(' ')) { if (MVAngle::read(res, in, chk)) { res = Quantity(res.get("deg").getValue()/360., "d"); } else { return MVAngle::handleReadError (in, throwExcp); } } else if (in.tSkipChar('T')) { // new FITS/ISO if (MVAngle::read(res, in, False)) { res = Quantity(res.get("deg").getValue()/360., "d"); // Allow possible time zone as in ISO-8601 if (in.testChar('+') || in.testChar('-')) { Double s = in.getSign(); Double r = in.getuInt(); if (in.tSkipChar(':')) { r += Double(in.getuInt())/60.0; } res -= Quantity(s*r/24.0,"d"); // Time zone } else { in.tSkipChar('Z'); // accept FITS/ISO UTC } } else { return MVAngle::handleReadError (in, throwExcp); } } if (tp == 0) { res += MVTime(r, mm, dd).get(); } else { res += r; } } res *= s; // Sign if (chk) { in.skipBlank(); if (!in.eos()) { return MVAngle::handleReadError (in, throwExcp); // not fully consumed } } in.unpush(); return True; } Bool MVTime::read(Quantity &res, const String &in, Bool chk) { return read (res, in, chk, False); } Bool MVTime::read(Quantity &res, const String &in, Bool chk, Bool throwExcp) { MUString tmp(in); // Pointed non-const String if (!MVTime::read(res, tmp, chk, throwExcp)) { Double r = tmp.getDouble(); UnitVal u; String us; if (!MVAngle::unitString(u,us,tmp)) { return MVAngle::handleReadError (tmp, throwExcp); } if (u == UnitVal::NODIM) { res = Quantity(r,"d"); } else if (u == UnitVal::TIME) { res = Quantity(r,us); } else if (u == UnitVal::ANGLE) { res = Quantity(Quantity(r/(2.0*M_PI),us).getBaseValue(), "d"); } else { return MVAngle::handleReadError (tmp, throwExcp); } } return True; } ostream &operator<<(ostream &os, const MVTime &meas) { if (MVTime::interimSet) { MVTime::interimSet = False; meas.print(os, MVTime::interimFormat); } else { meas.print(os, MVTime::defaultFormat); } return os; } istream &operator>>(istream &is, MVTime &meas) { String str; is >> str; if (ios::failbit & is.rdstate()) return is; Quantity t; if (MVTime::read(t, str)) { meas = MVTime(t).get(); } else { is.clear(ios::failbit | is.rdstate()); } return is; } ostream &operator<<(ostream &os, const MVTime::Format &form) { MVTime::interimFormat = form; MVTime::interimSet = True; return os; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Quanta/MVTime.h000066400000000000000000000443771476623553700170150ustar00rootroot00000000000000//# MVTime.h: Class to handle date/time type conversions and I/O //# Copyright (C) 1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MVTIME_H #define CASA_MVTIME_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class String; class MVEpoch; class Time; //# Constants (SUN compiler does not accept non-simple default arguments) // // Class to handle date/time type conversions and I/O // // // // // //
        105. Quantum //
        106. MVAngle //
        107. // ISO8601 standard on dates and time. // // // // From Measure, Value and Time // // // // An MVTime is a simple Double for date/time conversions and I/O. // Its internal value is in MJD. For high precision the // MVEpoch class should be used.
          // It can be constructed from a Double (in which case MJD are assumed), // or from a Quantity (Quantum). Quantities must be in // either angle or time units, or from a // MVEpoch
          // The OS/Time class can be used as both input // and output. An MVTime(Time) constructor exists, as well // as a Time getTime().
          // Construction from year, month, day is also supported. // Dates before 16 Oct 1582 are considered to be Julian, // rather than Gregorian // It has an automatic conversion to Double, so all standard mathematical // operations can operate on it.
          // The class has a number of special functions to obtain data: //
            //
          • Double day() will return value in days //
          • Double hour() will return value in hours //
          • Double minute() will return value in minutes //
          • Double second() will return value in seconds //
          • Quantity get() will return days //
          • Quantity get(Unit) will return in specified units // (angle(in which case it will be between -pi and +pi) or time) //
          • uInt weekday() will return day of week (1=Mon, 7=Sun) //
          • uInt month() will return month (1=Jan) //
          • Int year() will return year //
          • uInt monthday() will return day of the month //
          • uInt yearday() will return day of year (Jan01 = 1) //
          • uInt yearweek() will return week of year // (week containing Jan04 = 1, week start on Monday). // The week before the first week will be called 0, contrary // to standard practice (week 53/52 of previous year). //
          • Int ymd() will return yyyymmdd as a single number //
          • const String &dayName() will return name of day // (Sun, Mon, Tue, Wed, Thu, Fri, Sat) //
          • const String &monthName() will retrun name of Month // (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec) //
          // Output formatting is done with the << statement, with the // following rules: //
            //
          • standard output is done in the following format: // hh:mm:ss.tt. The number of // digits presented will be based on the precision attached to the // current stream //
          • output can be formatted by using either the setFormat() // method for global angle format setting, or the output of // MVTime::Format() data for a once off change (see later). // Formats have a first argument which // determines the type (default, if not given, MVTime::TIME, other // possibility MVTime::ANGLE (as +ddd.mm.ss.tt..), // the second the number of digits wanted (default stream precision), // with a value: //
              //
            • <3 : hh:: only //
            • <5 : hh:mm: //
            • <7 : hh:mm:ss //
            • >6 : with precision-6 t's added //
            // comparable for angle. The added colons are // to enable input // checking of the format. Look at the 'clean' types to bypass them. // // The MVTime::YMD format implies TIME, and will // precede the time with 'yyyy/mm/dd/' (or use // MVTime::YMD_ONLY to include NO_TIME // modifier).
            // The MVTime::DMY format implies TIME, and will // precede the time with 'dd-Mon-yyyy/'.
            // The MVTime::FITS format implies TIME, and will // precede the time with 'ccyy-mm-ddT'.
            // The MVTime::ISO format implies FITS followed by a Z // for the UTC time zone. It uses a space instead of T as separator. // It also implies CLEAN. // The BOOST format implies DMY and USE_SPACE (space instead // of slash between date and time). //
            // The output format can be modified with modifiers (specify as // MVTime::TIME | MVTime::MOD (or + MVTime::MOD)). // For overloading/casting // problems with some compilers, the // use of modifiers necessitates either the presence of a precision // (i.e. (A|B, prec)), or an explicit cast: // ((MVTime::formatTypes)(A|B)), or make use of // the provided TIME[_CLEAN][_NO_H[M]] and // ANGLE[_CLEAN][_NO_D[M]]. // // // The modifiers can be: //
              //
            • MVTime::CLEAN to suppress leading or trailing // periods (or colons for TIME). Note that he result can not be // read automatically. //
            • MVTime::NO_H (or NO_D) to suppress // the output of hours (or degrees): useful for offsets //
            • MVTime::NO_HM (or NO_DM), to // suppress the degrees and minutes. //
            • MVTime::DAY will precede the output with // 'Day-' (e.g. Wed-). Space delimiter is used for USE_SPACE. //
            • MVTime::NO_TIME will suppress printing of time. //
            // Output in formats like 20' can be done via the standard // Quantum output (e.g. stream << time.get("'") ). //
          • Available formats: //
              //
            • MVTime::ANGLE in +ddd.mm.ss.ttt format //
            • MVTime::TIME in hh:mm:ss.ttt format //
            • MVTime::[ANGLE|TIME]_CLEAN format without superfluous periods //
            • MVTime::[ANGLE|TIME][_CLEAN]_NO_[D|H][M] in format with // leading zero fields left empty. //
            • MVTime::CLEAN modifier for suppressing superfluous periods //
            • MVTime::USE_SPACE to use a space instead of a slash // as delimiter between date and time. //
            • MVTime::USE_Z to follow the time by a Z for the UTC time zone. //
            • MVTime::NO_[D|H][M] modifier to suppress first field(s) //
            • MVTime::DIG2 modifier to get +dd.mm.ss.ttt in angle or // time format(i.e. in range -90 - +90 or -12 - +12) //
            • MVTime::LOCAL modifier to produce local time (as derived from // aipsrc time.tzoffset). In FITS mode the time zone will // be appended (as hh:mm). // The adding of the timezone is not part // of the FITS standard, but of the underlying ISO standard. It can // be used to export local times in standard format. //
            //
          // The default formatting can be overwritten by a // MVTime::setFormat(); statement; which returns an // MVTime::Format // structure, that can be used in a subsequent one to reset to previous. // The format set holds for all MVTime output on all streams.
          // Temporary formats (i.e. for one MVTime output only), can be set by // outputting a format (i.e. stream << MVTime::Format() << ... ). // A setFormat() will also // reset any lingering temporary format. // A setFormat(getFormat()) will reset without changing. Problems could // arise in parallel processors. // Input can be read if the values are in any of the above (non-clean) output // formats.
          // For other formatting practice, the output can be written to a String with // the string() member functions.
          // Note that using a temporary format is inherently thread-unsafe because // the format is kept in a static variable. Another thread may overwrite // the format just set. The only thread-safe way to format an MVTime is using // a print or string that accepts a Format object. // // Strings and input can be converted to an MVTime (or Quantity) by // Bool read(Quantity &out, const String &in) and // istream >> MVTime &. In the latter case the actual // reading is done by the String read, which reads between white-spaces.
          // The following input formats (note no blanks allowed) are supported // (+stands for an optional + or -; v for an unsigned integer; dv for a // floating number. [] indicate optional values. Separating codes are // case insensitive), numbers(like yyyy) can be of any length. // The separator between date and time part can be a slash (as shown below), // a hyphen, or one or more spaces. //
            //
          • today -- (UT) time now //
          • today/[time] -- time on today (0:0:0 if omitted) //
          • yyyy/mm/dd[/time] -- date + time. An omitted date (leading /) // will be today + time; an omitted month will // indicate use of day number in year (1 == 1/1) //
          • dd[-]MMM[-]yyyy[/time] -- date +time If yyyy <100: around 2000. // MMM can be at least first three characters // of month name; or a month number (1 == Jan). // Omitted month indicates day is day number. //
          • ccyy-mm-dd[Ttime[Z|+-hh[:mm]]] -- new FITS format the 'T' as time // separator. Time should be UTC. // The 'Z' separator (for UTC) is part of an // earlier FITS proposal, and will be recognised // for backward compatibility. // A signed hh or hh:mm can be present to // indicate time zone. This value will be // subtracted to give UTC. To recognise this // format, the year should be greater than 1000. // The time-zone information // is not part of the FITS standard, but of the // underlying ISO standard. //
          // The time can be expressed as described in // MVAngle // Examples of valid strings: // // ToDay note case independence // 1996/11/20 20 November 1996 0h UT // 1996/11/20/5:20 20 November 1996 at 5h20m // 20Nov96-5h20m same (again no case dependence) // 1996-11-20T5:20 same (FITS format, case dependent) // //
          // // // See synopsis // // // // To be able to format date/time-like values in user-required ways. // // // //
        108. Nothing I know of // class MVTime { public: //# Enumerations // Format types enum formatTypes { ANGLE, TIME, CLEAN = 4, NO_D = 8, NO_DM = NO_D+16, YMD = TIME+32, DMY = TIME+64, DAY = 128, NO_TIME = 256, MJD = TIME+512, DIG2 = 1024, FITS = TIME+2048, LOCAL = 4096, USE_SPACE = 8192, ALPHA = 16384, USE_Z = 32768, ISO = FITS + USE_Z + USE_SPACE + CLEAN, BOOST = DMY + USE_SPACE, NO_H = NO_D, NO_HM = NO_DM, ANGLE_CLEAN = ANGLE + CLEAN, ANGLE_NO_D = ANGLE + NO_D, ANGLE_NO_DM = ANGLE + NO_DM, ANGLE_CLEAN_NO_D = ANGLE + CLEAN + NO_D, ANGLE_CLEAN_NO_DM = ANGLE + CLEAN + NO_DM, TIME_CLEAN = TIME + CLEAN, TIME_NO_H = TIME + NO_H, TIME_NO_HM = TIME + NO_HM, TIME_CLEAN_NO_H = TIME + CLEAN + NO_H, TIME_CLEAN_NO_HM = TIME + CLEAN + NO_HM, YMD_ONLY = YMD + NO_TIME, MOD_MASK = CLEAN + NO_DM + DAY + NO_TIME + DIG2 + LOCAL + USE_SPACE + USE_Z + ALPHA }; //# Local structure // Format structure class Format { public: friend class MVTime; Format(MVTime::formatTypes intyp = MVTime::TIME, uInt inprec = 0) : typ(intyp), prec(inprec) {;}; Format(uInt inprec) : typ(MVTime::TIME), prec(inprec) {;}; // Construct from type and precision (present due to overlaoding problems) Format(uInt intyp, uInt inprec) : typ((MVTime::formatTypes)intyp), prec(inprec) {;}; private: MVTime::formatTypes typ; uInt prec; }; //# Friends // Output a date/time friend ostream &operator<<(ostream &os, const MVTime &meas); // Input a date/time friend istream &operator>>(istream &is, MVTime &meas); // Set a temporary format friend ostream &operator<<(ostream &os, const MVTime::Format &form); //# Constructors // Default constructor: generate a zero value MVTime(); // Copy constructor MVTime(const MVTime &other); // Copy assignment MVTime &operator=(const MVTime &other); // Constructor from Double (in MJD) MVTime(Double d); // Constructor from Quantum : value can be an angle or time // //
        109. AipsError if not a time or angle // MVTime(const Quantity &other); // Constructor from Time MVTime(const Time &other); // Constructor from MVEpoch; MVTime(const MVEpoch &other); // Constructor from yy, mm, dd, dd (all dd with fractions allowed) MVTime(Int yy, Int mm, Double dd, Double d=0.0); //# Destructor ~MVTime(); //# Operators // Conversion operator operator Double() const; //# General member functions // Make res time Quantity from string. The String version will accept // a time/angle Quantity as well. It returns False in case of an error. // chk=True means that the entire string should be consumed. // throwExcp=True means that an exception is thrown in case of an error. // static Bool read(Quantity &res, const String &in, Bool chk=True); static Bool read(Quantity &res, MUString &in, Bool chk=True); static Bool read(Quantity &res, const String &in, Bool chk, Bool throwExcp); static Bool read(Quantity &res, MUString &in, Bool chk, Bool throwExcp); // // Get value of date/time (MJD) in given units // Double day() const; Double hour() const; Double minute() const; Double second() const; Quantity get() const; Quantity get(const Unit &inunit) const; Time getTime() const; // // Get indicated part of the time/date // const String &dayName() const; static const String &dayName(uInt which); const String &monthName() const; static const String &monthName(uInt which); // Mon = 1; Sun = 7; uInt weekday() const; // Jan =1 uInt month() const; uInt monthday() const; Int year() const; Int ymd() const; uInt yearday() const; uInt yearweek() const; // // Output data. // // The first function below is thread-unsafe because it uses the result of // the setFormat function which changes a static class member. // The other functions are thread-safe because the format is directly given. // // String string() const; String string(MVTime::formatTypes intyp, uInt inprec = 0) const; String string(uInt intyp, uInt inprec) const; String string(uInt inprec) const; String string(const MVTime::Format &form) const; void print(ostream &oss, const MVTime::Format &form) const; // // Set default format // // It is thread-unsafe to print using the setFormat functions because they // change a static class member. The only thred-safe way to print a time is // to use the print function above. // // static Format setFormat(MVTime::formatTypes intyp, uInt inprec = 0); static Format setFormat(uInt intyp, uInt inprec); static Format setFormat(uInt inprec = 0); static Format setFormat(const Format &form); // // Get default format static Format getFormat(); // Get code belonging to string. 0 if not known static MVTime::formatTypes giveMe(const String &in); // Get time zone offset (in days) static Double timeZone(); private: //# Data // Value Double val; // Default format static MVTime::Format defaultFormat; // Temporary format // static MVTime::Format interimFormat; static Bool interimSet; // //# Member functions // Get the y,m,d values void ymd(Int &yyyy, Int &mm, Int &dd) const; }; // Global functions. // Output // ostream &operator<<(ostream &os, const MVTime &meas); ostream &operator>>(ostream &is, MVTime &meas); // Set a temporary format (thread-unsafe). ostream &operator<<(ostream &os, const MVTime::Format &form); // // equality and comparison operators, use operator Double which returns days inline Bool operator==(const MVTime &lh, const MVTime &rh) { return (lh.operator Double() == rh.operator Double());} inline Bool operator!=(const MVTime &lh, const MVTime &rh) { return (lh.operator Double() != rh.operator Double());} inline Bool operator<(const MVTime &lh, const MVTime &rh) { return (lh.operator Double() < rh.operator Double());} inline Bool operator<=(const MVTime &lh, const MVTime &rh) { return (lh.operator Double() <= rh.operator Double());} inline Bool operator>(const MVTime &lh, const MVTime &rh) { return (lh.operator Double() > rh.operator Double());} inline Bool operator>=(const MVTime &lh, const MVTime &rh) { return (lh.operator Double() >= rh.operator Double());} } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Quanta/MVuvw.cc000066400000000000000000000232771476623553700170720ustar00rootroot00000000000000//# MVuvw.cc: A 3D vector on Earth //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // MVuvw class //# Constructors MVuvw::MVuvw() : MVPosition() {} MVuvw::MVuvw(Double in) : MVPosition(in) {} MVuvw::MVuvw(const Quantity &l) : MVPosition(l) {} MVuvw::MVuvw(Double in0, Double in1, Double in2) : MVPosition(in0, in1, in2) {} MVuvw::MVuvw(const Quantity &l, Double angle0, Double angle1) : MVPosition(l, angle0, angle1) {} MVuvw::MVuvw(const Quantity &l, const Quantity &angle0, const Quantity &angle1) : MVPosition(l, angle0, angle1) {} MVuvw::MVuvw(const Quantum > &angle) : MVPosition(angle) {} MVuvw::MVuvw(const Quantity &l, const Quantum > &angle) : MVPosition(l, angle) {} MVuvw::MVuvw(const Vector &other) : MVPosition(other) {} MVuvw::MVuvw(const Vector &other) : MVPosition() { if (!putValue(other)) { throw (AipsError("Illegal quantum vector in MVuvw constructor")); } } MVuvw::MVuvw(const MVBaseline &pos, const MVDirection &dr, Bool ew) : MVPosition() { // Next for sgi_ntv to get it working properly MVDirection dr1(dr); dr1.adjust(); RotMatrix x(Euler(dr1.getLat() - M_PI_2, 1u, -dr1.getLong() - M_PI_2, 3u)); if (ew) {} xyz = (x * pos).getValue(); } MVuvw::MVuvw(const MVPosition &other) : MVPosition(other) {} //# Operators Bool MVuvw:: operator==(const MVuvw &other) const { return (allEQ(xyz, other.xyz)); } Bool MVuvw:: operator!=(const MVuvw &other) const { return (!(*this == other)); } Bool MVuvw:: near(const MVuvw &other, Double tol) const { return (allNear(xyz, other.xyz, tol)); } Bool MVuvw:: near(const MVuvw &other, Quantity tol) const { return (separation(other,"rad") <= tol); } Bool MVuvw:: nearAbs(const MVuvw &other, Double tol) const { return (allNearAbs(xyz, other.xyz, tol)); } Double MVuvw:: operator*(const MVuvw &other) const { Double tmp = 0.0; for (Int i=0; i<3; i++) { tmp += xyz(i) * other.xyz(i); } return tmp; } MVuvw MVuvw::operator-() const { MVuvw tmp; tmp = *this; tmp.xyz = -xyz; return tmp; } MVuvw &MVuvw::operator+=(const MVuvw &right) { xyz += right.xyz; return *this; } MVuvw MVuvw::operator+(const MVuvw &right) const { MVuvw tmp = *this; tmp += right; return tmp; } MVuvw &MVuvw::operator-=(const MVuvw &right) { xyz -= right.xyz; return *this; } MVuvw MVuvw::operator-(const MVuvw &right) const{ MVuvw tmp = *this; tmp -= right; return tmp; } //# Member functions void MVuvw::assure(const MeasValue &in) { if (!dynamic_cast(&in)) { throw(AipsError("Illegal MeasValue type argument: MVuvw")); } } void MVuvw::adjust() {} void MVuvw::adjust(Double &res) { res = std::sqrt(operator*(*this)); if (res != 0.0 && res != 1.0) { xyz /= res; } } void MVuvw::readjust(Double res) { if (res == 0.0) { xyz *= 1e-12; } else { xyz *= res; } } Double MVuvw::radius() { return (std::sqrt(operator*(*this))); } Vector MVuvw::get() const{ Vector tmp(3); tmp(0) = std::sqrt(operator*(*this)); Double ln = (tmp(0) == 0.0 ? 1.0 : tmp(0)); Double loc = xyz(0)/ln; if (loc == 0) { tmp(1) = std::asin(xyz(1)/ln); } else { tmp(1) = std::atan2(xyz(1),xyz(0)); } tmp(2) = std::asin(xyz(2)/ln); return tmp; } const Vector &MVuvw::getValue() const { return xyz; } Quantum > MVuvw::getAngle() const{ Vector tp(3), tmp(2); tp = get(); tmp(0) = tp(1); tmp(1) = tp(2); return Quantum >(tmp,"rad"); } Quantum > MVuvw::getAngle(const Unit &unit) const{ return getAngle().get(unit); } Quantity MVuvw::getLength() const{ Double tmp = std::sqrt(operator*(*this)); return Quantity(tmp,"m"); } Quantity MVuvw::getLength(const Unit &unit) const { return getLength().get(unit); } Double MVuvw::uvwAngle(const MVuvw &other) const { Vector t1(3); Vector t2(3); t1 = get(); t2 = other.get(); Double s1,c1; c1 = std::cos(t1(2)) * std::sin(t2(2)) - std::sin(t1(2)) * std::cos(t2(2)) * std::cos(t1(1) - t2(1)); s1 = -std::cos(t2(2)) * std::sin(t1(1) - t2(1)); if (s1 != 0 || c1 != 0) { return std::atan2(s1, c1); } else { return Double(0.0); } } Quantity MVuvw::uvwAngle(const MVuvw &other, const Unit &unit) const { return Quantity(uvwAngle(other), "rad").get(unit); } Double MVuvw::separation(const MVuvw &other) const { MVuvw t1(*this); MVuvw t2(other); t1.adjust(); t2.adjust(); t1 -= t2; Double d1 = t1.radius()/2.0; d1 = (d1 < 1.0 ? d1 : 1.0); return (2*std::asin(d1)); } Quantity MVuvw::separation(const MVuvw &other, const Unit &unit) const { return Quantity(separation(other), "rad").get(unit); } MVuvw MVuvw::crossProduct(const MVuvw &other) const { MVuvw res; res(0) = xyz(1)*other(2) - xyz(2)*other(1); res(1) = xyz(2)*other(0) - xyz(0)*other(2); res(2) = xyz(0)*other(1) - xyz(1)*other(0); return res; } void MVuvw::print(ostream &os) const { os << getValue(); } MeasValue *MVuvw::clone() const { return (new MVuvw(*this)); } Vector MVuvw::getVector() const { return xyz; } void MVuvw::putVector(const Vector &in) { if (in.nelements() == 3) { xyz = in; } else { xyz = 0.0; for (uInt i=0; i > MVuvw::getRecordValue() const { Vector t(3); t = get(); Vector > tmp(3); tmp(2) = Quantity(t(0), "m"); tmp(0) = Quantity(t(1), "rad"); tmp(1) = Quantity(t(2), "rad"); return tmp; } Vector > MVuvw::getXRecordValue() const { Vector > tmp(3); tmp(0) = Quantity(xyz(0), "m"); tmp(1) = Quantity(xyz(1), "m"); tmp(2) = Quantity(xyz(2), "m"); return tmp; } Bool MVuvw::putValue(const Vector > &in) { uInt i = in.nelements(); if (i != 3 ) return False; if (in(0).check(UnitVal::LENGTH)) { if (in(1).check(UnitVal::LENGTH) && in(2).check(UnitVal::LENGTH)) { for (uInt j = 0; j tsin(2), tcos(2); for (uInt j=1; j < i; j++) { tsin(j-1) = (sin(in(j))).getValue(); tcos(j-1) = (cos(in(j))).getValue(); } xyz = Double(0.0); xyz(0) = tcos(0) * tcos(1); xyz(1) = tsin(0) * tcos(1); xyz(2) = tsin(1); readjust(in(0).getBaseValue()); } else { return False; } } else if (in(2).check(UnitVal::LENGTH)) { if (in(0).check(UnitVal::ANGLE) && in(1).check(UnitVal::ANGLE)) { Vector tsin(2), tcos(2); Int j; for (j=0; j < 2; j++) { tsin(j) = (sin(in(j))).getValue(); tcos(j) = (cos(in(j))).getValue(); } xyz = Double(0.0); xyz(0) = tcos(0) * tcos(1); xyz(1) = tsin(0) * tcos(1); xyz(2) = tsin(1); readjust(in(2).getBaseValue()); } else { return False; } } return True; } MVuvw operator*(const RotMatrix &left, const MVuvw &right) { MVuvw result; for (Int i=0; i<3; i++) { result(i) = 0; for (Int j=0; j<3; j++) { result(i) += left(i,j) * right(j); } } return result; } MVuvw operator*(const MVuvw &left, const RotMatrix &right) { MVuvw result(left); result *= right; return result; } MVuvw operator*(Double left, const MVuvw &right) { MVuvw result(right); result *= left; return result; } MVuvw operator*(const MVuvw &left, Double right) { MVuvw result(left); result *= right; return result; } Double operator*(const Vector &left, const MVuvw &right) { MVuvw tmp(left); return (tmp * right); } Double operator*(const MVuvw &left, const Vector &right) { MVuvw tmp(right); return (tmp * left); } Double operator*(const MVPosition &left, const MVuvw &right) { MVuvw tmp(left); return (tmp * right); } Double operator*(const MVuvw &left, const MVPosition &right) { MVuvw tmp(right); return (tmp * left); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Quanta/MVuvw.h000066400000000000000000000215451476623553700167300ustar00rootroot00000000000000//# MVuvw.h: A 3D vector on Earth //# Copyright (C) 1998,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MVUVW_H #define CASA_MVUVW_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MVDirection; class MVBaseline; // A 3D vector on Earth // // // // //
        110. MeasValue // // // // From Measure, Value and uvw // // // // A MVuvw is a 3-vector of uvws in a rectangular frame with // internal units of m.
          // It can be constructed with: //
            //
          • MVuvw() creates point at origin (0,0,0) //
          • MVuvw(MVuvw) creates a copy //
          • MVuvw(MVPosition) creates (x,y,z) from the given position //
          • MVuvw(Double, Double, Double) creates (x,y,z) with // specified values (assuming meters) //
          • MVuvw(Quantity length,Double, Double) creates a MVuvw assuming // that the two values are (in radians) angle along 'equator' // and towards 'pole'. //
          • MVuvw(Quantity length, Quantity, Quantity) creates a MVuvw // assuming angles as in previous, or uvws //
          • MVuvw(Quantity, Quantum >) creates a // MVuvw from angle vector, using first two angles, and // assuming second as zero if not present. //
          • MVuvw(Quantum > creates from // angles or uvws, depending on the units in the // quantum vector. In the angle case, // the data derived can be scaled with the readjust() function. If // the unit of the quantum vector is length, uvw is // assumed. //
          • MVuvw(Vector creates from angles (less than // or equal to two elements) or x,y,z (3 elements). //
          • MVuvw(Vector creates from length+angles, // angles, or x,y,z, depending on units. //
          • MVuvw(MVBaseline, MVDirection) creates a uvw // in the specified reference direction (in same reference frame) //
          // A void adjust(Double) function normalises the vector to a length of 1; // a get() returns as a // Double 3-vector the length and angles of the uvw vector; // a getAngle() returns a Quantum 2-vector, (uInt) returns the indicated // element, and getValue returns the vector.
          // uvws can be added and subtracted.
          // The multiplication of two uvws produces the in-product.
          //
          // // // See Muvw class. // // // // To do coordinate transformations // // // //
        111. Implement for EW //
        112. Get sign (especially of V) correct //
        113. Let it handle Vectors of UVW //
        114. Add some rotation matrix history for speed // class MVuvw : public MVPosition { public: //# Friends //# Constructors // Default constructor generates a (0,0,0) uvw MVuvw(); // Creates from an MVPosition MVuvw(const MVPosition &other); // Creates a specified vector MVuvw(Double in0, Double in1, Double in2); // Creates a vector with specified length towards pole // explicit MVuvw(Double in0); MVuvw(const Quantity &l); // // Creates the uvw from specified (azimuth,elevation) angles and length MVuvw(const Quantity &l, Double angle0, Double angle1); // Creates the uvw from specified angles and length. or uvws // //
        115. AipsError if quantities not in angle format // // MVuvw(const Quantity &l, const Quantity &angle0, const Quantity &angle1); // If not enough angles: pole assumed (if none), or elevation =0 (if 1) MVuvw(const Quantum > &angle); MVuvw(const Quantity &l, const Quantum > &angle); // // Create from specified length and/or angles and/or uvw // MVuvw(const Vector &other); MVuvw(const Vector &other); // // uvw from a baseline and a reference direction (in same frame) // MVuvw(const MVBaseline &pos, const MVDirection &dr, Bool ew=False); // //# Operators // Multiplication defined as in-product // Double operator*(const MVuvw &other) const; // // Equality comparisons // Bool operator== (const MVuvw &other) const; Bool operator!= (const MVuvw &other) const; Bool near(const MVuvw &other, Double tol=1e-13) const; Bool near(const MVuvw &other, Quantity tol) const; Bool nearAbs(const MVuvw &other, Double tol=1e-13) const; // // Addition and subtraction // MVuvw operator-() const; MVuvw &operator+=(const MVuvw &right); MVuvw operator+(const MVuvw &right) const; MVuvw &operator-=(const MVuvw &right); MVuvw operator-(const MVuvw &right) const; // //# General Member Functions // Tell me your type // static void assure(const MeasValue &in); // // Normalise direction aspects by adjusting the length to 1 // virtual void adjust(); virtual void adjust(Double &res); virtual void readjust(Double res); // // Get radius(i.e. length of vector, in m) of uvw virtual Double radius(); // Generate a 3-vector of coordinates (length(m), angles(rad)) Vector get() const; // Generate a 3-vector of x,y,z in m const Vector &getValue() const; // Generate angle 2-vector (in rad) Quantum > getAngle() const; // and with specified units Quantum > getAngle(const Unit &unit) const; // Generate the length Quantity getLength() const; // and generate it with the specified units Quantity getLength(const Unit &unit) const; // Get the uvw angle between the directions. I.e. the angle between // the direction from one to the pole, and from one to the other. // Double uvwAngle(const MVuvw &other) const; Quantity uvwAngle(const MVuvw &other, const Unit &unit) const; // // Get the angular separation between two directions. // Double separation(const MVuvw &other) const; Quantity separation(const MVuvw &other, const Unit &unit) const; // // Produce the cross product MVuvw crossProduct(const MVuvw &other) const; // Print data virtual void print(ostream &os) const; // Clone virtual MeasValue *clone() const; // Get the value in internal units virtual Vector getVector() const; // Set the value from internal units (set 0 for empty vector) virtual void putVector(const Vector &in); // Get the internal value as a Vector. Usable in // records. The getXRecordValue() gets additional information for records. // Note that the Vectors could be empty. // virtual Vector > getRecordValue() const; virtual Vector > getXRecordValue() const; virtual Vector > getTMRecordValue() const { return getXRecordValue(); } ; // // Set the internal value if correct values and dimensions virtual Bool putValue(const Vector > &in); }; //# Global functions // Rotate a uvw vector with rotation matrix and other multiplications // MVuvw operator*(const RotMatrix &left, const MVuvw &right); MVuvw operator*(const MVuvw &left, const RotMatrix &right); MVuvw operator*(Double left, const MVuvw &right); MVuvw operator*(const MVuvw &left, Double right); Double operator*(const Vector &left, const MVuvw &right); Double operator*(const MVuvw &left, const Vector &right); Double operator*(const MVPosition &left, const MVuvw &right); Double operator*(const MVuvw &left, const MVPosition &right); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Quanta/MeasValue.cc000066400000000000000000000037261476623553700176650ustar00rootroot00000000000000//# MeasValue.cc: Base class for values in a Measure //# Copyright (C) 1996,1997,1998,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constants //# Constructors //# Destructor MeasValue::~MeasValue() {} //# Operators //# Member functions Vector > MeasValue::getXRecordValue() const { return Vector >(0); } Vector > MeasValue::getTMRecordValue() const { return getRecordValue(); } void MeasValue::adjust() {} void MeasValue::adjust(Double &val) { val = 1.0; } void MeasValue::readjust(Double) { } //# Global functions ostream &operator<<(ostream &os, const MeasValue &meas) { meas.print(os); return os; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Quanta/MeasValue.h000066400000000000000000000163461476623553700175310ustar00rootroot00000000000000//# MeasValue.h: Base class for values in a Measure //# Copyright (C) 1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MEASVALUE_H #define CASA_MEASVALUE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Quantum; // // Base class for values in a Measure // // // // // //
        116. Measure class // // // // // // // MeasValue forms the abstract base class for the values of quantities within // a reference frame. Examples of derived classes are: //
            //
          • MVEpoch: a moment in time //
          • MVDirection: a direction in space //
          • MVPosition: a position on Earth //
          • MVFrequency //
          • MVRadialVelocity //
          • MVDoppler //
          // MeasValue is the generic name for the more specific instances like, e.g., // MVEpoch, an instant in time.
          // MeasValues can in general be constructed from an appropiate value, or array // of values.
          // The value can be expressed in the internally used units (e.g. // days for time, a 3-vector for direction in space), as an array of internally // used units, or as a Quantum: a value with // appropiate units. Vector > // and Quantum > can // also be used. // // The value of the MeasValue can be obtained by a variety of // get functions, returning in general internal or Quantum // values. Special formatting (like hh:mm:ss.t, dd.mm.ss.t, yy/mm/dd etc) // are catered for in conversion-type classes like // MVTime, MVAngle // // Note that the class is a pure virtual class. No instances can be created, // but it describes the minimum set of functions necessary in derived functions. // In the member description a number of dummy routines are // present. They are the only way I have found to get cxx2html to // get the belonging text properly presented. // //
          // // // See individual MV and Measure classes // // // // To be able to specify a physical entity appropiate for the measured // quantity. // // // // class MeasValue { public: //# Enumerations //# Typedefs //# Friends // Output a MeasValue friend ostream &operator<<(ostream &os, const MeasValue &meas); //# Constructor // Each derived class should have at least the following constructors: // // MV() // some default // MV(Double) // some default or error if vector expected // MV(Vector) // with check for array number of elements // MV(Quantity) // MV(Vector) // MV(Quantum > // // Float (or other standard type) versions could be added if appropiate. // Dummy for cxx2html void dummy_constr() const {;}; //# Destructor // Destructor virtual ~MeasValue(); //# Operators // The following operators should be present at least. // // MV &operator+=(const MV &meas); // MV &operator-=(const MV &meas); // Bool operator==(const MV &meas) const; // Bool operator!=(const MV &meas) const; // Bool near(const MV &meas, Double tol = 1e-13) const; // Bool nearAbs(const MV &meas, Double tol = 1e-13) const; // // Dummy for cxx2html void dummy_operator() const {;}; //# General Member Functions // Print a MeasValue virtual void print(ostream &os) const = 0; // Clone a MeasValue virtual MeasValue *clone() const = 0; // Get the internal value as a Vector. // Note that the vector could // be empty, or not be a true representation (different data sizes e.g.) virtual Vector getVector() const = 0; // Get the internal value as a Vector. Usable in // records. The getXRecordValue() gets additional information for records. // The getTMRecordValue() gets the record values as deemed appropriate for // the TableMeasures. // Note that the Vectors could be empty. // virtual Vector > getRecordValue() const = 0; virtual Vector > getXRecordValue() const; virtual Vector > getTMRecordValue() const; // // Set the internal value from a Vector of values (obtained in principle // with a getVector()). It will be assumed that the Vector is correctly // formatted. If Vector is too long, the remainder will be discarded. // If Vector is too short, action will depend on the individual classes, // but in general act the same way as a constructor with a short Vector. virtual void putVector(const Vector &in) = 0; // Set the internal value if correct values and dimensions virtual Bool putValue(const Vector > &in) = 0; // Some of the Measure values used need the occasional adjustments to proper // values. Examples are MVDirection (direction cosines) which have to be // normalised to a length of 1 and MEpoch (time) which have to have its // precision maintained. For others it is an effctive no-operation. // // Adjust value virtual void adjust(); // Adjust value and return a normalisation value virtual void adjust(Double &val); // Re-adjust, i.e. undo a previous adjust, with value virtual void readjust(Double val); // private: }; //# Global functions // Global functions // // Output declaration ostream &operator<<(ostream &os, const MeasValue &meas); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Quanta/QBase.cc000066400000000000000000000042031476623553700167650ustar00rootroot00000000000000//# QBase.cc: base class for Quantum //# Copyright (C) 1994,1995,1996,1998,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN QBase::QBase() : qUnit() {} QBase::QBase(const Unit &s) : qUnit(s) {} QBase::~QBase() {} //# QBase general member functions const String &QBase::getUnit() const { return qUnit.getName(); } void QBase::setUnit(const Unit &s) { qUnit = s; } void QBase::setUnit(const QBase &other) { qUnit = other.qUnit; } Bool QBase::isConform(const Unit &s) const { return (qUnit.getValue() == s.getValue()); } Bool QBase::isConform(const QBase &other) const { return (qUnit.getValue() == other.qUnit.getValue()); } //# Global functions ostream &operator<<(ostream &os, const QBase &meas) { meas.print(os); return os; } LogIO &operator<<(LogIO& os, const QBase &meas) { os.output() << meas; return os; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Quanta/QBase.h000066400000000000000000000101471476623553700166330ustar00rootroot00000000000000//# QBase.h: base class for Quantum //# Copyright (C) 1994,1995,1996,1998,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_QBASE_H #define CASA_QBASE_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class LogIO; //# Typedefs // // // Base for Quantities (i.e. dimensioned values) // // // // // //
        117. Unit // // // // QBase is the base class for Quantum. // // // // Quantities are values with a unit. Their basic specification can be one of // two forms: // // Quantity( Double value, String unit); // or: Unit unit // Quantum ( Type value, String unit) // or: Unit unit // // See Quantum for details. // // // // To provide the possibilty of mixing units from different // Quantum, Quantum // // // //
        118. Some inlining (did not work first go) // class QBase { //# Friends public: //# Constructors // Default constructor, generates "" QBase(); // Construct dimensioned QBase (e.g. 'km/Mpc') // //
        119. AipsError if non-matching unit dimensions // // QBase(const Unit &s); // // Destructor virtual ~QBase(); //# Member functions // Get units of QBase // // Return the string representation of the current units attached to QBase const String &getUnit() const; // // Re-specify parts of a QBase // // Set new unit, without changing value void setUnit(const Unit &s); // Set new unit, copied from specified QBase, without changing value void setUnit(const QBase &other); // // Check for conformal matching units (e.g. dam and Mpc) // // Using specified units Bool isConform(const Unit &s) const; // Using units specified in QBase Bool isConform(const QBase &other) const; // // Get a copy of Quantum virtual QBase *clone() const = 0; // Get the unit attached to the Quantum (use getUnit() if only interested in // the String part of the unit) virtual const Unit &getFullUnit() const = 0; // Print a Quantum virtual void print(ostream &os) const = 0; // Get the type of derived Quantum (using QuantumType). // All should have: // static uInt myType(); virtual uInt type() const = 0; protected: //# Data members Unit qUnit; }; //# Inline Implementations //# Global functions // Global functions // // Output declaration ostream &operator<<(ostream &os, const QBase &meas); LogIO &operator<<(LogIO &os, const QBase &meas); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Quanta/QC.cc000066400000000000000000000023211476623553700162740ustar00rootroot00000000000000//# QC.cc: physical constants with units //# Copyright (C) 1994-1998,2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes casacore-3.7.1/casa/Quanta/QC.h000066400000000000000000000176531476623553700161540ustar00rootroot00000000000000//# QC.h: physical constants with units //# Copyright (C) 1994,1995,1996,1997,1998,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_QC_H #define CASA_QC_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations //# Typedefs // // Physical constants (i.e. dimensioned values) // // // // //
        120. Quantum // // // A QC is based on the Quantum and C (constants) class // // // QC:name will produce a Quantity (Quantum<Double>) value consisting of // a value and a unit. See the Quantum class // for possibilities of manipulating quanta. // tQuantum will give a list of the currently available constants // // // To obtain the velocity of light in pc/a, use: // // #include // Double vel_pcpy = (C::c).convert("pc/a").getValue(); // // //############################################################################ //# NOTE: Delete the following listing of the public data members when //# public data members are handled properly by the documentation //# extractor. //############################################################################ // The following constants are defined as public data members of class QC. // // // The following public data member documentation is currently extracted by // hand, and thus could be out of date if this documentation was not updated // when the class was modified. // // // // // vel of light // Quantum c( ); // // // Gravitational constant // Quantum G( ); // // // Planck // Quantum h( ); // // // HI line // Quantum HI( ); // // // Gas constant // Quantum R( ); // // // Avogadro // Quantum NA( ); // // // electron charge // Quantum e( ); // // // proton mass // Quantum mp( ); // // // mp/me // Quantum mp_me( ); // // // permeability vacuum // Quantum mu0( ); // // // permittivity vacuum // Quantum epsilon0( ); // // // Boltzmann // Quantum k( ); // // // Faraday // Quantum F( ); // // // mass electron // Quantum me( ); // // // radius electron // Quantum re( ); // // // Bohr's radius // Quantum a0( ); // // // Solar radius // Quantum R0( ); // // // IAU Gaussian grav. const **2 // Quantum k2( ); // // // quarter turn = 90 degrees = pi/2 radians // Quantum qTurn( ); // // // half turn = 180 degrees = pi radians // Quantum hTurn( ); // // // full turn = 360 degrees = 2pi radians // Quantum fTurn( ); // // // // Physical constants should be known with their proper dimensions // // // // class QC { friend class QC_init; public: //# If you change any of the public data members, make the corresponding //# change above to the documentation of the public data members. // vel of light inline static const Quantum &c( ) { static Quantum result(C::c,"m/s"); return result; } // Gravitational constant inline static const Quantum &G( ) { static Quantum result(6.67259e-11,"N.m2/kg2"); return result; } // Planck inline static const Quantum &h( ) { static Quantum result(6.6260755e-34,"J.s"); return result; } // HI line inline static const Quantum &HI( ) { static Quantum result(1420.405751786, "MHz"); return result; } // Gas constant inline static Quantum &R( ) { static Quantum result(8.314510,"J/K/mol"); return result; } // Avogadro inline static const Quantum &NA( ) { static Quantum result(6.0221367e+23,"mol-1"); return result; } // electron charge inline static const Quantum &e( ) { static Quantum result(1.60217733e-19,"C"); return result; } // proton mass inline static const Quantum &mp( ) { static Quantum result(1.6726231e-27,"kg"); return result; } // mp/me inline static const Quantum &mp_me( ) { static Quantum result(1836.152701,""); return result; } // permeability vacuum inline static const Quantum &mu0( ) { static Quantum result(4.0e-7*M_PI,"H/m"); return result; } // permittivity vacuum inline static const Quantum &epsilon0( ) { static Quantum result(1.0/(4.0e-7*M_PI*C::c*C::c),"F/m"); return result; } // Boltzmann inline static const Quantum &k( ) { static Quantum result(8.314510/6.0221367e+23,"J/K"); return result; } // Faraday inline static const Quantum &F( ) { static Quantum result(6.0221367e+23*1.60217733e-19,"C/mol"); return result; } // mass electron inline static const Quantum &me( ) { static Quantum result(1.6726231e-27/1836.152701,"kg"); return result; } // radius electron inline static const Quantum &re( ) { static Quantum result(2.8179e-15,"m"); return result; } // Bohr's radius inline static const Quantum &a0( ) { static Quantum result(5.2918e-11,"m"); return result; } // Solar radius inline static const Quantum &R0( ) { static Quantum result(6.9599e+08,"m"); return result; } // IAU Gaussian grav. const **2 inline static const Quantum &k2( ) { const Double IAU_k=0.01720209895; static Quantum result(IAU_k*IAU_k,"AU3/d2/S0"); return result; } // quarter turn = 90 degrees = pi/2 radians inline static const Quantum &qTurn( ) { static Quantum result(90.0, "deg"); return result; } // half turn = 180 degrees = pi radians inline static const Quantum &hTurn( ) { static Quantum result(180.0, "deg"); return result; } // full turn = 360 degrees = 2pi radians inline static const Quantum &fTurn( ) { static Quantum result(360.0, "deg"); return result; } }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Quanta/QLogical.h000066400000000000000000000172211476623553700173330ustar00rootroot00000000000000//# QLogical.h: class to manipulate physical, dimensioned quantities //# Copyright (C) 1994,1995,1996,1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_QLOGICAL_H #define CASA_QLOGICAL_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations //# Typedefs // // Logical operations for the Quantum class. // // // // // //
        121. Unit //
        122. Quantum // // // // QLogical derived from Quantum logical functions // // // // Quantities are values with a unit. Their basic specification can be one of // two forms: // // Quantity( Double value, String unit); // or: Unit unit // Quantum ( Type value, String unit) // or: Unit unit // // // A unit is a string of known unit fields separated // by 'space' or '.' (to indicate multiply) or '/' (to indicate divide). // See the Unit class for details. // Example: km/s/(Mpc.s)2 is identical to km.s-1.Mpc-2.s-2 // // This file defines the logical operations that can be done on // Quantum. // // They can be subdivided into various groupings: //
            //
          • Straight comparisons: // unequal if non-conforming units or different values //
          • Comparisons //
          • Special make Bool routines // to cater for array comparisons //
          // // The operations defined are: //
            //
          • Quantum == Quantum or ==T //
          • Quantum != Quantum or !=T //
          • > < >= <= of Quantum or T and Quantum //
          • near, nearAbs(Quantum or T, Quantum or T [, tolerance]) //
          //
          // // // To separate the logical operations from Quantum // // // //
        123. Some inlining (did not work first go) //
        124. Recode with allEQ etc if part of library for all data types, // and get rid of the special QMakeBool(), and the // include LogiArrayFwd.h // // // // Quantum logical operations -- Logical operations // for the Quantum class. // // // // Straight comparisons: unequal if non-conforming units or different values // if units made equal. I.e. 1in != 1m , // 1in != 1s , 36in == 1yd ./ // template Bool operator==(const Quantum &left, const Quantum &other); template Bool operator==(const Quantum &left, const Qtype &other); template Bool operator==(const Qtype &left, const Quantum &other); template Bool operator!=(const Quantum &left, const Quantum &other); template Bool operator!=(const Quantum &left, const Qtype &other); template Bool operator!=(const Qtype &left, const Quantum &other); // // Near-ness tests: unequal if non-conforming units. In other cases the // tolerance is in the units of the first argument, with the second argument // converted, if necessary, to the same units as the first. // Note (SUN?) compiler does not accept default arguments in template functions // template Bool near(const Quantum &left, const Quantum &other); template Bool near(const Quantum &left, const Quantum &other, Double tol); template Bool near(const Quantum &left, const Qtype &other); template Bool near(const Quantum &left, const Qtype &other, Double tol); template Bool near(const Qtype &left, const Quantum &other); template Bool near(const Qtype &left, const Quantum &other, Double tol); template Bool nearAbs(const Quantum &left, const Quantum &other); template Bool nearAbs(const Quantum &left, const Quantum &other, Double tol); template Bool nearAbs(const Quantum &left, const Quantum &other, const Quantum& tol); template Bool nearAbs(const Quantum &left, const Qtype &other); template Bool nearAbs(const Quantum &left, const Qtype &other, Double tol); template Bool nearAbs(const Qtype &left, const Quantum &other); template Bool nearAbs(const Qtype &left, const Quantum &other, Double tol); // // // Comparisons. The comparisons are done on values at equal units with // transparent conversion if necessary. // //
        125. AipsError if non-conforming units // // template Bool operator<(const Quantum &left, const Quantum &other); template Bool operator<(const Quantum &left, const Qtype &other); template Bool operator<(const Qtype &left, const Quantum &other); template Bool operator>(const Quantum &left, const Quantum &other); template Bool operator>(const Quantum &left, const Qtype &other); template Bool operator>(const Qtype &left, const Quantum &other); template Bool operator<=(const Quantum &left, const Quantum &other); template Bool operator<=(const Quantum &left, const Qtype &other); template Bool operator<=(const Qtype &left, const Quantum &other); template Bool operator>=(const Quantum &left, const Quantum &other); template Bool operator>=(const Quantum &left, const Qtype &other); template Bool operator>=(const Qtype &left, const Quantum &other); // // // Special make Bool routines to cater for array comparisons // Bool QMakeBool(Int val); Bool QMakeBool(const LogicalArray &val); // //# Inline Implementations // } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/casa/Quanta/QLogical.tcc000066400000000000000000000173321476623553700176600ustar00rootroot00000000000000//# QLogical.cc: class to manipulate physical, dimensioned quantities //# Copyright (C) 1994,1995,1996,1997,1998,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_QLOGICAL_TCC #define CASA_QLOGICAL_TCC //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template Bool operator==(const Quantum &left, const Quantum &other) { if (left.getFullUnit().getValue() == other.getFullUnit().getValue() ){ Qtype tmp; tmp = other.get(left.getFullUnit()).getValue(); return QMakeBool((left.getValue()) == (tmp)); } return False; } template Bool operator==(const Quantum &left, const Qtype &other) { Quantum loc; loc = other; return QMakeBool(left == loc); } template Bool operator==(const Qtype &left, const Quantum &other) { Quantum loc; loc = left; return QMakeBool(loc == other); } template Bool operator!=(const Quantum &left, const Quantum &other) { return QMakeBool(!(left == other)); } template Bool operator!=(const Quantum &left, const Qtype &other) { return QMakeBool(!(left == other)); } template Bool operator!=(const Qtype &left, const Quantum &other) { return QMakeBool(!(left == other)); } template Bool near(const Quantum &left, const Quantum &other) { if (left.getFullUnit().getValue() == other.getFullUnit().getValue()) { return QMakeBool(near(left.getValue(), other.get(left.getFullUnit()).getValue())); } return False; } template Bool near(const Quantum &left, const Quantum &other, Double tol) { UnitVal kind, knew; if (left.getFullUnit().getValue() == other.getFullUnit().getValue()) { return QMakeBool(near(left.getValue(), other.get(left.getFullUnit()).getValue(),tol)); } return False; } template Bool near(const Quantum &left, const Qtype &other) { Quantum loc; loc = other; return QMakeBool(near(left, loc)); } template Bool near(const Quantum &left, const Qtype &other, Double tol) { Quantum loc; loc = other; return QMakeBool(near(left, loc, tol)); } template Bool near(const Qtype &left, const Quantum &other) { Quantum loc; loc = left; return QMakeBool(near(loc, other)); } template Bool near(const Qtype &left, const Quantum &other, Double tol) { Quantum loc; loc = left; return QMakeBool(near(loc, other, tol)); } template Bool nearAbs(const Quantum &left, const Quantum &other) { if (left.getFullUnit().getValue() == other.getFullUnit().getValue()) { return QMakeBool(nearAbs(left.getValue(), other.get(left.getFullUnit()).getValue())); } return False; } template Bool nearAbs(const Quantum &left, const Quantum &other, Double tol) { if (left.getFullUnit().getValue() == other.getFullUnit().getValue()) { return QMakeBool(nearAbs(left.getValue(), other.get(left.getFullUnit()).getValue(),tol)); } return False; } template Bool nearAbs(const Quantum &left, const Quantum &other, const Quantum& tol) { if (left.getFullUnit().getValue() == tol.getFullUnit().getValue()) { return nearAbs( left.get(tol.getUnit()), other.get(tol.getUnit()), tol.getValue() ); } return False; } template Bool nearAbs(const Quantum &left, const Qtype &other) { Quantum loc; loc = other; return QMakeBool(nearAbs(left, loc)); } template Bool nearAbs(const Quantum &left, const Qtype &other, Double tol) { Quantum loc; loc = other; return QMakeBool(nearAbs(left, loc, tol)); } template Bool nearAbs(const Qtype &left, const Quantum &other) { Quantum loc; loc = left; return QMakeBool(nearAbs(loc, other)); } template Bool nearAbs(const Qtype &left, const Quantum &other, Double tol) { Quantum loc; loc = left; return QMakeBool(nearAbs(loc, other, tol)); } template Bool operator<(const Quantum &left, const Quantum &other) { if (left.getFullUnit().getValue() != other.getFullUnit().getValue()) { throw (AipsError("Quantum::operator< unequal units '" + left.getUnit() + ", '" + other.getUnit() + "'")); } else { return QMakeBool(QMakeBool(left.getValue() < other.get(left.getFullUnit()).getValue())); } return False; } template Bool operator<(const Quantum &left, const Qtype &other) { Quantum loc; loc = other; return QMakeBool(left < loc); } template Bool operator<(const Qtype &left, const Quantum &other) { Quantum loc; loc = left; return QMakeBool(loc < other); } template Bool operator>(const Quantum &left, const Quantum &other) { if (left.getFullUnit().getValue() != other.getFullUnit().getValue()) { throw (AipsError("Quantum::operator< unequal units '" + left.getUnit() + ", '" + other.getUnit() + "'")); } else { return QMakeBool(left.getValue() > other.get(left.getFullUnit()).getValue()); } return False; } template Bool operator>(const Quantum &left, const Qtype &other) { Quantum loc; loc = other; return QMakeBool(left > loc); } template Bool operator>(const Qtype &left, const Quantum &other) { Quantum loc; loc = left; return QMakeBool(loc > other); } template Bool operator<=(const Quantum &left, const Quantum &other) { return QMakeBool(!(left > other)); } template Bool operator<=(const Quantum &left, const Qtype &other) { return QMakeBool(!(left > other)); } template Bool operator<=(const Qtype &left, const Quantum &other) { return QMakeBool(!(left > other)); } template Bool operator>=(const Quantum &left, const Quantum &other) { return QMakeBool(!(left < other)); } template Bool operator>=(const Quantum &left, const Qtype &other) { return QMakeBool(!(left < other)); } template Bool operator>=(const Qtype &left, const Quantum &other) { return QMakeBool(!(left < other)); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Quanta/QLogical2.cc000066400000000000000000000031741476623553700175550ustar00rootroot00000000000000//# QLogical.cc: class to manipulate physical, dimensioned quantities //# Copyright (C) 1994,1995,1996,1998,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Bool QMakeBool(Int val) { return ((val)); } Bool QMakeBool(const LogicalArray &val) { return (allAND(val, True)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Quanta/QMath.h000066400000000000000000000215471476623553700166600ustar00rootroot00000000000000//# QMath.h: Mathematical operations for the Quantum class. //# Copyright (C) 1994,1995,1996,1998,1999,2000,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_QMATH_H #define CASA_QMATH_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Typedefs // // Mathematical operations for the Quantum class. // // // // // // //
        126. Unit //
        127. Quantum // // // // QMath derived from Quantum Mathematical functions // // // // Quantities are values with a unit. Their basic specification can be one of // two forms: // // Quantity( Double value, String unit); // or: Unit unit // Quantum ( Type value, String unit) // or: Unit unit // // // A unit is a string of known unit fields separated // by a space or a . (to indicate multiply) or a / (to indicate divide). // See the Unit class for details. // Example: km/s/(Mpc.s)2 is identical to km.s-1.Mpc-2.s-2 // // This file defines the mathematical operations that can be done on // Quantum. // // They can be subdivided into various groupings: //
            //
          • Unary operations //
          • In place arithmetic functions: left hand side changed in place //
          • Arithmetic functions: return Quantum //
          • Some useful arithmetic (linear) functions //
          • Trigonometric functions //
          • Functions to implement integer ceil/floor //
          // // The operations/functions defined are: //
            //
          • unary +(Quantum) //
          • unary -(Quantum) //
          • +=Quantum; +=T; -=Quantum; -=T; //
          • *=Quantum, *=T; /=Quantum; /=T; //
          • +,-,*,/ for Quantum,Quantum; T,Quantum; Quantum,T; //
          • abs, ceil, floor(Quantum) //
          • pow(Quantum, Int); //
          • sin, cos, tan(Quantum) with proper unit handling //
          • asin, acos, atan, atan2(Quantum) with proper unit handling //
          • log, log10, exp, root, sqrt with proper unit handling //
          // // Some operators are implemented as member functions, and can be found in the // Quantum class. // //
          // // // To separate the mathematical operations from Quantum. // // // //
        128. Some inlining (did not work first go) // // // Quantum mathematical operations -- Mathematical operations // for the Quantum class. // // // Unary operations // // See Quantum class // // In place arithmetic functions: left hand side changed in place // //
        129. AipsError if non-conforming units (+ and -) //
        130. AipsError if illegal result unit (* and /; programming error) // // // See Quantum class // // Arithmetic operators: return Quantum // //
        131. AipsError if non-conforming units (+ and -) // // // See Quantum class for equal argument types template Quantum operator+(const Quantum &left, const Qtype &other); template Quantum operator+(const Qtype &left, const Quantum &other); template Quantum operator-(const Quantum &left, const Qtype &other); template Quantum operator-(const Qtype &left, const Quantum &other); template Quantum operator*(const Quantum &left, const Qtype &other); template Quantum operator*(const Qtype &left, const Quantum &other); template Quantum operator/(const Quantum &left, const Qtype &other); template Quantum operator/(const Qtype &left, const Quantum &other); // // Some useful arithmetic (linear) functions // // Return the Quantum raised to specified power; take the (integer) root; // and integerization // //
        132. AipsError if power exponent too large (abs > 100) //
        133. AipsError if root exponent zero // template Quantum pow(const Quantum &left, Int p); template Quantum root(const Quantum &left, Int p); template Quantum sqrt(const Quantum &left); template Quantum abs(const Quantum &left); template Quantum ceil(const Quantum &left); template Quantum floor(const Quantum &left); // // Trigonometric and exponential functions // For direct functions input should be in angles, output will be empty units. // For inverse functions input should be empty, output in radians // //
        134. AipsError if incorrect units. I.e. non-angle for direct functions, // non-empty for inverse functions; non-empty for exp and log // // template Quantum sin(const Quantum &left); template Quantum cos(const Quantum &left); template Quantum tan(const Quantum &left); template Quantum asin(const Quantum &left); template Quantum acos(const Quantum &left); template Quantum atan(const Quantum &left); template Quantum atan2(const Quantum &left, const Quantum &other); template Quantum atan2(const Quantum &left, const Qtype &other); template Quantum atan2(const Qtype &left, const Quantum &other); template Quantum log(const Quantum &left); template Quantum log10(const Quantum &left); template Quantum exp(const Quantum &left); // // min and max template Quantum min(const Quantum &left, const Quantum &other); template Quantum max(const Quantum &left, const Quantum &other); // Functions to implement integer ceil/floor and others // Int ceil(const Int &val); Int floor(const Int &val); Array operator *(const Array &in, Double f); Array operator /(const Array &in, Double f); Array operator *(const Array &in, Double f); Array operator /(const Array &in, Double f); Array operator *(const Array &in, Double f); Array operator /(const Array &in, Double f); Array operator *(const Array &in, Double f); Array operator /(const Array &in, Double f); // //# Inline Implementations // } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/casa/Quanta/QMath.tcc000066400000000000000000000224601476623553700171750ustar00rootroot00000000000000//# QMath.cc: class to manipulate physical, dimensioned quantities //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_QMATH_TCC #define CASA_QMATH_TCC //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template Quantum operator+(const Quantum &left, const Qtype &other) { Quantum loc; loc = left; loc += other; return loc; } template Quantum operator+(const Qtype &left, const Quantum &other) { Quantum loc; loc = other; loc += left; return loc; } template Quantum operator-(const Quantum &left, const Qtype &other) { Quantum loc; loc = left; loc -= other; return loc; } template Quantum operator-(const Qtype &left, const Quantum &other) { Quantum loc; loc = other; loc -= left; return loc; } template Quantum operator*(const Quantum &left, const Qtype &other) { Quantum loc = left; loc *= other; return loc; } template Quantum operator*(const Qtype &left, const Quantum &other) { Quantum loc; loc = other; loc *= left; return loc; } template Quantum operator/(const Quantum &left, const Qtype &other) { Quantum loc = left; loc /= other; return loc; } template Quantum operator/(const Qtype &left, const Quantum &other) { Quantum loc; loc = other; loc /= left; return loc; } // pow. Implemented as a repeated multiplication/division: // - to cater for all data types // - only Int powers allowed // - limited values of exponentials foreseen template Quantum pow(const Quantum &left, Int p) { if (::abs(p) >= 100) throw (AipsError("Quantum::pow exponent too large")); // Make sure 1 in current data type available Quantum res; Qtype tmp; tmp = left.getValue() * 0. + 1.; Int i; if (p>=0) { for (i=0; ip; i--) tmp /= left.getValue(); } res.setValue(tmp); if (p == 0 || left.getUnit().empty()) res.setUnit(""); else { String sloc = "(" + left.getUnit() + ")"; if (p < 0) { sloc += "-"; p = -p; } if (p/10 != 0) sloc += Char(Int(p)/10 + '0'); sloc += Char(Int(p)%10 + '0'); res.setUnit(sloc); } return res; } template Quantum root(const Quantum &left, Int p) { if (p == 0) throw (AipsError("Quantum::root exponent zero")); Quantum res; res.setValue(casacore::pow(left.getValue(), 1.0/Double(p))); UnitVal vres(left.getFullUnit().getValue().root(p)); ostringstream oss; oss << vres.getDim(); res.setUnit(String(oss)); res.setValue(res.getValue() * vres.getFac()); return res; } template Quantum sqrt(const Quantum &left) { return root(left, 2); } template Quantum abs(const Quantum &left) { Qtype tmp = left.getValue(); Qtype ret; ret = abs((tmp)); return (Quantum(ret,left)); } template Quantum ceil(const Quantum &left) { Qtype tmp = left.getValue(); Qtype ret; ret = ceil((tmp)); return (Quantum(ret,left)); } template Quantum floor(const Quantum &left) { Qtype tmp = left.getValue(); Qtype ret; ret = floor((tmp)); return (Quantum(ret,left)); } template Quantum sin(const Quantum &left) { if (left.getFullUnit().getValue() != UnitVal::ANGLE) { throw (AipsError("Quantum::sin illegal unit type '" + left.getUnit() + "'")); } Quantum res; res.setValue(left.getBaseValue()); res.setValue(sin((res.getValue()))); res.setUnit(""); return (res); } template Quantum cos(const Quantum &left) { if (left.getFullUnit().getValue() != UnitVal::ANGLE) { throw (AipsError("Quantum::cos illegal unit type '" + left.getUnit() + "'")); } Quantum res; res.setValue(left.getBaseValue()); res.setValue(cos((res.getValue()))); res.setUnit(""); return (res); } template Quantum tan(const Quantum &left) { if (left.getFullUnit().getValue() != UnitVal::ANGLE) { throw (AipsError("Quantum::tan illegal unit type '" + left.getUnit() + "'")); } Quantum res; res.setValue(left.getBaseValue()); res.setValue(tan((res.getValue()))); res.setUnit(""); return (res); } template Quantum asin(const Quantum &left) { if (left.getFullUnit().getValue() != UnitVal::NODIM) { throw (AipsError("Quantum::asin illegal unit type '" + left.getUnit() + "'")); } Quantum res; res.setValue(left.getBaseValue()); res.setValue(asin((res.getValue()))); res.setUnit("rad"); return (res); } template Quantum acos(const Quantum &left) { if (left.getFullUnit().getValue() != UnitVal::NODIM) { throw (AipsError("Quantum::acos illegal unit type '" + left.getUnit() + "'")); } Quantum res; res.setValue(left.getBaseValue()); res.setValue(acos((res.getValue()))); res.setUnit("rad"); return (res); } template Quantum atan(const Quantum &left) { if (left.getFullUnit().getValue() != UnitVal::NODIM) { throw (AipsError("Quantum::atan illegal unit type '" + left.getUnit() + "'")); } Quantum res; res.setValue(left.getBaseValue()); res.setValue(atan((res.getValue()))); res.setUnit("rad"); return (res); } template Quantum atan2(const Quantum &left, const Quantum &other) { if ((left.getFullUnit().getValue() != UnitVal::NODIM) || (other.getFullUnit().getValue() != UnitVal::NODIM)) { throw (AipsError("Quantum::atan2 illegal unit type '" + left.getUnit() + "'")); } Quantum res; Qtype tmp; tmp = other.getBaseValue(); res.setValue(left.getBaseValue()); res.setValue(atan2((res.getValue()),(tmp))); res.setUnit("rad"); return (res); } template Quantum atan2(const Quantum &left, const Qtype &other) { Quantum res; res = other; return (atan2(left,res)); } template Quantum atan2(const Qtype &left, const Quantum &other) { Quantum res; res = left; return (atan2(res,other)); } template Quantum log(const Quantum &left) { if (left.getFullUnit().getValue() != UnitVal::NODIM) { throw (AipsError("Quantum::log illegal unit type '" + left.getUnit() + "'")); } Quantum res; res.setValue(left.getBaseValue()); res.setValue(log((res.getValue()))); res.setUnit(""); return (res); } template Quantum log10(const Quantum &left) { if (left.getFullUnit().getValue() != UnitVal::NODIM) { throw (AipsError("Quantum::log10 illegal unit type '" + left.getUnit() + "'")); } Quantum res; res.setValue(left.getBaseValue()); res.setValue(log10((res.getValue()))); res.setUnit(""); return (res); } template Quantum exp(const Quantum &left) { if (left.getFullUnit().getValue() != UnitVal::NODIM) { throw (AipsError("Quantum::exp illegal unit type '" + left.getUnit() + "'")); } Quantum res; res.setValue(left.getBaseValue()); res.setValue(exp((res.getValue()))); res.setUnit(""); return (res); } template Quantum min(const Quantum &left, const Quantum &other) { return left < other ? left : other; } template Quantum max(const Quantum &left, const Quantum &other) { return left > other ? left : other; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Quanta/QMath2.cc000066400000000000000000000046061476623553700170750ustar00rootroot00000000000000//# QMath2.cc: class to manipulate physical, dimensioned quantities //# Copyright (C) 1994,1995,1996,1998,2000,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Int ceil(const Int &val) { return (val); } Int floor(const Int &val) { return (val); } Float real(const Float &val) { return val; } Double real(const Double &val) { return val; } Array operator *(const Array &in, Double f) { return in * Complex(f); } Array operator /(const Array &in, Double f) { return in / Complex(f); } Array operator *(const Array &in, Double f) { return in * DComplex(f); } Array operator /(const Array &in, Double f) { return in / DComplex(f); } Array operator *(const Array &in, Double f) { return in * Float(f); } Array operator /(const Array &in, Double f) { return in / Float(f); } Array operator *(const Array &in, Double f) { return in * Int(f); } Array operator /(const Array &in, Double f) { return in / Int(f); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Quanta/QVector.h000066400000000000000000000054251476623553700172260ustar00rootroot00000000000000//# Quantum.h: class to manipulate physical, dimensioned quantities //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_QVECTOR_H #define CASA_QVECTOR_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template class QVector; typedef QVector QVD; // // Specialization for Quantum > // // // // //
        135. Quantum // // // // Vector of quantities. // // // // Objects of type Quantum > are used often in our code. // We need a way to access individual elements easily // template class QVector : public Quantum > { public: // zero elements QVector(); QVector(const Vector& v, const Unit& u); // convert a Vector of Quanta to a QVector. Useful // when reading a table column of Quanta. All elements in // q will be converted to units of the first element in q. // An exception will be thrown if not all elements in q conform // to the same unit. QVector(const Vector >& q); // access single element Quantum operator[](uInt index) const; size_t size() const; size_t nelements() const; void scale(T d); // add operators as needed QVector operator+(const QVector& d) const; QVector operator-(const QVector& d) const; QVector operator/(T d) const; Quantum min() const; Quantum max() const; }; } #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif #endif casacore-3.7.1/casa/Quanta/QVector.tcc000066400000000000000000000064351476623553700175520ustar00rootroot00000000000000//# Copyright (C) 1996,1998,1999,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_QVECTOR_TCC #define CASA_QVECTOR_TCC #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template QVector::QVector() : Quantum >() {} template QVector::QVector(const Vector& v, const Unit& u) : Quantum >(v, u) {} template QVector::QVector(const Vector >& q) : Quantum >(Vector(q.size()), "") { uInt n = q.size(); if (n == 0) { return; } Unit u(q[0].getFullUnit()); this->setUnit(u); Vector copy(n); typename Vector::iterator iter = copy.begin(); typename Vector::iterator end = copy.end(); typename Vector >::const_iterator qiter = q.begin(); while (iter != end) { *iter = qiter->getValue(u, True); ++iter; ++qiter; } this->setValue(copy); } template Quantum QVector::operator[](uInt index) const { return Quantum(this->getValue()[index], this->getUnit()); } template size_t QVector::size() const { return this->getValue().size(); } template size_t QVector::nelements() const { return this->getValue().nelements(); } template void QVector::scale(T d) { this->setValue(d*this->getValue()); } template QVector QVector::operator+(const QVector& that) const { return QVector(this->getValue() + that.getValue(this->getFullUnit()), this->getFullUnit()); } template QVector QVector::operator-(const QVector& that) const { return QVector(this->getValue() - that.getValue(this->getFullUnit()), this->getFullUnit()); } template QVector QVector::operator/(const T d) const { return QVector((Vector)(this->getValue()/d), this->getFullUnit()); } template Quantum QVector::min() const { return Quantum(casacore::min(this->getValue()), this->getFullUnit()); } template Quantum QVector::max() const { return Quantum(casacore::max(this->getValue()), this->getFullUnit()); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Quanta/Quantum.h000066400000000000000000000415221476623553700172730ustar00rootroot00000000000000//# Quantum.h: class to manipulate physical, dimensioned quantities //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_QUANTUM_H #define CASA_QUANTUM_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Quantum; //# Typedefs typedef Quantum Quantity; // // Quantities (i.e. dimensioned values) // // // // // // //
        136. Unit // // // // A Quantity is defined as a single Double value with attached units. // From this definition the templated Quantum class arose, to have non-Double, // non-scalar quantities. // // // // Quantities are values with a unit. Their basic specification can be one of // two forms: // // Quantity( Double value, String unit); // or: Unit unit // Quantum ( Type value, String unit) // or: Unit unit // // // A unit is a string of known unit fields separated // by 'space' or '.' (to indicate multiply) or '/' (to indicate divide). // See the Unit class for details. // // Example: km/s/(Mpc.s)2 is identical to km.s-1.Mpc-2.s-2 // //

          Defining a Quantum

          // The following list of constructors is available. // // In the following 'String' can be replaced by 'Unit' everywhere. The // only difference being a check for a legitimate unit string being executed // if Unit specified (with exception if error) // // // 'Quantum' can, if Type equals Double, be replaced // with 'Quantity' // // 'Type' can be any simple or non-simple arithmetic type. // // E.g. , , > // //
            //
          • Quantum() value 0 generated //
          • Quantum( Quantum) copy constructor //
          • Quantum( Type factor) value factor generated //
          • Quantum( Type factor, Unit unit) specified quantity //
          • Quantum( Type factor, Quantum quant) specified factor, // the unit from the quant //
          // // //

          Manipulating quantities

          // Mathematical operators and functions and // logical operations (comparisons) // are defined on Quantums. They are, // of course, only available if the template Type supports them. //
            //
          • = assignment of identical //
          • * *= multiple two Quantums of same , or Quantum and type //
          • / /= divide two Quantums of same , or Quantum and type // note: // In multiplication and division, and if is scalar, the left or // right-hand side can be of type (e.g 2.*Quantity is allowed) //
          • + += add two Quantums of same or Quantum and type // and same unit dimensions (else exception) //
          • - -= subtract (same as +) //
          • - negate Quantum //
          • + unary + on Quantum //
          • == != compare unit dimensions and value of same . They will // be unequal if the units do not match or the values (possibly // converted to common base units). All comparisons work also // on a Quantum and //
          • < > compare unit dimensions. Exception if no match, // else compare the values //
          • <= >= ibid //
          • pow(Int) raise to an (integer) power //
          // // //

          Manipulating the value and/or units of quanta

          // Quantities can be converted to other units by the following set of member // functions: //
            //
          • convert() will convert the quantum to canonical units. // E.g. given myval=Quantity(5.,"Jy"), // myval.convert() will convert the qunatum to // Quantity(5.e-26,"kg.s-2") //
          • convert(Unit unit) will convert the quantum to the // specified unit with any remaining dimensions // expressed in canonical units. E.g given // myval as above, myval.convert("W/cm") will // make it Quantity(5.e-28,"W/cm.m-1.s") //
          • convert(Quantum quant) will convert the quantum // to the units of the specified quant with the // same conversion rules as the previous one //
          // All converting type methods (i.e. convert(), get() and // getValue() with specified units), will automatically convert also from // time to angle units (or v.v) if necessary, as long as they are simple. I.e. // deg will be converted to h, but asking to convert m/s to m/deg will // produce the standard conversion to m/deg.rad/s. // // Quanta can be checked for having the correct unit dimensions (e.g. before // addition or comparing) by the following two member functions, which will // return a Bool value: //
            //
          • isConform(Unit unit) //
          • isConform(Quantum quant) //
          • check(UnitVal kind) //
          // or by an assertion, which will throw an exception:
          //
            //
          • assure(UnitVal kind) //
          // // The quantum can be retrieved with a change in units by: //
            //
          • get() will return the quantum converted to canonical units. // E.g. given myval=Quantity(5.,"Jy"), // myval.get() will return // Quantity(5.e-26,"kg.s-2") //
          • get(Unit unit) will return the quantum converted to the // specified unit with any remaining dimensions // expressed in canonical units. E.g given // myval as above, myval.get("W/cm") will // return it as Quantity(5.e-28,"W/cm.m-1.s") //
          • get(Quantum quant) will return the quantum converted // to the units of the specified quant with the // same conversion rules as the previous one //
          // // The value and units of a quantum can be set or retrieved separately by the // following member functions: //
            //
          • getValue() return the value (as Type) of the quantum. // myval.get().getValue() will return the // value of myval expressed in canonical units // //
          • getValue(Unit unit) return the value (as converted to unit) //
          • getUnit() return the String part of the unit of the // quantum (use getFullUnit if interested in // the complete Unit, e.g. for re-use) //
          • getFullUnit() return the complete unit of the Quantum (use // getUnit() if interested in String part only) //
          • setValue(Type val) replace the value of the quantum with val, // leaving the units the same //
          • scale(Type val) multiply the value (leaving units same) by the // specified value //
          • setUnit(Unit unit) replace the units of the quantum, leaving // the value the same. //
          • setUnit(Quantum quant) ibid //
          • set(String quantity) replace the value and unit as deduced from quantity //
          // // The output operator (<<) will produce the value of the quantum and its // units. Given Quantity myval(5.,"mJy"), << myval will produce: // 5.0 mJy; while << myval.get("yW/m2") // will produce: .00005 yW/m2.s.
          // The input operator (>>, or the static read functions) will // convert a String to a Quantum (quantity only for now). The analysis // will do the following: //
            //
          • Check if it can be converted as a time/angle, if so use // (MVAngle) //
          • Check if it can be used as a date/time. if so use // (MVTime) //
          • Interpret as a value with units //
          // Since e.g. 12d could be interpreted as // being both an angle (12 degrees) or a quantity (12 days), the only way // is to differentiate them with a decimal point (12.d will be days) // //
          // // // An experiment has measured the energy of a photon in keV. The following will // output the wavelength and frequency of this photon (see the // QC class for quantity constants): // // #include // Double myval; // keV photon energy // Quantity quant(myval,"keV"); // make quantity // cout << "A photon with energy " << quant << endl // << " has a frequency of " // << (quant/QC::h)->get("GHz") << endl // h=Planck // << " and a wavelength of " // << (QC::c/quant/QC::h)->get("nm") // c=light velocity // << " or " << QC::c/quant/QC::h << endl; // // // // // Major use is foreseen in all calculations with observed data. // // //
        137. prefix +,- //
        138. + - * / and += -= *= /= //
        139. < <= == != >= > //
        140. sin //
        141. cos //
        142. tan //
        143. asin //
        144. acos //
        145. atan //
        146. atan2 //
        147. abs //
        148. ceil //
        149. floor //
        150. // It is assumed that all these functions return either Bool or // the same data type as inputted (i.e. QType). Special functions are // provided in this module to convert Int and LogicalArray to Bool; // and to convert were necessary to Complex (e.g. abs(Complex)). // // // //
        151. Some inlining (did not work first go) // template class Quantum : public QBase{ //# Friends // Input, only quantity is supported now friend istream& operator>> (istream &is, Quantity &ku); public: //# Constructors // Default constructor, generates '0' Quantum(); // Copy constructor (deep copy) Quantum(const Quantum &other); // Construct undimensioned quantum (i.e. unit="") Quantum(const Qtype &factor); // Construct dimensioned quantum (e.g. '1.23 km/Mpc') // //
        152. AipsError if non-matching unit dimensions // // Quantum(const Qtype &factor, const Unit &s); // // Construct quantum with unit copied from existing quantum Quantum(const Qtype &factor, const QBase &other); //# Operators // Assignment (deep copy) Quantum &operator=(const Quantum &other); // Unary operations // const Quantum &operator+() const; Quantum operator-() const; // // In place arithmetic functions: left hand side changed in place // //
        153. AipsError if non-conforming units (+ and -) //
        154. AipsError if illegal result unit (* and /; programming error) // // Quantum &operator+=(const Quantum &other); Quantum &operator+=(const Qtype &other); Quantum &operator-=(const Quantum &other); Quantum &operator-=(const Qtype &other); Quantum &operator*=(const Quantum &other); Quantum &operator*=(const Qtype &other); Quantum &operator/=(const Quantum &other); Quantum &operator/=(const Qtype &other); // // Arithmetic operators: return Quantum // //
        155. AipsError if non-conforming units (+ and -) // // See QMath class for unequal argument types // Quantum operator+(const Quantum &other) const; Quantum operator-(const Quantum &other) const; Quantum operator*(const Quantum &other) const; Quantum operator/(const Quantum &other) const; // //# General member functions // Get value of quantum in current units (i.e. in units specified in quantum) // const Qtype &getValue() const; Qtype &getValue(); // // Get value in canonical base units Qtype getBaseValue() const; // Get value in specified units. // If the other units do not conform to the units of this // object and requireConform is True, an exception is thrown, // with the following exceptions: //
          - angle to/from time conversions are implicitly supported //
          - frequency to/from/ wavelength conversions are implicitly supported //#
          Note, I added requireConform and made the default value False for //# backward compatibility. However, I think that ultimately requireConform //# should be removed and an exception should be thrown if the units do //# not conform. It's not clear to me why this was not in the original //# implementation; it's much too easy for non-conformation bugs to //# slip by unnoticed. - dmehring 09feb2015 //# It should be left in since conversion from time to angle makes sense. //# Maybe the default could be changed to True. - gvandiepen09feb2016 Qtype getValue(const Unit &other, Bool requireConform=False) const; // Get the unit (as Unit) that is attached to the Quantum. (use getUnit() if // interested in the String part only, e.g. for output) virtual const Unit &getFullUnit() const; // Re-specify parts of a quantum // // Scale ( i.e. multiply) the value of the Quantum without changing units void scale(const Qtype &factor); // Set the value without changing units void setValue(const Qtype &val); // Set the value and unit deduced from input string // At the moment the implementation can only convert // scalars to the appropiate Quantum. If format for Array input defined, // it could easily be changed. In addition recognition of date/time/angle // still has to be added // static Bool read(Quantity &res, const String &in); static Bool read(Quantity &res, MUString &in); // // // Check if of specified type Bool check(const UnitVal &uv) const; // Assert correct kind // //
        156. AipsError if non-conforming unit dimensions // void assure(const UnitVal &uv) const; // Return a Quantum converted to specified units // // Convert to canonical units Quantum get() const; // Convert to specified units; any remainder will be expressed in canonical // units. E.g. conversion of Jy/pc into W/ly2 will result in W/ly2.m-1.s . // //
        157. AipsError if illegal unit // Quantum get(const Unit &s) const; // Convert a Quantum to units from specified quantum (ibid example) Quantum get(const Quantum &other) const; // // Convert a Quantum to specified units // // Convert to canonical units void convert(); // Convert to specified units; any remainder will be expressed in canonical // units. E.g. conversion of Jy/pc into W/ly2 will result in W/ly2.m-1.s . // //
        158. AipsError if illegal unit // void convert(const Unit &s); // Convert a Quantum to units from specified quantum (ibid example) void convert(const Quantum &other) ; // // Get a copy of Quantum virtual QBase *clone() const; // Print a Quantum virtual void print(ostream &os) const; // Get the type (using QuantumType) of derived Quantum (faster than Strings) // virtual uInt type() const; static uInt myType(); // private: //# Data members // Actual quantum value Qtype qVal; }; // Global functions // Global input function // Output/Input // // only Quantity is supported on input istream& operator>> (istream &is, Quantity &ku); Bool readQuantity(Quantity &res, MUString &in); Bool readQuantity(Quantity &res, const String &in); // //# Declare extern templates for often used types. extern template class Quantum; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/casa/Quanta/Quantum.tcc000066400000000000000000000242741476623553700176220ustar00rootroot00000000000000//# Quantum.cc: class to manipulate physical, dimensioned quantities //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_QUANTUM_TCC #define CASA_QUANTUM_TCC //# Includes #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template Quantum::Quantum() : QBase() { qVal = Qtype();} template Quantum::Quantum(const Quantum &other) : QBase(other) { // Here qVal is copy-assigned in the constructor body // instead of direct-initialized in the member initializer list (which invokes // its copy constructor) to cope with Array/Vector/Matrix values: they copy by // reference on the copy construction, but by value on copy assignment (and // this class wants to do the latter). // See https://github.com/casacore/casacore/commit/e5d8484b5108f0a890ddd9d494c2efcab738ce7c#r42799565 // for reference qVal = other.qVal; } template Quantum::Quantum(const Qtype &factor) : QBase() { qVal = factor; } template Quantum::Quantum(const Qtype &factor, const Unit &s) : QBase(s) { qVal = factor; } template Quantum::Quantum(const Qtype &factor, const QBase &other) : QBase(other) { qVal = factor; } //# Quantum operators template Quantum &Quantum::operator=(const Quantum &/*other*/) = default; template const Quantum &Quantum::operator+() const{ return *this; } template Quantum Quantum::operator-() const{ Quantum loc; loc.qVal = -qVal; loc.qUnit = qUnit; return loc; } template Quantum &Quantum::operator+=(const Quantum &other) { if (qUnit.getValue() != other.qUnit.getValue()) { throw (AipsError("Quantum::operator+ unequal units '" + qUnit.getName() + ", '" + other.qUnit.getName() + "'")); } else { Qtype tmp = other.getValue(qUnit); qVal += (tmp); } return *this; } template Quantum &Quantum::operator+=(const Qtype &other) { qVal += other; return *this; } template Quantum &Quantum::operator-=(const Quantum &other) { if (qUnit.getValue() != other.qUnit.getValue()) { throw (AipsError("Quantum::operator- unequal units '" + qUnit.getName() + ", '" + other.qUnit.getName() + "'")); } else { Qtype tmp = other.getValue(qUnit); qVal -= (tmp); } return *this; } template Quantum &Quantum::operator-=(const Qtype &other) { qVal -= (other); return *this; } template Quantum &Quantum::operator*=(const Quantum &other) { qVal *= (other.qVal); if (!(other.qUnit.getName().empty())) { if (qUnit.getName().empty()) { qUnit = other.qUnit; } else { qUnit = Unit(qUnit.getName() + ("." + other.qUnit.getName())); } } return *this; } template Quantum &Quantum::operator*=(const Qtype &other) { qVal *= (other); return *this; } template Quantum &Quantum::operator/=(const Quantum &other) { qVal /= (other.qVal); if (!(other.qUnit.getName().empty())) { if (qUnit.getName().empty()) { qUnit = Unit(String("(") + other.qUnit.getName() + String(")-1")); } else { qUnit = Unit(qUnit.getName() + ("/(" + other.qUnit.getName() + ")")); } } return *this; } template Quantum &Quantum::operator/=(const Qtype &other) { qVal /= (other); return *this; } template Quantum Quantum::operator+(const Quantum &other) const{ Quantum loc; loc = *this; loc += other; return loc; } template Quantum Quantum::operator-(const Quantum &other) const{ Quantum loc; loc = *this; loc -= other; return loc; } template Quantum Quantum::operator*(const Quantum &other) const{ Quantum loc; loc = *this; loc *= other; return loc; } template Quantum Quantum::operator/(const Quantum &other) const{ Quantum loc; loc = *this; loc /= other; return loc; } template void Quantum::print(ostream &os) const { os << qVal << " " << qUnit.getName(); } //# Quantum general member functions template const Qtype &Quantum::getValue() const { return qVal; } template Qtype & Quantum::getValue() { return qVal; } template Qtype Quantum::getValue(const Unit &other, Bool requireConform) const { UnitVal myType = qUnit.getValue(); UnitVal otherType = other.getValue(); Double myFac = myType.getFac(); Double otherFac = otherType.getFac(); Double d1 = otherFac/myFac; if (myType == otherType) { return (Qtype)(qVal/d1); } if ( myType == UnitVal::ANGLE && otherType == UnitVal::TIME ) { d1 *= C::circle/C::day; } else if ( myType == UnitVal::TIME && otherType == UnitVal::ANGLE ) { d1 *= C::day/C::circle; } else if( myType == 1/UnitVal::TIME && otherType == UnitVal::LENGTH ) { return (Qtype)(C::c/qVal/myFac/otherFac); } else if( myType == UnitVal::LENGTH && otherType == 1/UnitVal::TIME ) { return (Qtype)(C::c/qVal/myFac/otherFac); } else if (requireConform) { ThrowCc( "From/to units not consistent. Cannot convert " + qUnit.getName() + " to " + other.getName() ); } return (Qtype)(qVal/d1); } template Qtype Quantum::getBaseValue() const { return (Qtype)(qVal * qUnit.getValue().getFac()); } template const Unit &Quantum::getFullUnit() const { return qUnit; } template void Quantum::scale(const Qtype &factor) { qVal *= (factor); } template void Quantum::setValue(const Qtype &val) { qVal = val; } template Bool Quantum::read(Quantity &res, MUString &in) { return readQuantity(res, in); } template Bool Quantum::read(Quantity &res, const String &in) { return readQuantity(res, in); } template Bool Quantum::check(const UnitVal &uv) const { return ( (qUnit.getValue() == uv) ? True : False); } template void Quantum::assure(const UnitVal &uv) const { if (qUnit.getValue() != uv) { throw(AipsError("Quantum::assure non-conforming unit type '" + getUnit() + "'")); } } template void Quantum::convert() { this->convert(Unit()); } template void Quantum::convert(const Unit &s) { if (qUnit.getValue() == s.getValue()) { // To suppress some warnings, next statement not used // qVal *= (qUnit.getValue().getFac()/s.getValue().getFac()); qVal = Qtype (qVal * (qUnit.getValue().getFac()/s.getValue().getFac())); qUnit = s; } else { if (qUnit.getValue() == UnitVal::ANGLE && s.getValue() == UnitVal::TIME) { qVal = Qtype (qVal * (qUnit.getValue().getFac()/s.getValue().getFac()) * C::day/C::circle); qUnit = s; } else if (qUnit.getValue() == UnitVal::TIME && s.getValue() == UnitVal::ANGLE) { qVal = Qtype (qVal * (qUnit.getValue().getFac()/s.getValue().getFac()) * C::circle/C::day); qUnit = s; } else { qUnit.setValue(qUnit.getValue() / s.getValue()); ostringstream oss; oss << qUnit.getValue().getDim(); // Suppress (gcc) warnings: qVal = Qtype (qVal * qUnit.getValue().getFac()); if (s.empty()) { qUnit = String(oss); } else { qUnit = Unit(s.getName() + '.' + String(oss).after(0)); } } } } template void Quantum::convert(const Quantum &other) { this->convert(other.qUnit); } template Quantum Quantum::get() const { return get(Unit()); } template Quantum Quantum::get(const Unit &s) const { Quantum res = *this; res.convert(s); return res; } template Quantum Quantum::get(const Quantum &other) const{ return get(other.qUnit); } template QBase *Quantum::clone() const { return (new Quantum(*this)); } template uInt Quantum::type() const { return quantumType (static_cast *>(0)); } template uInt Quantum::myType() { return quantumType (static_cast *>(0)); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Quanta/Quantum2.cc000066400000000000000000000063021476623553700175100ustar00rootroot00000000000000//# Quantum2.cc: class to manipulate phsical, dimensioned quantities //# Copyright (C) 1996,1998,1999,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Define extern templates for often used types. template class Quantum; istream &operator>> (istream &is, Quantity &ku) { String str; is >> str; if (ios::failbit & is.rdstate()) return is; Quantity t; if (Quantity::read(t, str)) { ku = t; } else { is.clear(ios::failbit | is.rdstate()); } return is; } Bool readQuantity(Quantity &res, MUString &in) { Double val0 = 0.0; String unit = ""; res = Quantity(); UnitVal uv; in.push(); if (!in.eos()) { if (MVAngle::read(res, in) || MVTime::read(res, in)) { val0 = res.getValue(); unit = res.getUnit(); } else { val0 = in.getDouble(); unit = in.get(); // Check if valid unit specified if (!UnitVal::check(unit, uv)) { in.pop(); return False; } } } // // The next statement is necessary once the read return arg is templated // Qtype tmp = (Qtype)((res.getValue()) + val0) res.setValue(val0); res.setUnit(unit); in.unpush(); return True; } Bool readQuantity(Quantity &res, const String &in) { static const Regex ex("^[[:space:][:punct:]]*[[:digit:]]"); static const Regex ex2("[tT][oO][dD][aA][yY]"); static const Regex ex3("[nN][oO][wW]"); MUString tmp(in); // The next construct is to cater for an unexplained error in // the Linux egcs stream input library; and an even more funny one in sgi /// if (!in.empty() && (in[0] == 'n' || in[0] == 'N' || in[0] == 'y' || /// in[0] == 'Y')) { if (!in.empty() && !in.contains(ex2) && !in.contains(ex3) && !in.contains(ex)) { tmp = MUString(String("0.0") + in); // Pointed non-const String } return readQuantity(res, tmp); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Quanta/QuantumHolder.cc000066400000000000000000000566541476623553700206030ustar00rootroot00000000000000//# QuantumHolder.cc: A holder for Quantum to enable record conversions //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors QuantumHolder::QuantumHolder() : hold_p() {} QuantumHolder::QuantumHolder(const QBase &in) : hold_p(in.clone()) {} QuantumHolder::QuantumHolder(const QuantumHolder &other) : RecordTransformable(), hold_p() { if (other.hold_p) { hold_p.reset (other.hold_p->clone()); } } //# Destructor QuantumHolder::~QuantumHolder() {} //# Operators QuantumHolder &QuantumHolder::operator=(const QuantumHolder &other) { if (this != &other) { if (other.hold_p) { hold_p.reset (other.hold_p->clone()); } else { hold_p.reset(); } } return *this; } //# Member Functions Bool QuantumHolder::isEmpty() const { return (!hold_p); } Bool QuantumHolder::isQuantum() const { return (static_cast(hold_p)); } Bool QuantumHolder::isScalar() const { return (hold_p && nelements() == 1); } Bool QuantumHolder::isVector() const { return (hold_p && ndim() == 1); } Bool QuantumHolder::isArray() const { return (hold_p && ndim() > 0); } Bool QuantumHolder::isReal() const { return (hold_p && (isQuantumDouble() || isQuantumFloat() || isQuantumInt() || isQuantumArrayDouble() || isQuantumArrayFloat() || isQuantumArrayInt())); } Bool QuantumHolder::isComplex() const { return (hold_p && (isQuantumComplex() || isQuantumDComplex() || isQuantumArrayComplex() || isQuantumArrayDComplex())); } Bool QuantumHolder::isQuantity() const { return (hold_p && isQuantumDouble()); } Bool QuantumHolder::isQuantumDouble() const { return (hold_p && hold_p->type() == Quantum::myType()); } Bool QuantumHolder::isQuantumFloat() const { return (hold_p && hold_p->type() == Quantum::myType()); } Bool QuantumHolder::isQuantumInt() const { return (hold_p && hold_p->type() == Quantum::myType()); } Bool QuantumHolder::isQuantumComplex() const { return (hold_p && hold_p->type() == Quantum::myType()); } Bool QuantumHolder::isQuantumDComplex() const { return (hold_p && hold_p->type() == Quantum::myType()); } Bool QuantumHolder::isQuantumArrayDouble() const { return (hold_p && (hold_p->type() == Quantum>::myType() || hold_p->type() == Quantum>::myType())); } Bool QuantumHolder::isQuantumArrayFloat() const { return (hold_p && (hold_p->type() == Quantum>::myType() || hold_p->type() == Quantum>::myType())); } Bool QuantumHolder::isQuantumArrayInt() const { return (hold_p && (hold_p->type() == Quantum>::myType() || hold_p->type() == Quantum>::myType())); } Bool QuantumHolder::isQuantumArrayComplex() const { return (hold_p && (hold_p->type() == Quantum>::myType() || hold_p->type() == Quantum>::myType())); } Bool QuantumHolder::isQuantumArrayDComplex() const { return (hold_p && (hold_p->type() == Quantum>::myType() || hold_p->type() == Quantum>::myType())); } Bool QuantumHolder::isQuantumVectorDouble() const { return (isQuantumArrayDouble() && ndim() == 1); } Bool QuantumHolder::isQuantumVectorFloat() const { return (isQuantumArrayFloat() && ndim() == 1); } Bool QuantumHolder::isQuantumVectorInt() const { return (isQuantumArrayInt() && ndim() == 1); } Bool QuantumHolder::isQuantumVectorComplex() const { return (isQuantumArrayComplex() && ndim() == 1); } Bool QuantumHolder::isQuantumVectorDComplex() const { return (isQuantumArrayDComplex() && ndim() == 1); } Int QuantumHolder::nelements() const { if (!hold_p) { throw(AipsError("Empty QuantumHolder argument for nelements")); } else if (isQuantumArrayDouble()) { return (static_cast>*>(hold_p.get()))->getValue().nelements(); } else if (isQuantumArrayFloat()) { return (static_cast>*>(hold_p.get()))->getValue().nelements(); } else if (isQuantumArrayInt()) { return (static_cast>*>(hold_p.get()))->getValue().nelements(); } else if (isQuantumArrayComplex()) { return (static_cast>*>(hold_p.get()))->getValue().nelements(); } else if (isQuantumArrayDComplex()) { return (static_cast>*>(hold_p.get()))->getValue().nelements(); } return 1; } Int QuantumHolder::ndim() const { if (!hold_p) { throw(AipsError("Empty QuantumHolder argument for ndim")); } else if (isQuantumArrayDouble()) { return (static_cast>*>(hold_p.get()))->getValue().ndim(); } else if (isQuantumArrayFloat()) { return (static_cast>*>(hold_p.get()))->getValue().ndim(); } else if (isQuantumArrayInt()) { return (static_cast>*>(hold_p.get()))->getValue().ndim(); } else if (isQuantumArrayComplex()) { return (static_cast>*>(hold_p.get()))->getValue().ndim(); } else if (isQuantumArrayDComplex()) { return (static_cast>*>(hold_p.get()))->getValue().ndim(); } return 0; } const QBase &QuantumHolder::asQuantum() const { if (!hold_p) { throw(AipsError("Empty QuantumHolder argument for asQuantum")); } return *hold_p; } const Quantum &QuantumHolder::asQuantity() { if (!hold_p) { throw(AipsError("Empty QuantumHolder argument for asQuantumDouble")); } if (!isReal() || !isScalar()) { throw(AipsError("Wrong QuantumHolder to convert asQuantumDouble")); } if (!isQuantity()) toReal(Quantum::myType()); return static_cast&>(*hold_p); } const Quantum &QuantumHolder::asQuantumDouble() { return asQuantity(); } const Quantum &QuantumHolder::asQuantumFloat() { if (!hold_p) { throw(AipsError("Empty QuantumHolder argument for asQuantumFloat")); } if (!isReal() || !isScalar()) { throw(AipsError("Wrong QuantumHolder to convert asQuantumFloat")); } if (!isQuantumFloat()) toReal(Quantum::myType()); return static_cast&>(*hold_p); } const Quantum &QuantumHolder::asQuantumInt() { if (!hold_p) { throw(AipsError("Empty QuantumHolder argument for asQuantumInt")); } if (!isReal() || !isScalar()) { throw(AipsError("Wrong QuantumHolder to convert asQuantumInt")); } if (!isQuantumInt()) toReal(Quantum::myType()); return static_cast&>(*hold_p); } const Quantum &QuantumHolder::asQuantumComplex() { if (!hold_p) { throw(AipsError("Empty QuantumHolder argument for asQuantumComplex")); } if (!isScalar()) { throw(AipsError("Wrong QuantumHolder to convert asQuantumComplex")); } if (!isQuantumComplex()) toComplex(Quantum::myType()); return static_cast&>(*hold_p); } const Quantum &QuantumHolder::asQuantumDComplex() { if (!hold_p) { throw(AipsError("Empty QuantumHolder argument for asQuantumDComplex")); } if (!isScalar()) { throw(AipsError("Wrong QuantumHolder to convert asQuantumDComplex")); } if (!isQuantumDComplex()) toComplex(Quantum::myType()); return static_cast&>(*hold_p); } const Quantum> &QuantumHolder::asQuantumVectorDouble() { if (!hold_p) { throw(AipsError("Empty QuantumHolder argument for asQuantumVectorDouble")); } if (isArray()) { if (!isQuantumArrayDouble()) { throw(AipsError("Cannot convert to QuantumVectorDouble")); } if (ndim() != 1) { (static_cast>*>(hold_p.get()))->getValue(). reform(IPosition(1, nelements())); } } else { if (!isReal()) { throw(AipsError("Wrong QuantumHolder to convert asQuantumVectorDouble")); } if (!isQuantumDouble()) toReal(Quantum::myType()); toVector(); } return static_cast>&>(*hold_p); } const Quantum> &QuantumHolder::asQuantumVectorFloat() { if (!hold_p) { throw(AipsError("Empty QuantumHolder argument for asQuantumVectorFloat")); } if (isArray()) { if (!isQuantumArrayFloat()) { throw(AipsError("Cannot convert to QuantumVectorFloat")); } if (ndim() != 1) { (static_cast>*>(hold_p.get()))->getValue(). reform(IPosition(1, nelements())); } } else { if (!isReal()) { throw(AipsError("Wrong QuantumHolder to convert asQuantumVectorFloat")); } if (!isQuantumFloat()) toReal(Quantum::myType()); toVector(); } return static_cast>&>(*hold_p); } const Quantum> &QuantumHolder::asQuantumVectorInt() { if (!hold_p) { throw(AipsError("Empty QuantumHolder argument for asQuantumVectorInt")); } if (isArray()) { if (!isQuantumArrayInt()) { throw(AipsError("Cannot convert to QuantumVectorInt")); } if (ndim() != 1) { (static_cast>*>(hold_p.get()))->getValue(). reform(IPosition(1, nelements())); } } else { if (!isReal()) { throw(AipsError("Wrong QuantumHolder to convert asQuantumVectorInt")); } if (!isQuantumInt()) toReal(Quantum::myType()); toVector(); } return static_cast>&>(*hold_p); } const Quantum> &QuantumHolder::asQuantumVectorComplex() { if (!hold_p) { throw(AipsError("Empty QuantumHolder argument for asQuantumVectorComplex")); } if (isArray()) { if (!isQuantumArrayComplex()) { throw(AipsError("Cannot convert to QuantumVectorComplex")); } if (ndim() != 1) { (static_cast>*>(hold_p.get()))->getValue(). reform(IPosition(1, nelements())); } } else { if (!isQuantumComplex()) toComplex(Quantum::myType()); toVector(); } return static_cast>&>(*hold_p); } const Quantum> &QuantumHolder::asQuantumVectorDComplex() { if (!hold_p) { throw(AipsError("Empty QuantumHolder argument for asQuantumVectorDComplex")); } if (isArray()) { if (!isQuantumArrayDComplex()) { throw(AipsError("Cannot convert to QuantumVectorDComplex")); } if (ndim() != 1) { (static_cast>*>(hold_p.get()))->getValue(). reform(IPosition(1, nelements())); } } else { if (!isQuantumDComplex()) toComplex(Quantum::myType()); toVector(); } return static_cast>&>(*hold_p); } const Quantum> &QuantumHolder::asQuantumArrayDouble() { if (!hold_p) { throw(AipsError("Empty QuantumHolder argument for asQuantumArrayDouble")); } if (isArray()) { if (!isQuantumArrayDouble()) { throw(AipsError("Cannot convert to QuantumArrayDouble")); } } else { if (!isReal()) { throw(AipsError("Wrong QuantumHolder to convert asQuantumArrayDouble")); } if (!isQuantumDouble()) toReal(Quantum::myType()); toArray(); } return static_cast>&>(*hold_p); } const Quantum> &QuantumHolder::asQuantumArrayFloat() { if (!hold_p) { throw(AipsError("Empty QuantumHolder argument for asQuantumArrayFloat")); } if (isArray()) { if (!isQuantumArrayFloat()) { throw(AipsError("Cannot convert to QuantumArrayFloat")); } } else { if (!isReal()) { throw(AipsError("Wrong QuantumHolder to convert asQuantumArrayFloat")); } if (!isQuantumFloat()) toReal(Quantum::myType()); toArray(); } return static_cast>&>(*hold_p); } const Quantum> &QuantumHolder::asQuantumArrayInt() { if (!hold_p) { throw(AipsError("Empty QuantumHolder argument for asQuantumArrayInt")); } if (isArray()) { if (!isQuantumArrayInt()) { throw(AipsError("Cannot convert to QuantumArrayInt")); } } else { if (!isReal()) { throw(AipsError("Wrong QuantumHolder to convert asQuantumArrayInt")); } if (!isQuantumInt()) toReal(Quantum::myType()); toArray(); } return static_cast>&>(*hold_p); } const Quantum> &QuantumHolder::asQuantumArrayComplex() { if (!hold_p) { throw(AipsError("Empty QuantumHolder argument for asQuantumArrayComplex")); } if (isArray()) { if (!isQuantumArrayComplex()) { throw(AipsError("Cannot convert to QuantumArrayComplex")); } } else { if (!isQuantumComplex()) toComplex(Quantum::myType()); toArray(); } return static_cast>&>(*hold_p); } const Quantum> &QuantumHolder::asQuantumArrayDComplex() { if (!hold_p) { throw(AipsError("Empty QuantumHolder argument for asQuantumArrayDComplex")); } if (isArray()) { if (!isQuantumArrayDComplex()) { throw(AipsError("Cannot convert to QuantumArrayDComplex")); } } else { if (!isQuantumDComplex()) toComplex(Quantum::myType()); toArray(); } return static_cast>&>(*hold_p); } Bool QuantumHolder::fromRecord(String &error, const RecordInterface &in) { String un; if (in.isDefined(String("value")) && in.isDefined(String("unit")) && in.type(in.idToNumber(RecordFieldId("unit"))) == TpString) { String un; in.get(RecordFieldId("unit"), un); switch (in.type(in.idToNumber(RecordFieldId("value")))) { case TpDouble: { Double vl; in.get(RecordFieldId("value"), vl); hold_p.reset (new Quantum(vl, un)); return True; } case TpFloat: { Float vl; in.get(RecordFieldId("value"), vl); hold_p.reset (new Quantum(vl, un)); return True; } case TpInt: { Int vl; in.get(RecordFieldId("value"), vl); hold_p.reset (new Quantum(vl, un)); return True; } case TpComplex: { Complex vl; in.get(RecordFieldId("value"), vl); hold_p.reset (new Quantum(vl, un)); return True; } case TpDComplex: { DComplex vl; in.get(RecordFieldId("value"), vl); hold_p.reset (new Quantum(vl, un)); return True; } case TpArrayDouble: { Array vl; in.get(RecordFieldId("value"), vl); hold_p.reset (new Quantum>(vl, un)); return True; } case TpArrayFloat: { Array vl; in.get(RecordFieldId("value"), vl); hold_p.reset (new Quantum>(vl, un)); return True; } case TpArrayInt: { Array vl; in.get(RecordFieldId("value"), vl); hold_p.reset (new Quantum>(vl, un)); return True; } case TpArrayComplex: { Array vl; in.get(RecordFieldId("value"), vl); hold_p.reset (new Quantum>(vl, un)); return True; } case TpArrayDComplex: { Array vl; in.get(RecordFieldId("value"), vl); hold_p.reset (new Quantum>(vl, un)); return True; } default: break; } } error += String("Illegal Quantum record in QuantumHolder::fromRecord\n"); return False; } Bool QuantumHolder::fromString(String &error, const String &in) { Quantum res; if (!Quantum::read(res, in)) { error += String("in QuantumHolder::fromString with input string \"") + in + String("\": Illegal input units or format\n"); return False; } hold_p.reset (new Quantum(res)); return True; } Bool QuantumHolder::toRecord(String &error, RecordInterface &out) const { if (hold_p) { if (out.isDefined("value")) out.removeField(RecordFieldId("value")); if (isQuantumDouble()) { out.define(RecordFieldId("value"), ((static_cast*>(hold_p.get()))->getValue())); } else if (isQuantumFloat()) { out.define(RecordFieldId("value"), ((static_cast*>(hold_p.get()))->getValue())); } else if (isQuantumInt()) { out.define(RecordFieldId("value"), ((static_cast*>(hold_p.get()))->getValue())); } else if (isQuantumComplex()) { out.define(RecordFieldId("value"), ((static_cast*>(hold_p.get()))->getValue())); } else if (isQuantumDComplex()) { out.define(RecordFieldId("value"), ((static_cast*>(hold_p.get()))->getValue())); } else if (isQuantumVectorDouble()) { out.define(RecordFieldId("value"), ((static_cast>*>(hold_p.get()))->getValue())); } else if (isQuantumVectorFloat()) { out.define(RecordFieldId("value"), ((static_cast>*>(hold_p.get()))->getValue())); } else if (isQuantumVectorInt()) { out.define(RecordFieldId("value"), ((static_cast>*>(hold_p.get()))->getValue())); } else if (isQuantumVectorComplex()) { out.define(RecordFieldId("value"), ((static_cast>*>(hold_p.get()))->getValue())); } else if (isQuantumVectorDComplex()) { out.define(RecordFieldId("value"), ((static_cast>*>(hold_p.get()))->getValue())); } else if (isQuantumArrayDouble()) { out.define(RecordFieldId("value"), ((static_cast>*>(hold_p.get()))->getValue())); } else if (isQuantumArrayFloat()) { out.define(RecordFieldId("value"), ((static_cast>*>(hold_p.get()))->getValue())); } else if (isQuantumArrayInt()) { out.define(RecordFieldId("value"), ((static_cast>*>(hold_p.get()))->getValue())); } else if (isQuantumArrayComplex()) { out.define(RecordFieldId("value"), ((static_cast>*>(hold_p.get()))->getValue())); } else if (isQuantumArrayDComplex()) { out.define(RecordFieldId("value"), ((static_cast>*>(hold_p.get()))->getValue())); } out.define(RecordFieldId("unit"), hold_p->getFullUnit().getName()); return True; } error += String("No Quantum specified in QuantumHolder::toRecord\n"); return False; } void QuantumHolder::toRecord(RecordInterface &out) const { String error; if (! toRecord(error, out)) { throw AipsError(error); } } Record QuantumHolder::toRecord() const { Record r; toRecord(r); return r; } void QuantumHolder::toReal(const uInt &tp) { Double d1=0; if (isArray()) { IPosition stx(ndim(), 0); if (isQuantumArrayDouble()) { d1 = static_cast>*>(hold_p.get())->getValue()(stx); } else if (isQuantumArrayFloat()) { d1 = static_cast>*>(hold_p.get())->getValue()(stx); } else if (isQuantumArrayInt()) { d1 = static_cast>*>(hold_p.get())->getValue()(stx); } } else { if (isQuantumDouble()) { d1 = static_cast*>(hold_p.get())->getValue(); } else if (isQuantumFloat()) { d1 = static_cast*>(hold_p.get())->getValue(); } else if (isQuantumInt()) { d1 = static_cast*>(hold_p.get())->getValue(); } } Unit x = hold_p->getFullUnit(); if (tp == Quantum::myType()) { hold_p.reset (new Quantum(d1, x)); } else if (tp == Quantum::myType()) { hold_p.reset (new Quantum(Float(d1), x)); } else if (tp == Quantum::myType()) { hold_p.reset (new Quantum(Int(d1), x)); } } void QuantumHolder::toComplex(const uInt &tp) { DComplex d1; if (isArray()) { IPosition stx(ndim(), 0); if (isQuantumArrayDouble()) { d1 = static_cast>*>(hold_p.get())->getValue()(stx); } else if (isQuantumArrayFloat()) { d1 = static_cast>*>(hold_p.get())->getValue()(stx); } else if (isQuantumArrayInt()) { d1 = static_cast>*>(hold_p.get())->getValue()(stx); } else if (isQuantumArrayComplex()) { d1 = static_cast>*>(hold_p.get())->getValue()(stx); } else if (isQuantumArrayDComplex()) { d1 = static_cast>*>(hold_p.get())->getValue()(stx); } } else { if (isQuantumDouble()) { d1 = static_cast*>(hold_p.get())->getValue(); } else if (isQuantumFloat()) { d1 = static_cast*>(hold_p.get())->getValue(); } else if (isQuantumInt()) { d1 = static_cast*>(hold_p.get())->getValue(); } else if (isQuantumComplex()) { d1 = static_cast*>(hold_p.get())->getValue(); } else if (isQuantumDComplex()) { d1 = static_cast*>(hold_p.get())->getValue(); } } Unit x = hold_p->getFullUnit(); if (tp == Quantum::myType()) { hold_p.reset (new Quantum(Complex(d1), x)); } else if (tp == Quantum::myType()) { hold_p.reset (new Quantum(d1, x)); } } void QuantumHolder::toVector() { Unit x = hold_p->getFullUnit(); if (isQuantumDouble()) { Vector d1(1); d1(0) = static_cast*>(hold_p.get())->getValue(); hold_p.reset (new Quantum>(d1, x)); } else if (isQuantumFloat()) { Vector d1(1); d1(0) = static_cast*>(hold_p.get())->getValue(); hold_p.reset (new Quantum>(d1, x)); } else if (isQuantumInt()) { Vector d1(1); d1(0) = static_cast*>(hold_p.get())->getValue(); hold_p.reset (new Quantum>(d1, x)); } else if (isQuantumComplex()) { Vector d1(1); d1(0) = static_cast*>(hold_p.get())->getValue(); hold_p.reset (new Quantum>(d1, x)); } else if (isQuantumDComplex()) { Vector d1(1); d1(0) = static_cast*>(hold_p.get())->getValue(); hold_p.reset (new Quantum>(d1, x)); } } void QuantumHolder::toArray() { toVector(); } const String &QuantumHolder::ident() const { static const String myid = "quant"; return myid; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Quanta/QuantumHolder.h000066400000000000000000000206001476623553700204230ustar00rootroot00000000000000//# QuantumHolder.h: A holder for Quantities to enable record conversions //# Copyright (C) 1998,1999,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_QUANTUMHOLDER_H #define CASA_QUANTUMHOLDER_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class QBase; class String; class RecordInterface; class Record; template class Quantum; // A holder for Quantums to enable record conversions // // // // //
        159. RecordInterface class //
        160. Quantity class // // // // A Holder of general Quantums // // // // This class can be used to handle a heterogeneous list of Quantums, and // can handle toRecord() and fromRecord() conversions. // A QuantumHolder // is created empty, from a Quantum (e.g. a Quantum) or a // Quantum>). // // The accepted range of Quantums is: //
            //
          • Quantum, Quantum, Quantum == Quantity //
          • Quantum, Quantum //
          • Quantum>, Quantum>, // Quantum> //
          • Quantum>, Quantum> //
          • Quantum>, Quantum>, // Quantum> //
          • Quantum>, Quantum> //
          // Scalars in the same group can be converted to any in the same group (e.g. // Int to Double); Vectors of length 1 can be converted to scalars in the // corresponding group; Scalars can always be converted to Vectors in the // corresponding group. Real scalar values can be converted to Complex values. // Vectors cannot be converted to other type vectors. // // Checks on the contents can be made with functions like // isQuantity and the contents can be obtained with // functions like asQuantity. It is an error to try and // retrieve a Quantum of the wrong type and doing so will generate an // exception (AipsError). //
          // // // // TableRecord rec; // an empty record // Quantity x(12.5, "km/s"); // a Quantity // String error; // an error message // if (!QuantumHolder(x).toRecord(error, rec)) { // make record // cout << error << endl; // }; // Record grec; // a Record // if (!QuantumHolder(x).toRecord(error, grec)) { // make record // cout << error << endl; // }; // // Note that for GlishRecords use can be made of the // // GlishRecord::to/fromrecord() methods. // // // // // To make general conversions between Quantums and records, without knowing // the actual Quantum being converted. // class QuantumHolder : public RecordTransformable { public: //# Friends //# Enumerations //# Constructors // Creates an empty holder QuantumHolder(); // Create from a Quantum (copy semantics) QuantumHolder(const QBase &in); // Copy a holder (copy semantics) QuantumHolder(const QuantumHolder &other); //# Destructor ~QuantumHolder(); //# Operators // Assignment (copy semantics) QuantumHolder &operator=(const QuantumHolder &other); //# Member Functions // Check if it holds a Quantity. Note that a Vector of length 1 will give // True to scalar questions. // Bool isEmpty() const; Bool isQuantum() const; Bool isScalar() const; Bool isVector() const; Bool isArray() const; Bool isReal() const; Bool isComplex() const; Bool isQuantity() const; Bool isQuantumDouble() const; Bool isQuantumFloat() const; Bool isQuantumInt() const; Bool isQuantumComplex() const; Bool isQuantumDComplex() const; Bool isQuantumVectorDouble() const; Bool isQuantumVectorFloat() const; Bool isQuantumVectorInt() const; Bool isQuantumVectorComplex() const; Bool isQuantumVectorDComplex() const; Bool isQuantumArrayDouble() const; Bool isQuantumArrayFloat() const; Bool isQuantumArrayInt() const; Bool isQuantumArrayComplex() const; Bool isQuantumArrayDComplex() const; // // Get number of numeric elements (1 if scalar, else // vector length) or dimensions (0 if scalar) // //
        161. AipsError if holder empty // // Int nelements() const; Int ndim() const; // // Get a Quantum from the holder (with lifetime as long // as holder exists). Conversions done if necessary and as described in // introduction. // //
        162. AipsError if holder empty or no conversion possible // // const QBase &asQuantum() const; const Quantum &asQuantity() ; const Quantum &asQuantumDouble() ; const Quantum &asQuantumFloat() ; const Quantum &asQuantumInt() ; const Quantum &asQuantumComplex() ; const Quantum &asQuantumDComplex() ; const Quantum> &asQuantumVectorDouble() ; const Quantum> &asQuantumVectorFloat() ; const Quantum> &asQuantumVectorInt() ; const Quantum> &asQuantumVectorComplex() ; const Quantum> &asQuantumVectorDComplex() ; const Quantum> &asQuantumArrayDouble() ; const Quantum> &asQuantumArrayFloat() ; const Quantum> &asQuantumArrayInt() ; const Quantum> &asQuantumArrayComplex() ; const Quantum> &asQuantumArrayDComplex() ; // // Create a Quantum from a record or a string. // A valid record will contain the following fields: //
            //
          • value: contains a numeric value of Int, Float, Double, Complex, // DComplex or a vector thereof //
          • unit: a string with a valid unit string. //
          // A valid string will be one of the special time/angle formats or a // value with a valid unit string. // Illegal values or units will return False and write an error message. // virtual Bool fromRecord(String &error, const RecordInterface &in); virtual Bool fromString(String &error, const String &in); // // Create a record from a Quantum. A False return and an error message is // only generated if there is no valid Quantum in the holder. virtual Bool toRecord(String &error, RecordInterface &out) const; // this version throws an exception rather than returning false virtual void toRecord(RecordInterface &out) const; // this version throws an exception or returns the result Record. virtual Record toRecord() const; // Return identification virtual const String &ident() const; private: //# Data Members // Pointer to a Quantity std::unique_ptr hold_p; //# General member functions // Convert to a different real scalar quantum void toReal(const uInt &tp); // Convert to a different complex scalar quantum void toComplex(const uInt &tp); // Convert scalar to Vector void toVector(); // Convert scalar to Array void toArray(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Quanta/QuantumType.h000066400000000000000000000074441476623553700201420ustar00rootroot00000000000000//# QuantumType.h: Get an integer type for a Qunatum //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_QUANTUMTYPE_H #define CASA_QUANTUMTYPE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Quantum; // Get an integer type for a Qunatum // // // // // The old way of getting a type number for a Quantum uses the Register // class. This did not work well with shared libraries, because each shared // library gets its own static type data. Hence a type generated in one // python module sometimes mismatched the type for the same Quantum in // another module. For instance, it appeared that whem first loading // pyrap.tables and thereafter pyrap.quanta, things went wrong. // // Therefore this class has been developed that returns the type for the // Quantum template types used by QuantumHolder. // // inline uInt quantumType (const Quantum*) { return 1; } inline uInt quantumType (const Quantum*) { return 2; } inline uInt quantumType (const Quantum*) { return 3; } inline uInt quantumType (const Quantum*) { return 4; } inline uInt quantumType (const Quantum*) { return 5; } inline uInt quantumType (const Quantum< Vector >*) { return 6; } inline uInt quantumType (const Quantum< Vector >*) { return 7; } inline uInt quantumType (const Quantum< Vector >*) { return 8; } inline uInt quantumType (const Quantum< Vector >*) { return 9; } inline uInt quantumType (const Quantum< Vector >*) { return 10; } inline uInt quantumType (const Quantum< Array >*) { return 11; } inline uInt quantumType (const Quantum< Array >*) { return 12; } inline uInt quantumType (const Quantum< Array >*) { return 13; } inline uInt quantumType (const Quantum< Array >*) { return 14; } inline uInt quantumType (const Quantum< Array >*) { return 15; } inline uInt quantumType (const Quantum< Matrix >*) { return 16; } inline uInt quantumType (const Quantum< Matrix >*) { return 17; } inline uInt quantumType (const Quantum< Matrix >*) { return 18; } inline uInt quantumType (const Quantum< Matrix >*) { return 19; } inline uInt quantumType (const Quantum< Matrix >*) { return 20; } // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Quanta/RotMatrix.cc000066400000000000000000000111231476623553700177220ustar00rootroot00000000000000//# RotMatrix.cc: a 3x3 rotation matrix //# Copyright (C) 1995,1996,1997,1998,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // RotMatrix class //# Constructors RotMatrix::RotMatrix() { for (Int i=0; i<3; i++) for (Int j=0; j<3; j++) { rotat[i][j] = (i==j) ? Double(1.0) : Double(0.0); } } RotMatrix::RotMatrix(const RotMatrix &other) { for (Int i=0; i<3; i++) for (Int j=0; j<3; j++) rotat[i][j] = other.rotat[i][j]; } RotMatrix &RotMatrix::operator=(const RotMatrix &other) { if (this != &other) { for (Int i=0; i<3; i++) for (Int j=0; j<3; j++) rotat[i][j] = other.rotat[i][j]; } return *this; } RotMatrix::RotMatrix(const Euler &other) { for (Int k=0; k<3; k++) for (Int j=0; j<3; j++) { rotat[k][j] = (k==j) ? Double(1.0) : Double(0.0); } for (Int i=0; i<3; i++) applySingle(other(i),other.get(i)); } RotMatrix::RotMatrix(const Euler &other, Int ax0, Int ax1, Int ax2) { for (Int i=0; i<3; i++) for (Int j=0; j<3; j++) { rotat[i][j] = (i==j) ? Double(1.0) : Double(0.0); } DebugAssert(abs(ax0) <= 3 && abs(ax1) <= 3 && abs(ax2) <= 3, AipsError); applySingle(other(0),ax0); applySingle(other(1),ax1); applySingle(other(2),ax2); } //# Destructor RotMatrix::~RotMatrix() {} //# Operators RotMatrix &RotMatrix::operator*=(const RotMatrix &other) { Double a[3]; Int j,k; for (Int i=0; i<3; i++) { for (j=0; j<3; j++) a[j] = rotat[i][j]; for (j=0; j<3; j++) { rotat[i][j] = a[0]; rotat[i][j] *= other.rotat[0][j]; for (k=1; k<3; k++) { rotat[i][j] += a[k] * other.rotat[k][j]; } } } return *this; } RotMatrix RotMatrix::operator*(const RotMatrix &other) const { RotMatrix result = *this; result *= other; return result; } Double &RotMatrix::operator()(uInt row, uInt column) { DebugAssert(row < 3 && column < 3, AipsError); return rotat[row][column]; } const Double &RotMatrix::operator()(uInt row, uInt column) const{ DebugAssert(row < 3 && column < 3, AipsError); return rotat[row][column]; } //# Methods Matrix RotMatrix::get() const { Matrix tmp(3,3); for (Int row=0; row<3; row++) for (Int col=0; col<3; col++) tmp(row, col) = rotat[row][col]; return tmp; } void RotMatrix::transpose() { Double tmp; for (Int row=0; row<3; row++) for (Int col=row+1; col<3; col++) { tmp = rotat[row][col]; rotat[row][col] = rotat[col][row]; rotat[col][row] = tmp; } } void RotMatrix::set(const Matrix &in) { for (Int row=0; row<3; row++) for (Int col=0; col<3; col++) rotat[row][col] = in(row, col); } void RotMatrix::set(const Vector &in0, const Vector &in1, const Vector &in2) { for (Int col=0; col<3; col++) { rotat[0][col] = in0(col); rotat[1][col] = in1(col); rotat[2][col] = in2(col); } } ostream &operator<< (ostream &os, const RotMatrix &rot) { os << rot.get(); return os; } void RotMatrix:: applySingle(Double angle, Int which) { if (angle*which != 0.0) { RotMatrix tmp; Int i = which%3; Int j = (i + 1)%3; tmp.rotat[i][i] = tmp.rotat[j][j] = cos(angle); tmp.rotat[i][j] = -(tmp.rotat[j][i] = sin(angle)); this->operator*=(tmp); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Quanta/RotMatrix.h000066400000000000000000000103231476623553700175650ustar00rootroot00000000000000//# RotMatrix.h: a 3x3 rotation matrix //# Copyright (C) 1995,1996,1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_ROTMATRIX_H #define CASA_ROTMATRIX_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class Euler; //# Constants (SUN compiler does not accept non-simple default arguments) // // A 3x3 rotation matrix // // // // // //
        163. Matrix //
        164. Euler // // // // From Rotation and Matrix // // // // A rotation matrix is a 3x3 matrix, which can be used to rotate a coordinate // system, notably the direction cosines in // MVDirection.
          // A RotMatrix can be constructed by the default constructor (which will // set the diagonal to 1), a copy constructor, and from a set of // Euler angles with RotMatrix(Euler).
          // Multiplication can be done (by *= and *) of two rotation matrices.
          // The (uInt, uInt) operator returns the indicated element. //
          // // // See Euler // // // // To use in nutation and other coordinate calculations // // // // class RotMatrix { public: //# Friends // Output a rotation matrix as a matrix friend ostream &operator<< (ostream &os, const RotMatrix &rot); //# Constructors // Default constructor generates a unit 3x3 matrix. RotMatrix(); // The copy constructor copies RotMatrix(const RotMatrix &other); // Make from an Euler RotMatrix(const Euler &other); // Make from an Euler around specified axes RotMatrix(const Euler &other, Int ax0, Int ax1, Int ax2); // Copy assignment RotMatrix &operator=(const RotMatrix &other); // Destructor ~RotMatrix(); //# Operators // The multiplication operations generate matrix products // RotMatrix &operator*=(const RotMatrix &other); RotMatrix operator*(const RotMatrix &other) const; // // Return the indicated element // Double &operator()(uInt row, uInt column); const Double &operator()(uInt row, uInt column) const; // //# Methods // Get as Matrix Matrix get() const; // Transpose the rotation matrix void transpose(); // Fill Rotation matrix from Matrix void set(const Matrix &in); // Fill Rotation matrix from 3 (row) vectors void set(const Vector &in0, const Vector &in1, const Vector &in2); private: //# Data // The rotation matrix (3x3) Double rotat[3][3]; //# Member functions // Apply to a rotation matrix a further rotation of angle around the specified // axis which (0 or 1 or 2). void applySingle(Double angle, Int which); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Quanta/Unit.cc000066400000000000000000000137701476623553700167220ustar00rootroot00000000000000//# Unit.cc: defines the Unit class //# Copyright (C) 1994-1999,2001,2004,2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Unit::Unit() : uName(), uVal() {} Unit::Unit(const Unit &other) : uName(), uVal() { uName = other.uName; uVal = other.uVal; } Unit::Unit(const std::string &other) : uName(other), uVal() { check(); } Unit::Unit(const char *other) : uName(other), uVal() { check(); } Unit::Unit(const char *other, Int len) : uName(other, len), uVal() { check(); } Unit::Unit(char other) : uName(other), uVal() { check(); } Unit::~Unit() {} Unit &Unit::operator=(const Unit &other) { if (this != &other) { uName = other.uName; uVal = other.uVal; } return *this; } Bool Unit::operator==(const Unit &other) const { return (uVal == other.uVal); } Bool Unit::operator!=(const Unit &other) const { return (uVal != other.uVal); } Bool Unit::empty() const{ return (uName.empty()); } const UnitVal &Unit::getValue() const { return uVal; } const String &Unit::getName() const { return uName; } void Unit::setValue(const UnitVal &in) { uVal = in; } void Unit::setName(const String &in) { uName = in; } //# --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- //# The following functions are designed to replace (in two passes) //# the following regular expression operations... //# (written by Darrell Schiebel) //# --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- //# static Regex f1("\\*\\*"); static String sf1(""); //# static Regex f2("\\^"); static String sf2(""); //# static Regex f3("\\*"); static String sf3("."); //# static Regex f4(" */ *"); static String sf4("/"); //# static Regex f5("//"); static String sf5("."); //# static Regex f6("\\.*/\\.*"); static String sf6("/"); //# static Regex sp(" +"); static String ssp("."); //# static Regex pd("\\.+"); static String spd("."); //# static Regex bp("^\\.+"); static String sbp(""); //# static Regex ep("\\.+$"); static String ebp(""); //# ... //# uName.gsub(f1, sf1); //# uName.gsub(f2, sf2); //# uName.gsub(f3, sf3); //# uName.gsub(f4, sf4); //# uName.gsub(f5, sf5); //# uName.gsub(f6, sf6); //# uName.gsub(sp, ssp); //# uName.gsub(pd, spd); //# uName.gsub(bp, sbp); //# uName.gsub(ep, ebp); //# --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- void pass_one( const char *source, char *dest ) { while ( *source ) { switch ( *source ) { case '^': ++source; continue; /** f2 **/ case '*': switch ( *++source ) { case '*': ++source; continue; /** f1 **/ default: *dest++ = '.'; /** f3 **/ continue; } case ' ': { char dotslash = '.'; while ( *source == ' ' || *source == '/' ) { if ( *source++ == '/' ) { dotslash = '/'; } } *dest++ = dotslash; /** f4, sp **/ continue; } case '/': switch ( *++source ) { case '/': ++source; *dest++ = '.'; continue; /** f5 **/ default: *dest++ = '/'; continue; } default: *dest++ = *source++; continue; } } *dest = '\0'; } void pass_two( char *source, char *dest ) { while ( *source == '.' ) ++source; /** bp **/ if ( *source ) { for ( char *end = source + strlen(source) - 1; end != source; --end ) { if ( *end == '.' ) { *end = '\0'; /** ep **/ } else { break; } } while ( *source ) { /** f6 **/ switch ( *source ) { case '.': { bool go = true; char dotslash = '.'; while ( *source && go ) { switch ( *source ) { case '/': dotslash = '/'; // fall through case '.': ++source; continue; default: go = false; continue; } } *dest++ = dotslash; /** f4, pd **/ continue; } default: *dest++ = *source++; continue; } } } *dest = '\0'; } void Unit::check() { if (!UnitVal::check(uName, uVal)) { throw (AipsError("Unit::check Illegal unit string '" + uName + "'")); } char stackbuf1[200]; char stackbuf2[200]; char *b1, *b2; /* avoid heap allocations for common small strings */ if (uName.size() < sizeof(stackbuf1)) { strcpy(stackbuf1, uName.c_str()); b1 = &stackbuf1[0]; b2 = &stackbuf2[0]; } else { b1 = strdup(uName.c_str()); b2 = (char*) malloc((uName.size()+1)*sizeof(char)); } pass_one(b1,b2); pass_two(b2,b1); uName = b1; if (b1 != &stackbuf1[0]) { free(b1); free(b2); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Quanta/Unit.h000066400000000000000000000211771476623553700165640ustar00rootroot00000000000000//# Unit.h: defines the Unit class //# Copyright (C) 1994-1996,1998-2000,2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_UNIT_H #define CASA_UNIT_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // defines physical units // // // // // //# // //# // // //# // //# // // // // Physical units are basically used as quantities (see the // Quantum class), i.e. // a value and a dimension. The Unit class, or one of its subsidaries, will // in general not be called separately. The only reason to make use of these // classes is to generate additional 'tagged' units, i.e. units with a // special name, e.g. 'beam' for a telescope beam, or 'JY', a non-SI name // for Jy. //

          Units

          // A Unit is a String, and can be defined as either a Unit or a String // everywhere where a Unit is required.
          // If defined as a Unit, the format of the string will be checked for a // legal definition and its value will be stored. If defined as a String, // the checking and determination of the value will be done each time // the string is encountered when a Unit is expected.
          // The use of a separate Unit variable will give a tremendous // speed increase, if compared to using the String representation in // e.g. Quantity(5,"deg") // // If using an explicit Unit variable (e.g. Unit a("5Bolton/beam")), // the check on the legality of the given string, and the conversion to the // cached canonical value in the variable 'a', is only done at creation time. This // means that if the user changes the value of a unit involved by the // putUser() method, the unit using it should be // re-created ( a = Unit("5Bolton/beam");). // // A unit is a string of one or more fields separated // by 'space' or '.' or '*' (FITS option) // (to indicate multiply) or '/' (to indicate divide). // Multiple separators are acted upon (i.e. m//s == m.s). // Separators are acted upon left-to-right (i.e. m/s/A == (m/s)/A; use // () to indicate otherwise (e.g. m/(s/A))). // // A field is a name, or a unit enclosed in (), optionally followed by an, // optionally signed, decimal constant. // The decimal constant may be proceeded by '**' or '^' (FITS option) // // E.g. m.(m/s)-2 == m-1.s2) // // A 'space' or '.' before an opening '(' can be omitted. // // A name can consist of case-sensitive letters, '_', ''', ':', '"' and '0' // ('0' not as first character). Digits 1-9 are allowed if preceded with // an '_'. // // Possible legal names are e.g. Jy, R0, R_1, "_2. // //
            //
          • ' is used for arcmin //
          • '' or " for arcsec //
          • : :: and ::: are used for h, min, s respectively //
          • _ is used for an undimensioned value (like beam or pixel) //
          //
          // The standard naming conventions for SI units are that they are // all in lowercase, unless derived from a person's name, when they start // with a capital letter. Notable exceptions are some of the astronomical // SI related units (e.g. AU). // // A name can be preceded by a (standard) decimal prefix. // // A name must be defined in a Unit map before it can be used. // // All SI units and some customary units are part of the classes. User // defined names can be added by the UnitMap::putUser() function (see // the UnitMap class). // // Example: // km/s/(Mpc.s)2 is identical to km.s-1.Mpc-2.s-2 // // There are 5 name lists in the UnitMap, which are searched in reverse order: //
            //
          1. Defining units: m, kg, s, A, K, cd, mol, rad, sr, _ //
          2. SI units: including a.o. g, Jy, AU //
          3. Customary units: e.g. lb, hp, ly //
          4. User defined units: defined by user (e.g. beam, KPH, KM) //
          5. Cached units: for speed in operations //
          // All known names can be viewed by running the tUnit test program, or // using the MapUnit::list() routine. // They are also (at least the 1999/09/15 values) available in the // Quanta module documentation. // // There is a difference between units without a dimension (non-dimensioned // I will call them), and undimensioned units. Non-dimensioned examples are // "", "%"; undimensioned examples: "beam", "pixel". // // //

          Unit class

          // The Unit class is not directly based on the String class, but Strings and // Units are interchangeable in all Unit and Quantum related calls. // (But notice the earlier note on speed if using explicit Strings often.) // // To calculate with Units (or Strings representing units), use the // UnitVal class. To use dimensioned values, // use the Quantum (cq Quantity) class. // // Using Unit i.s.o. String will give an immediate check of the legality // of the unit string. // In addition the UnitVal class contains a check facility to determine the // legality of a unit string: // // Bool UnitVal::check("string"); // // //
          // // // // #include // // check if a string is a valid unit // if ( !UnitVal::check("Km") ) { cout << "Invalid unit string " << "Km" << endl; } // // define some units // String unit1="km/Mpc"; // Unit unit2="uJy/Mpc"; // // define your own unit name // UnitMap::putUser("my_univ", UnitVal( C::pi, unit2), "My universe param"); // // use the units in model calculations // Quantity observed( 8.97, "Mmy_univ/a"); // Quantity theory (3.8e-9, "mmy_univ/s"); // if ( ( observed / theory) < 1.) { cout << "Eureka" << endl; } // // // // // Make basis for all dimensioned values the SI system of units // // // //
        165. Some inlining (did not work first go) //
        166. Look into possiblity of conversion routine from rad2 to sr // class Unit { public: //# Constructors // Default empty string constructor Unit(); // Copy constructor Unit(const Unit &other); // String based constructors. // //
        167. AipsError if illegal unit string // // Unit(const std::string &other); Unit(const Char *other); explicit Unit(Char other); Unit(const Char *other, Int len); // // Destructor ~Unit(); //* Operators // Copy assignment Unit& operator=(const Unit &other); // Comparisons. Comparisons are done on the basis of the inherent units. I.e. // m/s are identical to AU/cy. // Bool operator==(const Unit &other) const; Bool operator!=(const Unit &other) const; // Fast check for "" units Bool empty() const; // //# Member functions // Get the unit value const UnitVal &getValue() const; // Get the unit name const String &getName() const; // Set the unit value void setValue(const UnitVal &in); // Set the unit name void setName(const String &in); private: //# Data String uName; UnitVal uVal; //# Member functions // Check format of unit string // //
        168. AipsError // void check(); }; //# Inline Implementations } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Quanta/UnitDim.cc000066400000000000000000000100441476623553700173430ustar00rootroot00000000000000//# UnitDim.cc: defines the (private) class describing basic SI dimensions //# Copyright (C) 1994,1995,1996,1997,1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN void UnitDim::init() { for (Int i=0; i(unitLong); } UnitDim::UnitDim(const UnitDim &other) { for (Int i=0; i(unitLong); } void UnitDim::init(Int pos) { for (Int i=0; i(unitLong); unitDim[pos]=1; } UnitDim::~UnitDim() {} UnitDim &UnitDim::operator=(const UnitDim &other) { if (this != &other) { for (Int i=0; i(unitLong); } return *this; } UnitDim &UnitDim::operator*=(const UnitDim &other) { for (Int i=0; i #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class String; class UnitVal; class UnitMap; // // // describes a unit in basic SI unit dimensions // // // // // // You should have at least a preliminary understanding of these classes: //
        169. Unit // // // // Based on Unit and the Dimension of a unit in SI defining units // // // // Physical units are strings consisting of one or more names of known // basic units, separated by '.' or ' ' (for multiplication) or '/' (for // division). Each name can optionally be preceded by a standard decimal // prefix, and/or followed by an (optionally signed) exponent. // Example: // km/s/(Mpc.s)2 is identical to km.s-1.Mpc-2.s-2 // // See the Unit for more details. // // The UnitDim class is a private class for use by the Unit classes. It // contains the dimensions in the 9 basic defining SI units of a unit. // // // // // // // The UnitDim class has been separated to keep the interface between a // complex unit description string and the basic SI units clean. // // // //
        170. Some inlining (did not work first go) // class UnitDim { //# Friends friend class UnitVal; friend class UnitMap; // Output the SI dimensions (e.g. 'km/s/g' as 'm kg-1 s-1') friend ostream& operator<<(ostream &os, const UnitDim &du); public: //# Enumerations // Enumeration of the order and number of the defining SI units. // If order or contents changed, change also in dimName() and dimFull(). enum Dim {Dm=0, Dkg, Ds, DA, DK, Dcd, Dmol, Drad, Dsr, Dnon, Dnumber}; // Constants // Number of Longs to cater for 9 bytes. #define UNITDIM_DLNUMBER 3 // Destructor ~UnitDim(); protected: void init( ); void init(Int pos); private: //# Constructors // Construct a unit with zero dimension in all SI units UnitDim() { init( ); } // Copy constructor UnitDim(const UnitDim &other); // Construct a unit dimension with a one in the indicated position (as // Dim enumerator) and zeroes in all other units UnitDim(Int pos) { init(pos); } //# Operators // Assignment (copy semantics) UnitDim &operator=(const UnitDim &other); // Operators to combine unit dimensions // // Multiplication adds the unit dimensions of all SI units UnitDim &operator*=(const UnitDim &other); UnitDim operator*(const UnitDim &other) const; // Division subtracts the unit dimensions of all SI units UnitDim &operator/=(const UnitDim &other); UnitDim operator/(const UnitDim &other) const; // // Compare dimension of units // // Compare for equal dimensions Bool operator==(const UnitDim &other) const; // Compare for unequal dimensions Bool operator!=(const UnitDim &other) const; // //# General Member Functions // Raise all SI defining units to an integer power UnitDim pow(Int p); // Get the tag for specified dimension static const String& dimName(uInt which); // Get the full name for the specified dimension static const String& dimFull(uInt which); //# Data Members // 1-byte vector to contain the dimensions of the defining SI units // (using same storage as Long vector for speed reasons) Long unitLong[UNITDIM_DLNUMBER]; signed char *unitDim; }; //# Inline Implementations //# Global definitions // Output ostream& operator<<(ostream &os, const UnitDim &du); } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Quanta/UnitMap.cc000066400000000000000000000262001476623553700173500ustar00rootroot00000000000000//# UnitMap.cc: defines the UnitMap class containing standard unit definitions //# Copyright (C) 1994-2002,2007,2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Initialize statics. std::mutex UnitMap::fitsMutex; UnitMap::UnitMap() {} UnitMap::~UnitMap() { } Bool UnitMap::getCache(const String& s, UnitVal &val) { map& mapCache = getMapCache(); map::iterator pos = mapCache.find(s); if (pos == mapCache.end()) { val = UnitVal(); return False; } val = pos->second; return True; } Bool UnitMap::getPref(const String& s, UnitName &name, UMaps* maps) { // maps can be passed in to avoid recursive getUnit calls during static initialization. UMaps& mapsref = (maps ? *maps : getMaps()); map& mapPref = mapsref.mapPref; map::iterator pos = mapPref.find(s); if (pos == mapPref.end()) { name = UnitName(); return False; } name = pos->second; return True; } Bool UnitMap::getUnit(const String& s, UnitName &name, UMaps* maps) { // maps can be passed in to avoid recursive getUnit calls during static initialization. UMaps& mapsref = (maps ? *maps : getMaps()); map& mapUser = mapsref.mapUser; map& mapCust = mapsref.mapCust; map& mapSI = mapsref.mapSI; map::iterator pos; if ((pos = mapUser.find(s)) != mapUser.end() || (pos = mapCust.find(s)) != mapCust.end() || (pos = mapSI.find(s)) != mapSI.end()) { } else { name = UnitName(); return False; } name = pos->second; return True; } void UnitMap::putCache(const String& s, const UnitVal& val) { map& mapCache = getMapCache(); if (! s.empty()) { mapCache.insert(map::value_type(s,val)); } } void UnitMap::putUser(const String& s, const UnitVal& val) { const String empty(""); UnitMap::putUser(s, val, empty); } void UnitMap::putUser(const String& s, const UnitVal& val, const String& name) { UnitName loc(s,val,name); UnitMap::putUser(loc); } void UnitMap::putUser(const UnitName& name) { map& mapUser = getMaps().mapUser; map& mapCust = getMaps().mapCust; map& mapSI = getMaps().mapSI; map::iterator pos; if ((pos = mapUser.find(name.getName())) != mapUser.end() || (pos = mapCust.find(name.getName())) != mapCust.end() || (pos = mapSI.find(name.getName())) != mapSI.end()) clearCache(); mapUser.insert(map::value_type(name.getName(), name)); } void UnitMap::removeUser(const String& s) { map& mapUser = getMaps().mapUser; map::iterator pos = mapUser.find(s); if (pos != mapUser.end()) { mapUser.erase(pos); clearCache(); } } void UnitMap::removeUser(const UnitName& name) { UnitMap::removeUser(name.getName()); } const String &UnitMap::getStringFITS(uInt which) { static const String FITSstring[N_FITS] = { "beam", "d", "deg", "deg", "Hz", "Jy", "K", "K", "km", "m", "m", "Pa", "pixel", "s", "s", "s", "V", "a", "a" }; return FITSstring[which]; } Bool UnitMap::getNameFITS(const UnitName *&name, uInt which) { static const UnitName FITSunit[N_FITS] = { UnitName("BEAM", UnitVal(1.0, getStringFITS(0)), "dimensionless beam"), UnitName("DAYS", UnitVal(1.0, getStringFITS(1)), "day"), UnitName("DEGREES", UnitVal(1.0, getStringFITS(2)), "degree"), UnitName("DEG", UnitVal(1.0, getStringFITS(3)), "degree"), UnitName("HZ", UnitVal(1.0, getStringFITS(4)), "hertz"), UnitName("JY", UnitVal(1.0, getStringFITS(5)), "jansky"), UnitName("KELVINS", UnitVal(1.0, getStringFITS(6)), "kelvin"), UnitName("KELVIN", UnitVal(1.0, getStringFITS(7)), "kelvin"), UnitName("KM", UnitVal(1.0, getStringFITS(8)), "km"), UnitName("METERS", UnitVal(1.0, getStringFITS(9)), "meter"), UnitName("M", UnitVal(1.0, getStringFITS(10)),"meter"), UnitName("PASCAL", UnitVal(1.0, getStringFITS(11)),"pascal"), UnitName("PIXEL", UnitVal(1.0, getStringFITS(12)),"dimensionless pixel"), UnitName("SECONDS", UnitVal(1.0, getStringFITS(13)),"second"), UnitName("SEC", UnitVal(1.0, getStringFITS(14)),"second"), UnitName("S", UnitVal(1.0, getStringFITS(15)),"second"), UnitName("VOLTS", UnitVal(1.0, getStringFITS(16)),"volt"), UnitName("YEARS", UnitVal(1.0, getStringFITS(17)),"year"), UnitName("YEAR", UnitVal(1.0, getStringFITS(18)),"year") }; if (which >= N_FITS) { return False; } name = &FITSunit[which]; return True; } void UnitMap::addFITS() { UMaps& maps = getMaps(); // Could be optimized using C++11 std::call_once(), but infrequently called. // If to be optimized, do note clearFITS() below (but not used atm). // Double checked locking is unsafe pre-C++11! std::lock_guard lock(UnitMap::fitsMutex); if (! maps.doneFITS) { uInt cnt = 0; const UnitName *Fname; while (UnitMap::getNameFITS(Fname, cnt)) { UnitMap::putUser(*Fname); cnt++; } maps.doneFITS = True; } } void UnitMap::clearFITS() { UMaps& maps = getMaps(); std::lock_guard lock(UnitMap::fitsMutex); if (maps.doneFITS) { uInt cnt = 0; const UnitName *Fname; while (UnitMap::getNameFITS(Fname, cnt)) { UnitMap::removeUser(*Fname); cnt++; } maps.doneFITS = False; } } Unit UnitMap::fromFITS(const Unit &un) { static const Regex sepa("[^a-zA-Z]"); MUString mus(un.getName()); String y; String z; const UnitName *nam; while (!mus.eos()) { if (mus.testChar(sepa)) y += String(mus.getChar()); else { z = mus.getAlpha(); for (uInt i=0; igetName()) { z = getStringFITS(i); break; } } y += z; } } return Unit(y); } Unit UnitMap::toFITS(const Unit &un) { static const Regex sepa("[^a-zA-Z]"); MUString mus(un.getName()); String y; String z; const UnitName *nam; while (!mus.eos()) { if (mus.testChar(sepa)) y += String(mus.getChar()); else { z = mus.getAlpha(); for (Int i=N_FITS-1; i>= 0; i--) { if (z == getStringFITS(i)) { getNameFITS(nam, i); z = nam->getName(); break; } } y += z; } } return Unit(y); } void UnitMap::clearCache() { getMapCache().clear(); } void UnitMap::listPref() { listPref(cout); } void UnitMap::listPref(ostream &os) { map& mapPref = getMaps().mapPref; for (map::iterator i=mapPref.begin(); i != mapPref.end(); ++i) { os << " " << i->second << endl; } } void UnitMap::listDef() { listDef(cout); } void UnitMap::listDef(ostream &os) { map& mapDef = getMaps().mapDef; for (map::iterator i=mapDef.begin(); i != mapDef.end(); ++i) { os << " " << i->second << endl; } } void UnitMap::listSI() { listSI(cout); } void UnitMap::listSI(ostream &os) { map& mapSI = getMaps().mapSI; for (map::iterator i=mapSI.begin(); i != mapSI.end(); ++i) { os << " " << i->second << endl; } } void UnitMap::listCust() { listCust(cout); } void UnitMap::listCust(ostream &os) { map& mapCust = getMaps().mapCust; for (map::iterator i=mapCust.begin(); i != mapCust.end(); ++i) { os << " " << i->second << endl; } } void UnitMap::listUser() { listUser(cout); } void UnitMap::listUser(ostream &os) { map& mapUser = getMaps().mapUser; for (map::iterator i=mapUser.begin(); i != mapUser.end(); ++i) { os << " " << i->second << endl; } } void UnitMap::list() { list(cout); } void UnitMap::list(ostream &os) { UMaps& maps = getMaps(); os << "Prefix table (" << maps.mapPref.size() << "):" << endl; listPref(os); os << "Defining unit table (" << maps.mapDef.size() << "):" << endl; listDef(os); os << "SI unit table (" << maps.mapSI.size() << "):" << endl; listSI(os); os << "Customary unit table (" << maps.mapCust.size() << "):" << endl; listCust(os); os << "User unit table (" << maps.mapUser.size() << "):" << endl; listUser(os); } void UnitMap::listCache() { listCache(cout); } void UnitMap::listCache(ostream &os) { map& mapCache = getMapCache(); os << "Cached unit table (" << mapCache.size() << "):" << endl; for (map::iterator i=mapCache.begin(); i != mapCache.end(); ++i) { os << " " << UnitName(i->first, i->second) << endl; } } const map &UnitMap::givePref() { return getMaps().mapPref; } const map &UnitMap::giveDef() { return getMaps().mapDef; } const map &UnitMap::giveSI() { return getMaps().mapSI; } const map &UnitMap::giveCust() { return getMaps().mapCust; } const map &UnitMap::giveUser() { return getMaps().mapUser; } const map &UnitMap::giveCache() { return getMapCache(); } UMaps& UnitMap::getMaps() { // Note that C++11 guarantees that the static is initialized only once, // even in a multi-threaded program. // But no recursivity should occur. static UMaps maps; return maps; } map& UnitMap::getMapCache() { // Note that C++11 guarantees that the static is initialized only once, // even in a multi-threaded program. static map mapCache; return mapCache; } void UMaps::init() { // Initialize the maps // Known prefixes UnitMap::initUMPrefix (*this); // Defining SI units UnitMap::initUMSI1 (*this); UnitMap::initUMSI2 (*this); // non-SI customary units UnitMap::initUMCust1 (*this); UnitMap::initUMCust2 (*this); UnitMap::initUMCust3 (*this); // FITS not done yet. doneFITS = False; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Quanta/UnitMap.h000066400000000000000000000271671476623553700172270ustar00rootroot00000000000000//# UnitMap.h: defines the UnitMap class containing standard unit definitions //# Copyright (C) 1994-2002,2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_UNITMAP_H #define CASA_UNITMAP_H //# Includes #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // Define a struct containing the static data members. // The static struct object is created in function getMaps // to ensure proper static initialization order. class UMaps { public: UMaps() {init();} // Decimal prefix list map mapPref; // Defining SI unit list map mapDef; // SI unit list map mapSI; // Customary list map mapCust; // User defined unit list map mapUser; // FITS unit list inclusion Bool doneFITS; private: void init(); }; //* Constants // IAU definition of Gaussian grav. constant for calculating IAU units const Double IAU_k=0.01720209895; // Number of FITS units recognised (change the FITSstring and FITSunit lists // in the UnitMap.cc when changing this number. const uInt N_FITS = 19; // // contains all simple known physical units // // // // // // You should have at least a preliminary understanding of these classes: //
        171. Unit // // // // Based on Units and the Casacore container classes called 'Map' // // // // Physical units are strings consisting of one or more names of known // basic units, separated by '.' or ' ' (for multiplication) or '/' (for // division). Each name can optionally be preceded by a standard decimal // prefix, and/or followed by an (optionally signed) exponent. // Example: // km/s/(Mpc.s)2 is identical to km.s-1.Mpc-2.s-2 // // See the Unit class for more details. // // The UnitMap class contains the known standard basic units, and any // other basic unit defined by the user of the Unit related classes. // The known units are divided into 5 different groups: //
            //
          1. Defining units: m, kg, s, A, K, cd, mol, rad, sr, _ //
          2. SI units: including a.o. Jy, AU etc) //
          3. Customary units: e.g. lb, hp, ly etc //
          4. User defined units: defined by user (e.g. Beam, KPH, KM) //
          5. Cached units: cached unit strings for speed in operations //
          // The full list of known units can be viewed by running the tUnit test // program. // // There is a difference between units without a dimension (non-dimensioned // I will call them), and undimensioned units. Non-dimensioned examples are // "", "%"; undimensioned examples: "beam", "pixel". // // // Information about the contents of the unit maps can be obtained by // the Bool functions (False if not present): //
            //
          • UnitMap::getPref("string", UnitName &) prefix //
          • UnitMap::getUnit("string", UnitName &) search user, // customary, SI (in that order) //
          • UnitMap::getCache("string", UnitVal &) search cache //
          // // The standard units can be viewed by the following commands, which // output to cout: //
            //
          • UnitMap::list() all prefixes and SI, Cust and User units //
          • UnitMap::listCache() current cache contents //
          • UnitMap::listPref() all prefixes //
          • UnitMap::listDef() all defining units //
          • UnitMap::listSI() all SI Units //
          • UnitMap::listCust() all customary units //
          • UnitMap::listUser() all user defined units //
          // // Units can be defined in the user list by: // The cache will be cleared if a user defined unit is overwritten, // to make sure no old value will be used. // // UnitMap::putUser("tag", UnitVal(factor,"unit"), "full name (optional)"); // or: // UnitMap::putUser(UnitName); // // // If using an explicit Unit variable (e.g. Unit a("5Bolton/beam")), // the check on the legality of the given string, and the conversion to the // cached canonical value in the variable 'a', is only done at creation time. This // means that if the user changes the value of a unit involved by the // putUser() method, the unit using it should be // re-created ( a = Unit("5Bolton/beam");). // // A special set of 'units' used in FITS datasets can be added by the command // // UnitMap::addFITS(); // // This set can be cleared from the user table by: // // UnitMap::clearFITS(); // // Note that Unitmap keeps track of the inclusion of the FITS inclusion, // making multiple calls inexpensive. The list of current FITS units can // be viewed by running the tUnit program, or looking at the FITSunit // table. // // Once the UnitMap::addFITS() has been run, the FITS units can be used as // any other unit. In addition, a FITS unit can be translated to standard // SI units by a call to Unit UnitMap::fromFITS(const Unit). Any // unit that is defined as a standard FITS unit will be translated. Unknown // ones will not be translated, making the way clear for having standard // units in a FITS units string. A comparable toFITS() translates in // the same way in the reversed direction. // // The cache can be cleared by: // // UnitMap::clearCache(); // //
          // // // Check for legal prefix: // // UnitName myUnit; // if (UnitMap::getPref("k", myUnit)) { cout << "k has value " << myUnit;} // // Define a value for the unit 'beam': // // UnitMap::putUser("beam",UnitVal(C::pi * 0.1, "\"_2"),"telescope beam"); // // List current cache: // // UnitMap::listCache(); // // // // // Standard list available to try to enhance use of SI and related units // // // //
        172. Some inlining (did not work first go) // class UnitMap { public: friend class UMaps; //# Constructors // Default constructor of maps UnitMap(); // Destructor ~UnitMap(); //# General member functions // Check if a unit name is known, and return its value if True // // Get a prefix definition from key static Bool getPref(const String &s, UnitName &name, UMaps* maps=0); // Get a standard unit definition (search order: User, Customary, SI) static Bool getUnit(const String &s, UnitName &name, UMaps* maps=0); // Get a cached definition static Bool getCache(const String &s, UnitVal &val); // // Save a definition of a full unit name in the cache (the cache will be // cleared if getting too large (200 entries) static void putCache(const String &s, const UnitVal &val); // Define a user defined standard unit. If the unit is being redefined, and it // has already been used in a user's Unit variable, the value // cached in that variable will not change. // static void putUser(const String &s, const UnitVal &val); static void putUser(const String &s, const UnitVal &val, const String &name); static void putUser(const UnitName &name); // // Remove a user unit // static void removeUser(const String &name); static void removeUser(const UnitName &name); // // Clear out the cache static void clearCache(); // Define FITS related unit names static void addFITS(); // Clear FITS related units from user list static void clearFITS(); // Translate a FITS unit to the proper units. Note that this is a translation // of the string only, no conversion. Unknown FITS units are not translated. // Hence any new definition of the FITS units will work ok static Unit fromFITS(const Unit &un); // Translate to a FITS unit static Unit toFITS(const Unit &un); // List some part of the standard unit lists on cout or stream // // List all known unit symbols // static void list(ostream &os); static void list(); // // List all units in cache // static void listCache(ostream &os); static void listCache(); // // List all prefixes // static void listPref(ostream &os); static void listPref(); // // List all defining units // static void listDef(ostream &os); static void listDef(); // // List all SI units // static void listSI(ostream &os); static void listSI(); // // List all customary units // static void listCust(ostream &os); static void listCust(); // // List all user defined units // static void listUser(ostream &os); static void listUser(); // // // Return the different maps // static const map &givePref(); static const map &giveDef(); static const map &giveSI(); static const map &giveCust(); static const map &giveUser(); static const map &giveCache(); // private: //# Constructors // Copy constructor (not implemented) UnitMap(const UnitMap &other); //# Operators // Copy assignment (not implemented) UnitMap &operator=(const UnitMap &other); static std::mutex fitsMutex; //# member functions // Get the static UMaps struct. static UMaps& getMaps(); // Get the static mapCache object. // This cannot be part of the UMaps struct, because the UnitVal ctor // is called in the initialization of UMaps, but uses mapCache resulting // in a recursive call. static map& getMapCache(); // Get the name of a FITS unit static Bool getNameFITS(const UnitName *&name, uInt which); // Get the belonging unit to a FITS unit static const String &getStringFITS(uInt which); static void initUM(); // Bits and pieces of initUM() to get compilation speed improved // static void initUMPrefix (UMaps&); static void initUMSI1 (UMaps&); static void initUMSI2 (UMaps&); static void initUMCust1 (UMaps&); static void initUMCust2 (UMaps&); static void initUMCust3 (UMaps&); // }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Quanta/UnitMap2.cc000066400000000000000000000074101476623553700174340ustar00rootroot00000000000000//# UnitMap2.cc: Unit map prefix initialisation //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Initialise the maps void UnitMap::initUMPrefix (UMaps& maps) { map& mapPref = maps.mapPref; mapPref.insert(map::value_type ("Q", UnitName("Q", C::quetta,"quetta"))); mapPref.insert(map::value_type ("R", UnitName("R", C::ronna, "ronna"))); mapPref.insert(map::value_type ("Y", UnitName("Y", C::yotta, "yotta"))); mapPref.insert(map::value_type ("Z", UnitName("Z", C::zetta, "zetta"))); mapPref.insert(map::value_type ("E", UnitName("E", C::exa, "exa"))); mapPref.insert(map::value_type ("P", UnitName("P", C::peta, "peta"))); mapPref.insert(map::value_type ("T", UnitName("T", C::tera, "tera"))); mapPref.insert(map::value_type ("G", UnitName("G", C::giga, "giga"))); mapPref.insert(map::value_type ("M", UnitName("M", C::mega, "mega"))); mapPref.insert(map::value_type ("k", UnitName("k", C::kilo, "kilo"))); mapPref.insert(map::value_type ("h", UnitName("h", C::hecto, "hecto"))); mapPref.insert(map::value_type ("da",UnitName("da",C::deka, "deka"))); mapPref.insert(map::value_type ("d", UnitName("d", C::deci, "deci"))); mapPref.insert(map::value_type ("c", UnitName("c", C::centi, "centi"))); mapPref.insert(map::value_type ("m", UnitName("m", C::milli, "milli"))); mapPref.insert(map::value_type ("u", UnitName("u", C::micro, "micro"))); mapPref.insert(map::value_type ("n", UnitName("n", C::nano, "nano"))); mapPref.insert(map::value_type ("p", UnitName("p", C::pico, "pico"))); mapPref.insert(map::value_type ("f", UnitName("f", C::femto, "femto"))); mapPref.insert(map::value_type ("a", UnitName("a", C::atto, "atto"))); mapPref.insert(map::value_type ("z", UnitName("z", C::zepto, "zepto"))); mapPref.insert(map::value_type ("y", UnitName("y", C::yocto, "yocto"))); mapPref.insert(map::value_type ("r", UnitName("r", C::ronto, "ronto"))); mapPref.insert(map::value_type ("q", UnitName("q", C::quecto,"quecto"))); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Quanta/UnitMap3.cc000066400000000000000000000113641476623553700174400ustar00rootroot00000000000000//# UnitMap3.cc: Unit map SI unit initialisation part 1 //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# constants // Initialise the maps void UnitMap::initUMSI1(UMaps& maps) { map& mapDef = maps.mapDef; map& mapSI = maps.mapSI; for (Int i=0; i::value_type (UnitDim::dimName(i), UnitName(UnitDim::dimName(i), UnitVal(1.0, i), UnitDim::dimFull(i)))); // SI units if (i != UnitDim::Dkg) { mapSI.insert(map::value_type (UnitDim::dimName(i), UnitName(UnitDim::dimName(i), UnitVal(1.0, i), UnitDim::dimFull(i)))); } } mapSI.insert(map::value_type ("$", UnitName("$", UnitVal(1., UnitDim::Dnon), "currency"))); mapSI.insert(map::value_type ("%", UnitName("%", UnitVal(0.01), "percent"))); mapSI.insert(map::value_type ("%%", UnitName("%%", UnitVal(0.001), "permille"))); mapSI.insert(map::value_type ("g", UnitName("g", UnitVal(0.001, UnitDim::Dkg), "gram"))); mapSI.insert(map::value_type ("Bq", UnitName("Bq", UnitVal(1., "s-1", &maps), "becquerel"))); mapSI.insert(map::value_type ("Hz", UnitName("Hz", UnitVal(1., "s-1", &maps), "hertz"))); mapSI.insert(map::value_type ("C", UnitName("C", UnitVal(1.,"A.s", &maps), "coulomb"))); mapSI.insert(map::value_type ("lm", UnitName("lm", UnitVal(1., "cd.sr", &maps), "lumen"))); mapSI.insert(map::value_type ("N", UnitName("N", UnitVal(1., "kg.m.s-2", &maps), "newton"))); mapSI.insert(map::value_type ("J", UnitName("J", UnitVal(1., "N.m", &maps), "joule"))); mapSI.insert(map::value_type ("W", UnitName("W", UnitVal(1., "J.s-1", &maps), "watt"))); mapSI.insert(map::value_type ("V", UnitName("V", UnitVal(1., "W.A-1", &maps), "volt"))); mapSI.insert(map::value_type ("F", UnitName("F", UnitVal(1., "C.V-1", &maps), "farad"))); mapSI.insert(map::value_type ("Gy", UnitName("Gy", UnitVal(1., "J.kg-1", &maps), "gray"))); mapSI.insert(map::value_type ("lx", UnitName("lx", UnitVal(1., "lm.m-2", &maps), "lux"))); mapSI.insert(map::value_type ("Ohm", UnitName("Ohm", UnitVal(1., "V.A-1", &maps), "ohm"))); mapSI.insert(map::value_type ("Pa", UnitName("Pa", UnitVal(1., "N.m-2", &maps), "pascal"))); mapSI.insert(map::value_type ("S", UnitName("S", UnitVal(1., "Ohm-1", &maps), "siemens"))); mapSI.insert(map::value_type ("Sv", UnitName("Sv", UnitVal(1., "J.kg-1", &maps), "sievert"))); mapSI.insert(map::value_type ("Wb", UnitName("Wb", UnitVal(1., "V.s", &maps), "weber"))); mapSI.insert(map::value_type ("H", UnitName("H", UnitVal(1., "Wb.A-1", &maps), "henry"))); mapSI.insert(map::value_type ("T", UnitName("T", UnitVal(1., "Wb.m-2", &maps), "tesla"))); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Quanta/UnitMap4.cc000066400000000000000000000103621476623553700174360ustar00rootroot00000000000000//# UnitMap4.cc: Unit map SI initialisation part 2 //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# constants // IAU definition of light time (s) unit distance to calculate IAU units static const Double IAU_tauA=499.0047837; // Initialise the maps void UnitMap::initUMSI2 (UMaps& maps) { map& mapSI = maps.mapSI; // non-metric SI units mapSI.insert(map::value_type ("deg", UnitName("deg", UnitVal(C::degree, "rad", &maps), "degree"))); mapSI.insert(map::value_type ("arcmin", UnitName("arcmin", UnitVal(C::arcmin, "rad", &maps), "arcmin"))); mapSI.insert(map::value_type ("arcsec", UnitName("arcsec", UnitVal(C::arcsec, "rad", &maps), "arcsec"))); mapSI.insert(map::value_type ("as", UnitName("as", UnitVal(1., "arcsec", &maps), "arcsec"))); mapSI.insert(map::value_type ("L", UnitName("L", UnitVal(1., "dm3", &maps), "litre"))); mapSI.insert(map::value_type ("l", UnitName("l", UnitVal(1., "L", &maps), "litre"))); mapSI.insert(map::value_type ("d", UnitName("d", UnitVal(C::day, "s", &maps), "day"))); mapSI.insert(map::value_type ("h", UnitName("h", UnitVal(C::hour, "s", &maps), "hour"))); mapSI.insert(map::value_type ("min", UnitName("min", UnitVal(C::minute, "s", &maps), "minute"))); mapSI.insert(map::value_type ("a", UnitName("a", UnitVal(24.*3600.*365.25, "s", &maps), "year"))); mapSI.insert(map::value_type ("t", UnitName("t", UnitVal(1000., "kg", &maps), "tonne"))); // Astronomical SI units mapSI.insert(map::value_type ("Jy", UnitName("Jy", UnitVal(1.0e-26, "W/m2/Hz", &maps), "jansky"))); mapSI.insert(map::value_type ("AU", UnitName("AU", UnitVal(C::c * IAU_tauA, "m", &maps), "astronomical unit"))); mapSI.insert(map::value_type ("UA", UnitName("UA", UnitVal(1., "AU", &maps), "astronomical unit"))); mapSI.insert(map::value_type ("AE", UnitName("AE", UnitVal(1., "AU", &maps), "astronomical unit"))); mapSI.insert(map::value_type ("S0", UnitName("S0", UnitVal(IAU_k*IAU_k/6.67259e-11, "AU3/d2/(m3/kg/s2)", &maps), "solar mass"))); mapSI.insert(map::value_type ("M0", UnitName("M0", UnitVal(1., "S0", &maps), "solar mass"))); mapSI.insert(map::value_type ("pc", UnitName("pc", UnitVal(1./C::arcsec, "AU", &maps), "parsec"))); mapSI.insert(map::value_type ("cy", UnitName("cy", UnitVal(24.*3600.*36525, "s", &maps), "century"))); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Quanta/UnitMap5.cc000066400000000000000000000101661476623553700174410ustar00rootroot00000000000000//# UnitMap5.cc: Unit map custom units initialisation part 1 //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Initialise the maps void UnitMap::initUMCust1 (UMaps& maps) { map& mapCust = maps.mapCust; mapCust.insert(map::value_type ("sq_deg", UnitName("sq_deg", UnitVal(C::square_degree, "sr", &maps), "square degree"))); mapCust.insert(map::value_type ("sq_arcmin", UnitName("sq_arcmin", UnitVal(C::square_arcmin, "sr", &maps), "square arcmin"))); mapCust.insert(map::value_type ("sq_arcsec", UnitName("sq_arcsec", UnitVal(C::square_arcsec, "sr", &maps), "square arcsec"))); mapCust.insert(map::value_type ("deg_2", UnitName("deg_2", UnitVal(C::square_degree, "sr", &maps), "square degree"))); mapCust.insert(map::value_type ("arcmin_2", UnitName("arcmin_2", UnitVal(C::square_arcmin,"sr", &maps), "square arcmin"))); mapCust.insert(map::value_type ("arcsec_2", UnitName("arcsec_2", UnitVal(C::square_arcsec, "sr", &maps), "square arcsec"))); mapCust.insert(map::value_type ("'", UnitName("'", UnitVal(C::arcmin, "rad", &maps), "arcmin"))); mapCust.insert(map::value_type ("''", UnitName("''", UnitVal(C::arcsec, "rad", &maps), "arcsec"))); mapCust.insert(map::value_type ("\"", UnitName("\"", UnitVal(C::arcsec, "rad", &maps), "arcsec"))); mapCust.insert(map::value_type ("'_2", UnitName("'_2", UnitVal(C::square_arcmin, "sr", &maps), "square arcmin"))); mapCust.insert(map::value_type ("''_2", UnitName("''_2", UnitVal(C::square_arcsec, "sr", &maps), "square arcsec"))); mapCust.insert(map::value_type ("\"_2", UnitName("\"_2", UnitVal(C::square_arcsec, "sr", &maps), "square arcsec"))); mapCust.insert(map::value_type (":", UnitName(":", UnitVal(1., "h", &maps), "hour"))); mapCust.insert(map::value_type ("::", UnitName("::", UnitVal(1., "min", &maps), "minute"))); mapCust.insert(map::value_type (":::", UnitName(":::", UnitVal(1., "s", &maps), "second"))); mapCust.insert(map::value_type ("FU", UnitName("FU", UnitVal(1., "Jy", &maps), "flux unit"))); mapCust.insert(map::value_type ("fu", UnitName("fu", UnitVal(1., "FU", &maps), "flux unit"))); mapCust.insert(map::value_type ("WU", UnitName("WU", UnitVal(5., "mJy", &maps), "WSRT flux unit"))); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Quanta/UnitMap6.cc000066400000000000000000000130541476623553700174410ustar00rootroot00000000000000//# UnitMap6.cc: Unit map custom units initialisation part 2 //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Initialise the maps void UnitMap::initUMCust2 (UMaps& maps) { map& mapCust = maps.mapCust; mapCust.insert(map::value_type ("abA", UnitName("abA", UnitVal(10.0, "A", &maps), "abampere"))); mapCust.insert(map::value_type ("abC", UnitName("abC", UnitVal(10.0, "C", &maps), "abcoulomb"))); mapCust.insert(map::value_type ("abF", UnitName("abF", UnitVal(1.0e+9, "F", &maps), "abfarad"))); mapCust.insert(map::value_type ("abH", UnitName("abH", UnitVal(1.0e-9, "H", &maps), "abhenry"))); mapCust.insert(map::value_type ("abOhm", UnitName("abOhm", UnitVal(1.0e-9, "Ohm", &maps), "abohm"))); mapCust.insert(map::value_type ("abV", UnitName("abV", UnitVal(1.0e-8, "V", &maps), "abvolt"))); mapCust.insert(map::value_type ("statA", UnitName("statA", UnitVal((0.1/C::c), "A", &maps), "statampere"))); mapCust.insert(map::value_type ("statC", UnitName("statC", UnitVal((0.1/C::c), "C", &maps), "statcoulomb"))); mapCust.insert(map::value_type ("statF", UnitName("statF", UnitVal(1.0/(3.0e+3*C::c), "F", &maps), "statfarad"))); mapCust.insert(map::value_type ("statH", UnitName("statH", UnitVal((3.0e+3*C::c), "H", &maps), "stathenry"))); mapCust.insert(map::value_type ("statOhm", UnitName("statOhm", UnitVal((3.0e+3*C::c), "Ohm", &maps), "statohm"))); mapCust.insert(map::value_type ("statV", UnitName("statV", UnitVal((C::c*1.0e-6), "V", &maps), "statvolt"))); mapCust.insert(map::value_type ("debye", UnitName("debye", UnitVal(10e-18, "statC.cm", &maps), "statvolt"))); mapCust.insert(map::value_type ("ac", UnitName("ac", UnitVal(4.0*40*16.5*12*2.54e-2*16.5*12*2.54e-2, "m2", &maps), "acre"))); mapCust.insert(map::value_type ("Ah", UnitName("Ah", UnitVal(1., "A.h", &maps), "ampere hour"))); mapCust.insert(map::value_type ("Angstrom", UnitName("Angstrom", UnitVal(1.0e-10, "m", &maps), "angstrom"))); mapCust.insert(map::value_type ("atm", UnitName("atm", UnitVal(1.01325e+5, "Pa", &maps), "standard atmosphere"))); mapCust.insert(map::value_type ("ata", UnitName("ata", UnitVal(9.80665, "N.cm-2", &maps), "technical atmosphere"))); mapCust.insert(map::value_type ("u", UnitName("u", UnitVal(1.661e-27, "kg", &maps), "atomic mass unit"))); mapCust.insert(map::value_type ("bar", UnitName("bar", UnitVal(1.0e+5, "Pa", &maps), "bar"))); mapCust.insert(map::value_type ("Btu", UnitName("Btu", UnitVal(1055.056, "J", &maps), "British thermal unit (Int)"))); mapCust.insert(map::value_type ("cal", UnitName("cal", UnitVal(4.1868, "J", &maps), "calorie (Int)"))); mapCust.insert(map::value_type ("Cal", UnitName("Cal", UnitVal(1., "kcal", &maps), "large calorie (Int)"))); mapCust.insert(map::value_type ("CM", UnitName("CM", UnitVal((1e-3/5.0), "kg", &maps), "metric carat"))); mapCust.insert(map::value_type ("mHg", UnitName("mHg", UnitVal(13.5951*9.80665, "kPa", &maps), "metre of mercury"))); mapCust.insert(map::value_type ("dyn", UnitName("dyn", UnitVal(1.0e-5, "N", &maps), "dyne"))); mapCust.insert(map::value_type ("eV", UnitName("eV", UnitVal(1.60217733e-19, "J", &maps), "electron volt"))); mapCust.insert(map::value_type ("erg", UnitName("erg", UnitVal(1.0e-7, "J", &maps), "erg"))); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Quanta/UnitMap7.cc000066400000000000000000000143631476623553700174460ustar00rootroot00000000000000//# UnitMap7.cc: Unit map custom units initialisation part 3 //# Copyright (C) 2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Initialise the maps void UnitMap::initUMCust3 (UMaps& maps) { map& mapCust = maps.mapCust; mapCust.insert(map::value_type ("fl_oz", UnitName("fl_oz", UnitVal(277.4193*2.54*2.54*2.54/5/4/2/4, "cm3", &maps), "fluid ounce (Imp)"))); mapCust.insert(map::value_type ("USfl_oz", UnitName("USfl_oz", UnitVal(231*2.54*2.54*2.54/4/4/2/4, "cm3", &maps), "fluid ounce (US)"))); mapCust.insert(map::value_type ("ft", UnitName("ft", UnitVal(12*2.54e-2, "m", &maps), "foot"))); mapCust.insert(map::value_type ("fur", UnitName("fur", UnitVal(220*3*12*2.54, "cm", &maps), "furlong"))); mapCust.insert(map::value_type ("Gal", UnitName("Gal", UnitVal(1., "cm/s2", &maps), "gal"))); mapCust.insert(map::value_type ("gal", UnitName("gal", UnitVal(277.4193*2.54*2.54*2.54, "cm3", &maps), "gallon (Imp)"))); mapCust.insert(map::value_type ("USgal", UnitName("USgal", UnitVal(231*2.54*2.54*2.54, "cm3", &maps), "gallon (US)"))); mapCust.insert(map::value_type ("G", UnitName("G", UnitVal(1.0e-4, "T", &maps), "gauss"))); mapCust.insert(map::value_type ("Gb", UnitName("Gb", UnitVal(10.0/(4.0 * M_PI), "A", &maps), "gilbert"))); mapCust.insert(map::value_type ("ha", UnitName("ha", UnitVal(1.,"hm2", &maps), "hectare"))); mapCust.insert(map::value_type ("hp", UnitName("hp", UnitVal(745.7, "W", &maps), "horsepower"))); mapCust.insert(map::value_type ("cwt", UnitName("cwt", UnitVal(4*2*14*0.45359237, "kg", &maps), "hundredweight"))); mapCust.insert(map::value_type ("in", UnitName("in", UnitVal(2.54, "cm", &maps), "inch"))); mapCust.insert(map::value_type ("kn", UnitName("kn", UnitVal(6080*12*2.54, "cm/h", &maps), "knot (Imp)"))); mapCust.insert(map::value_type ("ly", UnitName("ly", UnitVal(9.46073047e+15, "m", &maps), "light year"))); mapCust.insert(map::value_type ("Mx", UnitName("Mx", UnitVal(1.0e-8, "Wb", &maps), "maxwell"))); mapCust.insert(map::value_type ("mile", UnitName("mile", UnitVal(5280*12*2.54e-2, "m", &maps), "mile"))); mapCust.insert(map::value_type ("n_mile", UnitName("n_mile", UnitVal(6080*12*2.54, "cm", &maps), "nautical mile (Imp)"))); mapCust.insert(map::value_type ("Oe", UnitName("Oe", UnitVal(1000.0/(4.0*M_PI), "A/m", &maps), "oersted"))); mapCust.insert(map::value_type ("oz", UnitName("oz", UnitVal(1./16.*0.45359237, "kg", &maps), "ounce (avoirdupois)"))); mapCust.insert(map::value_type ("lb", UnitName("lb", UnitVal(0.45359237, "kg", &maps), "pound (avoirdupois)"))); mapCust.insert(map::value_type ("R", UnitName("R", UnitVal(2.58e-4, "C/kg", &maps), "mile"))); mapCust.insert(map::value_type ("sb", UnitName("sb", UnitVal(1e4, "cd/m2", &maps), "stilb"))); mapCust.insert(map::value_type ("St", UnitName("St", UnitVal(1., "cm2/s", &maps), "stokes"))); mapCust.insert(map::value_type ("Torr", UnitName("Torr", UnitVal((1.0/760.0)*1.01325e+5, "Pa", &maps), "torr"))); mapCust.insert(map::value_type ("yd", UnitName("yd", UnitVal(3*12*2.54, "cm", &maps), "yard"))); mapCust.insert(map::value_type ("yr", UnitName("yr", UnitVal(24.*3600.*365.25, "s", &maps), "year"))); mapCust.insert(map::value_type ("adu", UnitName("adu", UnitVal(1.,UnitDim::Dnon), "dimensionless ADC unit"))); mapCust.insert(map::value_type ("beam", UnitName("beam", UnitVal(1.,UnitDim::Dnon), "undefined beam area"))); mapCust.insert(map::value_type ("count", UnitName("count", UnitVal(1.,UnitDim::Dnon), "count"))); mapCust.insert(map::value_type ("pixel", UnitName("pixel", UnitVal(1.,UnitDim::Dnon), "pixel"))); mapCust.insert(map::value_type ("lambda", UnitName("lambda", UnitVal(1.,UnitDim::Dnon), "lambda"))); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Quanta/UnitName.cc000066400000000000000000000050401476623553700175120ustar00rootroot00000000000000//# UnitName.cc: defines a tagged unit definition //# Copyright (C) 1994,1995,1996,1997,1998,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN UnitName::UnitName() : basicKind(), basicTag(""), basicName("") {} UnitName::UnitName(const UnitName &other) : basicKind(other.basicKind), basicTag(other.basicTag), basicName(other.basicName) {} UnitName::UnitName(const String &tag, const UnitVal &kind, const String &name) : basicKind(kind), basicTag(tag), basicName(name) {} UnitName::UnitName(const Unit &tag, const String &name) : basicKind(), basicTag(), basicName(name) { basicKind = tag.getValue(); basicTag = tag.getName(); } UnitName::~UnitName() {} UnitName &UnitName::operator=(const UnitName &other) { if (this != &other) { basicKind=other.basicKind; basicTag=other.basicTag; basicName=other.basicName; } return *this; } ostream& operator<< (ostream &os, const UnitName &name) { static String FillString(" "); Int i=os.precision(); os << name.basicTag << (FillString)(0,10 - name.basicTag.length()) << "(" << name.basicName << ")" << (FillString)(0,27 - name.basicName.length()) << setprecision(12) << name.basicKind << setprecision(i); return os; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Quanta/UnitName.h000066400000000000000000000120621476623553700173560ustar00rootroot00000000000000//# UnitName.h: defines a tagged unit definition //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_UNITNAME_H #define CASA_UNITNAME_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // handles physical units // // // // // // You should have at least a preliminary understanding of these classes: //
        173. Unit // // // // The class name derives from the basic Unit and the Name giving possibilities // of this class to a newly defined unit tag. // // // // Physical units are strings consisting of one or more names of known // basic units, separated by '.' or ' ' (for multiplication) or '/' (for // division). Each name can optionally be preceded by a standard decimal // prefix, and/or followed by an (optionally signed) exponent. // // Example: // km/s/(Mpc.s)2 is identical to km.s-1.Mpc-2.s-2 // // See the Unit class for more details. // // The UnitName class defines new basic, tagged units. If, e.g., for one // reason or another you want, in addition to the standard defined SI and // customary units, to define a unit with a name 'KPH' to stand for the // composite SI unit 'km/hr', it can be done by creating a UnitName, and // mapping it to the UnitMap lists. // The UnitMap::putUser can also be used without creating a UnitName // first // // // UnitName myKPH( "KPH", UnitVal(3.6,"km/ks")); // note ks = kilosecond // UnitMap::putUser(myKPH); // // //

          Constructing a tagged unit definition

          // The following constructors are available: //
            //
          1. UnitName() create unnamed value 1. //
          2. UnitName(const UnitName&) copy constructor //
          3. UnitName("tag", UnitVal, "full name") //
          // // An assignment (copy semantics) is available. // // //

          Obtaining information about tagged unit

          // The following information can be obatined from a UnitName: //
            //
          1. UnitVal getVal() const will return the unit definition value //
          2. String getName() const will return the unit name //
          // // //
          // // // To obtain the definition of a Jy, you could: // // // Define a UnitVal unit definition // UnitVal mydef; // // And fill it with the appropiate definition // mydef = (UnitMap::getUnit("Jy"))->getVal(); // // // //# // //# // class UnitName { //# friends // Output the unit tag, description and its definition friend ostream& operator<< (ostream &os, const UnitName &name); public: //# Constructors // Default constructor UnitName(); // Copy constructor UnitName(const UnitName &other); // Construct from different parts // UnitName(const String &nameTag, const UnitVal &kind, const String &fullName = String()); UnitName(const Unit &unit, const String &fullName = String()); // // Destructor ~UnitName(); //# Operators // Assigment (copy semantics) UnitName &operator=(const UnitName &other); //# General member functions // Get definition value of the unit const UnitVal &getVal() const { return basicKind; } // Get the name tag of the defined unit const String &getName() const { return basicTag; } // Get the full name of the defined unit const String &getFullName() const { return basicName; } private: //# Data members // Value of defined unit UnitVal basicKind; // Name tag of unit String basicTag; // Full name and description of unit String basicName; }; //# Inline Implementations } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Quanta/UnitVal.cc000066400000000000000000000161231476623553700173600ustar00rootroot00000000000000//# UnitVal.cc: defines the class describing a unit as a value and a dimension //# Copyright (C) 1994-2001,2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN int UnitVal_static_initializer::initialized = 0; //# Constants UnitVal UnitVal::NODIM( 1.); UnitVal UnitVal::UNDIM( 1., UnitDim::Dnon); UnitVal UnitVal::LENGTH( 1., UnitDim::Dm); UnitVal UnitVal::MASS( 1., UnitDim::Dkg); UnitVal UnitVal::TIME( 1., UnitDim::Ds); UnitVal UnitVal::CURRENT( 1., UnitDim::DA); UnitVal UnitVal::TEMPERATURE( 1., UnitDim::DK); UnitVal UnitVal::INTENSITY( 1., UnitDim::Dcd); UnitVal UnitVal::MOLAR( 1., UnitDim::Dmol); UnitVal UnitVal::ANGLE( 1., UnitDim::Drad); UnitVal UnitVal::SOLIDANGLE( 1., UnitDim::Dsr); void UnitVal::init(Double factor) { kindFactor = factor; kindDim.init(); } void UnitVal::init(Double factor, Int pos) { kindFactor = factor; kindDim.init(pos); } UnitVal::UnitVal() : kindFactor(1.0), kindDim() {} UnitVal::UnitVal(const UnitVal &other) : kindFactor(other.kindFactor), kindDim(other.kindDim) {} UnitVal::UnitVal(Double factor, const String& s, UMaps* maps) : kindFactor(1.), kindDim() { if (UnitMap::getCache(s,*this)) { kindFactor *= factor; } else if (UnitVal::create(s, *this, maps)) { UnitMap::putCache(s,*this); kindFactor *= factor; } else { throw (AipsError("UnitVal::UnitVal Illegal unit string '" + s + "'")); } } UnitVal::~UnitVal() {} UnitVal &UnitVal::operator=(const UnitVal &other) { if (this != &other) { kindFactor = other.kindFactor; kindDim = other.kindDim; } return *this; } UnitVal &UnitVal::operator*=(const UnitVal &other) { kindFactor *= other.kindFactor; kindDim *= other.kindDim; return *this; } UnitVal operator*(const UnitVal &in, const UnitVal &other) { UnitVal result = in; result *= other; return result; } UnitVal &UnitVal::operator/=(const UnitVal &other) { kindFactor /= other.kindFactor; kindDim /= other.kindDim; return *this; } UnitVal operator/(const UnitVal &in, const UnitVal &other) { UnitVal result = in; result /= other; return result; } Bool UnitVal::operator==(const UnitVal &other) const { return kindDim == other.kindDim; } Bool UnitVal::operator!=(const UnitVal &other) const { return kindDim != other.kindDim; } ostream& operator<< (ostream &os, const UnitVal &ku) { os << ku.kindFactor << ku.kindDim; return os; } UnitVal UnitVal::pow(Int p) { UnitVal loc; loc.kindFactor = ::pow(kindFactor,Double(p)); loc.kindDim = kindDim.pow(p); return(loc); } UnitVal UnitVal::root(Int p) const { if (p==0) throw (AipsError("UnitVal::UnitVal Illegal root zero taken")); UnitVal loc; loc.kindDim = kindDim; for (Int i=0; i 1 && UnitMap::getPref(key(0,1), loc, maps)) { UnitName loc1 = UnitName(); if (UnitMap::getUnit(key.from(1), loc1, maps)) { res = (loc.getVal() * loc1.getVal()); return True; } else if ( key.length() > 2 && UnitMap::getPref(key(0,2),loc)) { if (UnitMap::getUnit(key.from(2), loc1, maps)) { res = (loc.getVal() * loc1.getVal()); return True; } } } return False; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Quanta/UnitVal.h000066400000000000000000000245131476623553700172240ustar00rootroot00000000000000//# UnitVal.h: defines the class describing a unit as a value and a dimension //# Copyright (C) 1994-1999,2000,2001,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_UNITVAL_H #define CASA_UNITVAL_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class String; class MUString; class UnitMap; class UMaps; // // // describes any valid unit as a factor and a dimenion of SI units // // // // // // You should have at least a preliminary understanding of these classes: //
        174. Unit // // // // The class name derives from Units and gives a Value for a unit string // // // // Physical units are strings consisting of one or more names of known // basic units, separated by '.' or ' ' (for multiplication) or '/' (for // division). Each name can optionally be preceded by a standard decimal // prefix, and/or followed by an (optionally signed) exponent. // Example: // km/s/(Mpc.s)2 is identical to km.s-1.Mpc-2.s-2 // // See the Unit class for more details. // // The UnitVal class maps a Unit string to a factor and a dimension of SI // defining units. E.g 'km/s' will be 1000 m.s-1 . // This class is only of interest if the manipulation of units is of // direct interest. Normally units will be used as Quantities and Quantums // (see the Quantum class) only, // i.e. as a physical quantity having a value and unit. // The class can also be used to check the validity of a unit string. // //

          Constructing UnitVal values

          // // UnitVal has the following constructors: //
            //
          • UnitVal() creates an (non-dimensioned) value 1. //
          • UnitVal(Double f) creates an (non-dimensioned) value f. //
          • UnitVal(Double f, String s) creates value f with unit s //
          • UnitVal(Double f, Int i) (private) creates value f with unit // at position i in dimension vector //
          // // //

          Manipulating unit values

          // // The UnitVal can be manipulated by the following operators and functions: //
            //
          • *, / generates combined UnitVal (e.g. 1 yd * 1 m = 0.9 m2) //
          • pow(Int) UnitVal(2,"km")->pow(2) = 4000000 m2 //
          • root(Int) UnitVal(4000000,"m2")->root(2) = 2 km //
          • ==, != compares dimensions only: 1 yd == 5 ly: True //
          • getFac() will return the factor (Double) //
          • getDim() will return the dimensions (as UnitDim) //
          • << will output formatted unit (factor and dimension) //
          // To aid in checking the dimensionality of units, the following constants // are available: //
            //
          • UnitVal::NODIM //
          • UnitVal::UNDIM //
          • UnitVal::LENGTH //
          • UnitVal::MASS //
          • UnitVal::ANGLE //
          • UnitVal::SOLIDANGLE //
          • UnitVal::MOLAR //
          • UnitVal::CURRENT //
          • UnitVal::TIME //
          • UnitVal::TEMPERATURE //
          • UnitVal::INTENSITY //
          // // Any other dimension can be checked by a combination. To check e.g. if // a unit is an acceleration, use: UnitVal::LENGTH/UnitVal::TIME/UnitVal::TIME // // //

          Checking for valid unit strings

          // // The validity of a unit string can be checked by: // // // Check if the given String is a valid unit representation. The String // // will be cached in the unit maps for later reference if True // if ( UnitVal::check( "km/s/Mpc") ) {...} // // //
          // // // An observation contains values in Janskys and in Westerbork Units. The // data can be combined by the following code: // // // The Fits tape gave JY, we check if defined, else we define them // if ( !UnitVal::check( "JY")) { // UnitMap::putUser("JY", UnitVal(1.,"Jy"), "FITS way to write Jy"); // } // // The Fits tape gave WU (which are defined): // // We check if JY and WU are of the same dimension: // if (UnitVal(1.,"JY") != UnitVal(1.,"WU")) { // cerr << "Wrong dimension for either JY ( " << // UnitVal(1.,"JY")->getDim() << // ") or WU ( " << // UnitVal(1.,"WU")->getDim() << ")" << endl; // } // // And output the relation between WU and JY, and the WU value: // cout << "1 WU = " << ( UnitVal(1.,"WU")/UnitVal(1.,"Jy") )->getVal() << // " JY with 1 WU = " << UnitVal(1.,"WU") << endl; // // // // To separate the actual manipulation of unit values from the related // quantity // // // //
        175. Some inlining (did not work first go) // class UnitVal { //# Friends // Multiply friend UnitVal operator*(const UnitVal &in, const UnitVal &other); // Divide friend UnitVal operator/(const UnitVal &in, const UnitVal &other); // Output a unit as a value and a string of SI defining units friend ostream& operator<<(ostream &os, const UnitVal &ku); // ensure that statics are initialized friend class UnitVal_static_initializer; public: //# Constructors // Construct an non-dimensioned value of 1 UnitVal(); // Copy constructor UnitVal(const UnitVal &other); // Construct an non-dimensioned value UnitVal(Double factor) { init(factor); } // Construct a fully dimensioned value // //
        176. AipsError // UnitVal(Double factor, const String &s, UMaps* = 0); // Construct a value with a single unit at position specified UnitVal(Double factor, Int pos) { init(factor, pos); } // Destructor ~UnitVal(); //# Operators // Assignment (copy semantics) UnitVal &operator=(const UnitVal &other); // Manipulate units // // Multiply different units UnitVal &operator*=(const UnitVal &other); // Divide different units UnitVal &operator/=(const UnitVal &other); // Compare the dimensionality of different units Bool operator==(const UnitVal &other) const; Bool operator!=(const UnitVal &other) const; // //# General member functions // Raise a unit to an integer power UnitVal pow(Int p); // Take integer root // //
        177. AipsError if power equals zero //
        178. AipsError if unit dimensions not multiple of power // // UnitVal root(Int p) const; UnitVal sqrt() const; // // Get the data parts of the unit value definition // // Get the dimensions in the defining SI units const UnitDim &getDim() const; // Get the factor of the unit (as compared to pure SI units) Double getFac() const; // //# Helper functions // Convert a unit string to a proper unit value and cache the result. The // function will return False if invalid string specified static Bool check(const String &s); // Convert a unit string to a proper unit value, cache the result and compare // the dimension with the specified unit value. False if any of the steps fails static Bool check(const String &s, UnitVal &loc); //# Data members // Some constants to check type of units // static UnitVal NODIM; static UnitVal UNDIM; static UnitVal LENGTH; static UnitVal MASS; static UnitVal TIME; static UnitVal CURRENT; static UnitVal TEMPERATURE; static UnitVal INTENSITY; static UnitVal MOLAR; static UnitVal ANGLE; static UnitVal SOLIDANGLE; // protected: // alternate initialization void init(Double factor); void init(Double factor, Int pos); private: //# Data members // The factor necessary to express the specified unit in the defining SI units Double kindFactor; // The dimensions of the unit in the defining SI units UnitDim kindDim; // Convert (and check) a unit string to an SI value representation // static Bool create(const String &s, UnitVal &res, UMaps* = 0); static Bool create(MUString &str, UnitVal &res, UMaps* = 0); // // Determine sign of unit power (i.e. if '.' or '/') static Int psign(MUString &str); // Determine exponent of unit symbol static Int power(MUString &str); // Determine symbol name in unit string static Bool field(MUString &str, UnitVal &res, UMaps*); }; //# Inline Implementations //# Global functions // Global output function // // Output ostream& operator<<(ostream &os, const UnitVal &ku); // // Static initialisation of UnitVal constants static class UnitVal_static_initializer { public: UnitVal_static_initializer( ) { if ( ! initialized ) { UnitVal::NODIM.init( 1.); UnitVal::UNDIM.init( 1., UnitDim::Dnon); UnitVal::LENGTH.init( 1., UnitDim::Dm); UnitVal::MASS.init( 1., UnitDim::Dkg); UnitVal::TIME.init( 1., UnitDim::Ds); UnitVal::CURRENT.init( 1., UnitDim::DA); UnitVal::TEMPERATURE.init( 1., UnitDim::DK); UnitVal::INTENSITY.init( 1., UnitDim::Dcd); UnitVal::MOLAR.init( 1., UnitDim::Dmol); UnitVal::ANGLE.init( 1., UnitDim::Drad); UnitVal::SOLIDANGLE.init( 1., UnitDim::Dsr); initialized = 1; } } private: static int initialized; } unitval_static_initializer; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Quanta/test/000077500000000000000000000000001476623553700164435ustar00rootroot00000000000000casacore-3.7.1/casa/Quanta/test/CMakeLists.txt000066400000000000000000000005321476623553700212030ustar00rootroot00000000000000set (tests tMVAngle tMVPosition tMVTime tQuantum tQuantumHolder tQVector tUnit ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_casa) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/casa/Quanta/test/tMVAngle.cc000066400000000000000000000110511476623553700204250ustar00rootroot00000000000000//# tMVAngle.cc: test program for MVAngle class //# Copyright (C) 1994,1995,1996,1998,1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include void showTime (MVAngle time, uInt format, uInt prec) { cout << MVAngle::Format(format,prec) << time << endl; } Bool testMUS (Quantity& q, const String& str, double exp=0, Bool chk=True) { MUString mus(str); Bool res = MVAngle::read (q, mus, chk); if (res) { if (!near(q.getValue(), exp)) { cout << "Incorrect value for " << str << endl; AlwaysAssertExit(near(q.getValue(), exp)); } } return res; } int main () { try { Quantity q; AlwaysAssertExit (! testMUS (q, "5")); AlwaysAssertExit (! testMUS (q, "5h")); AlwaysAssertExit (! testMUS (q, "5h20")); AlwaysAssertExit (testMUS (q, "5h20m", 15*(5+20./60))); AlwaysAssertExit (! testMUS (q, "5.h20m")); AlwaysAssertExit (! testMUS (q, "5h20.m")); AlwaysAssertExit (! testMUS (q, "5hm")); AlwaysAssertExit (testMUS (q, "5h20ms", 15*(5+20./60))); AlwaysAssertExit (testMUS (q, "5h20m12.3", 15*(5+20./60+12.3/3600))); AlwaysAssertExit (testMUS (q, "5h20m12.3s", 15*(5+20./60+12.3/3600))); AlwaysAssertExit (testMUS (q, "5:", 15*5)); AlwaysAssertExit (testMUS (q, "5:20", 15*(5+20./60))); AlwaysAssertExit (testMUS (q, "5:20:", 15*(5+20./60))); AlwaysAssertExit (testMUS (q, "5:20:12.3", 15*(5+20./60+12.3/3600))); AlwaysAssertExit (! testMUS (q, "5:20:12.3s")); AlwaysAssertExit (testMUS (q, "12:00:00", 15*12)); AlwaysAssertExit (! testMUS (q, "5h20:")); AlwaysAssertExit (! testMUS (q, "5:20m")); AlwaysAssertExit (! testMUS (q, "5:20,")); AlwaysAssertExit (! testMUS (q, "5:20,", False)); AlwaysAssertExit (! testMUS (q, "20:19.378")); AlwaysAssertExit (testMUS (q, "13:45:32.8187", 15*(13+45./60+32.8187/3600))); AlwaysAssertExit (! testMUS (q, "13:45:32.8187Z")); AlwaysAssertExit (! testMUS (q, "13:45:32.8187+1:0")); AlwaysAssertExit (testMUS (q, "13:45:32.8187Z", 15*(13+45./60+32.8187/3600), False)); AlwaysAssertExit (testMUS (q, "13:45:32.8187+1:0", 15*(13+45./60+32.8187/3600), False)); AlwaysAssertExit (! testMUS (q, "13:45Z")); AlwaysAssertExit (! testMUS (q, "13:45+1:0")); AlwaysAssertExit (testMUS (q, "13:45Z", 15*(13+45./60), False)); AlwaysAssertExit (testMUS (q, "13:45+1:0", 15*(13+45./60), False)); AlwaysAssertExit (testMUS (q, ".20.", 20./60)); AlwaysAssertExit (! testMUS (q, ".20")); AlwaysAssertExit (testMUS (q, "..20.", 20./3600)); AlwaysAssertExit (! testMUS (q, "1:.", False)); AlwaysAssertExit (testMUS (q, "1:[", 15*1, False)); AlwaysAssertExit (testMUS (q, "1d", 1)); AlwaysAssertExit (testMUS (q, "1d0", 1)); AlwaysAssertExit (testMUS (q, "1d0[", 1, False)); AlwaysAssertExit (! testMUS (q, "1h0[", 1, False)); AlwaysAssertExit (! testMUS (q, "1dm", 0, False)); AlwaysAssertExit (! testMUS (q, "1d1", 0, False)); AlwaysAssertExit (! testMUS (q, "1d0m[")); AlwaysAssertExit (testMUS (q, "1d0m[", 1, False)); AlwaysAssertExit (testMUS (q, "1d1m5.7[", 1+1./60+5.7/3600, False)); } catch (std::exception& x) { cout << "Unexpected exception: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/casa/Quanta/test/tMVPosition.cc000066400000000000000000000102421476623553700212040ustar00rootroot00000000000000//# tMVPosition.cc: test program for MVPosition class //# Copyright (C) 2015 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include #include #include #include #include #include #include #if defined(USE_THREADS) #include #include #endif void test_parallel(); void test_parallel_openmp(); void test_parallel() { #if defined(USE_THREADS) double sum = 0; static const size_t thread_max = 50; static const size_t loop_max = 50 * 2; std::vector threads(thread_max); std::mutex sum_mutex; for(size_t i = 0; i < thread_max ; ++i) { threads[i] = std::thread([&sum, &sum_mutex]() { std::unique_ptr positions (new MVPosition[loop_max]); double part_sum = 0; for(size_t j = 0; j < loop_max ; ++j) { positions[j] = MVPosition(); positions[j](2) = j; part_sum += positions[j](2); } std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) ); std::lock_guard lock(sum_mutex); sum += part_sum; }); } std::for_each(threads.begin(),threads.end(),[](std::thread& x){x.join();}); AlwaysAssertExit(sum == thread_max * loop_max*(loop_max-1)/2); //Arithmetic series #endif } void test_parallel_openmp() { double sum = 0; static const size_t thread_max = 50; static const size_t loop_max = 50 * 2; //50 is the value for max_cache_array inside MVPosition #ifdef _OPENMP #pragma omp parallel for reduction(+:sum) #endif for(size_t i = 0; i < thread_max ; ++i) { MVPosition positions[loop_max]; double part_sum = 0; for(size_t j = 0; j < loop_max ; ++j) { positions[j] = MVPosition(); (positions[j])(2) = j; part_sum += (positions[j])(2); } sum += part_sum; } AlwaysAssertExit(sum == thread_max * loop_max*(loop_max-1)/2); //Arithmetic series } int main () { try { MVPosition pos, pos2; pos(0) = 1; pos(1) = 2; pos(2) = 3; AlwaysAssertExit(pos(0) == 1); AlwaysAssertExit(pos(1) == 2); AlwaysAssertExit(pos(2) == 3); pos2 = pos * 2; AlwaysAssertExit(pos2(0) == 2); AlwaysAssertExit(pos2(1) == 4); AlwaysAssertExit(pos2(2) == 6); pos *= 2.; AlwaysAssertExit(pos(0) == 2); AlwaysAssertExit(pos(1) == 4); AlwaysAssertExit(pos(2) == 6); AlwaysAssertExit(pos == pos2); RotMatrix rot; for (int i = 0; i < 9; i++) { rot(i / 3, i % 3) = i; AlwaysAssertExit(rot(i / 3, i % 3) == i); } pos2 = pos * rot; AlwaysAssertExit(pos2(0) == 48); AlwaysAssertExit(pos2(1) == 60); AlwaysAssertExit(pos2(2) == 72); pos *= rot; AlwaysAssertExit(pos(0) == 48); AlwaysAssertExit(pos(1) == 60); AlwaysAssertExit(pos(2) == 72); AlwaysAssertExit(pos2 == pos); test_parallel(); } catch (std::exception& x) { cout << "Unexpected exception: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/casa/Quanta/test/tMVTime.cc000066400000000000000000000143431476623553700203040ustar00rootroot00000000000000//# tMVTime.cc: test program for MVTime class //# Copyright (C) 1994,1995,1996,1998,1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include void showTime (MVTime time, uInt format, uInt prec) { cout << MVTime::Format(format,prec) << time << endl; } void checkTime (const String& str, uInt yy, uInt mm, uInt dd, uInt h, uInt m, uInt s, double ss, Bool chk=True) { Quantity q; AlwaysAssertExit (MVTime::read(q, str, chk)); MVTime mvtm(q); Time tm = mvtm.getTime(); AlwaysAssertExit (mvtm.year() == Int(yy)); AlwaysAssertExit (mvtm.month() == mm); AlwaysAssertExit (mvtm.monthday() == dd); AlwaysAssertExit (tm.year() == yy); AlwaysAssertExit (tm.month() == mm); AlwaysAssertExit (tm.dayOfMonth() == dd); AlwaysAssertExit (tm.hours() == h); AlwaysAssertExit (tm.minutes() == m); AlwaysAssertExit (tm.seconds() == s); AlwaysAssertExit (nearAbs(tm.dseconds(), s+ss, 1e-4)); } void checkExcp (const String& str) { Quantity q; Bool ok = False; try { MVTime::read(q, str, True, True); } catch (const std::exception& x) { cout << "Expected exception: " << x.what() << endl; ok = True; } AlwaysAssertExit (ok); } int main () { try { Quantity q; AlwaysAssertExit (MVTime::read (q, "")); AlwaysAssertExit (! MVTime::read (q, "20Nov96-5h20")); AlwaysAssertExit (! MVTime::read (q, "20Nov96-5hm")); AlwaysAssertExit (! MVTime::read (q, "1996-11-20T5.20")); AlwaysAssertExit (! MVTime::read (q, "1996-11-20T5:20,", True)); AlwaysAssertExit (MVTime::read (q, "today")); AlwaysAssertExit (MVTime::read (q, "today/12:00:00")); AlwaysAssertExit (MVTime::read (q, "today 12:00:00")); AlwaysAssertExit (MVTime::read (q, "today-12:00:00")); AlwaysAssertExit (MVTime::read (q, "ToDay")); AlwaysAssertExit (! MVTime::read (q, "1996-11-20T5:20,")); AlwaysAssertExit (MVTime::read (q, "1996-11-20T5:20,", False)); checkTime (" 1996-11-20T5:20 ", 1996, 11, 20, 5, 20, 0, 0); checkTime ("1996-11-20T..20.", 1996, 11, 20, 0, 0, 1, 5./15); // time in deg!! checkTime ("12-Mar-2011 12:00:00", 2011, 3, 12, 12, 0, 0, 0); checkTime ("12-Mar-2011 12:00:00", 2011, 3, 12, 12, 0, 0, 0); checkTime ("1996/11/20", 1996, 11, 20 , 0, 0, 0, 0); checkTime ("1996/11/20/5:20", 1996, 11, 20, 5, 20, 0, 0); checkTime ("20Nov96-5h20m", 1996, 11, 20, 5, 20, 0, 0); checkTime ("1996-11-20T5:20", 1996, 11, 20, 5, 20, 0, 0); checkTime ("1996-11-20T5:20:", 1996, 11, 20, 5, 20, 0, 0); checkTime ("1996-11-20T5:20:19.378Z", 1996, 11, 20, 5, 20, 19, 0.378); checkTime ("1996-11-20T5:20Z", 1996, 11, 20, 5, 20, 0, 0); checkTime ("2017-Jul-1/11:57:0.0", 2017, 7, 1, 11, 57, 0, 0); AlwaysAssertExit (!MVTime::read (q, "2017-Jul-1x/11:57:0.0")); checkTime ("2017-Jul-1x/11:57:0.0", 2017, 7, 1, 0, 0, 0, 0, False); checkExcp ("2017-Jul-1x/11:57:0.0"); checkExcp ("x"); checkExcp ("2017-Jul-1/11:57:0.0 x"); AlwaysAssertExit (MVTime::read (q, "25-Jan-2012/13:45:32.8187")); MVTime time(q); showTime (q, MVTime::ANGLE, 9); showTime (q, MVTime::ANGLE+MVTime::NO_D, 9); showTime (q, MVTime::ANGLE+MVTime::NO_DM, 9); showTime (q, MVTime::ANGLE+MVTime::ALPHA, 9); showTime (q, MVTime::ANGLE+MVTime::NO_D+MVTime::ALPHA, 9); showTime (q, MVTime::ANGLE+MVTime::NO_DM+MVTime::ALPHA, 9); showTime (q, MVTime::ANGLE+MVTime::CLEAN, 9); showTime (q, MVTime::ANGLE+MVTime::NO_D+MVTime::CLEAN, 9); showTime (q, MVTime::ANGLE+MVTime::NO_DM+MVTime::CLEAN, 9); showTime (q, MVTime::ANGLE+MVTime::ALPHA+MVTime::CLEAN, 9); showTime (q, MVTime::ANGLE+MVTime::NO_D+MVTime::ALPHA+MVTime::CLEAN, 9); showTime (q, MVTime::ANGLE+MVTime::NO_DM+MVTime::ALPHA+MVTime::CLEAN, 9); showTime (q, MVTime::TIME, 9); showTime (q, MVTime::TIME+MVTime::NO_D, 9); showTime (q, MVTime::TIME+MVTime::NO_DM, 9); showTime (q, MVTime::TIME+MVTime::ALPHA, 9); showTime (q, MVTime::TIME+MVTime::NO_D+MVTime::ALPHA, 9); showTime (q, MVTime::TIME+MVTime::NO_DM+MVTime::ALPHA, 9); showTime (q, MVTime::TIME+MVTime::CLEAN, 9); showTime (q, MVTime::TIME+MVTime::NO_D+MVTime::CLEAN, 9); showTime (q, MVTime::TIME+MVTime::NO_DM+MVTime::CLEAN, 9); showTime (q, MVTime::TIME+MVTime::ALPHA+MVTime::CLEAN, 9); showTime (q, MVTime::TIME+MVTime::NO_D+MVTime::ALPHA+MVTime::CLEAN, 9); showTime (q, MVTime::TIME+MVTime::NO_DM+MVTime::ALPHA+MVTime::CLEAN, 9); showTime (q, MVTime::BOOST, 0); showTime (q, MVTime::FITS, 10); showTime (q, MVTime::ISO, 10); showTime (q, MVTime::YMD, 5); showTime (q, MVTime::DMY, 4); showTime (q, MVTime::DMY+MVTime::CLEAN, 4); showTime (q, MVTime::DAY, 9); showTime (q, MVTime::DAY+MVTime::NO_TIME, 9); showTime (q, MVTime::DAY+MVTime::DMY+MVTime::USE_SPACE, 9); showTime (q, MVTime::MJD, 9); showTime (q, MVTime::NO_TIME+MVTime::MJD, 9); } catch (std::exception& x) { cout << "Unexpected exception: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/casa/Quanta/test/tMVTime.out000066400000000000000000000013201476623553700205150ustar00rootroot00000000000000Expected exception: Invalid date/time '2017-Jul-1x/11:57:0.0', invalid char about pos 10 Expected exception: Invalid date/time 'x', invalid char about pos 1 Expected exception: Invalid date/time '2017-Jul-1/11:57:0.0 x', invalid char about pos 11 +206.23.12.281 +.23.12.281 +..12.281 +206d23m12.281 +d23m12.281 +dm12.281 206.23.12.281 23.12.281 12.281 206d23m12.281 23m12.281 12.281 13:45:32.819 :45:32.819 ::32.819 13h45m32.819 h45m32.819 hm32.819 13:45:32.819 45:32.819 32.819 13h45m32.819 45m32.819 32.819 25-Jan-2012 13:45:33 2012-01-25T13:45:32.8187 2012-01-25 13:45:32.8187Z 2012/01/25/13:45:33 25-Jan-2012/13:46: 25-Jan-2012/13:46 Wed-13:45:32.819 Wed Wed 25-Jan-2012 13:45:32.819 55951/13:45:32.819 55951 casacore-3.7.1/casa/Quanta/test/tQVector.cc000066400000000000000000000076211476623553700205270ustar00rootroot00000000000000//# Copyright (C) 1994,1995,1996,1998,1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include int main () { try { QVector x; AlwaysAssert(x.size() == 0, AipsError); Vector vy(2); vy.set(4.0); String unit = "g"; QVector y(vy, unit); AlwaysAssert(y.size() == vy.size(), AipsError); AlwaysAssert(y[0].getValue() == vy[0], AipsError); AlwaysAssert(y.getUnit() == unit, AipsError); QVector t = y/2; Vector expec = y.getValue()/2.0; AlwaysAssert(allTrue(t.getValue() == expec), AipsError); AlwaysAssert(t.getUnit() == unit, AipsError); y.scale(0.5); expec = t.getValue(); AlwaysAssert(allTrue(y.getValue() == expec), AipsError); AlwaysAssert(y.getUnit() == unit, AipsError); Vector zx(2); zx[0] = 0; zx[1] = 1; QVector z(zx, unit); Quantity mymin = z.min(); AlwaysAssert(mymin.getValue() == 0, AipsError); AlwaysAssert(mymin.getUnit() == unit, AipsError); Vector aa(2), bb(2); aa[0] = 1; aa[1] = 2; bb[0] = 3; bb[1] = 4; QVector a(aa, "g"); QVector b(bb, "mg"); QVector add = a + b; AlwaysAssert(add.getValue()[0] == 1.003, AipsError); AlwaysAssert(add.getValue()[1] == 2.004, AipsError); AlwaysAssert(add.getUnit() == "g", AipsError); add = b + a; AlwaysAssert(add.getValue()[0] == 1003, AipsError); AlwaysAssert(add.getValue()[1] == 2004, AipsError); AlwaysAssert(add.getUnit() == "mg", AipsError); QVector sub = a - b; AlwaysAssert(sub.getValue()[0] == 0.997, AipsError); AlwaysAssert(sub.getValue()[1] == 1.996, AipsError); AlwaysAssert(sub.getUnit() == "g", AipsError); sub = b - a; AlwaysAssert(sub.getValue()[0] == -997, AipsError); AlwaysAssert(sub.getValue()[1] == -1996, AipsError); AlwaysAssert(sub.getUnit() == "mg", AipsError); { Vector vq(2, Quantity(5, "s")); vq[1].setUnit("m"); Bool thrown = False; try { // quantities don't have conformant units QVector qv(vq); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); vq[1].setUnit("s"); vq[1].setValue(2); QVector qv(vq); AlwaysAssert(qv.getValue()[0] == 5, AipsError); AlwaysAssert(qv.getValue()[1] == 2, AipsError); AlwaysAssert(qv.getFullUnit().getName() == "s", AipsError); vq[1].setUnit("ms"); QVector qv1(vq); AlwaysAssert(qv1.getValue()[0] == 5, AipsError); AlwaysAssert(qv1.getValue()[1] == 0.002, AipsError); AlwaysAssert(qv1.getFullUnit().getName() == "s", AipsError); } } catch (const std::exception& x) { cerr << x.what() << endl; return 1; } return 0; } casacore-3.7.1/casa/Quanta/test/tQuantum.cc000066400000000000000000000267631476623553700206060ustar00rootroot00000000000000//# tQuantum.cc: test program for Quantum and QC class //# Copyright (C) 1994,1995,1996,1998,1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include int main () { Quantity A(5,"m"), B(2.,"yd"); Quantity C(3.); Quantity D(6,"Jy"); Quantity DEG(5,"deg"); Quantity l4; Quantum CQ(7,"ml"); Vector V(2),V2(2),V1(2); Vector VI(2); V(0)=3; V(1)=4; V2=2; V1=3; VI(0)=7; VI(1)=8; Quantum > VQ(V,"uA"); Vector QVQ(3); QVQ(0)=A; QVQ(1)=D; QVQ(2)=DEG; try { cout << "Test quantity classes (Quantum, QC, Quantity)..." << endl; cout << endl << "--------------------------" << endl; cout << "Some physical constants:" << endl << endl; cout << " QC::c " << QC::c( ) << endl; cout << " QC::G " << QC::G( ) << endl; cout << " QC::h " << QC::h( ) << endl; cout << " QC::HI " << QC::HI( ) << endl; cout << " QC::R " << QC::R( ) << endl; cout << " QC::NA " << QC::NA( ) << endl; cout << " QC::e " << QC::e( ) << endl; cout << " QC::mp " << QC::mp( ) << endl; cout << " QC::mp_me " << QC::mp_me( ) << endl; cout << " QC::mu0 " << QC::mu0( ) << endl; cout << " QC::epsilon0 " << QC::epsilon0( ) << endl; cout << " QC::k " << QC::k( ) << endl; cout << " QC::F " << QC::F( ) << endl; cout << " QC::me " << QC::me( ) << endl; cout << " QC::re " << QC::re( ) << endl; cout << " QC::a0 " << QC::a0( ) << endl; cout << " QC::R0 " << QC::R0( ) << endl; cout << " QC::k2 " << QC::k2( ) << endl; cout << endl << "--------------------------" << endl; cout << "Manipulate quantities:" << endl << endl; UnitMap::clearCache(); cout << "QVQ = Vector(A,D,DEG) = " << QVQ << endl; cout << "A = Quantity(5,\"m\") = " << A << endl; cout << "B = Quantity(2.,\"yd\") = " << B << endl; cout << "C = Quantity(3.) = " << C << endl; cout << "D = Quantity(6,\"Jy\") = " << D << endl; cout << "DEG = Quantity(5,\"deg\") = " << DEG << endl; cout << "CQ = Complex(7,\"ml\") = " << CQ << endl; cout << "VQ = Vector((3,4),\"uA\") = " << VQ << endl; cout << "V2 = Vector(2,2) = " << V2 << endl; cout << "V1 = Vector(3) = " << V1 << endl; Quantity *E=new Quantity(10,"L"); cout << "*E = new Quantity(10,\"L\") = "<< *E << endl; delete E; cout << "-QVQ = " << -QVQ << endl; cout << "A*QVQ = " << A*QVQ << endl; cout << "A*B = " << A*B << endl; cout << "-B = " << -B << endl; cout << "2.*B = " << 2.*B << endl; cout << "B*2. = " << B*2. << endl; cout << "B/2. = " << B/2. << endl; cout << "2*CQ = " << Complex(2)*CQ << endl; cout << "V2*VQ = " << V2*VQ << endl; cout << "V1*VQ = " << V1*VQ << endl; cout << "A*C = " << A*C << endl; cout << "A/B = " << A/B << endl; cout << "A/C = " << A/C << endl; cout << "(A/B)^-5 = " << pow((A/B),-5) << endl; cout << "A+B = " << A+B << endl; cout << "A-B = " << A-B << endl; cout << "B+A = " << B+A << endl; cout << "B-A = " << B-A << endl; cout << "abs(A) = " << abs(A) << endl; cout << "abs(CQ) = " << abs(CQ) << endl; cout << "abs(VQ) = " << abs(VQ) << endl; cout << "ceil(A) = " << ceil(A) << endl; cout << "ceil(CQ) = " << ceil(CQ) << endl; cout << "ceil(VQ) = " << ceil(VQ) << endl; cout << "floor(A) = " << floor(A) << endl; cout << "floor(CQ) = " << floor(CQ) << endl; cout << "floor(VQ) = " << floor(VQ) << endl; cout << "sin(DEG) = " << sin(DEG) << endl; cout << "asin() = " << asin(sin(DEG)) << endl; cout << "atan2(1,5) = " << atan2(Quantity(1),Quantity(5)) << endl; cout << "log() = " << log(Quantity(2)) << endl; cout << "log10() = " << log10(Quantity(2)) << endl; cout << "exp() = " << exp(Quantity(2)) << endl; cout << "sqrt() = " << sqrt(Quantity(2)) << endl; cout << "sqrt(m2.s4) = " << sqrt(Quantity(2, "m2.s4")) << endl; cout << "sqrt(Jy2) = " << sqrt(Quantity(2, "Jy2")) << endl; cout << "sqrt(Jy2)^2 = " << sqrt(Quantity(2, "Jy2")) * sqrt(Quantity(2, "Jy2")) << endl; cout << "B==A = " << (B==A) << endl; cout << "VQ==V2 = " << (VQ==V2) << endl; cout << "VQ!=V1 = " << (VQ==V1) << endl; cout << "B!=A = " << (B!=A) << endl; cout << "D==A = " << (D==A) << endl; cout << "D!=A = " << (D!=A) << endl; cout << "BA = " << (B>A) << endl; cout << "B<=A = " << (B<=A) << endl; cout << "B>=A = " << (B>=A) << endl; cout << endl << "--------------------------" << endl; cout << "Convert " << "A*B" << " to:" << endl; cout << " m: " << (A*B).get("m") << endl; cout << " yd: " << (A*B).get("yd") << endl; cout << " m2: " << (A*B).get("m2") << endl; cout << " yd2: " << (A*B).get("yd2") << endl; cout << " unit: " << (A*B).getUnit() << endl; cout << " canonical: " << (A*B).get() << endl; cout << " can val: " << (A*B).get().getValue() << endl; cout << " can unit: " << (A*B).get().getUnit() << endl; cout << " km2 val: " << (A*B).get("km2").getValue() << endl; cout << endl << "--------------------------" << endl; cout << "Other conversions:" << endl; cout << "A to B " << (A.convert(B),A) << endl; cout << "D to Watt " << D.get("W") << endl; cout << "A value to 9 " << (A.setValue(9),A) << endl; cout << "A unit to km " << (A.setUnit("km"),A) << endl; cout << "scale A by 3 " << (A.scale(3),A) << endl; cout << endl << "--------------------------" << endl; cout << "Check types" << endl << endl; cout << " Are dam's Mpc's? " << Quantity(1,"dam").isConform("Mpc") << endl; cout << " Are Jy's Watts? " << Quantity(1,"Jy").isConform("W") << endl; cout << endl << "--------------------------" << endl; cout << "List contents of Cache" << endl << endl; UnitMap::listCache(); cout << endl << "--------------------------" << endl; Quantum ll5(5,Quantum(7.,"mm/s")); cout << "Mixed Quantity/Quantum " << ll5 << endl; } catch (std::exception& x) { cout << x.what() << endl; } cout << endl << "--------------------------" << endl; cout << "Try illegal operations" << endl << endl; try { Quantity loc(5,"KpH"); } catch (std::exception& x) { cout << x.what() << endl; } try { Quantity loc(A+D); } catch (std::exception& x) { cout << x.what() << endl; } try { // put in conditional so result is used, // so compiler won't emit warning of unused result if(A q(1, "Hz"); q.getValue("K"); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(! thrown, AipsError); try { Quantum q(1, "Hz"); q.getValue("K", True); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); Quantum q(1, "m"); AlwaysAssert(q.getValue("km") == 0.001, AipsError); q = Quantum(1, "h"); AlwaysAssert(q.getValue("deg") == 15, AipsError); q = Quantum(30, "deg"); AlwaysAssert(near(q.getValue("min"), 120.0), AipsError); q = Quantum(1.5, "GHz"); AlwaysAssert(near(q.getValue("cm"), 19.9862, 1e-5), AipsError); q = Quantum(3, "mm"); AlwaysAssert(near(q.getValue("MHz"), 99930.8, 1e-5), AipsError); } // Test copy construction and copy assignment with Vector does deep copying { // Copy constructor Quantum> original({1, 2, 3}, "m"); Quantum> copy_constructed(original); copy_constructed.getValue()[0] = 100; AlwaysAssert(original.getValue()[0] == 1, AipsError); // Copy assignment Quantum> copy_assigned; copy_assigned = original; copy_assigned.getValue()[0] = 100; AlwaysAssert(original.getValue()[0] == 1, AipsError); } cout << endl << "--------------------------" << endl; return 0; } casacore-3.7.1/casa/Quanta/test/tQuantum.out000066400000000000000000000112261476623553700210140ustar00rootroot00000000000000Test quantity classes (Quantum, QC, Quantity)... -------------------------- Some physical constants: QC::c 2.99792e+08 m/s QC::G 6.67259e-11 N.m2/kg2 QC::h 6.62608e-34 J.s QC::HI 1420.41 MHz QC::R 8.31451 J/K/mol QC::NA 6.02214e+23 mol-1 QC::e 1.60218e-19 C QC::mp 1.67262e-27 kg QC::mp_me 1836.15 QC::mu0 1.25664e-06 H/m QC::epsilon0 8.85419e-12 F/m QC::k 1.38066e-23 J/K QC::F 96485.3 C/mol QC::me 9.10939e-31 kg QC::re 2.8179e-15 m QC::a0 5.2918e-11 m QC::R0 6.9599e+08 m QC::k2 0.000295912 AU3/d2/S0 -------------------------- Manipulate quantities: QVQ = Vector(A,D,DEG) = [5 m, 6 Jy, 5 deg] A = Quantity(5,"m") = 5 m B = Quantity(2.,"yd") = 2 yd C = Quantity(3.) = 3 D = Quantity(6,"Jy") = 6 Jy DEG = Quantity(5,"deg") = 5 deg CQ = Complex(7,"ml") = (7,0) ml VQ = Vector((3,4),"uA") = [3, 4] uA V2 = Vector(2,2) = [2, 2] V1 = Vector(3) = [3, 3] *E = new Quantity(10,"L") = 10 L -QVQ = [-5 m, -6 Jy, -5 deg] A*QVQ = [25 m.m, 30 m.Jy, 25 m.deg] A*B = 10 m.yd -B = -2 yd 2.*B = 4 yd B*2. = 4 yd B/2. = 1 yd 2*CQ = (14,0) ml V2*VQ = [6, 8] uA V1*VQ = [9, 12] uA A*C = 15 m A/B = 2.5 m/(yd) A/C = 1.66667 m (A/B)^-5 = 0.01024 (m/(yd))-5 A+B = 6.8288 m A-B = 3.1712 m B+A = 7.46807 yd B-A = -3.46807 yd abs(A) = 5 m abs(CQ) = (7,0) ml abs(VQ) = [3, 4] uA ceil(A) = 5 m ceil(CQ) = (7,0) ml ceil(VQ) = [3, 4] uA floor(A) = 5 m floor(CQ) = (7,0) ml floor(VQ) = [3, 4] uA sin(DEG) = 0.0871557 asin() = 0.0872665 rad atan2(1,5) = 0.197396 rad log() = 0.693147 log10() = 0.30103 exp() = 7.38906 sqrt() = 1.41421 sqrt(m2.s4) = 1.41421 m.s2 sqrt(Jy2) = 1.41421e-26 kg.s-2 sqrt(Jy2)^2 = 2e-52 kg.s-2.kg.s-2 B==A = 0 VQ==V2 = 0 VQ!=V1 = 0 B!=A = 1 D==A = 0 D!=A = 1 BA = 0 B<=A = 1 B>=A = 0 -------------------------- Convert A*B to: m: 9.144 m.m yd: 10 yd.m m2: 9.144 m2 yd2: 10.9361 yd2 unit: m.yd canonical: 9.144 m2 can val: 9.144 can unit: m2 km2 val: 9.144e-06 -------------------------- Other conversions: A to B 5.46807 yd D to Watt 6e-26 W.m-2.s A value to 9 9 yd A unit to km 9 km scale A by 3 27 km -------------------------- Check types Are dam's Mpc's? 1 Are Jy's Watts? 0 -------------------------- List contents of Cache Cached unit table (26): kg.s-2 () 1 kg.s-2 m.s2 () 1 m.s2 m2 () 1 m2 (m/(yd))-5() 0.639265234981 Jy () 1e-26 kg.s-2 Jy2 () 1e-52 kg2.s-4 L () 0.001 m3 Mpc () 3.08567758065e+22 m W () 1 m2.kg.s-3 W.m-2.s () 1 kg.s-2 dam () 10 m kg.s-2.kg.s-2 () 1 kg2.s-4 km () 1000 m km2 () 1000000 m2 m () 1 m m.Jy () 1e-26 m.kg.s-2 m.deg () 0.0174532925199 m.rad m.m () 1 m2 m.yd () 0.9144 m2 m/(yd) () 1.09361329834 m2 () 1 m2 m2.s4 () 1 m2.s4 rad () 1 rad yd () 0.9144 m yd.m () 0.9144 m2 yd2 () 0.83612736 m2 -------------------------- Mixed Quantity/Quantum 5 mm/s -------------------------- Try illegal operations Unit::check Illegal unit string 'KpH' Quantum::operator+ unequal units 'km, 'Jy' Quantum::operator< unequal units 'km, 'Jy' Quantum::pow exponent too large Unit::check Illegal unit string 'JY' Quantum::sin illegal unit type 'km' Quantum::log illegal unit type 'km' UnitVal::UnitVal Illegal unit dimensions for root -------------------------- casacore-3.7.1/casa/Quanta/test/tQuantumHolder.cc000066400000000000000000000155261476623553700217370ustar00rootroot00000000000000//# tQuantumHolder.cc: This program tests QuantumHolder //# Copyright (C) 1998,2000,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include int main() { try { cout << "Test QuantumHolder " << endl; cout << "----------------------------------------------------" << endl; String error; QuantumHolder q00, q01; Quantity x00(12.5, "km/s"); Quantum x01(30.3, "Jy/a"); Quantum x02(2, "pc3/d"); String s00("12:30:00"); String s01("-97.8 Mpc/a"); String s02("12.5JY"); Record y00; cout << "Input quantity: " << (QBase &)x00 << endl; if (QuantumHolder(x00).toRecord(error, y00)) { if (q00.fromRecord(error, y00)) { cout <<"Record output quantity: " << q00.asQuantum() << endl; } else { cout << "From error: " << error << endl; } } else { cout << "To error: " << error << endl; } QuantumHolder q02(q00); if (q00.asQuantity() != q02.asQuantity()) { cout << "Error in copy constructor" << endl; } cout << "Is quantum: " << q00.isQuantum() << endl; cout << "Is quantity: " << q00.isQuantity() << endl; cout << "Is empty: " << q00.isEmpty() << endl; cout << "Is scalar: " << q00.isScalar() << endl; cout << "Is array: " << q00.isArray() << endl; cout << "Is real: " << q00.isReal() << endl; cout << "Is complex: " << q00.isComplex() << endl; cout << "Is Double: " << q00.isQuantumDouble() << endl; cout << "Is Float: " << q00.isQuantumFloat() << endl; cout << "Is Int: " << q00.isQuantumInt() << endl; cout << "Is Complex: " << q00.isQuantumComplex() << endl; cout << "Is DComplex: " << q00.isQuantumDComplex() << endl; cout << "Is Vector Double: " << q00.isQuantumVectorDouble() << endl; cout << "Is Vector Float: " << q00.isQuantumVectorFloat() << endl; cout << "Is Vector Int: " << q00.isQuantumVectorInt() << endl; cout << "Is Vector Complex: " << q00.isQuantumVectorComplex() << endl; cout << "Is Vector DComplex: " << q00.isQuantumVectorDComplex() << endl; q01 = q00; cout << "As quantity: " << q00.asQuantity() << endl; cout << "As Double: " << q00.asQuantumDouble() << endl; cout << "As Vector Double: " << q00.asQuantumVectorDouble() << endl; cout << "As Float: " << q00.asQuantumFloat() << endl; cout << "As Vector Float: " << q00.asQuantumVectorFloat() << endl; cout << "As Int: " << q00.asQuantumInt() << endl; cout << "As Vector Int: " << q00.asQuantumVectorInt() << endl; q00 = q01; cout << "As Complex: " << q00.asQuantumComplex() << endl; cout << "As Vector Complex: " << q00.asQuantumVectorComplex() << endl; cout << "As DComplex: " << q00.asQuantumDComplex() << endl; cout << "As Vector DComplex: " << q00.asQuantumVectorDComplex() << endl; cout << "Input quantity: " << (QBase &)x01 << endl; if (QuantumHolder(x01).toRecord(error, y00)) { if (q00.fromRecord(error, y00)) { cout <<"Record output quantity: " << q00.asQuantum() << endl; } else { cout << "From error: " << error << endl; } } else { cout << "To error: " << error << endl; } cout << "Input quantity: " << (QBase &)x02 << endl; if (QuantumHolder(x02).toRecord(error, y00)) { if (q00.fromRecord(error, y00)) { cout <<"Record output quantity: " << q00.asQuantity() << endl; } else { cout << "From error: " << error << endl; } } else { cout << "To error: " << error << endl; } cout << "Error expected:" << endl; cout << "Input quantity: " << (QBase &)x00 << endl; if (QuantumHolder(x00).toRecord(error, y00)) { y00.renameField("units", RecordFieldId("unit")); if (q00.fromRecord(error, y00)) { cout <<"Record output quantity: " << q00.asQuantity() << endl; } else { cout << "From error: " << error << endl; } } else { cout << "To error: " << error << endl; } cout << "Input String: " << s00 << endl; if (q00.fromString(error, s00)) { cout << "As quantity: " << q00.asQuantum() << endl; } else { cout << "Unexpected error for String " << s00 << endl; } cout << "Input String: " << s01 << endl; if (q00.fromString(error, s01)) { cout << "As quantity: " << q00.asQuantum() << endl; } else { cout << "Unexpected error for String " << s01 << endl; } cout << "Input String: " << s02 << endl; if (q00.fromString(error, s02)) { cout << "As quantity: " << q00.asQuantum() << endl; } else { cout << "Expected error for String " << s02 << " (value still " << q00.asQuantum() << ")" << endl; cout << "Error message now: " << error << endl; } cout << "----------------------------------------------------" << endl; } catch (std::exception& x) { cout << x.what() << endl; return 1; } return 0; } casacore-3.7.1/casa/Quanta/test/tQuantumHolder.out000066400000000000000000000035141476623553700221530ustar00rootroot00000000000000Test QuantumHolder ---------------------------------------------------- Input quantity: 12.5 km/s Record output quantity: 12.5 km/s Is quantum: 1 Is quantity: 1 Is empty: 0 Is scalar: 1 Is array: 0 Is real: 1 Is complex: 0 Is Double: 1 Is Float: 0 Is Int: 0 Is Complex: 0 Is DComplex: 0 Is Vector Double: 0 Is Vector Float: 0 Is Vector Int: 0 Is Vector Complex: 0 Is Vector DComplex: 0 As quantity: 12.5 km/s As Double: 12.5 km/s As Vector Double: [12.5] km/s As Float: 12.5 km/s As Vector Float: [12.5] km/s As Int: 12 km/s As Vector Int: [12] km/s As Complex: (12.5,0) km/s As Vector Complex: [(12.5,0)] km/s As DComplex: (12.5,0) km/s As Vector DComplex: [(12.5,0)] km/s Input quantity: 30.3 Jy/a Record output quantity: 30.3 Jy/a Input quantity: 2 pc3/d Record output quantity: 2 pc3/d Error expected: Input quantity: 12.5 km/s From error: Illegal Quantum record in QuantumHolder::fromRecord Input String: 12:30:00 As quantity: 187.5 deg Input String: -97.8 Mpc/a As quantity: -97.8 Mpc/a Input String: 12.5JY Expected error for String 12.5JY (value still -97.8 Mpc/a) Error message now: Illegal Quantum record in QuantumHolder::fromRecord in QuantumHolder::fromString with input string "12.5JY": Illegal input units or format ---------------------------------------------------- casacore-3.7.1/casa/Quanta/test/tUnit.cc000066400000000000000000000156741476623553700200720ustar00rootroot00000000000000//# tUnit.cc: test program for Unit section of Measures module //# Copyright (C) 1994-1996,1998-2000,2002,2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include int main () { try { cout << "Test units class (Unit)..." << endl; cout << "--------------------------" << endl; UnitName localName; UnitVal localVal; cout << "Define user unit symbols:" << endl; cout << " beam = 0.1*pi \"_2 ; Jypb = 1 Jy/beam" << endl; UnitMap::putUser("beam",UnitVal(M_PI * 0.1,"\"_2"),"telescope beam"); UnitName jypb("Jypb",UnitVal(1.,"Jy/beam"),"Jansky/beam"); UnitMap::putUser(jypb); cout << endl << "--------------------------" << endl; cout << "List all defined symbols" << endl << endl; UnitMap::list(); cout << endl << "--------------------------" << endl; cout << "List contents of Cache" << endl << endl; UnitMap::listCache(); cout << endl << "--------------------------" << endl; cout << "Check if prefixes known" << endl << endl; cout << "'k' prefix = " << UnitMap::getPref("k",localName) << " ( " << localName << ")" << endl; cout << "'K' prefix = " << UnitMap::getPref("K",localName) << " ( " << localName << ")" << endl; cout << endl << "--------------------------" << endl; cout << "Check if cached unit" << endl << endl; localVal = UnitVal(); cout << "'Jy/beam' = " << UnitMap::getCache("Jy/beam",localVal) << " ( " << localVal << ")" << endl; cout << "'Jy/beam' = " << UnitMap::getCache("Jy/beam",localVal) << " ( " << localVal << ")" << endl; localVal = UnitVal(); cout << "'JY/beam' = " << UnitMap::getCache("JY/beam",localVal) << " ( " << localVal << ")" << endl; cout << "'JY/beam' = " << UnitMap::getCache("JY/beam",localVal) << " ( " << localVal << ")" << endl; cout << endl << "--------------------------" << endl; cout << "Check if known unit" << endl << endl; cout << "'Jy' = " << UnitMap::getUnit("Jy",localName) << " ( " << localName << ")" << endl; cout << "'JY' = " << UnitMap::getUnit("JY",localName) << " ( " << localName << ")" << endl; cout << endl << "--------------------------" << endl; cout << "Get value and name from a unit name" << endl << endl; cout << "Jypb: Value = " << jypb.getVal() << " Name = " << jypb.getName() << endl; cout << endl << "--------------------------" << endl; cout << "Create and manipulate a unit value" << endl << endl; UnitVal myVal(3.2,"W"); UnitVal myVal1(2.1,"mg"); UnitVal myVal2 = myVal; cout << "A = UnitVal(3.2,\"W\") = " << myVal << endl; cout << "B = UnitVal(2.1,\"mg\") = " << myVal1 << endl; cout << "A*B = " << myVal*myVal1 << endl; cout << "2*A = " << 2*myVal << endl; cout << "A*2 = " << myVal*2 << endl; cout << "A/B = " << myVal/myVal1 << endl; cout << "A^3 = " << myVal.pow(3) << endl; cout << "A^-2 = " << myVal.pow(-2) << endl; cout << "A==B = " << (myVal==myVal1) << endl; cout << "A==A = " << (myVal==myVal) << endl; cout << "A!=B = " << (myVal!=myVal1) << endl; cout << "check(A) = " << UnitVal::check("W") << endl; cout << "check(\"KpH\") = " << UnitVal::check("KpH",myVal2) << " = " << myVal2 << endl; cout << "factor(A) = " << myVal.getFac() << endl; cout << "dim(A) = " << myVal.getDim() << endl; cout << endl << "--------------------------" << endl; cout << "Test FITS values" << endl << endl; cout << "User list before FITS:" << endl; UnitMap::listUser(); UnitMap::removeUser("beam"); UnitMap::addFITS(); cout << "User list after FITS:" << endl; UnitMap::listUser(); UnitVal myF1(4.0,"mJY/BEAM"); cout << "A = 4 mJY/BEAM : " << myF1 << endl; Unit Fstr("M(JY5/SEC2.(YEAR.HZ).KM)"); cout << "A FITS unit to translate: " << Fstr.getName() << endl; cout << "it translates to: " << UnitMap::fromFITS(Fstr).getName() << endl; cout << "or: " << Fstr.getValue() << endl; Unit Fstr2(UnitMap::fromFITS(Fstr)); cout << "and back to: " << UnitMap::toFITS(Fstr2).getName() << endl; cout << "or: " << Fstr2.getValue() << endl; Fstr = Unit("M(JY5/SEC**2.(YEAR*HZ)*KM)"); cout << "A FITS unit to translate: " << Fstr.getName() << endl; cout << "it translates to: " << UnitMap::fromFITS(Fstr).getName() << endl; cout << "or: " << Fstr.getValue() << endl; Fstr2 = (UnitMap::fromFITS(Fstr)); cout << "and back to: " << UnitMap::toFITS(Fstr2).getName() << endl; cout << "or: " << Fstr2.getValue() << endl; Fstr = Unit("M(JY5/SEC^2.(YEAR*HZ).KM)"); cout << "A FITS unit to translate: " << Fstr.getName() << endl; cout << "it translates to: " << UnitMap::fromFITS(Fstr).getName() << endl; cout << "or: " << Fstr.getValue() << endl; Fstr2 = (UnitMap::fromFITS(Fstr)); cout << "and back to: " << UnitMap::toFITS(Fstr2).getName() << endl; cout << "or: " << Fstr2.getValue() << endl; UnitMap::clearFITS(); cout << "User list after FITS removal:" << endl; UnitMap::listUser(); } catch (std::exception& x) { cout << "Unexpected: " << x.what() << endl; } cout << endl << "--------------------------" << endl; cout << "Try illegal Unit values" << endl << endl; try { Unit ca="Kpm/s"; } catch (std::exception& x) { cout << x.what() << endl; } try { UnitVal errval(2.,"KpH"); } catch (std::exception& x) { cout << x.what() << endl; } cout << endl << "--------------------------" << endl; return(0); } casacore-3.7.1/casa/Quanta/test/tUnit.out000066400000000000000000000312721476623553700203040ustar00rootroot00000000000000Test units class (Unit)... -------------------------- Define user unit symbols: beam = 0.1*pi "_2 ; Jypb = 1 Jy/beam -------------------------- List all defined symbols Prefix table (24): E (exa) 1e+18 G (giga) 1000000000 M (mega) 1000000 P (peta) 1e+15 Q (quetta) 1e+30 R (ronna) 1e+27 T (tera) 1e+12 Y (yotta) 1e+24 Z (zetta) 1e+21 a (atto) 1e-18 c (centi) 0.01 d (deci) 0.1 da (deka) 10 f (femto) 1e-15 h (hecto) 100 k (kilo) 1000 m (milli) 0.001 n (nano) 1e-09 p (pico) 1e-12 q (quecto) 1e-30 r (ronto) 1e-27 u (micro) 1e-06 y (yocto) 1e-24 z (zepto) 1e-21 Defining unit table (10): A (ampere) 1 A K (kelvin) 1 K _ (undimensioned) 1 _ cd (candela) 1 cd kg (kilogram) 1 kg m (metre) 1 m mol (mole) 1 mol rad (radian) 1 rad s (second) 1 s sr (steradian) 1 sr SI unit table (50): $ (currency) 1 _ % (percent) 0.01 %% (permille) 0.001 A (ampere) 1 A AE (astronomical unit) 149597870659 m AU (astronomical unit) 149597870659 m Bq (becquerel) 1 s-1 C (coulomb) 1 s.A F (farad) 1 m-2.kg-1.s4.A2 Gy (gray) 1 m2.s-2 H (henry) 1 m2.kg.s-2.A-2 Hz (hertz) 1 s-1 J (joule) 1 m2.kg.s-2 Jy (jansky) 1e-26 kg.s-2 K (kelvin) 1 K L (litre) 0.001 m3 M0 (solar mass) 1.98891944407e+30 kg N (newton) 1 m.kg.s-2 Ohm (ohm) 1 m2.kg.s-3.A-2 Pa (pascal) 1 m-1.kg.s-2 S (siemens) 1 m-2.kg-1.s3.A2 S0 (solar mass) 1.98891944407e+30 kg Sv (sievert) 1 m2.s-2 T (tesla) 1 kg.s-2.A-1 UA (astronomical unit) 149597870659 m V (volt) 1 m2.kg.s-3.A-1 W (watt) 1 m2.kg.s-3 Wb (weber) 1 m2.kg.s-2.A-1 _ (undimensioned) 1 _ a (year) 31557600 s arcmin (arcmin) 0.000290888208666 rad arcsec (arcsec) 4.8481368111e-06 rad as (arcsec) 4.8481368111e-06 rad cd (candela) 1 cd cy (century) 3155760000 s d (day) 86400 s deg (degree) 0.0174532925199 rad g (gram) 0.001 kg h (hour) 3600 s l (litre) 0.001 m3 lm (lumen) 1 cd.sr lx (lux) 1 m-2.cd.sr m (metre) 1 m min (minute) 60 s mol (mole) 1 mol pc (parsec) 3.08567758065e+16 m rad (radian) 1 rad s (second) 1 s sr (steradian) 1 sr t (tonne) 1000 kg Customary unit table (78): " (arcsec) 4.8481368111e-06 rad "_2 (square arcsec) 2.35044305391e-11 sr ' (arcmin) 0.000290888208666 rad '' (arcsec) 4.8481368111e-06 rad ''_2 (square arcsec) 2.35044305391e-11 sr '_2 (square arcmin) 8.46159499408e-08 sr : (hour) 3600 s :: (minute) 60 s ::: (second) 1 s Ah (ampere hour) 3600 s.A Angstrom (angstrom) 1e-10 m Btu (British thermal unit (Int)) 1055.056 m2.kg.s-2 CM (metric carat) 0.0002 kg Cal (large calorie (Int)) 4186.8 m2.kg.s-2 FU (flux unit) 1e-26 kg.s-2 G (gauss) 0.0001 kg.s-2.A-1 Gal (gal) 0.01 m.s-2 Gb (gilbert) 0.795774715459 A Mx (maxwell) 1e-08 m2.kg.s-2.A-1 Oe (oersted) 79.5774715459 m-1.A R (mile) 0.000258 kg-1.s.A St (stokes) 0.0001 m2.s-1 Torr (torr) 133.322368421 m-1.kg.s-2 USfl_oz (fluid ounce (US)) 2.95735295625e-05 m3 USgal (gallon (US)) 0.003785411784 m3 WU (WSRT flux unit) 5e-29 kg.s-2 abA (abampere) 10 A abC (abcoulomb) 10 s.A abF (abfarad) 1000000000 m-2.kg-1.s4.A2 abH (abhenry) 1e-09 m2.kg.s-2.A-2 abOhm (abohm) 1e-09 m2.kg.s-3.A-2 abV (abvolt) 1e-08 m2.kg.s-3.A-1 ac (acre) 4046.8564224 m2 adu (dimensionless ADC unit) 1 _ arcmin_2 (square arcmin) 8.46159499408e-08 sr arcsec_2 (square arcsec) 2.35044305391e-11 sr ata (technical atmosphere) 98066.5 m-1.kg.s-2 atm (standard atmosphere) 101325 m-1.kg.s-2 bar (bar) 100000 m-1.kg.s-2 beam (undefined beam area) 1 _ cal (calorie (Int)) 4.1868 m2.kg.s-2 count (count) 1 _ cwt (hundredweight) 50.80234544 kg debye (statvolt) 3.33564095198e-29 m.s.A deg_2 (square degree) 0.000304617419787 sr dyn (dyne) 1e-05 m.kg.s-2 eV (electron volt) 1.60217733e-19 m2.kg.s-2 erg (erg) 1e-07 m2.kg.s-2 fl_oz (fluid ounce (Imp)) 2.84130488996e-05 m3 ft (foot) 0.3048 m fu (flux unit) 1e-26 kg.s-2 fur (furlong) 201.168 m gal (gallon (Imp)) 0.00454608782394 m3 ha (hectare) 10000 m2 hp (horsepower) 745.7 m2.kg.s-3 in (inch) 0.0254 m kn (knot (Imp)) 0.514773333333 m.s-1 lambda (lambda) 1 _ lb (pound (avoirdupois)) 0.45359237 kg ly (light year) 9.46073047e+15 m mHg (metre of mercury) 133322.387415 m-1.kg.s-2 mile (mile) 1609.344 m n_mile (nautical mile (Imp)) 1853.184 m oz (ounce (avoirdupois)) 0.028349523125 kg pixel (pixel) 1 _ sb (stilb) 10000 m-2.cd sq_arcmin (square arcmin) 8.46159499408e-08 sr sq_arcsec (square arcsec) 2.35044305391e-11 sr sq_deg (square degree) 0.000304617419787 sr statA (statampere) 3.33564095198e-10 A statC (statcoulomb) 3.33564095198e-10 s.A statF (statfarad) 1.11188031733e-12 m-2.kg-1.s4.A2 statH (stathenry) 899377374000 m2.kg.s-2.A-2 statOhm (statohm) 899377374000 m2.kg.s-3.A-2 statV (statvolt) 299.792458 m2.kg.s-3.A-1 u (atomic mass unit) 1.661e-27 kg yd (yard) 0.9144 m yr (year) 31557600 s User unit table (2): Jypb (Jansky/beam) 1.35425483146e-15 kg.s-2.sr-1 beam (telescope beam) 7.38413463084e-12 sr -------------------------- List contents of Cache Cached unit table (1): Jy/beam () 1.35425483146e-15 kg.s-2.sr-1 -------------------------- Check if prefixes known 'k' prefix = 1 ( k (kilo) 1000) 'K' prefix = 0 ( () 1) -------------------------- Check if cached unit 'Jy/beam' = 1 ( 1.35425e-15 kg.s-2.sr-1) 'Jy/beam' = 1 ( 1.35425e-15 kg.s-2.sr-1) 'JY/beam' = 0 ( 1) 'JY/beam' = 0 ( 1) -------------------------- Check if known unit 'Jy' = 1 ( Jy (jansky) 1e-26 kg.s-2) 'JY' = 0 ( () 1) -------------------------- Get value and name from a unit name Jypb: Value = 1.35425e-15 kg.s-2.sr-1 Name = Jypb -------------------------- Create and manipulate a unit value A = UnitVal(3.2,"W") = 3.2 m2.kg.s-3 B = UnitVal(2.1,"mg") = 2.1e-06 kg A*B = 6.72e-06 m2.kg2.s-3 2*A = 6.4 m2.kg.s-3 A*2 = 6.4 m2.kg.s-3 A/B = 1.52381e+06 m2.s-3 A^3 = 32.768 m6.kg3.s-9 A^-2 = 0.0976562 m-4.kg-2.s6 A==B = 0 A==A = 1 A!=B = 1 check(A) = 1 check("KpH") = 0 = 1 factor(A) = 3.2 dim(A) = m2.kg.s-3 -------------------------- Test FITS values User list before FITS: Jypb (Jansky/beam) 1.35425483146e-15 kg.s-2.sr-1 beam (telescope beam) 7.38413463084e-12 sr User list after FITS: BEAM (dimensionless beam) 1 _ DAYS (day) 86400 s DEG (degree) 0.0174532925199 rad DEGREES (degree) 0.0174532925199 rad HZ (hertz) 1 s-1 JY (jansky) 1e-26 kg.s-2 Jypb (Jansky/beam) 1.35425483146e-15 kg.s-2.sr-1 KELVIN (kelvin) 1 K KELVINS (kelvin) 1 K KM (km) 1000 m M (meter) 1 m METERS (meter) 1 m PASCAL (pascal) 1 m-1.kg.s-2 PIXEL (dimensionless pixel) 1 _ S (second) 1 s SEC (second) 1 s SECONDS (second) 1 s VOLTS (volt) 1 m2.kg.s-3.A-1 YEAR (year) 31557600 s YEARS (year) 31557600 s A = 4 mJY/BEAM : 4e-29 kg.s-2._-1 A FITS unit to translate: M(JY5/SEC2.(YEAR.HZ).KM) it translates to: m(Jy5/s2.(a.Hz).km) or: 3.15576e-120 m2.kg5.s-12 and back to: M(JY5/S2.(YEAR.HZ).KM) or: 3.15576e-120 m2.kg5.s-12 A FITS unit to translate: M(JY5/SEC2.(YEAR.HZ).KM) it translates to: m(Jy5/s2.(a.Hz).km) or: 3.15576e-120 m2.kg5.s-12 and back to: M(JY5/S2.(YEAR.HZ).KM) or: 3.15576e-120 m2.kg5.s-12 A FITS unit to translate: M(JY5/SEC2.(YEAR.HZ).KM) it translates to: m(Jy5/s2.(a.Hz).km) or: 3.15576e-120 m2.kg5.s-12 and back to: M(JY5/S2.(YEAR.HZ).KM) or: 3.15576e-120 m2.kg5.s-12 User list after FITS removal: Jypb (Jansky/beam) 1.35425483146e-15 kg.s-2.sr-1 -------------------------- Try illegal Unit values Unit::check Illegal unit string 'Kpm/s' UnitVal::UnitVal Illegal unit string 'KpH' -------------------------- casacore-3.7.1/casa/System.h000066400000000000000000000070601476623553700156730ustar00rootroot00000000000000//# System.h: System related classes. //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_SYSTEM_H #define CASA_SYSTEM_H #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // // Classes and global functions for system use // // // // // This module is a bag of related systems classes and // global functions. // // The following functionality is available: //
            //
          • Class // Aipsrc // to read the aipsrc general resource files. //
          • Class // AipsrcValue // to read values from the Aipssrc general resource files. //
          • Class // AipsrcVector // to read multiple values from the Aipssrc general resource files. //
          • Class // AppInfo // to hold general information for application. //
          • Class // Choice // to ask a user a choice. //
          • Class // ObjectID // to hold a unique identifier for distributed and other objects. //
          • Class // PGPlotter // to offer a standard plotting object for application programmers. // using // PGPlotterInterface as its abstract base class. //
          • Class // ProgressMeter // to offer visual indication of a tasks progress. //
          // // You may want to look at the individual header files // to see whether you might not prefer to include only the header // files you really need; it may be more efficient to do so. // // //
          //# //#
        179. //# // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/System/000077500000000000000000000000001476623553700155175ustar00rootroot00000000000000casacore-3.7.1/casa/System/Aipsrc.cc000066400000000000000000000443131476623553700172540ustar00rootroot00000000000000//# Aipsrc.cc: Class to read the aipsrc general resource files //# Copyright (C) 1995,1996,1997,1998,2000,2001,2002,2003,2004,2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // This is the function that does most of the work. It is pretty slow for // large maps, but no real problem. Bool Aipsrc::matchKeyword(uInt &where, const String &keyword, uInt start) { for (uInt i=start; i &tname) { String res; if (find(res, keyword)) { value = MUString::minimaxNC(res, tname); return (value < tname.nelements() ? True : False); } return False; } Bool Aipsrc::find(uInt &value, const String &keyword, Int Nname, const String tname[], const String &deflt) { if (!find(value, keyword, Nname, tname)) { value = MUString::minimaxNC(deflt, Nname, tname); return False; } return True; } Bool Aipsrc::find(uInt &value, const String &keyword, const Vector &tname, const String &deflt) { if (!find(value, keyword, tname)) { value = MUString::minimaxNC(deflt, tname); return False; } return True; } Bool Aipsrc::findDir(String& foundDir, const String& lastPart, const Vector& prepends, const Vector& appends, Bool useStds) { // Setup a string that is either "/" + lastPart or blank. String myLastPart(""); if (lastPart != "") { myLastPart += "/" + lastPart; } // Note that this function returns as soon as possible, i.e. it goes until it // matches or runs out of possibilities. for (uInt i = 0; i < prepends.nelements(); ++i) { foundDir = prepends[i] + myLastPart; File testPath(foundDir); if (testPath.isDirectory()) { return True; } } if (useStds) { // Test . using lastPart or ., not "". if (lastPart!= "") { foundDir = lastPart; } else { foundDir = "."; } File testDot(foundDir); if (testDot.isDirectory()) { return True; } foundDir = aipsHome() + myLastPart; File testAipsHome(foundDir); if (testAipsHome.isDirectory()) { return True; } foundDir = aipsRoot() + myLastPart; File testAipsRoot(foundDir); if (testAipsRoot.isDirectory()) { return True; } } for (uInt i = 0; i < appends.nelements(); ++i) { foundDir = appends[i] + myLastPart; File testPath(foundDir); if (testPath.isDirectory()) { return True; } } return False; } void Aipsrc::reRead() { parse(); // i.e. bypass theirCallOnce(parse) } Double Aipsrc::lastRead() { return lastParse; } const Block &Aipsrc::values() { std::call_once(theirCallOnceFlag, parse); return keywordValue; } const Block &Aipsrc::patterns() { std::call_once(theirCallOnceFlag, parse); return keywordPattern; } void Aipsrc::fillAips() { uhome = EnvironmentVariable::get("HOME"); if (uhome.empty()) { throw AipsError(String("The HOME environment variable has not been set" "\n\t(see system administrator)")); } String aipsPath; if (extAipsPath.empty()) { aipsPath = EnvironmentVariable::get("CASAPATH"); if (aipsPath.empty()) { aipsPath = EnvironmentVariable::get("AIPSPATH"); } } else { aipsPath = extAipsPath; } // Set the path to home if not defined in any way. if (aipsPath.empty()) { setAipsPath(uhome); aipsPath = extAipsPath; } Int n = aipsPath.freq(' ') + aipsPath.freq(' ') + 4; String *newdir = new String[n]; n = split(aipsPath, newdir, n, Regex("[ ]")); // Cater for non-existing fields for (Int i=n; i<4; i++) { newdir[i] = "UnKnOwN"; } root = newdir[0]; arch = root + "/" + newdir[1]; site = arch + "/" + newdir[2]; host = site + "/" + newdir[3]; delete [] newdir; } void Aipsrc::setAipsPath(const String &path) { if (extAipsPath.empty()) { extAipsPath = path + " "; } } const String &Aipsrc::aipsRoot() { std::call_once(theirCallOnceFlag, parse); return root; } const String &Aipsrc::aipsArch() { std::call_once(theirCallOnceFlag, parse); return arch; } const String &Aipsrc::aipsSite() { std::call_once(theirCallOnceFlag, parse); return site; } const String &Aipsrc::aipsHost() { std::call_once(theirCallOnceFlag, parse); return host; } const String &Aipsrc::aipsHome() { std::call_once(theirCallOnceFlag, parse); return home; } uInt Aipsrc::registerRC(const String &keyword, Block &nlst) { uInt n; for (n=0; nnlst.nelements()) { nlst.resize(n); } nlst[n-1] = keyword; return n; } uInt Aipsrc::registerRC(const String &keyword, const String &deflt) { uInt n = Aipsrc::registerRC(keyword, nstrlst); strlst.resize(n); find (strlst[n-1], keyword, deflt); return n; } uInt Aipsrc::registerRC(const String &keyword, Int Nname, const String tname[], const String &deflt) { uInt n = Aipsrc::registerRC(keyword, ncodlst); codlst.resize(n); find (codlst[n-1], keyword, Nname, tname, deflt); return n; } uInt Aipsrc::registerRC(const String &keyword, const Vector &tname, const String &deflt) { uInt n = Aipsrc::registerRC(keyword, ncodlst); codlst.resize(n); find (codlst[n-1], keyword, tname, deflt); return n; } const String &Aipsrc::get(uInt keyword) { AlwaysAssert(keyword>0 && keyword<=strlst.nelements(), AipsError); return strlst[keyword-1]; } const uInt &Aipsrc::get(uInt &code, uInt keyword) { AlwaysAssert(keyword>0 && keyword<=codlst.nelements(), AipsError); code = codlst[keyword-1]; return codlst[keyword-1]; } void Aipsrc::set(uInt keyword, const String &deflt) { AlwaysAssert(keyword>0 && keyword<=strlst.nelements(), AipsError); strlst[keyword-1] = deflt; } void Aipsrc::set(uInt keyword, Int Nname, const String tname[], const String &deflt) { AlwaysAssert(keyword>0 && keyword<=codlst.nelements(), AipsError); find (codlst[keyword-1], String::toString(keyword), Nname, tname, deflt); } void Aipsrc::set(uInt keyword, const Vector &tname, const String &deflt) { AlwaysAssert(keyword>0 && keyword<=codlst.nelements(), AipsError); find (codlst[keyword-1], String::toString(keyword), tname, deflt); } void Aipsrc::save(uInt keyword) { AlwaysAssert(keyword>0 && keyword<=strlst.nelements(), AipsError); Aipsrc::save(nstrlst[keyword-1], strlst[keyword-1]); } void Aipsrc::save(uInt keyword, const String tname[]) { AlwaysAssert(keyword>0 && keyword<=codlst.nelements(), AipsError); Aipsrc::save(ncodlst[keyword-1], tname[codlst[keyword-1]]); } void Aipsrc::save(uInt keyword, const Vector &tname) { AlwaysAssert(keyword>0 && keyword<=codlst.nelements(), AipsError); Aipsrc::save(ncodlst[keyword-1], tname(codlst[keyword-1])); } // Note that the parameters should not be references! void Aipsrc::save(const String keyword, const String val) { static uInt nv_r = Aipsrc::registerRC("user.aipsrc.edit.keep", "5"); static String editTxt = "# Edited at "; std::call_once(theirCallOnceFlag, parse); String filn(uhome + "/.aipsrc"); String filno(filn + ".old"); RegularFile fil(filn); RegularFile filo(filno); if (fil.exists()) { fil.move(filno, True); } else if (filo.exists()) { filo.remove(); } ofstream ostr(filn.chars(), ios::out); ostr << editTxt << MVTime(Time()).string(MVTime::YMD | MVTime::LOCAL, 0) << endl; ostr << keyword << ": " << val << endl; fil = RegularFile(filno); Char *buf = new Char[8192]; // Single lines must fit in this if (fil.exists()) { String buffer; Int nv = atoi(Aipsrc::get(nv_r).chars()); // number to keep Bool editSeen = False; // if edit line seen String editBuf; // edit line buffer Int editCnt = 0; // count for edits String kwt = keyword + ":"; // keyword test ifstream istr(filno.chars(), ios::in ); while (istr.getline(buf, 8192)) { buffer = buf; if (editSeen) { if (buffer.index(kwt) == 0) { editCnt++; if (editCnt < nv) { // copy ostr << editBuf << endl; ostr << buffer << endl; } editSeen = False; continue; } else { ostr << editBuf << endl; } } editSeen = (buffer.index(editTxt) == 0); if (editSeen) { editBuf = buffer; } else { ostr << buffer << endl; } } } delete [] buf; } void Aipsrc::parse() { // Refill basic data fillAips(); // If defined, use setting of CASARCFILES. Make sure it ends with a colon. String filelist = EnvironmentVariable::get("CASARCFILES"); if (! filelist.empty()) { filelist += ':'; } else { // Otherwise use CASAPATH. // This parse based on order HOME, AIPSROOT, AIPSHOST, AIPSSITE, AIPSARCH filelist = uhome + String("/.casarc:"); filelist += uhome + String("/.casa/rc:"); filelist += uhome + String("/.aipsrc:"); filelist += (root + String("/.aipsrc:")); filelist += (host + String("/aipsrc:")); filelist += (site + String("/aipsrc:")); filelist += (arch + String("/aipsrc:")); } doParse(filelist); String x; // Use findNoParse() variant to avoid recursion back into parse(). if (findNoParse(x, String("user.aipsdir"), 0)) { home = x; } else { home = uhome + "/aips++"; } } void Aipsrc::doParse(String &fileList) { Time x; lastParse = x.modifiedJulianDay(); // Save time of parse Int nkw = Aipsrc::genParse(Aipsrc::keywordPattern, Aipsrc::keywordValue, Aipsrc::fileEnd, fileList); const String gs00("."); // make correct patterns const String gs01("\\."); const String gs10("*"); const String gs11(".*"); String keyword; for (Int i=0; i &keywordPattern, Block &keywordValue, uInt &fileEnd, const String &fileList) { keywordValue.resize(0, True); // Clear the old values if any keywordPattern.resize(0, True); Block keywordFile; fileEnd = 0; uInt nkw = 0; // # of keywords found Int nfile = 0; // # of files found // This here be the parse function. It looks through all the directories // looking for files to parse. Int dirCount(fileList.freq(':') + 1); String *directories = new String[dirCount]; dirCount = split(fileList, directories, dirCount, ":"); keywordFile.resize(dirCount); Char *buf = new Char[8192]; for (Int i=0; i= keywordPattern.nelements()) { keywordPattern.resize(2*keywordPattern.nelements() + 1); keywordValue.resize(keywordPattern.nelements()); } keywordValue[nkw] = value; keywordPattern[nkw] = keyword; nkw++; if (i == 0) fileEnd = nkw; } } } } nfile++; } delete [] buf; delete [] directories; // Resize static lists keywordValue.resize(nkw, True); keywordPattern.resize(nkw, True); return keywordValue.nelements(); } void Aipsrc::show() { show(cout); } void Aipsrc::show(ostream &oStream) { std::call_once(theirCallOnceFlag, parse); String nam; const String gs00(".*"); const String gs01("*"); const String gs10("\\."); const String gs11("."); oStream << keywordValue.nelements() << " keyword/value pairs found:" << endl; for (uInt j = 0; j &namlst, Vector &vallst, const String &fileList) { uInt ef; Block nl; Block vl; Int nkw = Aipsrc::genParse(nl, vl, ef, fileList); Block nla; Block vla; nla.resize(0); vla.resize(0); uInt n; for (Int i=nkw-1; i>=0; i--) { // reverse order to do aipsrc like if (!nl[i].contains('*')) { // no wild cards n = Aipsrc::registerRC(nl[i], nla); vla.resize(n); vla[n-1] = vl[i]; } } namlst = Vector(nla.begin(), nla.end()); vallst = Vector(vla.begin(), vla.end()); return namlst.nelements(); } void Aipsrc::genSave(Vector &namlst, Vector &vallst, const String &fnam) { static String editTxt = "# Saved at "; String filno(fnam + ".old"); RegularFile fil(fnam); RegularFile filo(filno); if (fil.exists()) { fil.move(filno, True); } else if (filo.exists()) { filo.remove(); } ofstream ostr(fnam.chars(), ios::out); ostr << editTxt << MVTime(Time()).string(MVTime::YMD | MVTime::LOCAL, 0) << endl; for (Int i=namlst.nelements()-1; i>=0; i--) { ostr << namlst(i) << ": " << vallst(i) << endl; } } void Aipsrc::genSet(Vector &namlst, Vector &vallst, const String &nam, const String &val) { Block nl = makeBlock(namlst); uInt n = Aipsrc::registerRC(nam, nl); if (n > vallst.nelements()) vallst.resize(n, True); vallst(n-1) = val; // if (n > namlst.nelements()) namlst.resize(n, True); namlst.resize(0); namlst = Vector(nl.begin(), nl.end()); } Bool Aipsrc::genUnSet(Vector &namlst, Vector &vallst, const String &nam) { uInt n; uInt N = namlst.nelements(); for (n=0; nN) return False; for (uInt i=n; i &namlst, Vector &vallst, const String &nam) { uInt n; for (n=0; nvallst.nelements()) return False; val = vallst(n-1); return True; } // Static Initializations -- Only really want to read the files once std::once_flag Aipsrc::theirCallOnceFlag; Double Aipsrc::lastParse = 0; Block Aipsrc::keywordPattern(0); Block Aipsrc::keywordValue(0); uInt Aipsrc::fileEnd = 0; String Aipsrc::extAipsPath = String(); String Aipsrc::root = String(); String Aipsrc::arch = String(); String Aipsrc::site = String(); String Aipsrc::host = String(); String Aipsrc::home = String(); String Aipsrc::uhome= String(); Bool Aipsrc::filled = False; Block Aipsrc::strlst(0); Block Aipsrc::nstrlst(0); Block Aipsrc::codlst(0); Block Aipsrc::ncodlst(0); } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/System/Aipsrc.h000066400000000000000000000415751476623553700171250ustar00rootroot00000000000000//# Aipsrc.h: Class to read the casa general resource files //# Copyright (C) 1995,1996,1997,1998,1999,2002,2004,2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_AIPSRC_H #define CASA_AIPSRC_H #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations template class AipsrcValue; template class AipsrcVector; class Aipsrc; //# Typedefs typedef AipsrcValue AipsrcDouble; typedef AipsrcValue AipsrcInt; typedef AipsrcValue AipsrcBool; typedef Aipsrc AipsrcString; typedef AipsrcVector AipsrcVDouble; typedef AipsrcVector AipsrcVInt; typedef AipsrcVector AipsrcVBool; typedef AipsrcVector AipsrcVString; // Class to read the casa general resource files // // // // //
        180. None // // // // A class for getting values from the casa resource files // // // // The static Aipsrc class can get information from the casa resource files. // It has the same functionality as getrc (c program used for Casacore // installation scripts).
          // In addition it acts as a central clearing house between system and // software by providing functionality to obtain Casacore system parameters // (like AIPSPATH elements), and the possibility of storing system wide // information provided by a class for reference by other classes.
          // The format of a line in a resource file is: // // # Line starting with an # in column 1 is a comment (as is an empty line) // keyword: value // keyword: value // // The keyword (starting at first non-blank) // consists in general of keyword fields separated by periods: // // printer.ps.page // measures.precession.d_interval // measures.nutation.d_interval // // and, by preference, in lower case (but // search is case sensitive) with an _ as word-parts separator.
          // The keyword and value are separated by a :. The value is the string // from the first non-whitespace character after the separator to the end of // the line. Interpretation of the string is in general the program's // responsibility, but special find() calls (see below) exist to // aid.
          // Any part of the keyword string can be replaced by a wildcard * // to indicate all values with that structure (e.g. // *.d_interval would indicate in the example above both the // precession and the nutation d_interval.
          // A match between a keyword to be found and a keyword in the resource files // will be the first match (taking wildcards into account) encountered in the // search through the resource files. // The resource files to be looked at can be defined in the environment // variable CASARCFILES. If undefined, the resource files searched are (in the // given order): // // ~/.casarc // ~/.casa/rc // ~/.aipsrc // $AIPSROOT/.aipsrc // $AIPSHOST/aipsrc // $AIPSSITE/aipsrc // $AIPSARCH/aipsrc // // It is not an error for any of the aipsrc files to be absent or empty. // However, it is an error if HOME has not been set: // an exception will occur. AIPSPATH will in general be // read from the global environment variables, but can, before any other // Aipsrc related call, be set with the // setAipsPath() call.
          // If AIPSPATH is not set in either way, it is set to the home directory. //

          // The basic interaction with the class is with the static keyword match function // Bool Aipsrc::find(String &result, const String &keyword) // // A set of // Bool AipsrcValue::find(Type &result, const String &keyword, ...) // // are available to interpret the string value found. // (see AipsrcValue).
          // All the find // functions have the ability to set a default if there is no match, // while also unit conversion is possible.
          // The Bool return indicates if the keyword was found, and, in the case of the // interpretative finds, if an 'important' format error was found (e.g. // '+12a' will be accepted as a Double, with a result of '12', since the // standard double conversion in >> will produce this result.) // The search keyword (unlike the file keyword) has no // wildcards. The real name should, of course, be looked for. // To aid in other places, the following (static) methods are available // to get the requested information (derived from HOME and // AIPSPATH, computer system information and/or aipsrc keywords): //

            //
          • const String &Aipsrc::aipsRoot() //
          • const String &Aipsrc::aipsArch() //
          • const String &Aipsrc::aipsSite() //
          • const String &Aipsrc::aipsHost() //
          • const String &Aipsrc::aipsHome() //
          // Other, numeric, system information can be found in // AipsrcValue.
          // // Given an AIPSPATH of // /epp/aips++ sun4sol_gnu epping norma // aipsSite will return // /epp/aips++/sun4sol_gnu/epping. // // The basic find above reacts with the aipsrc files available. If regular // access is necessary (e.g. a lot of routines have to check independently a // certain integration time limit), keywords can be registered to // enable: //
            //
          • fast access with integer code, rather than string //
          • ability to set values from programs if no aipsrc information given // (a dynamic default) //
          • update the $HOME/.aipsrc keyword/value list with save() //
          // The registered value is never equal to zero, hence a zero // value can be used to check if registration is done. Also, registering the // same keyword twice is safe, and will produce the same value. // When saving a keyword/value pair in $HOME/.aipsrc, the old // version is saved in $HOME/.aipsrc.old, before the keyword/value // pair is prepended to the file. A limited number of edits of the same keyword // is preserved only (default 5, changeable with the // user.aipsrc.edit.keep keyword. //
          // // // // String printerPage; // result of keyword find // if(!Aipsrc::find(printerPage, "printer.ps.page")) { // look for keyword match // printerPage = "notSet"; // }; // // A more convenient way of accomplishing the same result is: // // Aipsrc::find(printerPage, "printer.ps.page", "notSet"); // // Here the final argument is the default to use if the keyword is not found // at all.
          // If you often want to know, dynamically, the current 'printer.ps.page' // value, you could do something like: // // static uInt pp = Aipsrc::registerRC("printer.ps.page", "noSet"); // String printerPage = Aipsrc::get(pp); // // Processing, and maybe somewhere else: // Aipsrc::set(pp, "nowSet"); // // ... // printerPage = Aipsrc::get(pp); // // and save it to the $HOME/.aipsrc list // Aipsrc::save(pp); // //
          // // // Programs need a way to interact with the aipsrc files. // // // //
        181. AipsError if the environment variables HOME and/or AIPSPATH not set. // // // // class Aipsrc { public: //# Constructors //# Destructor //# Copy assignment //# Member functions // //
        182. AipsError if HOME environment variable not set // // The find() functions will, given a keyword, return the value // with a matched keyword found in the files. If no match found the // function will be False. The findNoHome() emulates the -i // switch of getrc by bypassing the ~/.aipsrc file. // static Bool find(String &value, const String &keyword); static Bool findNoHome(String &value, const String &keyword); // // These finds check a (possible) value of the keyword against a list // of coded values provided, and return an index into the list (N if not // found). Matching is minimax, case insensitive. Always better to use // the one with default. return is False if no keyword or no match. // static Bool find(uInt &value, const String &keyword, Int Nname, const String tname[]); static Bool find(uInt &value, const String &keyword, const Vector &tname); // // This find usually saves you some lines of code, since you can supply the // default you want to use when no such keyword is defined. // If the return value is False, the keyword was not found and the default // was used. // static Bool find(String &value, const String &keyword, const String &deflt); static Bool findNoHome(String &value, const String &keyword, const String &deflt); static Bool find(uInt &value, const String &keyword, Int Nname, const String tname[], const String &deflt); static Bool find(uInt &value, const String &keyword, const Vector &tname, const String &deflt); // // Sets foundDir to the first /firstPart/lastPart path that it finds // present on the system, where /firstPart comes from, in order, // this list: // contents of prepends // + useStd ? (., aipsHome(), aipsRoot()) : () // + contents of appends static Bool findDir(String& foundDir, const String& lastPart="", const Vector& prepends=Vector(), const Vector& appends=Vector(), Bool useStds=True); // Functions to register keywords for later use in get() and set(). The // returned value is the index for get() and set(). // static uInt registerRC(const String &keyword, const String &deflt); static uInt registerRC(const String &keyword, Int Nname, const String tname[], const String &deflt); static uInt registerRC(const String &keyword, const Vector &tname, const String &deflt); // // Gets are like find, but using registered integers rather than names. // static const String &get(uInt keyword); // get for code static const uInt &get(uInt &code, uInt keyword); // // Sets allow registered values to be set // static void set(uInt keyword, const String &deflt); static void set(uInt keyword, Int Nname, const String tname[], const String &deflt); static void set(uInt keyword, const Vector &tname, const String &deflt); // // Save a registered keyword value to $HOME/.aipsrc // static void save(uInt keyword); static void save(uInt keyword, const String tname[]); static void save(uInt keyword, const Vector &tname); // // Set an AIPSPATH that should be used in stead of a global AIPSPATH. // This call should be made before any Aipsrc related call. The AIPSPATH // will have up to 4 fields (which can all be empty) giving the root, host, // site and arch directory that will be searched for possible // [.]aipsrc files. static void setAipsPath(const String &path = String()); // Returns the appropriate Casacore or system variable values // static const String &aipsRoot(); static const String &aipsArch(); static const String &aipsSite(); static const String &aipsHost(); // Returns: ~/aips++ static const String &aipsHome(); // // The reRead() function will reinitialise the static maps and read // the aipsrc files again. It could be useful in some interactive circumstances. // Note: Calling reRead() while using the static maps is not (thread-)safe. // (Getting it right is a lot of work, but why apply settings while processing?) // Note: casa_measures MeasTable.cc reads its iau2000_reg and // iau2000a_reg upon first uses. Those cached values are not re-read, // but only influence what useIAU2000() and useIAU2000A() return. // // lastRead() returns the time last reRead. // static void reRead(); static Double lastRead(); // // The following functions return the full lists of available data. They could // be useful for debugging purposes. // static const Block &values(); static const Block &patterns(); // // The following show() function, useful for debugging, outputs // all keyword/value pairs found static void show(ostream &oStream); // Prints all info on cout static void show(); // The following set is a general set of functions // // Read aipsrc type files (without wildcards), and return the unique names // and values in the Vector arguments. The return value is number of names. static uInt genRestore(Vector &namlst, Vector &vallst, const String &fileList); // Save the names/values in file static void genSave(Vector &namlst, Vector &vallst, const String &fnam); // Set (new or overwrite) keyword/value pair static void genSet(Vector &namlst, Vector &vallst, const String &nam, const String &val); // Remove a keyword from list (False if not in list) static Bool genUnSet(Vector &namlst, Vector &vallst, const String &nam); // Get the value of a keyword static Bool genGet(String &val, Vector &namlst, Vector &vallst, const String &nam); // protected: // Actual find function static Bool find(String &value, const String &keyword, uInt start); // Actual find function to use during parse() without recursing into parse() static Bool findNoParse(String &value, const String &keyword, uInt start); // The registration function static uInt registerRC(const String &keyword, Block &nlst); // Actual saving static void save(const String keyword, const String val); private: //# Data // Object to ensure safe multi-threaded lazy single initialization static std::once_flag theirCallOnceFlag; // Last time data was (re)read static Double lastParse; // List of values belonging to keywords found static Block keywordValue; // List of patterns deducted from names static Block keywordPattern; // The start of the non-home values static uInt fileEnd; // The possibly set external AIPSPATH static String extAipsPath; // AIPSROOT static String root; // AIPSARCH static String arch; // AIPSSITE static String site; // AIPSHOST static String host; // AIPSHOME static String home; // HOME static String uhome; // Indicate above filled static Bool filled; // String register list // static Block strlst; static Block nstrlst; static Block codlst; static Block ncodlst; // //# General member functions // Read in the aipsrc files. Always called using theirCallOnce (except for reRead()). // static void parse(); static void doParse(String &fileList); // // The following parse function can be used for any list of files. It will // return the list of Patterns and values found, and the last keyword number // of first file in list. static uInt genParse(Block &keywordPattern, Block &keywordValue, uInt &fileEnd, const String &fileList); // Locate the right keyword in the static maps static Bool matchKeyword(uInt &where, const String &keyword, uInt start); // Fill in root, arch, site, host and home static void fillAips(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/System/AipsrcBool.cc000066400000000000000000000061751476623553700200740ustar00rootroot00000000000000//# AipsrcBool.cc: Specialisation for AipsrcValue //# Copyright (C) 1995,1996,1997,1998,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Data AipsrcValue AipsrcValue::myp_p; std::mutex AipsrcValue::theirMutex; //# Constructor AipsrcValue::AipsrcValue() : tlst(0), ntlst(0) {} //# Destructor AipsrcValue::~AipsrcValue() {} Bool AipsrcValue::find(Bool &value, const String &keyword) { String res; Bool x = Aipsrc::find(res, keyword, 0); if (x) value = (res.size() > 0 && (res[0]=='t' || res[0]=='T' || res[0]=='y' || res[0]=='Y' || (res[0]>='1' && res[0]<='9'))); return x; } Bool AipsrcValue::find(Bool &value, const String &keyword, const Bool &deflt) { return (find(value, keyword) ? True : (value = deflt, False)); } uInt AipsrcValue::registerRC(const String &keyword, const Bool &deflt) { std::lock_guard lock(theirMutex); uInt n = Aipsrc::registerRC(keyword, myp_p.ntlst); myp_p.tlst.resize(n); find ((myp_p.tlst)[n-1], keyword, deflt); return n; } const Bool &AipsrcValue::get(uInt keyword) { std::lock_guard lock(theirMutex); AlwaysAssert(keyword > 0 && keyword <= myp_p.tlst.nelements(), AipsError); return (myp_p.tlst)[keyword-1]; } void AipsrcValue::set(uInt keyword, const Bool &deflt) { std::lock_guard lock(theirMutex); AlwaysAssert(keyword > 0 && keyword <= myp_p.tlst.nelements(), AipsError); (myp_p.tlst)[keyword-1] = deflt; } void AipsrcValue::save(uInt keyword) { std::lock_guard lock(theirMutex); AlwaysAssert(keyword > 0 && keyword <= myp_p.tlst.nelements(), AipsError); ostringstream oss; if ((myp_p.tlst)[keyword-1]) { oss << "true"; } else { oss << "false"; } Aipsrc::save((myp_p.ntlst)[keyword-1], String(oss)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/System/AipsrcVBool.cc000066400000000000000000000072151476623553700202160ustar00rootroot00000000000000//# AipsrcVBool.cc: Specialisation for AipsrcVector //# Copyright (C) 1995,1996,1997,1998,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Data AipsrcVector AipsrcVector::myp_p; std::mutex AipsrcVector::theirMutex; //# Constructor AipsrcVector::AipsrcVector() : tlst(0), ntlst(0) {} //# Destructor AipsrcVector::~AipsrcVector() {} Bool AipsrcVector::find(Vector &value, const String &keyword) { String res; Bool x = Aipsrc::find(res, keyword, 0); if (x) { const Regex ws("[ ]+"); const Regex tTrue("^([tT]|[yY]|[1-9])"); res.gsub(ws, " "); Int m = res.freq(" ") +1; String *nres = new String[m]; m = split(res, nres, m, " "); value = Vector(m); for (Int i=0; i::find(Vector &value, const String &keyword, const Vector &deflt) { return (find(value, keyword) ? True : (value = deflt, False)); } uInt AipsrcVector::registerRC(const String &keyword, const Vector &deflt) { std::lock_guard lock(theirMutex); uInt n = Aipsrc::registerRC(keyword, myp_p.ntlst); myp_p.tlst.resize(n); find ((myp_p.tlst)[n-1], keyword, deflt); return n; } const Vector &AipsrcVector::get(uInt keyword) { std::lock_guard lock(theirMutex); AlwaysAssert(keyword > 0 && keyword <= myp_p.tlst.nelements(), AipsError); return (myp_p.tlst)[keyword-1]; } void AipsrcVector::set(uInt keyword, const Vector &deflt) { std::lock_guard lock(theirMutex); AlwaysAssert(keyword > 0 && keyword <= myp_p.tlst.nelements(), AipsError); (myp_p.tlst)[keyword-1].resize(deflt.nelements()); (myp_p.tlst)[keyword-1] = deflt; } void AipsrcVector::save(uInt keyword) { std::lock_guard lock(theirMutex); AlwaysAssert(keyword > 0 && keyword <= myp_p.tlst.nelements(), AipsError); ostringstream oss; Int n = ((myp_p.tlst)[keyword-1]).nelements(); for (Int i=0; i //# Copyright (C) 1995,1996,1997,1998,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Data AipsrcVector AipsrcVector::myp_p; std::mutex AipsrcVector::theirMutex; //# Constructor AipsrcVector::AipsrcVector() : tlst(0), ntlst(0) {} //# Destructor AipsrcVector::~AipsrcVector() {} Bool AipsrcVector::find(Vector &value, const String &keyword) { String res; Bool x = Aipsrc::find(res, keyword, 0); if (x) { const Regex ws("[ ]+"); res.gsub(ws, " "); Int m = res.freq(" ") +1; String *nres = new String[m]; m = split(res, nres, m, " "); value = Vector(m); for (Int i=0; i::find(Vector &value, const String &keyword, const Vector &deflt) { return (find(value, keyword) ? True : (value = deflt, False)); } uInt AipsrcVector::registerRC(const String &keyword, const Vector &deflt) { std::lock_guard lock(theirMutex); uInt n = Aipsrc::registerRC(keyword, myp_p.ntlst); myp_p.tlst.resize(n); find ((myp_p.tlst)[n-1], keyword, deflt); return n; } const Vector &AipsrcVector::get(uInt keyword) { std::lock_guard lock(theirMutex); AlwaysAssert(keyword > 0 && keyword <= myp_p.tlst.nelements(), AipsError); return (myp_p.tlst)[keyword-1]; } void AipsrcVector::set(uInt keyword, const Vector &deflt) { std::lock_guard lock(theirMutex); AlwaysAssert(keyword > 0 && keyword <= myp_p.tlst.nelements(), AipsError); (myp_p.tlst)[keyword-1].resize(deflt.nelements()); (myp_p.tlst)[keyword-1] = deflt; } void AipsrcVector::save(uInt keyword) { std::lock_guard lock(theirMutex); AlwaysAssert(keyword > 0 && keyword <= myp_p.tlst.nelements(), AipsError); ostringstream oss; Int n = ((myp_p.tlst)[keyword-1]).nelements(); for (Int i=0; i #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations class Unit; // Class to read values from the Aipsrc general resource files // // // // // //
        183. Aipsrc // // // // A class for getting values from the Aipsrc files // // // // The static AipsrcValue class can get typed values from the Aipsrc // resource files.
          // The basic interaction with the class is with the static keyword match // functions: // // Bool AipsrcValue::find(Type &result, const String &keyword) // Bool AipsrcValue::find(Type &result, const String &keyword, // const Type &deflt) // // comparable to the standard (String) Aipsrc // find.
          // If the resource file contains a multi-valued keyword, use the // AipsrcVector class instead. // // The class is templated. For ease of use typedefs are provided for: // // AipsrcDouble, AipsrcInt, AipsrcBool, AipsrcString // AipsrcVDouble, AipsrcVInt, AipsrcVBool, AipsrcVString // // In addition to the above finds, special finds: // // Bool AipsrcValue::find(Type &result, const String &keyword, // const Unit &defun, const Unit &resun) // Bool AipsrcValue::find(Type &result, const String &keyword, // const Unit &defun, const Unit &resun, // const Type &deflt) // // are provided. These finds will read the keyword value as a Quantity. // If no units are given, the defun are assumed. The result is converted // to the resun, before the value is returned. E.g. // // Double x; // find(x, "time.offset", "h", "d"); // // will return: //
            //
          • 2.5/24 for a value specified as 2.5 in resource file //
          • 2.5/24 for 2:30:00 //
          • 0.5/24 for 30min //
          • 0.5 for 0.5d //
          // // The class has registerRC, get, set functions as described in // Aipsrc. Note that registration is on a // per Type basis, and hence registration of the same keyword in different // types (and possible sets) act on different values, but with the same // result if no set has been done. // // Specialisation exists for Bool, where True is // any value string starting with one of 'yYtT123456789', and False in // all other cases, and no finds with Units are provided. Strings are // supposed to be handled by standard Aipsrc // class for single values, and a specialisation exists for the // AipsrcVector case. // //
          // // // // String tzoff; // result of keyword find // if (!AipsrcValue::find(tzoff, "time.zone.offset")) { // look for key // tzoff = -5; // }; // // A more convenient way of accomplishing the same result is: // // AipsrcDouble::find(tzoff, "time.zone.offset", -5); // // or even: // // AipsrcDouble::find(tzoff, "time.zone.offset", // "h", "h", -5); // // Here the final argument is the default to use if the keyword is not found // at all. // // // // //
        184. All types with a >> defined. // // Since interpretation of the keyword value string is done with the standard // input right-shift operator, specialisations are necessary for non-standard // cases like Bool. They are provided. String is supposed to be handled by // standard Aipsrc. // // // // // Programs need a way to interact with the AipsrcValue files. // // // //
        185. AipsError if the environment variables HOME and/or AIPSPATH not set. // // // // template class AipsrcValue : public Aipsrc { public: //# Constructors // Default constructor // // A constructor (and destructor) have been provided to be able to generate // a (routine-level) static register list. This had to be done since // static data members are not yet implemented in the gcc compiler for // templated classes. Once they are available the tlist and // ntlst data can become static, constructor and desctructor and // all references to the init() method can disappear. // AipsrcValue(); //# Destructor // See note with constructor ~AipsrcValue(); //# Member functions // The find() functions will, given a keyword, return the value // of a matched keyword found in the files. If no match found the // function will be False, and the default returned if specified. // static Bool find(T &value, const String &keyword); static Bool find(T &value, const String &keyword, const T &deflt); // // These find() functions will, given a keyword, read the value // of a matched keyword as a Quantity. If no unit has been given in the // keyword value, the defun Unit will be assumed. The value returned // will be converted to the resun Unit. If no match found, the default // value is returned (see example above). // static Bool find(T &value, const String &keyword, const Unit &defun, const Unit &resun); static Bool find(T &value, const String &keyword, const Unit &defun, const Unit &resun, const T &deflt); // // Functions to register keywords for later use in get() and set(). The // returned value is the index for get() and set(). // static uInt registerRC(const String &keyword, const T &deflt); static uInt registerRC(const String &keyword, const Unit &defun, const Unit &resun, const T &deflt); // // Gets are like find, but using registered integers rather than names. The // aipsrc file is read only once, and values can be set as well. // static const T &get(uInt keyword); // // Sets allow registered values to be set // static void set(uInt keyword, const T &deflt); // // Save registered value to $HOME/.aipsrc static void save(uInt keyword); private: //# Data // The global AipsrcValue object static AipsrcValue myp_p; static std::mutex theirMutex; // Register list // Block tlst; Block ntlst; // //# Constructors // Copy constructor (not implemented) AipsrcValue &operator=(const AipsrcValue &other); //# Copy assignment (not implemented) AipsrcValue(const AipsrcValue &other); //# General member functions }; template <> Bool AipsrcValue::find(String &value, const String &keyword, const Unit &defun, const Unit &resun); // Specialization of AipsrcValue for Bool // // template <> class AipsrcValue : public Aipsrc { public: AipsrcValue(); ~AipsrcValue(); static Bool find(Bool &value, const String &keyword); static Bool find(Bool &value, const String &keyword, const Bool &deflt); static uInt registerRC(const String &keyword, const Bool &deflt); static const Bool &get(uInt keyword); static void set(uInt keyword, const Bool &deflt); static void save(uInt keyword); private: static AipsrcValue myp_p; static std::mutex theirMutex; Block tlst; Block ntlst; AipsrcValue &operator=(const AipsrcValue &other); AipsrcValue(const AipsrcValue &other); }; //# Declare extern templates for often used types. extern template class AipsrcValue; extern template class AipsrcValue; extern template class AipsrcValue; extern template class AipsrcValue; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/casa/System/AipsrcValue.tcc000066400000000000000000000104241476623553700204310ustar00rootroot00000000000000//# AipsrcValue.cc: Class to read values from the Aipsrc general resource files //# Copyright (C) 1995,1996,1997,1998,1999,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_AIPSRCVALUE_TCC #define CASA_AIPSRCVALUE_TCC //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Data template AipsrcValue AipsrcValue::myp_p; template std::mutex AipsrcValue::theirMutex; //# Constructor template AipsrcValue::AipsrcValue() : tlst(0), ntlst(0) {} //# Destructor template AipsrcValue::~AipsrcValue() {} template Bool AipsrcValue::find(T &value, const String &keyword) { String res; Bool x = Aipsrc::find(res, keyword, 0); if (x) { istringstream instr(res); instr >> value; } return x; } template Bool AipsrcValue::find(T &value, const String &keyword, const T &deflt) { return (find(value, keyword) ? True : (value = deflt, False)); } template Bool AipsrcValue::find(T &value, const String &keyword, const Unit &defun, const Unit &resun) { String res; Bool x = Aipsrc::find(res, keyword, 0); if (x) { Quantum qres; istringstream instr(res); instr >> qres; if (qres.check(UnitVal::NODIM)) qres.setUnit(defun); value = (T) qres.getValue(resun); } return x; } template Bool AipsrcValue::find(T &value, const String &keyword, const Unit &defun, const Unit &resun, const T &deflt) { return (find(value, keyword, defun, resun) ? True : (value = deflt, False)); } template uInt AipsrcValue::registerRC(const String &keyword, const T &deflt) { std::lock_guard lock(theirMutex); uInt n = Aipsrc::registerRC(keyword, myp_p.ntlst); myp_p.tlst.resize(n); find ((myp_p.tlst)[n-1], keyword, deflt); return n; } template uInt AipsrcValue::registerRC(const String &keyword, const Unit &defun, const Unit &resun, const T &deflt) { std::lock_guard lock(theirMutex); uInt n = Aipsrc::registerRC(keyword, myp_p.ntlst); myp_p.tlst.resize(n); find ((myp_p.tlst)[n-1], keyword, defun, resun, deflt); return n; } template const T &AipsrcValue::get(uInt keyword) { std::lock_guard lock(theirMutex); AlwaysAssert(keyword > 0 && keyword <= myp_p.tlst.nelements(), AipsError); return (myp_p.tlst)[keyword-1]; } template void AipsrcValue::set(uInt keyword, const T &deflt) { std::lock_guard lock(theirMutex); AlwaysAssert(keyword > 0 && keyword <= myp_p.tlst.nelements(), AipsError); (myp_p.tlst)[keyword-1] = deflt; } template void AipsrcValue::save(uInt keyword) { ostringstream oss; { std::lock_guard lock(theirMutex); AlwaysAssert(keyword > 0 && keyword <= myp_p.tlst.nelements(), AipsError); oss << (myp_p.tlst)[keyword-1]; } // Unlock has to be done before save, because MVTime uses AipsrcValue. Aipsrc::save((myp_p.ntlst)[keyword-1], String(oss)); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/System/AipsrcValue2.cc000066400000000000000000000036671476623553700203420ustar00rootroot00000000000000//# AipsrcValue2.cc: Class to read values from the Aipsrc general resource files //# Copyright (C) 1995,1996,1997,1998,1999,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Instantiate extern templates for often used types. template class AipsrcValue; template class AipsrcValue; template class AipsrcValue; template class AipsrcValue; template <> Bool AipsrcValue::find(String &value, const String &keyword, const Unit &, const Unit &) { String res; Bool x = Aipsrc::find(res, keyword, 0); value = res; return x; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/System/AipsrcVector.h000066400000000000000000000166241476623553700203050ustar00rootroot00000000000000//# AipsrcVector.h: Read multiple values from the Aipsrc resource files //# Copyright (C) 1995,1996,1997,1999,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_AIPSRCVECTOR_H #define CASA_AIPSRCVECTOR_H #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations class Unit; // Read multiple values from the Aipsrc resource files // // // // //
        186. AipsrcValue // // // // A class for getting multiple values from the Aipsrc files // // // // The available functions (and notes) are the same as in // AipsrcValue, but with a Vector result. // // // //
        187. All types with a >> defined. // // Since interpretation of the keyword value string is done with the standard // input right-shift operator, specialisations are necessary for non-standard // cases like Bool and String. They are provided. // // // // // // // // Programs need a way to get multi-valued keywords from the Aipsrc files. // // // //
        188. AipsError if the environment variables HOME and/or AIPSPATH not set. // // // // template class AipsrcVector : public Aipsrc { public: //# Constructors // Default constructor // See a note in AipsrcValue. AipsrcVector(); //# Destructor ~AipsrcVector(); //# Member functions // The find() functions will, given a keyword, return the value // of a matched keyword found in the files. If no match found the // function will be False, and the default returned if specified. // static Bool find(Vector &value, const String &keyword); static Bool find(Vector &value, const String &keyword, const Vector &deflt); // // These find() functions will, given a keyword, read the values // of a matched keyword as a Quantity. If no unit has been given in the // keyword value, the defun Unit will be assumed. The value returned // will be converted to the resun Unit. If no match found, the default // value is returned (see example above). // static Bool find(Vector &value, const String &keyword, const Unit &defun, const Unit &resun); static Bool find(Vector &value, const String &keyword, const Unit &defun, const Unit &resun, const Vector &deflt); // // Functions to register keywords for later use in get() and set(). The // returned value is the index for get() and set(). // static uInt registerRC(const String &keyword, const Vector &deflt); static uInt registerRC(const String &keyword, const Unit &defun, const Unit &resun, const Vector &deflt); // // Gets are like find, but using registered integers rather than names. // static const Vector &get(uInt keyword); // // Sets allow registered values to be set // static void set(uInt keyword, const Vector &deflt); // // Save registered value to $HOME/.aipsrc static void save(uInt keyword); private: //# Data static AipsrcVector myp_p; static std::mutex theirMutex; // register list // Block > tlst; Block ntlst; // //# Constructors // Copy constructor (not implemented) AipsrcVector &operator=(const AipsrcVector &other); //# Copy assignment (not implemented) AipsrcVector(const AipsrcVector &other); //# General member functions }; #define AipsrcVector_String AipsrcVector // Specialization of AipsrcVector for String // // // The name AipsrcVector_String is only for cxx2html // documentation problems. Use AipsrcVector in your code. // template <> class AipsrcVector_String : public Aipsrc { public: AipsrcVector_String(); ~AipsrcVector_String(); static Bool find(Vector &value, const String &keyword); static Bool find(Vector &value, const String &keyword, const Vector &deflt); static uInt registerRC(const String &keyword, const Vector &deflt); static const Vector &get(uInt keyword); static void set(uInt keyword, const Vector &deflt); static void save(uInt keyword); private: static AipsrcVector_String myp_p; static std::mutex theirMutex; Block > tlst; Block ntlst; AipsrcVector_String &operator=(const AipsrcVector_String &other); AipsrcVector_String(const AipsrcVector_String &other); }; #undef AipsrcVector_String #define AipsrcVector_Bool AipsrcVector // Specialization of AipsrcVector for Bool // // // The name AipsrcVector_Bool is only for cxx2html // documentation problems. Use AipsrcVector in your code. // template <> class AipsrcVector_Bool : public Aipsrc { public: AipsrcVector_Bool(); ~AipsrcVector_Bool(); static Bool find(Vector &value, const String &keyword); static Bool find(Vector &value, const String &keyword, const Vector &deflt); static uInt registerRC(const String &keyword, const Vector &deflt); static const Vector &get(uInt keyword); static void set(uInt keyword, const Vector &deflt); static void save(uInt keyword); private: static AipsrcVector_Bool myp_p; static std::mutex theirMutex; Block > tlst; Block ntlst; AipsrcVector_Bool &operator=(const AipsrcVector_Bool &other); AipsrcVector_Bool(const AipsrcVector_Bool &other); }; #undef AipsrcVector_Bool } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/casa/System/AipsrcVector.tcc000066400000000000000000000112421476623553700206160ustar00rootroot00000000000000//# AipsrcVector.cc: Read multiple values from the Aipsrc resource files //# Copyright (C) 1995,1996,1997,1998,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_AIPSRCVECTOR_TCC #define CASA_AIPSRCVECTOR_TCC //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Data template AipsrcVector AipsrcVector::myp_p; //# Constructor template AipsrcVector::AipsrcVector() : tlst(0), ntlst(0) {} //# Destructor template AipsrcVector::~AipsrcVector() {} template Bool AipsrcVector::find(Vector &value, const String &keyword) { String res; Bool x = Aipsrc::find(res, keyword, 0); if (x) { const Regex ws("[ ]+"); res.gsub(ws, " "); Int m = res.freq(" ") +1; String *nres = new String[m]; m = split(res, nres, m, " "); value = Vector(m); for (Int i=0; i> value(i); } delete [] nres; } return x; } template Bool AipsrcVector::find(Vector &value, const String &keyword, const Vector &deflt) { return (find(value, keyword) ? True : (value = deflt, False)); } template Bool AipsrcVector::find(Vector &value, const String &keyword, const Unit &defun, const Unit &resun) { String res; Bool x = Aipsrc::find(res, keyword, 0); if (x) { const Regex ws("[ ]+"); res.gsub(ws, " "); Int m = res.freq(" ") +1; String *nres = new String[m]; m = split(res, nres, m, " "); value = Vector(m); Quantum qres; for (Int i=0; i> qres; if (qres.check(UnitVal::NODIM)) qres.setUnit(defun); value(i) = (T) qres.getValue(resun); } delete [] nres; } return x; } template Bool AipsrcVector::find(Vector &value, const String &keyword, const Unit&, const Unit&, const Vector &deflt) { return (find(value, keyword) ? True : (value = deflt, False)); } template uInt AipsrcVector::registerRC(const String &keyword, const Vector &deflt) { uInt n = Aipsrc::registerRC(keyword, myp_p.ntlst); myp_p.tlst.resize(n); find ((myp_p.tlst)[n-1], keyword, deflt); return n; } template uInt AipsrcVector::registerRC(const String &keyword, const Unit &defun, const Unit &resun, const Vector &deflt) { uInt n = Aipsrc::registerRC(keyword, myp_p.ntlst); myp_p.tlst.resize(n); find ((myp_p.tlst)[n-1], keyword, defun, resun, deflt); return n; } template const Vector &AipsrcVector::get(uInt keyword) { AlwaysAssert(keyword > 0 && keyword <= myp_p.tlst.nelements(), AipsError); return (myp_p.tlst)[keyword-1]; } template void AipsrcVector::set(uInt keyword, const Vector &deflt) { AlwaysAssert(keyword > 0 && keyword <= myp_p.tlst.nelements(), AipsError); (myp_p.tlst)[keyword-1].resize(deflt.nelements()); (myp_p.tlst)[keyword-1] = deflt; } template void AipsrcVector::save(uInt keyword) { AlwaysAssert(keyword > 0 && keyword <= myp_p.tlst.nelements(), AipsError); ostringstream oss; Int n = ((myp_p.tlst)[keyword-1]).nelements(); for (Int i=0; i #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Bool AppInfo::need_init_p = True; uInt AppInfo::tz_r = 0; Vector AppInfo::workDirectories(uInt minimumFreeSpaceInMB) { static Bool init = False; static uInt workdir = 0; if (!init) { init = True; // Default is an empty vector Vector empty; workdir = AipsrcVector::registerRC("user.directories.work", empty); } Vector workdirs(AipsrcVector::get(workdir).copy()); if (workdirs.nelements() == 0) { // We haven't been given a work directory list, so use a sensible // default. If "." exists and is writable use it, otherwise use "/tmp". Directory dir("."); if (!dir.exists() || !dir.isWritable()) { dir = Directory("/tmp"); } if (dir.exists() && dir.isWritable()) { workdirs.resize(1); workdirs(0) = dir.path().originalName(); } } // OK, elmiinate candidates (if any). Vector good(workdirs.nelements()); good = True; for (uInt i=0; i masked(workdirs, good); workdirs.resize(0); workdirs = masked.getCompressedArray(); return workdirs; } String AppInfo::workDirectory(uInt minimumFreeSpaceInMB) { static uInt count = 0; count++; Vector candidates = workDirectories(minimumFreeSpaceInMB); if (candidates.nelements() == 0) { LogIO os(LogOrigin("AppInfo", "workDirectory(uInt)", WHERE)); os << LogIO::SEVERE << "No work directory with at least " << minimumFreeSpaceInMB << "MB free can be found." << endl << "Check aipsrc variable user.directories.work." << LogIO::EXCEPTION; } return candidates((count-1) % candidates.nelements()); } String AppInfo::workFileName(uInt minimumFreeSpaceInMB, const String &filenamePrefix) { String dir = workDirectory(minimumFreeSpaceInMB); return File::newUniqueName(dir, filenamePrefix).originalName(); } void AppInfo::init() { need_init_p = False; // timezone Double tz; // Get System offset as default tz_r = AipsrcValue:: registerRC("system.time.tzoffset", "h", "d", Time::timeZoneDays()); tz = AppInfo::timeZone(); // Do the asserts at the end so that all the variables are initialized as // well as possible before throwing an exception. AlwaysAssert(tz >= -0.625 && tz <= 0.625, AipsError); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/System/AppInfo.h000066400000000000000000000135041476623553700172270ustar00rootroot00000000000000//# AppInfo.h: General information for applications //# Copyright (C) 1996,1997 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_APPINFO_H #define CASA_APPINFO_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations class String; // // General information for applications. // // // // // //
        189. Aipsrc class // // // // This class provides general information that an application might want to // know about its processing environment. This will be based either on // information coded into aipsrc variables, or // on information which can be obtained directly from some other source. For // example, the time zone will generally be obtained from the host OS, but it // can be overridden by an aipsrc variable if // necessary. // // Generally speaking, this class is provided to hide the details of how the // information is encoded into an aipsrc variables // and to avoid having to change applications if the information moves from // being coded into a variable to being deduced at runtime. // // It is expected that the information which is available from this class will // accrete with time. // // // // Further encapsulate information which is usually in aipsrc variables. // // // //
        190. AipsError if abs(timeZone()) > 0.625 // // // // class AppInfo { public: // Return a list of directory names into which the user may write data. If // minimumFreeSpace is set (>0) then only directories with at // least that much free space (in megabytes) are returned. If the aipsrc // variable user.directories.work is set, the candidate // directories are taken from that variable, otherwise the current working // directory (".") is chosen if it exists and is writeable, otherwise /tmp // is the candidate. Only one of "." and "/tmp" is chosen, not both. // // If no suitable directories are found (i.e., writable directories with // enough free space), a zero-length vector is returned. A warning is // issued to the logging system for directories which do not exist or are // not writable. static Vector workDirectories(uInt minimumFreeSpaceInMB=0); // Choose a workDirectory with at least minimumFreeSpace MB of // free space available. It uses workDirectories. If there is // more than one valid directory it arranges to choose different // directories in succession in an attempt to spread out the I/O. That is, // on the first call it will return directory1, on the second it will // return directory2, etc. in a cyclical fashion. One can imagine more // elaborate algorithms than this, however this should suffice for some // time, if not forever. // //
        191. An AipsError is thrown if no // directory with enough free space is found. // static String workDirectory(uInt minimumFreeSpaceInMB=0); // This function returns a fully qualified filename for a non-existent file // in a work directory with enough free space. That is, you can create a // temporary file with the name returned from this function. This function // calls workDirectory and then appends a unique (file does not // exist) filename. By default the prefix of temporary file name is // aipstmp_, but you can override this if you choose. // //
        192. An AipsError is thrown if no // directory with enough free space is found. // static String workFileName(uInt minimumFreeSpaceInMB=0, const String &filenamePrefix="aipstmp_"); // Return the local time zone offset in day fractions. This value has to be // added to UTC to get local time. Generally the OS supplied value will be // used, however it can be overridden with // system.time.tzoffset if necessary. static Double timeZone(); private: //# Data static Bool need_init_p; static uInt tz_r; //# Methods // Force an initialization of the AppInfo values. static void init(); }; //# Inlines inline Double AppInfo::timeZone() {if (need_init_p) init(); return AipsrcValue::get(tz_r);} } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/System/AppState.cc000066400000000000000000000050301476623553700175450ustar00rootroot00000000000000//# AppState.cc: casacore library configuration without environment variabes //# Copyright (C) 2017 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include namespace casacore { AppState *AppStateSource::user_state = 0; struct FOR_GCC_4_8_DEFECTS { std::string operator( )(std::string s, std::string dir) { if ( s.size( ) > 0 ) return s; struct stat statbuf; std::string path = dir + "/" + needle; return stat( path.c_str( ), &statbuf ) == 0 ? path : s; } std::string needle; }; std::string AppState::resolve(const std::string &subdir) const { struct stat statbuf; if ( stat( subdir.c_str( ), &statbuf ) == 0 ) return subdir; FOR_GCC_4_8_DEFECTS in_lieu_of_lambda = { subdir }; const std::list &casadata = dataPath( ); std::string result = std::accumulate( casadata.begin( ), casadata.end( ), std::string( ), in_lieu_of_lambda ); // std::string result = std::accumulate( casadata.begin( ), casadata.end( ), std::string( ), // [&](std::string s, std::string dir ) { // if ( s.size( ) > 0 ) return s; // std::string path = dir + sep + subdir; // return stat( path.c_str( ), &statbuf ) == 0 ? path : s; } ); return result.size( ) > 0 ? result : subdir; } } casacore-3.7.1/casa/System/AppState.h000066400000000000000000000116641476623553700174210ustar00rootroot00000000000000//# AppState.h: casacore library configuration without environment variabes //# Copyright (C) 2017 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_APPSTATE_H #define CASA_APPSTATE_H #include #include #include #include namespace casacore { // // Base class for application state // // // // // // This class is the base class for casacore state. Its purpose is to // allow applications initialize casacore's state without resorting to // environment variables. This is done by creating an object whose // class is derived from this base class, and then initializing the // AppStateSource with the newly created object. After initialization, // the AppStateSource takes ownership of the object. Please see the // documentation for AppStateSource for more information. // class AppState { public: // use the data path to find the filename... virtual std::string resolve(const std::string &filename) const; // get the list of directories in the data path... virtual std::list dataPath( ) const { static std::list result; return result; } // Get AppState specified directory for (IERS) measures data. // // If data is not found in the specified directory and the // specified directiory name is nonempty (size > 0), an // exception will be thrown in findTab. virtual std::string measuresDir( ) const { static std::string result; return result; } virtual bool initialized( ) const { return false; } virtual ~AppState( ) { } }; // // Allow configuration of casacore without environment variables // // // // // // This class allows packages which use casacore to configure casacore // behavior without reverting to environment variables. It is composed // primarly of static functions. An external application configures // casacore by calling the initialize(...) member function passing in // a pointer to an object which is derived from the AppState base class. // AppStateSource takes ownership of the provided pointer. // // When casacore no longer depends on compilers whose standard is older // than C++11, the raw pointers here should be changed to // unique_ptrs. The std::unique_ptr constructor is a constexpr, and it // does not throw exceptions. // // // class MyState: public casacore::AppState { // public: // MyState( ) { } // // const std::list &dataPath( ) const { // static std::list my_path; // return my_path; // } // // bool initialized( ) const { return true; } // }; // // MyState &get_my_state( ) { // if ( AppStateSource::fetch( ).initialized( ) == false ) // casacore::AppStateSource::initialize( new MyState ); // return dynamic_cast(AppStateSource::fetch( )); // } // // int main( int argc, char *argv[] ) { // MyState &state = get_my_state( ); // ... // return 0; // } // class AppStateSource { public: static void initialize(AppState *init) { static std::mutex mutex_p; std::lock_guard lock(mutex_p); if ( user_state ) delete user_state; user_state = init; } static AppState &fetch( ) { static AppState default_result; return user_state ? *user_state : default_result; } private: static AppState *user_state; AppStateSource( ) { } AppStateSource( AppStateSource const &) { } // prevent copying void operator=(AppStateSource const &) { } // prevent assignment }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/System/Casarc.cc000066400000000000000000000476321476623553700172360ustar00rootroot00000000000000//# Casarc.h: Class to read the casa general resource files //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #define USE_FLOCK 0 #define CASARC_DEBUG 0 using std::make_pair; using std::string; using std::list; using std::map; namespace casacore { bool Casarc::initialized = false; map *Casarc::rcfiles = 0; map *Casarc::filenames = 0; std::list *Casarc::rclist = 0; std::string *Casarc::default_path = 0; unsigned int CasarcCleanup::creation_count = 0; static off_t find_eol( const char *in, off_t len, off_t offset ) { off_t eoloff = offset; while ( in[eoloff] != '\n' && in[eoloff] != '\0' && eoloff < len ) ++eoloff; return eoloff; } static void copyline( char *out, int outlen, const char *in, int inlen, off_t offset=0 ) { int i = 0; for ( ; i < (outlen-1) && (i+offset) < inlen; ++i ) { if ( in[offset+i] != '\n' && in[offset+i] != '\0' ) out[i] = in[offset+i]; else break; } out[i] = '\0'; } void Casarc::setDefaultPath( const std::string &path ) { default_path = new std::string(path); } void Casarc::clearDefaultPath( ) { delete default_path; default_path = 0; } Casarc &Casarc::instance( ) { if ( initialized == false ) { startup( ); } if ( default_path ) { return instance( *default_path ); } else { const char *home = getenv("HOME"); if ( home == 0 ) return instance( "casarc" ); struct stat statbuf; char buf[2048]; snprintf( buf, sizeof(buf), "%s/.casa", home ); if ( stat( buf, &statbuf ) == 0 ) { if ( S_ISDIR(statbuf.st_mode) ) { return instance( std::string(buf) + "/rc" ); } } return instance( std::string(home) + "/.casarc" ); } } Casarc &Casarc::instance( const std::string &path ) { if ( initialized == false ) { startup( ); } map::iterator f_iter = filenames->find(path); if ( f_iter == filenames->end( ) ) { struct stat buf; if ( stat( path.c_str( ), &buf ) >= 0 ) { if ( ! S_ISREG(buf.st_mode)) { throw( "Casarc::instance, parameter is not a regular file: " + path ); } map::iterator r_iter = rcfiles->find(buf.st_ino); if ( r_iter != rcfiles->end( ) ) { // found by inode, but not by filename (two paths to the same file) filenames->insert(make_pair(path,r_iter->second)); return *(r_iter->second); } else { Casarc *ret = new Casarc(path); return *ret; } } else { Casarc *ret = new Casarc(path); return *ret; } } else { return *(f_iter->second); } } void Casarc::put( const std::string &keyword, const std::string &value ) { #if CASARC_DEBUG >= 2 fprintf( stderr, "<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>\n", getpid() ); #endif int the_lock = lock( READ ); sync( ); struct timeval tv = { 0, 0 }; gettimeofday( &tv, 0 ); std::map::iterator mapping = rcmap.find(keyword); char buf[512]; if ( mapping == rcmap.end( ) ) { rcmap.insert(make_pair(keyword,value)); int fd = lock( APPEND ); if ( mapped_file[mapped_file_size-1] != '\n' ) { strftime( buf, 512, "\n# added %F %T\n", localtime(&tv.tv_sec) ); } else { strftime( buf, 512, "# added %F %T\n", localtime(&tv.tv_sec) ); } Int lng = strlen(buf); AlwaysAssert (write(fd, buf, lng) == lng, AipsError); AlwaysAssert (write(fd, keyword.c_str(), keyword.length()) == Int(keyword.length()), AipsError); AlwaysAssert (write(fd, ": ", 2) == 2, AipsError); AlwaysAssert (write(fd, value.c_str(), value.length()) == Int(value.length()), AipsError); AlwaysAssert (write(fd, "\n", 1) == 1, AipsError); unlock( fd ); } else { mapping->second = value; std::map::iterator meta = rcmetamap.find(keyword); if ( meta == rcmetamap.end( ) ) throw( "Casarc::put, internal inconsistency between data & meta" ); strftime( buf, 512, "# modified %F %T\n", localtime(&tv.tv_sec) ); off_t start = (meta->second.time_length( ) ? meta->second.time_offset( ) : meta->second.key_offset( )); off_t end = meta->second.value_offset( ) + meta->second.value_length( ); #if CASARC_DEBUG >= 2 { fprintf( stderr, " >>old-offset>>>> start: %d, end: %d\n", start, end ); char *oldv = (char*) calloc( end - start + 10, sizeof(char) ); memcpy( oldv, &mapped_file[start], end-start ); fprintf( stderr, " >>old-value>>>>>%s<< [%d]\n", oldv, strlen(oldv) ); free( oldv ); } { char *outputbuf = (char*) malloc( (start + 1) * sizeof(char) ); memcpy(outputbuf, mapped_file, start); outputbuf[start] = '\0'; fprintf( stderr, " >>prefix>>>>>>>>%s<< [%d]\n",outputbuf, strlen(outputbuf) ); free( outputbuf ); } #endif int buflen = strlen(buf); int copylen = sizeof(char) * ( start + (buflen + keyword.length() + value.length() + 5) + (mapped_file_size - end) ); char *copy = (char*) malloc(copylen); off_t off = 0; memcpy( copy, mapped_file, start ); off += start; snprintf( ©[off], copylen-off, "%s%s: %s", buf, keyword.c_str(), value.c_str() ); #if CASARC_DEBUG >= 2 fprintf( stderr, " >>update>>>>>>>>%s<< [%d]\n", ©[off], strlen(©[off]) ); #endif off += strlen(©[off]); #if CASARC_DEBUG >= 2 int sufstart = off; #endif memcpy( ©[off], &mapped_file[end], mapped_file_size-end ); off += mapped_file_size-end; if ( copy[off-1] != '\n' ) { copy[off++] = '\n'; } copy[off] = '\0'; #if CASARC_DEBUG >= 2 fprintf( stderr, " >>suffix>>>>>>>>%s<< [%d]\n", &mapped_file[end], strlen(&mapped_file[end]) ); fprintf( stderr, " >>updated-file>>%s<< [%d]\n", copy, strlen(copy) ); #endif munmap( mapped_file, mapped_file_size ); mapped_file = 0; mapped_file_size = 0; int fd = lock( WRITE ); AlwaysAssert (write( fd, copy, off) == off, AipsError); free( copy ); unlock( fd ); } unlock( the_lock ); #if CASARC_DEBUG >= 2 fprintf( stderr, "<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>\n", getpid() ); #endif } const std::list &Casarc::list( ) { if ( rclist == 0 ) { rclist = new std::list( ); } return *rclist; } bool Casarc::get( const std::string &keyword, std::string &value ) { sync( ); std::map::iterator iter = rcmap.find(keyword); if ( iter == rcmap.end( ) ) return false; value = iter->second; return true; } std::string Casarc::get( const std::string &keyword ) { sync( ); std::map::iterator iter = rcmap.find(keyword); if ( iter == rcmap.end( ) ) return std::string(""); return iter->second; } size_t Casarc::size( ) const { return rcmap.size( ); } void Casarc::sync( ) { struct stat buf; if ( stat( filename.c_str( ), &buf ) >= 0 ) { if ( ! S_ISREG(buf.st_mode)) { throw( "Casarc::sync, not a regular file: " + filename ); } } if ( mapped_file == 0 || mapped_file_size != buf.st_size || current_modification_time(buf) != timestamp ) { #if CASARC_DEBUG >= 1 fprintf( stderr, "casarc update: %ld => ", size() ); read_file( ); fprintf( stderr, "%ld (pid:%d)\n", size(), getpid() ); #else read_file( ); #endif } } void Casarc::close( int fd ) { #if USE_FLOCK ::close( fd ); #else if ( have_lock.size( ) == 0 ) { ::close( fd ); for ( std::list::iterator iter = stale_fds.begin(); iter != stale_fds.end(); ++iter ) { ::close( *iter ); } stale_fds.clear( ); } else { stale_fds.push_back(fd); } #endif } int Casarc::lock( lock_mode mode ) { #if USE_FLOCK int flags = ( mode == READ ? O_RDONLY : #else int flags = ( mode == READ ? O_RDWR : #endif mode == READ_WRITE ? O_RDWR : mode == WRITE ? O_WRONLY | O_TRUNC : mode == APPEND ? O_APPEND | O_WRONLY : O_RDONLY ); int fd = open( filename.c_str( ), flags ); if ( fd < 0 ) { throw( "Casarc::lock: could not open " + filename ); } // (a) if process A acquires the lock and then forks() the child // should realize that it does not have the file locked... // (b) the same process can lock the file more than once... pid_t pid = getpid( ); if ( have_lock.size( ) > 0 && have_lock.front( ) == pid ) { have_lock.push_front(pid); return fd; } if ( have_lock.size( ) > 0 ) { have_lock.clear( ); } #if USE_FLOCK if ( flock( fd, LOCK_EX ) < 0 ) { throw( "Casarc::lock, failed to lock: " + filename ); } #else struct flock lock; lock.l_type = F_WRLCK; /** F_RDLCK, F_WRLCK, F_UNLCK **/ lock.l_start = 0; /** byte offset, relative to l_whence **/ lock.l_whence = SEEK_SET; /** SEEK_SET, SEEK_CUR, SEEK_END **/ lock.l_len = 0; /** bytes (0 means to EOF) **/ if ( fcntl( fd, F_SETLKW, &lock ) < 0 ) { perror("what tha...." ); throw( "Casarc::lock, failed to lock: " + filename ); } #endif have_lock.push_front(pid); return fd; } void Casarc::unlock( int fd ) { if ( have_lock.size( ) > 0 && have_lock.front( ) != getpid( ) ) { have_lock.clear( ); return; } if ( have_lock.size( ) == 0 ) return; have_lock.pop_front( ); if ( have_lock.size( ) == 0 ) { #if USE_FLOCK if ( flock( fd, LOCK_UN ) < 0 ) { throw( "Casarc::unlock, failed to unlock: " + filename ); } #else struct flock lock; lock.l_type = F_UNLCK; /** F_RDLCK, F_WRLCK, F_UNLCK **/ lock.l_start = 0; /** byte offset, relative to l_whence **/ lock.l_whence = SEEK_SET; /** SEEK_SET, SEEK_CUR, SEEK_END **/ lock.l_len = 0; /** bytes (0 means to EOF) **/ if ( fcntl( fd, F_SETLK, &lock ) < 0 ) { throw( "Casarc::unlock, failed to lock: " + filename ); } #endif } close(fd); } Casarc::iterator Casarc::begin( ) { sync( ); return rcmap.begin( ); } Casarc::iterator Casarc::end( ) { sync( ); return rcmap.end( ); } Casarc::Casarc( const std::string &path ) : mapped_file(0), mapped_file_size(0), have_lock(0), filename(path), inode(0) { struct stat buf; if ( initialized == false ) { startup( ); } if ( stat( path.c_str( ), &buf ) < 0 ) { int fd = open( path.c_str( ), O_CREAT | O_WRONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH ); if ( fd < 0 ) { throw( "Casarc, could not create: " + path ); } else { ::close(fd); if ( stat( path.c_str( ), &buf ) < 0 ) { throw( "Casarc, could not stat: " + path ); } } } if ( ! S_ISREG(buf.st_mode)) { throw( "Casarc, parameter is not a regular file: " + path ); } inode = buf.st_ino; read_file( ); rcfiles->insert(make_pair(inode,this)); filenames->insert(make_pair(path,this)); rclist->push_back(this); #if CASARC_DEBUG >= 1 fprintf( stderr, "casarc start: %ld rc files managed [", rclist->size() ); for ( std::list::iterator iter = rclist->begin( ); iter != rclist->end( ); ++iter ) { fprintf( stderr, iter == rclist->begin() ? "%ld" : ",%ld", (*iter)->size() ); } fprintf( stderr, "] (pid:%d)\n", getpid() ); #endif } double Casarc::current_modification_time( struct stat &buf ) { if ( ! S_ISREG(buf.st_mode)) { throw( "Casarc::current_modification_time: " + filename + " is not a regular file" ); } #if defined(__APPLE__) // micro seconds: 1000000 // nano seconds: 1000000000 // ...unfortunately, the resolution for modification time seems to be seconds... return (double) buf.st_mtimespec.tv_sec + (double) buf.st_mtimespec.tv_nsec / (double) 1000000000; #else // ...non-apple platforms seem upfront about the 1 sec resolution... return (double) buf.st_mtime; #endif } #define is_added_tag(PTR,OFF) \ (PTR[OFF+0] == '#' && PTR[OFF+1] == ' ' && \ PTR[OFF+2] == 'a' && PTR[OFF+3] == 'd' && PTR[OFF+4] == 'd' && PTR[OFF+5] == 'e' && PTR[OFF+6] == 'd' && PTR[OFF+7] == ' ' && \ isdigit(PTR[OFF+8]) && isdigit(PTR[OFF+9]) && isdigit(PTR[OFF+10]) && isdigit(PTR[OFF+11]) && PTR[OFF+12] == '-' && \ isdigit(PTR[OFF+13]) && isdigit(PTR[OFF+14]) && PTR[OFF+15] == '-' && isdigit(PTR[OFF+16]) && isdigit(PTR[OFF+17]) && PTR[OFF+18] == ' ' && \ isdigit(PTR[OFF+19]) && isdigit(PTR[OFF+20]) && PTR[OFF+21] == ':' && isdigit(PTR[OFF+22]) && isdigit(PTR[OFF+23]) && PTR[OFF+24] == ':' && \ isdigit(PTR[OFF+25]) && isdigit(PTR[OFF+26])) #define is_modified_tag(PTR,OFF) \ (PTR[OFF+0] == '#' && PTR[OFF+1] == ' ' && \ PTR[OFF+2] == 'm' && PTR[OFF+3] == 'o' && PTR[OFF+4] == 'd' && PTR[OFF+5] == 'i' && PTR[OFF+6] == 'f' && PTR[OFF+7] == 'i' && PTR[OFF+8] == 'e' && \ PTR[OFF+9] == 'd' && PTR[OFF+10] == ' ' && \ isdigit(PTR[OFF+11]) && isdigit(PTR[OFF+12]) && isdigit(PTR[OFF+13]) && isdigit(PTR[OFF+14]) && PTR[OFF+15] == '-' && \ isdigit(PTR[OFF+16]) && isdigit(PTR[OFF+17]) && PTR[OFF+18] == '-' && isdigit(PTR[OFF+19]) && isdigit(PTR[OFF+20]) && PTR[OFF+21] == ' ' && \ isdigit(PTR[OFF+22]) && isdigit(PTR[OFF+23]) && PTR[OFF+24] == ':' && isdigit(PTR[OFF+25]) && isdigit(PTR[OFF+26]) && PTR[OFF+27] == ':' && \ isdigit(PTR[OFF+28]) && isdigit(PTR[OFF+29])) void Casarc::read_file( ) { int fd = lock( READ ); // // stat( ) file to make sure that it is there... and is a regular file... // struct stat buf; if ( fstat( fd, &buf ) < 0 ) { throw( "Casarc::read_file: could not stat " + filename ); } else if ( ! S_ISREG(buf.st_mode)) { throw( "Casarc::read_file, parameter is not a regular file: " + filename ); } else if ( buf.st_size > (1024 * 1024 * 2) ) { throw( "Casarc::read_file, maximum file size (2M) exceeded" ); } // // with osx one cannot memory map a zero length file // -- Wed Nov 17 21:32:45 UTC 2010 // if ( buf.st_size == 0 ) { AlwaysAssert (write( fd, "\n", 1) == 1, AipsError); lseek( fd, 0, SEEK_SET ); if ( fstat( fd, &buf ) < 0 ) throw( "Casarc::read_file, internal error" ); } timestamp = current_modification_time( buf ); rcmap.erase(rcmap.begin( ),rcmap.end( )); rcmetamap.erase(rcmetamap.begin( ),rcmetamap.end( )); if ( mapped_file != 0 ) munmap( mapped_file, mapped_file_size ); mapped_file_size = buf.st_size; if ( (mapped_file = (char*) mmap( 0, mapped_file_size, PROT_READ, MAP_SHARED, fd, 0 )) == (void*) -1 ) { perror( "casarc" ); throw( "Casarc::read_file, could not memory map casarc file" ); } int line_count = 0; char *keyword, *value; int keyword_len = 64; int value_len = 64; value = (char*) malloc(sizeof(char) * value_len); keyword = (char*) malloc(sizeof(char) * keyword_len); off_t timestp = 0; off_t timestplen = 0; for ( off_t off = 0; off < mapped_file_size; ++off ) { if ( isspace(mapped_file[off]) ) { if ( mapped_file[off] == '\n' ) ++line_count; continue; } if ( mapped_file[off] == '#' ) { if ( is_added_tag(mapped_file,off) || is_modified_tag(mapped_file,off) ) { timestp = off; timestplen = find_eol( mapped_file, mapped_file_size, timestp ) - timestp; } else { timestp = 0; timestplen = 0; } off = find_eol(mapped_file, mapped_file_size, off); continue; } off_t keystart = off; int keylen = 0; for ( ; ! isspace(mapped_file[keystart+keylen]) && mapped_file[keystart+keylen] != ':' && (keystart+keylen) < mapped_file_size; ++keylen ) {} if ( keylen == 0 || mapped_file[keystart+keylen] == '\n' ) { char obuf[21]; copyline( obuf, 21, mapped_file, mapped_file_size, off ); fprintf( stderr, "casarc error: ignoring malformed line %u: %s [file:%s] (pid:%d)\n", line_count, obuf, path().c_str( ), getpid() ); off = find_eol(mapped_file, mapped_file_size, off); continue; } if ( keyword_len < (keylen + 1) ) { while ( keyword_len < (keylen + 1) ) keyword_len *= 2; keyword = (char *) realloc( keyword, keyword_len ); } memcpy(keyword, &mapped_file[keystart], keylen); keyword[keylen] = '\0'; off_t valstart = off + keylen; int vallen = 0; while ( isspace(mapped_file[valstart]) && mapped_file[valstart] != '\n' && valstart < mapped_file_size ) ++valstart; if ( mapped_file[valstart] != ':' ) { char obuf[21]; copyline( obuf, 21, mapped_file, mapped_file_size, off ); fprintf( stderr, "casarc error: ignoring malformed line %u: %s [file:%s] (pid:%d) \n", line_count, obuf, path().c_str( ), getpid() ); off = find_eol(mapped_file, mapped_file_size, off); continue; // raise exception, return an error? // ...no, we have to carry on... } ++valstart; // skip ':' while ( isspace(mapped_file[valstart]) && mapped_file[valstart] != '\n' && valstart < mapped_file_size ) ++valstart; while ( mapped_file[valstart+vallen] != '\n' && (valstart+vallen) < mapped_file_size ) ++vallen; while ( vallen > 0 && isspace(mapped_file[valstart+vallen]) ) --vallen; if ( (valstart+vallen) < mapped_file_size && isspace(mapped_file[valstart+vallen+1]) ) ++vallen; // move to byte beyond non-space values if ( value_len < (vallen + 1) ) { while ( value_len < (vallen + 1) ) value_len *= 2; value = (char *) realloc( value, value_len ); } memcpy(value, &mapped_file[valstart], vallen); value[vallen] = '\0'; off = valstart+vallen; rcmap.insert(make_pair(string((const char*)keyword),string((const char*)value))); rcmetamap.insert(make_pair(string((const char*)keyword),meta_entry_(keystart,keylen,valstart,vallen,timestp,timestplen))); timestp = 0; timestplen = 0; } free(keyword); free(value); unlock( fd ); } void Casarc::startup( ) { if ( initialized == false ) { initialized = true; rcfiles = new std::map( ); filenames = new std::map( ); rclist = new std::list( ); } } void Casarc::shutdown( ) { if ( initialized ) { #if CASARC_DEBUG >= 1 fprintf( stderr, "casarc halt: %ld rc files managed [", rclist->size() ); for ( std::list::iterator iter = rclist->begin( ); iter != rclist->end( ); ++iter ) { fprintf( stderr, iter == rclist->begin() ? "%ld" : ",%ld", (*iter)->size() ); } fprintf( stderr, "] (pid:%d)\n", getpid() ); #endif initialized = false; for ( std::list::iterator iter = rclist->begin( ); iter != rclist->end( ); ++iter ) { delete *iter; } delete rcfiles; rcfiles = 0; delete filenames; filenames = 0; delete rclist; rclist = 0; delete default_path; default_path = 0; } } } casacore-3.7.1/casa/System/Casarc.h000066400000000000000000000114101476623553700170610ustar00rootroot00000000000000//# Casarc.h: Class to read the casa general resource files //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_CASARC_H #define CASA_CASARC_H #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class Casarc { friend class CasarcCleanup; public: typedef std::map::const_iterator iterator; // return default casarc file, e.g. ~/.casarc static Casarc &instance( ); // set and clear the default casarc path (to something other than ~/.casarc) static void setDefaultPath( const std::string &path ); static void clearDefaultPath( ); // return a casarc object attached to a particular file static Casarc &instance( const std::string &path ); // return the list of rcfiles that have been loaded static const std::list & list( ); // adds the keyword->value mapping void put( const std::string &keyword, const std::string &value ); // retrieves the mapping for keyword (or "" if the mapping is not defined) std::string get( const std::string &keyword ); // retrieves the mapping for keyword in value (and returns true if the // mapping is defined or false otherwise) bool get( const std::string &keyword, std::string &value ); size_t size( ) const; // path to the file that this Casarc mirrors... const std::string &path( ) const { return filename; } iterator begin( ); iterator end( ); private: class meta_entry_ { public: meta_entry_( off_t keys_, int keyl_, off_t vals_, int vall_, off_t times_, int timel_ ) : key_offset_(keys_), key_len_(keyl_), val_offset_(vals_), val_len_(vall_), time_offset_(times_), time_len_(timel_) { } off_t key_offset( ) const { return key_offset_; } int key_length( ) const { return key_len_; } off_t value_offset( ) const { return val_offset_; } int value_length( ) const { return val_len_; } off_t time_offset( ) const { return time_offset_; } int time_length( ) const { return time_len_; } private: off_t key_offset_; int key_len_; off_t val_offset_; int val_len_; off_t time_offset_; int time_len_; }; char *mapped_file; off_t mapped_file_size; std::list stale_fds; void close( int ); std::list have_lock; enum lock_mode { READ, READ_WRITE, WRITE, APPEND }; // returns a file descriptor or -1 on error... int lock( lock_mode mode ); void unlock( int fd ); void sync( ); double current_modification_time( struct stat &buf ); std::string filename; double timestamp; std::map rcmap; // this is broken off separate from the rcmap to allow the // rcmap to be used for iteration by users of this class... std::map rcmetamap; void read_file( ); ino_t inode; // singleton Casarcs, (inode->casarc) static std::map *rcfiles; static std::map *filenames; static std::list *rclist; static std::string *default_path; // path is the path to the .casarc (or .aipsrc file) Casarc( const std::string &path ); static bool initialized; static void startup( ); static void shutdown( ); ~Casarc( ) { } }; static class CasarcCleanup { public: CasarcCleanup( ) { creation_count += 1; } ~CasarcCleanup( ) { if ( --creation_count == 0 ) { Casarc::shutdown( ); } } private: static unsigned int creation_count; } local_cleanup_object; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/System/Choice.cc000066400000000000000000000051161476623553700172230ustar00rootroot00000000000000//# Choice.cc: Ask a choice to the user //# Copyright (C) 2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Default is no choice function, thus return first choice. Choice::ChoiceFunc* Choice::theirChoiceFunc = 0; String Choice::choice (const String& descriptiveText, const Vector& choices) { if (choices.nelements() == 0) { return ""; } if (theirChoiceFunc == 0) { return choices[0]; } return theirChoiceFunc (descriptiveText, choices); } Choice::ChoiceFunc* Choice::setChoiceFunc (Choice::ChoiceFunc* func) { Choice::ChoiceFunc* tmp = theirChoiceFunc; theirChoiceFunc = func; return tmp; } String Choice::ostreamChoice (std::ostream& os, const String& descriptiveText, const Vector& choices) { if (choices.nelements() == 0) { return ""; } char answer[256]; while (True) { os << descriptiveText << " ([" << choices(0) << ']'; for (uInt i=1; i #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Class to ask a user a choice // // // // // // // // // class Choice { public: // Define the signature of the choice function. typedef String ChoiceFunc (const String& descriptiveText, const Vector& choices); // Get a choice from the user. // The choice function to be used can be set using setChoiceFunc. // It can, for instance, be done by ObjectController. // Initially no choice function is set. In that case it returns // the first choice (so that should be the default choice). // If choices is zero length, an empty string is returned. static String choice (const String& descriptiveText, const Vector& choices); // Set the choice function. // It returns the old choice function. static ChoiceFunc* setChoiceFunc (ChoiceFunc* func); // A choice function asking on stderr. static String stderrChoice (const String& descriptiveText, const Vector& choices) { return ostreamChoice (std::cerr, descriptiveText, choices); } // A choice function asking on stdout. // It outputs the descriptiveText followed by a blank, the options (enclosed // in parentheses) and a colon. // The default option is shown in square brackets. static String stdoutChoice (const String& descriptiveText, const Vector& choices) { return ostreamChoice (std::cout, descriptiveText, choices); } private: // Ask on an ostream. static String ostreamChoice (std::ostream&, const String& descriptiveText, const Vector& choices); //# Pointer to the choice function. static ChoiceFunc* theirChoiceFunc; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/System/ObjectID.cc000066400000000000000000000133001476623553700174460ustar00rootroot00000000000000//# : this defines , which ... //# Copyright (C) 1996,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ObjectID::ObjectID(Bool makeNull) : sequence_number_p(0), process_id_p(0), creation_time_p(0), hostname_p("") { if (! makeNull) { sequence_number_p = sequence_number(); process_id_p = HostInfo::processID(); creation_time_p = Int(HostInfo::secondsFrom1970() + 0.499); hostname_p = HostInfo::hostName(); } } ObjectID::ObjectID(Int sequence, Int pid, Int time, const String &hostname) : sequence_number_p(sequence), process_id_p(pid), creation_time_p(time), hostname_p(hostname) { // Nothing } ObjectID::ObjectID(const ObjectID &other) : sequence_number_p(other.sequence_number_p), process_id_p(other.process_id_p), creation_time_p(other.creation_time_p), hostname_p(other.hostname_p) { // Nothing } ObjectID &ObjectID::operator=(const ObjectID &other) { if (this != &other) { sequence_number_p = other.sequence_number_p; process_id_p = other.process_id_p; creation_time_p = other.creation_time_p; hostname_p = other.hostname_p; } return *this; } Bool ObjectID::isNull() const { return (sequence_number_p == 0 && process_id_p == 0 && creation_time_p == 0 && hostname_p == ""); } Bool ObjectID::operator==(const ObjectID &other) const { return (sequence_number_p == other.sequence_number_p && process_id_p == other.process_id_p && creation_time_p == other.creation_time_p && hostname_p == other.hostname_p); } Bool ObjectID::operator!=(const ObjectID &other) const { return (! (*this == other)); } Int ObjectID::sequence_number() { static int seqno = -1; seqno++; return seqno; } void ObjectID::toString(String &out) const { out = ""; if (isNull()) { return; } ostringstream os; os << "sequence=" << sequence() << " host=" << hostName() << " pid=" << pid() << " time=" << creationTime(); out = os.str(); } static Bool toInt(Int &val, String &error, const String &in) { error = ""; val = 0; Int len = in.length(); if (len == 0) { error = "No digits in number."; return False; } for (Int i=0; i 9) { error = String("Illegal character (") + digit + ") in number"; return False; } val = 10*val + diff; } return True; } Bool ObjectID::fromString(String &error, const String &in) { error = ""; *this = ObjectID(True); if (in == "") { return True; // Null string is the null object! } // Allow for extra fields. String parsed[8]; // keyword=value for each String Int found = split(in, parsed, sizeof(parsed)/sizeof(String), Regex("[ \t,]+")); if (found <= 0) { error = String("Could not parse string: ") + in; return False; } Bool foundSeq = False, foundHost = False, foundPid = False, foundTime = False; String host; Int seq, pid, time; Bool ok = True; String splitup[2]; String &key = splitup[0]; String &val = splitup[1]; for (Int i=0; ok && i #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations template class Block; // // ObjectID: A unique identifier for distributed and other objects. // // // // //
        193. none // // // // The ObjectID class name reflects its role as the single identifier for // distributed and other user-level objects. // // // // The ObjectID class is used to give a unique identifier to ``high-level'' // objects in the system. Internally the ObjectID consists of a sequence number // (unique within the creating process), a process id, a creation time, and a // host id. Pragmatically the ObjectID should be unique with no dangers of // collisions. // // A special ``Null'' ObjectID is available. // // // // The fundamental purpose for an ObjectID is to provide a unique identifier // for persistent objects, or for objects that might be accessed outside the // creating processes address space. // // // //
        194. Nothing (hostid -> hostname on this date). // class ObjectID { public: // If makeNull is True, make the null ObjectID, otherwise create // a unique ObjectID. ObjectID(Bool makeNull = False); // Create explicitly from the provided constituents. ObjectID(Int sequence, Int pid, Int time, const String &hostname); // Copy other. Note that if the ObjectID is embedded inside an // object, the enclosing object probably does not want to copy the ObjectID // since generally speaking the identity of the enclosing object should be // immutable. // ObjectID(const ObjectID &other); ObjectID &operator=(const ObjectID &other); // // Is this ObjectID set? Bool isNull() const; // Compare two ObjectID's for (in)equality. // Bool operator==(const ObjectID &other) const; Bool operator!=(const ObjectID &other) const; // // It is useful to interconvert between strings and ObjecID's, e.g. when // saving to FITS or writing to a table. The form of the string is: // // sequence=123 host=hostname pid=pid time=time // // with an optional comma between the fields. // However, in general user code should not depend on the exact form of // the string. // // If this fails, an error message is set and the ObjectID is the null // ObjectID. Bool fromString(String &error, const String &in); // Note that out is zero'd before it is set. void toString(String &out) const; // // Ordinarily the user does not need to get at the exact state of the, // ObjectID, however it is available for those times when it is necessary. // Int sequence() const; Int pid() const; Int creationTime() const; const String &hostName() const; // // Extract objectID strings (as set by glish script substitute.g) from // a command, convert them to ObjectID objects, store those in the // Block, and replace the strings by their Block indices as // $OBJ#n#O where n is the index. static String extractIDs (Block& objectIDs, const String& command); private: Int sequence_number_p; Int process_id_p; Int creation_time_p; String hostname_p; // Make a unique sequence number, returns 0 on first call, 1 on next, ... static Int sequence_number(); }; uInt hashFunc(const ObjectID &); ostream &operator<<(ostream &os, const ObjectID &id); //# Inlines inline Int ObjectID::sequence() const { return sequence_number_p; } inline Int ObjectID::pid() const { return process_id_p; } inline Int ObjectID::creationTime() const { return creation_time_p; } inline const String &ObjectID::hostName() const { return hostname_p; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/System/ObjectID2.cc000066400000000000000000000053261476623553700175410ustar00rootroot00000000000000//# ObjectID2.cc: Hash related OjectID functions. Prevent link coupling. //# Copyright (C) 1996,1999,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include // needed for snprintf namespace casacore { //# NAMESPACE CASACORE - BEGIN uInt hashFunc(const ObjectID &key) { // We should check to see if this hash is any good uInt result = 0; result |= key.sequence() & 0xff; result |= (key.pid() & 0xff) << 8; result |= (key.creationTime() & 0xff) << 16; result |= uInt(key.hostName()[0]) << 24; return result; } String ObjectID::extractIDs (Block& objectIDs, const String& command) { objectIDs.resize (0, True, True); String error; String result; String str = command; // Extract object-id from the command, convert it to an // ObjectID in the block, and put its index into the command. Int index = str.index ("'ObjectID=["); while (index >= 0) { result += str.before(index); index += 11; Int pos = str.index ("]'", index); ObjectID oid; // Convert to ObjectID. // If not succesfull, put original back. if (! oid.fromString (error, str(index, pos-index))) { result += str(index-11, pos-index+13); } else { uInt n = objectIDs.nelements() + 1; objectIDs.resize (n); objectIDs[n-1] = oid; char buf[16]; snprintf (buf, sizeof(buf), "$OBJ#%i#O", n); result += buf; str = str.after(pos+1); } index = str.index ("'ObjectID=["); } result += str; return result; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/System/PGPlotter.cc000066400000000000000000000431631476623553700177150ustar00rootroot00000000000000//# PGPlotter.cc: Standard plotting object for application programmers. //# Copyright (C) 1997,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Default is no create function, thus use createLocal. PGPlotter::CreateFunction* PGPlotter::creator_p = 0; PGPlotter::PGPlotter() { // Nothing } PGPlotter::PGPlotter(PGPlotterInterface* worker) : worker_p(worker) { // Nothing } PGPlotter::PGPlotter (const String &device, uInt mincolors, uInt maxcolors, uInt sizex, uInt sizey) { *this = create (device, mincolors, maxcolors, sizex, sizey); } PGPlotter::PGPlotter(const PGPlotter &other) : PGPlotterInterface(), worker_p(other.worker_p) { // Nothing } PGPlotter &PGPlotter::operator=(const PGPlotter &other) { worker_p = other.worker_p; return *this; } PGPlotter::~PGPlotter() { // Nothing } PGPlotter PGPlotter::create (const String &device, uInt mincolors, uInt maxcolors, uInt sizex, uInt sizey) { if (creator_p == 0) { return PGPlotterNull::createPlotter(device, mincolors, maxcolors, sizex, sizey); } return creator_p (device, mincolors, maxcolors, sizex, sizey); } PGPlotter::CreateFunction* PGPlotter::setCreateFunction (PGPlotter::CreateFunction* func, Bool override) { CreateFunction* tmp = creator_p; if (override || tmp == 0) { creator_p = func; } return tmp; } void PGPlotter::detach() { worker_p->resetPlotNumber(); // Implemented for PGPlotterGlish only std::shared_ptr empty; worker_p = empty; } Bool PGPlotter::isAttached() const { return (static_cast(worker_p)); } void PGPlotter::message(const String &text) { ok(); worker_p->message(text); } Record PGPlotter::curs(Float x, Float y) { ok(); Record retval = worker_p->curs(x, y); if (!worker_p->isAttached()) worker_p = 0; return retval; } void PGPlotter::arro(Float x1, Float y1, Float x2, Float y2) { ok(); worker_p->arro(x1, y1, x2, y2); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::ask(Bool flag) { ok(); worker_p->ask(flag); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::bbuf() { ok(); worker_p->bbuf(); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::box(const String &xopt, Float xtick, Int nxsub, const String &yopt, Float ytick, Int nysub) { ok(); worker_p->box(xopt, xtick, nxsub, yopt, ytick, nysub); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::circ(Float xcent, Float ycent, Float radius) { ok(); worker_p->circ(xcent, ycent, radius); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::draw(Float x, Float y) { ok(); worker_p->draw(x, y); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::ebuf() { ok(); worker_p->ebuf(); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::env(Float xmin, Float xmax, Float ymin, Float ymax, Int just, Int axis) { ok(); worker_p->env(xmin, xmax, ymin, ymax, just, axis); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::eras() { ok(); worker_p->eras(); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::errb(Int dir, const Vector &x, const Vector &y, const Vector &e, Float t) { ok(); worker_p->errb(dir, x, y, e, t); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::erry(const Vector &x, const Vector &y1, const Vector &y2, Float t) { ok(); worker_p->erry(x, y1, y2, t); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::hist(const Vector &data, Float datmin, Float datmax, Int nbin, Int pcflag) { ok(); worker_p->hist(data, datmin, datmax, nbin, pcflag); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::lab(const String &xlbl, const String &ylbl, const String &toplbl) { ok(); worker_p->lab(xlbl, ylbl, toplbl); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::line(const Vector &xpts, const Vector &ypts) { ok(); worker_p->line(xpts, ypts); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::move(Float x, Float y) { ok(); worker_p->move(x, y); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::mtxt(const String &side, Float disp, Float coord, Float fjust, const String &text) { ok(); worker_p->mtxt(side, disp, coord, fjust, text); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::page() { ok(); worker_p->page(); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::poly(const Vector &xpts, const Vector &ypts) { ok(); worker_p->poly(xpts, ypts); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::pt(const Vector &xpts, const Vector &ypts, Int symbol) { ok(); worker_p->pt(xpts, ypts, symbol); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::ptxt(Float x, Float y, Float angle, Float fjust, const String &text) { ok(); worker_p->ptxt(x, y, angle, fjust, text); if (!worker_p->isAttached()) worker_p = 0; } Int PGPlotter::qci() { ok(); Int retval = worker_p->qci(); if (!worker_p->isAttached()) worker_p = 0; return retval; } Int PGPlotter::qtbg() { ok(); Int retval = worker_p->qtbg(); if (!worker_p->isAttached()) worker_p = 0; return retval; } Vector PGPlotter::qtxt(Float x, Float y, Float angle, Float fjust, const String &text) { ok(); Vector retval = worker_p->qtxt(x, y, angle, fjust, text); if (!worker_p->isAttached()) worker_p = 0; return retval; } Vector PGPlotter::qwin() { ok(); Vector retval = worker_p->qwin(); if (!worker_p->isAttached()) worker_p = 0; return retval; } void PGPlotter::rect(Float x1, Float x2, Float y1, Float y2) { ok(); worker_p->rect(x1, x2, y1, y2); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::sah(Int fs, Float angle, Float vent) { ok(); worker_p->sah(fs, angle, vent); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::save() { ok(); worker_p->save(); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::sch(Float size) { ok(); worker_p->sch(size); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::sci(Int ci) { ok(); worker_p->sci(ci); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::scr(Int ci, Float cr, Float cg, Float cb) { ok(); worker_p->scr(ci, cr, cg, cb); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::sfs(Int fs) { ok(); worker_p->sfs(fs); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::sls(Int ls) { ok(); worker_p->sls(ls); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::slw(Int lw) { ok(); worker_p->slw(lw); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::stbg(Int tbci) { ok(); worker_p->stbg(tbci); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::subp(Int nxsub, Int nysub) { ok(); worker_p->subp(nxsub, nysub); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::svp(Float xleft, Float xright, Float ybot, Float ytop) { ok(); worker_p->svp(xleft, xright, ybot, ytop); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::swin(Float x1, Float x2, Float y1, Float y2) { ok(); worker_p->swin(x1, x2, y1, y2); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::tbox(const String &xopt, Float xtick, Int nxsub, const String &yopt, Float ytick, Int nysub) { ok(); worker_p->tbox(xopt, xtick, nxsub, yopt, ytick, nysub); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::text(Float x, Float y, const String &text) { ok(); worker_p->text(x, y, text); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::unsa() { ok(); worker_p->unsa(); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::updt() { ok(); worker_p->updt(); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::vstd() { ok(); worker_p->vstd(); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::wnad(Float x1, Float x2, Float y1, Float y2) { ok(); worker_p->wnad(x1, x2, y1, y2); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::ok() const { if (!isAttached()) { throw(AipsError("Attempt to plot to an unattached PGPlotter!")); } } void PGPlotter::conl(const Matrix &a, Float c, const Vector &tr, const String &label, Int intval, Int minint) { ok(); worker_p->conl(a, c, tr, label, intval, minint); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::cont(const Matrix &a, const Vector &c, Bool nc, const Vector &tr) { ok(); worker_p->cont(a, c, nc, tr); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::ctab(const Vector &l, const Vector &r, const Vector &g, const Vector &b, Float contra, Float bright) { ok(); worker_p->ctab(l, r, g, b, contra, bright); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::gray(const Matrix &a, Float fg, Float bg, const Vector &tr) { ok(); worker_p->gray(a, fg, bg, tr); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::iden() { ok(); worker_p->iden(); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::imag(const Matrix &a, Float a1, Float a2, const Vector &tr) { ok(); worker_p->imag(a, a1, a2, tr); if (!worker_p->isAttached()) worker_p = 0; } Vector PGPlotter::qcir() { ok(); Vector retval = worker_p->qcir(); if (!worker_p->isAttached()) worker_p = 0; return retval; } Vector PGPlotter::qcol() { ok(); Vector retval = worker_p->qcol(); if (!worker_p->isAttached()) worker_p = 0; return retval; } void PGPlotter::scir(Int icilo, Int icihi) { ok(); worker_p->scir(icilo, icihi); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::sitf(Int itf) { ok(); worker_p->sitf(itf); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::bin(const Vector &x, const Vector &data, Bool center) { ok(); worker_p->bin(x, data, center); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::conb(const Matrix &a, const Vector &c, const Vector &tr, Float blank) { ok(); worker_p->conb(a, c, tr, blank); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::cons(const Matrix &a, const Vector &c, const Vector &tr) { ok(); worker_p->cons(a, c, tr); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::errx(const Vector &x1, const Vector &x2, const Vector &y, Float t) { ok(); worker_p->errx(x1, x2, y, t); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::hi2d(const Matrix &data, const Vector &x, Int ioff, Float bias, Bool center, const Vector &ylims) { ok(); worker_p->hi2d(data, x, ioff, bias, center, ylims); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::ldev() { ok(); worker_p->ldev(); if (!worker_p->isAttached()) worker_p = 0; } Vector PGPlotter::len(Int units, const String &string) { ok(); Vector retval = worker_p->len(units, string); if (!worker_p->isAttached()) worker_p = 0; return retval; } String PGPlotter::numb(Int mm, Int pp, Int form) { ok(); String retval = worker_p->numb(mm, pp, form); if (!worker_p->isAttached()) worker_p = 0; return retval; } void PGPlotter::panl(Int ix, Int iy) { ok(); worker_p->panl(ix, iy); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::pap(Float width, Float aspect) { ok(); worker_p->pap(width, aspect); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::pixl(const Matrix &ia, Float x1, Float x2, Float y1, Float y2) { ok(); worker_p->pixl(ia, x1, x2, y1, y2); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::pnts(const Vector &x, const Vector &y, const Vector symbol) { ok(); worker_p->pnts(x, y, symbol); if (!worker_p->isAttached()) worker_p = 0; } Vector PGPlotter::qah() { ok(); Vector retval = worker_p->qah(); if (!worker_p->isAttached()) worker_p = 0; return retval; } Int PGPlotter::qcf() { ok(); Int retval = worker_p->qcf(); if (!worker_p->isAttached()) worker_p = 0; return retval; } Float PGPlotter::qch() { ok(); Float retval = worker_p->qch(); if (!worker_p->isAttached()) worker_p = 0; return retval; } Vector PGPlotter::qcr(Int ci) { ok(); Vector retval = worker_p->qcr(ci); if (!worker_p->isAttached()) worker_p = 0; return retval; } Vector PGPlotter::qcs(Int units) { ok(); Vector retval = worker_p->qcs(units); if (!worker_p->isAttached()) worker_p = 0; return retval; } Int PGPlotter::qfs() { ok(); Int retval = worker_p->qfs(); if (!worker_p->isAttached()) worker_p = 0; return retval; } Vector PGPlotter::qhs() { ok(); Vector retval = worker_p->qhs(); if (!worker_p->isAttached()) worker_p = 0; return retval; } Int PGPlotter::qid() { ok(); Int retval = worker_p->qid(); if (!worker_p->isAttached()) worker_p = 0; return retval; } String PGPlotter::qinf(const String &item) { ok(); String retval = worker_p->qinf(item); if (!worker_p->isAttached()) worker_p = 0; return retval; } Int PGPlotter::qitf() { ok(); Int retval = worker_p->qitf(); if (!worker_p->isAttached()) worker_p = 0; return retval; } Int PGPlotter::qls() { ok(); Int retval = worker_p->qls(); if (!worker_p->isAttached()) worker_p = 0; return retval; } Int PGPlotter::qlw() { ok(); Int retval = worker_p->qlw(); if (!worker_p->isAttached()) worker_p = 0; return retval; } Vector PGPlotter::qpos() { ok(); Vector retval = worker_p->qpos(); if (!worker_p->isAttached()) worker_p = 0; return retval; } Vector PGPlotter::qvp(Int units) { ok(); Vector retval = worker_p->qvp(units); if (!worker_p->isAttached()) worker_p = 0; return retval; } Vector PGPlotter::qvsz(Int units) { ok(); Vector retval = worker_p->qvsz(units); if (!worker_p->isAttached()) worker_p = 0; return retval; } Float PGPlotter::rnd(Float x, Int nsub) { ok(); Float retval = worker_p->rnd(x, nsub); if (!worker_p->isAttached()) worker_p = 0; return retval; } Vector PGPlotter::rnge(Float x1, Float x2) { ok(); Vector retval = worker_p->rnge(x1, x2); if (!worker_p->isAttached()) worker_p = 0; return retval; } void PGPlotter::scf(Int font) { ok(); worker_p->scf(font); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::scrn(Int ci, const String &name) { ok(); worker_p->scrn(ci, name); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::shls(Int ci, Float ch, Float cl, Float cs) { ok(); worker_p->shls(ci, ch, cl, cs); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::shs(Float angle, Float sepn, Float phase) { ok(); worker_p->shs(angle, sepn, phase); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::vect(const Matrix &a, const Matrix &b, Float c, Int nc, const Vector &tr, Float blank) { ok(); worker_p->vect(a, b, c, nc, tr, blank); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::vsiz(Float xleft, Float xright, Float ybot, Float ytop) { ok(); worker_p->vsiz(xleft, xright, ybot, ytop); if (!worker_p->isAttached()) worker_p = 0; } void PGPlotter::wedg(const String &side, Float disp, Float width, Float fg, Float bg, const String &label) { ok(); worker_p->wedg(side, disp, width, fg, bg, label); if (!worker_p->isAttached()) worker_p = 0; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/System/PGPlotter.h000066400000000000000000000276771476623553700175730ustar00rootroot00000000000000//# PGPlotter.h: Standard plotting object for application programmers. //# Copyright (C) 1997,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_PGPLOTTER_H #define CASA_PGPLOTTER_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class String; // // Standard plotting object for application programmers. // // // // // //
        195. PGPlotterInterface // // // // This is the class that a programmer should instantiate if he wants to open // a "device" to plot to. The device might be local, or it might be remote // (i.e., running under Glish). The philosophy of the plotting interface is // described in the // PGPlotterInterface documentation. // // It is possible that the object might not be attached to a valid plot device // (for example, the user might have said "no plotting." Programerss should // check the isAttached() member before plotting. If you attempt to // plot to an unattached plotter, an exception is thrown. // // Copying a PGPlotter uses reference semantics -- after copying // plotting on the old and new objects will result in the plot commands // appearing on the same device. The device is closed only when the last // reference is destructed. // // You can detach a plotter from a device with the detach() call. // If there are no other references to the plotter, this will close the device. // (What it actually does is call the destructor on the object. For a local // PGPPLOT device this will close it). // // // // // // plot y = x*x // Vector x(100), y(100); // indgen(x); // y = x*x; // PGPlotter plotter("myplot.ps/ps"); // plotter.env(0, 100, 0, 100*100, 0, 0); // plotter.line(x, y); // // // // //
        196. Add more plot calls. // class PGPlotter : public PGPlotterInterface { public: // Define the signature of a function creating a PGPlotter object. typedef PGPlotter CreateFunction (const String &device, uInt mincolors, uInt maxcolors, uInt sizex, uInt sizey); // The default constructor does not attach to any plotter, that is // isAttached() returns False. An exception is thrown if you // attempt to plot to an unattached PGPlotter. PGPlotter(); // Create PGPlotter object using the curreent create function. PGPlotter (const String &device, uInt mincolors=2, uInt maxcolors=100, uInt sizex=600, uInt sizey=450); // Create from the given PGPlotterInterface instantiation. // It takes over the pointer. PGPlotter (PGPlotterInterface*); // Copies use reference semantics, i.e. after copying the new and old // copy both draw onto the same surface. // PGPlotter(const PGPlotter &other); PGPlotter &operator=(const PGPlotter &other); // // If this is the last reference, close the plot. virtual ~PGPlotter(); // Create a PGPlotter object using the current create function. static PGPlotter create (const String &device, uInt mincolors=2, uInt maxcolors=100, uInt sizex=600, uInt sizey=450); // Set the create function. It returns the current create function. // It is, for example, used by ObjectController to attach to glish. // The initial create function creates a detached PGPlotter object. // If override==False, the function is only set if it was // not already set. static CreateFunction* setCreateFunction (CreateFunction*, Bool override=True); // True if it is OK to plot to this object. virtual Bool isAttached() const; // Detach from the object. If this is the last reference to the object, // call its destructor (this will call pgclos on a local device). void detach(); // This is not a standard PGPLOT command. In the Glish/PGPLOT window, it // puts a message in the message line. By default it sends it to the logger. // In any event, this is intended for short helpful messages (e.g. // saying which keys to press to mark a spectrum). virtual void message(const String &text); // This is an emulated standard PGPLOT command. It returns a record // containing the fields: // // [ok=Bool, x=Float, y=Float, ch=String]; // If the remote device cannot do cursor feedback, ok==F. // virtual Record curs(Float x, Float y); // Standard PGPLOT commands. Documentation for the individual commands // can be found in the Glish manual and in the standard PGPLOT documentation // which may be found at http://astro.caltech.edu/~tjp/pgplot/. // The Glish/PGPLOT documentation is preferred since this interface follows // it exactly (e.g. the array sizes are inferred both here and in Glish, // whereas they must be passed into standard PGPLOT). // //
        197. An AipsError will be thrown // if the plotter is unattached. // // virtual void arro(Float x1, Float y1, Float x2, Float y2); virtual void ask(Bool flag); virtual void bbuf(); virtual void bin(const Vector &x, const Vector &data, Bool center); virtual void box(const String &xopt, Float xtick, Int nxsub, const String &yopt, Float ytick, Int nysub); virtual void circ(Float xcent, Float ycent, Float radius); virtual void conb(const Matrix &a, const Vector &c, const Vector &tr, Float blank); virtual void conl(const Matrix &a, Float c, const Vector &tr, const String &label, Int intval, Int minint); virtual void cons(const Matrix &a, const Vector &c, const Vector &tr); virtual void cont(const Matrix &a, const Vector &c, Bool nc, const Vector &tr); virtual void ctab(const Vector &l, const Vector &r, const Vector &g, const Vector &b, Float contra, Float bright); virtual void draw(Float x, Float y); virtual void ebuf(); virtual void env(Float xmin, Float xmax, Float ymin, Float ymax, Int just, Int axis); virtual void eras(); virtual void errb(Int dir, const Vector &x, const Vector &y, const Vector &e, Float t); virtual void errx(const Vector &x1, const Vector &x2, const Vector &y, Float t); virtual void erry(const Vector &x, const Vector &y1, const Vector &y2, Float t); virtual void gray(const Matrix &a, Float fg, Float bg, const Vector &tr); virtual void hi2d(const Matrix &data, const Vector &x, Int ioff, Float bias, Bool center, const Vector &ylims); virtual void hist(const Vector &data, Float datmin, Float datmax, Int nbin, Int pcflag); virtual void iden(); virtual void imag(const Matrix &a, Float a1, Float a2, const Vector &tr); virtual void lab(const String &xlbl, const String &ylbl, const String &toplbl); virtual void ldev(); virtual Vector len(Int units, const String &string); virtual void line(const Vector &xpts, const Vector &ypts); virtual void move(Float x, Float y); virtual void mtxt(const String &side, Float disp, Float coord, Float fjust, const String &text); virtual String numb(Int mm, Int pp, Int form); virtual void page(); virtual void panl(Int ix, Int iy); virtual void pap(Float width, Float aspect); virtual void pixl(const Matrix &ia, Float x1, Float x2, Float y1, Float y2); virtual void pnts(const Vector &x, const Vector &y, const Vector symbol); virtual void poly(const Vector &xpts, const Vector &ypts); virtual void pt(const Vector &xpts, const Vector &ypts, Int symbol); virtual void ptxt(Float x, Float y, Float angle, Float fjust, const String &text); virtual Vector qah(); virtual Int qcf(); virtual Float qch(); virtual Int qci(); virtual Vector qcir(); virtual Vector qcol(); virtual Vector qcr(Int ci); virtual Vector qcs(Int units); virtual Int qfs(); virtual Vector qhs(); virtual Int qid(); virtual String qinf(const String &item); virtual Int qitf(); virtual Int qls(); virtual Int qlw(); virtual Vector qpos(); virtual Int qtbg(); virtual Vector qtxt(Float x, Float y, Float angle, Float fjust, const String &text); virtual Vector qvp(Int units); virtual Vector qvsz(Int units); virtual Vector qwin(); virtual void rect(Float x1, Float x2, Float y1, Float y2); virtual Float rnd(Float x, Int nsub); virtual Vector rnge(Float x1, Float x2); virtual void sah(Int fs, Float angle, Float vent); virtual void save(); virtual void scf(Int font); virtual void sch(Float size); virtual void sci(Int ci); virtual void scir(Int icilo, Int icihi); virtual void scr(Int ci, Float cr, Float cg, Float cb); virtual void scrn(Int ci, const String &name); virtual void sfs(Int fs); virtual void shls(Int ci, Float ch, Float cl, Float cs); virtual void shs(Float angle, Float sepn, Float phase); virtual void sitf(Int itf); virtual void sls(Int ls); virtual void slw(Int lw); virtual void stbg(Int tbci); virtual void subp(Int nxsub, Int nysub); virtual void svp(Float xleft, Float xright, Float ybot, Float ytop); virtual void swin(Float x1, Float x2, Float y1, Float y2); virtual void tbox(const String &xopt, Float xtick, Int nxsub, const String &yopt, Float ytick, Int nysub); virtual void text(Float x, Float y, const String &text); virtual void unsa(); virtual void updt(); virtual void vect(const Matrix &a, const Matrix &b, Float c, Int nc, const Vector &tr, Float blank); virtual void vsiz(Float xleft, Float xright, Float ybot, Float ytop); virtual void vstd(); virtual void wedg(const String &side, Float disp, Float width, Float fg, Float bg, const String &label); virtual void wnad(Float x1, Float x2, Float y1, Float y2); // private: std::shared_ptr worker_p; static CreateFunction* creator_p; // Throws an exception if !isAttached() void ok() const; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/System/PGPlotterInterface.cc000066400000000000000000000033061476623553700215310ustar00rootroot00000000000000//# PGPlotterInterface.cc: Abstract base class for PGPLOT style plotting. //# Copyright (C) 1997,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN PGPlotterInterface::~PGPlotterInterface() { // Nothing } void PGPlotterInterface::message(const String &text) { // On the logger we want to suppres blank lines. if (text != "") { LogIO os; os << text << LogIO::POST; } } Bool PGPlotterInterface::isAttached() const { return True; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/System/PGPlotterInterface.h000066400000000000000000000263771476623553700214100ustar00rootroot00000000000000//# PGPlotterInterface.h: Abstract base class for PGPLOT style plotting. //# Copyright (C) 1997,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_PGPLOTTERINTERFACE_H #define CASA_PGPLOTTERINTERFACE_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class Record; class String; // // Abstract base class for PGPLOT style plotting. // // // // // //
        198. General familiarity with PGPLOT, especially of the style of the // Glish/PGPLOT binding. // // // // PGPlotter for the plotting style, Interface because it is an abstract base // class, not a concrete derived class. // // // // This class represents an interface for plotting to a PGPLOT style plotting // interface. In general, the differences between actual PGPLOT and this // interface is: //
            //
          1. The functions related to opening and closing are not implemented, // since it is assumed the derived class constructor/destructor will // handle this. //
          2. The leading "pg" is removed from the name since by being in a class // there are no namespace issues. //
          3. Casacore array classes are used in place of raw pointers. This also // obviates the need for passing in array dimensions. Similarly the // subregion arguments (I1, I2, J1, J2) are left out since the array // classes have their own subsectioning methods. //
          4. Output values are returned from the function //
          // The rules are basically the same as for the Glish/PGPLOT binding, and thus // the individual routines are not documented here. //
          // // // // void plotFunction(const PGPlotterInterface &plotter) { // // plot y = x*x // Vector x(100), y(100); // indgen(x); // y = x*x; // plotter.env(0, 100, 0, 100*100, 0, 0); // plotter.line(x, y); // } // // // // // General plotting interface for programmers, while allowing the location and // form of the plot to vary. // // // //
        199. Add the missing PGPLOT functions. //
        200. Emulate band as well as curs? // class PGPlotterInterface { public: virtual ~PGPlotterInterface(); // True if it is OK to plot to this object. This method is implemented for // devices where you have to worry about devices detaching (e.g., the Glish // pgplotter might be dismissed by the user). The default implementation is // to always return True. virtual Bool isAttached() const; // This is not a standard PGPLOT command. In the Glish/PGPLOT window, it // puts a message in the message line. By default it sends it to the logger. // In any event, this is intended for short helpful messages (e.g. // saying which keys to press to mark a spectrum). virtual void message(const String &text); // This is not a standard PGPLOT command. It is only needed for // the PGPlotterGlish class which connects to Glish/PGPLOT window // This Glish object (actually a pgplotter/pgplotwidget.g) has an // internal counter plot counter which needs to be reset to 0 // when the process detaches from the plotter, so that the next // plot on the device is the first one again. Without this, the // prompting behaviour of the Glish plotter is different from native // PGPLOT virtual void resetPlotNumber () {;}; // This is an emulated standard PGPLOT command. It returns a record // containing the fields: // // [ok=Bool, x=Float, y=Float, ch=String]; // If the remote device cannot do cursor feedback, ok==F. // // The input x,y values is the "guess" for the location the user will want // to pick. On some devices, the cursor will be positioned at (world // coordinates) x,y. virtual Record curs(Float x, Float y) = 0; // Standard PGPLOT commands. Documentation for the individual commands // can be found in the Glish manual and in the standard PGPLOT documentation // which may be found at http://astro.caltech.edu/~tjp/pgplot/. // The Glish/PGPLOT documentation is preferred since this interface follows // it exactly (e.g. the array sizes are inferred both here and in Glish, // whereas they must be passed into standard PGPLOT). // virtual void arro(Float x1, Float y1, Float x2, Float y2) = 0; virtual void ask(Bool flag) = 0; virtual void bbuf() = 0; virtual void bin(const Vector &x, const Vector &data, Bool center) = 0; virtual void box(const String &xopt, Float xtick, Int nxsub, const String &yopt, Float ytick, Int nysub) = 0; virtual void circ(Float xcent, Float ycent, Float radius) = 0; virtual void conb(const Matrix &a, const Vector &c, const Vector &tr, Float blank) = 0; virtual void conl(const Matrix &a, Float c, const Vector &tr, const String &label, Int intval, Int minint) = 0; virtual void cons(const Matrix &a, const Vector &c, const Vector &tr) = 0; virtual void cont(const Matrix &a, const Vector &c, Bool nc, const Vector &tr) = 0; virtual void ctab(const Vector &l, const Vector &r, const Vector &g, const Vector &b, Float contra, Float bright) = 0; virtual void draw(Float x, Float y) = 0; virtual void ebuf() = 0; virtual void env(Float xmin, Float xmax, Float ymin, Float ymax, Int just, Int axis) = 0; virtual void eras() = 0; virtual void errb(Int dir, const Vector &x, const Vector &y, const Vector &e, Float t) = 0; virtual void errx(const Vector &x1, const Vector &x2, const Vector &y, Float t) = 0; virtual void erry(const Vector &x, const Vector &y1, const Vector &y2, Float t) = 0; virtual void gray(const Matrix &a, Float fg, Float bg, const Vector &tr) = 0; virtual void hi2d(const Matrix &data, const Vector &x, Int ioff, Float bias, Bool center, const Vector &ylims) = 0; virtual void hist(const Vector &data, Float datmin, Float datmax, Int nbin, Int pcflag) = 0; virtual void iden() = 0; virtual void imag(const Matrix &a, Float a1, Float a2, const Vector &tr) = 0; virtual void lab(const String &xlbl, const String &ylbl, const String &toplbl) = 0; virtual void ldev() = 0; virtual Vector len(Int units, const String &string) = 0; virtual void line(const Vector &xpts, const Vector &ypts) = 0; virtual void move(Float x, Float y) = 0; virtual void mtxt(const String &side, Float disp, Float coord, Float fjust, const String &text) = 0; virtual String numb(Int mm, Int pp, Int form) = 0; virtual void page() = 0; virtual void panl(Int ix, Int iy) = 0; virtual void pap(Float width, Float aspect) = 0; virtual void pixl(const Matrix &ia, Float x1, Float x2, Float y1, Float y2) = 0; virtual void pnts(const Vector &x, const Vector &y, const Vector symbol) = 0; virtual void poly(const Vector &xpts, const Vector &ypts) = 0; virtual void pt(const Vector &xpts, const Vector &ypts, Int symbol) = 0; virtual void ptxt(Float x, Float y, Float angle, Float fjust, const String &text) = 0; virtual Vector qah() = 0; virtual Int qcf() = 0; virtual Float qch() = 0; virtual Int qci() = 0; virtual Vector qcir() = 0; virtual Vector qcol() = 0; virtual Vector qcr(Int ci) = 0; virtual Vector qcs(Int units) = 0; virtual Int qfs() = 0; virtual Vector qhs() = 0; virtual Int qid() = 0; virtual String qinf(const String &item) = 0; virtual Int qitf() = 0; virtual Int qls() = 0; virtual Int qlw() = 0; virtual Vector qpos() = 0; virtual Int qtbg() = 0; virtual Vector qtxt(Float x, Float y, Float angle, Float fjust, const String &text) = 0; virtual Vector qvp(Int units) = 0; virtual Vector qvsz(Int units) = 0; virtual Vector qwin() = 0; virtual void rect(Float x1, Float x2, Float y1, Float y2) = 0; virtual Float rnd(Float x, Int nsub) = 0; virtual Vector rnge(Float x1, Float x2) = 0; virtual void sah(Int fs, Float angle, Float vent) = 0; virtual void save() = 0; virtual void scf(Int font) = 0; virtual void sch(Float size) = 0; virtual void sci(Int ci) = 0; virtual void scir(Int icilo, Int icihi) = 0; virtual void scr(Int ci, Float cr, Float cg, Float cb) = 0; virtual void scrn(Int ci, const String &name) = 0; virtual void sfs(Int fs) = 0; virtual void shls(Int ci, Float ch, Float cl, Float cs) = 0; virtual void shs(Float angle, Float sepn, Float phase) = 0; virtual void sitf(Int itf) = 0; virtual void sls(Int ls) = 0; virtual void slw(Int lw) = 0; virtual void stbg(Int tbci) = 0; virtual void subp(Int nxsub, Int nysub) = 0; virtual void svp(Float xleft, Float xright, Float ybot, Float ytop) = 0; virtual void swin(Float x1, Float x2, Float y1, Float y2) = 0; virtual void tbox(const String &xopt, Float xtick, Int nxsub, const String &yopt, Float ytick, Int nysub) = 0; virtual void text(Float x, Float y, const String &text) = 0; virtual void unsa() = 0; virtual void updt() = 0; virtual void vect(const Matrix &a, const Matrix &b, Float c, Int nc, const Vector &tr, Float blank) = 0; virtual void vsiz(Float xleft, Float xright, Float ybot, Float ytop) = 0; virtual void vstd() = 0; virtual void wedg(const String &side, Float disp, Float width, Float fg, Float bg, const String &label) = 0; virtual void wnad(Float x1, Float x2, Float y1, Float y2) = 0; // }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/System/PGPlotterNull.cc000066400000000000000000000250041476623553700205420ustar00rootroot00000000000000//# PGPlotterNull.cc: Plot to a PGPLOT device "NULL" to this process. //# Copyright (C) 1997,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN PGPlotterNull::PGPlotterNull(const String &) : beenWarned(True) { // If this fails, we need a bit more development to copy Float*'s to // float*'s. AlwaysAssertExit(sizeof(Float) == sizeof(float)); noplotter(); } PGPlotterNull::~PGPlotterNull() { noplotter(); } PGPlotter PGPlotterNull::createPlotter (const String &device, uInt, uInt, uInt, uInt) { return PGPlotter (new PGPlotterNull (device)); } Record PGPlotterNull::curs(Float, Float) { Record retval; noplotter(); return retval; } void PGPlotterNull::arro(Float, Float, Float, Float) { noplotter(); } void PGPlotterNull::ask(Bool) { noplotter(); } void PGPlotterNull::bbuf() { noplotter(); } void PGPlotterNull::box(const String &, Float, Int, const String &, Float, Int) { noplotter(); } void PGPlotterNull::circ(Float, Float, Float) { noplotter(); } void PGPlotterNull::draw(Float, Float) { noplotter(); } void PGPlotterNull::ebuf() { noplotter(); } void PGPlotterNull::env(Float, Float, Float, Float, Int, Int) { noplotter(); } void PGPlotterNull::eras() { noplotter(); } void PGPlotterNull::errb(Int, const Vector &, const Vector &, const Vector &, Float) { noplotter(); } void PGPlotterNull::erry(const Vector &, const Vector &, const Vector &, Float) { noplotter(); } void PGPlotterNull::hist(const Vector &, Float, Float, Int, Int) { noplotter(); } void PGPlotterNull::lab(const String &, const String &, const String &) { noplotter(); } void PGPlotterNull::line(const Vector &, const Vector &) { noplotter(); } void PGPlotterNull::move(Float, Float) { noplotter(); } void PGPlotterNull::mtxt(const String &, Float, Float, Float, const String &) { noplotter(); } void PGPlotterNull::page() { noplotter(); } void PGPlotterNull::poly(const Vector &, const Vector &) { noplotter(); } void PGPlotterNull::pt(const Vector &, const Vector &, Int) { noplotter(); } void PGPlotterNull::ptxt(Float, Float, Float, Float, const String &) { noplotter(); } Int PGPlotterNull::qci() { noplotter(); return 0; } Int PGPlotterNull::qtbg() { noplotter(); return 0; } Vector PGPlotterNull::qtxt(Float, Float, Float, Float, const String &) { Vector xboxybox(8); xboxybox = 0.; noplotter(); return xboxybox; } Vector PGPlotterNull::qwin() { Vector retval(4); retval = 0; noplotter(); return retval; } void PGPlotterNull::rect(Float, Float, Float, Float) { noplotter(); } void PGPlotterNull::sah(Int, Float, Float) { noplotter(); } void PGPlotterNull::save() { noplotter(); } void PGPlotterNull::sch(Float) { noplotter(); } void PGPlotterNull::sci(Int) { noplotter(); } void PGPlotterNull::scr(Int, Float, Float, Float) { noplotter(); } void PGPlotterNull::sfs(Int) { noplotter(); } void PGPlotterNull::sls(Int) { noplotter(); } void PGPlotterNull::slw(Int) { noplotter(); } void PGPlotterNull::stbg(Int) { noplotter(); } void PGPlotterNull::subp(Int, Int) { noplotter(); } void PGPlotterNull::svp(Float, Float, Float, Float) { noplotter(); } void PGPlotterNull::swin(Float, Float, Float, Float) { noplotter(); } void PGPlotterNull::tbox(const String &, Float, Int, const String &, Float, Int) { noplotter(); } void PGPlotterNull::text(Float, Float, const String &) { noplotter(); } void PGPlotterNull::unsa() { noplotter(); } void PGPlotterNull::updt() { noplotter(); } void PGPlotterNull::vstd() { noplotter(); } void PGPlotterNull::wnad(Float, Float, Float, Float) { noplotter(); } void PGPlotterNull::conl(const Matrix &, Float, const Vector &, const String &, Int, Int) { noplotter(); } void PGPlotterNull::cont(const Matrix &, const Vector &, Bool, const Vector &) { noplotter(); } void PGPlotterNull::ctab(const Vector &, const Vector &, const Vector &, const Vector &, Float, Float) { noplotter(); } void PGPlotterNull::gray(const Matrix &, Float, Float, const Vector &) { noplotter(); } void PGPlotterNull::iden() { noplotter(); } void PGPlotterNull::imag(const Matrix &, Float, Float, const Vector &) { noplotter(); } Vector PGPlotterNull::qcir() { Vector retval(2); retval = 0; noplotter(); return retval; } Vector PGPlotterNull::qcol() { Vector retval(2); retval = 0; noplotter(); return retval; } void PGPlotterNull::scir(Int, Int) { noplotter(); } void PGPlotterNull::sitf(Int) { noplotter(); } void PGPlotterNull::bin(const Vector &, const Vector &, Bool) { noplotter(); } void PGPlotterNull::conb(const Matrix &, const Vector &, const Vector &, Float) { noplotter(); } void PGPlotterNull::cons(const Matrix &, const Vector &, const Vector &) { noplotter(); } void PGPlotterNull::errx(const Vector &, const Vector &, const Vector &, Float) { noplotter(); } void PGPlotterNull::hi2d(const Matrix &, const Vector &, Int, Float, Bool, const Vector &) { noplotter(); } void PGPlotterNull::ldev() { noplotter(); } Vector PGPlotterNull::len(Int, const String &) { Vector retval(2); retval =0; noplotter(); return retval; } String PGPlotterNull::numb(Int, Int, Int) { noplotter(); return String(); } void PGPlotterNull::panl(Int, Int) { noplotter(); } void PGPlotterNull::pap(Float, Float) { noplotter(); } void PGPlotterNull::pixl(const Matrix &, Float, Float, Float, Float) { noplotter(); } void PGPlotterNull::pnts(const Vector &, const Vector &, const Vector) { noplotter(); } Vector PGPlotterNull::qah() { Vector retval(3); retval = 0; noplotter(); return retval; } Int PGPlotterNull::qcf() { noplotter(); return 0; } Float PGPlotterNull::qch() { noplotter(); return 0; } Vector PGPlotterNull::qcr(Int) { Vector retval(3); retval = 0; noplotter(); return retval; } Vector PGPlotterNull::qcs(Int) { Vector retval(2); retval = 0; noplotter(); return retval; } Int PGPlotterNull::qfs() { noplotter(); return 0; } Vector PGPlotterNull::qhs() { Vector retval(3); retval = 0; noplotter(); return retval; } Int PGPlotterNull::qid() { noplotter(); return 0; } String PGPlotterNull::qinf(const String &) { noplotter(); return String(); } Int PGPlotterNull::qitf() { noplotter(); return 0; } Int PGPlotterNull::qls() { noplotter(); return 0; } Int PGPlotterNull::qlw() { noplotter(); return 0; } Vector PGPlotterNull::qpos() { Vector retval(2); retval = 0; noplotter(); return retval; } Vector PGPlotterNull::qvp(Int) { Vector retval(4); retval = 0; noplotter(); return retval; } Vector PGPlotterNull::qvsz(Int) { Vector retval(4); retval = 0; noplotter(); return retval; } Float PGPlotterNull::rnd(Float x, Int) { noplotter(); return x; } Vector PGPlotterNull::rnge(Float, Float) { Vector retval(2); retval = 0; noplotter(); return retval; } void PGPlotterNull::scf(Int) { noplotter(); } void PGPlotterNull::scrn(Int, const String &) { noplotter(); } void PGPlotterNull::shls(Int, Float, Float, Float) { noplotter(); } void PGPlotterNull::shs(Float, Float, Float) { noplotter(); } void PGPlotterNull::vect(const Matrix &, const Matrix &, Float, Int, const Vector &, Float) { noplotter(); } void PGPlotterNull::vsiz(Float, Float, Float, Float) { noplotter(); } void PGPlotterNull::wedg(const String &, Float, Float, Float, Float, const String &) { noplotter(); } void PGPlotterNull::noplotter() { if(!beenWarned){ std::cerr << "Warning no plotter attached. Attach a plotter to get plots" << std::endl; beenWarned = True; } } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/System/PGPlotterNull.h000066400000000000000000000235111476623553700204050ustar00rootroot00000000000000//# PGPlotterNull.h: Plot to a PGPLOT device "null" to this process. //# Copyright (C) 1997,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef GRAPHICS_PGPLOTTERNULL_H #define GRAPHICS_PGPLOTTERNULL_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class String; // // Plot to a PGPLOT device "local" to this process. // // // // // //
        201. PGPlotterInterface // // // // "Null" is used to denote that no plotting is done // // // // Generally programmers should not use this class, instead they should use // PGPlotter instead. // // This class make a concrete // PGPlotterInterface object which // calls PGPLOT directly, i.e. PGPLOT is linked into the current executable. // // // // // // plot y = x*x // Vector x(100), y(100); // indgen(x); // y = x*x; // PGPlotterNull plotter("myplot.ps/ps"); // plotter.env(0, 100, 0, 100*100, 0, 0); // plotter.line(x, y); // // // // // It might be necessary to call PGPLOT directly in some circumstances. For // example, it might be too inefficient to pass a lot of Image data over the // glish bus. // // // //
        202. Add more plot calls. // class PGPlotterNull : public PGPlotterInterface { public: // Open "device", which must be a valid PGPLOT style device, for example // /cps for colour postscript (or myfile.ps/cps // if you want to name the file), or /xs or /xw for // and X-windows display. // //
        203. An AipsError will be thrown // if the underlying PGPLOT open fails for some reason. // PGPlotterNull(const String &device); // The destructor closes the pgplot device. virtual ~PGPlotterNull(); // The create function to create a PGPlotter object using a PGPlotterNull. // It only uses the device argument. static PGPlotter createPlotter (const String &device, uInt, uInt, uInt, uInt); // This is an emulated standard PGPLOT command. It returns a record // containing the fields: // // [ok=Bool, x=Float, y=Float, ch=String]; // If the remote device cannot do cursor feedback, ok==F. // virtual Record curs(Float x, Float y); // Standard PGPLOT commands. Documentation for the individual commands // can be found in the Glish manual and in the standard PGPLOT documentation // which may be found at http://astro.caltech.edu/~tjp/pgplot/. // The Glish/PGPLOT documentation is preferred since this interface follows // it exactly (e.g. the array sizes are inferred both here and in Glish, // whereas they must be passed into standard PGPLOT). // virtual void arro(Float x1, Float y1, Float x2, Float y2); virtual void ask(Bool flag); virtual void bbuf(); virtual void bin(const Vector &x, const Vector &data, Bool center); virtual void box(const String &xopt, Float xtick, Int nxsub, const String &yopt, Float ytick, Int nysub); virtual void circ(Float xcent, Float ycent, Float radius); virtual void conb(const Matrix &a, const Vector &c, const Vector &tr, Float blank); virtual void conl(const Matrix &a, Float c, const Vector &tr, const String &label, Int intval, Int minint); virtual void cons(const Matrix &a, const Vector &c, const Vector &tr); virtual void cont(const Matrix &a, const Vector &c, Bool nc, const Vector &tr); virtual void ctab(const Vector &l, const Vector &r, const Vector &g, const Vector &b, Float contra, Float bright); virtual void draw(Float x, Float y); virtual void ebuf(); virtual void env(Float xmin, Float xmax, Float ymin, Float ymax, Int just, Int axis); virtual void eras(); virtual void errb(Int dir, const Vector &x, const Vector &y, const Vector &e, Float t); virtual void errx(const Vector &x1, const Vector &x2, const Vector &y, Float t); virtual void erry(const Vector &x, const Vector &y1, const Vector &y2, Float t); virtual void gray(const Matrix &a, Float fg, Float bg, const Vector &tr); virtual void hi2d(const Matrix &data, const Vector &x, Int ioff, Float bias, Bool center, const Vector &ylims); virtual void hist(const Vector &data, Float datmin, Float datmax, Int nbin, Int pcflag); virtual void iden(); virtual void imag(const Matrix &a, Float a1, Float a2, const Vector &tr); virtual void lab(const String &xlbl, const String &ylbl, const String &toplbl); virtual void ldev(); virtual Vector len(Int units, const String &string); virtual void line(const Vector &xpts, const Vector &ypts); virtual void move(Float x, Float y); virtual void mtxt(const String &side, Float disp, Float coord, Float fjust, const String &text); virtual String numb(Int mm, Int pp, Int form); virtual void page(); virtual void panl(Int ix, Int iy); virtual void pap(Float width, Float aspect); virtual void pixl(const Matrix &ia, Float x1, Float x2, Float y1, Float y2); virtual void pnts(const Vector &x, const Vector &y, const Vector symbol); virtual void poly(const Vector &xpts, const Vector &ypts); virtual void pt(const Vector &xpts, const Vector &ypts, Int symbol); virtual void ptxt(Float x, Float y, Float angle, Float fjust, const String &text); virtual Vector qah(); virtual Int qcf(); virtual Float qch(); virtual Int qci(); virtual Vector qcir(); virtual Vector qcol(); virtual Vector qcr(Int ci); virtual Vector qcs(Int units); virtual Int qfs(); virtual Vector qhs(); virtual Int qid(); virtual String qinf(const String &item); virtual Int qitf(); virtual Int qls(); virtual Int qlw(); virtual Vector qpos(); virtual Int qtbg(); virtual Vector qtxt(Float x, Float y, Float angle, Float fjust, const String &text); virtual Vector qvp(Int units); virtual Vector qvsz(Int units); virtual Vector qwin(); virtual void rect(Float x1, Float x2, Float y1, Float y2); virtual Float rnd(Float x, Int nsub); virtual Vector rnge(Float x1, Float x2); virtual void sah(Int fs, Float angle, Float vent); virtual void save(); virtual void scf(Int font); virtual void sch(Float size); virtual void sci(Int ci); virtual void scir(Int icilo, Int icihi); virtual void scr(Int ci, Float cr, Float cg, Float cb); virtual void scrn(Int ci, const String &name); virtual void sfs(Int fs); virtual void shls(Int ci, Float ch, Float cl, Float cs); virtual void shs(Float angle, Float sepn, Float phase); virtual void sitf(Int itf); virtual void sls(Int ls); virtual void slw(Int lw); virtual void stbg(Int tbci); virtual void subp(Int nxsub, Int nysub); virtual void svp(Float xleft, Float xright, Float ybot, Float ytop); virtual void swin(Float x1, Float x2, Float y1, Float y2); virtual void tbox(const String &xopt, Float xtick, Int nxsub, const String &yopt, Float ytick, Int nysub); virtual void text(Float x, Float y, const String &text); virtual void unsa(); virtual void updt(); virtual void vect(const Matrix &a, const Matrix &b, Float c, Int nc, const Vector &tr, Float blank); virtual void vsiz(Float xleft, Float xright, Float ybot, Float ytop); virtual void vstd(); virtual void wedg(const String &side, Float disp, Float width, Float fg, Float bg, const String &label); virtual void wnad(Float x1, Float x2, Float y1, Float y2); // private: // Undefined and inaccessible PGPlotterNull(const PGPlotterNull &); PGPlotterNull& operator=(const PGPlotterNull &); // PGPLOT id int id_p; Bool beenWarned; void noplotter(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/System/ProgressMeter.cc000066400000000000000000000210251476623553700206270ustar00rootroot00000000000000//# ProgressMeter.h: Visual indication of a tasks progress. //# Copyright (C) 1997,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // First implement a simple stderr based progress meter that just prints out // 0%....10....20....30....40....50....60....70....80....90....100% // cerr is better than cout because it isn't buffered usually, so the above // will come out right away. Also, one often wants to direct "real" output // to a file, but see informative messages on the screen. // If we have lots and lots of progress meters we should figure out // a way to reclaim the following storage. static Block stderr_min, stderr_max, stderr_last; static Block stderr_title; static Block stderr_time; static Block stderr_startflag; const char* ProgressMeter::PROGRESSFILE = "/tmp/xidjapdfs"; static Int stderr_creation_function(Double min, Double max, const String &t, const String &, const String &, const String &, Bool) { Int n = stderr_min.nelements() + 1; stderr_min.resize(n); stderr_max.resize(n); stderr_last.resize(n); stderr_title.resize(n); stderr_time.resize(n); stderr_startflag.resize(n); stderr_min[n-1] = min; stderr_max[n-1] = max; stderr_last[n-1] = min; stderr_title[n-1] = t; stderr_time[n-1] = time(0); stderr_startflag[n-1] = False; //cerr << "\n0%"; return n; } static void stderr_show_function(Int id, Double value) { if (id < 0 || id > Int(stderr_min.nelements())) { return; } id--; // 0-relative stderr_last[id] = value; fstream file_op(ProgressMeter::PROGRESSFILE, ios::out | ios::app); file_op << stderr_time[id] << "," << stderr_title[id] << "," << stderr_min[id] << "," << stderr_max[id] << "," << stderr_last[id] << "\n"; file_op.close(); return ; } static void stderr_busy_function(Int id) { if (id < 0 || id > Int(stderr_min.nelements())) { return; } id--; // 0-relative fstream file_op(ProgressMeter::PROGRESSFILE, ios::out | ios::app); file_op << stderr_time[id] << "," << stderr_title[id] << "," << "0,0,1\n"; file_op.close(); return ; } static void stderr_done_function(Int id) { if (id < 0 || id > Int(stderr_min.nelements())) { return; } id--; // 0-relative fstream file_op(ProgressMeter::PROGRESSFILE, ios::out | ios::app); file_op << stderr_time[id] << "," << stderr_title[id] << "," << "0,1,1\n"; file_op.close(); return ; } static void stderr_update_function(Int id, Double value) { if (id < 0 || id > Int(stderr_min.nelements())) { cerr << __FILE__ << " illegal id " << id << endl; return; } id--; // 0-relative Int percent = Int((value - stderr_min[id]) / (stderr_max[id] - stderr_min[id]) * 100.0); Int lastpercent = Int((stderr_last[id] - stderr_min[id]) / (stderr_max[id] - stderr_min[id]) * 100.0); // if (::fabs((stderr_last[id] - stderr_min[id])/stderr_min[id]) < 0.001) cerr << "\n0%"; if (!stderr_startflag[id] && ::fabs((stderr_last[id] - stderr_min[id])/stderr_min[id]) < 0.001) { cerr << "\n0%"; stderr_startflag[id] = True; } if (percent > lastpercent) { stderr_last[id] = value; // Probably we could do this more efficiently. We need to get all the // "missing" ..'s etc if we have jumped a lot since our last updated. for (Int i=lastpercent+1; i<=percent; i++) { if (i%2 == 0 && i%10 != 0) { cerr << "."; } else if (i %10 == 0) { cerr << i; if (i >= 100) { cerr << "%\n"; } } } } } Int (*ProgressMeter::creation_function_p)(Double, Double, const String &, const String &, const String &, const String &, Bool) = stderr_creation_function; void (*ProgressMeter::update_function_p)(Int, Double) = stderr_update_function; void (*ProgressMeter::show_function_p)(Int, Double) = stderr_show_function; void (*ProgressMeter::busy_function_p)(Int) = stderr_busy_function; void (*ProgressMeter::done_function_p)(Int) = stderr_done_function; ProgressMeter::ProgressMeter() : id_p(-1), min_p(0.0), max_p(1.0), update_every_p(1), update_count_p(0) { } ProgressMeter::ProgressMeter(Double min, Double max, const String &title, const String &subtitle, const String &minlabel, const String &maxlabel, Bool estimateTime, Int updateEvery) : id_p(-1), min_p(min), max_p(max), update_every_p(updateEvery), update_count_p(0) { // Correct silently if (update_every_p <= 0) { update_every_p = 1; } if (creation_function_p) { id_p = creation_function_p(min, max, title, subtitle, minlabel, maxlabel, estimateTime); } } ProgressMeter::ProgressMeter(Double min, Double max, const String &title) : id_p(-1), min_p(min), max_p(max), update_every_p(1), update_count_p(0) { if (creation_function_p) { id_p = creation_function_p(min, max, title, "", "", "", False); } } ProgressMeter::~ProgressMeter() { // Do not update if still 0, otherwise no initialization done in update. if (update_count_p > 0) update_count_p++; update(max_p, True); } void ProgressMeter::_update(Double value, Bool force) { update_count_p++; if (update_count_p == 1) { startTime = time(&startTime); showProgress = False; force = True; } time_t itsTime; itsTime = time(&itsTime); if(!showProgress && itsTime >= startTime + time_t(7)) showProgress = True; if(!showProgress){ if((value >= min_p) && (value <= max_p)){ if(update_count_p == 1 || force || ((update_count_p%update_every_p)== 0)) { show_function_p(id_p, value); } } } } void ProgressMeter::busy() { busy_function_p(id_p); } void ProgressMeter::done() { done_function_p(id_p); } void ProgressMeter::update(Double value, Bool force) { update_count_p++; // Always force the first one through if (update_count_p == 1) { showProgress = False; startTime = time(&startTime); force = True; } time_t itsTime; itsTime = time(&itsTime); if(!showProgress && itsTime >= startTime + time_t(7)) showProgress = True; if(showProgress){ if((value >= min_p) && (value <= max_p)){ if(update_count_p == 1 || force || ((update_count_p%update_every_p)== 0)) { // Do the update if we have a "sink" and a valid id if (id_p > 0 && update_function_p) { update_function_p(id_p, value); } else { // If we have more than one progress meter active at once // this might look pretty confusing. We can decide what to // do if that ever actually happens. } } }else{ //cerr << "WARNING: progress meter trying to update beyond range" << endl;//The user does not need to know that the programmer does not know how to add. } } } Double ProgressMeter::min() const { return min_p; } Double ProgressMeter::max() const { return max_p; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/System/ProgressMeter.h000066400000000000000000000141471476623553700205000ustar00rootroot00000000000000//# ProgressMeter.h: Visual indication of a tasks progress. //# Copyright (C) 1997,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_PROGRESSMETER_H #define CASA_PROGRESSMETER_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class String; // // Visual indication of a tasks progress. // // // // // // This class is used to provide a visual indication to the user of the progress // of his task. If the process is not connected to the DO system, calls to the // progress meter are NO-OP's, so you can safely use this class in general // library code and it won't cause problems for processes which are not // attached to the distributed object system. It also won't cause any extra // stuff to be linked in to your executable in this case. // // The progress meter will usually be removed from the screen once the maximum // value is set, so you should not reuse the ProgressMeter after that has // happened. It is harmless, but it will not result in any visual feedback for // the user. // // While the "min" is usually less than "max", if in fact it is greater than // "max" the progress meter will count down correctly. // // // // // void calculate(uInt n) { // Int skip = n / 200; // ProgressMeter meter(0, n, "Title", "Subtitle", "", "", True, skip); // for (uInt i=0; i // // // // Give the user visual indication of a long-running tasks progress. // // // //
        204. When the upper bound isn't known, it might be useful to have a busy // bar that just moves back and forth to show that activity is happening. //
        205. We should probably have some way to suppress progress bars for tasks // that are only going to take a few seconds. // class ProgressMeter { public: // Makes a null progress meter, i.e. no updates to the screen are // generated. ProgressMeter(); // Create a progress meter with the given min and max values and labels. // if estimateTime is True, an estimate of the // time remaining will be made for the user. This estimate assumes that // the remaining portion will compute at the same rate as the portion // completed so far, so the time should not be estimated for processes // which are non-linear. // // Any labels which are set to the empty string will have sensible defaults // supplied. In particular, minlabel and maxlabel // will be set to the display the minimum and maximum values. // // Normally the progress bar will be updated with every call to // update(). If however you will be sending many events // then you might want to update the GUI every updateEvery'th // event for efficiency. Generally there's no point updating more than // a couple of hundred times since the eye can't distinguish differences // in the progress bar position at that level. If updateEvery is <=0, it // is set to 1 for you. ProgressMeter(Double min, Double max, const String &title, const String &subtitle, const String &minlabel, const String &maxlabel, Bool estimateTime = True, Int updateEvery=1); ProgressMeter(Double min, Double max, const String &title); // The destruction of the meter will cause an update to be sent with the // maximum value. This will usually cause the GUI window to be removed // from the screen. Thus the progress meter should generally live as long // as the calculation it is tracking. ~ProgressMeter(); void update(Double value, Bool force=False); void _update(Double value, Bool force=False); void busy(); void done(); // Display the min and max values of the progress meter. // Double min() const; Double max() const; // friend class ObjectController; static const char* PROGRESSFILE; private: Int id_p; Double min_p, max_p; Int update_every_p, update_count_p; // Time the progress meter began time_t startTime; Bool showProgress; // These are set by ObjectController for executables that have the tasking // system in them, otherwise they are null and this class just does no-ops. static Int (*creation_function_p)(Double, Double, const String &, const String &, const String &, const String &, Bool); static void (*update_function_p)(Int, Double); static void (*show_function_p)(Int, Double); static void (*busy_function_p)(Int); static void (*done_function_p)(Int); // Undefined and inaccessible ProgressMeter(const ProgressMeter &); ProgressMeter &operator=(const ProgressMeter &); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/System/test/000077500000000000000000000000001476623553700164765ustar00rootroot00000000000000casacore-3.7.1/casa/System/test/CMakeLists.txt000066400000000000000000000005321476623553700212360ustar00rootroot00000000000000set (tests tAipsrc tAipsrcValue tAppInfo tAppState tCasarc01 tChoice tObjectID ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_casa) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/casa/System/test/tAipsrc.cc000066400000000000000000000062541476623553700204210ustar00rootroot00000000000000//# tAipsrc.cc: This program tests the Aipsrc interface //# Copyright (C) 1996,1997,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include int main(){ String aipsrcKeyword("printer.ps1.paper"); String aipsrcValue; Aipsrc::find(aipsrcValue, aipsrcKeyword); cout << aipsrcKeyword << " " << aipsrcValue << endl; Aipsrc::reRead(); Aipsrc::find(aipsrcValue, aipsrcKeyword); cout << aipsrcKeyword << " " << aipsrcValue << endl; aipsrcKeyword = String("bogus.wes.key"); Aipsrc::find(aipsrcValue, aipsrcKeyword); cout << aipsrcKeyword << " " << aipsrcValue << endl; aipsrcKeyword = String("printer.ps.paper"); Aipsrc::find(aipsrcValue, aipsrcKeyword); cout << aipsrcKeyword << " " << aipsrcValue << endl; aipsrcKeyword = String("dummy.abc"); Aipsrc::find(aipsrcValue, aipsrcKeyword); cout << "*" << aipsrcKeyword << "*" << aipsrcValue << "*" << endl; cout << "AIPSROOT: " << Aipsrc::aipsRoot() << endl; cout << "AIPSARCH: " << Aipsrc::aipsArch() << endl; cout << "AIPSSITE: " << Aipsrc::aipsSite() << endl; cout << "AIPSHOST: " << Aipsrc::aipsHost() << endl; cout << "AIPSHOME: " << Aipsrc::aipsHome() << endl; Aipsrc::show(cout); // test the "find with default" { String result; AlwaysAssertExit(Aipsrc::find(result, "foobar", "default") == False && result == "default"); AlwaysAssertExit(Aipsrc::findNoHome(result, "foobar", "default") == False && result == "default"); } // test the register { uInt n = Aipsrc::registerRC("foobar", "invalid"); uInt n1= Aipsrc::registerRC("printer.ps1.paper", "B4"); cout << "Registrations: " << n << ", " << n1 << endl; cout << "Values: " << Aipsrc::get(n) << ", " << Aipsrc::get(n1) << endl; n = Aipsrc::registerRC("foobar", "invalid"); Aipsrc::set(n1, "C4"); cout << "Registrations: " << n << ", " << n1 << endl; cout << "Values: " << Aipsrc::get(n) << ", " << Aipsrc::get(n1) << endl; Aipsrc::save(n1); } return 0; } casacore-3.7.1/casa/System/test/tAipsrcValue.cc000066400000000000000000000073301476623553700214120ustar00rootroot00000000000000//# tAipsrcValue.cc: This program tests the Aipsrc value interface //# Copyright (C) 1996,1997,1998,1999,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include int main(){ String aipsrcKeyword("my.double.test"); String aipsrcKeyword1("mine.double.test"); String aipsrcKeyword2("mine.bool.test"); String aipsrcValue; Double aVal; Bool bVal; Vector vVal; Aipsrc::find(aipsrcValue, aipsrcKeyword); cout << aipsrcKeyword << " " << aipsrcValue << endl; Aipsrc::find(aipsrcValue, aipsrcKeyword1); cout << aipsrcKeyword1 << " " << aipsrcValue << endl; AipsrcValue::find(aVal, aipsrcKeyword, 10.5); cout << aipsrcKeyword << " (D): " << aVal << endl; AipsrcValue::find(aVal, aipsrcKeyword1, 22.9); cout << aipsrcKeyword1 << " (D): " << aVal << endl; AipsrcValue::find(bVal, aipsrcKeyword2, True); cout << aipsrcKeyword2 << " (B): " << bVal << endl; AipsrcVector::find(vVal, aipsrcKeyword1); cout << aipsrcKeyword1 << " (V): " << vVal << endl; AipsrcValue::find(aVal, aipsrcKeyword1, "m/s", "km/s"); cout << aipsrcKeyword1 << " (Q): " << aVal << endl; cout << "AIPSROOT: " << Aipsrc::aipsRoot() << endl; cout << "AIPSARCH: " << Aipsrc::aipsArch() << endl; cout << "AIPSSITE: " << Aipsrc::aipsSite() << endl; cout << "AIPSHOST: " << Aipsrc::aipsHost() << endl; cout << "AIPSHOME: " << Aipsrc::aipsHome() << endl; { uInt n = AipsrcValue::registerRC(aipsrcKeyword, 100.05); uInt n1= AipsrcValue::registerRC(aipsrcKeyword1, 220.09); uInt n2= AipsrcValue::registerRC(aipsrcKeyword2, False); cout << "Registrations: " << n << ", " << n1 << ", " << n2 << endl; Double aVal1; aVal = AipsrcValue::get(n); aVal1 = AipsrcValue::get(n1); bVal = AipsrcValue::get(n2); cout << "Values: " << aVal << ", " << aVal1 << ", " << bVal << endl; n = AipsrcValue::registerRC(aipsrcKeyword, 2345); AipsrcValue::set(n1, 9876); cout << "Registrations: " << n << ", " << n1 << endl; aVal = AipsrcValue::get(n); aVal1 = AipsrcValue::get(n1); cout << "Values: " << aVal << ", " << aVal1 << endl; AipsrcValue::save(n); AipsrcValue::save(n1); AipsrcValue::save(n2); } return 0; } casacore-3.7.1/casa/System/test/tAppInfo.cc000066400000000000000000000075061476623553700205350ustar00rootroot00000000000000//# tAppInfo.cc: Test the AppInfo class //# Copyright (C) 1996,1997,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include int main() { Double tz = AppInfo::timeZone(); cout << "Timezone offset (hours) : " << tz*24.0 << endl; AlwaysAssertExit(tz == AppInfo::timeZone() && tz >= -1.0 && tz <= 1.0); { // Test the work directory stuff // First set the work directory list to a known state // We have to initialize the work directories first before // we register the directories we really want. Vector tmp = AppInfo::workDirectories(); tmp.resize(0); Vector dirs(3); dirs(0) = "."; dirs(1) = "/tmp"; dirs(2) = "/doesnotexist"; uInt index = AipsrcVector::registerRC( "user.directories.work", dirs); AipsrcVector::set(index, dirs); cerr << "\n\n=====Expect a single WARN level log message\n" << endl; tmp = AppInfo::workDirectories(); AlwaysAssertExit(tmp.nelements()==2 && tmp(0) == dirs(0) && tmp(1) == dirs(1)); // OK, now only have valid directories so we don't get all kinds // of log WARNings. AipsrcVector::set(index, tmp); tmp.resize(0); // Someday this test will fail! tmp = AppInfo::workDirectories(1000000); // Indeed this tests failed in Nov-2009 on a 2 TByte disk. // So outcommented the test. ///AlwaysAssertExit(tmp.nelements() == 0); // Check that we cycle through the valid directories tmp.resize(0); tmp = AppInfo::workDirectories(); String dir1 = AppInfo::workDirectory(); String dir2 = AppInfo::workDirectory(); AlwaysAssertExit(dir1 != dir2 && (dir1 == tmp(0) || dir2 == tmp(1)) && (dir2 == tmp(0) || dir2 == tmp(1))); String file = AppInfo::workFileName(); AlwaysAssertExit(file.contains(dir1) || file.contains(dir2)); AlwaysAssertExit(file.contains(Regex("/aipstmp_"))); file = AppInfo::workFileName(0, "foo_"); AlwaysAssertExit(file.contains(dir1) || file.contains(dir2)); AlwaysAssertExit(file.contains(Regex("/foo_"))); ///Bool caught = False; try { cerr << "=====Expect a single SEVERE level message\n"; file = AppInfo::workFileName(1000000); } catch (std::exception& x) { ///caught = True; } // Do not check if it failed or succeeded, because that is // system dependent (same reason as workDirectories test). ///AlwaysAssertExit(caught); } cerr << "OK" << endl; return 0; } casacore-3.7.1/casa/System/test/tAppState.cc000066400000000000000000000047601476623553700207210ustar00rootroot00000000000000//# tAppState.cc: This program tests the AppState interface //# Copyright (C) 2017 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include class MyState: public AppState { public: MyState( const std::list &init ) : my_path(init) { } std::list dataPath( ) const { return my_path; } bool initialized( ) const { return true; } private: std::list my_path; }; int main( int , char *[] ) { AlwaysAssertExit(AppStateSource::fetch( ).initialized( ) == false); std::list my_path_state; // in an actual implementation these would be paths // to casacore tables or images... but this is just // for demonstration... my_path_state.push_back("/usr/bin"); my_path_state.push_back("/bin"); my_path_state.push_back("/opt/local/bin"); my_path_state.push_back("/home/rh/root/usr/bin"); AppState *original_state = &AppStateSource::fetch( ); MyState *new_state = new MyState( my_path_state ); AppStateSource::initialize( new_state ); AlwaysAssertExit(&AppStateSource::fetch( ) != original_state); AlwaysAssertExit(&AppStateSource::fetch( ) == new_state); AlwaysAssertExit(AppStateSource::fetch( ).dataPath( ) == my_path_state); return 0; } casacore-3.7.1/casa/System/test/tCasarc01.cc000066400000000000000000000032641476623553700205330ustar00rootroot00000000000000//# tCasarc01.cc: This program tests the Casarc interface //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include int main() { casacore::Casarc &rc = casacore::Casarc::instance("tCasarc01_tmp.rc"); rc.put("viewer.dpg.position.mousetools", "top"); rc.put("viewer.dpg.position.mousetools", "right"); rc.put("viewer.dpg.position.mousetools", "top"); rc.put("viewer.dpg.position.mousetools", "left"); for ( casacore::Casarc::iterator iter = rc.begin(); iter != rc.end(); ++iter ) { std::cout << iter->first << ": " << iter->second << std::endl; } } casacore-3.7.1/casa/System/test/tChoice.cc000066400000000000000000000036601476623553700203700ustar00rootroot00000000000000//# tChoice.cc: Test program for class Choice //# Copyright (C) 2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include int main() { Vector choices(3); choices[0] = "no"; choices[1] = "yes"; choices[2] = "none"; // The default is no. AlwaysAssertExit (Choice::choice ("ask", choices) == "no"); // Set the choice function to asking on stdout. Choice::setChoiceFunc (Choice::stdoutChoice); // Ask the choice 3 times and show the result. std::cout << Choice::choice ("Give choice1", choices) << std::endl; std::cout << Choice::choice ("Give choice2", choices) << std::endl; std::cout << Choice::choice ("Give choice3", choices) << std::endl; return 0; } casacore-3.7.1/casa/System/test/tChoice.in000066400000000000000000000000301476623553700203750ustar00rootroot00000000000000no YES yes non no1 none casacore-3.7.1/casa/System/test/tChoice.out000066400000000000000000000004461476623553700206110ustar00rootroot00000000000000Give choice1 ([no],yes,none): no Give choice2 ([no],yes,none): 'YES' is an invalid answer; retry Give choice2 ([no],yes,none): yes Give choice3 ([no],yes,none): 'non' is an invalid answer; retry Give choice3 ([no],yes,none): 'no1' is an invalid answer; retry Give choice3 ([no],yes,none): none casacore-3.7.1/casa/System/test/tChoice.run000066400000000000000000000000471476623553700206030ustar00rootroot00000000000000$casa_checktool ./tChoice < tChoice.in casacore-3.7.1/casa/System/test/tObjectID.cc000066400000000000000000000053301476623553700206150ustar00rootroot00000000000000//# tObjectID.cc: This program tests the ObjectID class //# Copyright (C) 1994,1995,1996,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include void assert_hash(const ObjectID &id) { if (!id.isNull()) { // Hash bytes are: seq number, pid, creation time and hostname // The sequence number starts at zero, and PIDs and creation time // can be multiples of 256. At least the other field will always have // a bit set auto hash = hashFunc(id); AlwaysAssertExit(((hash >> 24) & 0xff) != 0); } } int main() { try{ // default ctor ObjectID ID1; ObjectID ID2; assert_hash(ID1); assert_hash(ID2); // inequality operator AlwaysAssertExit(ID1!=ID2); // assignment operator ID1 = ID2; // equality operator, too! AlwaysAssertExit(ID1 == ID2); assert_hash(ID1); assert_hash(ID2); // copy ctor ObjectID ID1copy(ID1); AlwaysAssertExit(ID1 == ID1copy); assert_hash(ID1copy); ObjectID null(True); AlwaysAssertExit(null.isNull()); String copied; ID1.toString(copied); ID1 = ObjectID(True); String error; AlwaysAssertExit(ID1.fromString(error, copied)); AlwaysAssertExit(ID1 == ID1copy); assert_hash(ID1); ID1.toString(copied); } catch (std::exception& x) { cerr << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/casa/Utilities.h000066400000000000000000000124231476623553700163610ustar00rootroot00000000000000//# Utilities.h: Bag of unrelated classes and groups for general use. //# Copyright (C) 1995,1996,1997,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_UTILITIES_H #define CASA_UTILITIES_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // // Classes and global functions for general use // // // // // This module is a bag of unrelated mini-modules, classes and // global functions. The following functional groups can be recognized: //
            //
          • Object utilities: //
              //
            • ObjCompare // objects with each other. A signature for comparison functions // is defined (required for comparison functions used in the // Sort // class), and one such function is provided. //
            • objcopy/objmove/objset // copies objects from one place to another. //
            • Mark // objects as valid or invalid. //
            • Notices // provide basic support for shared access of data by various objects. //
            • Sort // objects on one or more keys, in ascending or descending order. // Fast sorting // is provided for certain types of objects. //
            • Binary Search // templated functions for sorted containers (ascending or descending // order) are available. //
            • Linear Search // templated functions for unsorted containers are available. //
            //
          • Logical utilities: //
              //
            • Assertion // lets you throw an error when a condition in not fullfilled. //
            • Bit vectors // are an efficient method to keep True/False information on a set of // items or conditions. //
            //
          • Pointer utilities //
              //
            • Counted pointers // provide support for reference counting. //
            //
          • Datatype utilities //
              //
            • DataType // enumerates the possible data types in the table system. //
            • ValType // describes the data types and their undefined values. //
            //
          • Other utilities //
              //
            • Dynamic buffers // are used to store data in dynamically allocated buffers. //
            • Regular expressions // are supported by the class Regex // built on top of std::regex. //
            • Sequences // of any datatype can be derived from the base class // Sequence. // One example is uIntSequence, // provided for general use. //
            • Strings. // for the C++ preprocessor //
            //
          // // You may want to look at the individual header files // to see whether you might not prefer to include only the header // files you really need; it may be more efficient to do so. // // //
          //# //#
        206. //# // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Utilities/000077500000000000000000000000001476623553700162065ustar00rootroot00000000000000casacore-3.7.1/casa/Utilities/AlignMemory.cc000066400000000000000000000040411476623553700207370ustar00rootroot00000000000000//# AlignMemory.cc: Class to specify and calculate memory alignment //# Copyright (C) 2014 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include namespace casacore { //#Begin casa namespace void* AlignMemory::alloc (size_t size) const { void* ptr = 0; if (size > 0) { // posix_memalign alignment must be at least sizeof(void*). if (itsAlign >= sizeof(void*)) { int sts = posix_memalign (&ptr, itsAlign, size); if (sts != 0) { throw AllocError("posix_memalign (aligned alloc) failed for " + String::toString(size) + " bytes", size); } } else { ptr = malloc(size); if (ptr == 0) { throw AllocError("malloc failed for " + String::toString(size) + " bytes", size); } } } return ptr; } } //#End casa namespace casacore-3.7.1/casa/Utilities/AlignMemory.h000066400000000000000000000054621476623553700206110ustar00rootroot00000000000000//# AlignMemory.h: Class to specify and calculate memory alignment //# Copyright (C) 2014 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_ALIGNMEMORY_H #define CASA_ALIGNMEMORY_H #include #include namespace casacore { //#Begin casa namespace // Referenced counted pointer for constant data // // // // This class is Counted because it is reference counted. // // // This class implements a reference counting mechanism. It // allows CountedPtrs to be passed around freely, // incrementing or decrementing the reference count as needed when one // CountedPtr is assigned to another. When the // reference count reaches zero the internal storage is deleted by // default, but this behavior can be overridden. // // Internally the class uses std::shared_ptr to be thread-safe. Note that // tr1 is used if the compiler does not support C++11 yet. // // // Reference counting // class AlignMemory { public: // Default alignment is none. explicit AlignMemory (uInt alignment=0) : itsAlign(alignment) {} // Get the alignment. uInt alignment() const { return itsAlign; } // Allocate the given amount of memory with the correct alignment. // If alignment < sizeof(void*), malloc will be used, otherwise posix_memalign. // The alignment must be a power of 2 for posix_memalign to succeed. // It can be freed with the normal free. void* alloc (size_t size) const; private: uInt itsAlign; }; } //#End casa namespace #endif casacore-3.7.1/casa/Utilities/Assert.h000066400000000000000000000175641476623553700176350ustar00rootroot00000000000000//# Assert.h: Throw exceptions when Assertions fail. //# Copyright (C) 1993,1994,1995,1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_ASSERT_H #define CASA_ASSERT_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Utility class for Assert macros. // // // // //
        207. module Exceptions // // // Templated class assert_ is the basis for the macros // DebugAssertExit, DebugAssert, // AlwaysAssertExit, and AlwaysAssert which // form the "public interface" to the Assertion mechanism. // // // The present Assertion mechanism uses the exception // handling mechanism to throw the errors when an Assertion // fails. It can be used in two ways: //
          //
          DebugAssertExit(expr) //
          AlwaysAssertExit(expr) //
          cause the program to abort if expr evaluates to a // null value. This form is intended for the end users // because presumabily at their level there is no way to recover // from errors. //
          DebugAssert(expr, exception) //
          AlwaysAssert(expr, exception) //
          throw the specified exception if the expr is // null. This form is designed to be used by library // elements because it actually raises an exception which // can be later caught in the regular way. //
          // // DebugAssertExit and // DebugAssert are only invoked in // debug mode (i.e. when AIPS_DEBUG is defined); otherwise // they preprocess to null statements. AlwaysAssertExit // and AlwaysAssert are always invoked. // // // Class assert_ is internal to the // Assertion mechanism and should be undocumented. However, // documenting the class is the only way to document this mechanism, // which for the rest consists of preprocessor macros. // // //
          // // The implementation of the Array classes // contains many examples of the Assertion mechanism. The following // application of the Assertion mechanism is taken from the archive of // the aips2-workers@nrao.edu mail group (Brian Glendenning, 1994/03/23): // // I thought I'd readvertise a technique I use that helps me find // problems in the classes I write. I have found this to be an // EXTREMELY useful way of discovering bugs automatically (so the users // of your class don't have to manually). // // In your class, write an ok() member function that // returns a Bool. Allow for inheritance and make it a // virtual function (in fact, the derived class's ok() would // probably call the ok() from its parent, as well as doing // specific stuff for the derived class). // // Then in every member function, place a call to ok() in // an Assertion. Like this: // // DebugAssert(ok(), AipsError); // include aips/Assert.h in your .cc file // // // The second argument is the exception you want to throw. // AipsError will always do, although you can throw a // more particular one if you want to. This Assertion will not be in // production code -- i.e. if AIPS_DEBUG is not defined, the // above line will be a null statement. I place these lines at the entry // to all member functions (except I place them at the end of a // constructor!). (I normally don't put an Assertion in an inline // function). // // In the ok() function you should Assert a class's // invariants. This is more or less the same as Asserting that an // object's private and protected data are consistent. For // example, one of the simple tests I do in the array classes is Assert // that the number of elements (which I cache) is indeed equal to the // product of its shape (I do ~15 tests in the ok() for the // new Array class). // template class assert_ { public: // assert_(int expr, const char *msg) { if (! expr) throw(t(msg)); } assert_(const void *ptr, const char *msg) { if (! ptr) throw(t(msg)); } assert_(int expr, const char *msg, const char* file, Int line); assert_(const void *ptr, const char *msg, const char* file, Int line); // // A no-op, but it keeps g++ from complaining about "variable not used" // errors void null() {} }; // These marcos are provided for use instead of simply using the // constructors of assert_ to allow addition of line // numbers and file name in the future. // // DebugAssert and AlwaysAssert are designed to // be used by library elements because they actually raise an exception // which can later be later caught. // DebugAssertExit and AlwaysAssertExit are // intended to be used by the applications writer, because they cause an // exit(0). #define AlwaysAssert(expr, exception) \ {casacore::assert_ dummy_(expr, "Failed AlwaysAssert " #expr,__FILE__,(casacore::Int)__LINE__); dummy_.null(); } #define AlwaysAssertExit(expr) \ {casacore::assert_ dummy_(expr, "Unrecoverable AlwaysAssertExit: " #expr,__FILE__,(casacore::Int)__LINE__); dummy_.null();} #if defined(AIPS_DEBUG) //# The backslashes below have spaces after them to make the egcs // compiler happy # (otherwise it thinks they are multiline // // comments). If ever uncommented # the spaces should be removed. // #define DebugAssert(expr, exception) // (assert_ (expr, "Failed Assertion: " #expr)) // #define Assert(expr) // (assert_ (expr, "Unrecoverable Assertion: " #expr)) // #define DebugAssert(expr, exception) // (assert_ (expr, "Failed Assertion: " #expr,__FILE__,(Int)__LINE__)) // #define Assert(expr) // (assert_ (expr, "Unrecoverable Assertion: " #expr,__FILE__,(Int)__LINE__)) #define DebugAssert(expr, exception) \ {casacore::assert_ dummy_(expr, "Failed Assertion: " #expr,__FILE__,(casacore::Int)__LINE__); dummy_.null();} #define DebugAssertExit(expr) \ {casacore::assert_ dummy_(expr, "Unrecoverable Assertion: " #expr,__FILE__,(casacore::Int)__LINE__); dummy_.null();} #else #define DebugAssert(expr, exception) #define DebugAssertExit(expr) #endif } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/casa/Utilities/Assert.tcc000066400000000000000000000036271476623553700201520ustar00rootroot00000000000000//# Assert.cc: Throw exception when assertion fails. //# Copyright (C) 1993,1994,1995,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_ASSERT_TCC #define CASA_ASSERT_TCC #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template assert_::assert_(int expr, const char *msg, const char* file, Int line) { static char message[1024]; if (! expr) { snprintf(message,sizeof(message),"(%s : %i) %s",file,line,msg); throw(t(message)); } } template assert_::assert_(const void *ptr, const char *msg, const char* file, Int line) { static char message[1024]; if (! ptr) { snprintf(message,sizeof(message),"(%s : %i) %s",file,line,msg); throw(t(message)); } } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Utilities/BinarySearch.h000066400000000000000000000146031476623553700207350ustar00rootroot00000000000000//# BinarySearch.h: Binary search through linear, sorted, data structures //# Copyright (C) 1995,1996,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_BINARYSEARCH_H #define CASA_BINARYSEARCH_H //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Binary search a sorted, linear, data structure. // // // // // These binary search functions work on sorted, linear data structures // which have operator() or operator[] defined on them (e.g. // C-array, Vector, IPosition, Block, ScalarColumn, etc.) // Two versions of the functions are provided, one which uses // parentheses () for indexing, one which uses square brackets [] (obviously // the latter one can also be used for ordinary C-style pointers and arrays). // It is assumed that the container uses zero-based indexing. // // The container must be sorted (sorting is available through the // Sort and // GenSort // classes, and from various // Table sort functions). The returned index // is in the range [0..n] inclusive. That is, from the first element of the // container to one past the last element of the container (zero-based indices). // If the container is sorted in ascending order, the returned index is the // first one whose element is greater than or equal to the searched for value. // If it is sorted in descending order, the returned index is the first which // is less than or equal to the searched for value. That is, the returned // index gives the position at which the value would be inserted (possibly // either at the end, or requiring the existing values to be "pushed" to the // right) maintaining the sort order. Obviously index n can only be // returned if the value searched for is past the end of the array, thus // has to be inserted at the end. // // The functions determine for themselves whether the container is sorted in // ascending or descending order by comparing the first and last element. // // While normally you want to search a container with indices in the range // [0 ... n-1], any desired lower bound may be used instead. // // // The functions do not check if the container is valid, i.e. if // the container is sorted and if the container does not contain duplicate // values. // // // These functions loosely follow some written by Ger van Diepen in a more // specialized context. // // // // // Vector vi; // ... // Sets vi somehow // genSort(vi); // Int val; // Bool found; // while (cin >> val && val != -999) { // Int where = binarySearch(found, vi, val, vi.nelements()); // if (found) { // cout << "Found " << val << " at position " << where << endl; // } else { // cout << val << " is not in the vector, but it belongs at " << // where << endl; // } // } // // // // // I found that I (BEG) was writing binary search functions several times, // for example when checking whether the cached off and gain scans in time // sorted data needed to be refilled. It generally seems like a useful little // utility function. // // // //
        208. operator(Int) or operator[Int] needs to be defined. //
        209. The index must be zero based. //
        210. The result of that indexing must be an expression that can be // compared with an object of class ElType. Normally in fact it would // be a temporary of class ElType. // // //
        211. The less than operator (<) and greater than (>) operators need to // be defined, and have their usual ordering relations. // // // //
        212. I suspect that an implementation is possible that only calls // operator() or [] once during each evaluation of the while loop. //
        213. MACROize implementation so that code isn't repeated twice. Or, // possibly implement one using the other (e.g. by introducing an adapter // class that turns (i) into [i]. // // // Search container for value. There are assumed to be at least // n elements in the container. The container will be searched for // indices in the range [lower ... lower + n - 1] Return the index // of the first element which is greater than or equal to (ascending order) or // less than or equal to (descending order) the value. // // This version of the function is for containers that use () for indexing. template Int binarySearch(Bool &found, const Container &container, const ElType &value, uInt n, Int lower=0); // This version of the function is for containers that use [] for indexing. template Int binarySearchBrackets(Bool &found, const Container &container, const ElType &value, uInt n, Int lower=0); // // } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/casa/Utilities/BinarySearch.tcc000066400000000000000000000070671476623553700212650ustar00rootroot00000000000000//# BinarySearch.cc: Binary search through linear, sorted, data structures //# Copyright (C) 1995,1996,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_BINARYSEARCH_TCC #define CASA_BINARYSEARCH_TCC #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //#!!!!! If you change either of the following, make sure you change the other //#!!!!! similarly. template Int binarySearch(Bool &found, const Container &container, const ElType &value, uInt n, Int originalLower) { found = False; if (n == 0) { return 0; } Int lower = originalLower; Int upper = lower + n - 1; Int middle = 0; Bool ascending = (! (container(upper) < container(lower))); Bool toLeft, toRight; ElType midval; while (lower <= upper) { middle = (upper + lower) / 2; midval = container(middle); if (ascending) { toLeft = (value < midval); } else { toLeft = (value > midval); } if (toLeft) { upper = middle - 1; } else { if (ascending) { toRight = (value > midval); } else { toRight = (value < midval); } if (toRight) { middle++; lower = middle; } else { // exact match, but still we want to get to the beginning of // sequence upper = middle - 1; found = True; } } } return middle; } template Int binarySearchBrackets(Bool &found, const Container &container, const ElType &value, uInt n, Int originalLower) { found = False; if (n == 0) { return 0; } Int lower = originalLower; Int upper = lower + n - 1; Int middle = 0; Bool ascending = (! (container[upper] < container[lower])); Bool toLeft, toRight; ElType midval; while (lower <= upper) { middle = (upper + lower) / 2; midval = container[middle]; if (ascending) { toLeft = (value < midval); } else { toLeft = (value > midval); } if (toLeft) { upper = middle - 1; } else { if (ascending) { toRight = (value > midval); } else { toRight = (value < midval); } if (toRight) { middle++; lower = middle; } else { // exact match, but still we want to get to the beginning of // sequence upper = middle - 1; found = True; } } } return middle; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Utilities/BitVector.cc000066400000000000000000000147141476623553700204250ustar00rootroot00000000000000//# BitVector.cc: A BitVector class with variable bit vector size //# Copyright (C) 1993,1994,1995,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN BitVector::BitVector() : size_p (0), bits_p (0) {} BitVector::BitVector (uInt length, Bool state) : size_p (length), bits_p ((length + WORDSIZE - 1) / WORDSIZE, uInt(0)) { if (state) { set (state); } } BitVector::BitVector (const BitVector& that) : size_p (that.size_p), bits_p (that.bits_p) {} BitVector::~BitVector() {} BitVector& BitVector::operator= (const BitVector& that) { size_p = that.size_p; bits_p = that.bits_p; return *this; } BitVector& BitVector::operator= (Bool state) { set (state); return *this; } void BitVector::putBit (uInt pos, Bool state) { if (state) { setBit (pos); }else{ clearBit (pos); } } Bool BitVector::toggleBit (uInt pos) { Bool result = getBit (pos); putBit (pos, (!result)); return result; } Bool BitVector::getBit (uInt pos) const { DebugAssert (pos < size_p, AipsError); uInt index = pos/WORDSIZE; Bool result = True; if ((bits_p[index] & (1 << (pos - index*WORDSIZE))) == 0) { result = False; } return result; } void BitVector::resize (uInt length, Bool state, Bool copy) { //# Do a true resize. uInt oldSize = size_p; bits_p.resize ((length + WORDSIZE - 1) / WORDSIZE, True, copy); size_p = length; if (!copy) { set (state); }else{ if (length > oldSize) { set (oldSize, length-oldSize, state); } } } void BitVector::set (Bool state) { uInt value = 0; if (state) { value = ~value; } for (uInt i=0; i size_p) { throw (AipsError ("BitVector::set past end-of-vector")); } if (length == 0) { return; } //# Determine the full words that can be set. //# When setting till the end of the vector, make endWord last word. uInt beginWord = (start + WORDSIZE - 1) / WORDSIZE; uInt endWord = end / WORDSIZE; if (end == size_p) { endWord = bits_p.nelements(); } uInt i; //# When there are no full words, we have to do part of a word only. if (beginWord >= endWord) { for (i=start; i size_p) { throw (AipsError ("BitVector::set past end-of-thisvector")); } if (thatStart+length > that.size_p) { throw (AipsError ("BitVector::set past end-of-thatvector")); } for (uInt i=0; i #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class BitVectorHelper; // The size of a unsigned Integer ( assumes 8-bit char ) const uInt WORDSIZE = sizeof(uInt)*8; // // Bit vectors of any size // // // // // A variable utilized as a discrete collection of bits is referred // to as a bit vector. // // // Bit vectors are an efficent method of keeping True/False // information on a set of items or conditions. Class BitVector // provides functions to manipulate individual bits in the vector and // to perform logical operations on whole bit vectors. // // // // // Create a bit vector with 20 bits (and set them all to False). // BitVector bv (20, False); // // // Change some individual bits: // // Turn On (make True) bit 19. // bv.setBit (19); // // Turn Off (make False) bit 12 (superfluous here). // bv.clearBit (12); // // Toggle bit 5 (here: change value from 0 (False) to 1 (True)). // bv.toggleBit (5) // // Another way of setting a bit using the index operator. // bv[0] = True; // // Assign the value of bit 0 to bit 1 (in three ways). // bv[1] = bv.getBit(0); // bv[1] = bv[0]; // bv.putBit (1, bv.getBit(0)); // // // Show the bit vector size and its value on standard output. // cout << "Size of bit vector: "<< b.nbits() <<"\n"; // cout << "Value of bit vector: "<< bv <<"\n"; // // // Perform logical operations on bit vectors. // // Create two more bit vectors. // BitVector bv2 (40, False); // BitVector bv3 (40, True); // // bitwise OR // bv = bv2 | bv3; // // bitwise AND // bv = bv2 & bv3; // // bitwise XOR // bv = bv2 ^ bv3; // // bitwise NOT // bv = ~bv2; // // // Reset all bits to False, and then to True // bv = False; // bv.set (True); // // Change the vector's size to 10 (and copy the old values). // bv.resize (10); // // Change back to original size and set all bits to True. // void bv.resize (size, True, False); // // class BitVector { public: // BitVectorHelper is a helper class. friend class BitVectorHelper; // Create a bit vector of length 0. BitVector (); // Create a bit vector with length bits // and set all bits to to the specified state. BitVector (uInt length, Bool state); // Copy constructor (copy semantics). BitVector (const BitVector& that); // Delete the bit vector. ~BitVector (); // Assignment (copy semantics). BitVector& operator= (const BitVector& that); // Set all bits to the given state. BitVector& operator= (Bool state); // Return the number of bits in the bitvector. uInt nbits() const; // Set a bit at the given position (0-relative). // In debug-mode an exception is thrown when the position is invalid. void setBit (uInt pos); // Clear a bit at the given position (0-relative). // In debug-mode an exception is thrown when the position is invalid. void clearBit (uInt pos); // Toggle a bit at the given position (0-relative). // It returns the original state. // In debug-mode an exception is thrown when the position is invalid. Bool toggleBit (uInt pos); // Get a bit at the given position (0-relative). // In debug-mode an exception is thrown when the position is invalid. Bool getBit (uInt pos) const; // Set a bit at the given position (0-relative) to the given state. // In debug-mode an exception is thrown when the position is invalid. void putBit (uInt pos, Bool state); // Index operator to access the specified bit. // In debug-mode an exception is thrown when the position is invalid. // Bool operator[] (uInt pos) const; BitVectorHelper operator[] (uInt pos); // // Logical operations on whole bit vectors. // The binary operators & (bitwise // AND), | (bitwise OR) and ^ (bitwise XOR), // and the unary operator ~ (bitwise NOT) are provided. // An exception is thrown if the lengths of the vectors differ. // BitVector operator& (const BitVector& that) const; BitVector operator| (const BitVector& that) const; BitVector operator^ (const BitVector& that) const; BitVector operator~ () const; // // Logical in-place operations on whole bit vectors. // The binary operators & (bitwise // AND), | (bitwise OR) and ^ (bitwise XOR), // and the unary operator reverse (bitwise NOT) are provided. // An exception is thrown if the lengths of the vectors differ. // void operator&= (const BitVector& that); void operator|= (const BitVector& that); void operator^= (const BitVector& that); void reverse (); // // Returns True if all bits are equal. // An exception is thrown if the lengths of the vectors differ. Bool operator== (const BitVector& that) const; // Returns True if a bit differs. // An exception is thrown if the lengths of the vectors differ. Bool operator!= (const BitVector& that) const; // Resize the bit vector to the new length. // By default the original bits are copied. // The remaining bits (or all bits in case of no copy) are // set the the given state. void resize (uInt length, Bool state=False, Bool copy=True); // Set all bits of the bit vector to the specified state. void set (Bool state); // Set length bits starting at the start position // (0-relative) to the given state. // An exception is thrown if start+length exceeds the length // of the vector. void set (uInt start, uInt length, Bool state); // Copy length bits starting at thatStart in the // other BitVector to this BitVector starting at thisStart. void copy (uInt thisStart, uInt length, const BitVector& that, uInt thatStart); // Write a representation of the bit vector (a list of // zeros and ones enclosed in square // parentheses) to ostream. friend ostream& operator<< (ostream&, const BitVector& vector); private: // Number of bits in the BitVector object. uInt size_p; // Pointer to the actual bit vector, stored as a contiguous // sequence of one or more unsigned integers. Block bits_p; }; // Helper class for BitVector // // // //
        214. class BitVector // // // Helper class for class BitVector. // For all practical purposes a BitVectorHelper object is the individual bit in // a bit vector. It is the object returned by the index operator of // BitVector. // class BitVectorHelper { friend class BitVector; public: // Copy constructor has to be public. BitVectorHelper (const BitVectorHelper& that); // Set the bit to the state of the bit in the other BitVector. // Thus assignment has not the usual copy semantics, but affects // the underlying BitVector bit. const BitVectorHelper& operator= (const BitVectorHelper& that) const; // Set to a state. const BitVectorHelper& operator= (Bool state) const; // Defines the conversion from BitVectorHelper to // Bool. operator Bool() const; private: uInt bitNumber_p; // Pointer back to the original vector. BitVector* vecPtr_p; // The constructor we actually use. BitVectorHelper (uInt bitNumber, BitVector* vector); }; inline void BitVector::setBit (uInt pos) { DebugAssert (pos < size_p, AipsError); uInt index = pos/WORDSIZE; bits_p[index] |= (1 << (pos - index*WORDSIZE)); } inline void BitVector::clearBit (uInt pos) { DebugAssert (pos < size_p, AipsError); uInt index = pos/WORDSIZE; bits_p[index] &= (~ (1 << (pos - index*WORDSIZE))); } inline Bool BitVector::operator[] (uInt pos) const { return getBit (pos); } inline uInt BitVector::nbits() const { return size_p; } inline BitVectorHelper::BitVectorHelper (uInt bitNumber, BitVector* vector) : bitNumber_p (bitNumber), vecPtr_p (vector) {} inline BitVectorHelper BitVector::operator[] (uInt pos) { return BitVectorHelper (pos, this); } inline BitVectorHelper::BitVectorHelper (const BitVectorHelper& that) : bitNumber_p (that.bitNumber_p), vecPtr_p (that.vecPtr_p) {} inline const BitVectorHelper& BitVectorHelper::operator= (Bool state) const { vecPtr_p->putBit (bitNumber_p, state); return *this; } inline BitVectorHelper::operator Bool() const { return vecPtr_p->getBit (bitNumber_p); } inline const BitVectorHelper& BitVectorHelper::operator= (const BitVectorHelper& that) const { vecPtr_p->putBit (bitNumber_p, that.vecPtr_p->getBit (that.bitNumber_p)); return *this; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Utilities/CASATask.h000066400000000000000000000046071476623553700177200ustar00rootroot00000000000000//# CasaTask.h: //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef _CASA_TASK_H #define _CASA_TASK_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Task interface // // // // //
        215. SomeClass //
        216. SomeOtherClass //
        217. some concept // // // // // // // // // // // // // // // //
        218. //
        219. // // // //
        220. //
        221. // // // //
        222. add this feature //
        223. fix this bug //
        224. start discussion of this possible extension // class CASATask { public: CASATask(const Record ¶ms) : pset(params) { } CASATask(const RecordDesc &desc) { pset = Record(desc); } virtual ~CASATask() { } Record getParams() const { return pset; } void setParams(const Record ¶ms) { pset = params; } private: CASATask(); Record pset; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Utilities/COWPtr.h000066400000000000000000000303131476623553700174750ustar00rootroot00000000000000//# COWPtr.h: this defines the Copy-On-Write-Pointer class. //# Copyright (C) 1996,1997,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_COWPTR_H #define CASA_COWPTR_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Copy-On-Write-Pointer class - allows control of copy based on constness. // // // // // //
        225. none // // // // The COWPtr class name is a contraction of Copy-On-Write-Pointer // and is a reflection of its role as a carrier of objects which need to // minimize their copying and control their destruction. Such objects only // need to copy if written to. // // // // COWPtr can be used by other classes to implement copy-on-write // semantics. Copy-on-write means that a copy of an object is not // made until necessary. A well-known example is a String class // with internally a pointer to a StringRep containing the true string. // When a copy of a String is made, the StringRep is not copied yet. // Only when the String gets changed and when more than one String // points to the same StringRep, a copy of the StringRep is made. // This technique can prevent a lot of copying when arguments are // passed by value. //
          // Implementing a String in this way is straightforward when // String defines the pointer to its StringRep as COWPtr // and uses the appropriate functions (ref() and rwRef()) to execute // const and non-const StringRep functions. //
          // An example of this (straightforward) usage is class // RecordDesc. //

          // COWPtr offers possibilities for more advanced usage: //

            //
          • Normally a copy (on write) is made when more than one String points to // the same StringRep. By constructing the COWPtr object with // readOnly=True, it is possible to already do that when only one // String points to a StringRep. This can be used when a function // returns an object referencing a constant object. For instance, // a function can return an Array object referencing another Array // which should not be altered. // By returning a COWPtr with readOnly=True, // it is assured that a copy is made as soon as somebody wants // to change the returned Array object. No (expensive) copy is // made when only const access is being done. //
          • Normally the COWPtr object takes over the pointer and deletes // the underlying object when it is not used anymore. With the // deleteIt flag it is possible to change this behavior. //
          //

          // Apart from the fact that COWPtr handles the copying, it has // the big advantage that it forces that its access functions (ref and // rwRef) are used in the correct way (ie. ref() for a const // function and rwRef() for a non-const function). This ensures that // copies are made when needed and not made when not needed. //

          // Note that COWPtr uses the default constructor and the assignment // operator to make a copy (thus not the copy constructor). The // reason for this is that the copy constructor of some classes // (e.g. Array) has reference semantics iso. copy semantics. // // // //

          Example 1:

          // // class String { // public: // // The constructor allocates a StringRep and hands to pointer // // to COWPtr. // String() // : itsRep (new StringRep;) {} // // This non-const function needs rwRef to make a copy when needed. // void set (const char* str) {itsRep.rwRef().set (str);} // // This const function can use ref (making a copy is not needed). // const char* get const {return itsRep.ref();} // private: // COWPtr itsRep; // }; // class StringRep { // friend class String; // private: // void set (const char*); // const char* get() const; // char* itsData; // }; // //

          Example 2:

          // This function requires a const Array be passed out from the local scope. // The Array is created with non-const functions out of necessity (i.e. no // const versions of the Array::getSlice() function exist.) Preventing // copies of the Array from being made forces us to use a COWPtr. The COWPtr // has arguments which allow us to declare the Array as const and not make // any copies until a write operation is performed. // // void myFunc(COWPtr > &obj){ // // make a nonconst from some static const Array that exists "out there" // Array &nonConstArray = (Array &)staticConstArray; // // "fill" the COWPtr and bring back constness without copying. The first // // "True" argument indicates the caller of this function may take // // control of the dynamic pointer's destruction. The second "True" // // argument indicates the array is read only and should make a copy of // // itself if writing is needed. // obj.set(new Array(nonConstArray.getSlice(...), True, True)); // } // // The caller of the function will get their piece of a const array without // making a copy until the last possible moment (maybe never.) // // #include // main(){ // // create a null filled COWPtr // COWPtr > COW; // // fill it inside myfunc // myFunc(COW); // // use a single element - still no copies have been made! // Float someVal = COW->operator()(IPosition(2,3,3)) // // write to the array - now we get a copy! // COW.rwRef().set(42.0f); // // etc... // }; // // // // // Three words; efficiency, efficiency, efficiency. Not everything may be // passed as a reference. With COWPtrs we may fake it. // // // //
        226. default constructor //
        227. assignment operator // // // //
        228. AipsError // // // //
        229. none // template class COWPtr { private: // Helper class to make deletion of object optional. class Deleter { public: Deleter (Bool deleteIt) : deleteIt_p (deleteIt) {} void operator() (T * data) const { if (deleteIt_p) delete data; } private: Bool deleteIt_p; }; public: // The default constructor: used to create a null pointer which is // delete-able by the destructor. It is not "readOnly" so that it may be // changed by the COWPtr::set() function. inline COWPtr(); // The dynamic "pointer to object" constructor: default behavior is to // delete the allocated memory when this instance's of COWPtr is destructed. // Or the Boolean argument of "deleteIt = False" implies the pointer is // being maintained by an object other than this instance of COWPtr and // will not delete the allocated memory upon this instance's destruction. // Control of copying is provided by the Boolean "readOnly" argument. The // default value of "readOnly = False" forces a copy if the number of // references to the dynamic memory is greater than one. Copying is always // done if the constructor is given an argument of "readOnly = True". // The only copying done (if ever) is upon a call to // COWPtr::rwRef(). explicit COWPtr(T *obj, Bool deleteIt = True, Bool readOnly = False); // copy ctor with reference semantics inline COWPtr(const COWPtr &other); // assignment operator with reference semantics inline COWPtr &operator=(const COWPtr &other); // return a pointer to a const object. This prevents "write" operations. inline const T *operator->() const; // return a reference to a const object. This prevents "write" operations. inline const T &operator*() const; // Function used to change this instance of COWPtr. The pointer must be // dynamically allocated. Default behavior is to // delete the allocated memory when this instance's of COWPtr is destructed. // Or the Boolean argument of "deleteIt = False" implies the pointer is // being maintained by an object other than this instance of COWPtr and // will not delete the allocated memory upon this instance's destruction. // Control of copying is provided by the Boolean "readOnly" argument. The // default value of "readOnly = False" forces a copy if the number of // references to the dynamic memory is greater than one. Copying is always // done if the constructor is given an argument of "readOnly = True". // The only copying done (if ever) is upon a call to // COWPtr::rwRef(). // void set(T *obj, Bool deleteIt = True, Bool readOnly = False); // return a const reference to the object. inline const T &ref() const; // return a readable and writable reference to this instance. Instances of // COWPtr constructed with argument "readOnly = True" will be made a copy. // Additionally, all instances of COWPtr with more than one reference to // the allocated memory stored within will be copied. inline T &rwRef(); // returns False if this contains a non-null ptr, otherwise, return True. inline Bool isNull() const; // returns True if the object is const, otherwise, return False. inline Bool isReadOnly() const; // returns True if the object is the only instance, otherwise, return False. inline Bool isUnique() const; // Return True if copied, otherwise, False. This function will make this // instance's object a copy if it is constructed with // "readOnly = True." Additionally, all instances of COWPtr with more // than one reference to the allocated memory stored within will be // copied. Bool makeUnique(); protected: std::shared_ptr obj_p; Bool const_p; }; //# Make our own default pointer - deleteIt==True by default, const_p==False template inline COWPtr::COWPtr() : obj_p (nullptr, Deleter(True)), const_p (False) { // does nothing } //# copy ctor with reference semantics template inline COWPtr::COWPtr(const COWPtr &other) : obj_p (other.obj_p), const_p (other.const_p) { // does nothing } //assignment operator with reference semantics template inline COWPtr &COWPtr::operator=(const COWPtr &other) { if (this != &other) { obj_p = other.obj_p; const_p = other.const_p; } return *this; } template inline const T *COWPtr::operator->() const { return obj_p.operator->(); } template inline const T &COWPtr::operator*() const { return obj_p.operator*(); } template inline const T &COWPtr::ref() const { return *obj_p; } template inline T &COWPtr::rwRef() { makeUnique(); return *obj_p; } template inline Bool COWPtr::isNull() const { return !obj_p; } template inline Bool COWPtr::isReadOnly() const { return const_p; } template inline Bool COWPtr::isUnique() const { return (const_p || obj_p.use_count()>1) ? False : True; } } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/casa/Utilities/COWPtr.tcc000066400000000000000000000042351476623553700200230ustar00rootroot00000000000000//# COWPtr.cc: this defines the Copy-On-Write-Pointer class. //# Copyright (C) 1996 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_COWPTR_TCC #define CASA_COWPTR_TCC #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template COWPtr::COWPtr(T *obj, Bool deleteIt, Bool readOnly) : obj_p (obj, Deleter(deleteIt)), const_p (readOnly) { // does nothing } template void COWPtr::set(T *obj, Bool deleteIt, Bool readOnly) { obj_p = std::shared_ptr(obj, Deleter(deleteIt)); const_p = readOnly; } // make this a copy if more than one exists. template Bool COWPtr::makeUnique() { Bool madeCopy = False; if (const_p || obj_p.use_count() > 1) { // A copy has to be made. // Use default ctor and assignment because copy ctor of e.g. Array // has reference semantics. std::shared_ptr tmp(new T, Deleter(True)); *tmp = *obj_p; obj_p.swap (tmp); const_p = False; madeCopy = True; } return madeCopy; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Utilities/Compare.cc000066400000000000000000000034041476623553700201040ustar00rootroot00000000000000//# Compare.cc: Non-templated code to compare two objects //# Copyright (C) 2013 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include namespace casacore { //# NAMESPACE CASACORE - BEGIN CompareNoCase::~CompareNoCase() {} int CompareNoCase::comp(const void * obj1, const void * obj2) const { const String& v1 = *static_cast(obj1); const String& v2 = *static_cast(obj2); return fcompare (v1, v2); } CompareAlwaysTrue::~CompareAlwaysTrue() {} int CompareAlwaysTrue::comp(const void * obj1, const void * obj2) const { (void)obj1; // Avoid compiler warning (void)obj2; return 0; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Utilities/Compare.h000066400000000000000000000156631476623553700177600ustar00rootroot00000000000000//# Compare.h: compare two objects of the same type //# Copyright (C) 1994,1995,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_COMPARE_H #define CASA_COMPARE_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // signature of comparison functions // // // // This typedef defines the signature of the comparison functions used // in, for instance, the Sort class: functions // with two const void* arguments returning an // int value. One such function is defined in the // ObjCompare class. // // typedef int ObjCompareFunc (const void*, const void*); // // abstract base class for comparing two objects // // // // // The abstract class BaseCompare is used for comparisons // in sorting or iterating. One can derive a concrete comparison class // from it. // class BaseCompare { public: virtual ~BaseCompare() {} // Compare two objects, and return. //
            //
          • -1 if obj1 < obj2; //
          • 0 if obj1 == obj2; //
          • 1 otherwise. //
          virtual int comp (const void* obj1, const void* obj2) const = 0; // Get the data type of a straight-forward sort comparison in ObjCompare. // It is used to test if a the faster GenSortIndirect can be used. // By default it returns TpOther. virtual DataType dataType() const { return TpOther; } }; // compare two objects // // // // The templated class ObjCompare really is only a place // holder for the static function compare which compares two // objects of type T. // // //
        230. operator== //
        231. operator< // template class ObjCompare: public BaseCompare { public: virtual ~ObjCompare(); // Compare two objects, and return //
            //
          • -1 if obj1 < obj2; //
          • 0 if obj1 == obj2; //
          • 1 otherwise. //
          // The static function is not inlined allowing one to take the address of // it. Furthermore, the function's signature agrees with // ObjCompareFunc. static int compare (const void* obj1, const void* obj2); virtual int comp (const void* obj1, const void* obj2) const; // Get the data type of the sort comparison. virtual DataType dataType() const; }; // Integer comparison class with intervals // // // // This class is meant for comparison in the TableIterator class. // It does not compare on the value itself, but compares intervals. // In that way it is possible to iterate through a table in, for example, // time chunks of N seconds. The start value X gives the start value of // the base interval. Lower intervals are still possible. // So the intervals will be ..., X-2N:X-N, X-N:N, X:X+N, X+N:X+2N, ... // template class CompareIntervalInt : public BaseCompare { public: // Construct from the given interval values. CompareIntervalInt(Int64 interval, Int64 start); virtual ~CompareIntervalInt(); // Compare the interval the left and right value belong to. virtual int comp(const void * obj1, const void * obj2) const; private: Int64 itsInterval; Int64 itsStart; }; // Real comparison class with intervals // // // // This class is meant for comparison in the TableIterator class. // It does not compare on the value itself, but compares intervals. // In that way it is possible to iterate through a table in, for example, // time chunks of N seconds. The start value X gives the start value of // the base interval. Lower intervals are still possible. // So the intervals will be ..., X-2N:X-N, X-N:N, X:X+N, X+N:X+2N, ... // template class CompareIntervalReal : public BaseCompare { public: // Construct from the given interval values. CompareIntervalReal(Double interval, Double start); virtual ~CompareIntervalReal(); // Compare the interval the left and right value belong to. virtual int comp(const void * obj1, const void * obj2) const; private: Double itsInterval; Double itsStart; }; // Case-insensitive string comparison class // // // // This class is meant for an case-insensitive comparison in a sort // or table iteration. // class CompareNoCase : public BaseCompare { public: virtual ~CompareNoCase(); // Compare the left and right string value in a case-insensitive way. virtual int comp(const void * obj1, const void * obj2) const; }; // Comparison class that is always true // // This class is meant to always give true and can be used to ensure // that all the values of a given column are grouped together. // class CompareAlwaysTrue : public BaseCompare { public: virtual ~CompareAlwaysTrue(); // Comparison function that gives always true virtual int comp(const void * obj1, const void * obj2) const; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/casa/Utilities/Compare.tcc000066400000000000000000000063271476623553700202770ustar00rootroot00000000000000//# Compare.cc: Templated function to compare two objects //# Copyright (C) 1994,1995 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_COMPARE_TCC #define CASA_COMPARE_TCC #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template ObjCompare::~ObjCompare() {} template int ObjCompare::compare (const void* obj1, const void* obj2) { return (*(const T*)obj1 < *(const T*)obj2 ? -1 : (*(const T*)obj1 == *(const T*)obj2 ? 0 : 1)); } template int ObjCompare::comp (const void* obj1, const void* obj2) const { return (*(const T*)obj1 < *(const T*)obj2 ? -1 : (*(const T*)obj1 == *(const T*)obj2 ? 0 : 1)); } template DataType ObjCompare::dataType() const { return whatType(); } template CompareIntervalInt::CompareIntervalInt(Int64 interval, Int64 start) : itsInterval(interval), itsStart(start) {} template CompareIntervalInt::~CompareIntervalInt() {} template int CompareIntervalInt::comp(const void * obj1, const void * obj2) const { Int64 v1 = *static_cast(obj1); Int64 v2 = *static_cast(obj2); // Shortcut if values are equal. if (v1 == v2) return 0; // The times are binned in bins with a width of itsInterval. Int64 t1 = (v1-itsStart) / itsInterval; Int64 t2 = (v2-itsStart) / itsInterval; return (t1==t2 ? 0 : (t1 CompareIntervalReal::CompareIntervalReal(Double interval, Double start) : itsInterval(interval), itsStart(start) {} template CompareIntervalReal::~CompareIntervalReal() {} template int CompareIntervalReal::comp(const void * obj1, const void * obj2) const { T v1 = *static_cast(obj1); T v2 = *static_cast(obj2); // Shortcut if values are equal. if (v1 == v2) return 0; // The times are binned in bins with a width of interval_p. Double t1 = std::floor((v1 - itsStart) / itsInterval); Double t2 = std::floor((v2 - itsStart) / itsInterval); return (t1==t2 ? 0 : (t1 #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN CompositeNumber::CompositeNumber(const uInt maxval) { itsMaxComplete = maxval; if (itsMaxComplete < 2) { itsMaxComplete = 2; } generate(itsMaxComplete); } void CompositeNumber::generate(const uInt maxval) { itsMaxComplete = maxval; uInt n2 = (uInt)(log((Float)maxval)/log(2.0) + 1) +1; uInt n3 = (uInt)(log((Float)maxval)/log(3.0) + 1) +1; uInt n5 = (uInt)(log((Float)maxval)/log(5.0) + 1) +1; itsNumbers.resize(n2*n3*n5); uInt n = 0; for (uInt i2=0; i2::sort(itsNumbers, n2*n3*n5); } CompositeNumber::~CompositeNumber() {} uInt CompositeNumber::nextLarger(const uInt testValue) { if (testValue > itsMaxComplete) { generate(testValue); } for (uInt i=0;i< itsNumbers.nelements(); i++) { if (itsNumbers[i] > testValue) { return itsNumbers[i]; } } return itsNumbers[0]; } uInt CompositeNumber::nextSmaller(const uInt testValue) { if (testValue > itsMaxComplete) { generate(testValue); } for (Int i=itsNumbers.nelements()-1; i>=0; i--) { if (itsNumbers[i] < testValue) { return itsNumbers[i]; } } return itsNumbers[0]; } uInt CompositeNumber::nearest(const uInt testValue) { if (testValue > itsMaxComplete) { generate(testValue); } for (uInt i=0;i< itsNumbers.nelements(); i++) { if (itsNumbers[i] > testValue) { if (i==0) { return itsNumbers[0]; } else if (abs((Int)(itsNumbers[i]-testValue)) < abs((Int)(itsNumbers[(i-1)]-testValue)) ) { return itsNumbers[i]; } else { return itsNumbers[(i-1)]; } } } // Should never make it here! return itsNumbers[0]; } uInt CompositeNumber::nextLargerEven(const uInt testValue) { if (testValue > itsMaxComplete) { generate(testValue); } for (uInt i=0;i< itsNumbers.nelements(); i++) { if (itsNumbers[i] > testValue && (itsNumbers[i]%2==0)) { return itsNumbers[i]; } } return itsNumbers[0]; } uInt CompositeNumber::nextSmallerEven(const uInt testValue) { if (testValue > itsMaxComplete) { generate(testValue); } for (Int i=itsNumbers.nelements()-1; i>=0; i--) { if (itsNumbers[i] < testValue && (itsNumbers[i]%2==0)) { return itsNumbers[i]; } } return itsNumbers[0]; } uInt CompositeNumber::nearestEven(const uInt testValue) { uInt up = nextLargerEven( testValue ); uInt down = nextSmallerEven( testValue ); if (abs((Int)(up-testValue)) < abs((Int)(down-testValue)) ) { return up; } else { return down; } } Bool CompositeNumber::isComposite(const uInt testValue) { if (testValue > itsMaxComplete) { generate(testValue); } for (uInt i=0;i< itsNumbers.nelements(); i++) { if (itsNumbers[i] == testValue) { return True; } } return False; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Utilities/CompositeNumber.h000066400000000000000000000055511476623553700215000ustar00rootroot00000000000000//# CompositeNumber.h: generate a composite number //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_COMPOSITENUMBER_H #define CASA_COMPOSITENUMBER_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // This class generates composite numbers // // // This class generates a list of composite numbers made up // of powers of 2, 3, and 5, which are less than // some max value and returns the smallest composite number greater // than some number given. // // // // CompositeNumber cn(1000); // Int n = cn.nextLarger(319); // Int m = cn.nextSmaller(462); // Int l = cn.nearest(462); // // class CompositeNumber { public: // constructor: // Note: if you later make a call with value > maxval, we // will recalculate the list of composite numbers CompositeNumber (const uInt maxval = 8192); // destructor ~CompositeNumber(); // return the next larger composite number uInt nextLarger(const uInt value); // return the next smaller composite number uInt nextSmaller(const uInt value); // return the nearest composite number uInt nearest(const uInt value); // return the next larger even composite number uInt nextLargerEven(const uInt value); // return the next smaller even composite number uInt nextSmallerEven(const uInt value); // return the closest even composite number uInt nearestEven(const uInt value); // returns True is value is composite Bool isComposite(const uInt value); private: Block itsNumbers; uInt itsMaxComplete; void generate(const uInt maxval); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Utilities/Copy.h000066400000000000000000000230651476623553700172770ustar00rootroot00000000000000//# Copy.h: Copy objects from one C-style array to another. //# Copyright (C) 1994-1997,1999-2002,2005,2015 //# Associated Universities, Inc. Washington DC, USA. //# National Astronomical Observatory of Japan //# 2-21-1, Osawa, Mitaka, Tokyo, 181-8588, Japan. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_COPY_H #define CASA_COPY_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Copy objects from one C-style array to another. // // // // // // Objset is used to fill a C-style array of objects. // // Objcopy and objmove are used to copy objects from one place to // another. Optionally a stride can be supplied. // // The functions are equivalent to C's memcpy and // memmove. // When possible C++ standard library functions are used to implement them // // Similar to memcpy and memmove, the difference between objcopy // and objmove is that objmove takes account of an overlap of source and // destination. In general, objcopy is slighty (but only slighty) faster. // // // Setting and copying arrays of built-in types: // // // Create int array of 4 elements // size_t size=4; // int* ia = new int[size]; // // Initialize all elements to value 99 // objset(ia, 99, size); // // Change all odd elements to 66 -> [99 66 99 66] // objset(ia+1, 66, 5, 2); // // // Create another 4-element int array // int* ia2 = new int[size]; // // Copy array ia into array ia2 -> [99 66 99 66] // objmove(ia2, ia, size); // // Copy the even elements of ia to the odd elements of ia2 // // -> [99 99 99 99] // objcopy(ia2+1, ia, size/2, 2, 2); // // // Setting and copying arrays of a randomly chosen type: // // // Create 4-element array of 3-element Block objects // size_t size=4; // Block* ta = new Block[size]; // Block set(3); // // Initialize the array -> [[123][123][123][123]] // set[0] = 1; set[1] = 2; set[2] = 3; // objset(ta, set, size); // // Change odd Blocks to [777]-> [[123][777][123][777]] // set[0] = set[1] = set[2] = 7; // objset(ta + 1, set, size/2, 2); // // // Create another Block array // Block* ta2 = new Block[size]; // // Copy the even elements of ta to the first elements of ta2 // // -> [[123][123]...] // objcopy(ta2, ta, size/2, 1, 2); // // // // Throw the various AipsErrors when incorrect arguments used void objthrowmv1(const void *to, const void *from, const size_t n); void objthrowmv2(const void *to, const void *from, const size_t n, const size_t toStride, const size_t fromStride); void objthrowcp1(const void *to, const void *from, const size_t n); void objthrowcp2(const void *to, const void *from, const size_t n, const size_t toStride, const size_t fromStride); void objthrowfl1(const void *to, const size_t n); void objthrowfl2(const void *to, const size_t n, const size_t toStride); // // Test routines // // Test on how to handle the overlap in move void objtestmv(size_t &nLeft, size_t &startLeft, size_t &startRight, const void *to, const void *from, const size_t n, const size_t toStride, const size_t fromStride, const void *toPn, const void *fromPn, const size_t fromMto, const size_t toMfrom); // // Copy methods // // The general function to copy n objects from one place // to another if overlap between to and from fields // is possible. Strides may be specified, i.e. you may copy from every // fromStride-th position into every toStride-th // one. // // The function will call std::copy() when possible. // Objmove works correctly if the source and destination overlap in any way. // // An exception will be thrown if the source or the destination does not // exist (and n is non-zero) or if the strides are non-positive. // //
        232. AipsError // // // template void objmove(T* to, const T* from, size_t n) { objthrowmv1(to,from,n); (to= from+n) ? std::copy(from,from+n,to) : std::copy_backward(from,from+n,to+n); } template void objmove(T* to, const T* from, size_t n, size_t toStride, size_t fromStride) { if (!n) return; objthrowmv2(to,from,n,toStride,fromStride); if (toStride*fromStride == 1) { objmove(to, from, n); return; } size_t nLeft, startLeft, startRight; size_t fromMto=0; size_t toMfrom=0; if (toStride > fromStride && from > to) fromMto = (from-to)/(toStride-fromStride); else if (toStride < fromStride && from < to) toMfrom = (to-from)/(fromStride-toStride); objtestmv(nLeft, startLeft, startRight, to, from, n, toStride, fromStride, to+n*toStride, from+n*fromStride, fromMto, toMfrom); n -= nLeft; if (nLeft) { const T* fromPtr = from + startLeft*fromStride; T* toPtr = to + startLeft*toStride; while (nLeft--) { *toPtr = *fromPtr; fromPtr += fromStride; toPtr += toStride; }; }; // Do the moves from the right. if (n) { const T* fromPtr = from + startRight*fromStride; T* toPtr = to + startRight*toStride; while (n--) { fromPtr -= fromStride; toPtr -= toStride; *toPtr = *fromPtr; }; }; } // // The non-general function to copy n objects from one place // to another. Strides may be specified, i.e. you may copy from every // fromStride-th position into every toStride-th // one. // // Objcopy/objcopyctor does not take an overlap of source and destination into account. // Objmove should be used if that is an issue. // // Objcopyctor copy objects from from by calling copy constructor // on each element in to. // // An exception will be thrown if the source or the destination does not // exist or if the strides are non-positive. // //
        233. AipsError // // // template void objcopy(T* to, const T* from, size_t n) { objthrowcp1(to,from,n); std::copy(from, from+n, to); } template void objcopy(T* to, const T* from, size_t n, size_t toStride, size_t fromStride) { objthrowcp2(to,from,n,toStride,fromStride); while (n--) { *to = *from; to += toStride; from += fromStride; } } template void objcopyctor(T* to, const T* from, size_t n) { objthrowcp1(to, from, n); size_t i; try { for (i = 0; i < n; ++i) { ::new (&to[i]) T(from[i]); } } catch (...) { while (i > 0) { // roll back to[--i].~T(); } throw; } } template void objcopyctor(T* to, const T* from, size_t n, size_t toStride, size_t fromStride) { objthrowcp2(to, from, n, toStride, fromStride); size_t i = 0; try { for (i = 0; i < n; ++i) { ::new (to) T(*from); to += toStride; from += fromStride; } } catch (...) { while (i > 0) { // roll back --i; to -= toStride; to->~T(); } throw; } } // // Fill n elements of an array of objects with the given // value, optionally with a stride. Note that the fillValue is passed // by value. // // An exception will be thrown if the destination array does not exist // or if the stride is non-positive. // // //
        234. AipsError // // // template void objset(T* to, const T fillValue, size_t n) { objthrowfl1(to,n); std::fill_n(to, n, fillValue); } template void objset(T* to, const T fillValue, size_t n, size_t toStride) { objthrowfl2(to,n,toStride); while (n--){*to = fillValue; to += toStride; }; } // // } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/casa/Utilities/Copy.tcc000066400000000000000000000026531476623553700176210ustar00rootroot00000000000000//# Copy.cc: Copy objects from one c-array to another, with optional strides. //# Copyright (C) 1994,1995,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_COPY_TCC #define CASA_COPY_TCC #include //# Note that this is empty to make transition between explicit and //# implicit templates easy. #endif casacore-3.7.1/casa/Utilities/Copy2.cc000066400000000000000000000112021476623553700175050ustar00rootroot00000000000000//# Copy2.cc: Non-templated parts (tests/exceptions) for object copies //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN void objthrowmv1(const void *to, const void *from, const size_t n) { if (n > 0 && (!from || !to)) throw(AipsError("objmove(T* to, const T* from, size_t n)" " - illegal argument")); } void objthrowmv2(const void *to, const void *from, const size_t n, const size_t toStride, const size_t fromStride) { if (n > 0 && (!from || !to || !toStride || !fromStride)) throw(AipsError("objmove(T* to, const T* from, size_t n, size_t toStride, " "size_t fromStride) - illegal argument")); } void objthrowcp1(const void *to, const void *from, const size_t n) { if (n > 0 && (!from || !to)) throw(AipsError("objcopy(T* to, const T* from, size_t n)" " - illegal argument")); } void objthrowcp2(const void *to, const void *from, const size_t n, const size_t toStride, const size_t fromStride) { if (n > 0 && (!from || !to || !toStride || !fromStride)) throw(AipsError("objcopy(T* to, const T* from, size_t n, size_t toStride, " "size_t fromStride) - illegal argument")); } void objthrowfl1(const void *to, const size_t n) { if (n > 0 && !to) throw(AipsError("objset(T* to, const T fillValue, size_t n)" " - illegal argument")); } void objthrowfl2(const void *to, const size_t n, const size_t toStride) { if (n > 0 && (!to || !toStride)) throw(AipsError("objset(T* to, const T fillValue, size_t n, " "size_t toStride) - illegal argument")); } void objtestmv(size_t &nLeft, size_t &startLeft, size_t &startRight, const void *to, const void *from, const size_t n, const size_t toStride, const size_t fromStride, const void *toPn, const void *fromPn, const size_t fromMto, const size_t toMfrom) { // It's not a simple block move. // The to and from interval may overlap, so determine // if we have to start moving from the left or right. nLeft = n; startLeft = 0; startRight = n; // First test if the to and from intervals are disjoint. // If so, we can move everything from the left. if (toPn <= from || to >= fromPn); else { // When the strides are equal, we can also move everything // from the left when to starts before from. // Otherwise everything has to be moved from the right. if (toStride == fromStride) { if (to <= from); else nLeft = 0; } else { // Hmm, it's getting more complex. // First consider the case toStride > fromStride. // When to starts after from, move everything from the right. // When to ends before the end of from, move from the left. if (toStride > fromStride) { if (to >= from) nLeft = 0; else if (toPn <= fromPn); else { // The intervals overlap in a way that part has to be // moved from the left, part from the right. // Determine the crosspoint. nLeft = fromMto; if (nLeft > n) nLeft = n; } } else { // This case is the opposite from the previous one. // However, the first part has to be moved from the right // and the last part from the left. if (from >= to); else if (fromPn <= toPn) nLeft = 0; else { startRight = toMfrom; if (startRight > n) startRight = n; startLeft = startRight; nLeft = n - startRight; } } } } } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Utilities/CountedPtr.h000066400000000000000000000222001476623553700204420ustar00rootroot00000000000000//# CountedPtr.h: Referenced counted pointer classes //# Copyright (C) 1993,1994,1995,1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_COUNTEDPTR_H #define CASA_COUNTEDPTR_H ////#warning CountedPtr is deprecated; use std::shared_ptr instead #include #include //# Define the old names for backward compatibility. #define SHARED_PTR std::shared_ptr #define DYNAMIC_POINTER_CAST std::dynamic_pointer_cast #define CONST_POINTER_CAST std::const_pointer_cast #define STATIC_POINTER_CAST std::static_pointer_cast namespace casacore { //#Begin casa namespace // act on dereference error // // Global function that throws an exception. It is called by the // member functions of the counted pointer classes when an // un-initialized (null) pointer is followed. // // void throw_Null_CountedPtr_dereference_error(); // // Referenced counted pointer for constant data // // // // This class is Counted because it is reference counted. // // // This class implements a reference counting mechanism. It // allows CountedPtrs to be passed around freely, // incrementing or decrementing the reference count as needed when one // CountedPtr is assigned to another. When the // reference count reaches zero the internal storage is deleted by // default, but this behavior can be overridden. // // Internally the class uses std::shared_ptr to be thread-safe. Note that // tr1 is used if the compiler does not support C++11 yet. // // // Reference counting // template class CountedPtr { protected: // Helper class to make deletion of object optional. template class Deleter { public: Deleter (Bool deleteIt) : reallyDeleteIt_p (deleteIt) {} void operator() (T * data) const { if (reallyDeleteIt_p) delete data;} private: Bool reallyDeleteIt_p; }; public: // This constructor allows for the creation of a null // CountedPtr. The assignment operator can be used // to assign a null CountedPtr from another // pointer. // CountedPtr() : pointerRep_p () {} // This constructor sets up a reference count for the val // pointer. By default, the data pointed to by val // will be deleted when it is no longer referenced. Passing in // False for delit will prevent the data // from being deleted when the reference count reaches zero. // // After the counted pointer is initialized // the value should no longer be manipulated by the raw pointer of // type t*. // CountedPtr(t *val, Bool delit = True) : pointerRep_p (val, Deleter (delit)) {} // This copy constructor allows CountedPtrs to be // initialized from other CountedPtrs for which the pointer TP* // is convertible to T*. template CountedPtr(const CountedPtr& that) : pointerRep_p(that.pointerRep_p) {} // Create from a shared_ptr. CountedPtr (const std::shared_ptr& rep) : pointerRep_p (rep) {} // This destructor only deletes the really stored data when it was // initialized as deletable and the reference count is zero. ~CountedPtr() {} // This assignment operator allows CountedPtrs to be // copied from other CountedPtrs for which the pointer TP* // is convertible to t*. template CountedPtr& operator=(const CountedPtr& that) { pointerRep_p = that.pointerRep_p; return *this; } // Reset the pointer. // void reset (t *val, Bool delit=True) { pointerRep_p = PointerRep (val, Deleter(delit)); } void reset() { pointerRep_p.reset(); } // // The CountedPtr indirection operator simply // returns a reference to the value being protected. If the pointer // is un-initialized (null), an exception will be thrown. The member // function // null() // can be used to catch such a condition in time. // The address of the reference returned should // not be stored for later use. // t &operator*() const { if (null()){ throw_Null_CountedPtr_dereference_error(); } return pointerRep_p.operator* (); } // This dereferencing operator behaves as expected; it returns the // pointer to the value being protected, and then its dereferencing // operator will be invoked as appropriate. If the pointer is // un-initialized (null), an exception will be thrown. The member // function // null() // can be used to catch such a condition in time. t *operator->() const { return get (); } // Get the underlying pointer. t* get () const { return pointerRep_p.get(); } // Equality operator which checks to see if two // CountedPtrs are pointing at the same thing. Bool operator==(const CountedPtr &other) const { return (get() == other.get()); } //# Note: use of const void* gives ambiguius overload error. Bool operator==(int ptr) const { return (ptr == 0 && get() == 0); } // Non-equality operator which checks to see if two // CountedPtrs are not pointing at the same thing. Bool operator!=(const CountedPtr &other) const { return (get() != other.get() ? True : False); } //# Note: use of const void* gives ambiguius overload error. Bool operator!=(int ptr) const { return (ptr != 0 || get() != 0); } // This assignment operator allows the object to which the current // CountedPtr points to be changed. CountedPtr & operator=(t *v) { pointerRep_p = PointerRep (v); return * this; } // Cast functions. // template CountedPtr static_ptr_cast() const { return CountedPtr (std::static_pointer_cast (pointerRep_p)); } template CountedPtr const_ptr_cast() const { return CountedPtr (std::const_pointer_cast (pointerRep_p)); } template CountedPtr dynamic_ptr_cast() const { return CountedPtr (std::dynamic_pointer_cast (pointerRep_p)); } // // Sometimes it is useful to know if there is more than one // reference made. This is a way of getting that. Of course the point // of these classes is that this information is normally not required. uInt nrefs() const { return pointerRep_p.use_count(); } // Check to see if this CountedPtr is // un-initialized, null. Bool null() const { return get() == 0; } // Test if it contains a valid pointer. operator bool() const { return get() != 0; } private: // Make all types of CountedPtr a friend for the templated operator=. template friend class CountedPtr; typedef std::shared_ptr PointerRep; PointerRep pointerRep_p; }; // A shared_ptr is used as implementation. inline Bool countedPtrShared() { return True; } // Cast the CountedPtr from one pointer type to another. template CountedPtr static_pointer_cast (const CountedPtr& that) { return that.template static_ptr_cast(); } template CountedPtr const_pointer_cast (const CountedPtr& that) { return that.template const_ptr_cast(); } template CountedPtr dynamic_pointer_cast (const CountedPtr& that) { return that.template dynamic_ptr_cast(); } } //#End casa namespace #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/casa/Utilities/CountedPtr.tcc000066400000000000000000000026531476623553700207760ustar00rootroot00000000000000//# CountedPtr.cc: Referenced counted pointer classes //# Copyright (C) 1993,1994,1995,1996,1999,2000,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_COUNTEDPTR_TCC #define CASA_COUNTEDPTR_TCC #include namespace casacore { //# NAMESPACE CASACORE - BEGIN } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Utilities/CountedPtr2.cc000066400000000000000000000030271476623553700206700ustar00rootroot00000000000000//# CountedPtr2.cc: Referenced counted pointer classes (non-templated functions) //# Copyright (C) 1993,1994,1995,1996,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN void throw_Null_CountedPtr_dereference_error() { throw (AipsError("CountedPtr: null dereference error")); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Utilities/DataType.cc000066400000000000000000000106071476623553700202340ustar00rootroot00000000000000//# DataType.h: data types (primarily) in the table system //# Copyright (C) 1993,1994,1995,1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ostream &operator<<(ostream &os, DataType type) { switch (type) { case TpBool: os << "Bool"; break; case TpChar: os << "Char"; break; case TpUChar: os << "uChar"; break; case TpShort: os << "Short"; break; case TpUShort: os << "uShort"; break; case TpInt: os << "Int"; break; case TpUInt: os << "uInt"; break; case TpInt64: os << "Int64"; break; case TpFloat: os << "float"; break; case TpDouble: os << "double"; break; case TpComplex: os << "Complex"; break; case TpDComplex: os << "DComplex"; break; case TpString: os << "String"; break; case TpTable: os << "Table"; break; case TpArrayBool: os << "Array"; break; case TpArrayChar: os << "Array"; break; case TpArrayUChar: os << "Array"; break; case TpArrayShort: os << "Array"; break; case TpArrayUShort: os << "Array"; break; case TpArrayInt: os << "Array"; break; case TpArrayUInt: os << "Array"; break; case TpArrayInt64: os << "Array"; break; case TpArrayFloat: os << "Array"; break; case TpArrayDouble: os << "Array"; break; case TpArrayComplex: os << "Array"; break; case TpArrayDComplex: os << "Array"; break; case TpArrayString: os << "Array"; break; case TpRecord: os << "Record"; break; case TpOther: os << "Other"; break; case TpQuantity: os << "Quantity"; break; case TpArrayQuantity: os << "Array"; break; default: os << "unknown (cannot happen)'"; } return os; } Bool isScalar(DataType type) { return ((type <= TpString) || (type == TpQuantity) || (type == TpInt64)); } Bool isScalarFun(DataType type){return isScalar(type);} Bool isArray(DataType type) { return ((type >= TpArrayBool && type <= TpArrayString) || (type == TpArrayQuantity) || (type == TpArrayInt64)); } Bool isReal(DataType type) { return (type>=TpChar && type<=TpDouble) || (type>=TpArrayChar && type<=TpArrayDouble); } Bool isComplex(DataType type) { return type==TpComplex || type==TpDComplex || type==TpArrayComplex || type==TpArrayDComplex; } Bool isNumeric(DataType type) { return isReal(type) || isComplex(type); } DataType asScalar(DataType type) { AlwaysAssert(type != TpOther && type != TpRecord && type != TpTable, AipsError); DataType tmp = type; if (isArray(tmp)) { if (tmp == TpArrayQuantity) { tmp = TpQuantity; } else if (tmp == TpArrayInt64) { tmp = TpInt64; } else { tmp = DataType(type - TpArrayBool + TpBool); } } return tmp; } DataType asArray(DataType type) { AlwaysAssert(type != TpOther && type != TpRecord && type != TpTable, AipsError); DataType tmp = type; if (isScalar(tmp)) { if (tmp == TpQuantity) { tmp = TpArrayQuantity; } else if (tmp == TpInt64) { tmp = TpArrayInt64; } else { tmp = DataType(type - TpBool + TpArrayBool); } } return tmp; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Utilities/DataType.h000066400000000000000000000236741476623553700201060ustar00rootroot00000000000000//# DataType.h: data types (primarily) in the table system //# Copyright (C) 1993,1994,1995,1996,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_DATATYPE_H #define CASA_DATATYPE_H #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class Table; template class Quantum; class String; class Record; // Data types (primarily) in the table system // // // // // DataType enumerates possible data types. While this enum is primarily // used in the table system, some // use of it is made elsewhere. Besides the enum // itself, operator<< is defined for DataType; it prints a DataType // in the form DataType=Bool. // // Also, global functions are written which take a "const pointer to type" and // return its DataType (TpOther if unknown). These functions can occasionally // allow one to avoid a switch on type, and can be useful in constructing // templated classes which are only valid for certain types. // // Global functions are also provided which allow one to convert an // array type to the equivalent scalar type and vice versa. // // // New data types should be added just before TpNumberOfTypes, and after all // the existing enumerations, to avoid changing the number of an existing type // which would cause misinterpretation of data types stored in existing files. // Note also that if any new scalar and array types are added that this // will break the exising isScalar, isArray, asScalar and asArray functions. // // // // Data types long and unsigned long are not // possible. The types Int and uInt are always // 4 bytes, so long is not needed and may only cause // confusion. // // // // // The simplest uses of the DataType enumeration and functions are fairly // obvious, for example: // // Double d; // DataType type = whatType(&d); // cout << type << endl; // switch(type) { // case TpChar: ... // ... // case TpDouble: ... // } // // // A less obvious use is for "attaching" a templated object or function to a // non-templated object in a safe way. For example: // // class IntFloatContainer { // public: // Int intval; // Float floatval; // void *ptr(DataType type) { // if (type == whatType(&intval)) // return &intval; // else if (type == whatType(&floatval)) // return &floatval; // else // return 0; // Illegal type // } // }; // // template class ValueAccessor { // public: // ValueAccessor(IntFloatContainer *container) : container_p(container) { // if (container_p->ptr(whatType(static_cast(0))) == 0) // throw(AipsError("Illegal type...")); // } // T &value() { return *((T*)container_p->ptr(whatType(static_cast(0)))); } // private: // IntFloatContainer *container_p; // }; // // // So, this example provides a typesafe interface to values of only a small // number of types (and it fairly gracefully allows additional types to be // added; in particular the accessor class needs no modification). Techniques // such as this are appropriate for situations where one needs to deal with // many (but finite) numbers of types. For example, with FITS. // // //
        235. Clean up comment as soon as enum's are properly extracted. // // // Enumeration of the data types in the table system // // // Enumeration of the possible data types for keywords and table columns. // enum DataType {TpBool, TpChar, TpUChar, TpShort, TpUShort, TpInt, TpUInt, TpFloat, TpDouble, TpComplex, TpDComplex, TpString, TpTable, TpArrayBool, TpArrayChar, TpArrayUChar, TpArrayShort, TpArrayUShort, TpArrayInt, TpArrayUInt, TpArrayFloat, TpArrayDouble, TpArrayComplex, TpArrayDComplex, TpArrayString, TpRecord, TpOther, //#// TpLDouble, //#// TpArrayLDouble, TpQuantity, TpArrayQuantity, TpInt64, TpArrayInt64, // Since we start at zero, this is the number of types in the // enum. TpNumberOfTypes }; // Write a formated representation (e.g., Type=Bool) of the given data type. ostream &operator<<(ostream &os, DataType type); // These (specialized) functions return the DataType that corresponds // to the template type. TpOther is returned for types that are // not specialized, as is void. // template inline DataType whatType() { return TpOther; } #define DEFINE_WHATTYPE(SPECIALIZED_TYPE, RETURN_TYPE) \ template<> inline DataType whatType() { return RETURN_TYPE; } DEFINE_WHATTYPE(void, TpOther) DEFINE_WHATTYPE(Bool, TpBool) DEFINE_WHATTYPE(Char, TpChar) DEFINE_WHATTYPE(uChar, TpUChar) DEFINE_WHATTYPE(Short, TpShort) DEFINE_WHATTYPE(uShort, TpUShort) DEFINE_WHATTYPE(Int, TpInt) DEFINE_WHATTYPE(uInt, TpUInt) DEFINE_WHATTYPE(Int64, TpInt64) DEFINE_WHATTYPE(float, TpFloat) DEFINE_WHATTYPE(double, TpDouble) DEFINE_WHATTYPE(Complex, TpComplex) DEFINE_WHATTYPE(DComplex, TpDComplex) DEFINE_WHATTYPE(String, TpString) DEFINE_WHATTYPE(Table, TpTable) DEFINE_WHATTYPE(Array, TpArrayBool) DEFINE_WHATTYPE(Array, TpArrayChar) DEFINE_WHATTYPE(Array, TpArrayUChar) DEFINE_WHATTYPE(Array, TpArrayShort) DEFINE_WHATTYPE(Array, TpArrayUShort) DEFINE_WHATTYPE(Array, TpArrayInt) DEFINE_WHATTYPE(Array, TpArrayUInt) DEFINE_WHATTYPE(Array, TpArrayInt64) DEFINE_WHATTYPE(Array, TpArrayFloat) DEFINE_WHATTYPE(Array, TpArrayDouble) DEFINE_WHATTYPE(Array, TpArrayComplex) DEFINE_WHATTYPE(Array, TpArrayDComplex) DEFINE_WHATTYPE(Array, TpArrayString) DEFINE_WHATTYPE(Record, TpRecord) DEFINE_WHATTYPE(Quantum, TpQuantity) DEFINE_WHATTYPE(Array>, TpArrayQuantity) #undef DEFINE_WHATTYPE // // It is sometimes useful to discover what the corresponding // scalar (or array) type is for a given array (or scalar) type. // Calling these with TpOther, TpTable, and TpRecord results // in an exception being thrown. // DataType asScalar(DataType type); DataType asArray(DataType type); // /** * Returns the number of bytes that this type takes when serialized * to disk. For dynamic types (arrays, records, etc.), a value * of 0 is returned. TpBool returns a value of 1, but be aware that * it may be stored with bit-packing. */ constexpr size_t SizeOfType(DataType dtype) { switch (dtype) { case DataType::TpBool: case DataType::TpChar: case DataType::TpUChar: return 1; case DataType::TpShort: case DataType::TpUShort: return 2; case DataType::TpInt: case DataType::TpUInt: case DataType::TpFloat: return 4; case DataType::TpDouble: case DataType::TpComplex: case DataType::TpInt64: return 8; case DataType::TpDComplex: return 16; case DataType::TpArrayBool: case DataType::TpArrayChar: case DataType::TpArrayUChar: case DataType::TpArrayShort: case DataType::TpArrayUShort: case DataType::TpArrayInt: case DataType::TpArrayUInt: case DataType::TpArrayInt64: case DataType::TpArrayFloat: case DataType::TpArrayDouble: case DataType::TpArrayComplex: case DataType::TpArrayDComplex: case DataType::TpArrayQuantity: case DataType::TpArrayString: case DataType::TpOther: case DataType::TpQuantity: case DataType::TpRecord: case DataType::TpString: case DataType::TpTable: case DataType::TpNumberOfTypes: return 0; } return 0; } // It is occasionally useful to discover whether or not a DataType represents // an array or scalar value. Note that TpTable, TpRecord, and TpOther are neither // scalar nor array types. // Bool isScalar(DataType type); Bool isArray(DataType type); Bool isScalarFun(DataType type); //{return isScalar(type);} // // It is sometimes useful to discover if a DataType represents a real // numeric value (i.e., can it be cast to a Double?) This returns True // for both real scalar and array type. Bool isReal(DataType type); // Returns True for Complex or DComplex scalar or array types Bool isComplex(DataType type); // Returns True if the type is either Real or Complex/DComplex Bool isNumeric(DataType type); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Utilities/DefaultValue.h000066400000000000000000000061571476623553700207510ustar00rootroot00000000000000//# DefaultValue.h: fill a variable with its default value. //# Copyright (C) 1995,1996,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_DEFAULTVALUE_H #define CASA_DEFAULTVALUE_H #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A templated function which sets a variable to a default value. // // // // // // // // // The DefaultValue function name is derived from its use to fill a data type // with a default value, usually zero. // // // // The DefaultValue function is passed an instance of a data type and the // variable is filled with a default value. The majority of classes may // use the templated version here. Special classes may use their own // non-templated specializations as demonstrated in // ../Utilities/test/tDefaultValue.cc. // // // // // Int foo = 35; // defaultValue(foo); // AlwaysAssert(foo == 0, AipsError); // Array bar; // defaultValue(bar); // AlwaysAssert(allEQ(bar, 0.0f), AipsError); // // A special class may need its own implementation: // // void defaultValue(MySpecialClass &val){ // // make a default value be all zeros // val.operator()(IPosition(2,3,4)) = Table.keywords().defaultval(); // }; // // // // // We needed a common way of setting all objects to zero or some // null/default value. Specializing a templated function seemed the only way // to reach everyone. // // // //
        236. constructor T(Int) //
        237. assignment operator (copy semantics) // // // //
        238. none // // // //
        239. none // // template inline void defaultValue(T &theValue) { theValue = T(0); } // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Utilities/DynBuffer.cc000066400000000000000000000074401476623553700204060ustar00rootroot00000000000000//# DynBuffer.cc: Store data in dynamically allocated buffers //# Copyright (C) 1993,1994,1995 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // This is the implementation of the DynBuffer class. // Construct the class. // Allocate a first buffer of bufsz (default 4096) bytes. // If we do not allocate it here, it will be done by newbuf. // However, then we may get a huge buffer, which may be not so nice. // bufsz may be sufficient for many purposes. DynBuffer::DynBuffer (uInt bsz) : bufsz_p (bsz), nrbuf_p (0), maxnrbuf_p(10), uselen_p (10), totlen_p (10), bufptr_p (10) { allocstart (); bufptr_p[0] = new Char[bufsz_p]; totlen_p[0] = bufsz_p; nrbuf_p = 1; } DynBuffer::~DynBuffer () { remove (0); } void DynBuffer::remove (uInt n) { for (Int i=n; i= 0) { uselen_p[curbuf_p] = curuselen_p; } // If no more buffers, get new one with required length. // Use a minimum length of bufsz. // Extend the administration blocks if they are full. if (curbuf_p == nrbuf_p-1) { if (nrbuf_p == maxnrbuf_p) { maxnrbuf_p += 10; bufptr_p.resize (maxnrbuf_p); totlen_p.resize (maxnrbuf_p); uselen_p.resize (maxnrbuf_p); } totlen_p[nrbuf_p] = (nr*valsz > bufsz_p ? nr*valsz : bufsz_p); bufptr_p[nrbuf_p] = new Char[totlen_p[nrbuf_p]]; nrbuf_p++; } // Okay, we have another buffer. // Set the current lengths and buffer pointer. curbuf_p++; curuselen_p = 0; curtotlen_p = totlen_p[curbuf_p]; curbufptr_p = bufptr_p[curbuf_p]; } return (n= 0) { uselen_p[curbuf_p] = curuselen_p; } } Bool DynBuffer::next (uInt& len, Char*& ptr) { if (nextbuf_p > curbuf_p) { len = 0; return False; // no more buffers }else{ len = uselen_p[nextbuf_p]; ptr = bufptr_p[nextbuf_p]; nextbuf_p++; return True; } } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Utilities/DynBuffer.h000066400000000000000000000160061476623553700202460ustar00rootroot00000000000000//# DynBuffer.h: Store data in dynamically allocated buffers //# Copyright (C) 1993,1994,1995,1996 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_DYNBUFFER_H #define CASA_DYNBUFFER_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Store data in dynamically allocated buffers // // // // // // DynBuffer allows one to store data in dynamically allocated buffers. // When a buffer is full, an additional buffer can be allocated and // "linked" to the existing one; so, the data may not be stored contiguously // You can loop through all the linked buffers and get their individual // addresses and sizes, so that you can access the data. // // // Example (without exception handling): // // uInt nrOfValues, nrNeeded, nrAvailable;// nr of data values // float* pData = floatarr; // ptr to data to be handled // Char* pBuffer; // ptr to buffer // // DynBuffer buffer; // create buffer // buffer.allocstart(); // prepare for storing // nrNeeded = nrOfValues; // nr of values to store // // copy data into dynamic buffer // while (nrNeeded > 0) { // nrAvailable = buffer.alloc (nrNeeded, sizeof(float), pBuffer); // // get buffer space: // // room for nrAvailable values // memcpy (pBuffer, pData, nrAvailable*sizeof(float)); // // copy that many data values // nrNeeded -= nrAvailable; // how much more needed? // pData += nrAvailable; // pointer to as yet unstored data // } // // Maybe store more values // . // . // // Retrieve all the data values from the buffers and write them // buffer.nextstart(); // goto buffer start // while (buffer.next (nrAvailable, pBuffer)) { // // get next buffer // write (fd, nrAvailable, pBuffer); // write data from that buffer // } // // // // This class is developed as an intermediate buffer for // class AipsIO, // but it may serve other purposes as well. // class DynBuffer { public: // Allocate a first buffer of the specified number of bytes // (default 4096). When the allocation fails, an exception is thrown. DynBuffer (uInt nrOfBytes=4096); // Remove the whole buffer, i.e. the first buffer and all the // buffers appended to it. ~DynBuffer (); // Prepare for storing data (re-initialize the buffer) void allocstart (); // Allocate buffer space for nrOfValues values of size // valueSize bytes, and return the pointer ptr // to the buffer and the number of values that fit in the buffer. // // When not all values fit in the current buffer, new buffer space // is added (probably non-contiguous). If that allocation fails an // exception is thrown. uInt alloc (uInt nrOfValues, uInt valueSize, Char*& ptr); // Remove buffer nrOfBuffer and the buffers appended to it, // and re-initialize the current buffer. By default we keep the first // buffer (i.e. the one numbered 0). // // The idea is that you may want to free intermediate storage // space taken up by data that you no longer need, and that the // first buffer is often big enough to hold further data. So, you // only remove the first buffer in special cases. void remove (uInt nrOfBuffer=1); // Prepare for data retrieval (set up for looping through the buffers). void nextstart (); // Get the pointer to the next buffer and its used length in bytes. // The function returns a False value if there are no more // buffers. Bool next (uInt& usedLength, Char*& ptr); private: // Get the next buffer for storing nrOfValues values of // size valueSize bytes, and return the number of values // that can be stored in the free space of that buffer (maybe less // than nrOfValues). // // The new current buffer can be the present one (if it has free // space), the next buffer already allocated (if there is one), or // a newly allocated and linked-in buffer. If, in the last case, // the allocation fails an exception is thrown. uInt newbuf (uInt nrOfValues, uInt valueSize); // size of 1st buffer and min. bufsize uInt bufsz_p; // buffernr for next function Int nextbuf_p; // current buffernr Int curbuf_p; // nr of buffers allocated Int nrbuf_p; // size of Blocks Int maxnrbuf_p; // used length per buffer Block uselen_p; // total length per buffer Block totlen_p; // pointer to buffer PtrBlock bufptr_p; // used length of current buffer uInt curuselen_p; // total length of current buffer uInt curtotlen_p; // pointer to current buffer Char* curbufptr_p; }; //# Allocate buffer space for the nrOfValues values. //# Return pointer to the buffer and nr of values that fit in it. //# Use a more specialized function if not all values fit. //# In this way the function can be kept small and thus used inline. //# newbuf will seldom be required, unless large vectors are stored. inline uInt DynBuffer::alloc (uInt nrOfValues, uInt valueSize, Char*& ptr) { uInt n = nrOfValues; if (n*valueSize > curtotlen_p-curuselen_p) { n = newbuf (nrOfValues, valueSize); } ptr = curbufptr_p + curuselen_p; curuselen_p += n*valueSize; return n; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Utilities/Fallible.h000066400000000000000000000131551476623553700200760ustar00rootroot00000000000000//# Fallible.h: Identifies a value as valid or invalid //# Copyright (C) 1994,1995,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_FALLIBLE_H #define CASA_FALLIBLE_H #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# The following function is to be found in Fallible2.cc not Fallible.cc //# because it's a non-templated function and template instantiators normally //# do not like them in the same .cc file with templated functions. // // throw exception on access of an invalid object // // This function gets called when an invalid object is accessed. It // just throws an exception. Since we have inline functions, let's keep // the throw out of them to keep them from moving out of line. // //
        240. AipsError // // // void AccessInvalidFallibleObject(); // // Mark a value as valid or invalid. // // // // // This is to be used for values which might be fallible, i.e. might not // be valid. // // // This class resembles the one in Scientific and Engineering C++ // by Barton and Nackman. While it was written with that book closed, the // class is simple enough that resemblances likely remain. // // This class essentially just holds a value (with automatic conversion) // and allows inquiry as to whether the value is valid. If the value is // used and is indeed invalid an exception will be thrown. // // A copy of the value is stored in the Fallible object, so // making copies shouldn't be too expensive. It is anticipated that this // class will most often be used with built in, or other small, types. // // // Suppose we write some code that turns a day/month/year into a day // of the week: // // enum DayName {Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, // Saturday}; // Fallible dayFromDate(uInt day, uInt month, uInt year); // a func. // // And we also have some other function that needs a day name, for example // just prints it: // // ostream &operator<<(ostream &os, DayName day); // Print name as string // // // Since the automatic conversions are defined, if we are certain that the // dates are valid, we can just go ahead and use the value: // // cout << dayFromData(2, 1, 1962) << endl; // A valid date // // If, by some chance, you are wrong and a date fails, then an exception will // be thrown and a run-time error will occur. // // If, as is more likely the case, you don't know a priori whether a test will // succeed, you can check it: // // Fallible result = dayFromDate(d,m,y); // who knows if valid? // if (result.isValid()) { // cout << result << endl; // } else { // // some corrective action // } // // // // The alternatives are to have "special values" (e.g. have an "undefined // day" in the enumeration) or return a Boolean, or change a Boolean. While // those solutions are often adequate, Fallible can often be // more natural. // // //
        241. default constructor //
        242. copy constructor // template class Fallible { public: // The default constructor creates an invalid object. Fallible() : value_p(T()), isValid_p(False) {} // Create a valid object Fallible(const T &value) : value_p(value), isValid_p(True) {} //# Actually, the default copy ctor and assignment operator would work Fallible(const Fallible &other) : value_p(other.value_p), isValid_p(other.isValid_p) {} Fallible &operator=(const Fallible &other) {value_p = other.value_p; isValid_p = other.isValid_p; return *this;} ~Fallible() {} // Automatically convert a Fallible to a T. operator T() const { if (! isValid_p) AccessInvalidFallibleObject(); return value_p; } // Sometimes it's more convenient to not rely on a compiler supplied // conversion, especially when the compiler is confused. T value() const { if (! isValid_p) AccessInvalidFallibleObject(); return value_p; } Bool isValid() const {return isValid_p;} private: T value_p; Bool isValid_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Utilities/Fallible2.cc000066400000000000000000000033211476623553700203100ustar00rootroot00000000000000//# Fallible2.cc: Throw an exception upon invalid object access //# Copyright (C) 1994,1995 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# This is Fallible2.cc not Fallible.cc because it's a non-templated function //# and template instantiators normally do not like them in the same .cc file //# with templated functions. #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN void AccessInvalidFallibleObject() { throw(AipsError("Fallible:: invalid object accessed. Sorry I don't know" " from where")); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Utilities/GenSort.h000066400000000000000000000413051476623553700177430ustar00rootroot00000000000000//# GenSort.h: General sort functions //# Copyright (C) 1993,1994,1995,1996,1997,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_GENSORT_H #define CASA_GENSORT_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations. template class Block; // General in-place sort functions // // // // // The static member functions of this templated class are highly optimized // sort functions. They do an in-place sort of an array of values. The // functions are templated, so they can in principle be used with any // data type. However, if used with non-builtin data types, their // class must provide certain functions (see Template Type Argument // Requirements). // // If it is impossible or too expensive to define these functions, the // Sort class can be used instead. This sorts // indirectly using an index array. Instead of the functions mentioned // above it requires a comparison routine. // // The GenSort functions can sort: //
            //
          • C-arrays of values; //
          • Arrays of values -- the array can have any shape // and the increment can be >1; //
          • Blocks of values -- there is a special function to // sort less elements than the size of the Block. //
          // // The sort order can be specified in the order field: //
          //
          Sort::Ascending (default), //
          Sort::Descending. //
          // // Previously the sort algorithm to use could be given in the options field. //
          //
          Sort::QuickSort (default) //
          is the fastest. It is about 4-6 times faster // than the qsort function on the SUN. No worst case has been // found, even not for cases where qsort is terribly slow. //
          Sort::HeapSort //
          is about twice as slow as quicksort. // It has the advantage that the worst case is always o(n*log(n)), // while quicksort can have hypothetical inputs with o(n*n). //
          Sort::InsSort //
          is o(n*n) for random inputs. It is, however, the // only stable sort (i.e. equal values remain in the original order). //
          // However, these options are not used anymore because the sort now always // uses a merge sort that is equally fast for random input and much faster for // degenerated cases like an already ordered or reversely ordered array. // Furthermore, merge sort is always stable and will be parallelized if OpenMP // support is enabled giving a 6-fold speedup on 8 cores. //
          Sort::NoDuplicates in the options field indicates that // duplicate values will be removed (only the first occurrance is kept). //
          The previous sort functionality is still available through the functions // quickSort, heapSort, and insSort. //

          // All the sort functions return the number of values sorted as their // function value; when duplicate values have been removed, the number of // unique valuess will be returned. //

          // The class also provides a function to find the k-th largest value // in an array of values. This uses a stripped-down version of quicksort // and is at least 6 times faster than a full quicksort. // // //

        243. operator= to assign when swapping elements //
        244. operator<, operator> and // operator== to compare elements //
        245. default constructor to allocate a temporary // template class GenSort { public: // Sort a C-array containing nr T-type objects. // The sort is done in place and is always stable (thus equal keys keep // their original order). It returns the number of values, which // can be different if a NoDuplicates sort is done. //
          Insertion sort is used for short arrays (<50 elements). Otherwise, // a merge sort is used which will be parallelized if casacore is built // with OpenMP support. // static uInt sort (T*, uInt nr, Sort::Order = Sort::Ascending, int options = 0); static uInt sort (Array&, Sort::Order = Sort::Ascending, int options = 0); static uInt sort (Block&, uInt nr, Sort::Order = Sort::Ascending, int options = 0); // // Find the k-th largest value. //
          Note: it does a partial quicksort, thus the data array gets changed. static T kthLargest (T* data, uInt nr, uInt k); // Sort C-array using quicksort. static uInt quickSort (T*, uInt nr, Sort::Order = Sort::Ascending, int options = 0); // Sort C-array using heapsort. static uInt heapSort (T*, uInt nr, Sort::Order = Sort::Ascending, int options = 0); // Sort C-array using insertion sort. static uInt insSort (T*, uInt nr, Sort::Order = Sort::Ascending, int options = 0); // Sort C-array using parallel merge sort (using OpenMP). // By default OpenMP determines the number of threads that can be used. static uInt parSort (T*, uInt nr, Sort::Order = Sort::Ascending, int options = 0, int nthread = 0); // Swap 2 elements in array. static inline void swap (T&, T&); // Reverse the elements in res and put them into data. // Care is taken if both pointers reference the same data. static void reverse (T* data, const T* res, uInt nrrec); private: // Thedata buffer is divided in nparts parts. // In each part the values are in ascending order. // The index tells the nr of elements in each part. // Recursively each two subsequent parts are merged until only part is left // (giving the sorted array). Alternately data and tmp // are used for the merge result. The pointer containing the final result // is returned. //
          If possible, merging the parts is done in parallel (using OpenMP). static T* merge (T* data, T* tmp, uInt nrrec, uInt* index, uInt nparts); // Quicksort in ascending order. static void quickSortAsc (T*, Int, Bool multiThread=False, Int rec_lim=128); // Heapsort in ascending order. static void heapSortAsc (T*, Int); // Helper function for ascending heapsort. static void heapAscSiftDown (Int, Int, T*); // Insertion sort in ascending order. static uInt insSortAsc (T*, Int, int option); // Insertion sort in ascending order allowing duplicates. // This is also used by quicksort for its last steps. static uInt insSortAscDup (T*, Int); // Insertion sort in ascending order allowing no duplicates. // This is also used by the other sort algorithms to skip duplicates. static uInt insSortAscNoDup (T*, Int); }; // General indirect sort functions // // // // This class is similar to GenSort. // The only difference is that the functions in this class sort // indirectly instead of in-place. // They return the result of the sort as an sorted vector of indices // This is slower, because an extra indirection is involved in each // comparison. However, this sort allows to sort const data. // Another advantage is that this sort is always stable (i.e. equal // values are kept in their original order). // // The class is templated on the type T of the sort key and the type // INX of the index vector. In principle INX can be any type, but // it should be a sufficiently large integer type (say uInt or uInt64). template class GenSortIndirect { public: // Sort a C-array containing nr T-type objects. // The resulting index vector gives the sorted indices. static INX sort (Vector& indexVector, const T* data, INX nr, Sort::Order = Sort::Ascending, int options = Sort::QuickSort); // Sort a C-array containing nr T-type objects. // The resulting index vector gives the sorted indices. static INX sort (Vector& indexVector, const Array& data, Sort::Order = Sort::Ascending, int options = Sort::QuickSort); // Sort a C-array containing nr T-type objects. // The resulting index vector gives the sorted indices. static INX sort (Vector& indexVector, const Block& data, INX nr, Sort::Order = Sort::Ascending, int options = Sort::QuickSort); // Find the index of the k-th largest value. static INX kthLargest (T* data, INX nr, INX k); // Sort container using quicksort. // The argument inx gives the index defining the order of the // values in the data array. Its length must be at least nr // and it must be filled with the index values of the data. // Usually this is 0..nr, but it could contain a selection of the data. static INX quickSort (INX* inx, const T* data, INX nr, Sort::Order, int options); // Sort container using heapsort. static INX heapSort (INX* inx, const T* data, INX nr, Sort::Order, int options); // Sort container using insertion sort. static INX insSort (INX* inx, const T* data, INX nr, Sort::Order, int options); // Sort container using parallel merge sort (using OpenMP). // By default the maximum number of threads is used. static INX parSort (INX* inx, const T* data, INX nr, Sort::Order, int options, int nthreads=0); private: // Swap 2 indices. static inline void swapInx (INX& index1, INX& index2); // Thedata buffer is divided in nparts parts. // In each part the values are in ascending order. // The index tells the nr of elements in each part. // Recursively each two subsequent parts are merged until only part is left // (giving the sorted array). Alternately data and tmp // are used for the merge result. The pointer containing the final result // is returned. //
          If possible, merging the parts is done in parallel (using OpenMP). static INX* merge (const T* data, INX* inx, INX* tmp, INX nrrec, INX* index, INX nparts); // Check if 2 values are in ascending order. // When equal, the order is correct if index1 Global in-place sort functions // The following global functions are easier to use than the static // GenSort member functions. // They do an in-place sort of data, thus the data themselves are moved // ending up in the requested order. //

          // The default sorting method is QuickSort, which is the fastest. // However, there are pathological cases where it can be slow. // HeapSort is about twice a slow, but its speed is guaranteed. // InsSort (insertion sort) can be very, very slow, but it is the only // stable sort method (i.e. equal values are kept in their original order). // However, indirect sorting methods // are available to make QuickSort and HeapSort stable. //

          // All sort methods have an option to skip duplicate values. This is the // only case where the returned number of values can be less than the // original number of values. // template inline uInt genSort (T* data, uInt nr, Sort::Order order = Sort::Ascending, int options=0) { return GenSort::sort (data, nr, order, options); } template inline uInt genSort (Array& data, Sort::Order order = Sort::Ascending, int options=0) { return GenSort::sort (data, order, options); } template inline uInt genSort (Block& data, Sort::Order order = Sort::Ascending, int options=0) { return GenSort::sort (data, data.nelements(), order, options); } template inline uInt genSort (Block& data, uInt nr, Sort::Order order = Sort::Ascending, int options=0) { return GenSort::sort (data, nr, order, options); } // //

          Global indirect sort functions // The following global functions easier to use than the static // GenSortIndirect member functions. // They do an indirect sort of data, thus the data themselves are not moved. // Rather an index vector is returned giving the sorted data indices. //

          // The sorting method used is merge sort, which is always stable. // It is the fastest, especially if it can use multiple threads. //

          // Unlike the in-place sorting methods // , all indirect sorting methods are stable (i.e. equal // values are left in their original order). //

          // All sort methods have an option to skip duplicate values. This is the // only case where the returned number of values can be less than the // original number of values. // template inline uInt genSort (Vector& indexVector, const T* data, INX nr, Sort::Order order = Sort::Ascending, int options=0) { return GenSortIndirect::sort (indexVector, data, nr, order, options); } template inline uInt genSort (Vector& indexVector, const Array& data, Sort::Order order = Sort::Ascending, int options=0) { return GenSortIndirect::sort (indexVector, data, order, options); } template inline uInt genSort (Vector& indexVector, const Block& data, Sort::Order order = Sort::Ascending, int options=0) { return GenSortIndirect::sort (indexVector, data, data.nelements(), order, options); } template inline uInt genSort (Vector& indexVector, const Block& data, INX nr, Sort::Order order = Sort::Ascending, int options=0) { return GenSortIndirect::sort (indexVector, data, nr, order, options); } // // Implement inline member functions. template inline void GenSort::swap (T& l, T& r) { T t = l; l = r; r = t; } template inline void GenSortIndirect::swapInx (INX& i, INX& j) { INX t = i; i = j; j = t; } template inline int GenSortIndirect::isAscending (const T* data, INX i, INX j) { return (data[i] > data[j] || (data[i] == data[j] && i > j)); } } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/casa/Utilities/GenSort.tcc000066400000000000000000000650071476623553700202720ustar00rootroot00000000000000//# GenSort.cc: General sort functions //# Copyright (C) 1993,1994,1995,1996,1997,1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_GENSORT_TCC #define CASA_GENSORT_TCC #include #include #include #include #include #include #include #include #ifdef _OPENMP # include #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN // Do a quicksort in ascending order. // All speedups are from Sedgewick; Algorithms in C. template void GenSort::quickSortAsc (T* data, Int nr, Bool multiThread, Int rec_lim) { // QuickSorting small sets makes no sense. // It will be finished with an insertion sort. // The number 32 is determined experimentally. It is not very critical. if (nr <= 32) { return; } // not enough progress, abort into runtime limited heapsort if (rec_lim < 0) { heapSortAsc(data, nr); return; } // Choose a partition element by taking the median of the // first, middle and last element. // Store the partition element at the end. // Do not use Sedgewick\'s advise to store the partition element in // data[nr-2]. This has dramatic results for reversed ordered arrays. Int i = (nr-1)/2; // middle element T* sf = data; // first element T* sl = data+nr-1; // last element if (data[i] < *sf) swap (data[i], *sf); if (*sl < *sf) swap (*sl, *sf); if (data[i] < *sl) swap (data[i], *sl); T par = *sl; // partition element // Now partition until the pointers cross. for (;;) { while (*++sf < par) ; while (*--sl > par) ; if (sf >= sl) break; swap (*sf, *sl); } swap (*sf, data[nr-1]); i = sf-data; if (multiThread) { /* limit threads to what the code can do to not span unnecessary * workers */ #ifdef _OPENMP int nthreads = std::min(2, omp_get_max_threads()); /* TODO parallel for only uses 2 threads of the group, should use tasks * only parallelize when work time ~ barrier spin time (3ms) * otherwise oversubscription kills performance */ #pragma omp parallel for num_threads(nthreads) if (nr > 500000) #endif for (int thr=0; thr<2; ++thr) { if (thr==0) quickSortAsc (data, i, False, rec_lim - 1); // sort left part if (thr==1) quickSortAsc (sf+1, nr-i-1, False, rec_lim - 1); // sort right part } } else { quickSortAsc (data, i, False, rec_lim - 1); // sort left part quickSortAsc (sf+1, nr-i-1, False, rec_lim - 1); // sort right part } } // Find the k-th largest element using a partial quicksort. template T GenSort::kthLargest (T* data, uInt nr, uInt k) { if (k >= nr) { throw (AipsError ("kthLargest(data, nr, k): k must be < nr")); } Int st = 0; Int end = Int(nr) - 1; // Partition until a set of 1 or 2 elements is left. while (end > st+1) { // Choose a partition element by taking the median of the // first, middle and last element. // Store the partition element at the end. // Do not use Sedgewick\'s advise to store the partition element in // data[nr-2]. This has dramatic results for reversed ordered arrays. Int i = (st+end)/2; // middle element T* sf = data+st; // first element T* sl = data+end; // last element if (data[i] < *sf) swap (data[i], *sf); if (*sl < *sf) swap (*sl, *sf); if (data[i] < *sl) swap (data[i], *sl); T par = *sl; // partition element // Now partition until the pointers cross. for (;;) { while (*++sf < par) ; while (*--sl > par) ; if (sf >= sl) break; swap (*sf, *sl); } swap (*sf, data[end]); // Determine index of partitioning and update the start and end // to take left or right part. i = sf-data; if (i <= Int(k)) st = i; if (i >= Int(k)) end = i; } if (end == st+1) { if (data[st] > data[end]) { swap (data[st], data[end]); } } return data[k]; } // Do an insertion sort in ascending order. template uInt GenSort::insSortAsc (T* data, Int nr, int opt) { if ((opt & Sort::NoDuplicates) == 0) { return insSortAscDup (data, nr); } return insSortAscNoDup (data, nr); } // Do an insertion sort in ascending order. // Keep duplicate elements. template uInt GenSort::insSortAscDup (T* data, Int nr) { Int j; T cur; for (Int i=1; i0 && data[j-1] > cur) { data[j] = data[j-1]; j--; } data[j] = cur; } return nr; } // Do an insertion sort in ascending order. // Skip duplicate elements. template uInt GenSort::insSortAscNoDup (T* data, Int nr) { if (nr < 2) { return nr; // nothing to sort } Int j, k; T cur; Int n = 1; for (Int i=1; i0 && data[j-1] > cur) { j--; } if (j <= 0 || !(data[j-1] == cur)) { // no equal key for (k=n-1; k>=j; k--) { data[k+1] = data[k]; // now shift to right } data[j] = cur; // insert in right place n++; } } return n; } // Do a heapsort in ascending order. template void GenSort::heapSortAsc (T* data, Int nr) { // Use the heapsort algorithm described by Jon Bentley in // UNIX Review, August 1992. data--; Int j; for (j=nr/2; j>=1; j--) { heapAscSiftDown (j, nr, data); } for (j=nr; j>=2; j--) { swap (data[1], data[j]); heapAscSiftDown (1, j-1, data); } } template void GenSort::heapAscSiftDown (Int low, Int up, T* data) { T sav = data[low]; Int c; Int i; for (i=low; (c=2*i)<=up; i=c) { if (c < up && data[c+1] > data[c]) { c++; } data[i] = data[c]; } data[i] = sav; for ( ; (c=i/2)>= low; i=c) { if (!(data[i] > data[c])) { break; } swap (data[c], data[i]); } } template uInt GenSort::parSort (T* data, uInt nr, Sort::Order ord, int opt, int nthread) { int nthr = nthread; // to avoid compiler warning #ifdef _OPENMP if (nthread > 0) { nthr = nthread; // Do not use more threads than there are values. if (uInt(nthr) > nr) nthr = nr; } else { nthr = omp_get_max_threads(); if (uInt(nthr) > nr) nthr = nr; } if (nthr == 0) nthr = 1; #else nthr = 1; #endif Block index(nr+1); Block tinx(nthr+1); Block np(nthr); // Determine ordered parts in the array. // It is done in parallel, whereafter the parts are combined. int step = nr/nthr; for (int i=0; i data[j]) { index[tinx[i]+nparts] = j; // out of order, thus new part nparts++; } } np[i] = nparts; } // Make index parts consecutive by shifting to the left. // See if last and next part can be combined. uInt nparts = np[0]; for (int i=1; i data[tinx[i]]) { index[nparts++] = index[tinx[i]]; } if (nparts == tinx[i]+1) { nparts += np[i]-1; } else { for (uInt j=1; j indexVector(nr); indgen(indexVector); INX* inx = indexVector.data(); INX st = 0; INX end = INX(nr) - 1; // Partition until a set of 1 or 2 elements is left. while (end > st+1) { // Choose a partition element by taking the median of the // first, middle and last element. // Store the partition element at the end. // Do not use Sedgewick\'s advise to store the partition element in // data[nr-2]. This has dramatic results for reversed ordered arrays. INX i = (st+end)/2; // middle element INX* sf = inx+st; // first element INX* sl = inx+end; // last element if (data[inx[i]] < data[*sf]) swapInx (inx[i], *sf); if (data[*sl] < data[*sf]) swapInx (*sl, *sf); if (data[inx[i]] < data[*sl]) swapInx (inx[i], *sl); T partVal = data[*sl]; // partition element // Now partition until the pointers cross. for (;;) { while (data[*++sf] < partVal) ; while (data[*--sl] > partVal) ; if (sf >= sl) break; swapInx (*sf, *sl); } swapInx (*sf, inx[end]); // Determine index of partitioning and update the start and end // to take left or right part. i = sf-inx; if (i <= INX(k)) st = i; if (i >= INX(k)) end = i; } if (end == st+1) { if (data[inx[st]] > data[inx[end]]) { swapInx (inx[st], inx[end]); } } return inx[k]; } // Do an insertion sort in ascending order. template INX GenSortIndirect::insSortAsc (INX* inx, const T* data, INX nr, int opt) { if ((opt & Sort::NoDuplicates) == 0) { return insSortAscDup (inx, data, nr); }else{ return insSortAscNoDup (inx, data, nr); } } // Do an insertion sort in ascending order. // Keep duplicate elements. template INX GenSortIndirect::insSortAscDup (INX* inx, const T* data, INX nr) { for (INX i=1; i0 && isAscending (data, inx[j-1], cur)) { inx[j] = inx[j-1]; j--; } inx[j] = cur; } return nr; } // Do an insertion sort in ascending order. // Skip duplicate elements. template INX GenSortIndirect::insSortAscNoDup (INX* inx, const T* data, INX nr) { if (nr < 2) { return nr; // nothing to sort } INX n = 1; for (INX i=1; i0 && data[inx[j-1]] > data[cur]) { j--; } if (j <= 0 || !(data[inx[j-1]] == data[cur])) { // no equal key for (Int64 k=n-1; k>=j; k--) { inx[k+1] = inx[k]; // now shift to right } inx[j] = cur; // insert in right place n++; } } return n; } // Do a heapsort in ascending order. template void GenSortIndirect::heapSortAsc (INX* inx, const T* data, INX nr) { // Use the heapsort algorithm described by Jon Bentley in // UNIX Review, August 1992. inx--; INX j; for (j=nr/2; j>=1; j--) { heapAscSiftDown (inx, j, nr, data); } for (j=nr; j>=2; j--) { swapInx (inx[1], inx[j]); heapAscSiftDown (inx, 1, j-1, data); } } template void GenSortIndirect::heapAscSiftDown (INX* inx, INX low, INX up, const T* data) { INX sav = inx[low]; INX c; INX i; for (i=low; (c=2*i)<=up; i=c) { if (c < up && isAscending (data, inx[c+1], inx[c])) { c++; } inx[i] = inx[c]; } inx[i] = sav; for ( ; (c=i/2)>= low; i=c) { if (isAscending (data, inx[c], inx[i])) { break; } swapInx (inx[c], inx[i]); } } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Utilities/LinearSearch.h000066400000000000000000000127021476623553700207210ustar00rootroot00000000000000//# LinearSearch.h: Linear search through linear data structures //# Copyright (C) 1997,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_LINEARSEARCH_H #define CASA_LINEARSEARCH_H //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //

          // Linear search a linear data structure. // // // // // These linear search functions work on linear data structures // which have operator() or operator[] defined on them (e.g. // C-array, Vector, IPosition, Block, ScalarColumn, etc.) // Two versions of the functions are provided, one which uses // parentheses () for indexing, one which uses square brackets [] (obviously // the latter one can also be used for ordinary C-style pointers and arrays). // It is assumed that the container uses zero-based indexing. // // The returned index is in the range [0..n-1]. When the value is // not found, -1 is returned. // // While normally you want to search a container with indices in the range // [0 ... n-1], any desired lower bound may be used instead. // // // Linear searching should only be used for small arrays. // For larger arrays sort and // binarySearch // should be used. // // // // // // Vector vi; // ... // Sets vi somehow // Int val; // Bool found; // while (cin >> val && val != -999) { // Int where = linearSearch(found, vi, val, vi.nelements()); // if (found) { // cout << "Found " << val << " at position " << where << endl; // } else { // cout << val << " is not in the vector, but it belongs at " << // where << endl; // } // } // // // // // Neil Killeen needed a linear search on a Vector. // Modelling it after BinarySearch was the logical step to take. // // // //
        246. operator(Int) or operator[Int] needs to be defined. //
        247. The index must be zero based. //
        248. The result of that indexing must be an expression that can be // compared with an object of class ElType. Normally in fact it would // be a temporary of class ElType. //
        249. Member function nelements() is needed when the shorthand is taken. // // //
        250. The equal operator (==) need to be defined. // // // //
        251. I suspect that an implementation is possible that only calls // operator() or [] once during each evaluation of the while loop. //
        252. MACROize implementation so that code isn't repeated twice. Or, // possibly implement one using the other (e.g. by introducing an adapter // class that turns (i) into [i]. // // // Search container for value. There are assumed to be at least // n elements in the container. The container will be searched for // indices in the range [lower ... lower + n - 1] Return the index // of the first element which is greater than or equal to (ascending order) or // less than or equal to (descending order) the value. // When not found, -1 is returned and found is set to False. //# GvD 19971008: The functions need different names, because g++ gives errors //# when instantiating. // // This version of the function is for containers that use () for indexing. template Int linearSearch1 (const Container& container, const ElType& value, uInt lower = 0); template Int linearSearch (Bool& found, const Container& container, const ElType& value, uInt n, uInt lower=0); // This version of the function is for containers that use [] for indexing. template Int linearSearchBrackets1 (const Container& container, const ElType& value, uInt lower = 0); template Int linearSearchBrackets (Bool& found, const Container& container, const ElType& value, uInt n, uInt lower=0); // // } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/casa/Utilities/LinearSearch.tcc000066400000000000000000000051411476623553700212420ustar00rootroot00000000000000//# LinearSearch.cc: Linear search through linear data structures //# Copyright (C) 1997 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_LINEARSEARCH_TCC #define CASA_LINEARSEARCH_TCC //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template Int linearSearch (Bool& found, const Container& container, const ElType& value, uInt n, uInt lower) { n += lower; while (lower < n) { if (container(lower) == value) { found = True; return lower; } lower++; } found = False; return -1; } template Int linearSearch1 (const Container& container, const ElType& value, uInt lower) { uInt n = container.nelements(); while (lower < n) { if (container(lower) == value) { return lower; } lower++; } return -1; } template Int linearSearchBrackets (Bool& found, const Container& container, const ElType& value, uInt n, uInt lower) { n += lower; while (lower < n) { if (container[lower] == value) { found = True; return lower; } lower++; } found = False; return -1; } template Int linearSearchBrackets1 (const Container& container, const ElType& value, uInt lower) { uInt n = container.nelements(); while (lower < n) { if (container[lower] == value) { return lower; } lower++; } return -1; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Utilities/MUString.cc000066400000000000000000000254161476623553700202350ustar00rootroot00000000000000//# MUString.cc: Pointed String class to ais analysis of quantity strings //# Copyright (C) 1996,1997,1998,1999,2001,2002,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Constructors MUString::MUString() : str(), ptr(0), len(0), stack(0), stpt(0), stat(True), lget() {} MUString::MUString(const String &in) : str(in), ptr(0), len(in.length()), stack(0), stpt(0), stat(True), lget() { } MUString::MUString(const Char *in) : str(in), ptr(0), len(0), stack(0), stpt(0), stat(True), lget() { len = str.length(); } MUString::MUString(Char in) : str(in), ptr(0), len(0), stack(0), stpt(0), stat(True), lget() { len = str.length(); } MUString::MUString(const MUString &other) : str(other.str), ptr(other.ptr), len(other.len), stack(0), stpt(0), stat(True), lget() {} MUString &MUString::operator=(const MUString &other) { if (this != &other) { str = other.str; ptr = other.ptr; len = other.len; stack = Block(0); stpt = 0; stat = True; lget = String(); } return *this; } // Destructor MUString::~MUString() {} // Operators String MUString::operator()() { return get(); } // General member functions void MUString::push() { while (stpt >= stack.nelements()) stack.resize(2*stpt + 1); stack[stpt++] = ptr; } void MUString::pop() { ptr = stpt > 0 ? stack[--stpt] : 0; } void MUString::unpush() { if (stpt > 0) --stpt; } void MUString::skipBlank() { while (ptr < len && testBlank()) ptr++; } Bool MUString::testBlank() const { return (ptr >= len || str[ptr] == ' ' || str[ptr] == '\t'); } Bool MUString::tSkipBlank() { return ((ptr < len && testBlank()) ? (skipBlank(), True) : False); } Bool MUString::testSign() const { return (ptr < len && (str[ptr] == '-' || str[ptr] == '+')); } void MUString::skipSign() { while (testSign()) ptr++; } Bool MUString::tSkipSign() { return (testSign() ? (skipSign(), True) : False); } Int MUString::getSign() { Int t = 1; Int p = initLast(); if (testSign()) { while (testSign()) { if (str[ptr++] == '-') t = -t; } setLast(p); } return t; } Bool MUString::testInt() const { static Regex ex("[-+]*[0-9]"); return testString(ex); } Bool MUString::tSkipInt() { return (testInt() ? (skipInt(), True) : False);; } Bool MUString::testuInt() const { return testNum(); } Bool MUString::tSkipuInt() { return (testuInt() ? (skipuInt(), True) : False); } Int MUString::getInt() { Int s = 0; Int p = initLast(); if (testInt()) { s = getSign(); s *= getuInt(); setLast(p); } return s; } void MUString::skipInt() { getInt(); } uInt MUString::getuInt() { Int t = 0; Int p = initLast(); if (testuInt()) { while (testNum()) { t *= 10; t += str[ptr++] - '0'; } setLast(p); } return t; } void MUString::skipuInt() { getuInt(); } Bool MUString::testDouble() const { static Regex ex ("[-+]?(([0-9]+\\.[0-9]*)|([0-9]+)|(\\.[0-9]+))([eE][+-]?[0-9]+)?"); return testString(ex); } void MUString::skipDouble() { getDouble(); } Bool MUString::tSkipDouble() { return (testDouble() ? (skipDouble(), True) : False); } Double MUString::getDouble() { static Regex ex ("[-+]?(([0-9]+\\.[0-9]*)|([0-9]+)|(\\.[0-9]+))([eE][+-]?[0-9]+)?"); Double res = 0.0; if (ptr < len && testDouble()) { istringstream instr(str.at(ex, ptr)); instr >> res; skipString(ex); } return res; } void MUString::skipChar(Int n) { adjustPtr(ptr + n); } void MUString::skipChar(Char ch) { while (testChar(ch)) ptr++; } Bool MUString::tSkipChar(Char ch) { return (testChar(ch) ? (skipChar(ch), True) : False); } void MUString::skipCharNC(Char ch) { while (testCharNC(ch)) ptr++; } Bool MUString::tSkipCharNC(Char ch) { return (testCharNC(ch) ? (skipCharNC(ch), True) : False); } Bool MUString::tSkipOneChar(Char ch) { if (testChar(ch)) { ptr++; return True; } return False; } Bool MUString::tSkipOneCharNC(Char ch) { if (testCharNC(ch)) { ptr++; return True; } return False; } void MUString::skipChar(const Regex &ex) { while (testChar(ex)) ptr++; } Bool MUString::tSkipChar(const Regex &ex) { return (testChar(ex) ? (skipChar(ex), True) : False); } void MUString::skipAlpha() { while (testAlpha()) ptr++; } Bool MUString::tSkipAlpha() { return (testAlpha() ? (skipAlpha(), True) : False); } void MUString::skipAlphaNum() { if (testAlpha()) { ptr++; while (testAlphaNum()) ptr++; } } Bool MUString::tSkipAlphaNum() { return (testAlpha() ? (skipAlphaNum(), True) : False); } void MUString::skipNum() { while (testNum()) ptr++; } Bool MUString::tSkipNum() { return (testNum() ? (skipNum(), True) : False); } Bool MUString::testChar(Char ch) const { return (ptr < len && str[ptr] == ch); } Bool MUString::testCharNC(Char ch) const { return (ptr < len && (str[ptr] == toupper(ch) || str[ptr] == tolower(ch))); } Bool MUString::testChar(const Regex &ex) const { return (ptr < len && String(str[ptr]).index(ex) == 0); } Bool MUString::testAlpha() const { static Regex ex("[a-zA-Z_]"); return testChar(ex); } Bool MUString::testNum() const { static Regex ex("[0-9]"); return testChar(ex); } Bool MUString::testAlphaNum() const { static Regex ex ("[a-zA-Z_0-9]"); return testChar(ex); } Char MUString::getChar() { return (ptr < len ? str[ptr++] : ' '); } String MUString::getAlpha() { Int p = initLast(); if (tSkipAlpha()) setLast(p); return lget;; } String MUString::getAlphaNum() { Int p = initLast(); if (tSkipAlphaNum()) setLast(p); return lget; } Bool MUString::testString(const Regex &ex) const { return (ptr < len && String(String(str).from(Int(ptr))).index(ex) == 0); } Bool MUString::testString(const String &ex) const { if (ptr < len) { Int tl = (len-ptr < ex.length()) ? len-ptr : ex.length(); String t = String(str)(ptr,tl); return (t.matches(ex)); } return False; } Bool MUString::testStringNC(const String &ex) const { if (ptr < len) { Int tl = (len-ptr < ex.length()) ? len-ptr : ex.length(); String t = String(str)(ptr,tl); t.downcase(); String u = ex; u.downcase(); return (t.matches(u)); } return False; } Bool MUString::tSkipString(const Regex &ex) { return (testString(ex) ? (skipString(ex), True) : False); } Bool MUString::tSkipString(const String &ex) { return (testString(ex) ? (skipString(ex), True) : False); } Bool MUString::tSkipStringNC(const String &ex) { return (testStringNC(ex) ? (skipStringNC(ex), True) : False); } void MUString::skipString(const Regex &ex) { if (testString(ex)) adjustPtr(ptr + str.at(ex, ptr).length()); } void MUString::skipString(const String &ex) { if (testString(ex)) adjustPtr(ptr + ex.length()); } void MUString::skipStringNC(const String &ex) { if (testStringNC(ex)) adjustPtr(ptr + ex.length()); } String MUString::getString(const Regex &ex) { Int p = initLast(); if (tSkipString(ex)) setLast(p); return lget; } String MUString::getString(const String &ex) { Int p = initLast(); if (tSkipString(ex)) setLast(p); return lget; } String MUString::getStringNC(const String &ex) { Int p = initLast(); if (tSkipStringNC(ex)) setLast(p); return lget; } Bool MUString::matchPair(Char nd) { Char st = getChar(); Int cnt = 1; Int p = initLast(); while (ptr < len) { if (testChar(st)) { cnt++; } else if (testChar(nd)) { cnt--; if (cnt == 0) break; } skipChar(); } if (cnt == 0) { setLast(p); skipChar(); return True; } adjustPtr(p-1); return False; } Int MUString::freqChar(Char ch) const { Int c = 0; for (uInt i = ptr; i < len; i++) { if (str[i] == ch) c++; } return c; } String MUString::get() { return get(ptr, len); } String MUString::get(uInt st) { return get(st, len); } String MUString::get(uInt st, uInt nd) { push(); adjustPtr(st); Int p = initLast(); adjustPtr(nd); setLast(p); pop(); return lget; } Int MUString::getPtr() const { return ptr; } void MUString::setPtr(Int in) { adjustPtr(in); } Bool MUString::eos() const { return (ptr >= len); } Bool MUString::status() const { return stat; } const String &MUString::lastGet() const { return lget; } void MUString::adjustPtr(Int in) { ptr = in<0 ? 0 : (in>(Int)len ? len : in); } Int MUString::initLast() { static String em; stat = False; lget = em; return ptr; } void MUString::setLast(Int st) { if (st < (Int)ptr) { stat = True; lget = str(st, ptr-st); } } uInt MUString::minimaxNC(const String &in, Int N_name, const String tname[]) { Int i; String a = upcase(in); // Exact fit? for (i=0; i= N_name) { size_t ia = a.length(); for (i=0; i &tname) { Bool delIt; const String *stor = tname.getStorage(delIt); uInt rt = minimaxNC(in, tname.nelements(), stor); tname.freeStorage(stor, delIt); return rt; } ostream &operator<<(ostream &os, const MUString &in) { if (in.ptr < in.len) os << String(in.str).from(Int(in.ptr)); return os; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Utilities/MUString.h000066400000000000000000000304411476623553700200710ustar00rootroot00000000000000//# MUString.h: Pointed String class to aid analysis of quantity strings //# Copyright (C) 1996,1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MUSTRING_H #define CASA_MUSTRING_H //# Includes #include #include #include #include //# Forward Declarations #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class Regex; // // Pointed String class to aid analysis of quantity strings // // // // //
        253. String // // // // From Measure Utility String // // // // The MUString is a class with a String and an embedded pointer. It can be used // to linearly analyse a string for its semantics. Imagine for instance a // string that represents an angle. It could be formatted as // [+-]hh:mm:ss.ttt // or as [+-]hh[hH]mm[mM] or as // [+-]dd.mm.ss.ttt or with .'s replaced with // dms or as [+-]ddd.fff deg etc.
          // The available methods aid in analysing this string (see example).
          // The following analysis method classes are avaible: //
            //
          • construct -- all constructors create a string with the pointer // starting at the beginning of the string //
          • testX(arg) -- all test methods test if the next available // character(s) fulfill the specified argument test. E.g. // Bool testSign() test if current character is + or -. // If at end of string; False is returned, except for // testBlank(). No pointer update. Any method with // NC at the end (for no-case) will test irrespective // of the case. //
          • skipX(arg) -- all skip methods skip all available character(s) // fulfilling the specified argument. E.g. // void skipSign() will skip all + and - found at // the current and subsequent positions. Pointer updated. //
          • tSkipX(arg) -- will skip as skipX, and return Bool as in testX. // Pointer updated //
          • getX(arg) -- will get the indicated X value from the string. // Pointer updated. A get will always return a valid result. // However, if the value did not exist (e.g. // Double getDouble() form a string like "abc" // will return 0.0) a False status will be saved. It can be // interrogated by the Bool status() function. // The string part used in producing the value is also // saved, and can be obtained with // const String &lastGet(). // No saving in case of a simple getChar() is done. //
          • stack -- if it is necessary to save the current position of the // pointer (for maybe later restoration) a void push() // and void pop() are available //
          • pointer -- the pointer can be manipulated with void setPtr() // and Int getPtr(). Pointers are always protected in // their value. //
          // The following types (X in the above list) are available //
            //
          • Char -- a single character //
          • CharNC -- a single character with no case //
          • String -- a string //
          • StringNC -- a string without case //
          • Alpha -- a through z and A through Z and _ (underscore) //
          • Num -- digits 0 through 9 //
          • AlphaNum -- a field staring with Alpha, and remainder (if any) // Alpha or Num //
          • Sign -- a plus or minus //
          • Blank -- a space ar a tab //
          • uInt -- unsigned integer //
          • Int -- an optionally signed integer //
          • Double -- a double value //
          // General string aids are available. The main one a minimax, caseless // check of an input String against a vector: // static uInt minimaxNC(String in, Int N_name, String name[]) // and its vector equivalent: // static uInt minimaxNC(String in, Vector name). // Success is indicated by a return value less than N_name or the // vector length. //
          // // // See MVAngle class for example background. // The following example is the conversion of different input angle formats // to a Quantity. A full blown example, // but gives some idea of intricacies involved. // // res = Quantity(0.0, "rad"); // result // MUString tmp(in); // Pointed non-const String // tmp.skipBlank(); // Double s = tmp.getSign(); // sign // tmp.push(); // Save position to rescan // Double r = tmp.getuInt(); // first field // Int tp = 0; // distributor // if (tmp.tSkipChar('.')) { // if more than one ., dms format // Double r1 = tmp.getuInt(); // if (tmp.tSkipChar('.')) { // r += r1/60.0 + tmp.getDouble()/3600.0; // tp = 4; // } else { // else value with units // tmp.pop(); // Reset position // r = tmp.getDouble(); // }; // } else if (tmp.tSkipCharNC('d')) { // dms // tp = 1; // } else if (tmp.tSkipCharNC('h')) { // hms // tp = 2; // } else if (tmp.tSkipChar(':')) { // hms // tp = 3; // }; // switch (tp) { // case 0: { // UnitVal u; String us; // if (!MVAngle::unitString(u,us,tmp)) return False; // r *= s; // if (u == UnitVal::NODIM) { // check correct dimension // res = Quantity(r,"rad"); // return True; // }; // if (u == UnitVal::ANGLE) { // res = Quantity(r,us); // return True; // }; // if (u == UnitVal::TIME) { // res = Quantity(Quantity(r/240.,us).getBaseValue(), "deg"); // return True; // }; // return False; // }; // break; // // case 1: // case 2: // case 3: { // get remainder od ms and hms formats // Char tc = 'm'; // if (tp == 3) tc = ':'; // tmp.push(); // Double r1 = tmp.getuInt(); // if (tmp.tSkipChar('.')) { // tmp.pop(); // r += tmp.getDouble()/3600.; // } else if (tmp.tSkipCharNC(tc)) { // r += r1/60.0 + tmp.getDouble()/3600.; // } else { // r += r1/3600.0; // }; // r *= s; // }; // break; // // default: // break; // }; // // switch (tp) { // make correct units // // case 1: // case 4: // res = Quantity(r,"deg"); // break; // // case 2: // case 3: // res = Quantity(Quantity(r/240.,"h").getBaseValue(), "deg"); // break; // // default: // break; // // }; // return True; // // // // // The class was written to be able to analyse an input string for its // Quantum representation as value with // units, or os a date/time or as an angle. // // // //
        254. nothing I know of // class MUString { public: //# Friends // Output String starting at pointer friend ostream &operator<<(ostream &os, const MUString &in); //# Enumerations //# Constructors // Default constructor creates an empty string MUString(); // Create from String; setting pointer at start // MUString(const String &in); MUString(const Char *in); MUString(char in); // // Copy constructor; new pointer will be same as old MUString(const MUString &other); // Copy assignment; new pointer will be same as old MUString &operator=(const MUString &other); // Destructor ~MUString(); //# Operators // Obtain remaining string (same as get()). String operator()(); //# General Member Functions // Save current pointer on internal stack void push(); // Restore pointer from stack (or set to start if stack empty) void pop(); // Restore stack for one level void unpush(); // Act on whitespace; adjusting pointer if skip // void skipBlank(); Bool testBlank() const; Bool tSkipBlank(); // // Act on sign; return +1 or -1 depending on signs found (-- == +) // void skipSign(); Bool testSign() const; Bool tSkipSign(); Int getSign(); // // Act on integer field. If no integer found in 0 returned; and False // void skipInt(); Bool testInt() const; Bool tSkipInt(); Int getInt(); void skipuInt(); Bool tSkipuInt(); Bool testuInt() const; uInt getuInt(); // // Act on Double field. If no value 0 returned and False. // void skipDouble(); Bool testDouble() const; Bool tSkipDouble(); Double getDouble(); // // Act on character(s) // void skipChar(Int n=1); void skipChar(Char ch); Bool tSkipChar(Char nc); void skipCharNC(Char ch); Bool tSkipCharNC(Char ch); Bool tSkipOneChar(Char ch); Bool tSkipOneCharNC(Char ch); void skipChar(const Regex &ex); Bool tSkipChar(const Regex &ex); void skipAlpha(); Bool tSkipAlpha(); void skipNum(); Bool tSkipNum(); void skipAlphaNum(); Bool tSkipAlphaNum(); Bool testChar(Char ch) const; Bool testCharNC(Char ch) const; Bool testChar(const Regex &ex) const; Bool testAlpha() const; Bool testNum() const; Bool testAlphaNum() const; Char getChar(); String getAlpha(); String getAlphaNum(); // // Act on series of characters // Bool testString(const Regex &ex) const; Bool testString(const String &ex) const; Bool testStringNC(const String &ex) const; Bool tSkipString(const Regex &ex); Bool tSkipString(const String &ex); Bool tSkipStringNC(const String &ex); void skipString(const Regex &ex); void skipString(const String &ex); void skipStringNC(const String &ex); String getString(const Regex &ex); String getString(const String &ex); String getStringNC(const String &ex); // // Match a pair of opening(at pointer)/closing characters (e.g. ( and )). // Return False if wrong semantics. The string between the pair // (excluding them) // will be put in Last. If false, the ptr will be as originally; if True // it will point beyond the matched closing character Bool matchPair(Char nd); // Get frequency of occurrence Int freqChar(Char ch) const; // Get part of string // String get(); String get(uInt st); String get(uInt st, uInt nd); // // Get pointer Int getPtr() const; // (Re-)set pointer void setPtr(Int in=0); // test for end of string Bool eos() const; // Get status last get Bool status() const; // Get String found at last get const String &lastGet() const; // Do minimax check on list of Strings // static uInt minimaxNC(const String &in, Int N_name, const String tname[]); static uInt minimaxNC(const String &in, const Vector &tname); // private: // Data // String value String str; // 0-based pointer into string uInt ptr; // Length of string uInt len; // Pointer stack Block stack; // Pointer into stack uInt stpt; // Status of last get Bool stat; // String found at last get String lget; // Member functions // Make a new pointer between 0 and len inclusive void adjustPtr(Int in); // Initialise last settings; return pointer Int initLast(); // Set last settings void setLast(Int st); }; // Global functions // Output global functions // Output // ostream &operator<<(ostream &os, const MUString &in); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Utilities/Precision.cc000066400000000000000000000056651476623553700204640ustar00rootroot00000000000000//# DataType.h: data types (primarily) in the table system //# Copyright (C) 1993,1994,1995,1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN uInt precisionForValueErrorPairs (const Vector& pair1, const Vector& pair2) { Double val1 = fabs(pair1[0]); Double err1 = pair1[1]; Double value = val1; Double error = fabs(err1); if (pair2.size() == 2){ Double val2 = fabs(pair2[0]); Double err2 = pair2[1]; value = max(val1, val2); error = (err1 == 0 || err2 == 0) ? max(fabs(err1), fabs(err2)) : min(fabs(err1), fabs(err2)); } // Here are a few general safeguards // If we are dealing with a value smaller than the estimated error // (e.g., 0.6 +/- 12) , the roles in formatting need to be // reversed. if ( value < error ) { value = max(value,0.1*error); // TODO be cool and figure out a way to swap without using a temporary variable Double tmp = value; value = error; error = tmp; } // A value of precisely 0 formats as if it were 1. If the error is // precisely 0, we print to 3 significant digits if ( value == 0 ) { value = 1; } if ( error == 0 ) { error = 0.1*value; } // Arithmetically we have to draw the limit somewhere. It is // unlikely that numbers (and errors) < 1e-10 are reasonably // printed using this limited technique. value = max(value, 1e-10); error = max(error, 1e-8); // Print only to two significant digits in the error error = 0.1*error; // Generate format // Add little value for possible round-off error uInt after = 0; if ( log10(error) < 0 ) { after = int(fabs(log10(error)) + 1e-8) + 1; } return after; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Utilities/Precision.h000066400000000000000000000036031476623553700203140ustar00rootroot00000000000000//# Precision.h: Determine appropriate precision for printing //# Copyright (C) 1994,1995,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_PRECISION_H #define CASA_PRECISION_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Various precision-related functions // // // // Various functions for determining precision for printing. // // Determine precision for two related value,error pairs (such as RA-Dec) uInt precisionForValueErrorPairs (const Vector& pair1, const Vector& pair2); } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Utilities/PtrHolder.h000066400000000000000000000211221476623553700202600ustar00rootroot00000000000000//# PtrHolder.h: Hold and delete pointers not deleted by object destructors //# Copyright (C) 1994,1995,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_PTRHOLDER_H #define CASA_PTRHOLDER_H //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Hold and delete pointers not deleted by object destructors // // // // // //
        255. module Exceptions // // // PtrHolders hold allocated pointers which should be // deleted when an exception is thrown. Exceptions only call destructors // of objects. Thus, for example, storage allocated in a global function // (outside of an object)is not deleted. A PtrHolder solves // this problem: it merely holds the pointer and deletes it when it is // destroyed itself, e.g. when an exception is thrown or when the // function exits normally. // // // // void func(Int *ptr); // some other function that takes a pointer // // ... // // True below means it's an array, False (the default) would mean // // a singleton object. // PtrHolder iholder(new Int[10000], True); // func(iholder); // converts automatically to ptr // (iholder.ptr() + 5) = 11; // use pointer explicitly // some_function_that_throws_exception(); // pointer is deleted // // // // Avoid leaks when throwing/catching exceptions. // // //
        256. Use the autoptr class from the Standard Library // template class PtrHolder { public: // The default constructor uses a null pointer. [[deprecated("Use std::unique_ptr")]] PtrHolder(); // Construct a PtrHolder from a pointer which MUST have // been allocated from new, since the destructor will // call delete on it. If the pointer is to an array, // i.e. allocated with operator new[], then // isCarray should be set to True. (This parameter is // required because C-arrays need to be deleted with // delete[].) // // After the pointer is placed into the holder, the user should // not manually delete the pointer; the PtrHolder // object will do that, unless set() or // clear() is called with deleteCurrentPtr // set to False. The pointer must also only be put into // one holder to avoid double deletion. [[deprecated("Use std::unique_ptr")]] PtrHolder(T *pointer, Bool isCArray = False); ~PtrHolder(); // Set the pointer to a new value. If deleteCurrentPtr is // True (the default), then delete the existing pointer first. If // isCarray is True, then the new pointer is assumed to // have been allocated with new[]. void set(T *pointer, Bool isCarray = False, Bool deleteCurrentPtr = True); // Set the current pointer to null; if deletePtr is True // (the default), then the current pointer is deleted first. void clear(Bool deleteCurrentPtr = True); // Release the pointer for use. // T *ptr() { return ptr_p; } const T *ptr() const { return ptr_p; } // // Attempt to automatically release a pointer when required. If the // compiler can't figure it out, you can use the ptr() // member function directly. operator T *() { return ptr_p; } operator T *() const { return ptr_p; } // // Make it possible to use -> on the pointer object. T* operator->() const { return ptr_p; } // See if the pointer points to a C-array. Bool isCArray() const {return isCarray_p;} private: //# Undefined and inaccessible PtrHolder(const PtrHolder &other); PtrHolder &operator=(const PtrHolder &other); //# We'd also like the following to be undefined and inaccessible, //# unfortunately CFront doesn't seem to let you do that. //# void *operator new(size_t s); //# Put functionality in one place void delete_pointer_if_necessary(); T *ptr_p; //# If space were critical, we could make isCarray_p a char Bool isCarray_p; }; // // Hold and delete pointers not deleted by object destructors // // // // // //
        257. module Exceptions // // // SPtrHolders hold allocated pointers to non-array objects // which should be deleted when an exception is thrown. // SPtrHolder is similar to PtrHolder, but easier to use and only valid // for pointer to a single object, thus not to a C-array of objects. // // // // void func(Table *ptr); // some other function that takes a pointer // // ... // // True below means it's an array, False (the default) would mean // // a singleton object. // SPtrHolder iholder(new Table(...)); // func(iholder); // converts automatically to ptr // Table* tab = iholder.transfer(); // transfer ownership // // If an exception is thrown in function func, the Table will be // deleted automatically. After the function call, the ownership is tranfered // back to the 'user' // // // std::auto_ptr is harder to use and its future is unclear. //
          // PtrHolder is not fully inlined and has C-array overhead. // Furthermore the automatic conversion to a T* is dangerous, because the // programmer may not be aware that the pointer is maybe taken over. //
          template class SPtrHolder { public: // Construct an SPtrHolder from a pointer which MUST have // been allocated from new, since the destructor will // After the pointer is placed into the holder, the user should // not manually delete the pointer unless the transfer function is called. // The pointer must also only be put into // one holder to avoid double deletion. explicit SPtrHolder (T* ptr = 0) : itsPtr(ptr) {} ~SPtrHolder() { delete itsPtr; } // Reset the pointer. void reset (T* ptr) { if (ptr != itsPtr) { delete itsPtr; itsPtr = ptr; }} // Transfer ownership of the pointer. // I.e. return the pointer and set it to 0 in the object. T* transfer() { T* ptr = itsPtr; itsPtr = 0; return ptr; } // Release the pointer. void release() { itsPtr = 0; } // Make it possible to dereference the pointer object. // T& operator*() { return *itsPtr; } const T& operator*() const { return *itsPtr; } // // Make it possible to use -> on the pointer object. T* operator->() const { return itsPtr; } // Get the pointer for use. // T* ptr() { return itsPtr; } const T* ptr() const { return itsPtr; } // private: // SPrtHolder cannot be copied. // SPtrHolder(const SPtrHolder &other); SPtrHolder &operator=(const SPtrHolder &other); // //# The pointer itself. T* itsPtr; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/casa/Utilities/PtrHolder.tcc000066400000000000000000000043661476623553700206150ustar00rootroot00000000000000//# PtrHolder.cc: Hold pointers to be deleted when exceptions are thrown //# Copyright (C) 1994,1995,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_PTRHOLDER_TCC #define CASA_PTRHOLDER_TCC //# #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template PtrHolder::PtrHolder() : ptr_p (0), isCarray_p(False) {} template PtrHolder::PtrHolder (T *pointer, Bool isCarray) : ptr_p (pointer), isCarray_p(isCarray) {} template void PtrHolder::set (T *pointer, Bool isCarray, Bool deleteCurrentPtr) { if (deleteCurrentPtr) { delete_pointer_if_necessary(); } ptr_p = pointer; isCarray_p = isCarray; } template void PtrHolder::clear (Bool deleteCurrentPtr) { if (deleteCurrentPtr) { delete_pointer_if_necessary(); } ptr_p = 0; } template void PtrHolder::delete_pointer_if_necessary() { if (ptr_p) { if (isCarray_p) { delete [] ptr_p; } else { delete ptr_p; } ptr_p = 0; } } template PtrHolder::~PtrHolder() { delete_pointer_if_necessary(); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Utilities/RecordTransformable.cc000066400000000000000000000034601476623553700224560ustar00rootroot00000000000000//# RecordTransformable.cc: //# Copyright (C) 1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN RecordTransformable::~RecordTransformable() { // Nothing } Bool RecordTransformable::fromString(String & error, const String & inString) { if (False) inString.empty(); // stop warning error += "Cannot initialise this object from a string\n"; return False; } const String &RecordTransformable::ident() const { static String myid = String(); return myid; } // Local Variables: // compile-command: "gmake OPTLIB=1 RecordTransformable" // End: } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Utilities/RecordTransformable.h000066400000000000000000000131061476623553700223160ustar00rootroot00000000000000//# RecordTransformable.h: Interface class for converting to/from records //# Copyright (C) 1998,1999,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_RECORDTRANSFORMABLE_H #define CASA_RECORDTRANSFORMABLE_H #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class String; class RecordInterface; // Interface class for converting to/from records // // // // //
        258. RecordInterface // // // // This class defines the interface that a class should use if the can be // transformed into a record representation. // // // // This abstract base class is intended to be publicly inherited by classes // that contain functions which can represent the object as a record (these // functions should be called toRecord and // fromRecord). Examples of records are: //
            //
          • Record //
          • TableRecord //
          // // This interface defines two functions that convert between a RecordInterface // and the class that inherits these functions. These functions are often used // to parse input that is beyond the programs control e.g. user input from // glish or Table records that may have been generated elsewhere. Hence // exceptions should not thrown be thrown by these functions. Instead the // function should return False and append an error message to the supplied // String when the transformation cannot be accomplished. // // // Converting to/from a GlishRecord requires an extra step. // First a Record should be used which can thereafter be converted to/from // a GlishRecord using the appropriate GlishRecord functions. // //
          // // // The following example prints out a class using its record representation. // This example is in the file tRecordTransformable.cc // // void printAsRecord(const RecordTransformable & myClass) { // String errorMessage; // Record rec; // if (!myClass.toRecord(errorMessage, rec)) { // cout << "Cannot convert class to a Record. The reason is:" << endl; // cout << errorMessage << endl; // } else { // cout << rec.ndefined() << endl; // } // } // // // // // This class was designed to standardise the function interface for converting // between an object and its record representation. // // // //
        259. Nothing I hope! // class RecordTransformable { public: // The destructor must be virtual so that the destructor of derived classes // is actually used. virtual ~RecordTransformable(); // Convert the class to an Record representation. The input record may // already contain fields and these fields may be silently overridden. New // fields may be added to the input Record. If the transformation succeeds // then the error String is unchanged and the function returns // True. Otherwise the function returns False and appends an error message to // the supplied String giving the reason why the conversion failed. virtual Bool toRecord(String & error, RecordInterface & outRecord) const = 0; // Initialise the class from a Record representation. The input record should // contain the fields that are required by the class. Other fields will be // ignored. If the transformation succeeds then the error String is unchanged // and the function returns True. Otherwise the function returns False and // appends an error message to the supplied String giving the reason why the // conversion failed. virtual Bool fromRecord(String & error, const RecordInterface & inRecord) =0; // Initialise the class from a String representation. A string cannot // contain enough information for many objects. Hence the default // implementation of this class returns False, indicating that the class // could not be initialised and an error message is appended to the supplied // string. If the class can be initialised from a string then this function // should be overridden. virtual Bool fromString(String & error, const String & inString); // Specify the identification of the record (e.g. 'meas', 'quant'). The // default implementation returns a empty string. virtual const String &ident() const; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Utilities/Regex.cc000066400000000000000000000373461476623553700176040ustar00rootroot00000000000000//# Regex.cc: Regular expression class //# Copyright (C) 1993,1994,1995,1996,1997,2000,2001,2002,2003,2019 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // Regex class implementation #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Regex::Regex() {} Regex::Regex(const String& str, Bool fast, Bool toECMAScript) : itsStr (str) { // Make the possible exception thrown by regex a bit more clear. try { std::regex::flag_type flags = std::regex::ECMAScript; if (fast) { flags |= std::regex::optimize; } if (toECMAScript) { std::regex::operator= (std::regex(toEcma(str), flags)); } else { std::regex::operator= (std::regex(str, flags)); } } catch (const std::exception& x) { throw AipsError ("Error in regex " + str + ": " + x.what()); } } void Regex::operator=(const String& str) { std::regex::operator= (Regex(str)); itsStr = str; } String::size_type Regex::match(const Char* s, String::size_type len, String::size_type pos) const { Int ps = static_cast(pos); if (ps < 0) { ps += len; } if (ps < 0) return String::npos; // A zero-length string can match .* // Therefore try fullMatch for such a case. if (ps == static_cast(len) && fullMatch (s+ps, 0)) { return 0; } if (ps >= static_cast(len)) return String::npos; Int matchlen; String::size_type res = search(s, len, matchlen, ps); if (res != String::npos) { if (static_cast(res) == ps) { res = matchlen; } else { res = String::npos; // no match from start on } } return res; } Bool Regex::fullMatch(const Char* s, String::size_type len) const { return std::regex_match(s, s+len, *this); } String::size_type Regex::search(const Char* s, String::size_type len, Int& matchlen, Int pos) const { // Searching from the end means trying to match from the end on. if (pos < 0) { return searchBack (s, len, matchlen, -pos); } if (pos >= static_cast(len)) return String::npos; std::cmatch result; if (std::regex_search(s+pos, s+len, result, *this)) { matchlen = result.length(0); return pos + result.position(0); // start position of match } matchlen = 0; return String::npos; // no match } String::size_type Regex::searchBack(const Char* s, String::size_type len, Int& matchlen, uInt pos) const { if (pos >= len) { return String::npos; } for (Int p = len-pos; p>=0; --p) { String::size_type ml = match(s, len, p); if (ml != String::npos) { matchlen = ml; return p; } } matchlen = 0; return String::npos; // no match } String::size_type Regex::find(const Char* s, String::size_type len, Int& matchlen, String::size_type pos) const { Int xpos = pos; if (xpos<0) return String::npos; return search(s, len, matchlen, xpos); } ostream& operator<<(ostream& ios, const Regex& exp) { return ios << exp.itsStr; } String Regex::toEcma(const String& rx) { Int inbrcount = -1; Bool inBracket = False; Bool charClass = False; Bool escaped = False; uInt pattLeng = rx.length(); String result; result.reserve (rx.size()); for (uInt i=0; i= '1' && c <= '9') { // This is a backreference. // Put brackets around the next character if numeric as well. // Note backreferences cannot be used in a bracket expression. if (i+1 < pattLeng && rx[i+1] >= '0' && rx[i+1] <= '9') { result.push_back (c); result.push_back ('['); result.push_back (rx[i+1]); c = ']'; i++; } } } else if (c == '\\') { escaped = True; inbrcount = 1; // in case escaped inside bracket expression } else if (!inBracket) { if (c == '[') { // Opening bracket puts us in a bracket expression. inBracket = True; charClass = False; inbrcount = -1; // to know if ] is normal or end of br.expr. } else if (c == ']') { // Outside a bracket expression ] has to be escaped as well. result.push_back ('\\'); } } else if (c == ']') { if (inbrcount > 0) { // A closing bracket puts us back in the normal state. // But a closing bracket immediately after the start of a bracket // expression is a literal ] and not the end of the expression. // It has to be escaped in Ecma. inBracket = False; } else { result.push_back ('\\'); inbrcount = 0; } } else if (c != '^') { // A starting ^ is a not and does not count yet. // I.e., a ] given hereafter is not the end of bracket expression yet. // Some character is found. inbrcount = 0; // An opening bracket followed by a colon is the start of a // Posix character class. // Go to next char, so closing bracket in [[:] is end of // bracket and not end of character class. // Otherwise [ has to be escaped. if (c == '[') { if (i+1 emptySubStr; uInt pattLeng = pattern.length(); String result; result.reserve (3*pattLeng); CState state = stream; for (uInt i=0; i 0) { result.push_back (c); c = '?'; } emptySubStr.pop_back(); } else { result.push_back ('\\'); } break; case '[': // Opening bracket puts us in a special state. state = bracketopen; charClass = False; inbrcount = -1; break; case '*': // * gets .* result.push_back ('.'); break; case '?': // ? gets . c = '.'; break; case '\\': // Backslash puts us in a special state. state = escapechar; break; // leave all other chars unchanged } break; case bracketopen: if (c == ']' && inbrcount > 0) { // A closing bracket immediately after the start of a bracket // expression is a literal ] and not the end of the expression. // Otherwise a closing bracket puts us back in the normal state. state = stream; } else if ((c == '!' || c == '^') && inbrcount < 0) { // A starting ! or ^ is a not and does not count yet. c = '^'; } else { // Some character is found. if (inbrcount < 0) { inbrcount = 0; } // An opening bracket followed by a colon is the start of a // Posix character class. // Go to next char, so closing bracket in [[:] is end of // bracket and not end of character class. if (c == '[' && i+1= 0) { if (inBracket) { // If a range, the entire range must be copied if both ends are alpha. if (i+2 < strLeng && str[i+1] == '-' && isalpha(str[i+2])) { i += 2; int ec = str[i]; result.push_back (c1); result.push_back ('-'); result.push_back (ec); if (islower(ec)) { result.push_back (c2); result.push_back ('-'); result.push_back (toupper(ec)); } else if (isupper(ec)) { result.push_back (c2); result.push_back ('-'); result.push_back (tolower(ec)); } } else { // Add to the bracketed characters. result.push_back (c1); result.push_back (c2); } } else { // It was a single character; put in brackets now. result.push_back ('['); result.push_back (c1); result.push_back (c2); result.push_back (']'); } } else { // Copy any other char. result.push_back (c); } } } return result; } // some built-in Regular expressions const Regex RXwhite("[ \n\t\r\v\f]+"); const Regex RXint("[-+]?[0-9]+"); const Regex RXdouble("[-+]?(([0-9]+\\.[0-9]*)|([0-9]+)|(\\.[0-9]+))([eE][+-]?[0-9]+)?"); const Regex RXalpha("[A-Za-z]+"); const Regex RXlowercase("[a-z]+"); const Regex RXuppercase("[A-Z]+"); const Regex RXalphanum("[0-9A-Za-z]+"); const Regex RXidentifier("[A-Za-z_][A-Za-z0-9_]*"); } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Utilities/Regex.h000066400000000000000000000316321476623553700174360ustar00rootroot00000000000000//# Regex.h: Regular expression class //# Copyright (C) 1993,1994,1995,1996,1997,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_REGEX_H #define CASA_REGEX_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Regular expression class (based on std::regex) // // // // // // This class provides regular expression functionality, such as // matching and searching in strings, comparison of expressions, and // input/output. It is built on the standard C++ regular expression class // using the ECMAScript syntax. It is almost the same as the regular expression // syntax used until March 2019 which used GNU's cregex.cc. // ECMAScript offers more functionality (such as non-greedy matching), // but there is a slight difference how brackets are used. In the old // regex they did not need to be escaped, while they have to for ECMAScript. // Furthermore, in the old Regex up to 9 backreferences could be given, so // \15 meant the first backreference followed by a 5. In ECMAScript it means // the 15th and parentheses are needed to get the old meaning. // These differences are solved in the Regex constructor which adds escape // characters as needed. Thus existing code using Regex does not need to be changed. // // Apart from proper regular expressions, it also supports glob patterns // (UNIX file name patterns) by means of a conversion to a proper regex string. // Also ordinary strings and SQL-style patterns can be converted to a proper // regex string. //

          // See http://www.cplusplus.com/reference/regex/ECMAScript for the syntax. //

          //
          ^ //
          matches the beginning of a line. //
          $ //
          matches the end of a line. //
          . //
          matches any character //
          * //
          zero or more times the previous subexpression. //
          + //
          one or more times the previous subexpression. //
          ? //
          zero or one time the previous subexpression. //
          {n,m} //
          interval operator to specify how many times a subexpression // can match. See man page of egrep or regexp for more detail. //
          [] //
          matches any character inside the brackets; e.g. [abc]. // A hyphen can be used for a character range; e.g. [a-z]. //
          // A ^ right after the opening bracket indicates "not"; // e.g. [^abc] means any character but a, b, and c. // If ^ is not the first character, it is a literal caret. // If - is the last character, it is a literal hyphen. // If ] is the first character, it is a literal closing bracket. //
          // Special character classes are // [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:], // [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:]. // The brackets are part of the name; e.g. // [^[:upper:]] is equal to [^A-Z]. // Note that [:upper:] is more portable, because A-Z fails // for the EBCDIC character set. //
          ( ) //
          grouping to change the normal operator precedence. //
          | //
          or operator. Matches left side or right side. //
          \\1 till \\9. Backreference to a subexpression. Matches part of string // equal to string part that matched the subexpression. //
          // Special characters have to be escaped with a backslash to use them // literally. Only inside the square brackets, escaping should not be done. // See the man page of egrep or regexp for more information about // regular expressions. //

          // Several global Regex objects are predefined for common functionality. //

          //
          RXwhite //
          one or more whitespace characters //
          RXint //
          integer number (also negative) //
          RXdouble //
          double number (with e or E as exponent) //
          RXalpha //
          one or more alphabetic characters (lowercase and/or uppercase) //
          RXlowercase //
          lowercase alphabetic //
          RXuppercase //
          uppercase alphabetic //
          RXalphanum //
          one or more alphabetic/numeric characters (lowercase and/or uppercase) //
          RXidentifier //
          identifier name (first alphabetic or underscore, then zero or // more alphanumeric and/or underscores //
          // The static member function fromPattern converts a shell-like // pattern to a String which can be used to create a Regex from it. // A pattern has the following special characters: //
          //
          * //
          Zero or more arbitrary characters. //
          ? //
          One arbitrary character //
          [] //
          The same as [] in a regular expression (see above). // In addition to ^ a ! can be used to indicate "not". //
          {,} //
          A brace expression which is like brace expansion in some shells. // It is similar to the | construct in a regular expression. //
          // E.g. {abc,defg} means abc or defg. // Brace expressions can be nested and can contain other // special characters. //
          // E.g. St{Man*.{h,cc},Col?*.{h,cc,l,y}} //
          A literal comma or brace in a brace expression can be given by // escaping it with a backslash. //
          // The static member function fromSQLPattern converts an SQL-like // pattern to a String which can be used to create a Regex from it. // A pattern has the following special characters: //
          //
          % //
          Zero or more arbitrary characters. //
          _ //
          One arbitrary character //
          // The static member function fromString converts a normal // string to a regular expression. This function escapes characters in // the string which are special in a regular expression. In this way a // normal string can be passed to a function taking a regular expression. // // The static member function makeCaseInsensitive returns a // new regular expression string containing the case-insensitive version of // the given expression string. //
          // // // Regex RXwhite("[ \n\t\r\v\f]+"); // (blank, newline, tab, return, vertical tab, formfeed) // Regex RXint("[-+]?[0-9]+"); // Regex RXdouble("[-+]?(([0-9]+\\.[0-9]*)|([0-9]+)|(\\.[0-9]+))([eE][+-]?[0-9]+)?"); // Regex RXalpha("[A-Za-z]+"); // Regex RXlowercase("[a-z]+"); // Regex RXuppercase("[A-Z]+"); // Regex RXalphanum("[0-9A-Za-z]+"); // Regex RXidentifier("[A-Za-z_][A-Za-z0-9_]*"); // // In RXdouble the . is escaped via a backslash to get it literally. // The second backslash is needed to escape the backslash in C++. // // Regex rx1 (Regex::fromPattern ("St*.{h,cc}"); // results in regexp "St.*\.((h)|(cc))" // Regex rx2 (Regex::fromString ("tRegex.cc"); // results in regexp "tRegex\.cc" // // //# //# class Regex: std::regex { public: // Default constructor uses a zero-length regular expression. Regex(); // Construct a regular expression from the string. // If toECMAScript=True, function toEcma is called to convert the old cregex // syntax to the new ECMAScript syntax. // If fast=True, matching efficiency is preferred over efficiency constructing // the regex object. explicit Regex(const String& exp, Bool fast=False, Bool toECMAScript=True); // Construct a new regex (using the default Regex constructor arguments). void operator=(const String& str); // Convert the possibly old-style regex to the Ecma regex which means // that unescaped [ and ] inside a bracket expression will be escaped and // that a numeric character after a backreference is enclosed in brackets // (otherwise the backreference uses multiple characters). static String toEcma(const String& rx); // Convert a shell-like pattern to a regular expression string. // This is useful for people who are more familiar with patterns // than with regular expressions. static String fromPattern(const String& pattern); // Convert an SQL-like pattern to a regular expression string. // This is useful TaQL which mimics SQL. static String fromSQLPattern(const String& pattern); // Convert a normal string to a regular expression string. // This consists of escaping the special characters. // This is useful when one wants to provide a normal string // (which may contain special characters) to a function working // on regular expressions. static String fromString(const String& str); // Create a case-insensitive regular expression string from the given // regular expression string. // It does it by inserting the lowercase and uppercase version of // characters in the input string into the output string. static String makeCaseInsensitive (const String& str); // Get the regular expression string. const String& regexp() const { return itsStr; } // Test if the regular expression matches (first part of) string s. // The return value gives the length of the matching string part, // or String::npos if there is no match or an error. // The string has len characters and the test starts at // position pos. The string may contain null characters. // Negative p is allowed to define the start from the end. // // // Use the appropriate String functions // to test if a string matches a regular expression. // Regex::match is pretty low-level. // String::size_type match(const Char* s, String::size_type len, String::size_type pos=0) const; // Test if the regular expression matches the entire string. Bool fullMatch(const Char* s, String::size_type len) const; // Test if the regular expression occurs anywhere in string s. // The return value gives the position of the first substring // matching the regular expression. The length of that substring // is returned in matchlen. // The string has len characters and the test starts at // position pos. The string may contain null characters. // If the pos given is negative, the search starts -pos from the end. // // Use the appropriate String functions // to test if a regular expression occurs in a string. // Regex::search is pretty low-level. // // String::size_type search(const Char* s, String::size_type len, Int& matchlen, Int pos=0) const; String::size_type find(const Char* s, String::size_type len, Int& matchlen, String::size_type pos=0) const; // // Search backwards. String::size_type searchBack(const Char* s, String::size_type len, Int& matchlen, uInt pos) const; // Write the regex string. friend ostream& operator<<(ostream& ios, const Regex& exp); protected: String itsStr; // the reg. exp. string }; // some built in regular expressions extern const Regex RXwhite; //# = "[ \n\t\r\v\f]+" extern const Regex RXint; //# = "-?[0-9]+" extern const Regex RXdouble; //# = "-?(([0-9]+\\.[0-9]*)| //# ([0-9]+)|(\\.[0-9]+)) //# ([eE][+-]?[0-9]+)?" extern const Regex RXalpha; //# = "[A-Za-z]+" extern const Regex RXlowercase; //# = "[a-z]+" extern const Regex RXuppercase; //# = "[A-Z]+" extern const Regex RXalphanum; //# = "[0-9A-Za-z]+" extern const Regex RXidentifier; //# = "[A-Za-z_][A-Za-z0-9_]*" } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Utilities/Sequence.h000066400000000000000000000052501476623553700201310ustar00rootroot00000000000000//# Sequence.h: provides sequences //# Copyright (C) 1993,1994,1995,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_SEQUENCE_H #define CASA_SEQUENCE_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // virtual templated base class for sequences // // // // // The virtual base class for sequences in the library. // It is templated to allow users to derive sequences of any type, // e.g. libg++'s Integers. // template class Sequence { public: virtual ~Sequence(){}; // Force derived classes to provide this function, to return the // next value in the sequence. virtual t getNext () = 0; }; // uInt sequence for general use // // // // // This class provides a uInt based sequence for general use. // class uIntSequence : public Sequence { public: // Get the next uInt value in the sequence (thread-safe). // uInt getNext() { return SgetNext(); } static uInt SgetNext(); // private: #if defined(USE_THREADS) static std::atomic next; #else static uInt next; #endif }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/casa/Utilities/Sequence.tcc000066400000000000000000000030221476623553700204460ustar00rootroot00000000000000//# Sequence.cc: provides sequences //# Copyright (C) 1993,1994,1995,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_SEQUENCE_TCC #define CASA_SEQUENCE_TCC #include namespace casacore { //# NAMESPACE CASACORE - BEGIN /* // Inlined this in Sequence.h so this is now // just a dummy file template Sequence::~Sequence() { // Nothing } */ } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Utilities/Sequence2.cc000066400000000000000000000032351476623553700203520ustar00rootroot00000000000000//# Sequence.cc: Templated virtual base class for sequences //# Copyright (C) 1993,1994,1995 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include namespace casacore { //# NAMESPACE CASACORE - BEGIN #if defined(USE_THREADS) std::atomic uIntSequence::next(1); // start at 1 to stay in sync with RegSequence, FIXME fix comment, RegSequnce no longer exists #else uInt uIntSequence::next = 1; #endif uInt uIntSequence::SgetNext() { #if defined(USE_THREADS) return next.fetch_add(1); #else return next++; #endif } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Utilities/Sort.cc000066400000000000000000000215461476623553700174540ustar00rootroot00000000000000//# Sort.cc: Sort on one or more keys, ascending and/or descending //# Copyright (C) 1993,1994,1995,1996,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include ///#include #include // for rand #ifdef _OPENMP # include #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN SortKey::SortKey (const void* dat, const std::shared_ptr& cmpobj, uInt inc, int opt) : order_p (opt), data_p (dat), incr_p (inc), ccmpObj_p (cmpobj), cmpObj_p (cmpobj.operator->()) { if (order_p != Sort::Descending) { order_p = Sort::Ascending; // make sure order has correct value } } SortKey::SortKey (const SortKey& that) : order_p (that.order_p), data_p (that.data_p), incr_p (that.incr_p), ccmpObj_p (that.ccmpObj_p), cmpObj_p (that.cmpObj_p) {} SortKey::~SortKey() {} SortKey& SortKey::operator= (const SortKey& that) { if (this != &that) { order_p = that.order_p; data_p = that.data_p; incr_p = that.incr_p; ccmpObj_p = that.ccmpObj_p; cmpObj_p = that.cmpObj_p; } return *this; } uInt SortKey::tryGenSort (Vector& indexVector, uInt nrrec, int opt) const { Sort::Order ord = (order_p < 0 ? Sort::Ascending : Sort::Descending); DataType dtype = cmpObj_p->dataType(); if (dtype == TpDouble) { if (incr_p == sizeof(Double)) { return GenSortIndirect::sort (indexVector, (Double*)data_p, nrrec, ord, opt); } } else if (dtype == TpFloat) { if (incr_p == sizeof(Float)) { return GenSortIndirect::sort (indexVector, (Float*)data_p, nrrec, ord, opt); } } else if (dtype == TpUInt) { if (incr_p == sizeof(uInt)) { return GenSortIndirect::sort (indexVector, (uInt*)data_p, nrrec, ord, opt); } } else if (dtype == TpInt) { if (incr_p == sizeof(Int)) { return GenSortIndirect::sort (indexVector, (Int*)data_p, nrrec, ord, opt); } } else if (dtype == TpInt64) { if (incr_p == sizeof(Int64)) { return GenSortIndirect::sort (indexVector, (Int64*)data_p, nrrec, ord, opt); } } else if (dtype == TpString) { if (incr_p == sizeof(String)) { return GenSortIndirect::sort (indexVector, (String*)data_p, nrrec, ord, opt); } } return 0; } uInt64 SortKey::tryGenSort (Vector& indexVector, uInt64 nrrec, int opt) const { Sort::Order ord = (order_p < 0 ? Sort::Ascending : Sort::Descending); DataType dtype = cmpObj_p->dataType(); if (dtype == TpDouble) { if (incr_p == sizeof(Double)) { return GenSortIndirect::sort (indexVector, (Double*)data_p, nrrec, ord, opt); } } else if (dtype == TpFloat) { if (incr_p == sizeof(Float)) { return GenSortIndirect::sort (indexVector, (Float*)data_p, nrrec, ord, opt); } } else if (dtype == TpUInt) { if (incr_p == sizeof(uInt)) { return GenSortIndirect::sort (indexVector, (uInt*)data_p, nrrec, ord, opt); } } else if (dtype == TpInt) { if (incr_p == sizeof(Int)) { return GenSortIndirect::sort (indexVector, (Int*)data_p, nrrec, ord, opt); } } else if (dtype == TpInt64) { if (incr_p == sizeof(Int64)) { return GenSortIndirect::sort (indexVector, (Int64*)data_p, nrrec, ord, opt); } } else if (dtype == TpString) { if (incr_p == sizeof(String)) { return GenSortIndirect::sort (indexVector, (String*)data_p, nrrec, ord, opt); } } return 0; } Sort::Sort() : nrkey_p (0), data_p (0), size_p (0), order_p (0) {} Sort::Sort (const void* dat, uInt sz) : nrkey_p (0), data_p (dat), size_p (sz), order_p (0) {} Sort::Sort (const Sort& that) : nrkey_p (0), data_p (0), size_p (0), order_p (0) { copy (that); } Sort::~Sort() { for (size_t i=0; i& cmp, uInt inc, Order ord) { addKey (new SortKey(dat, cmp, inc, ord)); } void Sort::sortKey (uInt off, DataType dt, Order ord) { if (data_p == 0) { throw SortNoData(); } addKey ((char*)data_p+off, dt, size_p, ord); } void Sort::sortKey (uInt off, const std::shared_ptr& cmp, Order ord) { if (data_p == 0) { throw SortNoData(); } addKey (new SortKey ((char*)data_p+off, cmp, size_p, ord)); } void Sort::addKey (const void* dat, DataType dt, uInt inc, int ord) { uInt sz = ValType::getTypeSize (dt); if (inc != 0) { if (sz > inc) { throw SortInvIncr(); } sz = inc; } addKey (new SortKey (dat, ValType::getCmpObj(dt), sz, ord)); } void Sort::addKey (SortKey* key) { if (nrkey_p == 0) { order_p = key->order(); } else if (order_p != key->order()) { order_p = 0; // mixed order } if (nrkey_p >= keys_p.nelements()) { keys_p.resize (keys_p.nelements() + 32); } keys_p[nrkey_p++] = key; } uInt Sort::sort (Vector& indexVector, uInt nrrec, int options, Bool tryGenSort) const { return doSort (indexVector, nrrec, options, tryGenSort); } uInt64 Sort::sort (Vector& indexVector, uInt64 nrrec, int options, Bool tryGenSort) const { return doSort (indexVector, nrrec, options, tryGenSort); } uInt Sort::unique (Vector& uniqueVector, uInt nrrec) const { return doUnique (uniqueVector, nrrec); } uInt Sort::unique (Vector& uniqueVector, const Vector& indexVector) const { return doUnique (uniqueVector, indexVector); } uInt Sort::unique (Vector& uniqueVector, Vector& changeKey, const Vector& indexVector) const { return doUnique (uniqueVector, changeKey, indexVector); } uInt64 Sort::unique (Vector& uniqueVector, uInt64 nrrec) const { return doUnique (uniqueVector, nrrec); } uInt64 Sort::unique (Vector& uniqueVector, const Vector& indexVector) const { return doUnique (uniqueVector, indexVector); } uInt64 Sort::unique (Vector& uniqueVector, Vector& changeKey, const Vector& indexVector) const { return doUnique (uniqueVector, changeKey, indexVector); } // } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Utilities/Sort.h000066400000000000000000000434661476623553700173230ustar00rootroot00000000000000//# Sort.h: Sort objects on one or more keys //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_SORT_H #define CASA_SORT_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Define a Sort key // // // // // SortKey is a helper class for the Sort class. // It holds the following information about a sort key: //
            //
          • Address of the data array containing the sort key; //
          • A std::shared_ptr to a comparison object to be used // (of a class derived from the abstract base class BaseCompare). //
          • Increment for the next data point -- this lets you specify a // stride for keys embedded in a struct; //
          • Sort order -- ascending or descending; //
          //
          class SortKey { public: friend class Sort; // Define a sort key in a given data array using the indicated // comparison object, stride and sort order. SortKey (const void* data, const std::shared_ptr&, uInt increment, int order); // Copy constructor (copy semantics). SortKey (const SortKey&); ~SortKey(); // Assignment (copy semantics). SortKey& operator= (const SortKey&); // Try if GenSort can be used for this single key. // If it succeeds, it returns the resulting number of elements. // Otherwise it returns 0. uInt tryGenSort (Vector& indexVector, uInt nrrec, int opt) const; uInt64 tryGenSort (Vector& indexVector, uInt64 nrrec, int opt) const; // Get the sort order. int order() const { return order_p; } protected: // sort order; -1 = ascending, 1 = descending int order_p; // address of first data point const void* data_p; // increment for next data point uInt incr_p; // comparison object; use std::shared_ptr for memory management std::shared_ptr ccmpObj_p; // comparison object; use raw pointer for performance BaseCompare* cmpObj_p; }; // Sort on one or more keys, ascending and/or descending // // // // // Sort lets you sort data on one or more keys in a mix of // Sort::ascending and Sort::descending order. // Duplicates can be skipped by giving the option // Sort::NoDuplicates. Only in this case the number of output // elements can be different from the number of input elements. //
          The unique function offers another way of getting // unique values. //

          // Class Sort does not sort the data themselves, but // returns an index to them. This gives more flexibility and // allows the sort to be stable; but it is slower. //
          Very fast sorting of the data themselves can be done with the // functions in class GenSort. // If sorting on a single key with a standard data type is done, // Sort will use GenSortIndirect to speed up the sort. //
          // Four sort algorithms are provided: //

          //
          Sort::ParSort //
          The parallel merge sort is the fastest if it can use multiple threads. // For a single thread it has O(n*log(n)) behaviour, but is slower // than quicksort. // A drawback is that it needs an extra index array to do the merge. //
          Sort::InsSort //
          Insertion sort has O(n*n) behaviour, thus is very slow for large // arrays. However, it is the fastest method for small arrays // (< 50 elements) and for arrays already (almost) in the right order. //
          Sort::QuickSort //
          Care has been taken to solve the well-known quicksort problems // like "array already in order" or "many equal elements". The // behaviour is O(n*log(n)) in all the cases tested, even in // degenerated cases where the SUN Solaris qsort algorithm is O(n*n). //
          Sort::HeapSort //
          Heapsort has O(n*log(n)) behaviour. Its speed is lower than // that of QuickSort, so QuickSort is the default algorithm. //
          // The default is to use QuickSort for small arrays or if only a single // thread can be used. Otherwise ParSort is the default. // // All sort algorithms are stable, which means that the original // order is kept when keys are equal. // // The sort is a four step process: //
            //
          1. Construct the Sort object. //
          2. Define the sort keys. The function sortKey must be // called for each sort key (the most significant one first). // The comparison object can be passed in directly, or a // basic data type // can be given. In the latter case the appropriate ObjCompare // comparison object will be created. //
          3. Sort the data. The function sort returns an index // array, which is allocated when needed. //
          4. Destruct the Sort object (usually done automatically) // and delete the index array. //
          // The data can be in a single array of structs, in separate arrays, or // in a mix of those. Of course, all arrays must have the same length. // The data can be passed to the Sort constructor and/or to the // sortKey function. If passed to the Sort constructor, // the offset of the data item in the data array must be given to // sortKey. //
          // // In the first example we sort the data contained in two "parallel" // arrays, idata and ddata, both of length // nrdata. // // Sort sort; // sort.sortKey (idata, TpInt); // define 1st sort key // sort.sortKey (ddata, TpDouble,0,Sort::Descending); // define 2nd sort key // Vector inx; // sort.sort (inx, nrdata); // for (uInt i=0; i // Now nr contains the nr of records (=nrdata) // and inx an array of (sorted) indices. // // In the second example we sort the data stored in an array of structs // on the double (ascending) and the string (descending). We can pass // the data to the Sort constructor, and the offsets of the // struct elements to the sortKey function. // // struct Ts { // String as; // double ad; // } // Vector inx; // Sort sort (tsarr, sizeof(Ts)); // sort.sortKey ((char*)&tsarr[0].ad - (char*)tsarr, TpDouble); // sort.sortKey ((char*)&tsarr[0].as - (char*)tsarr, TpString, // Sort::Descending); // sort.sort (inx, nrts); // // Note that the first argument in function sortKey gives // the offset of the variable in the struct. // // Alternatively, and probably slightly easier, we could pass the data // to the sortKey function and use an increment: // // struct Ts { // String as; // double ad; // } // Vector inx; // Sort sort; // sort.sortKey (&tsarr[0].ad, TpDouble, sizeof(Ts)); // sort.sortKey (&tsarr[0].as, TpString, sizeof(Ts), Sort::Descending); // sort.sort (inx, nrts); // // // Finally, we could provide a comparison object for the struct. // // struct Ts { // String as; // double ad; // } // class MyCompare: public BaseCompare { // virtual int comp (const void* val1, const void* val2) const // { // const Ts& t1 = *(Ts*)val1; // const Ts& t2 = *(Ts*)val2; // if (t1.ad < t2.ad) return -1; // if (t1.ad > t2.ad) return 1; // if (t1.as < t2.as) return 1; // string must be descending // if (t1.as > t2.as) return -1; // return 0; // } // }; // Vector inx; // Sort sort; // sort.sortKey (tsarr, compareTs, sizeof(Ts)); // sort.sort (inx, nrts); // class Sort { public: // Enumerate the sort options: enum Option {DefaultSort=0, // ParSort, but QuickSort for small array HeapSort=1, // use Heapsort algorithm InsSort=2, // use insertion sort algorithm QuickSort=4, // use Quicksort algorithm ParSort=8, // use parallel merge sort algorithm NoDuplicates=16}; // skip data with equal sort keys // Enumerate the sort order: enum Order {Ascending=-1, Descending=1}; // The default constructor can be used when the data is only passed // in via function sortKey. Sort(); // Construct a Sort object for the given data array with elements // of elementSize bytes. This data array will be used // when an offset is given to the sortKey functions. // You can still pass additional data arrays to the // sortKey functions. Sort (const void* data, uInt elementSize); // Copy constructor (copy semantics). Sort (const Sort&); ~Sort(); // Assignment (copy semantics). Sort& operator= (const Sort&); // Define a sort key (the most significant key should be defined first). // The key contains: //
            //
          • A pointer to the start of the data array. --- When structs are // sorted on an element in the struct, the pointer must point to // that element in the first struct. //
          • A pointer to the comparison object to be used. --- The // comparison object can be specified in two ways: //
              //
            • by giving a // basic data type, // in which case the appropriate comparison object will be // created automatically, or //
            • by a std::shared_ptr of a comparison object. // You may want to use the templated comparison classes // ObjCompare(), // but you are free to use any other class derived from BaseCompare // that implements the comp function. //
            //
          • The increment from one data element to the next. --- When // structs are sorted on an element in the struct, the increment // should be the size of the struct. If the comparison object is // automatically created using the data type specified, the default // increment is the size of the data type. //
          • The sort order. --- Ascending (default) or // Descending; //
          // // When the data array has been passed to the Sort constructor, // the data pointer and the increment arguments can be replaced by a // single argument: the offset of the key in each element of the array. // // void sortKey (const void* data, DataType, uInt increment = 0, Order = Ascending); void sortKey (const void* data, const std::shared_ptr&, uInt increment, Order = Ascending); void sortKey (uInt offset, DataType, Order = Ascending); void sortKey (uInt offset, const std::shared_ptr&, Order = Ascending); // // Sort the data array of nrrec records. // The result is an array of indices giving the requested order. // It returns the number of resulting records. The indices array // is resized to that number. //
          By default it'll try if the faster GenSortIndirect can be used // if a sort on a single key is used. uInt sort (Vector& indexVector, uInt nrrec, int options = DefaultSort, Bool tryGenSort = True) const; uInt64 sort (Vector& indexVector, uInt64 nrrec, int options = DefaultSort, Bool tryGenSort = True) const; // Get all unique records in a sorted array. The array order is // given in the indexVector (as possibly returned by the sort function). // The default indexVector is 0..nrrec-1. // The index of each first unique record is returned in the uniqueVector. // They are indices in the supplied indexVector, so // data[indexVector(uniqueVector(i))] // is giving the i-th unique record. // Note that the records indexed by indexVector(uniqueVector(i)) // till indexVector(uniqueVector(i+1)) are all the same. //
          // It returns the number of unique records. The unique array // is resized to that number. // The third version also gives back a vector with the keys that // change in each sorting group. The size of changeKey is the same as // uniqueVector, and for each unique sorting group indicates the index // of the keyword that will change at the end of the group. // uInt unique (Vector& uniqueVector, uInt nrrec) const; uInt unique (Vector& uniqueVector, const Vector& indexVector) const; uInt unique (Vector& uniqueVector, Vector& changeKey, const Vector& indexVector) const; uInt64 unique (Vector& uniqueVector, uInt64 nrrec) const; uInt64 unique (Vector& uniqueVector, const Vector& indexVector) const; uInt64 unique (Vector& uniqueVector, Vector& changeKey, const Vector& indexVector) const; // private: template T doSort (Vector& indexVector, T nrrec, int options = DefaultSort, Bool tryGenSort = True) const; template T doUnique (Vector& uniqueVector, T nrrec) const; template T doUnique (Vector& uniqueVector, const Vector& indexVector) const; template T doUnique (Vector& uniqueVector, Vector& changeKey, const Vector& indexVector) const; // Copy that Sort object to this. void copy (const Sort& that); // Add a sort key giving a data type and stride or the sort key. // void addKey (const void* data, DataType, uInt increment, int options); void addKey (SortKey*); // // Do an insertion sort, optionally skipping duplicates. // template T insSort (T nr, T* indices) const; template T insSortNoDup (T nr, T* indices) const; // // Do a merge sort, if possible in parallel using OpenMP. // Note that the env.var. OMP_NUM_TRHEADS sets the maximum nr of threads // to use. It defaults to the number of cores. template T parSort (int nthr, T nrrec, T* inx) const; template void merge (T* inx, T* tmp, T size, T* index, T nparts) const; // Do a quicksort, optionally skipping duplicates // (qkSort is the actual quicksort function). // template T quickSort (T nr, T* indices) const; template T quickSortNoDup (T nr, T* indices) const; template void qkSort (T nr, T* indices) const; // // Do a heapsort, optionally skipping duplicates. // template T heapSort (T nr, T* indices) const; template T heapSortNoDup (T nr, T* indices) const; // // Siftdown algorithm for heapsort. template void siftDown (T low, T up, T* indices) const; // Compare 2 records based on the comparison functions template int compare (T index1, T index2) const; // As compare() but it also gives back the index of the first comparison // function that didn't match. template int compareChangeIdx(T i1, T i2, size_t& idxComp) const; // Swap 2 indices. template inline void swap (T index1, T index2, T* indices) const { T t = indices[index1]; indices[index1] = indices[index2]; indices[index2] = t; } //# Data memebers PtrBlock keys_p; //# keys to sort on size_t nrkey_p; //# #sort-keys const void* data_p; //# pointer to data records uInt size_p; //# size of data record int order_p; //# -1=asc 0=mixed 1=desc }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Utilities/Sort.tcc000066400000000000000000000331101476623553700176260ustar00rootroot00000000000000//# Sort.tcc: Sort objects on one or more keys //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_SORT_TCC #define CASA_SORT_TCC //# Includes #include #include #include #ifdef _OPENMP #include #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN template T Sort::doSort (Vector& indexVector, T nrrec, int opt, Bool doTryGenSort) const { if (nrrec == 0) { return nrrec; } //# Try if we can use the faster GenSort when we have one key only. if (doTryGenSort && nrkey_p == 1) { uInt n = keys_p[0]->tryGenSort (indexVector, nrrec, opt); if (n > 0) { return n; } } indexVector.resize (nrrec); indgen (indexVector); // Pass the sort function a C-array of indices, because indexing // in there is (much) faster than in a vector. Bool del; T* inx = indexVector.getStorage (del); // Choose the sort required. int nodup = opt & NoDuplicates; int type = opt - nodup; // Determine default sort to use. int nthr = 1; #ifdef _OPENMP nthr = omp_get_max_threads(); // Do not use more threads than there are values. if (uInt(nthr) > nrrec) nthr = nrrec; #endif if (type == DefaultSort) { type = (nrrec<1000 || nthr==1 ? QuickSort : ParSort); } T n = 0; switch (type) { case QuickSort: if (nodup) { n = quickSortNoDup (nrrec, inx); }else{ n = quickSort (nrrec, inx); } break; case HeapSort: if (nodup) { n = heapSortNoDup (nrrec, inx); }else{ n = heapSort (nrrec,inx); } break; case InsSort: if (nodup) { n = insSortNoDup (nrrec, inx); }else{ n = insSort (nrrec, inx); } break; case ParSort: n = parSort (nthr, nrrec, inx); if (nodup) { n = insSortNoDup (nrrec, inx); } break; default: throw SortInvOpt(); } indexVector.putStorage (inx, del); // If n < nrrec, some duplicates have been deleted. // This means we have to resize the Vector. if (n < nrrec) { indexVector.resize (n, True); } return n; } template T Sort::doUnique (Vector& uniqueVector, T nrrec) const { Vector indexVector(nrrec); indgen (indexVector); return doUnique (uniqueVector, indexVector); } template T Sort::doUnique (Vector& uniqueVector, const Vector& indexVector) const { Vector changeKey; return doUnique (uniqueVector, changeKey, indexVector); } template T Sort::doUnique (Vector& uniqueVector, Vector& changeKey, const Vector& indexVector) const { T nrrec = indexVector.nelements(); uniqueVector.resize (nrrec); changeKey.resize (nrrec); if (nrrec == 0) { return 0; } // Pass the sort function a C-array of indices, because indexing // in there is (much) faster than in a vector. Bool delInx, delUniq, delChange; const T* inx = indexVector.getStorage (delInx); T* uniq = uniqueVector.getStorage (delUniq); size_t* change = changeKey.getStorage (delChange); uniq[0] = 0; T nruniq = 1; size_t idxComp; for (T i=1; i T Sort::parSort (int nthr, T nrrec, T* inx) const { Block index(nrrec+1); Block tinx(nthr+1); Block np(nthr); // Determine ordered parts in the array. // It is done in parallel, whereafter the parts are combined. int step = nrrec/nthr; for (int i=0; i T Sort::quickSortNoDup (T nrrec, T* inx) const { qkSort (nrrec, inx); return insSortNoDup (nrrec, inx); } template void Sort::qkSort (T nr, T* inx) const { // If the nr of elements to be sorted is less than N, it is // better not to use quicksort anymore (according to Sedgewick). // Take N=15, because that seems to work best after testing // N=5, 10, 15 and 20. if (nr <= 15) { return; } // According to Sedgewick it is best to use a random partition element // to avoid degenerated cases (if the data is already in order for example) // rand is not a particularly good random number generator, but good // enough for this purpose. // Put this element at the beginning of the array. T p = rand() % nr; swap (T(0), p, inx); // Now shift all elements < partition-element to the left. // If an element is equal, shift every other element to avoid // degeneration. This trick is described by Jon Bentley in // UNIX Review, October 1992. // We do not have equal elements anymore (because of the stability // property introduced on 13-Feb-1995). T j = 0; for (T i=1; i T Sort::heapSort (T nrrec, T* inx) const { // Use the heapsort algorithm described by Jon Bentley in // UNIX Review, August 1992. T j; inx--; for (j=nrrec/2; j>=1; j--) { siftDown (j, nrrec, inx); } for (j=nrrec; j>=2; j--) { swap (T(1), j, inx); siftDown (T(1), j-1, inx); } return nrrec; } template T Sort::heapSortNoDup (T nrrec, T* inx) const { heapSort (nrrec, inx); return insSortNoDup (nrrec, inx); } template void Sort::siftDown (T low, T up, T* inx) const { T sav = inx[low]; T c; T i; for (i=low; (c=2*i)<=up; i=c) { if (c < up && compare(inx[c+1], inx[c]) <= 0) { c++; } inx[i] = inx[c]; } inx[i] = sav; for ( ; (c=i/2)>=low; i=c) { if (compare (inx[i], inx[c]) > 0) { break; } swap (c, i, inx); } } // Note that the block of SortKeys is defined as void*, to achieve // that only 1 type of Block is needed. // Casting is perfectly save. // The comparison functions return: // -1 when obj1 < obj2 // 0 when obj1 = obj2 // 1 when obj1 > obj2 // compare returns: // 2 when data[i1],data[i2] is in correct order // (thus data[i1] < data[i2] for ascending sort) // 1 when data is equal and indices are in order // 0 when data is out of order // -1 when data is equal and indices are out of order template int Sort::compare (T i1, T i2) const { size_t idxComp; return compareChangeIdx(i1, i2, idxComp); } // This is a similar function to compare() but it also gives back which is the // first comparison function that doesn't match. // idxComp gives the comparison function index. In case the function returns // 1 or -1 idxComp is not modified. template int Sort::compareChangeIdx(T i1, T i2, size_t& idxComp) const { int seq; SortKey* skp; for (size_t i=0; icmpObj_p->comp ((char*)skp->data_p + i1*skp->incr_p, (char*)skp->data_p + i2*skp->incr_p); if (seq == skp->order_p) { idxComp = i; return 2; // in order } if (seq != 0) { idxComp = i; return 0; // out-of-order } } // Equal keys, so return i1 namespace casacore { //# NAMESPACE CASACORE - BEGIN // Implementation of Sort error classes. SortError::SortError (Category c) : AipsError("Sort error",c) { ; } SortError::SortError (const String& str,Category c) : AipsError(str,c) { ; } SortError::~SortError () noexcept { ; } SortInvDT::SortInvDT (Category c) : SortError ("Invalid sort data type",c) { ; } SortInvDT::~SortInvDT () noexcept { ; } SortInvIncr::SortInvIncr (Category c) : SortError ("Sort increment < key Incr",c) { ; } SortInvIncr::~SortInvIncr () noexcept { ; } SortNoData::SortNoData (Category c) : SortError ("No data array given to constructor",c) { ; } SortNoData::~SortNoData () noexcept { ; } SortInvOpt::SortInvOpt (Category c) : SortError ("Invalid sort option given",c) { ; } SortInvOpt::~SortInvOpt () noexcept { ; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Utilities/SortError.h000066400000000000000000000075101476623553700203230ustar00rootroot00000000000000//# SortError.h: Error classes for the sort class //# Copyright (C) 1993,1994,1995,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_SORTERROR_H #define CASA_SORTERROR_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Generic Sort exception // // // //
        260. Sort // // // SortError is the generic Sort exception; catching this one means catching // all Sort exceptions. Note that you have to catch AipsError to catch // all possible exceptions. // class SortError : public AipsError { public: SortError (Category c=GENERAL); SortError (const String&,Category c=GENERAL); ~SortError () noexcept; }; // Invalid data type used for this sort key // // // //
        261. Sort // // // Invalid data type used for this sort key // class SortInvDT : public SortError { public: SortInvDT (Category c=INVALID_ARGUMENT); ~SortInvDT () noexcept; }; // Invalid increment used for this sort key // // // //
        262. Sort // // // Invalid increment used for this sort key. // The increment should be >= size of sort key. // class SortInvIncr : public SortError { public: SortInvIncr (Category c=INVALID_ARGUMENT); ~SortInvIncr () noexcept; }; // No data array given to Sort constructor. // // // //
        263. Sort // // // No data array has been given to Sort constructor. // class SortNoData : public SortError { public: SortNoData (Category c=INITIALIZATION); ~SortNoData () noexcept; }; // Invalid sort option given to routine dosort. // // // //
        264. Sort // // // Invalid sort option has been given to routine dosort. // class SortInvOpt : public SortError { public: SortInvOpt (Category c=INVALID_ARGUMENT); ~SortInvOpt () noexcept; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Utilities/StringDistance.cc000066400000000000000000000134331476623553700214420ustar00rootroot00000000000000//# StringDistance.cc: Class to deal with Levensthein distance of strings //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include using namespace std; namespace casacore { StringDistance::StringDistance() : itsMaxDistance (0), itsCountSwaps (False), itsIgnoreBlanks (False), itsCaseInsensitive (False) {} StringDistance::StringDistance (const String& source, int maxDistance, Bool countSwaps, Bool ignoreBlanks, Bool caseInsensitive) : itsSource (source), itsMaxDistance (maxDistance), itsCountSwaps (countSwaps), itsIgnoreBlanks (ignoreBlanks), itsCaseInsensitive (caseInsensitive) { if (ignoreBlanks) { itsSource = removeBlanks (itsSource); } if (caseInsensitive) { itsSource.downcase(); } if (itsMaxDistance < 0) { itsMaxDistance = 1 + itsSource.size() / 3; } // Size the matrix such that it suffices for all possible strings. itsMatrix.resize (itsSource.size() + 1, itsSource.size() + itsMaxDistance + 1); itsMatrix = -1; } Bool StringDistance::match (const String& target) const { String t(target); if (itsIgnoreBlanks) { t = removeBlanks (target); } int diff = t.size(); diff -= itsSource.size(); if (std::abs(diff) > itsMaxDistance) { return false; } if (itsCaseInsensitive) { t.downcase(); } if (itsMaxDistance == 0) { return t == itsSource; } return doDistance(itsSource, t, itsCountSwaps, itsMatrix) <= itsMaxDistance; } Int StringDistance::distance (const String& target) const { String t(target); if (itsIgnoreBlanks) { t = removeBlanks (target); } if (t.size() > itsSource.size() + itsMaxDistance) { return t.size() - itsSource.size(); } if (itsCaseInsensitive) { t.downcase(); } return doDistance (itsSource, t, itsCountSwaps, itsMatrix); } Int StringDistance::distance (const String& source, const String& target, Bool countSwaps) { Matrix matrix (source.size() + 1, target.size() + 1); return doDistance (source, target, countSwaps, matrix); } Int StringDistance::doDistance (const String& source, const String& target, Bool countSwaps, Matrix& matrix) { int n = source.size(); int m = target.size(); if (n == 0) { return m; } if (m == 0) { return n; } // Initialize the first row and column. for (int i=0; i<=n; ++i) { matrix(i,0) = i; } for (int j=0; j<=m; ++j) { matrix(0,j) = j; } // Loop over all characters in source and target. for (int j=0; j0 && j>0) { int trans = matrix(i-1,j-1) + 1; if (source[i-1] != t_j) trans++; if (s_i != target[j-1]) trans++; if (cell > trans) cell=trans; } matrix(i+1,j+1) = cell; } } // Now the last element contains the distance. return matrix(n,m); } String StringDistance::removeBlanks (const String& source) { String dest; int n = source.size(); dest.reserve (n); for (int i=0; i #include #include namespace casacore { // // Class to deal with Levensthein distance of strings. // // // The Levenshtein Distance is a metric telling how similar strings are. // It is also known as the Edit Distance. // // The distance tells how many operations (i.e., character substitutions, // insertions, and deletions are needed to transform one string into another. //
          There are several extensions to the basic definition: //
            //
          • Treat a swap of adjacent characters as one operation. //
          • Give a weight to operations (e.g., insertions and deletions have // half the weight of the other operations). //
          • Take locality into account. For example, successive substitutions // weigh more than non-successive. //
          • Extend to wildcarded strings. //
          // This class optionally uses the swap extension. Furthermore one can // optionally ignore blanks. By default both options are used. // // The code is based on code written by Anders Sewerin Johansen. // Calculating the distance is an expensive O(N^2) operation, thus // should be used with care. // // The class is constructed with the source string to compare against. // Thereafter its match or distance // function can be used for each target string. //
          class StringDistance { public: // Default constructor sets maxDistance to 0. StringDistance(); // Construct from the source string and maximum distance. // If the maximum distance is negative, it defaults to 1+strlength/3. // Note that maximum distance 0 means that the strings must match exactly. explicit StringDistance (const String& source, Int maxDistance=-1, Bool countSwaps=True, Bool ignoreBlanks=True, Bool caseInsensitive=False); // Get data members. // const string& source() const { return itsSource; } Int maxDistance() const { return itsMaxDistance; } const Matrix& matrix() const { return itsMatrix; } // // Test if the given target string is within the maximum distance. Bool match (const String& target) const; // Calculate the distance from the string to the string given in the constructor. // If the length of target exceeds source length + maxDistance, // the difference in lengths is returned. Int distance (const String& target) const; // Calculate the distance between the two strings. // This is slower than the distance member function, because // it has to allocate the underlying Matrix for each invocation. static Int distance (const String& source, const String& target, Bool countSwaps=True); // Remove blanks from the given string. static String removeBlanks (const String& source); private: // Calculate the distance. static Int doDistance (const String& source, const String& target, Bool countSwaps, Matrix& matrix); private: String itsSource; mutable Matrix itsMatrix; Int itsMaxDistance; Bool itsCountSwaps; Bool itsIgnoreBlanks; Bool itsCaseInsensitive; }; } //# end namespace #endif casacore-3.7.1/casa/Utilities/Template.h000066400000000000000000000216001476623553700201310ustar00rootroot00000000000000//# Template.h: Canonicalise, format etc. Casacore template definitions //# Copyright (C) 2001,2002,2004,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_TEMPLATE_H #define CASA_TEMPLATE_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations class Regex; template class Vector; // // Canonicalise, format and other actions on Casacore template definitions // // // // // //
        265. Knowledge about the Casacore DYO template system // // // A set of methods on template repository files and on template definitions to be // used in the reident, used, unused and duplicates programs (see // Sytem manual // for details.
          // Methods exist to read templates, to canonicalise them for comparison and // search functions and to format them for output. // // // // To make template formatting identical across formatting/testing programs. // // // //
        266. nothing I know // class Template { public: //# Constructors // Default constructor. Need to read data into it Template(); // Create from the file names given explicit Template(const Vector &files); // Create from the file name given explicit Template(const String &filename); // Destructor ~Template(); // Operators const String &operator[](uInt n) { return output_p[n]; } //# Member functions // Clear the object for a re-use. void reset(); // Read the templates file or files into the class. Multiple reading is additive. // Errors are reported to cerr, and commented out in the file. // void read(const Vector &files); void read(const String &filename); // // Get the number of template entries uInt getCount() const { return count_p; }; // Get the number of template definition lines found uInt getTDCount() const { return tdcount_p; }; // Get the number of templates found after all processing uInt getTCount() const { return tcount_p; }; // Get the number of duplicates found uInt getDCount() { return dcount_p; }; // Get the various template definition information fields. // Meant for testing and special projects only. // const String &getTDFlist(uInt n) { return tdflist_p[n]; }; const String &getTDlist(uInt n) { return tdlist_p[n]; }; const uInt &getTDfile(uInt n) { return tdfile_p[n]; }; const uInt &getTDline(uInt n) { return tdline_p[n]; }; const String &getTDname(uInt n) { return tdname_p[n]; }; // // Canonicalise the template entries in the object. If switch True, do only // the templates entry for duplication void canonical(const Bool tmplonly=False); // Split the entries in number, name id, rest void splitName(); // Sort the data on name and number and fill in missing number. If switch // is True, renumber all template entries in sequence. void sortName(const Bool renumber=False); // Write the data formatted to the specified file. Notify errors and warnings // by writing to cerr. If warn is False, some warnings will be // compressed into a general warning. void writeOut(ostream &os, const Bool warn=False); // Write the duplicate list; the userFile gets ***; isSys gives the system switch void writeDup(ostream &os, const String &userFile, Bool isSys=False); private: //# Data // Each element is a template entry on a single line Block output_p; // Count the lines uInt count_p; // Count the templates uInt tcount_p; // Record comment lines Block comout_p; // And where they originated Block comptr_p; // And count the comment lines uInt ccount_p; // Indicate data split Bool isSplit_p; // Count the duplicates uInt dcount_p; // Data split of number string (or empty/spaces) Block nstring_p; // Data split all text Block allstring_p; // Data split name string (first include file) Block namstring_p; // Data split numeric number Block nval_p; // List of files used Block tdflist_p; // Number of template definitions extracted from input uInt tdcount_p; // List of template definitions Block tdlist_p; // Pointers to in which file in list Block tdfile_p; // Line number in file at which template found Block tdline_p; // List of comparison names Block tdname_p; //# Constructors // Copy constructor (not implemented) Template(const Template &other); //# Operators // Assignment (not implemented) Template &operator=(const Template &other); //# Member functions // Save comment void setComment(const String &txt, const Bool atstart=False); // Save a line void setOutput(const String &txt); //# Static conversion data // Patterns to analyse an input line static const Regex spaces; static const Regex comment; static const Regex ifRE; static const Regex endifRE; static const Regex elseRE; static const Regex templateRE; static const Regex contRE; static const Regex fileRE; static const Regex typedefRE; static const Regex auxtemplRE; static const Regex namespaceRE; // Simple pattern and replacements to make canonical templates files static const uInt Ncanon = 52; static const Regex PATcanon[Ncanon]; static const String REPcanon[Ncanon]; // For canonical change: replacement of pattern with pattern static const uInt Ncanon2 = 15; static const Regex PATcanon20[Ncanon2]; static const Regex PATcanon21[Ncanon2]; static const String REPcanon2[Ncanon2]; // Make canonical numbers of 4 digits minimum static const uInt Nnmin = 4; static const Regex PATnmin[Nnmin]; static const String REPnmin[Nnmin]; // Make canonical numbers of 4 digits maximum static const uInt Nnmax = 1; static const Regex PATnmax[Nnmax]; static const Regex REPnmax[Nnmax]; // Patterns to split off number and name // Patterns to split off number and name static const Regex splitnum; static const Regex splitnam; // Patterns to check the template line static const Regex sifRE; static const Regex stemRE; static const Regex sconstRE; static const Regex sretRE1; static const Regex sretRE2; static const Regex sretRE3; static const Regex sretRE4; static const Regex stypedefRE; static const Regex sauxtemplRE; static const Regex snamespaceRE; // Replacement patterns for ifs in saved line static const uInt Ninif = 5; static const String PATinif[Ninif]; static const String REPinif[Ninif]; // Tests for finding real templates for duplicate tests static const Regex classprelude; static const Regex functionprelude; static const Regex forwardprelude; static const Regex funcnameprelude; static const Regex mylistprelude; // Data to remove spaces at begin, end, make single, count/remove const static const Regex leadsp; static const Regex endsp; static const Regex mulsp; static const Regex constsp; static const String nullsp; static const String singlesp; // Patterns to make all typedefs comparisons for duplicates possible // Note that the first three should be in that position for run-time // change on some systems. static const uInt Ntypedef = 23; static const Regex PATtypedef0[Ntypedef]; static const Regex PATtypedef1[Ntypedef]; static String REPtypedef[Ntypedef]; // Name of repository files static const String reposName; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/casa/Utilities/Template.tcc000066400000000000000000000624751476623553700204720ustar00rootroot00000000000000//# Template.cc: Canonicalise, format etc. Casacore template definitions //# Copyright (C) 2001-2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_TEMPLATE_TCC #define CASA_TEMPLATE_TCC //# Includes #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Static constants // Patterns to analyse an input line const Regex Template::spaces = String("^[[:space:]]*$"); const Regex Template::comment = String("^[[:space:]]*#"); const Regex Template::ifRE = String("^[[:space:]]*#if"); const Regex Template::endifRE = String("^[[:space:]]*#endif[[:space:]]*$"); const Regex Template::elseRE = String("^[[:space:]]*#else[[:space:]]*$"); const Regex Template::templateRE = String("^[[:space:]]*template[[:space:]<]"); const Regex Template::contRE = String("^[[:space:]]*="); const Regex Template::fileRE = String("^[[:space:]]*[[:digit:]]*" "[[:space:]]*" "[^.[:space:]]*[.](cc|h)"); const Regex Template::typedefRE = String("^[[:space:]]*typedef[[:space:]]"); const Regex Template::auxtemplRE = String("^[[:space:]]*" "AIPS_[A-Z0-9]*_AUX_TEMPLATES" "[[:space:](]"); const Regex Template::namespaceRE= String("^[[:space:]]*#namespace"); // Simple pattern and replacements to make canonical templates files const Regex Template::PATcanon[Ncanon] = { String("[[:space:]]"), // 00 String(" ="), String(" *"), String("^ "), String(" $"), String(" [(]"), String(" ,"), String(","), String("&"), String("[*][*]"), String(" [*] [*]"), // 10 String("[(] [*][)]"), String("[(] *"), String("[*]const"), String(" operator "), String(" operator[ ]*&[ ]*&[(]"), String(" <"), String("< "), String(" >"), String(">>"), String(">>"), // 20 String("operator> >[(]"), String(" template *< *>"), String(" template *<"), String("> *class "), String("short unsigned int"), String("unsigned short int"), String("short signed int"), String("signed short int"), String("long unsigned int"), String("unsigned long int"), // 30 String("long signed int"), String("signed long int"), String("unsigned char"), String("signed char"), String("unsigned short"), String("short unsigned"), String("signed short"), String("short signed"), String("short int"), String("unsigned long"), // 40 String("long unsigned"), String("signed long"), String("long signed"), String("long int"), String("long float"), String("long double"), String("unsigned int"), String("signed int"), String(" *;+$"), String(" *"), // 50 String(">const") }; const String Template::REPcanon[Ncanon] = { " ", // 00 " = ", " ", "", "", "(", ",", ", ", " &", "* *", " **", // 10 "(*)", "(", "* const", " operator", " operator&&(", "<", "<", ">", "> >", "> >", // 20 "operator>>(", " template <> ", " template <", "> class ", "uShort", "uShort", "Short", "Short", "uLong", "uLong", // 30 "Long", "Long", "uChar", "Char", "uShort", "uShort", "Short", "Short", "Short", "uLong", // 40 "uLong", "Long", "Long", "Long", "Double", "lDouble", "uInt", "Int", "", " ", // 50 "> const" }; const Regex Template::PATcanon20[Ncanon2] = { String("[^[:alnum:]_]char[^[:alnum:]_]"), // 00 String("[^[:alnum:]_]short[^[:alnum:]_]"), String("[^[:alnum:]_]unsigned[^[:alnum:]_]"), String("[^[:alnum:]_]signed[^[:alnum:]_]"), String("[^[:alnum:]_]int[^[:alnum:]_]"), String("[^[:alnum:]_]long[^[:alnum:]_]"), String("[^[:alnum:]_]float[^[:alnum:]_]"), String("[^[:alnum:]_]double[^[:alnum:]_]"), String("[^[:alnum:]_]complex"), String("[^[:alnum:]_]complex"), String(""), // 10 String(""), String("[^[:alnum:]_]bool[^[:alnum:]_]"), String("std::Complex"), String("std::DComplex") }; const Regex Template::PATcanon21[Ncanon2] = { String("char"), // 00 String("short"), String("unsigned"), String("signed"), String("int"), String("long"), String("float"), String("double"), String("complex"), String("complex"), String(""), // 10 String(""), String("bool"), String("std::Complex"), String("std::DComplex") }; const String Template::REPcanon2[Ncanon2] = { "Char", // 00 "Short", "uInt", "Int", "Int", "Long", "Float", "Double", "Complex", "DComplex", "", // 10 "", "Bool", "std::complex", "std::complex" }; // Make canonical numbers of 4 digits minimum const Regex Template::PATnmin[Nnmin] = { String("^[^[:digit:]]"), // 00 String("^[[:digit:]] "), String("^[[:digit:]][[:digit:]] "), String("^[[:digit:]][[:digit:]][[:digit:]] ") }; const String Template::REPnmin[Nnmin] = { "0000 ", // 00 "000", "00", "0" }; // Make canonical numbers of 4 digits maximum const Regex Template::PATnmax[Nnmax] = { String("^[[:digit:]][[:digit:]][[:digit:]][[:digit:]][[:digit:]]+ ") }; const Regex Template::REPnmax[Nnmax] = { String("[[:digit:]][[:digit:]][[:digit:]][[:digit:]] ") }; // Patterns to split off number and name const Regex Template::splitnum = String("^[[:digit:]]+ "); const Regex Template::splitnam = String("^[^ ]+ "); // Patterns to check the saved template line const Regex Template::sifRE("^#if"); const Regex Template::stemRE("^template"); const Regex Template::sconstRE("([(] *const|, *const|< *const)"); const Regex Template::sretRE1("^template class "); const Regex Template::sretRE2("^template [^ ]*[(]"); const Regex Template::sretRE3("^template <"); const Regex Template::sretRE4("[^t][^o][^r>]>[(]"); const Regex Template::stypedefRE("^typedef"); const Regex Template::sauxtemplRE("^AIPS_[A-Z0-9]*_AUX_TEMPLATES"); const Regex Template::snamespaceRE("^#namespace"); // Replacement patterns for ifs in saved line const String Template::PATinif[Ninif] = { String("& &"), // 00 String("= ="), String("<"), String("< ="), String(">") }; const String Template::REPinif[Ninif] = { "&&", // 00 "==", " < ", "<=", " >" }; // Tests for finding real templates for duplicate tests const Regex Template::classprelude("^.*template[[:space:]]*class[[:space:]]*"); const Regex Template::functionprelude("^.*template[[:space:]]*"); const Regex Template::forwardprelude("^.*template[[:space:]]*<"); const Regex Template::funcnameprelude("[^[:space:]]*[:(]"); const Regex Template::mylistprelude("^[[:space:]]*[[:digit:]]+: "); // Data to remove spaces at begin, end, make single, count/remove const const Regex Template::leadsp("^[[:space:]]+"); const Regex Template::endsp("[[:space:]]+$"); const Regex Template::mulsp("[[:space:]]+"); const Regex Template::constsp("const"); const String Template::nullsp; const String Template::singlesp(" "); // Patterns to make all typedefs comparisons for duplicates possible // Note that the first three should be in that position for run-time // change on some systems. const Regex Template::PATtypedef0[Ntypedef] = { String("[^[:alnum:]_]FitsLong[^[:alnum:]_]"), // 00 String("[^[:alnum:]_]lDouble[^[:alnum:]_]"), String("[^[[:alnum:]_]Long[^[:alnum:]_]"), String("[[:alnum:]_]DataStatus[[:alnum:]_]"), String("[[:alnum:]_]LogicalRecord[[:alnum:]_]"), String("[[:alnum:]_]TapeHeader[[:alnum:]_]"), String("[[:alnum:]_]Convolver[[:alnum:]_]"), String("[[:alnum:]_]Convolver][[:alnum:]_]"), String("[[:alnum:]_]AipsrcValue[[:alnum:]_]"), String("[[:alnum:]_]AipsrcValue[[:alnum:]_]"), String("[[:alnum:]_]AipsrcValue[[:alnum:]_]"), // 10 String("[[:alnum:]_]AipsrcVector[[:alnum:]_]"), String("[[:alnum:]_]AipsrcVector[[:alnum:]_]"), String("[[:alnum:]_]AipsrcVector[[:alnum:]_]"), String("[[:alnum:]_]AipsrcVector[[:alnum:]_]"), String("[[:alnum:]_]LogicalArrayElem[[:alnum:]_]"), String("[[:alnum:]_]Array[[:alnum:]_]"), String("[[:alnum:]_]MaskedArray[[:alnum:]_]"), String("[[:alnum:]_]Cube[[:alnum:]_]"), String("[[:alnum:]_]Matrix[[:alnum:]_]"), String("[[:alnum:]_]Vector[[:alnum:]_]"), // 20 String("[[:alnum:]_]MeasurementSet[[:alnum:]_]"), String("[[:alnum:]_]Quantum[[:alnum:]_]") }; const Regex Template::PATtypedef1[Ntypedef] = { String("FitsLong"), // 00 String("lDouble"), String("Long"), String("DataStatus"), String("LogicalRecord"), String("TapeHeader"), String("Convolver"), String("Convolver"), String("AipsrcValue"), String("AipsrcValue"), String("AipsrcValue"), // 10 String("AipsrcVector"), String("AipsrcVector"), String("AipsrcVector"), String("AipsrcVector"), String("LogicalArrayElem"), String("Array"), String("MaskedArray"), String("Cube"), String("Matrix"), String("Vector"), // 20 String("MeasurementSet"), String("Quantum") }; String Template::REPtypedef[Ntypedef] = { "Long", // 00 "lDouble", "Long", "DataStatusStructure", "LogicalRecordStructure", "TapeHeader", "DoubleConvolver", "FloatConvolver", "AipsrcBool", "AipsrcDouble", "AipsrcInt", // 10 "AipsrcVBool", "AipsrcVDouble", "AipsrcVInt", "AipsrcVString", "Bool", "LogicalArray", "MaskedArrayLogical", "LogicalCube", "LogicalMatrix", "LogicalVector", //20 "MS", "Quantity" }; // Name of repository files const String Template::reposName = "/_ReposFiller/templates"; //# Constructors Template::Template() : output_p(100), count_p(0), tcount_p(0), comout_p(100), comptr_p(100), ccount_p(0), isSplit_p(False), dcount_p(0), nstring_p(0), allstring_p(0), namstring_p(0), nval_p(0), tdflist_p(0), tdcount_p(0), tdlist_p(100), tdfile_p(100), tdline_p(100), tdname_p(0) { reset(); } Template::Template(const Vector &files) : output_p(100), count_p(0), tcount_p(0), comout_p(100), comptr_p(100), ccount_p(0), isSplit_p(False), dcount_p(0), nstring_p(0), allstring_p(0), namstring_p(0), nval_p(0), tdflist_p(0), tdcount_p(0), tdlist_p(100), tdfile_p(100), tdline_p(100), tdname_p(0) { reset(); read(files); } Template::Template(const String &filename) : output_p(100), count_p(0), tcount_p(0), comout_p(100), comptr_p(100), ccount_p(0), isSplit_p(False), dcount_p(0), nstring_p(0), allstring_p(0), namstring_p(0), nval_p(0), tdflist_p(0), tdcount_p(0), tdlist_p(100), tdfile_p(100), tdline_p(100), tdname_p(0) { reset(); read(filename); } //# Destructor Template::~Template() {} //# Member functions void Template::reset() { count_p = 0; tcount_p = 0; ccount_p = 0; isSplit_p = False; dcount_p = 0; tdcount_p = 0; tdflist_p.resize(0); // Make sure all (known) variable typedefs are catered for if (typeid(FitsLong) == typeid(Int)) REPtypedef[0] = "Int"; if (typeid(lDouble) == typeid(Double)) REPtypedef[1] = "Double"; if (typeid(Long) == typeid(Int)) REPtypedef[2] = "Int"; } void Template::read(const Vector &files) { for (uInt i=0; i < files.nelements(); i++) { // for each file... read(files(i)); } } void Template::read(const String &filename) { // Open and read file ifstream file(filename.chars(), ios::in); if (!file) { cerr << "Cannot open input file " << filename << endl; return; } // Save filename in list tdflist_p.resize(tdflist_p.nelements()+1); tdflist_p[tdflist_p.nelements()-1] = filename; String extracted; // a single input line String combine; // a full combined line uInt c1 = 0; // the input line count Bool ok(True); while (ok && (((extracted = ""), (ok = getline(file, extracted))) || !combine.empty())) { c1++; // Count input lines Bool err = False; // Skip empty lines if ((extracted.empty() || extracted.contains(spaces)) && ok) continue; // Check if correct first line if (combine.empty() && ok) { // Comment allowed if (extracted.contains(comment)) setComment(extracted, combine.empty()); // Start of entry allowed else if (extracted.contains(fileRE)) combine = extracted; else err = True; if (!err) continue; } // Handle regular extension lines if ((extracted.contains(ifRE) || extracted.contains(endifRE) || extracted.contains(elseRE) || extracted.contains(templateRE) || extracted.contains(contRE) || extracted.contains(typedefRE) || extracted.contains(auxtemplRE) || extracted.contains(namespaceRE)) && ok && !err) { // Replace a continuation include line with /=/ pattern if (extracted.contains(contRE)) extracted.gsub(contRE, String("/=/")); combine += String(" ") + extracted; // make one line // Find proper templates for list if (extracted.contains(templateRE)) { if (extracted.contains(forwardprelude)) continue; // skip forward declarations if (extracted.contains(mylistprelude)) { // special nnnn: list format extracted = extracted.after(mylistprelude); } else if (extracted.contains(classprelude)) { extracted = extracted.after(classprelude); // template class } else if (extracted.contains(functionprelude)) { extracted = extracted.after(functionprelude); // template global if (extracted.contains(funcnameprelude)) { extracted = extracted.from(funcnameprelude); } } else continue; // unknown entry if (!extracted.empty()) { // save the entry if (tdcount_p >= tdlist_p.nelements()) { tdlist_p.resize(tdcount_p+100); tdfile_p.resize(tdcount_p+100); tdline_p.resize(tdcount_p+100); } tdlist_p[tdcount_p] = extracted; tdfile_p[tdcount_p] = tdflist_p.nelements()-1; tdline_p[tdcount_p++] = c1; } } continue; } // Handle comment lines if (ok && !err && extracted.contains(comment)) { setComment(extracted, combine.empty()); continue; } // Handle an initial line if ((ok && !err && extracted.contains(fileRE)) || !ok) { if (!combine.empty()) setOutput(combine); combine = extracted; } else err = True; if (err) { cerr << "Warning: illegal entry commented out near line " << c1 << " in " << filename << ":\n\t" << extracted(0, ((extracted.length() <= 60) ? extracted.length() : 60)) << " ..." << endl; for (uInt j=0; j inx; Sort sort; sort.sortKey(allstring_p.storage(), TpString); sort.sortKey(nstring_p.storage(), TpString); // Sort and fill missing numbers sort.sort(inx, count_p); // Make numbers if (renumber) { String prev; uInt ident(0); for (uInt j=0; j mid) ? nval_p[inx(j)] : mid; j++; } mid = (mid/10)*10 + 10; } if (prev == namstring_p[inx(k)]) { if (nval_p[inx(k)] < 1000) { ostringstream text; text << mid; nstring_p[inx(k)] = String(text) + " "; nval_p[inx(k)] = mid; mid += 10; } else { for (Int j=k-1; j>=pid; j--) { if (nval_p[inx(k)] == nval_p[inx(j)]) { ostringstream text; text << mid; namstring_p[inx(k)] = String(text) + " "; nval_p[inx(k)] = mid; mid += 10; break; } } } } else { prev = ""; k--; } } } // Make new full line for (uInt j=0; j= 0 && comptr_p[j] < Int(count_p)) { for (uInt j3=0; j340) { os << w << v << endl; v = "= "; // Indicate follow-on include w = " "; // Indent for (Int i1=0; i10) { c--; w = " "; for (Int i1=0; i1= Int(count_p)) { c1++; os << comout_p[j2] << endl; } } if (cwarn) { cerr << "Warning: One or more possibly superfluous template arguments " "given.\n Run reident with the -v (verbose) switch to learn more" << endl; } } void Template::writeDup(ostream &os, const String &userFile, Bool isSys) { // Sort the name list Vector inx; Sort sort; sort.sortKey(tdname_p.storage(), TpString); sort.sort(inx, tdcount_p); uInt i(0); // Count the entries // Scan all entries for groups dcount_p = 0; while (i1) { // Check if -s switch given Bool doit = True; if (isSys) { doit = False; // Check if _ReposFiller mentioned for (uInt j=i; j= comout_p.nelements()) { comout_p.resize(ccount_p+100); comptr_p.resize(ccount_p+100); } comout_p[ccount_p] = txt; comptr_p[ccount_p] = count_p; if (atstart && count_p == 0) comptr_p[ccount_p] = -1; ccount_p++; } void Template::setOutput(const String &txt) { if (count_p >= output_p.nelements()) output_p.resize(count_p+100); output_p[count_p++] = txt; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Utilities/ValType.cc000066400000000000000000000353371476623553700201140ustar00rootroot00000000000000//# ValType.cc: Class describing the data types and their undefined values //# Copyright (C) 1993,1994,1995,1996,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# This is the implementation of the ValType class. //# Most functions are inlined in the header file. ValType::ValType () {} const Bool ValType::undefbool = False; const Char ValType::undefchar = (Char)-128; const uChar ValType::undefuchar = 0; const Short ValType::undefshort = -32768; const uShort ValType::undefushort = 0; const Int ValType::undefint = -2*Int(32768*32768); const uInt ValType::undefuint = 0; const Int64 ValType::undefint64 = -2*Int64(32768*32768)*Int64(32768*32768); const float ValType::undeffloat = -FLT_MIN; const Complex ValType::undefcomplex (-FLT_MIN, -FLT_MIN); const double ValType::undefdouble = -DBL_MIN; const DComplex ValType::undefdcomplex (-DBL_MIN, -DBL_MIN); const String ValType::undefstring (""); //# Get the name of the data type. const String& ValType::getTypeStr (DataType dt) { switch (dt) { case TpBool: return strbool(); case TpChar: return strchar(); case TpUChar: return struchar(); case TpShort: return strshort(); case TpUShort: return strushort(); case TpInt: return strint(); case TpUInt: return struint(); case TpInt64: return strint64(); case TpFloat: return strfloat(); case TpDouble: return strdouble(); case TpComplex: return strcomplex(); case TpDComplex: return strdcomplex(); case TpString: return strstring(); case TpRecord: return strrecord(); case TpTable: return strtable(); case TpOther: return strother(); default: break; } return strunknown(); } //# Get the size of the data type. int ValType::getTypeSize (DataType dt) { switch (dt) { case TpBool: case TpArrayBool: return sizeof(Bool); case TpChar: case TpArrayChar: return sizeof(Char); case TpUChar: case TpArrayUChar: return sizeof(uChar); case TpShort: case TpArrayShort: return sizeof(short); case TpUShort: case TpArrayUShort: return sizeof(unsigned short); case TpInt: case TpArrayInt: return sizeof(Int); case TpUInt: case TpArrayUInt: return sizeof(uInt); case TpInt64: case TpArrayInt64: return sizeof(Int64); case TpFloat: case TpArrayFloat: return sizeof(float); case TpDouble: case TpArrayDouble: return sizeof(double); case TpComplex: case TpArrayComplex: return sizeof(Complex); case TpDComplex: case TpArrayDComplex: return sizeof(DComplex); case TpString: case TpArrayString: return sizeof(String); default: break; } return 0; } //# Get the canonical size of the data type. int ValType::getCanonicalSize (DataType dt, Bool BECanonical) { if (BECanonical) { switch (dt) { case TpChar: case TpArrayChar: return CanonicalConversion::canonicalSize (static_cast(0)); case TpUChar: case TpArrayUChar: return CanonicalConversion::canonicalSize (static_cast(0)); case TpShort: case TpArrayShort: return CanonicalConversion::canonicalSize (static_cast(0)); case TpUShort: case TpArrayUShort: return CanonicalConversion::canonicalSize (static_cast(0)); case TpInt: case TpArrayInt: return CanonicalConversion::canonicalSize (static_cast(0)); case TpUInt: case TpArrayUInt: return CanonicalConversion::canonicalSize (static_cast(0)); case TpInt64: case TpArrayInt64: return CanonicalConversion::canonicalSize (static_cast(0)); case TpFloat: case TpArrayFloat: return CanonicalConversion::canonicalSize (static_cast(0)); case TpDouble: case TpArrayDouble: return CanonicalConversion::canonicalSize (static_cast(0)); case TpComplex: case TpArrayComplex: return 2*CanonicalConversion::canonicalSize (static_cast(0)); case TpDComplex: case TpArrayDComplex: return 2*CanonicalConversion::canonicalSize (static_cast(0)); default: break; } } else { switch (dt) { case TpChar: case TpArrayChar: return LECanonicalConversion::canonicalSize (static_cast(0)); case TpUChar: case TpArrayUChar: return LECanonicalConversion::canonicalSize (static_cast(0)); case TpShort: case TpArrayShort: return LECanonicalConversion::canonicalSize (static_cast(0)); case TpUShort: case TpArrayUShort: return LECanonicalConversion::canonicalSize (static_cast(0)); case TpInt: case TpArrayInt: return LECanonicalConversion::canonicalSize (static_cast(0)); case TpUInt: case TpArrayUInt: return LECanonicalConversion::canonicalSize (static_cast(0)); case TpInt64: case TpArrayInt64: return LECanonicalConversion::canonicalSize (static_cast(0)); case TpFloat: case TpArrayFloat: return LECanonicalConversion::canonicalSize (static_cast(0)); case TpDouble: case TpArrayDouble: return LECanonicalConversion::canonicalSize (static_cast(0)); case TpComplex: case TpArrayComplex: return 2*LECanonicalConversion::canonicalSize (static_cast(0)); case TpDComplex: case TpArrayDComplex: return 2*LECanonicalConversion::canonicalSize (static_cast(0)); default: break; } } return 0; } void ValType::getCanonicalFunc (DataType dt, Conversion::ValueFunction*& readFunc, Conversion::ValueFunction*& writeFunc, uInt& nrElementsPerValue, Bool BECanonical) { nrElementsPerValue = 1; if (BECanonical) { switch (dt) { case TpBool: case TpArrayBool: readFunc = &Conversion::bitToBool; writeFunc = &Conversion::boolToBit; break; case TpChar: case TpArrayChar: readFunc = CanonicalConversion::getToLocal (static_cast(0)); writeFunc = CanonicalConversion::getFromLocal (static_cast(0)); break; case TpUChar: case TpArrayUChar: readFunc = CanonicalConversion::getToLocal (static_cast(0)); writeFunc = CanonicalConversion::getFromLocal (static_cast(0)); break; case TpShort: case TpArrayShort: readFunc = CanonicalConversion::getToLocal (static_cast(0)); writeFunc = CanonicalConversion::getFromLocal (static_cast(0)); break; case TpUShort: case TpArrayUShort: readFunc = CanonicalConversion::getToLocal (static_cast(0)); writeFunc = CanonicalConversion::getFromLocal (static_cast(0)); break; case TpInt: case TpArrayInt: readFunc = CanonicalConversion::getToLocal (static_cast(0)); writeFunc = CanonicalConversion::getFromLocal (static_cast(0)); break; case TpUInt: case TpArrayUInt: readFunc = CanonicalConversion::getToLocal (static_cast(0)); writeFunc = CanonicalConversion::getFromLocal (static_cast(0)); break; case TpInt64: case TpArrayInt64: readFunc = CanonicalConversion::getToLocal (static_cast(0)); writeFunc = CanonicalConversion::getFromLocal (static_cast(0)); break; case TpComplex: case TpArrayComplex: nrElementsPerValue = 2; CASACORE_FALLTHROUGH; case TpFloat: case TpArrayFloat: readFunc = CanonicalConversion::getToLocal (static_cast(0)); writeFunc = CanonicalConversion::getFromLocal (static_cast(0)); break; case TpDComplex: case TpArrayDComplex: nrElementsPerValue = 2; CASACORE_FALLTHROUGH; case TpDouble: case TpArrayDouble: readFunc = CanonicalConversion::getToLocal (static_cast(0)); writeFunc = CanonicalConversion::getFromLocal (static_cast(0)); break; default: readFunc = 0; writeFunc = 0; } } else { switch (dt) { case TpBool: case TpArrayBool: readFunc = &Conversion::bitToBool; writeFunc = &Conversion::boolToBit; break; case TpChar: case TpArrayChar: readFunc = LECanonicalConversion::getToLocal (static_cast(0)); writeFunc = LECanonicalConversion::getFromLocal (static_cast(0)); break; case TpUChar: case TpArrayUChar: readFunc = LECanonicalConversion::getToLocal (static_cast(0)); writeFunc = LECanonicalConversion::getFromLocal (static_cast(0)); break; case TpShort: case TpArrayShort: readFunc = LECanonicalConversion::getToLocal (static_cast(0)); writeFunc = LECanonicalConversion::getFromLocal (static_cast(0)); break; case TpUShort: case TpArrayUShort: readFunc = LECanonicalConversion::getToLocal (static_cast(0)); writeFunc = LECanonicalConversion::getFromLocal (static_cast(0)); break; case TpInt: case TpArrayInt: readFunc = LECanonicalConversion::getToLocal (static_cast(0)); writeFunc = LECanonicalConversion::getFromLocal (static_cast(0)); break; case TpUInt: case TpArrayUInt: readFunc = LECanonicalConversion::getToLocal (static_cast(0)); writeFunc = LECanonicalConversion::getFromLocal (static_cast(0)); break; case TpInt64: case TpArrayInt64: readFunc = LECanonicalConversion::getToLocal (static_cast(0)); writeFunc = LECanonicalConversion::getFromLocal (static_cast(0)); break; case TpComplex: case TpArrayComplex: nrElementsPerValue = 2; CASACORE_FALLTHROUGH; case TpFloat: case TpArrayFloat: readFunc = LECanonicalConversion::getToLocal (static_cast(0)); writeFunc = LECanonicalConversion::getFromLocal (static_cast(0)); break; case TpDComplex: case TpArrayDComplex: nrElementsPerValue = 2; CASACORE_FALLTHROUGH; case TpDouble: case TpArrayDouble: readFunc = LECanonicalConversion::getToLocal (static_cast(0)); writeFunc = LECanonicalConversion::getFromLocal (static_cast(0)); break; default: readFunc = 0; writeFunc = 0; } } } //# Test if a data type can be promoted to another. //# Note that the cases fall through. Bool ValType::isPromotable (DataType from, DataType to) { if (from == TpOther) return False; if (from == to) return True; switch (from) { case TpChar: if (to == TpShort) return True; CASACORE_FALLTHROUGH; case TpShort: if (to == TpInt) return True; CASACORE_FALLTHROUGH; case TpInt: if (to == TpInt64) return True; CASACORE_FALLTHROUGH; case TpInt64: case TpFloat: case TpDouble: if (to == TpFloat || to == TpDouble) return True; CASACORE_FALLTHROUGH; case TpComplex: case TpDComplex: if (to == TpComplex || to == TpDComplex) return True; return False; case TpUChar: if (to == TpUShort) return True; CASACORE_FALLTHROUGH; case TpUShort: if (to == TpUInt) return True; CASACORE_FALLTHROUGH; case TpUInt: if (to == TpInt64) return True; if (to == TpFloat || to == TpDouble) return True; if (to == TpComplex || to == TpDComplex) return True; return False; default: break; } return False; } //# Get the comparison routine. ObjCompareFunc* ValType::getCmpFunc (DataType dt) { switch (dt) { case TpBool: return &ObjCompare::compare; case TpChar: return &ObjCompare::compare; case TpUChar: return &ObjCompare::compare; case TpShort: return &ObjCompare::compare; case TpUShort: return &ObjCompare::compare; case TpInt: return &ObjCompare::compare; case TpUInt: return &ObjCompare::compare; case TpInt64: return &ObjCompare::compare; case TpFloat: return &ObjCompare::compare; case TpDouble: return &ObjCompare::compare; case TpComplex: return &ObjCompare::compare; case TpDComplex: return &ObjCompare::compare; case TpString: return &ObjCompare::compare; default: break; } return 0; } //# Get the comparison object. std::shared_ptr ValType::getCmpObj (DataType dt) { switch (dt) { case TpBool: return std::make_shared>(); case TpChar: return std::make_shared>(); case TpUChar: return std::make_shared>(); case TpShort: return std::make_shared>(); case TpUShort: return std::make_shared>(); case TpInt: return std::make_shared>(); case TpUInt: return std::make_shared>(); case TpInt64: return std::make_shared>(); case TpFloat: return std::make_shared>(); case TpDouble: return std::make_shared>(); case TpComplex: return std::make_shared>(); case TpDComplex: return std::make_shared>(); case TpString: return std::make_shared>(); default: break; } return std::shared_ptr(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/Utilities/ValType.h000066400000000000000000000526311476623553700177520ustar00rootroot00000000000000//# ValType.h: Data types and their undefined values //# Copyright (C) 1993,1994,1995,1996,1998,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_VALTYPE_H #define CASA_VALTYPE_H //# Includes #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableRecord; // // Data types and their undefined values. // // // // // //
        267. enum DataType // // // Class ValType describes the data types and their // "undefined values". // // Supported are built-in data types, Bool, // String, Complex and DComplex. // As a rule, the smallest possible value of a data type is used as its // "undefined value"; for String we use the null string, and // for Bool the value False. // // The class does not contain data. It merely defines constants and // has overloaded functions that return in some form the "undefined // value", the data type, or certain other information about the data // type. // class ValType { public: // Get the "undefined value" for this data type as the function's // return value. // static Bool undefBool (); static Char undefChar (); static uChar undefUChar (); static Short undefShort (); static uShort undefUShort (); static Int undefInt (); static uInt undefUInt (); static Int64 undefInt64 (); static float undefFloat (); static double undefDouble (); static Complex undefComplex (); static DComplex undefDComplex (); static String undefString (); // // Get the "undefined value" for this data type in the argument. // The void* function is not doing anything and is for // TpOther types. // static void getUndef (Bool*); static void getUndef (Char*); static void getUndef (uChar*); static void getUndef (Short*); static void getUndef (uShort*); static void getUndef (Int*); static void getUndef (uInt*); static void getUndef (Int64*); static void getUndef (float*); static void getUndef (double*); static void getUndef (Complex*); static void getUndef (DComplex*); static void getUndef (String*); static void getUndef (void*); // // Get the data type code for this type as the function's // return value. // static DataType getType (const Bool*); static DataType getType (const Char*); static DataType getType (const uChar*); static DataType getType (const Short*); static DataType getType (const uShort*); static DataType getType (const Int*); static DataType getType (const uInt*); static DataType getType (const Int64*); static DataType getType (const float*); static DataType getType (const double*); static DataType getType (const Complex*); static DataType getType (const DComplex*); static DataType getType (const String*); static DataType getType (const TableRecord*); static DataType getType (const void*); // // Get the name of the data type. The void* returns // the string "Other ". // static const String& getTypeStr (DataType); static const String& getTypeStr (const Bool*); static const String& getTypeStr (const Char*); static const String& getTypeStr (const uChar*); static const String& getTypeStr (const Short*); static const String& getTypeStr (const uShort*); static const String& getTypeStr (const Int*); static const String& getTypeStr (const uInt*); static const String& getTypeStr (const Int64*); static const String& getTypeStr (const float*); static const String& getTypeStr (const double*); static const String& getTypeStr (const Complex*); static const String& getTypeStr (const DComplex*); static const String& getTypeStr (const String*); static const String& getTypeStr (const TableRecord*); static const String& getTypeStr (const void*); // // Get the size of data type (in local format). static int getTypeSize (DataType); // Get the size of data type in canonical format. //
          The argument BECanonical determines if the big-endian // or little-endian canonical format is used. static int getCanonicalSize (DataType, Bool BECanonical = True); // Get the functions to convert to/from canonical format. // These functions take the number of pixels as the length argument. // It returns the number of elements per value; normally this is 1, // but for complex values it is 2 (since they convert float/double). //
          The argument BECanonical determines if the big-endian // or little-endian canonical format is used. static void getCanonicalFunc (DataType dt, Conversion::ValueFunction*& readFunc, Conversion::ValueFunction*& writeFunc, uInt& nrElementsPerValue, Bool BECanonical = True); // Test if a data type can be promoted to another. static Bool isPromotable (DataType from, DataType to); // Get the pointer to the routine which compares two values. static ObjCompareFunc* getCmpFunc (DataType); // Get the object which compares two values. static std::shared_ptr getCmpObj (DataType); // Put the value into AipsIO. // The void* function is not doing anything and is for // TpOther types. // static void put (AipsIO&, const Bool*); static void put (AipsIO&, const Char*); static void put (AipsIO&, const uChar*); static void put (AipsIO&, const Short*); static void put (AipsIO&, const uShort*); static void put (AipsIO&, const Int*); static void put (AipsIO&, const uInt*); static void put (AipsIO&, const Int64*); static void put (AipsIO&, const float*); static void put (AipsIO&, const double*); static void put (AipsIO&, const Complex*); static void put (AipsIO&, const DComplex*); static void put (AipsIO&, const String*); static void put (AipsIO&, const void*); // // Get the value from AipsIO. // The void* function is not doing anything and is for // TpOther types. // static void get (AipsIO&, Bool*); static void get (AipsIO&, Char*); static void get (AipsIO&, uChar*); static void get (AipsIO&, Short*); static void get (AipsIO&, uShort*); static void get (AipsIO&, Int*); static void get (AipsIO&, uInt*); static void get (AipsIO&, Int64*); static void get (AipsIO&, float*); static void get (AipsIO&, double*); static void get (AipsIO&, Complex*); static void get (AipsIO&, DComplex*); static void get (AipsIO&, String*); static void get (AipsIO&, void*); // // Put the value into the ostream. // The void* function is not doing anything and is for // TpOther types. // static void put (ostream&, const Bool*); static void put (ostream&, const Char*); static void put (ostream&, const uChar*); static void put (ostream&, const Short*); static void put (ostream&, const uShort*); static void put (ostream&, const Int*); static void put (ostream&, const uInt*); static void put (ostream&, const Int64*); static void put (ostream&, const float*); static void put (ostream&, const double*); static void put (ostream&, const Complex*); static void put (ostream&, const DComplex*); static void put (ostream&, const String*); static void put (ostream&, const void*); // // Check if a value is defined, i.e. if it mismatches the given // undefined value. The void* function (for non-standard // data types) always returns the value 1, since such // values cannot be undefined. // static int isDefined (const Bool* value, const Bool* undef); static int isDefined (const Char* value, const Char* undef); static int isDefined (const uChar* value, const uChar* undef); static int isDefined (const Short* value, const Short* undef); static int isDefined (const uShort* value, const uShort* undef); static int isDefined (const Int* value, const Int* undef); static int isDefined (const uInt* value, const uInt* undef); static int isDefined (const Int64* value, const Int64* undef); static int isDefined (const float* value, const float* undef); static int isDefined (const double* value, const double* undef); static int isDefined (const Complex* value, const Complex* undef); static int isDefined (const DComplex* value, const DComplex* undef); static int isDefined (const String* value, const String* undef); static int isDefined (const void* value, const void* undef); // private: static const Bool undefbool ; static const Char undefchar ; static const uChar undefuchar ; static const Short undefshort ; static const uShort undefushort ; static const Int undefint ; static const uInt undefuint ; static const Int64 undefint64 ; static const float undeffloat ; static const double undefdouble ; static const Complex undefcomplex ; static const DComplex undefdcomplex; static const String undefstring ; static const String &strbool( ) { static String result("Bool "); return result; } static const String &strchar( ) { static String result("Char "); return result; } static const String &struchar( ) { static String result("uChar "); return result; } static const String &strshort( ) { static String result("Short "); return result; } static const String &strushort( ) { static String result("uShort "); return result; } static const String &strint( ) { static String result("Int "); return result; } static const String &struint( ) { static String result("uInt "); return result; } static const String &strint64( ) { static String result("Int64 "); return result; } static const String &strfloat( ) { static String result("float "); return result; } static const String &strdouble( ) { static String result("double "); return result; } static const String &strcomplex( ) { static String result("Complex "); return result; } static const String &strdcomplex( ) { static String result("DComplex"); return result; } static const String &strstring( ) { static String result("String "); return result; } static const String &strrecord( ) { static String result("Record "); return result; } static const String &strtable( ) { static String result("Table "); return result; } static const String &strother( ) { static String result("Other "); return result; } static const String &strunknown( ) { static String result("unknown "); return result; } // // This class is not meant to be constructed. // ValType (); }; inline Bool ValType::undefBool () {return undefbool;} inline Char ValType::undefChar () {return undefchar;} inline uChar ValType::undefUChar () {return undefuchar;} inline Short ValType::undefShort () {return undefshort;} inline uShort ValType::undefUShort () {return undefushort;} inline Int ValType::undefInt () {return undefint;} inline uInt ValType::undefUInt () {return undefuint;} inline Int64 ValType::undefInt64 () {return undefint64;} inline float ValType::undefFloat () {return undeffloat;} inline double ValType::undefDouble () {return undefdouble;} inline Complex ValType::undefComplex () {return undefcomplex;} inline DComplex ValType::undefDComplex () {return undefdcomplex;} inline String ValType::undefString () {return undefstring;} inline void ValType::getUndef (Bool* val) {*val = undefbool;} inline void ValType::getUndef (Char* val) {*val = undefchar;} inline void ValType::getUndef (uChar* val) {*val = undefuchar;} inline void ValType::getUndef (Short* val) {*val = undefshort;} inline void ValType::getUndef (uShort* val) {*val = undefushort;} inline void ValType::getUndef (Int* val) {*val = undefint;} inline void ValType::getUndef (uInt* val) {*val = undefuint;} inline void ValType::getUndef (Int64* val) {*val = undefint64;} inline void ValType::getUndef (float* val) {*val = undeffloat;} inline void ValType::getUndef (double* val) {*val = undefdouble;} inline void ValType::getUndef (Complex* val) {*val = undefcomplex;} inline void ValType::getUndef (DComplex* val) {*val = undefdcomplex;} inline void ValType::getUndef (String* val) {*val = undefstring;} inline void ValType::getUndef (void*) {} inline DataType ValType::getType (const Bool*) {return TpBool;} inline DataType ValType::getType (const Char*) {return TpChar;} inline DataType ValType::getType (const uChar*) {return TpUChar;} inline DataType ValType::getType (const Short*) {return TpShort;} inline DataType ValType::getType (const uShort*) {return TpUShort;} inline DataType ValType::getType (const Int*) {return TpInt;} inline DataType ValType::getType (const uInt*) {return TpUInt;} inline DataType ValType::getType (const Int64*) {return TpInt64;} inline DataType ValType::getType (const float*) {return TpFloat;} inline DataType ValType::getType (const double*) {return TpDouble;} inline DataType ValType::getType (const Complex*) {return TpComplex;} inline DataType ValType::getType (const DComplex*) {return TpDComplex;} inline DataType ValType::getType (const String*) {return TpString;} inline DataType ValType::getType (const TableRecord*) {return TpRecord;} inline DataType ValType::getType (const void*) {return TpOther;} inline const String& ValType::getTypeStr (const Bool*) {return strbool();} inline const String& ValType::getTypeStr (const Char*) {return strchar();} inline const String& ValType::getTypeStr (const uChar*) {return struchar();} inline const String& ValType::getTypeStr (const Short*) {return strshort();} inline const String& ValType::getTypeStr (const uShort*) {return strushort();} inline const String& ValType::getTypeStr (const Int*) {return strint();} inline const String& ValType::getTypeStr (const uInt*) {return struint();} inline const String& ValType::getTypeStr (const Int64*) {return strint64();} inline const String& ValType::getTypeStr (const float*) {return strfloat();} inline const String& ValType::getTypeStr (const double*) {return strdouble();} inline const String& ValType::getTypeStr (const Complex*) {return strcomplex();} inline const String& ValType::getTypeStr (const DComplex*) {return strdcomplex();} inline const String& ValType::getTypeStr (const String*) {return strstring();} inline const String& ValType::getTypeStr (const TableRecord*) {return strrecord();} inline const String& ValType::getTypeStr (const void*) {return strother();} inline void ValType::put (AipsIO& ios, const Bool* value) {ios << *value;} inline void ValType::put (AipsIO& ios, const Char* value) {ios << *value;} inline void ValType::put (AipsIO& ios, const uChar* value) {ios << *value;} inline void ValType::put (AipsIO& ios, const Short* value) {ios << *value;} inline void ValType::put (AipsIO& ios, const uShort* value) {ios << *value;} inline void ValType::put (AipsIO& ios, const Int* value) {ios << *value;} inline void ValType::put (AipsIO& ios, const uInt* value) {ios << *value;} inline void ValType::put (AipsIO& ios, const Int64* value) {ios << *value;} inline void ValType::put (AipsIO& ios, const float* value) {ios << *value;} inline void ValType::put (AipsIO& ios, const double* value) {ios << *value;} inline void ValType::put (AipsIO& ios, const Complex* value) {ios << *value;} inline void ValType::put (AipsIO& ios, const DComplex* value) {ios << *value;} inline void ValType::put (AipsIO& ios, const String* value) {ios << *value;} inline void ValType::put (AipsIO&, const void*) {} inline void ValType::get (AipsIO& ios, Bool* value) {ios >> *value;} inline void ValType::get (AipsIO& ios, Char* value) {ios >> *value;} inline void ValType::get (AipsIO& ios, uChar* value) {ios >> *value;} inline void ValType::get (AipsIO& ios, Short* value) {ios >> *value;} inline void ValType::get (AipsIO& ios, uShort* value) {ios >> *value;} inline void ValType::get (AipsIO& ios, Int* value) {ios >> *value;} inline void ValType::get (AipsIO& ios, uInt* value) {ios >> *value;} inline void ValType::get (AipsIO& ios, Int64* value) {ios >> *value;} inline void ValType::get (AipsIO& ios, float* value) {ios >> *value;} inline void ValType::get (AipsIO& ios, double* value) {ios >> *value;} inline void ValType::get (AipsIO& ios, Complex* value) {ios >> *value;} inline void ValType::get (AipsIO& ios, DComplex* value) {ios >> *value;} inline void ValType::get (AipsIO& ios, String* value) {ios >> *value;} inline void ValType::get (AipsIO&, void*) {} inline void ValType::put (ostream& ios, const Bool* value) {ios << *value;} inline void ValType::put (ostream& ios, const Char* value) {ios << *value;} inline void ValType::put (ostream& ios, const uChar* value) {ios << *value;} inline void ValType::put (ostream& ios, const Short* value) {ios << *value;} inline void ValType::put (ostream& ios, const uShort* value) {ios << *value;} inline void ValType::put (ostream& ios, const Int* value) {ios << *value;} inline void ValType::put (ostream& ios, const uInt* value) {ios << *value;} inline void ValType::put (ostream& ios, const Int64* value) {ios << *value;} inline void ValType::put (ostream& ios, const float* value) {ios << *value;} inline void ValType::put (ostream& ios, const double* value) {ios << *value;} inline void ValType::put (ostream& ios, const Complex* value) {ios << *value;} inline void ValType::put (ostream& ios, const DComplex* value) {ios << *value;} inline void ValType::put (ostream& ios, const String* value) {ios << *value;} inline void ValType::put (ostream&, const void*) {} inline int ValType::isDefined (const Bool* value, const Bool* undef) {return *value != *undef;} inline int ValType::isDefined (const Char* value, const Char* undef) {return *value != *undef;} inline int ValType::isDefined (const uChar* value, const uChar* undef) {return *value != *undef;} inline int ValType::isDefined (const Short* value, const Short* undef) {return *value != *undef;} inline int ValType::isDefined (const uShort* value, const uShort* undef) {return *value != *undef;} inline int ValType::isDefined (const Int* value, const Int* undef) {return *value != *undef;} inline int ValType::isDefined (const uInt* value, const uInt* undef) {return *value != *undef;} inline int ValType::isDefined (const Int64* value, const Int64* undef) {return *value != *undef;} inline int ValType::isDefined (const float* value, const float* undef) {return *value != *undef;} inline int ValType::isDefined (const double* value, const double* undef) {return *value != *undef;} inline int ValType::isDefined (const Complex* value, const Complex* undef) {return *value != *undef;} inline int ValType::isDefined (const DComplex* value, const DComplex* undef) {return *value != *undef;} inline int ValType::isDefined (const String* value, const String* undef) {return *value != *undef;} inline int ValType::isDefined (const void*, const void*) {return 1;} } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Utilities/ValTypeId.h000066400000000000000000000065001476623553700202210ustar00rootroot00000000000000//# ValType.h: The id-string of a value type //# Copyright (C) 1993,1994,1995,1996,1998,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_VALTYPEID_H #define CASA_VALTYPEID_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // The id-string of a value type // // // // // //
        268. class ValType // // // The templated global functions valDataTypeId get the data type id string // of the given type. // For standard types this is the ValType data type string. // For other types (i.e. classes) it is the result of the class dataTypeId // function. // // template inline String valDataTypeId (const T*) { return T::dataTypeId(); } inline String valDataTypeId (const Bool* obj) { return ValType::getTypeStr (obj); } inline String valDataTypeId (const Char* obj) { return ValType::getTypeStr (obj); } inline String valDataTypeId (const uChar* obj) { return ValType::getTypeStr (obj); } inline String valDataTypeId (const Short* obj) { return ValType::getTypeStr (obj); } inline String valDataTypeId (const uShort* obj) { return ValType::getTypeStr (obj); } inline String valDataTypeId (const Int* obj) { return ValType::getTypeStr (obj); } inline String valDataTypeId (const uInt* obj) { return ValType::getTypeStr (obj); } inline String valDataTypeId (const Int64* obj) { return ValType::getTypeStr (obj); } inline String valDataTypeId (const float* obj) { return ValType::getTypeStr (obj); } inline String valDataTypeId (const double* obj) { return ValType::getTypeStr (obj); } inline String valDataTypeId (const Complex* obj) { return ValType::getTypeStr (obj); } inline String valDataTypeId (const DComplex* obj) { return ValType::getTypeStr (obj); } inline String valDataTypeId (const String* obj) { return ValType::getTypeStr (obj); } inline String valDataTypeId (const TableRecord* obj) { return ValType::getTypeStr (obj); } // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Utilities/generic.h000066400000000000000000000042011476623553700177700ustar00rootroot00000000000000//# generic.h: some defines, from the GNU C++ Library //# This may look like C code, but it is really -*- C++ -*- //# //# Copyright (C) 1988 Free Software Foundation //# written by Doug Lea (dl@rocky.oswego.edu) //# //# This file is part of the GNU C++ Library. This library is free //# software; you can redistribute it and/or modify it under the terms of //# the GNU Library General Public License as published by the Free //# Software Foundation; either version 2 of the License, or (at your //# option) any later version. This library 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 Library General Public License for more details. //# You should have received a copy of the GNU Library General Public //# License along with this library; if not, write to the Free Software //# Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. #ifndef CASA_GENERIC_H #define CASA_GENERIC_H namespace casacore { //# NAMESPACE CASACORE - BEGIN /* * See the CPP manual, argument prescan section for explanation */ // Generic gnu macros // // // #define g_name2(a,b) gEnErIc2(a,b) #define gEnErIc2(a,b) a ## b #define g_name3(a,b,c) gEnErIc3(a,b,c) #define gEnErIc3(a,b,c) a ## b ## c #define g_name4(a,b,c,d) gEnErIc4(a,b,c,d) #define gEnErIc4(a,b,c,d) a ## b ## c ## d #define GENERIC_STRING(a) gEnErIcStRiNg(a) #define gEnErIcStRiNg(a) #a #define g_declare(clas,t) g_name2(clas,declare)(t) #define g_declare2(clas,t1,t2) g_name2(clas,declare2)(t1,t2) #define g_implement(clas,t) g_name2(clas,implement)(t) #define g_implement2(clas,t1,t2) g_name2(clas,implement2)(t1,t2) //extern genericerror(int,char*); typedef int (*GPT)(int,char*); #define g_set_handler(gen,type,x) g_name4(set_,type,gen,_handler)(x) #define g_errorhandler(gen,type) g_name3(type,gen,handler) #define g_callerror(gen,type,a,b) (*g_errorhandler(gen,type))(a,b) // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/Utilities/test/000077500000000000000000000000001476623553700171655ustar00rootroot00000000000000casacore-3.7.1/casa/Utilities/test/CMakeLists.txt000066400000000000000000000010021476623553700217160ustar00rootroot00000000000000set (tests dCOWptr dMUString tAssert tBinarySearch tBitVector tCompare tCompositeNumber tCopy tCountedPtr tCOWPtr tDataType tDefaultValue tDynBuffer tFallible tGenSort tLinearSearch tPrecision tRegex_1 tRegex2 tRegex tSort_1 tSort tStringDistance ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_casa) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/casa/Utilities/test/dCOWptr.cc000066400000000000000000000067451476623553700210320ustar00rootroot00000000000000//# dCOWPtr.cc: how to use COWPtr's (copy-on-write pointers) //# Copyright (C) 1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include int main() { // create an array of dimension 1, length 4 Array arr(IPosition(1,4)); // create a second array from the first array. note that neither // array has any values yet assigned to them Array arr1(arr); // assign the elements of the first array indgen(arr); Bool deleteIt = False; Bool readOnly = True; COWPtr > arrptr(&arr1, deleteIt, readOnly); //COWPtr< Array > arrptr(&arr1, False, True); // The COWptr does not have exclusive control of arr1 as I will also // access it through normal array functions. It is Readonly so that when // I modify it, it is forced to make a copy. Otherwise it will only make // a copy when its internal number of references (increased using the // assignment operator and copy constructor) is greater than one and a // write operation is performed. However the COWptr does not not know // how many external references (through normal array functions) there // are so it is advisable to always make a copy if other methods of // accessing the data are used. If COWptr functions were the only // mechanism for accessing the data then I would not need to make the // data Readonly. cout << "The original array is:" << arr << endl; cout << "Array 1 is a reference to it:" << arr1 << endl; arr1(IPosition(1,0)) = 10; cout << "Modifying array 1 modifies the original array also:" << arr1<< endl; cout << "Or accessing it via the COWptr gives the same answer:" << *arrptr << endl; arrptr.rwRef().operator()(IPosition(1,1)) = 11; // I need the rwRef function as both the operator-> and operator* // functions return constant references and I need a non-const one to be // able to modify the data cout << "Array 1 has been modified using COWptr functions:" << *arrptr << endl; cout << "But the normally accessed Array 1 is unchanged:" << arr1 << endl; cout << "As is the original array:" << arr << endl; cout << "as the COWptr has made a copy" << endl; } casacore-3.7.1/casa/Utilities/test/dCOWptr.out000066400000000000000000000006231476623553700212410ustar00rootroot00000000000000The original array is:[0, 1, 2, 3] Array 1 is a reference to it:[0, 1, 2, 3] Modifying array 1 modifies the original array also:[10, 1, 2, 3] Or accessing it via the COWptr gives the same answer:[10, 1, 2, 3] Array 1 has been modified using COWptr functions:[10, 11, 2, 3] But the normally accessed Array 1 is unchanged:[10, 1, 2, 3] As is the original array:[10, 1, 2, 3] as the COWptr has made a copy casacore-3.7.1/casa/Utilities/test/dMUString.cc000066400000000000000000000075241476623553700213600ustar00rootroot00000000000000//# dMUString.cc: test of MUString class //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include int main() { try { cout << "Demonstrate MVAngle input " << endl; cout << "----------------------------------------------------" << endl; String in = "12.13.15.9"; Quantity res; cout << MVAngle::read(res, in) << endl; cout << in << " = " << res << endl; in = "12d13m15.9"; cout << MVAngle::read(res, in) << endl; cout << in << " = " << res << endl; in = "12h13m15.9"; cout << MVAngle::read(res, in) << endl; cout << in << " = " << res << endl; in = "12d15.9"; cout << MVAngle::read(res, in) << endl; cout << in << " = " << res << endl; in = "12:13:15.9"; cout << MVAngle::read(res, in) << endl; cout << in << " = " << res << endl; in = "12:15.9"; cout << MVAngle::read(res, in) << endl; cout << in << " = " << res << endl; in = "15.9"; cout << MVAngle::read(res, in) << endl; cout << in << " = " << res << endl; in = "15.9 \""; cout << MVAngle::read(res, in) << endl; cout << in << " = " << res << endl; in = "15.9 '"; cout << MVAngle::read(res, in) << endl; cout << in << " = " << res << endl; in = "15.9 s"; cout << MVAngle::read(res, in) << endl; cout << in << " = " << res << endl; in = "15 s"; cout << MVAngle::read(res, in) << endl; cout << in << " = " << res << endl; } catch (std::exception& x) { cout << x.what() << endl; } try { String in = "12.13.15.9p"; Quantity res; cout << MVAngle::read(res, in) << endl; cout << in << " = " << res << endl; } catch (std::exception& x) { cout << x.what() << endl; } try { String in = "12.5pm"; String bb; Int ptr = 0; Int l = in.length(); Double res = 0.0; if (ptr < l) { String loc0 = in; // non-const string Int p = in.index(Regex("[ ]"),ptr); p = (p<0) ? l : p; String loc = loc0.at(ptr,p-ptr); ptr = p; istringstream instr(loc); streampos stt(instr.tellg()); cout << "Pos0: " << instr.tellg()-stt << instr.rdstate() << endl; instr >> res; cout << "Pos0a:" << instr.tellg()-stt << instr.rdstate() << endl; cout << "ios::failbit: " << ios::failbit << endl; instr.clear(~ios::failbit & instr.rdstate()); cout << "Pos0b:" << instr.tellg()-stt << instr.rdstate() << endl; instr >> bb; cout << "Pos1: " << instr.tellg()-stt << endl; cout << in << " = " << res << " : " << bb << endl; } } catch (std::exception& x) { cout << x.what() << endl; } return(0); } casacore-3.7.1/casa/Utilities/test/tAssert.cc000066400000000000000000000037541476623553700211320ustar00rootroot00000000000000//# tAssert.cc: This program tests the ASSERT macros //# Copyright (C) 2022 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include int main() { #ifdef AIPS_DEBUG cout << "AIPS_DEBUG is set" << endl; DebugAssert (1==1, AipsError); DebugAssertExit (1==1); try { DebugAssert (1==0, AipsError); AlwaysAssertExit (1==0); } catch (const AipsError& x) { cout << " " << x.what() << endl; } #else cout << "AIPS_DEBUG is not set" << endl; DebugAssert (1==0, AipsError); // should be a no-op #endif AlwaysAssert (1==1, AipsError); AlwaysAssertExit (1==1); try { AlwaysAssert (1==2, AipsError); return 1; } catch (const AipsError& x) { cout << " " << x.what() << endl; } cout << "tAssert ended OK" << endl; return 0; // exit with success status } casacore-3.7.1/casa/Utilities/test/tBinarySearch.cc000066400000000000000000000150641476623553700222400ustar00rootroot00000000000000//# tBinarySearch.cc: This program tests the binary search functions //# Copyright (C) 1995,1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #if !defined(AIPS_DEBUG) #define AIPS_DEBUG #endif #if !defined(AIPS_ARRAY_INDEX_CHECK) #define AIPS_ARRAY_INDEX_CHECK #endif #include #include #include #include #include #include int main() { Bool found; { IPosition ip1(1, 5); AlwaysAssertExit(binarySearch(found, ip1, 1, ip1.nelements()) == 0 && !found); AlwaysAssertExit(binarySearch(found, ip1, 5, ip1.nelements()) == 0 && found); AlwaysAssertExit(binarySearch(found, ip1, 10, ip1.nelements()) == 1 && !found); AlwaysAssertExit(binarySearch(found, ip1, 10, 0u) == 0 && !found); } { IPosition ip1(3, 1, 5, 9); AlwaysAssertExit(binarySearch(found, ip1, 0, ip1.nelements()) == 0 && !found); AlwaysAssertExit(binarySearch(found, ip1, 1, ip1.nelements()) == 0 && found); AlwaysAssertExit(binarySearch(found, ip1, 3, ip1.nelements()) == 1 && !found); AlwaysAssertExit(binarySearch(found, ip1, 5, ip1.nelements()) == 1 && found); AlwaysAssertExit(binarySearch(found, ip1, 7, ip1.nelements()) == 2 && !found); AlwaysAssertExit(binarySearch(found, ip1, 9, ip1.nelements()) == 2 && found); AlwaysAssertExit(binarySearch(found, ip1, 10, ip1.nelements()) == 3 && !found); } { IPosition ip1(3, 9, 5, 1); AlwaysAssertExit(binarySearch(found, ip1, 0, ip1.nelements()) == 3 && !found); AlwaysAssertExit(binarySearch(found, ip1, 1, ip1.nelements()) == 2 && found); AlwaysAssertExit(binarySearch(found, ip1, 3, ip1.nelements()) == 2 && !found); AlwaysAssertExit(binarySearch(found, ip1, 5, ip1.nelements()) == 1 && found); AlwaysAssertExit(binarySearch(found, ip1, 7, ip1.nelements()) == 1 && !found); AlwaysAssertExit(binarySearch(found, ip1, 9, ip1.nelements()) == 0 && found); AlwaysAssertExit(binarySearch(found, ip1, 10, ip1.nelements()) == 0 && !found); } { IPosition ip1(1, 5); AlwaysAssertExit(binarySearchBrackets(found, ip1, 1, ip1.nelements()) == 0 && !found); AlwaysAssertExit(binarySearchBrackets(found, ip1, 5, ip1.nelements()) == 0 && found); AlwaysAssertExit(binarySearchBrackets(found, ip1, 10, ip1.nelements()) == 1 && !found); AlwaysAssertExit(binarySearchBrackets(found, ip1, 10, 0u) == 0 && !found); } { int ia[4]; // int ia[] = {22,9,5,1}; isn't available on all compilers ia[0] = 22; ia[1] = 9; ia[2] = 5; ia[3] = 1; int *ip = &ia[0]; AlwaysAssertExit((binarySearchBrackets(found, ip, 55, 4u) == 0) && !found); AlwaysAssertExit((binarySearchBrackets(found, ip, 22, 4u) == 0) && found); AlwaysAssertExit((binarySearchBrackets(found, ip, 11, 4u) == 1) && !found); AlwaysAssertExit((binarySearchBrackets(found, ip, 9, 4u) == 1) && found); AlwaysAssertExit((binarySearchBrackets(found, ip, 7, 4u) == 2) && !found); AlwaysAssertExit((binarySearchBrackets(found, ip, 5, 4u) == 2) && found); AlwaysAssertExit((binarySearchBrackets(found, ip, 3, 4u) == 3) && !found); AlwaysAssertExit((binarySearchBrackets(found, ip, 1, 4u) == 3) && found); AlwaysAssertExit((binarySearchBrackets(found, ip, -99, 4u) == 4) && !found); AlwaysAssertExit((binarySearchBrackets(found, ip, -99, 0u) == 0) && !found); } { int ia[4]; ia[0] = 1; ia[1] = 5; ia[2] = 9; ia[3] = 22; int *ip = &ia[0]; AlwaysAssertExit((binarySearchBrackets(found, ip, 55, 4u) == 4) && !found); AlwaysAssertExit((binarySearchBrackets(found, ip, 22, 4u) == 3) && found); AlwaysAssertExit((binarySearchBrackets(found, ip, 11, 4u) == 3) && !found); AlwaysAssertExit((binarySearchBrackets(found, ip, 9, 4u) == 2) && found); AlwaysAssertExit((binarySearchBrackets(found, ip, 7, 4u) == 2) && !found); AlwaysAssertExit((binarySearchBrackets(found, ip, 5, 4u) == 1) && found); AlwaysAssertExit((binarySearchBrackets(found, ip, 3, 4u) == 1) && !found); AlwaysAssertExit((binarySearchBrackets(found, ip, 1, 4u) == 0) && found); AlwaysAssertExit((binarySearchBrackets(found, ip, -99, 4u) == 0) && !found); } { int ia[4]; ia[0] = 1; ia[1] = 5; ia[2] = 9; ia[3] = 22; int *ip = &ia[0]; AlwaysAssertExit((binarySearchBrackets(found, ip, 55, 2u, 2) == 4) && !found); AlwaysAssertExit((binarySearchBrackets(found, ip, 22, 2u, 2) == 3) && found); AlwaysAssertExit((binarySearchBrackets(found, ip, 11, 2u, 2) == 3) && !found); AlwaysAssertExit((binarySearchBrackets(found, ip, 9, 2u, 2) == 2) && found); AlwaysAssertExit((binarySearchBrackets(found, ip, 7, 2u, 2) == 2) && !found); AlwaysAssertExit((binarySearchBrackets(found, ip, 5, 2u, 2) == 2) && !found); AlwaysAssertExit((binarySearchBrackets(found, ip, 3, 2u, 2) == 2) && !found); AlwaysAssertExit((binarySearchBrackets(found, ip, 1, 2u, 2) == 2) && !found); AlwaysAssertExit((binarySearchBrackets(found, ip, -99, 2u, 2) == 2) && !found); } { static int ia[4]; // all 0 by language rules int *ip = &ia[0]; AlwaysAssertExit((binarySearchBrackets(found, ip, 0, 4u) == 0) && found); AlwaysAssertExit((binarySearchBrackets(found, ip, -1, 4u) == 0) && !found); AlwaysAssertExit((binarySearchBrackets(found, ip, +1, 4u) == 4) && !found); } cout << "OK\n"; return 0; } casacore-3.7.1/casa/Utilities/test/tBitVector.cc000066400000000000000000000104671476623553700215710ustar00rootroot00000000000000//# tBitVector.cc: This program tests the BitVector class //# Copyright (C) 1994,1995,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include void doIt() { uInt maxbits=35; // Create bit vectors BitVector b; BitVector c(maxbits,True); BitVector d; uInt i; b.resize (maxbits); for (i=0; i<(maxbits/2); i++) { b.setBit(i*2); } b[maxbits-2] = b[maxbits-3]; cout << "b = "; for (i=0; i #include #include #include #include #include #include #include #include static Bool testFunc(Array *ptr, const Array &array, Bool deleteIt, Bool constant) { COWPtr > COW(ptr, deleteIt, constant); // only const T functions may be used through the pointer. AlwaysAssert(COW->nelements() == 128, AipsError); // only const T functions may be used through the dereferenced object. AlwaysAssert(allEQ(*COW, array), AipsError); // return a reference to this instance. AlwaysAssert(allEQ(COW.ref(), array),AipsError); // fill this instance. The pointer must be dynamically allocated. Default // behavior is to delete the pointer when this instance's destructer is // called. "deleteIt = False" implies the pointer is being maintained by // another object,(i.e. this is a copy - do not delete.) The // Boolean "readOnly" argument forces the COWPtr to treat the templated // data as const. This allows non-const data operations be used to fill a // const acting version of COWPtr. // note: this deletes the old ptr and resets it. Array *foobar = new Array(IPosition(2,5,5)); *foobar = 0.0; COW.set(foobar, deleteIt, constant); // return a readable and writable reference to this instance. COW.rwRef().set(22.0); AlwaysAssert(allEQ(COW.rwRef(), 22.0f), AipsError); // returns False if this contains a non-null ptr. Otherwise, True. AlwaysAssert(COW.isNull() == False, AipsError); // make this a copy if more than one exist. if (COW.isReadOnly()){ AlwaysAssert(COW.makeUnique(), AipsError); } else { AlwaysAssert(!COW.makeUnique(), AipsError); } // assignment operator with reference semantics Array *fooAlso = new Array(array); COW = COWPtr >(fooAlso, deleteIt, constant); AlwaysAssert(allEQ(*COW, array), AipsError); //-------------------- test default ctor--------------------------- // default ctor COWPtr > deflt; // returns False if this contains a non-null ptr. Otherwise, True. AlwaysAssert(deflt.isNull() == True, AipsError); // assignment operator with reference semantics Array *fooAgain = new Array(array); deflt = COWPtr >(fooAgain, deleteIt, constant); // only const T functions may be used through the pointer. AlwaysAssert(deflt->nelements() == 128, AipsError); // only const T functions may be used through the dereferenced object. AlwaysAssert(allEQ(*deflt, array),AipsError); // return a reference to this instance. AlwaysAssert(allEQ(deflt.ref(), array),AipsError); // fill this instance. The pointer must be dynamically allocated. Default // behavior is to delete the pointer when this instance's destructer is // called. "deleteIt = False" implies the pointer is being maintained by // another object,(i.e. this is a copy - do not delete.) The // Boolean "readOnly" argument forces the COWPtr to treat the templated // data as const. This allows non-const data operations be used to fill a // const acting version of COWPtr. Array *bar = new Array(IPosition(2,5,5)); *bar = 0.0; deflt.set(bar, deleteIt, constant); // return a readable and writable reference to this instance. deflt.rwRef().set(22.0); AlwaysAssert(allEQ(deflt.rwRef(), 22.0f), AipsError); // make this a copy if more than one exist. if (deflt.isReadOnly()){ AlwaysAssert(deflt.makeUnique(), AipsError); } else { AlwaysAssert(!deflt.makeUnique(), AipsError); } // ------------------test copy ctor----------------------- // copy ctor with reference semantics COWPtr > copy(COW); // only const T functions may be used through the pointer. AlwaysAssert(copy->nelements() == 128, AipsError); // only const T functions may be used through the dereferenced object. AlwaysAssert(allEQ(*copy, array),AipsError); // return a reference to this instance. AlwaysAssert(allEQ(copy.ref(), array),AipsError); // fill this instance. The pointer must be dynamically allocated. Default // behavior is to delete the pointer when this instance's destructer is // called. "deleteIt = False" implies the pointer is being maintained by // another object,(i.e. this is a copy - do not delete.) The // Boolean "readOnly" argument forces the COWPtr to treat the templated // data as const. This allows non-const data operations be used to fill a // const acting version of COWPtr. Array *foo = new Array(IPosition(2,5,5)); *foo = 0.0; copy.set(foo, deleteIt, constant); // return a readable and writable reference to this instance. copy.rwRef().set(22.0); AlwaysAssert(allEQ(copy.rwRef(), 22.0f), AipsError); // returns False if this contains a non-null ptr. Otherwise, True. AlwaysAssert(copy.isNull() == False, AipsError); // make this a copy if more than one exist. if (copy.isReadOnly()){ AlwaysAssert(copy.makeUnique(), AipsError); } else { AlwaysAssert(!copy.makeUnique(), AipsError); } //assignment copy = COW; AlwaysAssert(allEQ(*copy, array), AipsError); if (!deleteIt) { delete foo; delete bar; delete foobar; delete fooAlso; delete fooAgain; } return True; } int main() { try { Array array(IPosition(3,2,4,16)); for (int i=0; i<2; i++) for (int j=0; j<4; j++) for (int k=0; k<16; k++) array(IPosition(3,i,j,k)) = i+j+k; // we have four permutations // Case 0: a const which controls the ptr. Array *ptr = new Array(array.copy()); AlwaysAssert(ptr, AipsError); AlwaysAssert(testFunc(ptr, array, True, True), AipsError); // Case 1: a non-const which controls the ptr. ptr = new Array(array.copy()); AlwaysAssert(ptr, AipsError); AlwaysAssert(testFunc(ptr, array, True, False), AipsError); // Case 2: a const which doesn't control the pointer ptr = new Array(array.copy()); AlwaysAssert(ptr, AipsError); AlwaysAssert(testFunc(ptr, array, False, True), AipsError); AlwaysAssert(ptr, AipsError); delete ptr; // Case 3: a non-const which doesn't control the pointer ptr = new Array(array.copy()); AlwaysAssert(ptr, AipsError); AlwaysAssert(testFunc(ptr, array, False, False), AipsError); AlwaysAssert(ptr, AipsError); delete ptr; cout << "OK" << endl; } catch (std::exception& x) { cerr << x.what() << endl; } return 0; } casacore-3.7.1/casa/Utilities/test/tCompare.cc000066400000000000000000000100751476623553700212510ustar00rootroot00000000000000//# tCompare.cc: Test program for the Compare classes //# Copyright (C) 2013 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include // This program tests the Compare classes. // It sorts some data in ascending and/or descending order. // The results are written to stdout. A script executing this program, // compares the output with a reference output file. // Test case-insensitive sort. void sort1 (Int option) { String arr[10]; arr[0] = "aaa"; arr[1] = "aaac"; arr[2] = "AAA"; arr[3] = "AaAb"; arr[4] = "AaA"; arr[5] = "AAAd"; arr[6] = "aaad"; arr[7] = "aaab"; arr[8] = "aaab2"; arr[9] = "aaab1"; std::shared_ptr cmp(new CompareNoCase()); Sort sort; sort.sortKey (arr, cmp, sizeof(String)); Vector inx; sort.sort (inx, 10, option); for (uInt i=0; i cmp(new CompareIntervalReal(2,1)); Sort sort; sort.sortKey (arr, cmp, sizeof(Double)); Vector inx; sort.sort (inx, 10, option); for (uInt i=0; i cmp(new CompareIntervalReal(3,0)); Sort sort; sort.sortKey (arr, cmp, sizeof(Double)); Vector inx; sort.sort (inx, 10, option); for (uInt i=0; i #include #include #include int main() { { CompositeNumber cn; uInt n; n= cn.nextLarger(41); cout << "Next larger composite number of 41 is " << n << endl; n= cn.nextLargerEven(41); cout << "Next larger even composite number of 41 is " << n << endl; n = cn.nextSmaller(41); cout << "Next smaller composite number of 41 is " << n << endl; n = cn.nextSmallerEven(41); cout << "Next smaller even composite number of 41 is " << n << endl; n = cn.nearest(41); cout << "The nearest composite number to 41 is " << n << endl; n = cn.nearestEven(41); cout << "The nearest even composite number to 41 is " << n << endl; n = cn.nextLarger(397); cout << "Next larger composite number of 397 is " << n << endl; n = cn.nextLargerEven(397); cout << "Next larger even composite number of 397 is " << n << endl; n = cn.nextSmallerEven(397); cout << "Next smaller even composite number of 397 is " << n << endl; n = cn.nextSmaller(397); cout << "Next smaller composite number of 397 is " << n << endl; n = cn.nearest(397); cout << "The nearest composite number to 397 is " << n << endl; n = cn.nearestEven(397); cout << "The nearest even composite number to 397 is " << n << endl; n = cn.nextLargerEven(9362); cout << "Next larger even composite number of 9362 is " << n << endl; n = cn.nextLarger(9362); cout << "Next larger composite number of 9362 is " << n << endl; n = cn.nextSmaller(9362); cout << "Next smaller composite number of 9362 is " << n << endl; n = cn.nearest(9362); cout << "The nearest composite number to 9362 is " << n << endl; n = cn.nearestEven(9362); cout << "The nearest even composite number to 9362 is " << n << endl; cout << "Is 40 composite? " << cn.isComposite(40) << endl; cout << "Is 41 composite? " << cn.isComposite(41) << endl; cout << "Is 128 composite? " << cn.isComposite(128) << endl; cout << "Is 129 composite? " << cn.isComposite(129) << endl; cout << "Is 1024 composite? " << cn.isComposite(1024) << endl; cout << "Is 1025 composite? " << cn.isComposite(1025) << endl; cout << "Is 1026 composite? " << cn.isComposite(1026) << endl; cout << "Is 1027 composite? " << cn.isComposite(1027) << endl; } { CompositeNumber cn(100); uInt n; n= cn.nextLarger(41); cout << "Next larger composite number of 41 is " << n << endl; n = cn.nextSmaller(41); cout << "Next smaller composite number of 41 is " << n << endl; n = cn.nearest(41); cout << "The nearest composite number to 41 is " << n << endl; n = cn.nextLarger(397); cout << "Next larger composite number of 397 is " << n << endl; n = cn.nextSmaller(397); cout << "Next smaller composite number of 397 is " << n << endl; n = cn.nearest(397); cout << "The nearest composite number to 397 is " << n << endl; cout << "Is 40 composite? " << cn.isComposite(40) << endl; cout << "Is 41 composite? " << cn.isComposite(41) << endl; cout << "Is 128 composite? " << cn.isComposite(128) << endl; cout << "Is 129 composite? " << cn.isComposite(129) << endl; cout << "Is 1024 composite? " << cn.isComposite(1024) << endl; cout << "Is 1025 composite? " << cn.isComposite(1025) << endl; cout << "Is 1026 composite? " << cn.isComposite(1026) << endl; cout << "Is 1027 composite? " << cn.isComposite(1027) << endl; } } casacore-3.7.1/casa/Utilities/test/tCopy.cc000066400000000000000000000267611476623553700206060ustar00rootroot00000000000000//# tCopy.cc: This program tests the functions in Copy.h //# Copyright (C) 1994,1995,1996,1998,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include // A random class to copy #include #include // This program tests all functions in Copy.h (objset, objcopy and objmove). // Especially objmove is tested in all kinds of circumstances // (overlapping, non-overlapping, strides). // // The tests are mainly done for a builtin data type, for which the // straightforward copy/move operations are inlined as memcpy/memmove. // To complete the tests, they are also done for data types Block // and void*. int main() { Int size=100; // should be a multiple of 4 Int* ia = new int[size]; objset(ia, 99, size); // Test setting values. Int i; for (i=0; i* ta = new Block[size]; Block set(3); set[0] = 1; set[1] = 2; set[2] = 3; objset(ta, set, size, 1); for (i=0; i < size; i++) AlwaysAssertExit(ta[i][0] == 1 && ta[i][1] == 2 && ta[i][2] == 3); set[0] = set[1] = set[2] = 7; objset(ta + 1, set, size/2, 2); for (i=0; i < size; i+=2) { AlwaysAssertExit(ta[i][0] == 1 && ta[i][1] == 2 && ta[i][2] == 3); AlwaysAssertExit(ta[i+1][0] == 7 && ta[i+1][1] == 7 && ta[i+1][2] == 7); } for (i=0; i* ta2 = new Block[size]; // Test objcopy for a non-builtin data type. objcopy(ta2, ta, size/2, 1, 2); objcopy(ta2+size/2, ta+1, size/2, 1, 2); for (i=0; i < size/2; i++) AlwaysAssertExit(ta2[i][0] == 2*i && ta2[i][1] == 2*i+size && ta2[i][2] == 2*i+2*size); for (i=size/2; i < size; i++) AlwaysAssertExit(ta2[i][0] == 2*(i-size/2)+1 && ta2[i][1] == 2*(i-size/2)+1+size && ta2[i][2] == 2*(i-size/2)+1+2*size); objcopy(ta, ta2, size); for (i=0; i < size/2; i++) AlwaysAssertExit(ta[i][0] == 2*i && ta[i][1] == 2*i+size && ta[i][2] == 2*i+2*size); for (i=size/2; i < size; i++) AlwaysAssertExit(ta[i][0] == 2*(i-size/2)+1 && ta[i][1] == 2*(i-size/2)+1+size && ta[i][2] == 2*(i-size/2)+1+2*size); // Test objmove for a non-builtin data type. for (i=0; i(0), size); objmove(va2, va, size); for (i=0; i and Block are used. // See if they compile and link well. PtrBlock cbl(10,static_cast(0)); PtrBlock cbl2(cbl); PtrBlock bl(10,static_cast(0)); PtrBlock bl2(bl); // PtrBlock is based on Block Block bvl(10,static_cast(0)); Block bvl2(bvl); delete [] ia; delete [] ia2; delete [] ta; delete [] ta2; delete [] va; delete [] va2; cout << "OK\n"; return 0; } casacore-3.7.1/casa/Utilities/test/tCountedPtr.cc000066400000000000000000000073651476623553700217620ustar00rootroot00000000000000//# tCountedPtr.cc: This program tests the Counted Pointer class //# Copyright (C) 1993,1994,1995,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include // class myobj is defined in tCountedPtr.h String prt(CountedPtr &obj) { return obj->name(); } void testDerived() { cout << "start testDerived" << endl; { CountedPtr v0 (new myobj("v0")); CountedPtr v1 (new myobj1("v1")); CountedPtr v2 (new myobj1("v2")); CountedPtr v3(v1); CountedPtr v4(v2); CountedPtr v5(dynamic_pointer_cast(v1)); cout << v0->name() << ' ' << v1->name() << ' ' << v2->name() << ' ' << v3->name() << ' ' << v4->name() << ' ' << v5->name() << endl; v0 = v1; cout << v0->name() << ' ' << v1->name() << ' ' << v2->name() << ' ' << v3->name() << ' ' << v4->name() << ' ' << v5->name() << endl; v0 = v2; cout << v0->name() << ' ' << v1->name() << ' ' << v2->name() << ' ' << v3->name() << ' ' << v4->name() << ' ' << v5->name() << endl; v2 = v5; cout << v0->name() << ' ' << v1->name() << ' ' << v2->name() << ' ' << v3->name() << ' ' << v4->name() << ' ' << v5->name() << endl; } cout << "end testDerived" << endl; } int main() { cout << ">>>" << endl; if (countedPtrShared()) { cout << "Using shared_ptr" << endl; } else { cout << "Not using shared_ptr" << endl; } cout << "<<<" << endl; CountedPtr var = new myobj("fred"); CountedPtr var2 = var; CountedPtr var3 = var; CountedPtr var4 (var); AlwaysAssertExit (var != 0); cout << (*var).name() << ".." << (*var2).name() << ".." << (*var3).name() << ".." << (*var4).name() << ".." << endl; var = new myobj("barney"); cout << (*var).name() << ".." << (*var2).name() << ".." << (*var3).name() << ".." << (*var4).name() << ".." << endl; // Check to see if the no-delete feature is honored // by the copy constructor and the assignment operator myobj * doNotDelete = new myobj ("Don't delete me!"); { CountedPtr p1 (doNotDelete, False); CountedPtr p2 (p1); CountedPtr p3; p3 = p1; } cout << "Now explicitly delete object" << endl; delete doNotDelete; // Test reset. var2.reset(new myobj("betty")); cout << var->name() << ".." << var2->name() << ".." << prt(var3) << ".." << var4->name() << ".." << endl; // Test with a derived class. testDerived(); return 0; } casacore-3.7.1/casa/Utilities/test/tCountedPtr.h000066400000000000000000000047231476623553700216170ustar00rootroot00000000000000//# tCountedPtr.cc: helps building test program tCountedPtr //# Copyright (C) 1993,1994,1995,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // Split off from the original tCountedPtr.cc // in order to satisfy the automated building of the test program. #ifndef CASA_TCOUNTEDPTR_H #define CASA_TCOUNTEDPTR_H //# Includes #include #include #include #include // // Example class for test of CountedPtr class // // // // // //# Classes you should understand before using this one. //
        269. CountedPtr // // // Make a class to test the templated class CountedPtr. // class myobj { protected: String store; public: myobj(const char *str) : store(str) { cout << "myobj(" << store << ") ctor" << endl; } virtual ~myobj() { cout << "myobj(" << store << ") dtor" << endl;} virtual String name() const { return store; } }; class myobj1 :public myobj { public: myobj1(const char *str) : myobj(str) { cout << " myobj1(" << store << ") ctor" << endl; } virtual ~myobj1() { cout << " myobj1(" << store << ") dtor" << endl;} virtual String name() const { return "myobj1_" + store; } }; #endif casacore-3.7.1/casa/Utilities/test/tCountedPtr.out000066400000000000000000000013251476623553700221720ustar00rootroot00000000000000>>> Using shared_ptr <<< myobj(fred) ctor fred..fred..fred..fred.. myobj(barney) ctor barney..fred..fred..fred.. myobj(Don't delete me!) ctor Now explicitly delete object myobj(Don't delete me!) dtor myobj(betty) ctor barney..betty..fred..fred.. start testDerived myobj(v0) ctor myobj(v1) ctor myobj1(v1) ctor myobj(v2) ctor myobj1(v2) ctor v0 myobj1_v1 myobj1_v2 myobj1_v1 myobj1_v2 myobj1_v1 myobj(v0) dtor myobj1_v1 myobj1_v1 myobj1_v2 myobj1_v1 myobj1_v2 myobj1_v1 myobj1_v2 myobj1_v1 myobj1_v2 myobj1_v1 myobj1_v2 myobj1_v1 myobj1_v2 myobj1_v1 myobj1_v1 myobj1_v1 myobj1_v2 myobj1_v1 myobj1(v1) dtor myobj(v1) dtor myobj1(v2) dtor myobj(v2) dtor end testDerived myobj(fred) dtor myobj(betty) dtor myobj(barney) dtor casacore-3.7.1/casa/Utilities/test/tDataType.cc000066400000000000000000000275541476623553700214100ustar00rootroot00000000000000//# tDataType.cc: This program tests the DataType related functions //# Copyright (C) 1995,1996,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include void simpleTests() { // First check the << operator { ostringstream formatter; formatter << TpBool; AlwaysAssertExit(String(formatter) == "Bool"); } { ostringstream formatter; formatter << TpChar; AlwaysAssertExit(formatter.str() == "Char"); } { ostringstream formatter; formatter << TpUChar; AlwaysAssertExit(formatter.str() == "uChar"); } { ostringstream formatter; formatter << TpShort; AlwaysAssertExit(formatter.str() == "Short"); } { ostringstream formatter; formatter << TpUShort; AlwaysAssertExit(formatter.str() == "uShort"); } { ostringstream formatter; formatter << TpInt; AlwaysAssertExit(formatter.str() == "Int"); } { ostringstream formatter; formatter << TpUInt; AlwaysAssertExit(formatter.str() == "uInt"); } { ostringstream formatter; formatter << TpInt64; AlwaysAssertExit(formatter.str() == "Int64"); } { ostringstream formatter; formatter << TpFloat; AlwaysAssertExit(formatter.str() == "float"); } { ostringstream formatter; formatter << TpDouble; AlwaysAssertExit(formatter.str() == "double"); } { ostringstream formatter; formatter << TpComplex; AlwaysAssertExit(formatter.str() == "Complex"); } { ostringstream formatter; formatter << TpDComplex; AlwaysAssertExit(formatter.str() == "DComplex"); } { ostringstream formatter; formatter << TpString; AlwaysAssertExit(formatter.str() == "String"); } { ostringstream formatter; formatter << TpTable; AlwaysAssertExit(formatter.str() == "Table"); } { ostringstream formatter; formatter << TpArrayBool; AlwaysAssertExit(formatter.str() == "Array"); } { ostringstream formatter; formatter << TpArrayChar; AlwaysAssertExit(formatter.str() == "Array"); } { ostringstream formatter; formatter << TpArrayUChar; AlwaysAssertExit(formatter.str() == "Array"); } { ostringstream formatter; formatter << TpArrayShort; AlwaysAssertExit(formatter.str() == "Array"); } { ostringstream formatter; formatter << TpArrayUShort; AlwaysAssertExit(formatter.str() == "Array"); } { ostringstream formatter; formatter << TpArrayInt; AlwaysAssertExit(formatter.str() == "Array"); } { ostringstream formatter; formatter << TpArrayUInt; AlwaysAssertExit(formatter.str() == "Array"); } { ostringstream formatter; formatter << TpArrayInt64; AlwaysAssertExit(formatter.str() == "Array"); } { ostringstream formatter; formatter << TpArrayFloat; AlwaysAssertExit(formatter.str() == "Array"); } { ostringstream formatter; formatter << TpArrayDouble; AlwaysAssertExit(formatter.str() == "Array"); } { ostringstream formatter; formatter << TpArrayComplex; AlwaysAssertExit(formatter.str() == "Array"); } { ostringstream formatter; formatter << TpArrayDComplex; AlwaysAssertExit(formatter.str() == "Array"); } { ostringstream formatter; formatter << TpArrayString; AlwaysAssertExit(formatter.str() == "Array"); } { ostringstream formatter; formatter << TpRecord; AlwaysAssertExit(formatter.str() == "Record"); } { ostringstream formatter; formatter << TpOther; AlwaysAssertExit(formatter.str() == "Other"); } { ostringstream formatter; formatter << TpQuantity; AlwaysAssertExit(formatter.str() == "Quantity"); } { ostringstream formatter; formatter << TpArrayQuantity; AlwaysAssertExit(formatter.str() == "Array"); } // Now check the whatType() functions class Goofy; AlwaysAssertExit( whatType() == TpOther ); AlwaysAssertExit( whatType() == TpBool ); AlwaysAssertExit( whatType() == TpChar ); AlwaysAssertExit( whatType() == TpUChar ); AlwaysAssertExit( whatType() == TpShort ); AlwaysAssertExit( whatType() == TpUShort ); AlwaysAssertExit( whatType() == TpInt ); AlwaysAssertExit( whatType() == TpUInt ); AlwaysAssertExit( whatType() == TpInt64 ); AlwaysAssertExit( whatType() == TpFloat ); AlwaysAssertExit( whatType() == TpDouble ); AlwaysAssertExit( whatType() == TpComplex ); AlwaysAssertExit( whatType() == TpDComplex ); AlwaysAssertExit( whatType() == TpString ); AlwaysAssertExit( whatType
      • () == TpTable ); AlwaysAssertExit( whatType>() == TpArrayBool ); AlwaysAssertExit( whatType>() == TpArrayChar ); AlwaysAssertExit( whatType>() == TpArrayUChar ); AlwaysAssertExit( whatType>() == TpArrayShort ); AlwaysAssertExit( whatType>() == TpArrayUShort ); AlwaysAssertExit( whatType>() == TpArrayInt ); AlwaysAssertExit( whatType>() == TpArrayUInt ); AlwaysAssertExit( whatType>() == TpArrayInt64 ); AlwaysAssertExit( whatType>() == TpArrayFloat ); AlwaysAssertExit( whatType>() == TpArrayDouble ); AlwaysAssertExit( whatType>() == TpArrayComplex ); AlwaysAssertExit( whatType>() == TpArrayDComplex ); AlwaysAssertExit( whatType>() == TpArrayString ); AlwaysAssertExit( whatType() == TpRecord ); AlwaysAssertExit( whatType() == TpQuantity ) ; AlwaysAssertExit( whatType>() == TpArrayQuantity ); AlwaysAssertExit (isScalar(TpBool) && isScalar(TpChar) && isScalar(TpUChar)&& isScalar(TpShort) && isScalar(TpUShort) && isScalar(TpInt) && isScalar(TpUInt) && isScalar(TpFloat) && isScalar(TpDouble) && isScalar(TpComplex) && isScalar(TpDComplex) && isScalar(TpString) && isScalar(TpQuantity) && isScalar(TpInt64) && !isScalar(TpTable) && !isScalar(TpRecord) && !isScalar(TpOther) && !isScalar(TpArrayBool) && !isScalar(TpArrayChar) && !isScalar(TpArrayUChar)&& !isScalar(TpArrayShort) && !isScalar(TpArrayUShort) && !isScalar(TpArrayInt) && !isScalar(TpArrayUInt) && !isScalar(TpArrayFloat) && !isScalar(TpArrayDouble) && !isScalar(TpArrayComplex) && !isScalar(TpArrayDComplex) && !isScalar(TpArrayString) && !isScalar(TpArrayQuantity) && !isScalar(TpArrayInt64)); AlwaysAssertExit (!isArray(TpBool) && !isArray(TpChar) && !isArray(TpUChar)&& !isArray(TpShort) && !isArray(TpUShort) && !isArray(TpInt) && !isArray(TpUInt) && !isArray(TpFloat) && !isArray(TpDouble) && !isArray(TpComplex) && !isArray(TpDComplex) && !isArray(TpString) && !isArray(TpTable) && !isArray(TpRecord) && !isArray(TpOther) && !isArray(TpQuantity) && !isArray(TpInt64) && isArray(TpArrayBool) && isArray(TpArrayChar) && isArray(TpArrayUChar)&& isArray(TpArrayShort) && isArray(TpArrayUShort) && isArray(TpArrayInt) && isArray(TpArrayUInt) && isArray(TpArrayFloat) && isArray(TpArrayDouble) && isArray(TpArrayComplex) && isArray(TpArrayDComplex) && isArray(TpArrayString) && isArray(TpArrayQuantity) && isArray(TpArrayInt64)); AlwaysAssertExit(asScalar(TpBool) == TpBool && asScalar(TpChar) == TpChar && asScalar(TpUChar) == TpUChar && asScalar(TpShort) == TpShort && asScalar(TpUShort) == TpUShort && asScalar(TpInt) == TpInt && asScalar(TpUInt) == TpUInt && asScalar(TpInt64) == TpInt64 && asScalar(TpFloat) == TpFloat && asScalar(TpDouble) == TpDouble && asScalar(TpComplex) == TpComplex && asScalar(TpDComplex) == TpDComplex && asScalar(TpString) == TpString && asScalar(TpQuantity) == TpQuantity && asScalar(TpArrayBool) == TpBool && asScalar(TpArrayChar) == TpChar && asScalar(TpArrayUChar) == TpUChar && asScalar(TpArrayShort) == TpShort && asScalar(TpArrayUShort) == TpUShort && asScalar(TpArrayInt) == TpInt && asScalar(TpArrayUInt) == TpUInt && asScalar(TpArrayInt64) == TpInt64 && asScalar(TpArrayFloat) == TpFloat && asScalar(TpArrayDouble) == TpDouble && asScalar(TpArrayComplex) == TpComplex && asScalar(TpArrayDComplex) == TpDComplex && asScalar(TpArrayString) == TpString && asScalar(TpArrayQuantity) == TpQuantity); AlwaysAssertExit(asArray(TpBool) == TpArrayBool && asArray(TpChar) == TpArrayChar && asArray(TpUChar) == TpArrayUChar && asArray(TpShort) == TpArrayShort && asArray(TpUShort) == TpArrayUShort && asArray(TpInt) == TpArrayInt && asArray(TpUInt) == TpArrayUInt && asArray(TpInt64) == TpArrayInt64 && asArray(TpFloat) == TpArrayFloat && asArray(TpDouble) == TpArrayDouble && asArray(TpComplex) == TpArrayComplex && asArray(TpDComplex) == TpArrayDComplex && asArray(TpString) == TpArrayString && asArray(TpQuantity) == TpArrayQuantity && asArray(TpArrayBool) == TpArrayBool && asArray(TpArrayChar) == TpArrayChar && asArray(TpArrayUChar) == TpArrayUChar && asArray(TpArrayShort) == TpArrayShort && asArray(TpArrayUShort) == TpArrayUShort && asArray(TpArrayInt) == TpArrayInt && asArray(TpArrayUInt) == TpArrayUInt && asArray(TpArrayInt64) == TpArrayInt64 && asArray(TpArrayFloat) == TpArrayFloat && asArray(TpArrayDouble) == TpArrayDouble && asArray(TpArrayComplex) == TpArrayComplex && asArray(TpArrayDComplex) == TpArrayDComplex && asArray(TpArrayString) == TpArrayString && asArray(TpArrayQuantity) == TpArrayQuantity); } // to be called using types for which an exception from asScalar is expected void excpAsScalar(DataType type) { Bool hadExcp = False; try { asScalar(type); } catch (std::exception& x) { hadExcp = True; } AlwaysAssert(hadExcp, AipsError); } void excpAsArray(DataType type) { Bool hadExcp = False; try { asArray(type); } catch (std::exception& x) { hadExcp = True; } AlwaysAssert(hadExcp, AipsError); } void excpTests() { excpAsScalar(TpTable); excpAsScalar(TpRecord); excpAsScalar(TpOther); excpAsArray(TpTable); excpAsArray(TpRecord); excpAsArray(TpOther); } int main() { try { simpleTests(); excpTests(); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/casa/Utilities/test/tDefaultValue.cc000066400000000000000000000041731476623553700222460ustar00rootroot00000000000000//# tDefaultValue.cc - tests the default value function //# Copyright (C) 1996,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include // a bogus test case of a specializtion for Strings static void defaultValue(String &val) { val = "defaultval"; } int main() { try { Int foo0; defaultValue(foo0); AlwaysAssert(foo0 == 0, AipsError); Float foo1; defaultValue(foo1); AlwaysAssert(foo1 == 0.0f, AipsError); Double foo2; defaultValue(foo2); AlwaysAssert(foo2 == 0.0f, AipsError); // test specializations String foo3; defaultValue(foo3); AlwaysAssert(foo3 == String("defaultval"), AipsError); } catch (std::exception& x) { cout << "\nCaught an exception: " << x.what() << endl; } cout << "OK" << endl; return 0; } casacore-3.7.1/casa/Utilities/test/tDynBuffer.cc000066400000000000000000000067411476623553700215540ustar00rootroot00000000000000//# tDynBuffer.cc: This program tests the DynBuffer class //# Copyright (C) 1993,1994,1995,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include // This program tests the DynBuffer class. // It allocates buffers, stores data in it and reads it back. // The results are written to stdout. A script executes this test program. int main () { uInt nrval,n; union { Char* ptr; uInt p; // to display a pointer }; DynBuffer buf; nrval = 100; while (nrval > 0) { n = buf.alloc (nrval,8,ptr); // allocate for nrval bytes cout << n << " " << p << endl; nrval -= n; } nrval = 1000; while (nrval > 0) { n = buf.alloc (nrval,8,ptr); // allocate for nrval bytes more cout << n << " " << p << endl; nrval -= n; } buf.nextstart(); while (buf.next (n,ptr)) { // loop through allocated buffers cout << "next " << n << " " << p << endl; } buf.allocstart(); // dismiss buffer contents nrval = 1000; while (nrval > 0) { n = buf.alloc (nrval,8,ptr); // reuse previous buffers cout << n << " " << p << endl; nrval -= n; } nrval = 101; while (nrval > 0) { n = buf.alloc (nrval,8,ptr); // reuse and allocate for 1 byte more cout << n << " " << p << endl; nrval -= n; } buf.nextstart(); while (buf.next (n,ptr)) { cout << "next " << n << " " << p << endl; } buf.remove(1); // remove buffers (keep 1) buf.allocstart(); nrval = 1000; while (nrval > 0) { n = buf.alloc (nrval,8,ptr); cout << n << " " << p << endl; nrval -= n; } nrval = 101; while (nrval > 0) { n = buf.alloc (nrval,8,ptr); cout << n << " " << p << endl; nrval -= n; } buf.nextstart(); while (buf.next (n,ptr)) { cout << "next " << n << " " << p << endl; } buf.remove(0); buf.allocstart(); nrval = 1000; while (nrval > 0) { n = buf.alloc (nrval,8,ptr); cout << n << " " << p << endl; nrval -= n; } nrval = 101; while (nrval > 0) { n = buf.alloc (nrval,8,ptr); cout << n << " " << p << endl; nrval -= n; } buf.nextstart(); while (buf.next (n,ptr)) { cout << "next " << n << " " << p << endl; } return 0; // exit with success status } casacore-3.7.1/casa/Utilities/test/tFallible.cc000066400000000000000000000044111476623553700213720ustar00rootroot00000000000000//# tFallible: Test Fallible class //# Copyright (C) 1994,1995,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #if !defined(AIPS_DEBUG) #define AIPS_DEBUG #endif #include #include #include #include #include void Foo(Int x) { AlwaysAssertExit(x == 5); } int main() { // None of the following should cause an exception Fallible fi(5); Foo(fi); AlwaysAssertExit(fi.value() == 5); Fallible fi2(10); fi = fi2; AlwaysAssertExit(fi.value() == 10); Fallible fi3(fi2); AlwaysAssertExit(fi3.value() == 10); AlwaysAssertExit(fi3.isValid() == True); Fallible fi4; AlwaysAssertExit(fi4.isValid() == False); Fallible fi5(fi4); // All of the following should cause an exception Bool caught = False; try { Foo(fi4); } catch (std::exception& x) { caught = True; } AlwaysAssertExit(caught); caught = False; try { fi5.value(); } catch (std::exception& x) { caught = True; } AlwaysAssertExit(caught); cout << "OK" << endl; return 0; } casacore-3.7.1/casa/Utilities/test/tGenSort.cc000066400000000000000000000221561476623553700212470ustar00rootroot00000000000000//# tGenSort.cc: This program tests the global templated sort routines //# Copyright (C) 1993,1994,1995,1996,1997,1999,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include void sortall (Int*, uInt, int, Sort::Order, Bool); uInt doSort (Vector& inx, const Int* arr, uInt nr, Sort::Order ord, int type) { inx.resize (nr); indgen(inx); if ((type & Sort::QuickSort) != 0) { return GenSortIndirect::quickSort (inx.data(), arr, nr, ord, type); } else if ((type & Sort::HeapSort) != 0) { return GenSortIndirect::heapSort (inx.data(), arr, nr, ord, type); } else if ((type & Sort::InsSort) != 0) { return GenSortIndirect::insSort (inx.data(), arr, nr, ord, type); } return genSort (inx, arr, nr, ord, type); } uInt doSort (Int* arr, uInt nr, Sort::Order ord, int type) { if ((type & Sort::QuickSort) != 0) { return GenSort::quickSort (arr, nr, ord, type); } else if ((type & Sort::HeapSort) != 0) { return GenSort::heapSort (arr, nr, ord, type); } else if ((type & Sort::InsSort) != 0) { return GenSort::insSort (arr, nr, ord, type); } return genSort (arr, nr, ord, type); } int main(int argc, const char* argv[]) { uInt nr=4000; int type=Sort::DefaultSort; Sort::Order ord = Sort::Ascending; if (argc > 1) { istringstream istr(argv[1]); istr >> nr; } if (argc > 2) { istringstream istr(argv[2]); istr >> type; } if (argc > 3) { ord = Sort::Descending; } if ((type & Sort::QuickSort) != 0) { cout << "quickSort"; } else if ((type & Sort::InsSort) != 0) { cout << "insSort "; } else if ((type & Sort::HeapSort) != 0) { cout << "heapSort "; } else if ((type & Sort::ParSort) != 0) { cout << "parSort "; } else { cout << "defSort "; } if (ord == Sort::Ascending) { cout << " Ascending"; }else{ cout << " Descending"; } // Outcomment the resulting number for assay when duplicates are // skipped for a random array. This number may differ from run to run. Bool showFlag = True; if ((type & Sort::NoDuplicates) != 0) { cout << " (no duplicates)"; showFlag = False; } cout << endl; Int* a1 = new Int[nr]; Int* a2 = new Int[nr]; Int* a3 = new Int[nr]; Int* a4 = new Int[nr]; Int* a5 = new Int[nr]; for (uInt i=0; i::quickSort (indx, data, nr, Sort::Ascending, 0); for (uInt i=0; i < nr; i++) { data[i] = i; } data[nr - 1] = -1; GenSort::quickSort (data, nr, Sort::Ascending, 0); delete [] indx; delete [] data; return 0; // exit with success status } void sortall (Int* arr, uInt nr, int type, Sort::Order ord, Bool showFlag) { if (nr <= 5000000) { // Do an indirect sort for 'smaller' arrays only. Vector inx(nr); Vector index(nr); indgen (index); // fill with 0,1,2,... Timer tim1; Int n1 = doSort (inx, arr, nr, ord, type); cout <<": Indirect / direct" << endl; if (!showFlag) { cout << ">>> Resulting number may vary" << endl; } cout << setw(8) << n1 << endl; if (!showFlag) { cout << "<<<" << endl; } cout << ">>> Indirect "; tim1.show(); cout << "<<<" << endl; if (ord == Sort::Ascending) { for (Int i=1; i arr[inx(i-1)]) { cout << "desc order error on index " << i << endl; break; } if (arr[inx(i)] == arr[inx(i-1)] && index(inx(i)) > index(inx(i-1))) { cout << "desc equal order error on index " << i << endl; break; } } } if ((type & Sort::NoDuplicates) != 0) { for (Int i=1; i>>" << endl; } cout << setw(8) << n << endl; if (!showFlag) { cout << "<<< Resulting number may vary" << endl; } cout << ">>> GenSort "; tim.show(); cout << "<<<" << endl; if (ord == Sort::Ascending) { for (Int i=1; i arr[i-1]) { cout << "desc order error on index " << i << endl; break; } } } if ((type & Sort::NoDuplicates) != 0) { for (Int i=1; i::kthLargest (cparr, n, n/2); cout << ">>> ind kthLar: "; tim.show(); cout << "<<<" << endl; uInt mid = n/2; if (ord == Sort::Descending) { mid = (n-1)/2; } if (cparr[kth] != arr[mid]) { cout << "ind kthLargest is " << kth << "; should be " << mid << endl; } } tim.mark(); Int kth = GenSort::kthLargest (cparr, n, n/2); cout << ">>> kthLar: "; tim.show(); cout << "<<<" << endl; uInt mid = n/2; if (ord == Sort::Descending) { mid = (n-1)/2; } if (kth != arr[mid]) { cout << "kthLargest is " << kth << "; should be " << arr[mid] << endl; } // Test STL algorithms. cout << ">>>" << endl; if ((type & Sort::NoDuplicates) != 0) { memcpy (cparr, arr, n*sizeof(Int)); } else { memcpy (cparr, cp2arr, n*sizeof(Int)); } tim.mark(); std::nth_element (cparr, cparr+n/2, cparr+n); tim.show ("STL-nth "); memcpy (cparr, cp2arr, nr*sizeof(Int)); tim.mark(); std::sort (cp2arr, cp2arr+nr); tim.show ("STL-sort "); tim.mark(); std::stable_sort (cparr, cparr+nr); tim.show ("STL-stable "); cout << "<<<" << endl; delete [] cparr; delete [] cp2arr; } /* Test remarks on MacBook OS-X Tiger g++-4.01. -O2: 1. casa's quicksort and kthLargest are as fast as STL sort and nth_element. 2. median could use min(2nd partition) to determine element (n+1)/2. */ casacore-3.7.1/casa/Utilities/test/tGenSort.out000066400000000000000000000404101476623553700214620ustar00rootroot00000000000000heapSort Ascending ordered array : Indirect / direct 2500 >>> Timing: 0.02 real 0.03 user 0 system <<< 2500 >>> Timing: 0.02 real 0.02 user 0 system <<< reversed array: Indirect / direct 2500 >>> Timing: 0.02 real 0.02 user 0 system <<< 2500 >>> Timing: 0.02 real 0.02 user 0 system <<< random array : Indirect / direct 2500 >>> Timing: 0.03 real 0.03 user 0 system <<< 2500 >>> Timing: 0.02 real 0.02 user 0 system <<< equal array : Indirect / direct 2500 >>> Timing: 0.02 real 0.02 user 0 system <<< 2500 >>> Timing: 0.02 real 0.02 user 0 system <<< 10 diff. array: Indirect / direct 2500 >>> Timing: 0.03 real 0.03 user 0 system <<< 2500 >>> Timing: 0.01 real 0.01 user 0 system <<< insSort Ascending ordered array : Indirect / direct 2500 >>> Timing: 0.01 real 0.01 user 0 system <<< 2500 >>> Timing: 0 real 0 user 0 system <<< reversed array: Indirect / direct 2500 >>> Timing: 1.38 real 1.29 user 0 system <<< 2500 >>> Timing: 0.72 real 0.69 user 0 system <<< random array : Indirect / direct 2500 >>> Timing: 1.19 real 0.66 user 0 system <<< 2500 >>> Timing: 0.77 real 0.34 user 0 system <<< equal array : Indirect / direct 2500 >>> Timing: 0 real 0 user 0 system <<< 2500 >>> Timing: 0 real 0 user 0 system <<< 10 diff. array: Indirect / direct 2500 >>> Timing: 1.05 real 0.6 user 0 system <<< 2500 >>> Timing: 0.6 real 0.33 user 0.01 system <<< quickSort Ascending ordered array : Indirect / direct 2500 >>> Timing: 0.01 real 0.02 user 0 system <<< 2500 >>> Timing: 0 real 0 user 0 system <<< reversed array: Indirect / direct 2500 >>> Timing: 0.01 real 0.01 user 0 system <<< 2500 >>> Timing: 0 real 0 user 0 system <<< random array : Indirect / direct 2500 >>> Timing: 0.01 real 0.01 user 0 system <<< 2500 >>> Timing: 0.01 real 0.01 user 0 system <<< equal array : Indirect / direct 2500 >>> Timing: 0.01 real 0.01 user 0 system <<< 2500 >>> Timing: 0.01 real 0.01 user 0 system <<< 10 diff. array: Indirect / direct 2500 >>> Timing: 0.15 real 0.03 user 0 system <<< 2500 >>> Timing: 0 real 0 user 0 system <<< parSort Ascending ordered array : Indirect / direct 2500 >>> Timing: 0.01 real 0.02 user 0 system <<< 2500 >>> Timing: 0 real 0 user 0 system <<< reversed array: Indirect / direct 2500 >>> Timing: 0.01 real 0.01 user 0 system <<< 2500 >>> Timing: 0 real 0 user 0 system <<< random array : Indirect / direct 2500 >>> Timing: 0.01 real 0.01 user 0 system <<< 2500 >>> Timing: 0.01 real 0.01 user 0 system <<< equal array : Indirect / direct 2500 >>> Timing: 0.01 real 0.01 user 0 system <<< 2500 >>> Timing: 0.01 real 0.01 user 0 system <<< 10 diff. array: Indirect / direct 2500 >>> Timing: 0.15 real 0.03 user 0 system <<< 2500 >>> Timing: 0 real 0 user 0 system <<< heapSort Ascending (no duplicates) ordered array : Indirect / direct 2500 >>> Timing: 0.02 real 0.02 user 0.01 system <<< 2500 >>> Timing: 0.02 real 0.02 user 0 system <<< reversed array: Indirect / direct 2500 >>> Timing: 0.03 real 0.03 user 0 system <<< 2500 >>> Timing: 0.26 real 0.03 user 0 system <<< random array : Indirect / direct >>> Resulting number may vary 2411 <<< >>> Timing: 0.03 real 0.03 user 0 system <<< >>> 2411 >>> Resulting number may vary >>> Timing: 0.02 real 0.02 user 0 system <<< equal array : Indirect / direct 1 >>> Timing: 0.18 real 0.04 user 0 system <<< 1 >>> Timing: 0.01 real 0.01 user 0 system <<< 10 diff. array: Indirect / direct 10 >>> Timing: 0.03 real 0.02 user 0 system <<< 10 >>> Timing: 0.08 real 0.02 user 0 system <<< insSort Ascending (no duplicates) ordered array : Indirect / direct 2500 >>> Timing: 0 real 0 user 0.01 system <<< 2500 >>> Timing: 0 real 0 user 0 system <<< reversed array: Indirect / direct 2500 >>> Timing: 3.24 real 1.56 user 0 system <<< 2500 >>> Timing: 3.31 real 1.27 user 0 system <<< random array : Indirect / direct >>> Resulting number may vary 2411 <<< >>> Timing: 1.49 real 0.77 user 0 system <<< >>> 2411 >>> Resulting number may vary >>> Timing: 1.1 real 0.61 user 0 system <<< equal array : Indirect / direct 1 >>> Timing: 0 real 0 user 0 system <<< 1 >>> Timing: 0.01 real 0.01 user 0 system <<< 10 diff. array: Indirect / direct 10 >>> Timing: 0.01 real 0.01 user 0 system <<< 10 >>> Timing: 0 real 0 user 0 system <<< quickSort Ascending (no duplicates) ordered array : Indirect / direct 2500 >>> Timing: 0.1 real 0.01 user 0.01 system <<< 2500 >>> Timing: 0 real 0 user 0 system <<< reversed array: Indirect / direct 2500 >>> Timing: 0.01 real 0.01 user 0 system <<< 2500 >>> Timing: 0 real 0 user 0 system <<< random array : Indirect / direct >>> Resulting number may vary 2411 <<< >>> Timing: 0.13 real 0.02 user 0 system <<< >>> 2411 >>> Resulting number may vary >>> Timing: 0.01 real 0.01 user 0 system <<< equal array : Indirect / direct 1 >>> Timing: 0.01 real 0.01 user 0 system <<< 1 >>> Timing: 0 real 0 user 0 system <<< 10 diff. array: Indirect / direct 10 >>> Timing: 0.02 real 0.02 user 0 system <<< 10 >>> Timing: 0 real 0 user 0 system <<< parSort Ascending (no duplicates) ordered array : Indirect / direct 2500 >>> Timing: 0.1 real 0.01 user 0.01 system <<< 2500 >>> Timing: 0 real 0 user 0 system <<< reversed array: Indirect / direct 2500 >>> Timing: 0.01 real 0.01 user 0 system <<< 2500 >>> Timing: 0 real 0 user 0 system <<< random array : Indirect / direct >>> Resulting number may vary 2411 <<< >>> Timing: 0.13 real 0.02 user 0 system <<< >>> 2411 >>> Resulting number may vary >>> Timing: 0.01 real 0.01 user 0 system <<< equal array : Indirect / direct 1 >>> Timing: 0.01 real 0.01 user 0 system <<< 1 >>> Timing: 0 real 0 user 0 system <<< 10 diff. array: Indirect / direct 10 >>> Timing: 0.02 real 0.02 user 0 system <<< 10 >>> Timing: 0 real 0 user 0 system <<< heapSort Descending ordered array : Indirect / direct 2500 >>> Timing: 0.02 real 0.03 user 0 system <<< 2500 >>> Timing: 0.02 real 0.02 user 0 system <<< reversed array: Indirect / direct 2500 >>> Timing: 0.02 real 0.02 user 0 system <<< 2500 >>> Timing: 0.01 real 0.01 user 0 system <<< random array : Indirect / direct 2500 >>> Timing: 0.02 real 0.02 user 0 system <<< 2500 >>> Timing: 0.01 real 0.01 user 0 system <<< equal array : Indirect / direct 2500 >>> Timing: 0.03 real 0.03 user 0 system <<< 2500 >>> Timing: 0.01 real 0.01 user 0 system <<< 10 diff. array: Indirect / direct 2500 >>> Timing: 0.02 real 0.02 user 0 system <<< 2500 >>> Timing: 0.02 real 0.02 user 0 system <<< insSort Descending ordered array : Indirect / direct 2500 >>> Timing: 3.18 real 1.32 user 0 system <<< 2500 >>> Timing: 1.59 real 0.71 user 0 system <<< reversed array: Indirect / direct 2500 >>> Timing: 0 real 0 user 0 system <<< 2500 >>> Timing: 0 real 0 user 0 system <<< random array : Indirect / direct 2500 >>> Timing: 1.44 real 0.64 user 0 system <<< 2500 >>> Timing: 0.93 real 0.35 user 0 system <<< equal array : Indirect / direct 2500 >>> Timing: 0 real 0 user 0 system <<< 2500 >>> Timing: 0 real 0 user 0 system <<< 10 diff. array: Indirect / direct 2500 >>> Timing: 0.9 real 0.58 user 0 system <<< 2500 >>> Timing: 0.31 real 0.31 user 0 system <<< quickSort Descending ordered array : Indirect / direct 2500 >>> Timing: 0.01 real 0.01 user 0 system <<< 2500 >>> Timing: 0 real 0 user 0 system <<< reversed array: Indirect / direct 2500 >>> Timing: 0 real 0 user 0 system <<< 2500 >>> Timing: 0 real 0 user 0 system <<< random array : Indirect / direct 2500 >>> Timing: 0.01 real 0.01 user 0 system <<< 2500 >>> Timing: 0.01 real 0.01 user 0 system <<< equal array : Indirect / direct 2500 >>> Timing: 0.01 real 0.01 user 0 system <<< 2500 >>> Timing: 0 real 0 user 0 system <<< 10 diff. array: Indirect / direct 2500 >>> Timing: 0.01 real 0.01 user 0 system <<< 2500 >>> Timing: 0.01 real 0.01 user 0 system <<< parSort Descending ordered array : Indirect / direct 2500 >>> Timing: 0.01 real 0.01 user 0 system <<< 2500 >>> Timing: 0 real 0 user 0 system <<< reversed array: Indirect / direct 2500 >>> Timing: 0 real 0 user 0 system <<< 2500 >>> Timing: 0 real 0 user 0 system <<< random array : Indirect / direct 2500 >>> Timing: 0.01 real 0.01 user 0 system <<< 2500 >>> Timing: 0.01 real 0.01 user 0 system <<< equal array : Indirect / direct 2500 >>> Timing: 0.01 real 0.01 user 0 system <<< 2500 >>> Timing: 0 real 0 user 0 system <<< 10 diff. array: Indirect / direct 2500 >>> Timing: 0.01 real 0.01 user 0 system <<< 2500 >>> Timing: 0.01 real 0.01 user 0 system <<< heapSort Descending (no duplicates) ordered array : Indirect / direct 2500 >>> Timing: 0.03 real 0.03 user 0.01 system <<< 2500 >>> Timing: 0.02 real 0.02 user 0 system <<< reversed array: Indirect / direct 2500 >>> Timing: 0.03 real 0.03 user 0 system <<< 2500 >>> Timing: 0.02 real 0.02 user 0 system <<< random array : Indirect / direct >>> Resulting number may vary 2411 <<< >>> Timing: 0.03 real 0.03 user 0 system <<< >>> 2411 >>> Resulting number may vary >>> Timing: 0.02 real 0.02 user 0 system <<< equal array : Indirect / direct 1 >>> Timing: 0.03 real 0.03 user 0 system <<< 1 >>> Timing: 0.01 real 0.01 user 0 system <<< 10 diff. array: Indirect / direct 10 >>> Timing: 0.02 real 0.02 user 0 system <<< 10 >>> Timing: 0.02 real 0.02 user 0 system <<< insSort Descending (no duplicates) ordered array : Indirect / direct 2500 >>> Timing: 1.54 real 1.54 user 0 system <<< 2500 >>> Timing: 1.29 real 1.21 user 0 system <<< reversed array: Indirect / direct 2500 >>> Timing: 0 real 0 user 0 system <<< 2500 >>> Timing: 0 real 0 user 0 system <<< random array : Indirect / direct >>> Resulting number may vary 2411 <<< >>> Timing: 0.73 real 0.73 user 0 system <<< >>> 2411 >>> Resulting number may vary >>> Timing: 0.57 real 0.57 user 0 system <<< equal array : Indirect / direct 1 >>> Timing: 0 real 0 user 0 system <<< 1 >>> Timing: 0 real 0 user 0 system <<< 10 diff. array: Indirect / direct 10 >>> Timing: 0 real 0 user 0 system <<< 10 >>> Timing: 0.01 real 0.01 user 0 system <<< quickSort Descending (no duplicates) ordered array : Indirect / direct 2500 >>> Timing: 0.01 real 0.02 user 0 system <<< 2500 >>> Timing: 0.01 real 0.01 user 0 system <<< reversed array: Indirect / direct 2500 >>> Timing: 0.01 real 0.01 user 0 system <<< 2500 >>> Timing: 0 real 0 user 0 system <<< random array : Indirect / direct >>> Resulting number may vary 2411 <<< >>> Timing: 0.02 real 0.02 user 0 system <<< >>> 2411 >>> Resulting number may vary >>> Timing: 0 real 0 user 0 system <<< equal array : Indirect / direct 1 >>> Timing: 0.01 real 0.01 user 0 system <<< 1 >>> Timing: 0.01 real 0.01 user 0 system <<< 10 diff. array: Indirect / direct 10 >>> Timing: 0.01 real 0.02 user 0 system <<< 10 >>> Timing: 0 real 0 user 0 system <<< parSort Descending (no duplicates) ordered array : Indirect / direct 2500 >>> Timing: 0.01 real 0.02 user 0 system <<< 2500 >>> Timing: 0.01 real 0.01 user 0 system <<< reversed array: Indirect / direct 2500 >>> Timing: 0.01 real 0.01 user 0 system <<< 2500 >>> Timing: 0 real 0 user 0 system <<< random array : Indirect / direct >>> Resulting number may vary 2411 <<< >>> Timing: 0.02 real 0.02 user 0 system <<< >>> 2411 >>> Resulting number may vary >>> Timing: 0 real 0 user 0 system <<< equal array : Indirect / direct 1 >>> Timing: 0.01 real 0.01 user 0 system <<< 1 >>> Timing: 0.01 real 0.01 user 0 system <<< 10 diff. array: Indirect / direct 10 >>> Timing: 0.01 real 0.02 user 0 system <<< 10 >>> Timing: 0 real 0 user 0 system <<< casacore-3.7.1/casa/Utilities/test/tGenSort.run000066400000000000000000000015041476623553700214600ustar00rootroot00000000000000#!/bin/sh #----------------------------------------------------------------------------- # Script to test tGenSort in various ways. #============================================================================= $casa_checktool ./tGenSort 2500 1 $casa_checktool ./tGenSort 2500 2 $casa_checktool ./tGenSort 2500 4 $casa_checktool ./tGenSort 2500 8 $casa_checktool ./tGenSort 2500 17 $casa_checktool ./tGenSort 2500 18 $casa_checktool ./tGenSort 2500 20 $casa_checktool ./tGenSort 2500 24 $casa_checktool ./tGenSort 2500 1 desc $casa_checktool ./tGenSort 2500 2 desc $casa_checktool ./tGenSort 2500 4 desc $casa_checktool ./tGenSort 2500 8 desc $casa_checktool ./tGenSort 2500 17 desc $casa_checktool ./tGenSort 2500 18 desc $casa_checktool ./tGenSort 2500 20 desc $casa_checktool ./tGenSort 2500 24 desc casacore-3.7.1/casa/Utilities/test/tLinearSearch.cc000066400000000000000000000140621476623553700222230ustar00rootroot00000000000000//# tLinearSearch.cc: This program tests the linear search functions //# Copyright (C) 1997,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #if !defined(AIPS_DEBUG) #define AIPS_DEBUG #endif #if !defined(AIPS_ARRAY_INDEX_CHECK) #define AIPS_ARRAY_INDEX_CHECK #endif #include #include #include #include #include #include int main() { Bool found; { IPosition ip1(1, 5); AlwaysAssertExit(linearSearch(found, ip1, 1, ip1.nelements()) == -1 && !found); AlwaysAssertExit(linearSearch1(ip1, 5) == 0); AlwaysAssertExit(linearSearch1(ip1, 10) == -1); AlwaysAssertExit(linearSearch(found, ip1, 5, 0u) == -1 && !found); } { IPosition ip1(3, 1, 5, 9); AlwaysAssertExit(linearSearch(found, ip1, 0, ip1.nelements()) == -1 && !found); AlwaysAssertExit(linearSearch(found, ip1, 1, ip1.nelements()) == 0 && found); AlwaysAssertExit(linearSearch(found, ip1, 3, ip1.nelements()) == -1 && !found); AlwaysAssertExit(linearSearch(found, ip1, 5, ip1.nelements()) == 1 && found); AlwaysAssertExit(linearSearch(found, ip1, 7, ip1.nelements()) == -1 && !found); AlwaysAssertExit(linearSearch(found, ip1, 9, ip1.nelements()) == 2 && found); AlwaysAssertExit(linearSearch(found, ip1, 10, ip1.nelements()) == -1 && !found); } { IPosition ip1(3, 9, 5, 1); AlwaysAssertExit(linearSearch(found, ip1, 0, ip1.nelements()) == -1 && !found); AlwaysAssertExit(linearSearch(found, ip1, 1, ip1.nelements()) == 2 && found); AlwaysAssertExit(linearSearch(found, ip1, 3, ip1.nelements()) == -1 && !found); AlwaysAssertExit(linearSearch(found, ip1, 5, ip1.nelements()) == 1 && found); AlwaysAssertExit(linearSearch(found, ip1, 7, ip1.nelements()) == -1 && !found); AlwaysAssertExit(linearSearch(found, ip1, 9, ip1.nelements()) == 0 && found); AlwaysAssertExit(linearSearch(found, ip1, 10, ip1.nelements()) == -1 && !found); } { int ia[4]; // int ia[] = {22,9,5,1}; isn't available on all compilers ia[0] = 22; ia[1] = 9; ia[2] = 5; ia[3] = 1; int *ip = &ia[0]; AlwaysAssertExit((linearSearchBrackets(found, ip, 55, 4u) == -1) && !found); AlwaysAssertExit((linearSearchBrackets(found, ip, 22, 4u) == 0) && found); AlwaysAssertExit((linearSearchBrackets(found, ip, 11, 4u) == -1) && !found); AlwaysAssertExit((linearSearchBrackets(found, ip, 9, 4u) == 1) && found); AlwaysAssertExit((linearSearchBrackets(found, ip, 7, 4u) == -1) && !found); AlwaysAssertExit((linearSearchBrackets(found, ip, 5, 4u) == 2) && found); AlwaysAssertExit((linearSearchBrackets(found, ip, 3, 4u) == -1) && !found); AlwaysAssertExit((linearSearchBrackets(found, ip, 1, 4u) == 3) && found); AlwaysAssertExit((linearSearchBrackets(found, ip, -99, 4u) == -1) && !found); AlwaysAssertExit((linearSearchBrackets(found, ip, -99, 0u) == -1) && !found); } { int ia[4]; ia[0] = 1; ia[1] = 5; ia[2] = 9; ia[3] = 22; int *ip = &ia[0]; AlwaysAssertExit((linearSearchBrackets(found, ip, 55, 4u) == -1) && !found); AlwaysAssertExit((linearSearchBrackets(found, ip, 22, 4u) == 3) && found); AlwaysAssertExit((linearSearchBrackets(found, ip, 11, 4u) == -1) && !found); AlwaysAssertExit((linearSearchBrackets(found, ip, 9, 4u) == 2) && found); AlwaysAssertExit((linearSearchBrackets(found, ip, 7, 4u) == -1) && !found); AlwaysAssertExit((linearSearchBrackets(found, ip, 5, 4u) == 1) && found); AlwaysAssertExit((linearSearchBrackets(found, ip, 3, 4u) == -1) && !found); AlwaysAssertExit((linearSearchBrackets(found, ip, 1, 4u) == 0) && found); AlwaysAssertExit((linearSearchBrackets(found, ip, -99, 4u) == -1) && !found); } { int ia[4]; ia[0] = 1; ia[1] = 5; ia[2] = 9; ia[3] = 22; int *ip = &ia[0]; AlwaysAssertExit((linearSearchBrackets(found, ip, 55, 2u, 2) == -1) && !found); AlwaysAssertExit((linearSearchBrackets(found, ip, 22, 2u, 2) == 3) && found); AlwaysAssertExit((linearSearchBrackets(found, ip, 11, 2u, 2) == -1) && !found); AlwaysAssertExit((linearSearchBrackets(found, ip, 9, 2u, 2) == 2) && found); AlwaysAssertExit((linearSearchBrackets(found, ip, 7, 2u, 2) == -1) && !found); AlwaysAssertExit((linearSearchBrackets(found, ip, 5, 2u, 2) == -1) && !found); AlwaysAssertExit((linearSearchBrackets(found, ip, 3, 2u, 2) == -1) && !found); AlwaysAssertExit((linearSearchBrackets(found, ip, 1, 2u, 2) == -1) && !found); AlwaysAssertExit((linearSearchBrackets(found, ip, -99, 2u, 2) == -1) && !found); } { static int ia[4]; // all 0 by language rules int *ip = &ia[0]; AlwaysAssertExit((linearSearchBrackets(found, ip, 0, 4u) == 0) && found); AlwaysAssertExit((linearSearchBrackets(found, ip, -1, 4u) == -1) && !found); AlwaysAssertExit((linearSearchBrackets(found, ip, +1, 4u) == -1) && !found); } cout << "OK\n"; return 0; } casacore-3.7.1/casa/Utilities/test/tPrecision.cc000066400000000000000000000065361476623553700216250ustar00rootroot00000000000000//# tRegex.cc: Test program for the Regex class //# Copyright (C) 1993,1994,1995,1996,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include void testit( const Vector& x, const Vector& y, const uInt expectedPrecision ) { std::ostringstream testStream; testStream << "x = " << x[0] << " +/- " << x[1] << ", y "; if (y.size() == 0) { testStream << "nonexistant, "; } else { testStream << y[0] << " +/- " << y[1] << ", "; } testStream << "results in a precision of " << expectedPrecision; cout << "*** "<< testStream.str() << " ***" << endl; uInt precision = precisionForValueErrorPairs(x, y); cout << "prec="< x(2, 0); Vector y; uInt expected = 3; testit(x, y, expected); x[0] = 5; x[1] = 0; expected = 2; testit(x, y, expected); x[0] = 5.1; x[1] = 0; expected = 2; testit(x, y, expected); x[0] = -5.1; x[1] = 0; expected = 2; testit(x, y, expected); x[0] = 12.25; x[1] = 0; expected = 1; testit(x, y, expected); x[0] = -12.25; x[1] = 0; expected = 1; testit(x, y, expected); x[0] = 12.25; x[1] = 0.003; expected = 4; testit(x, y, expected); x[0] = 12.25; x[1] = 0.009; expected = 4; testit(x, y, expected); x[0] = 12.25; x[1] = 9; expected = 1; testit(x, y, expected); y.resize(2); x[0] = 12.25; x[1] = 9; y[0] = 12.25; y[1] = 0.009; expected = 4; testit(x, y, expected); y.resize(2); x[0] = 1200.25; x[1] = 900; y[0] = 1200.25; y[1] = 90; expected = 0; testit(x, y, expected); } catch (std::exception& x) { cout << x.what() << endl; return 1; } return 0; // exit with success status } casacore-3.7.1/casa/Utilities/test/tRegex.cc000066400000000000000000000170201476623553700207320ustar00rootroot00000000000000//# tRegex.cc: Test program for the Regex class //# Copyright (C) 1993,1994,1995,1996,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include //# Forward Declarations // Test program for the Regex class // First do some simple Regex things. void testBasic() { // Do some basic tests for a simple regex. Regex exp("a?bcd(bcdcdd)?"); AlwaysAssertExit (exp.match ("bdc",3) == String::npos); AlwaysAssertExit (exp.match ("abcd",3) == String::npos); AlwaysAssertExit (exp.match ("abcd",4) == 4); AlwaysAssertExit (exp.match ("abcdbcdcdd",10) == 10); AlwaysAssertExit (exp.match ("abcdbcdcdd",10) == 10); AlwaysAssertExit (exp.match ("bcdxxx", 6) == 3); AlwaysAssertExit (! exp.fullMatch ("bdc",3)); AlwaysAssertExit (! exp.fullMatch ("abcd",3)); AlwaysAssertExit (exp.fullMatch ("abcd",4)); AlwaysAssertExit (exp.fullMatch ("abcdbcdcdd",10)); AlwaysAssertExit (! exp.fullMatch ("bcdxxx", 6)); // Test with a given start position (from start and from end). // Test valid start positions. AlwaysAssertExit (exp.match ("abcde",5,0) == 4); AlwaysAssertExit (exp.match ("abcde",5,1) == 3); AlwaysAssertExit (exp.match ("abcde",5,2) == String::npos); AlwaysAssertExit (exp.match ("abcde",5,-5) == 4); AlwaysAssertExit (exp.match ("abcde",5,-4) == 3); AlwaysAssertExit (exp.match ("abcde",5,-3) == String::npos); // Test invalid start positions. AlwaysAssertExit (exp.match ("abcde",5,-6) == String::npos); AlwaysAssertExit (exp.match ("abcde",5,5) == String::npos); // Test some predefined regex-es. AlwaysAssertExit (RXalpha.match ("bcd",0) == String::npos); AlwaysAssertExit (RXalpha.match ("bcd",1) == 1); AlwaysAssertExit (RXalpha.match ("bcd",2) == 2); AlwaysAssertExit (RXalpha.match ("bcd",3) == 3); AlwaysAssertExit (RXalpha.match ("bcd",4) == 3); // Also test the String's matches function for a regex. AlwaysAssertExit (String("1").matches(RXdouble)); AlwaysAssertExit (String("-1").matches(RXdouble)); AlwaysAssertExit (String("+1").matches(RXdouble)); AlwaysAssertExit (String("1.").matches(RXdouble)); AlwaysAssertExit (String("1.1").matches(RXdouble)); AlwaysAssertExit (String(".1").matches(RXdouble)); AlwaysAssertExit (String("1.e1").matches(RXdouble)); AlwaysAssertExit (String("1.1e+1").matches(RXdouble)); AlwaysAssertExit (String(".1E-1").matches(RXdouble)); AlwaysAssertExit (! String(".1.e-1").matches(RXdouble)); // Some more basic tests using the copy constructor. Regex exp2(exp); AlwaysAssertExit (exp2.match ("abcdbcdcdd",10) == 10); AlwaysAssertExit (String("abcdbcdcdd").matches(exp2)); AlwaysAssertExit (! String("abcdb").matches(exp2)); AlwaysAssertExit (String("abcd").matches(exp2)); AlwaysAssertExit (String("bcd").matches(exp2)); AlwaysAssertExit (exp2.regexp() == "a?bcd(bcdcdd)?"); // The same using the assignment operator. Regex exp3("any"); exp3 = exp2; AlwaysAssertExit (exp3.match ("abcdbcdcdd",10) == 10); AlwaysAssertExit (String("abcdbcdcdd").matches(exp3)); AlwaysAssertExit (! String("abcdb").matches(exp3)); AlwaysAssertExit (String("abcd").matches(exp3)); AlwaysAssertExit (String("bcd").matches(exp3)); AlwaysAssertExit (exp3.regexp() == "a?bcd(bcdcdd)?"); // Arbitrary strings match. Regex exp5(".+"); AlwaysAssertExit (exp5.match ("", 0) == String::npos); AlwaysAssertExit (exp5.match ("", 1) == 1); AlwaysAssertExit (exp5.match ("a", 1) == 1); AlwaysAssertExit (exp5.match ("0123456789", 10) == 10); AlwaysAssertExit (exp5.match ("a", 2) == 2); AlwaysAssertExit (exp5.match ("\0\0", 2) == 2); // Check that an empty string matches .* but not .+ AlwaysAssertExit (Regex(".*").match ("", 0) == 0); AlwaysAssertExit (Regex(".+").match ("", 0) == String::npos); AlwaysAssertExit (Regex(".*").fullMatch ("", 0)); AlwaysAssertExit (! Regex(".+").fullMatch ("", 0)); cout << "end of testBasic" << endl; } // Test the output operator. void testIO() { Regex exp5("a?bcd(bcdcdd)?"); Regex exp2(".+"); std::ostringstream oss; oss << exp5; AlwaysAssertExit (String(oss.str()) == exp5.regexp()); cout << "end of testIO" << endl; } void testSearch() { Int matchlen; Regex exp1("abc"); Regex exp2("(abc)+"); AlwaysAssertExit (exp1.search("12abc345", 8, matchlen) == 2); AlwaysAssertExit (matchlen == 3); AlwaysAssertExit (exp1.search("abcabc345", 9, matchlen) == 0); AlwaysAssertExit (matchlen == 3); AlwaysAssertExit (exp1.search("ab345", 5, matchlen) == String::npos); AlwaysAssertExit (exp2.search("12abc345", 8, matchlen) == 2); AlwaysAssertExit (matchlen == 3); AlwaysAssertExit (exp2.search("abcabc345", 9, matchlen) == 0); AlwaysAssertExit (matchlen == 6); AlwaysAssertExit (exp2.search("ab345", 5, matchlen) == String::npos); // Test search backwards. AlwaysAssertExit (exp1.searchBack("12abc345", 8, matchlen, 0) == 2); AlwaysAssertExit (matchlen == 3); AlwaysAssertExit (exp1.searchBack("12abcabc345", 8, matchlen, 0) == 5); AlwaysAssertExit (matchlen == 3); AlwaysAssertExit (exp2.searchBack("12abc345", 8, matchlen, 0) == 2); AlwaysAssertExit (matchlen == 3); AlwaysAssertExit (exp2.searchBack("12abcabc345", 8, matchlen, 0) == 5); AlwaysAssertExit (matchlen == 3); cout << "end of testSearch" << endl; } // Test a Regex in parallel. void testParallel() { #ifdef HAVE_OPENMP #pragma omp parallel #endif for (int i=0; i<32; ++i) { Regex rx(".*"); AlwaysAssert (String("ab").matches(rx), AipsError); } } int main () { try { // Test parallel first to ensure initialization works fine. testParallel(); // Test some specific pattern conversions. AlwaysAssertExit (Regex::toEcma("[][]") == String("[\\]\\[]")); AlwaysAssertExit (Regex::toEcma("[\\]\\[]") == String("[\\]\\[]")); AlwaysAssertExit (Regex::toEcma("[[:alpha:]]") == String("[[:alpha:]]")); AlwaysAssertExit (Regex::toEcma("(the)\\15a") == String("(the)\\1[5]a")); // Test the Regex functions. testBasic(); testIO(); testSearch(); } catch (const std::exception& x) { cout << x.what() << endl; return 1; } return 0; // exit with success status } casacore-3.7.1/casa/Utilities/test/tRegex2.cc000066400000000000000000000100021476623553700210050ustar00rootroot00000000000000//# tRegex.cc: Test program for the Regex class //# Copyright (C) 1993,1994,1995,1996,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include //# Forward Declarations void a(); // Test program for the Regex class // This program tests the class Regex. // It also tests if a Vector of Regex objects works fine. // The results are written to stdout. The script executing this program, // compares the results with the reference output file. int main () { try { a(); } catch (std::exception& x) { cout << x.what() << endl; return 1; } return 0; // exit with success status } // Make sure the return value for a non-match is the same // for 32 and 64 bit machines. String::size_type doMatch (const Regex& exp, const char* str, uInt pos) { String::size_type k = exp.match (str, pos); if (k == String::npos) { return 4294967295u; } return k; } // First do some simple Regex things. void a() { // Regex exp(Regex::fromPattern("a")); Regex exp22(Regex::fromPattern("[CR]T[^ab]*")); cout << "RTa " << String("RTa").matches(exp22) << endl; cout << "RTb " << String("RTb").matches(exp22) << endl; cout << "RTc " << String("RTc").matches(exp22) << endl; Regex exp(Regex::fromPattern("{a,b,c,d,e,f,g}")); cout << String("a").matches(exp) << endl; cout << String("b").matches(exp) << endl; cout << String("c").matches(exp) << endl; cout << String("d").matches(exp) << endl; cout << String("e").matches(exp) << endl; cout << String("f").matches(exp) << endl; cout << String("g").matches(exp) << endl; Regex exp1(Regex::fromPattern("x@{a,b}@y@{c,d}@{h,i}")); cout << "exp1 " << String("x@a@y@c@h").matches(exp1) << endl; Regex exp1b("xx@(aa|bb)@yy@(cc|dd|ee|ff|gg)@(hh|ii)"); cout << "exp1b " << String("xx@aa@yy@cc@hh").matches(exp1b) << endl; cout << "exp1b " << String("xx@aab@yy@cceegg@hhi").matches(exp1b) << endl; Regex exp1c("xx@(a(a|b)b)@yy@(c(c|d)(d|e)(e|f)(f|g)g)@(h(h|i)i)"); cout << "exp1c " << String("xx@aa@yy@cc@hh").matches(exp1c) << endl; cout << "exp1c " << String("xx@aab@yy@cceegg@hhi").matches(exp1c) << endl; Regex exp1a("xx@((aa)|(bb))@yy@((cc)|(dd)|(ee)|(ff)|gg)@((hh)|(ii))"); cout << "exp1a " << String("xx@aa@yy@cc@hh").matches(exp1a) << endl; cout << Regex::fromPattern("Gain:{11,22}:Real:{CS010_HBA0,CS010_HBA1,CS010_HBA2,CS008_HBA0,CS010_HBA3}:{CasA,CygA}") << endl; cout << Regex::fromPattern("Gain:{11,22}:Real:{CS010_HBA0,CS010_HBA1,CS010_HBA2,CS010_HBA3}:{CasA,CygA}") << endl; Regex exp2(Regex::fromPattern("Gain:{11,22}:Real:{CS010_HBA0,CS010_HBA1,CS010_HBA2,CS010_HBA3}:{CasA,CygA}")); cout << String("Gain:11:Real:CS010_HBA0:CasA").matches(exp2) << endl; } casacore-3.7.1/casa/Utilities/test/tRegex2.out000066400000000000000000000003651476623553700212420ustar00rootroot00000000000000RTa 0 RTb 0 RTc 1 1 1 1 1 1 1 1 exp1 1 exp1b 1 exp1b 0 exp1c 0 exp1c 1 exp1a 1 Gain:(11|22):Real:(CS010_HBA0|CS010_HBA1|CS010_HBA2|CS008_HBA0|CS010_HBA3):(CasA|CygA) Gain:(11|22):Real:(CS010_HBA0|CS010_HBA1|CS010_HBA2|CS010_HBA3):(CasA|CygA) 1 casacore-3.7.1/casa/Utilities/test/tRegex_1.cc000066400000000000000000000232021476623553700211510ustar00rootroot00000000000000//# tRegex_1.cc: Regex test program //# Copyright (C) 1996,1998,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include int main () { const Int ntests = 32; String p[ntests]; p[0] = "^().+|$"; p[1] = "\\,"; p[2] = "{a,{ab,cd}efg,b}"; p[3] = "{(),test}"; p[4] = "}{a,b}"; p[5] = "}{a,b}}"; p[6] = "}{a,\\}}}"; p[7] = "[[]"; p[8] = "[]]"; p[9] = "[]]]"; p[10] = "[]]*"; p[11] = "[]*]"; p[12] = "[[]*"; p[13] = "[[*]"; p[14] = "DfG*"; p[15] = "abc?vf??skkre*"; p[16] = "[^^]"; p[17] = "[!^]"; p[18] = "[^!]"; p[19] = "[!!]"; p[20] = "[{a,b}*^[?()+|]"; p[21] = "{a,bc*^?()+|]}"; p[22] = "s{t{aa,bb},c{dd,ee}f}"; p[23] = "{[{,}]}"; p[24] = "a\\,b"; p[25] = "\n\t"; p[26] = "\\?\\**"; p[27] = "\\\\"; p[28] = "__3%a%*"; p[29] = "*.{h,hpp,c,cc,cpp}"; p[30] = "*.{[hc]{,pp},cc}"; p[31] = "*"; cout << "Pattern --> Regular Expression" << endl; cout << "------------------------------" << endl; int i; for (i=0; i " << Regex::fromPattern(p[i]) << endl; } cout << endl << "Pattern --> Case Insensitive Regular Expression" << endl; cout << "-----------------------------------------------" << endl; for (i=0; i " << Regex::makeCaseInsensitive(Regex::fromPattern(p[i])) << endl; } cout << endl << "SQLPattern --> Regular Expression" << endl; cout << "---------------------------------" << endl; for (i=0; i " << Regex::fromSQLPattern(p[i]) << endl; } cout << endl << "String --> Regular Expression" << endl; cout << "-----------------------------" << endl; for (i=0; i " << Regex::fromString(p[i]) << endl; Regex exp(Regex::fromString(p[i])); AlwaysAssertExit (p[i].matches(exp)); } #define CHECKPATT(i,str) \ AlwaysAssertExit (String(str).matches (Regex(Regex::fromPattern(p[i])))) #define CHECKNPATT(i,str) \ AlwaysAssertExit (!String(str).matches (Regex(Regex::fromPattern(p[i])))) #define CHECKPATTCI(i,str) \ AlwaysAssertExit (String(str).matches (Regex(Regex::makeCaseInsensitive(Regex::fromPattern(p[i]))))) #define CHECKNPATTCI(i,str) \ AlwaysAssertExit (!String(str).matches (Regex(Regex::makeCaseInsensitive(Regex::fromPattern(p[i]))))) CHECKPATT (0, "^().+|$"); CHECKPATT (1, ","); CHECKPATT (2, "a"); CHECKPATT (2, "abefg"); CHECKPATT (2, "cdefg"); CHECKNPATT(2, "ab"); CHECKNPATT(2, "abdefg"); CHECKPATT (3, "()"); CHECKPATT (3, "test"); CHECKPATT (4, "}a"); CHECKPATT (5, "}a}"); CHECKNPATT(5, "}a"); CHECKPATT (6, "}a}"); CHECKPATT (6, "}}}"); CHECKNPATT(6, "}a"); CHECKNPATT(6, "}}"); CHECKPATT (7, "["); CHECKPATT (8, "]"); CHECKPATT (9, "]]"); CHECKNPATT(9, "]"); CHECKPATT (10, "]]]]]"); CHECKPATT (11, "]"); CHECKPATT (11, "*"); CHECKNPATT(11, "**"); CHECKNPATT(11, "]*"); CHECKPATT (12, "["); CHECKPATT (12, "[[[["); CHECKPATT (13, "["); CHECKPATT (13, "*"); CHECKNPATT(13, "[*"); CHECKPATT (14, "DfG"); CHECKPATT (14, "DfGxyz"); CHECKNPATT(14, "Df"); CHECKNPATT(14, "Dfg"); CHECKPATT (15, "abcxvfxxskkrexxx"); CHECKPATT (16, "x"); CHECKNPATT(16, "^"); CHECKPATT (17, "x"); CHECKNPATT(17, "^"); CHECKPATT (18, "x"); CHECKNPATT(18, "!"); CHECKPATT (19, "x"); CHECKNPATT(19, "!"); CHECKPATT (20, "{"); CHECKPATT (20, "a"); CHECKPATT (20, ","); CHECKPATT (20, "b"); CHECKPATT (20, "}"); CHECKPATT (20, "*"); CHECKPATT (20, "^"); CHECKPATT (20, "["); CHECKPATT (20, "?"); CHECKPATT (20, "("); CHECKPATT (20, ")"); CHECKPATT (20, "+"); CHECKPATT (20, "|"); CHECKNPATT(20, "x"); CHECKPATT (21, "a"); CHECKPATT (21, "bc^x()+|]"); CHECKPATT (21, "bcxxx^x()+|]"); CHECKNPATT(21, "bcxxx^xx)+|]"); CHECKPATT (22, "staa"); CHECKPATT (22, "stbb"); CHECKPATT (22, "scddf"); CHECKPATT (22, "sceef"); CHECKNPATT(22, "scee"); CHECKPATT (23, "{"); CHECKPATT (23, "}"); CHECKPATT (23, ","); CHECKNPATT(23, "x"); CHECKPATT (24, "a,b"); CHECKPATT (25, "\n\t"); CHECKNPATT(25, "\n "); CHECKPATT (26, "?*"); CHECKPATT (26, "?*xx"); CHECKNPATT(26, "x*xx"); CHECKNPATT(26, "?xxx"); CHECKPATT (27, "\\"); CHECKPATT (28, "__3%a%"); CHECKNPATT(30, ""); CHECKPATT (31, ""); CHECKPATTCI (0, "^().+|$"); CHECKPATTCI (1, ","); CHECKPATTCI (2, "a"); CHECKPATTCI (2, "abefg"); CHECKPATTCI (2, "cdefg"); CHECKNPATTCI(2, "ab"); CHECKNPATTCI(2, "abdefg"); CHECKPATTCI (3, "()"); CHECKPATTCI (3, "test"); CHECKPATTCI (4, "}a"); CHECKPATTCI (5, "}a}"); CHECKNPATTCI(5, "}a"); CHECKPATTCI (6, "}a}"); CHECKPATTCI (6, "}}}"); CHECKNPATTCI(6, "}a"); CHECKNPATTCI(6, "}}"); CHECKPATTCI (7, "["); CHECKPATTCI (8, "]"); CHECKPATTCI (9, "]]"); CHECKNPATTCI(9, "]"); CHECKPATTCI (10, "]]]]]"); CHECKPATTCI (11, "]"); CHECKPATTCI (11, "*"); CHECKNPATTCI(11, "**"); CHECKNPATTCI(11, "]*"); CHECKPATTCI (12, "["); CHECKPATTCI (12, "[[[["); CHECKPATTCI (13, "["); CHECKPATTCI (13, "*"); CHECKNPATTCI(13, "[*"); CHECKPATTCI (14, "dfg"); CHECKPATTCI (14, "DFG"); CHECKPATTCI (14, "dfgxyz"); CHECKNPATTCI(14, "df"); CHECKPATTCI (15, "abcxvfxxskkrexxx"); CHECKPATTCI (16, "x"); CHECKNPATTCI(16, "^"); CHECKPATTCI (17, "x"); CHECKNPATTCI(17, "^"); CHECKPATTCI (18, "x"); CHECKNPATTCI(18, "!"); CHECKPATTCI (19, "x"); CHECKNPATTCI(19, "!"); CHECKPATTCI (20, "{"); CHECKPATTCI (20, "a"); CHECKPATTCI (20, ","); CHECKPATTCI (20, "b"); CHECKPATTCI (20, "}"); CHECKPATTCI (20, "*"); CHECKPATTCI (20, "^"); CHECKPATTCI (20, "["); CHECKPATTCI (20, "?"); CHECKPATTCI (20, "("); CHECKPATTCI (20, ")"); CHECKPATTCI (20, "+"); CHECKPATTCI (20, "|"); CHECKNPATTCI(20, "x"); CHECKPATTCI (21, "a"); CHECKPATTCI (21, "bc^x()+|]"); CHECKPATTCI (21, "bcxxx^x()+|]"); CHECKNPATTCI(21, "bcxxx^xx)+|]"); CHECKPATTCI (22, "staa"); CHECKPATTCI (22, "stbb"); CHECKPATTCI (22, "scddf"); CHECKPATTCI (22, "sceef"); CHECKNPATTCI(22, "scee"); CHECKPATTCI (23, "{"); CHECKPATTCI (23, "}"); CHECKNPATTCI(23, "x"); CHECKPATTCI (24, "a,b"); CHECKPATTCI (25, "\n\t"); CHECKNPATTCI(25, "\n "); CHECKPATTCI (26, "?*"); CHECKPATTCI (26, "?*xx"); CHECKNPATTCI(26, "x*xx"); CHECKNPATTCI(26, "?xxx"); CHECKPATTCI (27, "\\"); CHECKPATTCI (28, "__3%a%"); CHECKPATTCI (29, "file.h"); CHECKPATTCI (29, "file.hpp"); CHECKPATTCI (29, "file.c"); CHECKPATTCI (29, "file.cc"); CHECKPATTCI (29, "file.cpp"); CHECKNPATTCI(29, "file.hh"); CHECKNPATTCI(29, "file.hp"); CHECKNPATTCI(29, "file.cp"); CHECKPATTCI (30, "file.h"); CHECKPATTCI (30, "file.hpp"); CHECKPATTCI (30, "file.c"); CHECKPATTCI (30, "file.cc"); CHECKPATTCI (30, "file.cpp"); CHECKNPATTCI(30, "file.hh"); CHECKNPATTCI(30, "file.hp"); CHECKNPATTCI(30, "file.cp"); AlwaysAssertExit (Regex::makeCaseInsensitive("[ab]") == "[aAbB]"); AlwaysAssertExit (Regex::makeCaseInsensitive("[]AB]") == "[]AaBb]"); AlwaysAssertExit (Regex::makeCaseInsensitive("[^]AB]") == "[^]AaBb]"); AlwaysAssertExit (Regex::makeCaseInsensitive("[B\\a]b]") == "[Bb\\aA][bB]]"); AlwaysAssertExit (Regex::makeCaseInsensitive("a-c") == "[aA]-[cC]"); AlwaysAssertExit (Regex::makeCaseInsensitive("[a-c]") == "[a-cA-C]"); AlwaysAssertExit (Regex::makeCaseInsensitive("[a-]\\w") == "[aA-]\\w"); AlwaysAssertExit (Regex::fromPattern("[:alpha:]") == "[:alpha:]"); AlwaysAssertExit (Regex::fromPattern("[[:alpha:]]") == "[[:alpha:]]"); AlwaysAssertExit (Regex::makeCaseInsensitive("[:alpha:]") == "[:aAlLpPhHaA:]"); AlwaysAssertExit (Regex::makeCaseInsensitive("[[:alpha:]]") == "[[:alpha:]]"); AlwaysAssertExit (Regex::makeCaseInsensitive (Regex::fromPattern("[[:alpha:]x[:bb:]]")) == "[[:alpha:]xX[:bb:]]"); // Omitting the " makes a bid difference. AlwaysAssertExit (Regex::makeCaseInsensitive (Regex::fromPattern("[[alpha:]x[:bb:]]")) == "[[aAlLpPhHaA:][xX][:bBbB:]]"); return 0; } casacore-3.7.1/casa/Utilities/test/tRegex_1.out000066400000000000000000000064661476623553700214100ustar00rootroot00000000000000Pattern --> Regular Expression ------------------------------ ^().+|$ --> \^\(\)\.\+\|\$ \, --> , {a,{ab,cd}efg,b} --> (a|(ab|cd)efg|b) {(),test} --> (\(\)|test) }{a,b} --> \}(a|b) }{a,b}} --> \}(a|b)\} }{a,\}}} --> \}(a|\})\} [[] --> [[] []] --> []] []]] --> []]] []]* --> []].* []*] --> []*] [[]* --> [[].* [[*] --> [[*] DfG* --> DfG.* abc?vf??skkre* --> abc.vf..skkre.* [^^] --> [^^] [!^] --> [^^] [^!] --> [^!] [!!] --> [^!] [{a,b}*^[?()+|] --> [{a,b}*^[?()+|] {a,bc*^?()+|]} --> (a|bc.*\^.\(\)\+\|]) s{t{aa,bb},c{dd,ee}f} --> s(t(aa|bb)|c(dd|ee)f) {[{,}]} --> ([{,}]) a\,b --> a,b --> \?\** --> \?\*.* \\ --> \\ __3%a%* --> __3%a%.* *.{h,hpp,c,cc,cpp} --> .*\.(h|hpp|c|cc|cpp) *.{[hc]{,pp},cc} --> .*\.([hc](pp)?|cc) * --> .* Pattern --> Case Insensitive Regular Expression ----------------------------------------------- ^().+|$ --> \^\(\)\.\+\|\$ \, --> , {a,{ab,cd}efg,b} --> ([aA]|([aA][bB]|[cC][dD])[eE][fF][gG]|[bB]) {(),test} --> (\(\)|[tT][eE][sS][tT]) }{a,b} --> \}([aA]|[bB]) }{a,b}} --> \}([aA]|[bB])\} }{a,\}}} --> \}([aA]|\})\} [[] --> [[] []] --> []] []]] --> []]] []]* --> []].* []*] --> []*] [[]* --> [[].* [[*] --> [[*] DfG* --> [Dd][fF][Gg].* abc?vf??skkre* --> [aA][bB][cC].[vV][fF]..[sS][kK][kK][rR][eE].* [^^] --> [^^] [!^] --> [^^] [^!] --> [^!] [!!] --> [^!] [{a,b}*^[?()+|] --> [{aA,bB}*^[?()+|] {a,bc*^?()+|]} --> ([aA]|[bB][cC].*\^.\(\)\+\|]) s{t{aa,bb},c{dd,ee}f} --> [sS]([tT]([aA][aA]|[bB][bB])|[cC]([dD][dD]|[eE][eE])[fF]) {[{,}]} --> ([{,}]) a\,b --> [aA],[bB] --> \?\** --> \?\*.* \\ --> \\ __3%a%* --> __3%[aA]%.* *.{h,hpp,c,cc,cpp} --> .*\.([hH]|[hH][pP][pP]|[cC]|[cC][cC]|[cC][pP][pP]) *.{[hc]{,pp},cc} --> .*\.([hHcC]([pP][pP])?|[cC][cC]) * --> .* SQLPattern --> Regular Expression --------------------------------- ^().+|$ --> \^\(\)\.\+\|\$ \, --> \\, {a,{ab,cd}efg,b} --> \{a,\{ab,cd\}efg,b\} {(),test} --> \{\(\),test\} }{a,b} --> \}\{a,b\} }{a,b}} --> \}\{a,b\}\} }{a,\}}} --> \}\{a,\\\}\}\} [[] --> \[\[\] []] --> \[\]\] []]] --> \[\]\]\] []]* --> \[\]\]\* []*] --> \[\]\*\] [[]* --> \[\[\]\* [[*] --> \[\[\*\] DfG* --> DfG\* abc?vf??skkre* --> abc\?vf\?\?skkre\* [^^] --> \[\^\^\] [!^] --> \[!\^\] [^!] --> \[\^!\] [!!] --> \[!!\] [{a,b}*^[?()+|] --> \[\{a,b\}\*\^\[\?\(\)\+\|\] {a,bc*^?()+|]} --> \{a,bc\*\^\?\(\)\+\|\]\} s{t{aa,bb},c{dd,ee}f} --> s\{t\{aa,bb\},c\{dd,ee\}f\} {[{,}]} --> \{\[\{,\}\]\} a\,b --> a\\,b --> \?\** --> \\\?\\\*\* \\ --> \\\\ __3%a%* --> ..3.*a.*\* *.{h,hpp,c,cc,cpp} --> \*\.\{h,hpp,c,cc,cpp\} *.{[hc]{,pp},cc} --> \*\.\{\[hc\]\{,pp\},cc\} * --> \* String --> Regular Expression ----------------------------- ^().+|$ --> \^\(\)\.\+\|\$ \, --> \\, {a,{ab,cd}efg,b} --> \{a,\{ab,cd\}efg,b\} {(),test} --> \{\(\),test\} }{a,b} --> \}\{a,b\} }{a,b}} --> \}\{a,b\}\} }{a,\}}} --> \}\{a,\\\}\}\} [[] --> \[\[\] []] --> \[\]\] []]] --> \[\]\]\] []]* --> \[\]\]\* []*] --> \[\]\*\] [[]* --> \[\[\]\* [[*] --> \[\[\*\] DfG* --> DfG\* abc?vf??skkre* --> abc\?vf\?\?skkre\* [^^] --> \[\^\^\] [!^] --> \[!\^\] [^!] --> \[\^!\] [!!] --> \[!!\] [{a,b}*^[?()+|] --> \[\{a,b\}\*\^\[\?\(\)\+\|\] {a,bc*^?()+|]} --> \{a,bc\*\^\?\(\)\+\|\]\} s{t{aa,bb},c{dd,ee}f} --> s\{t\{aa,bb\},c\{dd,ee\}f\} {[{,}]} --> \{\[\{,\}\]\} a\,b --> a\\,b --> \?\** --> \\\?\\\*\* \\ --> \\\\ __3%a%* --> __3%a%\* *.{h,hpp,c,cc,cpp} --> \*\.\{h,hpp,c,cc,cpp\} *.{[hc]{,pp},cc} --> \*\.\{\[hc\]\{,pp\},cc\} * --> \* casacore-3.7.1/casa/Utilities/test/tSort.cc000066400000000000000000000243141476623553700206130ustar00rootroot00000000000000//# tSort.cc: Test program for the Sort class //# Copyright (C) 1994,1995,1996,1997,1998,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include // This program test the class Sort. // It sorts some data in ascending and/or descending order. // The results are written to stdout. A script executing this program, // compares the output with a reference output file. void sortit (int opt) { Int arr[10]; Int64 ar2[10]; Int ar3[10]; uInt i; double ard[10]; struct Ts { double ad; String as; }; Ts arts[10]; for (i=0; i<10; i++) { arr[i] = i; ar2[i] = 10-i; ar3[i] = 19-i; ard[i] = i/3; arts[i].ad = ard[i]; arts[i].as = "abc"; } // Note: ar3 is a specific test if 'last' in ParSort's merge works fine. ar3[9] = 20; arts[2].as = "ABC"; arts[5].as = "xyzabc"; Sort sort; sort.sortKey (arr,TpInt); // sort arr Vector inxvec; uInt nr = sort.sort (inxvec,10,opt,False); // get indices back in inxvec for (i=0; i inxvec; uInt nr = sort.sort (inxvec, nrdata, options); uInt i; for (i=1; i data[inxvec(i-1)]) { cout << "Desc order error on index " << i << endl; } } if (data[inxvec(i)] == data[inxvec(i-1)]) { if ((options & Sort::NoDuplicates) != 0) { cout << "Duplicate value on index" << i << endl; }else{ if (order == Sort::Ascending) { if (inxvec(i) < inxvec(i-1)) { cout << "Asc equal order error on index " << i << endl; } }else{ if (inxvec(i) > inxvec(i-1)) { cout << "Desc equal order error on index " << i << endl; } } } } } if ((options & Sort::NoDuplicates) == 0) { AlwaysAssertExit (nr == nrdata); AlwaysAssertExit (nr == inxvec.nelements()); } else { Vector inxvec2; sort.sort (inxvec2, nrdata, Sort::QuickSort); Vector uniqvec; uInt nr2 = sort.unique (uniqvec, inxvec2); AlwaysAssertExit (nr2 == nr); AlwaysAssertExit (nr2 == uniqvec.nelements()); for (i=0; i 0) { if (data[inxvec2(uniqvec(i))] == data[inxvec2(uniqvec(i-1))]) { cout << "Non-unique value on index" << i << endl; } } } } } // Test with 1 and 2 keys, because 1 key is short-circuited to GenSort. void sortall (int options, Sort::Order order) { const uInt nrdata = 10; Int data[nrdata]; Int data2[nrdata]; Sort sort; sort.sortKey (data, TpInt, 0, order); Sort sort2; sort2.sortKey (data, TpInt, 0, order); sort2.sortKey (data2, TpInt, 0, order); for (uInt i=0; i inxvec; uInt nr = sort.sort (inxvec, nrdata, Sort::ParSort); AlwaysAssertExit (nr == nrdata); // Get the group boundaries (portions of the dataset where all the sorting // functions are evaluated to 0, i.e., they are identical from the point of // view of the sorting) Vector uniqueVector; Vector changeKey; sort.unique(uniqueVector, changeKey, inxvec); for (size_t i=0; i #include #include #include #include #include #include #include //# Forward Declarations Bool sortarr (Int*, uInt nr, int); Bool sortall (Int*, uInt nr, uInt type); Bool sort2 (uInt nr); // Define file global variable for cmp-routine. static Int* gbla; // This program tests the speed of the Sort class . // It sorts some data in ascending and/or descending order. // The timing results are written to stdout. int main(int argc, const char* argv[]) { Bool success = True; uInt nr=5000; if (argc > 1) { istringstream istr(argv[1]); istr >> nr; } cout << nr << " elements" << endl; Int* a1 = new Int[nr]; Int* a2 = new Int[nr]; Int* a3 = new Int[nr]; Int* a4 = new Int[nr]; Int* a5 = new Int[nr]; Int* a6 = new Int[nr]; Int* a7 = new Int[nr]; if (a1==0 || a2==0 || a3==0 || a4==0 || a5==0 || a6==0 || a7==0) { cout << "Allocation error" << endl; } for (uInt i=0; i::compare (&gbla[*(uInt*)i],&gbla[*(uInt*)j]);} void qksort (Int nr, uInt* inx) { if (nr <= 1) { return; } // According to Sedgewick it is best to use a random partition element // to avoid degenerated cases (if the data is already in order for example) // rand is not a particularly good random number generator, but good // enough for this purpose. // Put this element at the beginning of the array. Int p = rand() % nr; uInt sav = inx[0]; inx[0] = inx[p]; inx[p] = sav; // Now shift all elements < partition-element to the left. // If an element is equal, shift every other element to avoid // degeneration. This trick is described by Jon Bentley in // UNIX Review, October 1992. Int j = 0; int cm, sw=0; for (Int i=1; i 0 || (cm == 0 && (sw = !sw))) { sav = inx[i]; inx[i] = inx[++j]; inx[j] = sav; } } sav = inx[0]; inx[0] = inx[j]; inx[j] = sav; qksort (j, inx); qksort (nr-j-1, inx+j+1); } Bool sortall (Int* arr, uInt nr, uInt type) { Bool success = True; if (nr <= 5000) { cout << "InsSort "; if (! sortarr (arr, nr, Sort::InsSort)) { success = False; } } cout << "ParSort "; if (! sortarr (arr, nr, Sort::ParSort)) { success = False; } cout << "QuickSort "; if (! sortarr (arr, nr, Sort::QuickSort)) { success = False; } cout << "HeapSort "; if (! sortarr (arr, nr, Sort::HeapSort)) { success = False; } Timer tim; uInt i; if (type==0 || (type==2 && nr<=10000) || (type==5 && nr<=20000) || (type==10 && nr<=100000)) { cout << "qsort "; uInt* inx = new uInt[nr]; if (inx == 0) { cout << "Allocation Error" << endl; return False; } for (i=0; i::compare); tim.show(); } return success; } Bool sortarr1 (Int* arr, uInt nr, int opt) { Bool success = True; Sort sort; sort.sortKey (arr,TpInt); Vector ptr; Timer tim; sort.sort (ptr,nr,opt,False); tim.show(); for (uInt i=1; i inx1; sort.sort (inx1, vec1.size(), Sort::ParSort); cout << "parsort2 "; timer.show(); } { Timer timer; Int nrant = 1 + max(max(vec1), max(vec2)); Vector bl(vec1*nrant); bl += vec2; cout << " fill "; timer.show(); Vector inx; GenSortIndirect::sort (inx, bl); cout << "indsort "; timer.show(); } return True; } casacore-3.7.1/casa/Utilities/test/tStringDistance.cc000066400000000000000000000044511476623553700226050ustar00rootroot00000000000000//# tStringDistance.cc: Test of class StringDistance //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include using namespace std; using namespace casacore; int main(int argc, char* argv[2]) { try { if (argc < 3) { cout << "Run as: tStringDistance source target [countSwap] " << "[ignoreBlanks] [caseInsensitive]" << endl; return 1; } Bool countSwap = (argc > 3 && *argv[3] == '1'); Bool ignoreBlanks = (argc > 4 && *argv[4] == '1'); Bool caseInsensitive = (argc > 5 && *argv[5] == '1'); StringDistance dist(argv[1], -1, countSwap, ignoreBlanks, caseInsensitive); cout << dist.source() << ' ' << dist.maxDistance() << ": match=" << dist.match(argv[2]) << " dist=" << dist.distance(argv[2]) << " distlr=" << StringDistance::distance(argv[1], argv[2], countSwap) << " distrl=" << StringDistance::distance(argv[2], argv[1], countSwap) << endl; cout << dist.matrix(); } catch (exception& x) { cout << "Unexpected exception: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/casa/Utilities/test/tStringDistance.out000066400000000000000000000043111476623553700230220ustar00rootroot00000000000000abcd 2: match=1 dist=0 distlr=0 distrl=0 Axis Lengths: [5, 7] (NB: Matrix in Row/Column order) [0, 1, 2, 3, 4, -1, -1 1, 0, 1, 2, 3, -1, -1 2, 1, 0, 1, 2, -1, -1 3, 2, 1, 0, 1, -1, -1 4, 3, 2, 1, 0, -1, -1] abcd 2: match=1 dist=0 distlr=0 distrl=0 Axis Lengths: [5, 7] (NB: Matrix in Row/Column order) [0, 1, 2, 3, 4, -1, -1 1, 0, 1, 2, 3, -1, -1 2, 1, 0, 1, 2, -1, -1 3, 2, 1, 0, 1, -1, -1 4, 3, 2, 1, 0, -1, -1] AbCd 2: match=0 dist=4 distlr=4 distrl=4 Axis Lengths: [5, 7] (NB: Matrix in Row/Column order) [0, 1, 2, 3, 4, -1, -1 1, 1, 2, 3, 4, -1, -1 2, 2, 2, 3, 4, -1, -1 3, 3, 3, 3, 4, -1, -1 4, 4, 4, 4, 4, -1, -1] abcd 2: match=1 dist=0 distlr=4 distrl=4 Axis Lengths: [5, 7] (NB: Matrix in Row/Column order) [0, 1, 2, 3, 4, -1, -1 1, 0, 1, 2, 3, -1, -1 2, 1, 0, 1, 2, -1, -1 3, 2, 1, 0, 1, -1, -1 4, 3, 2, 1, 0, -1, -1] abcd 2: match=1 dist=0 distlr=2 distrl=2 Axis Lengths: [5, 7] (NB: Matrix in Row/Column order) [0, 1, 2, 3, 4, -1, -1 1, 0, 1, 2, 3, -1, -1 2, 1, 0, 1, 2, -1, -1 3, 2, 1, 0, 1, -1, -1 4, 3, 2, 1, 0, -1, -1] ACbd 2: match=0 dist=3 distlr=3 distrl=3 Axis Lengths: [5, 7] (NB: Matrix in Row/Column order) [0, 1, 2, 3, 4, -1, -1 1, 1, 2, 3, 4, -1, -1 2, 2, 2, 3, 4, -1, -1 3, 3, 2, 3, 4, -1, -1 4, 4, 3, 3, 3, -1, -1] acbd 2: match=1 dist=2 distlr=3 distrl=3 Axis Lengths: [5, 7] (NB: Matrix in Row/Column order) [0, 1, 2, 3, 4, -1, -1 1, 0, 1, 2, 3, -1, -1 2, 1, 1, 1, 2, -1, -1 3, 2, 1, 2, 2, -1, -1 4, 3, 2, 2, 2, -1, -1] acbd 2: match=1 dist=1 distlr=3 distrl=3 Axis Lengths: [5, 7] (NB: Matrix in Row/Column order) [0, 1, 2, 3, 4, -1, -1 1, 0, 1, 2, 3, -1, -1 2, 1, 1, 1, 2, -1, -1 3, 2, 1, 1, 2, -1, -1 4, 3, 2, 2, 1, -1, -1] acbd 2: match=0 dist=3 distlr=4 distrl=4 Axis Lengths: [5, 7] (NB: Matrix in Row/Column order) [0, 1, 2, 3, 4, 5, -1 1, 0, 1, 2, 3, 4, -1 2, 1, 1, 2, 2, 3, -1 3, 2, 1, 2, 3, 3, -1 4, 3, 2, 2, 3, 3, -1] acbd 2: match=1 dist=1 distlr=4 distrl=4 Axis Lengths: [5, 7] (NB: Matrix in Row/Column order) [0, 1, 2, 3, 4, -1, -1 1, 0, 1, 2, 3, -1, -1 2, 1, 1, 1, 2, -1, -1 3, 2, 1, 1, 2, -1, -1 4, 3, 2, 2, 1, -1, -1] ab 1: match=1 dist=1 distlr=1 distrl=1 Axis Lengths: [3, 4] (NB: Matrix in Row/Column order) [0, 1, 2, -1 1, 1, 1, -1 2, 1, 1, -1] casacore-3.7.1/casa/Utilities/test/tStringDistance.run000077500000000000000000000013331476623553700230230ustar00rootroot00000000000000#!/bin/sh #------------------------------------------------------------------- # Script to test the StringDistance class. #====================================================== $casa_checktool ./tStringDistance abcd abcd 0 0 0 $casa_checktool ./tStringDistance abcd abcd 1 1 1 $casa_checktool ./tStringDistance AbCd aBcD 0 0 0 $casa_checktool ./tStringDistance AbCd aBcD 0 0 1 $casa_checktool ./tStringDistance AbCd abcd 0 0 1 $casa_checktool ./tStringDistance ACbd abcd 0 0 0 $casa_checktool ./tStringDistance ACbd abcd 0 0 1 $casa_checktool ./tStringDistance ACbd abcd 1 0 1 $casa_checktool ./tStringDistance ACbd 'ab cd' 1 0 1 $casa_checktool ./tStringDistance ACbd 'ab cd' 1 1 1 $casa_checktool ./tStringDistance ab ba 1 1 1 casacore-3.7.1/casa/aips.cc000066400000000000000000000026641476623553700155060ustar00rootroot00000000000000//# aips.cc: Global initialization for standard types, etc. //# Copyright (C) 1993,1994,1996,1997,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include namespace casacore { //# NAMESPACE CASACORE - BEGIN #if defined(AIPS_DEBUG) Bool aips_debug_on = True; #else Bool aips_debug_on = False; #endif } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/aips.h000066400000000000000000000033201476623553700153360ustar00rootroot00000000000000//# aips.h: Global initialization for standard types, etc. //# Copyright (C) 1993-1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_AIPS_H #define CASA_AIPS_H //# Define compiler specific flags #include //# Define the standard types used by Casacore. #include //# Define the extra non-standard types used by Casacore #include //# Define the special Casacore macros #include //# Define the namespace used by Casacore namespace casacore { //# NAMESPACE CASACORE - BEGIN } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/aipsdef.h000066400000000000000000000061271476623553700160250ustar00rootroot00000000000000//# aipsdef.h: Global initialization for special Casacore macros //# Copyright (C) 2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_AIPSDEF_H #define CASA_AIPSDEF_H #include //# needed for Bool //# Define the Casacore global macros //# Defined the "aips_name2" macro which is used to join two tokens. #if defined(__STDC__) || defined(__ANSI_CPP__) || defined(__hpux) #define aips_name2(a,b) a##b #else #define aips_name2(a,b) a/**/b #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN // If AIPS_DEBUG is not defined, then the symbol expands to (0) which in an // if should be removed by the dead code eliminator of any optimizer; thus // using this in your code should have no performance penalty in the normal // case. If compiled with AIPS_DEBUG, then aips_debug is (defined to ) // a global boolean variable (so it can be turned on and off in a debugger) // which is initialized to True. extern Bool aips_debug_on; } //# NAMESPACE CASACORE - END #if !defined(AIPS_DEBUG) #define aips_debug (0) #else // The reason that we just don't make this a variable here is so that // we can link against libraries compiled with or without AIPS_DEBUG // without having any missing symbols. #define aips_debug aips_debug_on #endif // HP/UX #if defined(__hpux__) #define AIPS_HPUX #endif // The restrict keyword is supported by some compilers only. #if !defined(AIPS_KAICC) && !defined(AIPS_INTELCC) #if !defined(restrict) #define restrict #endif #endif // Define the macros to stringify a preprocessor variable. #define CASACORE_STRINGIFY(x) CASACORE_STRINGIFY_HELPER(x) #define CASACORE_STRINGIFY_HELPER(x) #x // A fallthrough attribute to avoid compiler warnings, // available only from C++17 onwards #if __cplusplus >= 201703L #define CASACORE_FALLTHROUGH [[fallthrough]] #elif defined(__GNUC__) && __GNUC__ >= 7 #define CASACORE_FALLTHROUGH [[gnu::fallthrough]] #elif defined(__clang__) #define CASACORE_FALLTHROUGH [[clang::fallthrough]] #else #define CASACORE_FALLTHROUGH #endif #endif casacore-3.7.1/casa/aipsenv.h000066400000000000000000000132711476623553700160550ustar00rootroot00000000000000//# aipsenv.h: Global initialization for special Casacore macros //# Copyright (C) 2000,2001,2002,2003,2004,2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // this file contains all the compiler specific defines #ifndef CASA_AIPSENV_H #define CASA_AIPSENV_H namespace casacore { //# NAMESPACE CASACORE - BEGIN // Set if compiler supports C++11 or newer #if __cplusplus >= 201103L #define AIPS_CXX11 #endif // Set if compiler supports C++14 or newer #if __cplusplus >= 201402L #define AIPS_CXX11 #define AIPS_CXX14 #endif // Set if GCC compiler is used. #if defined(AIPS_GCC) #undef AIPS_GCC #endif #if defined(__GNUC__) #define AIPS_GCC #endif /* ONLY USE IF CODE WILL _NOT_ WORK WITH NEWER VERSIONS */ #if defined(AIPS_GCC2) #undef AIPS_GCC2 #endif #if (defined(AIPS_GCC) && __GNUC_CC == 2) #define AIPS_GCC2 #endif /* ONLY USE IF CODE WILL _NOT_ WORK WITH NEWER VERSIONS */ #if defined(AIPS_GCC295) #undef AIPS_GCC295 #endif #if (defined(AIPS_GCC2) && __GNUC_MINOR__ == 95) #define AIPS_GCC295 #endif /* ONLY USE IF CODE WILL _NOT_ WORK WITH NEWER VERSIONS */ #if defined(AIPS_GCC3) #undef AIPS_GCC3 #endif #if (defined(AIPS_GCC) && __GNUC__ == 3) #define AIPS_GCC3 #endif /* ONLY USE IF CODE WILL _NOT_ WORK WITH NEWER VERSIONS */ #if defined(AIPS_GCC4) #undef AIPS_GCC4 #endif #if (defined(AIPS_GCC) && __GNUC__ == 4) #define AIPS_GCC4 #endif // Alternate project compiler #if defined(AIPS_SGI) #undef AIPS_SGI #endif #if defined(__sgi) #define AIPS_SGI #if defined(_MIPS_SZPTR) && (_MIPS_SZPTR == 64) #define AIPS_64B #define SGI64 #endif #endif // Alternate project compiler #if defined(AIPS_SUN_NATIVE) #undef AIPS_SUN_NATIVE #endif #if defined(__SUNPRO_CC) #define AIPS_SUN_NATIVE #endif #if defined(AIPS_SOLARIS) #undef AIPS_SOLARIS #endif #if defined(__sun) #define AIPS_SOLARIS #endif #if defined(AIPS_HP) #undef AIPS_HP #endif #if defined(__hp) #define AIPS_HP #endif #if defined(AIPS_ALPHA) #undef AIPS_ALPHA #endif #if defined(__alpha) #define AIPS_ALPHA #define AIPS_64B #endif #if defined(AIPS_BSD) #undef AIPS_BSD #endif #if defined(__FreeBSD__) #define AIPS_BSD #define AIPS_NOLARGEFILE #endif #if defined(AIPS_LINUX) #undef AIPS_LINUX #endif #if defined(__linux__) #define AIPS_LINUX #endif #if defined(AIPS_KAI) #undef AIPS_KAI #endif #if defined(__kai) #define AIPS_KAI #endif #if defined(AIPS_AIX) #undef AIPS_AIX #endif #if defined(_AIX) #define AIPS_AIX #endif #if defined(AIPS_INTELCC) #undef AIPS_INTELCC #endif #if defined(__INTEL_COMPILER) #define AIPS_INTELCC #endif #if defined(AIPS_CRAY_PGI) #undef AIPS_CRAY_PGI #endif #if defined(__QK_USER__) #define AIPS_CRAY_PGI #if !defined(AIPS_NOLARGEFILE) #define AIPS_NOLARGEFILE #endif #if !defined(AIPS_NO_LEA_MALLOC) #define AIPS_NO_LEA_MALLOC #endif #endif #if defined(AIPS_CRAY_CATAMOUNT) #undef AIPS_CRAY_CATAMOUNT #endif #if defined(__LIB_CATAMOUNT__) #define AIPS_CRAY_CATAMOUNT #endif #if (defined(__ia64) || defined(__x86_64__) || defined(__aarch64__)) # if !defined(AIPS_64B) # define AIPS_64B # endif #endif #if defined(AIPS_I386) #undef AIPS_I386 #endif #if defined(i386) #define AIPS_I386 #endif #if defined(AIPS_DARWIN) #undef AIPS_DARWIN #endif #if defined(__APPLE__) #define AIPS_DARWIN // No need for largefile definition as it is the default under DARWIN #define AIPS_NOLARGEFILE // Don't use AIPS_LITTLE_ENDIAN as this would prevent universal builds // from working. Auto-detect from AIPS_I386 for intel Macs #define AIPS_NO_LEA_MALLOC # if defined(AIPS_LITTLE_ENDIAN) # undef AIPS_LITTLE_ENDIAN # endif #endif // If the compiler specifies endianness, use that #if !(defined(AIPS_LITTLE_ENDIAN)) #if (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__)) #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #define AIPS_LITTLE_ENDIAN #endif #else // Automatically configure for known LITTLE ENDIAN systems #if (defined(AIPS_ALPHA) || defined(AIPS_I386) || defined(__x86_64__) || defined(__ARMEL__) || defined(__AARCH64EL__)) #define AIPS_LITTLE_ENDIAN #endif #endif #endif // If needed, define the LFS variables (needed in code using cfitsio). #ifndef AIPS_NOLARGEFILE # undef _FILE_OFFSET_BITS # define _FILE_OFFSET_BITS 64 # ifndef _LARGEFILE_SOURCE # define _LARGEFILE_SOURCE # endif # ifndef _LARGEFILE64_SOURCE # define _LARGEFILE64_SOURCE # endif #endif // Compiler hints for branch prediction (and code layout). // Use only after profiling performance critical code, // or on checks for exceptional conditions. #if defined __GNUC__ || defined __clang__ #define AIPS_LIKELY(x) __builtin_expect(!!(x), 1) #define AIPS_UNLIKELY(x) __builtin_expect(!!(x), 0) #else #define AIPS_LIKELY(x) (x) #define AIPS_UNLIKELY(x) (x) #endif } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/aipstype.h000066400000000000000000000037121476623553700162450ustar00rootroot00000000000000//# aipstype.h: Global initialization for standard Casacore types //# Copyright (C) 2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_AIPSTYPE_H #define CASA_AIPSTYPE_H // For temporary backward namespace compatibility, use casa as alias for casacore. //# Note: namespace casa = casacore; does not work for forward declarations. #if defined (UseCasaNamespace) # define casacore casa #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN // Define the standard types used by Casacore typedef bool Bool; const Bool True = true; const Bool False = false; typedef char Char; typedef unsigned char uChar; typedef short Short; typedef unsigned short uShort; typedef int Int; typedef unsigned int uInt; typedef long Long; typedef unsigned long uLong; typedef float Float; typedef double Double; typedef long double lDouble; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/aipsxtype.h000066400000000000000000000035331476623553700164360ustar00rootroot00000000000000//# aipsxtype.h: Global initialization for special Casacore types //# Copyright (C) 2000,2001,2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_AIPSXTYPE_H #define CASA_AIPSXTYPE_H namespace casacore { //# NAMESPACE CASACORE - BEGIN // Define the extra non-standard types used by Casacore // (like proposed uSize, Size) // A guaranteed 64-bit long integer (for a.o. large file systems). // An implementation must support the + and - operators. typedef long long Int64; typedef unsigned long long uInt64; // All FITS code seems to assume longs are 4 bytes. Currently // this corresponds to an "int" on all useful platforms. typedef int FitsLong; // Define the type of a row number in a table. typedef uInt64 rownr_t; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/apps/000077500000000000000000000000001476623553700151765ustar00rootroot00000000000000casacore-3.7.1/casa/apps/CMakeLists.txt000066400000000000000000000004521476623553700177370ustar00rootroot00000000000000# Programs foreach(prog casahdf5support) add_executable (${prog} ${prog}.cc) add_pch_support(${prog}) target_link_libraries (${prog} casa_casa) install(TARGETS ${prog}) endforeach() # Scripts ##foreach(prog countcode) ## install(PROGRAMS ${prog} DESTINATION bin) ##endforeach() casacore-3.7.1/casa/apps/casahdf5support.cc000066400000000000000000000032471476623553700206260ustar00rootroot00000000000000//# casahdf5support.cc: test if casacore is build with HDF5 support //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include using namespace casacore; // Test if casacore is built with support for HDF5. // It reurns 0 if so, otherwise 1. // A message is printed unless -s is given as the first argument. int main(int argc, char*[]) { if (HDF5Object::hasHDF5Support()) { if (argc < 2) { cout << "casacore built with HDF5 support" << endl; } return 0; } if (argc < 2) { cout << "casacore built without HDF5 support" << endl; } return 1; } casacore-3.7.1/casa/apps/countcode000077500000000000000000000456461476623553700171260ustar00rootroot00000000000000#!/usr/bin/env python # This script counts the lines of code in various types of source files. # It has options to print summaries at various levels. # # It can be used with python version 2 and 3. # todo: # - possibly specify/override/add files/dirs to be ignored on command line import re import os import sys import stat import time import argparse # Define the various file types and comment marker for single line and multi line. # It defines the following fields: # - The file type # - A list of matching file name extensions # - None or a regex defining a matching file name extension pattern # - A list of matching file names # - The delimiter defining a comment in a single line # - The delimiters defining the start and end of a block comment # - Indication if file contains code or other info (1=code, 0=other) # Similar to cloc there are a few theoretical problems: # 1. If a quoted string contains comment delimiters, they are recognized # as comment delimiters. In principle some regexes could be defined to replace # such strings, possibly after first replacing escaped backslashes and quotes. # This is quite some work for cases that do not occur in practice. # 2. A regex like '""".*"""' is greedy, thus a line like """some1"""some2""" # is fully matched. In practice such lines are not used. # See cnt.py-new for an attempt solving these issues. types = [ ('C++', ['cc', 'tcc', 'hcc', 'cpp', 'cxx'], None, [], '//', '/*', '*/', 1), ('C++Hdr', ['h', 'hpp', 'hxx'], None, [], '//', '/*', '*/', 1), ('C', ['c'], None, [], '//', '/*', '*/', 1), ('Cuda', ['cu'], None, [], '//', '/*', '*/', 1), ('OpenCL', ['cl'], None, [], '//', '/*', '*/', 1), ('Fortran', ['f', 'for'], None, [], '*', '', '', 1), ('Assembly', ['m', 'S'], None, [], '', '', '', 1), ('Lisp', ['lisp'], None, [], '', '', '', 1), ('SQL', ['sql'], None, [], '--', '', '', 1), ('TaQL', ['taql'], None, [], '#', '', '', 1), ('Flex', ['l', 'll'], None, [], '//', '/*', '*/', 1), ('Bison', ['y', 'yy'], None, [], '//', '/*', '*/', 1), ('Python', ['py', 'python', 'python3'], None, [], '#', '"""', '"""', 1), ('Perl', ['pl', 'perl'], None, [], '#', '', '', 1), ('test-run', ['run'], None, [], '','','', 0), ('test-in', ['in'], re.compile('in_.*'), [], '','','', 0), ('test-out', ['out', 'stdout'], None, [], '','','', 0), ('sh', ['sh'], None, [], '#', '', '', 0), ('bash', ['bash'], None, [], '#', '', '', 0), ('csh', ['csh'], None, [], '#', '', '', 0), ('tcsh', ['tcsh'], None, [], '#', '', '', 0), ('CMake', ['cmake'], None, ['CMakeLists.txt'], '#', '', '', 0), ('Config', ['conf', 'cfg', 'dat'], None, [], '#', '', '', 0), ('Component', ['comp'], None, [], '#', '', '', 0), ('parset', ['parset'], re.compile('parset.*'), [], '#', '', '', 0), ('log_prop', ['log_prop'], None, [], '#', '', '', 0), ('rst', ['rst'], None, [], '', '', '', 0), ('doxygen', ['dox'], None, ['doxygen.cfg'], '', '', '', 0), ('xml', ['xml', 'xsl', 'xsd'], None, [], '', '', '', 0), ('html', ['html', 'htm'], None, [], '', '', '', 0), ('binary', [], None, [], '', '', '', 0), # binary files ('ignore', ['log', 'shar', 'tmp', 'ps', 'fig','omt'], re.compile('.*(~|-sav|-new|info|params|sed|md)'), ['templates','makefile','changelog','.gitignore','.travis.yml'], '', '', '', 0), # files to be ignored ('unknown', [], None, [], '', '', '', 0) ] def showTypes (verbose): for (type,exts,extre,filenms,comm,scomm,ecomm,ctyp) in types: print ('%-24s code=%d' % (type,ctyp)) print (' file name extensions: ', exts) if verbose: if not extre is None: print (' extension pattern: ', extre.pattern) if len(filenms) > 0: print (' file names: ', filenms) if len(comm) > 0: print (' comment marker: ', comm) if len(scomm) > 0: print (' start comment block: ', scomm) print (' end comment block: ', ecomm) # Define regex for a line containing an alphanumeric character reAlphaNum = re.compile('\w') def hasAlphaNum (line): l = line.strip() return len(l) > 0 and reAlphaNum.search(line) # From http://stackoverflow.com/questions/898669/how-can-i-detect-if-a-file-is-binary-non-text-in-python # Might treat UTF-16 files also as binary. def is_textfile(filename): fin = open(filename, 'rb') try: CHUNKSIZE = 4096 while 1: chunk = fin.read(CHUNKSIZE) if sys.version_info.major == 2: if '\0' in chunk: return False else: if 0 in chunk: return False if len(chunk) < CHUNKSIZE: break finally: fin.close() return True def add_escape(str): ''' Add an escape character for special regex characters''' out = '' for c in str: if c in '.*[]|': out += '\\' out += c return out # Return tuple with nr of files, nr of lines, nr of code lines, # nr of comment lines, nr of blank lines, and nr of header lines. # linecomm: comment marker for a single line # scomm: start block comment marker (empty is no block comments) # ecomm: end block comment marker # basic: False = only count lines with >= 1 alphanumeric char and # count header separately def countcodecomm (filename, linecomm, scomm='', ecomm='', basic=False): f = open(filename) nhdr = 0 nblank = 0 ncomm = 0 ncode = 0 nline = 0 skipHeader = not basic blockComm = False if len(scomm) > 0: scomm_esc = add_escape(scomm) ecomm_esc = add_escape(scomm) reComm2a = re.compile('\s*' + scomm_esc + '\s*' + ecomm_esc + '\s*') reComm2b = re.compile('\s*' + scomm_esc + '(.*)' + ecomm_esc + '\s*') reSComm = re.compile(scomm_esc) reEComm = re.compile(ecomm_esc) reTillSComm = re.compile('.*' + scomm_esc + '\s*') reTillEComm = re.compile('.*' + ecomm_esc + '\s*') reFromSComm = re.compile('\s*' + scomm_esc + '.*') reFromEComm = re.compile('\s*' + ecomm_esc + '.*') # Loop over all lines in the file. for line in f: nline += 1 # Skip file header till first non-comment line. # This header is usually the licensing info. if skipHeader: if len(linecomm) > 0 and line[:len(linecomm)] == linecomm: nhdr += 1 continue skipHeader = False # Remove leading and trailing whitespace (including newline) line = line.strip() if len(line) == 0: nblank += 1 else: # Handle lines in a block comment. if blockComm: if reEComm.search (line): # End of block comment blockComm = False # Remove the part until the comment marker. # If nothing left, it can be a comment line. l1 = reTillEComm.sub ('', line) if len(l1) == 0 or (not basic and not hasAlphaNum(l1)): if basic or hasAlphaNum(reFromEComm.sub ('', line)): ncomm += 1 continue line = l1 else: # A line inside a block comment # Only count as comment if an alphanumeric in it if basic or hasAlphaNum(line): ncomm += 1 continue # Test for start of block comment. hasBlCom = False if len(scomm) > 0: # Remove empty block comments on a single line. l1 = reComm2a.sub ('', line) if len(l1) == 0: # If nothing left, the line contains 'scomm ecomm' only. if basic: ncomm += 1 continue # Remove non-empty block comments on a single line. l1 = reComm2b.sub ('', l1) if l1 != line: hasBlCom = hasAlphaNum(reComm2b.sub (r'{\1}', line)) if len(l1) == 0: # Nothing left; count if appropriate. if basic or hasBlCom: ncomm += 1 continue line = l1 # Check for the start of a block comment if reSComm.search (line): blockComm = True # Remove the part past the comment marker. # If nothing left, it can be a comment line. l1 = reFromSComm.sub ('', line) if len(l1) == 0: if basic or hasBlCom or hasAlphaNum(reTillSComm.sub ('', line)): ncomm += 1 continue line = l1 # A code or a single comment line # Count if it contains an alphanumeric character. if basic or hasAlphaNum(line): if len(linecomm) > 0 and line[:len(linecomm)] == linecomm: ncomm += 1 else: ncode += 1 elif hasBlCom: # Also count as comment if there was a comment block. ncomm += 1 return (1, nline, ncode, ncomm, nblank, nhdr) def printHeader(): sys.stdout.write ('%9s%7s%9s%9s%16s%16s%9s%9s\n' % ('Type','Files','Lines','Code','Comment','Blank','Header','Other')) def printCount(file, type, cnt, ccperc): perc = [0.,0.] t = cnt[1] if ccperc: t = cnt[2] + cnt[3] # code + comment if t > 0: for i in (0,1): perc[i] = 100. * cnt[i+2] / t file.write ('%9s %6d %8d %8d %5.1f%% %8d %5.1f%% %8d %8d %8d\n' % (type, cnt[0], cnt[1], cnt[2], perc[0], cnt[3], perc[1], cnt[4], cnt[5], cnt[1]-cnt[2]-cnt[3]-cnt[4]-cnt[5])) else: if cnt[0] > 0: file.write ('%9s %6d\n' % (type, cnt[0])) # Count another file. # If present, use the shebang to derive the file type. # Otherwise count it as unknown. def countother(filename, basic, usecode): f = open(filename) nline = 0 nblank = 0 # Test first line for shebang. for line in f: if line[:2] == '#!': # Remove shebang, whitespace and comment. line = line[2:].strip() recomm = re.compile('#.*') line = recomm.sub('', line) # Remove till last slash and optionally env. rescr = re.compile('.*/') reenv = re.compile('env\s\s*') line = rescr.sub('', line) ext = reenv.sub('', line).lower() # Count a known file type for (type,exts,extre,filenms,comm,scomm,ecomm,ctyp) in types: if ext in exts: if usecode and ctyp==0: return (type, ctyp, (1,0,0,0,0,0)) return (type, ctyp, countcodecomm (filename,comm,scomm,ecomm,basic)) line = line.strip() if len(line) == 0: nblank += 1 nline += 1 # Unknown file type, nothing to be counted. if usecode: return ('unknown', 0, (1,0,0,0,0,0)) return ('unknown', 0, (1,nline,0,0,nblank,0)) def countfiles(dirname, test, basic, ccperc, verbose, printlevel, level, usecode, dosum, warn_unknown): sums = [{}, {}] for t in types: sums[0][t[0]] = [0,0,0,0,0,0] sums[1][t[0]] = [0,0,0,0,0,0] # Determine if it is a test directory. inx = 0 if test and os.path.basename(dirname) == 'test': inx = 1 files = os.listdir(dirname) for file in files: if file not in ['.git', '.svn', '.cvs', 'CVS', 'doc']: ffile = os.path.join(dirname,file) try: mode = os.lstat(ffile).st_mode except OSError: sys.stderr.write ('No such file: %s\n' % ffile) continue if stat.S_ISLNK(mode): # skip symlinks because casacore contains symlink to itself continue elif stat.S_ISDIR(mode): cnts = countfiles (ffile, test, basic, ccperc, verbose, printlevel, level+1, usecode, dosum, warn_unknown) for j in [0,1]: for t in types: for i in range(len(sums[j][t[0]])): sums[j][t[0]][i] += cnts[j][t[0]][i] elif stat.S_ISREG(mode): if not is_textfile(ffile): type = 'binary' ctyp = 0 cnt = (1,0,0,0,0,0) else: fnd = False (root,ext) = os.path.splitext(ffile) if len(ext) > 0: ext = ext[1:] # remove . for (type,exts,extre,filenms,comm,scomm,ecomm,ctyp) in types: if file in filenms or ext in exts or (not extre is None and extre.match(ext)): if type == 'ignore' or (usecode and ctyp==0): cnt = (1,0,0,0,0,0) else: cnt = countcodecomm (ffile,comm,scomm,ecomm,basic) fnd = True break if not fnd: (type,ctyp,cnt) = countother (ffile, basic, usecode) if not usecode or ctyp != 0: for i in range(len(cnt)): sums[inx][type][i] += cnt[i] if type == 'unknown': if warn_unknown: sys.stderr.write ('Unknown type: %s\n' % ffile) elif verbose: sys.stderr.write ('** %s\n' % ffile) printCount (sys.stderr, type, cnt, ccperc); if level <= printlevel: bl = level*2*' ' for j in [0,1]: first = True sumall = [0,0,0,0,0,0] for t in types: c = sums[j][t[0]] if c[0] > 0: if first: tc = '' if j==1: tc = ' testcode' sys.stdout.write ('%s%s%s\n' % (bl,dirname,tc)) first = False if dosum: for i in range(len(c)): sumall[i] += c[i] else: printCount (sys.stdout, t[0], c, ccperc); if dosum: printCount (sys.stdout, '', sumall, ccperc) return sums def testit(): print (countcodecomm ('/Users/diepen/testcnt1', '#')) print (countcodecomm ('/Users/diepen/testcnt2', '#', '"""', '"""')) print (countcodecomm ('/Users/diepen/testcnt1', '#', '', '', False)) print (countcodecomm ('/Users/diepen/testcnt2', '#', '"""', '"""', False)) if __name__ == '__main__': # Define the options. parser = argparse.ArgumentParser(prog='PROG') parser.add_argument('-b', '--basic', help='count copyright header and lines without an alphanumeric character as code/comment lines', action='store_true') parser.add_argument('-c', '--code', help='only use source files containing code (e.g. no .parset)', action='store_true') parser.add_argument('-s', '--sum', help='only calculate and print the sum of all file types', action='store_true') parser.add_argument('-l', '--limitperc', help='limit to the nr of code and comment lines to determine percentages', action='store_true') parser.add_argument('-p', '--printlevel', type=int, default=0, help='first directory level to print (default 0 (=top))') parser.add_argument('-w', '--warn_unknown', help='warn if a file with an unknown type is found', action='store_true') parser.add_argument('-d', '--displaytypes', help='display the currently recognized file types (full info with -v)', action='store_true') parser.add_argument('-t', '--testinclude', help='do not count test directories separately', action='store_true') parser.add_argument('-v', '--verbose', help='print count for each source file', action='store_true') parser.add_argument('directory', nargs='?', default='.', help='name of top directory to count source files (default is .)') # If nothing given, do test and show options. if len(sys.argv) == 1: #print 'Testing the script ...' #testit() print ('') print ('countcode counts per known source file type the number of source lines in the') print (' files in the given directory and recursively in its subdirectories.') print ('It supports many file types. The type is recognized from the file name extension') print (' or the shebang script type. Use -s to see all supported types.') print ('The following line types are counted:') print (' code: pure code lines)') print (' comment: pure comment lines') print (' blank: empty lines or lines containing whitespace only') print (' header: the copyright header (leading comment lines)') print (' other: all other lines (e.g., single {, /*, etc.)') print ('Unless -b is given, a pure code or comment line has to contain an alphanumeric') print (' character; e.g., a single } does not count as code line.') print ('It calculates the percentage of code and comment lines in the total number of') print (' lines or (if -l is given) in the sum of code and comment lines.') print ('Unless -t is given, files in test directories are counted separately.') print ('Normal output is written on stdout; verbose on stderr.') print ('Files with an unknown type are reported on stderr.') print ('Note that -bt should give about the same results as a tool like cloc.') print ('') parser.parse_args(['-h']) else: values = parser.parse_args(sys.argv[1:]) if values.displaytypes: showTypes (values.verbose) else: dirname = values.directory test = not values.testinclude # Remove possible trailing slash if len(dirname) > 1 and dirname[-1] == '/': dirname = dirname[:-1] sys.stdout.write ('%s Count %s test=%d basic=%d limitperc=%d code=%d\n'%(time.ctime(),dirname,test,values.basic,values.limitperc,values.code)) printHeader() countfiles (dirname, test, values.basic, values.limitperc, values.verbose, values.printlevel, 0, values.code, values.sum, values.warn_unknown) printHeader() sys.stdout.write ('%s Count %s test=%d basic=%d limitperc=%d code=%d\n'%(time.ctime(),dirname,test,values.basic,values.limitperc,values.code)) casacore-3.7.1/casa/apps/plotmemory000077500000000000000000000015071476623553700173360ustar00rootroot00000000000000#!/usr/bin/env python import numpy as np #import matplotlib.pyplot as plt import argparse parser = argparse.ArgumentParser(description = "Show plots of the output of watchmemory") parser.add_argument("input", help="Input log file") parser.add_argument("-1", "--onlymem", help="Show only memory, not CPU", action= "store_true") args = parser.parse_args() # The input file should contain a header line followed by a line per second # containing the memory and CPU percentage. log = np.rot90(np.loadtxt(args.input, skiprows=1)) x = np.arange(len(log[0])) plt.figure(1) if not args.onlymem: plt.subplot(211) plt.plot(x, log[0]) plt.xlabel('Seconds since start') plt.ylabel('Memory Usage [%]') if not args.onlymem: plt.subplot(212) plt.plot(x, log[1]) plt.xlabel('Seconds since start') plt.ylabel('CPU usage [%]') plt.show() casacore-3.7.1/casa/apps/watchmemory000077500000000000000000000043351476623553700174700ustar00rootroot00000000000000#!/usr/bin/env python import time import string import sys import os def get_cpumem(pid): # http://unix.stackexchange.com/questions/58539/top-and-ps-not-showing-the-same-cpu-result if pid.isdigit(): cmd="ps -p "+str(pid)+" -o pid,%cpu,%mem,etime,cputime" else: cmd="ps -C "+pid+" -o pid,%cpu,%mem,etime,cputime" psoutput=os.popen(cmd).read().split("\n") if len(psoutput) < 2: return None elif len(psoutput) > 2 and len(psoutput[2]) > 0: sys.stderr.write("warning: multiple processes called "+str(pid)+", picking first\n") d = psoutput[1].split() etime=parsetime(d[3]) cputime=parsetime(d[4]) return (float(d[1]), float(d[2]), etime,cputime) def parsetime(timestr): ''' convert [[hh:]:mm:]ss into seconds ''' timelst=timestr.split(':') secs=int(timelst[-1]) # seconds if len(timelst)>1: secs+=int(timelst[-2])*60 # minutes if len(timelst)>2: secs+=int(timelst[-3])*3600 #hours return float(secs) if __name__ == '__main__': if not len(sys.argv) == 2: sys.stderr.write("usage: %s PID / process name (e.g. NDPPP)\n" % sys.argv[0]) sys.stderr.write("\n") sys.stderr.write("This script collects %memory and %CPU used by a program.\n") sys.stderr.write("Standard out should be piped to a file to be plotted with plotmemory.\n") exit(2) print("%CPU\tMEM") prevetime,prevcputime=None,None nummisses=0 try: while True and nummisses<60: x = get_cpumem(sys.argv[1]) if not x: sys.stderr.write("no such process: "+sys.argv[1]+"\n") nummisses+=1 else: nummisses=0 if prevetime==None or prevetime==x[2]: cpuperc=x[0] sys.stderr.write("guessing cpu perc\n") else: # Update x[0] to be instantaneous CPU % cpuperc=(x[3]-prevcputime*1.0)/(x[2]-prevetime*1.0)*100. prevetime=x[2] prevcputime=x[3] print("%.2f\t%.2f" % (cpuperc, x[1])) sys.stdout.flush() time.sleep(1) except KeyboardInterrupt: print('') exit(0) casacore-3.7.1/casa/casa.dox000066400000000000000000000057751476623553700156740ustar00rootroot00000000000000//# casa.dox: doxygen description of casa package //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ namespace casacore { // \defgroup casa casa package (libcasa_casa) // The casa package contains the core modules. // // It is important to note that most of the code has been developed before STL // came into existence. Several classes (such as Map) in module Containers // are superseded by their STL counterparts and are not used anymore // in the Casacore code. However, for backward compatibility the // classes still exist, but are not built unless a special // Cmake option is given. // // A few pre-STL classes still exist though. // Regex and String are derived from their STL counterparts because // they add functionality. } casacore-3.7.1/casa/complex.h000066400000000000000000000034151476623553700160560ustar00rootroot00000000000000//# complex.h: import std complex functions into namespace casacore //# Copyright (C) 2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_STD_COMPLEX_H #define CASA_STD_COMPLEX_H // Make sure any special macros are set #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN using std::real; using std::imag; using std::norm; using std::abs; using std::arg; //using std::conj; using std::cos; using std::cosh; using std::sin; using std::sinh; using std::tan; using std::tanh; using std::exp; using std::log; using std::sqrt; using std::polar; using std::pow; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/config.h.in000066400000000000000000000025561476623553700162660ustar00rootroot00000000000000//# config.h: build configuration //# Copyright (C) 2015 //# National Astronomical Observatory of Japan //# 2-21-1, Osawa, Mitaka, Tokyo, 181-8588, Japan. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef CASA_CONFIG_H #define CASA_CONFIG_H #define CASA_DEFAULT_ALIGNMENT (@CASA_DEFAULT_ALIGNMENT@UL) #endif // CASA_CONFIG_Hcasacore-3.7.1/casa/fstream.h000066400000000000000000000030451476623553700160470ustar00rootroot00000000000000//# fstream.h: Interim solution for standard/nonstandard system fstream //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_FSTREAM_H #define CASA_FSTREAM_H //# Define the C standard C++ include file. #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN using std::fstream; using std::ifstream; using std::ofstream; using std::filebuf; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/iomanip.h000066400000000000000000000031331476623553700160400ustar00rootroot00000000000000//# iomanip.h: Interim solution for standard/nonstandard system iomanip //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_IOMANIP_H #define CASA_IOMANIP_H //# Define the C standard C++ include file. #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN using std::setw; using std::setfill; using std::setprecision; using std::setbase; using std::resetiosflags; using std::setiosflags; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/iosfwd.h000066400000000000000000000036511476623553700157040ustar00rootroot00000000000000//# iosfwd.h: Interim solution for standard/nonstandard system iosfwd //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_IOSFWD_H #define CASA_IOSFWD_H //# Make sure any special macros are set #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Define the IO system forward declarations. Note that if fully standard // suppliant, the iosfwd will also forward declare the stringstream classes. // If strstream classes have to be known, include // instead. using std::ios; using std::istream; using std::ostream; using std::iostream; using std::streambuf; using std::filebuf; using std::ifstream; using std::ofstream; using std::fstream; using std::fpos; using std::streampos; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/iosstrfwd.h000066400000000000000000000034001476623553700164250ustar00rootroot00000000000000//# iosstrfwd.h: Interim solution for standard/nonstandard system iosfwd //# Copyright (C) 2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_IOSSTRFWD_H #define CASA_IOSSTRFWD_H //# Make sure any special macros are set #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Define the IO system forward declarations for strings (strstream) as well // After changeover to sstream, iosfwd takes care of it all. // If no strstream classes have to be declared, use // instead. using std::istringstream; using std::ostringstream; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/iostream.h000066400000000000000000000044541476623553700162360ustar00rootroot00000000000000//# iostream.h: Interim solution for standard/nonstandard system iostream //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_IOSTREAM_H #define CASA_IOSTREAM_H //# Define the C standard C++ include file. //# This is an interim solution to cater for the SGI non-existence of //# them (e.g. ) //# Make sure any special macros are set #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN using std::istream; using std::ostream; using std::iostream; using std::cin; using std::cout; using std::cerr; using std::endl; using std::flush; using std::ws; using std::ios; using std::streampos; using std::streamoff; using std::streamsize; using std::dec; using std::hex; using std::oct; using std::internal; using std::left; using std::right; using std::fixed; using std::scientific; using std::boolalpha; using std::noboolalpha; using std::showbase; using std::noshowbase; using std::showpoint; using std::noshowpoint; using std::showpos; using std::noshowpos; using std::skipws; using std::noskipws; using std::uppercase; using std::nouppercase; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/istream.h000066400000000000000000000027601476623553700160550ustar00rootroot00000000000000//# istream.h: Interim solution for standard/nonstandard system istream //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_ISTREAM_H #define CASA_ISTREAM_H //# Define the C standard C++ include file. #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN using std::istream; using std::ws; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/math.h000066400000000000000000000035351476623553700153430ustar00rootroot00000000000000//# math.h: Interim solution for standard/nonstandard system cmath //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_STD_MATH_H #define CASA_STD_MATH_H //# Define the C standard C++ include file. #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN using std::abs; using std::fabs; using std::cos; using std::cosh; using std::sin; using std::sinh; using std::tan; using std::tanh; using std::asin; using std::acos; using std::atan; using std::atan2; using std::exp; using std::log; using std::log10; using std::sqrt; using std::pow; using std::floor; using std::ceil; using std::fmod; using ::erf; using ::erfc; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/namespace.h000066400000000000000000000001741476623553700163420ustar00rootroot00000000000000#ifndef CASACORE_NAMESPACE_H #define CASACORE_NAMESPACE_H #include using namespace casacore; #endif casacore-3.7.1/casa/ostream.h000066400000000000000000000030331476623553700160550ustar00rootroot00000000000000//# ostream.h: Interim solution for standard/nonstandard system ostream //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_OSTREAM_H #define CASA_OSTREAM_H //# Define the C standard C++ include file. #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN using std::ostream; using std::endl; using std::ends; using std::flush; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/sstream.h000066400000000000000000000033211476623553700160610ustar00rootroot00000000000000//# sstream.h: Interim solution for standard/nonstandard system sstream //# Copyright (C) 2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_SSTREAM_H #define CASA_SSTREAM_H //# Define the C standard C++ include file. //# This is an interim solution to cater for the SGI non-existence of //# them (e.g. ) //# Make sure any special macros are set #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN using std::ostringstream; using std::istringstream; using std::stringstream; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/stdexcept.h000066400000000000000000000032331476623553700164100ustar00rootroot00000000000000//# stdexcept.h: Make standard exceptions availabe in casa namespace //# Copyright (C) 2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_STDEXCEPT_H #define CASA_STDEXCEPT_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN using std::exception; using std::logic_error; using std::domain_error; using std::invalid_argument; using std::length_error; using std::out_of_range; using std::runtime_error; using std::range_error; using std::overflow_error; using std::underflow_error; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casa/stdio.h000066400000000000000000000025551476623553700155350ustar00rootroot00000000000000//# stdio.h: Interim solution for standard/nonstandard system cstdio //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_STDIO_H #define CASA_STDIO_H //# Define the C standard C++ include file. #include #include #endif casacore-3.7.1/casa/stdlib.h000066400000000000000000000025621476623553700156720ustar00rootroot00000000000000//# stdlib.h: Interim solution for standard/nonstandard system cstdlib //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_STDLIB_H #define CASA_STDLIB_H //# Define the C standard C++ include file. #include #include #endif casacore-3.7.1/casa/stdmap.h000066400000000000000000000030151476623553700156730ustar00rootroot00000000000000//# stdmap.h: Interim solution for standard/nonstandard system map //# Copyright (C) 2001,2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_STDMAP_H #define CASA_STDMAP_H //# Define the C standard C++ include file. #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN using std::map; using std::multimap; using std::pair; using std::allocator; using std::less; } #endif casacore-3.7.1/casa/stdvector.h000066400000000000000000000027071476623553700164270ustar00rootroot00000000000000//# vector.h: Interim solution for standard/nonstandard system vector //# Copyright (C) 2002,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_STD_VECTOR_H #define CASA_STD_VECTOR_H //# Define the standard C++ include file. #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN using std::vector; } #endif casacore-3.7.1/casa/string.h000066400000000000000000000025721476623553700157200ustar00rootroot00000000000000//# string.h: Interim solution for standard/nonstandard system cstring //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_STD_STRING_H #define CASA_STD_STRING_H //# Define the C standard C++ include file. #include #include #endif casacore-3.7.1/casa/test/000077500000000000000000000000001476623553700152125ustar00rootroot00000000000000casacore-3.7.1/casa/test/CMakeLists.txt000066400000000000000000000004351476623553700177540ustar00rootroot00000000000000set (tests tTypes ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_casa) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/casa/test/COPYING000066400000000000000000000430761476623553700162570ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy 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., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. casacore-3.7.1/casa/test/demo000066400000000000000000000017041476623553700160630ustar00rootroot00000000000000#! /bin/csh -f # if ($?DISPLAY == 0) then echo DISPLAY env. var. is not set" exit 0 endif if ($?KHOROS_HOME == 0) then foreach try (/aips2/khoros) echo "Trying KHOROS_HOME as $try..." if (-d $try) then setenv KHOROS_HOME $try break endif end if ($?KHOROS_HOME == 0) then echo "Enter a directory for KHOROS_HOME." set answer = $< setenv KHOROS_HOME $answer endif if ($?KHOROS_HOME == 0) then echo "Cannot find suitable KHOROS_HOME, can't run the demo" exit 1 endif endif setenv KHOROS_MAIL "$LOGNAME" setenv KHOROS_LOG "$HOME/khoros.cmdlog" setenv KHOROS_VERBOSE "no" setenv TMPDIR "/usr/tmp" setenv KHOROS_KEYWORDS "$KHOROS_HOME/repos/Keywords" setenv KHOROS_TOOLBOX "$KHOROS_HOME/repos/Toolboxes" echo "Loading KHOROS from $KHOROS_HOME..." set path=(. $KHOROS_HOME/bin $path) rehash xrdb -m $KHOROS_HOME/dotfiles/Xdefaults.snazzy echo "Running CANTATA..." cantata -restore demo.workspace.Z casacore-3.7.1/casa/test/tTypes.cc000066400000000000000000000034371476623553700170200ustar00rootroot00000000000000//# tTypes.cc: Test that fundamental types are valid for Casacore //# Copyright (C) 1998 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include int main() { { // Make sure that uChar is unsigned. uChar cc = 0; cc--; Int C = cc; AlwaysAssertExit(C == 255); } { // Make sure the sizes are OK. AlwaysAssertExit(sizeof(Int) == 4 && sizeof(uInt) == 4 && sizeof(Short) == 2 && sizeof(uShort) == 2 && sizeof(Int64) == 8 && sizeof(uInt64) == 8 && sizeof(Float) == 4 && sizeof(Double) == 8 && sizeof(lDouble) >= sizeof(Double)); } return 0; } casacore-3.7.1/casa/typeinfo.h000066400000000000000000000025721476623553700162470ustar00rootroot00000000000000//# typeinfo.h: Interim solution for standard/nonstandard system typeinfo //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_TYPEINFO_H #define CASA_TYPEINFO_H //# Define the C standard C++ include file. #include #include #endif casacore-3.7.1/casa/vector.h000066400000000000000000000027071476623553700157140ustar00rootroot00000000000000//# vector.h: Interim solution for standard/nonstandard system vector //# Copyright (C) 2002,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_STD_VECTOR_H #define CASA_STD_VECTOR_H //# Define the standard C++ include file. #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN using std::vector; } #endif casacore-3.7.1/casa/version.cc000066400000000000000000000030221476623553700162240ustar00rootroot00000000000000//# version.cc: Get casacore version //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Get the casacore version. const char* getVersion() { return CASACORE_VERSION; } // Get the version of casacore on CASA's vendor branch const std::string getVersionCASA() { return CASACORE_VERSION; } } //# NAMESPACE CASACORE - END casacore-3.7.1/casa/version.h.in000066400000000000000000000037471476623553700165110ustar00rootroot00000000000000//# version.h: Get casacore version //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef CASA_VERSION_H #define CASA_VERSION_H #include #include #define CASACORE_MAJOR_VERSION @PROJECT_VERSION_MAJOR@ #define CASACORE_MINOR_VERSION @PROJECT_VERSION_MINOR@ #define CASACORE_PATCH_VERSION @PROJECT_VERSION_PATCH@ #define CASACORE_VERSION CASACORE_STRINGIFY(@PROJECT_VERSION@) namespace casacore { //# NAMESPACE CASACORE - BEGIN // Get the casacore version. extern "C" const char* getVersion(); // Get the version of casacore on CASA's vendor branch // Note: CASA's private version of casacore has a lifecycle // which is not necessarily identical to versions of casacore // elsewhere. This function returns the version of casacore // on CASA's vendor branch. const std::string getVersionCASA(); } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/casacore000077700000000000000000000000001476623553700151022.ustar00rootroot00000000000000casacore-3.7.1/casacore.pc.in000066400000000000000000000005141476623553700160350ustar00rootroot00000000000000prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=${prefix} libdir=${exec_prefix}/lib includedir=${prefix}/include Name: Casacore Description: @CMAKE_PROJECT_DESCRIPTION@ Version: @PROJECT_VERSION@ Requires: @pc_req_public@ Requires.private: @pc_req_private@ Libs: -L${libdir} @PRIVATE_LIBS@ Cflags: -I${includedir} -I@WCSLIB_INCLUDE_DIR@ casacore-3.7.1/changescripts/000077500000000000000000000000001476623553700161615ustar00rootroot00000000000000casacore-3.7.1/changescripts/dataman.sed000066400000000000000000000156511476623553700202730ustar00rootroot00000000000000s%/Tables/BaseMappedArrayEngine\.h%/DataMan/BaseMappedArrayEngine.h% s%/Tables/BaseMappedArrayEngine\.tcc%/DataMan/BaseMappedArrayEngine.tcc% s%/Tables/BitFlagsEngine\.cc%/DataMan/BitFlagsEngine.cc% s%/Tables/BitFlagsEngine\.h%/DataMan/BitFlagsEngine.h% s%/Tables/BitFlagsEngine\.tcc%/DataMan/BitFlagsEngine.tcc% s%/Tables/CompressComplex\.cc%/DataMan/CompressComplex.cc% s%/Tables/CompressComplex\.h%/DataMan/CompressComplex.h% s%/Tables/CompressFloat\.cc%/DataMan/CompressFloat.cc% s%/Tables/CompressFloat\.h%/DataMan/CompressFloat.h% s%/Tables/DataManAccessor\.cc%/DataMan/DataManAccessor.cc% s%/Tables/DataManAccessor\.h%/DataMan/DataManAccessor.h% s%/Tables/DataManError\.cc%/DataMan/DataManError.cc% s%/Tables/DataManError\.h%/DataMan/DataManError.h% s%/Tables/DataManInfo\.cc%/DataMan/DataManInfo.cc% s%/Tables/DataManInfo\.h%/DataMan/DataManInfo.h% s%/Tables/DataManager\.cc%/DataMan/DataManager.cc% s%/Tables/DataManager\.h%/DataMan/DataManager.h% s%/Tables/ForwardCol\.cc%/DataMan/ForwardCol.cc% s%/Tables/ForwardCol\.h%/DataMan/ForwardCol.h% s%/Tables/ForwardColRow\.cc%/DataMan/ForwardColRow.cc% s%/Tables/ForwardColRow\.h%/DataMan/ForwardColRow.h% s%/Tables/ISMBase\.cc%/DataMan/ISMBase.cc% s%/Tables/ISMBase\.h%/DataMan/ISMBase.h% s%/Tables/ISMBucket\.cc%/DataMan/ISMBucket.cc% s%/Tables/ISMBucket\.h%/DataMan/ISMBucket.h% s%/Tables/ISMColumn\.cc%/DataMan/ISMColumn.cc% s%/Tables/ISMColumn\.h%/DataMan/ISMColumn.h% s%/Tables/ISMIndColumn\.cc%/DataMan/ISMIndColumn.cc% s%/Tables/ISMIndColumn\.h%/DataMan/ISMIndColumn.h% s%/Tables/ISMIndex\.cc%/DataMan/ISMIndex.cc% s%/Tables/ISMIndex\.h%/DataMan/ISMIndex.h% s%/Tables/IncrStManAccessor\.cc%/DataMan/IncrStManAccessor.cc% s%/Tables/IncrStManAccessor\.h%/DataMan/IncrStManAccessor.h% s%/Tables/IncrementalStMan\.cc%/DataMan/IncrementalStMan.cc% s%/Tables/IncrementalStMan\.h%/DataMan/IncrementalStMan.h% s%/Tables/MSMBase\.cc%/DataMan/MSMBase.cc% s%/Tables/MSMBase\.h%/DataMan/MSMBase.h% s%/Tables/MSMColumn\.cc%/DataMan/MSMColumn.cc% s%/Tables/MSMColumn\.h%/DataMan/MSMColumn.h% s%/Tables/MSMDirColumn\.cc%/DataMan/MSMDirColumn.cc% s%/Tables/MSMDirColumn\.h%/DataMan/MSMDirColumn.h% s%/Tables/MSMIndColumn\.cc%/DataMan/MSMIndColumn.cc% s%/Tables/MSMIndColumn\.h%/DataMan/MSMIndColumn.h% s%/Tables/MappedArrayEngine\.h%/DataMan/MappedArrayEngine.h% s%/Tables/MappedArrayEngine\.tcc%/DataMan/MappedArrayEngine.tcc% s%/Tables/MemoryStMan\.cc%/DataMan/MemoryStMan.cc% s%/Tables/MemoryStMan\.h%/DataMan/MemoryStMan.h% s%/Tables/RetypedArrayEngine\.h%/DataMan/RetypedArrayEngine.h% s%/Tables/RetypedArrayEngine\.tcc%/DataMan/RetypedArrayEngine.tcc% s%/Tables/RetypedArraySetGet\.h%/DataMan/RetypedArraySetGet.h% s%/Tables/RetypedArraySetGet\.tcc%/DataMan/RetypedArraySetGet.tcc% s%/Tables/SSMBase\.cc%/DataMan/SSMBase.cc% s%/Tables/SSMBase\.h%/DataMan/SSMBase.h% s%/Tables/SSMColumn\.cc%/DataMan/SSMColumn.cc% s%/Tables/SSMColumn\.h%/DataMan/SSMColumn.h% s%/Tables/SSMDirColumn\.cc%/DataMan/SSMDirColumn.cc% s%/Tables/SSMDirColumn\.h%/DataMan/SSMDirColumn.h% s%/Tables/SSMIndColumn\.cc%/DataMan/SSMIndColumn.cc% s%/Tables/SSMIndColumn\.h%/DataMan/SSMIndColumn.h% s%/Tables/SSMIndStringColumn\.cc%/DataMan/SSMIndStringColumn.cc% s%/Tables/SSMIndStringColumn\.h%/DataMan/SSMIndStringColumn.h% s%/Tables/SSMIndex\.cc%/DataMan/SSMIndex.cc% s%/Tables/SSMIndex\.h%/DataMan/SSMIndex.h% s%/Tables/SSMStringHandler\.cc%/DataMan/SSMStringHandler.cc% s%/Tables/SSMStringHandler\.h%/DataMan/SSMStringHandler.h% s%/Tables/ScaledArrayEngine\.h%/DataMan/ScaledArrayEngine.h% s%/Tables/ScaledArrayEngine\.tcc%/DataMan/ScaledArrayEngine.tcc% s%/Tables/ScaledComplexData\.h%/DataMan/ScaledComplexData.h% s%/Tables/ScaledComplexData\.tcc%/DataMan/ScaledComplexData.tcc% s%/Tables/StArrAipsIO\.cc%/DataMan/StArrAipsIO.cc% s%/Tables/StArrAipsIO\.h%/DataMan/StArrAipsIO.h% s%/Tables/StArrayFile\.cc%/DataMan/StArrayFile.cc% s%/Tables/StArrayFile\.h%/DataMan/StArrayFile.h% s%/Tables/StIndArrAIO\.cc%/DataMan/StIndArrAIO.cc% s%/Tables/StIndArrAIO\.h%/DataMan/StIndArrAIO.h% s%/Tables/StIndArray\.cc%/DataMan/StIndArray.cc% s%/Tables/StIndArray\.h%/DataMan/StIndArray.h% s%/Tables/StManAipsIO\.cc%/DataMan/StManAipsIO.cc% s%/Tables/StManAipsIO\.h%/DataMan/StManAipsIO.h% s%/Tables/StManColumn\.cc%/DataMan/StManColumn.cc% s%/Tables/StManColumn\.h%/DataMan/StManColumn.h% s%/Tables/StandardStMan\.cc%/DataMan/StandardStMan.cc% s%/Tables/StandardStMan\.h%/DataMan/StandardStMan.h% s%/Tables/StandardStManAccessor\.cc%/DataMan/StandardStManAccessor.cc% s%/Tables/StandardStManAccessor\.h%/DataMan/StandardStManAccessor.h% s%/Tables/TSMColumn\.cc%/DataMan/TSMColumn.cc% s%/Tables/TSMColumn\.h%/DataMan/TSMColumn.h% s%/Tables/TSMCoordColumn\.cc%/DataMan/TSMCoordColumn.cc% s%/Tables/TSMCoordColumn\.h%/DataMan/TSMCoordColumn.h% s%/Tables/TSMCube\.cc%/DataMan/TSMCube.cc% s%/Tables/TSMCube\.h%/DataMan/TSMCube.h% s%/Tables/TSMCubeBuff\.cc%/DataMan/TSMCubeBuff.cc% s%/Tables/TSMCubeBuff\.h%/DataMan/TSMCubeBuff.h% s%/Tables/TSMCubeMMap\.cc%/DataMan/TSMCubeMMap.cc% s%/Tables/TSMCubeMMap\.h%/DataMan/TSMCubeMMap.h% s%/Tables/TSMDataColumn\.cc%/DataMan/TSMDataColumn.cc% s%/Tables/TSMDataColumn\.h%/DataMan/TSMDataColumn.h% s%/Tables/TSMFile\.cc%/DataMan/TSMFile.cc% s%/Tables/TSMFile\.h%/DataMan/TSMFile.h% s%/Tables/TSMIdColumn\.cc%/DataMan/TSMIdColumn.cc% s%/Tables/TSMIdColumn\.h%/DataMan/TSMIdColumn.h% s%/Tables/TSMOption\.cc%/DataMan/TSMOption.cc% s%/Tables/TSMOption\.h%/DataMan/TSMOption.h% s%/Tables/TSMShape\.cc%/DataMan/TSMShape.cc% s%/Tables/TSMShape\.h%/DataMan/TSMShape.h% s%/Tables/TiledCellStMan\.cc%/DataMan/TiledCellStMan.cc% s%/Tables/TiledCellStMan\.h%/DataMan/TiledCellStMan.h% s%/Tables/TiledColumnStMan\.cc%/DataMan/TiledColumnStMan.cc% s%/Tables/TiledColumnStMan\.h%/DataMan/TiledColumnStMan.h% s%/Tables/TiledDataStMan\.cc%/DataMan/TiledDataStMan.cc% s%/Tables/TiledDataStMan\.h%/DataMan/TiledDataStMan.h% s%/Tables/TiledDataStManAccessor\.cc%/DataMan/TiledDataStManAccessor.cc% s%/Tables/TiledDataStManAccessor\.h%/DataMan/TiledDataStManAccessor.h% s%/Tables/TiledFileAccess\.cc%/DataMan/TiledFileAccess.cc% s%/Tables/TiledFileAccess\.h%/DataMan/TiledFileAccess.h% s%/Tables/TiledFileHelper\.cc%/DataMan/TiledFileHelper.cc% s%/Tables/TiledFileHelper\.h%/DataMan/TiledFileHelper.h% s%/Tables/TiledShapeStMan\.cc%/DataMan/TiledShapeStMan.cc% s%/Tables/TiledShapeStMan\.h%/DataMan/TiledShapeStMan.h% s%/Tables/TiledStMan\.cc%/DataMan/TiledStMan.cc% s%/Tables/TiledStMan\.h%/DataMan/TiledStMan.h% s%/Tables/TiledStManAccessor\.cc%/DataMan/TiledStManAccessor.cc% s%/Tables/TiledStManAccessor\.h%/DataMan/TiledStManAccessor.h% s%/Tables/VSCEngine\.h%/DataMan/VSCEngine.h% s%/Tables/VSCEngine\.tcc%/DataMan/VSCEngine.tcc% s%/Tables/VirtArrCol\.h%/DataMan/VirtArrCol.h% s%/Tables/VirtArrCol\.tcc%/DataMan/VirtArrCol.tcc% s%/Tables/VirtColEng\.cc%/DataMan/VirtColEng.cc% s%/Tables/VirtColEng\.h%/DataMan/VirtColEng.h% s%/Tables/VirtScaCol\.h%/DataMan/VirtScaCol.h% s%/Tables/VirtScaCol\.tcc%/DataMan/VirtScaCol.tcc% s%/Tables/VirtualTaQLColumn\.cc%/DataMan/VirtualTaQLColumn.cc% s%/Tables/VirtualTaQLColumn\.h%/DataMan/VirtualTaQLColumn.h% casacore-3.7.1/changescripts/latticemath.sed000066400000000000000000000066141476623553700211640ustar00rootroot00000000000000s%/Lattices/CLIPNearest2D\.h%/LatticeMath/CLIPNearest2D.h% s%/Lattices/CLIPNearest2D\.tcc%/LatticeMath/CLIPNearest2D.tcc% s%/Lattices/CLInterpolator2D\.h%/LatticeMath/CLInterpolator2D.h% s%/Lattices/CLInterpolator2D\.tcc%/LatticeMath/CLInterpolator2D.tcc% s%/Lattices/LattStatsProgress\.cc%/LatticeMath/LattStatsProgress.cc% s%/Lattices/LattStatsProgress\.h%/LatticeMath/LattStatsProgress.h% s%/Lattices/LattStatsSpecialize\.cc%/LatticeMath/LattStatsSpecialize.cc% s%/Lattices/LattStatsSpecialize\.h%/LatticeMath/LattStatsSpecialize.h% s%/Lattices/LatticeAddNoise\.cc%/LatticeMath/LatticeAddNoise.cc% s%/Lattices/LatticeAddNoise\.h%/LatticeMath/LatticeAddNoise.h% s%/Lattices/LatticeApply\.h%/LatticeMath/LatticeApply.h% s%/Lattices/LatticeApply\.tcc%/LatticeMath/LatticeApply.tcc% s%/Lattices/LatticeCleanProgress\.cc%/LatticeMath/LatticeCleanProgress.cc% s%/Lattices/LatticeCleanProgress\.h%/LatticeMath/LatticeCleanProgress.h% s%/Lattices/LatticeCleaner\.h%/LatticeMath/LatticeCleaner.h% s%/Lattices/LatticeCleaner\.tcc%/LatticeMath/LatticeCleaner.tcc% s%/Lattices/LatticeConvolver\.h%/LatticeMath/LatticeConvolver.h% s%/Lattices/LatticeConvolver\.tcc%/LatticeMath/LatticeConvolver.tcc% s%/Lattices/LatticeFFT\.cc%/LatticeMath/LatticeFFT.cc% s%/Lattices/LatticeFFT\.h%/LatticeMath/LatticeFFT.h% s%/Lattices/LatticeFractile\.h%/LatticeMath/LatticeFractile.h% s%/Lattices/LatticeFractile\.tcc%/LatticeMath/LatticeFractile.tcc% s%/Lattices/LatticeHistProgress\.cc%/LatticeMath/LatticeHistProgress.cc% s%/Lattices/LatticeHistProgress\.h%/LatticeMath/LatticeHistProgress.h% s%/Lattices/LatticeHistSpecialize\.cc%/LatticeMath/LatticeHistSpecialize.cc% s%/Lattices/LatticeHistSpecialize\.h%/LatticeMath/LatticeHistSpecialize.h% s%/Lattices/LatticeHistograms\.h%/LatticeMath/LatticeHistograms.h% s%/Lattices/LatticeHistograms\.tcc%/LatticeMath/LatticeHistograms.tcc% s%/Lattices/LatticeProgress\.cc%/LatticeMath/LatticeProgress.cc% s%/Lattices/LatticeProgress\.h%/LatticeMath/LatticeProgress.h% s%/Lattices/LatticeSlice1D\.h%/LatticeMath/LatticeSlice1D.h% s%/Lattices/LatticeSlice1D\.tcc%/LatticeMath/LatticeSlice1D.tcc% s%/Lattices/LatticeStatistics\.h%/LatticeMath/LatticeStatistics.h% s%/Lattices/LatticeStatistics\.tcc%/LatticeMath/LatticeStatistics.tcc% s%/Lattices/LatticeStatsBase\.cc%/LatticeMath/LatticeStatsBase.cc% s%/Lattices/LatticeStatsBase\.h%/LatticeMath/LatticeStatsBase.h% s%/Lattices/LatticeTwoPtCorr\.h%/LatticeMath/LatticeTwoPtCorr.h% s%/Lattices/LatticeTwoPtCorr\.tcc%/LatticeMath/LatticeTwoPtCorr.tcc% s%/Lattices/LineCollapser\.h%/LatticeMath/LineCollapser.h% s%/Lattices/LineCollapser\.tcc%/LatticeMath/LineCollapser.tcc% s%/Lattices/MultiTermLatticeCleaner\.h%/LatticeMath/MultiTermLatticeCleaner.h% s%/Lattices/MultiTermLatticeCleaner\.tcc%/LatticeMath/MultiTermLatticeCleaner.tcc% s%/Lattices/TiledCollapser\.h%/LatticeMath/TiledCollapser.h% s%/Lattices/TiledCollapser\.tcc%/LatticeMath/TiledCollapser.tcc% s%/Lattices/MaskedLatticeStatsDataProvider\.h%/LatticeMath/MaskedLatticeStatsDataProvider.h% s%/Lattices/LatticeStatsDataProvider\.h%/LatticeMath/LatticeStatsDataProvider.h% s%/Lattices/MaskedLatticeStatsDataProvider\.tcc%/LatticeMath/MaskedLatticeStatsDataProvider.tcc% s%/Lattices/LatticeStatsDataProvider\.tcc%/LatticeMath/LatticeStatsDataProvider.tcc% s%/Lattices/LatticeStatsDataProviderBase\.tcc%/LatticeMath/LatticeStatsDataProviderBase.tcc% s%/Lattices/LatticeStatsDataProviderBase\.h%/LatticeMath/LatticeStatsDataProviderBase.h% casacore-3.7.1/changescripts/lel.sed000066400000000000000000000042571476623553700174420ustar00rootroot00000000000000s%/Lattices/LELArray\.h%/LEL/LELArray.h% s%/Lattices/LELArray\.tcc%/LEL/LELArray.tcc% s%/Lattices/LELArrayBase\.cc%/LEL/LELArrayBase.cc% s%/Lattices/LELArrayBase\.h%/LEL/LELArrayBase.h% s%/Lattices/LELAttribute\.cc%/LEL/LELAttribute.cc% s%/Lattices/LELAttribute\.h%/LEL/LELAttribute.h% s%/Lattices/LELBinary\.h%/LEL/LELBinary.h% s%/Lattices/LELBinary\.tcc%/LEL/LELBinary.tcc% s%/Lattices/LELBinary2\.cc%/LEL/LELBinary2.cc% s%/Lattices/LELBinary2\.h%/LEL/LELBinary2.h% s%/Lattices/LELBinaryEnums\.h%/LEL/LELBinaryEnums.h% s%/Lattices/LELCondition\.h%/LEL/LELCondition.h% s%/Lattices/LELCondition\.tcc%/LEL/LELCondition.tcc% s%/Lattices/LELConvert\.h%/LEL/LELConvert.h% s%/Lattices/LELConvert\.tcc%/LEL/LELConvert.tcc% s%/Lattices/LELCoordinates\.cc%/LEL/LELCoordinates.cc% s%/Lattices/LELCoordinates\.h%/LEL/LELCoordinates.h% s%/Lattices/LELFunction\.h%/LEL/LELFunction.h% s%/Lattices/LELFunction\.tcc%/LEL/LELFunction.tcc% s%/Lattices/LELFunction2\.cc%/LEL/LELFunction2.cc% s%/Lattices/LELFunction2\.h%/LEL/LELFunction2.h% s%/Lattices/LELFunctionEnums\.h%/LEL/LELFunctionEnums.h% s%/Lattices/LELInterface\.h%/LEL/LELInterface.h% s%/Lattices/LELInterface\.tcc%/LEL/LELInterface.tcc% s%/Lattices/LELLattCoord\.cc%/LEL/LELLattCoord.cc% s%/Lattices/LELLattCoord\.h%/LEL/LELLattCoord.h% s%/Lattices/LELLattCoordBase\.cc%/LEL/LELLattCoordBase.cc% s%/Lattices/LELLattCoordBase\.h%/LEL/LELLattCoordBase.h% s%/Lattices/LELLattice\.h%/LEL/LELLattice.h% s%/Lattices/LELLattice\.tcc%/LEL/LELLattice.tcc% s%/Lattices/LELRegion\.cc%/LEL/LELRegion.cc% s%/Lattices/LELRegion\.h%/LEL/LELRegion.h% s%/Lattices/LELScalar\.h%/LEL/LELScalar.h% s%/Lattices/LELScalar\.tcc%/LEL/LELScalar.tcc% s%/Lattices/LELSpectralIndex\.h%/LEL/LELSpectralIndex.h% s%/Lattices/LELSpectralIndex\.tcc%/LEL/LELSpectralIndex.tcc% s%/Lattices/LELUnary\.h%/LEL/LELUnary.h% s%/Lattices/LELUnary\.tcc%/LEL/LELUnary.tcc% s%/Lattices/LELUnary2\.cc%/LEL/LELUnary2.cc% s%/Lattices/LELUnary2\.h%/LEL/LELUnary2.h% s%/Lattices/LELUnaryEnums\.h%/LEL/LELUnaryEnums.h% s%/Lattices/LatticeExpr\.h%/LEL/LatticeExpr.h% s%/Lattices/LatticeExpr\.tcc%/LEL/LatticeExpr.tcc% s%/Lattices/LatticeExprNode\.cc%/LEL/LatticeExprNode.cc% s%/Lattices/LatticeExprNode\.h%/LEL/LatticeExprNode.h% casacore-3.7.1/changescripts/msoper.sed000066400000000000000000000023401476623553700201620ustar00rootroot00000000000000s%/MeasurementSets/MS1ToMS2Converter\.h%/MSOper/MS1ToMS2Converter.h% s%/MeasurementSets/MSConcat\.h%/MSOper/MSConcat.h% s%/MeasurementSets/MSDerivedValues\.h%/MSOper/MSDerivedValues.h% s%/MeasurementSets/MSFlagger\.h%/MSOper/MSFlagger.h% s%/MeasurementSets/MSKeys\.h%/MSOper/MSKeys.h% s%/MeasurementSets/MSLister\.h%/MSOper/MSLister.h% s%/MeasurementSets/MSMetaData\.h%/MSOper/MSMetaData.h% s%/MeasurementSets/MSReader\.h%/MSOper/MSReader.h% s%/MeasurementSets/MSSummary\.h%/MSOper/MSSummary.h% s%/MeasurementSets/MSValidIds\.h%/MSOper/MSValidIds.h% s%/MeasurementSets/NewMSSimulator\.h%/MSOper/NewMSSimulator.h% s%/MeasurementSets/MS1ToMS2Converter\.cc%/MSOper/MS1ToMS2Converter.cc% s%/MeasurementSets/MSConcat\.cc%/MSOper/MSConcat.cc% s%/MeasurementSets/MSDerivedValues\.cc%/MSOper/MSDerivedValues.cc% s%/MeasurementSets/MSFlagger\.cc%/MSOper/MSFlagger.cc% s%/MeasurementSets/MSKeys\.cc%/MSOper/MSKeys.cc% s%/MeasurementSets/MSLister\.cc%/MSOper/MSLister.cc% s%/MeasurementSets/MSMetaData\.cc%/MSOper/MSMetaData.cc% s%/MeasurementSets/MSReader\.cc%/MSOper/MSReader.cc% s%/MeasurementSets/MSSummary\.cc%/MSOper/MSSummary.cc% s%/MeasurementSets/MSValidIds\.cc%/MSOper/MSValidIds.cc% s%/MeasurementSets/NewMSSimulator\.cc%/MSOper/NewMSSimulator.cc% casacore-3.7.1/changescripts/mssel.sed000066400000000000000000000156621476623553700200130ustar00rootroot00000000000000s%/MeasurementSets/MSAntennaGram\.cc%/MSSel/MSAntennaGram.cc% s%/MeasurementSets/MSAntennaGram\.h%/MSSel/MSAntennaGram.h% s%/MeasurementSets/MSAntennaGram\.ll%/MSSel/MSAntennaGram.ll% s%/MeasurementSets/MSAntennaGram\.yy%/MSSel/MSAntennaGram.yy% s%/MeasurementSets/MSAntennaIndex\.cc%/MSSel/MSAntennaIndex.cc% s%/MeasurementSets/MSAntennaIndex\.h%/MSSel/MSAntennaIndex.h% s%/MeasurementSets/MSAntennaParse\.cc%/MSSel/MSAntennaParse.cc% s%/MeasurementSets/MSAntennaParse\.h%/MSSel/MSAntennaParse.h% s%/MeasurementSets/MSArrayGram\.cc%/MSSel/MSArrayGram.cc% s%/MeasurementSets/MSArrayGram\.h%/MSSel/MSArrayGram.h% s%/MeasurementSets/MSArrayGram\.ll%/MSSel/MSArrayGram.ll% s%/MeasurementSets/MSArrayGram\.yy%/MSSel/MSArrayGram.yy% s%/MeasurementSets/MSArrayParse\.cc%/MSSel/MSArrayParse.cc% s%/MeasurementSets/MSArrayParse\.h%/MSSel/MSArrayParse.h% s%/MeasurementSets/MSCorrGram\.cc%/MSSel/MSCorrGram.cc% s%/MeasurementSets/MSCorrGram\.h%/MSSel/MSCorrGram.h% s%/MeasurementSets/MSCorrGram\.ll%/MSSel/MSCorrGram.ll% s%/MeasurementSets/MSCorrGram\.yy%/MSSel/MSCorrGram.yy% s%/MeasurementSets/MSCorrParse\.cc%/MSSel/MSCorrParse.cc% s%/MeasurementSets/MSCorrParse\.h%/MSSel/MSCorrParse.h% s%/MeasurementSets/MSDataDescIndex\.cc%/MSSel/MSDataDescIndex.cc% s%/MeasurementSets/MSDataDescIndex\.h%/MSSel/MSDataDescIndex.h% s%/MeasurementSets/MSDopplerIndex\.cc%/MSSel/MSDopplerIndex.cc% s%/MeasurementSets/MSDopplerIndex\.h%/MSSel/MSDopplerIndex.h% s%/MeasurementSets/MSFeedIndex\.cc%/MSSel/MSFeedIndex.cc% s%/MeasurementSets/MSFeedIndex\.h%/MSSel/MSFeedIndex.h% s%/MeasurementSets/MSFieldGram\.cc%/MSSel/MSFieldGram.cc% s%/MeasurementSets/MSFieldGram\.h%/MSSel/MSFieldGram.h% s%/MeasurementSets/MSFieldGram\.ll%/MSSel/MSFieldGram.ll% s%/MeasurementSets/MSFieldGram\.yy%/MSSel/MSFieldGram.yy% s%/MeasurementSets/MSFieldIndex\.cc%/MSSel/MSFieldIndex.cc% s%/MeasurementSets/MSFieldIndex\.h%/MSSel/MSFieldIndex.h% s%/MeasurementSets/MSFieldParse\.cc%/MSSel/MSFieldParse.cc% s%/MeasurementSets/MSFieldParse\.h%/MSSel/MSFieldParse.h% s%/MeasurementSets/MSFreqOffIndex\.cc%/MSSel/MSFreqOffIndex.cc% s%/MeasurementSets/MSFreqOffIndex\.h%/MSSel/MSFreqOffIndex.h% s%/MeasurementSets/MSObservationGram\.cc%/MSSel/MSObservationGram.cc% s%/MeasurementSets/MSObservationGram\.h%/MSSel/MSObservationGram.h% s%/MeasurementSets/MSObservationGram\.ll%/MSSel/MSObservationGram.ll% s%/MeasurementSets/MSObservationGram\.yy%/MSSel/MSObservationGram.yy% s%/MeasurementSets/MSObservationParse\.cc%/MSSel/MSObservationParse.cc% s%/MeasurementSets/MSObservationParse\.h%/MSSel/MSObservationParse.h% s%/MeasurementSets/MSObsIndex\.cc%/MSSel/MSObsIndex.cc% s%/MeasurementSets/MSObsIndex\.h%/MSSel/MSObsIndex.h% s%/MeasurementSets/MSParse\.cc%/MSSel/MSParse.cc% s%/MeasurementSets/MSParse\.h%/MSSel/MSParse.h% s%/MeasurementSets/MSPointingIndex\.cc%/MSSel/MSPointingIndex.cc% s%/MeasurementSets/MSPointingIndex\.h%/MSSel/MSPointingIndex.h% s%/MeasurementSets/MSPolIndex\.cc%/MSSel/MSPolIndex.cc% s%/MeasurementSets/MSPolIndex\.h%/MSSel/MSPolIndex.h% s%/MeasurementSets/MSPolnGram\.cc%/MSSel/MSPolnGram.cc% s%/MeasurementSets/MSPolnGram\.h%/MSSel/MSPolnGram.h% s%/MeasurementSets/MSPolnParse\.cc%/MSSel/MSPolnParse.cc% s%/MeasurementSets/MSPolnParse\.h%/MSSel/MSPolnParse.h% s%/MeasurementSets/MSScanGram\.cc%/MSSel/MSScanGram.cc% s%/MeasurementSets/MSScanGram\.h%/MSSel/MSScanGram.h% s%/MeasurementSets/MSScanGram\.ll%/MSSel/MSScanGram.ll% s%/MeasurementSets/MSScanGram\.yy%/MSSel/MSScanGram.yy% s%/MeasurementSets/MSScanParse\.cc%/MSSel/MSScanParse.cc% s%/MeasurementSets/MSScanParse\.h%/MSSel/MSScanParse.h% s%/MeasurementSets/MSSelectableMainColumn\.h%/MSSel/MSSelectableMainColumn.h% s%/MeasurementSets/MSSelectableTable\.cc%/MSSel/MSSelectableTable.cc% s%/MeasurementSets/MSSelectableTable\.h%/MSSel/MSSelectableTable.h% s%/MeasurementSets/MSSelection\.cc%/MSSel/MSSelection.cc% s%/MeasurementSets/MSSelection\.h%/MSSel/MSSelection.h% s%/MeasurementSets/MSSelectionError\.cc%/MSSel/MSSelectionError.cc% s%/MeasurementSets/MSSelectionError\.h%/MSSel/MSSelectionError.h% s%/MeasurementSets/MSSelectionErrorHandler\.cc%/MSSel/MSSelectionErrorHandler.cc% s%/MeasurementSets/MSSelectionErrorHandler\.h%/MSSel/MSSelectionErrorHandler.h% s%/MeasurementSets/MSSelectionKeywords\.cc%/MSSel/MSSelectionKeywords.cc% s%/MeasurementSets/MSSelectionKeywords\.h%/MSSel/MSSelectionKeywords.h% s%/MeasurementSets/MSSelectionTools\.cc%/MSSel/MSSelectionTools.cc% s%/MeasurementSets/MSSelectionTools\.h%/MSSel/MSSelectionTools.h% s%/MeasurementSets/MSSelector\.cc%/MSSel/MSSelector.cc% s%/MeasurementSets/MSSelector\.h%/MSSel/MSSelector.h% s%/MeasurementSets/MSSelUtil\.h%/MSSel/MSSelUtil.h% s%/MeasurementSets/MSSelUtil\.tcc%/MSSel/MSSelUtil.tcc% s%/MeasurementSets/MSSelUtil2\.h%/MSSel/MSSelUtil2.h% s%/MeasurementSets/MSSelUtil2\.tcc%/MSSel/MSSelUtil2.tcc% s%/MeasurementSets/MSSourceIndex\.cc%/MSSel/MSSourceIndex.cc% s%/MeasurementSets/MSSourceIndex\.h%/MSSel/MSSourceIndex.h% s%/MeasurementSets/MSSpwGram\.cc%/MSSel/MSSpwGram.cc% s%/MeasurementSets/MSSpwGram\.h%/MSSel/MSSpwGram.h% s%/MeasurementSets/MSSpwGram\.ll%/MSSel/MSSpwGram.ll% s%/MeasurementSets/MSSpwGram\.yy%/MSSel/MSSpwGram.yy% s%/MeasurementSets/MSSpwIndex\.cc%/MSSel/MSSpwIndex.cc% s%/MeasurementSets/MSSpwIndex\.h%/MSSel/MSSpwIndex.h% s%/MeasurementSets/MSSpWindowIndex\.cc%/MSSel/MSSpWindowIndex.cc% s%/MeasurementSets/MSSpWindowIndex\.h%/MSSel/MSSpWindowIndex.h% s%/MeasurementSets/MSSpwParse\.cc%/MSSel/MSSpwParse.cc% s%/MeasurementSets/MSSpwParse\.h%/MSSel/MSSpwParse.h% s%/MeasurementSets/MSStateGram\.cc%/MSSel/MSStateGram.cc% s%/MeasurementSets/MSStateGram\.h%/MSSel/MSStateGram.h% s%/MeasurementSets/MSStateGram\.ll%/MSSel/MSStateGram.ll% s%/MeasurementSets/MSStateGram\.yy%/MSSel/MSStateGram.yy% s%/MeasurementSets/MSStateIndex\.cc%/MSSel/MSStateIndex.cc% s%/MeasurementSets/MSStateIndex\.h%/MSSel/MSStateIndex.h% s%/MeasurementSets/MSStateParse\.cc%/MSSel/MSStateParse.cc% s%/MeasurementSets/MSStateParse\.h%/MSSel/MSStateParse.h% s%/MeasurementSets/MSSysCalIndex\.cc%/MSSel/MSSysCalIndex.cc% s%/MeasurementSets/MSSysCalIndex\.h%/MSSel/MSSysCalIndex.h% s%/MeasurementSets/MSTableIndex\.cc%/MSSel/MSTableIndex.cc% s%/MeasurementSets/MSTableIndex\.h%/MSSel/MSTableIndex.h% s%/MeasurementSets/MSTimeDefinitions\.h%/MSSel/MSTimeDefinitions.h% s%/MeasurementSets/MSTimeGram\.cc%/MSSel/MSTimeGram.cc% s%/MeasurementSets/MSTimeGram\.h%/MSSel/MSTimeGram.h% s%/MeasurementSets/MSTimeGram\.ll%/MSSel/MSTimeGram.ll% s%/MeasurementSets/MSTimeGram\.yy%/MSSel/MSTimeGram.yy% s%/MeasurementSets/MSTimeParse\.cc%/MSSel/MSTimeParse.cc% s%/MeasurementSets/MSTimeParse\.h%/MSSel/MSTimeParse.h% s%/MeasurementSets/MSUvDistGram\.cc%/MSSel/MSUvDistGram.cc% s%/MeasurementSets/MSUvDistGram\.h%/MSSel/MSUvDistGram.h% s%/MeasurementSets/MSUvDistGram\.ll%/MSSel/MSUvDistGram.ll% s%/MeasurementSets/MSUvDistGram\.yy%/MSSel/MSUvDistGram.yy% s%/MeasurementSets/MSUvDistParse\.cc%/MSSel/MSUvDistParse.cc% s%/MeasurementSets/MSUvDistParse\.h%/MSSel/MSUvDistParse.h% s%/MeasurementSets/MSWeatherIndex\.cc%/MSSel/MSWeatherIndex.cc% s%/MeasurementSets/MSWeatherIndex\.h%/MSSel/MSWeatherIndex.h% casacore-3.7.1/changescripts/regions.sed000066400000000000000000000050511476623553700203250ustar00rootroot00000000000000s%/Lattices/FITSMask\.cc%/LRegions/FITSMask.cc% s%/Lattices/FITSMask\.h%/LRegions/FITSMask.h% s%/Lattices/LCBox\.cc%/LRegions/LCBox.cc% s%/Lattices/LCBox\.h%/LRegions/LCBox.h% s%/Lattices/LCComplement\.cc%/LRegions/LCComplement.cc% s%/Lattices/LCComplement\.h%/LRegions/LCComplement.h% s%/Lattices/LCConcatenation\.cc%/LRegions/LCConcatenation.cc% s%/Lattices/LCConcatenation\.h%/LRegions/LCConcatenation.h% s%/Lattices/LCDifference\.cc%/LRegions/LCDifference.cc% s%/Lattices/LCDifference\.h%/LRegions/LCDifference.h% s%/Lattices/LCEllipsoid\.cc%/LRegions/LCEllipsoid.cc% s%/Lattices/LCEllipsoid\.h%/LRegions/LCEllipsoid.h% s%/Lattices/LCExtension\.cc%/LRegions/LCExtension.cc% s%/Lattices/LCExtension\.h%/LRegions/LCExtension.h% s%/Lattices/LCHDF5Mask\.cc%/LRegions/LCHDF5Mask.cc% s%/Lattices/LCHDF5Mask\.h%/LRegions/LCHDF5Mask.h% s%/Lattices/LCIntersection\.cc%/LRegions/LCIntersection.cc% s%/Lattices/LCIntersection\.h%/LRegions/LCIntersection.h% s%/Lattices/LCLELMask\.cc%/LRegions/LCLELMask.cc% s%/Lattices/LCLELMask\.h%/LRegions/LCLELMask.h% s%/Lattices/LCMask\.cc%/LRegions/LCMask.cc% s%/Lattices/LCMask\.h%/LRegions/LCMask.h% s%/Lattices/LCPagedMask\.cc%/LRegions/LCPagedMask.cc% s%/Lattices/LCPagedMask\.h%/LRegions/LCPagedMask.h% s%/Lattices/LCPixelSet\.cc%/LRegions/LCPixelSet.cc% s%/Lattices/LCPixelSet\.h%/LRegions/LCPixelSet.h% s%/Lattices/LCPolygon\.cc%/LRegions/LCPolygon.cc% s%/Lattices/LCPolygon\.h%/LRegions/LCPolygon.h% s%/Lattices/LCRegion\.cc%/LRegions/LCRegion.cc% s%/Lattices/LCRegion\.h%/LRegions/LCRegion.h% s%/Lattices/LCRegion2\.cc%/LRegions/LCRegion2.cc% s%/Lattices/LCRegionFixed\.cc%/LRegions/LCRegionFixed.cc% s%/Lattices/LCRegionFixed\.h%/LRegions/LCRegionFixed.h% s%/Lattices/LCRegionMulti\.cc%/LRegions/LCRegionMulti.cc% s%/Lattices/LCRegionMulti\.h%/LRegions/LCRegionMulti.h% s%/Lattices/LCRegionSingle\.cc%/LRegions/LCRegionSingle.cc% s%/Lattices/LCRegionSingle\.h%/LRegions/LCRegionSingle.h% s%/Lattices/LCSlicer\.cc%/LRegions/LCSlicer.cc% s%/Lattices/LCSlicer\.h%/LRegions/LCSlicer.h% s%/Lattices/LCStretch\.cc%/LRegions/LCStretch.cc% s%/Lattices/LCStretch\.h%/LRegions/LCStretch.h% s%/Lattices/LCUnion\.cc%/LRegions/LCUnion.cc% s%/Lattices/LCUnion\.h%/LRegions/LCUnion.h% s%/Lattices/LatticeRegion\.cc%/LRegions/LatticeRegion.cc% s%/Lattices/LatticeRegion\.h%/LRegions/LatticeRegion.h% s%/Lattices/LattRegionHolder\.cc%/LRegions/LattRegionHolder.cc% s%/Lattices/LattRegionHolder\.h%/LRegions/LattRegionHolder.h% s%/Lattices/RegionType\.cc%/LRegions/RegionType.cc% s%/Lattices/RegionType\.h%/LRegions/RegionType.h% s%/Containers/ContainerIO\.h%/BasicSL/STLIO.h% casacore-3.7.1/changescripts/taql.sed000066400000000000000000000076721476623553700176330ustar00rootroot00000000000000s%/Tables/ExprAggrNode\.cc%/TaQL/ExprAggrNode.cc% s%/Tables/ExprAggrNode\.h%/TaQL/ExprAggrNode.h% s%/Tables/ExprAggrNodeArray\.cc%/TaQL/ExprAggrNodeArray.cc% s%/Tables/ExprAggrNodeArray\.h%/TaQL/ExprAggrNodeArray.h% s%/Tables/ExprConeNode\.cc%/TaQL/ExprConeNode.cc% s%/Tables/ExprConeNode\.h%/TaQL/ExprConeNode.h% s%/Tables/ExprDerNode\.cc%/TaQL/ExprDerNode.cc% s%/Tables/ExprDerNode\.h%/TaQL/ExprDerNode.h% s%/Tables/ExprDerNodeArray\.cc%/TaQL/ExprDerNodeArray.cc% s%/Tables/ExprDerNodeArray\.h%/TaQL/ExprDerNodeArray.h% s%/Tables/ExprFuncNode\.cc%/TaQL/ExprFuncNode.cc% s%/Tables/ExprFuncNode\.h%/TaQL/ExprFuncNode.h% s%/Tables/ExprFuncNodeArray\.cc%/TaQL/ExprFuncNodeArray.cc% s%/Tables/ExprFuncNodeArray\.h%/TaQL/ExprFuncNodeArray.h% s%/Tables/ExprGroup\.cc%/TaQL/ExprGroup.cc% s%/Tables/ExprGroup\.h%/TaQL/ExprGroup.h% s%/Tables/ExprGroupAggrFunc\.cc%/TaQL/ExprGroupAggrFunc.cc% s%/Tables/ExprGroupAggrFunc\.h%/TaQL/ExprGroupAggrFunc.h% s%/Tables/ExprGroupAggrFuncArray\.cc%/TaQL/ExprGroupAggrFuncArray.cc% s%/Tables/ExprGroupAggrFuncArray\.h%/TaQL/ExprGroupAggrFuncArray.h% s%/Tables/ExprLogicNode\.cc%/TaQL/ExprLogicNode.cc% s%/Tables/ExprLogicNode\.h%/TaQL/ExprLogicNode.h% s%/Tables/ExprLogicNodeArray\.cc%/TaQL/ExprLogicNodeArray.cc% s%/Tables/ExprLogicNodeArray\.h%/TaQL/ExprLogicNodeArray.h% s%/Tables/ExprMathNode\.cc%/TaQL/ExprMathNode.cc% s%/Tables/ExprMathNode\.h%/TaQL/ExprMathNode.h% s%/Tables/ExprMathNodeArray\.cc%/TaQL/ExprMathNodeArray.cc% s%/Tables/ExprMathNodeArray\.h%/TaQL/ExprMathNodeArray.h% s%/Tables/ExprNode\.cc%/TaQL/ExprNode.cc% s%/Tables/ExprNode\.h%/TaQL/ExprNode.h% s%/Tables/ExprNodeArray\.cc%/TaQL/ExprNodeArray.cc% s%/Tables/ExprNodeArray\.h%/TaQL/ExprNodeArray.h% s%/Tables/ExprNodeRecord\.cc%/TaQL/ExprNodeRecord.cc% s%/Tables/ExprNodeRecord\.h%/TaQL/ExprNodeRecord.h% s%/Tables/ExprNodeRep\.cc%/TaQL/ExprNodeRep.cc% s%/Tables/ExprNodeRep\.h%/TaQL/ExprNodeRep.h% s%/Tables/ExprNodeSet\.cc%/TaQL/ExprNodeSet.cc% s%/Tables/ExprNodeSet\.h%/TaQL/ExprNodeSet.h% s%/Tables/ExprRange\.cc%/TaQL/ExprRange.cc% s%/Tables/ExprRange\.h%/TaQL/ExprRange.h% s%/Tables/ExprUDFNode\.cc%/TaQL/ExprUDFNode.cc% s%/Tables/ExprUDFNode\.h%/TaQL/ExprUDFNode.h% s%/Tables/ExprUDFNodeArray\.cc%/TaQL/ExprUDFNodeArray.cc% s%/Tables/ExprUDFNodeArray\.h%/TaQL/ExprUDFNodeArray.h% s%/Tables/ExprUnitNode\.cc%/TaQL/ExprUnitNode.cc% s%/Tables/ExprUnitNode\.h%/TaQL/ExprUnitNode.h% s%/Tables/RecordExpr\.cc%/TaQL/RecordExpr.cc% s%/Tables/RecordExpr\.h%/TaQL/RecordExpr.h% s%/Tables/RecordGram\.cc%/TaQL/RecordGram.cc% s%/Tables/RecordGram\.h%/TaQL/RecordGram.h% s%/Tables/RecordGram\.ll%/TaQL/RecordGram.ll% s%/Tables/RecordGram\.yy%/TaQL/RecordGram.yy% s%/Tables/TaQLNode\.cc%/TaQL/TaQLNode.cc% s%/Tables/TaQLNode\.h%/TaQL/TaQLNode.h% s%/Tables/TaQLNodeDer\.cc%/TaQL/TaQLNodeDer.cc% s%/Tables/TaQLNodeDer\.h%/TaQL/TaQLNodeDer.h% s%/Tables/TaQLNodeHandler\.cc%/TaQL/TaQLNodeHandler.cc% s%/Tables/TaQLNodeHandler\.h%/TaQL/TaQLNodeHandler.h% s%/Tables/TaQLNodeRep\.cc%/TaQL/TaQLNodeRep.cc% s%/Tables/TaQLNodeRep\.h%/TaQL/TaQLNodeRep.h% s%/Tables/TaQLNodeResult\.cc%/TaQL/TaQLNodeResult.cc% s%/Tables/TaQLNodeResult\.h%/TaQL/TaQLNodeResult.h% s%/Tables/TaQLNodeVisitor\.cc%/TaQL/TaQLNodeVisitor.cc% s%/Tables/TaQLNodeVisitor\.h%/TaQL/TaQLNodeVisitor.h% s%/Tables/TaQLResult\.cc%/TaQL/TaQLResult.cc% s%/Tables/TaQLResult\.h%/TaQL/TaQLResult.h% s%/Tables/TaQLStyle\.cc%/TaQL/TaQLStyle.cc% s%/Tables/TaQLStyle\.h%/TaQL/TaQLStyle.h% s%/Tables/TableExprData\.cc%/TaQL/TableExprData.cc% s%/Tables/TableExprData\.h%/TaQL/TableExprData.h% s%/Tables/TableExprId\.cc%/TaQL/TableExprId.cc% s%/Tables/TableExprId\.h%/TaQL/TableExprId.h% s%/Tables/TableExprIdAggr\.h%/TaQL/TableExprIdAggr.h% s%/Tables/TableGram\.cc%/TaQL/TableGram.cc% s%/Tables/TableGram\.h%/TaQL/TableGram.h% s%/Tables/TableGram\.ll%/TaQL/TableGram.ll% s%/Tables/TableGram\.yy%/TaQL/TableGram.yy% s%/Tables/TableParse\.cc%/TaQL/TableParse.cc% s%/Tables/TableParse\.h%/TaQL/TableParse.h% s%/Tables/UDFBase\.cc%/TaQL/UDFBase.cc% s%/Tables/UDFBase\.h%/TaQL/UDFBase.h% casacore-3.7.1/changescripts/updateall000077500000000000000000000003731476623553700200650ustar00rootroot00000000000000#!/bin/sh # Update for all changes in Casacore release 2.0. # Find the path used to start the script. pgmpath=`dirname $0` pgmpath=`cd $pgmpath > /dev/null 2>&1 && pwd` $pgmpath/updateinclude $* $pgmpath/updatenamespace $* $pgmpath/updatepath $* casacore-3.7.1/changescripts/updateinclude000077500000000000000000000013711476623553700207370ustar00rootroot00000000000000#!/bin/sh # Update the include paths for the moved files in Casacore release 2.0. # Find the path used to start the script. pgmpath=`dirname $0` pgmpath=`cd $pgmpath > /dev/null 2>&1 && pwd` for FILE in $* do sed -f $pgmpath/taql.sed "$FILE" > "$FILE.n1" sed -f $pgmpath/dataman.sed "$FILE.n1" > "$FILE.n2" sed -f $pgmpath/latticemath.sed "$FILE.n2" > "$FILE.n3" sed -f $pgmpath/lel.sed "$FILE.n3" > "$FILE.n4" sed -f $pgmpath/regions.sed "$FILE.n4" > "$FILE.n5" sed -f $pgmpath/mssel.sed "$FILE.n5" > "$FILE.n6" sed -f $pgmpath/msoper.sed "$FILE.n6" > "$FILE.n7" if ! diff "$FILE.n7" "$FILE" >& /dev/null; then mv "$FILE.n7" "$FILE" # echo " changed #includes in $FILE" fi rm -f "$FILE".n[1234567] done casacore-3.7.1/changescripts/updatenamespace000077500000000000000000000004331476623553700212460ustar00rootroot00000000000000#!/bin/sh # Change namespace casa to casacore for Casacore release 2.0. for FILE in $* do perl -p -i -e s'%namespace +casa([ };])%namespace casacore\1%' "$FILE" perl -p -i -e s'%NAMESPACE CASA %NAMESPACE CASACORE %' "$FILE" perl -p -i -e s'%casa::%casacore::%'g "$FILE" done casacore-3.7.1/changescripts/updatepath000077500000000000000000000004301476623553700202430ustar00rootroot00000000000000#!/bin/sh # Add casacore/ to the #include path for Casacore release 2.0. for FILE in $* do perl -p -i -e s'%(#\s*include\s*<\s*)(casa|tables|scimath|measures|lattices|images|ms|msfits|meas|derivedmscal|coordinates|mirlib|fits|python/Converters)/%\1casacore/\2/%' "$FILE" done casacore-3.7.1/cmake/000077500000000000000000000000001476623553700144045ustar00rootroot00000000000000casacore-3.7.1/cmake/CTest2JUnit.xsl000066400000000000000000000153101476623553700172120ustar00rootroot00000000000000 BuildName: BuildStamp: Name: Generator: CompilerName: OSName: Hostname: OSRelease: OSVersion: OSPlatform: Is64Bits: VendorString: VendorID: FamilyID: ModelID: ProcessorCacheSize: NumberOfLogicalCPU: NumberOfPhysicalCPU: TotalVirtualMemory: TotalPhysicalMemory: LogicalProcessorsPerPhysical: ProcessorClockFrequency: casacore-3.7.1/cmake/FindCFITSIO.cmake000066400000000000000000000062351476623553700173150ustar00rootroot00000000000000# - Try to find CFITSIO. # Variables used by this module: # CFITSIO_ROOT_DIR - CFITSIO root directory # Variables defined by this module: # CFITSIO_FOUND - system has CFITSIO # CFITSIO_INCLUDE_DIR - the CFITSIO include directory (cached) # CFITSIO_INCLUDE_DIRS - the CFITSIO include directories # (identical to CFITSIO_INCLUDE_DIR) # CFITSIO_LIBRARY - the CFITSIO library (cached) # CFITSIO_LIBRARIES - the CFITSIO libraries # (identical to CFITSIO_LIBRARY) # CFITSIO_VERSION_STRING the found version of CFITSIO, padded to 3 digits # Copyright (C) 2009 # ASTRON (Netherlands Institute for Radio Astronomy) # P.O.Box 2, 7990 AA Dwingeloo, The Netherlands # # This file is part of the LOFAR software suite. # The LOFAR software suite 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 3 of the License, or # (at your option) any later version. # # The LOFAR software suite 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 the LOFAR software suite. If not, see . # # $Id$ if(NOT CFITSIO_FOUND) find_path(CFITSIO_INCLUDE_DIR fitsio.h HINTS ${CFITSIO_ROOT_DIR} PATH_SUFFIXES include include/cfitsio include/libcfitsio0) if(CFITSIO_INCLUDE_DIR) FILE(READ "${CFITSIO_INCLUDE_DIR}/fitsio.h" CFITSIO_H) set(CFITSIO_VERSION_REGEX ".*#define CFITSIO_VERSION[^0-9]*([0-9]+)\\.([0-9]+).*") if ("${CFITSIO_H}" MATCHES ${CFITSIO_VERSION_REGEX}) # Pad CFITSIO minor version to three digit because 3.181 is older than 3.35 STRING(REGEX REPLACE ${CFITSIO_VERSION_REGEX} "\\1.\\200" CFITSIO_VERSION_STRING "${CFITSIO_H}") STRING(SUBSTRING ${CFITSIO_VERSION_STRING} 0 5 CFITSIO_VERSION_STRING) STRING(REGEX REPLACE "^([0-9]+)[.]([0-9]+)" "\\1" CFITSIO_VERSION_MAJOR ${CFITSIO_VERSION_STRING}) # CFITSIO_VERSION_MINOR will contain 80 for 3.08, 181 for 3.181 and 200 for 3.2 STRING(REGEX REPLACE "^([0-9]+)[.]0*([0-9]+)" "\\2" CFITSIO_VERSION_MINOR ${CFITSIO_VERSION_STRING}) else () set(CFITSIO_VERSION_STRING "Unknown") endif() endif(CFITSIO_INCLUDE_DIR) find_library(CFITSIO_LIBRARY cfitsio HINTS ${CFITSIO_ROOT_DIR} PATH_SUFFIXES lib) find_library(M_LIBRARY m) mark_as_advanced(CFITSIO_INCLUDE_DIR CFITSIO_LIBRARY M_LIBRARY) if(CMAKE_VERSION VERSION_LESS "2.8.3") find_package_handle_standard_args(CFITSIO DEFAULT_MSG CFITSIO_LIBRARY M_LIBRARY CFITSIO_INCLUDE_DIR) else () include(FindPackageHandleStandardArgs) find_package_handle_standard_args(CFITSIO REQUIRED_VARS CFITSIO_LIBRARY M_LIBRARY CFITSIO_INCLUDE_DIR VERSION_VAR CFITSIO_VERSION_STRING) endif () set(CFITSIO_INCLUDE_DIRS ${CFITSIO_INCLUDE_DIR}) set(CFITSIO_LIBRARIES ${CFITSIO_LIBRARY} ${M_LIBRARY}) endif(NOT CFITSIO_FOUND) casacore-3.7.1/cmake/FindDL.cmake000066400000000000000000000010571476623553700165110ustar00rootroot00000000000000# - find where dlopen and friends are located. # DL_FOUND - system has dynamic linking interface available # DL_INCLUDE_DIR - where dlfcn.h is located. # DL_LIBRARIES - libraries needed to use dlopen include(CheckFunctionExists) find_path(DL_INCLUDE_DIR NAMES dlfcn.h) find_library(DL_LIBRARIES NAMES dl) if(DL_LIBRARIES) set(DL_FOUND) else(DL_LIBRARIES) check_function_exists(dlopen DL_FOUND) # If dlopen can be found without linking in dl then dlopen is part # of libc, so don't need to link extra libs. set(DL_LIBRARIES "") endif(DL_LIBRARIES) casacore-3.7.1/cmake/FindFFTW3.cmake000066400000000000000000000071521476623553700170450ustar00rootroot00000000000000# - Try to find FFTW3. # Usage: find_package(FFTW3 [COMPONENTS [single double long-double threads]]) # # Variables used by this module: # FFTW3_ROOT_DIR - FFTW3 root directory # Variables defined by this module: # FFTW3_FOUND - system has FFTW3 # FFTW3_INCLUDE_DIR - the FFTW3 include directory (cached) # FFTW3_INCLUDE_DIRS - the FFTW3 include directories # (identical to FFTW3_INCLUDE_DIR) # FFTW3[FL]?_LIBRARY - the FFTW3 library - double, single(F), # long-double(L) precision (cached) # FFTW3[FL]?_THREADS_LIBRARY - the threaded FFTW3 library - double, single(F), # long-double(L) precision (cached) # FFTW3_LIBRARIES - list of all FFTW3 libraries found # Copyright (C) 2009-2010 # ASTRON (Netherlands Institute for Radio Astronomy) # P.O.Box 2, 7990 AA Dwingeloo, The Netherlands # # This file is part of the LOFAR software suite. # The LOFAR software suite 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 3 of the License, or # (at your option) any later version. # # The LOFAR software suite 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 the LOFAR software suite. If not, see . # # $Id: FindFFTW3.cmake 15918 2010-06-25 11:12:42Z loose $ # Use double precision by default. if(FFTW3_FIND_COMPONENTS MATCHES "^$") set(_components double) else() set(_components ${FFTW3_FIND_COMPONENTS}) endif() # Loop over each component. set(_libraries) foreach(_comp ${_components}) if(_comp STREQUAL "single") list(APPEND _libraries fftw3f) elseif(_comp STREQUAL "double") list(APPEND _libraries fftw3) elseif(_comp STREQUAL "long-double") list(APPEND _libraries fftw3l) elseif(_comp STREQUAL "threads") set(_use_threads ON) else(_comp STREQUAL "single") message(FATAL_ERROR "FindFFTW3: unknown component `${_comp}' specified. " "Valid components are `single', `double', `long-double', and `threads'.") endif(_comp STREQUAL "single") endforeach(_comp ${_components}) # If using threads, we need to link against threaded libraries as well. if(_use_threads) set(_thread_libs) foreach(_lib ${_libraries}) list(APPEND _thread_libs ${_lib}_threads) endforeach(_lib ${_libraries}) set(_libraries ${_thread_libs} ${_libraries}) endif(_use_threads) # Keep a list of variable names that we need to pass on to # find_package_handle_standard_args(). set(_check_list) # Search for all requested libraries. foreach(_lib ${_libraries}) string(TOUPPER ${_lib} _LIB) find_library(${_LIB}_LIBRARY ${_lib} HINTS ${FFTW3_ROOT_DIR} PATH_SUFFIXES lib) mark_as_advanced(${_LIB}_LIBRARY) list(APPEND FFTW3_LIBRARIES ${${_LIB}_LIBRARY}) list(APPEND _check_list ${_LIB}_LIBRARY) endforeach(_lib ${_libraries}) # Search for the header file. find_path(FFTW3_INCLUDE_DIR fftw3.h HINTS ${FFTW3_ROOT_DIR} PATH_SUFFIXES include) mark_as_advanced(FFTW3_INCLUDE_DIR) list(APPEND _check_list FFTW3_INCLUDE_DIR) set(FFTW3_INCLUDE_DIRS ${FFTW3_INCLUDE_DIR}) # Handle the QUIETLY and REQUIRED arguments and set FFTW_FOUND to TRUE if # all listed variables are TRUE include(FindPackageHandleStandardArgs) find_package_handle_standard_args(FFTW3 DEFAULT_MSG ${_check_list}) casacore-3.7.1/cmake/FindNUMPY.cmake000066400000000000000000000105521476623553700171220ustar00rootroot00000000000000# - Find the NumPy libraries # This module finds if NumPy is installed, and sets the following variables # indicating where it is. # # TODO: Update to provide the libraries and paths for linking npymath lib. # # NUMPY_FOUND - was NumPy found # NUMPY_VERSION - the version of NumPy found as a string # NUMPY_VERSION_MAJOR - the major version number of NumPy # NUMPY_VERSION_MINOR - the minor version number of NumPy # NUMPY_VERSION_PATCH - the patch version number of NumPy # NUMPY_VERSION_DECIMAL - e.g. version 1.6.1 is 10601 # NUMPY_INCLUDE_DIRS - path to the NumPy include files #============================================================================ # Copyright 2012 Continuum Analytics, Inc. # # MIT License # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to permit # persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. # #============================================================================ # Finding NumPy involves calling the Python interpreter if(NumPy_FIND_REQUIRED) find_package(PythonInterp REQUIRED) else() find_package(PythonInterp) endif() if(NOT PYTHONINTERP_FOUND) set(NUMPY_FOUND FALSE) return() endif() execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c" "import numpy as n; print(n.__version__); print(n.get_include());" RESULT_VARIABLE _NUMPY_SEARCH_SUCCESS OUTPUT_VARIABLE _NUMPY_VALUES_OUTPUT ERROR_VARIABLE _NUMPY_ERROR_VALUE OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT _NUMPY_SEARCH_SUCCESS MATCHES 0) if(NumPy_FIND_REQUIRED) message(FATAL_ERROR "NumPy import failure:\n${_NUMPY_ERROR_VALUE}") else() message(WARNING "NumPy import failure:\n${_NUMPY_ERROR_VALUE}") endif() set(NUMPY_FOUND FALSE) return() endif() # Convert the process output into a list string(REGEX REPLACE ";" "\\\\;" _NUMPY_VALUES ${_NUMPY_VALUES_OUTPUT}) string(REGEX REPLACE "\n" ";" _NUMPY_VALUES ${_NUMPY_VALUES}) # Just in case there is unexpected output from the Python command. list(GET _NUMPY_VALUES -2 NUMPY_VERSION) list(GET _NUMPY_VALUES -1 NUMPY_INCLUDE_DIRS) if("${NUMPY_INCLUDE_DIRS}" STREQUAL "None") # If numpy headers are not installed, n.get_include() returns None message(FATAL_ERROR "Numpy was found, but headers were not installed.") set(NUMPY_INCLUDE_DIRS) # Unset this variable set(NUMPY_FOUND FALSE) return() endif() string(REGEX MATCH "^[0-9]+\\.[0-9]+\\.[0-9]+" _VER_CHECK "${NUMPY_VERSION}") if("${_VER_CHECK}" STREQUAL "") # The output from Python was unexpected. Raise an error always # here, because we found NumPy, but it appears to be corrupted somehow. message(FATAL_ERROR "Requested version and include path from NumPy, got instead:\n${_NUMPY_VALUES_OUTPUT}\n") return() endif() # Make sure all directory separators are '/' string(REGEX REPLACE "\\\\" "/" NUMPY_INCLUDE_DIRS ${NUMPY_INCLUDE_DIRS}) # Get the major and minor version numbers string(REGEX REPLACE "\\." ";" _NUMPY_VERSION_LIST ${NUMPY_VERSION}) list(GET _NUMPY_VERSION_LIST 0 NUMPY_VERSION_MAJOR) list(GET _NUMPY_VERSION_LIST 1 NUMPY_VERSION_MINOR) list(GET _NUMPY_VERSION_LIST 2 NUMPY_VERSION_PATCH) string(REGEX MATCH "[0-9]*" NUMPY_VERSION_PATCH ${NUMPY_VERSION_PATCH}) math(EXPR NUMPY_VERSION_DECIMAL "(${NUMPY_VERSION_MAJOR} * 10000) + (${NUMPY_VERSION_MINOR} * 100) + ${NUMPY_VERSION_PATCH}") find_package_message(NUMPY "Found NumPy: version \"${NUMPY_VERSION}\" ${NUMPY_INCLUDE_DIRS}" "${NUMPY_INCLUDE_DIRS}${NUMPY_VERSION}") set(NUMPY_FOUND TRUE) casacore-3.7.1/cmake/FindPthreads.cmake000066400000000000000000000055541476623553700177720ustar00rootroot00000000000000# Find the Pthreads library # This module searches for the Pthreads library (including the # pthreads-win32 port). # # This module defines these variables: # # PTHREADS_FOUND - True if the Pthreads library was found # PTHREADS_LIBRARY - The location of the Pthreads library # PTHREADS_INCLUDE_DIR - The include directory of the Pthreads library # PTHREADS_DEFINITIONS - Preprocessor definitions to define (HAVE_PTHREAD_H is a fairly common one) # # This module responds to the PTHREADS_EXCEPTION_SCHEME # variable on Win32 to allow the user to control the # library linked against. The Pthreads-win32 port # provides the ability to link against a version of the # library with exception handling. IT IS NOT RECOMMENDED # THAT YOU CHANGE PTHREADS_EXCEPTION_SCHEME TO ANYTHING OTHER THAN # "C" because most POSIX thread implementations do not support stack # unwinding. # # PTHREADS_EXCEPTION_SCHEME # C = no exceptions (default) # (NOTE: This is the default scheme on most POSIX thread # implementations and what you should probably be using) # CE = C++ Exception Handling # SE = Structure Exception Handling (MSVC only) # # # Define a default exception scheme to link against # and validate user choice. # IF(NOT DEFINED PTHREADS_EXCEPTION_SCHEME) # Assign default if needed SET(PTHREADS_EXCEPTION_SCHEME "C") ELSE(NOT DEFINED PTHREADS_EXCEPTION_SCHEME) # Validate IF(NOT PTHREADS_EXCEPTION_SCHEME STREQUAL "C" AND NOT PTHREADS_EXCEPTION_SCHEME STREQUAL "CE" AND NOT PTHREADS_EXCEPTION_SCHEME STREQUAL "SE") MESSAGE(FATAL_ERROR "See documentation for FindPthreads.cmake, only C, CE, and SE modes are allowed") ENDIF(NOT PTHREADS_EXCEPTION_SCHEME STREQUAL "C" AND NOT PTHREADS_EXCEPTION_SCHEME STREQUAL "CE" AND NOT PTHREADS_EXCEPTION_SCHEME STREQUAL "SE") IF(NOT MSVC AND PTHREADS_EXCEPTION_SCHEME STREQUAL "SE") MESSAGE(FATAL_ERROR "Structured Exception Handling is only allowed for MSVC") ENDIF(NOT MSVC AND PTHREADS_EXCEPTION_SCHEME STREQUAL "SE") ENDIF(NOT DEFINED PTHREADS_EXCEPTION_SCHEME) # # Find the header file # FIND_PATH(PTHREADS_INCLUDE_DIR pthread.h) # # Find the library # SET(names) IF(MSVC) SET(names pthreadV${PTHREADS_EXCEPTION_SCHEME}2 pthread ) ELSEIF(MINGW) SET(names pthreadG${PTHREADS_EXCEPTION_SCHEME}2 pthread ) ELSE(MSVC) # Unix / Cygwin / Apple / Etc. SET(names pthread) ENDIF(MSVC) FIND_LIBRARY(PTHREADS_LIBRARY ${names} DOC "The Portable Threads Library") INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(Pthreads DEFAULT_MSG PTHREADS_LIBRARY PTHREADS_INCLUDE_DIR) IF(PTHREADS_INCLUDE_DIR AND PTHREADS_LIBRARY) SET(PTHREADS_DEFINITIONS -DHAVE_PTHREAD_H) SET(PTHREADS_INCLUDE_DIRS ${PTHREADS_INCLUDE_DIR}) SET(PTHREADS_LIBRARIES ${PTHREADS_LIBRARY}) ENDIF(PTHREADS_INCLUDE_DIR AND PTHREADS_LIBRARY) MARK_AS_ADVANCED(PTHREADS_INCLUDE_DIR) MARK_AS_ADVANCED(PTHREADS_LIBRARY) casacore-3.7.1/cmake/FindReadline.cmake000066400000000000000000000042001476623553700177260ustar00rootroot00000000000000# - Try to find readline, a library for easy editing of command lines. # Variables used by this module: # READLINE_ROOT_DIR - Readline root directory # Variables defined by this module: # READLINE_FOUND - system has Readline # READLINE_INCLUDE_DIR - the Readline include directory (cached) # READLINE_INCLUDE_DIRS - the Readline include directories # (identical to READLINE_INCLUDE_DIR) # READLINE_LIBRARY - the Readline library (cached) # READLINE_LIBRARIES - the Readline library plus the libraries it # depends on # Copyright (C) 2009 # ASTRON (Netherlands Institute for Radio Astronomy) # P.O.Box 2, 7990 AA Dwingeloo, The Netherlands # # This file is part of the LOFAR software suite. # The LOFAR software suite 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 3 of the License, or # (at your option) any later version. # # The LOFAR software suite 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 the LOFAR software suite. If not, see . # # $Id: FindReadline.cmake 15228 2010-03-16 09:27:26Z loose $ if(NOT READLINE_FOUND) find_path(READLINE_INCLUDE_DIR readline/readline.h HINTS ${READLINE_ROOT_DIR} PATH_SUFFIXES include) find_library(READLINE_LIBRARY readline HINTS ${READLINE_ROOT_DIR} PATH_SUFFIXES lib) find_library(NCURSES_LIBRARY ncurses) # readline depends on libncurses mark_as_advanced(READLINE_INCLUDE_DIR READLINE_LIBRARY NCURSES_LIBRARY) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Readline DEFAULT_MSG READLINE_LIBRARY NCURSES_LIBRARY READLINE_INCLUDE_DIR) set(READLINE_INCLUDE_DIRS ${READLINE_INCLUDE_DIR}) set(READLINE_LIBRARIES ${READLINE_LIBRARY} ${NCURSES_LIBRARY}) endif(NOT READLINE_FOUND) casacore-3.7.1/cmake/FindSOFA.cmake000066400000000000000000000012421476623553700167360ustar00rootroot00000000000000# - Try to find SOFA: the IAU Standards of Fundamental Astronomy libraries # Variables used by this module: # SOFA_ROOT_DIR - SOFA root directory # Variables defined by this module: # SOFA_FOUND - system has SOFA # SOFA_LIBRARY - the SOFA library (cached) # SOFA_LIBRARIES - the SOFA libraries # (identical to SOFA_LIBRARY) if(NOT SOFA_FOUND) find_library(SOFA_LIBRARY sofa HINTS ${SOFA_ROOT_DIR} PATH_SUFFIXES lib) mark_as_advanced(SOFA_LIBRARY) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(SOFA DEFAULT_MSG SOFA_LIBRARY) set(SOFA_LIBRARIES ${SOFA_LIBRARY}) endif(NOT SOFA_FOUND) casacore-3.7.1/cmake/FindWCSLIB.cmake000066400000000000000000000055451476623553700172030ustar00rootroot00000000000000# - Try to find WCSLIB: the FITS "World Coordinate System" library # Variables used by this module: # WCSLIB_ROOT_DIR - WCSLIB root directory # Variables defined by this module: # WCSLIB_FOUND - system has WCSLIB # WCSLIB_INCLUDE_DIR - the WCSLIB include directory (cached) # WCSLIB_INCLUDE_DIRS - the WCSLIB include directories # (identical to WCSLIB_INCLUDE_DIR) # WCSLIB_LIBRARY - the WCSLIB library (cached) # WCSLIB_LIBRARIES - the WCSLIB libraries # (identical to WCSLIB_LIBRARY) # WCSLIB_VERSION_STRING the found version of WCSLIB # Copyright (C) 2009 # ASTRON (Netherlands Institute for Radio Astronomy) # P.O.Box 2, 7990 AA Dwingeloo, The Netherlands # # This file is part of the LOFAR software suite. # The LOFAR software suite 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 3 of the License, or # (at your option) any later version. # # The LOFAR software suite 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 the LOFAR software suite. If not, see . # # $Id$ if(NOT WCSLIB_FOUND) find_path(WCSLIB_INCLUDE_DIR wcslib/wcsconfig.h HINTS ${WCSLIB_ROOT_DIR} PATH_SUFFIXES include) if(WCSLIB_INCLUDE_DIR) FILE(READ "${WCSLIB_INCLUDE_DIR}/wcslib/wcsconfig.h" WCSLIB_H) set(WCSLIB_VERSION_REGEX ".*#define WCSLIB_VERSION[^0-9]*([0-9]+)\\.([0-9]+).*") if ("${WCSLIB_H}" MATCHES ${WCSLIB_VERSION_REGEX}) STRING(REGEX REPLACE ${WCSLIB_VERSION_REGEX} "\\1.\\2" WCSLIB_VERSION_STRING "${WCSLIB_H}") STRING(REGEX REPLACE "^([0-9]+)[.]([0-9]+)" "\\1" WCSLIB_VERSION_MAJOR ${WCSLIB_VERSION_STRING}) STRING(REGEX REPLACE "^([0-9]+)[.]([0-9]+)" "\\2" WCSLIB_VERSION_MINOR ${WCSLIB_VERSION_STRING}) else () set(WCSLIB_VERSION_STRING "Unknown") endif () endif(WCSLIB_INCLUDE_DIR) find_library(WCSLIB_LIBRARY wcs HINTS ${WCSLIB_ROOT_DIR} PATH_SUFFIXES lib) find_library(M_LIBRARY m) mark_as_advanced(WCSLIB_INCLUDE_DIR WCSLIB_LIBRARY M_LIBRARY) if(CMAKE_VERSION VERSION_LESS "2.8.3") find_package_handle_standard_args(WCSLIB DEFAULT_MSG WCSLIB_LIBRARY M_LIBRARY WCSLIB_INCLUDE_DIR) else () include(FindPackageHandleStandardArgs) find_package_handle_standard_args(WCSLIB REQUIRED_VARS WCSLIB_LIBRARY M_LIBRARY WCSLIB_INCLUDE_DIR VERSION_VAR WCSLIB_VERSION_STRING) endif () set(WCSLIB_INCLUDE_DIRS ${WCSLIB_INCLUDE_DIR}) set(WCSLIB_LIBRARIES ${WCSLIB_LIBRARY} ${M_LIBRARY}) endif(NOT WCSLIB_FOUND) casacore-3.7.1/cmake/PCHSupport.cmake000066400000000000000000000017621476623553700174230ustar00rootroot00000000000000option (USE_PCH "Use pre-compiled headers when possible to speed up build" YES) # PCH support is only available from cmake 3.16 onwards if(NOT ${CMAKE_VERSION} VERSION_GREATER "3.16" AND USE_PCH) message(STATUS "Pre-compile headers only available from cmake version >= 3.16, turning OFF") set(USE_PCH NO) endif() function(init_pch_support shlib headers) if (NOT USE_PCH) return() endif() target_precompile_headers(${shlib} PRIVATE ${headers}) set(_pch_pie_target ${shlib}_pch_pie) set(_pch_pie_target ${_pch_pie_target} PARENT_SCOPE) file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/empty.cpp) add_library(${_pch_pie_target} STATIC ${CMAKE_CURRENT_BINARY_DIR}/empty.cpp) set_target_properties(${_pch_pie_target} PROPERTIES POSITION_INDEPENDENT_CODE OFF) target_precompile_headers(${_pch_pie_target} PRIVATE ${headers}) endfunction() function(add_pch_support target) if (NOT USE_PCH) return() endif() target_precompile_headers(${target} REUSE_FROM ${_pch_pie_target}) endfunction() casacore-3.7.1/cmake/PythonInstall.cmake000066400000000000000000000103131476623553700202140ustar00rootroot00000000000000# - Install Python source files. # python_install(source1..sourceN DESTINATION install_dir) # Install Python source files and byte-compile them in the directory # ${PYTHON_INSTALL_DIR}/${install_dir}. # Copyright (C) 2008-2009 # ASTRON (Netherlands Foundation for Research in Astronomy) # P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, seg@astron.nl # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # $Id: PythonInstall.cmake 23473 2013-01-10 08:02:35Z loose $ # Search for the Python interpreter. find_package(PythonInterp) # Derive the Python site-packages installation directory and build directory. if(PYTHON_EXECUTABLE) set(_cmd "from distutils.sysconfig import get_python_lib" "print(get_python_lib(plat_specific=True, prefix=''))") execute_process( COMMAND "${PYTHON_EXECUTABLE}" "-c" "${_cmd}" OUTPUT_VARIABLE _pydir ERROR_VARIABLE _pyerr OUTPUT_STRIP_TRAILING_WHITESPACE) if(_pyerr) message(FATAL_ERROR "Python command failed:\n${_pyerr}") endif(_pyerr) set(PYTHON_BUILD_DIR "${CMAKE_BINARY_DIR}/${_pydir}" CACHE PATH "Build directory for Python extensions" FORCE) set(PYTHON_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${_pydir}" CACHE PATH "Installation directory for Python extensions" FORCE) endif(PYTHON_EXECUTABLE) # # macro python_install # macro(python_install) # Precondition check. if(NOT PYTHON_EXECUTABLE) message(FATAL_ERROR "python_install: Python interpreter not available") endif(NOT PYTHON_EXECUTABLE) # Parse arguments. string(REGEX REPLACE ";?DESTINATION.*" "" _py_files "${ARGN}") string(REGEX MATCH "DESTINATION;.*" _dest_dir "${ARGN}") string(REGEX REPLACE "^DESTINATION;" "" _dest_dir "${_dest_dir}") if(_py_files MATCHES "^$") message(FATAL_ERROR "python_install: no sources files specified") endif(_py_files MATCHES "^$") if(_dest_dir MATCHES "^$" OR _dest_dir MATCHES ";") message(FATAL_ERROR "python_install: destination directory invalid") endif(_dest_dir MATCHES "^$" OR _dest_dir MATCHES ";") # Set python package build/install directory. set(_inst_dir "${PYTHON_INSTALL_DIR}/${_dest_dir}") set(_build_dir "${PYTHON_BUILD_DIR}/${_dest_dir}") # Install and byte-compile each Python file. foreach(_py ${_py_files}) get_filename_component(_py_path ${_py} PATH) get_filename_component(_py_abs ${_py} ABSOLUTE) # Create a symlink to each Python file; needed to mimic install tree. file(MAKE_DIRECTORY ${_build_dir}/${_py_path}) execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${_py_abs} ${_build_dir}/${_py}) install(FILES ${_py} DESTINATION ${_inst_dir}/${_py_path}) set(_py_code "import py_compile" "print('-- Byte-compiling: ${_inst_dir}/${_py}')" "py_compile.compile('${_inst_dir}/${_py}', doraise=True)") install(CODE "execute_process(COMMAND ${PYTHON_EXECUTABLE} -c \"${_py_code}\" RESULT_VARIABLE _result) if(NOT _result EQUAL 0) message(FATAL_ERROR \"Byte-compilation FAILED: ${_inst_dir}/${_py}\") endif(NOT _result EQUAL 0)") endforeach(_py ${_py_files}) # Make sure that there's a __init__.py file in each build/install directory. string(REGEX REPLACE "/" ";" _dir_list ${_dest_dir}) set(_init_dir) foreach(_dir ${_dir_list}) set(_init_dir "${_init_dir}/${_dir}") execute_process(COMMAND ${CMAKE_COMMAND} -E touch "${PYTHON_BUILD_DIR}${_init_dir}/__init__.py") install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E touch \"${PYTHON_INSTALL_DIR}${_init_dir}/__init__.py\")") endforeach(_dir ${_dir_list}) endmacro(python_install) casacore-3.7.1/cmake/SelectLibraryConfigurations.cmake000066400000000000000000000077321476623553700230760ustar00rootroot00000000000000# select_library_configurations( basename ) # # This macro takes a library base name as an argument, and will choose good # values for basename_LIBRARY, basename_LIBRARIES, basename_LIBRARY_DEBUG, and # basename_LIBRARY_RELEASE depending on what has been found and set. If only # basename_LIBRARY_RELEASE is defined, basename_LIBRARY, basename_LIBRARY_DEBUG, # and basename_LIBRARY_RELEASE will be set to the release value. If only # basename_LIBRARY_DEBUG is defined, then basename_LIBRARY, # basename_LIBRARY_DEBUG and basename_LIBRARY_RELEASE will take the debug value. # # If the generator supports configuration types, then basename_LIBRARY and # basename_LIBRARIES will be set with debug and optimized flags specifying the # library to be used for the given configuration. If no build type has been set # or the generator in use does not support configuration types, then # basename_LIBRARY and basename_LIBRARIES will take only the release values. #============================================================================= # Copyright 2009 Kitware, Inc. # Copyright 2009 Will Dicharry # Copyright 2005-2009 Kitware, Inc. # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= # (To distributed this file outside of CMake, substitute the full # License text for the above reference.) # This macro was adapted from the FindQt4 CMake module and is maintained by Will # Dicharry . # Utility macro to check if one variable exists while another doesn't, and set # one that doesn't exist to the one that exists. macro( _set_library_name basename GOOD BAD ) if( ${basename}_LIBRARY_${GOOD} AND NOT ${basename}_LIBRARY_${BAD} ) set( ${basename}_LIBRARY_${BAD} ${${basename}_LIBRARY_${GOOD}} ) set( ${basename}_LIBRARY ${${basename}_LIBRARY_${GOOD}} ) set( ${basename}_LIBRARIES ${${basename}_LIBRARY_${GOOD}} ) endif( ${basename}_LIBRARY_${GOOD} AND NOT ${basename}_LIBRARY_${BAD} ) endmacro( _set_library_name ) macro( select_library_configurations basename ) # if only the release version was found, set the debug to be the release # version. _set_library_name( ${basename} RELEASE DEBUG ) # if only the debug version was found, set the release value to be the # debug value. _set_library_name( ${basename} DEBUG RELEASE ) if (${basename}_LIBRARY_DEBUG AND ${basename}_LIBRARY_RELEASE ) # if the generator supports configuration types or CMAKE_BUILD_TYPE # is set, then set optimized and debug options. if( CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE ) set( ${basename}_LIBRARY optimized ${${basename}_LIBRARY_RELEASE} debug ${${basename}_LIBRARY_DEBUG} ) set( ${basename}_LIBRARIES optimized ${${basename}_LIBRARY_RELEASE} debug ${${basename}_LIBRARY_DEBUG} ) else( CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE ) # If there are no configuration types or build type, just use # the release version set( ${basename}_LIBRARY ${${basename}_LIBRARY_RELEASE} ) set( ${basename}_LIBRARIES ${${basename}_LIBRARY_RELEASE} ) endif( CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE ) endif( ${basename}_LIBRARY_DEBUG AND ${basename}_LIBRARY_RELEASE ) set( ${basename}_LIBRARY ${${basename}_LIBRARY} CACHE FILEPATH "The ${basename} library" ) if( ${basename}_LIBRARY ) set( ${basename}_FOUND TRUE ) endif( ${basename}_LIBRARY ) mark_as_advanced( ${basename}_LIBRARY ${basename}_LIBRARY_RELEASE ${basename}_LIBRARY_DEBUG ) endmacro( select_library_configurations ) casacore-3.7.1/cmake/cmake_assay000077500000000000000000000022721476623553700166150ustar00rootroot00000000000000#!/bin/sh # This script executes a casacore test program using casacore_assay. # It copies the possible source files (run,py,in,out) needed by the test # to the working directory and removes them afterwards. # # It returns 0 for the UNTESTED status (3), otherwise cmake sees it as an error. # Get the source root directory. rootdir=`echo $0 | sed -e 's%/cmake/cmake_assay%%'` # Get the package directory (is always the last 3 levels). pkg=`pwd | sed -e 's%.*\(/[^/][^/]*/[^/][^/]*/[^/][^/]*\)$%\1%'` testsrcdir=$rootdir$pkg export testsrcdir # Copy possible files needed for the test. for TYP in run py in out do if [ -e $testsrcdir/$1.$TYP ]; then cp $testsrcdir/$1.$TYP . fi done for FIL in $testsrcdir/$1.in_* do cp -r $FIL . done > /dev/null 2>&1 # avoid error message if no such files #for FIL in $testsrcdir/*.data_* #do # cp -r $FIL . #done > /dev/null 2>&1 # avoid error message if no such files # Execute the test. "$rootdir/build-tools/casacore_assay" "$@" status=$? # Remove the possibly copied files. rm -rf $1.{run,py,in,out} $1.in_* #*.data_* # Return status 0 if untested (otherwise cmake sees it as an error). if [ $status = 3 ]; then status=0 fi exit $status casacore-3.7.1/coordinates/000077500000000000000000000000001476623553700156365ustar00rootroot00000000000000casacore-3.7.1/coordinates/CMakeLists.txt000066400000000000000000000032051476623553700203760ustar00rootroot00000000000000# # CASA coordinates # add_library (casa_coordinates Coordinates/Coordinate.cc Coordinates/CoordinateSystem.cc Coordinates/CoordinateUtil.cc Coordinates/Direction2Coordinate.cc Coordinates/DirectionCoordinate.cc Coordinates/FITSCoordinateUtil.cc Coordinates/GaussianConvert.cc Coordinates/LinearCoordinate.cc Coordinates/LinearXform2.cc Coordinates/LinearXform.cc Coordinates/ObsInfo.cc Coordinates/Projection.cc Coordinates/Spectral2Coordinate.cc Coordinates/SpectralCoordinate.cc Coordinates/QualityCoordinate.cc Coordinates/StokesCoordinate.cc Coordinates/TabularCoordinate.cc ) set(top_level_headers Coordinates.h ) init_pch_support(casa_coordinates ${top_level_headers}) target_link_libraries (casa_coordinates casa_fits ${WCSLIB_LIBRARIES} ${CASACORE_ARCH_LIBS}) install (TARGETS casa_coordinates LIBRARY DESTINATION lib${LIB_SUFFIX} ARCHIVE DESTINATION lib${LIB_SUFFIX} LIBRARY PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) install (FILES Coordinates/DirectionCoordinate.h Coordinates/LinearXform.h Coordinates/SpectralCoordinate.h Coordinates/Coordinate.h Coordinates/StokesCoordinate.h Coordinates/QualityCoordinate.h Coordinates/FITSCoordinateUtil.h Coordinates/CoordinateSystem.h Coordinates/FrequencyAligner.h Coordinates/FrequencyAligner.tcc Coordinates/Projection.h Coordinates/ObsInfo.h Coordinates/CoordinateUtil.h Coordinates/GaussianConvert.h Coordinates/LinearCoordinate.h Coordinates/TabularCoordinate.h DESTINATION include/casacore/coordinates/Coordinates ) install (FILES ${top_level_headers} DESTINATION include/casacore/coordinates ) add_subdirectory (Coordinates/test ${EXCL_ALL}) casacore-3.7.1/coordinates/Coordinates.h000066400000000000000000000323031476623553700202620ustar00rootroot00000000000000//# Coordinates.h : Classes to interconvert computation positions with physical //# Copyright (C) 1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef COORDINATES_COORDINATES_H #define COORDINATES_COORDINATES_H //# Module includes #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // // // Classes to interconvert pixel and world (physical) coordinates // // //
      • Knowledge of astronomical coordinate conversions in general. Probably the // best documents are the papers by Mark Calabretta and Eric Greisen. // The initial draft from 1996 can be found at // http://www.atnf.csiro.au/~mcalabre. It is this draft that the // Coordinate classes are based upon. Since then, this paper has evolved // into three which can be found at the above address, and will be published in the // Astronomy and Astrophysics Supplement Series (probably in 2000). // The design has changed since the initial draft. When these papers // are finalized, and the IAU has ratified the new standards, WCSLIB // (Mark Calabretta's implementation of these conventions) will be // revised for the new designs. At that time, the Coordinate classes // may also be revised. //
      • Generic Casacore classes; especially those in the // Arrays module. //
      • The Measures module. // // // // // // The primary notion is that a Coordinate // can interconvert between a length "n" Vector (the // pixel coordinate) and a length "m" Vector (the // "world" coordinate). Note that "m" and "n" do not in // principle have to be the same (so that one can get both the RA and DEC from // an image slice, for example), however in practice they currently always are. // Each Coordinate has the full mapping from pixel to world coordinates, i.e. // it has a reference value, reference pixel, increments, and an arbitrary // transformation matrix. To go from a pixel to a world coordinate the following // steps are applied: //
          //
        1. The reference pixel is subtracted from the pixel position. //
        2. The result is multiplied by the transformation matrix. //
        3. The result is multiplied by an increment per output axis to convert // it into physical coordinates. //
        4. For some coordinate types (e.g., Direction), a non-linear function is // applied to this result. //
        // // The classes are arranged as follows. The base class is // Coordinate which defines the // interface. Classes derived from it are //
          //
        1. DirectionCoordinate //
        2. LinearCoordinate //
        3. SpectralCoordinate //
        4. TabularCoordinate //
        5. StokesCoordinate //
        6. CoordinateSystem //
        // // Other classes are Projection // which is used to specify an astronomical projection for // DirectionCoordinates, and LinearXform // a helper class which the application programmer will not interact with. // // CoordinateSystem is // the class that application programmers will usually interact // with. A CoordinateSystem consists of a collection of the other // classes derived from Coordinate. Normally one group will be for // RA/DEC, another for a Stokes axis, and another group for the spectral axis. // The axes may be transposed arbitrarily, for example RA could be // the first axis, and DEC the third. // // Normally the CoordinateSystem being manipulated will be embedded in a PagedImage // or other object. Note that the axes of the PagedImage do not // necessarily map directly to the axes in the CoordinateSystem. Functionality // is provided to determine this mapping. // // One or more axes from the CoordinateSystem may be removed. Pixel axes and/or // world axes may be removed. You are encouraged to leave all the world axes // when you remove pixel axes. //
        // If a world axis is removed, the corresponding pixel axis is also removed. // This means that one can be sure that a pixel axis always has a // corresponding world axis (it makes no sense otherwise). The opposite is // not necessarily true: a world axis can exist without a pixel axis. // // The linear transformation and sky projection computations are carried out in an // underlying library -- WCSLIB -- written by Mark Calabretta of the ATNF. // //
        // // // // All pixels coordinates are zero relative. // // // // First, let's make a DirectionCoordinate --- used to represent a direction, // usually an RA/DEC, but it could also be, e.g., an AZ/EL pair. // // Matrix xform(2,2); // 1 // xform = 0.0; xform.diagonal() = 1.0; // 2 // Quantum refLon(135.0, "deg"); // Quantum refLat(60.0, "deg"); // Quantum incLon(-1.0, "deg"); // Quantum incLat(1.0, "deg"); // DirectionCoordinate radec(MDirection::J2000, // 3 // Projection(Projection::SIN), // 4 // refLon, refLat, // 5 // incLon, incLat, // 6 // xform, // 7 // 128, 128); // 8 // //
          //
        • 1-2:Here we set up a diagonal transformation matrix. // Normally this matrix should be diagonal, however if you wanted // to introduce a rotation or skew, you would do it through this // matrix. //
        • 3:This defines the astronomical type of the world // coordinate. Most of the time it will probably be J2000 // or B1950, but there are many other possibilities as listed // in the MDirection class // header. //
        • 4:The Projection class // defines the "geometry" that is used to map xy<-->world. SIN // is the most common projection for radio interferometers. Note that // SIN can optionally take parameters as defined in Calabretta and Greisen. // If not provided, they default to 0.0, which is the "old" SIN // convention. //
        • 5:Set the reference position to RA=135, DEC=60 degrees. // Note that the native units of a DirectionCoordinate is radians. //
        • 6: Set the increments to -1 degree in RA, and +1 degree // in DEC. //
        • 7: Set the previously defined transformation matrix. //
        • 8: Set the zero-relative reference pixel. Note that it does // not have to be incremental. At the reference pixel, the world // coordinate has the reference value. //
        // // Although we happeend to create our DirectionCoordinate with Quanta in degrees, // these have been converted to radians by the constructor. We can set the native units // to degrees if we wish as follows: // // Vector units(2); units = "deg"; // 9 // radec.setWorldAxisUnits(units); // 10 // // The increment and reference value are updated appropriately. // // Set up a couple of vectors to use the world and pixel coordinate values. // // Vector world(2), pixel(2); // 11 // pixel = 138.0; // 12 // // We use 138 as an abitrary pixel position which is near the reference pixel // so we can tell if the answers look foolish or not. // // We can actually perform a transformation like this as follows. If // it succeeds we print the value of the world coordinate. // // Bool ok = radec.toWorld(world, pixel); // 13 // if (!ok) { // 14 // cout << "Error: " << radec.errorMessage() << endl; // 15 // return 1; // 16 // } // 17 // cout << world << " <--- " << pixel << endl; // 18 // // There is an overloaded "toWorld" function that produces an MDirection // in case you want to, e.g., find out what the position in B1950 coordinates // would be. // // The reverse transformation takes place similarly: // // ok = radec.toPixel(pixel, world); // 19 // // // Suppose we have an image with a Stokes axis. It can be set up as follows: // // Vector iquv(4); // iquv(0) = Stokes::I; iquv(1) = Stokes::Q; // 21 // iquv(2) = Stokes::U; iquv(3) = Stokes::V; // 22 // StokesCoordinate stokes(iquv); // 23 // // We create an integer array the same length as the Stokes axis, and place // the corresponding Stokes enum into each element of the array. The values // must be unique, e.g. there can only be one "I" plane. // Besides the generic Vector toWorld/toPixel interface, // you can also directly interconvert between Stokes enum and and (zero-relative) // plane number: // // Int plane; // 24 // ok = stokes.toPixel(plane, Stokes::Q); // 25 // // Here it will return True and set plane to 1. On the other // hand, it would return False for: // // ok = stokes.toPixel(plane, Stokes::XX); // 26 // // since "XX" is not one of the Stokes enumerations we used to create this // coordinate. // // A Spectral ("frequency") coordinate may be created as follows: // // SpectralCoordinate spectral(MFrequency::TOPO, // 27 // 1.4E+9, // 28 // 2.0E+4, // 29 // 0, // 30 // 1420.40575E+6); // 31 // // The default frequency units of a spectral coordinate are Hz, although they // may be changed to whatever is convenient. The first line (27) defines the // type of frequency we have -- topocentric here. The second (28) line // defines the frequency at the reference pixel, 0 (28) here. The channel // increment is defined on line 29. A rest frequency of the spectral may // be provided. It is useful in calculating doppler velocities. These calculations // are carried out by the MFrequency and // MDoppler classes of the Measures system. //
        // // // The primary motivation is to provide support for converting pixel locations in an // image to physical ("world") positions. // // //
      • Add measures interfaces that handle reference frame conversions //
      • offset coordinates // // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/coordinates/Coordinates/000077500000000000000000000000001476623553700201105ustar00rootroot00000000000000casacore-3.7.1/coordinates/Coordinates/Coordinate.cc000066400000000000000000001153301476623553700225110ustar00rootroot00000000000000//#Coordinate.cc: this defines the Coordinate class //# Copyright (C) 1997,1998,1999,2000,2001,2002,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Coordinate::Coordinate() : worldMin_p(0), worldMax_p(0) {} Coordinate::Coordinate(const Coordinate& other) : worldMin_p(0), worldMax_p(0), error_p(other.error_p) { worldMin_p = other.worldMin_p; worldMax_p = other.worldMax_p; } Coordinate& Coordinate::operator=(const Coordinate& other) { if (this != &other) { worldMin_p.resize(other.worldMin_p.nelements()); worldMax_p.resize(other.worldMax_p.nelements()); // worldMin_p = other.worldMin_p; worldMax_p = other.worldMax_p; error_p = other.error_p; } return *this; } Coordinate::~Coordinate() {} Bool Coordinate::toWorldMany(Matrix& world, const Matrix& pixel, Vector& failures) const { AlwaysAssert(nPixelAxes()==pixel.nrow(), AipsError); const uInt nTransforms = pixel.ncolumn(); world.resize(nWorldAxes(), nTransforms); failures.resize(nTransforms); // Vector pixTmp(nPixelAxes()); Vector worldTmp(nWorldAxes()); // ArrayAccessor > jPixel(pixel); ArrayAccessor > jWorld(world); // String errorMsg; uInt nError = 0; uInt k,l; ArrayAccessor > iPixel, iWorld; // for (jPixel.reset(),jWorld.reset(),l=0; jPixel!=jPixel.end(); ++jPixel,++jWorld,l++) { iPixel = jPixel; // Partial assignment for (iPixel.reset(),k=0; iPixel!=iPixel.end(); ++iPixel,k++) { pixTmp[k] = *iPixel; } // failures[l] = !toWorld (worldTmp, pixTmp); if (failures[l]) { nError++; if (nError == 1) errorMsg = errorMessage(); // Save the first error message } else { iWorld = jWorld; // Partial assigment for (iWorld.reset(),k=0; iWorld!=iWorld.end(); ++iWorld,k++) { *iWorld = worldTmp[k]; } } } // if (nError != 0) set_error(errorMsg); // put back the first error return (nError==0); } Bool Coordinate::toPixelMany(Matrix& pixel, const Matrix& world, Vector& failures) const { AlwaysAssert(nWorldAxes()==world.nrow(), AipsError); const uInt nTransforms = world.ncolumn(); pixel.resize(nPixelAxes(), nTransforms); failures.resize(nTransforms); // Vector pixTmp(nPixelAxes()); Vector worldTmp(nWorldAxes()); // ArrayAccessor > jPixel(pixel); ArrayAccessor > jWorld(world); // String errorMsg; uInt nError = 0; uInt k,l; ArrayAccessor > iPixel, iWorld; // for (jWorld.reset(),jPixel.reset(),l=0; jWorld!=jWorld.end(); ++jWorld,++jPixel,l++) { iWorld = jWorld; // Partial assigment for (iWorld.reset(),k=0; iWorld!=iWorld.end(); ++iWorld,k++) { worldTmp[k] = *iWorld; } // failures[l] = !toPixel(pixTmp, worldTmp); if (failures[l]) { nError++; if (nError == 1) errorMsg = errorMessage(); // Save the first error message } else { iPixel = jPixel; // Partial assignment for (iPixel.reset(),k=0; iPixel!=iPixel.end(); ++iPixel,k++) { *iPixel= pixTmp[k]; } } } // if (nError != 0) set_error(errorMsg); // put back the first error return (nError==0); } Bool Coordinate::toMix(Vector& worldOut, Vector& pixelOut, const Vector& worldIn, const Vector& pixelIn, const Vector& worldAxes, const Vector& pixelAxes, const Vector&, const Vector&) const // // Default implementation ok for non-coupled coordinated like // Linear. Coupled coordinates like DirectionCoordinate // need their own implementation // { static Vector pixel_tmp; static Vector world_tmp; const uInt nWorld = worldAxes.nelements(); const uInt nPixel = pixelAxes.nelements(); // DebugAssert(nWorld == nWorldAxes(), AipsError); DebugAssert(worldIn.nelements()==nWorld, AipsError); DebugAssert(nPixel == nPixelAxes(), AipsError); DebugAssert(pixelIn.nelements()==nPixel, AipsError); // for (uInt i=0; i &units) { if (units.nelements() != nWorldAxes()) { set_error("Wrong number of elements in units vector"); return False; } else { // If the units are unchanged just return True. Vector old = worldAxisUnits(); if (allEQ(old, units)) { return True; } } Bool ok = True; String error; Vector factor; ok = find_scale_factor(error, factor, units, worldAxisUnits()); if (ok) { ok = setIncrement(increment() * factor); if (ok) { ok = setReferenceValue(referenceValue() * factor); } } else { set_error(error); } return ok; } void Coordinate::checkFormat(Coordinate::formatType& format, const Bool ) const { // Scientific or fixed formats only are allowed. // Absolute or offset is irrelevant if (format != Coordinate::SCIENTIFIC && format != Coordinate::FIXED) format = Coordinate::DEFAULT; // if (format == Coordinate::DEFAULT) format = Coordinate::MIXED; } void Coordinate::getPrecision(Int &precision, Coordinate::formatType& format, Bool absolute, Int defPrecScientific, Int defPrecFixed, Int ) const { // Absolute or offset is irrelevant checkFormat (format, absolute); if (format == Coordinate::SCIENTIFIC) { if (defPrecScientific >= 0) { precision = defPrecScientific; } else { precision = 6; } } else if (format == Coordinate::FIXED) { if (defPrecFixed >= 0) { precision = defPrecFixed; } else { precision = 6; } // } else if (format == Coordinate::MIXED) { // Auto format by STL formatter so precision not relevant } else { // RI 20091213 but we should still set it so its not later accessed uninitalized: // (also make this branch catch Coordinate::DEFAULT precision = 6; } } String Coordinate::format( String& units, Coordinate::formatType format, Double worldValue, uInt worldAxis, Bool isAbsolute, Bool showAsAbsolute, Int precision, Bool usePrecForMixed ) const // // isAbsolute // T means the worldValue is given as absolute // F means the worldValue is given as relative // // showAsAbsolute // T means the worldValue should be formatted as absolute // F means the worldValue should be formatted as relative // { DebugAssert(worldAxis < nWorldAxes(), AipsError); // Check format Coordinate::formatType form = format; checkFormat (form, showAsAbsolute); // Set default precision Int prec = precision; if (prec < 0) getPrecision(prec, form, showAsAbsolute, -1, -1, -1); // Convert given world value to absolute or relative as needed static Vector world; if (world.nelements()!=nWorldAxes()) world.resize(nWorldAxes()); // if (showAsAbsolute) { if (!isAbsolute) { world = 0.0; world(worldAxis) = worldValue; makeWorldAbsolute(world); worldValue = world(worldAxis); } } else { if (isAbsolute) { world = referenceValue(); world(worldAxis) = worldValue; makeWorldRelative(world); worldValue = world(worldAxis); } } // If units are empty, use native unit. // Convert to specified unit if possible String nativeUnit = worldAxisUnits()(worldAxis); if (units.empty()) units = nativeUnit; // Now check validity of unit Unit nativeUnitU(nativeUnit); Unit currentUnitU(units); // if (currentUnitU != nativeUnitU) { throw(AipsError("Requested units are invalid for this Coordinate")); } else { static Quantum q; q.setValue(worldValue); q.setUnit(nativeUnitU); worldValue = q.getValue(currentUnitU); } ostringstream oss; bool precision_set = false; // ensure that there is enough precision... may need more tweaking... Vector inc(increment()); if ( inc.nelements( ) > 0 && ((worldValue - trunc(worldValue)) != 0) ) { static Quantum qdelta; qdelta.setValue(inc(0)); qdelta.setUnit(nativeUnitU); Double worldIncr = qdelta.getValue(currentUnitU); int needed_precision = 1; for ( Double compare = 1.0; fabs(worldValue) > compare; compare *= 10 ) { ++needed_precision; } if ( fabs(worldIncr) < 1.0 ) for ( Double compare = 0.1; fabs(worldIncr) < compare; compare /= 10 ) { ++needed_precision; } else { int adjust = 1; for ( Double compare = 1.0; fabs(worldIncr) > compare; compare *= 10 ) { ++adjust; } if ( adjust < needed_precision ) needed_precision -= adjust; } if ( needed_precision > 5 ) { oss.precision( needed_precision + 1 ); precision_set = true; } } // Format and get units. if (form == Coordinate::MIXED) { if (usePrecForMixed) { oss << setprecision(prec); } oss << worldValue; } else if (form == Coordinate::SCIENTIFIC) { oss.setf(ios::scientific, ios::floatfield); if ( precision_set == false ) oss.precision(prec); oss << worldValue; } else if (form == Coordinate::FIXED) { oss.setf(ios::fixed, ios::floatfield); if ( precision_set == false ) oss.precision(prec); oss << worldValue; } // return String(oss); } String Coordinate::formatQuantity (String& units, Coordinate::formatType format2, const Quantum& worldValue, uInt worldAxis, Bool isAbsolute, Bool showAsAbsolute, Int precision) { DebugAssert(worldAxis < nWorldAxes(), AipsError); // Use derived class formatter return format(units, format2, worldValue.getValue(Unit(worldAxisUnits()(worldAxis))), worldAxis, isAbsolute, showAsAbsolute, precision); } // after = factor * before Bool Coordinate::find_scale_factor(String &error, Vector &factor, const Vector &units, const Vector &oldUnits) { factor.resize(units.nelements()); Bool ok = (units.nelements() == oldUnits.nelements()); if (!ok) { error = "units and oldUnits are different sizes!"; } else { // Try to find the scaling factors between the old and new units uInt n = units.nelements(); for (uInt i=0; i&, const Vector&) const { String tmp = String("Coordinates of type ") + showType() + String(" cannot be Fourier Transformed"); throw AipsError(tmp); } void Coordinate::fourierUnits (String& nameOut, String& unitOut, String& unitInCanon, Coordinate::Type type, Int axis, const String& unitIn, const String& nameIn) const // // A disgusting fudgy routine to work out some nice names and units // Fourier coordinates. Rather limited in its knowledge currently. // { Unit time("s"); Unit freq("Hz"); Unit rad("rad"); Unit unitIn2(unitIn); // if (type==Coordinate::DIRECTION) { if (unitIn2==rad) { unitInCanon = String("rad"); if (axis==0) { nameOut = String("UU"); } else if (axis==1) { nameOut = String("VV"); } else { throw(AipsError("Illegal DirectionCoordinate axis")); } unitOut = String("lambda"); } else { nameOut = String("Inverse(") + nameIn + String(")"); unitOut = String("1/") + unitIn; unitInCanon = unitIn; } } else if (type==Coordinate::LINEAR || type==Coordinate::SPECTRAL || type==Coordinate::TABULAR) { if (unitIn2==freq) { nameOut = String("Time"); unitOut = String("s"); unitInCanon = "Hz"; } else if (unitIn2==time) { nameOut = String("Frequency"); unitOut = String("Hz"); unitInCanon = "s"; } else { nameOut = String("Inverse(") + nameIn + String(")"); unitOut = String("1/") + unitIn; unitInCanon = unitIn; } } else if (type==Coordinate::STOKES) { throw (AipsError("Cannot provide Fourier coordinate name for Stokes coordinate")); } else if (type==Coordinate::QUALITY) { throw (AipsError("Cannot provide Fourier coordinate name for Quality coordinate")); } else if (type==Coordinate::COORDSYS) { throw (AipsError("Cannot provide Fourier coordinate name for CoordinateSystem coordinate")); } else { nameOut = String("Inverse(") + nameIn + String(")"); unitOut = String("1/") + unitIn; unitInCanon = unitIn; } } void Coordinate::makeWorldAbsoluteMany (Matrix& value) const { makeWorldAbsRelMany (value, True); } void Coordinate::makeWorldRelativeMany (Matrix& value) const { makeWorldAbsRelMany (value, False); } void Coordinate::makePixelAbsoluteMany (Matrix& value) const { makePixelAbsRelMany (value, True); } void Coordinate::makePixelRelativeMany (Matrix& value) const { makePixelAbsRelMany (value, False); } void Coordinate::makeWorldAbsRelMany (Matrix& value, Bool toAbs) const { Vector col(nWorldAxes()); Vector lastInCol(nWorldAxes()); Vector lastOutCol(nWorldAxes()); uInt k,l; Bool same; ArrayAccessor > i; ArrayAccessor > j(value); for (j.reset(),l=0; j!=j.end(); j++,l++) { i = j; same = True; for (i.reset(),k=0; i!=i.end(); i++,k++) { col[k] = *i; if (l==0 || (l!=0 && !casacore::near(col[k],lastInCol[k]))) same = False; } lastInCol = col; // if (same) { for (i.reset(),k=0; i!=i.end(); ++i,k++) { *i = lastOutCol[k]; } } else { if (toAbs) { makeWorldAbsolute(col); } else { makeWorldRelative(col); } // for (i.reset(),k=0; i!=i.end(); ++i,k++) { *i = col[k]; } lastOutCol = col; } } } void Coordinate::makePixelAbsRelMany (Matrix& value, Bool abs) const { Vector col(nPixelAxes()); Vector lastInCol(nPixelAxes()); Vector lastOutCol(nPixelAxes()); uInt k,l; Bool same; ArrayAccessor > i; ArrayAccessor > j(value); for (j.reset(),l=0; j!=j.end(); j++,l++) { i = j; same = True; for (i.reset(),k=0; i!=i.end(); i++,k++) { col[k] = *i; if (l==0 || (l!=0 && !casacore::near(col[k],lastInCol[k]))) same = False; } lastInCol = col; // if (same) { for (i.reset(),k=0; i!=i.end(); ++i,k++) { *i = lastOutCol[k]; } } else { if (abs) { makePixelAbsolute(col); } else { makePixelRelative(col); } // for (i.reset(),k=0; i!=i.end(); ++i,k++) { *i = col[k]; } lastOutCol = col; } } } void Coordinate::makeWorldAbsolute (Vector& world) const { DebugAssert(world.nelements()==nWorldAxes(),AipsError); world += referenceValue(); } void Coordinate::makeWorldAbsoluteRef (Vector& world, const Vector& refVal) const { DebugAssert(world.nelements()==nWorldAxes(),AipsError); DebugAssert(refVal.nelements()==nWorldAxes(),AipsError); world += refVal; } void Coordinate::makeWorldRelative (Vector& world) const { DebugAssert(world.nelements()==nWorldAxes(),AipsError); world -= referenceValue(); } void Coordinate::makePixelAbsolute (Vector& pixel) const { DebugAssert(pixel.nelements()==nPixelAxes(),AipsError); pixel += referencePixel(); } void Coordinate::makePixelRelative (Vector& pixel) const { DebugAssert(pixel.nelements()==nPixelAxes(),AipsError); pixel -= referencePixel(); } Bool Coordinate::setWorldMixRanges (const IPosition& shape) { const uInt n = shape.nelements(); if (n!=nPixelAxes()) { set_error("Shape has must be of length nPixelAxes"); return False; } AlwaysAssert(nPixelAxes()==nWorldAxes(), AipsError); // Use defaults if conversion fails setDefaultWorldMixRanges(); // Do conversions 25% off edge of image Vector pMin(n), pMax(n); Vector wMin, wMax; for (uInt i=0; i 0) { Double n2 = 1.5 * s2; pMin(i) = s2 - n2; pMax(i) = s2 + n2; } } Bool ok1 = toWorld(wMin, pMin); Bool ok2 = toWorld(wMax, pMax); if (ok1 && ok2) { for (uInt i=0; i 0) { // If shape not known use default value worldMin_p(i) = wMin(i); worldMax_p(i) = wMax(i); } } return True; } else { return False; } // return True; } void Coordinate::setDefaultWorldMixRanges () { const uInt n = nWorldAxes(); worldMin_p.resize(n); worldMax_p.resize(n); worldMin_p = -1.0e99; worldMax_p = 1.0e99; } Bool Coordinate::doNearPixel (const Coordinate& other, const Vector& thisAxes, const Vector& otherAxes, Double tol) const { if (type() != other.type()) { set_error("Coordinate types differ"); return False; } // if (allEQ(thisAxes, False) && allEQ(otherAxes, False)) { return True; } // if (nPixelAxes() != other.nPixelAxes()) { set_error("Number of pixel axes differs"); return False; } if (nWorldAxes() != other.nWorldAxes()) { set_error("Number of world axes differs"); return False; } // const Vector& thisRefVal(referenceValue()); const Vector& otherRefVal(other.referenceValue()); const Vector& thisInc(increment()); const Vector& otherInc(other.increment()); const Vector& thisRefPix(referencePixel()); const Vector& otherRefPix(other.referencePixel()); /* const Vector& thisNames(worldAxisNames()); const Vector& otherNames(other.worldAxisNames()); */ const Vector& thisUnits(worldAxisUnits()); const Vector& otherUnits(other.worldAxisUnits()); // const Matrix& thisPC(linearTransform()); const Matrix& otherPC(other.linearTransform()); if (thisPC.nrow() != otherPC.nrow()) { set_error ("PC matrices have different numbers of rows"); return False; } if (thisPC.ncolumn() != otherPC.ncolumn()) { set_error ("PC matrices have different numbers of columns"); return False; } // for (uInt i=0; i r1 = thisPC.row(i); Vector r2 = otherPC.row(i); for (uInt j=0; j c1 = thisPC.column(i); Vector c2 = otherPC.column(i); for (uInt j=0; j xf = linearTransform(); // Generate rotation matrix components Double angleRad = angle.getValue(Unit("rad")); Matrix rotm(2, 2); Double s = sin(-angleRad); Double c = cos(-angleRad); rotm(0, 0) = c; rotm(0, 1) = s; rotm(1, 0) = -s; rotm(1, 1) = c; // Create new linear transform matrix Matrix xform(2, 2); xform(0, 0) = rotm(0, 0) * xf(0, 0) + rotm(0, 1) * xf(1, 0); xform(0, 1) = rotm(0, 0) * xf(0, 1) + rotm(0, 1) * xf(1, 1); xform(1, 0) = rotm(1, 0) * xf(0, 0) + rotm(1, 1) * xf(1, 0); xform(1, 1) = rotm(1, 0) * xf(0, 1) + rotm(1, 1) * xf(1, 1); // Apply new linear transform matrix Coordinate* result = clone(); result->setLinearTransform(xform); return result; } Bool Coordinate::toWorldWCS (Vector& world, const Vector& pixel, ::wcsprm& wcs) const { const uInt nAxes = nPixelAxes(); world.resize(nAxes); // DebugAssert(pixel.nelements() == nAxes, AipsError); // Generate pointers and intermediaries for wcs Bool delPixel, delWorld; const double* pixelStore = pixel.getStorage(delPixel); double* worldStore = world.getStorage(delWorld); // double phi; double theta=0; // initialize, because wcslib not always sets theta // Convert from pixel to world with wcs units int stat; int iret; constexpr uInt NAXES_THRESHOLD = 10; // avoid dynamic memory allocation for modest number of coordinate axes // note that output stored in imgCrd is not used if (nAxes <= NAXES_THRESHOLD) { thread_local static double imgCrd[NAXES_THRESHOLD]; iret = wcsp2s (&wcs, 1, nAxes, pixelStore, imgCrd, &phi, &theta, worldStore, &stat); } else { Block imgCrd(nAxes); iret = wcsp2s (&wcs, 1, nAxes, pixelStore, imgCrd.storage(), &phi, &theta, worldStore, &stat); } pixel.freeStorage(pixelStore, delPixel); world.putStorage(worldStore, delWorld); // if (iret!=0) { String errorMsg = String("wcslib wcsp2s error: ") + wcsp2s_errmsg[iret]; set_error(errorMsg); return False; } // return True; } Bool Coordinate::toPixelWCS(Vector &pixel, const Vector &world, ::wcsprm& wcs) const { pixel.resize(world.nelements()); const uInt nAxes = nWorldAxes(); DebugAssert(world.nelements() == nAxes, AipsError); // Generate pointers and intermediaries for wcs Bool delPixel, delWorld; double* pixelStore = pixel.getStorage(delPixel); const double* worldStore = world.getStorage(delWorld); // double phi; double theta; // Convert with world with wcs units to pixel int stat; int iret; constexpr uInt NAXES_THRESHOLD = 10; // avoid dynamic memory allocation for modest number of coordinate axes // note that output stored in imgCrd is not used if (nAxes <= NAXES_THRESHOLD) { thread_local static double imgCrd[NAXES_THRESHOLD]; iret = wcss2p (&wcs, 1, nAxes, worldStore, &phi, &theta, imgCrd, pixelStore, &stat); } else { Block imgCrd(nAxes); iret = wcss2p (&wcs, 1, nAxes, worldStore, &phi, &theta, imgCrd.storage(), pixelStore, &stat); } pixel.putStorage(pixelStore, delPixel); world.freeStorage(worldStore, delWorld); // if (iret!=0) { String errorMsg = String("wcslib wcss2p error: ") + wcss2p_errmsg[iret]; set_error(errorMsg); return False; } // return True; } Bool Coordinate::toWorldManyWCS (Matrix& world, const Matrix& pixel, Vector& failures, ::wcsprm& wcs) const { uInt nTransforms = pixel.ncolumn(); uInt nAxes = nPixelAxes(); AlwaysAssert(pixel.nrow()==nAxes, AipsError); world.resize(pixel.shape()); failures.resize(nTransforms); // Generate pointers and intermediaries for wcs Bool deleteWorld, deletePixel; Double* pWorld = world.getStorage(deleteWorld); const Double* pPixel = pixel.getStorage(deletePixel); // Bool deleteImgCrd, deletePhi, deleteTheta, deleteStat; Matrix imgCrd(nAxes,nTransforms); Vector phi(nTransforms); Vector theta(nTransforms); Vector stat(nTransforms); // Convert from pixel to world with wcs units Double* pImgCrd = imgCrd.getStorage(deleteImgCrd); Double* pPhi = phi.getStorage(deletePhi); Double* pTheta = theta.getStorage(deleteTheta); Int* pStat = stat.getStorage(deleteStat); // int iret = wcsp2s (&wcs, nTransforms, nAxes, pPixel, pImgCrd, pPhi, pTheta, pWorld, pStat); for (uInt i=0; i& pixel, const Matrix& world, Vector& failures, ::wcsprm& wcs) const { uInt nTransforms = world.ncolumn(); uInt nAxes = nWorldAxes(); AlwaysAssert(world.nrow()==nAxes, AipsError); pixel.resize(world.shape()); failures.resize(nTransforms); // Generate wcs pointers and intermediaries Bool deleteWorld, deletePixel; Double* pPixel = pixel.getStorage(deletePixel); const Double* pWorld = world.getStorage(deleteWorld); // Bool deleteImgCrd, deletePhi, deleteTheta, deleteStat; Matrix imgCrd(nAxes,nTransforms); Vector phi(nTransforms); Vector theta(nTransforms); Vector stat(nTransforms); // Double* pImgCrd = imgCrd.getStorage(deleteImgCrd); Double* pPhi = phi.getStorage(deletePhi); Double* pTheta = theta.getStorage(deleteTheta); Int* pStat = stat.getStorage(deleteStat); // Convert from wcs units to pixel const int nC = nTransforms; int iret = wcss2p (&wcs, nC, nAxes, pWorld, pPhi, pTheta, pImgCrd, pPixel, pStat); for (uInt i=0; i& world, const Vector& toCurrentFactors) const { for (uInt i=0; i row(world.row(i)); // Reference row *= toCurrentFactors[i]; } } void Coordinate::fromCurrentMany(Matrix& world, const Vector& toCurrentFactors) const { for (uInt i=0; i row(world.row(i)); // Reference row /= toCurrentFactors[i]; } } void Coordinate::convertToMany (Matrix& world) const { AlwaysAssert(nWorldAxes()==world.nrow(), AipsError); Vector worldTmp(nWorldAxes()); ArrayAccessor > jWorld(world); ArrayAccessor > iWorld; // uInt k; for (jWorld.reset(); jWorld!=jWorld.end(); ++jWorld) { iWorld = jWorld; // Partial assignment for (iWorld.reset(),k=0; iWorld!=iWorld.end(); ++iWorld,k++) { worldTmp[k] = *iWorld; } // Convert this coordinate pair convertTo(worldTmp); // Fill back into Matrix iWorld = jWorld; // Partial assigment for (iWorld.reset(),k=0; iWorld!=iWorld.end(); ++iWorld,k++) { *iWorld = worldTmp[k]; } } } void Coordinate::convertFromMany (Matrix& world) const { AlwaysAssert(nWorldAxes()==world.nrow(), AipsError); Vector worldTmp(nWorldAxes()); ArrayAccessor > jWorld(world); ArrayAccessor > iWorld; // uInt k; for (jWorld.reset(); jWorld!=jWorld.end(); ++jWorld) { iWorld = jWorld; // Partial assignment for (iWorld.reset(),k=0; iWorld!=iWorld.end(); ++iWorld,k++) { worldTmp[k] = *iWorld; } // Convert this coordinate pair convertFrom(worldTmp); // Fill back into Matrix iWorld = jWorld; // Partial assigment for (iWorld.reset(),k=0; iWorld!=iWorld.end(); ++iWorld,k++) { *iWorld = worldTmp[k]; } } } void Coordinate::pcToXform (Matrix& xform, const ::wcsprm& wcs) const { uInt n = wcs.naxis; xform.resize(n,n); // uInt count = 0; for (uInt i=0; i& xform) const { uInt n = wcs.naxis; AlwaysAssert(xform.nrow()==n && xform.ncolumn()==n, AipsError); // uInt count = 0; for (uInt i=0; i lock(wcsset_mutex); if (int iret = wcsset(&wcs)) { String errmsg = "wcs wcsset_error: "; errmsg += wcsset_errmsg[iret]; throw(AipsError(errmsg)); } } //#if (WCSLIB_VERSION_MAJOR == 5 && WCSLIB_VERSION_MINOR >= 7) || WCSLIB_VERSION_MAJOR > 5 // In wcslib >= 5.7 wcssub internally calls wcssnpv/wcssnps to temporarily // set two global variables to a particular value before calling wcsini, which // in turn reads their values; after wcsini returns wcssub sets the variables to // their previous values. This situation creates a race condition between // concurrent, independent calls to wcssub and wcsini. Thus, we serialize them // with this lock static std::mutex wcs_initsubcopy_mutex; //#endif // WCSLIB_VERSION >= 5.7 void Coordinate::init_wcs(::wcsprm& wcs, int naxis) { //#if (WCSLIB_VERSION_MAJOR == 5 && WCSLIB_VERSION_MINOR >= 7) || WCSLIB_VERSION_MAJOR > 5 std::lock_guard lock(wcs_initsubcopy_mutex); //#endif // WCSLIB_VERSION >= 5.7 if (int iret = wcsini(1, naxis, &wcs)) { String errmsg = "wcs wcsini_error: "; errmsg += wcsini_errmsg[iret]; throw(AipsError(errmsg)); } } void Coordinate::sub_wcs(const ::wcsprm &src, int &nsub, int axes[], ::wcsprm &dst) { // see init_wcs //#if (WCSLIB_VERSION_MAJOR == 5 && WCSLIB_VERSION_MINOR >= 7) || WCSLIB_VERSION_MAJOR > 5 std::lock_guard lock(wcs_initsubcopy_mutex); //#endif // WCSLIB_VERSION >= 5.7 if (int iret = wcssub(1, &src, &nsub, axes, &dst)) { String errmsg = "wcslib wcssub error: "; errmsg += wcsini_errmsg[iret]; throw(AipsError(errmsg)); } } void Coordinate::copy_wcs(const ::wcsprm &src, ::wcsprm &dst) { // see init_wcs //#if (WCSLIB_VERSION_MAJOR == 5 && WCSLIB_VERSION_MINOR >= 7) || WCSLIB_VERSION_MAJOR > 5 std::lock_guard lock(wcs_initsubcopy_mutex); //#endif // WCSLIB_VERSION >= 5.7 if (int iret = wcssub(1, &src, 0, 0, &dst)) { String errmsg = "wcslib wcscopy error: "; errmsg += wcsini_errmsg[iret]; throw(AipsError(errmsg)); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/coordinates/Coordinates/Coordinate.h000066400000000000000000000627201476623553700223570ustar00rootroot00000000000000//# Coordinate.h: Interface for converting between world and pixel coordinates //# Copyright (C) 1997,1999,2000,2001,2002,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef COORDINATES_COORDINATE_H #define COORDINATES_COORDINATE_H #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template class Quantum; class IPosition; class RecordInterface; class Projection; // // Interface for converting between world and pixel coordinates. // // // // // //
      • Knowledge of astronomical coordinate conversions in general. Probably the // best documents are the papers by Mark Calabretta and Eric Greisen. // The initial draft from 1996 can be found at // http://www.atnf.csiro.au/~mcalabre. It is this draft that the // Coordinate classes are based upon. Since then, this paper has evolved // into three which can be found at the above address, and will be published in the // Astronomy and Astrophysics Supplement Series (probably in 2000). // The design has changed since the initial draft. When these papers // are finalized, and the IAU has ratified the new standards, WCSLIB // (Mark Calabretta's implementation of these conventions) will be // revised for the new designs. At that time, the Coordinate classes // may also be revised. //
      • Generic Casacore classes; especially those in the // Arrays module. //
      • Perhaps some of the information in the // Measures module. // // // // The Coordinate class defines the generic interface whereby a pixel position // is converted to a world (sky, frequency, stokes, ...) position and vice // versa. The pixel and world coordinates are in general // multi-dimensional values. In general there need not be the same number of // pixel and world axes, although this will normally be the case. // // The fundamental model is that a pixel is first converted into a relative // physical coordinate by: //
          //
        1. Subtracting a reference pixel value from the pixel location; then //
        2. Multiplying this offset by a general transformation matrix (usually // to account for rotation, but any matrix is allowed); then //
        3. Multiplying this product by an increment in physical units. //
        // After this linear stage, the final coordinate value is computed from this // relative physical unit and a reference value, and possibly some other // parameters. In the case of a sky position, these latter include at least the // projection type. In the case of a purely linear coordinate, the reference value // is merely added to the relative physical coordinate. The interface also // allows the axes to be assigned names (reasonable defaults will be selected), // and for physical units. // // Both absolute and relative coordinates are supported. The main // interface supports conversion between absolute pixel // and absolute world coordinate. There are then functions to // convert absolute coordinates to relative and vice versa. // A relative pixel coordinate is defined according to // // relative = absolute - reference // // A relative world coordinate is similar, although there may // be deviations from this formula (e.g. for DirectionCoordinate // a cos(latitude) term is incorporated and for StokesCoordinate // relative world coordinates are defined to be the same as // absolute world coordinates. // //
        // // // All absolute pixels coordinates are zero relative. // // // // This is a base class so there is no direct example, but // see the example in Coordinates.h // for use of the derived classes. // // // // Encapsulate the common interface to coordinate conversion so that it may // be used polymorphically. // // // //
      • AipsError //
      • AllocError // // // //
      • Perhaps common FITS related interfaces should go in this class. // // class Coordinate { public: // This enum lists the types of the derived classes. It is primarly used // in the CoordinateSystem class. enum Type { // Linear axes. LINEAR, // A direction. Usually RA/DEC. DIRECTION, // A spectral axis. SPECTRAL, // A Stokes axis. STOKES, // A one-dimensional Cooordinate system, usually created from a table // although it can also be purely linear. TABULAR, // to mark DATA and ERROR values QUALITY, // A CoordinateSystem (a collection of Coordinates). COORDSYS }; // This enum is used for formatting world values into Strings enum formatType { // Default; formatter decides DEFAULT, // Scientific format (e.g. -1.2397E+03) SCIENTIFIC, // Fixed floating format (e.g. 12.134) FIXED, // Either scientific or floating point, auto-selected by the C++ // STL formatting routines. May not be available for all Coordinate // types. MIXED, // HHH:MM:SS.SSS style formatting TIME }; // Destructor. Needs to be public so the user can delete Coordinate* objects virtual ~Coordinate(); // List the type of this Coordinate object. // virtual Type type() const = 0; virtual String showType() const = 0; static String typeToString (Coordinate::Type type); // // How many world/pixel axes are there in this Coordinate? While the number // of world and pixel axes will generally be the same, it is not a // requirement. For example, in CoordinateSystem you could remove a pixel // axis and leave the corresponding world axis. Also, if we ever implement // a "SlicedCoordinate" class then there would be more world than pixel // coordinates (the pixel coordinate would be a pixel number along the slice, // whereas the world axes would continue to be RA/DEC). // virtual uInt nPixelAxes() const = 0; virtual uInt nWorldAxes() const = 0; // // Convert an absolute pixel position to an absolute world position or vice // versa. Returns True // if the conversion succeeds, otherwise it returns False and method // errorMessage contains an error message. The input vector must be of length // nPixelAxes or nWorldAxes. The output vector // is resized appropriately. // if useConversionFrame, if the coordinate has a conversion layer frame // (such as can be present in spectral and direction coordinates), it // is used. Else, the native frame is used for the conversion. // virtual Bool toWorld(Vector &world, const Vector &pixel, Bool useConversionFrame=True) const = 0; virtual Bool toPixel(Vector &pixel, const Vector &world) const = 0; // // Mixed absolute pixel/world coordinate conversion. // worldIn and worldAxes are vectors of length nWorldAxes. // pixelIn and pixelAxes are of length nPixelAxes. // worldAxes(i) = True specifies you have given a world // value in worldIn(i) to convert to pixel. // pixelAxes(i)=True specifies you have given a pixel // value in pixelIn(i) to convert to world. // You cannot specify the same axis via worldAxes // and pixelAxes. // Values in pixelIn are converted to world and // put into worldOut in the appropriate world axis // location. Values in worldIn are copied to // worldOut. // Values in worldIn are converted to pixel and // put into pixelOut in the appropriate pixel axis // location. Values in pixelIn are copied to // pixelOut. // worldMin and worldMax specify the range of the world // coordinate (in the world axis units of that world axis // in the CoordinateSystem) being solved for in a mixed calculation // for each world axis. They are only actually needed for DirectionCoordinates // and for all other Coordinates the relevant elements // can be undefined. If you don't know, use -180 to 180 // degrees for longitude, and -90 to 90 for latitude. // Removed axes are handled (for example, a removed pixel // axis with remaining corresponding world axis will // correctly be converted to world using the replacement // value). // Returns True if the conversion succeeds, otherwise it returns False and // errorMessage() contains an error message. The output vectors // are resized. virtual Bool toMix(Vector& worldOut, Vector& pixelOut, const Vector& worldIn, const Vector& pixelIn, const Vector& worldAxes, const Vector& pixelAxes, const Vector& worldMin, const Vector& worldMax) const; // Set the world min and max ranges, for use in function toMix, for // a lattice of the given shape for this coordinate. The default implementation // here sets the range for pixels dangling 25% off the image. // Returns False if fails with a reason in errorMessage(). // setDefaultWorldMixRanges sets the range for each axis to +/-1e99 // The ranges remain zero length vectors until you explicitly // initialize them. // virtual Bool setWorldMixRanges (const IPosition& shape); virtual void setDefaultWorldMixRanges (); Vector worldMixMin () const {return worldMin_p;}; Vector worldMixMax () const {return worldMax_p;}; // // Batch up a lot of transformations. The first (most rapidly varying) axis // of the matrices contain the coordinates. Returns False if any conversion // failed and errorMessage() will hold a message. // The failures array (True for fail, False for success) // is the length of the number of conversions and // holds an error status for each conversion. The default // implementation is provided that works with the "single" version of // toWorld and toPixel, but for maximum efficiency these should be // overridden. // virtual Bool toWorldMany(Matrix& world, const Matrix& pixel, Vector& failures) const; virtual Bool toPixelMany(Matrix& pixel, const Matrix& world, Vector& failures) const; // // Make absolute coordinates relative and vice-versa (with // respect to the reference value). // Vectors must be length nPixelAxes() or // nWorldAxes() or memory access errors will occur // virtual void makePixelRelative (Vector& pixel) const; virtual void makePixelAbsolute (Vector& pixel) const; virtual void makeWorldRelative (Vector& world) const; virtual void makeWorldAbsolute (Vector& world) const; // // Make absolute coordinates relative and vice versa with respect // to the given reference value. Add the other functions in this grouping // as needed. Vectors must be length nPixelAxes() or // nWorldAxes() or memory access errors will occur // virtual void makeWorldAbsoluteRef (Vector& world, const Vector& refVal) const; // // Batch up a lot of absolute/relative transformations. // Parameters as above for // toWorldMany and toPixelMany // virtual void makePixelRelativeMany (Matrix& pixel) const; virtual void makePixelAbsoluteMany (Matrix& pixel) const; virtual void makeWorldRelativeMany (Matrix& world) const; virtual void makeWorldAbsoluteMany (Matrix& world) const; // // Return the requested attributed. // virtual Vector worldAxisNames() const = 0; virtual Vector referencePixel() const = 0; virtual Matrix linearTransform() const = 0; virtual Vector increment() const = 0; virtual Vector referenceValue() const = 0; virtual Vector worldAxisUnits() const = 0; // // Set the requested attribute. Note that these just // change the internal values, they do not cause any recomputation. // virtual Bool setWorldAxisNames(const Vector &names) = 0; virtual Bool setReferencePixel(const Vector &refPix) = 0; virtual Bool setLinearTransform(const Matrix &xform) = 0; virtual Bool setIncrement(const Vector &inc) = 0; virtual Bool setReferenceValue(const Vector &refval) = 0; // // Change the units. Adjust the increment and // reference value by the ratio of the old and new units. This implies that // the units must be known Unit strings, and that // they must be compatible, e.g. they can't change from time to length. // // A default implementation is available which does everything except set // the units vector, which must be done in the derived class. virtual Bool setWorldAxisUnits(const Vector &units) = 0; // Find the Coordinate for when we Fourier Transform ourselves. This pointer // must be deleted by the caller. Axes specifies which axes of the Coordinate // you wish to transform. Shape specifies the shape of the image // associated with all the axes of the Coordinate. Currently the // output reference pixel is always shape/2. virtual Coordinate* makeFourierCoordinate (const Vector& axes, const Vector& shape) const; // If the last conversion to world or pixel coordinates resulted in an // error, report that error. If the last conversion succeeded, it is // undefined what this will return (it might well contain the last error // message). const String& errorMessage() const; // Comparison to fractional tolerance (for floating point values). // Don't compare on specified axes in Coordinate. If the comparison // returns False, errorMessage() contains a message. // virtual Bool near(const Coordinate& other, Double tol=1.0e-6) const = 0; virtual Bool near(const Coordinate& other, const Vector& excludeAxes, Double tol=1.0e-6) const = 0; // // Provide a common interface to getting formatted representations of // coordinate values. Different derived Coordinate types are formatted // in different ways. For example, an RA/DEC DirectionCoordinate // uses an HMS.SS/DMS.SS representation. A Galactic Lat/Long DirectionCoordinate // uses floating format in degrees. Other derived Coordinates are formatted with // scientific format or floating format. The derived class format functions // provide this functionality. // // You may specify the format with the format argument and a value // from the enum Coordinate::formatType. If you give it the value // Coordinate::DEFAULT then a sensible default is used. // // A mechanism for specifying the precision number of significant digits after // decimal point is provided. You can specify the precision directly when // calling format if it is unambiguous how the derived Coordinate is // going to be formatted. For example, a LinearCoordinate is always formatted with // scientific format. However, if you are using these classes polymorphically, you // don't want to have to know this and some derived Coordinates may be formatted // in multiple ways (such as the DirectionCoordinate examples above). // Therefore, the function getPrecision enables // you to set default precisions for the different styles of formatting // used variously in the base and derived classes. This function chooses the // precision from these default values, according to the type of derived // Coordinate that your object is and what value for format that // you give (refer to the derived classes for details on this). // // Some derived classes will format differently depending upon whether // you want to format an absolute or offset world value input via // absolute (e.g. DirectionCoordinates). // // The provided worldValue must be in the native units // of the Coordinate. It may be an absolute (isAbsolute=True) // or relative (isAbsolute=False) value. You may choose to // format the world value as absolute (showAsAbsolute=True) or // relative (showAsAbsolute=False). axis // specifies which axis of the Coordinate this value belongs to. // // units specifies the units in which the input world value // will be formatted. // If units is empty, the native unit for the given axis // is used. // // Some derived classes will format in units different from the // native unit of the Coordinate. The units of // the formatted number are returned in units. // If the units string is provided, the unit must be // consistent with the native unit of the coordinate. The input // world value will be converted to this unit. // // You can also use the Quantum interface. The units of the Quantum // can then be anything consistent with the Coordinate. // // The default implementation here is to format only // with scientific or fixed formats. If precision is negative, a // the default precision is used. // // virtual void getPrecision(Int &precision, Coordinate::formatType& format, Bool showAsAbsolute, Int defPrecScientific, Int defPrecFixed, Int defPrecTime) const; virtual String format( String& units, Coordinate::formatType format, Double worldValue, uInt axis, Bool isAbsolute=True, Bool showAsAbsolute=True, Int precision=-1, Bool usePrecForMixed=False ) const; String formatQuantity(String& units, Coordinate::formatType format, const Quantum& worldValue, uInt axis, Bool isAbsolute=True, Bool showAsAbsolute=True, Int precision=-1); // // Used for persistence. Derived classes will have similar static // restore methods. It will typically only return False if fieldName // has already been defined. virtual Bool save(RecordInterface &container, const String &fieldName) const = 0; // Make a copy of ourself. This pointer has been allocated with // new and must be deleted by the caller. virtual Coordinate *clone() const = 0; // Comparison only made for specified axes in this and other Coordinate // The default implementation should be ok for all Coordinate types // except Stokes and Quality... virtual Bool doNearPixel (const Coordinate& other, const Vector& thisAxes, const Vector& otherAxes, Double tol=1.0e-6) const; // return the result of rotating the coordinate clockwise through the specified angle. // Rotation occurs about the reference pixel. // Coordinate must have exactly two pixel axes. The return type is the same // as the input type. It is the caller's responsibility to delete the returned pointer // when done with it to prevent a memory leak. // This method ultimately just changes the input coordinate's linear transform matrix. virtual Coordinate* rotate(const Quantum& angle) const; // Call wcsset on the wcs structure static void set_wcs (::wcsprm& wcs); // Call wcsini on the wcs structure static void init_wcs (::wcsprm& wcs, int naxis); // Call wcssub on the src/dst pair static void sub_wcs(const ::wcsprm &src, int &nsub, int axes[], ::wcsprm &dst); // Call wcssub on the src/dst pair with null nsub/axes static void copy_wcs(const ::wcsprm &src, ::wcsprm &dst); protected: // Default constructor. Make an empty coordinate. Used by derived classes. Coordinate(); // Copy constructor (copy semantics) Coordinate(const Coordinate& other); // Assignment (copy semantics) Coordinate& operator=(const Coordinate& other); // Set error message void set_error(const String &errorMsg) const; // Bool find_scale_factor(String &error, Vector &factor, const Vector &units, const Vector &oldUnits); // Tries to find a canonical unit for input unit (e.g. GHz -> Hz), and // tells you the output name and unit for the Fourier coordinate // pairing with the canonical unit void fourierUnits (String& nameOut, String& unitOut, String& unitInCanon, Coordinate::Type type, Int axis, const String& unitIn, const String& nameIn) const; // Functions to interconvert pixel<->world via wcs. These functions are called // explicitly by the to{world,Pixel} functions in the appropriate wcs-based derived // classes. // Bool toWorldWCS (Vector &world, const Vector &pixel, wcsprm& wcs) const; Bool toPixelWCS(Vector &pixel, const Vector &world, wcsprm& wcs) const; Bool toWorldManyWCS (Matrix& world, const Matrix& pixel, Vector& failures, wcsprm& wcs) const; Bool toPixelManyWCS (Matrix& pixel, const Matrix& world, Vector& failures, wcsprm& wcs) const; // Functions for handling conversion between the current units and // the wcs units. These are called explicitly by the appropriate // derived class. // convertFrom // void toCurrentMany (Matrix& world, const Vector& toCurrentFactors) const; void fromCurrentMany(Matrix& world, const Vector& toCurrentFactors) const; // // Functions for handling conversion between the current reference frame // and the native one. The default implementations do nothing. They // should be over-ridden in the derived classes. // virtual void convertTo (Vector&) const {} virtual void convertFrom (Vector&) const {} // // Functions for handling conversion between the current reference frame // and the native one for many conversions. These functions just // call the virtual functions for single conversions. // void convertToMany (Matrix& world) const; void convertFromMany (Matrix& world) const; // // Interconvert between wcs PC cards and Matrix xForm format void pcToXform (Matrix& xForm, const wcsprm& wcs) const; void xFormToPC (wcsprm& wcs, const Matrix& xForm) const; // // toMix ranges. Should be set by derived class. Vector worldMin_p, worldMax_p; private: mutable String error_p; // Check format type void checkFormat(Coordinate::formatType& format, const Bool absolute) const; void makeWorldAbsRelMany (Matrix& value, Bool toAbs) const; void makePixelAbsRelMany (Matrix& value, Bool toAbs) const; }; //###### Inlines inline const String& Coordinate::errorMessage() const { return error_p; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/coordinates/Coordinates/CoordinateSystem.cc000066400000000000000000004366301476623553700237270ustar00rootroot00000000000000//# CoordinateSystem.cc: hold a collection of coordinates //# Copyright (C) 1997,1998,1999,2000,2001,2002,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN const String CoordinateSystem::_class = "CoordinateSystem"; std::mutex CoordinateSystem::_mapInitMutex; map CoordinateSystem::_friendlyAxisMap = map(); CoordinateSystem::CoordinateSystem() : coordinates_p(0), world_maps_p(0), world_tmps_p(0), world_replacement_values_p(0), pixel_maps_p(0), pixel_tmps_p(0), pixel_replacement_values_p(0), worldAxes_tmps_p(0), pixelAxes_tmps_p(0), worldOut_tmps_p(0), pixelOut_tmps_p(0), worldMin_tmps_p(0), worldMax_tmps_p(0) { setDefaultWorldMixRanges(); } void CoordinateSystem::copy(const CoordinateSystem &other) { if (this == &other) { return; } // clear(); // obsinfo_p = other.obsinfo_p; coordinates_p = other.coordinates_p; const uInt n = coordinates_p.nelements(); // uInt i; for (i=0; i < n; i++) { coordinates_p[i] = coordinates_p[i]->clone(); AlwaysAssert(coordinates_p[i], AipsError); } // world_maps_p.resize(n); world_tmps_p.resize(n); world_replacement_values_p.resize(n); pixel_maps_p.resize(n); pixel_tmps_p.resize(n); pixel_replacement_values_p.resize(n); // worldAxes_tmps_p.resize(n); pixelAxes_tmps_p.resize(n); worldOut_tmps_p.resize(n); pixelOut_tmps_p.resize(n); worldMin_tmps_p.resize(n); worldMax_tmps_p.resize(n); // for (i=0; i(*(other.world_maps_p[i])); world_tmps_p[i] = new Vector(other.world_tmps_p[i]->copy()); world_replacement_values_p[i] = new Vector(other.world_replacement_values_p[i]->copy()); AlwaysAssert(world_maps_p[i] != 0 && world_tmps_p[i] != 0 && world_replacement_values_p[i] != 0, AipsError); pixel_maps_p[i] = new Block(*(other.pixel_maps_p[i])); pixel_tmps_p[i] = new Vector(other.pixel_tmps_p[i]->copy()); pixel_replacement_values_p[i] = new Vector(other.pixel_replacement_values_p[i]->copy()); AlwaysAssert(pixel_maps_p[i] != 0 && pixel_tmps_p[i] != 0 && pixel_replacement_values_p[i] != 0, AipsError); // worldAxes_tmps_p[i] = new Vector(other.worldAxes_tmps_p[i]->copy()); pixelAxes_tmps_p[i] = new Vector(other.pixelAxes_tmps_p[i]->copy()); AlwaysAssert(worldAxes_tmps_p[i] != 0 && pixelAxes_tmps_p[i] != 0, AipsError); // worldOut_tmps_p[i] = new Vector(other.worldOut_tmps_p[i]->copy()); pixelOut_tmps_p[i] = new Vector(other.pixelOut_tmps_p[i]->copy()); AlwaysAssert(worldOut_tmps_p[i] != 0 && pixelOut_tmps_p[i] != 0, AipsError); // worldMin_tmps_p[i] = new Vector(other.worldMin_tmps_p[i]->copy()); worldMax_tmps_p[i] = new Vector(other.worldMax_tmps_p[i]->copy()); AlwaysAssert(worldMin_tmps_p[i] != 0 && worldMax_tmps_p[i] != 0, AipsError); } } void CoordinateSystem::clear() { const uInt n = coordinates_p.nelements(); // for (uInt i=0; i(coordinates_p[n]->nWorldAxes()); AlwaysAssert(world_maps_p[n], AipsError); uInt i; for (i=0; i < world_maps_p[n]->nelements(); i++) { world_maps_p[n]->operator[](i) = oldWorldAxes + i; } // // world_tmps_p // world_tmps_p.resize(n+1); world_tmps_p[n] = new Vector(coordinates_p[n]->nWorldAxes()); AlwaysAssert(world_tmps_p[n], AipsError); // // pixel_maps_p // pixel_maps_p.resize(n+1); pixel_maps_p[n] = new Block(coordinates_p[n]->nPixelAxes()); AlwaysAssert(pixel_maps_p[n], AipsError); for (i=0; i < pixel_maps_p[n]->nelements(); i++) { pixel_maps_p[n]->operator[](i) = oldPixelAxes + i; } // // pixel_tmps_p // pixel_tmps_p.resize(n+1); pixel_tmps_p[n] = new Vector(coordinates_p[n]->nPixelAxes()); AlwaysAssert(pixel_tmps_p[n], AipsError); // // pixel_replacement_values_p // pixel_replacement_values_p.resize(n+1); pixel_replacement_values_p[n] = new Vector(coordinates_p[n]->nPixelAxes()); AlwaysAssert(pixel_replacement_values_p[n], AipsError); *(pixel_replacement_values_p[n]) = 0.0; // // world_replacement_values_p // world_replacement_values_p.resize(n+1); world_replacement_values_p[n] = new Vector(coordinates_p[n]->nWorldAxes()); AlwaysAssert(world_replacement_values_p[n], AipsError); coordinates_p[n] -> toWorld(*(world_replacement_values_p[n]), *(pixel_replacement_values_p[n])); // // worldAxes_tmps_p // worldAxes_tmps_p.resize(n+1); worldAxes_tmps_p[n] = new Vector(coordinates_p[n]->nWorldAxes()); AlwaysAssert(worldAxes_tmps_p[n], AipsError); // // pixelAxes_tmps_p // pixelAxes_tmps_p.resize(n+1); pixelAxes_tmps_p[n] = new Vector(coordinates_p[n]->nPixelAxes()); AlwaysAssert(pixelAxes_tmps_p[n], AipsError); // // worldOut_tmps_p // worldOut_tmps_p.resize(n+1); worldOut_tmps_p[n] = new Vector(coordinates_p[n]->nWorldAxes()); AlwaysAssert(worldOut_tmps_p[n], AipsError); // // pixelOut_tmps_p // pixelOut_tmps_p.resize(n+1); pixelOut_tmps_p[n] = new Vector(coordinates_p[n]->nPixelAxes()); AlwaysAssert(pixelOut_tmps_p[n], AipsError); // // worldMin_tmps_p // worldMin_tmps_p.resize(n+1); worldMin_tmps_p[n] = new Vector(coordinates_p[n]->nWorldAxes()); AlwaysAssert(worldMin_tmps_p[n], AipsError); // // worldMax_tmps_p // worldMax_tmps_p.resize(n+1); worldMax_tmps_p[n] = new Vector(coordinates_p[n]->nWorldAxes()); AlwaysAssert(worldMax_tmps_p[n], AipsError); } void CoordinateSystem::transpose(const Vector &newWorldOrder, const Vector &newPixelOrder) { AlwaysAssert(newWorldOrder.nelements() == nWorldAxes(), AipsError); AlwaysAssert(newPixelOrder.nelements() == nPixelAxes(), AipsError); const uInt nc = nCoordinates(); const uInt nw = newWorldOrder.nelements(); const uInt np = newPixelOrder.nelements(); // Verify that all axes are in new*Order once (only) Vector found(nw); found = False; uInt i; for (i=0; i=0 && uInt(which)=0 && uInt(which) < np && !found(which), AipsError); found(which) = True; } // PtrBlock *> newWorldMaps(nc); PtrBlock *> newPixelMaps(nc); newWorldMaps.set(static_cast *>(0)); newPixelMaps.set(static_cast *>(0)); // copy the maps (because the deleted axes will be staying put) for (i=0; i(*world_maps_p[i]); newPixelMaps[i] = new Block(*pixel_maps_p[i]); AlwaysAssert((newWorldMaps[i] && newPixelMaps[i]), AipsError); } // Move the world axes to their new home for (i=0; ioperator[](axis) = i; } // Move the pixel axes to their new home for (i=0; ioperator[](axis) = i; } // OK, now overwrite the new locations for (i=0; i& pixelAxisMap, Vector& pixelAxisTranspose, const CoordinateSystem& other) const // // . pixelAxisMap(i) is the location of pixel axis // i (from other) in *this. A value of -1 indicates that // a pixel axis could not be matched. // . pixelAxisTranspose(i) is the location of pixel axis i (from *this) // in other. It tells you how to transpose // "other" to be in the order of "*this". A value of -1 indicates // that a world axis could not be matched. // { if (other.nPixelAxes() ==0) { set_error(String("The supplied CoordinateSystem has no valid pixel axes")); return False; } if (nPixelAxes() ==0) { set_error(String("The current CoordinateSystem has no valid pixel axes")); return False; } // pixelAxisMap.resize(other.nPixelAxes()); pixelAxisMap = -1; pixelAxisTranspose.resize(nPixelAxes()); pixelAxisTranspose = -1; // Vector worldAxisMap, worldAxisTranpose; Vector refChange; Bool ok = worldMap(worldAxisMap, worldAxisTranpose, refChange, other); if (!ok) return False; // Int w, tw; for (uInt i=0; i= 0) pixelAxisMap(i) = worldAxisToPixelAxis(tw); } // for (uInt i=0; i= 0) pixelAxisTranspose(i) = other.worldAxisToPixelAxis(tw); } // return True; } Bool CoordinateSystem::worldMap(Vector& worldAxisMap, Vector& worldAxisTranspose, Vector& refChange, const CoordinateSystem& other) const // // Make a map from "*this" to "other" // // . Returns False if either "*this" or "other" have no valid // world axes. Otherwise true. // . The coordinate systems can have arbitrary numbers of coordinates // in any relative order. // . Removed world and pixel axes are handled. // . worldAxisMap(i) is the location of world axis // i (from other) in *this. A value of -1 indicates that // a world axis could not be matched. // . worldAxisTranspose(i) is the location of world axis i (from *this) // in other. It tells you how to transpose // "other" to be in the order of "*this". A value of -1 indicates // that a world axis could not be matched. // . If refChange(i) is True, it means that if the coordinate matched, // there is a difference in reference type (E.g J2000->B1950) // for worldAxis i in "other" { // Resize the maps and initialize worldAxisMap.resize(other.nWorldAxes()); worldAxisMap = -1; worldAxisTranspose.resize(nWorldAxes()); worldAxisTranspose = -1; refChange.resize(nWorldAxes()); refChange = False; // if (other.nWorldAxes() ==0) { set_error(String("The supplied CoordinateSystem has no valid world axes")); return False; } if (nWorldAxes() ==0) { set_error(String("The current CoordinateSystem has no valid world axes")); return False; } // Loop over "other" coordinates const uInt nCoord = nCoordinates(); const uInt nCoord2 = other.nCoordinates(); Vector usedCoords(nCoord,False); for (uInt coord2=0; coord2= nWorldAxes()) { ostringstream oss; oss << "Illegal removal world axis number (" << axis << "), max is (" << nWorldAxes() << ")" << endl; set_error (String(oss)); return False; } // Remove the corresponding pixel axis (if there).. Int pixAxis = worldAxisToPixelAxis (axis); if (pixAxis >= 0) { // Find pixel coordinate corresponding to world replacement value Vector world(referenceValue()); world(axis) = replacement; Vector pixel(nPixelAxes()); if (!toPixel(pixel, world)) return False; // removePixelAxis (pixAxis, pixel(pixAxis)); } const uInt nc = nCoordinates(); Int coord, caxis; findWorldAxis(coord, caxis, axis); world_replacement_values_p[coord]->operator()(caxis) = replacement; Int oldValue = world_maps_p[coord]->operator[](caxis); world_maps_p[coord]->operator[](caxis) = -1 * (oldValue+1); // Makes the actual axis recoverable for (uInt i=0; inelements(); j++) { if (world_maps_p[i]->operator[](j) > Int(axis)) { world_maps_p[i]->operator[](j)--; } } } return True; } Bool CoordinateSystem::removePixelAxis(uInt axis, Double replacement) { if (axis >= nPixelAxes()) { ostringstream oss; oss << "Illegal removal pixel axis number (" << axis << "), max is (" << nPixelAxes() << ")" << endl; set_error (String(oss)); return False; } // const uInt nc = nCoordinates(); Int coord, caxis; findPixelAxis(coord, caxis, axis); pixel_replacement_values_p[coord]->operator()(caxis) = replacement; Int oldValue = pixel_maps_p[coord]->operator[](caxis); pixel_maps_p[coord]->operator[](caxis) = -1 * (oldValue+1); // Makes the actual axis recoverable // for (uInt i=0; inelements(); j++) { if (pixel_maps_p[i]->operator[](j) > Int(axis)) { pixel_maps_p[i]->operator[](j)--; } } } return True; } /* //This implementation is flawed. It only works if you have removed //one axis. I think you need to specify coordinate and axis in coordinate Bool CoordinateSystem::worldReplacementValue (Double& replacement, uInt axis) const { Int coordinate = -1; Int axisInCoordinate = -1; if (checkWorldReplacementAxis(coordinate, axisInCoordinate, axis)) { replacement = world_replacement_values_p[coordinate]->operator()(axisInCoordinate); return True; } // return False; } Bool CoordinateSystem::setWorldReplacementValue (uInt axis, Double replacement) { Int coordinate = -1; Int axisInCoordinate = -1; if (checkWorldReplacementAxis(coordinate, axisInCoordinate, axis)) { world_replacement_values_p[coordinate]->operator()(axisInCoordinate) = replacement; return True; } // return False; } Bool CoordinateSystem::pixelReplacementValue (Double& replacement, uInt axis) const { Int coordinate = -1; Int axisInCoordinate = -1; if (checkPixelReplacementAxis(coordinate, axisInCoordinate, axis)) { replacement = pixel_replacement_values_p[coordinate]->operator()(axisInCoordinate); return True; } // return False; } Bool CoordinateSystem::setPixelReplacementValue (uInt axis, Double replacement) { Int coordinate = -1; Int axisInCoordinate = -1; if (checkPixelReplacementAxis(coordinate, axisInCoordinate, axis)) { pixel_replacement_values_p[coordinate]->operator()(axisInCoordinate) = replacement; return True; } // return False; } */ CoordinateSystem CoordinateSystem::subImage(const Vector &originShift, const Vector &pixincFac, const Vector& newShape) const { CoordinateSystem coords = *this; coords.subImageInSitu(originShift, pixincFac, newShape); return coords; } void CoordinateSystem::subImageInSitu(const Vector &originShift, const Vector &pixincFac, const Vector& newShape) { AlwaysAssert(originShift.nelements() == nPixelAxes() && pixincFac.nelements() == nPixelAxes(), AipsError); const uInt nShape = newShape.nelements(); AlwaysAssert(nShape==0 || nShape==nPixelAxes(), AipsError); // We could get rid of this assumption by multiplying by accounting for the PC // matrix as well as cdelt, or going through group-by-group, but it doesn't // seem necessary now, or maybe ever. AlwaysAssert(originShift.nelements() == pixincFac.nelements(), AipsError); uInt n = nPixelAxes(); Vector crpix = referencePixel().copy(); Vector cdelt = increment().copy(); // Not efficient, but easy and this code shouldn't be called often Int coordinate, axisInCoordinate; for (uInt i=0; i 0 ) { // tabular spectral coordinate SpectralCoordinate spCoord = spectralCoordinate(coordinate); SpectralCoordinate::SpecType nativeType = spCoord.nativeType(); Vector newWorldValues(newShape.nelements() > 0 ? newShape[i] : spCoord.worldValues().size()); // switch off reference conversion if necessary MFrequency::Types baseType = spCoord.frequencySystem(False); MFrequency::Types convType = spCoord.frequencySystem(True); MEpoch convEpoch; MPosition convPos; MDirection convDir; if(baseType!=convType){ spCoord.getReferenceConversion(convType, convEpoch, convPos, convDir); spCoord.setReferenceConversion(baseType, convEpoch, convPos, convDir); } for (uInt j=0; j< newWorldValues.size(); j++) { Double pix = originShift[i] + j*pixincFac[i]; if (! spCoord.toWorld(newWorldValues[j], pix)) { throw AipsError("Unable to convert tabular spectral coordinate"); } } // restore reference conversion if(baseType!=convType){ spCoord.setReferenceConversion(convType, convEpoch, convPos, convDir); } SpectralCoordinate newCoord( baseType, newWorldValues, spCoord.restFrequency() ); if (! newCoord.setNativeType(nativeType)) { throw AipsError("Unable to set native type of tabular spectral coordinate."); } // adding the conversion layer if one exists if(!newCoord.setReferenceConversion(convType, convEpoch, convPos, convDir)){ throw AipsError("Unable to set reference conversion layer of tabular spectral coordinate."); } crpix(i) = 0; cdelt[i] = newCoord.increment()[0]; replaceCoordinate(newCoord, coordinate); } else { AlwaysAssert(pixincFac(i) > 0, AipsError); crpix(i) -= originShift(i); crpix(i) /= pixincFac(i); cdelt(i) *= pixincFac(i); } } setReferencePixel(crpix); setIncrement(cdelt); } void CoordinateSystem::restoreOriginal() { CoordinateSystem coord; // Make a copy and then assign it back uInt n = coordinates_p.nelements(); for (uInt i=0; itype(); } String CoordinateSystem::showType(uInt whichCoordinate) const { AlwaysAssert(whichCoordinateshowType(); } const Coordinate& CoordinateSystem::coordinate(uInt which) const { AlwaysAssert(which < nCoordinates(), AipsError); return *(coordinates_p[which]); } const LinearCoordinate& CoordinateSystem::linearCoordinate(uInt which) const { AlwaysAssert(which < nCoordinates() && coordinates_p[which]->type() == Coordinate::LINEAR, AipsError); return dynamic_cast(*(coordinates_p[which])); } const DirectionCoordinate& CoordinateSystem::directionCoordinate(uInt which) const { AlwaysAssert(which < nCoordinates() && coordinates_p[which]->type() == Coordinate::DIRECTION, AipsError); return dynamic_cast(*(coordinates_p[which])); } const DirectionCoordinate& CoordinateSystem::directionCoordinate() const { if (! hasDirectionCoordinate()) { throw AipsError( String(__FUNCTION__) + ": Coordinate system has no direction coordinate" ); } return directionCoordinate(directionCoordinateNumber()); } const SpectralCoordinate& CoordinateSystem::spectralCoordinate(uInt which) const { AlwaysAssert(which < nCoordinates() && coordinates_p[which]->type() == Coordinate::SPECTRAL, AipsError); return dynamic_cast(*(coordinates_p[which])); } const SpectralCoordinate& CoordinateSystem::spectralCoordinate() const { if (! this->hasSpectralAxis()) { throw AipsError( String(__FUNCTION__) + ": Coordinate system has no spectral coordinate" ); } return spectralCoordinate(spectralCoordinateNumber()); } const StokesCoordinate& CoordinateSystem::stokesCoordinate(uInt which) const { AlwaysAssert(which < nCoordinates() && coordinates_p[which]->type() == Coordinate::STOKES, AipsError); return dynamic_cast(*(coordinates_p[which])); } const StokesCoordinate& CoordinateSystem::stokesCoordinate() const { if (! this->hasPolarizationCoordinate()) { throw AipsError( String(__FUNCTION__) + ": Coordinate system has no polarization coordinate" ); } return stokesCoordinate(polarizationCoordinateNumber()); } const QualityCoordinate& CoordinateSystem::qualityCoordinate(uInt which) const { AlwaysAssert(which < nCoordinates() && coordinates_p[which]->type() == Coordinate::QUALITY, AipsError); return dynamic_cast(*(coordinates_p[which])); } const TabularCoordinate& CoordinateSystem::tabularCoordinate(uInt which) const { AlwaysAssert(which < nCoordinates() && coordinates_p[which]->type() == Coordinate::TABULAR, AipsError); return dynamic_cast(*(coordinates_p[which])); } Bool CoordinateSystem::replaceCoordinate(const Coordinate &newCoordinate, uInt which) { // Basic checks. The number of axes must be the same as this function does not // change any of the axis removal or mappings etc. AlwaysAssert(which < nCoordinates() && newCoordinate.nPixelAxes() == coordinates_p[which]->nPixelAxes() && newCoordinate.nWorldAxes() == coordinates_p[which]->nWorldAxes(), AipsError); Bool typesEqual = newCoordinate.type()==coordinates_p[which]->type(); const Vector& oldUnits(coordinates_p[which]->worldAxisUnits()); const Vector& newUnits(newCoordinate.worldAxisUnits()); // Replace coordinate delete coordinates_p[which]; coordinates_p[which] = newCoordinate.clone(); AlwaysAssert(coordinates_p[which], AipsError); // Now, the world replacement values are a bother. They may well have the wrong // units now. So try to find scale factors if the Coordinates were of // the same type. Bool ok = False; Int where = 0; if (typesEqual) { String errMsg; Vector factor; ok = Coordinate::find_scale_factor(errMsg, factor, newUnits, oldUnits); // if (ok) { // Apply scale factor for (uInt i=0; ioperator()(i) *= factor[i]; } } } if (ok) return True; // If different types, or the scale factors could not be found (non-conformant units) the // best we can do is set the reference values for the replacement values if (!ok || !typesEqual) { const Vector& refVal(newCoordinate.referenceValue()); for (uInt i=0; ioperator[](i); if (where < 0) { world_replacement_values_p[which]->operator()(i) = refVal[i]; } else { world_replacement_values_p[which]->operator()(i) = 0.0; } } } return False; } Int CoordinateSystem::findCoordinate(Coordinate::Type type, Int afterCoord) const { if (afterCoord < -1) { afterCoord = -1; } Int n = nCoordinates(); Bool found = False; while (++afterCoord < n) { if (coordinates_p[afterCoord]->type() == type) { found = True; break; } } if (found) { return afterCoord; } else { return -1; } } void CoordinateSystem::findWorldAxis(Int &coordinate, Int &axisInCoordinate, uInt axisInCoordinateSystem) const { coordinate = axisInCoordinate = -1; AlwaysAssert(axisInCoordinateSystem < nWorldAxes(), AipsError); // const uInt orig = axisInCoordinateSystem; // alias for less typing const uInt nc = nCoordinates(); // for (uInt i=0; inelements(); for (uInt j=0; joperator[](j) == Int(orig)) { coordinate = i; axisInCoordinate = j; return; } } } } void CoordinateSystem::findPixelAxis(Int &coordinate, Int &axisInCoordinate, uInt axisInCoordinateSystem) const { coordinate = axisInCoordinate = -1; AlwaysAssert(axisInCoordinateSystem < nPixelAxes(), AipsError); const uInt orig = axisInCoordinateSystem; // alias for less typing const uInt nc = nCoordinates(); for (uInt i=0; inelements(); for (uInt j=0; joperator[](j) == Int(orig)) { coordinate = i; axisInCoordinate = j; return; } } } } Int CoordinateSystem::pixelAxisToWorldAxis(uInt pixelAxis) const { Int coordinate, axisInCoordinate; findPixelAxis(coordinate, axisInCoordinate, pixelAxis); if (axisInCoordinate>=0 && coordinate>=0) { return worldAxes(coordinate)(axisInCoordinate); } return -1; } Int CoordinateSystem::worldAxisToPixelAxis(uInt worldAxis) const { Int coordinate, axisInCoordinate; findWorldAxis(coordinate, axisInCoordinate, worldAxis); if (axisInCoordinate>=0 && coordinate>=0) { return pixelAxes(coordinate)(axisInCoordinate); } return -1; } Vector CoordinateSystem::worldAxes(uInt whichCoord) const { // Implemented in terms of the public member functions. It would be more // efficient to use the private data, but would be harder to maintain. // This isn't apt to be called often, so choose the easier course. AlwaysAssert(whichCoord < nCoordinates(), AipsError); Vector retval(coordinate(whichCoord).nWorldAxes()); retval = -1; // Axes which aren't found must be removed! const uInt naxes = nWorldAxes(); for (uInt i=0; i CoordinateSystem::pixelAxes(uInt whichCoord) const { AlwaysAssert(whichCoord < nCoordinates(), AipsError); Vector retval(coordinate(whichCoord).nPixelAxes()); // retval = -1; // Axes which aren't found must be removed! const uInt naxes = nPixelAxes(); for (uInt i=0; inelements(); for (uInt j=0; joperator[](j) >= 0) { count++; } } } return count; } uInt CoordinateSystem::nPixelAxes() const { uInt count = 0; const uInt nc = nCoordinates(); for (uInt i=0; inelements(); for (uInt j=0; joperator[](j) >= 0) { count++; } } } return count; } Vector CoordinateSystem::toWorld(const Vector &pixel) const { Vector world; if (! toWorld(world, pixel)) { throw AipsError("Cannot convert pixel to world coordinates"); } return world; } Vector CoordinateSystem::toWorld(const IPosition& pixel) const { Vector world; if (! toWorld(world, pixel)) { throw AipsError("Cannot convert pixel to world coordinates"); } return world; } Bool CoordinateSystem::toWorld(Vector &world, const Vector &pixel, Bool useConversionFrame) const { if(pixel.nelements() != nPixelAxes()){ ostringstream oss; oss << "pixel.nelements() != nPixelAxes(): " << pixel.nelements() << ", " << nPixelAxes(); throw (AipsError(String(oss))); } if (world.nelements()!=nWorldAxes()) world.resize(nWorldAxes()); const uInt nc = coordinates_p.nelements(); Bool ok = True; for (uInt i=0; inelements(); uInt j; for (j=0; joperator[](j); if (where >= 0) { //cerr << "i j where " << i << " " << j << " " << where <operator()(j) = pixel(where); } else { pixel_tmps_p[i]->operator()(j) = pixel_replacement_values_p[i]->operator()(j); } } Bool oldok = ok; ok = coordinates_p[i]->toWorld( *(world_tmps_p[i]), *(pixel_tmps_p[i]), useConversionFrame); if (!ok) { // Transfer the error message. Note that if there is more than // one error message this transfers the last one. I suppose this // is as good as any. set_error(coordinates_p[i]->errorMessage()); } ok = (ok && oldok); const uInt nwa = world_maps_p[i]->nelements(); for (j=0; joperator[](j); if (where >= 0) { world(where) = world_tmps_p[i]->operator()(j); } } } return ok; } Vector CoordinateSystem::toPixel(const Vector &world) const { Vector pixel; if (! toPixel(pixel, world)) { throw AipsError("Cannot convert world to pixel coordinates"); } return pixel; } Bool CoordinateSystem::toWorld(Vector &world, const IPosition &pixel) const { static Vector pixel_tmp; if (pixel_tmp.nelements()!=pixel.nelements()) pixel_tmp.resize(pixel.nelements()); // const uInt& n = pixel.nelements(); for (uInt i=0; i &pixel, const Vector &world) const { AlwaysAssert(world.nelements() == nWorldAxes(), AipsError); if (pixel.nelements()!=nPixelAxes()) pixel.resize(nPixelAxes()); const uInt nc = coordinates_p.nelements(); Bool ok = True; Int where; for (uInt i=0; inelements(); uInt j; for (j=0; joperator[](j); if (where >= 0) { world_tmps_p[i]->operator()(j) = world(where); } else { world_tmps_p[i]->operator()(j) = world_replacement_values_p[i]->operator()(j); } } Bool oldok = ok; ok = coordinates_p[i]->toPixel( *(pixel_tmps_p[i]), *(world_tmps_p[i])); if (!ok) { // Transfer the error message. Note that if there is more than // one error message this transfers the last one. I suppose this // is as good as any. set_error(coordinates_p[i]->errorMessage()); } ok = (ok && oldok); const uInt npxa = pixel_maps_p[i]->nelements(); for (j=0; joperator[](j); if (where >= 0) { pixel(where) = pixel_tmps_p[i]->operator()(j); } } } return ok; } Quantity CoordinateSystem::toWorldLength( const Double nPixels, const uInt pixelAxis ) const { if (pixelAxis >= nPixelAxes()) { throw AipsError( String(__FUNCTION__) + "pixelAxis greater or equal to nPixelAxes" ); } Int coord, coordAxis; findWorldAxis(coord, coordAxis, pixelAxis); return Quantity( fabs(nPixels*coordinates_p[coord]->increment()[coordAxis]), worldAxisUnits()[pixelAxisToWorldAxis(pixelAxis)] ); } Bool CoordinateSystem::toWorldMany(Matrix& world, const Matrix& pixel, Vector& failures) const { AlwaysAssert(nPixelAxes()==pixel.nrow(), AipsError); const uInt nTransforms = pixel.ncolumn(); world.resize(nWorldAxes(), nTransforms); // // Matrix indexing:: // matrix(nCoords, nTransforms) == matrix(nrows, ncolumns) // matrix(i,j); j=0->nTransforms, i=0->nCoordinates; // matrix.column(j) = vector of coordinates for one transformation // matrix.row(i) = vector of transforms for one coordinate // // Loop over coordinates uInt i, k; Int where; Bool ok = True; // const uInt nCoords = coordinates_p.nelements(); for (k=0; knelements(); Matrix pixTmp(nPixelAxes,nTransforms); // for (i=0; ioperator[](i); if (where >= 0) { pixTmp.row(i) = pixel.row(where); } else { pixTmp.row(i) = pixel_replacement_values_p[k]->operator()(i); } } // Do conversion using Coordinate specific implementation const uInt nWorldAxes = world_maps_p[k]->nelements(); Matrix worldTmp(nWorldAxes,nTransforms); Vector failuresTmp; ok = coordinates_p[k]->toWorldMany(worldTmp, pixTmp, failuresTmp); // We get the last error message from whatever coordinate it is if (!ok) { set_error(coordinates_p[k]->errorMessage()); } // Now copy result from temporary into output world matrix for (i=0; ioperator[](i); if (where >= 0) { world.row(where) = worldTmp.row(i); } } } // Code should be written to merge the failures vectors // Code should be written to merge the OK statuses failures.resize(nCoords); failures = False; // return ok; } Bool CoordinateSystem::toPixelMany(Matrix& pixel, const Matrix& world, Vector& failures) const { AlwaysAssert(nWorldAxes()==world.nrow(), AipsError); const uInt nTransforms = world.ncolumn(); pixel.resize(nPixelAxes(), nTransforms); // // Matrix indexing:: // matrix(nCoords, nTransforms) == matrix(nrows, ncolumns) // matrix(i,j); j=0->nTransforms, i=0->nCoordinates; // matrix.column(j) = vector of coordinates for one transformation // matrix.row(i) = vector of transforms for one coordinate // // Loop over coordinates uInt i, k; Int where; Bool ok = True; // const uInt nCoords = coordinates_p.nelements(); for (k=0; knelements(); Matrix worldTmp(nWorldAxes,nTransforms); // for (i=0; ioperator[](i); if (where >= 0) { worldTmp.row(i) = world.row(where); } else { worldTmp.row(i) = world_replacement_values_p[k]->operator()(i); } } // Do conversion using Coordinate specific implementation const uInt nPixelAxes = pixel_maps_p[k]->nelements(); Matrix pixTmp(nPixelAxes,nTransforms); Vector failuresTmp; ok = coordinates_p[k]->toPixelMany(pixTmp, worldTmp, failuresTmp); // We get the last error message from whatever coordinate it is if (!ok) { set_error(coordinates_p[k]->errorMessage()); } // Now copy result from temporary into output pixel matrix for (i=0; ioperator[](i); if (where >= 0) { pixel.row(where) = pixTmp.row(i); } } } // Code should be written to merge the failures vectors failures.resize(nCoords); failures = False; // return ok; } Bool CoordinateSystem::toMix(Vector& worldOut, Vector& pixelOut, const Vector& worldIn, const Vector& pixelIn, const Vector& worldAxes, const Vector& pixelAxes, const Vector& minWorld, const Vector& maxWorld) const { const uInt nWorld = worldAxes.nelements(); const uInt nPixel = pixelAxes.nelements(); AlwaysAssert(nWorld == nWorldAxes(), AipsError); AlwaysAssert(worldIn.nelements()==nWorld, AipsError); AlwaysAssert(nPixel == nPixelAxes(), AipsError); AlwaysAssert(pixelIn.nelements()==nPixel, AipsError); AlwaysAssert(minWorld.nelements()==nWorld, AipsError); AlwaysAssert(maxWorld.nelements()==nWorld, AipsError); // const uInt nCoord = coordinates_p.nelements(); if (worldOut.nelements()!=nWorldAxes()) worldOut.resize(nWorldAxes()); if (pixelOut.nelements()!=nPixelAxes()) pixelOut.resize(nPixelAxes()); for (uInt i=0; inelements(); const uInt nPixelAxes = pixel_maps_p[i]->nelements(); AlwaysAssert(nAxes==nPixelAxes, AipsError); // for (uInt j=0; joperator[](j); if (where >= 0) { world_tmps_p[i]->operator()(j) = worldIn(where); worldAxes_tmps_p[i]->operator()(j) = worldAxes(where); worldMin_tmps_p[i]->operator()(j) = minWorld(where); worldMax_tmps_p[i]->operator()(j) = maxWorld(where); } else { // // World axis removed. Use replacement value. If the world axis // is removed, so will the pixel axis be. // world_tmps_p[i]->operator()(j) = world_replacement_values_p[i]->operator()(j); // // We have to decide what conversion to do (pixel<->world) for the removed axis. // For coupled axes like DirectionCoordinate, I do for the removed axis whatever I did for // the unremoved axis, if there is one... If both world axes are removed, ultimately // it doesn't really matter what I do since the pixel axes will be gone as well, and there // is nowhere to put the output ! For uncoupled axes it doesn't matter. // if (type(i)==Coordinate::DIRECTION) { Vector units(coordinate(i).worldAxisUnits()); // Int where2; if (j==0) { // 0 or 1 where2 = world_maps_p[i]->operator[](1); worldMin_tmps_p[i]->operator()(0) = coordinates_p[i]->worldMixMin()(0); worldMax_tmps_p[i]->operator()(0) = coordinates_p[i]->worldMixMax()(0); } else { where2 = world_maps_p[i]->operator[](0); worldMin_tmps_p[i]->operator()(1) = coordinates_p[i]->worldMixMin()(1); worldMax_tmps_p[i]->operator()(1) = coordinates_p[i]->worldMixMax()(1); } if (where2 >= 0) { worldAxes_tmps_p[i]->operator()(j) = worldAxes(where2); } else { worldAxes_tmps_p[i]->operator()(j) = False; } } else { worldAxes_tmps_p[i]->operator()(j) = True; // // worldMin/Max irrelevant except for DirectionCoordinate // } } } // for (uInt j=0; joperator[](j); if (where >= 0) { pixel_tmps_p[i]->operator()(j) = pixelIn(where); pixelAxes_tmps_p[i]->operator()(j) = pixelAxes(where); } else { // Pixel axis removed. It is possible to remove the pixel axis but not // the world axis. pixel_tmps_p[i]->operator()(j) = pixel_replacement_values_p[i]->operator()(j); // // Here I assume nPixelAxes=nWorldAxes for the specific coordinate // and the order is the same. This is the truth as far as I know it. // // We set pixelAxes to the opposite of worldAxes. Thus if its // given as world, use it. If its not given as world, use the // replacement pixel value pixelAxes_tmps_p[i]->operator()(j) = !worldAxes_tmps_p[i]->operator()(j); } } // if (!coordinates_p[i]->toMix(*(worldOut_tmps_p[i]), *(pixelOut_tmps_p[i]), *(world_tmps_p[i]), *(pixel_tmps_p[i]), *(worldAxes_tmps_p[i]), *(pixelAxes_tmps_p[i]), *(worldMin_tmps_p[i]), *(worldMax_tmps_p[i]))) { set_error(coordinates_p[i]->errorMessage()); return False; } // for (uInt j=0; joperator[](j); if (where>=0) worldOut(where) = worldOut_tmps_p[i]->operator()(j); where = pixel_maps_p[i]->operator[](j); if (where>=0) pixelOut(where) = pixelOut_tmps_p[i]->operator()(j); } } return True; } void CoordinateSystem::makeWorldRelative (Vector& world) const { AlwaysAssert(world.nelements() == nWorldAxes(), AipsError); // const uInt nc = coordinates_p.nelements(); Int where; for (uInt i=0; inelements(); // Copy elements for this coordinate and replace removed axis values uInt j; for (j=0; joperator[](j); if (where >= 0) { world_tmps_p[i]->operator()(j) = world(where); } else { world_tmps_p[i]->operator()(j) = world_replacement_values_p[i]->operator()(j); } } // Convert for this coordinate. coordinates_p[i]->makeWorldRelative(*(world_tmps_p[i])); // Copy to output for (j=0; joperator[](j); if (where >= 0) world(where) = world_tmps_p[i]->operator()(j); } } } void CoordinateSystem::makeWorldAbsoluteRef (Vector& world, const Vector& refVal) const { AlwaysAssert(world.nelements() == nWorldAxes(), AipsError); AlwaysAssert(refVal.nelements() == nWorldAxes(), AipsError); // const uInt nc = coordinates_p.nelements(); Int where; for (uInt i=0; inelements(); // Copy elements for this coordinate and replace removed axis values // Borrow worldOut_tmps vector from toMix uInt j; for (j=0; joperator[](j); if (where >= 0) { world_tmps_p[i]->operator()(j) = world(where); worldOut_tmps_p[i]->operator()(j) = refVal(where); } else { world_tmps_p[i]->operator()(j) = world_replacement_values_p[i]->operator()(j); worldOut_tmps_p[i]->operator()(j) = coordinates_p[i]->referenceValue()(j); // Use refval } } // Convert for this coordinate. coordinates_p[i]->makeWorldAbsoluteRef (*(world_tmps_p[i]), *(worldOut_tmps_p[i])); // Copy to output for (j=0; joperator[](j); if (where >= 0) world(where) = world_tmps_p[i]->operator()(j); } } } void CoordinateSystem::makeWorldAbsolute (Vector& world) const { AlwaysAssert(world.nelements() == nWorldAxes(), AipsError); // const uInt nc = coordinates_p.nelements(); Int where; for (uInt i=0; inelements(); // Copy elements for this coordinate and replace removed axis values uInt j; for (j=0; joperator[](j); if (where >= 0) { world_tmps_p[i]->operator()(j) = world(where); } else { world_tmps_p[i]->operator()(j) = world_replacement_values_p[i]->operator()(j); } } // Convert for this coordinate. Make private temporary to optimize further coordinates_p[i]->makeWorldAbsolute(*(world_tmps_p[i])); // Copy to output for (j=0; joperator[](j); if (where >= 0) world(where) = world_tmps_p[i]->operator()(j); } } } void CoordinateSystem::makePixelRelative (Vector& pixel) const { AlwaysAssert(pixel.nelements() == nPixelAxes(), AipsError); // const uInt nc = coordinates_p.nelements(); Int where; for (uInt i=0; inelements(); // Copy elements for this coordinate and replace removed axis values uInt j; for (j=0; joperator[](j); if (where >= 0) { pixel_tmps_p[i]->operator()(j) = pixel(where); } else { pixel_tmps_p[i]->operator()(j) = pixel_replacement_values_p[i]->operator()(j); } } // Convert for this coordinate. coordinates_p[i]->makePixelRelative(*(pixel_tmps_p[i])); // Copy to output for (j=0; joperator[](j); if (where >= 0) pixel(where) = pixel_tmps_p[i]->operator()(j); } } } void CoordinateSystem::makePixelAbsolute (Vector& pixel) const { AlwaysAssert(pixel.nelements() == nPixelAxes(), AipsError); // const uInt nc = coordinates_p.nelements(); Int where; for (uInt i=0; inelements(); // Copy elements for this coordinate and replace removed axis values uInt j; for (j=0; joperator[](j); if (where >= 0) { pixel_tmps_p[i]->operator()(j) = pixel(where); } else { pixel_tmps_p[i]->operator()(j) = pixel_replacement_values_p[i]->operator()(j); } } // Convert for this coordinate. Make private temporary to optimize further coordinates_p[i]->makePixelAbsolute(*(pixel_tmps_p[i])); // Copy to output for (j=0; joperator[](j); if (where >= 0) pixel(where) = pixel_tmps_p[i]->operator()(j); } } } Bool CoordinateSystem::convert (Vector& coordOut, const Vector& coordIn, const Vector& absIn, const Vector& unitsIn, MDoppler::Types dopplerIn, const Vector& absOut, const Vector& unitsOut, MDoppler::Types dopplerOut, Double pixInOffset, Double pixOutOffset) { Matrix coordsIn(coordIn.nelements(), 1); Matrix coordsOut(coordIn.nelements(), 1); coordsIn.column(0) = coordIn; // if (convert(coordsOut, coordsIn, absIn, unitsIn, dopplerIn, absOut, unitsOut, dopplerOut, pixInOffset, pixOutOffset)) { coordOut = coordsOut.column(0); return True; } return False; } Bool CoordinateSystem::convert (Matrix& coordsOut, const Matrix& coordsIn, const Vector& absIn, const Vector& unitsIn, MDoppler::Types dopplerIn, const Vector& absOut, const Vector& unitsOut, MDoppler::Types dopplerOut, Double pixInOffset, Double pixOutOffset) { if (nWorldAxes() != nPixelAxes()) { throw (AipsError("Number of pixel and world axes differs")); } // Copy CS so we can mess about with it CoordinateSystem cSysIn = *this; CoordinateSystem cSysOut = *this; // const uInt n = nWorldAxes(); if (n != coordsIn.nrow()) { set_error("Coordinates must all be of length nWorldAxes"); return False; } // Bool ok = absIn.nelements()==n && unitsIn.nelements()==n && absOut.nelements()==n && unitsOut.nelements()==n; if (!ok) { set_error("Inputs must all be of length nWorldAxes"); return False; } coordsOut.resize(coordsIn.shape()); // Find input and output velocity axes, set native units strings // and figure out the allThing Bools IPosition velAxesIn(n); IPosition velAxesOut(n); PtrBlock specCoordsIn(n, (SpectralCoordinate*)0); PtrBlock specCoordsOut(n, (SpectralCoordinate*)0); // Vector unitsIn2(cSysIn.worldAxisUnits().copy()); Vector unitsOut2(cSysOut.worldAxisUnits().copy()); // Vector worldAxesIn(n,False), worldAxesOut(n, False); Vector pixelAxesIn(n,False), pixelAxesOut(n, False); // Bool allPixIn = True; // All input are pixel units Bool allWorldIn = True; // All input are consistent with native units Bool allAbsIn = True; Bool allRelIn = True; // Bool allPixOut = True; // All output are pixel units Bool allWorldOut = True; // All output are consistent with native units Bool allAbsOut = True; Bool allRelOut = True; // String sPix("pix"); Unit velUnit("km/s"); Int coordinate, axisInCoordinate; uInt jIn = 0; uInt jOut = 0; uInt idx; for (uInt i=0; isetVelocity (unitsIn(i), dopplerIn); // velAxesIn(jIn) = i; jIn++; allWorldIn = False; } else if (cSysIn.type(coordinate) == Coordinate::LINEAR) { unitsIn2(i) = unitsIn(i); worldAxesIn(i) = True; } else { set_error("axis with km/s units is neither Spectral nor Linear"); cleanUpSpecCoord(specCoordsIn, specCoordsOut); return False; } } else { unitsIn2(i) = unitsIn(i); worldAxesIn(i) = True; } allPixIn = False; } else { allWorldIn = False; pixelAxesIn(i) = True; } if (absIn(i)) { allRelIn = False; } else { allAbsIn = False; } // Output axes if (unitsOut(i)!=sPix) { Unit uu(unitsOut(i)); if (uu==velUnit) { cSysOut.findWorldAxis(coordinate, axisInCoordinate, i); if (cSysOut.type(coordinate) == Coordinate::SPECTRAL) { specCoordsOut[i] = new SpectralCoordinate(cSysOut.spectralCoordinate(coordinate)); specCoordsOut[i]->setVelocity (unitsOut(i), dopplerOut); // velAxesOut(jOut) = i; jOut++; allWorldOut = False; } else if (cSysOut.type(coordinate) == Coordinate::LINEAR) { unitsOut2(i) = unitsOut(i); worldAxesOut(i) = True; } else { set_error("axis with km/s units is neither Spectral nor Linear"); cleanUpSpecCoord(specCoordsIn, specCoordsOut); return False; } } else { unitsOut2(i) = unitsOut(i); worldAxesOut(i) = True; } allPixOut = False; } else { allWorldOut = False; pixelAxesOut(i) = True; } // if (absOut(i)) { allRelOut = False; } else { allAbsOut = False; } } velAxesIn.resize(jIn,True); velAxesOut.resize(jOut,True); uInt nVelIn = velAxesIn.nelements(); uInt nVelOut = velAxesOut.nelements(); // Set coordinate system units if (!cSysIn.setWorldAxisUnits(unitsIn2)) { set_error(cSysIn.errorMessage()); cleanUpSpecCoord(specCoordsIn, specCoordsOut); return False; } // if (!cSysOut.setWorldAxisUnits(unitsOut2)) { set_error(cSysOut.errorMessage()); cleanUpSpecCoord(specCoordsIn, specCoordsOut); return False; } // Generate toMix ranges. The user *MUST* have called // setWorldMixRanges first. They will be adjusted automatically // for the unit changes Vector worldMin, worldMax; worldMin = cSysIn.worldMixMin(); worldMax = cSysIn.worldMixMax(); // Set up vectors of which we will overwrite bits and pieces Vector absWorldIn(n), relWorldIn(n); Vector absPixelIn(n), relPixelIn(n); Vector absWorldIn2(n), absPixelIn2(n); Vector absWorldOut(n), relWorldOut(n); Vector absPixelOut(n), relPixelOut(n); // Vector world(n), coordOut(n); Double absVel, absVelRef, absFreq; // Vector coordIn(n); Vector coordInFloat(n); Vector coordInInt(n); // Vector relWorldRefIn(cSysIn.referenceValue().copy()); cSysIn.makeWorldRelative(relWorldRefIn); Vector relPixelRefIn(cSysIn.referencePixel().copy()); cSysIn.makePixelRelative(relPixelRefIn); // Loop over fields in record. First we convert to absolute pixel. // Then we convert to whatever we want. We take as many short cuts // as we can. const uInt nCoords = coordsIn.ncolumn(); for (uInt j=0; j 0) { world = cSysIn.referenceValue(); for (uInt i=0; ifrequencyToVelocity(absVelRef, world(idx)))) { set_error(specCoordsIn[idx]->errorMessage()); cleanUpSpecCoord(specCoordsIn, specCoordsOut); return False; } absVel += absVelRef; // rel = abs - ref } // Convert to absolute world if (!(specCoordsIn[idx]->velocityToFrequency(absFreq, absVel))) { set_error(specCoordsIn[idx]->errorMessage()); cleanUpSpecCoord(specCoordsIn, specCoordsOut); return False; } absWorldIn(idx) = absFreq; worldAxesIn(idx) = True; } } // Do mixed conversion to get abs world AND abs pixel if (!cSysIn.toMix(absWorldOut, absPixelOut, absWorldIn, absPixelIn, worldAxesIn, pixelAxesIn, worldMin, worldMax)) { set_error(cSysIn.errorMessage()); cleanUpSpecCoord(specCoordsIn, specCoordsOut); return False; } } // At this point we have a vector of absolute world (cSysIn units) // AND absolute pixel. Bug out now if we can. if (allAbsOut && allPixOut) { coordOut = absPixelOut + pixOutOffset; coordsOut.column(j) = coordOut; continue; } relPixelOut = absPixelOut; cSysOut.makePixelRelative(relPixelOut); if (allRelOut && allPixOut) { coordsOut.column(j) = relPixelOut; continue; } // We must convert our world values to cSysOut units. // We can use the absPixelOut vector for that if (!cSysOut.toWorld(absWorldOut, absPixelOut)) { set_error(cSysOut.errorMessage()); cleanUpSpecCoord(specCoordsIn, specCoordsOut); return False; } // if (allAbsOut && allWorldOut) { coordsOut.column(j) = absWorldOut; continue; } relWorldOut = absWorldOut; cSysOut.makeWorldRelative(relWorldOut); if (allRelOut && allWorldOut) { coordsOut.column(j) = relWorldOut; continue; } // OK on with mixed cases. Pick out the output value for everything // except velocity for (uInt i=0; i 0) { world = cSysOut.referenceValue(); for (uInt i=0; ifrequencyToVelocity(absVel, absWorldOut(idx)))) { set_error(specCoordsOut[idx]->errorMessage()); cleanUpSpecCoord(specCoordsIn, specCoordsOut); return False; } // if (absOut(idx)) { coordOut(idx) = absVel; } else { if (!(specCoordsOut[idx]->frequencyToVelocity(absVelRef, world(idx)))) { set_error(specCoordsOut[idx]->errorMessage()); cleanUpSpecCoord(specCoordsIn, specCoordsOut); return False; } coordOut(idx) = absVel - absVelRef; // rel = abs - ref } } } coordsOut.column(j) = coordOut; } // cleanUpSpecCoord(specCoordsIn, specCoordsOut); return True; } Vector CoordinateSystem::worldAxisNames() const { Vector retval(nWorldAxes()); for (uInt i=0; i tmp = coordinates_p[coord]->worldAxisNames(); retval(i) = tmp(coordAxis); } return retval; } Vector CoordinateSystem::worldAxisUnits() const { Vector retval(nWorldAxes()); for (uInt i=0; i tmp = coordinates_p[coord]->worldAxisUnits(); retval(i) = tmp(coordAxis); } return retval; } Vector CoordinateSystem::referencePixel() const { Vector retval(nPixelAxes()); for (uInt i=0; i tmp = coordinates_p[coord]->referencePixel(); retval(i) = tmp(coordAxis); } return retval; } Matrix CoordinateSystem::linearTransform() const { uInt nr = nWorldAxes(); uInt nc = nPixelAxes(); Matrix retval(nr,nc); retval = 0.0; for (uInt i=0; i= 0 && worldAxis >= 0 && pixelAxis >= 0) { Matrix tmp(coordinates_p[worldCoord]->linearTransform()); retval(i,j) = tmp(worldAxis, pixelAxis); } } } return retval; } Vector CoordinateSystem::increment() const { Vector retval(nWorldAxes()); for (uInt i=0; i tmp = coordinates_p[coord]->increment(); retval(i) = tmp(coordAxis); } return retval; } Vector CoordinateSystem::referenceValue() const { Vector retval(nWorldAxes()); for (uInt i=0; i tmp = coordinates_p[coord]->referenceValue(); retval(i) = tmp(coordAxis); } return retval; } Bool CoordinateSystem::setWorldAxisNames(const Vector &names) { Bool ok = (names.nelements()==nWorldAxes()); if (!ok) { set_error("names vector must be of length nWorldAxes()"); return False; } // const uInt nc = nCoordinates(); for (uInt i=0; i tmp(coordinates_p[i]->worldAxisNames().copy()); const uInt na = tmp.nelements(); for (uInt j=0; joperator[](j); if (which >= 0) { tmp(j) = names(which); } } ok = (coordinates_p[i]->setWorldAxisNames(tmp) && ok); if (!ok) set_error (coordinates_p[i]->errorMessage()); } return ok; } Bool CoordinateSystem::setWorldAxisUnits(const Vector &units) { return setWorldAxisUnits (units, False); } Bool CoordinateSystem::setWorldAxisUnits(const Vector &units, Bool throwException) { String error; if (units.nelements() != nWorldAxes()) { error = "units vector must be of length nWorldAxes()"; } else { const uInt nc = nCoordinates(); for (uInt i=0; i tmp(coordinates_p[i]->worldAxisUnits().copy()); uInt na = tmp.nelements(); for (uInt j=0; joperator[](j); if (which >= 0) tmp[j] = units[which]; } // Set new units if (! coordinates_p[i]->setWorldAxisUnits(tmp)) { error = coordinates_p[i]->errorMessage(); } } } // Check if an error has occurred. If so, throw if needed. if (error.empty()) { return True; // no error } else if (throwException) { throw AipsError (error); } set_error (error); return False; } Bool CoordinateSystem::setReferencePixel(const Vector &refPix) { Bool ok = (refPix.nelements()==nPixelAxes()); if (!ok) { set_error("ref. pix vector must be of length nPixelAxes()"); return False; } // const uInt nc = nCoordinates(); for (uInt i=0; i tmp(coordinates_p[i]->referencePixel().copy()); uInt na = tmp.nelements(); for (uInt j=0; joperator[](j); if (which >= 0) { tmp(j) = refPix(which); } } ok = (coordinates_p[i]->setReferencePixel(tmp) && ok); if (!ok) set_error (coordinates_p[i]->errorMessage()); } return ok; } Bool CoordinateSystem::setLinearTransform(const Matrix &xform) { const uInt nc = nCoordinates(); Bool ok = True; for (uInt i=0; i tmp(coordinates_p[i]->linearTransform().copy()); uInt nrow = tmp.nrow(); uInt ncol = tmp.ncolumn(); for (uInt j=0; joperator[](j); Int whichcol = pixel_maps_p[i]->operator[](k); if (whichrow >= 0 && whichcol >= 0) { tmp(j,k) = xform(whichrow,whichcol); } } } ok = (coordinates_p[i]->setLinearTransform(tmp) && ok); if (!ok) set_error (coordinates_p[i]->errorMessage()); } return ok; } Bool CoordinateSystem::setIncrement(const Vector &inc) { Bool ok = (inc.nelements()==nWorldAxes()); if (!ok) { set_error("increment vector must be of length nWorldAxes()"); return False; } // const uInt nc = nCoordinates(); for (uInt i=0; i tmp(coordinates_p[i]->increment().copy()); uInt na = tmp.nelements(); for (uInt j=0; joperator[](j); if (which >= 0) { tmp(j) = inc(which); } } ok = (coordinates_p[i]->setIncrement(tmp) && ok); if (!ok) set_error (coordinates_p[i]->errorMessage()); } return ok; } Bool CoordinateSystem::setReferenceValue(const Vector &refval) { Bool ok = (refval.nelements()==nWorldAxes()); if (!ok) { set_error("ref. val vector must be of length nWorldAxes()"); return False; } // const uInt nc = nCoordinates(); for (uInt i=0; i tmp(coordinates_p[i]->referenceValue().copy()); uInt na = tmp.nelements(); for (uInt j=0; joperator[](j); if (which >= 0) { tmp(j) = refval(which); } } ok = (coordinates_p[i]->setReferenceValue(tmp) && ok); if (!ok) set_error (coordinates_p[i]->errorMessage()); } return ok; } Bool CoordinateSystem::near(const Coordinate& other, Double tol) const // // Compare this CoordinateSystem with another. // { Vector excludePixelAxes; return near(other,excludePixelAxes,tol); } Bool CoordinateSystem::near(const Coordinate& other, const Vector& excludePixelAxes, Double tol) const // // Compare this CoordinateSystem with another. // // Do not compare axis descriptors on the specified pixel axes; // a dubious thing to do. // // // The separation of world axes and pixel axes, and the ability to // remove axes makes this function a great big mess. // { // Basic checks if (this->type() != other.type()) { set_error("Comparison is not with another CoordinateSystem"); return False; } const CoordinateSystem& cSys = dynamic_cast(other); if (nCoordinates() != cSys.nCoordinates()) { set_error("The CoordinateSystems have different numbers of coordinates"); return False; } if (nPixelAxes() != cSys.nPixelAxes()) { set_error("The CoordinateSystems have different numbers of pixel axes"); return False; } if (nWorldAxes() != cSys.nWorldAxes()) { set_error("The CoordinateSystems have different numbers of world axes"); return False; } // Loop over number of coordinates ostringstream oss; for (Int i=0; i= 0) { allGone = False; break; } } // Continue if we have some unremoved world axes in this coordinate Int excSize = coordinate(i).nPixelAxes(); Vector excludeAxes(excSize); if (!allGone) { // If any of the list of CoordinateSystem exclusion pixel axes // inhabit this coordinate, make a list of the axes in this // coordinate that they correspond to. Int coord, axisInCoord; Int k = 0; for (j=0; j= 0) { // Not removed (can't find it if it's been removed !) findWorldAxis(coord1, axisInCoord1, worldAxes(i)(j)); cSys.findWorldAxis(coord2, axisInCoord2, worldAxes(i)(j)); // This better not happen ! if (coord1 != coord2) { oss << "The coordinate numbers differ (!!) for coordinate number " << i; set_error(String(oss)); return False; } // This might if (axisInCoord1 != axisInCoord2) { oss << "World axis " << j << " in the CoordinateSystems" << "has a different axis number in coordinate number " << i; set_error(String(oss)); return False; } } } // Now, finally, compare the current coordinate from the two // CoordinateSystems except on the specified axes. Leave it // this function to set the error message if (!coordinate(i).near(cSys.coordinate(i),excludeAxes,tol)) { set_error(coordinate(i).errorMessage()); return False; } } } return True; } Bool CoordinateSystem::nearPixel (const CoordinateSystem& other, Double tol) const { if (this->type() != other.type()) { set_error("Comparison is not with another CoordinateSystem"); return False; } // const CoordinateSystem& cSys1 = *this; const CoordinateSystem& cSys2 = dynamic_cast(other); // const uInt nPixelAxes1 = cSys1.nPixelAxes(); const uInt nPixelAxes2 = cSys2.nPixelAxes(); // if (nPixelAxes1 != nPixelAxes2) { set_error("The CoordinateSystems have different numbers of pixel axes"); return False; } // const uInt nPixelAxes = nPixelAxes1; Int coord1, axisInCoord1; Int coord2, axisInCoord2; for (uInt i=0; i=0, AipsError); AlwaysAssert(coord2>=0, AipsError); // const Coordinate& c1 = cSys1.coordinate(coord1); const Coordinate& c2 = cSys2.coordinate(coord2); if (c1.type() != c2.type()) { ostringstream oss; oss << "The coordinate types differ for pixel axis number " << i; set_error(String(oss)); return False; } // Vector pixelAxes1 = cSys1.pixelAxes(coord1); Vector pixelAxes2 = cSys2.pixelAxes(coord2); // Vector whichAxes1(pixelAxes1.nelements(), True); Vector whichAxes2(pixelAxes2.nelements(), True); // for (uInt j=0; j=0 && axis >= 0, AipsError); return coordinates_p[coord]->format( units, format, worldValue, axis, isAbsolute, showAsAbsolute, precision, usePrecForMixed ); } ObsInfo CoordinateSystem::obsInfo() const { return obsinfo_p; } void CoordinateSystem::setObsInfo(const ObsInfo &obsinfo) { obsinfo_p = obsinfo; } String CoordinateSystem::coordRecordName(uInt which) const { // Write each string into a field it's type plus coordinate // number, e.g. direction0 string basename = "unknown"; switch (coordinates_p[which]->type()) { case Coordinate::LINEAR: basename = "linear"; break; case Coordinate::DIRECTION: basename = "direction"; break; case Coordinate::SPECTRAL: basename = "spectral"; break; case Coordinate::STOKES: basename = "stokes"; break; case Coordinate::TABULAR: basename = "tabular"; break; case Coordinate::QUALITY: basename = "quality"; break; case Coordinate::COORDSYS: basename = "coordsys"; break; } ostringstream onum; onum << which; return basename + onum.str(); } Bool CoordinateSystem::save(RecordInterface &container, const String &fieldName) const { Record subrec; if (container.isDefined(fieldName)) { set_error(String("The fieldName is already defined in the supplied record")); return False; } // Write the obsinfo String error; Bool ok = obsinfo_p.toRecord(error, subrec); if (!ok) { set_error (error); return False; } // If no coordinates, just run away with the ObsInfo // in place uInt nc = coordinates_p.nelements(); if (nc==0) { container.defineRecord(fieldName, subrec); return True; } for (uInt i=0; itype()) { case Coordinate::LINEAR: basename = "linear"; break; case Coordinate::DIRECTION: basename = "direction"; break; case Coordinate::SPECTRAL: basename = "spectral"; break; case Coordinate::STOKES: basename = "stokes"; break; case Coordinate::QUALITY: basename = "quality"; break; case Coordinate::TABULAR: basename = "tabular"; break; case Coordinate::COORDSYS: basename = "coordsys"; break; } ostringstream onum; onum << i; String num = onum; String name = basename + num; coordinates_p[i]->save(subrec, name); name = String("worldmap") + num; subrec.define(name, Vector(world_maps_p[i]->begin(), world_maps_p[i]->end())); name = String("worldreplace") + num; subrec.define(name, Vector(*world_replacement_values_p[i])); name = String("pixelmap") + num; subrec.define(name, Vector(pixel_maps_p[i]->begin(), pixel_maps_p[i]->end())); name = String("pixelreplace") + num; subrec.define(name, Vector(*pixel_replacement_values_p[i])); } // if (ok) { container.defineRecord(fieldName, subrec); } return ok; } CoordinateSystem* CoordinateSystem::restore(const RecordInterface &container, const String &fieldName) { CoordinateSystem *retval = 0; // Handle an empty field name Record subrec; if (fieldName.empty()) { subrec = Record(container); } else { if (container.isDefined(fieldName)) { subrec = Record(container.asRecord(fieldName)); } else { return retval; } } // PtrBlock tmp; Int nc = 0; // num coordinates PtrBlock coords; static const String linear = "linear"; static const String direction = "direction"; static const String spectral = "spectral"; static const String stokes = "stokes"; static const String quality = "quality"; static const String tabular = "tabular"; static const String coordsys = "coordsys"; while(True) { ostringstream onum; onum << nc; String num = onum; nc++; if (subrec.isDefined(linear + num)) { coords.resize(nc); coords[nc-1] = LinearCoordinate::restore(subrec, linear+num); } else if (subrec.isDefined(direction + num)) { coords.resize(nc); coords[nc-1] = DirectionCoordinate::restore( subrec, direction+num ); } else if (subrec.isDefined(spectral + num)) { coords.resize(nc); coords[nc-1] = SpectralCoordinate::restore(subrec, spectral+num); } else if (subrec.isDefined(stokes + num)) { coords.resize(nc); coords[nc-1] = StokesCoordinate::restore(subrec, stokes+num); } else if (subrec.isDefined(quality + num)) { coords.resize(nc); coords[nc-1] = QualityCoordinate::restore(subrec, quality+num); } else if (subrec.isDefined(tabular + num)) { coords.resize(nc); coords[nc-1] = TabularCoordinate::restore(subrec, tabular+num); } else if (subrec.isDefined(coordsys + num)) { coords.resize(nc); coords[nc-1] = CoordinateSystem::restore(subrec, coordsys+num); } else { break; } AlwaysAssert(coords[nc-1] != 0, AipsError); } nc = coords.nelements(); // retval = new CoordinateSystem; Int i; for (i=0; iaddCoordinate(*(coords[i])); delete coords[i]; coords[i] = 0; } for (i=0; i dummy; String num(onum), name; name = String("worldmap") + num; subrec.get(name, dummy); *(retval->world_maps_p[i]) = makeBlock(dummy); name = String("worldreplace") + num; subrec.get(name, *(retval->world_replacement_values_p[i])); name = String("pixelmap") + num; subrec.get(name, dummy); *(retval->pixel_maps_p[i]) = makeBlock(dummy); name = String("pixelreplace") + num; subrec.get(name, *(retval->pixel_replacement_values_p[i])); } // // Get the obsinfo // String error; Bool ok = retval->obsinfo_p.fromRecord(error, subrec); AlwaysAssert(ok, AipsError); // Should never happen // return retval; } Coordinate *CoordinateSystem::clone() const { return new CoordinateSystem(*this); } Bool CoordinateSystem::toFITSHeader(RecordInterface &header, IPosition &shape, Bool oneRelative, Char prefix, Bool writeWCS, Bool preferVelocity, Bool opticalVelocity, Bool preferWavelength, Bool airWavelength) const { FITSCoordinateUtil fcu; return fcu.toFITSHeader(header, shape, *this, oneRelative, prefix, writeWCS, preferVelocity, opticalVelocity, preferWavelength, airWavelength); } Bool CoordinateSystem::fromFITSHeader (Int& stokesFITSValue, CoordinateSystem& cSysOut, RecordInterface& recHeader, const Vector& header, const IPosition& shape, uInt which) { FITSCoordinateUtil fcu; return fcu.fromFITSHeader (stokesFITSValue, cSysOut, recHeader, header, shape, which); } void CoordinateSystem::makeWorldAbsoluteMany (Matrix& world) const { makeWorldAbsRelMany (world, True); } void CoordinateSystem::makeWorldRelativeMany (Matrix& world) const { makeWorldAbsRelMany (world, False); } void CoordinateSystem::makePixelAbsoluteMany (Matrix& pixel) const { makePixelAbsRelMany (pixel, True); } void CoordinateSystem::makePixelRelativeMany (Matrix& pixel) const { makePixelAbsRelMany (pixel, False); } void CoordinateSystem::makeWorldAbsRelMany (Matrix& world, Bool toAbs) const { const uInt nTransforms = world.ncolumn(); // Loop over coordinates uInt i, k; Int where; // const uInt nCoords = coordinates_p.nelements(); for (k=0; knelements(); Matrix worldTmp(nWorldAxes,nTransforms); for (i=0; ioperator[](i); if (where >= 0) { worldTmp.row(i) = world.row(where); } else { worldTmp.row(i) = world_replacement_values_p[k]->operator()(i); } } // Do conversion using Coordinate specific implementation if (toAbs) { coordinates_p[k]->makeWorldAbsoluteMany(worldTmp); } else { coordinates_p[k]->makeWorldRelativeMany(worldTmp); } // Unload for (i=0; ioperator[](i); if (where >= 0) { world.row(where) = worldTmp.row(i); } } } } void CoordinateSystem::makePixelAbsRelMany (Matrix& pixel, Bool toAbs) const { const uInt nTransforms = pixel.ncolumn(); // Loop over coordinates uInt i, k; Int where; // const uInt nCoords = coordinates_p.nelements(); for (k=0; knelements(); Matrix pixelTmp(nPixelAxes,nTransforms); for (i=0; ioperator[](i); if (where >= 0) { pixelTmp.row(i) = pixel.row(where); } else { pixelTmp.row(i) = pixel_replacement_values_p[k]->operator()(i); } } // Do conversion using Coordinate specific implementation if (toAbs) { coordinates_p[k]->makePixelAbsoluteMany(pixelTmp); } else { coordinates_p[k]->makePixelRelativeMany(pixelTmp); } // Unload for (i=0; ioperator[](i); if (where >= 0) { pixel.row(where) = pixelTmp.row(i); } } } } Coordinate* CoordinateSystem::makeFourierCoordinate (const Vector& axes, const Vector& shape) const { LogIO os(LogOrigin(_class, __FUNCTION__, WHERE)); // if (axes.nelements() != nPixelAxes()) { throw (AipsError("Invalid number of specified pixel axes")); } if (axes.nelements()==0) { throw (AipsError("There are no pixel axes in this CoordinateSystem")); } // if (allEQ(axes,False)) { throw (AipsError("You have not specified any axes to transform")); } // if (shape.nelements() != nPixelAxes()) { throw (AipsError("Invalid number of elements in shape")); } // Make a copy of the CS. The caste is safe. Coordinate* pC = clone(); CoordinateSystem* pCS = dynamic_cast(pC); // uInt nReplaced = 0; const uInt nCoord = nCoordinates(); for (uInt i=0; i coordSysAxes = pixelAxes(i); Vector coordAxes(coordSysAxes.nelements(),False); Vector coordShape(coordAxes.nelements(),0); // for (uInt j=0; jreplaceCoordinate(*pC2, i); delete pC2; } } // pCS = 0; return pC; } Bool CoordinateSystem::checkAxesInThisCoordinate(const Vector& axes, uInt which) const // // 1) See if this coordinate has any axes to be FTd // 2) Make sure they are all good. // { LogIO os(LogOrigin(_class, __FUNCTION__, WHERE)); // Bool wantIt = False; // Loop over pixel axes in the coordinatesystem Int coord, axisInCoord, worldAxis; for (uInt i=0; i CoordinateSystem::list (LogIO& os, MDoppler::Types doppler, const IPosition& latticeShape, const IPosition& tileShape, Bool postLocally) const { LogSinkInterface& lsi = os.localSink(); uInt n = lsi.nelements(); Int iStart = 0; if (n>0) iStart = n - 1; os << LogIO::NORMAL << endl; // List DirectionCoordinate type from the first DirectionCoordinate we find listDirectionSystem(os); // List rest frequency and reference frame from the first spectral axis we find listFrequencySystem(os, doppler); // Pointing center listPointingCenter(os); // List telescope, observer, date os << "Telescope : " << obsinfo_p.telescope() << endl; os << "Observer : " << obsinfo_p.observer() << endl; // MEpoch epoch = obsinfo_p.obsDate(); MEpoch defEpoch = ObsInfo::defaultObsDate(); if (epoch.getValue().getDay() != defEpoch.getValue().getDay()) { MVTime time = MVTime(epoch.getValue()); os << "Date observation : " << time.string(MVTime::YMD, 12) << endl; } else { os << "Date observation : " << "UNKNOWN" << endl; } if (obsinfo_p.isTelescopePositionSet()) { os << "Telescope position: " << obsinfo_p.telescopePositionString() << endl; } os << endl; // Determine the widths for all the fields that we want to list Bool doShape = tileShape.nelements()>0 && latticeShape.nelements()>0 && tileShape.nelements()==latticeShape.nelements(); uInt widthName, widthProj, widthShape, widthTile, widthRefValue; uInt widthRefPixel, widthInc, widthUnits, totWidth, widthCoordType; uInt widthAxis, widthCoordNumber; String nameName, nameProj, nameShape, nameTile, nameRefValue; String nameRefPixel, nameInc, nameUnits, nameCoordType, nameAxis; String nameCoordNumber; Int precRefValSci, precRefValFloat, precRefValRADEC; Int precRefPixFloat, precIncSci; getFieldWidths (os, widthAxis, widthCoordType, widthCoordNumber, widthName, widthProj, widthShape, widthTile, widthRefValue, widthRefPixel, widthInc, widthUnits, precRefValSci, precRefValFloat, precRefValRADEC, precRefPixFloat, precIncSci, nameAxis, nameCoordType, nameCoordNumber, nameName, nameProj, nameShape, nameTile, nameRefValue, nameRefPixel, nameInc, nameUnits, doppler, latticeShape, tileShape); // Write headers os.output().fill(' '); os.output().setf(ios::left, ios::adjustfield); os.output().width(widthAxis); os << nameAxis; os.output().width(widthCoordNumber); os << nameCoordNumber; os.output().width(widthCoordType); os << nameCoordType; os.output().width(widthName); os << nameName; os.output().setf(ios::right, ios::adjustfield); os.output().width(widthProj); os << nameProj; if (doShape) { os.output().width(widthShape); os << nameShape; os.output().width(widthTile); os << nameTile; } os.output().width(widthRefValue); os << nameRefValue; os.output().width(widthRefPixel); os << nameRefPixel; os.output().width(widthInc); os << nameInc; os << nameUnits << endl; totWidth = widthAxis + widthCoordType + widthCoordNumber + widthName + widthProj + widthShape + widthTile + widthRefValue + widthRefPixel + widthInc + widthUnits; os.output().fill('-'); os.output().width(totWidth); os.output().setf(ios::right, ios::adjustfield); os << " " << endl; os.output() << setfill(' '); // Loop over the pixel axes in the CS. Int axisInCoordinate, coordinate; for (uInt pixelAxis=0; pixelAxistype() == Coordinate::SPECTRAL) { listVelocity (os, pc, widthAxis, widthCoordType, widthCoordNumber, widthName, widthProj, widthShape, widthTile, widthRefValue, widthRefPixel, widthInc, widthUnits, False, axisInCoordinate, pixelAxis, doppler, precRefValSci, precRefValFloat, precRefValRADEC, precRefPixFloat, precIncSci); } // If we have listed all of the pixel axes from this coordinate, then // see if there are any world axes without pixel axes and list them Vector pixelAxes = this->pixelAxes(coordinate); Vector worldAxes = this->worldAxes(coordinate); Int maxPixAxis = max(pixelAxes); if (Int(pixelAxis) == maxPixAxis) { for (uInt axis=0; axis= 0) { listHeader(os, pc, widthAxis, widthCoordType, widthCoordNumber, widthName, widthProj, widthShape, widthTile, widthRefValue, widthRefPixel, widthInc, widthUnits, False, coordinate, axis, -1, precRefValSci, precRefValFloat, precRefValRADEC, precRefPixFloat, precIncSci, latticeShape, tileShape); // if (pc->type() == Coordinate::SPECTRAL) { listVelocity (os, pc, widthAxis, widthCoordType, widthCoordNumber, widthName, widthProj, widthShape, widthTile, widthRefValue, widthRefPixel, widthInc, widthUnits, False, axis, -1, doppler, precRefValSci, precRefValFloat, precRefValRADEC, precRefPixFloat, precIncSci); } } } } // delete pc; } os << endl; // Post it if (postLocally) { os.postLocally(); } else { os.post(); } // n = lsi.nelements(); Vector messages(n-iStart); if (postLocally) { for (uInt i=iStart; i0 && latticeShape.nelements()>0 && tileShape.nelements()==latticeShape.nelements(); // Header names for fields nameAxis = "Axis"; nameCoordType = "Type"; nameCoordNumber = "Coord"; nameName = "Name"; nameProj = "Proj"; nameShape = "Shape"; nameTile = "Tile"; nameRefValue = "Coord value"; nameRefPixel = "at pixel"; nameInc = "Coord incr"; nameUnits = " Units"; // Initialize (logger will never be actually used in this function) widthName = widthProj = widthShape = widthTile = widthRefValue = 0; widthRefPixel = widthInc = widthUnits = widthCoordType = widthAxis = 0; widthCoordNumber = 0; // Loop over number of world axes Int pixelAxis; uInt worldAxis; Int coordinate, axisInCoordinate; for (worldAxis=0; worldAxistype() == Coordinate::SPECTRAL) { listVelocity (os, pc, widthAxis, widthCoordType, widthCoordNumber, widthName, widthProj, widthShape, widthTile, widthRefValue, widthRefPixel, widthInc, widthUnits, True, axisInCoordinate, pixelAxis, doppler, precRefValSci, precRefValFloat, precRefValRADEC, precRefPixFloat, precIncSci); } // delete pc; } // Compare with header widths. We only list the coordinate type // if we are not listing the shape widthAxis = max(nameAxis.length(), widthAxis) + 1; widthCoordType = max(nameCoordType.length(), widthCoordType) + 1; widthCoordNumber = max(nameCoordNumber.length(), widthCoordNumber) + 1; widthName = max(nameName.length(), widthName) + 1; widthProj = max(nameProj.length(), widthProj) + 1; if (doShape) { widthShape = max(nameShape.length(), widthShape) + 1; widthTile = max(nameTile.length(), widthTile) + 1; } widthRefValue = max(nameRefValue.length(), widthRefValue) + 1; widthRefPixel = max(nameRefPixel.length(), widthRefPixel) + 1; widthInc = max(nameInc.length(), widthInc) + 1; widthUnits = max(nameUnits.length(), widthUnits); } void CoordinateSystem::listHeader (LogIO& os, Coordinate* pc, uInt& widthAxis, uInt& widthCoordType, uInt& widthCoordNumber, uInt& widthName, uInt& widthProj, uInt& widthShape, uInt& widthTile, uInt& widthRefValue, uInt& widthRefPixel, uInt& widthInc, uInt& widthUnits, Bool findWidths, Int coordinate, Int axisInCoordinate, Int pixelAxis, Int precRefValSci, Int precRefValFloat, Int precRefValRADEC, Int precRefPixFloat, Int, const IPosition& latticeShape, const IPosition& tileShape) const // // List all the good stuff // // Input: // os The LogIO to write to // pc Pointer to the coordinate // coordinate Coordinate number // axisIncoordinate The axis number in this coordinate // pixelAxis The axis in the image for this axis in this coordinate // { // Clear flags if (!findWidths) clearFlags(os); // Axis number String string; { ostringstream oss; if (pixelAxis != -1) { //oss << pixelAxis + 1; oss << pixelAxis; } else { oss << ".."; } string = String(oss); if (findWidths) { widthAxis = max(widthAxis, string.length()); } else { os.output().setf(ios::left, ios::adjustfield); os.output().width(widthAxis); os << string; } } // Coordinate number { ostringstream oss; //oss << coordinate + 1; oss << coordinate; string = String(oss); if (findWidths) { widthCoordNumber = max(widthCoordNumber, string.length()); } else { os.output().setf(ios::left, ios::adjustfield); os.output().width(widthCoordNumber); os << string; } } // Coordinate type string = Coordinate::typeToString(pc->type()); if (findWidths) { widthCoordType = max(widthCoordType, string.length()); } else { os.output().setf(ios::left, ios::adjustfield); os.output().width(widthCoordType); os << string; } // Axis name string = pc->worldAxisNames()(axisInCoordinate); if (pc->type() == Coordinate::SPECTRAL) { SpectralCoordinate* sc = dynamic_cast(pc); if (sc->isTabular()) { string += " (tab)"; } } if (findWidths) { widthName = max(widthName, string.length()); } else { os.output().setf(ios::left, ios::adjustfield); os.output().width(widthName); os << string; } // Projection if (pc->type() == Coordinate::DIRECTION) { DirectionCoordinate* dc = dynamic_cast(pc); string = dc->isNCP() ? "NCP" : dc->projection().name(); } else { string = " "; } if (findWidths) { widthProj = max(widthProj, string.length()); } else { os.output().setf(ios::right, ios::adjustfield); os.output().width(widthProj); os << string; } // Number of pixels Bool doShape = tileShape.nelements()>0 && latticeShape.nelements()>0 && tileShape.nelements()==latticeShape.nelements(); if (doShape) { if (pixelAxis != -1) { ostringstream oss; oss << latticeShape(pixelAxis); string = String(oss); } else { string = " "; } if (findWidths) { widthShape = max(widthShape, string.length()); } else { os.output().width(widthShape); os << string; } // Tile shape if (pixelAxis != -1) { ostringstream oss; oss << tileShape(pixelAxis); string = String(oss); } else { string = " "; } if (findWidths) { widthTile = max(widthTile, string.length()); } else { os.output().width(widthTile); os << string; } } // Remember units Vector oldUnits(pc->nWorldAxes()); oldUnits = pc->worldAxisUnits(); Vector units(pc->nWorldAxes()); // Reference value String refValListUnits; { Coordinate::formatType form; Int prec; // if (pc->type() == Coordinate::STOKES) { StokesCoordinate* sc = dynamic_cast(pc); // Vector world(1); Vector pixel(1); String sName; form = Coordinate::DEFAULT; if (pixelAxis != -1) { const uInt nPixels = sc->stokes().nelements(); for (uInt i=0; itoWorld(world, pixel); String temp; if (ok) { temp = sc->format(refValListUnits, form, world(0), axisInCoordinate, True, True, -1); } else { temp = "?"; } if (i>0) { sName += String(" ") + temp; } else { sName += temp; } } } else { pixel(0) = (*pixel_replacement_values_p[coordinate])[axisInCoordinate]; Bool ok = sc->toWorld(world, pixel); if (ok) { sName = sc->format(refValListUnits, form, world(0), axisInCoordinate, True, True, -1); } else { sName = "?"; } } string = sName; } else if (pc->type() == Coordinate::QUALITY) { QualityCoordinate* qc = dynamic_cast(pc); // Vector world(1); Vector pixel(1); String sName; form = Coordinate::DEFAULT; if (pixelAxis != -1) { const uInt nPixels = qc->quality().nelements(); for (uInt i=0; itoWorld(world, pixel); String temp; if (ok) { temp = qc->format(refValListUnits, form, world(0), axisInCoordinate, True, True, -1); } else { temp = "?"; } if (i>0) { sName += String(" ") + temp; } else { sName += temp; } } } else { pixel(0) = (*pixel_replacement_values_p[coordinate])[axisInCoordinate]; Bool ok = qc->toWorld(world, pixel); if (ok) { sName = qc->format(refValListUnits, form, world(0), axisInCoordinate, True, True, -1); } else { sName = "?"; } } string = sName; } else { form = Coordinate::DEFAULT; pc->getPrecision(prec, form, True, precRefValSci, precRefValFloat, precRefValRADEC); if (pixelAxis != -1) { string = pc->format(refValListUnits, form, pc->referenceValue()(axisInCoordinate), axisInCoordinate, True, True, prec); } else { Vector world; pc->toWorld(world, (*pixel_replacement_values_p[coordinate])); string = pc->format(refValListUnits, form, world(axisInCoordinate), axisInCoordinate, True, True, prec); } } } if (findWidths) { widthRefValue = max(widthRefValue,string.length()); } else { os.output().width(widthRefValue); os << string; } // Reference pixel if (pc->type() != Coordinate::STOKES && (pc->type()!= Coordinate::QUALITY)) { ostringstream oss; oss.setf(ios::fixed, ios::floatfield); oss.precision(precRefPixFloat); if (pixelAxis != -1) { oss << pc->referencePixel()(axisInCoordinate); // + 1.0; } else { oss << (*pixel_replacement_values_p[coordinate])[axisInCoordinate]; // + 1.0; } string = String(oss); if (findWidths) { widthRefPixel = max(widthRefPixel,string.length()); } else { os.output().width(widthRefPixel); os << string; } } // Increment String incUnits; if (pc->type() != Coordinate::STOKES && (pc->type()!= Coordinate::QUALITY)) { if (pixelAxis != -1) { Coordinate::formatType form; Int prec; form = Coordinate::SCIENTIFIC; pc->getPrecision(prec, form, False, precRefValSci, precRefValFloat, precRefValRADEC); string = pc->format(incUnits, form, pc->increment()(axisInCoordinate), axisInCoordinate, False, False, prec); } else { string = " "; } if (findWidths) { widthInc = max(widthInc,string.length()); } else { os.output().width(widthInc); os << string; } } // Units if ((pc->type()!= Coordinate::STOKES) && (pc->type()!= Coordinate::QUALITY)) { if (pixelAxis != -1) { string = " " + incUnits; } else { string = " " + refValListUnits; } if (findWidths) { widthUnits = max(widthUnits,string.length()); } else { os.output().setf(ios::left, ios::adjustfield); os << string; } } if (!findWidths) os << endl; } void CoordinateSystem::listVelocity (LogIO& os, Coordinate* pc, uInt widthAxis, uInt widthCoordType, uInt widthCoordNumber, uInt& widthName, uInt widthProj, uInt widthShape, uInt widthTile, uInt& widthRefValue, uInt widthRefPixel, uInt& widthInc, uInt& widthUnits, Bool findWidths, Int axisInCoordinate, Int pixelAxis, MDoppler::Types doppler, Int precRefValSci, Int precRefValFloat, Int precRefValRADEC, Int precRefPixFloat, Int precIncSci) const // // List all the good stuff // // Input: // os The LogIO to write to // pc Pointer to the coordinate // axisIncoordinate The axis number in this coordinate // pixelAxis The axis in the image for this axis in this coordinate // { // Clear flags if (!findWidths) clearFlags(os); // Caste to get SpectralCoordinate SpectralCoordinate* sc0 = dynamic_cast(pc); SpectralCoordinate sc(*sc0); // If the rest freq is non-positive, out we go Double restFreq = sc.restFrequency(); if (restFreq <=0.0) return; // Axis number String string; if (!findWidths) { os.output().width(widthAxis); string = " "; os << string; } // Coordinate number if (!findWidths) { os.output().width(widthCoordNumber); string = " "; os << string; } // Coordinate type if (!findWidths) { os.output().width(widthCoordType); string = " "; os << string; } // Axis name string = "Velocity"; if (findWidths) { widthName = max(widthName, string.length()); } else { os.output().setf(ios::left, ios::adjustfield); os.output().width(widthName); os << string; } // Projection if (!findWidths) { os.output().setf(ios::right, ios::adjustfield); os.output().width(widthProj); string = " "; os << string; } // Number of pixels if (widthShape>0 && widthTile>0) { if (!findWidths) { os.output().width(widthShape); string = " "; os << string; } // Tile shape if (!findWidths) { os.output().width(widthTile); string = " "; os << string; } } // Remember units Vector oldUnits(sc.nWorldAxes()); oldUnits = sc.worldAxisUnits(); Vector units(sc.nWorldAxes()); // Convert reference pixel it to a velocity and format Coordinate::formatType form; String velUnits("km/s"); Int prec; form = Coordinate::DEFAULT; sc.getPrecision(prec, form, True, precRefValSci, precRefValFloat, precRefValRADEC); // String empty; sc.setVelocity (empty, doppler); string = sc.format(velUnits, form, sc.referenceValue()(axisInCoordinate), axisInCoordinate, True, True, prec); if (findWidths) { widthRefValue = max(widthRefValue,string.length()); } else { os.output().width(widthRefValue); os << string; } // Reference pixel if (pixelAxis != -1) { ostringstream oss; oss.setf(ios::fixed, ios::floatfield); oss.precision(precRefPixFloat); oss << sc.referencePixel()(axisInCoordinate); // + 1.0; string = String(oss); } else { string = " "; } if (!findWidths) { os.output().width(widthRefPixel); os << string; } // Increment if (pixelAxis != -1) { Double velocityInc; if (!velocityIncrement(velocityInc, sc, doppler, velUnits)) { string = "Fail"; } else { ostringstream oss; oss.setf(ios::scientific, ios::floatfield); oss.precision(precIncSci); oss << velocityInc; string = String(oss); } } else { string = " "; } if (findWidths) { widthInc = max(widthInc,string.length()); } else { os.output().width(widthInc); os << string; } // Increment units if (pixelAxis != -1) { string = " " + velUnits; } else { string = " "; } if (findWidths) { widthUnits = max(widthUnits,string.length()); } else { os.output().setf(ios::left, ios::adjustfield); os << string; } if (!findWidths) os << endl; } void CoordinateSystem::clearFlags(LogIO& os) const // // Clear all the formatting flags // { os.output().unsetf(ios::left); os.output().unsetf(ios::right); os.output().unsetf(ios::internal); os.output().unsetf(ios::dec); os.output().unsetf(ios::oct); os.output().unsetf(ios::hex); os.output().unsetf(ios::showbase | ios::showpos | ios::uppercase | ios::showpoint); os.output().unsetf(ios::scientific); os.output().unsetf(ios::fixed); } Bool CoordinateSystem::velocityIncrement(Double& velocityInc, SpectralCoordinate& sc, MDoppler::Types doppler, const String& velUnits) const { // DO this the hard way for now until Wim gives me spectralMachine if (sc.nWorldAxes() != 1) return False; Double refPix = sc.referencePixel()(0); // Find world values at refPix +/- 0.5 and take difference Double pixel; pixel = refPix + 0.5; Quantum velocity1; sc.setVelocity (velUnits, doppler); if (!sc.pixelToVelocity(velocity1, pixel)) return False; // pixel = refPix - 0.5; Quantum velocity2; if (!sc.pixelToVelocity(velocity2, pixel)) return False; // Return increment velocityInc = velocity1.getValue() - velocity2.getValue(); return True; } void CoordinateSystem::listDirectionSystem(LogIO& os) const { Int afterCoord = -1; Int ic = findCoordinate(Coordinate::DIRECTION, afterCoord); if (ic >= 0) { const DirectionCoordinate& coord = directionCoordinate(uInt(ic)); MDirection::Types type = coord.directionType(); MDirection::Types conversionType; coord.getReferenceConversion(conversionType); // if (type==conversionType) { os << "Direction reference : " << MDirection::showType(type) << endl; } else { os << "Direction reference : " << MDirection::showType(type) << " (-> " << MDirection::showType(conversionType) << ")" << endl; } } } void CoordinateSystem::listFrequencySystem(LogIO& os, MDoppler::Types doppler) const { Int afterCoord = -1; Int ic = findCoordinate(Coordinate::SPECTRAL, afterCoord); if (ic >= 0) { const SpectralCoordinate& coord = spectralCoordinate(uInt(ic)); MFrequency::Types type = coord.frequencySystem(); MFrequency::Types conversionType; MEpoch epoch; MDirection direction; MPosition position; coord.getReferenceConversion(conversionType, epoch, position, direction); // if (type==conversionType) { os << "Spectral reference : " << MFrequency::showType(type) << endl; } else { os << "Spectral reference : " << MFrequency::showType(type) << " (-> " << MFrequency::showType(conversionType) << ")" << endl; } // os << "Velocity type : " << MDoppler::showType(doppler) << endl; // String str = coord.formatRestFrequencies(); if (!str.empty()) os << str << endl; } } void CoordinateSystem::listPointingCenter (LogIO& os) const { Int afterCoord = -1; Int iC = findCoordinate(Coordinate::DIRECTION, afterCoord); if (iC >= 0) { if (!obsinfo_p.isPointingCenterInitial()) { Int prec; Coordinate::formatType form(Coordinate::DEFAULT); coordinates_p[iC]->getPrecision(prec, form, True, 6, 6, 6); // MVDirection pc = obsinfo_p.pointingCenter(); Quantum qLon = pc.getLong(Unit(String("deg"))); Quantum qLat = pc.getLat(Unit(String("deg"))); // String listUnits; String lon = coordinates_p[iC]->formatQuantity(listUnits, form, qLon, 0, True, True, prec); String lat = coordinates_p[iC]->formatQuantity(listUnits, form, qLat, 1, True, True, prec); // ostringstream oss; oss << "Pointing center : " << lon << " " << lat; os << String(oss) << endl; } } } StokesCoordinate CoordinateSystem::stokesSubImage(const StokesCoordinate& sc, Int originShift, Int pixincFac, Int newShape) const { const Vector& values = sc.stokes(); const Int nValues = values.nelements(); // Int start = originShift; if (start < 0 || start > nValues-1) { throw(AipsError("Illegal origin shift")); } // Vector newStokes(nValues); Int j = start; Int n = 0; while (j <= nValues-1) { newStokes(n) = values(j); n++; j += pixincFac; } // If shape given, use it if (newShape>0) { if (newShape>n) { throw(AipsError("New shape is invalid")); } // newStokes.resize(newShape, True); } else { newStokes.resize(n, True); } // StokesCoordinate scOut(sc); scOut.setStokes(newStokes); return scOut; } QualityCoordinate CoordinateSystem::qualitySubImage(const QualityCoordinate& qc, Int originShift, Int pixincFac, Int newShape) const { const Vector& values = qc.quality(); const Int nValues = values.nelements(); // Int start = originShift; if (start < 0 || start > nValues-1) { throw(AipsError("Illegal origin shift")); } // Vector newQuality(nValues); Int j = start; Int n = 0; while (j <= nValues-1) { newQuality(n) = values(j); n++; j += pixincFac; } // If shape given, use it if (newShape>0) { if (newShape>n) { throw(AipsError("New shape is invalid")); } // newQuality.resize(newShape, True); } else { newQuality.resize(n, True); } // QualityCoordinate qcOut(qc); qcOut.setQuality(newQuality); return qcOut; } Bool CoordinateSystem::setWorldMixRanges (const IPosition& shape) { AlwaysAssert(shape.nelements()==nPixelAxes(), AipsError); // for (uInt i=0; i pA = pixelAxes(i); Vector wA = worldAxes(i); IPosition shape2(coordinates_p[i]->nPixelAxes()); for (uInt j=0; jsetWorldMixRanges (shape2)) { set_error(coordinates_p[i]->errorMessage()); return False; } // If there is a removed pixel axis, but not world axis // we can be cleverer. We need to use the removed pixel coordinate // value as the centre value. The DC knows nothing about the removal, // its the CS that knows this. if (coordinates_p[i]->type()==Coordinate::DIRECTION) { DirectionCoordinate* dC = dynamic_cast(coordinates_p[i]); Vector pixel(dC->referencePixel().copy()); Vector which(dC->nWorldAxes(), False); Bool doit = False; for (uInt j=0; j=0) { pixel(j) = pixel_replacement_values_p[i]->operator()(j); which(j) = True; doit = True; } } // if (doit) { Vector world; dC->toWorld(world, pixel); dC->setWorldMixRanges(which, world); } } } //cerr << "min, max = " << worldMixMin() << worldMixMax() << endl; return True; } void CoordinateSystem::setDefaultWorldMixRanges () { for (uInt i=0; isetDefaultWorldMixRanges (); } } Vector CoordinateSystem::worldMixMin () const // // Could speed up by holding min/max in a private variable // { Vector wm(nWorldAxes()); for (uInt i=0; i tmp = coordinates_p[coord]->worldMixMin(); wm(i) = tmp(coordAxis); } return wm; } Vector CoordinateSystem::worldMixMax () const // // Could speed up by holding min/max in a private variable // { Vector wm(nWorldAxes()); for (uInt i=0; i tmp = coordinates_p[coord]->worldMixMax(); wm(i) = tmp(coordAxis); } return wm; } void CoordinateSystem::cleanUpSpecCoord (PtrBlock& in, PtrBlock& out) { for (uInt i=0; i& worldAxes = cSys.worldAxes(i); const Vector& pixelAxes = cSys.pixelAxes(i); noWorld = allEQ(worldAxes, -1); noPixel = allEQ(pixelAxes, -1); // if (!noWorld || !noPixel) { // This coordinate has some pixel or world axes left, so hang onto it cSysOut.addCoordinate(coord); // We must remove, in the output CS, the same world/pixel // axes that have been removed in the input CS Vector refVal = coord.referenceValue(); Vector refPix = coord.referencePixel(); const Vector& worldAxesOut = cSysOut.worldAxes(j); const Vector& pixelAxesOut = cSysOut.pixelAxes(j); for (uInt k=worldAxes.nelements(); k>0;) { k--; // if (worldAxes(k) == -1) { cSysOut.removeWorldAxis(worldAxesOut(k), refVal(k)); // Assumes worldAxes in ascending order } } for (uInt k=worldAxes.nelements(); k>0;) { k--; // if (pixelAxes(k) == -1) { cSysOut.removePixelAxis(pixelAxesOut(k), refPix(k)); // Assumes pixelAxes in ascending order } } // j++; } } // return cSysOut; } /* Bool CoordinateSystem::checkWorldReplacementAxis(Int& coordinate, Int& axisInCoordinate, uInt axis) const { // Find number of (unremoved) world axes const uInt nC = nCoordinates(); uInt nWorld = 0; for (uInt i=0; inelements(); } // Check axis if (axis >= nWorld) { ostringstream oss; oss << "Illegal removal world axis number (" << axis << "), max is (" << nWorld << ")" << endl; set_error (String(oss)); return False; } // Find coordinate and axis in coordinate coordinate = -1; axisInCoordinate = -1; Int value; for (uInt i=0; inelements(); for (uInt j=0; joperator[](j); // We stored -1 * (axis + 1) when we removed it if (value < 0) { value = -1 * (value + 1); if (value == Int(axis)) { coordinate = i; axisInCoordinate = j; break; } } } } // Check if (coordinate==-1 || axisInCoordinate==-1) { ostringstream oss; oss << "Cannot find world axis " << axis << endl; set_error (String(oss)); return False; } // return True; } Bool CoordinateSystem::checkPixelReplacementAxis(Int& coordinate, Int& axisInCoordinate, uInt axis) const { // Find number of (unremoved) pixel axes const uInt nC = nCoordinates(); uInt nPixel = 0; for (uInt i=0; inelements(); } // Check axis if (axis >= nPixel) { ostringstream oss; oss << "Illegal removal pixel axis number (" << axis << "), max is (" << nPixel << ")" << endl; set_error (String(oss)); return False; } // Find coordinate and axis in coordinate for removed axis coordinate = -1; axisInCoordinate = -1; Int value; for (uInt i=0; inelements(); for (uInt j=0; joperator[](j); // We stored -1 * (axis + 1) when we removed it if (value < 0) { value = -1 * (value + 1); if (value == Int(axis)) { coordinate = i; axisInCoordinate = j; break; } } } } // Check if (coordinate==-1 || axisInCoordinate==-1) { ostringstream oss; oss << "Cannot find removed pixel axis " << axis << endl; set_error (String(oss)); return False; } // return True; } */ Bool CoordinateSystem::mapOne(Vector& worldAxisMap, Vector& worldAxisTranspose, Vector& refChange, const CoordinateSystem& cSys1, const CoordinateSystem& cSys2, const uInt coord1, const uInt coord2) const // // Update the world axis mappings from one coordinate system to another. // { // Make tests on specific coordinate types here. We already // know that the two cSys are the same coordinate type // (e.g. DIRECTION) Bool refDiff = False; if (cSys2.coordinate(coord2).type() == Coordinate::DIRECTION) { if (cSys1.directionCoordinate(coord1).directionType() != cSys2.directionCoordinate(coord2).directionType()) { refDiff = True; } } else if (cSys2.coordinate(coord2).type() == Coordinate::SPECTRAL) { if (cSys1.spectralCoordinate(coord1).frequencySystem() != cSys2.spectralCoordinate(coord2).frequencySystem()) { refDiff = True; } } // How many world and pixel axes for these coordinates uInt nWorld1 = cSys1.worldAxes(coord1).nelements(); uInt nWorld2 = cSys2.worldAxes(coord2).nelements(); uInt nPixel1 = cSys1.pixelAxes(coord1).nelements(); uInt nPixel2 = cSys2.pixelAxes(coord2).nelements(); // These tests should never fail if (nWorld1 != nWorld2) return False; if (nPixel1 != nPixel2) return False; // Find their world and pixel axes Vector world1 = cSys1.worldAxes(coord1); Vector pixel1 = cSys1.pixelAxes(coord1); Vector world2 = cSys2.worldAxes(coord2); Vector pixel2 = cSys2.pixelAxes(coord2); const Vector& units1 = cSys1.coordinate(coord1).worldAxisUnits(); const Vector& units2 = cSys2.coordinate(coord2).worldAxisUnits(); // Compare quantities for the world axes. for (uInt j=0; j= 0 && spectralCoordNum < (Int)nCoordinates()); } Int CoordinateSystem::spectralCoordinateNumber() const { return findCoordinate(Coordinate::SPECTRAL); } Int CoordinateSystem::spectralAxisNumber(Bool doWorld) const { if (! hasSpectralAxis()) { return -1; } Int specIndex = findCoordinate(Coordinate::SPECTRAL); if (doWorld) { return worldAxes(specIndex)[0]; } return pixelAxes(specIndex)[0]; } Bool CoordinateSystem::hasPolarizationCoordinate() const { Int polarizationCoordNum = findCoordinate(Coordinate::STOKES); return ( polarizationCoordNum >= 0 && polarizationCoordNum < (Int)nCoordinates() ); } Int CoordinateSystem::polarizationCoordinateNumber() const { // don't do hasPolarizationAxis check or you will go down an infinite recursion path :) return findCoordinate(Coordinate::STOKES); } Int CoordinateSystem::polarizationAxisNumber(Bool doWorld) const { if (! hasPolarizationCoordinate()) { return -1; } if (doWorld) { return worldAxes(polarizationCoordinateNumber())[0]; } return pixelAxes(polarizationCoordinateNumber())[0]; } Bool CoordinateSystem::hasQualityAxis() const { Int qualityCoordNum = findCoordinate(Coordinate::QUALITY); return ( qualityCoordNum >= 0 && qualityCoordNum < (Int)nCoordinates() ); } Int CoordinateSystem::qualityAxisNumber() const { if (! hasQualityAxis()) { return -1; } return pixelAxes(qualityCoordinateNumber())[0]; } Int CoordinateSystem::qualityCoordinateNumber() const { // don't do hasQualityAxis check or you will go down an infinite recursion path :) return findCoordinate(Coordinate::QUALITY); } Int CoordinateSystem::qualityPixelNumber(const String& qualityString) const{ if (! hasQualityAxis()) { return -1; } Int qualCoordNum = findCoordinate(Coordinate::QUALITY); QualityCoordinate qualityCoord = qualityCoordinate(qualCoordNum); Int qualityPix = -1; qualityCoord.toPixel(qualityPix, Quality::type(qualityString)); if (qualityPix < 0) { return -1; } return qualityPix; } String CoordinateSystem::qualityAtPixel(const uInt pixel) const { if (! hasQualityAxis()) { return ""; } Int qualCoordNum = qualityCoordinateNumber(); QualityCoordinate qualityCoord = qualityCoordinate(qualCoordNum); Int quality = qualityCoord.quality()[pixel]; Quality::QualityTypes qualityType = Quality::type(quality); return Quality::name(qualityType); } Int CoordinateSystem::stokesPixelNumber(const String& stokesString) const { if (! hasPolarizationCoordinate()) { return -1; } Int polCoordNum = findCoordinate(Coordinate::STOKES); StokesCoordinate stokesCoord = stokesCoordinate(polCoordNum); Int stokesPix = -1; stokesCoord.toPixel(stokesPix, Stokes::type(stokesString)); if (stokesPix < 0) { return -1; } return stokesPix; } String CoordinateSystem::stokesAtPixel(const uInt pixel) const { if (! hasPolarizationCoordinate()) { return ""; } Int polCoordNum = polarizationCoordinateNumber(); StokesCoordinate stokesCoord = stokesCoordinate(polCoordNum); Int stokes = stokesCoord.stokes()[pixel]; Stokes::StokesTypes stokesType = Stokes::type(stokes); return Stokes::name(stokesType); } Int CoordinateSystem::directionCoordinateNumber() const { // don't do a hasDirectionCoordinate() check or you will go down an infinite recursion path return findCoordinate(Coordinate::DIRECTION); } Bool CoordinateSystem::hasDirectionCoordinate() const { Int directionCoordNum = directionCoordinateNumber(); return ( directionCoordNum >= 0 && directionCoordNum < (Int)nCoordinates() ); } Vector CoordinateSystem::directionAxesNumbers() const { if (! hasDirectionCoordinate()) { return Vector(); } return pixelAxes(directionCoordinateNumber()); } Int CoordinateSystem::linearCoordinateNumber() const { // don't do a hasLinearCoordinate() check or you will go down an infinite recursion path return findCoordinate(Coordinate::LINEAR); } Bool CoordinateSystem::hasLinearCoordinate() const { Int linearCoordNum = linearCoordinateNumber(); return ( linearCoordNum >= 0 && linearCoordNum < (Int)nCoordinates() ); } Vector CoordinateSystem::linearAxesNumbers() const { if (! hasLinearCoordinate()) { return Vector(); } return pixelAxes(linearCoordinateNumber()); } void CoordinateSystem::_initFriendlyAxisMap() { std::lock_guard lock(_mapInitMutex); if (_friendlyAxisMap.size() == 0) { _friendlyAxisMap["velocity"] = "spectral"; _friendlyAxisMap["frequency"] = "spectral"; _friendlyAxisMap["right ascension"] = "ra"; } } Vector CoordinateSystem::getWorldAxesOrder( Vector& myNames, Bool requireAll, Bool allowFriendlyNames ) const { LogIO os(LogOrigin(_class, __FUNCTION__, WHERE)); if (allowFriendlyNames) { _initFriendlyAxisMap(); } uInt naxes = nWorldAxes(); uInt raxes = myNames.size(); if (requireAll && raxes != naxes) { os << "Image has " << naxes << " axes but " << raxes << " were given. Number of given axes must match the number of image axes" << LogIO::EXCEPTION; } Vector axisNames = worldAxisNames(); _downcase(axisNames); _downcase(myNames); Vector myorder(raxes); Vector matchMap(naxes, ""); for (uInt i=0; i matchedNames(0); vector matchedNumbers(0); for (uInt j=0; j 1) { os << "Multiple axes " << matchedNames << " match requested axis " << name << LogIO::EXCEPTION; } uInt axisIndex = matchedNumbers[0]; if (matchMap[axisIndex].empty()) { myorder[i] = axisIndex; matchMap[axisIndex] = name; } else { os << "Ambiguous axis specification. Both " << matchMap[axisIndex] << " and " << name << " match image axis name " << matchedNames[0] << LogIO::EXCEPTION; } } return myorder; } Bool CoordinateSystem::isDirectionAbscissaLongitude() const { ThrowIf( ! hasDirectionCoordinate(), "Coordinate system has no direction coordinate" ); Vector dirPixelAxes = directionAxesNumbers(); ThrowIf( dirPixelAxes(0) == -1 || dirPixelAxes(1) == -1, "The pixel axes for the DirectionCoordinate have been removed" ); return dirPixelAxes(0) < dirPixelAxes(1); } void CoordinateSystem::setSpectralConversion ( const String frequencySystem ) { String err; ThrowIf( ! setSpectralConversion(err, frequencySystem), err ); } Bool CoordinateSystem::setSpectralConversion ( String& errorMsg, const String frequencySystem ) { if (! hasSpectralAxis()) { return True; } if (! hasDirectionCoordinate()) { errorMsg = String("No DirectionCoordinate; cannot set Spectral conversion layer"); return False; } MFrequency::Types ctype; if (!MFrequency::getType(ctype, frequencySystem)) { errorMsg = String("invalid frequency system " + frequencySystem); return False; } SpectralCoordinate coord = spectralCoordinate(); MFrequency::Types oldctype; MEpoch epoch; MPosition position; MDirection direction; coord.getReferenceConversion(oldctype, epoch, position, direction); if (ctype == oldctype) { return True; } const DirectionCoordinate& dCoord = directionCoordinate(); const Vector& rp = dCoord.referencePixel(); if (!dCoord.toWorld(direction, rp)) { errorMsg = dCoord.errorMessage(); return False; } const ObsInfo& oi = obsInfo(); String telescope = oi.telescope(); if (! MeasTable::Observatory(position, telescope)) { errorMsg = String("Cannot find observatory; cannot set Spectral conversion layer"); return False; } epoch = oi.obsDate(); Double t = epoch.getValue().get(); if (t <= 0.0) { errorMsg = String("Epoch not valid; cannot set Spectral conversion layer"); return False; } coord.setReferenceConversion(ctype, epoch, position, direction); replaceCoordinate(coord, this->spectralCoordinateNumber()); return True; } Bool CoordinateSystem::setRestFrequency ( String& errorMsg, const Quantity& freq ) { Double value = freq.getValue(); if (value < 0.0) { errorMsg = "The rest frequency/wavelength is below zero!"; return False; } else if (isNaN(value)) { errorMsg = "The rest frequency/wavelength is NaN!"; return False; } else if (isInf(value)) { errorMsg = "The rest frequency/wavelength is InF!"; return False; } static Unit HZ(String("GHz")); static Unit M(String("m")); Unit t(freq.getUnit()); if (t != HZ && t!= M) { errorMsg = "Illegal spectral unit " + freq.getUnit(); return False; } if (! hasSpectralAxis()) { return True; } SpectralCoordinate sCoord = spectralCoordinate(); Unit oldUnit(sCoord.worldAxisUnits()[0]); MVFrequency newFreq = MVFrequency(freq); Double newValue = newFreq.get(oldUnit).getValue(); if (isNaN(newValue)) { errorMsg = "The new rest frequency/wavelength is NaN!"; return False; } else if (isInf(newValue)) { errorMsg = "The new rest frequency/wavelength is InF!"; return False; } if (!sCoord.setRestFrequency(newValue)) { errorMsg = sCoord.errorMessage(); return False; } this->replaceCoordinate(sCoord, spectralCoordinateNumber()); return True; } } //# NAMESPACE CASACORE - END casacore-3.7.1/coordinates/Coordinates/CoordinateSystem.h000066400000000000000000001344571476623553700235730ustar00rootroot00000000000000//# CoordinateSystem.h: Interconvert pixel and image coordinates. //# Copyright (C) 1997,1998,1999,2000,2001,2002,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef COORDINATES_COORDINATESYSTEM_H #define COORDINATES_COORDINATESYSTEM_H #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class DirectionCoordinate; class LinearCoordinate; class SpectralCoordinate; class StokesCoordinate; class QualityCoordinate; class TabularCoordinate; class IPosition; class LogIO; // // Interconvert pixel and world coordinates. // // // // // // //
      • Coordinate // // // CoordinateSystem is the normal interface to coordinate systems, // typically attached to an // ImageInterface, however the // coordinate system can be manipulated on its own. CoordinateSystem // is in turn composed from various classes derived from the base class // Coordinate. //

        // The fundamental operations available to the user of a // CoordinateSystem are: //

          //
        1. Transform a world (physical) coordinate to a pixel coordinate // or vice versa via the methods toWorld and toPixel. //
        2. Compose a CoordinateSystem from one or more independent groups, // typically the sky-plane transformation will be one group, and the // spectral axis will be another group. Each group consists of a linear // transformation (in FITS terms, apply CRPIX, PC, CDELT) // to turn the pixel coordinates into relative world coordinates, // followed by a (possibly) nonlinear projection to world coordinates // (i.e. apply CTYPE and CRVAL), typically a sky projection // or a frequency to velocity conversion. Note that an arbitrary rotation // or linear transformation can be applied by changing the // matrix. //
        3. Transpose the world and/or pixel axes. //
        4. One or more pixel or world axes may be removed. You are encouraged to // leave all the world axes if you remove a pixel axis. // Removing a world axis also removes the corresponding pixel axis. //
        5. Calculate the CoordinateSystem that results from a subimage // operation. //
        // // Note that all the knowledge to do with removing and transposing axes is // maintained by the CoordinateSystem. The individual Coordinates, of which it // is made, know nothing about this. //

        // Although the CoordinateSystem exists in the absence of an image, the usual // place you will find one is attached to an object derived from ImageInterface // such as PagedImage. When you do so, the physical (or pixel) axes in the image // map one to one with the pixel axes contained in the CoordinateSystem. // It cannot be any other way as when you create a PagedImage, it is checked // that there are equal numbers of image and CoordinateSystem pixel axes. // It is up to the creator of the PagedImage to make sure that they are // in the correct order. //

        // However, the CoordinateSystem may have more world axes than pixel axes // because it is possible to remove a pixel axis but not its associated // world axis (for example for a moment image). Now, if you use // the CoordinateSystem functions // referencePixel and referenceValue, you will find the vector of reference // values will have more values than the vector of reference pixels, // if a pixel axis has been removed but not the world axis. You // must use the ancilliary functions provided // to find out what is where. //

        // Let's consider an example where a CoordinateSystem consisted of // a DirectionCoordinate and a SpectralCoordinate. Let us say that // the first two pixel axes of the image associate (roughly of course // because lines of constant RA and DEC are not parallel with // the pixel coordinates) with the DirectionCoordinate (RA and DEC say) // and the third pixel axis is the SpectralCoordinate. // Now imagine we collapse the image along the second pixel axis (roughly, // the DEC axis). For the output image, we remove the second pixel axis // from the CoordinateSystem, but leave the world axis intact. This enables // us to still be able to make coordinate conversions for the first (roughly RA) // pixel axis. Thus, CoordinateSystem::referenceValue would return a Vector of // length 3 (for RA, DEC and spectral), but CoordinateSystem::referencePixel // would return a vector length 2 (for RA and spectral). //

        // Now this CoordinateSystem has two Coordinates, a DirectionCoordinate and // a SpectralCoordinate, and let us state that that is the order in which // they exist in the CoordinateSystem (you can change them about if you wish); // they are coordinates number 0 and 1. The DirectionCoordinate has two axes // (RA and DEC) and the SpectralCoordinate has one axis. Only the // CoordinateSystem knows about removed axes, the DirectionCoordinate // itself is ignorant that it has been bisected. If you want to find // out what axis in the Coordinate system is where, you can use // the functions findPixelAxis or findWorldAxis. // // If we asked the former to find pixel axis 0, it would tell us that the // Coordinate number was 0 (the DirectionCoordinate) and that the axis in // that coordinate was 0 (the first axis in a DirectionCoordinate // is always longitude, the second always latitude). If we asked it to find // pixel axis 1, it would tell us that the coordinate number was 1 // (the SpectralCoordinate) and that the axis in that coordinate was 0 // (there is only one axis in a SpectralCoordinate). If we asked for // pixelAxis 2 that would generate an error because our squashed image // only has 2 pixel axes. // // Now, if we asked findWorldAxis similar questions, // it would tell us that worldAxis 0 in the CoordinateSystem can be found in // coordinate 0 (the DirectionCoordinate) in axis 0 of that DirectionCoordinate. // Similarly, worldAxis 1 in the CoordinateSystem (which has not been removed) // is in coordinate 0 (the DirectionCoordinate) in axis 1 of that // Finally, worldAxis 2 in the CoordinateSystem is in coordinate 1 // (the SpectralCoordinate) in axis 0 of that SpectralCoordinate. //

        // Other handy functions are pixelAxes and worldAxes. // These list the pixel and world axes in // the CoordinateSystem for the specified coordinate. Thus, if we asked // pixelAxes to find the pixel axes for coordinate 0 (the DirectionCoordinate) // in the CoordinateSystem it would return a vector [0, -1] indicating // the second axis of the DirectionCoordinate has been removed. However, // the worldAxes function would return [0,1] as no world axis has been removed. // Similarly, if operated on coordinate 1 (the SpectralCoordinate), pixelAxes // would return [1] and worldAxes would return [2]. // // Because you can transpose the CoordinateSystem about, you should NEVER ASSUME // ANYTHING except that the pixel axes of the CoordinateSystem map to the pixel // axes of the image when you first construct the image. // //

        // SpectralCoordinate and DirectionCoordinate both have a (non-virtual) function // called setReferenceConversion. This enables an extra conversion // layer so that conversion between pixel and world can go to a reference frame // other than the construction reference. When you use the function // convert, these layers are active, but ONLY if the // requested conversion is purely between pixel and world. For // a SpectralCoordinate this must always be true (only has one axis) // but for the DirectionCoordinate you might request a mixed // pixel/world conversion. In this case, the extra conversion layer // is ill-defined and not active (for the DirectionCoordinate part of it). // // // All pixels coordinates are zero relative. // // // See the example in Coordinates.h // and tCoordinateSystem.cc // // // Coordinate systems for images. // // // //

      • AipsError // // // //
      • Undelete individual removed axes. //
      • Non-integral pixel shifts/decimations in subimage operations? //
      • Copy-on-write for efficiency? //
      • Check if the classes are thread safe in general // // class CoordinateSystem : public Coordinate { public: // Default constructor. This is an empty CoordinateSystem. CoordinateSystem(); // Copying constructor (copy semantics) CoordinateSystem(const CoordinateSystem &other); // Assignment (copy semantics). CoordinateSystem &operator=(const CoordinateSystem &other); // Destructor virtual ~CoordinateSystem(); // Add another Coordinate to this CoordinateSystem. This addition is done // by copying, so that if coord changes the change is NOT // reflected in the CoordinateSystem. void addCoordinate(const Coordinate &coord); // Transpose the CoordinateSystem so that world axis 0 is // newWorldOrder(0) and so on for all the other axes. // newPixelOrder works similarly. Normally you will give the // same transformation vector for both the world and pixel transformations, // however this is not required. void transpose(const Vector &newWorldOrder, const Vector &newPixelOrder); // Find the world and pixel axis mappings to the supplied CoordinateSystem // from the current coordinate system. False is // returned if either the supplied or current coordinate system, // has no world axes (and a message recoverable with function // errorMessage indicating why). Otherwise True is returned. // worldAxisMap(i) is the location of world axis i (from the // supplied CoordinateSystem, cSys, in the current CoordinateSystem. // worldAxisTranspose(i) is the location of world axis // i (from the current CoordinateSystem) in the supplied // CoordinateSystem, cSys. The output vectors // are resized appropriately by this function. A value of -1 // in either vector means that the axis could not be found in the other // CoordinateSystem. The vector refChange says // if the types are the same, is there a reference type change // (e.g. TOPO versus LSR for the SpectralCoordinate, // or J2000 versus GALACTIC for DirectionCoordinate). Thus // if refChange(i) is True, it means world axis i in the // current CoordinateSystem was matched, but has a different // reference type to that of the supplied CoordinateSystem. // Bool worldMap (Vector& worldAxisMap, Vector& worldAxisTranspose, Vector& refChange, const CoordinateSystem& cSys) const; Bool pixelMap (Vector& pixelAxisMap, Vector& pixelAxisTranspose, const CoordinateSystem& cSys) const; // // Remove a world or pixel axis. When its value is required for forward or // backwards transformations, use replacement //
        // When a world axis is removed, the corresponding pixel axis is removed // too, because it makes no sense having a pixel axis without world // coordinates. //
        // Removing a pixel axis without removing the corresponding world axis // is, however, possible and meaningful. It can be used when e.g. a // frequency plane is taken from a cube. The plane has 2 pixel axes, but // the 3rd world axis can still describe the frequency coordinate. // See also the functions in CoordinateUtil // for removing lists of pixel/world axes (tricky because they shift down) // // False is returned (an error in errorMessage() will be set) // if the axis is illegal, else returns True. // Bool removeWorldAxis(uInt axis, Double replacement); Bool removePixelAxis(uInt axis, Double replacement); // // Return a CoordinateSystem appropriate for a shift of origin // (the shift is subtracted from the reference pixel) // and change of increment (the increments are multipled // by the factor). Both vectors should be of length nPixelAxes(). // // The newShape vector is only needed for the StokesCoordinate, // if any. If this vector is of length zero, the new StokesCoordinate // is formed from all of the available input Stokes after application // of the shift and increment factor. Otherwise, // the new Stokes axis length is equal to that specified after // appliction of the shift and increment and excess values // discarded. In addition, for any StokesCoordinate, the // shift and factor must be integer. So Int(value+0.5) // is taken before they are used. // CoordinateSystem subImage(const Vector &originShift, const Vector &incrFac, const Vector& newShape) const; void subImageInSitu (const Vector &originShift, const Vector &incrFac, const Vector& newShape); // // Untranspose and undelete all axes. Does not undo the effects of // subimaging. void restoreOriginal(); // Returns the number of Coordinates that this CoordinateSystem contains. // The order might be unrelated to the axis order through the results of // transposing and removing axes. uInt nCoordinates() const; // For a given Coordinate say where its world and pixel axes are in // this CoordinateSystem. The position in the returned Vector is its // axis number in the Coordinate, and its value is the axis // number in the CoordinateSystem. If the value is less than zero the axis // has been removed from this CoordinateSystem. // Vector worldAxes(uInt whichCoord) const; Vector pixelAxes(uInt whichCoord) const; // // Return the type of the given Coordinate. Coordinate::Type type(uInt whichCoordinate) const; // Returns the type of the given Coordinate as a string. String showType(uInt whichCoordinate) const; // Return the given Coordinate as a reference to the base // class object. const Coordinate& coordinate(uInt which) const; // Return the given Coordinate. // Throws an exception if retrieved as the wrong type. // The versions which take no parameters will return the // first (or in most cases only) coordinate of the requested type. // If no such coordinate exists, an exception is thrown. // const LinearCoordinate &linearCoordinate(uInt which) const; const DirectionCoordinate &directionCoordinate() const; const DirectionCoordinate &directionCoordinate(uInt which) const; const SpectralCoordinate &spectralCoordinate(uInt which) const; const SpectralCoordinate &spectralCoordinate() const; const StokesCoordinate &stokesCoordinate() const; const StokesCoordinate &stokesCoordinate(uInt which) const; const QualityCoordinate &qualityCoordinate(uInt which) const; const TabularCoordinate &tabularCoordinate(uInt which) const; // // Replace one Coordinate with another. The mapping of the coordinate axes // to the CoordinateSystem axes is unchanged, therefore the number of world // and pixel axes must not be changed. You can, somewhat dangerously, // change the type of the coordinate however. For example, replace a // SpectralCoordinate with a 1-D Linearcoordinate. It is dangerous because // the world replacement values (see removeWorldAxis) have to be scaled. // The algorithm tries to find a scale factor between the old and new // units and applies it to the replacement values. If it can't find // a scale factor (non-conformant units) then the reference value is // used for any world replacement values. If the latter occurs, // it returns False, else True is returned. Bool replaceCoordinate(const Coordinate &newCoordinate, uInt whichCoordinate); // Find the Coordinate number that corresponds to the given type. // Since there might be more than one Coordinate of a given type you // can call this multiple times setting afterCoord to // the last value found. Returns -1 if a Coordinate of the desired // type is not found. Int findCoordinate(Coordinate::Type type, Int afterCoord = -1) const; // Given an axis number (pixel or world) in the CoordinateSystem, // find the corresponding coordinate number and axis in that Coordinate. // The returned values are set to -1 if the axis does not exist. // void findWorldAxis(Int &coordinate, Int &axisInCoordinate, uInt axisInCoordinateSystem) const; void findPixelAxis(Int &coordinate, Int &axisInCoordinate, uInt axisInCoordinateSystem) const; // // Find the world axis for the given pixel axis in a CoordinateSystem. // Returns -1 if the world axis is unavailable (e.g. if it has been // removed). Int pixelAxisToWorldAxis(uInt pixelAxis) const; // Find the pixel axis for the given world axis in a CoordinateSystem. // Returns -1 if the pixel axis is unavailable (e.g. if it has been // removed). Int worldAxisToPixelAxis(uInt worldAxis) const; // Return the name of the record field in which the coordinate is stored. String coordRecordName(uInt which) const; // Returns Coordinate::COORDSYS virtual Coordinate::Type type() const; // Always returns "System" virtual String showType() const; // Sums the number of axes in the Coordinates that the CoordinateSystem // contains, allowing for removed axes. // virtual uInt nPixelAxes() const; virtual uInt nWorldAxes() const; // // Convert a pixel position to a world position or vice versa. Returns True // if the conversion succeeds, otherwise it returns False and // errorMessage() contains an error message. // The input vector must be of length nPixelAxes or // nWorldAxes. The output vector is resized appropriately. // if useConversionFrame, if the coordinate has a conversion layer frame // (such as can be present in spectral and direction coordinates), it // is used. Else, the native frame is used for the conversion. // virtual Bool toWorld(Vector &world, const Vector &pixel, Bool useConversionFrame=True) const; // This one throws an exception rather than returning False. After all, that's // what exceptions are for. virtual Vector toWorld(const Vector &pixel) const; virtual Bool toPixel(Vector &pixel, const Vector &world) const; // This one throws an exception rather than returning False. virtual Vector toPixel(const Vector &world) const; // // convert a pixel "length" to a world "length" virtual Quantity toWorldLength( const Double nPixels, const uInt pixelAxis ) const; // This is provided as a convenience since it is a very commonly desired // operation through CoordinateSystem. The output vector is resized. Bool toWorld(Vector &world, const IPosition &pixel) const; Vector toWorld(const IPosition& pixel) const; // Batch up a lot of transformations. The first (most rapidly varying) axis // of the matrices contain the coordinates. Returns False if any conversion // failed and errorMessage() will hold a message. // The failures array (True for fail, False for success) // is the length of the number of conversions and // holds an error status for each conversion. // virtual Bool toWorldMany(Matrix& world, const Matrix& pixel, Vector& failures) const; virtual Bool toPixelMany(Matrix& pixel, const Matrix& world, Vector& failures) const; // // Mixed pixel/world coordinate conversion. // worldIn and worldAxes are of length nworldAxes. // pixelIn and pixelAxes are of length nPixelAxes. // worldAxes(i)=True specifies you have given a world // value in worldIn(i) to convert to pixel. // pixelAxes(i)=True specifies you have given a pixel // value in pixelIn(i) to convert to world. // You cannot specify the same axis via worldAxes // and pixelAxes. // Values in pixelIn are converted to world and // put into worldOut in the appropriate world axis // location. Values in worldIn are copied to // worldOut. // Values in worldIn are converted to pixel and // put into pixelOut in the appropriate pixel axis // location. Values in pixelIn are copied to // pixelOut. Vectors // worldMin and worldMax specify the range of the world // coordinate (in the world axis units of that world axis // in the coordinate system) being solved for in a mixed calculation // for each world axis. They are only actually used for DirectionCoordinates // and for all other coordinates the relevant elements are ignored. // Functions setWorldMixRanges, worldMixMin, worldMixMax can be // used to compute and recover the world ranges. If you don't know // the values, use functions setDefaultWorldMixRanges, worldMixMin, worldMixMax. // Removed axes are handled (for example, a removed pixel // axis with remaining corresponding world axis will // correctly be converted to world using the replacement // value). // Returns True if the conversion succeeds, otherwise it returns False and // errorMessage() contains an error message. The output vectors // are resized. virtual Bool toMix(Vector& worldOut, Vector& pixelOut, const Vector& worldIn, const Vector& pixelIn, const Vector& worldAxes, const Vector& pixelAxes, const Vector& worldMin, const Vector& worldMax) const; // Compute and recover the world min and max ranges, for use in function toMix, // for a lattice of the given shape (must be of length nPixelAxes()). // Removed pixel axes (with remaining world axes are handled). With // the retrieval functions, the output vectors are resized. They return // False if they fail (and then setDefaultWorldMixRanges generates the ranges) // with a reason in errorMessage(). // The setDefaultWorldMixRanges function // gives you a useful default range if you don't know the shape. // The only Coordinate type for which these ranges are actually // used in toMix is DirectionCoordinate (because its coupled). For // the rest the functionality is provided but never used // by toMix. // virtual Bool setWorldMixRanges (const IPosition& shape); virtual void setDefaultWorldMixRanges (); virtual Vector worldMixMin () const; virtual Vector worldMixMax () const; // // Make absolute coordinates relative and vice-versa (relative // to the reference pixel/value). The vectors must be of length // nPixelAxes() or nWorldAxes() // virtual void makePixelRelative (Vector& pixel) const; virtual void makePixelAbsolute (Vector& pixel) const; virtual void makeWorldRelative (Vector& world) const; virtual void makeWorldAbsolute (Vector& world) const; // // Make absolute coordinates relative and vice versa with respect // to the given reference value. Add the other functions in this grouping // as needed. The vectors must be of length // nPixelAxes() or nWorldAxes() // virtual void makeWorldAbsoluteRef (Vector& world, const Vector& refVal) const; // // Batch up a lot of absolute/relative transformations. // Parameters as above for // toWorldMany and toPixelMany // virtual void makePixelRelativeMany (Matrix& pixel) const; virtual void makePixelAbsoluteMany (Matrix& pixel) const; virtual void makeWorldRelativeMany (Matrix& world) const; virtual void makeWorldAbsoluteMany (Matrix& world) const; // // General coordinate conversion. Only works if no axes // have been removed and no axis reordering has occurred. // That is pixel axes and world axes are the same. // // Specify the input coordinate values, input units, // whether value is absolute (or relative). For output // specify units and abs/rel. Units may be 'pix' and velocity consistent // units (e.g. m/s). Specify doppler types if velocities // involved. The pixel offsets allow for the input // and output pixel coordinates to be something other than 0-rel. // If your pixel coordinates are 1-rel input and output, set the // offsets to -1 and 1 // // The Matrix interface lets you do many conversions efficiently. // Use Matrix(nAxes, nConversions) and // Matrix.column()=coordinate or // Matrix(axis, iConversion) to get the order right. // // These functions invoke toMix // so make sure you call setWorldMixRanges // first to set up the world ranges. // Bool convert (Vector& coordOut, const Vector& coordin, const Vector& absIn, const Vector& unitsIn, MDoppler::Types dopplerIn, const Vector& absOut, const Vector& unitsOut, MDoppler::Types dopplerOut, Double pixInOffset = 0.0, Double pixOutOffset = 0.0); Bool convert (Matrix& coordOut, const Matrix& coordIn, const Vector& absIn, const Vector& unitsIn, MDoppler::Types dopplerIn, const Vector& absOut, const Vector& unitsOut, MDoppler::Types dopplerOut, Double pixInOffset = 0.0, Double pixOutOffset = 0.0); // // Return the requested attribute. // virtual Vector worldAxisNames() const; virtual Vector referencePixel() const; virtual Matrix linearTransform() const; virtual Vector increment() const; virtual Vector referenceValue() const; // // Set the requested attribute. Note that these just // change the internal values, they do not cause any recomputation. // virtual Bool setWorldAxisNames(const Vector &names); virtual Bool setReferencePixel(const Vector &refPix); virtual Bool setLinearTransform(const Matrix &xform); virtual Bool setIncrement(const Vector &inc); virtual Bool setReferenceValue(const Vector &refval); // // Set/get the units. Adjust the increment and // reference value by the ratio of the old and new units. This implies that // the units must be known Unit strings, and // that they must be compatible, e.g. they can't change from time to // length. If throwException=True, throw an exception rather than // returning False on failure. // virtual Bool setWorldAxisUnits(const Vector &units); Bool setWorldAxisUnits(const Vector &units, Bool throwException); virtual Vector worldAxisUnits() const; // // Comparison function. Any private Double data members are compared // with the specified fractional tolerance. Don't compare on the specified // pixel axes in the CoordinateSystem. If the comparison returns // False, errorMessage() contains a message about why. // virtual Bool near(const Coordinate& other, Double tol=1e-6) const; virtual Bool near(const Coordinate& other, const Vector& excludePixelAxes, Double tol=1e-6) const; // // This function compares this and the other coordinate system, // but ONLY for the non-removed pixel axes. It is less strict // than near, which, for example, insists the number of coordinates // is the same in each CS Bool nearPixel (const CoordinateSystem& other, Double tol=1e-6) const; // Format a world value nicely through the // common format interface. See Coordinate // for basics. // // You specify a world value and its corresponding world axis in // the CoordinateSystem. // // For the specified worldAxis, the coordinate // number in the CoordinateSystem is found and the actual derived Coordinate // class object for that number is created. The arguments to the formatting // function are then passed on to the formatter for that Coordinate. So // refer to the other derived Coordinate classes for specifics on the // formatting. virtual String format( String& units, Coordinate::formatType format, Double worldValue, uInt worldAxis, Bool isAbsolute=True, Bool showAsAbsolute=True, Int precision=-1, Bool usePrecForMixed=False ) const; // Miscellaneous information related to an observation, for example the // observation date. // ObsInfo obsInfo() const; void setObsInfo(const ObsInfo &obsinfo); // // Find the CoordinateSystem (you can safely caste the pointer to a CoordinateSystem) // for when we Fourier Transform ourselves. This pointer // must be deleted by the caller. Axes specifies which pixel axes of the Coordinate // System you wish to transform. Shape specifies the shape of the image // associated with all the axes of the CoordinateSystem. Currently you have // no control over the reference pixel, it is always shape/2. virtual Coordinate* makeFourierCoordinate (const Vector& axes, const Vector& shape) const; // Save the CoordinateSystem into the supplied record using the supplied field name. // The field must not exist, otherwise False is returned. // If the CoordinateSystem is empty False is also returned. // If False is returned, errorMessage() contains a message about why. virtual Bool save(RecordInterface &container, const String &fieldName) const; // Restore the CoordinateSystem from a record. The fieldName // can be empty, in which case the CoordinateSystem is restored // directly from the Record, rather than a subrecord of it. // A null pointer means that the restoration did not succeed - probably // because fieldName doesn't exist or doesn't contain a CoordinateSystem. static CoordinateSystem *restore(const RecordInterface &container, const String &fieldName); // Make a copy of the CoordinateSystem using new. The caller is responsible for calling // delete. virtual Coordinate* clone() const; // Convert a CoordinateSystem to FITS, i.e. fill in ctype etc. In the record // the keywords are vectors, it is expected that the actual FITS code will // split them into scalars and upcase the names. Returns False if one of the // keywords is already taken. // // If writeWCS is True, attempt to write the WCS convention (Greisen and // Calabretta "Representation of celestial coordinates in FITS"). // Use oneRelative=True to convert zero-relative pixel coordinates to // one-relative FITS coordinates. // // prefix gives the prefix for the FITS keywords. E.g., // if prefix="c" then crval, cdelt etc. // if prefix="d" then drval, ddelt etc. //# Much of the work in to/from fits should be moved to the individual //# classes. Bool toFITSHeader(RecordInterface &header, IPosition &shape, Bool oneRelative, Char prefix = 'c', Bool writeWCS=True, Bool preferVelocity=True, Bool opticalVelocity=True, Bool preferWavelength=False, Bool airWavelength=False) const; // Probably even if we return False we should set up the best linear // coordinate that we can. // Use oneRelative=True to convert one-relative FITS pixel coordinates to // zero-relative Casacore coordinates. // On output, stokesFITSValue // holds the FITS value of any unofficial Stokes (beam, optical depth, // spectral index) for the last unofficial value accessed (-1 if none). // The idea is that if the Stokes axis is of length one and holds an // unofficial value, you should drop the STokes axis and convert that // value to ImageInfo::ImageTypes with // ImageInfo::imageTypeFromFITSValue. // If on input, stokesFITSValue is positive, then a warning // is issued if any unofficial values are encountered. // Otherwise no warning is issued. //# cf comment in toFITS. static Bool fromFITSHeader(Int& stokesFITSValue, CoordinateSystem &coordsys, RecordInterface& recHeader, const Vector& header, const IPosition& shape, uInt which=0); // List all header information. By default, the reference // values and pixel increments are converted to a "nice" unit before // formatting (e.g. RA is shown as HH:MM:SS.S). // For spectral axes, both frequency and velocity information is listed. You // can specify what velocity definition you want with velocityType // If you wish, you can specify two shapes; a lattice and tile shape // (perhaps an image from which the CoordinateSystem came) // If you give (both of) these, they are included in the listing. If you pass // in zero length IPositions then they are not included in // the listing. If postlocally=True the formatted summary lines // are written locally only to the sink, and then returned by the return value // vector. Vector list(LogIO& os, MDoppler::Types doppler, const IPosition& latticeShape, const IPosition& tileShape, Bool postLocally=False) const; // Does this coordinate system have a spectral axis? Bool hasSpectralAxis() const; // What number is the spectral axis? // If doWorld=True, the world axis number is returned. // Otherwise, the pixel axis number is returned. // Returns -1 if the spectral axis (world c.q. pixel) does not exist. Int spectralAxisNumber(Bool doWorld=False) const; // what number is the spectral coordinate? // Returns -1 if no spectral coordinate exists. Int spectralCoordinateNumber() const; // does this coordinate system have a polarizaion/stokes coordinate? Bool hasPolarizationCoordinate() const; Bool hasPolarizationAxis() const { return hasPolarizationCoordinate(); } // Given a stokes or polarization parameter, find the pixel location. // Note the client is responsible for any boundedness checks // (eg finite number of stokes in an image). Int stokesPixelNumber(const String& stokesString) const; // what is the number of the polarization/stokes coordinate? // Returns -1 if no stokes coordinate exists. Int polarizationCoordinateNumber() const; // What is the number of the polarization/stokes axis? // If doWorld=True, the world axis number is returned. // Otherwise, the pixel axis number is returned. // Returns -1 if the stokes axis (world c.q. pixel) does not exist. Int polarizationAxisNumber(Bool doWorld=False) const; // Does this coordinate system have a quality axis? Bool hasQualityAxis() const; // what number is the quality axis? Returns -1 if no quality axis exists. Int qualityAxisNumber() const; // what is the number of the quality coordinate? // Returns -1 if no quality coordinate exists. Int qualityCoordinateNumber() const; // Given a quality parameter, find the pixel location. // Note the client is responsible for any boundedness checks // (eg finite number of quality in an image). Int qualityPixelNumber(const String& qualityString) const; String qualityAtPixel(const uInt pixel) const; Int directionCoordinateNumber() const; Bool hasDirectionCoordinate() const; // Get the pixel axis numbers of the direction coordinate in this object. // The order of the returned axis numbers is always longitude axis first, // latitude axis second. Vector directionAxesNumbers() const; String stokesAtPixel(const uInt pixel) const; Int linearCoordinateNumber() const; Bool hasLinearCoordinate() const; Vector linearAxesNumbers() const; // Get the 0 based order of the minimal match strings specified in order. // If requireAll is True, checks are done to ensure that all axes in // the coordinate system are uniquely specified in order. // If allowFriendlyNames is True, the following (fully specified) strings // will match the specified axes: // "spectral" matches both "frequency" and "velocity". // "ra" matches "right ascension". Vector getWorldAxesOrder(Vector& myNames, Bool requireAll, Bool allowFriendlyNames=False) const; // Is the abscissa in the DirectionCoordinate the longitude axis? // Throws exception if there is no DirectionCoordinate or if either of // the direction pixel axes have been removed. // For a normal direction coordinate, this will return True. Bool isDirectionAbscissaLongitude() const; // Set Spectral conversion layer of SpectralCoordinate in CoordinateSystem // so that pixel<->world go to the specified frequency system (a valid // MFrequency::Types string). Returns False if frequency system invalid // or if no DirectionCoordinate or if cant get Date/Epoch. // Bool setSpectralConversion (String& errorMsg, const String frequencySystem); // This version throws an exception rather than returning False. void setSpectralConversion (const String frequencySystem); // // Set rest frequency of SpectralCoordinate in CoordinateSystem. // Unit must be consistent with Hz or m. // Returns False if invalid inputs (and CS not changed) and an error message. Bool setRestFrequency (String& errorMsg, const Quantity& freq); private: // Where we store copies of the coordinates we are created with. PtrBlock coordinates_p; // For coordinate[i] axis[j], // world_maps_p[i][j], if >=0 gives the location in the // input vector that maps to this coord/axis, // <0 means that the axis has been removed // world_tmp_p[i] a temporary vector length coord[i]->nworldAxes() // replacement_values_p[i][j] value to use for this axis if removed PtrBlock *> world_maps_p; PtrBlock *> world_tmps_p; PtrBlock *> world_replacement_values_p; // Same meanings as for the world*'s above. PtrBlock *> pixel_maps_p; PtrBlock *> pixel_tmps_p; PtrBlock *> pixel_replacement_values_p; // These temporaries all needed for the toMix function PtrBlock *> worldAxes_tmps_p; PtrBlock *> pixelAxes_tmps_p; PtrBlock *> worldOut_tmps_p; PtrBlock *> pixelOut_tmps_p; PtrBlock *> worldMin_tmps_p; PtrBlock *> worldMax_tmps_p; // Miscellaneous information about the observation associated with this // Coordinate System. ObsInfo obsinfo_p; const static String _class; static std::mutex _mapInitMutex; static std::map _friendlyAxisMap; static void _initFriendlyAxisMap(); // Helper functions to group common code. Bool mapOne(Vector& worldAxisMap, Vector& worldAxisTranspose, Vector& refChange, const CoordinateSystem& cSys, const CoordinateSystem& cSys2, const uInt coord, const uInt coord2) const; void copy(const CoordinateSystem &other); void clear(); Bool checkAxesInThisCoordinate(const Vector& axes, uInt which) const; // Delete some pointer blocks void cleanUpSpecCoord (PtrBlock& in, PtrBlock& out); // Delete temporary maps void deleteTemps (const uInt which); // Many abs/rel conversions // void makeWorldAbsRelMany (Matrix& value, Bool toAbs) const; void makePixelAbsRelMany (Matrix& value, Bool toAbs) const; // // Do subImage for Stokes StokesCoordinate stokesSubImage(const StokesCoordinate& sc, Int originShift, Int pixincFac, Int newShape) const; // Do subImage for Quality QualityCoordinate qualitySubImage(const QualityCoordinate& qc, Int originShift, Int pixincFac, Int newShape) const; // Strip out coordinates with all world and pixel axes removed CoordinateSystem stripRemovedAxes (const CoordinateSystem& cSys) const; // All these functions are in support of the list function // void listDirectionSystem(LogIO& os) const; void listFrequencySystem(LogIO& os, MDoppler::Types velocityType) const; void listPointingCenter (LogIO& os) const; void getFieldWidths (LogIO& os, uInt& widthAxis, uInt& widthCoordType, uInt& widthCoordNumber, uInt& widthName, uInt& widthProj, uInt& widthShape, uInt& widthTile, uInt& widthRefValue, uInt& widthRefPixel, uInt& widthInc, uInt& widthUnits, Int& precRefValSci, Int& precRefValFloat, Int& precRefValRADEC, Int& precRefPixFloat, Int& precIncSci, String& nameAxis, String& nameCoordType, String& nameCoordNumber, String& nameName, String& nameProj, String& nameShape, String& nameTile, String& nameRefValue, String& nameRefPixel, String& nameInc, String& nameUnits, MDoppler::Types velocityType, const IPosition& latticeShape, const IPosition& tileShape) const; void listHeader (LogIO& os, Coordinate* pc, uInt& widthAxis, uInt& widthCoordType, uInt& widthCoordNumber, uInt& widthName, uInt& widthProj, uInt& widthShape, uInt& widthTile, uInt& widthRefValue, uInt& widthRefPixel, uInt& widthInc, uInt& widthUnits, Bool findWidths, Int coordinate, Int axisInCoordinate, Int pixelAxis, Int precRefValSci, Int precRefValFloat, Int precRefValRADEC, Int precRefPixFloat, Int precIncSci, const IPosition& latticeShape, const IPosition& tileShape) const; void listVelocity (LogIO& os, Coordinate* pc, uInt widthAxis, uInt widthCoordType, uInt widthCoordNumber, uInt& widthName, uInt widthProj, uInt widthShape, uInt widthTile, uInt& widthRefValue, uInt widthRefPixel, uInt& widthInc, uInt& widthUnits, Bool findWidths, Int axisInCoordinate, Int pixelAxis, MDoppler::Types velocityType, Int precRefValSci, Int precRefValFloat, Int precRefValRADEC, Int precRefPixFloat, Int precIncSci) const; void clearFlags (LogIO& os) const; Bool velocityIncrement(Double& velocityInc, SpectralCoordinate& sc, MDoppler::Types velocityType, const String& velUnits) const; // void _downcase(Vector& vec) const { for (uInt i=0; i #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { // begin namespace casa void CoordinateUtil::addDirAxes(CoordinateSystem & coords){ Matrix xform(2, 2); xform = 0.0; xform.diagonal() = 1.0; DirectionCoordinate dirAxes(MDirection::J2000, Projection(Projection::SIN), 0.0, 0.0, // Ref is at RA = 0, Dec = 0 1.0, 1.0, // The increment is overwritten below xform, // Rotation matrix 0.0, 0.0, // Ref pixel is 0,0 999.0, 999.0); // reset the increment to 1 minute of arc on both axes Vector units(2); units = String("'"); Vector inc(2); inc(0) = -1.0; inc(1) = 1.0; dirAxes.setWorldAxisUnits(units); AlwaysAssert(dirAxes.setIncrement(inc) == True, AipsError); // Add the direction coordinates to the system. coords.addCoordinate(dirAxes); } void CoordinateUtil::addIQUVAxis(CoordinateSystem & coords){ Vector pols(4); pols(0) = Stokes::I; pols(1) = Stokes::Q; pols(2) = Stokes::U; pols(3) = Stokes::V; StokesCoordinate polAxis(pols); // Add the stokes coordinates to the system. coords.addCoordinate(polAxis); } void CoordinateUtil::addIAxis(CoordinateSystem & coords){ Vector pols(1); pols(0) = Stokes::I; StokesCoordinate polAxis(pols); // Add the stokes coordinates to the system. coords.addCoordinate(polAxis); } Bool CoordinateUtil::addStokesAxis(CoordinateSystem & coords, uInt shape) { if (shape<1 || shape>4) return False; // Vector which; if (shape==1) { which.resize(1); which(0) = Stokes::I; } else if (shape==2) { which.resize(2); which(0) = Stokes::I; which(1) = Stokes::Q; } else if (shape==3) { which.resize(3); which(0) = Stokes::I; which(1) = Stokes::Q; which(2) = Stokes::U; } else if (shape==4) { which.resize(4); which(0) = Stokes::I; which(1) = Stokes::Q; which(2) = Stokes::U; which(3) = Stokes::V; } StokesCoordinate sc(which); coords.addCoordinate(sc); return True; } void CoordinateUtil::addFreqAxis(CoordinateSystem & coords) { SpectralCoordinate freqAxis(MFrequency::LSRK, // Local standard of rest 1415E6, // ref. freq. = 1415MHz 1E3, // 1 kHz bandwidth/channel 0.0, // channel 0 is the ref. QC::HI( ).getValue(Unit("Hz"))); // HI coords.addCoordinate(freqAxis); } void CoordinateUtil::addLinearAxes(CoordinateSystem & coords, const Vector& names, const IPosition& shape) { const uInt n = names.nelements(); // Vector units(n); Vector refVal(n); Vector refPix(n); Vector inc(n); // for (uInt i=0; i pc(n, n); pc = 0.0; pc.diagonal() = 1.0; // LinearCoordinate lc(names, units, refVal, inc, pc, refPix); coords.addCoordinate(lc); } CoordinateSystem CoordinateUtil::defaultCoords2D(){ CoordinateSystem coords; CoordinateUtil::addDirAxes(coords); return coords; } CoordinateSystem CoordinateUtil::defaultCoords3D(){ CoordinateSystem coords; CoordinateUtil::addDirAxes(coords); CoordinateUtil::addFreqAxis(coords); return coords; } CoordinateSystem CoordinateUtil::defaultCoords4D(){ CoordinateSystem coords; CoordinateUtil::addDirAxes(coords); CoordinateUtil::addIQUVAxis(coords); CoordinateUtil::addFreqAxis(coords); return coords; } CoordinateSystem CoordinateUtil::defaultCoords(uInt dims){ switch (dims){ case 2: return CoordinateUtil::defaultCoords2D(); case 3: return CoordinateUtil::defaultCoords3D(); case 4: return CoordinateUtil::defaultCoords4D(); default: throw(AipsError("defaultCoords() - cannot create cordinates except " "for a 2, 3 or 4-dimensional image")); // The following line is just to suppress a compiler warning that this // function does not always return a CoordinateSystem. It is never // executed. return CoordinateUtil::defaultCoords2D(); } } uInt CoordinateUtil::addAxes ( CoordinateSystem& csys, Bool direction, Bool spectral, const String& stokes, Bool linear, Bool tabular, Bool silent ) { uInt nExtra = 0; if (direction) { if (! csys.hasDirectionCoordinate()) { addDirAxes(csys); nExtra += 2; } else if(!silent){ throw AipsError("Image already contains a DirectionCoordinate"); } } if (spectral) { if (! csys.hasSpectralAxis()) { addFreqAxis(csys); nExtra++; } else if(!silent){ throw AipsError("Image already contains a SpectralCoordinate"); } } if (! stokes.empty()) { if (! csys.hasPolarizationCoordinate()) { Vector which(1); String tmp = upcase(stokes); which(0) = Stokes::type(tmp); StokesCoordinate sc(which); csys.addCoordinate(sc); nExtra++; } else if(!silent){ throw AipsError("Image already contains a StokesCoordinate"); } } if (linear) { if (! csys.hasLinearCoordinate()) { Vector names(1); Vector units(1); Vector refVal(1); Vector refPix(1); Vector incr(1); names(0) = "Axis1"; units(0) = "km"; refVal(0) = 0.0; refPix(0) = 0.0; incr(0) = 1.0; Matrix pc(1,1); pc.set(0.0); pc.diagonal() = 1.0; LinearCoordinate lc(names, units, refVal, incr, pc, refPix); csys.addCoordinate(lc); nExtra++; } else if(!silent){ throw AipsError("Image already contains a LinearCoordinate"); } } if (tabular) { Int afterCoord = -1; Int iC = csys.findCoordinate(Coordinate::TABULAR, afterCoord); if (iC<0) { TabularCoordinate tc; csys.addCoordinate(tc); nExtra++; } else if(!silent){ throw AipsError("Image already contains a TabularCoordinate"); } } ThrowIf( nExtra == 0 && ! silent, "No degenerate axes specified" ); return nExtra; } Int CoordinateUtil::findSpectralAxis(const CoordinateSystem & coords) { const Int coordinate = coords.findCoordinate(Coordinate::SPECTRAL); if (coordinate < 0) return coordinate; // AlwaysAssert(coords.findCoordinate(Coordinate::SPECTRAL, coordinate) == -1, AipsError); const Vector pixelAxes = coords.pixelAxes(coordinate); AlwaysAssert(pixelAxes.nelements() == 1, AipsError); return pixelAxes(0); } void CoordinateUtil::findSpectralAxis(Int& pixelAxis, Int& worldAxis, Int& coordinate, const CoordinateSystem & coords) { pixelAxis = -1; worldAxis = -1; coordinate = coords.findCoordinate(Coordinate::SPECTRAL); if (coordinate < 0) return; // AlwaysAssert(coords.findCoordinate(Coordinate::SPECTRAL, coordinate) == -1, AipsError); // const Vector pixelAxes = coords.pixelAxes(coordinate); AlwaysAssert(pixelAxes.nelements() == 1, AipsError); pixelAxis = pixelAxes(0); // const Vector worldAxes = coords.worldAxes(coordinate); AlwaysAssert(worldAxes.nelements() == 1, AipsError); worldAxis = worldAxes(0); // return; } Vector CoordinateUtil::findDirectionAxes(const CoordinateSystem & coords) { const Int coordinate = coords.findCoordinate(Coordinate::DIRECTION); Vector retVal; if (coordinate < 0) return retVal; // AlwaysAssert(coords.findCoordinate(Coordinate::DIRECTION, coordinate) == -1, AipsError); retVal = coords.pixelAxes(coordinate); return retVal; } void CoordinateUtil::findDirectionAxes(Vector& pixelAxes, Vector& worldAxes, Int& coordinate, const CoordinateSystem & coords) { pixelAxes.resize(0); worldAxes.resize(0); coordinate = coords.findCoordinate(Coordinate::DIRECTION); if (coordinate < 0) return; // AlwaysAssert(coords.findCoordinate(Coordinate::DIRECTION, coordinate) == -1, AipsError); // pixelAxes = coords.pixelAxes(coordinate); AlwaysAssert(pixelAxes.nelements() == 2, AipsError); // worldAxes = coords.worldAxes(coordinate); AlwaysAssert(worldAxes.nelements() == 2, AipsError); // return; } Int CoordinateUtil::findStokesAxis(Vector& whichPols, const CoordinateSystem& coords) { const Int coordinate = coords.findCoordinate(Coordinate::STOKES); if (coordinate < 0) { whichPols.resize(1); whichPols(0) = Stokes::I; return coordinate; } AlwaysAssert(coords.findCoordinate(Coordinate::STOKES, coordinate) == -1, AipsError); const Vector pixelAxes = coords.pixelAxes(coordinate); AlwaysAssert(pixelAxes.nelements() == 1, AipsError); const StokesCoordinate& polCoord = coords.stokesCoordinate(coordinate); const Vector polsAsInts = polCoord.stokes(); const uInt nStokes = polsAsInts.nelements(); whichPols.resize(nStokes); for (uInt i = 0; i < nStokes; i++) { whichPols(i) = (Stokes::StokesTypes) polsAsInts(i); } return pixelAxes(0); } void CoordinateUtil::findStokesAxis(Int& pixelAxis, Int& worldAxis, Int& coordinate, const CoordinateSystem & coords) { pixelAxis = -1; worldAxis = -1; coordinate = coords.findCoordinate(Coordinate::STOKES); if (coordinate < 0) return; // AlwaysAssert(coords.findCoordinate(Coordinate::STOKES, coordinate) == -1, AipsError); // const Vector pixelAxes = coords.pixelAxes(coordinate); AlwaysAssert(pixelAxes.nelements() == 1, AipsError); pixelAxis = pixelAxes(0); // const Vector worldAxes = coords.worldAxes(coordinate); AlwaysAssert(worldAxes.nelements() == 1, AipsError); worldAxis = worldAxes(0); // return; } Bool CoordinateUtil::removeAxes(CoordinateSystem& csys, Vector& worldReplacement, const Vector& worldAxes, const Bool removeThem) // // Remove all the world axes and associated pixel axes // derived from the given list (a list to keep or remove) // of world axes. // This is awkward because as soon as you remove an // axis, they all shuffle down one ! The replacement values // are optional. If these vectors are the wrong length, // (e.g. 0), the reference pixels/values are used. The used // values are returned. // { // Bug out if nothing to do if (worldAxes.nelements() == 0) return True; // Make sure the world axes are valid uInt i,j; for (i=0; i= Int(csys.nWorldAxes())) return False; } // Make a list of the axes to remove in ascending order // with no duplicates Vector remove(csys.nWorldAxes()); if (removeThem) { remove.resize(worldAxes.nelements()); remove = worldAxes; GenSort::sort(remove, Sort::Ascending, Sort::NoDuplicates); } else { for (i=0,j=0; i=0; k--) { if (!csys.removeWorldAxis(remove(k), worldReplacement(k))) return False; } return True; } Bool CoordinateUtil::removePixelAxes(CoordinateSystem& csys, Vector& pixelReplacement, const Vector& pixelAxes, const Bool removeThem) { // Bug out if nothing to do if (pixelAxes.nelements() == 0) return True; // Make sure the pixel axes are valid uInt i,j; for (i=0; i= Int(csys.nPixelAxes())) return False; } // Make a list of the axes to remove in ascending order // with no duplicates Vector remove(csys.nPixelAxes()); if (removeThem) { remove.resize(pixelAxes.nelements()); remove = pixelAxes; GenSort::sort(remove, Sort::Ascending, Sort::NoDuplicates); } else { for (i=0,j=0; i=0; k--) { if (!csys.removePixelAxis(remove(k), pixelReplacement(k))) return False; } return True; } CoordinateSystem CoordinateUtil::makeCoordinateSystem(const IPosition& shape, Bool doLinear) { const uInt n = shape.nelements(); CoordinateSystem csys; // Attach an ObsInfo record so images that are made // with this have something sensible ObsInfo obsInfo; obsInfo.setObserver(String("Karl Jansky")); obsInfo.setTelescope(String("ALMA")); // It must be easier than this... USe 0.0001 // so that roundoff does not tick the 0 to 24 Time time(2000, 1, 1, 0, 0, 0.0001); MVTime time2(time); MVEpoch time4(time2); MEpoch date(time4); obsInfo.setObsDate(date); csys.setObsInfo(obsInfo); // if (doLinear) { Vector names(n); for (uInt i=0; i=2) { CoordinateUtil::addDirAxes(csys); } // if (n>=3) { if (CoordinateUtil::addStokesAxis(csys, uInt(shape(2)))) { doneStokes = True; } else { CoordinateUtil::addFreqAxis(csys); doneFreq = True; } } // uInt nDone = 0; if (n>=4) { nDone = 4; if (doneStokes) { CoordinateUtil::addFreqAxis(csys); doneFreq = True; } else { if (CoordinateUtil::addStokesAxis(csys, uInt(shape(3)))) { doneStokes = True; } else { if (!doneFreq) { CoordinateUtil::addFreqAxis(csys); doneFreq = True; } else { nDone = 3; } } } } // Linear for the rest if (nDone==3 || n >=5) { const uInt nLeft = n - nDone; if (nLeft > 0) { IPosition shape2(nLeft); Vector names(nLeft); for (uInt i=0; i& rp = dCoord.referencePixel(); if (!dCoord.toWorld(dirTo, rp)) { os << dCoord.errorMessage() << LogIO::EXCEPTION; } } // { Coordinate::Type type = Coordinate::DIRECTION; Int afterCoord = -1; Int c = coordsFrom.findCoordinate(type, afterCoord); if (c<0) { os << "No Direction coordinate in 'from' CoordinateSystem" << LogIO::EXCEPTION; } const DirectionCoordinate& dCoord = coordsFrom.directionCoordinate(c); const Vector& rp = dCoord.referencePixel(); if (!dCoord.toWorld(dirFrom, rp)) { os << dCoord.errorMessage() << LogIO::EXCEPTION; } } // MFrequency::Types typeTo, typeFrom; { Coordinate::Type type = Coordinate::SPECTRAL; Int afterCoord = -1; Int c = coordsTo.findCoordinate(type, afterCoord); if (c<0) { os << "No Spectral coordinate in 'to' CoordinateSystem" << LogIO::EXCEPTION; } const SpectralCoordinate& sCoord = coordsTo.spectralCoordinate(c); typeTo = sCoord.frequencySystem(); } { Coordinate::Type type = Coordinate::SPECTRAL; Int afterCoord = -1; Int c = coordsFrom.findCoordinate(type, afterCoord); if (c<0) { os << "No Spectral coordinate in 'from' CoordinateSystem" << LogIO::EXCEPTION; } const SpectralCoordinate& sCoord = coordsFrom.spectralCoordinate(c); typeFrom = sCoord.frequencySystem(); } // const ObsInfo& obsInfoTo = coordsTo.obsInfo(); const ObsInfo& obsInfoFrom = coordsFrom.obsInfo(); // String telFrom = obsInfoFrom.telescope(); String telTo = obsInfoTo.telescope(); MPosition posFrom, posTo; findObservatoryOrRaiseException(os, posFrom, telFrom); findObservatoryOrRaiseException(os, posTo, telTo); // return makeFrequencyMachine(os, machine, typeTo, typeFrom, dirTo, dirFrom, obsInfoTo.obsDate(), obsInfoFrom.obsDate(), posTo, posFrom, unit); } void CoordinateUtil::findObservatoryOrRaiseException(LogIO& os, MPosition& pos, const String& tel) { Bool found = MeasTable::Observatory(pos, tel); if(!found){ os << "Cannot find the observatory name " << tel << " in the CASA" << endl; os << "database. Please request that it be added." << LogIO::EXCEPTION; } } Bool CoordinateUtil::makeFrequencyMachine(LogIO& os, MFrequency::Convert& machine, MFrequency::Types typeTo, MFrequency::Types typeFrom, const MDirection& dirTo, const MDirection& dirFrom, const MEpoch& epochTo, const MEpoch& epochFrom, const MPosition& posTo, const MPosition& posFrom, const Unit& unit) { // Create frames MeasFrame frameFrom; MeasFrame frameTo; // Add Direction frameFrom.set(dirFrom); frameTo.set(dirTo); // Add Epoch os << LogOrigin("CoordinateUtil", "makeFrequencyMachine"); if(epochFrom.getValue().get() < 0.0) os << "The output CoordinateSystem has no valid epoch" << LogIO::EXCEPTION; if(epochTo.getValue().get() < 0.0) os << "The input CoordinateSystem has no valid epoch" << LogIO::EXCEPTION; frameFrom.set(epochFrom); frameTo.set(epochTo); // Add the position frameFrom.set(posFrom); frameTo.set(posTo); // Make the machine MFrequency::Ref refFrom(typeFrom, frameFrom); MFrequency::Ref refTo(typeTo, frameTo); machine = MFrequency::Convert(unit, refFrom, refTo); // Test a conversion Bool ok = True; MFrequency freqTo; Quantum freq(1.0e9, Unit(String("Hz"))); MFrequency freqFrom(freq, typeFrom); try { freqTo = machine(freqFrom); } catch (std::exception& x) { ok = False; } if (!ok) { os << LogIO::WARN; os << "Unable to convert between the input and output SpectralCoordinates" << endl; os << "this probably means one is in the REST frame which requires" << endl; os << "the radial velocity - this is not implemented yet" << LogIO::POST; } // return ok; } Bool CoordinateUtil::holdsSky (Bool& holdsOneSkyAxis, const CoordinateSystem& csys, Vector pixelAxes) { AlwaysAssert(pixelAxes.nelements()==2, AipsError); // holdsOneSkyAxis = False; Int dirCoordinate = csys.findCoordinate(Coordinate::DIRECTION); if (dirCoordinate!=-1) { Vector dirPixelAxes = csys.pixelAxes(dirCoordinate); if ( (dirPixelAxes(0)==pixelAxes(0) && dirPixelAxes(1)==pixelAxes(1)) || (dirPixelAxes(0)==pixelAxes(1) && dirPixelAxes(1)==pixelAxes(0))) { return True; } // if ( (pixelAxes(0)==dirPixelAxes(0) && pixelAxes(1)!=dirPixelAxes(1)) || (pixelAxes(0)!=dirPixelAxes(0) && pixelAxes(1)==dirPixelAxes(1)) || (pixelAxes(0)==dirPixelAxes(1) && pixelAxes(1)!=dirPixelAxes(0)) || (pixelAxes(0)!=dirPixelAxes(1) && pixelAxes(1)==dirPixelAxes(0)) ) { holdsOneSkyAxis = True; } } return False; } void CoordinateUtil::setNiceAxisLabelUnits(CoordinateSystem& csys) { for (uInt i = 0; i < csys.nCoordinates(); i++) { Coordinate::Type type = csys.type(i); if (type==Coordinate::DIRECTION) { setDirectionUnit (csys, String("deg"), i); } else if (type==Coordinate::SPECTRAL) { SpectralCoordinate coord(csys.spectralCoordinate(i)); Vector str(coord.nWorldAxes()); for (uInt j = 0; j < str.nelements(); j++) str(j) = "km/s"; MDoppler::Types oldDoppler = coord.velocityDoppler(); coord.setVelocity (String("km/s"), oldDoppler); csys.replaceCoordinate(coord, i); } } } Bool CoordinateUtil::findSky(String&errorMessage, Int& dC, Vector& pixelAxes, Vector& worldAxes, const CoordinateSystem& csys) // // Assumes only one DirectionCoordinate . {pixel,world}Axes says where // in the CS the DirectionCoordinate axes are // { CoordinateUtil::findDirectionAxes (pixelAxes, worldAxes, dC, csys); if (dC<0 || pixelAxes.nelements()!=2 || worldAxes.nelements()!=2) { errorMessage = "Image does not have 2 sky coordinate axes"; return False; } // for (uInt i=0; i<2; i++) { if (pixelAxes(i)==-1 || worldAxes(i)==-1) { errorMessage = "Image does not have 2 sky coordinate axes"; return False; } } // return True; } Stokes::StokesTypes CoordinateUtil::findSingleStokes (LogIO& os, const CoordinateSystem& csys, uInt pixel) { Stokes::StokesTypes stokes(Stokes::Undefined); Int stokesCoordinateNumber = csys.findCoordinate(Coordinate::STOKES); if (stokesCoordinateNumber==-1) { os << LogIO::WARN << "There is no Stokes coordinate in the CoordinateSystem - assuming Stokes I" << LogIO::POST; stokes = Stokes::I; } else { StokesCoordinate stokesCoordinate = csys.stokesCoordinate(stokesCoordinateNumber); // // Find out what Stokes the specified pixel belongs to. // if (!stokesCoordinate.toWorld(stokes, Int(pixel))) { os << "StokesCoordinate conversion failed because " << stokesCoordinate.errorMessage() << LogIO::EXCEPTION; } } return stokes; } String CoordinateUtil::formatCoordinate( const IPosition& pixel, const CoordinateSystem& csys, Int precision ) { ThrowIf( pixel.size() != csys.nPixelAxes(), "Number of elements in pixel (" + String::toString(pixel.size()) + ") must be equal to number of pixel axes in coordinate system (" + String::toString(csys.nPixelAxes()) + ")" ); Vector pixel2(csys.nPixelAxes()); for (uInt i=0; i& pixel, const CoordinateSystem& csys, Int precision ) { Vector world; if (!csys.toWorld(world, pixel)) { String err = String("Error converting coordinate position because ") + csys.errorMessage(); throw(AipsError(err)); } String s2; for (uInt i=0; i thisWorldAxes; Vector thatWorldAxes; Vector refChange; if (! thiscsys.worldMap (thatWorldAxes, thisWorldAxes, refChange, thatcsys)) { return 9; } // This must be a subset of that or that a subset of this. // We are interested in pixel axes only, so transform the world axes // to pixel axes and remove world axes without pixel axes. Vector thisPixelAxes = toPixelAxes (thiscsys, thatcsys, thisWorldAxes); Vector thatPixelAxes = toPixelAxes (thatcsys, thiscsys, thatWorldAxes); // thisPixelAxes tells which pixel axes of this are part of that. // thatPixelAxes tells which pixel axes of that are part of this. // Check if the axes are in the correct order (ascending). // I.e. it is not supported that this and that have the same axes, // but in a different order. if (! checkOrder (thisPixelAxes)) { return 9; } if (! checkOrder (thatPixelAxes)) { return 9; } // Only one of the coordinate systems can be a subset. Bool thisIsSubSet = anyLT (thatPixelAxes, 0); Bool thatIsSubSet = anyLT (thisPixelAxes, 0); if (thisIsSubSet) { if (thatIsSubSet) { return 9; } return -1; } else if (thatIsSubSet) { return 1; } return 0; //equal } Vector CoordinateUtil::toPixelAxes (const CoordinateSystem& thiscsys, const CoordinateSystem& thatcsys, const Vector& worldAxes) { // Map the world axes to pixel axes. Vector pixelAxes(thiscsys.nPixelAxes(), -1); for (uInt i=0; i= 0) { Int pixAxis = thiscsys.worldAxisToPixelAxis (i); if (pixAxis >= 0) { pixelAxes(pixAxis) = thatcsys.worldAxisToPixelAxis (worldAxes(i)); } } } return pixelAxes; } Bool CoordinateUtil::checkOrder (const Vector& pixelAxes) { // Check if the mapped axes are in ascending order. I.e. we do not allow // that the order of axes in 2 images is different. Int last = -1; for (uInt i=0; i= 0) { if (pixelAxes(i) <= last) { return False; } last = pixelAxes(i); } } return True; } Bool CoordinateUtil::findExtendAxes (IPosition& newAxes, IPosition& stretchAxes, const IPosition& newShape, const IPosition& oldShape, const CoordinateSystem& newcsys, const CoordinateSystem& oldcsys) { Vector oldWorldAxes; Vector newWorldAxes; Vector refChange; if (! oldcsys.worldMap (newWorldAxes, oldWorldAxes, refChange, newcsys)) { return False; } // Old must be a subset of new. // We are interested in pixel axes only, so transform the world axes // to pixel axes and remove world axes without pixel axes. Vector oldPixelAxes = toPixelAxes (oldcsys, newcsys, oldWorldAxes); Vector newPixelAxes = toPixelAxes (newcsys, oldcsys, newWorldAxes); // oldPixelAxes tells which pixel axes of old are not part of new. // newPixelAxes tells which pixel axes of new are not part of old. // Check if the axes are in the correct order. if (! checkOrder (oldPixelAxes)) { return False; } if (! checkOrder (newPixelAxes)) { return False; } // Old must be a subset of new. if (anyLT (oldPixelAxes, 0)) { return False; } // Find the new and stretch axes. uInt nrdim = newPixelAxes.nelements(); if (nrdim != newShape.nelements()) { return False; } newAxes.resize (nrdim); stretchAxes.resize (nrdim); uInt nrn = 0; uInt nrs = 0; for (uInt i=0; i oldShape.nelements()) { return False; } if (oldShape(i-nrn) == 1 && newShape(i) > 1) { stretchAxes(nrs++) = i; } } } newAxes.resize (nrn); stretchAxes.resize (nrs); return True; } Bool CoordinateUtil::cylindricalFix (CoordinateSystem& csys, String& errorMessage, const IPosition& shape) { Vector pixelAxes, worldAxes; Int coord; findDirectionAxes(pixelAxes, worldAxes, coord, csys); if (coord < 0) return True; // if (pixelAxes.nelements()<2 || worldAxes.nelements()<2) { errorMessage = String("not enough pixel or world axes in DirectionCoordinate"); return False; } // check shape here DirectionCoordinate dirCoord (csys.directionCoordinate(coord)); if (pixelAxes[0] < 0 || pixelAxes[1] < 0 || !dirCoord.cylindricalFix (shape(pixelAxes[0]), shape(pixelAxes[1]))) { errorMessage = dirCoord.errorMessage(); return False; } // csys.replaceCoordinate (dirCoord, coord); return True; } Bool CoordinateUtil::setVelocityState (String& errorMsg, CoordinateSystem& csys, const String& unit, const String& spcquant) { static Unit kms(String("km/s")); // Int after = -1; Int iS = csys.findCoordinate(Coordinate::SPECTRAL, after); if (iS>=0) { SpectralCoordinate sCoord = csys.spectralCoordinate(iS); // Get current state //cout << "setVelocityState unit: " << unit << " spcquant: " << spcquant << endl; MDoppler::Types oldDoppler = sCoord.velocityDoppler(); String oldVelUnit = sCoord.velocityUnit(); SpectralCoordinate::SpecType oldspcType = sCoord.nativeType(); // Prepare new state MDoppler::Types newDoppler(oldDoppler); String newVelUnit(oldVelUnit); SpectralCoordinate::SpecType newspcType(oldspcType); // Find new Doppler or spectral state, if any if (!spcquant.empty()) { if (!MDoppler::getType(newDoppler, spcquant) && !SpectralCoordinate::stringtoSpecType(newspcType, spcquant)) { errorMsg = String("Illegal velocity Doppler/spectral type"); return False; } } // Find new spectral unit if any if (!unit.empty()) { newVelUnit = unit; } // Set new doppler. if (!sCoord.setVelocity (newVelUnit, newDoppler)) { errorMsg = sCoord.errorMessage(); return False; } // Set new spectral type. if (!sCoord.setNativeType(newspcType)) { errorMsg = sCoord.errorMessage(); return False; } // Replace in CS csys.replaceCoordinate(sCoord, iS); } return True; } Bool CoordinateUtil::setSpectralState (String& errorMsg, CoordinateSystem& csys, const String& unit, const String& spcquant) { static Unit KMS(String("km/s")); static Unit HZ(String("GHz")); static Unit M(String("m")); // //cout << "setSpectralState unit: " << unit << " spcype: " << spcquant << endl; Int after = -1; Int iS = csys.findCoordinate(Coordinate::SPECTRAL, after); if (iS>=0) { SpectralCoordinate sCoord = csys.spectralCoordinate(iS); // Prepare new state MDoppler::Types newDoppler(sCoord.velocityDoppler()); String newVelUnit(sCoord.velocityUnit()); String newWaveUnit(sCoord.wavelengthUnit()); SpectralCoordinate::SpecType newspcType = sCoord.nativeType(); Vector newWorldAxisUnits(sCoord.worldAxisUnits().copy()); // Find new Doppler, if any if (!spcquant.empty()) { if (!MDoppler::getType(newDoppler, spcquant) && !SpectralCoordinate::stringtoSpecType(newspcType, spcquant)) { errorMsg = String("Illegal velocity Doppler/spectral type"); return False; } } // If the spectral unit is consistent with Hz, we update the world // axis units. If it is consistent with km/s we update the // velocity state if (!unit.empty()) { Unit t(unit); if (t == HZ) { //cout << "New HZ" << endl; newWorldAxisUnits[0] = unit; } else if (t == KMS) { //cout << "New velocity" << endl; newVelUnit = unit; } else if (t == M) { //cout << "New wavelength unit " <=0) { SpectralCoordinate sCoord = csys.spectralCoordinate(iS); // Set format Unit //cout << "setSpectralFormatting unit: " << unit << " spcquant: " << spcquant << endl; sCoord.setFormatUnit (unit); // Velocity State MDoppler::Types oldDoppler = sCoord.velocityDoppler(); String oldVelUnit = sCoord.velocityUnit(); SpectralCoordinate::SpecType oldspcType = sCoord.nativeType(); // MDoppler::Types newDoppler(oldDoppler); String newVelUnit(oldVelUnit); SpectralCoordinate::SpecType newspcType(oldspcType); // Find new Doppler, if any if (!spcquant.empty()) { if (!MDoppler::getType(newDoppler, spcquant) && !SpectralCoordinate::stringtoSpecType(newspcType, spcquant)){ errorMsg = String("Illegal velocity Doppler/spectral state - no change"); newDoppler = oldDoppler; newspcType = oldspcType; return False; } } // if (oldDoppler != newDoppler) { if (!sCoord.setVelocity (newVelUnit, newDoppler)) { errorMsg = sCoord.errorMessage(); return False; } } // Set spectral type. if (newspcType != oldspcType){ if (!sCoord.setNativeType(newspcType)) { errorMsg = sCoord.errorMessage(); return False; } } // Replace in CS csys.replaceCoordinate(sCoord, iS); } // return True; } Bool CoordinateUtil::isSky (LogIO& os, const CoordinateSystem& cSys) { const uInt nPixelAxes = cSys.nPixelAxes(); if (nPixelAxes != 2) { os << "The CoordinateSystem is not two dimensional. It has " << nPixelAxes << " dimensions" << LogIO::EXCEPTION; } Bool xIsLong = True; Int dirCoordinate = cSys.findCoordinate(Coordinate::DIRECTION); if (dirCoordinate==-1) { os << "There is no DirectionCoordinate (sky) in this CoordinateSystem" << LogIO::EXCEPTION; } Vector dirPixelAxes = cSys.pixelAxes(dirCoordinate); if (dirPixelAxes(0) == -1 || dirPixelAxes(1) == -1) { os << "The pixel axes for the DirectionCoordinate have been removed" << LogIO::EXCEPTION; } // Which axis is longitude and which is latitude if(dirPixelAxes(0)==0 && dirPixelAxes(1)==1) { xIsLong = True; } else { xIsLong = False; } return xIsLong; } Bool CoordinateUtil::setRestFrequency (String& errorMsg, CoordinateSystem& cSys, const String& unit, const Double& value) { static Unit HZ(String("GHz")); static Unit M(String("m")); // Int after = -1; Int iS = cSys.findCoordinate(Coordinate::SPECTRAL, after); if (iS>=0) { SpectralCoordinate sCoord = cSys.spectralCoordinate(iS); // Check for weird value if (value < 0.0){ errorMsg = String("The rest frequency/wavelength is below zero!"); return False; } else if (isNaN(value)){ errorMsg = String("The rest frequency/wavelength is NaN!"); return False; } else if (isInf(value)){ errorMsg = String("The rest frequency/wavelength is InF!"); return False; } // Get the old rest frequency and unit Double oldValue = sCoord.restFrequency(); Unit oldUnit = Unit(sCoord.worldAxisUnits()(0)); // Check whether something has to be done if (!unit.empty() && (value != oldValue) && (value>0 || oldValue>0)){ // Make sure the unit conforms with m or Hz Unit t(unit); if (t != HZ && t!= M) { errorMsg = String("Illegal spectral unit"); return False; } // Compute the rest frequency in the given units from the input Quantity newQuant=Quantity(value, Unit(unit)); MVFrequency newFreq = MVFrequency(newQuant); Double newValue = newFreq.get(oldUnit).getValue(); // Exclude weird numbers if (isNaN(newValue)){ errorMsg = String("The new rest frequency/wavelength is NaN!"); return False; } else if (isInf(newValue)){ errorMsg = String("The new rest frequency/wavelength is InF!"); return False; } // Set the new rest frequency if (!sCoord.setRestFrequency(newValue)) { errorMsg = sCoord.errorMessage(); return False; } } // Replace in CS cSys.replaceCoordinate(sCoord, iS); } return True; } Bool CoordinateUtil::setSpectralConversion (String& errorMsg, CoordinateSystem& cSys, const String frequencySystem) { // Set conversion type. This lets the SC convert to other frequency systems // We need some extra info from the ObsInfo for the Spectral conversion layer // We avoid trying to fish it out unless we have to, because it might // not be present and we would get unecessary failures. Int after = -1; Int iS = cSys.findCoordinate(Coordinate::SPECTRAL, after); if (iS>=0) { SpectralCoordinate coord(cSys.spectralCoordinate(iS)); MFrequency::Types oldctype; MEpoch epoch; MPosition position; MDirection direction; coord.getReferenceConversion(oldctype, epoch, position, direction); // MFrequency::Types ctype; if (!MFrequency::getType(ctype, frequencySystem)) { errorMsg = String("invalid frequency system"); return False; } // if (ctype!=oldctype) { // We also need a direction. Use the reference if we can find one after = -1; Int cD = cSys.findCoordinate(Coordinate::DIRECTION, after); if (cD<0) { errorMsg = String("No DirectionCoordinate; cannot set Spectral conversion layer"); return False; } else { const DirectionCoordinate& dCoord = cSys.directionCoordinate(cD); const Vector& rp = dCoord.referencePixel(); if (!dCoord.toWorld(direction, rp)) { errorMsg = dCoord.errorMessage(); return False; } // Now find the epoch and position const ObsInfo& oi = cSys.obsInfo(); String telescope = oi.telescope(); if (!MeasTable::Observatory(position, telescope)) { errorMsg = String("Cannot find observatory; cannot set Spectral conversion layer"); return False; } else { epoch = oi.obsDate(); Double t = epoch.getValue().get(); if (t <= 0.0) { errorMsg = String("Epoch not valid; cannot set Spectral conversion layer"); return False; } else { coord.setReferenceConversion(ctype, epoch, position, direction); } } } } // cSys.replaceCoordinate(coord, iS); } return True; } Bool CoordinateUtil::setDirectionUnit (CoordinateSystem& csys, const String& unit, Int which) { // FInd DC Vector pixelAxes, worldAxes; Int iC = which; if (iC < 0) { CoordinateUtil::findDirectionAxes (pixelAxes, worldAxes, iC, csys); } else { worldAxes = csys.worldAxes (iC); } // if (iC >= 0) { // Fill a vector of units for the unremoved DC axes uInt nWorldAxes = 0; for (uInt i=0; i= 0) nWorldAxes++; } Vector units(nWorldAxes); units = unit; // Now set them return CoordinateUtil::setCoordinateUnits (csys, units, iC); } return True; } Bool CoordinateUtil::setDirectionConversion (String& errorMsg, CoordinateSystem& csys, const String directionSystem) { Int after = -1; Int iS = csys.findCoordinate(Coordinate::DIRECTION, after); if (iS>=0) { // Convert code from String String code = directionSystem; code.upcase(); MDirection::Types type; if (!MDirection::getType(type, code)) { errorMsg = String("Invalid direction reference system"); return False; } // Update and replace DirectionCoordinate coord = csys.directionCoordinate (iS); coord.setReferenceConversion(type); csys.replaceCoordinate(coord, iS); } return True; } Bool CoordinateUtil::setCoordinateUnits (CoordinateSystem& csys, const Vector& units, uInt which) { AlwaysAssert(which worldAxes = csys.worldAxes(which); uInt nWorldAxes = 0; for (uInt i=0; i=0) nWorldAxes++; } // Make sure we have the right number AlwaysAssert(nWorldAxes==units.nelements(), AipsError); // Find the world units vector for this CS Vector tUnits = csys.worldAxisUnits().copy(); // Now slot in the new units. For the removed axes, their units // are unchanged. They can never be brought back so it doesn't matter. uInt j = 0; for (uInt i=0; i= 0) { tUnits[worldAxes[i]] = units[j]; j++; } } // return csys.setWorldAxisUnits (tUnits); } Coordinate::Type CoordinateUtil::findPixelAxis (const CoordinateSystem& csys, Int axis) { Int coord, axisInCoordinate; csys.findPixelAxis(coord, axisInCoordinate, axis); if (coord<0) { throw(AipsError("Given pixel axis does not exist in CoordinateSystem")); } // return csys.type (coord); } Coordinate::Type CoordinateUtil::findWorldAxis (const CoordinateSystem& csys, Int axis) { Int coord, axisInCoordinate; csys.findWorldAxis(coord, axisInCoordinate, axis); if (coord<0) { throw(AipsError("Given world axis does not exist in CoordinateSystem")); } // return csys.type (coord); } Bool CoordinateUtil::dropRemovedAxes ( CoordinateSystem& csysOut, const CoordinateSystem& csysIn, Bool preserveAxesOrder ) { Bool dropped = False; CoordinateSystem tmp; csysOut = tmp; csysOut.setObsInfo(csysIn.obsInfo()); Vector removeWorld(csysIn.nPixelAxes()); Vector removePixel(csysIn.nWorldAxes()); uInt k = 0; uInt l = 0; vector worldAxesOrder; vector pixelAxesOrder; for (uInt i=0; i& pixelAxesIn = csysIn.pixelAxes(i); const Vector& worldAxesIn = csysIn.worldAxes(i); AlwaysAssert(pixelAxesIn.nelements()==worldAxesIn.nelements(), AipsError); Bool allRemoved = allEQ(pixelAxesIn, -1) && allEQ(worldAxesIn,-1); if (allRemoved) { dropped = True; } else { csysOut.addCoordinate(csysIn.coordinate(i)); if (preserveAxesOrder) { for (uInt m=0; m= 0) { worldAxesOrder.push_back(worldAxesIn[m]); } if (pixelAxesIn[m] >= 0) { pixelAxesOrder.push_back(pixelAxesIn[m]); } } } // Maintain a list of axes to do virtual removal of Int c = csysOut.nCoordinates() - 1; Vector pixelAxesOut = csysOut.pixelAxes(c); Vector worldAxesOut = csysOut.worldAxes(c); AlwaysAssert(pixelAxesOut.nelements()==worldAxesOut.nelements(), AipsError); AlwaysAssert(pixelAxesIn.nelements()==worldAxesIn.nelements(), AipsError); const uInt n = worldAxesOut.nelements(); for (uInt j=0; j0) { removeWorld.resize(k, True); GenSort::sort(removeWorld, Sort::Descending, Sort::NoDuplicates); for (uInt i=0; i0) { removePixel.resize(l, True); GenSort::sort(removePixel, Sort::Descending, Sort::NoDuplicates); // for (uInt i=0; i(worldAxesOrder), Vector(pixelAxesOrder)); } return dropped; } CoordinateSystem CoordinateUtil::makeBinnedCoordinateSystem (const IPosition& factors, const CoordinateSystem& csysIn, Bool failOnStokes) { const uInt nDim = factors.nelements(); AlwaysAssert(csysIn.nPixelAxes()==nDim,AipsError); // Check Stokes. if (failOnStokes) { Int coord, axisInCoord; for (uInt i=0; i incrIn(csysIn.increment().copy()); Vector incrOut(incrIn.copy()); Vector refPixIn(csysIn.referencePixel().copy()); Vector refPixOut(refPixIn.copy()); // Loop over pixel axes for (uInt pA=0; pA=0) { incrOut(wA) *= Double(factors[pA]); } } // CoordinateSystem csysOut(csysIn); csysOut.setReferencePixel(refPixOut); csysOut.setIncrement(incrOut); // return csysOut; } String CoordinateUtil::axisLabel (const Coordinate& coord, uInt axis, Bool doWorld, Bool doAbs, Bool doVel) { String axisName = coord.worldAxisNames()(axis); // String nativeUnit = coord.worldAxisUnits()(axis); // Coordinate::Type ctype = coord.type(); String base; // if (ctype == Coordinate::DIRECTION) { const DirectionCoordinate& dcoord = dynamic_cast(coord); // MDirection::Types dtype = dcoord.directionType(); MDirection::Types ctype; dcoord.getReferenceConversion(ctype); // Bool isLong = (axis==0); // Depending on the requested labelling type, we convert // the axis unit name to something sensible. This is // because it's confusing to see Galactic coordinates // called 'Right Ascension' say. uInt ctypeI = static_cast(ctype); MDirection::GlobalTypes gType = MDirection::globalType(ctypeI); if (dtype != ctype) { if (gType==MDirection::GRADEC) { if (isLong) { axisName = "Right Ascension"; } else { axisName = "Declination"; } } else if (gType==MDirection::GHADEC) { if (isLong) { axisName = "Hour Angle"; } else { axisName = "Declination"; } } else if (gType==MDirection::GAZEL) { if (isLong) { axisName = "Azimuth"; } else { axisName = "Elevation"; } } else if (gType==MDirection::GLONGLAT) { if (isLong) { axisName = "Longitude"; } else { axisName = "Latitude"; } } } // String stype = MDirection::showType(ctype); if (doAbs) { if (doWorld) { base = stype + String(" ") + axisName; } else { base = stype + String(" ") + axisName + String(" (pixels)"); } } else { base = String("Relative ") + stype + String(" ") + axisName + String(" (") + nativeUnit + String(")"); } } else if (ctype == Coordinate::SPECTRAL) { const SpectralCoordinate& dcoord = dynamic_cast(coord); // Get frame conversion state MFrequency::Types ctype; MEpoch epoch; MPosition position; MDirection direction; dcoord.getReferenceConversion(ctype, epoch, position, direction); String freqType = MFrequency::showType(ctype); // Get velocity state // if (doWorld) { // We must avoid making a unit from the String 'pixels' if (doVel) { String velUnit = dcoord.velocityUnit(); String doppler = MDoppler::showType(dcoord.velocityDoppler()); base = freqType + String(" ") + doppler + String(" velocity (") + velUnit + String(")"); } else { base = freqType + String(" ") + axisName + String(" (") + nativeUnit + String(")"); } } else { base = freqType + String(" ") + axisName + String(" (pixels)"); } // if (!doAbs) { base = String("Relative ") + base; } } else if (ctype==Coordinate::STOKES) { base = axisName; if (doWorld) { if (!doAbs) base = String("Relative ") + base; } else { if (!doAbs) base = String("Relative ") + base + String(" (") + nativeUnit + String(")"); } } else { base = axisName + String(" (") + nativeUnit + String(")"); if (!doAbs) base = String("Relative ") + base; } return base; } } // end namespace casacore casacore-3.7.1/coordinates/Coordinates/CoordinateUtil.h000066400000000000000000000631641476623553700232200ustar00rootroot00000000000000//# CoordinateUtils.h: static functions dealing with coordinates //# Copyright (C) 1997,1998,1999,2000,2001,2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef COORDINATES_COORDINATEUTIL_H #define COORDINATES_COORDINATEUTIL_H #include #include #include #include #include //# For enums #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class CoordinateSystem; class DirectionCoordinate; class ObsInfo; class String; class LogIO; class MEpoch; class MPosition; class Unit; // Functions for creating default CoordinateSystems // // // // //
      • CoordinateSystem // // // // CoordinateUtils follows the Casacore naming convention for static functions // that are associated with a class. // // // // This file contains declarations for static functions that manipulate // coordinate systems. It currently contains functions for: //
          //
        • Adding default axes to a CoordinateSystem //
        • Creating a default CoordinateSystem //
        • Finding specified axes in a CoordinateSystem //
        // // The functions for adding default axes to a CoordinateSystem can add // either a RA/DEC pair of axes, a Polarisation Axis, or a Spectral Axis to // a user supplied coordinate system. The default values for these functions // are: //
          //
        • addDirAxes this adds a DirectionCoordinate with a // reference pixel of (0,0) corresponding to an RA/DEC of (0,0) in a // J2000 reference frame. The pixel increment is 1 arc-minute. //
        • addIQUVAxis this adds a polarization axis with four // elements corresponding to the Stokes (I,Q,U,V) components. //
        • addIAxis this adds a polarization axis with one // element corresponding to the Stokes I component only //
        • addFreqAxis this adds a spectral axis with a reference // frequency of 1.415GHz on channel 0. The channel bandwidth (pixel // increment) is 1kHz, and the reference frame is the kinematical Local Standard of // rest (MFrequency::LSRK). //
        // // The defaultCoords functions, create from scratch a // CoordinateSystem using the above described addXXXAxis // functions to add the required number of dimensions to the // CoordinateSystem. Only 2, 3 or 4 dimensional coordinate systems can be // constructed using these functions. The coordinate systems always have // RA/Dec axes. Three dimensional Systems add a spectral axis and // four-dimensional systems add an IQUV polarization axis. An exception // (AipsError) is thrown if defaultCoords(uInt) is called with a // parameter that is not 2, 3, or 4. // // The defaultCoordsXX functions return the coordinate system by // value (which involves a copy of the CoordinateSystem) and hence are not // as effcient as the addXXXAxis functions. // // If the default axes provided by these functions are not quite what is // required it is possible to use member functions of the // CoordinateSystem // and Coordinate classes // (DirectionCoordinate, // StokesCoordinate, // SpectralCoordinate etc.) // to tweak the appropriate parameters of the specified axis. // // Now we turn to the functions for finding axes in a CoordinateSystem. With // a CoordinateSystem object it is not required that the first Coordinate // axis in the the CoordinateSystem map to the first pixel axis in an // image. Hence it is necessary to determine which pixel axis corresponds to a // specified Coordinate and this can be done using these functions. Some // coordinate types, in particular DirectionCoordinate, usually map to more // than one pixel axis (DirectionsCoordinates are inherently two-dimensional). // // This group contains declarations for static functions that search // CoordinateSystem's for a coordinate of the specified type. It returns the // pixel axis (zero relative) of the specified coordinate type. If the supplied // Coordinate system does not contain the specified coordinate type the // returned value is function specific (but usually -1). If the supplied // CoordinateSystem contains two or more of the specified coordinateType then // an exception (AipsError) is thrown. // // Finally functions are provided for removing lists of pixel/world axes // from a CoordinateSystem. // This process is made a little awkward by the fact that when you // remove one axis, all the rest shuffle down one, so it is // provided here. Generally, one only needs to remove one axis // (in which case you should use the CoordinateSystem::removeWorldAxis and // CoordinateSystem::removcePixelAxis functions), but on occaision, // the multiple need is there. //
        // // // I use these functions when creating test images. // // PagedImage(IPosition(4,256,256,4,32), CoordinateUtil::defaultCoords4D(), // String("test.image")); // // // // // Functions are needed to handle images without specifying a canonical // coordinate order. For example suppose we want to find the spectral aixs // of a PagedImage object. // // // const Int spectralAxis = CoordinateUtil::findSpectralAxis(image.coordinates()); // cout << "The spectral axis is of shape " << image.shape()(spectralAxis) << endl; // // // // // Here we remove the first and last world axes, and their associated // pixel axes from a 3D CoordinateSystem. The reference values and // reference pixels are used for the replacement values. // // // CoordinateSystem cSys = CoordinateUtil::defaultCoords3D(); // Vector worldAxes(2); // worldAxes(0) = 0; worldAxes(1) = cSys.nWorldAxes()-1; // Vector worldRep; // Bool ok = CoordinateUtil::removeAxes(cSys, worldRep, worldAxes, True); // cout << "For world axes used " << worldRep << " for replacement" << endl; // // // // // // I got fed up writing small functions to create and find coordinates when writing // test programs involving Images and ComponentModels. // // // //
      • AipsError // // // // Many of these methods belong in the CoordinateSystem class, // eg all the add* methods, and in fact CoordinateSystem already has analogs // for many of them. The factory methods which create a CoordinateSystem // could also arguably go in CoordinateSystem as static methods. Having a separate // utility class that really just has methods that operate on or create CoordinateSystem // objects makes no sense. CoordinateUtil is the antithesis of object oriented design, // and we need to endeavor to expunge it from our system. // // // Static functions for creating default coordinate systems // class CoordinateUtil { public: // Add a RA/DEC pair of direction axes (ie. a DirectionCoordinate) to the // user supplied CoordinateSystem. See the synopsis above for the current // default values. static void addDirAxes(CoordinateSystem& coords); // Add a Stokes I,Q,U,V axis to the user supplied CoordinateSystem. static void addIQUVAxis(CoordinateSystem& coords); // Add a Stokes I (only) axis to the user supplied CoordinateSystem. static void addIAxis(CoordinateSystem& coords); // Add a Stokes axis of length 1 to 4 selected from I,Q,U,V // E.g. if shape=2 you get IQ. Returns False if shape // is not in the range 1 to 4 static Bool addStokesAxis(CoordinateSystem& coords, uInt shape); // Add Linear axes. The LinearCoordinate can have > 1 axes (like // the DirectionCoordinate has 2). The number of axes is given // by the length of the names argument. If you supply a shape, // it will be used to set the reference pixel to 1/2 the shape. // If the shape does not have the same number of elements as // the names variable, the reference pixel will be 0 static void addLinearAxes (CoordinateSystem & coords, const Vector& names, const IPosition& shape); // Add a spectral axis to the user supplied CoordinateSystem. See the // synopsis above for the current default values. static void addFreqAxis(CoordinateSystem& coords); // Add one axis for each of the specified coordinate types. // Returns the number of axes added. // If silent==True, existing axes are silently ignored. // This should really be a method of CoordinateSystem, but the // code was moved from ImageUtilities which makes heavy use // of CoordUtil methods (which aren't available to CoordinateSystem) static uInt addAxes ( CoordinateSystem& csys, Bool direction, Bool spectral, const String& stokes, Bool linear, Bool tabular, Bool silent=False ); // Return a 2-dimensional coordinate system with RA/DEC axes only. static CoordinateSystem defaultCoords2D(); // Return a 3-dimensional coordinate system with RA/DEC axes and a spectral axis. static CoordinateSystem defaultCoords3D(); // Return a 4-dimensional coordinate system with RA/DEC axes, an IQUV // polarisation axis and a spectral axis. static CoordinateSystem defaultCoords4D(); // Calls one of the above three functions depending of the arguement. An // AipsError is thrown if dims is not 2, 3, or 4. static CoordinateSystem defaultCoords(uInt dims); // If doLinear=False, Tries to make a standard RA/DEC/Stokes/Frequency CoordinateSystem // depending upon the shape. The shape for the Stokes axis // must be <= 4. If axis 2 can't be Stokes it will be a Spectral // axis instead. AFter the standard types, the rest (if any) // of the CoordinateSystem consists of LinearCoordinates. // If doLinear=True, then you just get a linear coordinate system static CoordinateSystem makeCoordinateSystem(const IPosition& shape, Bool doLinear=False); // // Find which pixel axis in the CoordinateSystem corresponds to the // SpectralCoordinate. If there is no SpectralCoordinate in the coordinate // system then return -1. static Int findSpectralAxis(const CoordinateSystem & coords); // Find the SpectralCoordinate in the CoordinateSystem, and then // return the most general description of where it is. // If there is no SpectralCoordinate in the CoordinateSystem then return // -1 for coordinate. If the world or pixel axis has been removed, // return -1 for that value. static void findSpectralAxis(Int& pixelAxis, Int& worldAxis, Int& coordinate, const CoordinateSystem & coords); // Find which pixel axes correspond to the DirectionCoordinate in the // supplied coordinate system and return this as a Vector. If there is no // DirectionCoordinate in the CoordinateSystem then return a Vector of zero // length. Normally the returned Vector will have a length of two. // However, if the pixel axis has been removed, then the resultant // vector will take the value -1 for that axis. static Vector findDirectionAxes(const CoordinateSystem & coords); // Find which pixel axes correspond to the DirectionCoordinate in the supplied coordinate // system and return the most general description of where it is. If there is // no DirectionCoordinate then coordinate is returned with value -1. // Values of -1 in the returned vectors indicate an axis has been removed. static void findDirectionAxes(Vector& pixelAxes, Vector& worldAxes, Int& coordinate, const CoordinateSystem & coords); // Find which pixel axis is the polarisation axis in the supplied // CoordinateSystem and return this. If there is no StokesCoordinate in the // CoordinateSystem return a negative number. The actual polarisations on the // returned pixel axis are returned in the whichPols Vector. Each element of // this Vector is a Stokes::StokesTypes enumerator and the length of the Vector // is the same as the length of the polarisation axis. If there is no // polarisation axis the whichPols returns a unit length Vector containing // Stokes::I static Int findStokesAxis(Vector& whichPols, const CoordinateSystem& coords); // Find the StokesCoordinate in the CoordinateSystem, and then // return the most general description of where it is. // If there is no StokesCoordinate in the CoordinateSystem then return // -1 for coordinate. If the world or pixel axis has been removed, // return -1 for that value. static void findStokesAxis(Int& pixelAxis, Int& worldAxis, Int& coordinate, const CoordinateSystem & coords); // Find Coordinate type for this pixel or world axis // static Coordinate::Type findPixelAxis (const CoordinateSystem& cSys, Int axis); static Coordinate::Type findWorldAxis (const CoordinateSystem& cSys, Int axis); // // Remove a list of world axes and their associated // pixel axes from a CoordinateSystem. The list of world // axes to be removed is derived from a list giving either axes to remove, // or axes to keep (controlled by whether remove // is True or False. The replacement values (see functions // CoordinateSystem::removeWorldAxis) for the world axes // can be given. For the associated pixel axes, the pixel replacement // coordinate is found by converting the world coordinate // to a pixel coordinate. If the length of the replacement value // vector is not the number of world axes to be removed then // the reference values will be used (e.g. use zero length // vectors). static Bool removeAxes(CoordinateSystem& cSys, Vector& worldReplacement, const Vector& worldAxes, const Bool remove); // Remove a list of pixel axes but not their associated // world axes from a CoordinateSystem. // The list of pixel axes to be removed is derived from a // list giving either axes to remove, // or axes to keep (controlled by whether remove // is True or False. The replacement values (see functions // CoordinateSystem::removePixelAxis) for the pixel axes // can be given. If the length of the replacement value // vector is not the number of pixel axes to be removed then // the reference pixel will be used (e.g. use zero length // vectors). static Bool removePixelAxes(CoordinateSystem& cSys, Vector& pixelReplacement, const Vector& pixelAxes, const Bool remove); // Physically (nont just virtually) drop coordinates from the CoordinateSystem // if all axes are fully removed. For coordinates with axes partially removed // (world/pixel) preserve that removal state in the output CS. No effort // is made to deal in any way with transposed systems, unless perserveAxesOrder // is True, and then the ordering of the axes of the output coordinate system // will be the same as the input cSysIn (sans dropped axes of course). static Bool dropRemovedAxes ( CoordinateSystem& cSysOut, const CoordinateSystem& cSysIn, Bool preserveAxesOrder=False ); // Setup Measures conversion machine for MDirections. // Returns True if the machine was needed and set. Returns False // if the machine was not needed and not set. static Bool makeDirectionMachine(LogIO& os, MDirection::Convert& machine, const DirectionCoordinate& dirCoordTo, const DirectionCoordinate& dirCoordFrom, const ObsInfo& obsTo, const ObsInfo& obsFrom); // Setup Measures conversion machines for MFrequencies. // Returns False if a trial conversion failed, else returns True. // There must be both a Direction and a Spectral // Coordinate in the CoordinateSystem when making the Frequency machine, // else an exception occurs. static Bool makeFrequencyMachine(LogIO& os, MFrequency::Convert& machine, Int coordinateTo, Int coordinateFrom, const CoordinateSystem& coordsTo, const CoordinateSystem& coordsFrom, const Unit& unit=Unit(String("Hz"))); // Setup Measures conversion machines for MFrequencies. // Returns False if a trial conversion failed, else returns True. static Bool makeFrequencyMachine(LogIO& os, MFrequency::Convert& machine, MFrequency::Types typeTo, MFrequency::Types typeFrom, const MDirection& dirTo, const MDirection& dirFrom, const MEpoch& epochTo, const MEpoch& epochFrom, const MPosition& posTo, const MPosition& posFrom, const Unit& unit=Unit(String("Hz"))); // Find the Sky in the CoordinateSystem. Assumes only one DirectionCoordinate. // pixelAxes and worldAxes say where // in the CS the DirectionCoordinate axes are (long then lat). // Returns False and an error message if it can't find the sky. static Bool findSky(String& errorMessage, Int& dirCoord, Vector& pixelAxes, Vector& worldAxes, const CoordinateSystem& cSys); // Do the specified axes hold the sky ? Returns False if no DirectionCoordinate // or if only one axis of the DirectionCoordinate is held or the specified // pixel axes don't pertain to the DirectionCoordinate. static Bool holdsSky (Bool& holdsOneSkyAxis, const CoordinateSystem& cSys, Vector pixelAxes); // Find the Stokes for the specified pixel. If there is no Stokes in the // CoordinateSystem, returns Stokes::I static Stokes::StokesTypes findSingleStokes (LogIO& os, const CoordinateSystem& cSys, uInt pixel=0); // Set the world axis units in the CS to 'deg' for Direction. For Spectral // set the velocity handling to use 'km/s' units. Other coordinates // are not touched. static void setNiceAxisLabelUnits(CoordinateSystem& cSys); // Set world axis units for specific Coordinate. Returnd False if fails to set units // with error in cSys.errorMessage(). static Bool setCoordinateUnits (CoordinateSystem& cSys, const Vector& units, uInt which); // Set a unit for all unremoved world axes in the DirectionCoordinate in the // CS. Returns False if fails to set unit with error in cSys. If no DC // returns True static Bool setDirectionUnit (CoordinateSystem& cSys, const String& unit, Int which=-1); // Set Direction conversion layer of DirectionCoordinate in CoordinateSystem // so that pixel<->world go to the specified direction system (a valid // MDirection::Types string). Returns False with error if direction // system invalid. If no DirectionCoordinate returns True static Bool setDirectionConversion (String& errorMsg, CoordinateSystem& cSys, const String directionSystem); // Set spectral state of SpectralCoordinate in CoordinateSystem. // Unit must be consistent with Hz or m/s and the doppler a valid MDoppler string. // For no change, leave either String empty. // Returns False if invalid inputs (and CS not changed) and an error message. static Bool setSpectralState (String& errorMsg, CoordinateSystem& cSys, const String& unit, const String& spcquant); // Set velocity state of SpectralCoordinate in CoordinateSystem. // Unit must be consistent m/s and the doppler a valid MDoppler string. // For no change, leave either String empty. // Returns False if invalid inputs (and CS not changed) and an error message. static Bool setVelocityState (String& errorMsg, CoordinateSystem& cSys, const String& unit, const String& spcquant); //#/// Kept setRestFrequency for CASA-4.2 // Does the CoordinateSystem hold just the sky? // Returns True if CS pixel axis 0 is the longitude and 1 latitude // else returns False static Bool isSky (LogIO& os, const CoordinateSystem& cSys); //#/// Kept setRestFrequency for CASA-4.2 // Set rest frequency of SpectralCoordinate in CoordinateSystem. // Unit must be consistent with Hz or m. // Returns False if invalid inputs (and CS not changed) and an error message. static Bool setRestFrequency (String& errorMsg, CoordinateSystem& cSys, const String& unit, const Double& value); //#/// Kept setSpectralConversion for old casarest // Set Spectral conversion layer of SpectralCoordinate in CoordinateSystem // so that pixel<->world go to the specified frequency system (a valid // MFrequency::Types string). Returns False if frequency system invalid // or if no DirectionCoordinate or if cant get Date/Epoch static Bool setSpectralConversion (String& errorMsg, CoordinateSystem& cSys, const String frequencySystem); // Set default format unit and doppler velocity state of SpectralCoordinate in CoordinateSystem. // Unit can be consistent with Hz or m/s // Returns False if invalid inputs (and CS not changed) and an error message. static Bool setSpectralFormatting (String& errorMsg, CoordinateSystem& cSys, const String& unit, const String& spcquant); // Convert an absolute pixel coordinate to world and format with // default Coordinate formatting // static String formatCoordinate(const IPosition& pixel, const CoordinateSystem& cSys, Int precision = -1); static String formatCoordinate(const Vector& pixel, const CoordinateSystem& cSys, Int precision = -1); // // Generate axis label String from coordinate. Specify coordinate axis, // whether world or pixel labels required, whether absolute or // relative. For spectral coordinates, doVel says if you want to // use the velocity information contained in it to generate the label static String axisLabel (const Coordinate& coord, uInt axisInCoordinate=0, Bool doWorld=True, Bool doAbs=True, Bool doVel=False); // // Check how the coordinates of this and that compare. // The return value tells how they compare. //
        -1: left is subset //
        0: equal //
        1: left is superset //
        9: invalid (mismatch) static Int compareCoordinates (const CoordinateSystem& thisCsys, const CoordinateSystem& thatCsys); // Convert the world axes map given in worldAxes to a pixel axes map. static Vector toPixelAxes (const CoordinateSystem& thisCsys, const CoordinateSystem& thatCsys, const Vector& worldAxes); // Check if the axes in the pixel axes map are in ascending order. static Bool checkOrder (const Vector& pixelAxes); // Find the new and stretch axes when comparing the old and new // coordinates and shapes (helper for ExtendImage). static Bool findExtendAxes (IPosition& newAxes, IPosition& stretchAxes, const IPosition& newShape, const IPosition& oldShape, const CoordinateSystem& newCsys, const CoordinateSystem& oldCsys); //
        // Fix up Cylindrical parameters in any DirectionCoordinate for when the longitude // is outside of [-180,180] range. If it returns False, it failed and an error // message is returned as well. This function should be called on any // CS made from an imported image like FITS static Bool cylindricalFix (CoordinateSystem& cSys, String& errorMessage, const IPosition& shape); // Apply the binning factors to the CS and create a new one reflecting the binning // You can optionally throw an exception if factors is non-unit for any Stokes axis static CoordinateSystem makeBinnedCoordinateSystem (const IPosition& factors, const CoordinateSystem& cSysIn, Bool failOnStokes=False); private: // Sets pos to the position found for tel in the database, or // raises an exception + error message. static void findObservatoryOrRaiseException(LogIO& os, MPosition& pos, const String& tel); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/coordinates/Coordinates/Direction2Coordinate.cc000066400000000000000000000071251476623553700244360ustar00rootroot00000000000000//# Direction2Coordinate.cc: this defines measures related DirectionCoordinate code //# Copyright (C) 1997,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include // A different file so that apps which don't need measures don't link them all // in (measures bring in tables and lots of other stuff) namespace casacore { //# NAMESPACE CASACORE - BEGIN Bool DirectionCoordinate::toWorld(MDirection &world, const Vector &pixel) const { static MVDirection world_tmp; if (toWorld(world_tmp, pixel)) { world.set(world_tmp, MDirection::Ref(type_p)); return True; } // return False; } Bool DirectionCoordinate::toWorld(MVDirection &world, const Vector &pixel) const { static Vector world_tmp(2); if (toWorld(world_tmp, pixel)) { world.setAngle(world_tmp(0)*to_radians_p[0], world_tmp(1)*to_radians_p[1]); return True; } return False; } MVDirection DirectionCoordinate::toWorld( const Vector &pixel ) const { MVDirection x; ThrowIf( ! toWorld(x, pixel), errorMessage() ); return x; } Bool DirectionCoordinate::toPixel( Vector &pixel, const MDirection &world ) const { if (type_p == MDirection::castType(world.getRef().getType())) { return toPixel(pixel, world.getValue()); } else { MDirection converted = MDirection::Convert(world, type_p)(); return toPixel(pixel, converted.getValue()); } } Bool DirectionCoordinate::toPixel(Vector &pixel, const MVDirection &world) const { static Vector world_tmp(2); // Convert to current units world_tmp(0) = world.getLong() / to_radians_p[0]; world_tmp(1) = world.getLat() / to_radians_p[1]; // return toPixel(pixel, world_tmp); } Vector DirectionCoordinate::toPixel(const MVDirection &world) const { Vector x; ThrowIf( ! toPixel(x, world), errorMessage() ); return x; } Vector DirectionCoordinate::toPixel(const MDirection &world) const { Vector x; ThrowIf( ! toPixel(x, world), errorMessage() ); return x; } } //# NAMESPACE CASACORE - END casacore-3.7.1/coordinates/Coordinates/DirectionCoordinate.cc000066400000000000000000001746621476623553700243670ustar00rootroot00000000000000//# DirectionCoordinate.cc: this defines the DirectionCoordinate class //# Copyright (C) 1997,1998,1999,2000,2001,2002,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN DirectionCoordinate::DirectionCoordinate() : Coordinate(), type_p(MDirection::J2000), conversionType_p(type_p), projection_p(Projection(Projection::CAR)), names_p(axisNames(type_p,False).copy()), pConversionMachineTo_p(0), pConversionMachineFrom_p(0) { Matrix xform(2,2); xform = 0.0; xform.diagonal() = 1.0; makeDirectionCoordinate (type_p, projection_p, 0.0, 0.0, 1.0, 1.0, xform, 0.0, 0.0, 999.0, 999.0); setDefaultWorldMixRanges(); setRotationMatrix(); } DirectionCoordinate::DirectionCoordinate(MDirection::Types directionType, const Projection &projection, Double refLong, Double refLat, Double incLong, Double incLat, const Matrix &xform, Double refX, Double refY, Double longPole, Double latPole) : Coordinate(), type_p(directionType), conversionType_p(type_p), projection_p(projection), names_p(axisNames(directionType,False).copy()), units_p(2), pConversionMachineTo_p(0), pConversionMachineFrom_p(0) { makeDirectionCoordinate (type_p, projection_p, refLong, refLat, incLong, incLat, xform, refX, refY, longPole, latPole); setDefaultWorldMixRanges(); setRotationMatrix(); } DirectionCoordinate::DirectionCoordinate(MDirection::Types directionType, const ::wcsprm& wcs, Bool oneRel) : Coordinate(), type_p(directionType), conversionType_p(type_p), names_p(axisNames(type_p,False).copy()), pConversionMachineTo_p(0), pConversionMachineFrom_p(0) { // Check wcs structure if (wcs.naxis!=2) { throw(AipsError("wcs structure must have 2 celestial axes")); } // Create Projection (throw away projection_p ?) String ctypeLon(wcs.ctype[0]); String ctypeLat(wcs.ctype[1]); uInt n = wcs.npv; Vector pars(n); for (uInt i=0; i& refLong, const Quantum& refLat, const Quantum& incLong, const Quantum& incLat, const Matrix &xform, Double refX, Double refY, const Quantum& longPole, const Quantum& latPole) : Coordinate(), type_p(directionType), conversionType_p(type_p), projection_p(projection), names_p(axisNames(directionType,False).copy()), units_p(2), pConversionMachineTo_p(0), pConversionMachineFrom_p(0) { Unit rad("rad"); // if (!refLong.isConform(rad)) { throw(AipsError("Specified longitude is not angular")); } if (!refLat.isConform(rad)) { throw(AipsError("Specified latitude is not angular")); } if (!incLong.isConform(rad)) { throw(AipsError("Specified longitude increment is not angular")); } if (!incLat.isConform(rad)) { throw(AipsError("Specified latitude increment is not angular")); } // Double lon = refLong.getValue(rad); Double lat = refLat.getValue(rad); Double dlon = incLong.getValue(rad); Double dlat = incLat.getValue(rad); // Double dLongPole = longPole.getValue(); Double dLatPole = latPole.getValue(); // // Any value >=999 is taken as the default, regardless of units // if (dLongPole < 999.0) { dLongPole = longPole.getValue(rad); } else { dLongPole = 999.0; } if (dLatPole < 999.0) { dLatPole = latPole.getValue(rad); } else { dLatPole = 999.0; } // makeDirectionCoordinate (type_p, projection_p, lon, lat, dlon, dlat, xform, refX, refY, dLongPole, dLatPole); setDefaultWorldMixRanges(); setRotationMatrix(); } DirectionCoordinate::DirectionCoordinate(const DirectionCoordinate &other) : Coordinate(other), pConversionMachineTo_p(0), pConversionMachineFrom_p(0) { wcs_p.flag = -1; // Says not initialized copy(other); } DirectionCoordinate &DirectionCoordinate::operator=(const DirectionCoordinate &other) { if (this != &other) { Coordinate::operator=(other); copy(other); } return *this; } DirectionCoordinate::~DirectionCoordinate() { if (wcs_p.flag != -1) { wcsfree(&wcs_p); } // if (pConversionMachineTo_p) { delete pConversionMachineTo_p; pConversionMachineTo_p = 0; } if (pConversionMachineFrom_p) { delete pConversionMachineFrom_p; pConversionMachineFrom_p = 0; } } Coordinate::Type DirectionCoordinate::type() const { return Coordinate::DIRECTION; } String DirectionCoordinate::showType() const { return String("Direction"); } uInt DirectionCoordinate::nPixelAxes() const { return 2; } uInt DirectionCoordinate::nWorldAxes() const { return 2; } void DirectionCoordinate::setReferenceConversion (MDirection::Types type) { // See if something to do if (conversionType_p==type) return; // If conversion type the same as the native type, just // remove the machines conversionType_p = type; if (pConversionMachineTo_p) { delete pConversionMachineTo_p; pConversionMachineTo_p = 0; } if (pConversionMachineFrom_p) { delete pConversionMachineFrom_p; pConversionMachineFrom_p = 0; } if (conversionType_p == type_p) return; // Now make new machines as needed makeConversionMachines(); } Bool DirectionCoordinate::toWorld(Vector &world, const Vector &pixel, Bool useConversionFrame) const { // To World with wcs if (toWorldWCS (world, pixel, wcs_p)) { // To current units from wcs units toCurrent (world); // Convert to specified conversion reference type if (useConversionFrame) { convertTo(world); } return True; } else { return False; } } Bool DirectionCoordinate::toPixel(Vector &pixel, const Vector &world) const { static Vector world_tmp; DebugAssert(world.nelements() == nWorldAxes(), AipsError); // Convert from specified conversion reference type world_tmp.resize(nWorldAxes()); world_tmp[0] = world[0]; world_tmp[1] = world[1]; convertFrom(world_tmp); // To degrees for wcs fromCurrent (world_tmp); // To pixel with wcs return toPixelWCS(pixel, world_tmp, wcs_p); } Bool DirectionCoordinate::toMix(Vector& worldOut, Vector& pixelOut, const Vector& worldIn, const Vector& pixelIn, const Vector& worldAxes, const Vector& pixelAxes, const Vector& worldMin, const Vector& worldMax) const { Bool useConversionType = False; // Temporaries static Vector in_tmp; static Vector out_tmp; // const uInt nPixel = pixelAxes.nelements(); DebugAssert(worldAxes.nelements()==nWorldAxes(), AipsError); DebugAssert(pixelAxes.nelements()==nPixelAxes(), AipsError); DebugAssert(worldIn.nelements()==worldAxes.nelements(), AipsError); DebugAssert(pixelIn.nelements()==pixelAxes.nelements(), AipsError); DebugAssert(worldMin.nelements()==worldAxes.nelements(), AipsError); DebugAssert(worldMax.nelements()==worldAxes.nelements(), AipsError); // for (uInt i=0; iworld // if (!toWorld(worldOut, pixelIn)) return False; pixelOut[0] = pixelIn[0]; pixelOut[1] = pixelIn[1]; } else if (worldAxes[0] && worldAxes[1]) { // // world->pixel // if (!toPixel(pixelOut, worldIn)) return False; worldOut[0] = worldIn[0]; worldOut[1] = worldIn[1]; } else if (pixelAxes[0] && worldAxes[1]) { // // pixel,world->world,pixel // in_tmp.resize(2); out_tmp.resize(2); // in_tmp[0] = pixelIn[0]; in_tmp[1] = worldIn[1]; if (!toMix2(out_tmp, in_tmp, worldMin, worldMax, False)) { return False; } // pixelOut[0] = in_tmp[0]; pixelOut[1] = out_tmp[1]; worldOut[0] = out_tmp[0]; worldOut[1] = in_tmp[1]; // if (useConversionType) { convertTo(worldOut); } } else if (worldAxes[0] && pixelAxes[1]) { // // world,pixel->pixel,world // in_tmp.resize(2); out_tmp.resize(2); // in_tmp[0] = worldIn[0]; in_tmp[1] = pixelIn[1]; if (!toMix2(out_tmp, in_tmp, worldMin, worldMax, True)) { return False; } // pixelOut[0] = out_tmp[0]; pixelOut[1] = in_tmp[1]; worldOut[0] = in_tmp[0]; worldOut[1] = out_tmp[1]; // if (useConversionType) { convertTo(worldOut); } } return True; } Bool DirectionCoordinate::toWorldMany (Matrix& world, const Matrix& pixel, Vector& failures) const { // To World with wcs if (toWorldManyWCS (world, pixel, failures, wcs_p)) { // Convert to current units from wcs units toCurrentMany (world, toCurrentFactors()); // Convert to specified conversion reference type if (pConversionMachineTo_p) convertToMany(world); } else { return False; } // return True; } Bool DirectionCoordinate::toPixelMany (Matrix& pixel, const Matrix& world, Vector& failures) const { AlwaysAssert(world.nrow()==nWorldAxes(), AipsError); // Copy input as we have to convert it to all sorts of things Matrix world2(world.copy()); // Convert from specified conversion reference type if (pConversionMachineTo_p) convertFromMany (world2); // Convert from current units to wcs units (degrees) fromCurrentMany (world2, toCurrentFactors()); // Convert with wcs to pixel return toPixelManyWCS (pixel, world2, failures, wcs_p); } MDirection::Types DirectionCoordinate::directionType(Bool showConversion) const { if (showConversion) { return conversionType_p; } else { return type_p; } } Projection DirectionCoordinate::projection() const // // SHould return this by construction from wcs object // { return projection_p; } Vector DirectionCoordinate::worldAxisNames() const { return names_p; } Vector DirectionCoordinate::worldAxisUnits() const { return units_p; } Vector DirectionCoordinate::referenceValue() const { Vector crval(2); crval[0] = wcs_p.crval[0]; crval[1] = wcs_p.crval[1]; // degrees toCurrent(crval); return crval; } Vector DirectionCoordinate::increment() const { Vector cdelt(2); cdelt[0] = wcs_p.cdelt[0]; cdelt[1] = wcs_p.cdelt[1]; // degrees toCurrent(cdelt); return cdelt; } Matrix DirectionCoordinate::linearTransform() const { Matrix tmp; pcToXform (tmp, wcs_p); return tmp; } Vector DirectionCoordinate::referencePixel() const { Vector crpix(2); crpix[0] = wcs_p.crpix[0]; crpix[1] = wcs_p.crpix[1]; return crpix; } Bool DirectionCoordinate::setWorldAxisNames(const Vector &names) { if (!(names.nelements()==nWorldAxes())) { set_error("names vector must be of length 2"); return False; } // names_p = names; return True; } Bool DirectionCoordinate::setWorldAxisUnits(const Vector &units) { if (!(units.nelements()==nWorldAxes())) { set_error("units vector must be of length 2"); return False; } // String error; Vector factor; Bool ok = find_scale_factor(error, factor, units, worldAxisUnits()); if (ok) { to_degrees_p[0] /= factor[0]; to_degrees_p[1] /= factor[1]; // to_radians_p[0] = to_degrees_p[0] * C::degree; to_radians_p[1] = to_degrees_p[1] * C::degree; } else { set_error(error); } if (ok) { units_p = units; worldMin_p *= factor; worldMax_p *= factor; } return ok; } Bool DirectionCoordinate::setReferencePixel(const Vector &refPix) { if (!(refPix.nelements()==nPixelAxes())) { set_error("reference pixels vector must be of length 2"); return False; } // //cout << "refPix[0]=" << refPix[0] // << " refPix[1]=" << refPix[1] // << endl; wcs_p.crpix[0] = refPix[0]; wcs_p.crpix[1] = refPix[1]; set_wcs(wcs_p); // return True; } Bool DirectionCoordinate::setLinearTransform(const Matrix &xform) { Bool ok = (xform.nrow() == nWorldAxes() && xform.ncolumn() == nWorldAxes()); if (!ok) { set_error("linear transform matrix has wrong shape"); return False; } // Set PC cards xFormToPC (wcs_p, xform); set_wcs(wcs_p); normalizePCMatrix(); // return True; } Bool DirectionCoordinate::setIncrement(const Vector &inc) { Bool ok = (inc.nelements()==nWorldAxes()); if (!ok) { set_error("Two increments must be provided!"); return False; } // Vector tmp(inc.copy()); fromCurrent(tmp); wcs_p.cdelt[0] = tmp[0]; wcs_p.cdelt[1] = tmp[1]; set_wcs(wcs_p); // return ok; } Bool DirectionCoordinate::setReferenceValue(const Vector &refval) { Bool ok = (refval.nelements()==nWorldAxes()); if (!ok) { set_error("Two ref. values must be provided!"); return False; } // Vector tmp(refval.copy()); fromCurrent(tmp); // // special treatment for SFL projection (see Calabretta & Greisen 2002) if(projection_p.type() == Projection::SFL){ if (wcs_p.cdelt[1] != 0. && (!(wcs_p.altlin&4) || wcs_p.crota[1]==0.) ){ // Force reference point to lat = 0 if CROTA is not set or is zero // to avoid "wcsset_error: Ill-conditioned coordinate transformation parameters" wcs_p.crpix[1] -= tmp[1]/wcs_p.cdelt[1]; tmp[1] = 0.; } } // wcs_p.crval[0] = tmp[0]; wcs_p.crval[1] = tmp[1]; set_wcs(wcs_p); // Update offset coordinate rotation matrix setRotationMatrix(); // return ok; } Vector DirectionCoordinate::axisNames(MDirection::Types type, Bool FITSName) { Vector names(2); if (FITSName) { switch(type) { case MDirection::J2000: case MDirection::JMEAN: case MDirection::APP: case MDirection::B1950: case MDirection::B1950_VLA: case MDirection::BMEAN: case MDirection::BTRUE: case MDirection::ICRS: names[0] = "RA"; names[1] = "DEC"; break; case MDirection::GALACTIC: names[0] = "GLON"; names[1] = "GLAT"; break; case MDirection::SUPERGAL: names[0] = "SLON"; names[1] = "SLAT"; break; case MDirection::ECLIPTIC: case MDirection::MECLIPTIC: case MDirection::TECLIPTIC: names[0] = "ELON"; names[1] = "ELAT"; break; case MDirection::HADEC: names[0] = "RA"; // HA not recognized by WCS names[1] = "DEC"; break; /* // WCS does not recognize AZEL case MDirection::AZEL: case MDirection::AZELSW: names[0] = "AZ"; names[1] = "EL"; break; */ default: names[0] = "??LN"; names[1] = "??LT"; } } else { switch(type) { case MDirection::J2000: // These are all RA/DEC systems case MDirection::JMEAN: case MDirection::APP: case MDirection::B1950: case MDirection::B1950_VLA: case MDirection::BMEAN: case MDirection::BTRUE: case MDirection::ICRS: names[0] = "Right Ascension"; names[1] = "Declination"; break; case MDirection::GALACTIC: case MDirection::SUPERGAL: case MDirection::ECLIPTIC: case MDirection::MECLIPTIC: case MDirection::TECLIPTIC: names[0] = "Longitude"; names[1] = "Latitude"; break; case MDirection::HADEC: names[0] = "Hour Angle"; names[1] = "Declination"; break; /* // WCS does not recognize AZEL case MDirection::AZEL: case MDirection::AZELSW: names[0] = "Azimuth"; names[1] = "Elevation"; break; */ default: names[0] = "Longitude"; names[1] = "Latitude"; } } return names; } Bool DirectionCoordinate::isNCP() const { if (projection_p.type() == Projection::SIN) { Vector pars = projection_p.parameters(); if (pars.size() == 2 && (anyNE(pars, 0.0)) && pars[0] == 0) { Quantity dec(referenceValue()[1], worldAxisUnits()[1]); return ( dec.getValue() != 0 && casacore::near(pars[1], 1/tan(dec.getValue("rad"))) ); } } return False; } void DirectionCoordinate::checkFormat(Coordinate::formatType& format, Bool absolute) const { MDirection::GlobalTypes gtype = MDirection::globalType(type_p); if (format == Coordinate::DEFAULT) { if (gtype == MDirection::GRADEC || gtype == MDirection::GHADEC) { if (absolute) { format = Coordinate::TIME; } else { format = Coordinate::SCIENTIFIC; } } else if (gtype == MDirection::GLONGLAT) { format = Coordinate::FIXED; } else if (gtype == MDirection::GAZEL) { format = Coordinate::FIXED; } else { format = Coordinate::SCIENTIFIC; } } } DirectionCoordinate DirectionCoordinate::convert( Quantity& angle, MDirection::Types directionType ) const { ThrowIf( ! hasSquarePixels(), "Coordinate rotation can only be performed on a coordinate that has " "square pixels" ); // create the rotated coordinate Matrix xform(2, 2, 0); xform.diagonal() = 1.0; Vector refPix = referencePixel(); Vector inc = increment(); Vector incQ(2); incQ[0] = Quantity(inc[0], units_p[0]); incQ[1] = Quantity(inc[1], units_p[1]); DirectionCoordinate myClone(*this); myClone.setReferenceConversion(directionType); Vector refValNewFrame; ThrowIf( ! myClone.toWorld(refValNewFrame, refPix, True), "Unable to convert reference pixel to world value of new frame" ); Vector refValNewFrameQ(2); refValNewFrameQ[0] = Quantity(refValNewFrame[0], units_p[0]); refValNewFrameQ[1] = Quantity(refValNewFrame[1], units_p[1]); DirectionCoordinate converted( directionType, projection_p, refValNewFrameQ[0], refValNewFrameQ[1], incQ[0], incQ[1], xform, refPix[0], refPix[1] ); // Determine the rotation angle. This is useful for knowing how much to // rotate beams, etc. // zero the reference pixel to avoid precision loss by potentially needing // to subtract two large numbers that differ beyond less than 1 ThrowIf( ! myClone.setReferencePixel(Vector(2, 0)), "Failed to zero reference pixel in temporary coordinate" ); // make the increment and units more friendly for this computation static const String unit = "deg"; ThrowIf( ! myClone.setWorldAxisUnits(Vector(2, unit)), "Failed to set world axis units in temporary coordinate" ); Vector cloneInc(2, 1); for (uInt i=0; i<2; ++i) { if (sign(inc[i] < 0)) { cloneInc[i] = -1; } } myClone.setIncrement(cloneInc); // To get the angle that the conversion ref frame makes with the pixel axes, // get the pixel coordinates of an offset of 1 arcsec along the positive // latitude axis of the new frame. Use the latitude-like coordinate rather // than the longitude like coordinate so we don't have to deal with // cos(latitude) factors. Vector offsetValNewFrameQ = refValNewFrameQ.copy(); // avoid offsetting over the poles by offsetting north if the refval is // south of the equator, offset south if refval is north of or on equator Bool offsetNorth = refValNewFrame[1] < 0; offsetValNewFrameQ[1] += Quantity(offsetNorth ? 1 : -1, unit); Vector offsetValNewFrame(2); offsetValNewFrame[0] = offsetValNewFrameQ[0].getValue(unit); offsetValNewFrame[1] = offsetValNewFrameQ[1].getValue(unit); Vector offsetPixel; ThrowIf( ! myClone.toPixel(offsetPixel, offsetValNewFrame), "Unable to convert offset world value to offset pixel value" ); if (! offsetNorth) { offsetPixel = -offsetPixel; } Double signX = sign(offsetPixel[0]); Double yFrame = offsetPixel[1]; // Remember that acos never returns a negative value Double angleInRad = acos(yFrame/sqrt(sum(offsetPixel*offsetPixel))); // get the quadrant right if (signX > 0) { // we have to rotate the world coordinate counter-clockwise to align // new ref frame world axes with the pixel axes. Our convention in this // implementation is that a postive angle represents a clockwise // rotation (which was probably an unfortunate choice) angleInRad = -angleInRad; } angle = Quantity(angleInRad, "rad"); return converted; } void DirectionCoordinate::getPrecision (Int& precision, Coordinate::formatType& format, Bool absolute, Int defPrecScientific, Int defPrecFixed, Int defPrecTime) const { // Fill in DEFAULT checkFormat(format, absolute); // Set precisions depending upon requested format type and // desire to see absolute or offset coordinate if (format == Coordinate::SCIENTIFIC) { if (defPrecScientific >= 0) { precision = defPrecScientific; } else { precision = 6; } } else if (format == Coordinate::FIXED) { if (defPrecFixed >= 0) { precision = defPrecFixed; } else { precision = 6; } } else if (format == Coordinate::TIME) { if (defPrecTime >= 0) { precision = defPrecTime; } else { precision = 3; } } } String DirectionCoordinate::format(String& units, Coordinate::formatType format, Double worldValue, uInt worldAxis, Bool isAbsolute, Bool showAsAbsolute, Int precision, Bool) const { DebugAssert(worldAxis< nWorldAxes(), AipsError); DebugAssert(nWorldAxes()==2, AipsError); // Convert given world value to absolute or relative as needed static Vector world; world.resize(nWorldAxes()); // if (showAsAbsolute) { if (!isAbsolute) { world = 0.0; world(worldAxis) = worldValue; makeWorldAbsolute(world); worldValue = world(worldAxis); } } else { if (isAbsolute) { world = referenceValue(); world(worldAxis) = worldValue; makeWorldRelative(world); worldValue = world(worldAxis); } } // Fill in DEFAULT format Coordinate::formatType form = format; // Check format. If showAsAbsolute==F, TIME formatting is switched off checkFormat(form, showAsAbsolute); // Set default precision if needed Int prec = precision; if (prec < 0) getPrecision(prec, form, showAsAbsolute, -1, -1, -1); // Set global DirectionCoordinate type MDirection::GlobalTypes gtype = MDirection::globalType(type_p); // Check units static const Unit unitRAD("rad"); String nativeUnit = worldAxisUnits()(worldAxis); if (units.empty()) { // leave empty telling formatLong/Lat to make up something nice } else { Unit unitCurrent(units); if (unitCurrent != unitRAD) { throw (AipsError("Specified unit is invalid")); } } // Convert value to radians first so we can make an MVAngle from it Double worldValue2; worldValue2 = C::degree * worldValue * to_degrees_p[worldAxis]; MVAngle mVA(worldValue2); // We need to put the longitudes into the correct convention // All latitudes better be -90 -> 90 and we don't need to fiddle them // If the units are empty, the formatters will make something up String string; if (worldAxis==0) { string = formatLongitude (units, mVA, gtype, showAsAbsolute, form, prec); } else { string = formatLatitude (units, mVA, showAsAbsolute, form, prec); } // Only return actual units if not TIME formatting if (form==Coordinate::TIME) units = String(""); // return string; } String DirectionCoordinate::formatLongitude (String& units, MVAngle& mVA, MDirection::GlobalTypes gtype, Bool absolute, Coordinate::formatType form, Int prec) const { ostringstream oss; MVAngle mVA2(mVA); // Time formatting we can do straight away if (form==Coordinate::TIME) { // Format with mVA2. TIME formatting really only makes // sense for RA and HA, but we do something with the others // TIME formatting is only possible for absolute=T prec += 6; if (gtype == MDirection::GRADEC) { oss << mVA2.string(MVAngle::TIME,prec); } else if (gtype==MDirection::GHADEC) { oss << mVA2.string(MVAngle::TIME+MVAngle::DIG2,prec); } else if (gtype==MDirection::GAZEL) { oss << mVA2.string(MVAngle::ANGLE,prec); } else if (gtype==MDirection::GLONGLAT) { oss << mVA2.string(MVAngle::ANGLE,prec); } // // oss << ends; return String(oss); } // Continue on with other format types Double value = mVA2.get().getValue(); // Radians Bool emptyUnits = units.empty(); // if (gtype==MDirection::GRADEC){ if (absolute) mVA2 = mVA(0.0); // 0->2pi (0->360) if (emptyUnits) { value = mVA2.get(Unit("deg")).getValue(); units = "deg"; } else { value = mVA2.get(Unit(units)).getValue(); } } else if (gtype==MDirection::GHADEC) { if (absolute) mVA2 = mVA(); // -pi->pi (-180->180) if (emptyUnits) { value = mVA2.get().getValue() * 24.0 / (2.0*M_PI); units = "h"; } else { value = mVA2.get(Unit(units)).getValue(); } } else if (gtype==MDirection::GAZEL) { if (absolute) mVA2 = mVA(0.0); // 0->2pi (0->360) if (emptyUnits) { value = mVA2.get(Unit("deg")).getValue(); units = "deg"; } else { value = mVA2.get(Unit(units)).getValue(); } } else if (gtype==MDirection::GLONGLAT) { if (type_p==MDirection::ECLIPTIC || type_p==MDirection::MECLIPTIC || type_p==MDirection::TECLIPTIC) { if (absolute) mVA2 = mVA(); // -pi->pi (-180->180) } else { if (absolute) mVA2 = mVA(0.0); // 0->2pi (0->360) } if (emptyUnits) { value = mVA2.get(Unit("deg")).getValue(); units = "deg"; } else { value = mVA2.get(Unit(units)).getValue(); } } else { if (absolute) mVA2 = mVA(0.0); // 0->2pi (0->360) value = mVA2.get(Unit(units)).getValue(); } // For relative coordinates, use arcsec if user didnt specify if (!absolute) { if (emptyUnits&& units=="deg") { value *= 3600.0; units = "arcsec"; } } // if (form==Coordinate::SCIENTIFIC) { oss.setf(ios::scientific, ios::floatfield); oss.precision(prec); oss << value; } else if (form==Coordinate::FIXED) { oss.setf(ios::fixed, ios::floatfield); oss.precision(prec); oss << value; } else if (form==Coordinate::MIXED) { oss << value; } // return String(oss); } String DirectionCoordinate::formatLatitude (String& units, MVAngle& mVA, Bool absolute, Coordinate::formatType form, Int prec) const { ostringstream oss; MVAngle mVA2(mVA); // TIME formatting we can do straight away if (form==Coordinate::TIME) { prec += 6; oss << mVA2.string(MVAngle::ANGLE+MVAngle::DIG2,prec); } // Continue on with other format types Double value = mVA2.get().getValue(); // Radians Bool emptyUnits = units.empty(); // if (!emptyUnits) { value = mVA2.get(Unit(units)).getValue(); } else { value = mVA2.get(Unit("deg")).getValue(); units = "deg"; } // if (!absolute) { if (emptyUnits && units=="deg") { value *= 3600.0; units = "arcsec"; } } // if (form==Coordinate::SCIENTIFIC) { oss.setf(ios::scientific, ios::floatfield); oss.precision(prec); oss << value; } else if (form==Coordinate::FIXED) { oss.setf(ios::fixed, ios::floatfield); oss.precision(prec); oss << value; } else if (form==Coordinate::MIXED) { oss << value; } return String(oss); } Coordinate *DirectionCoordinate::clone() const { return new DirectionCoordinate(*this); } Bool DirectionCoordinate::near(const Coordinate& other, Double tol) const { Vector excludeAxes; return near(other, excludeAxes, tol); } Bool DirectionCoordinate::near(const Coordinate& other, const Vector& excludeAxes, Double tol) const { if (this->type() != other.type()) { set_error("Comparison is not with another DirectionCoordinate"); return False; } // const DirectionCoordinate& dCoord = dynamic_cast(other); // Projection if (!projection_p.near(dCoord.projection_p, tol)) { set_error("The DirectionCoordinates have differing projections"); return False; } // Type if (type_p != dCoord.type_p) { set_error("The DirectionCoordinates have differing types"); return False; } // Conversion type if (conversionType_p != dCoord.conversionType_p) { // Should this be an error or a warning ? set_error("The DirectionCoordinates have differing conversion types"); return False; } // Number of pixel and world axes is the same for a DirectionCoordinate AlwaysAssert(nPixelAxes()==nWorldAxes(), AipsError); Vector exclude(nPixelAxes()); exclude = False; Bool found; const uInt nExcl = excludeAxes.nelements(); if (nExcl > 0) { for (uInt i=0; i= 0) exclude[i] = True; } } // Check names ostringstream oss; if (names_p.nelements() != dCoord.names_p.nelements()) { set_error("The DirectionCoordinates have differing numbers of world axis names"); return False; } for (uInt i=0; i& thisVal = referenceValue(); const Vector& thatVal = dCoord.referenceValue(); if (thisVal.nelements() != thatVal.nelements()) { set_error("The DirectionCoordinates have differing reference values"); return False; } for (uInt i=0; i projparms(subrec.toArrayDouble("projection_parameters")); Projection proj(Projection::type(projname), projparms); // if (!subrec.isDefined("crval")) { ostringstream oss; oss << "DirectionCoordinate::restore: " << "crval is not defined."; throw AipsError(oss.str()); } Vector crval(subrec.toArrayDouble("crval")); // if (!subrec.isDefined("crpix")) { ostringstream oss; oss << "DirectionCoordinate::restore: " << "crpix is not defined."; throw AipsError(oss.str()); } Vector crpix(subrec.toArrayDouble("crpix")); // if (!subrec.isDefined("cdelt")) { ostringstream oss; oss << "DirectionCoordinate::restore: " << "cdelt is not defined."; throw AipsError(oss.str()); } Vector cdelt(subrec.toArrayDouble("cdelt")); // if (!subrec.isDefined("pc")) { ostringstream oss; oss << "DirectionCoordinate::restore: " << "pc is not defined."; throw AipsError(oss.str()); } Matrix pc(subrec.toArrayDouble("pc")); // Double longPole, latPole; longPole = latPole = 999.0; // Optional if (subrec.isDefined("longpole")) { subrec.get("longpole", longPole); // Always degrees longPole *= C::degree; } if (subrec.isDefined("latpole")) { subrec.get("latpole", latPole); // Always degrees latPole *= C::degree; } // if (!subrec.isDefined("axes")) { ostringstream oss; oss << "DirectionCoordinate::restore: " << "axes is not defined."; throw AipsError(oss.str()); } Vector axes; subrec.get("axes", axes); // if (!subrec.isDefined("units")) { ostringstream oss; oss << "DirectionCoordinate::restore: " << "units is not defined."; throw AipsError(oss.str()); } Vector units; subrec.get("units", units); // Dummy DC which we overwrite. Use refVal [0.0, 0.5] rather than [0.0, 0.0] // to avoid divide by zero in FITSCoordinateUtil::cTypeFromDirection function. // See that function for further comments // Quantum refLong(crval[0], units[0]); Quantum refLat(crval[1], units[1]); Quantum incLong(cdelt[0], units[0]); Quantum incLat(cdelt[1], units[1]); DirectionCoordinate* pDirection = new DirectionCoordinate(sys, proj, refLong.getValue(Unit("rad")), refLat.getValue(Unit("rad")), incLong.getValue(Unit("rad")), incLat.getValue(Unit("rad")), pc, crpix[0], crpix[1], longPole, latPole); AlwaysAssert(pDirection, AipsError); // Set the actual units and names pDirection->setWorldAxisUnits(units); pDirection->setWorldAxisNames(axes); // Set the conversion type if it exists if (subrec.isDefined("conversionSystem")) { String conversionSystem; subrec.get("conversionSystem", conversionSystem); MDirection::Types cSystem; Bool ok = MDirection::getType(cSystem, conversionSystem); if (ok) { pDirection->setReferenceConversion(cSystem); } } // return pDirection; } void DirectionCoordinate::setProjection(const Projection& p) { Matrix xform; pcToXform(xform, wcs_p); projection_p = p; makeWCS (wcs_p, xform, projection_p, type_p, wcs_p.crpix[0], wcs_p.crpix[1], wcs_p.crval[0], wcs_p.crval[1], wcs_p.cdelt[0], wcs_p.cdelt[1], wcs_p.lonpole, wcs_p.latpole); } void DirectionCoordinate::setReferenceFrame(const MDirection::Types rf) { Matrix xform; pcToXform(xform, wcs_p); type_p = rf; makeWCS (wcs_p, xform, projection_p, type_p, wcs_p.crpix[0], wcs_p.crpix[1], wcs_p.crval[0], wcs_p.crval[1], wcs_p.cdelt[0], wcs_p.cdelt[1], wcs_p.lonpole, wcs_p.latpole); } Bool DirectionCoordinate::hasSquarePixels() const { Vector inc = increment(); return casacore::near(fabs(inc[0]), fabs(inc[1]), 2e-10); } void DirectionCoordinate::toCurrent(Vector& value) const { value[0] /= to_degrees_p[0]; value[1] /= to_degrees_p[1]; } const Vector DirectionCoordinate::toCurrentFactors () const { return 1.0 / to_degrees_p; } void DirectionCoordinate::fromCurrent(Vector& value) const { value[0] *= to_degrees_p[0]; value[1] *= to_degrees_p[1]; } Bool DirectionCoordinate::toMix2(Vector& out, const Vector& in, const Vector& minWorld, const Vector& maxWorld, Bool longIsWorld) const // // vectors must be of length 2. no checking // { // // Temporaries // int mixpix; int mixcel; double mix_vspan[2]; double mix_world[2]; double mix_pixcrd[2]; double mix_imgcrd[2]; double mix_vstep; int mix_viter; double mix_phi, mix_theta; String errorMsg; // // Set input world/pixel arrays // if (longIsWorld) { mixcel = 1; // 1 or 2 (a code, not an index) mixpix = 1; // index into pixcrd array // mix_world[wcs_p.lng] = in[0] * to_degrees_p[0]; mix_pixcrd[mixpix] = in[1]; // // Latitude span // mix_vspan[0] = minWorld[1] * to_degrees_p[1]; mix_vspan[1] = maxWorld[1] * to_degrees_p[1]; } else { mixcel = 2; // 1 or 2 (a code, not an index) mixpix = 0; // index into pixcrd array // mix_world[wcs_p.lat] = in[1] * to_degrees_p[1]; mix_pixcrd[mixpix] = in[0]; // // Longitude span // mix_vspan[0] = minWorld[0] * to_degrees_p[0]; mix_vspan[1] = maxWorld[0] * to_degrees_p[0]; } // // Do it // // mix_vstep = 1.0; mix_vstep = 0.0; mix_viter = 5; int iret = wcsmix(&wcs_p, mixpix, mixcel, mix_vspan, mix_vstep, mix_viter, mix_world, &mix_phi, &mix_theta, mix_imgcrd, mix_pixcrd); if (iret!=0) { errorMsg= "wcs wcsmix_error: "; errorMsg += wcsmix_errmsg[iret]; set_error(errorMsg); return False; } // // Fish out the results // if (longIsWorld) { out[0] = mix_pixcrd[0]; out[1] = mix_world[wcs_p.lat] / to_degrees_p[1]; } else { out[0] = mix_world[wcs_p.lng] / to_degrees_p[0]; out[1] = mix_pixcrd[1]; } // Phi and theta may be returned in a different interface somewhen // Could assign these in toMix rather than pass them out // but not very clear // phi = mix_phi / to_degrees_p[0]; // theta = mix_theta / to_degrees_p[1]; // return True; } Coordinate* DirectionCoordinate::makeFourierCoordinate( const Vector& axes, const Vector& shape ) const { // axes says which axes in the coordinate are to be transformed // shape is the shape of the image for all axes in this coordinate AlwaysAssert(nPixelAxes()==2, AipsError); AlwaysAssert(nWorldAxes()==2, AipsError); if (axes.nelements() != 2) { set_error ("Invalid number of specified axes"); return 0; } if (!axes[0] || !axes[1]) { set_error ("You must specify both axes of the DirectionCoordinate to transform"); return 0; } if (shape.nelements() != 2) { set_error ("Invalid number of elements in shape"); return 0; } // Find names and units for Fourier coordinate and units to set // for this DirectionCoordinate Vector names = worldAxisNames().copy(); Vector units = worldAxisUnits().copy(); // Not copying causes a reference to this object's units to be passed // in to the fourierUnits() call below and they can be changed. // That would be bad. Vector unitsCanon = worldAxisUnits().copy(); Vector namesOut(worldAxisNames().copy()); Vector unitsOut(worldAxisUnits().copy()); fourierUnits( namesOut[0], unitsOut[0], unitsCanon[0], Coordinate::DIRECTION, 0, units[0], names[0] ); fourierUnits( namesOut[1], unitsOut[1], unitsCanon[1], Coordinate::DIRECTION, 1, units[1], names[1] ); // Make a copy of ourselves and set the new units DirectionCoordinate dc = *this; if (!dc.setWorldAxisUnits(unitsCanon)) { set_error ("Could not set world axis units"); return 0; } // Create a LinearXform to do the inversion Vector cdelt = dc.increment().copy(); dc.fromCurrent(cdelt); LinearXform linear(dc.referencePixel(), cdelt, dc.linearTransform()); // Now make the new output LinearCoordinate. Vector crpix(2), scale(2), crval(2,0.0); crpix[0] = Int(shape[0] / 2); crpix[1] = Int(shape[1] / 2); scale[0] = dc.to_degrees_p[0] / Double(shape[0]); scale[1] = dc.to_degrees_p[1] / Double(shape[1]); String errMsg; std::unique_ptr pLinearF( linear.fourierInvert(errMsg, axes, crpix, scale) ); if (pLinearF==0) { set_error (errMsg); return 0; } return new LinearCoordinate( namesOut, unitsOut, crval, pLinearF->cdelt(), pLinearF->pc(), pLinearF->crpix() ); } void DirectionCoordinate::makeDirectionCoordinate(MDirection::Types directionType, const Projection& proj, Double refLong, Double refLat, Double incLong, Double incLat, const Matrix& xform, Double refX, Double refY, Double longPole, Double latPole) { initializeFactors(); // Double longPole2 = longPole; Double latPole2 = latPole; if (longPole < 999.0) { longPole2 = longPole * to_degrees_p[0]; } if (latPole < 999.0) { latPole2 = latPole * to_degrees_p[1]; } // makeWCS(wcs_p, xform, proj, directionType, refX, refY, refLong*to_degrees_p[0], refLat*to_degrees_p[1], incLong*to_degrees_p[0], incLat*to_degrees_p[1], longPole2, latPole2); } Bool DirectionCoordinate::cylindricalFix (Int shapeLong, Int shapeLat) // // Fix up Cylindrical parameters for when longitude outside of [-180,180] range // This has to be in its own function because the image shape intrudes. // { int naxis[2]; naxis[0] = shapeLong; naxis[1] = shapeLat; // Updates crval, crpix, and lonPole int ierr = cylfix (naxis, &wcs_p); // ierr = 0 means successful update. // ierr = -1 means no changed needed LogIO os(LogOrigin("DirectionCoordinate", "cylindricalFix", WHERE)); // if (ierr==-1) { // os << LogIO::NORMAL << "No cylindrical coordinate update was required" << LogIO::POST; return True; } // if (ierr == 0) { // Update internals of DirectionCoordinate Vector refVal(nWorldAxes()); refVal[0] = wcs_p.crval[0]; refVal[1] = wcs_p.crval[1]; toCurrent(refVal); setReferenceValue (refVal); // Vector refPix(nPixelAxes()); refPix[0] = wcs_p.crpix[0]; refPix[1] = wcs_p.crpix[1]; setReferencePixel(refPix); // os << LogIO::NORMAL << "A cylindrical coordinate update was required and applied" << LogIO::POST; } else { set_error(String("DirectionCoordinate::cylindricalFix - ") + String("Could not convert CYL header to [-180,180] longitude range")); return False; } // return True; } Vector DirectionCoordinate::longLatPoles () const { Vector x(4); x[0] = wcs_p.crval[0]; // refLong; x[1] = wcs_p.crval[1]; // refLat; x[2] = wcs_p.lonpole; // longPole x[3] = wcs_p.latpole; // latPole return x; } Quantity DirectionCoordinate::getPixelArea() const { Vector cdelt = increment(); Quantity forUnit = Quantity(1, units_p[0]) * Quantity(1, units_p[1]); return Quantity(fabs(cdelt[0]*cdelt[1]), forUnit.getUnit()); } // These world abs/rel functions are independent of the conversion direction type. void DirectionCoordinate::makeWorldRelative (Vector& world) const { static MVDirection mv; DebugAssert(world.nelements()==2, AipsError); // mv.setAngle(world[0]*to_radians_p[0], world[1]*to_radians_p[1]); mv *= rot_p; // world[0] = cos(mv.getLat()) * mv.getLong() / to_radians_p[0]; world[1] = mv.getLat() / to_radians_p[1]; } void DirectionCoordinate::makeWorldRelative (MDirection& world) const { static MVDirection mv; mv = world.getValue() * rot_p; Double lon = mv.getLong(); Double lat = mv.getLat(); mv.setAngle(cos(lat)*lon, lat); world.set(mv); } void DirectionCoordinate::makeWorldAbsolute (Vector& world) const { static MVDirection mv; DebugAssert(world.nelements()==2, AipsError); // Double lat = world[1]*to_radians_p[1]; mv.setAngle(world[0]*to_radians_p[0]/cos(lat), lat); mv = rot_p * mv; // world[0] = mv.getLong() / to_radians_p[0]; world[1]= mv.getLat() / to_radians_p[1]; } void DirectionCoordinate::makeWorldAbsoluteRef (Vector& world, const Vector& refVal) const { static MVDirection mv; DebugAssert(world.nelements()==2, AipsError); DebugAssert(refVal.nelements()==2, AipsError); // RotMatrix rot; setRotationMatrix(rot, refVal[0], refVal[1]); // Double lat = world[1]*to_radians_p[1]; mv.setAngle(world[0]*to_radians_p[0]/cos(lat), lat); mv = rot * mv; // world[0] = mv.getLong() / to_radians_p[0]; world[1]= mv.getLat() / to_radians_p[1]; } void DirectionCoordinate::makeWorldAbsolute (MDirection& world) const { static MVDirection mv; // Double lon = world.getValue().getLong(); Double lat = world.getValue().getLat(); mv.setAngle(lon/cos(lat), lat); // world.set(rot_p * mv); } Bool DirectionCoordinate::setWorldMixRanges (const IPosition& shape) { AlwaysAssert(nWorldAxes()==nPixelAxes(), AipsError); const uInt n = shape.nelements(); if (n!=nPixelAxes()) { set_error("Shape must be of length nPixelAxes"); return False; } // Find centre of image. // If the shape is -1, it means its not known for this world // axis (user may have removed a pixel axis in CoordinateSystem) // Use reference pixel in this case const Vector& cdelt = increment(); Vector pixelIn(referencePixel().copy()); Vector worldOut(nWorldAxes()); for (uInt i=0; i 0) { pixelIn(i) = shape(i) / 2.0; } } if (!toWorld(worldOut, pixelIn)) return False; // Vector units = worldAxisUnits(); Double cosdec = cos(worldOut[1] * to_radians_p[1]); Double fac = 1.0; // If the shape is -1, it means its not known for this world // axis (user may have removed a pixel axis in CoordinateSystem) // They might have also have removed a pixel axis but not a world axis. // Really, in this case, the replacement world value should be used. Float frac = 0.5; Int n2 = 0; for (uInt i=0; i<2; i++) { fac = 1.0; if (i==0) fac = cosdec; // Find number of pixels to offset from centre. // Do something arbitrary if the shape is <= 1 if (shape(i)<=1) { n2 = 10; } else if (shape(i) > 0) { n2 = (shape(i) + Int(frac*shape(i))) / 2; } // worldMin_p(i) = worldOut(i) - abs(cdelt(i))*n2/fac; worldMax_p(i) = worldOut(i) + abs(cdelt(i))*n2/fac; // if (i==0) { worldMin_p(i) = putLongInPiRange (worldMin_p(i), units(i)); worldMax_p(i) = putLongInPiRange (worldMax_p(i), units(i)); } } // return True; } void DirectionCoordinate::setWorldMixRanges (const Vector& which, const Vector& world) { AlwaysAssert(which.nelements()==nWorldAxes(), AipsError); AlwaysAssert(world.nelements()==nWorldAxes(), AipsError); // const Vector& cdelt = increment(); const Vector& units = worldAxisUnits(); // Vector pixelIn(referencePixel().copy()); Vector worldOut; toWorld(worldOut, pixelIn); // Double cosdec = cos(worldOut[1] * to_radians_p[1]); Int n = 10; // Arbitrary offset for (uInt i=0; i pi range worldMin_p(i) = putLongInPiRange (worldMin_p(i), units[0]); worldMax_p(i) = putLongInPiRange (worldMax_p(i), units[0]); } else { worldMin_p(i) = world(i) - abs(cdelt(i))*n; worldMin_p(i) = max(worldMin_p(i), -90.0/to_degrees_p[1]); // worldMax_p(i) = world(i) + abs(cdelt(i))*n; worldMax_p(i) = min(worldMax_p(i), 90.0/to_degrees_p[1]); } } } } void DirectionCoordinate::setDefaultWorldMixRanges () { worldMin_p.resize(2); worldMax_p.resize(2); worldMin_p[0] = -180.0/to_degrees_p[0]; //long worldMax_p[0] = 180.0/to_degrees_p[0]; worldMin_p[1] = -90.0/to_degrees_p[1]; //lat worldMax_p[1] = 90.0/to_degrees_p[1]; } void DirectionCoordinate::setRotationMatrix () { setRotationMatrix(rot_p, referenceValue()[0], referenceValue()[1]); } void DirectionCoordinate::setRotationMatrix (RotMatrix& rot, Double lon, Double lat) const // // Set rotation matrix for use in handling offset coordinates // { Double refLon = lon * to_radians_p[0]; Double refLat = lat * to_radians_p[1]; // MVDirection refPos(refLon, refLat); Euler eul(refLat, 2u, -refLon, 3); RotMatrix rot2(eul); rot2.transpose(); // rot = rot2; } void DirectionCoordinate::makeConversionMachines () { if (type_p != conversionType_p) { MDirection::Ref oldType(type_p); MDirection::Ref newType(conversionType_p); pConversionMachineTo_p = new MDirection::Convert(oldType, newType); pConversionMachineFrom_p = new MDirection::Convert(newType, oldType); } } void DirectionCoordinate::initializeFactors () { // Initially we are in radians to_degrees_p.resize(2); to_radians_p.resize(2); units_p.resize(2); // to_degrees_p[0] = 1.0 / C::degree; to_degrees_p[1] = to_degrees_p[0]; to_radians_p[0] = 1.0; to_radians_p[1] = 1.0; // units_p = "rad"; } void DirectionCoordinate::convertTo (Vector& world) const { static MVDirection inMV; // I can't set the machine to operate in the native units because // the user can set them differently for lon and lat if (pConversionMachineTo_p) { inMV.setAngle(world[0]*to_radians_p[0], world[1]*to_radians_p[1]); world = (*pConversionMachineTo_p)(inMV).getValue().get() / to_radians_p; } } void DirectionCoordinate::convertFrom (Vector& world) const { static MVDirection inMV; // I can't set the machine to operate in the native units because // the user can set them differently for lon and lat if (pConversionMachineFrom_p) { inMV.setAngle(world[0]*to_radians_p[0], world[1]*to_radians_p[1]); world = (*pConversionMachineFrom_p)(inMV).getValue().get() / to_radians_p; } } Double DirectionCoordinate::putLongInPiRange (Double lon, const String& unit) const { Unit u(unit); Quantum q(lon, u); MVAngle mva(q); const MVAngle& mva2 = mva(); Double t = mva2.get(u).getValue(); // if (t < 0) t += 360.0/to_degrees_p[0]; return t; } // Helper functions to help us interface to WCS void DirectionCoordinate::makeWCS(::wcsprm& wcs, const Matrix& xform, const Projection& proj, MDirection::Types directionType, Double refPixLong, Double refPixLat, Double refLong, Double refLat, Double incLong, Double incLat, Double longPole, Double latPole) // // wcs used 1-rel pixel coordinates // { wcs.flag = -1; init_wcs(wcs, 2); // Fill in PC matrix xFormToPC (wcs, xform); // Now the rest wcs.crpix[0] = refPixLong; wcs.crpix[1] = refPixLat; // wcs.cdelt[0] = incLong; wcs.cdelt[1] = incLat; // wcs.crval[0] = refLong; wcs.crval[1] = refLat; // wcs.lonpole = longPole; wcs.latpole = latPole; // Construct FITS ctype vector Vector axisNames = DirectionCoordinate::axisNames(directionType, True); Vector ctype = FITSCoordinateUtil::cTypeFromDirection (proj, axisNames, False); strncpy (wcs.ctype[0], ctype[0].chars(), 9); strncpy (wcs.ctype[1], ctype[1].chars(), 9); // String name = Projection::name(proj.type()); const Vector& projParameters = proj.parameters(); const uInt nProj = projParameters.nelements(); uInt startAt = ((proj.type() == Projection::ZPN) ? 0 : 1); // Only ZPN uses prjprm->p[0] // wcs.npv = nProj; for (uInt i=0; i < nProj; i++) { // wcs.pv[i].i = 2; // Latitude, 1-relative wcs.pv[i].m = i+startAt; wcs.pv[i].value = projParameters[i]; } // Fill in the wcs structure set_wcs(wcs); // normalize the PC Matrix normalizePCMatrix(); } void DirectionCoordinate::normalizePCMatrix() { Bool changed(False); // go over each of the two rows for (uInt i=0; i<2; i++) { Double pcNorm = 0; // compute the factor uInt jStart = i*2; uInt jEnd = jStart + 2; uInt idiag = jStart + i; Double rowSign = wcs_p.pc[idiag]/fabs(wcs_p.pc[idiag]); for (uInt j=jStart; j < jEnd; j++){ pcNorm += wcs_p.pc[j]*wcs_p.pc[j]; } if (pcNorm != 0.0 && pcNorm != 1.0){ changed=True; pcNorm = sqrt(pcNorm); // make diagonal element positive pcNorm *=rowSign; // normalize all elements for (uInt j=jStart; j < jEnd; j++){ wcs_p.pc[j] /= pcNorm; } // adjust the increment correspondingly wcs_p.cdelt[i] *= pcNorm; } } // mark the changes in the wcs struct if (changed){ wcs_p.altlin |= 1; set_wcs(wcs_p); } } void DirectionCoordinate::copy(const DirectionCoordinate &other) { // copy from "other" to this DirectionCoordinate instantiation if(other.wcs_p.pv != NULL && other.wcs_p.pv->i > 2){ // temporary, for debugging std::cerr << "wcs_p.pv.i was " << other.wcs_p.pv->i ; other.wcs_p.pv->i = 2; std:: cerr << ", corrected to." << other.wcs_p.pv->i << std::endl; } type_p = other.type_p; conversionType_p = other.conversionType_p; projection_p = other.projection_p; // names_p = other.names_p; units_p = other.units_p; to_degrees_p = other.to_degrees_p.copy(); to_radians_p = other.to_radians_p.copy(); rot_p = other.rot_p; // Copy WCS structure. if (wcs_p.flag != -1) { wcsfree (&wcs_p); } copy_wcs(other.wcs_p, wcs_p); set_wcs(wcs_p); // Machines if (pConversionMachineTo_p) { delete pConversionMachineTo_p; pConversionMachineTo_p = 0; } if (pConversionMachineFrom_p) { delete pConversionMachineFrom_p; pConversionMachineFrom_p = 0; } makeConversionMachines(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/coordinates/Coordinates/DirectionCoordinate.h000066400000000000000000001024271476623553700242170ustar00rootroot00000000000000//# DirectionCoordinate.h: Interconvert pixel positions and directions (e.g. RA/DEC) //# Copyright (C) 1997,1998,1999,2000,2001,2002,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef COORDINATES_DIRECTIONCOORDINATE_H #define COORDINATES_DIRECTIONCOORDINATE_H #include #include #include #include #include #include #include #include struct celprm; struct prjprm; struct wcsprm; namespace casacore { //# NAMESPACE CASACORE - BEGIN class MVDirection; class MVAngle; class LogIO; template class Quantum; // // Interconvert pixel positions and directions (e.g. RA/DEC). // // // // // //
      • Knowledge of astronomical coordinate conversions in general. Probably the // best documents are the papers by Mark Calabretta and Eric Greisen. // The initial draft from 1996 can be found at // http://www.atnf.csiro.au/~mcalabre. It is this draft that the // Coordinate classes are based upon. Since then, this paper has evolved // into three which can be found at the above address, and will be published in the // Astronomy and Astrophysics Supplement Series (probably in 2000). // The design has changed since the initial draft. When these papers // are finalized, and the IAU has ratified the new standards, WCSLIB // (Mark Calabretta's implementation of these conventions) will be // revised for the new designs. At that time, the Coordinate classes // may also be revised. //
      • Coordinate defines the fundamental // interface to coordinate conversions. //
      • MDirection defines the types of // directions (J2000 etc.) which are defined. The measures machinery // also implements "astronomical" conversions which are outside the // scope of these coordinates (for example, J2000 to // B1950). //
      • Projection defines the types of // celestial projections which are available. // // // // This class implements pixel to world coordinate conversions. This class // implements geometric conversions (e.g. SIN projection) via the WCS library // and also provides an interface to astronomical conversions (RA/DEC <--> l,b) // via the Measures module. // // // // // All absolute pixels coordinates are zero relative. // // // // Let's make a DirectionCoordinate --- used to represent a direction, // usually an RA/DEC, but it could also be, e.g., an AZ/EL pair. // // Matrix xform(2,2); // 1 // xform = 0.0; xform.diagonal() = 1.0; // 2 // DirectionCoordinate radec(MDirection::J2000, // 3 // Projection(Projection::SIN), // 4 // 135*C::pi/180.0, 60*C::pi/180.0, // 5 // -1*C::pi/180.0, 1*C::pi/180, // 6 // xform, // 7 // 128, 128); // 8 // //
          //
        • 1-2:Here we set up a diagonal transformation matrix. // Normally this matrix should be diagonal, however if you wanted // to introduce a rotation or skew, you would do it through this // matrix. //
        • 3:This defines the astronomical type of the world // coordinate. Most of the time it will probably be J2000 // or B1950, but many other possibilities are possible as listed // in the MDirection class // header. //
        • 4:The Projection class // defines the "geometry" that is used to map xy<-->world. SIN // is the most common projection for radio interferometers. Note that // SIN can optionally take parameters as defined in Calabretta and Greisen. // If not provided, they default to 0.0, which is the "old" SIN // convention. //
        • 5:Set the reference position to RA=135, DEC=60 degrees. // Note that the native units of a Direction is radians. //
        • 6: Set the increments to -1 degree in RA, and +1 degree // in DEC. //
        • 7: Set the previously defined transformation matrix. //
        • 8: Set the zero-relative reference pixel. Note that it does // not have to be incremental. At the reference pixel, the world // coordinate has the reference value. //
        // // In this example is is more convenient to change the units to degrees. This can // be accomplished as follows: // // Vector units(2); units = "deg"; // 9 // radec.setWorldAxisUnits(units); // 10 // // The increment and reference value are updated appropriately. // // Set up a couple of vectors to use the world and pixel coordinate values. // // Vector world(2), pixel(2); // 11 // pixel = 138.0; // 12 // // We use 138 as an arbitrary pixel position which is near the reference pixel // so we can tell if the answers look foolish or not. // We can actually perform a transformation like this as follows. If // it succeeds we print the value of the world coordinate. // // Bool ok = radec.toWorld(world, pixel); // 13 // if (!ok) { // 14 // cout << "Error: " << radec.errorMessage() << endl; // 15 // return 1; // 16 // } // 17 // cout << world << " <--- " << pixel << endl; // 18 // // There is an overloaded "toWorld" function that produces an MDirection // in case you want to, e.g., find out what the position in B1950 coordinates // would be. // // The reverse transformation takes place similarly: // // ok = radec.toPixel(pixel, world); // 19 // //
        // // // We could also have made the above DirectionCoordinate using the Quantum-based // constructor, which is a little more elegant if you want to use degrees. // // Matrix xform(2,2); // xform = 0.0; xform.diagonal() = 1.0; // Quantum refLon(135.0, "deg"); // Quantum refLat(60.0, "deg"); // Quantum incLon(-1.0, "deg"); // Quantum incLat(1.0, "deg"); // DirectionCoordinate radec(MDirection::J2000, // Projection(Projection::SIN), // refLon, refLat, // incLon, incLat, // xform, // 128, 128); // // But note that the constructor will have converted the native units // of the DirectionCoordinate to radians. So the Double-based toWorld and // toPixel functions will be in terms of radians. If you want the native // units to be degrees, then again you can use // // // Vector units(2); units = "deg"; // radec.setWorldAxisUnits(units); // // and thereafter degrees are the native units. // // // // Directions in the sky are fundamental to astronomy. // // // // //
      • AipsError // // // //
      • Nothing // class DirectionCoordinate : public Coordinate { public: // The default constructor creates a J2000 DirectionCoordinate with a // CARtesion projection with longitude,latitude 0,0 at pixel 0,0 and an // increment of +1 radian per pixel on both axes. DirectionCoordinate(); // Define the DirectionCoordinate transformation. refLong and // refLat will normally the the RA/DEC of the pixel described by // refX/refY. incLat/incLong // are the increments per pixel (RA is usually negative), and the xform // matrix is usually the unit diagonal matrix unless you have a rotation or // some other linear transformation between the pixel and world axes. // // Note that the units are radians initially. You can change it to degrees // or something else with the setWorldAxisUnits method later if you want. // // longPole and latPole are defined by Calabretta and Greisen (these // are reference points not at the native pole). In general // you can leave these out and the default values will cause them // to be computed appropriately. However, when reading from FITS // the LONPOLE and LATPOLE keywords are passed along here. DirectionCoordinate(MDirection::Types directionType, const Projection &projection, Double refLong, Double refLat, Double incLong, Double incLat, const Matrix &xform, Double refX, Double refY, Double longPole=999.0, Double latPole=999.0); // Create DirectionCoordinate with Quantum-based interface. // Parameters are the same as above. // Regardless of the units of the quanta, the initial units // of the DirectionCoordinate will be converted radians. // You can change it to degrees or something else with the // setWorldAxisUnits method later if you want. // // longPole and latPole are defined by Calabretta and Greisen (these // are reference points not at the native pole). In general // you can leave these out and the default values will cause them // to be computed appropriately. However, when reading from FITS // the LONPOLE and LATPOLE keywords are passed along here. // To get the default the 999.0 value should be used (units // are irrelevant in that case) DirectionCoordinate(MDirection::Types directionType, const Projection &projection, const Quantum& refLong, const Quantum& refLat, const Quantum& incLong, const Quantum& incLat, const Matrix &xform, Double refX, Double refY, const Quantum& longPole=Quantum(999.0,Unit("rad")), const Quantum& latPole=Quantum(999.0,Unit("rad"))); // Constructor from WCS structure; must hold ONLY a celestial wcs structure // Specify whether the absolute pixel coordinates in the wcs structure // are 0- or 1-relative. The coordinate is always constructed with 0-relative // pixel coordinates DirectionCoordinate(MDirection::Types directionType, const ::wcsprm& wcs, Bool oneRel=True); // Copy constructor (copy semantics) DirectionCoordinate(const DirectionCoordinate &other); // Assignment (copy semantics). DirectionCoordinate &operator=(const DirectionCoordinate &other); // Destructor virtual ~DirectionCoordinate(); // Return Coordinate::DIRECTION virtual Coordinate::Type type() const; // Always returns the String "Direction". virtual String showType() const; // Always returns 2. // virtual uInt nPixelAxes() const; virtual uInt nWorldAxes() const; // // Set extra conversion type. Whenever a conversion from pixel to world is done, // the world value is then further converted to this MDirection::Types value. // For example, your DirectionCoordinate may be defined in J2000. // You can use this to get the world values out in say GALACTIC. // Similarly, whenever you convert from world to pixel, the world // value is assumed to be that appropriate to the conversionDirectionType. // It is first converted to the MDirection::Types with which the // DirectionCoordinate was constructed and from there to pixel. // If you don't call this function, or you set the same type // for which the DirectionCoordinate was constructed, no extra // conversions occur. Some conversions will fail. These are the // ones that require extra frame information (epoch, position) such // as to AZEL from J2000 etc. This will be added later. // // In the mixed pixel/world conversion routine toMix // the implementation is only partial. See the comments for this // function below. // void setReferenceConversion (MDirection::Types type); void getReferenceConversion (MDirection::Types& type) const {type=conversionType_p;}; // // Convert a pixel position to a world position or vice versa. Returns True // if the conversion succeeds, otherwise it returns False and method // errorMessage returns its error message. // The output vectors are appropriately resized. // if useConversionFrame, if the coordinate has a conversion // layer frame, it is used. Else, the native frame is used for the conversion. // virtual Bool toWorld(Vector &world, const Vector &pixel, Bool useConversionFrame=True) const; // world values must have units equivalent to the world axis // units. If the coordinate has a conversion layer, the world coordinates // must be supplied in the conversion frame. virtual Bool toPixel(Vector &pixel, const Vector &world) const; // // Mixed pixel/world coordinate conversion. // worldIn and worldAxes are of length // nWorldAxes. // pixelIn and pixelAxes are of length nPixelAxes. // worldAxes(i)=True specifies you have given a world // value in worldIn(i) to convert to pixel. // pixelAxes(i)=True specifies you have given a pixel // value in pixelIn(i) to convert to world. // You cannot specify the same axis via worldAxes // and pixelAxes. // Values in pixelIn are converted to world and // put into worldOut in the appropriate world axis // location. Values in worldIn are copied to // worldOut. // Values in worldIn are converted to pixel and // put into pixelOut in the appropriate pixel axis // location. Values in pixelIn are copied to // pixelOut. // // worldMin and worldMax specify the range of the world // coordinate (in the world axis units of that world axis // in the CoordinateSystem) being solved for in a mixed calculation // for each world axis. Some mixed solutions can be degenerate, whereupon you // you must say which one you want. Use functions setWorldMixRanges // and worldMixMin, worldMixMax to set these ranges, // If you don't know, use the defaults (function setDefaultWorldMixRanges. // Removed axes are handled (for example, a removed pixel // axis with remaining corresponding world axis will // correctly be converted to world using the replacement // value). // Returns True if the conversion succeeds, otherwise it returns False and // errorMessage() contains an error message. The output vectors // are resized. // // If you actually request a pure pixel to world or world to pixel // via toMix, then the functions toWorld or toPixel // will be invoked directly (see above) and the extra conversion layer // invoked through function setReferenceConversion will be active. // However, if you request a true mixed pixel/world conversion, // the extra conversion layer is not activated (because of the nature of mixed // conversions). This situation may change in the future // with a partial implementation added. virtual Bool toMix(Vector& worldOut, Vector& pixelOut, const Vector& worldIn, const Vector& pixelIn, const Vector& worldAxes, const Vector& pixelAxes, const Vector& worldMin, const Vector& worldMax) const; // Compute and retrieve the world min and max ranges, for use in function toMix, // for a lattice of the given shape (for this coordinate). Using these // ranges in toMix should speed it up and help avoid ambiguity. // If the shape is negative, that indicates that the shape is unknown // for that axis. The default range is used for that axis. This situation // arises in a CoordinateSystem for which a pixel, but not a world axis // has been removed. // The output vectors are resized. Returns False if fails (and // then setDefaultWorldMixRanges generates the ranges) // with a reason in errorMessage(). // The setDefaultWorldMixRanges function // just gives you [-90->90], [-180,180] (in appropriate units) // virtual Bool setWorldMixRanges (const IPosition& shape); virtual void setDefaultWorldMixRanges (); // // Non-virtual function. When which is T, use the // world value as the center for the mix world range. void setWorldMixRanges (const Vector& which, const Vector& world); // A convenient way to turn the world vector into an MDirection or MVDirection // for further processing in the Measures system. //
        We could improve the performance of this if it would be useful. However it is // expected that normally one would just call this once to get a template // MDirection, and then call the vector versions. //
        In case of a failure, the versions with a Bool return value will return // False. The other versions will throw an exception. // Bool toWorld(MDirection &world, const Vector &pixel) const; Bool toPixel(Vector &pixel, const MDirection &world) const; Bool toWorld(MVDirection &world, const Vector &pixel) const; Bool toPixel(Vector &pixel, const MVDirection &world) const; MVDirection toWorld(const Vector &pixel) const; Vector toPixel(const MVDirection &world) const; Vector toPixel(const MDirection &world) const; // // Batch up a lot of transformations. The first (most rapidly varying) axis // of the matrices contain the coordinates. Returns False if any conversion // failed and errorMessage() will hold a message. // The failures array is the length of the number of conversions // (True for failure, False for success) // virtual Bool toWorldMany(Matrix &world, const Matrix &pixel, Vector &failures) const; virtual Bool toPixelMany(Matrix &pixel, const Matrix &world, Vector &failures) const; // // Make absolute world coordinates relative and vice-versa (relative to // the reference value). Note that these functions are independent // of the MDirection::Types (set either at construction or by function // setReferenceConversion). The vectors must be // of length nWorldAxes or memory access errors will occur // virtual void makeWorldRelative (Vector& world) const; virtual void makeWorldRelative (MDirection& world) const; virtual void makeWorldAbsolute (Vector& world) const; virtual void makeWorldAbsolute (MDirection& world) const; // // Make absolute coordinates relative and vice versa with respect // to the given reference value. Add the other functions in this grouping // as needed. // virtual void makeWorldAbsoluteRef (Vector& world, const Vector& refVal) const; // // Recover the requested attribute. // MDirection::Types directionType(Bool showConversion=False) const; Projection projection() const; virtual Vector worldAxisNames() const; virtual Vector worldAxisUnits() const; virtual Vector referenceValue() const; virtual Vector increment() const; virtual Matrix linearTransform() const; virtual Vector referencePixel() const; // // Set the value of the requested attribute. Note that these just // change the internal values, they do not cause any recomputation. // virtual Bool setWorldAxisNames(const Vector &names); virtual Bool setReferencePixel(const Vector &refPix); virtual Bool setLinearTransform(const Matrix &xform); virtual Bool setIncrement(const Vector &inc); virtual Bool setReferenceValue(const Vector &refval); // // Change the world axis units. Adjust the increment and // reference value by the ratio of the old and new units. // The units must be compatible with // angle. The units are initially "rad" (radians). virtual Bool setWorldAxisUnits(const Vector &units); // Return canonical axis names for the given MDirection type, // giving FITS names if desired. // BEG think this should be in the MDirection class, but WNB // disagrees. Leave it here for now. static Vector axisNames(MDirection::Types type, Bool FITSName = False); // Comparison function. Any private Double data members are compared // with the specified fractional tolerance. Don't compare on the specified // axes in the Coordinate. If the comparison returns False, method // errorMessage returns a message about why. // virtual Bool near(const Coordinate& other, Double tol=1e-6) const; virtual Bool near(const Coordinate& other, const Vector& excludeAxes, Double tol=1e-6) const; // // Format a DirectionCoordinate coordinate world value nicely through the // common format interface. See Coordinate // for basics. // // Formatting types that are allowed are SCIENTIFIC, FIXED, MIXED, and TIME // If you ask for format type Coordinate::DEFAULT then the // selected format depends upon what the value of the enum // MDirection::GlobalTypes is for this DirectionCoordinate. // For example, if it is GRADEC or GHADEC you would // get Coordinate::TIME style formatting (DD:MM:SS.SS), otherwise // you would get Coordinate::FIXED formatting by default. // // axis says which axis in this Coordinate we are formatting. // We have to know this because we may format Longitude and Latitude differently. // For Coordinate::TIME style formatting, precision // refers to the places after the decimal in the SS field. // // If you leave units empty, then it makes up a nice unit for you. // virtual void getPrecision (Int& precision, Coordinate::formatType& format, Bool showAsAbsolute, Int defPrecScientific, Int defPrecFixed, Int defPrecTime) const; virtual String format(String& units, Coordinate::formatType format, Double worldValue, uInt axis, Bool isAbsolute, Bool showAsAbsolute, Int precision=-1, Bool usePrecForMixed=False) const; // // Fix cylindrical coordinates to put the longitude in [-180,180] range. // If False returned, it failed an an error is in errorMessage // This fix is not done automatically internally because of the dependence // on the image shape. It should be called for any foreign image // (such as FITS) that is imported Bool cylindricalFix (Int shapeLong, Int shapeLat); // Find the Coordinate for when we Fourier Transform ourselves. This pointer // must be deleted by the caller. Axes specifies which axes of the Coordinate // you wish to transform. Shape specifies the shape of the image // associated with all the axes of the Coordinate. Currently the // output reference pixel is always shape/2. If the pointer returned is 0, // it failed with a message in errorMessage virtual Coordinate* makeFourierCoordinate (const Vector& axes, const Vector& shape) const; // Save the DirectionCoordinate into the supplied record using the supplied field name. // The field must not exist, otherwise False is returned. virtual Bool save(RecordInterface &container, const String &fieldName) const; // Recover the DirectionCoordinate from a record. // A null pointer means that the restoration did not succeed. static DirectionCoordinate *restore(const RecordInterface &container, const String &fieldName); // Make a copy of the DirectionCoordinate using new. The caller // is responsible for calling delete. virtual Coordinate *clone() const; // Fish out the ref and non-native poles (refLong, refLat, longPole, latPole) // Not for general use. Units are degrees. Vector longLatPoles() const; // get the pixel area. Quantity getPixelArea() const; // Convert this coordinate to another reference frame by rotating it about // the reference pixel so the the axes of the new reference frame are // aligned along the current pixel axes. The reference pixel remains the // same and the conversion is exact for the reference pixel and in general // becomes less accurate as distance from reference pixel increases. The // latitude like and the longitude like pixel increments are preserved. // Conversions which require extra information such as epoch and position // are not supported. The angle parameter is the angle between // the new coordinate and the pixel coordinate, measured clockwise from the // positive y-axis of the new coordinate to the positive y-axis of the pixel // coordinate; ie, it is the clockwise angle through which the current world // coordinate would have to be rotated so that the new coordinate's axes // would be parallel to the pixel axes. The accuracy of the returned angle // is good to at least 7 digits. DirectionCoordinate convert( Quantity& angle, MDirection::Types directionType ) const; // Set the projection. void setProjection(const Projection&); // Set the base (as opposed to conversion) reference frame. void setReferenceFrame(const MDirection::Types rf); // Are the pixels square? Bool hasSquarePixels() const; // Is the projection equivalent to NCP? Bool isNCP() const; private: // Direction type MDirection::Types type_p, conversionType_p; // Projection parameters Projection projection_p; // WCS structure. This is mutable because the wcs functions // that do toPixel and toWorld (which have const signature) // require a non const wcs structure. so either all of these // virtual functions lose their const or we use mutable... mutable ::wcsprm wcs_p; // WCS computes in degrees - use this to convert back and forth between // current DirectionCoordinate units and degrees or radians Vector to_degrees_p; // From current units to degrees Vector to_radians_p; // From current units to radians // Axis names. Vector names_p; // Current units. Vector units_p; // Rotation matrix used to handle relative coordinates RotMatrix rot_p; // Conversion machines. // "To" handles type_p -> conversionType_p // "From" handles conversionType_p -> type_p; mutable MDirection::Convert* pConversionMachineTo_p; mutable MDirection::Convert* pConversionMachineFrom_p; // Interconvert between the current units and wcs units (degrees) // void toCurrent(Vector& degrees) const; void fromCurrent(Vector& current) const; // // Check formatting types. void checkFormat(Coordinate::formatType& format, Bool absolute) const; // Format a latitude. String formatLatitude (String& units, MVAngle& mVA, Bool absolute, Coordinate::formatType form, Int prec) const; // Format a longitude. String formatLongitude (String& units, MVAngle& mVA, MDirection::GlobalTypes gtype, Bool absolute, Coordinate::formatType form, Int prec) const; // Mixed pixel/world coordinate conversion. Vector in must // be length nWorldAxes (2). Specify whether longitude // (in(0)) or latitude (in(1)) is the world coordinate . It is // assumed that the other value is the pixel coordinate. Bool toMix2(Vector& out, const Vector& in, const Vector& minWorld, const Vector& maxWorld, Bool longIsWorld) const; // Initialize unit conversion vectors and units void initializeFactors (); // Helper functions interfacing to WCS. // void makeDirectionCoordinate(MDirection::Types directionType, const Projection& proj, Double refLong, Double refLat, Double incLong, Double incLat, const Matrix &xform, Double refX, Double refY, Double longPole, Double latPole); // void makeWCS(::wcsprm& wcs, const Matrix& xform, const Projection& proj, MDirection::Types directionType, Double refPixLong, Double refPixLat, Double refLong, Double refLat, Double incLong, Double incLat, Double longPole, Double latPole); // // Normalize each row of the PC matrix such that increment() will return the actual // angular increment and any scale factors are removed from the PC matrix // (modifies wcs_p.pc _and_ wcs_p.cdelt _and_ wcs_p.altlin, // executes set_wcs() and hence wcsset() on the struct) // See Greisen & Calabretta, A&A 395, 1061-1075 (2002), equation (4) void normalizePCMatrix(); Double putLongInPiRange (Double lon, const String& unit) const; // Set up conversion machine void makeConversionMachines(); // Convert from type_p -> conversionType_p // virtual void convertTo (Vector& world) const; virtual void convertFrom (Vector& world) const; // // Copy private data void copy (const DirectionCoordinate& other); // Set up the offset coordinate rotation matrix. Units // of long and lat are current world units // void setRotationMatrix (); void setRotationMatrix (RotMatrix& rot, Double lon, Double lat) const; // // Return unit conversion vector for converting to current units const Vector toCurrentFactors () const; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/coordinates/Coordinates/FITSCoordinateUtil.cc000066400000000000000000001654231476623553700240450ustar00rootroot00000000000000//# FITSCoordinateUtil.cc: inter-convert CoordinateSystem and FITS headers //# Copyright (C) 1997,1998,1999,2000,2001,2002,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Bool FITSCoordinateUtil::toFITSHeader(RecordInterface &header, IPosition &shape, const CoordinateSystem& cSys, Bool oneRelative, Char prefix, Bool writeWCS, Bool preferVelocity, Bool opticalVelocity, Bool preferWavelength, Bool airWavelength) const { LogIO os(LogOrigin("FITSCoordinateUtil", "toFITSHeader", WHERE)); // Validation const Int n = cSys.nWorldAxes(); String sprefix(prefix); if (header.isDefined(sprefix + "rval") || header.isDefined(sprefix + "rpix") || header.isDefined(sprefix + "delt") || header.isDefined(sprefix + "type") || header.isDefined(sprefix + "unit")) { os << LogIO::SEVERE << "Already contains one or more of *rval, *rpix, " "*delt, *type, *unit"; return False; } Double offset = 0.0; if (oneRelative) { offset = 1.0; } // Canonicalize units and find sky axes CoordinateSystem coordsys = cSys; // Find the sky coordinate, if any Int skyCoord = coordsys.findCoordinate(Coordinate::DIRECTION); Int longAxis = -1, latAxis = -1; // Find the spectral axis, if any Int specCoord = coordsys.findCoordinate(Coordinate::SPECTRAL); Int specAxis = -1; // Find the Stokes axis if any. Int stokesCoord = coordsys.findCoordinate(Coordinate::STOKES); Int stokesAxis = -1; // If any axes have been removed from a coordinate, you find it out here. Int i; for (i=0; i 0) { if (cSys.tabularCoordinate(tabCoord).pixelValues().nelements() > 0 && !( ((preferVelocity && opticalVelocity) || preferWavelength) && tabCoord==specCoord) ) { os << LogIO::NORMAL << "Note: Your coordinate system has one or more TABULAR axes for which\n" "the lookup table will be lost in the conversion to FITS, and\n" "will be replaced by averaged (i.e. linearized) axes." << LogIO::POST; break; } } // change the units to degrees for the sky axes Vector units(coordsys.worldAxisUnits().copy()); if (longAxis >= 0) units(longAxis) = "deg"; if (latAxis >= 0) units(latAxis) = "deg"; // and to the canonical units for the spectral and stokes axis if (specAxis >= 0) units(specAxis) = "Hz"; if (stokesAxis >= 0) units(stokesAxis) = ""; coordsys.setWorldAxisUnits(units); // If there is a spectral conversion layer, make it permanent here if(specCoord>=0){ SpectralCoordinate sCoord(coordsys.spectralCoordinate(specCoord)); MFrequency::Types nativeCtype = sCoord.frequencySystem(False); // native type MFrequency::Types convCtype = sCoord.frequencySystem(True); // converted type if (convCtype != nativeCtype) { MEpoch convEpoch; MPosition convPosition; MDirection convDirection; sCoord.getReferenceConversion(convCtype, convEpoch, convPosition, convDirection); // modify the spec coordsys corresponding to the conversion layer sCoord.transformFrequencySystem(convCtype, convEpoch, convPosition, convDirection); // replace the spec-coordsys in coordsys by the new one coordsys.replaceCoordinate(sCoord, specCoord); } } // Generate keywords. If we find we have a DC with one of the // axes removed, it will be linearized here. Double longPole, latPole; Vector crval, crpix, cdelt, pvi_ma; // crota is deprecated for FITS output // Vector crota; Vector ctype, cunit; Matrix pc; Bool isNCP = False; if (!generateFITSKeywords (os, isNCP, longPole, latPole, crval, crpix, cdelt, // crota, pvi_ma, ctype, cunit, pc, coordsys, skyCoord, longAxis, latAxis, specAxis, stokesAxis, writeWCS, offset, sprefix)) { return False; } // Special stokes handling if (stokesAxis >= 0) { if (!toFITSHeaderStokes (crval, crpix, cdelt, os, coordsys, stokesAxis, stokesCoord)) return False; } // If there are more world than pixel axes, we will need to add // degenerate pixel axes and modify the shape. if (Int(coordsys.nPixelAxes()) < n) { IPosition shapetmp = shape; shape.resize(n); Vector crpixtmp = crpix.copy(); crpix.resize(n); Int count = 0; for (Int worldAxis=0; worldAxis= 0) { // We have a pixel axis shape(worldAxis) = shapetmp(count); crpix(worldAxis) = crpixtmp(count); count++; } else { // No corresponding pixel axis. shape(worldAxis) = 1; crpix(worldAxis) = 1.0; } } } // Try to work out the epoch/equinox // Also LONGPOLE and LATPOLE here. if (skyCoord >= 0) { const DirectionCoordinate& dCoord = coordsys.directionCoordinate(skyCoord); MDirection::Types radecsys = dCoord.directionType(); Double equinox = -1.0; String radesys = ""; switch(radecsys) { case MDirection::J2000: equinox = 2000.0; radesys = "FK5"; break; case MDirection::B1950: equinox = 1950.0; radesys = "FK4"; break; case MDirection::B1950_VLA: equinox = 1979.9; radesys = "FK4"; break; case MDirection::ICRS: radesys = "ICRS"; break; default: ; // Nothing } if (equinox > 0) { if (writeWCS) { header.define("equinox", equinox); } else { header.define("epoch", equinox); } } if (radesys!=""){ header.define("radesys", radesys); } // header.define("lonpole", longPole); //const Projection& proj = dCoord.projection(); //if (!Projection::isZenithal(proj.type())) { // header.define("latpole", latPole); // Not relevant for zenithals //} header.define("latpole", latPole); } // Actually write the header if (writeWCS && Int(coordsys.nPixelAxes()) == n) { header.define("pc", pc); } else if (writeWCS) { os << LogIO::SEVERE << "writeWCS && nPixelAxes() != n. Requires " "development!!!" << LogIO::POST; } header.define(sprefix + "type", ctype); header.define(sprefix + "rval", crval); header.define(sprefix + "delt", cdelt); // header.define(sprefix + "rota", crota); header.define(sprefix + "rpix", crpix); header.define(sprefix + "unit", cunit); if (skyCoord >=0 && pvi_ma.nelements() > 0) { if (!writeWCS) { for (uInt k=0; k= 0) { const SpectralCoordinate &spec = coordsys.spectralCoordinate(specCoord); spec.toFITS(header, specAxis, os, oneRelative, preferVelocity, opticalVelocity, preferWavelength, airWavelength); } // Write out the obsinfo String error; Bool ok = coordsys.obsInfo().toFITS(error, header); if (!ok) { os << LogIO::SEVERE << "Error converting ObsInfo: " << error << LogIO::POST; } return ok; } Bool FITSCoordinateUtil::toFITSHeaderStokes(Vector& crval, Vector& crpix, Vector& cdelt, LogIO& os, const CoordinateSystem& coordsys, Int stokesAxis, Int stokesCoord) const { Vector stokes(coordsys.stokesCoordinate(stokesCoord).stokes()); Int inc = 1; Bool inorder = True; if (stokes.nelements() > 1) { inc = Stokes::FITSValue(Stokes::StokesTypes(stokes(1))) - Stokes::FITSValue(Stokes::StokesTypes(stokes(0))); for (uInt k=2; k& crval, Vector& crpix, Vector& cdelt, // Vector& crota, Vector& pvi_ma, Vector& ctype, Vector& cunit, Matrix& pc, const CoordinateSystem& cSys, Int skyCoord, Int longAxis, Int latAxis, Int specAxis, Int stokesAxis, Bool, Double offset, const String&) const { const Int n = cSys.nWorldAxes(); crval = cSys.referenceValue(); crpix = cSys.referencePixel() + offset; cdelt = cSys.increment(); // Generate FITS ctypes from DirectionCoordinate Vector cctype(2); if (skyCoord >= 0) { const DirectionCoordinate dCoord = cSys.directionCoordinate(skyCoord); pvi_ma = dCoord.projection().parameters(); longPole = dCoord.longLatPoles()(2); latPole = dCoord.longLatPoles()(3); // const DirectionCoordinate &dc = cSys.directionCoordinate(skyCoord); Double reflat = 0.; if(latAxis>=0){ reflat = M_PI/180.0*crval(latAxis); } cctype = cTypeFromDirection (isNCP, dc.projection(), DirectionCoordinate::axisNames(dc.directionType(), True), reflat, True); } // ctype = cSys.worldAxisNames(); for (Int i=0; i < n; i++) { if (i == longAxis || i == latAxis) { if (i==longAxis) { ctype[i] = cctype[0]; } else { ctype[i] = cctype[1]; } } else if (i == specAxis) { // Nothing - will be handled by SpectralCoordinate } else if (i == stokesAxis) { ctype[i] = "STOKES"; } else { // Linear and Tabular ctype[i].upcase(); if (ctype[i].length() > 8) { ctype[i] = ctype[i].at(0,8); } while (ctype[i].length() < 8) ctype[i] += " "; } } // CUNIT is case sensitive. cunit = cSys.worldAxisUnits(); for (Int i=0; i 8) { cunit(i) = cunit(i).at(0,8); } while (cunit(i).length() < 8) cunit(i) += " "; } // Matrix imageLT = cSys.linearTransform(); pc = imageLT; // need to transpose to conform with FITSKeywordUtil for(uInt i=0; i& header, const IPosition& shape, uInt which) const { // this method takes header converts it into cSys and puts the remainer into recHeader LogIO os(LogOrigin("FITSCoordinateUtil", "fromFITSHeader")); CoordinateSystem cSysTmp; if (header.nelements()==0) { os << "Header is empty - cannot create CoordinateSystem" << LogIO::WARN; return False; } // Convert header to char* for wcs parser // Keep obsgeo-x,y,z because wcspih removes them, but they are needed // by ObsInfo. vector saveCards; int nkeys = header.nelements(); String all; for (int i=0; i= 19 && // kludge changes 'RA--SIN ' to 'RA---SIN', etc. header[i][0]=='C' && header[i][1]=='T' && header[i][2]=='Y' && header[i][3]=='P' && header[i][4]=='E' && (header[i][5]=='1'|| header[i][5]=='2') && header[i][14]=='-' && header[i][18]==' ') { strncpy(tmp,header[i].c_str(),hsize+1); tmp[18]=tmp[17];tmp[17]=tmp[16];tmp[16]=tmp[15];tmp[15]=tmp[14]; all = all.append(tmp); os << LogIO::NORMAL << "Header\n"<< header[i] << "\nwas interpreted as\n" << tmp << LogIO::POST; } else if (hsize >= 19 && // change GLON-FLT to GLON-CAR, etc. header[i][0]=='C' && header[i][1]=='T' && header[i][2]=='Y' && header[i][3]=='P' && header[i][4]=='E' && (header[i][5]=='1'|| header[i][5]=='2') && header[i][15]=='-' && header[i][16]=='F' && header[i][17]=='L' && header[i][18]=='T') { strncpy(tmp,header[i].c_str(),hsize+1); tmp[16]='C'; tmp[17]='A'; tmp[18]='R'; all = all.append(tmp); os << LogIO::NORMAL << "Header\n"<< header[i] << "\nwas interpreted as\n" << tmp << LogIO::POST; } else if ( // adding the first condition (FEEQ, VRAD, VOPT) is necessary to avoid incorrect munging // of position-velocity image axes. ! (header[i].contains("FREQ") || header[i].contains("VRAD") || header[i].contains("VOPT")) && hsize >= 19 && ( header[i].startsWith("CTYPE1") || header[i].startsWith("CTYPE2") ) && header[i][15]==' ' && header[i][16]==' ' && header[i][17]==' ' && header[i][18]==' ' ) { // change 'GLON ' to 'GLON-CAR', etc. strncpy(tmp,header[i].c_str(),hsize+1); tmp[15]='-'; tmp[16]='C'; tmp[17]='A'; tmp[18]='R'; all = all.append(tmp); os << LogIO::NORMAL << "Header\n"<< header[i] << "\nwas interpreted as\n" << tmp << LogIO::POST; } else if (hsize >= 19 && // change 'OBSFREQ' to 'RESTFRQ' header[i][0]=='O' && header[i][1]=='B' && header[i][2]=='S' && header[i][3]=='F' && header[i][4]=='R' && header[i][5]=='E' && header[i][6]=='Q' && header[i][7]==' ') { strncpy(tmp,header[i].c_str(),hsize+1); tmp[0]='R'; tmp[1]='E'; tmp[2]='S'; tmp[3]='T'; tmp[4]='F'; tmp[5]='R'; tmp[6]='Q'; tmp[7]=' '; all = all.append(tmp); os << LogIO::NORMAL << "Header\n"<< header[i] << "\nwas interpreted as\n" << tmp << LogIO::POST; } else if (hsize >= 24 && // ignore "-SIP" header[i][0]=='C' && header[i][1]=='T' && header[i][2]=='Y' && header[i][3]=='P' && header[i][4]=='E' && (header[i][5]=='1'|| header[i][5]=='2') && header[i][19]=='-' && header[i][20]=='S' && header[i][21]=='I' && header[i][22]=='P' && header[i][23]=='\'') { strncpy(tmp,header[i].c_str(),hsize+1); tmp[19]='\'';tmp[20]=tmp[21]=tmp[22]=tmp[23]=' '; all = all.append(tmp); os << LogIO::NORMAL << "The SIP convention for representing distortion in FITS headers\n is not part of FITS standard v3.0" << " and not yet supported by CASA.\n Header\n "<< header[i] << "\n was interpreted as\n " << tmp << LogIO::POST; } else { if(header[i].contains("-GLS")){ os << LogIO::WARN << "Note: The GLS projection is deprecated. Use SFL instead." << LogIO::POST; } all = all.append(header(i)); } delete [] tmp; } char* pChar2 = const_cast(all.chars()); // Print cards for debugging Bool print(False); if (print) { cerr << "Header Cards " << endl; for (Int i=0; i= uInt(nwcs)) { os << LogIO::WARN << "Requested WCS # " << which << " (zero-based) exceeds the number available, i.e. must be smaller than " << nwcs << LogIO::POST; os << LogIO::WARN << "Will use the last available one." << LogIO::POST; which = nwcs-1; } // Add the saved OBSGEO keywords. // This is a bit tricky because pChar2 is in fact the char* of String 'all'. // So make a new string and add them to it. String newHdr; if (saveCards.size() > 0) { newHdr = String(pChar2); for (uInt i=0; i(newHdr.chars()); } // Put the rest of the header into a Record for subsequent use cardsToRecord (os, recHeader, pChar2); // Add FITS units to system UnitMap::addFITS(); // Set the ObsInfo. Some of what we need is in the WCS struct (date) and some in // the FITS Records now. Remove cards from recHeader as used. ObsInfo obsInfo = getObsInfo (os, recHeader, wcsPtr[which]); cSysTmp.setObsInfo(obsInfo); // // Now fix up wcs internal values for various inconsistencies,errors // and non-standard FITS formats. This may invoke : // celfix: translate AIPS-convention celestial projection types, -NCP and -GLS, set in CTYPEia. // spcfix: translate AIPS-convention spectral types, FREQ-LSR, FELO-HEL, etc., set in CTYPEia. // datfix: recast the older DATE-OBS date format to year-2000 standard // form, and derive MJD-OBS from it if not already set. // cylfix: fixes WCS FITS header cards for malformed cylindrical projections // that suffer from the problem described in Sect. 7.3.4 of Paper I. // unitifx: fixes non-standard units // Vector wcsNames(NWCSFIX); wcsNames(DATFIX) = String("datfix"); wcsNames(UNITFIX) = String("unitfix"); wcsNames(CELFIX) = String("celfix"); wcsNames(SPCFIX) = String("spcfix"); wcsNames(CYLFIX) = String("cylfix"); // int stat[NWCSFIX]; ctrl = 7; // Do all unsafe unit corrections // wcsfix needs Int shape, so copy it. std::vector tmpshp(shape.begin(), shape.end()); Bool doAbort=False; uInt eCount=0; if (wcsfix(ctrl, &(tmpshp[0]), &wcsPtr[which], stat) > 0) { for (int i=0; i0) { os << LogIO::NORMAL << wcsNames(i) << " incurred the error " << wcsfix_errmsg[err] << LogIO::POST; eCount++; if(i==CELFIX){ doAbort=True; } } } if(eCount>1 || doAbort) { os << LogIO::WARN << "The wcs function failures are too severe to continue ..." << LogIO::POST; status = wcsvfree(&nwcs, &wcsPtr); if (status!=0) { String errmsg = "wcs memory deallocation error: "; os << errmsg << LogIO::EXCEPTION; } // return False; } os << LogIO::NORMAL << "Will try to continue ..." << LogIO::POST; } // Now fish out the various coordinates from the wcs structure and build the CoordinateSystem Vector dirAxes; Vector linAxes; Int longAxis = -1; Int latAxis = -1; Int specAxis = -1; Int stokesAxis = -1; const uInt nAxes = wcsPtr[which].naxis; if(nAxes>shape.size()){ os << LogIO::NORMAL << "The WCS for this image contains " << nAxes - shape.size() << " degenerate axes." << LogIO::POST; } else if(nAxes order(nAxes); Int nspecial = 0; // Anything other than linear // if (longAxis >=0) nspecial++; if (latAxis >=0) nspecial++; if (stokesAxis >= 0) nspecial++; if (specAxis >= 0) nspecial++; // Int linused = 0; for (Int i=0; i= 0) { // stokes is axis 0 if no dir, otherwise 2 order(i) = 2; } else { order(i) = 0; } } else if (i == specAxis) { if (longAxis >= 0 && stokesAxis >= 0) { order(i) = 3; // stokes and dir } else if (longAxis >= 0) { order(i) = 2; // dir only } else if (stokesAxis >= 0) { order(i) = 1; // stokes but no dir } else { order(i) = 0; // neither stokes or dir } } else { order(i) = nspecial + linused; linused++; } } // cSysTmp.transpose(order,order); // cSys = cSysTmp; return True; } static Bool do_sub_wcs(const ::wcsprm& wcs, int &nsub, Block &axes, ::wcsprm &wcsDest, LogIO &os) { try { Coordinate::sub_wcs(wcs, nsub, axes.storage(), wcsDest); return True; } catch (const AipsError &e) { os << LogIO::WARN << e.what() << LogIO::POST; return False; } } Bool FITSCoordinateUtil::addDirectionCoordinate (CoordinateSystem& cSys, Vector& dirAxes, const ::wcsprm& wcs, LogIO& os) const { // Extract wcs structure pertaining to Direction Coordinate int nsub = 2; Block axes(nsub); axes[0] = WCSSUB_LONGITUDE; axes[1] = WCSSUB_LATITUDE; // ::wcsprm wcsDest; wcsInit (wcsDest); Bool ok = do_sub_wcs(wcs, nsub, axes, wcsDest, os); // See if we found the Sky if (ok && nsub==2) { // Call wcssset on new struct setWCS (wcsDest); // dirAxes.resize(2); dirAxes[0] = axes[0] - 1; // 1 -> 0 rel dirAxes[1] = axes[1] - 1; // Extract Direction system MDirection::Types dirSystem; String errMsg; if (!directionSystemFromWCS (os, dirSystem, errMsg, wcsDest)) { os << LogIO::WARN << errMsg << LogIO::POST; ok = False; } // Try to make DirectionCoordinate and fix up zero increments etc and add to CoordinateSystem if (ok) { try { Bool oneRel = True; // wcs structure from FITS has 1-rel pixel coordinates DirectionCoordinate c(dirSystem, wcsDest, oneRel); // fixCoordinate (c, os); cSys.addCoordinate(c); } catch (std::exception& x) { os << LogIO::WARN << x.what() << LogIO::POST; ok = False; } } } // Clean up wcsfree (&wcsDest); return ok; } Bool FITSCoordinateUtil::addLinearCoordinate (CoordinateSystem& cSys, Vector& linAxes, const ::wcsprm& wcs, LogIO& os) const { // Extract wcs structure pertaining to Linear Coordinate int nsub = 1; Block axes(wcs.naxis); axes[0] = -(WCSSUB_LONGITUDE | WCSSUB_LATITUDE | WCSSUB_SPECTRAL | WCSSUB_STOKES); // ::wcsprm wcsDest; wcsInit (wcsDest); // Bool ok = do_sub_wcs(wcs, nsub, axes, wcsDest, os); // See if we found the coordinate if (ok && nsub>0) { // Call wcssset on new struct setWCS (wcsDest); linAxes.resize(nsub); for (int i=0; i 0 rel } // Try to make LinearCoordinate from wcs structure and // fix up zero increments etc and add to CoordinateSystem if (ok) { try { Bool oneRel = True; // wcs structure from FITS has 1-rel pixel coordinates LinearCoordinate c(wcsDest, oneRel); // fixCoordinate (c, os); cSys.addCoordinate(c); } catch (std::exception& x) { os << LogIO::WARN << x.what() << LogIO::POST; ok = False; } } } // Clean up wcsfree (&wcsDest); return ok; } void FITSCoordinateUtil::wcsInit (::wcsprm& wcsDest) { wcsDest.flag = -1; // wcslib-4.8 introduced the following members. // Unfortunately it does not always initialize them. // In version 5 it is fixed; for older versions it is unclear. #if WCSLIB_VERSION_MAJOR == 4 && WCSLIB_VERSION_MINOR >= 8 wcsDest.err = 0; wcsDest.lin.err = 0; wcsDest.spc.err = 0; wcsDest.cel.err = 0; wcsDest.cel.prj.err = 0; #endif } Bool FITSCoordinateUtil::addStokesCoordinate (CoordinateSystem& cSys, Int& stokesAxis, Int& stokesFITSValue, const ::wcsprm& wcs, const IPosition& shape, LogIO& os) const { // Extract wcs structure pertaining to Stokes Coordinate int nsub = 1; Block axes(nsub); axes[0] = WCSSUB_STOKES; // ::wcsprm wcsDest; wcsInit (wcsDest); Bool ok = do_sub_wcs(wcs, nsub, axes, wcsDest, os); // See if we found the axis if (ok && nsub==1) { // Call wcssset on new struct setWCS (wcsDest); // Try to create StokesCoordinate stokesAxis = axes[0] - 1; // 1 -> 0 rel uInt stokesAxisShape = 1; if(stokesAxis<(Int)shape.size()){ stokesAxisShape = shape(stokesAxis); } Bool warnStokes = stokesFITSValue > 0; stokesFITSValue = -1; Vector stokes(1); stokes = 1; StokesCoordinate c(stokes); // No default constructor String errMsg; if (stokesCoordinateFromWCS (os, c, stokesFITSValue, errMsg, wcsDest, stokesAxisShape, warnStokes)) { cSys.addCoordinate(c); } else { os << LogIO::WARN << errMsg << LogIO::POST; ok = False; } } // Clean up wcsfree (&wcsDest); return ok; } Bool FITSCoordinateUtil::addSpectralCoordinate (CoordinateSystem& cSys, Int& specAxis, const ::wcsprm& wcs, const IPosition& shape, LogIO& os) const { // Extract wcs structure pertaining to Spectral Coordinate int nsub = 1; Block axes(nsub); axes[0] = WCSSUB_SPECTRAL; ::wcsprm wcsDest; wcsInit (wcsDest); Bool ok = do_sub_wcs(wcs, nsub, axes, wcsDest, os); uInt nc = 1; if(axes[0]-1<(Int)shape.nelements()){ nc = shape(axes[0]-1); // the number of channels of the spectral axis } SpectralCoordinate::SpecType nativeSType = SpectralCoordinate::FREQ; // See if we found the axis if (ok && nsub==1) { String errMsg; // throws exception if wcsset() fails setWCS (wcsDest); String cType = wcsDest.ctype[0]; if (cType.contains("WAVE") || cType.contains("AWAV")){ if(nc==0){ os << LogIO::WARN << "Will omit tabular spectral coordinate with no channels." << LogIO::POST; wcsfree (&wcsDest); return True; } // make a tabular frequency coordinate from the wavelengths MFrequency::Types freqSystem; specAxis = axes[0]-1; if (!frequencySystemFromWCS (os, freqSystem, errMsg, wcsDest)) { os << LogIO::WARN << errMsg << LogIO::POST; ok = False; } Double cRval = wcsDest.crval[0]; Double cRpix = wcsDest.crpix[0]; Double cDelt = wcsDest.cdelt[0]; Double cPc = wcsDest.pc[0]; Vector wavelengths(nc); //cout << "crval " << cRval << " crpix " << cRpix << " pc " << cPc << " cdelt " << cDelt << endl; String waveUnit = String(wcsDest.cunit[0]); Double restFrequency = wcs.restfrq; if (restFrequency==0.){ if(wcs.restwav != 0.){ restFrequency = C::c/wcs.restwav; } } for(uInt i=0; i frequencies(nc); //cout << "crval " << cRval << " crpix " << cRpix << " pc " << cPc << " cdelt " << cDelt << endl; //cout << "restfrq " << restFrequency << " cunit " << String(wcsDest.cunit[0]) << endl; Unit uCunit(String(wcsDest.cunit[0])); Unit mps("m/s"); for(uInt i=0; i-C::c){ frequencies(i) = restFrequency/(vel/C::c+1.); // in Hz } else{ frequencies(i) = HUGE_VAL; } //cout << "freq i " << i << " " << frequencies(i) << " Hz"<< endl; } SpectralCoordinate c(freqSystem, frequencies, restFrequency); nativeSType = SpectralCoordinate::VOPT; c.setNativeType(nativeSType); try { cSys.addCoordinate(c); } catch (std::exception& x) { os << LogIO::WARN << x.what() << LogIO::POST; ok = False; } } else{ // make a coordinate linear in frequency using wcslib // Convert the struct to a frequency base... // ...really convert to FREQ... casa (non-core) above depends // on receiving FREQ coordinates (not VELO)... if other uses // of addSpectralCoordinate( ) depend on retrieving VELO, then // FREQ conversion should be added as an option, taking advantage // of wcslib's conversion abilities. int index=0; char ctype[9]; if (cType.contains("FREQ")){ strcpy(ctype,"FREQ-???"); nativeSType = SpectralCoordinate::FREQ; } else if(cType.contains("VELO")){ strcpy(ctype, "FREQ-???"); nativeSType = SpectralCoordinate::VRAD; } else if (cType.contains("VRAD")){ strcpy(ctype, "FREQ-???"); nativeSType = SpectralCoordinate::VRAD; } else { os << LogIO::WARN << "Unrecognized frequency type" << LogIO::POST; ok = False; } if (ok) { int status = 0; if ((status=wcssptr(&wcsDest, &index, ctype))) { os << LogIO::WARN << "Failed to convert Spectral coordinate to Frequency, error status = " << status << ": " << endl << " " << wcs_errmsg[status] << endl; switch(status){ case 4: case 5: case 6: case 7: os << "Will try to continue ..."; break; default: os << "Will not try to continue ..."; ok = False; } os << LogIO::POST; } else { // throws exception if wcsset() fails setWCS (wcsDest); } } // Find frequency system MFrequency::Types freqSystem; if (ok) { specAxis = axes[0]-1; if (!frequencySystemFromWCS (os, freqSystem, errMsg, wcsDest)) { os << LogIO::WARN << errMsg << LogIO::POST; ok = False; } } // Try to create SpectralCoordinate and fix up zero // increments etc and add to CoordinateSystem if (ok) { try { Bool oneRel = True; // wcs structure from FITS has 1-rel pixel coordinate SpectralCoordinate c(freqSystem, wcsDest, oneRel); c.setNativeType(nativeSType); fixCoordinate (c, os); cSys.addCoordinate(c); } catch (std::exception& x) { os << LogIO::WARN << x.what() << LogIO::POST; ok = False; } } } } else { //os << LogIO::DEBUG1 << "passing empty or nonexistant spectral Coordinate axis" << LogIO::POST; os << "passing empty or nonexistant spectral Coordinate axis" << LogIO::POST; } // Clean up wcsfree (&wcsDest); return ok; } Bool FITSCoordinateUtil::directionSystemFromWCS (LogIO& os, MDirection::Types& type,String& errMsg, const ::wcsprm& wcs) const { // Extract Equinox keyword // Bool eqIsDefined = !undefined(wcs.equinox); Double equinox(0.0); if (eqIsDefined) equinox = wcs.equinox; Bool eqIs1950(False); Bool eqIs1950VLA(False); Bool eqIs2000(False); if (eqIsDefined) { eqIs1950 = casacore::near(equinox, 1950.0); eqIs1950VLA = casacore::near(equinox, 1979.9); eqIs2000 = casacore::near(equinox, 2000.0); } // Extract RADESYS keyword Bool sysIsDefined = wcs.radesys[0]!='\0'; String raDecSys; if (sysIsDefined) { String tt(wcs.radesys); Int i1 = tt.index(RXwhite,0); if (i1==-1) i1 = tt.length(); raDecSys = String(tt.before(i1)); } // Extract CTYPEs (must exist) String cTypeLon(wcs.ctype[0]); String cTypeLat(wcs.ctype[1]); cTypeLon.upcase(); cTypeLat.upcase(); // See if we have xLON/xLAT pair String cLon(cTypeLon.at(0,4)); String cLat(cTypeLat.at(0,4)); ostringstream oss2; if (cLon=="GLON" && cLat=="GLAT") { // galactic coordinates type = MDirection::GALACTIC; return True; } else if (cLon=="ELON" && cLat=="ELAT") { // ecliptic for J2000 equator and equinox // Paper II suggests to use DATE-OBS or MJD-OBS rather than equinox ? if (!eqIsDefined || (eqIsDefined && eqIs2000)) { type = MDirection::ECLIPTIC; return True; } else { oss2 << "Equinox " << equinox << " is invalid for Ecliptic Coordinates - must be 2000.0"; errMsg = String(oss2); return False; } } else if (cLon=="SLON" && cLat=="SLAT") { // supergalactic coordinates type = MDirection::SUPERGAL; return True; } else if (cLon=="HLON" && cLat=="HLAT") { errMsg = String("Helioecliptic Coordinates are not supported"); return False; } else { String cLon2(cTypeLon.at(1,3)); String cLat2(cTypeLat.at(1,3)); if ( (cLon2=="LON" || cLat2=="LAT") || (cLon2=="LAT" || cLat2=="LON") ) { oss2 << cLon << " and " << cLat << " are unsupported LON/LAT types"; errMsg = String(oss2); return False; } } // OK we have dispensed with xLON/xLAT, let's move on to the rest // Since we have successfully constructed a celestial wcsprm object // we can assume the CTYPEs are correct if (raDecSys==String("ICRS")) { if (!eqIsDefined || eqIs2000) { type = MDirection::ICRS; return True; } else { oss2 << "Direction system ICRS with equinox " << equinox << " is not supported"; errMsg = String(oss2); return False; } } else if (raDecSys==String("FK5")) { if (!eqIsDefined || eqIs2000) { // equinox always Julian for FK5 type = MDirection::J2000; // Needs return True; } else { oss2 << "Direction system FK5 with equinox " << equinox << " is not supported"; errMsg = String(oss2); return False; } } else if (raDecSys==String("FK4")) { if (!eqIsDefined || eqIs1950) { // equinox always Besellian for FK4 type = MDirection::B1950; return True; } else if (!eqIsDefined || eqIs1950VLA) { type = MDirection::B1950_VLA; return True; } else { oss2 << "Direction system FK4 with equinox " << equinox << " is not supported"; errMsg = String(oss2); return False; } } else if (raDecSys==String("FK4-NO-E")) { if (!eqIsDefined || eqIs1950) { // equinox always Besellian type = MDirection::B1950; return True; } else if (!eqIsDefined || eqIs1950VLA) { type = MDirection::B1950_VLA; return True; } else { oss2 << "Direction system FK4-NO-E with equinox " << equinox << " is not supported"; errMsg = String(oss2); return False; } } else if (raDecSys==String("GAPPT")) { type = MDirection::APP; errMsg = String("Direction system GAPPT is not supported"); return False; } else { if (sysIsDefined) { oss2 << "Direction system '" << raDecSys << "' is not supported"; errMsg = String(oss2); return False; } else { if (eqIsDefined) { // No RaDecSys but Equinox available if (equinox>=1984.0) { // Paper II type = MDirection::J2000; // FK5 return True; } else if (casacore::near(equinox,1979.9)) { type = MDirection::B1950_VLA; return True; } else { type = MDirection::B1950; // FK4 return True; } } else { // No RaDecSys or equinox os << "No Direction system is defined - J2000 assumed" << LogIO::POST; type = MDirection::J2000; // Defaults to ICRS return True; } } } // errMsg = String("FITSCoordinateUtil::directionSystemFromWCS - logic error"); return False; } Bool FITSCoordinateUtil::frequencySystemFromWCS (LogIO& os, MFrequency::Types& type,String& errMsg, const ::wcsprm& wcs) const // // After running it through the wcsFixItUp function, I can assume that // wcs.specsys will always be filled in and that CTYPE will be adjusted // appropriately. // { if (wcs.specsys[0]=='\0') { if (wcs.velref==0) { // velref was also not given os << LogIO::NORMAL << "Neither SPECSYS nor VELREF keyword given, spectral reference frame not defined ..." << LogIO::POST; type = MFrequency::Undefined; return True; } else { // velref was given Int vref = wcs.velref; os << LogIO::NORMAL << "No SPECSYS but found (deprecated) VELREF keyword with value " << vref << LogIO::POST; if(vref>256){ vref -= 256; } switch(vref){ case 1: type = MFrequency::LSRK; os << LogIO::NORMAL << " => LSRK assumed" << LogIO::POST; break; case 2: type = MFrequency::BARY; os << LogIO::NORMAL << " => BARY assumed" << LogIO::POST; break; case 3: type = MFrequency::TOPO; os << LogIO::NORMAL << " => TOPO assumed" << LogIO::POST; break; case 4: type = MFrequency::LSRD; os << LogIO::NORMAL << " => LSRD assumed" << LogIO::POST; break; case 5: type = MFrequency::GEO; os << LogIO::NORMAL << " => GEO assumed" << LogIO::POST; break; case 6: type = MFrequency::REST; os << LogIO::NORMAL << " => REST assumed" << LogIO::POST; break; case 7: type = MFrequency::GALACTO; os << LogIO::NORMAL << " => GALACTO assumed" << LogIO::POST; break; default: type = MFrequency::TOPO; os << LogIO::WARN << "Undefined by AIPS convention. TOPO assumed." << LogIO::POST; break; } return True; } } String specSys(wcs.specsys); specSys.upcase(); // Extract system ostringstream oss; if (specSys=="TOPOCENT") { type = MFrequency::TOPO; return True; } else if (specSys=="GEOCENTR") { type = MFrequency::GEO; return True; } else if (specSys=="BARYCENT") { type = MFrequency::BARY; return True; } else if (specSys=="HELIOCEN") { type = MFrequency::BARY; os << LogIO::NORMAL << "The HELIOCENTRIC frequency system is deprecated in FITS - it is assumed BARYCENTIC was meant" << LogIO::POST; return True; } else if (specSys=="LSRK") { type = MFrequency::LSRK; return True; } else if (specSys=="LSRD") { type = MFrequency::LSRD; return True; } else if (specSys=="GALACTOC") { type = MFrequency::GALACTO; return True; } else if (specSys=="LOCALGRP") { type = MFrequency::LGROUP; return True; } else if (specSys=="CMBDIPOL") { type = MFrequency::CMB; return True; } else if (specSys=="SOURCE") { type = MFrequency::REST; return True; } else { oss << "Frequency system '" << specSys << "' is not supported"; errMsg = String(oss); return False; } // errMsg = String("FITSCoordinateUtil::frequencySystemFromWCS - logic error"); return False; } Bool FITSCoordinateUtil::stokesCoordinateFromWCS (LogIO& os, StokesCoordinate& coord, Int& stokesFITSValue, String& errMsg, const ::wcsprm& wcs, uInt shape, Bool warnStokes) const { // For the StokesCoordinate, the shape is not separable from the coordinate if (shape>4) { os << "The Stokes axis is longer than 4 pixels. This is not supported" << LogIO::EXCEPTION; return False; } // if (wcs.naxis != 1) { os << "The wcs structure holding the StokesAxis can only have one axis" << LogIO::EXCEPTION; } // Fish out values Double crpix = wcs.crpix[0] - 1.0; // Make 0-rel Double crval = wcs.crval[0]; Double cdelt = wcs.cdelt[0]; // Vector stokes(shape); for (uInt k=0; k= 0) { stokes(k) = Int(tmp + 0.01); } else { stokes(k) = Int(tmp - 0.01); } // if (stokes(k)==0) { if (warnStokes) { os << LogIO::NORMAL << "Detected Stokes coordinate = 0; this is an unoffical" << endl; os << "Convention for an image containing a beam. Putting Stokes=Undefined" << endl; os << "Better would be to write your FITS image with the correct Stokes" << LogIO::POST; } // stokes(k) = Stokes::Undefined; stokesFITSValue = 0; } else if (stokes(k)==5) { os << LogIO::SEVERE << "The FITS image Stokes axis has the unofficial percentage polarization value." << endl; os << "This is not supported. Will use fractional polarization instead " << endl; os << "You must scale the image by 0.01" << LogIO::POST; stokes(k) = Stokes::PFlinear; } else if (stokes(k)==8) { if (warnStokes) { os << LogIO::SEVERE << "The FITS image Stokes axis has the unofficial spectral index value." << endl; os << "This is not supported. Putting Stokes=Undefined" << LogIO::POST; } stokes(k) = Stokes::Undefined; stokesFITSValue = 8; } else if (stokes(k)==9) { if (warnStokes) { os << LogIO::SEVERE << "The Stokes axis has the unofficial optical depth" << endl; os << "value. This is not supported. Putting Stokes=Undefined" << LogIO::POST; } stokes(k) = Stokes::Undefined; stokesFITSValue = 9; } else { Stokes::StokesTypes type = Stokes::fromFITSValue(stokes(k)); if (type == Stokes::Undefined) { os << LogIO::SEVERE << "A Stokes coordinate of " << stokes(k) << " was detected; this is not valid. Putting Stokes=Undefined" << endl; } stokes(k) = type; } } // Now make StokesCoordinate try { coord = StokesCoordinate(stokes); } catch (std::exception& x) { errMsg = x.what(); return False; } // return True; } ObsInfo FITSCoordinateUtil::getObsInfo (LogIO& os, RecordInterface& header, const ::wcsprm& wcs) const { ObsInfo oi; // Observer and Telescope are in the FITS cards record. Vector error; oi.fromFITS (error, header); // Now overwrite the date info from the wcs struct String timeSysStr("UTC"); if (header.isDefined("timesys")) { Record subRec = header.asRecord("timesys"); timeSysStr = subRec.asString("value"); } // MEpoch::Types timeSystem; MEpoch::getType (timeSystem, timeSysStr); // The date information is in the WCS structure // 'mjdobs' takes precedence over 'dateobs' Bool mjdIsDefined = !undefined(wcs.mjdobs); Bool dateObsDefined = wcs.dateobs[0]!='\0'; if (mjdIsDefined) { Double mjdObs = wcs.mjdobs; // MEpoch dateObs(Quantum(mjdObs,"d"), timeSystem); oi.setObsDate (dateObs); } else if (dateObsDefined) { // String dateObsStr(wcs.dateobs[0]); String dateObsStr(wcs.dateobs); MVTime time; if (FITSDateUtil::fromFITS(time, timeSystem, dateObsStr, timeSysStr)) { oi.setObsDate(MEpoch(time.get(), timeSystem)); } else { os << LogIO::NORMAL << "Failed to decode DATE-OBS & TIMESYS keywords - no date set" << LogIO::POST; } } // Remove fields from record Vector cards = ObsInfo::keywordNamesFITS(); for (uInt i=0; i FITSCoordinateUtil::cTypeFromDirection( Bool& isNCP, const Projection& proj, const Vector& axisNames, Double refLat, Bool printError ) { // // RefLat in radians // { DirectionCoordinate dc( MDirection::J2000, proj, 0, refLat, 1e-5, -1e-5, Matrix::identity(2), 0, 0 ); isNCP = dc.isNCP(); } return cTypeFromDirection(proj, axisNames, printError); } Vector FITSCoordinateUtil::cTypeFromDirection ( const Projection& proj, const Vector& axisNames, Bool printError ) { LogIO os(LogOrigin("FITSCoordinateUtil", "cTypeFromDirection", WHERE)); Vector ctype(2); for (uInt i=0; i<2; i++) { String name = axisNames(i); while (name.length() < 4) { name += "-"; } switch(proj.type()) { // Zenithal/Azimuthal perspective. case Projection::AZP: // Slant zenithal perspective, new case Projection::SZP: // Gnomonic. case Projection::TAN: // Stereographic. case Projection::STG: // zenith/azimuthal equidistant. case Projection::ARC: // zenithal/azimuthal polynomial. case Projection::ZPN: // zenithal/azimuthal equal area. case Projection::ZEA: // Airy. case Projection::AIR: // Cylindrical perspective. case Projection::CYP: // Plate carree case Projection::CAR: // Mercator. case Projection::MER: // Cylindrical equal area. case Projection::CEA: // Conic perspective. case Projection::COP: // Conic equidistant. case Projection::COD: // Conic equal area. case Projection::COE: // Conic orthomorphic. case Projection::COO: // Bonne. case Projection::BON: // Polyconic. case Projection::PCO: // Sanson-Flamsteed (global sinusoidal). // The old GLS projection is now SFL. The 'GLS' // string will be converted to 'SFL' case Projection::SFL: // Parabolic. case Projection::PAR: // Hammer-Aitoff. case Projection::AIT: // Mollweide. case Projection::MOL: // COBE quadrilateralized spherical cube. case Projection::CSC: // Quadrilateralized spherical cube. case Projection::QSC: // Tangential spherical cube. case Projection::TSC: // HEALPix grid, new case Projection::HPX: // Orthographics/synthesis. case Projection::SIN: name = name + "-" + proj.name(); break; default: if (i == 0) { // Only print the message once for long/lat if (printError) { os << LogIO::WARN << proj.name() << " is not known to standard FITS (it is known to WCS)." << LogIO::POST; } } name = name + "-" + proj.name(); break; } ctype(i) = name; } return ctype; } void FITSCoordinateUtil::setWCS (::wcsprm& wcs) const { Coordinate::set_wcs(wcs); } Bool FITSCoordinateUtil::getCDFromHeader(Matrix& cd, uInt n, const RecordInterface& header) // // We have to read the CDj_i cards and ultimately pack them into the // WCS linprm structure in the right order. // The expected order in WCS linprm is // // lin.pc = {CD1_1, CD1_2, CD2_1, CD2_2} ... // // You can get this via // // pc[2][2] = {{CD1_1, CD1_2}, // {CD2_1, CD2_2}} // // which is to say, // // pc[0][0] = CD1_1, // pc[0][1] = CD1_2, // pc[1][0] = CD2_1, // pc[1][1] = CD2_2, // // for which the storage order is // // CD1_1, CD1_2, CD2_1, CD2_2 // // so linprm will be happy if you set // // lin.pc = *pc; // // This packing and unpacking actually happens in // LinearXform::set_linprm and LinearXform::pc // // as we stuff the CD matrix inro the PC matrix // and set cdelt = 1 deg // { cd.resize(n,n); cd = 0.0; cd.diagonal() = 1.0; // for (uInt i=0; i& pc, uInt n, const RecordInterface& header, const String& sprefix) { if (header.isDefined("pc")) { // Unlikely to encounter this, as the current WCS papers // use the CD rather than PC matrix. The Casacore user binding // (Image tool) does not allow the WCS definition to be written // so probably we could remove this if (header.isDefined(sprefix + "rota")) { os << "Ignoring redundant " << sprefix << "rota in favour of " "pc matrix." << LogIO::NORMAL << LogIO::POST; } header.get("pc", pc); if (pc.ncolumn() != pc.nrow()) { os << "The PC matrix must be square" << LogIO::EXCEPTION; } } else if (header.isDefined(sprefix + "rota")) { Vector crota; header.get(sprefix + "rota", crota); // Turn crota into PC matrix pc.resize(crota.nelements(), crota.nelements()); pc = 0.0; pc.diagonal() = 1.0; // We can only handle one non-zero angle for (uInt i=0; i= 0) { os << LogIO::SEVERE << "Can only convert one non-zero" " angle from " << sprefix << "rota to pc matrix. Using the first." << LogIO::POST; } else { rotationAxis = i; } } } // if (rotationAxis >= 0 && pc.nrow() > 1) { // can't rotate 1D! if (rotationAxis > 0) { pc(rotationAxis-1,rotationAxis-1) = pc(rotationAxis,rotationAxis) = cos(crota(rotationAxis)*M_PI/180.0); pc(rotationAxis-1,rotationAxis)= -sin(crota(rotationAxis)*M_PI/180.0); pc(rotationAxis,rotationAxis-1)= sin(crota(rotationAxis)*M_PI/180.0); } else { os << LogIO::NORMAL << "Unusual to rotate about first" " axis." << LogIO::POST; pc(rotationAxis+1,rotationAxis+1) = pc(rotationAxis,rotationAxis) = cos(crota(rotationAxis)*M_PI/180.0); // Assume sign of rotation is correct although its not on the expected axis (AIPS convention) pc(rotationAxis,rotationAxis+1)=-sin(crota(rotationAxis)*M_PI/180.0); pc(rotationAxis+1,rotationAxis)= sin(crota(rotationAxis)*M_PI/180.0); } } } else { // Pure diagonal PC matrix pc.resize(n, n); pc = 0.0; pc.diagonal() = 1.0; } } void FITSCoordinateUtil::cardsToRecord (LogIO& os, RecordInterface& rec, char* pHeader) const // // Convert the fitshdr struct to a Casacore Record for ease of later use // { // Specific keywords to be located const uInt nKeyIds = 0; ::fitskeyid keyids[1]; // Parse the header // sanitize to avoid segfaults in fitshdr Bool crlfwarned = False; for(uInt i=0; i 0); subRec.define("value", value); break; } case 2: // 32-bit Int { Int value(keys[i].keyvalue.i); subRec.define("value", value); break; } case 3: // 64-bit Int { os << LogIO::WARN << "Cannot yet handle 64-bit Ints; dropping card " << name << LogIO::POST; break; } case 4: // Very long integer { os << LogIO::WARN << "Cannot yet handle very long Ints; dropping card " << name << LogIO::POST; break; } case 5: // Floating point { Double value(keys[i].keyvalue.f); subRec.define("value", value); break; } case 6: // Integer and floating complex case 7: { Complex value(keys[i].keyvalue.c[0],keys[i].keyvalue.c[1]); subRec.define("value", value); break; } case 8: // String { String value(keys[i].keyvalue.s); subRec.define("value", value); break; } default: { if (keys[i].type < 0) { os << LogIO::WARN << "Failed to extract card " << keys[i].keyword << LogIO::POST; } break; } } // subRec.define("value", String("TEST")); // If we managed to parse the keyword, then deal with Units and comments. // Units are in inline comment in the form [m/s] (we strip the []) if (subRec.isDefined("value")) { String comment(keys[i].comment); if (keys[i].ulen>0) { String unit(comment, 1, keys[i].ulen-2); subRec.define("unit", unit); } else { subRec.define("comment", comment); } // Define sub record if (rec.isDefined(name)) { os << LogIO::NORMAL << "Duplicate card '" << name << "'in header - only first will be used" << LogIO::POST; } else { rec.defineRecord(name, subRec); } } } // free (keys); } void FITSCoordinateUtil::fixCoordinate (Coordinate& c, LogIO& os) const { return; // Vector cdelt = c.increment(); Vector crval = c.referenceValue(); // const uInt n = cdelt.nelements(); Coordinate::Type type = c.type(); String sType = c.showType(); // for (uInt i=0; i #include #include #include struct wcsprm; namespace casacore { //# NAMESPACE CASACORE - BEGIN class Coordinate; class CoordinateSystem; class StokesCoordinate; class Projection; class IPosition; class LogIO; class Record; // // // // // // // //
      • CoordinateSystem // // // Helper functions to inter-converft between a CoordinateSystem and FITS // headers. // // // // // // // I hate FITS // // // //
      • AipsError // // // // // class FITSCoordinateUtil { public: // Constructor FITSCoordinateUtil() {;}; // Convert CoordinateSystem to a FITS header. In the record // the keywords are vectors, it is expected that the actual FITS code will // split them into scalars and upcase the names. Returns False if one of the // keywords is already taken. // // If writeWCS is True, attempt to write the WCS convention (Greisen and // Calabretta "Representation of celestial coordinates in FITS") as // approved in version 3.0 of the FITS standard. // Use oneRelative=True to convert zero-relative pixel coordinates to // one-relative FITS coordinates. // // prefix gives the prefix for the FITS keywords. E.g., // if prefix="c" then crval, cdelt etc. // if prefix="d" then drval, ddelt etc. //# Much of the work in to/from fits should be moved to the individual //# classes. Bool toFITSHeader(RecordInterface &header, IPosition &shape, const CoordinateSystem& cSys, Bool oneRelative, Char prefix = 'c', Bool writeWCS=True, Bool preferVelocity=True, Bool opticalVelocity=True, Bool preferWavelength=False, Bool airWavelength=False) const; // Probably even if we return False we should set up the best linear // coordinate that we can. On output, stokesFITSValue // holds the FITS value of any unofficial Stokes (beam, optical depth, // spectral index) for the last unofficial value accessed (-1 if none). // The idea is that if the Stokes axis is of length one and holds an unofficial value, // you should drop the STokes axis and convert that value to ImageInfo::ImageTypes // with ImageInfo::imageTypeFromFITSValue. If on input, stokesFITSValue // is positive, then a warning is issued if any unofficial values are encountered. // Otherwise no warning is issued. //# cf comment in toFITS. // Bool fromFITSHeader(Int& stokesFITSValue, CoordinateSystem& coordsys, RecordInterface& recHeader, const Vector& header, const IPosition& shape, uInt which=0) const; // // Helper function to create a FITS style CTYPE vector from the // axis names from a DirectionCoordinate static Vector cTypeFromDirection (Bool& isNCP, const Projection& proj, const Vector& axisNames, Double refLat, Bool printError); static Vector cTypeFromDirection (const Projection& proj, const Vector& axisNames, Bool printError); private: // Generate actual FITS keywords Bool generateFITSKeywords (LogIO& os, Bool& isNCP, Double& longPole, Double& latPole, Vector& crval, Vector& crpix, Vector& cdelt, //# Vector& crota, //# Vector& projp, Vector& pvi_ma, Vector& ctype, Vector& cunit, Matrix& pc, const CoordinateSystem& cSys, Int skyCoord, Int longAxis, Int latAxis, Int specAxis, Int stokesAxis, Bool writeWCS, Double offset, const String& sprefix) const; // Special Stokes processing for conversion to FITS header Bool toFITSHeaderStokes(Vector& crval, Vector& crpix, Vector& cdelt, LogIO& os, const CoordinateSystem& coordsys, Int stokesAxis, Int stokesCoord) const; // Look for Coordinate type and add to CS // Bool addDirectionCoordinate (CoordinateSystem& cSys, Vector& axes, const wcsprm& wcs, LogIO& os) const; Bool addSpectralCoordinate (CoordinateSystem& cSys, Int& axis, const wcsprm& wcs, const IPosition& shape, LogIO& os) const; Bool addStokesCoordinate (CoordinateSystem& cSys, Int& axis, Int& stokesFITSValue, const wcsprm& wcs, const IPosition& shape, LogIO& os) const; Bool addLinearCoordinate (CoordinateSystem& cSys, Vector& axes, const wcsprm& wcs, LogIO& os) const; // // Decode values from WCS structures which are generated via the wcs FITS parser // Bool directionSystemFromWCS (LogIO& os, MDirection::Types& type, String& errMsg, const wcsprm& wcs) const; Bool frequencySystemFromWCS (LogIO& os, MFrequency::Types& type, String& errMsg, const wcsprm& wcs) const; Bool stokesCoordinateFromWCS (LogIO& os, StokesCoordinate& coord, Int& stokesFITSValue, String& errMSg, const wcsprm& wcs, uInt shape, Bool warnStokes) const; // // Decode ObsInfo from wcs structure ObsInfo getObsInfo(LogIO& os, RecordInterface& header, const wcsprm& wcs) const; // Call wcsset void setWCS (wcsprm& wcs) const; // Decode CD cards from FITS file header (Record interface) Bool getCDFromHeader(Matrix& cd, uInt n, const RecordInterface& header); // Decode PC matrix from FITS header (Record interface) void getPCFromHeader(LogIO& os, Int& rotationAxis, Matrix& pc, uInt n, const RecordInterface& header, const String& sprefix); // Helper function to convert a wcs structure holding FITS keywords // into a Record for later consumption. void cardsToRecord (LogIO& os, RecordInterface& rec, char* pHeader) const; // Fix up Coordinate for zero increments and the like // Possibly the wcs FITS parser could do this void fixCoordinate(Coordinate& c, LogIO& os) const; // Initialize the wcsprm struct. // It sets the flag to -1, but furthermore it clears the err pointers // because wcslib-4.8 (shipped with Ubuntu) sometimes fails to do so. static void wcsInit (::wcsprm& wcsDest); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/coordinates/Coordinates/FrequencyAligner.h000066400000000000000000000215061476623553700235300ustar00rootroot00000000000000//# FrequenctAligner.h: Align spectra in frequency space //# Copyright (C) 1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef COORDINATES_FREQUENCYALIGNER_H #define COORDINATES_FREQUENCYALIGNER_H //# Includes #include #include #include #include #include #include namespace casacore { //# Forward Declarations class MEpoch; class MDirection; class MPosition; class String; // // Aligns spectra in frequency space // // // // // // // // //
      • InterpoateArray1D //
      • Array // // // Spectra are converted to the specified reference frame and aligned at // a specified instant in time. // // You should not try to convert from, say, a SpectralCoordinate::TOPO to // MFrequency::TOPO as this would be meaningless. This class is designed // mainly to convert say from a SpectralCoordinate::TOPO to say, a BARY // frame and align. // // // // Required for ASAP single-dish package // // // // template class FrequencyAligner { public: // Default constructor (object not viable) FrequencyAligner(); // Constructor specifies a SpectralCoordinate (any extra reference conversion // frame set in it will be ignored), the number of pixels in the spectra to // be aligned, a reference epoch to which all spectra will // be aligned, a direction on the sky, a position on the earth (the observatory), // and desired frequency system to align in. FrequencyAligner(const SpectralCoordinate& specCoord, uInt nPixels, const MEpoch& refEpoch, const MDirection& dir, const MPosition& pos, MFrequency::Types freqSystem); // Copy constructor (copy semantics) FrequencyAligner (const FrequencyAligner& other); // Assignment (copy semantics) FrequencyAligner& operator=(const FrequencyAligner& other); // Destructor ~FrequencyAligner(); // Set a tolerance (in pixels) to trigger regridding (function align). // If the maximum abcissa difference for the current spectrum abcissa compared // to the reference abcissa is greater than tol (pixels) then a // regrid is triggered. Otherwise the input is just copied to the output when // function align is called. Set to 0 to turn this tolerance // assessment off. This function may be not really worth using. void setTolerance (Double tol) {itsDiffTol = abs(tol);}; // Align (via regridding) one spectrum taken at the specified epoch to // the reference epoch. Your provide the ordinate and mask (True==Good) // for the spectrum. The lengths of these vectors must be the same // as nPixels given in the constructor. The output vectors // are resized as needed. // You can use the last cached abcissa (computed by // this function) rather than recompute it if you have more than one spectrum // at the same epoch to convert (e.g. different polarizations). // If you do this, it is your responsibility to make sure that you // have called this function at least once with useCachedAbcissa=False. // If extrapolate is True, the regridding process is allowed // to extrapolate outside of the abcissa domain. Otherwise masked pixels will result. // Returns True if a regrid triggered, else False if just copied (see function // setTolerance. Bool align (Vector& yOut, Vector& maskOut, const Vector& yIn, const Vector& maskIn, const MEpoch& epoch, Bool useCachedAbcissa, typename InterpolateArray1D::InterpolationMethod method, Bool extrapolate=False); // This function is the same as the previous except that you can specify the input abcissa as well // as the data and mask. The input abcissa must be in the same units as the Construction // SpectralCoordinate. The abcissa values must be in the same base reference frame // as the Construction SpectralCoordinate. So instead of the abcissa (in the // output reference frame) being computed from the Construction SC, you get to specify // the abcissa directly. This might be useful if you have more than one set of // spectra to align, all in the same Frame, but with different attributes such // as reference value/pixel etc. The output spectrum is still regridded to the // abcissa at the reference time generated at construction. // from the current Bool align (Vector& yOut, Vector& maskOut, const Vector& xIn, const Vector& yIn, const Vector& maskIn, const MEpoch& epoch, Bool useCachedAbcissa, typename InterpolateArray1D::InterpolationMethod method, Bool extrapolate=False); // Align many spectra stored in an Array along the specified axis. All spectra are aligned // to the same frequency abcissa (as described in previous function). If any alignment // returns False, then the return value will be False, otherwise True is returned. Bool alignMany (Array& yOut, Array& maskOut, const Array& yIn, const Array& maskIn, uInt axis, const MEpoch& epoch, typename InterpolateArray1D::InterpolationMethod method, Bool extrapolate=False); // Get the reference abcissa (as a frequency in the axis units set in the SpectralCoordinate) at the reference epoch void getReferenceAbcissa (Vector& xOut) const; // Get the abcissa (as a frequency in the axis units set in the SpectralCoordinate) last cached by function align void getAbcissa (Vector& xOut) const; // Get new aligned SpectralCoordinate. It is probably non-linear, but if you would // like a linear approximation, use the doLinear argument. SpectralCoordinate alignedSpectralCoordinate (Bool doLinear=True) const; private: SpectralCoordinate itsSpecCoord; MFrequency::Convert itsMachine; MFrequency::Ref itsRefOut; // Need this as there is no easy way to update // the conversion machines epoch otherwise MFrequency::Types itsFreqSystem; // Vector itsRefFreqX; // Reference frequency abcissa Vector itsFreqX; // Frequency abcissa Double itsDiffTol; // Tolerance which triggers a regrid // Internal copy void copyOther (const FrequencyAligner& other); // Create the Conversion machine void makeMachine (const MEpoch& refEpoch, const MDirection& dir, const MPosition& pos, MFrequency::Types freqSystem, const Unit& unit); // Generate an abcissa with the machine Double makeAbcissa (Vector& f, Bool doMaxDiff); // Regrid one spectrum Bool regrid (Vector& yOut, Vector& maskOut, const Vector& xOut, const Vector& xIn, const Vector& yIn, const Vector& maskIn, typename InterpolateArray1D::InterpolationMethod method, Bool extrapolate, Double maxDiff) const; }; } //# End namespace casacore #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/coordinates/Coordinates/FrequencyAligner.tcc000066400000000000000000000315651476623553700240600ustar00rootroot00000000000000//# FrequencyAligner.cc: implements FrequencyAligner which aligns spectra in frequency space //# Copyright (C) 1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef COORDINATES_FREQUENCYALIGNER_TCC #define COORDINATES_FREQUENCYALIGNER_TCC #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { template FrequencyAligner::FrequencyAligner() : itsRefFreqX(0), itsFreqX(0), itsDiffTol(0.0) {} template FrequencyAligner::FrequencyAligner(const SpectralCoordinate& specCoord, uInt nPixels, const MEpoch& refEpoch, const MDirection& dir, const MPosition& pos, MFrequency::Types freqSystem) : itsSpecCoord(specCoord), itsFreqSystem(freqSystem), itsRefFreqX(0), itsFreqX(0), itsDiffTol(0.0) { // Reset the conversion machinery so that there are no extra frame // conversions in the SpectralCoordinate (its machinery is not optimum // for a lot of conversions) itsSpecCoord.setReferenceConversion (itsSpecCoord.frequencySystem(), refEpoch, pos, dir); // Generate the Frequency Machine and set the epoch to the reference epoch Unit unit(specCoord.worldAxisUnits()(0)); makeMachine (refEpoch, dir, pos, itsFreqSystem, unit); // Generate the frequency abcissa at the reference epoch. The frequency // spectrum is of the MFrequency::Types of the SC. itsRefFreqX.resize(nPixels); makeAbcissa (itsRefFreqX, False); // itsFreqX.resize(nPixels); itsFreqX = 0.0; } template FrequencyAligner::FrequencyAligner(const FrequencyAligner& other) : itsRefFreqX(0), itsFreqX(0), itsDiffTol(0.0) { copyOther(other); } template FrequencyAligner& FrequencyAligner::operator=(const FrequencyAligner& other) { if (this != &other) { copyOther(other); } // return *this; } template FrequencyAligner::~FrequencyAligner() {} template Bool FrequencyAligner::align (Vector& yOut, Vector& maskOut, const Vector& yIn, const Vector& maskIn, const MEpoch& epoch, Bool useCachedAbcissa, typename InterpolateArray1D::InterpolationMethod method, Bool extrapolate) { const uInt nPixels = itsRefFreqX.nelements(); AlwaysAssert(nPixels>1, AipsError); AlwaysAssert(yIn.nelements()==nPixels,AipsError); AlwaysAssert(maskIn.nelements()==nPixels,AipsError); // Update epoch in FrequencyMachine itsRefOut.getFrame().resetEpoch(epoch); itsMachine.setOut(itsRefOut); // Generate abcissa at this epoch Double maxDiff = -1; if (useCachedAbcissa) { maxDiff = abs(itsFreqX[0]-itsRefFreqX[0]); } else { maxDiff = makeAbcissa (itsFreqX, True); } maxDiff /= abs(itsRefFreqX[1]-itsRefFreqX[0]); // Max diff as a fraction of a channel // Regrid to reference frequency abcissa. Bool ok = regrid (yOut, maskOut, itsRefFreqX, itsFreqX, yIn, maskIn, method, extrapolate, maxDiff); return ok; } template Bool FrequencyAligner::align (Vector& yOut, Vector& maskOut, const Vector& xIn, const Vector& yIn, const Vector& maskIn, const MEpoch& epoch, Bool useCachedAbcissa, typename InterpolateArray1D::InterpolationMethod method, Bool extrapolate) { const uInt nPixels = itsRefFreqX.nelements(); AlwaysAssert(nPixels>1, AipsError); AlwaysAssert(xIn.nelements()==nPixels,AipsError); AlwaysAssert(yIn.nelements()==nPixels,AipsError); AlwaysAssert(maskIn.nelements()==nPixels,AipsError); // Update epoch in FrequencyMachine itsRefOut.getFrame().resetEpoch(epoch); itsMachine.setOut(itsRefOut); // The user provided abcissa is in the input Frame. Convert it to the output // Frame at the specfied Epoch Double maxDiff = -1; if (useCachedAbcissa) { maxDiff = abs(itsFreqX[0]-itsRefFreqX[0]); } else { for (uInt i=0; i Bool FrequencyAligner::alignMany (Array& yOut, Array& maskOut, const Array& yIn, const Array& maskIn, uInt axis, const MEpoch& epoch, typename InterpolateArray1D::InterpolationMethod method, Bool extrapolate) { // Checks const IPosition shp = yIn.shape(); AlwaysAssert(shp.isEqual(maskIn.shape()), AipsError); const Int n = itsRefFreqX.nelements(); AlwaysAssert(n>1, AipsError); AlwaysAssert(axis yItIn(yIn, axis); ReadOnlyVectorIterator mItIn(maskIn, axis); VectorIterator yItOut(yOut, axis); VectorIterator mItOut(maskOut, axis); // Iterate through Array and align each vector with the same grid Bool ok = True; while (!yItIn.pastEnd()) { // Align Bool ok2 = regrid (yItOut.vector(), mItOut.vector(), itsRefFreqX, itsFreqX, yItIn.vector(), mItIn.vector(), method, extrapolate, maxDiff); if (!ok2) ok = False; // Next vector yItIn.next(); mItIn.next(); yItOut.next(); mItOut.next(); } // return ok; } template void FrequencyAligner::getReferenceAbcissa(Vector& xOut) const { xOut.resize(itsRefFreqX.nelements()); xOut = itsRefFreqX; } template void FrequencyAligner::getAbcissa(Vector& xOut) const { xOut.resize(itsFreqX.nelements()); xOut = itsFreqX; } // Private functions template Bool FrequencyAligner::regrid (Vector& yOut, Vector& maskOut, const Vector& xOut, const Vector& xIn, const Vector& yIn, const Vector& maskIn, typename InterpolateArray1D::InterpolationMethod method, Bool extrapolate, Double maxDiff) const { Bool ok = False; if (maxDiff > itsDiffTol) { Int methodInt = static_cast(method); InterpolateArray1D::interpolate (yOut, maskOut, xOut, xIn, yIn, maskIn, methodInt, True, extrapolate); ok = True; } else { yOut.resize(yIn.nelements()); yOut = yIn; maskOut.resize(maskIn.nelements()); maskOut = maskIn; ok = False; } // return ok; } template void FrequencyAligner::makeMachine (const MEpoch& refEpoch, const MDirection& dir, const MPosition& pos, MFrequency::Types freqSystem, const Unit& unit) { // Make the Frequency conversion machine. All we have to do with it in // future is update the epoch LogIO os(LogOrigin("FrequencyAligner", "makeMachine", WHERE)); if (!CoordinateUtil::makeFrequencyMachine(os, itsMachine, freqSystem, itsSpecCoord.frequencySystem(), dir, dir, refEpoch, refEpoch, pos, pos, unit)) { os << "A trial conversion failed - something wrong with frequency conversion machine" << LogIO::EXCEPTION; } // MeasFrame frameOut; frameOut.set(dir); frameOut.set(refEpoch); frameOut.set(pos); itsRefOut = MFrequency::Ref(freqSystem, frameOut); } template Double FrequencyAligner::makeAbcissa (Vector& freq, Bool doDiff) { const uInt n = freq.nelements(); Double world; Double maxDiff = -1; if (doDiff) { for (uInt i=0; i void FrequencyAligner::copyOther(const FrequencyAligner& other) { itsMachine = other.itsMachine; // itsFreqSystem = other.itsFreqSystem; // itsRefFreqX.resize(other.itsRefFreqX.nelements()); itsRefFreqX = other.itsRefFreqX; // itsFreqX.resize(other.itsFreqX.nelements()); itsFreqX = other.itsFreqX; // itsDiffTol = other.itsDiffTol; } template SpectralCoordinate FrequencyAligner::alignedSpectralCoordinate (Bool doLinear) const { const uInt n = itsRefFreqX.nelements(); AlwaysAssert(n>0,AipsError); // Get SpectralCoordinate const Vector& units = itsSpecCoord.worldAxisUnits(); Unit unit(units(0)); Quantum restFreq(itsSpecCoord.restFrequency(), unit); // Create SC. Units will be Hz SpectralCoordinate sC; if (doLinear) { Double crpix = 0.0; Quantum crval(itsRefFreqX[0], unit); Quantum cdelt((itsRefFreqX[n-1]-itsRefFreqX[0])/Double(n-1), unit); // sC = SpectralCoordinate(itsFreqSystem, crval, cdelt, crpix, restFreq); } else { Quantum > freqs(itsRefFreqX, unit); sC = SpectralCoordinate(itsFreqSystem, freqs, restFreq); } // Set back to original unit sC.setWorldAxisUnits(units); // Set rest freq state sC.setRestFrequencies(itsSpecCoord.restFrequencies(), False); sC.selectRestFrequency(restFreq.getValue()); // We don't want to set the frame conversion state (although possibly // the user might like to be able to access the reference pos/dir/epoch ?) // We can set the velocity state though MDoppler::Types doppler = itsSpecCoord.velocityDoppler(); String velUnit = itsSpecCoord.velocityUnit(); sC.setVelocity (velUnit, doppler); // Axis names sC.setWorldAxisNames(itsSpecCoord.worldAxisNames()); // return sC; } } //# End namespace casacore #endif casacore-3.7.1/coordinates/Coordinates/GaussianConvert.cc000066400000000000000000000307731476623553700235440ustar00rootroot00000000000000//# GaussianConvert.cc: Class to convert between pixel and world coordinates for Gaussians //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN GaussianConvert::GaussianConvert() : itsValid(False) { } GaussianConvert::GaussianConvert(const CoordinateSystem& cSys, const Vector& worldAxes) : itsCSys(cSys), itsWorldAxes(worldAxes.copy()), itsErrorMessage(""), itsValid(True) { checkWorldAxes(); checkCoordinateSystem(); } GaussianConvert::~GaussianConvert() { } GaussianConvert::GaussianConvert(const GaussianConvert& other) : itsCSys(other.itsCSys), itsWorldAxes(other.itsWorldAxes.copy()), itsErrorMessage(other.itsErrorMessage), itsValid(other.itsValid) { } GaussianConvert& GaussianConvert::operator=(const GaussianConvert& other) { if (this != &other) { itsCSys = other.itsCSys; itsWorldAxes.resize(0); itsWorldAxes = other.itsWorldAxes; itsErrorMessage = other.itsErrorMessage; itsValid = other.itsValid; } return *this; } void GaussianConvert::setCoordinateSystem (const CoordinateSystem& cSys) { itsCSys = cSys; checkCoordinateSystem(); // if (itsWorldAxes.nelements()==2) itsValid = True; } void GaussianConvert::setWorldAxes (const Vector& worldAxes) { itsWorldAxes.resize(0); itsWorldAxes = worldAxes; checkWorldAxes(); // if (itsCSys.nCoordinates()!=0) itsValid = True; } Bool GaussianConvert::toWorld(Quantum& majorAxisOut, Quantum& minorAxisOut, Quantum& positionAngleOut, Double majorAxisIn, Double minorAxisIn, const Quantum& positionAngleIn) { if (!itsValid) { itsErrorMessage = String("the converter state is invalid; ") + String("use setCoordinateSystem and/or setWorldAxes"); return False; } // Check output quantum units. If none, use that of CoordinateSystem String unitMajor = majorAxisOut.getUnit(); String unitMinor = minorAxisOut.getUnit(); // String unitAxes; if (unitMajor.length()==0 && unitMinor.length()==0) { unitAxes = itsCSys.worldAxisUnits()(itsWorldAxes(0)); } else { if (unitMajor != unitMinor) { itsErrorMessage = "major and minor axes units differ"; return False; } unitAxes = unitMajor; } // Set the units of the CoordinateSystem to be the same for the two // axes. We checked in the constructor that they were dimensionally equivalent, // so this is OK Vector units(itsCSys.worldAxisUnits().copy()); units(itsWorldAxes(0)) = unitAxes; units(itsWorldAxes(1)) = unitAxes; if (!itsCSys.setWorldAxisUnits(units)) { itsErrorMessage = "failed to set axis units because" + itsCSys.errorMessage(); return False; } // Convert Double minOut, majOut; convertAxes (minOut, majOut, positionAngleOut, minorAxisIn, majorAxisIn, positionAngleIn, itsCSys, String("toWorld")); // minorAxisOut.setValue(minOut); minorAxisOut.setUnit(Unit(unitAxes)); majorAxisOut.setValue(majOut); majorAxisOut.setUnit(Unit(unitAxes)); // return True; } Bool GaussianConvert::toPixel(Double& majorAxisOut, Double& minorAxisOut, Quantum& positionAngleOut, const Quantum& majorAxisIn, const Quantum& minorAxisIn, const Quantum& positionAngleIn) { if (!itsValid) { itsErrorMessage = "the converter state is invalid; use setCoordinateSystem and/or setWorldAxes"; return False; } // Convert axes to same unit Quantum majIn(majorAxisIn); Quantum minIn(minorAxisIn); majIn.convert(Unit(minIn.getUnit())); String unitAxes = majIn.getUnit(); // // Set the units of the CoordinateSystem to be the same for the two // axes. We checked in the constructor that they were dimensionally equivalent, // so this is OK Vector units(itsCSys.worldAxisUnits().copy()); units(itsWorldAxes(0)) = unitAxes; units(itsWorldAxes(1)) = unitAxes; if (!itsCSys.setWorldAxisUnits(units)) { itsErrorMessage = "failed to set axis units because" + itsCSys.errorMessage(); return False; } // Convert convertAxes (minorAxisOut, majorAxisOut, positionAngleOut, minIn.getValue(), majIn.getValue(), positionAngleIn, itsCSys, String("toPixel")); // return True; } Bool GaussianConvert::toWorld(Vector >& world, const Vector& pixel) { if (!itsValid) { itsErrorMessage = "the converter state is invalid; use setCoordinateSystem and/or setWorldAxes"; return False; } // if (pixel.nelements() != 2) { itsErrorMessage = "the pixel vector must have 2 elements"; return False; } // Vector pixel2(itsCSys.referencePixel().copy()); Int pixelAxis0 = itsCSys.worldAxisToPixelAxis(itsWorldAxes(0)); if (pixelAxis0==-1) { itsErrorMessage = "the first world axis has no corresponding pixel axis"; return False; } Int pixelAxis1 = itsCSys.worldAxisToPixelAxis(itsWorldAxes(1)); if (pixelAxis1==-1) { itsErrorMessage = "the second world axis has no corresponding pixel axis"; return False; } pixel2(pixelAxis0) = pixel(0); pixel2(pixelAxis1) = pixel(1); // Vector world2; if (!itsCSys.toWorld(world2, pixel2)) { itsErrorMessage = "failed to convert to world because" + itsCSys.errorMessage(); return False; } // // Assign // Vector > world3(2); { Quantum tmp(world2(itsWorldAxes(0)), itsCSys.worldAxisUnits()(itsWorldAxes(0))); String unit; if (world.nelements() >= 1) unit = world(0).getUnit(); if (!unit.empty()) tmp.convert(Unit(unit)); world3(0) = tmp; } { Quantum tmp(world2(itsWorldAxes(1)), itsCSys.worldAxisUnits()(itsWorldAxes(1))); String unit; if (world.nelements() >= 2) unit = world(1).getUnit(); if (!unit.empty()) tmp.convert(Unit(unit)); world3(1) = tmp; } world.resize(2); world(0) = world3(0); world(1) = world3(1); // return True; } Bool GaussianConvert::toPixel(Vector& pixel, const Vector >& world) { if (!itsValid) { itsErrorMessage = "the converter state is invalid; use setCoordinateSystem and/or setWorldAxes"; return False; } // if (world.nelements() != 2) { itsErrorMessage = "the world vector must have 2 elements"; return False; } // Vector world2(itsCSys.referenceValue().copy()); Vector units(itsCSys.worldAxisUnits()); // { Quantum tmp = world(0); tmp.convert(Unit(units(itsWorldAxes(0)))); world2(itsWorldAxes(0)) = tmp.getValue(); } { Quantum tmp = world(1); tmp.convert(Unit(units(itsWorldAxes(1)))); world2(itsWorldAxes(1)) = tmp.getValue(); } // if (!itsCSys.toPixel(pixel, world2)) { itsErrorMessage = "failed to convert to pixel because" + itsCSys.errorMessage(); return False; } // return True; } // Private functions void GaussianConvert::convertAxes (Double& minorAxisOut, Double& majorAxisOut, Quantum& positionAngleOut, Double minorAxisIn, Double majorAxisIn, const Quantum& positionAngleIn, const CoordinateSystem& cSys, String dir) { // // The defined convention for the Gaussian2D functional, with which I should probably // be consistent, is positive from +y to -x (ccw). The normal astronomical convention // for DirectionCoordinates positive +y to +x (N through E). This means // I must flip the sign for DirectionCoordinates on the x axis. // // // World axes already checked to exist in CS // Int coordinate0, coordinate1, axisInCoordinate0, axisInCoordinate1; cSys.findWorldAxis(coordinate0, axisInCoordinate0, itsWorldAxes(0)); cSys.findWorldAxis(coordinate1, axisInCoordinate1, itsWorldAxes(1)); Bool flipX = False; Bool flipY = False; if (coordinate0==coordinate1 && cSys.type(coordinate0)==Coordinate::DIRECTION) { if (axisInCoordinate0==0) flipX = True; // Long is worldAxes(0) if (axisInCoordinate1==0) flipY = True; // Long is worldAxes(1) } // Double dx = cSys.increment()(itsWorldAxes(0)); if (flipX) dx *= -1; Double dy = cSys.increment()(itsWorldAxes(1)); if (flipY) dy *= -1; // Double sinpa = sin(positionAngleIn.getValue("rad")); Double cospa = cos(positionAngleIn.getValue("rad")); // Double alpha = square(cospa/minorAxisIn) + square(sinpa/majorAxisIn); Double beta = square(sinpa/minorAxisIn) + square(cospa/majorAxisIn); Double gamma = (2/square(minorAxisIn) - 2/square(majorAxisIn))*cospa*sinpa; // if (dir=="toWorld") { alpha /= dx*dx; beta /= dy*dy; gamma /= dx*dy; } else { alpha *= dx*dx; beta *= dy*dy; gamma *= dx*dy; } // Double s = alpha + beta; Double t = sqrt(square(alpha-beta)+square(gamma)); // minorAxisOut = sqrt(2.0/(s+t)); majorAxisOut = sqrt(2.0/(s-t)); // String unitPA = positionAngleOut.getUnit(); if (unitPA.length()==0) unitPA = positionAngleIn.getUnit(); // // Put position angle into the range 0 -> pi (same as that // returned by Gaussian2D functional) // Double pa2; if (abs(gamma)+abs(alpha-beta)==0.0) { pa2 = 0; } else { // // -pi -> pi // pa2 = 0.5*atan2(gamma,alpha-beta); } Double pa3 = positionAngleRange(pa2); // positionAngleOut.setValue(pa3); positionAngleOut.setUnit(Unit("rad")); positionAngleOut.convert(Unit(unitPA)); } Double GaussianConvert::positionAngleRange(Double pa) // // Put in the range 0->pi // { Double pa2 = fmod(pa, M_PI); if (pa2 < 0.0) pa2 += M_PI; return pa2; } void GaussianConvert::checkWorldAxes() { if (itsWorldAxes.nelements() != 2) { throw(AipsError("GaussianConvert - worldAxes must be of length 2")); } if (itsWorldAxes(0) >= itsCSys.nWorldAxes()) { throw(AipsError("worldAxes(0) is invalid")); } if (itsWorldAxes(1) >= itsCSys.nWorldAxes()) { throw(AipsError("worldAxes(1) is invalid")); } if (itsWorldAxes(0) == itsWorldAxes(1)) { throw(AipsError("worldAxes must be different")); } // Unit u0(itsCSys.worldAxisUnits()(itsWorldAxes(0))); Unit u1(itsCSys.worldAxisUnits()(itsWorldAxes(1))); if (u0 != u1) { String msg("GaussianConvert::checkWorldAxes - units of specified axes must be dimensionally consistent"); throw(AipsError(msg)); } } void GaussianConvert::checkCoordinateSystem() { if (itsCSys.nWorldAxes() < 2) { String msg("GaussianConvert::checkCoordinateSystem - the coordinate system must have at least 2 world axes"); throw(AipsError(msg)); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/coordinates/Coordinates/GaussianConvert.h000066400000000000000000000122421476623553700233750ustar00rootroot00000000000000//# GaussianConvert.h: Class to convert units of Gaussians from pixel to world //# Copyright (C) 1997,1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef COORDINATES_GAUSSIANCONVERT_H #define COORDINATES_GAUSSIANCONVERT_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template class Quantum; // // Converts Gaussian parameters between pixel and world // // // // // //
      • CoordinateSystem // // // Converts Gaussian parameters between world and pixel. // In the pixel coordinate system ([0,0] in center of image) // the position angle is positive +y to -x. This is consistent // with Gaussian2D. In the world coordinate system the pa // is positive N through E // // // // // // //
      • Position angle signs require more thinking in Casacore // class GaussianConvert { public: // Default constructor GaussianConvert (); // Constructor. You specify which world axes (must be length 2) // of the coordinate system are the relevant ones for // your gaussian (x then y) GaussianConvert (const CoordinateSystem& cSys, const Vector& worldAxes); // Destructor ~GaussianConvert (); // Copy constructor. Uses copy semantics. GaussianConvert(const GaussianConvert& other); // Assignment operator. Uses copy semantics. GaussianConvert& operator=(const GaussianConvert& other); // (Re)set the coordinate system void setCoordinateSystem (const CoordinateSystem& cSys); // Re(set) the world axes void setWorldAxes (const Vector& worldAxes); // Convert Gaussian parameters from pixels to world. Returns // False if it fails with an error message recoverable with // function errorMessage. If you set the units of the output // axis quanta they will be honoured, otherwise they will come out // in the axis units of the coordinate system. For the output position angle, // if the output units are not set, the units of the input position angle // will be used. Bool toWorld(Quantum& majorAxisOut, Quantum& minorAxisOut, Quantum& positionAngleOut, Double majorAxisIn, Double minorAxisIn, const Quantum& positionAngleIn); // Convert Gaussian parameters from world to pixel. Returns // False if it fails with an error message recoverable with // function errorMessage. For the output position angle, // if the output units are not set, the units of the input position angle // will be used. Bool toPixel(Double& majorAxisOut, Double& minorAxisOut, Quantum& positionAngleOut, const Quantum& majorAxisIn, const Quantum& minorAxisIn, const Quantum& positionAngleIn); // Convert location // Bool toPixel(Vector& pixel, const Vector >& world); Bool toWorld(Vector >& world, const Vector& pixel); // // Recover error messages from the conversion functions String errorMessage() const {return itsErrorMessage;} private: CoordinateSystem itsCSys; Vector itsWorldAxes; String itsErrorMessage; Bool itsValid; void convertAxes (Double& minorAxisOut, Double& majorAxisOut, Quantum& positionAngleOut, Double minorAxisIn, Double majorAxisIn, const Quantum& positionAngleIn, const CoordinateSystem& cSys, String dir); void checkCoordinateSystem(); void checkWorldAxes(); Double positionAngleRange(Double pa); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/coordinates/Coordinates/LinearCoordinate.cc000066400000000000000000000515131476623553700236460ustar00rootroot00000000000000//# LinearCoordinate.cc: this defines LinearCoordinate //# Copyright (C) 1997,1998,1999,2000,2001,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LinearCoordinate::LinearCoordinate(uInt naxis) : Coordinate() { Vector refVal(naxis), refPix(naxis), incr(naxis); Matrix pc(naxis,naxis); Vector names(naxis), units(naxis); // pc = 0.0; for (uInt i=0; i& names, const Vector& units, const Vector& refVal, const Vector& inc, const Matrix& pc, const Vector& refPix) : Coordinate() { uInt naxis = names.nelements(); makeWCS (wcs_p, naxis, refPix, refVal, inc, pc, units, names); // setDefaultWorldMixRanges(); } LinearCoordinate::LinearCoordinate(const Vector& names, const Vector >& refVal, const Vector >& inc, const Matrix& pc, const Vector& refPix) : Coordinate() { // Check dimensions const uInt n = names.nelements(); AlwaysAssert(refVal.nelements() == n && inc.nelements() == n && pc.nrow() == n && pc.ncolumn() == n && refPix.nelements() == n, AipsError); // Vector cdelt(n), crval(n); Vector units(n); for (uInt i=0; i &world, const Vector &pixel, Bool) const { return toWorldWCS (world, pixel, wcs_p); } Bool LinearCoordinate::toPixel(Vector &pixel, const Vector &world) const { return toPixelWCS (pixel, world, wcs_p); } Vector LinearCoordinate::worldAxisNames() const { const uInt n = nPixelAxes(); Vector tmp(n); for (uInt i=0; i LinearCoordinate::worldAxisUnits() const { const uInt n = nWorldAxes(); Vector tmp(n); for (uInt i=0; i LinearCoordinate::referenceValue() const { const uInt n = nWorldAxes(); Vector tmp(n); for (uInt i=0; i LinearCoordinate::increment() const { const uInt n = nWorldAxes(); Vector tmp(n); for (uInt i=0; i LinearCoordinate::linearTransform() const { Matrix tmp; pcToXform (tmp, wcs_p); return tmp; } Vector LinearCoordinate::referencePixel() const { const uInt n = nPixelAxes(); Vector tmp(n); for (uInt i=0; i &names) { Bool ok = (names.nelements() == nWorldAxes()); if (!ok) { set_error("names vector has the wrong size"); } else { for (uInt i=0; i &units) { Vector d1 = increment(); Bool ok = Coordinate::setWorldAxisUnits(units); if (ok) { for (uInt i=0; i &units) { Bool ok = units.nelements() == nWorldAxes(); if (ok) { for (uInt i=0; i &refPix) { Bool ok = (refPix.nelements() == nWorldAxes()); if (! ok) { set_error("reference pixel vector has the wrong size"); } else { for (uInt i=0; i &pc) { Bool ok = (pc.nrow() == nWorldAxes() && pc.ncolumn() == nWorldAxes() ); if (!ok) { set_error("Transform matrix has the wrong size"); } else { xFormToPC(wcs_p, pc); set_wcs(wcs_p); } // return ok; } Bool LinearCoordinate::setIncrement(const Vector &inc) { Bool ok = (inc.nelements() == nWorldAxes()); if (! ok) { set_error("increment vector has the wrong size"); } else { for (uInt i=0; i &refval) { Bool ok = (refval.nelements() == nWorldAxes()); if (! ok) { set_error("reference value vector has the wrong size"); } else { for (uInt i=0; i excludeAxes; return near(other, excludeAxes, tol); } Bool LinearCoordinate::near(const Coordinate& other, const Vector& excludeAxes, Double tol) const { if (other.type() != this->type()) { set_error("Comparison is not with another LinearCoordinate"); return False; } const LinearCoordinate& lCoord = dynamic_cast(other); // Check descriptor vector lengths Vector names1(worldAxisNames()); Vector names2(lCoord.worldAxisNames()); Vector units1(worldAxisUnits()); Vector units2(lCoord.worldAxisUnits()); Vector crval1(referenceValue()); Vector crval2(lCoord.referenceValue()); Vector cdelt1(increment()); Vector cdelt2(lCoord.increment()); Vector crpix1(referencePixel()); Vector crpix2(lCoord.referencePixel()); // if (names1.nelements() != names2.nelements()) { set_error("The LinearCoordinates have differing numbers of world axis names"); return False; } if (units1.nelements() != units2.nelements()) { set_error("The LinearCoordinates have differing numbers of axis units"); return False; } if (crval1.nelements() != crval2.nelements()) { set_error("The LinearCoordinates have differing numbers of reference values"); return False; } if (cdelt1.nelements() != cdelt2.nelements()) { set_error("The LinearCoordinates have differing numbers of increments"); return False; } if (crpix1.nelements() != crpix2.nelements()) { set_error("The LinearCoordinates have differing numbers of reference pixels"); return False; } // Number of pixel and world axes is the same for a LinearCoordinate // Add an assertion check should this change. Other code in LC has // checked that all the vectors we are comparing have the same // length as nPixelAxes() AlwaysAssert(nPixelAxes()==nWorldAxes(), AipsError); Vector exclude(nPixelAxes()); exclude = False; Bool found; uInt j = 0; uInt i; for (i=0; i= 0) exclude(j++) = True; } // Check the descriptors ostringstream oss; for (i=0; i pc1 = linearTransform(); Matrix pc2 = lCoord.linearTransform(); if (pc1.nrow() != pc2.nrow()) { set_error(String("The LinearCoordinates have different PC matrix shapes")); return False; } if (pc1.ncolumn() != pc2.ncolumn()) { set_error(String("The LinearCoordinates have different PC matrix shapes")); return False; } // Compare row by row. An axis will turn up in the PC matrix in any row // or column with that number. E.g., values pertaining to axis "i" will // be found in all entries of row "i" and all entries of column "i". for (uInt j=0; j row1 = pc1.row(j); Vector row2 = pc2.row(j); if (!exclude(j)) { for (uInt i=0; i crval(subrec.toArrayDouble("crval")); // if (!subrec.isDefined("crpix")) { return 0; } Vector crpix(subrec.toArrayDouble("crpix")); // if (!subrec.isDefined("cdelt")) { return 0; } Vector cdelt(subrec.toArrayDouble("cdelt")); // if (!subrec.isDefined("pc")) { return 0; } Matrix pc(subrec.toArrayDouble("pc")); // if (!subrec.isDefined("axes")) { return 0; } Vector axes; subrec.get("axes", axes); // if (!subrec.isDefined("units")) { return 0; } Vector units; subrec.get("units", units); // LinearCoordinate* pLinear = new LinearCoordinate(axes, units, crval, cdelt, pc, crpix); AlwaysAssert(pLinear, AipsError); return pLinear; } Coordinate *LinearCoordinate::clone() const { return new LinearCoordinate(*this); } Coordinate* LinearCoordinate::makeFourierCoordinate (const Vector& axes, const Vector& shape) const // // axes says which axes in the coordinate are to be transformed // shape is the shape of the image for all axes in this coordinate // { if (axes.nelements() != nPixelAxes()) { set_error ("Invalid number of specified axes"); return 0; } uInt nT = 0; for (uInt i=0; i& units(worldAxisUnits()); const Vector& names(worldAxisNames()); // Vector unitsCanon(worldAxisUnits().copy()); Vector unitsOut = worldAxisUnits().copy(); Vector namesOut(worldAxisNames().copy()); // Vector crval2(referenceValue().copy()); Vector crpix(referencePixel().copy()); Vector scale(nPixelAxes(), 1.0); // for (uInt i=0; icdelt(), pLinearF->pc(), pLinearF->crpix()); delete pLinearF; return pLinear; } void LinearCoordinate::makeWCS (::wcsprm& wcs, uInt naxis, const Vector& refPix, const Vector& refVal, const Vector& incr, const Matrix& pc, const Vector& units, const Vector& names) { AlwaysAssert(refPix.nelements() == naxis && refVal.nelements() == naxis && incr.nelements() == naxis && pc.nrow() == naxis && pc.ncolumn() == naxis && units.nelements()==naxis && names.nelements()==naxis, AipsError); // Set up wcs structure internals wcs.flag = -1; init_wcs(wcs, naxis); // Assign values for (uInt i=0; i #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template class Quantum; // // Interconvert between pixel and a linear world coordinate. // // // // // //
      • Coordinate defines the fundamental // interface to coordinate conversions. // // // // The LinearCoordinate class ties pixel and world axes together through // a general linear transformation. // // // world = (cdelt * PC * (pixel - crpix)) + crval // // Where PC is an NxN matrix; pixel, crval, crpix and world are length N // vectors, and cdelt is an NxN diagonal matrix, represented as a length // N vector. // // The LinearCoordinate can contain several uncoupled axes (similar to the way // in which the DirectionCoordinate contains two axes). // // // // All pixels coordinates are zero relative. // // // // Let's make a LinearCoordinate with just one axis containing // a coordinate describing length. // // Vector crpix(1); crpix = 0.0; // Vector crval(1); crval = 100.0; // Vector cdelt(1); cdelt = -10.0; // Matrix pc(1,1); pc= 0; pc.diagonal() = 1.0; // Vector name(1); name = "length"; // Vector units(1); units = "km"; // // LinearCoordinate lin(names, units, crval, cdelt, pc, crpix); // // // Now do a coordinate conversion // // // Vector world, pixel(1); // pixel = 2.0; // if (!lin.toWorld(world, pixel)) { // cerr << "Error : " << lin.errorMessage() << endl; // } else { // cerr << "pixel, world = " << pixel << world << endl; // } // // The answer should of course be -20km. // // // // This class is intended for use with axes which do not have specific coordinate // types. A "time" axis would be a good example. // // // //
      • AipsError // // // //
      • Allow differing numbers of world and pixel axes. Requires a change in // WCS or use of a different library. // // class LinearCoordinate : public Coordinate { public: // The default constructor makes a LinearCoordinate for which pixel // and world coordinates are equal. naxes gives the number // of axes in the Coordinate. LinearCoordinate(uInt naxes = 1); // Construct the LinearCoordinate LinearCoordinate(const Vector &names, const Vector &units, const Vector &refVal, const Vector &inc, const Matrix &pc, const Vector &refPix); // Construct LinearCoordinate with Quantum-based interface. // The units of the increment (inc) will be converted to // those of the reference value (refVal) which will // then serve as the units of the Coordinate. LinearCoordinate(const Vector &names, const Vector >& refVal, const Vector >& inc, const Matrix &pc, const Vector &refPix); // Constructor from WCS structure; must hold ONLY a linear wcs structure // Specify whether the absolute pixel coordinates in the wcs structure // are 0- or 1-relative. The coordinate is always constructed with 0-relative // pixel coordinates LinearCoordinate(const wcsprm& wcs, Bool oneRel=True); // Copy constructor (copy semantics). LinearCoordinate(const LinearCoordinate &other); // Assignment (copy semantics). LinearCoordinate &operator=(const LinearCoordinate &other); // Destructor. virtual ~LinearCoordinate(); // Returns Coordinate::LINEAR. virtual Coordinate::Type type() const; // Returns the String "Linear". virtual String showType() const; // Returns the number of pixel/world axes. The number of axes is arbitrary, // however the number of world and pixel axes must at present be the same. // virtual uInt nPixelAxes() const; virtual uInt nWorldAxes() const; // // Convert a pixel position to a worl position or vice versa. Returns True // if the conversion succeeds, otherwise it returns False and method // errorMessage returns an error message. The output // vectors are appropriately resized. The value of the Bool parameter passed // to toWorld() has no effect as this type of coordinate does not support a // conversion layer frame. // virtual Bool toWorld(Vector &world, const Vector &pixel, Bool=True) const; virtual Bool toPixel(Vector &pixel, const Vector &world) const; // // Return the requested attribute // virtual Vector worldAxisNames() const; virtual Vector referenceValue() const; virtual Vector increment() const; virtual Matrix linearTransform() const; virtual Vector referencePixel() const; virtual Vector worldAxisUnits() const; // // Set the value of the requested attributed. Note that these just // change the internal values, they do not cause any recomputation. // virtual Bool setWorldAxisNames(const Vector &names); virtual Bool setReferencePixel(const Vector &refPix); virtual Bool setLinearTransform(const Matrix &pc); virtual Bool setIncrement(const Vector &inc); virtual Bool setReferenceValue(const Vector &refval); // // Set the world axis units. Adjust the increment and // reference value by the ratio of the old and new units. // The units must be compatible with the current units. virtual Bool setWorldAxisUnits(const Vector &units); // Overwrite the world axis units with no compatibility // checks or adjustment. Bool overwriteWorldAxisUnits(const Vector &units); // Comparison function. Any private Double data members are compared // with the specified fractional tolerance. Don't // compare on the specified // axes in the Coordinate. If the comparison returns False, method // errorMessage contains a message about why. // virtual Bool near(const Coordinate& other, Double tol=1e-6) const; virtual Bool near(const Coordinate& other, const Vector& excludeAxes, Double tol=1e-6) const; // // Find the Coordinate for when we Fourier Transform ourselves. This pointer // must be deleted by the caller. Axes specifies which axes of the Coordinate // you wish to transform. Shape specifies the shape of the image // associated with all the axes of the Coordinate. Currently the // output reference pixel is always shape/2. If the pointer returned is 0, // it failed with a message in errorMessage virtual Coordinate* makeFourierCoordinate (const Vector& axes, const Vector& shape) const; // Save the LinearCoordinate into the supplied record using the supplied field name. // The field must not already exist, otherwise False is returned. virtual Bool save(RecordInterface &container, const String &fieldName) const; // Restore the LinearCoordinate from a record. // A null pointer means that the restoration did not succeed - probably // because fieldName doesn't exist or doesn't contain a CoordinateSystem. static LinearCoordinate *restore(const RecordInterface &container, const String &fieldName); // Make a copy of the LinearCoordinate using new. The caller is responsible for calling // delete. virtual Coordinate *clone() const; private: // An interface to the WCSLIB linear transformation routines. mutable ::wcsprm wcs_p; // Copy private data void copy (const LinearCoordinate& other); // Make wcs structure void makeWCS (wcsprm& wcs, uInt naxis, const Vector& refPix, const Vector& refVal, const Vector& incr, const Matrix& pc, const Vector& units, const Vector& names); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/coordinates/Coordinates/LinearXform.cc000066400000000000000000000222001476623553700226410ustar00rootroot00000000000000//# LinearXForm.cc: this defines LinearXForm //# Copyright (C) 1997-2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //#--------------------------------------------------------------------------- #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LinearXform::LinearXform(uInt naxis) : isPCDiagonal_p(True) { linprm_p.flag = -1; linini(1, naxis, &linprm_p); set_linprm(); } LinearXform::LinearXform(const Vector &crpixIn, const Vector &cdeltIn) : isPCDiagonal_p(True) { const uInt naxis = crpixIn.nelements(); AlwaysAssert(cdeltIn.nelements() == naxis, AipsError); // int n = naxis; linprm_p.flag = -1; linini(1, n, &linprm_p); // for (uInt i = 0; i < naxis; i++) { linprm_p.crpix[i] = crpixIn[i]; linprm_p.cdelt[i] = cdeltIn[i]; } set_linprm(); } LinearXform::LinearXform(const Vector& crpixIn, const Vector& cdeltIn, const Matrix& pcIn) { const uInt naxis = crpixIn.nelements(); AlwaysAssert(cdeltIn.nelements() == naxis && pcIn.nrow() == naxis && pcIn.ncolumn() == naxis, AipsError); // int n = naxis; linprm_p.flag = -1; linini(1, n, &linprm_p); // Double zero = 0.0; Double tol = 1e-12; isPCDiagonal_p = True; // uInt ij = 0; for (uInt i = 0; i < naxis; i++) { linprm_p.crpix[i] = crpixIn[i]; linprm_p.cdelt[i] = cdeltIn[i]; // for (uInt j = 0; j < naxis; j++) { // Is pc is diagonal? Done purely for use in the Fourier // inversion stuff. Urk. if (i != j && !casacore::near(pcIn(j,i),zero,tol)) { isPCDiagonal_p = False; } linprm_p.pc[ij++] = pcIn(j,i); } } // set_linprm(); } LinearXform::LinearXform(const LinearXform &other) : isPCDiagonal_p(other.isPCDiagonal_p) { linprm_p.flag = -1; lincpy(1, &(other.linprm_p), &linprm_p); set_linprm(); } LinearXform &LinearXform::operator=(const LinearXform &other) { if (this != &other) { lincpy(1, &(other.linprm_p), &linprm_p); isPCDiagonal_p = other.isPCDiagonal_p; set_linprm(); } return *this; } LinearXform::~LinearXform() { linfree(&linprm_p); } uInt LinearXform::nWorldAxes() const { return linprm_p.naxis; } Bool LinearXform::forward(Vector& pixel, const Vector& world, String& errorMsg) const { uInt naxis = world.nelements(); pixel.resize(naxis); // Bool delPixel, delWorld; double* pixelStor = pixel.getStorage(delPixel); const double* worldStor = world.getStorage(delWorld); // int n = naxis; if (int err = linx2p(&linprm_p, 1, n, worldStor, pixelStor)) { errorMsg = "wcs linx2p error: "; errorMsg += linx2p_errmsg[err]; return False; } // pixel.putStorage(pixelStor, delPixel); world.freeStorage(worldStor, delWorld); // return True; } Bool LinearXform::reverse(Vector &world, const Vector &pixel, String &errorMsg) const { uInt naxis = pixel.nelements(); world.resize(naxis); // Bool delPixel, delWorld; const double* pixelStor = pixel.getStorage(delPixel); double* worldStor = world.getStorage(delWorld); // int n = naxis; if (int err = linp2x(&linprm_p, 1, n, pixelStor, worldStor)) { errorMsg = "wcs linp2x error: "; errorMsg += linp2x_errmsg[err]; return False; } // pixel.freeStorage(pixelStor, delPixel); world.putStorage(worldStor, delWorld); // return True; } Vector LinearXform::crpix() const { uInt naxis = linprm_p.naxis; Vector tmp(naxis); // const double* dp = linprm_p.crpix; for (uInt i = 0; i < naxis; i++) { tmp[i] = *(dp++); } return tmp; } Vector LinearXform::cdelt() const { uInt naxis = linprm_p.naxis; Vector tmp(naxis); // const double* dp = linprm_p.cdelt; for (uInt i = 0; i < naxis; i++) { tmp[i] = *(dp++); } return tmp; } Matrix LinearXform::pc() const { uInt naxis = linprm_p.naxis; Matrix tmp(naxis,naxis); // const double* dp = linprm_p.pc; for (uInt i = 0; i < naxis; i++) { for (uInt j = 0; j < naxis; j++) { tmp(j,i) = *(dp++); } } return tmp; } void LinearXform::crpix(const Vector &newvals) { AlwaysAssert(newvals.nelements() == nWorldAxes(), AipsError); // const Vector& cdlt = this->cdelt(); const Matrix& pcm = this->pc(); // *this = LinearXform(newvals, cdlt, pcm); } void LinearXform::cdelt(const Vector &newvals) { AlwaysAssert(newvals.nelements() == nWorldAxes(), AipsError); // const Vector& crp = this->crpix(); const Matrix& pcm = this->pc(); // *this = LinearXform(crp, newvals, pcm); } void LinearXform::pc(const Matrix& newvals) { AlwaysAssert(newvals.nrow() == nWorldAxes() && newvals.ncolumn() == nWorldAxes(), AipsError); // const Vector& crp = this->crpix(); const Vector& cdlt = this->cdelt(); *this = LinearXform(crp, cdlt, newvals); } Bool LinearXform::near(const LinearXform& other, Double tol) const { Vector excludeAxes; return near(other, excludeAxes, tol); } Bool LinearXform::near(const LinearXform& other, const Vector& excludeAxes, Double tol) const { // Number of pixel and world axes is the same for a LinearXform. uInt naxes = excludeAxes.nelements(); Vector exclude(linprm_p.naxis); Bool found; for (uInt i = 0; i < nWorldAxes(); i++) { exclude[i] = (linearSearch(found, excludeAxes, Int(i), naxes) >= 0); } // Compare reference pixels and increments. { const Vector& d1 = this->crpix(); const Vector& d2 = other.crpix(); if (d1.nelements() != d2.nelements()) return False; for (uInt i = 0; i < d1.nelements(); i++) { if (!exclude[i]) { if (!casacore::near(d1(i),d2(i),tol)) return False; } } } { const Vector& d1 = this->cdelt(); const Vector& d2 = other.cdelt(); if (d1.nelements() != d2.nelements()) return False; for (uInt i = 0; i < d1.nelements(); i++) { if (!exclude(i)) { if (!casacore::near(d1[i],d2[i],tol)) return False; } } } // Check the matrix. Matrix pc1 = this->pc(); Matrix pc2 = other.pc(); if (pc1.nrow() != pc2.nrow()) return False; if (pc1.ncolumn() != pc2.ncolumn()) return False; // Compare row by row. An axis will turn up in the PC matrix in any row // or column with that number. E.g., values pertaining to axis "i" will // be found in all entries of row "i" and all entries of column "i". for (uInt j = 0; j < pc1.nrow(); j++) { Vector row1 = pc1.row(j); Vector row2 = pc2.row(j); if (!exclude(j)) { for (uInt i = 0; i < row1.nelements(); i++) { if (!exclude(i)) { if (!casacore::near(row1(i),row2(i),tol)) return False; } } } } return True; } void LinearXform::set_linprm(void) { if (int err = linset(&linprm_p)) { String errmsg = "wcs linset error: "; errmsg += linset_errmsg[err]; throw(AipsError(errmsg)); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/coordinates/Coordinates/LinearXform.h000066400000000000000000000153411476623553700225130ustar00rootroot00000000000000//# LinearXform.h: Perform a linear transform between input and output vectors //# Copyright (C) 1997-2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef COORDINATES_LINEARXFORM_H #define COORDINATES_LINEARXFORM_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class String; // // Perform a linear transform between input and output vectors // // // // // //
      • General knowledge of Casacore Arrays //
      • Knowledge of FITS terminology in coordinate transformations // // // // This class represents the common linear part of a FITS coordinate // transformation. In particular it does the following: // // world = cdelt * PC * (pixel - crpix) // // Where PC is an NxN matrix; pixel, crpix (reference pixel) and world are // length N vectors; and cdelt (increment) is an NxN diagonal matrix, // represented as a length N vector. // // Normally this class isn't used directly, rather it is used indirectly through // a class like LinearCoordinate. // // The actual computations are performed by WCSLIB, written by Mark Calabretta // of the ATNF. // // // // Let's make a LinearXform housing two axes with a unit // diagonal PC matrix and convert from pixel to world // // // Vector crpix(2), cdelt(2); // crpix(0) = 10.0; crpix(1) = 20.0; // cdelt(0) = 1.0; cdelt(1) = -1.0; // LinearXform lxf(crpix, cdelt); // // String errMsg; // Vector world, pixel(2); // pixel = 10.0; // Bool ok = lxf.reverse(world, pixel, errMsg); // if (ok) { // cerr << "pixel, world = " << pixel << world << endl; // } else { // cerr << "Error : " << errMsg << endl; // } // // The answer should be a world vector with values 0 and -10. // // // // Factor out the common linear part of coordinate transformations. // // // //
      • AipsError // // // //
      • Allow different numbers of pixel and world axes. // // class LinearXform { public: // Construct with specified number of axes. The reference pixel is // assumed to be 0, and the increment is assumed to be unity, and the // PC matrix is assumed to be diagonal. LinearXform(uInt naxis=1); // Construct the linear transformation from the supplied reference pixel // and increment. The PC matrix is the unit matrix. // crpix and cdelt must have the same number // of elements. LinearXform(const Vector &crpix, const Vector &cdelt); // Construct a linear transformation, supplying all of the reference pixel, // increment and PC matrix. // The vectors must be of the same length ("n") and the number of rows and // columns in the matrix must also be n. LinearXform(const Vector &crpix, const Vector &cdelt, const Matrix &pc); // Copy constructor (copy sematics) LinearXform(const LinearXform &other); // Assignment (copy sematics) LinearXform &operator=(const LinearXform &other); // Destructor ~LinearXform(); // Returns the number of world axes, which for this class is also the // number of pixel axes. uInt nWorldAxes() const; // Convert world coordinates to pixel coordinates (forward), or pixel // coordinates to world (reverse). If the conversion works True is returned, // otherwise False is returned and errorMsg is set. The output vectors // are resized appropriately. // Bool forward(Vector &pixel, const Vector &world, String &errorMsg) const; Bool reverse(Vector &world, const Vector &pixel, String &errorMsg) const; // // Retrieve the value of crpix, cdelt, and pc. // Vector crpix() const; Vector cdelt() const; Matrix pc() const; // // Set the value of crpix, cdelt, and pc. Note that since you can only // set one of them, you cannot change the dimensionality of the transform // using these functions. Instead use assignment on a temporary, i.e.: // linxform = LinearXform (crpix,crval,pc); // void crpix(const Vector &newvals); void cdelt(const Vector &newvals); void pc(const Matrix &newvals); // // Invert the LinearXform ready for use in a Fourier Transformed Coordinate. // It is the callers responsibility to delete the pointer. If it fails // the pointer is 0 and an error message is provided LinearXform* fourierInvert (String& errMsg, const Vector& axes, const Vector& crpix, const Vector& scale) const; // Comparison function. Any private Double data members are compared // with the specified fractional tolerance. You can specify axes to // exclude from the comparison if you wish. // Bool near(const LinearXform& other, Double tol=1e-6) const; Bool near(const LinearXform& other, const Vector& excludeAxes, Double tol=1e-6) const; // private: // A WCSLIB C-structure. mutable linprm linprm_p; Bool isPCDiagonal_p; void set_linprm(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/coordinates/Coordinates/LinearXform2.cc000066400000000000000000000057031476623553700227340ustar00rootroot00000000000000//# LinearXform2.cc: //# Copyright (C) 1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LinearXform* LinearXform::fourierInvert (String& errMsg, const Vector& axes, const Vector& crpix, const Vector& scale) const { if (axes.nelements() != nWorldAxes()) { errMsg = "axes length is invalid"; return 0; } if (crpix.nelements() != nWorldAxes()) { errMsg = "crpix length is invalid"; return 0; } if (scale.nelements() != nWorldAxes()) { errMsg = "scale length is invalid"; return 0; } // Matrix pc0; if (isPCDiagonal_p) { // Short cut which enables us to separate out axes pc0 = pc(); Vector d(pc0.diagonal().copy()); for (uInt i=0; i cdelt0(cdelt().copy()); Vector crpix0(LinearXform::crpix().copy()); for (uInt i=0; i #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN String ObsInfo::defaultTelescope() { return "UNKNOWN"; } String ObsInfo::defaultObserver() { return defaultTelescope(); } MEpoch ObsInfo::defaultObsDate() { MEpoch tmp; return tmp; } MVDirection ObsInfo::defaultPointingCenter () { MVDirection tmp(0.0, 0.0); return tmp; } ObsInfo::ObsInfo() : telescope_p(defaultTelescope()), observer_p(defaultObserver()), obsdate_p(defaultObsDate()), isTelPositionSet_p(False), pointingCenter_p(defaultPointingCenter()), isPointingCenterInitial_p(True) { // Nothing } ObsInfo::~ObsInfo() { // Nothing } void ObsInfo::copy_other(const ObsInfo &other) { if (this != &other) { telescope_p = other.telescope_p; observer_p = other.observer_p; obsdate_p = other.obsdate_p; telPosition_p = other.telPosition_p; isTelPositionSet_p = other.isTelPositionSet_p; pointingCenter_p = other.pointingCenter_p; isPointingCenterInitial_p = other.isPointingCenterInitial_p; } } ObsInfo::ObsInfo(const ObsInfo &other) : RecordTransformable() { copy_other(other); } ObsInfo &ObsInfo::operator=(const ObsInfo &other) { copy_other(other); return *this; } String ObsInfo::telescope() const { return telescope_p; } ObsInfo& ObsInfo::setTelescope(const String &telescope) { telescope_p = telescope; if (!isTelPositionSet_p) { MPosition pos; bool telescope_found = false; // Make ObsInfo work even if no Observatories table is installed // since that table is distributed separately in a large package try { telescope_found = MeasTable::Observatory (pos, telescope); } catch (const std::exception&) { LogIO os(LogOrigin("ObsInfo", "setTelescope", WHERE)); os << "Cannot read table of Observatories, continuing "; os << "without telescope position in ObsInfo" << LogIO::WARN; } if (telescope_found) { setTelescopePosition (pos); } } return *this; } ObsInfo& ObsInfo::setTelescopePosition(const MPosition &pos) { telPosition_p = MPosition::Convert(pos, MPosition::ITRF)(); isTelPositionSet_p = True; return *this; } String ObsInfo::telescopePositionString() const { ostringstream oss; if (isTelPositionSet_p) { MVPosition pos1 = telPosition_p.getValue(); oss << "[" << pos1.getValue()[0] << "m, " << pos1.getValue()[1] << "m, " << pos1.getValue()[2] << "m] (ITRF)"; } return oss.str(); } String ObsInfo::observer() const { return observer_p; } ObsInfo& ObsInfo::setObserver(const String &observer) { observer_p = observer; return *this; } MEpoch ObsInfo::obsDate() const { return obsdate_p; } ObsInfo& ObsInfo::setObsDate(const MEpoch &obsDate) { obsdate_p = obsDate; return *this; } MVDirection ObsInfo::pointingCenter() const { return pointingCenter_p; } ObsInfo& ObsInfo::setPointingCenter (const MVDirection& direction) // // Because the default value (0,0) is a legal pointing center, // we maintain isPointingCenterInitial_p so that we can detect // when the user has set a real pointing center. // { pointingCenter_p = direction; isPointingCenterInitial_p = False; return *this; } Bool ObsInfo::toRecord(String & error, RecordInterface & outRecord) const { error = ""; // outRecord.define("telescope", telescope()); // outRecord.define("observer", observer()); // Bool ok = True; { MeasureHolder mh(obsDate()); Record rec; ok = mh.toRecord(error, rec); if (ok) { outRecord.defineRecord("obsdate", rec); } } // { Record rec; Vector v = pointingCenter().get(); // radians rec.define("value", v); rec.define("initial", isPointingCenterInitial_p); outRecord.defineRecord("pointingcenter", rec); } // if (isTelPositionSet_p) { MeasureHolder mh(telPosition_p); Record rec; ok = mh.toRecord(error, rec); if (ok) { outRecord.defineRecord("telescopeposition", rec); } } return ok; } Bool ObsInfo::fromRecord(String & error, const RecordInterface & inRecord) { error = ""; // ObsInfo tmp; (*this) = tmp; // Make sure we are "empty" first; // Int field = inRecord.fieldNumber("telescope"); if (field >= 0) { if (inRecord.type(field) != TpString) { error = "Type of telescope field is not String!"; return False; } setTelescope(inRecord.asString(field)); } // field = inRecord.fieldNumber("observer"); if (field >= 0) { if (inRecord.type(field) != TpString) { error = "Type of observer field is not String!"; return False; } setObserver(inRecord.asString(field)); } // field = inRecord.fieldNumber("obsdate"); if (field >= 0) { if (inRecord.type(field) != TpRecord) { error = "Type of obsdate field is not Record!"; return False; } MeasureHolder mh; Bool ok = mh.fromRecord(error, inRecord.asRecord(field)); if (!ok) { return False; } if (!mh.isMEpoch()) { error = "obsdate field is not an MEpoch!"; return False; } setObsDate(mh.asMEpoch()); } // field = inRecord.fieldNumber("telescopeposition"); if (field >= 0) { if (inRecord.type(field) != TpRecord) { error = "Type of telescopeposition field is not Record!"; return False; } MeasureHolder mh; Bool ok = mh.fromRecord(error, inRecord.asRecord(field)); if (!ok) { return False; } if (!mh.isMPosition()) { error = "obsdate field is not an MPosition!"; return False; } setTelescopePosition(mh.asMPosition()); } else { isTelPositionSet_p = False; } // field = inRecord.fieldNumber("pointingcenter"); if (field >= 0) { if (inRecord.type(field) != TpRecord) { error = "Type of pointingcenter field is not Record !"; return False; } Record rec = inRecord.asRecord(field); // Vector v; Int field2 = rec.fieldNumber("value"); if (field2 >= 0) { v = Vector(rec.toArrayDouble(field2)); } else { error = "field pointingcenter does not contain subfield 'value'"; return False; } // Bool b = False; Int field3 = rec.fieldNumber("initial"); if (field3 >= 0) { if (rec.type(field3) != TpBool) { error = "pointingcenter.initial field is not Bool"; return False; } else { b = rec.asBool(field3); } } else { error = "field pointingcenter does not contain subfield 'initial'"; return False; } // Don't use function "setPointingCenter" as it will set // isPointingCenterInitial_p to False isPointingCenterInitial_p = b; pointingCenter_p = MVDirection(v); } // return True; } Bool ObsInfo::toFITS(String & error, RecordInterface & outRecord) const { error = ""; // String name = "telescop"; if (telescope() != defaultTelescope()) { outRecord.define(name, telescope()); } else { // Remove it if it already exists Int field = outRecord.fieldNumber(name); if (field >= 0 && !outRecord.isFixed()) { outRecord.removeField(field); } } // name = "observer"; if (observer() != defaultObserver()) { outRecord.define(name, observer()); } else { // Remove it if it already exists Int field = outRecord.fieldNumber(name); if (field >= 0 && !outRecord.isFixed()) { outRecord.removeField(field); } } // // Sort of yucky, but avoids a cast! name = "date-obs"; MVTime time = obsDate().get("s"); if (time != MVTime(defaultObsDate().get("s"))) { // Roundoff problems? // Very (but only) longwinded way to get at the MEpoch::Types MEpoch::Types dtype(MEpoch::castType(obsDate().getRef().getType())); String date, timesys; FITSDateUtil::toFITS(date, timesys, time, dtype); outRecord.define(name, date); outRecord.define("timesys", timesys); } else { // Remove it if it already exists // Maybe we should also remove TIMESYS, but it is conceivably needed // for some other DATE field. FITS sure is yuck-o. Int field = outRecord.fieldNumber(name); if (field >= 0 && !outRecord.isFixed()) { outRecord.removeField(field); } } // if (!isPointingCenterInitial_p) { String nameLong("obsra"); String nameLat("obsdec"); // Normalize Longitude to 0->360 deg MVAngle lon1(pointingCenter().get()(0)); MVAngle lon2 = lon1(); // +/- pi Double lon3 = lon2.degree(); // +/- 180 if (lon3 < 0) lon3 += 360.0; // 0 -> 360 outRecord.define(nameLong, lon3); // Double lat = pointingCenter().getLat(Unit("deg")).getValue(); outRecord.define(nameLat, lat); } else { // Remove it if it already exists String nameLong = "obsra"; String nameLat = "obsdec"; // Int field = outRecord.fieldNumber(nameLong); if (field >= 0 && !outRecord.isFixed()) { outRecord.removeField(field); } field = outRecord.fieldNumber(nameLat); if (field >= 0 && !outRecord.isFixed()) { outRecord.removeField(field); } } // Vector names(3); names[0] = "obsgeo-x"; names[1] = "obsgeo-y"; names[2] = "obsgeo-z"; if (isTelPositionSet_p) { // Store position in ITRF meters. MVPosition mvpos = telPosition_p.getValue(); for (int i=0; i<3; ++i) { outRecord.define (names[i], mvpos.getValue()[i]); } } else { // Remove it if it already exists for (int i=0; i<3; ++i) { Int field = outRecord.fieldNumber(names[i]); if (field >= 0 && !outRecord.isFixed()) { outRecord.removeField(field); } } } // return True; } Bool ObsInfo::fromFITS(Vector& error, const RecordInterface & rec) { error.resize(4); // Bool ok = True; ObsInfo tmp; (*this) = tmp; // Make sure we are "empty" first; // Item 0 (might be in 'TELESCOP' or 'INSTRUME' and they might // both be there and they might also hold only spaces) Bool done = False; String field("telescop"); if (rec.isDefined(field)) { Record subRec = rec.asRecord(field); if (subRec.dataType(String("value")) != TpString) { error(0) = "Type of TELESCOP field is not String!"; ok = False; } else { String ss = subRec.asString(String("value")); ss = ss.before(' '); if (ss.length()>0) { setTelescope(ss); done = True; } } } // if (!done) { field = String("instrume"); if (rec.isDefined(field)) { Record subRec = rec.asRecord(field); if (subRec.dataType(String("value")) != TpString) { error(0) = "Type of INSTRUME field is not String!"; ok = False; } else { setTelescope(subRec.asString(String("value"))); } } } // Item 1 field = String("observer"); if (rec.isDefined(field)) { Record subRec = rec.asRecord(field); if (subRec.dataType("value") != TpString) { error(1) = "Type of OBSERVER field is not String!"; ok = False; } else { setObserver(subRec.asString("value")); } } // Item 2 String field1("date-obs"); String field2("timesys"); String timeSysStr("UTC"); // if (rec.isDefined(field1)) { Record subRec1 = rec.asRecord(field1); if (subRec1.dataType("value") != TpString) { error(2) = "Type of DATE-OBS field is not a String!"; ok = False; } else { if (rec.isDefined(field2)) { Record subRec2 = rec.asRecord(field2); if (subRec2.dataType("value") == TpString) { timeSysStr = subRec2.asString("value"); } } // MVTime time; MEpoch::Types timeSys; String dateString = subRec1.asString("value"); Bool ok2 = FITSDateUtil::fromFITS(time, timeSys, dateString, timeSysStr); if (ok2) { setObsDate(MEpoch(time.get(), timeSys)); } else { error(2) = "Could not decode FITS date format from keywords"; ok = False; } } } // Item 3 Int fieldLong = rec.fieldNumber("obsra"); Int fieldLat = rec.fieldNumber("obsdec"); if (fieldLong>=0 && fieldLat>=0) { Record subRec1 = rec.asRecord(fieldLong); Record subRec2 = rec.asRecord(fieldLat); if (subRec1.dataType("value") != TpDouble || subRec2.dataType("value") != TpDouble) { error(3) = "Type of OBSRA or OBSDEC field is not Double!"; ok = False; } else { MVDirection mvd((subRec1.asDouble("value"))*M_PI/180.0, (subRec2.asDouble("value"))*M_PI/180.0); setPointingCenter(mvd); } } // Item 4 Int fieldx = rec.fieldNumber("obsgeo-x"); Int fieldy = rec.fieldNumber("obsgeo-y"); Int fieldz = rec.fieldNumber("obsgeo-z"); if (fieldx>=0 && fieldy>=0 && fieldz>=0) { Record subRec1 = rec.asRecord(fieldx); Record subRec2 = rec.asRecord(fieldy); Record subRec3 = rec.asRecord(fieldz); if (subRec1.dataType("value") != TpDouble || subRec2.dataType("value") != TpDouble || subRec3.dataType("value") != TpDouble) { error(3) = "Type of OBSGEO fields is not Double!"; ok = False; } else { MVPosition mvp(subRec1.asDouble("value"), subRec2.asDouble("value"), subRec3.asDouble("value")); setTelescopePosition(MPosition(mvp, MPosition::ITRF)); } } // if (ok) error.resize(0); return ok; } Vector ObsInfo::keywordNamesFITS() { Vector vs(9); vs(0) = "telescop"; vs(1) = "observer"; vs(2) = "date-obs"; vs(3) = "timesys"; vs(4) = "obsra"; vs(5) = "obsdec"; vs(6) = "obsgeo-x"; vs(7) = "obsgeo-y"; vs(8) = "obsgeo-z"; return vs; } ostream &operator<<(ostream &os, const ObsInfo &info) { os << "Telescope: " << info.telescope(); if (info.isTelescopePositionSet()) { os << " Position: " << info.telescopePositionString(); } os << " Observer: " << info.observer() << " Date Observed: " << info.obsDate() << " Pointing Center: " << info.pointingCenter(); return os; } } //# NAMESPACE CASACORE - END casacore-3.7.1/coordinates/Coordinates/ObsInfo.h000066400000000000000000000211671476623553700216270ustar00rootroot00000000000000//# ObsInfo.h: Store miscellaneous information related to an observation //# Copyright (C) 1998,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef COORDINATES_OBSINFO_H #define COORDINATES_OBSINFO_H #include #include #include #include #include #include //# Forward declarations #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Store miscellaneous information related to an observation. // // // // // //
      • RecordTransformable //
      • MEpoch // // // // This class is used to record miscellaneous information about an observation. // At present it contains the following: //
          //
        1. Telescope name //
        2. Observer name //
        3. Observation date //
        4. Pointing centre (as distinct from the phase center or tangent point) //
        // This list can easily be extended if necessary. // // This class has functions to interconvert with a record in a "lossless" // fashion, and to also interconvert between a record that contains a list of // FITS keywords. //
        // // // The interface is a simple get/set interface. Note that the "set" methods // can be chained together since each set method returns a reference to its // object (rather like cout). // // ObsInfo oi; // oi.setTelescope("VLA").setObserver("Glendenning"); // ... // cout << "The date observed is: " << oi.obsDate() << endl; // // // // // Record information to allow more full measures conversion, e.g. topo->lsr // requires observatory location and time. Also record in a typesafe way // image summary information users are used to from classic AIPS. // // // //
      • Nothing known // class ObsInfo : public RecordTransformable { public: // Default constructor makes an object where all the // parameters are set to their default values (see below) ObsInfo(); // Destructor ~ObsInfo(); // Copy all fields from "other" to this object. Uses copy semantics. // ObsInfo(const ObsInfo &other); ObsInfo &operator=(const ObsInfo &other); // // Telescope identifier. If this is a "standard" telescope, you should use // the same name as is available in the Observatories method of class // MeasTable. Defaults to "UNKNOWN". //
        // The telescope position can be set and will be converted to ITRF. // If the telescope position has not been set explicitly, it will be // set for a standard telescope found in the MeasTable. // String telescope() const; ObsInfo& setTelescope(const String &telescope); Bool isTelescopePositionSet() const { return isTelPositionSet_p; } const MPosition& telescopePosition() const { return telPosition_p; } String telescopePositionString() const; ObsInfo& setTelescopePosition(const MPosition&); // // The name (or initials) of the observer. Defaults to "UNKNOWN". // String observer() const; ObsInfo& setObserver(const String &observer); // // When was the observation taken (start time)? This is somewhat // problematical for observations which are taken at multiple // epochs. You should use the start time of the first sample. // The default is the MEpoch default: MJD 0 UTC // MEpoch obsDate() const; ObsInfo& setObsDate(const MEpoch &obsDate); // // What was the pointing centre, as distinct from the phase centre ? // This value is specified as an MVDirection. // This means it is you, the callers responsibility, to know what its reference // type is in order to turn it into an MDirection. // The default is (0,0) (or [1,0,0]). After you have called setPointingCenter, // the function isPointingCenterInitial will return False. // MVDirection pointingCenter() const; ObsInfo& setPointingCenter (const MVDirection& direction); // // Because the default pointing center is a valid value (0,0), this // function is available to tell you whether the pointing center has // been set (with setPointingCenter) to some value other than its // initial (return False) Bool isPointingCenterInitial () const {return isPointingCenterInitial_p;}; // Functions to interconvert between an ObsInfo and a record. These // functions are inherited from class // RecordTransformable. As new // fields get added to ObsInfo these functions should be augmented. Missing // fields should not generate an error to in fromRecord to allow for // backwards compatibility - null values should be supplied instead. // The field names are "observer", "telescope", "obsdate", and // "pointingcenter" // virtual Bool toRecord(String & error, RecordInterface & outRecord) const; virtual Bool fromRecord(String & error, const RecordInterface & inRecord); // // Functions to interconvert between an ObsInfo and FITS keywords // (converted to a Record). For the pointing center, the FITS // keywords OBSRA and OBSDEC are used. Failure of fromFITS // should probably not be regarded as fatal as the default ObsInfo // values are viable. For each item contained // in the ObsInfo, an attempt to decode it from FITS is made. // If any of them fail, False is returned, but it attempts to decode // them all. For those that fail // an error message is held in error // in the order telescope (error(0)), observer (error(1)), date // (error(2)), pointing center (error(3)). error will // be returned of length 0 if the return value is True, else // it will be length 4. // Bool toFITS(String & error, RecordInterface & outRecord) const; Bool fromFITS(Vector& error, const RecordInterface & inRecord); // In some circumstances it might be useful to know what the defaults for // the various values are so you can check if they have been set. // static String defaultTelescope(); static String defaultObserver(); static MEpoch defaultObsDate(); static MVDirection defaultPointingCenter(); // // It might be useful to know what FITS keyword names are used in to/from // FITS so we can remove them so they won't be used more than once. The // names are in lower case. static Vector keywordNamesFITS(); private: String telescope_p; String observer_p; MEpoch obsdate_p; MPosition telPosition_p; Bool isTelPositionSet_p; MVDirection pointingCenter_p; Bool isPointingCenterInitial_p; // True when ObsInfo contructed. // False after setPointingCenter called // Common copy ctor/assignment operator code. void copy_other(const ObsInfo &other); }; // Global functions // // Output declaration - useful for debugging. ostream &operator<<(ostream &os, const ObsInfo &info); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/coordinates/Coordinates/Projection.cc000066400000000000000000000243661476623553700225460ustar00rootroot00000000000000//# Projection.cc: this defines Projection which wraps up wcs projection //# Copyright (C) 1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Projection::Projection(Projection::Type which) : which_p(which), parameters_p(0) { validate(); } Projection::Projection(Projection::Type which, const Vector ¶meters) : which_p(which), parameters_p(parameters.copy()) { validate(); } Projection::Projection(const String& ctypeLon, const String& ctypeLat, const Vector& parameters) { String t1(ctypeLon); String t2(ctypeLat); which_p = type(t1, t2); if (which_p==N_PROJ) { throw(AipsError("No celestial projection found in CTYPE keywords")); } parameters_p = parameters; // validate(True); } Projection::Projection(const Projection &other) : which_p(other.which_p), parameters_p(other.parameters_p.copy()) { validate(); } Projection &Projection::operator=(const Projection &other) { if (this != &other) { which_p = other.which_p; parameters_p.resize(other.parameters_p.nelements()); parameters_p = other.parameters_p; validate(); } return *this; } Projection::~Projection() { // Nothing } String Projection::name() const { return name(which_p); } String Projection::name(Projection::Type proj) { switch (proj) { case AZP: return "AZP"; case TAN: return "TAN"; case SIN: return "SIN"; case STG: return "STG"; case ARC: return "ARC"; case ZPN: return "ZPN"; case ZEA: return "ZEA"; case AIR: return "AIR"; case CYP: return "CYP"; case CAR: return "CAR"; case MER: return "MER"; case CEA: return "CEA"; case COP: return "COP"; case COD: return "COD"; case COE: return "COE"; case COO: return "COO"; case BON: return "BON"; case PCO: return "PCO"; case SFL: return "SFL"; case PAR: return "PAR"; case AIT: return "AIT"; case MOL: return "MOL"; case CSC: return "CSC"; case QSC: return "QSC"; case TSC: return "TSC"; case SZP: return "SZP"; case HPX: return "HPX"; default: throw(AipsError("Projection::name(Type) - unknown projection")); }; return "An impossible error has occurred! NOTREACHED"; } Projection::Type Projection::type(const String &name) { Projection::Type retval = N_PROJ; if (name == "AZP") { retval = AZP; } else if (name == "TAN") { retval = TAN; } else if (name == "SIN") { retval = SIN; } else if (name == "STG") { retval = STG; } else if (name == "ARC") { retval = ARC; } else if (name == "ZPN") { retval = ZPN; } else if (name == "ZEA") { retval = ZEA; } else if (name == "AIR") { retval = AIR; } else if (name == "CYP") { retval = CYP; } else if (name == "CAR") { retval = CAR; } else if (name == "MER") { retval = MER; } else if (name == "CEA") { retval = CEA; } else if (name == "COP") { retval = COP; } else if (name == "COD") { retval = COD; } else if (name == "COE") { retval = COE; } else if (name == "COO") { retval = COO; } else if (name == "BON") { retval = BON; } else if (name == "PCO") { retval = PCO; } else if (name == "GLS" || name == "SFL") { // The GLS projection has been renamed to SFL by Calabretta and Greisen // The original GLS projection in AIPS was wrong and so SFL was invented. // wcs routines replace GLS by SFL with no further checking, so we will // do the same here. retval = SFL; } else if (name == "PAR") { retval = PAR; } else if (name == "AIT") { retval = AIT; } else if (name == "MOL") { retval = MOL; } else if (name == "CSC") { retval = CSC; } else if (name == "QSC") { retval = QSC; } else if (name == "TSC") { retval = TSC; } else if (name == "SZP") { retval = SZP; } else if (name == "HPX") { retval = HPX; } return retval; } uInt Projection::nParameters(Projection::Type proj) { // return maximum number of parameters switch (proj) { case AZP: return 2; case TAN: return 0; case SIN: return 2; case STG: return 0; case ARC: return 0; case ZPN: return 20; case ZEA: return 0; case AIR: return 1; case CYP: return 2; case CAR: return 0; case MER: return 0; case CEA: return 1; case COP: return 2; case COD: return 2; case COE: return 2; case COO: return 2; case BON: return 1; case PCO: return 0; case SFL: return 0; case PAR: return 0; case AIT: return 0; case MOL: return 0; case CSC: return 0; case QSC: return 0; case TSC: return 0; case SZP: return 3; case HPX: return 0; default: throw(AipsError("Projection::nParameters() - unknown projection")); } return 0; // NOTREACHED } uInt Projection::nMinParameters(Projection::Type proj) { // return minimum number of parameters switch (proj) { case AZP: return 0; case TAN: return 0; case SIN: return 0; case STG: return 0; case ARC: return 0; case ZPN: return 0; case ZEA: return 0; case AIR: return 0; case CYP: return 0; case CAR: return 0; case MER: return 0; case CEA: return 0; case COP: return 1; case COD: return 1; case COE: return 1; case COO: return 1; case BON: return 1; case PCO: return 0; case SFL: return 0; case PAR: return 0; case AIT: return 0; case MOL: return 0; case CSC: return 0; case QSC: return 0; case TSC: return 0; case SZP: return 0; case HPX: return 0; default: throw(AipsError("Projection::nMinParameters() - unknown projection")); } return 0; // NOTREACHED } Bool Projection::near(const Projection &other, Double tol) const { if (which_p != other.which_p) return False; if (parameters_p.nelements() != other.parameters_p.nelements()) return False; for (uInt i=0; i=0) ctypeLong = String(ctypeLong.before(i1)); // i1 = ctypeLat.index(RXwhite,0); if (i1>=0) ctypeLat = String(ctypeLat.before(i1)); Int l1 = ctypeLong.length(); Int l2 = ctypeLat.length(); Int n = 4; String proj1(ctypeLong.at(n, l1-4)); String proj2(ctypeLat.at(n, l2-4)); // Get rid of leading -'s proj1.gsub(Regex("^-*"), String("")); proj2.gsub(Regex("^-*"), String("")); // Get rid of spaces proj1.gsub(Regex(" *"), String("")); proj2.gsub(String(" "), String("")); // if (proj1 != proj2) { throw (AipsError("Projection codes must be identical")); } // if (proj1==String("")) { throw (AipsError("No projection code given in direction axes")); } // return type(proj1); } } //# NAMESPACE CASACORE - END casacore-3.7.1/coordinates/Coordinates/Projection.h000066400000000000000000000156301476623553700224020ustar00rootroot00000000000000//# Projection.h: Geometric parameters needed for a sky projection to a plane //# Copyright (C) 1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef COORDINATES_PROJECTION_H #define COORDINATES_PROJECTION_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Geometric parameters needed for a sky projection to a plane // // // // // // //
      • Knowledge of astronomical coordinate conversions in general. Probably the // best documents are the papers by Mark Calabretta and Eric Greisen. // The initial draft from 1996 can be found at // http://www.atnf.csiro.au/~mcalabre. It is this draft that the // Coordinate classes are based upon. Since then, this paper has evolved // into three which can be found at the above address, and will be published in the // Astronomy and Astrophysics Supplement Series (probably in 2000). // The design has changed since the initial draft. When these papers // are finalized, and the IAU has ratified the new standards, WCSLIB // (Mark Calabretta's implementation of these conventions) will be // revised for the new designs. At that time, the Coordinate classes // may also be revised. // // // // This class is used to hold: //
          //
        1. The type of the projection (e.g. SIN); and //
        2. The parameters of the projection, if any. These parameters are described // by Calabretta and Greisen (called PROJP) in the 1996 draft. // In the recent versions, this paper has split into three, and the // projection parameters have been reworked into the PV matrix. // However, these have not yet been implemented in WCSLIB so we // stick with the old ones for now. //
        //
        // // // // Projection proj(Projection::CAR); // cerr << proj.parameters() << endl; // // This projection requires no parameters so the printed parameter // vector would be of zero length. // // // //
      • AipsError // // // //
      • Worry about projection parameters which are unit dependent (i.e. // radians vs. degrees). //
      • LONGPOLE should probably go in here. // // class Projection { public: // Hold all the known types of celestial projections. enum Type { // Zenithal/Azimuthal perspective. AZP, // Slant zenithal perspective, new SZP, // Gnomonic. TAN, // Orthographics/synthesis. SIN, // Stereographic. STG, // zenith/azimuthal equidistant. ARC, // zenithal/azimuthal polynomial. ZPN, // zenithal/azimuthal equal area. ZEA, // Airy. AIR, // Cylindrical perspective. CYP, // Plate carree CAR, // Mercator. MER, // Cylindrical equal area. CEA, // Conic perspective. COP, // Conic equidistant. COD, // Conic equal area. COE, // Conic orthomorphic. COO, // Bonne. BON, // Polyconic. PCO, // Sanson-Flamsteed (global sinusoidal). // The old GLS projection is now SFL. The 'GLS' // string will be converted to 'SFL' SFL, // Parabolic. PAR, // Hammer-Aitoff. AIT, // Mollweide. MOL, // COBE quadrilateralized spherical cube. CSC, // Quadrilateralized spherical cube. QSC, // Tangential spherical cube. TSC, // HEALPix grid HPX, // N_PROJ gives the number of supported projections - it shouldn't be used // as a projection N_PROJ }; // Construct a projection which needs no parameters. SIN is unique in that // it can be created with 0 or 2 parameters. Projection(Projection::Type which=CAR); // Construct a projection from FITS CTYPE keywords Projection(const String& ctypeLin, const String& ctypeLat, const Vector& parameters); // Construct a projection which needs parameters. The parameter vector must be // the length of the required number of parameters. Projection(Projection::Type which, const Vector ¶meters); // Copy constructor (copy semantics). Projection(const Projection &other); // Assignment (copy semantics) Projection &operator=(const Projection &other); // Destructor ~Projection(); // What is the Type of this projection? Projection::Type type() const; // What is the type of this projection as a String (e.g. "SIN"). // String name() const; static String name(Projection::Type proj); // // Turn a projection type name into a Type. // Returns N_PROJ if the projection is not known. static Projection::Type type(const String &name); // How many parameters does this projection have at most? // What is the minimum number of parameters that have to be supplied? // What are the parameter values? // static uInt nParameters(Projection::Type proj); static uInt nMinParameters(Projection::Type proj); const Vector ¶meters() const; // // Comparison to fractional tolerance. Bool near(const Projection &other, Double tol=1.0e-6) const; // Is this projection a 'zenithal' projection static Bool isZenithal (Projection::Type proj); private: Projection::Type which_p; Vector parameters_p; void validate(const Bool verbose=False); Projection::Type type (String& ctypeLong, String& ctypeLat) const; }; //#---------- Inlines -------------------------------------------------------------- inline Projection::Type Projection::type() const {return which_p;} inline const Vector & Projection::parameters() const {return parameters_p;} } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/coordinates/Coordinates/QualityCoordinate.cc000066400000000000000000000364151476623553700240700ustar00rootroot00000000000000//# QualityCoordinate.cc: this defines QualityCoordinate which shoe-horns Quality axes into a Coordinate //# Copyright (C) 1997,1998,1999,2000,2001,2002,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN QualityCoordinate::QualityCoordinate(const Vector &whichQuality) : Coordinate(), values_p(whichQuality.nelements()), crval_p(0), crpix_p(0), matrix_p(1), cdelt_p(1), name_p("Quality"), unit_p("") { // tested: tQualityCoordinate: 122 setQuality(whichQuality); nValues_p = values_p.nelements(); setDefaultWorldMixRanges(); } QualityCoordinate::QualityCoordinate(const QualityCoordinate &other) : Coordinate(other), values_p (other.values_p), crval_p (other.crval_p), crpix_p (other.crpix_p), matrix_p (other.matrix_p), cdelt_p (other.cdelt_p), name_p (other.name_p), unit_p (other.unit_p), nValues_p(other.nValues_p) { // tested: tQualityCoordinate: 146 setDefaultWorldMixRanges(); } QualityCoordinate &QualityCoordinate::operator=(const QualityCoordinate &other) { // tested: tQualityCoordinate: 175 if (this != &other) { Coordinate::operator=(other); values_p = other.values_p; crval_p = other.crval_p; crpix_p = other.crpix_p; matrix_p = other.matrix_p; cdelt_p = other.cdelt_p; name_p = other.name_p; unit_p = other.unit_p; nValues_p = other.nValues_p; } return *this; } QualityCoordinate::~QualityCoordinate() {} Coordinate::Type QualityCoordinate::type() const { // tested: tQualityCoordinate: 168 return Coordinate::QUALITY; } String QualityCoordinate::showType() const { // tested: tQualityCoordinate: 174 return String("Quality"); } uInt QualityCoordinate::nPixelAxes() const { // tested: tQualityCoordinate: 181 return 1; } uInt QualityCoordinate::nWorldAxes() const { // tested: tQualityCoordinate: 188 return 1; } Bool QualityCoordinate::toWorld(Quality::QualityTypes &quality, Int pixel) const { // tested: tQualityCoordinate: 443 Double world; if (toWorld (world, static_cast(pixel))) { quality = Quality::type(values_p[pixel]); return True; } return False; } Bool QualityCoordinate::toPixel(Int& pixel, Quality::QualityTypes quality) const { // tested: tQualityCoordinate: 437 Double tmp; if (toPixel(tmp, static_cast(quality))) { pixel = Int(tmp + 0.5); return True; } return False; } Bool QualityCoordinate::toWorld(Vector& world, const Vector& pixel, Bool) const { // tested: tQualityCoordinate: 403 DebugAssert(pixel.nelements()==1, AipsError); world.resize(1); // Double tmp; if (toWorld(tmp, pixel(0))) { world(0) = tmp; return True; } return False; } Bool QualityCoordinate::toPixel(Vector &pixel, const Vector &world) const { // tested: tQualityCoordinate: 411 DebugAssert(world.nelements()==1, AipsError); pixel.resize(1); // Double tmp; if (toPixel(tmp, world(0))) { pixel(0) = tmp; return True; } return False; } Double QualityCoordinate::toWorld (Quality::QualityTypes quality) { // tested: tQualityCoordinate: 456 return static_cast(quality); } Quality::QualityTypes QualityCoordinate::toWorld (Double world) { // tested: tQualityCoordinate: 456 Int i = Int(world + 0.5); if (i < 0 || i>=Quality::NumberOfTypes) { return Quality::Undefined; } // return static_cast(i); } Vector QualityCoordinate::quality() const { // tested: tQualityCoordinate: 257, 435 return Vector(values_p.begin(), values_p.end()); } void QualityCoordinate::setQuality (const Vector &whichQuality) { // implicitly tested via the constructor AlwaysAssert(whichQuality.nelements()>0, AipsError); // Make sure the quality occur at most once Block alreadyUsed(Quality::NumberOfTypes); alreadyUsed = False; for (uInt i=0; i QualityCoordinate::worldAxisNames() const { // tested: tQualityCoordinate: 195 Vector names(1); names = name_p; return names; } Vector QualityCoordinate::worldAxisUnits() const { // tested: tQualityCoordinate: 221 Vector units(1); units = unit_p; return units; } Vector QualityCoordinate::referencePixel() const { // tested: tQualityCoordinate: 315 Vector crpix(1); crpix = crpix_p; return crpix; } Matrix QualityCoordinate::linearTransform() const { // tested: tQualityCoordinate: 307 Matrix matrix(1,1); matrix(0,0) = matrix_p; return matrix; } Vector QualityCoordinate::increment() const { // tested: tQualityCoordinate: 307 Vector cdelt(1); cdelt = cdelt_p; return cdelt; } Vector QualityCoordinate::referenceValue() const { // tested: tQualityCoordinate: 299 Vector crval(1); crval = crval_p; return crval; } Bool QualityCoordinate::setWorldAxisNames(const Vector &names) { // tested: tQualityCoordinate: 204 Bool ok = names.nelements()==1; if (!ok) { set_error ("names vector must be of length 1"); } else { name_p = names(0); } return ok; } Bool QualityCoordinate::setWorldAxisUnits(const Vector &) { // tested: tQualityCoordinate: 227, 242 return True; } Bool QualityCoordinate::setReferencePixel(const Vector &) { // tested: tQualityCoordinate: 365 return True; } Bool QualityCoordinate::setLinearTransform(const Matrix &) { // tested: tQualityCoordinate: 380 return True; } Bool QualityCoordinate::setIncrement(const Vector &) { // tested: tQualityCoordinate: 350 return True; } Bool QualityCoordinate::setReferenceValue(const Vector &) { // tested: tQualityCoordinate: 336 return True; } Bool QualityCoordinate::near(const Coordinate& other, Double tol) const { // tested: basic test criteria in many // tests in tQualityCoordinate Vector excludeAxes; return near(other, excludeAxes, tol); } Bool QualityCoordinate::near(const Coordinate& other, const Vector& excludeAxes, Double) const { // tested: basic test criteria in many // tests in tQualityCoordinate if (other.type() != this->type()) { set_error("Comparison is not with another QualityCoordinate"); return False; } // Check name const QualityCoordinate& sCoord = dynamic_cast(other); if (name_p != sCoord.name_p) { set_error("The QualityCoordinates have differing world axis names"); return False; } // Number of pixel and world axes is the same for a QualityCoordinate // and it always 1. So if excludeAxes contains "0" we are done. // Add an assertion check should this change Bool found; if (linearSearch(found, excludeAxes, 0, excludeAxes.nelements()) >= 0) return True; // The only other thing that really matters in the QualityCoordinate // is the values along the axis. Nothing else (e.g. crval_p etc) // is ever actually used. if (nValues_p != sCoord.nValues_p) { set_error("The QualityCoordinates have different numbers of Quality values"); return False; } return True; } Bool QualityCoordinate::doNearPixel (const Coordinate& other, const Vector&, const Vector&, Double) const { // tested: tQualityCoordinate: 568 if (other.type() != Coordinate::QUALITY) { set_error("Other Coordinate type is not Quality"); return False; } // // The only other thing that really matters in the QualityCoordinate // is the values along the axis. Nothing else (e.g. crval_p etc) // is ever actually used. // Here we simply test that the number of quality is the same and that // is that // const QualityCoordinate& sCoord = dynamic_cast(other); if (nValues_p != sCoord.nValues_p) { set_error("The QualityCoordinates have different numbers of Quality values"); return False; } // return True; } Bool QualityCoordinate::save(RecordInterface &container, const String &fieldName) const { // tested: tQualityCoordinate: 267 Bool ok = !container.isDefined(fieldName); if (ok) { Record subrec; subrec.define("axes", worldAxisNames()); // Vector quality(nValues_p); for (Int i=0; i axes; subrec.get("axes", axes); // if (!subrec.isDefined("quality")) { return 0; } Vector quality; subrec.get("quality", quality); Vector iquality(quality.nelements()); for (uInt i=0; isetWorldAxisNames(axes); AlwaysAssert(retval, AipsError); return retval; } Coordinate *QualityCoordinate::clone() const { // tested: tQualityCoordinate: 285 return new QualityCoordinate(*this); } String QualityCoordinate::format(String& units, Coordinate::formatType, Double worldValue, uInt worldAxis, Bool, Bool, Int, Bool) const // // world abs=rel for Quality // { // tested: tQualityCoordinate: 478 units = worldAxisUnits()(worldAxis); return Quality::name(QualityCoordinate::toWorld (worldValue)); } void QualityCoordinate::makePixelRelative (Vector& pixel) const // // rel = abs - ref // { // tested: tQualityCoordinate: 547 DebugAssert(pixel.nelements()==1, AipsError); // Int index = Int(pixel(0) + 0.5); if (index >= 0 && index < nValues_p) { pixel -= referencePixel(); } else { ostringstream os; os << "Absolute pixel " << index << " is out of range [0.." << nValues_p-1 << "]"; String s(os); throw(AipsError(s)); } } void QualityCoordinate::makePixelAbsolute (Vector& pixel) const // // abs = rel + ref // { // tested: tQualityCoordinate: 558 DebugAssert(pixel.nelements()==1, AipsError); pixel += referencePixel(); // Int index = Int(pixel(0) + 0.5); if (index < 0 || index >= nValues_p) { ostringstream os; os << "Absolute pixel " << index << " is out of range [0.." << nValues_p-1 << "]"; String s(os); throw(AipsError(s)); } } void QualityCoordinate::makeWorldRelative (Vector&) const // // By definition, for QualityCoordinate, world abs = rel // // there is nothing to test. { } void QualityCoordinate::makeWorldAbsolute (Vector&) const // // By definition, for QualityCoordinate, world abs = rel // // there is nothing to test. { } // Private functions Bool QualityCoordinate::toWorld(Double& world, const Double pixel) const { // implicitly tested via the public method // toWorld() Int index = Int(pixel + 0.5); if (index >= 0 && index < nValues_p) { world = values_p[index]; return True; } else { ostringstream os; os << "Pixel " << index << " is out of range [0.." << nValues_p-1 << "]"; set_error(os); return False; } } Bool QualityCoordinate::toPixel(Double& pixel, const Double world) const { // implicitly tested via the public method // toWorld() Bool found = False; Int index; for (index=0; index pixel(nPixelAxes()); pixel(0) = 0; toWorld(worldMin_p, pixel); pixel(0) = nValues_p - 1; toWorld(worldMax_p, pixel); } } //# NAMESPACE CASACORE - END casacore-3.7.1/coordinates/Coordinates/QualityCoordinate.h000066400000000000000000000236521476623553700237310ustar00rootroot00000000000000//# QualityCoordinate.h: Interconvert between pixel number and Quality value. //# Copyright (C) 1997,1998,1999,2000,2001,2002,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef COORDINATES_QUALITYCOORDINATE_H #define COORDINATES_QUALITYCOORDINATE_H #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Interconvert between pixel and Quality value. // // // // // // //
      • Coordinate //
      • Quality // // // // // // // All pixel coordinates are zero relative. // // // // In this example we create a QualityCoordinate containing 'DATA' // and 'ERROR' // // Vector newQuality(2); // newQuality(0) = Quality::DATA; // newQuality(1) = Quality::ERROR; // qual = QualityCoordinate(newQuality); // // // // // // class QualityCoordinate : public Coordinate { public: // The length of whichQuality is the length of the axis, and the values // define which quality are in which axis value. Often the vector will be of // length 2 and will contain Quality::DATA, and ERROR. explicit QualityCoordinate(const Vector &whichQuality); // Copy constructor (copy semantics) QualityCoordinate(const QualityCoordinate &other); // Assignment (copy semantics) QualityCoordinate &operator=(const QualityCoordinate &other); // Destructor. virtual ~QualityCoordinate(); // Returns Coordinates::QUALITY. virtual Coordinate::Type type() const; // Always returns the String "Quality". virtual String showType() const; // Always returns 1. // virtual uInt nPixelAxes() const; virtual uInt nWorldAxes() const; // // Convert a pixel to a world coordinate or vice versa. Returns True // if the conversion succeeds, otherwise it returns False and method // errorMessage returns an error message. // The output vectors are appropriately resized before use. // The Bool parameter in toWorld() is ignored as this coordinate does not // support a conversion layer frame. // virtual Bool toWorld(Vector &world, const Vector &pixel, Bool=True) const; virtual Bool toPixel(Vector &pixel, const Vector &world) const; // // Interconvert between pixel and world as a Quality type. // It returns False if no conversion could be done. // Bool toPixel(Int &pixel, Quality::QualityTypes quality) const; Bool toWorld(Quality::QualityTypes &quality, Int pixel) const; // // Interconvert between world stored as a Double and world stored as // a Quality type. Since these functions are static, any valid // Quality type can be used. The second function returns // Quality::Undefined if world is illegal. // static Double toWorld (Quality::QualityTypes quality); static Quality::QualityTypes toWorld (Double world); // // Make absolute coordinates relative and vice-versa. // For the QualityCoordinate relative world coordinates are defined to be the // same as absolute world coordinates. Relative pixels do have meaning // and are implemented (rel = abs - refPix) // virtual void makePixelRelative (Vector& pixel) const; virtual void makePixelAbsolute (Vector& pixel) const; virtual void makeWorldRelative (Vector& world) const; virtual void makeWorldAbsolute (Vector& world) const; // // Get the Quality values (Quality::QualityType) that we constructed // with into a vector Vector quality() const; // Set a new vector of Quality values (a vector of Quality::QualityType) void setQuality (const Vector &whichQuality); // Report the value of the requested attribute. // virtual Vector worldAxisNames() const; virtual Vector referencePixel() const; virtual Matrix linearTransform() const; virtual Vector increment() const; virtual Vector referenceValue() const; // // Set the value of the requested attribute. For the QualityCoordinate, // these have no effect (always return True) except for setWorldAxisNames. // virtual Bool setWorldAxisNames(const Vector &names); virtual Bool setReferencePixel(const Vector &refPix); virtual Bool setLinearTransform(const Matrix &xform); virtual Bool setIncrement(const Vector &inc) ; virtual Bool setReferenceValue(const Vector &refval) ; // // The set function has no effect as the units must be empty for a QualityCoordinate // Always returns True // virtual Bool setWorldAxisUnits(const Vector &units); virtual Vector worldAxisUnits() const; // // Set the world min and max ranges, for use in function toMix, // for a lattice of the given shape (for this coordinate). // The implementation here gives world coordinates at the start // and end of the Quality axis. // The output vectors are resized. Returns False if fails (and // then setDefaultWorldMixRanges generates the ranges) // with a reason in errorMessage(). // The setDefaultWorldMixRanges function // gives you [-1e99->1e99]. // virtual Bool setWorldMixRanges (const IPosition& shape); virtual void setDefaultWorldMixRanges (); // // Format a QualityCoordinate world value with the common format // interface (refer to the base class Coordinate // for basics. // // A QualityCoordinate is formatted differently from other Coordinate // types. The world value is converted to the character representation // as defined by the enum QualityTypes in the class // Quality. // // Thus, all other arguments to do with formatting and precision are ignored. virtual String format(String& units, Coordinate::formatType format, Double worldValue, uInt worldAxis, Bool isAbsolute=True, Bool showAsAbsolute=True, Int precision = -1, Bool usePrecForMixed=False) const; // Comparison function. Any private Double data members are compared // with the specified fractional tolerance. Don't compare on the specified // axes in the Coordinate. If the comparison returns False, method // errorMessage returns a message about why. // virtual Bool near(const Coordinate& other, Double tol=1e-6) const; virtual Bool near(const Coordinate& other, const Vector& excludeAxes, Double tol=1e-6) const; // // Save the QualityCoordinate into the supplied record using the supplied field name. // The field must not exist, otherwise False is returned. virtual Bool save(RecordInterface &container, const String &fieldName) const; // Recover the QualityCoordinate from a record. // A null pointer means that the restoration did not succeed - probably // because fieldName doesn't exist or doesn't contain a CoordinateSystem. static QualityCoordinate* restore(const RecordInterface &container, const String &fieldName); // Make a copy of the QualityCoordinate using new. The caller is responsible for calling // delete. virtual Coordinate *clone() const; // Comparison only made for specified axes in this and other Coordinate virtual Bool doNearPixel (const Coordinate& other, const Vector& thisAxes, const Vector& otherAxes, Double tol=1.0e-6) const; private: Bool toWorld(Double& world, const Double pixel) const; Bool toPixel(Double& pixel, const Double world) const; // Block values_p; // Keep these for subimaging purposes. Double crval_p, crpix_p, matrix_p, cdelt_p; String name_p; String unit_p; Int nValues_p; // Undefined and inaccessible QualityCoordinate(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/coordinates/Coordinates/Spectral2Coordinate.cc000066400000000000000000000316661476623553700243020ustar00rootroot00000000000000//# Spectral2Coordinate.cc: this defines Measures related SpectralCoordinate functions //# Copyright (C) 1997,1998,1999,2000,2001,2002,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Bool SpectralCoordinate::toWorld(MFrequency& world, Double pixel) const { static MVFrequency world_tmp; if (toWorld(world_tmp, pixel)) { world.set(world_tmp, MFrequency::Ref(type_p)); return True; } return False; } Bool SpectralCoordinate::toWorld(MVFrequency& world, Double pixel) const { Double world_tmp; static Quantum q_tmp; // if (toWorld(world_tmp, pixel)) { q_tmp.setValue(world_tmp); q_tmp.setUnit(Unit(worldAxisUnits()(0))); world = MVFrequency(q_tmp); return True; } return False; } Bool SpectralCoordinate::toPixel(Double& pixel, const MFrequency& world) const { return toPixel(pixel, world.getValue()); } Bool SpectralCoordinate::toPixel(Double& pixel, const MVFrequency& world) const { Double world_tmp; world_tmp = world.get(Unit(worldAxisUnits()(0))).getValue(); return toPixel(pixel, world_tmp); } Bool SpectralCoordinate::pixelToVelocity (Quantum& velocity, Double pixel) const { Double world; if (!toWorld(world, pixel)) return False; return frequencyToVelocity (velocity, world); } Bool SpectralCoordinate::pixelToVelocity (Double& velocity, Double pixel) const { Double world; if (!toWorld(world, pixel)) return False; velocity = pVelocityMachine_p->makeVelocity(world).getValue(); // if(isNaN(velocity)){ set_error("velocity is NaN"); return False; } else{ return True; } } Bool SpectralCoordinate::pixelToVelocity (Vector& velocity, const Vector& pixel) const { velocity.resize(pixel.nelements()); // Perhaps its faster to make a vector of world and do them // all in one go in the machine ? Haven't tested it. Double world; for (uInt i=0; imakeVelocity(world).getValue(); } // if(isNaN(velocity(0))){ set_error("velocity is NaN"); return False; } else{ return True; } } Bool SpectralCoordinate::frequencyToVelocity (Quantum& velocity, Double frequency) const { velocity = pVelocityMachine_p->makeVelocity(frequency); MVFrequency mvf(frequency); // if(isNaN(velocity.getValue())){ set_error("velocity is NaN"); return False; } else{ return True; } } Bool SpectralCoordinate::frequencyToVelocity (Double& velocity, Double frequency) const { static Quantum t; t = pVelocityMachine_p->makeVelocity(frequency); velocity = t.getValue(); if(isNaN(velocity)){ set_error("velocity is NaN"); return False; } else{ return True; } } Bool SpectralCoordinate::frequencyToVelocity (Vector& velocity, const Vector& frequency) const { velocity.resize(frequency.nelements()); velocity = pVelocityMachine_p->makeVelocity(frequency).getValue(); // if(isNaN(velocity(0))){ set_error("velocity is NaN"); return False; } else{ return True; } } Bool SpectralCoordinate::frequencyToVelocity (Quantum& velocity, const MFrequency& frequency) const { return frequencyToVelocity(velocity, frequency.getValue()); } Bool SpectralCoordinate::frequencyToVelocity (Quantum& velocity, const MVFrequency& frequency) const { velocity = pVelocityMachine_p->operator()(frequency); if(isNaN(velocity.getValue())){ set_error("velocity is NaN"); return False; } else { return True; } } Bool SpectralCoordinate::frequencyToWavelength (Vector& wavelength, const Vector& frequency) const { wavelength.resize(frequency.nelements()); // wave = C::c/freq * 1/to_hz_p * 1/to_m_p Double factor = C::c/to_hz_p/to_m_p; Bool rval=True; for(uInt i=0; i0.){ wavelength(i) = factor/frequency(i); } else{ wavelength(i) = HUGE_VAL; set_error("input frequency is <= 0"); rval = False; } } return rval; } Bool SpectralCoordinate::frequencyToAirWavelength (Vector& wavelength, const Vector& frequency) const { wavelength.resize(frequency.nelements()); // airwave = C::c/freq * 1/to_hz_p * 1/to_m_p/refractive_index Double factor = C::c/to_hz_p/to_m_p; Bool rval = True; for(uInt i=0; i0.){ Double vacWave = factor/frequency(i); //cout << "toWave: vacWave " << vacWave << " to_m_p " << to_m_p << endl; wavelength(i) = vacWave/FITSSpectralUtil::refractiveIndex(vacWave* 1E6 * to_m_p); //cout << "toWave air wave " << wavelength(i) << endl; } else{ wavelength(i) = HUGE_VAL; set_error("input frequency is <= 0"); rval = False; } } return rval; } Bool SpectralCoordinate::airWavelengthToFrequency (Vector& frequency, const Vector& airWavelength) const { frequency.resize(airWavelength.nelements()); // freq = C::c/wave * 1/to_hz_p * 1/to_m_p, wave = n(airwave)*airwave Double factor = C::c/to_hz_p/to_m_p; Bool rval = True; for(uInt i=0; i0.){ Double lambda_um = airWavelength(i) * 1E6L * to_m_p; // in micrometers frequency(i) = factor/airWavelength(i)/FITSSpectralUtil::refractiveIndex(lambda_um); //cout << "toFreq: air wave " << airWavelength(i) << " lambda_um " << lambda_um << endl; //cout << "toFreq: freq " << frequency(i) << endl; } else{ frequency(i) = HUGE_VAL; set_error("input frequency is <= 0"); rval = False; } } return rval; } Bool SpectralCoordinate::wavelengthToFrequency (Vector& frequency, const Vector& wavelength) const { // since the functional form of the conversion is identical, we can reuse the inverse function return frequencyToWavelength(frequency,wavelength); } Bool SpectralCoordinate::velocityToPixel (Double& pixel, Double velocity) const { Double frequency; if (!velocityToFrequency(frequency, velocity)) return False; return toPixel(pixel, frequency); } Bool SpectralCoordinate::velocityToPixel (Vector& pixel, const Vector& velocity) const { pixel.resize(velocity.nelements()); Double frequency, pix; for (uInt i=0; imakeFrequency (velocity).getValue(); if(frequency<=0.){ set_error("frequency <= 0"); return False; } else { return True; } } Bool SpectralCoordinate::velocityToFrequency (Vector& frequency, const Vector& velocity) const { frequency.resize(velocity.nelements()); for (uInt i=0; imakeFrequency (velocity(i)).getValue(); } // if(frequency(0)<=0.){ set_error("frequency <= 0"); return False; } else { return True; } } void SpectralCoordinate::makeVelocityMachine (const String& velUnit, MDoppler::Types velType, const Unit& freqUnit, MFrequency::Types freqType, Double restFreq) { Quantum rF(restFreq, freqUnit); pVelocityMachine_p = new VelocityMachine(MFrequency::Ref(freqType), freqUnit, MVFrequency(rF), MDoppler::Ref(velType), Unit(velUnit)); } void SpectralCoordinate::updateVelocityMachine (const String& velUnit, MDoppler::Types velType) { if (pVelocityMachine_p->getDopplerUnits().getName() != velUnit) { pVelocityMachine_p->set(Unit(velUnit)); } if (MDoppler::castType(pVelocityMachine_p->getDopplerReference().getType()) != velType) { pVelocityMachine_p->set(MDoppler::Ref(velType)); } } Int SpectralCoordinate::makeConversionMachines (MFrequency::Types type, MFrequency::Types conversionType, const MEpoch& epoch, const MPosition& position, const MDirection& direction) { LogIO os(LogOrigin("SpectralCoordinate", "makeConversionMachines")); // Clean up extent machines deleteConversionMachines(); // If the types are the same, don't make the machines. if (conversionType==type_p) return 2; // It is assumed the passed in Measures are viable pConversionMachineTo_p = new MFrequency::Convert(); Bool ok1 = CoordinateUtil::makeFrequencyMachine (os, *pConversionMachineTo_p, conversionType, type, direction, direction, epoch, epoch, position, position); // pConversionMachineFrom_p = new MFrequency::Convert(); Bool ok2 = CoordinateUtil::makeFrequencyMachine (os, *pConversionMachineFrom_p, type, conversionType, direction, direction, epoch, epoch, position, position); // if (!ok1 || !ok2) { // This means the trial conversions when the machines were made failed. // Usually this means the rest frequency or radial velocity was required deleteConversionMachines(); return -1; } else if (pConversionMachineTo_p->isNOP() && pConversionMachineFrom_p->isNOP()) { // If the machines are noOps, delete them deleteConversionMachines(); return 3; } else { // Set up units so we can just use doubles in conversions String unit = worldAxisUnits()(0); pConversionMachineTo_p->set(Unit(unit)); pConversionMachineFrom_p->set(Unit(unit)); } // return 1; } void SpectralCoordinate::convertTo (Vector& world) const { if (pConversionMachineTo_p) { for(uInt i=0; i& world) const { if (pConversionMachineFrom_p) { for(uInt i=0; i #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN SpectralCoordinate::SpectralCoordinate() : Coordinate(), type_p(MFrequency::TOPO), conversionType_p(type_p), restfreqs_p(0), restfreqIdx_p(0), pConversionMachineTo_p(0), pConversionMachineFrom_p(0), pVelocityMachine_p(0), velType_p(MDoppler::RADIO), velUnit_p("km/s"), waveUnit_p("mm"), unit_p(Unit("Hz")), axisName_p("Frequency"), formatUnit_p("") { restfreqs_p.resize(1); restfreqs_p(0) = 0.0; makeVelocityMachine (velUnit_p, velType_p, unit_p, type_p, restfreqs_p(restfreqIdx_p)); // makeWCS(wcs_p, String("FREQ"), 0.0, 0.0, 1.0, 1.0, restfreqs_p(0)); to_hz_p = 1.0; to_m_p = 0.001; nativeType_p = SpectralCoordinate::FREQ; // setDefaultWorldMixRanges(); } SpectralCoordinate::SpectralCoordinate(MFrequency::Types type, Double refVal, Double inc, Double refPix, Double restFrequency) : Coordinate(), type_p(type), conversionType_p(type_p), restfreqs_p(0), restfreqIdx_p(0), pConversionMachineTo_p(0), pConversionMachineFrom_p(0), pVelocityMachine_p(0), velType_p(MDoppler::RADIO), velUnit_p("km/s"), waveUnit_p("mm"), unit_p(Unit("Hz")), axisName_p("Frequency"), formatUnit_p("") { // restfreqs_p.resize(1); restfreqs_p(0) = max(0.0, restFrequency); // makeVelocityMachine (velUnit_p, velType_p, unit_p, type_p, restfreqs_p(restfreqIdx_p)); // makeWCS(wcs_p, String("FREQ"), refPix, refVal, inc, 1.0, restfreqs_p(0)); to_hz_p = 1.0; to_m_p = 0.001; nativeType_p = SpectralCoordinate::FREQ; // setDefaultWorldMixRanges(); } SpectralCoordinate::SpectralCoordinate(MFrequency::Types type, const Quantum& refVal, const Quantum& inc, Double refPix, const Quantum& restFrequency) : Coordinate(), type_p(type), conversionType_p(type_p), restfreqs_p(0), restfreqIdx_p(0), pConversionMachineTo_p(0), pConversionMachineFrom_p(0), pVelocityMachine_p(0), velType_p(MDoppler::RADIO), velUnit_p("km/s"), waveUnit_p("mm"), unit_p(Unit("Hz")), axisName_p("Frequency"), formatUnit_p("") { Unit hz("Hz"); if (!refVal.isConform(hz)) { throw(AipsError("Unit of reference frequency is not consistent with Hz")); } if (!inc.isConform(hz)) { throw(AipsError("Unit of frequency increment is not consistent with Hz")); } if (!restFrequency.isConform(hz)) { throw(AipsError("Unit of rest frequency is not consistent with Hz")); } // AlwaysAssert(restFrequency.getValue(hz)>=0.0, AipsError); restfreqs_p.resize(1); restfreqs_p(0) = max(0.0, restFrequency.getValue(hz)); // makeVelocityMachine (velUnit_p, velType_p, unit_p, type_p, restfreqs_p(restfreqIdx_p)); // makeWCS(wcs_p, String("FREQ"), refPix, refVal.getValue(hz), inc.getValue(hz), 1.0, restfreqs_p(0)); to_hz_p = 1.0; to_m_p = 0.001; nativeType_p = SpectralCoordinate::FREQ; // setDefaultWorldMixRanges(); } SpectralCoordinate::SpectralCoordinate( MFrequency::Types type, const Vector &freqs, Double restFrequency) : Coordinate(), type_p(type), conversionType_p(type_p), restfreqs_p(0), restfreqIdx_p(0), pConversionMachineTo_p(0), pConversionMachineFrom_p(0), pVelocityMachine_p(0), velType_p(MDoppler::RADIO), velUnit_p("km/s"), waveUnit_p("mm"), unit_p(Unit("Hz")), axisName_p("Frequency"), formatUnit_p("") { AlwaysAssert(restFrequency>=0.0, AipsError); restfreqs_p.resize(1); restfreqs_p(0) = max(0.0, restFrequency); _setTabulatedFrequencies(freqs); to_hz_p = 1.0; to_m_p = 0.001; nativeType_p = SpectralCoordinate::FREQ; makeVelocityMachine( velUnit_p, velType_p, unit_p, type_p, restfreqs_p(restfreqIdx_p) ); wcs_p.flag = -1; // Uninitialized setDefaultWorldMixRanges(); } SpectralCoordinate::SpectralCoordinate( MFrequency::Types type, const Quantum >& freqs, const Quantum& restFrequency ) : Coordinate(), type_p(type), conversionType_p(type_p), restfreqs_p(0), restfreqIdx_p(0), pConversionMachineTo_p(0), pConversionMachineFrom_p(0), pVelocityMachine_p(0), velType_p(MDoppler::RADIO), velUnit_p("km/s"), waveUnit_p("mm"), unit_p(Unit("Hz")), axisName_p("Frequency"), formatUnit_p("") { Unit hz("Hz"); if (!freqs.isConform(hz)) { throw(AipsError("Unit of frequencies is not consistent with Hz")); } if (!restFrequency.isConform(hz)) { throw(AipsError("Unit of rest frequency is not consistent with Hz")); } AlwaysAssert(restFrequency.getValue(hz)>=0.0, AipsError); restfreqs_p.resize(1); restfreqs_p(0) = max(0.0, restFrequency.getValue(hz)); Vector freqs2 = freqs.getValue(hz); _setTabulatedFrequencies(freqs2); to_hz_p = 1.0; to_m_p = 0.001; nativeType_p = SpectralCoordinate::FREQ; makeVelocityMachine( velUnit_p, velType_p, unit_p, type_p, restfreqs_p(restfreqIdx_p) ); wcs_p.flag = -1; // Uninitialized setDefaultWorldMixRanges(); } SpectralCoordinate::SpectralCoordinate( MFrequency::Types freqType, MDoppler::Types velType, const Vector& velocities, const String& velUnit, Double restFrequency ) : Coordinate(), type_p(freqType), conversionType_p(type_p), restfreqs_p(0), restfreqIdx_p(0), pConversionMachineTo_p(0), pConversionMachineFrom_p(0), pVelocityMachine_p(0), velType_p(velType), velUnit_p("km/s"), waveUnit_p("mm"), unit_p("Hz"), axisName_p("Frequency"), formatUnit_p("") { restfreqs_p.resize(1); restfreqs_p(0) = restFrequency; // Convert to frequency makeVelocityMachine( velUnit, velType, String("Hz"), freqType, restFrequency ); Quantum > frequencies = pVelocityMachine_p->makeFrequency(velocities); _setTabulatedFrequencies(frequencies.getValue()); to_hz_p = 1.0; to_m_p = 0.001; if (velType == MDoppler::OPTICAL) { nativeType_p = SpectralCoordinate::VOPT; } else { nativeType_p = SpectralCoordinate::VRAD; } // Now remake Velocity Machine to be consistent with state deleteVelocityMachine(); makeVelocityMachine ( velUnit_p, velType_p, unit_p, type_p, restfreqs_p(restfreqIdx_p) ); wcs_p.flag = -1; // Uninitialized setDefaultWorldMixRanges(); } SpectralCoordinate::SpectralCoordinate( MFrequency::Types freqType, const Vector& wavelengths, const String&waveUnit, Double restFrequency, Bool inAir ) : Coordinate(), type_p(freqType), conversionType_p(type_p), restfreqs_p(0), restfreqIdx_p(0), pConversionMachineTo_p(0), pConversionMachineFrom_p(0), pVelocityMachine_p(0), velType_p(MDoppler::RADIO), velUnit_p("km/s"), waveUnit_p("mm"), unit_p("Hz"), axisName_p("Frequency"), formatUnit_p("") { restfreqs_p.resize(1); restfreqs_p(0) = restFrequency; to_hz_p = 1.; to_m_p = 0.001; // Convert to frequency if(!setWavelengthUnit(waveUnit)){ throw(AipsError("Wavelength unit is not consistent with m")); } Vector frequencies; if(inAir){ airWavelengthToFrequency(frequencies, wavelengths); nativeType_p = SpectralCoordinate::AWAV; } else{ wavelengthToFrequency(frequencies, wavelengths); nativeType_p = SpectralCoordinate::WAVE; } _setTabulatedFrequencies(frequencies); // Now remake Velocity Machine to be consistent with state deleteVelocityMachine(); makeVelocityMachine ( velUnit_p, velType_p, unit_p, type_p, restfreqs_p(restfreqIdx_p) ); wcs_p.flag = -1; // Uninitialized setDefaultWorldMixRanges(); } SpectralCoordinate::SpectralCoordinate (MFrequency::Types type, const ::wcsprm& wcs, Bool oneRel) : Coordinate(), type_p(type), conversionType_p(type_p), restfreqs_p(0), restfreqIdx_p(0), pConversionMachineTo_p(0), pConversionMachineFrom_p(0), pVelocityMachine_p(0), velType_p(MDoppler::RADIO), velUnit_p("km/s"), waveUnit_p("mm"), unit_p(Unit("Hz")), axisName_p("Frequency"), formatUnit_p("") { // Check holds only spectral wcs structure // Copy wcs structure wcs_p.flag = -1; copy_wcs(wcs, wcs_p); set_wcs(wcs_p); to_hz_p = 1.0; to_m_p = 0.001; // Make 0-relative if (oneRel) { wcs_p.crpix[0] -= 1.0; } // Rest frequency restfreqs_p.resize(1); restfreqs_p(0) = max(0.0, wcs.restfrq); nativeType_p = SpectralCoordinate::FREQ; // Velocity machine makeVelocityMachine (velUnit_p, velType_p, unit_p, type_p, restfreqs_p(restfreqIdx_p)); // Set name to something from wcs structure (from ctypes) ? setDefaultWorldMixRanges(); } SpectralCoordinate::SpectralCoordinate(const SpectralCoordinate &other) : Coordinate(other), pConversionMachineTo_p(0), pConversionMachineFrom_p(0), pVelocityMachine_p(0) { wcs_p.flag = -1; // Uninitialized copy(other); } SpectralCoordinate& SpectralCoordinate::operator=(const SpectralCoordinate &other) { if (this != &other) { Coordinate::operator=(other); copy(other); } return *this; } SpectralCoordinate::~SpectralCoordinate() { deleteConversionMachines(); deleteVelocityMachine(); if (wcs_p.flag != -1) { wcsfree (&wcs_p); } } Coordinate::Type SpectralCoordinate::type() const { return Coordinate::SPECTRAL; } String SpectralCoordinate::showType() const { return String("Spectral"); } uInt SpectralCoordinate::nPixelAxes() const { return 1; } uInt SpectralCoordinate::nWorldAxes() const { return 1; } Bool SpectralCoordinate::toWorld (Vector &world, const Vector &pixel, Bool useConversionFrame) const { // Convert to World (Hz) Bool ok = True; if (_tabular) { ok = _tabular->toWorld(world, pixel); if (!ok) set_error(_tabular->errorMessage()); } else { ok = toWorldWCS (world, pixel, wcs_p); } if (!ok) return False; // Convert to correct units from Hz toCurrent(world); // Convert to output reference type if (useConversionFrame) { convertTo(world); } // return ok; } Bool SpectralCoordinate::toWorld(Double& world, const Double& pixel) const { thread_local static Vector pixel_tmp1(1); thread_local static Vector world_tmp1(1); // pixel_tmp1[0] = pixel; if (toWorld(world_tmp1, pixel_tmp1)) { world = world_tmp1[0]; return True; } else { return False; } } Bool SpectralCoordinate::toPixel (Vector &pixel, const Vector &world) const { thread_local static Vector world_tmp1(1); DebugAssert(world.nelements()==1, AipsError); Bool ok = True; // Convert from specified conversion reference type world_tmp1[0] = world[0]; convertFrom(world_tmp1); // Convert from current units to Hz fromCurrent(world_tmp1); // Convert to pixel if (_tabular) { ok = _tabular->toPixel(pixel, world_tmp1); if (!ok) set_error(_tabular->errorMessage()); } else { ok = toPixelWCS (pixel, world_tmp1, wcs_p); } // return ok; } Bool SpectralCoordinate::toPixel(Double& pixel, const Double& world) const { static Vector pixel_tmp2(1); static Vector world_tmp2(1); // world_tmp2[0] = world; if (toPixel(pixel_tmp2, world_tmp2)) { pixel = pixel_tmp2(0); return True; } else { return False; } } Bool SpectralCoordinate::toWorldMany (Matrix& world, const Matrix& pixel, Vector& failures) const { // Convert to world (Hz) Bool ok = True; if (_tabular) { ok = _tabular->toWorldMany(world, pixel, failures); if (!ok) set_error(_tabular->errorMessage()); } else { ok = toWorldManyWCS (world, pixel, failures, wcs_p); } if (!ok) return False; // Convert to current units from wcs units toCurrentMany (world, toCurrentFactors()); // Convert to specified conversion reference type if (pConversionMachineTo_p) convertToMany(world); // return True; } Bool SpectralCoordinate::toPixelMany (Matrix& pixel, const Matrix& world, Vector& failures) const { uInt nWorld = nWorldAxes(); AlwaysAssert(world.nrow()==nWorld, AipsError); // Copy input as we have to convert it to all sorts of things Matrix world2(world.copy()); // Convert from specified conversion reference type if (pConversionMachineTo_p) convertFromMany (world2); // Convert from current units to wcs units (Hz) fromCurrentMany (world2, toCurrentFactors()); // Convert to pixel Bool ok = True; if (_tabular) { _tabular->toPixelMany(pixel, world2, failures); if (!ok) set_error(_tabular->errorMessage()); } else { ok = toPixelManyWCS (pixel, world2, failures, wcs_p); } // return ok; } Vector SpectralCoordinate::worldAxisNames() const { Vector tmp(1); tmp[0] = axisName_p; return tmp; } Vector SpectralCoordinate::worldAxisUnits() const { Vector tmp(1); tmp(0) = unit_p.getName(); return tmp; } Vector SpectralCoordinate::referencePixel() const { if (_tabular) { return _tabular->referencePixel(); } else { Vector crpix(1); crpix[0] = wcs_p.crpix[0]; return crpix; } } Matrix SpectralCoordinate::linearTransform() const { if (_tabular) { return _tabular->linearTransform(); } else { Matrix tmp(1,1); tmp(0,0) = wcs_p.pc[0]; return tmp; } } Vector SpectralCoordinate::increment() const { // Get in Hz Vector value(1); if (_tabular) { value= _tabular->increment(); } else { value[0] = wcs_p.cdelt[0]; } // Convert to current units toCurrent (value); // return value; } Vector SpectralCoordinate::referenceValue() const { // Get in Hz Vector value(1); if (_tabular) { value= _tabular->referenceValue(); } else { value[0] = wcs_p.crval[0]; } // Convert to current units toCurrent (value); // return value; } Bool SpectralCoordinate::setWorldAxisNames(const Vector& names) { Bool ok = (names.nelements()==1); if (!ok) { set_error ("names vector must be of length 1"); } else { axisName_p = names[0]; } return ok; } Bool SpectralCoordinate::setWorldAxisUnits(const Vector& units) { if (!(units.nelements()==1)) { set_error("units vector must be of length 1"); return False; } // Find scale factor to convert old to new String error; Vector factor; Bool ok = find_scale_factor(error, factor, units, worldAxisUnits()); if (ok) { // Set new unit unit_p = Unit(units[0]); // The increment and reference value are *always* stored in the // wcs struct (or TabCoord) in Hz. All we have to do is indicate // that the conversion from current units to Hz has changed to_hz_p /= factor[0]; // Scale rest frequencies restfreqs_p *= factor[0]; // Update Velocity machines pVelocityMachine_p->set(unit_p); if (pConversionMachineTo_p && pConversionMachineTo_p) { pConversionMachineTo_p->set(unit_p); pConversionMachineFrom_p->set(unit_p); } } else { set_error(error); } // return ok; } Bool SpectralCoordinate::setVelocity (const String& velUnit, MDoppler::Types velType) { static const Unit unitsKMS_b(String("km/s")); if (!velUnit.empty()) { Unit unit(velUnit); if (unit!=unitsKMS_b) { set_error("Unit must be empty or consistent with m/s"); return False; } velUnit_p = velUnit; } velType_p = velType; updateVelocityMachine(velUnit_p, velType_p); // return True; } Bool SpectralCoordinate::setWavelengthUnit(const String& waveUnit) { static const Unit unitsM_b(String("m")); String wu = waveUnit; if (wu.empty()) { wu = "mm"; // the default } Unit unit(wu); if (unit!=unitsM_b) { set_error("Unit must be empty or consistent with m"); return False; } String error; Vector factor; Vector outUnit(1,"m"); Vector inUnit(1,wu); if(!find_scale_factor(error, factor, outUnit, inUnit)){ set_error(error); return False; } to_m_p = factor(0); waveUnit_p = wu; return True; } Bool SpectralCoordinate::setNativeType(const SpectralCoordinate::SpecType spcType) { // just copy that over nativeType_p = spcType; // return True; } //static Bool stringtoSpecType(SpecType &specType, const String &stypeString) const; //String SpectralCoordinate::specTypetoString(SpecType specType) Bool SpectralCoordinate::specTypetoString(String &stypeString, const SpecType &specType) { Bool rvalue=True; switch (specType) { case FREQ: stypeString = String("frequency"); break; case VRAD: stypeString = String("radio velocity"); break; case VOPT: stypeString = String("optical velocity"); break; case BETA: stypeString = String("true"); break; case WAVE: stypeString = String("wavelength"); break; case AWAV: stypeString = String("air wavelength"); break; default: rvalue=False; } return rvalue; } //static Bool stringtoSpecType(SpecType &specType, const String &stypeString) const; //SpectralCoordinate::SpecType SpectralCoordinate::stringtoSpecType(String stypeString) Bool SpectralCoordinate::stringtoSpecType(SpecType &specType, const String &stypeString) { if (!stypeString.compare("frequency")){ specType = FREQ; return True; } else if (!stypeString.compare("radio velocity")){ specType = VRAD; return True; } else if (!stypeString.compare("optical velocity")){ specType = VOPT; return True; } else if (!stypeString.compare("true")){ specType = BETA; return True; } else if (!stypeString.compare("wavelength")){ specType = WAVE; return True; } else if (!stypeString.compare("air wavelength")){ specType = AWAV; return True; } else { return False; } // should never get to here return False; } Bool SpectralCoordinate::setReferenceConversion (MFrequency::Types conversionType, const MEpoch& epoch, const MPosition& position, const MDirection& direction) { // See if something to do if (conversionType_p==conversionType) return True; // Int ok = makeConversionMachines(type_p, conversionType, epoch, position, direction); if (ok==-1) { // Trial conversion failed. The machines will be deleted so we must set the // conversion machines back to what they were before this calamity. makeConversionMachines(type_p, conversionType_p, epoch_p, position_p, direction_p); return False; } // conversionType_p = conversionType; epoch_p = epoch; position_p = position; direction_p = direction; // return True; } Bool SpectralCoordinate::setReferencePixel(const Vector &refPix) { if (!(refPix.nelements()==nPixelAxes())) { set_error("reference pixels vector must be of length 1"); return False; } // Bool ok= True; if (_tabular) { ok = _tabular->setReferencePixel(refPix); if (!ok) set_error (_tabular->errorMessage()); } else { // Set WCS card wcs_p.crpix[0] = refPix[0]; // Tell WCS set_wcs(wcs_p); } // return ok; } Bool SpectralCoordinate::setLinearTransform(const Matrix &xform) { Bool ok = (xform.nrow()==1 && xform.ncolumn()==1); if (!ok) { set_error("linear transform matrix has wrong shape"); return False; } // if (_tabular) { ok = _tabular->setLinearTransform(xform); if (!ok) set_error(_tabular->errorMessage()); } else { // Set PC card wcs_p.pc[0] = xform(0,0); set_wcs(wcs_p); } // return ok; } Bool SpectralCoordinate::setIncrement (const Vector& incr) { if (!(incr.nelements()==nWorldAxes())) { set_error("increment vector must be of length 1"); return False; } // Convert to Hz Vector value(incr.copy()); fromCurrent (value); // Now set Bool ok= True; if (_tabular) { ok = _tabular->setIncrement(value); if (!ok) set_error (_tabular->errorMessage()); } else { // Set WCS card wcs_p.cdelt[0] = value[0]; set_wcs(wcs_p); } // return ok; } Bool SpectralCoordinate::setReferenceValue(const Vector& refval) { if (!(refval.nelements()==nWorldAxes())) { set_error("reference value vector must be of length 1"); return False; } // Convert to Hz Vector value(refval.copy()); fromCurrent (value); // Bool ok= True; if (_tabular) { ok = _tabular->setReferenceValue(value); if (!ok) set_error (_tabular->errorMessage()); } else { // Set WCS card wcs_p.crval[0] = value[0]; set_wcs(wcs_p); } // return ok; } Double SpectralCoordinate::restFrequency() const { return restfreqs_p(restfreqIdx_p); } Vector SpectralCoordinate::pixelValues() const { if (_tabular) { return _tabular->pixelValues(); } else { Vector pixels; return pixels; } } Vector SpectralCoordinate::worldValues() const { Vector worlds; if (_tabular) { worlds = _tabular->worldValues(); // Hz toCurrent(worlds); } // return worlds; } MFrequency::Types SpectralCoordinate::frequencySystem(Bool showConversion) const { if (showConversion) { return conversionType_p; } else { return type_p; } } void SpectralCoordinate::setFrequencySystem(MFrequency::Types type, Bool verbose) { if (type==type_p) return; // MFrequency::Types oldType = type_p; type_p = type; deleteVelocityMachine(); makeVelocityMachine (String("km/s"), velType_p, unit_p, type_p, restfreqs_p(restfreqIdx_p)); // The conversion machines are no longer viable. However, it is // is risky to re-create the machines with the new type_p. This // is because the only way to ensure epoch_p, position_p, direction_p // are valid (default construction values are arbitrary) is for // the user to have called setReferenceConversion. Now initially, // conversionType_p = type_p. If the user changes type_p to // something else, and then we remake the machine, that would // use whatever values are in the above Measures, and that // could still be the default values. So better is to turn off // the current conversion, and demand the user re-issues the // setReferenceConversion function if (verbose && oldType != conversionType_p) { LogIO os(LogOrigin("SpectralCoordinate", "setFrequencySystem")); os << LogIO::WARN << "Resetting the conversion frequency system " << MFrequency::showType(conversionType_p) << endl; os << "to the new native frequency system " << MFrequency::showType(type_p) << endl; os << "You must explicitly reset the conversion frequency system if desired" << LogIO::POST; } // deleteConversionMachines(); conversionType_p = type_p; } Bool SpectralCoordinate::transformFrequencySystem(MFrequency::Types type, const MEpoch& epoch, const MPosition& position, const MDirection& direction){ Bool rval=True; MFrequency::Types nativeCtype = frequencySystem(False); if (type != nativeCtype) { MFrequency::Types origType; // original type of the conversion layer MEpoch origEpoch; MPosition origPosition; MDirection origDirection; getReferenceConversion(origType, origEpoch, origPosition, origDirection); // use the reference conversion layer to do the transformation if(origType!=type){ if(!setReferenceConversion(type, epoch, position, direction)){ setReferenceConversion(origType, origEpoch, origPosition, origDirection); return False; } } if(pixelValues().nelements() > 1){ // we have a tabular spectral coordinate Vector oldunits(worldAxisUnits()); Vector tmpunits(1,"Hz"); // need freqs in Hz for setTabulatedFrequencies setWorldAxisUnits(tmpunits); Vector tpixels = _tabular->pixelValues(); Vector newFreqs(tpixels.size()); toWorld(newFreqs, tpixels); _setTabulatedFrequencies(newFreqs); setWorldAxisUnits(oldunits); Vector newCrval(1, newFreqs[0]); setReferenceValue(newCrval); if(tpixels[tpixels.size()-1]-tpixels[0] != 0.){ Vector newCdelt(1, (newFreqs[tpixels.size()-1]-newFreqs[0])/(tpixels[tpixels.size()-1]-tpixels[0])); setIncrement(newCdelt); } Vector newRefPix(1, tpixels[0]); setReferencePixel(newRefPix); } else{ // not tabular: only need to change ctype, crval, cdelt Vector newCrval(1,0.); toWorld(newCrval[0], referencePixel()[0]); Double tmpWorld=0.; toWorld(tmpWorld, referencePixel()[0]+1); Vector newCdelt(1, tmpWorld-newCrval[0]); setReferenceValue(newCrval); setIncrement(newCdelt); } setFrequencySystem(type, False); if(origType!=type){ rval = setReferenceConversion(origType, origEpoch, origPosition, origDirection); } } return rval; } Bool SpectralCoordinate::setRestFrequency(Double newFrequency, Bool append) { newFrequency = max(0.0, newFrequency); if (append) { uInt n = restfreqs_p.nelements(); restfreqs_p.resize(n+1, True); restfreqs_p(n) = newFrequency; restfreqIdx_p = n; } else { restfreqs_p(restfreqIdx_p) = newFrequency; } // Update velocity machine with the active rest frequency Quantum rf(restfreqs_p(restfreqIdx_p), unit_p); pVelocityMachine_p->set(MVFrequency(rf)); // Update wcs struct with the active rest frequency wcs_p.restfrq = rf.getValue(Unit("Hz")); // return True; } void SpectralCoordinate::setRestFrequencies(const Vector& restFrequencies, uInt which, Bool append) { for (uInt i=0; i=0.0, AipsError); } // if (append) { Vector tmp = concatenateArray (restfreqs_p, restFrequencies); restfreqs_p.resize(0); restfreqs_p = tmp; } else { restfreqs_p.resize(0); restfreqs_p = restFrequencies; } // AlwaysAssert(which= 0.0, AipsError); uInt which = 0; Double d, diff = 1.0e99; for (uInt i=0; i rf(restfreqs_p(restfreqIdx_p), unit_p); pVelocityMachine_p->set(MVFrequency(rf)); // Update wcs struct with the active rest frequency wcs_p.restfrq = rf.getValue(Unit("Hz")); } Bool SpectralCoordinate::near(const Coordinate& other, Double tol) const { Vector excludeAxes; return near(other, excludeAxes, tol); } Bool SpectralCoordinate::near(const Coordinate& other, const Vector& excludeAxes, Double tol) const { if (this->type() != other.type()) { set_error("Comparison is not with another SpectralCoordinate"); return False; } // const SpectralCoordinate& sCoord = dynamic_cast(other); // Type if (type_p != sCoord.frequencySystem()) { set_error("The SpectralCoordinates have differing frequency systems"); return False; } // Rest freq if (!casacore::near(restFrequency(), sCoord.restFrequency(), tol)) { set_error("The SpectralCoordinates have differing active rest frequencies"); return False; } // Perhaps we shouldn't check the lists of rest frequencies. // Does it really matter ? const Vector& rfs = sCoord.restFrequencies(); if (restfreqs_p.nelements() != rfs.nelements()) { set_error("The SpectralCoordinates have differing numbers of rest frequencies"); return False; } // for (uInt i=0; i 0) { if (excludeAxes(0)) exclude = True; } // Check names ostringstream oss; if (!exclude) { if (axisName_p != sCoord.axisName_p) { set_error(String("The SpectralCoordinates have differing axis names")); return False; } } // Unit if (unit_p != sCoord.unit_p) { set_error (String("The SpectralCoordinates have differing units")); return False; } // Reference Value { const Vector& thisVal = referenceValue(); const Vector& thatVal = sCoord.referenceValue(); if (!exclude) { if (!casacore::near(thisVal[0],thatVal[0])) { set_error(String("The SpectralCoordinates have differing reference values")); return False; } } } // LinearXForm components //Tabular spectral coordinates with 1 channel has increment 0. by definition in TabularCoordinates ! //so linear transform test is bound to fail so skip for that case if( !(_tabular && (_tabular->pixelValues()).nelements()==1)) { LinearXform thisVal(referencePixel(), increment(), linearTransform()); LinearXform thatVal(sCoord.referencePixel(), sCoord.increment(), sCoord.linearTransform()); if (!(thisVal.near(thatVal, excludeAxes))) { set_error(String("The SpectralCoordinates have differing LinearXform components")); return False; } } // Velocity Stuff if (velType_p != sCoord.velType_p) { set_error("The SpectralCoordinates have differing velocity types"); return False; } if (velUnit_p != sCoord.velUnit_p) { set_error("The SpectralCoordinates have differing velocity units"); return False; } // return True; } Bool SpectralCoordinate::save(RecordInterface &container, const String &fieldName) const { Bool ok = (!container.isDefined(fieldName)); if (ok) { String system = MFrequency::showType(type_p); // Record subrec; subrec.define ("version", 2); // Original unversioned was v 1 ! subrec.define("system", system); subrec.define("restfreq", restFrequency()); subrec.define("restfreqs", restFrequencies()); subrec.define("velType", Int(velType_p)); subrec.define("nativeType", Int(nativeType_p)); subrec.define("velUnit", velUnit_p); subrec.define("waveUnit", waveUnit_p); subrec.define("formatUnit", formatUnit_p); // We may have TC (for tabular coordinates) or not. if (_tabular) { ok = (_tabular->save(subrec, "tabular")); // Always Hz } else { ok = wcsSave (subrec, wcs_p, "wcs"); // Always Hz } if (!ok) return False; // subrec.define("unit", worldAxisUnits()(0)); subrec.define("name", axisName_p); // Conversion machine state String error; Record subrec2; { MeasureHolder mh(direction_p); Record subrec3; mh.toRecord (error, subrec3); subrec2.defineRecord("direction", subrec3); } { MeasureHolder mh(position_p); Record subrec3; mh.toRecord (error, subrec3); subrec2.defineRecord("position", subrec3); } { MeasureHolder mh(epoch_p); Record subrec3; mh.toRecord (error, subrec3); subrec2.defineRecord("epoch", subrec3); } String conversionType = MFrequency::showType(conversionType_p); subrec2.define("system", conversionType); subrec.defineRecord("conversion", subrec2); // container.defineRecord(fieldName, subrec); } return ok; } SpectralCoordinate* SpectralCoordinate::restore(const RecordInterface &container, const String &fieldName) // // The SpectralCOordinate changed from always holding a TabularCoordinate // (which held either a tabular or non-tabular coordinate) to holding a // TC (for tabular coordinates only) or a wcs struct (non-tabular coordinates). // Hence the different code depending on the version of the record // { if (! container.isDefined(fieldName)) { return 0; } Record subrec(container.asRecord(fieldName)); // if (!subrec.isDefined("version")) { return restoreVersion1(subrec); // Original V 1 } else { Int v; subrec.get("version", v); if (v==2) { return restoreVersion2(subrec); // Current V 2 } else { return 0; } } } SpectralCoordinate* SpectralCoordinate::restoreVersion1 (const RecordInterface& subrec) { //cerr << "Enter SC::restoreVersion1" << endl; // We should probably do more type-checking as well as checking // for existence of the fields. if (!subrec.isDefined("system")) { return 0; } // String system; subrec.get("system", system); MFrequency::Types freqSys; if (system == "LSR") { // LSR is perpetuated in old images but is now deprecated in Measures // So we must still read old ones not handled by MFrequency::getType freqSys = MFrequency::LSRK; } else { if (!MFrequency::getType(freqSys, system)) return 0; } // if (!subrec.isDefined("restfreq")) { return 0; } Double restfreq; subrec.get("restfreq", restfreq); // Get TC if (!subrec.isDefined("tabular")) { return 0; } TabularCoordinate* pTabular = TabularCoordinate::restore(subrec, "tabular"); if (pTabular==0) return 0; // Get stuff String unit = pTabular->worldAxisUnits()(0); // Create new SpectralCoordinate (will be in Hz regarldess of unit) SpectralCoordinate* pSpectral = 0; Unit qUnit(unit); Quantum qRestFreq(restfreq, qUnit); const Vector& worlds = pTabular->worldValues(); if (worlds.nelements() > 0) { Quantum > qWorlds(worlds, qUnit); pSpectral = new SpectralCoordinate (freqSys, qWorlds, qRestFreq); // Set units first ! pSpectral->setWorldAxisUnits(pTabular->worldAxisUnits()); pSpectral->setReferencePixel(pTabular->referencePixel()); pSpectral->setReferenceValue(pTabular->referenceValue()); } else { Quantum qcrval(pTabular->referenceValue()(0), qUnit); Quantum qcdelt(pTabular->increment()(0), qUnit); Double crpix(pTabular->referencePixel()(0)); pSpectral = new SpectralCoordinate (freqSys, qcrval, qcdelt, crpix, qRestFreq); pSpectral->setWorldAxisUnits(pTabular->worldAxisUnits()); } AlwaysAssert(pSpectral, AipsError); // Set PC matrix. I can't imagine anyone would really set this // but you never know... pSpectral->setLinearTransform(pTabular->linearTransform()); // Set name pSpectral->setWorldAxisNames(pTabular->worldAxisNames()); delete pTabular; pTabular = 0; // String formatUnit(""); if (subrec.isDefined("formatUnit")) { // optional formatUnit = subrec.asString("formatUnit"); } pSpectral->setFormatUnit(formatUnit); // Velocity restoreVelocity(pSpectral, subrec); // Multiple Rest Frequencies restoreRestFrequencies (pSpectral, subrec, restfreq); // Conversion state restoreConversion (pSpectral, subrec); // return pSpectral; } SpectralCoordinate* SpectralCoordinate::restoreVersion2 (const RecordInterface& subrec) { // cerr << "Enter SC::restoreVersion2" << endl; // We should probably do more type-checking as well as checking // for existence of the fields. if (!subrec.isDefined("system")) { return 0; } // String system; subrec.get("system", system); MFrequency::Types freqSys; // if (system == "LSR") { // LSR is perpetuated in old images but is now deprecated in Measures // So we must still read old ones not handled by MFrequency::getType freqSys = MFrequency::LSRK; } else { if (!MFrequency::getType(freqSys, system)) return 0; } // if (!subrec.isDefined("restfreq")) { return 0; } Double restfreq; subrec.get("restfreq", restfreq); // Get unit String unit; if (!subrec.isDefined("unit")) { return 0; } subrec.get("unit", unit); // Get name String name; if (!subrec.isDefined("name")) { return 0; } subrec.get("name", name); // Create SC from TC or wcs structure Unit qUnit(unit); Quantum qRestFreq(restfreq, qUnit); // SpectralCoordinate* pSpectral = 0; if (subrec.isDefined("tabular")) { // Reconstitute the TC (will be Hz) TabularCoordinate* pTabular = TabularCoordinate::restore(subrec, "tabular"); if (pTabular == 0) return 0; // Create SC (will be in Hz regardless of units) Quantum > qWorlds(pTabular->worldValues(), Unit(pTabular->worldAxisUnits()(0))); pSpectral = new SpectralCoordinate (freqSys, qWorlds, qRestFreq); AlwaysAssert(pSpectral, AipsError); // pSpectral->setReferencePixel(pTabular->referencePixel()); pSpectral->setReferenceValue(pTabular->referenceValue()); // Hz pSpectral->setLinearTransform(pTabular->linearTransform()); delete pTabular; pTabular = 0; } else if (subrec.isDefined("wcs")) { Double crval, crpix, cdelt, pc; String ctype; if (!wcsRestore (crval, crpix, cdelt, pc, ctype, subrec.asRecord("wcs"))) return 0; // Make SC, will be in Hz regardless of units Quantum qcrval(crval, qUnit); Quantum qcdelt(cdelt, qUnit); pSpectral = new SpectralCoordinate (freqSys, qcrval, qcdelt, crpix, qRestFreq); AlwaysAssert(pSpectral, AipsError); // Matrix xform(1,1); xform = pc; pSpectral->setLinearTransform(xform); } else { return 0; } // Now set the actual units which will reset all of the (correct ?) internals Vector tmp(1); tmp[0] = unit; pSpectral->setWorldAxisUnits(tmp); // Name tmp[0] = name; pSpectral->setWorldAxisNames(tmp); // String formatUnit(""); if (subrec.isDefined("formatUnit")) { // optional formatUnit = subrec.asString("formatUnit"); } pSpectral->setFormatUnit(formatUnit); // Set CTYPE. What to do with this I don't know yet... // It is not actually captured in the interface anywhere. // When finally we can make a wcsprm from a funny Spectral // FITS card with CTYPE set have to decide what to do... // Velocity restoreVelocity(pSpectral, subrec); // Multiple Rest Frequencies restoreRestFrequencies (pSpectral, subrec, restfreq); // Conversion state restoreConversion (pSpectral, subrec); // // Wavelength conversion String waveUnit("mm"); if (subrec.isDefined("waveUnit")) { // optional formatUnit = subrec.asString("waveUnit"); } pSpectral->setWavelengthUnit(waveUnit); SpectralCoordinate::SpecType spcType = SpectralCoordinate::FREQ; if (subrec.isDefined("nativeType")) { // optional spcType = static_cast(subrec.asInt("nativeType")); } pSpectral->setNativeType(spcType); return pSpectral; } void SpectralCoordinate::restoreVelocity (SpectralCoordinate*& pSpectral, const RecordInterface& subrec) // // Velocity handling. // { // velType was added after the initial deployment so its optional MDoppler::Types velType=MDoppler::RADIO; // Must match what's defined String velUnit("km/s"); // in Constructors if (subrec.isDefined("velType")) { // optional velType = static_cast(subrec.asInt("velType")); } else if (subrec.isDefined("prefVelType")) { // name changed velType = static_cast(subrec.asInt("prefVelType")); } // if (subrec.isDefined("velUnit")) { // optional velUnit = subrec.asString("velUnit"); } else if (subrec.isDefined("prefVelUnit")) { // name changed velUnit = subrec.asString("prefVelUnit"); } // pSpectral->setVelocity(velUnit, velType); // Updates Velocity Machine } void SpectralCoordinate::restoreRestFrequencies (SpectralCoordinate*& pSpectral, const RecordInterface& subrec, Double restfreq) // // Rest frequency handling // { // Multiple rest frequencies were added after initial deployment if (subrec.isDefined("restfreqs")) { // optional Vector restFreqs(subrec.toArrayDouble("restfreqs")); // Old images might have a negative restfreq. Don't propagate that for (uInt i=0; isetRestFrequencies(restFreqs, 0, False); pSpectral->selectRestFrequency(restfreq); } else { pSpectral->setRestFrequency(restfreq, False); // Updates Velocity Machine } } void SpectralCoordinate::restoreConversion (SpectralCoordinate*& pSpectral, const RecordInterface& subrec) // // Get Conversion state // { // The conversion state was added after initial deployment if (subrec.isDefined("conversion")) { Record subrec2 = subrec.asRecord("conversion"); // String tmp = subrec2.asString("system"); MFrequency::Types conversionFreqSys; if (!MFrequency::getType(conversionFreqSys, tmp)) { conversionFreqSys = pSpectral->frequencySystem(); } // String error; MeasureHolder mhD; if (!mhD.fromRecord(error,subrec2.asRecord("direction"))) { delete pSpectral; throw(AipsError(error)); } // MeasureHolder mhP; if (!mhP.fromRecord(error,subrec2.asRecord("position"))) { delete pSpectral; throw(AipsError(error)); } // MeasureHolder mhE; if (!mhE.fromRecord(error,subrec2.asRecord("epoch"))) { delete pSpectral; throw(AipsError(error)); } // Set the conversion state if (!pSpectral->setReferenceConversion (conversionFreqSys, mhE.asMEpoch(), mhP.asMPosition(), mhD.asMDirection())) { delete pSpectral; throw (AipsError("Failed to set conversion layer state")); } } } Coordinate *SpectralCoordinate::clone() const { return new SpectralCoordinate(*this); } void SpectralCoordinate::toFITS(RecordInterface &header, uInt whichAxis, LogIO &logger, Bool oneRelative, Bool preferVelocity, Bool opticalVelDef, Bool preferWavelength, Bool airWaveDef) const { const Double offset(1.0*Int(oneRelative == True)); logger << LogOrigin("SpectralCoordinate", "toFITS", WHERE); if(preferVelocity && preferWavelength){ throw AipsError("Cannot export spectral axis for velocity AND wavelength. You have to choose one."); } // Verify that the required headers exist and are the right type AlwaysAssert(header.isDefined("ctype") && header.dataType("ctype") == TpArrayString && header.shape("ctype").nelements() == 1 && header.shape("ctype")(0) > Int(whichAxis), AipsError); AlwaysAssert(header.isDefined("crval") && header.dataType("crval") == TpArrayDouble && header.shape("crval").nelements() == 1 && header.shape("crval")(0) > Int(whichAxis), AipsError); AlwaysAssert(header.isDefined("crpix") && header.dataType("crpix") == TpArrayDouble && header.shape("crpix").nelements() == 1 && header.shape("crpix")(0) > Int(whichAxis), AipsError); AlwaysAssert(header.isDefined("cdelt") && header.dataType("cdelt") == TpArrayDouble && header.shape("cdelt").nelements() == 1 && header.shape("cdelt")(0) > Int(whichAxis), AipsError); Vector ctype, cunit; header.get("ctype", ctype); Vector crval(header.toArrayDouble("crval")); Vector crpix(header.toArrayDouble("crpix")); Vector cdelt(header.toArrayDouble("cdelt")); if (header.isDefined("cunit")) { AlwaysAssert(header.dataType("cunit") == TpArrayString && header.shape("cunit").nelements() == 1 && header.shape("cunit")(0) > Int(whichAxis), AipsError); header.get("cunit", cunit); } String Ctype, Cunit, Specsys; Double Crval, Cdelt, Crpix, Altrval, Altrpix; Int Velref; Bool HaveAlt; Double Restfreq = Quantity(restfreqs_p(restfreqIdx_p), // Canonicalize worldAxisUnits()(0)).getBaseValue(); Double RefFreq = Quantity(referenceValue()(0), worldAxisUnits()(0)).getBaseValue(); Double FreqInc = Quantity(increment()(0), worldAxisUnits()(0)).getBaseValue(); Double RefPix = referencePixel()(0) + offset; Double linTrans = linearTransform()(0,0); // always one-dimensional MDoppler::Types VelPreference = opticalVelDef ? MDoppler::OPTICAL : MDoppler::RADIO; // Determine possible changes to RefFreq etc. and check if we are linear in the preferred quantity. // If not, give a warning. // Fill pixel numbers Vector pixel; if (pixelValues().nelements() > 1) { // tabular axis pixel.assign(pixelValues()); Vector vf0, vf1; if(!toWorld(vf0, Vector(1,pixel(0))) || !toWorld(vf1, Vector(1,pixel(1)))){ logger << LogIO::SEVERE << "Error calculating deviations from linear" << errorMessage() << LogIO::POST; } convertFrom(vf0); convertFrom(vf1); RefFreq = vf0(0); // value in Hz in native reference frame FreqInc = vf1(0) - RefFreq; // dto. RefPix = pixel(0) + offset; } else{ uInt nEl = 0; if(header.isDefined("naxis") && header.dataType("naxis") == TpArrayInt && header.shape("naxis").nelements() == 1 && header.shape("naxis")(0) > Int(whichAxis)){ Vector naxis(header.toArrayInt("naxis")); nEl = naxis(whichAxis); } pixel.resize(nEl); for(uInt i=0; i vfx; Double fx; for (uInt i=0; i(1,pixel(i))); if (!ok) { logger << LogIO::SEVERE << "Error calculating deviations " "from linear" << errorMessage() << LogIO::POST; break; } convertFrom(vfx); // to native reference frame fx = vfx(0); // frequencies Double actual = fx; // value in Hz Double linear = RefFreq + FreqInc*(linTrans*pixel(i)-(RefPix-offset)); // also in Hz gridSpacing = FreqInc; if(preferWavelength){ // check if we are linear in wavelength if(actual>0. && RefFreq>0. && (RefFreq+FreqInc)>0.){ actual = C::c/actual; linear = C::c/RefFreq + (C::c/(RefFreq+FreqInc) - C::c/RefFreq)*(linTrans*pixel(i) - (RefPix-offset)); gridSpacing = -(C::c/(RefFreq+FreqInc) - C::c/RefFreq); } else{ logger << LogIO::SEVERE << "Zero or negative frequency." << LogIO::POST; break; } } else if(preferVelocity && opticalVelDef){ // optical velocity if(actual>0. && RefFreq>0.){ Double refVelocity = -C::c * (1.0 - Restfreq / RefFreq); Double velocityIncrement = -C::c * (1.0 - Restfreq / (RefFreq + FreqInc)) - refVelocity; actual = -C::c * (1.0 - Restfreq / actual); linear = refVelocity + velocityIncrement * (linTrans*pixel(i) - (RefPix-offset)); gridSpacing = -velocityIncrement; } else{ logger << LogIO::SEVERE << "Zero or negative frequency." << LogIO::POST; break; } } //else {} // radio velocity or frequency, both linear in frequency if(maxDeviation0. && gridSpacing>0. && maxDeviation/gridSpacing>1E-3) { string sUnit = "Hz"; if(preferWavelength){ sUnit = "m"; } else if(preferVelocity && opticalVelDef){ sUnit = "m/s"; } logger << LogIO::WARN << "Spectral axis is non-linear in the requested output quantity" << endl << "but CASA can presently only write linear axes to FITS." << endl << "In this image, the maximum deviation from linearity is " << maxDeviation << " " << sUnit << endl << " or " << maxDeviation/gridSpacing*100. << "% of the grid spacing." << LogIO::POST; } AlwaysAssert(FITSSpectralUtil::toFITSHeader(Ctype, Crval, Cdelt, Crpix, Cunit, HaveAlt, Altrval, Altrpix, Velref, Restfreq, Specsys, logger, RefFreq, RefPix, FreqInc, type_p, preferVelocity, VelPreference, preferWavelength, airWaveDef), AipsError); ctype(whichAxis) = Ctype; crval(whichAxis) = Crval; crpix(whichAxis) = Crpix; cdelt(whichAxis) = Cdelt; if (cunit.nelements() > 0) { if (Ctype.contains("VELO") || Ctype.contains("FELO")|| Ctype.contains("VRAD")|| Ctype.contains("VOPT")) { cunit(whichAxis) = "m/s"; } else if (Ctype.contains("FREQ")) { cunit(whichAxis) = "Hz"; } else if (Ctype.contains("WAVE")|| Ctype.contains("AWAV")) { cunit(whichAxis) = Cunit; } else { AlwaysAssert(0, AipsError); // NOTREACHED } } if (Restfreq > 0) { header.define("restfrq", Restfreq); // FITS standard v3.0 is RESTFRQ, no longer RESTFREQ header.setComment("restfrq", "Rest Frequency (Hz)"); if(!Specsys.empty()){ header.define("specsys", Specsys); header.setComment("specsys", "Spectral reference frame"); } } if (HaveAlt && !preferWavelength) { // alternate representation not valid for ctype WAVE header.define("altrval", Altrval); header.setComment("altrval", "Alternate frequency reference value"); header.define("altrpix", Altrpix); header.setComment("altrpix", "Alternate frequency reference pixel"); header.define("velref", Velref); header.setComment("velref", "1 LSR, 2 HEL, 3 OBS, +256 Radio"); FITSKeywordUtil::addComment(header, "casacore non-standard usage: 4 LSD, 5 GEO, 6 SOU, 7 GAL"); } // OK, put the primary header information back header.define("ctype", ctype); header.define("crval", crval); header.define("crpix", crpix); header.define("cdelt", cdelt); if (cunit.nelements() > 0) { header.define("cunit", cunit); } } Coordinate* SpectralCoordinate::makeFourierCoordinate (const Vector& axes, const Vector& shape) const // // axes says which axes in the coordinate are to be transformed // shape is the shape of the image for all axes in this coordinate // { if (_tabular) { set_error("Cannot Fourier Transform a non-linear SpectralCoordinate"); return 0; } // if (axes.nelements() != 1) { set_error ("Invalid number of specified axes"); return 0; } if (shape.nelements() != 1) { set_error ("Invalid number of elements in shape"); return 0; } // if (!axes[0]) { set_error ("You have not specified any axes to transform"); return 0; } // const Vector& units = worldAxisUnits(); const Vector& names = worldAxisNames(); // Vector unitsCanon(units.copy()); Vector unitsOut(units.copy()); Vector namesOut(names.copy()); // fourierUnits(namesOut[0], unitsOut[0], unitsCanon[0], Coordinate::SPECTRAL, 0, units[0], names[0]); // Make a copy of ourselves so we can change the units (else we would // need to make this a non-const function) SpectralCoordinate sc(*this); if (!sc.setWorldAxisUnits(unitsCanon)) { set_error ("Could not set world axis units"); return 0; } // Set the Fourier coordinate parameters. This does not yet handle // the pc matrix being anything other than unity... Vector crval(sc.referenceValue().copy()); Vector crpix(sc.referencePixel().copy()); Vector cdelt(sc.increment().copy()); crval[0] = 0.0; cdelt[0] = 1.0 / (shape(0) * cdelt(0)); crpix[0] = Int(shape(0)/2); // Now create the new output LinearCoordinate Matrix pc(1, 1); pc = 0.0; pc.diagonal() = 1.0; return new LinearCoordinate(namesOut, unitsOut, crval, cdelt, pc, crpix); } void SpectralCoordinate::deleteVelocityMachine () { if (pVelocityMachine_p) { delete pVelocityMachine_p; pVelocityMachine_p = 0; } } void SpectralCoordinate::deleteConversionMachines() { if (pConversionMachineTo_p) { delete pConversionMachineTo_p; pConversionMachineTo_p = 0; } // if (pConversionMachineFrom_p) { delete pConversionMachineFrom_p; pConversionMachineFrom_p = 0; } } Bool SpectralCoordinate::setFormatUnit (const String& unit) { const Unit unitHZ(String("Hz")); const Unit unitKMS(String("km/s")); const Unit unitM(String("m")); Unit t(unit); if (t != unitHZ && t != unitKMS && t != unitM) { return False; } // formatUnit_p = unit; return True; } String SpectralCoordinate::format (String& units, Coordinate::formatType format, Double worldValue, uInt worldAxis, Bool isAbsolute, Bool showAsAbsolute, Int precision, Bool usePrecForMixed) const { AlwaysAssert(worldAxis < nWorldAxes(), AipsError); // Check format Coordinate::formatType form = format; checkFormat (form, showAsAbsolute); // Set default precision Int prec = precision; if (prec < 0) getPrecision(prec, form, showAsAbsolute, -1, -1, -1); // If units are empty use formatUnit_p unit. If that's // empty use natuive world unit. // If given units are not consistent with native units // then see if they are velocity. If so, convert to // desired units. static const Unit unitsHZ(String("Hz")); static const Unit unitsKMS_c(String("km/s")); static const Unit unitsM_c(String("m")); static Quantum qVel; // static Quantum qFreq; static Vector vWave; static Vector world; // Use default format unit (which itself may be empty) if empty if (units.empty()) { units = formatUnit_p; } Unit unit(units); // String theString; if (units.empty() || unit == unitsHZ) { // Requested unit is empty or consistent with Hz. theString = Coordinate::format(units, form, worldValue, worldAxis, isAbsolute, showAsAbsolute, precision, usePrecForMixed); } else { // unit not frequency if (unit == unitsKMS_c) { // unit consistent with velocty world.resize(nWorldAxes()); // We must convert to absolute first (regardless of how we want // to see the value) as we are formatting in velocity units if (!isAbsolute) { world = 0.0; world(worldAxis) = worldValue; makeWorldAbsolute(world); worldValue = world(worldAxis); } // if (showAsAbsolute) { if (!frequencyToVelocity (qVel, worldValue)) { theString = "Fail"; return theString; } // Convert from velUnit_p (used in f2v) to desired unit worldValue = qVel.getValue(unit); } else { // Find relative coordinate in km/s consistent units static Vector vel(2), freq2(2); freq2(0) = referenceValue()(worldAxis); freq2(1) = worldValue; if (!frequencyToVelocity(vel, freq2)) { theString = "Fail"; return theString; } // Convert from velUnit_p (used in f2v) to desired unit Quantum t(vel[1]-vel[0], Unit(velUnit_p)); // rel=abs-ref worldValue = t.getValue(unit); } } else{ // unit should be wavelength if (unit != unitsM_c) { throw(AipsError("Requested units must be consistent with km/s, m, or Hz for a SpectralCoordinate")); } // Requested unit is consistent with m world.resize(nWorldAxes()); // new start vWave.resize(nWorldAxes()); // new end // We must convert to absolute first (regardless of how we want // to see the value) as we are formatting in wavelength units if (!isAbsolute) { world = 0.0; world(worldAxis) = worldValue; makeWorldAbsolute(world); worldValue = world(worldAxis); } // new start world = 0.0; vWave = 0.0; world(worldAxis) = worldValue; if (nativeType_p == SpectralCoordinate::AWAV) frequencyToAirWavelength(vWave, world); else frequencyToWavelength(vWave, world); Quantity tmpI=Quantity(vWave(worldAxis), waveUnit_p); worldValue = tmpI.get(unit).getValue(); // new end if (!showAsAbsolute) { // new start // Find relative coordinate in m consistent units world(worldAxis) = referenceValue()(worldAxis); if (nativeType_p == SpectralCoordinate::AWAV) frequencyToAirWavelength(vWave, world); else frequencyToWavelength(vWave, world); Quantity tmpI=Quantity(vWave(worldAxis), waveUnit_p); worldValue = worldValue - tmpI.get(unit).getValue(); // subtract reference // new end } } // end if // ostringstream oss; if (form == Coordinate::MIXED) { oss << worldValue; } else if (form == Coordinate::SCIENTIFIC) { oss.setf(ios::scientific, ios::floatfield); oss.precision(prec); oss << worldValue; } else if (form == Coordinate::FIXED) { oss.setf(ios::fixed, ios::floatfield); oss.precision(prec); oss << worldValue; } theString = String(oss); } // return theString; } void SpectralCoordinate::checkFormat(Coordinate::formatType& format, const Bool ) const { // Absolute or offset is irrelevant if (format != Coordinate::SCIENTIFIC && format != Coordinate::FIXED) format = Coordinate::DEFAULT; // if (format == Coordinate::DEFAULT) format = Coordinate::MIXED; } const Vector& SpectralCoordinate::restFrequencies() const { return restfreqs_p; } String SpectralCoordinate::formatRestFrequencies () const { const Vector& rfs = restFrequencies(); Double rf = restFrequency(); String unit = worldAxisUnits()(0); const uInt n = rfs.nelements(); // if (n==0) return String(""); // It should never be that the active rest frequency is zero // but there is more than one. Zero is often used when making // a continuum SpectralCoordinate where the restfreq is irrelevant ostringstream oss; if (rf > 0.0) { oss << "Rest frequency : " << rf; // if (n > 1) { oss << " ["; uInt j = 0; for (uInt i=0; i 0) oss << ", "; oss << rfs(i); j++; } } oss << "]"; } // oss << " " << unit; } // return String(oss); } void SpectralCoordinate::makeWCS(::wcsprm& wcs, const String& ctype, Double refPix, Double refVal, Double inc, Double pc, Double restFreq) { wcs.flag = -1; init_wcs(wcs, 1); // Fill it in wcs.pc[0] = pc; wcs.crpix[0] = refPix; wcs.cdelt[0] = inc; wcs.crval[0] = refVal; wcs.restfrq = restFreq; strcpy (wcs.ctype[0], ctype.chars()); // Unit currently ignored; Hz assumed /* String unit("Hz"); strcpy (wcs.cunit[0], unit.chars()); */ // Fill in the wcs structure set_wcs(wcs); } Bool SpectralCoordinate::wcsSave (RecordInterface& rec, const ::wcsprm& wcs, const String& fieldName) const // // Save the things that come out of the wcs structure // { Bool ok = (!rec.isDefined(fieldName)); // String ctype(wcs.ctype[0], 9); if (ok) { Record subrec; subrec.define("crval", referenceValue()(0)); subrec.define("crpix", referencePixel()(0)); subrec.define("cdelt", increment()(0)); subrec.define("pc", linearTransform()(0,0)); subrec.define("ctype", ctype); // rec.defineRecord(fieldName, subrec); } return ok; } Bool SpectralCoordinate::wcsRestore (Double& crval, Double& crpix, Double& cdelt, Double& pc, String& ctype, const RecordInterface& rec) { if (rec.isDefined("crval")) { rec.get("crval", crval); } else { return False; } // if (rec.isDefined("crpix")) { rec.get("crpix", crpix); } else { return False; } // if (rec.isDefined("cdelt")) { rec.get("cdelt", cdelt); } else { return False; } // if (rec.isDefined("pc")) { rec.get("pc", pc); } else { return False; } // if (rec.isDefined("ctype")) { rec.get("ctype", ctype); } else { return False; } // return True; } void SpectralCoordinate::toCurrent(Vector& value) const { value /= to_hz_p; } void SpectralCoordinate::fromCurrent(Vector& value) const { value *= to_hz_p; } const Vector SpectralCoordinate::toCurrentFactors () const { Vector t(1); t[0] = 1.0 / to_hz_p; return t; } void SpectralCoordinate::copy (const SpectralCoordinate &other) { type_p = other.type_p; to_hz_p = other.to_hz_p; to_m_p = other.to_m_p; restfreqs_p.resize(0); restfreqs_p = other.restfreqs_p; restfreqIdx_p = other.restfreqIdx_p; // Clean up first if (wcs_p.flag != -1) { wcsfree (&wcs_p); } // Copy TabularCoordinate or wcs structure. Only one of the two // is allocated at any given time. if (other._tabular) { _tabular.reset(new TabularCoordinate(*(other._tabular))); } else { if (_tabular) { _tabular.reset(); } copy_wcs(other.wcs_p, wcs_p); set_wcs(wcs_p); } conversionType_p = other.conversionType_p; direction_p = other.direction_p; position_p = other.position_p; epoch_p = other.epoch_p; velType_p = other.velType_p; velUnit_p = other.velUnit_p; waveUnit_p = other.waveUnit_p; nativeType_p = other.nativeType_p; unit_p = other.unit_p; axisName_p = other.axisName_p; formatUnit_p = other.formatUnit_p; // Machines makeConversionMachines(type_p, conversionType_p, epoch_p, position_p, direction_p); deleteVelocityMachine(); if (other.pVelocityMachine_p) { pVelocityMachine_p = new VelocityMachine(*(other.pVelocityMachine_p)); } } void SpectralCoordinate::_setTabulatedFrequencies(const Vector& freqs) { Vector channels(freqs.nelements()); indgen(channels); _tabular.reset(new TabularCoordinate(channels, freqs, "Hz", "Frequency")); } ostream& SpectralCoordinate::print(ostream& os) const { os << "tabular " << _tabular.get() << endl; os << "to_hz_p " << to_hz_p << endl; os << "to_m_p " << to_m_p << endl; os << "type_p " << MFrequency::showType(type_p) << endl; os << "conversionType_p " << MFrequency::showType(conversionType_p) << endl; os << "restfreqs_p " << restfreqs_p << endl; os << "restfreqIdx_p " << restfreqIdx_p << endl; os << "pConversionMachineTo_p " << pConversionMachineTo_p << endl; os << "pConversionMachineFrom_p " << pConversionMachineFrom_p << endl; os << "pVelocityMachine_p " << pVelocityMachine_p << endl; os << "velType_p " << velType_p << endl; os << "velUnit_p " << velUnit_p << endl; os << "waveUnit_p " << waveUnit_p << endl; os << "nativeType_p " << nativeType_p << endl; os << "unit_p " << unit_p.getName() << endl; os << "increment " << increment() << endl; os << "axisName_p " << axisName_p << endl; os << "formatUnit_p " << formatUnit_p << endl; os << "direction_p " << direction_p << endl; os << "position_p " << position_p << endl; os << "epoch_p " << epoch_p << endl; return os; } Bool SpectralCoordinate::isTabular() const { return static_cast(_tabular); } ostream &operator<<(ostream &os, const SpectralCoordinate& spcoord) { return spcoord.print(os); } } //# NAMESPACE CASACORE - END casacore-3.7.1/coordinates/Coordinates/SpectralCoordinate.h000066400000000000000000000773071476623553700240640ustar00rootroot00000000000000//# SpectralCoordinate.h: Interconvert between pixel and frequency. //# Copyright (C) 1997,1998,1999,2000,2001,2002,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef COORDINATES_SPECTRALCOORDINATE_H #define COORDINATES_SPECTRALCOORDINATE_H #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class TabularCoordinate; class LogIO; class MVFrequency; class VelocityMachine; template class Quantum; // // Interconvert pixel and frequency values. // // // // // // //
      • Coordinate //
      • MFrequency, // MDoppler and // VelocityMachine // classes if you want radial velocities. // // // // This class performs the mapping from pixel to frequency. // This can be done via a Tabular lookup or via an algorithmic // implementation which may be linear or non-linear. The latter // is implemented via the WCS library. // // // // // All pixels coordinates are zero relative. // // // // Let us make a linear SpectralCoordinate first // // Double restfreq = 1.420405752E9; // Double crpix = 10.0; // Double crval = 1.4e9; // Double cdelt = 1.0e6; // SpectralCoordinate sc(MFrequency::TOPO, crval, cdelt, crpix, restfreq); // // Double world, pixel; // pixel = 12.1; // if (!sc.toWorld(world, pixel)) { // cerr << "Error : " << sc.errorMessage() << endl; // } else { // cerr << "pixel, world = " << pixel << ", " << world << endl; // } // // // // // // Now we make a non-linear SpectralCoordinate // // Vector freqs(5); // freqs(0) = 1.4e9; freqs(1) = 1.41e9; // freqs(2) = 1.43e9; freqs(3) = 1.44e9; // freqs(4) = 1.47e9; // SpectralCoordinate sc(MFrequency::LSRK, freqs, restfreq); // // Double world, pixel; // world = 1.42e9; // if (!sc.toPixel(pixel, world)) { // cerr << "Error : " << sc.errorMessage() << endl; // } else { // cerr << "world, pixel = " << world << ", " << pixel << endl; // } // // // // // // Spectral-line astronomy requires a specialized SpectralCoordinate. // // //
      • Allow other than linear interpolations for frequency lookup. // // class SpectralCoordinate : public Coordinate { public: enum SpecType { // taken from the FITS spectral coordinate type codes FREQ, VRAD, VOPT, BETA, WAVE, AWAV }; // Default constructor. It is equivalent to doing // SpectralCoordinate(MFrequency::TOPO, 0.0, 1.0, 0.0) SpectralCoordinate(); // Create a linear frequency axis SpectralCoordinate // f0 is the frequency of the reference pixel, inc is the pixel increment, // refPix is the reference pixel. You can // optionally store the rest frequency for later use in calculating radial // velocities. Use 0 for restFrequency if continuum. // // Frequencies and increments initially in Hz. SpectralCoordinate(MFrequency::Types type, Double f0, Double inc, Double refPix, Double restFrequency = 0.0); // Create linear frequency axis SpectralCoordinate with Quantum-based interface. // Parameters are the same as above. // Regardless of the units of the Quanta, the initial units // of the SpectralCoordinate will be Hz. You can change it to // something else with the setWorldAxisUnits method later if you want. // Use 0 for restFrequency if continuum. SpectralCoordinate(MFrequency::Types type, const Quantum& f0, const Quantum& inc, Double refPix, const Quantum& restFrequency = Quantum(0.0,"Hz")); // Construct a SpectralCoordinate with the specified frequencies (in Hz). // This axis can be nonlinear; the increments and related // functions return the average values // (calculated from the first and last pixel's frequencies). // // A linear interpolation/extrapolation is used for pixels which are // not supplied. The reference pixel is chosen to be 0. // The frequencies must increase or decrease monotonically (otherwise // the toPixel lookup would not be possible). // Use 0 for restFrequency if continuum. SpectralCoordinate(MFrequency::Types type, const Vector &freqs, Double restFrequency = 0.0); // Construct a SpectralCoordinate with the specified frequencies // with Quantum-based interface. // Parameters are the same as above. // Regardless of the units of the Quanta, the initial units // of the SpectralCoordinate will be Hz. // Use 0 for restFrequency if continuum. SpectralCoordinate(MFrequency::Types type, const Quantum >& freqs, const Quantum& restFrequency = Quantum(0.0,"Hz")); // Construct a SpectralCoordinate with the specified velocities (in km/s). // They will be converted to Hz and the SpectralCoordinate constructed. // This axis can be nonlinear; the increments and related // functions return the average values // (calculated from the first and last pixel's frequencies). // // A linear interpolation/extrapolation is used for pixels which are // not supplied. The reference pixel is chosen to be 0. // The velocities must increase or decrease monotonically (otherwise // the toPixel lookup would not be possible). SpectralCoordinate(MFrequency::Types freqType, MDoppler::Types velType, const Vector& velocities, const String& velUnit, Double restFrequency = 0.0); // Construct a SpectralCoordinate with the specified wavelengths (in mm). // They will be converted to Hz and the SpectralCoordinate constructed. // This axis can be nonlinear; the increments and related // functions return the average values // (calculated from the first and last pixel's frequencies). // If inAir is True, the input wavelengths are assumed to be Air Wavelengths. // They are converted to vacuum frequency using the refractive index // which is calculated based on the mean input air wavelength. // // A linear interpolation/extrapolation is used for pixels which are // not supplied. The reference pixel is chosen to be 0. // The wavelengths must increase or decrease monotonically (otherwise // the toPixel lookup would not be possible). SpectralCoordinate(MFrequency::Types freqType, const Vector& wavelengths, const String& waveUnit, Double restFrequency = 0.0, Bool inAir = False); // Construct from wcs structure. Must hold only a spectral wcs structure // Specify whether the absolute pixel coordinates in the wcs structure // are 0- or 1-relative. The coordinate is always constructed with 0-relative // pixel coordinates SpectralCoordinate(MFrequency::Types freqType, const ::wcsprm& wcs, Bool oneRel=True); // Copy constructor (copy semantics). SpectralCoordinate(const SpectralCoordinate &other); // Assignment (copy semantics). SpectralCoordinate &operator=(const SpectralCoordinate &other); // Destructor. virtual ~SpectralCoordinate(); // Always returns Coordinate::SPECTRAL. virtual Coordinate::Type type() const; // Always returns the String "Spectral". virtual String showType() const; // Always returns 1. // virtual uInt nPixelAxes() const; virtual uInt nWorldAxes() const; // // Set extra conversion layer. Whenever a conversion from pixel to world is done, // the world value is then further converted to this MFrequency::Types value. // For example, your SpectralCoordinate may be defined in LSRK. // You can use this to get the world values out in say BARY. You must // specify the position on earth, the epoch and the direction for the conversions // and it is your responsibility to ensure they are viable. // Similarly, whenever you convert from world to pixel, the world // value is assumed to be that appropriate to the setReferenceConversion type. // It is first converted to the MFrequency::Types with which the // SpectralCoordinate was constructed and from there to pixel. // If you don't call this function, or you set the same type // for which the SpectralCoordinate was constructed, no extra // conversions occur. Some conversions will fail. These are the // ones that require extra frame information (radial velocity) such // as to REST. This will be added later. In this case this function // returns False (and the conversion parameters are all left as they were), // else it returns True. // Bool setReferenceConversion (MFrequency::Types type, const MEpoch& epoch, const MPosition& position, const MDirection& direction); void getReferenceConversion (MFrequency::Types& type, MEpoch& epoch, MPosition& position, MDirection& direction) const {type = conversionType_p; epoch=epoch_p; position=position_p; direction=direction_p;}; // // Convert a pixel to a world coordinate or vice versa. Returns True // if the conversion succeeds, otherwise it returns False and // errorMessage() contains an error message. The input vectors // must be of length one and the output vectors are resized if they are not // already of length one. // if useConversionFrame, if the coordinate has a conversion // layer frame, it is used. Else, the native frame is used for the conversion. // virtual Bool toWorld(Vector &world, const Vector &pixel, Bool useConversionFrame=True) const; virtual Bool toPixel(Vector &pixel, const Vector &world) const; Bool toWorld(Double& world, const Double& pixel) const; Bool toPixel(Double& pixel, const Double& world) const; // // Convert a pixel (channel number) into an MFrequency or MVFrequency and vice // versa. Usually you will do // this for calculating velocities or converting frequencies from one frame // to another. // Bool toWorld(MFrequency &world, Double pixel) const; Bool toPixel(Double& pixel, const MFrequency &world) const; Bool toWorld(MVFrequency &world, Double pixel) const; Bool toPixel(Double& pixel, const MVFrequency &world) const; // // Batch up a lot of transformations. The first (most rapidly varying) axis // of the matrices contain the coordinates. Returns False if any conversion // failed and errorMessage() will hold a message. // The failures array (True for fail, False for success) // is the length of the number of conversions and // holds an error status for each conversion. // virtual Bool toWorldMany(Matrix& world, const Matrix& pixel, Vector& failures) const; virtual Bool toPixelMany(Matrix& pixel, const Matrix& world, Vector& failures) const; // // Set the state that is used for conversions from pixel and frequency to velocity // or wavelength. The SpectralCoordinate is constructed with // MDoppler::RADIO and km/s as the velocity conversion state // and mm as the wavelength conversion state. // The functions in this class which use this state are those that convert // to or from velocity. Also, function format uses the Doppler // state set here. If the function returns False it means the unit was // not valid. There will be an error message in function errorMessage // Bool setVelocity (const String& velUnit=String("km/s"), MDoppler::Types velType=MDoppler::RADIO); MDoppler::Types velocityDoppler () const {return velType_p;}; String velocityUnit () const {return velUnit_p;}; // Bool setWavelengthUnit (const String& waveUnit=String("mm")); String wavelengthUnit () const {return waveUnit_p;}; // Bool setNativeType (const SpectralCoordinate::SpecType spcType); SpectralCoordinate::SpecType nativeType() const {return nativeType_p;} // // Functions to convert to velocity (uses the current active // rest frequency) or wavelength. There is no reference frame // change but you can specify the velocity Doppler and the output // units of the velocity with function setVelocity // or setWavelength respectively. When the input is a frequency stored // as a Double it must be in the current units of the SpectralCoordinate. // // Note that the extra conversion layer (see function setReferenceConversion) // is active in the pixelToVelocity functions (because internally // the use toWorld) but not in the frequencyToVelocity // or frequencyToWavelength functions. // Bool pixelToVelocity (Quantum& velocity, Double pixel) const; Bool pixelToVelocity (Double& velocity, Double pixel) const; Bool pixelToVelocity (Vector& velocity, const Vector& pixel) const; // Bool frequencyToVelocity (Quantum& velocity, Double frequency) const; Bool frequencyToVelocity (Quantum& velocity, const MFrequency& frequency) const; Bool frequencyToVelocity (Quantum& velocity, const MVFrequency& frequency) const; Bool frequencyToVelocity (Double& velocity, Double frequency) const; Bool frequencyToVelocity (Vector& velocity, const Vector& frequency) const; // Bool frequencyToWavelength (Vector& wavelength, const Vector& frequency) const; Bool frequencyToAirWavelength (Vector& wavelength, const Vector& frequency) const; // The refractive index of air (argument can be wavelength or airwavelength) // according to Greisen et al., 2006, A&A, 464, 746. // If airwavelength is used there is an error of the order of 1E-9. // Argument must be in micrometers! //static Double refractiveIndex(const Double& lambda_um); // // Functions to convert from velocity (uses the current active // rest frequency) or wavelength. There is no reference frame // change but you can specify the velocity Doppler and the output // units of the velocity with function setVelocity // and those of the wavelength with setWavelength. // When the input is a frequency stored // as a Double it must be in the current units of the SpectralCoordinate. // // Note that the extra conversion layer (see function setReferenceConversion) // is active in the pixelToVelocity functions (because internally // the use toPixel) but not in the frequencyToVelocity functions. // Bool velocityToPixel (Double& pixel, Double velocity) const; Bool velocityToPixel (Vector& pixel, const Vector& velocity) const; // Bool velocityToFrequency (Double& frequency, Double velocity) const; Bool velocityToFrequency (Vector& frequency, const Vector& velocity) const; // Bool wavelengthToFrequency (Vector& frequency, const Vector& wavelength) const; Bool airWavelengthToFrequency (Vector& frequency, const Vector& wavelength) const; // // The SpectralCoordinate can maintain a list of rest frequencies // (e.g. multiple lines within a band). However, only // one of them is active (e.g. for velocity conversions) at any // one time. Function restFrequency returns that // frequency. Function restFrequencies returns // all of the possible restfrequencies. // // When you construct the SpectralCoordinate, you give it one rest frequency // and it is the active one. Thereafter you can add a new restfrequency // with function setRestFrequency (append=True) and // that frequency will become the active one. With this function // and append=False, the current active restfrequency will // be replaced by the one you give. // // You can change the list of // restfrequencies with function setRestFrequencies. When // you do so, you can either replace the list of rest frequencies or append to it. // You specify which frequency of the new (appended) list // becomes active. // // You can also select the active rest frequency either by an index into // the current list (exception if out of range) given by // restFrequencies() or by the value in the list // nearest to the frequency you give. // // Whenever you change the active rest frequency, the class internals // are adjusted (e.g. the velocity machine is updated). // Double restFrequency() const; const Vector& restFrequencies() const; Bool setRestFrequency(Double newFrequency, Bool append=False); void setRestFrequencies(const Vector& newFrequencies, uInt which=0, Bool append=False); void selectRestFrequency(uInt which); void selectRestFrequency(Double frequency); String formatRestFrequencies () const; // // Retrieve/set the frequency system. Note that setting the // frequency system just changes the internal value of the // frequency system. In addition, it will reset the internal // conversion frequency system to the new type and delete any // conversion machines. // MFrequency::Types frequencySystem(Bool showConversion=False) const; void setFrequencySystem(MFrequency::Types type, Bool verbose=True); // Transform the SpectralCoordinate to a different native reference frame // keeping the conversion layer as is Bool transformFrequencySystem(MFrequency::Types type, const MEpoch& epoch, const MPosition& position, const MDirection& direction); // // Report the value of the requested attribute. // virtual Vector worldAxisNames() const; virtual Vector referencePixel() const; virtual Matrix linearTransform() const; virtual Vector increment() const; virtual Vector referenceValue() const; // // Set the value of the requested attribute. Note that these just // change the internal values, they do not cause any recomputation. // virtual Bool setWorldAxisNames(const Vector &names); virtual Bool setReferencePixel(const Vector &refPix); virtual Bool setLinearTransform(const Matrix &xform); virtual Bool setIncrement(const Vector &inc) ; virtual Bool setReferenceValue(const Vector &refval); // // Get the table, i.e. the pixel and world values. The length of these // Vectors will be zero if this axis is pure linear (i.e. if the // channel and frequencies are related through an increment and offset). // Vector pixelValues() const; Vector worldValues() const; // // Set/get the unit. Adjust the increment and // reference value by the ratio of the old and new units. // The unit must be compatible with frequency. // virtual Bool setWorldAxisUnits(const Vector &units); virtual Vector worldAxisUnits() const; // // Comparison function. Any private Double data members are compared // with the specified fractional tolerance. Don't compare on the specified // axes in the Coordinate. If the comparison returns False, // errorMessage() contains a message about why. // virtual Bool near(const Coordinate& other, Double tol=1e-6) const; virtual Bool near(const Coordinate& other, const Vector& excludeAxes, Double tol=1e-6) const; // // Find the Coordinate for when we Fourier Transform ourselves. This pointer // must be deleted by the caller. Axes specifies which axes of the Coordinate // you wish to transform. Shape specifies the shape of the image // associated with all the axes of the Coordinate. Currently the // output reference pixel is always shape/2. Cannot transform tabular // coordinates. If the pointer returned is 0, it failed with a message // in errorMessage virtual Coordinate* makeFourierCoordinate (const Vector& axes, const Vector& shape) const; // Format a SpectralCoordinate coordinate world value nicely through the // common format interface. See Coordinate // for basics. // // Format types SCIENTIFIC, FIXED, MIXED and DEFAULT are supported. // DEFAULT will use MIXED. // // The world value must always be given in native frequency units. // Use argument unit to determine what it will be // converted to for formatting. If unit is given, it // must be dimensionally consistent with Hz, m, or m/s. // If you give a unit consistent with m/s then the // appropriate velocity Doppler type is taken from that set by // function setVelocity. There is no frame conversion. // If unit is empty, the unit given by setFormatUnit // is used. If this is turn empty, then native units are used. virtual String format(String& unit, Coordinate::formatType format, Double worldValue, uInt worldAxis, Bool isAbsolute=True, Bool showAsAbsolute=True, Int precision=-1, Bool usePrecForFixed=False) const; // Set the default formatter unit (which is initialized to empty). Must // be consistent with Hz or km/s. // If the given unit is illegal, False is returned and the internal state unchanged. // This unit is used by the function format when the given // unit is empty. // String formatUnit () const {return formatUnit_p;} Bool setFormatUnit (const String& unit); // // Convert to FITS header record. When writing the FITS record, // the fields "ctype, crval, crpix", and "cdelt" must already be created. Other header // words are created as needed. Use oneRelative=True to // convert zero-relative SpectralCoordinate pixel coordinates to // one-relative FITS coordinates, and vice-versa. If preferVelocity=True // the primary axis type will be velocity, if preferWavelength=True it will // be wavelength, else frequency. For a velocity axis, if opticalVelDef=False, // the radio velocity definition will be used, else optical definition. Similarly for a // wavelength axis, if airWaveDef=True air wavelength will be used, the // default is vacuum wavelength. // void toFITS(RecordInterface &header, uInt whichAxis, LogIO &logger, Bool oneRelative=True, Bool preferVelocity=True, Bool opticalVelDef=True, Bool preferWavelength=False, Bool airWaveDef=False) const; // Old interface. Handled by wcs in new interface in FITSCoordinateUtil.cc // static Bool fromFITSOld(SpectralCoordinate &out, String &error, // const RecordInterface &header, // uInt whichAxis, // LogIO &logger, Bool oneRelative=True); // // Save the SpectralCoordinate into the supplied record using the supplied field name. // The field must not exist, otherwise False is returned. virtual Bool save(RecordInterface &container, const String &fieldName) const; // Recover the SpectralCoordinate from a record. // A null pointer means that the restoration did not succeed. static SpectralCoordinate* restore(const RecordInterface &container, const String &fieldName); // Convert from String to spectral type and vice versa. static Bool specTypetoString(String &stypeString, const SpecType &specType); static Bool stringtoSpecType(SpecType &specType, const String &stypeString); // Make a copy of the SpectralCoordinate using new. The caller is responsible for calling // delete. virtual Coordinate* clone() const; ostream& print(ostream& os) const; // is this a tabular coordinate? Bool isTabular() const; private: std::unique_ptr _tabular; // Tabular coordinate OR mutable ::wcsprm wcs_p; // wcs structure is used Double to_hz_p; // Convert from current world units to Hz Double to_m_p; // Convert from current wavelength units to m // MFrequency::Types type_p, conversionType_p; // Frequency system and conversion system Vector restfreqs_p; // List of possible rest frequencies uInt restfreqIdx_p; // Current active rest frequency index // Conversion machines; for pixel<->world conversions only. mutable MFrequency::Convert* pConversionMachineTo_p; // For type_p -> conversionType_p mutable MFrequency::Convert* pConversionMachineFrom_p; // For conversionType_p -> type_p VelocityMachine* pVelocityMachine_p; // The velocity machine does all conversions between world & velocity. MDoppler::Types velType_p; // Velocity Doppler String velUnit_p; // Velocity unit // String waveUnit_p; // Wavelength unit for conversions between world & wavelength SpectralCoordinate::SpecType nativeType_p; // The native spectral type // Unit unit_p; // World axis unit String axisName_p; // The axis name String formatUnit_p; // The default unit for the format function // MDirection direction_p; // These are a part of the frame set for MPosition position_p; // the reference conversions machines MEpoch epoch_p; // They are only private so we can save their state // Format checker void checkFormat(Coordinate::formatType& format, const Bool ) const; // Copy private data void copy (const SpectralCoordinate& other); // Convert to and from conversion reference type virtual void convertTo (Vector& world) const; virtual void convertFrom (Vector& world) const; // Deletes and sets pointer to 0 void deleteVelocityMachine (); // Deletes and sets pointers to 0 void deleteConversionMachines (); // Set up pixel<->world conversion machines // Returns: 3 (machines were noOPs, machines deleted) // 2 (types the same, machines deleted), // 1 (machines created and functioning) // -1 (machines could not make trial conversion, machines deleted) Int makeConversionMachines (MFrequency::Types type, MFrequency::Types conversionType, const MEpoch& epoch, const MPosition& position, const MDirection& direction); // Create velocity<->frequency machine void makeVelocityMachine (const String& velUnit, MDoppler::Types velType, const Unit& freqUnit, MFrequency::Types freqType, Double restFreq); // Make spectral wcs structure (items in Hz) static void makeWCS(wcsprm& wcs, const String& ctype, Double refPix, Double refVal, Double inc, Double pc, Double restFreq); // Record restoration handling // static SpectralCoordinate* restoreVersion1 (const RecordInterface& container); static SpectralCoordinate* restoreVersion2 (const RecordInterface& container); static void restoreVelocity (SpectralCoordinate*& pSpectral, const RecordInterface& subrec); static void restoreRestFrequencies (SpectralCoordinate*& pSpectral, const RecordInterface& subrec, Double restFreq); static void restoreConversion (SpectralCoordinate*& pSpectral, const RecordInterface& subrec); // // Interconvert between the current units and wcs units (Hz) // void toCurrent(Vector& value) const; void fromCurrent(Vector& value) const; // // Return unit conversion vector for converting to current units const Vector toCurrentFactors () const; // Update Velocity Machine void updateVelocityMachine (const String& velUnit, MDoppler::Types velType); // Restore wcs stuff from Record static Bool wcsRestore (Double& crval, Double& crpix, Double& cdelt, Double& pc, String& ctype, const RecordInterface& rec); // Save wcs stuff into Record Bool wcsSave (RecordInterface& rec, const wcsprm& wcs, const String& fieldName) const; void _setTabulatedFrequencies(const Vector& freqs); }; ostream &operator<<(ostream &os, const SpectralCoordinate& spcoord); } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/coordinates/Coordinates/StokesCoordinate.cc000066400000000000000000000365541476623553700237140ustar00rootroot00000000000000//# StokesCoordinate.cc: this defines StokesCoordinate which shoe-horns Stokes axes into a Coordinate //# Copyright (C) 1997,1998,1999,2000,2001,2002,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN StokesCoordinate::StokesCoordinate(const Vector &whichStokes) : Coordinate(), values_p(whichStokes.nelements()), crval_p(0), crpix_p(0), matrix_p(1), cdelt_p(1), name_p("Stokes"), unit_p("") { setStokes(whichStokes); nValues_p = values_p.nelements(); setDefaultWorldMixRanges(); } StokesCoordinate::StokesCoordinate(const StokesCoordinate &other) : Coordinate(other), values_p(other.values_p), crval_p(other.crval_p), crpix_p(other.crpix_p), matrix_p(other.matrix_p), cdelt_p(other.cdelt_p), name_p(other.name_p), unit_p(other.unit_p), nValues_p(other.nValues_p) { setDefaultWorldMixRanges(); } StokesCoordinate &StokesCoordinate::operator=(const StokesCoordinate &other) { if (this != &other) { Coordinate::operator=(other); values_p = other.values_p; crval_p = other.crval_p; crpix_p = other.crpix_p; matrix_p = other.matrix_p; cdelt_p = other.cdelt_p; name_p = other.name_p; unit_p = other.unit_p; nValues_p = other.nValues_p; } return *this; } StokesCoordinate::~StokesCoordinate() {} Coordinate::Type StokesCoordinate::type() const { return Coordinate::STOKES; } String StokesCoordinate::showType() const { return String("Stokes"); } uInt StokesCoordinate::nPixelAxes() const { return 1; } uInt StokesCoordinate::nWorldAxes() const { return 1; } Bool StokesCoordinate::toWorld(Stokes::StokesTypes &stokes, Int pixel) const { Double world; if (toWorld (world, static_cast(pixel))) { stokes = Stokes::type(values_p[pixel]); return True; } return False; } Bool StokesCoordinate::toPixel(Int& pixel, Stokes::StokesTypes stokes) const { Double tmp; if (toPixel(tmp, static_cast(stokes))) { pixel = Int(tmp + 0.5); return True; } return False; } Bool StokesCoordinate::toWorld(Vector& world, const Vector& pixel, Bool) const { DebugAssert(pixel.nelements()==1, AipsError); world.resize(1); // Double tmp; if (toWorld(tmp, pixel(0))) { world(0) = tmp; return True; } return False; } Bool StokesCoordinate::toPixel(Vector &pixel, const Vector &world) const { DebugAssert(world.nelements()==1, AipsError); pixel.resize(1); // Double tmp; if (toPixel(tmp, world(0))) { pixel(0) = tmp; return True; } return False; } Double StokesCoordinate::toWorld (Stokes::StokesTypes stokes) { return static_cast(stokes); } Stokes::StokesTypes StokesCoordinate::toWorld (Double world) { Int i = Int(world + 0.5); if (i < 0 || i>=Stokes::NumberOfTypes) { return Stokes::Undefined; } // return static_cast(i); } Vector StokesCoordinate::stokes() const { return Vector(values_p.begin(), values_p.end()); } Vector StokesCoordinate::stokesStrings() const { uInt n = values_p.size(); Vector ret(n); for (uInt i=0; i &whichStokes) { AlwaysAssert(whichStokes.nelements()>0, AipsError); // Make sure the stokes occur at most once Block alreadyUsed(Stokes::NumberOfTypes); alreadyUsed = False; for (uInt i=0; i StokesCoordinate::worldAxisNames() const { Vector names(1); names = name_p; return names; } Vector StokesCoordinate::worldAxisUnits() const { Vector units(1); units = unit_p; return units; } Vector StokesCoordinate::referencePixel() const { Vector crpix(1); crpix = crpix_p; return crpix; } Matrix StokesCoordinate::linearTransform() const { Matrix matrix(1,1); matrix(0,0) = matrix_p; return matrix; } Vector StokesCoordinate::increment() const { Vector cdelt(1); cdelt = cdelt_p; return cdelt; } Vector StokesCoordinate::referenceValue() const { Vector crval(1); crval = crval_p; return crval; } Bool StokesCoordinate::setWorldAxisNames(const Vector &names) { Bool ok = names.nelements()==1; if (!ok) { set_error ("names vector must be of length 1"); } else { name_p = names(0); } return ok; } Bool StokesCoordinate::setWorldAxisUnits(const Vector &) { return True; } Bool StokesCoordinate::setReferencePixel(const Vector &) { return True; } Bool StokesCoordinate::setLinearTransform(const Matrix &) { return True; } Bool StokesCoordinate::setIncrement(const Vector &) { return True; } Bool StokesCoordinate::setReferenceValue(const Vector &) { return True; } Bool StokesCoordinate::near(const Coordinate& other, Double tol) const { Vector excludeAxes; return near(other, excludeAxes, tol); } Bool StokesCoordinate::near(const Coordinate& other, const Vector& excludeAxes, Double) const { if (other.type() != this->type()) { set_error("Comparison is not with another StokesCoordinate"); return False; } // Check name const StokesCoordinate& sCoord = dynamic_cast(other); if (name_p != sCoord.name_p) { set_error("The StokesCoordinates have differing world axis names"); return False; } // Number of pixel and world axes is the same for a StokesCoordinate // and it always 1. SO if excludeAxes contains "0" we are done. // Add an assertion check should this change Bool found; if (linearSearch(found, excludeAxes, 0, excludeAxes.nelements()) >= 0) return True; // The only other thing that really matters in the STokesCoordinate // is the values along the axis. Nothing else (e.g. crval_p etc) // is ever actually used. if (nValues_p != sCoord.nValues_p) { set_error("The StokesCoordinates have different numbers of Stokes values"); return False; } // Conformance testing usually verifies aspects of the Coordinate // such as refval, refpix etc. Because the StokesCoordinate // violates that model, and all the matters are the actual Stokes // values, it is a bit hard to know what to test. E.g. I make // a LatticeExprNode from complex(q,u). The conformance check // comes in here and will fail if I test actual values on the // Stokes axis. Until I know what to do better, comment it out. /* for (Int i=0; i&, const Vector&, Double) const { if (other.type() != Coordinate::STOKES) { set_error("Other Coordinate type is not Stokes"); return False; } // // The only other thing that really matters in the STokesCoordinate // is the values along the axis. Nothing else (e.g. crval_p etc) // is ever actually used. However, if we check these, we getinto // trouble for things like ImageExpr making P from Q and U say // (i.e. the two coordinates have differnet values). So we // simply test that the number of stokes is the same and that // is that // const StokesCoordinate& sCoord = dynamic_cast(other); if (nValues_p != sCoord.nValues_p) { set_error("The StokesCoordinates have different numbers of Stokes values"); return False; } // return True; } Bool StokesCoordinate::save(RecordInterface &container, const String &fieldName) const { Bool ok = !container.isDefined(fieldName); if (ok) { Record subrec; subrec.define("axes", worldAxisNames()); // Vector stokes(nValues_p); for (Int i=0; i axes; subrec.get("axes", axes); // if (!subrec.isDefined("stokes")) { return 0; } Vector stokes; subrec.get("stokes", stokes); Vector istokes(stokes.nelements()); for (uInt i=0; i crval(subrec.toArrayDouble("crval")); if (!subrec.isDefined("crpix")) { return 0; } Vector crpix(subrec.toArrayDouble("crpix")); if (!subrec.isDefined("cdelt")) { return 0; } Vector cdelt(subrec.toArrayDouble("cdelt")); if (!subrec.isDefined("pc")) { return 0; } Matrix pc(subrec.toArrayDouble("pc")); */ StokesCoordinate* retval = new StokesCoordinate(istokes); AlwaysAssert(retval, AipsError); retval->setWorldAxisNames(axes); AlwaysAssert(retval, AipsError); // // retval->setWorldAxisUnits(units); // It never makes sense to set the units // /* retval-> setIncrement(cdelt); retval->setReferenceValue(crval); retval->setReferencePixel(crpix); // */ // return retval; } Coordinate *StokesCoordinate::clone() const { return new StokesCoordinate(*this); } String StokesCoordinate::format(String& units, Coordinate::formatType, Double worldValue, uInt worldAxis, Bool, Bool, Int, Bool) const // // world abs=rel for Stokes // { units = worldAxisUnits()(worldAxis); return Stokes::name(StokesCoordinate::toWorld (worldValue)); } void StokesCoordinate::makePixelRelative (Vector& pixel) const // // rel = abs - ref // { DebugAssert(pixel.nelements()==1, AipsError); // Int index = Int(pixel(0) + 0.5); if (index >= 0 && index < nValues_p) { pixel -= referencePixel(); } else { ostringstream os; os << "Absolute pixel " << index << " is out of range [0.." << nValues_p-1 << "]"; String s(os); throw(AipsError(s)); } } void StokesCoordinate::makePixelAbsolute (Vector& pixel) const // // abs = rel + ref // { DebugAssert(pixel.nelements()==1, AipsError); pixel += referencePixel(); // Int index = Int(pixel(0) + 0.5); if (index < 0 || index >= nValues_p) { ostringstream os; os << "Absolute pixel " << index << " is out of range [0.." << nValues_p-1 << "]"; String s(os); throw(AipsError(s)); } } void StokesCoordinate::makeWorldRelative (Vector&) const // // By definition, for StokesCoordinate, world abs = rel // { } void StokesCoordinate::makeWorldAbsolute (Vector&) const // // By definition, for StokesCoordinate, world abs = rel // {} // Private functions Bool StokesCoordinate::toWorld(Double& world, const Double pixel) const { Int index = Int(pixel + 0.5); if (index >= 0 && index < nValues_p) { world = values_p[index]; return True; } else { ostringstream os; os << "Pixel " << index << " is out of range [0.." << nValues_p-1 << "]"; set_error(os); return False; } } Bool StokesCoordinate::toPixel(Double& pixel, const Double world) const { Bool found = False; Int index; for (index=0; index pixel(nPixelAxes()); pixel(0) = 0; toWorld(worldMin_p, pixel); pixel(0) = nValues_p - 1; toWorld(worldMax_p, pixel); } } //# NAMESPACE CASACORE - END casacore-3.7.1/coordinates/Coordinates/StokesCoordinate.h000066400000000000000000000270651476623553700235530ustar00rootroot00000000000000//# StokesCoordinate.h: Interconvert between pixel number and Stokes value. //# Copyright (C) 1997,1998,1999,2000,2001,2002,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef COORDINATES_STOKESCOORDINATE_H #define COORDINATES_STOKESCOORDINATE_H #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Interconvert between pixel and Stokes value. // // // // // // //
      • Coordinate //
      • Stokes // // // // Although not really a "coordinate", an axis where pixel numbers are used // for different Stokes values is in wide use. The StokesCoordinate // is a poor fit to the Coordinate polymorphic model. // You will probably find that if you try to use the Coordinate // classes polymorphically, that StokesCoordinate will cause you // to break the polymorphism. I.e. you may have to deal with // a specific StokesCoordinate) // // The StokesCoordinate just maintains a list (given in the constructor) of // possibly non-monotonic Stokes values. The values of the list are the // world values (they are values from the Stokes::StokesTypes enum). // Thus world = list[pixel] where pixel is the pixel coordinate (0,1, ...) // // This means that concepts such as reference pixel, reference value, // increment etc are meaningless for StokesCoordinate. You can recover these // these attributes, but the functions to set these attributes have no effect. // // Note also that for the StokesCoordinate relative world coordinates are defined to be the // as absolute, since there is no meaning for a relative Stokes world value (e.g. // what is XX -RL ?? Relative pixel coordinates are defined. // // // // // All pixel coordinates are zero relative. // // // // In this example we create a StokesCoordinate housing IQUV // // Vector iquv(4); // iquv(0) = Stokes::I; iquv(1) = Stokes::Q; // iquv(2) = Stokes::U; iquv(3) = Stokes::V; // StokesCoordinate stokes(iquv); // // // // // It is conventional to make a pseudo-axis of Stokes parameters. // Another approach is to make an image type called Stokes. // // // //
      • This could probably be generalized into an "enumeration axis" class. // // class StokesCoordinate : public Coordinate { public: // The length of whichStokes is the length of the axis, and the values // define which stokes are in which axis value. Often the vector will be of // length 4 and will contain Stokes::I, Q, U and V, however any valid value // from the stokes enum may be used. The values may not repeat however, e.g. // only one axis position may contain "I". explicit StokesCoordinate(const Vector &whichStokes); // Copy constructor (copy semantics) StokesCoordinate(const StokesCoordinate &other); // Assignment (copy semantics) StokesCoordinate &operator=(const StokesCoordinate &other); // Destructor. virtual ~StokesCoordinate(); // Returns Coordinates::STOKES. virtual Coordinate::Type type() const; // Always returns the String "Stokes". virtual String showType() const; // Always returns 1. // virtual uInt nPixelAxes() const; virtual uInt nWorldAxes() const; // // Convert a pixel to a world coordinate or vice versa. Returns True // if the conversion succeeds, otherwise it returns False and method // errorMessage returns an error message. // The output vectors are appropriately resized before use. // The Bool parameter in toWorld() is ignored as this coordinate does not // support a conversion layer frame. // virtual Bool toWorld(Vector &world, const Vector &pixel, Bool=True) const; virtual Bool toPixel(Vector &pixel, const Vector &world) const; // // Interconvert between pixel and world as a Stokes type. // It returns False if no conversion could be done. // Bool toPixel(Int &pixel, Stokes::StokesTypes stokes) const; Bool toWorld(Stokes::StokesTypes &stokes, Int pixel) const; // // Interconvert between world stored as a Double and world stored as // a Stokes type. Since these functions are static, any valid // Stokes type can be used. The second function returns // Stokes::Undefined if world is illegal. // static Double toWorld (Stokes::StokesTypes stokes); static Stokes::StokesTypes toWorld (Double world); // // Make absolute coordinates relative and vice-versa. // For the StokesCoordinate relative world coordinates are defined to be the // same as absolute world coordinates. Relative pixels do have meaning // and are implemented (rel = abs - refPix) // virtual void makePixelRelative (Vector& pixel) const; virtual void makePixelAbsolute (Vector& pixel) const; virtual void makeWorldRelative (Vector& world) const; virtual void makeWorldAbsolute (Vector& world) const; // // Get the Stokes values (Stokes::StokesType) that we constructed // with into a vector Vector stokes() const; // Get the stokes string representations Vector stokesStrings() const; // Set a new vector of Stokes values (a vector of Stokes::StokesType) void setStokes (const Vector &whichStokes); // Report the value of the requested attribute. // virtual Vector worldAxisNames() const; virtual Vector referencePixel() const; virtual Matrix linearTransform() const; virtual Vector increment() const; virtual Vector referenceValue() const; // // Set the value of the requested attribute. For the StokesCoordinate, // these have no effect (always return True) except for setWorldAxisNames. // virtual Bool setWorldAxisNames(const Vector &names); virtual Bool setReferencePixel(const Vector &refPix); virtual Bool setLinearTransform(const Matrix &xform); virtual Bool setIncrement(const Vector &inc) ; virtual Bool setReferenceValue(const Vector &refval) ; // // The set function has no effect as the units must be empty for a StokesCoordinate // Always returns True // virtual Bool setWorldAxisUnits(const Vector &units); virtual Vector worldAxisUnits() const; // // Set the world min and max ranges, for use in function toMix, // for a lattice of the given shape (for this coordinate). // The implementation here gives world coordinates at the start // and end of the Stokes axis. // The output vectors are resized. Returns False if fails (and // then setDefaultWorldMixRanges generates the ranges) // with a reason in errorMessage(). // The setDefaultWorldMixRanges function // gives you [-1e99->1e99]. // virtual Bool setWorldMixRanges (const IPosition& shape); virtual void setDefaultWorldMixRanges (); // // Format a StokesCoordinate world value with the common format // interface (refer to the base class Coordinate // for basics. // // A StokesCoordinate is formatted differently from other Coordinate // types. The world value is converted to the character representation // as defined by the enum StokesTypes in the class // Stokes. // // Thus, all other arguments to do with formatting and precision are ignored. virtual String format(String& units, Coordinate::formatType format, Double worldValue, uInt worldAxis, Bool isAbsolute=True, Bool showAsAbsolute=True, Int precision = -1, Bool usePrecForMixed=False) const; // Comparison function. Any private Double data members are compared // with the specified fractional tolerance. Don't compare on the specified // axes in the Coordinate. If the comparison returns False, method // errorMessage returns a message about why. // virtual Bool near(const Coordinate& other, Double tol=1e-6) const; virtual Bool near(const Coordinate& other, const Vector& excludeAxes, Double tol=1e-6) const; // // Save the StokesCoordinate into the supplied record using the supplied field name. // The field must not exist, otherwise False is returned. virtual Bool save(RecordInterface &container, const String &fieldName) const; // Recover the StokesCoordinate from a record. // A null pointer means that the restoration did not succeed - probably // because fieldName doesn't exist or doesn't contain a CoordinateSystem. static StokesCoordinate* restore(const RecordInterface &container, const String &fieldName); // Make a copy of the StokesCoordinate using new. The caller is responsible for calling // delete. virtual Coordinate *clone() const; // Comparison only made for specified axes in this and other Coordinate virtual Bool doNearPixel (const Coordinate& other, const Vector& thisAxes, const Vector& otherAxes, Double tol=1.0e-6) const; private: Bool toWorld(Double& world, const Double pixel) const; Bool toPixel(Double& pixel, const Double world) const; // Block values_p; // Keep these for subimaging purposes. Double crval_p, crpix_p, matrix_p, cdelt_p; String name_p; String unit_p; Int nValues_p; // Undefined and inaccessible StokesCoordinate(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/coordinates/Coordinates/TabularCoordinate.cc000066400000000000000000000544311476623553700240300ustar00rootroot00000000000000//# TabularCoordinate.cc: Table lookup 1-D coordinate, with interpolation //# Copyright (C) 1997,1998,1999,2000,2001,2002,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TabularCoordinate::TabularCoordinate() : Coordinate(), crval_p(0), cdelt_p(1), crpix_p(0), matrix_p(1.0), unit_p(""), name_p("Tabular"), channel_corrector_p(0), channel_corrector_rev_p(0) { setDefaultWorldMixRanges(); } TabularCoordinate::TabularCoordinate(Double refval, Double inc, Double refpix, const String &unit, const String &axisName) : Coordinate(), crval_p(refval), cdelt_p(inc), crpix_p(refpix), matrix_p(1.0), unit_p(unit), name_p(axisName), channel_corrector_p(0), channel_corrector_rev_p(0) { setDefaultWorldMixRanges(); } TabularCoordinate::TabularCoordinate(const Quantum& refval, const Quantum& inc, Double refpix, const String &axisName) : Coordinate(), crpix_p(refpix), matrix_p(1.0), name_p(axisName), channel_corrector_p(0), channel_corrector_rev_p(0) { // Check and assign if (refval.isConform(inc)) { crval_p = refval.getValue(); unit_p = refval.getUnit(); // Convert inc to units of reference value cdelt_p = inc.getValue(Unit(unit_p)); } else { throw (AipsError("Units of reference value and increment inconsistent")); } // setDefaultWorldMixRanges(); } TabularCoordinate::TabularCoordinate(const Vector &pixelValues, const Vector &worldValues, const String &unit, const String &axisName) : Coordinate(), crval_p(0.0), cdelt_p(0.0), crpix_p(0.0), matrix_p(0.0), unit_p(unit), name_p(axisName), channel_corrector_p(0), channel_corrector_rev_p(0) { makeNonLinearTabularCoordinate(pixelValues, worldValues); setDefaultWorldMixRanges(); } TabularCoordinate::TabularCoordinate(const Vector& pixelValues, const Quantum >& worldValues, const String &axisName) : Coordinate(), crval_p(0.0), cdelt_p(0.0), crpix_p(0.0), matrix_p(0.0), name_p(axisName), channel_corrector_p(0), channel_corrector_rev_p(0) { unit_p = worldValues.getUnit(); Vector world = worldValues.getValue(); makeNonLinearTabularCoordinate(pixelValues, world); setDefaultWorldMixRanges(); } void TabularCoordinate::clear_self() { crval_p = cdelt_p = crpix_p = matrix_p = -999.0; unit_p = "UNSET"; name_p = "UNSET"; if (channel_corrector_p) { delete channel_corrector_p; delete channel_corrector_rev_p; } channel_corrector_p = channel_corrector_rev_p = 0; } TabularCoordinate::TabularCoordinate(const TabularCoordinate &other) : Coordinate(), crval_p(0.0), cdelt_p(0.0), crpix_p(0.0), matrix_p(0.0), unit_p("UNSET"), name_p("UNSET"), channel_corrector_p(0), channel_corrector_rev_p(0) { copy(other); } TabularCoordinate &TabularCoordinate::operator=(const TabularCoordinate &other) { if (this != &other) { copy(other); } return *this; } void TabularCoordinate::copy(const TabularCoordinate &other) { clear_self(); Coordinate::operator=(other); crval_p = other.crval_p; cdelt_p = other.cdelt_p; crpix_p = other.crpix_p; unit_p = other.unit_p; name_p = other.name_p; matrix_p = other.matrix_p; if (other.channel_corrector_p != 0) { channel_corrector_p = new Interpolate1D(*other.channel_corrector_p); channel_corrector_rev_p = new Interpolate1D(*other.channel_corrector_rev_p); AlwaysAssert(channel_corrector_p != 0 && channel_corrector_rev_p != 0, AipsError); } } TabularCoordinate::~TabularCoordinate() { clear_self(); } Coordinate::Type TabularCoordinate::type() const { return Coordinate::TABULAR; } String TabularCoordinate::showType() const { return String("Tabular"); } uInt TabularCoordinate::nPixelAxes() const { return 1; } uInt TabularCoordinate::nWorldAxes() const { return 1; } Bool TabularCoordinate::toWorld(Double& world, Double pixel) const { if (channel_corrector_p) { pixel = (*channel_corrector_p)(pixel); } world = crval_p + cdelt_p * matrix_p * (pixel - crpix_p); return True; } Bool TabularCoordinate::toPixel(Double &pixel, Double world) const { pixel = (world - crval_p)/(cdelt_p * matrix_p) + crpix_p; if (channel_corrector_rev_p) { pixel = (*channel_corrector_rev_p)(pixel); } return True; } Bool TabularCoordinate::toWorld(Vector &world, const Vector &pixel, Bool) const { Bool rval = True; world.resize(pixel.nelements()); for(uInt i=0; i &pixel, const Vector &world) const { DebugAssert(world.nelements()==1,AipsError); // pixel.resize(1); return toPixel(pixel(0), world(0)); } Bool TabularCoordinate::toWorldMany(Matrix& world, const Matrix& pixel, Vector& failures) const { const uInt nTransforms = pixel.ncolumn(); Double alpha = cdelt_p * matrix_p; Double beta = crval_p -alpha * crpix_p; // world.resize(nWorldAxes(),nTransforms); Vector worlds(world.row(0)); // Only 1 axis in TC Vector pixels(pixel.row(0)); // if (channel_corrector_p) { for (uInt j=0; j& pixel, const Matrix& world, Vector& failures) const { const uInt nTransforms = world.ncolumn(); Double alpha = cdelt_p * matrix_p; Double beta = crpix_p - crval_p / alpha; // pixel.resize(nPixelAxes(),nTransforms); Vector worlds(world.row(0)); // Only 1 axis in TC Vector pixels(pixel.row(0)); // if (channel_corrector_rev_p) { for (uInt j=0; j TabularCoordinate::worldAxisNames() const { Vector tmp(1); tmp(0) = name_p; return tmp; } Vector TabularCoordinate::worldAxisUnits() const { Vector tmp(1); tmp(0) = unit_p; return tmp; } Vector TabularCoordinate::referencePixel() const { Vector tmp(1); tmp(0) = crpix_p; return tmp; } Vector TabularCoordinate::referenceValue() const { Vector tmp(1); tmp(0) = crval_p; return tmp; } Vector TabularCoordinate::increment() const { Vector tmp(1); tmp(0) = cdelt_p; return tmp; } Matrix TabularCoordinate::linearTransform() const { Matrix tmp(1,1); tmp(0,0) = matrix_p; return tmp; } Bool TabularCoordinate::setWorldAxisNames(const Vector &names) { Bool ok = (names.nelements()==1); if (!ok) { set_error ("names vector must be of length 1"); } else { name_p = names(0); } return ok; } Bool TabularCoordinate::setWorldAxisUnits(const Vector &units) { Bool ok = (units.nelements()==1); if (!ok) { set_error ("units vector must be of length 1"); } else { Vector d1 = increment(); ok = Coordinate::setWorldAxisUnits(units); if (ok) { unit_p = units(0); // Vector d2 = increment(); worldMin_p *= d2 / d1; worldMax_p *= d2 / d1; } } return ok; } Bool TabularCoordinate::overwriteWorldAxisUnits(const Vector &units) { Bool ok = (units.nelements()==1); if (ok) { unit_p = units(0); } else { set_error ("units vector must be of length 1"); } return ok; } Bool TabularCoordinate::setReferencePixel(const Vector &refPix) { Bool ok = (refPix.nelements()==1); if (!ok) { set_error ("reference pixel vector must be of length 1"); } else { crpix_p = refPix(0); } return ok; } Bool TabularCoordinate::setLinearTransform(const Matrix &xform) { Bool ok = (xform.nelements()==1); if (!ok) { set_error ("linear transform matrix must be of length 1"); } else { matrix_p = xform(0,0); } return ok; } Bool TabularCoordinate::setIncrement(const Vector &inc) { Bool ok = (inc.nelements()==1); if (!ok) { set_error ("increment vector must be of length 1"); } else { cdelt_p = inc(0); } return ok; } Bool TabularCoordinate::setReferenceValue(const Vector &refval) { Bool ok = (refval.nelements()==1); if (!ok) { set_error ("reference values vector must be of lenth 1"); } else { crval_p = refval(0); } return ok; } Vector TabularCoordinate::pixelValues() const { Vector pixels; if (channel_corrector_p) { pixels = channel_corrector_p->getX(); } return pixels; } Vector TabularCoordinate::worldValues() const { Vector tmp = pixelValues(); const uInt n = tmp.nelements(); for (uInt i=0; i excludeAxes; return near(other, excludeAxes, tol); } Bool TabularCoordinate::near(const Coordinate& other, const Vector& excludeAxes, Double tol) const { if (other.type() != this->type()) { set_error(String("Comparison is not with another TabularCoordinate")); return False; } // The TabularCoordinate has only one axis (pixel and world). // Therefore, if excludeAxes contains "0", we are done. // Add an assertion failure check should this ever change AlwaysAssert(nPixelAxes() == 1, AipsError); AlwaysAssert(nWorldAxes() == 1, AipsError); Bool found; if (linearSearch(found, excludeAxes, 0, excludeAxes.nelements()) >= 0) return True; // Check units and name if (unit_p != other.worldAxisUnits()(0)) { set_error("The TabularCoordinates have differing axis units"); return False; } if (name_p != other.worldAxisNames()(0)) { set_error("The TabularCoordinates have differing world axis names"); return False; } // Private data crval_p, cdelt_p, crpix_p, matrix_p are formed // from the Table values so in principle there is no need to // check them. However, if they differ, that might be faster // than working through the table so check them anyway. const TabularCoordinate& tCoord = dynamic_cast(other); if (!casacore::near(crval_p,tCoord.crval_p,tol)) { set_error("The TabularCoordinates have differing average reference values"); return False; } if (!casacore::near(crpix_p,tCoord.crpix_p,tol)) { set_error("The TabularCoordinates have differing average reference pixels"); return False; } if (!casacore::near(cdelt_p,tCoord.cdelt_p,tol)) { set_error("The TabularCoordinates have differing average increments"); return False; } if (!casacore::near(matrix_p,tCoord.matrix_p,tol)) { // It's really just one component of the matrix set_error("The TabularCoordinates have differing linear transformation matrices"); return False; } // Check the table Vector data1 = this->pixelValues(); Vector data2 = tCoord.pixelValues(); if (data1.nelements() != data2.nelements()) { set_error("The TabularCoordinates have differing numbers of entries in the pixel value table"); return False; } uInt i; for (i=0; iworldValues(); data2 = tCoord.worldValues(); if (data1.nelements() != data2.nelements()) { set_error("The TabularCoordinates have differing numbers of entries in the world value table"); return False; } for (i=0; i tmp; subrec.define("pixelvalues", tmp); subrec.define("worldvalues", tmp); } container.defineRecord(fieldName, subrec); } return ok; } TabularCoordinate* TabularCoordinate::restore(const RecordInterface &container, const String &fieldName) { if (! container.isDefined(fieldName)) { return 0; } Record subrec(container.asRecord(fieldName)); if (!subrec.isDefined("crval")) { return 0; } Vector crval(subrec.toArrayDouble("crval")); if (!subrec.isDefined("crpix")) { return 0; } Vector crpix(subrec.toArrayDouble("crpix")); if (!subrec.isDefined("cdelt")) { return 0; } Vector cdelt(subrec.toArrayDouble("cdelt")); if (!subrec.isDefined("pc")) { return 0; } Matrix pc(subrec.toArrayDouble("pc")); if (!subrec.isDefined("axes")) { return 0; } Vector axes; subrec.get("axes", axes); if (!subrec.isDefined("units")) { return 0; } Vector units; subrec.get("units", units); if (!subrec.isDefined("pixelvalues") || !subrec.isDefined("worldvalues")) { return 0; } Vector pixels(subrec.toArrayDouble("pixelvalues")); Vector world (subrec.toArrayDouble("worldvalues")); TabularCoordinate *retval = 0; if (pixels.nelements() > 0) { retval = new TabularCoordinate(pixels, world, units(0), axes(0)); } else { retval = new TabularCoordinate(crval(0), cdelt(0), crpix(0), units(0), axes(0)); } // return retval; } Coordinate *TabularCoordinate::clone() const { return new TabularCoordinate(*this); } Coordinate* TabularCoordinate::makeFourierCoordinate (const Vector& axes, const Vector& shape) const // // axes says which axes in the coordinate are to be transformed // shape is the shape of the image for all axes in this coordinate // { if (channel_corrector_p) { set_error("Cannot Fourier Transform a non-linear TabularCoordinate"); return 0; } // if (axes.nelements() != nPixelAxes()) { set_error ("Invalid number of specified axes"); return 0; } uInt nT = 0; for (uInt i=0; i& units = worldAxisUnits(); const Vector& names = worldAxisNames(); // Vector unitsCanon(worldAxisUnits().copy()); Vector unitsOut(worldAxisUnits().copy()); Vector namesOut(worldAxisNames().copy()); // for (uInt i=0; i crval(tc.referenceValue().copy()); Vector crpix(tc.referencePixel().copy()); Vector cdelt(tc.increment().copy()); for (uInt i=0; i pc(1, 1); pc = 0.0; pc.diagonal() = 1.0; return new LinearCoordinate(namesOut, unitsOut, crval, cdelt, pc, crpix); } void TabularCoordinate::makeNonLinearTabularCoordinate(const Vector &pixelValues, const Vector &worldValues) { const uInt n = pixelValues.nelements(); if (n < 1 || n != worldValues.nelements()) { throw(AipsError("TabularCoordinate::TabularCoordinate - illegal table " "(length 0 or n(pixelvalues) != n(worldvalues)")); } if(n==1){ // trivial case // Work out "global" crval etc. crval_p = worldValues(0); crpix_p = pixelValues(0); cdelt_p = 0.; matrix_p = 1.0; Vector averagePixel(1,pixelValues(0)); ScalarSampledFunctional in(pixelValues), avg(averagePixel); channel_corrector_p = new Interpolate1D(in, avg, True, True); channel_corrector_rev_p = new Interpolate1D(avg, in, True, True); AlwaysAssert(channel_corrector_p != 0 && channel_corrector_rev_p != 0, AipsError); channel_corrector_p->setMethod(Interpolate1D::nearestNeighbour); channel_corrector_rev_p->setMethod(Interpolate1D::nearestNeighbour); } else{ // n>1 if (pixelValues(n-1) - pixelValues(0) == 0) { throw(AipsError("TabularCoordinate::TabularCoordinate - illegal table " "first and last pixel values are the same")); } // Work out "global" crval etc. crval_p = worldValues(0); crpix_p = pixelValues(0); cdelt_p = (worldValues(n-1) - worldValues(0)) / (pixelValues(n-1) - pixelValues(0)); matrix_p = 1.0; if (cdelt_p == 0.0) { throw(AipsError("TabularCoordinate - start and " "end values in table must differ")); } Double signworld = ((worldValues(n-1) - worldValues(0)) > 0 ? 1.0 : -1.0); Double signpixel = ((pixelValues(n-1) - pixelValues(0)) > 0 ? 1.0 : -1.0); // Check that the pixel values and values monotonically increase or decrease // and if so, work out the difference between the actual supplied pixel and // the "average" pixel value. Vector averagePixel(n); for (uInt i=0; i1) { Double diffworld = signworld*(worldValues(i) - worldValues(i-1)); Double diffpixel = signpixel*(pixelValues(i) - pixelValues(i-1)); if (diffworld <= 0 || diffpixel <= 0) { throw(AipsError("TabularCoordinate - pixel and world values " "must increase or decrease monotonically")); } } averagePixel(i) = (worldValues(i) - crval_p)/cdelt_p + crpix_p; } ScalarSampledFunctional in(pixelValues), avg(averagePixel); channel_corrector_p = new Interpolate1D(in, avg, True, True); channel_corrector_rev_p = new Interpolate1D(avg, in, True, True); AlwaysAssert(channel_corrector_p != 0 && channel_corrector_rev_p != 0, AipsError); channel_corrector_p->setMethod(Interpolate1D::linear); channel_corrector_rev_p->setMethod(Interpolate1D::linear); } // endif } } //# NAMESPACE CASACORE - END casacore-3.7.1/coordinates/Coordinates/TabularCoordinate.h000066400000000000000000000300451476623553700236650ustar00rootroot00000000000000//# TabularCoordinate.h: Table lookup 1-D coordinate, with interpolation //# Copyright (C) 1997,1998,1999,2000,2001,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef COORDINATES_TABULARCOORDINATE_H #define COORDINATES_TABULARCOORDINATE_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template class Interpolate1D; template class Quantum; class LogIO; // // Table lookup 1-D coordinate, with interpolation. // // // // // //
      • Coordinate // // // // This class is used where the world and pixel values are determined by a // lookup table. For fractional pixel values, a linear interpolation is used. // The values returned for, e.g., the increment, are based on // the average of the whole table. At present, // the values must either increase or decrease monotonically. // // // // All pixels coordinates are zero relative. // // // // Let's make a non-linear TabularCoordinate and convert a pixel // value to world (which will use linear interpolation) // // Vector pixelValues(3); // Vector worldValues(3); // pixelValues(0) = 122.0; // pixelValues(1) = 300.0; // pixelValues(2) = 6524.0; // worldValues(0) = 1.1e6; // worldValues(1) = 2.1e6; // worldValues(2) = 2.2e6; // // String unit("km"); // String axisName("length"); // // TabularCoordinate tc(pixelValues, worldValues, unit, axisName); // // Double world, pixel; // pixel = 200.12; // if (!tc.toWorld(world, pixel)) { // cerr << "Error : " << tc.errorMessage() << endl; // } else { // cerr << "pixel, world = " << pixel << ", " << world << endl; // } // // // // // This class was motivated by the need for an irregular axis, such as a collection // of frequencies. For example, the SpectralCoordinate class contains a TabularCoordinate. // // // // //
      • AipsError // // // //
      • Allow interpolations other than linear. // class TabularCoordinate : public Coordinate { public: // Default constructor. It is equivalent to // TabularCoordinate(0,1,0, "", "Tabular"); TabularCoordinate(); // Create a linear TabularCoordinate where // world = refval + inc*(pixel-refpix) TabularCoordinate(Double refval, Double inc, Double refpix, const String &unit, const String &axisName); // Create a linear TabularCoordinate with a Quantum-based interface where // world = refval + inc*(pixel-refpix). The units of the // increment (inc) will be converted to // those of the reference value (refVal) which will // then serve as the units of the Coordinate. TabularCoordinate(const Quantum& refval, const Quantum& inc, Double refpix, const String& axisName); // Construct a TabularCoordinate with the specified world values. The // increments and related functions return the average values // calculated from the first and last world values. The number of pixel // and world values must be the same. Normally the pixel values will be // 0,1,2,..., but this is not required. // // A linear interpolation/extrapolation is used for channels which are not // supplied. The reference channel (pixel) is chosen to be 0. The // frequencies must increase or decrease monotonically (otherwise the // toPixel lookup would not be possible). TabularCoordinate(const Vector &pixelValues, const Vector &worldValues, const String &unit, const String &axisName); // Construct a TabularCoordinate with the specified world values // via the Quantum-based interface. All comments for the // previous constructor apply TabularCoordinate(const Vector& pixelValues, const Quantum >& worldValues, const String &axisName); // Copy constructor (copy semantics). TabularCoordinate(const TabularCoordinate &other); // Assignment (copy semantics). TabularCoordinate &operator=(const TabularCoordinate &other); // Destructor. virtual ~TabularCoordinate(); // Returns Coordinate::TABULAR. virtual Coordinate::Type type() const; // Always returns the String "Tabular". virtual String showType() const; // Always returns 1. // virtual uInt nPixelAxes() const; virtual uInt nWorldAxes() const; // // Convert a pixel position to a world position or vice versa. Returns True // if the conversion succeeds, otherwise it returns False and method // errorMessage contains an error message. The output // vectors are appropriately resized. // The Bool parameter in toWorld() has no effect as this coordinate does // not support a conversion layer frame. // virtual Bool toWorld(Vector &world, const Vector &pixel, Bool=True) const; virtual Bool toPixel(Vector &pixel, const Vector &world) const; Bool toWorld(Double &world, Double pixel) const; Bool toPixel(Double &pixel, Double world) const; // // Batch up a lot of transformations. The first (most rapidly varying) axis // of the matrices contain the coordinates. Returns False if any conversion // failed and errorMessage() will hold a message. // The failures array (True for fail, False for success) // is the length of the number of conversions and // holds an error status for each conversion. // virtual Bool toWorldMany(Matrix &world, const Matrix &pixel, Vector& failures) const; virtual Bool toPixelMany(Matrix& pixel, const Matrix& world, Vector& failures) const; // // Make absolute coordinates relative and vice-versa (with // respect to the referencfe value). // Vectors must be length nPixelAxes() or // nWorldAxes() or memory access errors will occur // virtual void makePixelRelative (Vector& pixel) const {pixel -= crpix_p;}; virtual void makePixelAbsolute (Vector& pixel) const {pixel += crpix_p;}; virtual void makeWorldRelative (Vector& world) const {world -= crval_p;}; virtual void makeWorldAbsolute (Vector& world) const {world += crval_p;}; // // Return the requested attribute. // virtual Vector worldAxisNames() const; virtual Vector referencePixel() const; virtual Matrix linearTransform() const; virtual Vector increment() const; virtual Vector referenceValue() const; // // Set the value of the requested attribute. Note that these just // change the internal values, they do not cause any recomputation. // virtual Bool setWorldAxisNames(const Vector &names); virtual Bool setReferencePixel(const Vector &refPix); virtual Bool setLinearTransform(const Matrix &xform); virtual Bool setIncrement(const Vector &inc) ; virtual Bool setReferenceValue(const Vector &refval); // // Set/get the axis unit. Adjust the increment and // reference value by the ratio of the old and new units. // The unit must be compatible with the current units. // virtual Bool setWorldAxisUnits(const Vector &units); virtual Vector worldAxisUnits() const; // // Overwrite the world axis units with no compatibility // checks or adjustment. Bool overwriteWorldAxisUnits(const Vector &units); // Get the table, i.e. the pixel and world values. The length of these // Vectors will be zero if this axis is pure linear. // Vector pixelValues() const; Vector worldValues() const; // // Comparison function. Any private Double data members are compared // with the specified fractional tolerance. Don't compare on the specified // axes in the Coordinate. If the comparison returns False, method // errorMessage() contains a message about why. // virtual Bool near(const Coordinate& other, Double tol=1e-6) const; virtual Bool near(const Coordinate& other, const Vector& excludeAxes, Double tol=1e-6) const; // // Find the Coordinate for when we Fourier Transform ourselves. This pointer // must be deleted by the caller. Axes specifies which axes of the Coordinate // you wish to transform. Shape specifies the shape of the image // associated with all the axes of the Coordinate. Currently the // output reference pixel is always shape/2. If the pointer returned is 0, // it failed with a message in errorMessage virtual Coordinate* makeFourierCoordinate (const Vector& axes, const Vector& shape) const; // Save the TabularCoordinate into the supplied record using the supplied field name. // The field must not exist, otherwise False is returned. virtual Bool save(RecordInterface &container, const String &fieldName) const; // Recover the TabularCoordinate from a record. // A null pointer means that the restoration did not succeed - probably // because fieldName doesn't exist or doesn't contain a CoordinateSystem. static TabularCoordinate *restore(const RecordInterface &container, const String &fieldName); // Make a copy of the TabularCoordinate using new. The caller is responsible for calling // delete. virtual Coordinate *clone() const; private: Double crval_p, cdelt_p, crpix_p; Double matrix_p; String unit_p; String name_p; // Channel_True = channel_corrections_p(Channel_average). // Interpolate1D *channel_corrector_p; Interpolate1D *channel_corrector_rev_p; // // Common for assignment operator and destructor. void clear_self(); // Common code for copy ctor and assignment operator. void copy(const TabularCoordinate &other); void makeNonLinearTabularCoordinate(const Vector &pixelValues, const Vector &worldValues); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/coordinates/Coordinates/test/000077500000000000000000000000001476623553700210675ustar00rootroot00000000000000casacore-3.7.1/coordinates/Coordinates/test/CMakeLists.txt000066400000000000000000000010461476623553700236300ustar00rootroot00000000000000set (tests dCoordinates dRemoveAxes dWorldMap tCoordinate tCoordinateSystem tCoordinateUtil tDirectionCoordinate tFrequencyAligner tGaussianConvert tLinearCoordinate tLinearXform tObsInfo tProjection tSpectralCoordinate tStokesCoordinate tQualityCoordinate tTabularCoordinate ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_coordinates) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/coordinates/Coordinates/test/dCoordinates.cc000066400000000000000000000161151476623553700240200ustar00rootroot00000000000000//# Program.cc: This program ... //# Copyright (C) 1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include //# Enumerated lines are in the Coordinates.h module header. If you //# change them, change Coordinates.h to match. int main() { try { // Direction Coordinate Matrix xform(2,2); // 1 xform = 0.0; xform.diagonal() = 1.0; // 2 DirectionCoordinate radec(MDirection::J2000, // 3 Projection(Projection::SIN), // 4 135*M_PI/180.0, 60*M_PI/180.0, // 5 -1*M_PI/180.0, 1*M_PI/180, // 6 xform, // 7 128.0, 128.0, // 8 999.0, 999.0); Vector units(2); units = "deg"; // 9 radec.setWorldAxisUnits(units); // 10 Vector world(2), pixel(2); // 11 pixel = 138.0; // 12 Bool ok = radec.toWorld(world, pixel); // 13 if (!ok) { // 14 cout << "Error: " << radec.errorMessage() << endl; // 15 return 1; // 16 } // 17 cout << world << " <--- " << pixel << endl; // 18 ok = radec.toPixel(pixel, world); // 19 if (!ok) { cout << "Error: " << radec.errorMessage() << endl; return 1; } cout << world << " ---> " << pixel << endl; // StokesCoordinate Vector iquv(4); // 20 iquv(0) = Stokes::I; iquv(1) = Stokes::Q; // 21 iquv(2) = Stokes::U; iquv(3) = Stokes::V; // 22 StokesCoordinate stokes(iquv); // 23 Int plane; // 24 ok = stokes.toPixel(plane, Stokes::Q); // 25 if (!ok) { cout << "Error: " << stokes.errorMessage() << endl; return 1; } cout << "Stokes Q is plane " << plane << endl; ok = stokes.toPixel(plane, Stokes::XX); // 26 if (!ok) { cout << "Expected error: " << stokes.errorMessage() << endl; cout << "Continuing..." << endl; } cout << "Stokes XX is plane " << plane << endl; // SpectralCoordinate SpectralCoordinate spectral(MFrequency::TOPO, // 27 1400 * 1.0E+6, // 28 20 * 1.0E+3, // 29 0, // 30 1420.40575 * 1.0E+6); // 31 units.resize(1); pixel.resize(1); world.resize(1); units = "MHz"; spectral.setWorldAxisUnits(units); pixel = 50; ok = spectral.toWorld(world, pixel); if (!ok) { cout << "Error: " << spectral.errorMessage() << endl; return 1; } cout << world << " <--- " << pixel << endl; ok = spectral.toPixel(pixel, world); if (!ok) { cout << "Error: " << spectral.errorMessage() << endl; return 1; } cout << world << " ---> " << pixel << endl; // CoordinateSystem CoordinateSystem coordsys; coordsys.addCoordinate(radec); coordsys.addCoordinate(stokes); coordsys.addCoordinate(spectral); world.resize(4); pixel.resize(4); pixel(0) = 138; pixel(1) = 138; pixel(2) = 2; pixel(3) = 50; ok = coordsys.toWorld(world, pixel); if (!ok) { cout << "Error: " << coordsys.errorMessage() << endl; return 1; } cout << world << " <--- " << pixel << endl; ok = coordsys.toPixel(pixel, world); if (!ok) { cout << "Error: " << coordsys.errorMessage() << endl; return 1; } cout << world << " ---> " << pixel << endl; // CoordinateSystem::transpose Vector tran(4); tran(0) = 0; tran(1) = 1; tran(2) = 3; tran(3) = 2; coordsys.transpose(tran,tran); pixel(2) = 50; pixel(3) = 2; ok = coordsys.toWorld(world, pixel); if (!ok) { cout << "Error: " << coordsys.errorMessage() << endl; return 1; } cout << world << " <--- " << pixel << endl; ok = coordsys.toPixel(pixel, world); if (!ok) { cout << "Error: " << coordsys.errorMessage() << endl; return 1; } cout << world << " ---> " << pixel << endl; // CoordinateSystem::remove*Axis ok = coordsys.removePixelAxis(0, 138.0); if (!ok) { cout << "Error: " << coordsys.errorMessage() << endl; return 1; } pixel.resize(3); pixel(0) = 138; pixel(1) = 50; pixel(2) = 2; ok = coordsys.toWorld(world, pixel); if (!ok) { cout << "Error: " << coordsys.errorMessage() << endl; return 1; } cout << world << " <--- " << pixel << endl; ok = coordsys.toPixel(pixel, world); if (!ok) { cout << "Error: " << coordsys.errorMessage() << endl; return 1; } cout << world << " ---> " << pixel << endl; TableRecord rec; ok = coordsys.save(rec, "CS"); if (!ok) { cout << "Error: " << coordsys.errorMessage() << endl; return 1; } CoordinateSystem* pCoordSys = CoordinateSystem::restore(rec,"CS"); if (pCoordSys == 0) { cout << "Failed to restore from record" << endl; return 1; } else { delete pCoordSys; } } catch (std::exception& x) { cerr << "aipserror: error " << x.what() << endl; return 1; } cout << "ok" << endl; return 0; } casacore-3.7.1/coordinates/Coordinates/test/dCoordinates.out000066400000000000000000000010241476623553700242330ustar00rootroot00000000000000[107.367, 67.8951] <--- [138, 138] [107.367, 67.8951] ---> [138, 138] Stokes Q is plane 1 Expected error: Stokes value XX is not contained in this StokesCoordinate Continuing... Stokes XX is plane 1 [1401] <--- [50] [1401] ---> [50] [107.367, 67.8951, 3, 1401] <--- [138, 138, 2, 50] [107.367, 67.8951, 3, 1401] ---> [138, 138, 2, 50] [107.367, 67.8951, 1401, 3] <--- [138, 138, 50, 2] [107.367, 67.8951, 1401, 3] ---> [138, 138, 50, 2] [107.367, 67.8951, 1401, 3] <--- [138, 50, 2] [107.367, 67.8951, 1401, 3] ---> [138, 50, 2] ok casacore-3.7.1/coordinates/Coordinates/test/dRemoveAxes.cc000066400000000000000000000213051476623553700236210ustar00rootroot00000000000000//# dRemoveAxes.cc: demonstrate use of CoordinateUtil::removeAxes function //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001,2002,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include int main() { try { LogOrigin lor("dRemoveAxes", "main()", WHERE); LogIO os(lor); IPosition d1, d2; Vector worldReplacement; Vector pixelReplacement; // { cout << "remove world axes = [0, 1] and associated pixel axes from [ra, dec, freq]" << endl; CoordinateSystem cSys = CoordinateUtil::defaultCoords3D(); Vector list(1); list(0) = 2; Bool remove = False; if (CoordinateUtil::removeAxes(cSys, worldReplacement, list, remove)) { cSys.list(os, MDoppler::RADIO, d1, d2); } else { cout << "failed" << endl; } } cout << endl << endl; { cout << "remove world axes = [2] and associated pixel axes from [ra, dec, freq]" << endl; CoordinateSystem cSys = CoordinateUtil::defaultCoords3D(); Vector list(1); list(0) = 2; Bool remove = True; if (CoordinateUtil::removeAxes(cSys, worldReplacement, list, remove)) { cSys.list(os, MDoppler::RADIO, d1, d2); } else { cout << "failed" << endl; } } cout << endl << endl; { cout << "remove world axes = [0, 2] and associated pixel axes from [ra,dec,freq]" << endl; CoordinateSystem cSys = CoordinateUtil::defaultCoords3D(); Vector list(2); list(0) = 0; list(1) = 2; Bool remove = True; if (CoordinateUtil::removeAxes(cSys, worldReplacement, list, remove)) { cSys.list(os, MDoppler::RADIO, d1, d2); } else { cout << "failed" << endl; } } cout << endl << endl; { cout << "remove world axes = [0] and associated pixel axes from [ra, dec, freq]" << endl; cout << "and then world axes = [0, 1] and associated pixel axes " << endl; CoordinateSystem cSys = CoordinateUtil::defaultCoords3D(); Vector list(1); list(0) = 0; Bool remove = True; if (CoordinateUtil::removeAxes(cSys, worldReplacement, list, remove)) { list.resize(2); list(0) = 0; list(1) = 1; if (CoordinateUtil::removeAxes(cSys, worldReplacement, list, remove)) { cSys.list(os, MDoppler::RADIO, d1, d2); } else { cout << "failed" << endl; } } else { cout << "failed" << endl; } } cout << endl << endl; { cout << "remove world axes = [0, 2] and associated pixel axes from [ra,dec,freq]" << endl; CoordinateSystem cSys = CoordinateUtil::defaultCoords3D(); Vector list(2); list(0) = 0; list(1) = 2; Bool remove = True; Vector incr = cSys.increment(); Vector refVal = cSys.referenceValue(); Vector refPix = cSys.referencePixel(); worldReplacement.resize(2); worldReplacement(0) = (-1 - refPix(list(0)))*incr(list(0)) + refVal(list(0)); worldReplacement(1) = (10 - refPix(list(1)))*incr(list(1)) + refVal(list(1)); cout << "specified world replacement values = " << worldReplacement << endl; if (CoordinateUtil::removeAxes(cSys, worldReplacement, list, remove)) { cSys.list(os, MDoppler::RADIO, d1, d2); } else { cout << "failed" << endl; } } cout << endl << endl; { cout << "remove world axes = [0, 2] and associated pixel axes from [ra,dec,freq]" << endl; CoordinateSystem cSys = CoordinateUtil::defaultCoords3D(); Vector list(2); list(0) = 0; list(1) = 2; Bool remove = True; if (CoordinateUtil::removeAxes(cSys, worldReplacement, list, remove)) { cSys.list(os, MDoppler::RADIO, d1, d2); } else { cout << "failed" << endl; } } cout << endl << endl; { cout << "remove pixel axes = [0] from [ra,dec,freq]" << endl; CoordinateSystem cSys = CoordinateUtil::defaultCoords3D(); pixelReplacement.resize(1); pixelReplacement(0) = -20.0; cout << "specified pixel replacement values = " << pixelReplacement << endl; Vector list(1); list(0) = 0; Bool remove = True; if (CoordinateUtil::removePixelAxes(cSys, pixelReplacement, list, remove)) { cSys.list(os, MDoppler::RADIO, d1, d2); } else { cout << "failed" << endl; } } cout << endl << endl; { cout << "remove pixel axes = [1] from [ra,dec,freq]" << endl; CoordinateSystem cSys = CoordinateUtil::defaultCoords3D(); pixelReplacement.resize(1); pixelReplacement(0) = +20.0; cout << "specified pixel replacement values = " << pixelReplacement << endl; Vector list(1); list(0) = 1; Bool remove = True; if (CoordinateUtil::removePixelAxes(cSys, pixelReplacement, list, remove)) { cSys.list(os, MDoppler::RADIO, d1, d2); } else { cout << "failed" << endl; } } cout << endl << endl; { cout << "remove pixel axes = [0,1] from [ra,dec,freq]" << endl; CoordinateSystem cSys = CoordinateUtil::defaultCoords3D(); pixelReplacement.resize(2); pixelReplacement(0) = -20.0; pixelReplacement(1) = +20.0; cout << "specified pixel replacement values = " << pixelReplacement << endl; Vector list(2); list(0) = 0; list(1) = 1; Bool remove = True; if (CoordinateUtil::removePixelAxes(cSys, pixelReplacement, list, remove)) { cSys.list(os, MDoppler::RADIO, d1, d2); } else { cout << "failed" << endl; } } cout << endl << endl; { cout << "remove pixel axes = [0,2] from [ra,dec,freq]" << endl; CoordinateSystem cSys = CoordinateUtil::defaultCoords3D(); pixelReplacement.resize(2); pixelReplacement(0) = -20.0; pixelReplacement(1) = +20.0; cout << "specified pixel replacement values = " << pixelReplacement << endl; Vector list(2); list(0) = 0; list(1) = 2; Bool remove = True; if (CoordinateUtil::removePixelAxes(cSys, pixelReplacement, list, remove)) { cSys.list(os, MDoppler::RADIO, d1, d2); } else { cout << "failed" << endl; } } cout << endl << endl; { cout << "remove pixel axes = [1,2] from [ra,dec,freq]" << endl; CoordinateSystem cSys = CoordinateUtil::defaultCoords3D(); pixelReplacement.resize(2); pixelReplacement(0) = -20.0; pixelReplacement(1) = +20.0; cout << "specified pixel replacement values = " << pixelReplacement << endl; Vector list(2); list(0) = 1; list(1) = 2; Bool remove = True; if (CoordinateUtil::removePixelAxes(cSys, pixelReplacement, list, remove)) { cSys.list(os, MDoppler::RADIO, d1, d2); } else { cout << "failed" << endl; } } cout << endl << endl; } catch (std::exception& x) { cerr << "aipserror: error " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/coordinates/Coordinates/test/dWorldMap.cc000066400000000000000000000225431476623553700232750ustar00rootroot00000000000000//# dWorldMap.cc: demonstarte use of CoordinateSystem::worldMap //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include void list (Bool ok, Bool ok2, Vector& wmap, Vector& wtranspose, Vector& pmap, Vector& ptranspose, CoordinateSystem& cSys1, CoordinateSystem& cSys2); int main() // // Test out the {world,pixel}Map function in CoordinateSystem // { try { Vector wmap, pmap, wtranspose, ptranspose; Vector refChange; { cout << "2D [ra, dec] & 0D" << endl; CoordinateSystem cSys1 = CoordinateUtil::defaultCoords2D(); CoordinateSystem cSys2; Bool ok = cSys1.worldMap(wmap, wtranspose, refChange, cSys2); Bool ok2 = cSys1.pixelMap(pmap, ptranspose, cSys2); list (ok, ok2, wmap, wtranspose, pmap, ptranspose, cSys1, cSys2); cout << endl << endl; } { cout << "0D & 2D [ra, dec]" << endl; CoordinateSystem cSys1; CoordinateSystem cSys2 = CoordinateUtil::defaultCoords2D(); Bool ok = cSys1.worldMap(wmap, wtranspose, refChange, cSys2); Bool ok2 = cSys1.pixelMap(pmap, ptranspose, cSys2); list (ok, ok2, wmap, wtranspose, pmap, ptranspose, cSys1, cSys2); cout << endl << endl; } { cout << "3D [ra, dec, spec] & 3D [ra, dec, spec]" << endl; CoordinateSystem cSys1 = CoordinateUtil::defaultCoords3D(); CoordinateSystem cSys2 = CoordinateUtil::defaultCoords3D(); Bool ok = cSys1.worldMap(wmap, wtranspose, refChange, cSys2); Bool ok2 = cSys1.pixelMap(pmap, ptranspose, cSys2); list (ok, ok2, wmap, wtranspose, pmap, ptranspose, cSys1, cSys2); cout << endl << endl; } { cout << "2D [ra, dec] & 3D [ra, dec, spec]" << endl; CoordinateSystem cSys1 = CoordinateUtil::defaultCoords2D(); CoordinateSystem cSys2 = CoordinateUtil::defaultCoords3D(); Bool ok = cSys1.worldMap(wmap, wtranspose, refChange, cSys2); Bool ok2 = cSys1.pixelMap(pmap, ptranspose, cSys2); list (ok, ok2, wmap, wtranspose, pmap, ptranspose, cSys1, cSys2); cout << endl << endl; } { cout << "3D [ra, dec, spec] & 2D [ra, dec]" << endl; CoordinateSystem cSys1 = CoordinateUtil::defaultCoords3D(); CoordinateSystem cSys2 = CoordinateUtil::defaultCoords2D(); Bool ok = cSys1.worldMap(wmap, wtranspose, refChange, cSys2); Bool ok2 = cSys1.pixelMap(pmap, ptranspose, cSys2); list (ok, ok2, wmap, wtranspose, pmap, ptranspose, cSys1, cSys2); cout << endl << endl; } { cout << "3D [ra, dec, spec] & 3D [dec, spec, ra]" << endl; CoordinateSystem cSys1 = CoordinateUtil::defaultCoords3D(); CoordinateSystem cSys2 = CoordinateUtil::defaultCoords3D(); Vector worldOrder(cSys2.nWorldAxes()); Vector pixelOrder(cSys2.nPixelAxes()); worldOrder(0) = 1; worldOrder(1) = 2; worldOrder(2) = 0; pixelOrder = worldOrder; cSys2.transpose(worldOrder, pixelOrder); Bool ok = cSys1.worldMap(wmap, wtranspose, refChange, cSys2); Bool ok2 = cSys1.pixelMap(pmap, ptranspose, cSys2); list (ok, ok2, wmap, wtranspose, pmap, ptranspose, cSys1, cSys2); cout << endl << endl; } { cout << "2D [ra,dec] & 3D [ra, dec, spec] " << endl; cout << " [0, 1] & [0, 1, -1]" << endl; CoordinateSystem cSys2 = CoordinateUtil::defaultCoords3D(); Int pSpec = CoordinateUtil::findSpectralAxis(cSys2); if (pSpec >= 0) { Int wSpec = cSys2.pixelAxisToWorldAxis(pSpec); cSys2.removeWorldAxis(wSpec, cSys2.referenceValue()(wSpec)); // CoordinateSystem cSys1 = CoordinateUtil::defaultCoords2D(); Bool ok = cSys1.worldMap(wmap, wtranspose, refChange, cSys2); Bool ok2 = cSys1.pixelMap(pmap, ptranspose, cSys2); list (ok, ok2, wmap, wtranspose, pmap, ptranspose, cSys1, cSys2); } else { cout << "Spectral missing. This was not expected" << endl; } cout << endl << endl; } { cout << "2D [ra,dec] & 3D [spec, dec, ra] " << endl; cout << " [0, 1] & [-1, 0, 1]" << endl; CoordinateSystem cSys1 = CoordinateUtil::defaultCoords2D(); CoordinateSystem cSys2 = CoordinateUtil::defaultCoords3D(); Vector worldOrder(cSys2.nWorldAxes()); Vector pixelOrder(cSys2.nPixelAxes()); worldOrder(0) = 2; worldOrder(1) = 1; worldOrder(2) = 0; pixelOrder = worldOrder; cSys2.transpose(worldOrder, pixelOrder); // Int pSpec = CoordinateUtil::findSpectralAxis(cSys2); if (pSpec >= 0) { Int wSpec = cSys2.pixelAxisToWorldAxis(pSpec); cSys2.removeWorldAxis(wSpec, cSys2.referenceValue()(wSpec)); // Bool ok = cSys1.worldMap(wmap, wtranspose, refChange, cSys2); Bool ok2 = cSys1.pixelMap(pmap, ptranspose, cSys2); list (ok, ok2,wmap, wtranspose, pmap, ptranspose, cSys1, cSys2); } else { cout << "Spectral missing. This was not expected" << endl; } cout << endl << endl; } { cout << "3D [spec, dec, ra] & 2D [ra,dec]" << endl; cout << " [-1, 0, 1] & [0, 1]" << endl; CoordinateSystem cSys1 = CoordinateUtil::defaultCoords3D(); CoordinateSystem cSys2 = CoordinateUtil::defaultCoords2D(); Vector worldOrder(cSys1.nWorldAxes()); Vector pixelOrder(cSys1.nPixelAxes()); worldOrder(0) = 2; worldOrder(1) = 1; worldOrder(2) = 0; pixelOrder = worldOrder; cSys1.transpose(worldOrder, pixelOrder); // Int pSpec = CoordinateUtil::findSpectralAxis(cSys1); if (pSpec >= 0) { Int wSpec = cSys1.pixelAxisToWorldAxis(pSpec); cSys1.removeWorldAxis(wSpec, cSys1.referenceValue()(wSpec)); // Bool ok = cSys1.worldMap(wmap, wtranspose, refChange, cSys2); Bool ok2 = cSys1.pixelMap(pmap, ptranspose, cSys2); list (ok, ok2, wmap, wtranspose, pmap, ptranspose, cSys1, cSys2); } else { cout << "Spectral missing. This was not expected" << endl; } cout << endl << endl; } { cout << "2D [ra, dec] & 2D [spec, stokes]" << endl; CoordinateSystem cSys1 = CoordinateUtil::defaultCoords2D(); CoordinateSystem cSys2; CoordinateUtil::addFreqAxis(cSys2); CoordinateUtil::addIQUVAxis(cSys2); Bool ok = cSys1.worldMap(wmap, wtranspose, refChange, cSys2); Bool ok2 = cSys1.pixelMap(pmap, ptranspose, cSys2); list (ok, ok2, wmap, wtranspose, pmap, ptranspose, cSys1, cSys2); cout << endl << endl; } { cout << "3D [ra, dec, spec] & 4D [ra, dec, stokes, spec]" << endl; CoordinateSystem cSys1 = CoordinateUtil::defaultCoords3D(); CoordinateSystem cSys2; CoordinateUtil::addDirAxes(cSys2); CoordinateUtil::addIQUVAxis(cSys2); CoordinateUtil::addFreqAxis(cSys2); Bool ok = cSys1.worldMap(wmap, wtranspose, refChange, cSys2); Bool ok2 = cSys1.pixelMap(pmap, ptranspose, cSys2); list (ok, ok2, wmap, wtranspose, pmap, ptranspose, cSys1, cSys2); cout << endl << endl; } } catch (std::exception& x) { cerr << "aipserror: error " << x.what() << endl; return 1; } return 0; } void list (Bool ok, Bool ok2, Vector& wmap, Vector& wtranspose, Vector& pmap, Vector& ptranspose, CoordinateSystem& cSys1, CoordinateSystem& cSys2) { cout << endl; if (!ok) { cout << "worldMap failed with message " << cSys1.errorMessage() << endl; } if (!ok2) { cout << "pixelMap failed with message " << cSys1.errorMessage() << endl; } cout << "cSys1.worldAxisNames = " << cSys1.worldAxisNames() << endl; cout << "cSys1.refpix = " << cSys1.referencePixel() << endl; cout << "cSys2.worldAxisNames = " << cSys2.worldAxisNames() << endl; cout << "cSys2.refpix = " << cSys2.referencePixel() << endl; // cout << "world map = " << wmap << endl; cout << "pixel map = " << pmap << endl; cout << "world transpose = " << wtranspose << endl; cout << "pixel transpose = " << ptranspose << endl; } casacore-3.7.1/coordinates/Coordinates/test/tCoordinate.cc000066400000000000000000000053341476623553700236560ustar00rootroot00000000000000//# tCoordinate.cc: Test program for Coordinate //# Copyright (C) 1998,1999,2000,2001,2002,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include #include #include #include int main() { try { String type = Coordinate::typeToString(Coordinate::DIRECTION); if (type!=String("Direction")) { throw(AipsError("Failed typeToString for DIRECTION")); } // type = Coordinate::typeToString(Coordinate::SPECTRAL); if (type!=String("Spectral")) { throw(AipsError("Failed typeToString for SPECTRAL")); } // type = Coordinate::typeToString(Coordinate::TABULAR); if (type!=String("Tabular")) { throw(AipsError("Failed typeToString for TABULAR")); } // type = Coordinate::typeToString(Coordinate::STOKES); if (type!=String("Stokes")) { throw(AipsError("Failed typeToString for STOKES")); } // type = Coordinate::typeToString(Coordinate::QUALITY); if (type!=String("Quality")) { throw(AipsError("Failed typeToString for QUALITY")); } // type = Coordinate::typeToString(Coordinate::LINEAR); if (type!=String("Linear")) { throw(AipsError("Failed typeToString for LINEAR")); } // type = Coordinate::typeToString(Coordinate::COORDSYS); if (type!=String("System")) { throw(AipsError("Failed typeToString for COORDSYS")); } } catch (std::exception& x) { cerr << "aipserror: error " << x.what() << endl; return (1); } cout << "ok" << endl; return (0); } casacore-3.7.1/coordinates/Coordinates/test/tCoordinateSystem.cc000066400000000000000000002620611476623553700250650ustar00rootroot00000000000000//# tCoordinateSystem.cc: Test program for CoordinateSystem //# Copyright (C) 1998,1999,2000,2001,2002,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include DirectionCoordinate makeDirectionCoordinate(Bool unitsAreDegrees=True, MDirection::Types type=MDirection::J2000); SpectralCoordinate makeSpectralCoordinate (); StokesCoordinate makeStokesCoordinate(Bool silly=True); QualityCoordinate makeQualityCoordinate(); LinearCoordinate makeLinearCoordinate(uInt nAxes=2); TabularCoordinate makeTabularCoordinate(); CoordinateSystem makeCoordinateSystem(uInt& nCoords, Vector& types, Vector& sTypes, uInt& iDC, uInt& iSpC, uInt& iTC, uInt& iStC, uInt& iQuC, uInt& iLC, DirectionCoordinate& dC, SpectralCoordinate& spC, TabularCoordinate& tC, StokesCoordinate& stC, QualityCoordinate& quC, LinearCoordinate& lC); void doit (CoordinateSystem& lc, uInt nCoords, const Vector& types, const Vector& sTypes, const uInt iDC, const uInt iSpC, const uInt iTC, const uInt iStC, const uInt iQuC, const uInt iLC, const DirectionCoordinate&, const SpectralCoordinate&, const TabularCoordinate&, const StokesCoordinate&, const QualityCoordinate&, const LinearCoordinate&); void doit2 (CoordinateSystem& cSys); void doit3 (CoordinateSystem& cSys); void doit4 (); void doit5 (); void doit6 (); void doit7 (); void verifyCAS3264 (); void spectralAxisNumber(); void polarizationAxisNumber(); int main() { try { uInt nCoords; Vector types; Vector sTypes; DirectionCoordinate dC; SpectralCoordinate spC; TabularCoordinate tC; StokesCoordinate stC = makeStokesCoordinate(); // No default constrcutor QualityCoordinate quC = makeQualityCoordinate(); // No default constrcutor LinearCoordinate lC; uInt iDC; uInt iSpC; uInt iTC; uInt iStC; uInt iQuC; uInt iLC; { CoordinateSystem cSys = makeCoordinateSystem(nCoords, types, sTypes, iDC, iSpC, iTC, iStC, iQuC, iLC, dC, spC, tC, stC, quC, lC); } { CoordinateSystem cSys1 = makeCoordinateSystem(nCoords, types, sTypes, iDC, iSpC, iTC, iStC, iQuC, iLC, dC, spC, tC, stC, quC, lC); CoordinateSystem cSys2 = makeCoordinateSystem(nCoords, types, sTypes, iDC, iSpC, iTC, iStC, iQuC, iLC, dC, spC, tC, stC, quC, lC); // near tests if (!cSys1.near(cSys1)) { String msg = String("Failed near test 0 because ") + cSys1.errorMessage(); throw(AipsError(msg)); } if (!cSys1.near(cSys2)) { String msg = String("Failed near test 1 because ") + cSys1.errorMessage(); throw(AipsError(msg)); } Vector excludeAxes(2); excludeAxes(0) = 0; excludeAxes(1) = 2; if (!cSys1.near(cSys2, excludeAxes)) { String msg = String("Failed near test 2 because ") + cSys1.errorMessage(); throw(AipsError(msg)); } // nearPixel tests if (!cSys1.nearPixel(cSys1)) { String msg = String("Failed nearPixel test 0 because ") + cSys1.errorMessage(); throw(AipsError(msg)); } if (!cSys1.nearPixel(cSys2)) { String msg = String("Failed nearPixel test 1 because ") + cSys1.errorMessage(); throw(AipsError(msg)); } // Vector refVal1(cSys1.referenceValue().copy()); Vector refPix1(cSys1.referencePixel().copy()); Vector inc1(cSys1.increment().copy()); Vector refVal2(cSys2.referenceValue().copy()); Vector refPix2(cSys2.referencePixel().copy()); Vector inc2(cSys2.increment().copy()); // refVal1(0) += inc1(0); cSys1.setReferenceValue(refVal1); if (cSys1.nearPixel(cSys2)) { String msg = String("Unexpectedly passed nearPixel test 2"); throw(AipsError(msg)); } refVal1(0) -= inc1(0); cSys1.setReferenceValue(refVal1); refVal2(0) += inc2(0); cSys2.setReferenceValue(refVal2); if (cSys1.nearPixel(cSys2)) { String msg = String("Unexpectedly passed nearPixel test 3"); throw(AipsError(msg)); } refVal2(0) -= inc2(0); cSys2.setReferenceValue(refVal2); // // refPix1(0) += 1; cSys1.setReferencePixel(refPix1); if (cSys1.nearPixel(cSys2)) { String msg = String("Unexpectedly passed nearPixel test 4"); throw(AipsError(msg)); } refPix1(0) -= 1; cSys1.setReferencePixel(refPix1); refPix2(0) += 1; cSys2.setReferencePixel(refPix2); if (cSys1.nearPixel(cSys2)) { String msg = String("Unexpectedly passed nearPixel test 5"); throw(AipsError(msg)); } refPix2(0) -= 1; cSys2.setReferencePixel(refPix2); //// inc1(0) *= 2.0; cSys1.setIncrement(inc1); if (cSys1.nearPixel(cSys2)) { String msg = String("Unexpectedly passed nearPixel test 6"); throw(AipsError(msg)); } inc1(0) /= 2.0; cSys1.setIncrement(inc1); inc2(0) *= 2.0; cSys2.setIncrement(inc2); if (cSys1.nearPixel(cSys2)) { String msg = String("Unexpectedly passed nearPixel test 7"); throw(AipsError(msg)); } inc2(0) /= 2.0; cSys2.setIncrement(inc2); //// Int pAxis = cSys1.nPixelAxes() - 2; cSys1.removePixelAxis(pAxis, 0.0); pAxis = 1; cSys1.removePixelAxis(pAxis, 0.0); if (cSys1.nearPixel(cSys2)) { String msg = String("Unexpectedly passed nearPixel test 8"); throw(AipsError(msg)); } // pAxis = cSys2.nPixelAxes() - 2; cSys2.removePixelAxis(pAxis, 0.0); pAxis = 1; cSys2.removePixelAxis(pAxis, 0.0); if (!cSys1.nearPixel(cSys2)) { String msg = String("Failed nearPixel test 9 because ") + cSys1.errorMessage(); throw(AipsError(msg)); } } { CoordinateSystem cSys = makeCoordinateSystem(nCoords, types, sTypes, iDC, iSpC, iTC, iStC, iQuC, iLC, dC, spC, tC, stC, quC, lC); String oneStokes = cSys.stokesAtPixel((uInt)1); if (oneStokes!="RL") { throw(AipsError("Stokes value at pixel 1 is wrong!")); } String oneQual = cSys.qualityAtPixel((uInt)1); if (oneQual!="ERROR") { throw(AipsError("Quality value at pixel 1 is wrong!")); } } { CoordinateSystem cSys = makeCoordinateSystem(nCoords, types, sTypes, iDC, iSpC, iTC, iStC, iQuC, iLC, dC, spC, tC, stC, quC, lC); doit(cSys, nCoords, types, sTypes, iDC, iSpC, iTC, iStC, iQuC, iLC, dC, spC, tC, stC, quC, lC); } { CoordinateSystem cSys = makeCoordinateSystem(nCoords, types, sTypes, iDC, iSpC, iTC, iStC, iQuC, iLC, dC, spC, tC, stC, quC, lC); doit2(cSys); } { CoordinateSystem cSys = makeCoordinateSystem(nCoords, types, sTypes, iDC, iSpC, iTC, iStC, iQuC, iLC, dC, spC, tC, stC, quC, lC); doit3(cSys); } { doit4(); } { doit5(); } { // doit6(); } { CoordinateSystem cSys = makeCoordinateSystem(nCoords, types, sTypes, iDC, iSpC, iTC, iStC, iQuC, iLC, dC, spC, tC, stC, quC, lC); // LogOrigin lO(String("tCoordinateSystem"), String("main()"), WHERE); LogIO os(lO); IPosition s1, s2; cSys.list(os, MDoppler::RADIO, s1, s2); } { verifyCAS3264(); } { cout << "*** Test getWorldAxisOrder" << endl; CoordinateSystem csys = CoordinateUtil::defaultCoords(4); Vector myNames(1, "spectral"); Bool ok = True; try { Vector axes = csys.getWorldAxesOrder(myNames, False, False); ok = False; } catch (const AipsError& e) {} AlwaysAssert(ok, AipsError); Vector axes = csys.getWorldAxesOrder(myNames, False, True); AlwaysAssert(axes[0] == 3, AipsError); } { spectralAxisNumber(); polarizationAxisNumber(); } { cout << "*** test isDirectionAbscissaLongitude()" << endl; CoordinateSystem csys = CoordinateUtil::defaultCoords(4); AlwaysAssert(csys.isDirectionAbscissaLongitude(), AipsError); Vector worldOrder(4); indgen(worldOrder); Vector pixelOrder = worldOrder.copy(); pixelOrder[0] = 1; pixelOrder[1] = 0; csys.transpose(worldOrder, pixelOrder); AlwaysAssert(! csys.isDirectionAbscissaLongitude(), AipsError); csys.removePixelAxis(0, 0.0); try { csys.removePixelAxis(0, 0.0); // expected exception not thrown if we get here AlwaysAssert(False, AipsError); } catch (const std::exception& x) {} csys = CoordinateSystem(); CoordinateUtil::addFreqAxis(csys); try { csys.removePixelAxis(0, 0.0); // expected exception not thrown if we get here AlwaysAssert(False, AipsError); } catch (const std::exception& x) {} csys = CoordinateUtil::defaultCoords(4); indgen(pixelOrder); pixelOrder[0] = 1; pixelOrder[1] = 2; pixelOrder[2] = 0; csys.transpose(worldOrder, pixelOrder); AlwaysAssert(! csys.isDirectionAbscissaLongitude(), AipsError); csys = CoordinateUtil::defaultCoords(4); indgen(pixelOrder); pixelOrder[1] = 2; pixelOrder[2] = 1; csys.transpose(worldOrder, pixelOrder); AlwaysAssert(csys.isDirectionAbscissaLongitude(), AipsError); } { cout << "*** test directionAxesNumbers" << endl; CoordinateSystem csys = CoordinateUtil::defaultCoords(4); Vector dan = csys.directionAxesNumbers(); AlwaysAssert(dan[0] == 0 && dan[1] == 1, AipsError); Vector worldOrder(4); indgen(worldOrder); Vector pixelOrder = worldOrder.copy(); pixelOrder[0] = 1; pixelOrder[1] = 3; pixelOrder[3] = 0; csys.transpose(worldOrder, pixelOrder); dan = csys.directionAxesNumbers(); AlwaysAssert(dan[0] == 3 && dan[1] == 0, AipsError); } { cout << "*** test setRestFrequency()" << endl; Bool ok=False; String errorMsg; CoordinateSystem csys = CoordinateUtil::defaultCoords3D(); Int pixelAxis, worldAxis, coordinate; CoordinateUtil::findSpectralAxis(pixelAxis, worldAxis, coordinate, csys); // make sure negative rest frequency is refused Quantity freq(-100, "GHz"); ok = csys.setRestFrequency (errorMsg, freq); AlwaysAssertExit (!ok); cerr << "Frequency correctly NOT set with message = " << errorMsg << endl; Double x; setNaN(x); freq.setValue(x); ok = csys.setRestFrequency (errorMsg, freq); AlwaysAssertExit (!ok); cerr << "Frequency correctly NOT set with message = " << errorMsg << endl; setInf(x); freq.setValue(x); ok = csys.setRestFrequency (errorMsg, freq); AlwaysAssertExit (!ok); cerr << "Frequency correctly NOT set with message = " << errorMsg << endl; Quantity wavelength(0, "nm"); ok = csys.setRestFrequency (errorMsg, wavelength); AlwaysAssertExit(!ok); cerr << "Frequency correctly NOT set with message = " << errorMsg << endl; Quantity vel(100, "km/s"); ok = csys.setRestFrequency (errorMsg, vel); AlwaysAssertExit(!ok); cerr << "Frequency correctly NOT set with message = " << errorMsg << endl; freq = Quantity(100, "GHz"); ok = csys.setRestFrequency (errorMsg, freq); AlwaysAssertExit (ok); cerr << "Frequency set to: " << freq << endl; const SpectralCoordinate &sCoo1 = csys.spectralCoordinate(coordinate); AlwaysAssertExit(near(1.0e+11, sCoo1.restFrequency(), 1.0e-8)); freq.setValue(90); ok = csys.setRestFrequency (errorMsg, freq); AlwaysAssertExit (ok); cerr << "Frequency set to: " << freq << endl; const SpectralCoordinate &sCoo2 = csys.spectralCoordinate(coordinate); AlwaysAssertExit(near(0.9e+11, sCoo2.restFrequency(), 1.0e-8)); cerr << "The input was verified" << endl; wavelength = Quantity(1, "mm"); ok = csys.setRestFrequency (errorMsg, wavelength); AlwaysAssertExit (ok); cerr << "Frequency set to: " << wavelength << endl; const SpectralCoordinate &sCoo3 = csys.spectralCoordinate(coordinate); AlwaysAssertExit(near(QC::c( ).getValue()/1.0e-03, sCoo3.restFrequency(), 1.0e-8)); cerr << "The input was verified" << endl; } { cout << "*** test toWorld using both native and conversion layer frames" << endl; CoordinateSystem csys = CoordinateUtil::defaultCoords(4); Matrix xform(2, 2, 0); xform.diagonal() = 1; DirectionCoordinate dc( MDirection::J2000, Projection::SIN, Quantity(0, "deg"), Quantity(0, "deg"), Quantity(-1, "arcsec"), Quantity(1, "arcsec"), xform, 0, 0 ); dc.setReferenceConversion(MDirection::GALACTIC); SpectralCoordinate sc( MFrequency::LSRK, Quantity(1500, "MHz"), Quantity(1, "kHz"), 0, Quantity(1500, "MHz") ); MEpoch epoch(Quantity(60000, "d"), MEpoch::UTC); MPosition position( Quantity(10, "m"), Quantity(135, "deg"), Quantity(40, "deg"), MPosition::ITRF ); MDirection direction(Quantity(20, "deg"), Quantity(50, "deg"), MDirection::J2000); sc.setReferenceConversion(MFrequency::CMB, epoch, position, direction); csys.replaceCoordinate(dc, 0); csys.replaceCoordinate(sc, 2); Vector pixel(4, 0); pixel[0] = 60; pixel[1] = 60; pixel[3] = 10; Vector world(4); csys.toWorld(world, pixel); AlwaysAssert(near(world[0], 1.6811, 1e-5), AipsError); AlwaysAssert(near(world[1], -1.05011, 1e-5), AipsError); AlwaysAssert(near(world[3], 1.50121e+09, 1e-5), AipsError); csys.toWorld(world, pixel, True); AlwaysAssert(near(world[0], 1.6811, 1e-5), AipsError); AlwaysAssert(near(world[1], -1.05011, 1e-5), AipsError); AlwaysAssert(near(world[3], 1.50121e+09, 1e-5), AipsError); csys.toWorld(world, pixel, False); AlwaysAssert(near(world[0], 6.28289, 1e-5), AipsError); AlwaysAssert(near(world[1], 2.90888e-4, 1e-5), AipsError); AlwaysAssert(world[3] == 1.50001e+09, AipsError); } } catch (const std::exception& x) { cerr << "Error " << x.what() << endl; return (1); } cout << "ok" << endl; return (0); } void doit (CoordinateSystem& cSys, uInt nCoords, const Vector& types, const Vector& sTypes, const uInt iDC, const uInt iSpC, const uInt iTC, const uInt iStC, const uInt iQuC, const uInt iLC, const DirectionCoordinate& dC, const SpectralCoordinate& spC, const TabularCoordinate& tC, const StokesCoordinate& stC, const QualityCoordinate& quC, const LinearCoordinate& lC) { // Test copy constructor { CoordinateSystem cSys2(cSys); if (!cSys.near(cSys2)) { String msg = String("Failed copy constructor test because ") + cSys.errorMessage(); throw(AipsError(msg)); } } // Test assignment { CoordinateSystem cSys2; cSys2 = cSys; if (!cSys.near(cSys2)) { String msg = String("Failed assignment test because ") + cSys.errorMessage(); throw(AipsError(msg)); } } // Test member functions if (cSys.nCoordinates() != nCoords) { throw(AipsError("Failed nCoordinates test")); } if (cSys.showType() != "System") { throw(AipsError("Failed showType test")); } if (cSys.type() != Coordinate::COORDSYS) { throw(AipsError("Failed type test 1")); } for (uInt i=0; i worldAxes = cSys.worldAxes(coordinate); if (axisInCoordinate >= Int(worldAxes.nelements()) || axisInCoordinate<0) { throw(AipsError("Failed findWorldAxis test 2")); } if (worldAxes.nelements() == cSys.coordinate(coordinate).nWorldAxes()) { Bool ok = False; // Try and find the original world axis (i) in this list // of world axes // for (uInt j=0; j pixelAxes = cSys.pixelAxes(coordinate); if (axisInCoordinate >= Int(pixelAxes.nelements()) || axisInCoordinate<0) { throw(AipsError("Failed findPixelAxis test 2")); } if (pixelAxes.nelements() == cSys.coordinate(coordinate).nPixelAxes()) { Bool ok = False; // Try and find the original pixel axis (i) in this list // of pixel axes // for (uInt j=0; j pixelAxes = cSys.pixelAxes(i); Vector worldAxes = cSys.worldAxes(i); for (uInt j=0; j worldAxisNames = cSys.worldAxisNames(); Vector worldAxisUnits = cSys.worldAxisUnits(); Vector refValues = cSys.referenceValue(); Vector inc = cSys.increment(); // for (uInt i=0; i worldAxes = cSys.worldAxes(i); Vector worldAxisNames2 = cSys.coordinate(i).worldAxisNames(); Vector worldAxisNames3(worldAxes.nelements()); Vector worldAxisUnits2 = cSys.coordinate(i).worldAxisUnits(); Vector worldAxisUnits3(worldAxes.nelements()); Vector refValues2 = cSys.coordinate(i).referenceValue(); Vector refValues3(worldAxes.nelements()); Vector inc2 = cSys.coordinate(i).increment(); Vector inc3(worldAxes.nelements()); // for (uInt j=0; j worldAxes = cSys.worldAxes(iC); worldAxisUnits(worldAxes(0)) = "deg"; worldAxisUnits(worldAxes(1)) = "arcmin"; if (!cSys.setWorldAxisUnits(worldAxisUnits)) { throw(AipsError(String("Failed to set axis units because") + cSys.errorMessage())); } if (!allEQ(worldAxisUnits, cSys.worldAxisUnits())) { throw(AipsError("Failed axis units set/recovery test")); } } Vector bogus = worldAxisUnits.copy(); bogus.resize(worldAxisUnits.size() - 1); AlwaysAssert(! cSys.setWorldAxisUnits(bogus), AipsError); try { cSys.setWorldAxisUnits(bogus, True); // this should have thrown an exception, if not, its a failure AlwaysAssert(False, AipsError); } catch (const std::exception& x) {} // // // Now check the pixel axis descriptors // Vector refPixels = cSys.referencePixel(); for (uInt i=0; i pixelAxes = cSys.pixelAxes(i); Vector refPixels2 = cSys.coordinate(i).referencePixel(); Vector refPixels3(pixelAxes.nelements()); // for (uInt j=0; j xform = cSys.linearTransform(); xform(0,0) = 10.0; // this affects the direction coordinate which enforces that // the linear transform rows pertaining to it obey // for each i: xform(i,0)^2 + xform(i,1)^s == 1.0 // The normalization is tranferred to the increment. Matrix xformOut(cSys.nWorldAxes(), cSys.nWorldAxes()); xformOut = 0.; xformOut.diagonal() = 1.0; Vector cdeltOut; cdeltOut = cSys.increment(); cdeltOut(0) *= 10.; if (!cSys.setLinearTransform(xform)) { throw(AipsError(String("Failed to set linear transform because") + cSys.errorMessage())); } if (!allNear(xformOut, cSys.linearTransform(), 1e-6)) { throw(AipsError("Failed linear transform set/recovery test (wrong resulting xform)")); } if (!allNear(cdeltOut, cSys.increment(), 1E-6)) { throw(AipsError("Failed linear transform set/recovery test (wrong resulting cdelt)")); } // // Test FITS interface. Do this with a CS without a TabularCoordinate // because that is not reflected back by the FITS conversion // { CoordinateSystem cSys3; Vector header; Record rec; IPosition shape; Int stokesFITSValue = -1; // if (CoordinateSystem::fromFITSHeader(stokesFITSValue, cSys3, rec, header, shape, 0)) { throw(AipsError("Unexpectedly did not fail fromFITSHeader (0)")); } } { CoordinateSystem cSys2; cSys2.addCoordinate(makeDirectionCoordinate(False)); StokesCoordinate stokesCoord = makeStokesCoordinate(False); uInt shapeStokes = stokesCoord.stokes().nelements(); uInt stokesAxis = 2; QualityCoordinate qualCoord = makeQualityCoordinate(); uInt shapeQual = qualCoord.quality().nelements(); uInt qualAxis = 3; cSys2.addCoordinate(stokesCoord); cSys2.addCoordinate(qualCoord); cSys2.addCoordinate(makeSpectralCoordinate()); cSys2.addCoordinate(makeLinearCoordinate()); // Record rec; IPosition shape(cSys2.nPixelAxes(),64); shape(stokesAxis) = shapeStokes; shape(qualAxis) = shapeQual; if (!cSys2.toFITSHeader(rec, shape, True, 'c', False, True, True)) { throw(AipsError(String("Failed to convert to FITS header (1)"))); } // Assigning cSys3 rather than leaving it empty will force testing of // more code in fromFITSHeader // This part still needs to be fixed. At the moment it doesn't use fromFITSHeader // correctly // CoordinateSystem cSys3 = CoordinateUtil::defaultCoords2D(); // Vector header; // Int stokesFITSValue = -1; // if (!CoordinateSystem::fromFITSHeader(stokesFITSValue, cSys3, rec, header, // shape, 0)) { // throw(AipsError("Failed to convert from FITS header (1)")); // } // if (!cSys2.near(cSys3)) { // msg = String("Failed to/fromFITS consistency test (1) because ") + // cSys2.errorMessage(); // throw(AipsError(msg)); // } } // Do lots of Stokes combinations to exercise as much code // as possible { CoordinateSystem cSys2; Vector whichStokes(4); whichStokes(0) = Stokes::I; whichStokes(1) = Stokes::Q; whichStokes(2) = Stokes::U; whichStokes(3) = Stokes::V; StokesCoordinate stokesCoord(whichStokes); uInt shapeStokes = stokesCoord.stokes().nelements(); uInt stokesAxis = 0; cSys2.addCoordinate(stokesCoord); // Record rec; IPosition shape(cSys2.nPixelAxes(),64); shape(stokesAxis) = shapeStokes; if (!cSys2.toFITSHeader(rec, shape, True, 'c', False, True, True)) { throw(AipsError(String("Failed to convert to FITS header (2)"))); } // This part still needs to be fixed. At the moment it doesn't use fromFITSHeader // correctly // CoordinateSystem cSys3; // Vector header; // Int stokesFITSValue = -1; // if (!CoordinateSystem::fromFITSHeader(stokesFITSValue, cSys3, rec, header, shape, 0)) { // throw(AipsError("Failed to convert from FITS header (2)")); // } // if (!cSys2.near(cSys3)) { // msg = String("Failed to/fromFITS consistency test (2) because ") + // cSys2.errorMessage(); // throw(AipsError(msg)); // } } { CoordinateSystem cSys2; Vector whichStokes(4); whichStokes(0) = Stokes::RR; whichStokes(1) = Stokes::LL; whichStokes(2) = Stokes::RL; whichStokes(3) = Stokes::LR; StokesCoordinate stokesCoord(whichStokes); uInt shapeStokes = stokesCoord.stokes().nelements(); uInt stokesAxis = 0; cSys2.addCoordinate(stokesCoord); // Record rec; IPosition shape(cSys2.nPixelAxes(),64); shape(stokesAxis) = shapeStokes; if (!cSys2.toFITSHeader(rec, shape, True, 'c', False, True, True)) { throw(AipsError(String("Failed to convert to FITS header (3)"))); } // This part still needs to be fixed. At the moment it doesn't use fromFITSHeader // correctly // CoordinateSystem cSys3; // Vector header; // Int stokesFITSValue = -1; // if (!CoordinateSystem::fromFITSHeader(stokesFITSValue, cSys3, rec, header, shape, 0)) { // throw(AipsError("Failed to convert from FITS header (3)")); // } // if (!cSys2.near(cSys3)) { // msg = String("Failed to/fromFITS consistency test (3) because ") + // cSys2.errorMessage(); // throw(AipsError(msg)); // } } { CoordinateSystem cSys2; Vector whichStokes(4); whichStokes(0) = Stokes::XX; whichStokes(1) = Stokes::YY; whichStokes(2) = Stokes::XY; whichStokes(3) = Stokes::YX; StokesCoordinate stokesCoord(whichStokes); uInt shapeStokes = stokesCoord.stokes().nelements(); uInt stokesAxis = 0; cSys2.addCoordinate(stokesCoord); // Record rec; IPosition shape(cSys2.nPixelAxes(),64); shape(stokesAxis) = shapeStokes; if (!cSys2.toFITSHeader(rec, shape, True, 'c', False, True, True)) { throw(AipsError(String("Failed to convert to FITS header (4)"))); } // This part still needs to be fixed. At the moment it doesn't use fromFITSHeader // correctly // CoordinateSystem cSys3; // Vector header; // Int stokesFITSValue = -1; // if (!CoordinateSystem::fromFITSHeader(stokesFITSValue, cSys3, rec, header, shape, 0)) { // throw(AipsError("Failed to convert from FITS header (4)")); // } // if (!cSys2.near(cSys3)) { // msg = String("Failed to/fromFITS consistency test (4) because ") + // cSys2.errorMessage(); // throw(AipsError(msg)); // } } // // Test record saving. // Record rec; if (!cSys.save(rec, "coordsys")) { throw(AipsError("Saving to Record failed")); } // This part still needs to be fixed. At the moment it doesn't use fromFITSHeader // correctly // CoordinateSystem* pcSys = CoordinateSystem::restore(rec, "coordsys"); // if (!pcSys->near(cSys, 1e-6)) { // throw(AipsError("Reflection through record interface (1) failed")); // } // delete pcSys; // // Record rec2 = rec.asRecord("coordsys"); // pcSys = CoordinateSystem::restore(rec2, ""); // if (!pcSys->near(cSys, 1e-6)) { // throw(AipsError("Reflection through record interface (2) failed")); // } // delete pcSys; // // Test clone // Coordinate* pcSys2 = cSys.clone(); if (!pcSys2->near(cSys, 1e-6)) { throw(AipsError("Clone function failed")); } delete pcSys2; // // Coordinate restoration // { CoordinateSystem cSys2, cSys3; cSys2.addCoordinate(makeDirectionCoordinate()); cSys2.addCoordinate(makeStokesCoordinate(False)); cSys2.addCoordinate(makeQualityCoordinate()); cSys2.addCoordinate(makeLinearCoordinate()); cSys3 = cSys2; // Vector wOrder(cSys2.nWorldAxes()); Vector pOrder(cSys2.nPixelAxes()); for (uInt i=0; i originShift(cSys.nPixelAxes()); Vector pixinc(cSys.nPixelAxes()); // { originShift = 1.0; pixinc = 1.0; Vector newShape; // CoordinateSystem cSys2 = cSys.subImage(originShift, pixinc, newShape); if (cSys.nCoordinates() != cSys2.nCoordinates()) { throw(AipsError("Failed originShift creation test 1")); } Vector pixel = cSys.referencePixel(); Vector pixel2 = cSys2.referencePixel() + 1.0; pixel2(stokesPixelAxis) = pixel(stokesPixelAxis); pixel2(qualityPixelAxis) = pixel(qualityPixelAxis); if (!allNear(pixel, pixel2, 1e-6)) { throw(AipsError("Failed originShift test 1")); } // CoordinateSystem cSys3(cSys); cSys3.subImageInSitu (originShift, pixinc, newShape); if (cSys.nCoordinates() != cSys3.nCoordinates()) { throw(AipsError("Failed originShift creation test 2")); } Vector pixel3 = cSys3.referencePixel() + 1.0; pixel3(stokesPixelAxis) = pixel(stokesPixelAxis); pixel3(qualityPixelAxis) = pixel(qualityPixelAxis); if (!allNear(pixel, pixel3, 1e-6)) { throw(AipsError("Failed originShift test 2")); } } // { originShift = 0.0; pixinc = 2.0; Vector newShape; // { CoordinateSystem cSys2 = cSys.subImage(originShift, pixinc, newShape); Vector oldStokes = cSys.stokesCoordinate(whichStokesCoordinate).stokes(); Vector newStokes = cSys2.stokesCoordinate(whichStokesCoordinate).stokes(); Vector oldQual = cSys.qualityCoordinate(whichQualityCoordinate).quality(); Vector newQual = cSys2.qualityCoordinate(whichQualityCoordinate).quality(); Vector newStokes2 = oldStokes(IPosition(1,0), IPosition(1,oldStokes.nelements()-1), IPosition(1,2)); Vector newQual2 = oldQual(IPosition(1,0), IPosition(1,oldQual.nelements()-1), IPosition(1,2)); if (!allEQ(newStokes, newStokes2)) { throw(AipsError("Failed Stokes originShift Stokes test")); } if (!allEQ(newQual, newQual2)) { throw(AipsError("Failed Quality originShift Quality test")); } } // { CoordinateSystem cSys2(cSys); cSys2.subImageInSitu (originShift, pixinc, newShape); Vector oldStokes = cSys.stokesCoordinate(whichStokesCoordinate).stokes(); Vector newStokes = cSys2.stokesCoordinate(whichStokesCoordinate).stokes(); Vector oldQual = cSys.qualityCoordinate(whichQualityCoordinate).quality(); Vector newQual = cSys2.qualityCoordinate(whichQualityCoordinate).quality(); Vector newStokes2 = oldStokes(IPosition(1,0), IPosition(1,oldStokes.nelements()-1), IPosition(1,2)); Vector newQual2 = oldQual(IPosition(1,0), IPosition(1,oldQual.nelements()-1), IPosition(1,2)); if (!allEQ(newStokes, newStokes2)) { throw(AipsError("Failed Stokes originShift Stokes test")); } if (!allEQ(newQual, newQual2)) { throw(AipsError("Failed Quality originShift Quality test")); } } } } } void doit2 (CoordinateSystem& cSys) { Int stokesPixelAxis = -1; Int stokesWorldAxis = -1; Int iC = cSys.findCoordinate(Coordinate::STOKES); if (iC>=0) { stokesPixelAxis = cSys.pixelAxes(iC)(0); stokesWorldAxis = cSys.worldAxes(iC)(0); } Int qualityPixelAxis = -1; Int qualityWorldAxis = -1; iC = cSys.findCoordinate(Coordinate::QUALITY); if (iC>=0) { qualityPixelAxis = cSys.pixelAxes(iC)(0); qualityWorldAxis = cSys.worldAxes(iC)(0); } // // Test conversion // Vector pixel(cSys.referencePixel()), world; if (!cSys.toWorld(world, pixel)) { throw(AipsError(String("toWorld #1 conversion failed because ") + cSys.errorMessage())); } if (!allNear(world, cSys.referenceValue(), 1e-6)) { throw(AipsError("Coordinate conversion gave wrong results")); } if (!cSys.toPixel(pixel, world)) { throw(AipsError(String("toPixel conversion failed because ") + cSys.errorMessage())); } if (!allNear(pixel, cSys.referencePixel(), 1e-6)) { throw(AipsError("Coordinate conversion gave wrong results")); } // pixel(0) = 123.0; Vector pixel2(pixel.copy()); if (!cSys.toWorld(world, pixel)) { throw(AipsError(String("toWorld #2 conversion failed because ") + cSys.errorMessage())); } if (!cSys.toPixel(pixel, world)) { throw(AipsError(String("toPixel #3 conversion failed because ") + cSys.errorMessage())); } if (!allNear(pixel2, pixel, 1e-6)) { throw(AipsError("Coordinate reflection gave wrong results")); } // pixel = 2.0; pixel(stokesPixelAxis) = 0.0; pixel(qualityPixelAxis)= 0.0; IPosition iPixel(pixel.nelements()); for (uInt i=0; i world2; if (!cSys.toWorld(world, pixel)) { throw(AipsError(String("toWorld #4 conversion failed because ") + cSys.errorMessage())); } if (!cSys.toWorld(world2, iPixel)) { throw(AipsError(String("toWorld #5 conversion failed because ") + cSys.errorMessage())); } if (!allNear(world, world2, 1e-6)) { throw(AipsError("toWorld consistency test failed")); } // Vector failures; const uInt nBatch = 3; Matrix pixel3(cSys.nPixelAxes(), nBatch); Matrix world3(cSys.nWorldAxes(), nBatch); for (uInt i=0; i refPix = cSys.referencePixel(); Vector pixel4 = refPix.copy(); cSys.makePixelRelative(pixel4); if (!allNear(pixel4, 0.0, 1e-6)) { throw(AipsError("Coordinate makePixelRelative 1 gave wrong results")); } // pixel4 = refPix + 1.0; Vector tmp = pixel4.copy(); cSys.makePixelRelative(pixel4); if (!allNear(pixel4, 1.0, 1e-6)) { throw(AipsError("Coordinate makePixelRelative 2 gave wrong results")); } cSys.makePixelAbsolute (pixel4); if (!allNear(pixel4, tmp, 1e-6)) { throw(AipsError("Coordinate makePixelAbsolute 1 gave wrong results")); } // pixel4 = refPix - 1.0; tmp = pixel4; cSys.makePixelRelative(pixel4); if (!allNear(pixel4, -1.0, 1e-6)) { throw(AipsError("Coordinate makePixelRelative 3 gave wrong results")); } cSys.makePixelAbsolute (pixel4); if (!allNear(pixel4, tmp, 1e-6)) { throw(AipsError("Coordinate makePixelAbsolute 2 gave wrong results")); } } // relative/absolute world { Vector result = cSys.referenceValue().copy(); // Vector refVal = cSys.referenceValue(); Vector world4 = refVal.copy(); cSys.makeWorldRelative(world4); result = 0.0; result(stokesWorldAxis) = refVal(stokesWorldAxis); result(qualityWorldAxis) = refVal(qualityWorldAxis); if (!allNearAbs(world4, result, 1e-6)) { throw(AipsError("makeWorldRelative 1 gave wrong results")); } // Vector incr = cSys.increment(); world4 = refVal + incr; Vector tmp = world4.copy(); cSys.makeWorldRelative(world4); cSys.makeWorldAbsolute (world4); if (!allNearAbs(world4, tmp, 1e-6)) { throw(AipsError("makeWorldAbsolute/Relative reflection 1 failed")); } // world4 = refVal - incr; tmp = world4; cSys.makeWorldRelative(world4); cSys.makeWorldAbsolute (world4); if (!allNearAbs(world4, tmp, 1e-6)) { throw(AipsError("Coordinate makeWorldAbsolute/Reflection 2 failed")); } } // // Formatting // { Int iDC = cSys.findCoordinate(Coordinate::DIRECTION); Vector worldAxes = cSys.worldAxes(iDC); Vector worldAxisUnits = cSys.worldAxisUnits(); worldAxisUnits(worldAxes(0)) = "rad"; worldAxisUnits(worldAxes(1)) = "rad"; cSys.setWorldAxisUnits(worldAxisUnits); // String unit("rad"); Double val = 0.12343; Quantum valq(0.12343, Unit("rad")); valq.convert(Unit("deg")); String str = cSys.format(unit, Coordinate::FIXED, val, worldAxes(0), True, True, 4); String str2 = cSys.formatQuantity(unit, Coordinate::FIXED, valq, worldAxes(0), True, True, 4); if (str != "0.1234" || str2 != "0.1234") { throw(AipsError("Failed format test 1")); } str = cSys.format(unit, Coordinate::FIXED, val, worldAxes(1), True, True, 4); str2 = cSys.formatQuantity(unit, Coordinate::FIXED, valq, worldAxes(1), True, True, 4); if (str != "0.1234" || str2!="0.1234") { throw(AipsError("Failed format test 2")); } // str = cSys.format(unit, Coordinate::SCIENTIFIC, val, worldAxes(0), True, True, 4); str2 = cSys.formatQuantity(unit, Coordinate::SCIENTIFIC, valq, worldAxes(0), True, True, 4); if (str != "1.2343e-01" || str2 != "1.2343e-01") { throw(AipsError("Failed format test 3")); } str = cSys.format(unit, Coordinate::SCIENTIFIC, val, worldAxes(1), True, True, 4); str2 = cSys.formatQuantity(unit, Coordinate::SCIENTIFIC, valq, worldAxes(1), True, True, 4); if (str != "1.2343e-01" || str2 != "1.2343e-01") { throw(AipsError("Failed format test 4")); } } { Int iSpC = cSys.findCoordinate(Coordinate::SPECTRAL); Vector worldAxes = cSys.worldAxes(iSpC); // String unit; Double val = 0.12343; Quantum valq(0.12343, Unit("rad")); valq.convert(Unit("deg")); String str = cSys.format(unit, Coordinate::FIXED, val, worldAxes(0), True, True, 4); String str2 = cSys.formatQuantity(unit, Coordinate::FIXED, valq, worldAxes(0), True, True, 4); if (str != "0.1234" || str2 != "0.1234") { throw(AipsError("Failed format test 5")); } } } void doit3 (CoordinateSystem& cSys) { // // Transposition // { Vector newWorldOrder(cSys.nWorldAxes()); Vector newPixelOrder(cSys.nPixelAxes()); for (uInt i=0; i worldAxes = cSys.worldAxes(iDC); Vector pixelAxes = cSys.pixelAxes(iDC); Vector newWorldAxes(worldAxes.copy()); Vector newPixelAxes(pixelAxes.copy()); newWorldOrder(worldAxes(0)) = worldAxes(1); newWorldOrder(worldAxes(1)) = worldAxes(0); newPixelOrder(pixelAxes(0)) = pixelAxes(1); newPixelOrder(pixelAxes(1)) = pixelAxes(0); newWorldAxes(0) = worldAxes(1); newWorldAxes(1) = worldAxes(0); newPixelAxes(0) = pixelAxes(1); newPixelAxes(1) = pixelAxes(0); // cSys.transpose(newWorldOrder, newPixelOrder); if (!allEQ(newWorldAxes, cSys.worldAxes(iDC)) || !allEQ(newPixelAxes, cSys.pixelAxes(iDC))) { throw(AipsError("Failed transposition test")); } } // // World, pixel map // { CoordinateSystem cSys2; cSys2.addCoordinate(makeStokesCoordinate(False)); cSys2.addCoordinate(makeQualityCoordinate()); cSys2.addCoordinate(makeDirectionCoordinate(False, MDirection::B1950)); cSys2.addCoordinate(makeLinearCoordinate()); CoordinateSystem cSys3 = cSys2; cSys3.replaceCoordinate(makeDirectionCoordinate(False, MDirection::J2000),2); // Vector worldAxisMap, worldAxisTranspose; Vector pixelAxisMap, pixelAxisTranspose; Vector refChange; if (!cSys2.worldMap(worldAxisMap, worldAxisTranspose, refChange, cSys3)) { throw(AipsError("Failed to make world map 1")); } Vector wMap(cSys2.nWorldAxes()), wTranspose(cSys2.nWorldAxes()); for (uInt i=0; i pMap(cSys2.nPixelAxes()), pTranspose(cSys2.nPixelAxes()); for (uInt i=0; i newWorldOrder(cSys2.nWorldAxes()); Vector newPixelOrder(cSys2.nPixelAxes()); for (uInt i=0; i worldAxes = cSys2.worldAxes(iDC); Vector pixelAxes = cSys2.pixelAxes(iDC); newWorldOrder(worldAxes(0)) = worldAxes(1); newWorldOrder(worldAxes(1)) = worldAxes(0); newPixelOrder(pixelAxes(0)) = pixelAxes(1); newPixelOrder(pixelAxes(1)) = pixelAxes(0); // cSys2.transpose(newWorldOrder, newPixelOrder); // if (!cSys2.worldMap(worldAxisMap, worldAxisTranspose, refChange, cSys3)) { throw(AipsError("Failed to make world map 2")); } if (!cSys2.pixelMap(pixelAxisMap, pixelAxisTranspose, cSys3)) { throw(AipsError("Failed to make pixel map 2")); } Vector newMap(wMap.copy()); Vector newTranspose(worldAxisTranspose.copy()); newMap(worldAxes(0)) = newWorldOrder(worldAxes(0)); newMap(worldAxes(1)) = newWorldOrder(worldAxes(1)); newTranspose(worldAxes(0)) = newWorldOrder(worldAxes(0)); newTranspose(worldAxes(1)) = newWorldOrder(worldAxes(1)); // if (!allEQ(newMap, worldAxisMap) || !allEQ(newTranspose, worldAxisTranspose)) { throw(AipsError("Failed worldMap test 2a")); } if (refChange(0)!=False || refChange(2)!=True || refChange(3)!=True || refChange(4)!=False) { throw(AipsError("Failed worldMap test 2b")); } if (!allEQ(newMap, pixelAxisMap) || !allEQ(newTranspose, pixelAxisTranspose)) { throw(AipsError("Failed pixelMap test 2a")); } } } void doit4() // // test mixed conversion functions // { CoordinateSystem cSys; LinearCoordinate lC = makeLinearCoordinate(1); // 0 cSys.addCoordinate(lC); SpectralCoordinate spC = makeSpectralCoordinate(); // 1 cSys.addCoordinate(spC); DirectionCoordinate dC = makeDirectionCoordinate(True);// 2 & 3 cSys.addCoordinate(dC); // // cout << "Reference pixel = " << cSys.referencePixel() << endl; // cout << "Reference value = " << cSys.referenceValue() << endl; // Vector pixelIn(cSys.nPixelAxes()); Vector worldIn(cSys.nWorldAxes()); Vector pixelAxes(cSys.nPixelAxes()); Vector worldAxes(cSys.nWorldAxes()); Vector worldOut, pixelOut; IPosition shape(cSys.nPixelAxes(), 512); if (!cSys.setWorldMixRanges(shape)) { throw(AipsError(String("setMixRanges failed with ") + cSys.errorMessage())); } // Vector dRefVal = dC.referenceValue(); Vector tmp = cSys.worldAxes(2); // // Force a failure. ALl axes must be pixel or world // pixelAxes.set(False); worldAxes.set(False); Vector worldMin = cSys.worldMixMin(); Vector worldMax = cSys.worldMixMax(); if (cSys.toMix(worldOut, pixelOut, worldIn, pixelIn, worldAxes, pixelAxes, worldMin, worldMax)) { throw(AipsError(String("toMix forced failure 1 did not occur"))); } pixelAxes(0) = True; if (cSys.toMix(worldOut, pixelOut, worldIn, pixelIn, worldAxes, pixelAxes, worldMin, worldMax)) { throw(AipsError(String("toMix forced failure 2 did not occur"))); } // // First test pure pixel->world and world->pixel via the // mix function. // pixelIn = cSys.referencePixel().copy(); if (!cSys.toWorld(worldOut, pixelIn)) { throw(AipsError(String("toWorld conversion failed because ") + cSys.errorMessage())); } // pixelAxes.set(True); worldAxes.set(False); Vector worldOut2; if (!cSys.toMix(worldOut2, pixelOut, worldIn, pixelIn, worldAxes, pixelAxes, worldMin, worldMax)) { throw(AipsError(String("toMix 1 conversion failed because ") + cSys.errorMessage())); } if (!allNear(worldOut, worldOut2, 1e-6)) { throw(AipsError("toWorld/toMix consistency test failed")); } if (!allNear(pixelOut, pixelIn, 1e-6)) { throw(AipsError("toWorld/toMix consistency test failed")); } // // Now try pure world->pixel // worldIn = cSys.referenceValue().copy(); if (!cSys.toPixel(pixelOut, worldIn)) { throw(AipsError(String("toPixel conversion failed because ") + cSys.errorMessage())); } // pixelAxes.set(False); worldAxes.set(True); Vector pixelOut2; if (!cSys.toMix(worldOut, pixelOut2, worldIn, pixelIn, worldAxes, pixelAxes, worldMin, worldMax)) { throw(AipsError(String("toMix 2 conversion failed because ") + cSys.errorMessage())); } if (!allNear(pixelOut, pixelOut2, 1e-6)) { throw(AipsError("toPixel/toMix consistency test failed")); } if (!allNear(worldOut, worldIn, 1e-6)) { throw(AipsError("toPixel/toMix consistency test failed")); } // // Now do a real mix. Use reference values/pixels so we // can confirm correctness // pixelIn(0) = cSys.referencePixel()(0); // Linear pixel pixelIn(2) = cSys.referencePixel()(2); // Direction long pixel pixelAxes.set(False); pixelAxes(0) = True; pixelAxes(2) = True; // worldIn(1) = cSys.referenceValue()(1); // Spectral world worldIn(3) = cSys.referenceValue()(3); // Direction lat world worldAxes.set(False); worldAxes(1) = True; worldAxes(3) = True; // if (!cSys.toMix(worldOut, pixelOut, worldIn, pixelIn, worldAxes, pixelAxes, worldMin, worldMax)) { throw(AipsError(String("toMix 3 conversion failed because ") + cSys.errorMessage())); } // if (!allNear(worldOut, cSys.referenceValue(), 1e-8)) { throw(AipsError("toMix consistency test 1 failed")); } if (!allNear(pixelOut, cSys.referencePixel(), 1e-8)) { throw(AipsError("toMix consistency test 1 failed")); } // // Try another one // pixelIn(1) = cSys.referencePixel()(1); // Spectral pixel pixelIn(3) = cSys.referencePixel()(3); // Direction lat pixel pixelAxes.set(False); pixelAxes(1) = True; pixelAxes(3) = True; // worldIn(0) = cSys.referenceValue()(0); // Linear world worldIn(2) = cSys.referenceValue()(2); // Direction long world worldAxes.set(False); worldAxes(0) = True; worldAxes(2) = True; // if (!cSys.toMix(worldOut, pixelOut, worldIn, pixelIn, worldAxes, pixelAxes, worldMin, worldMax)) { throw(AipsError(String("toMix 4 conversion failed because ") + cSys.errorMessage())); } if (!allNear(worldOut, cSys.referenceValue(), 1e-8)) { throw(AipsError("toMix consistency test 2 failed")); } if (!allNear(pixelOut, cSys.referencePixel(), 1e-8)) { throw(AipsError("toMix consistency test 2 failed")); } // // Now a non-reference value/pixel reflection test // pixelIn(1) = 20.12; // Spectral pixel pixelIn(3) = shape(3) - 20; // Direction lat pixel pixelAxes.set(False); pixelAxes(1) = True; pixelAxes(3) = True; // worldIn(0) = cSys.referenceValue()(0) + 5*cSys.increment()(0); // Linear world worldIn(2) = cSys.referenceValue()(2) - 10*cSys.increment()(2); // Direction long world worldAxes.set(False); worldAxes(0) = True; worldAxes(2) = True; // Vector saveWorldIn(worldIn.copy()); Vector savePixelIn(pixelIn.copy()); // if (!cSys.toMix(worldOut, pixelOut, worldIn, pixelIn, worldAxes, pixelAxes, worldMin, worldMax)) { throw(AipsError(String("toMix 5 conversion failed because ") + cSys.errorMessage())); } // pixelIn(0) = pixelOut(0); pixelIn(2) = pixelOut(2); pixelAxes.set(False); pixelAxes(0) = True; pixelAxes(2) = True; // worldIn(1) = worldOut(1); worldIn(3) = worldOut(3); worldAxes.set(False); worldAxes(1) = True; worldAxes(3) = True; // if (!cSys.toMix(worldOut, pixelOut, worldIn, pixelIn, worldAxes, pixelAxes, worldMin, worldMax)) { throw(AipsError(String("toMix 6 conversion failed because ") + cSys.errorMessage())); } // if (!near(worldOut(0), saveWorldIn(0), 1e-8)) { throw(AipsError("toMix consistency test 3 failed")); } if (!near(worldOut(2), saveWorldIn(2), 1e-8)) { throw(AipsError("toMix consistency test 3 failed")); } if (!near(pixelOut(1), savePixelIn(1), 1e-8)) { throw(AipsError("toMix consistency test 3 failed")); } if (!near(pixelOut(3), savePixelIn(3), 1e-8)) { throw(AipsError("toMix consistency test 3 failed")); } // // Now reorder the CS world axes, Gulp // Linear, Spectral, Direction -> Direction, Spectral, Linear // Vector wOrder(cSys.nWorldAxes()); Vector pOrder(cSys.nPixelAxes()); for (uInt i=0; i pixelIn(cSys.nPixelAxes()); Vector worldIn(cSys.nWorldAxes()); Vector pixelAxes(cSys.nPixelAxes()); Vector worldAxes(cSys.nWorldAxes()); Vector worldOut, pixelOut; // Vector worldMin(cSys.nWorldAxes()); Vector worldMax(cSys.nWorldAxes()); // pixelAxes.set(False); worldAxes.set(False); worldIn = cSys.referenceValue().copy(); pixelIn = cSys.referencePixel().copy(); // if (!cSys.toMix(worldOut, pixelOut, worldIn, pixelIn, worldAxes, pixelAxes, worldMin, worldMax)) { throw(AipsError(String("toMix conversion failed because ") + cSys.errorMessage())); } if (!allNear(worldOut, cSys.referenceValue(), 1e-8)) { throw(AipsError(String("Failed removal test 1a"))); } if (pixelOut.nelements()!=0) { throw(AipsError(String("Failed removal test 1a"))); } } { CoordinateSystem cSys; LinearCoordinate lC = makeLinearCoordinate(1); cSys.addCoordinate(lC); cSys.removeWorldAxis(0, cSys.referenceValue()(0)); // Vector pixelIn(cSys.nPixelAxes()); Vector worldIn(cSys.nWorldAxes()); Vector pixelAxes(cSys.nPixelAxes()); Vector worldAxes(cSys.nWorldAxes()); Vector worldOut, pixelOut; // Vector worldMin(cSys.nWorldAxes()); Vector worldMax(cSys.nWorldAxes()); // pixelAxes.set(False); worldAxes.set(False); worldIn = cSys.referenceValue().copy(); pixelIn = cSys.referencePixel().copy(); // if (!cSys.toMix(worldOut, pixelOut, worldIn, pixelIn, worldAxes, pixelAxes, worldMin, worldMax)) { throw(AipsError(String("toMix conversion failed because ") + cSys.errorMessage())); } if (worldOut.nelements()!=0) { throw(AipsError(String("Failed removal test 2a"))); } if (pixelOut.nelements()!=0) { throw(AipsError(String("Failed removal test 2b"))); } } { // pr,pr->w,w CoordinateSystem cSys; DirectionCoordinate dC = makeDirectionCoordinate(True); cSys.addCoordinate(dC); cSys.removePixelAxis(0, cSys.referencePixel()(0)); cSys.removePixelAxis(0, cSys.referencePixel()(0)); // Vector pixelIn(cSys.nPixelAxes()); Vector worldIn(cSys.nWorldAxes()); Vector pixelAxes(cSys.nPixelAxes()); Vector worldAxes(cSys.nWorldAxes()); Vector worldOut, pixelOut; Vector dRefVal = dC.referenceValue(); // Vector worldMin(cSys.nWorldAxes()); Vector worldMax(cSys.nWorldAxes()); Vector tmp = cSys.worldAxes(0); if (tmp(0)!=-1) { worldMin(tmp(0)) = dRefVal(0) - 10.0; worldMax(tmp(0)) = dRefVal(0) + 10.0; } if (tmp(1)!=-1) { worldMin(tmp(1)) = dRefVal(1) - 10.0; worldMax(tmp(1)) = dRefVal(1) + 10.0; } // pixelAxes.set(False); worldAxes.set(False); worldIn = cSys.referenceValue().copy(); pixelIn = cSys.referencePixel().copy(); // if (!cSys.toMix(worldOut, pixelOut, worldIn, pixelIn, worldAxes, pixelAxes, worldMin, worldMax)) { throw(AipsError(String("toMix conversion failed because ") + cSys.errorMessage())); } if (!allNear(worldOut, cSys.referenceValue(), 1e-8)) { throw(AipsError(String("Failed removal test 3a"))); } if (pixelOut.nelements()!=0) { throw(AipsError(String("Failed removal test 3b"))); } } { // pr,p->w,w CoordinateSystem cSys; DirectionCoordinate dC = makeDirectionCoordinate(True); cSys.addCoordinate(dC); cSys.removePixelAxis(0, cSys.referencePixel()(0)); // Vector pixelIn(cSys.nPixelAxes()); Vector worldIn(cSys.nWorldAxes()); Vector pixelAxes(cSys.nPixelAxes()); Vector worldAxes(cSys.nWorldAxes()); Vector worldOut, pixelOut; Vector dRefVal = dC.referenceValue(); // Vector worldMin(cSys.nWorldAxes()); Vector worldMax(cSys.nWorldAxes()); Vector tmp = cSys.worldAxes(0); if (tmp(0)!=-1) { worldMin(tmp(0)) = dRefVal(0) - 10.0; worldMax(tmp(0)) = dRefVal(0) + 10.0; } if (tmp(1)!=-1) { worldMin(tmp(1)) = dRefVal(1) - 10.0; worldMax(tmp(1)) = dRefVal(1) + 10.0; } // pixelAxes.set(False); pixelAxes(0) = True; worldAxes.set(False); worldIn = cSys.referenceValue().copy(); pixelIn = cSys.referencePixel().copy(); // if (!cSys.toMix(worldOut, pixelOut, worldIn, pixelIn, worldAxes, pixelAxes, worldMin, worldMax)) { throw(AipsError(String("toMix conversion failed because ") + cSys.errorMessage())); } if (!allNear(worldOut, cSys.referenceValue(), 1e-8)) { throw(AipsError(String("Failed removal test 4a"))); } if (pixelOut.nelements()!=1) { throw(AipsError(String("Failed removal test 4b"))); } if (!near(pixelOut(0), cSys.referencePixel()(0), 1e-8)) { throw(AipsError(String("Failed removal test 4c"))); } } { // pr,w->w,p CoordinateSystem cSys; DirectionCoordinate dC = makeDirectionCoordinate(True); cSys.addCoordinate(dC); cSys.removePixelAxis(0, cSys.referencePixel()(0)); // Vector pixelIn(cSys.nPixelAxes()); Vector worldIn(cSys.nWorldAxes()); Vector pixelAxes(cSys.nPixelAxes()); Vector worldAxes(cSys.nWorldAxes()); Vector worldOut, pixelOut; Vector dRefVal = dC.referenceValue(); // Vector worldMin(cSys.nWorldAxes()); Vector worldMax(cSys.nWorldAxes()); Vector tmp = cSys.worldAxes(0); if (tmp(0)!=-1) { worldMin(tmp(0)) = dRefVal(0) - 10.0; worldMax(tmp(0)) = dRefVal(0) + 10.0; } if (tmp(1)!=-1) { worldMin(tmp(1)) = dRefVal(1) - 10.0; worldMax(tmp(1)) = dRefVal(1) + 10.0; } // pixelAxes.set(False); worldAxes.set(False); worldAxes(1) = True; worldIn = cSys.referenceValue().copy(); pixelIn = cSys.referencePixel().copy(); // if (!cSys.toMix(worldOut, pixelOut, worldIn, pixelIn, worldAxes, pixelAxes, worldMin, worldMax)) { throw(AipsError(String("toMix conversion failed because ") + cSys.errorMessage())); } if (!allNear(worldOut, cSys.referenceValue(), 1e-8)) { throw(AipsError(String("Failed removal test 5a"))); } if (pixelOut.nelements()!=1) { throw(AipsError(String("Failed removal test 5b"))); } if (!near(pixelOut(0), cSys.referencePixel()(0), 1e-8)) { throw(AipsError(String("Failed removal test 5c"))); } } { // w,pr->p,w CoordinateSystem cSys; DirectionCoordinate dC = makeDirectionCoordinate(True); cSys.addCoordinate(dC); cSys.removePixelAxis(1, cSys.referencePixel()(1)); // Vector pixelIn(cSys.nPixelAxes()); Vector worldIn(cSys.nWorldAxes()); Vector pixelAxes(cSys.nPixelAxes()); Vector worldAxes(cSys.nWorldAxes()); Vector worldOut, pixelOut; Vector dRefVal = dC.referenceValue(); // Vector worldMin(cSys.nWorldAxes()); Vector worldMax(cSys.nWorldAxes()); Vector tmp = cSys.worldAxes(0); if (tmp(0)!=-1) { worldMin(tmp(0)) = dRefVal(0) - 10.0; worldMax(tmp(0)) = dRefVal(0) + 10.0; } if (tmp(1)!=-1) { worldMin(tmp(1)) = dRefVal(1) - 10.0; worldMax(tmp(1)) = dRefVal(1) + 10.0; } // pixelAxes.set(False); worldAxes.set(False); worldAxes(0) = True; worldIn = cSys.referenceValue().copy(); pixelIn = cSys.referencePixel().copy(); // if (!cSys.toMix(worldOut, pixelOut, worldIn, pixelIn, worldAxes, pixelAxes, worldMin, worldMax)) { throw(AipsError(String("toMix conversion failed because ") + cSys.errorMessage())); } if (!allNear(worldOut, cSys.referenceValue(), 1e-8)) { throw(AipsError(String("Failed removal test 6a"))); } if (pixelOut.nelements()!=1) { throw(AipsError(String("Failed removal test 6b"))); } if (!near(pixelOut(0), cSys.referencePixel()(0), 1e-8)) { throw(AipsError(String("Failed removal test 6c"))); } } { // p,pr->w,w CoordinateSystem cSys; DirectionCoordinate dC = makeDirectionCoordinate(True); cSys.addCoordinate(dC); cSys.removePixelAxis(1, cSys.referencePixel()(1)); // Vector pixelIn(cSys.nPixelAxes()); Vector worldIn(cSys.nWorldAxes()); Vector pixelAxes(cSys.nPixelAxes()); Vector worldAxes(cSys.nWorldAxes()); Vector worldOut, pixelOut; Vector dRefVal = dC.referenceValue(); // Vector worldMin(cSys.nWorldAxes()); Vector worldMax(cSys.nWorldAxes()); Vector tmp = cSys.worldAxes(0); if (tmp(0)!=-1) { worldMin(tmp(0)) = -180.0; worldMax(tmp(0)) = 180.0; } if (tmp(1)!=-1) { worldMin(tmp(1)) = -90.0; worldMax(tmp(1)) = 90.0; } // pixelAxes.set(False); pixelAxes(0) = True; worldAxes.set(False); worldIn = cSys.referenceValue().copy(); pixelIn = cSys.referencePixel().copy(); // if (!cSys.toMix(worldOut, pixelOut, worldIn, pixelIn, worldAxes, pixelAxes, worldMin, worldMax)) { throw(AipsError(String("toMix conversion failed because ") + cSys.errorMessage())); } if (!allNear(worldOut, cSys.referenceValue(), 1e-8)) { throw(AipsError(String("Failed removal test 7a"))); } if (pixelOut.nelements()!=1) { throw(AipsError(String("Failed removal test 7b"))); } if (!near(pixelOut(0), cSys.referencePixel()(0), 1e-8)) { throw(AipsError(String("Failed removal test 7c"))); } } } DirectionCoordinate makeDirectionCoordinate(Bool unitsAreDegrees, MDirection::Types type) { Projection proj = Projection::SIN; Vector crval(2); Vector crpix(2); Vector cdelt(2); Matrix xform(2,2); // crval(0) = 0.1; crval(1) = 0.5; crpix(0) = 100.0; crpix(1) = 120.0; cdelt(0) = 1e-6; cdelt(1) = 2e-6; xform = 0.0; xform.diagonal() = 1.0; DirectionCoordinate dC(type, proj, crval(0), crval(1), cdelt(0), cdelt(1), xform, crpix(0), crpix(1), 999.0, 999.0); // if (unitsAreDegrees) { Vector units(2); units(0) = "deg"; units(1) = "deg"; dC.setWorldAxisUnits(units); } // return dC; } SpectralCoordinate makeSpectralCoordinate () { MFrequency::Types type = MFrequency::TOPO; Double f0 = 1.4e9; Double finc = 4e6; Double refchan = 10.5; Double restFreq = 1.420405752E9; // return SpectralCoordinate(type, f0, finc, refchan, restFreq); } StokesCoordinate makeStokesCoordinate(Bool silly) { if (silly) { Vector whichStokes(5); whichStokes(0) = Stokes::Q; whichStokes(1) = Stokes::RL; whichStokes(2) = Stokes::YY; whichStokes(3) = Stokes::I; whichStokes(4) = Stokes::LL; // Vector stokesStrings(5); stokesStrings(0) = "Q"; stokesStrings(1) = "RL"; stokesStrings(2) = "YY"; stokesStrings(3) = "I"; stokesStrings(4) = "LL"; /* Vector whichStokes(5); whichStokes(0) = Stokes::Q; whichStokes(1) = Stokes::LL; whichStokes(2) = Stokes::XX; whichStokes(3) = Stokes::I; whichStokes(4) = Stokes::XY; // Vector stokesStrings(5); stokesStrings(0) = "Q"; stokesStrings(1) = "LL"; stokesStrings(2) = "XX"; stokesStrings(3) = "I"; stokesStrings(4) = "XY"; // Vector whichStokes(2); whichStokes(0) = Stokes::I; whichStokes(1) = Stokes::V; // Vector stokesStrings(2); stokesStrings(0) = "I"; stokesStrings(1) = "V"; */ // return StokesCoordinate(whichStokes); } else { Vector whichStokes(4); whichStokes(0) = Stokes::I; whichStokes(1) = Stokes::Q; whichStokes(2) = Stokes::U; whichStokes(3) = Stokes::V; // Vector stokesStrings(4); stokesStrings(0) = "I"; stokesStrings(1) = "Q"; stokesStrings(2) = "U"; stokesStrings(3) = "V"; // return StokesCoordinate(whichStokes); } } QualityCoordinate makeQualityCoordinate() { Vector whichQuality(2); whichQuality(0) = Quality::DATA; whichQuality(1) = Quality::ERROR; // return QualityCoordinate(whichQuality); } LinearCoordinate makeLinearCoordinate (uInt nAxes) { Vector names(nAxes); Vector units(nAxes); Vector crpix(nAxes); Vector crval(nAxes); Vector cdelt(nAxes); Matrix xform(nAxes,nAxes); // for (uInt i=0; i1) units(1) = "rad"; if (nAxes>2) units(2) = "kg"; // return LinearCoordinate(names, units, crval, cdelt, xform, crpix); } TabularCoordinate makeTabularCoordinate() { String axisName = "TabularDoggies"; String axisUnit = "km"; Double crval = 10.12; Double crpix = -128.32; Double cdelt = 3.145; // return TabularCoordinate(crval, cdelt, crpix, axisUnit, axisName); } CoordinateSystem makeCoordinateSystem(uInt& nCoords, Vector& types, Vector& sTypes, uInt& iDC, uInt& iSpC, uInt& iTC, uInt& iStC, uInt& iQuC, uInt& iLC, DirectionCoordinate& dC, SpectralCoordinate& spC, TabularCoordinate& tC, StokesCoordinate& stC, QualityCoordinate& quC, LinearCoordinate& lC) { CoordinateSystem cSys; dC = makeDirectionCoordinate(); spC = makeSpectralCoordinate(); tC = makeTabularCoordinate(); stC = makeStokesCoordinate(); quC = makeQualityCoordinate(); lC = makeLinearCoordinate(); cSys.addCoordinate(dC); cSys.addCoordinate(spC); cSys.addCoordinate(tC); cSys.addCoordinate(stC); cSys.addCoordinate(quC); cSys.addCoordinate(lC); iDC = 0; iSpC = 1; iTC = 2; iStC = 3; iQuC = 4; iLC = 5; nCoords = 6; types.resize(6); types(0) = Coordinate::DIRECTION; types(1) = Coordinate::SPECTRAL; types(2) = Coordinate::TABULAR; types(3) = Coordinate::STOKES; types(4) = Coordinate::QUALITY; types(5) = Coordinate::LINEAR; sTypes.resize(6); sTypes(0) = "Direction"; sTypes(1) = "Spectral"; sTypes(2) = "Tabular"; sTypes(3) = "Stokes"; sTypes(4) = "Quality"; sTypes(5) = "Linear"; return cSys; } void doit6 () { CoordinateSystem cSys; SpectralCoordinate spC = makeSpectralCoordinate(); // 0 cSys.addCoordinate(spC); DirectionCoordinate dC = makeDirectionCoordinate(); // 1 & 2 cSys.addCoordinate(dC); Coordinate* pC = 0; // Vector axes(cSys.nPixelAxes(), False); Vector shape(cSys.nPixelAxes(), 0); shape(0) = 64; shape(1) = 128; shape(2) = 256; // Induced failures { // No axes Bool failed = False; try { pC = cSys.makeFourierCoordinate (axes, shape); } catch (std::exception& x) { failed = True; } if (!failed) { throw(AipsError("Failed to induce forced error (1) in makeFourierCoordinate")); } delete pC; } { // Illegal axes Bool failed = False; Vector axes2(20, True); try { pC = cSys.makeFourierCoordinate (axes2, shape); } catch (std::exception& x) { failed = True; } if (!failed) { throw(AipsError("Failed to induce forced error (1) in makeFourierCoordinate")); } delete pC; } { // Illegal shape Bool failed = False; Vector shape2(20, 100); try { pC = cSys.makeFourierCoordinate (axes, shape2); } catch (std::exception& x) { failed = True; } if (!failed) { throw(AipsError("Failed to induce forced error (1) in makeFourierCoordinate")); } delete pC; } // These should work. All the underlying coordinates have been // tested, so just make sure the right coordinate has been replaced { axes.set(False); axes(0) = True; pC = cSys.makeFourierCoordinate (axes, shape); // Vector units2 = pC->worldAxisUnits(); Vector names2 = pC->worldAxisNames(); for (uInt i=0; i units2 = pC->worldAxisUnits(); Vector names2 = pC->worldAxisNames(); if (units2(0)!=String("s") || units2(1)!=String("lambda") || units2(2)!=String("lambda")) { throw(AipsError("makeFourierCoordinate (2) failed units test")); } if (names2(0)!=String("Time") || names2(1)!=String("UU") || names2(2)!=String("VV")) { throw(AipsError("makeFourierCoordinate (2) failed names test")); } delete pC; } } void verifyCAS3264() { cout << __FUNCTION__ << endl; CoordinateSystem cSys; SpectralCoordinate spC = makeSpectralCoordinate(); // 0 cSys.addCoordinate(spC); DirectionCoordinate dC = makeDirectionCoordinate(); // 1 & 2 cSys.addCoordinate(dC); cout << cSys.nPixelAxes(); Vector ftRef(3); Vector ref(3); ref(0)= 22.5; ref(1)= 18.2; ref(2) = 31.48; cerr << "Utils: ref = " << ref << endl; cSys.setReferencePixel(ref); cerr << "Utils coords.refpix: " << cSys.referencePixel() << endl; } void spectralAxisNumber() { cout << "*** test spectralAxisNumber()" << endl; CoordinateSystem csys = CoordinateUtil::defaultCoords4D(); AlwaysAssert(csys.spectralAxisNumber(False) == 3, AipsError); AlwaysAssert(csys.spectralAxisNumber(True) == 3, AipsError); Vector worldOrder(4); worldOrder[0] = 3; worldOrder[1] = 2; worldOrder[2] = 1; worldOrder[3] = 0; Vector pixelOrder(4); pixelOrder[0] = 1; pixelOrder[1] = 2; pixelOrder[2] = 3; pixelOrder[3] = 0; csys.transpose(worldOrder, pixelOrder); AlwaysAssert(csys.spectralAxisNumber(False) == 2, AipsError); AlwaysAssert(csys.spectralAxisNumber(True) == 0, AipsError); csys.removePixelAxis(2, 0); AlwaysAssert(csys.spectralAxisNumber(False) == -1, AipsError); AlwaysAssert(csys.spectralAxisNumber(True) == 0, AipsError); csys.replaceCoordinate(LinearCoordinate(), 2); AlwaysAssert(csys.spectralAxisNumber(False) == -1, AipsError); AlwaysAssert(csys.spectralAxisNumber(True) == -1, AipsError); } void polarizationAxisNumber() { cout << "*** test polarizationAxisNumber()" << endl; CoordinateSystem csys = CoordinateUtil::defaultCoords4D(); AlwaysAssert(csys.polarizationAxisNumber(False) == 2, AipsError); AlwaysAssert(csys.polarizationAxisNumber(True) == 2, AipsError); Vector worldOrder(4); worldOrder[0] = 3; worldOrder[1] = 2; worldOrder[2] = 1; worldOrder[3] = 0; Vector pixelOrder(4); pixelOrder[0] = 1; pixelOrder[1] = 0; pixelOrder[2] = 3; pixelOrder[3] = 2; csys.transpose(worldOrder, pixelOrder); AlwaysAssert(csys.polarizationAxisNumber(False) == 3, AipsError); AlwaysAssert(csys.polarizationAxisNumber(True) == 1, AipsError); csys.removePixelAxis(3, 0); AlwaysAssert(csys.polarizationAxisNumber(False) == -1, AipsError); AlwaysAssert(csys.polarizationAxisNumber(True) == 1, AipsError); csys.replaceCoordinate(LinearCoordinate(), 1); AlwaysAssert(csys.polarizationAxisNumber(False) == -1, AipsError); AlwaysAssert(csys.polarizationAxisNumber(True) == -1, AipsError); } casacore-3.7.1/coordinates/Coordinates/test/tCoordinateUtil.cc000066400000000000000000000655461476623553700245270ustar00rootroot00000000000000//# tCoordinateUtil.cc: Test program for CoordinateUtil class //# Copyright (C) 1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include void test0(); void test1(); void test2(); void test3(); void test4(); void test5(); int main() { try { test0(); test1(); test2(); test3(); test4(); test5(); } catch (const std::exception& x) { cerr << "aipserror: error " << x.what() << endl; return 1; } return 0; } void test0() { CoordinateSystem csys2 = CoordinateUtil::defaultCoords2D(); CoordinateSystem csys3 = CoordinateUtil::defaultCoords3D(); CoordinateSystem csys4 = CoordinateUtil::defaultCoords4D(); AlwaysAssertExit (CoordinateUtil::compareCoordinates (csys2, csys2) == 0); AlwaysAssertExit (CoordinateUtil::compareCoordinates (csys3, csys3) == 0); AlwaysAssertExit (CoordinateUtil::compareCoordinates (csys4, csys4) == 0); AlwaysAssertExit (CoordinateUtil::compareCoordinates (csys2, csys3) == -1); AlwaysAssertExit (CoordinateUtil::compareCoordinates (csys2, csys4) == -1); AlwaysAssertExit (CoordinateUtil::compareCoordinates (csys3, csys4) == -1); AlwaysAssertExit (CoordinateUtil::compareCoordinates (csys3, csys2) == 1); AlwaysAssertExit (CoordinateUtil::compareCoordinates (csys4, csys2) == 1); AlwaysAssertExit (CoordinateUtil::compareCoordinates (csys4, csys3) == 1); IPosition newAxes, stretchAxes; AlwaysAssertExit (CoordinateUtil::findExtendAxes (newAxes, stretchAxes, IPosition(4,5,6,1,5), IPosition(4,5,1,1,1), csys4, csys4)); AlwaysAssertExit (newAxes.isEqual (IPosition())); AlwaysAssertExit (stretchAxes.isEqual (IPosition(2,1,3))); AlwaysAssertExit (CoordinateUtil::findExtendAxes (newAxes, stretchAxes, IPosition(3,5,6,3), IPosition(2,5,6), csys3, csys2)); AlwaysAssertExit (newAxes.isEqual (IPosition(1,2))); AlwaysAssertExit (stretchAxes.isEqual (IPosition())); AlwaysAssertExit (CoordinateUtil::findExtendAxes (newAxes, stretchAxes, IPosition(4,5,6,3,4), IPosition(2,5,6), csys4, csys2)); AlwaysAssertExit (newAxes.isEqual (IPosition(2,2,3))); AlwaysAssertExit (stretchAxes.isEqual (IPosition())); AlwaysAssertExit (CoordinateUtil::findExtendAxes (newAxes, stretchAxes, IPosition(4,5,6,3,4), IPosition(3,5,6,1), csys4, csys3)); AlwaysAssertExit (newAxes.isEqual (IPosition(1,2))); AlwaysAssertExit (stretchAxes.isEqual (IPosition(1,3))); } void test1() { IPosition newAxes, stretchAxes; { CoordinateSystem csys1; CoordinateSystem csys2; CoordinateUtil::addDirAxes (csys1); CoordinateUtil::addFreqAxis (csys2); AlwaysAssertExit (CoordinateUtil::compareCoordinates (csys1, csys2) == 9); AlwaysAssertExit (CoordinateUtil::compareCoordinates (csys2, csys1) == 9); AlwaysAssertExit (! CoordinateUtil::findExtendAxes (newAxes, stretchAxes, IPosition(2,5,6), IPosition(1,8), csys1, csys2)); CoordinateUtil::addDirAxes (csys2); AlwaysAssertExit (CoordinateUtil::findExtendAxes (newAxes, stretchAxes, IPosition(3,8,5,6), IPosition(2,1,1), csys2, csys1)); AlwaysAssertExit (newAxes.isEqual (IPosition(1,0))); AlwaysAssertExit (stretchAxes.isEqual (IPosition(2,1,2))); CoordinateUtil::addFreqAxis (csys1); AlwaysAssertExit (CoordinateUtil::compareCoordinates (csys1, csys2) == 9); AlwaysAssertExit (CoordinateUtil::compareCoordinates (csys2, csys1) == 9); AlwaysAssertExit (! CoordinateUtil::findExtendAxes (newAxes, stretchAxes, IPosition(3,5,6,3), IPosition(3,3,4,6), csys1, csys2)); } } void test2 () { // // DirectionCoordinate // cout << "" << endl; cout << "DirectionCoordinate" << endl; cout << "*******************" << endl; { CoordinateSystem cSys = CoordinateUtil::defaultCoords2D(); Vector pixelAxes, worldAxes; Int coordinate; CoordinateUtil::findDirectionAxes(pixelAxes, worldAxes, coordinate, cSys); cout << "Pixel axes= " << pixelAxes << endl; cout << "World axes= " << worldAxes << endl; cout << "Coordinate = " << coordinate << endl; Vector pixelAxes2 = CoordinateUtil::findDirectionAxes(cSys); cout << "Pixel axes2 = " << pixelAxes2 << endl << endl; } { CoordinateSystem cSys = CoordinateUtil::defaultCoords2D(); cout << "Remove pixel axis 1 (DEC)" << endl; cSys.removePixelAxis(1, 0.0); Vector pixelAxes, worldAxes; Int coordinate; CoordinateUtil::findDirectionAxes(pixelAxes, worldAxes, coordinate, cSys); cout << "Pixel axes= " << pixelAxes << endl; cout << "World axes= " << worldAxes << endl; cout << "Coordinate = " << coordinate << endl; Vector pixelAxes2 = CoordinateUtil::findDirectionAxes(cSys); cout << "Pixel axes2 = " << pixelAxes2 << endl << endl; } { CoordinateSystem cSys = CoordinateUtil::defaultCoords2D(); cout << "Remove world axis 1 (DEC)" << endl; cSys.removeWorldAxis(1, 0.0); Vector pixelAxes, worldAxes; Int coordinate; CoordinateUtil::findDirectionAxes(pixelAxes, worldAxes, coordinate, cSys); cout << "Pixel axes= " << pixelAxes << endl; cout << "World axes= " << worldAxes << endl; cout << "Coordinate = " << coordinate << endl; Vector pixelAxes2 = CoordinateUtil::findDirectionAxes(cSys); cout << "Pixel axes2 = " << pixelAxes2 << endl << endl; } { CoordinateSystem cSys = CoordinateUtil::defaultCoords2D(); cout << "Remove pixel axis 0 (RA)" << endl; cout << "Remove world axis 1 (DEC)" << endl; cSys.removePixelAxis(0, 0.0); cSys.removeWorldAxis(1, 0.0); Vector pixelAxes, worldAxes; Int coordinate; CoordinateUtil::findDirectionAxes(pixelAxes, worldAxes, coordinate, cSys); cout << "Pixel axes= " << pixelAxes << endl; cout << "World axes= " << worldAxes << endl; cout << "Coordinate = " << coordinate << endl; Vector pixelAxes2 = CoordinateUtil::findDirectionAxes(cSys); cout << "Pixel axes2 = " << pixelAxes2 << endl << endl; } { CoordinateSystem cSys = CoordinateUtil::defaultCoords2D(); cout << "Remove world axis 0 (RA)" << endl; cout << "Remove world axis 1 (DEC)" << endl; cSys.removeWorldAxis(0, 0.0); cSys.removeWorldAxis(0, 0.0); // Shuffle down one Vector pixelAxes, worldAxes; Int coordinate; CoordinateUtil::findDirectionAxes(pixelAxes, worldAxes, coordinate, cSys); cout << "Pixel axes= " << pixelAxes << endl; cout << "World axes= " << worldAxes << endl; cout << "Coordinate = " << coordinate << endl; Vector pixelAxes2 = CoordinateUtil::findDirectionAxes(cSys); cout << "Pixel axes2 = " << pixelAxes2 << endl << endl; } // // SpectralCoordinate // cout << "" << endl; cout << "Spectral Coordinate" << endl; cout << "*******************" << endl; { CoordinateSystem cSys = CoordinateUtil::defaultCoords2D(); cout << "No spectral axis" << endl; Int pixelAxis, worldAxis; Int coordinate; CoordinateUtil::findSpectralAxis(pixelAxis, worldAxis, coordinate, cSys); cout << "Pixel axis= " << pixelAxis << endl; cout << "World axis= " << worldAxis << endl; cout << "Coordinate = " << coordinate << endl; Int pixelAxis2 = CoordinateUtil::findSpectralAxis(cSys); cout << "Pixel axis2 = " << pixelAxis2 << endl << endl; } { CoordinateSystem cSys = CoordinateUtil::defaultCoords3D(); Int pixelAxis, worldAxis; Int coordinate; CoordinateUtil::findSpectralAxis(pixelAxis, worldAxis, coordinate, cSys); cout << "Pixel axis= " << pixelAxis << endl; cout << "World axis= " << worldAxis << endl; cout << "Coordinate = " << coordinate << endl; Int pixelAxis2 = CoordinateUtil::findSpectralAxis(cSys); cout << "Pixel axis2 = " << pixelAxis2 << endl << endl; } { CoordinateSystem cSys = CoordinateUtil::defaultCoords3D(); cout << "Remove pixel axis 2 (Spectral)" << endl; cSys.removePixelAxis(2, 0.0); Int pixelAxis, worldAxis; Int coordinate; CoordinateUtil::findSpectralAxis(pixelAxis, worldAxis, coordinate, cSys); cout << "Pixel axis= " << pixelAxis << endl; cout << "World axis= " << worldAxis << endl; cout << "Coordinate = " << coordinate << endl; Int pixelAxis2 = CoordinateUtil::findSpectralAxis(cSys); cout << "Pixel axis2 = " << pixelAxis2 << endl << endl; } { CoordinateSystem cSys = CoordinateUtil::defaultCoords3D(); cout << "Remove world axis 2 (Spectral)" << endl; cSys.removeWorldAxis(2, 0.0); Int pixelAxis, worldAxis; Int coordinate; CoordinateUtil::findSpectralAxis(pixelAxis, worldAxis, coordinate, cSys); cout << "Pixel axis= " << pixelAxis << endl; cout << "World axis= " << worldAxis << endl; cout << "Coordinate = " << coordinate << endl; Int pixelAxis2 = CoordinateUtil::findSpectralAxis(cSys); cout << "Pixel axis2 = " << pixelAxis2 << endl << endl; } // // StokesCoordinate // cout << "" << endl; cout << "Stokes Coordinate" << endl; cout << "*******************" << endl; Vector whichPols; { CoordinateSystem cSys = CoordinateUtil::defaultCoords2D(); cout << "No stokes axis" << endl; Int pixelAxis, worldAxis; Int coordinate; CoordinateUtil::findStokesAxis(pixelAxis, worldAxis, coordinate, cSys); cout << "Pixel axis= " << pixelAxis << endl; cout << "World axis= " << worldAxis << endl; cout << "Coordinate = " << coordinate << endl; Int pixelAxis2 = CoordinateUtil::findStokesAxis(whichPols, cSys); cout << "Pixel axis2 = " << pixelAxis2 << endl << endl; } { CoordinateSystem cSys = CoordinateUtil::defaultCoords4D(); Int pixelAxis, worldAxis; Int coordinate; CoordinateUtil::findStokesAxis(pixelAxis, worldAxis, coordinate, cSys); cout << "Pixel axis= " << pixelAxis << endl; cout << "World axis= " << worldAxis << endl; cout << "Coordinate = " << coordinate << endl; Int pixelAxis2 = CoordinateUtil::findStokesAxis(whichPols, cSys); cout << "Pixel axis2 = " << pixelAxis2 << endl << endl; } { CoordinateSystem cSys = CoordinateUtil::defaultCoords4D(); cout << "Remove pixel axis 2 (Stokes)" << endl; cSys.removePixelAxis(2, 0.0); Int pixelAxis, worldAxis; Int coordinate; CoordinateUtil::findStokesAxis(pixelAxis, worldAxis, coordinate, cSys); cout << "Pixel axis= " << pixelAxis << endl; cout << "World axis= " << worldAxis << endl; cout << "Coordinate = " << coordinate << endl; Int pixelAxis2 = CoordinateUtil::findStokesAxis(whichPols, cSys); cout << "Pixel axis2 = " << pixelAxis2 << endl << endl; } { CoordinateSystem cSys = CoordinateUtil::defaultCoords4D(); cout << "Remove world axis 2 (Stokes)" << endl; cSys.removeWorldAxis(2, 0.0); Int pixelAxis, worldAxis; Int coordinate; CoordinateUtil::findStokesAxis(pixelAxis, worldAxis, coordinate, cSys); cout << "Pixel axis= " << pixelAxis << endl; cout << "World axis= " << worldAxis << endl; cout << "Coordinate = " << coordinate << endl; Int pixelAxis2 = CoordinateUtil::findStokesAxis(whichPols, cSys); cout << "Pixel axis2 = " << pixelAxis2 << endl << endl; } // addStokesAxis { CoordinateSystem cSys; CoordinateUtil::addStokesAxis(cSys, 4); Int afterCoord = -1; Int coordinate = cSys.findCoordinate(Coordinate::STOKES, afterCoord); uInt nPixelAxes = cSys.nPixelAxes(); uInt nWorldAxes = cSys.nWorldAxes(); if (coordinate!=0 || nPixelAxes!=1 || nWorldAxes!=1 || cSys.type(coordinate)!=Coordinate::STOKES) { throw(AipsError("addStokesAxis failed")); } } // addLinearAxes { const uInt n = 4; CoordinateSystem cSys; Vector names(n); names(0) = "axis0"; names(1) = "axis1"; names(2) = "axis2"; names(3) = "axis3"; IPosition shape; CoordinateUtil::addLinearAxes(cSys, names, shape); Int coordinate; Int afterCoord = -1; coordinate = cSys.findCoordinate(Coordinate::LINEAR, afterCoord); // uInt nPixelAxes = cSys.nPixelAxes(); uInt nWorldAxes = cSys.nWorldAxes(); Vector refPix = cSys.referencePixel(); // if (coordinate!=0 || nPixelAxes!=n || nWorldAxes!=n || cSys.type(coordinate)!=Coordinate::LINEAR || !::allNear(refPix, Double(0.0), Double(1.0e-6))) { throw(AipsError("addLinearAxes failed")); } } { const uInt n = 2; CoordinateSystem cSys; Vector names(n); names(0) = "axis0"; names(1) = "axis1"; IPosition shape(n, 100); CoordinateUtil::addLinearAxes(cSys, names, shape); Int coordinate; Int afterCoord = -1; coordinate = cSys.findCoordinate(Coordinate::LINEAR, afterCoord); // uInt nPixelAxes = cSys.nPixelAxes(); uInt nWorldAxes = cSys.nWorldAxes(); Vector refPix = cSys.referencePixel(); // if (coordinate!=0 || nPixelAxes!=n || nWorldAxes!=n || cSys.type(coordinate)!=Coordinate::LINEAR || !::allNear(refPix, Double(50.0), Double(1e-6))) { throw(AipsError("addLinearAxes failed")); } } // // makeCoordinateSYstem. A lot of built in knowledge in // the verification process. Urk. // { IPosition shape(1, 10); CoordinateSystem cSys = CoordinateUtil::makeCoordinateSystem(shape); Int coordinate; Int afterCoord = -1; coordinate = cSys.findCoordinate(Coordinate::SPECTRAL, afterCoord); if (coordinate!=0 || cSys.nPixelAxes()!=1 || cSys.nWorldAxes()!=1 || cSys.type(coordinate)!=Coordinate::SPECTRAL) { throw(AipsError("makeCoordinateSystem 1 failed")); } } { IPosition shape(2, 10, 10); CoordinateSystem cSys = CoordinateUtil::makeCoordinateSystem(shape); Int coordinate; Int afterCoord = -1; coordinate = cSys.findCoordinate(Coordinate::DIRECTION, afterCoord); if (coordinate!=0 || cSys.nPixelAxes()!=2 || cSys.nWorldAxes()!=2 || cSys.type(coordinate)!=Coordinate::DIRECTION) { throw(AipsError("makeCoordinateSystem 2 failed")); } } { IPosition shape(3, 10, 10, 4); CoordinateSystem cSys = CoordinateUtil::makeCoordinateSystem(shape); Int c0, c1; Int afterCoord = -1; c0 = cSys.findCoordinate(Coordinate::DIRECTION, afterCoord); c1 = cSys.findCoordinate(Coordinate::STOKES, afterCoord); if (c0 !=0 || cSys.type(c0)!=Coordinate::DIRECTION || c1 !=1 || cSys.type(c1)!=Coordinate::STOKES) { throw(AipsError("makeCoordinateSystem 3a failed")); } if (cSys.nPixelAxes()!=3 || cSys.nWorldAxes()!=3) { throw(AipsError("makeCoordinateSystem 3b failed")); } } { IPosition shape(4, 10, 10, 4, 16); CoordinateSystem cSys = CoordinateUtil::makeCoordinateSystem(shape); Int c0, c1, c2; Int afterCoord = -1; c0 = cSys.findCoordinate(Coordinate::DIRECTION, afterCoord); c1 = cSys.findCoordinate(Coordinate::STOKES, afterCoord); c2 = cSys.findCoordinate(Coordinate::SPECTRAL, afterCoord); if (c0 !=0 || cSys.type(c0)!=Coordinate::DIRECTION || c1 !=1 || cSys.type(c1)!=Coordinate::STOKES || c2 !=2 || cSys.type(c2)!=Coordinate::SPECTRAL) { throw(AipsError("makeCoordinateSystem 4a failed")); } if (cSys.nPixelAxes()!=4 || cSys.nWorldAxes()!=4) { throw(AipsError("makeCoordinateSystem 4b failed")); } } { IPosition shape(4, 10, 10, 16, 4); CoordinateSystem cSys = CoordinateUtil::makeCoordinateSystem(shape); Int c0, c1, c2; Int afterCoord = -1; c0 = cSys.findCoordinate(Coordinate::DIRECTION, afterCoord); c1 = cSys.findCoordinate(Coordinate::SPECTRAL, afterCoord); c2 = cSys.findCoordinate(Coordinate::STOKES, afterCoord); if (c0 !=0 || cSys.type(c0)!=Coordinate::DIRECTION || c1 !=1 || cSys.type(c1)!=Coordinate::SPECTRAL || c2 !=2 || cSys.type(c2)!=Coordinate::STOKES) { throw(AipsError("makeCoordinateSystem 5a failed")); } if (cSys.nPixelAxes()!=4 || cSys.nWorldAxes()!=4) { throw(AipsError("makeCoordinateSystem 5b failed")); } } { IPosition shape(6, 10, 10, 16, 4, 2, 3); CoordinateSystem cSys = CoordinateUtil::makeCoordinateSystem(shape); Int c0, c1, c2, c3; Int afterCoord = -1; c0 = cSys.findCoordinate(Coordinate::DIRECTION, afterCoord); c1 = cSys.findCoordinate(Coordinate::SPECTRAL, afterCoord); c2 = cSys.findCoordinate(Coordinate::STOKES, afterCoord); c3 = cSys.findCoordinate(Coordinate::LINEAR, afterCoord); if (c0 !=0 || cSys.type(c0)!=Coordinate::DIRECTION || c1 !=1 || cSys.type(c1)!=Coordinate::SPECTRAL || c2 !=2 || cSys.type(c2)!=Coordinate::STOKES || c3 !=3 || cSys.type(c3)!=Coordinate::LINEAR) { throw(AipsError("makeCoordinateSystem 6a failed")); } if (cSys.nPixelAxes()!=6 || cSys.nWorldAxes()!=6) { throw(AipsError("makeCoordinateSystem 6b")); } } } void test3 () // // Test function dropRemovedAxes. 4D makes Direction, Stokes, Spectral // { // No removed axes { CoordinateSystem cSysIn = CoordinateUtil::defaultCoords4D(); CoordinateSystem cSysOut; Bool dropped = CoordinateUtil::dropRemovedAxes(cSysOut, cSysIn); AlwaysAssert(dropped==False, AipsError); AlwaysAssert(cSysIn.near(cSysOut), AipsError); } // Remove world&pixel axis for spectral axis - can be fully dropped { CoordinateSystem cSysIn = CoordinateUtil::defaultCoords4D(); Int pixelAxis, worldAxis, coord; CoordinateUtil::findSpectralAxis(pixelAxis, worldAxis, coord, cSysIn); cSysIn.removeWorldAxis(worldAxis, 0.0); // CoordinateSystem cSysOut; Bool dropped = CoordinateUtil::dropRemovedAxes(cSysOut, cSysIn); AlwaysAssert(dropped==True, AipsError); AlwaysAssert(cSysOut.nCoordinates()==(cSysIn.nCoordinates()-1), AipsError); AlwaysAssert(cSysOut.nPixelAxes()==cSysIn.nPixelAxes(), AipsError); AlwaysAssert(cSysOut.nWorldAxes()==cSysIn.nWorldAxes(), AipsError); CoordinateUtil::findSpectralAxis(pixelAxis, worldAxis, coord, cSysOut); AlwaysAssert(coord==-1, AipsError); AlwaysAssert(worldAxis==-1, AipsError); AlwaysAssert(pixelAxis==-1, AipsError); } // Remove pixel axis only for spectral axis - cannot be fully dropped { CoordinateSystem cSysIn = CoordinateUtil::defaultCoords4D(); Int pixelAxis, worldAxis, coord; CoordinateUtil::findSpectralAxis(pixelAxis, worldAxis, coord, cSysIn); cSysIn.removePixelAxis(pixelAxis, 0.0); // CoordinateSystem cSysOut; Bool dropped = CoordinateUtil::dropRemovedAxes(cSysOut, cSysIn); AlwaysAssert(dropped==False, AipsError); AlwaysAssert(cSysOut.nCoordinates()==cSysIn.nCoordinates(), AipsError); AlwaysAssert(cSysOut.nPixelAxes()==cSysIn.nPixelAxes(), AipsError); AlwaysAssert(cSysOut.nWorldAxes()==cSysIn.nWorldAxes(), AipsError); Vector pixelAxes = cSysOut.pixelAxes(coord); Vector worldAxes = cSysOut.worldAxes(coord); AlwaysAssert(pixelAxes(0)==-1, AipsError); AlwaysAssert(worldAxes(0)==worldAxis, AipsError); } // Remove world and pixel axis for half of DirectionCoordinate - cannot be fully dropped { CoordinateSystem cSysIn = CoordinateUtil::defaultCoords4D(); Vector pixelAxes, worldAxes; Int coord; CoordinateUtil::findDirectionAxes(pixelAxes, worldAxes, coord, cSysIn); cSysIn.removeWorldAxis(worldAxes(0), 0.0); // CoordinateSystem cSysOut; Bool dropped = CoordinateUtil::dropRemovedAxes(cSysOut, cSysIn); AlwaysAssert(dropped==False, AipsError); AlwaysAssert(cSysOut.nCoordinates()==cSysIn.nCoordinates(), AipsError); AlwaysAssert(cSysOut.nPixelAxes()==cSysIn.nPixelAxes(), AipsError); AlwaysAssert(cSysOut.nWorldAxes()==cSysIn.nWorldAxes(), AipsError); Vector pixelAxesOut = cSysOut.pixelAxes(coord); Vector worldAxesOut = cSysOut.worldAxes(coord); AlwaysAssert(pixelAxesOut(0)==-1, AipsError); AlwaysAssert(worldAxesOut(0)==-1, AipsError); AlwaysAssert(pixelAxesOut(1)>=0, AipsError); AlwaysAssert(worldAxesOut(1)>=0, AipsError); } // Remove world and pixel axis for all of DirectionCoordinate - can be fully dropped { CoordinateSystem cSysIn = CoordinateUtil::defaultCoords4D(); Vector pixelAxes, worldAxes; Int coord; CoordinateUtil::findDirectionAxes(pixelAxes, worldAxes, coord, cSysIn); cSysIn.removeWorldAxis(worldAxes(1), 0.0); cSysIn.removeWorldAxis(worldAxes(0), 0.0); // CoordinateSystem cSysOut; Bool dropped = CoordinateUtil::dropRemovedAxes(cSysOut, cSysIn); AlwaysAssert(dropped==True, AipsError); AlwaysAssert(cSysOut.nCoordinates()==cSysIn.nCoordinates()-1, AipsError); AlwaysAssert(cSysOut.nPixelAxes()==cSysIn.nPixelAxes(), AipsError); AlwaysAssert(cSysOut.nWorldAxes()==cSysIn.nWorldAxes(), AipsError); CoordinateUtil::findDirectionAxes(pixelAxes, worldAxes, coord, cSysOut); AlwaysAssert(coord==-1, AipsError); AlwaysAssert(worldAxes.nelements()==0, AipsError); AlwaysAssert(pixelAxes.nelements()==0, AipsError); } { // axis order is preserved when dropping an axis. CoordinateSystem cSysIn = CoordinateUtil::defaultCoords4D(); Vector order(4); order[0] = 0; order[1] = 1; order[2] = 3; order[3] = 2; cSysIn.transpose(order, order); cSysIn.removePixelAxis(0, 0.0); CoordinateSystem cSysOut; Bool dropped = CoordinateUtil::dropRemovedAxes(cSysOut, cSysIn, False); AlwaysAssert(dropped==False, AipsError); AlwaysAssert( cSysOut.spectralAxisNumber() != cSysIn.spectralAxisNumber(), AipsError ); AlwaysAssert( cSysOut.polarizationAxisNumber() != cSysIn.polarizationAxisNumber(), AipsError ); AlwaysAssert( cSysOut.worldAxes(cSysOut.spectralCoordinateNumber())[0] != cSysIn.worldAxes(cSysIn.spectralCoordinateNumber())[0], AipsError ); AlwaysAssert( cSysOut.worldAxes(cSysOut.polarizationCoordinateNumber())[0] != cSysIn.worldAxes(cSysIn.polarizationCoordinateNumber())[0], AipsError ); cSysOut = CoordinateSystem(); dropped = CoordinateUtil::dropRemovedAxes(cSysOut, cSysIn, True); AlwaysAssert( cSysOut.spectralAxisNumber() == cSysIn.spectralAxisNumber(), AipsError ); AlwaysAssert( cSysOut.polarizationAxisNumber() == cSysIn.polarizationAxisNumber(), AipsError ); AlwaysAssert( cSysOut.worldAxes(cSysOut.spectralCoordinateNumber())[0] == cSysIn.worldAxes(cSysIn.spectralCoordinateNumber())[0], AipsError ); AlwaysAssert( cSysOut.worldAxes(cSysOut.polarizationCoordinateNumber())[0] == cSysIn.worldAxes(cSysIn.polarizationCoordinateNumber())[0], AipsError ); } } void test4 () // // test function axisLabel // { CoordinateSystem cSys = CoordinateUtil::defaultCoords4D(); // uInt axis = 0; Bool doWorld = True; Bool doAbs = True; Bool doVel = False; String label; // for (uInt i=0; i #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include DirectionCoordinate makeCoordinate(MDirection::Types type, Projection& proj, Vector& crval, Vector& crpix, Vector& cdelt, Matrix& xform); void doit (DirectionCoordinate& lc, MDirection::Types type, const Vector& axisName, const String& axisUnit); void doit2 (DirectionCoordinate& lc, Vector& crval, Vector& crpix, Vector& cdelt, Matrix& xform); void doit3 (DirectionCoordinate& lc); void doit4 (DirectionCoordinate& lc); void doit5 (DirectionCoordinate& lc); void doit6(); void doit7 (); void doit8 (); void doit9 (); void doit10 (); #if defined(USE_THREADS) #include void _create_many_coordinates() { // Simply copying a coordinate originally caused multithreading problems for(size_t i = 0; i != 1000; i++) { DirectionCoordinate d1; DirectionCoordinate d2(d1); } } void test_multithreaded_behaviour() { std::cout << "*** Test multithreaded coordinate creation" << std::endl; auto t1 = std::thread(_create_many_coordinates); auto t2 = std::thread(_create_many_coordinates); t1.join(); t2.join(); } #endif // USE_THREADS int main() { #if defined(USE_THREADS) { test_multithreaded_behaviour(); } #endif // USE_THREADS try { Projection proj; Vector crval, crpix, cdelt; Matrix xform; // Constructors { DirectionCoordinate lc; } { DirectionCoordinate lc = makeCoordinate(MDirection::J2000, proj, crval, crpix, cdelt, xform); } // { for (uInt i=0; i excludeAxes(1, 0); if (!lc.near(lc2, excludeAxes)) { throw(AipsError(String("Failed near test 2 because") + lc.errorMessage())); } } // Test Quantum constructor interface { Matrix xform(2,2); xform = 0.0; xform.diagonal() = 1.0; Projection proj(Projection::SIN); MDirection::Types type(MDirection::B1950); // Vector crval(2); Vector crpix(2); Vector cdelt(2); // crval(0) = 0.1; crval(1) = 0.5; crpix(0) = 100.0; crpix(1) = 120.0; cdelt(0) = 1e-6; cdelt(1) = 2e-6; // DirectionCoordinate dc1(type, proj, crval(0), crval(1), cdelt(0), cdelt(1), xform, crpix(0), crpix(1)); // Quantum lon(crval(0)*180.0/M_PI, "deg"); Quantum lat(crval(1)*180.0/M_PI, "deg"); Quantum dlon(cdelt(0)*180.0/M_PI, "deg"); Quantum dlat(cdelt(1)*180.0/M_PI, "deg"); Quantum longPole(999.0, "deg"); Quantum latPole(999.0, "deg"); // DirectionCoordinate dc2(type, proj, lon, lat, dlon, dlat, xform, crpix(0), crpix(1), longPole, latPole); // if (!dc1.near(dc2)) { throw(AipsError("Quantum interface constructor failed consistency test")); } } // Test WCS constructor (only partial test) { ::wcsprm wcs; wcs.flag = -1; int iret = wcsini(1, 2, &wcs); if (iret!=0) { throw(AipsError(String("Failed to make wcs structure"))); } // String ctype0("RA---SIN"); String ctype1("DEC--SIN"); // strncpy (wcs.ctype[0], ctype0.chars(), 9); strncpy (wcs.ctype[1], ctype1.chars(), 9); // iret = wcsset(&wcs); if (iret!=0) { throw(AipsError(String("Failed to init wcs structure"))); } // DirectionCoordinate dc3 (MDirection::J2000, wcs); // DirectionCoordinate dc4(dc3); if (!dc3.near(dc4)) { throw(AipsError(String("wcs interface constructor failed consistency test 1"))); } DirectionCoordinate dc5; dc5 = dc3; if (!dc5.near(dc3)) { throw(AipsError(String("wcs interface constructor failed consistency test 2"))); } wcsfree(&wcs); } // Test the rest { DirectionCoordinate lc = makeCoordinate(MDirection::J2000, proj, crval, crpix, cdelt, xform); Vector axisNames(2); axisNames(0) = "Right Ascension"; axisNames(1) = "Declination"; String axisUnit = "rad"; doit(lc, MDirection::J2000, axisNames, axisUnit); } { DirectionCoordinate lc = makeCoordinate(MDirection::J2000, proj, crval, crpix, cdelt, xform); doit2(lc, crval, crpix, cdelt, xform); } { DirectionCoordinate lc = makeCoordinate(MDirection::J2000, proj, crval, crpix, cdelt, xform); doit3(lc); } { DirectionCoordinate lc = makeCoordinate(MDirection::GALACTIC, proj, crval, crpix, cdelt, xform); Vector axisNames(2); axisNames(0) = "Longitude"; axisNames(1) = "Latitude"; String axisUnit = "rad"; doit(lc, MDirection::GALACTIC, axisNames, axisUnit); } { DirectionCoordinate lc = makeCoordinate(MDirection::GALACTIC, proj, crval, crpix, cdelt, xform); doit2(lc, crval, crpix, cdelt, xform); } { DirectionCoordinate lc = makeCoordinate(MDirection::GALACTIC, proj, crval, crpix, cdelt, xform); doit3(lc); } { DirectionCoordinate lc = makeCoordinate(MDirection::J2000, proj, crval, crpix, cdelt, xform); doit4(lc); } { DirectionCoordinate lc = makeCoordinate(MDirection::J2000, proj, crval, crpix, cdelt, xform); doit5(lc); } { doit6 (); } { doit7 (); } { doit8(); } { doit9(); } { doit10(); } { // getPixelArea DirectionCoordinate dc = makeCoordinate( MDirection::J2000, proj, crval, crpix, cdelt, xform ); cout << "cdelt " << cdelt << endl; Quantity pixelArea = dc.getPixelArea(); AlwaysAssert(pixelArea.getValue() == fabs(cdelt[0]*cdelt[1]), AipsError); Vector units = dc.worldAxisUnits(); AlwaysAssert( pixelArea.getUnit() == (Quantity(1, units[0])*Quantity(1, units[1])).getUnit(), AipsError ); } { cout << "*** Test rotate" << endl; DirectionCoordinate dc = makeCoordinate( MDirection::J2000, proj, crval, crpix, cdelt, xform ); // No rotation Coordinate *c = dc.rotate(Quantity(0, "deg")); Vector p1(2); p1[0] = 10; p1[1] = 20; Vector p2(2); p2[0] = 200; p2[1] = 240; Vector got, exp; c->toWorld(got, p1); dc.toWorld(exp, p1); Vector p1World = exp.copy(); AlwaysAssert(allTrue(got == exp), AipsError); c->toWorld(got, p2); dc.toWorld(exp, p2); Vector p2World = exp.copy(); AlwaysAssert(allTrue(got == exp), AipsError); delete c; // non-zero rotation c = dc.rotate(Quantity(30, "deg")); c->toPixel(got, p1World); Vector orig; dc.toPixel(orig, p1World); exp[0] = 72.05771366; exp[1] = -11.60254038; AlwaysAssert(allNear(got, exp, 1e-8), AipsError); delete c; } { cout << "*** Test convert()" << endl; Projection p(Projection::SIN, Vector(2, 0)); Matrix xform(2, 2, 0); xform.diagonal() = 1; DirectionCoordinate origGalactic( MDirection::GALACTIC, p, Quantity(0.15064170309942734, "rad"), Quantity(-0.0027706408478612907, "rad"), Quantity(-6, "arcsec"), Quantity(6, "arcsec"), xform, 1573.5, -6.5 ); Record r; origGalactic.save(r, "coords"); cout << "*** original " << r << endl; Quantity angle; DirectionCoordinate converted = origGalactic.convert(angle, MDirection::J2000); Record s; converted.save(s, "coords"); cout << "*** converted " << s << endl; Vector origRefVal = origGalactic.referenceValue(); Vector units = origGalactic.worldAxisUnits(); MDirection origRefDir( Quantity(origRefVal[0], units[0]), Quantity(origRefVal[1], units[1]), MDirection::GALACTIC ); Quantum > expRefDir = MDirection::Convert( origRefDir, MDirection::J2000 )().getAngle(); Vector gotRefDir = converted.referenceValue(); AlwaysAssert( near( gotRefDir[0], expRefDir.getValue(units[0])[0], 1e-8 ), AipsError ); AlwaysAssert( near( gotRefDir[1], expRefDir.getValue(units[1])[1], 1e-8 ), AipsError ); Vector origWorld; Vector pixel(2, 20); origGalactic.toWorld(origWorld, pixel); MDirection origDir2( Quantity(origWorld[0], units[0]), Quantity(origWorld[1], units[1]), MDirection::GALACTIC ); Quantum > expDir = MDirection::Convert( origDir2, MDirection::J2000 )().getAngle(); Vector gotDir; converted.toWorld(gotDir, pixel); Vector pixela(2, -6.5); pixela[0] = 1573.5; Vector pixelb = pixela.copy(); pixelb[0] = 1574.5; //Vector pixelc = pixela.copy(); //pixelc[0] = 1500; Vector worlda, worldb, worldc; converted.toWorld(worlda, pixela); converted.toWorld(worldb, pixelb); //converted.toWorld(worldc, pixelc); cout << "*** a " << worlda << endl; cout << "*** b " << worldb << endl; //cout << "*** c " << worldc << endl; cout << "*** inc " << converted.increment() << endl; std::map, Double> expAngle; expAngle[std::pair(0, -80)] = -165.668663; expAngle[std::pair(0, -60)] = -159.272545; expAngle[std::pair(0, -40)] = -136.458146; expAngle[std::pair(0, -20)] = -56.3749304; expAngle[std::pair(0, 0)] = -23.4795798; expAngle[std::pair(0, 20)] = -15.2757315; expAngle[std::pair(0, 40)] = -12.3186824; expAngle[std::pair(0, 60)] = -11.4331534; expAngle[std::pair(0, 80)] = -11.9775365; expAngle[std::pair(4, -80)] = 128.395658; expAngle[std::pair(4, -60)] = 114.424745; expAngle[std::pair(4, -40)] = 93.4929423; expAngle[std::pair(4, -20)] = 71.2514923; expAngle[std::pair(4, 0)] = 55.048387; expAngle[std::pair(4, 20)] = 45.7495975; expAngle[std::pair(4, 40)] = 41.4598396; expAngle[std::pair(4, 60)] = 40.9329567; expAngle[std::pair(4, 80)] = 44.0088699; expAngle[std::pair(8, -80)] = 68.355075; expAngle[std::pair(8, -60)] = 61.8452755; expAngle[std::pair(8, -40)] = 58.6597989; expAngle[std::pair(8, -20)] = 58.6455176; expAngle[std::pair(8, 0)] = 61.8014664; expAngle[std::pair(8, 20)] = 68.2804866; expAngle[std::pair(8, 40)] = 78.0043392; expAngle[std::pair(8, 60)] = 89.9436203; expAngle[std::pair(8, 80)] = 101.89424; expAngle[std::pair(12, -80)] = 11.9775367; expAngle[std::pair(12, -60)] = 11.4331535; expAngle[std::pair(12, -40)] = 12.3186824; expAngle[std::pair(12, -20)] = 15.2757315; expAngle[std::pair(12, 0)] = 23.4795796; expAngle[std::pair(12, 20)] = 56.3749301; expAngle[std::pair(12, 40)] = 136.458146; expAngle[std::pair(12, 60)] = 159.272545; expAngle[std::pair(12, 80)] = 165.668663; expAngle[std::pair(16, -80)] = -44.0088701; expAngle[std::pair(16, -60)] = -40.9329566; expAngle[std::pair(16, -40)] = -41.4598395; expAngle[std::pair(16, -20)] = -45.7495973; expAngle[std::pair(16, 0)] = -55.0483868; expAngle[std::pair(16, 20)] = -71.2514922; expAngle[std::pair(16, 40)] = -93.4929422; expAngle[std::pair(16, 60)] = -114.424745; expAngle[std::pair(16, 80)] = -128.395658; expAngle[std::pair(20, -80)] = -101.894241; expAngle[std::pair(20, -60)] = -89.9436205; expAngle[std::pair(20, -40)] = -78.0043394; expAngle[std::pair(20, -20)] = -68.2804869; expAngle[std::pair(20, 0)] = -61.8014666; expAngle[std::pair(20, 20)] = -58.6455178; expAngle[std::pair(20, 40)] = -58.659799; expAngle[std::pair(20, 60)] = -61.8452755; expAngle[std::pair(20, 80)] = -68.3550746; // test various points around the sky for (Int ha=0; ha<24; ha+=4) { for (Int dec=-80; dec<85; dec+=20) { DirectionCoordinate j2000( MDirection::J2000, p, Quantity(ha*15, "deg"), Quantity(dec, "deg"), Quantity(-1, "arcsec"), Quantity(1, "arcsec"), xform, 100, 100 ); units = j2000.worldAxisUnits(); Vector j2000RefVal = j2000.referenceValue(); MDirection j2000RefDir( Quantity(j2000RefVal[0], units[0]), Quantity(j2000RefVal[1], units[1]), MDirection::J2000 ); DirectionCoordinate toGalactic = j2000.convert( angle, MDirection::GALACTIC ); AlwaysAssert( allEQ(toGalactic.worldAxisUnits(), units), AipsError ); AlwaysAssert( allEQ(toGalactic.linearTransform(), xform), AipsError ); AlwaysAssert( toGalactic.projection().type() == p.type(), AipsError ); AlwaysAssert( near( angle.getValue("deg"), expAngle[std::pair(ha, dec)], 1e-7 ), AipsError ); DirectionCoordinate backToJ2000 = toGalactic.convert( angle, MDirection::J2000 ); Vector newJ2000RefVal = backToJ2000.referenceValue(); if (newJ2000RefVal[0] < -0.1) { newJ2000RefVal[0] += 2*M_PI; } AlwaysAssert( allNearAbs(newJ2000RefVal, j2000RefVal, 1e-9), AipsError ); AlwaysAssert( near( angle.getValue("deg"), -expAngle[std::pair(ha, dec)], 1e-7 ), AipsError ); } } } { cout << "*** test hasSquarePixels()" << endl; DirectionCoordinate dc( MDirection::J2000, Projection::SIN, Quantity(1, "rad"), Quantity(1, "rad"), Quantity(-6, "arcsec"), Quantity(6, "arcsec"), xform, 1573.5, -6.5 ); AlwaysAssert(dc.hasSquarePixels(), AipsError); dc = DirectionCoordinate( MDirection::J2000, Projection::SIN, Quantity(1, "rad"), Quantity(1, "rad"), Quantity(-6, "arcsec"), Quantity(-6, "arcsec"), xform, 1573.5, -6.5 ); dc = DirectionCoordinate( MDirection::J2000, Projection::SIN, Quantity(1, "rad"), Quantity(1, "rad"), Quantity(6, "arcsec"), Quantity(-6, "arcsec"), xform, 1573.5, -6.5 ); AlwaysAssert(dc.hasSquarePixels(), AipsError); dc = DirectionCoordinate( MDirection::J2000, Projection::SIN, Quantity(1, "rad"), Quantity(1, "rad"), Quantity(6, "arcsec"), Quantity(6, "arcsec"), xform, 1573.5, -6.5 ); AlwaysAssert(dc.hasSquarePixels(), AipsError); dc = DirectionCoordinate( MDirection::J2000, Projection::SIN, Quantity(1, "rad"), Quantity(1, "rad"), Quantity(7, "arcsec"), Quantity(6, "arcsec"), xform, 1573.5, -6.5 ); AlwaysAssert(! dc.hasSquarePixels(), AipsError); } { // test isNCP() Vector parms(2, 0); Projection projection(Projection::SIN, parms); Double refLong = 0; Double refLat = 0.5; Double inc = M_PI/180/3600; Matrix xform = Matrix::identity(2); Double refX = 0; Double refY = 0; DirectionCoordinate dc( MDirection::J2000, projection, refLong, refLat, inc, -inc, xform, refX, refY ); AlwaysAssert(! dc.isNCP(), AipsError); parms[1] = 1/tan(refLat); projection = Projection(Projection::SIN, parms); dc = DirectionCoordinate( MDirection::J2000, projection, refLong, refLat, inc, -inc, xform, refX, refY ); AlwaysAssert(dc.isNCP(), AipsError); projection = Projection(Projection::TAN, parms); dc = DirectionCoordinate( MDirection::J2000, projection, refLong, refLat, inc, -inc, xform, refX, refY ); AlwaysAssert(! dc.isNCP(), AipsError); } { cout << "*** test toWorld without converting to conversion layer frame" << endl; Matrix xform(2, 2, 0); xform.diagonal() = 1; DirectionCoordinate dc( MDirection::J2000, Projection::SIN, Quantity(0, "deg"), Quantity(0, "deg"), Quantity(-1, "arcsec"), Quantity(1, "arcsec"), xform, 0, 0 ); dc.setReferenceConversion(MDirection::GALACTIC); Vector pixel(2, 60); Vector world(2); // default uses True dc.toWorld(world, pixel); AlwaysAssert(near(world[0], 1.6811, 1e-5), AipsError); AlwaysAssert(near(world[1], -1.05011, 1e-5), AipsError); dc.toWorld(world, pixel, True); AlwaysAssert(near(world[0], 1.6811, 1e-5), AipsError); AlwaysAssert(near(world[1], -1.05011, 1e-5), AipsError); dc.toWorld(world, pixel, False); AlwaysAssert(near(world[0], 6.28289, 1e-5), AipsError); AlwaysAssert(near(world[1], 2.90888e-4, 1e-5), AipsError); } } catch (const std::exception& x) { cerr << "aipserror: error " << x.what() << endl; return (1); } cout << "ok" << endl; return (0); } DirectionCoordinate makeCoordinate(MDirection::Types type, Projection& proj, Vector& crval, Vector& crpix, Vector& cdelt, Matrix& xform) { crval.resize(2); crpix.resize(2); cdelt.resize(2); crval(0) = 0.1; crval(1) = -0.5; crpix(0) = 100.0; crpix(1) = 120.0; cdelt(0) = -1e-6; cdelt(1) = 2e-6; xform.resize(2,2); xform = 0.0; xform.diagonal() = 1.0; proj = Projection::SIN; return DirectionCoordinate(type, proj, crval(0), crval(1), cdelt(0), cdelt(1), xform, crpix(0), crpix(1), 999.0, 999.0); } void doit (DirectionCoordinate& lc, MDirection::Types type, const Vector& axisNames, const String& axisUnit) { // Test copy constructor { DirectionCoordinate lc2(lc); if (!lc.near(lc2)) { throw(AipsError("Failed copy constructor test")); } } // Test assignment { DirectionCoordinate lc2; lc2 = lc; if (!lc.near(lc2)) { throw(AipsError("Failed assignment test")); } } // Test member functions if (lc.type() != Coordinate::DIRECTION) { throw(AipsError("Failed type test")); } if (lc.directionType() != type) { throw(AipsError("Failed directionType test")); } if (lc.showType() != "Direction") { throw(AipsError("Failed showType test")); } // if (lc.nPixelAxes() != 2) { throw(AipsError("Failed nPixelAxes test")); } // if (lc.nWorldAxes() != 2) { throw(AipsError("Failed nWorldAxes test")); } // if (!allEQ(axisNames, lc.worldAxisNames())) { throw(AipsError("Failed world axis name recovery test")); } Vector names(axisNames.copy()); names(0) = "Horsies"; if (!lc.setWorldAxisNames(names)) { throw(AipsError(String("Failed to set world axis name because") + lc.errorMessage())); } if (!allEQ(names, lc.worldAxisNames())) { throw(AipsError("Failed axis name set/recovery test")); } // Vector units(2); units(0) = axisUnit; units(1) = axisUnit; if (!allEQ(units, lc.worldAxisUnits())) { throw(AipsError("Failed world axis units recovery test")); } units(0) = "deg"; if (!lc.setWorldAxisUnits(units)) { throw(AipsError(String("Failed to set world axis units because ") + lc.errorMessage())); } if (!allEQ(units, lc.worldAxisUnits())) { throw(AipsError("Failed world axis units set/recovery test")); } // // Test record saving // TableRecord rec; if (!lc.save(rec, "Direction")) { throw(AipsError("Coordinate saving to Record failed")); } DirectionCoordinate* plc = DirectionCoordinate::restore(rec, "Direction"); if (!plc->near(lc, 1e-6)) { throw(AipsError(String("Restore failed with") + plc->errorMessage())); } delete plc; // // Test clone // Coordinate* plc2 = lc.clone(); if (!plc2->near(lc, 1e-6)) { throw(AipsError("Clone function failed")); } delete plc2; } void doit2 (DirectionCoordinate& lc, Vector& crval, Vector& crpix, Vector& cdelt, Matrix& xform) { if (!allEQ(crval, lc.referenceValue())) { throw(AipsError("Failed reference value recovery test")); } // if (!allEQ(cdelt, lc.increment())) { throw(AipsError("Failed increment recovery test")); } // if (!allEQ(crpix, lc.referencePixel())) { throw(AipsError("Failed reference pixel recovery test")); } // if (!allEQ(xform, lc.linearTransform())) { throw(AipsError("Failed Direction transform recovery test")); } // crval /= 2.0; if (!lc.setReferenceValue(crval)) { throw(AipsError(String("Failed to set reference value because") + lc.errorMessage())); } if (!allEQ(crval, lc.referenceValue())) { throw(AipsError("Failed reference value set/recovery test")); } // cdelt *= 3.0; if (!lc.setIncrement(cdelt)) { throw(AipsError(String("Failed to set increment because") + lc.errorMessage())); } if (!allEQ(cdelt, lc.increment())) { throw(AipsError("Failed increment set/recovery test")); } // crpix += 13.0; if (!lc.setReferencePixel(crpix)) { throw(AipsError(String("Failed to set reference pixel because") + lc.errorMessage())); } if (!allEQ(crpix, lc.referencePixel())) { throw(AipsError("Failed reference pixel set/recovery test")); } // xform.diagonal() = -2.0; Matrix xformOut(2,2); xformOut = 0.; xformOut.diagonal() = 1.0; Vector cdeltOut; cdeltOut = cdelt * -2.0; if (!lc.setLinearTransform(xform)) { throw(AipsError(String("Failed to set linear transform because") + lc.errorMessage())); } if (!allEQ(xformOut, lc.linearTransform())) { throw(AipsError("Failed linear transform set/recovery test (wrong resulting xform)")); } if (!allEQ(cdeltOut, lc.increment())) { throw(AipsError("Failed linear transform set/recovery test (wrong resulting cdelt)")); } } void doit3 (DirectionCoordinate& lc) { // // Test conversion // const Vector& refPix = lc.referencePixel(); const Vector& refVal = lc.referenceValue(); Vector pixel(2), world(2); // pixel = refPix.copy(); if (!lc.toWorld(world, pixel)) { throw(AipsError(String("toWorld conversion (0) failed because ") + lc.errorMessage())); } if (!allNear(refVal, world, 1e-6)) { throw(AipsError("World values (0) are wrong")); } // world = refVal.copy(); if (!lc.toPixel(pixel, world)) { throw(AipsError(String("toPixel conversion (0) failed because ") + lc.errorMessage())); } if (!allNear(refPix, pixel, 1e-6)) { throw(AipsError("Pixel values (0) are wrong")); } // Vector axisUnits = lc.worldAxisUnits().copy(); axisUnits.set("rad"); if (!lc.setWorldAxisUnits(axisUnits)) { throw(AipsError(String("failed to set world axis units to radians because ") + lc.errorMessage())); } // pixel(0) = 12.2; pixel(1) = -22.2; // if (!lc.toWorld(world, pixel)) { throw(AipsError(String("toWorld conversion (1) failed because ") + lc.errorMessage())); } Vector pixel2(2); if (!lc.toPixel(pixel2, world)) { throw(AipsError(String("toPixel conversion (1) failed because ") + lc.errorMessage())); } if (!allNear(pixel2, pixel, 1e-6)) { throw(AipsError("Coordinate conversion reflection 1 failed")); } MVDirection dirV; if (!lc.toWorld(dirV, pixel)) { throw(AipsError(String("toWorld conversion (3) failed because ") + lc.errorMessage())); } if (!allNear(dirV.get(), world, 1e-6)) { throw(AipsError("Coordinate conversion values (MVDirection) are wrong")); } Vector pix2; pix2 = lc.toPixel(dirV); AlwaysAssert(allNear(pix2, pixel, 1e-6), AipsError); axisUnits.set("deg"); if (!lc.setWorldAxisUnits(axisUnits)) { throw(AipsError(String("failed to set world axis units to degrees because ") + lc.errorMessage())); } if (!allNear(dirV.get(), world, 1e-6)) { throw(AipsError("Coordinate conversion values (MVDirection) are wrong")); } Vector pixel3; if (!lc.toPixel(pixel3, dirV)) { throw(AipsError(String("toPixel conversion (3) failed because ") + lc.errorMessage())); } if (!allNear(pixel3, pixel, 1e-6)) { throw(AipsError("Coordinate conversion reflection 3 failed")); } // MDirection dir; if (!lc.toWorld(dir, pixel)) { throw(AipsError(String("toWorld conversion (2) failed because ") + lc.errorMessage())); } if (!allNear(dir.getValue().get(), world, 1e-6)) { throw(AipsError("Coordinate conversion values (MDirection) are wrong")); } axisUnits.set("rad"); if (!lc.setWorldAxisUnits(axisUnits)) { throw(AipsError(String("failed to set world axis units to radians because ") + lc.errorMessage())); } if (!lc.toPixel(pixel3, dir)) { throw(AipsError(String("toPixel conversion (2) failed because ") + lc.errorMessage())); } if (!allNear(pixel3, pixel, 1e-6)) { throw(AipsError("Coordinate conversion reflection 2 failed")); } MDirection converted = MDirection::Convert(dir, MDirection::SUPERGAL)(); Vector pixel4; if (!lc.toPixel(pixel4, converted)) { throw(AipsError(String("toPixel conversion (3) after conversion failed because ") + lc.errorMessage())); } if (!allNear(pixel4, pixel, 1e-5)) { throw(AipsError("Coordinate conversion reflection 3 failed")); } // relative/absolute world { Vector refVal = lc.referenceValue().copy(); Vector world4 = refVal; lc.makeWorldRelative(world4); if (!allNearAbs(world4, 0.0, 1e-6)) { throw(AipsError("Coordinate makeWorldRelative 1 gave wrong results")); } // Vector incr = 100.0*lc.increment().copy(); world4 += incr; Vector tmp = world4.copy(); lc.makeWorldRelative(world4); Vector world4b(world4.copy()); lc.makeWorldAbsolute (world4); if (!allNearAbs(world4, tmp, 1e-6)) { throw(AipsError("Coordinate makeWorldAbsolute/Relative reflection 1 gave wrong results")); } // world4 = lc.referenceValue() - 100.0*incr; tmp = world4; lc.makeWorldRelative(world4); lc.makeWorldAbsolute (world4); if (!allNearAbs(world4, tmp, 1e-6)) { throw(AipsError("Coordinate makeWorldAbsolute 2 gave wrong results")); } } // { MDirection coord; lc.toWorld(coord, lc.referencePixel() + 10.0); // MVDirection mv1 = coord.getValue(); lc.makeWorldRelative(coord); MVDirection mv2 = coord.getValue(); lc.makeWorldAbsolute(coord); MVDirection mv3 = coord.getValue(); if (!allNearAbs(mv1.get(),mv3.get(), 1e-6)) { throw(AipsError("Coordinate makeWorldAbsolute/Relative reflection 2 failed")); } } // // Formatting. // Int prec; Coordinate::formatType fType = Coordinate::SCIENTIFIC; lc.getPrecision(prec, fType, True, 6, 4, 2); if (prec != 6) { throw(AipsError("Failed getPrecision test 1")); } fType = Coordinate::FIXED; lc.getPrecision(prec, fType, True, 6, 4, 2); if (prec != 4) { throw(AipsError("Failed getPrecision test 2")); } // String unit("rad"); Double val = 0.12343; Quantum valq(0.12343, "rad"); valq.convert(Unit("deg")); // String str = lc.format(unit, Coordinate::FIXED, val, 0, True, True, 4); if (unit!="rad" || str != "0.1234") { throw(AipsError("Failed format test 1a")); } uInt axis = 0; Int prec2 = 4; str = lc.formatQuantity(unit, Coordinate::FIXED, valq, axis, True, True, prec2); if (unit!="rad" || str != "0.1234") { throw(AipsError("Failed format test 1b")); } // str = lc.format(unit, Coordinate::SCIENTIFIC, val, 0, True, True, 4); if (unit!="rad" || str != "1.2343e-01") { throw(AipsError("Failed format test 2a")); } str = lc.formatQuantity(unit, Coordinate::SCIENTIFIC, valq, 0, True, True, 4); if (unit!="rad" || str != "1.2343e-01") { throw(AipsError("Failed format test 2b")); } // str = lc.format(unit, Coordinate::FIXED, val, 1, True, True, 4); if (unit!="rad" || str != "0.1234") { throw(AipsError("Failed format test 3a")); } str = lc.formatQuantity(unit, Coordinate::FIXED, valq, 1, True, True, 4); if (str != "0.1234") { throw(AipsError("Failed format test 3b")); } // str = lc.format(unit, Coordinate::SCIENTIFIC, val, 1, True, True, 4); if (unit!="rad" || str != "1.2343e-01") { throw(AipsError("Failed format test 4a")); } str = lc.formatQuantity(unit, Coordinate::SCIENTIFIC, valq, 1, True, True, 4); if (unit!="rad" || str != "1.2343e-01") { throw(AipsError("Failed format test 4b")); } // // Fairly ugly way to work out what kind of MDirection // we have in the DC. Need to know this to figure out what // the formatting is going to do ! // str = lc.format(unit, Coordinate::TIME, val, 0, True, True, 4); String str2 = lc.formatQuantity(unit, Coordinate::TIME, valq, 0, True, True, 4); MDirection::GlobalTypes globalType = dir.globalType(lc.directionType()); if (globalType==MDirection::GRADEC) { if (str != "00:28:17.2843" || str2 != "00:28:17.2843") { throw(AipsError("Failed format test 5a")); } } else if (globalType==MDirection::GLONGLAT) { if (str != "+007.04.19.2650" || str2 != "+007.04.19.2650") { throw(AipsError("Failed format test 5b")); } } else { throw(AipsError("Internal error")); } // str = lc.format(unit, Coordinate::TIME, val, 1, True, True, 4); str2 = lc.formatQuantity(unit, Coordinate::TIME, valq, 1, True, True, 4); if (globalType==MDirection::GRADEC) { if (str != "+07.04.19.2650" || str2 != "+07.04.19.2650") { throw(AipsError("Failed format test 6a")); } } else if (globalType==MDirection::GLONGLAT) { if (str != "+07.04.19.2650" || str2 != "+07.04.19.2650") { throw(AipsError("Failed format test 6b")); } } else { throw(AipsError("Internal error")); } } void doit4 (DirectionCoordinate& dC) // // Mixed conversions // { Vector pixelIn(2), worldIn(2), pixelOut, worldOut; Vector pixelAxes(2), worldAxes(2); Vector units = dC.worldAxisUnits().copy(); Vector refVal = dC.referenceValue().copy(); Vector refPix = dC.referencePixel().copy(); // IPosition shape(2, 512); if (!dC.setWorldMixRanges(shape)) { throw(AipsError(String("setMixRanges failed because ") + dC.errorMessage())); } // // Forced errors // pixelAxes.set(False); worldAxes.set(False); Vector minWorld = dC.worldMixMin().copy(); Vector maxWorld = dC.worldMixMax().copy(); if (dC.toMix(worldOut, pixelOut, worldIn, pixelIn, worldAxes, pixelAxes, minWorld, maxWorld)) { throw(AipsError(String("Forced fail 1 of toMix did not occur"))); } pixelAxes(0) = True; if (dC.toMix(worldOut, pixelOut, worldIn, pixelIn, worldAxes, pixelAxes, minWorld, maxWorld)) { throw(AipsError(String("Forced fail 2 of toMix did not occur"))); } // // pixel,pixel->world,world // pixelAxes.set(True); worldAxes.set(False); pixelIn = dC.referencePixel().copy(); // if (!dC.toMix(worldOut, pixelOut, worldIn, pixelIn, worldAxes, pixelAxes, minWorld, maxWorld)){ throw(AipsError(String("Failed pixel->world conversion failed because ") + dC.errorMessage())); } if (!allNear(pixelOut, dC.referencePixel(), 1e-8)) { throw(AipsError(String("Failed pixel->world consistency test 1a"))); } if (!allNear(worldOut, dC.referenceValue(), 1e-8)) { throw(AipsError(String("Failed pixel->world consistency test 1b"))); } // // world,world->pixel,pixel // pixelAxes.set(False); worldAxes.set(True); worldIn = dC.referenceValue().copy(); if (!dC.toMix(worldOut, pixelOut, worldIn, pixelIn, worldAxes, pixelAxes, minWorld, maxWorld)) { throw(AipsError(String("Failed world->pixel conversion failed because ") + dC.errorMessage())); } if (!allNear(pixelOut, dC.referencePixel(), 1e-8)) { throw(AipsError(String("Failed world->pixel consistency test 1a"))); } if (!allNear(worldOut, dC.referenceValue(), 1e-8)) { throw(AipsError(String("Failed world->pixel consistency test 1b"))); } // // world,pixel -> pixel,world // worldIn(0) = dC.referenceValue()(0); pixelIn(1) = dC.referencePixel()(1); pixelAxes.set(False); worldAxes.set(False); worldAxes(0) = True; pixelAxes(1) = True; if (!dC.toMix(worldOut, pixelOut, worldIn, pixelIn, worldAxes, pixelAxes, minWorld, maxWorld)) { throw(AipsError(String("Conversion failed because ") + dC.errorMessage())); } if (!allNear(pixelOut, dC.referencePixel(), 1e-6)) { throw(AipsError(String("Failed world->pixel consistency test 2a"))); } if (!allNear(worldOut, dC.referenceValue(), 1.0e-6)) { throw(AipsError(String("Failed world->pixel consistency test 2b"))); } // // pixel, world -> world, pixel // pixelIn(0) = dC.referencePixel()(0); worldIn(1) = dC.referenceValue()(1); pixelAxes.set(False); worldAxes.set(False); worldAxes(1) = True; pixelAxes(0) = True; if (!dC.toMix(worldOut, pixelOut, worldIn, pixelIn, worldAxes, pixelAxes, minWorld, maxWorld)) { throw(AipsError(String("Conversion failed because ") + dC.errorMessage())); } if (!allNear(pixelOut, dC.referencePixel(), 1e-6)) { throw(AipsError(String("Failed world->pixel consistency test 3a"))); } if (!allNear(worldOut, dC.referenceValue(), 1e-6)) { throw(AipsError(String("Failed world->pixel consistency test 3b"))); } // // world,pixel -> pixel,world -> world,pixel // Do it off the reference values // worldIn(0) = dC.referenceValue()(0) + 5*dC.increment()(0); pixelIn(1) = 2.32; pixelAxes.set(False); pixelAxes(1) = True; worldAxes.set(False); worldAxes(0) = True; Vector saveWorldIn(worldIn.copy()); Vector savePixelIn(pixelIn.copy()); if (!dC.toMix(worldOut, pixelOut, worldIn, pixelIn, worldAxes, pixelAxes, minWorld, maxWorld)) { throw(AipsError(String("Conversion failed because ") + dC.errorMessage())); } pixelIn(0) = pixelOut(0); worldIn(1) = worldOut(1); pixelAxes.set(False); pixelAxes(0) = True; worldAxes.set(False); worldAxes(1) = True; if (!dC.toMix(worldOut, pixelOut, worldIn, pixelIn, worldAxes, pixelAxes, minWorld, maxWorld)) { throw(AipsError(String("Conversion failed because ") + dC.errorMessage())); } if (!near(worldOut(0), saveWorldIn(0), 1e-6)) { throw(AipsError("Failed mixed conversion reflection test 1a")); } if (!near(pixelOut(1), savePixelIn(1), 1e-6)) { throw(AipsError("Failed mixed conversion reflection test 1a")); } // // pixel,world -> world,pixel -> pixel, world // Do it off the reference values // worldIn(1) = dC.referenceValue()(1) + 5*dC.increment()(1); pixelIn(0) = 2.32; pixelAxes.set(False); pixelAxes(0) = True; worldAxes.set(False); worldAxes(1) = True; saveWorldIn = worldIn.copy(); savePixelIn = pixelIn.copy(); if (!dC.toMix(worldOut, pixelOut, worldIn, pixelIn, worldAxes, pixelAxes, minWorld, maxWorld)) { throw(AipsError(String("Conversion failed because ") + dC.errorMessage())); } pixelIn(1) = pixelOut(1); worldIn(0) = worldOut(0); pixelAxes.set(False); pixelAxes(1) = True; worldAxes.set(False); worldAxes(0) = True; if (!dC.toMix(worldOut, pixelOut, worldIn, pixelIn, worldAxes, pixelAxes, minWorld, maxWorld)) { throw(AipsError(String("Conversion failed because ") + dC.errorMessage())); } if (!near(worldOut(1), saveWorldIn(1), 1e-6)) { throw(AipsError("Failed mixed conversion reflection test 2a")); } if (!near(pixelOut(0), savePixelIn(0), 1e-6)) { throw(AipsError("Failed mixed conversion reflection test 2a")); } } void doit5 (DirectionCoordinate& dC) // // Fourier coordinate. LinearXform has been tested // pretty hard (it does the work for DirectionCoordinate) so don't fuss about with // the values much // { Vector axes(2, True); Vector shape(2); shape(0) = 128; shape(1) = 256; // All axes { dC.setWorldAxisUnits(Vector(2, "arcmin")); Vector inc(2, 1); inc[0] = -1; dC.setIncrement(inc); std::unique_ptr pC(dC.makeFourierCoordinate (axes, shape)); // Vector units2 = pC->worldAxisUnits(); Vector names2 = pC->worldAxisNames(); Vector crval2 = pC->referenceValue(); Vector crpix2 = pC->referencePixel(); Vector inc2 = pC->increment(); if (units2(0)!=String("lambda") || units2(1)!=String("lambda")) { throw(AipsError("makeFourierCoordinate (1) failed units test")); } if (names2(0)!=String("UU") || names2(1)!=String("VV")) { throw(AipsError("makeFourierCoordinate (1) failed names test")); } if (!allNear(crval2,0.0,1e-13)) { throw(AipsError("makeFourierCoordinate (1) failed crval test")); } // inc tests verity CAS-13629 if (! near(inc2[0], -2*13.4287, 1e-5)) { throw(AipsError("makeFourierCoordinate (1) failed cdelt[0] test")); } if (! near(inc2[1], 13.4287, 1e-5)) { throw(AipsError("makeFourierCoordinate (1) failed cdelt[1] test")); } for (uInt i=0; inPixelAxes(); i++) { if (!near(Double(Int(shape(i)/2)), crpix2(i))) { throw(AipsError("makeFourierCoordinate (1) failed crpix test")); } } } // Not all axes { axes.set(True); axes(1) = False; Coordinate* pC = dC.makeFourierCoordinate (axes, shape); if (pC) { delete pC; throw(AipsError("Failed to induce forced error in makeFourierCoordinate (2)")); } delete pC; } } void doit6 () { Vector crval(2); Vector cdelt(2); Vector crpix(2); crval(0) = 10.0; crval(1) = -50.0; crpix(0) = 100.0; crpix(1) = 120.0; cdelt(0) = -10.0 / 3600.0; cdelt(1) = 20.0 / 3600; Matrix xform(2,2); xform = 0.0; xform.diagonal() = 1.0; Projection proj(Projection::CAR); MDirection::Types type = MDirection::J2000; Double longPole = 175; Double latPole = -90; // DirectionCoordinate dc(type, proj, crval(0)*M_PI/180.0, crval(1)*M_PI/180.0, cdelt(0)*M_PI/180.0, cdelt(1)*M_PI/180.0, xform, crpix(0), crpix(1), longPole*M_PI/180.0, latPole*M_PI/180.0); // Vector poles = dc.longLatPoles(); Bool ok = (near(poles(0), crval(0)) && near(poles(1), crval(1)) && near(poles(2), longPole)); // The lat pole get recomputed so don't test it if (!ok) { throw(AipsError("Failed longLatPoles 1 extraction test")); } // Quantum refLong(10.0, Unit("deg")); Quantum refLat(-50.0, Unit("deg")); Quantum incLong(-10.0, Unit("arcsec")); Quantum incLat(20.0, Unit("arcsec")); Quantum longPole2(175.0, Unit("deg")); Quantum latPole2(-90.0, Unit("deg")); // DirectionCoordinate dc2(type, proj, refLong, refLat, incLong, incLat, xform, crpix(0), crpix(1), longPole2, latPole2); // Vector poles2 = dc2.longLatPoles(); ok = (near(poles2(0), refLong.getValue(Unit("deg"))) && near(poles2(1), refLat.getValue(Unit("deg"))) && near(poles2(2), longPole2.getValue("deg"))); // The lat pole get recomputed so don't test it if (!ok) { throw(AipsError("Failed longLatPoles 2 extraction test")); } } void doit7 () { // // Test conversion with reference change // Projection proj; Vector crval, crpix, cdelt; Matrix xform; // DirectionCoordinate lc = makeCoordinate(MDirection::J2000, proj, crval, crpix, cdelt, xform); // Vector units = lc.worldAxisUnits().copy(); units = String("deg"); lc.setWorldAxisUnits(units); Vector rv(2); rv(0) = 120.0; rv(1) = -35.0; lc.setReferenceValue(rv); // lc.setReferenceConversion(MDirection::GALACTIC); MDirection::Types type2; lc.getReferenceConversion (type2); if (type2!=MDirection::GALACTIC) { throw(AipsError(String("did not recover referenceConversion type"))); } // Vector pixel = lc.referencePixel().copy() + 10.0; Vector world; if (!lc.toWorld(world, pixel)) { throw(AipsError(String("toWorld + type change conversion (1) failed because ") + lc.errorMessage())); } // Vector pixel2; if (!lc.toPixel(pixel2, world)) { throw(AipsError(String("toPixel + type change conversion (1) failed because ") + lc.errorMessage())); } if (!allNear(pixel, pixel2, 1e-5)) { throw(AipsError("Reference change conversion reflection 1 failed")); } } void doit8 () { Projection proj; Vector crval, crpix, cdelt; Matrix xform; // No frame conversion { DirectionCoordinate lc = makeCoordinate(MDirection::J2000, proj, crval, crpix, cdelt, xform); // Vector failures, failures2; const Int nCoord = 1000; Matrix pixel(2, nCoord), pixel2; Matrix world(2, nCoord); // Double incr = 1001.0 / nCoord; // Make sure pixel!=0 so dont have problems with 'near' function Double pix = -500.0; for (Int i=0; i world2; for (Int i=0; i failures, failures2; const Int nCoord = 1000; Matrix pixel(2, nCoord), pixel2; Matrix world(2, nCoord); // Double incr = 1001.0 / nCoord; // Make sure pixel!=0 so dont have problems with 'near' function Double pix = -500.0; for (Int i=0; i world2; for (Int i=0; i xform(2,2); xform = 0.0; xform.diagonal() = 1.0; DirectionCoordinate dc(MDirection::SUN, Projection::SIN, 0.0, 0.0, -1.0e-6, 1.0e-6, xform, 0.0, 0.0, 999.0, 999.0); Vector pixel(2), world; pixel = 0.0; Bool ok = dc.toWorld(world, pixel); AlwaysAssert(ok, AipsError); cerr << "pixel, world = " << pixel << world << endl; } void doit10 () { Matrix xform(2,2); xform = 0.0; xform.diagonal() = 1.0; DirectionCoordinate dc(MDirection::GALACTIC, Projection::SIN, 0.0, 0.0, -1.0e-6, 1.0e-6, xform, 0.0, 0.0, 999.0, 999.0); // Vector units = dc.worldAxisUnits().copy(); units(0) = String("deg"); units(1) = String("deg"); if (!dc.setWorldAxisUnits(units)) { throw(AipsError(String("failed to set Units because ") + dc.errorMessage())); } // Vector incr = dc.increment().copy(); incr(0) = 1.0; if (!dc.setIncrement(incr)) { throw(AipsError(String("failed to set increment because ") + dc.errorMessage())); } // /* Vector refVal = dc.referenceValue().copy(); refVal(0) = 200; if (!dc.setReferenceValue(refVal)) { throw(AipsError(String("failed to set refVal because ") + dc.errorMessage())); } */ // Vector refPix = dc.referencePixel().copy(); refPix(0) = 200; if (!dc.setReferencePixel(refPix)) { throw(AipsError(String("failed to set refPix because ") + dc.errorMessage())); } // if (!dc.cylindricalFix (180, 180)) { throw(AipsError(String("failed to make cylindrical fix because") + dc.errorMessage())); } } casacore-3.7.1/coordinates/Coordinates/test/tFrequencyAligner.cc000066400000000000000000000226061476623553700250330ustar00rootroot00000000000000//# tFrequencyAligner.cc: Test program for FrequencyAligner //# Copyright (C) 1998,1999,2000,2001,2002,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include SpectralCoordinate makeLinearCoordinate(MFrequency::Types type, Double& crval, Double& cdelt, Double& crpix, Double& restFreq, const String& unit); int main() { try { Double f0, finc, refchan, restFreq; Vector freqs; Matrix xform(1,1); xform(0,0) = 1.0; String unit("GHz"); // Make SC MFrequency::Types sysIn(MFrequency::TOPO); MFrequency::Types sysOut(MFrequency::BARY); SpectralCoordinate lc = makeLinearCoordinate(sysIn, f0, finc, refchan, restFreq, unit); // Make aligner const uInt nPix = 16; Quantum t(50237.29, Unit(String("d"))); MVEpoch t2(t); MEpoch refEpoch(t2); // MPosition pos; MeasTable::Observatory(pos, String("ATCA")); // Quantum lon(0.0,Unit(String("rad"))); Quantum lat(-35.0,Unit(String("deg"))); MDirection dir(lon, lat, MDirection::J2000); FrequencyAligner fa(lc, nPix, refEpoch, dir, pos, sysOut); InterpolateArray1D::InterpolationMethod method=InterpolateArray1D::linear; Bool extrapolate=False; Bool useCachedX = False; // Generate some data Vector maskIn(nPix,True), maskOut(nPix); Vector yOut(nPix), yIn(nPix), yOut2(nPix); Float val = 0.0; for (uInt i=0; i tt(50237.50, Unit(String("d"))); MVEpoch t3(tt); MEpoch epoch(t3); cerr << "No Interpolation" << endl; AlwaysAssert(!fa.align (yOut, maskOut, yIn, maskIn, epoch, useCachedX, method, extrapolate), AipsError); /* cerr << " yIn = " << yIn << endl; cerr << " yOut = " << yOut << endl; */ AlwaysAssert (allNear(yOut, yIn, 1e-6), AipsError); // Remove tolerance fa.setTolerance (0.0); } // Align with new abcissa computed { Quantum tt(50237.50, Unit(String("d"))); MVEpoch t3(tt); MEpoch epoch(t3); cerr << "Interpolation" << endl; AlwaysAssert(fa.align (yOut, maskOut, yIn, maskIn, epoch, useCachedX, method, extrapolate),AipsError); /* cerr << " yIn = " << yIn << endl; cerr << " yOut = " << yOut << endl; */ AlwaysAssert (!allNear(yOut, yIn, 1e-6), AipsError); // Feeble test ! // Align with new abcissa taken from cached vector useCachedX = True; AlwaysAssert(fa.align (yOut2, maskOut, yIn, maskIn, epoch, useCachedX, method, extrapolate),AipsError); AlwaysAssert (allNear(yOut2, yOut, 1e-6), AipsError); } // Get Abcissas for fun { Vector xRefOut, xOut; fa.getReferenceAbcissa (xRefOut); fa.getAbcissa (xOut); /* cerr << " xRefOut = " << xRefOut << endl; cerr << " xOut = " << xOut << endl; */ } // Align many in one call { cerr << "Align many" << endl; const uInt nx = yIn.nelements(); const uInt ny = 5; IPosition shp(2,nx,ny); Array yInMany(shp); Array maskInMany(shp); Array yOutMany; Array maskOutMany; // IPosition pp(2,0); for (uInt j=0; j tt(50237.50, Unit(String("d"))); MVEpoch t3(tt); MEpoch epoch(t3); // uInt axis = 0; // Bool ok = fa.alignMany (yOutMany, maskOutMany, yInMany, maskInMany, axis, epoch, method, extrapolate); AlwaysAssert(ok, AipsError); ReadOnlyVectorIterator it(yOutMany,axis); Vector data1; Vector mask1; uInt cnt = 0; while (!it.pastEnd()) { if (cnt==0) { data1 = it.vector(); } else { AlwaysAssert (allNear(it.vector(), data1, 1e-6), AipsError); } it.next(); cnt++; } } // Copy constructor and test results the same FrequencyAligner va2(fa); { cerr << "Copy Constructor" << endl; Quantum tt(50237.50, Unit(String("d"))); MVEpoch t3(tt); MEpoch epoch(t3); Vector yOut3; useCachedX = True; AlwaysAssert(fa.align (yOut3, maskOut, yIn, maskIn, epoch, useCachedX, method, extrapolate),AipsError); // Use cached AlwaysAssert (allNear(yOut3, yOut, 1e-6), AipsError); // useCachedX = False; AlwaysAssert(fa.align (yOut2, maskOut, yIn, maskIn, epoch, useCachedX, method, extrapolate),AipsError); // Recompute AlwaysAssert (allNear(yOut3, yOut, 1e-6), AipsError); } // Assignment and test results the same FrequencyAligner va3; va3 = fa; { cerr << "Assignment operator" << endl; Quantum tt(50237.50, Unit(String("d"))); MVEpoch t3(tt); MEpoch epoch(t3); Vector yOut3; useCachedX = True; AlwaysAssert(fa.align (yOut3, maskOut, yIn, maskIn, epoch, useCachedX, method, extrapolate),AipsError); // Use cached AlwaysAssert (allNear(yOut3, yOut, 1e-6), AipsError); // useCachedX = False; AlwaysAssert(fa.align (yOut2, maskOut, yIn, maskIn, epoch, useCachedX, method, extrapolate),AipsError); // Recompute AlwaysAssert (allNear(yOut3, yOut, 1e-6), AipsError); } } catch (std::exception& x) { cerr << "aipserror: error " << x.what() << endl; return (1); } cout << "ok" << endl; return (0); } SpectralCoordinate makeLinearCoordinate (MFrequency::Types type, Double& f0, Double& finc, Double& refchan, Double& restFreq, const String& unit) { refchan = 10.5; finc = 4e6; f0 = 1.4e9; restFreq = 1.420405752E9; // SpectralCoordinate sc(type, f0, finc, refchan, restFreq); Vector units(1); units(0) = unit; sc.setWorldAxisUnits(units); return sc; } casacore-3.7.1/coordinates/Coordinates/test/tGaussianConvert.cc000066400000000000000000000150211476623553700246740ustar00rootroot00000000000000//# tGaussianConvert.cc: Test program for GaussianConvert //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include #include #include #include #include #include #include #include #include #include #include #include #include void doit (Double majorPixels, Double minorPixels, const Quantum& pa1, const CoordinateSystem& cSys, const Vector& worldAxes, Double exMinorWorld, Double exMajorWorld, Double exPAWorld); void doit2 (Vector& pixel, const CoordinateSystem& cSys, const Vector& worldAxes); int main() { try { CoordinateSystem cSys = CoordinateUtil::defaultCoords2D(); Vector worldAxes(2); worldAxes(0) = 0; worldAxes(1) = 1; // Vector units(2); units(0) = "arcsec"; units(1) = "arcsec"; cSys.setWorldAxisUnits(units); // // Axis conversions // { Vector deltas(2); deltas(0) = -1.0; deltas(1) = 1.0; cSys.setIncrement(deltas); // cout << "inc = " << cSys.increment() << endl; // Double majorPixels = 10.0; Double minorPixels = 5.0; Quantum pa1(30.0, Unit("deg")); doit(majorPixels, minorPixels, pa1, cSys, worldAxes, 5.0, 10.0, 30.0); } { Vector deltas(2); deltas(0) = 1.0; deltas(1) = 1.0; cSys.setIncrement(deltas); // cout << "inc = " << cSys.increment() << endl; // Double majorPixels = 10.0; Double minorPixels = 5.0; Quantum pa1(30, Unit("deg")); doit(majorPixels, minorPixels, pa1, cSys, worldAxes, 5.0, 10.0, 150.0); } { Vector deltas(2); deltas(0) = -1.0; deltas(1) = 2.0; cSys.setIncrement(deltas); // cout << "inc = " << cSys.increment() << endl; // Double majorPixels = 10.0; Double minorPixels = 10.0; Quantum pa1(0.0, Unit("deg")); doit(majorPixels, minorPixels, pa1, cSys, worldAxes, 10.0, 20.0, 0.0); } { Vector deltas(2); deltas(0) = -1.0; deltas(1) = 2.0; cSys.setIncrement(deltas); // cout << "inc = " << cSys.increment() << endl; // Double majorPixels = 10.0; Double minorPixels = 5.0; Quantum pa1(90.0, Unit("deg")); doit(majorPixels, minorPixels, pa1, cSys, worldAxes, 10.0, 10.0, 45.0); } // // Position conversions // { Vector pixel(cSys.referencePixel().copy()); doit2 (pixel, cSys, worldAxes); } } catch (std::exception& x) { cerr << "aipserror: error " << x.what() << endl; return 1; } cout << "ok" << endl; return 0; } void doit (Double majorPixels, Double minorPixels, const Quantum& pa1, const CoordinateSystem& cSys, const Vector& worldAxes, Double exMinorWorld, Double exMajorWorld, Double exPAWorld) { // // Convert from pixels to world // GaussianConvert gc(cSys, worldAxes); Quantum majorWorld, minorWorld; Quantum pa2; AlwaysAssert(gc.toWorld(majorWorld, minorWorld, pa2, majorPixels, minorPixels, pa1), AipsError); // // Reflect back to pixels // Double majorPixels2, minorPixels2; Quantum pa3; AlwaysAssert(gc.toPixel(majorPixels2, minorPixels2, pa3, majorWorld, minorWorld, pa2), AipsError); // cout << "Major axis : pixel, world, pixel = " << majorPixels << " " << majorWorld << " " << majorPixels2 << endl; cout << "Minor axis : pixel, world, pixel = " << minorPixels << " " << minorWorld << " " << minorPixels2 << endl; cout << "Position Angle : pixel, world, pixel = " << pa1 << " " << pa2 << " " << pa3 << endl << endl; // AlwaysAssert(near(majorWorld.getValue(),exMajorWorld,1e-6), AipsError); AlwaysAssert(near(minorWorld.getValue(),exMinorWorld,1e-6), AipsError); AlwaysAssert(near(pa2.getValue(),exPAWorld,1e-6), AipsError); // AlwaysAssert(near(majorPixels,majorPixels2,1e-6), AipsError); AlwaysAssert(near(minorPixels,minorPixels2,1e-6), AipsError); AlwaysAssert(near(pa1.getValue(),pa3.getValue(),1e-6), AipsError); } void doit2 (Vector& pixel, const CoordinateSystem& cSys, const Vector& worldAxes) { // // Convert from pixels to world // GaussianConvert gc(cSys, worldAxes); Vector > world; AlwaysAssert(gc.toWorld(world, pixel), AipsError); // // Reflect back to pixels // Vector pixel2; AlwaysAssert(gc.toPixel(pixel2, world), AipsError); AlwaysAssert(allNear(pixel2,pixel,1e-6), AipsError); // // Change units and see what happens // world(0).convert(Unit("arcsec")); world(1).convert(Unit("arcmin")); AlwaysAssert(gc.toPixel(pixel2, world), AipsError); AlwaysAssert(allNear(pixel2,pixel,1e-6), AipsError); } casacore-3.7.1/coordinates/Coordinates/test/tLinearCoordinate.cc000066400000000000000000000433551476623553700250160ustar00rootroot00000000000000//# tLinearCoordinate.cc: Test program for LinearCoordinate //# Copyright (C) 1998,1999,2000,2001,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include #include #include #include #include #include #include #include #include #include #include LinearCoordinate makeCoordinate(Vector& names, Vector& units, Vector& crpix, Vector& crval, Vector& cdelt, Matrix& xform, uInt n=2); int main() { try { Vector names, units; Vector crpix, crval, cdelt; Matrix xform; // Constructors { LinearCoordinate lc(1); } { LinearCoordinate lc = makeCoordinate(names, units, crpix, crval, cdelt, xform); } // Test near function { LinearCoordinate lc = makeCoordinate(names, units, crpix, crval, cdelt, xform); LinearCoordinate lc2 = makeCoordinate(names, units, crpix, crval, cdelt, xform); if (!lc.near(lc2)) { throw(AipsError("Failed near test 1")); } Vector excludeAxes(1, 1); if (!lc.near(lc2, excludeAxes)) { throw(AipsError("Failed near test 2")); } } // Test Quantum constructor interface { Matrix xform(2,2); xform = 0.0; xform.diagonal() = 1.0; // Vector crval(2); Vector crpix(2); Vector cdelt(2); Vector names(2); Vector units(2); // crval(0) = 0.1; crval(1) = 0.5; crpix(0) = 100.0; crpix(1) = 120.0; cdelt(0) = 100.0; cdelt(1) = 1.0; units(0) = "m"; units(1) = "m"; // LinearCoordinate lc1(names, units, crval, cdelt, xform, crpix); // Vector > crval2(2); Vector > cdelt2(2); crval2(0) = Quantum(crval(0), units(0)); crval2(1) = Quantum(crval(1), units(1)); cdelt2(0) = Quantum(100*cdelt(0), "cm"); cdelt2(1) = Quantum(100*cdelt(1), "cm"); // LinearCoordinate lc2(names, crval2, cdelt2, xform, crpix); // if (!lc1.near(lc2)) { throw(AipsError(String("Quantum interface constructor failed consistency test"))); } } // Test copy constructor { LinearCoordinate lc = makeCoordinate(names, units, crpix, crval, cdelt, xform); LinearCoordinate lc2(lc); if (!lc.near(lc2)) { throw(AipsError("Failed copy constructor test")); } } // Test assignment { LinearCoordinate lc = makeCoordinate(names, units, crpix, crval, cdelt, xform); LinearCoordinate lc2; lc2 = lc; if (!lc.near(lc2)) { throw(AipsError("Failed assignment test")); } } // Test member functions { LinearCoordinate lc = makeCoordinate(names, units, crpix, crval, cdelt, xform); if (lc.type() != Coordinate::LINEAR) { throw(AipsError("Failed type test")); } if (lc.showType() != "Linear") { throw(AipsError("Failed showType test")); } // if (lc.nPixelAxes() != 2) { throw(AipsError("Failed nPixelAxes test")); } // if (lc.nWorldAxes() != 2) { throw(AipsError("Failed nWorldAxes test")); } // if (!allEQ(names, lc.worldAxisNames())) { throw(AipsError("Failed world axis name recovery test")); } // if (!allEQ(crval, lc.referenceValue())) { throw(AipsError("Failed reference value recovery test")); } // if (!allEQ(cdelt, lc.increment())) { throw(AipsError("Failed increment recovery test")); } // if (!allEQ(crpix, lc.referencePixel())) { throw(AipsError("Failed reference pixel recovery test")); } // if (!allEQ(units, lc.worldAxisUnits())) { throw(AipsError("Failed world axis units recovery test")); } // if (!allEQ(xform, lc.linearTransform())) { throw(AipsError("Failed linear transform recovery test")); } // names(0) = "horsies"; if (!lc.setWorldAxisNames(names)) { throw(AipsError(String("Failed to set world axis name because") + lc.errorMessage())); } if (!allEQ(names, lc.worldAxisNames())) { throw(AipsError("Failed axis name set/recovery test")); } // crval(0) = 111.1; if (!lc.setReferenceValue(crval)) { throw(AipsError(String("Failed to set reference value because") + lc.errorMessage())); } if (!allEQ(crval, lc.referenceValue())) { throw(AipsError("Failed reference value set/recovery test")); } // cdelt(0) = -10.3; if (!lc.setIncrement(cdelt)) { throw(AipsError(String("Failed to set increment because") + lc.errorMessage())); } if (!allEQ(cdelt, lc.increment())) { throw(AipsError("Failed increment set/recovery test")); } // crpix(0) = 23.0; if (!lc.setReferencePixel(crpix)) { throw(AipsError(String("Failed to set reference pixel because") + lc.errorMessage())); } if (!allEQ(crpix, lc.referencePixel())) { throw(AipsError("Failed reference pixel set/recovery test")); } // units(0) = "km"; if (!lc.setWorldAxisUnits(units)) { throw(AipsError(String("Failed to set world axis units because ") + lc.errorMessage())); } if (!allEQ(units, lc.worldAxisUnits())) { throw(AipsError("Failed world axis units set/recovery test 1")); } units(0) = "GHz"; if (!lc.overwriteWorldAxisUnits(units)) { throw(AipsError(String("Failed to overwrite world axis units because ") + lc.errorMessage())); } if (!allEQ(units, lc.worldAxisUnits())) { throw(AipsError("Failed world axis units set/recovery test 2")); } // xform.diagonal() = -2.0; if (!lc.setLinearTransform(xform)) { throw(AipsError(String("Failed to set linear transform because") + lc.errorMessage())); } if (!allEQ(xform, lc.linearTransform())) { throw(AipsError("Failed linear transform set/recovery test")); } // Int prec; Coordinate::formatType fType = Coordinate::SCIENTIFIC; lc.getPrecision(prec, fType, True, 6, 4, 2); if (prec != 6) { throw(AipsError("Failed getPrecision test 1")); } fType = Coordinate::FIXED; lc.getPrecision(prec, fType, True, 6, 4, 2); if (prec != 4) { throw(AipsError("Failed getPrecision test 2")); } // String unit; Double val = 20.12345; Quantum valq(val, Unit(units(1))); String str = lc.format(unit, Coordinate::FIXED, val, 1, True, True, 4); String str2 = lc.formatQuantity(unit, Coordinate::FIXED, valq, 1, True, True, 4); if (str != "20.1234" || str2 != "20.1234") { throw(AipsError("Failed format test 1")); } // str = lc.format(unit, Coordinate::SCIENTIFIC, val, 1, True, True, 4); str2 = lc.formatQuantity(unit, Coordinate::SCIENTIFIC, valq, 1, True, True, 4); if (str != "2.0123e+01" || str2 != "2.0123e+01") { throw(AipsError("Failed format test 2")); } // unit = "MHz"; val = 20.0; str = lc.format(unit, Coordinate::FIXED, val, 0, True, True, 4); if (str != "20000.0000") { throw(AipsError("Failed format test 3")); } // { Vector w(2); Vector u(2); w = lc.referenceValue(); u = lc.worldAxisUnits(); w(0) = 10.0; u(0) = "GHz"; if (!lc.setReferenceValue(w)) { throw(AipsError(lc.errorMessage())); } if (!lc.setWorldAxisUnits(u)) { throw(AipsError(lc.errorMessage())); } // unit = "MHz"; val = 1.0; str = lc.format(unit, Coordinate::FIXED, val, 0, False, True, 4); if (str != "11000.0000") { throw(AipsError("Failed format test 4")); } } } // Test conversion { LinearCoordinate lc = makeCoordinate(names, units, crpix, crval, cdelt, xform); Vector pixel(2), world2(2), world; pixel(0) = 12.2; pixel(1) = -20.32; if (!lc.toWorld(world, pixel)) { throw(AipsError(String("toWorld conversion failed because ") + lc.errorMessage())); } // // Compute expected values. This only works because xform is unity // world2(0) = (pixel(0) - crpix(0)) * cdelt(0) + crval(0); world2(1) = (pixel(1) - crpix(1)) * cdelt(1) + crval(1); // if (!allNear(world2, world, 1e-6)) { throw(AipsError("toWorld conversion gave wrong answer")); } // Vector pixel2; if (!lc.toPixel(pixel2, world)) { throw(AipsError(String("toPixel conversion failed because ") + lc.errorMessage())); } if (!allNear(pixel2, pixel, 1e-6)) { throw(AipsError("Coordinate conversion reflection failed")); } } // // Test conversion reflection with harder case // { LinearCoordinate lc = makeCoordinate(names, units, crpix, crval, cdelt, xform); xform(0,0) = 1.0; xform(1,0) = 2.0; xform(0,1) = 1.5; xform(1,1) = 2.5; if (!lc.setLinearTransform(xform)) { throw(AipsError(String("Failed to set linear transform because") + lc.errorMessage())); } // Vector pixel(2), world; pixel(0) = 12.2; pixel(1) = -20.32; if (!lc.toWorld(world, pixel)) { throw(AipsError(String("toWorld conversion failed because ") + lc.errorMessage())); } // Vector pixel2; if (!lc.toPixel(pixel2, world)) { throw(AipsError(String("toPixel conversion failed because ") + lc.errorMessage())); } if (!allNear(pixel2, pixel, 1e-6)) { throw(AipsError("Coordinate conversion reflection failed")); } } // // Test Fourier Coordinate. Hard to do much with it. LinearXform has been tested // pretty hard too. // { LinearCoordinate lc0 = makeCoordinate(names, units, crpix, crval, cdelt, xform, 3); units(0) = "GHz"; units(1) = "Hz"; units(2) = "s"; LinearCoordinate lc(names, units, crval, cdelt, xform, crpix); // Vector axes(names.nelements(), True); Vector shape(names.nelements()); for (uInt i=0; i units2 = pC->worldAxisUnits(); Vector names2 = pC->worldAxisNames(); Vector crval2 = pC->referenceValue(); Vector crpix2 = pC->referencePixel(); if (units2(0)!=String("s") || units2(1)!=String("s") || units2(2)!=String("Hz")) { throw(AipsError("makeFourierCoordinate (1) failed units test")); } if (names2(0)!=String("Time") || names2(1)!=String("Time") || names2(2)!=String("Frequency")) { throw(AipsError("makeFourierCoordinate (1) failed names test")); } if (!allNear(crval2,0.0,1e-13)) { throw(AipsError("makeFourierCoordinate (1) failed crval test")); } for (uInt i=0; inPixelAxes(); i++) { if (!near(Double(Int(shape(i)/2)), crpix2(i))) { throw(AipsError("makeFourierCoordinate (1) failed crpix test")); } } delete pC; } // Not all axes { axes.set(True); axes(1) = False; Coordinate* pC = lc.makeFourierCoordinate (axes, shape); // const Vector& units2 = pC->worldAxisUnits(); const Vector& names2 = pC->worldAxisNames(); const Vector& crval2 = pC->referenceValue(); const Vector& crpix2 = pC->referencePixel(); if (units2(0)!=String("s") || units2(1)!=String("Hz") || units2(2)!=String("Hz")) { throw(AipsError("makeFourierCoordinate (2) failed units test")); } if (names2(0)!=String("Time") || names2(1)!=names(1) || names2(2)!=String("Frequency")) { throw(AipsError("makeFourierCoordinate (2) failed names test")); } for (uInt i=0; inPixelAxes(); i++) { if (i==1) { if (!near(crpix(i), crpix2(i))) { throw(AipsError("makeFourierCoordinate (2) failed crpix test")); } if (!near(crval(i), crval2(i))) { throw(AipsError("makeFourierCoordinate (2) failed crval test")); } } else { if (!near(Double(Int(shape(i)/2)), crpix2(i))) { throw(AipsError("makeFourierCoordinate (2) failed crpix test")); } if (!near(0.0, crval2(i))) { throw(AipsError("makeFourierCoordinate (2) failed crval test")); } } } delete pC; } // Not all axes and non-diagonal PC { xform(0,1) = 1.0; LinearCoordinate lc2(names, units, crval, cdelt, xform, crpix); // axes.set(True); axes(1) = False; Coordinate* pC = lc2.makeFourierCoordinate (axes, shape); if (pC) { delete pC; throw(AipsError("Failed to induce forced error in makeFourierCoordinate (3)")); } } } // // Test record saving // { LinearCoordinate lc = makeCoordinate(names, units, crpix, crval, cdelt, xform); TableRecord rec; if (!lc.save(rec, "linear")) { throw(AipsError("Coordinate saving to Record failed")); } LinearCoordinate* plc = LinearCoordinate::restore(rec, "linear"); if (!plc->near(lc, 1e-6)) { throw(AipsError("Coordinate reflection through record interface failed")); } delete plc; } // // Test clone // { LinearCoordinate lc = makeCoordinate(names, units, crpix, crval, cdelt, xform); Coordinate* plc = lc.clone(); if (!plc->near(lc, 1e-6)) { throw(AipsError("Clone function failed")); } delete plc; } } catch (std::exception& x) { cerr << "aipserror: error " << x.what() << endl; return (1); } cout << "ok" << endl; return (0); } LinearCoordinate makeCoordinate (Vector& names, Vector& units, Vector& crpix, Vector& crval, Vector& cdelt, Matrix& xform, uInt n) { Vector uu(5); uu(0) = "m"; uu(1) = "rad"; uu(2) = "s"; uu(3) = "GHz"; uu(4) = "pc"; // names.resize(n); units.resize(n); crpix.resize(n); cdelt.resize(n); crval.resize(n); xform.resize(n,n); // for (uInt i=0; i4) { units(i) = uu(0); } else { units(i) = uu(i); } } xform = 0.0; xform.diagonal() = 1.0; // LinearCoordinate lc(names, units, crval, cdelt, xform, crpix); return lc; } casacore-3.7.1/coordinates/Coordinates/test/tLinearXform.cc000066400000000000000000000232751476623553700240210ustar00rootroot00000000000000//# tLinearXform.cc: Test program for LinearXform class //# Copyright (C) 1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include #include #include #include #include #include #include #include #include #include int main() { try { // Constructors { LinearXform xf(1); } { Vector crpix(3), cdelt(3); crpix(0) = 10.0; crpix(1) = 20.0; crpix(2) = 30.0; cdelt(0) = 1.0; cdelt(1) = 1.5; cdelt(2) = 2.0; // LinearXform lxf(crpix, cdelt); // Matrix px(3,3); px = 0.0; px.diagonal() = 1.0; LinearXform lxf2(crpix, cdelt, px); } // Test near function { Vector crpix(3), cdelt(3); crpix(0) = 10.0; crpix(1) = 20.0; crpix(2) = 30.0; cdelt(0) = 1.0; cdelt(1) = 1.5; cdelt(2) = 2.0; LinearXform lxf(crpix, cdelt); LinearXform lxf2(crpix, cdelt); if (!lxf.near(lxf2)) { throw(AipsError("Failed near test 1")); } Vector excludeAxes(1,1); if (!lxf.near(lxf2, excludeAxes)) { throw(AipsError("Failed near test 2")); } } // Test copy constructor { Vector crpix(3), cdelt(3); crpix(0) = 10.0; crpix(1) = 20.0; crpix(2) = 30.0; cdelt(0) = 1.0; cdelt(1) = 1.5; cdelt(2) = 2.0; LinearXform lxf(crpix, cdelt); LinearXform lxf2(lxf); if (!lxf.near(lxf2)) { throw(AipsError("Failed copy constructor test")); } } // Test assignment { Vector crpix(3), cdelt(3); crpix(0) = 10.0; crpix(1) = 20.0; crpix(2) = 30.0; cdelt(0) = 1.0; cdelt(1) = 1.5; cdelt(2) = 2.0; LinearXform lxf(crpix, cdelt); LinearXform lxf2; lxf2 = lxf; if (!lxf.near(lxf2)) { throw(AipsError("Failed assignment test")); } } // Test member functions { Vector crpix(3), cdelt(3); crpix(0) = 10.0; crpix(1) = 20.0; crpix(2) = 30.0; cdelt(0) = 1.0; cdelt(1) = 1.5; cdelt(2) = 2.0; Matrix px(3,3); px = 0.0; px.diagonal() = 1.0; LinearXform lxf(crpix, cdelt, px); // if (lxf.nWorldAxes() != 3) { throw(AipsError("Failed worldAxes test")); } // if (!allEQ(crpix, lxf.crpix())) { throw(AipsError("Failed crpix recovery test")); } // if (!allEQ(cdelt, lxf.cdelt())) { throw(AipsError("Failed cdelt recovery test")); } // if (!allEQ(px, lxf.pc())) { throw(AipsError("Failed pc recovery test")); } // crpix = crpix * 2.0; lxf.crpix(crpix); if (!allEQ(crpix, lxf.crpix())) { throw(AipsError("Failed crpix set/recovery test")); } // cdelt = cdelt * 2.0; lxf.cdelt(cdelt); if (!allEQ(cdelt, lxf.cdelt())) { throw(AipsError("Failed cdelt set/recovery test")); } // px.diagonal() = 2.0; lxf.pc(px); if (!allEQ(px, lxf.pc())) { throw(AipsError("Failed pc set/recovery test")); } } // // Test Fourier inverter. Can only do this by knowing the algorithm... // { Vector crpix(2), cdelt(2); crpix(0) = 10.0; crpix(1) = 20.0; cdelt(0) = 1.0; cdelt(1) = 1.5; Matrix pc(2,2); Vector diag(2); diag(0) = 1.0; diag(1) = 2.0; pc = 0.0; pc.diagonal() = diag; LinearXform lxf(crpix, cdelt, pc); // All axes Vector axes(2, True); Vector crpix2(2); Vector scale(2); crpix2(0) = 1.0; crpix2(1) = 2.0; scale(0) = 5.0; scale(1) = 2.0; String errMsg; // { LinearXform* lxf2 = lxf.fourierInvert(errMsg, axes, crpix2, scale); if (!casacore::allNear(crpix2, lxf2->crpix(),1e-13)) { throw(AipsError("fourierInvert (1) crpix test failed")); } Vector tmp(2); tmp(0) = 1.0 / cdelt(0) * scale(0); tmp(1) = 1.0 / cdelt(1) * scale(1); if (!casacore::allNear(tmp, lxf2->cdelt(),1e-13)) { throw(AipsError("fourierInvert (1) cdelt test failed")); } if (!near(1.0/diag(0),lxf2->pc()(0,0)) || !near(1.0/diag(1),lxf2->pc()(1,1)) || !near(0.0,lxf2->pc()(0,1)) || !near(0.0,lxf2->pc()(1,0))) { throw(AipsError("fourierInvert (1) pc test failed")); } delete lxf2; } // Not all axes { axes(1) = False; LinearXform* lxf2 = lxf.fourierInvert(errMsg, axes, crpix2, scale); if (!near(crpix2(0), lxf2->crpix()(0),1e-13) || !near(lxf.crpix()(1), lxf2->crpix()(1),1e-13)) { throw(AipsError("fourierInvert (2) crpix test failed")); } Vector tmp(2); tmp(0) = 1.0 / cdelt(0) * scale(0); if (!near(tmp(0), lxf2->cdelt()(0),1e-13) || !near(cdelt(1), lxf2->cdelt()(1),1e-13)) { throw(AipsError("fourierInvert (2) cdelt test failed")); } if (!near(1.0/diag(0),lxf2->pc()(0,0)) || !near(diag(1),lxf2->pc()(1,1)) || !near(0.0,lxf2->pc()(0,1)) || !near(0.0,lxf2->pc()(1,0))) { throw(AipsError("fourierInvert (2) pc test failed")); } delete lxf2; } // Non-diagonal pc matrix all axes { axes.set(True); pc(1,0) = 2.0; pc(0,1) = 2.0; lxf.pc(pc); LinearXform* lxf2 = lxf.fourierInvert(errMsg, axes, crpix2, scale); if (!casacore::allNear(crpix2, lxf2->crpix(),1e-13)) { throw(AipsError("fourierInvert (3) crpix test failed")); } Vector tmp(2); tmp(0) = 1.0 / cdelt(0) * scale(0); tmp(1) = 1.0 / cdelt(1) * scale(1); if (!casacore::allNear(tmp, lxf2->cdelt(),1e-13)) { throw(AipsError("fourierInvert (3) cdelt test failed")); } if (!casacore::allNear(invert(pc), lxf2->pc(), 1e-13)) { throw(AipsError("fourierInvert (3) pc test failed")); } delete lxf2; } // Non-diagonal pc matrix not axes { axes.set(True); axes(1) = False; pc(1,0) = 2.0; pc(0,1) = 2.0; lxf.pc(pc); LinearXform* lxf2 = lxf.fourierInvert(errMsg, axes, crpix2, scale); if (lxf2) { delete lxf2; throw(AipsError("Failed to induce forced fourierInvert error")); } } } // Test conversion { Vector crpix(3), cdelt(3); crpix(0) = 10.0; crpix(1) = 20.0; crpix(2) = 30.0; cdelt(0) = 1.0; cdelt(1) = 1.5; cdelt(2) = 2.0; Matrix px(3,3); px = 0.0; px.diagonal() = 1.0; LinearXform lxf(crpix, cdelt, px); // Vector world(crpix.copy()); Vector pixel(3); String error; Bool ok = lxf.forward(pixel, world, error); if (!ok) { throw(AipsError(String("Forwards conversion failed because ") + error)); } for (uInt i=0; i world2; ok = lxf.reverse(world2, pixel, error); if (!casacore::allNear(world, world2, 1e-6)) { throw(AipsError("Conversion reflection failed")); } } } catch (std::exception& x) { cerr << "aipserror: error " << x.what() << endl; return (1); } cout << "ok" << endl; return (0); } casacore-3.7.1/coordinates/Coordinates/test/tObsInfo.cc000066400000000000000000000240601476623553700231230ustar00rootroot00000000000000//# tObsInfo.cc: test program for class ObsInfo //# Copyright (C) 1998,2000,2001,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include int main() { // Default constructor ObsInfo oi; // Default values AlwaysAssertExit(oi.telescope() == oi.defaultTelescope()); AlwaysAssertExit(oi.defaultTelescope() == "UNKNOWN"); // AlwaysAssertExit(oi.observer() == oi.defaultObserver()); AlwaysAssertExit(oi.defaultObserver() == "UNKNOWN"); // MEpoch dflt = oi.defaultObsDate(); AlwaysAssertExit(near(oi.obsDate().get("s").getValue(), 0.0)); // AlwaysAssertExit(oi.isPointingCenterInitial()); MVDirection dmvd = oi.defaultPointingCenter(); Vector v = dmvd.get(); AlwaysAssertExit(near(v(0), 0.0)); AlwaysAssertExit(near(v(1), 0.0)); MPosition telPos( MVPosition( Quantity( 10, "m"), Quantity( -6, "deg"), Quantity( 50, "deg")), MPosition::Ref(MPosition::WGS84)); // Set items oi.setTelescope("telescope").setObserver("observer"). setObsDate(MVEpoch(1234.0)).setPointingCenter(MVDirection(0.01, 0.02)); AlwaysAssertExit(oi.telescope() == "telescope" && oi.observer() == "observer" && near(oi.obsDate().get("d").getValue(), 1234.0) && near(oi.pointingCenter().get()(0),0.01) && near(oi.pointingCenter().get()(1),0.02)); AlwaysAssertExit(!oi.isTelescopePositionSet()); AlwaysAssertExit(!oi.isPointingCenterInitial()); oi.setTelescopePosition(telPos); AlwaysAssertExit(oi.isTelescopePositionSet()); MPosition mpos1 = MPosition::Convert (oi.telescopePosition(), MPosition::WGS84)(); MVPosition pos1 = mpos1.getValue(); AlwaysAssertExit (near (pos1.get()[0], 10., 1e-5)); AlwaysAssertExit (near (pos1.getLong("deg").getValue(), -6., 1e-5)); AlwaysAssertExit (near (pos1.getLat("deg").getValue(), 50., 1e-5)); // Copy constructor and assignment ObsInfo oi2(oi); AlwaysAssertExit(oi2.telescope() == "telescope" && oi2.observer() == "observer" && near(oi2.obsDate().get("d").getValue(), 1234.0) && near(oi2.pointingCenter().get()(0),0.01) && near(oi2.pointingCenter().get()(1),0.02)); AlwaysAssertExit(!oi2.isPointingCenterInitial()); AlwaysAssertExit(oi2.isTelescopePositionSet()); // ObsInfo oi2a; Double dateVal = 55000.5; oi2a.setTelescope("telescope2").setObserver("observer2"). setObsDate(MVEpoch(dateVal)).setPointingCenter(MVDirection(0.03,0.04)); oi = oi2a; AlwaysAssertExit(oi.telescope() == "telescope2" && oi.observer() == "observer2" && near(oi.obsDate().get("d").getValue(), dateVal) && near(oi.pointingCenter().get()(0),0.03) && near(oi.pointingCenter().get()(1),0.04)); AlwaysAssertExit(!oi.isPointingCenterInitial()); AlwaysAssertExit(!oi.isTelescopePositionSet()); oi.setTelescopePosition(telPos); // Test output. cout << oi << endl; // Record interface Record rec; String error; AlwaysAssertExit(oi.toRecord(error, rec)); ObsInfo oi3; AlwaysAssertExit(oi3.fromRecord(error, rec)); AlwaysAssertExit(oi3.telescope() == "telescope2" && oi3.observer() == "observer2" && near(oi3.obsDate().get("d").getValue(), dateVal) && near(oi3.pointingCenter().get()(0),0.03) && near(oi3.pointingCenter().get()(1),0.04)); AlwaysAssertExit(oi3.isTelescopePositionSet()); MPosition mpos3 = MPosition::Convert (oi.telescopePosition(), MPosition::WGS84)(); MVPosition pos3 = mpos3.getValue(); AlwaysAssertExit (near (pos3.get()[0], 10., 1e-5)); AlwaysAssertExit (near (pos3.getLong("deg").getValue(), -6., 1e-5)); AlwaysAssertExit (near (pos3.getLat("deg").getValue(), 50., 1e-5)); Record reca; AlwaysAssertExit(oi2a.toRecord(error, reca)); AlwaysAssertExit(oi2.isTelescopePositionSet()); AlwaysAssertExit(oi2.fromRecord(error, reca)); AlwaysAssertExit(!oi2.isTelescopePositionSet()); // Forced errors { Record rec3; Double x = 0; rec3.define("telescope", x); AlwaysAssertExit(!oi3.fromRecord(error, rec3)); } { Record rec3; Double x = 0; rec3.define("observer", x); AlwaysAssertExit(!oi3.fromRecord(error, rec3)); } { Record rec3; Double x = 0; rec3.define("obsdate", x); AlwaysAssertExit(!oi3.fromRecord(error, rec3)); } { Record rec3; Record rec4; Double x = 0; rec4.define("doggies", x); rec3.defineRecord("obsdate", rec4); AlwaysAssertExit(!oi3.fromRecord(error, rec3)); } { Record rec3; Double x = 0; rec3.define("pointingcenter", x); AlwaysAssertExit(!oi3.fromRecord(error, rec3)); } { Record rec3; Record rec4; rec3.defineRecord("pointingcenter", rec4); AlwaysAssertExit(!oi3.fromRecord(error, rec3)); } { Record rec3; Record rec4; Double x(0); rec4.define("value", x); rec3.defineRecord("pointingcenter", rec4); AlwaysAssertExit(!oi3.fromRecord(error, rec3)); } { Record rec3; Record rec4; Vector x(2); rec4.define("value", x); Double y = 0.0; rec4.define("initial", y); rec3.defineRecord("pointingcenter", rec4); AlwaysAssertExit(!oi3.fromRecord(error, rec3)); } // FITS Vector error2; Record rec2; cerr << oi << endl; AlwaysAssertExit(oi.toFITS(error, rec2)); AlwaysAssertExit(oi.toFITS(error, rec2)); // Second pass removes pre-existing fields // the record delivered by toFITS contains fields as fields, // the record accepted by fromFITS contains fields as subrecords // -- a round trip is therefore not possible directly. Record rec3; for (uInt i=0; i vs = ObsInfo::keywordNamesFITS(); AlwaysAssertExit (vs.nelements() == 9); AlwaysAssertExit(vs(0) == "telescop" && vs(1) == "observer" && vs(2) == "date-obs" && vs(3) == "timesys" && vs(4) == "obsra" && vs(5) == "obsdec" && vs(6) == "obsgeo-x" && vs(7) == "obsgeo-y" && vs(8) == "obsgeo-z"); cout << "OK" << endl; return 0; } casacore-3.7.1/coordinates/Coordinates/test/tObsInfo.out000066400000000000000000000003011476623553700233350ustar00rootroot00000000000000Telescope: telescope2 Position: [4.08537e+06m, -429389m, 4.8628e+06m] (ITRF) Observer: observer2 Date Observed: Epoch: 55000::12:00:00.0000 Pointing Center: [0.998751, 0.0299715, 0.0399893] OK casacore-3.7.1/coordinates/Coordinates/test/tProjection.cc000066400000000000000000000113161476623553700237000ustar00rootroot00000000000000//# tProjection.cc: Test program for Projection class //# Copyright (C) 1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include int main() { try { // Test parameter setting and recovery for (Int i=0; i pars(nP); for (uInt j=0; j pars(2); pars(0) = 0.1; pars(1) = 0.2; { Projection proj(Projection::SIN, pars); Projection proj2(Projection::SIN, pars); if (!proj.near(proj2,1e-6)) { throw(AipsError("Near function fails")); } } // Test assignment (and zero par constructor) { Projection proj(Projection::SIN, pars); Projection proj2(Projection::TAN); proj2 = proj; // if (proj2.name() != proj.name() || !allEQ(proj2.parameters(),proj.parameters()) || proj2.type() != proj.type() || !proj2.near(proj, 1e-6)) { throw(AipsError("Assignment fails")); } // pars.resize(0); Projection proj3(Projection::SIN, pars); } // // Test copy constructor // { Projection proj(Projection::SIN, pars); Projection proj2(proj); if (proj2.name() != proj.name() || !allEQ(proj2.parameters(),proj.parameters()) || proj2.type() != proj2.type() || !proj2.near(proj, 1e-6)) { throw(AipsError("Copy constructor fails")); } } } catch (std::exception& x) { cerr << "aipserror: error " << x.what() << endl; return (1); } cout << "ok" << endl; return (0); } casacore-3.7.1/coordinates/Coordinates/test/tQualityCoordinate.cc000066400000000000000000000373311476623553700252310ustar00rootroot00000000000000//# tQualityCoordinate.cc: Test program for QualityCoordinate //# Copyright (C) 1998,1999,2000,2001,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include #include #include #include #include #include #include #include #include #include #include QualityCoordinate makeCoordinate(Vector& whichQuality, Vector& qualityStrings); void doit (QualityCoordinate& lc, const Vector& whichQuality, Bool verbose=True); void doit2 (QualityCoordinate& lc, const Vector& whichQuality, Bool verbose=True); void doit3 (QualityCoordinate& lc, const Vector& whichQuality, const Vector& qualityStrings, Bool verbose); void doit4(QualityCoordinate& lc, Bool verbose); void doit5(Bool verbose); void doit6(QualityCoordinate& lc, Bool verbose); int main() { try { Vector whichQuality; Vector qualityStrings; Bool verbose=False; // Constructors { QualityCoordinate lc = makeCoordinate(whichQuality, qualityStrings); } // Test near function { QualityCoordinate lc = makeCoordinate(whichQuality, qualityStrings); QualityCoordinate lc2 = makeCoordinate(whichQuality, qualityStrings); if (!lc.near(lc2)) { throw(AipsError("Failed near test 1")); } Vector excludeAxes(1, 0); if (!lc.near(lc2, excludeAxes)) { throw(AipsError("Failed near test 2")); } } // Test the rest { QualityCoordinate lc = makeCoordinate(whichQuality, qualityStrings); doit(lc, whichQuality, verbose); doit2(lc, whichQuality, verbose); } { QualityCoordinate lc = makeCoordinate(whichQuality, qualityStrings); doit3(lc, whichQuality, qualityStrings, verbose); } { QualityCoordinate lc = makeCoordinate(whichQuality, qualityStrings); doit4(lc, verbose); } { doit5(verbose); } { QualityCoordinate lc = makeCoordinate(whichQuality, qualityStrings); doit6(lc, verbose); } } catch (std::exception& x) { cerr << "aipserror: error " << x.what() << endl; return (1); } cout << "ok" << endl; return (0); } QualityCoordinate makeCoordinate(Vector& whichQuality, Vector& qualityStrings) { // choose all quality types whichQuality.resize(2); whichQuality(0) = Quality::DATA; whichQuality(1) = Quality::ERROR; qualityStrings.resize(2); qualityStrings(0) = "DATA"; qualityStrings(1) = "ERROR"; // generate the coosys return QualityCoordinate(whichQuality); } void doit (QualityCoordinate& lc, const Vector& whichQuality, Bool verbose) { // Test copy constructor { QualityCoordinate lc2(lc); if (!lc.near(lc2)) { throw(AipsError("Failed copy constructor test")); } if (verbose) cout << "Passed copy constructor test!" << endl; } // Test assignment { Vector whichQuality2(1); whichQuality2(0) = Quality::DATA; QualityCoordinate lc2 = QualityCoordinate(whichQuality2); lc2 = lc; if (!lc.near(lc2)) { throw(AipsError("Failed assignment test")); } if (verbose) cout << "Passed assignment test!" << endl; } // Test member functions if (lc.type() != Coordinate::QUALITY) { throw(AipsError("Failed type test")); } if (verbose) cout << "Passed type test!" << endl; // if (lc.showType() != "Quality") { throw(AipsError("Failed showType test")); } if (verbose) cout << "Passed showType test!" << endl; // if (lc.nPixelAxes() != 1) { throw(AipsError("Failed nPixelAxes test")); } if (verbose) cout << "Passed nPixelAxes test!" << endl; // if (lc.nWorldAxes() != 1) { throw(AipsError("Failed nWorldAxes test")); } if (verbose) cout << "Passed nWorldAxes test!" << endl; // Vector axisNames(1); axisNames(0) = "Quality"; if (!allEQ(axisNames, lc.worldAxisNames())) { throw(AipsError("Failed world axis name recovery test")); } if (verbose) cout << "Passed world axis name recovery test!" << endl; // axisNames(0) = "Horsies"; if (!lc.setWorldAxisNames(axisNames)) { throw(AipsError(String("Failed to set world axis name because") + lc.errorMessage())); } if (verbose) cout << "Passed to set world axis name!" << endl; // if (!allEQ(axisNames, lc.worldAxisNames())) { throw(AipsError("Failed axis name set/recovery test")); } if (verbose) cout << "Passed axis name set/recovery test!" << endl; // // There is no unit we can set // Vector axisUnits(1); axisUnits(0) = ""; if (!allEQ(axisUnits, lc.worldAxisUnits())) { throw(AipsError("Failed world axis units recovery test")); } if (verbose) cout << "Passed world axis units test!" << endl; // if (!lc.setWorldAxisUnits(axisUnits)) { throw(AipsError(String("Failed to set world axis units because ") + lc.errorMessage())); } if (verbose) cout << "Passed to set world axis units!" << endl; // if (!allEQ(axisUnits, lc.worldAxisUnits())) { throw(AipsError("Failed world axis units set/recovery test")); } if (verbose) cout << "Passed world axis units set/recovery test!" << endl; // axisUnits(0) = "Mulies"; if (!lc.setWorldAxisUnits(axisUnits)) { throw(AipsError(String("Failed to set world axis units because ") + lc.errorMessage())); } if (verbose) cout << "Passed to set world axis units!" << endl; // if (allEQ(axisUnits, lc.worldAxisUnits())) { throw(AipsError(String("World axis units set/recovery succeeded unexpectedly!"))); } if (verbose) cout << "Failed as expected as world axis units can not be set/recovered!" << endl; // if (!allEQ(whichQuality, lc.quality())) { throw(AipsError("Failed Quality recovery test")); } if (verbose) cout << "Passed quality recovery test!" << endl; // // // Test record saving // TableRecord rec; if (!lc.save(rec, "Quality")) { throw(AipsError("Coordinate saving to Record failed")); } if (verbose) cout << "Passed coordinate saving to Record !" << endl; // QualityCoordinate* plc = QualityCoordinate::restore(rec, "Quality"); if (!plc->near(lc, 1e-6)) { throw(AipsError("Coordinate reflection through record interface failed")); } if (verbose) cout << "Passed coordinate reflection through record interface!" << endl; delete plc; // Test clone // Coordinate* plc2 = lc.clone(); if (!plc2->near(lc, 1e-6)) { throw(AipsError("Clone function failed")); } if (verbose) cout << "Passed clone function!" << endl; delete plc2; } void doit2 (QualityCoordinate& lc, const Vector& whichQuality, Bool verbose) { Vector crval(1); crval(0) = Double(whichQuality(0)); if (!allEQ(crval, lc.referenceValue())) { throw(AipsError("Failed reference value recovery test")); } if (verbose) cout << "Passed reference value recovery test!" << endl; // Vector cdelt(1); cdelt(0) = 1.0; if (!allEQ(cdelt, lc.increment())) { throw(AipsError("Failed increment recovery test")); } if (verbose) cout << "Passed increment recovery test!" << endl; // Vector crpix(1); crpix(0) = 0.0; if (!allEQ(crpix, lc.referencePixel())) { throw(AipsError("Failed reference pixel recovery test")); } if (verbose) cout << "Passed reference pixel recovery test!" << endl; // Matrix xform(1,1); xform(0,0) = 1.0; if (!allEQ(xform, lc.linearTransform())) { throw(AipsError("Failed Quality transform recovery test")); } if (verbose) cout << "Passed quality transform recovery test!" << endl; // Vector oldRefVal = lc.referenceValue(); Vector oldIncr = lc.increment(); Vector oldRefPix = lc.referencePixel(); Matrix oldLinTr = lc.linearTransform(); crval(0) = 111.1; if (!lc.setReferenceValue(crval)) { throw(AipsError(String("Failed to set reference value because") + lc.errorMessage())); } if (verbose) cout << "Passed to set reference value!" << endl; if (!allEQ(oldRefVal, lc.referenceValue())) { throw(AipsError("Failed reference value set/recovery test")); } if (verbose) cout << "Passed reference value set/recovery test!" << endl; // cdelt(0) = -10.3; if (!lc.setIncrement(cdelt)) { throw(AipsError(String("Failed to set increment because") + lc.errorMessage())); } if (verbose) cout << "Passed to set increment!" << endl; // if (!allEQ(oldIncr, lc.increment())) { throw(AipsError("Failed increment set/recovery test")); } if (verbose) cout << "Passed increment set/recovery test!" << endl; // crpix(0) = 23.0; if (!lc.setReferencePixel(crpix)) { throw(AipsError(String("Failed to set reference pixel because") + lc.errorMessage())); } if (verbose) cout << "Passed to set reference pixel!" << endl; // if (!allEQ(oldRefPix, lc.referencePixel())) { throw(AipsError("Failed reference pixel set/recovery test")); } if (verbose) cout << "Passed reference pixel set/recovery test!" << endl; // xform.diagonal() = -2.0; if (!lc.setLinearTransform(xform)) { throw(AipsError(String("Failed to set linear transform because") + lc.errorMessage())); } if (verbose) cout << "Passed to set linear transform!" << endl; if (!allEQ(oldLinTr, lc.linearTransform())) { throw(AipsError("Failed linear transform set/recovery test")); } if (verbose) cout << "Passed linear transform set/recovery test!" << endl; } void doit3 (QualityCoordinate& lc, const Vector& whichQuality, const Vector& qualityStrings, Bool verbose) { // // Test conversion // Vector pixel(1), world; pixel(0) = lc.referencePixel()(0); if (!lc.toWorld(world, pixel)) { throw(AipsError(String("toWorld conversion failed because ") + lc.errorMessage())); } if (verbose) cout << "Passed toWorld conversion!" << endl; // Vector pixel2(1); if (!lc.toPixel(pixel2, world)) { throw(AipsError(String("toPixel conversion failed because ") + lc.errorMessage())); } if (verbose) cout << "Passed toPixel conversion!" << endl; if (!allNear(pixel2, pixel, 1e-6)) { throw(AipsError("Coordinate conversion reflection 1 failed")); } if (verbose) cout << "Passed toPixel conversion!" << endl; // world(0) = -10000.0; if (lc.toPixel(pixel2, world)) { throw(AipsError(String("toPixel succeeded unexpectedly"))); } else { if (verbose) cout << "Failed as expected with" << lc.errorMessage() << endl; } // Int pixel3; for (Int i=0; i axes(lc.nWorldAxes(), True); Vector shape(lc.nPixelAxes(), 10); Bool failed = False; Coordinate* pC = 0; try { pC = lc.makeFourierCoordinate (axes, shape); } catch (std::exception& x) { failed = True; } if (!failed) { throw(AipsError("Failed to induce forced error (1) in makeFourierCoordinate")); } else{ if (verbose) cout << "Succeeded to induce forced error (1) in makeFourierCoordinate!" << endl; } delete pC; } void doit5(Bool verbose) { // Test setQuality { Vector quality(1); quality(0) = Quality::DATA; Vector qualityStrings(1); qualityStrings(0) = String("DATA"); QualityCoordinate lc(quality); // quality.resize(2); qualityStrings.resize(2); quality(0) = Quality::DATA; quality(1) = Quality::ERROR; qualityStrings(0) = String("DATA"); qualityStrings(1) = String("ERROR"); lc.setQuality(quality); // Vector quality2 = lc.quality(); AlwaysAssert(quality2.nelements()==2, AipsError); AlwaysAssert(Quality::type(quality2(0))==Quality::DATA, AipsError); AlwaysAssert(Quality::type(quality2(1))==Quality::ERROR, AipsError); // doit(lc, quality, verbose); doit3(lc, quality, qualityStrings, verbose); } } void doit6(QualityCoordinate& lc, Bool verbose) { { Vector absPix(1); absPix(0) = 0.0; lc.makePixelRelative(absPix); if (!allNear(absPix, 0.0, 1.0e-05)) throw(AipsError("Failed to convert value to relative!")); else if (verbose) cout << "Succeeded to convert value to relative!" << endl; } { Vector relPix(1); relPix(0) = 0.0; lc.makePixelAbsolute(relPix); if (!allNear(relPix, 0.0, 1.0e-05)) throw(AipsError("Failed to convert value to absolute!")); else if (verbose) cout << "Succeeded to convert value to absolute!" << endl; } { Coordinate *lc2 = lc.clone(); Vector b1(1), b2(1); if (!lc.doNearPixel(*lc2, b1, b2)) throw(AipsError("Failed to find doNear values!")); else if (verbose) cout << "Succeeded to find doNear values!" << endl; delete lc2; Vector newQuality(1); newQuality.resize(1); newQuality(0) = Quality::ERROR; Coordinate *lc3 = new QualityCoordinate(newQuality); if (lc.doNearPixel(*lc3, b1, b2)) throw(AipsError("Unexpectedly succeeded to find doNear values!")); else if (verbose) cout << "Failed as expected to find doNear values!" << endl; delete lc3; } } casacore-3.7.1/coordinates/Coordinates/test/tSpectralCoordinate.cc000066400000000000000000001363351476623553700253620ustar00rootroot00000000000000//# tSpectralCoordinate.cc: Test program for SpectralCoordinate //# Copyright (C) 1998,1999,2000,2001,2002,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include SpectralCoordinate makeLinearCoordinate(MFrequency::Types type, Double& crval, Double& cdelt, Double& crpix, Double& restFreq); SpectralCoordinate makeNonLinearCoordinate (MFrequency::Types type, Vector& freqs, Double& restFreq); Double velInc (Double dF, Double f0, MDoppler::Types velType); void refConv(); int main() { try { Double f0, finc, refchan, restFreq; Vector freqs; Matrix xform(1,1); xform(0,0) = 1.0; // Vector names(1); names(0) = "Frequency"; Vector units(1); units(0) = "Hz"; Vector crpix(1), crval(1), cdelt(1); // Constructors { SpectralCoordinate lc = makeLinearCoordinate(MFrequency::TOPO, f0, finc, refchan, restFreq); } { SpectralCoordinate lc = makeNonLinearCoordinate(MFrequency::TOPO, freqs, restFreq); } { SpectralCoordinate lc = makeNonLinearCoordinate(MFrequency::TOPO, freqs, restFreq); Vector velocities; lc.setVelocity (String("km/s"), MDoppler::OPTICAL); lc.frequencyToVelocity(velocities, freqs); SpectralCoordinate lc2(MFrequency::TOPO, MDoppler::OPTICAL, velocities, String("km/s"), restFreq); Double freq; for (uInt i=0; i wavelengths; lc.setWavelengthUnit (String("m")); lc.frequencyToWavelength(wavelengths, freqs); SpectralCoordinate lc2(MFrequency::TOPO, wavelengths, String("m")); Double freq; for (uInt i=0; i excludeAxes(1, 1); if (!lc.near(lc2, excludeAxes)) { throw(AipsError(String("Failed near test 2 because") + lc.errorMessage())); } } // Test Quantum constructor interfaces { Double crval = 1.4e9; Double crpix = 1.0; Double cdelt = 1.0e3; Double restFreq = 1.41e9; SpectralCoordinate sc1(MFrequency::TOPO, crval, cdelt, crpix, restFreq); // Quantum crval2(100*crval, "cHz"); Quantum cdelt2(100*cdelt, "cHz"); Quantum restFreq2(100*restFreq, "cHz"); SpectralCoordinate sc2(MFrequency::TOPO, crval2, cdelt2, crpix, restFreq2); // if (!sc1.near(sc2)) { throw(AipsError(String("Quantum interface (1) constructor failed consistency test"))); } } { Vector freqs(3); freqs(0) = 1.4e9; freqs(1) = 1.5e9; freqs(2) = 1.7e9; Double restFreq = 1.41e9; SpectralCoordinate sc1(MFrequency::TOPO, freqs, restFreq); // Quantum > freqs2(100.0*freqs, "cHz"); Quantum restFreq2(100.0*restFreq, "cHz"); SpectralCoordinate sc2(MFrequency::TOPO, freqs2, restFreq2); // if (!sc1.near(sc2)) { throw(AipsError(String("Quantum interface (2) constructor failed consistencey test"))); } } // Test copy constructor { SpectralCoordinate lc = makeLinearCoordinate(MFrequency::TOPO, f0, finc, refchan, restFreq); SpectralCoordinate lc2(lc); if (!lc.near(lc2)) { throw(AipsError("Failed copy constructor test")); } } // Test assignment { SpectralCoordinate lc = makeLinearCoordinate(MFrequency::TOPO, f0, finc, refchan, restFreq); SpectralCoordinate lc2; lc2 = lc; if (!lc.near(lc2)) { throw(AipsError("Failed assignment test")); } } // Test member functions { SpectralCoordinate lc = makeLinearCoordinate(MFrequency::TOPO, f0, finc, refchan, restFreq); crpix(0) = refchan; crval(0) = f0; cdelt(0) = finc; // if (lc.type() != Coordinate::SPECTRAL) { throw(AipsError("Failed type test")); } if (lc.showType() != "Spectral") { throw(AipsError("Failed showType test")); } // if (lc.nPixelAxes() != 1) { throw(AipsError("Failed nPixelAxes test")); } // if (lc.nWorldAxes() != 1) { throw(AipsError("Failed nWorldAxes test")); } // if (!allEQ(names, lc.worldAxisNames())) { throw(AipsError("Failed world axis name recovery test")); } // if (!allEQ(crval, lc.referenceValue())) { throw(AipsError("Failed reference value recovery test")); } // if (!allEQ(cdelt, lc.increment())) { throw(AipsError("Failed increment recovery test")); } // if (!allEQ(crpix, lc.referencePixel())) { throw(AipsError("Failed reference pixel recovery test")); } // if (!allEQ(units, lc.worldAxisUnits())) { throw(AipsError("Failed world axis units recovery test")); } // if (!allEQ(xform, lc.linearTransform())) { throw(AipsError("Failed linear transform recovery test")); } // if (!near(restFreq, lc.restFrequency())) { throw(AipsError("Failed rest frequency recovery test")); } // if (lc.frequencySystem() != MFrequency::TOPO) { throw(AipsError("Failed frequency system recovery test")); } // names(0) = "horsies"; if (!lc.setWorldAxisNames(names)) { throw(AipsError(String("Failed to set world axis name because") + lc.errorMessage())); } if (!allEQ(names, lc.worldAxisNames())) { throw(AipsError("Failed axis name set/recovery test")); } // crval(0) = 111.1; if (!lc.setReferenceValue(crval)) { throw(AipsError(String("Failed to set reference value because") + lc.errorMessage())); } if (!allEQ(crval, lc.referenceValue())) { throw(AipsError("Failed reference value set/recovery test")); } // cdelt(0) = -10.3; if (!lc.setIncrement(cdelt)) { throw(AipsError(String("Failed to set increment because") + lc.errorMessage())); } if (!allEQ(cdelt, lc.increment())) { throw(AipsError("Failed increment set/recovery test")); } // crpix(0) = 23.0; if (!lc.setReferencePixel(crpix)) { throw(AipsError(String("Failed to set reference pixel because") + lc.errorMessage())); } if (!allEQ(crpix, lc.referencePixel())) { throw(AipsError("Failed reference pixel set/recovery test")); } // units(0) = "GHz"; if (!lc.setWorldAxisUnits(units)) { throw(AipsError(String("Failed to set world axis units because ") + lc.errorMessage())); } if (!allEQ(units, lc.worldAxisUnits())) { throw(AipsError("Failed world axis units set/recovery test")); } // xform.diagonal() = -2.0; if (!lc.setLinearTransform(xform)) { throw(AipsError(String("Failed to set linear transform because") + lc.errorMessage())); } if (!allEQ(xform, lc.linearTransform())) { throw(AipsError("Failed linear transform set/recovery test")); } // restFreq = 1.3; if (!lc.setRestFrequency(restFreq, False)) { throw(AipsError(String("Failed to set rest frequency because") + lc.errorMessage())); } if (!near(restFreq, lc.restFrequency())) { throw(AipsError("Failed rest frequency set/recovery test")); } // Vector rf(2); rf(0) = lc.restFrequency(); rf(1) = restFreq; if (!lc.setRestFrequency(restFreq, True)) { throw(AipsError(String("Failed to set rest frequency because") + lc.errorMessage())); } if (!near(restFreq, lc.restFrequency())) { throw(AipsError("Failed rest frequency set/recovery test")); } // restFreq = 1.4; rf.resize(3,True); rf(2) = restFreq; if (!lc.setRestFrequency(restFreq, True)) { throw(AipsError(String("Failed to set rest frequency because") + lc.errorMessage())); } if (!near(restFreq, lc.restFrequency())) { throw(AipsError("Failed rest frequency set/recovery test")); } const Vector& restFreqs = lc.restFrequencies(); if (restFreqs.nelements() != rf.nelements()) { throw(AipsError("Failed restFrequencies recovery test 1")); } for (uInt i=0; i& restFreqs2 = lc.restFrequencies(); if (restFreqs2.nelements() != rf.nelements()) { throw(AipsError("Failed setRestFrequencies test 1")); } for (uInt i=0; i pixelValues = lc.pixelValues(); Vector worldValues = lc.worldValues(); if (pixelValues.nelements()!=0 || worldValues.nelements()!=0) { throw(AipsError("Failed linear pixel/worldValues function test")); } } // { SpectralCoordinate lc2 = makeNonLinearCoordinate(MFrequency::TOPO, freqs, restFreq); Vector pixelValues = lc2.pixelValues(); Vector worldValues = lc2.worldValues(); if (!casacore::allNear(worldValues, freqs, 1e-6)) { throw(AipsError("Failed non-linear worldValues function test")); } Vector pixels2(freqs.nelements()); for (uInt i=0; i axes(1, True); Vector shape(1); shape(0) = 128; // All axes { Coordinate* pC = lc.makeFourierCoordinate (axes, shape); Vector units2 = pC->worldAxisUnits(); Vector names2 = pC->worldAxisNames(); Vector crval2 = pC->referenceValue(); Vector crpix2 = pC->referencePixel(); if (units2(0)!=String("s")) { throw(AipsError("makeFourierCoordinate (1) failed units test")); } if (names2(0)!=String("Time")) { throw(AipsError("makeFourierCoordinate (1) failed names test")); } if (!casacore::allNear(crval2,0.0,1e-13)) { throw(AipsError("makeFourierCoordinate (1) failed crval test")); } for (uInt i=0; inPixelAxes(); i++) { if (!near(Double(Int(shape(i)/2)), crpix2(i))) { throw(AipsError("makeFourierCoordinate (1) failed crpix test")); } } delete pC; } // No axes { axes.set(False); Coordinate* pC = lc.makeFourierCoordinate (axes, shape); if (pC) { delete pC; throw(AipsError("Failed to induce forced error (1) in makeFourierCoordinate")); } } // Non linear { axes.set(True); Coordinate* pC = lc2.makeFourierCoordinate (axes, shape); if (pC) { delete pC; throw(AipsError("Failed to induce forced error (2) in makeFourierCoordinate")); } } } // Test conversion { SpectralCoordinate lc = makeLinearCoordinate(MFrequency::TOPO, f0, finc, refchan, restFreq); Vector pixel(1), world; pixel(0) = 12.2; if (!lc.toWorld(world, pixel)) { throw(AipsError(String("toWorld conversion failed because ") + lc.errorMessage())); } // Vector pixel2; if (!lc.toPixel(pixel2, world)) { throw(AipsError(String("toPixel conversion failed because ") + lc.errorMessage())); } if (!casacore::allNear(pixel2, pixel, 1e-6)) { throw(AipsError("Coordinate conversion reflection failed")); } Double pix, wrld; pix = 12.2; if (!lc.toWorld(wrld, pix)) { throw(AipsError(String("toWorld conversion failed because ") + lc.errorMessage())); } // Double pix2; if (!lc.toPixel(pix2, wrld)) { throw(AipsError(String("toPixel conversion failed because ") + lc.errorMessage())); } if (!casacore::allNear(pix2, pix, 1e-6)) { throw(AipsError("Coordinate conversion reflection failed")); } } { SpectralCoordinate lc = makeLinearCoordinate(MFrequency::TOPO, f0, finc, refchan, restFreq); Double pixel; MFrequency world; pixel = 12.2; if (!lc.toWorld(world, pixel)) { throw(AipsError(String("toWorld conversion failed because ") + lc.errorMessage())); } // Double pixel2; if (!lc.toPixel(pixel2, world)) { throw(AipsError(String("toPixel conversion failed because ") + lc.errorMessage())); } if (!casacore::allNear(pixel2, pixel, 1e-6)) { throw(AipsError("Coordinate conversion reflection failed")); } } { SpectralCoordinate lc = makeNonLinearCoordinate(MFrequency::TOPO, freqs, restFreq); // Vector pixel(1), world; pixel(0) = 12.2; if (!lc.toWorld(world, pixel)) { throw(AipsError(String("toWorld conversion failed because ") + lc.errorMessage())); } // Vector pixel2; if (!lc.toPixel(pixel2, world)) { throw(AipsError(String("toPixel conversion failed because ") + lc.errorMessage())); } if (!casacore::allNear(pixel2, pixel, 1e-6)) { throw(AipsError("Coordinate conversion reflection failed")); } Double pix, wrld; pix = 12.2; if (!lc.toWorld(wrld, pix)) { throw(AipsError(String("toWorld conversion failed because ") + lc.errorMessage())); } // Double pix2; if (!lc.toPixel(pix2, wrld)) { throw(AipsError(String("toPixel conversion failed because ") + lc.errorMessage())); } if (!casacore::allNear(pix2, pix, 1e-6)) { throw(AipsError("Coordinate conversion reflection failed")); } } { SpectralCoordinate lc = makeNonLinearCoordinate(MFrequency::TOPO, freqs, restFreq); // Double pixel; MFrequency world; pixel = 12.2; if (!lc.toWorld(world, pixel)) { throw(AipsError(String("toWorld conversion failed because ") + lc.errorMessage())); } // Adjust coordinate units to make test harder Vector units = lc.worldAxisUnits(); units.set("KHz"); lc.setWorldAxisUnits(units); // Double pixel2; if (!lc.toPixel(pixel2, world)) { throw(AipsError(String("toPixel conversion failed because ") + lc.errorMessage())); } if (!casacore::allNear(pixel2, pixel, 1e-6)) { throw(AipsError("Coordinate conversion reflection failed")); } } { SpectralCoordinate lc = makeNonLinearCoordinate(MFrequency::TOPO, freqs, restFreq); // Double pixel; MVFrequency world; pixel = 12.2; if (!lc.toWorld(world, pixel)) { throw(AipsError(String("toWorld conversion failed because ") + lc.errorMessage())); } // Adjust coordinate units to make test harder Vector units = lc.worldAxisUnits(); units.set("KHz"); lc.setWorldAxisUnits(units); // Double pixel2; if (!lc.toPixel(pixel2, world)) { throw(AipsError(String("toPixel conversion failed because ") + lc.errorMessage())); } if (!casacore::allNear(pixel2, pixel, 1e-6)) { throw(AipsError("Coordinate conversion reflection failed")); } } // // Test velocity conversion; only RADIO velocities tested // { String velUnit("km/s"); MDoppler::Types velType = MDoppler::RADIO; // refchan = 0.0; finc = 4e6; restFreq = 1.420405752E9; f0 = restFreq; SpectralCoordinate lc(MFrequency::TOPO, f0, finc, refchan, restFreq); Double dVel = velInc(finc, f0, velType); // Pixel <-> Velocity Double vel; Double pix = 0.0; Double pix2; lc.setVelocity (velUnit, velType); if (!lc.pixelToVelocity(vel, pix)) { throw(AipsError(String("pixelToVelocity 1 conversion failed because ") + lc.errorMessage())); } if (!near(vel, 0.0)) { throw(AipsError(String("pixelToVelocity 1 gave wrong answer"))); } if (!lc.velocityToPixel(pix2, vel)) { throw(AipsError(String("velocityToPixel 1 conversion failed because ") + lc.errorMessage())); } if (!near(pix2, pix)) { throw(AipsError(String("velocityToPixel 1 gave wrong answer"))); } // pix = 1.0; if (!lc.pixelToVelocity(vel, pix)) { throw(AipsError(String("pixelToVelocity 2 conversion failed because ") + lc.errorMessage())); } if (!near(vel, dVel)) { throw(AipsError(String("pixelToVelocity 2 gave wrong answer"))); } if (!lc.velocityToPixel(pix2, vel)) { throw(AipsError(String("velocityToPixel 2 conversion failed because ") + lc.errorMessage())); } if (!near(pix2, pix)) { throw(AipsError(String("velocityToPixel 2 gave wrong answer"))); } // Vector pixels(2), pixels2; Vector velocities; pixels(0) = 0.0; pixels(1) = 1.0; if (!lc.pixelToVelocity(velocities, pixels)) { throw(AipsError(String("pixelToVelocity 3 conversion failed because ") + lc.errorMessage())); } if (!near(velocities(0), 0.0) || !near(velocities(1), dVel)) { throw(AipsError(String("pixelToVelocity 3 gave wrong answer"))); } if (!lc.velocityToPixel(pixels2, velocities)) { throw(AipsError(String("velocityToPixel 3 conversion failed because ") + lc.errorMessage())); } if (!near(pixels2(0), pixels(0)) || !near(pixels2(1), pixels(1))) { throw(AipsError(String("pixelToVelocity 3 gave wrong answer"))); } // Quantum velQ; pix = 0.0; if (!lc.pixelToVelocity(velQ, pix)) { throw(AipsError(String("pixelToVelocity 4 conversion failed because ") + lc.errorMessage())); } if (!near(velQ.getValue(), 0.0)) { throw(AipsError(String("pixelToVelocity 4 gave wrong answer"))); } if (!lc.velocityToPixel(pix2, velQ.getValue())) { throw(AipsError(String("velocityToPixel 4 conversion failed because ") + lc.errorMessage())); } if (!near(pix2, pix)) { throw(AipsError(String("velocityToPixel 4 gave wrong answer"))); } // Frequency <-> Velocity Double freq; if (!lc.frequencyToVelocity(vel, f0)) { throw(AipsError(String("frequencyToVelocity 1 conversion failed because ") + lc.errorMessage())); } if (!near(vel, 0.0)) { throw(AipsError(String("frequencyToVelocity 1 gave wrong answer"))); } if (!lc.velocityToFrequency (freq, vel)) { throw(AipsError(String("velocityToFrequency 1 conversion failed because ") + lc.errorMessage())); } if (!near(freq, f0)) { throw(AipsError(String("velocityToFrequency 1 gave wrong answer"))); } // Vector frequencies(2), frequencies2; frequencies(0) = f0; frequencies(1) = f0 + finc; if (!lc.frequencyToVelocity(velocities, frequencies)) { throw(AipsError(String("frequencyToVelocity 2 conversion failed because ") + lc.errorMessage())); } if (!near(velocities(0), 0.0) || !near(velocities(1), dVel)) { throw(AipsError(String("frequencyToVelocity 2 gave wrong answer"))); } if (!lc.velocityToFrequency (frequencies2, velocities)) { throw(AipsError(String("velocityToFrequency 2 conversion failed because ") + lc.errorMessage())); } if (!near(frequencies2(0), frequencies(0)) || !near(frequencies2(1), frequencies(1))) { throw(AipsError(String("velocityToFrequency 2 gave wrong answer"))); } // lc.setVelocity (String("m/s"), MDoppler::RADIO); if (!lc.frequencyToVelocity(velocities, frequencies)) { throw(AipsError(String("frequencyToVelocity 2b conversion failed because ") + lc.errorMessage())); } if (!near(velocities(0), 0.0) || !near(velocities(1), dVel*1000)) { throw(AipsError(String("frequencyToVelocity 2b gave wrong answer"))); } if (!lc.velocityToFrequency (frequencies2, velocities)) { throw(AipsError(String("velocityToFrequency 2b conversion failed because ") + lc.errorMessage())); } if (!near(frequencies2(0), frequencies(0)) || !near(frequencies2(1), frequencies(1))) { throw(AipsError(String("velocityToFrequency 2b gave wrong answer"))); } // lc.setVelocity (String("km/s"), MDoppler::RADIO); if (!lc.frequencyToVelocity(velQ, f0+finc)) { throw(AipsError(String("frequencyToVelocity 3 conversion failed because ") + lc.errorMessage())); } if (!near(velQ.getValue(), dVel)) { throw(AipsError(String("frequencyToVelocity 3 gave wrong answer"))); } // if (!lc.frequencyToVelocity(velQ, f0)) { throw(AipsError(String("frequencyToVelocity 4 conversion failed because ") + lc.errorMessage())); } if (!near(velQ.getValue(), 0.0)) { throw(AipsError(String("frequencyToVelocity 4 gave wrong answer"))); } if (!lc.frequencyToVelocity(velQ, f0+finc)) { throw(AipsError(String("frequencyToVelocity 5 conversion failed because ") + lc.errorMessage())); } if (!near(velQ.getValue(), dVel)) { throw(AipsError(String("frequencyToVelocity 5 gave wrong answer"))); } // MVFrequency mvf(f0); MFrequency mf(mvf); if (!lc.frequencyToVelocity(velQ, mf)) { throw(AipsError(String("frequencyToVelocity 7 conversion failed because ") + lc.errorMessage())); } if (!near(velQ.getValue(), 0.0)) { throw(AipsError(String("frequencyToVelocity 7 gave wrong answer"))); } mvf = MVFrequency(f0+finc); mf = MFrequency(mvf); if (!lc.frequencyToVelocity(velQ, mf)) { throw(AipsError(String("frequencyToVelocity 8 conversion failed because ") + lc.errorMessage())); } if (!near(velQ.getValue(), dVel)) { throw(AipsError(String("frequencyToVelocity 8 gave wrong answer"))); } // mvf = MVFrequency(f0); if (!lc.frequencyToVelocity(velQ, mvf)) { throw(AipsError(String("frequencyToVelocity 9 conversion failed because ") + lc.errorMessage())); } if (!near(velQ.getValue(), 0.0)) { throw(AipsError(String("frequencyToVelocity 9 gave wrong answer"))); } mvf = MVFrequency(f0+finc); if (!lc.frequencyToVelocity(velQ, mvf)) { throw(AipsError(String("frequencyToVelocity 9 conversion failed because ") + lc.errorMessage())); } if (!near(velQ.getValue(), dVel)) { throw(AipsError(String("frequencyToVelocity 9 gave wrong answer"))); } // Frequency <-> Wavelength // Vector wavelengths; frequencies(0) = f0; frequencies(1) = f0 + finc; Double w0 = C::c/f0*1000.; // default unit is mm Double w1 = C::c/frequencies(1)*1000.; if (!lc.frequencyToWavelength(wavelengths, frequencies)) { throw(AipsError(String("frequencyToWavelength conversion failed because ") + lc.errorMessage())); } if (!near(wavelengths(0), w0) || !near(wavelengths(1), w1)) { throw(AipsError(String("frequencyToWavelength gave wrong answer"))); } if (!lc.wavelengthToFrequency (frequencies2, wavelengths)) { throw(AipsError(String("wavelengthToFrequency conversion failed because ") + lc.errorMessage())); } if (!near(frequencies2(0), frequencies(0)) || !near(frequencies2(1), frequencies(1))) { throw(AipsError(String("wavelengthToFrequency gave wrong answer"))); } // lc.setWavelengthUnit(String("m")); if (!lc.frequencyToWavelength(wavelengths, frequencies)) { throw(AipsError(String("frequencyToWavelength b conversion failed because ") + lc.errorMessage())); } if (!near(wavelengths(0), w0/1000.) || !near(wavelengths(1), w1/1000.)) { throw(AipsError(String("frequencyToWavelength b gave wrong answer"))); } if (!lc.wavelengthToFrequency (frequencies2, wavelengths)) { throw(AipsError(String("wavelengthToFrequency b conversion failed because ") + lc.errorMessage())); } if (!near(frequencies2(0), frequencies(0)) || !near(frequencies2(1), frequencies(1))) { throw(AipsError(String("wavelengthToFrequency b gave wrong answer"))); } // Frequency <-> Air Wavelength // // first test refractive index if(abs(FITSSpectralUtil::refractiveIndex(.480)-1.00029494145L)>1E-9){ cout << (FITSSpectralUtil::refractiveIndex(.480)-1.00029494145L)*1E6 << endl; throw(AipsError(String("refreactive index in air not correct"))); } lc.setWavelengthUnit(String("mm")); Vector airWavelengths; frequencies(0) = 6.26E14; frequencies(1) = 3.21E14; Double aw0 = C::c/frequencies(0)*1000.; // default unit is mm aw0 /= FITSSpectralUtil::refractiveIndex(aw0*1000.); // takes wavelength in microns Double aw1 = C::c/frequencies(1)*1000.; aw1 /= FITSSpectralUtil::refractiveIndex(aw1*1000.); if (!lc.frequencyToAirWavelength(airWavelengths, frequencies)) { throw(AipsError(String("frequencyToAirWavelength conversion failed because ") + lc.errorMessage())); } if (!near(airWavelengths(0), aw0) || !near(airWavelengths(1), aw1)) { cout << airWavelengths(0) << " " << aw0 << " " << airWavelengths(1) << " " << aw1 << endl; cout << airWavelengths(0) - aw0 << " " << airWavelengths(1) - aw1 << endl; throw(AipsError(String("frequencyToAirWavelength gave wrong answer"))); } if (!lc.airWavelengthToFrequency (frequencies2, airWavelengths)) { throw(AipsError(String("airWavelengthToFrequency conversion failed because ") + lc.errorMessage())); } if (!near(frequencies2(0), frequencies(0),5E-9) || !near(frequencies2(1), frequencies(1),5E-9)) { cout << frequencies2(0) << " " << frequencies(0) << " " << frequencies2(1) << " " << frequencies(1) << endl; cout << frequencies2(0) - frequencies(0) << " " << frequencies2(1) - frequencies(1) << endl; throw(AipsError(String("airWavelengthToFrequency gave wrong answer"))); } // lc.setWavelengthUnit(String("m")); if (!lc.frequencyToAirWavelength(airWavelengths, frequencies)) { throw(AipsError(String("frequencyToAirWavelength b conversion failed because ") + lc.errorMessage())); } if (!near(airWavelengths(0), aw0/1000.) || !near(airWavelengths(1), aw1/1000.)) { throw(AipsError(String("frequencyToAirWavelength b gave wrong answer"))); } if (!lc.airWavelengthToFrequency (frequencies2, airWavelengths)) { throw(AipsError(String("airWavelengthToFrequency b conversion failed because ") + lc.errorMessage())); } if (!near(frequencies2(0), frequencies(0), 5E-9) || !near(frequencies2(1), frequencies(1), 5E-9)) { throw(AipsError(String("airWavelengthToFrequency b gave wrong answer"))); } } // Test reference conversions { refConv(); } // // Test record saving // { SpectralCoordinate lc = makeLinearCoordinate(MFrequency::TOPO, f0, finc, refchan, restFreq); Vector rf(2); rf(0) = 1.0e9; rf(1) = 2.0e9; lc.setRestFrequencies(rf, 0, False); Record rec; if (!lc.save(rec, "linear")) { throw(AipsError("Linear SpectralCoordinate saving to Record failed")); } SpectralCoordinate* plc = SpectralCoordinate::restore(rec, "linear"); if (!plc) { throw(AipsError("Failed to restore Linear SpectralCoordinate")); } if (!plc->near(lc, 1e-6)) { throw(AipsError("Linear SpectralCoordinate reflection through record interface failed")); } delete plc; } { SpectralCoordinate lc = makeNonLinearCoordinate(MFrequency::TOPO, freqs, restFreq); Vector rf(2); rf(0) = 1.0e9; rf(1) = 2.0e9; lc.setRestFrequencies(rf, 0, False); Record rec; if (!lc.save(rec, "nonlinear")) { throw(AipsError("Non-linear SpectralCoordinate saving to Record failed")); } SpectralCoordinate* plc = SpectralCoordinate::restore(rec, "nonlinear"); if (!plc) { throw(AipsError("Failed to restore non-linear SpectralCoordinate")); } if (!plc->near(lc, 1e-6)) { throw(AipsError("Non-linear SpectralCoordinate reflection through record interface failed")); } delete plc; } // // Test clone // { SpectralCoordinate lc = makeLinearCoordinate(MFrequency::TOPO, f0, finc, refchan, restFreq); Coordinate* plc = lc.clone(); if (!plc->near(lc, 1e-6)) { throw(AipsError("Clone function failed")); } delete plc; } { cout << "Test toWorld using both native and conversion layer frames" << endl; SpectralCoordinate sc( MFrequency::LSRK, Quantity(1500, "MHz"), Quantity(1, "kHz"), 0, Quantity(1500, "MHz") ); MEpoch epoch(Quantity(60000, "d"), MEpoch::UTC); MPosition position( Quantity(10, "m"), Quantity(135, "deg"), Quantity(40, "deg"), MPosition::ITRF ); MDirection direction(Quantity(20, "deg"), Quantity(50, "deg"), MDirection::J2000); sc.setReferenceConversion(MFrequency::CMB, epoch, position, direction); Vector pixel(1, 10); Vector world(1); sc.toWorld(world, pixel); AlwaysAssert(near(world[0], 1.50121e+09, 1e-5), AipsError); sc.toWorld(world, pixel, True); AlwaysAssert(near(world[0], 1.50121e+09, 1e-5), AipsError); sc.toWorld(world, pixel, False); AlwaysAssert(world[0] == 1.50001e+09, AipsError); } } catch (const std::exception& x) { cerr << "aipserror: error " << x.what() << endl; return (1); } cout << "ok" << endl; return (0); } SpectralCoordinate makeLinearCoordinate (MFrequency::Types type, Double& f0, Double& finc, Double& refchan, Double& restFreq) { refchan = 10.5; finc = 4e6; f0 = 1.4e9; restFreq = 1.420405752E9; // return SpectralCoordinate(type, f0, finc, refchan, restFreq); } SpectralCoordinate makeNonLinearCoordinate (MFrequency::Types type, Vector& freqs, Double& restFreq) { restFreq = 1.420405752E9; freqs.resize(5); freqs(0) = 1.4e9; freqs(1) = 1.41e9; freqs(2) = 1.43e9; freqs(3) = 1.44e9; freqs(4) = 1.47e9; // return SpectralCoordinate(type, freqs, restFreq); } Double velInc (Double dF, Double f0, MDoppler::Types velType) { Double c = QC::c( ).getValue(Unit("km/s")); if (velType==MDoppler::RADIO) { return -c * dF / f0; } throw(AipsError("very cheap software")); } void refConv () // // Test conversion with reference change // { { // on a linear coordinate Double f0, finc, refchan, restFreq; SpectralCoordinate lc = makeLinearCoordinate(MFrequency::LSRK, f0, finc, refchan, restFreq); // Vector pixel = lc.referencePixel().copy(); Vector world; // if (!lc.toWorld(world, pixel)) { throw(AipsError(String("toWorld conversion (1) failed because ") + lc.errorMessage())); } // Quantum t(50237.29, Unit(String("d"))); MVEpoch t2(t); MEpoch epoch(t2); // MPosition pos; MeasTable::Observatory(pos, String("ATCA")); // Quantum lon(0.0,Unit(String("rad"))); Quantum lat(-35.0,Unit(String("deg"))); MDirection dir(lon, lat, MDirection::J2000); MFrequency::Types type = MFrequency::BARY; if (!lc.setReferenceConversion(type, epoch, pos, dir)) { throw(AipsError("setReferenceConversion failed")); } // if (!lc.toWorld(world, pixel)) { throw(AipsError(String("toWorld + reference conversion (1) failed because ") + lc.errorMessage())); } // Vector pixel2; if (!lc.toPixel(pixel2, world)) { throw(AipsError(String("toPixel + reference conversion (1) failed because ") + lc.errorMessage())); } // if (!casacore::allNear(pixel2, pixel, 1e-6)) { throw(AipsError("Coordinate + reference conversion reflection 1 failed")); } // MFrequency::Types type2; MEpoch epoch2; MPosition pos2; MDirection dir2; lc.getReferenceConversion(type2, epoch2, pos2, dir2); // AlwaysAssert(type2==MFrequency::BARY, AipsError); AlwaysAssert(near(epoch.getValue().get(), epoch2.getValue().get()), AipsError); AlwaysAssert(casacore::allNear(pos.getValue().get(), pos2.getValue().get(), 1e-6), AipsError); AlwaysAssert(casacore::allNear(dir.getValue().get(), dir2.getValue().get(), 1e-6), AipsError); Vector baryFreq; lc.toWorld(baryFreq, pixel); AlwaysAssert(lc.transformFrequencySystem(MFrequency::BARY, epoch, pos, dir), AipsError); AlwaysAssert(casacore::allNear(baryFreq, lc.referenceValue(), 1e-6), AipsError); } { // on a non-linear coordinate Double restFreq; Vector freqs; SpectralCoordinate lc = makeNonLinearCoordinate(MFrequency::LSRK, freqs, restFreq); // Vector pixel = lc.referencePixel().copy(); Vector world; // if (!lc.toWorld(world, pixel)) { throw(AipsError(String("toWorld conversion (2) failed because ") + lc.errorMessage())); } // Quantum t(50237.29, Unit(String("d"))); MVEpoch t2(t); MEpoch epoch(t2); // MPosition pos; MeasTable::Observatory(pos, String("ATCA")); // Quantum lon(0.0,Unit(String("rad"))); Quantum lat(-35.0,Unit(String("deg"))); MDirection dir(lon, lat, MDirection::J2000); MFrequency::Types type = MFrequency::CMB; if (!lc.setReferenceConversion(type, epoch, pos, dir)) { throw(AipsError("setReferenceConversion failed in nonlinear coordinate")); } // if (!lc.toWorld(world, pixel)) { throw(AipsError(String("toWorld + reference conversion (2) failed because ") + lc.errorMessage())); } // Vector pixel2; if (!lc.toPixel(pixel2, world)) { throw(AipsError(String("toPixel + reference conversion (2) failed because ") + lc.errorMessage())); } // if (!(fabs(pixel2[0]-pixel[0])<1e-6)) { throw(AipsError("Coordinate + reference conversion reflection 2 failed")); } // MFrequency::Types type2; MEpoch epoch2; MPosition pos2; MDirection dir2; lc.getReferenceConversion(type2, epoch2, pos2, dir2); // AlwaysAssert(type2==MFrequency::CMB, AipsError); AlwaysAssert(near(epoch.getValue().get(), epoch2.getValue().get()), AipsError); AlwaysAssert(casacore::allNear(pos.getValue().get(), pos2.getValue().get(), 1e-6), AipsError); AlwaysAssert(casacore::allNear(dir.getValue().get(), dir2.getValue().get(), 1e-6), AipsError); Vector cmbFreq; lc.toWorld(cmbFreq, pixel); AlwaysAssert(lc.transformFrequencySystem(MFrequency::CMB, epoch, pos, dir), AipsError); Vector cmbFreq2; lc.toWorld(cmbFreq2, pixel); AlwaysAssert(casacore::allNear(cmbFreq, lc.referenceValue(), 1e-6), AipsError); } } casacore-3.7.1/coordinates/Coordinates/test/tStokesCoordinate.cc000066400000000000000000000301641476623553700250460ustar00rootroot00000000000000//# tStokesCoordinate.cc: Test program for StokesCoordinate //# Copyright (C) 1998,1999,2000,2001,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include #include #include #include #include #include #include #include #include #include #include StokesCoordinate makeCoordinate(Vector& whichStokes, Vector& stokesStrings); void doit (StokesCoordinate& lc, const Vector& whichStokes); void doit2 (StokesCoordinate& lc, const Vector& whichStokes); void doit3 (StokesCoordinate& lc, const Vector& whichStokes, const Vector& stokesStrings); void doit4(StokesCoordinate& lc); void doit5(); int main() { try { Vector whichStokes; Vector stokesStrings; // Constructors { StokesCoordinate lc = makeCoordinate(whichStokes, stokesStrings); } // Test near function { StokesCoordinate lc = makeCoordinate(whichStokes, stokesStrings); StokesCoordinate lc2 = makeCoordinate(whichStokes, stokesStrings); if (!lc.near(lc2)) { throw(AipsError("Failed near test 1")); } Vector excludeAxes(1, 0); if (!lc.near(lc2, excludeAxes)) { throw(AipsError("Failed near test 2")); } } // Test the rest { StokesCoordinate lc = makeCoordinate(whichStokes, stokesStrings); doit(lc, whichStokes); doit2(lc, whichStokes); } { StokesCoordinate lc = makeCoordinate(whichStokes, stokesStrings); doit3(lc, whichStokes, stokesStrings); } { StokesCoordinate lc = makeCoordinate(whichStokes, stokesStrings); doit4(lc); } { doit5(); } { Vector stokesInts(4); stokesInts[0] = (Int)Stokes::V; stokesInts[1] = (Int)Stokes::LL; stokesInts[2] = (Int)Stokes::XY; stokesInts[3] = (Int)Stokes::Q; StokesCoordinate coord(stokesInts); Vector stokesStrings = coord.stokesStrings(); Vector expec(4); expec[0] = "V"; expec[1] = "LL"; expec[2] = "XY"; expec[3] = "Q"; AlwaysAssert(allTrue(stokesStrings == expec), AipsError); } } catch (const std::exception& x) { cerr << "aipserror: error " << x.what() << endl; return (1); } cout << "ok" << endl; return (0); } StokesCoordinate makeCoordinate(Vector& whichStokes, Vector& stokesStrings) { // // Choose random and silly collection of Stokeseses whichStokes.resize(5); whichStokes(0) = Stokes::Q; whichStokes(1) = Stokes::RL; whichStokes(2) = Stokes::YY; whichStokes(3) = Stokes::RY; whichStokes(4) = Stokes::QQ; // stokesStrings.resize(5); stokesStrings(0) = "Q"; stokesStrings(1) = "RL"; stokesStrings(2) = "YY"; stokesStrings(3) = "RY"; stokesStrings(4) = "QQ"; // return StokesCoordinate(whichStokes); } void doit (StokesCoordinate& lc, const Vector& whichStokes) { // Test copy constructor { StokesCoordinate lc2(lc); if (!lc.near(lc2)) { throw(AipsError("Failed copy constructor test")); } } // Test assignment { Vector whichStokes2(1); whichStokes2(0) = Stokes::I; StokesCoordinate lc2 = StokesCoordinate(whichStokes2); lc2 = lc; if (!lc.near(lc2)) { throw(AipsError("Failed assignment test")); } } // Test member functions if (lc.type() != Coordinate::STOKES) { throw(AipsError("Failed type test")); } if (lc.showType() != "Stokes") { throw(AipsError("Failed showType test")); } // if (lc.nPixelAxes() != 1) { throw(AipsError("Failed nPixelAxes test")); } // if (lc.nWorldAxes() != 1) { throw(AipsError("Failed nWorldAxes test")); } // Vector axisNames(1); axisNames(0) = "Stokes"; if (!allEQ(axisNames, lc.worldAxisNames())) { throw(AipsError("Failed world axis name recovery test")); } axisNames(0) = "Horsies"; if (!lc.setWorldAxisNames(axisNames)) { throw(AipsError(String("Failed to set world axis name because") + lc.errorMessage())); } if (!allEQ(axisNames, lc.worldAxisNames())) { throw(AipsError("Failed axis name set/recovery test")); } // // There is no unit we can set // Vector axisUnits(1); axisUnits(0) = ""; if (!allEQ(axisUnits, lc.worldAxisUnits())) { throw(AipsError("Failed world axis units recovery test")); } if (!lc.setWorldAxisUnits(axisUnits)) { throw(AipsError(String("Failed to set world axis units because ") + lc.errorMessage())); } if (!allEQ(axisUnits, lc.worldAxisUnits())) { throw(AipsError("Failed world axis units set/recovery test")); } // if (!allEQ(whichStokes, lc.stokes())) { throw(AipsError("Failed Stokes recovery test")); } // // Test record saving // TableRecord rec; if (!lc.save(rec, "Stokes")) { throw(AipsError("Coordinate saving to Record failed")); } StokesCoordinate* plc = StokesCoordinate::restore(rec, "Stokes"); if (!plc->near(lc, 1e-6)) { throw(AipsError("Coordinate reflection through record interface failed")); } delete plc; // // Test clone // Coordinate* plc2 = lc.clone(); if (!plc2->near(lc, 1e-6)) { throw(AipsError("Clone function failed")); } delete plc2; } void doit2 (StokesCoordinate& lc, const Vector& whichStokes) { Vector crval(1); crval(0) = Double(whichStokes(0)); if (!allEQ(crval, lc.referenceValue())) { throw(AipsError("Failed reference value recovery test")); } // Vector cdelt(1); cdelt(0) = 1.0; if (!allEQ(cdelt, lc.increment())) { throw(AipsError("Failed increment recovery test")); } // Vector crpix(1); crpix(0) = 0.0; if (!allEQ(crpix, lc.referencePixel())) { throw(AipsError("Failed reference pixel recovery test")); } // Matrix xform(1,1); xform(0,0) = 1.0; if (!allEQ(xform, lc.linearTransform())) { throw(AipsError("Failed Stokes transform recovery test")); } // Vector oldRefVal = lc.referenceValue(); Vector oldIncr = lc.increment(); Vector oldRefPix = lc.referencePixel(); Matrix oldLinTr = lc.linearTransform(); crval(0) = 111.1; if (!lc.setReferenceValue(crval)) { throw(AipsError(String("Failed to set reference value because") + lc.errorMessage())); } if (!allEQ(oldRefVal, lc.referenceValue())) { throw(AipsError("Failed reference value set/recovery test")); } // cdelt(0) = -10.3; if (!lc.setIncrement(cdelt)) { throw(AipsError(String("Failed to set increment because") + lc.errorMessage())); } if (!allEQ(oldIncr, lc.increment())) { throw(AipsError("Failed increment set/recovery test")); } // crpix(0) = 23.0; if (!lc.setReferencePixel(crpix)) { throw(AipsError(String("Failed to set reference pixel because") + lc.errorMessage())); } if (!allEQ(oldRefPix, lc.referencePixel())) { throw(AipsError("Failed reference pixel set/recovery test")); } // xform.diagonal() = -2.0; if (!lc.setLinearTransform(xform)) { throw(AipsError(String("Failed to set linear transform because") + lc.errorMessage())); } if (!allEQ(oldLinTr, lc.linearTransform())) { throw(AipsError("Failed linear transform set/recovery test")); } } void doit3 (StokesCoordinate& lc, const Vector& whichStokes, const Vector& stokesStrings) { // // Test conversion // Vector pixel(1), world; pixel(0) = lc.referencePixel()(0); if (!lc.toWorld(world, pixel)) { throw(AipsError(String("toWorld conversion failed because ") + lc.errorMessage())); } // Vector pixel2(1); if (!lc.toPixel(pixel2, world)) { throw(AipsError(String("toPixel conversion failed because ") + lc.errorMessage())); } if (!allNear(pixel2, pixel, 1e-6)) { throw(AipsError("Coordinate conversion reflection 1 failed")); } // world(0) = -10000.0; if (lc.toPixel(pixel2, world)) { throw(AipsError(String("toPixel succeeded unexpectedly"))); } else { // cout << "Failed as expected with" << lc.errorMessage() << endl; } // Int pixel3; for (Int i=0; i axes(lc.nWorldAxes(), True); Vector shape(lc.nPixelAxes(), 10); Bool failed = False; Coordinate* pC = 0; try { pC = lc.makeFourierCoordinate (axes, shape); } catch (std::exception& x) { failed = True; } if (!failed) { throw(AipsError("Failed to induce forced error (1) in makeFourierCoordinate")); } delete pC; } void doit5() { // Test setStokes { Vector stokes(1); stokes(0) = Stokes::I; Vector stokesStrings(1); stokesStrings(0) = String("I"); StokesCoordinate lc(stokes); // stokes.resize(2); stokesStrings.resize(2); stokes(0) = Stokes::Q; stokes(1) = Stokes::XX; stokesStrings(0) = String("Q"); stokesStrings(1) = String("XX"); lc.setStokes(stokes); // Vector stokes2 = lc.stokes(); AlwaysAssert(stokes2.nelements()==2, AipsError); AlwaysAssert(Stokes::type(stokes2(0))==Stokes::Q, AipsError); AlwaysAssert(Stokes::type(stokes2(1))==Stokes::XX, AipsError); // doit(lc, stokes); doit3(lc, stokes, stokesStrings); } } casacore-3.7.1/coordinates/Coordinates/test/tTabularCoordinate.cc000066400000000000000000000407111476623553700251670ustar00rootroot00000000000000//# tTabularCoordinate.cc: Test program for TabularCoordinate //# Copyright (C) 1998,1999,2000,2001,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include #include #include #include #include #include #include #include #include #include #include #include TabularCoordinate makeLinearCoordinate(String& axisName, String& axisUnit, Double& crval, Double& crpix, Double& cdelt); TabularCoordinate makeNonLinearCoordinate (String& axisName, String& axisUnit, Vector& pixelValues, Vector& worldValues); void doit (TabularCoordinate& lc, const String& axisName, const String& axisUnit); void doitLinear (const Double refVal, const Double refPix, const Double incr, const Double linTrans, TabularCoordinate& lc); void doitNonLinear (const Vector& pixelValues, const Vector& worldValues, TabularCoordinate& lc); int main() { try { String axisName, axisUnit; Double crpix, crval, cdelt; Matrix xform; Vector pixelValues; Vector worldValues; // Constructors { TabularCoordinate lc; } { TabularCoordinate lc = makeLinearCoordinate(axisName, axisUnit, crval, crpix, cdelt); } { TabularCoordinate lc = makeNonLinearCoordinate(axisName, axisUnit, pixelValues, worldValues); } // Test near function { TabularCoordinate lc = makeLinearCoordinate(axisName, axisUnit, crval, crpix, cdelt); TabularCoordinate lc2 = makeLinearCoordinate(axisName, axisUnit, crval, crpix, cdelt); if (!lc.near(lc2)) { throw(AipsError("Failed near test 1")); } Vector excludeAxes(1, 0); if (!lc.near(lc2, excludeAxes)) { throw(AipsError("Failed near test 2")); } } // Test Quantum constructor interfaces { Double crval = 100.0; Double crpix = 1.0; Double cdelt = 1.2; String name("length"); String unit("m"); // TabularCoordinate tc1(crval, cdelt, crpix, unit, name); // Quantum crval2(crval, "m"); Quantum cdelt2(100*cdelt, "cm"); TabularCoordinate tc2(crval2, cdelt2, crpix, name); // if (!tc1.near(tc2)) { throw(AipsError(String("Quantum interface (1) constructor failed consistency test"))); } } { Vector world(3); Vector pixel(3); pixel(0) = 1.0; pixel(1) = 3.0; pixel(2) = 6.0; world(0) = 10.0; world(1) = 30.0; world(2) = 60.0; String name("length"); String unit("m"); // TabularCoordinate tc1(pixel, world, unit, name); // Quantum > world2(world, unit); TabularCoordinate tc2(pixel, world2, name); // if (!tc1.near(tc2)) { throw(AipsError(String("Quantum interface (2) constructor failed consistency test"))); } } // Test the rest { TabularCoordinate lc = makeLinearCoordinate(axisName, axisUnit, crval, crpix, cdelt); doit(lc, axisName, axisUnit); } { TabularCoordinate lc = makeLinearCoordinate(axisName, axisUnit, crval, crpix, cdelt); doitLinear(crval, crpix, cdelt, 1.0, lc); } { TabularCoordinate lc = makeNonLinearCoordinate(axisName, axisUnit, pixelValues, worldValues); doit(lc, axisName, axisUnit); } { TabularCoordinate lc = makeNonLinearCoordinate(axisName, axisUnit, pixelValues, worldValues); doitNonLinear(pixelValues, worldValues, lc); } } catch (std::exception& x) { cerr << "aipserror: error " << x.what() << endl; return (1); } cout << "ok" << endl; return (0); } TabularCoordinate makeLinearCoordinate(String& axisName, String& axisUnit, Double& crval, Double& crpix, Double& cdelt) { crval = 10.12; crpix = -128.32; cdelt = 3.145; axisUnit = "km"; axisName = "Doggies"; return TabularCoordinate(crval, cdelt, crpix, axisUnit, axisName); } TabularCoordinate makeNonLinearCoordinate (String& axisName, String& axisUnit, Vector& pixelValues, Vector& worldValues) { pixelValues.resize(5); worldValues.resize(5); pixelValues(0) = 10.0; pixelValues(1) = 22.0; pixelValues(2) = 80.0; pixelValues(3) = 102.2; pixelValues(4) = 102.3; worldValues(0) = -100.0, worldValues(1) = -80.0; worldValues(2) = 140.2; worldValues(3) = 1000.212; worldValues(4) = 2100.3; axisUnit = "km"; axisName = "Doggies"; return TabularCoordinate(pixelValues, worldValues, axisUnit, axisName); } void doit (TabularCoordinate& lc, const String& axisName, const String& axisUnit) { // Test copy constructor { TabularCoordinate lc2(lc); if (!lc.near(lc2)) { throw(AipsError("Failed copy constructor test")); } } // Test assignment { TabularCoordinate lc2; lc2 = lc; if (!lc.near(lc2)) { throw(AipsError("Failed assignment test")); } } // Test member functions if (lc.type() != Coordinate::TABULAR) { throw(AipsError("Failed type test")); } if (lc.showType() != "Tabular") { throw(AipsError("Failed showType test")); } // if (lc.nPixelAxes() != 1) { throw(AipsError("Failed nPixelAxes test")); } // if (lc.nWorldAxes() != 1) { throw(AipsError("Failed nWorldAxes test")); } // Vector names(1); names(0) = axisName; if (!allEQ(names, lc.worldAxisNames())) { throw(AipsError("Failed world axis name recovery test")); } names(0) = "Horsies"; if (!lc.setWorldAxisNames(names)) { throw(AipsError(String("Failed to set world axis name because") + lc.errorMessage())); } if (!allEQ(names, lc.worldAxisNames())) { throw(AipsError("Failed axis name set/recovery test")); } // Vector units(1); units(0) = axisUnit; if (!allEQ(units, lc.worldAxisUnits())) { throw(AipsError("Failed world axis units recovery test")); } units(0) = "m"; if (!lc.setWorldAxisUnits(units)) { throw(AipsError(String("Failed to set world axis units because ") + lc.errorMessage())); } if (!allEQ(units, lc.worldAxisUnits())) { throw(AipsError("Failed world axis units set/recovery test")); } // Int prec; Coordinate::formatType fType = Coordinate::SCIENTIFIC; lc.getPrecision(prec, fType, True, 6, 4, 2); if (prec != 6) { throw(AipsError("Failed getPrecision test 1")); } fType = Coordinate::FIXED; lc.getPrecision(prec, fType, True, 6, 4, 2); if (prec != 4) { throw(AipsError("Failed getPrecision test 2")); } // String unit; Double val = 20.12345; Quantum valq(val, Unit(units(0))); String str = lc.format(unit, Coordinate::FIXED, val, 0, True, True, 4); String str2 = lc.formatQuantity(unit, Coordinate::FIXED, valq, 0, True, True, 4); if (str != "20.1234" || str2 != "20.1234") { throw(AipsError("Failed format test 1")); } str = lc.format(unit, Coordinate::SCIENTIFIC, val, 0, True, True, 4); str2 = lc.formatQuantity(unit, Coordinate::SCIENTIFIC, valq, 0, True, True, 4); if (str != "2.0123e+01" || str2 != "2.0123e+01") { throw(AipsError("Failed format test 2")); } // // Test record saving // TableRecord rec; if (!lc.save(rec, "Tabular")) { throw(AipsError("Coordinate saving to Record failed")); } TabularCoordinate* plc = TabularCoordinate::restore(rec, "Tabular"); if (!plc->near(lc, 1e-6)) { throw(AipsError("Coordinate reflection through record interface failed")); } delete plc; // // Test clone // Coordinate* plc2 = lc.clone(); if (!plc2->near(lc, 1e-6)) { throw(AipsError("Clone function failed")); } delete plc2; } void doitLinear (const Double refVal, const Double refPix, const Double incr, const Double linTrans, TabularCoordinate& lc) { Vector crval(1); crval(0) = refVal; Vector crpix(1); crpix(0) = refPix; Vector cdelt(1); cdelt(0) = incr; Matrix xform(1,1); xform(0,0) = linTrans; // if (!allEQ(crval, lc.referenceValue())) { throw(AipsError("Failed reference value recovery test")); } // if (!allEQ(cdelt, lc.increment())) { throw(AipsError("Failed increment recovery test")); } // if (!allEQ(crpix, lc.referencePixel())) { throw(AipsError("Failed reference pixel recovery test")); } // if (!allEQ(xform, lc.linearTransform())) { throw(AipsError("Failed Tabular transform recovery test")); } // crval(0) = 111.1; if (!lc.setReferenceValue(crval)) { throw(AipsError(String("Failed to set reference value because") + lc.errorMessage())); } if (!allEQ(crval, lc.referenceValue())) { throw(AipsError("Failed reference value set/recovery test")); } // cdelt(0) = -10.3; if (!lc.setIncrement(cdelt)) { throw(AipsError(String("Failed to set increment because") + lc.errorMessage())); } if (!allEQ(cdelt, lc.increment())) { throw(AipsError("Failed increment set/recovery test")); } // crpix(0) = 23.0; if (!lc.setReferencePixel(crpix)) { throw(AipsError(String("Failed to set reference pixel because") + lc.errorMessage())); } if (!allEQ(crpix, lc.referencePixel())) { throw(AipsError("Failed reference pixel set/recovery test")); } // xform.diagonal() = -2.0; if (!lc.setLinearTransform(xform)) { throw(AipsError(String("Failed to set linear transform because") + lc.errorMessage())); } if (!allEQ(xform, lc.linearTransform())) { throw(AipsError("Failed linear transform set/recovery test")); } // // Test conversion // Vector pixel(1), world2(1), world; pixel(0) = 12.2; if (!lc.toWorld(world, pixel)) { throw(AipsError(String("toWorld conversion failed because ") + lc.errorMessage())); } // // Compute expected values. // world2(0) = (pixel(0) - crpix(0)) * xform(0,0) * cdelt(0) + crval(0); // if (!allNear(world2, world, 1e-6)) { throw(AipsError("toWorld conversion gave wrong answer")); } // Vector pixel2; if (!lc.toPixel(pixel2, world)) { throw(AipsError(String("toPixel conversion failed because ") + lc.errorMessage())); } if (!allNear(pixel2, pixel, 1e-6)) { throw(AipsError("Coordinate conversion reflection failed")); } // // Test Fourier coordinate // { AlwaysAssert(lc.nPixelAxes()==1, AipsError); Vector axes(1, True); Vector shape(1); shape(0) = 128; // All axes { Coordinate* pC = lc.makeFourierCoordinate (axes, shape); // Vector units2 = pC->worldAxisUnits(); Vector names2 = pC->worldAxisNames(); Vector crval2 = pC->referenceValue(); Vector crpix2 = pC->referencePixel(); String tt = String("1/") + lc.worldAxisUnits()(0); if (units2(0)!=tt) { throw(AipsError("makeFourierCoordinate (1) failed units test")); } tt = String("Inverse(") + lc.worldAxisNames()(0) + String(")"); if (names2(0)!=tt) { throw(AipsError("makeFourierCoordinate (1) failed names test")); } if (!allNear(crval2,0.0,1e-13)) { throw(AipsError("makeFourierCoordinate (1) failed crval test")); } for (uInt i=0; inPixelAxes(); i++) { if (!near(Double(Int(shape(i)/2)), crpix2(i))) { throw(AipsError("makeFourierCoordinate (1) failed crpix test")); } } delete pC; } // No axes { axes.set(False); Coordinate* pC = lc.makeFourierCoordinate (axes, shape); if (pC) { delete pC; throw(AipsError("Failed to induce forced error (1) in makeFourierCoordinate")); } } } } void doitNonLinear (const Vector& pixelValues, const Vector& worldValues, TabularCoordinate& lc) { Vector crval(1); crval(0) = worldValues(0); Vector crpix(1); crpix(0) = pixelValues(0); Vector cdelt(1); Matrix xform(1,1); // if (!allEQ(crval, lc.referenceValue())) { throw(AipsError("Failed reference value recovery test")); } // if (!allEQ(crpix, lc.referencePixel())) { throw(AipsError("Failed reference pixel recovery test")); } // crval(0) = 111.1; if (!lc.setReferenceValue(crval)) { throw(AipsError(String("Failed to set reference value because") + lc.errorMessage())); } if (!allEQ(crval, lc.referenceValue())) { throw(AipsError("Failed reference value set/recovery test")); } // cdelt(0) = -10.3; if (!lc.setIncrement(cdelt)) { throw(AipsError(String("Failed to set increment because") + lc.errorMessage())); } if (!allEQ(cdelt, lc.increment())) { throw(AipsError("Failed increment set/recovery test")); } // crpix(0) = 23.0; if (!lc.setReferencePixel(crpix)) { throw(AipsError(String("Failed to set reference pixel because") + lc.errorMessage())); } if (!allEQ(crpix, lc.referencePixel())) { throw(AipsError("Failed reference pixel set/recovery test")); } // xform.diagonal() = -2.0; if (!lc.setLinearTransform(xform)) { throw(AipsError(String("Failed to set Tabular transform because") + lc.errorMessage())); } if (!allEQ(xform, lc.linearTransform())) { throw(AipsError("Failed Tabular transform set/recovery test")); } // // Test conversion // Vector pixel(1), world, pixel2; pixel(0) = 123.123; if (!lc.toWorld(world, pixel)) { throw(AipsError(String("toPixel conversion failed because ") + lc.errorMessage())); } if (!lc.toPixel(pixel2, world)) { throw(AipsError(String("toPixel conversion failed because ") + lc.errorMessage())); } if (!allNear(pixel2, pixel, 1e-6)) { throw(AipsError("Coordinate conversion reflection failed")); } // Fourier { Vector axes(lc.nPixelAxes(), True); Vector shape(lc.nPixelAxes(), 10); Coordinate* pC = lc.makeFourierCoordinate (axes, shape); if (pC) { delete pC; throw(AipsError("Failed to induce forced error (1) in makeFourierCoordinate")); } } } casacore-3.7.1/coordinates/coordinates.dox000066400000000000000000000027331476623553700206710ustar00rootroot00000000000000//# coordinates.dox: doxygen description of coordinates package //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ // \defgroup coordinates coordinates package (libcasa_coordinates) // // The coordinates package handles world coordinates for images // casacore-3.7.1/derivedmscal/000077500000000000000000000000001476623553700157665ustar00rootroot00000000000000casacore-3.7.1/derivedmscal/CMakeLists.txt000066400000000000000000000015621476623553700205320ustar00rootroot00000000000000# # CASA derivedmscal # add_library (casa_derivedmscal DerivedMC/DerivedMSCal.cc DerivedMC/DerivedColumn.cc DerivedMC/MSCalEngine.cc DerivedMC/Register.cc DerivedMC/UDFMSCal.cc ) set(top_level_headers DerivedMC.h ) init_pch_support(casa_derivedmscal ${top_level_headers}) target_link_libraries (casa_derivedmscal casa_ms ${CASACORE_ARCH_LIBS}) install (TARGETS casa_derivedmscal LIBRARY DESTINATION lib${LIB_SUFFIX} ARCHIVE DESTINATION lib${LIB_SUFFIX} LIBRARY PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) install (FILES DerivedMC/DerivedMSCal.h DerivedMC/DerivedColumn.h DerivedMC/MSCalEngine.h DerivedMC/Register.h DerivedMC/UDFMSCal.h DESTINATION include/casacore/derivedmscal/DerivedMC ) install (FILES ${top_level_headers} DESTINATION include/casacore/derivedmscal ) add_subdirectory (DerivedMC/test ${EXCL_ALL}) casacore-3.7.1/derivedmscal/DerivedMC.h000066400000000000000000000077661476623553700177610ustar00rootroot00000000000000//# DerivedMC.h: Derived MS and CalTable columns //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef DERIVEDMSCAL_DERIVEDMC_H #define DERIVEDMSCAL_DERIVEDMC_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // // // Derived MS and CalTable columns // // //
      • MeasurementSets module // // // // // // Class to handle derived columns in an MS or CalTable. // // // // A MeasurementSet or CalTable can be extended with virtual columns to // be able to use hourangle, azimuth/elevation, parallactic angle, and UVW // as if they were stored in the table using the DerivedMSCal virtual column // engine. Such columns have a fixed name, otherwise the engine cannot handle // them. The class description of the engine shows which columns can be handled // and gives an example how to add them. // // Class UDFMSCal contains TaQL user defined functions for these virtual // columns. In this way the columns do not need to be added to the table, // but can be used directly in a TaQL command. For example: // // select from my.ms where derivedmscal.ha1() > 10deg // // to select the rows where the hourangle of ANTENNA1 fulfills the condition. // If HA1 was added as a virtual column (which is more intrusive), the // command could look like: // // select from my.ms where HA1 > 10deg // // // UVW coordinates are already stored in an MS, but the virtual UVW_J2000 // column makes it possible to calculate them on the fly. // // An MS or CalTable can be created with one or more of these columns or they // can be added later using 'DerivedMSCal' as their data manager. // A column can, of course, also be removed. If all these columns are removed, // the Table System will also remove the data manager from the table. // // The derivedmscal library can be used in two ways: //
          //
        1. It needs to be linked in if the DerivedMSCal class is used. This // mode will probably be used rarely. //
        2. It will be loaded dynamically when a table is opened with columns // using this data manager, when a column is added to a table using // this data manager by name (thus without an object), or when // such a TaQL user defined function is used. //
        // For the second reason above it is important that the library // and the other casacore libraries are built shared. // //
        // // // It is very handy to be able to use a column like PA in software that can // deal with various table columns (e.g. TaQL, TablePlot, pyrap). // //# //# // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/derivedmscal/DerivedMC/000077500000000000000000000000001476623553700175705ustar00rootroot00000000000000casacore-3.7.1/derivedmscal/DerivedMC/DerivedColumn.cc000066400000000000000000000056761476623553700226550ustar00rootroot00000000000000//# DerivedColumn.cc: The derived MS columns. //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include namespace casacore { HourangleColumn::~HourangleColumn() {} void HourangleColumn::get (rownr_t rowNr, Double& data) { data = itsEngine->getHA (itsAntNr, rowNr); } ParAngleColumn::~ParAngleColumn() {} void ParAngleColumn::get (rownr_t rowNr, Double& data) { data = itsEngine->getPA (itsAntNr, rowNr); } LASTColumn::~LASTColumn() {} void LASTColumn::get (rownr_t rowNr, Double& data) { data = itsEngine->getLAST (itsAntNr, rowNr); } HaDecColumn::~HaDecColumn() {} IPosition HaDecColumn::shape (rownr_t) { return IPosition(1,2); } Bool HaDecColumn::isShapeDefined (rownr_t) { return True; } void HaDecColumn::getArray (rownr_t rowNr, Array& data) { itsEngine->getHaDec (itsAntNr, rowNr, data); } AzElColumn::~AzElColumn() {} IPosition AzElColumn::shape (rownr_t) { return IPosition(1,2); } Bool AzElColumn::isShapeDefined (rownr_t) { return True; } void AzElColumn::getArray (rownr_t rowNr, Array& data) { itsEngine->getAzEl (itsAntNr, rowNr, data); } ItrfColumn::~ItrfColumn() {} IPosition ItrfColumn::shape (rownr_t) { return IPosition(1,2); } Bool ItrfColumn::isShapeDefined (rownr_t) { return True; } void ItrfColumn::getArray (rownr_t rowNr, Array& data) { itsEngine->getItrf (itsAntNr, rowNr, data); } UVWJ2000Column::~UVWJ2000Column() {} IPosition UVWJ2000Column::shape (rownr_t) { return IPosition(1,3); } Bool UVWJ2000Column::isShapeDefined (rownr_t) { return True; } void UVWJ2000Column::getArray (rownr_t rowNr, Array& data) { itsEngine->getNewUVW (False, rowNr, data); } } //# end namespace casacore-3.7.1/derivedmscal/DerivedMC/DerivedColumn.h000066400000000000000000000120511476623553700225000ustar00rootroot00000000000000//# DerivedColumn.h: A derived MS column. //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef DERIVEDMSCAL_DERIVEDCOLUMN_H #define DERIVEDMSCAL_DERIVEDCOLUMN_H //# Includes #include #include #include #include namespace casacore { // Hourangle derived from TIME, etc. // class HourangleColumn : public VirtualScalarColumn { public: explicit HourangleColumn (MSCalEngine* engine, Int antnr) : itsEngine (engine), itsAntNr (antnr) {} virtual ~HourangleColumn(); virtual void get (rownr_t rowNr, Double& data); private: MSCalEngine* itsEngine; Int itsAntNr; //# -1=array 0=antenna1 1=antenna2 }; // Local sidereal time derived from TIME, etc. // class LASTColumn : public VirtualScalarColumn { public: explicit LASTColumn (MSCalEngine* engine, Int antnr) : itsEngine (engine), itsAntNr (antnr) {} virtual ~LASTColumn(); virtual void get (rownr_t rowNr, Double& data); private: MSCalEngine* itsEngine; Int itsAntNr; //# -1=array 0=antenna1 1=antenna2 }; // Parallactic angle derived from TIME, etc. // class ParAngleColumn : public VirtualScalarColumn { public: explicit ParAngleColumn (MSCalEngine* engine, Int antnr) : itsEngine (engine), itsAntNr (antnr) {} virtual ~ParAngleColumn(); virtual void get (rownr_t rowNr, Double& data); private: MSCalEngine* itsEngine; Int itsAntNr; //# 0=antenna1 1=antenna2 }; // Hourangle/declination derived from TIME, etc. // class HaDecColumn : public VirtualArrayColumn { public: explicit HaDecColumn (MSCalEngine* engine, Int antnr) : itsEngine (engine), itsAntNr (antnr) {} virtual ~HaDecColumn(); virtual IPosition shape (rownr_t rownr); virtual Bool isShapeDefined (rownr_t rownr); virtual void getArray (rownr_t rowNr, Array& data); private: MSCalEngine* itsEngine; Int itsAntNr; //# 0=antenna1 1=antenna2 }; // Azimuth/elevation derived from TIME, etc. // class AzElColumn : public VirtualArrayColumn { public: explicit AzElColumn (MSCalEngine* engine, Int antnr) : itsEngine (engine), itsAntNr (antnr) {} virtual ~AzElColumn(); virtual IPosition shape (rownr_t rownr); virtual Bool isShapeDefined (rownr_t rownr); virtual void getArray (rownr_t rowNr, Array& data); private: MSCalEngine* itsEngine; Int itsAntNr; //# 0=antenna1 1=antenna2 }; // Pointing ITRF coordinate derived from TIME, etc. // class ItrfColumn : public VirtualArrayColumn { public: explicit ItrfColumn (MSCalEngine* engine, Int antnr) : itsEngine (engine), itsAntNr (antnr) {} virtual ~ItrfColumn(); virtual IPosition shape (rownr_t rownr); virtual Bool isShapeDefined (rownr_t rownr); virtual void getArray (rownr_t rowNr, Array& data); private: MSCalEngine* itsEngine; Int itsAntNr; //# 0=antenna1 1=antenna2 }; // UVW J2000 derived from TIME, etc. // class UVWJ2000Column : public VirtualArrayColumn { public: explicit UVWJ2000Column (MSCalEngine* engine) : itsEngine (engine) {} virtual ~UVWJ2000Column(); virtual IPosition shape (rownr_t rownr); virtual Bool isShapeDefined (rownr_t rownr); virtual void getArray (rownr_t rowNr, Array& data); private: MSCalEngine* itsEngine; }; } //# end namespace #endif casacore-3.7.1/derivedmscal/DerivedMC/DerivedMSCal.cc000066400000000000000000000121351476623553700223430ustar00rootroot00000000000000//# DerivedMSCal.cc: Virtual column engine to return MS values //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { DerivedMSCal::DerivedMSCal() {} DerivedMSCal::DerivedMSCal (const Record&) {} DerivedMSCal::DerivedMSCal (const DerivedMSCal&) : VirtualColumnEngine(), itsEngine() {} DerivedMSCal::~DerivedMSCal() { for (uInt i=0; i #include #include namespace casacore { // // Virtual column engine to return derived MS values // // // // // //# Classes you should understand before using this one. //
      • The Table Data Managers concept as described in module file // Tables.h //
      • MeasurementSet // // // DerivedMSCal makes it possible to have virtual columns for the derived // MeasurementSet values hourangle, parallactic angle, azimuth/elevation, // and local sidereal time. In this way such derived values appear to be // ordinary columns with the exception that no values can be put into them. // // The following columns can be defined: //
          //
        • HA is the hourangle of the pointing from the array center (observatory position). //
        • HA1 is the hourangle of the pointing from ANTENNA1. //
        • HA2 is the hourangle of the pointing from ANTENNA2. //
        • HADEC is the hourangle/DEC of the pointing from the array center (observatory position). //
        • HADEC1 is the hourangle/DEC of the pointing from ANTENNA1. //
        • HADEC2 is the hourangle/DEC of the pointing from ANTENNA2. //
        • LAST is the local sidereal time of the pointing from the array center. //
        • LAST1 is the local sidereal time of the pointing from ANTENNA1. //
        • LAST2 is the local sidereal time of the pointing from ANTENNA2. //
        • PA1 is the parallactic angle of the pointing from ANTENNA1. //
        • PA2 is the parallactic angle of the pointing from ANTENNA2. //
        • AZEL is the azimuth/elevation of the pointing from the array center. //
        • AZEL1 is the azimuth/elevation of the pointing from ANTENNA1. //
        • AZEL2 is the azimuth/elevation of the pointing from ANTENNA2. //
        • ITRF is the pointing in (time-dependent) ITRF coordinates. //
        • UVW_J2000 is the UVW coordinates in J2000 (in meters). //
        // All columns have data type double and unit radian (except UVW). The HADEC, // AZEL, ITRF and UVW columns are array columnns while the others are // scalar columns. // // This engine is meant for a MeasurementSet, but can be used for any table // containing an ANTENNA and FIELD subtable and the relevant columns in the // main table (ANTENNA1 and/or ANTENNA2, FIELD_ID, and TIME). //
        In principle the array center is the Observatory position, which is // taken from the Measures Observatory table using the telescope name found // in the OBSERVATION subtable. However, if the subtable is not defined or // empty or if the telescope name is unknown, the position of the first antenna // is used as the array position. // // The engine can also be used for a CASA Calibration Table. It understands // how it references the MeasurementSets. Because calibration tables contain // no ANTENNA2 columns, columns XX2 are the same as XX1. //
        // // It makes it possible to use generic table software (like querying, // plotting, tablebrowser) on these values. // // // The following example shows how to add such columns to an MS and use // them thereafter. // // // Open the table for update (to be able to add the columns). // Table tab ("tDerivedMSCal_tmp.tab", Table::Update); // // Define the columns and add them using DerivedMSCal. // TableDesc td; // td.addColumn (ScalarColumnDesc("HA1")); // td.addColumn (ScalarColumnDesc("HA2")); // td.addColumn (ScalarColumnDesc("PA1")); // td.addColumn (ScalarColumnDesc("PA2")); // DerivedMSCal dataMan; // tab.addColumn (td, dataMan); // // Print values of all rows. // ScalarColumn ha1(tab, "HA1"); // ScalarColumn ha2(tab, "HA2"); // ScalarColumn pa1(tab, "PA1"); // ScalarColumn pa2(tab, "PA2"); // for (rownr_t row=0; row // // //
      • Take care of the feeds and their offsets. //
      • Have a conversion engine per field/antenna/feed? // class DerivedMSCal : public VirtualColumnEngine { public: // Create the data manager. DerivedMSCal(); // Create a Lofar storage manager with the given name. // The specifications are part of the record (as created by dataManagerSpec). explicit DerivedMSCal (const Record& spec); ~DerivedMSCal(); // Clone this object. virtual DataManager* clone() const; // Prepare the object. It sets the Table object in the engine. virtual void prepare(); // Get the type name of the data manager (i.e. DerivedMSCal). virtual String dataManagerType() const; // Record a record containing data manager specifications. virtual Record dataManagerSpec() const; // Columns can be added. virtual Bool canAddColumn() const; // Columns can be removed. virtual Bool canRemoveColumn() const; // Make the object from the type name string. // This function gets registered in the DataManager "constructor" map. // The caller has to delete the object. // The dataManName is not used. static DataManager* makeObject (const String& dataManName, const Record& spec); // Register the class name and the static makeObject "constructor". // This will make the engine known to the table system. static void registerClass(); private: // Copy constructor cannot be used. DerivedMSCal (const DerivedMSCal& that); // Assignment cannot be used. DerivedMSCal& operator= (const DerivedMSCal& that); // Do the final addition of a column. // It won't do anything. virtual void addColumn (DataManagerColumn*); // Remove a column from the data file. // It won't do anything. virtual void removeColumn (DataManagerColumn*); // Create a column in the storage manager on behalf of a table column. // The caller has to delete the newly created object. // // Create a scalar column. virtual DataManagerColumn* makeScalarColumn (const String& aName, int aDataType, const String& aDataTypeID); // Create an indirect array column. virtual DataManagerColumn* makeIndArrColumn (const String& aName, int aDataType, const String& aDataTypeID); // //# Declare member variables. MSCalEngine itsEngine; vector itsColumns; }; } //# end namespace #endif casacore-3.7.1/derivedmscal/DerivedMC/MSCalEngine.cc000066400000000000000000000417711476623553700221760ustar00rootroot00000000000000//# MSCalEngine.cc: Engine to calculate derived MS values //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { MSCalEngine::MSCalEngine() : itsLastCalInx (-1), itsReadFieldDir (True), itsDirColName ("PHASE_DIR") {} MSCalEngine::~MSCalEngine() {} void MSCalEngine::setTable (const Table& table) { // Set a new table. itsTable = table; // Clear everything, so it can be re-initialized. itsLastCalInx = -1; itsAntPos.clear(); itsMount.clear(); itsAntMB.clear(); if (itsReadFieldDir) { itsFieldDir.clear(); } itsCalIdMap.clear(); } double MSCalEngine::getHA (Int antnr, rownr_t rownr) { setData (antnr, rownr); return itsRADecToHADec().getValue().get()[0]; } void MSCalEngine::getHaDec (Int antnr, rownr_t rownr, Array& data) { setData (antnr, rownr); data = itsRADecToHADec().getValue().get(); } double MSCalEngine::getPA (Int antnr, rownr_t rownr) { Int mount = setData (antnr, rownr); if (mount == 1) { // Do the conversions using the machines. return itsRADecToAzEl().getValue().positionAngle (itsPoleToAzEl().getValue()); } return 0.; } double MSCalEngine::getLAST (Int antnr, rownr_t rownr) { setData (antnr, rownr); return itsUTCToLAST().getValue().get(); } void MSCalEngine::getAzEl (Int antnr, rownr_t rownr, Array& data) { setData (antnr, rownr); data = itsRADecToAzEl().getValue().get(); } void MSCalEngine::getItrf (Int antnr, rownr_t rownr, Array& data) { setData (antnr, rownr); data = itsRADecToItrf().getValue().get(); } void MSCalEngine::getNewUVW (Bool asApp, rownr_t rownr, Array& data) { setData (-1, rownr, True); Int ant1 = itsAntCol[0](rownr); Int ant2 = itsAntCol[1](rownr); if (ant1 == ant2) { data = 0.; } else { vector& antMB = itsAntMB[itsLastCalInx]; vector >& antUvw = itsAntUvw[itsLastCalInx]; Block& uvwFilled = itsUvwFilled[itsLastCalInx]; // Calculate UVW per antenna and subtract to get baseline. // Only calculate for an antenna if not done yet. Int ant = ant1; for (int i=0; i<2; ++i) { if (!uvwFilled[ant]) { itsBLToJ2000.setModel (antMB[ant]); MVBaseline bas = itsBLToJ2000().getValue(); MVuvw jvguvw(bas, itsLastDirJ2000.getValue()); if (asApp) { antUvw[ant] = Muvw::Convert(Muvw(jvguvw, Muvw::J2000), Muvw::Ref(Muvw::APP, itsFrame)) ().getValue().getVector(); } else { antUvw[ant] = Muvw(jvguvw, Muvw::J2000).getValue().getVector(); } uvwFilled[ant] = true; } ant = ant2; } // The UVW of the baseline is the difference of the antennae UVW. data = antUvw[ant2] - antUvw[ant1]; } } double MSCalEngine::getDelay (Int antnr, rownr_t rownr) { setData (-1, rownr, True); // Get the direction in ITRF xyz. Vector itrf = itsRADecToItrf().getValue().getValue(); Int ant1 = itsAntCol[0](rownr); Int ant2 = itsAntCol[1](rownr); AlwaysAssert (ant1 < Int(itsAntPos[itsLastCalInx].size()), AipsError); AlwaysAssert (ant2 < Int(itsAntPos[itsLastCalInx].size()), AipsError); // Get the antenna positions in ITRF xyz. const Vector& ap1 = itsAntPos[itsLastCalInx][ant1].getValue().getValue(); const Vector& ap2 = itsAntPos[itsLastCalInx][ant2].getValue().getValue(); // Delay (in meters) is inproduct (subtract array center position). double d1 = (itrf[0] * (ap1[0] - itsArrayItrf[0]) + itrf[1] * (ap1[1] - itsArrayItrf[1]) + itrf[2] * (ap1[2] - itsArrayItrf[2])); double d2 = (itrf[0] * (ap2[0] - itsArrayItrf[0]) + itrf[1] * (ap2[1] - itsArrayItrf[1]) + itrf[2] * (ap2[2] - itsArrayItrf[2])); if (antnr == 0) { return d1 / C::c; } else if (antnr == 1) { return d2 / C::c; } return (d1-d2) / C::c; } void MSCalEngine::setDirection (const MDirection& dir) { // Direction is explicitly given, so do not read from FIELD table. itsFieldDir.resize (1); itsFieldDir[0].resize (1); itsFieldDir[0][0] = dir; itsReadFieldDir = False; } void MSCalEngine::setDirColName (const String& colName) { itsDirColName = colName; itsReadFieldDir = True; } Int MSCalEngine::setData (Int antnr, rownr_t rownr, Bool fillAnt) { // Initialize if not done yet. if (itsLastCalInx < 0) { init(); } // Get the CAL_DESC_ID (if present). Int calInx = 0; Int calDescId = 0; if (! itsCalCol.isNull()) { calDescId = itsCalCol(rownr); // Update the CAL_DESC info if needed. if (calDescId >= Int(itsCalIdMap.size())) { fillCalDesc(); } // Map CAL_DESC_ID to the cal index. // Initialize other last ids if a new cal index. calInx = itsCalIdMap[calDescId]; if (calInx != itsLastCalInx) { itsLastFieldId = -1000; itsLastAntId = -1000; } } itsLastCalInx = calInx; // Get the array or antenna position and put into the measure frame. // Also get mount type (alt-az or other). Int mount = 0; if (antnr < 0) { // Set the frame's array position if needed. if (antnr != itsLastAntId) { itsFrame.resetPosition (itsArrayPos); itsLastAntId = antnr; } if (fillAnt && itsAntPos[calInx].empty()) { fillAntPos (calDescId, calInx); } } else { // Get the antenna id from the table. // Update the antenna positions if a higher antenna id is found. // In practice this will not happen, but it is possible that the ANTENNA // table was not fully filled yet. Int antId = itsAntCol[antnr](rownr); if (antId != itsLastAntId) { if (itsAntPos[calInx].empty()) { fillAntPos (calDescId, calInx); } AlwaysAssert (antId < Int(itsAntPos[calInx].size()), AipsError); itsFrame.resetPosition (itsAntPos[calInx][antId]); itsLastAntId = antId; } mount = itsMount[calInx][antId]; } // If needed, get the direction and put into the measure frame. // Get field id from the table; update the field positions if needed. Int fieldId = 0; if (itsReadFieldDir) { fieldId = itsFieldCol(rownr); } if (fieldId != itsLastFieldId) { if (fieldId >= Int(itsFieldDir[calInx].size())) { fillFieldDir (calDescId, calInx); } AlwaysAssert (fieldId < Int(itsFieldDir[calInx].size()), AipsError); const MDirection& dir = itsFieldDir[calInx][fieldId]; itsDirToJ2000.setModel (dir); // We can already convert the direction to J2000 if it is not a model // (thus not time dependent). // Otherwise force the time to change, so the J2000 is calculated there. if (dir.isModel()) { itsLastTime = -1e30; } else { itsLastDirJ2000 = itsDirToJ2000(); itsRADecToAzEl.setModel (itsLastDirJ2000); itsRADecToItrf.setModel (itsLastDirJ2000); itsRADecToHADec.setModel(itsLastDirJ2000); itsFrame.resetDirection (itsLastDirJ2000); } /// or better set above models to dir??? Ask Wim. ***** itsLastFieldId = fieldId; } // Set the epoch in the measure frame. Double time = itsTimeCol(rownr); if (time != itsLastTime) { MEpoch epoch = itsTimeMeasCol(rownr); itsFrame.resetEpoch (epoch); if (itsFieldDir[calInx][fieldId].isModel()) { itsLastDirJ2000 = itsDirToJ2000(); itsRADecToAzEl.setModel (itsLastDirJ2000); itsRADecToItrf.setModel (itsLastDirJ2000); itsRADecToHADec.setModel(itsLastDirJ2000); itsFrame.resetDirection (itsLastDirJ2000); } itsUTCToLAST.setModel (epoch); itsLastTime = time; itsUvwFilled[calInx] = False; } return mount; } void MSCalEngine::init() { const TableDesc& td = itsTable.tableDesc(); itsLastFieldId = -1000; itsLastAntId = -1000; itsLastTime = -1e30; itsAntCol[0].attach (itsTable, "ANTENNA1"); if (td.isColumn("ANTENNA2")) { itsAntCol[1].attach (itsTable, "ANTENNA2"); } else { itsAntCol[1].attach (itsTable, "ANTENNA1"); } if (td.isColumn("FEED1")) { itsFeedCol[0].attach (itsTable, "FEED1"); if (td.isColumn("FEED2")) { itsFeedCol[1].attach (itsTable, "FEED2"); } else { itsFeedCol[1].attach (itsTable, "FEED1"); } } itsFieldCol.attach (itsTable, "FIELD_ID"); itsTimeCol.attach (itsTable, "TIME"); itsTimeMeasCol.attach (itsTable, "TIME"); Table obsTab; if (td.isColumn("CAL_DESC_ID")) { itsCalCol.attach (itsTable, "CAL_DESC_ID"); // Fill CAl_DESC info from calibration table. fillCalDesc(); obsTab = getSubTable (0, "OBSERVATION", False); } else { // Nothing special, so simply initialize. itsAntPos.resize (1); itsMount.resize (1); itsAntMB.resize (1); itsAntUvw.resize (1); itsUvwFilled.resize(1); if (itsReadFieldDir) { itsFieldDir.resize (1); } itsCalIdMap = vector(1,0); if (itsTable.keywordSet().isDefined("OBSERVATION")) { obsTab = itsTable.keywordSet().asTable("OBSERVATION"); } } // Fill the antenna positions of the first CAL_DESC_ID. fillAntPos (0, 0); // Find observatory position. // Get it from the OBSERVATION subtable; otherwise try keyword TELESCOPE_NAME. // If not found, set it to the position of the middle antenna. Bool fndObs = False; if (! obsTab.isNull()) { if (obsTab.nrow() > 0) { String telescope = ScalarColumn(obsTab, "TELESCOPE_NAME")(0); fndObs = MeasTable::Observatory (itsArrayPos, telescope); } } if (!fndObs && itsTable.keywordSet().isDefined("TELESCOPE_NAME")) { String telescope = itsTable.keywordSet().asString("TELESCOPE_NAME"); fndObs = MeasTable::Observatory (itsArrayPos, telescope); } if (!fndObs && itsAntPos.size() > 0) { uInt nant = itsAntPos[0].size(); if (nant > 0) { itsArrayPos = itsAntPos[0][nant/2]; fndObs = True; } } AlwaysAssert (fndObs, AipsError); // Convert the antenna position to ITRF (for delay calculations). MPosition itrfPos = MPosition::Convert (itsArrayPos, MPosition::ITRF)(); itsArrayItrf = itrfPos.getValue().getValue(); // Initialize the converters. // Set up the frame for epoch and antenna position. itsFrame.set (MEpoch(), MPosition(), MDirection()); // Make the HADec pole as expressed in HADec. The pole is the default. MDirection::Ref rHADec(MDirection::HADEC, itsFrame); MDirection mHADecPole; mHADecPole.set (rHADec); itsPoleToAzEl.set (mHADecPole, MDirection::Ref(MDirection::AZEL,itsFrame)); // Set up the machine to convert RaDec to AzEl. itsRADecToAzEl.set (MDirection(), MDirection::Ref(MDirection::AZEL,itsFrame)); // Idem RaDec to ITRF. itsRADecToItrf.set (MDirection(), MDirection::Ref(MDirection::ITRF,itsFrame)); // Idem RaDec to HaDec. itsRADecToHADec.set (MDirection(), rHADec); // Idem direction to J2000. itsDirToJ2000.set (MDirection(), MDirection::Ref(MDirection::J2000,itsFrame)); // Idem UTC to LAST. itsUTCToLAST.set (MEpoch(), MEpoch::Ref(MEpoch::LAST,itsFrame)); // Idem MBaseline ITRF to J2000. itsBLToJ2000.set (MBaseline(), MBaseline::Ref(MBaseline::J2000,itsFrame)); } void MSCalEngine::fillAntPos (Int calDescId, Int calInx) { Table tab; if (itsCalCol.isNull()) { tab = itsTable.keywordSet().asTable("ANTENNA"); } else { tab = getSubTable (calDescId, "ANTENNA"); } ScalarMeasColumn posCol(tab, "POSITION"); ScalarColumn mountCol(tab, "MOUNT"); vector& antPos = itsAntPos[calInx]; vector& mounts = itsMount[calInx]; vector& antMB = itsAntMB[calInx]; vector >& antUvw = itsAntUvw[calInx]; Block& uvwFilled = itsUvwFilled[calInx]; antPos.reserve (tab.nrow()); mounts.reserve (tab.nrow()); antMB.reserve (tab.nrow()); for (rownr_t i=0; i= 6 && mount(0,6) == "alt-az") { mountType = 1; } mounts.push_back (mountType); antPos.push_back (MPosition::Convert (posCol(i), MPosition::ITRF)()); // Form an MBaseline per antenna (use first antenna as baseline origin). Vector pos = antPos[i].getValue().getVector(); Vector pos0 = antPos[0].getValue().getVector(); MVPosition mvpos((pos[0] - pos0[0]), (pos[1] - pos0[1]), (pos[2] - pos0[2])); antMB.push_back (MBaseline (MVBaseline(mvpos), MBaseline::ITRF)); } antUvw.resize (antPos.size()); uvwFilled.resize (antPos.size()); uvwFilled = False; } void MSCalEngine::fillFieldDir (Int calDescId, Int calInx) { // If direction is explicitly given, copy from the first one. if (!itsReadFieldDir) { if (calInx > 0) { itsFieldDir[calInx] = itsFieldDir[0]; } } else { // Read the directions from the FIELD subtable. Table tab; if (itsCalCol.isNull()) { tab = itsTable.keywordSet().asTable("FIELD"); } else { tab = getSubTable (calDescId, "FIELD"); } ArrayMeasColumn dirCol(tab, itsDirColName); vector& fieldDir = itsFieldDir[calInx]; fieldDir.reserve (tab.nrow()); for (rownr_t i=fieldDir.size(); i nameCol(tab, "MS_NAME"); // Handle CAL_DESC_IDs not seen so far. itsCalIdMap.reserve (tab.nrow()); for (rownr_t i=itsCalIdMap.size(); i::iterator iter = itsCalMap.find (msName); if (iter == itsCalMap.end()) { // New MS name, so add it. itsCalMap[msName] = inx; } else { inx = iter->second; } // Map this calDescId to inx. itsCalIdMap.push_back (inx); } // Resize others in case new entries added. itsAntPos.resize (itsCalMap.size()); itsMount.resize (itsCalMap.size()); itsAntMB.resize (itsCalMap.size()); itsAntUvw.resize (itsCalMap.size()); itsUvwFilled.resize(itsCalMap.size()); itsFieldDir.resize (itsCalMap.size()); } Table MSCalEngine::getSubTable (Int calDescId, const String& subTabName, Bool mustExist) { // If defined, open a subtable in the MS referred to by the name in the // MS_NAME column of the CAL_DESC subtable. Table calDescTab (itsTable.keywordSet().asTable("CAL_DESC")); ScalarColumn nameCol(calDescTab, "MS_NAME"); // If the path is relative, use the CalTable's directory. String msName = nameCol(calDescId); if (msName.empty()) { throw DataManError ("MSCalEngine: no MS name given in CAL_DESC table"); } if (msName[0] != '/') { msName = Path(itsTable.tableName()).dirName() + '/' + msName; } Table ms(msName); if (ms.keywordSet().isDefined (subTabName)) { return ms.keywordSet().asTable(subTabName); } if (mustExist) { throw DataManError ("MSCalEngine: subtable " + subTabName + " in CalTable's MS " + ms.tableName() + " does not exist"); } return Table(); } } //# end namespace casacore-3.7.1/derivedmscal/DerivedMC/MSCalEngine.h000066400000000000000000000226621476623553700220360ustar00rootroot00000000000000//# MSCalEngine.h: Engine to calculate derived MS values //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef DERIVEDMSCAL_MSCALENGINE_H #define DERIVEDMSCAL_MSCALENGINE_H //# Includes #include #include #include #include #include #include #include #include #include #include #include namespace casacore { // // Engine to calculate derived MS values // // // // // //# Classes you should understand before using this one. //
      • MeasurementSet // // // MSCalEngine is a class used to calculate derived MeasurementSet // values hourangle, parallactic angle, azimuth/elevation, // local sidereal time, and UVW coordinates. // It is used by the DerivedMSCal virtual columns and UDFs, but can // be used by other software as well. // // The following values can be obtained: //
          //
        • HA is the hourangle of the array center (observatory position). //
        • HA1 is the hourangle of ANTENNA1. //
        • HA2 is the hourangle of ANTENNA2. //
        • HADEC is the hourangle/DEC of the array center (observatory position). //
        • HADEC1 is the hourangle/DEC of ANTENNA1. //
        • HADEC2 is the hourangle/DEC of ANTENNA2. //
        • LAST is the local sidereal time of the array center. //
        • LAST1 is the local sidereal time of ANTENNA1. //
        • LAST2 is the local sidereal time of ANTENNA2. //
        • PA1 is the parallactic angle of ANTENNA1. //
        • PA2 is the parallactic angle of ANTENNA2. //
        • AZEL is the azimuth/elevation of the array center. //
        • AZEL1 is the azimuth/elevation of ANTENNA1. //
        • AZEL2 is the azimuth/elevation of ANTENNA2. //
        • ITRF is the direction in (time-dependent) ITRF coordinates. //
        • UVW_J2000 is the UVW coordinates in J2000 (in meters). //
        // All values have data type double and unit radian (except UVW). The HADEC, // AZEL, ITRF and UVW cvalues are arrays while the others are scalars. // // This engine is meant for a MeasurementSet, but can be used for any table // containing an ANTENNA and FIELD subtable and the relevant columns in the // main table (ANTENNA1 and/or ANTENNA2, FIELD_ID, and TIME). // It also looks if columns FEED1 and/or FEED2 exist. They are not used yet, // but might be in the future for support of multi-feed arrays. //
        In principle the array center is the Observatory position, which is // taken from the Measures Observatory table using the telescope name found // in the OBSERVATION subtable or in the table keyword TELESCOPE_NAME. // However, if the telescope name cannot be found or is unknown, the position // of the middle antenna is used as the array position. // // The new CASA Calibration Table format obeys the rules mentioned above, // so these tables are fully supported. Note they do not contain an // OBSERVATION subtable, but use keyword TELESCOPE_NAME. // // The engine can also be used for old CASA Calibration Tables. It understands // how they reference the MeasurementSets. Because these calibration tables // contain no ANTENNA2 columns, columns XX2 are the same as XX1. //
        // // Factor out common code. // // //
      • Take care of the feeds and their offsets. //
      • Have a conversion engine per field/antenna/feed? // class MSCalEngine { public: // Default constructor. MSCalEngine(); // Destructor. ~MSCalEngine(); // Get the table used. Table getTable() const { return itsTable; } // Use the given table (MS or CalTable) in the engine. void setTable (const Table&); // Set the direction to be used instead of a direction from the FIELD table. void setDirection (const MDirection&); // Set the direction column name to use in the FIELD table. void setDirColName (const String& colName); // Get the hourangle for the given row. double getHA (Int antnr, rownr_t rownr); // Get the hourangle/DEC for the given row. void getHaDec (Int antnr, rownr_t rownr, Array&); // Get the parallatic angle for the given row. double getPA (Int antnr, rownr_t rownr); // Get the local sidereal time for the given row. double getLAST (Int antnr, rownr_t rownr); // Get the azimuth/elevation for the given row. void getAzEl (Int antnr, rownr_t rownr, Array&); // Get the ITRF coordinates for the given row. void getItrf (Int antnr, rownr_t rownr, Array&); // Get the UVW in J2000 or APP for the given row. void getNewUVW (Bool asApp, rownr_t rownr, Array&); // Get the delay for the given row. double getDelay (Int antnr, rownr_t rownr); private: // Copy constructor cannot be used. MSCalEngine (const MSCalEngine& that); // Assignment cannot be used. MSCalEngine& operator= (const MSCalEngine& that); // Set the data in the measure converter machines. // The antenna positions are only filled in antnr>=0 or if fillAnt is set. // It returns the mount of the antenna. Int setData (Int antnr, rownr_t rownr, Bool fillAnt=False); // Initialize the column objects, etc. void init(); // Fill the CalDesc info for calibration tables. void fillCalDesc(); // Fill or update the antenna positions from the ANTENNA subtable at // row calDescId. It is stored in the calInx-th entry of itsAntPos/itsMount. void fillAntPos (Int calDescId, Int calInx); // Fill or update the field directions from the FIELD subtable at // row calDescId. It is stored in the calInx-th entry of itsFieldDir. void fillFieldDir (Int calDescId, Int calInx); // Get a calibration MS subtable for the given id. Table getSubTable (Int calDescId, const String& subTabName, Bool mustExist=True); //# Declare member variables. Table itsTable; //# MS or CalTable to use Int itsLastCalInx; //# id of CAL_DESC last used Int itsLastFieldId; //# id of the field last used Int itsLastAntId; //# -1 is array position used Double itsLastTime; ScalarColumn itsAntCol[2]; //# ANTENNA1 and ANTENNA2 ScalarColumn itsFeedCol[2]; //# FEED1 and FEED2 ScalarColumn itsFieldCol; //# FIELD_ID ScalarColumn itsTimeCol; //# TIME ScalarMeasColumn itsTimeMeasCol; //# TIME as Measure ScalarColumn itsCalCol; //# CAL_DESC_ID map itsCalMap; //# map of MS name to index vector itsCalIdMap; //# map of calId to index MPosition itsArrayPos; Vector itsArrayItrf; //# ITRF array position vector > itsAntPos; //# ITRF antenna positions vector > itsMount; //# 1=alt-az 0=else vector > itsFieldDir; //# J2000 field directions Bool itsReadFieldDir; //# False: explicit directions String itsDirColName; //# FIELD DIR column to read vector > itsAntMB; //# J2000 MBaseline per antenna vector > > itsAntUvw; //# J2000 UVW per antenna vector > itsUvwFilled; //# is UVW filled for antenna i? MDirection::Convert itsRADecToAzEl; //# converter ra/dec to az/el MDirection::Convert itsPoleToAzEl; //# converter pole to az/el MDirection::Convert itsRADecToHADec; //# converter ra/dec to ha/dec MDirection::Convert itsRADecToItrf; //# converter ra/dec to itrf MDirection::Convert itsDirToJ2000; //# converter direction to J2000 MEpoch::Convert itsUTCToLAST; //# converter UTC to LAST MBaseline::Convert itsBLToJ2000; //# convert ITRF to J2000 MeasFrame itsFrame; //# frame used by the converters MDirection itsLastDirJ2000; //# itsLastFieldId dir in J2000 }; } //# end namespace #endif casacore-3.7.1/derivedmscal/DerivedMC/Register.cc000066400000000000000000000340721476623553700216710ustar00rootroot00000000000000//# Register.cc: Register virtual column engine to return derived MS values //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include using namespace casacore; using namespace std; void register_derivedmscal() { // Register the table virtual column engine. DerivedMSCal::registerClass(); // Register the TaQL UDFs. // Derived quantities. UDFBase::registerUDF ("derivedmscal.HELP", HelpMsCalUDF::makeHELP); UDFBase::registerUDF ("derivedmscal.HA", UDFMSCal::makeHA); UDFBase::registerUDF ("derivedmscal.HA1", UDFMSCal::makeHA1); UDFBase::registerUDF ("derivedmscal.HA2", UDFMSCal::makeHA2); UDFBase::registerUDF ("derivedmscal.HADEC", UDFMSCal::makeHADEC); UDFBase::registerUDF ("derivedmscal.HADEC1", UDFMSCal::makeHADEC1); UDFBase::registerUDF ("derivedmscal.HADEC2", UDFMSCal::makeHADEC2); UDFBase::registerUDF ("derivedmscal.PA1", UDFMSCal::makePA1); UDFBase::registerUDF ("derivedmscal.PA2", UDFMSCal::makePA2); UDFBase::registerUDF ("derivedmscal.LAST", UDFMSCal::makeLAST); UDFBase::registerUDF ("derivedmscal.LAST1", UDFMSCal::makeLAST1); UDFBase::registerUDF ("derivedmscal.LAST2", UDFMSCal::makeLAST2); UDFBase::registerUDF ("derivedmscal.AZEL", UDFMSCal::makeAZEL); UDFBase::registerUDF ("derivedmscal.AZEL1", UDFMSCal::makeAZEL1); UDFBase::registerUDF ("derivedmscal.AZEL2", UDFMSCal::makeAZEL2); UDFBase::registerUDF ("derivedmscal.ITRF", UDFMSCal::makeITRF); UDFBase::registerUDF ("derivedmscal.UVWWVL", UDFMSCal::makeUvwWvl); UDFBase::registerUDF ("derivedmscal.UVWWVLS", UDFMSCal::makeUvwWvls); UDFBase::registerUDF ("derivedmscal.UVWJ2000", UDFMSCal::makeUvwJ2000); UDFBase::registerUDF ("derivedmscal.UVWJ2000WVL", UDFMSCal::makeWvlJ2000); UDFBase::registerUDF ("derivedmscal.UVWJ2000WVLS",UDFMSCal::makeWvlsJ2000); UDFBase::registerUDF ("derivedmscal.UVWAPP", UDFMSCal::makeUvwAPP); UDFBase::registerUDF ("derivedmscal.UVWAPPWVL", UDFMSCal::makeWvlAPP); UDFBase::registerUDF ("derivedmscal.UVWAPPWVLS",UDFMSCal::makeWvlsAPP); UDFBase::registerUDF ("derivedmscal.DELAY1", UDFMSCal::makeDelay1); UDFBase::registerUDF ("derivedmscal.DELAY2", UDFMSCal::makeDelay2); UDFBase::registerUDF ("derivedmscal.DELAY", UDFMSCal::makeDelay); UDFBase::registerUDF ("derivedmscal.STOKES", UDFMSCal::makeStokes); // CASA selection. UDFBase::registerUDF ("derivedmscal.BASELINE", UDFMSCal::makeBaseline); UDFBase::registerUDF ("derivedmscal.TIME", UDFMSCal::makeTime); UDFBase::registerUDF ("derivedmscal.SPW", UDFMSCal::makeSpw); UDFBase::registerUDF ("derivedmscal.UVDIST", UDFMSCal::makeUVDist); UDFBase::registerUDF ("derivedmscal.FIELD", UDFMSCal::makeField); UDFBase::registerUDF ("derivedmscal.ARRAY", UDFMSCal::makeArray); UDFBase::registerUDF ("derivedmscal.SCAN", UDFMSCal::makeScan); UDFBase::registerUDF ("derivedmscal.STATE", UDFMSCal::makeState); UDFBase::registerUDF ("derivedmscal.OBS", UDFMSCal::makeObs); // Data from subtables. UDFBase::registerUDF ("derivedmscal.ANT1NAME", UDFMSCal::makeAnt1Name); UDFBase::registerUDF ("derivedmscal.ANT2NAME", UDFMSCal::makeAnt2Name); UDFBase::registerUDF ("derivedmscal.ANT1COL", UDFMSCal::makeAnt1Col); UDFBase::registerUDF ("derivedmscal.ANT2COL", UDFMSCal::makeAnt2Col); UDFBase::registerUDF ("derivedmscal.STATECOL", UDFMSCal::makeStateCol); UDFBase::registerUDF ("derivedmscal.OBSCOL", UDFMSCal::makeObsCol); UDFBase::registerUDF ("derivedmscal.SPWCOL", UDFMSCal::makeSpwCol); UDFBase::registerUDF ("derivedmscal.POLCOL", UDFMSCal::makePolCol); UDFBase::registerUDF ("derivedmscal.FIELDCOL", UDFMSCal::makeFieldCol); UDFBase::registerUDF ("derivedmscal.PROCCOL", UDFMSCal::makeProcCol); UDFBase::registerUDF ("derivedmscal.SUBCOL", UDFMSCal::makeSubCol); } namespace casacore { void HelpMsCalUDF::showFuncsDerived (ostream& os) { os << "Derived direction coordinates functions" << endl; os << " A direction parameter can be given to the functions." << endl; os << " It can be the name of a FIELD subtable column (e.g., 'DELAY_DIR')," << endl; os << " the name of a source (e.g., 'SUN'), or a RA,DEC pair defining" << endl; os << " the J2000 source direction (e.g., [10h42m31.3, 45d51m16])." << endl; os << " If no direction argument is given, column DELAY_DIR is used for the" << endl; os << " delay functions, otherwise column PHASE_DIR." << endl; os << " double MSCAL.HA() " " hourangle of array center" << endl; os << " double MSCAL.HA1() " " hourangle of ANTENNA1" << endl; os << " double MSCAL.HA2() " " hourangle of ANTENNA2" << endl; os << " doublearray MSCAL.HADEC() " " hourangle/declination of array center" << endl; os << " doublearray MSCAL.HADEC1() " " hourangle/declination of ANTENNA1" << endl; os << " doublearray MSCAL.HADEC2() " " hourangle/declination of ANTENNA2" << endl; os << " doublearray MSCAL.AZEL() " " azimuth/elevation of array center" << endl; os << " doublearray MSCAL.AZEL1() " " azimuth/elevation of ANTENNA1" << endl; os << " doublearray MSCAL.AZEL2() " " azimuth/elevation of ANTENNA2" << endl; os << " doublearray MSCAL.ITRF() " " direction in ITRF coordinates" << endl; os << " double MSCAL.LAST() " " local sidereal time of array center" << endl; os << " double MSCAL.LAST1() " " local sidereal time of ANTENNA1" << endl; os << " double MSCAL.LAST2() " " local sidereal time of ANTENNA2" << endl; os << " double MSCAL.PA1() " " parallactic angle of ANTENNA1" << endl; os << " double MSCAL.PA2() " " parallactic angle of ANTENNA2" << endl; os << " doublearray MSCAL.UVWWVL() " " stored UVW coordinates in wvl for reffreq" << endl; os << " doublearray MSCAL.UVWWVLS() " " stored UVW coordinates in wvl per channel" << endl; os << " doublearray MSCAL.UVWJ2000() " " calc J2000 UVW coordinates in meters" << endl; os << " doublearray MSCAL.UVWJ2000WVL() " " calc J2000 UVW coordinates in wvl for reffreq" << endl; os << " doublearray MSCAL.UVWJ2000WVLS()" " calc J2000 UVW coordinates in wvl per channel" << endl; os << " doublearray MSCAL.UVWAPP() " " calc Apparent UVW coordinates in meters" << endl; os << " doublearray MSCAL.UVWAPPWVL() " " calc Apparent UVW coordinates in wvl for reffreq" << endl; os << " doublearray MSCAL.UVWAPPWVLS()" " calc Apparent UVW coordinates in wvl per channel" << endl; os << " double MSCAL.DELAY1() " " calc delay (seconds) of ANTENNA1 w.r.t. array center" << endl; os << " double MSCAL.DELAY2() " " calc delay (seconds) of ANTENNA2 w.r.t. array center" << endl; os << " double MSCAL.DELAY1() " " calc delay (seconds) of ANTENNA1 w.r.t. ANTENNA2" << endl; } void HelpMsCalUDF::showFuncsStokes (ostream& os, Bool showStokes) { os << "Stokes conversion functions:" << endl; os << " complexarray MSCAL.STOKES(complexarray, string) " " convert the data" << endl; os << " doublearray MSCAL.STOKES(doublearray, string) " " combine the weights" << endl; os << " boolarray MSCAL.STOKES(boolarray, string) " " combine the flags" << endl; if (showStokes) { os << endl; os << "The case-insensitive string argument defines the output Stokes axes." << endl; os << "It must be a comma separated list of Stokes names. All values" << endl; os<< "defined in the Casacore class Stokes are possible. Most important are:" << endl; os << " XX, XY, YX, and/or YY. LINEAR or LIN means XX,XY,YX,YY." << endl; os << " RR, RL, LR, and/or LL. CIRCULAR or CIRC means RR,RL,LR,LL." << endl; os << " I, Q, U, and/or V. IQUV or STOKES means I,Q,U,V." << endl; os << " PTOTAL is the polarized intensity (sqrt(Q**2+U**2+V**2))" << endl; os << " PLINEAR is the linearly polarized intensity (sqrt(Q**2+U**2))" << endl; os << " PFTOTAL is the polarization fraction (Ptotal/I)" << endl; os << " PFLINEAR is the linear polarization fraction (Plinear/I)" << endl; os << " PANGLE is the linear polarization angle (0.5*arctan(U/Q)) (in radians)" << endl; os << "If not given, the string argument defaults to 'IQUV'." << endl; } } void HelpMsCalUDF::showFuncsSelection (ostream& os) { os << "CASA selection functions:" << endl; os << " bool MSCAL.BASELINE (string) " " select using a baseline string" << endl; os << " bool MSCAL.TIME (string) " " select using a time string" << endl; os << " bool MSCAL.FIELD (string) " " select using a field string" << endl; os << " bool MSCAL.FEED (string) " " select using a feed string" << endl; os << " bool MSCAL.SCAN (string) " " select using a scan string" << endl; os << " bool MSCAL.SPW (string) " " select using a spectral-window string" << endl; os << " bool MSCAL.UVDIST (string) " " select using a uv-distance string" << endl; os << " bool MSCAL.STATE (string) " " select using a state string" << endl; os << " bool MSCAL.OBS (string) " " select using an observation string" << endl; os << " bool MSCAL.ARRAY (string) " " select using an array string" << endl; os << " More information about the CASA selection syntax can be found at" << endl << " http://casacore.github.io/casacore-notes/263.html" << endl; } void HelpMsCalUDF::showFuncsSubtable (ostream& os) { os << "Subtable information functions:" << endl; os << " MSCAL.ANT1NAME() " " the name of ANTENNA1" << endl; os << " MSCAL.ANT2NAME() " " the name of ANTENNA2" << endl; os << " MSCAL.ANT1COL('column') " " for ANTENNA1 the value in given column in ANTENNA subtable" << endl; os << " MSCAL.ANT2COL('column') " " for ANTENNA2 the value in given column in ANTENNA subtable" << endl; os << " MSCAL.STATECOL('column') " " for STATE_ID the value in given column in STATE subtable" << endl; os << " MSCAL.OBSCOL('column') " " for OBSERVATION_ID the value in given column in OBSERVATION subtable" << endl; os << " MSCAL.SPWCOL('column') " " for DATA_DESC_ID the value in given column in SPECTRAL_WINDOW subtable" << endl; os << " MSCAL.POLCOL('column') " " for DATA_DESC_ID the value in given column in POLARIZATION subtable" << endl; os << " MSCAL.FIELDCOL('column') " " for FIELD_ID the value in given column in FIELD subtable" << endl; os << " MSCAL.PROCCOL('column') " " for PROCESSOR_ID the value in given column in PROCESSOR subtable" << endl; os << " MSCAL.SUBCOL('subtable', 'column', 'idcolumn') " << endl << " for the id-column the value in given column in given subtable" << endl; } UDFBase* HelpMsCalUDF::makeHELP (const String&) { return new HelpMsCalUDF(); } void HelpMsCalUDF::setup (const Table&, const TaQLStyle&) { AlwaysAssert (operands().size() <= 1, AipsError); if (operands().size() == 1) { AlwaysAssert (operands()[0]->dataType() == TableExprNodeRep::NTString && operands()[0]->valueType() == TableExprNodeRep::VTScalar, AipsError); } // Set datatype, shape, unit, etc. setDataType (TableExprNodeRep::NTString); setNDim (0); // scalar setConstant (True); } String HelpMsCalUDF::getString (const TableExprId& id) { ostringstream os; String type; if (operands().size() == 1) { type = operands()[0]->getString(id); type.downcase(); } if (type.empty()) { showFuncsDerived (os); os << endl; showFuncsStokes (os, False); os << endl; showFuncsSelection (os); os << endl; showFuncsSubtable (os); os << endl << "Use 'show function mscal for more information" << endl; } else if (type == "derived") { showFuncsDerived (os); } else if (type == "stokes") { showFuncsStokes (os, True); } else if (type == "selection") { showFuncsSelection (os); } else if (type == "subtable" || type == "subtables") { showFuncsSubtable (os); } if (os.str().empty()) { os << type << " is an unknown mscal subtype; use derived, stokes, selection or subtable" << endl; } else { os << endl << "See also section 'Special MeasurementSet functions' at" << endl << "http://casacore.github.io/casacore-notes/199.html" << endl; } return os.str(); } } // end namespace casacore-3.7.1/derivedmscal/DerivedMC/Register.h000066400000000000000000000044531476623553700215330ustar00rootroot00000000000000//# Register.h: Register virtual column engine to return derived MS values //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef DERIVEDMSCAL_REGISTER_H #define DERIVEDMSCAL_REGISTER_H #include #include #include // // This function registers the DerivedMSCal virtual column engine and // the UDFMSCal UDFs. // It is called when the dynamic library derivedmscal.so/dylib is loaded. extern "C" { void register_derivedmscal(); } // namespace casacore { // // General meas function to show the available functions. // class HelpMsCalUDF: public UDFBase { public: // Function to create an object. static UDFBase* makeHELP (const String&); // Setup the object. virtual void setup (const Table&, const TaQLStyle&); // Get the value. virtual String getString (const TableExprId& id); // Show the possible functions. static void showFuncsDerived (std::ostream&); static void showFuncsStokes (std::ostream&, Bool showStokes); static void showFuncsSelection (std::ostream&); static void showFuncsSubtable (std::ostream&); }; } #endif casacore-3.7.1/derivedmscal/DerivedMC/UDFMSCal.cc000066400000000000000000001030671476623553700214040ustar00rootroot00000000000000//# UDFMSCal.cc: TaQL UDF to calculate derived MS values //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { UDFMSCal::UDFMSCal (ColType type, Int arg) : itsType (type), itsArg (arg) { if (itsType == DELAY) { // Default column to use for delays. itsEngine.setDirColName ("DELAY_DIR"); } } UDFMSCal::UDFMSCal (const String& funcName) : itsType (GETVALUE), itsArg (0), itsFuncName (funcName) {} UDFMSCal::UDFMSCal (const String& funcName, const String& subtabName, const String& idcolName, Int arg) : itsType (GETVALUE), itsArg (arg), itsFuncName (funcName), itsSubTabName (subtabName), itsIdColName (idcolName) {} UDFMSCal::UDFMSCal (const String& funcName, const String& subtabName, const String& idcolName, const String& subcolName) : itsType (GETVALUE), itsArg (0), itsFuncName (funcName), itsSubTabName (subtabName), itsIdColName (idcolName), itsSubColName (subcolName) {} UDFBase* UDFMSCal::makeHA (const String&) { return new UDFMSCal (HA, -1); } UDFBase* UDFMSCal::makeHA1 (const String&) { return new UDFMSCal (HA, 0); } UDFBase* UDFMSCal::makeHA2 (const String&) { return new UDFMSCal (HA, 1); } UDFBase* UDFMSCal::makeHADEC (const String&) { return new UDFMSCal (HADEC, -1); } UDFBase* UDFMSCal::makeHADEC1 (const String&) { return new UDFMSCal (HADEC, 0); } UDFBase* UDFMSCal::makeHADEC2 (const String&) { return new UDFMSCal (HADEC, 1); } UDFBase* UDFMSCal::makePA1 (const String&) { return new UDFMSCal (PA, 0); } UDFBase* UDFMSCal::makePA2 (const String&) { return new UDFMSCal (PA, 1); } UDFBase* UDFMSCal::makeLAST (const String&) { return new UDFMSCal (LAST, -1); } UDFBase* UDFMSCal::makeLAST1 (const String&) { return new UDFMSCal (LAST, 0); } UDFBase* UDFMSCal::makeLAST2 (const String&) { return new UDFMSCal (LAST, 1); } UDFBase* UDFMSCal::makeAZEL (const String&) { return new UDFMSCal (AZEL, -1); } UDFBase* UDFMSCal::makeAZEL1 (const String&) { return new UDFMSCal (AZEL, 0); } UDFBase* UDFMSCal::makeAZEL2 (const String&) { return new UDFMSCal (AZEL, 1); } UDFBase* UDFMSCal::makeITRF (const String&) { return new UDFMSCal (ITRF, -1); } UDFBase* UDFMSCal::makeUvwWvl (const String&) { return new UDFMSCal (UVWWVL, -1); } UDFBase* UDFMSCal::makeUvwWvls (const String&) { return new UDFMSCal (UVWWVLS, -1); } UDFBase* UDFMSCal::makeUvwJ2000 (const String&) { return new UDFMSCal (NEWUVW, 0); } UDFBase* UDFMSCal::makeWvlJ2000 (const String&) { return new UDFMSCal (NEWUVWWVL, 0); } UDFBase* UDFMSCal::makeWvlsJ2000 (const String&) { return new UDFMSCal (NEWUVWWVLS, 0); } UDFBase* UDFMSCal::makeUvwAPP (const String&) { return new UDFMSCal (NEWUVW, 1); } UDFBase* UDFMSCal::makeWvlAPP (const String&) { return new UDFMSCal (NEWUVWWVL, 1); } UDFBase* UDFMSCal::makeWvlsAPP (const String&) { return new UDFMSCal (NEWUVWWVLS, 1); } UDFBase* UDFMSCal::makeDelay (const String&) { return new UDFMSCal (DELAY, -1); } UDFBase* UDFMSCal::makeDelay1 (const String&) { return new UDFMSCal (DELAY, 0); } UDFBase* UDFMSCal::makeDelay2 (const String&) { return new UDFMSCal (DELAY, 1); } UDFBase* UDFMSCal::makeStokes (const String&) { return new UDFMSCal (STOKES, -1); } UDFBase* UDFMSCal::makeBaseline (const String&) { return new UDFMSCal (SELECTION, BASELINE); } UDFBase* UDFMSCal::makeCorr (const String&) { return new UDFMSCal (SELECTION, CORR); } UDFBase* UDFMSCal::makeTime (const String&) { return new UDFMSCal (SELECTION, TIME); } UDFBase* UDFMSCal::makeUVDist (const String&) { return new UDFMSCal (SELECTION, UVDIST); } UDFBase* UDFMSCal::makeSpw (const String&) { return new UDFMSCal (SELECTION, SPW); } UDFBase* UDFMSCal::makeField (const String&) { return new UDFMSCal (SELECTION, FIELD); } UDFBase* UDFMSCal::makeFeed (const String&) { return new UDFMSCal (SELECTION, FEED); } UDFBase* UDFMSCal::makeArray (const String&) { return new UDFMSCal (SELECTION, ARRAY); } UDFBase* UDFMSCal::makeScan (const String&) { return new UDFMSCal (SELECTION, SCAN); } UDFBase* UDFMSCal::makeState (const String&) { return new UDFMSCal (SELECTION, STATE); } UDFBase* UDFMSCal::makeObs (const String&) { return new UDFMSCal (SELECTION, OBS); } UDFBase* UDFMSCal::makeAnt1Name (const String& funcName) { return new UDFMSCal (funcName, "ANTENNA", "ANTENNA1", "NAME"); } UDFBase* UDFMSCal::makeAnt2Name (const String& funcName) { return new UDFMSCal (funcName, "ANTENNA", "ANTENNA2", "NAME"); } UDFBase* UDFMSCal::makeAnt1Col (const String& funcName) { return new UDFMSCal (funcName, "ANTENNA", "ANTENNA1"); } UDFBase* UDFMSCal::makeAnt2Col (const String& funcName) { return new UDFMSCal (funcName, "ANTENNA", "ANTENNA2"); } UDFBase* UDFMSCal::makeStateCol (const String& funcName) { return new UDFMSCal (funcName, "STATE", "STATE_ID", -1); } UDFBase* UDFMSCal::makeObsCol (const String& funcName) { return new UDFMSCal (funcName," OBSERVATION", "OBSERVATION_ID"); } UDFBase* UDFMSCal::makeSpwCol (const String& funcName) { return new UDFMSCal (funcName, "SPECTRAL_WINDOW", "SPECTRAL_WINDOW_ID", 1); } UDFBase* UDFMSCal::makePolCol (const String& funcName) { return new UDFMSCal (funcName, "POLARIZATION", "POLARIZATION_ID", 1); } UDFBase* UDFMSCal::makeFieldCol (const String& funcName) { return new UDFMSCal (funcName, "FIELD", "FIELD_ID"); } UDFBase* UDFMSCal::makeProcCol (const String& funcName) { return new UDFMSCal (funcName, "PROCESSOR", "PROCESSOR_ID", -1); } UDFBase* UDFMSCal::makeSubCol (const String& funcName) { return new UDFMSCal (funcName); } void UDFMSCal::setup (const Table& table, const TaQLStyle&) { if (table.isNull()) { throw AipsError ("MSCAL can only be used on a table"); } // Most functions are handled by the engine. if (itsType != STOKES && itsType != SELECTION && itsType != GETVALUE && itsType != UVWWVL && itsType != UVWWVLS) { itsEngine.setTable (table); if (operands().size() > 1) { throw AipsError("More than 1 argument given to MSCAL function"); } // Setup the direction if an argument is given. if (operands().size() == 1) { setupDir (operands()[0]); } } setDataType (TableExprNodeRep::NTDouble); switch (itsType) { case HA: case PA: case LAST: setNDim (0); setUnit ("rad"); break; case HADEC: case AZEL: case ITRF: setShape (IPosition(1,2)); itsTmpVector.resize (2); setUnit ("rad"); break; case UVWWVL: setupWvls (table, operands(), 0); setShape (IPosition(1,3)); itsTmpVector.resize (3); break; case UVWWVLS: setupWvls (table, operands(), 0); itsTmpVector.resize (3); setNDim(2); // The shape can vary (each band can be different) break; case NEWUVW: setUnit ("m"); setShape (IPosition(1,3)); itsTmpVector.resize (3); break; case NEWUVWWVL: setupWvls (table, operands(), 1); setShape (IPosition(1,3)); itsTmpVector.resize (3); break; case NEWUVWWVLS: setupWvls (table, operands(), 1); itsTmpVector.resize (3); setNDim (2); // The shape can vary (each band can be different) break; case DELAY: setNDim (0); setUnit ("s"); break; case STOKES: setupStokes (table, operands()); setDataType (operands()[0]->dataType()); break; case SELECTION: setupSelection (table, operands()); setNDim (0); setDataType (TableExprNodeRep::NTBool); break; case GETVALUE: setupGetValue (table, operands()); setNDim (itsDataNode.getNodeRep()->ndim()); setDataType (itsDataNode.getNodeRep()->dataType()); setUnit (itsDataNode.getNodeRep()->unit().getName()); break; } } void UDFMSCal::setupDir (TENShPtr& operand) { // Make sure the operand is a constant double array // or a single string (e.g. MOON). if (! operand->isConstant()) { throw AipsError("Only a constant value can be given as a MSCAL " "function argument"); } // In principle type NTInt could also be allowed, but that makes no sense // for direction values. So it's better to be a bit strict. if (operand->dataType() == TableExprNodeRep::NTDouble) { // Get direction (given in J2000). if (operand->valueType() != TableExprNodeRep::VTArray) { throw AipsError ("Argument to MSCAL function is not an array " "of 2 values"); } // Make sure the unit is rad. // Turn the array into a vector. TableExprNodeUnit::adaptUnit (operand, "rad"); Array dirs(operand->getArrayDouble(0).array()); if (dirs.size() != 2) { throw AipsError ("Argument to MSCAL function is not an array " "of 2 values"); } Vector dirVec(dirs.reform(IPosition(1,dirs.size()))); itsEngine.setDirection (MDirection(Quantity(dirVec[0], "rad"), Quantity(dirVec[1], "rad"), MDirection::J2000)); } else if (operand->dataType() == TableExprNodeRep::NTString) { // First try the string as a planetary object. // In the future comets can be supported like COMET:cometname. String str = operand->getString(0); Bool fnd = True; try { itsEngine.setDirection (MDirection::makeMDirection(str)); } catch (std::exception&) { fnd = False; } if (!fnd) { // Now do it as a FIELD column name. // Skip possible leading backslash (escape char). if (str.size() > 0 && str[0] == '\\') { str = str.from(1); } if (str.empty()) { throw AipsError ("An empty string given to a MSCAL function"); } itsEngine.setDirColName (str); } } else { throw AipsError ("Argument to MSCAL function must be double or " "string"); } } void UDFMSCal::setupWvls (const Table& table, vector& operands, uInt nargMax) { // There must be at least 1 argument (data). if (operands.size() > nargMax) { throw AipsError ("No arguments should be given to MSCAL.UVWWVL"); } // Read the reference and channel frequencies. // Divide by lightspeed for conversion to wavelengths. // Determine the maximum nr of frequencies in a band. Table spwTab(table.keywordSet().asTable("SPECTRAL_WINDOW")); ScalarColumn refCol(spwTab, "REF_FREQUENCY"); ArrayColumn freqCol(spwTab, "CHAN_FREQ"); itsWavel.reserve (spwTab.nrow()); itsWavels.reserve (spwTab.nrow()); uInt nfreq = 0; for (rownr_t i=0; i nfreq) { nfreq = itsWavels[i].size(); } } itsTmpUvwWvl.resize (IPosition(2, 3, nfreq)); if (itsType == UVWWVL || itsType == UVWWVLS) { itsUvwCol.attach (table, "UVW"); } // Set itsIdColName for recreateColumnObjects. itsIdColName = "DATA_DESC_ID"; itsIdNode = table.col(itsIdColName); // Get the spectal window ids. Table ddtab(table.keywordSet().asTable("DATA_DESCRIPTION")); ScalarColumn(ddtab, "SPECTRAL_WINDOW_ID").getColumn (itsDDIds); } void UDFMSCal::setupStokes (const Table& table, vector& operands) { // There must be at least 1 argument (data). if (operands.size() == 0 || operands.size() > 3) { throw AipsError ("1, 2, or 3 arguments must be given to " "MSCAL.STOKES"); } itsDataNode = TableExprNode(operands[0]); if (operands[0]->valueType() != TableExprNodeRep::VTArray || !(operands[0]->dataType() == TableExprNodeRep::NTBool || operands[0]->dataType() == TableExprNodeRep::NTDouble || operands[0]->dataType() == TableExprNodeRep::NTComplex)) { throw AipsError ("First argument of MSCAL.STOKES must be a " "Complex, Double or Bool array"); } // The optional second argument gives the output correlation types. // Default is iquv. String type = "IQUV"; if (operands.size() > 1) { if (! operands[1]->isConstant() || operands[1]->valueType() != TableExprNodeRep::VTScalar || operands[1]->dataType() != TableExprNodeRep::NTString) { throw AipsError ("Second argument of MSCAL.STOKES must be a " "constant String scalar"); } type = operands[1]->getString(0); type.upcase(); } // The optional third argument tells if a factor 2 must be applied to I. Bool rescale = False; if (operands.size() > 2) { if (! operands[2]->isConstant() || operands[2]->valueType() != TableExprNodeRep::VTScalar || operands[2]->dataType() != TableExprNodeRep::NTBool) { throw AipsError ("Second argument of MSCAL.STOKES must be a " "constant Bool scalar"); } rescale = operands[2]->getBool(0); } // Open the POLARIZATION subtable and get the input correlation types. Table polTable (table.keywordSet().asTable("POLARIZATION")); if (polTable.nrow() == 0) { throw AipsError("POLARIZATION subtable of " + table.tableName() + " is empty"); } Vector inTypes (ArrayColumn(polTable, "CORR_TYPE")(0)); // Convert the output string types to ints. // First convert abbrevs. if (type == "IQUV" || type == "STOKES") { type = "I,Q,U,V"; } else if (type == "CIRC" || type == "CIRCULAR") { type = "RR,RL,LR,LL"; } else if (type == "LIN" || type == "LINEAR") { type = "XX,XY,YX,YY"; } Vector types = stringToVector(type); if (types.empty()) { throw AipsError("No polarization types given in second argument of " "MSCAL.STOKES"); } Vector outTypes(types.size()); for (uInt i=0; ishape(); if (! shape.empty()) { shape[0] = outTypes.size(); setShape (shape); } else { // Shape is unknown, so only set the dimensionality. setNDim (operands[0]->ndim()); } } void UDFMSCal::setupSelection (const Table& table, vector& operands) { // There must be 1 argument (scalar string). if (operands.size() != 1) { throw AipsError ("1 argument must be given to MSCAL selections"); } if (operands[0]->valueType() != TableExprNodeRep::VTScalar || operands[0]->dataType() != TableExprNodeRep::NTString || !operands[0]->isConstant()) { throw AipsError ("Argument of MSCAL selection must be a " "constant string scalar"); } // Get and process the selection string. String selStr(operands[0]->getString(0)); switch (itsArg) { case BASELINE: { Table anttab(table.keywordSet().asTable("ANTENNA")); TableExprNode a1 (table.col("ANTENNA1")); TableExprNode a2 (a1); if (table.tableDesc().isColumn("ANTENNA2")) { a2 = TableExprNode (table.col("ANTENNA2")); } Vector selectedAnts1; Vector selectedAnts2; Matrix selectedBaselines; std::shared_ptr curHandler = std::make_shared(); MSAntennaParse::thisMSAErrorHandler.swap (curHandler); try { itsDataNode = msAntennaGramParseCommand (anttab, a1, a2, selStr, selectedAnts1, selectedAnts2, selectedBaselines); } catch (const std::exception&) { MSAntennaParse::thisMSAErrorHandler.swap (curHandler); throw; } MSAntennaParse::thisMSAErrorHandler.swap (curHandler); } break; case CORR: { MeasurementSet ms(table); if (msCorrGramParseCommand(&ms, selStr) == 0) { itsDataNode = *(msCorrGramParseNode()); } msCorrGramParseDeleteNode(); } break; case TIME: { MeasurementSet ms(table); TableExprNode node (table.col("TIME")); Matrix times; MSMainColInterface tmp; if (msTimeGramParseCommand (&ms,selStr, TableExprNode(), tmp, node, times) == 0) { itsDataNode = *(msTimeGramParseNode()); } msTimeGramParseDeleteNode(); } break; case UVDIST: { MeasurementSet ms(table); if (msUvDistGramParseCommand(&ms, selStr) == 0) { itsDataNode = *(msUvDistGramParseNode()); } msUvDistGramParseDeleteNode(); } break; case SPW: { Table ddtab (table.keywordSet().asTable("DATA_DESCRIPTION")); Table spwtab(table.keywordSet().asTable("SPECTRAL_WINDOW")); TableExprNode colAsTEN = table.col("DATA_DESC_ID"); Vector spwid, spwDDID; Matrix chanid; if (msSpwGramParseCommand(MSSpectralWindow(spwtab), MSDataDescription(ddtab), colAsTEN, selStr, spwid, chanid, spwDDID) == 0) { itsDataNode = *(msSpwGramParseNode()); } msSpwGramParseDeleteNode(); } break; case FIELD: { Table fieldtab(table.keywordSet().asTable("FIELD")); TableExprNode colAsTEN = table.col("FIELD_ID"); Vector fldid; itsDataNode = msFieldGramParseCommand (fieldtab, colAsTEN, selStr, fldid); msFieldGramParseDeleteNode(); } break; case FEED: { Table feedtab(table.keywordSet().asTable("FEED")); TableExprNode f1 (table.col("FEED1")); TableExprNode f2 (f1); if (table.tableDesc().isColumn("FEED2")) { f2 = TableExprNode (table.col("FEED2")); } Vector selectedFeed1; Vector selectedFeed2; Matrix selectedFeedPairs; std::shared_ptr curHandler = std::make_shared(); MSFeedParse::thisMSFErrorHandler.swap (curHandler); try { itsDataNode = msFeedGramParseCommand (feedtab, f1, f2, selStr, selectedFeed1, selectedFeed2, selectedFeedPairs); } catch (const std::exception&) { MSFeedParse::thisMSFErrorHandler.swap (curHandler); throw; } MSFeedParse::thisMSFErrorHandler.swap (curHandler); } break; case ARRAY: { MeasurementSet ms(table); Vector arrid; Int maxArr=1000; itsDataNode = msArrayGramParseCommand(&ms, selStr, arrid, maxArr); } break; case SCAN: { MeasurementSet ms(table); Vector scanid; Int maxScan=1000; itsDataNode = msScanGramParseCommand(&ms, selStr, scanid, maxScan); } break; case STATE: { MeasurementSet ms(table); Vector stateid; std::shared_ptr curHandler = std::make_shared(); MSStateParse::thisMSSErrorHandler.swap (curHandler); try { if (msStateGramParseCommand(&ms, selStr, stateid) == 0) { itsDataNode = *(msStateGramParseNode()); } } catch (const std::exception&) { msStateGramParseDeleteNode(); MSStateParse::thisMSSErrorHandler.swap (curHandler); throw; } MSStateParse::thisMSSErrorHandler.swap (curHandler); } break; case OBS: { MeasurementSet ms(table); Vector obsid; // Int maxObs=1000; TableExprNode colAsTEN = table.col("OBSERVATION_ID"); itsDataNode = msObservationGramParseCommand(&ms, ms.observation(), colAsTEN, selStr, obsid); } break; default: throw AipsError ("UDFMScal::setupSelection: unknown type " + String::toString(itsArg)); } } void UDFMSCal::setupGetValue (const Table& table, vector& operands) { int idinx = 0; // See if subtable and column name have to be given explicitly as the // first arguments. if (itsSubColName.empty()) { idinx = 1; if (itsSubTabName.empty()) idinx = 2; } uInt nargReq = idinx; // Id column must be given if id (ANTENNA1/2) is not part of function name. if (itsIdColName.empty()) nargReq++; if (operands.size() != nargReq) { throw AipsError ("Function " + itsFuncName + " has " + String::toString(operands.size()) + " arguments, but should have " + String::toString(nargReq)); } // Get subtable and column name; they must be constant strings. for (int i=0; iisConstant() || operands[i]->valueType() != TableExprNodeRep::VTScalar || operands[i]->dataType() != TableExprNodeRep::NTString) { throw AipsError ("First " + String::toString(idinx) + " argument(s) of function " + itsFuncName + " must be constant strings"); } String str = operands[i]->getString(0); if (str.empty()) { throw AipsError ("An empty subtable name or column name given in " "function " + itsFuncName); } if (i == idinx-1) { itsSubColName = str; } else { itsSubTabName = str; } } // An id column needs to be given if not using ANTENNA1 or ANTENNA2. // It must be an integer column. if (itsIdColName.empty()) { if (operands[idinx]->valueType() != TableExprNodeRep::VTScalar || operands[idinx]->dataType() != TableExprNodeRep::NTInt) { throw AipsError ("Last argument of function " + itsFuncName + " must be an integer scalar"); } itsIdNode = operands[idinx]; } else { if (itsArg == 1) { // Subtable has an indirection via the DATA_DESCRIPTION. // Get the ids of the required column. Table ddtab(table.keywordSet().asTable("DATA_DESCRIPTION")); ScalarColumn(ddtab, itsIdColName).getColumn (itsDDIds); itsIdColName = "DATA_DESC_ID"; } // Create the id node. /// This fails for GROUPBY, because node does not use groupby rownrs. /// I.e. has to do applySelection!!! /// Maybe let ExprUDFNode map group rownr to original rownr, but that /// causes problems when aggregate function used in mscal. /// Maybe add the created nodes in TableParse to applySelNodes_p, but /// maybe has to take care if it is in a group. /// Also look at other UDF functions (in mscal and meas). itsIdNode = table.col(itsIdColName); } // Create the node for the data item in the subtable. Table subtab(table.keywordSet().asTable(itsSubTabName)); itsDataNode = subtab.col(itsSubColName); } void UDFMSCal::recreateColumnObjects (const Vector& rownrs) { if (! itsIdColName.empty()) { TableExprNodeRep* col = const_cast(itsIdNode.getNodeRep()); col->applySelection (rownrs); } if (! itsUvwCol.isNull()) { Table tab(itsUvwCol.table()); itsUvwCol.attach (tab(rownrs), "UVW"); } Table tab(itsEngine.getTable()); if (! tab.isNull()) { itsEngine.setTable (tab(rownrs)); } } Int64 UDFMSCal::getRowNr (const TableExprId& id) { Int64 rownr = itsIdNode.getInt(id); if (itsArg == 1) { rownr = itsDDIds[rownr]; } return rownr; } Array UDFMSCal::toWvls (const TableExprId& id) { const Vector& wvl = itsWavels[itsDDIds[itsIdNode.getInt(id)]]; Double* ptr = itsTmpUvwWvl.data(); for (uInt i=0; i= itsDataNode.nrow()) { return False; } return itsDataNode.getBool (rownr); } default: throw AipsError ("UDFMSCal: unexpected getBool function"); } } Int64 UDFMSCal::getInt (const TableExprId& id) { switch (itsType) { case GETVALUE: { rownr_t rownr = getRowNr(id); if (itsArg < 0 && rownr >= itsDataNode.nrow()) { return 0; } return itsDataNode.getInt (rownr); } default: throw AipsError ("UDFMSCal: unexpected getInt function"); } } Double UDFMSCal::getDouble (const TableExprId& id) { DebugAssert (id.byRow(), AipsError); switch (itsType) { case HA: return itsEngine.getHA (itsArg, id.rownr()); case PA: return itsEngine.getPA (itsArg, id.rownr()); case LAST: return itsEngine.getLAST (itsArg, id.rownr()); case DELAY: return itsEngine.getDelay (itsArg, id.rownr()); case GETVALUE: { rownr_t rownr = getRowNr(id); if (itsArg < 0 && rownr >= itsDataNode.nrow()) { return 0.; } return itsDataNode.getDouble (rownr); } default: throw AipsError ("UDFMSCal: unexpected getDouble function"); } } DComplex UDFMSCal::getDComplex (const TableExprId& id) { DebugAssert (id.byRow(), AipsError); switch (itsType) { case GETVALUE: { rownr_t rownr = getRowNr(id); if (itsArg < 0 && rownr >= itsDataNode.nrow()) { return DComplex(); } return itsDataNode.getDComplex (rownr); } default: throw AipsError ("UDFMSCal: unexpected getDComplex function"); } } String UDFMSCal::getString (const TableExprId& id) { DebugAssert (id.byRow(), AipsError); switch (itsType) { case GETVALUE: { rownr_t rownr = getRowNr(id); if (itsArg < 0 && rownr >= itsDataNode.nrow()) { return String(); } return itsDataNode.getString (rownr); } default: throw AipsError ("UDFMSCal: unexpected getString function"); } } MArray UDFMSCal::getArrayBool (const TableExprId& id) { DebugAssert (id.byRow(), AipsError); switch (itsType) { case STOKES: { Array out; MArray marr; itsDataNode.get (id, marr); // Combine the flags. itsStokesConv.convert (out, marr.array()); if (! marr.hasMask()) { return MArray(out); } // Combine the mask elements. Array mask; itsStokesConv.convert (mask, marr.mask()); return MArray (out, mask); } case GETVALUE: return itsDataNode.getBoolAS (getRowNr(id)); default: throw AipsError ("UDFMSCal: unexpected getArrayBool function"); } } MArray UDFMSCal::getArrayInt (const TableExprId& id) { switch (itsType) { case GETVALUE: return itsDataNode.getIntAS (getRowNr(id)); default: throw AipsError ("UDFMSCal: unexpected getArrayInt function"); } } MArray UDFMSCal::getArrayDouble (const TableExprId& id) { DebugAssert (id.byRow(), AipsError); switch (itsType) { case HADEC: itsEngine.getHaDec (itsArg, id.rownr(), itsTmpVector); return MArray(itsTmpVector); case AZEL: itsEngine.getAzEl (itsArg, id.rownr(), itsTmpVector); return MArray(itsTmpVector); case ITRF: itsEngine.getItrf (itsArg, id.rownr(), itsTmpVector); return MArray(itsTmpVector); case UVWWVL: itsUvwCol.get (id.rownr(), itsTmpVector); itsTmpVector *= itsWavel[itsDDIds[itsIdNode.getInt(id)]]; return MArray(itsTmpVector); case UVWWVLS: itsUvwCol.get (id.rownr(), itsTmpVector); return MArray(toWvls (id)); case NEWUVW: itsEngine.getNewUVW (itsArg, id.rownr(), itsTmpVector); return MArray(itsTmpVector); case NEWUVWWVL: itsEngine.getNewUVW (itsArg, id.rownr(), itsTmpVector); itsTmpVector *= itsWavel[itsDDIds[itsIdNode.getInt(id)]]; return MArray(itsTmpVector); case NEWUVWWVLS: itsEngine.getNewUVW (itsArg, id.rownr(), itsTmpVector); return MArray(toWvls (id)); case STOKES: { // Unfortunately stokes weight conversion is only defined for Float, // while TableExprNode only has Double. // So conversions are necessary for the time being. // In the future we can add Double support to StokesConverter. MArray datad; Array dataf, outf; Array outd; itsDataNode.get (id, datad); dataf.resize (datad.shape()); convertArray (dataf, datad.array()); itsStokesConv.convert (outf, dataf); outd.resize (outf.shape()); convertArray (outd, outf); if (! datad.hasMask()) { return MArray(outd); } // Combine the mask elements. Array mask; itsStokesConv.convert (mask, datad.mask()); return MArray(outd, mask); } case GETVALUE: return itsDataNode.getDoubleAS (getRowNr(id)); default: throw AipsError ("UDFMSCal: unexpected getArrayDouble function"); } } MArray UDFMSCal::getArrayDComplex (const TableExprId& id) { switch (itsType) { case STOKES: { // Unfortunately stokes conversion is only defined for type Complex, // while TableExprNode only has DComplex. // So conversions are necessary for the time being. // In the future we can add DComplex support to StokesConverter // or Complex support to TableExprNode. MArray datad; Array outf, dataf; Array outd; itsDataNode.get (id, datad); dataf.resize (datad.shape()); convertArray (dataf, datad.array()); itsStokesConv.convert (outf, dataf); outd.resize (outf.shape()); convertArray (outd, outf); if (! datad.hasMask()) { return MArray(outd); } // Combine the mask elements. Array mask; itsStokesConv.convert (mask, datad.mask()); return MArray(outd, mask); } case GETVALUE: return itsDataNode.getDComplexAS (getRowNr(id)); default: throw AipsError ("UDFMSCal: unexpected getArrayDComplex function"); } } MArray UDFMSCal::getArrayString (const TableExprId& id) { switch (itsType) { case GETVALUE: return itsDataNode.getStringAS (getRowNr(id)); default: throw AipsError ("UDFMSCal: unexpected getArrayString function"); } } } //end namespace casacore-3.7.1/derivedmscal/DerivedMC/UDFMSCal.h000066400000000000000000000271731476623553700212510ustar00rootroot00000000000000//# UDFMSCal.h: TaQL UDFs to calculate derived MS values //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef DERIVEDMSCAL_UDFMSCAL_H #define DERIVEDMSCAL_UDFMSCAL_H //# Includes #include #include #include #include #include #include namespace casacore { // // TaQL UDFs to calculate derived MS values. // // // // // //# Classes you should understand before using this one. //
      • UDFBase // // // UDFMSCal defines UDFs (user defined functions) that can be used in TaQL // to get derived MeasurementSet values hourangle, parallactic angle, // azimuth/elevation, and local sidereal time. // In this way such derived values appear to be ordinary TaQL functions. // // The following functions can be defined: //
          //
        • HA is the hourangle of the array center (observatory position). //
        • HA1 is the hourangle of ANTENNA1. //
        • HA2 is the hourangle of ANTENNA2. //
        • HADEC is the hourangle/DEC of the array center (observatory position). //
        • HADEC1 is the hourangle/DEC of ANTENNA1. //
        • HADEC2 is the hourangle/DEC of ANTENNA2. //
        • LAST is the local sidereal time of the array center. //
        • LAST1 is the local sidereal time of ANTENNA1. //
        • LAST2 is the local sidereal time of ANTENNA2. //
        • PA1 is the parallactic angle of ANTENNA1. //
        • PA2 is the parallactic angle of ANTENNA2. //
        • AZEL1 is the azimuth/elevation of ANTENNA1. //
        • AZEL2 is the azimuth/elevation of ANTENNA2. //
        • ITRF is the PHASE_DIR in ITRF coordinates (depends on TIME only). //
        • UVW_J2000 is the UVW coordinates in J2000 (in meters) //
        • STOKES makes it possible to convert Stokes of data, flag, or weight. //
        • BASELINE is baseline selection using CASA syntax. //
        • CORR is correlation selection using CASA syntax. //
        • TIME is baseline selection using CASA syntax. //
        • UVDIST is UV-distance selection using CASA syntax. //
        • SPW is spectral window selection using CASA syntax. //
        • FIELD is field selection using CASA syntax. //
        • FEED is feed selection using CASA syntax. //
        • ARRAY is array selection using CASA syntax. //
        • SCAN is scan selection using CASA syntax. //
        • STATE is state selection using CASA syntax. //
        • OBS is observation selection using CASA syntax. //
        • ANTNAME is the name of the given antenna. //
        // The first functions have data type double and unit radian (except UVW). // The HADEC, AZEL, and UVW functions return arrays while the others return // scalars. //
        The STOKES function can have data type Complex, Double or Bool. //
        The latter functions are selection functions and return a Bool scalar. // // This class is meant for a MeasurementSet, but can be used for any table // containing an ANTENNA and FIELD subtable and the relevant columns in the // main table (ANTENNA1 and/or ANTENNA2, FIELD_ID, and TIME). //
        In principle the array center is the Observatory position, which is // taken from the Measures Observatory table using the telescope name found // in the OBSERVATION subtable. However, if the subtable is not defined or // empty or if the telescope name is unknown, the position of the first antenna // is used as the array position. // // The engine can also be used for a CASA Calibration Table. It understands // how it references the MeasurementSets. Because calibration tables contain // no ANTENNA2 columns, functions XX2 are the same as XX1. //
        // // It makes it possible to do queries on these values without having // to add columns for them. // class UDFMSCal: public UDFBase { public: // Define the possible 'column' types. enum ColType {HA, HADEC, PA, LAST, AZEL, ITRF, UVWWVL, UVWWVLS, NEWUVW, NEWUVWWVL, NEWUVWWVLS, DELAY, STOKES, SELECTION, GETVALUE}; // Define the possible selection types. enum SelType {BASELINE, CORR, TIME, UVDIST, SPW, FIELD, FEED, ARRAY, SCAN, STATE, OBS}; // Create object the given ColType and SelType. UDFMSCal (ColType, Int arg); // Create the object for getting a value from a column in a subtable. // explicit UDFMSCal (const String& funcName); UDFMSCal (const String& funcName, const String& subtabName, const String& idColName, Int arg=0); UDFMSCal (const String& funcName, const String& subtabName, const String& idColName, const String& colName); // // Function to create an object. static UDFBase* makeHA (const String&); static UDFBase* makeHA1 (const String&); static UDFBase* makeHA2 (const String&); static UDFBase* makeHADEC (const String&); static UDFBase* makeHADEC1 (const String&); static UDFBase* makeHADEC2 (const String&); static UDFBase* makePA1 (const String&); static UDFBase* makePA2 (const String&); static UDFBase* makeLAST (const String&); static UDFBase* makeLAST1 (const String&); static UDFBase* makeLAST2 (const String&); static UDFBase* makeAZEL (const String&); static UDFBase* makeAZEL1 (const String&); static UDFBase* makeAZEL2 (const String&); static UDFBase* makeITRF (const String&); static UDFBase* makeUvwWvl (const String&); static UDFBase* makeUvwWvls (const String&); static UDFBase* makeUvwJ2000 (const String&); static UDFBase* makeWvlJ2000 (const String&); static UDFBase* makeWvlsJ2000(const String&); static UDFBase* makeUvwAPP (const String&); static UDFBase* makeWvlAPP (const String&); static UDFBase* makeWvlsAPP (const String&); static UDFBase* makeDelay (const String&); static UDFBase* makeDelay1 (const String&); static UDFBase* makeDelay2 (const String&); static UDFBase* makeStokes (const String&); static UDFBase* makeBaseline (const String&); static UDFBase* makeCorr (const String&); static UDFBase* makeTime (const String&); static UDFBase* makeUVDist (const String&); static UDFBase* makeSpw (const String&); static UDFBase* makeField (const String&); static UDFBase* makeFeed (const String&); static UDFBase* makeArray (const String&); static UDFBase* makeScan (const String&); static UDFBase* makeState (const String&); static UDFBase* makeObs (const String&); static UDFBase* makeAnt1Name (const String&); static UDFBase* makeAnt2Name (const String&); static UDFBase* makeAnt1Col (const String&); static UDFBase* makeAnt2Col (const String&); static UDFBase* makeStateCol (const String&); static UDFBase* makeObsCol (const String&); static UDFBase* makeSpwCol (const String&); static UDFBase* makePolCol (const String&); static UDFBase* makeFieldCol (const String&); static UDFBase* makeProcCol (const String&); static UDFBase* makeSubCol (const String&); // Setup the object. virtual void setup (const Table&, const TaQLStyle&); // Get the value. virtual Bool getBool (const TableExprId& id); virtual Int64 getInt (const TableExprId& id); virtual Double getDouble (const TableExprId& id); virtual DComplex getDComplex (const TableExprId& id); virtual String getString (const TableExprId& id); virtual MArray getArrayBool (const TableExprId& id); virtual MArray getArrayInt (const TableExprId& id); virtual MArray getArrayDouble (const TableExprId& id); virtual MArray getArrayDComplex (const TableExprId& id); virtual MArray getArrayString (const TableExprId& id); // Let a derived class recreate its column objects in case a selection // has to be applied. virtual void recreateColumnObjects (const Vector& rownrs); private: // Setup the Stokes conversion. void setupStokes (const Table& table, std::vector& operands); // Setup the baseline selection. void setupSelection (const Table& table, std::vector& operands); // Setup direction conversion if a direction is explicitly given. void setupDir (TENShPtr& operand); // Setup getting column values from a subtable. void setupGetValue (const Table& table, std::vector& operands); // Setup getting the wavelength information. void setupWvls (const Table& table, std::vector& operands, uInt nargMax); // Get the rownr in the subtable for GetValue. // If itsArg==1 it uses indirection using itsDDIds. Int64 getRowNr (const TableExprId& id); // Convert the UVW coordinates to wavelengths for the full spectrum. Array toWvls (const TableExprId&); //# Data members. MSCalEngine itsEngine; StokesConverter itsStokesConv; TableExprNode itsDataNode; //# for stokes, selections and getvalues TableExprNode itsIdNode; //# node giving rowid for getvalues ArrayColumn itsUvwCol; ColType itsType; Int itsArg; //# antnr or SelType or getValueType //# -1 subtable can be empty //# 0 normal subtable //# 1 indirect subtable via DATA_DESC_ID String itsFuncName; String itsSubTabName; String itsIdColName; String itsSubColName; //# Preallocate arrays to avoid having to construct them too often. //# Makes it thread-unsafe though. Vector itsTmpVector; Array itsTmpUvwWvl; Vector itsDDIds; //# spw or pol ids from DATA_DESCRIPTION vector itsWavel; vector > itsWavels; }; // // Error handler class for MSSel selection // // // This error handler ignores the errors rising from the MSSel parsers. // class UDFMSCalErrorHandler : public MSSelectionErrorHandler { public: ~UDFMSCalErrorHandler() override = default; void handleError (MSSelectionError&) override {} void reportError (const char*, const String) override {} }; } //end namespace #endif casacore-3.7.1/derivedmscal/DerivedMC/test/000077500000000000000000000000001476623553700205475ustar00rootroot00000000000000casacore-3.7.1/derivedmscal/DerivedMC/test/CMakeLists.txt000066400000000000000000000004661476623553700233150ustar00rootroot00000000000000set (tests tDerivedMSCal tUDFMSCal ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_derivedmscal) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/derivedmscal/DerivedMC/test/tDerivedMSCal.cc000066400000000000000000000220061476623553700235040ustar00rootroot00000000000000//# tDerivedMSCal.h: Test program for class DerivedMSCal //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace casacore; using namespace std; void check (MSDerivedValues& mdv, uInt rownr, ScalarColumn& ha, ScalarColumn& last, ArrayColumn& azel) { double mha = mdv.hourAngle(); double tha = ha(rownr); AlwaysAssertExit (near(mha, tha, 1e-10)); double mlast = mdv.last().getValue().get(); double tlast = last(rownr); AlwaysAssertExit (near(mlast, tlast, 1e-10)); Vector mazel = mdv.azel().getValue().get(); Vector tazel = azel(rownr); AlwaysAssertExit (allNear(mazel, tazel, 1e-10)); AlwaysAssertExit (azel.shape(rownr) == IPosition(1,2)); AlwaysAssertExit (azel.ndim(rownr) == 1); AlwaysAssertExit (azel.isDefined(rownr)); } void check (MSDerivedValues& mdv, uInt rownr, ScalarColumn& ha, ScalarColumn& last, ArrayColumn& azel, ArrayColumn& itrf) { check (mdv, rownr, ha, last, azel); Vector titrf = itrf(rownr); cout << titrf << endl; AlwaysAssertExit (itrf.shape(rownr) == IPosition(1,2)); AlwaysAssertExit (itrf.ndim(rownr) == 1); AlwaysAssertExit (itrf.isDefined(rownr)); } void check (MSDerivedValues& mdv, uInt rownr, ScalarColumn& ha, ScalarColumn& last, ScalarColumn& pa, ArrayColumn& azel) { check (mdv, rownr, ha, last, azel); double mpa = mdv.parAngle(); double tpa = pa(rownr); AlwaysAssertExit (near(mpa, tpa, 1e-10)); } void check (uInt rownr, ArrayColumn& uvw, ArrayColumn& uvwJ2000) { if (uvw.isNull()) { AlwaysAssertExit (allEQ (uvwJ2000(rownr), 0.)); } else { if (! allNear (uvwJ2000(rownr), uvw(rownr), 1e-7)) { cout <<"UVW diff "<< uvwJ2000(rownr)<< uvw(rownr) << endl; } AlwaysAssertExit (allNear (uvwJ2000(rownr), uvw(rownr), 1e-5)); } AlwaysAssertExit (uvw.shape(rownr) == IPosition(1,3)); AlwaysAssertExit (uvw.ndim(rownr) == 1); AlwaysAssertExit (uvw.isDefined(rownr)); AlwaysAssertExit (uvwJ2000.shape(rownr) == IPosition(1,3)); AlwaysAssertExit (uvwJ2000.ndim(rownr) == 1); AlwaysAssertExit (uvwJ2000.isDefined(rownr)); } int main(int argc, char* argv[]) { try { DerivedMSCal::registerClass(); if (argc <= 1) { cout << "Run as: tDerivedMSCal msname/caltablename [checkuvw]" << endl; return 3; } Bool checkUVW = (argc > 2); // Copy the input table. // Also determine the name of the MS containing ANTENNA, etc. String msName ("tDerivedMSCal_tmp.tab"); { Table tab(argv[1]); tab.deepCopy ("tDerivedMSCal_tmp.tab", Table::New); if (tab.keywordSet().isDefined("CAL_DESC")) { msName = ScalarColumn (tab.keywordSet().asTable("CAL_DESC"), "MS_NAME")(0); } } // Add the columns. { Table tab ("tDerivedMSCal_tmp.tab", Table::Update); TableDesc td; td.addColumn (ScalarColumnDesc("HA")); td.addColumn (ScalarColumnDesc("HA1")); td.addColumn (ScalarColumnDesc("HA2")); td.addColumn (ScalarColumnDesc("PA1")); td.addColumn (ScalarColumnDesc("PA2")); td.addColumn (ScalarColumnDesc("LAST")); td.addColumn (ScalarColumnDesc("LAST1")); td.addColumn (ScalarColumnDesc("LAST2")); td.addColumn (ArrayColumnDesc ("AZEL")); td.addColumn (ArrayColumnDesc ("AZEL1")); td.addColumn (ArrayColumnDesc ("AZEL2")); td.addColumn (ArrayColumnDesc ("ITRF")); td.addColumn (ArrayColumnDesc ("UVW_J2000")); DerivedMSCal dataMan; tab.addColumn (td, dataMan); } // Loop through all rows and check values. MeasurementSet ms (msName); Table tab("tDerivedMSCal_tmp.tab"); ScalarColumn ha(tab, "HA"); ScalarColumn ha1(tab, "HA1"); ScalarColumn ha2(tab, "HA2"); ScalarColumn pa1(tab, "PA1"); ScalarColumn pa2(tab, "PA2"); ScalarColumn last(tab, "LAST"); ScalarColumn last1(tab, "LAST1"); ScalarColumn last2(tab, "LAST2"); ArrayColumn azel(tab, "AZEL"); ArrayColumn azel1(tab, "AZEL1"); ArrayColumn azel2(tab, "AZEL2"); ArrayColumn itrf(tab, "ITRF"); ArrayColumn uvwJ2000(tab, "UVW_J2000"); ScalarMeasColumn time(tab, "TIME"); ScalarColumn fld(tab, "FIELD_ID"); ScalarColumn ant1(tab, "ANTENNA1"); ScalarColumn ant2; if (tab.tableDesc().isColumn("ANTENNA2")) { ant2.attach (tab, "ANTENNA2"); } else { ant2.attach (tab, "ANTENNA1"); } ArrayColumn uvw; if (tab.tableDesc().isColumn("UVW")) { uvw.attach (tab, "UVW"); } MSDerivedValues mdv; mdv.setMeasurementSet (ms); // Take care that the same array center is used. // Find observatory position. // If not found, set it to the position of the middle antenna. Bool fndObs = False; MPosition arrayPos; Table obstab (ms.keywordSet().asTable("OBSERVATION")); if (obstab.nrow() > 0) { String telescope = ScalarColumn(obstab, "TELESCOPE_NAME")(0); fndObs = MeasTable::Observatory (arrayPos, telescope); } if (!fndObs) { MSAntennaColumns antcol(ms.antenna()); arrayPos = antcol.positionMeas()(ms.antenna().nrow()/2); } mdv.setObservatoryPosition (arrayPos); // Now loop through quite some rows and compare result of DerivedMSCal // with MSDerivedValues. rownr_t nr = std::max(tab.nrow(), rownr_t(1000)); Int lastFldId = -1; for (rownr_t i=0; i #include #include #include #include #include #include using namespace casacore; using namespace std; void createTable() { // Create main 'MS-like' table. const uInt nant=5; TableDesc td(MS::requiredTableDesc()); SetupNewTable setup("tUDFMSCal_tmp.tab", td, Table::New); MeasurementSet ms(setup); ms.createDefaultSubtables(Table::New); ScalarColumn a1col(ms, "ANTENNA1"); ScalarColumn a2col(ms, "ANTENNA2"); Int row=0; for (uInt i=0; i namecol(anttab, "NAME"); for (uInt i=0; i a1 (ScalarColumn(tab, "ANTENNA1").getColumn()); Vector a2 (ScalarColumn(tab, "ANTENNA2").getColumn()); for (uInt i=0; i //
      • DerivedMC: // special columns and TaQL functions for an MS // casacore-3.7.1/docker/000077500000000000000000000000001476623553700145735ustar00rootroot00000000000000casacore-3.7.1/docker/make_images.sh000077500000000000000000000012341476623553700173740ustar00rootroot00000000000000#!/bin/bash # # Script to make python wheels for several versions set -eu SCRIPT_DIR=$(cd $(dirname $0) && pwd) ROOT_DIR=$(git rev-parse --show-toplevel) REPOSITORY=quay.io/casacore/casacore GIT_TAG=$(git describe --tags --dirty) py_major=3 for py_minor in $(seq 8 13); do echo -e "\n\n******** Building wheel for python ${py_major}.${py_minor} ********\n" py_version=${py_major}${py_minor} docker build \ --build-arg PYMAJOR=${py_major} \ --build-arg PYMINOR=${py_minor} \ --build-arg THREADS=16 \ --file ${SCRIPT_DIR}/py_wheel.docker \ --tag ${REPOSITORY}:py${py_version}_${GIT_TAG} \ ${ROOT_DIR} done casacore-3.7.1/docker/py_wheel.docker000066400000000000000000000047061476623553700176070ustar00rootroot00000000000000FROM quay.io/pypa/manylinux2014_x86_64 # how many threads to use for compiling ARG THREADS=4 # These variables MUST be set using the --build-arg option ARG PYMAJOR ARG PYMINOR ENV TARGET cp${PYMAJOR}${PYMINOR}-cp${PYMAJOR}${PYMINOR} # boost version ENV BOOSTMAJOR 85 ENV BOOSTMINOR 0 ENV BOOST 1.${BOOSTMAJOR}.${BOOSTMINOR} ENV BOOST_ 1_${BOOSTMAJOR}_${BOOSTMINOR} # where do we epect casacore data build time and runtime ENV CASACORE_DATA /usr/share/casacore/data # install rpms RUN yum install -y flex cfitsio-devel blas-devel lapack-devel ncurses-devel readline-devel fftw-devel wcslib-devel gsl-devel # download other source code WORKDIR /tmp RUN curl -L http://www.iausofa.org/2015_0209_F/sofa_f-20150209_a.tar.gz --output /tmp/sofa.tgz RUN curl -L ftp://ftp.astron.nl/outgoing/Measures/WSRT_Measures.ztar --output /tmp/measures.tgz RUN curl -L https://downloads.sourceforge.net/project/boost/boost/${BOOST}/boost_${BOOST_}.tar.bz2 --output /tmp/boost.tar.bz2 RUN mkdir /build WORKDIR /build RUN mkdir -p ${CASACORE_DATA} WORKDIR ${CASACORE_DATA} RUN tar zxvf /tmp/measures.tgz # install and configure sofa and measures WORKDIR /build RUN tar zxvf /tmp/sofa.tgz WORKDIR /build/sofa/20150209_a/f77/src RUN make -j${THREADS} # setup boost WORKDIR /build RUN tar jxf /tmp/boost.tar.bz2 WORKDIR /build/boost_${BOOST_} RUN ./bootstrap.sh --prefix=/opt/boost \ --with-libraries=python \ --with-python=/opt/python/${TARGET}/bin/python \ --with-python-version=${PYMAJOR}.${PYMINOR} \ --with-python-root=/opt/python/${TARGET} RUN ./b2 -j${THREADS} \ cxxflags="-fPIC -I/opt/python/${TARGET}/include/python${PYMAJOR}.${PYMINOR}/" \ link=static,shared install # casacore wants numpy. Do not take oldest_supported_numpy, because we want numpy 2.0 compatibility RUN /opt/python/${TARGET}/bin/pip install numpy # set up casacore ADD . /code RUN useradd -ms /bin/bash casacore RUN chown casacore.casacore /code USER casacore RUN mkdir /code/build WORKDIR /code/build RUN cmake .. \ -DPython3_ROOT_DIR=/opt/python/${TARGET} \ -DPython3_EXECUTABLE=/opt/python/${TARGET}/bin/python3 \ -DPython3_LIBRARY=/opt/boost/lib/libboost_python${PYMAJOR}${PYMINOR}.so \ -DPython3_INCLUDE_DIR=/opt/python/${TARGET}/include/python${PYMAJOR}.${PYMINOR}/ \ -DSOFA_ROOT_DIR=/build \ -DBUILD_TESTING=OFF \ -DDATA_DIR=${CASACORE_DATA} \ -DPORTABLE=TRUE \ -DUSE_PCH=FALSE \ -DCMAKE_INSTALL_PREFIX=/usr/local RUN make -j${THREADS} USER root RUN make install casacore-3.7.1/docker/ubuntu2004_clang.docker000066400000000000000000000026641476623553700207700ustar00rootroot00000000000000FROM ubuntu:20.04 RUN export DEBIAN_FRONTEND=noninteractive \ && apt-get update \ && apt-get install -y \ clang \ cmake \ flex \ bison \ libblas-dev \ liblapack-dev \ libcfitsio-dev \ wcslib-dev \ libfftw3-dev \ gfortran \ libncurses5-dev \ libreadline6-dev \ libhdf5-serial-dev \ libboost-dev \ libboost-python-dev \ libboost-test-dev \ libgsl-dev \ python-is-python3 \ python3-numpy \ wget \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \ # Install WSRT Measures (extra casacore data, for tests) # Note: The file on the ftp site is updated daily. When warnings regarding leap # seconds appear, ignore them or regenerate the docker image. && wget -nv -O /WSRT_Measures.ztar ftp://ftp.astron.nl/outgoing/Measures/WSRT_Measures.ztar \ && mkdir -p /usr/local/share/casacore/data \ && cd /usr/local/share/casacore/data \ && tar xfz /WSRT_Measures.ztar \ && rm /WSRT_Measures.ztar ADD . /code RUN useradd -ms /bin/bash casacore \ && chown casacore.casacore /code USER casacore RUN mkdir /code/build WORKDIR /code/build RUN cmake .. \ -DBUILD_TESTING=ON \ -DUSE_OPENMP=OFF \ -DUSE_HDF5=ON \ -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} \ -DDATA_DIR=/usr/local/share/casacore/data \ -DCMAKE_C_COMPILER=/usr/bin/clang \ -DCMAKE_CXX_COMPILER=/usr/bin/clang++ \ && make -j`nproc` USER root RUN make install USER casacore casacore-3.7.1/docker/ubuntu2004_gcc.docker000066400000000000000000000025501476623553700204320ustar00rootroot00000000000000FROM ubuntu:20.04 RUN export DEBIAN_FRONTEND=noninteractive \ && apt-get update \ && apt-get install -y \ gcc \ g++ \ cmake \ flex \ bison \ libblas-dev \ liblapack-dev \ libcfitsio-dev \ wcslib-dev \ libfftw3-dev \ gfortran \ libncurses5-dev \ libreadline6-dev \ libhdf5-serial-dev \ libboost-dev \ libboost-python-dev \ libboost-test-dev \ libgsl-dev \ python-is-python3 \ python3-numpy \ wget \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \ # Install WSRT Measures (extra casacore data, for tests) # Note: The file on the ftp site is updated daily. When warnings regarding leap # seconds appear, ignore them or regenerate the docker image. && wget -nv -O /WSRT_Measures.ztar ftp://ftp.astron.nl/outgoing/Measures/WSRT_Measures.ztar \ && mkdir -p /usr/local/share/casacore/data \ && cd /usr/local/share/casacore/data \ && tar xfz /WSRT_Measures.ztar \ && rm /WSRT_Measures.ztar ADD . /code RUN useradd -ms /bin/bash casacore \ && chown casacore.casacore /code USER casacore RUN mkdir /code/build WORKDIR /code/build RUN cmake .. \ -DBUILD_TESTING=ON \ -DUSE_OPENMP=OFF \ -DUSE_HDF5=ON \ -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} \ -DDATA_DIR=/usr/local/share/casacore/data \ && make -j`nproc` USER root RUN make install USER casacore casacore-3.7.1/docker/ubuntu2204_clang.docker000066400000000000000000000030031476623553700207560ustar00rootroot00000000000000FROM ubuntu:22.04 RUN export DEBIAN_FRONTEND=noninteractive \ && apt-get update \ && apt-get install -y \ clang \ cmake \ flex \ bison \ libblas-dev \ liblapack-dev \ libcfitsio-dev \ wcslib-dev \ libfftw3-dev \ gfortran \ libc++-dev \ libc++abi-dev \ libncurses5-dev \ libreadline6-dev \ libhdf5-serial-dev \ libboost-dev \ libboost-python-dev \ libboost-test-dev \ libgsl-dev \ python-is-python3 \ python3-numpy \ wget \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \ # Install WSRT Measures (extra casacore data, for tests) # Note: The file on the ftp site is updated daily. When warnings regarding leap # seconds appear, ignore them or regenerate the docker image. && wget -nv -O /WSRT_Measures.ztar ftp://ftp.astron.nl/outgoing/Measures/WSRT_Measures.ztar \ && mkdir -p /usr/local/share/casacore/data \ && cd /usr/local/share/casacore/data \ && tar xfz /WSRT_Measures.ztar \ && rm /WSRT_Measures.ztar ADD . /code RUN useradd -ms /bin/bash casacore \ && chown casacore.casacore /code USER casacore RUN mkdir /code/build WORKDIR /code/build ENV CXXFLAGS="${CXXFLAGS} -stdlib=libc++" RUN cmake .. \ -DBUILD_TESTING=ON \ -DUSE_OPENMP=OFF \ -DUSE_HDF5=ON \ -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} \ -DDATA_DIR=/usr/local/share/casacore/data \ -DCMAKE_C_COMPILER=/usr/bin/clang \ -DCMAKE_CXX_COMPILER=/usr/bin/clang++ \ && make -j`nproc` USER root RUN make install USER casacore casacore-3.7.1/docker/ubuntu2204_gcc.docker000066400000000000000000000026141476623553700204350ustar00rootroot00000000000000FROM ubuntu:22.04 RUN export DEBIAN_FRONTEND=noninteractive \ && apt-get update \ && apt-get install -y \ gcc \ g++ \ cmake \ flex \ bison \ libblas-dev \ liblapack-dev \ libcfitsio-dev \ wcslib-dev \ libfftw3-dev \ gfortran \ libncurses5-dev \ libreadline6-dev \ libhdf5-serial-dev \ libboost-dev \ libboost-python-dev \ libboost-test-dev \ libgsl-dev \ python-is-python3 \ python3-numpy \ wget \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \ # Install WSRT Measures (extra casacore data, for tests) # Note: The file on the ftp site is updated daily. When warnings regarding leap # seconds appear, ignore them or regenerate the docker image. && wget -nv -O /WSRT_Measures.ztar ftp://ftp.astron.nl/outgoing/Measures/WSRT_Measures.ztar \ && mkdir -p /usr/local/share/casacore/data \ && cd /usr/local/share/casacore/data \ && tar xfz /WSRT_Measures.ztar \ && rm /WSRT_Measures.ztar ADD . /code RUN useradd -ms /bin/bash casacore \ && chown casacore.casacore /code USER casacore RUN mkdir /code/build WORKDIR /code/build RUN cmake .. \ -DBUILD_TESTING=ON \ -DUSE_OPENMP=OFF \ -DUSE_HDF5=ON \ -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} \ -DDATA_DIR=/usr/local/share/casacore/data \ -DBUILD_FFTPACK_DEPRECATED=ON \ && make -j`nproc` USER root RUN make install USER casacore casacore-3.7.1/docker/ubuntu2404_clang.docker000066400000000000000000000026641476623553700207740ustar00rootroot00000000000000FROM ubuntu:24.04 RUN export DEBIAN_FRONTEND=noninteractive \ && apt-get update \ && apt-get install -y \ clang \ cmake \ flex \ bison \ libblas-dev \ liblapack-dev \ libcfitsio-dev \ wcslib-dev \ libfftw3-dev \ gfortran \ libncurses5-dev \ libreadline6-dev \ libhdf5-serial-dev \ libboost-dev \ libboost-python-dev \ libboost-test-dev \ libgsl-dev \ python-is-python3 \ python3-numpy \ wget \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \ # Install WSRT Measures (extra casacore data, for tests) # Note: The file on the ftp site is updated daily. When warnings regarding leap # seconds appear, ignore them or regenerate the docker image. && wget -nv -O /WSRT_Measures.ztar ftp://ftp.astron.nl/outgoing/Measures/WSRT_Measures.ztar \ && mkdir -p /usr/local/share/casacore/data \ && cd /usr/local/share/casacore/data \ && tar xfz /WSRT_Measures.ztar \ && rm /WSRT_Measures.ztar ADD . /code RUN useradd -ms /bin/bash casacore \ && chown casacore.casacore /code USER casacore RUN mkdir /code/build WORKDIR /code/build RUN cmake .. \ -DBUILD_TESTING=ON \ -DUSE_OPENMP=OFF \ -DUSE_HDF5=ON \ -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} \ -DDATA_DIR=/usr/local/share/casacore/data \ -DCMAKE_C_COMPILER=/usr/bin/clang \ -DCMAKE_CXX_COMPILER=/usr/bin/clang++ \ && make -j`nproc` USER root RUN make install USER casacore casacore-3.7.1/docker/ubuntu2404_gcc.docker000066400000000000000000000027041476623553700204370ustar00rootroot00000000000000FROM ubuntu:24.04 RUN export DEBIAN_FRONTEND=noninteractive \ && apt-get update \ && apt-get install -y \ gcc \ g++ \ cmake \ flex \ bison \ libblas-dev \ liblapack-dev \ libcfitsio-dev \ wcslib-dev \ libfftw3-dev \ gfortran \ libncurses5-dev \ libreadline6-dev \ libhdf5-serial-dev \ libboost-dev \ libboost-filesystem-dev \ libboost-python-dev \ libboost-system-dev \ libboost-test-dev \ libgsl-dev \ python-is-python3 \ python3-numpy \ wget \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \ # Install WSRT Measures (extra casacore data, for tests) # Note: The file on the ftp site is updated daily. When warnings regarding leap # seconds appear, ignore them or regenerate the docker image. && wget -nv -O /WSRT_Measures.ztar ftp://ftp.astron.nl/outgoing/Measures/WSRT_Measures.ztar \ && mkdir -p /usr/local/share/casacore/data \ && cd /usr/local/share/casacore/data \ && tar xfz /WSRT_Measures.ztar \ && rm /WSRT_Measures.ztar ADD . /code RUN useradd -ms /bin/bash casacore \ && chown casacore.casacore /code USER casacore RUN mkdir /code/build WORKDIR /code/build RUN cmake .. \ -DBUILD_TESTING=ON \ -DUSE_OPENMP=OFF \ -DUSE_HDF5=ON \ -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} \ -DDATA_DIR=/usr/local/share/casacore/data \ -DBUILD_FFTPACK_DEPRECATED=ON \ && make -j`nproc` USER root RUN make install USER casacore casacore-3.7.1/doxygen.cfg000066400000000000000000002775101476623553700154760ustar00rootroot00000000000000# Doxyfile 1.8.5 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all text # before the first occurrence of this tag. Doxygen uses libiconv (or the iconv # built into libc) for the transcoding. See http://www.gnu.org/software/libiconv # for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = casacore # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify an logo or icon that is included in # the documentation. The maximum height of the logo should not exceed 55 pixels # and the maximum width should not exceed 200 pixels. Doxygen will copy the logo # to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = doc # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes # performance problems for the file system. # The default value is: NO. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese- # Traditional, Croatian, Czech, Danish, Dutch, English, Esperanto, Farsi, # Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en, # Korean, Korean-en, Latvian, Norwegian, Macedonian, Persian, Polish, # Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, # Turkish, Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = YES # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = YES # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = YES # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus # requiring an explicit \brief command for a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a # new page for each member. If set to NO, the documentation of a member will be # part of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:\n" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert # newlines. ALIASES = "template=\par Template requirements:\n" # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, Javascript, # C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make # doxygen treat .inc files as Fortran files (default is PHP), and .f files as C # (default is Fortran), use: inc=Fortran f=C. # # Note For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by by putting a % sign in front of the word # or globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small # doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. # Set to 1 based on feedback from a doxygen run LOOKUP_CACHE_SIZE = 1 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = YES # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined # locally in source files will be included in the documentation. If set to NO # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO these classes will be included in the various overviews. This option has # no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend # (class|struct|union) declarations. If set to NO these declarations will be # included in the documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file # names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. # The default value is: system dependent. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO the members will appear in declaration order. # The default value is: NO. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the # todo list. This list is created by putting \todo commands in the # documentation. # The default value is: YES. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the # test list. This list is created by putting \test commands in the # documentation. # The default value is: YES. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the # documentation. If the initializer consists of more lines than specified here # it will be hidden. Use a value of 0 to hide initializers completely. The # appearance of the value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 0 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # the bottom of the documentation of classes and structs. If set to YES the list # will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. Do not use file names with spaces, bibtex cannot handle them. See # also \cite for info how to create references. CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some parameters # in a documented function, or documenting parameters that don't exist or using # markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO doxygen will only warn about wrong or incomplete parameter # documentation, but not about the absence of documentation. # The default value is: NO. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. # Note: If this tag is empty the current directory is searched. INPUT = mainpage.dox \ casa \ coordinates \ derivedmscal \ fits \ images \ lattices \ measures \ meas \ ms \ msfits \ python \ scimath \ tables # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: http://www.gnu.org/software/libiconv) for the list of # possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank the # following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, # *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, # *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, # *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, # *.qsf, *.as and *.js. FILE_PATTERNS = *.dox \ *.h # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = YES # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = */build_* \ */.svn/* \ */demo/* \ */test/* \ */include/* \ */fortran/* \ */.git/* \ */*sav*/* # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. INPUT_FILTER = build-tools/doxygen_pp # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER ) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will be # generated. Documented entities will be cross-referenced with these sources. # # Note: To get rid of all source code in the generated output, make sure that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. # The default value is: NO. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # function all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES, then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see http://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the config file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = YES # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of # the HTML output. If left blank doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. # HTML_STYLESHEET = casa/tools/casacore.css # The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- # defined cascading style sheet that is included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefor more robust against future updates. # Doxygen will copy the style sheet file to the output directory. For an example # see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the stylesheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see # http://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use grayscales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this # to NO can help when comparing the output of multiple runs. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = NO # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: http://developer.apple.com/tools/xcode/), introduced with # OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on # Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for # words in the documentation. The HTML workshop also contains a viewer for # compressed HTML files. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO # The CHM_FILE tag can be used to specify the file name of the resulting .chm # file. You can add a path in front of the file if the result should not be # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler ( hhc.exe). If non-empty # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated ( # YES) or that it should be included in the master .chm file ( NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated ( # YES) or a normal table of contents ( NO) in the .chm file. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace # (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual # Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- # folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location of Qt's # qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the # generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has # the same information as the tab index, you could consider setting # DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 1 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # # Note that when changing this option you need to delete any form_*.png files in # the HTML output directory before the changes have effect. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # http://www.mathjax.org) which uses client side Javascript for the rendering # instead of using prerendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: # http://docs.mathjax.org/en/latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory # should contain the MathJax.js script. For instance, if the mathjax directory # is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from http://www.mathjax.org before deployment. # The default value is: http://cdn.mathjax.org/mathjax/latest. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the javascript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /
      • //# // // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/fits/FITS/000077500000000000000000000000001476623553700150365ustar00rootroot00000000000000casacore-3.7.1/fits/FITS/BasicFITS.cc000066400000000000000000000210761476623553700170620ustar00rootroot00000000000000//# FITS.cc: Transform a Casacore Array to or from a FITS disk file. //# Copyright (C) 1993,1994,1995,1996,1998,1999,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Array ReadFITS(const char *FileName, Bool &ok, String &ErrorMessage, String *unitName, Vector *axisNames, Vector *refPixel, Vector *refLocation, Vector *delta, std::map *keywords, String *objectName) { Array data; ok = True; FitsInput infile(FileName, FITS::Disk); if (infile.err()) { ok = False; ErrorMessage = String("Cannot open file ") + String(FileName); return data; } if (infile.rectype() != FITS::HDURecord || infile.hdutype() != FITS::PrimaryArrayHDU) { ok = False; ErrorMessage = "FITS file is not an image, or is malformed " "(or something)"; return data; } switch(infile.datatype()) { case FITS::BYTE: { PrimaryArray fitsdata(infile); ReadFITSin(fitsdata, data, ok, ErrorMessage, unitName, axisNames, refPixel, refLocation, delta, keywords, objectName); } break; case FITS::SHORT: { PrimaryArray fitsdata(infile); ReadFITSin(fitsdata, data, ok, ErrorMessage, unitName, axisNames, refPixel, refLocation, delta, keywords, objectName); } break; case FITS::LONG: { PrimaryArray fitsdata(infile); ReadFITSin(fitsdata, data, ok, ErrorMessage, unitName, axisNames, refPixel, refLocation, delta, keywords, objectName); } break; case FITS::FLOAT: { PrimaryArray fitsdata(infile); ReadFITSin(fitsdata, data, ok, ErrorMessage, unitName, axisNames, refPixel, refLocation, delta, keywords, objectName); } break; case FITS::DOUBLE: { PrimaryArray fitsdata(infile); ReadFITSin(fitsdata, data, ok, ErrorMessage, unitName, axisNames, refPixel, refLocation, delta, keywords, objectName); } break; default: ok = False; ErrorMessage = "Unknown datatype - no data returned"; } return data; } Bool WriteFITS(const char *FileName, const Array &array, String &ErrorMessage, const char *unitName, const Vector *axisNames, const Vector *refPixel, const Vector *refLocation, const Vector *delta, const std::map *keywords, const char *objectName, Int BITPIX, Float minPix, Float maxPix) { FitsOutput outfile(FileName, FITS::Disk); if (outfile.err()) { ErrorMessage = String("Cannot open file for writing: ") + String(FileName); return False; } FitsKeywordList kw; kw.mk(FITS::SIMPLE,True); Double bscale, bzero; const Short maxshort = 32767; const Short minshort = -32768; if (BITPIX == -32) { bscale = 1.0; bzero = 0.0; kw.mk(FITS::BITPIX, -32, "Floating point"); } else if (BITPIX == 16) { kw.mk(FITS::BITPIX, 16, "Short integer"); if (minPix > maxPix) { minMax(minPix, maxPix, array); } bscale = Double(maxPix - minPix)/Double(Int(maxshort) - Int(minshort)); bzero = Double(minPix) + bscale * (-Double(minshort)); } else { ErrorMessage = "BITPIX must be -32 (floating point) or 16 (short integer)"; return False; } kw.mk(FITS::NAXIS, int(array.ndim())); for (Int i=0; i < Int(array.ndim()); i++) { kw.mk(i+1, FITS::NAXIS, int(array.shape()(i))); } kw.mk(FITS::BSCALE, bscale, "physical = pixel*BSCALE + BZERO"); kw.mk(FITS::BZERO, bzero); // Set the "optional" keywords if (unitName) { kw.mk(FITS::BUNIT, unitName); } if (axisNames) { if (axisNames->nelements() != array.ndim()) { ErrorMessage = String("axisNames wrong length"); return False; } for (Int i=0; i < Int(array.ndim()); i++) { kw.mk(i+1, FITS::CTYPE, (*axisNames)(i).chars()); } } if (refPixel) { if (refPixel->nelements() != array.ndim()) { ErrorMessage = String("refPixel wrong length"); return False; } for (Int i=0; i < Int(array.ndim()); i++) { kw.mk(i+1, FITS::CRPIX, (*refPixel)(i) + 1.0f); } } if (refLocation) { if (refLocation->nelements() != array.ndim()) { ErrorMessage = String("refLocation wrong length"); return False; } for (Int i=0; i < Int(array.ndim()); i++) { kw.mk(i+1, FITS::CRVAL, (*refLocation)(i)); } } if (delta) { if (delta->nelements() != array.ndim()) { ErrorMessage = String("delta wrong length"); return False; } for (Int i=0; i < Int(array.ndim()); i++) { kw.mk(i+1, FITS::CDELT, (*delta)(i)); } } if (keywords) { for (const auto& elem : *keywords) { String key (elem.first); Double val (elem.second); // FITS requires upper case, length=8 (or less) keywords key.upcase(); if (key.length() > 8) { key = key.at(0,8); } kw.mk(key.chars(), val); } } if (objectName) { kw.mk(FITS::OBJECT, objectName); } ostringstream os; os << "Written by casacore "; kw.history(os.str().data()); kw.end(); switch (BITPIX) { case -32: { PrimaryArray pa(kw); if (pa.err()) { ErrorMessage = "Error constructing primary array from keywords"; return False; } Bool deleteIt; const Float *storage = array.getStorage(deleteIt); //*** Cast needed because of misdeclaration (I believe) in hdu.h pa.store((Float *)storage); // I don't think the following adequately check for errors on write. if (pa.write_hdr(outfile)) { array.freeStorage(storage, deleteIt); ErrorMessage = "Write error writing keywords"; return False; } if (pa.write(outfile) != Int(array.nelements())) { array.freeStorage(storage, deleteIt); ErrorMessage = "Write error writing data"; return False; } array.freeStorage(storage, deleteIt); } break; case 16: { PrimaryArray pa(kw); if (pa.err()) { ErrorMessage = "Error constructing primary array from keywords"; return False; } Bool deleteIt; const Float *storage = array.getStorage(deleteIt); Block storage16(array.nelements()); const uInt n = array.nelements(); for (uInt i=0; i= maxPix) { storage16[i] = maxshort; } else { storage16[i] = Short((storage[i] - bzero)/bscale); } } //*** Cast needed because of misdeclaration (I believe) in hdu.h pa.store(storage16.storage()); // I don't think the following adequately check for errors on write. if (pa.write_hdr(outfile)) { array.freeStorage(storage, deleteIt); ErrorMessage = "Write error writing keywords"; return False; } if (pa.write(outfile) != Int(array.nelements())) { array.freeStorage(storage, deleteIt); ErrorMessage = "Write error writing data"; return False; } array.freeStorage(storage, deleteIt); } break; default: ErrorMessage = "Impossible error in WriteFITS!"; return False; } return True; } } //# NAMESPACE CASACORE - END casacore-3.7.1/fits/FITS/BasicFITS.h000066400000000000000000000142621476623553700167230ustar00rootroot00000000000000//# FITS.h: Transform a Casacore Array to or from a FITS disk file. //# Copyright (C) 1993,1994,1995,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef FITS_BASICFITS_H #define FITS_BASICFITS_H #include //# Would like to forward declare #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class String; // Forward declarations // read a FITS file from a Casacore array // // // // ReadFITS Casacore interface routines. // // // Quick and dirty interface to the FITS classes for turning Casacore // arrays into FITS files and back. N.B. this will have many more features // in the future, also some files should be renamed since we now have // FITS.h and fits.h. // // // Read FITS from a file into a Casacore Array. Sets "ok" to False if there // is any problem. We only deal with data in the primary data array. // If ReadFITS fails, the state of array is undefined. Trailing // degenerate (length==1) axes are NOT removed. If desired, you may do // this yourself with the nonDegenerate array member function. // If ok is false, ErrorMessage will contain an information error message. // If necessary, the data is converted from whatever type it is stored as // to Float. BSCALE and BZERO are applied. Blanks are not handled. // // If unitName is non-null, the string it points to is filled with the FITS // BUNIT keyword. If axisNames is name of the axes (CTYPEn). // If refPixel is non-null, it is set to the reference pixel of the FITS file // (CRPIX). Similarly refLocation is set to the position // (image coordinates) of the reference pixel (CRVALn) and delta is // set to the increment along each axis (CDELTn). All // the vectors are resized if necessary. Note that FITS pixel indexing is // one-based, Casacore is 0-based, this correction is made. unitName and // axisNames have trailing blanks (a FITS "feature") removed. // // If "keywords" is non-null, the integral and floating point keywords // (excluding NAXIS*, BSCALE, BZERO) are read into keywords. Case is not // changed. // // // If objectName is non-null, the string it points to is set to the // value of the FITS OBJECT keyword. // // This will only work properly on an IEEE big-endian // machine at the moment. // // // blabla Array ReadFITS(const char *FileName, Bool &ok, String &ErrorMessage, String *unitName = 0, Vector *axisNames = 0, Vector *refPixel = 0, Vector *refLocation = 0, Vector *delta = 0, std::map *keywords = 0, String *objectName = 0); // // write a FITS file to a Casacore array // // // // WriteFITS Casacore interface routines. // // // Write a FITS file from a Casacore Array. Returns False if there is any // proglem. The data is written into the primary data array, and the data // is written in floating point (BITPIX=-32). If the operation fails, // ErrorMessage will contain an informative error. At the moment this // probably isn't bulletproof enough at finding errors on output. // // If any of unitName, axisNames, refPixel, refLocation, or delta are // non-null, the corresponding FITS keywords (BUNIT, CTYPEn, CRPIXn, // CRVALn, CDELTn) are set. CRVALn is corrected for the difference in // addressing between FITS and Casacore (1 vs. 0). If a Vector pointer // is non-null, then that vector must be the correct length. // // If keywords is non-null, the contents are written out as FITS keywords. // The names are upper-cased and truncated to 8 characters (yuck). No other // validation is done (e.g. that SIMPLE or NAXIS is not in the map). // // If objectName is non-null, the OBJECT keyword is set. // // BITPIX can presently be set to -32 or 16 only. When BITPIX is 16 it will // write BSCALE and BZERO into the FITS file. If minPix is greater than maxPix // the minimum and maximum pixel values will be determined from the array, // otherwise the supplied values will be used and pixels outside that range // will be truncated to the minimum and maximum pixel values (note that this // truncation does not occur for BITPIX=-32). // // // blabla Bool WriteFITS(const char *FileName, const Array &array, String &ErrorMessage, const char *unitName = 0, const Vector *axisNames = 0, const Vector *refPixel = 0, const Vector *refLocation = 0, const Vector *delta = 0, const std::map *keywords = 0, const char *objectName = 0, Int BITPIX=-32, Float minPix = 1.0, Float maxPix = -1.0); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/fits/FITS/BinTable.cc000066400000000000000000000767531476623553700170470ustar00rootroot00000000000000//# Bintable.cc: this defines BinaryTable, which converts FITS binary tables to Casacore Tables //# Copyright (C) 1994-1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Bool isSDFitsColumn(FITS::ReservedName name) { if (name == FITS::AUTHOR || name == FITS::CDELT || name == FITS::CROTA || name == FITS::CRPIX || name == FITS::CRVAL || name == FITS::CTYPE || name == FITS::DATE || name == FITS::DATE_OBS || name == FITS::EPOCH || name == FITS::EQUINOX || name == FITS::INSTRUME || name == FITS::OBJECT || name == FITS::OBSERVER || name == FITS::ORIGIN || name == FITS::TELESCOP) return True; else return False; } // The constructor BinaryTable::BinaryTable(FitsInput& fitsin, FITSErrorHandler errhandler, Bool useIncrSM, Bool sdfits) : BinaryTableExtension(fitsin, errhandler), currRowTab(0), nelem(0), colNames(0), vatypes_p(0), vaptr_p(0), va_p(0), theheap_p(0) { AlwaysAssert(err() == HeaderDataUnit::OK, AipsError); // is there a heap if (pcount()>0) { // yes, must read the entire table in at once so that // we can have access to the heap as we step through the table read(nrows()); if (notnull(theap())) { uInt heapOffset = theap() - rowsize()*nrows(); // Skip to the start of the heap // I don't see any way except to read these bogus bytes Block junk(heapOffset); ExtensionHeaderDataUnit::read(junk.storage(), heapOffset); } theheap_p = new char [pcount()]; AlwaysAssert(theheap_p, AipsError); ExtensionHeaderDataUnit::read(theheap_p, pcount()); // and do some initial decoding of the VADesc related stuff uInt ncol = ncols(); vatypes_p = new FITS::ValueType [ncol]; AlwaysAssert(vatypes_p, AipsError); vaptr_p = new void * [ncol]; AlwaysAssert(vaptr_p, AipsError); va_p = new VADescFitsField [ncol]; AlwaysAssert(va_p, AipsError); for (uInt i=0;iisreserved() || (sdfits && isSDFitsColumn(kw->kw().name()))) { // Get the kw name and remove the trailing spaces kwname = kw->name(); kwname.rtrim(' '); // if it is indexed, add the index to the keyword if (kw->isindexed()) { snprintf(index,sizeof(index),"%i",kw->index()); kwname = kwname + String(index); } // first check if this keyword exists in kwSet if (kwSet.isDefined(kwname)) { // Issue a warning and remove the old one cout << "Duplicate keyword name : " << kwname << " most recent occurrance takes precedence" << endl; kwSet.removeField(kwname); } switch (kw->type()) { // put NOVALUE fields in as string keywords with an emtpy string case FITS::NOVALUE: kwSet.define(kwname,""); break; case FITS::LOGICAL: kwSet.define(kwname, kw->asBool()); break; case FITS::CHAR: kwSet.define(kwname, kw->asString()); break; case FITS::STRING: kwSet.define(kwname, kw->asString()); break; case FITS::LONG: kwSet.define(kwname, kw->asInt()); break; case FITS::FLOAT: kwSet.define(kwname, kw->asFloat()); break; case FITS::DOUBLE: kwSet.define(kwname, kw->asDouble()); break; case FITS::COMPLEX: kwSet.define(kwname, kw->asComplex()); break; // the above types are all that should be present as keywords default: cerr << "Error: unrecognized table data type for keyword " << kwname << " type = " << kw->type() << endl; cerr << "That should not have happened" << endl; continue; } // end of switch on kw->type() // add any comment in kwSet.setComment(kwname, kw->comm()); } // end of if(!kw->isreserved()) } // end of loop over kw list // get some things to remember Int nfield = (Int ) tfields(); nelem = new Int[nfield]; colNames = new std::map(); AlwaysAssert(nelem, AipsError); // loop over the number of fields in the FITS table for (Int i=0; i < nfield; i++) { nelem[i] = field(i).nelements(); // check if the column name exists String colname(ttype(i)); // remove trailing spaces colname.rtrim(' '); if (td.isColumn(colname)) { // issue a warning, append column number to this name ostringstream newname; newname << colname << "." << i; // str gives the space to cptr, which must be deleted colname = newname.str(); cout << "Duplicate column name : " << ttype(i) << " this occurance will be named " << colname << endl; } else if (td.keywordSet().isDefined(colname)) { // rename the offending keyword, // the column name takes precedence! String newname = colname + "-keyword"; // cout << "Duplicate name (keyword & column) : " << ttype(i) // << " keyword will be renamed " << newname << endl; td.rwKeywordSet().renameField(newname, colname); } // enter the name in the colNames map colNames->insert(std::make_pair(i, colname)); // get a shorthand Bool for array versus scalar // NOTE: VADESC are always assumed to be array columns // but that fact is ignored by isArray - but thats ok, // it is not used in that case. Bool isArray = (nelem[i] > 1 && field(i).fieldtype() != FITS::CHAR && field(i).fieldtype() != FITS::STRING); // switch on the type of column switch (field(i).fieldtype()) { // BIT stored as LOGICAL case FITS::BIT: case FITS::LOGICAL: if (isArray) { td.addColumn(ArrayColumnDesc(colname,"", IPosition(1,nelem[i]), ColumnDesc::Direct)); } else { td.addColumn(ScalarColumnDesc(colname,"")); } break; // BYTE stored as uChar case FITS::BYTE: if (isArray) { td.addColumn(ArrayColumnDesc(colname,"", IPosition(1,nelem[i]), ColumnDesc::Direct)); } else { td.addColumn(ScalarColumnDesc(colname,"")); } break; case FITS::SHORT: if (isArray) { td.addColumn(ArrayColumnDesc(colname,"", IPosition(1,nelem[i]), ColumnDesc::Direct)); } else { td.addColumn(ScalarColumnDesc(colname,"")); } break; case FITS::LONG: if (isArray) { td.addColumn(ArrayColumnDesc(colname,"", IPosition(1,nelem[i]), ColumnDesc::Direct)); } else { td.addColumn(ScalarColumnDesc(colname,"")); } break; case FITS::CHAR: case FITS::STRING: // a CHAR and STRING type is always a string, never an array td.addColumn(ScalarColumnDesc(colname,"")); break; case FITS::FLOAT: if (isArray) { td.addColumn(ArrayColumnDesc(colname,"", IPosition(1,nelem[i]), ColumnDesc::Direct)); } else { td.addColumn(ScalarColumnDesc(colname,"")); } break; case FITS::DOUBLE: if (isArray) { td.addColumn(ArrayColumnDesc(colname,"", IPosition(1,nelem[i]), ColumnDesc::Direct)); } else { td.addColumn(ScalarColumnDesc(colname,"")); } break; case FITS::COMPLEX: if (isArray) { td.addColumn(ArrayColumnDesc(colname,"", IPosition(1,nelem[i]), ColumnDesc::Direct)); } else { td.addColumn(ScalarColumnDesc(colname,"")); } break; // ICOMPLEX is promoted to DCOMPLEX so no precision is lost case FITS::ICOMPLEX: case FITS::DCOMPLEX: if (isArray) { td.addColumn(ArrayColumnDesc(colname,"", IPosition(1,nelem[i]), ColumnDesc::Direct)); } else { td.addColumn(ScalarColumnDesc(colname,"")); } break; case FITS::VADESC: { // there MUST be a heap at this point AlwaysAssert(theheap_p, AipsError); switch (vatypes_p[i]) { case FITS::BIT: case FITS::LOGICAL: td.addColumn(ArrayColumnDesc(colname,"")); break; case FITS::BYTE: td.addColumn(ArrayColumnDesc(colname,"")); break; // shorts are promoted to LONGs case FITS::SHORT: case FITS::LONG: td.addColumn(ArrayColumnDesc(colname,"")); break; // an array of chars is just a scalar String case FITS::CHAR: td.addColumn(ScalarColumnDesc(colname,"")); break; case FITS::FLOAT: td.addColumn(ArrayColumnDesc(colname,"")); break; case FITS::DOUBLE: td.addColumn(ArrayColumnDesc(colname,"")); break; case FITS::COMPLEX: td.addColumn(ArrayColumnDesc(colname,"")); break; case FITS::DCOMPLEX: td.addColumn(ArrayColumnDesc(colname,"")); break; default: cerr << "Error:: column " << i << " has impossible type for variable array descriptor " << vatypes_p[i] << endl; break; } } break; // NOVALUE should not happen in a table default: cerr << "Error: column " << i << " has untranslatable type " << field(i).fieldtype() << " This should NEVER happen " << endl; continue; } // end of switch on FITS type // set the comment string if appropriate if (kwl(FITS::TTYPE, i)) td.rwColumnDesc(colname).comment() = kwl(FITS::TTYPE,i)->comm(); // for 0 element fields, set ZEROELEM keyword, Bool=True if (nelem[i]==0) td.rwColumnDesc(colname).rwKeywordSet().define("ZEROELEM", True); // attach associated information // Units td.rwColumnDesc(colname).rwKeywordSet().define("TUNIT", tunit(i)); // display format td.rwColumnDesc(colname).rwKeywordSet().define("TDISP", tunit(i)); // TDIM array dimension convention, this should really be // done above !! td.rwColumnDesc(colname).rwKeywordSet().define("TDIM", tdim(i)); // For integer types, add TNULL, gives the undefined value // for integer types if (field(i).fieldtype() == FITS::SHORT || field(i).fieldtype() == FITS::LONG) td.rwColumnDesc(colname).rwKeywordSet().define("TNULL", tnull(i)); } // end of loop over columns // add the virtual columns if sdfits is true // all of these should be scalars if (sdfits) { // first, remove duplicates - true columns take precedence Vector duplicates(kwSet.nfields()); uInt count = 0; uInt field; for (field=0;field(kwSet.name(field),kwSet.comment(field))); break; case TpUChar: td.addColumn(ScalarColumnDesc(kwSet.name(field),kwSet.comment(field))); break; case TpShort: td.addColumn(ScalarColumnDesc(kwSet.name(field),kwSet.comment(field))); break; case TpInt: td.addColumn(ScalarColumnDesc(kwSet.name(field),kwSet.comment(field))); break; case TpUInt: td.addColumn(ScalarColumnDesc(kwSet.name(field),kwSet.comment(field))); break; case TpFloat: td.addColumn(ScalarColumnDesc(kwSet.name(field),kwSet.comment(field))); break; case TpDouble: td.addColumn(ScalarColumnDesc(kwSet.name(field),kwSet.comment(field))); break; case TpComplex: td.addColumn(ScalarColumnDesc(kwSet.name(field),kwSet.comment(field))); break; case TpDComplex: td.addColumn(ScalarColumnDesc(kwSet.name(field),kwSet.comment(field))); break; case TpString: td.addColumn(ScalarColumnDesc(kwSet.name(field),kwSet.comment(field))); break; default: throw(AipsError("Impossible virtual column type")); break; } } } else { // no virtual columns, put any keywords in td and // clean out kwSet td.rwKeywordSet().merge(kwSet,RecordInterface::RenameDuplicates); RecordDesc emptyDesc; kwSet.restructure(emptyDesc); } // now, create currRowTable SetupNewTable newtab("", td, Table::Scratch); if (useIncrSM) { IncrementalStMan stman ("ISM"); newtab.bindAll(stman); } // and finally create the Table currRowTab = new Table(newtab, Table::Memory, 1); // OK, fill the one row of CurrRowTab if (nrows() > 0) { // if we don't have a heap, read a row if (!theheap_p) read(1); fillRow(); } } void BinaryTable::fillRow() { // loop over each field for (Int j=0;j thisfield = *(FitsField *)&field(j); Vector vec(nelem[j]); for (Int k=0;k 1) { ArrayColumn arrcol(tabcol); arrcol.put(0,vec); } else if (nelem[j] == 1) { tabcol.putScalar(0,vec(0)); } } break; case FITS::BIT: { FitsField thisfield = *(FitsField *)&field(j); Vector vec(nelem[j]); for (uInt k=0;k 1) { ArrayColumn arrcol(tabcol); arrcol.put(0,vec); } else if (nelem[j] == 1) { tabcol.putScalar(0,vec(0)); } } break; case FITS::BYTE: { FitsField thisfield = *(FitsField *)&field(j); Vector vec(nelem[j]); for (Int k=0;k 1) { ArrayColumn arrcol(tabcol); arrcol.put(0,vec); } else if (nelem[j] == 1) { tabcol.putScalar(0,vec(0)); } } break; case FITS::CHAR: case FITS::STRING: { FitsField thisfield = *(FitsField *)&field(j); // look for the true end of the string char * cptr = (char *)thisfield.data(); uInt length = thisfield.nelements(); while (length > 0 && (cptr[length-1] == '\0' || cptr[length-1] == ' ')) { length--; } tabcol.putScalar(0,String(cptr, length)); } break; case FITS::SHORT: { FitsField thisfield = *(FitsField *)&field(j); Vector vec(nelem[j]); for (Int k=0;k 1) { ArrayColumn arrcol(tabcol); arrcol.put(0,vec); } else if (nelem[j] == 1) { tabcol.putScalar(0,vec(0)); } } break; case FITS::LONG: { FitsField thisfield = *(FitsField *)&field(j); Vector vec(nelem[j]); for (Int k=0;k 1) { ArrayColumn arrcol(tabcol); arrcol.put(0,vec); } else if (nelem[j] == 1) { tabcol.putScalar(0,vec(0)); } } break; case FITS::FLOAT: { FitsField thisfield = *(FitsField *)&field(j); Vector vec(nelem[j]); for (Int k=0;k dvec(nelem[j]); convertArray(dvec, vec); dvec *= tscal(j); dvec += tzero(j); convertArray(vec, dvec); } else if (tzero(j) != 0) { vec += (Float )tzero(j); } if (nelem[j] > 1) { ArrayColumn arrcol(tabcol); arrcol.put(0,vec); } else if (nelem[j] == 1) { tabcol.putScalar(0,vec(0)); } } break; case FITS::DOUBLE: { FitsField thisfield = *(FitsField *)&field(j); Vector vec(nelem[j]); for (Int k=0;k 1) { ArrayColumn arrcol(tabcol); arrcol.put(0,vec); } else if (nelem[j] == 1) { tabcol.putScalar(0,vec(0)); } } break; case FITS::COMPLEX: { FitsField thisfield = *(FitsField *)&field(j); Vector vec(nelem[j]); for (Int k=0;k 1) { ArrayColumn arrcol(tabcol); arrcol.put(0,vec); } else if (nelem[j] == 1) { tabcol.putScalar(0,vec(0)); } } break; case FITS::DCOMPLEX: { FitsField thisfield = *(FitsField *)&field(j); Vector vec(nelem[j]); for (Int k=0;k 1) { ArrayColumn arrcol(tabcol); arrcol.put(0,vec); } else if (nelem[j] == 1) { tabcol.putScalar(0,vec(0)); } } break; case FITS::ICOMPLEX: { FitsField thisfield = *(FitsField *)&field(j); Vector vec(nelem[j]); for (Int k=0;k 1) { ArrayColumn arrcol(tabcol); arrcol.put(0,vec); } else if (nelem[j] == 1) { tabcol.putScalar(0,vec(0)); } } break; case FITS::VADESC: { FitsVADesc thisva = va_p[j](); // its a pity so many copies seem to be necessary // one to copy the heap into the local version of the // desired type // the second to hold and scale the values in a Casacore type // and finally the copy actually placed in the table switch (vatypes_p[j]) { case FITS::LOGICAL: { FitsLogical *vptr = (FitsLogical *)(vaptr_p[j]); FITS::f2l(vptr, (void *)(theheap_p + thisva.offset()), thisva.num()); Vector vec(thisva.num()); for (Int k=0;k arrcol(tabcol); arrcol.put(0,vec); } break; case FITS::BIT: { uChar *vptr = (uChar *)(vaptr_p[j]); FITS::f2l(vptr, (void *)(theheap_p + thisva.offset()), thisva.num()); // assumes 8 bits per uChar Int whichByte = -1; Vector vec(thisva.num()); uChar mask = 0200; for (Int k=0;k> k%8)); } ArrayColumn arrcol(tabcol); arrcol.put(0,vec); } break; case FITS::BYTE: { uChar *vptr = (uChar *)(vaptr_p[j]); FITS::f2l(vptr, (void *)(theheap_p + thisva.offset()), thisva.num()); Vector vec(thisva.num()); for (Int k=0;k arrcol(tabcol); arrcol.put(0,vec); } break; case FITS::CHAR: { Char *vptr = (Char *)(vaptr_p[j]); FITS::f2l(vptr, (void *)(theheap_p + thisva.offset()), thisva.num()); tabcol.putScalar(0,String(vptr, thisva.num())); } break; case FITS::SHORT: { Short *vptr = (Short *)(vaptr_p[j]); FITS::f2l(vptr, (void *)(theheap_p + thisva.offset()), thisva.num()); Vector vec(thisva.num()); for (Int k=0;k arrcol(tabcol); arrcol.put(0,vec); } break; case FITS::LONG: { FitsLong *vptr = (FitsLong *)(vaptr_p[j]); FITS::f2l(vptr, (void *)(theheap_p + thisva.offset()), thisva.num()); Vector vec(thisva.num()); for (Int k=0;k arrcol(tabcol); arrcol.put(0,vec); } break; case FITS::FLOAT: { Float *vptr = (Float *)(vaptr_p[j]); FITS::f2l(vptr, (void *)(theheap_p + thisva.offset()), thisva.num()); Vector vec(thisva.num()); for (Int k=0;k dvec(thisva.num()); convertArray(dvec, vec); dvec *= tscal(j); dvec += tzero(j); convertArray(vec, dvec); } else if (tzero(j) != 0) { vec += (Float )tzero(j); } ArrayColumn arrcol(tabcol); arrcol.put(0,vec); } break; case FITS::DOUBLE: { Double *vptr = (Double *)(vaptr_p[j]); FITS::f2l(vptr, (void *)(theheap_p + thisva.offset()), thisva.num()); Vector vec(thisva.num()); for (Int k=0;k arrcol(tabcol); arrcol.put(0,vec); } break; case FITS::COMPLEX: { Complex *vptr = (Complex *)(vaptr_p[j]); FITS::f2l(vptr, (void *)(theheap_p + thisva.offset()), thisva.num()); Vector vec(thisva.num()); for (Int k=0;k arrcol(tabcol); arrcol.put(0,vec); } break; case FITS::DCOMPLEX: { DComplex *vptr = (DComplex *)(vaptr_p[j]); FITS::f2l(vptr, (void *)(theheap_p + thisva.offset()), thisva.num()); Vector vec(thisva.num()); for (Int k=0;k arrcol(tabcol); arrcol.put(0,vec); } break; default: cerr << "Error unrecognized variable array type for field " << j << " type : " << vatypes_p[j] << endl; break; } } break; default: // NOVALUE (which shouldn't occur here cerr << "Error: unrecognized table data type for field " << j << endl; cerr << "That should not have happened" << endl; continue; } // end of loop over switch } // end of loop over fields // loop over all virtual columns if necessary if (kwSet.nfields() > 0) { for (uInt field=0;fieldtableDesc(); } TableRecord& BinaryTable::getKeywords() { return currRowTab->rwKeywordSet(); } const Table &BinaryTable::thisRow() { return (*currRowTab); } const Table &BinaryTable::nextRow() { // here, its user beware in reading past end of table // i.e. just the same way FITS works. if (!theheap_p) read(1); else ++(*this); fillRow(); return (*currRowTab); } } //# NAMESPACE CASACORE - END casacore-3.7.1/fits/FITS/BinTable.h000066400000000000000000000154741476623553700167020ustar00rootroot00000000000000//# BinTable.h: The class BinaryTable converts a FITS binary table into a Casacore Table. //# Copyright (C) 1995,1996,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef FITS_BINTABLE_H #define FITS_BINTABLE_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // BinaryTable is used to translate a FITS binary table to a Casacore Table. // // // // //# Classes you should understand before using this one. //
      • FitsInput //
      • HeaderDataUnit //
      • BinaryTableExtension //
      • Tables module // // // // BinaryTable inherits from the FITS BinaryTableExtension class and its // primary use is to convert that class to a Casacore Table. This explains // it's use but not its name. A better name should be found. // // // // The class starts with an already existing FitsInput object, which should // be set at a BinaryTableExtension HDU. Member functions provide a TableDesc // appropriate for the FITS data (to help in constructing a Casacore Table // compatible with the BinaryTableExtension), a Table containing the // current row of FITS data and a Table containing the next row of FITS data // (which can be used to step through the FitsInput, copying each row // using the RowCopier class), and a Table containin the entire FITS binary // table from the current row to the end of the table. // // // // We need a way to get FITS data into Casacore Tables. // // // // open a FitsInput from a disk file, if the HDU is a BinaryTableExtension, // then instantiate a BinTable object and get the entire table. A fair // amount of error checking has been eliminated from this example. // // FitsInput infits("myFITSFile", FITS::Disk); // switch (infits.hdutype()) { // case FITS::BinaryTableHDU: // BinaryTable bintab(infits); // Table tab = bintable.fullTable("myTable"); // break; // } // // There would obviously be other cases to the switch to deal with any // other HDUs (e.g. skip them via infits.skip_hdu()). The Table destructor // would write "myTable" to disk. // // // // //# A List of bugs, limitations, extensions or planned refinements. //
      • It would be nice to construct this directly from the BinaryTableExtension. //
      • When random access FITS becomes available, this needs to be able to deal with that. //
      • A corresponding class is needed for conversion from Casacore Tables to FITS. //
      • Throw exceptions rather than send messages to cout : however the entire FITS // module behaves this way, so it should all remain consistent. //
      • The following types of columns are not dealt with very well or at all // (Bit, Byte, 0-length columns). //
      • No attempt use any TDIM columns or keywords to shape arrays. // class BinaryTable : public BinaryTableExtension { public: // The only constructor is from a FitsInput, you can also optionally // provide a FITS error handler. If useMiriadSM is True, use // the Miriad storage manager for all columns, otherwise AipsIO. // If sdfits is True, all non-reserved and some reserved keyword // are treated as if they were columns with constant values // "virtual columns" in the sdfits convention. BinaryTable(FitsInput &, FITSErrorHandler errhandler = FITSError::defaultHandler, Bool useMiriadSM = False, Bool sdfits = False); ~BinaryTable(); // Get the full table, using the supplied arguments to construct the table. // The table will contain all data from the current row to the end of the // BinarTableExtension.If useMiriadSM is True, use the Miriad storage // manager for all columns, otherwise AipsIO. Table fullTable(const String& tabName, const Table::TableOption = Table::NewNoReplace, Bool useMiriadSM = False); // This version of the fullTable return a Memory based table // Its recommended if its being used as a temporary Table fullTable(); // Get an appropriate TableDesc (this is the same TableDesc used to // construct any Table objects returned by this class. const TableDesc& getDescriptor(); // Return the Table keywords (this is the same TableRecord used // in any Table objects returned by this class. TableRecord& getKeywords(); // Get a Table with a single row, the current row of the FITS table. // The returned Table is a Scratch table. // The standard BinaryTableExtension manipulation functions are // available to position the FITS input at the desired location. const Table &thisRow(); // Get a Table with a single row, the next row of the FITS table. // The returned Table is a Scratch table. // The FITS input is positioned to the next row and the values translated // and returned in a Table object. const Table &nextRow(); private: //# Data Members // This is the Scratch table containing the current row Table* currRowTab; // The number of elements for each column of the BinaryTableExtension Int *nelem; // This is a map from column number to column name std::map *colNames; TableRecord kwSet; // These are used by any VADesc columns FITS::ValueType *vatypes_p; void **vaptr_p; VADescFitsField *va_p; char *theheap_p; // this is the function that fills each row in as needed void fillRow(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/fits/FITS/CopyRecord.cc000066400000000000000000001053421476623553700174230ustar00rootroot00000000000000//# CopyRecord.cc: definition of CopyRecordToTable //# Copyright (C) 1995,1996,1997,1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN CopyRecordToTable::CopyRecordToTable(Table &outputTable, const RecordInterface &inputBuffer, const Vector inputMap) { Block counts(TpNumberOfTypes); counts.set(0); // Count how many fields of each type exist uInt n = inputBuffer.nfields(); uInt i; for (i=0; i < n; i++) { if (inputMap(i) != -1) counts[inputBuffer.description().type(i)]++; } uInt total = 0; table_bool.resize(counts[TpBool]); record_bool.resize(counts[TpBool]); total += counts[TpBool]; table_char.resize(counts[TpUChar]); record_char.resize(counts[TpUChar]); total += counts[TpUChar]; table_short.resize(counts[TpShort]); record_short.resize(counts[TpShort]); total += counts[TpShort]; table_int.resize(counts[TpInt]); record_int.resize(counts[TpInt]); total += counts[TpInt]; table_float.resize(counts[TpFloat]); record_float.resize(counts[TpFloat]); total += counts[TpFloat]; table_double.resize(counts[TpDouble]); record_double.resize(counts[TpDouble]); total += counts[TpDouble]; table_complex.resize(counts[TpComplex]); record_complex.resize(counts[TpComplex]); total += counts[TpComplex]; table_dcomplex.resize(counts[TpDComplex]); record_dcomplex.resize(counts[TpDComplex]); total += counts[TpDComplex]; table_string.resize(counts[TpString]); record_string.resize(counts[TpString]); total += counts[TpString]; table_array_bool.resize(counts[TpArrayBool]); record_array_bool.resize(counts[TpArrayBool]); total += counts[TpArrayBool]; table_array_char.resize(counts[TpArrayUChar]); record_array_char.resize(counts[TpArrayUChar]); total += counts[TpArrayUChar]; table_array_short.resize(counts[TpArrayShort]); record_array_short.resize(counts[TpArrayShort]); total += counts[TpArrayShort]; table_array_int.resize(counts[TpArrayInt]); record_array_int.resize(counts[TpArrayInt]); total += counts[TpArrayInt]; table_array_float.resize(counts[TpArrayFloat]); record_array_float.resize(counts[TpArrayFloat]); total += counts[TpArrayFloat]; table_array_double.resize(counts[TpArrayDouble]); record_array_double.resize(counts[TpArrayDouble]); total += counts[TpArrayDouble]; table_array_complex.resize(counts[TpArrayComplex]); record_array_complex.resize(counts[TpArrayComplex]); total += counts[TpArrayComplex]; table_array_dcomplex.resize(counts[TpArrayDComplex]); record_array_dcomplex.resize(counts[TpArrayDComplex]); total += counts[TpArrayDComplex]; table_array_string.resize(counts[TpArrayString]); record_array_string.resize(counts[TpArrayString]); total += counts[TpArrayString]; // Keeps track of what index we're writing into for each block. Block where(TpNumberOfTypes); Vector colnames(outputTable.tableDesc().columnNames()); where.set(0); for (i=0; i < inputMap.nelements(); i++) { if (inputMap(i) != -1) { uInt which = where[inputBuffer.description().type(i)]; switch(inputBuffer.description().type(i)) { case TpBool: record_bool[which].attachToRecord(inputBuffer, i); table_bool[which] = new ScalarColumn(outputTable, colnames(inputMap(i))); AlwaysAssert(table_bool[which] != 0, AipsError); break; case TpUChar: record_char[which].attachToRecord(inputBuffer, i); table_char[which] = new ScalarColumn(outputTable, colnames(inputMap(i))); AlwaysAssert(table_char[which] != 0, AipsError); break; case TpShort: record_short[which].attachToRecord(inputBuffer, i); table_short[which] = new ScalarColumn(outputTable, colnames(inputMap(i))); AlwaysAssert(table_short[which] != 0, AipsError); break; case TpInt: record_int[which].attachToRecord(inputBuffer, i); table_int[which] = new ScalarColumn(outputTable, colnames(inputMap(i))); AlwaysAssert(table_int[which] != 0, AipsError); break; case TpFloat: record_float[which].attachToRecord(inputBuffer, i); table_float[which] = new ScalarColumn(outputTable, colnames(inputMap(i))); AlwaysAssert(table_float[which] != 0, AipsError); break; case TpDouble: record_double[which].attachToRecord(inputBuffer, i); table_double[which] = new ScalarColumn(outputTable, colnames(inputMap(i))); AlwaysAssert(table_double[which] != 0, AipsError); break; case TpComplex: record_complex[which].attachToRecord(inputBuffer, i); table_complex[which] = new ScalarColumn(outputTable, colnames(inputMap(i))); AlwaysAssert(table_complex[which] != 0, AipsError); break; case TpDComplex: record_dcomplex[which].attachToRecord(inputBuffer, i); table_dcomplex[which] = new ScalarColumn(outputTable, colnames(inputMap(i))); AlwaysAssert(table_dcomplex[which] != 0, AipsError); break; case TpString: record_string[which].attachToRecord(inputBuffer, i); table_string[which] = new ScalarColumn(outputTable, colnames(inputMap(i))); AlwaysAssert(table_string[which] != 0, AipsError); break; case TpArrayBool: record_array_bool[which].attachToRecord(inputBuffer, i); table_array_bool[which] = new ArrayColumn(outputTable, colnames(inputMap(i))); AlwaysAssert(table_array_bool[which] != 0, AipsError); break; case TpArrayUChar: record_array_char[which].attachToRecord(inputBuffer, i); table_array_char[which] = new ArrayColumn(outputTable, colnames(inputMap(i))); AlwaysAssert(table_array_char[which] != 0, AipsError); break; case TpArrayShort: record_array_short[which].attachToRecord(inputBuffer, i); table_array_short[which] = new ArrayColumn(outputTable, colnames(inputMap(i))); AlwaysAssert(table_array_short[which] != 0, AipsError); break; case TpArrayInt: record_array_int[which].attachToRecord(inputBuffer, i); table_array_int[which] = new ArrayColumn(outputTable, colnames(inputMap(i))); AlwaysAssert(table_array_int[which] != 0, AipsError); break; case TpArrayFloat: record_array_float[which].attachToRecord(inputBuffer, i); table_array_float[which] = new ArrayColumn(outputTable, colnames(inputMap(i))); AlwaysAssert(table_array_float[which] != 0, AipsError); break; case TpArrayDouble: record_array_double[which].attachToRecord(inputBuffer, i); table_array_double[which] = new ArrayColumn(outputTable, colnames(inputMap(i))); AlwaysAssert(table_array_double[which] != 0, AipsError); break; case TpArrayComplex: record_array_complex[which].attachToRecord(inputBuffer, i); table_array_complex[which] = new ArrayColumn(outputTable, colnames(inputMap(i))); AlwaysAssert(table_array_complex[which] != 0, AipsError); break; case TpArrayDComplex: record_array_dcomplex[which].attachToRecord(inputBuffer, i); table_array_dcomplex[which] = new ArrayColumn(outputTable, colnames(inputMap(i))); AlwaysAssert(table_array_dcomplex[which] != 0, AipsError); break; case TpArrayString: record_array_string[which].attachToRecord(inputBuffer, i); table_array_string[which] = new ArrayColumn(outputTable, colnames(inputMap(i))); AlwaysAssert(table_array_string[which] != 0, AipsError); break; default: throw(AipsError( "CopyRecordToTable::CopyRecordToTable - unknown type")); } where[inputBuffer.description().type(i)]++; } } } CopyRecordToTable::CopyRecordToTable(const CopyRecordToTable &other) { *this = other; } CopyRecordToTable::~CopyRecordToTable() { clearAll(); } CopyRecordToTable &CopyRecordToTable::operator=(const CopyRecordToTable &other) { if (this != &other) { clearAll(); table_bool.resize(other.table_bool.nelements(), True); record_bool.resize(other.record_bool.nelements(), True); for (uInt i=0;i(*(other.table_bool[i])); record_bool[i] = other.record_bool[i]; AlwaysAssert(table_bool[i], AipsError); } table_char.resize(other.table_char.nelements(), True); record_char.resize(other.record_char.nelements(), True); for (uInt i=0;i(*(other.table_char[i])); record_char[i] = other.record_char[i]; AlwaysAssert(table_char[i], AipsError); } table_short.resize(other.table_short.nelements(), True); record_short.resize(other.record_short.nelements(), True); for (uInt i=0;i(*(other.table_short[i])); record_short[i] = other.record_short[i]; AlwaysAssert(table_short[i], AipsError); } table_int.resize(other.table_int.nelements(), True); record_int.resize(other.record_int.nelements(), True); for (uInt i=0;i(*(other.table_int[i])); record_int[i] = other.record_int[i]; AlwaysAssert(table_int[i], AipsError); } table_float.resize(other.table_float.nelements(), True); record_float.resize(other.record_float.nelements(), True); for (uInt i=0;i(*(other.table_float[i])); record_float[i] = other.record_float[i]; AlwaysAssert(table_float[i], AipsError); } table_double.resize(other.table_double.nelements(), True); record_double.resize(other.record_double.nelements(), True); for (uInt i=0;i(*(other.table_double[i])); record_double[i] = other.record_double[i]; AlwaysAssert(table_double[i], AipsError); } table_complex.resize(other.table_complex.nelements(), True); record_complex.resize(other.record_complex.nelements(), True); for (uInt i=0;i(*(other.table_complex[i])); record_complex[i] = other.record_complex[i]; AlwaysAssert(table_complex[i], AipsError); } table_dcomplex.resize(other.table_dcomplex.nelements(), True); record_dcomplex.resize(other.record_dcomplex.nelements(), True); for (uInt i=0;i(*(other.table_dcomplex[i])); record_dcomplex[i] = other.record_dcomplex[i]; AlwaysAssert(table_dcomplex[i], AipsError); } table_string.resize(other.table_string.nelements(), True); record_string.resize(other.record_string.nelements(), True); for (uInt i=0;i(*(other.table_string[i])); record_string[i] = other.record_string[i]; AlwaysAssert(table_string[i], AipsError); } table_array_bool.resize(other.table_array_bool.nelements(), True); record_array_bool.resize(other.record_array_bool.nelements(), True); for (uInt i=0;i(*(other.table_array_bool[i])); record_array_bool[i] = other.record_array_bool[i]; AlwaysAssert(table_array_bool[i], AipsError); } table_array_char.resize(other.table_array_char.nelements(), True); record_array_char.resize(other.record_array_char.nelements(), True); for (uInt i=0;i(*(other.table_array_char[i])); record_array_char[i] = other.record_array_char[i]; AlwaysAssert(table_array_char[i], AipsError); } table_array_short.resize(other.table_array_short.nelements(), True); record_array_short.resize(other.record_array_short.nelements(), True); for (uInt i=0;i(*(other.table_array_short[i])); record_array_short[i] = other.record_array_short[i]; AlwaysAssert(table_array_short[i], AipsError); } table_array_int.resize(other.table_array_int.nelements(), True); record_array_int.resize(other.record_array_int.nelements(), True); for (uInt i=0;i(*(other.table_array_int[i])); record_array_int[i] = other.record_array_int[i]; AlwaysAssert(table_array_int[i], AipsError); } table_array_float.resize(other.table_array_float.nelements(), True); record_array_float.resize(other.record_array_float.nelements(), True); for (uInt i=0;i(*(other.table_array_float[i])); record_array_float[i] = other.record_array_float[i]; AlwaysAssert(table_array_float[i], AipsError); } table_array_double.resize(other.table_array_double.nelements(), True); record_array_double.resize(other.record_array_double.nelements(), True); for (uInt i=0;i(*(other.table_array_double[i])); record_array_double[i] = other.record_array_double[i]; AlwaysAssert(table_array_double[i], AipsError); } table_array_complex.resize(other.table_array_complex.nelements(), True); record_array_complex.resize(other.record_array_complex.nelements(), True); for (uInt i=0;i(*(other.table_array_complex[i])); record_array_complex[i] = other.record_array_complex[i]; AlwaysAssert(table_array_complex[i], AipsError); } table_array_dcomplex.resize(other.table_array_dcomplex.nelements(), True); record_array_dcomplex.resize(other.record_array_dcomplex.nelements(), True); for (uInt i=0;i(*(other.table_array_dcomplex[i])); record_array_dcomplex[i] = other.record_array_dcomplex[i]; AlwaysAssert(table_array_dcomplex[i], AipsError); } table_array_string.resize(other.table_array_string.nelements(), True); record_array_string.resize(other.record_array_string.nelements(), True); for (uInt i=0;i(*(other.table_array_string[i])); record_array_string[i] = other.record_array_string[i]; AlwaysAssert(table_array_string[i], AipsError); } } return *this; } void CopyRecordToTable::copy(uInt rownr) { uInt i; for (i=0; i < table_bool.nelements(); i++) { table_bool[i]->put(rownr, *(record_bool[i])); } for (i=0; i < table_char.nelements(); i++) { table_char[i]->put(rownr, *(record_char[i])); } for (i=0; i < table_short.nelements(); i++) { table_short[i]->put(rownr, *(record_short[i])); } for (i=0; i < table_int.nelements(); i++) { table_int[i]->put(rownr, *(record_int[i])); } for (i=0; i < table_float.nelements(); i++) { table_float[i]->put(rownr, *(record_float[i])); } for (i=0; i < table_double.nelements(); i++) { table_double[i]->put(rownr, *(record_double[i])); } for (i=0; i < table_complex.nelements(); i++) { table_complex[i]->put(rownr, *(record_complex[i])); } for (i=0; i < table_dcomplex.nelements(); i++) { table_dcomplex[i]->put(rownr, *(record_dcomplex[i])); } for (i=0; i < table_string.nelements(); i++) { table_string[i]->put(rownr, *(record_string[i])); } for (i=0; i < table_array_bool.nelements(); i++) { table_array_bool[i]->put(rownr, *(record_array_bool[i])); } for (i=0; i < table_array_char.nelements(); i++) { table_array_char[i]->put(rownr, *(record_array_char[i])); } for (i=0; i < table_array_short.nelements(); i++) { table_array_short[i]->put(rownr, *(record_array_short[i])); } for (i=0; i < table_array_int.nelements(); i++) { table_array_int[i]->put(rownr, *(record_array_int[i])); } for (i=0; i < table_array_float.nelements(); i++) { table_array_float[i]->put(rownr, *(record_array_float[i])); } for (i=0; i < table_array_double.nelements(); i++) { table_array_double[i]->put(rownr, *(record_array_double[i])); } for (i=0; i < table_array_complex.nelements(); i++) { table_array_complex[i]->put(rownr, *(record_array_complex[i])); } for (i=0; i < table_array_dcomplex.nelements(); i++) { table_array_dcomplex[i]->put(rownr, *(record_array_dcomplex[i])); } for (i=0; i < table_array_string.nelements(); i++) { table_array_string[i]->put(rownr, *(record_array_string[i])); } } void CopyRecordToTable::clearAll() { uInt i; for (i=0; i < table_bool.nelements(); i++) { delete table_bool[i]; } table_bool.set(static_cast*>(0)); for (i=0; i < table_char.nelements(); i++) { delete table_char[i]; } table_char.set(static_cast*>(0)); for (i=0; i < table_short.nelements(); i++) { delete table_short[i]; } table_short.set(static_cast*>(0)); for (i=0; i < table_int.nelements(); i++) { delete table_int[i]; } table_int.set(static_cast*>(0)); for (i=0; i < table_float.nelements(); i++) { delete table_float[i]; } table_float.set(static_cast*>(0)); for (i=0; i < table_double.nelements(); i++) { delete table_double[i]; } table_double.set(static_cast*>(0)); for (i=0; i < table_complex.nelements(); i++) { delete table_complex[i]; } table_complex.set(static_cast*>(0)); for (i=0; i < table_dcomplex.nelements(); i++) { delete table_dcomplex[i]; } table_dcomplex.set(static_cast*>(0)); for (i=0; i < table_string.nelements(); i++) { delete table_string[i]; } table_string.set(static_cast*>(0)); for (i=0; i < table_array_bool.nelements(); i++) { delete table_array_bool[i]; } table_array_bool.set(static_cast*>(0)); for (i=0; i < table_array_char.nelements(); i++) { delete table_array_char[i]; } table_array_char.set(static_cast*>(0)); for (i=0; i < table_array_short.nelements(); i++) { delete table_array_short[i]; } table_array_short.set(static_cast*>(0)); for (i=0; i < table_array_int.nelements(); i++) { delete table_array_int[i]; } table_array_int.set(static_cast*>(0)); for (i=0; i < table_array_float.nelements(); i++) { delete table_array_float[i]; } table_array_float.set(static_cast*>(0)); for (i=0; i < table_array_double.nelements(); i++) { delete table_array_double[i]; } table_array_double.set(static_cast*>(0)); for (i=0; i < table_array_complex.nelements(); i++) { delete table_array_complex[i]; } table_array_complex.set(static_cast*>(0)); for (i=0; i < table_array_dcomplex.nelements(); i++) { delete table_array_dcomplex[i]; } table_array_dcomplex.set(static_cast*>(0)); for (i=0; i < table_array_string.nelements(); i++) { delete table_array_string[i]; } table_array_string.set(static_cast*>(0)); } void addRecordDesc(TableDesc &tableDescription, const RecordDesc &recDesc, const String &prefix) { uInt n = recDesc.nfields(); String fullPrefix = prefix; if (! prefix.empty()) { fullPrefix = prefix + "_"; } // variable length arrays will likely be identified by a neg. shape // but for now, we fake it out with an IPosition involving zeros // IPosition varShape(1,-1); for (uInt i=0; i < n; i++) { String colname = fullPrefix + recDesc.name(i); if (recDesc.isScalar(i)) { switch(recDesc.type(i)) { case TpBool: tableDescription.addColumn(ScalarColumnDesc(colname)); break; case TpUChar: tableDescription.addColumn(ScalarColumnDesc(colname)); break; case TpShort: tableDescription.addColumn(ScalarColumnDesc(colname)); break; case TpInt: tableDescription.addColumn(ScalarColumnDesc(colname)); break; case TpFloat: tableDescription.addColumn(ScalarColumnDesc(colname)); break; case TpDouble: tableDescription.addColumn(ScalarColumnDesc(colname)); break; case TpComplex: tableDescription.addColumn(ScalarColumnDesc(colname)); break; case TpDComplex: tableDescription.addColumn(ScalarColumnDesc(colname)); break; case TpString: tableDescription.addColumn(ScalarColumnDesc(colname)); break; default: AlwaysAssertExit(0); // NOTREACHED } } else if (recDesc.isArray(i)) { int options = 0; // if (recDesc.shape(i) != varShape) options = ColumnDesc::Direct; if (recDesc.shape(i).product() != 0) options = ColumnDesc::Direct; switch(recDesc.type(i)) { case TpArrayBool: if (options != 0) { tableDescription.addColumn(ArrayColumnDesc(colname, recDesc.shape(i), options)); } else { tableDescription.addColumn(ArrayColumnDesc(colname, options)); } break; case TpArrayUChar: if (options != 0) { tableDescription.addColumn(ArrayColumnDesc(colname, recDesc.shape(i), options)); } else { tableDescription.addColumn(ArrayColumnDesc(colname, options)); } break; case TpArrayShort: if (options != 0) { tableDescription.addColumn(ArrayColumnDesc(colname, recDesc.shape(i), options)); } else { tableDescription.addColumn(ArrayColumnDesc(colname, options)); } break; case TpArrayInt: if (options != 0) { tableDescription.addColumn(ArrayColumnDesc(colname, recDesc.shape(i), options)); } else { tableDescription.addColumn(ArrayColumnDesc(colname, options)); } break; case TpArrayFloat: if (options != 0) { tableDescription.addColumn(ArrayColumnDesc(colname, recDesc.shape(i), options)); } else { tableDescription.addColumn(ArrayColumnDesc(colname, options)); } break; case TpArrayDouble: if (options != 0) { tableDescription.addColumn(ArrayColumnDesc(colname, recDesc.shape(i), options)); } else { tableDescription.addColumn(ArrayColumnDesc(colname, options)); } break; case TpArrayComplex: if (options != 0) { tableDescription.addColumn(ArrayColumnDesc(colname, recDesc.shape(i), options)); } else { tableDescription.addColumn(ArrayColumnDesc(colname, options)); } break; case TpArrayDComplex: if (options != 0) { tableDescription.addColumn(ArrayColumnDesc(colname, recDesc.shape(i), options)); } else { tableDescription.addColumn(ArrayColumnDesc(colname, options)); } break; case TpArrayString: if (options != 0) { tableDescription.addColumn(ArrayColumnDesc(colname, recDesc.shape(i), options)); } else { tableDescription.addColumn(ArrayColumnDesc(colname, options)); } break; default: AlwaysAssertExit(0); // NOTREACHED } } else { AlwaysAssertExit(0); // NOTREACHED } } } CopyRecordToRecord::CopyRecordToRecord(RecordInterface &outputBuffer, const RecordInterface &inputBuffer, const Vector inputMap) { Block counts(TpNumberOfTypes); counts.set(0); // Count how many fields of each type exist uInt n = inputBuffer.nfields(); uInt i; for (i=0; i < n; i++) { if (inputMap(i) != -1) counts[inputBuffer.description().type(i)]++; } uInt total = 0; out_record_bool.resize(counts[TpBool]); in_record_bool.resize(counts[TpBool]); total += counts[TpBool]; out_record_char.resize(counts[TpUChar]); in_record_char.resize(counts[TpUChar]); total += counts[TpUChar]; out_record_short.resize(counts[TpShort]); in_record_short.resize(counts[TpShort]); total += counts[TpShort]; out_record_int.resize(counts[TpInt]); in_record_int.resize(counts[TpInt]); total += counts[TpInt]; out_record_float.resize(counts[TpFloat]); in_record_float.resize(counts[TpFloat]); total += counts[TpFloat]; out_record_double.resize(counts[TpDouble]); in_record_double.resize(counts[TpDouble]); total += counts[TpDouble]; out_record_complex.resize(counts[TpComplex]); in_record_complex.resize(counts[TpComplex]); total += counts[TpComplex]; out_record_dcomplex.resize(counts[TpDComplex]); in_record_dcomplex.resize(counts[TpDComplex]); total += counts[TpDComplex]; out_record_string.resize(counts[TpString]); in_record_string.resize(counts[TpString]); total += counts[TpString]; out_record_array_bool.resize(counts[TpArrayBool]); in_record_array_bool.resize(counts[TpArrayBool]); total += counts[TpArrayBool]; out_record_array_char.resize(counts[TpArrayUChar]); in_record_array_char.resize(counts[TpArrayUChar]); total += counts[TpArrayUChar]; out_record_array_short.resize(counts[TpArrayShort]); in_record_array_short.resize(counts[TpArrayShort]); total += counts[TpArrayShort]; out_record_array_int.resize(counts[TpArrayInt]); in_record_array_int.resize(counts[TpArrayInt]); total += counts[TpArrayInt]; out_record_array_float.resize(counts[TpArrayFloat]); in_record_array_float.resize(counts[TpArrayFloat]); total += counts[TpArrayFloat]; out_record_array_double.resize(counts[TpArrayDouble]); in_record_array_double.resize(counts[TpArrayDouble]); total += counts[TpArrayDouble]; out_record_array_complex.resize(counts[TpArrayComplex]); in_record_array_complex.resize(counts[TpArrayComplex]); total += counts[TpArrayComplex]; out_record_array_dcomplex.resize(counts[TpArrayDComplex]); in_record_array_dcomplex.resize(counts[TpArrayDComplex]); total += counts[TpArrayDComplex]; out_record_array_string.resize(counts[TpArrayString]); in_record_array_string.resize(counts[TpArrayString]); total += counts[TpArrayString]; // Keeps track of what index we're writing into for each block. Block where(TpNumberOfTypes); where.set(0); for (i=0; i < inputMap.nelements(); i++) { if (inputMap(i) != -1) { uInt which = where[inputBuffer.description().type(i)]; switch(inputBuffer.description().type(i)) { case TpBool: in_record_bool[which].attachToRecord(inputBuffer, i); out_record_bool[which].attachToRecord(outputBuffer, inputMap(i)); break; case TpUChar: in_record_char[which].attachToRecord(inputBuffer, i); out_record_char[which].attachToRecord(outputBuffer, inputMap(i)); break; case TpShort: in_record_short[which].attachToRecord(inputBuffer, i); out_record_short[which].attachToRecord(outputBuffer, inputMap(i)); break; case TpInt: in_record_int[which].attachToRecord(inputBuffer, i); out_record_int[which].attachToRecord(outputBuffer, inputMap(i)); break; case TpFloat: in_record_float[which].attachToRecord(inputBuffer, i); out_record_float[which].attachToRecord(outputBuffer, inputMap(i)); break; case TpDouble: in_record_double[which].attachToRecord(inputBuffer, i); out_record_double[which].attachToRecord(outputBuffer, inputMap(i)); break; case TpComplex: in_record_complex[which].attachToRecord(inputBuffer, i); out_record_complex[which].attachToRecord(outputBuffer, inputMap(i)); break; case TpDComplex: in_record_dcomplex[which].attachToRecord(inputBuffer, i); out_record_dcomplex[which].attachToRecord(outputBuffer, inputMap(i)); break; case TpString: in_record_string[which].attachToRecord(inputBuffer, i); out_record_string[which].attachToRecord(outputBuffer, inputMap(i)); break; case TpArrayBool: in_record_array_bool[which].attachToRecord(inputBuffer, i); out_record_array_bool[which].attachToRecord(outputBuffer, inputMap(i)); break; case TpArrayUChar: in_record_array_char[which].attachToRecord(inputBuffer, i); out_record_array_char[which].attachToRecord(outputBuffer, inputMap(i)); break; case TpArrayShort: in_record_array_short[which].attachToRecord(inputBuffer, i); out_record_array_short[which].attachToRecord(outputBuffer, inputMap(i)); break; case TpArrayInt: in_record_array_int[which].attachToRecord(inputBuffer, i); out_record_array_int[which].attachToRecord(outputBuffer, inputMap(i)); break; case TpArrayFloat: in_record_array_float[which].attachToRecord(inputBuffer, i); out_record_array_float[which].attachToRecord(outputBuffer, inputMap(i)); break; case TpArrayDouble: in_record_array_double[which].attachToRecord(inputBuffer, i); out_record_array_double[which].attachToRecord(outputBuffer, inputMap(i)); break; case TpArrayComplex: in_record_array_complex[which].attachToRecord(inputBuffer, i); out_record_array_complex[which].attachToRecord(outputBuffer, inputMap(i)); break; case TpArrayDComplex: in_record_array_dcomplex[which].attachToRecord(inputBuffer, i); out_record_array_dcomplex[which].attachToRecord(outputBuffer, inputMap(i)); break; case TpArrayString: in_record_array_string[which].attachToRecord(inputBuffer, i); out_record_array_string[which].attachToRecord(outputBuffer, inputMap(i)); break; default: throw(AipsError( "CopyRecordToRecord::CopyRecordToRecord - unknown type")); } where[inputBuffer.description().type(i)]++; } } } CopyRecordToRecord::~CopyRecordToRecord() { // nothing } void CopyRecordToRecord::copy() { uInt i; for (i=0; i < out_record_bool.nelements(); i++) { *(out_record_bool[i]) = *(in_record_bool[i]); } for (i=0; i < out_record_char.nelements(); i++) { *(out_record_char[i]) = *(in_record_char[i]); } for (i=0; i < out_record_short.nelements(); i++) { *(out_record_short[i]) = *(in_record_short[i]); } for (i=0; i < out_record_int.nelements(); i++) { *(out_record_int[i]) = *(in_record_int[i]); } for (i=0; i < out_record_float.nelements(); i++) { *(out_record_float[i]) = *(in_record_float[i]); } for (i=0; i < out_record_double.nelements(); i++) { *(out_record_double[i]) = *(in_record_double[i]); } for (i=0; i < out_record_complex.nelements(); i++) { *(out_record_complex[i]) = *(in_record_complex[i]); } for (i=0; i < out_record_dcomplex.nelements(); i++) { *(out_record_dcomplex[i]) = *(in_record_dcomplex[i]); } for (i=0; i < out_record_string.nelements(); i++) { *(out_record_string[i]) = *(in_record_string[i]); } for (i=0; i < out_record_array_bool.nelements(); i++) { *(out_record_array_bool[i]) = *(in_record_array_bool[i]); } for (i=0; i < out_record_array_char.nelements(); i++) { *(out_record_array_char[i]) = *(in_record_array_char[i]); } for (i=0; i < out_record_array_short.nelements(); i++) { *(out_record_array_short[i]) = *(in_record_array_short[i]); } for (i=0; i < out_record_array_int.nelements(); i++) { *(out_record_array_int[i]) = *(in_record_array_int[i]); } for (i=0; i < out_record_array_float.nelements(); i++) { *(out_record_array_float[i]) = *(in_record_array_float[i]); } for (i=0; i < out_record_array_double.nelements(); i++) { *(out_record_array_double[i]) = *(in_record_array_double[i]); } for (i=0; i < out_record_array_complex.nelements(); i++) { *(out_record_array_complex[i]) = *(in_record_array_complex[i]); } for (i=0; i < out_record_array_dcomplex.nelements(); i++) { *(out_record_array_dcomplex[i]) = *(in_record_array_dcomplex[i]); } for (i=0; i < out_record_array_string.nelements(); i++) { *(out_record_array_string[i]) = *(in_record_array_string[i]); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/fits/FITS/CopyRecord.h000066400000000000000000000215371476623553700172700ustar00rootroot00000000000000//# CopyRecord.h: Copy fields from a Record to a table or other record. //# Copyright (C) 1995,1996,1997,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef FITS_COPYRECORD_H #define FITS_COPYRECORD_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class Table; class TableDesc; class RecordInterface; class RecordDesc; class String; template class ScalarColumn; template class ArrayColumn; // // Copies fields from a Record to columns of a Table. // // // // // //
      • Record //
      • Table // // // // // // // // // // // // // This class should be generalized, and made better. It is the analog of // RowCopier, i.e. it copies all the fields from some Record to certain // columns of a table. The mapping from fields to columns occurs at // construction of the CopyRecordToTable object. // class CopyRecordToTable { public: // Set the mapping between fields and columns. In particular, // inputMap(fieldNumber) -> columnNumber. CopyRecordToTable(Table &outputTable, const RecordInterface &inputBuffer, const Vector inputMap); // assignment constructor, reference semantics CopyRecordToTable(const CopyRecordToTable &other); ~CopyRecordToTable(); // assignment operator, reference semantics CopyRecordToTable &operator=(const CopyRecordToTable &other); // Copy from the record (which must still exist) to the given row number // of the table (which must also still exist). void copy(uInt rownr); private: // We could just have a TableColumn for scalars, but we'd need all of // the array types anyway. PtrBlock *> table_bool; PtrBlock *> table_char; PtrBlock *> table_short; PtrBlock *> table_int; PtrBlock *> table_float; PtrBlock *> table_double; PtrBlock *> table_complex; PtrBlock *> table_dcomplex; PtrBlock *> table_string; PtrBlock *> table_array_bool; PtrBlock *> table_array_char; PtrBlock *> table_array_short; PtrBlock *> table_array_int; PtrBlock *> table_array_float; PtrBlock *> table_array_double; PtrBlock *> table_array_complex; PtrBlock *> table_array_dcomplex; PtrBlock *> table_array_string; Block > record_bool; Block > record_char; Block > record_short; Block > record_int; Block > record_float; Block > record_double; Block > record_complex; Block > record_dcomplex; Block > record_string; Block > > record_array_bool; Block > > record_array_char; Block > > record_array_short; Block > > record_array_int; Block > > record_array_float; Block > > record_array_double; Block > > record_array_complex; Block > > record_array_dcomplex; Block > > record_array_string; void clearAll(); // Undefined and inaccessible CopyRecordToTable(); }; //#! global functions // This function probably doesn't belong here, but I'm not yet sure where it does belong. // This function adds all the fields in recordDescription to tableDescription, prefixed by "prefix". // If prefix is empty, nothing is prepended // otherwise the string prefix + "_" is prepended to each RecordFieldPtr name in // constructing the TableDesc void addRecordDesc(TableDesc &tableDescription, const RecordDesc &recordDescription, const String &prefix); // // Copies fields between Records, possibly to fields with another name. // // // // // //
      • Record // // // // // // // // // // // // // class CopyRecordToRecord { public: // Set the mapping between fields and columns. In particular, // inputMap(fieldNumber) -> outputFieldNumber. CopyRecordToRecord(RecordInterface &outputBuffer, const RecordInterface &inputBuffer, const Vector inputMap); ~CopyRecordToRecord(); // Copy from the record (which must still exist) to the // output record (which must also still exist). void copy(); private: // Undefined and inaccessible CopyRecordToRecord(); CopyRecordToRecord(const CopyRecordToRecord &); CopyRecordToRecord &operator=(const CopyRecordToRecord &); Block > in_record_bool; Block > in_record_char; Block > in_record_short; Block > in_record_int; Block > in_record_float; Block > in_record_double; Block > in_record_complex; Block > in_record_dcomplex; Block > in_record_string; Block > > in_record_array_bool; Block > > in_record_array_char; Block > > in_record_array_short; Block > > in_record_array_int; Block > > in_record_array_float; Block > > in_record_array_double; Block > > in_record_array_complex; Block > > in_record_array_dcomplex; Block > > in_record_array_string; Block > out_record_bool; Block > out_record_char; Block > out_record_short; Block > out_record_int; Block > out_record_float; Block > out_record_double; Block > out_record_complex; Block > out_record_dcomplex; Block > out_record_string; Block > > out_record_array_bool; Block > > out_record_array_char; Block > > out_record_array_short; Block > > out_record_array_int; Block > > out_record_array_float; Block > > out_record_array_double; Block > > out_record_array_complex; Block > > out_record_array_dcomplex; Block > > out_record_array_string; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/fits/FITS/FITS2.h000066400000000000000000000050221476623553700160350ustar00rootroot00000000000000//# FITS2.h: Transform a Casacore Array to or from a FITS disk file (helper functions) //# Copyright (C) 1994,1995 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef FITS_FITS2_H #define FITS_FITS2_H #include //# Would like to forward declare #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class String; // Forward declaration // helper function for ReadFITS and WriteFITS // // // Helper functions to reduce the tedium/code replication of writing the // ReadFITS and WriteFITS functions. If a baseclass is introduced that // aboved the PrimaryArray class that contains functions like operator() // and copy() then these functions won't be necessary. // template void ReadFITSin(PrimaryArray &fitsdata, Array &data, Bool &ok, String &ErrorMessage, String *unitName, Vector *axisNames, Vector *refPixel, Vector *refLocation, Vector *delta, std::map *keywords, String *objectName); // } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/fits/FITS/FITS2.tcc000066400000000000000000000110171476623553700163600ustar00rootroot00000000000000//# FITS2.cc: Transform a Casacore Array to or from a FITS disk file (helper functions) //# Copyright (C) 1994,1995,1998,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef FITS_FITS2_TCC #define FITS_FITS2_TCC #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template void ReadFITSin(PrimaryArray &fitsdata, Array &data, Bool &ok, String &ErrorMessage, String *unitName, Vector *axisNames, Vector *refPixel, Vector *refLocation, Vector *delta, std::map *keywords, String *objectName) { IPosition shape; Bool deleteIt; shape.resize(fitsdata.dims()); for (uInt i=0; i < shape.nelements(); i++) shape(i) = fitsdata.dim(i); data.resize(shape); if (fitsdata.read() != Int(data.nelements())) { ErrorMessage = "Could not real all data"; ok = False; return; } Float *storage = data.getStorage(deleteIt); fitsdata.copy(storage); data.putStorage(storage, deleteIt); // Fill in the "optional" things if (unitName) { (*unitName) = fitsdata.bunit(); // Get rid of trailing blanks (*unitName).rtrim(' '); } if (axisNames) { (*axisNames).resize(fitsdata.dims()); for (Int i=0; iname(); if (kwname == "SIMPLE" || kwname == "BITPIX" || kwname == "END" || kwname == "BSCALE" || kwname == "BZERO" || kwname == "BUNIT" || kwname.at(0,5) == "CRVAL" || kwname.at(0,5) == "CRPIX" || kwname.at(0,5) == "CDELT" || kwname.at(0,5) == "NAXIS") { next=kwl.next(); continue; } switch (next->type()) { case FITS::DOUBLE: (*keywords)[kwname] = next->asDouble(); break; case FITS::FLOAT: (*keywords)[kwname] = next->asFloat(); break; case FITS::LONG: (*keywords)[kwname] = next->asInt(); break; default: break; } next = kwl.next(); } } if (objectName) { // This would not be necessary if ContFitsKeywordList had an exists() const FitsKeyword *kw = fitsdata.kwlist()(FITS::OBJECT); if (kw) { (*objectName) = String(kw->asString(),kw->valStrlen()); } else { (*objectName) = ""; } // Get rid of trailing blanks (*objectName).rtrim(' '); } } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/fits/FITS/FITSDateUtil.cc000066400000000000000000000151151476623553700175510ustar00rootroot00000000000000//# FITSDateUtil.cc: this defines FITSDateUtil //# Copyright (C) 2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN void FITSDateUtil::toFITS(String &date, String ×ys, const MVTime &time, MEpoch::Types system, DateStyle style, uInt precision) { date = "invalid"; timesys = "invalid"; // First get the DATE string. switch (style) { case OLD: { Int month = time.month(); Int day = time.monthday(); Int year = time.year() - 1900; AlwaysAssert(year >= 0 && year<100, AipsError); // 20th century only ostringstream out; out << setfill('0') << setw(2) << day << "/" << setw(2) << month << "/" << setw(2) << year; date = out.str(); } break; case NEW_DATEONLY: { date = time.string(MVTime::FITS + MVTime::NO_TIME, 0); } break; case NEW_DATEANDTIME: { date = time.string(MVTime::FITS, precision); // Full precision? // double check by converting this string back to a time Quantity q; MVTime::read(q,date); MVTime timeCheck = MVTime(q); // within 1/2 day of each other if (abs(time.day() - timeCheck.day()) > 0.5) { // send a SEVERE warning to the logger LogIO logger; logger << LogOrigin("FITSDateUtil", "toFITS", WHERE); logger << LogIO::SEVERE << "unexpected problem converting time to FITS string - " << "the resulting string is off by more than a day - " << LogIO::POST; logger << LogIO::SEVERE << "The output date string is : " << date << LogIO::POST; logger << LogIO::SEVERE << "please report this bug using bug()." << LogIO::POST; } } break; case AUTO_PICK: { style = precision > 0 ? NEW_DATEANDTIME : NEW_DATEONLY; Time now; if (time.year() < 1999 && now.year() < 1999) { style = OLD; } toFITS(date, timesys, time, system, style, precision); // recurse } break; default: AlwaysAssert(0, AipsError); // NOTREACHED } // And then timesys timesys = MEpoch::showType(system); // Canonicalize if (timesys == "IAT") { timesys = "TAI"; } else if (timesys == "UT1") { timesys = "UT"; } } Bool FITSDateUtil::fromFITS(MVTime &time, MEpoch::Types &system, const String &date, const String ×ys) { Bool ok = True; time = MVTime(1900,1,1.0); system = MEpoch::UTC; // Let's carry on with the date if (date.contains("/")) { // Old style ok = date.length() >= 8; ok = ok && isdigit(date[0]); ok = ok && isdigit(date[1]); ok = ok && isdigit(date[3]); ok = ok && isdigit(date[4]); ok = ok && isdigit(date[6]); ok = ok && isdigit(date[7]); Int zero = '0'; if (ok) { Int year = (date[6]-zero)*10 + date[7]-zero; year += 1900; Int month = (date[3]-zero)*10 + date[4]-zero; Double day = (date[0]-zero)*10 + date[1]-zero; time = MVTime(year, month, day); } } else { // New style Quantity q; ok = MVTime::read(q, date); if (ok) { time = MVTime(q); } } // Convert timesys first if (timesys == "") { system = MEpoch::UTC; // Default is UTC if not otherwise specified } else { if (timesys == "UTC") { system = MEpoch::UTC; } else if (timesys == "UT") { // Standard FITS starts here system = MEpoch::UT; } else if (timesys == "TAI") { system = MEpoch::TAI; } else if (timesys == "IAT") { system = MEpoch::IAT; } else if (timesys == "ET") { system = MEpoch::ET; } else if (timesys == "TT") { system = MEpoch::TT; } else if (timesys == "TDT") { system = MEpoch::TDT; } else if (timesys == "TDB") { system = MEpoch::TDB; } else if (timesys == "TCG") { system = MEpoch::TCG; } else if (timesys == "TCB") { system = MEpoch::TCB; } else if (timesys == "LAST") { // Casacore extensions here system = MEpoch::LAST; } else if (timesys == "LMST") { system = MEpoch::LMST; } else if (timesys == "GMST1") { system = MEpoch::GMST1; } else if (timesys == "GAST") { system = MEpoch::GAST; } else if (timesys == "UT1") { system = MEpoch::UT1; } else if (timesys == "UT2") { system = MEpoch::UT2; } else if (timesys == "GMST") { system = MEpoch::GMST; } else { ok = False; } } return ok; } Bool FITSDateUtil::convertDateString(String &out, const String &in) { MVTime time; MEpoch::Types system; Bool ok = FITSDateUtil::fromFITS(time, system, in, ""); if (ok) { String sys; uInt precision = findPrecision(in); FITSDateUtil::toFITS(out, sys, time, MEpoch::UTC, AUTO_PICK, precision); } return ok; } uInt FITSDateUtil::findPrecision(const String &fitsDate) { if (fitsDate.contains("/")) { return 0; // Old style has no time } // OK, new style uInt prec = 0; if (fitsDate.contains("T")) { prec += 6; // We are good at least to the second Int decimalpos = fitsDate.index('.'); if (decimalpos > 0) { // OK, we may have some decimal points, count 'em. for (uInt i=decimalpos+1; i #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class String; class MVTime; // // A class with static functions to help deal with FITS dates // // // // // //
      • General knowledge of FITS, particularly FITS date keywords, // is assumed. // // // // This is a collection of static utility functions for creating and // interpreting FITS date keywords (e.g. DATE-OBS). // // // // Its never necessary to construct a FITSDateUtil, just use the // static functions to help handle FITS dates. // // // // The strings that make up the value of FITS dates have a // precise format. This class encompasses knowlege of the formats // used and hopefully simplifies their creation and conversion // to and from Casacore MVTimes. // // class FITSDateUtil { public: enum DateStyle { // dd/mm/yy OLD, // yyyy-mm-dd NEW_DATEONLY, // yyyy-mm-ddThh:mm:ss[.ss...] NEW_DATEANDTIME, // OLD if the current year is before 1998 AND "time" is before 1998, // otherwise NEW_DATEANDTIME. AUTO_PICK}; // Convert an MVTime to a FITS date string and timesys string. The // time system must also be supplied. // Precision is only used when the time as well as the date is used // (NEW_DATEANDTIME or AUTO_PICK). // The default (16) gives 10^(-10) second accuracy, 6 gives second level // accuracy. Default is 10^(-6)s (1 micro-s) accuracy. 0 means date only, // no time (equivalent to NEW_DATEONLY). static void toFITS(String &date, String ×ys, const MVTime &time, MEpoch::Types system = MEpoch::UTC, DateStyle style= AUTO_PICK, uInt precision=12); // Convert a FITS date string and TIMESYS keyword value into an MVTime and system. // Returns False if it can't decode date and timesys. It tries to convert as // much as possible, for example if it can't decode timesys it still // attempts to decode the time. It sets the date to Jan 1/1900 if it can't // decode the time, and UTC if it can't decode timesys. If timesys is the // empty string then UTC is assumed. static Bool fromFITS(MVTime &time, MEpoch::Types &system, const String &date, const String ×ys); // Convert a FITS Date string to the current format. If the "in" format is // already correct it is just copied through. static Bool convertDateString(String &out, const String &in); // Determine the precision in a FITS date string. // Old style dates or no time returns 0, New style + time returns 6 + the number // of decimal points, i.e. if we have a time at all we assume it is at least // accurate to the second level. The result of this can be used in the call // to toFITS, i.e. it has the same meaning. // // This is mostly meant to be a helper function for convertDateString, but // it may be called by anyone. static uInt findPrecision(const String &fitsDate); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/fits/FITS/FITSError.cc000066400000000000000000000033061476623553700171260ustar00rootroot00000000000000//# FITSError.cc: this defines the static default FITS error handler function //# Copyright (C) 1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN void FITSError::defaultHandler(const char *errMessage, ErrorLevel severity) { LogIO os; if (severity == FITSError::WARN) { os << LogIO::WARN; } else { if (severity == FITSError::SEVERE) { os << LogIO::SEVERE; } } os << errMessage; os << LogIO::POST; } } //# NAMESPACE CASACORE - END casacore-3.7.1/fits/FITS/FITSError.h000066400000000000000000000101461476623553700167700ustar00rootroot00000000000000//# FITSError.h: default FITS error handling function, typdef, and enumeration //# Copyright (C) 1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef FITS_FITSERROR_H #define FITS_FITSERROR_H //#! Includes go here #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // default FITS error handling function, typdef, and enumeration // // // // // // FITSError contains the enumeration specifying the possible error // message levels. It also contains the default error handling function // for the FITS classes. // // // // This example shows how one could set up an error handler // which does what the FITS classes originally did - just // send the error message to cout without any indication as // to the severity of the error message. // // void coutErrHandler(const char *errMessage, FITSError::ErrorLevel) // { cout << errMessage << endl; } // // FitsInput fin("myFile", FITS::Disk, 10, coutErrHandler); // // Any error messages generated by fin will be sent to cout. // Error handlers for the HDUs would need to be indicated in // their constructors. For example: // // PrimaryArray pa(fin, coutErrHandler); // // The default error handler is FITSError::defaultHandler which // sends the error message to the global log sink at the // severity implied by ErrorLevel. // // The error handler only handles the error messages. It is up to // the programmer to check for the error status of classes like // FitsInput. // // // // Originally, FITS error message were simply sent to an ostream. In // order to have these error messages go to the Casacore logger by default, // this class was added. This was made a separate class because both // BlockIo and FITS need to use this class. The anticipated replacements // for the current FITS classes use a somewhat similar scheme. // class FITSError { public: // WARN means that the FITS file is still usable - this generally // happens when parsing the HDU and some minor, recoverable // violation of the FITS rules is detected. // SEVERE means that a fatal error has occurred and the FITS file // can not be properly processed. enum ErrorLevel { INFO, WARN, SEVERE }; // The default error handler. The errMessage is posted to // the global log sink at the severity implied by ErrorLevel. // It is assumed that errMessage is null terminated. static void defaultHandler(const char *errMessage, ErrorLevel severity); }; // // Define a typedef for the handler function signature for convenience. // // // // typedef void (*FITSErrorHandler) (const char* errMessage, FITSError::ErrorLevel severity); } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/fits/FITS/FITSFieldCopier.h000066400000000000000000000207611476623553700200700ustar00rootroot00000000000000//# FITSFieldCopier.h: Copy RORecordFields to FitsFields //# Copyright (C) 1996,1998,1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef FITS_FITSFIELDCOPIER_H #define FITS_FITSFIELDCOPIER_H #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Virtual base class for copying RORecordFields to FitsFields // // // // // //
      • RORecordField //
      • FitsFields // // // // // // // // // // // // // // // //
      • //
      • // // // //
      • actually document this // class FITSFieldCopier { public: // destructor virtual ~FITSFieldCopier() {}; // the things which does the work - to be implemented in each derived class virtual void copyToFITS() = 0; }; // // A FITSFieldCopier for copying scalar non-string RecordFields to FitsFields // // // // // //
      • RORecordField //
      • FitsFields // // // // // // // // // // // // // // // //
      • //
      • // // // //
      • actually document this // template class ScalarFITSFieldCopier : public FITSFieldCopier { public: ScalarFITSFieldCopier(RORecordFieldPtr *recptr, FitsField *fitsptr) : rec_p(recptr), fits_p(fitsptr) {} ~ScalarFITSFieldCopier() {delete rec_p; delete fits_p;} // Copy the current contents of the input RORecordFieldPtr to the // output FitsField virtual void copyToFITS() {(*fits_p)() = *(*rec_p); } private: RORecordFieldPtr *rec_p; FitsField *fits_p; ScalarFITSFieldCopier(const ScalarFITSFieldCopier &other); ScalarFITSFieldCopier &operator=( const ScalarFITSFieldCopier &other); }; // // A FITSFieldCopier for copying String RecordFields to FitsFields // // // // // //
      • RORecordField //
      • FitsFields // // // // // // // // // // // // // // // //
      • //
      • // // // //
      • actually document this // class StringFITSFieldCopier : public FITSFieldCopier { public: StringFITSFieldCopier(RORecordFieldPtr *rptr, FitsField *fptr) : rec_p(rptr), fits_p(fptr) {} // Copy the current contents of the input RORecordFieldPtr to the // output FitsField virtual void copyToFITS() { Int fitslength = fits_p->nelements(); Int reclength = (*(*rec_p)).length(); Int minlength = fitslength < reclength ? fitslength : reclength; const char *chars = (**rec_p).chars(); Int i; for (i=0; i *rec_p; FitsField *fits_p; // Undefined and inaccessible. StringFITSFieldCopier(const StringFITSFieldCopier &other); StringFITSFieldCopier &operator=(const StringFITSFieldCopier &other); }; // // A FITSFieldCopier for copying Array RecordFields to FitsFields // // // // // //
      • RORecordField //
      • FitsFields // // // // // // // // // // // // // // // //
      • //
      • // // // //
      • actually document this // template class ArrayFITSFieldCopier : public FITSFieldCopier { public: ArrayFITSFieldCopier(RORecordFieldPtr > *recptr, FitsField *fitsptr) : rec_p(recptr), fits_p(fitsptr) {} ~ArrayFITSFieldCopier() {delete rec_p; delete fits_p;} // Copy the current contents of the input RORecordFieldPtr to the // output FitsField virtual void copyToFITS() { uInt nfits = fits_p->nelements(); uInt narray = (**rec_p).nelements(); uInt nmin = narray < nfits ? narray : nfits; Bool deleteIt; const recordType *rptr = (**rec_p).getStorage(deleteIt); for (uInt i=0; i > *rec_p; FitsField *fits_p; // Undefined and inaccessible ArrayFITSFieldCopier(const ArrayFITSFieldCopier &other); ArrayFITSFieldCopier &operator=( const ArrayFITSFieldCopier &other); }; template class VariableArrayFITSFieldCopier : public FITSFieldCopier { public: VariableArrayFITSFieldCopier(RORecordFieldPtr > *recptr, FitsField *fitsptr, FitsField *tdirptr) : rec_p(recptr), fits_p(fitsptr), tdir_p(tdirptr) {} ~VariableArrayFITSFieldCopier() {delete rec_p; delete fits_p;} // Copy the current contents of the input RORecordFieldPtr to the // output FitsField virtual void copyToFITS() { uInt nfits = fits_p->nelements(); uInt narray = (**rec_p).nelements(); uInt nmin = narray < nfits ? narray : nfits; Bool deleteIt; const recordType *rptr = (**rec_p).getStorage(deleteIt); for (uInt i=0; inelements(); Int reclength = thisTDIR.length(); Int minlength = fitslength < reclength ? fitslength : reclength; const char *chars = thisTDIR.chars(); Int i; for (i=0; i > *rec_p; FitsField *fits_p; FitsField *tdir_p; // Undefined and inaccessible VariableArrayFITSFieldCopier(const VariableArrayFITSFieldCopier &other); VariableArrayFITSFieldCopier &operator=( const VariableArrayFITSFieldCopier &other); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/fits/FITS/FITSHistoryUtil.cc000066400000000000000000000243541476623553700203420ustar00rootroot00000000000000//# FITSHistoryUtil.cc: this defines FITSHistoryUtil //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN uInt FITSHistoryUtil::getHistoryGroup(Vector &strings, String &groupType, ConstFitsKeywordList &in) { LogIO os; os << LogOrigin("FITSHistoryUtil", "getHistoryGroup", WHERE); groupType = ""; const Regex groupstart("^ *[Cc][Aa][Ss][Aa] *[Ss][Tt][Aa][Rr][Tt] *"); const Regex groupend ("^ *[Cc][Aa][Ss][Aa] *[Ed][Nn][Dd]"); const Regex trailing(" *$"); const String empty; // if in is at the top, strangely enough, this gets the first kw // if curr() is used, the first kw would be parsed twice const FitsKeyword *key = in.next(); uInt nFound = 0; Bool foundStart = False; String tmp; while (key) { if (key->type() == FITS::NOVALUE && key->kw().name() == FITS::HISTORY) { // Found a history card. tmp = key->comm(); // Get rid of trailing spaces for all strings tmp.gsub(trailing, empty); if (tmp.contains(groupstart)) { // CASA START if (foundStart) { os << LogIO::SEVERE << "Cannot handle nested CASA START" " history keywords. Ignoring" << LogIO::POST; } else if (nFound > 0) { // OK, we are ending a normal HISTORY group break; } else { // OK, found a valid start of group. foundStart = True; tmp.gsub(groupstart, ""); tmp.gsub(" ", ""); groupType = tmp; } } else if (tmp.contains(groupend)) { // CASA END if (foundStart) { // OK, a normal end. // Attempt to parse the TYPE in the END statement to see // if it matches for debugging purposes. tmp.gsub(groupend, ""); tmp.gsub(" ", ""); if (tmp != "") { if (tmp != groupType) { os << LogIO::SEVERE << "HISTORY START and END types do not match (" << groupType << "," << tmp << ")" << LogIO::POST; } } break; } else { os << LogIO::DEBUG1 << "CASA END found in history without" " a corresponding START. Ignoring" << LogIO::POST; } } else { // A string in the group. if (nFound == 0 || (tmp.length() > 0 && tmp[0] != '>')) { nFound++; if (nFound >= strings.nelements()) { // Exponentially resize for efficiency strings.resize(2*nFound + 1, True); } strings(nFound-1) = tmp; } else { // continuation - strip out leading '>' strings(nFound-1) += tmp(1, tmp.length()-1); } } } key = in.next(); // Advance to next key } return nFound; } void FITSHistoryUtil::addHistoryGroup(FitsKeywordList &out, const vector &strings, uInt nstrings, const String &groupType) { LogIO os; os << LogOrigin("FITSHistoryUtil", "addHistoryGroup", WHERE); if (nstrings > strings.size()) { os << LogIO::SEVERE << "Asked to add more lines to history than there " "are strings (adjusting)." << LogIO::POST; nstrings = strings.size(); } if (groupType != "") { String tmp = String("CASA START ") + groupType; out.history(tmp.chars()); } const Int maxlen = 72; // 80 - length('HISTORY '); String tmp; for (uInt i=0; i lines = stringToVector(strings[i], '\n'); for (uInt j=0; j'. // We have to turn trailing spaces on one line // into leading spaces on the next line - obviously this // doesn't work if there are more than 71 of them! const int end = lines(j).length() - 1; int start=0, pos=0; Bool done = False; while (!done) { pos = start + maxlen - 1; if (pos >= end) { done = True; pos = end; } while (lines(j)[pos] == ' ' && pos > start) { pos--; // Backup to the first non-blank character } tmp = start == 0 ? "" : ">"; tmp += lines(j)(start, pos - start + 1); out.history(tmp.chars()); start = pos; start = pos+1; } } } } if (groupType != "") { out.history((String("CASA END ") + groupType).chars()); } } void FITSHistoryUtil::fromHISTORY(LoggerHolder& logger, const Vector &history, uInt nstrings, Bool aipsppFormat) { LogIO os; os << LogOrigin("FITSHistoryUtil", "fromHistory", WHERE); LogSink& sink = logger.sink(); AlwaysAssert(nstrings <= history.nelements(), AipsError); // if (aipsppFormat && (nstrings%2 == 0)) { // OK, the first line is supposed to be DATE PRIORITY [SRCCODE] [OBJID] // And the second one the message. Regex timePattern("^[^ ]*"); Regex timeAndPriorityPattern("^[^ ]* *[^ ]*"); Regex locationPattern("SRCCODE='.*'"); Regex objectIDPattern("OBJID='.*'"); MVTime time; MEpoch::Types timeSystem; // we don't care about this for log files! String date, priority, message, location, location2, objid, objid2; String tmp, msg; Double dtime; for (uInt i=0; i 0) { firstLine += aipsppFormat ? nstrings/2 : nstrings; } return firstLine; } } //# NAMESPACE CASACORE - END casacore-3.7.1/fits/FITS/FITSHistoryUtil.h000066400000000000000000000156321476623553700202030ustar00rootroot00000000000000//# FITSHistoryUtil.h: Class of static functions to help with FITS History cards. //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef FITS_FITSHISTORYUTIL_H #define FITS_FITSHISTORYUTIL_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class ConstFitsKeywordList; class FitsKeywordList; class String; class LoggerHolder; // // A class with static functions to help deal with FITS History cards. // // // // // //
      • General knowledge of FITS, and particularly FITS keywords, is // assumed. //
      • Presumably you are using this class in conjunction // with the "native" // FitsKeywordList // // // // This is a collection of static utility functions for use with FITS // HISTORY keywords. // // // // Manipulate HISTORY information. FITS HISTORY cards are interconverted with // String as follows: //
          //
        • 'HISTORY ' and trailing blanks are removed from each card. //
        • Continuation cards are CARDS that have '>' in the first line. //
        • A string is made by concatenating the leading card and all continuation // cards. //
        // For example: // // HISTORY Every good // HISTORY > boy deserves // HISTORY >fudge. // // Becomes the C++ String: "Every good boy deservesfudge." Note the lack of // a space between deserves and fudge. // // History cards are broken into groups. A group is delimited by // // HISTORY AIPS++ START TYPE // HISTORY AIPS++ END [TYPE] // // Where type might be, e.g., LOGTABLE. HISTORY cards not enclosed between // START/END pairs are implicitly of type "" (i.e. the empty string). // The TYPE is optional on the END statement. It is essentially a comment. // // At present, START/END pairs cannot be nested, although this would be an // obvious extension. //
        // // // The FitsKeywordList class can be somewhat tedious to use, as it deals with, // e.g., char* pointers rather than Strings. This class makes it easy to // interconvert between the HISTORY keywords and a Vector of related history // information. // // class FITSHistoryUtil { public: // Get the strings in the next keyword group. Returns the number of // strings found (0 when no history remains). If necessary, strings will be // resized larger. in must be set to the first card before the first call to // getHistoryGroup, and should not be reset until all history is extracted // (otherwise the same history will be extracted more than once). This method // can be used as follows: // // uInt n; // Vector group; // String type; // ConstFITSKeywordList keys(...); // ... // keys.first(); // while ((n = FITSHistoryUtil::getHistoryGroup(group, type, keys)) != 0) { // ... process this history group // } // // strings will have no embedded newlines. strings is not resized if it is more // than large enough to hold the number of history cards in the group (i.e. there // may be values at the end of strings which are not part of the requested group. static uInt getHistoryGroup(Vector &strings, String &groupType, ConstFitsKeywordList &in); // Add history strings of the specified groupType to an existing FitsKeywordList. // This function will split long strings across HISTORY cards and set // up the group START/END keywords if necessary. nstrings must be specified // because strings might have come from something like getHistoryGroup, i.e. // it might have garbage entries at the end. The strings may have embedded // newlines, but they must have no other non-printable characters. static void addHistoryGroup(FitsKeywordList &out, const std::vector &strings, uInt nstrings, const String &groupType); // Some functions to help convert between log tables and FITS HISTORY cards. // It is intended that these functions will only be used by the functions in // classes like ImageFITSConverter. // // Table rows are in Casacore format if they have a valid time and priority, // otherwise they are in the standard FITS HISTORY format. The history lines // are processed by contiguous groups where all lines in that group are // either in Casacore or HISTORY format. Note that history.nelements() might // be greater than nstrings for efficiency (i.e. the history vector will // not be shrunk unnecessarily). // // Note that these functions are in a separate .cc file so that if they // are not used the table function is not linked in if other functions in // this class are used. // // The strings are assumed to be from or going to the get/addHistoryGroup // functions, i.e. strings that span multiple lines are joined, // AIPS++ START/END cards are stripped, etc. // // The Casacore format is: the first line DATE PRIORITY [SRCCODE='xxx'] // [OBJID='xxx'] and the second lins is the message. These entries are in // an AIPS++ START LOGTABLE history sequence. // static void fromHISTORY(LoggerHolder& logSink, const Vector& history, uInt nstrings, Bool aipsppFormat); // toHistory signals that it is done by setting nstrings to 0. // The returned value is firstLine + n_lines_read, i.e. use // it as firstLine in your next call. static uInt toHISTORY(std::vector& history, Bool& aipsppFormat, uInt& nstrings, uInt firstLine, const LoggerHolder& logSink); // }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/fits/FITS/FITSKeywordUtil.cc000066400000000000000000001031321476623553700203150ustar00rootroot00000000000000//# FitsKeywordUtil: this defines FitsKeywordUtil //# Copyright (C) 2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Do a reverse lookup since the FITS classes need it. static Bool findReservedName(FITS::ReservedName &name, const String &basename) { const uInt n = FITS::ResWord.no(); for (uInt i=0; i= 0 && isdigit(fullName[where])) { ; // Nothing } where++; // where now points to the start of the numerical part - its // also the length of the number of characters before that name = fullName(0, where); String snum = fullName(where, (fullName.length()-where)); num = atol(snum.chars()); } static Bool splitKW2D(String &name, Int &nrow, Int &ncol, String &fullName) { name = ""; if(fullName.contains("_")){ // assume new matrix syntax ii_jj or i_j uInt where = 0;// Where the frst number starts while (where++ < fullName.length() && !isdigit(fullName[where])) { ; // Nothing } name = fullName(0, where); // found first non-digit String::size_type where2 = fullName.find('_'); if (where2 == String::npos || where2 == fullName.length()-1){ return False; } String snum1 = fullName(where, where2-where); Int nc = 2; // don't use the last digit if there are three if(fullName.length()-where2<2){ nc = 1; } String snum2 = fullName(where2+1, nc); nrow = atol(snum1.chars()); ncol = atol(snum2.chars()); } else { // old matrix syntax // We assume that 1/2 the characters belong to each of the two // numbers. Int where = fullName.length();// Where to split the number and base name while (--where >= 0 && isdigit(fullName[where])) { ; // Nothing } where++; // where now points to the start of the numerical part - its // also the length of the number of characters before that name = fullName(0, where); Int numlen = fullName.length() - where; if (numlen != 6) { // 2D arrays must be xxxyyy and so there must be 6 digits return False; } String snum1 = fullName(where, 3); String snum2 = fullName(where+3, 3); nrow = atol(snum1.chars()); ncol = atol(snum2.chars()); } return True; } FitsKeywordList FITSKeywordUtil::makeKeywordList(Bool primHead, Bool binImage) { FitsKeywordList retval; if (primHead) retval.mk(FITS::SIMPLE, True, "Standard FITS"); else if (binImage) retval.mk(FITS::XTENSION,"IMAGE ","IMAGE extension"); else retval.mk(FITS::XTENSION,"BINTABLE ","TABLE extension"); return retval; } Bool FITSKeywordUtil::addKeywords(FitsKeywordList &out, const RecordInterface &in) { LogIO os(LogOrigin("FITSKeywordUtil", "addKeywords", WHERE)); Bool ok = True; const uInt n = in.nfields(); static Regex commentName("^COMMENT"); static Regex historyName("^HISTORY"); uInt i = 0; while (i < n) { DataType type = in.type(i); if (isScalar(type)) { String name = upcase(in.name(i)); if (name.length() > 8) { // silently truncate COMMENT* and HISTORY* - the addCommand and addHistory // functions keep them unique in the RecordInterface by adding a random trailing // number if (!(name.contains(commentName) || name.contains(historyName))) { // this is just a warning, everything is still ok os << LogIO::WARN << "Name is too long for keyword " << name << " - truncated to first 8 characters." << LogIO::POST; } name = name.before(8); } // comments are automatically truncated by the FITS classes String comment = in.comment(i); switch(type) { case TpBool: { Bool val; in.get(i, val); out.mk(name.chars(), val, comment.chars()); } break; case TpInt: { Int val; in.get(i, val); out.mk(name.chars(), val, comment.chars()); } break; case TpShort: { Short val; in.get(i, val); out.mk(name.chars(), val, comment.chars()); } break; case TpUInt: { uInt val; in.get(i, val); out.mk(name.chars(), int(val), comment.chars()); } break; case TpFloat: { Float val; in.get(i, val); out.mk(name.chars(), val, comment.chars()); } break; case TpDouble: { Double val; in.get(i, val); out.mk(name.chars(), val, comment.chars()); } break; case TpString: { String val; in.get(i, val); if (name.contains(commentName)) { if (val == "") { out.spaces(); } else { out.comment(val.chars()); } } else if (name.contains(historyName)) { out.history(val.chars()); } else { if (val.length() > 68) { os << LogIO::SEVERE << "Value of keyword " << name.chars() << " will be truncated at 68 characters." << LogIO::POST; ok = False; val = val.before(68); } out.mk(name.chars(), val.chars(), comment.chars()); } } break; default: os << LogIO::SEVERE << "Illegal FITS type " << Int(in.type(i)) << " for field '" << name << "': ignoring this field." << LogIO::POST; // Note that we carry on anyway ok = False; } } else if (isArray(type)) { // Find out how many like-shaped array columns there are in a row // and interleave them, i.e. so we have crval1 crpix1 cdelt1, // crval2 crpix2 cdelt2, .. Int start = i; const Int length = in.shape(i).product(); uInt ndim = in.shape(i).nelements(); // SPECIAL: NAXIS is both an array AND a scalar! if (upcase(in.name(i)) == "NAXIS") { out.mk("NAXIS", int(length)); } // To do: special treatment of the PV array here which should not be interleaved if (ndim > 2) { os << LogIO::SEVERE << ndim << " dimensional array found. " " Flattening to 1 dimension" << LogIO::POST; ok = False; ndim = 1; } uInt end = i+1; while (end < n) { // If it's not an array if (!isArray(in.type(end))) { break; } // or it's size has changed if (in.shape(end).product() != length) { break; } // or its dimensionality has changed the run has ended. if (in.shape(end).nelements() != ndim) { break; } end++; } // Advance i to the right place so we don't see these fields // more than once. i = end - 1; // name length check. Keyword names must be <= 8 characters. // also do number of elements check // Note that we emit a SEVERE error but go on any way. for (uInt j=start; j 2) { os << LogIO::SEVERE << "Name is too long for array field " << in.name(j) << " - name will be truncated to first 2 characters." << LogIO::POST; ok = False; } // at most, 99 elements per dimension if (in.shape(i)(0) > 99 || in.shape(i)(1) > 99) { os << LogIO::SEVERE << "Too many rows or columns for array field " << in.name(j) << " - the first 99 rows and the first 99 columns will be used." << LogIO::POST; ok = False; } } else { // at most 999 elements if (length > 999) { os << LogIO::SEVERE << "Too many elements for field " << in.name(j) << " - the first 999 elements will be used." << LogIO::POST; ok = False; } String slen = String::toString(length); if (in.name(j).length() + slen.length() > 8) { os << LogIO::SEVERE << "Name is too long for array field " << in.name(j) << " - name will be truncated to first " << (8-uInt(slen.length())) << " characters." << LogIO::POST; ok = False; } } } // This is inefficient because we are getting the arrays many // times. We could optimize this if this is ever a problem. for (Int k=0; k 2) name = name.before(2); // Form i_j name Int nrow = in.shape(i)(0); Int ii = k % nrow + 1; Int jj = k / nrow + 1; ostringstream ostr; if(nrow>9){ // i.e. the indices have more than one digit ostr << setfill('0') << setw(2) << ii << "_" << setfill('0') << setw(2) << jj; } else{ ostr << setw(1) << ii << "_" << setw(1) << jj; } name += String(ostr); } else { ostringstream ostr; ostr << k + 1; num = String(ostr); } if (name.length() > (8-num.length())) name = name.before(8-num.length()); switch(type) { case TpArrayBool: { Array val; in.get(j, val); Bool deleteIt; Bool *storage = val.getStorage(deleteIt); if (ndim == 2) { out.mk(name.chars(), storage[k]); } else { FITS::ReservedName fname; if (findReservedName(fname, name)) { out.mk(int(k+1), fname, storage[k]); } else { out.mk((name + num).chars(), storage[k]); } } val.putStorage(storage, deleteIt); } break; case TpArrayInt: { Array val; in.get(j, val); Bool deleteIt; Int *storage = val.getStorage(deleteIt); if (ndim == 2) { out.mk(name.chars(), storage[k]); } else { FITS::ReservedName fname; if (findReservedName(fname, name)) { out.mk(int(k+1), fname, storage[k]); } else { out.mk((name + num).chars(), storage[k]); } } val.putStorage(storage, deleteIt); } break; case TpArrayFloat: { Array val; in.get(j, val); Bool deleteIt; Float *storage = val.getStorage(deleteIt); if (ndim == 2) { out.mk(name.chars(), storage[k]); } else { FITS::ReservedName fname; if (findReservedName(fname, name)) { out.mk(int(k+1), fname, storage[k]); } else { out.mk((name + num).chars(), storage[k]); } } val.putStorage(storage, deleteIt); } break; case TpArrayDouble: { Array val; in.get(j, val); Bool deleteIt; Double *storage = val.getStorage(deleteIt); if (ndim == 2) { out.mk(name.chars(), storage[k]); } else { FITS::ReservedName fname; if (findReservedName(fname, name)) { out.mk(int(k+1), fname, storage[k]); } else { out.mk((name + num).chars(), storage[k]); } } val.putStorage(storage, deleteIt); } break; case TpArrayString: { Array val; in.get(j, val); Bool deleteIt; String *storage = val.getStorage(deleteIt); String thisVal = storage[k]; if (thisVal.length() > 68) { os << LogIO::SEVERE << "Value of keyword " << name.chars() << " will be truncated at 68 characters." << LogIO::POST; ok = False; thisVal = thisVal.before(68); } if (ndim == 2) { out.mk(name.chars(), thisVal.chars()); } else { FITS::ReservedName fname; if (findReservedName(fname, name)) { out.mk(int(k+1), fname, thisVal.chars()); } else { out.mk((name + num).chars(), thisVal.chars()); } } val.putStorage(storage, deleteIt); } break; default: os << LogIO::SEVERE << "Illegal FITS type " << Int(type) << " for field '" << name << "': ignoring this field." << LogIO::POST; // Note that we carry on anyway ok = False; } } } } else { os << LogIO::SEVERE << "Illegal FITS type " << Int(in.type(i)) << " for field '" << in.name(i) << ". 'Must be scalar or array." << LogIO::POST; // Note that we carry on anyway ok = False; } i++; } return ok; } Bool FITSKeywordUtil::getKeywords(RecordInterface &out, ConstFitsKeywordList &in, const Vector &ignore, Bool ignoreHistory) { Bool ok = True; LogIO os(LogOrigin("FITSKeywordUtil", "getKeywords", WHERE)); // Reset to the beginning of the KW list in.first(); const Regex kw1D("^[a-z0-9]*[a-z][1-9]+"); // We can have X3F, but not XF3 // This fails with more than 99 axes. const Regex kw2D("^[a-z0-9]*[a-z]0[0-9][0-9]0[0-9][0-9]"); const Regex kw2Dmodern("^[a-z][a-z]?[0-9][0-9]?[_][0-9][0-9]?"); const Regex kw2Dstandard("^[[a-z][a-z]?[0-9]?[_][0-9]?"); const Regex crota("crota"); const Regex trailing(" *$"); const Regex cd("^cd[0-9]+[_][0-9]+"); const String empty; // The complication in this function springs from the fact that we want to // combine all the indexed and matrix (i.e. PCiiijjj or PCii_jja) keywords // together into array fields in the output record. // // First we take a pass through the keywords noting the array keywords and // their minimum and maximum indexes. Unfortunately we have to ignore the // FITS keyword functions isindexed() etc. because they do not know about // all indexed keywords, e.g. user-defined or "new" // indexed keywords. // // In addition, CROTA is a special case. It is only found on the latitude // axis of direction coordinates. If we find a CROTA, we make a full length // vector of it in the output header record. This is because the FITS // header cracking routines expect all the vector fields to be length naxis // The CROTA vector will be 0, except for the actual axis that had a CROTA // CD is another special case. It is left as scalar keywords to be interpreted // by the CoordinateSystem methods according to rules coded there. std::map min1D, max1D, min2Drow, min2Dcol, max2Drow, max2Dcol; // this may be a bug in fits that it isn't in.curr() const FitsKeyword *key = in.next(); // Bool foundCROTA = False; String baseCROTA; Int naxis = -1; Int maxis = -1; while(key) { String name = downcase(key->name()); if (name == "naxis" && !key->isindexed()) { if (key->type()==FITS::LONG) { naxis = key->asInt(); } } if (name == "maxis" && !key->isindexed()) { if (key->type()==FITS::LONG) { maxis = key->asInt(); } } // if (name.contains(crota)) { foundCROTA = True; baseCROTA = name; } // if ((name.contains(kw2Dstandard) || name.contains(kw2D) || name.contains(kw2Dmodern)) && !name.contains(cd)) { Int nrow, ncol; String base; if (!splitKW2D(base, nrow, ncol, name)) { os << LogIO::SEVERE << "Illegal matrix keyword " << name << LogIO::EXCEPTION; } if (min2Drow.find(base) == min2Drow.end()) min2Drow[base] = 99999; if (min2Dcol.find(base) == min2Dcol.end()) min2Dcol[base] = 99999; if (nrow < min2Drow[base]) {min2Drow[base] = nrow;} if (nrow > max2Drow[base]) {max2Drow[base] = nrow;} if (ncol < min2Dcol[base]) {min2Dcol[base] = ncol;} if (ncol > max2Dcol[base]) {max2Dcol[base] = ncol;} } else if ((key->isindexed() || name.contains(kw1D)) && !name.contains(cd)) { String base; Int num; if (key->isindexed()) { base = name; num = key->index(); } else { splitKW1D(base, num, name); } if (min1D.find(base) == min1D.end()) min1D[base] = 99999; if (num < min1D[base]) {min1D[base] = num;} if (num > max1D[base]) {max1D[base] = num;} } key = in.next(); } if (foundCROTA) { if (naxis==-1) { os << LogIO::SEVERE << "Failed to decode naxis keyword" << LogIO::EXCEPTION; } if (maxis==-1) { min1D[baseCROTA] = 1; max1D[baseCROTA] = naxis; } else{ // apparently a FITS-IDI file min1D[baseCROTA] = 1; max1D[baseCROTA] = maxis; } } // OK, now step through actually writing all the keywords in.first(); key = in.next(); // I think it's a bug in FITS that this isn't in.curr() while(key) { String fullName = downcase(key->name()); // Naxis and Maxis are special - it is both a scalar keywords and an // indexed keyword. If we have both ignore the scalar version. if ((fullName == "naxis" || fullName == "maxis") && !key->isindexed()) { key = in.next(); continue; } // OK, it's a keyword we have to process if ((fullName.contains(kw2Dstandard) || fullName.contains(kw2D) || fullName.contains(kw2Dmodern)) && !fullName.contains(cd)) { Int thisRow, thisCol; String base; splitKW2D(base, thisRow, thisCol, fullName); thisRow -= min2Drow[base]; thisCol -= min2Dcol[base]; Int fnum = out.fieldNumber(base); Int nrow = max2Drow[base]-min2Drow[base]+1; Int ncol = max2Dcol[base]-min2Dcol[base]+1; switch (key->type()) { case FITS::LOGICAL: { if (fnum >= 0 && out.type(fnum) != TpArrayBool) { os << LogIO::SEVERE << "Ignoring field '" << fullName << "' because its type does not match already created" << " field " << base << ". Continuing." << LogIO::POST; break; } Matrix mat; if (! out.isDefined(base)) { mat.resize(nrow,ncol); mat = False; } else { out.get(base, mat); } mat(thisRow,thisCol) = key->asBool(); out.define(base, mat); } break; case FITS::STRING : { if (fnum >= 0 && out.type(fnum) != TpArrayString) { os << LogIO::SEVERE << "Ignoring field '" << fullName << "' because its type does not match already created" << " field " << base << ". Continuing." << LogIO::POST; break; } Matrix mat; if (! out.isDefined(base)) { mat.resize(nrow,ncol); mat = ""; } else { out.get(base, mat); } // I think its a bug that the FITS classes leave keywords // with trailing blank spaces, but they do. Trailing blanks // are not significant in FITS keywords. // at any rate, we need to remove them. String tmp = key->asString(); tmp.gsub(trailing, empty); mat(thisRow,thisCol) = tmp; out.define(base, mat); } break; case FITS::FLOAT : // Convert to DOUBLE!! { if (fnum >= 0 && out.type(fnum) != TpArrayDouble) { os << LogIO::SEVERE << "Ignoring field '" << fullName << "' because its type does not match already created" << " field " << base << ". Continuing." << LogIO::POST; break; } Matrix mat; if (! out.isDefined(base)) { mat.resize(nrow,ncol); mat = 0.0; } else { out.get(base, mat); } mat(thisRow,thisCol) = key->asFloat(); out.define(base, mat); } break; case FITS::DOUBLE : { if (fnum >= 0 && out.type(fnum) != TpArrayDouble) { os << LogIO::SEVERE << "Ignoring field '" << fullName << "' because its type does not match already created" << " field " << base << ". Continuing." << LogIO::POST; break; } Matrix mat; if (! out.isDefined(base)) { mat.resize(nrow,ncol); mat = 0.0; } else { out.get(base, mat); } mat(thisRow,thisCol) = key->asDouble(); out.define(base, mat); } break; case FITS::LONG : { if (fnum >= 0 && out.type(fnum) != TpArrayInt) { os << LogIO::SEVERE << "Ignoring field '" << fullName << "' because its type does not match already created" << " field " << base << ". Continuing." << LogIO::POST; break; } Matrix mat; if (! out.isDefined(base)) { mat.resize(nrow,ncol); mat = 0; } else { out.get(base, mat); } mat(thisRow,thisCol) = key->asInt(); out.define(base, mat); } break; case FITS::COMPLEX : { if (fnum >= 0 && out.type(fnum) != TpArrayComplex) { os << LogIO::SEVERE << "Ignoring field '" << fullName << "' because its type does not match already created" << " field " << base << ". Continuing." << LogIO::POST; break; } Matrix mat; if (! out.isDefined(base)) { mat.resize(nrow,ncol); mat = Complex(0,0); } else { out.get(base, mat); } mat(thisRow,thisCol) = key->asComplex(); out.define(base, mat); } break; case FITS::DCOMPLEX : { if (fnum >= 0 && out.type(fnum) != TpArrayDComplex) { os << LogIO::SEVERE << "Ignoring field '" << fullName << "' because its type does not match already created" << " field " << base << ". Continuing." << LogIO::POST; break; } Matrix mat; if (! out.isDefined(base)) { mat.resize(nrow,ncol); mat = DComplex(0,0); } else { out.get(base, mat); } mat(thisRow,thisCol) = key->asDComplex(); out.define(base, mat); } break; default: os << LogIO::SEVERE << "Unknown type for keyword '" << fullName << "'. Continuing." << LogIO::POST; } } else if (key->isindexed() || (fullName.contains(kw1D) && !fullName.contains(cd))){ String base; Int num; if (key->isindexed()) { base = fullName; num = key->index(); } else { splitKW1D(base, num, fullName); } Int offset = num - min1D[base]; Int nelm = 0; Int fnum = out.fieldNumber(base); switch (key->type()) { case FITS::LOGICAL: if (fnum >= 0 && out.type(fnum) != TpArrayBool) { os << LogIO::WARN << "Ignoring field '" << fullName << "' because its type does not match already created" << " field " << base << ". Continuing." << LogIO::POST; break; } if (! out.isDefined(base)) { Vector vec(max1D[base] - min1D[base] + 1); vec = False; vec(offset) = key->asBool(); out.define(base, vec); } else { Vector vec; out.get(base, vec); nelm = vec.size(); if(offsetasBool(); out.define(base, vec); } else{ os << LogIO::WARN << "Ignoring field '" << fullName << "' because the maximum permitted number " << nelm << " is already reached. Continuing." << LogIO::POST; } } break; case FITS::STRING : { if (fnum >= 0 && out.type(fnum) != TpArrayString) { os << LogIO::WARN << "Ignoring field '" << fullName << "' because its type does not match already created" << " field " << base << ". Continuing." << LogIO::POST; break; } String tmp = key->asString(); // I think its a bug that that the FITS classes leave keywords // with trailing blank spaces, but they do. Trailing blanks // are not significant in FITS keywords. // at any rate, we need to remove them tmp.gsub(trailing, empty); if (! out.isDefined(base)) { Vector vec(max1D[base] - min1D[base] + 1); vec = ""; vec(offset) = tmp; out.define(base, vec); } else { Vector vec; out.get(base, vec); nelm = vec.size(); if(offset= 0 && out.type(fnum) != TpArrayDouble) { os << LogIO::WARN << "Ignoring field '" << fullName << "' because its type does not match already created" << " field " << base << ". Continuing." << LogIO::POST; break; } if (! out.isDefined(base)) { Vector vec(max1D[base] - min1D[base] + 1); vec = 0.0; vec(offset) = key->asFloat(); out.define(base, vec); } else { Vector vec; out.get(base, vec); nelm = vec.size(); if(offsetasFloat(); out.define(base, vec); } else{ os << LogIO::WARN << "Ignoring field '" << fullName << "' because the maximum permitted number " << nelm << " is already reached. Continuing." << LogIO::POST; } } break; case FITS::DOUBLE : if (fnum >= 0 && out.type(fnum) != TpArrayDouble) { os << LogIO::WARN << "Ignoring field '" << fullName << "' because its type does not match already created" << " field " << base << ". Continuing." << LogIO::POST; break; } if (! out.isDefined(base)) { Vector vec(max1D[base] - min1D[base] + 1); vec = 0.0; vec(offset) = key->asDouble(); out.define(base, vec); } else { Vector vec; out.get(base, vec); nelm = vec.size(); if(offsetasDouble(); out.define(base, vec); } else{ os << LogIO::WARN << "Ignoring field '" << fullName << "' because the maximum permitted number " << nelm << " is already reached. Continuing." << LogIO::POST; } } break; case FITS::LONG : if (fnum >= 0 && out.type(fnum) != TpArrayInt) { os << LogIO::WARN << "Ignoring field '" << fullName << "' because its type does not match already created" << " field " << base << ". Continuing." << LogIO::POST; break; } if (! out.isDefined(base)) { Vector vec(max1D[base] - min1D[base] + 1); vec = 0; vec(offset) = key->asInt(); out.define(base, vec); } else { Vector vec; out.get(base, vec); nelm = vec.size(); if(offsetasInt(); out.define(base, vec); } else{ os << LogIO::WARN << "Ignoring field '" << fullName << "' because the maximum permitted number " << nelm << " is already reached. Continuing." << LogIO::POST; } } break; case FITS::COMPLEX : if (fnum >= 0 && out.type(fnum) != TpArrayComplex) { os << LogIO::WARN << "Ignoring field '" << fullName << "' because its type does not match already created" << " field " << base << ". Continuing." << LogIO::POST; break; } if (! out.isDefined(base)) { Vector vec(max1D[base] - min1D[base] + 1); vec = Complex(0,0); vec(offset) = key->asComplex(); out.define(base, vec); } else { Vector vec; out.get(base, vec); nelm = vec.size(); if(offsetasComplex(); out.define(base, vec); } else{ os << LogIO::WARN << "Ignoring field '" << fullName << "' because the maximum permitted number " << nelm << " is already reached. Continuing." << LogIO::POST; } } break; case FITS::DCOMPLEX : if (fnum >= 0 && out.type(fnum) != TpArrayDComplex) { os << LogIO::WARN << "Ignoring field '" << fullName << "' because its type does not match already created" << " field " << base << ". Continuing." << LogIO::POST; break; } if (! out.isDefined(base)) { Vector vec(max1D[base] - min1D[base] + 1); vec = DComplex(0,0); vec(offset) = key->asDComplex(); out.define(base, vec); } else { Vector vec; out.get(base, vec); nelm = vec.size(); if(offsetasDComplex(); out.define(base, vec); } else{ os << LogIO::WARN << "Ignoring field '" << fullName << "' because the maximum permitted number " << nelm << " is already reached. Continuing." << LogIO::POST; } } break; default: os << LogIO::SEVERE << "Unknown type for keyword '" << fullName << "'. Continuing." << LogIO::POST; } } else { // It's a scalar if (out.isDefined(fullName) && fullName != "naxis") { os << LogIO::WARN << "Scalar keyword " << fullName << " will overwrite array field of same name. Continuing." << LogIO::POST; } switch (key->type()) { case FITS::LOGICAL: out.define(fullName,key->asBool()); break; case FITS::STRING : { String tmp = key->asString(); // I think its a bug that that the FITS classes leave keywords // with trailing blank spaces, but they do. Trailing blanks // are not significant in FITS keywords. // at any rate, we need to remove them tmp.gsub(trailing, empty); out.define(fullName, tmp); } break; case FITS::FLOAT : out.define(fullName,key->asFloat()); break; case FITS::DOUBLE : out.define(fullName,key->asDouble()); break; case FITS::LONG : out.define(fullName,key->asInt()); break; case FITS::COMPLEX : out.define(fullName,key->asComplex()); break; case FITS::DCOMPLEX : out.define(fullName,key->asDComplex()); break; case FITS::NOVALUE : // Skip all other than history if (!ignoreHistory && key->kw().name() == FITS::HISTORY) { addHistory(out, key->comm()); } break; default: os << LogIO::SEVERE << "Unknown type for keyword '" << fullName << "'. Continuing." << LogIO::POST; } } if (out.isDefined(fullName) && key->comm() != 0 && key->commlen()>0) { out.setComment(fullName, key->comm()); } key = in.next(); } removeKeywords(out, ignore); return ok; } void FITSKeywordUtil::removeKeywords(RecordInterface &out, const Vector &ignore) { LogIO os(LogOrigin("FITSKeywordUtil", "removeKeywords", WHERE)); const Int nregex = ignore.nelements(); Regex *regexlist = new Regex[nregex]; AlwaysAssert(regexlist, AipsError); Int i; for (i=0; i < nregex; i++) { regexlist[i] = Regex(ignore(i)); } const Int nfields = out.nfields(); // Go backwards because removing a field causes the previous fields to // be renumbered. String nametmp; for (i=nfields - 1; i>= 0; i--) { nametmp = out.name(i); for (Int j=0; j0) ostr << shape(0); for (uInt i=1;i 71) { ok = False; } return ok; } static void addText(RecordInterface &header, const String &comment, const char *leader) { static MLCG random; static Bool init = False; if (!init) { Time now; init = True; random.seed1(long(now.modifiedJulianDay()*86400.0)); } Vector lines = stringToVector(comment, '\n'); // Use a random number to prevent a CUBIC behaviour: // (N cards * N passes through the following loop * N for isDefined) String keyname; for (uInt i=0; i(random.asuInt()); do { ostringstream os; os << offset; keyname = leader + String(os); offset++; } while (header.isDefined(keyname)); header.define(keyname, lines(i)); } } void FITSKeywordUtil::addComment(RecordInterface &header, const String &comment) { addText(header, comment, "comment"); } void FITSKeywordUtil::addHistory(RecordInterface &header, const String &comment) { addText(header, comment, "history"); } } //# NAMESPACE CASACORE - END casacore-3.7.1/fits/FITS/FITSKeywordUtil.h000066400000000000000000000207301476623553700201610ustar00rootroot00000000000000//# FITSKeywordUtil.h: Class of static functions to help with FITS Keywords. //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef FITS_FITSKEYWORDUTIL_H #define FITS_FITSKEYWORDUTIL_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class ConstFitsKeywordList; class FitsKeywordList; class RecordInterface; class IPosition; class String; // // A class with static functions to help deal with FITS Keywords. // // // // // //
      • General knowledge of FITS, and particularly FITS keywords, is // assumed. //
      • Presumably you are using this class in conjunction // with the "native" // FitsKeywordList //
      • You also need to understand the // RecordInterface // class. // // // // This is a collection of static utility functions for use with FITS // keywords. // // // // This class provides functions to conveniently interconvert between Casacore // types and a FitsKeywordList which is needed by the native FITS classes. // It is more convenient to maintain the list within Casacore // as a Record, so we only need methods to turn a FitsKeywordList into a // Record, and vice versa. // // Note that it is not necessary to construct a FITSKeywordUtil object // since you can use its static functions directly. // // // // This example shows how you put values from a Record into a // FItsKeywordList. // // Record rec; // rec.define("hello", 6.5); // rec.define("world", True); // Vector naxis(5); // naxis(0) = 128; // naxis(1) = 64; // naxis(2) = 32; // naxis(3) = 16; // naxis(4) = 8; // rec.define("naxis", naxis); // // fields can have comments // rec.setComment("hello","A comment for HELLO"); // // Add a comment to the rec // FITSKeywordUtil::addComment(rec,"My comment goes here"); // // Create an empty FitsKeywordList, containing only "SIMPLE=T" // FitsKeywordList kwl = FITSKeywordUtil::makeKeywordList(); // // and add the fields in rec to this list // FITSKeywordUtil::addKeywords(kwl, rec); // // // // // This example shows how you extract fits keywords into a Record. // // Record rec; // FitsKeywordList kwl; // ConstFitsKeywordList kwlRO; // Vector ignore(1); // ignore(1)= "simple"; // ignore the SIMPLE keyword // FITSKeywordUtil::getKeywords(rec, kwlRO, ignore); // // // // // The FitsKeywordList class can be somewhat tedious to use, as it deals with, // e.g., char* pointers rather than Strings. This class makes it easy to // interconvert between FITS keywords and Casacore types. // // // //
      • Get/set history as a vector of strings as well. //
      • This could be a namespace rather than a class. // class FITSKeywordUtil { public: // Make an initial FitsKeywordList for either a FITS primary header // or a FITS extension header (image or table). A primary header // requires "SIMPLE = T", an extension header "XTENSION = IMAGE " // or "XTENSION = BINTABLE " for image or table, respectively. // This is required of any FITS keyword list. This is provided as // a convenience so that you do not have to know anything about the class // FitsKeywordList. static FitsKeywordList makeKeywordList(Bool primHead=True, Bool binImage=True); // Add the fields from in to the out FitsKeywordList as keywords. // Upcases field names, turns arrays into indexed keywords, tries to interleave // e.g. crval, crpix, etc. // COMMENT* are standalone comments, and HISTORY* are history cards. // (COMMENT and HISTORY may be of any capitalization). Note however that // you will generally add History keywords with the class // FITSHistoryUtil. // Returns False in the following instances: //
          //
        • The value of a string field is longer than 68 characters. The value is truncated. //
        • An illegal type for a FITS keyword (e.g. Complex). The field is ignored. //
        • An array field has more than 2 dimensions. The field is stored as a vector. //
        • An array field name is too long to hold the name and the index characters. The name is truncated. //
        • Too many rows or columns for a 2D array (first 999 in each are used). //
        • Too many elements in a 1D array (first 999 are used). //
        • A field is neither a scalar or an array (e.g. a record). The field is ignored. //
        static Bool addKeywords(FitsKeywordList &out, const RecordInterface &in); // Extract keywords from in and define them in out. // Output field names are downcased. Keywords matching // the names in ignore (which are treated as regular expressions) are // not created in out. This test happens after the field names // have been downcased. // All indexed keywords will be ignored if the root name is in the ignore // vector (e.g. NAXIS implies NAXIS4 and other indexed NAXIS keywords // are ignored). // By default history keywords are ignored, since they // should be handled in class // FITSHistoryUtil. // This always returns True. static Bool getKeywords(RecordInterface &out, ConstFitsKeywordList &in, const Vector &ignore, Bool ignoreHistory=True); // Remove some keywords from a record. This can be useful // if, e.g., you first need to construct a coordinate system from the // header, but you later want to remove CROTA etc. // The strings in the ignore vector are treated as regular expressions. static void removeKeywords(RecordInterface &out, const Vector &ignore); // Convert a TDIMnnn keyword value into an IPosition. This returns // False if the tdim string has an invalid format. static Bool fromTDIM(IPosition& shape, const String& tdim); // Convert an IPosition to a String appropriate for use as the // value of a TDIMnnn keyword. This returns False if the // converted string has more than 71 characters // (making it impossible to be used as a string keyword value). static Bool toTDIM(String& tdim, const IPosition& shape); // Add a comment/history to the supplied record. It will automatically // figure out a unique name and add it to the end. If the comment contains // embedded newlines this function will break the string across multiple // FITS comment entries. At present it will not however make sure that the // strings are short enough (i.e. <= 72 characters per line). // // Note that while you can add history anywhere into header, in the actual // keyword list they will always appear after the END keyword. // Note however that you will generally manipulate History keywords with // the class FITSHistoryUtil. // static void addComment(RecordInterface &header, const String &comment); static void addHistory(RecordInterface &header, const String &history); // }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/fits/FITS/FITSMultiTable.cc000066400000000000000000000204231476623553700200760ustar00rootroot00000000000000//# Fitsmultitablex.h: View multiple FITS files as a single table //# Copyright (C) 1995,1996,1997,1998,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Used for debug print statements // #include &fileNames, FITSTabular* (*tabMaker)(const String&)) : table_p(0), file_names_p(fileNames.copy()), nfiles_p(fileNames.nelements()), which_file_p(0), hasChanged_p(False), row_p(RecordInterface::Variable) { AlwaysAssert(nfiles_p > 0, AipsError); for (uInt i=0;iisValid()) { which_file_p = i; break; } } row_p.restructure(table_p->description()); row_p = table_p->currentRow(); } FITSMultiTable::~FITSMultiTable() { delete table_p; table_p = 0; } Bool FITSMultiTable::isValid() const { return table_p->isValid(); } const TableRecord &FITSMultiTable::keywords() const { return table_p->keywords(); } const RecordDesc &FITSMultiTable::description() const { return table_p->description(); } const Record &FITSMultiTable::units() const { return table_p->units(); } const Record &FITSMultiTable::displayFormats() const { return table_p->displayFormats(); } const Record &FITSMultiTable::nulls() const { return table_p->nulls(); } Bool FITSMultiTable::pastEnd() const { return (which_file_p >= nfiles_p); } void FITSMultiTable::next() { table_p->next(); Bool status = True; uInt thisWhich = which_file_p; if (table_p->pastEnd()) { which_file_p++; RecordDesc oldDescription = table_p->description(); status = False; if (which_file_p >= nfiles_p) status = True; while (which_file_p < nfiles_p && ! status) { status = table_p->reopen(file_names_p(which_file_p)); if (!status) { cerr << "FITSMultiTable::next() - Problem opening : " << file_names_p(which_file_p) << " - skipping this file " << endl; which_file_p++; } else { if (oldDescription != description()) { hasChanged_p = True; row_p.restructure(table_p->description()); } } } } // if status is False // reopen previous successfully opened file if (!status) { table_p->reopen(file_names_p(thisWhich)); } // if that failed, then that likely means that none of these will // work and we should probably throw the exception that will likely // happen here. row_p = table_p->currentRow(); } const Record &FITSMultiTable::currentRow() const { return row_p; } // This is truely bizare. For CFRONT based compiler the // following is required in order for this all to compile. // If this code is placed in place in filesInTimeRange where it // is used, both Centerline and Sun's CFRONT compilers complain // about not being able to find operator << (class ostream, unsigned int) // It seems to be tied to Vector in some sense since // commenting out all occurences of them makes the compiler // happy (but the code unusable, obviously). Moving the output // cout outside of filesInTimeRange makes the problem go away. // It is obviously a complicated interacting involving other // elements of this class as I am unable to reproduce it except // in this class. void timeRangeStatusMsg(uInt count) { cout << "Found " << count << " files in specified time range." << endl; } Vector FITSMultiTable::filesInTimeRange(const String &directoryName, const Time &startTime, const Time &endTime, Bool verboseErrors, Bool verboseStatus) { Time t1(startTime), t2(endTime); // Should not be necessary Double timeRange = t2 - t1; // If the screwed up start and end, work anyway if (timeRange < 0) { return filesInTimeRange(directoryName, endTime, startTime, verboseStatus, verboseErrors); } File file(directoryName); if (! file.isDirectory()) { throw(AipsError(directoryName + " is not a directory")); } Directory dir(file); Path path(file.path()); uInt nfiles = dir.nEntries(); Vector allfiles(nfiles); Vector allStartTimes(nfiles); uInt count = 0; // If this is still in use in the year 3xxx, it will fail! DirectoryIterator diriter(dir, Regex("^[12][0-9][0-9][0-9]_[0-9][0-9]_[0-9][0-9]_" "[0-9][0-9]:[0-9][0-9]:[0-9][0-9].*\\.fits$")); while (!diriter.pastEnd()) { allfiles(count) = diriter.name();; allStartTimes(count) = timeFromFile(allfiles(count)) - startTime; count++; diriter++; } Vector files(allfiles(Slice(0, count))); Vector startTimes(allStartTimes(Slice(0, count))); GenSort::sort(files); // Sorted in ascending order GenSort::sort(startTimes); // should sort exactly the same as files // It would be nice if only a single sort were needed // Work out the end times, assume they may be as late as the start time // of the next file. Guard the end with a large number Vector endTimes(files.nelements()); uInt i; for (i=0; i + 1< endTimes.nelements(); i++) { endTimes(i) = startTimes(i + 1); } if (endTimes.nelements() > 0) { endTimes(endTimes.nelements() - 1) = 1.0E+30; // + infinity } // Work out which files might have values in the appropriate range count = 0; for (i=0; i < files.nelements(); i++) { if (startTimes(i) <= timeRange && endTimes(i) >= 0.0) { count++; } } Vector foundFiles(count); count = 0; for (i=0; i < files.nelements(); i++) { if (startTimes(i) <= timeRange && endTimes(i) >= 0.0) { foundFiles(count) = dir.path().originalName() + "/" + files(i); count++; } } if (verboseStatus) { timeRangeStatusMsg(count); } return foundFiles; } FITSTabular* FITSMultiTable::defaultMaker(const String& fileName) { return new FITSTable(fileName); } Time FITSMultiTable::timeFromFile(const String &fileName) { // try to extract time, assume everything is a number that should be ok in this usage // just make sure fileName is a basename Path fpath(fileName); String fbase(fpath.baseName()); const char zero = '0'; uInt year = fbase[3] - zero + 10*(fbase[2] - zero) + 100*(fbase[1] - zero) + 1000*(fbase[0] - zero); uInt month = fbase[6] - zero + 10*(fbase[5] - zero); uInt day = fbase[9] - zero + 10*(fbase[8] - zero); uInt hour = fbase[12] - zero + 10*(fbase[11] - zero); uInt minutes = fbase[15] - zero + 10*(fbase[14] - zero); uInt seconds = fbase[18] - zero + 10*(fbase[17] - zero); return Time(year, month, day, hour, minutes, seconds*1.0); } } //# NAMESPACE CASACORE - END casacore-3.7.1/fits/FITS/FITSMultiTable.h000066400000000000000000000115071476623553700177430ustar00rootroot00000000000000//# FITSMultiTable.h: View multiple FITS files as a single table //# Copyright (C) 1995,1996,1997 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef FITS_FITSMULTITABLE_H #define FITS_FITSMULTITABLE_H #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // View multiple FITS files as a single table // // // // // // A FITSMultiTable is used to view a collection of FITS files on disk as a // single Table. That is, when next() is called, when one Table ends the next // is reopened until all files are exhausted. The FITS files must all have the // same description. Something clever should be done about the keywords. // // // // // // // // // // class FITSMultiTable : public FITSTabular { public: // The FITS files associated with the fileNames must all have the same // description, the second argument is a function to generate the // FITSTabular If not specified, a generic FITSTable is assumed. // The returned pointer IS controlled by this object. FITSMultiTable(const Vector &fileNames, FITSTabular* (*tabMaker)(const String &) = 0); ~FITSMultiTable(); virtual Bool isValid() const; virtual const TableRecord &keywords() const; virtual const RecordDesc &description() const; virtual const Record &units() const; virtual const Record &displayFormats() const; virtual const Record &nulls() const; virtual const String &name() const { return table_p->name(); } // Only returns True when all files are exhausted. virtual Bool pastEnd() const; // When end of data is hit on the current file, the next file is opened // automatically. virtual void next(); virtual const Record ¤tRow() const; // get the list of file names const Vector& fileNames() const { return file_names_p;} // Has the descriptor changed from when the file was opened virtual Bool hasChanged() const { return hasChanged_p;} // set hasChanged to False - used after hasChanged has been checked void resetChangedFlag() { hasChanged_p = False;} // A helper function to generate a list of fileNames. This function returns // all the files in "directoryName" which have the form // yyyy_mm_dd_hh:mm:ss_*.fits and which are (even partially) // in the time range specified by startTime and endTime. It is used to // generate a set of file names for use in the FITSMultiTable constructor. // If verboseStatus is True, some status messages appear on cout. // If verboseErrors is True improperly named files names (not matching the above // pattern) are named on cerrt. static Vector filesInTimeRange(const String &directoryName, const Time &startTime, const Time &endTime, Bool verboseErrors = False, Bool verboseStatus = False); // return the time as found in the given string using the form given above // There are no sanity checks in this subroutine static Time timeFromFile(const String &fileName); private: // Undefined and inaccessible FITSMultiTable(); FITSMultiTable(const FITSMultiTable &other); FITSMultiTable &operator=(const FITSMultiTable &other); FITSTabular *table_p; Vector file_names_p; uInt nfiles_p; uInt which_file_p; Bool hasChanged_p; Record row_p; FITSTabular* defaultMaker(const String& fileName); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/fits/FITS/FITSReader.cc000066400000000000000000000641071476623553700172450ustar00rootroot00000000000000//# FITSReader.cc: Parse a FITS disk file. //# Copyright (C) 1993,1994,1995,1996,1998,1999,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include namespace casacore { // Display basic info and the keyword list void showHDU(HeaderDataUnit *h) { LogIO os; os << LogOrigin("FITSReader", "showHDU", WHERE) << LogIO::DEBUGGING << "Data type " << h->datatype() << "\n" << "Data size " << (uInt)(h->fitsdatasize()) << "\n" << "Dimensions " << h->dims() << "\n" << LogIO::POST; for (int n = 0; n < h->dims(); n++) { os << LogOrigin("FITSReader", "showHDU", WHERE) << LogIO::NORMAL5 << "Axis " << (n + 1) << " size " << h->dim(n) << LogIO::POST; } //os << LogOrigin("FITSReader", "showHDU", WHERE) // << LogIO::NORMAL << "Keyword List:" << LogIO::POST; //ostringstream oss; //oss << *h << '\n'; //os << LogOrigin("FITSReader", "showHDU", WHERE) // << LogIO::NORMAL << String(oss) << '\n'; //FitsKeywordList *k = new FitsKeywordList(h->kwlist()); //k->first(); //ostringstream oss; //FitsKeyword *x = k->next(); //for (int i = 1; x != 0; ++i, x = k->next()) // oss << /*i << ". " <<*/ *x; //os << LogOrigin("FITSReader", "showHDU", WHERE) // << LogIO::NORMAL << String(oss) << '\n'; h->firstkw(); ostringstream oss; for (const FitsKeyword *x = h->nextkw(); x != 0; x = h->nextkw()) { int m = 0; if (x->kw().name() == FITS::ERRWORD) { oss << "ERROR!"; } else { oss << x->name(); m = String(x->name()).length(); if (x->index() != 0) { oss << x->index(); m += String::toString(x->index()).length(); } int n; Complex c; DComplex y; IComplex z; if (x->value()) { for (int i = 0; i < 8 - m; i++) oss << ' '; oss << "= "; switch (x->type()) { case FITS::NOVALUE: break; case FITS::LOGICAL: oss.width(22); oss << right; oss << ((*((Bool *)x->value()) == True) ? "T" : "F"); break; case FITS::BIT: oss.width(22); oss << right; oss << "*****"; break; case FITS::CHAR: oss.width(22); oss << right; oss << *((char *)x->value()); break; case FITS::BYTE: oss.width(22); oss << right; n = *((uChar *)x->value()); oss << n; break; case FITS::SHORT: oss.width(22); oss << right; oss << *((short *)x->value()); break; case FITS::LONG: oss.width(22); oss << right; oss << *((FitsLong *)x->value()); break; case FITS::STRING: oss << left; oss << "\'" << (char*)x->value() << "\'"; break; case FITS::FLOAT: oss.width(22); oss << right; oss << *((float *)x->value()); break; case FITS::DOUBLE: oss.width(22); oss << right; oss << *((double *)x->value()); break; case FITS::COMPLEX: oss.width(22); oss << right; c = *(Complex *)x->value(); oss << "(" << c.real() << "," << c.imag() << ")"; break; case FITS::DCOMPLEX: y = *(DComplex *)x->value(); oss.width(22); oss << right; oss << "(" << y.real() << "," << y.imag() << ")"; break; case FITS::ICOMPLEX: z = *(IComplex *)x->value(); oss.width(22); oss << right; oss << "(" << z.real() << "," << z.imag() << ")"; break; default: oss << "*****"; break; } if (x->commlen()) oss << " /" << x->comm() ; oss << "\n"; } else { if (x->commlen()) oss << ' ' << x->comm() ; oss << "\n"; } } } os << LogOrigin("FITSReader", "showHDU", WHERE) << LogIO::NORMAL << String(oss) << LogIO::POST; } // Read the data in a Primary Group and display the first few groups #define DOGROUP(Z) void showPrimaryGroup(PrimaryGroup &x) { \ LogIO os; \ int i, j; \ int ngroup_to_display = 2; \ int nele_to_display = 6; \ showHDU(&x); \ if (x.err() != HeaderDataUnit::OK) { \ os << LogOrigin("FITSReader", "showPrimaryGroup", WHERE) \ << LogIO::SEVERE \ << "Error occured during construction process" \ << LogIO::POST; \ } \ os << LogOrigin("FITSReader", "showPrimaryGroup", WHERE) \ << LogIO::NORMAL \ << x.gcount() << " groups total, display first " << nele_to_display \ << " elements of the first " << ngroup_to_display << " groups\n"; \ for (i = 0; i < x.gcount(); ++i) { \ x.read(); \ if (i < ngroup_to_display) { \ os << "Group " << i << " parms: " ; \ for (j = 0; j < x.pcount(); ++j) \ os << " " << x.parm(j); \ os << "\n"; \ os << "Group " << i << " data: " ; \ for (j = 0; j < 3 * nele_to_display ; j++) \ os << " " << x(j); \ os << "... \n"; \ } \ } \ os << LogIO::POST; \ delete &x; \ } // Read the data in a Primary Array and display the first few data points #define DOARRAY(Z) void showPrimaryArray(PrimaryArray &x) { \ LogIO os; \ int i, j, n0, n1; \ if (x.fitsdatasize()){ \ int ne = x.fitsdatasize(); \ x.read( ne ); \ } \ showHDU(&x); \ if (x.err() != HeaderDataUnit::OK) { \ os << LogOrigin("FITSReader", "showPrimaryArray", WHERE) \ << LogIO::SEVERE \ << "Error occured during construction process" \ << LogIO::POST; \ } \ os << LogOrigin("FITSReader", "showPrimaryArray", WHERE) \ << LogIO::NORMAL; \ if (x.dims() == 2) { \ n0 = x.dim(0) > 60 ? 60 : x.dim(0); \ n1 = x.dim(1) > 60 ? 60 : x.dim(1); \ for (i = 0; i < n0; ++i) \ for (j = 0; j < n1; ++j) \ os << "(" << i << "," << j << ") = " \ << x(i,j) << "\n"; \ } \ os << LogIO::POST; \ delete &x; \ } // now actually make the necessary versions of the above DOGROUP(unsigned char) DOGROUP(short) DOGROUP(FitsLong) DOGROUP(float) DOGROUP(double) DOARRAY(unsigned char) DOARRAY(short) DOARRAY(FitsLong) DOARRAY(float) DOARRAY(double) #undef DOGROUP #undef DOARRAY // Read and display a binary table void showBinaryTable(BinaryTableExtension &x) { LogIO os; if (x.err() != 0) { os << LogOrigin("FITSReader", "showBinaryTable", WHERE) << LogIO::WARN << "BT ERROR! " << x.err() << LogIO::POST; return; } showHDU(&x); ostringstream oss; oss << x.nrows() << " rows, " << x.ncols() << " cols, " << x.fitsdatasize() << " bytes total\n"; os << LogOrigin("FITSReader", "showBinaryTable", WHERE) << LogIO::NORMAL << String(oss) << LogIO::POST; oss.str(""); oss << "\nTable Data\n"; int i; for (i = 0; i < x.ncols(); ++i) { oss << "Col " << i << ": " << x.field(i).nelements() << " " << x.field(i).fieldtype() << " " << x.ttype(i) << " " << x.tunit(i) << "\n"; } os << LogOrigin("FITSReader", "showBinaryTable", WHERE) << LogIO::DEBUGGING << String(oss) << LogIO::POST; oss.str(""); x.read(x.nrows()); char *theheap = 0; if (x.pcount()) { if (x.notnull(x.theap())) { int heapOffset = x.theap() - x.rowsize()*x.nrows(); // skip to the start of the heap char *junk = new char[heapOffset]; x.ExtensionHeaderDataUnit::read(junk, heapOffset); } theheap = new char [x.pcount()]; x.ExtensionHeaderDataUnit::read(theheap, x.pcount()); } FITS::ValueType *vatypes = new FITS::ValueType[x.ncols()]; void **vaptr = new void *[x.ncols()]; VADescFitsField *va = new VADescFitsField[x.ncols()]; for (i = 0; i < x.ncols(); ++i) { vaptr[i] = 0; if (x.field(i).fieldtype() == FITS::VADESC) { int maxsize; FITS::parse_vatform(x.tform(i), vatypes[i], maxsize); x.bind(i, va[i]); if (vatypes[i] == FITS::NOVALUE) { oss << "Error in VA desc format for column " << i << " : " << x.tform(i) << '\n'; } else { switch (vatypes[i]) { case FITS::LOGICAL: vaptr[i] = (void *)(new FitsLogical[maxsize]); break; case FITS::BIT: { Int nbytes = maxsize / 8; if (maxsize % 8) nbytes++; maxsize = nbytes; } CASACORE_FALLTHROUGH; case FITS::BYTE: vaptr[i] = (void *)(new unsigned char[maxsize]); break; case FITS::SHORT: vaptr[i] = (void *)(new short[maxsize]); break; case FITS::LONG: vaptr[i] = (void *)(new FitsLong[maxsize]); break; case FITS::CHAR: vaptr[i] = (void *)(new char[maxsize]); break; case FITS::FLOAT: vaptr[i] = (void *)(new float[maxsize]); break; case FITS::DOUBLE: vaptr[i] = (void *)(new double[maxsize]); break; case FITS::COMPLEX: vaptr[i] = (void *)(new Complex[maxsize]); break; case FITS::DCOMPLEX: vaptr[i] = (void *)(new DComplex[maxsize]); break; default: oss << "Impossible VADesc type in column " << i << " : " << vatypes[i] << '\n'; break; } } } else { vatypes[i] = FITS::NOVALUE; } } int displayLines = 5; int displayCols = 20; oss << "the first " << displayCols << " cols of the first " << displayLines << " rows:\n"; os << LogOrigin("FITSReader", "showBinaryTable", WHERE) << LogIO::NORMAL << String(oss) << LogIO::POST; oss.str(""); for (int n = 0; n < displayLines && n < x.nrows(); ++n) { oss << x.currrow() << ": | "; for (i = 0; i < displayCols && i < x.ncols(); ++i) { if (i != 0) oss << "| "; if (x.field(i).nelements() != 0) oss << x.field(i) << ' '; if (x.field(i).fieldtype() == FITS::VADESC) { FitsVADesc thisva = va[i](); oss << " VA: "; if (thisva.num()) { switch (vatypes[i]) { case FITS::LOGICAL: { FitsLogical *vptr = (FitsLogical *)(vaptr[i]); FITS::f2l(vptr, (void *)(theheap + thisva.offset()), thisva.num()); oss << vptr[0]; for (int k=1;k> k%8)); } } break; case FITS::BYTE: { unsigned char *vptr = (unsigned char *)(vaptr[i]); FITS::f2l(vptr, (void *)(theheap + thisva.offset()), thisva.num()); oss << (int)vptr[0]; for (int k=1;k *paB; PrimaryArray *paS; PrimaryArray *paL; PrimaryArray *paF; PrimaryArray *paD; PrimaryGroup *pgB; PrimaryGroup *pgS; PrimaryGroup *pgL; PrimaryGroup *pgF; PrimaryGroup *pgD; BinaryTableExtension *bt; AsciiTableExtension *at; LogIO os; os << LogOrigin("FITSReader", "listFits", WHERE) << LogIO::NORMAL << "read fitsfile=" << fitsfile << LogIO::POST; FitsInput fin(fitsfile,FITS::Disk); os << LogOrigin("FITSReader", "listFits", WHERE) << LogIO::NORMAL5 << "FitsInput object created ok." << LogIO::POST; if (fin.err() == FitsIO::IOERR) { os << LogOrigin("FITSReader", "listFits", WHERE) << LogIO::WARN << "Error opening FITS input." << LogIO::POST; } else if (fin.err()) { os << LogOrigin("FITSReader", "listFits", WHERE) << LogIO::WARN << "Error reading initial record -- exiting." << LogIO::POST; } const int NMAXERRS = 5; int nerrs; for(nerrs = 0; nerrs < NMAXERRS && fin.rectype() != FITS::EndOfFile; ) { if (fin.rectype() == FITS::HDURecord) { switch (fin.hdutype()) { case FITS::PrimaryArrayHDU: os << LogOrigin("FITSReader", "listFits", WHERE) << LogIO::NORMAL << "Primary Array HDU ------>>>" << LogIO::POST; os << LogIO::DEBUGGING << "Data type is " << fin.datatype() << LogIO::POST; switch (fin.datatype()) { case FITS::BYTE: paB = new PrimaryArray(fin); showPrimaryArray(*paB); break; case FITS::SHORT: paS = new PrimaryArray(fin); showPrimaryArray(*paS); break; case FITS::LONG: paL = new PrimaryArray(fin); showPrimaryArray(*paL); break; case FITS::FLOAT: paF = new PrimaryArray(fin); showPrimaryArray(*paF); break; case FITS::DOUBLE: paD = new PrimaryArray(fin); showPrimaryArray(*paD); break; default: break; } os << LogOrigin("FITSReader", "listFits", WHERE) << LogIO::NORMAL << "<<<------ Primary Array HDU" << LogIO::POST; break; case FITS::PrimaryGroupHDU: os << LogOrigin("FITSReader", "listFits", WHERE) << LogIO::NORMAL << "Primary Group HDU ------>>>" << LogIO::POST; os << LogIO::DEBUGGING << "Data type is " << fin.datatype() << LogIO::POST; switch (fin.datatype()) { case FITS::BYTE: pgB = new PrimaryGroup(fin); showPrimaryGroup(*pgB); break; case FITS::SHORT: pgS = new PrimaryGroup(fin); showPrimaryGroup(*pgS); break; case FITS::LONG: pgL = new PrimaryGroup(fin); showPrimaryGroup(*pgL); break; case FITS::FLOAT: pgF = new PrimaryGroup(fin); showPrimaryGroup(*pgF); break; case FITS::DOUBLE: pgD = new PrimaryGroup(fin); showPrimaryGroup(*pgD); break; default: break; } os << LogOrigin("FITSReader", "listFits", WHERE) << LogIO::NORMAL << "<<<------ Primary Group HDU" << LogIO::POST; break; case FITS::AsciiTableHDU: os << LogOrigin("FITSReader", "listFits", WHERE) << LogIO::NORMAL << "Ascii Table HDU ------>>>" << LogIO::POST; os << LogIO::DEBUGGING << "Data type is " << fin.datatype() << LogIO::POST; at = new AsciiTableExtension(fin); showBinaryTable(*at); os << LogOrigin("FITSReader", "listFits", WHERE) << LogIO::NORMAL << "<<<------ Ascii Table HDU" << LogIO::POST; break; case FITS::BinaryTableHDU: os << LogOrigin("FITSReader", "listFits", WHERE) << LogIO::NORMAL << "Binary Table HDU ------>>>" << LogIO::POST; os << LogIO::DEBUGGING << "Data type is " << fin.datatype() << LogIO::POST; bt = new BinaryTableExtension(fin); showBinaryTable(*bt); os << LogOrigin("FITSReader", "listFits", WHERE) << LogIO::NORMAL << "<<<------ Binary Table HDU" << LogIO::POST; break; case FITS::ImageExtensionHDU: os << LogOrigin("FITSReader", "listFits", WHERE) << LogIO::NORMAL << "Image Extension HDU ------>>>" << LogIO::POST; os << LogIO::DEBUGGING << "Data type is " << fin.datatype() << LogIO::POST; switch (fin.datatype()) { case FITS::BYTE: paB = new ImageExtension(fin); showPrimaryArray(*paB); break; case FITS::SHORT: paS = new ImageExtension(fin); showPrimaryArray(*paS); break; case FITS::LONG: paL = new ImageExtension(fin); showPrimaryArray(*paL); break; case FITS::FLOAT: paF = new ImageExtension(fin); showPrimaryArray(*paF); break; case FITS::DOUBLE: paD = new ImageExtension(fin); showPrimaryArray(*paD); break; default: break; } os << LogOrigin("FITSReader", "listFits", WHERE) << LogIO::NORMAL << "<<<------ Image Extension HDU" << LogIO::POST; break; case FITS::PrimaryTableHDU: os << LogOrigin("FITSReader", "listFits", WHERE) << LogIO::NORMAL << "Primary Table HDU ------>>>" << LogIO::POST; os << LogIO::DEBUGGING << "Data type is " << fin.datatype() << LogIO::POST; switch (fin.datatype()) { case FITS::BYTE: paB = new PrimaryTable(fin); showPrimaryArray(*paB); break; case FITS::SHORT: paS = new PrimaryTable(fin); showPrimaryArray(*paS); break; case FITS::LONG: paL = new PrimaryTable(fin); showPrimaryArray(*paL); break; case FITS::FLOAT: paF = new PrimaryTable(fin); showPrimaryArray(*paF); break; case FITS::DOUBLE: paD = new PrimaryTable(fin); showPrimaryArray(*paD); break; default: break; } os << LogOrigin("FITSReader", "listFits", WHERE) << LogIO::NORMAL << "<<<------ Primary Table HDU" << LogIO::POST; break; case FITS::UnknownExtensionHDU: os << LogOrigin("FITSReader", "listFits", WHERE) << LogIO::NORMAL << "Unknown Extension HDU ------>>>" << LogIO::POST; //os << LogIO::DEBUGGING << "Data type is " << fin.datatype() << LogIO::POST; h = new ExtensionHeaderDataUnit(fin); h->skip(); delete h; os << LogOrigin("FITSReader", "listFits", WHERE) << LogIO::NORMAL << "<<<------ Unknown Extension HDU" << LogIO::POST; break; default: os << LogOrigin("FITSReader", "listFits", WHERE) << LogIO::NORMAL << "Unknown Extension HDU ------>>>" << LogIO::POST; os << LogIO::NORMAL << "<<<------ Unknown Extension HDU" << LogIO::POST; break; } }else if (fin.rectype() == FITS::BadBeginningRecord || fin.rectype() == FITS::UnrecognizableRecord) { os << LogOrigin("FITSReader", "listFits", WHERE) << LogIO::WARN << "Bad Record encountered" << LogIO::POST; }else if (fin.rectype() == FITS::SpecialRecord) { os << LogOrigin("FITSReader", "listFits", WHERE) << LogIO::WARN << "Special Record encountered" << LogIO::POST; } if (fin.err()) ++nerrs; } if (nerrs == NMAXERRS) os << LogOrigin("FITSReader", "listFits", WHERE) << LogIO::SEVERE << "Too many errors. Processing terminated." << LogIO::POST; else os << LogOrigin("FITSReader", "listFits", WHERE) << LogIO::NORMAL << "finish processing of Header-Data Units." << LogIO::POST; return ; } } casacore-3.7.1/fits/FITS/FITSReader.h000066400000000000000000000034311476623553700171000ustar00rootroot00000000000000//# FITSReader.h: Parse a FITS disk file //# Copyright (C) 1993,1994,1995,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef FITS_Reader_H #define FITS_Reader_H #include # include # include # include # include # include # include # include namespace casacore { class FITSReader { public: // Read and display values from a FITS file by using // the FitsInput::read(FITS::HDUType t, char *addr, int nb) method. void listFits(const char* fitsfile); }; } #endif casacore-3.7.1/fits/FITS/FITSSpectralUtil.cc000066400000000000000000000564601476623553700204610ustar00rootroot00000000000000//# FitsKeywordUtil: this defines FitsKeywordUtil //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Bool FITSSpectralUtil::fromFITSHeader(Int &spectralAxis, Double &referenceChannel, Double &referenceFrequency, Double &deltaFrequency, Vector &frequencies, MFrequency::Types &refFrame, MDoppler::Types &velocityPreference, Double &restFrequency, LogIO &logger, const RecordInterface &header, char prefix, Bool oneRelative) { Bool retval = True; // Start out invalid spectralAxis = -1; frequencies.resize(0); referenceChannel = 0; referenceFrequency = -1.0; deltaFrequency = 0.0; restFrequency = -1.0; const Double offset(oneRelative == True ? 1.0 : 0.0); logger << LogOrigin("FITSUtil", "fromFITSHeader", WHERE); String n_ctype = String(prefix) + "type"; String n_crval = String(prefix) + "rval"; String n_crpix = String(prefix) + "rpix"; String n_cdelt = String(prefix) + "delt"; String n_cunit = String(prefix) + "unit"; Int ndim; // Verify that the required headers exist and are the right type if (! (header.isDefined(n_ctype) && header.dataType(n_ctype) == TpArrayString && header.shape(n_ctype).nelements() == 1 && header.isDefined(n_crval) && header.dataType(n_crval) == TpArrayDouble && header.shape(n_crval).nelements() == 1 && header.isDefined(n_crpix) && header.dataType(n_crpix) == TpArrayDouble && header.shape(n_crpix).nelements() == 1 && header.isDefined(n_cdelt) && header.dataType(n_cdelt) == TpArrayDouble && header.shape(n_cdelt).nelements() == 1 && header.shape(n_cdelt)(0)) ) { logger << LogIO::SEVERE << "One of " << n_ctype << "," << n_crval << "," << n_crpix << "or " << n_cdelt << " does not exist or is the wrong type." << LogIO::POST; return False; } Bool has_altrval=header.isDefined(String("altrval")) && (header.dataType("altrval") == TpDouble || header.dataType("altrval") == TpFloat); Bool has_altrpix=header.isDefined(String("altrpix")) && (header.dataType("altrpix") == TpDouble ||header.dataType("altrpix") == TpFloat) ; Vector ctype, cunit; Vector crval, crpix, cdelt; header.get(n_ctype, ctype); header.get(n_crval, crval); header.get(n_crpix, crpix); header.get(n_cdelt, cdelt); // if possible get also the unit if (header.isDefined(n_cunit)) header.get(n_cunit, cunit); // naxis might not be consistent with the length of the CTYPEs // we need both. use naxis preferentially Vector naxis; if (header.isDefined("naxis")) { header.get("naxis", naxis); ndim = naxis.nelements(); } else { ndim = ctype.nelements(); } // Find the spectral axis, if any. for (Int i=0; i 256) { velocityPreference = MDoppler::RADIO; } if (header.isDefined("restfreq")) { if (header.dataType("restfreq") != TpDouble && header.dataType("restfreq") != TpFloat) { logger << LogIO::SEVERE << "Illegal type for RESTFREQ" << ", assuming 0.0 - velocity conversions will be impossible" << LogIO::POST; } else { header.get("restfreq", restFrequency); } } else if (header.isDefined("restfrq")) { if (header.dataType("restfrq") != TpDouble && header.dataType("restfrq") != TpFloat) { logger << LogIO::SEVERE << "Illegal type for RESTFRQ" << ", assuming 0.0 - velocity conversions will be impossible" << LogIO::POST; } else { header.get("restfrq", restFrequency); } } else if (header.isDefined("restwav")) { if (header.dataType("restwav") != TpDouble && header.dataType("restwav") != TpFloat) { logger << LogIO::SEVERE << "Illegal type for RESTWAV" << ", assuming infinity - velocity conversions will be impossible" << LogIO::POST; } else { Double restWavelength; header.get("restwav", restWavelength); if(restWavelength>0.){ restFrequency = C::c/restWavelength; } } } // convert the velocity frame tag in ctype to a reference frame String spectralAxisQualifier; if (ctype(spectralAxis).length() <= 5) { spectralAxisQualifier = ""; } else { spectralAxisQualifier = ctype(spectralAxis).after(4); }; if (header.isDefined("specsys")) { String specsys; header.get("specsys", specsys); if(!FITSSpectralUtil::frameFromSpecsys(refFrame, specsys)){ logger << LogIO::WARN << "Illegal value for SPECSYS (" << specsys << "), will assume topocentric" << LogIO::POST; } } else if (!FITSSpectralUtil::frameFromTag(refFrame, spectralAxisQualifier, velref)) { if (spectralAxisQualifier == "") { if ((velref%256) >= 0) { // no tag and velref is unrecognized logger << LogIO::SEVERE << "Illegal value for VELREF(" << velref << ") assuming topocentric" << LogIO::POST; } } else { // unrecognized tag logger << LogIO::SEVERE << "Unknown spectral reference frame " << spectralAxisQualifier << ". Assuming topocentric." << LogIO::POST; } } referenceChannel = crpix(spectralAxis) - offset; // ALTRVAL and ALTRPIX are being used in "FREQ" axis mode // Get NAXIS if we have it // This function returns a vector of frequencies as well as the reference // value/pixel, increment etc. However, this vector is linear in // frequency, so offers no more information than the reference value/increment // For random group, NAXIS1=0 and then CTYPE1,CRVAL1,CDELT1,CROTA1 are // omitted. spectralAxis is determined from ctype string array, so in order // to get corresponding naxis, it needs to be shift by 1. Int nChan = 1; if (naxis.nelements()>0) { Int naxisoffset=0; if (naxis(0)==0) naxisoffset=1; nChan = naxis(spectralAxis+naxisoffset); AlwaysAssert(nChan >= 1, AipsError); } const Double delt = cdelt(spectralAxis); const Double rval = crval(spectralAxis); const Double rpix = crpix(spectralAxis) - offset; // determine the unit in the spectral axis, use "m" as default String unit("m"); if ((Int)cunit.size()>spectralAxis) unit = cunit(spectralAxis); if (ctype(spectralAxis).contains("FREQ")) { referenceFrequency = rval; //HAS ALTRVAL if(has_altrval && (restFrequency >= 0.0)){ Double velo; header.get("altrval",velo); MDoppler ledop(Quantity(velo, "m/s"), velocityPreference); referenceFrequency=MFrequency::fromDoppler(ledop, restFrequency).getValue().getValue(); } //HAS ALTRPIX if(has_altrpix){ header.get("altrpix", referenceChannel); } // include one-based offset // NB: UVFITS refChan is generally one-based referenceChannel-=offset; deltaFrequency = delt; frequencies.resize(nChan); for (Int i=0; i0. && rval+delt!=0.){ referenceFrequency = C::c/(rval*to_m); deltaFrequency = C::c/((rval+delt)*to_m) - referenceFrequency; } else{ logger << LogIO::SEVERE << "Zero or negative wavelength as CRVAL." << LogIO::POST; return False; } frequencies.resize(nChan); for (Int i=0; i0.){ frequencies(i) = C::c/(wl*to_m); } else{ logger << LogIO::SEVERE << "Zero or negative wavelength at pixel " << i << LogIO::POST; return False; } } } else if (ctype(spectralAxis).contains("AWAV")) { Quantity wavUnit(1.0, Unit(unit)); wavUnit.convert(Unit("m")); Double to_m = wavUnit.getValue(); referenceChannel = rpix; if(rval>0. && rval+delt!=0.){ referenceFrequency = C::c/(rval*to_m*refractiveIndex(rval*to_m*1E6)); deltaFrequency = C::c/((rval+delt)*to_m*refractiveIndex((rval+delt)*to_m*1E6)) - referenceFrequency; } else{ logger << LogIO::SEVERE << "Zero or negative wavelength as CRVAL." << LogIO::POST; return False; } frequencies.resize(nChan); for (Int i=0; i0.){ frequencies(i) = C::c/(wl*to_m); } else{ logger << LogIO::SEVERE << "Zero or negative wavelength at pixel " << i << LogIO::POST; return False; } } } else { AlwaysAssert(0, AipsError); // NOTREACHED } return retval; } Bool FITSSpectralUtil::toFITSHeader(String &ctype, Double &crval, Double &cdelt, Double &crpix, String &cunit, Bool &haveAlt, Double &altrval, Double &altrpix, Int &velref, Double &restfreq, String &specsys, LogIO &logger, Double refFrequency, Double refChannel, Double freqIncrement, MFrequency::Types referenceFrame, Bool preferVelocity, MDoppler::Types velocityPreference, Bool preferWavelength, Bool airWavelength, Bool useDeprecatedCtypes) { // Dummy defaults ctype = ""; crval = cdelt = crpix = 0.0; haveAlt = False; altrval = altrpix = 0.0; velref = 0; specsys = ""; logger << LogOrigin("FITSUtil", "toFITSHeader", WHERE); if(preferVelocity && preferWavelength){ logger << LogIO::SEVERE << "Cannot produce FITS header for velocity AND wavelength. You have to choose one." << LogIO::POST; return False; } // Calculate the velocity related things first String ctypetag = ""; if (restfreq > 0.0) { haveAlt = True; // the following call to tagFromFrame is deprecated, better use SPECSYS if (!FITSSpectralUtil::tagFromFrame(ctypetag, velref, referenceFrame)) { logger << LogIO::NORMAL << "Cannot turn spectral type# " << Int(referenceFrame) << " into a AIPS-standard FITS spectral frame." << LogIO::POST; } switch (velocityPreference) { case MDoppler::OPTICAL: break; // NOTHING case MDoppler::RADIO: velref += 256; break; default: velref += 256; logger << LogIO::WARN << "Can only handle OPTICAL and RADIO velocities. Using RADIO" << LogIO::POST; break; } if (!FITSSpectralUtil::specsysFromFrame(specsys, referenceFrame)) { if(!specsys.empty()){ // i.e. if specsys is not undefined logger << LogIO::WARN << "Cannot turn spectral type# " << Int(referenceFrame) << " into a FITS SPECSYS keyword. Will use \"" << specsys << "\" instead." << LogIO::POST; } else{ // make sure also velref is not written if specsys is undefined haveAlt = False; } } } // Calculate velocity quantities Double refVelocity(0.0), velocityIncrement(0.0); if (haveAlt) { if (velref < 256) { // OPTICAL refVelocity = -C::c * (1.0 - restfreq / refFrequency); velocityIncrement = -C::c * (1.0 - restfreq / (refFrequency + freqIncrement)) - refVelocity; } else { // RADIO refVelocity = -C::c * (refFrequency/restfreq - 1.0); velocityIncrement = -C::c * ((refFrequency + freqIncrement)/restfreq - 1.0) - refVelocity; } } if(preferWavelength){ // axis is supposed to be linear in wavelength if(refFrequency<=0. || refFrequency+freqIncrement==0.){ logger << LogIO::SEVERE << "Zero or negative reference frequency." << LogIO::POST; return False; } if (airWavelength){ // use air wavelength ctype = String("AWAV"); crval = roundDouble(C::c/refFrequency/refractiveIndex(C::c/refFrequency*1E6), 12); cdelt = roundDouble(C::c/(refFrequency+freqIncrement)/refractiveIndex(C::c/(refFrequency+freqIncrement)*1E6) - crval, 12); crpix = refChannel; } else{ // use vacuum wavelength ctype = String("WAVE"); crval = roundDouble(C::c/refFrequency, 12); cdelt = roundDouble(C::c/(refFrequency+freqIncrement) - crval, 12); crpix = refChannel; } // set the wavelength unit: // crval >= 0.1m: "m" // 0.1m > crval >= 0.1e-03m: "mm" <-- ALMA wavelength range // 0.1e-03m > crval >= 1.0e-06m: "um" // 1.0e-06m > crval: "nm" if (crval >=0.1){ cunit = "m"; } else if ((0.1 > crval) && (crval >=0.1e-03)){ crval *= 1.0e+03; cdelt *= 1.0e+03; cunit = "mm"; } else if ((0.1e-03 > crval) && (crval >=1.0e-06)){ crval *= 1.0e+06; cdelt *= 1.0e+06; cunit = "um"; } else if (1.0e-06 > crval){ crval *= 1.0e+09; cdelt *= 1.0e+09; cunit = "nm"; } } else if (!haveAlt || !preferVelocity) { // FREQ is primary ctype = String("FREQ"); crval = refFrequency; cdelt = freqIncrement; crpix = refChannel; if (haveAlt) { altrval = refVelocity; altrpix = crpix; } } else { // Velocity of some type is primary if (velref < 256) { // Optical if(useDeprecatedCtypes){ ctype = String("FELO")+ctypetag; // deprecated, non-standard FITS } else{ ctype = String("VOPT"); } } else { // Radio if(useDeprecatedCtypes){ ctype = String("VELO")+ctypetag; // deprecated, non-standard FITS } else{ ctype = String("VRAD"); } } crval = refVelocity; cdelt = velocityIncrement; crpix = refChannel; // Always have ALT* because we fundamentally work in terms of // frequencies. altrval = refFrequency; altrpix = crpix; } return True; } Bool FITSSpectralUtil::frameFromTag(MFrequency::Types &refFrame, const String& tag, Int velref) { String theTag; for (uInt i=0; i= 0) { switch(velref % 256) { case 1: refFrame = MFrequency::LSRK; break; case 2: refFrame = MFrequency::BARY; break; case 3: refFrame = MFrequency::TOPO; break; case 4: refFrame = MFrequency::LSRD; break; case 5: refFrame = MFrequency::GEO; break; case 6: refFrame = MFrequency::REST; break; case 7: refFrame = MFrequency::GALACTO; break; default: result = False; // empty tag, illegal velref } } else { result = False; // empty tag, no velref } } else { result = False; } return result; } Bool FITSSpectralUtil::tagFromFrame(String &tag, Int &velref, MFrequency::Types refFrame) { Bool result = True; switch (refFrame) { case MFrequency::LSRK: tag = "-LSR"; velref = 1; break; case MFrequency::BARY: tag = "-HEL"; velref = 2; break; case MFrequency::TOPO: tag = "-OBS"; velref = 3; break; case MFrequency::LSRD: tag = "-LSD"; velref = 4; break; case MFrequency::GEO: tag = "-GEO"; velref = 5; break; case MFrequency::REST: tag = "-SOU"; velref = 6; break; case MFrequency::GALACTO: tag = "-GAL"; velref = 7; break; default: tag = "-OBS"; velref = 3; result = False; } return result; } Bool FITSSpectralUtil::specsysFromFrame(String &specsys, MFrequency::Types refFrame) { Bool result = True; switch (refFrame) { case MFrequency::LSRK: specsys = "LSRK"; break; case MFrequency::BARY: specsys = "BARYCENT"; break; case MFrequency::LSRD: specsys = "LSRD"; break; case MFrequency::GEO: specsys = "GEOCENTR"; break; case MFrequency::REST: specsys = "SOURCE"; break; case MFrequency::GALACTO: specsys = "GALACTOC"; break; case MFrequency::LGROUP: specsys = "LOCALGRP"; break; case MFrequency::CMB: specsys = "CMBDIPOL"; break; case MFrequency::TOPO: specsys = "TOPOCENT"; break; case MFrequency::Undefined: default: specsys = ""; result = False; } return result; } Bool FITSSpectralUtil::frameFromSpecsys(MFrequency::Types& refFrame, String& specsys) { Bool result = True; if(specsys == "LSRK"){ refFrame = MFrequency::LSRK; } else if(specsys == "BARYCENT"){ refFrame = MFrequency::BARY; } else if(specsys == "LSRD"){ refFrame = MFrequency::LSRD; } else if(specsys == "GEOCENTR"){ refFrame = MFrequency::GEO; } else if(specsys == "SOURCE"){ refFrame = MFrequency::REST; } else if(specsys == "GALACTOC"){ refFrame = MFrequency::GALACTO; } else if(specsys == "LOCALGRP"){ refFrame = MFrequency::LGROUP; } else if(specsys == "CMBDIPOL"){ refFrame = MFrequency::CMB; } else if(specsys == "TOPOCENT"){ refFrame = MFrequency::TOPO; } else{ refFrame = MFrequency::Undefined; result = False; } return result; } Double FITSSpectralUtil::refractiveIndex(const Double& lambda_um){ Double lambda2 = lambda_um * lambda_um; // based on Greisen et al., 2006, A&A, 464, 746 Double nOfLambda = 1.; if(lambda2 > 0.){ nOfLambda = 1. + 1E-6 * (287.6155 + 1.62887/lambda2 + 0.01360/lambda2/lambda2); } //cout << "ref index " << nOfLambda << endl; return nOfLambda; } } //# NAMESPACE CASACORE - END casacore-3.7.1/fits/FITS/FITSSpectralUtil.h000066400000000000000000000222501476623553700203110ustar00rootroot00000000000000//# FITSSpectralUtil.h: Static functions to help with FITS spectral axes. //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef FITS_FITSSPECTRALUTIL_H #define FITS_FITSSPECTRALUTIL_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class RecordInterface; class String; class LogIO; // // A class with static functions to help deal with FITS spectral axes. // // // // // //
      • General knowlege of FITS, FITS keywords, and FITS coordinate // conventions is assumed. //
      • Presumably you are using this in conjuction with the // FITSKeywordUtil class // to get or set the FITS coordinate axis information. // // // This is a collection of static utility functions for use with // FITS spectral axes. // // // This class provides functions to extract information from a FITS // header about the spectral axis, to setup a FITS header with // appropriate information for the spectral axis, and to translate // to and from the MFrequency reference frame codes and their FITS // equivalents. // It is never necessary to construct a FITSSpectralUtil, just use the // static functions to help handle FITS Spectral axes. // // // // // Record rec; // ... extract the FITS keyword values into rec using FITSKeywordUtil // Int whichAxis; // Double refPix, refFreq, freqInc, restFreq; // Vector freqs; // MFrequency::Types refFrame; // MDoppler::Types veldef; // LogIO logger; // FITSSpectralUtil::fromFITSHeader(whichAxis, refPix, refFreq, // freqInc, freqs, refFrame, veldef, logger, rec); // // // // // This is designed to be used after the keywords have been extracted from // the FITS file using the FITSKeywordUtil // class. Extracting spectral axis and related information requires detailed // knowledge of FITS conventions that this class strives to encapsulize. // // // //
      • General usage of units for frequency and velocity in "fromFITSHeader" // (currently only implemented for wavelength) // class FITSSpectralUtil { public: // Get information about the spectral axis from a record containing the // FITS axis information. Usually this will be from a FITS header using, // for example, the FITSKeywordUtil::getKeywords method. // referenceFrequency and deltaFrequency give the best // possible linear frequency scale. prefix is the first character in the // set of keywords describing the axes (e.g. crpix, crval, ctype - the prefix is // 'c'). If oneRelative is False, the returned referenceChannel is decrimented // from that found in header by 1. The naxis keywords are used to determine // the output length of the freqs vector. // This method returns False if: //
          //
        • no spectral axis is found. The freqs vector will have a length of zero. //
        • The expected set of axis description keywords is not found. //
        • The spectral axis is FELO but there is no rest frequency (making it // impossible to convert to frequency and construct a freqs vector). //
        • The combination FELO and RADIO is used (which does not make sense). //
        • The combination VELO and OPTICAL is used (not yet implemented). //
        static Bool fromFITSHeader(Int &spectralAxis, Double &referenceChannel, Double &referenceFrequency, Double &deltaFrequency, Vector &frequencies, MFrequency::Types &refFrame, MDoppler::Types &velocityPreference, Double &restFrequency, LogIO &logger, const RecordInterface &header, char prefix = 'c', Bool oneRelative=True); // Nearly the inverse of fromFITSHeader. This returns parameters which could // be used in filling in a header record with appropriate values for // the spectral axis given the arguments after the logger. // The alternate axis description values are set when sufficient information is available. // If they have been set, haveAlt will be set to True. //
          //
        • Note that the output arguments after "haveAlt" // should not be written to the FITS header unless haveAlt is true. //
        • Note that restfreq is both an input and an output. If there is no // rest frequency, set it to be <=0 on input. //
        // If preferVelocity is True, the // axis description parameters will be set to those appropriate for // a velocity axis given the referenceFrame, and velocityPreference // if possible. // If preferWavelength is True, the // axis description parameters will be set to those appropriate for // a wavelength axis given the referenceFrame if possible. // The two preferences cannot be True at the same time. // If airWavelength is True, the // axis description parameters will be set to those appropriate for // an air wavelength axis given the referenceFrame if possible. // This parameter has an effect only if preferWavelength is True. // This method always returns True. static Bool toFITSHeader(String &ctype, Double &crval, Double &cdelt, Double &crpix, String &cunit, Bool &haveAlt, Double &altrval, Double &altrpix, Int &velref, Double &restfreq, String &specsys, LogIO &logger, Double refFrequency, Double refChannel, Double freqIncrement, MFrequency::Types referenceFrame, Bool preferVelocity = True, MDoppler::Types velocityPreference = MDoppler::OPTICAL, Bool preferWavelength = False, Bool airWavelength = False, Bool useDeprecatedCtypes = False); // Convert a reference frame tag (typically found as the characters // after the first 4 characters in a ctype string for the // frequency-like axis) to a MFrequency::Types value. // A velref value (used in AIPS images to alternatively record // the velocity reference frame) may also optionally be supplied. // If tag is empty, velref will be used if it is >= 0. // This function returns False if: //
          //
        • The tag is not empty but is unrecognized. //
        • The tag is empty and velref is unrecognized. //
        • The tag is empty and velref is < 0 (no velref was supplied). //
        // The default value (set when the return value is False) is TOPO. static Bool frameFromTag(MFrequency::Types &referenceFrame, const String &tag, Int velref=-1); // Construct a reference frame tag from the given referenceFrame // An appropriate velref value is also constructed (this may need // to be adjusted by +256 if the velocity definition is radio before // being used in a FITS file). This returns False if the // reference frame is not recognized. The value of tag defaults // to "-OBS". static Bool tagFromFrame(String &tag, Int &velref, MFrequency::Types referenceFrame); // Construct a SPECSYS keyword value from the given referenceFrame // This returns False if the reference frame is not recognized. // The value of tag defaults to "TOPOCENT". static Bool specsysFromFrame(String &specsys, MFrequency::Types referenceFrame); static Bool frameFromSpecsys(MFrequency::Types& refFrame, String& specsys); // The refractive index of air (argument can be vacuum wavelength or airwavelength) // according to Greisen et al., 2006, A&A, 464, 746. // If vacuum wavelength is used there is an error of the order of 1E-9. // Argument must be in micrometers! static Double refractiveIndex(const Double& lambda_um); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/fits/FITS/FITSTable.cc000066400000000000000000001616351476623553700170760ustar00rootroot00000000000000//# FITSTable.h: Simplified interface to FITS tables with Casacore Look and Feel. //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Just returns the scalar type. static DataType fitsDataType(FITS::ValueType fitsType) { switch(fitsType) { case FITS::BIT: case FITS::LOGICAL: return TpBool; case FITS::CHAR: return TpString; case FITS::BYTE: return TpUChar; case FITS::SHORT: return TpShort; case FITS::LONG: return TpInt; case FITS::FLOAT: return TpFloat; case FITS::DOUBLE: return TpDouble; case FITS::COMPLEX: return TpComplex; case FITS::ICOMPLEX: // ICOMPLEX promoted to DComplex so no precision is lost case FITS::DCOMPLEX: return TpDComplex; case FITS::STRING: // return TpString; // FITS just has character arrays, // not strings default: return TpOther; // VADESC will trigger this } } // takes a pointer to an array of chars and returns // its length A null character terminates the string // and trailing spaces are not considered as part of the // string uInt charLength(const char *cptr, uInt maxLength) { uInt length = 0; // watch for any null termination while (length < maxLength && cptr[length] != '\0') { length++; } // don't count any trailing spaces while (length > 0 && cptr[length-1] == ' ') { length--; } return length; } FITSTabular::~FITSTabular() { // Nothing } TableRecord FITSTabular::keywordsFromHDU(HeaderDataUnit &hdu, Bool allKeywords) { // Setup the keywords. // First, delete the old ones TableRecord keywords; // Now add in all the keywords from this HDU hdu.firstkw(); const FitsKeyword *key = hdu.currkw(); Bool noValue = False; String name; FITS::ReservedName kwname; while (key) { name = key->name(); kwname = key->kw().name(); // skip certain keywords if allKeywords is not True if (!allKeywords && key->isreserved() && (kwname == FITS::BITPIX || kwname == FITS::GCOUNT || kwname == FITS::NAXIS || kwname == FITS::PCOUNT || kwname == FITS::TBCOL || kwname == FITS::TDIM || kwname == FITS::TDISP || kwname == FITS::TFIELDS || kwname == FITS::THEAP || kwname == FITS::TFORM || kwname == FITS::TNULL || kwname == FITS::TSCAL || kwname == FITS::TTYPE || kwname == FITS::TUNIT || kwname == FITS::TZERO || kwname == FITS::XTENSION)) { key = hdu.nextkw(); continue; } if (key->isindexed()) { ostringstream num; num << key->index(); name += String(num); } switch (key->type()) { case FITS::LOGICAL : keywords.define(name,key->asBool()); break; case FITS::STRING : { const char * cptr = key->asString(); uInt length = charLength(cptr, key->valStrlen()); keywords.define(name,String(cptr,length)); } break; case FITS::FLOAT : keywords.define(name,key->asFloat()); break; case FITS::DOUBLE : keywords.define(name,key->asDouble()); break; case FITS::LONG : keywords.define(name,key->asInt()); break; case FITS::COMPLEX : keywords.define(name,key->asComplex()); break; case FITS::DCOMPLEX : keywords.define(name,key->asDComplex()); break; case FITS::NOVALUE : noValue = True; break; default: throw(AipsError("FITSTablular::keywordsFromHDU() - unknown keyword type" " (cannot happen!)")); } // Don't comment keywords without a value (e.g. END). if (!noValue) { keywords.setComment(name, key->comm()); } key = hdu.nextkw(); } return keywords; } RecordDesc FITSTabular::descriptionFromHDU( BinaryTableExtension &hdu) { RecordDesc description; uInt ncol = hdu.ncols(); // this is needed here Record subStringInfo = subStringShapeFromHDU(hdu); IPosition shape; for (uInt i=0; i < ncol; i++) { DataType type = fitsDataType(hdu.field(i).fieldtype()); shape.resize(hdu.field(i).dims()); for (uInt j=0; j 0) { // fixed shape description.addField(colname, type, IPosition(1,nelem)); } else { // variable shape description.addField(colname, asArray(type)); } } else { // Scalar description.addField(colname, type); } } else { // Array if (shape.nelements() == 0) { // variable shapped array // leave shape off, make sure this is an array DataType // this makes this a variable sized array description.addField(colname, asArray(type)); } else { description.addField(colname, type, shape); } } } return description; } Record FITSTabular::subStringShapeFromHDU(BinaryTableExtension &hdu) { Record subStringShapes; uInt ncol = hdu.ncols(); Regex trailing(" *$"); // trailing blanks for (uInt i=0; i < ncol; i++) { String colname(hdu.ttype(i)); colname.rtrim(' '); String tform(hdu.tform(i)); tform.rtrim(' '); // Look for the sub-string convention, described in appendix C of // Cotton, Tody, and Pence. Its in the TFIELD for this column. // This probably could (should?) happen in FitsField. // But I understand this better so do it here. if (tform.matches(Regex("^.*A:SSTR[0-9]+(/[0-9]+)?$"))) { Record info; String sstr = tform.after(tform.find("SSTR")+3); String::size_type slinx = sstr.find('/'); if (slinx != String::npos) { // two integers separate by a the slash Int maxChars = atol(sstr.before(slinx).chars()); Int delim = atol(sstr.after(slinx).chars()); info.define("NCHAR", maxChars); info.define("NELEM", -1); info.define("DELIM", String(Char(delim))); } else { // it must be just an integer at this point Int nchars = atol(sstr.chars()); // fixed shape String array // determine the shape given nchars Int nelem = hdu.field(i).nelements() / nchars; if (nelem < 1) nelem = 1; info.define("NCHAR", nchars); info.define("NELEM", nelem); info.define("DELIM", String(Char('\0'))); } subStringShapes.defineRecord(colname, info); } } return subStringShapes; } Record FITSTabular::unitsFromHDU(BinaryTableExtension &hdu) { Record units; uInt ncol = hdu.ncols(); for (uInt i=0; i < ncol; i++) { String colname(hdu.ttype(i)); colname.rtrim(' '); String unitval(hdu.tunit(i)); unitval.rtrim(' '); if (!unitval.empty()) units.define(colname, unitval); } return units; } Record FITSTabular::displayFormatsFromHDU(BinaryTableExtension &hdu) { Record disps; uInt ncol = hdu.ncols(); for (uInt i=0; i < ncol; i++) { String colname(hdu.ttype(i)); colname.rtrim(' '); String dispval(hdu.tdisp(i)); dispval.rtrim(' '); if (!dispval.empty()) disps.define(colname, dispval); } return disps; } Record FITSTabular::nullsFromHDU(BinaryTableExtension &hdu) { Record nulls; uInt ncol = hdu.ncols(); // strangely, the first arg to hdu.kw is a reference // to a FITS::ReservedName (not even a const reference) // So, since I can't put FITS::TNULL there, I need to // make it a variable, first. Argh. FITS::ReservedName tnull = FITS::TNULL; for (uInt i=0; i < ncol; i++) { // only if column is BYTE, SHORT, LONG and // tscal == 1.0 and tzero = 0.0, i.e. no promotion will be done // also, make sure it has a TNULL keyword in the original // FITS since hdu.tnull(i) returns the minimum Int even // when no TNULL keyword is present. if (hdu.kw(tnull, i) && (hdu.field(i).fieldtype() == FITS::BYTE || hdu.field(i).fieldtype() == FITS::SHORT || hdu.field(i).fieldtype() == FITS::LONG) && hdu.tscal(i) == 1.0 && hdu.tzero(i) == 0.0) { String colname(hdu.ttype(i)); colname.rtrim(' '); nulls.define(colname, (hdu.kw(tnull,i))->asInt()); } } return nulls; } TableDesc FITSTabular::tableDesc(const FITSTabular &fitstabular) { // construct the output table given the description in the first // row of infits TableDesc td; td.rwKeywordSet() = fitstabular.keywords(); RecordDesc desc = fitstabular.description(); Record units = fitstabular.units(); Record disps = fitstabular.displayFormats(); Record nulls = fitstabular.nulls(); for (uInt i=0;i scd(desc.name(i), desc.comment(i)); scd.setDefault(ValType::undefBool()); td.addColumn(scd); } // td.addColumn(ScalarColumnDesc(desc.name(i), desc.comment(i))); break; case TpChar: { ScalarColumnDesc scd(desc.name(i), desc.comment(i)); scd.setDefault(ValType::undefChar()); td.addColumn(scd); } // td.addColumn(ScalarColumnDesc(desc.name(i), desc.comment(i))); break; case TpUChar: { ScalarColumnDesc scd(desc.name(i), desc.comment(i)); scd.setDefault(ValType::undefUChar()); td.addColumn(scd); } // td.addColumn(ScalarColumnDesc(desc.name(i), desc.comment(i))); break; case TpShort: { ScalarColumnDesc scd(desc.name(i), desc.comment(i)); scd.setDefault(ValType::undefShort()); td.addColumn(scd); } // td.addColumn(ScalarColumnDesc(desc.name(i), desc.comment(i))); break; case TpInt: { ScalarColumnDesc scd(desc.name(i), desc.comment(i)); scd.setDefault(ValType::undefInt()); td.addColumn(scd); } // td.addColumn(ScalarColumnDesc(desc.name(i), desc.comment(i))); break; case TpFloat: { ScalarColumnDesc scd(desc.name(i), desc.comment(i)); scd.setDefault(ValType::undefFloat()); td.addColumn(scd); } // td.addColumn(ScalarColumnDesc(desc.name(i), desc.comment(i))); break; case TpDouble: { ScalarColumnDesc scd(desc.name(i), desc.comment(i)); scd.setDefault(ValType::undefDouble()); td.addColumn(scd); } // td.addColumn(ScalarColumnDesc(desc.name(i), desc.comment(i))); break; case TpComplex: { ScalarColumnDesc scd(desc.name(i), desc.comment(i)); scd.setDefault(ValType::undefComplex()); td.addColumn(scd); } // td.addColumn(ScalarColumnDesc(desc.name(i), desc.comment(i))); break; case TpDComplex: { ScalarColumnDesc scd(desc.name(i), desc.comment(i)); scd.setDefault(ValType::undefDComplex()); td.addColumn(scd); } // td.addColumn(ScalarColumnDesc(desc.name(i), desc.comment(i))); break; case TpString: { ScalarColumnDesc scd(desc.name(i), desc.comment(i)); scd.setDefault(ValType::undefString()); td.addColumn(scd); } // td.addColumn(ScalarColumnDesc(desc.name(i), desc.comment(i))); break; default: cerr << "Unrecognized scalar column data type in column " << desc.name(i) << " : " << desc.type(i) << endl; break; } } else { Bool fixedShape = True; IPosition shape = desc.shape(i); Int options = 0; if (shape.nelements() == 1 && shape(0) == -1) { fixedShape = False; } else { options = ColumnDesc::FixedShape; } // this division between direct and indirect is arbitrary // I wonder what a good division is? if (fixedShape && shape.product() <= 20) { options = ColumnDesc::Direct; } switch (desc.type(i)) { case TpBool: case TpArrayBool: if (fixedShape) { td.addColumn(ArrayColumnDesc(desc.name(i), desc.comment(i), shape, options)); } else { td.addColumn(ArrayColumnDesc(desc.name(i), desc.comment(i))); } break; case TpChar: case TpArrayChar: if (fixedShape) { td.addColumn(ArrayColumnDesc(desc.name(i), desc.comment(i), shape, options)); } else { td.addColumn(ArrayColumnDesc(desc.name(i), desc.comment(i))); } break; case TpUChar: case TpArrayUChar: if (fixedShape) { td.addColumn(ArrayColumnDesc(desc.name(i), desc.comment(i), shape, options)); } else { td.addColumn(ArrayColumnDesc(desc.name(i), desc.comment(i))); } break; case TpShort: case TpArrayShort: if (fixedShape) { td.addColumn(ArrayColumnDesc(desc.name(i), desc.comment(i), shape, options)); } else { td.addColumn(ArrayColumnDesc(desc.name(i), desc.comment(i))); } break; case TpInt: case TpArrayInt: if (fixedShape) { td.addColumn(ArrayColumnDesc(desc.name(i), desc.comment(i), shape, options)); } else { td.addColumn(ArrayColumnDesc(desc.name(i), desc.comment(i))); } break; case TpFloat: case TpArrayFloat: if (fixedShape) { td.addColumn(ArrayColumnDesc(desc.name(i), desc.comment(i), shape, options)); } else { td.addColumn(ArrayColumnDesc(desc.name(i), desc.comment(i))); } break; case TpDouble: case TpArrayDouble: if (fixedShape) { td.addColumn(ArrayColumnDesc(desc.name(i), desc.comment(i), shape, options)); } else { td.addColumn(ArrayColumnDesc(desc.name(i), desc.comment(i))); } break; case TpComplex: case TpArrayComplex: if (fixedShape) { td.addColumn(ArrayColumnDesc(desc.name(i), desc.comment(i), shape, options)); } else { td.addColumn(ArrayColumnDesc(desc.name(i), desc.comment(i))); } break; case TpDComplex: case TpArrayDComplex: if (fixedShape) { td.addColumn(ArrayColumnDesc(desc.name(i), desc.comment(i), shape, options)); } else { td.addColumn(ArrayColumnDesc(desc.name(i), desc.comment(i))); } break; case TpString: case TpArrayString: if (fixedShape) { td.addColumn(ArrayColumnDesc(desc.name(i), desc.comment(i), shape, options)); } else { td.addColumn(ArrayColumnDesc(desc.name(i), desc.comment(i))); } break; default: cerr << "Unrecognized array column data type in column " << desc.name(i) << " : " << desc.type(i) << endl; break; } } // add in any units, displayFormats, and nulls, if available if (units.isDefined(desc.name(i))) { td.rwColumnDesc(desc.name(i)).rwKeywordSet().define("UNIT", units.asString(desc.name(i))); } if (disps.isDefined(desc.name(i))) { td.rwColumnDesc(desc.name(i)).rwKeywordSet().define("DISP", disps.asString(desc.name(i))); } if (nulls.isDefined(desc.name(i))) { td.rwColumnDesc(desc.name(i)).rwKeywordSet().define("NULL", nulls.asInt(desc.name(i))); } } return td; } FITSTable::FITSTable(uInt whichHDU, Bool allKeywords) : hdu_nr_p(whichHDU), row_nr_p(-1), raw_table_p(0), io_p(0), row_p(RecordInterface::Variable), allKeys_p(allKeywords), nfields_p(0), row_fields_p(0), field_types_p(0), vatypes_p(0), vaptr_p(0), va_p(0), theheap_p(0) { isValid_p = False; } FITSTable::FITSTable(const String &fileName, uInt whichHDU, Bool allKeywords) : hdu_nr_p(whichHDU), row_nr_p(-1), raw_table_p(0), io_p(0), row_p(RecordInterface::Variable), allKeys_p(allKeywords), nfields_p(0), row_fields_p(0), field_types_p(0), vatypes_p(0), vaptr_p(0), va_p(0), theheap_p(0) { isValid_p = reopen(fileName); } Bool FITSTable::reopen(const String &fileName) { clear_self(); // use the Path class so that ~ is parsed if present in the file name Path filePath(fileName); io_p = new FitsInput(filePath.expandedName().chars(), FITS::Disk); AlwaysAssert(io_p, AipsError); if (io_p->err() || io_p->eof()) { return False; } // construct the primary HDU keywords record if (io_p->hdutype() != FITS::PrimaryArrayHDU) return False; switch (io_p->datatype()) { case FITS::BYTE: { BytePrimaryArray pa(*io_p); primaryKeys_p = FITSTabular::keywordsFromHDU(pa, allKeys_p); if (pa.nelements()) reopenAtFirstHDU(filePath.expandedName().chars()); } break; case FITS::SHORT: { ShortPrimaryArray pa(*io_p); primaryKeys_p = FITSTabular::keywordsFromHDU(pa, allKeys_p); if (pa.nelements()) reopenAtFirstHDU(filePath.expandedName().chars()); } break; case FITS::LONG: { LongPrimaryArray pa(*io_p); primaryKeys_p = FITSTabular::keywordsFromHDU(pa, allKeys_p); if (pa.nelements()) reopenAtFirstHDU(filePath.expandedName().chars()); } break; case FITS::FLOAT: { FloatPrimaryArray pa(*io_p); primaryKeys_p = FITSTabular::keywordsFromHDU(pa, allKeys_p); if (pa.nelements()) reopenAtFirstHDU(filePath.expandedName().chars()); } break; case FITS::DOUBLE: { DoublePrimaryArray pa(*io_p); primaryKeys_p = FITSTabular::keywordsFromHDU(pa, allKeys_p); if (pa.nelements()) reopenAtFirstHDU(filePath.expandedName().chars()); } break; default: return False; } uInt i; for (i=1; i < hdu_nr_p && !io_p->err(); i++) { io_p->skip_hdu(); } if (io_p->err() || io_p->eof()) { return False; } // OK; we have a valid HDU if (io_p->hdutype() == FITS::BinaryTableHDU) { raw_table_p = new BinaryTableExtension(*io_p); } else if (io_p->hdutype() == FITS::AsciiTableHDU) { raw_table_p = new AsciiTableExtension(*io_p); } else { return False; } AlwaysAssert(raw_table_p, AipsError); keywords_p = FITSTabular::keywordsFromHDU(*raw_table_p, allKeys_p); description_p = FITSTabular::descriptionFromHDU(*raw_table_p); units_p = FITSTabular::unitsFromHDU(*raw_table_p); disps_p = FITSTabular::displayFormatsFromHDU(*raw_table_p); nulls_p = FITSTabular::nullsFromHDU(*raw_table_p); subStrShapes_p = FITSTabular::subStringShapeFromHDU(*raw_table_p); // resize some things based on the number of fields in the description nfields_p = description_p.nfields(); row_fields_p.resize(nfields_p); field_types_p.resize(nfields_p); promoted_p.resize(nfields_p); tdims_p.resize(nfields_p); promoted_p = False; tdims_p = -1; Bool anyPromoted = False; Bool anyReshaped = False; // look for fields to promote and TDIMnnn columns, extracting (nnn-1) for (i=0;itscal(i) != 1.0 || raw_table_p->tzero(i) != 0.0) && (type == TpUChar || type == TpArrayUChar || type == TpShort || type == TpArrayShort || type == TpInt || type == TpArrayInt)) { promoted_p[i] = True; anyPromoted = True; } if (description_p.name(i).matches(Regex("^TDIM[0-9]+$")) && description_p.type(i) == TpString) { String tdim = description_p.name(i); tdim = tdim.after(3); Int which = atol(tdim.chars())-1; if (which >= 0 && which < Int(tdims_p.nelements())) { anyReshaped = True; tdims_p[which] = i; } } } // it actually gets redone here so that we can preserve the order // of things in the description if (anyPromoted || anyReshaped) { RecordDesc newDesc; for (i=0;i= 0) { // this is really a variable shaped array if (!isArray(type)) { // but its stored here as a scalar - must // be just one element - promote it to an // an array type type = asArray(type); } // so we don't specify a shape here. newDesc.addField(description_p.name(i), type); } else { if (isArray(type)) { // add a field of the appropriate shape newDesc.addField(description_p.name(i), type, shape); } else { newDesc.addField(description_p.name(i), type); } } } description_p = newDesc; } row_p.restructure(description_p); // read the heap (and the rest of the table, since this is a // sequential access file only) if one is present if (raw_table_p->pcount()) { raw_table_p->read(raw_table_p->nrows()); if (raw_table_p->notnull(raw_table_p->theap())) { uInt heapOffset = raw_table_p->theap() - raw_table_p->rowsize()*raw_table_p->nrows(); // skip to the start of the heap // I don't see any way except to read these bogus bytes Block junk(heapOffset); raw_table_p->ExtensionHeaderDataUnit::read(junk.storage(), heapOffset); } theheap_p = new char [raw_table_p->pcount()]; AlwaysAssert(theheap_p, AipsError); raw_table_p->ExtensionHeaderDataUnit::read(theheap_p, raw_table_p->pcount()); } else { // just read one row, assuming there are any rows to read if (raw_table_p->nrows()) raw_table_p->read(1); } row_nr_p++; // Setup the record fields (one time only) for (i=0; i < nfields_p; i++) { switch( description_p.type(i)) { case TpBool: row_fields_p[i] = new RecordFieldPtr(row_p, i); break; case TpArrayBool: row_fields_p[i] = new RecordFieldPtr >(row_p, i); break; case TpUChar: row_fields_p[i] = new RecordFieldPtr(row_p, i); break; case TpArrayUChar: row_fields_p[i] = new RecordFieldPtr >(row_p, i); break; case TpShort: row_fields_p[i] = new RecordFieldPtr(row_p, i); break; case TpArrayShort: row_fields_p[i] = new RecordFieldPtr >(row_p, i); break; case TpInt: row_fields_p[i] = new RecordFieldPtr(row_p, i); break; case TpArrayInt: row_fields_p[i] = new RecordFieldPtr >(row_p, i); break; case TpFloat: row_fields_p[i] = new RecordFieldPtr(row_p, i); break; case TpArrayFloat: row_fields_p[i] = new RecordFieldPtr >(row_p, i); break; case TpDouble: row_fields_p[i] = new RecordFieldPtr(row_p, i); break; case TpArrayDouble: row_fields_p[i] = new RecordFieldPtr >(row_p, i); break; case TpComplex: row_fields_p[i] = new RecordFieldPtr(row_p, i); break; case TpArrayComplex: row_fields_p[i] = new RecordFieldPtr >(row_p, i); break; case TpDComplex: row_fields_p[i] = new RecordFieldPtr(row_p, i); break; case TpArrayDComplex: row_fields_p[i] = new RecordFieldPtr >(row_p, i); break; case TpString: row_fields_p[i] = new RecordFieldPtr(row_p, i); break; case TpArrayString: row_fields_p[i] = new RecordFieldPtr >(row_p, i); break; default: throw(AipsError("FITSTable::reopen() - unknown field type")); } AlwaysAssert(row_fields_p[i] != 0, AipsError); field_types_p[i] = description_p.type(i); } name_p = fileName; // set up things necessary fo VADESC cols // this is only necessary if a heap exists if (theheap_p) { Int ncols = raw_table_p->ncols(); vatypes_p.resize(raw_table_p->ncols()); vaptr_p.resize(raw_table_p->ncols()); va_p = new VADescFitsField [ncols]; AlwaysAssert(va_p, AipsError); for (i=0;incols());i++) { vaptr_p[i] = 0; vatypes_p[i] = FITS::NOVALUE; if (raw_table_p->field(i).fieldtype() == FITS::VADESC) { int maxsize; FITS::ValueType vtype; FITS::parse_vatform(raw_table_p->tform(i), vtype, maxsize); vatypes_p[i] = vtype; raw_table_p->bind(i, va_p[i]); if (vatypes_p[i] == FITS::NOVALUE) { throw(AipsError("FITSTable::reopen() - invalid VADESC format")); } switch (vatypes_p[i]) { case FITS::LOGICAL: vaptr_p[i] = (void *)(new FitsLogical[maxsize]); AlwaysAssert(vaptr_p[i], AipsError); break; case FITS::BIT: { Int nbytes = maxsize / 8; if (maxsize % 8) nbytes++; maxsize = nbytes; } // fall through to BYTE for actual allocation CASACORE_FALLTHROUGH; case FITS::BYTE: vaptr_p[i] = (void *)(new uChar[maxsize]); AlwaysAssert(vaptr_p[i], AipsError); break; case FITS::SHORT: vaptr_p[i] = (void *)(new Short[maxsize]); AlwaysAssert(vaptr_p[i], AipsError); break; case FITS::LONG: vaptr_p[i] = (void *)(new FitsLong[maxsize]); AlwaysAssert(vaptr_p[i], AipsError); break; case FITS::CHAR: vaptr_p[i] = (void *)(new Char[maxsize]); AlwaysAssert(vaptr_p[i], AipsError); break; case FITS::FLOAT: vaptr_p[i] = (void *)(new Float[maxsize]); AlwaysAssert(vaptr_p[i], AipsError); break; case FITS::DOUBLE: vaptr_p[i] = (void *)(new Double[maxsize]); AlwaysAssert(vaptr_p[i], AipsError); break; case FITS::COMPLEX: vaptr_p[i] = (void *)(new Complex[maxsize]); AlwaysAssert(vaptr_p[i], AipsError); break; case FITS::DCOMPLEX: vaptr_p[i] = (void *)(new DComplex[maxsize]); AlwaysAssert(vaptr_p[i], AipsError); break; default: cerr << "Impossible VADesc type in column " << i << " : " << vatypes_p[i] << endl; break; } } } } if (description_p.nfields() > 0 && raw_table_p->nrows()) { fill_row(); } isValid_p = True; return True; } void FITSTable::next() { // first, read a row or step to the next row row_nr_p++; if (row_nr_p >= raw_table_p->nrows()) { return; // Don't read past the end, this row is already filled } // Use the native FITS classes if (!theheap_p) raw_table_p->read(1); else ++(*raw_table_p); if (isValid()) fill_row(); } // What an ugly function! Simplify somehow! void FITSTable::fill_row() { // fill the current row into the Row object. for (uInt i=0; i < nfields_p; i++) { // get the scaling factors Double zero, scale; zero = raw_table_p->tzero(i); scale = raw_table_p->tscal(i); // get any necessary shapes IPosition shape; if (isArray(DataType(field_types_p[i])) && description_p.shape(i).nelements()==1 && description_p.shape(i)(0) == -1) { // this is only worth doing for arrays which can be reshaped if (tdims_p[i] >= 0) { // get the shape from the tdim column // get it from the raw fits since it might not have been filled yet Int tdfield = tdims_p[i]; DebugAssert(field_types_p[tdfield] == TpString, AipsError); FitsField &fitsRef = (FitsField &)(raw_table_p->field(tdfield)); // look for the true end of the string char * cptr = (char *)fitsRef.data(); uInt length = fitsRef.nelements(); while (length > 0 && (cptr[length-1] == '\0' || cptr[length-1] == ' ')) { length--; } String tdim((char *)fitsRef.data(), length); // remove the surrounding parenthesis tdim = tdim.after(tdim.find('(')); tdim = tdim.before(tdim.find(')')); // count up the number of commas Int ncommas = tdim.freq(','); shape.resize(ncommas+1, False); for (Int j=0;jfield(i).fieldtype() != FITS::CHAR) { DebugAssert(raw_table_p->field(i).fieldtype() == FITS::VADESC, AipsError); FitsVADesc thisva = va_p[i](); shape = IPosition(1, thisva.num()); } } } switch (raw_table_p->field(i).fieldtype()) { case FITS::LOGICAL: { FitsField &fitsRef = (FitsField &)(raw_table_p->field(i)); if (field_types_p[i] == TpBool) { RecordFieldPtr &rowRef = *((RecordFieldPtr *)row_fields_p[i]); *rowRef = fitsRef(); } else { DebugAssert(field_types_p[i] == TpArrayBool, AipsError); RecordFieldPtr > &rowRef = *((RecordFieldPtr > *)row_fields_p[i]); // does this need to be reshaped? Int n = raw_table_p->field(i).nelements(); if (tdims_p[i] >= 0) { (*rowRef).resize(shape); n = shape.product(); } Bool deleteIt; Bool *data = (*rowRef).getStorage(deleteIt); while (n) { n--; data[n] = fitsRef(n); } (*rowRef).putStorage(data, deleteIt); } } break; case FITS::BIT: { FitsField &fitsRef = (FitsField &)(raw_table_p->field(i)); if (field_types_p[i] == TpBool) { RecordFieldPtr &rowRef = *((RecordFieldPtr *)row_fields_p[i]); (*rowRef) = (int(fitsRef())); } else { DebugAssert(field_types_p[i] == TpArrayBool, AipsError); RecordFieldPtr > &rowRef = *((RecordFieldPtr > *)row_fields_p[i]); // does this need to be reshaped? Int n = raw_table_p->field(i).nelements(); if (tdims_p[i] >= 0) { (*rowRef).resize(shape); n = shape.product(); } Bool deleteIt; Bool *data = (*rowRef).getStorage(deleteIt); while (n) { n--; data[n] = (int(fitsRef(n))); } (*rowRef).putStorage(data, deleteIt); } } break; case FITS::CHAR: { String fieldName = description_p.name(i); if (!subStrShapes_p.isDefined(fieldName)) { DebugAssert(field_types_p[i] == TpString, AipsError); FitsField &fitsRef = (FitsField &)(raw_table_p->field(i)); RecordFieldPtr &rowRef = *((RecordFieldPtr *)row_fields_p[i]); char * cptr = (char *)fitsRef.data(); uInt length = charLength(cptr, fitsRef.nelements()); (*rowRef) = String(cptr, length); } else { DebugAssert(field_types_p[i] == TpArrayString, AipsError); FitsField &fitsRef = (FitsField &)(raw_table_p->field(i)); RecordFieldPtr > &rowRef = *((RecordFieldPtr > *)row_fields_p[i]); char * cptr = (char *)fitsRef.data(); uInt length = charLength(cptr, fitsRef.nelements()); String rawValue(cptr, length); // figure out a way to cache this to make it faster Record info(subStrShapes_p.asRecord(fieldName)); Int nels = info.asInt("NELEM"); Int nchar = info.asInt("NCHAR"); Vector result; if (nels > 0) { // fixed shape result.resize(nels); // and just do them all Int curr = 0; for (Int z=0;z &fitsRef = (FitsField &)(raw_table_p->field(i)); if (field_types_p[i] == TpUChar) { RecordFieldPtr &rowRef = *((RecordFieldPtr *)row_fields_p[i]); (*rowRef) = fitsRef(); } else { if (promoted_p[i]) { DebugAssert(field_types_p[i] == TpArrayDouble || field_types_p[i] == TpDouble, AipsError); if (field_types_p[i] == TpArrayDouble) { RecordFieldPtr > &rowRef = *((RecordFieldPtr > *)row_fields_p[i]); // does this need to be reshaped? Int n = raw_table_p->field(i).nelements(); if (tdims_p[i] >= 0) { (*rowRef).resize(shape); n = shape.product(); } Bool deleteIt; Double *data = (*rowRef).getStorage(deleteIt); while (n) { n--; data[n] = Double(fitsRef(n)); } (*rowRef).putStorage(data, deleteIt); *rowRef *= scale; *rowRef += zero; } else { RecordFieldPtr &rowRef = *((RecordFieldPtr *)row_fields_p[i]); (*rowRef) = Double(fitsRef())*scale + zero; } } else { DebugAssert(field_types_p[i] == TpArrayUChar, AipsError); RecordFieldPtr > &rowRef = *((RecordFieldPtr > *)row_fields_p[i]); // does this need to be reshaped? Int n = raw_table_p->field(i).nelements(); if (tdims_p[i] >= 0) { (*rowRef).resize(shape); n = shape.product(); } Bool deleteIt; uChar *data = (*rowRef).getStorage(deleteIt); while (n) { n--; data[n] = fitsRef(n); } (*rowRef).putStorage(data, deleteIt); } } } break; case FITS::SHORT: { FitsField &fitsRef = (FitsField &)(raw_table_p->field(i)); if (field_types_p[i] == TpShort) { RecordFieldPtr &rowRef = *((RecordFieldPtr *)row_fields_p[i]); (*rowRef) = fitsRef(); } else { if (promoted_p[i]) { DebugAssert(field_types_p[i] == TpArrayDouble || field_types_p[i] == TpDouble, AipsError); if (field_types_p[i] == TpArrayDouble) { RecordFieldPtr > &rowRef = *((RecordFieldPtr > *)row_fields_p[i]); // does this need to be reshaped? Int n = raw_table_p->field(i).nelements(); if (tdims_p[i] >= 0) { (*rowRef).resize(shape); n = shape.product(); } Bool deleteIt; Double *data = (*rowRef).getStorage(deleteIt); while (n) { n--; data[n] = Double(fitsRef(n)); } (*rowRef).putStorage(data, deleteIt); *rowRef *= scale; *rowRef += zero; } else { RecordFieldPtr &rowRef = *((RecordFieldPtr *)row_fields_p[i]); (*rowRef) = Double(fitsRef())*scale + zero; } } else { DebugAssert(field_types_p[i] == TpArrayShort, AipsError); RecordFieldPtr > &rowRef = *((RecordFieldPtr > *)row_fields_p[i]); // does this need to be reshaped? Int n = raw_table_p->field(i).nelements(); if (tdims_p[i] >= 0) { (*rowRef).resize(shape); n = shape.product(); } Bool deleteIt; Short *data = (*rowRef).getStorage(deleteIt); while (n) { n--; data[n] = fitsRef(n); } (*rowRef).putStorage(data, deleteIt); } } } break; case FITS::LONG: { FitsField &fitsRef = (FitsField &)(raw_table_p->field(i)); if (field_types_p[i] == TpInt) { RecordFieldPtr &rowRef = *((RecordFieldPtr *)row_fields_p[i]); (*rowRef) = fitsRef(); } else { if (promoted_p[i]) { DebugAssert(field_types_p[i] == TpArrayDouble || field_types_p[i] == TpDouble, AipsError); if (field_types_p[i] == TpArrayDouble) { RecordFieldPtr > &rowRef = *((RecordFieldPtr > *)row_fields_p[i]); // does this need to be reshaped? Int n = raw_table_p->field(i).nelements(); if (tdims_p[i] >= 0) { (*rowRef).resize(shape); n = shape.product(); } Bool deleteIt; Double *data = (*rowRef).getStorage(deleteIt); while (n) { n--; data[n] = Double(fitsRef(n)); } (*rowRef).putStorage(data, deleteIt); *rowRef *= scale; *rowRef += zero; } else { RecordFieldPtr &rowRef = *((RecordFieldPtr *)row_fields_p[i]); (*rowRef) = Double(fitsRef())*scale + zero; } } else { DebugAssert(field_types_p[i] == TpArrayInt, AipsError); RecordFieldPtr > &rowRef = *((RecordFieldPtr > *)row_fields_p[i]); // does this need to be reshaped? Int n = raw_table_p->field(i).nelements(); if (tdims_p[i] >= 0) { (*rowRef).resize(shape); n = shape.product(); } Bool deleteIt; Int *data = (*rowRef).getStorage(deleteIt); while (n) { n--; data[n] = fitsRef(n); } (*rowRef).putStorage(data, deleteIt); } } } break; case FITS::FLOAT: { FitsField &fitsRef = (FitsField &)(raw_table_p->field(i)); if (field_types_p[i] == TpFloat) { RecordFieldPtr &rowRef = *((RecordFieldPtr *)row_fields_p[i]); (*rowRef) = Float(fitsRef()*scale + zero); } else { DebugAssert(field_types_p[i] == TpArrayFloat, AipsError); RecordFieldPtr > &rowRef = *((RecordFieldPtr > *)row_fields_p[i]); // does this need to be reshaped? Int n = raw_table_p->field(i).nelements(); if (tdims_p[i] >= 0) { (*rowRef).resize(shape); n = shape.product(); } Bool deleteIt; Float *data = (*rowRef).getStorage(deleteIt); while (n) { n--; data[n] = fitsRef(n); } (*rowRef).putStorage(data, deleteIt); *rowRef *= Float(scale); *rowRef += Float(zero); } } break; case FITS::DOUBLE: { FitsField &fitsRef = (FitsField &)(raw_table_p->field(i)); if (field_types_p[i] == TpDouble) { RecordFieldPtr &rowRef = *((RecordFieldPtr *)row_fields_p[i]); (*rowRef) = fitsRef()*scale + zero; } else { DebugAssert(field_types_p[i] == TpArrayDouble, AipsError); RecordFieldPtr > &rowRef = *((RecordFieldPtr > *)row_fields_p[i]); // does this need to be reshaped? Int n = raw_table_p->field(i).nelements(); if (tdims_p[i] >= 0) { (*rowRef).resize(shape); n = shape.product(); } Bool deleteIt; Double *data = (*rowRef).getStorage(deleteIt); while (n) { n--; data[n] = fitsRef(n); } (*rowRef).putStorage(data, deleteIt); *rowRef *= scale; *rowRef += zero; } } break; case FITS::COMPLEX: { FitsField &fitsRef = (FitsField &)(raw_table_p->field(i)); if (field_types_p[i] == TpComplex) { RecordFieldPtr &rowRef = *((RecordFieldPtr *)row_fields_p[i]); const Complex& val = fitsRef(); (*rowRef) = Complex (val.real() * scale + zero, val.imag() * scale + zero); } else { DebugAssert(field_types_p[i] == TpArrayComplex, AipsError); RecordFieldPtr > &rowRef = *((RecordFieldPtr > *)row_fields_p[i]); // does this need to be reshaped? Int n = raw_table_p->field(i).nelements(); if (tdims_p[i] >= 0) { (*rowRef).resize(shape); n = shape.product(); } Bool deleteIt; Complex *data = (*rowRef).getStorage(deleteIt); while (n) { n--; const Complex& val = fitsRef(n); data[n] = Complex (val.real() * scale + zero, val.imag() * scale + zero); } (*rowRef).putStorage(data, deleteIt); } } break; case FITS::DCOMPLEX: { FitsField &fitsRef = (FitsField &)(raw_table_p->field(i)); if (field_types_p[i] == TpDComplex) { RecordFieldPtr &rowRef = *((RecordFieldPtr *)row_fields_p[i]); const DComplex& val = fitsRef(); (*rowRef) = DComplex (val.real() * scale + zero, val.imag() * scale + zero); } else { DebugAssert(field_types_p[i] == TpArrayDComplex, AipsError); RecordFieldPtr > &rowRef = *((RecordFieldPtr > *)row_fields_p[i]); // does this need to be reshaped? Int n = raw_table_p->field(i).nelements(); if (tdims_p[i] >= 0) { (*rowRef).resize(shape); n = shape.product(); } Bool deleteIt; DComplex *data = (*rowRef).getStorage(deleteIt); while (n) { n--; const DComplex& val = fitsRef(n); data[n] = DComplex (val.real() * scale + zero, val.imag() * scale + zero); } (*rowRef).putStorage(data, deleteIt); } } break; case FITS::ICOMPLEX: { FitsField &fitsRef = (FitsField &)(raw_table_p->field(i)); if (field_types_p[i] == TpDComplex) { RecordFieldPtr &rowRef = *((RecordFieldPtr *)row_fields_p[i]); const IComplex& val = fitsRef(); (*rowRef) = DComplex (val.real() * scale + zero, val.imag() * scale + zero); } else { DebugAssert(field_types_p[i] == TpArrayDComplex, AipsError); RecordFieldPtr > &rowRef = *((RecordFieldPtr > *)row_fields_p[i]); // does this need to be reshaped? Int n = raw_table_p->field(i).nelements(); if (tdims_p[i] >= 0) { (*rowRef).resize(shape); n = shape.product(); } Bool deleteIt; DComplex *data = (*rowRef).getStorage(deleteIt); while (n) { n--; const IComplex& val = fitsRef(n); data[n] = DComplex (val.real() * scale + zero, val.imag() * scale + zero); } (*rowRef).putStorage(data, deleteIt); } } break; case FITS::VADESC: { FitsVADesc thisva = va_p[i](); switch (vatypes_p[i]) { case FITS::LOGICAL: { FitsLogical *vptr = (FitsLogical *)(vaptr_p[i]); FITS::f2l(vptr, (void *)(theheap_p + thisva.offset()), thisva.num()); DebugAssert(field_types_p[i] == TpArrayBool, AipsError); RecordFieldPtr > &rowRef = *((RecordFieldPtr > *) row_fields_p[i]); // need to shape the output array (*rowRef).resize(shape); Bool deleteIt; Bool *data = (*rowRef).getStorage(deleteIt); Int n = shape.product(); while (n) { n--; data[n] = vptr[n]; } (*rowRef).putStorage(data, deleteIt); } break; case FITS::BIT: { uChar *vptr = (uChar *)(vaptr_p[i]); FITS::f2l(vptr, (void *)(theheap_p + thisva.offset()), thisva.num()); DebugAssert(field_types_p[i] == TpArrayBool, AipsError); RecordFieldPtr > &rowRef = *((RecordFieldPtr > *) row_fields_p[i]); // need to shape the output array (*rowRef).resize(shape); Bool deleteIt; Bool *data = (*rowRef).getStorage(deleteIt); Int n = shape.product(); Int whichByte = n/8 - 1; if (n%8) whichByte++; uChar mask = 0200; while (n) { n--; if (n%8 == 7) whichByte--; data[n] = (vptr[whichByte] & (mask >> n%8)); } (*rowRef).putStorage(data, deleteIt); } break; case FITS::CHAR: { // the sub string convention can't be used here Char *vptr = (Char *)(vaptr_p[i]); FITS::f2l(vptr, (void *)(theheap_p + thisva.offset()), thisva.num()); DebugAssert(field_types_p[i] == TpString, AipsError); RecordFieldPtr &rowRef = *((RecordFieldPtr *) row_fields_p[i]); uInt length = charLength(vptr, thisva.num()); (*rowRef) = String(vptr, length); } break; case FITS::BYTE: { uChar *vptr = (uChar *)(vaptr_p[i]); FITS::f2l(vptr, (void *)(theheap_p + thisva.offset()), thisva.num()); if (promoted_p[i]) { DebugAssert(field_types_p[i] == TpArrayDouble, AipsError); RecordFieldPtr > &rowRef = *((RecordFieldPtr > *) row_fields_p[i]); // need to shape the output array (*rowRef).resize(shape); Bool deleteIt; Double *data = (*rowRef).getStorage(deleteIt); Int n = shape.product(); while (n) { n--; data[n] = Double(vptr[n]); } (*rowRef).putStorage(data, deleteIt); *rowRef *= scale; *rowRef += zero; } else { DebugAssert(field_types_p[i] == TpArrayUChar, AipsError); RecordFieldPtr > &rowRef = *((RecordFieldPtr > *) row_fields_p[i]); // need to shape the output array (*rowRef).resize(shape); Bool deleteIt; uChar *data = (*rowRef).getStorage(deleteIt); Int n = shape.product(); while (n) { n--; data[n] = vptr[n]; } (*rowRef).putStorage(data, deleteIt); } } break; case FITS::SHORT: { Short *vptr = (Short *)(vaptr_p[i]); FITS::f2l(vptr, (void *)(theheap_p + thisva.offset()), thisva.num()); if (promoted_p[i]) { DebugAssert(field_types_p[i] == TpArrayDouble, AipsError); RecordFieldPtr > &rowRef = *((RecordFieldPtr > *) row_fields_p[i]); // need to shape the output array (*rowRef).resize(shape); Bool deleteIt; Double *data = (*rowRef).getStorage(deleteIt); Int n = shape.product(); while (n) { n--; data[n] = Double(vptr[n]); } (*rowRef).putStorage(data, deleteIt); *rowRef *= scale; *rowRef += zero; } else { DebugAssert(field_types_p[i] == TpArrayShort, AipsError); RecordFieldPtr > &rowRef = *((RecordFieldPtr > *) row_fields_p[i]); // need to shape the output array (*rowRef).resize(shape); Bool deleteIt; Short *data = (*rowRef).getStorage(deleteIt); Int n = shape.product(); while (n) { n--; data[n] = vptr[n]; } (*rowRef).putStorage(data, deleteIt); } } break; case FITS::LONG: { FitsLong *vptr = (FitsLong *)(vaptr_p[i]); FITS::f2l(vptr, (void *)(theheap_p + thisva.offset()), thisva.num()); if (promoted_p[i]) { DebugAssert(field_types_p[i] == TpArrayDouble, AipsError); RecordFieldPtr > &rowRef = *((RecordFieldPtr > *) row_fields_p[i]); // need to shape the output array (*rowRef).resize(shape); Bool deleteIt; Double *data = (*rowRef).getStorage(deleteIt); Int n = shape.product(); while (n) { n--; data[n] = Double(vptr[n]); } (*rowRef).putStorage(data, deleteIt); *rowRef *= scale; *rowRef += zero; } else { DebugAssert(field_types_p[i] == TpArrayInt, AipsError); RecordFieldPtr > &rowRef = *((RecordFieldPtr > *) row_fields_p[i]); // need to shape the output array (*rowRef).resize(shape); Bool deleteIt; Int *data = (*rowRef).getStorage(deleteIt); Int n = shape.product(); while (n) { n--; data[n] = vptr[n]; } (*rowRef).putStorage(data, deleteIt); } } break; case FITS::FLOAT: { Float *vptr = (Float *)(vaptr_p[i]); FITS::f2l(vptr, (void *)(theheap_p + thisva.offset()), thisva.num()); DebugAssert(field_types_p[i] == TpArrayFloat, AipsError); RecordFieldPtr > &rowRef = *((RecordFieldPtr > *) row_fields_p[i]); // need to shape the output array (*rowRef).resize(shape); Bool deleteIt; Float *data = (*rowRef).getStorage(deleteIt); Int n = shape.product(); while (n) { n--; data[n] = vptr[n]; } (*rowRef).putStorage(data, deleteIt); *rowRef *= Float(scale); *rowRef += Float(zero); } break; case FITS::DOUBLE: { Double *vptr = (Double *)(vaptr_p[i]); FITS::f2l(vptr, (void *)(theheap_p + thisva.offset()), thisva.num()); DebugAssert(field_types_p[i] == TpArrayDouble, AipsError); RecordFieldPtr > &rowRef = *((RecordFieldPtr > *) row_fields_p[i]); // need to shape the output array (*rowRef).resize(shape); Bool deleteIt; Double *data = (*rowRef).getStorage(deleteIt); Int n = shape.product(); while (n) { n--; data[n] = vptr[n]; } (*rowRef).putStorage(data, deleteIt); *rowRef *= scale; *rowRef += zero; } break; case FITS::COMPLEX: { Complex *vptr = (Complex *)(vaptr_p[i]); FITS::f2l(vptr, (void *)(theheap_p + thisva.offset()), thisva.num()); DebugAssert(field_types_p[i] == TpArrayComplex, AipsError); RecordFieldPtr > &rowRef = *((RecordFieldPtr > *) row_fields_p[i]); // need to shape the output array (*rowRef).resize(shape); Bool deleteIt; Complex *data = (*rowRef).getStorage(deleteIt); Int n = shape.product(); while (n) { n--; const Complex& val = vptr[n]; data[n] = Complex (val.real() * scale + zero, val.imag() * scale + zero); } (*rowRef).putStorage(data, deleteIt); } break; case FITS::DCOMPLEX: { DComplex *vptr = (DComplex *)(vaptr_p[i]); FITS::f2l(vptr, (void *)(theheap_p + thisva.offset()), thisva.num()); DebugAssert(field_types_p[i] == TpArrayDComplex, AipsError); RecordFieldPtr > &rowRef = *((RecordFieldPtr > *) row_fields_p[i]); // need to shape the output array (*rowRef).resize(shape); Bool deleteIt; DComplex *data = (*rowRef).getStorage(deleteIt); Int n = shape.product(); while (n) { n--; const DComplex& val = vptr[n]; data[n] = DComplex (val.real() * scale + zero, val.imag() * scale + zero); } (*rowRef).putStorage(data, deleteIt); } break; default: throw(AipsError("FITSTable::fillrow() - unexpected variable array type")); break; } } break; default: throw(AipsError("FITSTable::fill_row() - unknown data type")); } } } void FITSTable::clear_self() { row_nr_p = -1; delete raw_table_p; raw_table_p = 0; delete io_p; io_p = 0; uInt i; for (i=0; i < nfields_p; i++) { switch( field_types_p[i]) { case TpBool: delete (RecordFieldPtr *)row_fields_p[i]; break; case TpArrayBool: delete (RecordFieldPtr > *)row_fields_p[i]; break; case TpUChar: delete (RecordFieldPtr *)row_fields_p[i]; break; case TpArrayUChar: delete (RecordFieldPtr > *)row_fields_p[i]; break; case TpShort: delete (RecordFieldPtr *)row_fields_p[i]; break; case TpArrayShort: delete (RecordFieldPtr > *)row_fields_p[i]; break; case TpInt: delete (RecordFieldPtr *)row_fields_p[i]; break; case TpArrayInt: delete (RecordFieldPtr > *)row_fields_p[i]; break; case TpFloat: delete (RecordFieldPtr *)row_fields_p[i]; break; case TpArrayFloat: delete (RecordFieldPtr > *)row_fields_p[i]; break; case TpDouble: delete (RecordFieldPtr *)row_fields_p[i]; break; case TpArrayDouble: delete (RecordFieldPtr > *)row_fields_p[i]; break; case TpComplex: delete (RecordFieldPtr *)row_fields_p[i]; break; case TpArrayComplex: delete (RecordFieldPtr > *)row_fields_p[i]; break; case TpDComplex: delete (RecordFieldPtr *)row_fields_p[i]; break; case TpArrayDComplex: delete (RecordFieldPtr > *)row_fields_p[i]; break; case TpString: delete (RecordFieldPtr *)row_fields_p[i]; break; case TpArrayString: delete (RecordFieldPtr > *)row_fields_p[i]; break; default: throw(AipsError("FITSTable::clear_self() - unknown field type")); } row_fields_p[i] = 0; } for (i=0;i= Int(nrow())) torow = Int(nrow()) - 1; // if we are already there, just return if (torow == rownr()) return; // use the native FITS classes to move while (row_nr_p < torow) { row_nr_p++; if (!theheap_p) raw_table_p->read(1); else ++(*raw_table_p); } // and fill this row if (isValid()) fill_row(); } Bool FITSTable::pastEnd() const { return ((isValid() && row_nr_p >= raw_table_p->nrows()) || ! isValid()); } Bool FITSTable::virtualColumns(const Vector& keyNames) { // move keyNames Bool result = True; for (uInt i=0;i= 0) { switch (keywords_p.type(fieldNumber)) { case TpBool: { Bool value; keywords_p.get(keyNames(i), value); row_p.define(keyNames(i), value); } break; case TpUChar: { uChar value; keywords_p.get(keyNames(i), value); row_p.define(keyNames(i), value); } break; case TpShort: { Short value; keywords_p.get(keyNames(i), value); row_p.define(keyNames(i), value); } break; case TpInt: { Int value; keywords_p.get(keyNames(i), value); row_p.define(keyNames(i), value); } break; case TpUInt: { uInt value; keywords_p.get(keyNames(i), value); row_p.define(keyNames(i), value); } break; case TpFloat: { Float value; keywords_p.get(keyNames(i), value); row_p.define(keyNames(i), value); } break; case TpDouble: { Double value; keywords_p.get(keyNames(i), value); row_p.define(keyNames(i), value); } break; case TpComplex: { Complex value; keywords_p.get(keyNames(i), value); row_p.define(keyNames(i), value); } break; case TpDComplex: { DComplex value; keywords_p.get(keyNames(i), value); row_p.define(keyNames(i), value); } break; case TpString: { String value; keywords_p.get(keyNames(i), value); row_p.define(keyNames(i), value); } break; default: // this should never happen since the contents of // keywords_p should always be one of the previous types throw(AipsError("FITSTable::virtualColumns() invalid type in FITS keyword")); break; } row_p.setComment(keyNames(i), keywords_p.comment(keyNames(i))); // it should now be safe to delete this keyword keywords_p.removeField(keyNames(i)); } else { // not found in keywords_p result = False; } } // reset description description_p = row_p.description(); return result; } void FITSTable::reopenAtFirstHDU(const String &name) { delete io_p; io_p = 0; io_p = new FitsInput(name.chars(), FITS::Disk); AlwaysAssert(io_p, AipsError); // no need to check for err here, presumably io_p->skip_hdu(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/fits/FITS/FITSTable.h000066400000000000000000000430071476623553700167300ustar00rootroot00000000000000//# FITSTable.h: Simplified interface to FITS tables with Casacore Look and Feel. //# Copyright (C) 1995,1996,1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef FITS_FITSTABLE_H #define FITS_FITSTABLE_H #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class String; class FITSFieldCopier; class TableDesc; // // Simplified interface to FITS tables with Casacore Look and Feel. // // // // // // // // //
      • General knowledge of FITS binary and ASCII tables. // // // // // // // FITSTablular is an obstract base class which is used for read-only access to // tabular FITS-like data structures. // // // // // // // // // //
      • Eventually we'd like to be able to write the tables as well as read // them. // class FITSTabular { public: virtual ~FITSTabular(); // isValid() returns False if this object isn't a valid Tabular data // structure. virtual Bool isValid() const = 0; // Returns keywords which are associated with the underlying FITS files. virtual const TableRecord &keywords() const = 0; // Returns the description of the underlying FITS table. virtual const RecordDesc &description() const = 0; // Returns any TUNITnnn associated with a column (the field names // are the column names, each field value is the TUNITnnn value for // that field). Note that only those columns with a non-empty // TUNITnnn have an entry in the units() Record. virtual const Record &units() const = 0; // Returns any TDISPnnn associated with a column (the field names // are the column names, each field value is the TDISPnnn value for // that field). Note that only those columns with a non-empty // TDISPnnn have an entry in the displayFormats() Record. virtual const Record &displayFormats() const = 0; // Returns any TNULLnnn associated with a column (the field names // are the column names, each field value is the TNULLnnn value for // that field). Note that only those columns with a specific entry for // TNULLnnn and which have not been promoted to doubles due TSCAL // and TZERO values will have an entry in the nulls() Record. // The meaning of TNULL is only defined for integer and byte columns. // When a column is promoted to a double because of scaling, // any TNULL values will be assigned a value of NaN. virtual const Record &nulls() const = 0; // Returns True if we have advanced past the end of data. virtual Bool pastEnd() const = 0; // Advance the row if possible (guaranteed harmless if pastEnd() is True. virtual void next() = 0; // Reopen the table, default behavior is to do nothing, return False virtual Bool reopen(const String&) { return False; } // return the name virtual const String &name() const = 0; // Has the description changed since construction, default is False virtual Bool hasChanged() const { return False;} // reset the changed flag, default do nothing virtual void resetChangedFlag() {;} // Return the currentRow. This is guaranteed to be valid so long as only // member functions of this base class are called (so you can safely attach // RecordFieldPtr objects to it. The result is undefined if pastEnd() is True. virtual const Record ¤tRow() const = 0; // Helper function for retrieving keywords from a native-FITS hdu. // If allKeywords is not True, some keywords will be excluded // from the list. Currently the list of excluded keywords // includes TTYPEnnn, TFORMnnn, and TUNITnnn static TableRecord keywordsFromHDU(HeaderDataUnit &hdu, Bool allKeywords = False); // Helper function for retrieving a description from a native-FITS hdu. static RecordDesc descriptionFromHDU(BinaryTableExtension &hdu); // Help function for retrieving any shape information from String columns // using the SubString convention. // Information is returned in a Record having named fields = all String // columns following those convention. Each of these fields is, in turn, // a sub-record having these three fields: NCHAR, NELEM, DELIM. // If NELEM == -1 then there must have been a DELIM specified and // this field is a variable shaped string array where each element has // at most NCHAR and they are separated by DELIM (which is a String field here). // Otherwise, DELIM is not used and there are NCHAR per element for each // of NELEM in each cell for this column. static Record subStringShapeFromHDU(BinaryTableExtension &hdu); // Helper function for retrieving the TUNITnnn from a native-FITS hdu. static Record unitsFromHDU(BinaryTableExtension &hdu); // Helper function for retrieving the TDISPnnn from a native-FITS hdu. static Record displayFormatsFromHDU(BinaryTableExtension &hdu); // Helper function for retrieving the TNULLnnn from a native-FITS hdu. static Record nullsFromHDU(BinaryTableExtension &hdu); // Get a TableDesc appropriate to hold a FITSTabular // the keywords, description, units, displayFormats, and nulls are all used static TableDesc tableDesc(const FITSTabular &fitstabular); }; // // Attach a FITSTabular to a binary or ASCII table // // // // // // // // //
      • General knowledge of FITS binary and ASCII tables. // // // // // // // FITSTable is a FITSTabular which is attached to a FITS table (on disk only // presently), either Binary or ASCII. // // // // // // // // // //
      • // class FITSTable : public FITSTabular { public: // this creates an invalid (isValid() return False) FITSTable // Its primary purpose is so that FITSTables can be created before // the file name is known. reopen() is then used to open the file. FITSTable(uInt whichHDU=1, Bool allKeywords=False); // 0-relative HDU. It can never be zero by the FITS rules. // allKeywords is passed to FITSTabular::keywordsFromHDU // See the documentation for that function for a list of // excluded keywords when allKeywords is False. FITSTable(const String &fileName, uInt whichHDU=1, Bool allKeywords = False); ~FITSTable() { clear_self();} // Has the end of file been reached yet virtual Bool eof() const {return io_p->eof();} // Attach this FITSTable to a new file name, same HDU# as at open time virtual Bool reopen(const String &fileName); virtual const String& name() const { return name_p;} virtual Bool isValid() const {return isValid_p;} virtual const TableRecord &keywords() const {return keywords_p;} virtual const RecordDesc &description() const {return description_p;} virtual const Record &units() const {return units_p;} virtual const Record &displayFormats() const {return disps_p;} virtual const Record &nulls() const {return nulls_p;} virtual Bool pastEnd() const; virtual void next(); virtual const Record ¤tRow() const; // single FITS tables know how many rows there are // unlike general FITSTabulars, which may not know // (e.g. if it is a FITSMultiTable) virtual uInt nrow() const {return raw_table_p->nrows();} // these tables should also know where they are virtual Int rownr() const {return row_nr_p;} // and it should be possible to move to a desired row // the rownr() member can be used to verify that a move // was successful - this will happen if the requested row // was < rownr() or >= nrow() - i.e. movements backwards or // beyond the end of the file are not possible. virtual void move(Int torow); // the keywords from the Primary HDU virtual const TableRecord &primaryKeywords() const {return primaryKeys_p;} protected: // SDFITSTable needs to make some keywords appear as // columns, this requires access to description_p, keywords_p, and // row_p. However, its not something that typical FITSTable // users will want. Therefore, I've provided this protected // function for SDFITSTable to use so as to not have to provide // direct access to those data members at the public level. // The named keywords and values are appended to the end of // row_p and removed from keywords_p, description_p is modified // appropriately. The returned value is False if any named // keyword did not appear in keywords_p (however, all named // keywords that DO appear in keywords_p will have been correctly // moved). Bool virtualColumns(const Vector& keyNames); private: // Undefined and inaccessible. An alternative would be to use reference // semantics like Table. FITSTable(const FITSTable &); FITSTable &operator=(const FITSTable &); void fill_row(); void clear_self(); Bool isValid_p; String name_p; uInt hdu_nr_p; Int row_nr_p; BinaryTableExtension *raw_table_p; FitsInput *io_p; TableRecord keywords_p; TableRecord primaryKeys_p; RecordDesc description_p; Record row_p; Record units_p; Record disps_p; Record nulls_p; Record subStrShapes_p; Bool allKeys_p; // One per field in row_p, of the right type. i.e. casting required. uInt nfields_p; Block row_fields_p; Block field_types_p; Block promoted_p; Block tdims_p; // these are used by VADESC columns Block vatypes_p; Block vaptr_p; // I had trouble making a Block VADescFitsField *va_p; char *theheap_p; // It is necessary to read the PDA to get the primary keywords. // If there is any data there, the FITS classes do not provide any way to // just skip over them to get to the next HDU. The only way to do that is // to actually read all of the data. If there is no data, this step is // unnecessary and so this subroutine need only be called after the primary // keywords have been read AND the PDA has some data in it. Closing the // FitsInput and reopening it is faster in most cases than reading in each // data value. void reopenAtFirstHDU(const String &name); }; // // Simplified interface to create and write to a FITS Binary Table // // // // // // // // //
      • General knowledge of FITS binary and ASCII tables. // // // // // // // // // // // // // // // //
      • probably much // class FITSTableWriter { public: enum {DefaultMaxStringSize = 16}; // You MUST have already written a first HDU to FitsOutput. // description contains the names and types of the table columns to be written. // The row is not rearranged (i.e. they are used in order) for alignment purposes. // Array columns must have fixed shape unless tdimColumns is used. Use the // maxLengths record to indicate any string columns which should have a length other // than the default value by providing an int field of the same name as the string // field in this record. // The size of the table (nrows) must be given at creation. Use extraKeywords to // indicate any keywords not automatically created. The units record is used to // indicate the units for any column. Provide a string field with the same name as // the column field in description. If freeOutput is True, file must come from new // since it will be deleted upon destruction. You might not want this to happen if // you are going to write many tables to the same fits file. Use variableShapes to // signal which array columns have variable shape and use maxLengths to indicate // the maximum size of those variable shaped columns. The variableShapes record // should contain a String corresponding to the longest TDIM value appropriate for // each variable shaped column. The maxLengths record should contain an int field // for each variable shaped column which indicates the maximum number of elements // to be found in that column. Unused values in any cell in the variable shaped // columns will be filled with zero. These columns will use the SDFITS TDIM convention // where for column nnn there is a corresponding TDIMnnn column where // the values in each row are the true shape of the data in column nnn. // variableShapes appears as the last argument for backwards compatibility with // existing code. FITSTableWriter(FitsOutput *file, const RecordDesc &description, const Record &maxLengths, uInt nrows, const Record &extraKeywords, const Record &units, Bool freeOutput = True, const Record &variableShapes = Record()); ~FITSTableWriter(); // use this to set the value of the current row to be written RecordInterface &row() {return row_p;} // Write the current row() void write(); // Don't delete this out from under us! FitsOutput *writer() {return writer_p;} // Returns a writer, with the first HDU filled in (set to null). The caller // is responsible for deleting the pointer returned from makeWriter. static FitsOutput *makeWriter(const String &fileName); private: // Undefined and inaccessible FITSTableWriter(); FITSTableWriter(const FITSTableWriter&); FITSTableWriter& operator=(const FITSTableWriter&); Bool delete_writer_p; FitsOutput *writer_p; uInt nrows_written_p; BinaryTableExtension *bintable_p; Record row_p; PtrBlock copiers_p; }; // // Simplified interface to create and write to FITS random groups // // // // // // // // //
      • General knowledge of FITS binary and ASCII tables. // // // // // // // Like FITSTableWriter except that this must be the first HDU and // all "columns" in the description must have the same type, i.e. float. // // // // // // // // // //
      • probably much // class FITSGroupWriter { public: // Since this must always be the first HDU, there is no point in constructing it // with a FitsOutput. description indicates the names of the random groups parameters. // nrows is a synonym for ngroups. Use extraKeywords to // indicate any keywords not automatically created (SIMPLE, BITPIX, NAXIS*, EXTEND, // BLOCKED, GROUPS, PCOUNT, GOUNT, ORIGIN, END). If freeOutput is True, file will be // deleted by the destructor. You might not want this to happen if // you are going to write any extensions to the same fits file. You can get the // FitsOutput used here from write() FITSGroupWriter(const String &fileName, const RecordDesc &description, uInt nrows, const Record &extraKeywords, Bool freeOutput = True); ~FITSGroupWriter(); // Set the values for the current group RecordInterface &row() {return row_p;} // Write the current group (row()). void write(); // Don't delete this out from under us! FitsOutput *writer() {return writer_p;} private: // Undefined and inaccessible FITSGroupWriter(); FITSGroupWriter(const FITSGroupWriter&); FITSGroupWriter& operator=(const FITSGroupWriter&); Bool delete_writer_p; FitsOutput *writer_p; uInt nrows_written_p, nrows_total_p; PrimaryGroup *group_p; Record row_p; Int error_count_p; // Checks error status of writer_p and group_p. Cleans up and throws an exception if bad. void check_error(const char *extra_info = 0); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/fits/FITS/FITSTable2.cc000066400000000000000000000463351476623553700171570ustar00rootroot00000000000000//# FITSTable.h: Simplified interface to FITS tables with Casacore Look and Feel. //# Copyright (C) 1995,1996,1997,1998,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN uInt sizeofStringField(const RecordDesc &description, const Record &sizes, uInt whichField) { Int size = FITSTableWriter::DefaultMaxStringSize; AlwaysAssert(description.type(whichField) == TpString, AipsError); String name = description.name(whichField); Int which = sizes.fieldNumber(name); if (which >= 0) { sizes.get(which, size); } return size; } FITSTableWriter::FITSTableWriter(FitsOutput *file, const RecordDesc &description, const Record &maxLengths, uInt nrows, const Record &extraKeywords, const Record &units, Bool freeOutput, const Record &variableShapes) : delete_writer_p(freeOutput), writer_p(file), nrows_written_p(0), bintable_p(0), row_p(description), copiers_p(0) { uInt nfields = description.nfields(); Int sizeInBytes = 0; FitsKeywordList columns; uInt i; uInt thisColumn = 1; Block fieldMap(nfields,-1); Block tdimMap(nfields,-1); Block fieldSizes(nfields,0); for (i=0; i < nfields; i++) { const char *comment = 0; if (description.comment(i) != "") { comment = description.comment(i).chars(); } Bool hasVariableShape = (variableShapes.fieldNumber(description.name(i)) >= 0) && (maxLengths.fieldNumber(description.name(i)) >= 0); Int size = 1; String repeat = "1"; // Always write, even for scalars String code = "X"; switch (description.type(i)) { case TpArrayBool: if (hasVariableShape) { size = maxLengths.asInt(description.name(i)); } else { size = description.shape(i).product(); } { ostringstream buffer; buffer << size; repeat = String(buffer); } CASACORE_FALLTHROUGH; case TpBool: sizeInBytes += size*1; code = "L"; break; case TpArrayUChar: if (hasVariableShape) { size = maxLengths.asInt(description.name(i)); } else { size = description.shape(i).product(); } { ostringstream buffer; buffer << size; repeat = String(buffer); } CASACORE_FALLTHROUGH; case TpUChar: sizeInBytes += size*1; code = "B"; break; case TpArrayShort: if (hasVariableShape) { size = maxLengths.asInt(description.name(i)); } else { size = description.shape(i).product(); } { ostringstream buffer; buffer << size; repeat = String(buffer); } CASACORE_FALLTHROUGH; case TpShort: sizeInBytes += size*2; code = "I"; break; case TpArrayInt: if (hasVariableShape) { size = maxLengths.asInt(description.name(i)); } else { size = description.shape(i).product(); } { ostringstream buffer; buffer << size; repeat = String(buffer); } CASACORE_FALLTHROUGH; case TpInt: sizeInBytes += size*4; code = "J"; break; case TpArrayFloat: if (hasVariableShape) { size = maxLengths.asInt(description.name(i)); } else { size = description.shape(i).product(); } { ostringstream buffer; buffer << size; repeat = String(buffer); } CASACORE_FALLTHROUGH; case TpFloat: sizeInBytes += size*4; code = "E"; break; case TpArrayDouble: if (hasVariableShape) { size = maxLengths.asInt(description.name(i)); } else { size = description.shape(i).product(); } { ostringstream buffer; buffer << size; repeat = String(buffer); } CASACORE_FALLTHROUGH; case TpDouble: sizeInBytes += size*8; code = "D"; break; case TpArrayComplex: if (hasVariableShape) { size = maxLengths.asInt(description.name(i)); } else { size = description.shape(i).product(); } { ostringstream buffer; buffer << size; repeat = String(buffer); } CASACORE_FALLTHROUGH; case TpComplex: sizeInBytes += size*8; code = "C"; break; case TpArrayDComplex: if (hasVariableShape) { size = maxLengths.asInt(description.name(i)); } else { size = description.shape(i).product(); } { ostringstream buffer; buffer << size; repeat = String(buffer); } CASACORE_FALLTHROUGH; case TpDComplex: sizeInBytes += size*16; code = "M"; break; case TpString: { uInt stringlen = sizeofStringField(description, maxLengths, i); sizeInBytes += stringlen; ostringstream buffer; buffer << stringlen; repeat = String(buffer); code = "A"; } break; case TpArrayString: throw(AipsError("Arrays of strings are not yet supported")); break; default: throw(AipsError("Invalid type")); } columns.mk(thisColumn, FITS::TTYPE, description.name(i).chars(), comment); columns.mk(thisColumn, FITS::TFORM, (repeat + code).chars()); IPosition shape = description.shape(i); if (shape.nelements() > 1 && !hasVariableShape) { ostringstream buffer; buffer << "("; for (uInt j=0; j 0, AipsError); // OK, now we can start filling in the header FitsKeywordList kw; kw.mk(FITS::XTENSION,"BINTABLE","Binary Table Extension"); kw.mk(FITS::BITPIX,8,"Character Information"); kw.mk(FITS::NAXIS,2,"Two-dimensional table"); kw.mk(1,FITS::NAXIS,sizeInBytes,"Number of bytes per row"); kw.mk(2,FITS::NAXIS,Int(nrows),"Number of rows"); kw.mk(FITS::PCOUNT,0,"No random parameters"); kw.mk(FITS::GCOUNT,1,"Only one group"); kw.mk(FITS::TFIELDS,Int(thisColumn-1),"Number of columns"); kw.spaces(); // The following are too specific. // kw.mk(FITS::EXTNAME,"SINGLE DISH","Single Dish FITS convention"); // kw.mk(FITS::EXTVER,1,"Version"); kw.spaces(); // Write the users keywords next uInt nkeys = extraKeywords.nfields(); for (i=0; ierr(), AipsError); bintable_p->write_hdr(*writer_p); // OK, now we can attach the copiers. We could make a template function // for this to avoid replicating code. copiers_p.resize(nfields); for (i=0; i *rptr = new RORecordFieldPtr(row_p, i); FitsField *fptr = new FitsField; bintable_p->bind(whichField, *fptr); copiers_p[i] = new ScalarFITSFieldCopier(rptr, fptr); } break; case TpUChar: { RORecordFieldPtr *rptr = new RORecordFieldPtr(row_p, i); FitsField *fptr = new FitsField; bintable_p->bind(whichField, *fptr); copiers_p[i] = new ScalarFITSFieldCopier(rptr, fptr); } break; case TpShort: { RORecordFieldPtr *rptr = new RORecordFieldPtr(row_p, i); FitsField *fptr = new FitsField; bintable_p->bind(whichField, *fptr); copiers_p[i] = new ScalarFITSFieldCopier(rptr, fptr); } break; case TpInt: { RORecordFieldPtr *rptr = new RORecordFieldPtr(row_p, i); FitsField *fptr = new FitsField; bintable_p->bind(whichField, *fptr); copiers_p[i] = new ScalarFITSFieldCopier(rptr, fptr); } break; case TpFloat: { RORecordFieldPtr *rptr = new RORecordFieldPtr(row_p, i); FitsField *fptr = new FitsField; bintable_p->bind(whichField, *fptr); copiers_p[i] = new ScalarFITSFieldCopier(rptr, fptr); } break; case TpDouble: { RORecordFieldPtr *rptr = new RORecordFieldPtr(row_p, i); FitsField *fptr = new FitsField; bintable_p->bind(whichField, *fptr); copiers_p[i] = new ScalarFITSFieldCopier(rptr, fptr); } break; case TpComplex: { RORecordFieldPtr *rptr = new RORecordFieldPtr(row_p, i); FitsField *fptr = new FitsField; bintable_p->bind(whichField, *fptr); copiers_p[i] = new ScalarFITSFieldCopier(rptr, fptr); } break; case TpDComplex: { RORecordFieldPtr *rptr = new RORecordFieldPtr(row_p, i); FitsField *fptr = new FitsField; bintable_p->bind(whichField, *fptr); copiers_p[i] = new ScalarFITSFieldCopier(rptr, fptr); } break; case TpString: { RORecordFieldPtr *rptr = new RORecordFieldPtr(row_p, i); FitsField *fptr = new FitsField(sizeofStringField(description, maxLengths, i)); bintable_p->bind(whichField, *fptr); copiers_p[i] = new StringFITSFieldCopier(rptr, fptr); } break; case TpArrayBool: { RORecordFieldPtr > *rptr = new RORecordFieldPtr >(row_p, i); FitsField *fptr = new FitsField(fieldSizes[i]); bintable_p->bind(whichField, *fptr); if (whichTdim >= 0) { FitsField *tdirptr = new FitsField(variableShapes.asString(description.name(i)).length()); bintable_p->bind(whichTdim, *tdirptr); copiers_p[i] = new VariableArrayFITSFieldCopier (rptr, fptr, tdirptr); } else { copiers_p[i] = new ArrayFITSFieldCopier (rptr, fptr); } } break; case TpArrayUChar: { RORecordFieldPtr > *rptr = new RORecordFieldPtr >(row_p, i); FitsField *fptr = new FitsField(fieldSizes[i]); bintable_p->bind(whichField, *fptr); if (whichTdim >= 0) { FitsField *tdirptr = new FitsField(variableShapes.asString(description.name(i)).length()); bintable_p->bind(whichTdim, *tdirptr); copiers_p[i] = new VariableArrayFITSFieldCopier (rptr, fptr, tdirptr); } else { copiers_p[i] = new ArrayFITSFieldCopier (rptr, fptr); } } break; case TpArrayShort: { RORecordFieldPtr > *rptr = new RORecordFieldPtr >(row_p, i); FitsField *fptr = new FitsField(fieldSizes[i]); bintable_p->bind(whichField, *fptr); if (whichTdim >= 0) { FitsField *tdirptr = new FitsField(variableShapes.asString(description.name(i)).length()); bintable_p->bind(whichTdim, *tdirptr); copiers_p[i] = new VariableArrayFITSFieldCopier (rptr, fptr, tdirptr); } else { copiers_p[i] = new ArrayFITSFieldCopier (rptr, fptr); } } break; case TpArrayInt: { RORecordFieldPtr > *rptr = new RORecordFieldPtr >(row_p, i); FitsField *fptr = new FitsField(fieldSizes[i]); bintable_p->bind(whichField, *fptr); if (whichTdim >= 0) { FitsField *tdirptr = new FitsField(variableShapes.asString(description.name(i)).length()); bintable_p->bind(whichTdim, *tdirptr); copiers_p[i] = new VariableArrayFITSFieldCopier (rptr, fptr, tdirptr); } else { copiers_p[i] = new ArrayFITSFieldCopier (rptr, fptr); } } break; case TpArrayFloat: { RORecordFieldPtr > *rptr = new RORecordFieldPtr >(row_p, i); FitsField *fptr = new FitsField(fieldSizes[i]); bintable_p->bind(whichField, *fptr); if (whichTdim >= 0) { FitsField *tdirptr = new FitsField(variableShapes.asString(description.name(i)).length()); bintable_p->bind(whichTdim, *tdirptr); copiers_p[i] = new VariableArrayFITSFieldCopier (rptr, fptr, tdirptr); } else { copiers_p[i] = new ArrayFITSFieldCopier (rptr, fptr); } } break; case TpArrayDouble: { RORecordFieldPtr > *rptr = new RORecordFieldPtr >(row_p, i); FitsField *fptr = new FitsField(fieldSizes[i]); bintable_p->bind(whichField, *fptr); if (whichTdim >= 0) { FitsField *tdirptr = new FitsField(variableShapes.asString(description.name(i)).length()); bintable_p->bind(whichTdim, *tdirptr); copiers_p[i] = new VariableArrayFITSFieldCopier (rptr, fptr, tdirptr); } else { copiers_p[i] = new ArrayFITSFieldCopier (rptr, fptr); } } break; case TpArrayComplex: { RORecordFieldPtr > *rptr = new RORecordFieldPtr >(row_p, i); FitsField *fptr = new FitsField(fieldSizes[i]); bintable_p->bind(whichField, *fptr); if (whichTdim >= 0) { FitsField *tdirptr = new FitsField(variableShapes.asString(description.name(i)).length()); bintable_p->bind(whichTdim, *tdirptr); copiers_p[i] = new VariableArrayFITSFieldCopier (rptr, fptr, tdirptr); } else { copiers_p[i] = new ArrayFITSFieldCopier (rptr, fptr); } } break; case TpArrayDComplex: { RORecordFieldPtr > *rptr = new RORecordFieldPtr >(row_p, i); FitsField *fptr = new FitsField(fieldSizes[i]); bintable_p->bind(whichField, *fptr); if (whichTdim >= 0) { FitsField *tdirptr = new FitsField(variableShapes.asString(description.name(i)).length()); bintable_p->bind(whichTdim, *tdirptr); copiers_p[i] = new VariableArrayFITSFieldCopier (rptr, fptr, tdirptr); } else { copiers_p[i] = new ArrayFITSFieldCopier (rptr, fptr); } } break; default: AlwaysAssert(0, AipsError); } AlwaysAssert(copiers_p[i], AipsError); } } FITSTableWriter::~FITSTableWriter() { if (delete_writer_p) { delete writer_p; } uInt nfields = row_p.description().nfields(); for (uInt i=0; iset_next(1); for (uInt i=0; icopyToFITS(); } bintable_p->write(*writer_p); } FitsOutput *FITSTableWriter::makeWriter(const String &fileName) { const char *name = Path(fileName).expandedName().chars(); FitsOutput *file = new FitsOutput(name, FITS::Disk); FitsKeywordList st; st.mk(FITS::SIMPLE,True,"Standard FITS format"); st.mk(FITS::BITPIX,8,"Character Information"); st.mk(FITS::NAXIS,0,"No image data array present"); st.mk(FITS::EXTEND,True,"Extension exists"); st.spaces(); st.comment("The first data is in the HDU following this one"); st.spaces(); st.end(); PrimaryArray hdu1(st); hdu1.write_hdr(*file); return file; } } //# NAMESPACE CASACORE - END casacore-3.7.1/fits/FITS/FITSTable3.cc000066400000000000000000000227241476623553700171540ustar00rootroot00000000000000//# FITSTable.h: Simplified interface to FITS tables with Casacore Look and Feel. //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN FITSGroupWriter::FITSGroupWriter(const String &fileName, const RecordDesc &description, uInt nrows, const Record &extraKeywords, Bool freeOutput) : delete_writer_p(freeOutput), writer_p(0), nrows_written_p(0), nrows_total_p(nrows), group_p(0), error_count_p(0) { LogIO log(LogOrigin("FITSGroupWriter", "FITSGroupWriter", WHERE)); // Verify the description before doing anything else const uInt nfields = description.nfields(); Int arrayField = -1; uInt i; for (i=0; i(kw); AlwaysAssert(group_p, AipsError); check_error("creating random groups from keywords"); group_p->write_hdr(*writer_p); check_error("writing header"); } FITSGroupWriter::~FITSGroupWriter() { check_error("closing file"); if (nrows_written_p < nrows_total_p) { LogIO log(LogOrigin("FITSGroupWriter", "~FITSGroupWriter", WHERE)); log << LogIO::SEVERE << nrows_total_p << " rows must be written, only " << nrows_written_p << " have been." << endl << "Not enough rows were written, repeating the final row" << LogIO::POST; for (uInt i=nrows_written_p; i tmp; if (nrows_written_p >= nrows_total_p) { LogIO log(LogOrigin("FITSGroupWriter", "write", WHERE)); log << LogIO::SEVERE << "You've already written all the rows!!" << LogIO::POST; return; } uInt nfields = row_p.nfields(); // This could be sped up by keeping external copies of the field pointers Int param = 0; for (uInt i=0; istore(ptr); check_error("setting group array"); tmp.putStorage(ptr, deleteIt); } else { // A random "parameter" Float tmp2; row_p.get(i, tmp2); group_p->rawparm(param) = tmp2; check_error("setting group parameter"); param++; } } group_p->write(*writer_p); check_error("error writing row"); nrows_written_p++; } void FITSGroupWriter::check_error(const char *extra_info) { static LogOrigin OR("FITSGroupWriter", ""); static LogMessage msg(OR, LogMessage::SEVERE); if (group_p) { HeaderDataUnit::HDUErrs err = HeaderDataUnit::HDUErrs(group_p->err()); if (err != HeaderDataUnit::OK) { ostringstream os; os << "Random Groups error at row " << nrows_written_p << " "; switch (err) { case HeaderDataUnit::NOMEM: os << "(NOMEM)"; break; case HeaderDataUnit::MISSKEY: os << "(MISSKEY)"; break; case HeaderDataUnit::BADBITPIX: os << "(BADBITPIX)"; break; case HeaderDataUnit::NOAXISN: os << "(NOAXISN)"; break; case HeaderDataUnit::NOPCOUNT: os << "(NOPCOUNT)"; break; case HeaderDataUnit::NOGCOUNT: os << "(NOGCOUNT)"; break; case HeaderDataUnit::BADPCOUNT: os << "(BADPCOUNT)"; break; case HeaderDataUnit::BADGCOUNT: os << "(BADGCOUNT)"; break; case HeaderDataUnit::NOGROUPS: os << "(NOGROUPS)"; break; case HeaderDataUnit::BADNAXIS: os << "(BADNAXIS)"; break; case HeaderDataUnit::BADREC: os << "(BADREC)"; break; case HeaderDataUnit::BADTYPE: os << "(BADTYPE)"; break; case HeaderDataUnit::BADRULES: os << "(BADRULES)"; break; case HeaderDataUnit::BADSIZE: os << "(BADSIZE)"; break; case HeaderDataUnit::BADOPER: os << "(BADOPER)"; break; case HeaderDataUnit::BADCONV: os << "(BADCONV)"; break; case HeaderDataUnit::BADIO: os << "(BADIO)"; break; default: os << "(unknown error)"; } if (extra_info) { os << ". Error occured while " << extra_info << ".\n"; } msg.message(os); LogSink::postGlobally(msg); error_count_p++; } } if (writer_p) { FitsIO::FitsErrs err = (FitsIO::FitsErrs(writer_p->err())); if (err != FitsIO::OK) { ostringstream os; os << "I/O error at row " << nrows_written_p << " "; switch(err) { case FitsIO::IOERR: os << "(IOERR)"; break; case FitsIO::MISSKEY: os << "(MISSKEY)"; break; case FitsIO::BADBEGIN: os << "(BADBEGIN)"; break; case FitsIO::EMPTYFILE: os << "(EMPTYFILE)"; break; case FitsIO::NOPRIMARY: os << "(NOPRIMARY)"; break; case FitsIO::BADOPER: os << "(BADOPER)"; break; case FitsIO::MEMERR: os << "(MEMERR)"; break; case FitsIO::BADBITPIX: os << "(BADBITPIX)"; break; case FitsIO::NOAXISN: os << "(NOAXISN)"; break; case FitsIO::NOPCOUNT: os << "(NOPCOUNT)"; break; case FitsIO::NOGCOUNT: os << "(NOGCOUNT)"; break; case FitsIO::BADPCOUNT: os << "(BADPCOUNT)"; break; case FitsIO::BADGCOUNT: os << "(BADGCOUNT)"; break; case FitsIO::NOGROUPS: os << "(NOGROUPS)"; break; case FitsIO::BADNAXIS: os << "(BADNAXIS)"; break; case FitsIO::BADPRIMARY: os << "(BADPRIMARY)"; break; case FitsIO::BADSIZE: os << "(BADSIZE)"; break; case FitsIO::HDUERR: os << "(HDUERR)"; break; default: os << "(unknown error)"; } msg.message(os); LogSink::postGlobally(msg); error_count_p++; } } if (error_count_p > 5) { msg.message("More than 5 errors encountered! Throwing an exception"); LogSink::postGloballyThenThrow(msg); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/fits/FITS/FITSTimedTable.cc000066400000000000000000000233431476623553700200520ustar00rootroot00000000000000//# FITSTimedTable.cc: A Table with a time column //# Copyright (C) 1995,1996,1997,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN FITSTimedTable::FITSTimedTable() : atStart_p(True), hasChanged_p(False), changePending_p(False), table_p(0), row_now_p(0), row_next_p(0), how_past_end_p(0), timeColumn_p(0) { rowDesc_p.addField("Time", TpDouble); row_now_p = new Record(rowDesc_p); row_next_p = new Record(rowDesc_p); AlwaysAssert(row_now_p && row_next_p, AipsError); RecordFieldPtr time_now(*row_now_p,"Time"); *time_now = 0.0; RecordFieldPtr time_next(*row_next_p,"Time"); *time_next = 0.0; time_now_p.attachToRecord(*row_now_p, 0); time_next_p.attachToRecord(*row_next_p, 0); ok_p = True; } FITSTimedTable::FITSTimedTable(FITSTabular *originalTable, uInt whichColumnIsTime) : atStart_p(True), hasChanged_p(False), changePending_p(False), table_p(originalTable), row_now_p(0), row_next_p(0), rowDesc_p(table_p->description()), how_past_end_p(0), timeColumn_p(whichColumnIsTime) { AlwaysAssert(table_p, AipsError); if (!table_p->isValid()) { // as if this had been created with the default constructor table_p = 0; RecordDesc tmp; rowDesc_p = tmp; rowDesc_p.addField("Time", TpDouble); row_now_p = new Record(rowDesc_p); row_next_p = new Record(rowDesc_p); AlwaysAssert(row_now_p && row_next_p, AipsError); RecordFieldPtr time_now(*row_now_p,"Time"); *time_now = 0.0; RecordFieldPtr time_next(*row_next_p,"Time"); *time_next = 0.0; time_now_p.attachToRecord(*row_now_p, 0); time_next_p.attachToRecord(*row_next_p, 0); ok_p = True; timeColumn_p = 0; } else { initNowRecord(rowDesc_p); *row_now_p = table_p->currentRow(); table_p->next(); if (table_p->hasChanged()) { changePending_p = True; table_p->resetChangedFlag(); } initNextRecord(table_p->description()); *row_next_p = table_p->currentRow(); } } FITSTimedTable::~FITSTimedTable() { if (row_now_p) delete row_now_p; row_now_p = 0; if (row_next_p) delete row_next_p; row_next_p = 0; } Bool FITSTimedTable::isValid() const { if (!table_p) return True; return table_p->isValid(); } const TableRecord &FITSTimedTable::keywords() const { if (!table_p) return dummyKeywords; return table_p->keywords(); } const Record &FITSTimedTable::units() const { if (!table_p) return dummyUnits; return table_p->units(); } const Record &FITSTimedTable::displayFormats() const { if (!table_p) return dummyDisps; return table_p->displayFormats(); } const Record &FITSTimedTable::nulls() const { if (!table_p) return dummyNulls; return table_p->nulls(); } const RecordDesc &FITSTimedTable::description() const { return rowDesc_p; } Bool FITSTimedTable::pastEnd() const { // the constant version can not try and resync with underlying table return (!table_p || table_p->pastEnd()); } Bool FITSTimedTable::pastEnd() { // if how_past_end_p indicates we've been past the end // but table_p->pastEnd() is False, resyncronize with table if (!table_p) return True; if (how_past_end_p && !table_p->pastEnd()) { how_past_end_p = 0; *row_now_p = table_p->currentRow(); table_p->next(); if (table_p->hasChanged()) { rowDesc_p = table_p->description(); initNowRecord(rowDesc_p); initNextRecord(rowDesc_p); *row_now_p = table_p->currentRow(); hasChanged_p = True; changePending_p = False; table_p->resetChangedFlag(); } *row_next_p = table_p->currentRow(); } return (how_past_end_p > 1); } void FITSTimedTable::next() { if (!table_p) return; if (changePending_p) { rowDesc_p = table_p->description(); initNowRecord(rowDesc_p); changePending_p = False; hasChanged_p = True; } *row_now_p = table_p->currentRow(); // String curName = name(); table_p->next(); if (table_p->hasChanged()) { initNextRecord(table_p->description()); changePending_p = True; table_p->resetChangedFlag(); } *row_next_p = table_p->currentRow(); // if nextTime() < currentTime() advance until that isn't true or // end of file is reached // Int count = 0; while (!table_p->pastEnd() && (nextTime() < currentTime())) { table_p->next(); if (table_p->hasChanged()) { initNextRecord(table_p->description()); changePending_p = True; table_p->resetChangedFlag(); } *row_next_p = table_p->currentRow(); // if (curName != name()) { // cout << "File changed from " << curName << " to " << name() << endl; // curName = name(); // } // count++; } // if (count != 0) cout << "Skipped " << count << " rows" << endl; if (nextTime() < currentTime()) { *row_next_p = *row_now_p; how_past_end_p++; // cout << "Still bad time" << endl; } else if (table_p->pastEnd()) { if (how_past_end_p == 0) { *row_now_p = table_p->currentRow(); } how_past_end_p++; } atStart_p = False; ok_p = True; } const Record &FITSTimedTable::currentRow() const { return *row_now_p; } Record &FITSTimedTable::currentRow() { return *row_now_p; } Double FITSTimedTable::currentTime() const { return *time_now_p; } Double FITSTimedTable::nextTime() { if (pastEnd() || how_past_end_p > 0) { return 1.0e+30; } return *time_next_p; } void FITSTimedTable::setTime(Double time) { if (!table_p) return; // time MUST be >= currentTime() unless this is the first row // in which case simply give the current row // and set ok flag to false if (time < currentTime()) { if (atStart_p) { ok_p = False; return; } else { // just write out error message to cerr for now cerr.precision(12); cerr << "File : " << name() << endl; cerr << "requested time " << time << endl; cerr << "currentTime() " << currentTime() << endl; throw(AipsError("FITSTimedTable::setTime(Double time) - time is " "< currentTime() - can not currently back up in time." "File : " + name())); } } ok_p = True; atStart_p = False; // step through file until end is reached or // time is >= currentTime and < nextTime while (! pastEnd() && time >= nextTime()) next(); // simply return if pastEnd() or time == currentTime() if (pastEnd() || time == currentTime()) return; Double fraction = (time - currentTime()) / (nextTime() - currentTime()); // for now, we just interpolate TpFloat, TpDouble and TpArrayFloat and TpArrayDouble fields // But only when fraction is greater than 0.001 // This clearly needs a better solution // watch for columns in row_now that are missing in row_next uInt nextNr; if (fraction > 0.001) { for (uInt i=0;ifieldNumber(rowDesc_p.name(i)); } else { nextNr = i; } switch (rowDesc_p.type(i)) { case TpFloat: { RecordFieldPtr currField(*row_now_p, i); RecordFieldPtr nextField(*row_next_p, nextNr); *currField = (*nextField-*currField) * fraction + *currField; } break; case TpDouble: { RecordFieldPtr currField(*row_now_p, i); RecordFieldPtr nextField(*row_next_p, nextNr); *currField = (*nextField-*currField) * fraction + *currField; } break; case TpArrayFloat: { RecordFieldPtr > currField(*row_now_p, i); RecordFieldPtr > nextField(*row_next_p, nextNr); *currField = (*nextField-*currField) * Float(fraction) + *currField; } break; case TpArrayDouble: { RecordFieldPtr > currField(*row_now_p, i); RecordFieldPtr > nextField(*row_next_p, nextNr); *currField = (*nextField-*currField) * fraction + *currField; } break; default: // do nothing for all other types (including Complex and DComplex) ; break; } } } } void FITSTimedTable::initNowRecord(const RecordDesc& desc) { if (row_now_p) delete row_now_p; row_now_p = new Record(desc); AlwaysAssert(row_now_p, AipsError); time_now_p.attachToRecord(*row_now_p, timeColumn_p); } void FITSTimedTable::initNextRecord(const RecordDesc& desc) { if (row_next_p) delete row_next_p; row_next_p = new Record(desc); AlwaysAssert(row_next_p, AipsError); time_next_p.attachToRecord(*row_next_p, timeColumn_p); } } //# NAMESPACE CASACORE - END casacore-3.7.1/fits/FITS/FITSTimedTable.h000066400000000000000000000114011476623553700177040ustar00rootroot00000000000000//# FITSTimedTable.h: A Table with a time column //# Copyright (C) 1995,1996,1997 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef FITS_FITSTIMEDTABLE_H #define FITS_FITSTIMEDTABLE_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // // // // // //
      • // // // // // // // FITSTimedTable is used to look at FITS tables which have a time column. In // particular, it peeks ahead, and knows the time of the currentRow and of the // nextRow. // // It is constructed with a pointer to any FITSTabular. Presently, no memory // management is imposed to ensure that the pointer remains valid. // // // // // // // // // //
      • // class FITSTimedTable : public FITSTabular { public: // This is not connected to any data, isValid always returns True, // keywords and description return the default versions // hasChanged returns False, name returns an empty string // pastEnd returns False and next does nothing. // setTime does nothing, currentRow returns an empty record // and currentTime returns 0.0 // and ok returns True and nextTime returns 0.0 FITSTimedTable(); // Note, originalTable cannot be destructed, reopened, ...,during the // lifetime of this object. FITSTimedTable(FITSTabular *originalTable, uInt whichColumnIsTime=0); ~FITSTimedTable(); virtual Bool isValid() const; virtual const TableRecord &keywords() const; virtual const RecordDesc &description() const; virtual const Record &units() const; virtual const Record &displayFormats() const; virtual const Record &nulls() const; virtual Bool hasChanged() const { return hasChanged_p;} virtual void resetChangedFlag() { hasChanged_p = False; } virtual const String &name() const { return table_p->name(); } virtual Bool pastEnd() const; virtual Bool pastEnd(); virtual void next(); // interpolate to the desired time which must be >= the currentTime() // This uses a linear interpolation between adjacent floating point values. // Non-floating point values are NOT interpolated but have the value of the // most recent actual row. On the last row of the table, not interpolation // is done. virtual void setTime(Double time); virtual const Record ¤tRow() const; virtual Record ¤tRow(); // What is the time of the current row? Double currentTime() const; // this is True if the last setTime() finished as expected // It is False only if the requested time is before the current time // and the timed table as just been opened Bool ok() const { return ok_p;} // What will the time of the next row be? Returns a very large number if // it is past the end of the table. Double nextTime(); private: Bool atStart_p; Bool ok_p; Bool hasChanged_p; Bool changePending_p; FITSTabular *table_p; Record *row_now_p; Record *row_next_p; RORecordFieldPtr time_now_p; RORecordFieldPtr time_next_p; RecordDesc rowDesc_p; Int how_past_end_p; uInt timeColumn_p; TableRecord dummyKeywords; Record dummyUnits; Record dummyDisps; Record dummyNulls; void initNowRecord(const RecordDesc& desc); void initNextRecord(const RecordDesc& desc); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/fits/FITS/SDFITSTable.cc000066400000000000000000000117061476623553700173160ustar00rootroot00000000000000//# SDFITSTable.cc: defines SDFITSTable, a FITSTable following the SD convention //# Copyright (C) 1997,1998,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Block SDFITSTable::kwNames; void SDFITSTable::init_kwNames() { if (kwNames.nelements() != (NUM_CORE_KEYWORDS+1)) { kwNames.resize(NUM_CORE_KEYWORDS+1); kwNames[OBJECT] = FITS::ResWord.aname(FITS::OBJECT); kwNames[TELESCOP] = FITS::ResWord.aname(FITS::TELESCOP); kwNames[BANDWID] = "BANDWID"; kwNames[DATEOBS] = FITS::ResWord.aname(FITS::DATE_OBS); kwNames[EXPOSURE] = "EXPOSURE"; kwNames[TSYS] = "TSYS"; kwNames[UNKNOWN] = ""; } } SDFITSTable::CoreKeyword SDFITSTable::coreKeyword(const String& name) { init_kwNames(); uInt i = 0; while (i < NUM_CORE_KEYWORDS && kwNames[i] != name) { i++; } return CoreKeyword(i); } String SDFITSTable::coreKeywordName(CoreKeyword kw) { init_kwNames(); return kwNames[kw]; } SDFITSTable::SDFITSTable(const String& fileName, uInt whichHDU) : FITSTable(fileName, whichHDU), isSDFITS_p(False) { // check for valid (core) SDFITS keywords, move keywords to columns sdfits_shuffle(); } SDFITSTable::~SDFITSTable() { ; } Bool SDFITSTable::reopen(const String &fileName) { Bool result = FITSTable::reopen(fileName); if (result) sdfits_shuffle(); return result; } Bool SDFITSTable::isSDFitsColumn(const String& name) { Bool result; // if name is not reserved, return True if (!FITS::ResWord.isreserved(name.chars(), name.length())) { result = True; } else if (name != FITS::ResWord.aname(FITS::COMMENT) && name != FITS::ResWord.aname(FITS::DATAMAX) && name != FITS::ResWord.aname(FITS::DATAMIN) && name != FITS::ResWord.aname(FITS::EXTLEVEL) && name != FITS::ResWord.aname(FITS::EXTNAME) && name != FITS::ResWord.aname(FITS::EXTVER) && name != FITS::ResWord.aname(FITS::HISTORY) && name != FITS::ResWord.aname(FITS::REFERENC)) { // all of the above might reasonably be expected to be in // a FITS table as keywords which should remain keywords // Other (e.g. BITPIX, TFIELDS, etc) which describe the // table, are removed by FITSTable. Everything else // is a keyword which should be treated as a virtual column. // DATAMAX and DATAMIN above, when they appear as keywords // in an sdfits table, refer to the entire table and hence // should remain as keywords and not virtual columns. // When they appear as true column, then they obviously // should remain true columns. result = True; } else { result = False; } return result; } void SDFITSTable::sdfits_shuffle() { // if its already not valid, no sense going on if (isValid()) { // shift keywords to row Vector virtCols(keywords().nfields()); uInt virtCount = 0; uInt i; for (i=0;i #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // SDFITSTable is a FITSTable which follows the Single Dish FITS Convention. // // // // // //
      • FITSTable // // // // SDFITSTable is derived from FITSTable. It contains additional // checks and behaviour appropriate to the Single Dish FITS Convention // hence this is a Single Dish FITS Table, or SDFITSTable. // // // // This class behaves much like FITSTable. It additionally verifies // that the indicated HDU in the input FITS file follows the SDFITS // convention (it has all of the required columns) and it treats // keywords as virtual columns when appropriate. These virtual // columns will appear as fields in the currentRecord and description // and will NOT appear in the keywords. // // // // // // // It was useful to encapsulate this behaviour in a class so that // the checks on a valid SDFITS table and the treatment of keywords // as virtual columns would not need to appear everywhere it might // be used. // // // //
      • //
      • // // // //
      • everything // class SDFITSTable : public FITSTable { public: // the core keywords, UNKNOWN is not a core keyword, // NUM_CORE_KEYWORDS is a place holder enum CoreKeyword { OBJECT, TELESCOP, BANDWID, DATEOBS, EXPOSURE, TSYS, NUM_CORE_KEYWORDS, UNKNOWN=NUM_CORE_KEYWORDS }; // construct from a file SDFITSTable(const String &fileName, uInt whichHDU=1); // The destructor ~SDFITSTable(); // Attach this SDFITSTable to a new file name, same HDU# as at open time virtual Bool reopen(const String &fileName); // is this a valid SDFITS file virtual Bool isSDFITS() const { return isSDFITS_p;} // translate to/from core keyword names to enumeration static CoreKeyword coreKeyword(const String& name); static String coreKeywordName(CoreKeyword kw); private: Bool isSDFITS_p; // block of core keyword names static Block kwNames; // kwNames initialization function static void init_kwNames(); // check to see if the named keyword should // be turned into a column, all non-reserved keywords will // always be turned into a column. static Bool isSDFitsColumn(const String& name); // the array of keyword names // the regular FITSTable::reopen does nearly everything fine, // this function moves stuff out of the keywords and into the // output record as appropriate void sdfits_shuffle(); // undefined an inaccessible SDFITSTable(); SDFITSTable(const SDFITSTable &); SDFITSTable &operator=(const SDFITSTable &); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/fits/FITS/blockio.cc000066400000000000000000000344041476623553700167740ustar00rootroot00000000000000//# blockio.cc: //# Copyright (C) 1993,1994,1995,1996,1999,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // # include # include # include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //====================================================================================== void BlockIO::errmsg(IOErrs e, const char *s) { static char msgstring[180]; // storage for composing error messages ostringstream msgline; msgline << "BlockIO: "; if (m_filename == 0 || *m_filename == '\0') msgline << "File Descriptor " << m_fd; else msgline << "File " << m_filename; msgline << " Physical record " << m_block_no << " logical record " << m_rec_no << " --\n\t" << s << endl; m_err_status = e; // all BlockIO messages are SEVERE strncpy(msgstring, msgline.str().c_str(), sizeof(msgstring)-1); m_errfn(msgstring, FITSError::SEVERE); } //======================================================================================== // wrap the constructor with cfitsio of NASA. GYL BlockIO::BlockIO(const char *f, int o, int r, int n, FITSErrorHandler errhandler) : m_filename(0), m_options(o), m_recsize(r), m_nrec(n), m_blocksize(r * n), m_errfn(errhandler), m_err_status(OK), m_fd(-1), m_buffer(0), m_block_no(0), m_rec_no(0), m_current(0), m_iosize(0) { if (f == 0 || (*f == '\0')) { errmsg(NOSUCHFILE,"No filename was specified"); return; } if ((m_filename = new char [strlen(f) + 1]) == 0) { errmsg(NOMEM,"Cannot allocate memory"); return; } if ((m_buffer = new char [m_blocksize]) == 0) { errmsg(NOMEM,"Cannot allocate memory"); delete [] m_filename; m_filename = 0; return; } strcpy(m_filename,f); // using cfitsio of NASA to open fits file for writting and reading. int l_status, iomode; File myfile(m_filename); if (myfile.isReadable()) { // the file name does not use fits extensions l_status = OPEN_DISK_FILE; } else { l_status = 0; } if (m_options & O_CREAT){ if (fits_create_file(&m_fptr, m_filename, &l_status)){ /*create FITS file*/ fits_report_error(stderr, l_status); /* print error report */ errmsg(OPENERR,"File exists already!"); delete [] m_filename; delete [] m_buffer; m_filename = 0; m_buffer = 0; }else{ if( ((m_fptr->Fptr)->io_pos) != 0 ){ (m_fptr->Fptr)->io_pos = 0; } } }else{ iomode = READONLY; // using cfitsio function to open a file for reading. if ( fits_open_file(&m_fptr, m_filename, iomode, &l_status) ){ fits_report_error(stderr, l_status); /* print error report */ errmsg(OPENERR,"Open file error!"); delete [] m_filename; delete [] m_buffer; m_filename = 0; m_buffer = 0; }else{ // fits_open_file() puts the bytepos at the beginning of the data unit, so move it back to beginning of HDU. if( m_fptr == 0 ) cout << "[BlockIO::BlockIO()] m_fptr is null, open file failed." << endl; // cout<<"[BlockIO::BlockIO()] filesize = "<< (m_fptr->Fptr)->filesize << endl; if( ((m_fptr->Fptr)->bytepos) != 0 ){ if(ffmbyt(m_fptr, 0, REPORT_EOF, &l_status)!=0 ){ errmsg(OPENERR,"bytepos setting error!"); } } } } } //====================================================================================== // To see what to do with this constructor. we need a m_fptr for io operation. // Can we get the file name from the file descriptor fd? No. However, this constructor // is only used for standard io. So we do not have to worry about it. BlockIO::BlockIO(int f, int r, int n, FITSErrorHandler errhandler) : m_filename(0), m_options(0), m_recsize(r), m_nrec(n), m_blocksize(n * r), m_errfn(errhandler), m_err_status(OK), m_fd(f), m_block_no(0), m_rec_no(0), m_current(0), m_iosize(0) { if ((m_buffer = new char [m_blocksize]) == 0) { errmsg(NOMEM,"Cannot allocate memory"); return; } } //====================================================================================== // close fits file with cfitsio function BlockIO::~BlockIO() { if (m_filename != 0 && strlen(m_filename) > 0) { // For writing node, fits_close_file() damages the output file! int l_status = 0; if( m_options == O_RDONLY ){ if( fits_close_file(m_fptr, &l_status)>0 ){ errmsg(CLOSEERR,"[~BlockIO()] Error closing file."); } }else{ // end of if( m_option ...). // fits_close_file() does not work in this case. So use our own close_file(). if(close_file( m_fptr, &l_status)){ errmsg(CLOSEERR,"[~BlockIO()] Error closing file"); } } delete [] m_filename; }// end of if( m_filename ! =0, ...). delete [] m_buffer; } //======================================================================================== // Reset the fitsfile pointer void BlockIO::setfptr( fitsfile* ffp ){ int l_status = 0; if(close_file( m_fptr, &l_status)){ errmsg(CLOSEERR,"[BlockIO::setfptr()] Error closing file"); } m_fptr = ffp; } //======================================================================================== // Close the FITS file by calling the system dependent routine to physically // close the FITS file and free the memory. int BlockIO::close_file( fitsfile *fptr, int *status){ if (!fptr) return(*status = NULL_INPUT_PTR); else if ((fptr->Fptr)->validcode != VALIDSTRUC) // check for magic value return(*status = BAD_FILEPTR); ((fptr->Fptr)->open_count)--; // decrement usage counter if ((fptr->Fptr)->open_count == 0) // if no other files use structure { if( ffflsh(fptr, TRUE, status) ){ errmsg(CLOSEERR,"[BlockIO::close_file()] Failed to flush the file: (ffflsh)" ); cout<< (fptr->Fptr)->filename << endl; } if( file_close( (fptr->Fptr)->filehandle) ){ *status = FILE_NOT_CLOSED; // report if no previous error errmsg(CLOSEERR,"[BlockIO::close_file()] Failed to close the file: (ffclos)" ); cout<< (fptr->Fptr)->filename << endl; } fits_clear_Fptr( fptr->Fptr, status); // clear Fptr address #if CFITSIO_VERSION_MAJOR>=3 && CFITSIO_VERSION_MINOR>=181 // iobuffer was added with version 3.181... // cfitsio 3.03-3.14 do not have this... free((fptr->Fptr)->iobuffer); // free memory for I/O buffers #endif free((fptr->Fptr)->headstart); // free memory for headstart array free((fptr->Fptr)->filename); // free memory for the filename (fptr->Fptr)->filename = 0; (fptr->Fptr)->validcode = 0; // magic value to indicate invalid fptr free(fptr->Fptr); // free memory for the FITS file structure free(fptr); // free memory for the FITS file structure }else{ // just flush the buffers, don't disassociate them int zerostatus = 0; if (*status > 0) ffflsh(fptr, FALSE, &zerostatus); else ffflsh(fptr, FALSE, status); free(fptr); // free memory for the FITS file structure } return(*status); } //========================================================================================== BlockInput::~BlockInput() { } //========================================================================================== // wrap the read() method with cfitsio of NASA. GYL char *BlockInput::read() { m_current += m_recsize; if (m_current >= m_iosize) { int l_ntoread = m_blocksize; int l_status = 0; m_iosize = 0; // check that we do not exceed the end of the fits file OFF_T l_byte_left_in_file = (m_fptr->Fptr)->filesize - (m_fptr->Fptr)->bytepos; // if reached the end of file, return to caller with a NULL pointer. if( l_byte_left_in_file == 0 ){ //cout << "No more data in file, return a NULL pointer." << endl; return(NULL); } if( OFF_T(m_blocksize) > l_byte_left_in_file ){ l_ntoread = m_recsize; // switch down to reading single records instead of blocks if( OFF_T(m_recsize) > l_byte_left_in_file ){ // ignore remainder if not multiple of record size cout << "WARNING: fits blockio ignoring last " << l_byte_left_in_file << " bytes." << endl; return(NULL); } } // using the cfitsio function to read m_blocksize bytes from the file // pointed to by m_fptr from where the file position indicator currently at. OFF_T bytepost = (m_fptr->Fptr)->bytepos; // cout << "m_iosize, m_blocksize, m_recsize, m_iosize % m_recsize, bytepost, l_ntoread " // << m_iosize << " " << m_blocksize << " " << m_recsize << " " << m_iosize % m_recsize // << " " << bytepost << " " << l_ntoread << endl; ffgbyt( m_fptr, // I - FITS file pointer l_ntoread, // I - number of bytes to read m_buffer, // O - buffer to read into &l_status); // IO - error status if( l_status ){ fits_report_error(stderr, l_status); /* print error report */ return(0); } // try to move on to the next record bytepost = bytepost + OFF_T(l_ntoread); l_byte_left_in_file = (m_fptr->Fptr)->filesize - bytepost; if( l_byte_left_in_file >= OFF_T(m_recsize) ){ if(ffmbyt(m_fptr, bytepost, REPORT_EOF, &l_status)>0 ){ errmsg(READERR,"bytepos setting error!"); } }else{ (m_fptr->Fptr)->bytepos = bytepost; } m_block_no++; m_iosize = l_ntoread; // always a multiple of the record size m_err_status = OK; m_current = 0; } m_rec_no++; return &m_buffer[m_current]; } //================================================================================ // skip the next n logical record and then read the next one char *BlockInput::skip(int n) { while (n--){ read(); } return read(); } //===================================================================================================== // flush both io buffer of CFITSIO and m_buffer void BlockOutput::flush_buffer(){ int l_status = 0; // flush the io buffer of CFITSIO. // After each call to ffpbyt(), one can call ffflsh() only once. Since we did it after each callto // ffpbyt() already, here we cannot call ffflsh() again! //if(ffflsh(m_fptr, TRUE, &l_status)){ errmsg(WRITEERR,"[flush_buffer()] Error flushing buffer!"); } // flush the m_buffer if (m_current > 0) { // see write() for how ffpbyt() works. //ffwrite( m_fptr->Fptr, long_current, m_buffer, &l_status); if ( ffpbyt( m_fptr, m_current, m_buffer, &l_status) ){ fits_report_error(stderr, l_status); // print error report errmsg(WRITEERR,"[BlockOutput::flush_buffer()] Error writing record"); }else{ //m_iosize = m_current; m_err_status = OK; } // ffpbyt() doesn't write the last record onto disk,instead it puts the last // record into a buffer. So we call ffflsh() to flush everything onto disk. Otherwise // something would go wrong! if(ffflsh(m_fptr, TRUE, &l_status)){ errmsg(WRITEERR,"[flush_buffer()] Error flushing buffer!"); } //if(ffflus(m_fptr, &l_status)){ errmsg(WRITEERR,"[flush_buffer()] Error flushing buffer!"); } m_block_no++; m_current = 0; } } //==================================================================================================== BlockOutput::~BlockOutput() { // flush the IO buffer of CFITSIO and m_buffer before destruction flush_buffer(); } //==================================================================================================== // attach a logical record to the fits file. addr must point to a logical record. int BlockOutput::write(char *addr) { memcpy(&m_buffer[m_current],addr,m_recsize); m_rec_no++; m_current += m_recsize; // if buffer is full, write it to disk if (m_current >= m_blocksize) { /* put (write) the buffer of bytes to the output FITS file, starting at the current file position. Write large blocks of data directly to disk; write smaller segments to intermediate IO buffers to improve efficiency. */ int l_status = 0; ffpbyt( m_fptr, /* I - FITS file pointer */ m_blocksize, /* I - number of bytes to write */ m_buffer, /* I - buffer containing the bytes to write */ &l_status); /* IO - error status */ m_block_no++; if ( l_status ){ errmsg(WRITEERR,"[BlockOutput::write()] Error writing record"); }else{ m_iosize = m_blocksize; m_err_status = OK; } m_current = m_current - m_blocksize; // it should be zero. } return (int)m_err_status; } //======================================================================================================== BlockInput::BlockInput(const char *f, int r, int n, FITSErrorHandler errhandler) : BlockIO(f,O_RDONLY,r,n,errhandler) { } BlockInput::BlockInput(int f, int r, int n, FITSErrorHandler errhandler) : BlockIO(f,r,n,errhandler) { } BlockOutput::BlockOutput(const char *f, int r, int n, FITSErrorHandler errhandler) : BlockIO(f,O_RDWR|O_CREAT|O_TRUNC,r,n,errhandler) { } // replaced O_WRONLY by O_RDWR in above line. BlockOutput::BlockOutput(int f, int r, int n, FITSErrorHandler errhandler) : BlockIO(f,r,n,errhandler) { } } //# NAMESPACE CASACORE - END casacore-3.7.1/fits/FITS/blockio.h000066400000000000000000000156641476623553700166450ustar00rootroot00000000000000//# blockio.h: //# Copyright (C) 1993,1994,1995,1996,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef FITS_BLOCKIO_H #define FITS_BLOCKIO_H //# Include this file first, because it may set LFS variables used by cfitsio. #include //# Make sure that cfitsio does not declare the wcs headers. extern "C"{ #include //# header file from cfitsio #include //# using core functions of cfitsio } #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //---------------------------------------------------------------------------- // // fixed-length blocked sequentual I/O base class // // BlockIO is a low level base class that implements fixed-length // blocked sequential I/O. Its derived classes, BlockInput and BlockOutput // are used by the FitsInput and FitsOutput classes. Users will hardly ever // need to use this class directly. // // //
      • ifdef kludges until OS dependent flags are developed // for the compilation system. // class BlockIO { public: // error return code enum IOErrs { OK, NOSUCHFILE, NOMEM, OPENERR, CLOSEERR, READERR, WRITEERR }; int err() const { return (int)m_err_status; } // number of physical blocks read/written int blockno() const { return m_block_no; } // reset the m_iosize data member void reset_iosize() { m_iosize = 0; } // get the total bytes of data in m_buffer int iosize() const { return m_iosize; } // get the current read position within m_buffer int current() const { return m_current; } // get m_buffer char* buffer() const { return m_buffer; } // number of logical records read/written int recno() const { return m_rec_no; } // name of file associated with I/O stream, if applicable const char *fname() const { return m_filename; } // fits_close_file() does not work for reasons that the file pointer does not have the // knowledge of chdu which were written with write_hdr() not write_***_hdr(). So create // our own close_file() method. int close_file( fitsfile *fptr, int *status); // file descriptor associated with I/O stream, if applicable int fdes() const { return m_fd; } // get the fitsfile pointer fitsfile *getfptr() const { return m_fptr; } void setfptr( fitsfile* ffp ); protected: // Construction can be done either from a filename with open options // or from a file descriptor. // // The remaining arguments are the the logical record size and number // of records that make up a physical record followed by the // output stream that is used to write error messages to. // BlockIO(const char *, int, int, int = 1, FITSErrorHandler errhandler = FITSError::defaultHandler); BlockIO(int, int, int = 1, FITSErrorHandler errhandler = FITSError::defaultHandler); virtual ~BlockIO(); // char *m_filename; // name of file int m_options; // options on open statement const int m_recsize; // size in bytes of a logical record const int m_nrec; // maximum number of logical records const int m_blocksize; // size in bytes of physical records FITSErrorHandler m_errfn; // FITS error handler function IOErrs m_err_status; // error number int m_fd; // file descriptor char *m_buffer; // the actual data buffer itself int m_block_no; // number of physical blocks read/written int m_rec_no; // number of logical records read/written int m_current; // offset to current logical record // size of record in buffer int m_iosize; // using fitsfile structure from cfitsio of NASA fitsfile *m_fptr; // set the error message and error number for later recovery void errmsg(IOErrs, const char *); }; // fixed-length blocked sequential input base class // //
      • BlockIO // class BlockInput : public BlockIO { public: // Construction can be done either from a filename or from // a file descriptor. // // The remaining arguments are the the logical record size and number // of records that make up a physical record followed by the // output stream that is used to write error messages to. // BlockInput(const char *, int, int = 1, FITSErrorHandler errhandler = FITSError::defaultHandler); BlockInput(int, int, int = 1, FITSErrorHandler errhandler = FITSError::defaultHandler); virtual ~BlockInput(); // // read the next logical record or first // skip N logical records and then read the next one. // (note it is not possible to skip a record without // reading a record). // these functions return a pointer to an // internal record. The user must make sure that // after destruction of this class no dangling pointers // are left. // // virtual char *read(); // read a physical block. virtual char *skip(int); // }; // fixed-length blocked sequential output base class // //
      • BlockIO // class BlockOutput : public BlockIO { public: // Construction can be done either from a filename or from // a file descriptor. // // The remaining arguments are the the logical record size and number // of records that make up a physical record followed by the // output stream that is used to write error messages to. // BlockOutput(const char *, int, int = 1, FITSErrorHandler errhandler = FITSError::defaultHandler); BlockOutput(int, int, int = 1, FITSErrorHandler errhandler = FITSError::defaultHandler); virtual ~BlockOutput(); void flush_buffer(); // // write the next logical record. The input must point // to a logical record virtual int write(char *); }; } //# NAMESPACE CASACORE - END # endif casacore-3.7.1/fits/FITS/fits.cc000066400000000000000000002360761476623553700163300ustar00rootroot00000000000000//# fits.cc: //# Copyright (C) 1993,1994,1995,1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Partial implementation of little endian code by Kris Huber //# (kris@helios.ece.usu.edu) # include # include # include # include # include # include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Discussion of Reserved FitsKeyword Table // // 1. The reserved name itself (name_) is not unique; there may // be more than one entry with the same reserved name. // 2. The combination of reserved name, the data type, and whether // it is indexed or not (name_, type_, isindexed_) is unique // within the table. // 3. The table is sorted by reserved name + type + isindexed. // Initialize the static data in ReservedFitsKeywordCollection const int ReservedFitsKeywordCollection::no_items = 56; const ReservedFitsKeyword ReservedFitsKeywordCollection::resword[56] = { // key aname namesize isindexed Section // | | | type | isessential in|NOST // \|/ \|/ \|/\|/ \|/ \|/ \|/ // ------ ------ - ------- ----- ----- ------------ /* 0 */ { FITS::USER_DEF, "", 0, FITS::NOVALUE, False, False }, /* 1 */ { FITS::AUTHOR, "AUTHOR", 6, FITS::STRING, False, False }, // 5.2.2.3 /* 2 */ { FITS::BITPIX, "BITPIX", 6, FITS::LONG, False, True }, // 5.2.1.1 /* 3 */ { FITS::BLANK, "BLANK", 5, FITS::LONG, False, False }, // 5.2.2.5 /* 4 */ { FITS::BLOCKED, "BLOCKED", 7, FITS::LOGICAL, False, False }, // 5.2.2.1 /* 5 */ { FITS::BSCALE, "BSCALE", 6, FITS::REAL, False, False }, // 5.2.2.5 /* 6 */ { FITS::BUNIT, "BUNIT", 5, FITS::STRING, False, False }, // 5.2.2.5 /* 7 */ { FITS::BZERO, "BZERO", 5, FITS::REAL, False, False }, // 5.2.2.5 /* 8 */ { FITS::CDELT, "CDELT", 5, FITS::REAL, True, False }, // 5.2.2.5 /* 9 */ { FITS::COMMENT, "COMMENT", 7, FITS::NOVALUE, False, False }, // 5.2.2.4 /* 10 */ { FITS::CROTA, "CROTA", 5, FITS::REAL, True, False }, // 5.2.2.5 /* 11 */ { FITS::CRPIX, "CRPIX", 5, FITS::REAL, True, False }, // 5.2.2.5 /* 12 */ { FITS::CRVAL, "CRVAL", 5, FITS::REAL, True, False }, // 5.2.2.5 /* 13 */ { FITS::CTYPE, "CTYPE", 5, FITS::STRING, True, False }, // 5.2.2.5 /* 14 */ { FITS::DATAMAX, "DATAMAX", 7, FITS::REAL, False, False }, // 5.2.2.5 /* 15 */ { FITS::DATAMIN, "DATAMIN", 7, FITS::REAL, False, False }, // 5.2.2.5 /* 16 */ { FITS::DATE, "DATE", 4, FITS::STRING, False, False }, // 5.2.2.1 /* 17 */ { FITS::DATE_OBS, "DATE-OBS", 8, FITS::STRING, False, False }, // 5.2.2.2 /* 18 */ { FITS::END, "END", 3, FITS::NOVALUE, False, True }, // 5.2.1.1 /* 19 */ { FITS::EPOCH, "EPOCH", 5, FITS::REAL, False, False }, // 5.2.2.2 /* 20 */ { FITS::EQUINOX, "EQUINOX", 7, FITS::REAL, False, False }, // 5.2.2.2 /* 21 */ { FITS::EXTEND, "EXTEND", 6, FITS::LOGICAL, False, True }, // 5.2.1.2 /* 22 */ { FITS::EXTLEVEL, "EXTLEVEL", 8, FITS::LONG, False, False }, // 5.2.2.6 /* 23 */ { FITS::EXTNAME, "EXTNAME", 7, FITS::STRING, False, False }, // 5.2.2.6 /* 24 */ { FITS::EXTVER, "EXTVER", 6, FITS::LONG, False, False }, // 5.2.2.6 /* 25 */ { FITS::GCOUNT, "GCOUNT", 6, FITS::LONG, False, True }, // 5.2.1.2 /* 26 */ { FITS::GROUPS, "GROUPS", 6, FITS::LOGICAL, False, True }, // 7.1.1.6 /* 27 */ { FITS::HISTORY, "HISTORY", 7, FITS::NOVALUE, False, False }, // 5.2.2.4 /* 28 */ { FITS::INSTRUME, "INSTRUME", 8, FITS::STRING, False, False }, // 5.2.2.2 /* 29 */ { FITS::NAXIS, "NAXIS", 5, FITS::LONG, False, True }, // 5.2.1.1 /* 30 */ { FITS::NAXIS, "NAXIS", 5, FITS::LONG, True, True }, // 5.2.1.1 /* 31 */ { FITS::OBJECT, "OBJECT", 6, FITS::STRING, False, False }, // 5.2.2.2 /* 32 */ { FITS::OBSERVER, "OBSERVER", 8, FITS::STRING, False, False }, // 5.2.2.2 /* 33 */ { FITS::ORIGIN, "ORIGIN", 6, FITS::STRING, False, False }, // 5.2.2.1 /* 34 */ { FITS::PCOUNT, "PCOUNT", 6, FITS::LONG, False, True }, // 5.2.1.2 /* 35 */ { FITS::PSCAL, "PSCAL", 5, FITS::REAL, True, False }, // 7.1.2.2 /* 36 */ { FITS::PTYPE, "PTYPE", 5, FITS::STRING, True, False }, // 7.1.2.1 /* 37 */ { FITS::PZERO_FITS, "PZERO", 5, FITS::REAL, True, False }, // 7.1.2.3 /* 38 */ { FITS::REFERENC, "REFERENC", 8, FITS::STRING, False, False }, // 5.2.2.3 /* 39 */ { FITS::SIMPLE, "SIMPLE", 6, FITS::LOGICAL, False, True }, // 5.2.1.1 /* 40 */ { FITS::SPACES, " ", 8, FITS::NOVALUE, False, False }, // 5.2.2.4 /* 41 */ { FITS::TBCOL, "TBCOL", 5, FITS::LONG, True, False }, // 8.1.1 /* 42 */ { FITS::TDIM, "TDIM", 4, FITS::STRING, True, False }, // A.4, A.9.1 /* 43 */ { FITS::TDISP, "TDISP", 5, FITS::STRING, True, False }, // A.4 /* 44 */ { FITS::TELESCOP, "TELESCOP", 8, FITS::STRING, False, False }, // 5.2.2.2 /* 45 */ { FITS::TFIELDS, "TFIELDS", 7, FITS::LONG, False, False }, // 8.1.1 /* 46 */ { FITS::TFORM, "TFORM", 5, FITS::STRING, True, False }, // 8.1.1 /* 47 */ { FITS::THEAP, "THEAP", 5, FITS::LONG, False, False }, // A.4, A.9.2 /* 48 */ { FITS::TNULL, "TNULL", 5, FITS::STRING, True, False }, // 8.1.2 /* 49 */ { FITS::TNULL, "TNULL", 5, FITS::LONG , True, False }, // A.4 /* 50 */ { FITS::TSCAL, "TSCAL", 5, FITS::REAL, True, False }, // 8.1.2 /* 51 */ { FITS::TTYPE, "TTYPE", 5, FITS::STRING, True, False }, // 8.1.2 /* 52 */ { FITS::TUNIT, "TUNIT", 5, FITS::STRING, True, False }, // 8.1.2 /* 53 */ { FITS::TZERO, "TZERO", 5, FITS::REAL, True, False }, // 8.1.2 /* 54 */ { FITS::XTENSION, "XTENSION", 8, FITS::STRING, False, True }, // 5.2.1.2 /* 55 */ { FITS::ERRWORD, "", 0, FITS::NOVALUE, False, False } // last }; const ReservedFitsKeyword & ReservedFitsKeywordCollection::user_def_item = resword[0]; const ReservedFitsKeyword & ReservedFitsKeywordCollection::error_item = resword[55]; const ReservedFitsKeyword & ReservedFitsKeywordCollection::end__item = resword[18]; const ReservedFitsKeyword & ReservedFitsKeywordCollection::spaces_item = resword[40]; const ReservedFitsKeyword & ReservedFitsKeywordCollection::comment_item = resword[9]; const ReservedFitsKeyword & ReservedFitsKeywordCollection::history_item = resword[27]; const int ReservedFitsKeywordCollection::resalpha[26] = { // A B C D E F G H I J K L M N O P Q R 1, 2, 8, 14, 18, 0, 25, 27, 28, 0, 0, 0, 0, 29, 31, 34, 0, 38, // S T U V W X Y Z 39, 41, 0, 0, 0, 54, 0, 0 }; // Initialize the static data in FITS double FITS::tenpowerD[309] = { 1.0, 1.0E1, 1.0E2, 1.0E3, 1.0E4, 1.0E5, 1.0E6, 1.0E7, 1.0E8, 1.0E9, 1.0E10, 1.0E11, 1.0E12, 1.0E13, 1.0E14, 1.0E15, 1.0E16, 1.0E17, 1.0E18, 1.0E19, 1.0E20, 1.0E21, 1.0E22, 1.0E23, 1.0E24, 1.0E25, 1.0E26, 1.0E27, 1.0E28, 1.0E29, 1.0E30, 1.0E31, 1.0E32, 1.0E33, 1.0E34, 1.0E35, 1.0E36, 1.0E37, 1.0E38, 1.0E39, 1.0E40, 1.0E41, 1.0E42, 1.0E43, 1.0E44, 1.0E45, 1.0E46, 1.0E47, 1.0E48, 1.0E49, 1.0E50, 1.0E51, 1.0E52, 1.0E53, 1.0E54, 1.0E55, 1.0E56, 1.0E57, 1.0E58, 1.0E59, 1.0E60, 1.0E61, 1.0E62, 1.0E63, 1.0E64, 1.0E65, 1.0E66, 1.0E67, 1.0E68, 1.0E69, 1.0E70, 1.0E71, 1.0E72, 1.0E73, 1.0E74, 1.0E75, 1.0E76, 1.0E77, 1.0E78, 1.0E79, 1.0E80, 1.0E81, 1.0E82, 1.0E83, 1.0E84, 1.0E85, 1.0E86, 1.0E87, 1.0E88, 1.0E89, 1.0E90, 1.0E91, 1.0E92, 1.0E93, 1.0E94, 1.0E95, 1.0E96, 1.0E97, 1.0E98, 1.0E99, 1.0E100, 1.0E101, 1.0E102, 1.0E103, 1.0E104, 1.0E105, 1.0E106, 1.0E107, 1.0E108, 1.0E109, 1.0E110, 1.0E111, 1.0E112, 1.0E113, 1.0E114, 1.0E115, 1.0E116, 1.0E117, 1.0E118, 1.0E119, 1.0E120, 1.0E121, 1.0E122, 1.0E123, 1.0E124, 1.0E125, 1.0E126, 1.0E127, 1.0E128, 1.0E129, 1.0E130, 1.0E131, 1.0E132, 1.0E133, 1.0E134, 1.0E135, 1.0E136, 1.0E137, 1.0E138, 1.0E139, 1.0E140, 1.0E141, 1.0E142, 1.0E143, 1.0E144, 1.0E145, 1.0E146, 1.0E147, 1.0E148, 1.0E149, 1.0E150, 1.0E151, 1.0E152, 1.0E153, 1.0E154, 1.0E155, 1.0E156, 1.0E157, 1.0E158, 1.0E159, 1.0E160, 1.0E161, 1.0E162, 1.0E163, 1.0E164, 1.0E165, 1.0E166, 1.0E167, 1.0E168, 1.0E169, 1.0E170, 1.0E171, 1.0E172, 1.0E173, 1.0E174, 1.0E175, 1.0E176, 1.0E177, 1.0E178, 1.0E179, 1.0E180, 1.0E181, 1.0E182, 1.0E183, 1.0E184, 1.0E185, 1.0E186, 1.0E187, 1.0E188, 1.0E189, 1.0E190, 1.0E191, 1.0E192, 1.0E193, 1.0E194, 1.0E195, 1.0E196, 1.0E197, 1.0E198, 1.0E199, 1.0E200, 1.0E201, 1.0E202, 1.0E203, 1.0E204, 1.0E205, 1.0E206, 1.0E207, 1.0E208, 1.0E209, 1.0E210, 1.0E211, 1.0E212, 1.0E213, 1.0E214, 1.0E215, 1.0E216, 1.0E217, 1.0E218, 1.0E219, 1.0E220, 1.0E221, 1.0E222, 1.0E223, 1.0E224, 1.0E225, 1.0E226, 1.0E227, 1.0E228, 1.0E229, 1.0E230, 1.0E231, 1.0E232, 1.0E233, 1.0E234, 1.0E235, 1.0E236, 1.0E237, 1.0E238, 1.0E239, 1.0E240, 1.0E241, 1.0E242, 1.0E243, 1.0E244, 1.0E245, 1.0E246, 1.0E247, 1.0E248, 1.0E249, 1.0E250, 1.0E251, 1.0E252, 1.0E253, 1.0E254, 1.0E255, 1.0E256, 1.0E257, 1.0E258, 1.0E259, 1.0E260, 1.0E261, 1.0E262, 1.0E263, 1.0E264, 1.0E265, 1.0E266, 1.0E267, 1.0E268, 1.0E269, 1.0E270, 1.0E271, 1.0E272, 1.0E273, 1.0E274, 1.0E275, 1.0E276, 1.0E277, 1.0E278, 1.0E279, 1.0E280, 1.0E281, 1.0E282, 1.0E283, 1.0E284, 1.0E285, 1.0E286, 1.0E287, 1.0E288, 1.0E289, 1.0E290, 1.0E291, 1.0E292, 1.0E293, 1.0E294, 1.0E295, 1.0E296, 1.0E297, 1.0E298, 1.0E299, 1.0E300, 1.0E301, 1.0E302, 1.0E303, 1.0E304, 1.0E305, 1.0E306, 1.0E307, 1.0E308 }; float FITS::tenpowerF[39] = { 1.0F, 1.0E1F, 1.0E2F, 1.0E3F, 1.0E4F, 1.0E5F, 1.0E6F, 1.0E7F, 1.0E8F, 1.0E9F, 1.0E10F, 1.0E11F, 1.0E12F, 1.0E13F, 1.0E14F, 1.0E15F, 1.0E16F, 1.0E17F, 1.0E18F, 1.0E19F, 1.0E20F, 1.0E21F, 1.0E22F, 1.0E23F, 1.0E24F, 1.0E25F, 1.0E26F, 1.0E27F, 1.0E28F, 1.0E29F, 1.0E30F, 1.0E31F, 1.0E32F, 1.0E33F, 1.0E34F, 1.0E35F, 1.0E36F, 1.0E37F, 1.0E38F }; const int FITS::minfltexp = -38; const int FITS::maxfltexp = 38; const int FITS::mindblexp = -308; const int FITS::maxdblexp = 308; const int FITS::maxsigdigits = 17; const int FITS::maxdigl = 9; // max digits in a long const int FITS::maxexpdig = 3; // max digits in an exponent const Int FITS::minInt = INT_MIN; const Int FITS::maxInt = INT_MAX; # if defined(GNU) const float FITS::minfloat = C::minfloat; const float FITS::maxfloat = C::maxfloat; const double FITS::mindouble = C::mindouble; const double FITS::maxdouble = C::maxdouble; # else const float FITS::minfloat = FLT_MIN; const float FITS::maxfloat = FLT_MAX; const double FITS::mindouble = DBL_MIN; const double FITS::maxdouble = DBL_MAX; # endif ostream & operator << (ostream &o, const FitsLogical &x) { if (x.v == 'T') o << "True"; else if (x.v == 'F') o << "False"; else o << "Undefined"; return o; } ostream & operator << (ostream &o, const FitsVADesc &x) { o << "[" << x.num() << "," << x.offset() << "]"; return o; } int FITS::fitssize(FITS::ValueType t) { // 0 1 2 3 4 5 6 7 8 9 10 11 12 static int FitsDataSize[13] = {0,1,1,1,1,2,4,4,8,8,8, 16, 8}; return FitsDataSize[t]; } int FITS::localsize(FITS::ValueType t) { static int LocalDataSize[13] = { 0, // 0 sizeof(FitsLogical), // 1 sizeof(FitsBit), // 2 sizeof(char), // 3 sizeof(uChar), // 4 sizeof(short), // 5 sizeof(FitsLong), // 6 sizeof(float), // 7 sizeof(double), // 8 sizeof(Complex), // 9 sizeof(IComplex), // 10 sizeof(DComplex), // 11 sizeof(FitsVADesc) // 12 }; return LocalDataSize[t]; } // Data conversion: FitsLogical void FITS::f2l(FitsLogical *local_addr, void *fits_addr, int number) { memcpy(local_addr,fits_addr,(number * sizeof(FitsLogical))); } void FITS::l2f(void *fits_addr, FitsLogical *local_addr, int number) { memcpy(fits_addr,local_addr,(number * sizeof(FitsLogical))); } // Data conversion: FitsBit void FITS::f2l(FitsBit *local_addr, void *fits_addr, int number) { int n = number / 8; if (number % 8 != 0) ++n; memcpy(local_addr,fits_addr,(n * sizeof(uChar))); } void FITS::l2f(void *fits_addr, FitsBit *local_addr, int number) { int n = number / 8; if (number % 8 != 0) ++n; memcpy(fits_addr,local_addr,(n * sizeof(uChar))); } // Data conversion: char void FITS::f2l(char *local_addr, void *fits_addr, int number) { memcpy(local_addr,fits_addr,(number * sizeof(char))); } void FITS::l2f(void *fits_addr, char *local_addr, int number) { memcpy(fits_addr,local_addr,(number * sizeof(char))); } // Data conversion: uChar void FITS::f2l(uChar *local_addr, void *fits_addr, int number) { memcpy(local_addr,fits_addr,(number * sizeof(uChar))); } void FITS::l2f(void *fits_addr, uChar *local_addr, int number) { memcpy(fits_addr,local_addr,(number * sizeof(uChar))); } // Data conversion: short void FITS::f2l(short *local_addr, void *fits_addr, int number) { # if defined(AIPS_LITTLE_ENDIAN) FITS::swap2(local_addr, fits_addr, number); # else memcpy(local_addr,fits_addr,(number * sizeof(short))); # endif } void FITS::l2f(void *fits_addr, short *local_addr, int number) { # if defined(AIPS_LITTLE_ENDIAN) FITS::swap2(fits_addr, local_addr, number); # else memcpy(fits_addr,local_addr,(number * sizeof(short))); # endif } // Data conversion: long void FITS::f2l(long *local_addr, void *fits_addr, int number) { # if defined(AIPS_LITTLE_ENDIAN) switch(sizeof(long)) { case 4: FITS::swap4(local_addr, fits_addr, number); break; case 8: FITS::swap8(local_addr, fits_addr, number); break; } # else memcpy(local_addr,fits_addr,(number * sizeof(long))); # endif } void FITS::l2f(void *fits_addr, long *local_addr, int number) { FITS::f2l( (long *)fits_addr, local_addr, number ); } // Data conversion: Int void FITS::f2l(Int *local_addr, void *fits_addr, int number) { # if defined(AIPS_LITTLE_ENDIAN) switch(sizeof(Int)) { case 2: FITS::swap2(local_addr, fits_addr, number); break; case 4: FITS::swap4(local_addr, fits_addr, number); break; } # else memcpy(local_addr,fits_addr,(number * sizeof(Int))); # endif } void FITS::l2f(void *fits_addr, Int *local_addr, int number) { FITS::f2l((Int *) fits_addr, local_addr, number); } // Data conversion: float void FITS::f2l(float *local_addr, void *fits_addr, int number) { # if defined(AIPS_LITTLE_ENDIAN) swap4(local_addr, fits_addr, number); # else memcpy(local_addr,fits_addr,(number * sizeof(float))); # endif } void FITS::l2f(void *fits_addr, float *local_addr, int number) { # if defined(AIPS_LITTLE_ENDIAN) swap4(fits_addr, local_addr, number); # else memcpy(fits_addr,local_addr,(number * sizeof(float))); # endif } // Data conversion: double void FITS::f2l(double *local_addr, void *fits_addr, int number) { # if defined(AIPS_LITTLE_ENDIAN) swap8(local_addr, fits_addr, number); # else memcpy(local_addr,fits_addr,(number * sizeof(double))); # endif } void FITS::l2f(void *fits_addr, double *local_addr, int number) { # if defined(AIPS_LITTLE_ENDIAN) swap8(fits_addr, local_addr, number); # else memcpy(fits_addr,local_addr,(number * sizeof(double))); # endif } // Data conversion: Complex void FITS::f2l(Complex *local_addr, void *fits_addr, int number) { # if defined(AIPS_LITTLE_ENDIAN) FITS::f2l( (float *)local_addr, fits_addr, 2*number ); # else memcpy(local_addr,fits_addr,(number * sizeof(Complex))); # endif } void FITS::l2f(void *fits_addr, Complex *local_addr, int number) { # if defined(AIPS_LITTLE_ENDIAN) FITS::f2l( (float *)fits_addr, local_addr, 2*number ); # else memcpy(fits_addr,local_addr,(number * sizeof(Complex))); # endif } // Data conversion: IComplex void FITS::f2l(IComplex *local_addr, void *fits_addr, int number) { # if defined(AIPS_LITTLE_ENDIAN) FITS::f2l( (Int *)local_addr, fits_addr, 2*number ); # else memcpy(local_addr,fits_addr,(number * sizeof(IComplex))); # endif } void FITS::l2f(void *fits_addr, IComplex *local_addr, int number) { # if defined(AIPS_LITTLE_ENDIAN) FITS::f2l( (Int *)fits_addr, local_addr, 2*number ); # else memcpy(fits_addr,local_addr,(number * sizeof(IComplex))); # endif } // Data conversion: DComplex void FITS::f2l(DComplex *local_addr, void *fits_addr, int number) { # if defined(AIPS_LITTLE_ENDIAN) FITS::f2l( (double *)local_addr, fits_addr, 2*number ); # else memcpy(local_addr,fits_addr,(number * sizeof(DComplex))); # endif } void FITS::l2f(void *fits_addr, DComplex *local_addr, int number) { # if defined(AIPS_LITTLE_ENDIAN) FITS::f2l( (double *)fits_addr, local_addr, 2*number ); # else memcpy(fits_addr,local_addr,(number * sizeof(DComplex))); # endif } // Data conversion: FitsVADesc void FITS::f2l(FitsVADesc *local_addr, void *fits_addr, int number) { # if defined(AIPS_LITTLE_ENDIAN) FITS::f2l( (Int *)local_addr, fits_addr, 2*number ); # else memcpy(local_addr,fits_addr,(number * sizeof(FitsVADesc))); # endif } void FITS::l2f(void *fits_addr, FitsVADesc *local_addr, int number) { # if defined(AIPS_LITTLE_ENDIAN) FITS::f2l( (Int *)fits_addr, local_addr, 2*number ); # else memcpy(fits_addr,local_addr,(number * sizeof(FitsVADesc))); # endif } // Swap routines for 2, 4 and 8 byte items void FITS::swap2(void *dest, void *src, int number) { uChar *t = (uChar *)dest; uChar *s = (uChar *)src; if( t == s ) for (int i = 0; i < number; ++i, t += 2) { uChar tmp; tmp = *t; *t = t[1]; t[1] = tmp; } else for (int i = 0; i < number; ++i, t += 2) { t[1] = *s++; *t = *s++; } } void FITS::swap4(void *dest, void *src, int number) { uChar *t = (uChar *)dest; uChar *s = (uChar *)src; if( t == s ) { for (int i = 0; i < number; ++i, t += 4) { uChar tmp; tmp = *t; *t = t[3]; t[3] = tmp; tmp = t[1]; t[1] = t[2]; t[2] = tmp; } } else for (int i = 0; i < number; ++i, t += 4) { t[3] = *s++; t[2] = *s++; t[1] = *s++; *t = *s++; } } void FITS::swap8(void *dest, void *src, int number) { uChar *t = (uChar *)dest; uChar *s = (uChar *)src; if( t == s ) { for (int i = 0; i < number; ++i, t += 8) { uChar tmp; tmp = *t; *t = t[7]; t[7] = tmp; tmp = t[1]; t[1] = t[6]; t[6] = tmp; tmp = t[2]; t[2] = t[5]; t[5] = tmp; tmp = t[3]; t[3] = t[4]; t[4] = tmp; } } else { for (int i = 0; i < number; ++i, t += 8) { t[7] = *s++; t[6] = *s++; t[5] = *s++; t[4] = *s++; t[3] = *s++; t[2] = *s++; t[1] = *s++; *t = *s++; } } } void FITS::valstr(ostream &o, const ValueType &ty, const void *val) { int n; Complex x; DComplex y; IComplex z; if (!val) return; switch (ty) { case FITS::NOVALUE: break; case FITS::LOGICAL: o << ((*((Bool *)val) == True) ? "True" : "False"); break; case FITS::BIT: o << "*****"; break; case FITS::CHAR: o << *((char *)val); break; case FITS::BYTE: n = *((uChar *)val); o << n; break; case FITS::SHORT: o << *((short *)val); break; case FITS::LONG: o << *((FitsLong *)val); break; case FITS::STRING: o << "\'" << (char *)val << "\'"; break; case FITS::FLOAT: o << *((float *)val); break; case FITS::DOUBLE: o << *((double *)val); break; case FITS::COMPLEX: x = *(Complex *)val; o << "(" << x.real() << "," << x.imag() << ")"; break; case FITS::DCOMPLEX: y = *(DComplex *)val; o << "(" << y.real() << "," << y.imag() << ")"; break; case FITS::ICOMPLEX: z = *(IComplex *)val; o << "(" << z.real() << "," << z.imag() << ")"; break; default: o << "*****"; break; } } ostream & operator << (ostream &o, const FITS::ValueType &ty) { switch (ty) { case FITS::NOVALUE: break; case FITS::LOGICAL: o << " LOGICAL "; break; case FITS::BIT: o << " BIT "; break; case FITS::CHAR: o << " CHAR "; break; case FITS::BYTE: o << " BYTE "; break; case FITS::SHORT: o << " SHORT "; break; case FITS::LONG: o << " LONG "; break; case FITS::FLOAT: o << " FLOAT "; break; case FITS::DOUBLE: o << " DOUBLE "; break; case FITS::COMPLEX: o << " COMPLEX "; break; case FITS::ICOMPLEX: o << " ICOMPLEX "; break; case FITS::DCOMPLEX: o << " DCOMPLEX "; break; case FITS::VADESC: o << " VADESC "; break; case FITS::STRING: o << " STRING "; break; case FITS::FSTRING: o << " FSTRING "; break; case FITS::REAL: o << " REAL "; break; default: o << " ILLEGAL "; break; } return o; } const ReservedFitsKeyword & ReservedFitsKeywordCollection::match(int i, const char *s, int s_len, Bool n, FITS::ValueType t, const void *v, int v_len, const char *&msg) const { if (t == FITS::FLOAT || t == FITS::DOUBLE) t = FITS::REAL; // change t to REAL to match on types if (t == FITS::FSTRING) t = FITS::STRING; // change t to STRING to match on types // match type and isindexed if (resword[i].type() != t) { while (resword[i + 1].name() == resword[i].name()) { ++i; if (resword[i].type() == t) break; } if (resword[i].type() != t) { msg = "Keyword value has wrong data type."; return error_item; } } if (resword[i].isindexed() != n) { while ((resword[i + 1].name() == resword[i].name()) && (resword[i + 1].type() == resword[i].type())) { ++i; if (resword[i].isindexed() == n) break; } if (resword[i].isindexed() != n) { if (resword[i].isindexed()){ msg = "Keyword requires an index."; return error_item; } else { msg = "Keyword should not have an index."; return user_def_item; // treat as non-reserved keyword } } } return rules(resword[i],s,s_len,n,t,v,v_len,msg) == -1 ? error_item : resword[i]; } const ReservedFitsKeyword & ReservedFitsKeywordCollection::get( FITS::ReservedName nm, Bool n, FITS::ValueType t, const void *v, int v_len, const char *&msg) const { int i; msg = 0; for (i = 0; i < no_items; ++i) if (resword[i].name() == nm) break; return match(i,0,0,n,t,v,v_len,msg); } const ReservedFitsKeyword & ReservedFitsKeywordCollection::get(const char *s, int s_len, Bool n, FITS::ValueType t, const void *v, int v_len, const char *&msg) const { msg = 0; int i; // The index into the table. if (!FITS::isa_letter(*s)) return rules(user_def_item,s,s_len,n,t,v,v_len,msg) == -1 ? error_item : user_def_item; if ((i = resalpha[FITS::letter2bin(*s)]) == 0) return rules(user_def_item,s,s_len,n,t,v,v_len,msg) == -1 ? error_item : user_def_item; for(; *(resword[i].aname()) == *s; ++i) // search for a match if (resword[i].namesize() == s_len && strncmp(s,resword[i].aname(),s_len) == 0) break; if (*(resword[i].aname()) != *s || resword[i].namesize() != s_len) return rules(user_def_item,s,s_len,n,t,v,v_len,msg) == -1 ? error_item : user_def_item; return match(i,s,s_len,n,t,v,v_len,msg); } int ReservedFitsKeywordCollection::isreserved(const char *s, int s_len) const { int i; // The index into the table. // If this name MIGHT be a reserved name, this routine returns // an index into the reserved word table, otherwise 0 is returned. if (!FITS::isa_letter(*s)) return 0; if ((i = resalpha[FITS::letter2bin(*s)]) == 0) return 0; for(; *(resword[i].aname()) == *s; ++i) // search for a match if (resword[i].namesize() == s_len && strncmp(s,resword[i].aname(),s_len) == 0) break; if (*(resword[i].aname()) != *s || resword[i].namesize() != s_len) return 0; return i; } Bool ReservedFitsKeywordCollection::requires_value(int n) const { if (resword[n].type() == FITS::NOVALUE) return False; while (resword[n].name() == resword[n + 1].name()) { ++n; if (resword[n].type() == FITS::NOVALUE) return False; } return True; } int ReservedFitsKeywordCollection::rules(const ReservedFitsKeyword &res, const char *s, int s_len, Bool n, FITS::ValueType, const void *v, int v_len, const char *&msg) const { // Return: 0 = no errors, 1 = minor errors, -1 = major errors static int month[13] = {0,31,28,31,30,31,30,31,31,30,31,30,31}; int dd, mm, yy, mmdays; const char *p; // These are context independent rules. if (res.name() == FITS::USER_DEF) { for (int i = 0; i < s_len; ++i, ++s) { if (!(FITS::isa_letter(*s) || FITS::isa_digit(*s) || *s == '_' || *s == '-')) { msg = "Illegal characters in keyword name."; return 1; } } return 0; } // The name, isindexed, and type match an entry in the table. const Int *l; switch (res.name()) { case FITS::BITPIX: l = (const Int *)v; if (!(*l == 8 || *l == 16 || *l == 32 || *l == -32 || *l == -64)) { msg = "Illegal value for keyword BITPIX."; return -1; } break; case FITS::NAXIS: if (n == False) { l = (const Int *)v; if (*l < 0 || *l > 999) { msg = "Illegal value for keyword NAXIS."; return -1; } } break; case FITS::TFIELDS: l = (const Int *)v; if (*l < 0 || *l > 999) { msg = "Illegal value for keyword TFIELDS."; return -1; } break; case FITS::DATE: // s must be of the form `DD/MM/YY' or 'YYYY-MM-DD[THH:MM:SS[.sss]] case FITS::DATE_OBS: if (v_len < 8) { msg = "Illegal date format."; return 1; } p = (const char *)v; // first, is this the new format if ((v_len >= 10) && FITS::isa_digit(p[0]) && FITS::isa_digit(p[1]) && FITS::isa_digit(p[2]) && FITS::isa_digit(p[3]) && FITS::isa_digit(p[5]) && FITS::isa_digit(p[6]) && FITS::isa_digit(p[8]) && FITS::isa_digit(p[9]) && (p[4] == '-') && (p[7] == '-')) { // must be an attempt at the new format yy = FITS::digit2bin(p[0]) * 1000 + FITS::digit2bin(p[1]) * 100 + FITS::digit2bin(p[2]) * 10 + FITS::digit2bin(p[3]); mm = FITS::digit2bin(p[5]) * 10 + FITS::digit2bin(p[6]); dd = FITS::digit2bin(p[8]) * 10 + FITS::digit2bin(p[9]); if (v_len > 10) { // if there are more than 10 character, the time must // be fully there up to an optional decimal point if (v_len >= 19 && FITS::isa_digit(p[11]) && FITS::isa_digit(p[12]) && FITS::isa_digit(p[14]) && FITS::isa_digit(p[15]) && FITS::isa_digit(p[17]) && FITS::isa_digit(p[18]) && (p[10] == 'T') && (p[13] == ':') && (p[16] == ':')) { // 24 is okay, more than that is not in hours field if ((FITS::digit2bin(p[11]) == 2 && FITS::digit2bin(p[12]) > 4) || FITS::digit2bin(p[11]) > 2) { msg = "Illegal time."; return 1; } // 60 is okay, more than that is not in minutes field if ((FITS::digit2bin(p[14]) == 6 && FITS::digit2bin(p[15]) > 0) || FITS::digit2bin(p[14]) > 6) { msg = "Illegal time."; } // 60 is okay, more than that is not in secomds field if ((FITS::digit2bin(p[17]) == 6 && FITS::digit2bin(p[18]) > 0) || FITS::digit2bin(p[17]) > 6) { msg = "Illegal time."; return 1; } // decimal required at 19th char, if exists. if (v_len > 19 && !(p[19] == '.')) { msg = "Illegal date format."; return 1; } // digits required after decimal for (Int curr = 20;curr <= (v_len-1); curr++) { if (!FITS::isa_digit(p[curr])) { msg = "Illegal date format."; return 1; } } } else { msg = "Illegal date format."; return 1; } } } else if (FITS::isa_digit(p[0]) && FITS::isa_digit(p[1]) && FITS::isa_digit(p[3]) && FITS::isa_digit(p[4]) && FITS::isa_digit(p[6]) && FITS::isa_digit(p[7]) && (p[2] == '/') && (p[5] == '/')) { // the old format dd = FITS::digit2bin(p[0]) * 10 + FITS::digit2bin(p[1]); mm = FITS::digit2bin(p[3]) * 10 + FITS::digit2bin(p[4]); yy = FITS::digit2bin(p[6]) * 10 + FITS::digit2bin(p[7]); yy += 1900; } else { msg = "Illegal date format."; return 1; } if (mm == 0 || mm > 12) { msg = "Illegal date."; return 1; } mmdays = month[mm]; if ((mm == 2) && ((yy % 4) == 0)) mmdays = 29; if (dd == 0 || dd > mmdays) { msg = "Illegal date."; return 1; } break; // The following "default" was added to prevent compilers // such as GNU g++ from giving warnings about enumeration // values not being handled. This should be cleaned up // some point. // -OO default: // Nothing - OK break; } return 0; } const char *ReservedFitsKeywordCollection::aname(FITS::ReservedName nm) const { int i; for (i = 0; i < no_items; ++i) if (resword[i].name() == nm) break; return i < no_items ? resword[i].aname() : ""; } int ReservedFitsKeywordCollection::essential_name(const char *s, int s_len) const { // If this name MIGHT be an essential name, this routine returns // an index into the reserved word table, otherwise 0 is returned. int i; // The index into the table. if (!FITS::isa_letter(*s)) return 0; if ((i = resalpha[FITS::letter2bin(*s)]) == 0) return 0; for(; *(resword[i].aname()) == *s; ++i) // search for a match if (resword[i].namesize() == s_len && strncmp(s,resword[i].aname(),s_len) == 0) break; if (*(resword[i].aname()) != *s || resword[i].namesize() != s_len) return 0; if (resword[i].isessential()) return i; while (resword[i + 1].name() == resword[i].name()) { ++i; if (resword[i].isessential()) return i; } return 0; } const ReservedFitsKeyword & ReservedFitsKeywordCollection::get_essential(int i, Bool n, FITS::ValueType t, const void *v, int v_len, const char *&msg) const { // This index i must be to an essential name in the table. msg = 0; if (i <= 0 || i >= no_items) { msg = "Internal error! Invalid index into ResWord."; return error_item; } if (!resword[i].isessential()) { msg = "Internal error! Invalid index into ResWord."; return error_item; } // we have a match on name - match type and isindexed if (t == FITS::FLOAT || t == FITS::DOUBLE) t = FITS::REAL; // change t to REAL to match on types if (t == FITS::FSTRING) t = FITS::STRING; // change to to STRING to match on types if (resword[i].type() != t) { while ((resword[i + 1].name() == resword[i].name()) && resword[i + 1].isessential()) { ++i; if (resword[i].type() == t) break; } if (resword[i].type() != t) { msg = "Keyword value has wrong data type."; return error_item; } } if (resword[i].isindexed() != n) { while ((resword[i + 1].name() == resword[i].name()) && (resword[i + 1].type() == resword[i].type())) { ++i; if (resword[i].isindexed() == n) break; } if (resword[i].isindexed() != n) { if (resword[i].isindexed()) msg = "Keyword requires an index."; else msg = "Keyword must not have an index."; return error_item; } } return rules(resword[i],0,0,n,t,v,v_len,msg) == -1 ? error_item : resword[i]; } // Instantiate the reserved keyword collection ReservedFitsKeywordCollection ResWord_; ReservedFitsKeywordCollection &FITS::ResWord = ResWord_; void FITS::get_name(const char *s, int len, FitsNameResult &result) { int i; // A name is any sequence of text chars except ' ' and '=' result.err = FitsNameResult::OK; for (i = 0; *s == ' ' && (i < len); ++i, ++s) ; // skip spaces if (i == len || (!FITS::isa_text(*s)) || *s == '=') { result.isaname = False; // If there is no name, only result.begpos = i; // begpos has meaning. return; } result.isaname = True; result.begpos = i; for(; *s != ' ' && *s != '=' && (i < len) && FITS::isa_text(*s); ++i, ++s) ; result.endpos = i - 1; result.len = i - result.begpos; --s; result.isaindex = False; result.index = 0; if (FITS::isa_digit(*s)) { // get any index result.isaindex = True; result.index = FITS::digit2bin(*s--); --result.len; if (FITS::isa_digit(*s)) { i = 1; do { i *= 10; result.index += i * FITS::digit2bin(*s--); --result.len; } while(FITS::isa_digit(*s)); if (s[1] == '0') // index cannot have leading zeros result.err = FitsNameResult::NO_0_NDX; } } } int FITS::get_value_id(const char *s, int l, int &pos) { // A value_id is the first occurance of "=" in the field. // If there is a value_id, 1 is returned; otherwise, 0 is returned. // If a value_id is present, its position is recorded in pos. int i; for (i = 0; (i < l) && *s == ' '; ++i, ++s) ; // skip spaces if (i == l || *s != '=') { pos = 0; return 0; } pos = i; return 1; } /****************************************************************************** Fortran-77 Definition of integer, real, and double (no embedded blanks) -------------------------------------------------- digit := <0|1|2|3|4|5|6|7|8|9> digit_sequence := digit [ digit ... ] sign := <+|-> point := <.> integer := [sign] digit_sequence efield := E [integer] dfield := D [integer] basic_real := integer point [ digit_sequence ] [sign] point digit_sequence real := basic_real [ efield ] integer efield double := basic_real dfield integer dfield FITS fixed formats on keyword cards (NOST 5.3.2) ------------------------------------------------ string ' in col 11, ' to close (in or before col 80), '' represents ' within the string, trailing blanks not significant logical T or F in col 30 integer integer right justified in col 11-30 complex integer real part col 11-30, img part col 31-50, right just real float real or double right justified in col 11-30, point req complex float real part col 11-30, img part col 31-50, right just ******************************************************************************/ void FITS::get_value(const char *s, int len, FitsValueResult &result) { int i, j; result.type = FITS::NOVALUE; result.s[0] = 0; result.s[1] = 0; result.begpos = 0; result.endpos = 0; result.isa_point = False; result.pointpos = 0; result.no_sig = 0; result.errmsg = 0; for (i = 0; *s == ' ' && (i < len); ++s, ++i) ; // skip spaces if (i == len) // The field is all blanks. return; switch (*s) { // the first non-blank case 'T': // logical result.b = True; result.type = FITS::LOGICAL; result.begpos = i; result.endpos = i; return; case 'F': // logical result.b = False; result.type = FITS::LOGICAL; result.begpos = i; result.endpos = i; return; case '\'': // string result.type = FITS::STRING; result.begpos = i++; s++; if (i == len) { result.errmsg = "Invalid string syntax."; result.endpos = result.begpos; return; } result.s[0] = i; j = 0; // length of string for(;;) { if (i == len) { result.errmsg = "String has no ending quote mark."; i = len - 1; break; } else if (*s != '\'') { if (!FITS::isa_text(*s)) { if (!result.errmsg) result.errmsg = "String value contains non-ASCII_text."; } ++j; ++s; ++i; } else if (i == (len - 1)) { ++s; break; } else if (*(s+1) == '\'') { result.type = FITS::FSTRING; s += 2; i += 2; ++j; } else { ++s; break; } } result.endpos = i; --s; // chop off any trailing blanks if (*s == '\'') --s; while (*s == ' ' && j > 8) { --j; --s; } result.s[1] = j; return; case '(': // F77 list-directed complex result.type = FITS::NOVALUE; result.errmsg = "F77 list-directed complex not implemented."; return; default: if (*s == '.' && (i < (len - 1))) { if (s[i + 1] == 'T') { // F77 list-directed logical result.b = True; result.type = FITS::LOGICAL; result.begpos = i; i += 2; s += 2; for (; *s != ' ' && *s != '/' && (i < len); ++i) ; if (i == len) --i; result.endpos = i; return; } else if (s[i + 1] == 'F') { // F77 logical result.b = False; result.type = FITS::LOGICAL; result.begpos = i; i += 2; s += 2; for (; *s != ' ' && *s != '/' && (i < len); ++i) ; if (i == len) --i; result.endpos = i; return; } } // Must be numeric, it's the only thing left get_numeric(s,(len - i),result); result.begpos += i; // the beginning of this s is offset by i result.endpos += i; if (result.isa_point) result.pointpos += i; if (result.errmsg) { if (strcmp(result.errmsg,"Not a number") == 0) result.errmsg = "Value field is not a valid data type."; } break; } } int FITS::ckaccum(double &d, Int numb, int pow) { // compute d += numb * 10**pow checking for over/underflow double tmp = (double)numb; if (pow > 0) { if (pow > 2 * maxdblexp) return 1; if (tmp <= (maxdouble / tenpowerD[pow])) tmp *= tenpowerD[pow]; else return 1; } else if (pow < 0) { pow = -pow; if (pow > 2 * maxdblexp) return -1; if ((mindouble * tenpowerD[pow]) <= tmp) tmp /= tenpowerD[pow]; else return -1; } if ((maxdouble - tmp) < d) return 1; d += tmp; return 0; } int FITS::ckaccum(float &f, Int numb, int pow) { // compute f += numb * 10**pow checking for over/underflow float tmp = (float)numb; if (pow > 0) { if (pow > 2 * maxfltexp) return 1; if (tmp <= (maxfloat / tenpowerF[pow])) tmp *= tenpowerF[pow]; else return 1; } else if (pow < 0) { pow = -pow; if (pow > 2 * maxfltexp) return -1; if ((minfloat * tenpowerF[pow]) <= tmp) tmp /= tenpowerF[pow]; else return -1; } if ((maxfloat - tmp) < f) return 1; f += tmp; return 0; } void FITS::get_numeric(const char *s, int len, FitsValueResult &result) { int n; // the number of chars processed -- n == len signals `at end' int i, j; // counter, confined to local context const char *p; // utility valiable, confined to local context result.type = NOVALUE; // Initialize result result.errmsg = 0; result.l = 0; // It may not be nessary to init the rest of these. result.begpos = 0; result.endpos = 0; result.isa_point = False; result.pointpos = 0; result.no_sig = 0; // 1. Skip any spaces at the beginning for (n = 0; *s == ' ' && (n < len); ++n, ++s) ; if (n == len) { result.errmsg = "Value field is all blanks"; return; } // 2. Mark position as the start of the field result.begpos = n; // 3. Check for any sign that may be present int negsign = (*s == '-' ? (++s, ++n, 1) : (*s == '+' ? (++s, ++n, 0) : 0)); if (n == len || !(FITS::isa_digit(*s) || *s == '.')) { result.errmsg = "Not a number"; return; } // 4. Skip any leading 0s as insignificant for (; *s == '0' && n < len; ++n, ++s) ; if (n == len) { result.type = LONG; result.l = 0; result.endpos = n - 1; result.isa_point = False; result.pointpos = 0; result.no_sig = 1; return; } // 5. Get integer part of number. Get digits, store in Ints, // and count significant digits Int intpart1 = 0; // part 1 of digits of integer part Int intpart2 = 0; // part 2 of digits of integer part int sigint = 0; // number of significant digits if (isa_digit(*s)) { intpart1 = digit2bin(*s); ++sigint; for (++n, ++s; isa_digit(*s) && (n < len); ++n, ++s) { ++sigint; intpart1 = intpart1 * 10 + digit2bin(*s); if (sigint == maxdigl) // Longs can always hold maxdigl digits break; } if (sigint == maxdigl && n < len) { ++n; ++s; if (isa_digit(*s) && n < len) { intpart2 = digit2bin(*s); ++sigint; for (++n, ++s; isa_digit(*s) && (n < len); ++n, ++s) { ++sigint; intpart2 = intpart2 * 10 + digit2bin(*s); if (sigint == maxsigdigits) break; } } if (sigint == maxsigdigits && n < len) { // Discard digits for (++n, ++s; isa_digit(*s) && (n < len); ++n, ++s) ++sigint; // But count them } } } if (n == len || (!(*s == '.' || *s == 'E' || *s == 'D'))) { result.endpos = n - 1; result.isa_point = False; result.pointpos = 0; if (sigint < 10) { result.type = LONG; result.l = negsign ? (-intpart1) : intpart1; result.no_sig = sigint == 0 ? 1 : sigint; return; } if (sigint > 10) { result.errmsg = negsign ? "Integer underflow" : "Integer overflow"; return; } if (negsign) { intpart1 = -intpart1; if (((minInt + intpart2) / 10) <= intpart1) { result.type = LONG; result.l = intpart1 * 10 - intpart2; result.no_sig = sigint; return; } else { result.errmsg = "Integer underflow"; return; } } else { if (((maxInt - intpart2) / 10) >= intpart1) { result.type = LONG; result.l = intpart1 * 10 + intpart2; result.no_sig = sigint; return; } else { result.errmsg = "Integer overflow"; return; } } } // 6. If valid, the number is float or double. Get the fraction // part, if any. Int fracpart1 = 0; // part 1 of digits of fraction part Int fracpart2 = 0; // part 2 of digits of fraction part int sigfrac = 0; // number of significant digits in fraction int fracpos = 0; // position of first digit relative to point int exp = 0; // exponent int sigexp = 0; // number of significant digits in exponent char exp_type = ' '; // the exponent letter if (*s == '.') { result.isa_point = True; result.pointpos = n; // 7. Get the fraction part ++s; ++n; if (n == len) goto real_value; if (*s == 'E' || *s == 'D') goto get_exp; if (!(isa_digit(*s))) goto real_value; if (sigint == 0) { // i. e., there are no sig digs in int part for (; *s == '0' && n < len; ++n, ++s) ; if (n == len) goto real_value; fracpos = n - result.pointpos - 1; } p = s; // save start of fraction part for (; isa_digit(*s) && (n < len); ++s, ++n, ++sigfrac) ; // adjust sigfrac to only process the maximum number of sig digits if (sigint >= maxsigdigits) sigfrac = 0; else if ((sigint + sigfrac) > maxsigdigits) sigfrac = maxsigdigits - sigint; // get sigfrac digits starting at p if (sigfrac > 0) { fracpart1 = digit2bin(*p); for (i = 1, ++p; i < sigfrac && i < maxdigl; ++i, ++p) fracpart1 = fracpart1 * 10 + digit2bin(*p); if (i == maxdigl && i < sigfrac) { if (i < sigfrac) { fracpart2 = digit2bin(*p); for (++i, ++p; i < sigfrac; ++i, ++p) fracpart2 = fracpart2 * 10 + digit2bin(*p); } } } // 8. Check next char after fraction part if (!(*s == 'E' || *s == 'D')) goto real_value; } // 9. Get the exponent, if any get_exp: exp_type = *s++; ++n; if (n == len) goto real_value; // 10. Check for any sign that may be present i = 0; // i is the sign if (*s == '+' || *s == '-') { if (*s == '-') i = 1; ++n; ++s; if (n == len) { // This is a strange condition, the field result.endpos = n; // ends with a sign. Mark it as an error. result.errmsg = "Value field is not a valid number"; return; } } // 11. Skip any leading 0s as insignificant for (; *s == '0' && n < len; ++n, ++s) ; if (n == len) goto real_value; // 12. Get, at most, the max digits in an exponent if (isa_digit(*s)) { exp = digit2bin(*s); ++sigexp; for (++n, ++s; isa_digit(*s) && (n < len); ++n, ++s) { ++sigexp; exp = exp * 10 + digit2bin(*s); if (sigexp == maxexpdig) { ++n; if (n == len) break; if (isa_digit(s[1])) { result.endpos = n - 1; if (i) result.errmsg = "Exponent underflow"; else result.errmsg = "Exponent overflow"; return; } break; } } if (i) exp = -exp; } real_value: // 13. Compute real value // It's real because either result.isa_point == 1, or exp_type != ' '. result.endpos = n - 1; result.no_sig = sigint + sigfrac; if (result.no_sig <= 5 && (exp_type == ' ' || exp_type == 'E')) { if (intpart1 == 0) { if (fracpart1 == 0) { result.type = FLOAT; result.f = 0.0F; return; } else if ((exp - fracpos - 1) > minfltexp && (exp - fracpos) < maxfltexp) { result.type = FLOAT; result.f = tenF(fracpart1,(exp - sigfrac - fracpos)); if (negsign) result.f = -result.f; return; } } else if ((sigint - 1 + exp) > minfltexp && (sigint + exp) < maxfltexp) { result.type = FLOAT; result.f = (fracpart1 == 0) ? 0.0F : tenF(fracpart1,(exp - sigfrac - fracpos)); result.f += tenF(intpart1,exp); if (negsign) result.f = -result.f; return; } result.f = 0.0F; i = ckaccum(result.f,fracpart1,(exp - sigfrac - fracpos)); if (i) { result.errmsg = (i > 0) ? "Float overflow" : "Float underflow"; return; } i = ckaccum(result.f,intpart1,exp); if (i) { result.errmsg = (i > 0) ? "Float overflow" : "Float underflow"; return; } result.type = FLOAT; if (negsign) result.f = -result.f; return; } if (result.no_sig > 5 || exp_type == 'D') { if (intpart1 == 0) { if (fracpart1 == 0) { result.type = DOUBLE; result.d = 0.0; return; } else if ((exp - fracpos - 1) > mindblexp && (exp - fracpos) < maxdblexp) { result.type = DOUBLE; result.d = (fracpart2 == 0) ? 0.0 : tenD(fracpart2,(exp - sigfrac - fracpos)); i = (sigfrac < maxdigl) ? sigfrac : maxdigl; i = exp - fracpos - i; result.d += tenD(fracpart1,i); if (negsign) result.d = -result.d; return; } } else if ((sigint - 1 + exp) > mindblexp && (sigint + exp) < maxdblexp) { result.type = DOUBLE; result.d = (fracpart2 == 0) ? 0.0 : tenD(fracpart2,(exp - sigfrac - fracpos)); if (fracpart1) { i = (sigfrac < maxdigl) ? sigfrac : maxdigl; i = exp - fracpos - i; result.d += tenD(fracpart1,i); } if (intpart2) { i = sigint - maxsigdigits; if (i < 0) i = 0; i += exp; result.d += tenD(intpart2,i); } i = sigint - maxdigl; if ( i < 0) i = 0; i += exp; result.d += tenD(intpart1,i); if (negsign) result.d = -result.d; return; } } // There might be an overflow. result.d = 0.0; j = ckaccum(result.d,fracpart2,(exp - sigfrac - fracpos)); if (j) { result.errmsg = (j > 0) ? "Double overflow" : "Double underflow"; return; } if (fracpart1) { i = (sigfrac < maxdigl) ? sigfrac : maxdigl; i = exp - fracpos - i; j = ckaccum(result.d,fracpart1,i); if (j) { result.errmsg = (j > 0) ? "Double overflow" : "Double underflow"; return; } } if (intpart2) { i = sigint - maxsigdigits; if (i < 0) i = 0; i += exp; j = ckaccum(result.d,intpart2,i); if (j) { result.errmsg = (j > 0) ? "Double overflow" : "Double underflow"; return; } } i = sigint - maxdigl; if ( i < 0) i = 0; i += exp; j = ckaccum(result.d,intpart1,i); if (j) { result.errmsg = (j > 0) ? "Double overflow" : "Double underflow"; return; } result.type = DOUBLE; return; } int FITS::trim_comment(const char *s, int len) { // trim blanks from end of comment and return the length for (--len; len >= 0 && s[len] == ' '; --len) ; return ++len; } int FITS::chk_comment(const char *s, int len) { while (len--) // check comment for invalid characters if (!FITS::isa_text(*s++)) return -1; return 0; } int FITS::get_comment(const char *s, int len, int &begpos) { // find the beginning of a comment and return the trimmed length int i; for (i = 0; i < len && s[i] == ' '; ++i) ; if (i < len && s[i] == '/') { ++i; if (i < len) { begpos = i; return trim_comment(&s[i],(len - i)); } begpos = 0; return 0; } begpos = 0; return trim_comment(s,len); } void FITS::fstr2str(char *target, const char *source, int len) { // len is the `real' length, i. e. '' counts as one char while (len--) { *target++ = *source++; if (len && (*source == '\'')) ++source; } } int FITS::str2fstr(char *target, const char *source, int len) { // len is the maximum size of the target int i = 0; // the actual number of F77 chars in target int j = 0; // the actual number of ascii chars in target while (*source && len) { if (len && (*source == '\'')) { *target++ = '\''; ++j; --len; if (len ==0) break; } *target++ = *source++; ++i; ++j; --len; } for (; (i < 8) && len; ++i, --len) *target++ = ' '; // target must be at least 8 chars long return j; } void FITS::parse_vatform(const char *s, FITS::ValueType &valType, int &maxelem) { int ok = 1; if (s && *s != '\0') { // if first character is a digit, must be 0 or 1 if (FITS::isa_digit(*s)) { if (!(*s == '0' || *s == '1')) ok = 0; s++; } // this char must be a P if (ok && *s != 'P') ok = 0; if (ok) s++; // this char must be a letter if (ok && FITS::isa_letter(*s)) { switch (*s) { case 'L': valType = FITS::LOGICAL; break; case 'X': valType = FITS::BIT; break; case 'B': valType = FITS::BYTE; break; case 'I': valType = FITS::SHORT; break; case 'J': valType = FITS::LONG; break; case 'A': valType = FITS::CHAR; break; case 'E': valType = FITS::FLOAT; break; case 'D': valType = FITS::DOUBLE; break; case 'C': valType = FITS::COMPLEX; break; case 'M': valType = FITS::DCOMPLEX; break; default: valType = FITS::NOVALUE; break; } s++; } else { ok = 0; } // this char must be a '(' if (ok && *s == '(') { s++; // get all the digits which make up the maxelem // skip leading zeros maxelem = -1; while (FITS::isa_digit(*s) && *s == '0') s++; if (FITS::isa_digit(*s)) maxelem = FITS::digit2bin(*s++); while (FITS::isa_digit(*s)) { maxelem = maxelem * 10 + FITS::digit2bin(*s++); } // at the end of all the above, must find a ')' if (*s != ')' || maxelem < 0) ok = 0; } else { ok = 0; } } if (!ok) { maxelem = -1; valType = FITS::NOVALUE; } } FitsParse::FitsParse(int max) : no_errs_(0), max_errs(max) { err_ = new const char * [max_errs]; // check for storage allocation errors if (err_ == 0) { cerr << "FitsParse cannot allocate storage -- exiting\n"; exit(1); } } FitsKeyword & FitsParse::mkerr(const char *s, int len) { int comm_len = FITS::trim_comment(s,len); if (FITS::chk_comment(s,comm_len)) seterr("Comment contains non-ASCII_text."); return *new FitsKeyword("ERROR",5,FITS::NOVALUE,0,0,s,comm_len); } FitsKeyword & FitsParse::parse(const char *s, int len) { FitsNameResult kword; FitsValueResult val; int isa_value_id; int value_id_pos; int comm_pos; int comm_len; int pos; int i, n; no_errs_ = 0; // reset the number of errors FITS::get_name(s,8,kword); // First, get the name in the name field if (!kword.isaname) { if (kword.begpos >= 8) { comm_len = FITS::trim_comment(&s[8],(len - 8)); if (FITS::chk_comment(&s[8],comm_len)) seterr("Comment contains non-ASCII_text."); return *new FitsKeyword(&FITS::ResWord.spaces(), 0,FITS::NOVALUE,0,0,&s[8],comm_len); } else { seterr("Invalid name field."); return mkerr(s,len); } } int namelen = kword.endpos - kword.begpos + 1; if (kword.begpos != 0) seterr("Name should be left-justified."); else if (namelen < 8) { for (i = kword.endpos + 1; s[i] == ' ' && i < 8; ++i) ; if (i < 8) { seterr("Invalid name field."); return mkerr(s,len); } } if (namelen > 8) seterr("Name cannot be greater than 8 chars."); if (strncmp(s,"COMMENT",namelen) == 0) { comm_len = FITS::trim_comment(&s[8],(len - 8)); if (FITS::chk_comment(&s[8],comm_len)) seterr("Comment contains non-ASCII_text."); return *new FitsKeyword(&FITS::ResWord.comment(), 0,FITS::NOVALUE,0,0,&s[8],comm_len); } if (strncmp(s,"HISTORY",namelen) == 0) { comm_len = FITS::trim_comment(&s[8],(len - 8)); if (FITS::chk_comment(&s[8],comm_len)) seterr("Comment contains non-ASCII_text."); return *new FitsKeyword(&FITS::ResWord.history(), 0,FITS::NOVALUE,0,0,&s[8],comm_len); } // At this point we have eliminated SPACES, HISTORY, and COMMENT // cards and have some sort of name, either a user-defined or // reserved name. // Get the value indicator pos = kword.endpos + 1; isa_value_id = FITS::get_value_id(&s[pos],(len - pos),value_id_pos); if (!isa_value_id) { n = FITS::ResWord.isreserved(&s[kword.begpos],kword.len); if (n != 0 && FITS::ResWord.requires_value(n)) { seterr("No value indicator -- reserved keyword \ must have a value."); return mkerr(s,len); } comm_len = FITS::trim_comment(&s[pos],(len - pos)); const char *comerr; const ReservedFitsKeyword *com = &FITS::ResWord.get(&s[kword.begpos], namelen,kword.isaindex,FITS::NOVALUE,0,0,comerr); if (comerr) seterr(comerr); if (com->name() == FITS::ERRWORD) return mkerr(s,len); if (FITS::chk_comment(&s[pos],comm_len)) seterr("Comment contains non-ASCII_text."); if (com->name() == FITS::USER_DEF) return *new FitsKeyword(&s[kword.begpos],namelen, FITS::NOVALUE,0,0,&s[pos],comm_len); else return *new FitsKeyword(com,0,FITS::NOVALUE,0,0,&s[pos],comm_len); }; if (strncmp(s,"END",namelen) == 0) { seterr("END keyword has a value indicator -- corrected"); return *new FitsKeyword(&FITS::ResWord.end_item(), 0,FITS::NOVALUE,0,0,0,0); } pos += value_id_pos; if ((pos++) != 8) seterr("Value indicator does not conform to FITS standard."); if (s[pos] != ' ') seterr("Value indicator must be `= \' -- corrected"); ++pos; if (pos == len) { n = FITS::ResWord.isreserved(&s[kword.begpos],kword.len); if (n != 0 && FITS::ResWord.requires_value(n)) { seterr("Reserved keyword must have a value."); return mkerr(s,len); } seterr("Value indicator without a value."); return mkerr(s,len); } // Get the value FITS::get_value(&s[pos],(len - pos),val); val.begpos += pos; val.endpos += pos; if (val.isa_point) val.pointpos += pos; if (val.errmsg) { seterr(val.errmsg); return mkerr(s,len); } if (val.type == FITS::NOVALUE) { n = FITS::ResWord.isreserved(&s[kword.begpos],kword.len); if (n != 0 && FITS::ResWord.requires_value(n)) { seterr("Reserved keyword must have a value."); return mkerr(s,len); } seterr("Value indicator without a value."); return mkerr(s,len); } const void *addrval = &val.l; if (val.type == FITS::STRING || val.type == FITS::FSTRING) { val.s[0] += pos; addrval = &s[val.s[0]]; } // Now, see if name, index, type, value matches a reserved word const char *reserr = 0; const ReservedFitsKeyword *res = &FITS::ResWord.get(&s[kword.begpos], kword.len,kword.isaindex,val.type,addrval,val.s[1],reserr); if (reserr){ seterr(reserr); } if (res->name() == FITS::ERRWORD) { n = FITS::ResWord.isreserved(&s[kword.begpos],kword.len); if ((n != 0) && FITS::ResWord.isunique(n) && (FITS::ResWord[n].type() != val.type)) { // This is a attempt to correct a common error -- integers // where there should be reals. Convert these to doubles. if (val.type == FITS::LONG && FITS::ResWord[n].type() == FITS::REAL) { val.type = FITS::DOUBLE; val.d = (double)val.l; seterr("... converted to type double."); reserr = 0; res = &FITS::ResWord.get(&s[kword.begpos],kword.len, kword.isaindex,val.type,addrval,val.s[1],reserr); if (reserr) seterr(reserr); } else return mkerr(s,len); } else return mkerr(s,len); } // At this stage we have a legitimate keyword. pos = val.endpos + 1; comm_len = FITS::get_comment(&s[pos],(len - pos),comm_pos); if (FITS::chk_comment(&s[comm_pos + pos],comm_len)) seterr("Comment contains non-ASCII_text."); if (res->name() == FITS::USER_DEF) return *new FitsKeyword(&s[kword.begpos],namelen, val.type,addrval,val.s[1],&s[comm_pos + pos],comm_len); else { switch (val.type) { // check for adherence to fixed format case FITS::FSTRING: case FITS::STRING: // allow for lenths < 8 characters even though that isn't // exactly fixed format. if (!(val.begpos == 10 && val.endpos <= 79)) seterr("String value does not conform to FITS fixed format."); break; case FITS::LOGICAL: if (!(val.begpos == 29 && val.endpos == 29)) seterr("Logical value does not conform to FITS fixed format."); break; default: // is numeric if (!(val.begpos >= 10 && val.endpos == 29)) seterr("Numeric value does not conform to FITS fixed format."); break; } return *new FitsKeyword(res,kword.index, val.type,addrval,val.s[1],&s[comm_pos + pos],comm_len); } } const void *FitsKeyword::value() const { switch (type_) { case FITS::LOGICAL: return &bval; case FITS::LONG: return &ival; case FITS::FLOAT: return &fval; case FITS::DOUBLE: return &dval; default: return val; } } FitsKeyword & FitsKeyword::operator = (const char *v) { int vlen = strlen(v); if (type_ == FITS::STRING && vlen <= vallen) { memcpy(val,v,vlen); ((char *)val)[vlen] = '\0'; vallen = vlen; } else { type_ = FITS::STRING; char *p = new char [vlen + 1]; memchk(p); memcpy(val,v,vlen); ((char *)val)[vlen] = '\0'; vallen = vlen; del_val(); val = p; } return *this; } void FitsKeyword::comm(const char *c) { if (c == 0) { delete [] comm_; comm_ = 0; commlen_ = 0; return; } int clen = strlen(c); if (clen <= commlen_) { memcpy(comm_,c,clen); comm_[clen] = '\0'; commlen_ = clen; } else { char *p = new char [clen + 1]; memchk(p); memcpy(p,c,clen); p[clen] = '\0'; commlen_ = clen; delete [] comm_; comm_ = p; } return; } void FitsKeyword::name(const char *n) { if (isreserved()) { err(name(),type(),val,"Cannot change name of reserved word"); return; } if (n == 0) { err(name(),type(),val,"User-defined name cannot be null"); return; } int nlen = strlen(n); if (nlen <= namelen_) { memcpy(name_,n,nlen); name_[nlen] = '\0'; namelen_ = nlen; } else { char *p = new char [nlen + 1]; memchk(p); memcpy(p,n,nlen); p[nlen] = '\0'; namelen_ = nlen; delete [] name_; name_ = p; } return; } void FitsKeyword::memchk(void *p) { if (p == 0) { cout << "Keyword: could not allocate memory.\n"; exit(-1); } } void FitsKeyword::err(const char *nm, const FITS::ValueType &ty, const void *val, const char *msg) { cout << "Keyword Error: name = " << nm << " of type " << ty << " and value "; FITS::valstr(cout,ty,val); cout << "\n\t" << msg << "\n"; } void FitsKeyword::setval(const FITS::ValueType &ty, const void *v, int vlen) { if (ty == FITS::STRING || ty == FITS::FSTRING) { int i, plen = vlen; if (vlen < 8) plen = 8; // FITS strings must be at least 8 chars char *p = new char [plen + 1]; memchk(p); if (ty == FITS::STRING) memcpy(p,v,vlen); else FITS::fstr2str(p,(const char *)v,vlen); for (i = vlen; i < 8; i++) p[i] = ' '; p[i] = '\0'; val = p; vallen = i; type_ = FITS::STRING; } else { type_ = ty; val = 0; vallen = 0; switch(type_) { case FITS::LOGICAL: bval = *((Bool *)v); break; case FITS::LONG: ival = *((Int *)v); break; case FITS::FLOAT: fval = *((float *)v); break; case FITS::DOUBLE: dval = *((double *)v); break; case FITS::ICOMPLEX: val = new IComplex; memchk(val); *((IComplex *)val) = *((IComplex *)v); break; case FITS::COMPLEX: val = new Complex; memchk(val); *((Complex *)val) = *((Complex *)v); break; case FITS::DCOMPLEX: val = new DComplex; memchk(val); *((DComplex *)val) = *((DComplex *)v); break; // The following "default" was added to prevent compilers // such as GNU g++ from giving warnings about enumeration // values not being handled. This should be cleaned up // some point. // -OO default: break; } } } void FitsKeyword::setcomm(const char *c, int clen) { if (c == 0) { comm_ = 0; commlen_ = 0; return; } comm_ = new char [clen + 1]; memchk(comm_); memcpy(comm_,c,clen); comm_[clen] = '\0'; commlen_ = clen; } void FitsKeyword::init(const FitsKeyword &k) { next_ = 0; prev_ = 0; setval(k.type_,k.value(),k.vallen); setcomm(k.comm_,k.commlen_); kw_ = k.kw_; ndx = k.ndx; namelen_ = k.namelen_; if (k.name_) { name_ = new char [namelen_ + 1]; memchk(name_); memcpy(name_,k.name_,namelen_); name_[namelen_] = '\0'; } } FitsKeyword::FitsKeyword(const char *nm, int nmlen, FITS::ValueType ty, const void *v, int vlen, const char *cm, int cmlen) { // Construct a user-defined keyword next_ = 0; prev_ = 0; kw_ = &FITS::ResWord.userdef_item(); ndx = 0; namelen_ = nmlen; name_ = new char [namelen_ + 1]; memchk(name_); memcpy(name_,nm,namelen_); name_[namelen_] = '\0'; setval(ty,v,vlen); setcomm(cm,cmlen); } FitsKeyword::FitsKeyword(const ReservedFitsKeyword *r, int nd, FITS::ValueType ty, const void *v, int vlen, const char *cm, int cmlen) { // Construct a reserved keyword next_ = 0; prev_ = 0; kw_ = r; ndx = nd; namelen_ = r->namesize(); name_ = 0; setval(ty,v,vlen); setcomm(cm,cmlen); } void FitsKeyword::del_val() { if (val == 0) return; switch (type_) { case FITS::STRING: case FITS::FSTRING: { char *p = (char *)val; delete [] p; } break; case FITS::ICOMPLEX: { IComplex *p = (IComplex *)val; delete p; } break; case FITS::COMPLEX: { Complex *p = (Complex *)val; delete p; } break; case FITS::DCOMPLEX: { DComplex *p = (DComplex *)val; delete p; } break; default: // This is ugly, but this design doesn't allow catching this at // compile time - so we throw an error at run time. // Each unique type allocated by new should have its own switch above. cerr << "FitsKeyword::del_val() internal error - unknown type"; cerr << " - exiting." << endl; exit(1); } } ostream & operator << (ostream &o, const FitsKeyword &x) { if (x.kw().name() == FITS::ERRWORD) o << "ERROR: \t "; else { o << x.name(); if (x.index() == 0) { if (x.kw().name() == FITS::USER_DEF) o << ":U:"; else o << ": "; } else o << "[" << x.index() << "]:"; o << "\t" << x.type(); } FITS::valstr(o,x.type(),x.value()); if (x.commlen()) o << " \"" << x.comm() << "\""; o << "\n"; return o; } FitsKeyword &FitsKeywordList::make(const char *nm, FITS::ValueType ty, const void *val, const char *cm) { FitsKeyword *kw; if (!nm) { return makeErrKeyword("", ty, val, "User defined name cannot be NULL."); } int nmlen = strlen(nm); if (nmlen > 8) { return makeErrKeyword(nm, ty, val, "User defined name cannot be > 8 characters long."); } int cmlen = 0; if (cm) cmlen = strlen(cm); int vallen = 0; if (ty == FITS::STRING) { if (val == 0) ty = FITS::NOVALUE; else { vallen = strlen((char *)val); if (vallen > 68) { return makeErrKeyword(nm, ty, val, "String values cannot be > 68 characters long."); } } } int valsize = (vallen < 8) ? 8 : vallen; const char *errmsg = 0; const ReservedFitsKeyword *rw = &FITS::ResWord.get(nm,nmlen, False, ty,val,valsize,errmsg); if (errmsg) FitsKeyword::err(nm,ty,val,errmsg); if (rw->name() == FITS::USER_DEF) kw = new FitsKeyword(nm,nmlen,ty,val,vallen,cm,cmlen); else kw = new FitsKeyword(rw,0,ty,val,vallen,cm,cmlen); FitsKeyword::memchk(kw); return *kw; } FitsKeyword &FitsKeywordList::make(FITS::ReservedName nm, FITS::ValueType ty, const void *val, const char *cm) { FitsKeyword *kw; int cmlen = 0; if (cm) cmlen = strlen(cm); int vallen = 0; if (ty == FITS::STRING) { if (val == 0) ty = FITS::NOVALUE; else { vallen = strlen((char *)val); if (vallen > 68) { return makeErrKeyword(FITS::ResWord.aname(nm), ty, val, "String values cannot be > 68 characters long."); } } } int valsize = (vallen < 8) ? 8 : vallen; const char *errmsg = 0; const ReservedFitsKeyword *rw = &FITS::ResWord.get(nm,False, ty,val,valsize,errmsg); if (errmsg) FitsKeyword::err(FITS::ResWord.aname(nm),ty,val,errmsg); if (rw->name() == FITS::USER_DEF) { return makeErrKeyword(FITS::ResWord.aname(nm), ty, val, "Function cannot be used for user defined keyword."); } else { kw = new FitsKeyword(rw,0,ty,val,vallen,cm,cmlen); } FitsKeyword::memchk(kw); return *kw; } FitsKeyword &FitsKeywordList::make(int ind, FITS::ReservedName nm, FITS::ValueType ty, const void *val, const char *cm) { FitsKeyword *kw; int cmlen = 0; if (cm) cmlen = strlen(cm); int vallen = 0; if (ty == FITS::STRING) { if (val == 0) ty = FITS::NOVALUE; else { vallen = strlen((char *)val); if (vallen > 68) { return makeErrKeyword(FITS::ResWord.aname(nm), ty, val, "String values cannot be > 68 characters long."); } } } int valsize = (vallen < 8) ? 8 : vallen; const char *errmsg = 0; const ReservedFitsKeyword *rw = &FITS::ResWord.get(nm,True, ty,val,valsize,errmsg); if (errmsg) FitsKeyword::err(FITS::ResWord.aname(nm),ty,val,errmsg); if (rw->name() == FITS::USER_DEF) { return makeErrKeyword(FITS::ResWord.aname(nm), ty, val, "Function cannot be used for user defined keyword."); } else { kw = new FitsKeyword(rw,ind,ty,val,vallen,cm,cmlen); } FitsKeyword::memchk(kw); return *kw; } FitsKeyword &FitsKeywordList::makeErrKeyword(const char *name, FITS::ValueType type, const void *val, const char *errmsg) { FitsKeyword::err(name,type,val,errmsg); FitsKeyword *kw = new FitsKeyword(&FITS::ResWord.err_item(),0,FITS::NOVALUE,0,0,0,0); FitsKeyword::memchk(kw); return *kw; } ostream & operator << (ostream &o, FitsKeywordList &w) { w.first(); FitsKeyword *x = w.next(); for (int i = 1; x != 0; ++i, x = w.next()) o << i << ". " << *x; return o; } FitsKeywordList::FitsKeywordList(const FitsKeywordList &w) : beg_(0), end_(0), pos(0), total(0), cursor(0) { FitsKeyword *k; for (FitsKeyword *x = w.beg_; x != 0; x = x->next_) { k = new FitsKeyword(*x); FitsKeyword::memchk(k); insert(*k); } } FitsKeywordList::FitsKeywordList(ConstFitsKeywordList &w) : beg_(0), end_(0), pos(0), total(0), cursor(0) { FitsKeyword *k; w.first(); for (const FitsKeyword *x = w.next(); x != 0; x = w.next()) { k = new FitsKeyword(*x); FitsKeyword::memchk(k); insert(*k); } } FitsKeywordList & FitsKeywordList:: operator = (const FitsKeywordList &w) { delete_all(); FitsKeyword *k; for (FitsKeyword *x = w.beg_; x != 0; x = x->next_) { k = new FitsKeyword(*x); FitsKeyword::memchk(k); insert(*k); } return *this; } std::string FitsKeywordList::toString() const { std::string s; s.resize(80*total); std::fill (s.begin(), s.end(), ' '); char* sptr = &(s[0]); for (FitsKeyword *x = beg_; x != 0; x = x->next_) { FitsKeyCardTranslator::fmtcard (sptr, *x); sptr += 80; } return s; } FitsKeyword *FitsKeywordList::next() { if (cursor == total) { return 0; } if (cursor == 0) { ++cursor; return pos; } pos = pos->next_; ++cursor; return pos; } FitsKeyword *FitsKeywordList::prev() { if (cursor == 0) { return 0; } FitsKeyword *x = pos; if (pos->prev_) pos = pos->prev_; --cursor; return x; } // Return the i-th keyword -- keyword numbering starts with 0 FitsKeyword *FitsKeywordList::operator () (int n) { if (n < 0 || n >= total) return 0; first(); while(n--) next(); return curr(); } void FitsKeywordList::insert(FitsKeyword &k) { if (cursor == 0) { k.next_ = beg_; isempty() ? (end_ = &k) : (beg_->prev_ = &k); beg_ = &k; } else if (cursor == total) { k.prev_ = end_; isempty() ? (beg_ = &k) : (end_->next_ = &k); end_ = &k; } else { k.next_ = pos->next_; k.prev_ = pos; pos->next_->prev_ = &k; pos->next_ = &k; } pos = &k; ++cursor; ++total; } void FitsKeywordList::del() { if (isempty()) return; if (cursor == 0) { pos = beg_->next_; pos ? (pos->prev_ = 0) : (end_ = 0); delete beg_; beg_ = pos; --total; } else if (cursor == total) { pos = end_->prev_; pos ? (pos->next_ = 0) : (beg_ = 0); delete end_; end_ = pos; --total; cursor = total; } else { FitsKeyword *x = pos->prev_; pos->next_->prev_ = x; if (x) { x->next_ = pos->next_; delete pos; pos = x; } else { beg_ = pos->next_; delete pos; pos = beg_; } --total; --cursor; } } void FitsKeywordList::delete_all() { last(); while (!isempty()) del(); } FitsKeyword *FitsKeywordList::next(const FITS::ReservedName &n) { FitsKeyword *x; for (x = next(); x != 0; x = next()) if (x->isreserved() && !(x->isindexed()) && (n == x->kw().name())) break; return x ? curr() : 0; } FitsKeyword *FitsKeywordList::next(const FITS::ReservedName &n, int ndx) { FitsKeyword *x; for (x = next(); x != 0; x = next()) if (x->isreserved() && (x->index() == ndx) && (n == x->kw().name())) break; return x ? curr() : 0; } FitsKeyword *FitsKeywordList::next(const char *w) { FitsKeyword *x; for (x = next(); x != 0; x = next()) if (strcmp(w,x->name()) == 0) break; return x ? curr() : 0; } int FitsKeywordList::rules(FitsKeyword &x, FITSErrorHandler errhandler) { // apply rules to x against the keyword list // return: 0 = no errors, 1 = minor errors, -1 = major errors static char msgstring[180]; // storage for composing error messages if (x.kw().name() == FITS::USER_DEF) return 0; if (x.kw().name() == FITS::ERRWORD) return -1; switch (x.kw().name()) { case FITS::NAXIS: if (x.isindexed()) { if ((*this)(FITS::NAXIS) == 0) { errhandler("There is no NAXIS keyword", FITSError::SEVERE); return -1; } else { if (x.index() >= 1 && x.index() <= curr()->asInt()) { if (x.asInt() < 0) { ostringstream msgline; msgline << "Illegal value for keyword NAXIS" << x.index(); strncpy(msgstring, msgline.str().c_str(), sizeof(msgstring)-1); errhandler(msgstring, FITSError::SEVERE); return -1; } } } } break; case FITS::END: // there must be no comment if (x.commlen() != 0) { errhandler("Comments are not allowed on keyword END", FITSError::WARN); return 1; } break; case FITS::TBCOL: // for index between 1 and TFIELDS, value must be >= 0 if ((*this)(FITS::TFIELDS) == 0) { errhandler("There is no TFIELDS keyword", FITSError::SEVERE); return -1; } else { if (x.index() >= 1 && x.index() <= curr()->asInt()) { if (x.asInt() < 0) { ostringstream msgline; msgline << "Illegal value for keyword TBCOL" << x.index(); strncpy(msgstring, msgline.str().c_str(), sizeof(msgstring)-1); errhandler(msgstring, FITSError::SEVERE); return -1; } } } break; case FITS::BLANK: // BITPIX must exist and be positive if ((*this)(FITS::BITPIX) == 0) { errhandler("There is no BITPIX keyword", FITSError::SEVERE); return -1; } else { if (curr()->asInt() < 0) { // Used to be an error. Make it a warning instead. errhandler("Keyword BLANK not allowed when BITPIX < 0", FITSError::WARN); return 0; } } break; // The following "default" was added to prevent compilers // such as GNU g++ from giving warnings about enumeration // values not being handled. This should be cleaned up // some point. // -OO default: break; } return 0; } int FitsKeywordList::rules(FITSErrorHandler errhandler) { int rtn = 0; int n; FitsKeyword *endkey = 0; //first(); FitsKeyword *x; for (x = beg_; x != 0; x = x->next_) { n = rules(*x,errhandler); if (n != 0 && (rtn == 0 || (rtn == 1 && n == -1))) rtn = n; if (x->isreserved() && (x->kw().name() == FITS::END)) { endkey = x; break; } } if (!endkey) { errhandler("Keyword list has no END keyword.", FITSError::SEVERE); rtn = -1; } else { for (x = x->next_; x != 0; x = x->next_) { if (!(x->isreserved() && (x->kw().name() == FITS::SPACES) && x->commlen() == 0)) { errhandler("END keyword is not the last keyword.", FITSError::SEVERE); rtn = -1; } } } return rtn; } Bool FitsKeywordList::basic_rules() { int rtn = 0; const char *msg = 0; for (FitsKeyword *x = beg_; x != 0; x = x->next_) { rtn = FITS::ResWord.rules(x->kw(),x->name(),x->namelen(), x->isindexed(),x->type(),x->value(),x->valStrlen(),msg); if (rtn != 0 || msg != 0) return False; } return True; } FitsKeyCardTranslator::FitsKeyCardTranslator(int max) : cardno(0), max_errs(max), no_errs_(0) { err_ = new const char * [max_errs]; err_cardno_ = new int [max_errs]; blanks = new char [FitsRecSize]; for (int i = 0; i < FitsRecSize; ++i) blanks[i] = ' '; // check for storage allocation errors } FitsKeywordList &FitsKeyCardTranslator::parse(const char *buff, FitsKeywordList &kwlist, int count, FITSErrorHandler errhandler, Bool show_err) { char msgstring[180]; // storage for composing error messages int i, j; cardno = 0; int end_found = 0; for (i = 0; i < 36; ++i) { ++cardno; kwlist.parse(&buff[i*80],80); if (show_err && (kwlist.no_parse_errs() > 0)) { FITSError::ErrorLevel errlev = FITSError::INFO; if (strcmp(kwlist.curr()->name(),"ERROR") == 0) errlev = FITSError::WARN; ostringstream msgline; msgline << "FITS card " << (count * 36 + cardno) << ": "; msgline.write(&buff[i*80],80); strncpy(msgstring, msgline.str().c_str(), sizeof(msgstring)-1); errhandler(msgstring, errlev); for (j = 0; j < kwlist.no_parse_errs(); ++j) { errhandler(kwlist.parse_err(j), errlev); } } if (end_found) { if (kwlist.curr()->isreserved() && kwlist.curr()->kw().name() == FITS::SPACES && kwlist.curr()->commlen() == 0) kwlist.del(); else { if (no_errs_ < max_errs) { ostringstream msgline; msgline << "FITS card " << (count * 36 + cardno) << ": "; msgline.write(&buff[i*80],80); strncpy(msgstring, msgline.str().c_str(), sizeof(msgstring)-1); errhandler(msgstring, FITSError::WARN); errhandler("Invalid card after END keyword.", FITSError::WARN); } } } if (kwlist.curr()->isreserved() && kwlist.curr()->kw().name() == FITS::END) { end_found = 1; break; // don't attempt to read beyond END card (fails on ASCII null) } } return kwlist; } int FitsKeyCardTranslator::build(char *rec, FitsKeywordList &kw) { // Beginning at the current location in kw and at the beginning // of rec, reformat each keyword in kw for the FITS header output. // If the end of the list was encountered, return 0, otherwise 1. memcpy(rec,blanks,FitsRecSize); char *card = rec; FitsKeyword *x = kw.curr(); for (cardno = 0; cardno < FitsMaxCard && x != 0; ++cardno, card +=FitsCardSize, x = kw.next()) fmtcard(card,*x); return ( x ? 1 : 0 ); } void FitsKeyCardTranslator::fmtcard(char *card, const FitsKeyword &k) { int i, n; char c[3]; memcpy(card,k.name(),k.namelen()); if (k.isreserved() && k.isindexed()) { n = k.index(); for (i = 0; n > 0; ++i, n /= 10) c[i] = n % 10 + '0'; for (--i, n = k.namelen(); i >= 0; ++n, --i) card[n] = c[i]; } if (k.type() == FITS::NOVALUE) { if ((n = (k.commlen() <= 72 ? k.commlen() : 72))) memcpy(&card[8],k.comm(),n); } else if (k.type() == FITS::STRING) { card[8] = '='; card[10] = '\''; n = FITS::str2fstr(&card[11],k.asString(),69); card[11 + n] = '\''; if (k.commlen()) { i = 14 + n; if (i <= 30) { card[31] = '/'; if ((n = (k.commlen() <= 48 ? k.commlen() : 48))) memcpy(&card[32],k.comm(),n); } else { if (i < 80) { card[i - 1] = '/'; if ((n = (k.commlen() <= (80 - i) ? k.commlen() : (80 - i)))) memcpy(&card[i],k.comm(),n); } } } } else { card[8] = '='; switch (k.type()) { case FITS::LOGICAL: card[29] = (k.asBool() == True ? 'T' : 'F'); break; case FITS::LONG: snprintf(&card[18],FitsCardSize-18,"%12d",k.asInt()); card[30] = ' '; break; case FITS::FLOAT: snprintf(&card[16],FitsCardSize-16,"%#14.7E",k.asFloat()); card[30] = ' '; break; case FITS::DOUBLE: snprintf(&card[10],FitsCardSize-10,"%#20.12E",k.asDouble()); // optimum %23.15E for (i = 29; i < 10; --i) // change the E to a D if (card[i] == 'E') { card[i] = 'D'; break; } card[30] = ' '; break; case FITS::ICOMPLEX: snprintf(&card[18],FitsCardSize-18,"%12d",k.asIComplex().real()); card[30] = ' '; snprintf(&card[38],FitsCardSize-38,"%12d",k.asIComplex().imag()); card[50] = ' '; break; case FITS::COMPLEX: snprintf(&card[16],FitsCardSize-16,"%#14.6E",k.asComplex().real()); snprintf(&card[36],FitsCardSize-36,"%#14.6E",k.asComplex().imag()); card[50] = ' '; break; case FITS::DCOMPLEX: snprintf(&card[10],FitsCardSize-10,"%#20.12E",k.asDComplex().real()); snprintf(&card[30],FitsCardSize-30,"%#20.12E",k.asDComplex().imag()); for (i = 29; i < 10; --i) // change E to a D if (card[i] == 'E') { card[i] = 'D'; break; } for (i = 49; i < 30; --i) // change E to a D if (card[i] == 'E') { card[i] = 'D'; break; } card[50] = ' '; break; // The following "default" was added to prevent compilers // such as GNU g++ from giving warnings about enumeration // values not being handled. This should be cleaned up // some point. // -OO default: break; } if (k.commlen()) { if (!(k.type() == FITS::ICOMPLEX || k.type() == FITS::DCOMPLEX)) { card[31] = '/'; if ((n = (k.commlen() <= 48 ? k.commlen() : 48))) memcpy(&card[32],k.comm(),n); } else { card[51] = '/'; if ((n = (k.commlen() <= 28 ? k.commlen() : 28))) memcpy(&card[52],k.comm(),n); } } } } // For some amazing reason the following wouldn't inline FITS::ReservedName ReservedFitsKeyword::name() const { return name_; } Bool FitsFPUtil::isFP(const float *) {return True;} Bool FitsFPUtil::isFP(const double *) {return True;} Bool FitsFPUtil::isFP(const void *) {return False;} void FitsFPUtil::setNaN(double &val) { unsigned char *cptr = (unsigned char *)(&val); for (unsigned int i=0; i # include # include # include # include # include # include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# All FITS code seems to assume longs are 4 bytes. Currently //# this corresponds to an "int" on all useful platforms. typedef Int FitsLong; //# recovered by GYL //# Forward declarations class ReservedFitsKeywordCollection; class FitsNameResult; class FitsValueResult; class FitsKeyword; class FitsParse; // FITS templated helper class // // // // NoConvert is a template class that is not intended for // general use, it is used internally. // template class NoConvert { public: NoConvert() { } void operator = (int) {; } }; // FITS helper class // // // // FitsLogical is a helper class that is not intended for // general use. // // // Here is an example of the FitsLogical class. // // FitsLogical x; // FitsLogical y(True); // FitsLogical z = x; // ... // x = y; y = False; x.undefine(); // Bool b; // if (x.isdefined()) // b = x; // b = y; If y is undefined, b will be false. // // class FitsLogical { friend ostream & operator << (ostream &o, const FitsLogical &); public: FitsLogical() : v('\0') { } FitsLogical(Bool x) : v(x == True ? 'T' : 'F') { } FitsLogical & operator = (Bool x) { v = (x == True ? 'T' : 'F'); return *this; } ///ARO 2021-02-20: ///Removed the following function, because it seems incorrectly implemented and isn't used ///Bool isdefined() const { return v == '\0' ? True : False; } void undefine() { v = '\0'; } operator Bool() const { return v == 'T'; } protected: char v; }; // helper class for FITS Binary Tables // // // // This class is not intended for general use. It only has meaning // in the context of FITS Binary tables. There its use is incorporated // into the concept of a FitsField, where FitsBit is given a specialized // interpretation. // class FitsBit { public: FitsBit() : bit_array(0) { } FitsBit(unsigned char x) : bit_array(x) { } FitsBit & operator = (unsigned char x) { bit_array = x; return *this; } operator unsigned char() const { return bit_array; } protected: unsigned char bit_array; }; // Variable Length Array Descriptor // // class FitsVADesc { friend ostream & operator << (ostream &o, const FitsVADesc &); public: FitsVADesc() : no_elements(0), rel_offset(0) { } FitsVADesc(const FitsVADesc &x) : no_elements(x.no_elements), rel_offset(x.rel_offset) { } FitsVADesc & operator = (const FitsVADesc &x) { no_elements= x.no_elements; rel_offset = x.rel_offset; return *this; } FitsVADesc(int n, int o) : no_elements(n), rel_offset(o) { } void set(int n, int o) { no_elements = n; rel_offset = o; } int num() const { return no_elements; } int offset() const { return rel_offset; } protected: int no_elements; int rel_offset; }; // static functions and enumerations // // // // Many of the static functions are utility functions used internally in the // implementation of the member functions of the FITS classes. They are placed // in a single class to encapsulate them and to avoid adding many names to the // global name space. More important, from the user's perspective, are the // enumerations. They form the basic vocabulary of a FITS application. For example, // instead of referring to the FITS NAXIS keyword, // FITS::NAXIS should be used // class FITS { public: // FITS I/O Error message types // Basic FITS Data Types for keywords and data enum ValueType { NOVALUE = 0, LOGICAL = 1, BIT = 2, CHAR = 3, BYTE = 4, SHORT = 5, LONG = 6, FLOAT = 7, DOUBLE = 8, COMPLEX = 9, ICOMPLEX = 10, DCOMPLEX = 11, VADESC = 12, STRING, FSTRING, REAL }; // REAL means either FLOAT or DOUBLE // STRING and FSTRING are used internally in parsing keywords static FITS::ValueType getfitstype(NoConvert x) { x=0; return FITS::LOGICAL; } static FITS::ValueType getfitstype(NoConvert x) { x=0; return FITS::BIT; } static FITS::ValueType getfitstype(NoConvert x) { x=0; return FITS::CHAR; } static FITS::ValueType getfitstype(NoConvert x) { x=0; return FITS::BYTE; } static FITS::ValueType getfitstype(NoConvert x) { x=0; return FITS::SHORT; } static FITS::ValueType getfitstype(NoConvert x) { x=0; return FITS::LONG; } static FITS::ValueType getfitstype(NoConvert x) { x=0; return FITS::LONG; } static FITS::ValueType getfitstype(NoConvert x) { x=0; return FITS::FLOAT; } static FITS::ValueType getfitstype(NoConvert x) { x=0; return FITS::DOUBLE; } static FITS::ValueType getfitstype(NoConvert x) { x=0; return FITS::COMPLEX; } static FITS::ValueType getfitstype(NoConvert x) { x=0; return FITS::ICOMPLEX; } static FITS::ValueType getfitstype(NoConvert x) { x=0; return FITS::DCOMPLEX; } static FITS::ValueType getfitstype(NoConvert x) { x=0; return FITS::VADESC; } static int fitssize(FITS::ValueType t); static int localsize(FITS::ValueType t); // data conversion routines: FITS - local static void f2l(FitsLogical *,void *,int); static void l2f(void *,FitsLogical *,int); static void f2l(FitsBit *,void *,int); static void l2f(void *,FitsBit *,int); static void f2l(char *,void *,int); static void l2f(void *,char *,int); static void f2l(unsigned char *,void *,int); static void l2f(void *,unsigned char *,int); static void f2l(short *,void *,int); static void l2f(void *,short *,int); static void f2l(Int *,void *,int); static void l2f(void *,Int *,int); static void f2l(long *,void *,int); static void l2f(void *,long *,int); static void f2l(float *,void *,int); static void l2f(void *,float *,int); static void f2l(double *,void *,int); static void l2f(void *,double *,int); static void f2l(Complex *,void *,int); static void l2f(void *,Complex *,int); static void f2l(IComplex *,void *,int); static void l2f(void *,IComplex *,int); static void f2l(DComplex *,void *,int); static void l2f(void *,DComplex *,int); static void f2l(FitsVADesc *,void *,int); static void l2f(void *,FitsVADesc *,int); static void swap2(void *, void *, int); static void swap4(void *, void *, int); static void swap8(void *, void *, int); // FITS Reserved Names. PZERO is named strangely because it can conflict with // a standard #define in sys/param.h. enum ReservedName { USER_DEF, AUTHOR, BITPIX, BLANK, BLOCKED, BSCALE, BUNIT, BZERO, CDELT, COMMENT, CROTA, CRPIX, CRVAL, CTYPE, DATAMAX, DATAMIN, DATE, DATE_OBS, END, EPOCH, EQUINOX, EXTEND, EXTLEVEL, EXTNAME, EXTVER, GCOUNT, GROUPS, HISTORY, INSTRUME, NAXIS, OBJECT, OBSERVER, ORIGIN, PCOUNT, PSCAL, PTYPE, PZERO_FITS, REFERENC, SIMPLE, SPACES, TBCOL, TDIM, TDISP, TELESCOP, TFIELDS, TFORM, THEAP, TNULL, TSCAL, TTYPE, TUNIT, TZERO, XTENSION, ERRWORD, ALTRPIX, DATE_MAP }; // Types of FITS Records enum FitsRecType { InitialState, BadBeginningRecord, HDURecord, UnrecognizableRecord, SpecialRecord, EndOfFile }; // Supported FITS Physical Devices enum FitsDevice { Disk, Std, Tape9 }; // Types of FITS Header-Data Units enum HDUType { NotAHDU, PrimaryArrayHDU, PrimaryGroupHDU, AsciiTableHDU, BinaryTableHDU, ImageExtensionHDU, UnknownExtensionHDU, PrimaryTableHDU }; // Options on FITS array manipulations enum FitsArrayOption { NoOpt = 0, CtoF = 1, FtoC = 2}; static ReservedFitsKeywordCollection &ResWord; static void valstr(ostream &o, const ValueType &ty, const void *val); static Bool isa_digit(char c); static int digit2bin(char c); static Bool isa_text(char c); static Bool isa_letter(char); static int letter2bin(char); static void fstr2str(char *, const char *, int); static int str2fstr(char *, const char *, int); static void get_name(const char *s, int len, FitsNameResult &result); static int get_value_id(const char *s, int l, int &pos); static void get_value(const char *s, int len, FitsValueResult &result); static int trim_comment(const char *s, int len); static int chk_comment(const char *s, int len); static int get_comment(const char *s, int len, int &begpos); static void get_numeric(const char *s, int len, FitsValueResult &result); // utility function to parse the binary table variable array // column (i.e. uses the heap) of the form nPt(dddd) where n // is either 0 or 1, t is one of the standard FITS binary table // column types and dddd is the maximum number of elements used // by this column. If there is a format error in the input // string (*s), then valType will have the value NOVALUE and // maxelem will be -1. static void parse_vatform(const char *s, FITS::ValueType &valType, int &maxelem); static const Int minInt; static const Int maxInt; static const float minfloat; static const float maxfloat; static const double mindouble; static const double maxdouble; private: FITS(); static double tenpowerD[309]; static float tenpowerF[39]; static const int minfltexp; static const int maxfltexp; static const int mindblexp; static const int maxdblexp; static const int maxsigdigits; static const int maxdigl; // max digits in a long static const int maxexpdig; // max digits in an exponent static double tenD(Int, int); static float tenF(Int, int); static int ckaccum(double &, Int, int); static int ckaccum(float &, Int, int); }; inline FITS::FITS() { } // just a dummy function to prevent instantiation inline Bool FITS::isa_digit(char c) { return isdigit(c) ? True : False; } inline int FITS::digit2bin(char c) { return c - '0'; } inline Bool FITS::isa_text(char c) { return isprint(c) ? True : False; } inline Bool FITS::isa_letter(char c) { return isupper(c) ? True : False; } inline int FITS::letter2bin(char c) { return c - 'A'; } ostream & operator << (ostream &, const FITS::ValueType &); inline double FITS::tenD(Int numb, int pow) { return (pow > 0) ? (((double)numb) * tenpowerD[pow]) : ((pow < 0) ? (((double)numb) / tenpowerD[-pow]) : ((double)numb)); } inline float FITS::tenF(Int numb, int pow) { return (pow > 0) ? (((float)numb) * tenpowerF[pow]) : ((pow < 0) ? (((float)numb) / tenpowerF[-pow]) : ((float)numb)); } // reserved FITS keyword // // class ReservedFitsKeyword { public: const char *aname() const; FITS::ReservedName name() const; int namesize() const; FITS::ValueType type() const; Bool isindexed() const; Bool isessential() const; # if defined(TURBOCPP) // It is best for the following to be private, but // C-Front won't allow an initializer list if they are private. // This issue isn't that crucial since functions in // ReservedFitsKeywordCollection always return const items. private: # endif FITS::ReservedName name_; const char *aname_; int namesize_; FITS::ValueType type_; Bool isindexed_; // 0 = NOT INDEXED, 1 = INDEXED Bool isessential_; // 0 = NO, 1 = YES }; inline const char *ReservedFitsKeyword::aname() const { return aname_; } inline int ReservedFitsKeyword::namesize() const { return namesize_; } inline FITS::ValueType ReservedFitsKeyword::type() const { return type_; } inline Bool ReservedFitsKeyword::isindexed() const { return isindexed_; } inline Bool ReservedFitsKeyword::isessential() const { return isessential_; } // collection of reserved FITS keywords // // class ReservedFitsKeywordCollection { public: const ReservedFitsKeyword & operator [] (int i) const; int no() const; const ReservedFitsKeyword &get(FITS::ReservedName, Bool, FITS::ValueType, const void *, int, const char *&) const; const ReservedFitsKeyword &get(const char *, int, Bool, FITS::ValueType, const void *, int, const char *&) const; const char *aname(FITS::ReservedName) const; int essential_name(const char *, int) const; const ReservedFitsKeyword &get_essential(int, Bool, FITS::ValueType, const void *, int, const char *&) const; int isreserved(const char *, int) const; Bool isunique(int) const; Bool requires_value(int) const; const ReservedFitsKeyword &userdef_item() const; const ReservedFitsKeyword &err_item() const; const ReservedFitsKeyword &end_item() const; const ReservedFitsKeyword &spaces() const; const ReservedFitsKeyword &comment() const; const ReservedFitsKeyword &history() const; int rules(const ReservedFitsKeyword &, const char *, int, Bool, FITS::ValueType, const void *, int, const char *&) const; private: static const int no_items; // number of entries in the table static const ReservedFitsKeyword &user_def_item; // user-defined keyword static const ReservedFitsKeyword &error_item; // error in keyword static const ReservedFitsKeyword &end__item; static const ReservedFitsKeyword &spaces_item; static const ReservedFitsKeyword &comment_item; static const ReservedFitsKeyword &history_item; static const ReservedFitsKeyword resword[]; // table of reserved words static const int resalpha[26]; // alphabetic index to table const ReservedFitsKeyword &match(int, const char *, int, Bool, FITS::ValueType, const void *, int, const char *&) const; }; inline const ReservedFitsKeyword & ReservedFitsKeywordCollection:: operator [] (int i) const { return resword[i]; } inline int ReservedFitsKeywordCollection::no() const { return no_items; } inline Bool ReservedFitsKeywordCollection::isunique(int i) const { return (Bool)(resword[i + 1].name() != resword[i].name()); } inline const ReservedFitsKeyword &ReservedFitsKeywordCollection::userdef_item() const { return user_def_item; } inline const ReservedFitsKeyword &ReservedFitsKeywordCollection::err_item() const { return error_item; } inline const ReservedFitsKeyword &ReservedFitsKeywordCollection::end_item() const { return end__item; } inline const ReservedFitsKeyword &ReservedFitsKeywordCollection::spaces() const { return spaces_item; } inline const ReservedFitsKeyword &ReservedFitsKeywordCollection::comment() const { return comment_item; } inline const ReservedFitsKeyword &ReservedFitsKeywordCollection::history() const { return history_item; } // analyse the name of a header card // // // // Analyse the name of a header card // class FitsNameResult { public: Bool isaname; // 1 if there is a name present, otherwise 0 int begpos; // beginning position of name int endpos; // ending position of name Bool isaindex; // whether an index is present or not int index; // index if present int len; // length of name without index enum ErrMsg { OK = 0, NO_0_NDX }; ErrMsg err; }; // analyse the value of a header card // // // // Analyse the value of a header card // class FitsValueResult { public: FITS::ValueType type; union { Bool b; int s[2]; // for strings, s[0] is offset, s[1] length Int l; float f; double d; }; Complex c; IComplex lc; DComplex dc; int begpos; // beginning position of value int endpos; // ending position of value Bool isa_point; // 1 if a point, otherwise 0 int pointpos; // position of point, if any int no_sig; // number of significant digits const char *errmsg; // error message, if any }; // parse a header card // // // // parse a header card // class FitsParse { friend class FitsKeywordList; public: FitsKeyword &parse(const char *, int); // Parsing one string int no_errs() const; const char *err(int) const; private: FitsParse(int = 10); ~FitsParse(); int no_errs_; const int max_errs; const char **err_; int seterr(const char *); FitsKeyword &mkerr(const char *s, int len); }; inline FitsParse::~FitsParse() { delete [] err_; } inline int FitsParse::no_errs() const { return no_errs_; } inline const char *FitsParse::err(int i) const { return err_[i]; } inline int FitsParse::seterr(const char *s) { return no_errs_ < max_errs ? ( err_[no_errs_++] = s, 0) : -1; } // FITS keyword // // // // A FITS keyword contains a name, a value and a comment. // class FitsKeyword { friend class FitsKeywordList; friend class FitsParse; // A word about friends: FitsKeywordList accesses the next and prev // pointers and the FitsKeyword constructors. // FitsParse only accesses the FitsKeyword constructors. public: FitsKeyword(const FitsKeyword &); FitsKeyword & operator = (const FitsKeyword &); ~FitsKeyword(); // // get info about the name const char *name() const; int namelen() const; Bool isreserved() const; Bool isindexed() const; const ReservedFitsKeyword &kw() const; int index() const; // // // access the keyword comment const char *comm() const; int commlen() const; // // access the error status int err() const; // the datatype of the keyword FITS::ValueType type() const; // access the value of the keyword // Bool asBool() const; const char *asString() const; int valStrlen() const; Int asInt() const; float asFloat() const; double asDouble() const; IComplex asIComplex() const; Complex asComplex() const; DComplex asDComplex() const; const void *value() const; // // change the value of the keyword // FitsKeyword & operator = (Bool); FitsKeyword & operator = (const char *); FitsKeyword & operator = (Int); FitsKeyword & operator = (float); FitsKeyword & operator = (double); FitsKeyword & operator = (IComplex); FitsKeyword & operator = (Complex); FitsKeyword & operator = (DComplex); // // change the comment of the keyword void comm(const char *); // change the name of the keyword void name(const char *); private: FitsKeyword *next_; FitsKeyword *prev_; // // the keyword name // if name_ is 0, keyword is not a user defined name // if ndx is 0, there is no index char *name_; const ReservedFitsKeyword *kw_; int ndx; short namelen_; // // // the keyword comment // if comm_ is 0, there is no comment char *comm_; short commlen_; // // // the keyword value FITS::ValueType type_; union { Bool bval; Int ival; float fval; double dval; }; void *val; // pointer to allocated value, if any short vallen; // only used for string data void del_val(); // does an appropriate delete based on type // void init(const FitsKeyword &); void setval(const FITS::ValueType &, const void *, int); void setcomm(const char *, int); static void err(const char *, const FITS::ValueType &, const void *, const char *); static void memchk(void *); // // private constructors for use by friends // constructs user-defined keywords // parms: name, namelen, type, val, vallen, comm, commlen FitsKeyword(const char *, int , FITS::ValueType, const void *, int, const char *, int); // constructs reserved keywords // parms: resword, index, val, vallen, comm, commlen FitsKeyword(const ReservedFitsKeyword *, int, FITS::ValueType, const void *, int, const char *, int); // }; ostream & operator << (ostream &, const FitsKeyword &); inline FitsKeyword::FitsKeyword(const FitsKeyword &k) : next_(0), prev_(0), name_(0), kw_(0), comm_(0), val(0) { init(k); } inline FitsKeyword & FitsKeyword::operator = (const FitsKeyword &k) { delete [] name_; delete [] comm_; del_val(); init(k); return *this; } inline FitsKeyword::~FitsKeyword() { delete [] name_; delete [] comm_; del_val(); } inline const ReservedFitsKeyword &FitsKeyword::kw() const { return *kw_; } inline Bool FitsKeyword::isreserved() const { return (kw().name() != FITS::ERRWORD && kw().name() != FITS::USER_DEF) ? True : False; } inline const char *FitsKeyword::name() const { return isreserved() ? kw().aname() : (namelen_ ? name_ : ""); } inline int FitsKeyword::namelen() const { return namelen_; } inline Bool FitsKeyword::isindexed() const {return ndx > 0 ? True : False;} inline int FitsKeyword::index() const { return ndx; } inline const char *FitsKeyword::comm() const { return comm_ ? comm_ : ""; } inline int FitsKeyword::commlen() const { return commlen_; } inline int FitsKeyword::err() const { return (kw().name() == FITS::ERRWORD); } inline FITS::ValueType FitsKeyword::type() const { return type_; } inline Bool FitsKeyword::asBool() const { return bval; } inline const char *FitsKeyword::asString() const { return vallen ? (const char *)val : ""; } inline int FitsKeyword::valStrlen() const { return vallen; } inline Int FitsKeyword::asInt() const { if( type() != FITS::LONG ) { cerr << "Unexpected keyword type in FitsKeyword::asInt()\n"; exit(1); } return ival; } inline float FitsKeyword::asFloat() const { switch( type() ) { case FITS::BYTE: case FITS::SHORT: case FITS::LONG: return (float)ival; case FITS::FLOAT: return fval; case FITS::DOUBLE: return (float)dval; default: cerr << "Unexpected keyword type in asFloat()\n"; exit(1); } return 0.0; } inline double FitsKeyword::asDouble() const { switch( type() ) { case FITS::BYTE: case FITS::SHORT: case FITS::LONG: return (double)ival; case FITS::FLOAT: return (double)fval; case FITS::DOUBLE: return dval; default: cerr << "Unexpected keyword type in asDouble()\n"; exit(1); } return 0.0; } inline IComplex FitsKeyword::asIComplex() const { return *((IComplex *)val); } inline Complex FitsKeyword::asComplex() const { return *((Complex *)val); } inline DComplex FitsKeyword::asDComplex() const { return *((DComplex *)val); } inline FitsKeyword & FitsKeyword::operator = (Bool x) { bval = x; type_ = FITS::LOGICAL; return *this; } inline FitsKeyword & FitsKeyword::operator = (Int x) { ival = x; type_ = FITS::LONG; return *this; } inline FitsKeyword & FitsKeyword::operator = (float x) { fval = x; type_ = FITS::FLOAT; return *this; } inline FitsKeyword & FitsKeyword::operator = (double x) { dval = x; type_ = FITS::DOUBLE; return *this; } inline FitsKeyword & FitsKeyword::operator = (IComplex x) { *((IComplex *)val) = x; type_ = FITS::ICOMPLEX; return *this; } inline FitsKeyword & FitsKeyword::operator = (Complex x) { *((Complex *)val) = x; type_ = FITS::COMPLEX; return *this; } inline FitsKeyword & FitsKeyword::operator = (DComplex x) { *((DComplex *)val) = x; type_ = FITS::DCOMPLEX; return *this; } class ConstFitsKeywordList; // forward declaration // linked list of FITS keywords // // // // A linked list of FITS keywords. // class FitsKeywordList { public: FitsKeywordList(); ~FitsKeywordList(); FitsKeywordList(const FitsKeywordList &); FitsKeywordList(ConstFitsKeywordList &); FitsKeywordList & operator = (const FitsKeywordList &); // Convert the list to a string containing the 80-byte FITS headers. std::string toString() const; // delete the current keyword (the thing returned by curr()) from the list void del(); // Add (make) a reserved keyword with the given value and optional comment // The comment will be truncated if necessary to fit the available space. // String values must be less than 69 characters. String values longer than // that will result in an ERROR keyword instead of the desired keyword. // void mk(FITS::ReservedName k, Bool v, const char *c = 0); void mk(FITS::ReservedName k, const char *v = 0, const char *c = 0); void mk(FITS::ReservedName k, Int v, const char *c = 0); void mk(FITS::ReservedName k, long v, const char *c = 0); void mk(FITS::ReservedName k, double v, const char *c = 0); // // Add (make) an indexed reserved keyword with the given value and optional comment // The comment will be truncated if necessary to fit the available space. // String values must be less than 69 characters. String values longer than // that will result in an ERROR keyword instead of the desired keyword. // void mk(int n, FITS::ReservedName k, Bool v, const char *c = 0); void mk(int n, FITS::ReservedName k, const char *v, const char *c = 0); void mk(int n, FITS::ReservedName k, Int v, const char *c = 0); void mk(int n, FITS::ReservedName k, long v, const char *c = 0); void mk(int n, FITS::ReservedName k, double v, const char *c = 0); // // Add (make) a user defined keyword with the given name, value and optional comment. // The comment will be truncated if necessary to fit the available space. // The name must be no longer than 8 characters. Names longer than that will // result in an ERROR keyword instead of the desired keyword. // String values must no longer than 69 characters. String values longer than // that will result in an ERROR keyword instead of the desired keyword. // void mk(const char *n, Bool v, const char *c = 0); void mk(const char *n, const char *v = 0, const char *c = 0); void mk(const char *n, Int v, const char *c = 0); void mk(const char *n, long v, const char *c = 0); void mk(const char *n, float v, const char *c = 0); void mk(const char *n, double v, const char *c = 0); void mk(const char *n, Int r, Int i, const char *c = 0); void mk(const char *n, float r, float i, const char *c = 0); void mk(const char *n, double r, double i, const char *c = 0); // // add a spaces line void spaces(const char *n = 0, const char *c = 0); // add a comment card void comment(const char *n = 0, const char *c = 0); // add a history card void history(const char *c = 0); // add the end card. This must be at the end of the list. void end(); // Retrieve specific keywords -- these also set the current mark // // return the i-th keyword -- keyword numbering starts with 0 FitsKeyword * operator () (int); // return first and next non-indexed reserved keyword FitsKeyword * operator () (const FITS::ReservedName &); FitsKeyword * next(const FITS::ReservedName &); // return first and next indexed reserved keyword FitsKeyword * operator () (const FITS::ReservedName &, int); FitsKeyword * next(const FITS::ReservedName &, int); // return first and next user-defined keyword FitsKeyword * operator () (const char *); FitsKeyword * next(const char *); // // Bool isempty() const; void first(); void last(); FitsKeyword *next(); FitsKeyword *prev(); FitsKeyword *curr(); // // void delete_all(); int rules(FitsKeyword &, FITSErrorHandler errhandler = FITSError::defaultHandler); int rules(FITSErrorHandler errhandler = FITSError::defaultHandler); Bool basic_rules(); // // // For parsing a single string void parse(const char *, int); int no_parse_errs() const; const char *parse_err(int) const; // void insert(FitsKeyword &); private: FitsKeyword *beg_; FitsKeyword *end_; FitsKeyword *pos; int total; int cursor; FitsKeyword &make(const char *nm, FITS::ValueType t, const void *v, const char *c); FitsKeyword &make(FITS::ReservedName nm, FITS::ValueType t, const void *v, const char *c); FitsKeyword &make(int ind, FITS::ReservedName nm, FITS::ValueType t, const void *v, const char *c); // construct an error keyword - this happens when a name is invalid (NULL // or more than 8 characters) or a string value is too long (more than // 69 characters). It is the responsibility of the caller to the // several mk functions to ensure that that doesn't happen. By the time // it gets here, it is assumed that such problems are true errors. // This is used by the private make functions. FitsKeyword &makeErrKeyword(const char *name, FITS::ValueType type, const void *val, const char *errmsg); FitsParse card; }; ostream & operator << (ostream &o, FitsKeywordList &); // print the entire list inline FitsKeywordList::FitsKeywordList() : beg_(0), end_(0), pos(0), total(0), cursor(0) { } inline FitsKeywordList::~FitsKeywordList() { delete_all(); } inline Bool FitsKeywordList::isempty() const { return total == 0 ? True : False; } inline void FitsKeywordList::first() { cursor = 0; pos = beg_; } inline void FitsKeywordList::last() { cursor = total; pos = end_; } inline FitsKeyword *FitsKeywordList::curr() { return pos; } inline FitsKeyword *FitsKeywordList::operator () (const FITS::ReservedName &n) { first(); return next(n); } inline FitsKeyword *FitsKeywordList::operator () (const FITS::ReservedName &n, int ndx) { first(); return next(n,ndx); } inline FitsKeyword *FitsKeywordList::operator () (const char *w) { first(); return next(w); } inline void FitsKeywordList::parse(const char *s, int l) { insert(card.parse(s,l)); } inline int FitsKeywordList::no_parse_errs() const { return card.no_errs();} inline const char *FitsKeywordList::parse_err(int n) const { return card.err(n); } // FitsKeyword constructors for non-indexed Reserved keywords inline void FitsKeywordList::mk(FITS::ReservedName k, Bool v, const char *c) { insert(make(k,FITS::LOGICAL,&v,c)); } inline void FitsKeywordList::mk(FITS::ReservedName k, const char *v, const char *c) { insert(make(k,FITS::STRING,v,c)); } inline void FitsKeywordList::mk(FITS::ReservedName k, Int v, const char *c) { insert(make(k,FITS::LONG,&v,c)); } inline void FitsKeywordList::mk(FITS::ReservedName k, long v, const char *c) { insert(make(k,FITS::LONG,&v,c)); } inline void FitsKeywordList::mk(FITS::ReservedName k, double v, const char *c) { insert(make(k,FITS::DOUBLE,&v,c)); } // FitsKeyword constructors for indexed Reserved keywords inline void FitsKeywordList::mk(int n, FITS::ReservedName k, Bool v, const char *c) { Bool tmp; tmp = v; insert(make(n,k,FITS::LOGICAL,&tmp,c)); } inline void FitsKeywordList::mk(int n, FITS::ReservedName k, const char *v, const char *c) { insert(make(n,k,FITS::STRING,v,c)); } inline void FitsKeywordList::mk(int n, FITS::ReservedName k, Int v, const char *c) { insert(make(n,k,FITS::LONG,&v,c)); } inline void FitsKeywordList::mk(int n, FITS::ReservedName k, long v, const char *c) { insert(make(n,k,FITS::LONG,&v,c)); } inline void FitsKeywordList::mk(int n, FITS::ReservedName k, double v, const char *c) { insert(make(n,k,FITS::DOUBLE,&v,c)); } // FitsKeyword constructors for User-Defined keywords inline void FitsKeywordList::mk(const char *n, Bool v, const char *c) { Bool tmp; tmp = v; insert(make(n,FITS::LOGICAL,&tmp,c)); } inline void FitsKeywordList::mk(const char *n, const char *v, const char *c) { insert(make(n,FITS::STRING,v,c)); } inline void FitsKeywordList::mk(const char *n, Int v, const char *c) { insert(make(n,FITS::LONG,&v,c)); } inline void FitsKeywordList::mk(const char *n, long v, const char *c) { insert(make(n,FITS::LONG,&v,c)); } inline void FitsKeywordList::mk(const char *n, float v, const char *c) { insert(make(n,FITS::FLOAT,&v,c)); } inline void FitsKeywordList::mk(const char *n, double v, const char *c) { insert(make(n,FITS::DOUBLE,&v,c)); } inline void FitsKeywordList::mk(const char *n, Int r, Int i, const char *c) { IComplex v(r,i); insert(make(n,FITS::ICOMPLEX,&v,c)); } inline void FitsKeywordList::mk(const char *n, float r, float i, const char *c) { Complex v(r,i); insert(make(n,FITS::COMPLEX,&v,c)); } inline void FitsKeywordList::mk(const char *n, double r, double i, const char *c) { DComplex v(r,i); insert(make(n,FITS::DCOMPLEX,&v,c)); } // Additional keyword constructors for commentary, etc. inline void FitsKeywordList::spaces(const char *n, const char *c) { insert((n == 0 ? make(FITS::SPACES,FITS::NOVALUE,0,c) : (c == 0 ? make(FITS::SPACES,FITS::NOVALUE,0,n) : make(n,FITS::NOVALUE,0,c)))); } inline void FitsKeywordList::comment(const char *n, const char *c) { insert((n == 0 ? make(FITS::COMMENT,FITS::NOVALUE,0,c) : (c == 0 ? make(FITS::COMMENT,FITS::NOVALUE,0,n) : make(n,FITS::NOVALUE,0,c)))); } inline void FitsKeywordList::history(const char *c) { insert(make(FITS::HISTORY,FITS::NOVALUE,0,c)); } inline void FitsKeywordList::end() { insert(make(FITS::END,FITS::NOVALUE,0,0)); } // list of read-only FITS keywords // // class ConstFitsKeywordList { public: ConstFitsKeywordList(FitsKeywordList &x) : kw(x) { } const FitsKeyword * operator () (int n) { return kw(n); } const FitsKeyword * operator () (const FITS::ReservedName &x) { return kw(x); } const FitsKeyword * next(const FITS::ReservedName &x) { return kw.next(x); } const FitsKeyword * operator () (const FITS::ReservedName &x, int n) { return kw(x,n); } const FitsKeyword * next(const FITS::ReservedName &x, int n) { return kw.next(x,n); } const FitsKeyword * operator () (const char *x) { return kw(x); } const FitsKeyword * next(const char *x) { return kw.next(x); } Bool isempty() const { return kw.isempty(); } void first() { kw.first(); } void last() { kw.last(); } const FitsKeyword *next() { return kw.next(); } const FitsKeyword *prev() { return kw.prev(); } const FitsKeyword *curr() { return kw.curr(); } private: FitsKeywordList &kw; }; // translator between Keyword lists and fixed FITS cars // // // // also contains the parser ??? // class FitsKeyCardTranslator { public: FitsKeyCardTranslator(int = 100); ~FitsKeyCardTranslator(); FitsKeywordList & parse(const char *, FitsKeywordList &, int, FITSErrorHandler, Bool); int build(char *, FitsKeywordList &); int no_errs() const; const char *err(int) const; int err_cardno(int) const; static void fmtcard(char *, const FitsKeyword &); private: int cardno; // the current card number within record static constexpr int FitsCardSize = 80; static constexpr int FitsMaxCard = 36; static constexpr int FitsRecSize = 2880; int max_errs; int no_errs_; const char **err_; int *err_cardno_; char *blanks; }; inline FitsKeyCardTranslator::~FitsKeyCardTranslator() { delete [] err_; delete [] err_cardno_; delete [] blanks; } inline int FitsKeyCardTranslator::no_errs() const { return no_errs_; } inline const char *FitsKeyCardTranslator::err(int i) const { return err_[i]; } inline int FitsKeyCardTranslator::err_cardno(int i) const { return err_cardno_[i]; } // Utility functions for floating point values // // class FitsFPUtil { public: // These functions are useful to tell if some type is a floating point type. // This is useful in a templated function, where the processing can vary // depending on whether the type is FP or not (e.g. blank handling). // static Bool isFP(const float *); static Bool isFP(const double *); static Bool isFP(const void *); // // For blanking purposes, we need to be able to get a NaN. The NaN we set // is all bits on. // static void setNaN(double &val); static void setNaN(float &val); // }; } //# NAMESPACE CASACORE - END # endif casacore-3.7.1/fits/FITS/fitsio.cc000066400000000000000000001475521476623553700166600ustar00rootroot00000000000000//# fitsio.cc: //# Copyright (C) 1993,1994,1995,1996,1997,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA # include # include # include # include # include # include namespace casacore { //# NAMESPACE CASACORE - BEGIN // using cfitsio of NASA // can't use "" to replace <>. // The following is included through fitsio.h-->blockio.h already! //# include //# include // FitsIO::~FitsIO() { } FitsInput::~FitsInput() { delete &m_fin; } FitsDiskInput::~FitsDiskInput() { } FitsDiskOutput::~FitsDiskOutput() { } FitsStdInput::~FitsStdInput() { } FitsStdOutput::~FitsStdOutput() { } FitsTape9Input::~FitsTape9Input() { } FitsTape9Output::~FitsTape9Output() { } //# Cache used to hold errors from read_header_rec, messages and accompanying error levels Block messages_(32); Block errLevels_(32); uInt nerrs_ = 0; //============================================================================================ // special error handler function used by read_header_rec void readHeaderRecErrHandler(const char *errMessage, FITSError::ErrorLevel severity) { if (nerrs_ >= messages_.nelements()) { uInt newSize = messages_.nelements() * 2; messages_.resize(newSize, True, True); errLevels_.resize(newSize, True, True); } messages_[nerrs_] = String(errMessage); errLevels_[nerrs_] = Int(severity); nerrs_++; } //============================================================================================= void FitsInput::errmsg(FitsErrs e, const char *s) { //cout<<"[FitsInput::errmsg] called."<Fptr)->bytepos - (m_iosize - m_current)) / m_recsize + n; int l_totalrow = ((m_fptr->Fptr)->filesize) / m_recsize; if (l_endrow >= l_totalrow) { errmsg(READERR, "Attempt to read past end of file [FitsdiskInput::skip()]"); return (0); } // move the i/o pointer to the end position of the skipped block. // (m_iosize - m_current ) is the bytes of data left within the m_buffer // still need to test this part with big fits file. OFF_T bytpost = (m_fptr->Fptr)->bytepos + (m_recsize * uInt(n)) - (m_iosize - m_current); int l_status = 0; ffmbyt(m_fptr, bytpost, REPORT_EOF, &l_status); if (l_status) { fits_report_error(stderr, l_status); /* print error report */ errmsg(READERR, "bytepos setting error [FitsdiskInput::skip()]"); return (0); } else { // (m_iosize-m_current) is in the previous m_block_no already. int l_phy_rec = (n - (m_iosize - m_current) / m_recsize) / m_nrec; m_block_no += l_phy_rec; m_rec_no += n; m_iosize = 0; // the m_buffer data is all used. m_current = 0; // reset the buffer position to beginning. } return read(); } //=============================================================================================== BlockInput &FitsInput::make_input(const char *n, const FITS::FitsDevice &d, int b, FITSErrorHandler errhandler) { BlockInput *bptr = 0; switch (d) { case FITS::Disk: bptr = new FitsDiskInput(n, m_recsize, b, errhandler); break; case FITS::Tape9: //errmsg(IOERR,"FITS::Tape9 is not supported. Please use FITS::DISK!"); bptr = new FitsTape9Input(n, m_recsize, b, errhandler); break; case FITS::Std: bptr = new FitsStdInput(m_recsize, errhandler); break; } // Dereferences a null pointer if "d" was not caught in the above switch. return *bptr; } //============================================================================================= void FitsOutput::errmsg(FitsErrs e, const char *s) { static char msgstring[180]; ostringstream msgline; msgline << "FitsOutput error: "; if (m_fout.fname() == 0 || *m_fout.fname() == '\0') msgline << "File Descriptor " << m_fout.fdes(); else msgline << "File " << m_fout.fname(); msgline << " Physical record " << m_fout.blockno() << " logical record " << m_fout.recno() << " --\n\t" << s << endl; m_err_status = e; // all FitsIO output error messages are SEVERE strncpy(msgstring, msgline.str().c_str(), sizeof(msgstring) - 1); m_errfn(msgstring, FITSError::SEVERE); } //============================================================================================== BlockOutput &FitsOutput::make_output(const char *n, const FITS::FitsDevice &d, int b, FITSErrorHandler errhandler) { BlockOutput *bptr = 0; switch (d) { case FITS::Disk: bptr = new FitsDiskOutput(n, m_recsize, b, errhandler); break; case FITS::Tape9: //errmsg(IOERR,"FITS::Tape9 is not supported. Please use FITS::DISK!"); bptr = new FitsTape9Output(n, m_recsize, b, errhandler); break; case FITS::Std: bptr = new FitsStdOutput(m_recsize, errhandler); break; } // Dereferences a null pointer if "d" was not caught in the above // switch. return *bptr; } //======================================================================================== FitsOutput::FitsOutput(const char *n, const FITS::FitsDevice &d, int b, FITSErrorHandler errhandler) : FitsIO(errhandler), m_fout(make_output(n, d, b, errhandler)), m_required_keys_only(TRUE) { if (m_fout.err()) { m_rec_type = FITS::EndOfFile; errmsg(IOERR, "Error constructing output"); return; } m_curr = new char[m_recsize]; if (!m_curr) { m_rec_type = FITS::EndOfFile; errmsg(MEMERR, "Could not allocate storage for output buffer."); } m_fptr = m_fout.getfptr(); } //========================================================================================= FitsOutput::FitsOutput(FITSErrorHandler errhandler) : FitsIO(errhandler), m_fout(*(BlockOutput *) (new FitsStdOutput(m_recsize, errhandler))), m_required_keys_only(TRUE) { if (m_fout.err()) { m_rec_type = FITS::EndOfFile; errmsg(IOERR, "Error constructing output"); return; } m_curr = new char[m_recsize]; if (!m_curr) { m_rec_type = FITS::EndOfFile; errmsg(MEMERR, "Could not allocate storage for output buffer."); } } //========================================================================================== FitsOutput::~FitsOutput() { if (hdu_inprogress()) { errmsg(BADOPER, "ERROR! Output closed before HDU was complete."); } delete &m_fout; delete[] m_curr; } //======================================================================================== void FitsInput::init() { if (m_fin.err()) errmsg(IOERR, "[FitsInput::init()] Failed to construct input"); else { //cout<<"[FitsInput::init()] First call to BlockInput::read()." << endl; m_curr = m_fin.read(); m_got_rec = True; if (!m_curr) { errmsg(EMPTYFILE, "[FitsInput::init()] This is an empty file"); m_rec_type = FITS::EndOfFile; return; } if (m_fin.err()) { errmsg(IOERR, "[FitsInput::init()] Failed to read the first record"); m_rec_type = FITS::BadBeginningRecord; return; } m_kc.parse(m_curr, m_kw, 0, m_errfn, True); // get the fitsfile pointer m_fptr = m_fin.getfptr(); HeaderDataUnit::HDUErrs n; if (!HeaderDataUnit::determine_type(m_kw, m_hdu_type, m_data_type, m_errfn, n)) { errmsg(BADBEGIN, "[FitsInput::init()] Unrecognizable record at the beginning"); m_rec_type = FITS::BadBeginningRecord; return; } if (!(m_hdu_type == FITS::PrimaryArrayHDU || m_hdu_type == FITS::PrimaryTableHDU || m_hdu_type == FITS::PrimaryGroupHDU)) { errmsg(NOPRIMARY, "[FitsInput::init()] Missing primary header-data unit"); } else { m_isaprimary = True; if (m_kw(FITS::SIMPLE)->asBool() == True) m_valid_fits = True; else m_errfn( "Value of keyword SIMPLE is FALSE; this file may not be a " "valid FITS file [FitsInput::init()].", FITSError::WARN); if (m_kw(FITS::EXTEND)) if (m_kw.curr()->asBool() == True) m_extend = True; } m_rec_type = FITS::HDURecord; // Next block of code is to get the total number of hdu in this fits file // remember the cfitsio bytepos before calling any cfitsio function OFF_T l_bytepos = (m_fptr->Fptr)->bytepos; // get the total number of hdu in this fits file int l_status = 0; if (ffthdu(m_fptr, &m_thdunum, &l_status) > 0) { fits_report_error(stderr, l_status); // print error report errmsg(IOERR, "[FitsInput::init()] Failed to get total number of HDU."); return; } // set the cfitsio bytepos to what it was at begnning of this method. if (l_bytepos < ((m_fptr->Fptr)->filesize)) { if (ffmbyt(m_fptr, l_bytepos, REPORT_EOF, &l_status) > 0) { fits_report_error(stderr, l_status); // print error report errmsg(BADOPER, "[FitsInput::init()] bytepos setting error!"); } } else { (m_fptr->Fptr)->bytepos = l_bytepos; } } } //=============================================================================================== // return the header of the chdu as a Vector of Strings. Vector FitsInput::kwlist_str(Bool length80) { Vector cards; if (!m_header_done) { cout << "[FitsInput::kwlist_str()] If you need call this method, " "you should do so before reading any data from CHDU." << endl; return cards; } else { // remember the cfitsio bytepos before calling any cfitsio function OFF_T l_bytepos = (m_fptr->Fptr)->bytepos; int l_keysexist = 0, l_morekeys = 0, l_status = 0; // get the total number of keywords in the chdu if (ffghsp(m_fptr, &l_keysexist, &l_morekeys, &l_status)) { fits_report_error(stderr, l_status); // print error report cout << "[FitsInput::kwlist_str()] Failed to get total number of keywords in CHDU." << endl; return cards; } // get every card image as a char* and store them into cards. char cardImg[81]; ; cards.resize(l_keysexist + 1); for (int keynum = 1; keynum < l_keysexist + 1; keynum++) { if (ffgrec(m_fptr, keynum, cardImg, &l_status)) { // error reading card fits_report_error(stderr, l_status); // print error report errmsg(BADOPER, "[FitsInput::kwlist_str()] Failed to read the card!"); } else { String onecard(cardImg); cards[keynum - 1] = onecard; } } // since keysexist does not count the END keyword, we add it in. String endCard("END"); cards[l_keysexist] = endCard; // set the cfitsio bytepos to what it was at begnning of this method. if (l_bytepos < ((m_fptr->Fptr)->filesize)) { if (ffmbyt(m_fptr, l_bytepos, REPORT_EOF, &l_status) > 0) { fits_report_error(stderr, l_status); // print error report errmsg(BADOPER, "[FitsInput::kwlist_str()] bytepos setting error!"); } } else { (m_fptr->Fptr)->bytepos = l_bytepos; } // The next block is added by Neil Killeen if (length80) { String tmp( " "); // 12345678901234567890123456789012345678901234567890123456789012345678901234567890 for (uInt i = 0; i < cards.nelements(); i++) { String tmp2(tmp); tmp2.replace(0, cards(i).length(), cards(i)); cards(i) = tmp2; } } return cards; } } //================================================================================================ // read a special record or an unrecognizable record char * FitsInput::read_sp() { m_err_status = OK; if (m_rec_type == FITS::BadBeginningRecord) { if (m_got_rec) { m_got_rec = False; return m_curr; } m_curr = m_fin.read(); if (!m_curr) { m_rec_type = FITS::EndOfFile; m_got_rec = True; return 0; } if (m_fin.err()) { errmsg(IOERR, "[FitsInput::read_sp()] Failed to read a sp record."); return m_curr; } m_kw.delete_all(); m_kc.parse(m_curr, m_kw, 0, m_errfn, True); HeaderDataUnit::HDUErrs n; if (!HeaderDataUnit::determine_type(m_kw, m_hdu_type, m_data_type, m_errfn, n)) { return m_curr; } if (!(m_hdu_type == FITS::PrimaryArrayHDU || m_hdu_type == FITS::PrimaryGroupHDU || m_hdu_type == FITS::PrimaryTableHDU)) { errmsg(NOPRIMARY, "[FitsInput::read_sp()] Missing primary header-data unit."); } else { if (m_kw(FITS::SIMPLE)->asBool() == True) { m_valid_fits = True; } else { m_errfn( "[FitsInput::read_sp()] Value of keyword SIMPLE is FALSE; this" " file may not be a valid FITS file.", FITSError::WARN); } if (m_kw(FITS::EXTEND)) { if (m_kw.curr()->asBool() == True) { m_extend = True; } } } m_rec_type = FITS::HDURecord; m_got_rec = True; return 0; } else if (m_rec_type == FITS::UnrecognizableRecord) { if (m_got_rec) { m_got_rec = False; return m_curr; } m_curr = m_fin.read(); if (!m_curr) { m_rec_type = FITS::EndOfFile; m_got_rec = True; return 0; } if (m_fin.err()) { errmsg(IOERR, "[FitsInput::read_sp()] Failed to read a unrecognizable record."); return m_curr; } m_kw.delete_all(); m_kc.parse(m_curr, m_kw, 0, m_errfn, True); HeaderDataUnit::HDUErrs n; if (!HeaderDataUnit::determine_type(m_kw, m_hdu_type, m_data_type, m_errfn, n)) { return m_curr; } m_rec_type = FITS::HDURecord; m_got_rec = True; return 0; } else if (m_rec_type == FITS::SpecialRecord) { if (m_got_rec) { m_got_rec = False; return m_curr; } m_curr = m_fin.read(); if (!m_curr) { m_rec_type = FITS::EndOfFile; m_got_rec = True; m_err_status = OK; return 0; } if (m_fin.err()) { errmsg(IOERR, "[FitsInput::read_sp()] Failed to read a sp record."); return m_curr; } m_err_status = OK; return m_curr; } return 0; } //======================================================================================================== // implement read_head_rec() with CFITSIO of NASA void FitsInput::read_header_rec() { // make the next hdu be the chdu of cfitsio and set the file position pointer at the begining of the hdu. int l_status = 0, l_hdutype = 0, l_chdunum = 0; // get the number of the current hdu. This function returns the number of // chdu, not an error code. So we do not check the return. ffghdn(m_fptr, &l_chdunum); // if there is more hdu, make the next hdu be the current hdu if (l_chdunum < m_thdunum) { if (ffmrhd(m_fptr, 1, &l_hdutype, &l_status) > 0) { fits_report_error(stderr, l_status); // print error report errmsg(IOERR, "[FitsInput::read_header_rec()] Failed to move to the next hdu"); return; } } else { // reach the end of the fits file, end the program gracefully. m_curr = m_fin.read(); m_got_rec = True; if (!m_curr) { //cout << "[FitsInput::read_header_rec()] Reached the end of the FITS file" << endl; m_rec_type = FITS::EndOfFile; return; } if (m_fin.err()) { errmsg(IOERR, "[FitsInput::read_header_rec()] Failed to read first record of new header"); m_rec_type = FITS::UnrecognizableRecord; return; } //cout << "[FitsInput::read_header_rec()] Extra bytes after the end of the FITS file" << endl; m_rec_type = FITS::EndOfFile; return; } // since ffmrhd() reads the header, we need to move the file pointer back // to the beginning of the hdu before calling m_fin.read() OFF_T l_headstart, l_datastart, l_dataend; l_status = 0; // get size info of the current HDU if (ffghof(m_fptr, &l_headstart, &l_datastart, &l_dataend, &l_status) > 0) { fits_report_error(stderr, l_status); // print error report errmsg(BADSIZE, "[FitsInput::read_header_rec()] Failed to get the size of data."); return; } // move file pointer to the beginning of the new hdu. l_status = 0; if (ffmbyt(m_fptr, l_headstart, REPORT_EOF, &l_status)) { fits_report_error(stderr, l_status); // print error report errmsg(IOERR, "[FitsInput::read_header_rec()] Failed to move the file pointer to beginning."); } // reset m_iosize so that next m_fin.read() will start from the beginning of next hdu. m_fin.reset_iosize(); // end of the new code m_curr = m_fin.read(); m_got_rec = True; if (!m_curr) { //cout << "[FitsInput::read_header_rec()] Reached the end of the FITS file" << endl;; m_rec_type = FITS::EndOfFile; return; } if (m_fin.err()) { errmsg(IOERR, "[FitsInput::read_header_rec()] Failed to read the first record of new header"); m_rec_type = FITS::UnrecognizableRecord; return; } m_kw.delete_all(); // reset the cache counter nevertheless nerrs_ = 0; m_kc.parse(m_curr, m_kw, 0, readHeaderRecErrHandler, True); //cout << "[ FitsInput::read_header_rec()] Number of errors from parsing: nerrs_ = " << nerrs_ <>FitsInput::read_header_rec() - hdu_type=" << m_hdu_type << endl; if (!HeaderDataUnit::determine_type(m_kw, m_hdu_type, m_data_type, readHeaderRecErrHandler, n)) { // in this case, the header is completely bogus, the error messages which // convey that are the ones returned by determine_type, the ones returned // by parse are useless and needlessly confusing, so don't show them //cout<< "[ FitsInput::read_header_rec()] Error mesages from determin_type(): " << endl; for (uInt i = parseErrs; i < nerrs_; i++) { m_errfn(messages_[i].chars(), FITSError::ErrorLevel(errLevels_[i])); } nerrs_ = 0; m_rec_type = FITS::SpecialRecord; return; } //cout << "< 0) { fits_report_error(stderr, l_status); // print error report errmsg(BADSIZE, "[FitsInput::read_header_rec()] Failed to get the size of data."); return (int) m_err_status; } m_skipHDU_size = l_dataend - l_headstart; // -------------------------------------------------------------------------- int l_found_end = 0, l_namelen = 0; for (int nextkey = 1; !l_found_end; nextkey++) { // get next keyword // don't use ffgkyn here because it trys to parse the card to read // the value string, thus failing to read the file just because of // minor syntax errors in optional keywords. if (ffgrec(m_fptr, nextkey, l_card, &l_status) > 0) // get the 80-byte card { if (l_status == KEY_OUT_BOUNDS) { l_found_end = 1; // simply hit the end of the header l_status = 0; // reset error status //cout<<"[FitsInput::skip_hdu()] Found END keyword "< 0) { // test keyword name; catches no END snprintf( l_message, sizeof(l_message), "Name of keyword no. %d contains illegal character(s): %s", nextkey, l_keyname); errmsg(MISSKEY, l_message); if (nextkey % 36 == 0) { // test if at beginning of 36-card record. errmsg(MISSKEY, "[FitsInput::skip_hdu()] Possible missing END keyword."); return (int) m_err_status; } } if (!strcmp(l_keyname, "END")) { l_found_end = 1; } } } // -------------------------------------------------------------------------- // These functions don't work for END keyword! /*if(ffgcrd( m_fptr, l_keyname, l_card, &l_status )){ //if(ffgkey( m_fptr, l_keyname, l_keyval, l_comm, &l_status )) fits_report_error(stderr, l_status); // print error report errmsg(BADOPER,"[FitsInput::skip_hdu()] Missing END keyword."); return -1; } */ // check if the m_extend data member is set l_status = 0; if (!m_extend) { //l_keyname = "EXTEND"; strcpy(l_keyname, "EXTEND"); if (!ffgkey(m_fptr, l_keyname, l_keyval, l_comm, &l_status)) { if (l_keyval[0] == 'T') m_extend = True; } } // reset the m_iosize to 0, so that next m_fin.read() will start from where the file position pointer is; // Since read_header_rec() will move the chdu forward for one, no need to call ffmrhd() here. And // read_header_rec() will also move the cfitsio bytepos. m_fin.reset_iosize(); // read header to get ready for process_header read_header_rec(); // this will set the current hdu_type etc. if (err()) { return (int) m_err_status; } return 0; } //================================================================================= int FitsInput::process_header(FITS::HDUType t, FitsKeywordList &uk) { //cout << "[FitsInput::process_header] t=" << t << " hdu_type=" << m_hdu_type // << " m_header_done=" << m_header_done << endl; m_err_status = OK; m_item_size = 0; m_data_type = FITS::NOVALUE; m_data_size = 0; m_bytepos = 0; m_curr_size = 0; if (m_rec_type != FITS::HDURecord) { errmsg(BADOPER, "[FitsInput::process_header()] Not a hdu record"); return -1; } if (m_hdu_type != t) { errmsg(BADOPER, "[FitsInput::process_header()] mismatch hdu type"); return -1; } if (m_header_done) { //errmsg(BADOPER,"[FitsInput::process_header()] header already done"); //return -1; read_header_rec(); return 0; } uk.delete_all(); uk = m_kw; int cnt = 0; int i; FitsKeyword *x, *y; uk.first(); y = uk.next(); // set the list pointer for (;; m_kc.parse(m_curr, uk, cnt, m_errfn, True)) { // The worst error is if there is no END keyword. uk.last(); x = uk.prev(); // do backwards search for END if (x->kw().name() == FITS::END) break; while (x != y) { x = uk.prev(); if (x->kw().name() == FITS::END) break; } if (x->kw().name() == FITS::END) break; uk.last(); y = uk.prev(); // reset the list pointer uk.last(); // return list iterator to last position m_curr = m_fin.read(); // read the next record if (!m_curr) { errmsg(BADEOF, "[FitsInput::process_header()] Unexpected end of file"); m_rec_type = FITS::EndOfFile; return -1; } if (m_fin.err()) { errmsg(IOERR, "[FitsInput::process_header()] Unrecognizable record"); m_rec_type = FITS::UnrecognizableRecord; return -1; } ++cnt; // This attempts to deal with the problem of no END keyword // by searching for non-text data in the first 8 bytes. for (i = 0; i < 8; ++i) if (!FITS::isa_text(m_curr[i])) break; if (i < 8) { errmsg(MISSKEY, "[FitsInput::process_header()] Missing END keyword. Non-text data " "found in name field.\n\tEnd of keywords assumed."); break; } } //cout << "[ FitsInput::process_header()] keyword list uk:\n" << uk << endl; if (!m_extend) { if (uk(FITS::EXTEND)) if (uk.curr()->asBool() == True) m_extend = True; } HeaderDataUnit::HDUErrs n; Int nd; if (!HeaderDataUnit::compute_size(uk, m_data_size, nd, m_hdu_type, m_data_type, m_errfn, n)) { errmsg(BADSIZE, "[FitsInput::process_header()] Failed to compute size of data."); m_rec_type = FITS::UnrecognizableRecord; return -1; } //cout << "t=" << t << " m_curr_size=" << m_curr_size << " m_data_size=" << m_data_size << endl; //cout << "m_hdu_type=" << m_hdu_type << " m_header_done=" << m_header_done << endl; m_item_size = FITS::fitssize(m_data_type); m_curr_size = m_data_size; m_header_done = True; if (m_data_size > 0) { m_curr = m_fin.read(); m_got_rec = True; if (!m_curr) { m_hdu_type = FITS::NotAHDU; m_item_size = 0; m_data_type = FITS::NOVALUE; m_data_size = 0; m_curr_size = 0; errmsg(BADEOF, "[FitsInput::process_header()] Unexpected end of file."); m_rec_type = FITS::EndOfFile; return -1; } if (m_fin.err()) { m_hdu_type = FITS::NotAHDU; m_item_size = 0; m_data_type = FITS::NOVALUE; m_data_size = 0; m_curr_size = 0; errmsg(IOERR, "[FitsInput::process_header()] Failed to read first data record."); m_rec_type = FITS::UnrecognizableRecord; return -1; } } else { //cout << "FitsInput::process_header - t=" << t << " hdu_type=" << m_hdu_type << endl; if (t != FITS::PrimaryTableHDU) read_header_rec(); } return 0; } //=============================================================================================== // Implement the read_all() method with the cfitsio of NASA. // Read the whole data unit of current HDU from the beginning to end ( the // condition m_curr_size = m_data_size guarantees this. The // read data is stored into a char* buffer -- addr. // // If addr is too big, it cannot be fitted into memory. So if this function is // called, m_data_size actually cannot be bigger than the machine's memory size. // we still change the return type to OFF_T though. OFF_T FitsInput::read_all(FITS::HDUType t, char *addr) { if (m_curr_size < 0 || m_curr_size != m_data_size || m_rec_type != FITS::HDURecord || t != m_hdu_type || (!m_header_done)) { errmsg(BADOPER, "[FitsInput::read_all] Illegal operation on FITS input"); return 0; } // get size of the current HDU OFF_T l_headstart, l_datastart, l_dataend; int l_status = 0; if (ffghof(m_fptr, &l_headstart, &l_datastart, &l_dataend, &l_status) > 0) { fits_report_error(stderr, l_status); // print error report errmsg(BADSIZE, "[FitsInput::read_all()] Failed to get the size of current hdu"); return 0; } // determine how many byte of data is in the current hdu data unit. This is // probably redundant(actually this sometimes cause error) since m_data_size // is already determined when read header. //m_data_size = l_dataend - l_datastart; // this may not be needed. // // move file pointer to the beginning of the data unit of the current hsu l_status = 0; // The following may not be needed with if the condition m_curr_size = m_data_size // is met. ffmbyt(m_fptr, l_datastart, REPORT_EOF, &l_status); if (l_status) { fits_report_error(stderr, l_status); // print error report return (0); } // using the cfitsio function to read m_data_size bytes from the file // pointed to by m_fptr from where the file position indicator currently at. l_status = 0; ffgbyt(m_fptr, m_data_size, addr, &l_status); if (l_status) { fits_report_error(stderr, l_status); // print error report return (0); } if (l_dataend < ((m_fptr->Fptr)->filesize)) { if (ffmbyt(m_fptr, l_dataend, REPORT_EOF, &l_status) > 0) { fits_report_error(stderr, l_status); // print error report errmsg(BADOPER, "[FitsInput::read_all()] bytepos setting error!"); return (0); } } else { (m_fptr->Fptr)->bytepos = l_dataend; } m_curr_size = 0; // reset m_iosize so that next m_fin.read() will start from the beginning of next hdu. m_fin.reset_iosize(); read_header_rec(); return (m_data_size); } //========================================================================================= // Implement read() method with cfitsio of NASA throgh BlockInput::read(). // read next nb bytes into addr within the same hdu. If nb > m_curr_size, // make nb = m_curr_size. int FitsInput::read(FITS::HDUType t, char *addr, int nb) { // read next nb bytes into addr if (m_rec_type != FITS::HDURecord || t != m_hdu_type || (!m_header_done)) { errmsg(BADOPER, "[FitsInput::read()] Illegal operation on FITS input"); return 0; } if (m_curr_size == 0) { read_header_rec(); return 0; } if (OFF_T(nb) > m_curr_size) nb = m_curr_size; int n = nb; if (m_bytepos == m_recsize) { m_curr = m_fin.read(); if (!m_curr) { errmsg(BADEOF, "[FitsInput::read()] Unexpected end of file"); m_rec_type = FITS::EndOfFile; return -1; } if (m_fin.err()) { errmsg(IOERR, "[FitsInput::read()] Unrecognizable first data record."); m_rec_type = FITS::UnrecognizableRecord; return -1; } m_bytepos = 0; } do { if (n <= (m_recsize - m_bytepos)) { memcpy(addr, &m_curr[m_bytepos], n); m_bytepos += n; m_curr_size -= n; n = 0; } else { memcpy(addr, &m_curr[m_bytepos], (m_recsize - m_bytepos)); m_curr_size -= m_recsize - m_bytepos; n -= m_recsize - m_bytepos; addr += m_recsize - m_bytepos; m_curr = m_fin.read(); if (!m_curr) { errmsg(BADEOF, "[FitsInput::read()] Unexpected end of file"); m_rec_type = FITS::EndOfFile; return -1; } if (m_fin.err()) { errmsg(IOERR, "[FitsInput::read()] Unrecognizable data record."); m_rec_type = FITS::UnrecognizableRecord; return -1; } m_bytepos = 0; } } while (n > 0); if (m_curr_size == 0) { read_header_rec(); } //cout<<"[FitsInput::read()] byte read, nb = " << nb < m_curr_size) { nb = m_curr_size; } OFF_T l_n = nb; // if m_bytepos = m_recsize, the current data record is used up. So read a new record. if (m_bytepos == m_recsize) { m_curr = m_fin.read(); if (!m_curr) { errmsg(BADEOF, "[FitsInput::skip()] Unexpected end of file"); m_rec_type = FITS::EndOfFile; return -1; } if (m_fin.err()) { errmsg(IOERR, "[FitsInput::skip()] Failed to read first data record."); m_rec_type = FITS::UnrecognizableRecord; return -1; } m_bytepos = 0; } if (l_n <= OFF_T(m_fin.iosize() - m_fin.current())) { // In this case, there is enough data in m_buffer, so no need to get it from disk. do { // if l_n is smaller than (m_recsize-m_bytepos), simply skip the bytes l_n. // this "if block" also takes care of the ending part of the data to read. if (l_n <= OFF_T(m_recsize - m_bytepos)) { m_bytepos += l_n; m_curr_size -= l_n; l_n = 0; } else { m_curr_size -= m_recsize - m_bytepos; l_n -= m_recsize - m_bytepos; // since this is still within m_buffer, no need to check read errors. m_curr = m_fin.read(); m_bytepos = 0; } } while (l_n > 0); } else { // Need to skip more bytes than what m_buffer has. We could still have let the control use above // block. But if there are lots of data to read, the following block will be more efficient. int l_bb = m_fin.iosize() - m_fin.current(); // bytes in m_buffer int l_status = 0; int l_res = ((m_fptr->Fptr)->bytepos + l_n - l_bb) % m_recsize; // move file position pointer to the end of last complete record to skip. OFF_T l_postogo = ((m_fptr->Fptr)->bytepos) + l_n - l_bb - l_res; if (l_postogo < ((m_fptr->Fptr)->filesize)) { if (ffmbyt(m_fptr, l_postogo, REPORT_EOF, &l_status) > 0) { fits_report_error(stderr, l_status); // print error report errmsg(BADOPER, "[FitsInput::skip()] bytepos setting error!"); return -1; } } else { (m_fptr->Fptr)->bytepos = l_postogo; m_rec_type = FITS::EndOfFile; // do not need a return here. m_fin.read() // will handle it if reaches the end of file } m_fin.reset_iosize(); // this guarantees next m_fin.read() will read from disk. // the following lines read whatever is left by ffmbyt. Note that m_fin.read() // always read a complete record. That is why we only let ffmbyt() move the file // position pointer to the end of the last complete record to skip. m_curr = m_fin.read(); if (!m_curr) { //Is this an error? errmsg(BADEOF, "[FitsInput::skip()] Reached the end of the file."); m_rec_type = FITS::EndOfFile; return -1; } if (m_fin.err()) { errmsg(IOERR, "[FitsInput::skip()] Failed to read/skip data record."); m_rec_type = FITS::UnrecognizableRecord; return -1; } m_bytepos = l_res; } // set the current data size(remaining) within the data unit of the current hdu. m_curr_size -= l_n; if (m_curr_size == 0) { read_header_rec(); } return nb; } //===================================================================================== // Implement the skip_all() method with cfitsio of NASA // Skip the remaining data within the current HDU, and then moving to the // beginning of the data unit of the next hdu ( by calling read_header_rec()); void FitsInput::skip_all(FITS::HDUType t) { if (m_rec_type != FITS::HDURecord || t != m_hdu_type || (!m_header_done)) { errmsg(BADOPER, "[FitsInput::skip_all()] Illegal operation on FITS input"); return; } if (m_curr_size == 0) { read_header_rec(); return; } /* get size of the current HDU */ OFF_T l_headstart, l_datastart, l_dataend; int l_status = 0; if (ffghof(m_fptr, &l_headstart, &l_datastart, &l_dataend, &l_status) > 0) { fits_report_error(stderr, l_status); /* print error report */ errmsg(BADSIZE, "[FitsInput::skip_all()] Failed to get the size of current hdu."); return; } // Determine how many byte of data left within the current hdu data unit. // Consider the case that the file pointer is still pointing at the header part! l_status = 0; if (l_dataend < ((m_fptr->Fptr)->filesize)) { if (ffmbyt(m_fptr, l_dataend, REPORT_EOF, &l_status) > 0) { fits_report_error(stderr, l_status); // print error report errmsg(BADOPER, "[FitsInput::skip_all()] bytepos setting error!"); m_rec_type = FITS::UnrecognizableRecord; return; } } else { (m_fptr->Fptr)->bytepos = l_dataend; m_rec_type = FITS::EndOfFile; // do not need a return here. read_header_rec() // will handle it if reaches the end of file } m_curr_size = 0; m_bytepos = m_recsize; m_fin.reset_iosize(); // this guarantees next m_fin.read() will read from disk. read_header_rec(); return; } //========================================================================================= // Reset the fitsfile pointer void FitsOutput::setfptr(fitsfile* ffp) { //int l_status = 0; //if(m_fout.close_file( m_fptr, &l_status)){ // errmsg(IOERR,"[BlockIO::setfptr()] Error closing file"); //} // Cannot do above here. Since then cannot update BlockIO::m_fptr. // BlockIO.setfptr() will do above. m_fptr = ffp; } //========================================================================================= int FitsOutput::write_hdr(FitsKeywordList &kwl, FITS::HDUType t, FITS::ValueType dt, OFF_T ds, Int is) { if ((m_rec_type == FITS::EndOfFile) || (m_rec_type == FITS::SpecialRecord) || m_header_done || t == FITS::NotAHDU) { errmsg(BADOPER, "Illegal operation -- cannot write FITS header."); return -1; } if (t == FITS::PrimaryArrayHDU || t == FITS::PrimaryGroupHDU || t == FITS::PrimaryTableHDU) { if (m_rec_type != FITS::InitialState) { errmsg(BADOPER, "[FitsOutput::write_hdr()] Primary Header must be written first."); return -1; } else { m_isaprimary = True; if (kwl(FITS::SIMPLE)->asBool() == True) { m_valid_fits = True; } if (kwl(FITS::EXTEND)) { if (kwl.curr()->asBool() == True) { m_extend = True; } } } } else if (m_rec_type != FITS::HDURecord) { errmsg(BADOPER, "[FitsOutput::write_hdr()] Catastrophic error! Illegal record type."); //cout<<"[FitsOutput::write_hdr()] Illeagal record type."<asBool() == True) { m_valid_fits = True; } if (kwl(FITS::EXTEND)) { if (kwl.curr()->asBool() == True) { m_extend = True; } } } m_rec_type = FITS::HDURecord; m_hdu_type = t; m_data_type = dt; m_data_size = ds; m_item_size = is; m_curr_size = 0; m_bytepos = 0; m_err_status = OK; m_header_done = True; if (m_data_size == 0) { m_header_done = False; } } // write all data from addr int FitsOutput::write_all(FITS::HDUType t, char *addr, char pad) { if (!hdu_inprogress()) { errmsg(BADOPER, "Illegal operation -- no HDU in progress"); return -1; } if (t != m_hdu_type) { errmsg(BADOPER, "Illegal operation -- incorrect HDU type"); return -1; } // what if addr is used up first? GYL while ((m_data_size - m_curr_size) >= OFF_T(m_recsize)) { memcpy(m_curr, addr, m_recsize); m_fout.write(m_curr); addr += m_recsize; m_curr_size += m_recsize; } // note that m_curr_size starts from 0. GYL m_bytepos = m_data_size - m_curr_size; if (m_bytepos) { memcpy(m_curr, addr, m_bytepos); // pad the last record. GYL while (m_bytepos < m_recsize) { m_curr[m_bytepos++] = pad; } m_fout.write(m_curr); } m_data_size = 0; m_curr_size = 0; m_err_status = OK; m_header_done = False; return 0; } // BlockOutput::write() is wraped to cfitsio already. So no need // to directly wrap FitsOuput::write(). GYL int FitsOutput::write(FITS::HDUType t, char *addr, Int bytes, char pad) { int n; if (!hdu_inprogress()) { errmsg(BADOPER, "[FitsOutput::write()] Illegal operation -- no HDU in progress"); return -1; } if (t != m_hdu_type) { errmsg(BADOPER, "Illegal operation -- incorrect HDU type"); return -1; } //cout<<"[FitsOutput::write()] m_hdu_type = "<< m_hdu_type << endl; //cout<<"[FitsOutput::write()] is t == m_hdu_type? t = "<< t << endl; if ((bytes + m_curr_size) > m_data_size) { errmsg(BADOPER, "[FitsOutput::write] Attempt to write too much data -- truncated"); //cout<<"[FitsOutput::write] Attempt to write too much data -- truncated" << endl; bytes = m_data_size - m_curr_size; } if (bytes <= (m_recsize - m_bytepos)) { memcpy(&m_curr[m_bytepos], addr, bytes); m_bytepos += bytes; m_curr_size += bytes; } else { n = m_recsize - m_bytepos; memcpy(&m_curr[m_bytepos], addr, n); m_curr_size += n; addr += n; bytes -= n; m_fout.write(m_curr); // write a record while (bytes >= m_recsize) { memcpy(m_curr, addr, m_recsize); m_fout.write(m_curr); // write a record addr += m_recsize; m_curr_size += m_recsize; bytes -= m_recsize; } m_bytepos = bytes; if (bytes) { memcpy(m_curr, addr, bytes); m_curr_size += bytes; } } // Fill up and write the last record as long as the data doesn't // evenly fill the last record. if (m_curr_size == m_data_size) { if (bytes) { while (m_bytepos < m_recsize) { m_curr[m_bytepos++] = pad; } m_fout.write(m_curr); } m_data_size = 0; m_curr_size = 0; m_header_done = False; } m_err_status = OK; //cout<<"[FitsOutput::write()] Ending."<< endl; return 0; } int FitsOutput::write_sp(char *rec) { // write a special record if (m_rec_type == FITS::EndOfFile) { errmsg(BADOPER, "Illegal operation -- EOF has been written"); return -1; } if (hdu_inprogress()) { errmsg(BADOPER, "Illegal operation -- HDU in progress"); return -1; } if (m_rec_type != FITS::SpecialRecord) { m_rec_type = FITS::SpecialRecord; } m_fout.write(rec); return 0; } FitsDiskInput::FitsDiskInput(const char *f, int l, int n, FITSErrorHandler errhandler) : BlockInput(f, l, n, errhandler) { } FitsDiskOutput::FitsDiskOutput(const char *f, int l, int n, FITSErrorHandler errhandler) : BlockOutput(f, l, n, errhandler) { } FitsStdInput::FitsStdInput(int l, FITSErrorHandler errhandler) : BlockInput(0, l, 1, errhandler) { } FitsStdOutput::FitsStdOutput(int l, FITSErrorHandler errhandler) : BlockOutput(1, l, 1, errhandler) { } FitsTape9Input::FitsTape9Input(const char *f, int l, int n, FITSErrorHandler errhandler) : BlockInput(f, l, n, errhandler) { } FitsTape9Output::FitsTape9Output(const char *f, int l, int n, FITSErrorHandler errhandler) : BlockOutput(f, l, n, errhandler) { } FitsIO::FitsIO(FITSErrorHandler errhandler) : m_recsize(2880), m_valid_fits(False), m_extend(False), m_isaprimary(False), m_header_done(False), m_rec_type(FITS::InitialState), m_hdu_type( FITS::NotAHDU), m_errfn(errhandler), m_err_status(OK), m_curr(0), m_bytepos(0), m_item_size(0), m_data_type(FITS::NOVALUE), m_data_size(0), m_curr_size(0), m_skipHDU_size(0) { } FitsInput::FitsInput(const char *n, const FITS::FitsDevice &d, int b, FITSErrorHandler errhandler) : FitsIO(errhandler), m_fin(make_input(n, d, b, errhandler)), m_got_rec(False) { init(); } FitsInput::FitsInput(FITSErrorHandler errhandler) : FitsIO(errhandler), m_fin(*(BlockInput *) (new FitsStdInput(m_recsize, errhandler))) { init(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/fits/FITS/fitsio.h000066400000000000000000000255441476623553700165160ustar00rootroot00000000000000//# fitsio.h: //# Copyright (C) 1993,1994,1995,1996,1999,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef FITS_FITSIO_H #define FITS_FITSIO_H #include # include # include # include //# include # include namespace casacore { //# NAMESPACE CASACORE - BEGIN // sequential FITS I/O // // // // FitsIO is a base class that handles all the sequential blocked // FITS I/O. Special derived classes do the input and output. // No interpretation of the data is attempted here, there are // special FITS classes that handle syntax and interpretation. // // // // FitsInput fin("myfile.fits",FITS::Disk); // open disk file for FITS input // if (fin.err() == FitsIO::IOERR) { // check if open succeeded // cout << "Could not open FITS input\n"; // exit(0); // } // if (fin.rectype() == FITS::HDURecord && // test for primary array // fin.hdutype() == FITS::PrimaryArrayHDU) { // } // // class FitsIO { public: virtual ~FitsIO(); // error return code. Should be one of an // enumerated type: // //# until cxx2html can handle enum() we duplicate it here // // // enum FitsErrs { OK, IOERR, MISSKEY, BADBEGIN, EMPTYFILE, // NOPRIMARY, BADOPER, BADEOF, MEMERR, BADBITPIX, NOAXISN, // NOPCOUNT, NOGCOUNT, BADPCOUNT, BADGCOUNT, NOGROUPS, // BADNAXIS, BADPRIMARY, BADSIZE, HDUERR }; // // enum FitsErrs { OK, IOERR, MISSKEY, BADBEGIN, EMPTYFILE, NOPRIMARY, BADOPER, BADEOF, MEMERR, BADBITPIX, NOAXISN, NOPCOUNT, NOGCOUNT, BADPCOUNT, BADGCOUNT, NOGROUPS, BADNAXIS, BADPRIMARY, BADSIZE, HDUERR }; int err() const { return m_err_status; } // // // record size, in bytes, of a FITS block. // Normally set at 2880, unless some form of blocking was used. int fitsrecsize() const { return m_recsize; } // is it a valid fits file (SIMPLE==T). If not, the only // safest operation is to skip the data portion of the // current HeaderDataUnit Bool isafits() const { return m_valid_fits; } // see if there may be FITS extensions present (EXTENT==T) Bool isextend() const { return m_extend; } // test if end of file has been reached Bool eof() const { return Bool(m_rec_type == FITS::EndOfFile); } // the FITS record type FITS::FitsRecType rectype() const { return m_rec_type; } // Header Data Unit type (e.g. FITS::HDUType hdutype() const { return m_hdu_type; } FITS::ValueType datatype() const { return m_data_type; } // return the datasize of the current HDU. This excludes // the trailing end of the blocked data portion. OFF_T datasize() const { return m_data_size; } // data characteristics Int itemsize() const { return m_item_size; } // for input, size of remaining data // for output, size of data written OFF_T currsize() const { return m_curr_size; } // get FitsKeyCardTranslator FitsKeyCardTranslator& getkc(){ return m_kc; } // get the fitsfile pointer fitsfile *getfptr() const { return m_fptr; } // get the size of the last skipped HDU OFF_T getskipsize() const {return m_skipHDU_size;} protected: FitsIO(FITSErrorHandler); fitsfile *m_fptr; const int m_recsize; Bool m_valid_fits; // True if SIMPLE == T Bool m_extend; // True if EXTEND == T Bool m_isaprimary; // True if there is a primary HDU Bool m_header_done; // True if header has been processed FITS::FitsRecType m_rec_type; // always set FITS::HDUType m_hdu_type; // always set FITSErrorHandler m_errfn; // error handler function FitsErrs m_err_status; FitsKeyCardTranslator m_kc; FitsKeywordList m_kw; char *m_curr; // pointer to current record int m_bytepos; // current byte position within record Int m_item_size; // data characteristics FITS::ValueType m_data_type; //uInt m_data_size; OFF_T m_data_size; // for input, size of remaining data // for output, size of data written //uInt m_curr_size; OFF_T m_curr_size; // for size of the last HDU skipped OFF_T m_skipHDU_size; // set error message that belongs to one of the enumerated types virtual void errmsg(FitsErrs, const char *) = 0; }; // fixed-length sequential blocked FITS input class FitsInput : public FitsIO { friend int HeaderDataUnit::get_hdr(FITS::HDUType, FitsKeywordList &); friend OFF_T HeaderDataUnit::read_all_data(char *); friend int HeaderDataUnit::read_data(char *, Int); friend int HeaderDataUnit::skip(uInt); friend int HeaderDataUnit::skip(); public: // FitsInput(const char *, const FITS::FitsDevice &, int = 10, FITSErrorHandler errhandler = FITSError::defaultHandler); FitsInput(FITSErrorHandler errhandler = FITSError::defaultHandler); ~FitsInput(); // int skip_hdu(); // skip all remaining data void skip_all(FITS::HDUType); //int skip_hdu2(); // read special or unrecognizable records char *read_sp(); // get hdu header image cards as strings. By default the strings will be of // variable length. You can optionally ask for them to be length 80 (padded // with spaces). Vector kwlist_str(Bool length80=False); // number of physical blocks read/written int blockno() const {return m_fin.blockno();} // number of logical records read/written int recno() const {return m_fin.recno();} BlockInput & getfin(){ return m_fin; } // for test use only // the number of hdu in this fits file int getnumhdu() const {return m_thdunum;} private: BlockInput &m_fin; BlockInput &make_input(const char *, const FITS::FitsDevice &, int, FITSErrorHandler errhandler = FITSError::defaultHandler); // flag used for read control in errors Bool m_got_rec; // total number of hdu in this fits file int m_thdunum; virtual void errmsg(FitsErrs, const char *); void init(); void read_header_rec(); bool current_hdu_type( FITS::HDUType &); bool get_data_type( FITS::ValueType &); //# check if this comes out ok in cxx2html // Special interface to class HeaderDataUnit // // special way to process header int process_header(FITS::HDUType, FitsKeywordList &); // read all data into a given address - all responsibility is given // to the user OFF_T read_all(FITS::HDUType, char *); // read N bytes into address int read(FITS::HDUType, char *, int ); // skip N bytes int skip(FITS::HDUType, OFF_T); // }; // fixed-length sequential blocked FITS output class FitsOutput : public FitsIO { friend int HeaderDataUnit::write_hdr(FitsOutput &); friend int HeaderDataUnit::write_all_data(FitsOutput &, char *); friend int HeaderDataUnit::write_data(FitsOutput &, char *, Int); public: // FitsOutput(const char *, const FITS::FitsDevice &, int = 10, FITSErrorHandler errhandler = FITSError::defaultHandler); FitsOutput(FITSErrorHandler errhandler = FITSError::defaultHandler); ~FitsOutput(); // // used by PrimaryArray, BinaryTabelExtention etc to work with the constructor without keyword list. void set_data_info( FitsKeywordList &kwl, FITS::HDUType t, FITS::ValueType dt, OFF_T ds, Int is); // write a special record. For this the record type must also // be to set to FITS::SpecialRecord int write_sp(char *rec); // check if the current hdu is done. It was private. int hdu_complete() { return (m_rec_type == FITS::HDURecord && m_data_size == 0); } BlockOutput & getfout(){ return m_fout; } void setfptr( fitsfile* ffp ); Bool required_keys_only(){ return m_required_keys_only; } private: BlockOutput &m_fout; Bool m_required_keys_only; BlockOutput &make_output(const char *, const FITS::FitsDevice &, int, FITSErrorHandler errhandler = FITSError::defaultHandler); virtual void errmsg(FitsErrs, const char *); int hdu_inprogress() { return (m_rec_type == FITS::HDURecord && m_data_size > 0 && m_curr_size < m_data_size); } // Special interface to class HeaderDataUnit // int write_hdr(FitsKeywordList &, FITS::HDUType, FITS::ValueType, OFF_T, Int); // write all data from address int write_all(FITS::HDUType, char *, char); // write N bytes from address int write(FITS::HDUType, char *, Int, char); // }; // FITS input from disk class FitsDiskInput : public BlockInput { public: FitsDiskInput(const char *, int, int = 1, FITSErrorHandler errhandler = FITSError::defaultHandler); ~FitsDiskInput(); // implements skip in terms of lseek char *skip(int); }; // FITS output to disk class FitsDiskOutput : public BlockOutput { public: FitsDiskOutput(const char *, int, int = 1, FITSErrorHandler errhandler = FITSError::defaultHandler); ~FitsDiskOutput(); }; // FITS input from standard input class FitsStdInput : public BlockInput { public: FitsStdInput(int, FITSErrorHandler errhandler = FITSError::defaultHandler); ~FitsStdInput(); }; // FITS output to standard output class FitsStdOutput : public BlockOutput { public: FitsStdOutput(int, FITSErrorHandler errhandler = FITSError::defaultHandler); ~FitsStdOutput(); }; // FITS input from 9-track tape class FitsTape9Input : public BlockInput { public: FitsTape9Input(const char *, int, int = 10, FITSErrorHandler errhandler = FITSError::defaultHandler); ~FitsTape9Input(); }; // FITS output to 9-track tape class FitsTape9Output : public BlockOutput { public: FitsTape9Output(const char *, int, int = 10, FITSErrorHandler errhandler = FITSError::defaultHandler); ~FitsTape9Output(); }; } //# NAMESPACE CASACORE - END # endif casacore-3.7.1/fits/FITS/hdu.h000066400000000000000000001236261476623553700160010ustar00rootroot00000000000000//# hdu.h: //# Copyright (C) 1993,1994,1995,1996,1997,1999,2000,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef FITS_HDU_H #define FITS_HDU_H # include # include # include # include # include //# # include // If we ever wan to put varargs support back namespace casacore { //# NAMESPACE CASACORE - BEGIN class FitsInput; class FitsOutput; // base class that defines a HDU // // The class HeaderDataUnit contains what is common to all // header-data-units, including the collection of keywords. // From this class a number of FITS header-data-units are // derived, each of them with their own rich assortment of // functions for accessing and manipulating data of specific types. // // The following inheritence hierarchy illustrates the current // derived classes: // // // HeaderDataUnit // / | // / | // PrimaryArray ExtensionHeaderDataUnit // / | \ | // / | \ | // PrimaryGroup | ImageExtension | // | | // PrimaryTable BinaryTableExtension // / // / // AsciiTableExtension // // class HeaderDataUnit { friend std::ostream & operator << (std::ostream &, HeaderDataUnit &); public: virtual ~HeaderDataUnit(); Int dims() const { return no_dims; } Int dim(int n) const { return (0 enum HDUErrs { OK, NOMEM, MISSKEY, BADBITPIX, NOAXISN, NOPCOUNT, NOGCOUNT, BADPCOUNT, BADGCOUNT, NOGROUPS, BADNAXIS, BADREC, BADTYPE, BADRULES, BADSIZE, BADOPER, BADCONV, BADIO }; int err() const { return err_status; } // // skipping one or more HDU's // int skip(uInt n); int skip(); // // write the current header int write_hdr(FitsOutput &); // Determines the HDU type and the data type // Parameterss: keyword list, hdu type, data type, error handler and // error status. // Returns False if a serious error was detected, otherwise True static Bool determine_type(FitsKeywordList &, FITS::HDUType &, FITS::ValueType &, FITSErrorHandler, HDUErrs &); // Compute the total size of the data associated with an HDU. // The number of dimensions is also determined. This routine // assumes that hdu type has been appropriately set, but it may // be changed in the process. Data type is also determined. // Returns False if a serious error was detected, otherwise True static Bool compute_size(FitsKeywordList &, OFF_T &, Int &, FITS::HDUType &, FITS::ValueType &, FITSErrorHandler, HDUErrs &); // Operations on the HDU's keyword list // ConstFitsKeywordList &kwlist(){ return constkwlist_;} // return the header of the chdu as a vector of String. You can // force the strings to be length 80 (padded with spaces) Vector kwlist_str(Bool length80=False); void firstkw() { kwlist_.first(); } void lastkw() { kwlist_.last(); } const FitsKeyword *nextkw() { return kwlist_.next(); } const FitsKeyword *prevkw() { return kwlist_.prev(); } const FitsKeyword *currkw() { return kwlist_.curr(); } const FitsKeyword *kw(int n) { return kwlist_(n); } //# 07/21/98 AKH Added const to quite Apogee warnings: const FitsKeyword *kw(const FITS::ReservedName &n) { return kwlist_(n); } const FitsKeyword *nextkw(FITS::ReservedName &n) { return kwlist_.next(n); } const FitsKeyword *kw(FITS::ReservedName &n, int i) { return kwlist_(n,i); } const FitsKeyword *nextkw(FITS::ReservedName &n, int i) { return kwlist_.next(n,i); } const FitsKeyword *kw(const char *n) { return kwlist_(n); } const FitsKeyword *nextkw(const char *n) { return kwlist_.next(n); } void mk(FITS::ReservedName k, Bool v, const char *c = 0); void mk(FITS::ReservedName k, const char *v = 0, const char *c = 0); void mk(FITS::ReservedName k, Int v, const char *c = 0); void mk(FITS::ReservedName k, double v, const char *c = 0); void mk(int n, FITS::ReservedName k, Bool v, const char *c = 0); void mk(int n, FITS::ReservedName k, const char *v, const char *c = 0); void mk(int n, FITS::ReservedName k, Int v, const char *c = 0); void mk(int n, FITS::ReservedName k, double v, const char *c = 0); void mk(const char *n, Bool v, const char *c = 0); void mk(const char *n, const char *v = 0, const char *c = 0); void mk(const char *n, Int v, const char *c = 0); void mk(const char *n, float v, const char *c = 0); void mk(const char *n, double v, const char *c = 0); void mk(const char *n, Int r, Int i, const char *c = 0); void mk(const char *n, float r, float i, const char *c = 0); void mk(const char *n, double r, double i, const char *c = 0); void spaces(const char *n = 0, const char *c = 0); void comment(const char *n = 0, const char *c = 0); void history(const char *c = 0); // Bool notnull(double x) const { return double_null < x ? True : False; } Bool notnull(char *s) const { return ! s ? False : (s[0] != '\0' ? True : False); } Bool notnull(Int l) const { return Int_null < l ? True : False; } protected: // For input -- ~ should delete the keyword list: kwflag = 1 HeaderDataUnit(FitsInput &, FITS::HDUType, FITSErrorHandler errhandler = FITSError::defaultHandler); // For output -- ~ should not delete keyword list: kwflag = 0 // 07/21/98 AKH Clarification: HeaderDataUnit has a copy of the // FitsKeywordList, and should delete it. The kwflag // comments above are not important now. HeaderDataUnit(FitsKeywordList &, FITS::HDUType, FITSErrorHandler errhandler = FITSError::defaultHandler, FitsInput * = 0); // constructor for objects that write only required keyword to fits file. // the write method to call by these object should be those for the specific // hdu, such as write_bintbl_hdr(). HeaderDataUnit(FITS::HDUType, FITSErrorHandler errhandler = FITSError::defaultHandler, FitsInput * = 0); // for write required keywords only to use. bool init_data_unit( FITS::HDUType t ); FitsKeywordList &kwlist_; ConstFitsKeywordList constkwlist_; void posEnd(); FitsInput *fin; FITSErrorHandler errfn; HDUErrs err_status; void errmsg(HDUErrs, const char *); Int no_dims; // number of dimensions Int *dimn; // size of dimension N //uInt fits_data_size; // size in bytes of total amount of data OFF_T fits_data_size; // size in bytes of total amount of data FITS::ValueType data_type; // type of data - derived from BITPIX Int fits_item_size; // size in bytes of an item of FITS data Int local_item_size; // size in bytes of an item of local data FITS::HDUType hdu_type; // type of header/data unit char pad_char; // char to pad FITS data block // char * assign(FITS::ReservedName); char * assign(FITS::ReservedName, int); double asgdbl(FITS::ReservedName, double); double asgdbl(FITS::ReservedName, int, double); // double double_null; char char_null; Int Int_null; public: int get_hdr(FITS::HDUType, FitsKeywordList &); int read_data(char *, Int); int write_data(FitsOutput &, char *, Int); OFF_T read_all_data(char *); int write_all_data(FitsOutput &, char *); }; inline std::ostream & operator << (std::ostream &o, HeaderDataUnit &h) { return o << h.kwlist_; } inline void HeaderDataUnit::mk(FITS::ReservedName k, Bool v, const char *c) { posEnd(); kwlist_.mk(k,v,c); } inline void HeaderDataUnit::mk(FITS::ReservedName k, const char *v, const char *c) { posEnd(); kwlist_.mk(k,v,c); } inline void HeaderDataUnit::mk(FITS::ReservedName k, Int v, const char *c) { posEnd(); kwlist_.mk(k,v,c); } inline void HeaderDataUnit::mk(FITS::ReservedName k, double v, const char *c) { posEnd(); kwlist_.mk(k,v,c); } inline void HeaderDataUnit::mk(int n, FITS::ReservedName k, Bool v, const char *c) { posEnd(); kwlist_.mk(n,k,v,c); } inline void HeaderDataUnit::mk(int n, FITS::ReservedName k, const char *v, const char *c) { posEnd(); kwlist_.mk(n,k,v,c); } inline void HeaderDataUnit::mk(int n, FITS::ReservedName k, Int v, const char *c) { posEnd(); kwlist_.mk(n,k,v,c); } inline void HeaderDataUnit::mk(int n, FITS::ReservedName k, double v, const char *c) { posEnd(); kwlist_.mk(n,k,v,c); } inline void HeaderDataUnit::mk(const char *n, Bool v, const char *c) { posEnd(); kwlist_.mk(n,v,c); } inline void HeaderDataUnit::mk(const char *n, const char *v, const char *c) { posEnd(); kwlist_.mk(n,v,c); } inline void HeaderDataUnit::mk(const char *n, Int v, const char *c) { posEnd(); kwlist_.mk(n,v,c); } inline void HeaderDataUnit::mk(const char *n, float v, const char *c) { posEnd(); kwlist_.mk(n,v,c); } inline void HeaderDataUnit::mk(const char *n, double v, const char *c) { posEnd(); kwlist_.mk(n,v,c); } inline void HeaderDataUnit::mk(const char *n, Int r, Int i, const char *c) { posEnd(); kwlist_.mk(n,r,i,c); } inline void HeaderDataUnit::mk(const char *n, float r, float i, const char *c) { posEnd(); kwlist_.mk(n,r,i,c); } inline void HeaderDataUnit::mk(const char *n, double r, double i, const char *c) { posEnd(); kwlist_.mk(n,r,i,c); } inline void HeaderDataUnit::spaces(const char *n, const char *c) { posEnd(); kwlist_.spaces(n,c); } inline void HeaderDataUnit::comment(const char *n, const char *c) { posEnd(); kwlist_.comment(n,c); } inline void HeaderDataUnit::history(const char *c) { posEnd(); kwlist_.history(c); } // templated primary array base class of given type // // A Primary Data Array is represented by the following: // // data_array [NAXIS1][NAXIS2]...[NAXISN] // // // For a PrimaryArray, dims() gives the number of dimensions // and dim(i) gives the value of the i-th dimension // // WARNING! Multi-dimensional arrays are stored in FORTRAN order, // NOT in C order. Options on the store, copy, and move functions exist // to convert from one order to the other, if that is necessary. // // // It is important to understand the proper sequence of operations with // respect to I/O and data access. For input, the `read()' functions // allocate an internal buffer of the appropriate size, if not already // allocated, as well as reading and converting data; a `read()' function // must be performed prior to accessing the data, i. e. before executing // any `()', `data()', `copy()', or `move()' function. For output, the // `store()' function similarly allocates an internal buffer before // transfering data, and must be executed prior to any data access or // `write()' function. Note: If you call any version of store(), do not // call set_next(). // // Writing portions of an array at a time, rather than the entire array, // is a special case. The `set_next()' function is provided for this // purpose. It declares the intention to write out the next N elements and // must be executed prior to any `data()' function. It allocates a buffer // of appropriate size, if not already allocated. Again, via the `data()' // functions, one accesses the array as if the entire array were in // memory. The `write()' function always writes the number of current // elements in the internal buffer. The sequence of operations for each // portion of the array written would be: //
          //
        • `set_next(N)', //
        • fill the array using `data(N)' or other `data()' functions //
        • `write(fout)'. //
        // The `set_next()' function must NOT be used with // `read()' or `store()' functions; unpredictable results will occur. // // The following example illustrates the output cases. // // Suppose we have an image array with 512 rows and 1024 columns // stored in C-order. The C declaration would be: // // int source[1024][512]; // // To write out the entire array: // // FitsOutput fout; // some properly constructed FitsOutput // PrimaryArray pa; // some properly constructed PrimaryArray // pa.store(source,CtoF); // pa.write(fout); // // // Suppose we wanted to write out the two-dimensional image array a column // at a time, rather than write out the entire array. For FITS, dim(0) // is 512, dim(1) is 1024. The following code fragment writes one column // at a time in the proper FITS Fortran-order. // // // for (i = 0; i < dim(1); ++i) { // pa.set_next(dim(0)); // for (j = 0; j < dim(0); ++j) // data(j,i) = source[i][j]; // pa.write(fout); // } // // // //
        template class PrimaryArray : public HeaderDataUnit { public: typedef TYPE ElementType; // constructor from a FitsInput PrimaryArray(FitsInput &, FITSErrorHandler= FITSError::defaultHandler); // constructor from a FitsKeywordList PrimaryArray(FitsKeywordList &, FITSErrorHandler= FITSError::defaultHandler); // constructor does not require a FitsKeywordList. call write_priArr_hdr() after construction. PrimaryArray(FITSErrorHandler= FITSError::defaultHandler); // destructor virtual ~PrimaryArray(); // General access routines for a primary array // double bscale() const { return bscale_x; } double bzero() const { return bzero_x; } char *bunit() const { return bunit_x; } Bool isablank() const { return isablank_x; } Int blank() const { return blank_x; } char *ctype(int n) const { return ctype_x[n]; } double crpix(int n) const { return crpix_x[n]; } double crota(int n) const { return crota_x[n]; } double crval(int n) const { return crval_x[n]; } double cdelt(int n) const { return cdelt_x[n]; } double datamax() const { return datamax_x; } double datamin() const { return datamin_x; } OFF_T nelements() const { return totsize; } // // The overloaded operator functions `()' all return physical data, i. e., // data to which bscale() and bzero() have been applied, via the formula // // physical_data[i] = bscale() * raw_data[i] + bzero(). // // double operator () (int, int, int, int, int) const; double operator () (int, int, int, int) const; double operator () (int, int, int) const; double operator () (int, int) const; double operator () (int) const; // // The various `data()' functions allow one to access and set the raw data // itself. // TYPE & data(int, int, int, int, int); TYPE & data(int, int, int, int); TYPE & data(int, int, int); TYPE & data(int, int); TYPE & data(int); // // The `store()', `move()' and `copy()' functions allow bulk data // transfer between the internal FITS array and an external data // storage area. The external storage must have already been allocated // and it is assumed that the entire data array is in memory. // `store()' transfers raw data at `source' into the FITS array; an // allowable option is CtoF, which specifies to convert the array from // C-order to Fortran-order. `move()' is the opposite of `store()'. // `move()' transfers raw data from the FITS array to `target'; an // allowable option is FtoC, which specifies to convert the array from // Fortran-order to C-order. `copy()' is similar to `move()' except // that what is copied is physical data and not raw data; the physical // data can be either double or float. copy() also turns blanks into // NaN's. // int store(const TYPE *source, FITS::FitsArrayOption = FITS::NoOpt); void copy(double *target, FITS::FitsArrayOption = FITS::NoOpt) const; void copy(float *target, FITS::FitsArrayOption = FITS::NoOpt) const; void move(TYPE *target, FITS::FitsArrayOption = FITS::NoOpt) const; // // Use these versions if you are reading/writing "chunk by // chunk." No FtoC option is available. You are responsible for // ensuring that npixels corresponds to he number actually read or // written. Note that copy() turns blanks into NaN's. int store(const TYPE *source, int npixels); void copy(double *target, int npixels) const; void copy(float *target, int npixels) const; void move(TYPE *target, int npixels) const; // // // int write_priArr_hdr( FitsOutput &fout, int simple, int bitpix, int naxis, long naxes[], int extend ); // // The `read()' and `write()' functions control reading and writing data // from the external FITS I/O medium into the FITS array. Appropriate // conversions are made between FITS and local data representations. One // can read the entire array into memory, or one can only read portions of // the array. In the latter case, one must specify that the next N // elements are to be read or written. Note that the number of elements // must be specified, NOT the number of bytes. If one reads portions of // the array, as opposed to the entire array, only that portion is in // memory at a given time. One can still access the elements of the array // via the `()' and `data()' functions, as if the entire array was in // memory; obviously care must be taken in this case to access only those // portions that are actually in memory. // virtual int read(); virtual int read( int ); virtual int write(FitsOutput &); virtual OFF_T set_next(OFF_T); // //### if these, even as interspersed comments, cxx2html repeats the global //# group info.. //# read: read entire array into memory //# read() read next N elements into memory //# write; write current data //# set_next(): prepare to write next N elements protected: // construct from a FitsInput with given HDU type PrimaryArray(FitsInput &, FITS::HDUType, FITSErrorHandler errhandler = FITSError::defaultHandler); // construct from a FitsKeywordList with given HDU type PrimaryArray(FitsKeywordList &, FITS::HDUType, FITSErrorHandler errhandler = FITSError::defaultHandler); // construct witout FitsKeywordList for given HDU type( for ImageExtension and PrimaryGroup) PrimaryArray(FITS::HDUType, FITSErrorHandler errhandler = FITSError::defaultHandler); double bscale_x; double bzero_x; char *bunit_x; Bool isablank_x; Int blank_x; char **ctype_x; double *crpix_x; double *crota_x; double *crval_x; double *cdelt_x; double datamax_x; double datamin_x; OFF_T totsize; int *factor; // factors needed to compute array position offsets // compute a linear offset from array indicies // int offset(int, int) const; int offset(int, int, int) const; int offset(int, int, int, int) const; int offset(int, int, int, int, int) const; // OFF_T alloc_elems; // current number of allocated elements OFF_T beg_elem; // offset of first element in the buffer OFF_T end_elem; // offset of last element in the buffer // the allocated array TYPE *array; void pa_assign(); }; typedef PrimaryArray BytePrimaryArray; typedef PrimaryArray ShortPrimaryArray; typedef PrimaryArray LongPrimaryArray; typedef PrimaryArray FloatPrimaryArray; typedef PrimaryArray DoublePrimaryArray; // IMAGE extension of given type // //
      • typedef ImageExtension ByteImageExtension; //
      • typedef ImageExtension ShortImageExtension; //
      • typedef ImageExtension LongImageExtension; //
      • typedef ImageExtension FloatImageExtension; //
      • typedef ImageExtension DoubleImageExtension; // template class ImageExtension : public PrimaryArray { public: typedef TYPE ElementType; ImageExtension(FitsInput &, FITSErrorHandler errhandler = FITSError::defaultHandler); ImageExtension(FitsKeywordList &, FITSErrorHandler errhandler = FITSError::defaultHandler); // constructor for header consisted required keywords only ImageExtension(FITSErrorHandler errhandler = FITSError::defaultHandler); ~ImageExtension(); char *xtension() { return xtension_x; } char *extname() { return extname_x; } Int extver() { return extver_x; } Int extlevel() { return extlevel_x; } Int pcount() { return pcount_x; } Int gcount() { return gcount_x; } // write required keywords for ImageExtension int write_imgExt_hdr( FitsOutput &fout, int bitpix, int naxis, long *naxes); protected: char *xtension_x; char *extname_x; Int extver_x; Int extlevel_x; Int pcount_x; Int gcount_x; private: void ie_assign(); //# Make members in parent known protected: using PrimaryArray::assign; using PrimaryArray::errmsg; using PrimaryArray::init_data_unit; using PrimaryArray::pa_assign; using PrimaryArray::char_null; using PrimaryArray::kwlist_; using PrimaryArray::errfn; using PrimaryArray::hdu_type; using PrimaryArray::data_type; using PrimaryArray::fits_data_size; using PrimaryArray::fits_item_size; using PrimaryArray::array; using PrimaryArray::BADOPER; }; typedef ImageExtension ByteImageExtension; typedef ImageExtension ShortImageExtension; typedef ImageExtension LongImageExtension; typedef ImageExtension FloatImageExtension; typedef ImageExtension DoubleImageExtension; // Random Group datastructure // // A Random Group Structure is represented by the following: // // struct GroupData { // group_parms [PCOUNT]; // data_array [NAXIS2][NAXIS3]...[NAXISN]; // } group_data[GCOUNT]; // // // //#until cxx2html can handle this, duplicate //
      • typedef PrimaryGroup BytePrimaryGroup; //
      • typedef PrimaryGroup ShortPrimaryGroup; //
      • typedef PrimaryGroup LongPrimaryGroup; //
      • typedef PrimaryGroup FloatPrimaryGroup; //
      • typedef PrimaryGroup DoublePrimaryGroup; // // // Please note that the NOST has deprecated the Random Group // datastructure, it has been replaced by the much more powerfull // BINTABLE extension. // template class PrimaryGroup : public PrimaryArray { public: PrimaryGroup(FitsInput &, FITSErrorHandler errhandler = FITSError::defaultHandler); PrimaryGroup(FitsKeywordList &, FITSErrorHandler errhandler = FITSError::defaultHandler); // constructor for header consisted required keywords only PrimaryGroup(FITSErrorHandler errhandler = FITSError::defaultHandler); ~PrimaryGroup(); // Return basic parameters of a random group // Int gcount() const { return gcount_x; } Int pcount() const { return pcount_x; } char *ptype(int n) const { return ptype_x[n]; } double pscal(int n) const { return pscal_x[n]; } double pzero(int n) const { return pzero_x[n]; } // Int currgroup() const { return current_group; } double parm(int); // return physical parms TYPE & rawparm(int); // access raw parms void storeparm(const TYPE *source); void copyparm(double *target) const; void copyparm(float *target) const; void moveparm(TYPE *target) const; // read, or write the next group // int read(); int write(FitsOutput &); // // write the required keywords for PrimaryGroup // int write_priGrp_hdr( FitsOutput &fout, int simple, int bitpix, int naxis, long naxes[], long pcount, long gcount ); // // disable these functions, since they // are inherited from PrimaryArray // OFF_T set_next(OFF_T) { return 0; } int read(int) { return -1; } // protected: Int pcount_x; Int gcount_x; char **ptype_x; double *pscal_x; double *pzero_x; TYPE *group_parm; Int current_group; private: void pg_assign(); //# Make members in parent known protected: using PrimaryArray::assign; using PrimaryArray::errmsg; using PrimaryArray::init_data_unit; using PrimaryArray::pa_assign; using PrimaryArray::asgdbl; using PrimaryArray::nelements; using PrimaryArray::localitemsize; using PrimaryArray::fitsitemsize; using PrimaryArray::read_data; using PrimaryArray::write_data; using PrimaryArray::char_null; using PrimaryArray::kwlist_; using PrimaryArray::errfn; using PrimaryArray::err_status; using PrimaryArray::hdu_type; using PrimaryArray::data_type; using PrimaryArray::fits_data_size; using PrimaryArray::fits_item_size; using PrimaryArray::array; using PrimaryArray::totsize; using PrimaryArray::dimn; using PrimaryArray::no_dims; using PrimaryArray::factor; using PrimaryArray::ctype_x; using PrimaryArray::crpix_x; using PrimaryArray::crota_x; using PrimaryArray::crval_x; using PrimaryArray::cdelt_x; using PrimaryArray::BADOPER; using PrimaryArray::OK; using PrimaryArray::NOMEM; using PrimaryArray::BADIO; }; typedef PrimaryGroup BytePrimaryGroup; typedef PrimaryGroup ShortPrimaryGroup; typedef PrimaryGroup LongPrimaryGroup; typedef PrimaryGroup FloatPrimaryGroup; typedef PrimaryGroup DoublePrimaryGroup; // Primary Table structure // //
      • typedef PrimaryTable BytePrimaryTable; //
      • typedef PrimaryTable ShortPrimaryTable; //
      • typedef PrimaryTable LongPrimaryTable; //
      • typedef PrimaryTable FloatPrimaryTable; //
      • typedef PrimaryTable DoublePrimaryTable; // template class PrimaryTable : public PrimaryArray { public: typedef TYPE ElementType; PrimaryTable(FitsInput &, FITSErrorHandler errhandler = FITSError::defaultHandler); PrimaryTable(FitsKeywordList &, FITSErrorHandler errhandler = FITSError::defaultHandler); // constructor for header consisted required keywords only PrimaryTable(FITSErrorHandler errhandler = FITSError::defaultHandler); ~PrimaryTable(); // write required keywords for PrimaryTable int write_priTable_hdr( FitsOutput &fout, int bitpix, int naxis, long *naxes); int read(); int read(int) { return -1; } int write(FitsOutput &){ return -1; } char* object() const { return object_x; } char* telescop() const { return telescop_x; } char* instrume() const { return instrume_x; } char* dateobs() const { return dateobs_x; } char* datemap() const { return datemap_x; } char* bunit() const { return bunit_x; } float bscal() const { return bscale_x; } float bzero() const { return bzero_x; } float equinox() const { return equinox_x; } float altrpix() const { return altrpix_x; } protected: char* object_x; //OBJECT char* telescop_x; //TELESCOP char* instrume_x; //INSTRUME char* dateobs_x; //DATE-OBS char* datemap_x; //DATE-MAP Float bscale_x; //BSCALE Float bzero_x; //BZERO char* bunit_x; //BUNIT Float equinox_x; //EQUINOX Float altrpix_x; //ALTRPIX private: void pt_assign(); //# Make members in parent known protected: using PrimaryArray::assign; using PrimaryArray::errmsg; using PrimaryArray::init_data_unit; using PrimaryArray::pa_assign; using PrimaryArray::asgdbl; using PrimaryArray::nelements; using PrimaryArray::localitemsize; using PrimaryArray::fitsitemsize; using PrimaryArray::read_data; using PrimaryArray::write_data; using PrimaryArray::char_null; using PrimaryArray::kwlist_; using PrimaryArray::errfn; using PrimaryArray::err_status; using PrimaryArray::hdu_type; using PrimaryArray::data_type; using PrimaryArray::fits_data_size; using PrimaryArray::fits_item_size; using PrimaryArray::array; using PrimaryArray::totsize; using PrimaryArray::dimn; using PrimaryArray::no_dims; using PrimaryArray::factor; using PrimaryArray::ctype_x; using PrimaryArray::crpix_x; using PrimaryArray::crota_x; using PrimaryArray::crval_x; using PrimaryArray::cdelt_x; using PrimaryArray::BADOPER; using PrimaryArray::OK; using PrimaryArray::NOMEM; using PrimaryArray::BADIO; }; typedef PrimaryTable BytePrimaryTable; typedef PrimaryTable ShortPrimaryTable; typedef PrimaryTable LongPrimaryTable; typedef PrimaryTable FloatPrimaryTable; typedef PrimaryTable DoublePrimaryTable; // base class for generalized exentensions HDU class ExtensionHeaderDataUnit : public HeaderDataUnit { public: ExtensionHeaderDataUnit(FitsInput &, FITSErrorHandler errhandler = FITSError::defaultHandler); ExtensionHeaderDataUnit(FitsKeywordList &, FITSErrorHandler errhandler = FITSError::defaultHandler); ~ExtensionHeaderDataUnit(); char *xtension() { return xtension_x; } char *extname() { return extname_x; } Int extver() { return extver_x; } Int extlevel() { return extlevel_x; } Int pcount() { return pcount_x; } Int gcount() { return gcount_x; } // read next N bytes into addr int read(char *addr, int nbytes) { return read_data(addr, Int(nbytes)); } // write next N bytes from addr to the FITS output fout int write(FitsOutput &fout, char *addr, int nbytes) { return write_data(fout,addr,nbytes); } protected: ExtensionHeaderDataUnit(FitsInput &, FITS::HDUType, FITSErrorHandler errhandler = FITSError::defaultHandler); ExtensionHeaderDataUnit(FitsKeywordList &, FITS::HDUType, FITSErrorHandler errhandler = FITSError::defaultHandler); // This constructor is used for writing only required keywords. ExtensionHeaderDataUnit(FITS::HDUType, FITSErrorHandler errhandler = FITSError::defaultHandler); char *xtension_x; char *extname_x; Int extver_x; Int extlevel_x; Int pcount_x; Int gcount_x; private: void ex_assign(); }; // helper class class FitsBase { friend class BinaryTableExtension; friend class AsciiTableExtension; public: FitsBase(const FITS::ValueType &t, int n) : no_elements(n), data_type(t) { } virtual ~FitsBase(); unsigned int nelements() const { return (unsigned int)no_elements; } virtual int fitsfieldsize() const = 0; virtual int localfieldsize() const = 0; virtual void *data() = 0; virtual int dims() const; virtual int dim(int n) const; virtual int *vdim(); FITS::ValueType fieldtype() const { return data_type; } static FitsBase *make(const FITS::ValueType &, int = 1); static FitsBase *make(const FITS::ValueType &, int, int *); static FitsBase *make(FitsBase &); FitsBase & operator = (FitsBase &) = delete; virtual void show(std::ostream &) = 0; protected: int no_elements; // the number of elements in the field FITS::ValueType data_type; virtual void setaddr(void **) = 0; }; inline std::ostream & operator << (std::ostream &o, FitsBase &x) { x.show(o); return o; } // helper class // // Note that FitsField does not allocate space for the data. // Space is external to FitsField and its address is set via the // setaddr function. // template class FitsField : public FitsBase { public: FitsField(int n = 1) : FitsBase(FITS::getfitstype(NoConvert()),n), field(0) { } ~FitsField(); TYPE & operator () () { return (*field)[0]; } TYPE & operator () (int i) { return (*field)[i]; } FitsField & operator = (const TYPE &x) { (*field)[0] = x; return *this; } int fitsfieldsize() const; int localfieldsize() const; void *data(); void show(std::ostream &); protected: TYPE **field; void setaddr(void **addr); }; // helper class // //#until cxx2html can handle this, duplicate //
      • typedef FitsField LogicalFitsField; //
      • typedef FitsField BitFitsField; //
      • typedef FitsField CharFitsField; //
      • typedef FitsField ByteFitsField; //
      • typedef FitsField ShortFitsField; //
      • typedef FitsField LongFitsField; //
      • typedef FitsField FloatFitsField; //
      • typedef FitsField DoubleFitsField; //
      • typedef FitsField ComplexFitsField; //
      • typedef FitsField IComplexFitsField; //
      • typedef FitsField DComplexFitsField; //
      • typedef FitsField VADescFitsField; // // // Bit fields require special treatment // template <> class FitsField : public FitsBase { public: FitsField(int n = 1); ~FitsField(); FitsField & operator () () { byte_offset = 0; mask = 0200; return *this; } FitsField & operator () (unsigned i) { byte_offset = i / 8; mask = 0200 >> (i % 8); return *this; } FitsField & operator = (unsigned i) { (*field)[byte_offset] = (i == 0 ? ((*field)[byte_offset] & ~mask) : ((*field)[byte_offset] | mask)); return *this; } int fitsfieldsize() const; int localfieldsize() const; operator int() { return (((*field)[byte_offset] & mask) != 0); } void *data(); void show(std::ostream &); protected: FitsBit **field; unsigned char mask; int byte_offset; void setaddr(void **addr); }; typedef FitsField LogicalFitsField; typedef FitsField BitFitsField; typedef FitsField CharFitsField; typedef FitsField ByteFitsField; typedef FitsField ShortFitsField; typedef FitsField LongFitsField; typedef FitsField FloatFitsField; typedef FitsField DoubleFitsField; typedef FitsField ComplexFitsField; typedef FitsField IComplexFitsField; typedef FitsField DComplexFitsField; typedef FitsField VADescFitsField; // FITS array of given type template class FitsArray : public FitsField { public: FitsArray(int, const int *); ~FitsArray(); TYPE & operator () (int d0, int d1); TYPE & operator () (int, int, int); TYPE & operator () (int, int, int, int); TYPE & operator () (int, int, int, int, int); int dims() const; int dim(int n) const; int *vdim(); protected: int no_dims; int *dimn; int *factor; //# Make members in parent known protected: using FitsField::no_elements; using FitsField::field; }; // FITS array of FitsBit type // // We must specify a FitsArray as a specialization. // template <> class FitsArray : public FitsField { public: FitsArray(int, const int *); ~FitsArray(); FitsField & operator () (int d0, int d1); FitsField & operator () (int, int, int); FitsField & operator () (int, int, int, int); FitsField & operator () (int, int, int, int, int); //# Disabled for now - we might eventually want to put varargs support back //# FitsField & operator () (int, int, int, int, int, int ...); int dims() const; int dim(int n) const; int *vdim(); protected: int no_dims; int *dimn; int *factor; }; typedef FitsArray LogicalFitsArray; typedef FitsArray BitFitsArray; typedef FitsArray CharFitsArray; typedef FitsArray ByteFitsArray; typedef FitsArray ShortFitsArray; typedef FitsArray LongFitsArray; typedef FitsArray FloatFitsArray; typedef FitsArray DoubleFitsArray; typedef FitsArray ComplexFitsArray; typedef FitsArray IComplexFitsArray; typedef FitsArray DComplexFitsArray; typedef FitsArray VADescFitsArray; // BINTABLE extension class BinaryTableExtension : public ExtensionHeaderDataUnit { public: BinaryTableExtension(FitsInput &, FITSErrorHandler errhandler = FITSError::defaultHandler); BinaryTableExtension(FitsKeywordList &, FITSErrorHandler errhandler = FITSError::defaultHandler); // constructor to match write_bintbl_hdr() BinaryTableExtension( FITSErrorHandler errhandler = FITSError::defaultHandler); virtual ~BinaryTableExtension(); // return basic elements of a table // Int nrows() const { return dim(1); } Int ncols() const { return tfields_x; } uInt rowsize() const { return fitsrowsize; } Int tfields() const { return tfields_x; } const char *tform(int n) const { return tform_x[n]; } double tscal(int n) const { return tscal_x[n]; } double tzero(int n) const { return tzero_x[n]; } Bool isatnull(int n) const { return isatnull_x[n]; } Int tnull(int n) const { return tnull_x[n]; } const char *ttype(int n) const { return ttype_x[n]; } const char *tunit(int n) const { return tunit_x[n]; } const char *tdisp(int n) const { return tdisp_x[n]; } const char *tdim(int n) const { return tdim_x[n]; } const char *ctype(int n) const { return ctype_x[n]; } double crpix(int n) const { return crpix_x[n]; } double crota(int n) const { return crota_x[n]; } double crval(int n) const { return crval_x[n]; } double cdelt(int n) const { return cdelt_x[n]; } Int theap() const { return theap_x; } const char *author() const { return author_x; } const char *referenc() const { return referenc_x; } // // binds a FitsField to a column int bind(int, FitsBase &); // row selector functions // BinaryTableExtension & operator ++ (); BinaryTableExtension & operator -- (); BinaryTableExtension & operator () (int); // // read entire table into memory int read(); // read next N rows into memory int read(int); // prepare to write the next N rows int set_next(int); // write current rows int write(FitsOutput &); // create a binary table header without using FitsKeywordList objet. int write_binTbl_hdr(FitsOutput &, long, int, const char**, const char**, const char**, const char*, long ); // select a field FitsBase &field(int i) const { return *fld[i]; } // get current row Int currrow() const { return curr_row; } // sets field addresses in the current row //void set_fitsrow(Int); protected: BinaryTableExtension(FitsInput &, FITS::HDUType, FITSErrorHandler errhandler = FITSError::defaultHandler); BinaryTableExtension(FitsKeywordList &, FITS::HDUType, FITSErrorHandler errhandler = FITSError::defaultHandler); BinaryTableExtension(FITS::HDUType, FITSErrorHandler errhandler = FITSError::defaultHandler); Int tfields_x; char **tform_x; double *tscal_x; double *tzero_x; Bool *isatnull_x; Int *tnull_x; char **ttype_x; char **tunit_x; char **tdisp_x; char **tdim_x; char **ctype_x; double *crpix_x; double *crota_x; double *crval_x; double *cdelt_x; Int nAxis; Int theap_x; char *author_x; char *referenc_x; // read and write the next FITS data row // virtual int readrow(); virtual int writerow(FitsOutput &); // unsigned char *fitsrow; // the FITS data row buffer uInt *fits_offset; // Offsets to the fields within a FITS row uInt fitsrowsize; // size in bytes of a FITS data row Bool isoptimum; // tells whether optimum case exists or not // sets field addresses in the current row void set_fitsrow(Int); unsigned char *table; // the table in local format uInt tablerowsize; // size in bytes of a table row uInt alloc_row; // number of currently allocated rows Int beg_row; // range of rows currently in memory Int end_row; Int curr_row; FitsBase **fld; // The array of fields uInt *table_offset; // Offsets to the fields within a table row // data addresses of fields of current row void **data_addr; private: void bt_assign(); }; // (ascii) TABLE extension class AsciiTableExtension : public BinaryTableExtension { public: AsciiTableExtension(FitsInput &, FITSErrorHandler errhandler = FITSError::defaultHandler); AsciiTableExtension(FitsKeywordList &, FITSErrorHandler errhandler = FITSError::defaultHandler); AsciiTableExtension(FITSErrorHandler errhandler = FITSError::defaultHandler); ~AsciiTableExtension(); //# special overriden functions for ascii TABLE only // position in which column starts Int tbcol(int n) { return tbcol_x[n]; } // ascii string that represents the NULL value char *tnull(int n) { return tnulla_x[n]; } // write the required keywords for ASCIITableExtension int write_ascTbl_hdr( FitsOutput &, long, long, int, const char **, long *, const char **, const char **, const char *e); protected: Int *tbcol_x; char **tnulla_x; uInt *fits_width; // widths of the fields within a FITS row char **format; // converted formats of the fields // read and write the next FITS data row // int readrow(); int writerow(FitsOutput &); // private: void at_assign(); }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES # endif casacore-3.7.1/fits/FITS/hdu.tcc000066400000000000000000001462771476623553700163320ustar00rootroot00000000000000//# hdu.cc: //# Copyright (C) 1993,1994,1995,1996,1997,1999,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef FITS_HDU_TCC #define FITS_HDU_TCC //# Partial implementation of little endian code by Kris Huber //# (kris@helios.ece.usu.edu) #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //============================================================================ template PrimaryArray::PrimaryArray(FitsInput &f, FITSErrorHandler errhandler) : HeaderDataUnit(f,FITS::PrimaryArrayHDU,errhandler) { pa_assign(); // assign values from keyword list } //============================================================================ template PrimaryArray::PrimaryArray(FitsInput &f, FITS::HDUType t, FITSErrorHandler errhandler) : HeaderDataUnit(f,t,errhandler) { pa_assign(); // assign values from keyword list } //============================================================================ template PrimaryArray::PrimaryArray(FitsKeywordList &k, FITSErrorHandler errhandler) : HeaderDataUnit(k,FITS::PrimaryArrayHDU,errhandler,0) { pa_assign(); // assign values from keyword list } //============================================================================ template PrimaryArray::PrimaryArray(FitsKeywordList &k, FITS::HDUType t, FITSErrorHandler errhandler) : HeaderDataUnit(k,t,errhandler,0) { pa_assign(); // assign values from keyword list } //============================================================================= // constructor does not require a FitsKeywordList. call write_priArr_hdr() after construction. template PrimaryArray::PrimaryArray(FITSErrorHandler errhandler) : HeaderDataUnit(FITS::PrimaryArrayHDU,errhandler,0) { // pa_assign(); // in the case when user is not required to provide a kerword list object, // pa_assign() must be called from write_priArr_hdr(). } //=============================================================================== // protected, for ImageExention and PrimaryGroup to use template PrimaryArray::PrimaryArray( FITS::HDUType t, FITSErrorHandler errhandler) : HeaderDataUnit(t,errhandler,0) { } //================================================================================= template PrimaryArray::~PrimaryArray() { if (bunit_x != &char_null) delete [] bunit_x; if (no_dims > 0) { delete [] crpix_x; delete [] crota_x; delete [] crval_x; delete [] cdelt_x; for (int i = 0; i < no_dims; i++) if (ctype_x[i] != &char_null) delete [] ctype_x[i]; delete [] ctype_x; delete [] factor; } if (alloc_elems > 0) { delete [] array; } } //=================================================================================== template void PrimaryArray::pa_assign() { // assign values from keyword list int i; bscale_x = 1.0; // first, initialize everything bzero_x = 0.0; bunit_x = 0; isablank_x = False; blank_x = FITS::minInt; ctype_x = 0; crpix_x = 0; crota_x = 0; crval_x = 0; cdelt_x = 0; datamax_x = FITS::maxdouble; datamin_x = FITS::mindouble; totsize = 0; factor = 0; alloc_elems = 0; beg_elem = 0; end_elem = 0; array = 0; if (err_status != OK) // check for error in HDU construction return; if (FITS::getfitstype(NoConvert()) != datatype()) { errmsg(BADTYPE,"Wrong type! Current HDU is not of this type!"); return; } bscale_x = asgdbl(FITS::BSCALE,1.0); bzero_x = asgdbl(FITS::BZERO,0.0); if (kwlist_(FITS::BLANK) == 0) blank_x = Int_null; else { blank_x = kwlist_.curr()->asInt(); isablank_x = True; } datamax_x = asgdbl(FITS::DATAMAX,double_null); datamin_x = asgdbl(FITS::DATAMIN,double_null); bunit_x = assign(FITS::BUNIT); if (no_dims > 0) { crpix_x = new double [no_dims]; crota_x = new double [no_dims]; crval_x = new double [no_dims]; cdelt_x = new double [no_dims]; ctype_x = new char * [no_dims]; if (crpix_x == 0 || crota_x == 0 || crval_x == 0 || cdelt_x == 0 || ctype_x == 0) { errmsg(NOMEM,"Cannot allocate memory"); return; } for (i = 0; i < no_dims; i++) { crpix_x[i] = asgdbl(FITS::CRPIX,(i + 1),double_null); crota_x[i] = asgdbl(FITS::CROTA,(i + 1),double_null); crval_x[i] = asgdbl(FITS::CRVAL,(i + 1),double_null); cdelt_x[i] = asgdbl(FITS::CDELT,(i + 1),double_null); ctype_x[i] = assign(FITS::CTYPE,(i + 1)); } // Allocate and initialize factor and totsize. // These are for Fortran order. totsize = dimn[0]; for (i = 1; i < no_dims; i++) totsize *= dimn[i]; factor = new int [3*no_dims]; // We need a little extra space for CtoF and FtoC conversions. if (factor == 0) { errmsg(NOMEM,"Cannot allocate memory"); return; } factor[0] = 1; for (i = 1; i < no_dims; ++i) { factor[i] = factor[i - 1] * dimn[i - 1]; } } else { crpix_x = 0; crota_x = 0; crval_x = 0; cdelt_x = 0; ctype_x = 0; factor = 0; totsize = 0; } array = 0; // no space allocated for array alloc_elems = 0; beg_elem = 0; end_elem = -1; } //====================================================================================== template int PrimaryArray::offset(int d0, int d1) const { return d0 + factor[1] * d1; } //=================================================================================== template int PrimaryArray::offset(int d0, int d1, int d2) const { OFF_T offset; offset = d0 + d1 * factor[1] + d2 * factor[2]; return offset; } //=================================================================================== template int PrimaryArray::offset(int d0, int d1, int d2, int d3) const { OFF_T offset; offset = d0 + d1 * factor[1] + d2 * factor[2] + d3*factor[3]; return offset; } //=================================================================================== template int PrimaryArray::offset(int d0, int d1, int d2, int d3, int d4) const { OFF_T offset; offset = d0 + d1 * factor[1] + d2 * factor[2] + d3*factor[3] + d4*factor[4]; return offset; } //=================================================================================== template double PrimaryArray::operator () (int d0) const { //if (d0 < beg_elem || d0 > end_elem) { // out of bounds //} return bscale() * array[d0 - beg_elem] + bzero(); } //=================================================================================== template double PrimaryArray::operator () (int d0, int d1) const { OFF_T n = offset(d0,d1); //if (n < beg_elem || n > end_elem) { // out of bounds //} return bscale() * array[n - beg_elem] + bzero(); } //=================================================================================== template double PrimaryArray::operator () (int d0, int d1, int d2) const { OFF_T n = offset(d0,d1,d2); //if (n < beg_elem || n > end_elem) { // out of bounds //} return bscale() * array[n - beg_elem] + bzero(); } //==================================================================================== template double PrimaryArray::operator () (int d0, int d1, int d2, int d3) const { OFF_T n = offset(d0,d1,d2,d3); //if (n < beg_elem || n > end_elem) { // out of bounds //} return bscale() * array[n - beg_elem] + bzero(); } //==================================================================================== template double PrimaryArray::operator () (int d0, int d1, int d2, int d3, int d4) const { OFF_T n = offset(d0,d1,d2,d3,d4); //if (n < beg_elem || n > end_elem) { // out of bounds //} return bscale() * array[n - beg_elem] + bzero(); } //==================================================================================== template TYPE & PrimaryArray::data(int d0) { //if (d0 < beg_elem || d0 > end_elem) { // out of bounds //} return array[d0 - beg_elem]; } //==================================================================================== template TYPE & PrimaryArray::data(int d0, int d1) { //cout<< "PrimaryArray::data(int, int ) called."< end_elem) { // out of bounds //} return array[n - beg_elem]; } //===================================================================================== template TYPE & PrimaryArray::data(int d0, int d1, int d2) { OFF_T n = offset(d0,d1,d2); //if (n < beg_elem || n > end_elem) { // out of bounds //} return array[n - beg_elem]; } //===================================================================================== template TYPE & PrimaryArray::data(int d0, int d1, int d2, int d3) { OFF_T n = offset(d0,d1,d2,d3); //if (n < beg_elem || n > end_elem) { // out of bounds //} return array[n - beg_elem]; } //===================================================================================== template TYPE & PrimaryArray::data(int d0, int d1, int d2, int d3, int d4) { OFF_T n = offset(d0,d1,d2,d3,d4); //if (n < beg_elem || n > end_elem) { // out of bounds //} return array[n - beg_elem]; } //================================================================================================= template int PrimaryArray::read() { // read entire array // check if anything has been read if (fin->currsize() != fin->datasize()){ // illegal operation errmsg(BADOPER,"Illegal operation -- some data already read"); return -1; } if (set_next(nelements()) == -1) { // allocate nelements array elements cerr << "Buffer array is too big to fit into memory. You are using PrimaryArray::read()." << endl; cerr << "Please use PrimaryArray::read( int ) to read data by chunk."<< endl; //errmsg(BADOPER,"Error allocating Array."); return -1; } // read the data (this read number of FITS bytes) OFF_T nr = read_all_data((char *)array); if (nr != fitsdatasize()) { // check return value for errors errmsg(BADIO,"Error reading Array"); return -1; } // do the FITS to local conversion, including worrying about // the fact that array and FITS size may not be the same // ... int ne = nr / fitsitemsize(); // the actual number of elements read FITS::f2l( (TYPE *)array, array, ne ); return alloc_elems; } //============================================================================= template int PrimaryArray::read(int ne) { // read the next ne data elements if (set_next(ne) == -1) { // check buffer allocation return -1; } // read ne data elements int nr = read_data((char *)array,(ne*fitsitemsize())); if (nr < 1) { // check return value for errors errmsg(BADIO,"Error reading Array"); return -1; } nr /= fitsitemsize(); // the actual number of elements returned if (nr != ne) end_elem = beg_elem + nr -1; // do the FITS to local conversion, including worrying about // the fact that array and FITS size may not be the same // ... FITS::f2l( (TYPE *)array, array, ne ); return nr; } //============================================================================== template int PrimaryArray::write(FitsOutput &fout) { // do the local to FITS conversion, including worrying about // the fact that array and FITS size may not be the same // ... int ne = end_elem - beg_elem + 1; FITS::l2f( array, (TYPE *)array, ne ); // write the data from beg_elem to end_elem OFF_T nb = fitsitemsize() * ne; if (write_data(fout,(char *)array,nb) != 0) { errmsg(BADIO,"Error writing Array"); return -1; } return ne; } //========================================================================================= // newly wrapped. Use exception to handle the situation that the array is too big. template OFF_T PrimaryArray::set_next(OFF_T ne) { // if ne > current buffersize, reallocate if (ne > alloc_elems) { delete [] array; try{ array = new TYPE [ne]; }catch( std::bad_alloc& ){ // out of storage exception. cerr << "\narray is too big to fit into memory."<< endl; errmsg(BADOPER,"Error allocating Array."); alloc_elems = 0; beg_elem = 0; end_elem = -1; return -1; } alloc_elems = ne; } // set the limits of the array currently in memory beg_elem = end_elem + 1; end_elem = beg_elem + ne -1; return ne; } //========================================================================================= template int PrimaryArray::store(const TYPE *source, int npixels) { if (npixels < 0) { errmsg(BADSIZE, "npixels < 0"); return -1; } if ((OFF_T) npixels > nelements()) { errmsg(BADSIZE, "npixels > nelements()"); return -1; } if (set_next(npixels) == -1) { errmsg(BADSIZE, "set_next fails"); return -1; } memcpy(array, source, localitemsize()*npixels); return 0; } //========================================================================================= template int PrimaryArray::store(const TYPE *source, FITS::FitsArrayOption opt) { uInt count; Int offset, i, *sub; if (set_next(nelements()) == -1) { // allocate nelements array elements return -1; } if (opt == FITS::CtoF) { sub = &factor[dims()]; // algorithm for converting C-order to F-order count = 0; for (i = 0; i < dims(); ++i) sub[i] = 0; for(;;) { for (i = 1, offset = sub[0]; i < dims(); ++i) offset += sub[i] * factor[i]; array[offset] = source[count++]; if (count == nelements()) break; for (i = dims() - 1; i >= 0; --i) { ++sub[i]; if (sub[i] < dim(i)) break; sub[i] = 0; } } } else { memcpy(array,source,(localitemsize() * nelements())); } return 0; } //============================================================================================ template void PrimaryArray::copy(double *target, int npixels) const { if (npixels < 0 || npixels > end_elem - beg_elem + 1) { PrimaryArray *This = (PrimaryArray *)this; This->errmsg(BADSIZE, "npixels<0 or > number of read pixels"); } double scale = bscale(); double zero = bzero(); if (!isablank() || FitsFPUtil::isFP((TYPE *)0)) { // No blanks or we are FP for (int i=0; i void PrimaryArray::copy(double *target, FITS::FitsArrayOption opt) const { uInt count; Int offset, *sub, *C_factor; double fscale = (double)bscale(); double fzero = (double)bzero(); Bool blanked = isablank() && !FitsFPUtil::isFP((TYPE *)0) ? True:False; TYPE blankval = TYPE(0); if (blanked) { blankval = blank(); } double nan; FitsFPUtil::setNaN(nan); if (opt == FITS::FtoC) { sub = &factor[dims()]; C_factor = &sub[dims()]; // compute the C_factors Int i; for (i = 0; i < (dims() - 1); ++i) { C_factor[i] = 1; for (Int j = i + 1; j < dims(); ++j) C_factor[i] *= dim(j); } C_factor[i] = 1; // algorithm for converting F-order to C-order count = 0; for (i = 0; i < dims(); ++i) sub[i] = 0; for(;;) { for (i = 0, offset = 0; i < dims(); ++i) offset += sub[i] * C_factor[i]; target[offset] = (double)(fscale * array[count++] + fzero); if (count == nelements()) break; for (i = 0; i < dims(); ++i) { ++sub[i]; if (sub[i] == dim(i)) sub[i] = 0; else break; } } } else { uInt nmax = nelements(); if (!blanked) { for (uInt n = 0; n < nmax; ++n) target[n] = (double)(fscale * array[n] + fzero); } else { for (uInt n = 0; n < nmax; ++n) { target[n] = array[n] != blankval ? (double)(fscale * array[n] + fzero) : nan; } } } } //=================================================================================== template void PrimaryArray::copy(float *target, int npixels) const { if (npixels < 0 || npixels > end_elem - beg_elem + 1) { PrimaryArray *This = (PrimaryArray *)this; This->errmsg(BADSIZE, "npixels<0 or > number of read pixels"); } float scale = bscale(); float zero = bzero(); if (!isablank() || FitsFPUtil::isFP((TYPE *)0)) { // No blanks or we are FP for (int i=0; i void PrimaryArray::copy(float *target, FITS::FitsArrayOption opt) const { uInt count; Int offset, *sub, *C_factor; float fscale = (float)bscale(); float fzero = (float)bzero(); Bool blanked = isablank() && !FitsFPUtil::isFP((TYPE *)0) ? True:False; TYPE blankval = TYPE(0); if (blanked) { blankval = blank(); } float nan; FitsFPUtil::setNaN(nan); if (opt == FITS::FtoC) { sub = &factor[dims()]; C_factor = &sub[dims()]; // compute the C_factors Int i; for (i = 0; i < (dims() - 1); ++i) { C_factor[i] = 1; for (Int j = i + 1; j < dims(); ++j) C_factor[i] *= dim(j); } C_factor[i] = 1; // algorithm for converting F-order to C-order count = 0; for (i = 0; i < dims(); ++i) sub[i] = 0; for(;;) { for (i = 0, offset = 0; i < dims(); ++i) offset += sub[i] * C_factor[i]; target[offset] = (float)(fscale * array[count++] + fzero); if (count == nelements()) break; for (i = 0; i < dims(); ++i) { ++sub[i]; if (sub[i] == dim(i)) sub[i] = 0; else break; } } } else { uInt nmax = nelements(); if (!blanked) { for (uInt n = 0; n < nmax; ++n) target[n] = (float)(fscale * array[n] + fzero); } else { for (uInt n = 0; n < nmax; ++n) { target[n] = array[n] != blankval ? (float)(fscale * array[n] + fzero) : nan; } } } } //============================================================================== template void PrimaryArray::move(TYPE *target, int npixels) const { if (npixels < 0 || npixels > end_elem - beg_elem + 1) { PrimaryArray *This = (PrimaryArray *)this; This->errmsg(BADSIZE, "npixels<0 or > number of read pixels"); } memcpy(target, array, npixels*localitemsize()); } template void PrimaryArray::move(TYPE *target, FITS::FitsArrayOption opt) const { uInt count, offset; Int *sub, *C_factor; if (opt == FITS::FtoC) { sub = &factor[dims()]; C_factor = &sub[dims()]; // compute the C_factors Int i; for (i = 0; i < uInt(dims() - 1); ++i) { C_factor[i] = 1; for (Int j = i + 1; j < uInt(dims()); ++j) C_factor[i] *= dim(j); } C_factor[i] = 1; // algorithm for converting F-order to C-order count = 0; for (i = 0; i < uInt(dims()); ++i) sub[i] = 0; for(;;) { for (i = 0, offset = 0; i < uInt(dims()); ++i) offset += sub[i] * C_factor[i]; target[offset] = array[count++]; if (count == uInt(nelements())) break; for (i = 0; i < uInt(dims()); ++i) { ++sub[i]; if (sub[i] == dim(i)) sub[i] = 0; else break; } } } else { memcpy(target,array,(localitemsize() * nelements())); } } //=========================================================================================== // write required keywords for primary header. /* Write the primary header keywords into the CHU. The PCOUNT, GCOUNT and EXTEND keywords are not required in the primary header and are only written if the parameter of ffphpr(), pcount is not equal to zero, gcount is not equal to zero or one, and if extend is TRUE, respectively. */ template int PrimaryArray::write_priArr_hdr( FitsOutput &fout, // I - FITS output object int simple, // I - does file conform to FITS standard? 1/0 int bitpix, // I - number of bits per data value pixel int naxis, // I - number of axes in the data array long naxes[], // I - length of each data axis int extend ) // I - may FITS file have extensions? { // primary array header is the beginning of FITS file. So no need to call flush_buffer(). // flush m_buffer first //fout.getfout().flush_buffer(); int l_status = 0; // IO - error status if ( fout.rectype() != FITS::InitialState) { errmsg(BADOPER,"[PrimaryArray::write_priArr_hdr()] Primary Header must be the beginning of the file."); return -1; } // write the required keywords for PrimaryArray to fitsfile. long pcount = 0; // number of group parameters (usually 0) long gcount = 1; // number of random groups (usually 1 or 0) // taking pcount = 0, and gcount<1 will make ffpgpr() write the keywords for primary header only. // if pcount!=0 or gcount >1 ffphpr will write pcount and gcount(for PrimaryGroup). If extend // has a value of true( or >0), ffphpr will also write EXTEND keyword if(ffphpr(fout.getfptr(), simple, bitpix, naxis, naxes, pcount, gcount, extend, &l_status)){ errmsg(BADOPER,"Write Primary header failed![PrimaryArray::write_priArr_hdr]"); fits_report_error(stderr, l_status); // print error report return -1; } OFF_T l_headstart, l_datastart, l_dataend; l_status = 0; // get size info of the current HDU if (ffghof(fout.getfptr(), &l_headstart, &l_datastart, &l_dataend, &l_status) > 0){ fits_report_error(stderr, l_status); // print error report return -1; } // flush the buffer of CFITSIO so that the contents will be actually written to disk. if(ffflsh(fout.getfptr(), TRUE, &l_status)){ errmsg(BADOPER,"[PrimaryArray::write_priArr_hdr()] Error flushing buffer!"); } // move file pointer to the beginning of the new hdu. l_status = 0; if(ffmbyt(fout.getfptr(), l_headstart, REPORT_EOF, &l_status)){ fits_report_error(stderr, l_status); // print error report return -1; } // using the cfitsio function to read bytes from the file // pointed to by getfptr() from where the file position indicator currently at. l_status = 0; char * l_headerbytes = new char[l_datastart - l_headstart + 1]; if(ffgbyt(fout.getfptr(), l_datastart - l_headstart, l_headerbytes, &l_status)){ fits_report_error(stderr, l_status); // print error report return -1; } /*if(ffmbyt(fout.getfptr(), l_datastart, REPORT_EOF, &l_status)){ fits_report_error(stderr, l_status); // print error report return -1; }*/ // ffgbyt() sometimes does not move bytepos to the new position. So we do it. (fout.getfptr()->Fptr)->bytepos = l_datastart; // now parse the headerbytes into kwlist_. init_data_unit will use kwlist_. fout.getkc().parse( l_headerbytes, kwlist_ ,0, errfn,True); // init the info for the data unit init_data_unit( FITS::PrimaryArrayHDU ); // assign the binary table. This is done in constructor for the case when user // is required to provide a FitsKeywordList object. pa_assign(); // set the info for data unit. init_data_unit() generated the hdu_type, data_type, // fits_data_size and fits_item_size fout.set_data_info(kwlist_,hdu_type,data_type,fits_data_size,fits_item_size); return 0; } //==================================================================================== template ImageExtension::ImageExtension(FitsInput &f, FITSErrorHandler errhandler) : PrimaryArray(f,FITS::ImageExtensionHDU,errhandler) { ie_assign(); } //==================================================================================== template ImageExtension::ImageExtension(FitsKeywordList &k, FITSErrorHandler errhandler) : PrimaryArray(k,FITS::ImageExtensionHDU,errhandler) { ie_assign(); } //==================================================================================== // constructor that does not require a FitsKeywordList object as parameter template ImageExtension::ImageExtension(FITSErrorHandler errhandler) : PrimaryArray(FITS::ImageExtensionHDU,errhandler) { // ie_assign(); // in the case when user is not required to provide a kerword list object, // it must be called from write_imgext_hdr(). } //==================================================================================== template ImageExtension::~ImageExtension() { if (xtension_x != &char_null) delete [] xtension_x; if (extname_x != &char_null) delete [] extname_x; } //==================================================================================== template void ImageExtension::ie_assign() { extver_x = kwlist_(FITS::EXTVER) == 0 ? FITS::minInt : kwlist_.curr()->asInt(); extlevel_x = kwlist_(FITS::EXTLEVEL) == 0 ? FITS::minInt : kwlist_.curr()->asInt(); pcount_x = kwlist_(FITS::PCOUNT) == 0 ? FITS::minInt : kwlist_.curr()->asInt(); gcount_x = kwlist_(FITS::GCOUNT) == 0 ? FITS::minInt : kwlist_.curr()->asInt(); xtension_x = assign(FITS::XTENSION); extname_x = assign(FITS::EXTNAME); } //==================================================================================================== // write required keywords for ImageExtension template int ImageExtension::write_imgExt_hdr( FitsOutput &fout, // I - FITS output object int bitpix, /* I - bits per pixel */ int naxis, /* I - number of axes in the array */ long *naxes) /* I - size of each axis */ { // flush m_buffer first fout.getfout().flush_buffer(); int l_status = 0; // IO - error status if ( fout.rectype() == FITS::InitialState) { errmsg(BADOPER,"A Primary Header must be written first[ImageExtension::write_imgext_hdr]."); return -1; } if (!fout.hdu_complete()) { errmsg(BADOPER,"Previous HDU incomplete -- cannot write header[ImageExtension::write_imgext_hdr]."); return -1; } if( !fout.isextend() ) { errmsg(BADOPER,"Extensions are not allowed for this FITS file![ImageExtension::write_imgext_hdr]."); return -1; } if( !fout.required_keys_only() ){ cerr << "\n[ImageExtension::write_imgTbl_hdr()] write_imgTbl_hdr() works with other write_***_hdr()" << endl; cerr << " methods only. It will not work with write_hdr()." << endl; errmsg(BADOPER,"Used wrong header-writting function." ); return -1; } // Since the original file pointer does not have the hdu info about the hdu created by // write_hdu() method, we reopen the file to get a new file pointer with all the hdu info. // This may cause some loss of efficiency. But so far I have not found a better way. fitsfile* l_newfptr = 0; l_status = 0; //(fout.getfout()).close_file( fout.getfptr(), &l_status); //file_close( (fout.getfptr()->Fptr)->filehandle); const char * l_filename = (fout.getfout()).fname(); if (ffopen( &l_newfptr, l_filename, READWRITE, &l_status )){ //if(ffreopen( fout.getfptr(), &l_newfptr, &l_status )){ errmsg(BADOPER,"[ImageExtension::write_imgExt_hdr()] ffopen() CHDU failed!"); fits_report_error(stderr, l_status); // print error report return -1; } //ffmahd(l_newfptr, 1, 0, &l_status); // write the required keywords for ImageExtension to fitsfile. l_status = 0; if(ffcrim(l_newfptr, bitpix, naxis, naxes, &l_status)){ errmsg(BADOPER,"Write Image header failed![ImageExtension::write_imgext_hdr]"); fits_report_error(stderr, l_status); // print error report return -1; } // flush the buffer of CFITSIO so that the contents will be actually written to disk. // oopes, if we call ffflsh() in this case, the fits file is damaged! why? //if(ffflsh(l_newfptr, TRUE, &l_status)){ errmsg(BADOPER,"[PrimaryArray::write_priArr_hdr()] Error flushing buffer!"); } OFF_T l_headstart, l_datastart, l_dataend; l_status = 0; // get size info of the current HDU if (ffghof(l_newfptr, &l_headstart, &l_datastart, &l_dataend, &l_status) > 0){ fits_report_error(stderr, l_status); // print error report return -1; } // move file pointer to the beginning of the new hdu. l_status = 0; if(ffmbyt(l_newfptr, l_headstart, REPORT_EOF, &l_status)){ fits_report_error(stderr, l_status); // print error report return -1; } // using the cfitsio function to read bytes from the file // pointed to by getfptr() from where the file position indicator currently at. l_status = 0; char * l_headerbytes = new char[l_datastart - l_headstart + 1]; if(ffgbyt(l_newfptr, l_datastart - l_headstart, l_headerbytes, &l_status)){ fits_report_error(stderr, l_status); // print error report return -1; } // ffgbyt() sometimes does not move bytepos to the new position. So we do it. (l_newfptr->Fptr)->bytepos = l_datastart; // update the file pointer in FitsOutput. Always do this before updating the file pointer in BlockOutpu. fout.setfptr( l_newfptr ); // update the file pointer in BlockOutput. fout.getfout().setfptr( l_newfptr ); // now parse the headerbytes into kwlist_. init_data_unit will use kwlist_. fout.getkc().parse( l_headerbytes, kwlist_ ,0, errfn,True); // init the info for the data unit init_data_unit( FITS::ImageExtensionHDU ); // Call the parent pa_assign() method to assign the binary table. This is done in the // parent constructor for the case when user is required to provide a FitsKeywordList object. pa_assign(); // assign the binary table. This is done in constructor for the case when user // is required to provide a FitsKeywordList object. ie_assign(); // set the info for data unit. init_data_unit() generated the hdu_type, data_type, // fits_data_size and fits_item_size fout.set_data_info(kwlist_,hdu_type,data_type,fits_data_size,fits_item_size); return 0; } //=================================================================================================== template PrimaryGroup::PrimaryGroup(FitsInput &f, FITSErrorHandler errhandler) : PrimaryArray(f,FITS::PrimaryGroupHDU,errhandler) { pg_assign(); } //=================================================================================================== template PrimaryGroup::PrimaryGroup(FitsKeywordList &k, FITSErrorHandler errhandler) : PrimaryArray(k,FITS::PrimaryGroupHDU,errhandler) { pg_assign(); } //=================================================================================================== // constructor for the situation when only reqiured keywords for random groups // are to be written. template PrimaryGroup::PrimaryGroup(FITSErrorHandler errhandler) : PrimaryArray(FITS::PrimaryGroupHDU,errhandler) { // pg_assign(); // in the case when user is not required to provide a kerword list object, // pg_assign() must be called from write_priGrp_hdr(). } //=================================================================================================== template PrimaryGroup::~PrimaryGroup() { if (pcount_x > 0) { for (int i = 0; i < pcount_x; i++) if (ptype_x[i] != &char_null) delete [] ptype_x[i]; delete [] ptype_x; delete [] pzero_x; delete [] pscal_x; } delete [] group_parm; array = 0; // reset array to 0 so PrimaryArray won't delete anything } //=================================================================================================== // write the required keyword for promaryGroup( random groups) /* taking pcount = 0, and gcount<1 will make ffpgpr() write the keywords for primary header only. if pcount!=0 or gcount >1 ffphpr() will write pcount and gcount(for PrimaryGroup). If extend has a value of true( or >0), ffphpr() will also write EXTEND keyword */ template int PrimaryGroup::write_priGrp_hdr( FitsOutput &fout, // I - FITS output object int simple, // I - does file conform to FITS standard? 1/0 int bitpix, // I - number of bits per data value pixel int naxis, // I - number of axes in the data array long naxes[], // I - length of each data axis(make naxes[0]=0 for Primary Group) long pcount, // I - number of group parameters ( none 0 for Primary Group) long gcount ) // I - number of random groups ( greater than 1 for PrimaryGroup) { if( naxes[0] != 0 ){ errmsg(BADOPER,"[PrimaryGroup::write_priGrp_hdr())] For Primary Group, keyword NAXIS1 must be 0!"); return -1; } if( pcount < 1 && gcount <2 ){ errmsg(BADOPER,"[PrimaryGroup::write_priGrp_hdr())] In order to create keyword GROUPS, PCOUNT and GCOUNT, either PCOUNT's value must be >0 or GCOUNT's value must be > 1 !"); return -1; } // flush m_buffer first fout.getfout().flush_buffer(); int l_status = 0; // IO - error status if ( fout.rectype() != FITS::InitialState) { errmsg(BADOPER,"Primary Header must be the begnning of the file[PrimaryGroup::write_priGrp_hdr]."); return -1; } // write the required keywords for PrimaryGroup to fitsfile. int extend = 0; // EXTEND is not required by random group. Set it to zero so that it will not be written. if(ffphpr(fout.getfptr(), simple, bitpix, naxis, naxes, pcount, gcount, extend, &l_status)){ errmsg(BADOPER,"Write PrimaryGroup header failed![PrimaryGroup::write_priGrp_hdr]"); fits_report_error(stderr, l_status); // print error report return -1; } OFF_T l_headstart, l_datastart, l_dataend; l_status = 0; // get size info of the current HDU if (ffghof(fout.getfptr(), &l_headstart, &l_datastart, &l_dataend, &l_status) > 0){ fits_report_error(stderr, l_status); // print error report return -1; } // move file pointer to the beginning of the new hdu. l_status = 0; if(ffmbyt(fout.getfptr(), l_headstart, REPORT_EOF, &l_status)){ fits_report_error(stderr, l_status); // print error report return -1; } // using the cfitsio function to read bytes from the file // pointed to by getfptr() from where the file position indicator currently at. l_status = 0; char * l_headerbytes = new char[l_datastart - l_headstart + 1]; if(ffgbyt(fout.getfptr(), l_datastart - l_headstart, l_headerbytes, &l_status)){ fits_report_error(stderr, l_status); // print error report return -1; } // ffgbyt() sometimes does not move bytepos to the new position. So we do it. (fout.getfptr()->Fptr)->bytepos = l_datastart; // now parse the headerbytes into kwlist_. init_data_unit will use kwlist_. fout.getkc().parse( l_headerbytes, kwlist_ ,0, errfn,True); // init the info for the data unit init_data_unit( FITS::PrimaryGroupHDU ); // Call the parent pa_assign() method to assign the binary table. This is done in the // parent constructor for the case when user is required to provide a FitsKeywordList object. pa_assign(); // assign the binary table. This is done in constructor for the case when user // is required to provide a FitsKeywordList object. pg_assign(); // set the info for data unit. init_data_unit() generated the hdu_type, data_type, // fits_data_size and fits_item_size fout.set_data_info(kwlist_,hdu_type,data_type,fits_data_size,fits_item_size); return 0; } //===================================================================================== template void PrimaryGroup::pg_assign() { int i; ptype_x = 0; pscal_x = 0; pzero_x = 0; pcount_x = 0; gcount_x = 0; group_parm = 0; current_group = 0; if (err_status != OK) // check for previous errors return; // get gcount and pcount keywords pcount_x = kwlist_(FITS::PCOUNT)->asInt(); gcount_x = kwlist_(FITS::GCOUNT)->asInt(); // and assign keyword values if (pcount_x > 0) { pscal_x = new double [pcount_x]; pzero_x = new double [pcount_x]; ptype_x = new char * [pcount_x]; if (pscal_x == 0 || pzero_x == 0 || ptype_x == 0) { errmsg(NOMEM,"Cannot allocate memory"); return; } for (i = 0; i < pcount_x; i++) { pscal_x[i] = asgdbl(FITS::PSCAL,(i + 1),1.0); pzero_x[i] = asgdbl(FITS::PZERO_FITS,(i + 1),0.0); ptype_x[i] = assign(FITS::PTYPE,(i + 1)); } } // must recompute the array dimensions and factors // and set the group sizes totsize = dimn[1]; for (i = 2; i < no_dims; ++i) totsize *= dimn[i]; factor[0] = 1; for (i = 1; i < (no_dims - 1); ++i) factor[i] = factor[i - 1] * dimn[i]; for (i = 0; i < (no_dims - 1); ++i) dimn[i] = dimn[i+ 1]; --no_dims; // adjust all axis-relative keyword values if (ctype_x[0] != &char_null) delete [] ctype_x[0]; for (i = 0; i < no_dims; ++i) { crpix_x[i] = crpix_x[i + 1]; crota_x[i] = crota_x[i + 1]; crval_x[i] = crval_x[i + 1]; cdelt_x[i] = cdelt_x[i + 1]; ctype_x[i] = ctype_x[i + 1]; } // allocate buffer space for an entire group group_parm = new TYPE [pcount() + nelements()]; if (group_parm == 0) { errmsg(NOMEM,"Cannot allocate memory"); return; } array = &group_parm[pcount()]; // set location of array } //==================================================================================== template double PrimaryGroup::parm(int n) { return pscal(n) * group_parm[n] + pzero(n); } //==================================================================================== template TYPE & PrimaryGroup::rawparm(int n) { return group_parm[n]; } //==================================================================================== template void PrimaryGroup::storeparm(const TYPE *source) { memcpy(group_parm,source,(localitemsize() * pcount())); } //==================================================================================== template void PrimaryGroup::copyparm(double *target) const { for (Int i = 0; i < pcount(); ++i) target[i] = pscal(i) * group_parm[i] + pzero(i); } //==================================================================================== template void PrimaryGroup::copyparm(float *target) const { for (Int i = 0; i < pcount(); ++i) target[i] = (float)(pscal(i) * group_parm[i] + pzero(i)); } //==================================================================================== template void PrimaryGroup::moveparm(TYPE *target) const { memcpy(target,group_parm,(localitemsize() * pcount())); } //==================================================================================== template int PrimaryGroup::read() { // read the data Int nb = fitsitemsize() * (pcount() + nelements()); if (read_data((char *)group_parm,nb) != nb) { //error message return -1; } // do the FITS to local conversion, including worrying about // the fact that array and FITS size may not be the same // ... uInt ne = nb / fitsitemsize(); // the actual number of elements read FITS::f2l( (TYPE *)group_parm, group_parm, ne ); ++current_group; return 0; } //=================================================================================== template int PrimaryGroup::write(FitsOutput &fout) { // do the FITS to local conversion, including worrying about // the fact that array and FITS size may not be the same // ... int ne = pcount() + nelements(); FITS::l2f( group_parm, (TYPE *)group_parm, ne ); // write the current group OFF_T nb = fitsitemsize() * (pcount() + nelements()); if (write_data(fout,(char *)group_parm,nb) != 0) { errmsg(BADIO,"Error writing group"); return -1; } ++current_group; return 0; } //==================================================================================== template PrimaryTable::PrimaryTable(FitsInput &f, FITSErrorHandler errhandler) : PrimaryArray(f,FITS::PrimaryTableHDU,errhandler) { pt_assign(); } //==================================================================================== template PrimaryTable::PrimaryTable(FitsKeywordList &k, FITSErrorHandler errhandler) : PrimaryArray(k,FITS::PrimaryTableHDU,errhandler) { pt_assign(); } //==================================================================================== // constructor that does not require a FitsKeywordList object as parameter template PrimaryTable::PrimaryTable(FITSErrorHandler errhandler) : PrimaryArray(FITS::PrimaryTableHDU,errhandler) { // pt_assign(); // in the case when user is not required to provide a kerword list object, // it must be called from write_priTable_hdr(). } //==================================================================================== template PrimaryTable::~PrimaryTable() { if (object_x != &char_null) delete [] object_x; if (telescop_x != &char_null) delete telescop_x; } //==================================================================================== template void PrimaryTable::pt_assign() { object_x = assign(FITS::OBJECT); telescop_x = assign(FITS::TELESCOP); instrume_x = assign(FITS::INSTRUME); dateobs_x = assign(FITS::DATE_OBS); datemap_x = assign(FITS::DATE_MAP); //bscale_x = kwlist_(FITS::BSCALE)->asFloat(); //bzero_x = kwlist_(FITS::BZERO)->asFloat(); //bunit_x = assign(FITS::BUNIT); //equinox_x = kwlist_(FITS::EQUINOX)->asFloat(); //altrpix_x = kwlist_(FITS::ALTRPIX)->asFloat(); } template int PrimaryTable::read() { //return PrimaryArray::read(); return 0; } //==================================================================================================== // write required keywords for PrimaryTable template int PrimaryTable::write_priTable_hdr( FitsOutput &fout, // I - FITS output object int bitpix, /* I - bits per pixel */ int naxis, /* I - number of axes in the array */ long *naxes) /* I - size of each axis */ { // flush m_buffer first fout.getfout().flush_buffer(); int l_status = 0; // IO - error status if ( fout.rectype() == FITS::InitialState) { errmsg(BADOPER,"A Primary Header must be written first[PrimaryTable::write_priTable_hdr]."); return -1; } if (!fout.hdu_complete()) { errmsg(BADOPER,"Previous HDU incomplete -- cannot write header[PrimaryTable::write_priTable_hdr]."); return -1; } if( !fout.isextend() ) { errmsg(BADOPER,"Extensions are not allowed for this FITS file![PrimaryTable::write_priTable_hdr]."); return -1; } if( !fout.required_keys_only() ){ cerr << "\n[PrimaryTable::write_priTable_hdr()] write_priTable_hdr() " << "works with other write_***_hdr()" << endl; cerr << " methods only. It will not work with write_hdr()." << endl; errmsg(BADOPER,"Used wrong header-writting function." ); return -1; } // Since the original file pointer does not have the hdu info about the hdu created by // write_hdu() method, we reopen the file to get a new file pointer with all the hdu info. // This may cause some loss of efficiency. But so far I have not found a better way. fitsfile* l_newfptr = 0; l_status = 0; //(fout.getfout()).close_file( fout.getfptr(), &l_status); //file_close( (fout.getfptr()->Fptr)->filehandle); const char * l_filename = (fout.getfout()).fname(); if (ffopen( &l_newfptr, l_filename, READWRITE, &l_status )){ //if(ffreopen( fout.getfptr(), &l_newfptr, &l_status )){ errmsg(BADOPER,"[PrimaryTable::write_priTable_hdr()] ffopen() CHDU failed!"); fits_report_error(stderr, l_status); // print error report return -1; } //ffmahd(l_newfptr, 1, 0, &l_status); // write the required keywords for PrimaryTable to fitsfile. l_status = 0; if(ffcrim(l_newfptr, bitpix, naxis, naxes, &l_status)){ errmsg(BADOPER,"Write Image header failed![PrimaryTable::write_priTable_hdr]"); fits_report_error(stderr, l_status); // print error report return -1; } // flush the buffer of CFITSIO so that the contents will be actually written to disk. // oopes, if we call ffflsh() in this case, the fits file is damaged! why? //if(ffflsh(l_newfptr, TRUE, &l_status)){ errmsg(BADOPER,"[PrimaryArray::write_priArr_hdr()] Error flushing buffer!"); } OFF_T l_headstart, l_datastart, l_dataend; l_status = 0; // get size info of the current HDU if (ffghof(l_newfptr, &l_headstart, &l_datastart, &l_dataend, &l_status) > 0){ fits_report_error(stderr, l_status); // print error report return -1; } // move file pointer to the beginning of the new hdu. l_status = 0; if(ffmbyt(l_newfptr, l_headstart, REPORT_EOF, &l_status)){ fits_report_error(stderr, l_status); // print error report return -1; } // using the cfitsio function to read bytes from the file // pointed to by getfptr() from where the file position indicator currently at. l_status = 0; char * l_headerbytes = new char[l_datastart - l_headstart + 1]; if(ffgbyt(l_newfptr, l_datastart - l_headstart, l_headerbytes, &l_status)){ fits_report_error(stderr, l_status); // print error report return -1; } // ffgbyt() sometimes does not move bytepos to the new position. So we do it. (l_newfptr->Fptr)->bytepos = l_datastart; // update the file pointer in FitsOutput. Always do this before updating the file pointer in BlockOutpu. fout.setfptr( l_newfptr ); // update the file pointer in BlockOutput. fout.getfout().setfptr( l_newfptr ); // now parse the headerbytes into kwlist_. init_data_unit will use kwlist_. fout.getkc().parse( l_headerbytes, kwlist_ ,0, errfn,True); // init the info for the data unit init_data_unit( FITS::PrimaryTableHDU ); // Call the parent pa_assign() method to assign the binary table. This is done in the // parent constructor for the case when user is required to provide a FitsKeywordList object. pa_assign(); // assign the binary table. This is done in constructor for the case when user // is required to provide a FitsKeywordList object. pt_assign(); // set the info for data unit. init_data_unit() generated the hdu_type, data_type, // fits_data_size and fits_item_size fout.set_data_info(kwlist_,hdu_type,data_type,fits_data_size,fits_item_size); return 0; } //================================================================================== template FitsField::~FitsField() { } //================================================================================== template int FitsField::fitsfieldsize() const { return FITS::fitssize(data_type) * no_elements; } //================================================================================== template int FitsField::localfieldsize() const { return FITS::localsize(data_type) * no_elements; } //=================================================================================== template void * FitsField::data() { return *field; } //=================================================================================== template void FitsField::setaddr(void **addr) { field = (TYPE **)addr; } //=================================================================================== template void FitsField::show(std::ostream &o) { int i; unsigned char *s; char *p; if (no_elements == 0) return; if (fieldtype() == FITS::BYTE) { s = (unsigned char *)(*field); o << (int)s[0]; for (i = 1; i < no_elements; ++i) o << ", " << (int)s[i]; } else if (fieldtype() == FITS::CHAR) { p = (char *)(*field); for (i = 0; i < no_elements && *p != '\0'; ++i, ++p) o << *p; } else { o << (*field)[0]; for (i = 1; i < no_elements; ++i) o << ", " << (*field)[i]; } } //============================================================================ template FitsArray::FitsArray(int n, const int *d) : FitsField(1) { int i; if (n > 0) { no_dims = n; dimn = new int [no_dims]; factor = new int [no_dims]; dimn[0] = d[0]; no_elements = dimn[0]; for (i = 1; i < no_dims; ++i) { dimn[i] = d[i]; no_elements *= d[i]; } factor[0] = 1; for (i = 1; i < no_dims; ++i) factor[i] = factor[i - 1] * dimn[i - 1]; } else { no_dims = 1; dimn = 0; factor = 0; no_elements = 1; } } //============================================================================ template FitsArray::~FitsArray() { delete [] dimn; delete [] factor; } //============================================================================ template int FitsArray::dims() const { return no_dims; } //============================================================================ template int FitsArray::dim(int n) const { return dimn[n]; } //============================================================================ template int * FitsArray::vdim() { return dimn; } //============================================================================ template inline TYPE & FitsArray::operator () (int d0, int d1) { return (*field)[d0 + (factor[1] * d1)]; } //============================================================================ template inline TYPE & FitsArray::operator () (int d0, int d1, int d2) { return (*field)[d0 + (factor[1] * d1) + (factor[2]*d2)]; } //============================================================================ template inline TYPE & FitsArray::operator () (int d0, int d1, int d2, int d3) { return (*field)[d0 + (factor[1] * d1) + (factor[2]*d2) + (factor[3]*d3)]; } //============================================================================= template inline TYPE & FitsArray::operator () (int d0, int d1, int d2, int d3, int d4) { return (*field)[d0 + (factor[1] * d1) + (factor[2]*d2) + (factor[3]*d3) + (factor[4]*d4)]; } //============================================================================= } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/fits/FITS/hdu2.cc000066400000000000000000002353621476623553700162220ustar00rootroot00000000000000//# hdu2.cc: //# Copyright (C) 1993,1994,1995,1996,1997,1999,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA # include # include # include # include # include # include # include //# include namespace casacore { //# NAMESPACE CASACORE - BEGIN //== FitsBit specializations ================================================== FitsField::FitsField(int n) : FitsBase(FITS::BIT,n), field(0) { } FitsField::~FitsField() { } //============================================================================= int FitsField::fitsfieldsize() const { int n = no_elements / 8; if (no_elements % 8 != 0) ++n; return n; } //============================================================================= int FitsField::localfieldsize() const { int n = no_elements / 8; if (no_elements % 8 != 0) ++n; return n * sizeof(FitsBit); } //============================================================================= void * FitsField::data() { return *field; } //============================================================================= void FitsField::setaddr(void **addr) { field = (FitsBit **)addr; } //============================================================================= void FitsField::show(ostream &o) { for (int i = 0; i < no_elements; ++i) o << (int)((*this)(i)); } //================================================================================= FitsField & FitsArray::operator () (int d0, int d1) { FitsField *tmp = this; return (*tmp)(d0 + d1 * factor[1]); } //================================================================================= FitsField & FitsArray::operator () (int d0, int d1, int d2) { FitsField *tmp = this; return (*tmp)(d0 + d1 * factor[1] + d2*factor[2]); } //================================================================================= FitsField & FitsArray::operator () (int d0, int d1, int d2, int d3) { FitsField *tmp = this; return (*tmp)(d0 + d1 * factor[1] + d2*factor[2] + d3*factor[3]); } //================================================================================= FitsField & FitsArray::operator () (int d0, int d1, int d2, int d3, int d4) { FitsField *tmp = this; return (*tmp)(d0 + d1 * factor[1] + d2*factor[2] + d3*factor[3] + d4*factor[4]); } //================================================================================= #if 0 FitsField & FitsArray::operator () (int d0, int d1, int d2, int d3, int d4, int d5 ...) { FitsField *tmp = this; if (dims() > 6) { int offset, i; va_list ap; offset = d0 + d1*factor[1] + d2*factor[2] + d3*factor[3] + d4*factor[4] + d5*factor[5]; va_start(ap,d5); for (i = 6; i < dims(); ++i) offset += va_arg(ap,int) * factor[i]; va_end(ap); return (*tmp)(offset); } else return (*tmp)(d0 + d1*factor[1] + d2*factor[2] + d3*factor[3] + d4*factor[4] + d5*factor[5]); } #endif //== HeaderDataUnit =========================================================== void HeaderDataUnit::errmsg(HDUErrs e, const char *s) { static char msgstring[180]; // storage for composing error messages ostringstream msgline; msgline << "HDU error: " << s << endl; err_status = e; // all of the errors which use this function are SEVERE strncpy(msgstring, msgline.str().c_str(), sizeof(msgstring)-1); errfn(msgstring, FITSError::SEVERE); } //== determine_type of HeaderDataUnit ======================================== // This function determines the HDU type and the data type Bool HeaderDataUnit::determine_type(FitsKeywordList &kw, FITS::HDUType &htype, FITS::ValueType &dtype, FITSErrorHandler errhandler, HDUErrs &errstat) { //cout << "HeaderDataUnit::determine_type kw=\n" << kw << endl; errstat = OK; // Get SIMPLE or XTENSION, BITPIX, NAXIS, and NAXIS1 kw.first(); FitsKeyword *word1 = kw.next(); //cout << "word1=" << *word1 << endl; if (!word1) { errstat = MISSKEY; errhandler("There are no keywords", FITSError::SEVERE); htype = FITS::NotAHDU; return False; } FitsKeyword *p_bitpix = kw.next(); FitsKeyword *naxis = kw.next(); FitsKeyword *naxis1 = kw.next(); FitsKeyword *naxis2 = kw.next(); if (!p_bitpix || (p_bitpix->kw().name() != FITS::BITPIX)) { p_bitpix = kw(FITS::BITPIX); // look for BITPIX elsewhere if (!p_bitpix || (p_bitpix->kw().name() != FITS::BITPIX)) { errstat = MISSKEY; errhandler("Missing required BITPIX keyword", FITSError::WARN); } else errhandler("Keyword BITPIX is out of order", FITSError::WARN); } if ((!naxis || !(naxis->kw().name() == FITS::NAXIS && naxis->index() == 0))) { naxis = kw(FITS::NAXIS); // look for NAXIS elsewhere if ((!naxis || !(naxis->kw().name() == FITS::NAXIS && naxis->index() == 0))) { errstat = MISSKEY; errhandler("Missing required NAXIS keyword.", FITSError::WARN); } else errhandler("Keyword NAXIS is out of order.", FITSError::WARN); } if ((errstat == OK) && (naxis->asInt() != 0)) { if (!naxis1 || !(naxis1->kw().name() == FITS::NAXIS && naxis1->index() == 1)) { naxis1 = kw(FITS::NAXIS,1); if (!naxis1 || !(naxis1->kw().name() == FITS::NAXIS && naxis1->index() == 1)) { errstat = MISSKEY; errhandler("Missing required NAXIS1 keyword.", FITSError::WARN); } else errhandler("Keyword NAXIS1 is out of order.", FITSError::WARN); } } if (word1->kw().name() != FITS::SIMPLE && word1->kw().name() != FITS::XTENSION) { word1 = kw(FITS::SIMPLE); // look for SIMPLE if (!word1) { word1 = kw(FITS::XTENSION); // look for XTENSION if (word1) errhandler("Keyword XTENSION is out of order.", FITSError::WARN); else errhandler("Missing keywords SIMPLE and XTENSION.", FITSError::WARN); } else errhandler("Keyword SIMPLE is out of order.", FITSError::WARN); } if (!word1) { errstat = BADREC; errhandler("Unrecognizeable record.", FITSError::SEVERE); } if (errstat != OK) { htype = FITS::NotAHDU; return False; } // OK, got'em Int bitpix = p_bitpix->asInt(); // get value of BITPIX switch (bitpix) { case 8: dtype = FITS::BYTE; break; case 16: dtype = FITS::SHORT; break; case 32: dtype = FITS::LONG; break; case -32: dtype = FITS::FLOAT; break; case -64: dtype = FITS::DOUBLE; break; default: errstat = BADBITPIX; errhandler("Invalid value of BITPIX", FITSError::SEVERE); htype = FITS::NotAHDU; return False; } if (word1->kw().name() == FITS::SIMPLE) { //cout << "naxis=" << naxis->asInt() << " naxis2=" << naxis2->asInt() // << " naxis1=" << naxis1->asInt() << endl; if (naxis->asInt() > 0) { htype = FITS::PrimaryArrayHDU; if (naxis1->asInt() == 0) htype = FITS::PrimaryGroupHDU; else if (naxis->asInt() == 2 && (naxis2->asInt() == 0 && naxis1->asInt() == 777777701)) htype = FITS::PrimaryTableHDU; else htype = FITS::PrimaryArrayHDU; //cout << "htype=" << htype << endl; } else htype = FITS::PrimaryArrayHDU; //cout << "<asString(),"BINTABLE") == 0) htype = FITS::BinaryTableHDU; // For backward compatibility with many ancient NRAO files // written by classic AIPS. else if (strcmp(word1->asString(),"A3DTABLE") == 0) htype = FITS::BinaryTableHDU; else if (strcmp(word1->asString(),"IMAGE ") == 0) htype = FITS::ImageExtensionHDU; else htype = FITS::UnknownExtensionHDU; //cout << "<kw().name() == FITS::NAXIS && naxisn->index() == n)) { naxisn = kw(FITS::NAXIS,n); if (!naxisn || !(naxisn->kw().name() == FITS::NAXIS && naxisn->index() == n)) { st = NOAXISN; errhandler("Missing required NAXISn keyword", FITSError::SEVERE); datasize = 0; htype = FITS::NotAHDU; return False; } else{ errhandler("NAXISn keyword is out of order.", FITSError::WARN); } } // end of first if(!naxisn || ...). datasize *= naxisn->asInt(); }// end of for loop. datasize *= FITS::fitssize(dtype); return True; }// end of if( htype == ...). // Primary Table HDU else if (htype == FITS::PrimaryTableHDU) { //NAXIS1 = 777777701 and NAXIS2 = 0 datasize = 0; //by definition return True; } // Primary Group HDU else if ( htype == FITS::PrimaryGroupHDU) { datasize = 1; kw.next(); // skip NAXIS1; for (n = 2; n <= dims; n++) { naxisn = kw.next(); if (!naxisn || !(naxisn->kw().name() == FITS::NAXIS && naxisn->index() == n)) { naxisn = kw(FITS::NAXIS,n); if (!naxisn || !(naxisn->kw().name() == FITS::NAXIS && naxisn->index() == n)) { st = NOAXISN; errhandler("Missing required NAXISn keyword", FITSError::SEVERE); datasize = 0; htype = FITS::NotAHDU; return False; } else errhandler("NAXISn keyword is out of order.", FITSError::WARN); } datasize *= naxisn->asInt(); } if (!kw(FITS::PCOUNT)) { st = NOPCOUNT; errhandler("Missing required PCOUNT keyword", FITSError::SEVERE); datasize = 0; htype = FITS::NotAHDU; return False; } datasize += kw.curr()->asInt(); if (!kw(FITS::GCOUNT)) { st = NOGCOUNT; errhandler("Missing required GCOUNT keyword", FITSError::SEVERE); datasize = 0; htype = FITS::NotAHDU; return False; } datasize *= kw.curr()->asInt(); datasize *= FITS::fitssize(dtype); if (!kw(FITS::GROUPS)) { st = NOGROUPS; errhandler("Missing required GROUPS keyword", FITSError::WARN); } return True; } // Image Extension HDU else if ( htype == FITS::ImageExtensionHDU ) { datasize = 1; for (n = 1; n <= dims; n++) { naxisn = kw.next(); if (!naxisn || !(naxisn->kw().name() == FITS::NAXIS && naxisn->index() == n)) { naxisn = kw(FITS::NAXIS,n); if (!naxisn || !(naxisn->kw().name() == FITS::NAXIS && naxisn->index() == n)) { st = NOAXISN; errhandler("Missing required NAXISn keyword", FITSError::SEVERE); datasize = 0; htype = FITS::NotAHDU; return False; } else errhandler("NAXISn keyword is out of order.", FITSError::WARN); } datasize *= naxisn->asInt(); } if (!kw(FITS::PCOUNT)) { st = NOPCOUNT; errhandler("Missing required PCOUNT keyword", FITSError::WARN); } else if (kw.curr()->asInt() != 0) { st = BADPCOUNT; errhandler("Invalid value of PCOUNT keyword", FITSError::WARN); } if (!kw(FITS::GCOUNT)) { st = NOGCOUNT; errhandler("Missing required GCOUNT keyword", FITSError::WARN); } else if (kw.curr()->asInt() != 1) { st = BADGCOUNT; errhandler("Invalid value of GCOUNT keyword", FITSError::WARN); } datasize *= FITS::fitssize(dtype); return True; } // Conforming Extension HDU of unknown type else if ( htype == FITS::UnknownExtensionHDU ) { datasize = 1; for (n = 1; n <= dims; n++) { naxisn = kw.next(); if (!naxisn || !(naxisn->kw().name() == FITS::NAXIS && naxisn->index() == n)) { naxisn = kw(FITS::NAXIS,n); if (!naxisn || !(naxisn->kw().name() == FITS::NAXIS && naxisn->index() == n)) { st = NOAXISN; errhandler("Missing required NAXISn keyword", FITSError::SEVERE); datasize = 0; htype = FITS::NotAHDU; return False; } else errhandler("NAXISn keyword is out of order.", FITSError::SEVERE); } datasize *= naxisn->asInt(); } if (!kw(FITS::PCOUNT)) { st = NOPCOUNT; errhandler("Missing required PCOUNT keyword", FITSError::SEVERE); datasize = 0; htype = FITS::NotAHDU; return False; } datasize += kw.curr()->asInt(); if (!kw(FITS::GCOUNT)) { st = NOGCOUNT; errhandler("Missing required GCOUNT keyword", FITSError::SEVERE); datasize = 0; htype = FITS::NotAHDU; return False; } datasize *= kw.curr()->asInt(); datasize *= FITS::fitssize(dtype); return True; } // ASCII Table HDU else if ( htype == FITS::AsciiTableHDU ) { if (FITS::fitssize(dtype) != 1) { st = BADBITPIX; errhandler("BITPIX must be 8", FITSError::SEVERE); htype = FITS::NotAHDU; return False; } dtype = FITS::CHAR; // This is the proper type if (dims != 2) { st = BADNAXIS; errhandler("NAXIS must be 2", FITSError::SEVERE); htype = FITS::NotAHDU; return False; } datasize = 1; for (n = 1; n <= dims; n++) { naxisn = kw.next(); if (!naxisn || !(naxisn->kw().name() == FITS::NAXIS && naxisn->index() == n)) { st = NOAXISN; errhandler("Missing required NAXISn keyword", FITSError::SEVERE); datasize = 0; htype = FITS::NotAHDU; return False; } datasize *= naxisn->asInt(); } FitsKeyword *pcount = kw.next(); if (!pcount || !(pcount->kw().name() == FITS::PCOUNT && pcount->index() == 0)) { st = NOPCOUNT; errhandler("Missing required PCOUNT keyword", FITSError::WARN); } else if (pcount->asInt() != 0) { st = BADPCOUNT; errhandler("PCOUNT must be 0", FITSError::WARN); } FitsKeyword *gcount = kw.next(); if (!gcount || !(gcount->kw().name() == FITS::GCOUNT && gcount->index() == 0)) { st = NOGCOUNT; errhandler("Missing required GCOUNT keyword", FITSError::WARN); } else if (gcount->asInt() != 1) { st = BADGCOUNT; errhandler("GCOUNT must be 1", FITSError::WARN); } return True; } // Binary Table HDU else if ( htype == FITS::BinaryTableHDU ) { if (FITS::fitssize(dtype) != 1) { st = BADBITPIX; errhandler("BITPIX must be 8", FITSError::SEVERE); htype = FITS::NotAHDU; return False; } if (dims != 2) { st = BADNAXIS; errhandler("NAXIS must be 2", FITSError::SEVERE); htype = FITS::NotAHDU; return False; } datasize = 1; for (n = 1; n <= dims; n++) { naxisn = kw.next(); if (!naxisn || !(naxisn->kw().name() == FITS::NAXIS && naxisn->index() == n)) { st = NOAXISN; errhandler("Missing required NAXISn keyword", FITSError::SEVERE); datasize = 0; htype = FITS::NotAHDU; return False; } datasize *= naxisn->asInt(); } FitsKeyword *pcount = kw.next(); if (!pcount || !(pcount->kw().name() == FITS::PCOUNT && pcount->index() == 0)) { st = NOPCOUNT; errhandler("Missing required PCOUNT keyword", FITSError::WARN); } else { datasize += pcount->asInt(); // The heap convention } FitsKeyword *gcount = kw.next(); if (!gcount || !(gcount->kw().name() == FITS::GCOUNT && gcount->index() == 0)) { st = NOGCOUNT; errhandler("Missing required GCOUNT keyword", FITSError::WARN); } else if (gcount->asInt() != 1) { st = BADGCOUNT; errhandler("GCOUNT must be 1", FITSError::WARN); } return True; } return False; } //============================================================================ HeaderDataUnit::~HeaderDataUnit() { delete &kwlist_; delete [] dimn; } //================================================================================ HeaderDataUnit::HeaderDataUnit(FitsInput &f, FITS::HDUType t, FITSErrorHandler errhandler) : kwlist_(*(new FitsKeywordList)), constkwlist_(kwlist_), fin(&f), errfn(errhandler), err_status(OK), no_dims(0), dimn(0), fits_data_size(0), data_type(FITS::NOVALUE), fits_item_size(0), local_item_size(0), hdu_type(FITS::NotAHDU), pad_char('\0'), double_null(FITS::mindouble), char_null('\0'), Int_null(FITS::minInt) { if (fin->hdutype() != t) { errmsg(BADTYPE,"[HeaderDataUnit::HeaderDataUnit] " "Input does not contain an HDU of this type."); return; } //cout << ">>HeaderDataUnit::HeaderDataUnit - hdu_type=" << hdu_type // << " f.hdutype()=" << f.hdutype() // << " fin->hdutype()=" << fin->hdutype() << endl; hdu_type = fin->hdutype(); data_type = fin->datatype(); if (get_hdr(t,kwlist_) == -1) { // process the header records hdu_type = fin->hdutype(); err_status = BADSIZE; return; } //cout << "<hdutype()=" << fin->hdutype() << endl; //cout << "[HeaderDataUnit::HeaderDataUnit] kwlist_:\n" << kwlist_ << endl; if (hdu_type==FITS::PrimaryTableHDU) { //cout << "[HeaderDataUnit::HeaderDataUnit] kwlist_:\n" << kwlist_ << endl; return; } fits_data_size = fin->datasize(); // assign values fits_item_size = FITS::fitssize(data_type); local_item_size = FITS::localsize(data_type); //cout << "fits_data_size=" << fits_data_size // << "fits_item_size=" << fits_item_size // << "local_item_size=" << local_item_size // << endl; if(kwlist_(FITS::NAXIS) != 0){ //cout << "kwlist_(FITS::NAXIS) " << *kwlist_(FITS::NAXIS) << endl; no_dims = kwlist_(FITS::NAXIS)->asInt(); } else{ errmsg(NOAXISN, "[HeaderDataUnit::HeaderDataUnit] NAXIS keyword missing."); return; } //cout << "[HeaderDataUnit::HeaderDataUnit] no_dims=" << no_dims << endl; if (no_dims > 0) { if ((dimn = new Int [no_dims]) == 0) { errmsg(NOMEM,"[HeaderDataUnit::HeaderDataUnit] Cannot allocate memory."); no_dims = 0; return; } else { for (int i = 0; i < no_dims; i++) dimn[i] = kwlist_(FITS::NAXIS,(i + 1))->asInt(); } } //cout << "<hdutype()=" << fin->hdutype() << endl; } //================================================================================================= HeaderDataUnit::HeaderDataUnit(FitsKeywordList &k, FITS::HDUType t, FITSErrorHandler errhandler, FitsInput *f ) : kwlist_(*new FitsKeywordList(k)), constkwlist_(kwlist_), fin(f), errfn(errhandler), err_status(OK), no_dims(0), dimn(0), fits_data_size(0), data_type(FITS::NOVALUE), fits_item_size(0), local_item_size(0), hdu_type(FITS::NotAHDU), pad_char('\0'), double_null(FITS::mindouble), char_null('\0'), Int_null(FITS::minInt) { if( !init_data_unit( t )){ return; } } //================================================================================================= // Use this constructor to construct objects that write only required keywords to fitsfile. // the write method to call by these object should be those for the specific // hdu, such as write_binTbl_hdr(). HeaderDataUnit::HeaderDataUnit( FITS::HDUType, FITSErrorHandler errhandler, FitsInput *f ) : kwlist_(*new FitsKeywordList()), constkwlist_( kwlist_),fin(f), errfn(errhandler), err_status(OK), no_dims(0), dimn(0), fits_data_size(0), data_type(FITS::NOVALUE), fits_item_size(0), local_item_size(0), hdu_type(FITS::NotAHDU), pad_char('\0'), double_null(FITS::mindouble), char_null('\0'), Int_null(FITS::minInt) { // do not call init_data_unit() from here, since kwlist_ has no value yet. // init_data_unit() in this case will be called from write_XXX_hdr(); } //================================================================================================ // Call this after write the specific header( and generate the FitsKeywordList // the header info which has just been written.) bool HeaderDataUnit::init_data_unit( FITS::HDUType t ){ // kwlist_ is initialized in the constuctor or methods like write_bintbl_hdr() kwlist_.first(); FitsKeyword *fkw = kwlist_.curr(); if( fkw == 0 ){ errmsg(BADRULES,"Header is not constructed/written yet![HeaderDataUnit::init_data_unit]"); return false; }else{ //constkwlist_= *(new ConstFitsKeywordList(kwlist_)); // This might need to be recovered! //ConstFitsKeywordList constfkwl( kwlist_); //constkwlist_= constfkwl; } if ((!kwlist_.basic_rules()) || (kwlist_.rules(errfn) != 0)) { errmsg(BADRULES,"Errors in keyword list[HeaderDataUnit::init_data_unit]"); return false; } if (!determine_type(kwlist_,hdu_type,data_type,errfn,err_status)) { errmsg(BADTYPE,"Could not determine HDU type from keyword list [HeaderDataUnit::init_data_unit]"); hdu_type = FITS::NotAHDU; return false; } if (!compute_size(kwlist_,fits_data_size,no_dims, hdu_type,data_type,errfn,err_status)) { errmsg(BADSIZE,"Could not compute data size from keyword list[HeaderDataUnit::init_data_unit]"); hdu_type = FITS::NotAHDU; return false; } fits_item_size = FITS::fitssize(data_type); local_item_size = FITS::localsize(data_type); if (hdu_type != t) { errmsg(BADTYPE,"Improper keyword list for this HDU type[HeaderDataUnit::init_data_unit]"); hdu_type = FITS::NotAHDU; return false; } if (no_dims > 0) { if ((dimn = new Int [no_dims]) == 0) { errmsg(NOMEM,"Cannot allocate memory[HeaderDataUnit::init_data_unit]"); no_dims = 0; return false; }else { for (int i = 0; i < no_dims; i++) { dimn[i] = kwlist_(FITS::NAXIS,(i + 1))->asInt();} return true; } } return true; } //=============================================================================================================== void HeaderDataUnit:: posEnd() { // Position the kwlist_ cursor before the `END' keyword // Assumption: The `END' keyword is the last keyword. kwlist_.last(); kwlist_.prev(); } //============================================================================== char * HeaderDataUnit::assign(FITS::ReservedName nm) { char *s; if (kwlist_(nm) != 0) { if ((s = new char [kwlist_.curr()->valStrlen() + 1]) == 0) errmsg(NOMEM,"Cannot allocate memory"); else { memcpy(s,kwlist_.curr()->asString(),kwlist_.curr()->valStrlen()); s[kwlist_.curr()->valStrlen()] = '\0'; } } else s = &char_null; return s; } //============================================================================== char * HeaderDataUnit::assign(FITS::ReservedName nm, int ndx) { char *s; if (kwlist_(nm,ndx) != 0) { if ((s = new char [kwlist_.curr()->valStrlen() + 1]) == 0) errmsg(NOMEM,"Cannot allocate memory"); else { memcpy(s,kwlist_.curr()->asString(),kwlist_.curr()->valStrlen()); s[kwlist_.curr()->valStrlen()] = '\0'; } } else s = &char_null; return s; } //============================================================================= Vector HeaderDataUnit::kwlist_str(Bool length80){ return fin->kwlist_str(length80); } //============================================================================= int HeaderDataUnit::read_data(char *addr, Int nb) { return (fin ? fin->read(hdu_type,addr,nb) : 0); } //============================================================================= int HeaderDataUnit::write_data(FitsOutput &f, char *addr, Int nb) { return f.write(hdu_type,addr,nb,pad_char); } //============================================================================= OFF_T HeaderDataUnit::read_all_data(char *addr) { return (fin ? fin->read_all(hdu_type,addr) : 0); } //============================================================================= int HeaderDataUnit::write_all_data(FitsOutput &f, char *addr) { return f.write_all(hdu_type,addr,pad_char); } //============================================================================= int HeaderDataUnit::skip(uInt n) { return (fin ? fin->skip(hdu_type,n) : 0); } //============================================================================= int HeaderDataUnit::skip() { if (fin) fin->skip_all(hdu_type); return 0; } //============================================================================= int HeaderDataUnit::write_hdr(FitsOutput &f) { if(f.write_hdr(kwlist_,hdu_type,data_type,fits_data_size,fits_item_size)){ return -1; } return 0; } //============================================================================= int HeaderDataUnit::get_hdr(FITS::HDUType t, FitsKeywordList &kw) { return fin->process_header(t,kw); } //============================================================================= double HeaderDataUnit::asgdbl(FITS::ReservedName n, double x) { if (kwlist_(n) == 0) return x; else if (kwlist_.curr()->type() == FITS::DOUBLE) return kwlist_.curr()->asDouble(); else return (double)(kwlist_.curr()->asFloat()); } //============================================================================= double HeaderDataUnit::asgdbl(FITS::ReservedName n, int i, double x) { if (kwlist_(n,i) == 0) return x; else if (kwlist_.curr()->type() == FITS::DOUBLE) return kwlist_.curr()->asDouble(); else return (double)(kwlist_.curr()->asFloat()); } //== ExtensionHeaderDataUnit ================================================= ExtensionHeaderDataUnit::ExtensionHeaderDataUnit(FitsInput &f, FITSErrorHandler errhandler) : HeaderDataUnit(f,FITS::UnknownExtensionHDU,errhandler) { ex_assign(); } //============================================================================ ExtensionHeaderDataUnit::ExtensionHeaderDataUnit(FitsInput &f, FITS::HDUType t, FITSErrorHandler errhandler) : HeaderDataUnit(f,t,errhandler) { ex_assign(); } //============================================================================ ExtensionHeaderDataUnit::ExtensionHeaderDataUnit(FitsKeywordList &k, FITSErrorHandler errhandler) : HeaderDataUnit(k,FITS::UnknownExtensionHDU,errhandler,0) { ex_assign(); } //============================================================================ ExtensionHeaderDataUnit::ExtensionHeaderDataUnit(FitsKeywordList &k, FITS::HDUType t, FITSErrorHandler errhandler) : HeaderDataUnit(k,t,errhandler,0) { ex_assign(); } //============================================================================ ExtensionHeaderDataUnit::ExtensionHeaderDataUnit(FITS::HDUType t, FITSErrorHandler errhandler) : HeaderDataUnit( t,errhandler,0) { ex_assign(); } //============================================================================ ExtensionHeaderDataUnit::~ExtensionHeaderDataUnit() { if (xtension_x != &char_null) delete [] xtension_x; if (extname_x != &char_null) delete [] extname_x; } //==================================================================================== void ExtensionHeaderDataUnit::ex_assign() { extver_x = kwlist_(FITS::EXTVER) == 0 ? FITS::minInt : kwlist_.curr()->asInt(); extlevel_x = kwlist_(FITS::EXTLEVEL) == 0 ? FITS::minInt : kwlist_.curr()->asInt(); pcount_x = kwlist_(FITS::PCOUNT) == 0 ? FITS::minInt : kwlist_.curr()->asInt(); gcount_x = kwlist_(FITS::GCOUNT) == 0 ? FITS::minInt : kwlist_.curr()->asInt(); xtension_x = assign(FITS::XTENSION); extname_x = assign(FITS::EXTNAME); } //== FitsField and related classes =================================================== FitsBase::~FitsBase() { } //==================================================================================== int FitsBase::dims() const { return 1; } //==================================================================================== int FitsBase::dim(int n) const { return (n == 0 ? no_elements : 0); } //==================================================================================== int *FitsBase::vdim() { return &no_elements; } //==================================================================================== FitsBase * FitsBase::make(const FITS::ValueType &type,int n) { switch (type) { case FITS::LOGICAL: return (new FitsField (n)); case FITS::BIT: return (new FitsField (n)); case FITS::CHAR: return (new FitsField (n)); case FITS::BYTE: return (new FitsField (n)); case FITS::SHORT: return (new FitsField (n)); case FITS::LONG: return (new FitsField (n)); case FITS::FLOAT: return (new FitsField (n)); case FITS::DOUBLE: return (new FitsField (n)); case FITS::COMPLEX: return (new FitsField (n)); case FITS::ICOMPLEX: return (new FitsField (n)); case FITS::DCOMPLEX: return (new FitsField (n)); case FITS::VADESC: return (new FitsField (n)); // The following "default" has been added to prevent compilers // such as GNU g++ from complaining about the rest of FITS // enumerations not being handled in the switch statement. // -OO default: assert(0); break; } return 0; } //======================================================================================== FitsBase * FitsBase::make(const FITS::ValueType &type,int n, int *d) { switch (type) { case FITS::LOGICAL: return (new FitsArray (n,d)); case FITS::BIT: return (new FitsArray (n,d)); case FITS::CHAR: return (new FitsArray (n,d)); case FITS::BYTE: return (new FitsArray (n,d)); case FITS::SHORT: return (new FitsArray (n,d)); case FITS::LONG: return (new FitsArray (n,d)); case FITS::FLOAT: return (new FitsArray (n,d)); case FITS::DOUBLE: return (new FitsArray (n,d)); case FITS::COMPLEX: return (new FitsArray (n,d)); case FITS::ICOMPLEX: return (new FitsArray (n,d)); case FITS::DCOMPLEX: return (new FitsArray (n,d)); case FITS::VADESC: return (new FitsArray (n,d)); // The following "default" has been added to prevent compilers // such as GNU g++ from complaining about the rest of FITS // enumerations not being handled in the switch statement. // -OO default: assert(0); break; } return 0; } //======================================================================================= FitsBase * FitsBase::make(FitsBase &x) { if (x.dims() == 1) return make(x.fieldtype(),x.nelements()); return make(x.fieldtype(),x.dims(),x.vdim()); } //== AsciiTableExtension ===================================================== AsciiTableExtension::AsciiTableExtension(FitsInput &f, FITSErrorHandler errhandler) : BinaryTableExtension(f,FITS::AsciiTableHDU,errhandler) { pad_char = ' '; at_assign(); } //============================================================================ AsciiTableExtension::AsciiTableExtension(FitsKeywordList &k, FITSErrorHandler errhandler) : BinaryTableExtension(k,FITS::AsciiTableHDU,errhandler) { pad_char = ' '; at_assign(); } //=========================================================================== AsciiTableExtension::AsciiTableExtension(FITSErrorHandler errhandler): BinaryTableExtension(FITS::AsciiTableHDU,errhandler) { pad_char = ' '; // at_assign(); // this is done within write_asctbl_hdr(); } //=========================================================================== AsciiTableExtension::~AsciiTableExtension() { int i; if (tfields_x > 0) { for (i = 0; i < tfields_x; i++) { if (tnulla_x[i] != &char_null) delete tnulla_x[i]; delete [] format[i]; } delete [] tnulla_x; delete [] format; delete [] tbcol_x; delete [] fits_width; } } //=========================================================================== void AsciiTableExtension::at_assign() { int i, n, ne; size_t row_align; const char *s; char typecode; tfields_x = 0; // first initialize everything tbcol_x = 0; tform_x = 0; tscal_x = 0; tzero_x = 0; isatnull_x = 0; tnull_x = 0; tnulla_x = 0; ttype_x = 0; tunit_x = 0; tdisp_x = 0; tdim_x = 0; theap_x = 0; author_x = 0; referenc_x = 0; fld = 0; fits_offset = 0; fits_width = 0; format = 0; table_offset = 0; data_addr = 0; alloc_row = 0; table = 0; fitsrow = 0; tablerowsize = 0; fitsrowsize = 0; isoptimum = False; beg_row = 0; end_row = 0; curr_row = 0; if (err_status != OK) return; // Assign values from keywords if (kwlist_(FITS::TFIELDS) == 0) { errmsg(MISSKEY,"Missing required TFIELDS keyword"); tfields_x = 0; } else tfields_x = kwlist_.curr()->asInt(); if (tfields_x < 0 || tfields_x > 999) { errmsg(BADSIZE,"Invalid value for TFIELDS keyword"); tfields_x = 0; } theap_x = Int_null; author_x = assign(FITS::AUTHOR); referenc_x = assign(FITS::REFERENC); if (tfields_x == 0) return; tbcol_x = new Int [tfields_x]; tform_x = new char * [tfields_x]; tscal_x = new double [tfields_x]; tzero_x = new double [tfields_x]; isatnull_x = new Bool [tfields_x]; tnull_x = new int [tfields_x]; tnulla_x = new char * [tfields_x]; ttype_x = new char * [tfields_x]; tunit_x = new char * [tfields_x]; tdisp_x = new char * [tfields_x]; tdim_x = new char * [tfields_x]; if (tbcol_x == 0 || tform_x == 0 || tscal_x == 0 || tzero_x == 0 || isatnull_x == 0 || tnull_x == 0 || tnulla_x == 0 || ttype_x == 0 || tunit_x == 0 || tdisp_x == 0 || tdim_x == 0) { errmsg(NOMEM,"Cannot allocate memory"); return; } for (i = 0; i < tfields_x; i++) { tbcol_x[i] = kwlist_(FITS::TBCOL,(i + 1)) == 0 ? Int_null : kwlist_.curr()->asInt(); tform_x[i] = assign(FITS::TFORM,(i + 1)); tscal_x[i] = asgdbl(FITS::TSCAL,(i + 1),1.0); tzero_x[i] = asgdbl(FITS::TZERO,(i + 1),0.0); isatnull_x[i] = False; tnull_x[i] = Int_null; if (kwlist_(FITS::TNULL,(i + 1)) != 0) { if (kwlist_.curr()->type() == FITS::STRING) { if ((tnulla_x[i] = new char [kwlist_.curr()->valStrlen() + 1]) == 0) errmsg(NOMEM,"Cannot allocate memory"); else { memcpy(tnulla_x[i],kwlist_.curr()->asString(), kwlist_.curr()->valStrlen()); tnulla_x[i][kwlist_.curr()->valStrlen()] = '\0'; } } else { errmsg(BADTYPE,"Invalid value for keyword TNULL."); tnulla_x[i] = &char_null; } } else tnulla_x[i] = &char_null; ttype_x[i] = assign(FITS::TTYPE,(i + 1)); tunit_x[i] = assign(FITS::TUNIT,(i + 1)); tdisp_x[i] = &char_null; tdim_x[i] = &char_null; } // Allocate space for field pointer and create the fields fld = new FitsBase * [tfields()]; fits_offset = new uInt [tfields()]; fits_width = new uInt [tfields()]; format = new char * [tfields()]; table_offset = new uInt [tfields()]; data_addr = new void * [tfields()]; if (fld == 0 || fits_offset == 0 || fits_width == 0 || format == 0 || table_offset == 0 || data_addr == 0) { errmsg(NOMEM,"Cannot allocate memory"); return; } for (i = 0; i < tfields(); ++i) { format[i] = new char [strlen(tform(i)) + 3]; // the new format if (format[i] == 0) { errmsg(NOMEM,"Cannot allocate memory"); return; } n = 0; format[i][n++] = '%'; for (s = tform(i); *s == ' '; ++s) {} // skip leading blanks typecode = *s++; // code indicating kind of field fits_width[i] = 1; // get the fits field width if (FITS::isa_digit(*s)) { format[i][n++] = *s; fits_width[i] = FITS::digit2bin(*s++); while (FITS::isa_digit(*s)) { format[i][n++] = *s; fits_width[i] = fits_width[i] * 10 + FITS::digit2bin(*s++); } if (typecode == 'F' || typecode == 'E' || typecode == 'D') { if (*s == '.') { format[i][n++] = '.'; ++s; while (FITS::isa_digit(*s)) format[i][n++] = *s++; } } } switch (typecode) { case 'A': fld[i] = new FitsField(fits_width[i]); format[i][n++] = 's'; break; case 'I': fld[i] = new FitsField(1); format[i][n++] = 'd'; break; case 'F': fld[i] = new FitsField(1); format[i][n++] = 'f'; break; case 'E': fld[i] = new FitsField(1); format[i][n++] = 'E'; break; case 'D': fld[i] = new FitsField(1); format[i][n++] = 'E'; break; default: errmsg(BADRULES,"Invalid type code for TFORM"); fld[i] = 0; break; } format[i][n] = '\0'; // formats are converted } for (i = 0; i < tfields(); ++i) if (fld[i] == 0) // if any fields were not constructed return; // bail out for (i = 0; i < tfields(); ++i) fld[i]->setaddr(&data_addr[i]); // set field addresses // set FITS rowsize and compute tablerowsize fitsrowsize = dim(0); tablerowsize = 0; for (i = 0; i < tfields(); ++i) tablerowsize += fld[i]->localfieldsize(); isoptimum = False; // Determine field offsets for FITS and table rows for (i = 0; i < tfields(); ++i) { if ( tbcol(i) < 1 || tbcol(i) > (int)fitsrowsize) { errmsg(BADRULES,"Invalid value for TBCOL keyword"); return; } fits_offset[i] = tbcol(i) - 1; } // compute offsets for table rows n = 0; // the number of offsets computed ne = 0; // the current offset row_align = 0; // find the row alignment requirement for (i = 0; i < tfields(); ++i) if (fld[i]->fieldtype() == FITS::DOUBLE) { table_offset[i] = ne; ne += fld[i]->localfieldsize(); ++n; if (sizeof(double) > row_align) row_align = sizeof(double); } if (n < tfields()) for (i = 0; i < tfields(); ++i) if (fld[i]->fieldtype() == FITS::FLOAT) { table_offset[i] = ne; ne += fld[i]->localfieldsize(); ++n; if (sizeof(float) > row_align) row_align = sizeof(float); } if (n < tfields()) for (i = 0; i < tfields(); ++i) if (fld[i]->fieldtype() == FITS::LONG) { table_offset[i] = ne; ne += fld[i]->localfieldsize(); ++n; if (sizeof(FitsLong) > row_align) row_align = sizeof(FitsLong); } if (n < tfields()) for (i = 0; i < tfields(); ++i) if (fld[i]->fieldtype() == FITS::CHAR) { table_offset[i] = ne; ne += fld[i]->localfieldsize(); ++n; if (sizeof(unsigned char) > row_align) row_align = sizeof(unsigned char); } // check row alignment if ((tablerowsize % row_align) != 0) tablerowsize += row_align - (tablerowsize % row_align); // set data buffers and associated bounds markers fitsrow = new unsigned char [fitsrowsize]; if (fitsrow == 0) { errmsg(NOMEM,"Cannot allocate memory"); return; } beg_row = -1; end_row = -1; curr_row = -1; } //===================================================================================== int AsciiTableExtension::readrow() { FitsValueResult res; if (read_data((char *)fitsrow,fitsrowsize) != (int)fitsrowsize) return -1; // must convert ASCII data to binary for (int i = 0; i < tfields(); ++i) { if (fld[i]->fieldtype() == FITS::CHAR) memcpy(fld[i]->data(),&fitsrow[fits_offset[i]],fits_width[i]); else { FITS::get_numeric((char *)(&fitsrow[fits_offset[i]]), fits_width[i],res); if (res.errmsg) { errmsg(BADCONV,"Error converting data in current row."); return -1; } if (res.type != fld[i]->fieldtype()) { errmsg(BADCONV,"Error converting data in current row."); return -1; } switch (res.type) { case FITS::LONG: *((FitsLong *)(fld[i]->data())) = res.l; break; case FITS::FLOAT: *((float *)(fld[i]->data())) = res.f; break; case FITS::DOUBLE: *((double *)(fld[i]->data())) = res.d; break; default: errmsg(BADCONV,"Error converting data in current row."); return -1; } } } return 0; } //====================================================================================== int AsciiTableExtension::writerow(FitsOutput &fout) { // must convert binary data row to ASCII char tmp[32]; char *s, *t; int i; unsigned int n; for (i = 0; i < tfields(); ++i) { if (fld[i]->fieldtype() == FITS::CHAR) { t = (char *)&fitsrow[fits_offset[i]]; s = (char *)(fld[i]->data()); for (n = 0; n < fits_width[i] && *s != '\0'; ++n) *t++ = *s++; if (*s == '\0') for(; n < fits_width[i]; ++n) *t++ = ' '; } else { switch (fld[i]->fieldtype()) { case FITS::LONG: snprintf(tmp,sizeof(tmp),format[i],*((FitsLong *)(fld[i]->data()))); if (strlen(tmp) > fits_width[i]) { errmsg(BADCONV, "Ascii Table conversion error: numeric value exceeds field size"); for (t = (char *)&fitsrow[fits_offset[i]], n = 0; n < fits_width[i]; ++n, ++t) *t = ' '; // fill with blanks } else memcpy(&fitsrow[fits_offset[i]],tmp,fits_width[i]); break; case FITS::FLOAT: snprintf(tmp,sizeof(tmp),format[i],*((float *)(fld[i]->data()))); memcpy(&fitsrow[fits_offset[i]],tmp,fits_width[i]); break; case FITS::DOUBLE: snprintf(tmp,sizeof(tmp),format[i],*((double *)(fld[i]->data()))); for (t = &tmp[strlen(tmp) - 2]; *t != 'E'; --t) {} *t = 'D'; // Change the 'E' to a 'D' in the format memcpy(&fitsrow[fits_offset[i]],tmp,fits_width[i]); break; // The following "default" has been added to prevent compilers // such as GNU g++ from complaining about the rest of FITS // enumerations not being handled in the switch statement. // -OO default: assert(0); break; } } } return write_data(fout,(char *)fitsrow,fitsrowsize); } //========================================================================================================= // Put required Header keywords into the ASCII Table: /*Write the ASCII table header keywords into the CHU. The optional TUNITn and EXTNAME keywords are written only if the input pointers are not null. A null pointer may given for the *tbcol parameter in which case a single space will be inserted between each column of the table. Similarly, if rowlen is given = 0, then CFITSIO will calculate the default rowlength based on the tbcol and ttype values. */ int AsciiTableExtension::write_ascTbl_hdr( FitsOutput &fout, // I - FITS output object long naxis1, // I - width of row in the table(number of ascii chars) long naxis2, // I - number of rows in the table int tfields, // I - number of columns in the table const char **ttype, // I - name of each column long *tbcol, // I - byte offset in row to each column const char **tform, // I - value of TFORMn keyword for each column const char **tunit, // I - value of TUNITn keyword for each column const char *extname) // I - value of EXTNAME keyword, if any { // flush m_buffer first fout.getfout().flush_buffer(); if ( fout.rectype() == FITS::InitialState) { errmsg(BADOPER,"[AsciiTableExtension::write_ascTbl_hdr()] Primary Header must be written first."); return -1; } if (!fout.hdu_complete()) { errmsg(BADOPER,"[AsciiTableExtension::write_ascTbl_hdr()] Previous HDU incomplete -- cannot write header."); return -1; } if (!fout.isextend()) { errmsg(BADOPER,"[AsciiTableExtension::write_ascTbl_hdr()] Cannot write extension HDU - EXTEND not True."); return -1; } if( !fout.required_keys_only() ){ cerr << "\n[AsciiTableExtension::write_ascTbl_hdr()] write_ascTbl_hdr() works with other write_***_hdr()" << endl; cerr << "methods only. It will not work with write_hdr()." << endl; errmsg(BADOPER,"Used wrong header-writting function." ); return -1; } int l_status = 0; // Since the original file pointer does not have the hdu info about the hdu created by // write_hdu() method, we reopen the file to get a new file pointer with all the hdu info. // This may cause some loss of efficiency. But so far I have not found a better way. char * l_filename = new char[ 80 ]; if(ffflnm( fout.getfptr(), l_filename, &l_status )){ errmsg(BADOPER,"[AsciiTableExtension::write_ascTbl_hdr()] fflnm() failed!");} fitsfile* l_newfptr = 0; l_status = 0; if (ffopen( &l_newfptr, l_filename, READWRITE, &l_status )){ errmsg(BADOPER,"[AsciiTableExtension::write_ascTbl_hdr()] ffreopen() CHDU failed!"); fits_report_error(stderr, l_status); // print error report return -1; } // Create, initialize, and move the i/o pointer to a new extension appended to the end of the FITS file. l_status = 0; if(ffcrhd(l_newfptr, &l_status)){ errmsg(BADOPER,"[AsciiTableExtension::write_ascTbl_hdr() Create new HDU failed!"); fits_report_error(stderr, l_status); // print error report return -1; } // write the required keywords for AsciiTableExtension to fitsfile. if(ffphtb( l_newfptr, naxis1, naxis2, tfields, const_cast(ttype), tbcol, const_cast(tform), const_cast(tunit), const_cast(extname), &l_status)){ errmsg(BADOPER,"[AsciiTableExtension::write_ascTbl_hdr()] Write HDU header failed!"); fits_report_error(stderr, l_status); // print error report return -1; } OFF_T l_headstart, l_datastart, l_dataend; l_status = 0; // get size info of the current HDU if (ffghof(l_newfptr, &l_headstart, &l_datastart, &l_dataend, &l_status) > 0){ fits_report_error(stderr, l_status); // print error report return -1; } // move file pointer to the beginning of the new hdu. l_status = 0; if(ffmbyt(l_newfptr, l_headstart, REPORT_EOF, &l_status)){ fits_report_error(stderr, l_status); // print error report return -1; } // using the cfitsio function to read bytes from the file // pointed to by getfptr() from where the file position indicator currently at. l_status = 0; char * l_headerbytes = new char[ l_datastart - l_headstart + 1]; if(ffgbyt(l_newfptr, l_datastart - l_headstart, l_headerbytes, &l_status)){ fits_report_error(stderr, l_status); // print error report return -1; } // ffgbyt() sometimes does not move bytepos to the new position. So we do it. (l_newfptr->Fptr)->bytepos = l_datastart; fout.setfptr( l_newfptr ); // update the file pointer in FitsOutput. fout.getfout().setfptr( l_newfptr ); // update the file pointer in BlockOutput. // now parse the headerbytes into kwlist_. init_data_unit will use kwlist_. char* l_header = &l_headerbytes[0]; OFF_T l_usedbytes = 0; while( l_usedbytes < (l_datastart - l_headstart )){ fout.getkc().parse( l_header, kwlist_ ,0, errfn,True); l_usedbytes = l_usedbytes + fout.fitsrecsize(); l_header = &l_headerbytes[ l_usedbytes ]; } // init the info for the data unit init_data_unit( FITS::AsciiTableHDU ); // assign the asciitable. This is done in constructor for the case when user // is required to provide a FitsKeywordList object. at_assign(); // set the info for data unit. init_data_unit() generated the hdu_type, data_type, // fits_data_size and fits_item_size fout.set_data_info(kwlist_,hdu_type,data_type,fits_data_size,fits_item_size); return 0; } //================================================================================================= BinaryTableExtension::BinaryTableExtension(FitsInput &f, FITSErrorHandler errhandler) : ExtensionHeaderDataUnit(f,FITS::BinaryTableHDU,errhandler) { bt_assign(); } //================================================================================================= BinaryTableExtension::BinaryTableExtension(FitsKeywordList &k, FITSErrorHandler errhandler) : ExtensionHeaderDataUnit(k,FITS::BinaryTableHDU,errhandler) { bt_assign(); } //================================================================================================= // this constructor does not require a FitskeywordList from the user, which is // used for the situation when to create a header that contains only required // keywords. The related write method for this constructor is write_binTbl_hdr(). BinaryTableExtension::BinaryTableExtension( FITSErrorHandler errhandler) : ExtensionHeaderDataUnit( FITS::BinaryTableHDU,errhandler) { //bt_assign(); // this is done within write_bintbl_hdr(); } //================================================================================================= // this constructor does not require a FitskeywordList from the user, which is used // by AsciiTableExtension for the situation when to create a header that contains only // required keywords. The related write method for this constructor is write_asctbl_hdr(). BinaryTableExtension::BinaryTableExtension( FITS::HDUType hdutype, FITSErrorHandler errhandler) : ExtensionHeaderDataUnit( hdutype,errhandler) { // AsciiTableExtension calls this and then calls at_assign(). } //================================================================================================= BinaryTableExtension::BinaryTableExtension(FitsInput &f, FITS::HDUType t, FITSErrorHandler errhandler) : ExtensionHeaderDataUnit(f,t,errhandler) { // AsciiTableExtension calls this and then calls at_assign(). } //================================================================================================= BinaryTableExtension::BinaryTableExtension(FitsKeywordList &k, FITS::HDUType t, FITSErrorHandler errhandler) : ExtensionHeaderDataUnit(k,t,errhandler) { // AsciiTableExtension calls this and then calls at_assign(). } //================================================================================================ BinaryTableExtension::~BinaryTableExtension() { int i; if (author_x != &char_null) delete [] author_x; if (referenc_x != &char_null) delete [] referenc_x; if (tfields_x > 0) { delete [] tscal_x; delete [] tzero_x; for (i = 0; i < tfields_x; i++) { if (tform_x[i] != &char_null) delete [] tform_x[i]; if (ttype_x[i] != &char_null) delete [] ttype_x[i]; if (tunit_x[i] != &char_null) delete [] tunit_x[i]; if (tdisp_x[i] != &char_null) delete [] tdisp_x[i]; if (tdim_x[i] != &char_null) delete [] tdim_x[i]; delete fld[i]; } delete [] tform_x; delete [] isatnull_x; delete [] tnull_x; delete [] ttype_x; delete [] tunit_x; delete [] tdisp_x; delete [] tdim_x; } delete [] table; delete [] fld; delete [] table_offset; delete [] fits_offset; delete [] data_addr; if (!isoptimum) delete [] fitsrow; } //========================================================================== void BinaryTableExtension::bt_assign() { int i, j, n; size_t row_align; uInt ne; const char *s; const char *p; int *dd; int nd; tfields_x = 0; // first initialize everything tform_x = 0; tscal_x = 0; tzero_x = 0; isatnull_x = 0; tnull_x = 0; ttype_x = 0; tunit_x = 0; tdisp_x = 0; tdim_x = 0; theap_x = 0; author_x = 0; referenc_x = 0; fld = 0; fits_offset = 0; table_offset = 0; data_addr = 0; alloc_row = 0; table = 0; fitsrow = 0; tablerowsize = 0; fitsrowsize = 0; isoptimum = False; beg_row = 0; end_row = 0; curr_row = 0; if (err_status != OK) return; if (kwlist_(FITS::TFIELDS) == 0) { errmsg(MISSKEY,"Missing required TFIELDS keyword"); tfields_x = 0; } else tfields_x = kwlist_.curr()->asInt(); if (tfields_x < 0 || tfields_x > 999) { errmsg(BADSIZE,"Invalid value for TFIELDS keyword"); tfields_x = 0; } theap_x = kwlist_(FITS::THEAP) == 0 ? Int_null : kwlist_.curr()->asInt(); author_x = assign(FITS::AUTHOR); referenc_x = assign(FITS::REFERENC); if (tfields_x == 0) return; tform_x = new char * [tfields_x]; tscal_x = new double [tfields_x]; tzero_x = new double [tfields_x]; isatnull_x = new Bool [tfields_x]; tnull_x = new int [tfields_x]; ttype_x = new char * [tfields_x]; tunit_x = new char * [tfields_x]; tdisp_x = new char * [tfields_x]; tdim_x = new char * [tfields_x]; if (tform_x == 0 || tscal_x == 0 || tzero_x == 0 || isatnull_x == 0 || tnull_x == 0 || ttype_x == 0 || tunit_x == 0 || tdisp_x == 0 || tdim_x == 0) { errmsg(NOMEM,"Cannot allocate memory"); return; } for (i = 0; i < tfields_x; i++) { tform_x[i] = assign(FITS::TFORM,(i + 1)); tscal_x[i] = asgdbl(FITS::TSCAL,(i + 1),1.0); tzero_x[i] = asgdbl(FITS::TZERO,(i + 1),0.0); if (kwlist_(FITS::TNULL,(i + 1)) == 0) { isatnull_x[i] = False; tnull_x[i] = Int_null; } else { if (kwlist_.curr()->type() != FITS::LONG) { errmsg(BADTYPE,"Invalid value for keyword TNULL."); isatnull_x[i] = False; tnull_x[i] = Int_null; } else { tnull_x[i] = kwlist_.curr()->asInt(); isatnull_x[i] = True; } } ttype_x[i] = assign(FITS::TTYPE,(i + 1)); tunit_x[i] = assign(FITS::TUNIT,(i + 1)); tdisp_x[i] = assign(FITS::TDISP,(i + 1)); tdim_x[i] = assign(FITS::TDIM,(i + 1)); } // Allocate space for field pointer and create the fields fld = new FitsBase * [tfields()]; fits_offset = new uInt [tfields()]; table_offset = new uInt [tfields()]; data_addr = new void * [tfields()]; if (fld == 0 || fits_offset == 0 || table_offset == 0 || data_addr == 0) { errmsg(NOMEM,"Cannot allocate memory"); return; } for (i = 0; i < tfields(); ++i) { for (s = tform(i); *s == ' '; ++s) {} // skip leading blanks ne = 1; // ne is the number of elements in the field if (FITS::isa_digit(*s)) { ne = FITS::digit2bin(*s++); while (FITS::isa_digit(*s)) ne = ne * 10 + FITS::digit2bin(*s++); } p = tdim(i); if (*p != '\0') { // the multidimensional array case nd = 0; // get the dimensions, and store them in dd while (*p == ' ') ++p; // skip spaces if (*p != '(') { errmsg(BADRULES,"Invalid syntax in TDIM keyword"); return; } ++p; nd = 0; // the number of dimensions for (j = 0; p[j] != '\0' && p[j] != ')'; ++j) if (p[j] == ',') ++nd; ++nd; dd = new int [nd]; if (dd == 0) { errmsg(NOMEM,"Could not allocate memory"); return; } for (j = 0; j < nd; ++j) { while (*p == ' ') ++p; // skip spaces if (!FITS::isa_digit(*p)) { errmsg(BADRULES,"Invalid syntax in TDIM keyword"); return; } dd[j] = FITS::digit2bin(*p++); while (FITS::isa_digit(*p)) dd[j] = dd[j] * 10 + FITS::digit2bin(*p++); while (*p == ' ') ++p; // skip spaces if (*p == ',') ++p; else if (*p == ')') break; else { errmsg(BADRULES,"Invalid syntax in TDIM keyword"); return; } } switch (*s) { case 'L': fld[i] = new FitsArray(nd,dd); break; case 'X': fld[i] = new FitsArray(nd,dd); break; case 'B': fld[i] = new FitsArray(nd,dd); break; case 'I': fld[i] = new FitsArray(nd,dd); break; case 'J': fld[i] = new FitsArray(nd,dd); break; case 'A': fld[i] = new FitsArray(nd,dd); break; case 'E': fld[i] = new FitsArray(nd,dd); break; case 'D': fld[i] = new FitsArray(nd,dd); break; case 'C': fld[i] = new FitsArray(nd,dd); break; case 'M': fld[i] = new FitsArray(nd,dd); break; case 'P': fld[i] = new FitsArray(nd,dd); break; default: errmsg(BADRULES,"Invalid type code for TFORM"); fld[i] = 0; break; } delete [] dd; if (fld[i] == 0) { errmsg(NOMEM,"Cannot allocate memory"); return; } if (fld[i]->nelements() != ne) { errmsg(BADRULES,"Arraysize does not match fieldsize in TFORM"); return; } } else { // the single array case switch (*s) { case 'L': fld[i] = new FitsField(ne); break; case 'X': fld[i] = new FitsField(ne); break; case 'B': fld[i] = new FitsField(ne); break; case 'I': fld[i] = new FitsField(ne); break; case 'J': fld[i] = new FitsField(ne); break; case 'A': fld[i] = new FitsField(ne); break; case 'E': fld[i] = new FitsField(ne); break; case 'D': fld[i] = new FitsField(ne); break; case 'C': fld[i] = new FitsField(ne); break; case 'M': fld[i] = new FitsField(ne); break; case 'P': fld[i] = new FitsField(ne); break; default: errmsg(BADRULES,"Invalid type code for TFORM"); fld[i] = 0; break; } if (fld[i] == 0) { errmsg(NOMEM,"Cannot allocate memory"); return; } } } for (i = 0; i < tfields(); ++i) fld[i]->setaddr(&data_addr[i]); // set field addresses // compute FITS rowsize and tablerowsize fitsrowsize = 0; tablerowsize = 0; isoptimum = True; for (i = 0; i < tfields(); ++i) { fitsrowsize += fld[i]->fitsfieldsize(); tablerowsize += fld[i]->localfieldsize(); if (fld[i]->fitsfieldsize() != fld[i]->localfieldsize()) isoptimum = False; } // check for consistency if ((int)fitsrowsize != dim(0)) { errmsg(BADRULES,"Size of FITS row does not match NAXIS1"); return; } // Check field alignment and compute FITS offsets // This criteria for alignment will probably work on most // machines, but there may be some weird cases out there. fits_offset[0] = 0; for (i = 1; i < tfields(); ++i) { fits_offset[i] = fits_offset[i - 1] + fld[i - 1]->fitsfieldsize(); n = FITS::fitssize(fld[i]->fieldtype()); if (n > (int)sizeof(double))// since DComplex is implemented in n = sizeof(double); // terms of doubles, this is sufficient if ((fits_offset[i] % n) != 0) isoptimum = False; } if (isoptimum) { for (i = 0; i < tfields(); ++i) table_offset[i] = fits_offset[i]; // Must find the row alignment requirement n = 0; // the number of fields scanned row_align = 0; // the row alignment requirement for (i = 0; i < tfields(); ++i) if (fld[i]->fieldtype() == FITS::DCOMPLEX || fld[i]->fieldtype() == FITS::DOUBLE) { ++n; if (sizeof(double) > row_align) row_align = sizeof(double); } if (n < tfields()) for (i = 0; i < tfields(); ++i) if (fld[i]->fieldtype() == FITS::COMPLEX || fld[i]->fieldtype() == FITS::FLOAT) { ++n; if (sizeof(float) > row_align) row_align = sizeof(float); } if (n < tfields()) for (i = 0; i < tfields(); ++i) if (fld[i]->fieldtype() == FITS::VADESC || fld[i]->fieldtype() == FITS::LONG) { ++n; if (sizeof(FitsLong) > row_align) row_align = sizeof(FitsLong); } if (n < tfields()) for (i = 0; i < tfields(); ++i) if (fld[i]->fieldtype() == FITS::SHORT) { ++n; if (sizeof(short) > row_align) row_align = sizeof(short); } if (n < tfields()) for (i = 0; i < tfields(); ++i) if (fld[i]->fieldtype() == FITS::BYTE || fld[i]->fieldtype() == FITS::CHAR || fld[i]->fieldtype() == FITS::LOGICAL || fld[i]->fieldtype() == FITS::BIT) { ++n; if (sizeof(unsigned char) > row_align) row_align = sizeof(unsigned char); } } else { // Must compute separate offsets for local fields and align them. n = 0; // the number of offsets computed ne = 0; // the current offset row_align = 0; // find the row alignment requirement for (i = 0; i < tfields(); ++i) if (fld[i]->fieldtype() == FITS::DCOMPLEX || fld[i]->fieldtype() == FITS::DOUBLE) { table_offset[i] = ne; ne += fld[i]->localfieldsize(); ++n; if (sizeof(double) > row_align) row_align = sizeof(double); } if (n < tfields()) for (i = 0; i < tfields(); ++i) if (fld[i]->fieldtype() == FITS::COMPLEX || fld[i]->fieldtype() == FITS::FLOAT) { table_offset[i] = ne; ne += fld[i]->localfieldsize(); ++n; if (sizeof(float) > row_align) row_align = sizeof(float); } if (n < tfields()) for (i = 0; i < tfields(); ++i) if (fld[i]->fieldtype() == FITS::VADESC || fld[i]->fieldtype() == FITS::LONG) { table_offset[i] = ne; ne += fld[i]->localfieldsize(); ++n; if (sizeof(FitsLong) > row_align) row_align = sizeof(FitsLong); } if (n < tfields()) for (i = 0; i < tfields(); ++i) if (fld[i]->fieldtype() == FITS::SHORT) { table_offset[i] = ne; ne += fld[i]->localfieldsize(); ++n; if (sizeof(short) > row_align) row_align = sizeof(short); } if (n < tfields()) for (i = 0; i < tfields(); ++i) if (fld[i]->fieldtype() == FITS::BYTE || fld[i]->fieldtype() == FITS::CHAR || fld[i]->fieldtype() == FITS::LOGICAL || fld[i]->fieldtype() == FITS::BIT) { table_offset[i] = ne; ne += fld[i]->localfieldsize(); ++n; if (sizeof(unsigned char) > row_align) row_align = sizeof(unsigned char); } } // check row alignment if ((tablerowsize % row_align) != 0) { isoptimum = False; // must add padding to a table row tablerowsize += row_align - (tablerowsize % row_align); } //# We can never take the 'optimum' route if we have to convert fields # if defined(AIPS_LITTLE_ENDIAN) isoptimum = False; # endif // set data buffers and associated bounds markers alloc_row = 0; table = 0; for (i = 0; i < tfields(); ++i) data_addr[i] = 0; if (isoptimum) fitsrow = table; else { fitsrow = new unsigned char [fitsrowsize]; if (fitsrow == 0) { errmsg(NOMEM,"Cannot allocate memory"); return; } } beg_row = -1; end_row = -1; curr_row = -1; } //===================================================================================== int BinaryTableExtension::readrow() { int i; if (read_data((char *)fitsrow,fitsrowsize) != (int)fitsrowsize) return -1; //cout<<"[BinaryTableExtension::readrow()] One row of data read." << endl; if (!isoptimum) { for (i = 0; i < tfields(); ++i) { int ne = fld[i]->nelements(); void *src = &fitsrow[fits_offset[i]]; switch(fld[i]->fieldtype()) { case FITS::LOGICAL: FITS::f2l((FitsLogical *)(fld[i]->data()),src,ne); break; case FITS::BIT: FITS::f2l((FitsBit *)(fld[i]->data()),src,ne); break; case FITS::CHAR: FITS::f2l((char *)(fld[i]->data()),src,ne); break; case FITS::BYTE: FITS::f2l((unsigned char *)(fld[i]->data()),src,ne); break; case FITS::SHORT: FITS::f2l((short *)(fld[i]->data()),src,ne); break; case FITS::LONG: FITS::f2l((FitsLong *)(fld[i]->data()),src,ne); break; case FITS::FLOAT: FITS::f2l((float *)(fld[i]->data()),src,ne); break; case FITS::DOUBLE: FITS::f2l((double *)(fld[i]->data()),src,ne); break; case FITS::COMPLEX: FITS::f2l((Complex *)(fld[i]->data()),src,ne); break; case FITS::DCOMPLEX: FITS::f2l((DComplex *)(fld[i]->data()),src,ne); break; case FITS::VADESC: FITS::f2l((FitsVADesc *)(fld[i]->data()),src,ne); break; // The following "default" has been added to prevent compilers // such as GNU g++ from complaining about the rest of FITS // enumerations not being handled in the switch statement. // -OO default: assert(0); break; } } } return 0; } //======================================================================================== int BinaryTableExtension::writerow(FitsOutput &fout) { int i; if (!isoptimum) { for (i = 0; i < tfields(); ++i) { int ne = fld[i]->nelements(); void *tg = &fitsrow[fits_offset[i]]; switch(fld[i]->fieldtype()) { case FITS::LOGICAL: FITS::l2f(tg,(FitsLogical *)(fld[i]->data()),ne); break; case FITS::BIT: FITS::l2f(tg,(FitsBit *)(fld[i]->data()),ne); break; case FITS::CHAR: FITS::l2f(tg,(char *)(fld[i]->data()),ne); break; case FITS::BYTE: FITS::l2f(tg,(unsigned char *)(fld[i]->data()),ne); break; case FITS::SHORT: FITS::l2f(tg,(short *)(fld[i]->data()),ne); break; case FITS::LONG: FITS::l2f(tg,(FitsLong *)(fld[i]->data()),ne); break; case FITS::FLOAT: FITS::l2f(tg,(float *)(fld[i]->data()),ne); break; case FITS::DOUBLE: FITS::l2f(tg,(double *)(fld[i]->data()),ne); break; case FITS::COMPLEX: FITS::l2f(tg,(Complex *)(fld[i]->data()),ne); break; case FITS::DCOMPLEX: FITS::l2f(tg,(DComplex *)(fld[i]->data()),ne); break; case FITS::VADESC: FITS::l2f(tg,(FitsVADesc *)(fld[i]->data()),ne); break; // The following "default" has been added to prevent compilers // such as GNU g++ from complaining about the rest of FITS // enumerations not being handled in the switch statement. // -OO default: assert(0); break; } } } return write_data(fout,(char *)fitsrow,fitsrowsize); } //================================================================================ int BinaryTableExtension::set_next(int n) { // check if n rows have been allocated if (n > (int)alloc_row) { delete [] table; // must allocate more rows table = new unsigned char [n * tablerowsize]; if (table == 0) { errmsg(NOMEM,"Cannot allocate memory"); return -1; } alloc_row = n; } // update row markers beg_row = end_row + 1; end_row = beg_row + n - 1; curr_row = beg_row; set_fitsrow(beg_row); return n; } //================================================================================ void BinaryTableExtension::set_fitsrow(Int n) { curr_row = n; unsigned char *addr = &table[(curr_row - beg_row) * tablerowsize]; if (isoptimum) fitsrow = addr; // update field addresses for (int i = 0; i < tfields(); ++i) data_addr[i] = (void *)(addr + table_offset[i]); } int BinaryTableExtension::write(FitsOutput &fout) { OFF_T n; if (isoptimum) { n = (end_row - beg_row + 1) * fitsrowsize; //return ((write_data(fout,(char *)table,n) == n) ? 0 : -1); return (write_data(fout,(char *)table,n)); // It was above. GYL } else { // write rows from beg_row to end_row for (n = uInt(beg_row); n <= uInt(end_row); ++n) { set_fitsrow(n); if (writerow(fout) == -1) return -1; } } return 0; } //================================================================================ /*Put required Header keywords into the Binary Table: Write the binary table header keywords into the CHU. The optional TUNITn and EXTNAME keywords are written only if the input pointers are not null. The pcount parameter, which specifies the size of the variable length array heap, should initially = 0; CFITSIO will automatically update the PCOUNT keyword value if any variable length array data is written to the heap. The TFORM keyword value for variable length vector columns should have the form 'Pt(len)' or '1Pt(len)' where `t' is the data type code letter (A,I,J,E,D, etc.) and `len' is an integer specifying the maximum length of the vectors in that column (len must be greater than or equal to the longest vector in the column). If `len' is not specified when the table is created (e.g., the input TFORMn value is just '1Pt') then CFITSIO will scan the column when the table is first closed and will append the maximum length to the TFORM keyword value. Note that if the table is subsequently modified to increase the maximum length of the vectors then the modifying program is responsible for also updating the TFORM keyword value. GYL */ int BinaryTableExtension::write_binTbl_hdr( FitsOutput &fout, // I - FITS output object long naxis2, // I - number of rows in the table int tfields, // I - number of columns in the table const char **ttype, // I - name of each column const char **tform, // I - value of TFORMn keyword for each column const char **tunit, // I - value of TUNITn keyword for each column const char *extname, // I - value of EXTNAME keyword, if any long pcount ) // I - size of the variable length heap area { // flush m_buffer first fout.getfout().flush_buffer(); if ( fout.rectype() == FITS::InitialState) { errmsg(BADOPER,"[BinaryTableExtension::write_bintbl_hdr()] Primary Header must be written first."); return -1; } if (!fout.hdu_complete()) { errmsg(BADOPER,"[BinaryTableExtension::write_bintbl_hdr()] Previous HDU incomplete -- cannot write header."); return -1; } if (!fout.isextend()) { errmsg(BADOPER,"[BinaryTableExtension::write_bintbl_hdr()] Cannot write extension HDU - EXTEND not True."); return -1; } if( !fout.required_keys_only() ){ cerr << "\n[BinaryTableExtension::write_binTbl_hdr()] write_binTbl_hdr() works with other write_***_hdr()" << endl; cerr << "methods only. It will not work with write_hdr()." << endl; errmsg(BADOPER,"Used wrong header-writting function." ); return -1; } int l_status = 0; // Since the original file pointer does not have the hdu info about the hdu created by // write_hdu() method, we reopen the file to get a new file pointer with all the hdu info. // This may cause some loss of efficiency. But so far I have not found a better way. char * l_filename = new char[ 80 ]; if(ffflnm( fout.getfptr(), l_filename, &l_status )){ errmsg(BADOPER,"[BinaryTableExtension::write_bintbl_hdr()] fflnm() failed!");} fitsfile* l_newfptr = 0; l_status = 0; if (ffopen( &l_newfptr, l_filename, READWRITE, &l_status )){ errmsg(BADOPER,"[BinaryTableExtension::write_bintbl_hdr()] ffreopen() CHDU failed!"); fits_report_error(stderr, l_status); // print error report return -1; } // Create, initialize, and move the i/o pointer to a new extension appended to the end of the FITS file. l_status = 0; if(ffcrhd(l_newfptr, &l_status)){ errmsg(BADOPER,"[BinaryTableExtension::write_bintbl_hdr()] Create new HDU failed!"); fits_report_error(stderr, l_status); // print error report return -1; } // write the required keywords for BinaryTableExtension to fitsfile. if(ffphbn( l_newfptr, naxis2, tfields, const_cast(ttype), const_cast(tform), const_cast(tunit), const_cast(extname), pcount, &l_status)){ errmsg(BADOPER,"[BinaryTableExtension::write_bintbl_hdr()] Write HDU header failed!"); fits_report_error(stderr, l_status); // print error report return -1; } // flush the buffer of CFITSIO so that the contents will be actually written to disk. // oopes, if we call ffflsh() in this case, the fits file is damaged! why? if(ffflsh(l_newfptr, TRUE, &l_status)){ errmsg(BADOPER,"[PrimaryArray::write_priArr_hdr()] Error flushing buffer!"); } OFF_T l_headstart, l_datastart, l_dataend; l_status = 0; // get size info of the current HDU if (ffghof(l_newfptr, &l_headstart, &l_datastart, &l_dataend, &l_status) > 0){ fits_report_error(stderr, l_status); // print error report return -1; } // move file pointer to the beginning of the new hdu. l_status = 0; if(ffmbyt(l_newfptr, l_headstart, REPORT_EOF, &l_status)){ errmsg(BADOPER,"Moving to headstart failed![BinaryTableExtension::write_bintbl_hdr()]"); fits_report_error(stderr, l_status); // print error report return -1; } // using the cfitsio function to read bytes from the file // pointed to by getfptr() from where the file position indicator currently at. l_status = 0; char * l_headerbytes = new char[l_datastart - l_headstart + 1]; if(ffgbyt(l_newfptr, l_datastart - l_headstart, l_headerbytes, &l_status)){ errmsg(BADOPER,"ffgbyt() failed![BinaryTableExtension::write_bintbl_hdr()]"); fits_report_error(stderr, l_status); // print error report return -1; } // ffgbyt() sometimes does not move bytepos to the new position. So we do it. (l_newfptr->Fptr)->bytepos = l_datastart; fout.setfptr( l_newfptr ); // update the file pointer in FitsOutput. fout.getfout().setfptr( l_newfptr ); // update the file pointer in BlockOutput. // now parse the headerbytes into kwlist_. init_data_unit will use kwlist_. char* l_header = &l_headerbytes[0]; OFF_T l_usedbytes = 0; while( l_usedbytes < (l_datastart - l_headstart )){ fout.getkc().parse( l_header, kwlist_ ,0, errfn,True); l_usedbytes = l_usedbytes + fout.fitsrecsize(); l_header = &l_headerbytes[ l_usedbytes ]; } // init the info for the data unit init_data_unit( FITS::BinaryTableHDU ); // assign the binary table. This is done in constructor for the case when user // is required to provide a FitsKeywordList object. bt_assign(); // set the info for data unit. init_data_unit() generated the hdu_type, data_type, // fits_data_size and fits_item_size fout.set_data_info(kwlist_,hdu_type,data_type,fits_data_size,fits_item_size); return 0; } //===================================================================================== int BinaryTableExtension::read() { // read entire table into memory int nr = fitsdatasize() / fitsrowsize; return read(nr); } //===================================================================================== int BinaryTableExtension::read(int nr){ int i; if (nr < 1) return -1; if (set_next(nr) == -1) // check buffer allocation return -1; //read next nr rows into memory if (isoptimum) { i = nr * fitsrowsize; return ((read_data((char *)table,i) == i) ? 0 : -1); } else { // read next nr rows for (i = beg_row; i <= end_row; ++i) { if (readrow() == -1) return -1; ++(*this); } // set curr_row to the beginning row and set field addresses set_fitsrow(beg_row); } return 0; } //=================================================================================== BinaryTableExtension & BinaryTableExtension::operator ++ () { // increment curr_row and reset field addresses ++curr_row; set_fitsrow(curr_row); return *this; } //=================================================================================== BinaryTableExtension & BinaryTableExtension::operator -- () { // decrement curr_row and reset field addresses --curr_row; set_fitsrow(curr_row); return *this; } //=================================================================================== BinaryTableExtension & BinaryTableExtension::operator () (int n) { // set curr_row to n and reset field addresses curr_row = n; set_fitsrow(curr_row); return *this; } //=================================================================================== int BinaryTableExtension::bind(int no, FitsBase &f) { int i; // check if f's attributes matches field[no] if (f.fieldtype() != fld[no]->fieldtype() || // The last test is commented out since it seems reasonable // to allow a FitsField to be used where a FitsArray is, e.g. // always allow one-D arrays to be used for N-D. f.nelements() != fld[no]->nelements() /* || f.dims() != fld[no]->dims() */) { errmsg(BADRULES,"Variable type does not match this column."); return -1; } if (f.dims() > 1) { for (i = 0; i < f.dims(); ++i) if (f.dim(i) != fld[no]->dim(i)) { errmsg(BADRULES,"Variable type does not match this column."); return -1; } } // set f address to field[no] data's address f.setaddr(&data_addr[no]); return 0; } //=================================================================================== // We must specify FitsArray as a specialized class. FitsArray::FitsArray(int n, const int *d) : FitsField(1) { int i; if (n > 0) { no_dims = n; dimn = new int [no_dims]; factor = new int [no_dims]; dimn[0] = d[0]; no_elements = dimn[0]; for (i = 1; i < no_dims; ++i) { dimn[i] = d[i]; no_elements *= d[i]; } factor[0] = 1; for (i = 1; i < no_dims; ++i) factor[i] = factor[i - 1] * dimn[i - 1]; } else { no_dims = 1; dimn = 0; factor = 0; no_elements = 1; } } //=================================================================================== FitsArray::~FitsArray() { delete [] dimn; delete [] factor; } //================================================ int FitsArray::dims() const { return no_dims; } //================================================ int FitsArray::dim(int n) const { return dimn[n]; } //================================================ int * FitsArray::vdim() { return dimn; } //================================================ } //# NAMESPACE CASACORE - END casacore-3.7.1/fits/FITS/test/000077500000000000000000000000001476623553700160155ustar00rootroot00000000000000casacore-3.7.1/fits/FITS/test/CMakeLists.txt000066400000000000000000000010371476623553700205560ustar00rootroot00000000000000set (tests tBinTable tfits1 tfits2 tfits3 tfits4 tfits5 tfits_ascTbl2 tfits_ascTbl tfits_binTbl1 tfits_binTbl2 tFITS tFITSDateUtil tFITSHistoryUtil tfits_imgExt2 tFITSKeywordUtil tfits_priGrp tfitsread_data tfitsskip_all tfitsskip tfitsskip_hdu tFITSSpectralUtil t_priArr_imgExt tfitsreader ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_fits) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/fits/FITS/test/tBinTable.cc000066400000000000000000000105331476623553700201720ustar00rootroot00000000000000//# tBinTable.cc - this program tests the BinTable class //# Copyright (C) 1995,1996,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include int main(int argc, const char* argv[]) { try { Input inputs(1); inputs.create("inputFile", "", "The input FITS file", "String"); inputs.create("baseName", "", "The root name for all created files", "String"); inputs.create("storageManager", "miriad", "The storage manager to use - miriad (RLE) or aipsio " "(memory)", "String"); inputs.create("sdfits", "False", "Interpret keywords as virtual columns as in the SD-FITS convention", "Bool"); inputs.readArguments(argc, argv); String inputFilename = inputs.getString("inputFile"); String baseName = inputs.getString("baseName"); String storageManagerType = inputs.getString("storageManager"); Bool sdfits = inputs.getBool("sdfits"); storageManagerType.downcase(); Bool useMiriadSM; if (storageManagerType == "miriad") { useMiriadSM = True; } else if (storageManagerType == "aipsio") { useMiriadSM = False; } else { cout << storageManagerType << " is not a valid storage manager" << endl; return 1; } File inputFile(inputFilename); if (! inputFile.isReadable()) { cout << inputFilename << " is not readable - exiting" << endl; return 1; } Int tabCount = 0; // This allows for constructed names of the form baseName.table.xx int tabNameLen = baseName.length() + 10; char *tabName = new char[tabNameLen]; // construct the FITS input FitsInput infits(inputFilename.chars(), FITS::Disk); if (infits.err() != FitsIO::OK) { cout << "Problem instantiating FITS input " << infits.err() << endl; return 1; } while (!infits.eof()) { switch (infits.hdutype()) { case FITS::BinaryTableHDU: { snprintf(tabName,tabNameLen,"%s.table.%i",baseName.chars(),tabCount++); String tabNameString(tabName); cout << "BinaryTableHDU : " << tabNameString << " ... " ; BinaryTable bintab(infits, FITSError::defaultHandler, useMiriadSM, sdfits); if (infits.err() != FitsIO::OK) { cout << "Problem in infits while instantiating binary table " << infits.err() << endl; return 1; } Table tab = bintab.fullTable(tabNameString, Table::NewNoReplace, useMiriadSM); if (infits.err() != FitsIO::OK) { cout << "Problem in infits while making the table " << infits.err() << endl; return 1; } cout << "done." << endl; } break; default: cout << "Unable to do anything but skip this hdutype : " << Int(infits.hdutype()) << endl; infits.skip_hdu(); if (infits.err() != FitsIO::OK) { cout << "Problem in infits while skipping the hdu" << infits.err() << endl; return 1; } break; } } cout << "At end of file" << endl; } catch (std::exception& x) { cout << "Unexpected exception: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/fits/FITS/test/tBinTable.run000066400000000000000000000006671476623553700204200ustar00rootroot00000000000000#!/bin/sh if [ ${#AIPSPATH} = 0 ] then echo "UNTESTED: tBinTable (AIPSPATH not defined)" exit 3 fi DATAFILE=`echo $AIPSPATH | awk '{print $1}'`/data/demo/dishdemo/dishdemo1.fits if [ -f $DATAFILE ] then # make sure things are cleaned up rm -rf tBinTable_tmp.table.0 $casa_checktool ./tBinTable inputFile=$DATAFILE baseName=tBinTable_tmp else echo "UNTESTED: tBinTable, could not find the test data" $DATAFILE exit 3 fi casacore-3.7.1/fits/FITS/test/tFITS.cc000066400000000000000000000071401476623553700172570ustar00rootroot00000000000000//# tFITS.cc: This program tests the simple FITS wrappers //# Copyright (C) 1993,1994,1995,1999,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #if !defined(AIPS_DEBUG) #define AIPS_DEBUG #endif #include #include #include #include #include #include #include #include #include #include #include #include int main() { const char *file = "test.fits"; Matrix m(512,512), m2; // Create a wedge; rows in a matrix normally are columns in an image, // i.e. the result might be the transpose of what you expect. for (uInt i=0; i < 512; i++) { m.row(i) = float(i); } // Create the "optional" information std::map mapout, mapin; String unitout, unitin; unitout = "Jy"; Vector namesout(2), namesin(2); namesout(0) = "X" ; namesout(1) = "Y"; Vector refout(2), refin(2), locout(2), locin(2), deltaout(2), deltain(2); refout = 0.0f; locout = 1.0f; deltaout = 1.0f; mapout["hello"] = 1.0; mapout["world"] = 2.0; String objectin, objectout; objectout = "Testing 1.2.3."; String message; cout << "Writing...." << endl; // remove the fits file if already exists remove( file ); // unlink() at the end does this. if (WriteFITS(file,m,message, unitout.chars(), &namesout, &refout, &locout, &deltaout, &mapout, objectout.chars()) == False) { cout << "Write failed: " << message << endl; return 1; } Bool ok = True; cout << "Reading.... (will leave test.fits if program fails)" << endl; m2 = ReadFITS(file,ok,message, &unitin, &namesin, &refin, &locin, &deltain, &mapin, &objectin); if (ok == False) { cout << "Read failed: " << message << endl; return 1; } // Could fail just for roundoff reasons, but likely ok AlwaysAssertExit(allEQ(m,m2)); AlwaysAssertExit(unitout == unitin); AlwaysAssertExit(allEQ(namesout , namesin)); AlwaysAssertExit(allEQ(refout , refin)); AlwaysAssertExit(allEQ(locout , locin)); AlwaysAssertExit(allEQ(deltaout , deltain)); AlwaysAssertExit(mapin["HELLO"] == 1.0); AlwaysAssertExit(mapin["WORLD"] == 2.0); AlwaysAssertExit(objectout == objectin); unlink(file); cout << "OK\n"; return 0; } casacore-3.7.1/fits/FITS/test/tFITSDateUtil.cc000066400000000000000000000112511476623553700207110ustar00rootroot00000000000000//# tFITSDateUtil.cc: Test program for FITSDateUtil //# Copyright (C) 2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include int main() { try { // toFITS test String date; String timesys; MVTime timeIn; Quantity qtime; MVTime::read(qtime,"1990/03/31"); timeIn = qtime; MEpoch::Types systemIn = MEpoch::UTC; // force old style FITSDateUtil::toFITS(date, timesys, timeIn, systemIn, FITSDateUtil::OLD); if (date != "31/03/90") { throw(AipsError("FITSDateUtil::toFITS failed on conversion to OLD format")); } if (timesys != "UTC") { throw(AipsError("FITSDateUtil::toFITS conversion to OLD produced unexpected timesys value")); } // AUTO pick will now always get the new format, use current date // and time MVTime::read(qtime,"today"); timeIn = qtime; FITSDateUtil::toFITS(date, timesys, timeIn, systemIn); // fromeFITS test // verify that the date and timesys produced above can reproduce // timeIn and systemIn MVTime timeOut; MEpoch::Types systemOut; if (!FITSDateUtil::fromFITS(timeOut, systemOut, date, timesys)) throw(AipsError("unexpected failure of FITSDateUtil::fromFITS")); if (!near(Double(timeOut),Double(timeIn))) { throw(AipsError("FITSDateUtil::fromFITS failed to convert back to original time")); } if (!(systemOut == systemIn)) { throw(AipsError("FITSDateUtil::fromFITS failed to convert back to original time system")); } // test of a known problem date: 2001-09-26T00:00:00.00 String problemDate("2001-09-26T00:00:00.00"); if (!FITSDateUtil::fromFITS(timeOut, systemOut, problemDate, timesys)) throw(AipsError("unexpected failure of FITSDateUtil::fromFITS on problem date")); String probDateBack; FITSDateUtil::toFITS(probDateBack, timesys, timeOut, systemOut,FITSDateUtil::AUTO_PICK,8); if (problemDate != probDateBack) { throw(AipsError("Problem converting to/from the problem date")); } // convertDateString test String in("31/03/90"); String out; if (!FITSDateUtil::convertDateString(out, in)) { throw(AipsError("unexpected failure of FITSDateUtil::convertDateString - actual conversion")); } if (out != "1990-03-31") { throw(AipsError("FITSDateUtil::convertDateString failed to produce expected result")); } // pass-through conversion in = "2001-06-01"; if (!FITSDateUtil::convertDateString(out, in)) { throw(AipsError("unexpected failure of FITSDateUtil::convertDateString - pass through test")); } if (out != in) { throw(AipsError("FITSDateUtil::convertDateString failed to pass through the value")); } // findPrecision test // old style returns 0 if (FITSDateUtil::findPrecision("31/03/90") != 0) { throw(AipsError("FITSDateUtil::findPrecision - old style didn't return 0")); } // no time also returns 0 if (FITSDateUtil::findPrecision("2001-06-01") != 0) { throw(AipsError("FITSDateUtil::findPrecision - no time didn't return 0")); } // seconds accuracy -> 6 if (FITSDateUtil::findPrecision("2001-06-01T05:30:20") != 6) { throw(AipsError("FITSDateUtil::findPrecision - seconds accuracy didn't return 6")); } // should return 8 if (FITSDateUtil::findPrecision("2001-06-01T05:30:20.25") != 8) { throw(AipsError("FITSDateUtil::findPrecision - didn't return 8 as expected")); } } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/fits/FITS/test/tFITSHistoryUtil.cc000066400000000000000000000116101476623553700214740ustar00rootroot00000000000000//# tFITSHistoryUtil.cc: Test program for FITSHistoryUtil //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include int main() { try { // LogSink to use in testing here - use a memory log sink LogMessage message(LogOrigin("testFITSHistoryUtil()",WHERE)); message.message("this is a test").line(__LINE__); LoggerHolder logger(False); logger.sink().post(message); message.message("This is another LogMessage stored in the sink to be transfered to FITS"); logger.sink().post(message); std::vector history; Bool aipsppFormat = True; uInt nstrings, nread; nstrings = nread = 0; nread = FITSHistoryUtil::toHISTORY(history, aipsppFormat, nstrings, uInt(0), logger); // there are 2 things inserted here, so nread should be 2 AlwaysAssertExit(nread == 2); FitsKeywordList kwl; if (aipsppFormat) { FITSHistoryUtil::addHistoryGroup(kwl, history, nstrings, "LOGTABLE"); } else { FITSHistoryUtil::addHistoryGroup(kwl, history, nstrings, ""); } // add some other other history, to a different group std::vector otherHistory(3); otherHistory[0] = "I like cats."; otherHistory[1] = "This is a longer history message to see how it handles that sort of thing."; otherHistory[2] = "This is part of the OTHER group."; FITSHistoryUtil::addHistoryGroup(kwl, otherHistory, otherHistory.size(), "OTHER"); // and add some things without a group std::vector moreHistory(4); moreHistory[0] = "More history."; moreHistory[1] = "Still more history."; moreHistory[2] = "And still more history."; moreHistory[3] = "And the end of the more history, although this is another long message that should wrap"; FITSHistoryUtil::addHistoryGroup(kwl, moreHistory, moreHistory.size(), ""); // now retrieve stuff from kwl Vector stringsOut; String groupType; uInt n; ConstFitsKeywordList ckwl(kwl); ckwl.first(); while ((n = FITSHistoryUtil::getHistoryGroup(stringsOut, groupType, ckwl)) != 0) { LoggerHolder logOut(False); if (groupType == "LOGTABLE") { FITSHistoryUtil::fromHISTORY(logOut, stringsOut, n, True); Int iterCount = 0; LoggerHolder::const_iterator origIter = logger.begin(); for (LoggerHolder::const_iterator iter = logOut.begin(); iter != logOut.end(); iter++) { AlwaysAssertExit(origIter != logger.end()); iterCount++; // I'm not sure what the precision of the stuff stored // in the FITS kwl is. I think its 1s accuracy. AlwaysAssertExit((origIter->time()-iter->time())<=1); AlwaysAssertExit(origIter->priority()==iter->priority()); AlwaysAssertExit(origIter->message()==iter->message()); AlwaysAssertExit(origIter->location()==iter->location()); AlwaysAssertExit(origIter->objectID()==iter->objectID()); origIter++; } // we put 2 in, we should have gotten 2 out AlwaysAssertExit(iterCount==2); // the return value of n is 2* the number of log lines - // one for the message and one for the time/priority/origin // line AlwaysAssertExit(n==4); } else if (groupType == "OTHER") { AlwaysAssertExit(n==otherHistory.size()); for (uInt i=0;i #include #include #include #include #include #include #include #include #include #include #include #include int main() { try { // Create a Record that will contain the value to be put into FITS keywords Record myKeywords; myKeywords.define("hello", 6.5); myKeywords.define("world", True); // long keywords are truncated to 8 characters myKeywords.define("alongname", Short(-1)); // other scalar types to round out the testing of the code myKeywords.define("a", uInt(10)); myKeywords.define("b", Int(-10)); myKeywords.define("c", Float(10.0)); myKeywords.define("d","I like dogs"); // Array types for testing Vector flags(2); flags(0) = False; flags(1) = True; myKeywords.define("flags", flags); // NAXIS generates the NAXIS keyword as well as NAXIS1 .. NAXISn Vector naxis(5); naxis(0) = 128; naxis(1) = 64; naxis(2) = 32; naxis(3) = 16; naxis(4) = 8; myKeywords.define("naxis", naxis); Vector tarray(3); tarray(0) = 1; tarray(1) = 2; tarray(2) = 3; myKeywords.define("tarray", tarray); // make one a Matrix Matrix mat(3,3); mat(0,0) = 1; mat(1,0) = 2; mat(2,0) = 3; mat(0,1) = 4; mat(1,1) = 5; mat(2,1) = 6; mat(0,2) = 7; mat(1,2) = 8; mat(2,2) = 9; myKeywords.define("ma", mat); Vector sarray(2); sarray(0) = "Hello"; sarray(1) = "World"; myKeywords.define("sarray", sarray); // scalar fields can have comments myKeywords.setComment("hello", "A comment for HELLO"); myKeywords.setComment("alongname", "This is truncated from ALONGNAME"); // the CD-matrix keywords are special myKeywords.define("cd1_1", 1.0); myKeywords.define("cd1_2", 0.0); myKeywords.define("cd2_1", 0.0); myKeywords.define("cd2_2", 1.0); // Add a couple of comments. Note that addComment appends numbers to each // comment to keep them unique // Record::define could also be used - so long as the name of the comment // was unique e.g. "comment1" and "comment2" FITSKeywordUtil::addComment(myKeywords, "Comment created by testKeywordUtil."); FITSKeywordUtil::addComment(myKeywords, "Boring content, I agree!"); // Multi-line comments can also be added - the line is split at \n // There is no check on the length of the comment FITSKeywordUtil::addComment(myKeywords, "This comment will extend across\nmultiple lines.\nThree in this case."); // And add a HISTORY card - generally FITSHistoryUtil should be used FITSKeywordUtil::addHistory(myKeywords, "tFITSUtil"); // Create an empty FitsKeywordList, containing only "SIMPLE=T", which is // necessary for all valid FITS files. FitsKeywordList nativeList = FITSKeywordUtil::makeKeywordList(); // OK, now lets add the keywords to the native list AlwaysAssertExit(FITSKeywordUtil::addKeywords(nativeList, myKeywords)); // Fetch the keywords into another Record, suppressing "simple", since it // is generated by makeKeywordList and also suppress "world". Record myNewKeywords; Vector ignore(2); ignore(0) = "world"; ignore(1) = "simple"; // can't pass a FitsKeywordList as second arg of getKeywords because // it isn't const. ConstFitsKeywordList nativeListRO(nativeList); AlwaysAssertExit(FITSKeywordUtil::getKeywords(myNewKeywords, nativeListRO, ignore)); // compare contents of myNewKeywords with myKeywords // Every field in myKeywords should be in myNewKeywords EXCEPT // world, because we ignored it // comment* fields // history* fields // And alongname will have been truncated to alongnam // also check comments of each field for (uInt i=0;i=0 || inName == "world" || inName.contains(Regex("^comment")) || inName.contains(Regex("^history"))); // check types if (outField >= 0) { // short -> int // uInt -> int // Array -> Array DataType inType = myKeywords.dataType(i); DataType outType = myNewKeywords.dataType(outField); AlwaysAssertExit(inType == outType || (inType == TpShort && outType == TpInt) || (inType == TpUInt && outType == TpInt) || (inType == TpArrayFloat && outType == TpArrayDouble)); // check shapes AlwaysAssertExit(myKeywords.shape(i) == myNewKeywords.shape(outField)); // check comments AlwaysAssertExit(myKeywords.comment(i) == myNewKeywords.comment(outField)); // finally, check values switch(myKeywords.dataType(i)) { case TpBool: AlwaysAssertExit(myKeywords.asBool(i) == myNewKeywords.asBool(outField)); break; case TpUInt: AlwaysAssertExit(Int(myKeywords.asuInt(i)) == myNewKeywords.asInt(outField)); break; case TpInt: AlwaysAssertExit(myKeywords.asInt(i) == myNewKeywords.asInt(outField)); break; case TpShort: AlwaysAssertExit(Int(myKeywords.asShort(i)) == myNewKeywords.asInt(outField)); break; case TpFloat: AlwaysAssertExit(myKeywords.asFloat(i) == myNewKeywords.asFloat(outField)); break; case TpDouble: AlwaysAssertExit(myKeywords.asDouble(i) == myNewKeywords.asDouble(outField)); break; case TpString: AlwaysAssertExit(myKeywords.asString(i) == myNewKeywords.asString(outField)); break; case TpArrayBool: AlwaysAssertExit(allEQ(myKeywords.asArrayBool(i), myNewKeywords.asArrayBool(outField))); break; case TpArrayInt: AlwaysAssertExit(allEQ(myKeywords.asArrayInt(i), myNewKeywords.asArrayInt(outField))); break; case TpArrayFloat: { Array inArr(myKeywords.shape(i)); convertArray(inArr, myKeywords.asArrayFloat(i)); AlwaysAssertExit(allEQ(inArr, myNewKeywords.asArrayDouble(outField))); } break; case TpArrayDouble: AlwaysAssertExit(allEQ(myKeywords.asArrayDouble(i), myNewKeywords.asArrayDouble(outField))); break; case TpArrayString: AlwaysAssertExit(allEQ(myKeywords.asArrayString(i), myNewKeywords.asArrayString(outField))); break; default: throw(AipsError("Unrecognized field data type")); } } } // the COMMENT keywords can't come out easily, so don't test for them. // test HISTORY elsewhere // remove the tarray keywords ignore(0) = "tarray.*"; FITSKeywordUtil::removeKeywords(myNewKeywords, ignore); // verify that myNewKeywords doesn't contain any tarray keywords Regex rx("tarray.*"); for (uInt i=0;i c(1,2,3); c = 1; myKeywords = myNewKeywords = empty; nativeList = FITSKeywordUtil::makeKeywordList(); myKeywords.define("c", c); AlwaysAssertExit(!FITSKeywordUtil::addKeywords(nativeList, myKeywords)); AlwaysAssertExit(FITSKeywordUtil::getKeywords(myNewKeywords, nativeListRO, ignore)); AlwaysAssertExit(myNewKeywords.shape("c") == IPosition(1,6)); // name too long for matrix name - max of 2 chars myKeywords = myNewKeywords = empty; nativeList = FITSKeywordUtil::makeKeywordList(); myKeywords.define("mat", mat); AlwaysAssertExit(!FITSKeywordUtil::addKeywords(nativeList, myKeywords)); AlwaysAssertExit(FITSKeywordUtil::getKeywords(myNewKeywords, nativeListRO, ignore)); // all fields should not contain "mat" but should contain "ma" for (uInt i=0;i=0); // illegal array type Vector cvec(1); cvec = 1.0; myKeywords = myNewKeywords = empty; nativeList = FITSKeywordUtil::makeKeywordList(); myKeywords.define("cvec", cvec); AlwaysAssertExit(!FITSKeywordUtil::addKeywords(nativeList, myKeywords)); AlwaysAssertExit(FITSKeywordUtil::getKeywords(myNewKeywords, nativeListRO, ignore)); AlwaysAssertExit(myNewKeywords.nfields()==0); // illegal type (not a scalar or an array - i.e. another record); myKeywords = myNewKeywords = empty; myKeywords.defineRecord("rec",Record()); nativeList = FITSKeywordUtil::makeKeywordList(); AlwaysAssertExit(!FITSKeywordUtil::addKeywords(nativeList, myKeywords)); AlwaysAssertExit(FITSKeywordUtil::getKeywords(myNewKeywords, nativeListRO, ignore)); AlwaysAssertExit(myNewKeywords.nfields()==0); } catch (std::exception& x) { cerr << "Caught an exception: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/fits/FITS/test/tFITSSpectralUtil.cc000066400000000000000000000310011476623553700216040ustar00rootroot00000000000000//# tFITSSpectralUtil.cc: Test program for FITSSpectralUtil //# Copyright (C) 2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include int main() { try { // to from FITSHeader String ctype, cunit; Double crval, cdelt, crpix, altrval, altrpix; Int velref; Bool haveAlt; String specsys; Double restFreq = 1420.4058e6; Double refFreq = 1400.0e6; Double freqInc = 2.5e6; Double refPix = 512.0; MFrequency::Types refFrame = MFrequency::GALACTO; MDoppler::Types velPref = MDoppler::RADIO; LogIO logger; AlwaysAssertExit(FITSSpectralUtil:: toFITSHeader(ctype, crval, cdelt, crpix, cunit, haveAlt, altrval, altrpix, velref, restFreq, specsys, logger, refFreq, refPix, freqInc, refFrame, True, velPref)); // Actually construct the header record Record header; if (restFreq > 0) { header.define("restfrq",restFreq); header.setComment("restfrq","Rest Frequency (Hz)"); } if (haveAlt) { header.define("altrval",altrval); header.setComment("altrval","Alternate frequency reference value"); header.define("altrpix", altrpix); header.setComment("altrpix","Alternate frequency reference pixel"); header.define("velref", velref); header.setComment("velref", "1 LSR, 2 HEL, 3 OBS, +256 Radio"); // the following agree with the current usage in FITSSpectralUtil // which in turn follows from Greisen, Paper III. On the other // hand, that usage as applied here, to VELREF, is unlikely to // be understood by other FITS readers. Still, its better than // doing nothing for these rest frames until the convention in // Paper III or its successor is formally adopted. FITSKeywordUtil:: addComment(header, "casacore non-standard usage: 4 LSD, 5 GEO, 6 SOU, 7 GAL"); } header.define("specsys",specsys); // dummy primary header axes Vector ctypeVec(2), cunitVec(2); Vector crvalVec(2), crpixVec(2), cdeltVec(2); ctypeVec(0) = ctype; crvalVec(0) = crval; crpixVec(0) = crpix; cdeltVec(0) = cdelt; ctypeVec(1) = "STOKES"; crvalVec(1) = 1; crpixVec(1) = 1; cdeltVec(1) = 1; cunitVec(0) = "Hz"; cunitVec(1) = ""; // OK, put the primary header information back header.define("ctype", ctypeVec); header.define("crval", crvalVec); header.define("crpix", crpixVec); header.define("cdelt", cdeltVec); header.define("cunit", cunitVec); // and the other direction Int whichAxis; Double refPixOut, refFreqOut, freqIncOut, restFreqOut; Vector freqs; MFrequency::Types refFrameOut = MFrequency::GALACTO; MDoppler::Types velPrefOut = MDoppler::RADIO; AlwaysAssertExit(FITSSpectralUtil::fromFITSHeader(whichAxis, refPixOut, refFreqOut, freqIncOut, freqs, refFrameOut, velPrefOut, restFreqOut, logger, header, 'c', False)); AlwaysAssertExit(whichAxis==0); // note: the following is only true when onRelative==False in // fromFITSHeader AlwaysAssertExit(near(refPix,refPixOut)); AlwaysAssertExit(near(refFreq,refFreqOut)); AlwaysAssertExit(near(freqInc,freqIncOut)); AlwaysAssertExit(refFrame==refFrameOut); AlwaysAssertExit(velPref==velPrefOut); AlwaysAssertExit(near(restFreq,restFreqOut)); // tags from/to frames for (uInt i=0;i 0) { header.define("restfreq",restFreq); header.setComment("restfreq","Rest Frequency (Hz)"); } if (haveAlt) { header.define("altrval",altrval); header.setComment("altrval","Alternate frequency reference value"); header.define("altrpix", altrpix); header.setComment("altrpix","Alternate frequency reference pixel"); header.define("velref", velref); header.setComment("velref", "1 LSR, 2 HEL, 3 OBS, +256 Radio"); // the following agree with the current usage in FITSSpectralUtil // which in turn follows from Greisen, Paper III. On the other // hand, that usage as applied here, to VELREF, is unlikely to // be understood by other FITS readers. Still, its better than // doing nothing for these rest frames until the convention in // Paper III or its successor is formally adopted. FITSKeywordUtil:: addComment(header, "casacore non-standard usage: 4 LSD, 5 GEO, 6 SOU, 7 GAL"); } header.define("specsys",specsys); // dummy primary header axes Vector ctypeVec(2), cunitVec(2); Vector crvalVec(2), crpixVec(2), cdeltVec(2); ctypeVec(0) = ctype; crvalVec(0) = crval; crpixVec(0) = crpix; cdeltVec(0) = cdelt; cunitVec(0) = cunit; ctypeVec(1) = "STOKES"; crvalVec(1) = 1; crpixVec(1) = 1; cdeltVec(1) = 1; cunitVec(1) = ""; // OK, put the primary header information back header.define("ctype", ctypeVec); header.define("crval", crvalVec); header.define("crpix", crpixVec); header.define("cdelt", cdeltVec); header.define("cunit", cunitVec); // and the other direction Int whichAxis; Double refPixOut, refFreqOut, freqIncOut, restFreqOut; Vector freqs; MFrequency::Types refFrameOut = MFrequency::GALACTO; MDoppler::Types velPrefOut = MDoppler::RADIO; AlwaysAssertExit(FITSSpectralUtil::fromFITSHeader(whichAxis, refPixOut, refFreqOut, freqIncOut, freqs, refFrameOut, velPrefOut, restFreqOut, logger, header, 'c', False)); AlwaysAssertExit(whichAxis==0); // note: the following is only true when onRelative==False in // fromFITSHeader AlwaysAssertExit(near(refPix,refPixOut)); AlwaysAssertExit(near(refFreq,refFreqOut)); AlwaysAssertExit(near(freqInc,freqIncOut, 3E-6)); AlwaysAssertExit(refFrame==refFrameOut); AlwaysAssertExit(near(restFreq,restFreqOut)); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } try { // same as above, however with air wavelength spectral axis // to from FITSHeader String ctype, cunit, specsys; Double crval, cdelt, crpix, altrval, altrpix; Int velref; Bool haveAlt; Double restFreq = 1420.4058e6; Double refFreq = 1400.0e6; Double freqInc = 2.5e6; Double refPix = 512.0; MFrequency::Types refFrame = MFrequency::GALACTO; MDoppler::Types velPref = MDoppler::RADIO; LogIO logger; AlwaysAssertExit(FITSSpectralUtil:: toFITSHeader(ctype, crval, cdelt, crpix, cunit, haveAlt, altrval, altrpix, velref, restFreq, specsys, logger, refFreq, refPix, freqInc, refFrame, False, velPref, True, True)); // air wavelength preferred // Actually construct the header record Record header; if (restFreq > 0) { header.define("restfreq",restFreq); header.setComment("restfreq","Rest Frequency (Hz)"); } if (haveAlt) { header.define("altrval",altrval); header.setComment("altrval","Alternate frequency reference value"); header.define("altrpix", altrpix); header.setComment("altrpix","Alternate frequency reference pixel"); header.define("velref", velref); header.setComment("velref", "1 LSR, 2 HEL, 3 OBS, +256 Radio"); // the following agree with the current usage in FITSSpectralUtil // which in turn follows from Greisen, Paper III. On the other // hand, that usage as applied here, to VELREF, is unlikely to // be understood by other FITS readers. Still, its better than // doing nothing for these rest frames until the convention in // Paper III or its successor is formally adopted. FITSKeywordUtil:: addComment(header, "casacore non-standard usage: 4 LSD, 5 GEO, 6 SOU, 7 GAL"); } header.define("specsys",specsys); // dummy primary header axes Vector ctypeVec(2), cunitVec(2); Vector crvalVec(2), crpixVec(2), cdeltVec(2); ctypeVec(0) = ctype; crvalVec(0) = crval; crpixVec(0) = crpix; cdeltVec(0) = cdelt; cunitVec(0) = cunit; ctypeVec(1) = "STOKES"; crvalVec(1) = 1; crpixVec(1) = 1; cdeltVec(1) = 1; cunitVec(1) = ""; // OK, put the primary header information back header.define("ctype", ctypeVec); header.define("crval", crvalVec); header.define("crpix", crpixVec); header.define("cdelt", cdeltVec); header.define("cunit", cunitVec); // and the other direction Int whichAxis; Double refPixOut, refFreqOut, freqIncOut, restFreqOut; Vector freqs; MFrequency::Types refFrameOut = MFrequency::GALACTO; MDoppler::Types velPrefOut = MDoppler::RADIO; AlwaysAssertExit(FITSSpectralUtil::fromFITSHeader(whichAxis, refPixOut, refFreqOut, freqIncOut, freqs, refFrameOut, velPrefOut, restFreqOut, logger, header, 'c', False)); AlwaysAssertExit(whichAxis==0); // note: the following is only true when onRelative==False in // fromFITSHeader AlwaysAssertExit(near(refPix,refPixOut)); AlwaysAssertExit(near(refFreq,refFreqOut, 1E-11)); AlwaysAssertExit(near(freqInc,freqIncOut, 3E-6)); AlwaysAssertExit(refFrame==refFrameOut); AlwaysAssertExit(near(restFreq,restFreqOut)); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/fits/FITS/test/t_priArr_imgExt.cc000066400000000000000000000105001476623553700214170ustar00rootroot00000000000000//# t_priArr_imgExt.cc: FITS test program to create a primary array and an image extension //# using the PrimaryArray::write_priArr_hdr() and //# ImageExtension::write_imgExt_hdr() //# Modified from Copyright (C) 1993,1994,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes # include # include # include # include # include # include int main() { cout << "Test to create a primary array and an image extension\n"; FitsOutput fout("t_priArr_imgExt.dat",FITS::Disk); if (fout.err() == FitsIO::IOERR) { cout << "Could not open FITS output.\n"; exit(0); } // We will create an array with 3 rows and 6 columns, as one // would normally do in C. const int row = 10; const int col = 10; FitsLong data[col][row]; // And, we will populate it with data int i, j; for (i = 0; i < col; ++i) for(j = 0; j < row; ++j) data[i][j] = j * 10 + i; // Create the initial keyword list /*FitsKeywordList st; st.mk(FITS::SIMPLE,True,"Standard FITS format"); st.mk(FITS::BITPIX,32,"Integer data"); st.mk(FITS::NAXIS,2,"This is a primary array"); st.mk(1,FITS::NAXIS,row); st.mk(2,FITS::NAXIS,col); st.mk(FITS::EXTEND,True,"Extension exists"); //st.mk(FITS::EXTEND,False,"Extension exists"); st.spaces(); st.comment("This is test tfits_priArr."); st.spaces(); st.end(); */ // Create and write the initial HDU PrimaryArray hdu1; if (hdu1.err()){ cout<<"Error during construction of PrimaryArray."< ie; if (ie.err()) exit(0); cout << "ImageExtension constructed\n"; // Display the keyword list cout << ie; // parameters for write_imgExt_hdr() bitpix=32; naxis=2; long naxes_img[2]; naxes_img[0] = row; naxes_img[1] = col; if( !ie.write_imgExt_hdr(fout,bitpix, naxis, naxes_img) ){ cout << "ImageExtension header wrote ok!"<< endl; //return 0; } ie.set_next(row * col); // setup to write the whole array cout<<"set_next() done." << endl; for (i = 0; i < row; ++i){ for(j = 0; j < col; ++j) { ie.data(i,j) = i * 10 + j; // assign the data //cout<< "ie.data(i,j) = " << ie.data(i,j) << endl; } } cout<< "data(i,j) assignment ok."<< endl; ie.write(fout); // write the data cout << "ImageExtension data wrote!"<< endl; return 0; } casacore-3.7.1/fits/FITS/test/tfits1.cc000066400000000000000000000364451476623553700175520ustar00rootroot00000000000000//# tfits1.cc: FITS test program to read and display values from a FITS file //# Copyright (C) 1993,1994,1996,1998,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes # include # include # include # include # include # include #include // # include // test // Display basic info and the keyword list void show(HeaderDataUnit *h) { cout << "Data type " << h->datatype() << "\n" << "Data size " << h->fitsdatasize() << "\n" << "Dimensions " << h->dims() << "\n"; for (int n = 0; n < h->dims(); n++) cout << "Axis " << (n + 1) << " size " << h->dim(n) << "\n"; cout << "----- Keyword List -----\n" << *h << "\n"; } // Read the data in a Primary Group and display the first few groups #define DOGROUP(Z) void do_primary_group(PrimaryGroup &x) { \ int i, j; \ int number_to_display = 10; \ show(&x); \ if (x.err() != HeaderDataUnit::OK) { \ cout << "Error occured during construction process -- exiting\n"; \ exit(0); \ } \ for (i = 0; i < x.gcount(); ++i) { \ x.read(); \ if (i < number_to_display) { \ cout << "Group " << i << " parms: " << "\n"; \ for (j = 0; j < x.pcount(); ++j) \ cout << " "<< x.parm(j); \ cout << "\n"; \ cout << "Group " << i << " data: " << "\n"; \ for (j = 0; j < 4; ++j) \ cout << " " << x(j); \ cout << "\n"; \ } \ } \ delete &x; \ } // Read the data in a Primary Array and display the first few data points #define DOARRAY(Z) void do_primary_array(PrimaryArray &x) { \ cout << "[ tftis1.cc::do_primary_array() ] called. " << endl; \ int i, j, n0, n1; \ cout<< " The header card images are( testing kwlist_str()): " << endl; \ Vector imageCards = x.kwlist_str(); \ for( uInt k = 0; k< imageCards.nelements(); k++ ){ \ cout << imageCards[ k ] << endl;\ }\ if (x.fitsdatasize()) \ if(x.read()==-1 ){ exit(0);} \ show(&x); \ if (x.err() != HeaderDataUnit::OK) { \ cout << "Error occured during construction process -- exiting\n"; \ exit(0); \ } \ if (x.dims() == 2) { \ n0 = x.dim(0) > 60 ? 60 : x.dim(0); \ n1 = x.dim(1) > 60 ? 60 : x.dim(1); \ for (i = 0; i < n0; ++i) \ for (j = 0; j < n1; ++j) \ cout << "(" << i << "," << j << ") = " \ << x(i,j) << "\n"; \ } \ delete &x; \ } // now actually make the necessary versions of the above DOGROUP(unsigned char) DOGROUP(short) DOGROUP(FitsLong) DOGROUP(float) DOGROUP(double) DOARRAY(unsigned char) DOARRAY(short) DOARRAY(FitsLong) DOARRAY(float) DOARRAY(double) #undef DOGROUP #undef DOARRAY // Read and display a binary table void do_binary_table(BinaryTableExtension &x) { if (x.err() != 0) { cout << "BT ERROR! " << x.err() << endl; return; } cout << x.nrows() << " rows " << x.ncols() << " cols " << x.fitsdatasize() << " bytes total\n" << endl; cout << "----- Keyword List -----\n" << x << endl; cout << "\nTable Data\n\n"; int i; for (i = 0; i < x.ncols(); ++i) { cout << "Col " << i << ": " << x.field(i).nelements() << " " << x.field(i).fieldtype() << " " << x.ttype(i) << " " << x.tunit(i) << "\n"; } cout << endl; cout <<" [do_binary_table()] read all the table rows." << endl; cout <<" [do_binary_table()] x.nrows() = " << x.nrows() << endl; x.read(x.nrows()); // read all the table rows // any heap to read? char * theheap = 0; if (x.pcount()) { // offset of start of heap from current position, end of last row if (x.notnull(x.theap())) { int heapOffset = x.theap() - x.rowsize()*x.nrows(); // skip to the start of the heap // I don't see any way except to read these bogus bytes char *junk = new char[heapOffset]; x.ExtensionHeaderDataUnit::read(junk, heapOffset); } theheap = new char [x.pcount()]; // this code never checks for alloc errors, why start now x.ExtensionHeaderDataUnit::read(theheap, x.pcount()); } FITS::ValueType *vatypes = new FITS::ValueType[x.ncols()]; void **vaptr = new void *[x.ncols()]; VADescFitsField *va = new VADescFitsField[x.ncols()]; // decode the TFORMs of any VADESC columns for (i=0;i> k%8)); } } break; case FITS::BYTE: { unsigned char *vptr = (unsigned char *)(vaptr[i]); FITS::f2l(vptr, (void *)(theheap + thisva.offset()), thisva.num()); cout << (int)vptr[0]; for (int k=1;k *paB; PrimaryArray *paS; PrimaryArray *paL; PrimaryArray *paF; PrimaryArray *paD; PrimaryGroup *pgB; PrimaryGroup *pgS; PrimaryGroup *pgL; PrimaryGroup *pgF; PrimaryGroup *pgD; BinaryTableExtension *bt; AsciiTableExtension *at; cout << "Test of reading fits files" << endl; if (argc != 2) { cout << "ex1 " << "\n"; exit(0); } cout<<"argv[1]="<(fin); do_primary_array(*paB); break; case FITS::SHORT: paS = new PrimaryArray(fin); do_primary_array(*paS); break; case FITS::LONG: cout<<"About to instantiate PrimaryArray."<< endl; paL = new PrimaryArray(fin); cout<<"About to call do_primary_array( *paL )." << endl; do_primary_array(*paL); cout<<"After called do_primary_array()."<< endl; break; case FITS::FLOAT: cout<<"About to instantiate PrimaryArray."<< endl; paF = new PrimaryArray(fin); cout<<"About to call do_primary_array()"<< endl; do_primary_array(*paF); cout<<"Called do_primary_array()"<< endl; break; case FITS::DOUBLE: paD = new PrimaryArray(fin); do_primary_array(*paD); break; default: break; } cout<<"PrimaryArray appropriated object instantiated."<< endl; break; case FITS::PrimaryGroupHDU: cout << "----- Primary Group -----\n"; switch (fin.datatype()) { case FITS::BYTE: pgB = new PrimaryGroup(fin); do_primary_group(*pgB); break; case FITS::SHORT: pgS = new PrimaryGroup(fin); do_primary_group(*pgS); break; case FITS::LONG: pgL = new PrimaryGroup(fin); do_primary_group(*pgL); break; case FITS::FLOAT: pgF = new PrimaryGroup(fin); do_primary_group(*pgF); break; case FITS::DOUBLE: pgD = new PrimaryGroup(fin); do_primary_group(*pgD); break; default: break; } break; case FITS::AsciiTableHDU: at = new AsciiTableExtension(fin); cout << "\n\nASCII Table "; do_binary_table(*at); break; case FITS::BinaryTableHDU: bt = new BinaryTableExtension(fin); cout << "\n\nBinary Table "; do_binary_table(*bt); break; case FITS::ImageExtensionHDU: cout << "----- Image Extension -----\n"; switch (fin.datatype()) { case FITS::BYTE: paB = new ImageExtension(fin); do_primary_array(*paB); break; case FITS::SHORT: paS = new ImageExtension(fin); do_primary_array(*paS); break; case FITS::LONG: paL = new ImageExtension(fin); do_primary_array(*paL); break; case FITS::FLOAT: paF = new ImageExtension(fin); do_primary_array(*paF); break; case FITS::DOUBLE: paD = new ImageExtension(fin); do_primary_array(*paD); break; default: break; } cout << " Image Extension is passed into do_primary_array()." << endl; break; case FITS::UnknownExtensionHDU: h = new ExtensionHeaderDataUnit(fin); cout << "----- Unknown conforming extension -----\n"; h->skip(); delete h; break; default: cout << "This isn't supposed to happen\n"; break; } }else if (fin.rectype() == FITS::BadBeginningRecord || fin.rectype() == FITS::UnrecognizableRecord) { cout << "Bad Record encountered\n"; exit(0); }else if (fin.rectype() == FITS::SpecialRecord) { cout << "Special Record encountered\n"; exit(0); } if (fin.err()) ++nerrs; } // end of for( nerrs = 0, .... ) loop if (nerrs == NMAXERRS) cout << "Too many errors. Processing terminated.\n"; else cout << "End of Header-Data Units.\n"; cout << endl; cout<< "[tfits1.cc] Before retrun."<< endl; return 0; } casacore-3.7.1/fits/FITS/test/tfits2.cc000066400000000000000000000072341476623553700175450ustar00rootroot00000000000000//# tfits2.cc: FITS test program to create a primary array and an image extension //# Copyright (C) 1993,1994,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes # include # include # include # include # include #include int main() { cout << "Test to create a primary array and an image extension\n"; FitsOutput fout("tfits2.dat",FITS::Disk); if (fout.err() == FitsIO::IOERR) { cout << "Could not open FITS output.\n"; exit(0); } // We will create an array with 10 rows and 10 columns, as one // would normally do in C. const int row = 10; const int col = 10; FitsLong data[col][row]; // And, we will populate it with data int i, j; for (i = 0; i < col; ++i) for(j = 0; j < row; ++j) data[i][j] = j * 10 + i; // Create the initial keyword list FitsKeywordList st; st.mk(FITS::SIMPLE,True,"Standard FITS format"); st.mk(FITS::BITPIX,32,"Integer data"); st.mk(FITS::NAXIS,2,"This is a primary array"); st.mk(1,FITS::NAXIS,row); st.mk(2,FITS::NAXIS,col); st.mk(FITS::EXTEND,True,"Extension exists"); //st.mk(FITS::EXTEND,False,"Extension exists"); st.spaces(); st.comment("This is test 2."); st.spaces(); st.end(); // Create and write the initial HDU PrimaryArray hdu1(st); if (hdu1.err()) exit(0); cout << "Initial HDU constructed\n"; // Display the keyword list cout << hdu1; if( !hdu1.write_hdr(fout) ){ cout << "Primary Header wrote ok!"< ie(kw); if (ie.err()) exit(0); cout << "ImageExtension constructed\n"; // Display the keyword list cout << ie; if( !ie.write_hdr(fout) ){ cout << "ImageExtension header wrote ok!"<< endl; } ie.set_next(row * col); // setup to write the whole array for (i = 0; i < row; ++i){ for(j = 0; j < col; ++j) { ie.data(i,j) = i * 10 + j; } // assign the data } cout<< "data(i,j) assignment ok."<< endl; ie.write(fout); // write the data cout << "ImageExtension data wrote!"<< endl; return 0; } casacore-3.7.1/fits/FITS/test/tfits3.cc000066400000000000000000000056261476623553700175510ustar00rootroot00000000000000//# tfits3.cc: FITS test program to create a random group //# Copyright (C) 1993,1994,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes # include # include # include # include # include #include int main() { cout << "Test to create a random group\n"; FitsOutput fout("tfits3.dat",FITS::Disk); if (fout.err() == FitsIO::IOERR) { cout << "Could not open FITS output.\n"; exit(0); } int no_parms = 3; // we will create 3 parms in each group int no_groups = 6; // and 6 groups int no_data = 4; // and 4 data points in each group // Create a keyword list for a random group FitsKeywordList kw; kw.mk(FITS::SIMPLE,True,"Standard FITS format"); kw.mk(FITS::BITPIX,32,"Integer data"); kw.mk(FITS::NAXIS,2); kw.mk(1,FITS::NAXIS,0); kw.mk(2,FITS::NAXIS,no_data); kw.mk(FITS::GROUPS,True,"Random Group structure"); kw.mk(FITS::PCOUNT,no_parms); kw.mk(FITS::GCOUNT,no_groups); kw.spaces(); kw.comment("This is test 3."); kw.spaces(); kw.end(); PrimaryGroup pg(kw); if (pg.err()) exit(0); cout << "PrimaryGroup constructed\n"; cout << "Data type " << pg.datatype() << "\n" << "Data size " << pg.fitsdatasize() << "\n" << "Dimensions " << pg.dims() << "\n"; for (int n = 0; n < pg.dims(); n++) cout << "Axis " << (n + 1) << " size " << pg.dim(n) << endl; cout << pg << endl; // Display the keyword list pg.write_hdr(fout); int i, j; for (i = 0; i < no_groups; ++i) { for (j = 0; j < no_parms; ++j) pg.rawparm(j) = i * 10 + j; // assign the parms for (j = 0; j < no_data; ++j) pg.data(j) = i * 10 + j + 5; // assign the data pg.write(fout); // write the group } cout << "PrimaryGroup data written\n"; return 0; } casacore-3.7.1/fits/FITS/test/tfits4.cc000066400000000000000000000163261476623553700175510ustar00rootroot00000000000000//# tfits4.cc: FITS test program to create a binary table //# Copyright (C) 1993,1994,1998,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes # include # include # include # include # include #include int main() { cout << "Test to create a binary table\n"; FitsOutput fout("tfits4.dat",FITS::Disk); if (fout.err() == FitsIO::IOERR) { cout << "Could not open FITS output.\n"; exit(0); } // Create the initial keyword list FitsKeywordList st; st.mk(FITS::SIMPLE,True,"Standard FITS format"); st.mk(FITS::BITPIX,8,"Character Information"); st.mk(FITS::NAXIS,0,"No image data array present"); st.mk(FITS::EXTEND,True,"Extension exists"); st.mk("FILETYPE","GSC_REGION","Indicates file type"); st.mk(FITS::ORIGIN,"ST ScI","Space Telescope Science Institute"); st.mk(FITS::DATE,"01/06/89","Date of issue (dd/mm/yy)"); st.spaces(); st.comment(" THE GUIDE STAR CATALOG"); st.comment(""); st.comment(" An all-sky astrometric and photometric catalog"); st.comment(" prepared for the operation of the Hubble Space"); st.comment(" Telescope."); st.comment(""); st.comment(" Copyright, 1989, Association of Universities"); st.comment(" for Research in Astronomy, Inc."); st.comment(""); st.comment("This file contains data for one of the 9537 regions constituting the"); st.comment("Guide Star Catalog (GSC). Additional information on the GSC may be found"); st.comment("in accompanying scientific publications as well as in comments and"); st.comment("tables elsewhere on this set of volumes."); st.comment(""); st.comment("The Guide Star Catalog (GSC) was prepared by the Space Telescope Science"); st.comment("Institute (ST ScI), 3700 San Martin Drive, Baltimore, MD 21218, USA."); st.comment("ST ScI is operated by the Association of Universities for Research in"); st.comment("Astronomy, Inc. (AURA), under contract with the National Aeronautics and"); st.comment("Space Administration (NASA)."); st.spaces(); st.end(); // Create and write the initial HDU PrimaryArray hdu1(st); if (hdu1.err()) exit(0); cout << "Initial HDU constructed\n"; // Display the keyword list cout << hdu1; hdu1.write_hdr(fout); // Create the keyword list for the binary table FitsKeywordList kw; kw.mk(FITS::XTENSION,"BINTABLE","Binary Table Extension"); kw.mk(FITS::BITPIX,8,"Character Information"); kw.mk(FITS::NAXIS,2,"Two-dimensional table"); kw.mk(1,FITS::NAXIS,33,"Number of bytes per row"); kw.mk(2,FITS::NAXIS,50,"Number of rows"); kw.mk(FITS::PCOUNT,0,"No random parameters"); kw.mk(FITS::GCOUNT,1,"Only one group"); kw.mk(FITS::TFIELDS,10,"Ten fields per row"); kw.spaces(); kw.mk(FITS::EXTNAME,"GSC_REGION_00100","GSC Region No. 00100"); kw.mk(FITS::EXTVER,1,"Integer Version Number"); kw.spaces(); kw.mk(1,FITS::TTYPE,"GSC_ID","ID within Region"); kw.mk(1,FITS::TFORM,"J","Integer, 5 character field (I5.5 Style)"); kw.spaces(); kw.mk(2,FITS::TTYPE,"RA_DEG","Right Ascension - Decimal Degrees (0 to 360)"); kw.mk(2,FITS::TFORM,"E","Floating, 9 character field"); kw.spaces(); kw.mk(3,FITS::TTYPE,"DEC_DEG","Declination - Decimal Degrees (-90 to +90)"); kw.mk(3,FITS::TFORM,"E","Floating, 9 character field"); kw.spaces(); kw.mk(4,FITS::TTYPE,"POS_ERR","Position Error in Arc Seconds"); kw.mk(4,FITS::TFORM,"E","Floating, 5 character field"); kw.spaces(); kw.mk(5,FITS::TTYPE,"MAG","Magnitude"); kw.mk(5,FITS::TFORM,"E","Floating, 5 character field"); kw.spaces(); kw.mk(6,FITS::TTYPE,"MAG_ERR","Magnitude error"); kw.mk(6,FITS::TFORM,"E","Floating, 4 character field"); kw.spaces(); kw.mk(7,FITS::TTYPE,"MAG_BAND","Magnitude Band"); kw.mk(7,FITS::TFORM,"I","Integer, 2 character field (I2.2 Style)"); kw.spaces(); kw.mk(8,FITS::TTYPE,"CLASS","Classification"); kw.mk(8,FITS::TFORM,"I","Integer, 1 character field"); kw.spaces(); kw.mk(9,FITS::TTYPE,"PLATE_ID","GSSS Internal Plate Number"); kw.mk(9,FITS::TFORM,"4A","4 character field"); kw.spaces(); kw.mk(10,FITS::TTYPE,"MULTIPLE","(T/F) Flag for additional entries"); kw.mk(10,FITS::TFORM,"1A","Logical flag, 1 character field"); kw.spaces(); kw.end(); // Create and write the binary table HDU BinaryTableExtension bt(kw); if (bt.err()) exit(0); cout << "Binary Table HDU constructed\n"; // Display the keyword list cout << bt; bt.write_hdr(fout); // Construct variables that correspond to the fields FitsField id; FitsField ra; FitsField dec; FitsField pos_err; FitsField mag; FitsField mag_err; FitsField mag_band; FitsField classification; FitsField plate_id(4); FitsField multiple; // Bind the variables to the fields in the table bt.bind(0,id); bt.bind(1,ra); bt.bind(2,dec); bt.bind(3,pos_err); bt.bind(4,mag); bt.bind(5,mag_err); bt.bind(6,mag_band); bt.bind(7,classification); bt.bind(8,plate_id); bt.bind(9,multiple); // write the first batch of rows one at a time int i; for (i = 0; i < 25; ++i) { bt.set_next(1); id = i; ra = 79.8185 + i; dec = 0.08173 + i; pos_err = 0.3; mag = 12.19; mag_err = 0.4; mag_band = 1; classification = 3; plate_id(0) = '0'; plate_id(1) = '4'; plate_id(2) = 'Y'; plate_id(3) = 'P'; multiple = 'T'; cout << bt.currrow() << ": " << id << " " << ra << " " << dec << "\n"; bt.write(fout); } // write the remaining rows 5 at a time for (i = 0; i < 5; ++i) { bt.set_next(5); // set_next resets the current row for (int j = 0; j < 5; ++j) { // assign values to the next 5 rows id = i*10 + j; ra = 79.8185 + i*10 + j; dec = 0.08173 + i*10 + j; pos_err = 0.3; mag = 12.19; mag_err = 0.4; mag_band = 1; classification = 3; plate_id(0) = '0'; plate_id(1) = '4'; plate_id(2) = 'Y'; plate_id(3) = 'P'; multiple = 'T'; cout << bt.currrow() << ": " << id << " " << ra << " " << dec << "\n"; ++bt; // advance the current row } bt.write(fout); // write writes all rows designated in set_next } return 0; } casacore-3.7.1/fits/FITS/test/tfits5.cc000066400000000000000000000217511476623553700175500ustar00rootroot00000000000000//# tfits5.cc: FITS test program to read and display values from a FITS file //# Copyright (C) 1993,1994,1998,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes # include # include # include # include # include # include # include #include // Display basic info and the keyword list void show(HeaderDataUnit *h) { cout << "Data type " << h->datatype() << "\n" << "Data size " << h->fitsdatasize() << "\n" << "Dimensions " << h->dims() << "\n"; for (int n = 0; n < h->dims(); n++) cout << "Axis " << (n + 1) << " size " << h->dim(n) << "\n"; cout << "----- Keyword List -----\n" << *h << "\n"; } // Read and display a binary table void cvt_table(AsciiTableExtension &x, FitsOutput &fout) { if (x.err() != 0) { cout << "BT ERROR! " << x.err() << endl; return; } cout << x.nrows() << " rows " << x.ncols() << " cols " << x.fitsdatasize() << " bytes total\n" << endl; cout << "----- Keyword List -----\n" << x << endl; cout << "\nTable Data\n\n"; int i; for (i = 0; i < x.ncols(); ++i) { cout << "Col " << i << ": " << x.field(i).nelements() << " " << x.field(i).fieldtype() << " " << x.ttype(i) << " " << x.tunit(i) << "\n"; } cout << endl; x.read(); // read the whole ascii table int n; for (n = 0; n < 50 && n < x.nrows(); ++n) { // display first 50 rows cout << x.currrow() << ": | "; if (x.field(0).nelements() != 0) cout << x.field(0); for (i = 1; i < x.ncols(); ++i) { cout << " | "; if (x.field(i).nelements() != 0) cout << x.field(i); } cout << " |" << endl; ++x; // increment the current row } // We must first build a new keyword list using the old one FitsKeywordList bk = x.kwlist(); bk.first(); n = 0; char p[24]; int rs = 0; // compute the size of a row in the binary table for (i = 0; i < x.ncols(); ++i) rs += x.field(i).fitsfieldsize(); while (bk.next()) { // change the EXTENSION keyword if (bk.curr()->isreserved() && bk.curr()->kw().name() == FITS::XTENSION) { bk.del(); bk.mk(FITS::XTENSION,"BINTABLE"); } if (bk.curr()->isreserved() && bk.curr()->isindexed()) { // delete TBCOL and TNULL keywords if (bk.curr()->kw().name() == FITS::TBCOL || bk.curr()->kw().name() == FITS::TNULL) bk.del(); // and convert the TFORM keyword if (bk.curr()->kw().name() == FITS::TFORM) { bk.del(); switch (x.field(n).fieldtype()) { case FITS::CHAR: snprintf(p,sizeof(p),"%dA",x.field(n).fitsfieldsize()); bk.mk((n + 1),FITS::TFORM,p); break; case FITS::LONG: bk.mk((n + 1),FITS::TFORM,"J"); break; case FITS::FLOAT: bk.mk((n + 1),FITS::TFORM,"E"); break; case FITS::DOUBLE: bk.mk((n + 1),FITS::TFORM,"D"); break; default: break; } ++n; } // and convert NAXIS1 if (bk.curr()->kw().name() == FITS::NAXIS && bk.curr()->index() == 1) { bk.del(); bk.mk(1,FITS::NAXIS,rs); } } } cout << "New keyword list:\n" << bk << endl; BinaryTableExtension bt(bk); // create the binary table cout << "the new binary table\n"; show(&bt); bt.write_hdr(fout); /********************************************************************** This is one implementation *********************************************************************** // get ready to transfer the data bt.set_next(x.nrows()); x(0); // reset the ascii row pointer to the first row for (n = 0; n < x.nrows(); ++n) { for (i = 0; i < x.ncols(); ++i) bt.field(i) = x.field(i); ++x; // increment the ascii table row ++bt; // increment the binary table row } bt.write(fout); **********************************************************************/ /********************************************************************** This is another implementation that accomplishes the same thing. **********************************************************************/ FitsField *target_c; FitsField *source_c; FitsField *target_l; FitsField *source_l; FitsField *target_f; FitsField *source_f; FitsField *target_d; FitsField *source_d; FitsBase **fb; // allocate an array of pointers to the fields fb = new FitsBase * [bt.ncols()]; // and make a FitsField for each field for (i = 0; i < bt.ncols(); ++i) { // this makes a field exactly like the table field fb[i] = FitsBase::make(bt.field(i)); if (fb[i] == 0) exit(-1); // and bind the FitsField to the column bt.bind(i,*fb[i]); } // Now, assign data values and write the table bt.set_next(bt.nrows()); x(0); // reset the ascii row pointer to the first row for (n = 0; n < x.nrows(); ++n) { for (i = 0; i < x.ncols(); ++i) { switch (fb[i]->fieldtype()) { case FITS::CHAR: target_c = (FitsField *)fb[i]; source_c = &((FitsField &)x.field(i)); strncpy(&((*target_c)(0)),&((*source_c)(0)), fb[i]->fitsfieldsize()); break; case FITS::LONG: target_l = (FitsField *)fb[i]; source_l = &((FitsField &)x.field(i)); (*target_l)(0) = (*source_l)(0); break; case FITS::FLOAT: target_f = (FitsField *)fb[i]; source_f = &((FitsField &)x.field(i)); (*target_f)(0) = (*source_f)(0); break; case FITS::DOUBLE: target_d = (FitsField *)fb[i]; source_d = &((FitsField &)x.field(i)); (*target_d)(0) = (*source_d)(0); break; default: break; } } ++x; // increment the ascii table row ++bt; // increment the binary table row } bt.write(fout); /**********************************************************************/ delete &x; } int main(int argc, const char* argv[]) { AsciiTableExtension *at; cout << "Test5 -- convert an ASCII table to a binary table" << endl; if (argc != 2) { cout << "ex1 " << "\n"; exit(0); } FitsInput fin(argv[1],FITS::Disk); if (fin.err() == FitsIO::IOERR) { cout << "Could not open FITS input.\n"; exit(0); } else if (fin.err()) exit(0); const int NMAXERRS = 100; FitsOutput fout("tfits5.dat",FITS::Disk); if (fout.err() == FitsIO::IOERR) { cout << "Could not open FITS output.\n"; exit(0); } // Create the initial keyword list FitsKeywordList st; st.mk(FITS::SIMPLE,True,"Standard FITS format"); st.mk(FITS::BITPIX,8,"Character Information"); st.mk(FITS::NAXIS,0,"No image data array present"); st.mk(FITS::EXTEND,True,"Extension exists"); st.end(); // Create and write the initial HDU PrimaryArray hdu1(st); if (hdu1.err()) exit(0); cout << "Initial HDU constructed\n"; // Display the keyword list cout << hdu1; hdu1.write_hdr(fout); if (hdu1.err()) exit(0); int nerrs; for(nerrs = 0; nerrs < NMAXERRS && fin.rectype() != FITS::EndOfFile; ) { if (fin.rectype() == FITS::HDURecord) { switch (fin.hdutype()) { case FITS::PrimaryArrayHDU: case FITS::PrimaryGroupHDU: case FITS::BinaryTableHDU: case FITS::ImageExtensionHDU: case FITS::UnknownExtensionHDU: fin.skip_hdu(); cout << "Skipping HDU\n"; break; case FITS::AsciiTableHDU: at = new AsciiTableExtension(fin); if (at->err()) exit(0); cout << "\n\nASCII Table "; cvt_table(*at,fout); break; default: cout << "This isn't supposed to happen\n"; break; } } else if (fin.rectype() == FITS::BadBeginningRecord || fin.rectype() == FITS::UnrecognizableRecord) fin.read_sp(); else if (fin.rectype() == FITS::SpecialRecord) { cout << "Special Record encountered\n"; exit(0); } if (fin.err()) ++nerrs; } if (nerrs == NMAXERRS) cout << "Too many errors. Processing terminated.\n"; else cout << "End of Header-Data Units.\n"; cout << endl; return 0; } casacore-3.7.1/fits/FITS/test/tfits_ascTbl.cc000066400000000000000000000134701476623553700207520ustar00rootroot00000000000000//# tfits_ascTbl.cc: FITS test program to create a ascii table by using //# AsciiTableExtension::write_ascTbl_hdr(). //# Modified from Copyright (C) 1993,1994,1998,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes # include # include # include # include # include # include int main() { cout << "Test to create a binary table\n"; FitsOutput fout("tfits_ascTbl.dat",FITS::Disk); if (fout.err() == FitsIO::IOERR) { cout << "Could not open FITS output.\n"; exit(0); } // We will create an array with 3 rows and 6 columns, as one // would normally do in C. const int row = 10; const int col = 10; FitsLong data[col][row]; // And, we will populate it with data int i, j; for (i = 0; i < col; ++i) for(j = 0; j < row; ++j) data[i][j] = j * 10 + i; // Create and write the initial HDU PrimaryArray hdu1; if (hdu1.err()){ cout<<"Error during construction of PrimaryArray."< id; FitsField ra; FitsField dec; FitsField pos_err; FitsField mag; FitsField mag_err; FitsField mag_band; //FitsField classification; FitsField plate_id(4); //FitsField multiple; // Bind the variables to the fields in the table at.bind(0,id); at.bind(1,ra); at.bind(2,dec); at.bind(3,pos_err); at.bind(4,mag); at.bind(5,mag_err); at.bind(6,mag_band); //bt.bind(7,classification); at.bind(7,plate_id); //bt.bind(9,multiple); cout<<"write the first batch of rows one at a time."<::write_hdr() //# ( mixture test between write_hdr() and write_***_hdr()). //# Modified from Copyright (C) 1993,1994,1998,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes # include # include # include # include # include # include int main() { cout << "Test to create a binary table\n"; FitsOutput fout("tfits_ascTbl2.dat",FITS::Disk); if (fout.err() == FitsIO::IOERR) { cout << "Could not open FITS output.\n"; exit(0); } // Create the initial keyword list // We will create an array with 10 rows and 10 columns, as one // would normally do in C. const int row = 10; const int col = 10; FitsLong data[col][row]; // And, we will populate it with data int i, j; for (i = 0; i < col; ++i) for(j = 0; j < row; ++j) data[i][j] = j * 10 + i; // Create the initial keyword list FitsKeywordList st; st.mk(FITS::SIMPLE,True,"Standard FITS format"); st.mk(FITS::BITPIX,32,"Integer data"); st.mk(FITS::NAXIS,2,"This is a primary array"); st.mk(1,FITS::NAXIS,row); st.mk(2,FITS::NAXIS,col); st.mk(FITS::EXTEND,True,"Extension exists"); //st.mk(FITS::EXTEND,False,"Extension exists"); st.spaces(); st.comment("This is test tfits_binTbl1.cc."); st.spaces(); st.end(); // Create and write the initial HDU PrimaryArray hdu1(st); if (hdu1.err()) exit(0); cout << "Initial HDU constructed\n"; // Display the keyword list cout << hdu1; if( !hdu1.write_hdr(fout) ){ cout << "Primary Header wrote ok!"< id; FitsField ra; FitsField dec; FitsField pos_err; FitsField mag; FitsField mag_err; FitsField mag_band; //FitsField classification; FitsField plate_id(4); //FitsField multiple; // Bind the variables to the fields in the table at.bind(0,id); at.bind(1,ra); at.bind(2,dec); at.bind(3,pos_err); at.bind(4,mag); at.bind(5,mag_err); at.bind(6,mag_band); //bt.bind(7,classification); at.bind(7,plate_id); //bt.bind(9,multiple); cout<<"write the first batch of rows one at a time."< # include # include # include # include # include int main() { cout << "Test to create a binary table\n"; FitsOutput fout("tfits_binTbl1.dat",FITS::Disk); if (fout.err() == FitsIO::IOERR) { cout << "Could not open FITS output.\n"; exit(0); } // We will create an array with 3 rows and 6 columns, as one // would normally do in C. const int row = 10; const int col = 10; FitsLong data[col][row]; // And, we will populate it with data int i, j; for (i = 0; i < col; ++i) for(j = 0; j < row; ++j) data[i][j] = j * 10 + i; // Create and write the initial HDU PrimaryArray hdu1; if (hdu1.err()){ cout<<"Error during construction of PrimaryArray."< id; FitsField ra; FitsField dec; FitsField pos_err; FitsField mag; FitsField mag_err; FitsField mag_band; FitsField classification; FitsField plate_id(4); FitsField multiple; // Bind the variables to the fields in the table bt.bind(0,id); bt.bind(1,ra); bt.bind(2,dec); bt.bind(3,pos_err); bt.bind(4,mag); bt.bind(5,mag_err); bt.bind(6,mag_band); bt.bind(7,classification); bt.bind(8,plate_id); bt.bind(9,multiple); // write the first batch of rows one at a time //int i; for (i = 0; i < 25; ++i) { bt.set_next(1); id = i; ra = 79.8185 + i; dec = 0.08173 + i; pos_err = 0.3; mag = 12.19; mag_err = 0.4; mag_band = 1; classification = 3; plate_id(0) = '0'; plate_id(1) = '4'; plate_id(2) = 'Y'; plate_id(3) = 'P'; multiple = 'T'; cout << bt.currrow() << ": " << id << " " << ra << " " << dec << "\n"; bt.write(fout); } // write the remaining rows 5 at a time for (i = 0; i < 5; ++i) { bt.set_next(5); // set_next resets the current row for (int j = 0; j < 5; ++j) { // assign values to the next 5 rows id = i*10 + j; ra = 79.8185 + i*10 + j; dec = 0.08173 + i*10 + j; pos_err = 0.3; mag = 12.19; mag_err = 0.4; mag_band = 1; classification = 3; plate_id(0) = '0'; plate_id(1) = '4'; plate_id(2) = 'Y'; plate_id(3) = 'P'; multiple = 'T'; cout << bt.currrow() << ": " << id << " " << ra << " " << dec << "\n"; ++bt; // advance the current row } bt.write(fout); // write writes all rows designated in set_next } return 0; } casacore-3.7.1/fits/FITS/test/tfits_binTbl2.cc000066400000000000000000000135541476623553700210410ustar00rootroot00000000000000//# tfits_binTbl1.cc: FITS test program to create a binary table by using //# BinaryTableExtension::write_binTbl_hdr(). This program also tests the mixture use of //# write_hdr() and write_binTbl_hdr() methods. //# Modified from Copyright (C) 1993,1994,1998,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes # include # include # include # include # include # include int main() { cout << "Test to create a binary table\n"; FitsOutput fout("tfits_binTbl2.dat",FITS::Disk); if (fout.err() == FitsIO::IOERR) { cout << "Could not open FITS output.\n"; exit(0); } // Create the initial keyword list // We will create an array with 10 rows and 10 columns, as one // would normally do in C. const int row = 10; const int col = 10; FitsLong data[col][row]; // And, we will populate it with data int i, j; for (i = 0; i < col; ++i) for(j = 0; j < row; ++j) data[i][j] = j * 10 + i; // Create the initial keyword list FitsKeywordList st; st.mk(FITS::SIMPLE,True,"Standard FITS format"); st.mk(FITS::BITPIX,32,"Integer data"); st.mk(FITS::NAXIS,2,"This is a primary array"); st.mk(1,FITS::NAXIS,row); st.mk(2,FITS::NAXIS,col); st.mk(FITS::EXTEND,True,"Extension exists"); //st.mk(FITS::EXTEND,False,"Extension exists"); st.spaces(); st.comment("This is test tfits_binTbl1.cc."); st.spaces(); st.end(); // Create and write the initial HDU PrimaryArray hdu1(st); if (hdu1.err()) exit(0); cout << "Initial HDU constructed\n"; // Display the keyword list cout << hdu1; if( !hdu1.write_hdr(fout) ){ cout << "Primary Header wrote ok!"< id; FitsField ra; FitsField dec; FitsField pos_err; FitsField mag; FitsField mag_err; FitsField mag_band; FitsField classification; FitsField plate_id(4); FitsField multiple; // Bind the variables to the fields in the table bt.bind(0,id); bt.bind(1,ra); bt.bind(2,dec); bt.bind(3,pos_err); bt.bind(4,mag); bt.bind(5,mag_err); bt.bind(6,mag_band); bt.bind(7,classification); bt.bind(8,plate_id); bt.bind(9,multiple); // write the first batch of rows one at a time //int i; for (i = 0; i < 25; ++i) { bt.set_next(1); id = i; ra = 79.8185 + i; dec = 0.08173 + i; pos_err = 0.3; mag = 12.19; mag_err = 0.4; mag_band = 1; classification = 3; plate_id(0) = '0'; plate_id(1) = '4'; plate_id(2) = 'Y'; plate_id(3) = 'P'; multiple = 'T'; cout << bt.currrow() << ": " << id << " " << ra << " " << dec << "\n"; bt.write(fout); } // write the remaining rows 5 at a time for (i = 0; i < 5; ++i) { bt.set_next(5); // set_next resets the current row for (int j = 0; j < 5; ++j) { // assign values to the next 5 rows id = i*10 + j; ra = 79.8185 + i*10 + j; dec = 0.08173 + i*10 + j; pos_err = 0.3; mag = 12.19; mag_err = 0.4; mag_band = 1; classification = 3; plate_id(0) = '0'; plate_id(1) = '4'; plate_id(2) = 'Y'; plate_id(3) = 'P'; multiple = 'T'; cout << bt.currrow() << ": " << id << " " << ra << " " << dec << "\n"; ++bt; // advance the current row } bt.write(fout); // write writes all rows designated in set_next } return 0; } casacore-3.7.1/fits/FITS/test/tfits_imgExt2.cc000066400000000000000000000076711476623553700210670ustar00rootroot00000000000000//# tfits_imgExt2.cc: FITS test program to create a primary array and an image extension //# using the PrimaryArray::write_hdr() and ImageExtension::write_imgExt_hdr() //# ( mixture test between write_hdr() and write_***_hdr()). //# Modified from Copyright (C) 1993,1994,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes # include # include # include # include # include # include # include int main() { cout << "Test to create a primary array and an image extension\n"; FitsOutput fout("tfits_imgExt2.dat",FITS::Disk); if (fout.err() == FitsIO::IOERR) { cout << "Could not open FITS output.\n"; exit(0); } // Create the initial keyword list // We will create an array with 10 rows and 10 columns, as one // would normally do in C. const int row = 10; const int col = 10; FitsLong data[col][row]; // And, we will populate it with data int i, j; for (i = 0; i < col; ++i) for(j = 0; j < row; ++j) data[i][j] = j * 10 + i; // Create the initial keyword list FitsKeywordList st; st.mk(FITS::SIMPLE,True,"Standard FITS format"); st.mk(FITS::BITPIX,32,"Integer data"); st.mk(FITS::NAXIS,2,"This is a primary array"); st.mk(1,FITS::NAXIS,row); st.mk(2,FITS::NAXIS,col); st.mk(FITS::EXTEND,True,"Extension exists"); //st.mk(FITS::EXTEND,False,"Extension exists"); st.spaces(); st.comment("This is test tfits_binTbl1.cc."); st.spaces(); st.end(); // Create and write the initial HDU PrimaryArray hdu1(st); if (hdu1.err()) exit(0); cout << "Initial HDU constructed\n"; // Display the keyword list cout << hdu1; if( !hdu1.write_hdr(fout) ){ cout << "Primary Header wrote ok!"< ie; if (ie.err()) exit(0); cout << "ImageExtension constructed\n"; // Display the keyword list cout << ie; // parameters for write_imgExt_hdr() int bitpix=32; int naxis=2; long naxes_img[2]; naxes_img[0] = row; naxes_img[1] = col; if( !ie.write_imgExt_hdr(fout,bitpix, naxis, naxes_img) ){ cout << "ImageExtension wrote header ok!"<< endl; //return 0; }else{ cout<< "ImageExtension wrote header failed!" << endl; exit( 0 ); } ie.set_next(row * col); // setup to write the whole array cout<<"set_next() done." << endl; for (i = 0; i < row; ++i){ for(j = 0; j < col; ++j) { ie.data(i,j) = i * 10 + j; // assign the data //cout<< "ie.data(i,j) = " << ie.data(i,j) << endl; } } cout<< "data(i,j) assignment ok."<< endl; ie.write(fout); // write the data cout << "ImageExtension data wrote!"<< endl; return 0; } casacore-3.7.1/fits/FITS/test/tfits_priGrp.cc000066400000000000000000000064011476623553700210010ustar00rootroot00000000000000//# tfits_priGrp.cc: FITS test program to create a random group by using //# PrimaryGroup::write_priGrp_hdr(). //# Modified form Copyright (C) 1993,1994,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes # include # include # include # include # include # include # include int main() { cout << "Test to create a random group\n"; FitsOutput fout("tfits_priGrp.dat",FITS::Disk); if (fout.err() == FitsIO::IOERR) { cout << "Could not open FITS output.\n"; exit(0); } int naxis = 2; // number of axes + 1. int no_parms = 4; // we will create 4 parms in each group int no_groups = 6; // and 6 groups int no_data = 8; // and 4 data points in each group long naxes[2]; naxes[0]= 0; // must be zero for random group naxes[1] = no_data; // Create a keyword list for a random group /* FitsKeywordList kw; kw.mk(FITS::SIMPLE,True,"Standard FITS format"); kw.mk(FITS::BITPIX,32,"Integer data"); kw.mk(FITS::NAXIS,2); kw.mk(1,FITS::NAXIS,0); kw.mk(2,FITS::NAXIS,no_data); kw.mk(FITS::GROUPS,True,"Random Group structure"); kw.mk(FITS::PCOUNT,no_parms); kw.mk(FITS::GCOUNT,no_groups); kw.spaces(); kw.comment("This is test 3."); kw.spaces(); kw.end(); */ PrimaryGroup pg; if (pg.err()) exit(0); pg.write_priGrp_hdr(fout,True, 32, naxis,naxes, no_parms, no_groups ); cout << "PrimaryGroup constructed\n"; cout << "Data type " << pg.datatype() << "\n" << "Data size " << pg.fitsdatasize() << "\n" << "Dimensions " << pg.dims() << "\n"; for (int n = 0; n < pg.dims(); n++) cout << "Axis " << (n + 1) << " size " << pg.dim(n) << endl; cout << pg << endl; // Display the keyword list int i, j; for (i = 0; i < no_groups; ++i) { for (j = 0; j < no_parms; ++j){ pg.rawparm(j) = (FitsLong)( i * 10 + j); } // assign the parms for (j = 0; j < no_data; ++j) { pg.data(j) = (FitsLong)(i * 10 + j + 5); } // assign the data pg.write(fout); // write the group } cout << "PrimaryGroup data written\n"; //fout.~FitsOutput(); return 0; } casacore-3.7.1/fits/FITS/test/tfitsread_data.cc000066400000000000000000000366301476623553700213120ustar00rootroot00000000000000//# tfitsread_data.cc: FITS test program to read and display values from a FITS file //# by using the FitsInput::read(FITS::HDUType t, char *addr, int nb) method. //# Copyright (C) 1993,1994,1996,1998,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes # include # include # include # include # include # include # include # include // Display basic info and the keyword list void show(HeaderDataUnit *h) { cout << "Data type " << h->datatype() << "\n" << "Data size " << h->fitsdatasize() << "\n" << "Dimensions " << h->dims() << "\n"; for (int n = 0; n < h->dims(); n++) cout << "Axis " << (n + 1) << " size " << h->dim(n) << "\n"; cout << "----- Keyword List -----\n" << *h << "\n"; } // Read the data in a Primary Group and display the first few groups #define DOGROUP(Z) void do_primary_group(PrimaryGroup &x) { \ int i, j; \ int number_to_display = 10; \ show(&x); \ if (x.err() != HeaderDataUnit::OK) { \ cout << "Error occured during construction process -- exiting\n"; \ exit(0); \ } \ for (i = 0; i < x.gcount(); ++i) { \ x.read(); \ if (i < number_to_display) { \ cout << "Group " << i << " parms: " << "\n"; \ for (j = 0; j < x.pcount(); ++j) \ cout << " "<< x.parm(j); \ cout << "\n"; \ cout << "Group " << i << " data: " << "\n"; \ for (j = 0; j < 4; ++j) \ cout << " " << x(j); \ cout << "\n"; \ } \ } \ delete &x; \ } // Read the data in a Primary Array and display the first few data points /* cout<< " The header card images are( testing kwlist_str()): " << endl; \ Vector imageCards = x.kwlist_str(); \ for( int k = 0; k< imageCards.nelements(); k++ ){ \ cout << imageCards[ k ] << endl; \ }\ */ #define DOARRAY(Z) void do_primary_array(PrimaryArray &x) { \ cout << "[ tftis1.cc::do_primary_array() ] called. " << endl; \ int i, j, n0, n1; \ if (x.fitsdatasize()){ \ int ne = x.fitsdatasize(); \ x.read( ne ); \ } \ show(&x); \ if (x.err() != HeaderDataUnit::OK) { \ cout << "Error occured during construction process -- exiting\n"; \ exit(0); \ } \ if (x.dims() == 2) { \ n0 = x.dim(0) > 60 ? 60 : x.dim(0); \ n1 = x.dim(1) > 60 ? 60 : x.dim(1); \ for (i = 0; i < n0; ++i) \ for (j = 0; j < n1; ++j) \ cout << "(" << i << "," << j << ") = " \ << x(i,j) << "\n"; \ } \ delete &x; \ } // now actually make the necessary versions of the above DOGROUP(unsigned char) DOGROUP(short) DOGROUP(FitsLong) DOGROUP(float) DOGROUP(double) DOARRAY(unsigned char) DOARRAY(short) DOARRAY(FitsLong) DOARRAY(float) DOARRAY(double) #undef DOGROUP #undef DOARRAY // Read and display a binary table void do_binary_table(BinaryTableExtension &x) { if (x.err() != 0) { cout << "BT ERROR! " << x.err() << endl; return; } cout << x.nrows() << " rows " << x.ncols() << " cols " << x.fitsdatasize() << " bytes total\n" << endl; cout << "----- Keyword List -----\n" << x << endl; cout << "\nTable Data\n\n"; int i; for (i = 0; i < x.ncols(); ++i) { cout << "Col " << i << ": " << x.field(i).nelements() << " " << x.field(i).fieldtype() << " " << x.ttype(i) << " " << x.tunit(i) << "\n"; } cout << endl; cout <<" [do_binary_table()] read all the table rows." << endl; cout <<" [do_binary_table()] x.nrows() = " << x.nrows() << endl; x.read(x.nrows()); // read all the table rows // any heap to read? char * theheap = 0; if (x.pcount()) { // offset of start of heap from current position, end of last row if (x.notnull(x.theap())) { int heapOffset = x.theap() - x.rowsize()*x.nrows(); // skip to the start of the heap // I don't see any way except to read these bogus bytes char *junk = new char[heapOffset]; x.ExtensionHeaderDataUnit::read(junk, heapOffset); } theheap = new char [x.pcount()]; // this code never checks for alloc errors, why start now x.ExtensionHeaderDataUnit::read(theheap, x.pcount()); } FITS::ValueType *vatypes = new FITS::ValueType[x.ncols()]; void **vaptr = new void *[x.ncols()]; VADescFitsField *va = new VADescFitsField[x.ncols()]; // decode the TFORMs of any VADESC columns for (i=0;i> k%8)); } } break; case FITS::BYTE: { unsigned char *vptr = (unsigned char *)(vaptr[i]); FITS::f2l(vptr, (void *)(theheap + thisva.offset()), thisva.num()); cout << (int)vptr[0]; for (int k=1;k *paB; PrimaryArray *paS; PrimaryArray *paL; PrimaryArray *paF; PrimaryArray *paD; PrimaryGroup *pgB; PrimaryGroup *pgS; PrimaryGroup *pgL; PrimaryGroup *pgF; PrimaryGroup *pgD; BinaryTableExtension *bt; AsciiTableExtension *at; cout << "Test of reading fits files" << endl; if (argc != 2) { cout << "ex1 " << "\n"; exit(0); } cout<<"argv[1]="<(fin); do_primary_array(*paB); break; case FITS::SHORT: paS = new PrimaryArray(fin); do_primary_array(*paS); break; case FITS::LONG: cout<<"About to instantiate PrimaryArray."<< endl; paL = new PrimaryArray(fin); cout<<"About to call do_primary_array( *paL )." << endl; do_primary_array(*paL); cout<<"After called do_primary_array()."<< endl; break; case FITS::FLOAT: cout<<"About to instantiate PrimaryArray."<< endl; paF = new PrimaryArray(fin); cout<<"About to call do_primary_array()"<< endl; do_primary_array(*paF); cout<<"Called do_primary_array()"<< endl; break; case FITS::DOUBLE: paD = new PrimaryArray(fin); do_primary_array(*paD); break; default: break; } cout<<"PrimaryArray appropriated object instantiated."<< endl; break; case FITS::PrimaryGroupHDU: cout << "----- Primary Group -----\n"; switch (fin.datatype()) { case FITS::BYTE: pgB = new PrimaryGroup(fin); do_primary_group(*pgB); break; case FITS::SHORT: pgS = new PrimaryGroup(fin); do_primary_group(*pgS); break; case FITS::LONG: pgL = new PrimaryGroup(fin); do_primary_group(*pgL); break; case FITS::FLOAT: pgF = new PrimaryGroup(fin); do_primary_group(*pgF); break; case FITS::DOUBLE: pgD = new PrimaryGroup(fin); do_primary_group(*pgD); break; default: break; } break; case FITS::AsciiTableHDU: at = new AsciiTableExtension(fin); cout << "\n\nASCII Table "; do_binary_table(*at); break; case FITS::BinaryTableHDU: bt = new BinaryTableExtension(fin); cout << "\n\nBinary Table "; do_binary_table(*bt); break; case FITS::ImageExtensionHDU: cout << "----- Image Extension -----\n"; switch (fin.datatype()) { case FITS::BYTE: paB = new ImageExtension(fin); do_primary_array(*paB); break; case FITS::SHORT: paS = new ImageExtension(fin); do_primary_array(*paS); break; case FITS::LONG: paL = new ImageExtension(fin); do_primary_array(*paL); break; case FITS::FLOAT: paF = new ImageExtension(fin); do_primary_array(*paF); break; case FITS::DOUBLE: paD = new ImageExtension(fin); do_primary_array(*paD); break; default: break; } cout << " Image Extension is passed into do_primary_array()." << endl; break; case FITS::UnknownExtensionHDU: h = new ExtensionHeaderDataUnit(fin); cout << "----- Unknown conforming extension -----\n"; h->skip(); delete h; break; default: cout << "This isn't supposed to happen\n"; break; } }else if (fin.rectype() == FITS::BadBeginningRecord || fin.rectype() == FITS::UnrecognizableRecord) { cout << "Bad Record encountered\n"; exit(0); }else if (fin.rectype() == FITS::SpecialRecord) { cout << "Special Record encountered\n"; exit(0); } if (fin.err()) ++nerrs; } // end of for( nerrs = 0, .... ) loop if (nerrs == NMAXERRS) cout << "Too many errors. Processing terminated.\n"; else cout << "End of Header-Data Units.\n"; cout << endl; cout<< "[tfits1.cc] Before retrun."<< endl; return 0; } casacore-3.7.1/fits/FITS/test/tfitsreader.cc000066400000000000000000000031531476623553700206420ustar00rootroot00000000000000//# tfitsreader.cc: FITS test program to read and list a FITS file //# Copyright (C) 1993,1994,1996,1998,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA # include # include int main(int argc, const char* argv[]) { cout << "Test of reading fits files" << endl; if (argc != 2) { cout << "ex1 " << "\n"; exit(0); } cout << "argv[1]=" << argv[1] << endl; FITSReader fr; fr.listFits(argv[1]); cout << "tfitsreader Before retrun." << endl; return 0; } casacore-3.7.1/fits/FITS/test/tfitsskip.cc000066400000000000000000000372671476623553700203630ustar00rootroot00000000000000//# tfitsskip.cc: FITS test program to skip some data of primary array, //# ( calling the FitsInput::skip())and read and display other values //# from a FITS file. //# Modified from: Copyright (C) 1993,1994,1996,1998,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes # include # include # include # include # include # include # include // Display basic info and the keyword list void show(HeaderDataUnit *h) { cout << "Data type " << h->datatype() << "\n" << "Data size " << h->fitsdatasize() << "\n" << "Dimensions " << h->dims() << "\n"; for (int n = 0; n < h->dims(); n++) cout << "Axis " << (n + 1) << " size " << h->dim(n) << "\n"; cout << "----- Keyword List -----\n" << *h << "\n"; } // Read the data in a Primary Group and display the first few groups #define DOGROUP(Z) void do_primary_group(PrimaryGroup &x) { \ int i, j; \ int number_to_display = 10; \ show(&x); \ if (x.err() != HeaderDataUnit::OK) { \ cout << "Error occured during construction process -- exiting\n"; \ exit(0); \ } \ for (i = 0; i < x.gcount(); ++i) { \ x.read(); \ if (i < number_to_display) { \ cout << "Group " << i << " parms: " << "\n"; \ for (j = 0; j < x.pcount(); ++j) \ cout << " "<< x.parm(j); \ cout << "\n"; \ cout << "Group " << i << " data: " << "\n"; \ for (j = 0; j < 4; ++j) \ cout << " " << x(j); \ cout << "\n"; \ } \ } \ delete &x; \ } // Read the data in a Primary Array and display the first few data points #define DOARRAY(Z) void do_primary_array(PrimaryArray &x) { \ cout << "[ tftis1.cc::do_primary_array() ] called. " << endl; \ int i, j, n0, n1; \ if (x.fitsdatasize()){ \ if( x.hdutype() == FITS::PrimaryArrayHDU ){\ cout<<"[ tftis1.cc::do_primary_array() ] skipping the data unit of primaryarray." << endl; \ x.skip(28); \ x.skip(); \ }else{\ x.read(); \ }\ }\ show(&x); \ if (x.err() != HeaderDataUnit::OK) { \ cout << "Error occured during construction process -- exiting\n"; \ exit(0); \ } \ if (x.dims() == 2) { \ n0 = x.dim(0) > 6 ? 6 : x.dim(0); \ n1 = x.dim(1) > 6 ? 6 : x.dim(1); \ if( x.hdutype() != FITS::PrimaryArrayHDU ){ \ for (i = 0; i < n0; ++i) \ for (j = 0; j < n1; ++j) \ { cout << "(" << i << "," << j << ") = " \ << x(i,j) << "\n"; \ } \ } \ } \ delete &x; \ } // now actually make the necessary versions of the above DOGROUP(unsigned char) DOGROUP(short) DOGROUP(FitsLong) DOGROUP(float) DOGROUP(double) DOARRAY(unsigned char) DOARRAY(short) DOARRAY(FitsLong) DOARRAY(float) DOARRAY(double) #undef DOGROUP #undef DOARRAY // Read and display a binary table void do_binary_table(BinaryTableExtension &x) { if (x.err() != 0) { cout << "BT ERROR! " << x.err() << endl; return; } cout << x.nrows() << " rows " << x.ncols() << " cols " << x.fitsdatasize() << " bytes total\n" << endl; cout << "----- Keyword List -----\n" << x << endl; cout << "\nTable Data\n\n"; int i; for (i = 0; i < x.ncols(); ++i) { cout << "Col " << i << ": " << x.field(i).nelements() << " " << x.field(i).fieldtype() << " " << x.ttype(i) << " " << x.tunit(i) << "\n"; } cout << endl; cout <<" [do_binary_table()] read all the table rows." << endl; cout <<" [do_binary_table()] x.nrows() = " << x.nrows() << endl; x.read(x.nrows()); // read all the table rows // any heap to read? char * theheap = 0; if (x.pcount()) { // offset of start of heap from current position, end of last row if (x.notnull(x.theap())) { int heapOffset = x.theap() - x.rowsize()*x.nrows(); // skip to the start of the heap // I don't see any way except to read these bogus bytes char *junk = new char[heapOffset]; x.ExtensionHeaderDataUnit::read(junk, heapOffset); } theheap = new char [x.pcount()]; // this code never checks for alloc errors, why start now x.ExtensionHeaderDataUnit::read(theheap, x.pcount()); } FITS::ValueType *vatypes = new FITS::ValueType[x.ncols()]; void **vaptr = new void *[x.ncols()]; VADescFitsField *va = new VADescFitsField[x.ncols()]; // decode the TFORMs of any VADESC columns for (i=0;i> k%8)); } } break; case FITS::BYTE: { unsigned char *vptr = (unsigned char *)(vaptr[i]); FITS::f2l(vptr, (void *)(theheap + thisva.offset()), thisva.num()); cout << (int)vptr[0]; for (int k=1;k *paB; PrimaryArray *paS; PrimaryArray *paL; PrimaryArray *paF; PrimaryArray *paD; PrimaryGroup *pgB; PrimaryGroup *pgS; PrimaryGroup *pgL; PrimaryGroup *pgF; PrimaryGroup *pgD; BinaryTableExtension *bt; AsciiTableExtension *at; cout << "Test of reading fits files" << endl; if (argc != 2) { cout << "ex1 " << "\n"; exit(0); } cout<<"argv[1]="<(fin); do_primary_array(*paB); break; case FITS::SHORT: paS = new PrimaryArray(fin); do_primary_array(*paS); break; case FITS::LONG: cout<<"About to instantiate PrimaryArray."<< endl; paL = new PrimaryArray(fin); cout<<"About to call do_primary_array( *paL )." << endl; do_primary_array(*paL); cout<<"After called do_primary_array()."<< endl; break; case FITS::FLOAT: cout<<"About to instantiate PrimaryArray."<< endl; paF = new PrimaryArray(fin); cout<<"About to call do_primary_array()"<< endl; do_primary_array(*paF); cout<<"Called do_primary_array()"<< endl; break; case FITS::DOUBLE: paD = new PrimaryArray(fin); do_primary_array(*paD); break; default: break; } cout<<"PrimaryArray appropriated object instantiated."<< endl; break; case FITS::PrimaryGroupHDU: cout << "----- Primary Group -----\n"; switch (fin.datatype()) { case FITS::BYTE: pgB = new PrimaryGroup(fin); do_primary_group(*pgB); break; case FITS::SHORT: pgS = new PrimaryGroup(fin); do_primary_group(*pgS); break; case FITS::LONG: pgL = new PrimaryGroup(fin); do_primary_group(*pgL); break; case FITS::FLOAT: pgF = new PrimaryGroup(fin); do_primary_group(*pgF); break; case FITS::DOUBLE: pgD = new PrimaryGroup(fin); do_primary_group(*pgD); break; default: break; } break; case FITS::AsciiTableHDU: at = new AsciiTableExtension(fin); cout << "\n\nASCII Table "; do_binary_table(*at); break; case FITS::BinaryTableHDU: bt = new BinaryTableExtension(fin); cout << "\n\nBinary Table "; do_binary_table(*bt); break; case FITS::ImageExtensionHDU: cout << "----- Image Extension -----\n"; switch (fin.datatype()) { case FITS::BYTE: paB = new ImageExtension(fin); do_primary_array(*paB); break; case FITS::SHORT: paS = new ImageExtension(fin); do_primary_array(*paS); break; case FITS::LONG: paL = new ImageExtension(fin); do_primary_array(*paL); break; case FITS::FLOAT: paF = new ImageExtension(fin); do_primary_array(*paF); break; case FITS::DOUBLE: paD = new ImageExtension(fin); do_primary_array(*paD); break; default: break; } cout << " Image Extension is passed into do_primary_array()." << endl; break; case FITS::UnknownExtensionHDU: h = new ExtensionHeaderDataUnit(fin); cout << "----- Unknown conforming extension -----\n"; h->skip(); delete h; break; default: cout << "This isn't supposed to happen\n"; break; } }else if (fin.rectype() == FITS::BadBeginningRecord || fin.rectype() == FITS::UnrecognizableRecord) { cout << "Bad Record encountered\n"; exit(0); }else if (fin.rectype() == FITS::SpecialRecord) { cout << "Special Record encountered\n"; exit(0); } if (fin.err()) ++nerrs; } // end of for( nerrs = 0, .... ) loop if (nerrs == NMAXERRS) cout << "Too many errors. Processing terminated.\n"; else cout << "End of Header-Data Units.\n"; cout << endl; cout<< "[tfits1.cc] End of tfits1.cc."<< endl; return 0; } casacore-3.7.1/fits/FITS/test/tfitsskip_all.cc000066400000000000000000000371671476623553700212120ustar00rootroot00000000000000//# tfitsskip_all.cc: FITS test program to skip the data unit of primary array, //# ( calling the FitsInput::skip_all())and read and display other values //# from a FITS file //# Modified from: Copyright (C) 1993,1994,1996,1998,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes # include # include # include # include # include # include # include // Display basic info and the keyword list void show(HeaderDataUnit *h) { cout << "Data type " << h->datatype() << "\n" << "Data size " << h->fitsdatasize() << "\n" << "Dimensions " << h->dims() << "\n"; for (int n = 0; n < h->dims(); n++) cout << "Axis " << (n + 1) << " size " << h->dim(n) << "\n"; cout << "----- Keyword List -----\n" << *h << "\n"; } // Read the data in a Primary Group and display the first few groups #define DOGROUP(Z) void do_primary_group(PrimaryGroup &x) { \ int i, j; \ int number_to_display = 10; \ show(&x); \ if (x.err() != HeaderDataUnit::OK) { \ cout << "Error occured during construction process -- exiting\n"; \ exit(0); \ } \ for (i = 0; i < x.gcount(); ++i) { \ x.read(); \ if (i < number_to_display) { \ cout << "Group " << i << " parms: " << "\n"; \ for (j = 0; j < x.pcount(); ++j) \ cout << " "<< x.parm(j); \ cout << "\n"; \ cout << "Group " << i << " data: " << "\n"; \ for (j = 0; j < 4; ++j) \ cout << " " << x(j); \ cout << "\n"; \ } \ } \ delete &x; \ } // Read the data in a Primary Array and display the first few data points #define DOARRAY(Z) void do_primary_array(PrimaryArray &x) { \ cout << "[ tftis1.cc::do_primary_array() ] called. " << endl; \ int i, j, n0, n1; \ if (x.fitsdatasize()){ \ if( x.hdutype() == FITS::PrimaryArrayHDU ){\ cout<<"[ tftis1.cc::do_primary_array() ] skipping the data unit of primaryarray." << endl; \ x.skip(); \ }else{\ x.read(); \ }\ }\ show(&x); \ if (x.err() != HeaderDataUnit::OK) { \ cout << "Error occured during construction process -- exiting\n"; \ exit(0); \ } \ if (x.dims() == 2) { \ n0 = x.dim(0) > 6 ? 6 : x.dim(0); \ n1 = x.dim(1) > 6 ? 6 : x.dim(1); \ if( x.hdutype() != FITS::PrimaryArrayHDU ){ \ for (i = 0; i < n0; ++i) \ for (j = 0; j < n1; ++j) \ { cout << "(" << i << "," << j << ") = " \ << x(i,j) << "\n"; \ } \ } \ } \ delete &x; \ } // now actually make the necessary versions of the above DOGROUP(unsigned char) DOGROUP(short) DOGROUP(FitsLong) DOGROUP(float) DOGROUP(double) DOARRAY(unsigned char) DOARRAY(short) DOARRAY(FitsLong) DOARRAY(float) DOARRAY(double) #undef DOGROUP #undef DOARRAY // Read and display a binary table void do_binary_table(BinaryTableExtension &x) { if (x.err() != 0) { cout << "BT ERROR! " << x.err() << endl; return; } cout << x.nrows() << " rows " << x.ncols() << " cols " << x.fitsdatasize() << " bytes total\n" << endl; cout << "----- Keyword List -----\n" << x << endl; cout << "\nTable Data\n\n"; int i; for (i = 0; i < x.ncols(); ++i) { cout << "Col " << i << ": " << x.field(i).nelements() << " " << x.field(i).fieldtype() << " " << x.ttype(i) << " " << x.tunit(i) << "\n"; } cout << endl; cout <<" [do_binary_table()] read all the table rows." << endl; cout <<" [do_binary_table()] x.nrows() = " << x.nrows() << endl; x.read(x.nrows()); // read all the table rows // any heap to read? char * theheap = 0; if (x.pcount()) { // offset of start of heap from current position, end of last row if (x.notnull(x.theap())) { int heapOffset = x.theap() - x.rowsize()*x.nrows(); // skip to the start of the heap // I don't see any way except to read these bogus bytes char *junk = new char[heapOffset]; x.ExtensionHeaderDataUnit::read(junk, heapOffset); } theheap = new char [x.pcount()]; // this code never checks for alloc errors, why start now x.ExtensionHeaderDataUnit::read(theheap, x.pcount()); } FITS::ValueType *vatypes = new FITS::ValueType[x.ncols()]; void **vaptr = new void *[x.ncols()]; VADescFitsField *va = new VADescFitsField[x.ncols()]; // decode the TFORMs of any VADESC columns for (i=0;i> k%8)); } } break; case FITS::BYTE: { unsigned char *vptr = (unsigned char *)(vaptr[i]); FITS::f2l(vptr, (void *)(theheap + thisva.offset()), thisva.num()); cout << (int)vptr[0]; for (int k=1;k *paB; PrimaryArray *paS; PrimaryArray *paL; PrimaryArray *paF; PrimaryArray *paD; PrimaryGroup *pgB; PrimaryGroup *pgS; PrimaryGroup *pgL; PrimaryGroup *pgF; PrimaryGroup *pgD; BinaryTableExtension *bt; AsciiTableExtension *at; cout << "Test of reading fits files" << endl; if (argc != 2) { cout << "ex1 " << "\n"; exit(0); } cout<<"argv[1]="<(fin); do_primary_array(*paB); break; case FITS::SHORT: paS = new PrimaryArray(fin); do_primary_array(*paS); break; case FITS::LONG: cout<<"About to instantiate PrimaryArray."<< endl; paL = new PrimaryArray(fin); cout<<"About to call do_primary_array( *paL )." << endl; do_primary_array(*paL); cout<<"After called do_primary_array()."<< endl; break; case FITS::FLOAT: cout<<"About to instantiate PrimaryArray."<< endl; paF = new PrimaryArray(fin); cout<<"About to call do_primary_array()"<< endl; do_primary_array(*paF); cout<<"Called do_primary_array()"<< endl; break; case FITS::DOUBLE: paD = new PrimaryArray(fin); do_primary_array(*paD); break; default: break; } cout<<"PrimaryArray appropriated object instantiated."<< endl; break; case FITS::PrimaryGroupHDU: cout << "----- Primary Group -----\n"; switch (fin.datatype()) { case FITS::BYTE: pgB = new PrimaryGroup(fin); do_primary_group(*pgB); break; case FITS::SHORT: pgS = new PrimaryGroup(fin); do_primary_group(*pgS); break; case FITS::LONG: pgL = new PrimaryGroup(fin); do_primary_group(*pgL); break; case FITS::FLOAT: pgF = new PrimaryGroup(fin); do_primary_group(*pgF); break; case FITS::DOUBLE: pgD = new PrimaryGroup(fin); do_primary_group(*pgD); break; default: break; } break; case FITS::AsciiTableHDU: at = new AsciiTableExtension(fin); cout << "\n\nASCII Table "; do_binary_table(*at); break; case FITS::BinaryTableHDU: bt = new BinaryTableExtension(fin); cout << "\n\nBinary Table "; do_binary_table(*bt); break; case FITS::ImageExtensionHDU: cout << "----- Image Extension -----\n"; switch (fin.datatype()) { case FITS::BYTE: paB = new ImageExtension(fin); do_primary_array(*paB); break; case FITS::SHORT: paS = new ImageExtension(fin); do_primary_array(*paS); break; case FITS::LONG: paL = new ImageExtension(fin); do_primary_array(*paL); break; case FITS::FLOAT: paF = new ImageExtension(fin); do_primary_array(*paF); break; case FITS::DOUBLE: paD = new ImageExtension(fin); do_primary_array(*paD); break; default: break; } cout << " Image Extension is passed into do_primary_array()." << endl; break; case FITS::UnknownExtensionHDU: h = new ExtensionHeaderDataUnit(fin); cout << "----- Unknown conforming extension -----\n"; h->skip(); delete h; break; default: cout << "This isn't supposed to happen\n"; break; } }else if (fin.rectype() == FITS::BadBeginningRecord || fin.rectype() == FITS::UnrecognizableRecord) { cout << "Bad Record encountered\n"; exit(0); }else if (fin.rectype() == FITS::SpecialRecord) { cout << "Special Record encountered\n"; exit(0); } if (fin.err()) ++nerrs; } // end of for( nerrs = 0, .... ) loop if (nerrs == NMAXERRS) cout << "Too many errors. Processing terminated.\n"; else cout << "End of Header-Data Units.\n"; cout << endl; cout<< "[tfits1.cc] End of tfits1.cc."<< endl; return 0; } casacore-3.7.1/fits/FITS/test/tfitsskip_hdu.cc000066400000000000000000000362751476623553700212210ustar00rootroot00000000000000//# tfitsskip_hdu.cc: FITS test program to skip an entire hdu(primary array here ) //# and read and display values from a FITS file //# modified from Copyright (C) 1993,1994,1996,1998,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes # include # include # include # include # include # include # include // Display basic info and the keyword list void show(HeaderDataUnit *h) { cout << "Data type " << h->datatype() << "\n" << "Data size " << h->fitsdatasize() << "\n" << "Dimensions " << h->dims() << "\n"; for (int n = 0; n < h->dims(); n++) cout << "Axis " << (n + 1) << " size " << h->dim(n) << "\n"; cout << "----- Keyword List -----\n" << *h << "\n"; } // Read the data in a Primary Group and display the first few groups #define DOGROUP(Z) void do_primary_group(PrimaryGroup &x) { \ int i, j; \ int number_to_display = 10; \ show(&x); \ if (x.err() != HeaderDataUnit::OK) { \ cout << "Error occured during construction process -- exiting\n"; \ exit(0); \ } \ for (i = 0; i < x.gcount(); ++i) { \ x.read(); \ if (i < number_to_display) { \ cout << "Group " << i << " parms: " << "\n"; \ for (j = 0; j < x.pcount(); ++j) \ cout << " "<< x.parm(j); \ cout << "\n"; \ cout << "Group " << i << " data: " << "\n"; \ for (j = 0; j < 4; ++j) \ cout << " " << x(j); \ cout << "\n"; \ } \ } \ delete &x; \ } // Read the data in a Primary Array and display the first few data points #define DOARRAY(Z) void do_primary_array(PrimaryArray &x) { \ cout << "[ tftis1.cc::do_primary_array() ] called. " << endl; \ int i, j, n0, n1; \ if (x.fitsdatasize()) \ x.read(); \ show(&x); \ if (x.err() != HeaderDataUnit::OK) { \ cout << "Error occured during construction process -- exiting\n"; \ exit(0); \ } \ if (x.dims() == 2) { \ n0 = x.dim(0) > 6 ? 6 : x.dim(0); \ n1 = x.dim(1) > 6 ? 6 : x.dim(1); \ for (i = 0; i < n0; ++i) \ for (j = 0; j < n1; ++j) \ cout << "(" << i << "," << j << ") = " \ << x(i,j) << "\n"; \ } \ delete &x; \ } // now actually make the necessary versions of the above DOGROUP(unsigned char) DOGROUP(short) DOGROUP(FitsLong) DOGROUP(float) DOGROUP(double) DOARRAY(unsigned char) DOARRAY(short) DOARRAY(FitsLong) DOARRAY(float) DOARRAY(double) #undef DOGROUP #undef DOARRAY // Read and display a binary table void do_binary_table(BinaryTableExtension &x) { if (x.err() != 0) { cout << "BT ERROR! " << x.err() << endl; return; } cout << x.nrows() << " rows " << x.ncols() << " cols " << x.fitsdatasize() << " bytes total\n" << endl; cout << "----- Keyword List -----\n" << x << endl; cout << "\nTable Data\n\n"; int i; for (i = 0; i < x.ncols(); ++i) { cout << "Col " << i << ": " << x.field(i).nelements() << " " << x.field(i).fieldtype() << " " << x.ttype(i) << " " << x.tunit(i) << "\n"; } cout << endl; cout <<" [do_binary_table()] read all the table rows." << endl; cout <<" [do_binary_table()] x.nrows() = " << x.nrows() << endl; x.read(x.nrows()); // read all the table rows // any heap to read? char * theheap = 0; if (x.pcount()) { // offset of start of heap from current position, end of last row if (x.notnull(x.theap())) { int heapOffset = x.theap() - x.rowsize()*x.nrows(); // skip to the start of the heap // I don't see any way except to read these bogus bytes char *junk = new char[heapOffset]; x.ExtensionHeaderDataUnit::read(junk, heapOffset); } theheap = new char [x.pcount()]; // this code never checks for alloc errors, why start now x.ExtensionHeaderDataUnit::read(theheap, x.pcount()); } FITS::ValueType *vatypes = new FITS::ValueType[x.ncols()]; void **vaptr = new void *[x.ncols()]; VADescFitsField *va = new VADescFitsField[x.ncols()]; // decode the TFORMs of any VADESC columns for (i=0;i> k%8)); } } break; case FITS::BYTE: { unsigned char *vptr = (unsigned char *)(vaptr[i]); FITS::f2l(vptr, (void *)(theheap + thisva.offset()), thisva.num()); cout << (int)vptr[0]; for (int k=1;k *paB; PrimaryArray *paS; PrimaryArray *paL; PrimaryArray *paF; PrimaryArray *paD; PrimaryGroup *pgB; PrimaryGroup *pgS; PrimaryGroup *pgL; PrimaryGroup *pgF; PrimaryGroup *pgD; BinaryTableExtension *bt; AsciiTableExtension *at; cout << "Test of reading fits files" << endl; if (argc != 2) { cout << "ex1 " << "\n"; exit(0); } cout<<"argv[1]="<(fin); do_primary_array(*paB); break; case FITS::SHORT: paS = new PrimaryArray(fin); do_primary_array(*paS); break; case FITS::LONG: cout<<"About to instantiate PrimaryArray."<< endl; paL = new PrimaryArray(fin); cout<<"About to call do_primary_array( *paL )." << endl; do_primary_array(*paL); cout<<"After called do_primary_array()."<< endl; break; case FITS::FLOAT: cout<<"About to instantiate PrimaryArray."<< endl; paF = new PrimaryArray(fin); cout<<"About to call do_primary_array()"<< endl; do_primary_array(*paF); cout<<"Called do_primary_array()"<< endl; break; case FITS::DOUBLE: paD = new PrimaryArray(fin); do_primary_array(*paD); break; default: break; } cout<<"PrimaryArray appropriated object instantiated."<< endl; break; case FITS::PrimaryGroupHDU: cout << "----- Primary Group -----\n"; switch (fin.datatype()) { case FITS::BYTE: pgB = new PrimaryGroup(fin); do_primary_group(*pgB); break; case FITS::SHORT: pgS = new PrimaryGroup(fin); do_primary_group(*pgS); break; case FITS::LONG: pgL = new PrimaryGroup(fin); do_primary_group(*pgL); break; case FITS::FLOAT: pgF = new PrimaryGroup(fin); do_primary_group(*pgF); break; case FITS::DOUBLE: pgD = new PrimaryGroup(fin); do_primary_group(*pgD); break; default: break; } break; case FITS::AsciiTableHDU: at = new AsciiTableExtension(fin); cout << "\n\nASCII Table "; do_binary_table(*at); break; case FITS::BinaryTableHDU: bt = new BinaryTableExtension(fin); cout << "\n\nBinary Table "; do_binary_table(*bt); break; case FITS::ImageExtensionHDU: cout << "----- Image Extension -----\n"; switch (fin.datatype()) { case FITS::BYTE: paB = new ImageExtension(fin); do_primary_array(*paB); break; case FITS::SHORT: paS = new ImageExtension(fin); do_primary_array(*paS); break; case FITS::LONG: paL = new ImageExtension(fin); do_primary_array(*paL); break; case FITS::FLOAT: paF = new ImageExtension(fin); do_primary_array(*paF); break; case FITS::DOUBLE: paD = new ImageExtension(fin); do_primary_array(*paD); break; default: break; } cout << " Image Extension is passed into do_primary_array()." << endl; break; case FITS::UnknownExtensionHDU: h = new ExtensionHeaderDataUnit(fin); cout << "----- Unknown conforming extension -----\n"; h->skip(); delete h; break; default: cout << "This isn't supposed to happen\n"; break; } }else if (fin.rectype() == FITS::BadBeginningRecord || fin.rectype() == FITS::UnrecognizableRecord) { cout << "Bad Record encountered\n"; exit(0); }else if (fin.rectype() == FITS::SpecialRecord) { cout << "Special Record encountered\n"; exit(0); } if (fin.err()) ++nerrs; } // end of for( nerrs = 0, .... ) loop if (nerrs == NMAXERRS) cout << "Too many errors. Processing terminated.\n"; else cout << "End of Header-Data Units.\n"; cout << endl; cout<< "[tfits1.cc] Before retrun."<< endl; return 0; } casacore-3.7.1/fits/apps/000077500000000000000000000000001476623553700152345ustar00rootroot00000000000000casacore-3.7.1/fits/apps/CMakeLists.txt000066400000000000000000000002611476623553700177730ustar00rootroot00000000000000add_executable (fits2table fits2table/fits2table.cc) add_pch_support(fits2table) target_link_libraries (fits2table casa_fits ${CASACORE_ARCH_LIBS}) install(TARGETS fits2table) casacore-3.7.1/fits/apps/fits2table/000077500000000000000000000000001476623553700172735ustar00rootroot00000000000000casacore-3.7.1/fits/apps/fits2table/fits2table.cc000066400000000000000000000105601476623553700216430ustar00rootroot00000000000000//# fits2table - convert a FITS file into an casacore table //# Copyright (C) 1995,1996,1997,1999,2000,2001,2007 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include int main(int argc, const char* argv[]) { try { Input inputs(1); inputs.create("input", "", "The input FITS file", "String"); inputs.create("output", "", "The output Casacore Table name", "String"); inputs.create("which_hdu", "1", "0-relative to primary HDU, i.e. 1 is the smallest valid value.", "Integer"); inputs.create("storage", "standard", "The storage manager to use - standard or incremental " "(memory)", "String"); inputs.create("sdfits", "True", "Interpret keywords as virtual columns as in the SD-FITS convention", "Bool"); inputs.readArguments(argc, argv); String inputFilename = inputs.getString("input"); String outputFilename = inputs.getString("output"); String storageManagerType = inputs.getString("storage"); Int whichHDU = inputs.getInt("which_hdu"); Bool sdfits = inputs.getBool("sdfits"); storageManagerType.downcase(); Bool useIncrSM; if (storageManagerType == "incremental") { useIncrSM = True; } else if (storageManagerType == "standard") { useIncrSM = False; } else { cerr << storageManagerType << " is not a valid storage manager" << endl; return 1; } if (whichHDU < 1) { cerr << "whichHDU is not valid, must be >= 1" << endl; return 1; } File inputFile(inputFilename); if (! inputFile.isReadable()) { cerr << inputFilename << " is not readable - exiting" << endl; return 1; } // construct the FITS table of the appropriate type FITSTable *infits = 0; if (sdfits) { infits = new SDFITSTable(inputFilename, whichHDU); } else { infits = new FITSTable(inputFilename, whichHDU); } AlwaysAssert(infits, AipsError); if (!infits->isValid()) { cerr << "The indicated FITS file does not have a valid binary table at HDU=" << whichHDU << endl; return 1; } TableDesc td(FITSTabular::tableDesc(*infits)); // if sdfits, remove any TDIM columns from td, FITSTable takes care of interpreting them // and if sdfits is true, that most likely means we don't want to see them if (sdfits) { Vector cols(td.columnNames()); for (uInt i=0;inrow()); TableRow row(tab); uInt rownr = 0; while (rownr < tab.nrow()) { row.putMatchingFields(rownr, TableRecord(infits->currentRow())); infits->next(); rownr++; } cout << "done." << endl; } catch (std::exception& x) { cout << "Exception: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/fits/fits.dox000066400000000000000000000026421476623553700157560ustar00rootroot00000000000000//# fits.dox: doxygen description of fits package //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ // \defgroup fits fits package (libcasa_fits) // // The fits package contains high level interfaces to CFitsIO //
          //
        • FITS: // Interface to CFitsIO //
        casacore-3.7.1/images/000077500000000000000000000000001476623553700145715ustar00rootroot00000000000000casacore-3.7.1/images/CMakeLists.txt000066400000000000000000000100571476623553700173340ustar00rootroot00000000000000# # CASA Images # if (BISON_VERSION VERSION_LESS 3.0) BISON_TARGET (ImageExprGram Images/ImageExprGram.yy ${CMAKE_CURRENT_BINARY_DIR}/ImageExprGram.ycc COMPILE_FLAGS "-p ImageExprGram") else() BISON_TARGET (ImageExprGram Images/ImageExprGram.yy ${CMAKE_CURRENT_BINARY_DIR}/ImageExprGram.ycc COMPILE_FLAGS "-Dapi.prefix={ImageExprGram} --warnings=no-yacc") endif() FLEX_TARGET (ImageExprGram Images/ImageExprGram.ll ${CMAKE_CURRENT_BINARY_DIR}/ImageExprGram.lcc COMPILE_FLAGS "-PImageExprGram") include_directories (${CMAKE_CURRENT_BINARY_DIR}) add_library (casa_images Regions/WCEllipsoid.cc Regions/RegionManager.cc Regions/WCLELMask.cc Regions/WCPolygon.cc Regions/WCUnion.cc Regions/WCRegion2.cc Regions/RegionHandlerMemory.cc Regions/WCExtension.cc Regions/RegionHandlerTable.cc Regions/WCCompound.cc Regions/WCRegion.cc Regions/WCIntersection.cc Regions/WCConcatenation.cc Regions/ImageRegion.cc Regions/RFReaderWriter.cc Regions/RegionHandler.cc Regions/WCDifference.cc Regions/WCBox.cc Regions/AipsIOReaderWriter.cc Regions/WCComplement.cc Regions/RegionHandlerHDF5.cc Images/FITSErrorImage.cc Images/FITSImage.cc Images/FITSImgParser.cc Images/FITSQualityImage.cc Images/FITSQualityMask.cc Images/HDF5Image2.cc Images/Image_tmpl.cc Images/ImageAttrGroup.cc Images/ImageAttrGroupCasa.cc Images/ImageAttrGroupHDF5.cc Images/ImageAttrHandler.cc Images/ImageAttrHandlerCasa.cc Images/ImageAttrHandlerHDF5.cc Images/ImageBeamSet.cc Images/ImageExprGram.cc Images/ImageExprParse.cc Images/ImageFITS2Converter.cc Images/ImageInfo.cc Images/ImageOpener.cc Images/ImageProxy.cc Images/ImageUtilities.cc Images/LELImageCoord.cc Images/MIRIADImage.cc Images/MaskSpecifier.cc Images/PagedImage2.cc ${BISON_ImageExprGram_OUTPUTS} ${FLEX_ImageExprGram_OUTPUTS} ) set(top_level_headers Regions.h Images.h ) init_pch_support(casa_images ${top_level_headers}) target_link_libraries ( casa_images casa_coordinates casa_mirlib casa_lattices ${CASACORE_ARCH_LIBS} ) add_subdirectory (apps) install ( TARGETS casa_images LIBRARY DESTINATION lib${LIB_SUFFIX} ARCHIVE DESTINATION lib${LIB_SUFFIX} LIBRARY PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) install (FILES Images/CurvedImage2D.h Images/CurvedImage2D.tcc Images/ExtendImage.h Images/ExtendImage.tcc Images/FITS2Image.tcc Images/FITSErrorImage.h Images/FITSImage.h Images/FITSImgParser.h Images/FITSQualityImage.h Images/FITSQualityMask.h Images/HDF5Image.h Images/HDF5Image.tcc Images/ImageAttrGroup.h Images/ImageAttrGroupCasa.h Images/ImageAttrGroupHDF5.h Images/ImageAttrHandler.h Images/ImageAttrHandlerCasa.h Images/ImageAttrHandlerHDF5.h Images/ImageBeamSet.h Images/ImageConcat.h Images/ImageConcat.tcc Images/ImageExpr.h Images/ImageExpr.tcc Images/ImageExprGram.h Images/ImageExprParse.h Images/ImageFITSConverter.h Images/ImageFITSConverter.tcc Images/ImageInfo.h Images/ImageInterface.h Images/ImageInterface.tcc Images/ImageOpener.h Images/ImageProxy.h Images/ImageRegrid.h Images/ImageRegrid.tcc Images/ImageStatistics.h Images/ImageStatistics.tcc Images/ImageSummary.h Images/ImageSummary.tcc Images/ImageUtilities.h Images/ImageUtilities2.tcc Images/LELImageCoord.h Images/MIRIADImage.h Images/MaskSpecifier.h Images/PagedImage.h Images/PagedImage.tcc Images/RebinImage.h Images/RebinImage.tcc Images/SubImage.h Images/SubImage.tcc Images/TempImage.h Images/TempImage.tcc DESTINATION include/casacore/images/Images ) install (FILES Regions/AipsIOReaderWriter.h Regions/ImageRegion.h Regions/RegionHandler.h Regions/RegionHandlerHDF5.h Regions/RegionHandlerMemory.h Regions/RegionHandlerTable.h Regions/RegionManager.h Regions/RFReaderWriter.h Regions/WCBox.h Regions/WCComplement.h Regions/WCCompound.h Regions/WCConcatenation.h Regions/WCDifference.h Regions/WCEllipsoid.h Regions/WCExtension.h Regions/WCIntersection.h Regions/WCLELMask.h Regions/WCPolygon.h Regions/WCRegion.h Regions/WCUnion.h DESTINATION include/casacore/images/Regions ) install (FILES ${top_level_headers} DESTINATION include/casacore/images ) add_subdirectory (Images/test ${EXCL_ALL}) add_subdirectory (Regions/test ${EXCL_ALL}) casacore-3.7.1/images/Images.h000066400000000000000000000062421476623553700161530ustar00rootroot00000000000000//# Images.h: N-dimensional astronomical images //# Copyright (C) 1998,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_IMAGES_H #define IMAGES_IMAGES_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //#include //#include //#include //#include //#include //#include //#include //#include //#include //#include //#include //#include //#include //#include //#include //#include //#include //#include //#include //#include //#include // // // N-dimensional astronomical images // // //
      • Programmers of new Image classes should understand Inheritance //
      • Users of the Image classes should understand Polymorphism. //
      • class Lattice //
      • class CoordinateSystem // // // // // // // // // //# //# // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Images/000077500000000000000000000000001476623553700157765ustar00rootroot00000000000000casacore-3.7.1/images/Images/CurvedImage2D.h000066400000000000000000000220571476623553700205360ustar00rootroot00000000000000//# CurvedImage2D.h: An image crosscut based on a curve in a plane //# Copyright (C) 2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_CURVEDIMAGE2D_H #define IMAGES_CURVEDIMAGE2D_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class CurvedLattice2D; template class CLInterpolator2D; class PixelCurve1D; // // An image crosscut based on a curve in a plane. // // // // // // // // //
      • ImageInterface //
      • CurvedLattice2D // // // // Class CurvedImage2D can be used to make a crosscut through an image // with a dimensionality >= 2. The dimensionality of the resulting image // is one less. // The crosscut is based on a curve defined by a // PixelCurve1D object. The curve // can be any 1-dim function (e.g. straight line, spline) // supported by the Functionals module. The curve must be in one of the // main planes of the image as defined by the axes arguments in the // constructor. //
        For example: in an RA-DEC-FREQ image a straight line can be // defined in the RA-DEC plane (axis1=0, axis2=1) from blc {0,0) to // trc (511,511). The crosscut will follow this line, so the result is // a 2-dim image with axes 'line' and FREQ. So it contains the spectrum // for all points on the line (points (0,0), (1,1) ... (511,511)). //
        In this example the line only contains exact grid points. In // practice that usually won't be case, so interpolation has to be done. // This is done by a class derived from // CLInterpolator2D, so any // interpolation scheme is possible. Currently only the nearest neighbour // scheme is implemented (CLIPNearest2D). //
        // // // The following example uses a 3-dim image. // It makes a crosscut using a line from the blc to the trc in the XY plane. // The number of points on the line is the maximum of the number of points // in X and Y. // // // Open an image. // PagedImage image("name.img"); // // Make a straight line from (0,0) to the trc. // IPosition shp = lat.shape(); // Int xtop = shp(0); // Int ytop = shp(1); // Int nr = xtop; // if (nr > ytop) nr = ytop; // PixelCurve1D pc(0, 0, xtop-1, ytop-1, nr); // // Create the crosscut image. // // The new axis (the curve axis) is the first axis in the result. // CurvedImage2D clat(image, CLIPNearest2D(), pc, 0, 1, 0); // // Note that in the general case the line (or any curve) won't be from // the blc to the trc. In fact, it is possible to give any starting and // end point and any number of points on the curve. // // // // Users like to view arbitrary image crosscuts. // // //# //# template class CurvedImage2D: public ImageInterface { public: // The default constructor CurvedImage2D(); // Take a curved slice from the given image. // The PixelCurve1D object defines // the curve in one of the planes of the image. The arguments axis1 // and axis2 define the plane the curve is in. // The CLInterpolator2D object // defines the interpolation scheme for pixels that are not on grid points. // An example is CLIPNearest2D which takes the nearest neighbour. // The dimensionality of the CurvedImage2D is one less than the // dimensionality of the given image. Two axes (axis1 and axis2) are // replaced by the new axis representing the curve. The argument // curveAxis defines the axis number of the new axis. It defaults to the // last axis. // An exception is thrown if the dimensionality of the input image is < 2 // or if the given axes numbers are too high. // Note that the output CoordinateSystem of the CurvedImage is just a dummy // LinearCoordinate at this point. The values are all arbitrary. CurvedImage2D (const ImageInterface&, const CLInterpolator2D&, const PixelCurve1D&, uInt axis1, uInt axis2, Int curveAxis=-1); // Copy constructor (reference semantics). CurvedImage2D (const CurvedImage2D& other); virtual ~CurvedImage2D(); // Assignment (reference semantics). CurvedImage2D& operator= (const CurvedImage2D& other); // Make a copy of the object (reference semantics). // virtual ImageInterface* cloneII() const; // // Get the image type (returns name of derived class). virtual String imageType() const; // Is the CurvedImage2D masked? // It is if its parent image is masked. virtual Bool isMasked() const; // Does the image object have a pixelmask? // It does if its parent has a pixelmask. virtual Bool hasPixelMask() const; // Get access to the pixelmask in use (thus to the pixelmask of the parent). // An exception is thrown if the parent does not have a pixelmask. // virtual const Lattice& pixelMask() const; virtual Lattice& pixelMask(); // // Get the region used (always returns 0). virtual const LatticeRegion* getRegionPtr() const; // A CurvedImage2D is not persistent. virtual Bool isPersistent() const; // Is the CurvedImage2D paged to disk? virtual Bool isPaged() const; // An CurvedImage2D is not writable virtual Bool isWritable() const; // Returns the shape of the CurvedImage2D virtual IPosition shape() const; // This function returns the recommended maximum number of pixels to // include in the cursor of an iterator. virtual uInt advisedMaxPixels() const; // Function which changes the shape of the CurvedImage2D. // Throws an exception as resizing an CurvedImage2D is not possible. virtual void resize(const TiledShape& newShape); // Return the name of the parent ImageInterface object. virtual String name (Bool stripPath=False) const; // Check class invariants. virtual Bool ok() const; // Get access to the attribute handler (of the parent image). // If a handler keyword does not exist yet, it is created if // createHandler is set. // Otherwise the handler is empty and no groups can be created for it. virtual ImageAttrHandler& attrHandler (Bool createHandler=False); // Do the actual getting of an array of values. virtual Bool doGetSlice (Array& buffer, const Slicer& section); // Putting data is not possible. virtual void doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride); // Get a section of the mask. virtual Bool doGetMaskSlice (Array& buffer, const Slicer& section); // This function is used by the LatticeIterator class to generate an // iterator of the correct type for this Lattice. Not recommended // for general use. virtual LatticeIterInterface* makeIter (const LatticeNavigator& navigator, Bool useRef) const; // Get the best cursor shape. virtual IPosition doNiceCursorShape (uInt maxPixels) const; // Handle the (un)locking and syncing, etc. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; virtual void resync(); virtual void flush(); virtual void tempClose(); virtual void reopen(); // private: //# itsImagePtr points to the parent image. ImageInterface* itsImagePtr; CurvedLattice2D* itsCurLatPtr; //# Make members of parent class known. public: using ImageInterface::logger; protected: using ImageInterface::setCoordsMember; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/images/Images/CurvedImage2D.tcc000066400000000000000000000155311476623553700210570ustar00rootroot00000000000000//# CurvedImage2D.cc: An image crosscut based on a curve in a plane //# Copyright (C) 2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_CURVEDIMAGE2D_TCC #define IMAGES_CURVEDIMAGE2D_TCC #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template CurvedImage2D::CurvedImage2D() : itsImagePtr (0), itsCurLatPtr (0) {} template CurvedImage2D::CurvedImage2D (const ImageInterface& image, const CLInterpolator2D& interpolator, const PixelCurve1D& curve, uInt axis1, uInt axis2, Int curveAxis) : itsImagePtr (image.cloneII()) { itsCurLatPtr = new CurvedLattice2D (image, interpolator, curve, axis1, axis2, curveAxis); // Currently the output CS is an arbitrary Linear system // A correct CS needs to be set. Probably some new Coordinates // need to be derived to do this. CoordinateSystem cSysOut; LinearCoordinate c(itsCurLatPtr->ndim()); cSysOut.addCoordinate(c); cSysOut.setObsInfo(itsImagePtr->coordinates().obsInfo()); // setCoordsMember (cSysOut); setImageInfoMember (itsImagePtr->imageInfo()); setMiscInfoMember (itsImagePtr->miscInfo()); setUnitMember (itsImagePtr->units()); logger().addParent (itsImagePtr->logger()); } template CurvedImage2D::CurvedImage2D (const CurvedImage2D& other) : ImageInterface (other), itsImagePtr (other.itsImagePtr->cloneII()) { itsCurLatPtr = new CurvedLattice2D (*other.itsCurLatPtr); } template CurvedImage2D::~CurvedImage2D() { delete itsImagePtr; delete itsCurLatPtr; } template CurvedImage2D& CurvedImage2D::operator= (const CurvedImage2D& other) { if (this != &other) { ImageInterface::operator= (other); delete itsImagePtr; itsImagePtr = other.itsImagePtr->cloneII(); delete itsCurLatPtr; itsCurLatPtr = new CurvedLattice2D (*other.itsCurLatPtr); } return *this; } template ImageInterface* CurvedImage2D::cloneII() const { return new CurvedImage2D (*this); } template String CurvedImage2D::imageType() const { return "CurvedImage2D"; } template Bool CurvedImage2D::ok() const { return itsCurLatPtr->ok(); } template Bool CurvedImage2D::isMasked() const { return itsCurLatPtr->isMasked(); } template Bool CurvedImage2D::isPersistent() const { return itsCurLatPtr->isPersistent(); } template Bool CurvedImage2D::isPaged() const { return itsCurLatPtr->isPaged(); } template Bool CurvedImage2D::isWritable() const { return itsCurLatPtr->isWritable(); } template Bool CurvedImage2D::hasPixelMask() const { return itsCurLatPtr->hasPixelMask(); } template const Lattice& CurvedImage2D::pixelMask() const { return itsCurLatPtr->pixelMask(); } template Lattice& CurvedImage2D::pixelMask() { return itsCurLatPtr->pixelMask(); } template const LatticeRegion* CurvedImage2D::getRegionPtr() const { return itsCurLatPtr->getRegionPtr(); } template IPosition CurvedImage2D::shape() const { return itsCurLatPtr->shape(); } template void CurvedImage2D::resize (const TiledShape&) { throw (AipsError ("CurvedImage2D::resize is not possible")); } template String CurvedImage2D::name (Bool stripPath) const { return itsImagePtr->name (stripPath); } template ImageAttrHandler& CurvedImage2D::attrHandler (Bool createHandler) { return itsImagePtr->attrHandler (createHandler); } template Bool CurvedImage2D::doGetSlice (Array& buffer, const Slicer& section) { return itsCurLatPtr->doGetSlice (buffer, section); } template void CurvedImage2D::doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride) { itsCurLatPtr->doPutSlice (sourceBuffer, where, stride); } template Bool CurvedImage2D::doGetMaskSlice (Array& buffer, const Slicer& section) { return itsCurLatPtr->doGetMaskSlice (buffer, section); } template uInt CurvedImage2D::advisedMaxPixels() const { return itsCurLatPtr->advisedMaxPixels(); } template IPosition CurvedImage2D::doNiceCursorShape (uInt maxPixels) const { return itsCurLatPtr->niceCursorShape (maxPixels); } template LatticeIterInterface* CurvedImage2D::makeIter (const LatticeNavigator& navigator, Bool useRef) const { return itsCurLatPtr->makeIter (navigator, useRef); } template Bool CurvedImage2D::lock (FileLocker::LockType type, uInt nattempts) { return itsCurLatPtr->lock (type, nattempts); } template void CurvedImage2D::unlock() { itsCurLatPtr->unlock(); itsImagePtr->unlock(); } template Bool CurvedImage2D::hasLock (FileLocker::LockType type) const { return itsCurLatPtr->hasLock (type); } template void CurvedImage2D::resync() { itsCurLatPtr->resync(); itsImagePtr->resync(); } template void CurvedImage2D::flush() { itsImagePtr->flush(); } template void CurvedImage2D::tempClose() { itsCurLatPtr->tempClose(); itsImagePtr->tempClose(); logger().tempClose(); } template void CurvedImage2D::reopen() { itsImagePtr->reopen(); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Images/ExtendImage.h000066400000000000000000000146141476623553700203470ustar00rootroot00000000000000//# ExtendImage.h: An extension of an ImageInterface object //# Copyright (C) 2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_EXTENDIMAGE_H #define IMAGES_EXTENDIMAGE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class ExtendLattice; // // An extension of an ImageInterface object. // // // // // // // // //
      • ImageInterface //
      • ExtendLattice // // // // Class ExtendImage can be used to (virtually) extend an image // along axes with length 1 and/or to add new axes. In this way such // an image can be made conformant with another image. // E.g. it can be used to extend the continuum channel to // subtract it from each channel in an image cube. // // // // // // // // // Used by LEL to handle images with different dimensionalities. // // //# //# template class ExtendImage: public ImageInterface { public: // The default constructor ExtendImage(); // Create a ExtendImage from a Image. // The coordinate system of the given image should be a subset of the // new coordinate system. The same is true for the shape. ExtendImage (const ImageInterface& image, const IPosition& newShape, const CoordinateSystem& newCsys); // Copy constructor (reference semantics). ExtendImage (const ExtendImage& other); virtual ~ExtendImage(); // Assignment (reference semantics). ExtendImage& operator= (const ExtendImage& other); // Make a copy of the object (reference semantics). // virtual ImageInterface* cloneII() const; // // Get the image type (returns name of derived class). virtual String imageType() const; // Is the ExtendImage masked? // It is if its parent image is masked. virtual Bool isMasked() const; // Does the image object have a pixelmask? // It does if its parent has a pixelmask. virtual Bool hasPixelMask() const; // Get access to the pixelmask in use (thus to the pixelmask of the parent). // An exception is thrown if the parent does not have a pixelmask. // virtual const Lattice& pixelMask() const; virtual Lattice& pixelMask(); // // Get the region used (always returns 0). virtual const LatticeRegion* getRegionPtr() const; // A ExtendImage is not persistent. virtual Bool isPersistent() const; // Is the ExtendImage paged to disk? virtual Bool isPaged() const; // An ExtendImage is not writable virtual Bool isWritable() const; // Returns the shape of the ExtendImage virtual IPosition shape() const; // This function returns the recommended maximum number of pixels to // include in the cursor of an iterator. virtual uInt advisedMaxPixels() const; // Function which changes the shape of the ExtendImage. // Throws an exception as resizing an ExtendImage is not possible. virtual void resize(const TiledShape& newShape); // Return the name of the parent ImageInterface object. virtual String name (Bool stripPath=False) const; // Check class invariants. virtual Bool ok() const; // Get access to the attribute handler (of the parent image). // If a handler keyword does not exist yet, it is created if // createHandler is set. // Otherwise the handler is empty and no groups can be created for it. virtual ImageAttrHandler& attrHandler (Bool createHandler=False); // Do the actual getting of an array of values. virtual Bool doGetSlice (Array& buffer, const Slicer& section); // Putting data is not possible. virtual void doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride); // Get a section of the mask. virtual Bool doGetMaskSlice (Array& buffer, const Slicer& section); // This function is used by the LatticeIterator class to generate an // iterator of the correct type for this Lattice. Not recommended // for general use. virtual LatticeIterInterface* makeIter (const LatticeNavigator& navigator, Bool useRef) const; // Get the best cursor shape. virtual IPosition doNiceCursorShape (uInt maxPixels) const; // Handle the (un)locking and syncing, etc. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; virtual void resync(); virtual void flush(); virtual void tempClose(); virtual void reopen(); // private: //# itsImagePtr points to the parent image. std::unique_ptr> itsImagePtr; std::unique_ptr> itsExtLatPtr; //# Make members of parent class known. public: using ImageInterface::logger; protected: using ImageInterface::setCoordsMember; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/images/Images/ExtendImage.tcc000066400000000000000000000146061476623553700206720ustar00rootroot00000000000000//# ExtendImage.cc: An extension of an ImageInterface object //# Copyright (C) 2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_EXTENDIMAGE_TCC #define IMAGES_EXTENDIMAGE_TCC #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template ExtendImage::ExtendImage() {} template ExtendImage::ExtendImage (const ImageInterface& image, const IPosition& newShape, const CoordinateSystem& newCsys) : itsImagePtr (image.cloneII()) { IPosition newAxes, stretchAxes; if (! CoordinateUtil::findExtendAxes (newAxes, stretchAxes, newShape, image.shape(), newCsys, image.coordinates())) { throw AipsError ("ExtendImage - " "new csys or shape incompatible with old ones"); } itsExtLatPtr.reset(new ExtendLattice (image, newShape, newAxes, stretchAxes)); setCoordsMember (newCsys); this->setImageInfoMember (itsImagePtr->imageInfo()); this->setMiscInfoMember (itsImagePtr->miscInfo()); this->setUnitMember (itsImagePtr->units()); logger().addParent (itsImagePtr->logger()); } template ExtendImage::ExtendImage (const ExtendImage& other) : ImageInterface (other), itsImagePtr (other.itsImagePtr->cloneII()), itsExtLatPtr(new ExtendLattice (*other.itsExtLatPtr)) {} template ExtendImage::~ExtendImage() {} template ExtendImage& ExtendImage::operator= (const ExtendImage& other) { if (this != &other) { ImageInterface::operator= (other); itsImagePtr.reset(other.itsImagePtr->cloneII()); itsExtLatPtr.reset(new ExtendLattice (*other.itsExtLatPtr)); } return *this; } template ImageInterface* ExtendImage::cloneII() const { return new ExtendImage (*this); } template String ExtendImage::imageType() const { return "ExtendImage"; } template Bool ExtendImage::ok() const { return itsExtLatPtr->ok(); } template Bool ExtendImage::isMasked() const { return itsExtLatPtr->isMasked(); } template Bool ExtendImage::isPersistent() const { return itsExtLatPtr->isPersistent(); } template Bool ExtendImage::isPaged() const { return itsExtLatPtr->isPaged(); } template Bool ExtendImage::isWritable() const { return itsExtLatPtr->isWritable(); } template Bool ExtendImage::hasPixelMask() const { return itsExtLatPtr->hasPixelMask(); } template const Lattice& ExtendImage::pixelMask() const { return itsExtLatPtr->pixelMask(); } template Lattice& ExtendImage::pixelMask() { return itsExtLatPtr->pixelMask(); } template const LatticeRegion* ExtendImage::getRegionPtr() const { return itsExtLatPtr->getRegionPtr(); } template IPosition ExtendImage::shape() const { return itsExtLatPtr->shape(); } template void ExtendImage::resize (const TiledShape&) { throw (AipsError ("ExtendImage::resize is not possible")); } template String ExtendImage::name (Bool stripPath) const { return itsImagePtr->name (stripPath); } template ImageAttrHandler& ExtendImage::attrHandler (Bool createHandler) { return itsImagePtr->attrHandler (createHandler); } template Bool ExtendImage::doGetSlice (Array& buffer, const Slicer& section) { return itsExtLatPtr->doGetSlice (buffer, section); } template void ExtendImage::doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride) { itsExtLatPtr->doPutSlice (sourceBuffer, where, stride); } template Bool ExtendImage::doGetMaskSlice (Array& buffer, const Slicer& section) { return itsExtLatPtr->doGetMaskSlice (buffer, section); } template uInt ExtendImage::advisedMaxPixels() const { return itsExtLatPtr->advisedMaxPixels(); } template IPosition ExtendImage::doNiceCursorShape (uInt maxPixels) const { return itsExtLatPtr->niceCursorShape (maxPixels); } template LatticeIterInterface* ExtendImage::makeIter (const LatticeNavigator& navigator, Bool useRef) const { return itsExtLatPtr->makeIter (navigator, useRef); } template Bool ExtendImage::lock (FileLocker::LockType type, uInt nattempts) { return itsExtLatPtr->lock (type, nattempts); } template void ExtendImage::unlock() { itsExtLatPtr->unlock(); itsImagePtr->unlock(); } template Bool ExtendImage::hasLock (FileLocker::LockType type) const { return itsExtLatPtr->hasLock (type); } template void ExtendImage::resync() { itsExtLatPtr->resync(); itsImagePtr->resync(); } template void ExtendImage::flush() { itsImagePtr->flush(); } template void ExtendImage::tempClose() { itsExtLatPtr->tempClose(); itsImagePtr->tempClose(); logger().tempClose(); } template void ExtendImage::reopen() { itsImagePtr->reopen(); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Images/FITS2Image.tcc000066400000000000000000000304021476623553700202620ustar00rootroot00000000000000//# FITS2Image.cc: Class implementing templated functions for FITSImage //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_FITS2IMAGE_TCC #define IMAGES_FITS2IMAGE_TCC #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template void FITSImage::crackHeader (CoordinateSystem& cSys, IPosition& shape, ImageInfo& imageInfo, Unit& brightnessUnit, RecordInterface& miscInfo, Float& scale, Float& offset, uChar& magicUChar, Short& magicShort, Int& magicInt, Bool& hasBlanks, LogIO& os, FitsInput& infile, uInt whichRep) { // Shape PrimaryArray fitsImage(infile); Int ndim = fitsImage.dims(); shape.resize(ndim); for (Int i=0; i header = fitsImage.kwlist_str(True); // Get Coordinate System. Return un-used FITS cards in a Record for further use. Record headerRec; Bool dropStokes = True; Int stokesFITSValue = 1; cSys = ImageFITSConverter::getCoordinateSystem(stokesFITSValue, headerRec, header, os, whichRep, shape, dropStokes); ndim = shape.nelements(); _hasBeamsTable = headerRec.isDefined(ImageFITSConverter::CASAMBM) && headerRec.asRecord(ImageFITSConverter::CASAMBM).asBool("value"); // BITPIX DataType dataType = whatType(); // Int bitpix; Record subRec = headerRec.asRecord("bitpix"); subRec.get("value", bitpix); headerRec.removeField("bitpix"); if (dataType==TpFloat) { if (bitpix != -32) { throw (AipsError("bitpix card inconsistent with data type: expected bitpix = -32")); } } else if (dataType==TpDouble) { if (bitpix != -64) { throw (AipsError("bitpix card inconsistent with data type: expected bitpix = -64")); } } else if (dataType==TpInt) { if (bitpix != 32) { throw (AipsError("bitpix card inconsistent with data type: expected bitpix = 32")); } } else if (dataType==TpShort) { if (bitpix != 16) { throw (AipsError("bitpix card inconsistent with data type: expected bitpix = 16")); } } else if (dataType==TpUChar) { if (bitpix != 8) { throw (AipsError("bitpix card inconsistent with data type: expected bitpix = 8")); } } else { throw (AipsError("Unsupported Template type; Float & Double only are supported")); } // Scale and blank (will only be present for Int and Short) Double s = 1.0; Double o = 0.0; if (headerRec.isDefined("bscale")) { subRec = headerRec.asRecord("bscale"); subRec.get("value", s); headerRec.removeField("bscale"); } if (headerRec.isDefined("bzero")) { subRec = headerRec.asRecord("bzero"); subRec.get("value", o); headerRec.removeField("bzero"); } scale = s; offset = o; // Will only be present for Int and Short and uChar hasBlanks = False; if (headerRec.isDefined("blank")) { subRec = headerRec.asRecord("blank"); Int m; subRec.get("value", m); headerRec.removeField("blank"); if (dataType==TpUChar) { magicUChar = m; } else if (dataType==TpShort) { magicShort = m; } else if (dataType==TpInt) { magicInt = m; } else { magicUChar = m; magicShort = m; magicInt = m; } hasBlanks = True; } // Brightness Unit brightnessUnit = ImageFITSConverter::getBrightnessUnit(headerRec, os); // ImageInfo imageInfo = ImageFITSConverter::getImageInfo(headerRec); // If we had one of those unofficial pseudo-Stokes on the Stokes axis, store it in the imageInfo if (stokesFITSValue != -1) { ImageInfo::ImageTypes type = ImageInfo::imageTypeFromFITS(stokesFITSValue); if (type!= ImageInfo::Undefined) { imageInfo.setImageType(type); } } // Get rid of anything else we don't want to end up in MiscInfo // that will have passed through the FITS parsing process Vector ignore(9); ignore(0) = "^datamax$"; ignore(1) = "^datamin$"; ignore(2) = "^origin$"; ignore(3) = "^extend$"; ignore(4) = "^blocked$"; ignore(5) = "^blank$"; ignore(6) = "^simple$"; ignore(7) = "bscale"; ignore(8) = "bzero"; FITSKeywordUtil::removeKeywords(headerRec, ignore); // MiscInfo is whats left ImageFITSConverter::extractMiscInfo(miscInfo, headerRec); // Get and store history. Vector lines; String groupType; ConstFitsKeywordList kw = fitsImage.kwlist(); kw.first(); // Set the contents of the ImageInterface logger object (history) LoggerHolder& log = logger(); ImageFITSConverter::restoreHistory(log, kw); // Try and find the restoring beam in the history cards if // its not in the header if (! imageInfo.hasBeam()) { imageInfo.getRestoringBeam(log); } } template void FITSImage::crackExtHeader (CoordinateSystem& cSys, IPosition& shape, ImageInfo& imageInfo, Unit& brightnessUnit, RecordInterface& miscInfo, Float& scale, Float& offset, uChar& magicUChar, Short& magicShort, Int& magicInt, Bool& hasBlanks, LogIO& os, FitsInput& infile, uInt whichRep) { // Shape ImageExtension fitsImage(infile); Int ndim = fitsImage.dims(); shape.resize(ndim); for (Int i=0; i header = fitsImage.kwlist_str(True); // Get Coordinate System. Return un-used FITS cards in a Record for further use. Record headerRec; Bool dropStokes = True; Int stokesFITSValue = 1; cSys = ImageFITSConverter::getCoordinateSystem(stokesFITSValue, headerRec, header, os, whichRep, shape, dropStokes); ndim = shape.nelements(); _hasBeamsTable = headerRec.isDefined(ImageFITSConverter::CASAMBM) && headerRec.asRecord(ImageFITSConverter::CASAMBM).asBool("value"); // BITPIX DataType dataType = whatType(); // Int bitpix; Record subRec = headerRec.asRecord("bitpix"); subRec.get("value", bitpix); headerRec.removeField("bitpix"); if (dataType==TpFloat) { if (bitpix != -32) { throw (AipsError("bitpix card inconsistent with data type: expected bitpix = -32")); } } else if (dataType==TpDouble) { if (bitpix != -64) { throw (AipsError("bitpix card inconsistent with data type: expected bitpix = -64")); } } else if (dataType==TpInt) { if (bitpix != 32) { throw (AipsError("bitpix card inconsistent with data type: expected bitpix = 32")); } } else if (dataType==TpShort) { if (bitpix != 16) { throw (AipsError("bitpix card inconsistent with data type: expected bitpix = 16")); } } else if (dataType==TpUChar) { if (bitpix != 8) { throw (AipsError("bitpix card inconsistent with data type: expected bitpix = 16")); } } else { throw (AipsError("Unsupported Template type; Float & Double only are supported")); } // Scale and blank (will only be present for Int and Short) Double s = 1.0; Double o = 0.0; if (headerRec.isDefined("bscale")) { subRec = headerRec.asRecord("bscale"); subRec.get("value", s); headerRec.removeField("bscale"); } if (headerRec.isDefined("bzero")) { subRec = headerRec.asRecord("bzero"); subRec.get("value", o); headerRec.removeField("bzero"); } scale = s; offset = o; // Will only be present for Int and Short hasBlanks = False; if (headerRec.isDefined("blank")) { subRec = headerRec.asRecord("blank"); Int m; subRec.get("value", m); headerRec.removeField("blank"); if (dataType==TpUChar) { magicUChar = m; } else if (dataType==TpShort) { magicShort = m; } else if (dataType==TpInt) { magicInt = m; } else { magicUChar = m; magicShort = m; magicInt = m; } hasBlanks = True; } // Brightness Unit brightnessUnit = ImageFITSConverter::getBrightnessUnit(headerRec, os); // ImageInfo imageInfo = ImageFITSConverter::getImageInfo(headerRec); // If we had one of those unofficial pseudo-Stokes on the Stokes axis, store it in the imageInfo if (stokesFITSValue != -1) { ImageInfo::ImageTypes type = ImageInfo::imageTypeFromFITS(stokesFITSValue); if (type!= ImageInfo::Undefined) { imageInfo.setImageType(type); } } // Get rid of anything else we don't want to end up in MiscInfo // that will have passed through the FITS parsing process Vector ignore(12); ignore(0) = "^datamax$"; ignore(1) = "^datamin$"; ignore(2) = "^origin$"; ignore(3) = "^extend$"; ignore(4) = "^blocked$"; ignore(5) = "^blank$"; ignore(6) = "^simple$"; ignore(7) = "bscale"; ignore(8) = "bzero"; ignore(9) = "xtension"; ignore(10) = "pcount"; ignore(11) = "gcount"; FITSKeywordUtil::removeKeywords(headerRec, ignore); // MiscInfo is whats left ImageFITSConverter::extractMiscInfo(miscInfo, headerRec); // Get and store history. Vector lines; String groupType; ConstFitsKeywordList kw = fitsImage.kwlist(); kw.first(); // Set the contents of the ImageInterface logger object (history) LoggerHolder& log = logger(); ImageFITSConverter::restoreHistory(log, kw); // Try and find the restoring beam in the history cards if // its not in the header if (! imageInfo.hasSingleBeam()) { imageInfo.getRestoringBeam(log); } } /* template void FITSImage::crackHeader (CoordinateSystem &, IPosition &, ImageInfo &, Unit &, RecordInterface &, Float &, Float &, Short &, Int &, Bool &, LogIO &, FitsInput &, uInt); template void FITSImage::crackHeader (CoordinateSystem &, IPosition &, ImageInfo &, Unit &, RecordInterface &, Float &, Float &, Short &, Int &, Bool &, LogIO &, FitsInput &, uInt); template void FITSImage::crackHeader (CoordinateSystem &, IPosition &, ImageInfo &, Unit &, RecordInterface &, Float &, Float &, Short &, Int &, Bool &, LogIO &, FitsInput &, uInt); template void FITSImage::crackHeader (CoordinateSystem &, IPosition &, ImageInfo &, Unit &, RecordInterface &, Float &, Float &, Short &, Int &, Bool &, LogIO &, FitsInput &, uInt); */ } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Images/FITSErrorImage.cc000066400000000000000000000123571476623553700210370ustar00rootroot00000000000000//# FITSErrorImage.cc: Class providing native access to FITS images //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN FITSErrorImage::FITSErrorImage (const String& name, uInt whichRep, uInt whichHDU, FITSErrorImage::ErrorType errtype) : FITSImage(name, whichRep, whichHDU), errtype_p(errtype) { setupMask(); } FITSErrorImage::FITSErrorImage (const String& name, const MaskSpecifier& maskSpec, uInt whichRep, uInt whichHDU, FITSErrorImage::ErrorType errtype) : FITSImage (name, maskSpec, whichRep, whichHDU), errtype_p(errtype) { setupMask(); } FITSErrorImage::FITSErrorImage (const FITSErrorImage& other) : FITSImage(other), errtype_p(other.errtype_p) { setupMask(); } FITSErrorImage& FITSErrorImage::operator=(const FITSErrorImage& other) // // Assignment. Uses reference semantics // { if (this != &other) { FITSImage::operator= (other); errtype_p = other.errtype_p; setupMask(); } return *this; } FITSErrorImage::~FITSErrorImage() { } ImageInterface* FITSErrorImage::cloneII() const { return new FITSErrorImage (*this); } String FITSErrorImage::imageType() const { return "FITSErrorImage"; } Bool FITSErrorImage::doGetSlice(Array& buffer, const Slicer& section) { // set up the arrays IPosition shp = section.length(); if (!buffer.shape().isEqual(shp)) buffer.resize(shp); if (!buffer_p.shape().isEqual(shp)) buffer_p.resize(shp); // get the data+....//// FITSImage::doGetSlice(buffer_p, section); // Bool deletePtrD; const Float* pData = buffer_p.getStorage(deletePtrD); Bool deletePtrM; Float* pBuffer = buffer.getStorage(deletePtrM); // depending on the error type, // fill the resulting array with variance values switch (errtype_p) { case MSE: for (uInt i=0; i&, const IPosition&, const IPosition&) { // the image is read-only throw (AipsError ("FITSErrorImage::putSlice - " "is not possible as FITSErrorImage is not writable")); } FITSErrorImage::ErrorType FITSErrorImage::stringToErrorType(const String errorTypeStr){ // convert the string to an error type if (!errorTypeStr.compare("MSE")) return MSE; else if (!errorTypeStr.compare("RMSE")) return RMSE; else if (!errorTypeStr.compare("INVMSE")) return INVMSE; else if (!errorTypeStr.compare("INVRMSE")) return INVRMSE; else return UNKNOWN; } String FITSErrorImage::errorTypeToString(const FITSErrorImage::ErrorType errType){ // convert the error type to a string switch(errType) { case MSE: return "MSE"; case RMSE: return "RMSE"; case INVMSE: return "INVMSE"; case INVRMSE: return "INVRMSE"; case UNKNOWN: return "UNKNOWN"; default: return ""; // unknown } } void FITSErrorImage::setupMask() { // for the inverse error types, switch on // the masking of values 0.0 (in the FITS file) if (errtype_p == INVMSE || errtype_p == INVRMSE){ setMaskZero(True); } // throw an error for type "UNKNOWN", since // it is now known what to do. else if (errtype_p == UNKNOWN) throw (AipsError ("FITSErrorImage::setupMask - " "error type UNKNOWN is not accepted!")); } } //# NAMESPACE CASACORE - END casacore-3.7.1/images/Images/FITSErrorImage.h000066400000000000000000000106041476623553700206720ustar00rootroot00000000000000//# FITSErrorImage.h: Class providing native access to FITS images //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_FITSERRORIMAGE_H #define IMAGES_FITSERRORIMAGE_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MaskSpecifier; class IPosition; class Slicer; // // Class providing native access to FITS Error images. // // // // // //
      • FITSImage //
      • FITSMask // // // // // // // // // // // This provides native access to FITS error images. // //# //# class FITSErrorImage: public FITSImage { public: // The enum describes which types of error images exist. The type is fixed // during object creation and can not be changed at a later time. enum ErrorType { MSE, // the values are "mean squared error" (=variance) RMSE, // the values are "root mean squared error" (=sigma) INVMSE, // the values are inverse "means squared error" INVRMSE, // the values are inverse "root mean squared error" UNKNOWN, // unknown type DEFAULT=MSE }; // Construct a FITSImage from the disk FITS file name and extension and apply mask. explicit FITSErrorImage(const String& name, uInt whichRep=0, uInt whichHDU=0, FITSErrorImage::ErrorType errtype=MSE); // Construct a FITSImage from the disk FITS file name and extension and apply mask or not. FITSErrorImage(const String& name, const MaskSpecifier& mask, uInt whichRep=0, uInt whichHDU=0, FITSErrorImage::ErrorType errtype=MSE); // Copy constructor (reference semantics) FITSErrorImage(const FITSErrorImage& other); // Destructor virtual ~FITSErrorImage(); // Assignment (reference semantics) FITSErrorImage& operator=(const FITSErrorImage& other); // Make a copy of the object with new (reference semantics). virtual ImageInterface* cloneII() const; // Get the image type (returns "FITSErrorImage"). virtual String imageType() const; // Do the actual get of the data. // Returns False as the data do not reference another Array virtual Bool doGetSlice (Array& buffer, const Slicer& theSlice); // The FITSImage is not writable, so this throws an exception. virtual void doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride); // Return the error type. virtual FITSErrorImage::ErrorType errorType() const {return errtype_p;}; // Convert an image type to String. static FITSErrorImage::ErrorType stringToErrorType(String errorTypeStr); // Convert a String to an image type. static String errorTypeToString(FITSErrorImage::ErrorType errType); private: // Set the correct masking. void setupMask(); Array buffer_p; FITSErrorImage::ErrorType errtype_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Images/FITSImage.cc000066400000000000000000000572251476623553700200300ustar00rootroot00000000000000//# FITSImage.cc: Class providing native access to FITS images //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN FITSImage::FITSImage (const String& name, uInt whichRep, uInt whichHDU) : ImageInterface(), name_p (name), fullname_p (name), scale_p (1.0), offset_p (0.0), shortMagic_p (0), uCharMagic_p (0), longMagic_p (0), hasBlanks_p (False), dataType_p (TpOther), fileOffset_p(0), isClosed_p (True), filterZeroMask_p(False), whichRep_p(whichRep), whichHDU_p(whichHDU), _hasBeamsTable(False) { setup(); } FITSImage::FITSImage (const String& name, const MaskSpecifier& maskSpec, uInt whichRep, uInt whichHDU) : ImageInterface(), name_p (name), fullname_p (name), maskSpec_p (maskSpec), scale_p (1.0), offset_p (0.0), shortMagic_p (0), uCharMagic_p (0), longMagic_p (0), hasBlanks_p (False), dataType_p (TpOther), fileOffset_p(0), isClosed_p (True), filterZeroMask_p(False), whichRep_p(whichRep), whichHDU_p(whichHDU), _hasBeamsTable(False) { setup(); } FITSImage::FITSImage (const FITSImage& other) : ImageInterface(other), name_p (other.name_p), fullname_p (other.fullname_p), maskSpec_p (other.maskSpec_p), pTiledFile_p(other.pTiledFile_p), shape_p (other.shape_p), scale_p (other.scale_p), offset_p (other.offset_p), shortMagic_p (other.shortMagic_p), uCharMagic_p (other.uCharMagic_p), longMagic_p (other.longMagic_p), hasBlanks_p (other.hasBlanks_p), dataType_p (other.dataType_p), fileOffset_p(other.fileOffset_p), isClosed_p (other.isClosed_p), filterZeroMask_p(other.filterZeroMask_p), whichRep_p(other.whichRep_p), whichHDU_p(other.whichHDU_p), _hasBeamsTable(other._hasBeamsTable) { if (other.pPixelMask_p) { pPixelMask_p.reset (other.pPixelMask_p->clone()); } } FITSImage& FITSImage::operator=(const FITSImage& other) // // Assignment. Uses reference semantics // { if (this != &other) { ImageInterface::operator= (other); // pTiledFile_p = other.pTiledFile_p; // shared pointer // pPixelMask_p.reset(); if (other.pPixelMask_p) { pPixelMask_p.reset (other.pPixelMask_p->clone()); } // shape_p = other.shape_p; name_p = other.name_p; fullname_p = other.fullname_p; maskSpec_p = other.maskSpec_p; scale_p = other.scale_p; offset_p = other.offset_p; shortMagic_p = other.shortMagic_p; uCharMagic_p = other.uCharMagic_p; longMagic_p = other.longMagic_p; hasBlanks_p = other.hasBlanks_p; dataType_p = other.dataType_p; fileOffset_p= other.fileOffset_p; isClosed_p = other.isClosed_p; filterZeroMask_p = other.filterZeroMask_p; whichRep_p = other.whichRep_p; whichHDU_p = other.whichHDU_p; _hasBeamsTable = other._hasBeamsTable; } return *this; } LatticeBase* FITSImage::openFITSImage (const String& name, const MaskSpecifier& spec) { return new FITSImage (name, spec); } void FITSImage::registerOpenFunction() { ImageOpener::registerOpenImageFunction (ImageOpener::FITS, &openFITSImage); } // String FITSImage::get_fitsname(const String &fullname) { String fullname_l; String fitsname; Int close_bracepos, open_bracepos, fullname_length; fullname_l = fullname; fullname_l.trim(); fullname_length = fullname_l.length(); //cerr << "Initial name: " << fullname_l << endl; // check whether the strings ends with "]" if (fullname_l.compare(fullname_length-1, 1, "]", 1)) { // check for an open brace open_bracepos = fullname_l.rfind("[", fullname_length); if (open_bracepos > 0) { // check for a closing brace close_bracepos = fullname_l.rfind("]", fullname_length); // an open brace at the end indicates a mal-formed name if (close_bracepos < 0 || (open_bracepos > close_bracepos)) throw (AipsError(fullname_l + " has opening brace, but no closing brace.")); } // just copy the input fitsname = fullname_l; } else { // check for the last "[" open_bracepos = fullname_l.rfind("[", fullname_length); if (open_bracepos < 0) { throw (AipsError(fullname_l + " has closing brace, but no opening brace.")); } else { // separate the filename an the extension name //extexpr_p = String(fullname_l, open_bracepos+1, fullname_length-open_bracepos-2); fitsname =String(fullname_l, 0, open_bracepos); } } //cerr << "FITS name: " << fitsname <0.")); //cerr << "Extension version must be >0: " << extver << endl; //exit(0); } } // make it pretty extname.trim(); extname.upcase(); } //cerr << "Opening image parser with: "<< fitsname < 0 || extindex > -1) { FITSExtInfo fei = FITSExtInfo(fip.fitsname(True), extindex, extname, extver, True); fitsindex = fip.get_index(fei); if (fitsindex > -1) hduindex = (uInt)fitsindex; else throw (AipsError("Extension " + extstring + " does not exist in " + fitsname)); } else { hduindex = fip.get_firstdata_index(); if (hduindex > 1 || hduindex == fip.get_numhdu()) throw (AipsError("No data in the zeroth or first extension of " + fitsname)); } // return the index return hduindex; } ImageInterface* FITSImage::cloneII() const { return new FITSImage (*this); } String FITSImage::imageType() const { return className(); } String FITSImage::className() { static const string x = "FITSImage"; return x; } Bool FITSImage::isMasked() const { return hasBlanks_p; } const LatticeRegion* FITSImage::getRegionPtr() const { return 0; } IPosition FITSImage::shape() const { return shape_p.shape(); } uInt FITSImage::advisedMaxPixels() const { return shape_p.tileShape().product(); } IPosition FITSImage::doNiceCursorShape (uInt) const { return shape_p.tileShape(); } void FITSImage::resize(const TiledShape&) { throw (AipsError ("FITSImage::resize - a FITSImage is not writable")); } Bool FITSImage::doGetSlice(Array& buffer, const Slicer& section) { reopenIfNeeded(); if (pTiledFile_p->dataType() == TpFloat) { pTiledFile_p->get (buffer, section); } else if (pTiledFile_p->dataType() == TpDouble) { Array tmp; pTiledFile_p->get (tmp, section); buffer.resize(tmp.shape()); convertArray(buffer, tmp); } else if (pTiledFile_p->dataType() == TpInt) { pTiledFile_p->get (buffer, section, scale_p, offset_p, longMagic_p, hasBlanks_p); } else if (pTiledFile_p->dataType() == TpShort) { pTiledFile_p->get (buffer, section, scale_p, offset_p, shortMagic_p, hasBlanks_p); } else if (pTiledFile_p->dataType() == TpUChar) { pTiledFile_p->get (buffer, section, scale_p, offset_p, uCharMagic_p, hasBlanks_p); } return False; // Not a reference } void FITSImage::doPutSlice (const Array&, const IPosition&, const IPosition&) { throw (AipsError ("FITSImage::putSlice - " "is not possible as FITSImage is not writable")); } String FITSImage::name (Bool stripPath) const { Path path(name_p); if (stripPath) { return path.baseName(); } else { return path.absoluteName(); } } Bool FITSImage::isPersistent() const { return True; } Bool FITSImage::isPaged() const { return True; } Bool FITSImage::isWritable() const { // Its too hard to implement putMaskSlice becuase // magic blanking is used. It measn we lose // the data values if the mask is put somewhere return False; } Bool FITSImage::ok() const { return True; } DataType FITSImage::dataType() const { return TpFloat; } Bool FITSImage::doGetMaskSlice (Array& buffer, const Slicer& section) { if (!hasBlanks_p) { buffer.resize (section.length()); buffer = True; return False; } // reopenIfNeeded(); return pPixelMask_p->getSlice (buffer, section); } Bool FITSImage::hasPixelMask() const { return hasBlanks_p; } const Lattice& FITSImage::pixelMask() const { if (!hasBlanks_p) { throw (AipsError ("FITSImage::pixelMask - no pixelmask used")); } return *pPixelMask_p; } Lattice& FITSImage::pixelMask() { if (!hasBlanks_p) { throw (AipsError ("FITSImage::pixelMask - no pixelmask used")); } return *pPixelMask_p; } void FITSImage::tempClose() { if (! isClosed_p) { pPixelMask_p.reset(); pTiledFile_p.reset(); isClosed_p = True; } } void FITSImage::reopen() { if (isClosed_p) { open(); } } uInt FITSImage::maximumCacheSize() const { reopenIfNeeded(); return pTiledFile_p->maximumCacheSize() / ValType::getTypeSize(dataType_p); } void FITSImage::setMaximumCacheSize (uInt howManyPixels) { reopenIfNeeded(); const uInt sizeInBytes = howManyPixels * ValType::getTypeSize(dataType_p); pTiledFile_p->setMaximumCacheSize (sizeInBytes); } void FITSImage::setCacheSizeFromPath (const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath) { reopenIfNeeded(); pTiledFile_p->setCacheSize (sliceShape, windowStart, windowLength, axisPath); } void FITSImage::setCacheSizeInTiles (uInt howManyTiles) { reopenIfNeeded(); pTiledFile_p->setCacheSize (howManyTiles); } void FITSImage::clearCache() { if (! isClosed_p) { pTiledFile_p->clearCache(); } } void FITSImage::showCacheStatistics (ostream& os) const { reopenIfNeeded(); os << "FITSImage statistics : "; pTiledFile_p->showCacheStatistics (os); } void FITSImage::setup() { // Separate the FITS filename from any // possible extension specification name_p = get_fitsname(fullname_p); // Determine the HDU index from the extension specification uInt HDUnum = get_hdunum(fullname_p); // Compare the HDU index given directly and // the one extracted from the name if (HDUnum != whichHDU_p){ // if an extension information was given, // the index extracted from it wins if (name_p != fullname_p){ whichHDU_p = HDUnum; } else { // if the index given directly is zero (which means the default), // the zeroth index might be emptied and the index retrieved // in the method above (which is 1) is used if (!whichHDU_p) { whichHDU_p = HDUnum; } } } if (name_p.empty()) { throw AipsError("FITSImage: given file name is empty"); } // if (!maskSpec_p.name().empty()) { throw AipsError("FITSImage " + name_p + " has no named masks"); } Path path(name_p); String fullName = path.absoluteName(); // Fish things out of the FITS file CoordinateSystem cSys; IPosition shape; ImageInfo imageInfo; Unit brightnessUnit; Int recno; Int recsize; // Should be 2880 bytes (unless blocking used) FITS::ValueType dataType; Record miscInfo; // hasBlanks only relevant to Integer images. Says if 'blank' value defined in header getImageAttributes(cSys, shape, imageInfo, brightnessUnit, miscInfo, recsize, recno, dataType, scale_p, offset_p, uCharMagic_p, shortMagic_p, longMagic_p, hasBlanks_p, fullName, whichRep_p, whichHDU_p); // shape must be set before image info in cases of multiple beams shape_p = TiledShape (shape, TiledFileAccess::makeTileShape(shape)); setMiscInfoMember (miscInfo); // set ImageInterface data setCoordsMember (cSys); // Set FITSImage data setUnitMember (brightnessUnit); // By default, ImageInterface makes a memory-based LoggerHolder // which is all we need. We will fill it in later // I don't understand why I have to subtract one, as the // data should begin in the NEXT record. BobG surmises // the FITS classes read ahead... // MK: I think there is an additional read() and hence // count-up of recno when the file is first accessed and // then for every skipped hdu, thats where the "-1 - whichHDU comes from" fileOffset_p += (recno - 1 - whichHDU_p) * recsize; // dataType_p = TpFloat; if (dataType == FITS::DOUBLE) { dataType_p = TpDouble; } else if (dataType == FITS::SHORT) { dataType_p = TpShort; } else if (dataType == FITS::LONG) { dataType_p = TpInt; } else if (dataType == FITS::BYTE) { dataType_p = TpUChar; } // See if there is a mask specifier. Defaults to apply mask. if (maskSpec_p.useDefault()) { // We would like to use any mask. For 32 f.p. bit we don't know if there // are masked pixels (they are NaNs). For Integer types we do know if there // the magic value has been set (suggests there are masked pixels) and // hasBlanks_p was set to T or F by getImageAttributes if (dataType_p==TpFloat || dataType_p== TpDouble) hasBlanks_p = True; } else { // We don't want to use the mask hasBlanks_p = False; } // Open the image. open(); // Finally, read any supported extensions, like a BEAMS table if (_hasBeamsTable) { ImageFITSConverter::readBeamsTable(imageInfo, fullName, dataType_p); } setImageInfoMember (imageInfo); } void FITSImage::open() { Bool writable = False; Bool canonical = True; // The tile shape must not be a subchunk in all dimensions pTiledFile_p = std::make_shared(name_p, fileOffset_p, shape_p.shape(), shape_p.tileShape(), dataType_p, TSMOption(), writable, canonical); // Shares the pTiledFile_p pointer. Scale factors for integers FITSMask* fitsMask=0; if (hasBlanks_p) { if (dataType_p == TpFloat) { fitsMask = new FITSMask(pTiledFile_p.get()); } else if (dataType_p == TpDouble) { fitsMask = new FITSMask(pTiledFile_p.get()); } else if (dataType_p == TpUChar) { fitsMask = new FITSMask(pTiledFile_p.get(), scale_p, offset_p, uCharMagic_p, hasBlanks_p); } else if (dataType_p == TpShort) { fitsMask = new FITSMask(pTiledFile_p.get(), scale_p, offset_p, shortMagic_p, hasBlanks_p); } else if (dataType_p == TpInt) { fitsMask = new FITSMask(pTiledFile_p.get(), scale_p, offset_p, longMagic_p, hasBlanks_p); } if (fitsMask) { pPixelMask_p.reset (fitsMask); fitsMask->setFilterZero(filterZeroMask_p); } } // Ok, it is open now. isClosed_p = False; } void FITSImage::getImageAttributes (CoordinateSystem& cSys, IPosition& shape, ImageInfo& imageInfo, Unit& brightnessUnit, RecordInterface& miscInfo, Int& recordsize, Int& recordnumber, FITS::ValueType& dataType, Float& scale, Float& offset, uChar& uCharMagic, Short& shortMagic, Int& longMagic, Bool& hasBlanks, const String& name, uInt whichRep, uInt whichHDU) { LogIO os(LogOrigin("FITSImage", "getImageAttributes", WHERE)); File fitsfile(name); if (!fitsfile.exists() || !fitsfile.isReadable() || !fitsfile.isRegular()) { throw (AipsError(name + " does not exist or is not readable")); } // ImageOpener::ImageTypes type = ImageOpener::imageType(name_p); if (type != ImageOpener::FITS) { throw (AipsError(name + " is not a FITS image")); } // FitsInput infile(fitsfile.path().expandedName().chars(), FITS::Disk); if (infile.err()) { throw (AipsError("Cannot open file " + name + " (or other I/O error)")); } recordsize = infile.fitsrecsize(); // // Advance to the right HDU // for (uInt i=0; i(cSys, shape, imageInfo, brightnessUnit, miscInfo, scale, offset, uCharMagic, shortMagic, longMagic, hasBlanks, os, infile, whichRep); } else if (dataType==FITS::DOUBLE) { crackHeader(cSys, shape, imageInfo, brightnessUnit, miscInfo, scale, offset, uCharMagic, shortMagic, longMagic, hasBlanks, os, infile, whichRep); } else if (dataType==FITS::LONG) { crackHeader(cSys, shape, imageInfo, brightnessUnit, miscInfo, scale, offset, uCharMagic, shortMagic, longMagic, hasBlanks, os, infile, whichRep); } else if (dataType==FITS::SHORT) { crackHeader(cSys, shape, imageInfo, brightnessUnit, miscInfo, scale, offset, uCharMagic, shortMagic, longMagic, hasBlanks, os, infile, whichRep); } else if (dataType==FITS::BYTE) { crackHeader(cSys, shape, imageInfo, brightnessUnit, miscInfo, scale, offset, uCharMagic, shortMagic, longMagic, hasBlanks, os, infile, whichRep); } } else { if (dataType==FITS::FLOAT) { crackExtHeader(cSys, shape, imageInfo, brightnessUnit, miscInfo, scale, offset, uCharMagic, shortMagic, longMagic, hasBlanks, os, infile, whichRep); } else if (dataType==FITS::DOUBLE) { crackExtHeader(cSys, shape, imageInfo, brightnessUnit, miscInfo, scale, offset, uCharMagic, shortMagic, longMagic, hasBlanks, os, infile, whichRep); } else if (dataType==FITS::LONG) { crackExtHeader(cSys, shape, imageInfo, brightnessUnit, miscInfo, scale, offset, uCharMagic, shortMagic, longMagic, hasBlanks, os, infile, whichRep); } else if (dataType==FITS::SHORT) { crackExtHeader(cSys, shape, imageInfo, brightnessUnit, miscInfo, scale, offset, uCharMagic, shortMagic, longMagic, hasBlanks, os, infile, whichRep); } else if (dataType==FITS::BYTE) { crackExtHeader(cSys, shape, imageInfo, brightnessUnit, miscInfo, scale, offset, uCharMagic, shortMagic, longMagic, hasBlanks, os, infile, whichRep); } } // } // Get recordnumber recordnumber = infile.recno(); } void FITSImage::setMaskZero(Bool filterZero) { // set the zero masking on the // current mask if (pPixelMask_p) { dynamic_cast(pPixelMask_p.get())->setFilterZero(True); } // set the flag, such that an later // mask created in 'open()' will be OK // as well filterZeroMask_p = filterZero; } } //# NAMESPACE CASACORE - END casacore-3.7.1/images/Images/FITSImage.h000066400000000000000000000255721476623553700176720ustar00rootroot00000000000000//# FITSImage.h: Class providing native access to FITS images //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_FITSIMAGE_H #define IMAGES_FITSIMAGE_H //# Includes #include #include #include #include #include #include #include #include #include #ifndef WCSLIB_GETWCSTAB #define WCSLIB_GETWCSTAB #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Lattice; // class MaskSpecifier; class IPosition; class Slicer; class CoordinateSystem; class FITSMask; class FitsInput; // // Class providing native access to FITS images. // // // // // //
      • ImageInterface //
      • FITSMask // // // This class provides native access to FITS images. // 64bit, 32bit floating point, 32 bit and 16bit integer FITS images are // presently supported. // // // A FITSImage provides native access to FITS images by accessing them // with the TiledFileAccess class. The FITSImage is read only. // We could implement a writable FITSImage but putting the mask // would lose data values (uses magic blanking) and FITS is really // meant as an interchange medium, not an internal format. // // Because FITS uses magic value blanking, the mask is generated // on the fly as needed. // // // // FITSImage im("in.fits"); // LogIO logger(or); // ImageStatistics stats(im, logger); // Bool ok = stats.display(); // Display statistics // // // // This provides native access to FITS images. // //# //# class FITSImage: public ImageInterface { public: // Construct a FITSImage from the disk FITS file name and extension and apply mask. explicit FITSImage(const String& name, uInt whichRep=0, uInt whichHDU=0); // Construct a FITSImage from the disk FITS file name and extension and apply mask or not. FITSImage(const String& name, const MaskSpecifier& mask, uInt whichRep=0, uInt whichHDU=0); // Copy constructor (reference semantics) FITSImage(const FITSImage& other); // Destructor does nothing virtual ~FITSImage() = default; // Assignment (reference semantics) FITSImage& operator=(const FITSImage& other); // Function to open a FITS image (new parser) static LatticeBase* openFITSImage (const String& name, const MaskSpecifier&); // Register the open function. static void registerOpenFunction(); // Separate any extension specification and return the pure fitsname static String get_fitsname(const String &fullname); // Get the extension index for any extension specification given in the full name static uInt get_hdunum(const String &fullname); //# ImageInterface virtual functions // Make a copy of the object with new (reference semantics). virtual ImageInterface* cloneII() const; // Get the image type (returns FITSImage). virtual String imageType() const; // returns "FITSImage". Added so callers don't require an object to get // the image type. static String className(); // Function which changes the shape of the FITSImage. // Throws an exception as FITSImage is not writable. virtual void resize(const TiledShape& newShape); //# MaskedLattice virtual functions // Has the object really a mask? The FITSImage always // has a pixel mask and never has a region mask so this // always returns True virtual Bool isMasked() const; // FITSimage always has a pixel mask so returns True virtual Bool hasPixelMask() const; // Get access to the pixelmask. FITSImage always has a pixel mask. // virtual const Lattice& pixelMask() const; virtual Lattice& pixelMask(); // // Do the actual get of the mask data. The return value is always // False, thus the buffer does not reference another array. virtual Bool doGetMaskSlice (Array& buffer, const Slicer& section); // Get the region used. There is no region. // Always returns 0. virtual const LatticeRegion* getRegionPtr() const; //# Lattice virtual functions // Do the actual get of the data. // Returns False as the data do not reference another Array virtual Bool doGetSlice (Array& buffer, const Slicer& theSlice); // The FITSImage is not writable, so this throws an exception. virtual void doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride); //# LatticeBase virtual functions // The lattice is paged to disk. virtual Bool isPaged() const; // The lattice is persistent. virtual Bool isPersistent() const; // The FITSImage is not writable. virtual Bool isWritable() const; // Returns the name of the disk file. virtual String name (Bool stripPath=False) const; // return the shape of the FITSImage virtual IPosition shape() const; // Returns the maximum recommended number of pixels for a cursor. This is // the number of pixels in a tile. virtual uInt advisedMaxPixels() const; // Help the user pick a cursor for most efficient access if they only want // pixel values and don't care about the order or dimension of the // cursor. virtual IPosition doNiceCursorShape (uInt maxPixels) const; // Temporarily close the image. virtual void tempClose(); // Reopen a temporarily closed image. virtual void reopen(); // Check class invariants. virtual Bool ok() const; // Return the data type (TpFloat). virtual DataType dataType() const; // Return the (internal) data type. DataType internalDataType() const { return dataType_p; } // Return the HDU number uInt whichHDU () const { return whichHDU_p; } // Maximum size - not necessarily all used. In pixels. virtual uInt maximumCacheSize() const; // Set the maximum (allowed) cache size as indicated. virtual void setMaximumCacheSize (uInt howManyPixels); // Set the cache size as to "fit" the indicated path. virtual void setCacheSizeFromPath (const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath); // Set the actual cache size for this Array to be be big enough for the // indicated number of tiles. This cache is not shared with PagedArrays // in other rows and is always clipped to be less than the maximum value // set using the setMaximumCacheSize member function. // tiles. Tiles are cached using a first in first out algorithm. virtual void setCacheSizeInTiles (uInt howManyTiles); // Clears and frees up the caches, but the maximum allowed cache size is // unchanged from when setCacheSize was called virtual void clearCache(); // Report on cache success. virtual void showCacheStatistics (ostream& os) const; protected: // Set the masking of values 0.0 void setMaskZero(Bool filterZero); private: String name_p; String fullname_p; MaskSpecifier maskSpec_p; std::shared_ptr pTiledFile_p; std::unique_ptr> pPixelMask_p; TiledShape shape_p; Float scale_p; Float offset_p; Short shortMagic_p; uChar uCharMagic_p; Int longMagic_p; Bool hasBlanks_p; DataType dataType_p; Int64 fileOffset_p; Bool isClosed_p; Bool filterZeroMask_p; uInt whichRep_p; uInt whichHDU_p; Bool _hasBeamsTable; // Reopen the image if needed. void reopenIfNeeded() const { if (isClosed_p) const_cast(this)->reopen(); } // Setup the object (used by constructors). void setup(); // Open the image (used by setup and reopen). void open(); // Fish things out of the FITS file void getImageAttributes (CoordinateSystem& cSys, IPosition& shape, ImageInfo& info, Unit& brightnessUnit, RecordInterface& miscInfo, Int& recsize, Int& recno, FITS::ValueType& dataType, Float& scale, Float& offset, uChar& uCharMagic, Short& shortMagic, Int& longMagic, Bool& hasBlanks, const String& name, uInt whichRep, uInt whichHDU); // Crack a primary header template void crackHeader (CoordinateSystem& cSys, IPosition& shape, ImageInfo& imageInfo, Unit& brightnessUnit, RecordInterface& miscInfo, Float& scale, Float& offset, uChar& magicUChar, Short& magicShort, Int& magicLong, Bool& hasBlanks, LogIO& os, FitsInput& infile, uInt whichRep); // Crack an image extension header template void crackExtHeader (CoordinateSystem& cSys, IPosition& shape, ImageInfo& imageInfo, Unit& brightnessUnit, RecordInterface& miscInfo, Float& scale, Float& offset, uChar& uCharMagic, Short& magicShort, Int& magicLong, Bool& hasBlanks, LogIO& os, FitsInput& infile, uInt whichRep); }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/images/Images/FITSImgParser.cc000066400000000000000000000562661476623553700207030ustar00rootroot00000000000000//# FITSImgParser.cc: Class for parsing multi-extension FITS images //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //const char *FITSImgParser::storeKwords_p[] = {"HDUTYPE", "SCIDATA", "ERRDATA", "QUALDATA", // "ERRTYPE", "QUALTYPE", "QUALMASK"}; //const int FITSImgParser::nKwords_p=7; const char *FITSImgParser::storeKwords_p[] = {"HDUCLASS", "HDUDOC", "HDUVERS", "HDUCLAS1", "HDUCLAS2", "HDUCLAS3","SCIDATA", "ERRDATA", "QUALDATA", "QUALMASK"}; const int FITSImgParser::nKwords_p=10; FITSImgParser::FITSImgParser (const String& name) : name_p (name), numhdu_p (0), qualimglist_p(0), hasmeasurement_p(False) { setup(); find_qualimgs(); } FITSImgParser::FITSImgParser(const FITSImgParser& other): name_p(other.name_p), numhdu_p(other.numhdu_p), qualimglist_p(other.qualimglist_p), hasmeasurement_p(other.hasmeasurement_p) { // allocate the extensions and copy the information extensions_p = new FITSExtInfo[other.numhdu_p]; for (uInt index=0; index < numhdu_p; index++){ extensions_p[index] = other.extensions_p[index]; } } FITSImgParser::~FITSImgParser() { delete []extensions_p; } FITSImgParser& FITSImgParser::operator=(const FITSImgParser& other){ if (this != &other) { name_p = other.name_p; numhdu_p = other.numhdu_p; qualimglist_p = other.qualimglist_p; hasmeasurement_p = other.hasmeasurement_p; // allocate the extensions and copy the information extensions_p = new FITSExtInfo[other.numhdu_p]; for (uInt index=0; index < numhdu_p; index++){ extensions_p[index] = other.extensions_p[index]; } } return *this; } String FITSImgParser::fitsname (Bool stripPath) const { Path path(name_p); if (stripPath) { return path.baseName(); } else { return path.absoluteName(); } } Int FITSImgParser::get_index(const FITSExtInfo &extinfo) { // go over all extensions for (Int index=0; index < (Int)numhdu_p; index++){ // return the current index if is was the // one you are looking for if (extensions_p[index] == extinfo) return index; } // return the sign for 'not found' return -1; } Int FITSImgParser::find_extension(const String &extname, const Int &extversion){ // generate an extinfo object from the input FITSExtInfo fext_info = FITSExtInfo(fitsname(True), 0, extname, extversion, True); // return its index return get_index(fext_info); } uInt FITSImgParser::get_firstdata_index(void){ // go over all extensions for (uInt index=0; index < numhdu_p; index++){ // check for data if (extensions_p[index].has_data()) // return the index return index; } // return the number of extension // as default return numhdu_p; } String FITSImgParser::get_extlist_string(const String &delimiter, const String &qualmarker, const String &fitsmarker, const Bool &listall) { String bigString=""; // add the quality image sets if (listall){ for (uInt index=0; index < qualimglist_p.size(); index++){ bigString += qualmarker + fitsname(True) + String("[") + qualimglist_p(index) + String("]") + delimiter; } } // add the individual extensions for (uInt index=0; index < numhdu_p; index++){ if (extensions_p[index].has_data()) bigString += fitsmarker + extensions_p[index].get_extexpr() + delimiter; } // return the String return bigString; } Bool FITSImgParser::is_qualityimg(const String &extexpr){ Bool qualityimg; Vector extlist; // extract the list of extensions from the // extension expression // ok = get_extlist(extexpr, extlist); get_extlist(extexpr, extlist); if (extlist.size()<2){ //cout << "Only one extension given!" << endl; return False; } else if(extlist.size()>3){ //cout << "More than three extensions given!" << endl; return False; } // check for integer values in the extension list, // which indicates rather an extension version // number and not a quality image for (uInt index=0; index extindex(extlist.size()); for (uInt index=0; index identified(extlist.size(), False); // get the data extension // return "False" if there is no data extension Int data_ext = get_dataindex(extindex); if (data_ext < 0) return False; // mark the data extension as identified for (uInt index=0; index 0){ for (uInt index=0; index 0){ for (uInt index=0; index extlist; // give some defaults for // return values that may // not be set error_type=String(""); mask_type =String(""); mask_value=0; // extract the list of extensions from the // extension expression get_extlist(extexpr, extlist); // store the index of each extension Vector extindex(extlist.size(), -1); for (uInt index=0; index -1){ // search for the error extension String error_ext = get_errorext(data_HDU); if (error_ext.size() > 0){ // get the extension index error_HDU = find_extension(error_ext); // if the extension exists if (error_HDU > -1){ FitsKeyword *actkeyw; // extract the keyword "HDUCLAS3" actkeyw = extensions_p[error_HDU].get_keyword(String("HDUCLAS3")); // check whether the keyword exists if (actkeyw){ // convert the keyword to string String kw_errtype = String(actkeyw->asString()); kw_errtype.trim(); // set the value if possible if (kw_errtype.size()>0) error_type = kw_errtype; } } } else{ // explicitly set the default error_HDU =-1; } String mask_ext(""); /* Loading a mask does not yet work, * hence the identification makes no sense // search for the mask extension String mask_ext = get_maskext(data_HDU); */ if (mask_ext.size() > 0){ // get the extension index mask_HDU = find_extension(mask_ext); // if the extension exists if (mask_HDU > -1){ FitsKeyword *actkeyw; // extract the keyword "HDUCLAS3" actkeyw = extensions_p[mask_HDU].get_keyword(String("HDUCLAS3")); // check whether the keyword exists if (actkeyw){ // convert the keyword to string String kw_masktype = String(actkeyw->asString()); kw_masktype.trim(); // set the value if possible if (kw_masktype.size()>0) mask_type = kw_masktype; } // extract the keyword "QUALMASK" actkeyw = extensions_p[mask_HDU].get_keyword(String("QUALMASK")); // check whether the keyword exists if (actkeyw){ // convert the keyword to Int Int kw_maskval = actkeyw->asInt(); // set the value if possible if (kw_maskval) mask_value = kw_maskval; } } } else{ // explicitly set the default mask_HDU =-1; } } else{ // set defaults error_HDU =-1; mask_HDU =-1; } return True; } void FITSImgParser::setup(void) { // make sure a proper name is defined if (name_p.empty()) { throw AipsError("FITSImgParser::setup - Given file name is empty"); } // get the pathname Path path(name_p); String fullName = path.absoluteName(); // open the fits image FitsInput fin(path.expandedName().chars(),FITS::Disk); if (fin.err() == FitsIO::IOERR) throw (AipsError("FITSImgParser::setup - "+name_p+" Error opening FITS input.")); else if (fin.err()) throw (AipsError("FITSImgParser::setup - "+name_p+" Error reading initial record -- exiting.")); // get the number of HDU's int num_hdu = fin.getnumhdu(); // allocate the extensions extensions_p = new FITSExtInfo[num_hdu]; HeaderDataUnit *hdu; PrimaryArray *paB; PrimaryArray *paS; PrimaryArray *paL; PrimaryArray *paF; PrimaryArray *paD; uInt extindex = 0; Bool isfitsimg=True; while (fin.rectype() != FITS::EndOfFile && isfitsimg && !fin.err() && extindex < (uInt)num_hdu){ extindex++; if (fin.rectype() == FITS::HDURecord) { switch (fin.hdutype()) { case FITS::PrimaryArrayHDU: switch (fin.datatype()) { case FITS::BYTE: paB = new PrimaryArray(fin); process_extension(paB, extindex); delete paB; break; case FITS::SHORT: paS = new PrimaryArray(fin); process_extension(paS, extindex); delete paS; break; case FITS::LONG: paL = new PrimaryArray(fin); process_extension(paL, extindex); delete paL; break; case FITS::FLOAT: paF = new PrimaryArray(fin); process_extension(paF, extindex); delete paF; break; case FITS::DOUBLE: paD = new PrimaryArray(fin); process_extension(paD, extindex); delete paD; break; default: break; } break; case FITS::ImageExtensionHDU: switch (fin.datatype()) { case FITS::BYTE: paB = new ImageExtension(fin); process_extension(paB, extindex); delete paB; break; case FITS::SHORT: paS = new ImageExtension(fin); process_extension(paS, extindex); delete paS; break; case FITS::LONG: paL = new ImageExtension(fin); process_extension(paL, extindex); delete paL; break; case FITS::FLOAT: paF = new ImageExtension(fin); process_extension(paF, extindex); delete paF; break; case FITS::DOUBLE: paD = new ImageExtension(fin); process_extension(paD, extindex); delete paD; break; default: break; } break; case FITS::PrimaryGroupHDU: isfitsimg = False; break; case FITS::AsciiTableHDU: isfitsimg = False; break; case FITS::BinaryTableHDU: isfitsimg = False; break; case FITS::UnknownExtensionHDU: hdu = new ExtensionHeaderDataUnit(fin); hdu->skip(); delete hdu; break; default: cout << "This isn't supposed to happen\n"; break; } }else if (fin.rectype() == FITS::BadBeginningRecord || fin.rectype() == FITS::UnrecognizableRecord) { throw (AipsError("FITSImgParser::setup - Bad Record encountered")); }else if (fin.rectype() == FITS::SpecialRecord) { throw (AipsError("FITSImgParser::setup - Special Record encountered")); } } } void FITSImgParser::process_extension(HeaderDataUnit *h,const uInt &extindex) { String extname=""; Int extversion=-1; Bool hasdata=False; uInt actindex=extindex-1; FITSExtInfo fExtInfo; const FitsKeyword *actkeyw; FitsKeywordList kwlist=FitsKeywordList(); // check whether there is data // in the extension; // set the flag and skip to // the next HDU if (h->fitsdatasize()) { hasdata = True; h->skip(); } // get the extension name actkeyw = h->kw("EXTNAME"); if (actkeyw){ extname = actkeyw->asString(); extname.trim(); } // get the extension version actkeyw = h->kw("EXTVER"); if (actkeyw) extversion = actkeyw->asInt(); // get all relevant keywords for (int index=0; index < nKwords_p; index++){ actkeyw = h->kw((char *)storeKwords_p[index]); if (actkeyw){ FitsKeyword *newkey = new FitsKeyword(*actkeyw); kwlist.insert(*newkey); } } // create an Info object and add the keywords fExtInfo = FITSExtInfo(fitsname(True), actindex, extname, extversion, hasdata); fExtInfo.add_kwlist(kwlist); // add the extension information to the list; // enhance the HDU number extensions_p[numhdu_p++] = fExtInfo; } Bool FITSImgParser::get_extlist(const String &extexpr, Vector &extlist){ String extexpr_l = extexpr; extexpr_l.trim(); // there is nothing to do if (extexpr_l.size() < 1) return True; Int open_bracepos = 0; Int close_bracepos= extexpr_l.size(); // check whether the strings ends with "]" if (!extexpr_l.compare(extexpr_l.size()-1, 1, "]", 1)){ close_bracepos = extexpr_l.size()-1; } // check whether the strings ends with "]" if (!extexpr_l.compare(0, 1, "[", 1)){ open_bracepos = 1; close_bracepos -= 1; } String extexpr_b = String(extexpr_l, open_bracepos, close_bracepos); Int n_comma = extexpr_b.freq(","); Int f_start=0; for (Int index=0; index &extindex){ // go over all extensions for (uInt index=0; index -1) // check whether it is a data extension if (index_is_HDUtype(extindex(index), "DATA")) // return the index return extindex(index); } // -1 for not found return -1; } String FITSImgParser::get_errorext(const Int &ext_index){ String error_ext; FitsKeyword *actkeyw; // make sure the extension index does exist if (ext_index < 0 || ext_index > (Int)numhdu_p-1){ ostringstream os; os << ext_index; throw (AipsError("FITSImgParser::get_errorext - Can not access extension: "+String(os)+" in image: " + fitsname(True))); } // extract the keyword "ERRDATA" actkeyw = extensions_p[ext_index].get_keyword(String("ERRDATA")); // check whether the keyword exists if (actkeyw){ // convert the keyword to string String kw_error = String(actkeyw->asString()); kw_error.trim(); kw_error.upcase(); // check whether the HDUtype keyword // has the correct value if (kw_error.size()>0){ Int err_index = find_extension(kw_error); if (err_index > -1 && index_is_HDUtype(err_index, "ERROR")) error_ext=kw_error; } } return error_ext; } String FITSImgParser::get_maskext(const Int &ext_index){ String mask_ext; FitsKeyword *actkeyw; // make sure the extension index does exist if (ext_index < 0 || ext_index > (Int)numhdu_p-1){ ostringstream os; os << ext_index; throw (AipsError("FITSImgParser::get_maskext - Can not access extension: "+String(os)+" in image: " + fitsname(True))); } // extract the keyword "QUALDATA" actkeyw = extensions_p[ext_index].get_keyword(String("QUALDATA")); // check whether the keyword exists if (actkeyw){ // convert the keyword to string String kw_mask = String(actkeyw->asString()); kw_mask.trim(); kw_mask.upcase(); // check whether the HDUtype keyword // has the correct value if (kw_mask.size()>0){ Int mask_index = find_extension(kw_mask); if (mask_index > -1 && index_is_HDUtype(mask_index, "QUALITY")) mask_ext=kw_mask; } } return mask_ext; } Bool FITSImgParser::confirm_fix_keywords(const Int &ext_index){ FitsKeyword *actkeyw; Vector key_words(3), key_values(3); key_words(0) =String("HDUCLASS"); key_words(1) = String("HDUDOC"); key_words(2) = String("HDUCLAS1"); key_values(0)=String("ESO"); key_values(1) = String("DICD"); key_values(2) = String("IMAGE"); //key_words(3) = String("HDUVERS"); key_values(3) = String("DICD version 6"); for (uInt index=0; indexasString()); kword_string.trim(); // compare the keyword value and return true if they are identical if (kword_string.size()<1 || kword_string.compare(key_values(index))) return False; } else{ return False; } } return True; } Bool FITSImgParser::index_is_HDUtype(const Int &ext_index, const String &hdutype){ FitsKeyword *actkeyw; // make sure the extension index does exist if (ext_index < 0 || ext_index > (Int)numhdu_p-1){ ostringstream os; os << ext_index; throw (AipsError("FITSImgParser::index_is_HDUtype - Can not access extension: "+String(os)+" in image: " + fitsname(True))); } // verify the mandatory, fixed keywords if (!confirm_fix_keywords(ext_index)) return False; // extract the keyword "HDUCLAS2" actkeyw = extensions_p[ext_index].get_keyword(String("HDUCLAS2")); // check whether the keyword exists if (actkeyw){ // convert the keyword to string String kw_hdutype = String(actkeyw->asString()); kw_hdutype.trim(); // compare the keyword value and return true if they are identical if (kw_hdutype.size()>0 && !kw_hdutype.compare(hdutype)) return True; } // return False as default return False; } Bool FITSImgParser::find_qualimgs(void) { // go over all extensions for (uInt index=0; index < numhdu_p; index++){ // identify the current extension // as a data extension if (index_is_HDUtype((Int)index, "DATA")){ String errext, maskext; // search the corresponding error extension errext = get_errorext((Int)index); // confirm the existence // of the error extension if (errext.size()>0){ Int err_index = find_extension(errext); if (err_index < 0) errext = String(""); } maskext = String(""); /* Loading a mask does not yet work, * hence the identification makes no sense // search the corresponding mask extension maskext = get_maskext((Int)index); // confirm the existence of the mask extension if (maskext.size()>0){ Int mask_index = find_extension(maskext); if (mask_index < 0) maskext = String(""); } */ // if the data extension has an error or mask extension if (errext.size() > 0 || maskext.size()>0){ // compose the string representation String qualimgstr(extensions_p[index].get_extname()); if (errext.size()>0) qualimgstr += String(",") + errext; if (maskext.size()>0) qualimgstr += String(",") + maskext; // extend the list and append the string representation qualimglist_p.resize(qualimglist_p.size()+1, True); qualimglist_p(qualimglist_p.size()-1)=qualimgstr; } } } return True; } FITSExtInfo::FITSExtInfo(const String &name, const uInt &extindex, const String &extname, const Int &extversion, const Bool &hasdata) : name_p (name), extindex_p (extindex), extname_p (extname), extversion_p (extversion), hasdata_p (hasdata) { // make sure the extension // name is upper case extname_p.upcase(); } // Copy constructor (reference semantics) FITSExtInfo::FITSExtInfo(const FITSExtInfo& other) :name_p(other.name_p), extindex_p(other.extindex_p), extname_p(other.extname_p), extversion_p(other.extversion_p), hasdata_p(other.hasdata_p), kwlist_p(other.kwlist_p) { } // Destructor FITSExtInfo::~FITSExtInfo() { } // Assignment (reference semantics) FITSExtInfo& FITSExtInfo::operator=(const FITSExtInfo& other){ if (this != &other) { name_p = other.name_p; extindex_p = other.extindex_p; extname_p = other.extname_p; extversion_p = other.extversion_p; hasdata_p = other.hasdata_p; kwlist_p = FitsKeywordList(other.kwlist_p); } return *this; } Bool FITSExtInfo::operator==(const FITSExtInfo &extinfo) { if (name_p != extinfo.name_p) return False; if (extinfo.extname_p.length() > 0 && extinfo.extversion_p > -1) { //cout << "Comparing extname and extversion" << endl; if (extname_p == extinfo.extname_p && extversion_p == extinfo.extversion_p) return True; } else if (extinfo.extname_p.length() > 0) { //cout << "Comparing extname" << endl; if (extname_p == extinfo.extname_p) return True; } else { //cout << "Comparing index" << endl; if (extindex_p == extinfo.extindex_p) return True; } return False; } String FITSExtInfo::get_extexpr(void) { String extexpr=name_p + "[" + String::toString(extindex_p); if (extname_p.length() > 0){ extexpr += ':' + extname_p; if (extversion_p > -1){ ostringstream os; os << extversion_p; extexpr += "," + String(os); } } extexpr += "]"; return extexpr; } void FITSExtInfo::add_kwlist(FitsKeywordList &kwlist){ FitsKeyword *actkey; // if there is at least one keyword if (!kwlist.isempty()){ // iterate over the list kwlist.first(); actkey = kwlist.next(); while (actkey){ // copy the current keyword FitsKeyword *newkey = new FitsKeyword(*actkey); kwlist_p.insert(*newkey); // go to the next keyword actkey = kwlist.next(); } } } } //# NAMESPACE CASACORE - END casacore-3.7.1/images/Images/FITSImgParser.h000066400000000000000000000176121476623553700205350ustar00rootroot00000000000000//# FITSImgParser.h: Class for parsing multi-extension FITS images //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_FITSImgParser_H #define IMAGES_FITSImgParser_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class FITSExtInfo; class HeaderDataUnit; // // Class for handling FITS Image extensions // // // // // //
      • FITSExtInfo //
      • HeaderDataUnit // // // This class parses through a FITS image and stores essential information // for each extension. // // // The class parses through a FITS image and extracts information // on its extensions, e.g. the extension name and the extension version. // It is possible to identify a certain extension and to get the its // extension index. // // It is also explored whether some of the FITS extensions can be // loaded as a quality image (data + error + mask). // // // // FITSImgParser fitsImg("in.fits"); // uInt numHDU = fitsImg.get_numhdu(); // get the total number of HDU's // uInt firstdata = fitsImg.get_firstdata_index(); // get the first HDU with data // String allExts = fitsImg.get_extlist_string(String("\n")); // get a string representation of all extensions // String hasQual = fitsImg.has_qualityimg(); // check whether some of the extensions form quality image // // // // Investigate and select FITS extensions // //# //# class FITSImgParser { public: // Construct a parser from the FITS file. FITSImgParser(const String& name); // Copy constructor (reference semantics). FITSImgParser(const FITSImgParser& other); // Destructor, does not much. ~FITSImgParser(); // Assignment (reference semantics). FITSImgParser& operator=(const FITSImgParser& other); // Returns the name of the disk file. String fitsname (Bool stripPath=False) const; // Identify the index of an extension. Int get_index(const FITSExtInfo &extinfo); // Find an extension; return -1 if not found. Int find_extension(const String &extname, const Int &extversion=-1); // Get the index of the first extension with data. uInt get_firstdata_index(void); // Get the number of extensions. uInt get_numhdu(void) { return numhdu_p;}; // Get a string representation of the extension list. String get_extlist_string(const String &delimiter, const String &qualmarker="", const String &fitsmarker="", const Bool &listall=True); // Get the flag indicating at least one quality image. Bool has_qualityimg(void) {return qualimglist_p.size() > 0 ? True : False;}; // Check whether the extensions named in the extension expression // can be loaded as a quality image. Bool is_qualityimg(const String &extexpr); // Find all necessary access information for the extensions to be loaded // as a quality image. Bool get_quality_data(const String &extexpr, Int &data_HDU, Int &error_HDU, String &error_type, Int &mask_HDU, String &mask_type, Int &mask_value); private: String name_p; uInt numhdu_p; FITSExtInfo *extensions_p; Vector qualimglist_p; Bool hasmeasurement_p; static const char *storeKwords_p[]; static const int nKwords_p; // Setup the object (used by constructors). void setup(void); // Get the information on an extension. void process_extension(HeaderDataUnit *h, const uInt &extindex); // Extract the list of extensions from the extension expression. Bool get_extlist(const String &extexpr, Vector &extlist); // Get the first extension with HDU type "data" from the // list of indices. Returns "-1" if there is none. Int get_dataindex(const Vector &extindex); // Get the error extension name for the given data extension. String get_errorext(const Int &ext_index); // Get the mask extension name for the given data extension. String get_maskext(const Int &ext_index); // Check the keywords with fixed values Bool confirm_fix_keywords(const Int &ext_index); // Check whether the extension has a certain HDU type. Bool index_is_HDUtype(const Int &ext_index, const String &hdutype); // Find and store all set of extensions // that can be loaded as a quality image. Bool find_qualimgs(void); }; //class FitsKeywordList; // // Class for storing FITS Image extension information // // // // // // // // The class stores the essential information on a FITS // image extension. // // // The class stores the essential information on a FITS image extension, // which is the FITS file name, the extension name, the extension version, // the index within the FITS file. // // // // // FITSImgParser fitsImg("in.fits"); // FITSExtInfo extinfo("in.fits", 0, "SCI", 1, True); // Int index = fitsImg.get_index(extinfo); // get the index of extension "[SCI, 1]" // // // // // Helper class for accessing multi-extension FITS files. // // //# //# class FITSExtInfo { public: // Construct the object FITSExtInfo(const String &name, const uInt &extindex, const String &extname, const Int &extversion, const Bool &hasdata); // Construct the object FITSExtInfo() { FITSExtInfo("", 0, "", 0, False); }; // Copy constructor (reference semantics) FITSExtInfo(const FITSExtInfo& other); // Destructor does nothing. ~FITSExtInfo(); // Assignment (reference semantics). FITSExtInfo& operator=(const FITSExtInfo& other); // Relational operator. Bool operator==(const FITSExtInfo &extinfo); // All extension information as a string. String get_extexpr(void); // Return the extension name. String get_extname(void){return extname_p;}; // Return the extension version. Int get_extversion(void){return extversion_p;}; // Return whether there is data. Bool has_data(void){return hasdata_p;}; // Add a list of keywords. void add_kwlist(FitsKeywordList &kwlist); // Return a keyword. FitsKeyword *get_keyword(const String kname){return kwlist_p(kname.c_str());}; private: String name_p; uInt extindex_p; String extname_p; Int extversion_p; Bool hasdata_p; FitsKeywordList kwlist_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Images/FITSQualityImage.cc000066400000000000000000000524621476623553700213770ustar00rootroot00000000000000//# FITSQualityImage.cc: Class providing native access to FITS images //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN FITSQualityImage::FITSQualityImage(const String& name) : ImageInterface(), name_p (name), fullname_p (name), fitsdata_p (0), fitserror_p (0), pPixelMask_p (0), whichDataHDU_p (0), whichErrorHDU_p (0), whichMaskHDU_p (0), isClosed_p (False), isDataClosed_p (False), isErrorClosed_p (False) { getExtInfo(); setup(); } FITSQualityImage::FITSQualityImage(const String& name, uInt whichDataHDU, uInt whichErrorHDU) : ImageInterface(), name_p (name), fullname_p (name), fitsdata_p (0), fitserror_p (0), whichDataHDU_p (whichDataHDU), whichErrorHDU_p (whichErrorHDU), whichMaskHDU_p (0), errType_p (FITSErrorImage::DEFAULT), isClosed_p (False), isDataClosed_p (False), isErrorClosed_p (False) { setup(); } FITSQualityImage::FITSQualityImage (const FITSQualityImage& other) : ImageInterface(other), name_p (other.name_p), fitsdata_p (0), fitserror_p (0), shape_p (other.shape_p), whichDataHDU_p (other.whichDataHDU_p), whichErrorHDU_p (other.whichErrorHDU_p), whichMaskHDU_p (other.whichMaskHDU_p), errType_p (other.errType_p), isClosed_p (other.isClosed_p), isDataClosed_p (other.isDataClosed_p), isErrorClosed_p (other.isErrorClosed_p) { if (other.fitsdata_p != 0) { fitsdata_p = dynamic_cast(other.fitsdata_p->cloneII()); } if (other.fitserror_p != 0) { fitserror_p = dynamic_cast(other.fitserror_p->cloneII()); } if (fitsdata_p != 0 && fitserror_p != 0 && fitsdata_p->isMasked()) pPixelMask_p = new FITSQualityMask(fitsdata_p, fitserror_p); } FITSQualityImage& FITSQualityImage::operator=(const FITSQualityImage& other) // // Assignment. Uses reference semantics // { if (this != &other) { ImageInterface::operator= (other); delete fitsdata_p; fitsdata_p = 0; if (other.fitsdata_p != 0) { fitsdata_p = dynamic_cast(other.fitsdata_p->cloneII()); } delete fitserror_p; fitserror_p = 0; if (other.fitserror_p != 0) { fitserror_p = dynamic_cast(other.fitserror_p->cloneII()); } if (fitsdata_p != 0 && fitserror_p != 0 && fitsdata_p->isMasked()) pPixelMask_p = new FITSQualityMask(fitsdata_p, fitserror_p); name_p = other.name_p; shape_p = other.shape_p; whichDataHDU_p = other.whichDataHDU_p; whichErrorHDU_p = other.whichErrorHDU_p; whichMaskHDU_p = other.whichMaskHDU_p; errType_p = other.errType_p; isClosed_p = other.isClosed_p; isDataClosed_p = other.isDataClosed_p; isErrorClosed_p = other.isErrorClosed_p; } return *this; } FITSQualityImage::~FITSQualityImage() { delete fitsdata_p; fitsdata_p=0; delete fitserror_p; fitserror_p=0; delete pPixelMask_p; pPixelMask_p = 0; } ImageInterface* FITSQualityImage::cloneII() const { return new FITSQualityImage (*this); } Bool FITSQualityImage::qualFITSInfo(String &error, TableRecord &dataExtMiscInfo, TableRecord &errorExtMiscInfo, const TableRecord &miscInfo){ String tmpString; // check for a dedicated extension name in the record "sciextname" if (miscInfo.fieldNumber("sciextname")>-1 && miscInfo.type(miscInfo.fieldNumber("sciextname")==TpString)){ // set the given extension name miscInfo.get(String("sciextname"), tmpString); dataExtMiscInfo.define("extname", tmpString); } else { // set the default extension name dataExtMiscInfo.define("extname", "DATA"); } dataExtMiscInfo.setComment("extname", "name of data extension"); // set the data HDU type dataExtMiscInfo.define("hduclass", "ESO"); dataExtMiscInfo.setComment("hduclass", "class name"); dataExtMiscInfo.define("hdudoc", "DICD"); dataExtMiscInfo.setComment("hdudoc", "document with class description"); dataExtMiscInfo.define("hduvers", "DICD version 6"); dataExtMiscInfo.setComment("hduvers", "version number"); dataExtMiscInfo.define("hduclas1", "IMAGE"); dataExtMiscInfo.setComment("hduclas1", "the FITS type described"); dataExtMiscInfo.define("hduclas2", Quality::name(Quality::DATA)); dataExtMiscInfo.setComment("hduclas2", "extension type"); // check for a dedicated extension name in the record "errextname" if (miscInfo.fieldNumber("errextname")>-1 && miscInfo.type(miscInfo.fieldNumber("errextname")==TpString)){ // set the given extension name miscInfo.get(String("errextname"), tmpString); errorExtMiscInfo.define("extname", tmpString); // check for a dedicated extension name in the record "errextname" if (miscInfo.fieldNumber("hduclas3")>-1 && miscInfo.type(miscInfo.fieldNumber("hduclas3")==TpString)){ // read the string miscInfo.get(String("hduclas3"), tmpString); // make sure the chosen error type does exist if (FITSErrorImage::stringToErrorType(tmpString)==FITSErrorImage::UNKNOWN){ error="The error type: " + tmpString + " does not exist!"; return False; } // set the given extension name errorExtMiscInfo.define("hduclas3", tmpString); } else { // set the default error type errorExtMiscInfo.define("hduclas3", FITSErrorImage::errorTypeToString(FITSErrorImage::MSE)); } } else { // set the default extension name and error type errorExtMiscInfo.define("extname", "ERROR"); errorExtMiscInfo.define("hduclas3", FITSErrorImage::errorTypeToString(FITSErrorImage::MSE)); } errorExtMiscInfo.setComment("extname", "name of data extension"); errorExtMiscInfo.setComment("hduclas3", "error type"); // set the error HDU type errorExtMiscInfo.define("hduclass", "ESO"); errorExtMiscInfo.setComment("hduclass", "class name"); errorExtMiscInfo.define("hdudoc", "DICD"); errorExtMiscInfo.setComment("hdudoc", "document with class description"); errorExtMiscInfo.define("hduvers", "DICD version 6"); errorExtMiscInfo.setComment("hduvers", "version number"); errorExtMiscInfo.define("hduclas1", "IMAGE"); errorExtMiscInfo.setComment("hduclas1", "the FITS type described"); errorExtMiscInfo.define("hduclas2", Quality::name(Quality::ERROR)); errorExtMiscInfo.setComment ("hduclas2", "extension type"); // cross-reference between the data and the error extension errorExtMiscInfo.get(String("extname"), tmpString); dataExtMiscInfo.define("errdata", tmpString); dataExtMiscInfo.setComment("errdata", "name of error extension"); dataExtMiscInfo.get(String("extname"), tmpString); errorExtMiscInfo.define("scidata", tmpString); errorExtMiscInfo.setComment("scidata", "name of science data extension"); // add the additional information dataExtMiscInfo.merge(miscInfo, RecordInterface::SkipDuplicates); errorExtMiscInfo.merge(miscInfo, RecordInterface::SkipDuplicates); return True; } String FITSQualityImage::imageType() const { return "FITSQualityImage"; } void FITSQualityImage::resize(const TiledShape&) { throw (AipsError ("FITSQualityImage::resize - a FITSQualityImage is not writable")); } Bool FITSQualityImage::isMasked() const { return fitsdata_p->isMasked(); } Bool FITSQualityImage::hasPixelMask() const { return fitsdata_p->isMasked(); } const Lattice& FITSQualityImage::pixelMask() const { if (!fitsdata_p->isMasked()) { throw (AipsError ("FITSQualityImage::pixelMask - no pixelmask used")); } return *pPixelMask_p; } Lattice& FITSQualityImage::pixelMask() { if (!fitsdata_p->isMasked()) { throw (AipsError ("FITSQualityImage::pixelMask - no pixelmask used")); } return *pPixelMask_p; } const LatticeRegion* FITSQualityImage::getRegionPtr() const { return 0; } Bool FITSQualityImage::doGetSlice(Array& buffer, const Slicer& section) { // get the section dimension IPosition shp = section.length(); uInt ndim=section.ndim(); // resize the buffer if (!buffer.shape().isEqual(shp)) buffer.resize(shp); // set the in all except the last dimension IPosition tmpStart(ndim-1); IPosition tmpEnd(ndim-1); IPosition tmpStride(ndim-1); for (uInt index=0; index subData; Array subError; Array tmp; // prepare the call // for data values IPosition subStart(ndim); IPosition subEnd(ndim); for (uInt index=0; indexdoGetSlice(subData, subSection); tempCloseData(); // insert the retrieved data // into the output buffer tmp.reference(buffer(subStart, subEnd)); tmp=subData.addDegenerate(1); // prepare the call // for error values subStart(ndim-1) = 1; subEnd(ndim-1) = 1; // re-size the buffer if (!subError.shape().isEqual(subSection.length())) subError.resize(subSection.length()); // get the error values reopenErrorIfNeeded(); fitserror_p->doGetSlice(subError, subSection); tempCloseError(); // insert the retrieved data // into the output buffer tmp.reference(buffer(subStart, subEnd)); tmp=subError.addDegenerate(1); } else if (section.start()(ndim-1)==0) { // only data is requested Array subData; Array tmp; // prepare the call // for data values IPosition subStart(ndim); IPosition subEnd(ndim); for (uInt index=0; indexdoGetSlice(subData, subSection); tempCloseData(); // insert the retrieved data // into the output buffer tmp.reference(buffer(subStart, subEnd)); tmp=subData.addDegenerate(1); } else if (section.start()(ndim-1)==1) { // only error values are requested Array subError; Array tmp; // prepare the call // for error values IPosition subStart(ndim, 1); IPosition subEnd(ndim, 1); for (uInt index=0; indexdoGetSlice(subError, subSection); tempCloseError(); // insert the retrieved data // into the output buffer tmp.reference(buffer(subStart, subEnd)); tmp=subError.addDegenerate(1); } return False; } Bool FITSQualityImage::doGetMaskSlice (Array& buffer, const Slicer& section) { if (!fitsdata_p->isMasked()) { buffer.resize (section.length()); buffer = True; return False; } // reopenIfNeeded(); return pPixelMask_p->getSlice (buffer, section); } void FITSQualityImage::doPutSlice (const Array&, const IPosition&, const IPosition&) { throw (AipsError ("FITSQualityImage::putSlice - " "is not possible as FITSQualityImage is not writable")); } Bool FITSQualityImage::isPersistent() const { return True; } Bool FITSQualityImage::isPaged() const { return True; } Bool FITSQualityImage::isWritable() const { // Its too hard to implement putMaskSlice becuase // magic blanking is used. It means we lose // the data values if the mask is put somewhere return False; } String FITSQualityImage::name (Bool stripPath) const { return fitsdata_p->name(stripPath); } IPosition FITSQualityImage::shape() const { return shape_p.shape(); } uInt FITSQualityImage::advisedMaxPixels() const { return shape_p.tileShape().product(); } IPosition FITSQualityImage::doNiceCursorShape (uInt) const { return shape_p.tileShape(); } Bool FITSQualityImage::ok() const { return True; } void FITSQualityImage::tempClose() { //cout << "close!" << endl; if (! isClosed_p) { fitsdata_p->tempClose(); fitserror_p->tempClose(); } } void FITSQualityImage::tempCloseData() { if (! isDataClosed_p) { //cout << "Data closed!" << endl; fitsdata_p->tempClose(); } isDataClosed_p = True; } void FITSQualityImage::tempCloseError() { if (! isErrorClosed_p) { //cout << "Error closed!" << endl; fitserror_p->tempClose(); } isErrorClosed_p = True; } void FITSQualityImage::reopen() { //cout << "reopen!" << endl; if (isClosed_p) { fitsdata_p->reopen(); fitserror_p->reopen(); } } DataType FITSQualityImage::dataType () const { return fitsdata_p->dataType(); } uInt FITSQualityImage::maximumCacheSize() const { reopenIfNeeded(); return fitsdata_p->maximumCacheSize(); } void FITSQualityImage::setMaximumCacheSize (uInt howManyPixels) { reopenIfNeeded(); fitsdata_p->setMaximumCacheSize(howManyPixels); fitserror_p->setMaximumCacheSize(howManyPixels); } void FITSQualityImage::setCacheSizeFromPath (const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath) { reopenIfNeeded(); fitsdata_p->setCacheSizeFromPath(sliceShape, windowStart, windowLength, axisPath); fitserror_p->setCacheSizeFromPath(sliceShape, windowStart, windowLength, axisPath); } void FITSQualityImage::setCacheSizeInTiles (uInt howManyTiles) { reopenIfNeeded(); fitsdata_p->setCacheSizeInTiles(howManyTiles); fitserror_p->setCacheSizeInTiles(howManyTiles); } void FITSQualityImage::clearCache() { if (! isClosed_p) { fitsdata_p->clearCache(); fitserror_p->clearCache(); } } void FITSQualityImage::showCacheStatistics (ostream& os) const { reopenIfNeeded(); os << "FITSQualityImage statistics : "; fitsdata_p->showCacheStatistics(os); fitserror_p->showCacheStatistics(os); } void FITSQualityImage::getExtInfo() { LogIO os(LogOrigin("FITSQualityImage", "getExtInfo", WHERE)); String extexpr; Int whichDataHDU; Int whichErrorHDU; Int whichMaskHDU; Int maskValue; String errTypeStr; String maskTypeStr; // decompose the name into the fits name and extension list name_p = FITSImage::get_fitsname(fullname_p); extexpr = String(fullname_p, name_p.size(), fullname_p.size()-name_p.size()); // open a FITS parser FITSImgParser fip = FITSImgParser(name_p); // make sure that the extension expression // can be loaded as a quality image if (!fip.is_qualityimg(extexpr)) throw (AipsError ("FITSQualityImage::getExtInfo - " "The extensions " + extexpr + " in image: " + name_p + " can not be loaded as quality image!")); // determine all information to be able loading a quality image fip.get_quality_data(extexpr, whichDataHDU, whichErrorHDU, errTypeStr, whichMaskHDU, maskTypeStr, maskValue); // store the data extension, // exit if there is none if (whichDataHDU > -1) whichDataHDU_p = (uInt)whichDataHDU; else throw (AipsError ("FITSQualityImage::getExtInfo - " "No data extension")); // store the error extension, // exit if there is none // Note: Since mask files can not yet loaded, // the error extension is essential to // make a quality image if (whichErrorHDU > -1) whichErrorHDU_p = (uInt)whichErrorHDU; else throw (AipsError ("FITSQualityImage::getExtInfo - " "No error extension")); // convert the keyword value to // an error type if (errTypeStr.size()>0){ errType_p = FITSErrorImage::stringToErrorType(errTypeStr); if (errType_p == FITSErrorImage::UNKNOWN) throw (AipsError ("FITSQualityImage::getExtInfo - " "Unknown ERRTYPE value: " + errTypeStr)); } else{ os << LogIO::WARN << "No proper error type defined in the error extension. Assuming MSE (mean squared error)." << LogIO::POST; } // store the mask extension // notify that the mask extension // is not used. if (whichMaskHDU > -1){ whichMaskHDU_p = whichMaskHDU; os << LogIO::NORMAL << "A dedicated mask extension can not yet be loaded!" << LogIO::POST; } } void FITSQualityImage::setup() { // open the various fits extensions fitsdata_p = new FITSImage(name_p, 0, whichDataHDU_p); fitserror_p = new FITSErrorImage(name_p, 0, whichErrorHDU_p, errType_p); // do some checks on the input images checkInput(); // create the pixel mask pPixelMask_p = new FITSQualityMask(fitsdata_p, fitserror_p); IPosition data_shape=fitsdata_p->shape(); IPosition mm_shape(data_shape.nelements()+1); // set the shape for (uInt index=0; indexcoordinates(); Vector quality(2); quality(0) = Quality::DATA; quality(1) = Quality::ERROR; QualityCoordinate qualAxis(quality); cSys.addCoordinate(qualAxis); // set the coordinate system setCoordsMember(cSys); // set the units setUnitMember(fitsdata_p->units()); // set the image info setImageInfo(fitsdata_p->imageInfo()); // Form the tile shape. shape_p = TiledShape (mm_shape, TiledFileAccess::makeTileShape(mm_shape)); } Bool FITSQualityImage::checkInput(){ // make sure the data end error extensions // are NOT identical if (whichDataHDU_p == whichErrorHDU_p) throw (AipsError("Data and error extensions must be different!")); // make sure the data and error image have the same dimension if (fitsdata_p->shape() != fitserror_p->shape()) throw (AipsError("Data and error image have different shape!")); // make sure the data and error image have the same coordinate system CoordinateSystem dataCSys = fitsdata_p->coordinates(); CoordinateSystem errorCSys = fitserror_p->coordinates(); if (!dataCSys.near(errorCSys, 10e-6)) throw (AipsError("Data and error image have different coordinate system!")); return True; } void FITSQualityImage::reopenIfNeeded() const { if (isClosed_p){ fitsdata_p->reopen(); fitserror_p->reopen(); } } void FITSQualityImage::reopenErrorIfNeeded() { if (isErrorClosed_p){ fitserror_p->reopen(); isErrorClosed_p = False; } } void FITSQualityImage::reopenDataIfNeeded() { if (isDataClosed_p){ fitsdata_p->reopen(); isDataClosed_p = False; } } } //# NAMESPACE CASACORE - END casacore-3.7.1/images/Images/FITSQualityImage.h000066400000000000000000000212021476623553700212250ustar00rootroot00000000000000//# FITSQualityImage.h: Class providing native access to FITS images //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_FITSQUALITYIMAGE_H #define IMAGES_FITSQUALITYIMAGE_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Lattice; // class FITSImage; class FITSQualityMask; class IPosition; class Slicer; // // Class providing native access to FITS Quality Images. // // // // // //
      • FITSImage //
      • FITSErrorImage // // // The class provides access to a quality image via two extensions // in the corresponding FITS file. // // // A FITSQualityImage provides native access to FITS images by accessing // the data and the error values via the classes FITSImage and // FITSErrorImage, respectively. A QualityCoordinate connects these // two layers. The FITSQualityImage is read only. // // // // FITSQualityImage fitsQIStat("im.fits", 1, 2); // LogIO logger(or); // ImageStatistics stats(fitsQIStat, logger); // Bool ok = stats.display(); // // // // This provides access to FITS Quality Images // //# //# class FITSQualityImage: public ImageInterface { public: // Construct a FITSQualityImage from the FITS file name and extensions // specified in the input. explicit FITSQualityImage(const String& name); // Construct a FITSQualityImage from the disk FITS file name and extensions. explicit FITSQualityImage(const String& name, uInt whichDataHDU, uInt whichErrorHDU); // Copy constructor (reference semantics) FITSQualityImage(const FITSQualityImage& other); // Destructor ~FITSQualityImage(); // Assignment (reference semantics). FITSQualityImage& operator=(const FITSQualityImage& other); //# ImageInterface virtual functions // Make a copy of the object with new (reference semantics). virtual ImageInterface* cloneII() const; // Given the misc-info of a CASA image (with quality-axis) // the misc-info of the data sub-image and the error sub-image // are produced. This ensures that, if written to FITS, the // data and error extensions have the all necessary keywords. Bool static qualFITSInfo(String &error, TableRecord &dataExtMiscInfo, TableRecord &errorExtMiscInfo, const TableRecord &miscInfo); // Get the FITS data FITSImage *fitsData() const {return fitsdata_p;}; // Get the FITS error FITSErrorImage *fitsError() const {return fitserror_p;}; // Get the image type (returns FITSImage). virtual String imageType() const; // Function which changes the shape of the FITSQualityImage. // Throws an exception as FITSQualityImage is not writable. virtual void resize(const TiledShape& newShape); // Has the object really a mask? The FITSQualityImage always // has a pixel mask and never has a region mask so this // always returns True virtual Bool isMasked() const; // FITSQualityImage always has a pixel mask so returns True virtual Bool hasPixelMask() const; // Get access to the pixelmask. FITSQualityImage always has a pixel mask. // virtual const Lattice& pixelMask() const; virtual Lattice& pixelMask(); // // Get the region used. There is no region. // Always returns 0. virtual const LatticeRegion* getRegionPtr() const; // Do the actual get of the data. // Returns False as the data do not reference another Array virtual Bool doGetSlice (Array& buffer, const Slicer& theSlice); // The FITSQualityImage is not writable, so this throws an exception. virtual void doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride); // Do the actual get of the mask data. The return value is always // False, thus the buffer does not reference another array. virtual Bool doGetMaskSlice (Array& buffer, const Slicer& section); //# LatticeBase virtual functions // The lattice is paged to disk. virtual Bool isPaged() const; // The lattice is persistent. virtual Bool isPersistent() const; // The FITSImage is not writable. virtual Bool isWritable() const; // Returns the name of the disk file. virtual String name (Bool stripPath=False) const; // Return the shape of the FITSImage. virtual IPosition shape() const; // Returns the maximum recommended number of pixels for a cursor. This is // the number of pixels in a tile. virtual uInt advisedMaxPixels() const; // Help the user pick a cursor for most efficient access if they only want // pixel values and don't care about the order or dimension of the // cursor. virtual IPosition doNiceCursorShape (uInt maxPixels) const; // Check class invariants. virtual Bool ok() const; // Temporarily close the image. virtual void tempClose(); virtual void tempCloseData(); virtual void tempCloseError(); // Reopen a temporarily closed image. virtual void reopen(); // Return the (internal) data type (TpFloat or TpShort). DataType dataType () const; // Return the data HDU number uInt whichDataHDU () const { return whichDataHDU_p; } // Return the error HDU number uInt whichErrorHDU () const { return whichErrorHDU_p; } // Maximum size - not necessarily all used. In pixels. virtual uInt maximumCacheSize() const; // Set the maximum (allowed) cache size as indicated. virtual void setMaximumCacheSize (uInt howManyPixels); // Set the cache size as to "fit" the indicated path. virtual void setCacheSizeFromPath (const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath); // Set the actual cache size for this Array to be be big enough for the // indicated number of tiles. This cache is not shared with PagedArrays // in other rows and is always clipped to be less than the maximum value // set using the setMaximumCacheSize member function. // tiles. Tiles are cached using a first in first out algorithm. virtual void setCacheSizeInTiles (uInt howManyTiles); // Clears and frees up the caches, but the maximum allowed cache size is // unchanged from when setCacheSize was called virtual void clearCache(); // Report on cache success. virtual void showCacheStatistics (ostream& os) const; private: String name_p; String fullname_p; FITSImage *fitsdata_p; FITSErrorImage *fitserror_p; Lattice *pPixelMask_p; TiledShape shape_p; uInt whichDataHDU_p; uInt whichErrorHDU_p; uInt whichMaskHDU_p; FITSErrorImage::ErrorType errType_p; Bool isClosed_p; Bool isDataClosed_p; Bool isErrorClosed_p; // Reopen the image if needed. void reopenIfNeeded() const; void reopenDataIfNeeded(); void reopenErrorIfNeeded(); // Get the extension indices from an // extension expression. void getExtInfo(); // Setup the object (used by constructors). void setup(); // Make sure the input is compatible. Bool checkInput(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Images/FITSQualityMask.cc000066400000000000000000000166621476623553700212520ustar00rootroot00000000000000//# FITSMask.cc: an on-the-fly mask for FITS images //# Copyright (C) 1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN FITSQualityMask::FITSQualityMask(FITSImage *fitsData, FITSErrorImage *fitsError) : itsFitsData(fitsData), itsFitsError(fitsError), itsFilterZero(False) { // check the shapes are equal! AlwaysAssert(itsFitsData->shape()==itsFitsError->shape(), AipsError); } FITSQualityMask::FITSQualityMask (const FITSQualityMask& other) : Lattice(other), itsFitsData(other.itsFitsData), itsFitsError(other.itsFitsError), itsFilterZero(other.itsFilterZero) { } FITSQualityMask::~FITSQualityMask() {} FITSQualityMask& FITSQualityMask::operator= (const FITSQualityMask& other) { if (this != &other) { itsFitsData = other.itsFitsData; itsFitsError = other.itsFitsError; itsBuffer.resize(); itsBuffer = other.itsBuffer.copy(); itsFilterZero = other.itsFilterZero; } return *this; } Lattice* FITSQualityMask::clone() const { return new FITSQualityMask (*this); } Bool FITSQualityMask::isWritable() const { return False; } IPosition FITSQualityMask::shape() const { IPosition data_shape=itsFitsData->shape(); IPosition mm_shape(data_shape.nelements()+1); // set the shape for (uInt index=0; index& buffer, const Slicer& section) { // get the section dimension IPosition shp = section.length(); uInt ndim=section.ndim(); // resize the buffer if (!buffer.shape().isEqual(shp)) buffer.resize(shp); // set the in all except the last dimension IPosition tmpStart(ndim-1); IPosition tmpEnd(ndim-1); IPosition tmpStride(ndim-1); for (uInt index=0; index subData; Array subError; Array tmp; // prepare the call // for data mask IPosition subStart(ndim); IPosition subEnd(ndim); for (uInt index=0; indexdoGetMaskSlice(subData, subSection); //tempCloseData(); // insert the retrieved data // into the output buffer tmp.reference(buffer(subStart, subEnd)); tmp=subData.addDegenerate(1); // prepare the call // for error mask values subStart(ndim-1) = 1; subEnd(ndim-1) = 1; // re-size the buffer if (!subError.shape().isEqual(subSection.length())) subError.resize(subSection.length()); // get the error mask //reopenErrorIfNeeded(); itsFitsError->doGetMaskSlice(subError, subSection); //tempCloseError(); // insert the retrieved data // into the output buffer tmp.reference(buffer(subStart, subEnd)); tmp=subError.addDegenerate(1); } else if (section.start()(ndim-1)==0) { // only data is requested Array subData; Array tmp; // prepare the call // for data mask values IPosition subStart(ndim); IPosition subEnd(ndim); for (uInt index=0; indexdoGetMaskSlice(subData, subSection); //tempCloseData(); // insert the retrieved data // into the output buffer tmp.reference(buffer(subStart, subEnd)); tmp=subData.addDegenerate(1); } else if (section.start()(ndim-1)==1) { // only error is requested Array subError; Array tmp; // prepare the call // for error mask values IPosition subStart(ndim); IPosition subEnd(ndim); for (uInt index=0; indexdoGetMaskSlice(subError, subSection); //tempCloseError(); // insert the retrieved data // into the output buffer tmp.reference(buffer(subStart, subEnd)); tmp=subError.addDegenerate(1); } /* maybe this is needeed? // Apply the according filtering if (!itsFilterZero) ok = filterNaN(pMask, pData, mask.nelements()); else ok = filterZeroNaN(pMask, pData, mask.nelements()); itsBuffer.freeStorage(pData, deletePtrD); mask.putStorage(pMask, deletePtrM); */ return False; // Not a reference } void FITSQualityMask::doPutSlice (const Array&, const IPosition&, const IPosition&) { throw(AipsError("FITSQualityMask object is not writable")); } void FITSQualityMask::setFilterZero(Bool filterZero) { itsFilterZero = filterZero; } Bool FITSQualityMask::filterNaN(Bool *pMask, const Float *pData, const uInt nelems) { // loop over all elements for (uInt i=0; i #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class FITSImage; class FITSErrorImage; // // Provides an on-the-fly mask for FITS quality images // // // // // //
      • Lattice //
      • FITSQualityImage // // // This class provides a pixel mask for the FITSQualityImage class. // // // Masked values are indicated in FITS images via magic // value blanking. This class provides an on-the-fly mask. // The doGetSlice function reads the data values and returns // an Array which is True (good) or False (bad - blanked) // // Because FITSMask inherits from Lattice it can be // used as the private pixel mask data member for FITSQualityImage // returned by the MaskedLattice::pixelMask() functions // // The FITSQualityMask object is constructed from the FITSImage objects // of the data and the error extension. These must be the same one that // the FITSQUalityImage object constructs internally. They shared by both // FITSImage and FITSMask. // // // // // // // // // FITSQualityImage provides access to FITS images with a data and and error // extension. It needed an efficient way to handle the pixel mask // other than iterating all the way through the image // first to set a mask. // //# //#
      • add this feature //#
      • fix this bug //#
      • start discussion of this possible extension //# class FITSQualityMask : public Lattice { public: // The pointers are not cloned, just copied. FITSQualityMask (FITSImage *fitsData, FITSErrorImage *fitsError); // Copy constructor (reference semantics). FITSQualityMask (const FITSQualityMask& other) ; // Destructor virtual ~FITSQualityMask(); // The assignment operator with reference semantics. FITSQualityMask& operator= (const FITSQualityMask& other); // Make a copy of the object (reference semantics). virtual Lattice* clone() const; // Is the FITSMask writable? Returns False. Although it is not hard // to implement writing of the mask, data values would be lost // because of magic blanking. virtual Bool isWritable() const; // Return the shape of the Lattice including all degenerate // axes (ie. axes with a length of one) IPosition shape() const; // Do the actual getting of an array of values. virtual Bool doGetSlice (Array& buffer, const Slicer& section); // Do the actual getting of an array of values. Throws an exception. virtual void doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride); // Set the switch for filtering 0.0 virtual void setFilterZero(Bool filterZero); private: FITSQualityMask(); // Mask out ONLY NaN's Bool filterNaN(bool* pMask, const float* pData, const uInt nelems); // Mask out NaN's and values 0.0 Bool filterZeroNaN(Bool* pMask, const Float* pData, const uInt nelems); // FITSImage *itsFitsData; FITSErrorImage *itsFitsError; Array itsBuffer; Bool itsFilterZero; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Images/HDF5Image.h000066400000000000000000000326061476623553700176070ustar00rootroot00000000000000//# HDF5Image.h: astronomical image in HDF5 format //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_HDF5IMAGE_H #define IMAGES_HDF5IMAGE_H //# Includes #include #include #include #include #include //# Forward Declarations #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Read, store, and manipulate astronomical images in HDF5 format. // // // // // //
      • CoordinateSystem //
      • ImageInterface //
      • Lattice //
      • LatticeIterator //
      • LatticeNavigator //
      • ImageRegion // // // The HDF5Image name comes from its role as the Image class using HDF5. // // // All Casacore Images are Lattices. They may be treated like any other Lattice; // getSlice(...), putSlice(...), LatticeIterator for iterating, etc... // ArrayImages contain a map, a mask for that map, and coordinate // information. This provides a Lattice interface for images and their // respective coordinates. Additional functionality is defined by the // ImageInterface class. // // You can use the global function imagePixelType to determine // what the pixel type of an image is before you open the image if your // code can work with Images of many possible types, or for error checking. // // // // This example shows how to create a mask for an image, fill it, and // make it known to the image. // // // Open the image (as readonly for the moment). // HDF5Image myimage ("image.name"); // // Create a mask for the image. // // The mask will be stored in a subtable of the image. // LCPagedMask mask (RegionHandler::makeMask (myimage, "mask.name")); // // Fill the mask with whatever values (e.g. all True). // mask.set (True); // // Make the mask known to the image (with name mask1). // myimage.defineRegion ("mask1", mask, RegionHandler::Masks); // // Make the mask the default mask for this image. // myimage.setDefaultMask ("mask1"); // // It is possible to create as many masks as one likes. They can all // be defined as masks for the image (with different names, of course). // However, only one of them can be the default mask (the mask used // by default when the image is opened). When another mask has to be // used, one can do two things: //
          //
        • Use setDefaultMask to make the other mask the default mask. // This is advisable when the change should be more or less permanent. //
        • Open the HDF5Image without using a default mask. Thereafter // a SubImage object can be created // from the HDF5Image and the mask. This is advisable when it the // mask has to be used only one time. //
        //
        // // The size of astronomical data can be very large. The ability to fit an // entire image into random access memory cannot be guaranteed. Paging from // disk pieces of the image appeared to be the way to deal with this problem. // // // When you make a new HDF5Image, and you are transferring // information from some other HDF5Image, be aware that you // must copy, manually, things like miscInfo, imageInfo, units, // logSink (history) to the new file. // template class HDF5Image: public ImageInterface { public: // Construct a new Image from shape and coordinate information. The image // will be stored in the named file. HDF5Image (const TiledShape& mapShape, const CoordinateSystem& coordinateInfo, const String& nameOfNewFile); // Reconstruct an image from a pre-existing file. // By default the default pixelmask (if available) is used. explicit HDF5Image (const String& fileName, MaskSpecifier = MaskSpecifier()); // Copy constructor (reference semantics). HDF5Image (const HDF5Image& other); ~HDF5Image(); // Assignment operator (reference semantics). HDF5Image& operator= (const HDF5Image& other); // Make a copy of the object (reference semantics). virtual ImageInterface* cloneII() const; // Get the image type (returns name of derived class). virtual String imageType() const; // Return the current HDF5 file name. By default this includes the full path. // The path preceding the file name can be stripped off on request. virtual String name (Bool stripPath=False) const; // Function which changes the shape of the ImageExpr. // Throws an exception as an HDF5Image cannot be resized. virtual void resize(const TiledShape& newShape); // Check for symmetry in data members. virtual Bool ok() const; // Return the shape of the image. virtual IPosition shape() const; // Function which extracts an array from the map. virtual Bool doGetSlice (Array& buffer, const Slicer& theSlice); // Function to replace the values in the map with soureBuffer. virtual void doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride); // Get a pointer the default pixelmask object used with this image. // It returns 0 if no default pixelmask is used. virtual const LatticeRegion* getRegionPtr() const; // An HDF5Image is always persistent. virtual Bool isPersistent() const; // An HDF5Image is always paged to disk. virtual Bool isPaged() const; // Is the HDF5Image writable? virtual Bool isWritable() const; // Does the image object use a pixelmask? virtual Bool hasPixelMask() const; // Get access to the pixelmask used. // An exception is thrown if the image does not use a pixelmask. // virtual const Lattice& pixelMask() const; virtual Lattice& pixelMask(); // // Set the default pixelmask to the mask with the given name // (which has to exist in the "masks" group). // If the image file is writable, the setting is persistent by writing // the name as a keyword. // If the given mask name is the empty string, // the default pixelmask is unset. virtual void setDefaultMask (const String& maskName); // Use the mask as specified. // If a mask was already in use, it is replaced by the new one. virtual void useMask (MaskSpecifier = MaskSpecifier()); // Replace every element, x, of the lattice with the result of f(x). // you must pass in the address of the function -- so the function // must be declared and defined in the scope of your program. // Both versions of apply require a function that accepts a single // argument of type T (the Lattice template actual type) and returns // a result of the same type. The first apply expects a function with // an argument passed by value; the second expects the argument to // be passed by const reference. The first form ought to run faster // for the built-in types, which may be an issue for large Lattices // stored in memory, where disk access is not an issue. // virtual void apply (T (*function)(T)); virtual void apply (T (*function)(const T& )); virtual void apply (const Functional& function); // // Add a lattice to this image. HDF5Image& operator+= (const Lattice& other); // Function which sets the units associated with the image // pixels (i.e. the "brightness" unit). setUnits() returns // False if it cannot set the unit for some reason (e.g. the underlying // file is not writable). virtual Bool setUnits (const Unit& newUnits); // Flushes the new coordinate system to disk if the file is writable. virtual Bool setCoordinateInfo (const CoordinateSystem& coords); // These are the true implementations of the paran operator. // Not for public use // virtual T getAt (const IPosition& where) const; virtual void putAt (const T& value, const IPosition& where); // // Replace the miscinfo in the HDF5Image. // It can fail if, e.g., the underlying file is not writable. virtual Bool setMiscInfo (const RecordInterface& newInfo); // The ImageInfo object contains some miscellaneous information about the // image, which unlike that stored in MiscInfo, has a standard list of // things, such as the restoring beam. // Note that setImageInfo REPLACES the information with the new information. // It can fail if, e.g., the underlying file is not writable. virtual Bool setImageInfo(const ImageInfo& info); // Get access to the attribute handler. // If a handler keyword does not exist yet, it is created if // createHandler is set. // Otherwise the handler is empty and no groups can be created for it. virtual ImageAttrHandler& attrHandler (Bool createHandler=False); // Remove a region/mask belonging to the image from the given group // (which can be Any). // If a mask removed is the default mask, the image gets unmasked. //
        Optionally an exception is thrown if the region does not exist. virtual void removeRegion (const String& name, RegionHandler::GroupType = RegionHandler::Any, Bool throwIfUnknown = True); // This is the implementation of the letter for the envelope Iterator // class. Not for public use . virtual LatticeIterInterface* makeIter (const LatticeNavigator& navigator, Bool useRef) const; // Returns the maximum recommended number of pixels for a cursor. This is // the number of pixels in a tile. virtual uInt advisedMaxPixels() const; // Help the user pick a cursor for most efficient access. virtual IPosition doNiceCursorShape (uInt maxPixels) const; // Flush the data. virtual void flush(); private: // Function to return the internal HDF5File object to the RegionHandler. static const std::shared_ptr& getFile (void* imagePtr); // This must be called in every constructor and place where the image // is attached to a new image. void attach_logtable(); void open_logtable(); void restoreUnits (const RecordInterface& rec); void restoreMiscInfo (const RecordInterface& rec); void restoreImageInfo (const RecordInterface& rec); void restoreAll(); void check_conformance (const Lattice& other); void applyMaskSpecifier (const MaskSpecifier&); void applyMask (const String& maskName); //# Data members. HDF5Lattice map_p; LatticeRegion* regionPtr_p; ImageAttrHandlerHDF5 itsAttrHandler; //# Make members of parent class known. public: using ImageInterface::logSink; using ImageInterface::logger; using ImageInterface::imageInfo; using ImageInterface::coordinates; using ImageInterface::getDefaultMask; using ImageInterface::hasRegion; using ImageInterface::getImageRegionPtr; protected: using ImageInterface::setCoordsMember; using ImageInterface::setMiscInfoMember; using ImageInterface::setLogMember; using ImageInterface::setUnitMember; using ImageInterface::setImageInfoMember; }; // Tell if HDF5 images can be used. inline Bool canUseHDF5Image() { return HDF5Object::hasHDF5Support(); } // Determine the pixel type in the HDF5Image contained in // fileName. If the file doesn't appear to be HDF5 or cannot // be opened, TpOther is returned. // } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/images/Images/HDF5Image.tcc000066400000000000000000000336761476623553700201410ustar00rootroot00000000000000//# HDF5Image.tcc: defines the HDF5Image class //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_HDF5IMAGE_TCC #define IMAGES_HDF5IMAGE_TCC #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template HDF5Image::HDF5Image (const TiledShape& shape, const CoordinateSystem& coordinateInfo, const String& fileName) : ImageInterface(RegionHandlerHDF5(getFile, this)), regionPtr_p (0) { map_p = HDF5Lattice(shape, fileName, "map", "/"); attach_logtable(); AlwaysAssert(setCoordinateInfo(coordinateInfo), AipsError); } template HDF5Image::HDF5Image (const String& fileName, MaskSpecifier spec) : ImageInterface(RegionHandlerHDF5(getFile, this)), regionPtr_p (0) { map_p = HDF5Lattice(fileName, "map", "/"); attach_logtable(); restoreAll(); applyMaskSpecifier (spec); } template HDF5Image::HDF5Image (const HDF5Image& other) : ImageInterface(other), map_p (other.map_p), regionPtr_p (0) { if (other.regionPtr_p != 0) { regionPtr_p = new LatticeRegion (*other.regionPtr_p); } } template HDF5Image::~HDF5Image() { flush(); delete regionPtr_p; } template HDF5Image& HDF5Image::operator=(const HDF5Image& other) { if (this != &other) { ImageInterface::operator= (other); map_p = other.map_p; delete regionPtr_p; regionPtr_p = 0; if (other.regionPtr_p != 0) { regionPtr_p = new LatticeRegion (*other.regionPtr_p); } } return *this; } template ImageInterface* HDF5Image::cloneII() const { return new HDF5Image (*this); } template String HDF5Image::imageType() const { return "HDF5Image"; } template Bool HDF5Image::isPersistent() const { return True; } template Bool HDF5Image::isPaged() const { return True; } template Bool HDF5Image::isWritable() const { return map_p.isWritable(); } template Bool HDF5Image::hasPixelMask() const { return (regionPtr_p != 0 && regionPtr_p->hasMask()); } template const Lattice& HDF5Image::pixelMask() const { if (regionPtr_p == 0) { throw (AipsError ("HDF5Image::pixelMask - no pixelmask used")); } return *regionPtr_p; } template Lattice& HDF5Image::pixelMask() { if (regionPtr_p == 0) { throw (AipsError ("HDF5Image::pixelMask - no pixelmask used")); } return *regionPtr_p; } template const LatticeRegion* HDF5Image::getRegionPtr() const { return regionPtr_p; } template void HDF5Image::setDefaultMask (const String& regionName) { // Use the new region as the image's mask. applyMask (regionName); // Store the new default name. ImageInterface::setDefaultMask (regionName); } template void HDF5Image::useMask (MaskSpecifier spec) { applyMaskSpecifier (spec); } template void HDF5Image::applyMaskSpecifier (const MaskSpecifier& spec) { // Use default mask if told to do so. // If there is no default, use no mask. String name = spec.name(); if (spec.useDefault()) { name = getDefaultMask(); if (! hasRegion (name, RegionHandler::Masks)) { name = String(); } } applyMask (name); } template void HDF5Image::applyMask (const String& maskName) { // No region if no mask name is given. if (maskName.empty()) { delete regionPtr_p; regionPtr_p = 0; return; } // Reconstruct the ImageRegion object. // Turn the region into lattice coordinates. ImageRegion* regPtr = getImageRegionPtr (maskName, RegionHandler::Masks); LatticeRegion* latReg = new LatticeRegion (regPtr->toLatticeRegion (coordinates(), shape())); delete regPtr; // The mask has to cover the entire image. if (latReg->shape() != shape()) { delete latReg; throw (AipsError ("HDF5Image::setDefaultMask - region " + maskName + " does not cover the full image")); } // Replace current by new mask. delete regionPtr_p; regionPtr_p = latReg; } template String HDF5Image::name (Bool stripPath) const { return map_p.name (stripPath); } template IPosition HDF5Image::shape() const { return map_p.shape(); } template void HDF5Image::resize (const TiledShape&) { throw (AipsError ("HDF5Image::resize - an HDF5Image cannot be resized")); } template Bool HDF5Image::doGetSlice(Array& buffer, const Slicer& theSlice) { return map_p.doGetSlice(buffer, theSlice); } template void HDF5Image::doPutSlice(const Array& sourceBuffer, const IPosition& where, const IPosition& stride) { // if (throughmask_p || !mask_p) { map_p.putSlice(sourceBuffer,where,stride); // } else if (mask_p) { // Array map; //Array mask; //IPosition shape(sourceBuffer.shape()); //mask_p->getSlice(mask, where, shape, stride, True); //map_p.getSlice(map, where, shape, stride, True); // use maskedarrays to do all the work. //map(mask==False) = sourceBuffer; //map_p.putSlice(map,where,stride); // } else { // throw(AipsError("HDF5Image::putSlice - throughmask==False but no " // "mask exists.")); // } } // apply a function to all elements of the map template void HDF5Image::apply(T (*function)(T)) { map_p.apply(function); } // apply a function to all elements of a const map; template void HDF5Image::apply(T (*function)(const T&)) { map_p.apply(function); } template void HDF5Image::apply(const Functional& function) { map_p.apply(function); } template T HDF5Image::getAt(const IPosition& where) const { return map_p(where); } template void HDF5Image::putAt(const T& value, const IPosition& where) { map_p.putAt (value, where); } template LatticeIterInterface* HDF5Image::makeIter (const LatticeNavigator& navigator, Bool useRef) const { return map_p.makeIter (navigator, useRef); } template Bool HDF5Image::ok() const { return (map_p.ndim() == coordinates().nPixelAxes()); } template HDF5Image& HDF5Image::operator+= (const Lattice& other) { check_conformance(other); LatticeExpr expr(*this + other); this->copyData (expr); return *this; } template const std::shared_ptr& HDF5Image::getFile (void* imagePtr) { HDF5Image* im = static_cast*>(imagePtr); return im->map_p.file(); } template void HDF5Image::attach_logtable() { open_logtable(); } template void HDF5Image::open_logtable() { // No log table (yet?). } template void HDF5Image::restoreAll() { // Restore the coordinates. Record rec = HDF5Record::readRecord (*map_p.group(), "coordinfo"); CoordinateSystem* restoredCoords = CoordinateSystem::restore(rec, "coords"); AlwaysAssert(restoredCoords != 0, AipsError); setCoordsMember (*restoredCoords); delete restoredCoords; // Restore the image info. rec = HDF5Record::readRecord (*map_p.group(), "imageinfo"); restoreImageInfo (rec); // Restore the units. rec = HDF5Record::readRecord (*map_p.group(), "unitinfo"); restoreUnits (rec); // Restore the miscinfo. rec = HDF5Record::readRecord (*map_p.group(), "miscinfo"); restoreMiscInfo (rec); // Restore the mask/region info. dynamic_cast(this->getRegionHandler())->restore(); } template Bool HDF5Image::setCoordinateInfo (const CoordinateSystem& coords) { Bool ok = ImageInterface::setCoordinateInfo(coords); if (ok) { Record rec; AlwaysAssert (coordinates().save(rec, "coords"), AipsError); HDF5Record::writeRecord (*map_p.group(), "coordinfo", rec); } return ok; } template void HDF5Image::restoreMiscInfo (const RecordInterface& rec) { setMiscInfoMember (rec); } template Bool HDF5Image::setMiscInfo (const RecordInterface& newInfo) { setMiscInfoMember (newInfo); HDF5Record::writeRecord (*map_p.group(), "miscinfo", newInfo); return True; } template Bool HDF5Image::setUnits(const Unit& newUnits) { setUnitMember (newUnits); Record rec; rec.define("units", newUnits.getName()); HDF5Record::writeRecord (*map_p.group(), "unitinfo", rec); return True; } template void HDF5Image::restoreUnits (const RecordInterface& rec) { Unit retval; String unitName; if (rec.isDefined("units")) { if (rec.dataType("units") != TpString) { LogIO os; os << LogOrigin("HDF5Image", "units()", WHERE) << "'units' keyword in hdf5image is not a string! Units not restored." << LogIO::SEVERE << LogIO::POST; } else { rec.get("units", unitName); } } if (! unitName.empty()) { // OK, non-empty unit, see if it's valid, if not try some known things to // make a valid unit out of it. if (! UnitVal::check(unitName)) { // Beam and Pixel are the most common undefined units UnitMap::putUser("Pixel",UnitVal(1.0),"Pixel unit"); UnitMap::putUser("Beam",UnitVal(1.0),"Beam area"); } if (! UnitVal::check(unitName)) { // OK, maybe we need FITS UnitMap::addFITS(); } if (!UnitVal::check(unitName)) { LogIO os; os << LogOrigin("HDF5Image", "units()", WHERE) << LogIO::SEVERE << "Unit '" << unitName << "' is unknown. Not restoring units" << LogIO::POST; } else { retval = Unit(unitName); } } setUnitMember (retval); } template Bool HDF5Image::setImageInfo (const ImageInfo& info) { Bool ok = ImageInterface::setImageInfo(info); if (ok) { // Update the ImageInfo Record rec; String error; if (imageInfo().toRecord(error, rec)) { HDF5Record::writeRecord (*map_p.group(), "imageinfo", rec); } else { LogIO os; os << LogIO::SEVERE << "Error saving ImageInfo in record because " << error << LogIO::POST; ok = False; } } return ok; } template void HDF5Image::restoreImageInfo (const RecordInterface& rec) { String error; ImageInfo info; Bool ok = info.fromRecord (error, rec); if (!ok) { LogIO os; os << LogIO::WARN << "Failed to restore the ImageInfo because " << error << LogIO::POST; } else { setImageInfoMember (info); } } template void HDF5Image::removeRegion (const String& name, RegionHandler::GroupType type, Bool throwIfUnknown) { // Remove the default mask if it is the region to be removed. if (name == getDefaultMask()) { setDefaultMask (String()); } ImageInterface::removeRegion (name, type, throwIfUnknown); } template void HDF5Image::check_conformance(const Lattice& other) { if (! this->conform(other)) { throw AipsError("Shapes of image " + name() + " and other lattice do not conform"); } } template uInt HDF5Image::advisedMaxPixels() const { return map_p.advisedMaxPixels(); } template IPosition HDF5Image::doNiceCursorShape(uInt maxPixels) const { return map_p.niceCursorShape(maxPixels); } template void HDF5Image::flush() { map_p.flush(); logger().flush(); if (regionPtr_p != 0) { regionPtr_p->flush(); } itsAttrHandler.flush(); // Save the mask/region info. dynamic_cast(this->getRegionHandler())->save(); } template ImageAttrHandler& HDF5Image::attrHandler (Bool createHandler) { return itsAttrHandler.attachHid (*map_p.group(), createHandler, map_p.isWritable()); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Images/HDF5Image2.cc000066400000000000000000000042031476623553700200170ustar00rootroot00000000000000//# HDF5Image2.cc: non-templated function in HDF5Image //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN DataType hdf5imagePixelType (const String& fileName) { DataType retval = TpOther; if (HDF5File::isHDF5(fileName)) { try { HDF5File file(fileName); HDF5Group gid(file, "/", true); retval = HDF5DataSet::getDataType (gid.getHid(), "map"); } catch (std::exception& x) { // Nothing } } return retval; } Bool isHDF5Image (const String& fileName) { // It is an image if it is an HDF5 file with group /coordinfo. Bool retval = False; if (HDF5File::isHDF5(fileName)) { try { HDF5File file(fileName); HDF5Group gid1(file, "/", true); HDF5Group gid2(gid1, "coordinfo", true); retval = True; } catch (std::exception& x) { // Nothing } } return retval; } } //# NAMESPACE CASACORE - END casacore-3.7.1/images/Images/ImageAttrGroup.cc000066400000000000000000000025721476623553700212050ustar00rootroot00000000000000//# ImageAttrGroup.cc: Abstract base class for an image attributes group //# Copyright (C) 2012 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include namespace casacore { ImageAttrGroup::~ImageAttrGroup() {} } //# NAMESPACE CASACORE - END casacore-3.7.1/images/Images/ImageAttrGroup.h000066400000000000000000000143531476623553700210470ustar00rootroot00000000000000//# ImageAttrGroup.h: Abstract base class for an image attributes group //# Copyright (C) 2012 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_IMAGEATTRGROUP_H #define IMAGES_IMAGEATTRGROUP_H //# Includes #include #include #include namespace casacore { // // Abstract base class for an image attributes group. // // // // // //
      • ImageInterface // // // This class makes it possible to store extra attributes with an image to // describe atrbitrary meta information. // // // For LOFAR it is needed to store extra meta information and be possible to // convert it from casacore table format to HDF5 format and vice-versa. // Furthermore, it must be possible to access the information in a way that // arbitrary attributes can be stored and retrieved in a way that is agnostic // to the format the image is stored in. It must also work fine for an image // stored in FITS format, be it that such an image cannot have such attributes. // // The attributes are divided into groups. A group resides in a subtable // of a casacore image or in a group of an HDF5 image. This class handles // the attributes of a group. It can get and put the attribute values, as well // as their unit and measure info (type and reference frame type). // For HDF5 images the unit is stored in attribute attrname>_UNIT // and the measure info in attrname>_MEASINFO. For casacore images // that info is stored as TableMeasure info in the column keywords. // // All attributes in a group must have the same number of values, where each // value can be a scalar or (small) array. The unit and measure info have // only one value, thus all values of an attribute have the same unit. // // // This example shows how to get attributes from an image. // // // Open the image. // PagedImage myimage ("image.name"); // // Open the attribute handler. // ImageAttrHandler& attrHandler = myimage.attrHandler(); // // Get access to attibute group LOFAR_SOURCE. // ImageAttrGroup& lofarSource = attrHandler.openGroup ("LOFAR_SOURCE"); // // Get the names of all attributes in this group. // Vector attrNames = lofarSource.attrNames(); // // Get the value of the ATTRNAME attribute (if there). // if (lofarSource.hasAttr ("ATTRNAME)) { // ValueHolder vh (lofarSource.getData ("ATTRNAME")); // Vector name = vh.asString(); // } // // The following example shows how to add a group and attribute. // // // Open the image. // PagedImage myimage ("image.name"); // // Open the attribute handler. // ImageAttrHandler& attrHandler = myimage.attrHandler(); // // Add attribute group LOFAR_SOURCE. // ImageAttrGroup& lofarSource = attrHandler.createGroup (LOFAR_SOURCE); // // Add an attribute which has unit Hz. // // The value has 2 values (e.g. for 2 frequency bands). // Vector freqs(2); // freqs[0]=4.5e7; freqs[1]=5.5e7; // lofarSource.putData ("CENTER_FREQ", ValueHolder(freqs), // Vector // // // // LOFAR needed functionality to store arbitrary attributes. // class ImageAttrGroup { public: // Default constructor. ImageAttrGroup() {} virtual ~ImageAttrGroup(); // Get the number of rows in the group. virtual uInt nrows() const = 0; // Test if an attribute exists. virtual Bool hasAttr (const String& attrName) const = 0; // Get all attribute names. virtual Vector attrNames() const = 0; // Get the datatype of a attribute. // It returns TpOther if the attribute is not defined. virtual DataType dataType (const String& attrName) const = 0; // Get the data of the given attribute in the given row virtual ValueHolder getData (const String& attrName, uInt rownr) = 0; // Get the data of all attributes in a rows. virtual Record getDataRow (uInt rownr) = 0; // Get the possible units of the values. // An empty vector is returned if the attribute has no units. virtual Vector getUnit (const String& attrName) = 0; // Get the possible measure info as type and Ref. // An empty vector is returned if the attribute has no MEASINFO. virtual Vector getMeasInfo (const String& attrName) = 0; // Put the data of the given attribute in the given row. // If the row or attribute is new, it will be added. Note that the // new row must be directly after the last row in the group. //
        If not empty, the units and MEASINFO will be put as column keywords. // The MEASINFO vector must be given as type,Ref. virtual void putData (const String& attrName, uInt rownr, const ValueHolder& data, const Vector& units = Vector(), const Vector& measInfo = Vector()) = 0; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Images/ImageAttrGroupCasa.cc000066400000000000000000000167341476623553700220020ustar00rootroot00000000000000//# ImageAttrGroupCasa.cc: Attribute group for a CASA image //# Copyright (C) 2012 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include namespace casacore { ImageAttrGroupCasa::ImageAttrGroupCasa (const Table& image, const String& attrName) : itsTable (image.keywordSet().subRecord("ATTRGROUPS").asTable(attrName)) {} ImageAttrGroupCasa::~ImageAttrGroupCasa() { flush(); } void ImageAttrGroupCasa::flush() { if (! itsTable.table().isNull()) { itsTable.flush (True); } } uInt ImageAttrGroupCasa::nrows() const { return itsTable.table().nrow(); } Bool ImageAttrGroupCasa::hasAttr (const String& attrName) const { return itsTable.table().tableDesc().isColumn(attrName); } Vector ImageAttrGroupCasa::attrNames() const { return itsTable.table().tableDesc().columnNames(); } DataType ImageAttrGroupCasa::dataType (const String& attrName) const { const TableDesc& tdesc = itsTable.table().tableDesc(); if (tdesc.isColumn(attrName)) { return tdesc[attrName].dataType(); } return TpOther; } ValueHolder ImageAttrGroupCasa::getData (const String& attrName, uInt rownr) { ValueHolder value (itsTable.getCell (attrName, rownr)); if (value.isNull()) { value = ValueHolder (Array()); } return value; } Record ImageAttrGroupCasa::getDataRow (uInt rownr) { ROTableRow tabrow (itsTable.table()); // Transform TableRecord to Record. return ValueHolder(tabrow.get(rownr)).asRecord(); } Vector ImageAttrGroupCasa::getUnit (const String& attrName) { TableColumn col(itsTable.table(), attrName); if (col.keywordSet().isDefined("QuantumUnits")) { return col.keywordSet().asArrayString("QuantumUnits"); } return Vector(); } Vector ImageAttrGroupCasa::getMeasInfo (const String& attrName) { TableColumn col(itsTable.table(), attrName); if (col.keywordSet().isDefined("MEASINFO")) { Vector info(2); const TableRecord& rec = col.keywordSet().subRecord("MEASINFO"); info[0] = rec.asString("type"); info[1] = rec.asString("Ref"); return info; } return Vector(); } void ImageAttrGroupCasa::putData (const String& attrName, uInt rownr, const ValueHolder& data, const Vector& units, const Vector& measInfo) { itsTable.reopenRW(); // If needed, add the column for the attribute. if (addNewColumn (attrName, data)) { // Units and MEASINFO are supposed to be the same for all rows, // so only put them for the first time, thus if the column has been added. TableColumn col(itsTable.table(), attrName); if (!units.empty()) { itsTable.putKeyword (attrName, "QuantumUnits", -1, False, ValueHolder(units)); } if (!measInfo.empty()) { AlwaysAssert (measInfo.size() == 2, AipsError); // Define MEASINFO if not defined yet. if (! col.rwKeywordSet().isDefined("MEASINFO")) { TableRecord rec; col.rwKeywordSet().defineRecord ("MEASINFO", rec); } itsTable.putKeyword (attrName, "MEASINFO.type", -1, False, ValueHolder(measInfo[0])); itsTable.putKeyword (attrName, "MEASINFO.Ref", -1, False, ValueHolder(measInfo[1])); } } checkRows (attrName, rownr); itsTable.putCell (attrName, Vector(1,rownr), data); } void ImageAttrGroupCasa::checkRows (const String& attrName, uInt rownr) { uInt nrow = itsTable.nrows(); // A new row can only be added right after the last row. if (rownr > nrow) { throw AipsError("ImageAttrGroupCasa: row " + String::toString(rownr) + " of attribute " + attrName + " cannot be added; beyond current #rows " + String::toString(nrow)); } if (rownr == nrow) { itsTable.addRow(1); } } Bool ImageAttrGroupCasa::addNewColumn (const String& attrName, const ValueHolder& data) { Table& tab = itsTable.table(); if (tab.tableDesc().isColumn(attrName)) { // Column already exists. return False; } // Add the column with the correct type. // Assume arrays can have varying shapes. IPosition colShape(1,1); switch (data.dataType()) { case TpBool: tab.addColumn (ScalarColumnDesc(attrName)); break; case TpArrayBool: tab.addColumn (ArrayColumnDesc (attrName)); break; case TpChar: case TpUChar: case TpShort: case TpUShort: case TpInt: case TpUInt: tab.addColumn (ScalarColumnDesc(attrName)); break; case TpArrayInt: tab.addColumn (ArrayColumnDesc (attrName)); break; case TpFloat: tab.addColumn (ScalarColumnDesc(attrName)); break; case TpArrayFloat: tab.addColumn (ArrayColumnDesc (attrName)); break; case TpDouble: tab.addColumn (ScalarColumnDesc(attrName)); break; case TpArrayDouble: tab.addColumn (ArrayColumnDesc (attrName)); break; case TpComplex: tab.addColumn (ScalarColumnDesc(attrName)); break; case TpArrayComplex: tab.addColumn (ArrayColumnDesc (attrName)); break; case TpDComplex: tab.addColumn (ScalarColumnDesc(attrName)); break; case TpArrayDComplex: tab.addColumn (ArrayColumnDesc (attrName)); break; case TpString: tab.addColumn (ScalarColumnDesc(attrName)); break; case TpArrayString: tab.addColumn (ArrayColumnDesc (attrName)); break; default: throw AipsError("ImageAttrGroupCasa::addNewColumn: Unknown datatype " + String::toString(data.dataType())); } return True; } } //# NAMESPACE CASACORE - END casacore-3.7.1/images/Images/ImageAttrGroupCasa.h000066400000000000000000000105011476623553700216260ustar00rootroot00000000000000//# ImageAttrGroupCasa.h: Attribute group for a CASA image //# Copyright (C) 2012 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_IMAGEATTRGROUPCASA_H #define IMAGES_IMAGEATTRGROUPCASA_H //# Includes #include #include #include namespace casacore { // // Attribute group for a CASA image. // // // // // // // // //
      • ImageAttrGroup // // // // This is the implementation of base class class ImageAttrGroup for an image // stored in the casacore table format. // See the base class for more information. // class ImageAttrGroupCasa : public ImageAttrGroup { public: // The default constructor creates a null object. ImageAttrGroupCasa() {} // Construct the object for an attribute group in the image table. // Note that the group name is the name of a subtable containing the info. ImageAttrGroupCasa (const Table& image, const String& attrGroupName); virtual ~ImageAttrGroupCasa(); // Test if it is a null object. Bool isNull() const { return itsTable.table().isNull(); } // Flush the attibrutes if needed. void flush(); // Get the number of rows in the group. virtual uInt nrows() const; // Test if an attribute exists. virtual Bool hasAttr (const String& attrName) const; // Get all attribute names. virtual Vector attrNames() const; // Get the datatype of a attribute. // It returns TpOther if the attribute is not defined. virtual DataType dataType (const String& attrName) const; // Get the data of the given attribute. virtual ValueHolder getData (const String& attrName, uInt rownr); // Get the data of all attributes in a rows. virtual Record getDataRow (uInt rownr); // Get the possible units of the values. // An empty vector is returned if the attribute has no units. virtual Vector getUnit (const String& attrName); // Get the possible measure info as type and Ref. // An empty vector is returned if the attribute has no MEASINFO. virtual Vector getMeasInfo (const String& attrName); // Put the data of the given attribute. // If the table does not contain data yet, it will be sized to the size // of the vector. Otherwise the vector size has to match the table size. //
        If not empty, the units and MEASINFO will be put as column keywords. // The MEASINFO vector must be given as type,Ref. virtual void putData (const String& attrName, uInt rownr, const ValueHolder& data, const Vector& units = Vector(), const Vector& measInfo = Vector()); private: // Check if the size matches the number of rows. // Add rows if the table is still empty. void checkRows (const String& attrName, uInt size); // Add a new column for the given attribute for the data type in the value. Bool addNewColumn (const String& attrName, const ValueHolder&); //# Data members. TableProxy itsTable; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Images/ImageAttrGroupHDF5.cc000066400000000000000000000136631476623553700216170ustar00rootroot00000000000000//# ImageAttrGroupHDF5.cc: Attribute group for a HDF5 image //# Copyright (C) 2012 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include namespace casacore { ImageAttrGroupHDF5::ImageAttrGroupHDF5 (const HDF5Group& image, const String& attrName, Bool isWritable) : itsChanged (False), itsCanWrite (isWritable) { itsRecord = HDF5Record::readRecord (image, attrName); } ImageAttrGroupHDF5::~ImageAttrGroupHDF5() {} void ImageAttrGroupHDF5::flush (HDF5Group& image, const String& attrName) { if (itsChanged) { HDF5Record::writeRecord (image, attrName, itsRecord); itsChanged = False; } } uInt ImageAttrGroupHDF5::nrows() const { return itsRecord.nfields(); } Bool ImageAttrGroupHDF5::hasAttr (const String& attrName) const { if (itsRecord.empty()) { return False; } return itsRecord.subRecord(0).isDefined (attrName); } Vector ImageAttrGroupHDF5::attrNames() const { if (itsRecord.empty()) { return Vector(); } const Record& subRecord = itsRecord.subRecord(0); Vector names(subRecord.size()); uInt nr = 0; for (uInt i=0; i= 5 && name.substr(name.size()-5) == "_UNIT") || (name.size() >= 9 && name.substr(name.size()-9) == "_MEASINFO"))){ names[nr++] = subRecord.name(i); } } names.resize (nr, True); return names; } DataType ImageAttrGroupHDF5::dataType (const String& attrName) const { if (itsRecord.empty()) { return TpOther; } const Record& subRecord = itsRecord.subRecord(0); if (subRecord.isDefined (attrName)) { return subRecord.dataType (attrName); } return TpOther; } ValueHolder ImageAttrGroupHDF5::getData (const String& attrName, uInt rownr) { if (rownr >= itsRecord.nfields()) { throw AipsError("ImageAttrGroupHDF5: rownr " + String::toString(rownr) + " does not exist"); } const Record& subRecord = itsRecord.subRecord(rownr); return subRecord.asValueHolder (attrName); } Record ImageAttrGroupHDF5::getDataRow (uInt rownr) { if (rownr >= itsRecord.nfields()) { throw AipsError("ImageAttrGroupHDF5: rownr " + String::toString(rownr) + " does not exist"); } return itsRecord.subRecord(rownr); } Vector ImageAttrGroupHDF5::getUnit (const String& attrName) { if (! itsRecord.empty()) { const Record& subRecord = itsRecord.subRecord(0); if (subRecord.isDefined (attrName + "_UNIT")) { return subRecord.asArrayString(attrName + "_UNIT"); } } return Vector(); } Vector ImageAttrGroupHDF5::getMeasInfo (const String& attrName) { if (! itsRecord.empty()) { const Record& subRecord = itsRecord.subRecord(0); if (subRecord.isDefined (attrName + "_MEASINFO")) { return subRecord.asArrayString(attrName + "_MEASINFO"); } } return Vector(); } void ImageAttrGroupHDF5::putData (const String& attrName, uInt rownr, const ValueHolder& data, const Vector& units, const Vector& measInfo) { if (!itsCanWrite) { throw AipsError("ImageAttrGroupHDF5: attribute data cannot be written"); } checkRows(attrName, rownr); Record& subRecord = itsRecord.rwSubRecord(rownr); subRecord.defineFromValueHolder (attrName, data); if (!units.empty()) { subRecord.define (attrName + "_UNIT", units); } if (!measInfo.empty()) { AlwaysAssert (measInfo.size() == 2, AipsError); subRecord.define (attrName + "_MEASINFO", measInfo); } itsChanged = True; } String makeRowName (uInt rownr) { ostringstream ostr; ostr << std::setfill('0') << std::setw(5) << rownr; return ostr.str(); } void ImageAttrGroupHDF5::checkRows (const String& attrName, uInt rownr) { uInt nrow = itsRecord.nfields(); // A new row can only be added right after the last row. if (rownr > nrow) { throw AipsError("ImageAttrGroupHDF5: row " + String::toString(rownr) + " of attribute " + attrName + " cannot be added; beyond current #rows " + String::toString(nrow)); } if (rownr == nrow) { itsRecord.defineRecord (makeRowName(rownr), Record()); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/images/Images/ImageAttrGroupHDF5.h000066400000000000000000000110121476623553700214430ustar00rootroot00000000000000//# ImageAttrGroupHDF5.h: Attribute group for a HDF5 image //# Copyright (C) 2012 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_IMAGEATTRGROUPHDF5_H #define IMAGES_IMAGEATTRGROUPHDF5_H //# Includes #include #include #include #include namespace casacore { // // Attribute group for a HDF5 image. // // // // // // // // //
      • ImageAttrGroup // // // // This is the implementation of base class class ImageAttrGroup for an image // stored in the HDF5 format. // See the base class for more information. // class ImageAttrGroupHDF5 : public ImageAttrGroup { public: // The default constructor creates a null object. explicit ImageAttrGroupHDF5 (Bool isWritable=False) : itsChanged (False), itsCanWrite (isWritable) {} // Construct the object for an attribute group in the image. // If present, it reads all attributes. ImageAttrGroupHDF5 (const HDF5Group& image, const String& attrGroupName, Bool writable); virtual ~ImageAttrGroupHDF5(); // Test if it is a null object. Bool isNull() const { return itsRecord.empty(); } // Flush the attibrutes if needed. void flush (HDF5Group& image, const String& attrGroupName); // Get the number of rows in the group. virtual uInt nrows() const; // Test if an attribute exists. virtual Bool hasAttr (const String& attrName) const; // Get all attribute names. virtual Vector attrNames() const; // Get the datatype of a attribute. // It returns TpOther if the attribute is not defined. virtual DataType dataType (const String& attrName) const; // Get the data of the given attribute in the given row. virtual ValueHolder getData (const String& attrName, uInt rownr); // Get the data of all attributes in a rows. virtual Record getDataRow (uInt rownr); // Get the possible units of the values (stored as attrName_UNIT). // An empty vector is returned if the attribute has no units. virtual Vector getUnit (const String& attrName); // Get the possible measure info as type,Ref (stored as attrName_MEASINFO). // An empty vector is returned if the attribute has no MEASINFO. virtual Vector getMeasInfo (const String& attrName); // Put the data of the given attribute. // If the table does not contain data yet, it will be sized to the size // of the vector. Otherwise the vector size has to match the table size. //
        If not empty, the units and MEASINFO will be put as column keywords. // The MEASINFO vector must be given as type,Ref. virtual void putData (const String& attrName, uInt rownr, const ValueHolder& data, const Vector& units = Vector(), const Vector& measInfo = Vector()); private: // Check the rownr and add a row if needed. void checkRows (const String& attrName, uInt rownr); //# Data members. Record itsRecord; //# Record containing all attributes (subrecord per row) Bool itsChanged; //# Has the Record changed? Bool itsCanWrite; //# Can attributes be written? }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Images/ImageAttrHandler.cc000066400000000000000000000041611476623553700214620ustar00rootroot00000000000000//# ImageAttrHandler.cc: Abstract base class for an image attributes handler //# Copyright (C) 2012 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include namespace casacore { ImageAttrHandler::~ImageAttrHandler() { flush(); } void ImageAttrHandler::flush() {} Bool ImageAttrHandler::hasGroup (const String&) { return False; } Vector ImageAttrHandler::groupNames() const { return Vector(); } ImageAttrGroup& ImageAttrHandler::openGroup (const String& groupName) { throw AipsError("ImageAttrHandler: openGroup " + groupName + " does not exist"); } ImageAttrGroup& ImageAttrHandler::createGroup (const String& groupName) { throw AipsError("ImageAttrHandler: creation of group " + groupName + " cannot be done"); } void ImageAttrHandler::closeGroup (const String&) {} } //# NAMESPACE CASACORE - END casacore-3.7.1/images/Images/ImageAttrHandler.h000066400000000000000000000105151476623553700213240ustar00rootroot00000000000000//# ImageAttrHandler.h: Abstract base class for an image attributes handler //# Copyright (C) 2012 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_IMAGEATTRHANDLER_H #define IMAGES_IMAGEATTRHANDLER_H //# Includes #include #include #include #include namespace casacore { // // Abstract base class for an image attributes handler. // // // // // //
      • ImageInterface // // // This class makes it possible to store extra attributes with an image to // describe atrbitrary meta information. // // // For LOFAR it was needed to store extra meta information and to be able to // convert it from casacore table format to HDF5 format and vice-versa. // Furthermore, it must be possible to access the information in a way that // arbitrary info can be stored and retrieved. // // An ImageAttrHandler object handles those attributes in an image. Specific // handler classes exist for images stored in casacore and in HDF5 format. // The attributes are divided into group which are handled by ImageAttrGroup. // A group (e.g. LOFAR_SOURCES) maps to a subtable in casacore format and a // group in HDF5 format. // // // This example shows how to get attributes from an image. // // // Open the image (done as read/write when having write access). // PagedImage myimage ("image.name"); // // Get access to the attibute handler. // ImageAttrHandler& attrHandler = myimage.attrHandler(); // // Get the names of all attribute groups. // Vector groupNames = attrHandler.groupNames(); // // Create a new group and define an attribute defining Freq in Hz. // ImageAttrGroup& newGroup = attrHandler.createGroup ("NEW_GROUP"); // newGroup.putAttr ("Freq", ValueHolder(Vector(1, 1e7)), // Vector(1,"Hz")); // // // // // LOFAR needed functionality to store arbitrary attributes. // class ImageAttrHandler { public: // Default constructor. ImageAttrHandler() {} virtual ~ImageAttrHandler(); // Flush the attibrutes if needed. // The default implementation does nothing. virtual void flush(); // Test if the given attribute group is present. // The default implementation returns False. virtual Bool hasGroup (const String& name); // Get all attribute group names. // The default implementation returns an empty vector. virtual Vector groupNames() const; // Get access to a group. // The default implementation throws an exception. virtual ImageAttrGroup& openGroup (const String& groupName); // Create an attribute group with the given name. // The default implementation throws an exception. virtual ImageAttrGroup& createGroup (const String& groupName); // Close the group with the given name. // The default implementation does nothing. virtual void closeGroup (const String& groupName); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Images/ImageAttrHandlerCasa.cc000066400000000000000000000117101476623553700222500ustar00rootroot00000000000000//# ImageAttrHandlerCasa.cc: Attributes handler for CASA images //# Copyright (C) 2012 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include using namespace std; namespace casacore { ImageAttrHandlerCasa::ImageAttrHandlerCasa() : itsCanAdd (False) {} ImageAttrHandlerCasa::~ImageAttrHandlerCasa() {} void ImageAttrHandlerCasa::flush() { for (map::iterator it=itsGroupMap.begin(); it!=itsGroupMap.end(); ++it) { it->second.flush(); } } ImageAttrHandlerCasa& ImageAttrHandlerCasa::attachTable (const Table& image, Bool createHandler) { itsImageTable = image; itsGroupMap.clear(); // If ATTRGROUPS is defined, get all subtables (groups) in it. if (itsImageTable.keywordSet().isDefined("ATTRGROUPS")) { const TableRecord& rec = itsImageTable.keywordSet().subRecord("ATTRGROUPS"); for (uInt i=0; i ImageAttrHandlerCasa::groupNames() const { Vector names(itsGroupMap.size()); uInt i=0; for (map::const_iterator it=itsGroupMap.begin(); it!=itsGroupMap.end(); ++it) { names[i++] = it->first; } return names; } ImageAttrGroup& ImageAttrHandlerCasa::openGroup (const String& groupName) { map::iterator pos = itsGroupMap.find (groupName); if (pos == itsGroupMap.end()) { throw AipsError("ImageAttrHandlerCasa: group " + groupName + " does not exist"); } if (pos->second.isNull()) { // Open the subtable. pos->second = ImageAttrGroupCasa(itsImageTable, groupName); } return pos->second; } ImageAttrGroup& ImageAttrHandlerCasa::createGroup (const String& groupName) { if (hasGroup(groupName)) { throw AipsError("ImageAttrHandlerCasa: group " + groupName + " cannot be created; it already exists"); } // Assert that a group can be added. if (!itsCanAdd) { throw AipsError("ImageAttrHandlerCasa: cannot create group " + groupName + " because table keyword ATTRGROUPS does not exist"); } // Make write possible. itsImageTable.reopenRW(); // Create an empty subtable for the new group. SetupNewTable newtab(itsImageTable.tableName() + '/' + groupName, TableDesc(), Table::New); Table tab(newtab); tab.flush(); // Define the keyword holding the group. TableRecord& keyset = itsImageTable.rwKeywordSet(); TableRecord& rec = keyset.rwSubRecord("ATTRGROUPS"); rec.defineTable (groupName, tab); return itsGroupMap[groupName] = ImageAttrGroupCasa(itsImageTable,groupName); } void ImageAttrHandlerCasa::closeGroup (const String& groupName) { map::iterator pos = itsGroupMap.find (groupName); if (pos != itsGroupMap.end()) { pos->second.flush(); pos->second = ImageAttrGroupCasa(); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/images/Images/ImageAttrHandlerCasa.h000066400000000000000000000110601476623553700221100ustar00rootroot00000000000000//# ImageAttrHandlerCasa.h: Attributes handler for CASA images //# Copyright (C) 2012 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_IMAGEATTRHANDLERCASA_H #define IMAGES_IMAGEATTRHANDLERCASA_H //# Includes #include #include #include #include namespace casacore { // // Abstract base class for an image attributes handler. // // // // // //
      • ImageInterface // // // This class makes it possible to store extra attributes with an image to // describe atrbitrary meta information. // // // For LOFAR it was needed to store extra meta information and to be able to // convert it from casacore table format to HDF5 format and vice-versa. // Furthermore, it must be possible to access the information in a way that // arbitrary info can be stored and retrieved. // // The attributes are divided into handlers. Each handler can reside in a subtable // of the image or in a handler in HDF5. All attributes in a handler have the // same number of values, where each value can be a scalar or (small) array. // It is possible to define units and measure info for an attribute. // // // This example shows how to get attributes from an image. // make it known to the image. // // // Open the image (as readonly for the moment). // PagedImage myimage ("image.name"); // // Get access to attibute handler LOFAR_SOURCE. // ImageExtrAttr& = myimage.attrHandler ("LOFAR_SOURCE"); // // Get the data for some field. // Vector names = ImageExtrAttr->getString("NAME"); // // // // // LOFAR needed functionality to store arbitrary attributes. // class ImageAttrHandlerCasa : public ImageAttrHandler { public: // Default construct from the image table. ImageAttrHandlerCasa(); // Attach the table and return this object. // It looks for the table keyword ATTRGROUPS which contains the subtables // defining the attribute groups. // If the keyword does not exist, it will be added if createHandler // is set. // Otherwise the handler is an empty one and no groups can be added to it. ImageAttrHandlerCasa& attachTable (const Table& image, Bool createHandler = False); virtual ~ImageAttrHandlerCasa(); // Flush the attibrutes if needed. virtual void flush(); // Test if the given attribute group is present. virtual Bool hasGroup (const String& name); // Get all attribute group names. virtual Vector groupNames() const; // Get access to a group. virtual ImageAttrGroup& openGroup (const String& groupName); // Create an attribute group with the given name. virtual ImageAttrGroup& createGroup (const String& groupName); // Close the group with the given name. It will flush its attributes. // Nothing is done if it is not open. virtual void closeGroup (const String& groupName); private: Bool itsCanAdd; //# can groups be added? Table itsImageTable; //# Table object of image std::map itsGroupMap; //# attribute groups }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Images/ImageAttrHandlerHDF5.cc000066400000000000000000000112361476623553700220720ustar00rootroot00000000000000//# ImageAttrHandlerHDF5.cc: Attributes handler for HDF5 images //# Copyright (C) 2012 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include using namespace std; namespace casacore { ImageAttrHandlerHDF5::ImageAttrHandlerHDF5() : itsCanWrite (False) {} ImageAttrHandlerHDF5::~ImageAttrHandlerHDF5() {} ImageAttrHandlerHDF5& ImageAttrHandlerHDF5::attachHid (const HDF5Object& hid, Bool createHandler, Bool isWritable) { itsGroupMap.clear(); // If ATTRGROUPS is defined, get all groups in it. if (HDF5Group::exists (hid, "ATTRGROUPS")) { itsGroup = std::make_shared(hid, "ATTRGROUPS", true); vector names = HDF5Group::linkNames (*itsGroup); for (uInt i=0; i(hid, "ATTRGROUPS", false); itsCanWrite = True; } return *this; } void ImageAttrHandlerHDF5::flush() { for (map::iterator it=itsGroupMap.begin(); it!=itsGroupMap.end(); ++it) { it->second.flush (*itsGroup, it->first); } } Bool ImageAttrHandlerHDF5::hasGroup (const String& groupName) { return (itsGroupMap.find(groupName) != itsGroupMap.end()); } Vector ImageAttrHandlerHDF5::groupNames() const { Vector names(itsGroupMap.size()); uInt i=0; for (map::const_iterator it=itsGroupMap.begin(); it!=itsGroupMap.end(); ++it) { names[i++] = it->first; } return names; } ImageAttrGroup& ImageAttrHandlerHDF5::openGroup (const String& groupName) { map::iterator pos = itsGroupMap.find (groupName); if (pos == itsGroupMap.end()) { throw AipsError("ImageAttrHandlerHDF5: group " + groupName + " does not exist"); } if (pos->second.isNull()) { // Read the group. pos->second = ImageAttrGroupHDF5(*itsGroup, groupName, itsCanWrite); } return pos->second; } ImageAttrGroup& ImageAttrHandlerHDF5::createGroup (const String& groupName) { if (hasGroup(groupName)) { throw AipsError("ImageAttrHandlerHDF5: group " + groupName + " cannot be created; it already exists"); } // Assert that a group can be created. if (!itsCanWrite) { throw AipsError("ImageAttrHandlerHDF5: cannot create group " + groupName + " because image is not writable"); } return itsGroupMap[groupName] = ImageAttrGroupHDF5 (True); } void ImageAttrHandlerHDF5::closeGroup (const String& groupName) { map::iterator pos = itsGroupMap.find (groupName); if (pos != itsGroupMap.end()) { pos->second.flush (*itsGroup, groupName); pos->second = ImageAttrGroupHDF5(); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/images/Images/ImageAttrHandlerHDF5.h000066400000000000000000000120531476623553700217320ustar00rootroot00000000000000//# ImageAttrHandlerHDF5.h: Attributes handler for HDF5 images //# Copyright (C) 2012 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_IMAGEATTRHANDLERHDF5_H #define IMAGES_IMAGEATTRHANDLERHDF5_H //# Includes #include #include #include #include #include namespace casacore { // // Abstract base class for an image attributes handler. // // // // // //
      • ImageInterface // // // This class makes it possible to store extra attributes with an image to // describe atrbitrary meta information. // // // For LOFAR it was needed to store extra meta information and to be able to // convert it from casacore table format to HDF5 format and vice-versa. // Furthermore, it must be possible to access the information in a way that // arbitrary info can be stored and retrieved. // // The attributes are divided into handlers. Each handler can reside in a subtable // of the image or in a handler in HDF5. All attributes in a handler have the // same number of values, where each value can be a scalar or (small) array. // It is possible to define units and measure info for an attribute. // // // This example shows how to get attributes from an image. // make it known to the image. // // // Open the image (as readonly for the moment). // PagedImage myimage ("image.name"); // // Get access to attibute handler LOFAR_SOURCE. // ImageExtrAttr& = myimage.attrHandler ("LOFAR_SOURCE"); // // Get the data for some field. // Vector names = ImageExtrAttr->getString("NAME"); // // // // // LOFAR needed functionality to store arbitrary attributes. // class ImageAttrHandlerHDF5 : public ImageAttrHandler { public: // Default construct from the image table. ImageAttrHandlerHDF5(); // Attach the table and return this object. // It looks for the table keyword ATTRGROUPS which contains the subtables // defining the attribute groups. // If the keyword does not exist, it will be added if createHandler // is set. // Otherwise the handler is an empty one and no groups can be added to it. ImageAttrHandlerHDF5& attachTable (const Table& image, Bool createHandler = False); virtual ~ImageAttrHandlerHDF5(); // Attach the hid and return this object. // It looks for the group ATTRGROUPS which contains groups // defining the attribute groups. // If the group does not exist, it will be added if createHandler // is set. // Otherwise the handler is an empty one and no groups can be added to it. ImageAttrHandlerHDF5& attachHid (const HDF5Object& hid, Bool createHandler, Bool isWritable); // Flush the attibrutes if needed. virtual void flush(); // Test if the given attribute group is present. virtual Bool hasGroup (const String& name); // Get all attribute group names. virtual Vector groupNames() const; // Get access to a group. virtual ImageAttrGroup& openGroup (const String& groupName); // Create an attribute group with the given name. virtual ImageAttrGroup& createGroup (const String& groupName); // Close the group with the given name. It will flush its attributes. // Nothing is done if it is not open. virtual void closeGroup (const String& groupName); private: Bool itsCanWrite; //# writable? std::shared_ptr itsGroup; //# HDF5 group to add to std::map itsGroupMap; //# attribute groups }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Images/ImageBeamSet.cc000066400000000000000000000561641476623553700206040ustar00rootroot00000000000000//# Copyright (C) 1995,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include namespace casacore { const String ImageBeamSet::_DEFAULT_AREA_UNIT = "arcsec2"; ImageBeamSet::ImageBeamSet() : _beams (0, 0), _areaUnit (_DEFAULT_AREA_UNIT), _minBeam (GaussianBeam::NULL_BEAM), _maxBeam (GaussianBeam::NULL_BEAM), _minBeamPos (2, 0), _maxBeamPos(2, 0) {} ImageBeamSet::ImageBeamSet(const Matrix& beams) : _beams(beams) { ThrowIf( beams.empty(), "The number of channels and/or the number of stokes in " "the beams matrix is zero, which is not permitted" ); _calculateAreas(); } ImageBeamSet::ImageBeamSet(const GaussianBeam& beam) : _beams (1, 1, beam), _areas (1, 1, beam.getArea(_DEFAULT_AREA_UNIT)), _areaUnit (_DEFAULT_AREA_UNIT), _minBeam (beam), _maxBeam (beam), _minBeamPos (2, 0), _maxBeamPos (2, 0) {} ImageBeamSet::ImageBeamSet( uInt nchan, uInt nstokes, const GaussianBeam& beam ) : _beams(max(1u, nchan), max(1u, nstokes), beam), _areas (_beams.shape(), beam.getArea(_DEFAULT_AREA_UNIT)), _areaUnit (_DEFAULT_AREA_UNIT), _minBeam (beam), _maxBeam (beam), _minBeamPos (2, 0), _maxBeamPos (2, 0) {} ImageBeamSet::ImageBeamSet(const ImageBeamSet& other) : _beams (other._beams), _areas (other._areas), _areaUnit (other._areaUnit), _minBeam (other._minBeam), _maxBeam (other._maxBeam), _minBeamPos (other._minBeamPos), _maxBeamPos (other._maxBeamPos) {} ImageBeamSet::~ImageBeamSet() {} ImageBeamSet& ImageBeamSet::operator=(const ImageBeamSet& other) { if (this != &other) { _beams.assign(other._beams); _areas.assign(other._areas); _areaUnit = other._areaUnit; _minBeam = other._minBeam; _maxBeam = other._maxBeam; _minBeamPos = other._minBeamPos; _maxBeamPos = other._maxBeamPos; } return *this; } const GaussianBeam& ImageBeamSet::getBeam(Int chan, Int stokes) const { if (nchan() <= 1) { chan = 0; } if (nstokes() <= 1) { stokes = 0; } // Note that chan=-1 can only be given if nchan()==1. AlwaysAssert( chan >=0 && chan < Int(nchan()) && stokes >= 0 && stokes < Int(nstokes()), AipsError ); return _beams(chan, stokes); } Bool ImageBeamSet::operator==(const ImageBeamSet& other) const { return ( this == &other || ( _beams.shape() == other._beams.shape() && allEQ(_beams, other._beams) ) ); } Bool ImageBeamSet::operator!=(const ImageBeamSet& other) const { return !(*this == other); } const GaussianBeam& ImageBeamSet::getBeam() const { if (size() > 1) { throw AipsError( String(className()) + "::" + __FUNCTION__ + ": This object contains multiple beams, " "not a single beam" ); } else if (empty()) { throw AipsError( String(className()) + "::" + __FUNCTION__ + ": This object is empty." ); } return _beams(0, 0); } const String& ImageBeamSet::className() { static const String c = "ImageBeamSet"; return c; } void ImageBeamSet::resize(uInt nchan, uInt nstokes) { _beams.resize(max(1u, nchan), max(1u, nstokes)); _calculateAreas(); } void ImageBeamSet::setBeams(const Matrix& beams) { // Resize the beams if needed. // A beam axis can be extended if its length is 0 or 1. Int nch = nchan(); Int beamNchan = beams.shape()[0]; if (nch <= 1) { nch = beamNchan; } Int nst = nstokes(); Int beamNstokes = beams.shape()[1]; if (nst <= 1) { nst = beams.shape()[1]; } AlwaysAssert( (beamNchan == nch || beamNchan == 1) && (beamNstokes == nst || beamNstokes == 1), AipsError ); // Determine the step size in the given beams. Int incrChan = (beamNchan == 1 ? 0 : 1); Int incrStokes = (beamNstokes == 1 ? 0 : 1); // Set the beam set to the given beams. _beams.resize(nch, nst); Int js = 0; for (Int is = 0; is < nst; ++is, js += incrStokes) { Int jc = 0; for (Int ic = 0; ic < nch; ++ic, jc += incrChan) { _beams(ic, is) = beams(jc, js); } } _calculateAreas(); } void ImageBeamSet::set(const GaussianBeam& beam) { _beams = beam; _areas = beam.getArea(_areaUnit); _minBeam = beam; _maxBeam = beam; _minBeamPos = 0; _maxBeamPos = 0; } void ImageBeamSet::setBeam(Int chan, Int stokes, const GaussianBeam& beam) { AlwaysAssert( Int(chan) < _beams.shape()[0] && Int(stokes) < _beams.shape()[1], AipsError ); if (chan < 0 && stokes < 0) { *this = ImageBeamSet(beam); } else if (chan >= 0 && stokes >= 0) { IPosition location(2, chan, stokes); _replaceBeam( beam, location, location, _maxBeamPos == location || _minBeamPos[1] == location ); } else if (chan < 0) { // && stokes >= 0 _replaceBeam( beam, IPosition(2, 0, stokes), IPosition(2, nchan()-1, stokes), _maxBeamPos[1] == stokes || _minBeamPos[1] == stokes ); } else { // chan >=0 && stokes < 0 _replaceBeam( beam, IPosition(2, chan, 0), IPosition(2, chan, nstokes()-1), _maxBeamPos[0] == chan || _minBeamPos[0] == chan ); } } void ImageBeamSet::_replaceBeam( const GaussianBeam& beam, const IPosition& location1, const IPosition& location2, Bool overwriteMaxMin ) { _beams(location1, location2) = beam; if (overwriteMaxMin) { // we are overwriting the max or min beam, so we need to recalculate // the areas _calculateAreas(); } else { const auto area = beam.getArea(_areaUnit); _areas(location1, location2) = area; if (area < _areas(_minBeamPos)) { _minBeam = beam; _minBeamPos = location1; } if (area > _areas(_maxBeamPos)) { _maxBeam = beam; _maxBeamPos = location1; } } } const GaussianBeam& ImageBeamSet::getMaxAreaBeamForPol(IPosition& pos, uInt stokes) const { pos.resize(2); // If single Stokes, use the maximum itself. if (nstokes() <= 1) { pos = _maxBeamPos; return _maxBeam; } AlwaysAssert(stokes < nstokes(), AipsError); // Determine location of maximum area for given Stokes. Double mina, maxa; IPosition minPos; minMax( mina, maxa, minPos, pos, _areas(IPosition(2, 0, stokes), IPosition(2, nchan() - 1, stokes)) ); pos[1] = stokes; return _beams(pos); } const GaussianBeam& ImageBeamSet::getMinAreaBeamForPol(IPosition& pos, uInt stokes) const { pos.resize(2); // If single Stokes, use the minimum itself. if (nstokes() <= 1) { pos = _minBeamPos; return _minBeam; } AlwaysAssert(stokes < nstokes(), AipsError); // Determine location of minimum area for given Stokes. Double mina, maxa; IPosition maxPos; minMax(mina, maxa, pos, maxPos, _areas(IPosition(2, 0, stokes), IPosition(2, nchan() - 1, stokes))); pos[1] = stokes; return _beams(pos); } const GaussianBeam& ImageBeamSet::getMedianAreaBeamForPol( IPosition& pos, uInt stokes ) const { pos.resize(2); pos = _beams.shape() - 1; if (nstokes() > 1) { pos[1] = stokes; } AlwaysAssert(pos[1] >= 0 && pos[1] < _beams.shape()[1], AipsError); if (nchan() == 1) { return _beams(0, pos[1]); } // Do an indirect sort to find the location of the median. Vector indices; GenSortIndirect::sort(indices, _areas(IPosition(2, 0, pos[1]), IPosition(2, nchan() - 1, pos[1]))); pos[0] = indices[indices.size() / 2]; return _beams(pos[0], pos[1]); } GaussianBeam ImageBeamSet::getMedianAreaBeam() const { Vector indices; IPosition shape = _beams.shape(); if (shape[0] > 1 && shape[1] > 1) { GenSortIndirect::sort(indices, Vector(_areas.tovector())); return _beams.tovector()[indices[indices.size()/2]]; } else { GenSortIndirect::sort(indices, _areas); GaussianBeam medbeam = shape[0] > 1 ? _beams(indices[indices.size()/2], 0) : _beams(0, indices[indices.size()/2]); return medbeam; } } const GaussianBeam ImageBeamSet::getSmallestMinorAxisBeam() const { BeamIter ibend = _beams.end(); Bool found = False; Quantity minAxis; GaussianBeam res = *(_beams.begin()); for ( BeamIter ibeam = _beams.begin(); ibeam != ibend; ++ibeam ) { if (found) { Quantity test = ibeam->getMinor(); if ( test < minAxis || ( test == minAxis && ibeam->getArea(_DEFAULT_AREA_UNIT) < res.getArea(_DEFAULT_AREA_UNIT) ) ) { minAxis = test; res = *ibeam; } } else if (! ibeam->isNull()) { minAxis = ibeam->getMinor(); res = *ibeam; found = True; } } return res; } void ImageBeamSet::_calculateAreas() { _areas.resize(_beams.shape()); if (!_beams.empty()) { _areaUnit = _beams.begin()->getMajor().getUnit(); _areaUnit = (Quantity(Quantity(1, _areaUnit) * Quantity(1, _areaUnit)).getUnit()); Array::iterator iareas = _areas.begin(); BeamIter ibend = _beams.end(); for ( BeamIter ibeam = _beams.begin(); ibeam != ibend; ++ibeam, ++iareas ) { *iareas = ibeam->getArea(_areaUnit); } Double minArea, maxArea; minMax(minArea, maxArea, _minBeamPos, _maxBeamPos, _areas); _minBeam = _beams(_minBeamPos); _maxBeam = _beams(_maxBeamPos); } } ostream &operator<<(ostream &os, const ImageBeamSet& beamSet) { os << beamSet.getBeams(); return os; } ImageBeamSet ImageBeamSet::subset(const Slicer& slicer, const CoordinateSystem& csys) const { // This beamset can be used if it has a single beam. if (nelements() < 2) { return *this; } // Determine the relevant axis numbers in the coordinate system. Int axes[2]; axes[0] = csys.spectralAxisNumber(); axes[1] = csys.polarizationAxisNumber(); IPosition ss(slicer.start()); IPosition se(slicer.end()); IPosition si(slicer.stride()); // If the beamset has no or a single freq or stokes, adjust the slicer. IPosition beamss(2, 0), beamse(2, 0), beamsi(2, 1); for (Int i = 0; i < 2; ++i) { if (axes[i] >= 0 && _beams.shape()[i] > 1) { AlwaysAssert(_beams.shape()[i] > se[axes[i]], AipsError); beamss[i] = ss[axes[i]]; beamse[i] = se[axes[i]]; beamsi[i] = si[axes[i]]; } } return ImageBeamSet(_beams(beamss, beamse, beamsi)); } Bool ImageBeamSet::equivalent(const ImageBeamSet& that) const { if (empty() || that.empty()) { return empty() == that.empty(); } uInt nc1 = nchan(); uInt np1 = nstokes(); uInt nc2 = that.nchan(); uInt np2 = that.nstokes(); if (!(nc1 == nc2 || nc1 == 1 || nc2 == 1) || !(np1 == np2 || np1 == 1 || np2 == 1)) { return False; // shapes mismatch } uInt nc = max(nc1, nc2); uInt np = max(np1, np2); uInt incrc1 = (nc1 == 1 ? 0 : 1); uInt incrp1 = (np1 == 1 ? 0 : 1); uInt incrc2 = (nc2 == 1 ? 0 : 1); uInt incrp2 = (np2 == 1 ? 0 : 1); uInt c1 = 0, p1 = 0, c2 = 0, p2 = 0; for (uInt p = 0; p < np; ++p) { for (uInt c = 0; c < nc; ++c, c1 += incrc1, c2 += incrc2) { if (_beams(c1, p1) != that._beams(c2, p2)) { return False; // mismatch in a beam } } c1 = c2 = 0; p1 += incrp1; p2 += incrp2; } return True; } ImageBeamSet ImageBeamSet::fromRecord(const Record& rec) { ThrowIf( ! rec.isDefined("nChannels"), "no nChannels field found" ); ThrowIf( ! rec.isDefined("nStokes"), "no nStokes field found" ); auto nchan = rec.asuInt("nChannels"); if (nchan == 0) { // provide backward compatibility for records written with 0 channels nchan = 1; } auto nstokes = rec.asuInt("nStokes"); if (nstokes == 0) { // provide backward compatibility for records written with 0 stokes nstokes = 1; } uInt count = 0; uInt chan = 0; uInt stokes = 0; Matrix beams(nchan, nstokes); auto iterend = beams.end(); for ( auto iter = beams.begin(); iter != iterend; ++iter, ++count ) { String field = "*" + String::toString(count); ThrowIf( ! rec.isDefined(field), "Field " + field + " is not defined" ); *iter = GaussianBeam::fromRecord(rec.asRecord(field)); if (++chan == nchan) { chan = 0; ++stokes; } } return ImageBeamSet(beams); } Record ImageBeamSet::toRecord() const { Record perPlaneBeams; perPlaneBeams.define("nChannels", nchan()); perPlaneBeams.define("nStokes", nstokes()); Record rec; uInt count = 0; const Array& beams = getBeams(); for (const auto& beam: beams) { ThrowIf( beam.isNull(), "Invalid per plane beam found" ); Record rec = beam.toRecord(); perPlaneBeams.defineRecord("*" + String::toString(count), rec); ++count; } return perPlaneBeams; } void ImageBeamSet::rotate(const Quantity& angle, Bool unwrap) { ThrowIf( ! angle.isConform("rad"), "Quantity is not an angle" ); Matrix::iterator iter = _beams.begin(); Matrix::iterator end = _beams.end(); while(iter != end) { iter->setPA(iter->getPA(True) + angle, unwrap); ++iter; } _minBeam.setPA(_minBeam.getPA() + angle, unwrap); _maxBeam.setPA(_maxBeam.getPA() + angle, unwrap); } void ImageBeamSet::summarize( LogIO& log, Bool verbose, const CoordinateSystem& csys ) const { ostream& os = log.output(); Unit u("deg"); for ( Matrix::const_iterator iter = _beams.begin(); iter != _beams.end(); iter++ ) { if ( iter->getMajor("deg") < 1/3600 || iter->getMinor("deg") < 1/3600 ) { u = Unit("mas"); break; } if ( iter->getMajor("deg") < 1.0 || iter->getMinor("deg") < 1.0 ) { u = Unit("arcsec"); } } Bool hasSpectral = csys.hasSpectralAxis(); Bool hasStokes = csys.hasPolarizationCoordinate(); log.output() << "Restoring Beams " << endl; const SpectralCoordinate *spCoord = 0; IPosition beamsShape = _beams.shape(); uInt chanWidth = 0; uInt freqWidth = 0; uInt freqPrec = 0; uInt velPrec = 0; uInt velWidth = 0; uInt polWidth = 3; uInt typeWidth = 6; Bool myverbose = verbose || ! hasSpectral || (hasSpectral && beamsShape[0] <= 3); const StokesCoordinate *polCoord = hasStokes ? &csys.stokesCoordinate() : 0; if (hasSpectral) { spCoord = &csys.spectralCoordinate(); chanWidth = max(4, Int(log10(beamsShape[0])) + 1); // yes these really should be separated because width applies only to the first. ostringstream x; Double freq; spCoord->toWorld(freq, 0); if (spCoord->pixelValues().size() > 0) { freqPrec = 6; velPrec = 3; } else { Double inc = spCoord->increment()[0]; freqPrec = Int(abs(log10(inc/freq))) + 1; Double vel0, vel1; spCoord->pixelToVelocity(vel0, 0); spCoord->pixelToVelocity(vel1, 1); if (abs(vel0-vel1) > 10) { velPrec = 0; } else { velPrec = Int(abs(log10(abs(vel0-vel1)))) + 2; } } x << scientific << std::setprecision(freqPrec) << freq; freqWidth = x.str().length(); velWidth = velPrec + 5; if (myverbose) { os << std::setw(chanWidth) << "Chan" << " "; os << std::setw(freqWidth) << "Freq" << " "; os << std::setw(velWidth) << "Vel"; } else { if (hasStokes) { os << std::setw(polWidth) << "Pol" << " "; } os << std::setw(typeWidth) << "Type" << " "; os << std::setw(chanWidth) << "Chan" << " "; os << std::setw(freqWidth) << "Freq" << " "; os << std::setw(velWidth) << "Vel" << endl; } } if (myverbose) { if (hasStokes) { os << " "; os << std::setw(polWidth) << "Pol"; } os << endl; Int stokesPos = hasStokes ? hasSpectral ? 1 : 0 : -1; IPosition axisPath = hasSpectral && hasStokes ? IPosition(2, 1, 0) : IPosition(1, 0); ArrayPositionIterator iter(beamsShape, axisPath, False); while (! iter.pastEnd()) { const IPosition pos = iter.pos(); if (hasSpectral) { _chanInfoToStream( os, spCoord, pos[0], chanWidth, freqPrec, velWidth, velPrec ); } if (hasStokes) { Stokes::StokesTypes stokes; polCoord->toWorld(stokes, pos[stokesPos]); os << std::setw(polWidth) << Stokes::name(stokes) << " "; } _beamToStream(os, _beams(pos), u); os << endl; iter.next(); } } else { uInt mymax = hasStokes ? nstokes() : 1; for (uInt i=0; itoWorld(stokes, i); stokesString = Stokes::name(stokes); } for (uInt j=0; j<3; j++) { String aggType; GaussianBeam beam; IPosition pos; switch (j) { case 0: { aggType = "Max"; beam = getMaxAreaBeamForPol(pos, hasStokes? i : -1); break; } case 1: { aggType = "Min"; beam = getMinAreaBeamForPol(pos, hasStokes ? i : -1); break; } case 2: { aggType = "Median"; beam = getMedianAreaBeamForPol( pos, hasStokes ? i : -1 ); break; } default: { ThrowCc("Logic error: Unhandled aggregate type"); } } if (hasStokes) { os << std::setw(polWidth) << stokesString << " "; } os << std::setw(typeWidth) << aggType << " "; _chanInfoToStream( os, spCoord, pos[0], chanWidth, freqPrec, velWidth, velPrec ); _beamToStream(os, beam, u); os << endl; } } } } const Quantum> ImageBeamSet::getAreas() const { return Quantum>(_areas, _areaUnit); } const std::map>> ImageBeamSet::paramMatrices( const Unit& majminUnit, const Unit& paUnit ) const { const auto& beams = getBeams(); IPosition shape(beams.shape()); Matrix mymaj(shape); Matrix mymin(shape); Matrix pa(shape); auto majIter = mymaj.begin(); auto minIter = mymin.begin(); auto paIter = pa.begin(); for (const auto& beam: beams) { ThrowIf( beam.isNull(), "Invalid per plane beam found" ); *majIter = beam.getMajor(majminUnit); *minIter = beam.getMinor(majminUnit); *paIter = beam.getPA(paUnit, false); ++majIter; ++minIter; ++paIter; } std::map>> mymap; mymap["major"] = Quantum>(mymaj, majminUnit); mymap["minor"] = Quantum>(mymin, majminUnit); mymap["pa"] = Quantum>(pa, paUnit); return mymap; } void ImageBeamSet::_chanInfoToStream( ostream& os, const SpectralCoordinate *spCoord, const uInt chan, const uInt chanWidth, const uInt freqPrec, const uInt velWidth, const uInt velPrec ) { os << std::fixed << std::setw(chanWidth) << chan << " "; Double freq; spCoord->toWorld(freq, chan); os << scientific << std::setprecision(freqPrec) << freq << " "; Double vel; spCoord->pixelToVelocity(vel, chan); os << std::setw(velWidth) << fixed << std::setprecision(velPrec) << vel << " "; } void ImageBeamSet::_beamToStream( ostream& os, const GaussianBeam& beam, const Unit& unit ) { Quantity majAx = beam.getMajor(); majAx.convert(unit); Quantity minAx = beam.getMinor(); minAx.convert(unit); Quantity pa = beam.getPA(True); pa.convert("deg"); os << fixed << std::setprecision(4) << std::setw(9) << majAx << " x " << std::setw(9) << minAx << " pa=" << std::setw(8) << pa; } } casacore-3.7.1/images/Images/ImageBeamSet.h000066400000000000000000000271271476623553700204430ustar00rootroot00000000000000//# Copyright (C) 1996,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_IMAGEBEAMSET_H #define IMAGES_IMAGEBEAMSET_H #include #include #include //#include #include namespace casacore { class SpectralCoordinate; class CoordinateSystem; // // Represents a set of restoring beams associated with an image. // // // // // // // // A Set of Beams associated with an Image. // // // This class represents a set of restoring beams associated with // a deconvolved image. Internally, the beams are stored in a Matrix in // which the first dimension represents the spectral axis and the second // dimension represents the polarization axis. Methods which take the number // of channels and stokes as the input parameters will accept 0 for either of // these, in the cases where the corresponding axis is absent, but internally, // the associated axis of the storage Matrix will be set to one since a Matrix // with one dimension of length 0 must be empty. If one (or both) of the axes is // of length 1, the beam associated with that position is valid for all channels or // stokes at the position. For example, if one has an image with 10 spectral channels // and 4 stokes, and one has a beam set of dimensions (1, 4) associated with that image, // all channels for a given stokes will have the same beam. Similarly, if the beam set // is of shape (10, 1) in this case, all stokes will have the same beam for a given channel. // If the axis lengths of the beam set are greater than one, they must be exactly // the same length of the corresponding axes in the associated image. // // // // // // Restoring beams are used many places in image analysis tasks. // // // class ImageBeamSet { public: typedef Array::const_iterator BeamIter; // Construct an empty beam set. ImageBeamSet(); // Construct a beam set from an 2-D array of beams representing // the frequency and stokes axis. // Axis length 1 means it is valid for all channels cq. stokes. // If the image has 0 spectral channels or stokes, the corresponding // length of the axis in the provided matrix should be 1. ImageBeamSet( const Matrix& beams ); // construct an ImageBeamSet representing a single beam which is valid for // all channels and stokes ImageBeamSet(const GaussianBeam& beam); // Create an ImageBeamSet of the specified shape with all // GaussianBeams initialized to beam. ImageBeamSet(uInt nchan, uInt nstokes, const GaussianBeam& beam=GaussianBeam::NULL_BEAM); // The copy constructor (reference semantics). ImageBeamSet(const ImageBeamSet& other); ~ImageBeamSet(); // Assignment can change the shape (copy semantics). ImageBeamSet& operator=(const ImageBeamSet& other); // Beam sets are equal if the shapes and all corresponding beams are equal. Bool operator== (const ImageBeamSet& other) const; Bool operator!= (const ImageBeamSet& other) const; // Beam sets are equivalent if both have no beams or if the // expanded sets are equal. Expanded means that an axis can have // length 0 or 1 and is (virtually) expanded to the length of the matching // axis in the other beam set. Bool equivalent (const ImageBeamSet& that) const; // Get the number of elements in the beam array. // uInt nelements() const { return _beams.size(); } uInt size() const { return _beams.size(); } // Bool hasSingleBeam() const { return _beams.size() == 1; } // Does this beam set contain multiple beams? Bool hasMultiBeam() const { return _beams.size() > 1; } // Is the beam set empty? Bool empty() const { return _beams.empty(); } // Get the shape of the beam array. The minimum value for // a component of the returned IPosition is always 1. const IPosition& shape() const { return _beams.shape(); } // Get the number of channels in the beam array. Note that this will // always return a minimum of 1, even if nchan was specified as 0 on construction. uInt nchan() const { return _beams.shape()[0]; } // Get the number of stokes in the beam array. Note that this will always // return a minimum of 1, even if nstokes was specified as 0 on construction. uInt nstokes() const { return _beams.shape()[1]; } // Get the single global beam. If there are multiple beams, // an exception is thrown. const GaussianBeam& getBeam() const; // Get the beam at the specified location. // Note that a single channel or stokes in the beam set is valid for // all channels cq. stokes. // const GaussianBeam& getBeam(Int chan, Int stokes) const; const GaussianBeam &operator()(Int chan, Int stokes) const { return getBeam (chan, stokes); } // // Get a beam at the given 2-dim IPosition. It should match exactly, // thus a single channel or stokes in the beam set is not valid for all. // const GaussianBeam& getBeam(const IPosition& pos) const // { return _beams(pos); } // Set the beam at the given location. // The location must be within the beam set shape. // If chan or stokes is negative, then the beam applies // to all channels or stokes, respectively. If both are negative, the specified // beam becomes the global beam and the beam set is resized to (1, 1). void setBeam(Int chan, Int stokes, const GaussianBeam& beam); // Resize the beam array. nchan=0 or nstokes=0 // is silently changed to 1. void resize(uInt nchan, uInt nstokes); // Return a subset of the beam array. // The slicer is usually the slicer used for a subimage. // The slicer can contain multiple stokes or channels, even if the // beam set has only one. ImageBeamSet subset (const Slicer& imageSlicer, const CoordinateSystem& csys) const; // Get the beam array. const Matrix& getBeams() const { return _beams; } // Set the beams in this beam set. // The shape of the given array must match the beam set. // It also matches if an axis in array or beam set has length 1, which // means that it expands to the other length. void setBeams(const Matrix& beams); // Set all beams to the same value. void set(const GaussianBeam& beam); // Get the beam in the set which has the smallest area. GaussianBeam getMinAreaBeam() const { return _minBeam; } // Get the beam in the set which has the largest area. // Get the beam in the set which has the largest area. GaussianBeam getMaxAreaBeam() const { return _maxBeam; } // Get the beam in the set which has the median area. GaussianBeam getMedianAreaBeam() const; // Get the position of the beam with the minimum area. IPosition getMinAreaBeamPosition() const { return _minBeamPos; } // Get the position of the beam with the maximum area. IPosition getMaxAreaBeamPosition() const { return _maxBeamPos; } // Get the minimal, maximal, and median area beams and positions in the // beam set matrix for the given stokes. If the stokes axis has length 1 // in the beam matrix, it is valid for all stokes and no checking is done // that stokes is valid; the requested beam for the entire beam set // is simply returned in this case. // If the number of stokes in the beam matrix is >1, checking is done that // the specified value of stokes is valid and if not, an exception // is thrown. // const GaussianBeam& getMinAreaBeamForPol(IPosition& pos, uInt stokes) const; const GaussianBeam& getMaxAreaBeamForPol(IPosition& pos, uInt stokes) const; const GaussianBeam& getMedianAreaBeamForPol(IPosition& pos, uInt stokes) const; // static const String& className(); // Get the beam that has the smallest minor axis. If multiple beams have // the smallest minor axis, the beam in this subset with the smallest area // will be returned. const GaussianBeam getSmallestMinorAxisBeam() const; // convert ImageBeamSet to and from record // static ImageBeamSet fromRecord(const Record& rec); Record toRecord() const; // // If verbose, log all beams, if not just summarize beam stats. void summarize(LogIO& log, Bool verbose, const CoordinateSystem& csys) const; // Modify the beam set by rotating all beams counterclockwise through the // specified angle. If unwrap=True, unwrap the new position angle(s) so that // it falls in the range -90 to 90 degrees before setting it. void rotate(const Quantity& angle, Bool unwrap=False); // get all the beam areas in a single quantum matrix. const Quantum> getAreas() const; // get all the major axes, minor axes, and pas in quantum matrices. // The returned map has keys "major", "minor", and "pa". const std::map>> paramMatrices( const Unit& majminUnit=Unit("arcsec"), const Unit& paUnit="deg" ) const; private: static const String _DEFAULT_AREA_UNIT; Matrix _beams; Matrix _areas; String _areaUnit; GaussianBeam _minBeam, _maxBeam; IPosition _minBeamPos, _maxBeamPos; void _calculateAreas(); // common code for replacing a beam in a multi-beam set void _replaceBeam( const GaussianBeam& beam, const IPosition& location1, const IPosition& location2, Bool overwriteMaxMin ); // Show the spectral info. static void _chanInfoToStream( ostream& os, const SpectralCoordinate *spCoord, const uInt chan, const uInt chanWidth, const uInt freqPrec, const uInt velWidth, const uInt velPrec ); // Show the beam info. static void _beamToStream( ostream& os, const GaussianBeam& beam, const Unit& unit ); }; // Show the beam set info. ostream &operator<<(ostream &os, const ImageBeamSet& beamSet); } #endif casacore-3.7.1/images/Images/ImageConcat.h000066400000000000000000000271611476623553700203300ustar00rootroot00000000000000//# ImageConcat.h: concatenate images along an axis //# Copyright (C) 1996,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_IMAGECONCAT_H #define IMAGES_IMAGECONCAT_H //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class CoordinateSystem; template class ImageSummary; template class MaskedLattice; // // Concatenates images along a specified axis // // // // // //
      • LatticeConcat //
      • ImageInterface // // // This is a class designed to concatenate images along a specified axis // // // This is a class designed to concatenate images along a specified // axis. This means you can join them together. E.g., // join images of shape [10,20,30] and [10,20,40] into a lattice // of shape [10,20,70]. // // The ImageConcat object does not copy the input images, it // just references them. You can use the Lattice::copyData(Lattice) // function to fill an output image with the concatenated input images // // If you use the putSlice function, be aware that it will change the // underlying images if they are writable. // // You can also concatenate a lattice to an image. // // // // // IPosition shape(2, 10, 20); // PagedImage im1(shape, CoordinateUtil::defaultCoords2D(), // "tImageConcat_tmp1.img"); // im1.set(1.0); // PagedImage im2(shape, CoordinateUtil::defaultCoords2D(), // "tImageConcat_tmp2.img"); // im2.set(2.0); // //// Make concatenator for axis 0 // // ImageConcat concat(0); // //// Relax coordinate constraints // // concat.setImage(im1, True); // concat.setImage(im2, True); // //// Make output image and mask (if required, which it will be in this case) // // PagedImage im3(concat.shape(), CoordinateUtil::defaultCoords2D(), // "tImageConcat_tmp3.img"); // //// Copy to output // // im3.copyData(concat); // // // See tImageConcat.cc for more examples. // // // Image concatentation is a useful enduser requirement. // // //
      • Offer the ability to increase the dimensionality of // the output image // template class ImageConcat : public ImageInterface { public: // Constructor. Specify the pixel axis for concatenation explicit ImageConcat (uInt axis, Bool tempClose=True, Bool combineMiscInfo=True); // Construct the object from a Json file with the given name. // This constructor is usually called by ImageOpener::openImageConcat. ImageConcat (const JsonKVMap&, const String& fileName); // Default constructor, Sets the concatenation axis to 0 ImageConcat(); // Copy constructor (reference semantics) ImageConcat (const ImageConcat &other); // Destructor virtual ~ImageConcat(); // Assignment operator (reference semantics) ImageConcat &operator= (const ImageConcat &other); // Make a copy of the object (reference semantics). virtual ImageInterface* cloneII() const; // Save the image in file 'image.concat' in a directory with the given name. // An exception is thrown if such a directory or file already exists. // It can be opened by ImageOpener::openImage(Concat). virtual void save (const String& fileName) const; // Replace the miscinfo in the ConcatImage, which writes the image.concat file. // It can fail if, e.g., the directory to write to is not writable. virtual Bool setMiscInfo (const RecordInterface& newInfo); // Set the ImageInfo in the super class ImageInterface and in each // underlying image. If needed, its restoring beam is split along the // frequency or polarisation axis and set in each underlying image. virtual Bool setImageInfo(const ImageInfo& info); // Get the image type (returns name of derived class). virtual String imageType() const; // Is the lattice persistent and can it be loaded by other processes as well? virtual Bool isPersistent() const; // Sets a new image into the list to be concatenated. // If relax is False, throws an exception if the images // are not contiguous along the concatenation axis. // If relax is True, it will create a non-regular TabularCoordinate // for non-contiguous images if the coordinates are monotonic. // Otherwise, it just uses the coordinates of the image void setImage (ImageInterface& image, Bool relax); // Add a clone of the lattice to the list to be concatenated. // You can only concatenate a lattice with an image if // you have first used setImage to set an image (this // provides the CooridinateSystem information) void setLattice (MaskedLattice& lattice); // Return the number of images/lattices set so far uInt nimages() const { return latticeConcat_p.nlattices(); } // Returns the current concatenation axis (0 relative) uInt axis () const { return latticeConcat_p.axis(); } // Returns the number of dimensions of the *input* images/lattices // Returns 0 if none yet set. uInt imageDim() const { return latticeConcat_p.latticeDim(); } // Return a reference to the i-th image. ImageInterface& image(uInt i) const { return dynamic_cast&>(*(latticeConcat_p.lattice(i))); } // Handle the (un)locking and syncing, etc. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; virtual void resync(); virtual void flush(); virtual void tempClose(); virtual void reopen(); // // Return the name of the current ImageInterface object. // If the object is persistent, it returns its file name. // Otherwise it returns the string "Concatenation :" virtual String name (Bool stripPath=False) const; // Has the object really a mask? virtual Bool isMasked() const; // Does the image have a pixelmask? virtual Bool hasPixelMask() const; // Get access to the pixelmask. // An exception is thrown if the image does not have a pixelmask // virtual const Lattice& pixelMask() const; virtual Lattice& pixelMask(); // // Get the region used (always returns 0) virtual const LatticeRegion* getRegionPtr() const; // If all of the underlying lattices are writable returns True virtual Bool isWritable() const; // Return the shape of the concatenated image virtual IPosition shape() const; // Return the best cursor shape. It will try to return the best cusrsor of the //smallest constituent image along the non-direction axes (in order to minimize //bouncing from one image to another while iterating which may involve lots of //open and tempclose). virtual IPosition doNiceCursorShape (uInt maxPixels) const; // Do the actual get of the data. // The return value is always False, thus the buffer does not reference // another array. Generally the user should use function getSlice virtual Bool doGetSlice (Array& buffer, const Slicer& section); // Do the actual get of the mask data. // The return value is always False, thus the buffer does not reference // another array. Generally the user should use function getMaskSlice virtual Bool doGetMaskSlice (Array& buffer, const Slicer& section); // Do the actual put of the data into the Lattice. This will change the // underlying images (if they are writable) that were used to create the // ImageConcat object. It throws an exception if not writable. // Generally the user should use function putSlice virtual void doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride); // Throws an excpetion as you cannot reshape an ImageConcat object virtual void resize(const TiledShape&); // Check class invariants. virtual Bool ok() const; // These are the implementations of the LatticeIterator letters. // not for public use virtual LatticeIterInterface *makeIter (const LatticeNavigator &navigator, Bool useRef) const; private: LatticeConcat latticeConcat_p; Bool combineMiscInfo_p; Bool warnAxisNames_p, warnAxisUnits_p, warnImageUnits_p; Bool warnContig_p, warnRefPix_p, warnRefVal_p, warnInc_p, warnTab_p; Bool isContig_p; mutable String fileName_p; // Empty if not persistent Vector isImage_p; Vector pixelValues_p; Vector worldValues_p; Coordinate::Type originalAxisType_p; Double coordConvert(Int& worldAxis, LogIO& os, const CoordinateSystem& cSys, uInt axis, Double pixelCoord) const; void _checkContiguous(const IPosition& shape1, const CoordinateSystem& cSys1, const CoordinateSystem& cSys2, LogIO& os, uInt axis, Bool relax); void checkNonConcatAxisCoordinates (LogIO& os, const ImageInterface& image, Bool relax); Vector makeNewStokes(const Vector& stokes1, const Vector& stokes2); // Updates the CoordinateSystem in the ImageConcat image. The first lattice must // be an image. The first lattice is contiguous by definition. The Coordinate // System for the first image must be set before calling this function. For // the first image, this function just sets up worldValues and pixelValues void setCoordinates(); void _updatePixelAndWorldValues(uInt iIm); //# Make members of parent class known. public: using ImageInterface::logger; using ImageInterface::coordinates; using ImageInterface::units; using ImageInterface::miscInfo; protected: using ImageInterface::setCoordsMember; using ImageInterface::setMiscInfoMember; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/images/Images/ImageConcat.tcc000066400000000000000000000737111476623553700206540ustar00rootroot00000000000000//# ImageConcat.cc: concatenate images //# Copyright (C) 1995,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_IMAGECONCAT_TCC #define IMAGES_IMAGECONCAT_TCC #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template ImageConcat::ImageConcat() : latticeConcat_p(), combineMiscInfo_p(True), warnAxisNames_p(True), warnAxisUnits_p(True), warnImageUnits_p(True), warnRefPix_p(True), warnRefVal_p(True), warnInc_p(True), warnTab_p(True), isContig_p(True) {} template ImageConcat::ImageConcat (uInt axis, Bool tempClose, Bool combineMiscInfo) : latticeConcat_p(axis, tempClose), combineMiscInfo_p(combineMiscInfo), warnAxisNames_p(True), warnAxisUnits_p(True), warnImageUnits_p(True), warnContig_p(True), warnRefPix_p(True), warnRefVal_p(True), warnInc_p(True), warnTab_p(True), isContig_p(True) {} template ImageConcat::ImageConcat (const ImageConcat& other) : ImageInterface(other), latticeConcat_p(other.latticeConcat_p), combineMiscInfo_p(other.combineMiscInfo_p), warnAxisNames_p(other.warnAxisNames_p), warnAxisUnits_p(other.warnAxisUnits_p), warnImageUnits_p(other.warnImageUnits_p), warnContig_p(other.warnContig_p), warnRefPix_p(other.warnRefPix_p), warnRefVal_p(other.warnRefVal_p), warnInc_p(other.warnInc_p), warnTab_p(other.warnTab_p), isContig_p(other.isContig_p), fileName_p(other.fileName_p), pixelValues_p(other.pixelValues_p.copy()), worldValues_p(other.worldValues_p.copy()), originalAxisType_p(other.originalAxisType_p) { isImage_p.resize(other.isImage_p.nelements()); isImage_p = other.isImage_p; } template ImageConcat::~ImageConcat() {} template ImageConcat& ImageConcat::operator= (const ImageConcat& other) { if (this != &other) { ImageInterface::operator= (other); latticeConcat_p = other.latticeConcat_p; combineMiscInfo_p = other.combineMiscInfo_p; warnAxisNames_p = other.warnAxisNames_p; warnAxisUnits_p = other.warnAxisUnits_p; warnImageUnits_p = other.warnImageUnits_p; warnContig_p = other.warnContig_p; warnRefPix_p = other.warnRefPix_p; warnRefVal_p = other.warnRefVal_p; warnInc_p = other.warnInc_p; warnTab_p = other.warnTab_p; isContig_p = other.isContig_p; fileName_p = other.fileName_p; isImage_p.resize(other.isImage_p.nelements()); isImage_p = other.isImage_p; pixelValues_p.resize(other.pixelValues_p.nelements()); pixelValues_p = other.pixelValues_p; worldValues_p.resize(other.worldValues_p.nelements()); worldValues_p = other.worldValues_p; originalAxisType_p = other.originalAxisType_p; } return *this; } template ImageInterface* ImageConcat::cloneII() const { return new ImageConcat(*this); } template ImageConcat::ImageConcat (const JsonKVMap& jmap, const String& fileName) : latticeConcat_p(), combineMiscInfo_p(False), warnAxisNames_p(True), warnAxisUnits_p(True), warnImageUnits_p(True), warnContig_p(True), warnRefPix_p(True), warnRefVal_p(True), warnInc_p(True), warnTab_p(True), isContig_p(True), fileName_p(Path(fileName).absoluteName()) { // This must be the opposite of function save. AlwaysAssert (jmap.getInt("Version", 1) == 1, AipsError); uInt axis = jmap.get("Axis").getInt(); Bool tmpClose = jmap.getBool("TempClose", True); Vector names(jmap.get("Images").getArrayString()); latticeConcat_p=LatticeConcat(axis, tmpClose); // Combine miscinfo if not defined in the Json file. combineMiscInfo_p = !jmap.isDefined("MiscInfo"); for (uInt i=0; i* img = dynamic_cast*>(latt); if (img == 0) { delete latt; throw AipsError ("ImageConcat " + fileName + " contains image " + names[i] + " of another data type"); } setImage (*img, True); delete img; } if (jmap.isDefined("MiscInfo")) { TableRecord tabrec; tabrec.fromRecord (jmap.get("MiscInfo").getValueMap().toRecord()); this->setMiscInfoMember (tabrec); } } template void ImageConcat::save (const String& fileName) const { // Note that an ImageConcat is opened by ImageOpener. // Check that all images used are persistent. for (uInt i=0; iisPersistent()) { throw AipsError ("ImageConcat cannot be made persistent, because one of " "its images is not persistent"); } } // Get the absolute file name. String fullName = Path(fileName).absoluteName(); // Create the directory if not existing already. Directory dir(fullName); if (! dir.exists()) { dir.create (False); } // Create the Json file. JsonOut jout(fullName + "/imageconcat.json"); jout.start(); jout.write ("Version", 1); String dt(ValType::getTypeStr(this->dataType())); dt.trim(); jout.write ("DataType", dt); jout.write ("Axis", latticeConcat_p.axis()); jout.write ("TempClose", latticeConcat_p.isTempClose()); Vector names(latticeConcat_p.nlattices()); for (uInt i=0; iname(False); String fname = Path(name).absoluteName(); // Make path relative to parent, so parent can be moved. names[i] = Path::stripDirectory (fname, fullName); } jout.write ("Images", Array(names)); jout.write ("MiscInfo", miscInfo().toRecord()); jout.end(); fileName_p = fullName; } template Bool ImageConcat::setMiscInfo (const RecordInterface& newInfo) { setMiscInfoMember (newInfo); if (isPersistent()) { save (fileName_p); } return True; } template Bool ImageConcat::setImageInfo (const ImageInfo& info) { // Check the beamset and set the Imageinfo for the concat image. this->setImageInfoMember (info); // Set the ImageInfo in each individual image. // If the ImageConcat and the BeamInfo are along the frequency or stokes axis, // take the appropriate subset. uInt ndone = 0; for (uInt i=0; i& img = dynamic_cast&>(*(latticeConcat_p.lattice(i))); ImageInfo ii = img.imageInfo(); ndone += ii.setInfoSplitBeamSet (ndone, info, img.shape(), img.coordinates(), latticeConcat_p.axis()); img.setImageInfo(ii); } return True; } template Bool ImageConcat::isPersistent() const { return ! fileName_p.empty(); } template String ImageConcat::imageType() const { return "ImageConcat"; } // Public functions template void ImageConcat::setImage (ImageInterface& image, Bool relax) { LogIO os(LogOrigin("ImageConcat", __func__, WHERE)); // How many images have we set so far ? const uInt nIm = latticeConcat_p.nlattices(); IPosition oldShape = nIm > 0 ? this->shape() : IPosition(); // LatticeConcat allows the dimensionality to increase by // one, but ImageConcat can't do that yet - so an extra // test here. if (latticeConcat_p.axis() >= image.ndim()) { throw(AipsError("Axis number and image dimension are inconsistent")); } // Do Lattice relevant things. This does shape checks and // sets the lattice pointers latticeConcat_p.setLattice(image); // Do the extra image stuff. Most of it is coordinate rubbish. // The ImageInfo (except beams) comes from the first image only. // The miscInfo is merged from all images. isImage_p.resize(nIm+1,True); isImage_p(nIm) = True; if (nIm==0) { ImageInterface::setCoordinateInfo(image.coordinates()); this->setUnitMember (image.units()); this->setImageInfoMember (image.imageInfo()); this->setMiscInfoMember (image.miscInfo()); this->setCoordinates(); } else { if (combineMiscInfo_p) { TableRecord rec = miscInfo(); rec.merge (image.miscInfo(), RecordInterface::RenameDuplicates); this->setMiscInfoMember (rec); } // Combine the beams if possible. // Should be done before the coordinates are merged. this->rwImageInfo().combineBeams (image.imageInfo(), oldShape, image.shape(), this->coordinates(), image.coordinates(), latticeConcat_p.axis(), relax, os); // Compare the coordinates of this image with the current private // coordinates const CoordinateSystem& cSys0 = coordinates(); const CoordinateSystem& cSys = image.coordinates(); ThrowIf( cSys.nCoordinates() != cSys0.nCoordinates(), "Images have inconsistent numbers of coordinates" ); Int coord0, axisInCoordinate0; Int coord, axisInCoordinate; cSys0.findPixelAxis (coord0, axisInCoordinate0, latticeConcat_p.axis()); cSys.findPixelAxis(coord, axisInCoordinate, latticeConcat_p.axis()); ThrowIf( coord0<0 || coord<0, "Pixel axis has been removed for concatenation axis" ); ThrowIf( cSys.pixelAxisToWorldAxis(latticeConcat_p.axis()) < 0 || coordinates().pixelAxisToWorldAxis(latticeConcat_p.axis()) < 0, "World axis has been removed for concatenation axis" ); // This could be cleverer. E.g. allow some mixed types (Tabular/Linear) // Because the CoordinateSystem may change (e.g. -> Tabular) we hang onto // the Coordinate type of the original coordinate system for the first image if (cSys.type(coord0)!=originalAxisType_p) { os << "Coordinate types for concatenation axis are inconsistent" << LogIO::EXCEPTION; } if (!allEQ(cSys.worldAxisNames(), cSys0.worldAxisNames())) { ImageInfo::logMessage (warnAxisNames_p, os, relax, "Image axis names differ"); } if (!allEQ(cSys.worldAxisUnits(),cSys0.worldAxisUnits())) { ImageInfo::logMessage (warnAxisUnits_p, os, relax, "Image axis units differ"); } if (image.units().getName() != this->units().getName()) { ImageInfo::logMessage (warnAxisUnits_p, os, True, "Image units differ. " "Image units of the first image (" + this->units().getName() + ") will be used for the output image"); } // Compare coordinates at end of last image and start of new image if (latticeConcat_p.isTempClose()) latticeConcat_p.reopen(nIm-1); const ImageInterface* pImLast = dynamic_cast*>(latticeConcat_p.lattice(nIm-1)); const CoordinateSystem& cSysLast = pImLast->coordinates(); if (latticeConcat_p.isTempClose()) latticeConcat_p.tempClose(nIm-1); // once a lattice has been added that is not contiguous, the output coordinate // system will not be contiguous no matter what type of additional coordinate // systems are concatenated. if (isContig_p) { _checkContiguous ( pImLast->shape(), cSysLast, cSys, os, latticeConcat_p.axis(), relax ); } else { ThrowIf( ! relax, "A previously added image was not contiguous, so the only way" "the current image may be added is if relax=True" ); } // Compare coordinate descriptors not on concatenation axis checkNonConcatAxisCoordinates (os, image, relax); // Update the coordinates in the ImageConcat object now we are happy // all is well this->setCoordinates(); } // Add parent history logger().addParent (image.logger()); } template void ImageConcat::setLattice(MaskedLattice& lattice) { LogIO os(LogOrigin("ImageConcat", "setLattice(...)", WHERE)); // How many images have we set so far ? const uInt nIm = latticeConcat_p.nlattices(); // Must have already set an image before we can set a lattice if (nIm==0) { throw(AipsError("You must call setImage before you can call setLattice")); } // LatticeConcat allows the dimensionality to increase by // one, but ImageConcat can't do that yet - so an extra // test here. if (latticeConcat_p.axis() >= lattice.ndim()) { throw(AipsError("Axis number and lattice dimension are inconsistent")); } // Do Lattice relevant things. This makes shape checks and // sets the lattice pointers latticeConcat_p.setLattice(lattice); // Because the Lattice has no coordinates, we signal // a non-contiguity situation. Function setCoordinates // will make up a coordinate for this lattice isImage_p.resize(nIm+1,True); isImage_p(nIm) = False; isContig_p = False; // this->setCoordinates(); } // Public non-virtual over-ridden functions from ImageInterface // Public virtual functions template void ImageConcat::resize(const TiledShape&) { throw (AipsError ("ImageConcat::resize - an ImageConcat is not writable")); } template Bool ImageConcat::doGetSlice(Array& buffer, const Slicer& section) { return latticeConcat_p.doGetSlice(buffer, section); } template void ImageConcat::doPutSlice (const Array& buffer, const IPosition& where, const IPosition& stride) { latticeConcat_p.doPutSlice(buffer, where, stride); } template Bool ImageConcat::doGetMaskSlice (Array& buffer, const Slicer& section) { return latticeConcat_p.doGetMaskSlice (buffer, section); } template IPosition ImageConcat::doNiceCursorShape (uInt maxPixels) const { //Return the smallest cursor shape along the non X-Y direction from the constituent images if(nimages() > 0){ ///if image has 1 or 2 axes return cursor shape of first image if(shape().nelements() <= 2){ return image(0).niceCursorShape(maxPixels); } Vector dirpixaxes, dirworldaxes; Int coordaxis; CoordinateUtil::findDirectionAxes(dirpixaxes, dirworldaxes, coordaxis, coordinates()); Int64 minprod=std::numeric_limits::max(); Int minimage=-1; for (uInt k=0; k < nimages(); ++k){ IPosition curshape=image(k).niceCursorShape(maxPixels); Int64 prod= curshape.product()/ Int64(curshape(dirpixaxes(0))*curshape(dirpixaxes(1))); if(prod < minprod){ minprod=prod; minimage=k; } } return image(minimage).niceCursorShape(maxPixels); } return IPosition(0); } template Bool ImageConcat::ok() const { return True; } template LatticeIterInterface* ImageConcat::makeIter (const LatticeNavigator& nav, Bool useRef) const { return latticeConcat_p.makeIter(nav, useRef); } // Private functions template void ImageConcat::_checkContiguous ( const IPosition& shape1, const CoordinateSystem& cSys1, const CoordinateSystem& cSys2, LogIO& os, uInt axis, Bool relax ) { // // cSys1 from last image // cSys2 from current image // // Find out the coordinate of the concatenation axis at the location // of the last pixel from the previous image and compare. // // For Stokes axis we must do something different, because you can't // convert pixel -1 to Stokes. Bloody Stokes. coord already checked // to be consistent Int coord, axisInCoordinate; cSys2.findPixelAxis(coord, axisInCoordinate, axis); if (cSys2.type(coord)==Coordinate::STOKES) { // See if we can make a Stokes coordinate from all the previous // Stokes and the new Stokes. If we can, its ok Vector stokes = makeNewStokes(coordinates().stokesCoordinate(coord).stokes(), cSys2.stokesCoordinate(coord).stokes()); if (stokes.nelements()==0) { String coordType = cSys1.spectralAxisNumber() == (Int)axis ? "Spectral" : "Tabular"; ImageInfo::logMessage (warnContig_p, os, relax, "Images are not contiguous along the " "concatenation axis", "For this axis, a non-regular " + coordType + " coordinate will be made"); isContig_p = False; } } else { Int worldAxis; Double axisVal1 = coordConvert(worldAxis, os, cSys1, axis, Double(shape1(axis)-1)); Double axisVal2 = coordConvert(worldAxis, os, cSys2, axis, Double(-1.0)); Double inc = cSys1.increment()(worldAxis); if (abs(axisVal2-axisVal1) > 0.01*abs(inc)) { String coordType = cSys1.spectralAxisNumber() == (Int)axis ? "Spectral" : "Tabular"; ImageInfo::logMessage (warnContig_p, os, relax, "Images are not contiguous along the " "concatenation axis", "For this axis, a non-regular " + coordType + " coordinate will be made"); isContig_p = False; } } } template Double ImageConcat::coordConvert(Int& worldAxis, LogIO& os, const CoordinateSystem& cSys, uInt axis, Double pixelCoord) const { Vector pixel(cSys.nPixelAxes()); Vector world(cSys.nWorldAxes()); // pixel = cSys.referencePixel(); pixel(axis) = pixelCoord; if (!cSys.toWorld(world, pixel)) { os << "Coordinate conversion failed because " << cSys.errorMessage() << LogIO::EXCEPTION; } worldAxis = cSys.pixelAxisToWorldAxis(axis); if (worldAxis==-1) { os << "Concatenation pixel axis has no world axis" << LogIO::EXCEPTION; } return world(worldAxis); } template void ImageConcat::checkNonConcatAxisCoordinates (LogIO& os, const ImageInterface& imageIn, Bool relax) // // Check coordinate descriptors for each non-concatenation axis // for the current image being set and the coordinates currently // set. The ImageSummary objects gives us the descriptors in pixel // axis order { const uInt axis = latticeConcat_p.axis(); ImageSummary sumIn(imageIn); // if (latticeConcat_p.isTempClose()) latticeConcat_p.reopen(0); const ImageInterface* pIm0 = dynamic_cast*>(latticeConcat_p.lattice(0)); ImageSummary sum0(*pIm0); if (latticeConcat_p.isTempClose()) latticeConcat_p.tempClose(0); // Bool pixelOrder = True; const uInt dim = sumIn.ndim(); Vector refPix = sumIn.referencePixels(); Vector refPix0 = sum0.referencePixels(); Vector refVal = sumIn.referenceValues(pixelOrder); Vector refVal0 = sum0.referenceValues(pixelOrder); Vector inc = sumIn.axisIncrements(pixelOrder); Vector inc0 = sum0.axisIncrements(pixelOrder); // for (uInt j=0; j void ImageConcat::setCoordinates() // // Updates the CoordinateSystem in the ImageConcat image. The first lattice must // be an image. The first lattice is contiguous by definition. The Coordinate // System for the first image must be set before calling this function. For // the first image, this function just sets up worldValues and pixelValues // // { LogIO os(LogOrigin("ImageConcat", __func__, WHERE)); // If the images are not contiguous along the concatenation axis, // make an irregular TabularCoordinate. As usual Stokes demands // different handling CoordinateSystem cSys = coordinates(); const uInt axis = latticeConcat_p.axis(); Int coord, axisInCoord; cSys.findPixelAxis(coord, axisInCoord, axis); const uInt nIm = latticeConcat_p.nlattices(); const uInt iIm = nIm - 1; Vector stokes; // we always must update the world values, because even if // the currently added image is contiguous, the next image to be added might not // be _updatePixelAndWorldValues(iIm); if (iIm==0) { // Hang on to the type of coordinate of the concat axis for the first image originalAxisType_p = cSys.coordinate(coord).type(); return; } if (isContig_p) { if (latticeConcat_p.isTempClose()) latticeConcat_p.reopen(iIm); if (cSys.type(coord)==Coordinate::STOKES) { if (isImage_p(iIm)) { const ImageInterface* pIm = dynamic_cast*>(latticeConcat_p.lattice(iIm)); stokes = makeNewStokes(cSys.stokesCoordinate(coord).stokes(), pIm->coordinates().stokesCoordinate(coord).stokes()); } else { // This is unlikely to work. We make a Stokes axis starting from the // last Stokes already in coordinates() + 1. WIll only work // if results in a useable Stokes axis Vector stokes1 = coordinates().stokesCoordinate(coord).stokes(); Int last = stokes1(stokes1.nelements()-1); const uInt shape = latticeConcat_p.lattice(nIm-1)->shape()(axis); Vector stokes2 (shape,0); indgen(stokes2, last+1, 1); stokes = makeNewStokes(stokes1, stokes2); } // If Stokes ok, make new StokesCoordinate, replace it and set it if (stokes.nelements()==0) { os << "Cannot concatenate this Lattice with previous images as concatenation" << endl; os << "axis is Stokes and result would be illegal" << LogIO::EXCEPTION; } else { StokesCoordinate tmp(stokes); cSys.replaceCoordinate(tmp, uInt(coord)); if (!ImageInterface::setCoordinateInfo(cSys)) { os << "Failed to save new CoordinateSystem with StokesCoordinate" << LogIO::EXCEPTION; } } } if (latticeConcat_p.isTempClose()) latticeConcat_p.tempClose(iIm); } else { // The first lattice is enforced to be an image. Always use its units and names String unit, name; Int worldAxis = cSys.pixelAxisToWorldAxis(axis); unit = cSys.worldAxisUnits()(worldAxis); name = cSys.worldAxisNames()(worldAxis); // Make TabularCoordinate and replace it. If it's not monotonic, we // can't make the TC, so fall back to CS from first image Bool ok = True; String msg; try { if (originalAxisType_p == Coordinate::SPECTRAL) { SpectralCoordinate origSpCoord = cSys.spectralCoordinate(); SpectralCoordinate newSp( origSpCoord.frequencySystem(False), worldValues_p, origSpCoord.restFrequency() ); cSys.replaceCoordinate(newSp, uInt(coord)); } else { TabularCoordinate tc(pixelValues_p, worldValues_p, unit, name); cSys.replaceCoordinate(tc, uInt(coord)); } if (!ImageInterface::setCoordinateInfo(cSys)) { String ctype = originalAxisType_p == Coordinate::SPECTRAL ? "Spectral" : "Tabular"; os << "Failed to save new CoordinateSystem with " << ctype << "Coordinate" << LogIO::EXCEPTION; } } catch (const AipsError& x) { ok = False; msg = x.getMesg(); } if (!ok) { ImageInfo::logMessage ( warnTab_p, os, True, "Could not create Coordinate because " + msg, "CoordinateSystem set to that of first image " "instead" ); } } } template void ImageConcat::_updatePixelAndWorldValues(uInt iIm) { const uInt nPixelsOld = pixelValues_p.nelements(); uInt axis = latticeConcat_p.axis(); const uInt shapeNew = latticeConcat_p.lattice(iIm)->shape()(axis); pixelValues_p.resize(nPixelsOld+shapeNew, True); worldValues_p.resize(nPixelsOld+shapeNew, True); if (isImage_p(iIm)) { if (latticeConcat_p.isTempClose()) { latticeConcat_p.reopen(iIm); } const ImageInterface* pIm = dynamic_cast*>(latticeConcat_p.lattice(iIm)); const CoordinateSystem& cSys2 = pIm->coordinates(); if (latticeConcat_p.isTempClose()) { latticeConcat_p.tempClose(iIm); } Vector p = cSys2.referencePixel(); Vector w = cSys2.referenceValue(); // For each pixel in concatenation axis for this image, find world // and pixel values Int worldAxis = cSys2.pixelAxisToWorldAxis(axis); for (uInt j=0; j Vector ImageConcat::makeNewStokes(const Vector& stokes1, const Vector& stokes2) { Vector stokes = concatenateArray(stokes1, stokes2); Bool ok = True; try { StokesCoordinate tmp(stokes); } catch (AipsError& x) { ok = False; } // if (ok) { return stokes; } else { Vector tmp; return tmp; } } template Bool ImageConcat::lock (FileLocker::LockType type, uInt nattempts) { return latticeConcat_p.lock(type, nattempts); } template void ImageConcat::unlock() { latticeConcat_p.unlock(); } template Bool ImageConcat::hasLock (FileLocker::LockType type) const { return latticeConcat_p.hasLock(type); } template void ImageConcat::resync() { latticeConcat_p.resync(); } template void ImageConcat::flush() { latticeConcat_p.flush(); } template void ImageConcat::tempClose() { latticeConcat_p.tempClose(); } template void ImageConcat::reopen() { latticeConcat_p.reopen(); } template String ImageConcat::name (Bool stripPath) const { if (fileName_p.empty()) { return "Concatenation :"; } Path path(fileName_p); if (!stripPath) { return path.absoluteName(); } return path.baseName(); } template Bool ImageConcat::isMasked() const { return latticeConcat_p.isMasked(); } template Bool ImageConcat::hasPixelMask() const { return latticeConcat_p.hasPixelMask(); } template const Lattice& ImageConcat::pixelMask() const { return latticeConcat_p.pixelMask(); } template Lattice& ImageConcat::pixelMask() { return latticeConcat_p.pixelMask(); } template const LatticeRegion* ImageConcat::getRegionPtr() const { return latticeConcat_p.getRegionPtr(); } template Bool ImageConcat::isWritable() const { return latticeConcat_p.isWritable(); } template IPosition ImageConcat::shape() const { return latticeConcat_p.shape(); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Images/ImageExpr.h000066400000000000000000000177021476623553700200370ustar00rootroot00000000000000//# ImageExpr.h: contains expressions involving images //# Copyright (C) 1994,1995,1996,1997,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_IMAGEEXPR_H #define IMAGES_IMAGEEXPR_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class JsonKVMap; class IPosition; class Slicer; class LatticeNavigator; template class LatticeIterInterface; class String; // // Hold mathematical expressions involving ImageInterface objects // // // // // // // // //
      • LatticeExpr //
      • ImageInterface // // // // This class holds a LatticeExpr object but inherits from // ImageInterface hence ImageExpr // // // // An ImageExpr object holds a LatticeExpr object which can be used // to evaluate mathematical expressions involving Lattices. ImageExpr // exists so that direct manipulation of LatticeExpr objects by methods // expecting an ImageInterface, rather than a Lattice can occur. // // The ImageExpr object is constructed from a LatticeExpr object, but // only if the latter has true Coordinates associated with it. // The ImageExpr object is not writable, so the ImageExpr object // functions like a read only ImageInterface. // // // // // PagedImage a("imageB"); // Open PagedImages // PagedImage b("imageB"); // // LatticeExprNode node(a+b); // Create ImageExpr // LatticeExpr lExpr(node); // ImageExpr iExpr(lExpr); // // LogOrigin or("imageImpl", "main()", WHERE); // Create statistics object // LogIO logger(or); // ImageStatistics stats(iExpr, logger); // Bool ok = stats.display(); // Display statistics // // // The ImageExpr object is evaluated during the call to // stats.dislay(). Previously, the expression tree // has been constructed, but not evaluated. // // // // This enables one to evaluate expressions but not to have to write them // out to an output image. // // // // template class ImageExpr: public ImageInterface { public: // The default constructor ImageExpr(); // Construct an ImageExpr from a LatticeExpr. // The expr given should be the original expression string. // The fileName argument is meant for ImageOpener. // The coordinates are taken from the expression, usually the first image. // An exception is thrown if the expression has no coordinates. ImageExpr(const LatticeExpr& latticeExpr, const String& expr, const String& fileName = String()); ImageExpr(const LatticeExpr& latticeExpr, const String& expr, const String& fileName, const JsonKVMap&); // Same as previous constructor, but the coordinates are taken from the // given LELImageCoord object. ImageExpr(const LatticeExpr& latticeExpr, const String& expr, const String& fileName, const LELImageCoord& imCoord); // Copy constructor (reference semantics) ImageExpr(const ImageExpr& other); // Destructor does nothing ~ImageExpr(); // Assignment (reference semantics) ImageExpr& operator=(const ImageExpr& other); // Make a copy of the object (reference semantics). virtual ImageInterface* cloneII() const; // Save the image in an AipsIO file with the given name. // It can be opened by ImageOpener::openExpr. virtual void save (const String& fileName) const; // Set the file name. void setFileName (const String& name) { fileName_p = name; } // Replace the miscinfo in the ImageExpr, which writes the image.expr file. // It can fail if, e.g., the directory to write to is not writable. virtual Bool setMiscInfo (const RecordInterface& newInfo); // Get the image type (returns name of derived class). virtual String imageType() const; // Has the object really a mask? virtual Bool isMasked() const; // Get the region used. virtual const LatticeRegion* getRegionPtr() const; // return the shape of the ImageExpr virtual IPosition shape() const; // Function which changes the shape of the ImageExpr. // Throws an exception as ImageExpr is not writable. virtual void resize(const TiledShape& newShape); // Do the actual get of the mask data. // The return value is always False, thus the buffer does not reference // another array. virtual Bool doGetMaskSlice (Array& buffer, const Slicer& section); // Do the actual get of the data. virtual Bool doGetSlice (Array& buffer, const Slicer& theSlice); // putSlice is not possible on an expression, so it throws an exception. virtual void doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride); // If the object is persistent, the file name is given. // Otherwise it returns the expression string given in the constructor. virtual String name (Bool stripPath=False) const; // Check class invariants. virtual Bool ok() const; // These are the implementations of the LatticeIterator letters. // not for public use virtual LatticeIterInterface* makeIter( const LatticeNavigator& navigator, Bool useRef) const; // Returns False, as the ImageExpr is not writable. virtual Bool isWritable() const; // Is the lattice persistent and can it be loaded by other processes as well? virtual Bool isPersistent() const; // Help the user pick a cursor for most efficient access if they only want // pixel values and don't care about the order or dimension of the // cursor. virtual IPosition doNiceCursorShape (uInt maxPixels) const; // Handle the (un)locking and syncing. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; virtual void resync(); virtual void tempClose(); virtual void reopen(); // // Get the lattice expression. const LatticeExpr& expression() const { return latticeExpr_p; } private: void init (const LatticeExpr& latticeExpr, const String& expr, const String& fileName, const JsonKVMap&); //# Data members LatticeExpr latticeExpr_p; Unit unit_p; String exprString_p; mutable String fileName_p; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/images/Images/ImageExpr.tcc000066400000000000000000000205611476623553700203560ustar00rootroot00000000000000//# ImageExpr.cc: defines the ImageExpr class //# Copyright (C) 1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_IMAGEEXPR_TCC #define IMAGES_IMAGEEXPR_TCC #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template ImageExpr::ImageExpr() {} template ImageExpr::ImageExpr (const LatticeExpr& latticeExpr, const String& expr, const String& fileName) { init (latticeExpr, expr, fileName, JsonKVMap()); } template ImageExpr::ImageExpr (const LatticeExpr& latticeExpr, const String& expr, const String& fileName, const JsonKVMap& jmap) { init (latticeExpr, expr, fileName, jmap); } template void ImageExpr::init (const LatticeExpr& latticeExpr, const String& expr, const String& fileName, const JsonKVMap& jmap) { latticeExpr_p = latticeExpr; fileName_p = fileName; exprString_p = expr; const LELCoordinates lelCoordinate = latticeExpr_p.lelCoordinates(); const LELLattCoordBase* pLattCoord = &(lelCoordinate.coordinates()); if (! pLattCoord->hasCoordinates() || pLattCoord->classname() != "LELImageCoord") { throw (AipsError ("ImageExpr::constructor - the " "LatticeExpr does not have coordinates")); } // Cast to get at LELImageCoord const LELImageCoord* pImCoord = dynamic_cast(pLattCoord); AlwaysAssert (pImCoord != 0, AipsError); this->setCoordsMember (pImCoord->coordinates()); this->setImageInfoMember (pImCoord->imageInfo()); if (jmap.isDefined("MiscInfo")) { TableRecord tabrec; tabrec.fromRecord (jmap.get("MiscInfo").getValueMap().toRecord()); this->setMiscInfoMember (tabrec); } else { this->setMiscInfoMember (pImCoord->miscInfo()); } this->setUnitMember (pImCoord->unit()); } template ImageExpr::ImageExpr (const LatticeExpr& latticeExpr, const String& expr, const String& fileName, const LELImageCoord& imCoord) : latticeExpr_p(latticeExpr), fileName_p (fileName) { exprString_p = expr; this->setCoordsMember (imCoord.coordinates()); this->setImageInfoMember (imCoord.imageInfo()); this->setMiscInfoMember (imCoord.miscInfo()); this->setUnitMember (imCoord.unit()); } template ImageExpr::ImageExpr (const ImageExpr& other) : ImageInterface(other), latticeExpr_p (other.latticeExpr_p), unit_p (other.unit_p), exprString_p (other.exprString_p), fileName_p (other.fileName_p) {} template ImageExpr& ImageExpr::operator=(const ImageExpr& other) { if (this != &other) { ImageInterface::operator= (other); latticeExpr_p = other.latticeExpr_p; unit_p = other.unit_p; exprString_p = other.exprString_p; fileName_p = other.fileName_p; } return *this; } template ImageExpr::~ImageExpr() {} template ImageInterface* ImageExpr::cloneII() const { return new ImageExpr (*this); } template void ImageExpr::save (const String& fileName) const { // Note that an ImageExpr is opened by ImageOpener. // Check that the expression is given. if (exprString_p.empty()) { throw AipsError ("ImageExpr cannot be made persistent, because " "its expression string is empty"); } // Create the directory if not existing already. Directory dir(fileName); if (! dir.exists()) { dir.create (False); } // Create the Json file. JsonOut jout(fileName + "/imageexpr.json"); jout.start(); jout.write ("Version", 1); String dt(ValType::getTypeStr(this->dataType())); dt.trim(); jout.write ("DataType", dt); jout.write ("ImageExpr", exprString_p); jout.write ("MiscInfo", this->miscInfo().toRecord()); jout.end(); fileName_p = fileName; } template Bool ImageExpr::setMiscInfo (const RecordInterface& newInfo) { this->setMiscInfoMember (newInfo); if (isPersistent()) { save (fileName_p); } return True; } template String ImageExpr::imageType() const { return "ImageExpr"; } template Bool ImageExpr::isMasked() const { return latticeExpr_p.isMasked(); } template const LatticeRegion* ImageExpr::getRegionPtr() const { return latticeExpr_p.getRegionPtr(); } template IPosition ImageExpr::shape() const { return latticeExpr_p.shape(); } template void ImageExpr::resize(const TiledShape&) { throw (AipsError ("ImageExpr::resize - an ImageExpr is not writable")); } template Bool ImageExpr::doGetSlice (Array& buffer, const Slicer& section) { return latticeExpr_p.doGetSlice(buffer, section); } template void ImageExpr::doPutSlice (const Array&, const IPosition&, const IPosition&) { throw (AipsError ("ImageExpr::putSlice - " "is not possible as ImageExpr is not writable")); } template String ImageExpr::name (Bool stripPath) const { if (fileName_p.empty()) { if (exprString_p.empty()) { return exprString_p; } return "Expression: " + exprString_p; } Path path(fileName_p); if (!stripPath) { return path.absoluteName(); } return path.baseName(); } template Bool ImageExpr::isPersistent() const { return ! fileName_p.empty(); } template Bool ImageExpr::isWritable() const { return False; } template IPosition ImageExpr::doNiceCursorShape (uInt maxPixels) const { return latticeExpr_p.niceCursorShape(maxPixels); } template Bool ImageExpr::ok() const { return True; } template LatticeIterInterface* ImageExpr::makeIter (const LatticeNavigator &navigator, Bool useRef) const { return latticeExpr_p.makeIter(navigator, useRef); } template Bool ImageExpr::doGetMaskSlice (Array& buffer, const Slicer& section) { return latticeExpr_p.doGetMaskSlice (buffer, section); } template Bool ImageExpr::lock (FileLocker::LockType type, uInt nattempts) { return latticeExpr_p.lock (type, nattempts); } template void ImageExpr::unlock() { latticeExpr_p.unlock(); } template Bool ImageExpr::hasLock (FileLocker::LockType type) const { return latticeExpr_p.hasLock (type); } template void ImageExpr::resync() { latticeExpr_p.resync(); } template void ImageExpr::tempClose() { latticeExpr_p.tempClose(); } template void ImageExpr::reopen() { latticeExpr_p.reopen(); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Images/ImageExprGram.cc000066400000000000000000000132771476623553700210070ustar00rootroot00000000000000//# ImageExprGram.cc: Grammar for image expressions //# Copyright (C) 1998,1999,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // ImageExprGram; grammar for image command lines // This file includes the output files of bison and flex for // parsing command lines operating on lattices. #include #include #include #include #include // needed for bison actions #include //# stdlib.h is needed for bison 1.28 and needs to be included here //# (before the flex/bison files). //# Bison defines WHERE, which is also defined in LogOrigin.h (which //# is included in auto-template mode). //# So undefine WHERE first. #undef WHERE #include //# Let clang and gcc ignore some warnings in bison/flex code. //# Undef YY_NULL because flex can redefine it slightly differently (giving a warning). #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpragmas" #pragma GCC diagnostic ignored "-Wdeprecated-register" #pragma GCC diagnostic ignored "-Wsign-compare" #include "ImageExprGram.ycc" // bison output #ifdef YY_NULL # undef YY_NULL #endif #include "ImageExprGram.lcc" // flex output #pragma GCC diagnostic pop // Define the yywrap function for flex. int ImageExprGramwrap() { return 1; } namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Declare a file global pointer to a char* for the input string. static const char* strpImageExprGram = 0; static Int posImageExprGram = 0; // Define a class to delete the yy_buffer in case of an exception. class ImageExprGramState { public: ImageExprGramState (YY_BUFFER_STATE state) : itsState (state) {} ~ImageExprGramState() { clear(); } void clear() { if (itsState) { ImageExprGram_delete_buffer(itsState); itsState=0; } } YY_BUFFER_STATE state() const { return itsState; } private: ImageExprGramState (const ImageExprGramState&); ImageExprGramState& operator= (const ImageExprGramState&); YY_BUFFER_STATE itsState; //# this is a pointer to yy_buffer_state }; //# Parse the command. int imageExprGramParseCommand (const String& command) { // Save current state for re-entrancy. int sav_yy_start = yy_start; const char* savStrpImageExprGram = strpImageExprGram; Int savPosImageExprGram= posImageExprGram; YY_BUFFER_STATE sav_state = YY_CURRENT_BUFFER; // Create a new state buffer for new expression. ImageExprGramState next (ImageExprGram_create_buffer (ImageExprGramin, YY_BUF_SIZE)); ImageExprGram_switch_to_buffer (next.state()); yy_start = 1; strpImageExprGram = command.chars(); // get pointer to command string posImageExprGram = 0; // initialize string position int sts = ImageExprGramparse(); // parse command string // The current state has to be deleted before switching back to previous. next.clear(); // Restore previous state. yy_start = sav_yy_start; strpImageExprGram = savStrpImageExprGram; posImageExprGram= savPosImageExprGram; ImageExprGram_switch_to_buffer (sav_state); return sts; } //# Give the string position. Int& imageExprGramPosition() { return posImageExprGram; } //# Get the next input characters for flex. int imageExprGramInput (char* buf, int max_size) { int nr=0; while (*strpImageExprGram != 0) { if (nr >= max_size) { break; // get max. max_size char. } buf[nr++] = *strpImageExprGram++; } return nr; } void ImageExprGramerror (const char*) { throw (AipsError ("Image Expression: Parse error at or near '" + String(ImageExprGramtext) + "'")); } String imageExprGramRemoveEscapes (const String& in) { String out; int leng = in.length(); for (int i=0; i #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // Global functions for flex/bison scanner/parser for ImageExprGram // // // // // //# Classes you should understand before using this one. //
      • ImageExprGram.l and .y (flex and bison grammar) // // // Global functions are needed to define the input of the flex scanner // and to start the bison parser. // The input is taken from a string. // // // It is necessary to be able to give an image expression in ASCII. // This can be used in glish. // // //# A List of bugs, limitations, extensions or planned refinements. // // // Declare the bison parser (is implemented by bison command). int imageExprGramParseCommand (const String& command); // The yyerror function for the parser. // It throws an exception with the current token. void ImageExprGramerror (const char*); // Give the current position in the string. // This can be used when parse errors occur. Int& imageExprGramPosition(); // Declare the input routine for flex/bison. int imageExprGramInput (char* buf, int max_size); // A function to remove escaped characters. String imageExprGramRemoveEscapes (const String& in); // A function to remove quotes from a quoted string. String imageExprGramRemoveQuotes (const String& in); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Images/ImageExprGram.ll000066400000000000000000000213211476623553700210160ustar00rootroot00000000000000/* ImageExprGram.l: Lexical analyzer for image expressions Copyright (C) 1998,1999,2000,2003 Associated Universities, Inc. Washington DC, USA. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. Correspondence concerning AIPS++ should be addressed as follows: Internet email: casa-feedback@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA */ /* yy_unput is not used, so let flex not generate it, otherwise picky compilers will issue warnings. */ %option nounput %{ #undef YY_INPUT #define YY_INPUT(buf,result,max_size) result=imageExprGramInput(buf,max_size) #undef YY_DECL #define YY_DECL int ImageExprGramlex (YYSTYPE* lvalp) %} /* Complex values can be given as: FLOATi where i is the letter i (in lowercase only). In a NAME the backslash can be used to escape special characters like -. In that way a name like DIR/NAME can be given as DIR\/NAME. Alternatively the name can be enclosed in single or double quotes. */ WHITE [ \t\n]* DIGIT [0-9] INT {DIGIT}+ FEXP [Ee][+-]?{INT} DEXP [Dd][+-]?{INT} FLOAT {INT}{FEXP}|{INT}"."{DIGIT}*({FEXP})?|{DIGIT}*"."{INT}({FEXP})? DOUBLE {INT}{DEXP}|{INT}"."{DIGIT}*({DEXP})?|{DIGIT}*"."{INT}({DEXP})? FLINT {FLOAT}|{INT} DBINT {DOUBLE}|{INT} COMPLEX {FLINT}"i" DCOMPLEX {DBINT}"i" TRUE T FALSE F INDEXIN [Ii][Nn][Dd][Ee][Xx][Ii][Nn] INDEXNOTIN [Ii][Nn][Dd][Ee][Xx][Nn][Oo][Tt][Ii][Nn] INDEXN [Ii][Nn][Dd][Ee][Xx]{INT} IN [Ii][Nn] NOT [Nn][Oo][Tt] QSTRING \"[^\"\n]*\" ASTRING \'[^\'\n]*\' UQSTRING \"[^\"\n]*\n UASTRING \'[^\'\n]*\n STRING ({QSTRING}|{ASTRING})+ USTRING ({UQSTRING}|{UASTRING})+ NAME [A-Za-z_]([A-Za-z_0-9])* TMPNAME "$"{INT} TMPREGION "$"[rR]{INT} ESCNAME ([A-Za-z_~$]|(\\.))([A-Za-z0-9._~$]|(\\.))* COLONNAME ({NAME}|{ESCNAME})?":"":"?({NAME}|{ESCNAME}) %% /* Literals */ {DCOMPLEX} { imageExprGramPosition() += yyleng; Double value; sscanf (ImageExprGramtext, "%lf%*c", &value); lvalp->val = new ImageExprParse (DComplex (0, value)); ImageExprParse::addNode (lvalp->val); return LITERAL; } {COMPLEX} { imageExprGramPosition() += yyleng; Float value; sscanf (ImageExprGramtext, "%f%*c", &value); lvalp->val = new ImageExprParse (Complex (0, value)); ImageExprParse::addNode (lvalp->val); return LITERAL; } {DOUBLE} { imageExprGramPosition() += yyleng; lvalp->val = new ImageExprParse (atof(ImageExprGramtext)); ImageExprParse::addNode (lvalp->val); return LITERAL; } {FLOAT} { imageExprGramPosition() += yyleng; lvalp->val = new ImageExprParse (Float(atof(ImageExprGramtext))); ImageExprParse::addNode (lvalp->val); return LITERAL; } {INT} { imageExprGramPosition() += yyleng; Int ival = atoi(ImageExprGramtext); Double dval = atof(ImageExprGramtext); if (ival < dval-0.1 || ival > dval+0.1) { lvalp->val = new ImageExprParse (dval); } else { lvalp->val = new ImageExprParse (ival); } ImageExprParse::addNode (lvalp->val); return LITERAL; } {TRUE} { imageExprGramPosition() += yyleng; lvalp->val = new ImageExprParse (True); ImageExprParse::addNode (lvalp->val); return LITERAL; } {FALSE} { imageExprGramPosition() += yyleng; lvalp->val = new ImageExprParse (False); ImageExprParse::addNode (lvalp->val); return LITERAL; } {INDEXIN} { imageExprGramPosition() += yyleng; return INDEXIN; } {INDEXNOTIN} { imageExprGramPosition() += yyleng; return INDEXNOTIN; } {INDEXN} { imageExprGramPosition() += yyleng; Int ival = atoi(ImageExprGramtext+5); lvalp->val = new ImageExprParse (ival); ImageExprParse::addNode (lvalp->val); return INDEXN; } {IN} { imageExprGramPosition() += yyleng; return IN; } {NOT} { imageExprGramPosition() += yyleng; return NOT; } /* A simple name can be name of constant, function, or lattice */ {NAME} { imageExprGramPosition() += yyleng; lvalp->val = new ImageExprParse (ImageExprGramtext); ImageExprParse::addNode (lvalp->val); return NAME; } /* A temporary image number can be given */ {TMPNAME} { imageExprGramPosition() += yyleng; lvalp->val = new ImageExprParse (atoi(ImageExprGramtext+1)); ImageExprParse::addNode (lvalp->val); return NAME; } /* A temporary region number can be given */ {TMPREGION} { imageExprGramPosition() += yyleng; lvalp->val = new ImageExprParse (atoi(ImageExprGramtext+2)); ImageExprParse::addNode (lvalp->val); return TMPREG; } /* A lattice name can have more characters than a simple name Note that the name of a lattice file can be given in 3 ways: - When it contains only alphanumerical characters and ._~$ it can be given as such. E.g. a.img - When other characters are used, they have to be escaped. This can be done in 2 ways: - Enclose the string in single or double quotes (concatenation is possible). E.g. "a/b/c" results in a/b/c "a'b"'c"d' results in a'bc"d - Use the backslash escape character. E.g. a\/b\/c results in a/b/c Furthermore a name can look like ::region or lattice::region indicating that a region is given. */ {ESCNAME} { imageExprGramPosition() += yyleng; lvalp->val = new ImageExprParse (imageExprGramRemoveEscapes (ImageExprGramtext)); ImageExprParse::addNode (lvalp->val); return LATNAME; } {COLONNAME} { imageExprGramPosition() += yyleng; lvalp->val = new ImageExprParse (imageExprGramRemoveEscapes (ImageExprGramtext)); ImageExprParse::addNode (lvalp->val); return LATNAME; } {STRING} { imageExprGramPosition() += yyleng; lvalp->val = new ImageExprParse (imageExprGramRemoveQuotes (ImageExprGramtext)); ImageExprParse::addNode (lvalp->val); return LATNAME; } "==" { imageExprGramPosition() += yyleng; return EQ; } ">=" { imageExprGramPosition() += yyleng; return GE; } ">" { imageExprGramPosition() += yyleng; return GT; } "<=" { imageExprGramPosition() += yyleng; return LE; } "<" { imageExprGramPosition() += yyleng; return LT; } "!=" { imageExprGramPosition() += yyleng; return NE; } "&&" { imageExprGramPosition() += yyleng; return AND; } "||" { imageExprGramPosition() += yyleng; return OR; } "!" { imageExprGramPosition() += yyleng; return NOT; } "^" { imageExprGramPosition() += yyleng; return POWER; } "*" { imageExprGramPosition() += yyleng; return TIMES; } "/" { imageExprGramPosition() += yyleng; return DIVIDE; } "%" { imageExprGramPosition() += yyleng; return MODULO; } "+" { imageExprGramPosition() += yyleng; return PLUS; } "-" { imageExprGramPosition() += yyleng; return MINUS; } "(" { imageExprGramPosition() += yyleng; return LPAREN; } ")" { imageExprGramPosition() += yyleng; return RPAREN; } "[" { imageExprGramPosition() += yyleng; return LBRACKET; } "]" { imageExprGramPosition() += yyleng; return RBRACKET; } "," { imageExprGramPosition() += yyleng; return COMMA; } ":" { imageExprGramPosition() += yyleng; return COLON; } /* Whitespace is skipped */ {WHITE} { imageExprGramPosition() += yyleng; } /* An unterminated string is an error */ {USTRING} { throw (AipsError ("ImageExprParse: Unterminated string")); } /* terminate on EOF */ <> { yyterminate(); } /* Any other character is invalid */ . { return YYERRCODE; } %% casacore-3.7.1/images/Images/ImageExprGram.yy000066400000000000000000000175131476623553700210600ustar00rootroot00000000000000/* ImageExprGram.y: Parser for image expressions Copyright (C) 1998,1999,2003 Associated Universities, Inc. Washington DC, USA. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. Correspondence concerning AIPS++ should be addressed as follows: Internet email: casa-feedback@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA */ %{ using namespace casacore; %} %pure-parser /* make parser re-entrant */ %union { LatticeExprNode* node; ImageExprParse* val; Block* scalarvector; vector* slicelist; Slice* slice; } %token NAME /* name of constant, function, or lattice */ %token LATNAME /* lattice name */ %token TMPREG /* temporary region name */ %token LITERAL %token INDEXN %token INDEXIN %token INDEXNOTIN %token IN %token NOT %token LPAREN %token RPAREN %token LBRACKET %token RBRACKET %token COMMA %token COLON %type orexpr %type andexpr %type relexpr %type arithexpr %type simexpr %type scalarlist; %type rangelist; %type frange; %left OR %left AND %nonassoc EQ GT GE LT LE NE %left PLUS MINUS %left TIMES DIVIDE MODULO %nonassoc UNARY %nonassoc NOT %right POWER %{ int ImageExprGramlex (YYSTYPE*); %} %% command: orexpr { ImageExprParse::setNode (*$1); } ; orexpr: andexpr { $$ = $1; } | orexpr OR andexpr { $$ = new LatticeExprNode (*$1 || *$3); ImageExprParse::addNode ($$); } ; andexpr: relexpr { $$ = $1; } | andexpr AND relexpr { $$ = new LatticeExprNode (*$1 && *$3); ImageExprParse::addNode ($$); } ; relexpr: arithexpr { $$ = $1; } | arithexpr EQ arithexpr { $$ = new LatticeExprNode (*$1 == *$3); ImageExprParse::addNode ($$); } | arithexpr GT arithexpr { $$ = new LatticeExprNode (*$1 > *$3); ImageExprParse::addNode ($$); } | arithexpr GE arithexpr { $$ = new LatticeExprNode (*$1 >= *$3); ImageExprParse::addNode ($$); } | arithexpr LT arithexpr { $$ = new LatticeExprNode (*$1 < *$3); ImageExprParse::addNode ($$); } | arithexpr LE arithexpr { $$ = new LatticeExprNode (*$1 <= *$3); ImageExprParse::addNode ($$); } | arithexpr NE arithexpr { $$ = new LatticeExprNode (*$1 != *$3); ImageExprParse::addNode ($$); } ; arithexpr: simexpr { $$ = $1; } | arithexpr PLUS arithexpr { $$ = new LatticeExprNode (*$1 + *$3); ImageExprParse::addNode ($$); } | arithexpr MINUS arithexpr { $$ = new LatticeExprNode (*$1 - *$3); ImageExprParse::addNode ($$); } | arithexpr TIMES arithexpr { $$ = new LatticeExprNode (*$1 * *$3); ImageExprParse::addNode ($$); } | arithexpr DIVIDE arithexpr { $$ = new LatticeExprNode (*$1 / *$3); ImageExprParse::addNode ($$); } | arithexpr MODULO arithexpr { $$ = new LatticeExprNode (fmod (*$1, *$3)); ImageExprParse::addNode ($$); } | MINUS arithexpr %prec UNARY { $$ = new LatticeExprNode (-*$2); ImageExprParse::addNode ($$); } | PLUS arithexpr %prec UNARY { $$ = $2; } | NOT arithexpr { $$ = new LatticeExprNode (!*$2); ImageExprParse::addNode ($$); } | arithexpr POWER arithexpr { $$ = new LatticeExprNode (pow (*$1, *$3)); ImageExprParse::addNode ($$); } ; simexpr: LPAREN orexpr RPAREN { $$ = $2; } | simexpr LBRACKET orexpr RBRACKET { $$ = new LatticeExprNode ((*$1)[*$3]); ImageExprParse::addNode ($$); } | NAME LPAREN RPAREN { $$ = new LatticeExprNode ($1->makeFuncNode()); ImageExprParse::addNode ($$); } | NAME LPAREN orexpr RPAREN { $$ = new LatticeExprNode ($1->makeFuncNode (*$3)); ImageExprParse::addNode ($$); } | NAME LPAREN orexpr COMMA orexpr RPAREN { $$ = new LatticeExprNode ($1->makeFuncNode (*$3, *$5)); ImageExprParse::addNode ($$); } | NAME LPAREN orexpr COMMA orexpr COMMA orexpr RPAREN { $$ = new LatticeExprNode ($1->makeFuncNode (*$3, *$5, *$7)); ImageExprParse::addNode ($$); } | INDEXIN LPAREN orexpr COMMA LBRACKET rangelist RBRACKET RPAREN { $$ = new LatticeExprNode (ImageExprParse::makeIndexinNode (*$3, *$6)); ImageExprParse::addNode ($$); delete $6; } | INDEXNOTIN LPAREN orexpr COMMA LBRACKET rangelist RBRACKET RPAREN { LatticeExprNode node (ImageExprParse::makeIndexinNode (*$3, *$6)); $$ = new LatticeExprNode (!node); ImageExprParse::addNode ($$); delete $6; } | INDEXN IN LBRACKET rangelist RBRACKET { LatticeExprNode axis($1->makeLiteralNode()); $$ = new LatticeExprNode (ImageExprParse::makeIndexinNode (axis, *$4)); ImageExprParse::addNode ($$); delete $4; } | INDEXN NOT IN LBRACKET rangelist RBRACKET { LatticeExprNode axis($1->makeLiteralNode()); LatticeExprNode node(ImageExprParse::makeIndexinNode (axis, *$5)); $$ = new LatticeExprNode (!node); ImageExprParse::addNode ($$); delete $5; } | LATNAME { $$ = new LatticeExprNode ($1->makeLRNode()); ImageExprParse::addNode ($$); } | NAME { $$ = new LatticeExprNode ($1->makeLitLRNode()); ImageExprParse::addNode ($$); } | TMPREG { $$ = new LatticeExprNode ($1->makeRegionNode()); ImageExprParse::addNode ($$); } | LITERAL { $$ = new LatticeExprNode ($1->makeLiteralNode()); ImageExprParse::addNode ($$); } | LBRACKET scalarlist RBRACKET { $$ = new LatticeExprNode (ImageExprParse::makeValueList(*$2)); delete $2; ImageExprParse::addNode ($$); } ; scalarlist: scalarlist COMMA orexpr { $$ = $1; uInt nr = $$->nelements(); $$->resize (nr+1); (*$$)[nr] = *$3; } | orexpr { $$ = new Block(1, *$1); } ; rangelist: rangelist COMMA frange { $$ = $1; $$->push_back (*$3); delete $3; } | frange { $$ = new vector; $$->push_back (*$1); delete $1; } ; frange: LITERAL { $$ = ImageExprParse::makeSlice (*$1); } | LITERAL COLON LITERAL { $$ = ImageExprParse::makeSlice (*$1, *$3); } | LITERAL COLON LITERAL COLON LITERAL { $$ = ImageExprParse::makeSlice (*$1, *$3, *$5); } ; %% casacore-3.7.1/images/Images/ImageExprParse.cc000066400000000000000000000667161476623553700212010ustar00rootroot00000000000000//# ImageExprParse.cc: Classes to hold results from image expression parser //# Copyright (C) 1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Define pointer blocks holding temporary lattices and regions. static const Block* theTempLattices; static const PtrBlock* theTempRegions; //# Define static for holding the global directory name. static String theDirName; //# Define a block holding allocated nodes. //# They will be deleted when the expression is parsed. //# In that way they are also deleted in case of exceptions. static Block theNodes; static Block theNodesType; static uInt theNrNodes; //# Hold the last table used to lookup unqualified region names. static Table theLastTable; //# Hold a pointer to the last HDF5 file to lookup unqualified region names. static std::shared_ptr theLastHDF5; #define SAVE_GLOBALS \ const Block* savTempLattices=theTempLattices; \ const PtrBlock* savTempRegions=theTempRegions; \ String savDirName=theDirName; \ Block savNodes=theNodes; \ Block savNodesType=theNodesType; \ uInt savNrNodes=theNrNodes; \ Table savLastTable=theLastTable; \ std::shared_ptr savLastHDF5=theLastHDF5; #define RESTORE_GLOBALS \ theTempLattices=savTempLattices; \ theTempRegions=savTempRegions; \ theDirName=savDirName; \ theNodes=savNodes; \ theNodesType=savNodesType; \ theNrNodes=savNrNodes; \ theLastTable=savLastTable; \ theLastHDF5=savLastHDF5; // Clear the global info. void imageExprParse_clear() { theNrNodes = 0; theLastTable = Table(); theLastHDF5 = 0; } // Is there no last table or HDF5 file? Bool imageExprParse_hasNoLast() { return (theLastTable.isNull() && !theLastHDF5); } //# Initialize static members. LatticeExprNode ImageExprParse::theirNode; vector ImageExprParse::theirNames; Int ImageExprParse::theirLevel=0; ImageExprParse::ImageExprParse (Bool value) : itsType (TpBool), itsBval (value) {} ImageExprParse::ImageExprParse (Int value) : itsType (TpInt), itsIval (value) {} ImageExprParse::ImageExprParse (Float value) : itsType (TpFloat), itsFval (value) {} ImageExprParse::ImageExprParse (Double value) : itsType (TpDouble), itsDval (value) {} ImageExprParse::ImageExprParse (const Complex& value) : itsType (TpComplex), itsCval (value) {} ImageExprParse::ImageExprParse (const DComplex& value) : itsType (TpDComplex), itsDCval (value) {} ImageExprParse::ImageExprParse (const Char* value) : itsType (TpString), itsSval (String(value)) { ThrowIf(itsSval.empty(), "Illegal empty expression"); } ImageExprParse::ImageExprParse (const String& value) : itsType (TpString), itsSval (value) { ThrowIf(itsSval.empty(), "Illegal empty expression"); } Table& ImageExprParse::getRegionTable (void*, Bool) { return theLastTable; } const std::shared_ptr& ImageExprParse::getRegionHDF5 (void*) { return theLastHDF5; } void ImageExprParse::addNode (LatticeExprNode* node) { if (theNrNodes >= theNodes.nelements()) { theNodes.resize (theNrNodes + 32); theNodesType.resize (theNrNodes + 32); } theNodes[theNrNodes] = node; theNodesType[theNrNodes] = True; theNrNodes++; } void ImageExprParse::addNode (ImageExprParse* node) { if (theNrNodes >= theNodes.nelements()) { theNodes.resize (theNrNodes + 32); theNodesType.resize (theNrNodes + 32); } theNodes[theNrNodes] = node; theNodesType[theNrNodes] = False; theNrNodes++; } void ImageExprParse::deleteNodes() { for (uInt i=0; i dummyLat; PtrBlock dummyReg; return command (str, dummyLat, dummyReg, dirName); } LatticeExprNode ImageExprParse::command (const String& str, const Block& tempLattices, const PtrBlock& tempRegions, const String& dirName) { // Clear the image names if at the top level. vector savNames = theirNames; if (theirLevel == 0) { theirNames.clear(); } // Save the global variables to make it re-entrant. // Note that if a persistent ImageExpr is used in another expression, // ImageOpener will call ::command recursively. SAVE_GLOBALS; theTempLattices = &tempLattices; theTempRegions = &tempRegions; theDirName = dirName; imageExprParse_clear(); String message; String command = str + '\n'; Bool error = False; theirLevel++; try { // Parse and execute the command. if (imageExprGramParseCommand(command) != 0) { throw (AipsError("Parse error in image expression " + str)); } } catch (std::exception& x) { message = x.what(); error = True; } //# Save the resulting expression and clear the common node object. //# Only the top level image names are kept. LatticeExprNode node = theirNode; theirNode = LatticeExprNode(); if (--theirLevel > 0) { theirNames = savNames; } deleteNodes(); imageExprParse_clear(); //# If an exception was thrown; throw it again with the message. //# Get rid of the constructed node. if (error) { node = LatticeExprNode(); throw (AipsError(message + '\n' + "Scanned so far: " + command.before(imageExprGramPosition()))); } // Restore the global variables to make it re-entrant. RESTORE_GLOBALS; return node; } LatticeExprNode ImageExprParse::makeFuncNode() const { AlwaysAssert (itsType == TpString, AipsError); String val(itsSval); val.downcase(); if (val == "pi") { return LatticeExprNode (M_PI); } else if (val == "e") { return LatticeExprNode (M_E); } else { throw (AipsError ("0-argument function " + itsSval + " is unknown")); } return LatticeExprNode(); } LatticeExprNode ImageExprParse::makeFuncNode (const LatticeExprNode& arg1) const { AlwaysAssert (itsType == TpString, AipsError); String val(itsSval); val.downcase(); if (val == "sin") { return sin(arg1); } else if (val == "sinh") { return sinh(arg1); } else if (val == "asin") { return asin(arg1); } else if (val == "cos") { return cos(arg1); } else if (val == "cosh") { return cosh(arg1); } else if (val == "acos") { return acos(arg1); } else if (val == "tan") { return tan(arg1); } else if (val == "tanh") { return tanh(arg1); } else if (val == "atan") { return atan(arg1); } else if (val == "exp") { return exp(arg1); } else if (val == "log") { return log(arg1); } else if (val == "log10") { return log10(arg1); } else if (val == "sqrt") { return sqrt(arg1); } else if (val == "ceil") { return ceil(arg1); } else if (val == "floor") { return floor(arg1); } else if (val == "round") { return round(arg1); } else if (val == "sign") { return sign(arg1); } else if (val == "conj") { return conj(arg1); } else if (val == "abs" || val == "amplitude") { return abs(arg1); } else if (val == "arg" || val == "phase") { return arg(arg1); } else if (val == "real") { return real(arg1); } else if (val == "imag") { return imag(arg1); } else if (val == "min") { return min(arg1); } else if (val == "max") { return max(arg1); } else if (val == "median") { return median(arg1); } else if (val == "mean") { return mean(arg1); } else if (val == "variance") { return variance(arg1); } else if (val == "stddev") { return stddev(arg1); } else if (val == "avdev") { return avdev(arg1); } else if (val == "sum") { return sum(arg1); } else if (val == "replace") { return replace(arg1, 0); } else if (val == "ndim") { return ndim(arg1); } else if (val == "nelements" || val == "count") { return nelements(arg1); } else if (val == "any") { return any(arg1); } else if (val == "all") { return all(arg1); } else if (val == "ntrue") { return ntrue(arg1); } else if (val == "nfalse") { return nfalse(arg1); } else if (val == "isnan") { return isNaN(arg1); } else if (val == "mask") { return mask(arg1); } else if (val == "value") { return value(arg1); } else if (val == "float") { return toFloat(arg1); } else if (val == "double") { return toDouble(arg1); } else if (val == "complex") { return toComplex(arg1); } else if (val == "dcomplex") { return toDComplex(arg1); } else if (val == "bool" || val == "boolean") { return toBool(arg1); } else { throw (AipsError ("1-argument function " + itsSval + " is unknown")); } return LatticeExprNode(); } LatticeExprNode ImageExprParse::makeFuncNode (const LatticeExprNode& arg1, const LatticeExprNode& arg2) const { AlwaysAssert (itsType == TpString, AipsError); String val(itsSval); val.downcase(); if (val == "atan2") { return atan2(arg1, arg2); } else if (val == "pow") { return pow(arg1, arg2); } else if (val == "fmod") { return fmod(arg1, arg2); } else if (val == "min") { return min(arg1, arg2); } else if (val == "max") { return max(arg1, arg2); } else if (val == "complex") { return formComplex(arg1, arg2); } else if (val == "length") { return length(arg1, arg2); } else if (val == "amp") { return amp(arg1, arg2); } else if (val == "pa") { return pa(arg1, arg2); } else if (val == "spectralindex") { return spectralindex(arg1, arg2); } else if (val == "fractile") { return fractile(arg1, arg2); } else if (val == "fractilerange") { return fractileRange(arg1, arg2); } else if (val == "replace") { return replace(arg1, arg2); } else if (val == "rebin") { return rebin(arg1, makeBinning(arg2)); } else { throw (AipsError ("2-argument function " + itsSval + " is unknown")); } return LatticeExprNode(); } LatticeExprNode ImageExprParse::makeFuncNode (const LatticeExprNode& arg1, const LatticeExprNode& arg2, const LatticeExprNode& arg3) const { AlwaysAssert (itsType == TpString, AipsError); String val(itsSval); val.downcase(); if (val == "iif") { return iif(arg1, arg2, arg3); } else if (val == "fractilerange") { return fractileRange(arg1, arg2, arg3); } else { throw (AipsError ("3-argument function " + itsSval + " is unknown")); } return LatticeExprNode(); } LatticeExprNode ImageExprParse::makeLiteralNode() const { switch (itsType) { case TpBool: return LatticeExprNode (itsBval); case TpInt: return LatticeExprNode (itsIval); case TpFloat: return LatticeExprNode (itsFval); case TpDouble: return LatticeExprNode (itsDval); case TpComplex: return LatticeExprNode (itsCval); case TpDComplex: return LatticeExprNode (itsDCval); default: throw (AipsError ("ImageExprParse: unknown data type for literal")); } return LatticeExprNode(); } LatticeExprNode ImageExprParse::makeValueList (const Block& values) { // First determine the resulting data type (which is the 'highest' one). // It also checks if no mix of e.g. bool and numeric is used. DataType dtype = values[0].dataType(); for (uInt i=0; i vals(values.nelements()); for (uInt i=0; i(vals)); } case TpFloat: { Vector vals(values.nelements()); for (uInt i=0; i(vals)); } case TpDouble: { Vector vals(values.nelements()); for (uInt i=0; i(vals)); } case TpComplex: { Vector vals(values.nelements()); for (uInt i=0; i(vals)); } case TpDComplex: { Vector vals(values.nelements()); for (uInt i=0; i(vals)); } default: throw (AipsError ("ImageExprParse: unknown data type for value list")); } } IPosition ImageExprParse::makeBinning (const LatticeExprNode& values) { Vector vals; if (values.dataType() != TpFloat && values.dataType() != TpDouble) { throw (AipsError ("ImageExprParse: invalid data type for rebin factors")); } if (values.isScalar()) { vals.resize (1); vals[0] = values.getDouble(); } else { if (values.dataType() == TpFloat) { Vector val (values.getArrayFloat()); vals.resize (val.nelements()); for (uInt i=0; i end.itsIval) { throw AipsError("ImageExprParse: in s:e:i s must be <= e"); } return new Slice(start.itsIval, end.itsIval-start.itsIval+1); } Slice* ImageExprParse::makeSlice (const ImageExprParse& start, const ImageExprParse& end, const ImageExprParse& incr) { if (start.itsType!=TpInt || end.itsType!=TpInt || incr.itsType!=TpInt) { throw AipsError("ImageExprParse: s:e:i has to consist of integer values"); } if (start.itsIval > end.itsIval) { throw AipsError("ImageExprParse: in s:e:i s must be <= e"); } return new Slice(start.itsIval, end.itsIval, incr.itsIval, False); } LatticeExprNode ImageExprParse::makeIndexinNode (const LatticeExprNode& axis, const vector& slices) { // Determine maximum end value. size_t maxEnd = 0; for (uInt i=0; i maxEnd) { maxEnd = slices[i].end(); } } // Create a vector of that length and initialize to False. // Set the vector to True for all ranges. Vector flags(maxEnd+1, False); for (uInt i=0; i(flags)); } LatticeExprNode ImageExprParse::makeLRNode() const { // If the name is numeric, we have a temporary lattice number. // Find it in the block of temporary lattices. if (itsType == TpInt) { Int latnr = itsIval-1; if (latnr < 0 || latnr >= Int(theTempLattices->nelements())) { throw (AipsError ("ImageExprParse: invalid temporary image " "number given")); } return ((*theTempLattices)[latnr]); } // A true name has been given. // Split it using : as separator (:: is full separator). // However, ignore if : is escaped with a backslash. std::vector names; int leng = itsSval.length(); String part; for (int i=0; i 3) { throw (AipsError ("ImageExprParse: '" + itsSval + "' is an invalid lattice, image, " "or region name")); } } // If 1 element is given, try if it is a lattice or image. // If that does not succeed, it'll be tried later as a region. if (names.size() == 1) { LatticeExprNode node; if (tryLatticeNode (node, addDir(names[0]))) { theirNames.push_back (names[0]); return node; } } // If 2 elements given, it should be an image with a mask name. if (names.size() == 2) { theirNames.push_back (names[0]); return makeImageNode (addDir(names[0]), names[1]); } // One or three elements have been given. // If the first one is empty, a table must have been used already. if (names.size() == 1) { if (imageExprParse_hasNoLast()) { throw (AipsError ("ImageExprParse: '" + itsSval + "' is an unknown lattice or image " "or it is an unqualified region")); } } else if (names[0].empty()) { if (imageExprParse_hasNoLast()) { throw (AipsError ("ImageExprParse: unqualified region '" + itsSval + "' is used before any table is used")); } } else { // The first name is given; see if it is a readable table or HDF5 file. String fileName = addDir(names[0]); theirNames.push_back (names[0]); if (Table::isReadable (fileName)) { Table table (fileName); theLastTable = table; } else if (HDF5File::isHDF5(fileName)) { theLastHDF5 = std::make_shared(fileName); theLastTable = Table(); } else { throw (AipsError ("ImageExprParse: the table used in region name'" + itsSval + "' is unknown")); } } // Now try to find the region in the file. ImageRegion* regPtr = 0; int index = (names.size() == 1 ? 0 : 2); if (! theLastTable.isNull()) { RegionHandlerTable regHand(getRegionTable, 0); regPtr = regHand.getRegion (names[index], RegionHandler::Any, False); } if (theLastHDF5) { RegionHandlerHDF5 regHand(getRegionHDF5, 0); regPtr = regHand.getRegion (names[index], RegionHandler::Any, False); } if (regPtr == 0) { if (index == 0) { throw (AipsError ("ImageExprParse: '" + itsSval + "' is an unknown lattice, image, or region")); } else { throw (AipsError ("ImageExprParse: region '" + itsSval + " is an unknown region")); } } LatticeExprNode node (*regPtr); delete regPtr; return node; } String ImageExprParse::setAddDir (const String& dirName, const String& fileName) { theDirName = dirName; return addDir (fileName); } String ImageExprParse::addDir (const String& fileName) { // Prepend file name with directory if needed. if (theDirName.empty() || fileName.empty()) { return fileName; } // Expand file name to deal with cases like $HOME. String name = Path(fileName).expandedName(); if (name[0] == '/') { return fileName; } return theDirName + '/' + fileName; } Bool ImageExprParse::tryLatticeNode (LatticeExprNode& node, const String& name) const { // Try to open the image in a standard way. LatticeBase* pLatt = ImageOpener::openImage (name); if (pLatt) { String type; switch (pLatt->dataType()) { case TpFloat: { ImageInterface* img = dynamic_cast*>(pLatt); AlwaysAssert (img!=0, AipsError); node = LatticeExprNode (*img); type = img->imageType(); break; } case TpDouble: { ImageInterface* img = dynamic_cast*>(pLatt); AlwaysAssert (img!=0, AipsError); node = LatticeExprNode (*img); type = img->imageType(); break; } case TpComplex: { ImageInterface* img = dynamic_cast*>(pLatt); AlwaysAssert (img!=0, AipsError); node = LatticeExprNode (*img); type = img->imageType(); break; } case TpDComplex: { ImageInterface* img = dynamic_cast*>(pLatt); AlwaysAssert (img!=0, AipsError); node = LatticeExprNode (*img); type = img->imageType(); break; } default: delete pLatt; throw AipsError ("ImageExprParse: " + name + " is a PagedImage " "with an unsupported data type"); } // Set the last table used (for finding unqualified regions). if (type == "PagedImage") { theLastTable = Table(name); } else if (type == "HDF5Image") { theLastHDF5 = std::make_shared(name); theLastTable = Table(); } delete pLatt; return True; } // Try if it is a PagedArray. if (!Table::isReadable(name)) { return False; } Table table(name); String type = table.tableInfo().type(); if (type != TableInfo::type(TableInfo::PAGEDARRAY)) { return False; } if (table.nrow() != 1) { throw (AipsError ("ImageExprParse can only handle Lattices/" "Images with 1 row")); } DataType dtype = TpOther; String colName; ColumnDesc cdesc = table.tableDesc()[0]; if (cdesc.isArray()) { dtype = cdesc.dataType(); colName = cdesc.name(); } switch (dtype) { case TpBool: node = LatticeExprNode (PagedArray (table, colName, 0)); break; case TpFloat: node = LatticeExprNode (PagedArray (table, colName, 0)); break; case TpDouble: node = LatticeExprNode (PagedArray (table, colName, 0)); break; case TpComplex: node = LatticeExprNode (PagedArray (table, colName, 0)); break; case TpDComplex: node = LatticeExprNode (PagedArray (table, colName, 0)); break; default: throw (AipsError ("ImageExprParse: " + name + " is a PagedArray " "with an unsupported data type")); } // This is now the last table used (for finding unqualified regions). theLastTable = table; return True; } LatticeExprNode ImageExprParse::makeImageNode (const String& name, const String& mask) const { // Look if we need a mask for the image. // By default we do need one. MaskSpecifier spec(True); if (! mask.empty()) { String maskName = mask; maskName.upcase(); if (maskName == "NOMASK") { spec = MaskSpecifier(False); } else { spec = MaskSpecifier(mask); } } LatticeExprNode node; if (! Table::isReadable(name)) { LatticeBase* lattPtr = ImageOpener::openImage (name, spec); ImageInterface* img = 0; if (lattPtr != 0) { img = dynamic_cast*>(lattPtr); } if (img == 0) { throw AipsError ("ImageExprParse: " + name + " has an unknown image type"); } node = LatticeExprNode (*img); delete img; return node; } Table table(name); String type = table.tableInfo().type(); if (type != TableInfo::type(TableInfo::PAGEDIMAGE)) { throw (AipsError ("ImageExprParse: " + name + " is not a PagedImage")); } if (table.nrow() != 1) { throw (AipsError ("ImageExprParse can only handle Lattices/" "Images with 1 row")); } DataType dtype = TpOther; ColumnDesc cdesc = table.tableDesc()[0]; if (cdesc.isArray()) { dtype = cdesc.dataType(); } // Create the node from the lattice (and optional mask). switch (dtype) { case TpFloat: { node = LatticeExprNode (PagedImage (table, spec)); break; } /// case TpDouble: /// { /// node = LatticeExprNode (PagedImage (table, spec)); /// break; /// } case TpComplex: { node = LatticeExprNode (PagedImage (table, spec)); break; } /// case TpDComplex: /// { /// node = LatticeExprNode (PagedImage (table, spec)); /// break; /// } default: throw (AipsError ("ImageExprParse: " + name + " is a PagedImage " "with an unsupported data type")); } // This is now the last table used (for finding unqualified regions). theLastTable = table; return node; } LatticeExprNode ImageExprParse::makeLitLRNode() const { // The following outcommented code makes it possible to specify // a constant without (). // E.g. image.file * pi // instead of image.file * pi() // However, it forbids the use of pi, e, etc. as a lattice name and // may make things unclear. Therefore it is not supported (yet?). // This could be solved by enclosing such a name in quotes, e.g., 'pi'. /// if (itsSval == "pi") { /// return LatticeExprNode (C::pi); /// } else if (itsSval == "e") { /// return LatticeExprNode (C::e); /// } // It is not the name of a constant, so it must be a lattice name. return makeLRNode(); } LatticeExprNode ImageExprParse::makeRegionNode() const { // The name should be numeric. // Find it in the block of temporary lattices. AlwaysAssert (itsType == TpInt, AipsError); Int regnr = itsIval-1; if (regnr < 0 || regnr >= Int(theTempRegions->nelements())) { throw (AipsError ("ImageExprParse: invalid temporary region " "number given")); } return *((*theTempRegions)[regnr]); } } //# NAMESPACE CASACORE - END casacore-3.7.1/images/Images/ImageExprParse.h000066400000000000000000000331461476623553700210320ustar00rootroot00000000000000//# ImageExprParse.h: Classes to hold results from image expression parser //# Copyright (C) 1998,1999,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_IMAGEEXPRPARSE_H #define IMAGES_IMAGEEXPRPARSE_H //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Block; template class PtrBlock; class ImageRegion; class Table; class Slice; // // Class to hold values from image expression parser // // // // // //# Classes you should understand before using this one. //
      • LatticeExpr // // // ImageExprParse is the class used to parse an image expression command. // // // ImageExprParse is used by the parser of image expression statements. // The parser is written in Bison and Flex in files ImageExprGram.y and .l. // The statements in there use the routines in this file to act // upon a reduced rule. //

        // The main function (and the only function to be used by a user) is the // static function ImageExprParse::command which parses an expression command. // It returns a LatticeExprNode // object containing the expression represented as a tree. // The object can be used as a Lattice(Expr) in other operations. //

        // The syntax of the command is similar to that of expressions in C++. // E.g. // // min(img1, img2) + sin(img3) // // The following items can be used in an expression: //

          //
        • Binary operators +, -, *, /, % (modulo), and ^ (power). //
        • Unary operators + and -. //
        • Comparison operators ==, >, >=, <, <=, and !=. //
        • Logical operators &&, ||, and !. //
        • Constant single and double precision values. //
          No exponent or exponent "e" results in single precision (Float), // while "d" results in double precision (Double). //
        • The imaginary part of a complex value can be given by the suffix "i". // A full complex number can be given by addition. E.g. "3+4i". // The complex is single (Complex) or double (DComplex) precision // depending on the constituting parts. //
        • The special constants pi and e can be given as a double precision // value by means of the functions pi() and e(). //
        • Boolean constants T and F can be given. //
        • A lot of functions are available. // They are the same as the ones supported by class // LatticeExprNode. //
        • Explicit conversion functions float, double, complex and dcomplex // are available. Conversions are automatically done where needed, // but for performance reasons it may sometimes be better to do // explicit conversions. See also below in the first example. //
        • An image can to be given using its file name. The file name // can contain environment variables and user home directories // using the standard UNIX syntax $ENVVAR and ~username. // There are 3 ways to specify a file name: //
            //
          1. When the name contains no other special characters than // $, ~, and . it can be given as such. //
          2. Backslashes can be used to escape individual special characters. //
          3. The full name can be enclosed in quotes (single or double) // to escape the entire name. Adjacent quoted parts // are combined to one name, which can be used to use quotes // in the file name. //
          // Note that escaping has to be used too for the file name // T or F (otherwise it is the boolean constant). // E.g. // // image.data // "~noordam/data/image.data" // "~/image.data" // "$HOME/image.data" // $HOME\/image.data // "ab'c"'d"e' results in ab'cd"e // // Only input images with data type Float and Complex are supported, // because those data types are the only ones used so far. // Support of Bool, Double, and DComplex is very simple to build in. // The resulting lattice can be of type Bool, Float, Double, // Complex, and DComplex. //
        • An image can also be given by means of the $n notation, // where n is the sequence number in the // tempLattices argument given to the command // function. Note that the sequence numbers start counting at 1 // (to be compliant with glish indexing). //
          It can, for instance, be used to use non-persistent lattices // in an expression. //
        // When the expression is parsed, it is checked if the images and lattices // involved have conforming shapes and coordinates. Note, however, that // some functions (e.g. mean) reduce an image to a scalar. Such an image // can have a different shape and coordinates. //

        // The data types of the images and constants involved can be different. // The data type of a subexpression is the common data type (e.g. // Float and Double result in Double; Complex and Double result in DComplex). // Automatic implicit conversions are done where needed. However, for // performance reasons it may sometimes be better to convert explicitly. // See below in the first example. //

        // The expression evaluator (which is not part of the parser) evaluates // the expression in chunks to avoid having to keep large temporary // results. A scalar subexpression is evaluated only once to avoid // unnecessary (possibly expensive) calculations. //

        // Some examples: //

        //
        img1 + min(float(pi()), mean(img2)) //
        Suppose img1 and img2 are images with single precision data. // They do not need to have conforming shapes and coordinates, // because only the mean of img2 is used. //
        Note that pi is explicitly converted to single precision, // because pi() results in a Double. If that was not done, // the expression result would be a Double with the effect that // all data of img1 had to be converted to Double. //
        min(img1, (min(img1)+max(img1))/2) //
        This example shows that there are 2 min functions. One with a // single argument returning the minimum value of that image. // The other with 2 arguments returning a lattice containing // img1 data clipped at the value of the 2nd argument. //
        //
        // // // LatticeExpr expr ("a + sin(b)"); // ArrayLattice arr(expr.shape()); // arr.copyData (expr); // // Line 1 creates a LatticeExpr object for the given expression. Note that // a and b are names of lattice files (e.g. PagedImage). //
        Line 2 creates an ArrayLattice with the same shape as the expression // (which is the shape of lattice a (and b)). //
        Line 3 copies the result of the expression to the ArrayLattice. //
        // // It is necessary to be able to give an image expression command in ASCII. // This can be used in glish to operate on lattices/images. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class ImageExprParse { public: // Parse the given command. // It will open all lattices needed. // It returns the resulting image expression. //
        The tempLattices/tempRegions arguments make it possible // to use temporary lattices/images and regions in the expression by means // of the $n notation. //
        If a directory name is given, it is used instead of the working // directory for relative file names. // static LatticeExprNode command (const String& str, const String& dirName = String()); static LatticeExprNode command (const String& str, const Block& tempLattices, const PtrBlock& tempRegions, const String& dirName = String()); // // Construct a literal object for the given type. // ImageExprParse (Bool value); ImageExprParse (Int value); ImageExprParse (Float value); ImageExprParse (Double value); ImageExprParse (const Complex& value); ImageExprParse (const DComplex& value); ImageExprParse (const Char* value); ImageExprParse (const String& value); // // Make a LatticeExprNode for a function. // LatticeExprNode makeFuncNode () const; LatticeExprNode makeFuncNode (const LatticeExprNode& arg1) const; LatticeExprNode makeFuncNode (const LatticeExprNode& arg1, const LatticeExprNode& arg2) const; LatticeExprNode makeFuncNode (const LatticeExprNode& arg1, const LatticeExprNode& arg2, const LatticeExprNode& arg3) const; // // Make a LatticeExprNode object for the lattice or region name. LatticeExprNode makeLRNode() const; // Make a LatticeExprNode object for the name of constant, lattice, // or region. LatticeExprNode makeLitLRNode() const; // Make a LatticeExprNode object for the temporary region number. LatticeExprNode makeRegionNode() const; // Make a LatticeExprNode object for the literal value. LatticeExprNode makeLiteralNode() const; // Make a Slice object from 1-3 literals. // static Slice* makeSlice (const ImageExprParse& start); static Slice* makeSlice (const ImageExprParse& start, const ImageExprParse& end); static Slice* makeSlice (const ImageExprParse& start, const ImageExprParse& end, const ImageExprParse& incr); // // Make a node for the INDEXIN function. static LatticeExprNode makeIndexinNode (const LatticeExprNode& axis, const vector& slices); // Make an array from a value list. static LatticeExprNode makeValueList (const Block& values); // Make an IPosition containing the binning values. static IPosition makeBinning (const LatticeExprNode& values); // Get the names of the images used in the expression. static const vector& getImageNames() { return theirNames; } // Set the static node object (used by the .y file). static void setNode (const LatticeExprNode& node) { theirNode = node; } // Keep track of the nodes allocated while parsing the expression. // static void addNode (LatticeExprNode* node); static void addNode (ImageExprParse* node); static void deleteNodes(); // // A function to test addDir. It first sets the directory. static String setAddDir (const String& dirName, const String& fileName); private: // If a directory was given, prepend it to the file name if relative. static String addDir (const String& fileName); // Try if the name represent a lattice or image. // Return False if not. Bool tryLatticeNode (LatticeExprNode& node, const String& name) const; // Make the node from the image name and a mask name. // The mask name can be NOMASK (case insensitive) meaning that no mask // is applied to the image. LatticeExprNode makeImageNode (const String& name, const String& mask) const; // Callback function for RegionHandlerTable to get the table to be used. static Table& getRegionTable (void*, Bool); // Callback function for RegionHandlerHDF5 to get the file to be used. static const std::shared_ptr& getRegionHDF5 (void*); //# A 'global' node object to hold the resulting expression. static LatticeExprNode theirNode; //# The names of the images used in the expression. //# and the level of nesting. static vector theirNames; static Int theirLevel; DataType itsType; Bool itsBval; //# boolean literal Int itsIval; //# integer literal Float itsFval; //# Float literal Double itsDval; //# Double literal Complex itsCval; //# Complex literal DComplex itsDCval; //# DComplex literal String itsSval; //# lattice name; function name }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Images/ImageFITS2Converter.cc000066400000000000000000001665411476623553700220040ustar00rootroot00000000000000//# ImageFITS2Converter.cc : non-templated FITS<->Casacore image conversion //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN const String ImageFITSConverter::CASAMBM = "casambm"; Bool ImageFITSConverter::FITSToImage (ImageInterface *&newImage, String &error, const String &imageName, const String &fitsName, uInt whichRep, Int whichHDU, uInt memoryInMB, Bool allowOverwrite, Bool zeroBlanks) { LogIO os(LogOrigin("ImageFITSConverter")); newImage = 0; error = ""; // First make sure that imageName is writable and does not already // exist. Optionally remove it if it does. If imageName is empty, // great. That means we are going to make a TempImage if (!imageName.empty()) { File imfile(imageName); if (!ImageFITSConverter::removeFile (error, imfile, imageName, allowOverwrite)) return False; Directory imdir = imfile.path().dirName(); if (!imdir.exists() || !imdir.isWritable()) { error = String("Directory ") + imdir.path().originalName() + " does not exist or is not writable"; return False; } } File fitsfile(fitsName); if ( ! fitsfile.exists() || !fitsfile.isReadable() || ! fitsfile.isRegular() ) { error = fitsName + " does not exist or is not readable"; return False; } // OK, now see if we can attach the FITS reading classes FitsInput infile(fitsfile.path().expandedName().chars(), FITS::Disk); if (infile.err()) { error = String("Cannot open file (or other I/O error): ") + fitsName; return False; } // // Advance to the right HDU // Int theHDU = whichHDU; Int numHDU = infile.getnumhdu(); if(whichHDU<0){ // look for first readable HDU for(Int i=0; i fitsdata(infile); ImageFITSConverterImpl >::FITSToImage (newImage, error, imageName, whichRep, fitsdata, fitsName, TpChar, memoryInMB, zeroBlanks); } else { ImageExtension fitsdata(infile); ImageFITSConverterImpl >::FITSToImage (newImage, error, imageName, whichRep, fitsdata, fitsName, TpChar, memoryInMB, zeroBlanks); } success = True; break; case FITS::SHORT: if (infile.hdutype() == FITS::PrimaryArrayHDU) { PrimaryArray fitsdata(infile); ImageFITSConverterImpl >::FITSToImage (newImage, error, imageName, whichRep, fitsdata, fitsName, TpShort, memoryInMB, zeroBlanks); } else { ImageExtension fitsdata(infile); ImageFITSConverterImpl >::FITSToImage (newImage, error, imageName, whichRep, fitsdata, fitsName, TpShort, memoryInMB, zeroBlanks); } success = True; break; case FITS::LONG: if (infile.hdutype() == FITS::PrimaryArrayHDU) { PrimaryArray fitsdata(infile); ImageFITSConverterImpl >::FITSToImage (newImage, error, imageName, whichRep, fitsdata, fitsName, TpInt, memoryInMB, zeroBlanks); } else { ImageExtension fitsdata(infile); ImageFITSConverterImpl >::FITSToImage (newImage, error, imageName, whichRep, fitsdata, fitsName, TpInt, memoryInMB, zeroBlanks); } success = True; break; case FITS::FLOAT: if (infile.hdutype() == FITS::PrimaryArrayHDU) { PrimaryArray fitsdata(infile); ImageFITSConverterImpl >::FITSToImage (newImage, error, imageName, whichRep, fitsdata, fitsName, TpFloat, memoryInMB, zeroBlanks); } else { ImageExtension fitsdata(infile); ImageFITSConverterImpl >::FITSToImage (newImage, error, imageName, whichRep, fitsdata, fitsName, TpFloat, memoryInMB, zeroBlanks); } success = True; break; case FITS::DOUBLE: if (infile.hdutype() == FITS::PrimaryArrayHDU) { PrimaryArray fitsdata(infile); ImageFITSConverterImpl >::FITSToImage (newImage, error, imageName, whichRep, fitsdata, fitsName, TpDouble, memoryInMB, zeroBlanks); } else { ImageExtension fitsdata(infile); ImageFITSConverterImpl >::FITSToImage (newImage, error, imageName, whichRep, fitsdata, fitsName, TpDouble, memoryInMB, zeroBlanks); } success = True; break; default: if(whichHDU>=0){ error = "Unknown datatype - no data returned"; return False; } success = False; } } catch(const std::exception& x){ if(whichHDU>=0){ throw(x); } else{ success = False; } } if(whichHDU>=0 || success){ break; } else if(!success){ // skip to next useful HDU while(theHDU& image, const String &fitsName, uInt memoryInMB, Bool preferVelocity, Bool opticalVelocity, Int BITPIX, Float minPix, Float maxPix, Bool allowOverwrite, Bool degenerateLast, Bool verbose, Bool stokesLast, Bool preferWavelength, Bool airWavelength, const String& origin, Bool history) { LogIO os; os << LogOrigin("ImageFitsConverter", __FUNCTION__, WHERE); // error = ""; FitsOutput *outfile=0; // create the FITS output if (!ImageFITSConverter::openFitsOutput(error, outfile, fitsName, allowOverwrite)){ return False; } // get the coo-sys and check for a quality axis CoordinateSystem cSys= image.coordinates(); if (cSys.hasQualityAxis()){ // put the image to the FITSOut if (!ImageFITSConverter::QualImgToFITSOut (error, os, image, outfile, memoryInMB, preferVelocity, opticalVelocity, BITPIX, minPix, maxPix, degenerateLast, verbose, stokesLast, preferWavelength, airWavelength, origin, history)) { return False; } } else{ // put the image to the FITSOut if (!ImageFITSConverter::ImageToFITSOut (error, os, image, outfile, memoryInMB, preferVelocity, opticalVelocity, BITPIX, minPix, maxPix, degenerateLast, verbose, stokesLast, preferWavelength, airWavelength, True, True, origin, history)) { return False; } } if (outfile) { delete outfile; } return True; } IPosition ImageFITSConverter::copyCursorShape(String &report, const IPosition &shape, uInt imagePixelSize, uInt fitsPixelSize, uInt memoryInMB) { // We could make this more sophisticated by querying the actual tile // shape. However, the image will basically always need all but the // last dimension in memory for efficient traversal. // This function should err on the side of making a too-small cursor. const uInt ndim = shape.nelements(); // *2 because the pixels might exist in a buffer as well. We should // be able to do away with that. uInt maxPixels = memoryInMB * 1024 * 1024 / (imagePixelSize*2 + fitsPixelSize*2); maxPixels /= 2; // because 1/2 the pixels are in FITS, 1/2 in Image Int axis = ndim - 1; if (shape.product() > Int(maxPixels)) { while (--axis >= 0 && shape(axis) == 1) { ; // Nothing } } if (axis < 0) { axis = 0; // If we have a 1D image } uInt prod = 1; uInt i; for (i=0; Int(i)<=axis; i++) { prod *= shape(i); } // Correct for the probable tile shape for (i=axis+1; i 1) { if (shape(i) < 32) { prod *= shape(i); } else { prod *= 32; } } } // If the image slice is still too large drop back another axis. // The image will still be taking too much space, but the FITS buffering // won't contribute to the delinquency. if (prod > maxPixels) { while (--axis >= 0 && shape(axis) == 1) { ; // Nothing } } if (axis < 0) { axis = 0; // If we have a 1D image } IPosition cursorShape(ndim); cursorShape = 1; for (i=0; Int(i)<=axis; i++) { cursorShape(i) = shape(i); } ostringstream buffer; if (axis == Int(ndim) - 1) { buffer << "All pixels fit in memory"; } else { switch(axis) { case 0: buffer << "Copying row by row"; break; case 1: buffer << "Copying plane by plane"; break; case 2: buffer << "Copying cube by cube"; break; default: buffer << "Copying hypercube by hypercube"; } } buffer << " (" << cursorShape.product() << " pixels)."; report = String(buffer); return cursorShape; } CoordinateSystem ImageFITSConverter::getCoordinateSystem (Int& stokesFITSValue, RecordInterface& headerRec, const Vector& header, LogIO& os, uInt whichRep, IPosition& shape, Bool dropStokes) { // Get CS and return un-used cards in a Record for further use CoordinateSystem cSys; if (!CoordinateSystem::fromFITSHeader(stokesFITSValue, cSys, headerRec, header, shape, whichRep)) { os << LogIO::WARN << "No proper coordinate system defined in FITS file. Using dummy linear system instead." << LogIO::POST; CoordinateSystem cSys2; Vector names(shape.nelements()); for (uInt i=0; i shape.nelements()) { Int nDeg = cSys.nPixelAxes() - shape.nelements(); shape2.resize(cSys.nPixelAxes()); shape2 = 1; for (uInt i=0; i= 0 && stokesFITSValue >= 0) { uInt nS = cSys.stokesCoordinate(c).stokes().nelements(); if (nS==1) { CoordinateSystem cSys2; for (uInt i=0; i ignore(6); ignore(0) = "^date-map$"; ignore(1) = "date"; ignore(2) = "^naxis"; ignore(3) = "^naxis$"; ignore(4) = "^pc....."; ignore(5) = "^pc......"; FITSKeywordUtil::removeKeywords(headerRec, ignore); // return cSys; } ImageInfo ImageFITSConverter::getImageInfo ( RecordInterface& header ) { ImageInfo ii; Vector errors; Bool ok = ii.fromFITS (errors, header); if (!ok) { LogIO log(LogOrigin("ImageFITSConverter::getImageInfo", "ImageToFITS", WHERE)); log << errors << endl; } FITSKeywordUtil::removeKeywords(header, ImageInfo::keywordNamesFITS()); return ii; } void ImageFITSConverter::readBeamsTable( ImageInfo& info, const String& filename, const DataType type ) { FitsInput input(File(filename).path().expandedName().chars(), FITS::Disk); switch (type) { // advance to correct location in the input case TpFloat: { PrimaryArray fitsImage(input); break; } case TpDouble: { PrimaryArray fitsImage(input); break; } case TpInt: { PrimaryArray fitsImage(input); break; } case TpShort: { PrimaryArray fitsImage(input); break; } default: { throw AipsError("Unhandled FITS type"); break; } } Table beamTable; while (input.rectype() != FITS::EndOfFile && !input.err()) { if (input.hdutype() != FITS::BinaryTableHDU) { input.skip_hdu(); } else { BinaryTable binTab(input); String type = binTab.extname(); if (type.contains("BEAMS")) { beamTable = binTab.fullTable(); break; } } } if (beamTable.nrow() == 0) { // no beam table found return; } LogIO os; os << LogOrigin("ImageFITSConverter", __FUNCTION__) << LogIO::NORMAL << "Loading multiple beams from BEAMS table" << LogIO::POST; uInt nChan = beamTable.keywordSet().asuInt("NCHAN"); uInt nPol = beamTable.keywordSet().asuInt("NPOL"); info.setAllBeams(nChan, nPol, GaussianBeam::NULL_BEAM); ScalarColumn bmaj(beamTable, "BMAJ"); ScalarColumn bmin(beamTable, "BMIN"); ScalarColumn bpa(beamTable, "BPA"); ScalarColumn chan(beamTable, "CHAN"); ScalarColumn pol(beamTable, "POL"); String bmajUnit = bmaj.keywordSet().asString("TUNIT"); String bminUnit = bmin.keywordSet().asString("TUNIT"); String bpaUnit = bpa.keywordSet().asString("TUNIT"); GaussianBeam beam; Quantity xmaj(0, bmajUnit); Quantity xmin(0, bminUnit); Quantity xpa(0, bpaUnit); for (uInt i=0; i lines; String groupType; kw.first(); uInt n; while ((n=FITSHistoryUtil::getHistoryGroup(lines, groupType, kw))!=0) { if (groupType == "LOGTABLE") { FITSHistoryUtil::fromHISTORY(logger, lines, n, True); } else if (groupType == "") { FITSHistoryUtil::fromHISTORY(logger, lines, n, False); } } } Bool ImageFITSConverter::removeFile (String& error, const File& outFile, const String& outName, Bool allowOverwrite) { String basename = outFile.path().baseName(); if (basename.empty() || basename == "." || basename == "..") { throw AipsError( "Invalid file path " + outFile.path().absoluteName() + ". You really don't want me to delete the directory you're in." ); } if (outFile.exists()) { if (allowOverwrite) { String msg; try { if (outFile.isSymLink()) { SymLink sfile(outFile); sfile.remove(); } else if (outFile.isRegular()) { RegularFile rfile(outFile); rfile.remove(); } else if (outFile.isDirectory()) { Directory dfile(outFile); dfile.removeRecursive(); } else { msg = "Cannot remove file - unknown file type"; } } catch (std::exception& x) { msg = x.what(); } // if (outFile.exists()) { error = "Could not remove file " + outName; if (msg != "") { error += ": (" + msg + ")"; } return False; } } else { error = outName + " already exists, will not overwrite."; return False; } } return True; } Bool ImageFITSConverter::ImageHeaderToFITS (String &error, ImageFITSHeaderInfo& fhi, const ImageInterface& image, Bool preferVelocity, Bool opticalVelocity, Int BITPIX, Float minPix, Float maxPix, Bool degenerateLast, Bool verbose, Bool stokesLast, Bool preferWavelength, Bool airWavelength, Bool primHead, Bool allowAppend, const String& origin, Bool history) { LogIO os; os << LogOrigin("ImageFitsConverter", __FUNCTION__, WHERE); error = ""; // // Get coordinates and test that axis removal has been // mercifully absent // CoordinateSystem cSys = image.coordinates(); if (cSys.nWorldAxes() != cSys.nPixelAxes()) { error = "FITS requires that the number of world and pixel axes be" " identical."; return False; } // // Make degenerate axes last if requested // and make Stokes the very last if requested // IPosition shape = image.shape(); fhi.newShape = shape; const uInt ndim = shape.nelements(); fhi.cursorOrder.resize(ndim); // to be used later in the actual data copying for (uInt i=0; i order(ndim); Vector cNames = cSys.worldAxisNames(); uInt nStokes = 0; // number of stokes axes if(stokesLast){ for (uInt i=0; i0){ // apply the stokes reordering cSys.transpose(order,order); } if (degenerateLast) { // make sure the stokes axes stay where they are now for (uInt i=ndim-nStokes; i1) { // axis is not degenerate order(j) = i; // put it in front, keeping order fhi.newShape(j) = shape(i); j++; } } for (uInt i=0; i>(IPosition(0,0)); } // // Find scale factors // Record header; fhi.maxshort = 32767; fhi.minshort = -32768; fhi.hasBlanks = True; if (BITPIX == -32) { fhi.bscale = 1.0; fhi.bzero = 0.0; header.define("bitpix", BITPIX); header.setComment("bitpix", "Floating point (32 bit)"); // // We don't yet know if the image has blanks or not, so assume it does. // fhi.hasBlanks = True; } else if (BITPIX == 16) { header.define("bitpix", BITPIX); header.setComment("bitpix", "Short integer (16 bit)"); if (minPix > maxPix) { // Find the min and max of the image if (verbose) { os << LogIO::NORMAL << "Finding scaling factors for BITPIX=16 and look for masked or blanked values" << LogIO::POST; } fhi.hasBlanks = False; // // Set up iterator // IPosition cursorShape(image.niceCursorShape()); RO_MaskedLatticeIterator iter (image, LatticeStepper(shape, cursorShape, LatticeStepper::RESIZE)); ProgressMeter meter(0.0, 1.0*shape.product(), "Searching pixels", "", "", "", True, shape.product()/cursorShape.product()/50); // // Iterate // uInt count = 0; Bool deleteMaskPtr, deletePtr; for (iter.reset(); !iter.atEnd(); iter++) { const Array &cursor = iter.cursor(); const Float *cptr = cursor.getStorage(deletePtr); const uInt n = cursor.nelements(); // if (fhi.applyMask) { if (!fhi.pMask->shape().isEqual(cursor.shape())) { fhi.pMask->resize(cursor.shape()); } (*fhi.pMask) = iter.getMask(False); const Bool* maskPtr = fhi.pMask->getStorage(deleteMaskPtr); // // If a pixel is a NaN or the mask is False, it goes out as a NaN // for (uInt i=0; i maxPix) { minPix = maxPix = cptr[i]; } else { if (cptr[i] < minPix) minPix = cptr[i]; if (cptr[i] > maxPix) maxPix = cptr[i]; } } } fhi.pMask->freeStorage(maskPtr, deleteMaskPtr); } else { for (uInt i=0; i maxPix) { // First non-NaN we have run into. Init. minPix = maxPix = cptr[i]; } else { if (cptr[i] < minPix) minPix = cptr[i]; if (cptr[i] > maxPix) maxPix = cptr[i]; } } } } count += n; meter.update(count*1.0); cursor.freeStorage(cptr, deletePtr); } } // Make sure bscale does not come out to be zero if (::casacore::near(minPix, maxPix)) { if (::casacore::near(Float(0.0), maxPix)) { maxPix = 1.0; } else { maxPix = maxPix + 0.01*maxPix; } } // if (fhi.hasBlanks) { fhi.bscale = Double(maxPix - minPix)/Double(Int(fhi.maxshort) - Int(fhi.minshort+1)); fhi.bzero = Double(minPix) + fhi.bscale * (-Double(fhi.minshort+1)); } else { fhi.bscale = Double(maxPix - minPix)/Double(Int(fhi.maxshort) - Int(fhi.minshort)); fhi.bzero = Double(minPix) + fhi.bscale * (-Double(fhi.minshort)); } } else { error = "BITPIX must be -32 (floating point) or 16 (short integer)"; return False; } // At this point, for 32 floating point, we must apply the given // mask. For 16bit, we may know that there are in fact no blanks // in the image, so we can dispense with looking at the mask again. if (fhi.applyMask && !fhi.hasBlanks) fhi.applyMask = False; // Vector naxis(ndim); uInt i; for (i=0; i < ndim; i++) { naxis(i) = fhi.newShape(i); } header.define("naxis", naxis); if (allowAppend) header.define("extend", True); if (!primHead){ header.define("PCOUNT", 0); header.define("GCOUNT", 1); } header.define("bscale", fhi.bscale); header.setComment("bscale", "PHYSICAL = PIXEL*BSCALE + BZERO"); header.define("bzero", fhi.bzero); if (BITPIX>0 && fhi.hasBlanks) { header.define("blank", fhi.minshort); header.setComment("blank", "Pixels with this value are blank"); } if (BITPIX>0) { header.define("datamin", minPix); header.define("datamax", maxPix); fhi.minPix = minPix; fhi.maxPix = maxPix; } else { fhi.minPix = 1.0; fhi.maxPix = -1.0; } // const ImageInfo& ii = image.imageInfo(); if (!ii.toFITS (error, header)) { return False; } //header.define("COMMENT1", ""); // inserts spaces header.define("BUNIT", image.units().getName().chars()); header.setComment("BUNIT", "Brightness (pixel) unit"); // IPosition shapeCopy = fhi.newShape; Record saveHeader(header); Bool ok = cSys.toFITSHeader(header, shapeCopy, True, 'c', True, // use WCS preferVelocity, opticalVelocity, preferWavelength, airWavelength); if (!ok) { os << LogIO::SEVERE << "Could not make a standard FITS header. Setting" " a simple linear coordinate system." << LogIO::POST; // uInt n = cSys.nWorldAxes(); Matrix pc(n,n); pc=0.0; pc.diagonal() = 1.0; LinearCoordinate linear(cSys.worldAxisNames(), cSys.worldAxisUnits(), cSys.referenceValue(), cSys.increment(), cSys.linearTransform(), cSys.referencePixel()); CoordinateSystem linCS; linCS.addCoordinate(linear); // Recover old header before it got mangled by toFITSHeader header = saveHeader; IPosition shapeCopy = fhi.newShape; Bool ok = linCS.toFITSHeader(header, shapeCopy, True, 'c', False); // don't use WCS if (!ok) { error = "Fallback linear coordinate system fails also."; return False; } } // When this if test is True, it means some pixel axes had been removed from // the coordinate system and degenerate axes were added. if (naxis.nelements() != shapeCopy.nelements()) { naxis.resize(shapeCopy.nelements()); for (uInt j=0; j < shapeCopy.nelements(); j++) { naxis(j) = shapeCopy(j); } header.define("NAXIS", naxis); } // // Add in the fields from miscInfo that we can // const auto miscInfo = image.miscInfo(); const uInt nmisc = miscInfo.nfields(); for (i=0; i 8) { os << LogIO::NORMAL << "Truncating miscinfo field " << tmp0 << " to " << miscname << LogIO::POST; } // if (miscname != "end" && miscname != "END") { if (header.isDefined(miscname)) { // These warnings just cause confusion. They are usually // from the alt* keywords which FITSSpectralUtil writes. // They may also have been preserved in miscInfo when an // image came from FITS and hence the conflict. /* os << LogIO::WARN << "FITS keyword " << miscname << " is already defined so dropping it" << LogIO::POST; */ } else { DataType misctype = miscInfo.dataType(i); switch(misctype) { case TpBool: header.define(miscname, miscInfo.asBool(i)); break; case TpChar: case TpUChar: case TpShort: case TpUShort: case TpInt: case TpUInt: header.define(miscname, miscInfo.asInt(i)); break; case TpInt64: header.define(miscname, miscInfo.asInt(i)); break; case TpFloat: header.define(miscname, miscInfo.asfloat(i)); break; case TpDouble: header.define(miscname, miscInfo.asdouble(i)); break; case TpComplex: header.define(miscname, miscInfo.asComplex(i)); break; case TpDComplex: header.define(miscname, miscInfo.asDComplex(i)); break; case TpString: if (miscname.contains("date") && miscname != "date") { // Try to canonicalize dates (i.e. solve Y2K) String outdate; // We only need to convert the date, the timesys we'll just // copy through if ( FITSDateUtil::convertDateString(outdate, miscInfo.asString(i)) ) { // Conversion worked - change the header header.define(miscname, outdate); } else { // conversion failed - just copy the existing date header.define(miscname, miscInfo.asString(i)); } } else { // Just copy non-date strings through header.define(miscname, miscInfo.asString(i)); } break; // These should be the cases that we actually see. I don't think // asArray* converts types. case TpArrayBool: header.define(miscname, miscInfo.asArrayBool(i)); break; case TpArrayChar: case TpArrayUShort: case TpArrayInt: case TpArrayUInt: case TpArrayInt64: header.define(miscname, miscInfo.toArrayInt(i)); break; case TpArrayFloat: header.define(miscname, miscInfo.asArrayfloat(i)); break; case TpArrayDouble: header.define(miscname, miscInfo.asArraydouble(i)); break; case TpArrayString: header.define(miscname, miscInfo.asArrayString(i)); break; default: { ostringstream mytype; mytype << misctype; os << LogIO::NORMAL << "Not writing miscInfo field '" << miscname << "' - cannot handle type " << String(mytype) << LogIO::POST; } } } if (header.isDefined(miscname)) { header.setComment(miscname, image.miscInfo().comment(i)); } } } // // DATE // String date, timesys; Time nowtime; MVTime now(nowtime); FITSDateUtil::toFITS(date, timesys, now); header.define("date", date); header.setComment("date", "Date FITS file was written"); if (!header.isDefined("timesys") && !header.isDefined("TIMESYS")) { header.define("timesys", timesys); header.setComment("timesys", "Time system for HDU"); } // // ORIGIN // if (origin.empty()) { header.define("ORIGIN", "casacore-" + string(getVersion())); } else { header.define("ORIGIN", origin); } // Set up the FITS header fhi.kw = FITSKeywordUtil::makeKeywordList(primHead, True); if (ii.hasMultipleBeams()) { header.define(CASAMBM, True); header.setComment(CASAMBM, "CASA multiple BEAMS table present"); } //kw.mk(FITS::EXTEND, True, "Tables may follow"); // add the general keywords for WCS and so on ok = FITSKeywordUtil::addKeywords(fhi.kw, header); if (! ok) { error = "Error creating initial FITS header"; return False; } if(history){ // // HISTORY // const LoggerHolder& logger = image.logger(); // vector historyChunk; uInt nstrings; Bool aipsppFormat; uInt firstLine = 0; while (1) { firstLine = FITSHistoryUtil::toHISTORY(historyChunk, aipsppFormat, nstrings, firstLine, logger); if (nstrings == 0) { break; } String groupType; if (aipsppFormat) groupType = "LOGTABLE"; FITSHistoryUtil::addHistoryGroup(fhi.kw, historyChunk, nstrings, groupType); } } // // END // fhi.kw.end(); return True; } Bool ImageFITSConverter::ImageToFITSOut (String &error, LogIO &os, const ImageInterface& image, FitsOutput *outfile, uInt memoryInMB, Bool preferVelocity, Bool opticalVelocity, Int BITPIX, Float minPix, Float maxPix, Bool degenerateLast, Bool verbose, Bool stokesLast, Bool preferWavelength, Bool airWavelength, Bool primHead, Bool allowAppend, const String& origin, Bool history) { // Write the headers. ImageFITSHeaderInfo fhi; if (! ImageHeaderToFITS(error, fhi, image, preferVelocity, opticalVelocity, BITPIX, minPix, maxPix, degenerateLast, verbose, stokesLast, preferWavelength, airWavelength, primHead, allowAppend, origin, history)) { return False; } // // Finally get around to copying the data // IPosition shape = image.shape(); String report; IPosition newCursorShape = ImageFITSConverter::copyCursorShape (report, shape, sizeof(Float), sizeof(Float), memoryInMB); if(fhi.needNonOptimalCursor && fhi.newShape.nelements()>0){ // use cursor the size of one image row in order to enable axis re-ordering newCursorShape.resize(1); newCursorShape=fhi.newShape(0); } if (verbose) { os << "Copying '" << image.name() << "' to file " << report << LogIO::POST; } // // If this fails, more development is needed // AlwaysAssert(sizeof(Float) == sizeof(float), AipsError); AlwaysAssert(sizeof(Short) == sizeof(short), AipsError); try { Int nIter = max(1,shape.product()/newCursorShape.product()); Int iUpdate = max(1,nIter/20); // ProgressMeter* pMeter = 0; if (verbose) pMeter = new ProgressMeter(0.0, 1.0*shape.product(), "Image to FITS", "Pixels copied", "", "", True, iUpdate); uInt count = 0; Double curpixels = 1.0*newCursorShape.product(); // LatticeStepper stepper(shape, newCursorShape, fhi.cursorOrder); RO_MaskedLatticeIterator iter(image, stepper); const Int bufferSize = newCursorShape.product(); PrimaryArray* fits32 = 0; PrimaryArray* fits16 = 0; if (BITPIX == -32) { if (primHead) { fits32 = new PrimaryArray(fhi.kw); } else { fits32 = new ImageExtension(fhi.kw); } if (fits32==0 || fits32->err()) { error = "Error creating FITS file from keywords"; return False; } if (fits32->write_hdr(*outfile)) { error = "Error writing FITS header"; delete outfile; return False; } } else if (BITPIX == 16) { if (primHead) { fits16 = new PrimaryArray(fhi.kw); } else { fits16 = new ImageExtension(fhi.kw); } if (fits16==0 || fits16->err()) { error = "Error creating FITS file from keywords"; return False; } if (fits16->write_hdr(*outfile)) { delete outfile; error = "Error writing FITS header"; return False; } } else { AlwaysAssert(0, AipsError); // NOTREACHED } Short *buffer16 = 0; // Use this to write the scaled shorts into if (fits16) { buffer16 = new Short[bufferSize]; AlwaysAssert(buffer16, AipsError); } // // Iterate through the image. // for (iter.reset(); !iter.atEnd(); iter++) { const Array& cursor = iter.cursor(); Bool deletePtr; const Float* ptr = cursor.getStorage(deletePtr); // const Bool* maskPtr = 0; Bool deleteMaskPtr; if (fhi.applyMask) { if (!fhi.pMask->shape().isEqual(cursor.shape())) { fhi.pMask->resize(cursor.shape()); } (*fhi.pMask) = iter.getMask(False); maskPtr = fhi.pMask->getStorage(deleteMaskPtr); } // // pMeter->update((count*1.0 - 0.5)*curpixels); // const uInt nPts = cursor.nelements(); error= ""; Int n = 0; if (fits32) { if (fhi.applyMask) { Float* ptr2 = new float[nPts]; for (uInt j=0; jstore(ptr2, bufferSize); delete [] ptr2; } else { fits32->store(ptr, bufferSize); } Int hduErr = 0; if (!(hduErr = fits32->err())){ n = fits32->write(*outfile); if (n != bufferSize) { delete outfile; error = "Write failed (full disk or tape?)"; return False; } } else { error = "ImageFITS2Converter: Storing FITS primary Float array failed with HDU error code " + String::toString(hduErr); return False; } } else if (fits16) { short blankOffset = fhi.hasBlanks ? 1 : 0; // if (fhi.applyMask) { for (Int j=0; j fhi.maxPix) { buffer16[j] = fhi.maxshort; } else if (ptr[j] < fhi.minPix) { buffer16[j] = fhi.minshort + blankOffset; } else { buffer16[j] = Short((ptr[j] - fhi.bzero)/fhi.bscale); } } } } else { for (Int j=0; j fhi.maxPix) { buffer16[j] = fhi.maxshort; } else if (ptr[j] < fhi.minPix) { buffer16[j] = fhi.minshort + blankOffset; } else { buffer16[j] = Short((ptr[j] - fhi.bzero)/fhi.bscale); } } } } fits16->store(buffer16, bufferSize); Int hduErr = 0; if (!(hduErr = fits16->err())) { n = fits16->write(*outfile); if (n != bufferSize) { delete outfile; error = "Write failed (full disk or tape?"; return False; } } else { error = "ImageFITS2Converter: Storing FITS primary Short array failed with HDU error code " + String::toString(hduErr); return False; } } else { AlwaysAssert(0, AipsError); // NOTREACHED } // cursor.freeStorage(ptr, deletePtr); if (fhi.applyMask) fhi.pMask->freeStorage(maskPtr, deleteMaskPtr); // if ((fits32 && fits32->err()) || (fits16 && fits16->err()) || outfile->err()) { error = String("Error writing into file!"); delete outfile; return False; } count++; if (verbose) pMeter->update(count*curpixels); } if (fits32) { delete fits32; fits32 = 0; } else if (fits16) { delete fits16; fits16 = 0; delete buffer16; buffer16 = 0; } else { AlwaysAssert(0, AipsError); // NOTREACHED } // if (pMeter) delete pMeter; } catch (const std::exception& x) { error = "Unknown error copying image to FITS file"; if (outfile) { delete outfile; } return False; } const ImageInfo& ii = image.imageInfo(); if (ii.hasMultipleBeams()) { _writeBeamsTable(outfile, ii); } return True; } void ImageFITSConverter::_writeBeamsTable( FitsOutput *const &outfile, const ImageInfo& info ) { // write multiple beams to a table RecordDesc desc; Record stringLengths; // no strings Record units; GaussianBeam beam = *info.getBeamSet().getBeams().begin(); desc.addField("BMAJ", TpFloat); units.define("BMAJ", beam.getMajor().getUnit()); desc.addField("BMIN", TpFloat); units.define("BMIN", beam.getMinor().getUnit()); desc.addField("BPA", TpFloat); units.define("BPA", beam.getPA(True).getUnit()); desc.addField("CHAN", TpInt); desc.addField("POL", TpInt); Record extraKeywords; extraKeywords.define("EXTNAME", "BEAMS"); extraKeywords.define("EXTVER", 1); extraKeywords.define("XTENSION", "BINTABLE"); extraKeywords.setComment("XTENSION", "Binary extension"); extraKeywords.define("NCHAN", (Int)info.getBeamSet().nchan()); extraKeywords.define("NPOL", (Int)info.getBeamSet().nstokes()); FITSTableWriter writer( outfile, desc, stringLengths, info.getBeamSet().nelements(), extraKeywords, units, False ); RecordFieldPtr bmaj(writer.row(), "BMAJ"); RecordFieldPtr bmin(writer.row(), "BMIN"); RecordFieldPtr bpa(writer.row(), "BPA"); RecordFieldPtr chan(writer.row(), "CHAN"); RecordFieldPtr pol(writer.row(), "POL"); const ImageBeamSet& beamSet = info.getBeamSet(); IPosition axisPath(2, 0, 1); ArrayPositionIterator iter(beamSet.shape(), axisPath, False); while (! iter.pastEnd()) { const IPosition& pos = iter.pos(); GaussianBeam beam = beamSet(pos[0], pos[1]); *chan = pos[0]; *pol = pos[1]; *bmaj = beam.getMajor().getValue(); *bmin = beam.getMinor().getValue(); *bpa = beam.getPA("deg", True); writer.write(); iter.next(); } } Bool ImageFITSConverter::QualImgToFITSOut (String &error, LogIO &os, ImageInterface &image, FitsOutput *outfile, uInt memoryInMB, Bool preferVelocity, Bool opticalVelocity, Int BITPIX, Float minPix, Float maxPix, Bool degenerateLast, Bool verbose, Bool stokesLast, Bool preferWavelength, Bool airWavelength, const String& origin, Bool history) { // check whether the image is a generic FITS image FITSQualityImage *fitsQI=dynamic_cast(&image); if (fitsQI){ // Background: When writing to FITS and image that was generated // from a FITS image, it makes more sense to load in // and write out the data and error extension directly. // This avoids that meta-data get screwed when read into // a CASA image. Doing so, there is e.g. no need to take care // for the header keywords declaring the data and error // extension and so on, since they exist properly in the // respective FITS extensions. // load the data extension FITSImage *fitsImg = new FITSImage(fitsQI->name(False), 0, fitsQI->whichDataHDU()); // put the data extension to FITSOut if (!ImageFITSConverter::ImageToFITSOut (error, os, *fitsImg, outfile, memoryInMB, preferVelocity, opticalVelocity, BITPIX, minPix, maxPix, degenerateLast, verbose, stokesLast, preferWavelength, airWavelength, True, True, origin, history)) { if (fitsImg) delete fitsImg; return False; } delete fitsImg; fitsImg=0; // load the error extension fitsImg = new FITSImage(fitsQI->name(False), 0, fitsQI->whichErrorHDU()); // put the error extension to the FITSOut if (!ImageFITSConverter::ImageToFITSOut (error, os, *fitsImg, outfile, memoryInMB, preferVelocity, opticalVelocity, BITPIX, minPix, maxPix, degenerateLast, verbose, stokesLast, preferWavelength, airWavelength, False, False, origin, history)) { if (fitsImg) { delete fitsImg; } return False; } delete fitsImg; } else { TableRecord dataExtMiscInfo; TableRecord errorExtMiscInfo; // get the metadata (extension names etc.) for the data and the error if (!FITSQualityImage::qualFITSInfo(error, dataExtMiscInfo, errorExtMiscInfo, image.miscInfo())){ return False; } // find the quality axis CoordinateSystem cSys = image.coordinates(); Int qualAx = cSys.findCoordinate(Coordinate::QUALITY); Vector nPixelQual = cSys.pixelAxes(qualAx); uInt nAxisQual=nPixelQual(0); // build a slicer for the data Int qualIndex; if (!(cSys.qualityCoordinate(qualAx)).toPixel(qualIndex, Quality::DATA)){ error = "Could not locate DATA index in quality coordinate!"; return False; } IPosition startPos(image.ndim(), 0); IPosition lengthPos=image.shape(); startPos(nAxisQual) = qualIndex; lengthPos(nAxisQual) = 1; Slicer subSlicer(startPos, lengthPos, Slicer::endIsLength); // create the data sub-image and set the metadata SubImage *subData = new SubImage(image, subSlicer, AxesSpecifier(False)); subData->setMiscInfo(dataExtMiscInfo); // put the data sub-image to FITSOut if (!ImageFITSConverter::ImageToFITSOut (error, os, *subData, outfile, memoryInMB, preferVelocity, opticalVelocity, BITPIX, minPix, maxPix, degenerateLast, verbose, stokesLast, preferWavelength, airWavelength, True, True, origin, history)) { if (subData) { delete subData; } return False; } delete subData; // build the error slicer if (!(cSys.qualityCoordinate(qualAx)).toPixel(qualIndex, Quality::ERROR)){ error = "Could not locate ERROR index in quality coordinate!"; return False; } startPos(nAxisQual)=qualIndex; subSlicer=Slicer(startPos, lengthPos, Slicer::endIsLength); // create the error sub-image and set the metadata SubImage *subError = new SubImage(image, subSlicer, AxesSpecifier(False)); subError->setMiscInfo(errorExtMiscInfo); // put the error sub-image to FITSOut if (!ImageFITSConverter::ImageToFITSOut (error, os, *subError, outfile, memoryInMB, preferVelocity, opticalVelocity, BITPIX, minPix, maxPix, degenerateLast, verbose, stokesLast, preferWavelength, airWavelength, False, False, origin, history)) { if (subError) { delete subError; } return False; } delete subError; } return True; } Bool ImageFITSConverter::openFitsOutput(String &error, FitsOutput *(&fitsOut), const String &fitsName, const Bool &allowOverwrite) { if (fitsName == "-") { // Write to stdout fitsOut = new FitsOutput(); } else { // Make sure that the fits file does not already exist, and that we // can write to the directory File fitsfile(fitsName); if (!ImageFITSConverter::removeFile (error, fitsfile, fitsName, allowOverwrite)) { return False; } // Directory fitsdir = fitsfile.path().dirName(); if (!fitsdir.exists() || !fitsdir.isWritable()) { error = String("Directory ") + fitsdir.path().originalName() + " does not exist or is not writable"; return False; } // // OK, it appears to be a writable etc. file, let's try opening it. // fitsOut = new FitsOutput(fitsfile.path().expandedName().chars(), FITS::Disk); } // if (fitsOut == 0 || fitsOut->err()) { error = String("Cannot open file for writing: ") + fitsName; if (fitsOut != 0) { delete fitsOut; fitsOut = 0; } return False; } return True; } } //# NAMESPACE CASACORE - END casacore-3.7.1/images/Images/ImageFITSConverter.h000066400000000000000000000423351476623553700215560ustar00rootroot00000000000000//# ImageFITSConverter.h: Interconvert between Casacore Images and FITS files //# Copyright (C) 1996,1999,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_IMAGEFITSCONVERTER_H #define IMAGES_IMAGEFITSCONVERTER_H #include #include #include #include #include #include #include #ifndef WCSLIB_GETWCSTAB #define WCSLIB_GETWCSTAB #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN template class PagedImage; template class ImageInterface; class FitsOutput; class File; class ImageInfo; class CoordinateSystem; class RecordInterface; class TableRecord; class LogIO; class Unit; class LoggerHolder; class ConstFitsKeywordList; class FitsInput; // // Struct holding information derived from the image and its header // // // This is a helper struct to pass information from ImageHeaderToFITS // to ImageToFITSOut. // struct ImageFITSHeaderInfo { Bool applyMask; Bool needNonOptimalCursor; Bool hasBlanks; Double bzero; Double bscale; Short minshort; Short maxshort; double minPix; double maxPix; IPosition newShape; IPosition cursorOrder; FitsKeywordList kw; std::shared_ptr> pMask; }; // // Interconvert between Casacore Images and FITS files. // // // // // //
      • PagedImage //
      • PrimaryArray (and FITS concepts in // general). // // // // This class is a helper class that is used to interconvert between Casacore // images and FITS files. This adds no functionality over the general abilities // available in the underlying FITS classes, however it is a useful higher-level // packaging. // // There are two fundamental member functions in this class. // FITSToImage which turns a FITS file into a Casacore image, and // ImageToFITS which does the opposite. // // We can read images from any HDU inside the FITS file (although this isn't // well tested). Images with a quality axis (i.e. contain data and error values) // are stored in the primary HDU (data) and an extension HDU (error). Other // images are always written to the primary HDU. // // Pixels in the FITS file which are blanked are masked out (the mask // is set to False) in the output image. On conversion to FITS, // masked values are blanked. The mask which is read is the current // default mask. // // // // A FITS to image conversion may be accomplished as follows: // // PagedImage *image = 0; // String fitsName = "exists.fits"; // String imageName = "new.image"; // String error; // Bool ok = ImageFITSConverter::FITSToImage(image, error, imageName, fitsName); // if (!image) ... error ... // // A couple of things to note: //
          //
        • If ok is False, the conversion failed and error // will be set. //
        • The pointer "image" is set if the conversion succeeds. If it is // zero the conversion failed and error will contain an // error message. //
        • The caller is responsible for deleting the pointer image // when the conversion is successful. //
        // Similarly, an image to FITS conversion may be accomplished as follows: // // String imageName = argv[1]; // PagedImage image = ...; // An existing image from somewhere // String fitsName = "new.fits"; // String error; // Bool ok = ImageFITSConverter::ImageToFITS(error, image, fitsName); // // A couple of similar remarks can be made about this example: //
          //
        • If ok is False, the conversion failed and error // will be set. //
        //
        // // // FITS files are the fundamental transport format for images in Astronomy. // // // //
      • It might be useful to have functions that convert between FITS // and general lattices. //
      • Add support for PagedImage //
      • Convert multiple images at once? //
      • Allow writing FITS files to an image extension in an existing // FITS file. // class ImageFITSConverter { public: const static String CASAMBM; // Convert a FITS file to a Casacore image. //
          //
        • newImage will be zero if the conversion fail. If the // conversion succeeds, the caller is responsible for deleting this // pointer. //
        • error will be set if the conversion fails. //
        • If imageName is empty, a TempImage will be created, // otherwise a PagedImage on disk. //
        • fitsName must already exist (and have an image at the // indicated HDU). //
        • whichRep Zero-relative coordinate representation // (Starting with wcs FITS multiple coordinate representations // can be stored in a FITS file) //
        • whichHDU Zero-relative hdu. The default is correct for // a primary array, set it for an image extension. A value of -1 // makes the code look for the first readable HDU. //
        • memoryInMB. Setting this to zero will result in // row-by-row copying, otherwise it will attempt to with as large // a chunk-size as possible, while fitting in the desired memory. //
        • allowOverwrite If True, allow imageName to be // overwritten if it already exists. //
        • zeroBlanks If True, allow any blanked pixels are set // to zero rather than NaN //
        static Bool FITSToImage(ImageInterface*& newImage, String &error, const String &imageName, const String &fitsName, uInt whichRep = 0, Int whichHDU = 0, uInt memoryInMB = 64, Bool allowOverwrite=False, Bool zeroBlanks=False); // Convert a Casacore image to a FITS file. //
          //
        • return True if the conversion succeeds, False // otherwise. //
        • error will be set if the conversion fails. //
        • image The image to convert. //
        • fitsName If the name is "-" (the minus character), // then write to stdout Always writes to the primary array. //
        • memoryInMB. Setting this to zero will result in // row-by-row copying, otherwise it will attempt to with as large // a chunk-size as possible, while fitting in the desired memory. //
        • preferVelocityWrite a velocity primary spectral axis // if possible. //
        • opticalVelocityIf writing a velocity, use the optical // definition (otherwise use radio). //
        • BITPIX, minPix, maxPix // BITPIX can presently be set to -32 or 16 only. When BITPIX is // 16 it will write BSCALE and BZERO into the FITS file. If minPix // is greater than maxPix the minimum and maximum pixel values // will be determined from the array, otherwise the supplied // values will be used and pixels outside that range will be // truncated to the minimum and maximum pixel values (note that // this truncation does not occur for BITPIX=-32). //
        • allowOverwrite If True, allow fitsName to be // overwritten if it already exists. //
        • degenerateLast If True, axes of length 1 will be written // last to the header. //
        • preferWavelength If True, write a wavelength primary axis. //
        • airWavelength If True and preferWavelength is True write // an air wavelength primary axis. //
        • origin gives the origin, i.e., the name of the package. // If empty, it defaults to "casacore-"getVersion(). //
        // static Bool ImageToFITS(String &error, ImageInterface &image, const String &fitsName, uInt memoryInMB = 64, Bool preferVelocity = True, Bool opticalVelocity = True, Int BITPIX=-32, Float minPix = 1.0, Float maxPix = -1.0, Bool allowOverwrite=False, Bool degenerateLast=False, Bool verbose=True, Bool stokesLast=False, Bool preferWavelength=False, Bool airWavelength=False, const String& origin = String(), Bool history=True); static Bool ImageHeaderToFITS(String &error, ImageFITSHeaderInfo& fhi, const ImageInterface &image, Bool preferVelocity = True, Bool opticalVelocity = True, Int BITPIX=-32, Float minPix = 1.0, Float maxPix = -1.0, Bool degenerateLast=False, Bool verbose=True, Bool stokesLast=False, Bool preferWavelength=False, Bool airWavelength=False, Bool primHead = True, Bool allowAppend = True, const String& origin = String(), Bool history=True); // // Helper function - used to calculate a cursor appropriate for the // desired memory use. It's not intended that application programmers // call this, but you may if it's useful to you. static IPosition copyCursorShape(String &report, const IPosition &shape, uInt imagePixelSize, uInt fitsPixelSize, uInt memoryInMB); // Recover CoordinateSystem from header. // Used keywords are removed from header and the unused ones returned // in a Record for ease of use. // Degenerate axes may be added to shape if needed. static CoordinateSystem getCoordinateSystem (Int& imageType, RecordInterface& headerRec, const Vector& header, LogIO& os, uInt whichRep, IPosition& shape, Bool dropStokes); // Recover ImageInfo from header. Used keywords are removed from header static ImageInfo getImageInfo (RecordInterface& header); // Recover brightness unit from header. // Used keywords are removed from header. static Unit getBrightnessUnit (RecordInterface& header, LogIO& os); // Recover history from FITS file keyword list into logger. static void restoreHistory (LoggerHolder& logger, ConstFitsKeywordList& kw); // Parse header record and set MiscInfo static Bool extractMiscInfo (RecordInterface& miscInfo, const RecordInterface& header); // Read the BEAMS table if present and add the restoring beams to // info. static void readBeamsTable (ImageInfo& info, const String& filename, const DataType type); private: // Put a CASA image to an opened FITS image // Parameters as in "ImageToFITS". In addition: //
          //
        • output The FITS output to write to. //
        • primHead Write to a primary HDU. //
        • allowAppend Allow to append extension HDU's. //
        static Bool ImageToFITSOut (String &error, LogIO &os, const ImageInterface &image, FitsOutput *output, uInt memoryInMB = 64, Bool preferVelocity = True, Bool opticalVelocity = True, Int BITPIX=-32, Float minPix = 1.0, Float maxPix = -1.0, Bool degenerateLast=False, Bool verbose=True, Bool stokesLast=False, Bool preferWavelength=False, Bool airWavelength=False, Bool primHead=True, Bool allowAppend=False, const String& origin = String(), Bool history=True); // Put a CASA image with quality coordinate // to an opened FITS file // Parameters as in "ImageToFITS". In addition: //
          //
        • output The FITS output to write to. //
        static Bool QualImgToFITSOut (String &error, LogIO &os, ImageInterface &image, FitsOutput *outfile, uInt memoryInMB, Bool preferVelocity, Bool opticalVelocity, Int BITPIX, Float minPix, Float maxPix, Bool degenerateLast, Bool verbose, Bool stokesLast, Bool preferWavelength, Bool airWavelength, const String& origin, Bool history); // If existing, remove the file, symlink, or directory given by // outFile. It is only removed if allowOverwrite=True. // An exception (using argument outName) is thrown if the file could // not be removed. static Bool removeFile (String& error, const File& outFile, const String& outName, Bool allowOverwrite); // Create an open FITS file with the name given static Bool openFitsOutput (String &error, FitsOutput *(&openFitsOutput), const String &fitsName, const Bool &allowOverwrite); static void _writeBeamsTable (FitsOutput *const &outfile, const ImageInfo& info); }; // // This class is an internal class for ImageFITSConverter. // // // // This class is an internal class used to implement // ImageFitsConverter::FITSToImage - in particular, it has the code which // is dependent on the various types (BITPIX values). // template class ImageFITSConverterImpl { public: static void FITSToImage(ImageInterface *&newImage, String &error, const String &newImageName, const uInt whichRep, HDUType &fitsImage, const String& fitsFilename, const DataType dataType, const uInt memoryInMB = 64, const Bool zeroBlanks=False); }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/images/Images/ImageFITSConverter.tcc000066400000000000000000000251551476623553700221010ustar00rootroot00000000000000//# ImageFITSConverter.cc: this defines templated conversion from FITS to a Casacore Float image //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_IMAGEFITSCONVERTER_TCC #define IMAGES_IMAGEFITSCONVERTER_TCC #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // At least the Coordinate and header related things could be factored out // into template independent code. template void ImageFITSConverterImpl::FITSToImage( ImageInterface*& pNewImage, String &error, const String &newImageName, const uInt whichRep, HDUType &fitsImage, const String& fitsFilename, const DataType dataType, const uInt memoryInMB, const Bool zeroBlanks ) { LogIO os(LogOrigin("ImageFITSConverterImpl", __FUNCTION__, WHERE)); // Crack the header and get what we need out of it. DOn't get tricked // by the fact that HDUType is referring to the template type, not // to the enum HDUType in class FITS ! // ndim uInt ndim = fitsImage.dims(); // shape IPosition shape(ndim); for (Int i=0; i header = fitsImage.kwlist_str(True); // Get Coordinate System. Return un-used FITS cards in a Record for further use. Record headerRec; Bool dropStokes = True; Int stokesFITSValue = 1; CoordinateSystem coords = ImageFITSConverter::getCoordinateSystem( stokesFITSValue, headerRec, header, os, whichRep, shape, dropStokes ); ndim = shape.nelements(); // Create image try { if (newImageName.empty()) { pNewImage = new TempImage(shape, coords); os << LogIO::NORMAL << "Created (temp)image of shape " << shape << LogIO::POST; } else { pNewImage = new PagedImage(shape, coords, newImageName); os << LogIO::NORMAL << "Created image of shape " << shape << LogIO::POST; } } catch (const AipsError& x) { if (pNewImage) { delete pNewImage; } pNewImage = 0; error = String("Error creating or writing file ") + newImageName + ":" + x.getMesg(); return; } if (pNewImage == 0) { error = String("Unknown error writing ") + newImageName; return; } // Brightness Unit Unit bu = ImageFITSConverter::getBrightnessUnit(headerRec, os); pNewImage->setUnits(bu); // BITPIX Int bitpix; Record subRec = headerRec.asRecord("bitpix"); subRec.get("value", bitpix); headerRec.removeField("bitpix"); // BLANK Find out if we are blanked. This is only relevant to // BITPIX > 0 For 32 bit floating point is is not required // by FITS (illegal ?) and Casacore does not write it out. // Other packages may write it out, so a bit of code below // to handle it. Bool isBlanked = fitsImage.isablank(); Int blankVal = fitsImage.blank(); if (bitpix < 0 && isBlanked) { if (blankVal != -1) { // Warn that we only deal with NaN blanked FP image HDU's. os << LogIO::WARN << WHERE << "For floating point images, BLANK may only be set to -1 ignore(12); ignore(0) = "^datamax$"; ignore(1) = "^datamin$"; ignore(2) = "^origin$"; ignore(3) = "^extend$"; ignore(4) = "^blocked$"; ignore(5) = "^blank$"; ignore(6) = "^simple$"; ignore(7) = "bscale"; ignore(8) = "bzero"; ignore(9) = "xtension"; ignore(10) = "pcount"; ignore(11) = "gcount"; FITSKeywordUtil::removeKeywords(headerRec, ignore); // Put whatever is left in the header into the MiscInfo bucket Record miscInfo; ImageFITSConverter::extractMiscInfo (miscInfo, headerRec); pNewImage->setMiscInfo(miscInfo); // Restore the logtable from HISTORY (this could be moved to non-templated code) LoggerHolder& logger = pNewImage->logger(); ConstFitsKeywordList kw = fitsImage.kwlist(); ImageFITSConverter::restoreHistory (logger, kw); IPosition cursorShape(ndim), cursorOrder(ndim); String report; cursorShape = ImageFITSConverter::copyCursorShape(report, shape, sizeof(Float), sizeof(typename HDUType::ElementType), memoryInMB); os << LogIO::NORMAL << "Copy FITS file to '" << pNewImage->name() << "' " << report << LogIO::POST; LatticeStepper imStepper(shape, cursorShape, IPosition::makeAxisPath(ndim)); LatticeIterator imIter(*pNewImage, imStepper); Int nIter = max(1,pNewImage->shape().product()/cursorShape.product()); Int iUpdate = max(1,nIter/20); ProgressMeter meter(0.0, Double(pNewImage->shape().product()), "FITS to Image", "Pixels copied", "", "", True, iUpdate); Double nPixPerIter = cursorShape.product(); Double meterValue; // With floating point, we don't know ahead of time if there // are blanks or not. SO we have to make the mask, and then // delete it if its not needed. ImageRegion maskReg; std::unique_ptr> pMaskIter; Bool madeMask = False; if (bitpix<0 || isBlanked) { maskReg = pNewImage->makeMask ("mask0", False, False); LCRegion& mask = maskReg.asMask(); LatticeStepper pMaskStepper (shape, cursorShape, IPosition::makeAxisPath(ndim)); pMaskIter.reset (new LatticeIterator(mask, pMaskStepper)); pMaskIter->reset(); // reset iterator madeMask = True; } // Do the work. Iterate through in chunks. Bool hasBlanks = False; try { Int bufferSize = cursorShape.product(); for (imIter.reset(),meterValue=0.0; !imIter.atEnd(); imIter++) { Array& cursor = imIter.woCursor(); fitsImage.read(bufferSize); // Read from FITS meterValue += nPixPerIter*1.0/2.0; meter.update(meterValue); if (fitsImage.err()) { error = "Error reading from FITS image"; delete pNewImage; pNewImage = 0; return; } Bool deletePtr; Float *ptr = cursor.getStorage(deletePtr); // Get Image ptr fitsImage.copy(ptr, bufferSize); // Copy from fits // Deal with mask if necessary if (madeMask) { Array& maskCursor = pMaskIter->woCursor(); Bool deleteMaskPtr; Bool* mPtr = maskCursor.getStorage(deleteMaskPtr); if (zeroBlanks) { for (size_t i=0; ioperator++(); } else { if (zeroBlanks) { for (size_t i=0; idefineRegion ("mask0", maskReg, RegionHandler::Masks); pNewImage->setDefaultMask(String("mask0")); } } } catch (const AipsError& x) { error = String("Error writing pixel values to image: " ) + x.getMesg(); delete pNewImage; pNewImage = 0; } // ImageInfo (removes any consumed keywords) ImageInfo imageInfo = ImageFITSConverter::getImageInfo(headerRec); // If we had one of those unofficial pseudo-Stokes on the Stokes axis, store it in the imageInfo if (stokesFITSValue != -1) { ImageInfo::ImageTypes type = ImageInfo::imageTypeFromFITS(stokesFITSValue); if (type!= ImageInfo::Undefined) { imageInfo.setImageType(type); } } // Try and find the restoring beam in the history cards if // its not in the header if ( ! imageInfo.hasBeam() && ! imageInfo.getRestoringBeam(logger) ) { if ( headerRec.isDefined(ImageFITSConverter::CASAMBM) && headerRec.asRecord(ImageFITSConverter::CASAMBM).asBool("value") ) { ImageFITSConverter::readBeamsTable(imageInfo, fitsFilename, dataType); } else{ os << LogIO::NORMAL << "No usable restoring beam information found." << LogIO::POST; imageInfo.removeRestoringBeam(); } } pNewImage->setImageInfo(imageInfo); return; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Images/ImageInfo.cc000066400000000000000000000650171476623553700201540ustar00rootroot00000000000000//# ImageInfo.cc: Miscellaneous information related to an image //# Copyright (C) 1998,1999,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ImageInfo::ImageInfo() : _beams(ImageBeamSet()), _warnBeam(True), itsImageType(defaultImageType()), itsObjectName(defaultObjectName()) {} ImageInfo::~ImageInfo() {} void ImageInfo::copy_other(const ImageInfo &other) { if (this != &other) { _beams = other._beams; _warnBeam = other._warnBeam; itsImageType = other.itsImageType; itsObjectName = other.itsObjectName; } } ImageInfo::ImageInfo(const ImageInfo &other) : RecordTransformable() { copy_other(other); } ImageInfo &ImageInfo::operator=(const ImageInfo &other) { copy_other(other); return *this; } ImageInfo::ImageTypes ImageInfo::defaultImageType() { return ImageInfo::Intensity; } String ImageInfo::defaultObjectName() { return String(); } GaussianBeam ImageInfo::defaultRestoringBeam() { static const GaussianBeam x; return x; } GaussianBeam ImageInfo::restoringBeam( const Int channel, const Int polarization ) const { if (_beams.empty()) { // return a null beam return defaultRestoringBeam(); } else if (_beams.nelements() == 1) { return _beams.getBeam(); } else { return _beams.getBeam(channel, polarization); } } void ImageInfo::setRestoringBeam(const GaussianBeam& beam) { ThrowIf ( _beams.hasMultiBeam(), "This object has multiple beams. They must be removed before you can define a single global restoring beam" ); ThrowIf( beam.isNull(), "Beam is null and therefore invalid." ); ImageBeamSet bs(beam); _beams = bs; } void ImageInfo::_setRestoringBeam(const Record& inRecord) { if (_beams.hasMultiBeam()) { throw AipsError( "This object has multiple beams. They must be removed before you can define a single, global beam" ); } if (! inRecord.isDefined("restoringbeam")) { throw (AipsError("Input record must have a 'restoringbeam' field")); } GaussianBeam restoringBeam = GaussianBeam::fromRecord(inRecord.asRecord("restoringbeam")); setRestoringBeam(restoringBeam); } void ImageInfo::removeRestoringBeam() { _beams = ImageBeamSet(); } Bool ImageInfo::getRestoringBeam (LoggerHolder& logger) { for ( LoggerHolder::const_iterator iter = logger.begin(); iter != logger.end(); iter++ ) { String line = iter->message(); if ( line.contains(String("BMAJ")) && line.contains(String("BMIN")) && line.contains(String("BPA")) ) { Quantity major, minor, pa; String s[20]; int n = split(line, s, 20, RXwhite); for (Int i=0; i> x; if (x <= 0) { return False; } major = Quantity(x, Unit(String("deg"))); } else if (s[i].contains("BMIN")) { istringstream oss(s[i+1].chars()); Double x; oss >> x; if (x <= 0) { return False; } minor = Quantity(x, Unit(String("deg"))); } else if (s[i].contains("BPA")) { istringstream oss(s[i+1].chars()); Double x; oss >> x; pa = Quantity(x, Unit(String("deg"))); } } if (!(minor.isConform("rad") && major.isConform("rad") && pa.isConform("rad")) || (minor.getValue() > major.getValue()) ) { return False; } _beams = ImageBeamSet(GaussianBeam(major, minor, pa)); return True; } } return False; } ImageInfo::ImageTypes ImageInfo::imageType() const { return itsImageType; } ImageInfo& ImageInfo::setImageType(ImageInfo::ImageTypes type) { itsImageType = type; return *this; } String ImageInfo::imageType(ImageInfo::ImageTypes type) { String typeOut; switch(type) { case ImageInfo::Undefined: typeOut = String("Undefined"); break; case ImageInfo::Intensity: typeOut = String("Intensity"); break; case ImageInfo::Beam: typeOut = String("Beam"); break; case ImageInfo::ColumnDensity: typeOut = String("Column Density"); break; case ImageInfo::DepolarizationRatio: typeOut = String("Depolarization Ratio"); break; case ImageInfo::KineticTemperature: typeOut = String("Kinetic Temperature"); break; case ImageInfo::MagneticField: typeOut = String("Magnetic Field"); break; case ImageInfo::OpticalDepth: typeOut = String("Optical Depth"); break; case ImageInfo::RotationMeasure: typeOut = String("Rotation Measure"); break; case ImageInfo::RotationalTemperature: typeOut = String("Rotational Temperature"); break; case ImageInfo::SpectralIndex: typeOut = String("Spectral Index"); break; case ImageInfo::Velocity: typeOut = String("Velocity"); break; case ImageInfo::VelocityDispersion: typeOut = String("Velocity Dispersion"); break; default: typeOut = String("Undefined"); break; } return typeOut; } ImageInfo::ImageTypes ImageInfo::imageType(String type) { String typeUp = upcase(type); for (uInt i=0; i(i); String t1Up = upcase(ImageInfo::imageType(t0)); if (t1Up==typeUp) { return t0; } } return defaultImageType(); } ImageInfo::ImageTypes ImageInfo::imageTypeFromFITS (Int value) { if (value==0) { return ImageInfo::Beam; } else if (value==8) { return ImageInfo::SpectralIndex; } else if (value==9) { return ImageInfo::OpticalDepth; } else { return ImageInfo::Undefined; } } String ImageInfo::objectName () const { return itsObjectName; } ImageInfo& ImageInfo::setObjectName (const String& objectName) { itsObjectName = objectName; return *this; } Bool ImageInfo::toRecord( String & error, RecordInterface & outRecord ) const { error = ""; Bool ok = True; // If the beam is null, don't do anything as it will get // restored as null as well if it is not in the record if (_beams.hasSingleBeam()) { Record restoringBeamRecord = _beams.getBeam().toRecord(); outRecord.defineRecord("restoringbeam", restoringBeamRecord); } outRecord.define("imagetype", ImageInfo::imageType(itsImageType)); outRecord.define("objectname", itsObjectName); if (_beams.hasMultiBeam()) { try { outRecord.defineRecord("perplanebeams", _beams.toRecord()); } catch (const AipsError& x) { error = x.getLastMessage(); return False; } catch (const std::exception& x) { error = x.what(); return False; } } return ok; } Bool ImageInfo::fromRecord(String& error, const RecordInterface& inRecord) { // Returns default object if none in record // Make sure we are "empty" first ImageInfo tmp; (*this) = tmp; error = ""; QuantumHolder qh; if (inRecord.isDefined("restoringbeam")) { _setRestoringBeam(inRecord); } if (inRecord.isDefined("imagetype")) { String type = inRecord.asString("imagetype"); setImageType(ImageInfo::imageType(type)); } if (inRecord.isDefined("objectname")) { String objectName = inRecord.asString("objectname"); setObjectName(objectName); } if (inRecord.isDefined("perplanebeams")) { Record hpBeams = inRecord.asRecord("perplanebeams"); _beams = ImageBeamSet::fromRecord(hpBeams); } return True; } Bool ImageInfo::toFITS(String & error, RecordInterface & outRecord) const { error = ""; if (hasBeam()) { if (hasSingleBeam()) { GaussianBeam beam = restoringBeam(); outRecord.define("bmaj", beam.getMajor("deg")); outRecord.define("bmin", beam.getMinor("deg")); outRecord.define("bpa", beam.getPA(Unit("deg"))); } else { // caller now responsible for writing beams in multi-beam iamge } } else { if (!outRecord.isFixed()) { Int field = outRecord.fieldNumber("bmaj"); if (field >= 0) outRecord.removeField(field); field = outRecord.fieldNumber("bmin"); if (field >= 0) outRecord.removeField(field); field = outRecord.fieldNumber("bpa"); if (field >= 0) outRecord.removeField(field); } } // ImageInfo::ImageTypes type = imageType(); if (type!=ImageInfo::Undefined) { String type = ImageInfo::imageType(itsImageType); outRecord.define("btype", type); } else { if (!outRecord.isFixed()) { Int field = outRecord.fieldNumber("btype"); if (field >= 0) outRecord.removeField(field); } } { outRecord.define("object", itsObjectName); } return True; } Bool ImageInfo::fromFITS( Vector& error, const RecordInterface& header ) { // keyname // value - required // unit - optional // comment - optional error.resize(3); Bool ok = True; ImageInfo tmp; (*this) = tmp; // Make sure we are "empty" first; if ( header.isDefined("bmaj") && header.isDefined("bmin") && header.isDefined("bpa") ) { const RecordInterface& subRec0 = header.asRecord("bmaj"); const RecordInterface& subRec1 = header.asRecord("bmin"); const RecordInterface& subRec2 = header.asRecord("bpa"); Double bmaj, bmin, bpa; try { subRec0.get(0, bmaj); subRec1.get(0, bmin); subRec2.get(0, bpa); if(bmaj*bmin>0.){ // Assume FITS standard unit "degrees" Unit unit(String("deg")); Quantity bmajq(max(bmaj,bmin), unit); Quantity bminq(min(bmaj,bmin), unit); Quantity bpaq(bpa, unit); bmajq.convert(Unit("arcsec")); bminq.convert(Unit("arcsec")); bpaq.convert(Unit("deg")); setRestoringBeam(GaussianBeam(bmajq, bminq, bpaq)); } else { ostringstream oss; oss << "BMAJ, BMIN ("<< bmaj << ", " << bmin <<") are not positive"; error(0) = oss.str(); ok = False; } } catch(const std::exception& x) { error(0) = std::string("ERROR reading BMAJ, BMIN, BPA: ") + x.what(); ok = False; } } if (header.isDefined("btype")) { const RecordInterface& subRec = header.asRecord("btype"); if (subRec.dataType(0)==TpString) { String type; subRec.get(0, type); // We are going to cope with Casacore values and Miriad values // For Miriad there are a few extra ones (which we put on the Stokes // axis in Casacore - e.g. position angle). For the ones that are common // the Miriad ones have underscores and the Casacore ones have spaces ImageInfo::ImageTypes imageType = ImageInfo::imageType(type); if (imageType != ImageInfo::Undefined) { setImageType(imageType); } else { imageType = MiriadImageType (type); if (imageType != ImageInfo::Undefined) { setImageType(imageType); } } } else { error(1) = "BTYPE field is not of type String"; ok = False; } } if (header.isDefined("object")) { const RecordInterface& subRec = header.asRecord("object"); if (subRec.dataType(0)==TpString || subRec.dataType(0)==TpArrayChar) { String objectName; subRec.get(0, objectName); setObjectName(objectName); } else { error(2) = "OBJECT field is not of type String"; ok = False; } } if (ok) { error.resize(0); } return ok; } const ImageBeamSet& ImageInfo::getBeamSet() const { return _beams; } ostream &operator<<(ostream &os, const ImageInfo &info) { if (info.hasMultipleBeams()) { os << "Per plane beams: " << info.getBeamSet().getBeams() << endl; } else if (info.hasSingleBeam()) { GaussianBeam beam = info.getBeamSet().getBeam(); os << "Restoring beam : " << beam.getMajor() << ", " << beam.getMinor() << ", " << beam.getPA(True) << endl; } os << "Image Type = " << info.imageType(info.imageType()) << endl; os << "Object Name = " << info.objectName() << endl; return os; } Vector ImageInfo::keywordNamesFITS() { Vector vs(5); vs(0) = "bmaj"; vs(1) = "bmin"; vs(2) = "bpa"; vs(3) = "btype"; // Miriad convention vs(4) = "object"; return vs; } ImageInfo::ImageTypes ImageInfo::MiriadImageType ( const String& type ) { // We don't fully handle all the Miriad values because // some of them (see below) are dealt with in Casacore by // the Stokes axis. String typeUp = upcase(type); if (typeUp==String("INTENSITY")) { return ImageInfo::Intensity; } if (typeUp==String("BEAM")) { return ImageInfo::Beam; } if (typeUp==String("COLUMN_DENSITY")) { return ImageInfo::ColumnDensity; } if (typeUp==String("DEPOLARIZATION_RATIO")) { return ImageInfo::DepolarizationRatio; } if (typeUp==String("KINETIC_TEMPERATURE")) { return ImageInfo::KineticTemperature; } if (typeUp==String("MAGNETIC_FIELD")) { return ImageInfo::MagneticField; } if (typeUp==String("OPTICAL_DEPTH")) { return ImageInfo::OpticalDepth; } if (typeUp==String("ROTATION_MEASURE")) { return ImageInfo::RotationMeasure; } if (typeUp==String("ROTATIONAL_TEMPERATURE")) { return ImageInfo::RotationalTemperature; } if (typeUp==String("SPECTRAL_INDEX")) { return ImageInfo::SpectralIndex; } if (typeUp==String("VELOCITY")) { return ImageInfo::Velocity; } if (typeUp==String("VELOCITY_DISPERSION")) { return ImageInfo::VelocityDispersion; } return ImageInfo::Undefined; } void ImageInfo::setBeam( const Int channel, const Int stokes, const Quantity& majAx, const Quantity& minAx, const Quantity& pa ) { GaussianBeam beam(majAx, minAx, pa); setBeam(channel, stokes, beam); } void ImageInfo::setBeam( const Int channel, const Int stokes, const GaussianBeam& beam ) { ThrowIf( _beams.empty(), "Logic error: setAllBeams() or setBeams() must be called prior to setBeam()" ); _beams.setBeam(channel, stokes, beam); } void ImageInfo::setBeams(const ImageBeamSet& beams) { _beams = beams; } void ImageInfo::setAllBeams( const uInt nChannels, const uInt nPolarizations, const GaussianBeam& beam ) { _beams.resize (nChannels, nPolarizations); _beams.set(beam); } Record ImageInfo::beamToRecord(const Int channel, const Int stokes) const { if (_beams.nelements() == 0) { return Record(); } if (_beams.nelements() == 1 || channel >= 0 || stokes >= 0) { return this->restoringBeam(channel, stokes).toRecord(); } Record rstat; // return all multi beams in a record Record myRec; uInt nchan = _beams.nchan(); uInt nstokes = _beams.nstokes(); rstat.define("nChannels", nchan); rstat.define("nStokes", nstokes); Record beamRec; for (uInt i = 0; i < nchan; i++) { Record chanRec; for (uInt j = 0; j < nstokes; j++) { chanRec.defineRecord("*" + String::toString(j), _beams(i, j).toRecord()); } beamRec.defineRecord("*" + String::toString(i), chanRec); } rstat.defineRecord("beams", beamRec); return rstat; } void ImageInfo::checkBeamSet( const CoordinateSystem& coords, const IPosition& shape, const String& imageName ) const { if (!hasBeam()) { return; } // Adapt the info as needed. /* // removing this constraint because PV images do not have a direction coordinate // but users still want to carry beam information along. if (! coords.hasDirectionCoordinate()) { logSink << "Image " << imageName << " has no direction coordinate so " << "cannot have per plane beams." << LogIO::EXCEPTION; } */ uInt beamChannels = _beams.nchan(); uInt crdChannels = 1; if (coords.hasSpectralAxis()) { Int specAxisNum = coords.spectralAxisNumber(); crdChannels = shape[specAxisNum]; } uInt beamStokes = _beams.nstokes(); uInt crdStokes = 1; if (coords.hasPolarizationCoordinate()) { Int polAxisNum = coords.polarizationAxisNumber(); crdStokes = shape[polAxisNum]; } // Either the imageinfo has 1 channel or crdChannels channels. // Same for Stokes. ThrowIf( beamChannels != 1 && beamChannels != crdChannels, "Number of channels is not consistent" ); ThrowIf( beamStokes != 1 && beamStokes != crdStokes, "Number of polarizations is not consistent" ); // Check if no null beams. Array::const_iterator iterEnd=_beams.getBeams().end(); for ( Array::const_iterator iter=_beams.getBeams().begin(); iter!=iterEnd; ++iter ) { ThrowIf( iter->isNull(), "At least one of the beams in the beam set of " + imageName + " is null and thus invalid" ); } } void ImageInfo::_checkBeamShape (uInt& nchan, uInt& npol, const IPosition& shape, const CoordinateSystem& csys) const { nchan = 0; if (csys.hasSpectralAxis()) { nchan = shape[csys.spectralAxisNumber()]; } AlwaysAssert (getBeamSet().nchan() == nchan || getBeamSet().nchan() == 1, AipsError); npol = 0; if (csys.hasPolarizationCoordinate()) { npol = shape[csys.polarizationAxisNumber()]; } AlwaysAssert (getBeamSet().nstokes() == npol || getBeamSet().nstokes() == 1, AipsError); } void ImageInfo::combineBeams (const ImageInfo& infoThat, const IPosition& shapeThis, const IPosition& shapeThat, const CoordinateSystem& csysThis, const CoordinateSystem& csysThat, Int axis, Bool relax, LogIO& os) { ImageBeamSet beamSet; // Check if coord shape and beam shape match. uInt nchan1, npol1, nchan2, npol2; if (hasBeam()) { this->_checkBeamShape (nchan1, npol1, shapeThis, csysThis); } if (infoThat.hasBeam()) { infoThat._checkBeamShape (nchan2, npol2, shapeThat, csysThat); } // No beams if one info has no beams. if (hasBeam() != infoThat.hasBeam()) { logMessage (_warnBeam, os, relax, "One image does not have a beam while another does", "The concat image will have no beam"); } else if (hasBeam()) { // Both have a beam. // Concatenate if a beam axis is the concatenation axis. // Otherwise combine the beam sets. if (axis == csysThis.spectralAxisNumber()) { concatFreqBeams (beamSet, infoThat, nchan1, nchan2, relax, os); } else if (axis == csysThis.polarizationAxisNumber()) { concatPolBeams (beamSet, infoThat, npol1, npol2, relax, os); } else { mergeBeams (beamSet, infoThat, relax, os); } } _beams = beamSet; } uInt ImageInfo::setInfoSplitBeamSet (uInt ndone, const ImageInfo& concatInfo, const IPosition& shape, const CoordinateSystem& csys, Int concatAxis) { // Copy the non-beam info. _warnBeam = concatInfo._warnBeam; itsImageType = concatInfo.itsImageType; itsObjectName = concatInfo.itsObjectName; // Copy the beam info, if needed part of it. // If the concat is not freq nor stokes, the entire beam info can be copied. // This is also the case if the beam axes have length 1. // Otherwise part of the beamset has to be taken. IPosition st(shape.size(), 0); IPosition ss(shape); st[concatAxis] = ndone; if (csys.hasSpectralAxis() && concatAxis == csys.spectralAxisNumber() && concatInfo.getBeamSet().nchan() > 1) { setBeams (concatInfo.getBeamSet().subset (Slicer(st, ss), csys)); return shape[concatAxis]; } else if (csys.hasPolarizationAxis() && concatAxis == csys.polarizationAxisNumber() && concatInfo.getBeamSet().nstokes() > 1) { setBeams (concatInfo.getBeamSet().subset (Slicer(st, ss), csys)); return shape[concatAxis]; } // Set to the entire beam set. setBeams (concatInfo.getBeamSet()); return 1; } void ImageInfo::concatFreqBeams (ImageBeamSet& beamsOut, const ImageInfo& infoThat, Int nchanThis, Int nchanThat, Bool, LogIO&) const { // Determine the number of beams for the axes in both sets. Int nc1 = _beams.nchan(); Int np1 = _beams.nstokes(); Int nc2 = infoThat.getBeamSet().nchan(); Int np2 = infoThat.getBeamSet().nstokes(); AlwaysAssert (nc1 == nchanThis || nc1 == 1, AipsError); AlwaysAssert (nc2 == nchanThat || nc2 == 1, AipsError); AlwaysAssert (np1 == np2 || np1 == 1 || np2 == 1, AipsError); // If the first beam axis has size 1 and the beamsets are equivalent, // a first beamset can be used. // Note: in principle the same test could be done if nc2==1, but chances // are very low such a test is true, thus it is a waste of time. if (nc1 == 1 && _beams.equivalent(infoThat.getBeamSet())) { beamsOut = _beams; return; } // Determine nr of output beams in both axes. // The concat axis is the sum of the image axes. Int nc = nchanThis+nchanThat; Int np = max(np1,np2); // Now concatenate the beams. beamsOut.resize (nc,np); for (Int ip=0; ip #include #include #include #include #include #include //# Forward declarations #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class LoggerHolder; // // Miscellaneous information related to an image. // // // // // //
      • RecordTransformable // // // // This class is used to record information about an image. // At present it contains the following: //
          //
        1. The restoring beam(s) //
        2. A parameter describing what quantity the image holds. //
        3. The image object name. //
        // // Support for per plane (eg channel) dependent beams have been added. //
        // // // The interface is a simple get/set interface. Note that the "set" methods // can be chained together since each set method returns a reference to its // object (rather like cout). // // ImageInfo ii; // ii.setRestoringBeam(Quantity(30,"arcsec"), Quantity(10,"arcsec"), // Quantity(-18,"deg")); // ... // cout << "The restoring beam is : " << oi.restoringBeam() << endl; // // // // // This sort of information needed a standard place to go with a // standard interface so it could be moved out of MiscInfo. // class ImageInfo : public RecordTransformable { public: // This enum defines the actual quantity being held in an image // It's really only used for descriptive information. enum ImageTypes { Undefined=0, Intensity, Beam, ColumnDensity, DepolarizationRatio, KineticTemperature, MagneticField, OpticalDepth, RotationMeasure, RotationalTemperature, SpectralIndex, Velocity, VelocityDispersion, nTypes }; // Default constructor ImageInfo(); // Destructor ~ImageInfo(); // Copy constructor (copy semantics) ImageInfo(const ImageInfo &other); // Assignment (copy semantics) ImageInfo &operator=(const ImageInfo &other); // Set and get the Image Type. // ImageInfo::ImageTypes imageType () const; ImageInfo& setImageType(ImageTypes type); static String imageType(ImageInfo::ImageTypes type); static ImageInfo::ImageTypes imageType(String type); // // Set and get the Image object name // String objectName () const; ImageInfo& setObjectName (const String& object); // // Functions to interconvert between an ImageInfo and a record. These // functions are inherited from class // RecordTransformable. As new // fields get added to ImageInfo these functions should be augmented. Missing // fields should not generate an error to in fromRecord to allow for // backwards compatibility - null values should be supplied instead. // The record field names are: "restoringbeam, imagetype, objectname". // virtual Bool toRecord(String& error, RecordInterface& outRecord) const; virtual Bool fromRecord(String& error, const RecordInterface& inRecord); // // In some circumstances it might be useful to know what the defaults for // the various values are so you can check if they have been set. // The default restoring beam is a null vector. // static ImageTypes defaultImageType(); static String defaultObjectName(); static GaussianBeam defaultRestoringBeam(); // // Functions to interconvert between an ImageInfo and FITS keywords // (converted to a Record). Failure of fromFITS // should probably not be regarded as fatal as the default ImageInfo // values are viable. For each item contained // in the ImageInfo, an attempt to decode it from FITS is made. // If any of them fail, False is returned, but it attempts to decode // them all. For those that fail an error message is held in error // in the order restoring beam, and image type. // error will be returned of length 0 if the return // value is True, else it will be length 2. // Bool toFITS(String & error, RecordInterface & outRecord) const; Bool fromFITS(Vector& error, const RecordInterface & inRecord); // // This function takes an unofficial fitsValue found on the Stokes axis // and returns the appropriate ImageType. The idea is that you // detect the unofficial value, drop the Stokes axis, and store // the value as an ImageType in ImageInfo. Only values pertaining // to beam, optical depth and spectral index are handled here. All others // give back Undefined. See usage in Image FITS conversion classes. static ImageInfo::ImageTypes imageTypeFromFITS(Int fitsValue); // It might be useful to know what FITS keyword names are used in to/from // FITS so we can remove them so they won't be used more than once. The // names are in lower case. static Vector keywordNamesFITS(); // Convert the Miriad 'btype' strings to the ImageType. Some // Miriad 'btype's are dealt with in Casacore via the Stokes // axis (fractional_polarization, polarized_intensity, position_angle) // and so these will return Undefined. static ImageInfo::ImageTypes MiriadImageType (const String& type); // Set and get the beam. // Zero-based channel and stokes are // necessary and used if and only if the ImageBeamSet // has multiple beams for such an axis. If just a single beam, that beam // is returned. If no (or a null) beam, a null beam is returned. GaussianBeam restoringBeam(Int channel=-1, Int stokes=-1) const; // Set the single global restoring beam. An exception will be // thrown if this object already has multiple beams. In that case, // the caller must call removeRestoringBeam() first. void setRestoringBeam(const GaussianBeam& beam); //#/// Added to build casarest with nrao-nov12 void setRestoringBeam(const Quantum& major, const Quantum& minor, const Quantum& pa) { setRestoringBeam (GaussianBeam (major, minor, pa)); } // Remove all beams (global or per plane) associated with this object. void removeRestoringBeam(); // Get the beam set associated with this object const ImageBeamSet& getBeamSet() const; // Set the beam for a specific plane. // A value of channel or stokes of less than 0 // means that particular coordinate does not exist. Obviously, at least // one of these must be zero or greater. The only consistency checking // that is done is to ensure the values of channel and // stokes are consistent with the size of the beam array. // Additional consistency checks are done when this object is added via // ImageInterface::setImageInfo(). //
        This function cannot be used if no beams have been set via set(All)Beams. // void setBeam(Int channel, Int stokes, const Quantity& major, const Quantity& minor, const Quantity& pa); void setBeam(Int channel, Int stokes, const GaussianBeam& beam); // // does this object contain multiple beams? Bool hasMultipleBeams() const { return _beams.hasMultiBeam(); } // does this object contain a single beam Bool hasSingleBeam() const { return _beams.hasSingleBeam(); } // Does this object contain one or more beams? Bool hasBeam() const { return ! _beams.empty(); } // // Number of channels and stokes in per hyper-plane beam array uInt nChannels() const { return _beams.nchan(); } uInt nStokes() const { return _beams.nstokes(); } // // // Initialize all per-plane beams to the same value void setAllBeams( const uInt nChannels, const uInt nStokes, const GaussianBeam& beam ); // Set the per plane beams array directly. void setBeams(const ImageBeamSet& beams); // // This method is not meant for common use. New code should not use it. // Get the restoring beam from a LoggerHolder (where the history is stored) // as AIPS writes the beam in the FITS history rather than the header // keywords. If there is no beam, False is returned, and the internal // state of the object is unchanged. Bool getRestoringBeam (LoggerHolder& logger); // Convert the given beam to a Record. Record beamToRecord(Int channel, Int stokes) const; // Check if the beam set matches the coordinate axes sizes. void checkBeamSet (const CoordinateSystem& coords, const IPosition& shape, const String& imageName) const; // Append the other beamset to this one. void appendBeams (ImageInfo& infoThat, Int axis, Bool relax, LogIO& os, const CoordinateSystem& csysThis, const CoordinateSystem& csysThat, const IPosition& shapeThis, const IPosition& shapeThat); // Combine beam sets for the concatenation of images and replace // the beamset in this object by the result. // If channel or stokes is the concatenation axis, that beam axis // is concatenated. Otherwise it is checked if both beam sets // match and are merged. // If relax=False, an exception is thrown if mismatching. void combineBeams (const ImageInfo& infoThat, const IPosition& shapeThis, const IPosition& shapeThat, const CoordinateSystem& csysThis, const CoordinateSystem& csysThat, Int axis, Bool relax, LogIO& os); // Reset the info and beamset of this image with the appropriate part of // the beam set of the concat image it is part of. // It returns the number of channels or polarizations handled. uInt setInfoSplitBeamSet (uInt ndone, const ImageInfo& concatInfo, const IPosition& shape, const CoordinateSystem& csys, Int concatAxis); // Concatenate the beam sets along the frequency axis. void concatFreqBeams (ImageBeamSet& beamsOut, const ImageInfo& infoThat, Int nchanThis, Int nchanThat, Bool relax, LogIO& os) const; // Concatenate the beam sets along the stokes axis. void concatPolBeams (ImageBeamSet& beamsOut, const ImageInfo& infoThat, Int npolThis, Int npolThat, Bool relax, LogIO& os) const; // Merge the beam sets and check if they match. void mergeBeams (ImageBeamSet& beamsOut, const ImageInfo& infoThat, Bool relax, LogIO& os) const; // If relax=True, give a warning message if warn=True and set to False. // Otherwise give an error showing msg1 only. static void logMessage(Bool& warn, LogIO& os, Bool relax, const String& msg1, const String msg2=String()); // Get the beam area in terms of pixel size of the specified // DirectionCoordinate Double getBeamAreaInPixels(Int channel, Int stokes, const DirectionCoordinate&) const; static Double getBeamAreaInPixels( const GaussianBeam& beam, const DirectionCoordinate& dc ); private: // Common copy ctor/assignment operator code. void copy_other(const ImageInfo &other); // Set the restoring beam from the record. void _setRestoringBeam(const Record& inRecord); // Check if the beam shape matches the coordinates. // It sets nchan and npol to the values in the image shape. void _checkBeamShape (uInt& nchan, uInt& npol, const IPosition& shape, const CoordinateSystem& csys) const; //# Data members ImageBeamSet _beams; mutable Bool _warnBeam; //# tell if warning is already given ImageTypes itsImageType; String itsObjectName; }; // Global functions // // Output declaration - useful for debugging. ostream &operator<<(ostream &os, const ImageInfo &info); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Images/ImageInterface.h000066400000000000000000000337671476623553700210320ustar00rootroot00000000000000//# ImageInterface.h: a base class for astronomical images //# Copyright (C) 1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_IMAGEINTERFACE_H #define IMAGES_IMAGEINTERFACE_H //# Includes #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class LatticeIterInterface; template class COWPtr; class ImageRegion; class IPosition; class TiledShape; // // A base class for astronomical images. // // // // // //
      • Lattices //
      • CoordinateSystem // // // The ImageInterface class name is derived from its role as the cookie cutter // Interface base class for Images. // // // The ImageInterface class is an abstract base class. All Image classes // should derive from this class to ensure functions which operate on Images // will work for all Image derivations. // // An Image is currently defined as an Array of pixels, a Boolean mask, // defining which pixels are valid and coordinates to define the reference // frame. The only concrete class currently derived from this Interface is // PagedImage, which allows the image to be stored on disk, and only reads // specified portions of the image into memory. // // // As this is an abstract base class it is not possible to construct an // instance of this object. It can however be used as a function argument.
        // eg 1. (used in dImageInterface.cc) // // Float sumPixels(const ImageInterface& image){ // uInt rowLength = image.shape()(0); // IPosition rowShape(image.ndim()); // rowShape = 1; rowShape(0) = rowLength; // Float sumPix = 0; // RO_LatticeIterator iter(image, rowShape); // while(!iter.atEnd()){ // sumPix += sum(iter.vectorCursor()); // iter++; // } // return sumPix; // } // // // The main purpose of this class is for programming objects, the following // example is of how one would derive from ImageInterface:
        // eg 2. // // template class myNewImage : public ImageInterface // { // public: // // default constructor // myNewImage(); // // // argumented constructor // myNewImage(...); // // // destructor // ~myNewImage // // // the shape function is forced upon us by the Lattice base class // IPosition shape() const; // // // doGetSlice is another function required of all Lattice objects. // Bool doGetSlice(& buffer, const Slicer& section); // // // etc... // private: // // put the actual map data down here. // // etc... // }; // //
        // // The use of abstract base classes to guide inheritance seemed appropriate // for Images to ensure that CoordinateSystems and masking get handled // uniformly. // // //
      • replace ImageCoordinates // template class ImageInterface: public MaskedLattice { //# Make members of parent class known. public: using MaskedLattice::shape; public: ImageInterface(); // Construct for a specific region handler object. ImageInterface (const RegionHandler& regionHandler); // Copy constructor (copy semantics). ImageInterface (const ImageInterface& other); virtual ~ImageInterface(); // Make a copy of the derived object (reference semantics). // virtual MaskedLattice* cloneML() const; virtual ImageInterface* cloneII() const = 0; // // Get the image type (returns name of derived class). virtual String imageType() const = 0; // Function which changes the shape of the image (N.B. the data is thrown // away - the Image will be filled with nonsense afterwards) virtual void resize (const TiledShape& newShape) = 0; // Function which get and set the units associated with the image // pixels (i.e. the "brightness" unit). setUnits() returns // False if it cannot set the unit for some reason (e.g. the underlying // file is not writable). // virtual Bool setUnits (const Unit& newUnits); virtual const Unit& units() const { return unit_p; } // // Return the name of the current ImageInterface object. This will generally // be a file name for images that have a persistent form. Any path // before the actual file name can be optionally stripped off. virtual String name (Bool stripPath=False) const = 0; // Functions to set or replace the coordinate information in the Image // Returns False on failure, e.g. if the number of axes do not match. // virtual Bool setCoordinateInfo (const CoordinateSystem& coords); const CoordinateSystem& coordinates() const { return coords_p; } // // Function to get a LELCoordinate object containing the coordinates. virtual LELCoordinates lelCoordinates() const; // Get access to the LoggerHolder. // LoggerHolder& logger() { return log_p; } const LoggerHolder& logger() const { return log_p; } // // Allow messages to be logged to this ImageInterface. // LogIO& logSink() { return logger().logio(); } const LogIO& logSink() const { return const_cast*>(this)->logSink(); } // // Add the messages from the other image logger to this one. void appendLog (const LoggerHolder& other) { log_p.append (other); } // Often we have miscellaneous information we want to attach to an image. // This is where it goes. //
        // Note that setMiscInfo REPLACES the information with the new information. // It can fail if, e.g., the underlying table is not writable. // const TableRecord& miscInfo() const { return miscInfo_p; } virtual Bool setMiscInfo (const RecordInterface& newInfo); // // The ImageInfo object contains some miscellaneous information about the image // which unlike that stored in MiscInfo, has a standard list of things, // such as the restoring beam. // // Note that setImageInfo REPLACES the information with the new information. // It is up to the derived class to make the ImageInfo permanent. // const ImageInfo& imageInfo() const { return imageInfo_p; } // Get non-const access to the ImageInfo. ImageInfo& rwImageInfo() { return imageInfo_p; } virtual Bool setImageInfo (const ImageInfo& info); // // Get access to the attribute handler. // By default an empty handler is returned where no groups can be added to. // virtual ImageAttrHandler& attrHandler (Bool createHandler=False); ImageAttrHandler& roAttrHandler() const { return const_cast*>(this)->attrHandler(False); } // // Can the image handle region definition? Bool canDefineRegion() const { return regHandPtr_p->canDefineRegion(); } // Make a mask which is suitable for the type of image. // Optionally the mask can be initialized with the given value // (by default it will not). //
        Optionally the mask can be defined as an image region/mask // and turned in the default mask for the image. By default it will. virtual ImageRegion makeMask (const String& name, Bool defineAsRegion = True, Bool setAsDefaultMask = True, Bool initialize = False, Bool value = True); // Define a region/mask belonging to the image. // The group type determines if it stored as a region or mask. // If overwrite=False, an exception will be thrown if the region // already exists. //
        An exception is thrown if canDefineRegion is False. virtual void defineRegion (const String& name, const ImageRegion& region, RegionHandler::GroupType, Bool overwrite = False); // Does the image have a region with the given name? virtual Bool hasRegion (const String& regionName, RegionHandler::GroupType = RegionHandler::Any) const; // Get a region/mask belonging to the image from the given group // (which can be Any). //
        Optionally an exception is thrown if the region does not exist. // A zero pointer is returned if the region does not exist. // The caller has to delete the ImageRegion object created. virtual ImageRegion* getImageRegionPtr (const String& name, RegionHandler::GroupType = RegionHandler::Any, Bool throwIfUnknown = True) const; // Rename a region. // If a region with the new name already exists, it is deleted or // an exception is thrown (depending on overwrite). // The region name is looked up in the given group(s). //
        An exception is thrown if the old region name does not exist. virtual void renameRegion (const String& newName, const String& oldName, RegionHandler::GroupType = RegionHandler::Any, Bool overwrite = False); // Remove a region/mask belonging to the image from the given group // (which can be Any). //
        Optionally an exception is thrown if the region does not exist. virtual void removeRegion (const String& name, RegionHandler::GroupType = RegionHandler::Any, Bool throwIfUnknown = True); // Get the names of all regions/masks. virtual Vector regionNames (RegionHandler::GroupType = RegionHandler::Any) const; // Use the mask as specified. // If a mask was already in use, it is replaced by the new one. virtual void useMask (MaskSpecifier = MaskSpecifier()); // Set the default pixelmask to the mask with the given name // (which has to exist in the "masks" group). // If the image table is writable, the setting is persistent by writing // the name as a keyword. // If the given regionName is the empty string, // the default pixelmask is unset. virtual void setDefaultMask (const String& regionName); // Get the name of the default pixelmask. // An empty string is returned if no default pixelmask. virtual String getDefaultMask() const; // Get a region belonging to the image. // An exception is thrown if the region does not exist. ImageRegion getRegion (const String& regionName, RegionHandler::GroupType = RegionHandler::Any) const; // Make a unique region name from the given root name, thus make it such // that the name is not already in use for a region or mask. // The root name is returned if it is already unique. // Otherwise a number is appended to the root name to make it unique. // The number starts at the given number and is incremented until the name // is unique. String makeUniqueRegionName (const String& rootName, uInt startNumber = 1) const; // Check class invariants. virtual Bool ok() const = 0; // Save and restore an ImageInterface object to or from a state Record Bool toRecord (String& error, RecordInterface& outRec); Bool fromRecord (String& error, const RecordInterface& inRec); protected: // Assignment (copy semantics) is only useful for derived classes. ImageInterface& operator= (const ImageInterface& other); // Restore the image info from the record. Bool restoreImageInfo (const RecordInterface& rec); // Set the image logger variable. void setLogMember (const LoggerHolder& logger) { log_p = logger; } // Set the image info variable. void setImageInfoMember (const ImageInfo& imageInfo); // Set the coordinate system variable. void setCoordsMember (const CoordinateSystem& coords) { coords_p = coords; } // Set the unit variable. void setUnitMember (const Unit& unit) { unit_p = unit; } // Set the miscinfo variable. void setMiscInfoMember (const RecordInterface& rec) { miscInfo_p.assign (rec); } // Get access to the region handler. RegionHandler* getRegionHandler() { return regHandPtr_p; } private: // It is the job of the derived class to make these variables valid. CoordinateSystem coords_p; LoggerHolder log_p; ImageInfo imageInfo_p; Unit unit_p; TableRecord miscInfo_p; // The region handling object. RegionHandler* regHandPtr_p; // The attribute handling object. ImageAttrHandler itsBaseAttrHandler; }; //# Declare extern templates for often used types. extern template class ImageInterface; extern template class ImageInterface; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/images/Images/ImageInterface.tcc000066400000000000000000000251451476623553700213430ustar00rootroot00000000000000//# ImageInterface.cc: defines the Image base class non pure virtual stuff //# Copyright (C) 1996,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_IMAGEINTERFACE_TCC #define IMAGES_IMAGEINTERFACE_TCC #include #include // Put these early to work around g++ bug #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template ImageInterface::ImageInterface() : regHandPtr_p (0) { regHandPtr_p = new RegionHandler(); } template ImageInterface::ImageInterface (const RegionHandler& regHand) : regHandPtr_p (0) { regHandPtr_p = regHand.clone(); regHandPtr_p->setObjectPtr (this); } template ImageInterface::ImageInterface (const ImageInterface& other) : MaskedLattice (other), coords_p (other.coords_p), log_p (other.log_p), imageInfo_p (other.imageInfo_p), unit_p (other.unit_p), miscInfo_p (other.miscInfo_p), regHandPtr_p (0) { regHandPtr_p = other.regHandPtr_p->clone(); regHandPtr_p->setObjectPtr (this); } template ImageInterface& ImageInterface::operator= (const ImageInterface& other) { if (this != &other) { MaskedLattice::operator= (other); coords_p = other.coords_p; log_p = other.log_p; imageInfo_p = other.imageInfo_p; unit_p = other.unit_p; miscInfo_p = other.miscInfo_p; delete regHandPtr_p; regHandPtr_p = 0; regHandPtr_p = other.regHandPtr_p->clone(); regHandPtr_p->setObjectPtr (this); } return *this; } template ImageInterface::~ImageInterface() { delete regHandPtr_p; } template MaskedLattice* ImageInterface::cloneML() const { return cloneII(); } // reset coords template Bool ImageInterface::setCoordinateInfo(const CoordinateSystem &coords) { ostringstream errmsg; errmsg << "Cannot set coordinate system: "; Bool ok = (coords.nPixelAxes() == shape().nelements()); if (!ok) { errmsg << "coords.nPixelAxes() == " << coords.nPixelAxes() << ", image.ndim() == " << shape().nelements(); } else { // Check that the shape is compatible with the stokes coordinates Int stkcrd = -1; while (ok && (stkcrd = coords.findCoordinate(Coordinate::STOKES, stkcrd)) >= 0) { ok = True; Int axis = coords.pixelAxes(stkcrd)(0); const StokesCoordinate &stokes = coords.stokesCoordinate(stkcrd); if (axis >= 0) { Int nstokes = stokes.stokes().nelements(); Int axislength = shape()(axis); if (axislength > nstokes) { ok = False; errmsg << "Stokes axis is length " << axislength << " but we only have " << nstokes << " stokes values in Stokes Coordinate " << stkcrd << endl; } } } } if (ok) { coords_p = coords; LogIO os; os << LogIO::DEBUGGING << "Changing coordinate system:\n" << " ndim = " << shape().nelements() << endl << " axes = " << coords_p.worldAxisNames() << endl << " ref val = " << coords_p.referenceValue() << endl << " ref pix = " << coords_p.referencePixel() << endl << " delta = " << coords_p.increment() << " units = " << coords_p.worldAxisUnits() << endl << "linear xfrom = " << coords_p.linearTransform() << LogIO::POST; } else { LogIO os; os << LogIO::SEVERE << String(errmsg) << LogIO::POST; } return ok; } template LELCoordinates ImageInterface::lelCoordinates() const { return LELCoordinates (new LELImageCoord (coords_p, imageInfo_p, units(), miscInfo_p)); } template ImageRegion ImageInterface::makeMask (const String& name, Bool defineAsRegion, Bool setAsDefaultMask, Bool initialize, Bool value) { ImageRegion region = regHandPtr_p->makeMask (*this, name); if (initialize) { region.asMask().set (value); } if (defineAsRegion) { defineRegion (name, region, RegionHandler::Masks); if (setAsDefaultMask) { setDefaultMask (name); } } return region; } template void ImageInterface::defineRegion (const String& name, const ImageRegion& region, RegionHandler::GroupType type, Bool overwrite) { regHandPtr_p->defineRegion (name, region, type, overwrite); } template Bool ImageInterface::hasRegion (const String& name, RegionHandler::GroupType type) const { return regHandPtr_p->hasRegion (name, type); } template ImageRegion* ImageInterface::getImageRegionPtr (const String& name, RegionHandler::GroupType type, Bool throwIfUnknown) const { return regHandPtr_p->getRegion (name, type, throwIfUnknown); } template void ImageInterface::renameRegion (const String& newName, const String& oldName, RegionHandler::GroupType type, Bool throwIfUnknown) { regHandPtr_p->renameRegion (newName, oldName, type, throwIfUnknown); } template void ImageInterface::removeRegion (const String& name, RegionHandler::GroupType type, Bool throwIfUnknown) { regHandPtr_p->removeRegion (name, type, throwIfUnknown); } template Vector ImageInterface::regionNames (RegionHandler::GroupType type) const { return regHandPtr_p->regionNames (type); } template void ImageInterface::setDefaultMask (const String& name) { regHandPtr_p->setDefaultMask (name); } template String ImageInterface::getDefaultMask() const { return regHandPtr_p->getDefaultMask(); } template void ImageInterface::useMask (MaskSpecifier) { throw AipsError ("ImageInterface::useMask - not implemented"); } template ImageRegion ImageInterface::getRegion (const String& regionName, RegionHandler::GroupType type) const { ImageRegion* regptr = getImageRegionPtr (regionName, type, True); ImageRegion reg(*regptr); delete regptr; return reg; } template String ImageInterface::makeUniqueRegionName (const String& rootName, uInt startNumber) const { return regHandPtr_p->makeUniqueRegionName (rootName, startNumber); } template void ImageInterface::setImageInfoMember(const ImageInfo& info) { info.checkBeamSet (coords_p, shape(), name()); imageInfo_p = info; } template Bool ImageInterface::setImageInfo(const ImageInfo& info) // // Derived classes like PagedImage have to put this in the // permanent table keywordSet // { setImageInfoMember (info); return True; } template Bool ImageInterface::setMiscInfo(const RecordInterface& miscInfo) // // Derived classes like PagedImage have to put this in the // permanent table keywordSet // { miscInfo_p = miscInfo; return True; } template Bool ImageInterface::setUnits(const Unit& unit) // // Derived classes like PagedImage have to put this in the // permanent table keywordSet // { unit_p = unit; return True; } template Bool ImageInterface::toRecord(String& error, RecordInterface& outRec) { // // Save the current ImageInterface object to an output state record // Vector shape=this->shape().asVector(); outRec.define("shape", shape); // CoordinateSystem coordsys = coordinates(); Record coordinateRecord; coordsys.save(coordinateRecord, "coordsys"); outRec.defineRecord("coordsys", coordinateRecord, Record::Variable); // outRec.define("imagearray", this->get(), False); // Record imageInfoRecord; String errorString; imageInfo_p.toRecord(errorString, imageInfoRecord); outRec.defineRecord("imageinfo", imageInfoRecord, RecordInterface::Variable); error = String(); return True; } template Bool ImageInterface::fromRecord(String& error, const RecordInterface& inRec) { // Restore the current ImageInterface object from an input state record Vector shape; inRec.get("shape", shape); IPosition shape2(shape); TiledShape newShape(shape2); resize(newShape); // const Record& coordinateRecord(inRec.asRecord("coordsys")); CoordinateSystem* pCSys = CoordinateSystem::restore(coordinateRecord, "coordsys"); setCoordinateInfo(*pCSys); delete pCSys; // Convert from any type to the required type. Array imageArray; inRec.toArray("imagearray", imageArray); this->put(imageArray); // Record imageInfoRecord(inRec.asRecord("imageinfo")); String errorString; imageInfo_p.fromRecord(errorString, imageInfoRecord); error = String(); return True; } template ImageAttrHandler& ImageInterface::attrHandler (Bool) { return itsBaseAttrHandler; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Images/ImageOpener.cc000066400000000000000000000216101476623553700205000ustar00rootroot00000000000000//# ImageOpener.cc: A class with static functions to open an image of any type //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Initialize registry. std::map ImageOpener::theirOpenFuncMap; void ImageOpener::registerOpenImageFunction (ImageTypes type, OpenImageFunction* func) { theirOpenFuncMap[type] = func; } ImageOpener::ImageTypes ImageOpener::imageType (const String& name) { File file(name); if (file.isDirectory()) { if (File(name + "/imageconcat.json").isRegular()) { return IMAGECONCAT; } if (File(name + "/imageexpr.json").isRegular()) { return IMAGEEXPR; } if (Table::isReadable(name)) { TableInfo info = TableUtil::tableInfo (name); if (info.type() == TableInfo::type(TableInfo::PAGEDIMAGE)) { return AIPSPP; } else if (info.type() == TableInfo::type(TableInfo::COMPONENTLIST)) { TableDesc tableDesc; TableUtil::getLayout(tableDesc, name); if (tableDesc.keywordSet().isDefined("coords")) { return COMPLISTIMAGE; } } } else { if (File(name + "/header").isRegular() && File(name + "/image").isRegular()) { return MIRIAD; } } } else if (file.isRegular()) { // Find file type. String base = file.path().baseName(); Int i; for (i=base.length()-1; i>0; i--) { if (base[i] == '.') { break; } } if (i > 0 && base.after(i) == "image") { String descName = file.path().dirName() + '/' + base.before(i) + ".descr"; if (File(descName).isRegular()) { return GIPSY; } } RegularFileIO fio((RegularFile(file))); char buf[2880]; Int nread = fio.read (2880, buf, False); if (nread == 2880) { String str(buf, 80); if (str.matches (Regex("^SIMPLE *= *T.*"))) { return FITS; } } if (HDF5File::isHDF5(name)) { return HDF5; } } return UNKNOWN; } LatticeBase* ImageOpener::openPagedImage (const String& fileName, const MaskSpecifier& spec) { Table table(fileName); String type = table.tableInfo().type(); if (type != TableInfo::type(TableInfo::PAGEDIMAGE)) { return 0; } if (table.nrow() != 1) { return 0; } DataType dtype = TpOther; String colName; ColumnDesc cdesc = table.tableDesc()[0]; if (cdesc.isArray()) { dtype = cdesc.dataType(); colName = cdesc.name(); } switch (dtype) { case TpFloat: return new PagedImage (table, spec); case TpDouble: return new PagedImage (table, spec); case TpComplex: return new PagedImage (table, spec); case TpDComplex: return new PagedImage (table, spec); default: return 0; } } LatticeBase* ImageOpener::openHDF5Image (const String& fileName, const MaskSpecifier& spec) { if (! HDF5File::isHDF5(fileName)) { return 0; } // See if it is an image or just an array. if (! isHDF5Image(fileName)) { return 0; } DataType dtype = hdf5imagePixelType(fileName); switch (dtype) { case TpFloat: return new HDF5Image (fileName, spec); case TpDouble: return new HDF5Image (fileName, spec); case TpComplex: return new HDF5Image (fileName, spec); case TpDComplex: return new HDF5Image (fileName, spec); default: return 0; } } LatticeBase* ImageOpener::openImageConcat (const String& fileName) { // Note that combined with ImageConcat constructor this is the // opposite of ImageConcat::save. JsonKVMap jmap = JsonParser::parseFile (fileName + "/imageconcat.json"); String dtype = jmap.get("DataType").getString(); dtype.downcase(); LatticeBase* img = 0; if (dtype == "float") { img = new ImageConcat (jmap, fileName); } else if (dtype == "double") { img = new ImageConcat (jmap, fileName); } else if (dtype == "complex") { img = new ImageConcat (jmap, fileName); } else if (dtype == "dcomplex") { img = new ImageConcat (jmap, fileName); } return img; } LatticeBase* ImageOpener::openImageExpr (const String& fileName) { // This is the opposite of ImageExpr::save. JsonKVMap jmap = JsonParser::parseFile (fileName + "/imageexpr.json"); String expr = jmap.get("ImageExpr").getString(); LatticeBase* img = openExpr (expr, Block(), fileName, jmap); return img; } LatticeBase* ImageOpener::openExpr (const String& expr, const Block& nodes, const String& fileName) { return openExpr (expr, nodes, fileName, JsonKVMap()); } LatticeBase* ImageOpener::openExpr (const String& expr, const Block& nodes, const String& fileName, const JsonKVMap& jmap) { LatticeBase* lattice = 0; PtrBlock regions; LatticeExprNode node = ImageExprParse::command (expr, nodes, regions); switch (node.dataType()) { case TpFloat: lattice = new ImageExpr (LatticeExpr(node), expr, fileName, jmap); break; case TpDouble: lattice = new ImageExpr (LatticeExpr(node), expr, fileName, jmap); break; case TpComplex: lattice = new ImageExpr (LatticeExpr(node), expr, fileName, jmap); break; case TpDComplex: lattice = new ImageExpr (LatticeExpr(node), expr, fileName, jmap); break; default: throw AipsError ("invalid data type of image expression " + expr); } return lattice; } LatticeBase* ImageOpener::openImage (const String& fileName, const MaskSpecifier& spec) { if (fileName.empty()) { return 0; } ImageOpener::ImageTypes type = ImageOpener::imageType(fileName); // Override default openFunction if (theirOpenFuncMap.find(type) != theirOpenFuncMap.end()) { try { return theirOpenFuncMap[type] (fileName, spec); } catch (...) { // Try default openFunction if theirOpenFunction fails } } // Do not require the registration of a PagedImage or HDF5Image openFunction. if (type == AIPSPP) { return openPagedImage (fileName, spec); } else if (type == HDF5) { return openHDF5Image (fileName, spec); } else if (type == IMAGECONCAT) { return openImageConcat (fileName); } else if (type == IMAGEEXPR) { return openImageExpr (fileName); } FITSImage::registerOpenFunction(); MIRIADImage::registerOpenFunction(); // Try to open a foreign image. if (theirOpenFuncMap.find(type) == theirOpenFuncMap.end()) { return 0; } return theirOpenFuncMap[type] (fileName, spec); } } //# NAMESPACE CASACORE - END casacore-3.7.1/images/Images/ImageOpener.h000066400000000000000000000123061476623553700203440ustar00rootroot00000000000000//# ImageOpener.h: A class with static functions to open an image of any type //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_IMAGEOPENER_H #define IMAGES_IMAGEOPENER_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class LatticeBase; class LatticeExprNode; class JsonKVMap; // // Definition of image types and handlers // // // // // // // // // The class contains defines the possible image types. // It contains a registry containing functions to construct an image // based on its type. In this way any image can be used in the image package // without the need that the code must reside in the images package. // // // // FITS and MIRIAD needed to be moved out of the images package. // class ImageOpener { public: // Define the possible image types. enum ImageTypes { // Casacore (former AIPS++) AIPSPP, // FITS FITS, // Miriad MIRIAD, // Gipsy GIPSY, // Classic AIPS CAIPS, // Newstar NEWSTAR, // HDF5 HDF5, // ImageConcat IMAGECONCAT, // ImageExpr IMAGEEXPR, // ComponentListImage, CASA COMPLISTIMAGE, // Unknown UNKNOWN }; // Return the type of an image with the given name. Will throw an // exception if file does not exist. static ImageTypes imageType (const String& fileName); // Define the signature of a function opening an image. // Each basic image class (like FITSImage) must have a static open function // with this signature. // They can be registered using registerOpenImageFunction. // In this way a function like openImage can create any image object // without the need that all image classes are in the images package. // The LogIO object can be used for possible error reporting or logging. typedef LatticeBase* OpenImageFunction (const String& fileName, const MaskSpecifier&); // Register an openImageFunction. static void registerOpenImageFunction (ImageTypes, OpenImageFunction*); // Open an image in the file/table with the given name. // The specified mask will be applied (default is default mask). // A null pointer is returned for an unknown image type. // Non-Casacore image types must have been registered to be known. // Note that class ImageProxy has a function to open an image from a file // or from an image expression. static LatticeBase* openImage (const String& fileName, const MaskSpecifier& = MaskSpecifier()); // Open a Casacore paged image of any data type. static LatticeBase* openPagedImage (const String& fileName, const MaskSpecifier& = MaskSpecifier()); // Open an HDF5 paged image of any data type. static LatticeBase* openHDF5Image (const String& fileName, const MaskSpecifier& = MaskSpecifier()); // Open a persistent image concatenation of any type. static LatticeBase* openImageConcat (const String& fileName); // Open a persistent image expression of any type. // It reads the file written by ImageExpr::save. static LatticeBase* openImageExpr (const String& fileName); // Parse an image expression and return the ImageExpr object for it. // The block of nodes represents optional $i arguments in the expression. // The JsonKVMap gives the keys found in a persistent image.expr file. static LatticeBase* openExpr (const String& expr, const Block& nodes, const String& fileName = String()); static LatticeBase* openExpr (const String& expr, const Block& nodes, const String& fileName, const JsonKVMap&); private: // Mapping of the image type to an openImage function. static std::map theirOpenFuncMap; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Images/ImageProxy.cc000066400000000000000000001300301476623553700203660ustar00rootroot00000000000000//# ImageProxy.cc: Proxy interface to images //# Copyright (C) 1995-2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Do not use automatic template instantiation. #define CACACORE_NO_AUTO_TEMPLATES #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; namespace casacore { //# name space casa begins ImageProxy::ImageProxy() : itsImageFloat (0), itsImageDouble (0), itsImageComplex (0), itsImageDComplex (0), itsCoordSys (0), itsAttrHandler (0) {} ImageProxy::ImageProxy (LatticeBase* lattice) : itsImageFloat (0), itsImageDouble (0), itsImageComplex (0), itsImageDComplex (0), itsCoordSys (0), itsAttrHandler (0) { setup (lattice); } ImageProxy::ImageProxy (const String& name, const String& mask, const vector& images) : itsImageFloat (0), itsImageDouble (0), itsImageComplex (0), itsImageDComplex (0), itsCoordSys (0), itsAttrHandler (0) { // Register the functions to create a FITSImage or MIRIADImage object. FITSImage::registerOpenFunction(); MIRIADImage::registerOpenFunction(); LatticeBase* lattice = openImage (name, mask, images); setup (lattice); } ImageProxy::ImageProxy (const ValueHolder& values, const ValueHolder& mask, const Record& coordinates, const String& fileName, Bool overwrite, Bool asHDF5, const String& maskName, const IPosition& tileShape) : itsImageFloat (0), itsImageDouble (0), itsImageComplex (0), itsImageDComplex (0), itsCoordSys (0), itsAttrHandler (0) { if (!overwrite) { File file(fileName); if (file.exists()) { throw AipsError ("file " + fileName + " already exists and should not be overwritten"); } } switch (values.dataType()) { case TpArrayShort: case TpArrayUShort: case TpArrayInt: case TpArrayUInt: case TpArrayInt64: case TpArrayFloat: makeImage (values.asArrayFloat(), mask.asArrayBool(), IPosition(), coordinates, fileName, asHDF5, maskName, tileShape); break; case TpArrayDouble: makeImage (values.asArrayDouble(), mask.asArrayBool(), IPosition(), coordinates, fileName, asHDF5, maskName, tileShape); break; case TpArrayComplex: makeImage (values.asArrayComplex(), mask.asArrayBool(), IPosition(), coordinates, fileName, asHDF5, maskName, tileShape); break; case TpArrayDComplex: makeImage (values.asArrayDComplex(), mask.asArrayBool(), IPosition(), coordinates, fileName, asHDF5, maskName, tileShape); break; default: throw AipsError ("ImageProxy: invalid data type"); } } ImageProxy::ImageProxy (const IPosition& shape, const ValueHolder& value, const Record& coordinates, const String& fileName, Bool overwrite, Bool asHDF5, const String& maskName, const IPosition& tileShape, Int) : itsImageFloat (0), itsImageDouble (0), itsImageComplex (0), itsImageDComplex (0), itsCoordSys (0), itsAttrHandler (0) { if (!overwrite) { File file(fileName); if (file.exists()) { throw AipsError ("file " + fileName + " already exists and should not be overwritten"); } } switch (value.dataType()) { case TpUChar: case TpShort: case TpUShort: case TpInt: case TpUInt: case TpFloat: makeImage (Array(), Array(), shape, coordinates, fileName, asHDF5, maskName, tileShape); break; case TpDouble: makeImage (Array(), Array(), shape, coordinates, fileName, asHDF5, maskName, tileShape); break; case TpComplex: makeImage (Array(), Array(), shape, coordinates, fileName, asHDF5, maskName, tileShape); break; case TpDComplex: makeImage (Array(), Array(), shape, coordinates, fileName, asHDF5, maskName, tileShape); break; default: throw AipsError ("ImageProxy: invalid data type"); } } ImageProxy::ImageProxy (const Vector& names, Int axis) : itsImageFloat (0), itsImageDouble (0), itsImageComplex (0), itsImageDComplex (0), itsCoordSys (0), itsAttrHandler (0) { vector images; images.reserve (names.size()); for (uInt i=0; i())); } concatImages (images, axis); } ImageProxy::ImageProxy (const vector& images, Int axis, Int, Int) : itsImageFloat (0), itsImageDouble (0), itsImageComplex (0), itsImageDComplex (0), itsCoordSys (0), itsAttrHandler (0) { concatImages (images, axis); } ImageProxy::ImageProxy (const std::shared_ptr& image) : itsLattice (image), itsImageFloat (0), itsImageDouble (0), itsImageComplex (0), itsImageDComplex (0), itsCoordSys (0), itsAttrHandler (0) { if (itsLattice) { setup(); } } ImageProxy::ImageProxy (const ImageProxy& that) : itsLattice (that.itsLattice), itsImageFloat (0), itsImageDouble (0), itsImageComplex (0), itsImageDComplex (0), itsCoordSys (0), itsAttrHandler (0) { if (itsLattice) { setup(); } } ImageProxy& ImageProxy::operator= (const ImageProxy& that) { if (this != &that) { close(); itsLattice = that.itsLattice; if (itsLattice) { setup(); } } return *this; } ImageProxy::~ImageProxy() {} LatticeBase* ImageProxy::openImage (const String& name, const String& mask, const vector& images) { MaskSpecifier maskSp; if (!mask.empty()) { if (mask == "nomask") { maskSp = MaskSpecifier (""); } else { maskSp = MaskSpecifier (mask); } } Block tempNodes(images.size()); for (uInt i=0; i& nodes) { LatticeBase* lattice = ImageOpener::openImage (str, spec); if (lattice == 0) { lattice = ImageOpener::openExpr (str, nodes); } return lattice; } void ImageProxy::close() { itsLattice.reset(); itsImageFloat = 0; itsImageDouble = 0; itsImageComplex = 0; itsImageDComplex = 0; itsCoordSys = 0; itsAttrHandler = 0; } void ImageProxy::checkNull() const { if (!itsLattice) { throw AipsError ("ImageProxy does not contain an image object"); } } LatticeExprNode ImageProxy::makeNode() const { if (itsImageFloat) { return LatticeExprNode (*itsImageFloat); } else if (itsImageDouble) { return LatticeExprNode (*itsImageDouble); } else if (itsImageComplex) { return LatticeExprNode (*itsImageComplex); } else if (itsImageDComplex) { return LatticeExprNode (*itsImageDComplex); } throw AipsError ("ImageProxy does not contain an image object"); } template void ImageProxy::makeImage (const Array& array, const Array& mask, const IPosition& shape, const Record& coordinates, const String& name, Bool asHDF5, const String& maskName, const IPosition& tileShape) { // Get shape and check arguments. IPosition shp(shape); if (array.size() == 0) { if (shape.size() == 0) { throw AipsError ("A value array or a shape has to be given"); } } else { shp = array.shape(); if (mask.size() > 0) { AlwaysAssert (array.shape().isEqual(mask.shape()), AipsError); } } CoordinateSystem cSys; if (coordinates.empty()) { cSys = CoordinateUtil::makeCoordinateSystem (shp, False); centreRefPix(cSys, shp); } else { cSys = makeCoordinateSystem (coordinates, shp); } ImageInterface* image = 0; if (name.empty()) { image = new TempImage (shp, cSys, 1000); } else if (asHDF5) { image = new HDF5Image (makeTiledShape(tileShape,shp), cSys, name); } else { image = new PagedImage (makeTiledShape(tileShape,shp), cSys, name); } // Let ImageProxy take over the pointer. setup(image); // Write data if present. if (array.size() > 0) { image->put (array); } // Create and put mask if needed. // Use default 'mask0' if a mask is given. String mname(maskName); if (mname.empty() && mask.size() > 0) { mname = "mask0"; } if (!mname.empty()) { // Create a mask and make it the default mask. image->makeMask (mname, True, True); } // Put mask if present. if (mask.size() > 0) { image->pixelMask().put (mask); } } void ImageProxy::concatImages (const vector& images, Int axis) { if (images.size() == 0) { throw AipsError ("ImageProxy: no images given in vector"); } switch (images[0].getLattice()->dataType()) { case TpFloat: concatImagesFloat (images, axis); break; case TpDouble: concatImagesDouble (images, axis); break; case TpComplex: concatImagesComplex (images, axis); break; case TpDComplex: concatImagesDComplex (images, axis); break; default: throw AipsError ("Image has an invalid data type"); } } void ImageProxy::concatImagesFloat (const vector& images, Int axis) { ImageConcat* concat = new ImageConcat(axis); for (uInt i=0; idataType() != TpFloat) { throw AipsError ("Not all images to concatenate have type Float"); } // Note this cast is fully safe. concat->setImage (*(ImageInterface*)(lattice), True); } setup (concat); } void ImageProxy::concatImagesDouble (const vector& images, Int axis) { ImageConcat* concat = new ImageConcat(axis); for (uInt i=0; idataType() != TpDouble) { throw AipsError ("Not all images to concatenate have type Double"); } // Note this cast is fully safe. concat->setImage (*(ImageInterface*)(lattice), True); } setup (concat); } void ImageProxy::concatImagesComplex (const vector& images, Int axis) { ImageConcat* concat = new ImageConcat(axis); for (uInt i=0; idataType() != TpComplex) { throw AipsError ("Not all images to concatenate have type Complex"); } // Note this cast is fully safe. concat->setImage (*(ImageInterface*)(lattice), True); } setup (concat); } void ImageProxy::concatImagesDComplex (const vector& images, Int axis) { ImageConcat* concat = new ImageConcat(axis); for (uInt i=0; idataType() != TpDComplex) { throw AipsError ("Not all images to concatenate have type DComplex"); } // Note this cast is fully safe. concat->setImage (*(ImageInterface*)(lattice), True); } setup (concat); } void ImageProxy::setup (LatticeBase* lattice) { itsLattice.reset (lattice); setup(); } void ImageProxy::setup() { // Get raw pointer from the std::shared_ptr. LatticeBase* lattice = itsLattice.get(); switch (lattice->dataType()) { case TpFloat: itsImageFloat = dynamic_cast*>(lattice); break; case TpDouble: itsImageDouble = dynamic_cast*>(lattice); break; case TpComplex: itsImageComplex = dynamic_cast*>(lattice); break; case TpDComplex: itsImageDComplex = dynamic_cast*>(lattice); break; default: throw AipsError ("Image has an invalid data type"); } if (itsImageFloat) { itsCoordSys = &itsImageFloat->coordinates(); itsAttrHandler = &itsImageFloat->attrHandler(); } else if (itsImageDouble) { itsCoordSys = &itsImageDouble->coordinates(); itsAttrHandler = &itsImageDouble->attrHandler(); } else if (itsImageComplex) { itsCoordSys = &itsImageComplex->coordinates(); itsAttrHandler = &itsImageComplex->attrHandler(); } else if (itsImageDComplex) { itsCoordSys = &itsImageDComplex->coordinates(); itsAttrHandler = &itsImageDComplex->attrHandler(); } else { throw AipsError ("The lattice does not appear to be an image"); } } void ImageProxy::centreRefPix (CoordinateSystem& cSys, const IPosition& shape) const { // Center all axes except Stokes. Int after = -1; Int iS = cSys.findCoordinate (Coordinate::STOKES, after); Int sP = -1; if (iS >= 0) { sP = cSys.pixelAxes(iS)[0]; } Vector refPix = cSys.referencePixel(); for (uInt i=0; iisPersistent(); } String ImageProxy::name (Bool stripPath) const { checkNull(); return itsLattice->name (stripPath); } IPosition ImageProxy::shape() const { checkNull(); return itsLattice->shape(); } uInt ImageProxy::ndim() const { checkNull(); return itsLattice->shape().size(); } uInt ImageProxy::size() const { checkNull(); return itsLattice->shape().product(); } String ImageProxy::dataType() const { checkNull(); ostringstream ostr; ostr << itsLattice->dataType(); return ostr.str(); } DataType ImageProxy::type() const { checkNull(); return itsLattice->dataType(); } String ImageProxy::imageType() const { // LatticeBase does not have a fileType function or so. // So alas we have to use the one in the Image object. if (itsImageFloat) { return itsImageFloat->imageType(); } else if (itsImageDouble) { return itsImageDouble->imageType(); } else if (itsImageComplex) { return itsImageComplex->imageType(); } else if (itsImageDComplex) { return itsImageDComplex->imageType(); } throw AipsError ("ImageProxy does not contain an image object"); } Vector ImageProxy::attrGroupNames() const { checkNull(); return itsAttrHandler->groupNames(); } void ImageProxy::createAttrGroup (const String& groupName) { checkNull(); itsAttrHandler->createGroup (groupName); } Vector ImageProxy::attrNames (const String& groupName) const { checkNull(); return itsAttrHandler->openGroup(groupName).attrNames(); } uInt ImageProxy::attrNrows (const String& groupName) const { checkNull(); return itsAttrHandler->openGroup(groupName).nrows(); } ValueHolder ImageProxy::getAttr (const String& groupName, const String& attrName, uInt rownr) const { checkNull(); return itsAttrHandler->openGroup(groupName).getData (attrName, rownr); } Record ImageProxy::getAttrRow (const String& groupName, uInt rownr) const { checkNull(); return itsAttrHandler->openGroup(groupName).getDataRow (rownr); } Vector ImageProxy::getAttrUnit(const String& groupName, const String& attrName) const { checkNull(); return itsAttrHandler->openGroup(groupName).getUnit (attrName); } Vector ImageProxy::getAttrMeas(const String& groupName, const String& attrName) const { checkNull(); return itsAttrHandler->openGroup(groupName).getMeasInfo (attrName); } void ImageProxy::putAttr (const String& groupName, const String& attrName, uInt rownr, const ValueHolder& value, const Vector& units, const Vector& measInfo) { checkNull(); itsAttrHandler->openGroup(groupName).putData (attrName, rownr, value, units, measInfo); } ValueHolder ImageProxy::getData (const IPosition& blc, const IPosition& trc, const IPosition& inc) { IPosition shp = shape(); Slicer slicer(adjustBlc(blc, shp), adjustTrc(trc, shp), adjustInc(inc, shp), Slicer::endIsLast); if (itsImageFloat) { return ValueHolder (itsImageFloat->getSlice (slicer)); } else if (itsImageDouble) { return ValueHolder (itsImageDouble->getSlice (slicer)); } else if (itsImageComplex) { return ValueHolder (itsImageComplex->getSlice (slicer)); } else if (itsImageDComplex) { return ValueHolder (itsImageDComplex->getSlice (slicer)); } throw AipsError ("ImageProxy does not contain an image object"); } ValueHolder ImageProxy::getMask (const IPosition& blc, const IPosition& trc, const IPosition& inc) { IPosition shp = shape(); Slicer slicer(adjustBlc(blc, shp), adjustTrc(trc, shp), adjustInc(inc, shp), Slicer::endIsLast); if (itsImageFloat) { return ValueHolder (itsImageFloat->getMaskSlice (slicer)); } else if (itsImageDouble) { return ValueHolder (itsImageDouble->getMaskSlice (slicer)); } else if (itsImageComplex) { return ValueHolder (itsImageComplex->getMaskSlice (slicer)); } else if (itsImageDComplex) { return ValueHolder (itsImageDComplex->getMaskSlice (slicer)); } throw AipsError ("ImageProxy does not contain an image object"); } void ImageProxy::putData (const ValueHolder& value, const IPosition& blc, const IPosition& inc) { IPosition shp = shape(); IPosition ablc = adjustBlc (blc, shp); IPosition ainc = adjustInc (inc, shp); if (itsImageFloat) { itsImageFloat->putSlice (value.asArrayFloat(), ablc, ainc); } else if (itsImageDouble) { itsImageDouble->putSlice (value.asArrayDouble(), ablc, ainc); } else if (itsImageComplex) { itsImageComplex->putSlice (value.asArrayComplex(), ablc, ainc); } else if (itsImageDComplex) { itsImageDComplex->putSlice (value.asArrayDComplex(), ablc, ainc); } else { throw AipsError ("ImageProxy does not contain an image object"); } } void ImageProxy::putMask (const ValueHolder& value, const IPosition& blc, const IPosition& inc) { IPosition shp = shape(); IPosition ablc = adjustBlc (blc, shp); IPosition ainc = adjustInc (inc, shp); if (itsImageFloat) { doPutMask (*itsImageFloat, value, ablc, ainc); } else if (itsImageDouble) { doPutMask (*itsImageDouble, value, ablc, ainc); } else if (itsImageComplex) { doPutMask (*itsImageComplex, value, ablc, ainc); } else if (itsImageDComplex) { doPutMask (*itsImageDComplex, value, ablc, ainc); } else { throw AipsError ("ImageProxy does not contain an image object"); } } template void ImageProxy::doPutMask (ImageInterface& image, const ValueHolder& value, const IPosition& blc, const IPosition& inc) { checkNull(); Array maskArr = value.asArrayBool(); if (! image.hasPixelMask()) { // No mask yet. // Do not put if the entire mask is true. This might reflect a get // where all True-s are filled in if there is no mask. if (anyEQ(maskArr, False)) { // Create a mask and make it the default mask. image.makeMask ("mask0", True, True); // Initialize the mask if only part of the mask will be put. if (! maskArr.shape().isEqual (image.shape())) { image.pixelMask().set (True); } } } if (image.hasPixelMask()) { image.pixelMask().putSlice (value.asArrayBool(), blc, inc); } } Bool ImageProxy::hasLock (Bool writeLock) { checkNull(); return itsLattice->hasLock (writeLock ? FileLocker::Write : FileLocker::Read); } void ImageProxy::lock (Bool writeLock, Int nattempts) { checkNull(); itsLattice->lock (writeLock ? FileLocker::Write : FileLocker::Read, nattempts); } void ImageProxy::unlock() { checkNull(); itsLattice->unlock(); } ImageProxy ImageProxy::subImage (const IPosition& blc, const IPosition& trc, const IPosition& inc, Bool dropDegenerate) { return subImage2 (blc, trc, inc, dropDegenerate, False); } ImageProxy ImageProxy::subImage2 (const IPosition& blc, const IPosition& trc, const IPosition& inc, Bool dropDegenerate, Bool preserveAxesOrder) { AxesSpecifier axesSpec(!dropDegenerate); IPosition shp = shape(); Slicer slicer(adjustBlc(blc, shp), adjustTrc(trc, shp), adjustInc(inc, shp), Slicer::endIsLast); if (itsImageFloat) { return ImageProxy(new SubImage(*itsImageFloat, slicer, True, axesSpec, preserveAxesOrder)); } else if (itsImageDouble) { return ImageProxy(new SubImage(*itsImageDouble, slicer, True, axesSpec, preserveAxesOrder)); } else if (itsImageComplex) { return ImageProxy(new SubImage(*itsImageComplex, slicer, True, axesSpec, preserveAxesOrder)); } else if (itsImageDComplex) { return ImageProxy(new SubImage(*itsImageDComplex, slicer, True, axesSpec, preserveAxesOrder)); } throw AipsError ("ImageProxy does not contain an image object"); } IPosition ImageProxy::adjustBlc (const IPosition& blc, const IPosition& shp) { if (blc.size() > shp.size()) { throw AipsError ("blc length exceeds dimensionality of image"); } // Initialize possibly missing elements to 0. IPosition res(shp.size(), 0); for (uInt i=0; i= shp[i]) { throw AipsError ("blc value exceeds shape of image"); } else if (blc[i] > 0) { res[i] = blc[i]; } } return res; } IPosition ImageProxy::adjustTrc (const IPosition& trc, const IPosition& shp) { if (trc.size() > shp.size()) { throw AipsError ("trc length exceeds dimensionality of image"); } // Initialize possibly missing elements to shape. IPosition res(shp-1); for (uInt i=0; i 0 || trc[i] < shp[i]) { res[i] = trc[i]; } } return res; } IPosition ImageProxy::adjustInc (const IPosition& inc, const IPosition& shp) { if (inc.size() > shp.size()) { throw AipsError ("inc length exceeds dimensionality of image"); } // Initialize possibly missing elements to 1. IPosition res(shp.size(), 1); for (uInt i=0; i 1) { res[i] = inc[i]; } } return res; } String ImageProxy::unit()const { if (itsImageFloat) { return itsImageFloat->units().getName(); } else if (itsImageDouble) { return itsImageDouble->units().getName(); } else if (itsImageComplex) { return itsImageComplex->units().getName(); } else if (itsImageDComplex) { return itsImageDComplex->units().getName(); } throw AipsError ("ImageProxy does not contain an image object"); } Record ImageProxy::coordSys() const { checkNull(); Record rec; itsCoordSys->save (rec, "x"); Record& coord = rec.rwSubRecord("x"); // Add the pixel axes info, so it can be used in coordinates.py. // Give the info in C-order (thus reverse values). // Also add the axes lengths. IPosition shape = itsLattice->shape(); for (uInt i=0; inCoordinates(); ++i) { Vector paxes = itsCoordSys->pixelAxes(i); Vector axes(paxes.size()); Vector axshp(paxes.size()); for (uInt j=0; jcoordRecordName(i)); coordRec.define ("_image_axes", axes); coordRec.define ("_axes_sizes", axshp); } return coord; } const CoordinateSystem& ImageProxy::coordSysObject() const { checkNull(); return *itsCoordSys; } Vector ImageProxy::toWorld (const Vector& pixel, Bool reverseAxes) { checkNull(); Vector coord(pixel.size()); if (!reverseAxes) { coord = pixel; } else { for (uInt i=0; i world; if (! itsCoordSys->toWorld (world, coord)) { throw AipsError (itsCoordSys->errorMessage()); } if (!reverseAxes) { return world; } for (uInt i=0; i ImageProxy::toPixel (const Vector& world, Bool reverseAxes) { checkNull(); Vector coord(world.size()); if (!reverseAxes) { coord = world; } else { for (uInt i=0; i pixel; if (! itsCoordSys->toPixel (pixel, coord)) { throw AipsError (itsCoordSys->errorMessage()); } if (!reverseAxes) { return pixel; } for (uInt i=0; iimageInfo(); } else if (itsImageDouble) { return itsImageDouble->imageInfo(); } else if (itsImageComplex) { return itsImageComplex->imageInfo(); } else if (itsImageDComplex) { return itsImageDComplex->imageInfo(); } else { throw AipsError ("ImageProxy does not contain an image object"); } } Record ImageProxy::miscInfo() const { TableRecord rec; if (itsImageFloat) { rec = itsImageFloat->miscInfo(); } else if (itsImageDouble) { rec = itsImageDouble->miscInfo(); } else if (itsImageComplex) { rec = itsImageComplex->miscInfo(); } else if (itsImageDComplex) { rec = itsImageDComplex->miscInfo(); } else { throw AipsError ("ImageProxy does not contain an image object"); } return Record(rec); } void ImageProxy::toFits (const String& fileName, Bool overwrite, Bool velocity, Bool optical, Int bitpix, Double minpix, Double maxpix) const { checkNull(); Bool ok = False; String error ("Currently only float images can be converted to FITS"); if (itsImageFloat) { ok = ImageFITSConverter::ImageToFITS (error, *itsImageFloat, fileName, HostInfo::memoryFree()/1024, velocity, optical, bitpix, minpix, maxpix, overwrite, False, False); } if (!ok) { throw AipsError (error); } } Vector ImageProxy::history() const { const LoggerHolder* logger; if (itsImageFloat) { logger = &itsImageFloat->logger(); } else if (itsImageDouble) { logger = &itsImageDouble->logger(); } else if (itsImageComplex) { logger = &itsImageComplex->logger(); } else if (itsImageDComplex) { logger = &itsImageDComplex->logger(); } else { throw AipsError ("ImageProxy does not contain an image object"); } list l; for (LoggerHolder::const_iterator iter = logger->begin(); iter != logger->end(); iter++) { l.push_back (iter->message()); } Vector vec(l.size()); String* vecd = vec.data(); for (list::const_iterator iter=l.begin(); iter!=l.end(); ++iter) { *vecd++ = *iter; } return vec; } void ImageProxy::saveAs (const String& fileName, Bool overwrite, Bool hdf5, Bool copyMask, const String& newMaskName, const IPosition& newTileShape) const { if (!overwrite) { File file(fileName); if (file.exists()) { throw AipsError ("file " + fileName + " already exists and should not be overwritten"); } } if (itsImageFloat) { saveImage (fileName, hdf5, copyMask, newMaskName, newTileShape, *itsImageFloat); } else if (itsImageDouble) { saveImage (fileName, hdf5, copyMask, newMaskName, newTileShape, *itsImageDouble); } else if (itsImageComplex) { saveImage (fileName, hdf5, copyMask, newMaskName, newTileShape, *itsImageComplex); } else if (itsImageDComplex) { saveImage (fileName, hdf5, copyMask, newMaskName, newTileShape, *itsImageDComplex); } else { throw AipsError ("ImageProxy does not contain an image object"); } } TiledShape ImageProxy::makeTiledShape (const IPosition& newTileShape, const IPosition& shape, const IPosition& oldTileShape) const { if (! newTileShape.empty()) { return TiledShape (shape, newTileShape); } if (oldTileShape.empty()) { return TiledShape (shape); } return TiledShape (shape, oldTileShape); } template void ImageProxy::saveImage (const String& fileName, Bool hdf5, Bool copyMask, const String& newMaskName, const IPosition& newTileShape, const ImageInterface& image) const { checkNull(); ImageInterface* newImage; TiledShape tiledShape (makeTiledShape (newTileShape, image.shape(), image.niceCursorShape())); if (hdf5) { newImage = new HDF5Image (tiledShape, image.coordinates(), fileName); } else { newImage = new PagedImage (tiledShape, image.coordinates(), fileName); } newImage->copyData (image); ImageUtilities::copyMiscellaneous (*newImage, image); if (copyMask && image.isMasked()) { // Generate mask name if not given String maskName = newMaskName; if (maskName.empty()) { maskName = image.getDefaultMask(); if (maskName.empty()) { maskName = newImage->makeUniqueRegionName ("mask", 0); } } // Create a mask and make it the default mask. // Copy the image mask. newImage->makeMask (maskName, True, True); Lattice& pixelMaskOut = newImage->pixelMask(); LatticeIterator maskIter(pixelMaskOut); for (maskIter.reset(); !maskIter.atEnd(); maskIter++) { maskIter.rwCursor() = image.getMaskSlice(maskIter.position(), maskIter.cursorShape()); } } delete newImage; } Record ImageProxy::statistics (const Vector& axes, const String& mask, const ValueHolder& minMaxValues, Bool exclude, Bool robust) const { checkNull(); // Default for cursor is all axes. Vector axesc(axes); if (axesc.empty()) { axesc.resize (ndim()); indgen (axesc); } if (itsImageFloat) { return makeStatistics (*itsImageFloat, axesc, mask, minMaxValues, exclude, robust); } else if (itsImageDouble) { throw AipsError("No statistics possible yet on double precision images"); } else if (itsImageComplex) { throw AipsError("No statistics possible on complex images"); } else if (itsImageDComplex) { throw AipsError("No statistics possible on dcomplex images"); } else { throw AipsError ("ImageProxy does not contain an image object"); } } template Record ImageProxy::makeStatistics (const ImageInterface& image, const Vector& axes, const String&, const ValueHolder& minMaxValues, Bool exclude, Bool robust) const { checkNull(); ImageStatistics stats(image, False, False); // Set cursor axes. if (!stats.setAxes(axes)) { throw AipsError (stats.errorMessage()); } // Set pixel include/exclude ranges. Vector minMax; minMaxValues.getValue (minMax); if (minMax.size() > 0) { if (exclude) { stats.setInExCludeRange (Vector(), minMax, False); } else { stats.setInExCludeRange (minMax, Vector(), False); } } // Get statistics. Array npts, sum, sumsquared, min, max, mean, sigma; Array rms, fluxDensity, med, medAbsDevMed, quartile; if (robust) { stats.getStatistic (med, LatticeStatsBase::MEDIAN); stats.getStatistic (medAbsDevMed, LatticeStatsBase::MEDABSDEVMED); stats.getStatistic (quartile, LatticeStatsBase::QUARTILE); } stats.getStatistic (npts, LatticeStatsBase::NPTS); stats.getStatistic (sum, LatticeStatsBase::SUM); stats.getStatistic (sumsquared, LatticeStatsBase::SUMSQ); stats.getStatistic (min, LatticeStatsBase::MIN); stats.getStatistic (max, LatticeStatsBase::MAX); stats.getStatistic (mean, LatticeStatsBase::MEAN); stats.getStatistic (sigma, LatticeStatsBase::SIGMA); stats.getStatistic (rms, LatticeStatsBase::RMS); stats.getStatistic (fluxDensity, LatticeStatsBase::FLUX); Record retval; retval.define ("npts", npts); retval.define ("sum", sum); retval.define ("sumsq", sumsquared); retval.define ("min", min); retval.define ("max", max); retval.define ("mean", mean); if (robust) { retval.define ("median", med); retval.define ("medabsdevmed", medAbsDevMed); retval.define ("quartile", quartile); } retval.define ("sigma", sigma); retval.define ("rms", rms); ////retval.define ("flux", fluxDensity); IPosition minPos, maxPos; if (stats.getMinMaxPos(minPos, maxPos)) { if (minPos.nelements() > 0 && maxPos.nelements() > 0) { retval.define ("minpos", minPos.asVector()); retval.define ("maxpos", maxPos.asVector()); } } return retval; } ImageProxy ImageProxy::regrid (const Vector& axes, const String& outFile, Bool overwrite, const IPosition& shape, const Record& coordSys, const String& method, Int decimate, Bool replicate, Bool doRefChange, Bool forceRegrid) { if (!overwrite) { File file(outFile); if (file.exists()) { throw AipsError ("file " + outFile + " already exists and should not be overwritten"); } } if (itsImageFloat) { return doRegrid (*itsImageFloat, axes, outFile, shape, coordSys, method, decimate, replicate, doRefChange, forceRegrid); } else if (itsImageDouble) { throw AipsError("No regrid possible yet on double precision images"); } else if (itsImageComplex) { throw AipsError("No regrid possible on complex images"); } else if (itsImageDComplex) { throw AipsError("No regrid possible on dcomplex images"); } else { throw AipsError ("ImageProxy does not contain an image object"); } } template ImageProxy ImageProxy::doRegrid (const ImageInterface& image, const Vector& axes, const String& outFile, const IPosition& shape, const Record& coordSys, const String& method, Int decimate, Bool replicate, Bool doRefChange, Bool forceRegrid) { String method2 = method; method2.upcase(); IPosition outShape; if (shape.size() == 0 || shape[0] == -1) { outShape = image.shape(); } else { outShape = shape; } // Deal with axes IPosition axes2(axes); // Make CoordinateSystem from user given. CoordinateSystem cSysTo = makeCoordinateSystem (coordSys, outShape); CoordinateSystem cSysFrom = image.coordinates(); if(cSysTo.nCoordinates() == 0){ cSysTo = cSysFrom; } cSysTo.setObsInfo (cSysFrom.obsInfo()); // Now build a CS which copies the user specified Coordinate for // axes to be regridded and the input image Coordinate for axes not // to be regridded LogIO log; set regridCoords; CoordinateSystem cSys = ImageRegrid::makeCoordinateSystem (log, regridCoords, cSysTo, cSysFrom, axes2); if (cSys.nPixelAxes() != outShape.nelements()) { throw AipsError("The number of pixel axes in the output shape and " "Coordinate System must be the same"); } // Create the image and mask ImageInterface* pImOut; if (outFile.empty()) { pImOut = new TempImage(outShape, cSys); } else { pImOut = new PagedImage(outShape, cSys, outFile); /// make hdf5 if image is hdf5 } // Create proxy from it, so it gets deleted in case of an exception. ImageProxy proxy(pImOut); pImOut->set(0.0); ImageUtilities::copyMiscellaneous (*pImOut, image); Interpolate2D::Method imethod = Interpolate2D::stringToMethod(method); IPosition dummy; ImageRegrid ir; ir.disableReferenceConversions (!doRefChange); ir.regrid (*pImOut, imethod, axes2, image, replicate, decimate, True, forceRegrid); return proxy; } CoordinateSystem ImageProxy::makeCoordinateSystem (const Record& coordinates, const IPosition& shape) const { CoordinateSystem* csp; if (coordinates.nfields() == 1) { // Must be a record as an element Record tmp(coordinates.asRecord(RecordFieldId(0))); csp = CoordinateSystem::restore (tmp, ""); } else { csp = CoordinateSystem::restore (coordinates, ""); } CoordinateSystem cs(*csp); delete csp; // Fix up any body longitude ranges. String errMsg; if (!CoordinateUtil::cylindricalFix (cs, errMsg, shape)) { throw AipsError (errMsg); } return cs; } } // end of casa namespace casacore-3.7.1/images/Images/ImageProxy.h000066400000000000000000000453101476623553700202360ustar00rootroot00000000000000//# ImageProxy.h: Proxy interface to images //# Copyright (C) 1997-2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_IMAGEPROXY_H #define IMAGES_IMAGEPROXY_H //# Includes #include #include #include #include #include #include #include namespace casacore { //# Forward Declarations. class ImageInfo; template class ImageInterface; class LatticeExprNode; class CoordinateSystem; class ImageAttrHandler; // // ImageProxy is a proxy to an image having data type Float, Double, // Complex, or DComplex. Its primary purpose is to be bind the images // module to Python through pyrap.images. However, it can also be used // directly in C++. // // An ImageProxy object can be constructed for an image stored on disk in // Casacore, FITS, HDF5, or Miriad format. It can also be constructed given // a shape or an N-dim array with values. // Furthermore it can be constructed from a LEL expression (see class ImageExpr) // or a vector of images to be concatenated (see class ImageConcat). // // Many functions exist to operate on an ImageProxy object. For example: //
          //
        • get meta info (shape, data type, coordinates, etc.) //
        • save in Casacore, HDF5, or FITS format. //
        • regrid. //
        • get statistics. //
        • form a subimage (which is done in a virtual way). //
        // Functions regrid and statistics can only be used for Float images. // They throw an exception for images with other data types. // Note that using a LEL expression it is possible to (virtually) convert an // image with another type to a Float image. //
        class ImageProxy { public: // Default constructor is needed for Boost-Python. ImageProxy(); // Construct from the concatenation of the images along the axis. // The axis must be given as a Fortran-array axis. // All images must be of the same data type. ImageProxy (const Vector& names, Int axis); // Construct from a string that contains an image name or image expression. // It is opened using ImageOpener. ImageProxy (const String& name, const String& mask, const std::vector& images); // Construct from the concatenation of the image objects along the axis. // The axis must be given as a Fortran-array axis. // All images must be of the same data type. //# The dummy arguments are needed to let all constructors have a //# different number of arguments (for Boost-Python). ImageProxy (const std::vector& images, Int axis, Int dummy1=0, Int dummy2=0); // Construct from a ValueHolder containing an Array of Float or Complex. // If the name is empty it is created as a temporary image, otherwise // as a PagedImage or HDF5Image. // If the coordinates record is empty, default coordinates are used. // A mask is created if the mask name or mask value is not empty. ImageProxy (const ValueHolder& values, const ValueHolder& mask, const Record& coordinates, const String& imageName = String(), Bool overwrite = True, Bool asHDF5 = False, const String& maskName = String(), const IPosition& tileShape = IPosition()); // Construct from a shape. // If the name is empty it is created as a temporary image, otherwise // as a PagedImage or HDF5Image. // If the coordinates record is empty, default coordinates are used. // A mask is created if the mask name is not empty. ImageProxy (const IPosition& shape, const ValueHolder& value, const Record& coordinates, const String& imageName = String(), Bool overwrite = True, Bool asHDF5 = False, const String& maskName = String(), const IPosition& tileShape = IPosition(), Int dummy=0); // Construct from an existing image object. ImageProxy (const std::shared_ptr&); // Copy constructor (reference semantics). ImageProxy (const ImageProxy&); // Assignment (reference semantics). ImageProxy& operator= (const ImageProxy&); ~ImageProxy(); // Open the image (which can also be an expression). // It throws an exception if not succeeded. static LatticeBase* openImage (const String& name, const String& mask = String(), const std::vector& images = std::vector()); // Open an image in the file/table with the given name. // The specified mask will be applied (default is default mask). // A null pointer is returned for an unknown image type. // Non-Casacore image types must have been registered to be known. // If not successful, try to open it as an image expression. static LatticeBase* openImageOrExpr (const String& str, const MaskSpecifier&, const Block& nodes); // Close the image by setting all pointers to 0. void close(); // Turn the ImageProxy into a LatticeExprNode. LatticeExprNode makeNode() const; // Is the image persistent or temporary. Bool isPersistent() const; // Get the name of the image. String name (Bool stripPath=False) const; // Get the shape of the image. IPosition shape() const; // Get the dimensionality of the image. uInt ndim() const; // Get the size of the image (nr of pixels). uInt size() const; // Get the data type of the image. String dataType() const; DataType type() const; // Get the image type (PagedImage, HDF5Image, etc.) String imageType() const; // Get a chunk of data. ValueHolder getData (const IPosition& blc, const IPosition& trc, const IPosition& inc); // Get a chunk of the mask. ValueHolder getMask (const IPosition& blc, const IPosition& trc, const IPosition& inc); // Put a chunk of data. void putData (const ValueHolder&, const IPosition& blc, const IPosition& inc); // Put a chunk of the mask. // The mask will be created if not present yet. // That will not be done if the entire mask is True. void putMask (const ValueHolder& value, const IPosition& blc, const IPosition& inc); // Does the image have a read or write lock? Bool hasLock (Bool writeLock = False); // Try to acquire a read or write lock. // nattempts=0 means wait until acquired. Otherwise every second an // attempt is done. void lock (Bool writeLock=False, Int nattempts=0); // Release the lock acquired by lock(). void unlock(); // Get the names of the attribute groups. Vector attrGroupNames() const; // Create a new attribute group. void createAttrGroup (const String& groupName); // Get the names of all attributes in a group. Vector attrNames (const String& groupName) const; // Get the number of rows in an attribute group. uInt attrNrows (const String& groupName) const; // Get the value of an attribute in a group row. ValueHolder getAttr (const String& groupName, const String& attrName, uInt rownr) const; // Get all attributes in a group row. Record getAttrRow (const String& groupName, uInt rownr) const; // Get the unit(s) of an attribute in a group. Vector getAttrUnit(const String& groupName, const String& attrName) const; // Get the measinfo of an attribute in a group. Vector getAttrMeas(const String& groupName, const String& attrName) const; // Put the value, unit, and measinfo of an attribute in a group row. // The attribute or row is added if new. void putAttr (const String& groupName, const String& attrName, uInt rownr, const ValueHolder& value, const Vector& units, const Vector& measInfo); // Form a new (virtual) image being a subset of the image. // It uses preserveAxesOrder=False. ImageProxy subImage (const IPosition& blc, const IPosition& trc, const IPosition& inc, Bool dropDegenerate=True); // Same with a new function name for backward compatibility with old pyrap. ImageProxy subImage2 (const IPosition& blc, const IPosition& trc, const IPosition& inc, Bool dropDegenerate, Bool preserveAxesOrder); // Get the brightness unit. String unit() const; // Get the coordinate system. Record coordSys() const; const CoordinateSystem& coordSysObject() const; // Convert a pixel coordinate to world coordinate. // if reverseAxes=True the input and output vector will be // reversed (as needed for pyrap). Vector toWorld (const Vector& pixel, Bool reverseAxes); // Convert a world coordinate to pixel coordinate. // if reverseAxes=True the input and output vector will be // reversed (as needed for pyrap). Vector toPixel (const Vector& world, Bool reverseAxes); // Get the image info. Record imageInfo() const; const ImageInfo& imageInfoObject() const; // Get the miscellaneous info. Record miscInfo() const; // Get the history. Vector history() const; // Write the image in FITS format. // See class ImageFITSConverter for a description of the arguments. // Currently only a float image can be written to FITS. void toFits (const String& fitsfile, Bool overwrite=True, Bool velocity=True, Bool optical=True, Int bitpix=-32, Double minpix=1, Double maxpix=-1) const; // Write the image to an image file with the given name. // An exception is thrown if the name is the name of an already open image. void saveAs (const String& fileName, Bool overwrite=True, Bool hdf5=False, Bool copyMask=True, const String& newMaskName=String(), const IPosition& newTileShape=IPosition()) const; // Return the statistics for the given axes. // E.g., if axes 0,1 is given in a 3-dim image, the statistics are // calculated for each plane along the 3rd axis. // MinMaxValues can be given to include or exclude (4th argument) pixels // with values in the given range. If only one value is given, min=-abs(val) // and max=abs(val). // Robust statistics (Median, MedAbsDevMed, and Quartile) can be returned // too. Record statistics (const Vector& axes, const String& mask, const ValueHolder& minMaxValues, Bool exclude = False, Bool robust = False) const; // Regrid the image on the given axes to the given coordinate system. // The output is stored in the given file; it no file name is given a // temporary image is made. // If the output shape is empty, the old shape is used. // replicate=True means replication rather than regridding. ImageProxy regrid (const Vector& axes = Vector(), const String& outfile = String(), Bool overwriteOutFile = True, const IPosition& outShape = IPosition(), const Record& coordSys = Record(), const String& method = "linear", Int decimate = 10, Bool replicate = False, Bool doRefChange = True, Bool forceRegrid = False); // Check and adjust blc, trc, or inc using the shape. // static IPosition adjustBlc (const IPosition& blc, const IPosition& shp); static IPosition adjustTrc (const IPosition& trc, const IPosition& shp); static IPosition adjustInc (const IPosition& inc, const IPosition& shp); // /* ImageProxy rotate(const String& outfile, const IPosition& shape, const Quantity& pa, Record& region, const String& mask, const String& method = "cubic", Int decimate = 0, Bool replicate = False, Bool dropdeg = False, Bool overwrite = False); Bool setbrightnessunit (const String& unit); Bool setcoordsys (const Record& csys); Bool sethistory (const String& origin, const Vector& history); Bool setmiscinfo (const Record& info); ImageProxy subimage(const String& outfile, Record& region, const String& mask, Bool dropdeg = False, Bool overwrite = False, Bool list = True); Vector topixel(Record& value); */ LatticeBase* getLattice() const { return itsLattice.operator->(); } private: // Form an ImageProxy object from an existing image object. explicit ImageProxy (LatticeBase*); // Throw an exception if the object is null. void checkNull() const; // Make an image from an array or shape. template void makeImage (const Array& array, const Array& mask, const IPosition& shape, const Record& coordinates, const String& fileName, Bool asHDF5, const String& maskName, const IPosition& tileShape); // Form a concatenated image. // void concatImages (const std::vector& images, Int axis); void concatImagesFloat (const std::vector& images, Int axis); void concatImagesDouble (const std::vector& images, Int axis); void concatImagesComplex (const std::vector& images, Int axis); void concatImagesDComplex (const std::vector& images, Int axis); // // Setup the pointers for the various image data types. void setup(); // Setup the pointers for the various image data types. // It takes over the lattice pointer. void setup (LatticeBase* lattice); // Centre all axes except the Stokes one. void centreRefPix (CoordinateSystem& cSys, const IPosition& shape) const; // Put the mask and create it if needed. template void doPutMask (ImageInterface& image, const ValueHolder& value, const IPosition& blc, const IPosition& inc); // Copy the image to an image (PagedImage or HDF5Image) with the given name. // A new tile shape can be given. // If the image is masked, the mask can be copied as well. template void saveImage (const String& fileName, Bool hdf5, Bool copyMask, const String& newMaskName, const IPosition& newTileShape, const ImageInterface& image) const; // Form a tiled shape from the current shape and a possible new tile shape. TiledShape makeTiledShape (const IPosition& newTileShape, const IPosition& shape, const IPosition& oldTileShape=IPosition()) const; // Calculate the statistics. template Record makeStatistics (const ImageInterface& image, const Vector& axes, const String& mask, const ValueHolder& minMaxValues, Bool exclude, Bool robust) const; // Do the actual regridding. template ImageProxy doRegrid (const ImageInterface& image, const Vector& axes, const String& outfile, const IPosition& shape, const Record& coordSys, const String& method, Int decimate, Bool replicate, Bool doRefChange, Bool force); // Make a coordinate system from the Record. // The cylindrical fix is applied if needed. CoordinateSystem makeCoordinateSystem (const Record& coordinates, const IPosition& shape) const; //# Data members. //# itsLattice is the real data; the pointers are for type convenience only. std::shared_ptr itsLattice; ImageInterface* itsImageFloat; //# reference, so no delete ImageInterface* itsImageDouble; //# reference, so no delete ImageInterface* itsImageComplex; //# reference, so no delete ImageInterface* itsImageDComplex; //# reference, so no delete const CoordinateSystem* itsCoordSys; //# reference, so no delete ImageAttrHandler* itsAttrHandler; //# reference, so no delete }; } // end namespace casacore #endif casacore-3.7.1/images/Images/ImageRegrid.h000066400000000000000000000411361476623553700203330ustar00rootroot00000000000000//# ImageRegrid.h: Regrid Images //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_IMAGEREGRID_H #define IMAGES_IMAGEREGRID_H #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template class MaskedLattice; template class ImageInterface; template class Lattice; template class LatticeIterator; class CoordinateSystem; class DirectionCoordinate; class Coordinate; class ObsInfo; class IPosition; class Unit; class ProgressMeter; // This regrids one image to match the coordinate system of another // // // // //
      • ImageInterface //
      • CoordinateSystem //
      • Interpolate2D //
      • InterpolateArray1D // // // // Regrids, or resamples, images. // // // // This class enables you to regrid one image to the coordinate // system of another. You can regrid any or all of the // axes in the image. A range of interpolation schemes are available. // // It will cope with coordinate systems being in different orders // (coordinate, world axes, pixel axes). The basic approach is to // make a mapping from the input to the output coordinate systems, // but the output CoordinateSystem order is preserved in the output // image. // // Any DirectionCoordinate or LinearCoordinate holding exactly two axes // is regridded in one pass with a 2-D interpolation scheme. // All other axes are regridded in separate passes with a 1D interpolation // scheme. This means that a LinearCoordinate holding say 3 axes // where some of them are coupled will not be correctly regridded. // StokesCoordinates cannot be regridded. // // Multiple passes are made through the data, and the output of // each pass is the input of the next pass. The intermediate // images are stored as TempImages which may be in memory or // on disk, depending on their size. // // It can also simply insert this image into that one via // an integer shift. // // // // // // // // // // A common image analysis need is to regrid images, e.g. to compare // images from different telescopes. // // // //
      • AipsError // // // // template class ImageRegrid { public: // Default constructor ImageRegrid(); // copy constructor (copy semantics) ImageRegrid(const ImageRegrid &other); // destructor ~ImageRegrid(); // Assignment copy semantics) ImageRegrid& operator=(const ImageRegrid& other); // Regrid inImage onto the grid specified by outImage. // If outImage has a writable mask, it will be updated in that // output pixels at which the regridding failed will be masked bad (False) // and the pixel value set to zero. Otherwise the output mask is not changed. // Specify which pixel axes of outImage are to be // regridded. The coordinate and axis order of outImage // is preserved, regardless of where the relevant coordinates // are in inImage. // // decimate only applies when replicate=False. it is // the coordinate grid computation decimation FACTOR // (e.g. nCoordGrid ~ nIn / decimate). 0 means no decimation // (slowest and most accurate) void regrid(ImageInterface& outImage, typename Interpolate2D::Method method, const IPosition& whichOutPixelAxes, const ImageInterface& inImage, Bool replicate=False, uInt decimate=0, Bool showProgress=False, Bool forceRegrid=False, Bool verbose=False); // Get and set the 2-D coordinate grid. After a call to function regrid // in which coupled 2D coordinate (presently only DirectionCoordinate) is // regridded, this coordinate grid will be available. It can be reused // via the set2DCoordinateGrid function for another like plane // (e.g. if you choose to regrid planes of a cube separately). When you provide // the coordinate grid, it will no longer (for that 2D coordinate only) be // computed internally, which may save a lot of time. Ordinarily, if you // regridded many planes of a cube in one call to regrid, the coordinate grid // is cached for you. To trigger successive calls to regrid to go back to // internal computation, set zero length Cube and Matrix. gridMask // is True for successfull coordinate conversions, and False otherwise. // void get2DCoordinateGrid (Cube& grid, Matrix& gridMask) const; void set2DCoordinateGrid (const Cube& grid, const Matrix& gridMask, Bool notify=False); // // // Inserts inImage into outImage. The alignment is done by // placing the blc of inImage at the specified // absolute pixel of the outImage (outPixelLocation). If // the outPixelLocation vector is of zero length, then the images // are aligned by their reference pixels. Only integral shifts are done // in the aligment process. If outImage has a mask, it will be updated. // Returns False if no overlap of images, in which case the // output is not updated. Bool insert(ImageInterface& outImage, const Vector& outPixelLocation, const ImageInterface& inImage); // Print out useful debugging information (level 0 is none, // 1 is some, 2 is too much) void showDebugInfo(Int level=0) {itsShowLevel = level;}; // Enable/disable Measures Reference conversions void disableReferenceConversions(Bool disable=True) {itsDisableConversions = disable;}; // Helper function. We are regridding from cSysFrom to cSysTo for the // specified pixel axes of cSyFrom. This function returns a CoordinateSystem which, // for the pixel axes being regridded, copies the coordinates from cSysTo // (if coordinate type present in cSysTo) or cSysFrom (coordinate // type not present in cSysTo). // For the axes not being regridded, it copies the coordinates from // cSysFrom. This helps you build the cSys for function regrid. // The ObsInfo from cSysFrom is copied to the output CoordinateSystem. // If inShape has one or more elements it represenents the size of the // image to be regridded. It this must have the same number of elements // as the number of pixel axes in cSysFrom. If any of the values // are unity (ie the axes are degenerate), and the corresponding axis in csysFrom is the only // axis in its corresponding coordinate, this coordinate will not be replaced // even if the axis is specified in axes. // Upon return, coordsToBeRegridded will contain a list of the coordinates that will // be regridded. static CoordinateSystem makeCoordinateSystem( LogIO& os, std::set& coordsToBeRegridded, const CoordinateSystem& cSysTo, const CoordinateSystem& cSysFrom, const IPosition& axes, const IPosition& inShape=IPosition(), Bool giveStokesWarning=True ); private: Int itsShowLevel; Bool itsDisableConversions; // Cube its2DCoordinateGrid; Matrix its2DCoordinateGridMask; // Cube itsUser2DCoordinateGrid; Matrix itsUser2DCoordinateGridMask; Bool itsNotify; // // Check shape and axes. Exception if no good. If pixelAxes // of length 0, set to all axes according to shape void _checkAxes(IPosition& outPixelAxes, const IPosition& inShape, const IPosition& outShape, const Vector& pixelAxisMap, const CoordinateSystem& outCoords, Bool verbose); // Find maps between coordinate systems void findMaps (uInt nDim, Vector& pixelAxisMap1, Vector& pixelAxisMap2, const CoordinateSystem& inCoords, const CoordinateSystem& outCoords) const; // Find scale factor to conserve flux Double findScaleFactor(const Unit& units, const CoordinateSystem& inCoords, const CoordinateSystem& outCoords, Int inCoordinate, Int outCoordinate, LogIO& os) const; // Regrid one Coordinate void _regridOneCoordinate (LogIO& os, IPosition& outShape2, Vector& doneOutPixelAxes, MaskedLattice* &finalOutPtr, MaskedLattice* &inPtr, MaskedLattice* &outPtr, CoordinateSystem& outCoords, const CoordinateSystem& inCoords, Int outPixelAxis, const ImageInterface& inImage, const IPosition& outShape, Bool replicate, uInt decimate, Bool outIsMasked, Bool showProgress, Bool forceRegrid, typename Interpolate2D::Method method, Bool verbose); // Regrid DirectionCoordinate or 2-axis LinearCoordinate void regridTwoAxisCoordinate (LogIO& os, MaskedLattice& outLattice, const MaskedLattice& inLattice, const Unit& imageUnit, const CoordinateSystem& inCoords, const CoordinateSystem& outCoords, Int inCoordinate, Int outCoordinate, const Vector inPixelAxes, const Vector outPixelAxes, const Vector pixelAxisMap1, const Vector pixelAxisMap2, typename Interpolate2D::Method method, Bool replicate, uInt decimate, Bool showProgress); // Make regridding coordinate grid for this cursor. void make2DCoordinateGrid (LogIO& os, Bool& allFail, Bool&missedIt, Double& minInX, Double& minInY, Double& maxInX, Double& maxInY, Cube& in2DPos, Matrix& succeed, const CoordinateSystem& inCoords, const CoordinateSystem& outCoords, Int inCoordinate, Int outCoordinate, uInt xInAxis, uInt yInAxis, uInt xOutAxis, uInt yOutAxis, const IPosition& inPixelAxes, const IPosition& outPixelAxes, const IPosition& inShape, const IPosition& outPos, const IPosition& cursorShape, uInt decimate=0); // Make replication coordinate grid for this cursor void make2DCoordinateGrid (Cube& in2DPos, Double& minInX, Double& minInY, Double& maxInX, Double& maxInY, const Vector& pixelScale, uInt xInAxis, uInt yInAxis, uInt xOutAxis, uInt yOutAxis, uInt xInCorrAxis, uInt yInCorrAxis, uInt xOutCorrAxis, uInt yOutCorrAxis, const IPosition& outPos, const IPosition& cursorShape); // Make regridding coordinate grid for this axis void make1DCoordinateGrid (Block::BaseType>& xOut, Vector& failed, Bool& allFailed, Bool& allGood, const Coordinate& inCoord, const Coordinate& outCoord, Int inAxisInCoordinate, Int outAxisInCoordinate, MFrequency::Convert& machine, Bool useMachine); // Make replication coordinate grid for this axis void make1DCoordinateGrid (Block::BaseType>& xOut, typename NumericTraits::BaseType pixelScale) const; // Regrid 1 axis void regrid1D (MaskedLattice& outLattice, const MaskedLattice& inLattice, const Coordinate& inCoord, const Coordinate& outCoord, const Vector& inPixelAxes, const Vector& outPixelAxes, Int inAxisInCoordinate, Int outAxisInCoordinate, const Vector pixelAxisMap, typename Interpolate2D::Method method, MFrequency::Convert& machine, Bool replicate, Bool useMachine, Bool showProgress); // void regrid2DMatrix(Lattice& outCursor, LatticeIterator*& outMaskIterPtr, const Interpolate2D& interp, ProgressMeter*& pProgress, Double& iPix, uInt nDim, uInt xInAxis, uInt yInAxis, uInt xOutAxis, uInt yOutAxis, Double scale, Bool inIsMasked, Bool outIsMasked, const IPosition& outPos, const IPosition& outCursorShape, const IPosition& inChunkShape, const IPosition& inChunkBlc, const IPosition& pixelAxisMap2, Array& inDataChunk, Array*& inMaskChunkPtr, const Cube& pix2DPos, const Matrix& succeed); void findXYExtent (Bool& missedIt, Bool& allFailed, Double& minInX, Double& minInY, Double& maxInX, Double& maxInY, Cube& in2DPos, const Matrix& succeed, uInt xInAxis, uInt yInAxis, uInt xOutAxis, uInt yOutAxis, const IPosition& outPos, const IPosition& outCursorShape, const IPosition& inShape); // Bool minmax(Double &minX, Double &maxX, Double& minY, Double& maxY, const Array &xData, const Array &yData, const Array& mask); }; //# Declare extern templates for often used types. extern template class ImageRegrid; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/images/Images/ImageRegrid.tcc000066400000000000000000002301141476623553700206510ustar00rootroot00000000000000//# ImageRegrid.cc: Regrids images //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_IMAGEREGRID_TCC #define IMAGES_IMAGEREGRID_TCC #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template ImageRegrid::ImageRegrid() : itsShowLevel(0), itsDisableConversions(False), itsNotify(False) {;} template ImageRegrid::ImageRegrid(const ImageRegrid& other) : itsShowLevel(other.itsShowLevel), itsDisableConversions(other.itsDisableConversions), itsNotify(other.itsNotify) {;} template ImageRegrid::~ImageRegrid() {;} template ImageRegrid& ImageRegrid::operator=(const ImageRegrid& other) { if (this != &other) { itsShowLevel = other.itsShowLevel; itsDisableConversions = other.itsDisableConversions; itsNotify = other.itsNotify; } return *this; } template void ImageRegrid::regrid( ImageInterface& outImage, typename Interpolate2D::Method method, const IPosition& outPixelAxesU, const ImageInterface& inImage, Bool replicate, uInt decimate, Bool showProgress, Bool forceRegrid, Bool verbose ) { LogIO os(LogOrigin("ImageRegrid", __func__, WHERE)); Timer t0; IPosition outShape = outImage.shape(); IPosition inShape = inImage.shape(); const uInt nDim = inImage.ndim(); ThrowIf( nDim != outImage.ndim(), "The input and output images must have the same " "number of axes" ); if (inImage.imageInfo().hasMultipleBeams()) { ThrowIf( inImage.coordinates().hasSpectralAxis() && anyTrue( outPixelAxesU.asVector() == inImage.coordinates().spectralAxisNumber() ), "This image has multiple beams. The spectral axis cannot be regridded" ); ThrowIf( inImage.coordinates().hasPolarizationCoordinate() && anyTrue( outPixelAxesU.asVector() == inImage.coordinates().polarizationAxisNumber() ), "This image has multiple beams. The polarization axis cannot be regridded" ); } const Bool outIsMasked = outImage.isMasked() && outImage.hasPixelMask() && outImage.pixelMask().isWritable(); const CoordinateSystem& inCoords = inImage.coordinates(); CoordinateSystem outCoords = outImage.coordinates(); IPosition outPixelAxes = outPixelAxesU; // Find world and pixel axis maps Vector pixelAxisMap1, pixelAxisMap2; findMaps (nDim, pixelAxisMap1, pixelAxisMap2, inCoords, outCoords); // Check user pixel axes specifications _checkAxes(outPixelAxes, inShape, outShape, pixelAxisMap1, outCoords, verbose); { // warn if necessary, CAS-5110 Vector dirAxes = outCoords.directionAxesNumbers(); Bool regridDirAxis = False; for (uInt i=0; i inc = dc.increment(); Vector units = dc.worldAxisUnits(); Quantity inpix = min(Quantity(inc[0], units[0]), Quantity(inc[1], units[1])); Quantity inbeam = info.hasSingleBeam() ? info.restoringBeam().getMinor() : info.getBeamSet().getSmallestMinorAxisBeam().getMinor(); const DirectionCoordinate dcout = outImage.coordinates().directionCoordinate(); inc = dcout.increment(); units = dcout.worldAxisUnits(); Quantity outpix = min(Quantity(inc[0], units[0]), Quantity(inc[1], units[1])); if ( ( method == Interpolate2D::NEAREST && inbeam/inpix < Quantity(5, "") && outpix/inpix > Quantity(0.5, "") ) || ( method == Interpolate2D::LINEAR && inbeam/inpix < Quantity(3, "") && outpix/inpix > Quantity(0.75, "") ) || ( method == Interpolate2D::CUBIC && inbeam/inpix < Quantity(3, "") && outpix/inpix > Quantity(1, "") ) ) { LogIO log; log << LogOrigin("ImageRegrid", __func__) << LogIO::WARN << "You are regridding an image whose beam is not well sampled by the " << "pixel size. Total flux can be lost when regridding such " << "images, especially when the new pixel size is larger than " << "the old pixel size. It is recommended to check the total " << "flux of your input and output image, and if necessary " << "rebin the input to have smaller pixels." << LogIO::POST; } } } const uInt nOutRegridPixelAxes = outPixelAxes.nelements(); if (itsShowLevel>0) { cerr << "outPixelAxes = " << outPixelAxes << endl; } // Set output shape. This shape is incremental, for each regridding // pass it incrementally changes from the input shape to the output shape // We account here for different pixel axis orders IPosition outShape2(nDim); for (uInt paOut=0; paOut* inPtr = 0; CoordinateSystem inCoords2(inCoords); MaskedLattice* outPtr = 0; MaskedLattice* finalOutPtr = &outImage; Vector doneOutPixelAxes(outCoords.nPixelAxes(), True); for (uInt i=0; i0) { cerr << "Function regrid took " << t0.all() << endl; } } template void ImageRegrid::_regridOneCoordinate (LogIO& os, IPosition& outShape2, Vector& doneOutPixelAxes, MaskedLattice* &finalOutPtr, MaskedLattice* &inPtr, MaskedLattice* &outPtr, CoordinateSystem& outCoords, const CoordinateSystem& inCoords, Int outPixelAxis, const ImageInterface& inImage, const IPosition& outShape, Bool replicate, uInt decimate, Bool outIsMasked, Bool showProgress, Bool forceRegrid, typename Interpolate2D::Method method, Bool verbose) { Timer t0; Double s0 = 0.0; // Find world and pixel axis maps Vector pixelAxisMap1, pixelAxisMap2; findMaps (inImage.ndim(), pixelAxisMap1, pixelAxisMap2, inCoords, outCoords); // Find equivalent world axis Int outWorldAxis = outCoords.pixelAxisToWorldAxis(outPixelAxis); Int outCoordinate, outAxisInCoordinate; Int inCoordinate, inAxisInCoordinate; outCoords.findPixelAxis(outCoordinate, outAxisInCoordinate, outPixelAxis); Coordinate::Type type = outCoords.type(outCoordinate); // Find Coordinate in input image. Int inPixelAxis = pixelAxisMap1[outPixelAxis]; Int inWorldAxis = inCoords.pixelAxisToWorldAxis(inPixelAxis); inCoords.findPixelAxis(inCoordinate, inAxisInCoordinate, inPixelAxis); if (inCoordinate==-1 || inAxisInCoordinate==-1) { ostringstream oss1; ostringstream oss2; oss1 << outCoords.showType(outCoordinate); oss2 << outPixelAxis+1; String msg = String("Output axis (") + String(oss2) + String(") of coordinate type ") + String(oss1) + String("does not have a coordinate in the input " "CoordinateSystem"); ThrowCc(msg); } // Where are the input and output pixel axes for this coordinate ? // DIrectionCoordinates and LinearCoordinates (holding two axes) are // done in one pass. Others are done axis by axis which is flawed // when those axes are coupled (e.g. other axes of LC) Vector outPixelAxes = outCoords.pixelAxes(outCoordinate); Vector inPixelAxes = inCoords.pixelAxes(inCoordinate); Int maxMemoryInMB = 0; // Work out if we can do a 2-D regrid. Either a DC or a 2-axis LC // (maybe extend to two axes of N-axis LC in the future) if ( (type==Coordinate::DIRECTION) || (type==Coordinate::LINEAR && outPixelAxes.nelements()==2 && inPixelAxes.nelements()==2) ) { // We will do two pixel axes in this pass doneOutPixelAxes(outPixelAxes[0]) = True; doneOutPixelAxes(outPixelAxes[1]) = True; // Update the incremental output image shape. outShape2(outPixelAxes[0]) = outShape(outPixelAxes[0]); outShape2(outPixelAxes[1]) = outShape(outPixelAxes[1]); ThrowIf( outShape2(outPixelAxes[0])==1 && outShape2(outPixelAxes[1])==1, "You cannot regrid the Coordinate as it is " "of shape [1,1]" ); const IPosition inShape = inPtr->shape(); Bool shapeDiff = (outShape2(outPixelAxes[0]) != inShape(inPixelAxes(0))) || (outShape2(outPixelAxes[1]) != inShape(inPixelAxes(1))); // See if we really need to regrid this axis. If the coordinates and shape are the // same there is nothing to do apart from swap in and out pointers // or copy on last pass const Coordinate& cIn = inCoords.coordinate(inCoordinate); const Coordinate& cOut = outCoords.coordinate(outCoordinate); Bool regridIt = shapeDiff || forceRegrid || !(cIn.near(cOut)); Bool lastPass = allEQ(doneOutPixelAxes, True); // if (!regridIt) { if (verbose) { os << "Input and output shape/coordinate information for " << Coordinate::typeToString(cIn.type()) << " axes equal - no regridding needed" << LogIO::POST; } if (lastPass) { // Can't avoid this copy LatticeUtilities::copyDataAndMask (os, *finalOutPtr, *inPtr); } else { outPtr = inPtr; inPtr = 0; } return; } // Deal with pointers if (lastPass) { outPtr = finalOutPtr; } else { outPtr = new TempImage(TiledShape(outShape2), outCoords, maxMemoryInMB); if (outIsMasked) { String maskName("mask0"); TempImage* tmpPtr = dynamic_cast*>(outPtr); tmpPtr->makeMask(maskName, True, True, False); } } // regridTwoAxisCoordinate (os, *outPtr, *inPtr, inImage.units(), inCoords, outCoords, inCoordinate, outCoordinate, inPixelAxes, outPixelAxes, pixelAxisMap1, pixelAxisMap2, method, replicate, decimate, showProgress); } else { // Note that will do one pixel axis in this pass doneOutPixelAxes(outPixelAxes(outAxisInCoordinate)) = True; // Update the incremental output image shape. outShape2(outPixelAxes(outAxisInCoordinate)) = outShape(outPixelAxes(outAxisInCoordinate)); const IPosition inShape = inPtr->shape(); Bool shapeDiff = (outShape2(outPixelAxes(0)) != inShape(inPixelAxes(0))); // Get Coordinates. Set world axis units for input and output // coordinates for this pixel // axis to be the same. We can only do this via the // CoordinateSystem (or casting) // or by breaking polymorphism like we did for DirectionCoordinate. // Ho hum. Vector inUnits = inCoords.worldAxisUnits(); Vector outUnits = outCoords.worldAxisUnits(); outUnits(outWorldAxis) = inUnits(inWorldAxis); ThrowIf( !outCoords.setWorldAxisUnits(outUnits), "Failed to set output CoordinateSystem units" ); const Coordinate& inCoord = inCoords.coordinate(inCoordinate); const Coordinate& outCoord = outCoords.coordinate(outCoordinate); // See if we really need to regrid this axis. // If the coordinates are the same // there is nothing to do apart from swap in and out pointers or // copy on last pass IPosition t(1, outAxisInCoordinate); IPosition excludeAxes = IPosition::otherAxes(outCoord.nPixelAxes(), t); Bool regridIt = shapeDiff || forceRegrid || !(inCoord.near(outCoord, excludeAxes.asVector())); Bool lastPass = allEQ(doneOutPixelAxes, True); // if (!regridIt) { if (verbose) { os << "Input and output shape/coordinate information for " << Coordinate::typeToString(inCoord.type()) << " axis equal - no regridding needed" << LogIO::POST; } if (lastPass) { // Can't avoid this copy LatticeUtilities::copyDataAndMask (os, *finalOutPtr, *inPtr); } else { outPtr = inPtr; inPtr = 0; } return; } // Deal with pointers if (lastPass) { outPtr = finalOutPtr; } else { outPtr = new TempImage(TiledShape(outShape2), outCoords, maxMemoryInMB); if (outIsMasked) { String maskName("mask0"); TempImage* tmpPtr = dynamic_cast*>(outPtr); tmpPtr->makeMask(maskName, True, True, True, True); } } // Possibly make Frequency reference conversion machine. // We could use the internal // machine layer inside the SpectralCoordinate, but making // the machine explicitly // this way is more general because it allows the ObsInfos // and SpectralCoordinates // to be different. Bool madeIt = False; MFrequency::Convert machine; if (!itsDisableConversions && type==Coordinate::SPECTRAL) { madeIt = CoordinateUtil::makeFrequencyMachine(os, machine, inCoordinate, outCoordinate, inCoords, outCoords); } // Regrid if (itsShowLevel>0) { cerr << "usemachine=" << madeIt << endl; } regrid1D (*outPtr, *inPtr, inCoord, outCoord, inPixelAxes, outPixelAxes, inAxisInCoordinate, outAxisInCoordinate, pixelAxisMap2, method, machine, replicate, madeIt, showProgress); } // if (itsShowLevel > 0) { s0 += t0.all(); cerr << " Function regridOneCoordinate took " << s0 << endl; } } template Bool ImageRegrid::insert (ImageInterface& outImage, const Vector& outPixel, const ImageInterface& inImage) { LogIO os(LogOrigin("ImageRegrid", "insert(...)", WHERE)); // ThrowIf( outImage.ndim()!=inImage.ndim(), "The input and output images must have the same number of dimensions" ); // const CoordinateSystem& inCoords = inImage.coordinates(); const CoordinateSystem& outCoords = outImage.coordinates(); const uInt nPixelAxes = inCoords.nPixelAxes(); AlwaysAssert(outImage.shape().nelements()==nPixelAxes,AipsError); // Bool locateByRefPix = outPixel.nelements()==0; if (!locateByRefPix) { AlwaysAssert(outPixel.nelements()==nPixelAxes,AipsError); } // const IPosition& inShape = inImage.shape(); const IPosition& outShape = outImage.shape(); // const Vector& inRefPix = inCoords.referencePixel(); const Vector& outRefPix = outCoords.referencePixel(); // Where are the output blc/trc after placing the input image // No trimming yet IPosition outBlc(nPixelAxes), outTrc(nPixelAxes); IPosition inBlc(nPixelAxes), inTrc(nPixelAxes); Int coordinate, axisInCoordinate; for (uInt i=0; i(outRefPix(i) - inRefPix(i) + 0.5); } else { outBlc(i) = static_cast(outPixel(i) + 0.5); } outTrc(i) = outBlc(i) + inShape(i) - 1; // inBlc(i) = 0; inTrc(i) = inShape(i) - 1; } if (itsShowLevel>0) { cerr << "inBlc, inTrc = " << inBlc << inTrc << endl; cerr << "outBlc, outTrc = " << outBlc << outTrc << endl; } // Does the input miss the output entirely ? Bool missedIt = True; for (uInt i=0; i=0 && outTrc(i)=0 && outTrc(i)=0 && outBlc(i)0) { cerr << "missedIt = " << missedIt << endl; } if (missedIt) return False; // Now trim blc/trc for (uInt i=0; i0) { cerr << "After trimming " << endl; cerr << "inBlc, inTrc = " << inBlc << inTrc << endl; cerr << "outBlc, outTrc = " << outBlc << outTrc << endl; } // Copy the relevant portion Slicer inBox(inBlc, inTrc, Slicer::endIsLast); Slicer outBox(outBlc, outTrc, Slicer::endIsLast); SubImage inSub(inImage, inBox); SubImage outSub(outImage, outBox, True); // const Bool outIsMasked = outImage.isMasked() && outImage.hasPixelMask() && outImage.pixelMask().isWritable(); if (outIsMasked) { LatticeUtilities::copyDataAndMask(os, outSub, inSub); } else { outSub.copyData(inSub); } // return True; } template CoordinateSystem ImageRegrid::makeCoordinateSystem( LogIO& os, std::set& coordsToBeRegridded, const CoordinateSystem& cSysTo, const CoordinateSystem& cSysFrom, const IPosition& outPixelAxes, const IPosition& inShape, Bool giveStokesWarning ) { coordsToBeRegridded.clear(); os << LogOrigin("ImageRegrid", __func__, WHERE); const uInt nCoordsFrom = cSysFrom.nCoordinates(); const uInt nPixelAxesFrom = cSysFrom.nPixelAxes(); ThrowIf( inShape.nelements() > 0 && inShape.nelements() != nPixelAxesFrom, "Inconsistent size and csysFrom" ); // Create output CS. Copy the output ObsInfo over first. CoordinateSystem cSysOut(cSysFrom); // If specified axes are empty, set to all IPosition outPixelAxes2 = outPixelAxes.nelements() == 0 ? IPosition::makeAxisPath(nPixelAxesFrom) : outPixelAxes; // Loop over coordinates in the From CS for (uInt i=0; i 0 && giveStokesWarning) { os << LogIO::WARN << "A stokes coordinate cannot be regridded, ignoring" << LogIO::POST; } continue; } Vector pixelAxes = cSysFrom.pixelAxes(i); for (uInt k=0; k 0 && inShape[pixelAxes[k]] > 1) ) { for (uInt j=0; j void ImageRegrid::regridTwoAxisCoordinate ( LogIO& os, MaskedLattice& outLattice, const MaskedLattice& inLattice, const Unit& imageUnit, const CoordinateSystem& inCoords, const CoordinateSystem& outCoords, Int inCoordinate, Int outCoordinate, const Vector inPixelAxes, const Vector outPixelAxes, const Vector pixelAxisMap1, const Vector pixelAxisMap2, typename Interpolate2D::Method method, Bool replicate, uInt decimate, Bool showProgress) { // Compute output coordinate, find region around this coordinate // in input, interpolate. Any output mask is overwritten Timer t0, t1, t2, t3, t4; Double s0 = 0.0; Double s1 = 0.0; Double s2 = 0.0; Double s3 = 0.0; Double s4 = 0.0; AlwaysAssert(inPixelAxes.nelements()==2, AipsError); AlwaysAssert(outPixelAxes.nelements()==2, AipsError); Bool inIsMasked = inLattice.isMasked(); Bool outIsMasked = outLattice.isMasked() && outLattice.hasPixelMask() && outLattice.pixelMask().isWritable(); const IPosition inShape = inLattice.shape(); const IPosition outShape = outLattice.shape(); const uInt nDim = inLattice.ndim(); if (itsShowLevel>0) { cerr << "Replicate = " << replicate << endl; cerr << "inPixelAxes = " << inPixelAxes << endl; cerr << "outPixelAxes = " << outPixelAxes << endl; cerr << "inIsMasked " << inIsMasked << endl; cerr << "outIsMasked " << outIsMasked << endl; } if (itsShowLevel>0) { if (method==Interpolate2D::NEAREST) { cerr << "Method is nearest" << endl; } else if (method==Interpolate2D::LINEAR) { cerr << "Method is linear" << endl; } else if (method==Interpolate2D::CUBIC) { cerr << "Method is cubic" << endl; } } // We iterate through the output image by tile. We iterate through // each tile by matrix holding the Direction Coordinate axes (in // some order). We have a matrix(i=1:nrows,j=1:ncols). // // pixelAxisMap1(i) says where pixel axis i in the output image is // in the input image // pixelAxisMap2(i) says where pixel axis i in the // input image is in the output image // pixelAxes(0) says where Lon is in image (in or out) // pixelAxes(1) says where Lat is in image (in or out) // xOutAxis is the first direction axis in the output image // (associated with i) // yOutAxis is the second direction axis in the output image // (associated with j) // xInCorrAxis is the corresponding axis to xOutAxis in the input image // yInCorrAxis is the corresponding axis to yOutAxis in the input image // xInAxis is the first direction axis in the input image // yInAxis is the second direction axis in the input image // xOutCorrAxis is the corresponding axis to xInAxis in the output image // yOutCorrAxis is the corresponding axis to yInAxis in the output image // Example: // Regrid ra/dec axes with input order ra/dec/freq and output order // freq/dec/ra // // input image shape = [20, 30, 40] (ra/dec/freq) // output image shape = [40, 90, 60] (freq/dec/ra) - we are making ra/dec // shape 3x input // // outPixelAxes = [2,1] = [lon,lat] // The cursor matrix is of shape [nrow,ncol] = [90,60] // xOutAxis = 1 (dec) // yOutAxis = 2 (ra) // xInCorrAxis = 1 (dec) // yInCorrAxis = 0 (ra) // xInAxis = 0 (ra) // yInAxis = 1 (dec) // xOutCorrAxis = 2 (ra) // yOutCorrAxis = 1 (dec) const uInt xOutAxis = min(outPixelAxes(0), outPixelAxes(1)); const uInt yOutAxis = max(outPixelAxes(0), outPixelAxes(1)); uInt xInCorrAxis = pixelAxisMap1[xOutAxis]; uInt yInCorrAxis = pixelAxisMap1[yOutAxis]; const uInt xInAxis = min(inPixelAxes(0), inPixelAxes(1)); const uInt yInAxis = max(inPixelAxes(0), inPixelAxes(1)); uInt xOutCorrAxis = pixelAxisMap2[xInAxis]; uInt yOutCorrAxis = pixelAxisMap2[yInAxis]; // Make navigator and iterator for output data and mask. It is vital that // the "niceShape" is the same for both iterators. Because the mask and // lattice are both TempLattices, one might be on disk, one in core. // Hence we pick one nice shape and use it on both iterators, although // it may be suboptimal for one of the lattices. //IPosition niceShape = outLattice.niceCursorShape(); // Temporary fix for AIT/SIN regriding for full sky images // Hold a plane in memory IPosition niceShape=outLattice.shape(); niceShape=1; niceShape(xOutAxis)=outLattice.shape()(xOutAxis); niceShape(yOutAxis)=outLattice.shape()(yOutAxis); LatticeStepper outStepper(outShape, niceShape, LatticeStepper::RESIZE); LatticeIterator outIter(outLattice, outStepper); if (itsShowLevel>0) { cerr << "xOutAxis, yOutAxis = " << xOutAxis << ", " << yOutAxis << endl; cerr << "xInCorrAxis, yInCoorrAxis = " << xInCorrAxis << ", " << yInCorrAxis << endl; cerr << "xInAxis, yInAxis = " << xInAxis << ", " << yInAxis << endl; cerr << "xOutCorrAxis, yOutCoorrAxis = " << xOutCorrAxis << ", " << yOutCorrAxis << endl; // cerr << "cursor shape = " << niceShape << endl; cerr << "shape in, shape out" << inShape << outShape << endl; } // Deal with mask. Stepper will make a reference copy of the mask LatticeIterator* outMaskIterPtr = 0; if (outIsMasked) { Lattice& outMask = outLattice.pixelMask(); outMaskIterPtr = new LatticeIterator(outMask, outStepper); } // These tell us which chunk of input data we need to service each // iteration through the output image IPosition inChunkBlc(nDim); IPosition inChunkTrc(nDim); // These tell us which 2D piece of inChunk to read. // This is what we regrid from. IPosition inChunkBlc2D(nDim); IPosition inChunkTrc2D(nDim); // Coordinate conversion vectors Vector world(2), inPixel(2), outPixel(2); Vector pixelScale(nDim); pixelScale = 1.0; pixelScale(xInAxis) = Float(outShape(xOutCorrAxis)) / Float(inShape(xInAxis)); pixelScale(yInAxis) = Float(outShape(yOutCorrAxis)) / Float(inShape(yInAxis)); if (itsShowLevel > 0) { cerr << "pixelScale = " << pixelScale << endl; } // 2D interpolator Interpolate2D interp(method); // Various things needed along the way Vector pix2DPos2(2); IPosition outPos4, outPos3, outPos2, inPos; Double minInX, minInY, maxInX, maxInY; // Generate full plane of coordinates mapping each output // pixel to an input pixel IPosition shapeGrid(3, outShape(xOutAxis), outShape(yOutAxis), 2); its2DCoordinateGrid.resize(shapeGrid); its2DCoordinateGridMask.resize(outShape(xOutAxis), outShape(yOutAxis)); Bool allFailed = False; Bool missedIt = False; // Either generate the coordinate grid or use what the user has supplied t1.mark(); if (itsUser2DCoordinateGrid.nelements() > 0 && itsUser2DCoordinateGridMask.nelements() > 0) { if (itsNotify) { os << "Using user set DirectionCoordinate grid" << LogIO::POST; } // { IPosition shp1 = its2DCoordinateGrid.shape(); IPosition shp2 = itsUser2DCoordinateGrid.shape(); if (shp1.isEqual(shp2)) { its2DCoordinateGrid = itsUser2DCoordinateGrid; } else { os << "User set 2D coordinate grid has the wrong shape" << LogIO::POST; } } { IPosition shp1 = its2DCoordinateGridMask.shape(); IPosition shp2 = itsUser2DCoordinateGridMask.shape(); if (shp1.isEqual(shp2)) { its2DCoordinateGridMask = itsUser2DCoordinateGridMask; } else { os << "User set 2D coordinate grid mask has the wrong shape" << LogIO::POST; } } // allFailed = False; missedIt = False; } else { allFailed = False; missedIt = True; IPosition outPosFull(outLattice.ndim(),0); if (replicate) { make2DCoordinateGrid (its2DCoordinateGrid, minInX, minInY, maxInX, maxInY, pixelScale, xInAxis, yInAxis, xOutAxis, yOutAxis, xInCorrAxis, yInCorrAxis, xOutCorrAxis, yOutCorrAxis, outPosFull, outShape); missedIt = False; allFailed = False; its2DCoordinateGridMask.set(True); } else { make2DCoordinateGrid (os, allFailed, missedIt, minInX, minInY, maxInX, maxInY, its2DCoordinateGrid, its2DCoordinateGridMask, inCoords, outCoords, inCoordinate, outCoordinate, xInAxis, yInAxis, xOutAxis, yOutAxis, inPixelAxes, outPixelAxes, inShape, outPosFull, outShape, decimate); } } s1 += t1.all(); if (missedIt || allFailed) { outLattice.set(0.0); if (outIsMasked) { outLattice.pixelMask().set(False); } delete outMaskIterPtr; return; } // Progress meter ProgressMeter* pProgressMeter = 0; if (showProgress) { Double nMin = 0.0; Double nMax = Double(outLattice.shape().product()); ostringstream oss; oss << "Axes " << outPixelAxes + 1 << " : Pixels Regridded"; pProgressMeter = new ProgressMeter(nMin, nMax, String(oss), String("Regridding"), String(""), String(""), True, max(1,Int(nMax/20))); } // Find scale factor for Jy/pixel images Double scale = findScaleFactor(imageUnit, inCoords, outCoords, inCoordinate, outCoordinate, os); // Iterate through output image t2.mark(); Double iPix = 0.0; Int i2; for (outIter.reset(); !outIter.atEnd(); outIter++) { const IPosition& outCursorShape = outIter.cursorShape(); const IPosition& outPos = outIter.position(); if (itsShowLevel>0) { cerr << endl; cerr << "Output lattice iterator position = " << outPos << endl; cerr << "Shape of cursor = " << outIter.cursor().shape() << endl; } // Now get a chunk of input data which we will access over and over // as we interpolate it. missedIt = True; allFailed = True; t3.mark(); findXYExtent (missedIt, allFailed, minInX, minInY, maxInX, maxInY, its2DCoordinateGrid, its2DCoordinateGridMask, xInAxis, yInAxis, xOutAxis, yOutAxis, outPos, outCursorShape, inShape); s3 += t3.all(); if (itsShowLevel>0) { cerr << "missedIt, allFailed, minInX, maxInX, minInY, maxInY = " << missedIt << ", " << allFailed << ", " << minInX << ", " << maxInX << ", " << minInY << ", " << maxInY << endl; } if (missedIt || allFailed) { outIter.rwCursor().set(0.0); if (outIsMasked) outMaskIterPtr->rwCursor().set(False); if (showProgress) { pProgressMeter->update(iPix); iPix += Double(outCursorShape.product()); } } else { // For the non-regrid axes, the input and output shapes, // and hence positions, are the same. // pixelAxisMap2(i) says where pixel axis i in the input // image is in the output image. for (uInt k=0; k(floor(minInX)) - 3; inChunkBlc(xInAxis) = max(0,i2); i2 = static_cast(floor(minInY)) - 3; inChunkBlc(yInAxis) = max(0,i2); i2 = static_cast(ceil(maxInX)) + 3; inChunkTrc(xInAxis) = min(inShape(xInAxis)-1,i2); i2 = static_cast(ceil(maxInY)) + 3; inChunkTrc(yInAxis) = min(inShape(yInAxis)-1,i2); IPosition inChunkShape = inChunkTrc - inChunkBlc + 1; if (itsShowLevel>0) { cerr << "inChunkShape = " << inChunkShape << endl; cerr << "inChunkBlc, inChunkTrc " << inChunkBlc << inChunkTrc << endl; } // Get the input data and mask Array inDataChunk = inLattice.getSlice(inChunkBlc, inChunkShape); Array* inMaskChunkPtr = 0; if (inIsMasked) { inMaskChunkPtr = new Array(inLattice.getMaskSlice(inChunkBlc, inChunkShape)); } // Iterate through the output cursor by Matrices, // each holding a Direction plane. // This gets us just a few percent speed up over // iterating through pixel by pixel. ArrayLattice outCursor(outIter.rwCursor()); // reference copy t4.mark(); ThrowIf( inChunkShape(xInAxis)==1 && inChunkShape(yInAxis)==1, "Cannot regrid degenerate DirectionCoordinate plane" ); ThrowIf( inChunkShape(xInAxis)==1 || inChunkShape(yInAxis)==1, "Cannot yet handle DirectionCoordinate plane with one " "degenerate axis" ); regrid2DMatrix(outCursor, outMaskIterPtr, interp, pProgressMeter, iPix, nDim, xInAxis, yInAxis, xOutAxis, yOutAxis, scale, inIsMasked, outIsMasked, outPos, outCursorShape, inChunkShape, inChunkBlc, pixelAxisMap2, inDataChunk, inMaskChunkPtr, its2DCoordinateGrid, its2DCoordinateGridMask); s4 += t4.all(); } if (outIsMasked) { (*outMaskIterPtr)++; } } if (outIsMasked) delete outMaskIterPtr; if (showProgress) delete pProgressMeter; if (itsShowLevel > 0) { s2 += t2.all(); s0 += t0.all(); cerr << " Function regrid2DMatrix took " << s4 << endl; cerr << " Function findXYExtent took " << s3 << endl; cerr << " Iterating and regridding took " << s2 << endl; if (replicate) { cerr << " Function make2DCoordinateGrid (replication) took " << s1 << endl; } else { cerr << " Function make2DCoordinateGrid (regridding) took " << s1 << endl; } cerr << " Function regrid2D took " << s0 << endl; } } template void ImageRegrid::make2DCoordinateGrid(LogIO& os, Bool& allFailed, Bool&missedIt, Double& minInX, Double& minInY, Double& maxInX, Double& maxInY, Cube& in2DPos, Matrix& succeed, const CoordinateSystem& inCoords, const CoordinateSystem& outCoords, Int inCoordinate, Int outCoordinate, uInt xInAxis, uInt yInAxis, uInt xOutAxis, uInt yOutAxis, const IPosition& inPixelAxes, const IPosition& outPixelAxes, const IPosition& inShape, const IPosition& outPos, const IPosition& outCursorShape, uInt decimate) { // // in2DPos says where the output pixel (i,j) is located in the input image // Vector world(2), inPixel(2), outPixel(2); minInX = 100000000.0; minInY = 100000000.0; maxInX = -100000000.0; maxInY = -100000000.0; allFailed = True; Bool ok1=False, ok2=False; MVDirection inMVD, outMVD; // uInt ni = outCursorShape(xOutAxis); uInt nj = outCursorShape(yOutAxis); // Where in the Direction Coordinates are X and Y ? // pixelAxes(0) says where Lon is in DirectionCoordinate // pixelAxes(1) says where Lat is in DirectionCoordinate // The X axis is always the direction axis that appears first in the image // uInt inXIdx = 0; // [x,y] = [lon,lat] uInt inYIdx = 1; if (inPixelAxes(0)==Int(yInAxis)) { inXIdx = 1; // [x,y] = [lat,lon] inYIdx = 0; }; // uInt outXIdx = 0; uInt outYIdx = 1; if (outPixelAxes(0)==Int(yOutAxis)) { outXIdx = 1; outYIdx = 0; }; // Matrix doneIt(ni,nj); doneIt = False; // if (itsShowLevel > 0) { cerr << "inXIdx, inYIdx = " << inXIdx << ", " << inYIdx << endl; cerr << "outXIdx, outYIdx = " << outXIdx << ", " << outYIdx << endl; } // uInt nOutI = 0; uInt nOutJ = 0; uInt iInc = 1; uInt jInc = 1; if (decimate > 1) { Int nOut = ni / decimate; if (nOut <= 1) { cerr << "Illegal decimation factor for X; setting to unity" << endl; nOut = ni; } iInc = ni / (nOut- 1); // nOut = nj / decimate; if (nOut <= 1) { cerr << "Illegal decimation factor for Y; setting to unity" << endl; nOut = nj; } jInc = nj / (nOut - 1); if (iInc < 1) { cerr << "Illegal decimation increment computed for X; " "setting to unity" << endl; iInc = 1; } if (jInc < 1) { cerr << "Illegal decimation increment computed for Y; " "setting to unity" << endl; jInc = 1; } // if (iInc==1 && jInc==1) decimate = 0; else { for (uInt j=0; j 0) { cerr << "decimate, nOutI, nOutJ = " << decimate << ", " << nOutI << ", " << nOutJ << endl; cerr << "iInc, jInc = " << iInc << ", " << jInc << endl; } } // Matrix iInPos2D(nOutI,nOutJ); Matrix jInPos2D(nOutI,nOutJ); Matrix ijInMask2D(nOutI,nOutJ); // Are we dealing with a DirectionCoordinate or LinearCoordinate ? Bool isDir = inCoords.type(inCoordinate)==Coordinate::DIRECTION && outCoords.type(outCoordinate)==Coordinate::DIRECTION; DirectionCoordinate inDir, outDir; LinearCoordinate inLin, outLin; Bool useMachine = False; MDirection::Convert machine; if (isDir) { inDir = inCoords.directionCoordinate(inCoordinate); outDir = outCoords.directionCoordinate(outCoordinate); // Set units to degrees Vector units(2); units.set("deg"); ThrowIf( !inDir.setWorldAxisUnits(units), "Failed to set input DirectionCoordinate units to degrees" ); ThrowIf( !outDir.setWorldAxisUnits(units), "Failed to set output DirectionCoordinate units to degrees" ); // Possibly make Direction reference conversion machine. We could use the internal // machine layer inside the DirectionCoordinate, but making the machine explicitly // this way is more general because it allows the ObsInfo to be different. if (!itsDisableConversions) { useMachine = CoordinateUtil::makeDirectionMachine(os, machine, inDir, outDir, inCoords.obsInfo(), outCoords.obsInfo()); } } else { inLin = inCoords.linearCoordinate(inCoordinate); outLin = outCoords.linearCoordinate(outCoordinate); // Set units to same thing const Vector& units = inLin.worldAxisUnits().copy(); ThrowIf( !outLin.setWorldAxisUnits(units), "Failed to set output and input LinearCoordinate axis units the same" ); } // if (itsShowLevel>0) { cerr << "usemachine=" << useMachine << endl; } // If decimating, compute a sparse grid of coordinates and then // interpolate the others. // Otherwise, compute all coordinates (very expensive) // This approach is going to cause pixels along the right and top edges // to be masked as the coarse grid is unlikely to finish exactly // on the lattice edge const uInt nPixelAxes = 2; uInt nConversions; if ( decimate > 1 ) { nConversions = nOutI*nOutJ; } else { nConversions = ni*nj; } Timer t0; uInt ii = 0; uInt jj = 0; // if useMachine, then do each pixel separately. Otherwise do a bulk conversion if (useMachine) { // must be Direction // jj = 0; for (uInt j=0; j1) ijInMask2D(ii,jj) = False; } else { // This gives the 2D input pixel coordinate (relative to // the start of the full Lattice) // to find the interpolated result at. (,,0) pertains to // inX and (,,1) to inY in2DPos(i,j,0) = inPixel(inXIdx); in2DPos(i,j,1) = inPixel(inYIdx); allFailed = False; succeed(i,j) = True; // if (decimate <= 1) { minInX = min(minInX,inPixel(inXIdx)); minInY = min(minInY,inPixel(inYIdx)); maxInX = max(maxInX,inPixel(inXIdx)); maxInY = max(maxInY,inPixel(inYIdx)); } else { iInPos2D(ii,jj) = inPixel(inXIdx); jInPos2D(ii,jj) = inPixel(inYIdx); ijInMask2D(ii,jj) = True; }; }; }; }; } else { // generate coordinate conversions in bulk // set storage matrices for the conversions Matrix inPixelMatrix(nPixelAxes,nConversions); Matrix outPixelMatrix(nPixelAxes,nConversions); Matrix worldMatrix(nPixelAxes,nConversions); Vector failures1(nConversions); Vector failures2(nConversions); // set the output coordinates uInt kk = 0; jj = 0; for (uInt j=0; j1) ijInMask2D(ii,jj) = False; } else { // This gives the 2D input pixel coordinate (relative to // the start of the full Lattice) // to find the interpolated result at. (,,0) pertains to // inX and (,,1) to inY in2DPos(i,j,0) = inPixelMatrix(inXIdx,kk); in2DPos(i,j,1) = inPixelMatrix(inYIdx,kk); succeed(i,j) = True; // if (decimate <= 1) { minInX = min(minInX,inPixelMatrix(inXIdx,kk)); minInY = min(minInY,inPixelMatrix(inYIdx,kk)); maxInX = max(maxInX,inPixelMatrix(inXIdx,kk)); maxInY = max(maxInY,inPixelMatrix(inYIdx,kk)); } else { iInPos2D(ii,jj) = inPixelMatrix(inXIdx,kk); jInPos2D(ii,jj) = inPixelMatrix(inYIdx,kk); ijInMask2D(ii,jj) = True; }; }; kk++; }; }; }; }; if (itsShowLevel > 0) { cerr << "nII, nJJ= " << ii << ", " << jj << endl; cerr << "Sparse grid took " << t0.all() << endl; }; // Bi-linear Interpolation of x and y coordinates in the sparse grid // The coordinates that were already computed get interpolated as // well (should be identical). if (decimate > 1) { Timer t1; // Interpolate2D interp(Interpolate2D::LINEAR); Vector pos(2); Double resultI=0.0, resultJ=0.0; ArrayAccessor > sucp0; ArrayAccessor > sucp1(succeed); ArrayAccessor > in2Dp0; ArrayAccessor > in2Dp1(in2DPos); for (uInt j=0; j resultI ? maxInX : resultI; maxInY = maxInY > resultJ ? maxInY : resultJ; } else *sucp0 = False; sucp0++; in2Dp0++; }; sucp1++; in2Dp1++; }; if (itsShowLevel > 0) { cerr << "Interpolated grid took " << t1.all() << endl; }; }; // Does the output map to anywhere on the input ? missedIt = False; if (!allFailed) { Double ijMin = -0.5; Double iMax = inShape(xInAxis) - 0.5; Double jMax = inShape(yInAxis) - 0.5; // missedIt = (minInXiMax && maxInX>iMax) || (minInYjMax && maxInY>jMax); } if (itsShowLevel>0) { cerr << "allFailed, missedIt = " << allFailed << ", " << missedIt << endl; } } template void ImageRegrid::make2DCoordinateGrid (Cube& in2DPos, Double& minInX, Double& minInY, Double& maxInX, Double& maxInY, const Vector& pixelScale, uInt xInAxis, uInt yInAxis, uInt xOutAxis, uInt yOutAxis, uInt, uInt, uInt xOutCorrAxis, uInt yOutCorrAxis, const IPosition& outPos, const IPosition& outCursorShape) { Double oX = -0.5 + (1.0/2/pixelScale(xInAxis)); Double oY = -0.5 + (1.0/2/pixelScale(yInAxis)); // uInt ni = outCursorShape(xOutAxis); uInt nj = outCursorShape(yOutAxis); // if (xOutAxis == xOutCorrAxis) { // First output Direction axis corresponds to the first input Direction axis Double t0 = (outPos[xOutCorrAxis] / pixelScale(xInAxis)) + oX; Double t1 = (outPos[yOutCorrAxis] / pixelScale(yInAxis)) + oY; // for (uInt j=0; j void ImageRegrid::findXYExtent (Bool& missedIt, Bool& allFailed, Double& minInX, Double& minInY, Double& maxInX, Double& maxInY, Cube& in2DPos, const Matrix& succeed, uInt xInAxis, uInt yInAxis, uInt xOutAxis, uInt yOutAxis, const IPosition& outPos, const IPosition& outCursorShape, const IPosition& inShape) // // Finds the blc and trc (absolute pixel coordinates) of the INPUT image // for the OUTPUT chunk being regridded. // { uInt ni = outCursorShape(xOutAxis); uInt nj = outCursorShape(yOutAxis); // outPos is the BLC of this chunk in the output lattice uInt iOff = outPos[xOutAxis]; uInt jOff = outPos[yOutAxis]; // IPosition blc(2); blc(0) = iOff; blc(1) = jOff; IPosition trc(2); trc(0) = iOff + ni - 1; trc(1) = jOff + nj - 1; IPosition minPos, maxPos; // IPosition s = succeed.shape(); if (blc(0)==0 && blc(1)==0 && trc(0)==(s(0)-1) && trc(1)==(s(1)-1)) { // Short cut if we are going to use the full matrix allFailed = minmax (minInX, maxInX, minInY, maxInY, in2DPos.xyPlane(0), in2DPos.xyPlane(1), succeed); } else { // Pull out the relevant piece allFailed = minmax (minInX, maxInX, minInY, maxInY, in2DPos.xyPlane(0)(blc,trc), in2DPos.xyPlane(1)(blc,trc), succeed(blc,trc)); } // if (!allFailed) { Double ijMin = -0.5; Double iMax = inShape(xInAxis) - 0.5; Double jMax = inShape(yInAxis) - 0.5; missedIt = (minInXiMax && maxInX>iMax) || (minInYjMax && maxInY>jMax); } else { missedIt = True; } } template void ImageRegrid::regrid2DMatrix(Lattice& outCursor, LatticeIterator*& outMaskIterPtr, const Interpolate2D& interp, ProgressMeter*& pProgressMeter, Double& iPix, uInt nDim, uInt xInAxis, uInt yInAxis, uInt xOutAxis, uInt yOutAxis, Double scale, Bool inIsMasked, Bool outIsMasked, const IPosition& outPos, const IPosition& outCursorShape, const IPosition& inChunkShape, const IPosition& inChunkBlc, const IPosition& pixelAxisMap2, Array& inDataChunk, Array*& inMaskChunkPtr, const Cube& pix2DPos, const Matrix& succeed) { // // Iterate through a stack of DirectionCoordinate planes and interpolate them // // Setup Navigator and tell it which axes are the Direction ones in case // of other degenerate axes IPosition axisPath; IPosition outCursorAxes(2, xOutAxis, yOutAxis); IPosition outCursorIterShape(2, outCursorShape(xOutAxis), outCursorShape(yOutAxis)); LatticeStepper outCursorIterStepper(outCursor.shape(), outCursorIterShape, outCursorAxes, axisPath); LatticeIterator outCursorIter(outCursor, outCursorIterStepper); // LatticeIterator* outMaskCursorIterPtr = 0; Lattice* outMaskCursorPtr = 0; if (outIsMasked) { outMaskCursorPtr = new ArrayLattice(outMaskIterPtr->rwCursor()); outMaskCursorIterPtr = new LatticeIterator(*outMaskCursorPtr, outCursorIterStepper); } // IPosition inChunkBlc2D(nDim, 0); IPosition inChunkTrc2D(nDim); inChunkTrc2D = inChunkShape - 1; // IPosition inChunk2DShape(2); inChunk2DShape[0] = inChunkTrc2D[xInAxis] - inChunkBlc2D[xInAxis] + 1; inChunk2DShape[1] = inChunkTrc2D[yInAxis] - inChunkBlc2D[yInAxis] + 1; // Vector pix2DPos2(2); IPosition outPos3; Bool interpOK; T result(0); // for (outCursorIter.reset(); !outCursorIter.atEnd(); outCursorIter++) { // outCursorIter.position is the location of the BLC of the current matrix // within the current // cursor (tile) of data. outPos3 is the location of the BLC of the // current matrix within // the full lattice outPos3 = outPos + outCursorIter.position(); // Fish out the 2D piece of the inChunk relevant to this plane of the cursor for (uInt k=0; k& inDataChunk2D = inDataChunk(inChunkBlc2D, inChunkTrc2D).reform(inChunk2DShape); Matrix* inMaskChunk2DPtr = 0; if (inIsMasked) { inMaskChunk2DPtr = new Matrix((*inMaskChunkPtr) (inChunkBlc2D, inChunkTrc2D). reform(inChunk2DShape)); }; // Now work through each output pixel in the data Matrix and do the // interpolation uInt nCol = outCursorIter.matrixCursor().ncolumn(); uInt nRow = outCursorIter.matrixCursor().nrow(); Matrix &outMCursor = outCursorIter.rwMatrixCursor(); Matrix *outMaskMCursor = 0; if (outIsMasked) { outMaskMCursor = &(outMaskCursorIterPtr->rwMatrixCursor()); }; ArrayAccessor > sucp0; ArrayAccessor > sucp1(succeed); ArrayAccessor > outMp0; ArrayAccessor > outMp1(outMCursor); ArrayAccessor > outMaskMp0; ArrayAccessor > outMaskMp1; if (outIsMasked) outMaskMp1.init(*outMaskMCursor); uInt dpix2DPos = &pix2DPos(0,0,1) - &pix2DPos(0,0,0); for (uInt j=0; jupdate(iPix); iPix += nCol*nRow; }; // if (outIsMasked) (*outMaskCursorIterPtr)++; if (inIsMasked) delete inMaskChunk2DPtr; }; // if (inIsMasked) delete inMaskChunkPtr; if (outIsMasked) { delete outMaskCursorIterPtr; delete outMaskCursorPtr; }; } template void ImageRegrid::regrid1D (MaskedLattice& outLattice, const MaskedLattice& inLattice, const Coordinate& inCoord, const Coordinate& outCoord, const Vector& inPixelAxes, const Vector& outPixelAxes, Int inAxisInCoordinate, Int outAxisInCoordinate, const Vector pixelAxisMap, typename Interpolate2D::Method method, MFrequency::Convert& machine, Bool replicate, Bool useMachine, Bool showProgress) // // Any output mask is overwritten // { const Bool inIsMasked = inLattice.isMasked(); const Bool outIsMasked = outLattice.isMasked() && outLattice.hasPixelMask() && outLattice.pixelMask().isWritable(); // if (itsShowLevel>0) { cerr << "inIsMasked = " << inIsMasked << endl; cerr << "outIsMasked = " << outIsMasked << endl; } // const IPosition& inShape = inLattice.shape(); const IPosition& outShape = outLattice.shape(); const uInt nDim = inLattice.ndim(); const Int inPixelAxis = inPixelAxes(inAxisInCoordinate); const Int outPixelAxis = outPixelAxes(outAxisInCoordinate); // Generate vector of pixel coordinates const uInt nLine = outShape(outPixelAxis); Vector failed(nLine); Block::BaseType> outX(nLine); Bool allFailed = False; Bool allGood = True; // if (replicate) { Float pixelScale = Float(outShape(outPixelAxis)) / Float(inShape(inPixelAxis)); make1DCoordinateGrid (outX, pixelScale); } else { make1DCoordinateGrid (outX, failed, allFailed, allGood, inCoord, outCoord, inAxisInCoordinate, outAxisInCoordinate, machine, useMachine); } // Short cut if all conversions cactus if (allFailed) { outLattice.set(0.0); if (outIsMasked) { Lattice& outMask = outLattice.pixelMask(); outMask.set(False); } return; } // Generate vector of input X values for interpolator const uInt nIn = inShape(inPixelAxis); Block::BaseType> inX(nIn); if (itsShowLevel>0) cerr << "inX = "; for (uInt i=0; i0) cerr << inX[i] << ","; } if (itsShowLevel>0) cerr << endl; // if (itsShowLevel>0) cerr << "outX = "; for (uInt i=0; i0) cerr << outX[i] << ","; } if (itsShowLevel>0) cerr << endl; // // Make navigator and iterator for output data and mask. It is vital that // the "niceShape" is the same for both iterators. Because the mask and // lattice are both TempLattices, one might be on disk, one in core. // Hence we pick one nice shape and use it on both iterators IPosition niceShape = outLattice.niceCursorShape(); TiledLineStepper outStepper(outShape, niceShape, outPixelAxis); LatticeIterator outIter(outLattice, outStepper); // LatticeIterator* outMaskIterPtr = 0; if (outIsMasked) { Lattice& outMask = outLattice.pixelMask(); TiledLineStepper outMaskStepper(outShape, niceShape, outPixelAxis); outMaskIterPtr = new LatticeIterator(outMask, outMaskStepper); } // IPosition inSubShape(nDim,1); IPosition inPos(nDim); inSubShape(inPixelAxis) = inShape(inPixelAxis); // if (itsShowLevel>0) { cerr << "in, out pixel axis = " << inPixelAxis << ", " << outPixelAxis << endl; cerr << "shape in, shape out" << inShape << outShape << endl; cerr << "inSubShape=" << inSubShape << endl; } // Set interpolator method auto method1D = InterpolateArray1D::BaseType, T>::linear; if (method==Interpolate2D::NEAREST) { method1D = InterpolateArray1D::BaseType, T>::nearestNeighbour; if (itsShowLevel>0) { cerr << "Method = nearest" << endl; } } else if (method==Interpolate2D::LINEAR) { method1D = InterpolateArray1D::BaseType, T>::linear; if (itsShowLevel>0) { cerr << "Method = linear" << endl; } } else if (method==Interpolate2D::CUBIC) { method1D = InterpolateArray1D::BaseType, T>::spline; if (itsShowLevel>0) { cerr << "Method = cubic spline" << endl; } } // Progress meter ProgressMeter* pProgressMeter = 0; if (showProgress) { Double nMin = 0.0; Double nMax = Double(outLattice.shape().product()) / Double(outIter.cursorShape().product()); ostringstream oss; oss << "Axis " << outPixelAxis + 1 << " : Lines Regridded"; pProgressMeter = new ProgressMeter(nMin, nMax, String(oss), String("Regridding"), String(""), String(""), True, max(1,Int(nMax/20))); } // Iterate through output image by line Bool goodIsTrue = True; Bool extrapolate = False; Vector dummyOutMask(nLine); for (outIter.reset(); !outIter.atEnd(); outIter++) { const IPosition& outPos = outIter.position(); if (itsShowLevel>1) { cerr << endl; cerr << "Output lattice iterator position = " << outPos << endl; cerr << "Output lattice iterator cursor shape = " << outIter.cursorShape()<< endl; } // Get input vector of data and mask for (uInt i=0; i& inY = inLattice.getSlice(inPos, inSubShape, True); const Vector& inMask = inLattice.getMaskSlice(inPos, inSubShape, True); if (itsShowLevel>1) { cerr << "inPos=" << inPos << endl; cerr << "inY=" << inY << endl; cerr << "inY=" << inMask << endl; } // if (allGood) { if (outIsMasked) { InterpolateArray1D::BaseType, T>::interpolate(outIter.rwVectorCursor(), outMaskIterPtr-> rwVectorCursor(), outX, inX, inY, inMask, method1D, goodIsTrue, extrapolate); } else { InterpolateArray1D::BaseType, T>::interpolate(outIter.rwVectorCursor(), dummyOutMask, outX, inX, inY, inMask, method1D, goodIsTrue, extrapolate); } } else { // AND the coordinate conversion success vector and the input mask if (outIsMasked) { InterpolateArray1D::BaseType, T>::interpolate(outIter.rwVectorCursor(), outMaskIterPtr-> rwVectorCursor(), outX, inX, inY, (failed && inMask), method1D, goodIsTrue, extrapolate); } else { InterpolateArray1D::BaseType, T>::interpolate(outIter.rwVectorCursor(), dummyOutMask, outX, inX, inY, (failed && inMask), method1D, goodIsTrue, extrapolate); } } // if (itsShowLevel>1) { cerr << "outY = " << outIter.rwVectorCursor() << endl; if (outIsMasked) cerr << "outMask = " << outMaskIterPtr->rwVectorCursor() << endl; } // if (outIsMasked) (*outMaskIterPtr)++; if (showProgress) pProgressMeter->update(Double(outIter.nsteps())); } // if (outIsMasked) delete outMaskIterPtr; if (showProgress) delete pProgressMeter; } template void ImageRegrid::make1DCoordinateGrid (Block::BaseType>& outX, Vector& failed, Bool& allFailed, Bool& allGood, const Coordinate& inCoord, const Coordinate& outCoord, Int inAxisInCoordinate, Int outAxisInCoordinate, MFrequency::Convert& machine, Bool useMachine) { // Precompute vector of output coordinates to interpolate data at Double outPixel2, inPixel2; Vector world, inPixel; Vector outPixel = outCoord.referencePixel().copy(); // const uInt nLine = outX.nelements(); failed.resize(nLine); allFailed = True; allGood = True; Bool ok1 = False; Bool ok2 = False; MFrequency inMVF, outMVF; // if (useMachine) { // If we are going to Stoke up the MFrequency machine it means // we have a SpectralCoordinate; cast to it const SpectralCoordinate& inSpecCoord = dynamic_cast(inCoord); const SpectralCoordinate& outSpecCoord = dynamic_cast(outCoord); // for (uInt i=0; i0) { cerr << "allFailed=" << allFailed << endl; cerr << "allGood =" << allGood << endl; } if (itsShowLevel>1) { cerr << "failed = " << failed << endl; cerr << "outX="; for (uInt i=0;i void ImageRegrid::make1DCoordinateGrid (Block::BaseType>& outX, typename NumericTraits::BaseType pixelScale) const { Float oX = -0.5 + (1.0/2/pixelScale); const uInt nLine = outX.nelements(); for (uInt i=0; i::BaseType(i) / pixelScale) + oX; } } template void ImageRegrid::_checkAxes(IPosition& outPixelAxes, const IPosition& inShape, const IPosition& outShape, const Vector& pixelAxisMap1, const CoordinateSystem& outCoords, Bool verbose) { LogIO os(LogOrigin("ImageRegrid", __func__, WHERE)); ThrowIf(inShape.nelements()==0, "The input shape is illegal"); ThrowIf( outShape.nelements()==0, "The output shape is illegal" ); Int n1 = outPixelAxes.nelements(); const Int nOut = outShape.nelements(); ThrowIf( n1 > nOut, "You have specified more pixel axes than there are dimensions" ); // Fill in all axes if null pixelAxes given if (n1==0) { outPixelAxes = IPosition::makeAxisPath(nOut); n1 = outPixelAxes.nelements(); } // Check for Stokes and discard Int outCoordinate, outAxisInCoordinate; Int j = 0; for (Int i=0; i found(nOut, False); for (Int i=0; i=nOut, "Pixel axes are out of range" ); // ThrowIf( found(outPixelAxes(i)), "Specified pixel axes " + String::toString( outPixelAxes+1) + " are not unique" ); found(outPixelAxes(i)) = True; } // CHeck non-regriddded axis shapes are ok for (Int i=0; i void ImageRegrid::findMaps (uInt nDim, Vector& pixelAxisMap1, Vector& pixelAxisMap2, const CoordinateSystem& inCoords, const CoordinateSystem& outCoords) const { // Find mapping between CoordinateSystems // // worldAxisMap(i) is the location of world axis i (from the supplied // coordinate system, cSys, in the current coordinate system. // worldAxisTranspose(i) is the location of world axis i (from the current // coordinate system) in the supplied coordinate system, cSys. Vector worldAxisTranspose, worldAxisMap; Vector worldRefChange; if (!outCoords.worldMap(worldAxisMap, worldAxisTranspose, worldRefChange, inCoords)) { throw(AipsError(inCoords.errorMessage())); } // pixelAxisMap1(i) says where pixel axis i in the output coordinate system // is in the input coordinate system // pixelAxisMap2(i) says where pixel axis i in the input coordinate system // is in the output coordinate system pixelAxisMap1.resize(nDim); pixelAxisMap2.resize(nDim); for (uInt paOut=0; paOut0) { cerr << "worldmap, worldtranspose, refChange = " << worldAxisMap << worldAxisTranspose << worldRefChange << endl; cerr << "pixelaxismap{1,2} = " << pixelAxisMap1 << pixelAxisMap2 << endl; } } template Double ImageRegrid::findScaleFactor(const Unit& units, const CoordinateSystem& inCoords, const CoordinateSystem& outCoords, Int inCoordinate, Int outCoordinate, LogIO& os) const { Double fac = 1.0; String t = units.getName(); t.upcase(); if (t==String("JY/PIXEL")) { // Set units to the same thing if (inCoords.type(inCoordinate)==Coordinate::DIRECTION) { DirectionCoordinate inDir = inCoords.directionCoordinate(inCoordinate); DirectionCoordinate outDir = outCoords.directionCoordinate(outCoordinate); // Vector units(2); units.set("deg"); // inDir.setWorldAxisUnits(units); outDir.setWorldAxisUnits(units); // const Vector& incIn = inDir.increment(); const Vector& incOut = outDir.increment(); // fac = abs(incOut(0)*incOut(1) / incIn(0) / incIn(1)); os << "Applying Jy/pixel scale factor of " << fac << endl; } else if (inCoords.type(inCoordinate)==Coordinate::LINEAR) { LinearCoordinate inLin = inCoords.linearCoordinate(inCoordinate); LinearCoordinate outLin = outCoords.linearCoordinate(outCoordinate); // const Vector& units = inLin.worldAxisUnits().copy(); ThrowIf( !outLin.setWorldAxisUnits(units), "Failed to set output and input LinearCoordinate axis units the same" ); // const Vector& incIn = inLin.increment(); const Vector& incOut = outLin.increment(); // fac = abs(incOut(0)*incOut(1) / incIn(0) / incIn(1)); os << "Applying Jy/pixel scale factor of " << fac << endl; } } // return fac; } template Bool ImageRegrid::minmax(Double &minX, Double &maxX, Double &minY, Double &maxY, const Array &xData, const Array &yData, const Array &mask) { minX = 1.0e30; maxX = -1.0e30; minY = 1.0e30; maxY = -1.0e30; Array::const_iterator pMask = mask.begin(); Array::const_iterator pXend = xData.end(); for (Array::const_iterator pX = xData.begin(), pY = yData.begin(); pX != pXend; ++pX, ++pY, ++pMask) { if (*pMask) { minX = minX < *pX ? minX : *pX; maxX = maxX > *pX ? maxX : *pX; minY = minY < *pY ? minY : *pY; maxY = maxY > *pY ? maxY : *pY; }; }; return (maxX < minX); } template void ImageRegrid::get2DCoordinateGrid (Cube &grid, Matrix &gridMask) const { grid = its2DCoordinateGrid; gridMask = its2DCoordinateGridMask; } template void ImageRegrid::set2DCoordinateGrid (const Cube &grid, const Matrix &gridMask, Bool) { itsUser2DCoordinateGrid.resize(); itsUser2DCoordinateGrid = grid; itsUser2DCoordinateGridMask.resize(); itsUser2DCoordinateGridMask = gridMask; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Images/ImageStatistics.h000066400000000000000000000240241476623553700212460ustar00rootroot00000000000000//# ImageStatistics.h: generate statistics from an image //# Copyright (C) 1996,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_IMAGESTATISTICS_H #define IMAGES_IMAGESTATISTICS_H //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class ImageInterface; class IPosition; // // Displays various statistics from an image. // // // // // //
      • LatticeStatistics (base class) //
      • ImageInterface // // // This is a class designed to display and retrieve statistics from images // // // This class enable you to display and/or retrieve statistics evaluated over // specified regions from an image. The dimension of the region is arbitrary, but // the size of each dimension is always the size of the corresponding image axis. // The statistics are displayed as a function of location of the axes not // used to evaluate the statistics over. The axes which you evaluate the statistics // over are called the cursor axes, the others are called the display axes. // // This class is derived from the class LatticeStatistics which does all // the work. This class only adds some extra capability in terms of // logging world (rather than pixel) coordinates and computing the // synthesized beam area, if there is one. There are just a few virtual // functions for you to over-ride. These are rather specialized, they // are not part of a general polymorphic interface, just a way to // separate the Lattice and Image functionality out. // // See LatticeStatistics for details and examples. // // // // The generation of statistical information from an image is a basic // and necessary capability. // // //
      • Deal with complex images at least for statistics retrieval if not // plotting. //
      • Retrieve statistics at specified location of display axes //
      • Standard errors on statistical quantities //
      • Median, other more exotic statistics. Life made difficult by // accumulation image approach // template class ImageStatistics : public LatticeStatistics { public: // Constructor takes the image and a LogIO object for logging. // You can specify whether you want to see progress meters or not. // You can force the storage image to be disk based, otherwise // the decision for core or disk is taken for you. // If clone is True, the input image will be cloned, so the caller // can make changes to the input image, but the statistics will reflect the // image as it was at construction. If False, a reference to the input image // is used, and so the caller shouldn't make changes to the input image between // construction and calling statistics computation methods, unless it calls setNewImage() // to update the changed image. Obviously, cloning the image impacts performance // and memory usage. ImageStatistics (const ImageInterface& image, LogIO& os, Bool showProgress=True, Bool forceDisk=False, Bool clone=True); // Constructor takes the image only. In the absence of a logger you get no messages. // This includes error messages and potential listing of the statistics. // You can specify whether you want to see progress meters or not. // You can force the storage image to be disk based, otherwise // the decision for core or disk is taken for you. ImageStatistics (const ImageInterface& image, Bool showProgress=True, Bool forceDisk=False, Bool clone=True); // Copy constructor. Copy semantics are followed. Therefore any storage image // that has already been created for other is copied to *this ImageStatistics(const ImageStatistics &other); // Destructor virtual ~ImageStatistics (); // Assignment operator. Deletes any storage image associated with // the object being assigned to and copies any storage image that has // already been created for "other". ImageStatistics &operator=(const ImageStatistics &other); // Set a new ImageInterface object. A return value of False indicates the // image had an invalid type or that the internal state of the class is bad. // If clone is True, the input image will be cloned, so the caller // can make changes to the input image, but the statistics will reflect the // image as it was at construction. If False, a reference to the input image // is used, and so the caller shouldn't make changes to the input image between // construction and calling statistics computation methods, unless it calls setNewImage() // to update the changed image. Obviously, cloning the image impacts performance // and memory usage. Bool setNewImage (const ImageInterface& image, Bool clone=True); void setPrecision(Int precision); void setBlc(const IPosition& blc); IPosition getBlc() const; Int getPrecision() const; // list robust statistics? Should be called before display() void showRobust(const Bool show); inline void recordMessages(const Bool rm) { _recordMessages = rm; } inline vector getMessages() { return _messages; } inline void clearMessages() { _messages.resize(0); } void setListStats(Bool b) { _listStats = b; } protected: typedef typename NumericTraits::PrecisionType AccumType; virtual Bool _canDoFlux() const; private: // Data LogIO os_p; const ImageInterface* pInImage_p; std::shared_ptr> _inImPtrMgr; IPosition blc_; Int precision_; Bool _showRobust, _recordMessages, _listStats; mutable vector _messages; // Virtual functions. See LatticeStatistics for more information // about these, or see the implementation. // Get label for higher order axes virtual void getLabels(String& higherOrder, String& xAxis, const IPosition& dPos) const; // Get beam area in pixels if possible. Return False if the beam area could not be // calculated. virtual Bool _getBeamArea( Array& beamArea, String& msg ) const; // List min and max with world coordinates virtual void listMinMax (ostringstream& osMin, ostringstream& osMax, Int oWidth, DataType type); // List the statistics virtual Bool listStats (Bool hasBeam, const IPosition& dPos, const Matrix& ord); virtual void displayStats( AccumType nPts, AccumType sum, AccumType median, AccumType medAbsDevMed, AccumType quartile, AccumType sumSq, AccumType mean, AccumType var, AccumType rms, AccumType sigma, AccumType dMin, AccumType dMax, AccumType q1, AccumType q3 ); // If isFluxDensity is False, then the computed value is // a flux (ie flux density integrated over a spectral extent) Quantum _flux( Bool& isFluxDensity, AccumType sum, Double beamAreaInPixels ) const; Bool _computeFlux( Array& flux, const Array& npts, const Array& sum ); Bool _computeFlux( Quantum& flux, AccumType sum, const IPosition& pos, Bool posInLattice ); //# Make members of parent class known. protected: using LatticeStatistics::locInLattice; using LatticeStatistics::setStream; using LatticeStatistics::error_p; using LatticeStatistics::goodParameterStatus_p; using LatticeStatistics::haveLogger_p; using LatticeStatistics::displayAxes_p; using LatticeStatistics::cursorAxes_p; using LatticeStatistics::doRobust_p; using LatticeStatistics::doList_p; using LatticeStatistics::fixedMinMax_p; using LatticeStatistics::minPos_p; using LatticeStatistics::maxPos_p; using LatticeStatistics::blcParent_p; public: using LatticeStatistics::NPTS; using LatticeStatistics::SUM; using LatticeStatistics::FLUX; using LatticeStatistics::MEAN; using LatticeStatistics::MEDIAN; using LatticeStatistics::RMS; using LatticeStatistics::SIGMA; using LatticeStatistics::MIN; using LatticeStatistics::MAX; }; //# Declare extern templates for often used types. extern template class ImageStatistics; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/images/Images/ImageStatistics.tcc000066400000000000000000000756421476623553700216040ustar00rootroot00000000000000//# ImageStatistics.cc: generate statistics from an image //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_IMAGESTATISTICS_TCC #define IMAGES_IMAGESTATISTICS_TCC #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Public functions template ImageStatistics::ImageStatistics ( const ImageInterface& image, LogIO& os, Bool showProgress, Bool forceDisk, Bool clone ) : LatticeStatistics(image, os, showProgress, forceDisk, clone), pInImage_p(0), blc_(IPosition(image.coordinates().nPixelAxes(), 0)), precision_(-1), _showRobust(False), _recordMessages(False), _listStats(True), _messages() { ThrowIf(! setNewImage(image, clone), error_p); } template ImageStatistics::ImageStatistics ( const ImageInterface& image, Bool showProgress, Bool forceDisk, Bool clone ) : LatticeStatistics(image, showProgress, forceDisk, clone), pInImage_p(0), blc_(IPosition(image.coordinates().nPixelAxes(), 0)), precision_(-1), _showRobust(False), _recordMessages(False), _listStats(True), _messages() { ThrowIf(!setNewImage(image, clone), error_p); } template ImageStatistics::ImageStatistics(const ImageStatistics &other) // // Copy constructor. Storage image is not copied. // : LatticeStatistics(other), pInImage_p(0), blc_(other.getBlc()), precision_(other.getPrecision()), _showRobust(other._showRobust) { _inImPtrMgr.reset(other.pInImage_p->cloneII()); pInImage_p = _inImPtrMgr.get(); } // Assignment operator. Storage image is not copied template ImageStatistics &ImageStatistics::operator=(const ImageStatistics &other) { if (this != &other) { LatticeStatistics::operator=(other); _inImPtrMgr.reset(other.pInImage_p->cloneII()); pInImage_p = _inImPtrMgr.get(); precision_ = other.precision_; blc_ = other.blc_; _showRobust = other._showRobust; } return *this; } template ImageStatistics::~ImageStatistics() {} template Bool ImageStatistics::setNewImage( const ImageInterface& image, Bool clone ) { if (!goodParameterStatus_p) { return False; } if (clone) { _inImPtrMgr.reset(image.cloneII()); pInImage_p = _inImPtrMgr.get(); } else { _inImPtrMgr.reset(); pInImage_p = ℑ } // Pass it on to LatticeStatistics goodParameterStatus_p = this->setNewLattice(image, clone); return goodParameterStatus_p; } template Bool ImageStatistics::_getBeamArea( Array& beamArea, String& msg ) const { ImageInfo ii = pInImage_p->imageInfo(); Bool hasMultiBeams = ii.hasMultipleBeams(); Bool hasSingleBeam = !hasMultiBeams && ii.hasBeam(); const CoordinateSystem& cSys = pInImage_p->coordinates(); // use contains() not == so moment maps are dealt with nicely if (! hasMultiBeams && ! hasSingleBeam ) { msg = "Image has no beam"; return False; } else if (! cSys.hasDirectionCoordinate()) { msg = "Image does not have a direction coordinate"; return False; } else { String imageUnits = pInImage_p->units().getName(); imageUnits.upcase(); if (! imageUnits.contains("JY/BEAM")) { msg = "Image brightness units not conformant with Jy/beam"; return False; } } DirectionCoordinate dCoord = cSys.directionCoordinate(); IPosition beamAreaShape; if (this->_storageLatticeShape().size() == 1) { beamAreaShape.resize(1); beamAreaShape[0] = 1; } else { beamAreaShape.resize(this->_storageLatticeShape().size() - 1); for (uInt i = 0; i < beamAreaShape.size(); i++) { beamAreaShape[i] = this->_storageLatticeShape()[i]; } } beamArea.resize(beamAreaShape); beamArea.set(-1.0); if (hasSingleBeam) { beamArea.set( ii.getBeamAreaInPixels(-1, -1, dCoord) ); return True; } // per plane beams // ensure both the spectral and polarization axes are display axes, // a degenerate axis is considered not to be a cursor axis for // this purpose since no aggregation along that axis actually occurs IPosition shape = pInImage_p->shape(); Bool foundSpec = ! cSys.hasSpectralAxis() || shape[cSys.spectralAxisNumber(False)] == 1; Bool foundPol = ! cSys.hasPolarizationCoordinate() || shape[cSys.polarizationAxisNumber(False)] == 1; Int specAxis = foundSpec ? -1 : cSys.spectralAxisNumber(); Int polAxis = foundPol ? -1 : cSys.polarizationAxisNumber(); Bool found = False; Int storageSpecAxis = -1; Int storagePolAxis = -1; for (uInt i = 0; i < displayAxes_p.size(); i++) { if (displayAxes_p[i] == specAxis) { foundSpec = True; storageSpecAxis = i; } else if (displayAxes_p[i] == polAxis) { foundPol = True; storagePolAxis = i; } found = foundSpec && foundPol; if (found) { break; } } if (! found) { // if per-plane beams, either the spectral axis and/or the // polarization axis is not a display axis msg = "One or both of the spectral or polarization axes is " "not a display axis, not degenerate, and the image has multiple beams"; return False; } const ImageBeamSet& beams = ii.getBeamSet(); IPosition beamsShape = beams.shape(); Int beamPolAxis = -1; if (cSys.hasPolarizationCoordinate()) { beamPolAxis = specAxis < 0 ? 0 : 1; } IPosition curPos(beamAreaShape.nelements(), 0); GaussianBeam curBeam; IPosition curBeamPos(beams.shape().nelements(), 0); IPosition axisPath = IPosition::makeAxisPath(beamAreaShape.size()); ArrayPositionIterator iter(beamAreaShape, axisPath, False); Double pixAreaRad2 = dCoord.getPixelArea().getValue("rad2"); while (!iter.pastEnd()) { const IPosition curPos = iter.pos(); if (storageSpecAxis >= 0) { curBeamPos[0] = curPos[storageSpecAxis]; } if (storagePolAxis >= 0) { curBeamPos[beamPolAxis] = curPos[storagePolAxis]; } curBeam = beams(curBeamPos[0], curBeamPos[1]); beamArea(curPos) = curBeam.getArea("rad2")/pixAreaRad2; iter.next(); } return True; } template Bool ImageStatistics::listStats (Bool hasBeam, const IPosition& dPos, const Matrix& stats) // // List the statistics for this row to the logger // // Inputs: // dPos The location of the start of the cursor in the // storage image for this row // stats Statistics matrix // Outputs: // Bool Indicates coordinate transformations failed // { if (!haveLogger_p || ! _listStats) { // We will consider this situation as successful return True; } os_p << endl; // Set up the manipulators. We list the number of points as an integer so find // out how big the field width needs to be. Min of 6 so label fits. DataType type = whatType(); Int oDWidth = 14; if (type==TpComplex) { oDWidth = 2*oDWidth + 3; // (x,y) } // Have to convert LogIO object to ostream before can apply // the manipulators Int oPrec = 6; setStream(os_p.output(), oPrec); // Write the pixel and world coordinate of the higher order display axes to the logger if (displayAxes_p.nelements()>1) { String hLabel, xLabel; getLabels(hLabel, xLabel, dPos); os_p << hLabel << endl; } // Find the width of the field into which we are going to write the coordinate value // of the first display axis. Do this by formatting a dummy value. Vector sWorld(1); Vector pixels(1); pixels(0) = 1.0; IPosition blc(pInImage_p->ndim(),0); IPosition trc(pInImage_p->shape()-1); CoordinateSystem cSys = pInImage_p->coordinates(); ImageUtilities::pixToWorld(sWorld, cSys, displayAxes_p(0), cursorAxes_p, blc, trc, pixels, -1); String cName = ImageUtilities::shortAxisName(cSys.worldAxisNames()(displayAxes_p(0))); Int oCWidth = max(uInt(cName.length()), uInt(sWorld(0).length())) + 1; // Write headers const uInt nStatsAxes = cursorAxes_p.nelements(); os_p << endl; Int len0; if (nStatsAxes == 1) { os_p << "Profile "; len0 = 8; } else if (nStatsAxes == 2) { os_p << "Plane "; len0 = 6; } else if (nStatsAxes == 3) { os_p << "Cube "; len0 = 5; } else { os_p << "Hyper-cube "; len0 = 11; } os_p.output() << setw(oCWidth) << cName; os_p.output() << setw(oDWidth) << "Npts"; os_p.output() << setw(oDWidth) << "Sum"; if (hasBeam) os_p.output() << setw(oDWidth) << "FluxDensity"; os_p.output() << setw(oDWidth) << "Mean"; if (doRobust_p) os_p.output() << setw(oDWidth) << "Median"; os_p.output() << setw(oDWidth) << "Rms"; os_p.output() << setw(oDWidth) << "Std dev"; os_p.output() << setw(oDWidth) << "Minimum"; os_p.output() << setw(oDWidth) << "Maximum" << endl; // Convert pixel coordinates Vector of the first display axis to world coordinates const uInt n1 = stats.shape()(0); sWorld.resize(n1); pixels.resize(n1); // for (uInt j=0; j void ImageStatistics::showRobust(const Bool show) { _showRobust = show; } template void ImageStatistics::displayStats( AccumType nPts, AccumType sum, AccumType median, AccumType medAbsDevMed, AccumType quartile, AccumType sumSq, AccumType mean, AccumType var, AccumType rms, AccumType sigma, AccumType dMin, AccumType dMax, AccumType q1, AccumType q3 ) { if ( ! doList_p ) { // Nothing to display, listing data is turned off. return; } const CoordinateSystem& cSys(pInImage_p->coordinates()); // Have to convert LogIO object to ostream before can apply // the manipulators. Also formatting Complex numbers with // the setw manipulator fails, so I go to a lot of trouble // with ostringstreams (which are useable only once). const Int oPrec = 6; setStream(os_p.output(), oPrec); Unit bunit = pInImage_p->units(); String sbunit = bunit.getName(); Quantity uSquared(1, bunit); uSquared *= uSquared; String bunitSquared = uSquared.getUnit(); /////////////////////////////////////////////////////////////////////// // Do Values Section /////////////////////////////////////////////////////////////////////// vector messages; messages.push_back("Values --- "); ostringstream oss; if (_canDoFlux()) { Array beamArea; String msg; Bool hasBeam = _getBeamArea(beamArea, msg); Bool isFluxDensity; Quantum qFlux = _flux( isFluxDensity, sum, hasBeam ? *(beamArea.begin()) : 0 ); AccumType val = qFlux.getValue(); String unit = qFlux.getFullUnit().getName(); oss << " -- flux" << (isFluxDensity ? " density" : "") << " [flux]:" << (isFluxDensity ? "" : " ") << " " << val << " " << unit; messages.push_back(oss.str()); oss.str(""); } Bool doBiweight = this->_getAlgorithm() == StatisticsData::BIWEIGHT; if (LattStatsSpecialize::hasSomePoints(nPts)) { oss << " -- number of points [npts]: " << nPts; messages.push_back(oss.str()); oss.str(""); oss << " -- maximum value [max]: " << dMax << " " << sbunit; messages.push_back(oss.str()); oss.str(""); oss << " -- minimum value [min]: " << dMin << " " << sbunit; messages.push_back(oss.str()); oss.str(""); if (maxPos_p.size() > 0) { IPosition myMaxPos = maxPos_p + blc_; oss << " -- position of max value (pixel) [maxpos]: " << myMaxPos; messages.push_back(oss.str()); oss.str(""); } if (minPos_p.size() > 0) { IPosition myMinPos = minPos_p + blc_; oss << " -- position of min value (pixel) [minpos]: " << myMinPos; messages.push_back(oss.str()); oss.str(""); } if (maxPos_p.size() > 0) { oss << " -- position of max value (world) [maxposf]: " << CoordinateUtil::formatCoordinate (maxPos_p, cSys, precision_); messages.push_back(oss.str()); oss.str(""); } if (minPos_p.size() > 0) { oss << " -- position of min value (world) [minposf]: " << CoordinateUtil::formatCoordinate (minPos_p, cSys, precision_); messages.push_back(oss.str()); oss.str(""); } if (! doBiweight) { oss << " -- Sum of pixel values [sum]: " << sum << " " << sbunit; messages.push_back(oss.str()); oss.str(""); oss << " -- Sum of squared pixel values [sumsq]: " << sumSq << " " << bunitSquared; messages.push_back(oss.str()); oss.str(""); } } /////////////////////////////////////////////////////////////////////// // Do Statistical Section /////////////////////////////////////////////////////////////////////// messages.push_back("Statistics --- "); Vector priorities(0); if (LattStatsSpecialize::hasSomePoints(nPts)) { oss << " -- Mean of the pixel values [mean]: " << mean << " " << sbunit; messages.push_back(oss.str()); oss.str(""); if (! doBiweight) { oss << " -- Variance of the pixel values : " << var << " " << sbunit; messages.push_back(oss.str()); oss.str(""); } oss << " -- Standard deviation of the Mean [sigma]: " << sigma << " " << sbunit; messages.push_back(oss.str()); oss.str(""); if (! doBiweight) { oss << " -- Root mean square [rms]: " << rms << " " << sbunit; messages.push_back(oss.str()); oss.str(""); } if (_showRobust) { oss << " -- Median of the pixel values [median]: " << median << " " << sbunit; messages.push_back(oss.str()); oss.str(""); oss << " -- Median of the deviations [medabsdevmed]: " << medAbsDevMed << " " << sbunit; messages.push_back(oss.str()); oss.str(""); oss << " -- IQR [quartile]: " << quartile << " " << sbunit; messages.push_back(oss.str()); oss.str(""); oss << " -- First quartile [q1]: " << q1 << " " << sbunit; messages.push_back(oss.str()); oss.str(""); oss << " -- Third quartile [q3]: " << q3 << " " << sbunit; messages.push_back(oss.str()); oss.str(""); } priorities.resize(messages.size()); priorities = LogIO::NORMAL; } else { messages.push_back("No valid points found "); priorities.resize(messages.size()); priorities = LogIO::NORMAL; priorities[priorities.size()-1] = LogIO::WARN; } Vector::const_iterator jiter = priorities.begin(); for ( vector::const_iterator iter=messages.begin(); iter!=messages.end(); iter++, jiter++ ) { os_p << *jiter << *iter << LogIO::POST; if (_recordMessages) { _messages.push_back(*iter); } } } template Quantum::AccumType> ImageStatistics::_flux( Bool& isFluxDensity, AccumType sum, Double beamAreaInPixels ) const { ThrowIf( ! _canDoFlux(), "This object cannot be used to determine flux densities" ); isFluxDensity = True; Quantum flux(0, ""); String sbunit = pInImage_p->units().getName(); Bool intensityBeamBased = False; if (sbunit.contains("K")) { String areaUnit = "arcsec2"; flux.setUnit(sbunit + "." + areaUnit); flux.setValue( sum * pInImage_p->coordinates().directionCoordinate().getPixelArea().getValue(areaUnit) ); } else { flux.setUnit("Jy"); if (sbunit.contains("/beam")) { intensityBeamBased = True; uInt iBeam = sbunit.find("/beam"); if (beamAreaInPixels > 0) { flux.setValue(sum/beamAreaInPixels); } flux.setUnit(sbunit.substr(0, iBeam) + sbunit.substr(iBeam+5)); } } if (pInImage_p->coordinates().hasSpectralAxis()) { Int specAxis = pInImage_p->coordinates().spectralAxisNumber(False); Vector::const_iterator myend = cursorAxes_p.end(); if ( pInImage_p->shape()[specAxis] > 1 && std::find(cursorAxes_p.begin(), myend, specAxis) != myend ) { // integrate over nondegenerate spectral axis if (intensityBeamBased && pInImage_p->imageInfo().hasMultipleBeams()) { // the resolution varies by channel, so the previously computed // value based on the passed in sum is bogus because the beam area // varies vector newCursorAxes = cursorAxes_p.tovector(); newCursorAxes.erase( std::find(newCursorAxes.begin(), newCursorAxes.end(), specAxis) ); ImageStatistics newStats(*this); newStats.setAxes(Vector(newCursorAxes)); Array fluxDensities; newStats.getStatistic(fluxDensities, LatticeStatsBase::FLUX); flux.setValue(casacore::sum(fluxDensities)); } const SpectralCoordinate& spCoord = pInImage_p->coordinates().spectralCoordinate(); Quantity inc(0, ""); if (spCoord.restFrequency() > 0) { Double v0, v1; if ( spCoord.pixelToVelocity(v0, 0) && spCoord.pixelToVelocity(v1, 1) ) { inc = Quantity(abs(v1 - v0), spCoord.velocityUnit()); } } else { inc = Quantity(spCoord.increment()[0], spCoord.worldAxisUnits()[0]); } flux.setValue(flux.getValue()*inc.getValue()); Quantity q1(1, flux.getUnit()); Quantity q2(1, inc.getUnit()); flux.setUnit((q1*q2).getUnit()); isFluxDensity = False; } } if (isFluxDensity) { // the brightness unit may already imply this // image has been integrated over a spectral // range, such as the case for moment images UnitVal u = flux.getFullUnit().getValue(); std::vector fluxDensityUnits(2); fluxDensityUnits[0] = UnitVal(1, "Jy"); fluxDensityUnits[1] = UnitVal(1, "K*arcsec2"); std::vector spectralUnits(2); spectralUnits[0] = UnitVal(1, "km/s"); spectralUnits[1] = UnitVal(1, "Hz"); std::vector::const_iterator fiter = fluxDensityUnits.begin(); std::vector::const_iterator fend = fluxDensityUnits.end(); std::vector::const_iterator send = spectralUnits.end(); while (isFluxDensity && fiter != fend) { std::vector::const_iterator siter = spectralUnits.begin(); while (isFluxDensity && siter != send) { if (u == (*fiter) * (*siter)) { isFluxDensity = False; } ++siter; } ++fiter; } } return flux; } template Bool ImageStatistics::_computeFlux( Array& flux,const Array& npts, const Array& sum ) { Array beamArea; String msg; Bool gotBeamArea = _getBeamArea(beamArea, msg); if (! gotBeamArea) { String unit = pInImage_p->units().getName(); unit.downcase(); if (unit.contains("/beam") && ! pInImage_p->imageInfo().hasMultipleBeams()) { os_p << LogIO::WARN << "Unable to compute flux density: " << msg << LogIO::POST; return False; } } ReadOnlyVectorIterator sumIt(sum); ReadOnlyVectorIterator nPtsIt(npts); VectorIterator fluxIt(flux); std::unique_ptr> beamAreaIter( gotBeamArea ? new ReadOnlyVectorIterator(beamArea) : nullptr ); uInt n1 = nPtsIt.vector().nelements(); while (!nPtsIt.pastEnd()) { for (uInt i=0; i 0.5) { Bool isFluxDensity; fluxIt.vector()(i) = _flux( isFluxDensity, sumIt.vector()(i), gotBeamArea ? beamAreaIter->vector()(i) : 0 ).getValue(); } } nPtsIt.next(); sumIt.next(); fluxIt.next(); if (gotBeamArea) { beamAreaIter->next(); } } return True; } template Bool ImageStatistics::_computeFlux( Quantum& flux, AccumType sum, const IPosition& pos, Bool posInLattice ) { Array beamArea; String msg; Bool unused; if (_getBeamArea(beamArea, msg)) { IPosition beamPos = pos; if (posInLattice) { this->_latticePosToStoragePos(beamPos, pos); } flux = _flux(unused, sum, beamArea(beamPos)).getValue(); } else { String unit = pInImage_p->units().getName(); unit.downcase(); if (unit.contains("/beam")) { return False; } flux = _flux(unused, sum, 0).getValue(); } return True; } template Bool ImageStatistics::_canDoFlux() const { const CoordinateSystem& csys = pInImage_p->coordinates(); if (! csys.hasDirectionCoordinate()) { return False; } String unit = pInImage_p->units().getName(); Bool unitOK = unit.contains("K") || ( pInImage_p->imageInfo().hasBeam() && unit.contains("/beam") ); if (! unitOK) { return False; } Bool cursorHasDirection = False; Vector dirAxesNumbers = csys.directionAxesNumbers(); Vector::const_iterator dIter = dirAxesNumbers.begin(); Vector::const_iterator dEnd = dirAxesNumbers.end(); Vector::const_iterator curBegin = cursorAxes_p.begin(); Vector::const_iterator curEnd = cursorAxes_p.end(); while (dIter != dEnd) { if( std::find(curBegin, curEnd, *dIter) != curEnd ) { cursorHasDirection = True; break; } ++dIter; } if (! cursorHasDirection) { return False; } std::set okCursorAxes; okCursorAxes.insert(dirAxesNumbers.begin(), dirAxesNumbers.end()); IPosition shape = pInImage_p->shape(); if (csys.hasSpectralAxis()) { Int specAxis = csys.spectralAxisNumber(False); if ( shape[specAxis] > 1 && std::find(curBegin, curEnd, specAxis) != curEnd && csys.spectralCoordinate().isTabular() ) { // spectral axis is tabular, // spectral axis is nondegenerate and a cursor axis // FIXME the tabular constraints can // be removed, but that will take a bit of work return False; } okCursorAxes.insert(specAxis); } Vector::const_iterator curIter = curBegin; while (curIter != curEnd) { if ( shape[*curIter] > 1 && std::find(okCursorAxes.begin(), okCursorAxes.end(), *curIter) == okCursorAxes.end() ) { // There is a cursor axis that is nondegenerate and is neither // a spectral nor a direction axis return False; } ++curIter; } return True; } template void ImageStatistics::setPrecision(Int precision) { precision_ = precision; } template void ImageStatistics::setBlc(const IPosition& blc) { blc_ = blc; } template IPosition ImageStatistics::getBlc() const { return blc_; } template Int ImageStatistics::getPrecision() const { return precision_; } template void ImageStatistics::getLabels(String& hLabel, String& xLabel, const IPosition& dPos) const // // Get labels for top of plot and listing for the higher order axes // and get the label for the X-axis when plotting // { CoordinateSystem cSys = pInImage_p->coordinates(); xLabel = cSys.worldAxisNames()(displayAxes_p(0)) + " (pixels)"; hLabel =String(""); const uInt nDisplayAxes = displayAxes_p.nelements(); ostringstream oss; if (nDisplayAxes > 1) { Vector sWorld(1); Vector pixels(1); IPosition blc(pInImage_p->ndim(),0); IPosition trc(pInImage_p->shape()-1); for (uInt j=1; j void ImageStatistics::listMinMax(ostringstream& osMin, ostringstream& osMax, Int oWidth, DataType type) { if (!fixedMinMax_p) { // Find world coordinates of min and max. We list pixel coordinates // of min/max relative to the start of the parent lattice CoordinateSystem cSys(pInImage_p->coordinates()); String minPosString = CoordinateUtil::formatCoordinate (minPos_p, cSys); String maxPosString = CoordinateUtil::formatCoordinate (maxPos_p, cSys); // os_p << "Minimum value "; os_p.output() << setw(oWidth) << osMin.str(); if (type==TpFloat && minPos_p.size() > 0) { os_p << " at " << blcParent_p + minPos_p+1 << " (" << minPosString << ")" << endl; } os_p.post(); // os_p << "Maximum value "; os_p.output() << setw(oWidth) << osMax.str(); if (type==TpFloat && maxPos_p.size() > 0) { os_p << " at " << blcParent_p + maxPos_p+1 << " (" << maxPosString << ")" << endl; } os_p << endl; os_p.post(); } } } #endif casacore-3.7.1/images/Images/ImageSummary.h000066400000000000000000000165701476623553700205600ustar00rootroot00000000000000//# ImageSummary.h: List descriptive information from an image //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_IMAGESUMMARY_H #define IMAGES_IMAGESUMMARY_H //# Includes #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class ImageInterface; class IPosition; class Unit; class LogIO; class Coordinate; // // Provides and lists information about the header of an image. // // // // // //
      • ImageInterface //
      • Coordinates // // // This class lists the ancilliary descriptive information from an image // // // Images consist of pixel values and descriptive information. // This information describes the coordinate system, the image // units etc. This class enables you to // retrieve the descriptive information and/or list it. //

        // The functions that retrieve specific coordinate information in vectors // (e.g. referenceValues) return it in the order of the (pixel) axes of // the image. Note that this can be different from the order in which // the CoordinateSystem // functions of similar name might return them. This is because the // order of the coordinates in the CoordinateSystem is not necessarily // the same order as the pixel axes in the associated image, although // of course there is a known association. // // // This class lists information about the coordinates in the image. // The Coordinates classes can maintain the information in a variety // of units. For example, angular quantities are by default in radians, // but the manipulator of a CoordinateSystem // may have converted to some other unit such as arcseconds. This // means that when this class fetches coordinate information and returns // it to you (such as the referenceValues() function, // the information is returned to you in whatever units the coordinates // are currently in. It does not convert it. // // // // // PagedImage inImage(fileName); // ImageSummary summary(inImage); // LogOrigin or("myClass", "myFunction(...)", WHERE); // LogIO os(or); // summary.list(os); // // A PagedImage object is constructed and then logged to the // supplied LogIO object. // // // The viewing of the descriptive image information is a basic capability. // //# //# None that I know of. //# template class ImageSummary { public: // Constructor ImageSummary (const ImageInterface&); // Copy constructor ImageSummary (const ImageSummary &other); // Destructor ~ImageSummary(); // Assignment operator ImageSummary &operator=(const ImageSummary &other); // Retrieve number of image dimension Int ndim () const; // Retrieve image shape IPosition shape () const; // Retrieve tile shape with which image is stored on disk IPosition tileShape () const; // Retrieve axis names in pixel or world axis order. Vector axisNames (Bool pixelOrder=True) const; // Retrieve reference pixels (0 or 1 rel) Vector referencePixels (Bool oneRel=True) const; // Retrieve reference values in pixel or world axis order. Vector referenceValues (Bool pixelOrder=True) const; // Retrieve axis increments in pixel or world axis order. Vector axisIncrements (Bool pixelOrder=True) const; // Retrieve axis units in pixel or world axis order. Vector axisUnits(Bool pixelOrder=True) const; // Retrieve image units Unit units () const; // Retrieve image name. Any prepended path is stripped off. String name() const; // Retrieve observer name String observer() const; // Return epoch of observation as MEpoch or formatted string String obsDate(MEpoch& date) const; // Return telescope String telescope() const; // Return rest frequency. Returns False if none. Bool restFrequency(String& restFreqString, Quantum& restFreq) const; // Return frequency system. Returns False if none. Bool frequencySystem(String& freqTypeString, MFrequency::Types& freqType) const; // Return direction system. Returns False if none. Bool directionSystem(String& dirTypeString, MDirection::Types& dirType) const; // Retrieve whether image has mask or not Bool hasAMask () const; // Retrieve mask names Vector maskNames() const; // Retrieve region names Vector regionNames() const; // Retrieve default mask name. Empty if none String defaultMaskName() const; // Retrieve image type String imageType () const; // List all header information. By default, the reference // values and pixel increments are converted to a "nice" unit before // formatting (e.g. RA is shown as HH:MM:SS.S). // For spectral axes, both frequency and velocity information is listed. You // can specify what velocity definition you want with velocityType // If postLocally is True, the formatted strings are returned in the return value Vector list( LogIO& os, const MDoppler::Types velocityType=MDoppler::RADIO, Bool postLocally=False, const Bool verbose=False ); // Set a new image Bool setNewImage (const ImageInterface& image); private: CoordinateSystem cSys_p; ObsInfo obsInfo_p; ImageInfo imageInfo_p; const ImageInterface* pImage_p; String makeMasksString() const; String makeRegionsString() const; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/images/Images/ImageSummary.tcc000066400000000000000000000305211476623553700210720ustar00rootroot00000000000000//# ImageSummary.cc: list an image header //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_IMAGESUMMARY_TCC #define IMAGES_IMAGESUMMARY_TCC // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template ImageSummary::ImageSummary (const ImageInterface& image) : cSys_p(image.coordinates()), obsInfo_p(cSys_p.obsInfo()), imageInfo_p(image.imageInfo()), pImage_p(image.cloneII()) {} template ImageSummary::ImageSummary (const ImageSummary &other) : cSys_p(other.cSys_p), obsInfo_p(other.obsInfo_p), imageInfo_p(other.imageInfo_p), pImage_p(other.pImage_p->cloneII()) {} template ImageSummary::~ImageSummary () { delete pImage_p; } template ImageSummary &ImageSummary::operator=(const ImageSummary &other) // // Assignment operator // { if (this != &other) { cSys_p = other.cSys_p; obsInfo_p = other.obsInfo_p; imageInfo_p = other.imageInfo_p; delete pImage_p; pImage_p = other.pImage_p->cloneII(); } return *this; } template Int ImageSummary::ndim () const // // Retrieve number of image dimension // { return pImage_p->ndim(); } template IPosition ImageSummary::shape () const // // Get image shape // { return pImage_p->shape(); } template IPosition ImageSummary::tileShape () const // // Get image tile shape // { return pImage_p->niceCursorShape(); } template Vector ImageSummary::axisNames (Bool pixelOrder) const { Vector tmp(cSys_p.worldAxisNames()); if (!pixelOrder) return tmp.copy(); // // Every pixel axs must have a world axis, so don't check for removal // Vector tmp2(cSys_p.nPixelAxes()); for (uInt pixelAxis=0; pixelAxis Vector ImageSummary::referencePixels (Bool oneRel) const // // Get reference pixels for the pixel axes // { Vector off(cSys_p.nPixelAxes(),0.0); if (oneRel) off.set(1.0); return cSys_p.referencePixel().copy()+off; } template Vector ImageSummary::referenceValues (Bool pixelOrder) const { Vector tmp(cSys_p.referenceValue()); if (!pixelOrder) return tmp.copy(); // // Every pixel axs must have a world axis, so don't check for removal // Vector tmp2(cSys_p.nPixelAxes()); for (uInt pixelAxis=0; pixelAxis Vector ImageSummary::axisIncrements (Bool pixelOrder) const { Vector tmp(cSys_p.increment()); if (!pixelOrder) return tmp.copy(); // // Every pixel axs must have a world axis, so don't check for removal // Vector tmp2(cSys_p.nPixelAxes()); for (uInt pixelAxis=0; pixelAxis Vector ImageSummary::axisUnits (Bool pixelOrder) const { Vector tmp(cSys_p.worldAxisUnits()); if (!pixelOrder) return tmp.copy(); // // Every pixel axs must have a world axis, so don't check for removal // Vector tmp2(cSys_p.nPixelAxes()); for (uInt pixelAxis=0; pixelAxis Unit ImageSummary::units () const { return pImage_p->units(); } template String ImageSummary::name () const { const Bool stripPath = True; String name = pImage_p->name(stripPath); if (name.length()==0) { name = String("Temporary_image"); } return name; } template String ImageSummary::observer() const { return obsInfo_p.observer(); } template String ImageSummary::obsDate(MEpoch& epoch) const { epoch = obsInfo_p.obsDate(); MVTime time = MVTime(epoch.getValue()); return time.string(MVTime::YMD); } template String ImageSummary::telescope() const { return obsInfo_p.telescope(); } template Bool ImageSummary::restFrequency(String& restFreqString, Quantum& restFreq) const { Bool ok = False; Int spectralAxis = CoordinateUtil::findSpectralAxis(cSys_p); if (spectralAxis >= 0) { Int coordinate, axisInCoordinate; cSys_p.findPixelAxis (coordinate, axisInCoordinate, spectralAxis); // Double rf = cSys_p.spectralCoordinate(coordinate).restFrequency(); if (rf > 0.0) { restFreq.setValue(rf); restFreq.setUnit(cSys_p.spectralCoordinate(coordinate).worldAxisUnits()(axisInCoordinate)); ok = True; } } if (ok) { ostringstream oss; // oss.output().setf(ios::scientific, ios::floatfield); // oss.output().precision(8); oss << restFreq << endl; restFreqString= String(oss); } else { restFreq.setValue(0.0); restFreq.setUnit("Hz"); restFreqString = ""; ok = False; } return ok; } template Bool ImageSummary::frequencySystem(String& freqTypeString, MFrequency::Types& freqType) const { Bool ok; Int spectralAxis = CoordinateUtil::findSpectralAxis(cSys_p); if (spectralAxis >= 0) { Int coordinate, axisInCoordinate; cSys_p.findPixelAxis (coordinate, axisInCoordinate, spectralAxis); // freqType = cSys_p.spectralCoordinate(uInt(coordinate)).frequencySystem(); freqTypeString = MFrequency::showType(freqType); ok = True; } else { freqTypeString = ""; ok = False; } return ok; } template Bool ImageSummary::directionSystem(String& dirTypeString, MDirection::Types& dirType) const { Bool ok; Vector pixelAxes, worldAxes; Int coordinate; CoordinateUtil::findDirectionAxes(pixelAxes, worldAxes, coordinate, cSys_p); if (coordinate >= 0) { ok = True; dirType = cSys_p.directionCoordinate(uInt(coordinate)).directionType(); dirTypeString = MDirection::showType(dirType); } else { ok = False; dirTypeString = ""; } return ok; } template Bool ImageSummary::hasAMask () const // // See if image has a mask // { return pImage_p->isMasked(); } template Vector ImageSummary::maskNames() const { return pImage_p->regionNames(RegionHandler::Masks); } template Vector ImageSummary::regionNames() const { return pImage_p->regionNames(RegionHandler::Regions); } template String ImageSummary::defaultMaskName() const { return pImage_p->getDefaultMask(); } template String ImageSummary::imageType () const { return pImage_p->imageType(); } template Vector ImageSummary::list ( LogIO& os, const MDoppler::Types velocityType, Bool postLocally, const Bool verbose ) { os << LogIO::NORMAL << endl; MEpoch epoch; obsDate(epoch); // List random things os << "Image name : " << name() << endl; os << "Object name : " << imageInfo_p.objectName() << endl; os << "Image type : " << imageType() << endl; os << "Image quantity : " << ImageInfo::imageType(imageInfo_p.imageType()) << endl; // String list = makeMasksString(); os << "Pixel mask(s) : " << list << endl; // list = makeRegionsString(); os << "Region(s) : " << list << endl; // if (!units().getName().empty()) { os << "Image units : " << this->units().getName() << endl; } // Restoring beam if ( imageInfo_p.hasBeam()) { if (imageInfo_p.hasSingleBeam()) { GaussianBeam rb = imageInfo_p.restoringBeam(); Quantity majAx = rb.getMajor(); majAx.convert("deg"); Quantity minAx = rb.getMinor(); minAx.convert("deg"); if (majAx.getValue()<1.0 || minAx.getValue()<1.0) { majAx.convert(Unit("arcsec")); minAx.convert(Unit("arcsec")); } Quantity pa = rb.getPA(True); pa.convert(Unit("deg")); os.output() << "Restoring Beam : " << majAx << ", " << minAx << ", " << pa << endl; } else { imageInfo_p.getBeamSet().summarize( os, verbose, pImage_p->coordinates() ); } } if (postLocally) { os.postLocally(); } else { os.post(); } // List CoordinateSystem. The messages that were posted locally will // be still be stored in the sink and this function will fish them out. const Vector& messages = cSys_p.list(os, velocityType, shape(), tileShape(), postLocally); return messages; } template Bool ImageSummary::setNewImage (const ImageInterface& image) { // FIXME this should be done using shared pointers pImage_p = ℑ return True; } template String ImageSummary::makeMasksString() const { const String defaultMask = defaultMaskName(); const Vector masks = maskNames(); const uInt nMasks = masks.nelements(); if (nMasks==0) { if (hasAMask()) { return String("Parent is masked"); } else { return String("None"); } } // ostringstream oss; if (!defaultMask.empty()) { oss << defaultMask; if (nMasks==1) { return String(oss); } } // oss << " ["; uInt j = 0; for (uInt i=0; i 0) { oss << ", "; } oss << masks(i); j++; } } oss << "]"; return String(oss); } template String ImageSummary::makeRegionsString() const { const Vector regions = regionNames(); const uInt nRegions = regions.nelements(); if (nRegions==0) return String("None"); // ostringstream oss; uInt j=0; for (uInt i=0; i 0) { oss << ", "; } oss << regions(i); j++; } return String(oss); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Images/ImageUtilities.cc000066400000000000000000000234011476623553700212230ustar00rootroot00000000000000//# ImageUtilities.cc: Helper class for accessing images //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Bool ImageUtilities::pixToWorld ( Vector& sWorld, const CoordinateSystem& cSysIn, const Int& pixelAxis, const Vector& cursorAxes, const IPosition& blc, const IPosition& trc, const Vector& pixels, const Int& prec, const Bool usePrecForMixed ) // // This function converts pixel coordinates to world coordinates. // You specify pixel coordinates for only one axis, the pixel axis, // and you specify a Vector of pixels for conversion. For the // other pixel axes, if a pixel axis is found in the CursorAxes // vector, its pixel coordinate is set to the average pixel coordinate // in the specified region ((blc(i)+trc(i))/2), otherwise it // is set to the reference pixel. The Vector of world coordinates // for the pixel axis is returned as formatted Strings. If for some // reason it can't make the conversion, a string is returned as "?" // // Inputs // cSysIn The CoordinateSystem associated with the image // pixelAxis The pixel axis whose coordinates we are interested in. // cursorAxes If any of the pixel axes, i, in the image are found this // vector, assign their pixel coordinate to // (blc(i) + trc(i)) / 2 otherwise they get the // reference pixel // blc,trc The region of the image being accessed. The average // pixel coordinate in this region is used for the axes // found in CursorAxes. These must be of the same // dimension as the no. of pixel axes in teh // CoordinateSystem // pixels Vector of pixel coordinates (0 rel) to transform // for the pixel axis of interest. // prec Precision to format scientific output // Outputs // sWorld Vector of formatted strings of world coordinates // for the pixel axis // { // CHeck blc,trc if (blc.nelements()!=cSysIn.nPixelAxes() || trc.nelements()!=cSysIn.nPixelAxes()) return False; // Create pixel and world vectors for all pixel axes. Initialize pixel values // to reference pixel, but if an axis is a cursor axis (whose coordinate is // essentially being averaged) set the pixel to the mean pixel. Vector pix(cSysIn.nPixelAxes()); Vector world(cSysIn.nPixelAxes()); pix = cSysIn.referencePixel(); Bool found; uInt i; for (i=0; i inc = dirCoord.increment(); Quantity majAx(abs(inc[0]), "rad"); Quantity minAx(abs(inc[1]), "rad"); Quantity pa(0,"rad"); if (! suppressWarnings) { logIO << LogIO::WARN << "No restoring beam defined even though the " << "image brightness units contain a beam. Assuming " << "the restoring beam is one pixel. To avoid this non-fatal message " << "and subsequent related messages, add a restoring beam to your image's " << "header." << LogIO::POST; } return GaussianBeam(majAx, minAx, pa); } void ImageUtilities::writeImage( const TiledShape& mapShape, const CoordinateSystem& coordinateInfo, const String& imageName, const Array& pixels, LogIO& log, const Array& maskPixels ) { // using pattern from ImageProxy if (!maskPixels.empty()) { if (! maskPixels.shape().isEqual(mapShape.shape())) { log << "Requested image shape differs from pixel mask shape" << LogIO::EXCEPTION; } } PagedImage *newImage = new PagedImage( mapShape, coordinateInfo, imageName ); if (newImage == 0) { log << "Failed to create image " << imageName << LogIO::EXCEPTION; } newImage->put(pixels); if (! maskPixels.empty()) { newImage->makeMask("mask0", True, True).asMask().put(maskPixels); } log << LogIO::NORMAL << "Created image " << imageName << LogIO::POST; delete newImage; } void ImageUtilities::getUnitAndDoppler( String& xUnit, String& doppler, const uInt axis, const CoordinateSystem& csys ) { xUnit = csys.worldAxisUnits()[axis]; doppler = ""; Int specCoordIndex = csys.findCoordinate(Coordinate::SPECTRAL); if ( specCoordIndex >= 0 && axis == (uInt)csys.pixelAxes(specCoordIndex)[0] && ! csys.spectralCoordinate(specCoordIndex).velocityUnit().empty() ) { SpectralCoordinate specCoord = csys.spectralCoordinate(specCoordIndex); xUnit = specCoord.velocityUnit(); doppler = MDoppler::showType( specCoord.velocityDoppler() ); } } void ImageUtilities::copyAttributes (ImageAttrHandler& out, ImageAttrHandler& in) { Vector groupNames = in.groupNames(); for (uInt i=0; i attrNames = inGroup.attrNames(); for (uInt rownr=0; rownr #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class ImageInterface; template class Quantum; class CoordinateSystem; class Coordinate; class String; class IPosition; class LogIO; class AxesSpecifier; class ImageAttrHandler; // //

        // Utility functions for Image manipulation // // // // // // // // // //
      • IPosition //
      • Arrays //
      • Lattice // // // // Some helpful static functions that are common to some of my image // analysis application classes. // // // // I needed some bits and pieces. My goal isto move this rag-tag bunch // out of here into other classes as time goes on. So far // I have eliminated 80% of the original ! // // // //
      • // class ImageUtilities { public: // Open disk image (can be any registered image). Exception // if fileName empty or file does not exist or file is not // of legal image type. For Casacore images, the default mask is // applied. // template static void openImage (std::unique_ptr>& image, const String& fileName); template static void openImage (ImageInterface*& image, const String& fileName); template static std::shared_ptr> openImage (const String& fileName); // // Copy MiscInfo, ImageInfo, brightness unit and logger (history) from in to out template static void copyMiscellaneous (ImageInterface& out, const ImageInterface& in, Bool copyImageInfo = True); // Copy a mask from one image to another template static void copyMask (ImageInterface& out, const ImageInterface& in, const String& maskOut, const String& maskIn, AxesSpecifier axesSpecifier); // Copy the attributes from one image to another. static void copyAttributes (ImageAttrHandler& out, ImageAttrHandler& in); // Add one degenerate axis for each of the specified coordinate types. // If the outfile string is given the new image is a PagedImage. // If the outfile string is empty, the new image is a TempImage. template static void addDegenerateAxes ( LogIO& os, std::unique_ptr>& outImage, const ImageInterface& inImage, const String& outFile, Bool direction, Bool spectral, const String& stokes, Bool linear, Bool tabular, Bool overwrite, Bool silent=False ); // Function to bin up (average data) one axis of an N-D MaskedArray. The interface // is pretty specific to a particular application. It's here because // its implemented with ImageRebin. On input, the output MA *must* // have zero shape. The input and output Coordinates must have the // same type and have only one axis (Linear,Spectral & Tabular). // The output coordinate is adjusted for the binning. The binning // factor does not have to fit integrally into the shape of the specified // axis. template static void bin (MaskedArray& out, Coordinate& coordOut, const MaskedArray& in, const Coordinate& coordIn, uInt axis, uInt bin); // This function converts pixel coordinates to world coordinates. You // specify a vector of pixel coordinates (pixels) for only one // axis, the pixelAxis. For the other pixel axes in the // CoordinateSystem, if a pixel axis "i" is found in the // CursorAxes vector, its pixel coordinate is set to // the average of the selected region from the image for that axis // ((blc(i)+trc(i))/2)), otherwise it is set to the reference pixel. // The vector of world coordinates for pixelAxis is returned as formatted // Strings. If for some reason it can't make the conversion, the element // element is returned as "?" Returns False if the lengths of // <blc and trc are not equal to the number of pixel axes // in the coordinate system. static Bool pixToWorld ( Vector& sWorld, const CoordinateSystem& cSys, const Int& pixelAxis, const Vector& cursorAxes, const IPosition& blc, const IPosition& trc, const Vector& pixels, const Int& prec, const Bool usePrecForMixed=False ); // Convert long axis names "Right Ascension", "Declination", "Frequency" and // "Velocity" to "RA", "Dec", "Freq", "Vel" respectively. Unknown strings // are returned as given. static String shortAxisName (const String& axisName); // write the specified image and add the specified pixels to it. // Currently no checks are done to ensure the pixel array size and // mapShape are compatible; the caller is responsible for this check. static void writeImage( const TiledShape& mapShape, const CoordinateSystem& coordinateInfo, const String& imageName, const Array& pixels, LogIO& log, const Array& pixelMask = Array() ); static GaussianBeam makeFakeBeam( LogIO& logIO, const CoordinateSystem& csys, Bool suppressWarnings = False ); static void getUnitAndDoppler( String& xUnit, String& doppler, const uInt axis, const CoordinateSystem& csys ); }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/images/Images/ImageUtilities2.tcc000066400000000000000000000227541476623553700215030ustar00rootroot00000000000000//# ImageUtilities2.tcc: Implement templates functions //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_IMAGEUTILITIES2_TCC #define IMAGES_IMAGEUTILITIES2_TCC #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template void ImageUtilities::addDegenerateAxes( LogIO& os, std::unique_ptr>& outImage, const ImageInterface& inImage, const String& outFile, Bool direction, Bool spectral, const String& stokes, Bool linear, Bool tabular, Bool overwrite, Bool silent ) { // Verify output file if (!overwrite && !outFile.empty()) { NewFile validfile; String errmsg; if (!validfile.valueOK(outFile, errmsg)) { ThrowCc(errmsg); } } IPosition shape = inImage.shape(); CoordinateSystem cSys = inImage.coordinates(); IPosition keepAxes = IPosition::makeAxisPath(shape.nelements()); uInt nExtra = CoordinateUtil::addAxes ( cSys, direction, spectral, stokes, linear, tabular, silent ); if (nExtra > 0) { uInt n = shape.nelements(); shape.resize(n+nExtra,True); for (uInt i=0; i(shape, cSys)); } else { os << LogIO::NORMAL << "Creating image '" << outFile << "' of shape " << shape << LogIO::POST; outImage.reset(new PagedImage(shape, cSys, outFile)); } ImageInterface* pOutImage = outImage.get(); // Generate output masks Vector maskNames = inImage.regionNames(RegionHandler::Masks); const uInt nMasks = maskNames.nelements(); if (nMasks > 0) { for (uInt i=0; imakeMask(maskNames(i), True, False, True); } } pOutImage->setDefaultMask(inImage.getDefaultMask()); // Generate SubImage to copy the data into AxesSpecifier axesSpecifier(keepAxes); SubImage subImage(*pOutImage, True, axesSpecifier); // Copy masks (directly, can't do via SubImage) if (nMasks > 0) { for (uInt i=0; i void ImageUtilities::copyMiscellaneous (ImageInterface& out, const ImageInterface& in, Bool copyImageInfo) { out.setMiscInfo(in.miscInfo()); if (copyImageInfo) { out.setImageInfo(in.imageInfo()); } out.setUnits(in.units()); try { out.appendLog(in.logger()); } catch (const AipsError& x) { LogIO log(LogOrigin("ImageUtilities", __func__, WHERE)); log << LogIO::WARN << "Error copying image history: " << x.getMesg() << LogIO::POST; } copyAttributes (out.attrHandler(True), in.roAttrHandler()); } template void ImageUtilities::bin (MaskedArray& out, Coordinate& coordOut, const MaskedArray& in, const Coordinate& coordIn, uInt axis, uInt bin) { // Check AlwaysAssert(coordIn.nPixelAxes()==1 && coordIn.nWorldAxes()==1, AipsError); AlwaysAssert(coordOut.nPixelAxes()==1 && coordOut.nWorldAxes()==1, AipsError); // AlwaysAssert(coordIn.type()==coordOut.type(),AipsError); Coordinate::Type type = coordIn.type(); AlwaysAssert(type==Coordinate::LINEAR || type==Coordinate::SPECTRAL || type==Coordinate::TABULAR, AipsError); // const IPosition shapeIn = in.shape(); const uInt nDim = shapeIn.nelements(); AlwaysAssert(axis im(tShapeIn, cSysIn); // Set data im.put(in.getArray()); TempLattice pixelMask(shapeIn); pixelMask.put(in.getMask()); im.attachMask(pixelMask); // Create binner IPosition factors(nDim,1); factors(axis) = bin; RebinImage binIm(im, factors); // Assign output MA MaskedArray tmp(binIm.get(), binIm.getMask()); out = tmp; // Handle coordinate. const CoordinateSystem cSysOut = binIm.coordinates(); if (type==Coordinate::LINEAR) { const LinearCoordinate& cIn = cSysOut.linearCoordinate(axis); LinearCoordinate& cOut = dynamic_cast(coordOut); cOut = cIn; } else if (type==Coordinate::SPECTRAL) { const SpectralCoordinate& cIn = cSysOut.spectralCoordinate(axis); SpectralCoordinate& cOut = dynamic_cast(coordOut); cOut = cIn; } else if (type==Coordinate::TABULAR) { const TabularCoordinate& cIn = cSysOut.tabularCoordinate(axis); TabularCoordinate& cOut = dynamic_cast(coordOut); cOut = cIn; } } template void ImageUtilities::copyMask ( ImageInterface& out, const ImageInterface& in, const String& maskOut, const String& maskIn, const AxesSpecifier outSpec ) { // // Because you can't write to the mask of a SubImage, we pass // in an AxesSpecifier to be applied to the output mask. // In this way the dimensionality of in and out can be made // the same. // // Get masks ImageRegion iRIn = in.getRegion(maskIn, RegionHandler::Masks); const LCRegion& regionIn = iRIn.asMask(); ImageRegion iROut = out.getRegion(maskOut, RegionHandler::Masks); LCRegion& regionOut = iROut.asMask(); SubLattice subRegionOut(regionOut, True, outSpec); // Copy LatticeIterator maskIter(subRegionOut); for (maskIter.reset(); !maskIter.atEnd(); maskIter++) { subRegionOut.putSlice(regionIn.getSlice(maskIter.position(), maskIter.cursorShape()), maskIter.position()); } } template void ImageUtilities::openImage( ImageInterface*& pImage, const String& fileName ) { ThrowIf( fileName.empty(), "The image filename is empty" ); File file(fileName); ThrowIf( ! file.exists(), "File '" + fileName + "' does not exist" ); FITSImage::registerOpenFunction(); MIRIADImage::registerOpenFunction(); LatticeBase* lattPtr = ImageOpener::openImage (fileName); ThrowIf( ! lattPtr, "Image " + fileName + " cannot be opened; its type is unknown" ); if (lattPtr->dataType() != whatType()) { delete lattPtr; ThrowCc( "Logic Error: " + fileName + " has a different data type than the data type of the requested object" ); } pImage = dynamic_cast *>(lattPtr); if (pImage == 0) { delete lattPtr; ThrowCc( "Unrecognized image data type, " "presently only Float and Complex images are supported" ); } } template void ImageUtilities::openImage( std::unique_ptr>& image, const String& fileName ) { ImageInterface* p = 0; ImageUtilities::openImage(p, fileName); image.reset(p); } template std::shared_ptr> ImageUtilities::openImage (const String& fileName) { ImageInterface* p = 0; ImageUtilities::openImage(p, fileName); return std::shared_ptr> (p); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Images/Image_tmpl.cc000066400000000000000000000037041476623553700203670ustar00rootroot00000000000000//# Array_tmpl.cc: Explicit Array template instantiations //# Copyright (C) 2015 //# Associated Universities, Inc. Washington DC, USA, //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# Includes #include #include #include #include #include #include //# Instantiate extern templates for often used types. namespace casacore { template class PagedImage; template class PagedImage; template class ImageStatistics; template class ImageRegrid; template class ImageInterface; template class ImageInterface; template class TempImage; template class TempImage; template class SubImage; template class SubImage; } casacore-3.7.1/images/Images/Images_1.fig000066400000000000000000000072211476623553700201140ustar00rootroot00000000000000#FIG 3.2 Landscape Center Inches Letter 100.00 Single -2 1200 2 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 9000 1800 10500 1800 10500 2700 9000 2700 9000 1800 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 4 4050 1800 4050 1500 9750 1500 9750 1800 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 3300 1800 4800 1800 4800 2700 3300 2700 3300 1800 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 3 3900 3000 4050 2850 4200 3000 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 4050 2700 4050 2850 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 4 3000 3300 3000 3000 7200 3000 7200 3300 2 3 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 10500 2250 10650 2100 10800 2250 10650 2400 10500 2250 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 11100 1800 12600 1800 12600 2700 11100 2700 11100 1800 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 2250 3300 3750 3300 3750 4200 2250 4200 2250 3300 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 4050 3300 5550 3300 5550 4200 4050 4200 4050 3300 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 6600 3300 8100 3300 8100 4200 6600 4200 6600 3300 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 10800 2250 11100 2250 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 9750 2700 9750 2850 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 10650 4500 10650 4800 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 6150 300 7650 300 7650 1200 6150 1200 6150 300 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 6900 1200 6900 1350 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 3 6750 1500 6900 1350 7050 1500 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 9900 3300 11400 3300 11400 4200 9900 4200 9900 3300 2 3 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 10500 4350 10650 4200 10800 4350 10650 4500 10500 4350 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 9900 4800 11400 4800 11400 5700 9900 5700 9900 4800 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 3 9600 3000 9750 2850 9900 3000 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 4800 3000 4800 3300 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 4 7500 3300 7500 3000 10650 3000 10650 3300 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 3 7200 4500 7350 4350 7500 4500 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 7500 4800 9000 4800 9000 5700 7500 5700 7500 4800 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 5700 4800 7200 4800 7200 5700 5700 5700 5700 4800 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 7350 4200 7350 4350 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 4 6450 4800 6450 4500 8250 4500 8250 4800 2 3 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 8100 5850 8250 5700 8400 5850 8250 6000 8100 5850 2 3 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 6300 5850 6450 5700 6600 5850 6450 6000 6300 5850 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 0 3 0 0 1.00 60.00 120.00 6300 5850 5100 5850 5100 4200 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 6150 375 7650 375 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 3300 1875 4800 1875 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 9000 1875 10500 1875 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 6600 3375 8100 3375 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 0 4 0 0 1.00 60.00 120.00 8250 6000 8250 6150 3300 6150 3300 4200 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 0 3 0 0 1.00 60.00 120.00 8250 6150 9300 6150 9300 2700 4 0 0 0 0 0 18 0.0000 4 195 1650 3225 2400 MaskedLattice\001 4 0 0 0 0 0 18 0.0000 4 195 1260 11250 2250 Coordinate\001 4 0 0 0 0 0 18 0.0000 4 195 1215 2400 3900 SubLattice\001 4 0 0 0 0 0 18 0.0000 4 195 795 6525 900 Lattice\001 4 0 0 0 0 0 18 0.0000 4 255 1335 4125 3900 LatticeExpr\001 4 0 0 0 0 0 18 0.0000 4 255 1350 9975 3900 PagedImage\001 4 0 0 0 0 0 18 0.0000 4 255 1350 9975 5400 PagedArray\001 4 0 0 0 0 0 18 0.0000 4 255 795 11475 2550 System\001 4 0 0 0 0 0 18 0.0000 4 195 1020 9225 2550 Interface\001 4 0 0 0 0 0 18 0.0000 4 255 690 9375 2250 Image\001 4 0 0 0 0 0 18 0.0000 4 255 1230 5850 5400 ImageExpr\001 4 0 0 0 0 0 18 0.0000 4 255 1545 6600 3900 MaskedImage\001 4 0 0 0 0 0 18 0.0000 4 255 1110 7725 5400 SubImage\001 casacore-3.7.1/images/Images/LELImageCoord.cc000066400000000000000000000161261476623553700206610ustar00rootroot00000000000000//# LELImageCoord.cc: The letter class for image coordinates //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LELImageCoord::LELImageCoord() {} LELImageCoord::LELImageCoord (const CoordinateSystem& coordinates, const ImageInfo& imageInfo, const Unit& unit, const RecordInterface& miscInfo) : coords_p (new CoordinateSystem(coordinates)), imageInfo_p (imageInfo), unit_p (unit), miscInfo_p (miscInfo) {} LELImageCoord::~LELImageCoord() {} Bool LELImageCoord::hasCoordinates() const { return True; } String LELImageCoord::classname() const { return "LELImageCoord"; } uInt LELImageCoord::getSpectralInfo (Vector& worldCoordinates, const IPosition& shape) const { // Find the coordinate number of the spectral coordinate. const CoordinateSystem& csys = coordinates(); Int which = csys.findCoordinate (Coordinate::SPECTRAL); if (which < 0) { throw AipsError ("LatticeExpr - no spectral coordinate found"); } // Get the pixel axis of the spectral coordinate. Vector pixelAxes = csys.pixelAxes (which); AlwaysAssert (pixelAxes.nelements() == 1, AipsError); if (pixelAxes(0) < 0 || pixelAxes(0) >= Int(shape.nelements())) { // No pixel axis, so there is a replacement value for this axis. // We can only get that by converting a pixel position to world. Vector worlds; AlwaysAssert (csys.toWorld (worlds, IPosition(shape.nelements(), 0)), AipsError); Vector worldAxes = csys.worldAxes (which); AlwaysAssert (worldAxes.nelements() == 1, AipsError); worldCoordinates.resize (1); worldCoordinates(0) = worlds(worldAxes(0)); } else { // Get the world values for the entire spectral axis. uInt length = shape(pixelAxes(0)); const SpectralCoordinate& crd = csys.spectralCoordinate (which); worldCoordinates.resize (length); for (uInt i=0; i (ImageExpr (LatticeExpr(expr), ""), region); case TpDouble: return SubImage (ImageExpr (LatticeExpr(expr), ""), region); case TpComplex: return SubImage (ImageExpr (LatticeExpr(expr), ""), region); case TpDComplex: return SubImage (ImageExpr (LatticeExpr(expr), ""), region); case TpBool: return SubImage (ImageExpr (LatticeExpr(expr), ""), region); default: throw (AipsError ("LELImageCoord::makeSubLattice - unknown datatype")); } return LatticeExprNode(); } LatticeExprNode LELImageCoord::makeExtendLattice (const LatticeExprNode& expr, const IPosition& newShape, const LELLattCoordBase& newCoord) const { // Get new coordinate system. const LELImageCoord* cptr = dynamic_cast(&newCoord); AlwaysAssert (cptr != 0, AipsError); const CoordinateSystem& newCsys = cptr->coordinates(); switch (expr.dataType()) { case TpFloat: return ExtendImage (ImageExpr(LatticeExpr(expr), ""), newShape, newCsys); case TpDouble: return ExtendImage (ImageExpr(LatticeExpr(expr), ""), newShape, newCsys); case TpComplex: return ExtendImage (ImageExpr(LatticeExpr(expr), ""), newShape, newCsys); case TpDComplex: return ExtendImage (ImageExpr(LatticeExpr(expr), ""), newShape, newCsys); case TpBool: return ExtendImage (ImageExpr(LatticeExpr(expr), ""), newShape, newCsys); default: throw (AipsError ("LELImageCoord::makeExtendLattice - unknown datatype")); } return LatticeExprNode(); } LatticeExprNode LELImageCoord::makeRebinLattice (const LatticeExprNode& expr, const IPosition& binning) const { switch (expr.dataType()) { case TpFloat: return RebinImage (ImageExpr (LatticeExpr(expr), ""), binning); case TpDouble: return RebinImage (ImageExpr (LatticeExpr(expr), ""), binning); case TpComplex: return RebinImage (ImageExpr (LatticeExpr(expr), ""), binning); case TpDComplex: return RebinImage (ImageExpr (LatticeExpr(expr), ""), binning); default: throw (AipsError ("LELLattCoord::makeRebinLattice - invalid datatype")); } return LatticeExprNode(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/images/Images/LELImageCoord.h000066400000000000000000000117501476623553700205210ustar00rootroot00000000000000//# LELImageCoord.h: The letter class for image coordinates //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_LELIMAGECOORD_H #define IMAGES_LELIMAGECOORD_H //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class LatticeExprNode; class LattRegionHolder; // // The letter class for image coordinates. // // // // // //
      • LELLattCoord // // // This class is a letter class for the envelope class // LELCoordinates. // It acts as the coordinates class for Lattice objects with // proper coordinates (like PagedImage). // // // It must be possible to handle image coordinates in a lattice. // expression. // //# //#
      • //# class LELImageCoord : public LELLattCoord { public: LELImageCoord(); LELImageCoord (const CoordinateSystem& coords, const ImageInfo& imageInfo, const Unit& unit, const RecordInterface& miscInfo); virtual ~LELImageCoord(); // Get the coordinates. const CoordinateSystem& coordinates() const; // Get the ImageInfo. const ImageInfo& imageInfo() const; // Get the brightness unit. const Unit& unit() const; // Get the MiscInfo. const TableRecord& miscInfo() const; // Create a SubLattice for an expression node. virtual LatticeExprNode makeSubLattice (const LatticeExprNode& expr, const LattRegionHolder& region) const; // Create an extension for an expression node. virtual LatticeExprNode makeExtendLattice (const LatticeExprNode& expr, const IPosition& newShape, const LELLattCoordBase& newCoord) const; // Create a rebinning for an expression node. virtual LatticeExprNode makeRebinLattice (const LatticeExprNode& expr, const IPosition& binning) const; // The class has true coordinates (thus returns True). virtual Bool hasCoordinates() const; // Get the coordinates of the spectral axis for the given shape. // It returns the pixel axis number of the spectral coordinates. // -1 indicates that there is no pixel spectral axis. // An exception is thrown if there are no world spectral coordinates. virtual uInt getSpectralInfo (Vector& worldCoordinates, const IPosition& shape) const; // The name of the class. virtual String classname() const; // Check how the coordinates of this and that compare. // The return value tells how they compare. //
        -1: this is subset //
        0: equal //
        1: this is superset //
        9: invalid (mismatch) virtual Int compare (const LELLattCoordBase& other) const; // Check how the coordinates of this and that image compare. // This function is used by conform to make a // double virtual dispatch possible. virtual Int doCompare (const LELImageCoord& other) const; private: std::shared_ptr coords_p; ImageInfo imageInfo_p; Unit unit_p; TableRecord miscInfo_p; }; inline const CoordinateSystem& LELImageCoord::coordinates() const { return *coords_p; } inline const ImageInfo& LELImageCoord::imageInfo() const { return imageInfo_p; } inline const Unit& LELImageCoord::unit() const { return unit_p; } inline const TableRecord& LELImageCoord::miscInfo() const { return miscInfo_p; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Images/MIRIADImage.cc000066400000000000000000000740251476623553700202250ustar00rootroot00000000000000//# MIRIADImage.cc: Class providing native access to MIRIAD images //# Copyright (C) 2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // set this to 1 or 0 to benchmark tiled access vs. xyio(native miriad) access #define USE_TILE 1 MIRIADImage::MIRIADImage (const String& name) : ImageInterface(), name_p (name), pPixelMask_p(0), hasBlanks_p (False), dataType_p (TpOther), fileOffset_p(0), isClosed_p (True) { setup(); } MIRIADImage::MIRIADImage (const String& name, const MaskSpecifier& maskSpec) : ImageInterface(), name_p (name), maskSpec_p (maskSpec), pPixelMask_p(0), hasBlanks_p (False), dataType_p (TpOther), fileOffset_p(0), isClosed_p (True) { setup(); } MIRIADImage::MIRIADImage (const MIRIADImage& other) : ImageInterface(other), name_p (other.name_p), maskSpec_p (other.maskSpec_p), unit_p (other.unit_p), rec_p (other.rec_p), pTiledFile_p(other.pTiledFile_p), pPixelMask_p(0), shape_p (other.shape_p), hasBlanks_p (other.hasBlanks_p), dataType_p (other.dataType_p), fileOffset_p(other.fileOffset_p), isClosed_p (other.isClosed_p) { if (other.pPixelMask_p != 0) { pPixelMask_p = other.pPixelMask_p->clone(); } } MIRIADImage& MIRIADImage::operator=(const MIRIADImage& other) // // Assignment. Uses reference semantics // { if (this != &other) { ImageInterface::operator= (other); // pTiledFile_p = other.pTiledFile_p; // Counted pointer // delete pPixelMask_p; pPixelMask_p = 0; if (other.pPixelMask_p != 0) { pPixelMask_p = other.pPixelMask_p->clone(); } // shape_p = other.shape_p; name_p = other.name_p; maskSpec_p = other.maskSpec_p; unit_p = other.unit_p; rec_p = other.rec_p; hasBlanks_p = other.hasBlanks_p; dataType_p = other.dataType_p; fileOffset_p= other.fileOffset_p; isClosed_p = other.isClosed_p; } return *this; } MIRIADImage::~MIRIADImage() { delete pPixelMask_p; } LatticeBase* MIRIADImage::openMIRIADImage (const String& name, const MaskSpecifier& spec) { return new MIRIADImage (name, spec); } void MIRIADImage::registerOpenFunction() { ImageOpener::registerOpenImageFunction (ImageOpener::MIRIAD, &openMIRIADImage); } ImageInterface* MIRIADImage::cloneII() const { return new MIRIADImage (*this); } String MIRIADImage::imageType() const { return "MIRIADImage"; } Bool MIRIADImage::isMasked() const { return hasBlanks_p; } const LatticeRegion* MIRIADImage::getRegionPtr() const { return 0; } IPosition MIRIADImage::shape() const { return shape_p.shape(); } uInt MIRIADImage::advisedMaxPixels() const { return shape_p.tileShape().product(); } IPosition MIRIADImage::doNiceCursorShape (uInt) const { return shape_p.tileShape(); } void MIRIADImage::resize(const TiledShape&) { throw (AipsError ("MIRIADImage::resize - a MIRIADImage is not writable")); } Bool MIRIADImage::doGetSlice(Array& buffer, const Slicer& section) { reopenIfNeeded(); pTiledFile_p->get (buffer, section); return False; // Not a reference } void MIRIADImage::doPutSlice (const Array&, const IPosition&, const IPosition&) { throw (AipsError ("MIRIADImage::putSlice - " "is not possible yet as MIRIADImage is not writable")); } #if 0 Bool MIRIADImage::setUnits (const Unit& unit) { unit_p = unit; return True; } Unit MIRIADImage::units() const { return unit_p; } #endif String MIRIADImage::name (Bool stripPath) const { Path path(name_p); if (stripPath) { return path.baseName(); } else { return path.absoluteName(); } } const RecordInterface& MIRIADImage::miscInfo() const { return rec_p; } Bool MIRIADImage::setMiscInfo(const RecordInterface& rec) { rec_p = rec; return True; } Bool MIRIADImage::isPersistent() const { return True; } Bool MIRIADImage::isPaged() const { return True; } Bool MIRIADImage::isWritable() const { // Its too hard to implement putMaskSlice becuase // magic blanking is used. It means we lose // the data values if the mask is put somewhere return False; } Bool MIRIADImage::ok() const { return True; } Bool MIRIADImage::doGetMaskSlice (Array& buffer, const Slicer& section) { if (!hasBlanks_p) { buffer.resize (section.length()); buffer = True; return False; } // reopenIfNeeded(); return pPixelMask_p->getSlice (buffer, section); } Bool MIRIADImage::hasPixelMask() const { return hasBlanks_p; } const Lattice& MIRIADImage::pixelMask() const { if (!hasBlanks_p) { throw (AipsError ("MIRIADImage::pixelMask - no pixelmask used")); } return *pPixelMask_p; } Lattice& MIRIADImage::pixelMask() { if (!hasBlanks_p) { throw (AipsError ("MIRIADImage::pixelMask - no pixelmask used")); } return *pPixelMask_p; } void MIRIADImage::tempClose() { if (! isClosed_p) { delete pPixelMask_p; pTiledFile_p.reset(); isClosed_p = True; } } void MIRIADImage::reopen() { if (isClosed_p) { open(); } } uInt MIRIADImage::maximumCacheSize() const { reopenIfNeeded(); return pTiledFile_p->maximumCacheSize() / ValType::getTypeSize(dataType_p); } void MIRIADImage::setMaximumCacheSize (uInt howManyPixels) { reopenIfNeeded(); const uInt sizeInBytes = howManyPixels * ValType::getTypeSize(dataType_p); pTiledFile_p->setMaximumCacheSize (sizeInBytes); } void MIRIADImage::setCacheSizeFromPath (const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath) { reopenIfNeeded(); pTiledFile_p->setCacheSize (sliceShape, windowStart, windowLength, axisPath); } void MIRIADImage::setCacheSizeInTiles (uInt howManyTiles) { reopenIfNeeded(); pTiledFile_p->setCacheSize (howManyTiles); } void MIRIADImage::clearCache() { if (! isClosed_p) { pTiledFile_p->clearCache(); } } void MIRIADImage::showCacheStatistics (ostream& os) const { reopenIfNeeded(); os << "MIRIADImage statistics : "; pTiledFile_p->showCacheStatistics (os); } void MIRIADImage::setup() { if (name_p.empty()) { throw AipsError("MIRIADImage: given file name is empty"); } if (! maskSpec_p.name().empty()) { throw AipsError("MIRIADImage " + name_p + " has no named masks"); } Path path(name_p); String fullName = path.absoluteName(); // cerr << "MIRIAD::setup name=" << fullName << endl; // Fish things out of the MIRIAD file CoordinateSystem cSys; IPosition shape; ImageInfo imageInfo; Unit brightnessUnit; getImageAttributes(cSys, shape, imageInfo, brightnessUnit, rec_p, hasBlanks_p, fullName); // set ImageInterface data setCoordsMember (cSys); setImageInfoMember (imageInfo); setUnitMember(brightnessUnit); // We need to put the history in some memory based LogSink // setLogMember(logSink); // Set MIRIADImage data unit_p = brightnessUnit; // MIRIAD 'image' items have an offset of 4 bytes if address directly via tiles fileOffset_p = 4; dataType_p = TpFloat; // Miriad uses only 32bit IEEE floating point // See if there is a mask hasBlanks_p = False; // for now.... // Form the tile shape shape_p = TiledShape (shape, TiledFileAccess::makeTileShape(shape)); // Open the image. open(); } void MIRIADImage::open() { Bool writable = False; Bool canonical = True; String iname = name_p + "/image"; // fails for very small miriad images !! // The tile shape must not be a subchunk in all dimensions pTiledFile_p = std::make_shared(iname, fileOffset_p, shape_p.shape(), shape_p.tileShape(), dataType_p, TSMOption(), writable, canonical); // Shares the pTiledFile_p pointer. if (hasBlanks_p) { // pPixelMask_p = new Lattice; // pPixelMask_p.resize(shape_p.shape()); } // Okay, it is open now. isClosed_p = False; } void MIRIADImage::getImageAttributes (CoordinateSystem& cSys, IPosition& shape, ImageInfo& imageInfo, Unit& brightnessUnit, Record&, Bool& hasBlanks, const String& name) { LogIO os(LogOrigin("MIRIADImage", "getImageAttributes", WHERE)); int naxis = MAXNAX, axes[MAXNAX]; // see miriad's maxdimc.h int i, ndim; // Projection projn; // Vector projp; // Projection::Type ptype; Double offset = 1.0; // miriad crpix 'origin' is 1-based Int rotationAxis = -1; xyopen_c(&tno_p, const_cast(name.chars()), "old", naxis, axes); // open miriad file rdhdi_c(tno_p,"naxis",&ndim,0); // for convenience, get ndim #if 0 // DEBUG: output what size cube we found cerr << "MIRIAD::getImageAttributes: ["; for (i=0; i 0) cerr << "x"; cerr << axes[i] ; } cerr << "]" << endl; #endif // crackHeader(cSys, shape, imageInfo, brightnessUnit, miscInfo, os); hasBlanks = FALSE; shape.resize(ndim); for (Int i=0; i cdelt, crval, crpix; Vector naxes; Vector ctype; Matrix pc(2,2); String tmps, digit; char tmps64[64]; cdelt.resize(ndim); crval.resize(ndim); ctype.resize(ndim); crpix.resize(ndim); // Units : miriad uses 'bunit' FITS like units without case cares. rdhda_c(tno_p, "bunit", tmps64,"",64); String cunit = tmps64; UnitMap::addFITS(); if (UnitVal::check(cunit)) { brightnessUnit = UnitMap::fromFITS(Unit(cunit)); } else { Unit t; brightnessUnit = t; os << "FITS unit " << cunit << " unknown to CASA - ignoring." << LogIO::POST; } // get the miriad axes descriptors for (i=0; i(tmps.chars()), tmps64, "", 64); ctype(i) = tmps64; // cerr << tmps << "=>" << ctype(i) << endl; tmps = "crval" + digit.toString(i+1); rdhdd_c(tno_p,const_cast(tmps.chars()), &crval(i), 0.0); // cerr << tmps << "=>" << crval(i) << endl; tmps = "cdelt" + digit.toString(i+1); rdhdd_c(tno_p,const_cast(tmps.chars()), &cdelt(i), 0.0); // cerr << tmps << "=>" << cdelt(i) << endl; tmps = "crpix" + digit.toString(i+1); rdhdd_c(tno_p,const_cast(tmps.chars()), &crpix(i), 0.0); crpix(i) -= offset; // cerr << tmps << "=>" << crpix(i) << endl; } Int longAxis=-1, latAxis=-1, stokesAxis=-1, spectralAxis=-1; for (i=0; i= 0) { os << LogIO::SEVERE << "More than one longitude axis is " "present in header!"; // return False; } longAxis = i; } else if (subDEC==String("DEC") || ctype(i).contains("LAT") || subDEC.contains("MM")) { if (latAxis >= 0) { os << LogIO::SEVERE << "More than one latitude axis is " "present in header!"; // return False; // we already have a latitude axis! } latAxis = i; } else if (ctype(i).contains("STOKES")) { stokesAxis = i; } else if (ctype(i).contains("FREQ") || ctype(i).contains("FELO") || ctype(i).contains("VELO")) { spectralAxis = i; } } // We must have longitude AND latitude // (really, what about certain PV diagrams ???) if (longAxis >= 0 && latAxis < 0) { os << LogIO::SEVERE << "We have a longitude axis but no latitude axis!"; // return False; } if (latAxis >= 0 && longAxis < 0) { os << LogIO::SEVERE << "We have a latitude axis but no longitude axis!"; // return False; } // DIRECTION String proj1, proj2; Bool isGalactic = False; if (longAxis >= 0) { proj1 = ctype(longAxis); proj2 = ctype(latAxis); if (proj1.contains("GLON")) isGalactic = True; // Get rid of the first 4 characters, e.g., RA-- const Int l1 = proj1.length(); const Int l2 = proj2.length(); proj1 = String(proj1.at(4, l1-4)); proj2 = String(proj2.at(4, l2-4)); // Get rid of leading -'s proj1.gsub(Regex("^-*"), String("")); proj2.gsub(Regex("^-*"), String("")); // Get rid of spaces proj1.gsub(Regex(" *"), String("")); proj2.gsub(String(" "), String("")); if (proj1=="" && proj2=="") { // We must abandon making a celestial coordinate if there is no // projection. Defaulting to cartesian is the wrong thing to do // We must make a Linear Coordinate from it. os << WHERE << LogIO::WARN << "No projection has been defined so cannot make a Celestial Coordinate\n" "from this miriad header. Will make a LinearCoordinate instead" << LogIO::POST; longAxis = -1; latAxis = -1; } } if (longAxis >= 0) { if (proj1 != proj2) { // Maybe instead I should switch to CAR, or use the first? os << LogIO::SEVERE << "Longitude and latitude axes have different" " projections (" << proj1 << "!=" << proj2 << ")" << LogIO::POST; // return False; } // OK, let's make our Direction coordinate and add it to the // coordinate system. We'll worry about transposing later. MIRIAD // uses radians by convention // First, work out what the projection actually is. // Special case NCP - now SIN with parameters Vector projp; Projection::Type ptype; ptype = Projection::SIN; if (proj1 == "NCP") { os << LogIO::NORMAL << "NCP projection is now SIN projection in" " WCS.\nmiriad readers will not handle this correctly." << LogIO::POST; ptype = Projection::SIN; projp.resize(2); // According to Greisen and Calabretta projp(0) = 0.0; projp(1) = 1.0/tan(crval(latAxis)); } else { ptype = Projection::type(proj1); if (ptype == Projection::N_PROJ) { os << LogIO::SEVERE << "Unknown projection: (" << proj1 << ")"; //return False; } // projp header keyword not used in miriad } // OK, now try making the projection Projection projn; try { projn = Projection(ptype, projp); } catch (std::exception& x) { os << LogIO::SEVERE << "Error forming projection, maybe the " "wrong number of parameters\n(" << x.what() << ")" << LogIO::POST; //return False; } // fish out LONG/LATPOLE (use defaults, since miriad does not // use those in wcs headers Double longPole = 999.0; Double latPole = 999.0; // DEFAULT MDirection::Types radecsys = MDirection::J2000; if (isGalactic) { radecsys = MDirection::GALACTIC; } else { Double epoch; rdhdd_c(tno_p,"epoch", &epoch, 2000.0); if (::casacore::near(epoch, 1950.0)) { radecsys = MDirection::B1950; } else if (::casacore::near(epoch, 2000.0)) { radecsys = MDirection::J2000; } } // make sure this isn't a lie for miriad... pc(0,0) = pc(1,1) = 1.0; pc(0,1) = pc(1,0) = 0.0; Matrix dirpc(2,2); //cerr << "long/lat = " << longAxis << " " << latAxis << endl; dirpc(0,0) = pc(longAxis, longAxis); dirpc(0,1) = pc(longAxis, latAxis); dirpc(1,0) = pc(latAxis, longAxis); dirpc(1,1) = pc(latAxis, latAxis); // watch for cdelt=0 - its okay if that axis is degenerate // and (crpix+offset)=1 and rotationAxis < 0 = i.e. the only // pixel on that axis is the reference pixel and there is // no rotation specified - then cdelt=1 on that axis. If that // isn't done, that coord. can't be constructed because the // PC matrix will be reported as singular since its first // multiplied by cdelt before its used and in this case, that // doesn't matter since other pixels on that axis are never used. if (::casacore::near(cdelt(latAxis), 0.0) && ::casacore::near(crpix(latAxis)+offset, 1.0) && rotationAxis < 0) { cdelt(latAxis) = 1.0; // degrees } // if (::casacore::near(cdelt(longAxis), 0.0) && ::casacore::near(crpix(longAxis)+offset, 1.0) && rotationAxis < 0) { cdelt(longAxis) = 1.0; // degrees } DirectionCoordinate dir(radecsys, projn, crval(longAxis), crval(latAxis), cdelt(longAxis), cdelt(latAxis), dirpc, crpix(longAxis), crpix(latAxis), longPole, latPole); cSys.addCoordinate(dir); } // potential bug to track down: // - a miriad cube that has been processes with 'velsw axis=freq' has slightly // wrong labels when printed with dImageSummary if (spectralAxis >= 0) { // cerr << "Hey, process spectralAxis = " << spectralAxis << endl; // see SpectralCoordinate::fromFITS(tmp, error, header, spectralAxis,os); // and FITSSpectralUtil::fromFITSHeader // so, as opposed to doing it here, it should be done parallel to those places // Int velref = 2; // Default is optical + topocentric ("OBS") if (ctype(spectralAxis).contains("VELO")) { velref = 258; // radio + OBS } // Try to work out OPTICAL/RADIO/. Default to Optical String type(ctype(spectralAxis).before(4)); MDoppler::Types velocityPreference = MDoppler::OPTICAL; if (velref > 256) { velocityPreference = MDoppler::RADIO; } Double restFrequency; rdhdd_c(tno_p,"restfreq", &restFrequency, -1.0); restFrequency *= 1e9; // miriad uses GHz // convert the velocity frame tag in ctype to a reference frame String spectralAxisQualifier; if (ctype(spectralAxis).length() <= 5) { spectralAxisQualifier = ""; } else { spectralAxisQualifier = ctype(spectralAxis).after(4); } Double referenceChannel = crpix(spectralAxis); Double referenceFrequency = 0.0; Double deltaFrequency = 0.0; Vector frequencies; MFrequency::Types refFrame; Bool ok = FITSSpectralUtil::frameFromTag(refFrame, spectralAxisQualifier, velref); if (!ok) { if (spectralAxisQualifier == "") { if ((velref%256) >= 0) { // no tag and velref is unrecognized os << LogIO::SEVERE << "Illegal value for VELREF(" << velref << ") assuming topocentric" << LogIO::POST; } } else { // unrecognized tag os << LogIO::SEVERE << "Unknown spectral reference frame " << spectralAxisQualifier << ". Assuming topocentric." << LogIO::POST; } } Int nChan = shape(spectralAxis); Double delt = cdelt(spectralAxis); Double rval = crval(spectralAxis); Double rpix = crpix(spectralAxis); if (ctype(spectralAxis).contains("FREQ")) { delt *= 1e9; rval *= 1e9; referenceFrequency = rval; deltaFrequency = delt; frequencies.resize(nChan); for (Int i=0; i= 0) { if (shape(stokesAxis)>4) { os << "Stokes axis longer than 4 pixels. This is not acceptable" << LogIO::EXCEPTION; //return False; } Vector stokes(shape(stokesAxis)); for (Int k=0; k order(ndim); Int nspecial = 0; if (longAxis >= 0) nspecial++; if (latAxis >= 0) nspecial++; if (stokesAxis >= 0) nspecial++; if (spectralAxis >= 0) nspecial++; #if 0 // I can't figure this out now, there is something wrong here for miriad Int linused = 0; for (i=0; i= 0) { // stokes is axis 0 if no dir, otherwise 2 order(i) = 3; // 3 for MIRIAD !!! 2 for fits? } else { order(i) = 0; } } else if (i == spectralAxis) { if (longAxis >= 0 && stokesAxis >= 0) { order(i) = 2; // stokes and dir : (3 for fits, 2 for miriad?) } else if (longAxis >= 0) { order(i) = 2; // dir only } else if (stokesAxis >= 0) { order(i) = 1; // stokes but no dir } else { order(i) = 0; // neither stokes or dir } } else { order(i) = nspecial + linused; linused++; } } // cSys.transpose(order, order); #endif // ImageInfo. String btype; rdhda_c(tno_p, "btype", tmps64,"",64); btype = tmps64; ImageInfo::ImageTypes type = ImageInfo::MiriadImageType (btype); if (type!=ImageInfo::Undefined) imageInfo.setImageType(type); // Double bmaj, bmin, bpa; rdhdd_c(tno_p, "bmaj", &bmaj, 0.0); rdhdd_c(tno_p, "bmin", &bmin, 0.0); rdhdd_c(tno_p, "bpa", &bpa, 0.0); if (bmaj>0.0 && bmin>0.0 && abs(bpa)>0.0) { Quantity qbmaj(bmaj,Unit("rad")); Quantity qbmin(bmin,Unit("rad")); Quantity qbpa(bpa,Unit("deg")); imageInfo.setRestoringBeam(GaussianBeam(qbmaj, qbmin, qbpa)); } // ObsInfo ObsInfo oi; // DATE-OBS Double obstime; rdhdd_c(tno_p, "obstime", &obstime, -1.0); // cerr << "obstime=" << obstime << endl; if (obstime > -1.0) { obstime -= 2400000.5; // make it MJD ("d") MVEpoch mve(Quantity(obstime,"d")); MEpoch mep(mve,MEpoch::UTC); // miriad uses JDN (in UTC) -- no good oi.setObsDate(mep); } // TELESCOP String telescop; rdhda_c(tno_p, "telescop", tmps64,"",64); telescop = tmps64; if (!telescop.empty()) { oi.setTelescope(telescop); } // cSys.setObsInfo(oi); xyclose_c(tno_p); } } //# NAMESPACE CASACORE - END casacore-3.7.1/images/Images/MIRIADImage.h000066400000000000000000000231701476623553700200620ustar00rootroot00000000000000//# MIRIADImage.h: Class providing native access to MIRIAD images //# Copyright (C) 2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_MIRIADIMAGE_H #define IMAGES_MIRIADIMAGE_H //# Includes #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Lattice; // class MaskSpecifier; class IPosition; class Slicer; class CoordinateSystem; class FITSMask; class FitsInput; // // Class providing native access to MIRIAD images. // // // // // //
      • ImageInterface //
      • FITSMask // // // This class provides native access to MIRIAD images. // // // A MIRIADImage provides native access to MIRIAD images by accessing them // with the TiledFileAccess class. -- or -- the native miriad I/O routines. // The MIRIADImage is read only. -- really -- ?? // // // // // MIRIADImage im("cube1"); // LogIO logger(or); // ImageStatistics stats(im, logger); // Bool ok = stats.display(); // Display statistics // // // // This provides native access to MIRIAD images. // //# //# class MIRIADImage: public ImageInterface { public: // Construct a MIRIADImage from the disk MIRIAD dataset name and apply mask. explicit MIRIADImage(const String& name); // Construct a MIRIADImage from the disk MIRIAD file name and apply mask or not. MIRIADImage(const String& name, const MaskSpecifier&); // Copy constructor (reference semantics) MIRIADImage(const MIRIADImage& other); // Destructor does nothing ~MIRIADImage(); // Assignment (reference semantics) MIRIADImage& operator=(const MIRIADImage& other); // Function to open a MIRIAD image. static LatticeBase* openMIRIADImage (const String& name, const MaskSpecifier&); // Register the open function. static void registerOpenFunction(); //# ImageInterface virtual functions // Make a copy of the object with new (reference semantics). virtual ImageInterface* cloneII() const; // Get the image type (returns MIRIADImage). virtual String imageType() const; // Function which changes the shape of the MIRIADImage. // Throws an exception as MIRIADImage is not writable. virtual void resize(const TiledShape& newShape); // Functions which get and set the units associated with the image // pixels (i.e. the "brightness" unit). Initially the unit is empty. // Although the MIRIADimage is not writable, you can change the // unit in the MIRIADImage object, but it will not be changed // in the MIRIAD disk file. // #if 0 virtual Bool setUnits(const Unit& newUnits); virtual Unit units() const; #endif // // Often we have miscellaneous information we want to attach to an image. // Although MIRIADImage is not writable, you can set a new // MiscInfo record, but it will not be stored with the MIRIAD file // virtual const RecordInterface &miscInfo() const; virtual Bool setMiscInfo(const RecordInterface &newInfo); // //# MaskedLattice virtual functions // Has the object really a mask? The MIRIADImage always // has a pixel mask and never has a region mask so this // should always return True virtual Bool isMasked() const; // MIRIADimage always has a pixel mask so should return True virtual Bool hasPixelMask() const; // Get access to the pixelmask. MIRIADImage always has a pixel mask. // virtual const Lattice& pixelMask() const; virtual Lattice& pixelMask(); // // Do the actual get of the mask data. The return value is always // False, thus the buffer does not reference another array. virtual Bool doGetMaskSlice (Array& buffer, const Slicer& section); // Get the region used. There is no region. // Always returns 0. virtual const LatticeRegion* getRegionPtr() const; //# Lattice virtual functions // Do the actual get of the data. // Returns False as the data do not reference another Array virtual Bool doGetSlice (Array& buffer, const Slicer& theSlice); // The MIRIADImage is not writable, so this throws an exception. virtual void doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride); //# LatticeBase virtual functions // The lattice is paged to disk. virtual Bool isPaged() const; // The lattice is persistent. virtual Bool isPersistent() const; // The MIRIADImage is not writable. virtual Bool isWritable() const; // Returns the name of the disk file. virtual String name (Bool stripPath=False) const; // return the shape of the MIRIADImage virtual IPosition shape() const; // Returns the maximum recommended number of pixels for a cursor. This is // the number of pixels in a tile. virtual uInt advisedMaxPixels() const; // Help the user pick a cursor for most efficient access if they only want // pixel values and don't care about the order or dimension of the // cursor. virtual IPosition doNiceCursorShape (uInt maxPixels) const; // Temporarily close the image. virtual void tempClose(); // Reopen a temporarily closed image. virtual void reopen(); // Check class invariants. virtual Bool ok() const; // Return the (internal) data type (TpFloat or TpShort). DataType dataType () const { return dataType_p; } // Maximum size - not necessarily all used. In pixels. virtual uInt maximumCacheSize() const; // Set the maximum (allowed) cache size as indicated. virtual void setMaximumCacheSize (uInt howManyPixels); // Set the cache size as to "fit" the indicated path. virtual void setCacheSizeFromPath (const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath); // Set the actual cache size for this Array to be be big enough for the // indicated number of tiles. This cache is not shared with PagedArrays // in other rows and is always clipped to be less than the maximum value // set using the setMaximumCacheSize member function. // tiles. Tiles are cached using a first in first out algorithm. virtual void setCacheSizeInTiles (uInt howManyTiles); // Clears and frees up the caches, but the maximum allowed cache size is // unchanged from when setCacheSize was called virtual void clearCache(); // Report on cache success. virtual void showCacheStatistics (ostream& os) const; private: String name_p; // filename, as given Int tno_p; // miriad file handle MaskSpecifier maskSpec_p; Unit unit_p; Record rec_p; std::shared_ptr pTiledFile_p; Lattice* pPixelMask_p; // Float scale_p; // Float offset_p; // Short magic_p; TiledShape shape_p; Bool hasBlanks_p; DataType dataType_p; // always float's for miriad Int64 fileOffset_p; // always 4 for direct (tiled) access Bool isClosed_p; // Reopen the image if needed. void reopenIfNeeded() const { if (isClosed_p) const_cast(this)->reopen(); } // Setup the object (used by constructors). void setup(); // Open the image (used by setup and reopen). void open(); // Fish things out of the MIRIAD file void getImageAttributes (CoordinateSystem& cSys, IPosition& shape, ImageInfo& info, Unit& brightnessUnit, Record& miscInfo, Bool& hasBlanks, const String& name); // void crackHeader (CoordinateSystem& cSys, IPosition& shape, ImageInfo& imageInfo, Unit& brightnessUnit, Record& miscInfo, LogIO&os); // }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Images/MaskSpecifier.cc000066400000000000000000000026221476623553700210340ustar00rootroot00000000000000//# MaskSpecifier.cc: Class to hold a region of interest in an image //# Copyright (C) 1998,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# All functions are inlined. } //# NAMESPACE CASACORE - END casacore-3.7.1/images/Images/MaskSpecifier.h000066400000000000000000000063031476623553700206760ustar00rootroot00000000000000//# MaskSpecifier.h: Class to specify which mask to use in an image //# Copyright (C) 1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_MASKSPECIFIER_H #define IMAGES_MASKSPECIFIER_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Class to specify which mask to use in an image. // // // // // // The only purpose of MaskSpecifier is to reduce the number of constructors // in PagedImage. It makes it possible to specify if no mask, the default // mask, or another mask should be used when opening an existing PagedImage // object. //

        // Because the constructors automatically converts from a Bool or // a String, the user does not need to be aware of MaskSpecifier. // // // The number of constructors in PagedImage would be many more // without this class. It would need one taking a Bool and a String. // Because C++ converts a const char* to Bool instead of String, // a const char* would also be needed multiple times. // //# //#

      • //# class MaskSpecifier { public: // Default constructor. // It tells if the default mask should or no mask be used. MaskSpecifier (Bool useDefaultMask = True) : itsFlag(useDefaultMask) {} // Construct from a string. // It tells to use an alternative mask. An empty name means no mask. //# Note the const Char* constructor is needed, otherwise "name" //# is converted to a Bool by the compiler. // MaskSpecifier (const Char* maskName) : itsFlag(False), itsName(maskName) {} MaskSpecifier (const String& maskName) : itsFlag(False), itsName(maskName) {} // // Give the flag or name. // Bool useDefault() const { return itsFlag; } const String& name() const { return itsName; } // private: Bool itsFlag; String itsName; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Images/PagedImage.h000066400000000000000000000436521476623553700201440ustar00rootroot00000000000000//# PagedImage.h: read, store and manipulate astronomical images //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_PAGEDIMAGE_H #define IMAGES_PAGEDIMAGE_H //# Includes #include #include #include #include #include #include #include //# Forward Declarations #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Read, store, and manipulate astronomical images. // // // // // //
      • CoordinateSystem //
      • ImageInterface //
      • Lattice //
      • LatticeIterator //
      • LatticeNavigator //
      • ImageRegion // // // The PagedImage name comes from its role as the Image class with paging // from persistent memory. Users are thus invited to treat the // PagedImage instances like Casacore Lattices // // // All Casacore Images are Lattices. They may be treated like any other Lattice; // getSlice(...), putSlice(...), LatticeIterator for iterating, etc. // ArrayImages contain a map, a mask for that map, and coordinate // information. This provides a Lattice interface for images and their // respective coordinates. Additional functionality is defined by the // ImageInterface class. // // You can use the global function imagePixelType to determine // what the pixel type of an image is before you open the image if your // code can work with Images of many possible types, or for error checking. // // // // This example shows how to create a mask for an image, fill it, and // make it known to the image. // // // Open the image (as readonly for the moment). // PagedImage myimage ("image.name"); // // Create a mask for the image. // // The mask will be stored in a subtable of the image. // LCPagedMask mask (RegionHandler::makeMask (myimage, "mask.name")); // // Fill the mask with whatever values (e.g. all True). // mask.set (True); // // Make the mask known to the image (with name mask1). // myimage.defineRegion ("mask1", mask, RegionHandler::Masks); // // Make the mask the default mask for this image. // myimage.setDefaultMask ("mask1"); // // It is possible to create as many masks as one likes. They can all // be defined as masks for the image (with different names, of course). // However, only one of them can be the default mask (the mask used // by default when the image is opened). When another mask has to be // used, one can do two things: //
          //
        • Use setDefaultMask to make the other mask the default mask. // This is advisable when the change should be more or less permanent. //
        • Open the PagedImage without using a default mask. Thereafter // a SubImage object can be created // from the PagedImage and the mask. This is advisable when it the // mask has to be used only one time. //
        //
        // // The size of astronomical data can be very large. The ability to fit an // entire image into random access memory cannot be guaranteed. Paging from // disk pieces of the image appeared to be the way to deal with this problem. // // // // When you make a new PagedImage, and you are transferring // information from some other PagedImage, be aware that you // must copy, manually, things like miscInfo, imageInfo, units, // logSink (history) to the new file. // // //
      • The CoordinateSystem::store() function returns a TableRecord. That // TableRecord should be stored in the same row as our image. This will // allow ImageStack members to have their own coordinate frames. // template class PagedImage: public ImageInterface { public: // Construct a new Image from shape and coordinate information. // Data will be stored in the argument table. PagedImage (const TiledShape& mapShape, const CoordinateSystem& coordinateInfo, Table& table, uInt rowNumber = 0); // Construct a new Image from shape and coordinate information. Table // will be stored in the named file. PagedImage (const TiledShape& mapShape, const CoordinateSystem& coordinateInfo, const String& nameOfNewFile, uInt rowNumber = 0); // Construct a new Image from shape and coordinate information. Table // will be stored in the named file. // The lock options may be specified // PagedImage (const TiledShape& mapShape, const CoordinateSystem& coordinateInfo, const String& nameOfNewFile, TableLock::LockOption, uInt rowNumber = 0); PagedImage (const TiledShape& mapShape, const CoordinateSystem& coordinateInfo, const String& nameOfNewFile, const TableLock& lockOptions, uInt rowNumber = 0); // // Reconstruct an image from a pre-existing file. // By default the default pixelmask (if available) is used. explicit PagedImage (Table& table, MaskSpecifier = MaskSpecifier(), uInt rowNumber = 0); // Reconstruct an image from a pre-existing file. // By default the default pixelmask (if available) is used. explicit PagedImage (const String& filename, MaskSpecifier = MaskSpecifier(), uInt rowNumber = 0); // Reconstruct an image from a pre-existing file with Locking. // By default the default pixelmask (if available) is used. // PagedImage (const String& filename, TableLock::LockOption, MaskSpecifier = MaskSpecifier(), uInt rowNumber = 0); PagedImage (const String& filename, const TableLock& lockOptions, MaskSpecifier = MaskSpecifier(), uInt rowNumber = 0); // // Copy constructor (reference semantics). PagedImage (const PagedImage& other); ~PagedImage(); // Assignment operator (reference semantics). PagedImage& operator= (const PagedImage& other); // Make a copy of the object (reference semantics). virtual ImageInterface* cloneII() const; // Return the name of this derived class. static String className() { return "PagedImage"; } // Get the image type (returns name of this derived class). virtual String imageType() const; // A PagedImage is always persistent. virtual Bool isPersistent() const; // A PagedImage is always paged to disk. virtual Bool isPaged() const; // Is the PagedImage writable? virtual Bool isWritable() const; // Does the image object use a pixelmask? virtual Bool hasPixelMask() const; // Get access to the pixelmask used. // An exception is thrown if the image does not use a pixelmask. // virtual const Lattice& pixelMask() const; virtual Lattice& pixelMask(); // // Get a pointer the default pixelmask object used with this image. // It returns 0 if no default pixelmask is used. virtual const LatticeRegion* getRegionPtr() const; // Set the default pixelmask to the mask with the given name // (which has to exist in the "masks" group). // If the image table is writable, the setting is persistent by writing // the name as a keyword. // If the given regionName is the empty string, // the default pixelmask is unset. virtual void setDefaultMask (const String& maskName); // Use the mask as specified. // If a mask was already in use, it is replaced by the new one. virtual void useMask (MaskSpecifier = MaskSpecifier()); // Function to change the name of the Table file on disk. // PagedImage is given a file name at construction time. You may change // that name here. void rename (const String& newName); // Return the current Table name. By default this includes the full path. // the path preceding the file name can be stripped off on request. virtual String name (Bool stripPath=False) const; // Return the current TableColumn row number. uInt rowNumber() const; // Return the shape of the image. virtual IPosition shape() const; // Change the shape of the image (N.B. the data is thrown away). virtual void resize (const TiledShape& newShape); // Function which extracts an array from the map. virtual Bool doGetSlice (Array& buffer, const Slicer& theSlice); // Function to replace the values in the map with soureBuffer. virtual void doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride); // Replace every element, x, of the lattice with the result of f(x). // you must pass in the address of the function -- so the function // must be declared and defined in the scope of your program. // Both versions of apply require a function that accepts a single // argument of type T (the Lattice template actual type) and returns // a result of the same type. The first apply expects a function with // an argument passed by value; the second expects the argument to // be passed by const reference. The first form ought to run faster // for the built-in types, which may be an issue for large Lattices // stored in memory, where disk access is not an issue. // virtual void apply (T (*function)(T)); virtual void apply (T (*function)(const T& )); virtual void apply (const Functional& function); // // Add a lattice to this image. PagedImage& operator+= (const Lattice& other); // Function which sets the units associated with the image // pixels (i.e. the "brightness" unit). setUnits() returns // False if it cannot set the unit for some reason (e.g. the underlying // file is not writable). virtual Bool setUnits (const Unit& newUnits); // Return the table holding the data. Table& table() { return map_p.table(); } // Flushes the new coordinate system to disk if the table is writable. virtual Bool setCoordinateInfo (const CoordinateSystem& coords); // Check for symmetry in data members. virtual Bool ok() const; // These are the true implementations of the paran operator. // Not for public use // virtual T getAt (const IPosition& where) const; virtual void putAt (const T& value, const IPosition& where); // // Replace the miscinfo in the PagedImage. // It can fail if, e.g., the underlying table is not writable. virtual Bool setMiscInfo (const RecordInterface& newInfo); // The ImageInfo object contains some miscellaneous information about the // image, which unlike that stored in MiscInfo, has a standard list of // things, such as the restoring beam. // Note that setImageInfo REPLACES the information with the new information. // It can fail if, e.g., the underlying table is not writable. virtual Bool setImageInfo(const ImageInfo& info); // Get access to the attribute handler. // If a handler keyword does not exist yet, it is created if // createHandler is set. // Otherwise the handler is empty and no groups can be created for it. virtual ImageAttrHandler& attrHandler (Bool createHandler=False); // Remove a region/mask belonging to the image from the given group // (which can be Any). // If a mask removed is the default mask, the image gets unmasked. //
        Optionally an exception is thrown if the region does not exist. virtual void removeRegion (const String& name, RegionHandler::GroupType = RegionHandler::Any, Bool throwIfUnknown = True); // This is the implementation of the letter for the envelope Iterator // class. Not for public use . virtual LatticeIterInterface* makeIter (const LatticeNavigator& navigator, Bool useRef) const; // Returns the maximum recommended number of pixels for a cursor. This is // the number of pixels in a tile. virtual uInt advisedMaxPixels() const; // Help the user pick a cursor for most efficient access. virtual IPosition doNiceCursorShape (uInt maxPixels) const; // Maximum size - not necessarily all used. In pixels. virtual uInt maximumCacheSize() const; // Set the maximum (allowed) cache size as indicated. virtual void setMaximumCacheSize (uInt howManyPixels); // Set the cache size as to "fit" the indicated path. virtual void setCacheSizeFromPath (const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath); // Set the actual cache size for this Array to be be big enough for the // indicated number of tiles. This cache is not shared with PagedArrays // in other rows and is always clipped to be less than the maximum value // set using the setMaximumCacheSize member function. // tiles. Tiles are cached using a first in first out algorithm. virtual void setCacheSizeInTiles (uInt howManyTiles); // Clears and frees up the caches, but the maximum allowed cache size is // unchanged from when setCacheSize was called virtual void clearCache(); // Report on cache success. virtual void showCacheStatistics (ostream& os) const; // Handle the (un)locking. // Unlocking also unlocks the logtable and a possible mask table. // Locking only locks the image itself. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; // // Resynchronize the PagedImage object with the table contents. // The logtable and possible mask table are also synchronized if // they do not have a readlock. //
        This function is only useful if no read-locking is used, ie. // if the table lock option is UserNoReadLocking or AutoNoReadLocking. // In that cases the table system does not acquire a read-lock, thus // does not synchronize itself automatically. virtual void resync(); // Flush the data. virtual void flush(); // Close the Image and associated files temporarily. // It'll be reopened automatically when needed or when // reopen is called explicitly. virtual void tempClose(); // If needed, reopen a temporarily closed Image. virtual void reopen(); private: // Function to return the internal Table object to the RegionHandler. static Table& getTable (void* imagePtr, Bool writable); // This must be called in every constructor and place where the image // is attached to a new image. void attach_logtable(); void open_logtable(); void restoreUnits (const TableRecord& rec); void restoreMiscInfo (const TableRecord& rec); void restoreImageInfo (const TableRecord& rec); void restoreAll (const TableRecord& rec); void check_conformance (const Lattice& other); void reopenRW(); void setTableType(); void applyMaskSpecifier (const MaskSpecifier&); void applyMask (const String& maskName); void makePagedImage (const TiledShape& mapShape, const CoordinateSystem& coordinateInfo, const String& nameOfNewFile, const TableLock& lockOptions, uInt rowNumber); void makePagedImage (const String& filename, const TableLock& lockOptions, const MaskSpecifier&, uInt rowNumber); const Table& table() const { return const_cast*>(this)->table(); } PagedArray map_p; LatticeRegion* regionPtr_p; ImageAttrHandlerCasa itsAttrHandler; //# Make members of parent class known. public: using ImageInterface::logSink; using ImageInterface::logger; using ImageInterface::imageInfo; using ImageInterface::coordinates; using ImageInterface::getDefaultMask; using ImageInterface::hasRegion; using ImageInterface::getImageRegionPtr; protected: using ImageInterface::setCoordsMember; using ImageInterface::setMiscInfoMember; using ImageInterface::setLogMember; using ImageInterface::setUnitMember; using ImageInterface::setImageInfoMember; }; //# A nasty - the column name is hard-coded into this function, needs to //# be centralized somewhere. // Determine the pixel type in the PagedImage contained in // fileName. If the file doesn't appear to be a Table or cannot // be opened, TpOther is returned. // //# Declare extern templates for often used types. extern template class PagedImage; extern template class PagedImage; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/images/Images/PagedImage.tcc000066400000000000000000000543571476623553700204720ustar00rootroot00000000000000//# PagedImage.cc: defines the PagedImage class //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_PAGEDIMAGE_TCC #define IMAGES_PAGEDIMAGE_TCC #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template PagedImage::PagedImage (const TiledShape& shape, const CoordinateSystem& coordinateInfo, Table& table, uInt rowNumber) : ImageInterface(RegionHandlerTable(getTable, this)), map_p (shape, table, "map", rowNumber), regionPtr_p (0) { attach_logtable(); AlwaysAssert(setCoordinateInfo(coordinateInfo), AipsError); setTableType(); } template PagedImage::PagedImage (const TiledShape& shape, const CoordinateSystem& coordinateInfo, const String& filename, TableLock::LockOption lockMode, uInt rowNumber) : ImageInterface(RegionHandlerTable(getTable, this)), regionPtr_p (0) { makePagedImage (shape, coordinateInfo, filename, TableLock(lockMode), rowNumber); } template PagedImage::PagedImage (const TiledShape& shape, const CoordinateSystem& coordinateInfo, const String& filename, const TableLock& lockOptions, uInt rowNumber) : ImageInterface(RegionHandlerTable(getTable, this)), regionPtr_p (0) { makePagedImage (shape, coordinateInfo, filename, lockOptions, rowNumber); } template void PagedImage::makePagedImage (const TiledShape& shape, const CoordinateSystem& coordinateInfo, const String& filename, const TableLock& lockOptions, uInt rowNumber) { SetupNewTable newtab (filename, TableDesc(), Table::New); Table tab(newtab, lockOptions); map_p = PagedArray (shape, tab, "map", rowNumber); attach_logtable(); AlwaysAssert(setCoordinateInfo(coordinateInfo), AipsError); setTableType(); } template PagedImage::PagedImage (const TiledShape& shape, const CoordinateSystem& coordinateInfo, const String& filename, uInt rowNumber) : ImageInterface(RegionHandlerTable(getTable, this)), regionPtr_p (0) { SetupNewTable newtab (filename, TableDesc(), Table::New); Table tab(newtab); map_p = PagedArray (shape, tab, "map", rowNumber); attach_logtable(); AlwaysAssert(setCoordinateInfo(coordinateInfo), AipsError); setTableType(); } template PagedImage::PagedImage (Table& table, MaskSpecifier spec, uInt rowNumber) : ImageInterface(RegionHandlerTable(getTable, this)), map_p (table, "map", rowNumber), regionPtr_p (0) { attach_logtable(); restoreAll (table.keywordSet()); applyMaskSpecifier (spec); } template PagedImage::PagedImage (const String& filename, MaskSpecifier spec, uInt rowNumber) : ImageInterface(RegionHandlerTable(getTable, this)), regionPtr_p (0) { Table tab(filename); map_p = PagedArray(tab, "map", rowNumber); attach_logtable(); restoreAll (tab.keywordSet()); applyMaskSpecifier (spec); } template PagedImage::PagedImage (const String& filename, const TableLock& lockOptions, MaskSpecifier spec, uInt rowNumber) : ImageInterface(RegionHandlerTable(getTable, this)), regionPtr_p (0) { makePagedImage (filename, lockOptions, spec, rowNumber); } template PagedImage::PagedImage (const String& filename, TableLock::LockOption lockMode, MaskSpecifier spec, uInt rowNumber) : ImageInterface(RegionHandlerTable(getTable, this)), regionPtr_p (0) { makePagedImage (filename, TableLock(lockMode), spec, rowNumber); } template void PagedImage::makePagedImage (const String& filename, const TableLock& lockOptions, const MaskSpecifier& spec, uInt rowNumber) { Table tab(filename, lockOptions); map_p = PagedArray(tab, "map", rowNumber); attach_logtable(); restoreAll (tab.keywordSet()); applyMaskSpecifier (spec); } template PagedImage::PagedImage (const PagedImage& other) : ImageInterface(other), map_p (other.map_p), regionPtr_p (0) { if (other.regionPtr_p != 0) { regionPtr_p = new LatticeRegion (*other.regionPtr_p); } } template PagedImage::~PagedImage() { // Close the logger here in case the image table is going to be deleted. delete regionPtr_p; logger().tempClose(); } template PagedImage& PagedImage::operator=(const PagedImage& other) { if (this != &other) { ImageInterface::operator= (other); map_p = other.map_p; delete regionPtr_p; regionPtr_p = 0; if (other.regionPtr_p != 0) { regionPtr_p = new LatticeRegion (*other.regionPtr_p); } } return *this; } template ImageInterface* PagedImage::cloneII() const { return new PagedImage (*this); } template void PagedImage::restoreAll (const TableRecord& rec) { // Restore the coordinates. CoordinateSystem* restoredCoords = CoordinateSystem::restore(rec, "coords"); AlwaysAssert(restoredCoords != 0, AipsError); setCoordsMember (*restoredCoords); delete restoredCoords; // Restore the image info. restoreImageInfo (rec); // Restore the units. restoreUnits (rec); // Restore the miscinfo. restoreMiscInfo (rec); } template String PagedImage::imageType() const { return className(); } template Bool PagedImage::isPersistent() const { return True; } template Bool PagedImage::isPaged() const { return True; } template Bool PagedImage::isWritable() const { return map_p.isWritable(); } template void PagedImage::reopenRW() { //# First reopen if needed. map_p.reopen(); //# Open for write if not done yet and if writable. if (!table().isWritable() && isWritable()) { table().reopenRW(); } } template Bool PagedImage::hasPixelMask() const { return (regionPtr_p != 0 && regionPtr_p->hasMask()); } template const Lattice& PagedImage::pixelMask() const { if (regionPtr_p == 0) { throw (AipsError ("PagedImage::pixelMask - no pixelmask used")); } return *regionPtr_p; } template Lattice& PagedImage::pixelMask() { if (regionPtr_p == 0) { throw (AipsError ("PagedImage::pixelMask - no pixelmask used")); } return *regionPtr_p; } template const LatticeRegion* PagedImage::getRegionPtr() const { return regionPtr_p; } template void PagedImage::setDefaultMask (const String& regionName) { // Reopen for write when needed and possible. reopenRW(); // Use the new region as the image's mask. applyMask (regionName); // Store the new default name. ImageInterface::setDefaultMask (regionName); } template void PagedImage::useMask (MaskSpecifier spec) { applyMaskSpecifier (spec); } template void PagedImage::applyMaskSpecifier (const MaskSpecifier& spec) { // Use default mask if told to do so. // If it does not exist, use no mask. String name = spec.name(); if (spec.useDefault()) { name = getDefaultMask(); if (! hasRegion (name, RegionHandler::Masks)) { name = String(); } } applyMask (name); } template void PagedImage::applyMask (const String& maskName) { // No region if no mask name is given. if (maskName.empty()) { delete regionPtr_p; regionPtr_p = 0; return; } // Reconstruct the ImageRegion object. // Turn the region into lattice coordinates. ImageRegion* regPtr = getImageRegionPtr (maskName, RegionHandler::Masks); LatticeRegion* latReg = new LatticeRegion (regPtr->toLatticeRegion (coordinates(), shape())); delete regPtr; // The mask has to cover the entire image. if (latReg->shape() != shape()) { delete latReg; throw (AipsError ("PagedImage::setDefaultMask - region " + maskName + " does not cover the full image")); } // Replace current by new mask. delete regionPtr_p; regionPtr_p = latReg; } template void PagedImage::rename (const String& newName) { table().rename (newName, Table::New); } template String PagedImage::name (Bool stripPath) const { return map_p.name (stripPath); } template uInt PagedImage::rowNumber() const { return map_p.rowNumber(); } template IPosition PagedImage::shape() const { return map_p.shape(); } template void PagedImage::resize (const TiledShape& newShape) { if (newShape.shape().nelements() != coordinates().nPixelAxes()) { throw(AipsError("PagedImage::resize: coordinate info is " "the incorrect shape.")); } map_p.resize (newShape); } template Bool PagedImage::setCoordinateInfo (const CoordinateSystem& coords) { Bool ok = ImageInterface::setCoordinateInfo(coords); if (ok) { reopenRW(); Table& tab = table(); if (tab.isWritable()) { // Update the coordinates if (tab.keywordSet().isDefined("coords")) { tab.rwKeywordSet().removeField("coords"); } if (!(coordinates().save(tab.rwKeywordSet(), "coords"))) { LogIO os; os << LogIO::SEVERE << "Error saving coordinates in image " << name() << LogIO::POST; ok = False; } } else { LogIO os; os << LogIO::SEVERE << "Image " << name() << " is not writable; not saving coordinates" << LogIO::POST; } } return ok; } template Bool PagedImage::doGetSlice(Array& buffer, const Slicer& theSlice) { return map_p.doGetSlice(buffer, theSlice); } template void PagedImage::doPutSlice(const Array& sourceBuffer, const IPosition& where, const IPosition& stride) { // if (throughmask_p || !mask_p) { map_p.putSlice(sourceBuffer,where,stride); // } else if (mask_p) { // Array map; //Array mask; //IPosition shape(sourceBuffer.shape()); //mask_p->getSlice(mask, where, shape, stride, True); //map_p.getSlice(map, where, shape, stride, True); // use maskedarrays to do all the work. //map(mask==False) = sourceBuffer; //map_p.putSlice(map,where,stride); // } else { // throw(AipsError("PagedImage::putSlice - throughmask==False but no " // "mask exists.")); // } } // apply a function to all elements of the map template void PagedImage::apply(T (*function)(T)) { map_p.apply(function); } // apply a function to all elements of a const map; template void PagedImage::apply(T (*function)(const T&)) { map_p.apply(function); } template void PagedImage::apply(const Functional& function) { map_p.apply(function); } template T PagedImage::getAt(const IPosition& where) const { return map_p(where); } template void PagedImage::putAt(const T& value, const IPosition& where) { map_p.putAt (value, where); } template void PagedImage::restoreMiscInfo (const TableRecord& rec) { if (rec.isDefined("miscinfo") && rec.dataType("miscinfo") == TpRecord) { setMiscInfoMember (rec.asRecord ("miscinfo")); } } template Bool PagedImage::setMiscInfo (const RecordInterface& newInfo) { setMiscInfoMember (newInfo); reopenRW(); Table& tab = table(); if (! tab.isWritable()) { return False; } if (tab.keywordSet().isDefined("miscinfo")) { tab.rwKeywordSet().removeField("miscinfo"); } tab.rwKeywordSet().defineRecord("miscinfo", newInfo); return True; } template LatticeIterInterface* PagedImage::makeIter (const LatticeNavigator& navigator, Bool useRef) const { return map_p.makeIter (navigator, useRef); } template Bool PagedImage::ok() const { Int okay = (map_p.ndim() == coordinates().nPixelAxes()); return okay ? True : False; } template PagedImage& PagedImage::operator+= (const Lattice& other) { check_conformance(other); // Use LEL, so a mask is also handled. LatticeExpr expr(*this + other); this->copyData (expr); return *this; } template void PagedImage::attach_logtable() { open_logtable(); } template void PagedImage::open_logtable() { // Open logtable as readonly if main table is not writable. Table& tab = table(); setLogMember (LoggerHolder (name() + "/logtable", tab.isWritable())); // Insert the keyword if possible and if it does not exist yet. if (tab.isWritable() && ! tab.keywordSet().isDefined ("logtable")) { tab.rwKeywordSet().defineTable("logtable", Table(name() + "/logtable")); } } template Bool PagedImage::setUnits(const Unit& newUnits) { setUnitMember (newUnits); reopenRW(); Table& tab = table(); if (! tab.isWritable()) { return False; } if (tab.keywordSet().isDefined("units")) { tab.rwKeywordSet().removeField("units"); } tab.rwKeywordSet().define("units", newUnits.getName()); return True; } template void PagedImage::restoreUnits (const TableRecord& rec) { Unit retval; String unitName; if (rec.isDefined("units")) { if (rec.dataType("units") != TpString) { LogIO os; os << LogOrigin("PagedImage", "units()", WHERE) << "'units' keyword in image table is not a string! Units not restored." << LogIO::SEVERE << LogIO::POST; } else { rec.get("units", unitName); } } if (! unitName.empty()) { // OK, non-empty unit, see if it's valid, if not try some known things to // make a valid unit out of it. if (! UnitVal::check(unitName)) { // Beam and Pixel are the most common undefined units UnitMap::putUser("Pixel",UnitVal(1.0),"Pixel unit"); UnitMap::putUser("Beam",UnitVal(1.0),"Beam area"); } if (! UnitVal::check(unitName)) { // OK, maybe we need FITS UnitMap::addFITS(); } if (!UnitVal::check(unitName)) { // I give up! LogIO os; UnitMap::putUser(unitName, UnitVal(1.0, UnitDim::Dnon), unitName); os << LogIO::WARN << "FITS unit \"" << unitName << "\" unknown to CASA - will treat it as non-dimensional." << LogIO::POST; retval.setName(unitName); retval.setValue(UnitVal(1.0, UnitDim::Dnon)); } else { retval = Unit(unitName); } } setUnitMember (retval); } template void PagedImage::removeRegion (const String& name, RegionHandler::GroupType type, Bool throwIfUnknown) { reopenRW(); // Remove the default mask if it is the region to be removed. if (name == getDefaultMask()) { setDefaultMask (String()); } ImageInterface::removeRegion (name, type, throwIfUnknown); } template void PagedImage::check_conformance(const Lattice& other) { if (! this->conform(other)) { throw AipsError("Shapes of image " + name() + " and other lattice do not conform"); } } template uInt PagedImage::maximumCacheSize() const { return map_p.maximumCacheSize(); } template void PagedImage::setMaximumCacheSize(uInt howManyPixels) { map_p.setMaximumCacheSize(howManyPixels); if (regionPtr_p != 0) { regionPtr_p->setMaximumCacheSize(howManyPixels); } } template void PagedImage::setCacheSizeFromPath(const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath) { map_p.setCacheSizeFromPath(sliceShape, windowStart, windowLength, axisPath); if (regionPtr_p != 0) { regionPtr_p->setCacheSizeFromPath(sliceShape, windowStart, windowLength, axisPath); } } template void PagedImage::setCacheSizeInTiles (uInt howManyTiles) { map_p.setCacheSizeInTiles (howManyTiles); if (regionPtr_p != 0) { regionPtr_p->setCacheSizeInTiles (howManyTiles); } } template void PagedImage::clearCache() { map_p.clearCache(); if (regionPtr_p != 0) { regionPtr_p->clearCache(); } } template void PagedImage::showCacheStatistics(ostream& os) const { os << "Pixel statistics : "; map_p.showCacheStatistics(os); if (regionPtr_p != 0) { os << "Pixelmask statistics : "; regionPtr_p->showCacheStatistics(os); } } template uInt PagedImage::advisedMaxPixels() const { return map_p.advisedMaxPixels(); } template IPosition PagedImage::doNiceCursorShape(uInt maxPixels) const { return map_p.niceCursorShape(maxPixels); } template void PagedImage::setTableType() { TableInfo& info(table().tableInfo()); const String reqdType = info.type(TableInfo::PAGEDIMAGE); if (info.type() != reqdType) { info.setType(reqdType); } const String reqdSubType = info.subType(TableInfo::PAGEDIMAGE); if (info.subType() != reqdSubType) { info.setSubType(reqdSubType); } } template Table& PagedImage::getTable (void* imagePtr, Bool writable) { PagedImage* im = static_cast*>(imagePtr); if (writable) { im->reopenRW(); } return im->map_p.table(); } template Bool PagedImage::lock (FileLocker::LockType type, uInt nattempts) { return map_p.lock (type, nattempts); } template void PagedImage::unlock() { map_p.unlock(); logger().unlock(); if (regionPtr_p != 0) { regionPtr_p->unlock(); } } template Bool PagedImage::hasLock (FileLocker::LockType type) const { return map_p.hasLock (type); } template void PagedImage::resync() { map_p.resync(); logger().resync(); if (regionPtr_p != 0 && !regionPtr_p->hasLock (FileLocker::Read)) { regionPtr_p->resync(); } } template void PagedImage::flush() { itsAttrHandler.flush(); map_p.flush(); logger().flush(); if (regionPtr_p != 0) { regionPtr_p->flush(); } } template void PagedImage::tempClose() { map_p.tempClose(); logger().tempClose(); if (regionPtr_p != 0) { regionPtr_p->tempClose(); } } template void PagedImage::reopen() { map_p.reopen(); if (regionPtr_p != 0) { regionPtr_p->reopen(); } } template Bool PagedImage::setImageInfo (const ImageInfo& info) { // Set imageinfo in base class. Bool ok = ImageInterface::setImageInfo(info); if (ok) { // Make persistent in table keywords. reopenRW(); Table& tab = table(); if (tab.isWritable()) { // Delete existing one if there. if (tab.keywordSet().isDefined("imageinfo")) { tab.rwKeywordSet().removeField("imageinfo"); } // Convert info to a record and save as keyword. TableRecord rec; String error; if (imageInfo().toRecord(error, rec)) { tab.rwKeywordSet().defineRecord("imageinfo", rec); } else { // Could not convert to record. LogIO os; os << LogIO::SEVERE << "Error saving ImageInfo in image " << name() << "; " << error << LogIO::POST; ok = False; } } else { // Table not writable. LogIO os; os << LogIO::SEVERE << "Image " << name() << " is not writable; not saving ImageInfo" << LogIO::POST; } } return ok; } template void PagedImage::restoreImageInfo (const TableRecord& rec) { if (rec.isDefined("imageinfo")) { String error; ImageInfo info; Bool ok = info.fromRecord (error, rec.asRecord("imageinfo")); if (ok) { setImageInfoMember (info); } else { LogIO os; os << LogIO::WARN << "Failed to restore the ImageInfo in image " << name() << "; " << error << LogIO::POST; } } } template ImageAttrHandler& PagedImage::attrHandler (Bool createHandler) { return itsAttrHandler.attachTable (table(), createHandler); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Images/PagedImage2.cc000066400000000000000000000036411476623553700203560ustar00rootroot00000000000000//# PagedImage.cc: defines the PagedImage class (non-template code) //# Copyright (C) 1994,1995,1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN DataType imagePixelType(const String &fileName) { DataType retval = TpOther; if (Table::isReadable(fileName)) { try { TableDesc desc; TableUtil::getLayout(desc, fileName); ColumnDesc cdesc = desc["map"]; retval = cdesc.dataType(); } catch (std::exception& x) { // Nothing } } return retval; } } //# NAMESPACE CASACORE - END casacore-3.7.1/images/Images/RebinImage.h000066400000000000000000000147311476623553700201570ustar00rootroot00000000000000//# RebinImage.h: rebin an image //# Copyright (C) 2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_REBINIMAGE_H #define IMAGES_REBINIMAGE_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class RebinLattice; class LogIO; // // Rebin an image // // // // // // // // //
      • ImageInterface //
      • RebinLattice // // // // Class RebinImage can be used to rebin (data averaged over bin) an image // by integer amounts per axis. // // // // // IPosition factors(2,2,2); // PagedImage imageIn(String("myImage")): // RebinLattice rb(imageIn, factors); // IPosition shapeOut = rb.shape(); // TiledShape tShapeOut(shapeOut); // TempImage imageOut(tShapeOut, rb.coordinates()); // LatticeUtilities::copyDataAndMask(os, imageOut, rb); // ImageUtilities::copyMiscellaneous (imageOut, imageIn); // // // // // Users like to rebin images... // // // // template class RebinImage: public ImageInterface { public: // Default constructor (object useless) RebinImage (); // Constructor. The bin factors don't have to be integral. Anything left over // at the end is treated as a full bin. RebinImage (const ImageInterface&, const IPosition& factors); // Copy constructor (reference semantics). RebinImage (const RebinImage& other); virtual ~RebinImage(); // Assignment (reference semantics). RebinImage& operator= (const RebinImage& other); // Make a copy of the object (reference semantics). // virtual ImageInterface* cloneII() const; // // Get the image type (returns name of derived class). virtual String imageType() const; // Is the RebinImage masked? // It is if its parent image is masked. virtual Bool isMasked() const; // Does the image object have a pixelmask? // It does if its parent has a pixelmask. virtual Bool hasPixelMask() const; // Get access to the pixelmask in use (thus to the pixelmask of the parent). // An exception is thrown if the parent does not have a pixelmask. // virtual const Lattice& pixelMask() const; virtual Lattice& pixelMask(); // // Get the region used (always returns 0). virtual const LatticeRegion* getRegionPtr() const; // A RebinImage is not persistent. virtual Bool isPersistent() const; // Is the RebinImage paged to disk? virtual Bool isPaged() const; // An RebinImage is not writable virtual Bool isWritable() const; // Returns the shape of the RebinImage virtual IPosition shape() const; // This function returns the recommended maximum number of pixels to // include in the cursor of an iterator. virtual uInt advisedMaxPixels() const; // Function which changes the shape of the RebinImage. // Throws an exception as resizing an RebinImage is not possible. virtual void resize(const TiledShape& newShape); // Return the name of the parent ImageInterface object. virtual String name (Bool stripPath=False) const; // Check class invariants. virtual Bool ok() const; // Get access to the attribute handler (of the parent image). // If a handler keyword does not exist yet, it is created if // createHandler is set. // Otherwise the handler is empty and no groups can be created for it. virtual ImageAttrHandler& attrHandler (Bool createHandler=False); // Do the actual getting of an array of values. // Non-unit strides are not yet supported. virtual Bool doGetSlice (Array& buffer, const Slicer& section); // Putting data is not possible as the lattice is not writable. virtual void doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride); // Get a section of the mask. // Non-unit strides are not yet supported. virtual Bool doGetMaskSlice (Array& buffer, const Slicer& section); // This function is used by the LatticeIterator class to generate an // iterator of the correct type for this Lattice. Not recommended // for general use. virtual LatticeIterInterface* makeIter (const LatticeNavigator& navigator, Bool useRef) const; // Get the best cursor shape. virtual IPosition doNiceCursorShape (uInt maxPixels) const; // Handle the (un)locking and syncing, etc. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; virtual void resync(); virtual void flush(); virtual void tempClose(); virtual void reopen(); // private: //# itsImagePtr points to the parent image. ImageInterface* itsImagePtr; RebinLattice* itsRebinPtr; //# Make members of parent class known. public: using ImageInterface::logger; protected: using ImageInterface::setCoordsMember; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/images/Images/RebinImage.tcc000066400000000000000000000147621476623553700205050ustar00rootroot00000000000000//# RebinImage.cc: rebin an image //# Copyright (C) 2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_REBINIMAGE_TCC #define IMAGES_REBINIMAGE_TCC #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template RebinImage::RebinImage () : itsImagePtr (0), itsRebinPtr (0) {} template RebinImage::RebinImage (const ImageInterface& image, const IPosition& factors) : itsImagePtr (image.cloneII()) { ThrowIf ( image.imageInfo().hasMultipleBeams() && image.coordinates().hasSpectralAxis() && factors[image.coordinates().spectralAxisNumber()] != 1, "This image has multiple beams. The spectral axis cannot be rebinned" ); itsRebinPtr = new RebinLattice(image, factors); // CoordinateSystem cSys = CoordinateUtil::makeBinnedCoordinateSystem (factors, image.coordinates(), True); setCoordsMember (cSys); // this->setImageInfoMember (itsImagePtr->imageInfo()); this->setMiscInfoMember (itsImagePtr->miscInfo()); this->setUnitMember (itsImagePtr->units()); logger().addParent (itsImagePtr->logger()); } template RebinImage::RebinImage (const RebinImage& other) : ImageInterface (other), itsImagePtr (other.itsImagePtr->cloneII()) { itsRebinPtr = new RebinLattice (*other.itsRebinPtr); } template RebinImage::~RebinImage() { delete itsImagePtr; delete itsRebinPtr; } template RebinImage& RebinImage::operator= (const RebinImage& other) { if (this != &other) { delete itsImagePtr; itsImagePtr = 0; delete itsRebinPtr; itsRebinPtr = 0; ImageInterface::operator= (other); itsImagePtr = other.itsImagePtr->cloneII(); itsRebinPtr = new RebinLattice (*other.itsRebinPtr); } return *this; } template ImageInterface* RebinImage::cloneII() const { return new RebinImage (*this); } template String RebinImage::imageType() const { return "RebinImage"; } template Bool RebinImage::ok() const { return itsRebinPtr->ok(); } template Bool RebinImage::isMasked() const { return itsRebinPtr->isMasked(); } template Bool RebinImage::isPersistent() const { return itsRebinPtr->isPersistent(); } template Bool RebinImage::isPaged() const { return itsRebinPtr->isPaged(); } template Bool RebinImage::isWritable() const { return itsRebinPtr->isWritable(); } template Bool RebinImage::hasPixelMask() const { return itsRebinPtr->hasPixelMask(); } template const Lattice& RebinImage::pixelMask() const { return itsRebinPtr->pixelMask(); } template Lattice& RebinImage::pixelMask() { return itsRebinPtr->pixelMask(); } template const LatticeRegion* RebinImage::getRegionPtr() const { return itsRebinPtr->getRegionPtr(); } template IPosition RebinImage::shape() const { return itsRebinPtr->shape(); } template void RebinImage::resize (const TiledShape&) { throw (AipsError ("RebinImage::resize is not possible")); } template String RebinImage::name (Bool stripPath) const { return itsImagePtr->name (stripPath); } template ImageAttrHandler& RebinImage::attrHandler (Bool createHandler) { return itsImagePtr->attrHandler (createHandler); } template Bool RebinImage::doGetSlice (Array& buffer, const Slicer& section) { return itsRebinPtr->doGetSlice (buffer, section); } template void RebinImage::doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride) { itsRebinPtr->doPutSlice (sourceBuffer, where, stride); } template Bool RebinImage::doGetMaskSlice (Array& buffer, const Slicer& section) { return itsRebinPtr->doGetMaskSlice (buffer, section); } template uInt RebinImage::advisedMaxPixels() const { return itsRebinPtr->advisedMaxPixels(); } template IPosition RebinImage::doNiceCursorShape (uInt maxPixels) const { return itsRebinPtr->niceCursorShape (maxPixels); } template LatticeIterInterface* RebinImage::makeIter (const LatticeNavigator& navigator, Bool useRef) const { return itsRebinPtr->makeIter (navigator, useRef); } template Bool RebinImage::lock (FileLocker::LockType type, uInt nattempts) { return itsRebinPtr->lock (type, nattempts); } template void RebinImage::unlock() { itsRebinPtr->unlock(); itsImagePtr->unlock(); } template Bool RebinImage::hasLock (FileLocker::LockType type) const { return itsRebinPtr->hasLock (type); } template void RebinImage::resync() { itsRebinPtr->resync(); itsImagePtr->resync(); } template void RebinImage::flush() { itsImagePtr->flush(); } template void RebinImage::tempClose() { itsRebinPtr->tempClose(); itsImagePtr->tempClose(); logger().tempClose(); } template void RebinImage::reopen() { itsImagePtr->reopen(); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Images/SubImage.h000066400000000000000000000234521476623553700176510ustar00rootroot00000000000000//# SubImage.h: A (masked) subset of an ImageInterface object //# Copyright (C) 1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_SUBIMAGE_H #define IMAGES_SUBIMAGE_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class IPosition; class LattRegionHolder; class Slicer; template class SubLattice; class LatticeNavigator; template class LatticeIterInterface; class String; // // A (masked) subset of an ImageInterface object. // // // // // // // // //
      • ImageInterface //
      • SubLattice // // // // Class SubImage has to be used to apply a region or mask to an image. // Several functions are inherited from SubLattice and not declared // in this class. //

        // Using an AxesSpecifier object // it is possible to remove some or all degenerate axes (i.e. axes // with length 1) to get an image with a lower dimensionality. // // // // // // // // // // // // template class SubImage: public ImageInterface { public: // The default constructor SubImage(); // Create a SubImage from a Image. // This results in a SubImage without a real mask. //
        The "const Image" version yields a non-writable SubImage, // while for the non-const version one has to specify if the SubImage // should be writable (if the original image is non-writable, the // SubImage is always set to non-writable). //
        If preserveAxesOrder is True, the axes order will be preserved. This // is only important in cases where pixel axes are to be dropped, if not // the axes order will be preserved. If False and pixel axes are dropped, // the order of the coordinates will be preserved, but not necessarily // the axes. // SubImage (const ImageInterface& image, AxesSpecifier=AxesSpecifier(), Bool preserveAxesOrder=False); SubImage (ImageInterface& image, Bool writableIfPossible, AxesSpecifier=AxesSpecifier(), Bool preserveAxesOrder=False); // // Create a SubImage from the given Image and region. //
        An exception is thrown if the image shape used in the region // differs from the shape of the image. // SubImage (const ImageInterface& image, const LattRegionHolder& region, AxesSpecifier=AxesSpecifier(), Bool preserveAxesOrder=False); SubImage (ImageInterface& image, const LattRegionHolder& region, Bool writableIfPossible, AxesSpecifier=AxesSpecifier(), Bool preserveAxesOrder=False); // // Create a SubImage from the given Image and slicer. // The slicer can be strided. //
        An exception is thrown if the slicer exceeds the image shape. // SubImage (const ImageInterface& image, const Slicer& slicer, AxesSpecifier=AxesSpecifier(), Bool preserveAxesOrder=False); SubImage (ImageInterface& image, const Slicer& slicer, Bool writableIfPossible, AxesSpecifier=AxesSpecifier(), Bool preserveAxesOrder=False); // // Copy constructor (reference semantics). SubImage (const SubImage& other); virtual ~SubImage(); // Assignment (reference semantics). SubImage& operator= (const SubImage& other); // Make a copy of the object (reference semantics). // virtual ImageInterface* cloneII() const; // // Get the image type (returns name of derived class). virtual String imageType() const; // Is the SubImage masked? // It is if its parent image or its region is masked. virtual Bool isMasked() const; // Does the image object have a pixelmask? // It does if its parent has a pixelmask. virtual Bool hasPixelMask() const; // Get access to the pixelmask in use (thus to the pixelmask of the parent). // An exception is thrown if the parent does not have a pixelmask. // virtual const Lattice& pixelMask() const; virtual Lattice& pixelMask(); // // A SubImage is persistent if no region is applied to the parent image. // That is true if the region has the same shape as the parent image // and the region has no mask. virtual Bool isPersistent() const; // Is the SubImage paged to disk? virtual Bool isPaged() const; // Can the lattice data be referenced as an array section? virtual Bool canReferenceArray() const; // Is the SubImage writable? virtual Bool isWritable() const; // Get the region/mask object describing this subImage. virtual const LatticeRegion* getRegionPtr() const; // Returns the shape of the SubImage including all degenerate axes // (i.e. axes with a length of one). virtual IPosition shape() const; // Returns the number of axes in this SubImage. This includes all // degenerate axes. virtual uInt ndim() const; // Returns the total number of elements in this SubImage. virtual size_t nelements() const; // returns a value of "True" if this instance of Lattice and 'other' have // the same shape, otherwise returns a value of "False". virtual Bool conform (const Lattice& other) const; // This function returns the recommended maximum number of pixels to // include in the cursor of an iterator. virtual uInt advisedMaxPixels() const; // Get access to the attribute handler (of the parent image). // If a handler keyword does not exist yet, it is created if // createHandler is set. // Otherwise the handler is empty and no groups can be created for it. virtual ImageAttrHandler& attrHandler (Bool createHandler=False); // Get or put a single element in the lattice. // virtual T getAt (const IPosition& where) const; virtual void putAt (const T& value, const IPosition& where); // // Function which changes the shape of the SubImage. // Throws an exception as resizing a SubImage is not possible. virtual void resize(const TiledShape& newShape); // Return the name of the parent ImageInterface object. virtual String name (Bool stripPath=False) const; // Check class invariants. virtual Bool ok() const; // Do the actual getting of an array of values. virtual Bool doGetSlice (Array& buffer, const Slicer& section); // Do the actual getting of an array of values. virtual void doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride); // Get a section of the mask. virtual Bool doGetMaskSlice (Array& buffer, const Slicer& section); // This function is used by the LatticeIterator class to generate an // iterator of the correct type for this Lattice. Not recommended // for general use. virtual LatticeIterInterface* makeIter (const LatticeNavigator& navigator, Bool useRef) const; // Get the best cursor shape. virtual IPosition doNiceCursorShape (uInt maxPixels) const; // Handle the (un)locking and syncing, etc. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; virtual void resync(); virtual void flush(); virtual void tempClose(); virtual void reopen(); // private: // Set the coordinates. // It removes world axes if the subimage has axes removed. //
        If preserveAxesOrder is True and axes are dropped, it will preserve // the order of the axes as well as the order of the coordinates. void setCoords (const CoordinateSystem& coords, Bool preserveAxesOrder); void setCoords (const CoordinateSystem& coords); // Set the other members to the one in itsImagePtr. void setMembers(); // Set the members to the subset (in particular, the beamset). void setMembers (const Slicer& slicer); // Helper void convertIPosition(Vector& x, const IPosition& pos) const; //# itsImagePtr points to the parent image. ImageInterface* itsImagePtr; SubLattice* itsSubLatPtr; //# Make members of parent class known. public: using ImageInterface::logger; protected: using ImageInterface::setCoordsMember; }; //# Declare extern templates for often used types. extern template class SubImage; extern template class SubImage; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/images/Images/SubImage.tcc000066400000000000000000000271351476623553700201750ustar00rootroot00000000000000//# SubImage.cc: A subset of a Image //# Copyright (C) 1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_SUBIMAGE_TCC #define IMAGES_SUBIMAGE_TCC #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template SubImage::SubImage() : itsImagePtr (0), itsSubLatPtr (0) {} template SubImage::SubImage (const ImageInterface& image, AxesSpecifier axesSpec, Bool preserveAxesOrder) : itsImagePtr (image.cloneII()) { itsSubLatPtr = new SubLattice (image, axesSpec); setCoords (image.coordinates(), preserveAxesOrder); setMembers(); } template SubImage::SubImage (ImageInterface& image, Bool writableIfPossible, AxesSpecifier axesSpec, Bool preserveAxesOrder) : itsImagePtr (image.cloneII()) { itsSubLatPtr = new SubLattice (image, writableIfPossible, axesSpec); setCoords (image.coordinates(), preserveAxesOrder); setMembers(); } template SubImage::SubImage (const ImageInterface& image, const LattRegionHolder& region, AxesSpecifier axesSpec, Bool preserveAxesOrder) : itsImagePtr (image.cloneII()) { itsSubLatPtr = new SubLattice (image, region.toLatticeRegion(image.coordinates(), image.shape()), axesSpec); const Slicer& slicer = itsSubLatPtr->getRegionPtr()->slicer(); // Vector blc, inc; convertIPosition(blc, slicer.start()); convertIPosition(inc, slicer.stride()); // CoordinateSystem subCoords (image.coordinates().subImage (blc, inc, slicer.length().asVector())); setCoords (subCoords, preserveAxesOrder); setMembers (slicer); } template SubImage::SubImage (ImageInterface& image, const LattRegionHolder& region, Bool writableIfPossible, AxesSpecifier axesSpec, Bool preserveAxesOrder) : itsImagePtr (image.cloneII()) { itsSubLatPtr = new SubLattice (image, region.toLatticeRegion(image.coordinates(), image.shape()), writableIfPossible, axesSpec); const Slicer& slicer = itsSubLatPtr->getRegionPtr()->slicer(); // Vector blc, inc; convertIPosition(blc, slicer.start()); convertIPosition(inc, slicer.stride()); // CoordinateSystem subCoords (image.coordinates().subImage (blc, inc, slicer.length().asVector())); setCoords (subCoords, preserveAxesOrder); setMembers (slicer); } template SubImage::SubImage (const ImageInterface& image, const Slicer& slicer, AxesSpecifier axesSpec, Bool preserveAxesOrder) : itsImagePtr (image.cloneII()) { itsSubLatPtr = new SubLattice (image, slicer, axesSpec); const Slicer& refslicer = itsSubLatPtr->getRegionPtr()->slicer(); // Vector blc, inc; convertIPosition(blc, refslicer.start()); convertIPosition(inc, refslicer.stride()); CoordinateSystem subCoords (image.coordinates().subImage (blc, inc, refslicer.length().asVector())); setCoords (subCoords, preserveAxesOrder); setMembers (refslicer); } template SubImage::SubImage (ImageInterface& image, const Slicer& slicer, Bool writableIfPossible, AxesSpecifier axesSpec, Bool preserveAxesOrder) : itsImagePtr (image.cloneII()) { itsSubLatPtr = new SubLattice (image, slicer, writableIfPossible, axesSpec); const Slicer& refslicer = itsSubLatPtr->getRegionPtr()->slicer(); // Vector blc, inc; convertIPosition(blc, refslicer.start()); convertIPosition(inc, refslicer.stride()); CoordinateSystem subCoords (image.coordinates().subImage (blc, inc, refslicer.length().asVector())); setCoords (subCoords, preserveAxesOrder); setMembers (refslicer); } template SubImage::SubImage (const SubImage& other) : ImageInterface (other), itsImagePtr (other.itsImagePtr->cloneII()) { itsSubLatPtr = new SubLattice (*other.itsSubLatPtr); } template SubImage::~SubImage() { delete itsImagePtr; delete itsSubLatPtr; } template SubImage& SubImage::operator= (const SubImage& other) { if (this != &other) { ImageInterface::operator= (other); delete itsImagePtr; itsImagePtr = other.itsImagePtr->cloneII(); delete itsSubLatPtr; itsSubLatPtr = new SubLattice (*other.itsSubLatPtr); } return *this; } template ImageInterface* SubImage::cloneII() const { return new SubImage (*this); } template void SubImage::setMembers() { this->setImageInfoMember (itsImagePtr->imageInfo()); this->setMiscInfoMember (itsImagePtr->miscInfo()); this->setUnitMember (itsImagePtr->units()); logger().addParent (itsImagePtr->logger()); } template void SubImage::setMembers (const Slicer& slicer) { // Reset to a subset of the beams in the beamset. ImageInfo info (itsImagePtr->imageInfo()); ImageBeamSet subSet = info.getBeamSet().subset (slicer, itsImagePtr->coordinates()); info.removeRestoringBeam(); info.setBeams (subSet); this->setImageInfoMember (info); this->setMiscInfoMember (itsImagePtr->miscInfo()); this->setUnitMember (itsImagePtr->units()); logger().addParent (itsImagePtr->logger()); } template String SubImage::imageType() const { return "SubImage"; } template void SubImage::setCoords (const CoordinateSystem& coords, Bool preserveAxesOrder) { const AxesMapping& axesMap = itsSubLatPtr->getAxesMap(); AlwaysAssert (!axesMap.isReordered(), AipsError); if (!axesMap.isRemoved()) { setCoordsMember (coords); } else { const IPosition& map = axesMap.getToNew(); const uInt naxes = map.nelements(); Vector pixels(naxes), world(naxes); pixels = 0; coords.toWorld (world, pixels); CoordinateSystem crd(coords); for (Int i=naxes; i>0; ) { i--; if (map(i) < 0) { crd.removeWorldAxis (i, world(i)); } } // Actually drop any coordinates which have their axes fully removed CoordinateSystem crdOut; CoordinateUtil::dropRemovedAxes(crdOut, crd, preserveAxesOrder); setCoordsMember (crdOut); } } template Bool SubImage::ok() const { return itsSubLatPtr->ok(); } template Bool SubImage::isMasked() const { return itsSubLatPtr->isMasked(); } template Bool SubImage::isPersistent() const { return itsSubLatPtr->isPersistent(); } template Bool SubImage::isPaged() const { return itsSubLatPtr->isPaged(); } template Bool SubImage::canReferenceArray() const { return itsSubLatPtr->canReferenceArray(); } template Bool SubImage::isWritable() const { return itsSubLatPtr->isWritable(); } template Bool SubImage::hasPixelMask() const { return itsSubLatPtr->hasPixelMask(); } template const Lattice& SubImage::pixelMask() const { return itsSubLatPtr->pixelMask(); } template Lattice& SubImage::pixelMask() { return itsSubLatPtr->pixelMask(); } template const LatticeRegion* SubImage::getRegionPtr() const { return itsSubLatPtr->getRegionPtr(); } template IPosition SubImage::shape() const { return itsSubLatPtr->shape(); } template uInt SubImage::ndim() const { return itsSubLatPtr->ndim(); } template size_t SubImage::nelements() const { return itsSubLatPtr->nelements(); } template Bool SubImage::conform (const Lattice& other) const { return shape().isEqual (other.shape()); } template void SubImage::resize (const TiledShape&) { throw (AipsError ("SubImage::resize is not possible")); } template String SubImage::name (Bool stripPath) const { return itsImagePtr->name (stripPath); } template Bool SubImage::doGetSlice (Array& buffer, const Slicer& section) { return itsSubLatPtr->doGetSlice (buffer, section); } template void SubImage::doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride) { itsSubLatPtr->doPutSlice (sourceBuffer, where, stride); } template Bool SubImage::doGetMaskSlice (Array& buffer, const Slicer& section) { return itsSubLatPtr->doGetMaskSlice (buffer, section); } template uInt SubImage::advisedMaxPixels() const { return itsSubLatPtr->advisedMaxPixels(); } template IPosition SubImage::doNiceCursorShape (uInt maxPixels) const { return itsSubLatPtr->niceCursorShape (maxPixels); } template ImageAttrHandler& SubImage::attrHandler (Bool createHandler) { return itsImagePtr->attrHandler (createHandler); } template T SubImage::getAt (const IPosition& where) const { return itsSubLatPtr->getAt (where); } template void SubImage::putAt (const T& value, const IPosition& where) { itsSubLatPtr->putAt (value, where); } template LatticeIterInterface* SubImage::makeIter (const LatticeNavigator& navigator, Bool useRef) const { return itsSubLatPtr->makeIter (navigator, useRef); } template Bool SubImage::lock (FileLocker::LockType type, uInt nattempts) { return itsSubLatPtr->lock (type, nattempts); } template void SubImage::unlock() { itsSubLatPtr->unlock(); itsImagePtr->unlock(); } template Bool SubImage::hasLock (FileLocker::LockType type) const { return itsSubLatPtr->hasLock (type); } template void SubImage::resync() { itsSubLatPtr->resync(); itsImagePtr->resync(); } template void SubImage::flush() { itsImagePtr->flush(); } template void SubImage::tempClose() { itsSubLatPtr->tempClose(); itsImagePtr->tempClose(); logger().tempClose(); } template void SubImage::reopen() { itsImagePtr->reopen(); } template void SubImage::convertIPosition(Vector& x, const IPosition& pos) const { x.resize(pos.nelements()); for (uInt i=0; i #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //

        // Temporary astronomical images. // // // // // //
      • CoordinateSystem //
      • ImageInterface //
      • TempLattice // // // The TempImage name comes from its role as the Image class for temporary // storage. // // // The class TempImage is useful for storing temporary images // for which it is not known whether they can be held in memory. // It uses class TempLattice to // hold the image in memory when it is small enough. Otherwise it is // held in a temporary file. Similarly to TempLattice // one can give the maximum memory to use to control when the image // can be held in memory. //
        // The other Image information like coordinates, units, and miscinfo // is held in member variables and disappears when the TempImage object // is destructed. //

        // It is possibly to temporarily close a TempImage, which only takes effect // when it is created as a PagedArray. In this way it is possible to reduce // the number of open files in case a lot of TempImage objects are used. // A temporarily closed TempImage will be reopened automatically when needed. // It can also be reopened explicitly. // // // // // // // The size of astronomical data can be very large. The ability to fit an // entire image into random access memory cannot be guaranteed. Paging from // disk pieces of the image appeared to be the way to deal with this problem. // //# //#

      • Maybe move applyMask, maskPtr_p, etc to base class ImageInterface //# template class TempImage: public ImageInterface { public: // The default constructor creates an empty image. TempImage(); // Construct a temporary Image from shape and coordinate information. // If the image is sufficiently small, it is kept in memory. // Otherwise it is kept in a temporary disk table. It can // be forced to disk by setting maxMemoryinMB=0. // The algorithm is the same as in class // TempLattice. TempImage (const TiledShape& mapShape, const CoordinateSystem& coordinateInfo, Int maxMemoryInMB=-1); TempImage (const TiledShape& mapShape, const CoordinateSystem& coordinateInfo, Double maxMemoryInMB); // Copy constructor (reference semantics). TempImage (const TempImage& other); // Destructor ~TempImage(); // Assignment operator (reference semantics). TempImage& operator= (const TempImage& other); // Make a copy of the object (reference semantics). virtual ImageInterface* cloneII() const; // Get the image type (returns name of derived class). virtual String imageType() const; // Is the TempImage paged to disk? virtual Bool isPaged() const; // Can the lattice data be referenced as an array section? virtual Bool canReferenceArray() const; // Is the TempImage writable? virtual Bool isWritable() const; // Set the default pixelmask to the mask with the given name // (which has to exist in the "masks" group). // If the image table is writable, the setting is persistent by writing // the name as a keyword. // If the given regionName is the empty string, // the default pixelmask is unset. virtual void setDefaultMask (const String& maskName); // Delete the pixel mask attached to the TempImage. // Does nothing if there isn't one void removeMask() { setDefaultMask (""); } // Use the mask as specified. // If a mask was already in use, it is replaced by the new one. virtual void useMask (MaskSpecifier = MaskSpecifier()); // Remove a region/mask belonging to the image from the given group // (which can be Any). // If a mask removed is the default mask, the image gets unmasked. //
        Optionally an exception is thrown if the region does not exist. virtual void removeRegion (const String& name, RegionHandler::GroupType = RegionHandler::Any, Bool throwIfUnknown = True); // Attach a mask to the TempImage. // It replaces a probably already attached mask. // It has to have the same shape as the image. virtual void attachMask (const Lattice& mask); // It a mask attached to the image? virtual Bool isMasked() const; // Does the image object use a pixelmask? // This is similar to isMasked(). virtual Bool hasPixelMask() const; // Get access to the pixelmask used. // An exception is thrown if the image does not use a pixelmask. // virtual const Lattice& pixelMask() const; virtual Lattice& pixelMask(); // // Get a section of the mask. // It throws an exception if there is no mask. virtual Bool doGetMaskSlice (Array& buffer, const Slicer& section); // Flush the data. virtual void flush(); // Close the TempImage temporarily (if it is paged to disk). // Note that a possible mask is not closed. // It'll be reopened automatically when needed or when // reopen is called explicitly. virtual void tempClose(); // If needed, reopen a temporarily closed TempLattice. virtual void reopen(); // Function which changes the shape of the image (N.B. the data is thrown // away - the Image will be filled with nonsense afterwards) virtual void resize (const TiledShape& newShape); // Return the name of the current TempImage object. // It is always "Temporary_Image" virtual String name (Bool stripPath=False) const; // Return the shape of the image virtual IPosition shape() const; // Function which sets all of the elements in the Lattice to a value. virtual void set (const T& value); // Replace every element, x, of the lattice with the result of f(x). // You must pass in the address of the function -- so the function // must be declared and defined in the scope of your program. // Both versions of apply require a function that accepts a single // argument of type T (the Lattice template actual type) and returns // a result of the same type. The first apply expects a function with // an argument passed by value; the second expects the argument to // be passed by const reference. The first form ought to run faster // for the built-in types, which may be an issue for large images // stored in memory, where disk access is not an issue. // virtual void apply (T (*function)(T)); virtual void apply (T (*function)(const T&)); virtual void apply (const Functional& function); // // Get or put a single pixel. // Note that the function operator () can also be used to get a pixel. // virtual T getAt (const IPosition& where) const; virtual void putAt (const T& value, const IPosition& where); // // This is the implementations of the letters for the envelope Iterator // class Not for public use virtual LatticeIterInterface* makeIter (const LatticeNavigator& navigator, Bool useRef) const; // Returns the maximum recommended number of pixels for a cursor. // This is the number of pixels in a tile. virtual uInt advisedMaxPixels() const; // Help the user pick a cursor for most efficient access. virtual IPosition doNiceCursorShape (uInt maxPixels) const; // Maximum size - not necessarily all used. In pixels. virtual uInt maximumCacheSize() const; // Set the maximum (allowed) cache size as indicated. virtual void setMaximumCacheSize (uInt howManyPixels); // Set the cache size as to "fit" the indicated path. virtual void setCacheSizeFromPath (const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath); // Set the actual cache size for this Array to be be big enough for the // indicated number of tiles. This cache is not shared with PagedArrays // in other rows and is always clipped to be less than the maximum value // set using the setMaximumCacheSize member function. // tiles. Tiles are cached using a first in first out algorithm. virtual void setCacheSizeInTiles (uInt howManyTiles); // Clears and frees up the caches, but the maximum allowed cache size is // unchanged from when setCacheSize was called virtual void clearCache(); // Report on cache success. virtual void showCacheStatistics (ostream& os) const; // Check for symmetry in data members. virtual Bool ok() const; protected: // Get the region used (it always returns 0). virtual const LatticeRegion* getRegionPtr() const; // Function which extracts an array from the map. virtual Bool doGetSlice (Array& buffer, const Slicer& theSlice); // Function to replace the values in the map with soureBuffer. virtual void doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride); private: void applyMaskSpecifier (const MaskSpecifier&); void applyMask (const String& maskName); TempLattice* mapPtr_p; Lattice* maskPtr_p; //# Make members of parent class known. public: using ImageInterface::logger; using ImageInterface::coordinates; using ImageInterface::getDefaultMask; using ImageInterface::hasRegion; using ImageInterface::getImageRegionPtr; using ImageInterface::setCoordinateInfo; protected: using ImageInterface::setCoordsMember; }; //# Declare extern templates for often used types. extern template class TempImage; extern template class TempImage; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/images/Images/TempImage.tcc000066400000000000000000000234531476623553700203500ustar00rootroot00000000000000//# TempImage.cc: defines the TempImage class //# Copyright (C) 1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_TEMPIMAGE_TCC #define IMAGES_TEMPIMAGE_TCC #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template TempImage::TempImage() : ImageInterface (RegionHandlerMemory()), mapPtr_p (new TempLattice), maskPtr_p (0) {} template TempImage::TempImage (const TiledShape& mapShape, const CoordinateSystem& coordinateInfo, Double maxMemoryInMb) : ImageInterface (RegionHandlerMemory()), mapPtr_p (new TempLattice (mapShape, maxMemoryInMb)), maskPtr_p (0) { AlwaysAssert(setCoordinateInfo (coordinateInfo), AipsError); } template TempImage::TempImage (const TiledShape& mapShape, const CoordinateSystem& coordinateInfo, Int maxMemoryInMb) : ImageInterface (RegionHandlerMemory()), mapPtr_p (new TempLattice (mapShape, maxMemoryInMb)), maskPtr_p (0) { AlwaysAssert(setCoordinateInfo (coordinateInfo), AipsError); } template TempImage::TempImage (const TempImage& other) : ImageInterface (other), mapPtr_p (new TempLattice (*other.mapPtr_p)), maskPtr_p (0) { if (other.maskPtr_p != 0) { maskPtr_p = other.maskPtr_p->clone(); } } template TempImage& TempImage::operator= (const TempImage& other) { if (this != &other) { delete mapPtr_p; mapPtr_p = 0; delete maskPtr_p; maskPtr_p = 0; ImageInterface::operator= (other); mapPtr_p = new TempLattice (*other.mapPtr_p); if (other.maskPtr_p != 0) { maskPtr_p = other.maskPtr_p->clone(); } } return *this; } template TempImage::~TempImage() { delete mapPtr_p; delete maskPtr_p; } template ImageInterface* TempImage::cloneII() const { return new TempImage (*this); } template String TempImage::imageType() const { return "TempImage"; } template Bool TempImage::isPaged() const { return mapPtr_p->isPaged(); } template Bool TempImage::canReferenceArray() const { return mapPtr_p->canReferenceArray(); } template Bool TempImage::isWritable() const { return mapPtr_p->isWritable(); } template void TempImage::flush() { mapPtr_p->flush(); } template void TempImage::tempClose() { mapPtr_p->tempClose(); } template void TempImage::reopen() { mapPtr_p->reopen(); } template void TempImage::setDefaultMask (const String& regionName) { // Use the new region as the image's mask. applyMask (regionName); // Store the new default name. ImageInterface::setDefaultMask (regionName); } template void TempImage::useMask (MaskSpecifier spec) { applyMaskSpecifier (spec); } template void TempImage::applyMaskSpecifier (const MaskSpecifier& spec) { // Use default mask if told to do so. // If it does not exist, use no mask. String name = spec.name(); if (spec.useDefault()) { name = getDefaultMask(); if (! hasRegion (name, RegionHandler::Masks)) { name = ""; } } applyMask (name); } template void TempImage::applyMask (const String& maskName) { // No region if no mask name is given. if (maskName.empty()) { delete maskPtr_p; maskPtr_p = 0; return; } // Reconstruct the ImageRegion object. // Turn the region into lattice coordinates. ImageRegion* regPtr = getImageRegionPtr (maskName, RegionHandler::Masks); LatticeRegion* latReg = new LatticeRegion (regPtr->toLatticeRegion (coordinates(), shape())); delete regPtr; // The mask has to cover the entire image. if (latReg->shape() != shape()) { delete latReg; throw (AipsError ("TempImage::setDefaultMask - region " + maskName + " does not cover the full image")); } // Replace current by new mask. delete maskPtr_p; maskPtr_p = latReg; } template void TempImage::attachMask (const Lattice& mask) { if (! shape().isEqual (mask.shape())) { throw (AipsError ("TempImage::attachMask - " "shapes of lattice and mask mismatch")); } if (maskPtr_p) { delete maskPtr_p; maskPtr_p = 0; } maskPtr_p = mask.clone(); } template void TempImage::removeRegion (const String& name, RegionHandler::GroupType type, Bool throwIfUnknown) { // Remove the default mask if it is the region to be removed. if (name == getDefaultMask()) { setDefaultMask (""); } ImageInterface::removeRegion (name, type, throwIfUnknown); } template Bool TempImage::isMasked() const { return (maskPtr_p != 0); } template Bool TempImage::hasPixelMask() const { return (maskPtr_p != 0); } template const Lattice& TempImage::pixelMask() const { if (maskPtr_p == 0) { throw (AipsError ("TempImage::pixelMask - no mask attached")); } return *maskPtr_p; } template Lattice& TempImage::pixelMask() { if (maskPtr_p == 0) { throw (AipsError ("TempImage::pixelMask - no mask attached")); } return *maskPtr_p; } template Bool TempImage::doGetMaskSlice (Array& buffer, const Slicer& section) { // If no mask, base implementation returns a True mask. if (maskPtr_p == 0) { return MaskedLattice::doGetMaskSlice (buffer, section); } return maskPtr_p->doGetSlice (buffer, section); } template const LatticeRegion* TempImage::getRegionPtr() const { return 0; } template IPosition TempImage::shape() const { return mapPtr_p->shape(); } template void TempImage::resize (const TiledShape& newShape) { delete mapPtr_p; mapPtr_p = new TempLattice (newShape); } template Bool TempImage::doGetSlice (Array& buffer, const Slicer& section) { return mapPtr_p->doGetSlice (buffer, section); } template void TempImage::doPutSlice (const Array& buffer, const IPosition& where, const IPosition& stride) { mapPtr_p->doPutSlice (buffer, where, stride); } template String TempImage::name (Bool) const { return String ("Temporary_Image"); } template void TempImage::set (const T& value) { mapPtr_p->set (value); } template void TempImage::apply (T (*function)(T)) { mapPtr_p->apply (function); } template void TempImage::apply (T (*function)(const T&)) { mapPtr_p->apply (function); } template void TempImage::apply (const Functional& function) { mapPtr_p->apply (function); } template uInt TempImage::advisedMaxPixels() const { return mapPtr_p->advisedMaxPixels(); } template IPosition TempImage::doNiceCursorShape (uInt maxPixels) const { return mapPtr_p->niceCursorShape (maxPixels); } template uInt TempImage::maximumCacheSize() const { return mapPtr_p->maximumCacheSize(); } template void TempImage::setMaximumCacheSize (uInt howManyPixels) { mapPtr_p->setMaximumCacheSize (howManyPixels); } template void TempImage::setCacheSizeFromPath (const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath) { mapPtr_p->setCacheSizeFromPath (sliceShape, windowStart, windowLength, axisPath); } template void TempImage::setCacheSizeInTiles (uInt howManyTiles) { mapPtr_p->setCacheSizeInTiles (howManyTiles); } template void TempImage::clearCache() { mapPtr_p->clearCache(); } template void TempImage::showCacheStatistics (ostream& os) const { mapPtr_p->showCacheStatistics (os); } template T TempImage::getAt (const IPosition& where) const { return mapPtr_p->getAt (where); } template void TempImage::putAt (const T& value, const IPosition& where) { mapPtr_p->putAt (value, where); } template Bool TempImage::ok() const { return mapPtr_p->ok(); } template LatticeIterInterface* TempImage::makeIter (const LatticeNavigator& navigator, Bool useRef) const { return mapPtr_p->makeIter (navigator, useRef); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Images/test/000077500000000000000000000000001476623553700167555ustar00rootroot00000000000000casacore-3.7.1/images/Images/test/CMakeLists.txt000066400000000000000000000034741476623553700215250ustar00rootroot00000000000000set (datafiles qualityimage.fits # for tFITSQualityImage mexinputtest.fits # for tFITSImgParser imageStats.fits # for tImageStatistics #ngc5921.clean.fits # for tImageMetaData #ngc5921.clean.no_freq.no_stokes.fits # for tImageMetaData #jyperpixelimage.fits # for tImageMetaData imagetestimage.fits # for tFITSImage test_image.im/table.dat # for dImageStatistics. image2fits test_image.im/table.f0 test_image.im/table.f0_TSM0 test_image.im/table.info test_image.im/table.lock test_image.im/logtable/table.dat test_image.im/logtable/table.f0 test_image.im/logtable/table.info test_image.im/logtable/table.lock decon_test.im/logtable/table.info decon_test.im/logtable/table.lock decon_test.im/logtable/table.dat decon_test.im/logtable/table.f0 decon_test.im/table.info decon_test.im/table.lock decon_test.im/table.dat decon_test.im/table.f0_TSM0 decon_test.im/table.f0 ) foreach (file ${datafiles}) configure_file (${CMAKE_CURRENT_SOURCE_DIR}/${file} ${CMAKE_CURRENT_BINARY_DIR}/${file} COPYONLY) endforeach (file) set (tests dImageInterface dImageStatistics dImageSummary dPagedImage tExtendImage tFITSErrorImage tFITSExtImage tFITSExtImageII tFITSImage tFITSImgParser tFITSQualityImage tHDF5Image tImageAttrHandler tImageBeamSet tImageConcat tImageEmpty tImageExpr tImageExpr2 tImageExpr2Gram tImageExpr3Gram tImageExprGram tImageExprParse tImageExprParse_addDir tImageInfo tImageProxy tImageRegrid tImageStatistics tImageStatistics2 tImageUtilities tLELSpectralIndex tMIRIADImage tPagedImage tPagedImage2 tRebinImage tSubImage tTempImage ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_images) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/images/Images/test/dImageInterface.cc000066400000000000000000000047131476623553700223000ustar00rootroot00000000000000//# dImageInterface.cc: Illustrates the use of the ImageInterface base class //# Copyright (C) 1996,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include Float sumPixels(const ImageInterface& image){ uInt rowLength = image.shape()(0); IPosition rowShape(image.ndim()); rowShape = 1; rowShape(0) = rowLength; Float sumPix = 0; RO_LatticeIterator iter(image, rowShape); while(!iter.atEnd()){ // static_cast is a workaround for a SGI compiler bug sumPix += sum(static_cast >(iter.vectorCursor())); iter++; } return sumPix; } int main(){ PagedImage demo(IPosition(2, 10), CoordinateUtil::defaultCoords2D(), "dImageInterface_tmp.image"); demo.set(1.0f); cout << "Sum of all pixels is: " << sumPixels(demo) << endl; if (near(sumPixels(demo), 100.0f)){ cout << "OK" << endl; return 0; } else { cout << "FAIL" << endl; return 1; } } casacore-3.7.1/images/Images/test/dImageStatistics.cc000066400000000000000000000356511476623553700225370ustar00rootroot00000000000000//# dImageStatistics.cc: image statistics program //# Copyright (C) 1996,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // // // dImageStatistics iterates through an image accumulating and displaying statistics // from data chunks specified by the axes keyword. The statistics // are displayed as a function of location of the display axes // (the axes not specified by keyword axes) with line plots. // // in The name on the input Casacore image. Currently must be of // type and can be of any dimension. // // There is no default. // // axes An array of integers (1 relative) specifying which axes are to // have the statistics accumulated for, and which ones the statistics // will be displayed as a function of. For example, axes=3 would // cause imstat to work out statistics along the third (z) axis and // display them as a function of the first (x) and second (y) axes. // axes=1,3 would cause statistics of 1-3 (x-z) planes to be made // and then to be displayed as a function of the second axis (y) // location. // // The default is to evaluate the statistics over the entire image. // // blc,trc Region (1 relative) // inc Increment to step through image // stats This specifies which statistics you would like to see plotted. // Give a list choosing from // // npts The number of selected points // min The minimum value // max The maximum value // sum The sum of the values // sum_N(x_i) // mean The mean value // (1/N) * sum_N(x_i) () // sigma The standard deviation about the mean // (1/[N-1]) * sum_N(x_i - )**2 // rms The root mean square value // (1/N) * sum_N(x_i**2) // // The default is mean,sigma // // include This specifies a range of pixel values to *include* for the // statistics. If two values are given then include pixels in // the range include(1) to include(2). If one value is give, then // include pixels in the range -abs(include) to abs(include) // // The default is to include all data. // // exclude This specifies a range of pixel values to *exclude* for the // statistics. If two values are given then exlude pixels in // the range exclude(1) to exclude(2). If one value is give, then // exclude pixels in the range -abs(exclude) to abs(exclude) // // The default is to exclude no data. // // list Only active if making a plot. If True, write the statistics to the // standard output. // // Default is True. // // plotter The PGPLOT device. // // The default is /xs // // nxy The number of subplots to put on each page in x and y. Each // histogram takes one subplot. // // The default is 1,1 // // // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include enum defaults {AXES, REGION, STATS, RANGE, PLOTTING, NDEFAULTS=5}; int main (int argc, const char* argv[]) { try { Input inputs(1); inputs.version ("$Revision$"); // Get inputs String name = "test_image.im"; inputs.create("in", name, "Input file name"); inputs.create("axes", "-10", "Cursor axes"); inputs.create("blc", "-10", "blc"); inputs.create("trc", "-10", "trc"); inputs.create("inc", "-10", "inc"); inputs.create("stats", "mean,sigma", "Statistics to plot"); inputs.create("include", "0.0", "Pixel range to include"); inputs.create("exclude", "0.0", "Pixel range to exclude"); inputs.create("list", "True", "List statistics as well as plot ?"); inputs.create("plotter", "none", "PGPlot device"); inputs.create("nxy", "-1", "Number of subplots in x & y"); inputs.create("disk", "F", "Force storage image to be disk based"); inputs.readArguments(argc, argv); const String in = inputs.getString("in"); const Block cursorAxesB(inputs.getIntArray("axes")); const Block blcB(inputs.getIntArray("blc")); const Block trcB(inputs.getIntArray("trc")); const Block incB(inputs.getIntArray("inc")); const String statsToPlot = inputs.getString("stats"); const Block includeB = inputs.getDoubleArray("include"); const Block excludeB = inputs.getDoubleArray("exclude"); const Bool doList = inputs.getBool("list"); const Block nxyB(inputs.getIntArray("nxy")); String device = inputs.getString("plotter"); const Bool forceDisk = inputs.getBool("disk"); // Create defaults array Vector validInputs(NDEFAULTS); validInputs = False; LogOrigin lor("dImageStatistics", "main()", WHERE); LogIO os(lor); // Check image name and get image data type. if (in.empty()) { os << LogIO::NORMAL << "You must specify the image file name" << LogIO::POST; return 1; } // Convert cursor axes array to a vector (0 relative) Vector cursorAxes(cursorAxesB.begin(), cursorAxesB.end()); if (cursorAxes.nelements() == 1 && cursorAxes(0) == -10) { cursorAxes.resize(0); } else { for (uInt i=0; i include(includeB.nelements()); uInt i; for (i=0;i exclude(excludeB.nelements()); for (i=0;i statisticTypes = LatticeStatsBase::toStatisticTypes(statsToPlot, re); Vector nxy(nxyB.begin(), nxyB.end()); if (nxy.nelements() == 1 && nxy(0) == -1) nxy.resize(0); if (device != "none" && (statisticTypes.nelements()!=0 || !device.empty() || nxy.nelements()!=0)) validInputs(PLOTTING) = True; // Do the work DataType imageType = imagePixelType(in); // if (imageType==TpFloat) { // Construct image PagedImage inImage(in, True); SubImage* pSubImage2 = 0; if (validInputs(REGION)) { LCBox::verify(blc, trc, inc, inImage.shape()); cout << "Selected region : " << blc+1<< " to " << trc+1 << endl; const LCSlicer region(blc, trc); // SubImage* pSubImage = 0; if (inImage.isMasked()) { ImageRegion mask = inImage.getRegion(inImage.getDefaultMask(), RegionHandler::Masks); pSubImage = new SubImage(inImage, mask); } else { pSubImage = new SubImage(inImage); } pSubImage2 = new SubImage(*pSubImage, ImageRegion(region)); delete pSubImage; } else { if (inImage.isMasked()) { ImageRegion mask = inImage.getRegion(inImage.getDefaultMask(), RegionHandler::Masks); pSubImage2 = new SubImage(inImage, mask); } else { pSubImage2 = new SubImage(inImage); } } // Construct statistics object ImageStatistics stats(*pSubImage2, os, True, forceDisk); // Clean up SUbImage pointers Int nDim = pSubImage2->ndim(); if (pSubImage2!=0) delete pSubImage2; // Set state if (validInputs(AXES)) { if (!stats.setAxes(cursorAxes)) { os << stats.errorMessage() << LogIO::POST; return 1; } } if (validInputs(RANGE)) { if (!stats.setInExCludeRange(include, exclude, True)) { os << stats.errorMessage() << LogIO::POST; return 1; } } if (!stats.setList(doList)) { os << stats.errorMessage() << LogIO::POST; return 1; } if (validInputs(PLOTTING)) { PGPlotter plotter(device); /* if (!stats.setPlotting(plotter, statisticTypes, nxy)) { os << stats.errorMessage() << LogIO::POST; return 1; } */ } // Recover things os.post(); os << "Recovering display axes" << endl; Vector displayAxes = stats.displayAxes(); // os << endl << endl; os << "Recover array for each statistics type " << endl; const Int nStats = LatticeStatsBase::NSTATS; for (Int i=0; i a; LatticeStatsBase::StatisticsTypes t = static_cast(i); stats.getStatistic (a, t, True); } // os << "Recovering statistics slice from origin" << endl; IPosition pos(stats.displayAxes().nelements(),0); IPosition pos2(nDim,0); Vector dataV; if (!stats.getStats(dataV, pos, False)) { os << stats.errorMessage() << LogIO::POST; } if (!stats.getStats(dataV, pos2, True)) { os << stats.errorMessage() << LogIO::POST; } // Display statistics if (!stats.display()) { os << stats.errorMessage() << LogIO::POST; return 1; } // Test copy constructor os << LogIO::NORMAL << "Applying copy constructor" << endl; ImageStatistics stats2(stats); // Test assignment operator os << "Applying assignment" << LogIO::POST; stats = stats2; // Test setNewImage os << "Test setNewImage" << LogIO::POST; stats.setNewImage(inImage); if (!stats.display()) { os << stats.errorMessage() << LogIO::POST; } } else if (imageType==TpComplex) { // COnstruct image PagedImage inImage(in, True); SubImage* pSubImage2 = 0; if (validInputs(REGION)) { LCBox::verify(blc, trc, inc, inImage.shape()); cout << "Selected region : " << blc+1<< " to " << trc+1 << endl; const LCSlicer region(blc, trc); // SubImage* pSubImage = 0; if (inImage.isMasked()) { ImageRegion mask = inImage.getRegion(inImage.getDefaultMask(), RegionHandler::Masks); pSubImage = new SubImage(inImage, mask); } else { pSubImage = new SubImage(inImage); } pSubImage2 = new SubImage(*pSubImage, ImageRegion(region)); delete pSubImage; } else { if (inImage.isMasked()) { ImageRegion mask = inImage.getRegion(inImage.getDefaultMask(), RegionHandler::Masks); pSubImage2 = new SubImage(inImage, mask); } else { pSubImage2 = new SubImage(inImage); } } // Construct statistics object ImageStatistics stats(*pSubImage2, os, True, forceDisk); // Clean up SUbImage pointers if (pSubImage2!=0) delete pSubImage2; // Set state if (validInputs(AXES)) { if (!stats.setAxes(cursorAxes)) { os << stats.errorMessage() << LogIO::POST; return 1; } } if (!stats.setList(doList)) { os << stats.errorMessage() << LogIO::POST; return 1; } /* if (validInputs(PLOTTING)) { PGPlotter plotter(device); if (!stats.setPlotting(plotter, statisticTypes, nxy)) { os << stats.errorMessage() << LogIO::POST; return 1; } } */ // Display statistics if (!stats.display()) { os << stats.errorMessage() << LogIO::POST; return 1; } } else { os << LogIO::NORMAL << "images of type " << Int(imageType) << " not yet supported" << LogIO::POST; return 1; } } catch (std::exception& x) { cerr << "exception: error " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/images/Images/test/dImageSummary.cc000066400000000000000000000067361476623553700220440ustar00rootroot00000000000000//# imhead.cc: List image header //# Copyright (C) 1996,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main (int argc, const char* argv[]) { try { Input inputs(1); inputs.version ("$Revision$"); String name = "test_image.im"; inputs.create("in", name, "Input image name?"); inputs.create("type", "RADIO","Velocity type ?"); inputs.readArguments(argc, argv); const String in = inputs.getString("in"); const String velocityType = inputs.getString("type"); // Open image, construct helper class object and list header if (in.empty()) { cout << "You must specify the image file name" << endl; return 1; } LogOrigin lor("imhead", "main()", WHERE); LogIO os(lor); // Parse velocity type MDoppler::Types doppler; Bool ok = MDoppler::getType(doppler, velocityType); if (!ok) { os << "Invalid velocity type, using RADIO" << endl; doppler = MDoppler::RADIO; } // ImageOpener::ImageTypes imageType = ImageOpener::imageType(in); if (imageType==ImageOpener::AIPSPP) { DataType pixelType = imagePixelType(in); if (pixelType==TpFloat) { PagedImage im(in); ImageSummary header(im); header.list(os, doppler); } else { os << "Float images only" << LogIO::EXCEPTION; } } else if (imageType==ImageOpener::FITS) { FITSImage im(in); ImageSummary header(im); header.list(os, doppler); } else if (imageType==ImageOpener::MIRIAD) { MIRIADImage im(in); ImageSummary header(im); header.list(os, doppler); } else { os << "Unrecognized image type" << LogIO::EXCEPTION; } } catch (std::exception& x) { cerr << "aipserror: error " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/images/Images/test/dPagedImage.cc000066400000000000000000000272251476623553700214230ustar00rootroot00000000000000//# tPagedImage.cc: This program tests the PagedImage class //# Copyright (C) 1994,1995,1996,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //#-------------------------------------------------------------------------- #include #include #include #include #include #include #include #include #include #include #include int main() { cout << "untested" << endl; } /* //#predeclarations void getArguments(int argc, char **argv, Bool &verbose); Bool fudgedEquality(Float a, Float b); void deleteFile(const String &filename); ImageCoordinate build3Dcoords(); void createStandardImageOnDisk(const String &filename); void describeImage(const String &message, const PagedImage &image); void testConstructors(); void testSetMemberFunctions(); void testArrayofImagesAndAssignmentOperator(); //# define mega global variables Bool verbose_; const Float TEST_PIXEL_VALUE = 99.99; const String STANDARD = "image-standard"; const String NAME0 = "image-test-file_0"; const String NAME1 = "image-test-file_1"; const String NAME2 = "image-test-file_2"; const String NAME3 = "image-test-file_3"; const String NAME4 = "image-test-file_4"; const String NAME5 = "image-test-file_5"; const String NAME6 = "image-test-file_6"; const String NAME7 = "image-test-file_7"; const String NAME8 = "image-test-file_8"; const String NAME9 = "image-test-file_9"; int main(int argc, const char* argv[]) { getArguments(argc, argv, verbose_); createStandardImageOnDisk(STANDARD); try { testConstructors(); testSetMemberFunctions(); testArrayofImagesAndAssignmentOperator(); deleteFile(STANDARD); cout << "OK" << endl; } catch (std::exception x) { cerr << "Caught Exception: " << x.what() << endl; } return 0; } // fill debug variable void getArguments(int argc, char **argv, Bool &verbose) { verbose = False; for(uInt i=0; i< argc; i++) if(!strcmp(argv [i], "-v")) verbose = True; } // equality function for floats Bool fudgedEquality(Float a, Float b) { Float absA = fabs(a); Float absB = fabs(b); Float delta = fabs(absA - absB); Float margin; if(absA > absB) margin = absA / 1.0e5; else margin = absB / 1.0e5; if(delta > margin) { return False; } else { return True; } } // function which deletes files on disk void deleteFile(const String &filename) { String command = "rm -rf "; command += filename; system(command); } // function to print Image data void describeImage(const String &message, const PagedImage &image) { cout << message << " shape: "<< image.shape() << endl; // * cout << "coordinates, axisNames: " << image.coordinates().axisNames() << endl; cout << " referencePixels: " << image.coordinates().referencePixels() << endl; cout << " referenceValues: " << image.coordinates().referenceValues()<< endl; cout << " deltas: " << image.coordinates().deltas()<< endl; cout << " shape: " << image.coordinates().imageShape() << endl; // * / cout << " ok: "<< image.ok()<< endl; } // build a set of ImageCoordinates in three dimensions ImageCoordinate build3Dcoords() { // we need to know a projection method - here it is Global Sinusoid ProjectedPosition::Type myMethod(ProjectedPosition::GLS); // we need to know what form the coordinates are in - here it is RA & Dec SkyPosition::Type myVectorType(SkyPosition::EQUATORIAL); // we need to know the epoch of the coordinates SkyPosition::Epoch myEpoch(SkyPosition::J2000); // we need to know the vector itself. Vector myCoords(2); myCoords(0) = 122.35; myCoords(1) = -33.7764; // we need to know the position of the observer. // Let's create an EarthPosition with full description of all parameters. // We need a type - GEOCENTRIC seems good. EarthPosition::Type theType(EarthPosition::GEOCENTRIC); // We need the time and date of the observation... Double julianDate = 2449376.0; // and we can add on the UT. julianDate += 16.52/24.0; // we need the coordinates of our position. Vector ourPosition(3); // geocentric longitude (in degrees) goes in the first field of the vector. ourPosition(0) = 107.2334666; // geocentric latitude (in degrees) goes in the second field. ourPosition(1) = 34.1562394; // geocentric radius (in meters) goes in the last field. ourPosition(2) = 6372139.592; // then use these to build our EarthPosition. EarthPosition myObs(theType, julianDate, ourPosition); // we need to know a rotation - here it is zero. Double myRot = 0; // we need to know where the spherical position is to be on our 2-d // projection i.e. what pixel is associated with my object's position? Vector thePixel(2); thePixel(0) = 55.0; thePixel(1) = 526.3; // finally, we need to know the number of spherical units per integer on // our 2-d projection (i.e. binning per pixel). Vector theBinning(2); theBinning(0) = 3.04e-03; theBinning(1) = 3.6255e-03; // Now we can make fruit of our labor - the ProjectedPosition itself. ProjectedPosition myMapping(myMethod, myVectorType, myEpoch, myCoords, myObs, myRot, thePixel, theBinning); // we need to know what the units are of the measured value MeasuredValue::Type myValueUnit(MeasuredValue::RADIO_VELOCITY); // we need to know what the above units are in reference // here it is the velocity of the earth. ReferenceValue myRefValue(ReferenceValue::VELOCITY, 9.56e+03); // we need to know the value of the measurement Double myValue(9.5688823e+03); // we need to know the binning per "pixel" Double myBin(5.63e-04); // we need to know the position on the "number line" of our value Double myValuePos(27.3); // Now we may construct the LinearAxis itself. LinearAxis myLinearAxis(myValueUnit,myValue,myRefValue,myBin,myValuePos); ImageCoordinate coords; coords.addAxis(myMapping); coords.addAxis(myLinearAxis); return coords; } // build an Image and store in a file on disk void createStandardImageOnDisk(const String &filename) { if(verbose_) cout<< "-- createStandardImageOnDisk --"<< endl; ImageCoordinate coords(build3Dcoords()); IPosition imageShape(3, 50,47,31); PagedImage standard(imageShape, coords, filename); standard.set(TEST_PIXEL_VALUE); if(verbose_) describeImage("standard image", standard); } // these are the constructors to test: // PagedImage(const IPosition &shape, const MinimalCoords &coordinateInfo, // const String &nameOfNewFile, uInt rowNumber); // PagedImage(const IPosition &shape, const Array &array, // const MinimalCoords &coordinateInfo, // const String &nameOfNewFile, uInt rowNumber); // PagedImage(const String &filename, uInt rowNumber); // PagedImage(const PagedImage &other); // void testConstructors() { if (verbose_) cout << "-- testConstructors --" << endl; { IPosition shape(3,10,10,4); ImageCoordinate coords(build3Dcoords()); PagedImage image1(shape, coords, NAME0); if (verbose_) describeImage("shape, coords, filename ctor: ", image1); PagedImage image2(shape, coords, NAME1, True); if (verbose_) describeImage("array, array, coords, filename, masking? ctor: ", image2); PagedImage image3(STANDARD); if (verbose_) describeImage("old file ctor: ", image3); if (image3(IPosition(3,5,4,3)) != TEST_PIXEL_VALUE) throw(AipsError("image3, file-constructed image, wrong pixel value")); if (!image3.ok()) throw(AipsError("file-constructed image not ok")); PagedImage *image4 = new PagedImage(image3); if(verbose_) describeImage("Copy ctor: ", *image4); if((*image4)(IPosition(3,5,4,3)) != TEST_PIXEL_VALUE) throw(AipsError("image4, file assigned image, wrong pixel value")); if(!image4->ok()) throw(AipsError("file-constructed image not ok")); delete image4; } deleteFile(NAME0); deleteFile(NAME1); } // test some of the data member manipulation functions void testSetMemberFunctions() { if(verbose_) cout<< "-- test set member functions --"<< endl; { ImageCoordinate coords; { PagedImage image6(IPosition(3,10,10,4), coords, NAME2); if(verbose_) describeImage("shape, coords, filename ctor: ", image6); if(image6.ok()) throw(AipsError("shape-constructed image6 is ok, but shouldn't be!")); } coords = build3Dcoords(); PagedImage image6(IPosition(3,10,10,4), coords, NAME2); if(!image6.ok()) throw(AipsError("shape-constructed image6 - is not ok!")); image6.rename(NAME2); if(verbose_) describeImage("image6, after rename", image6); if(!image6.ok()) throw(AipsError("image6 after rename - not ok")); image6.setCoordinateInfo(build3Dcoords()); if(verbose_) describeImage("image6, after setCoordinates", image6); if(!image6.ok()) throw(AipsError("image6 after setCoordinates - not ok")); } deleteFile(NAME2); } // void testArrayofImagesAndAssignmentOperator() { if(verbose_) cout<< "-- test array of images --"<< endl; // * const uInt max = 10; String filenames [max]; filenames [0] = NAME0; filenames [1] = NAME1; filenames [2] = NAME2; filenames [3] = NAME3; filenames [4] = NAME4; filenames [5] = NAME5; filenames [6] = NAME6; filenames [7] = NAME7; filenames [8] = NAME8; filenames [9] = NAME9; { Image images [max]; for(uInt i=0; i< max; i++) { if(verbose_) cout<< "-- image array "<< i<< " --"<< endl; if(i%2) { images [i] = Image(IPosition(2,10,10)); images [i].setCoordinateInfo(MinimalCoords()); images [i].setName(filenames [i]); if(verbose_) describeImage("shape constructed image, setCoords, setName in array", images [i]); } // if i is odd else { images [i] = Image(IPosition(i+1,5), MinimalCoords(), filenames [i]); if(verbose_) describeImage("shape,coords,name constructed image in array,", images [i]); } // else if(!images[i].ok()) throw(AipsError("one of array of images not ok")); } // for i } // scope if(verbose_) cout<< "-- about to delete image files --"<< endl; for(uInt i=0; i< max; i++) deleteFile(filenames [i]); // * / } */ casacore-3.7.1/images/Images/test/dPagedImage.run000066400000000000000000000001251476623553700216300ustar00rootroot00000000000000#!/bin/sh echo "UNTESTED: dPagedImage requires retired ImageCoordinate class" exit 3 casacore-3.7.1/images/Images/test/decon_test.im/000077500000000000000000000000001476623553700215105ustar00rootroot00000000000000casacore-3.7.1/images/Images/test/decon_test.im/logtable/000077500000000000000000000000001476623553700233015ustar00rootroot00000000000000casacore-3.7.1/images/Images/test/decon_test.im/logtable/table.dat000066400000000000000000000025601476623553700250650ustar00rootroot00000000000000¾¾¾¾lTable PlainTable< TableDescLog message table5 TableRecord RecordDesc5 TableRecord RecordDescScalarColumnDesc TableRecord³ RecordDesctype refer m2 RecordDescm1 RecordDescm0 RecordDescpositionITRFc TableRecord; RecordDescvalueunit AXVnšE;™me TableRecord; RecordDescunit valuerad¿Ù‘ÐÃ4ÊÍe TableRecord; RecordDescunit valuerad¿ò벪#[ª TableRecordÓ RecordDesc system projection projection_parameters IPositionÿÿÿÿcrval IPositionÿÿÿÿcrpix IPositionÿÿÿÿcdelt IPositionÿÿÿÿpc IPositionÿÿÿÿaxes IPositionÿÿÿÿunits IPositionÿÿÿÿconversionSystem longpolelatpoleJ2000SIN5 Array5 Array5 Array@(@(5 Array¿ð?ðI Array?ð?ðG ArrayRight Ascension Declination/ Array''J2000@f€* Array5 Array* Array5 Array5 TableRecord RecordDesc— TableRecordx RecordDesc restoringbeam RecordDesc imagetype objectname ó TableRecord£ RecordDescmajor RecordDescminor RecordDesc positionangle RecordDesch TableRecord; RecordDescvalueunit @arcminh TableRecord; RecordDescunit valuearcmin@e TableRecord; RecordDescunit valuedeg IntensityJy/beam¯ TableRecordO RecordDescHypercolumn_map RecordDescE TableRecord° RecordDescndimdata IPositionÿÿÿÿcoord IPositionÿÿÿÿid IPositionÿÿÿÿ, Arraymap% Array% ArrayArrayColumnDesc] å=MG¤=Û´<=6¥­<<Ä*;D:MÄØ8<™ƒ76TwG4êÿx2£å(3^L56¥­6ö†28Œ“9Â:êÍ;6¥­gÜ^>½j›>6¥­>½j›>gÜ^>>k‘k=6¥­<êÍ;Â:Œ“9ö†286¥­6^L5£å(3Ø‹·3Œ“5Û´<7Â8LÉ:ÜÖR;gÜ^<Û´<=>!‹>Õ0ò>£å(?Û´!‹>>Û´<=gÜ^<ÜÖR;LÉ:Â8Û´<7Œ“5Ø‹·3LÉ46MG¤7£å(9!‹:Ø‹·;ÂÕ0ò>ÜÖR?Œ“?MG¤?Œ“?ÜÖR?Õ0ò>gÜ^>MG¤=Â<Ø‹·;!‹:£å(9MG¤76LÉ4gÜ^4ö†26] å7k‘k9Â:<^L=] å=½j›>£å(?Œ“?êÍ?] å?êÍ?Œ“?£å(?½j›>] å=^L=<Â:k‘k9] å7ö†26gÜ^4êÿx4TwG68<™ƒ9MÄØ:D<Ä*=>6¥­>Û´>Ä*=D£å(?Œ“?êÍ?] å?êÍ?Œ“?£å(?½j›>] å=^L=<Â:k‘k9] å7ö†26gÜ^4LÉ46MG¤7£å(9!‹:Ø‹·;ÂÕ0ò>ÜÖR?Œ“?MG¤?Œ“?ÜÖR?Õ0ò>gÜ^>MG¤=Â<Ø‹·;!‹:£å(9MG¤76LÉ4Ø‹·3Œ“5Û´<7Â8LÉ:ÜÖR;gÜ^<Û´<=>!‹>Õ0ò>£å(?Û´!‹>>Û´<=gÜ^<ÜÖR;LÉ:Â8Û´<7Œ“5Ø‹·3£å(3^L56¥­6ö†28Œ“9Â:êÍ;6¥­gÜ^>½j›>6¥­>½j›>gÜ^>>k‘k=6¥­<êÍ;Â:Œ“9ö†286¥­6^L5£å(3êÿx2TwG46<™ƒ7MÄØ8D:Ä*;<6¥­<Û´<=MG¤=] å=>] å=MG¤=Û´<=6¥­<<Ä*;D:MÄØ8<™ƒ76TwG4êÿx2Œ“1k‘k3Ä*5½j›68£å(9ö†2:Ä*;êÍ;gÜ^<Â<^L=Ä*=^L=´(ò¿S¦6À'7û>êº >€}¼;£>¿õ!Ë¿¾á$?9—¿" F¾.Z>ÈFf¿\T¾ ?Á?Ô j¿°±4¿oJ»O¸d?ÉÆ§¾päÍ¿¯wœ?9™·¾š}ý?k?d$¾’¤¿¬°€¾÷ò‹¿vX?æq¿‰2¿Yþ?œÒ¿r: @B?›¼ûJ—¿{í>Ÿ¥‚¾6O?”Õ(?j𣿃;:?Aè?‡ ˆ¿½(q½ …[½ïš?Œ;XA=ÿÿ¨>®y1?X´Ò>÷à¿Ëâ>þc·¿ì„ä?õW¿¥§¾¹du?f‚lÀ%ú¯¿lNÝ¿†¦ ¾s"À¿Ç]²?¨Eô@þ¿R`¿ŒÔO>@µc¿-¾¾E\M?›1†¾ÆAÅ?…cº¼“$v>œ?ʆ.>ŽøÀ Ø¿ká?ª—/?UÕ„¿Bp1Àðí½€v¢¿ô¦º¾³¼½¬^™@½Ú’J¾“Î@¯>n÷´?ߨ?ex¾ %¿Kâ@ Z½¿’0s@+XK>˜%”?™§J¾ý{d?xÆ?PÌ»¿m@p#casacore-3.7.1/images/Images/test/imagetestimage.fits000066400000000000000000001111001476623553700226230ustar00rootroot00000000000000SIMPLE = T /Standard FITS BITPIX = -32 /Floating point (32 bit) NAXIS = 2 NAXIS1 = 113 NAXIS2 = 76 BSCALE = 1.000000000000E+00 /PHYSICAL = PIXEL*BSCALE + BZERO BZERO = 0.000000000000E+00 BMAJ = 1.4861112E-02 BMIN = 9.4999997E-03 BPA = 6.0000000E+00 BUNIT = 'JY/BEAM ' /Brightness (pixel) unit EPOCH = 2.000000000000E+03 CTYPE1 = 'RA---SIN' CRVAL1 = 0.000000000000E+00 CDELT1 = -2.222222308810E-03 CROTA1 = 0.000000000000E+00 CRPIX1 = 5.600000000000E+01 CUNIT1 = 'DEG ' CTYPE2 = 'DEC--SIN' CRVAL2 = 0.000000000000E+00 CDELT2 = 3.333333234031E-03 CROTA2 = 0.000000000000E+00 CRPIX2 = 3.800000000000E+01 CUNIT2 = 'DEG ' DATE = '2000-02-29T08:29:11.946598' /Date FITS file was written TIMESYS = 'UTC ' /Time system for HDU ORIGIN = 'AIPS++ version 1.3 (build #340)' HISTORY File modified by user 'dbarnes' with fv on 1999-07-28T14:19:41 HISTORY File modified by user 'dbarnes' with fv on 1999-07-28T14:21:43 HISTORY END >}u4½¾ ,»­Ñ>©Ti>¯Áõ¼Î~ò¾"ó¾IEE¼l–ä>µ¦>ÌW?BÈ8?qšú?^ÛÆ?CV1?>2k?.ðì>û–g>ƒ°ç=¨m½Š”K¾›Þ¡¿'zr¿o>;¿gRþ¿"쎿 CÉ¿W–Á¿›Rb¿¯_U¿¾…¿ß‘K¿è쯿³ÏÚ¿i#Ì¿uv¿²4ý¿À[Ë¿‘°D¿k¶ ¿žgþ¿Ñ±ö¿ÀTÜ¿_ØÑ¾–ú¬½TþÞ<×r’=žEʽ¼>I¿(}¿Õ-¿fÐj¾ÇÐ4½„r¼½öðq¾J€·½µJ ½¬A¾ÚÕÙ¿]ýà¿‚C<¿Z h¿7y¾ñx꾺+Ǿf å½¶ýÔ½¹1¿¾ ¾¾õ4׿ Té¿ ÷l¿,/ï¿?ý¿V¾ó`š¿7a’¿€Ç%¿F ¾>•E=¬Ã˼­z=³Ö$?‡?+d«>šÁ£½í”T¾iÒ¥¾a‡ç¾’c»¾¦Çä¾|IE½IW¯>ÿœ?‡Ç>Ì8 ½­T2¾Ih‚>r›Q>ŬN¾ƒbL¿dà€¿(ߣ½4´<¼.¾ÍÙ¢¿…;öb}¿2/ÿ¿‘?Ó¿—Ùq¿J{S¿û>Ð}–½kŽ…½ M>Ôçþ?$‘l>Û5Û>_{>†,¢>‰7P=®H= Év>›ƒç?ŠÔ?%İ?J6?÷?&•!?ø]>Ëïë>Fù¡½: K¾µ|0¿ ’¾¿&÷Þ¾ÂÞ½å@¾uœ¿:Ea¿’-£¿¤-õ¿²ÎÑ¿Êü¿Àý‚¿„_•¿/¿b(пœÅ¶¿‰÷¥¿%¿s¿…Ý*¿°‹4¿ ¡¿ƒÈ½ó ¶¼.FÕ=.1°=ŒT¾òò¿`¿jq^¿>Œü¾œž˜½‡¥:½Ïs9½ÝXǼ²²½³Ï¾èZ²¿G&¿G°¿;²¿wÿ4Þ׿IÁ¿;!n¿%ë'¿.Ë¿RþÍ¿m‘b¿l˜ñ¿hµº¿o6®¿a%”¿>Ol¿R\ð¿œûK¿¿«X¿˜ïê¿R¾¶NR¾žÄ:;ßÕè?ƒ3?'70>ï×¾-ÔQ¾éûœ¿1ot¿hϳ¿wr¿L‘™¾Î‡ø>:­>ʆ<³¨¿ WƾÞá¯>ˆ=ñÃ>¿3Š¿6G¾ý;=ãpÓ¾÷¥¿ B¾Õ(Z½Ó… ¿4¿tÑ¿¦˜^¿DG_¾ÒŸ—?ž­=[Ôª½Ъ>§l€?ie?Ÿz? oÛ?!º²>ÿØï=5 V¾‡?±½³1˜>ö!>×ã>ÄV²>ÇO>מ>§;2=Ú0¾ gÁ¾¼ú¿è}¿'/~¾Ùi7€—ƒ½ i²¿$Hc¿ˆ2—¿”¿¿žUÌ¿°±Ý¿¤jÓ¿i@¿Al˜¿†áÄ¿¤Wö¿y™¼¿ž­¿ œ¿§¶>¿pÙ ¾ýcÚ¾ÊM¿î¾ûÞý¾­Hn¾‹“»¾Åâ¾þL¾Õbw¾u'þ98j¾oоQœF½¼7ù¾x¾ÀN½¿½¹¿ °ê¾îÛ̿Ѵ¿A$Ë¿Yü“¿S>z¿UU ¿yÎÅ¿–œ¿¥9 ¿¡\Ë¿/Í¿j5¿&¦¿JB¿Iv(¿ªÿ¿ÌoK¿¦+¿RÝ]¿¬{¿_¬½ó^ž>·[b>ÞAÇ=Ûä>¾‚…Ì¿ƒ[¿Tƒë¿‰¹v¿Œ‹>¿ZT©¾½++>.¢9>‹[&¾Ÿš˜¿g!Ž¿.‰½‹§S¾(¿P:׿Aã!>•?aÖ¾C¿x”½Ê£>Å.š½øaæ¿jîš¿iúB¾ K<Ò-€>ÛB²=³ß½Ÿa{=”* >s>£ƒ¨>òp•?1Å×?Ê<>(ƒá¾üû¼kÒ>Œa]>®™ >†#®>‹e>‰ý> ™]½sm¾€UQ¾ÅÏÁ¾úU¾÷¯¾„u)=¸©Ö>S5…¾ '¿=Š¿ˆ^Ï¿‡>@¿ˆÜ.¿‚X¿žã²¿‚‘Z¿z7Ë¿§¿y¿ÈdN¿¬ì…¿€ŒÓ¿ƒe:¿ „¥¿•(¿=J¾æÎ@¿ ð¿Mç¿Xä¿@è¾—Ò½àÑ*½âK¹¾5î¾Mgù¾[M{¾€€3¾iÀ³½íFO½‰~¾O½9¾Ï®¿Ÿ¿oœ¿W°¿íп˜¾ÿ¡ì¿Ö­¿8¿y_¿’,Š¿=[¿iæ¿ Bn¾7ûs½¢¹¾Íã ¿gÌG¿ŒBi¿U¥£¿$¾æ9é¾ÃC½Ij>‘5>x}½Jí%¾}c޾¬ß’¾øç¿!‹J¿ྫྷÓÈ=’ó>Ó—&>‰î=¾Ù{׿q0à¿(½ý‚¾¾p þ¿üõ¾ÞŠ?I^?R¢9½Îë¾ûÃ4>Yg·?E„Œ>§Ÿ¾•ï¾4B>‹gé>±®¹=Ñ·Ù½Iê¶¾އ¾gR½ÀDK;‹°>>š•>?!¤?!£r>©Æ6>y>Sù™>hÄÓ=MLž½Âÿa½ªð=  7=x;‘ö»è+y<Ïo½4“‰¾ ´b¾I½`€¨½È ¾ç—•¿j¤Š¿Š†¿g‡¿OkØ¿~˜+¿‘[¿~m³¿f»«¿˜¦°¿ÏT$¿ÝŸ+¿Âó·¿£*¿ƒÇQ¿*œ.¾üè½ø:¥¾fìN¾×Úª¿%,¾Ït ¾Åš<ÆÁò½>8¾KD¾?¯·½Ë½{_½ºýç½X®–<¦DH½Æ®Ì¾Ì>À¿ yò¿#t8¿ Ô¾æ"=¾¿£ÿ¾›§¾˜ÉJ¾×gï¿ Ç¿E¥¿¾ë‰ð¾˜¾‡¾E=ß¾b>©*¨> 8l>Ëá>ùâ¸>.‡<¾ß±«¿;A”¾Ì+¼£E»¾4u¾¶t{>=ê)?hK?,žœ¾…Z¾üÏ—>Oƒ¶?-Óä>Ç=ìÐ>>µ 7?}.>Sž€o¾—±Z¾B‡D½æ€½Ð‘<â4#>¼gº?!ž*?Ý>eؽ=§Ù<Üß¾tu¿!¹>¿F¢²¿;Å|÷¾–Iݾ@ œ<¥V¤>K‰>H…<à¶ý¾÷¾{—¾»Ò¿-Tª¿…"ª¿‰¡+¿9õû¾éﯿ9Ø¿O”¯¿5öU¿¬¿;¿«ùÔ¿ãüB¿Ó˜@¿Œc]¾÷½_½œ¦=¤•=îlp> ®>û=O™G½YÆ«½~Vi½ø­²¾­æE¾îjæ¾xy¢=Â^Å>¤†½ø¾F}½Š ñ¾+ ¾ø8Z¿,iØ¿¾ÃèS¾È‹^¿7à¿ Á°¿Ä‚¿ V¿(ý¿2ô³¿ šÜ¾¤U¾€z\¾¶ ò¾íòç¿ë¿ ¼/¿"™p¿*,Æ¿ƒ’¿+¾…yò=ò†->¯{>2€›¾0]¼¾•½>¾¯ë½ï}½;`ËÎ>,[Ü> 4>[Ý7¾½T¿D¿)4¾ 2=°²½ìº¾‚£ =ß>õM>ÿ•¿ü~¿Ý½ùºC=å#¸½ ¿›=(>Èÿ>¼ê%¾(™¿ ,f¾÷(¾@Z¯=*y}=þˆ­>›Ò‚?½e?(ç¾>ƒa¡¾Sâᄐ݃¾ë@¥¿O¼ð¿›*v¿ Ñj¿‚øp¿c„X¿eSŒ¿GIÛ¾ì¥)¾"Í¥½OOнü^C¾€ʾ¬¨p¾çôq¿?ü¯¿E¦¿’| ¿-”â¾N]¾N탾í&¬¾ïbÙ¾“í¾ôón¿‘7¿Òä ¿»||¿;‡:½‚"Ž>0§¼ë„½ìcU=*Ú>5­F=›8™¾âù¾žª¿<]¿FÆÚ¿Kº¾ÁH=#Î’½…ƒ¾ÍR2¾ÿòT¾£h¾’*ú¾èø8¾êf©¾`’±¾`¾×¿J1”¿eMQ¿NÞ ¿QÔ±¿tûi¿o¾¿!-ß¾±Qa¾âŠË¿Dñž¿Zo¿OоòÙ¿5ßñ¿„÷)¿‹'4¿W³z¾îü—½w>S½ƒ=÷^ˆ¾dHô¾ÞÝ«¾³e;ILj¾ª½Ò2”="ØG=·]¾†¾ñ…H¿ ð¾·Œ<Â_,>9ä†=ZE„½_.B=Ð=…{ý¾RG¿2I¿ƒH¾À ¾ÔK;ìŸY¾G ò>'·¿=ýM†¾—Œ\¾ÍF·¾éy’¾or>!²>…æ>÷??A+Ê?+¬¼ˆD¿ä{¿3ßW¿1ÜÆ¿pe)¿˜3Ö¿Œ¿¿d̼¿x{}¿œ¯Æ¿ %Š¿w~¿›¯¾»P¾€&6¾sµ}¾•oܾáí¿I‚¶¿¸å¿ªêˆ¿Zùu¾oúz½¤æÂ¾®‚W¿E£¾ÿÁe¿4؈¿š|¯¿¿‹B¿•Ýå¾ö©2¼Íóõ½˜¼¾»!¾úáã¾ËõÀ¾Â‹¿H¿=þ¿Iÿ¿aŠ™¿†p¿ré+¾ùª¾>x¯¾è>y¿bWÁ¿Zãa¾íO¾s³ð¾mV½Ë==ðÂl»õ¾ým´¿]!Ž¿KðV¿Ja¿0U”¿€"1¿‡Ä—¿3æ¾½,i¿‚¿WÕ¿@¼¾n÷»J‚߾ݚӿ|Q³¿‡ú¿A»€¾ý‚z¾–ƒ½QÏÎ=8ܾPÓ¾ðžÞ鉾Q™$½2ƒ$=`û.>(ø&=óô¯¾Z ¿ä¿÷¾¾Œ=Ötº>­88>Þ e>×><(=<Öž½ú<;y¿¾+J¾›<¾ç'¾Ç®‡¾‡z<‰ Ó¼6¨½ãºK<ŒÜå¾b…þ½Æv=Œb…>!Ž>Áop?#>ó,l¾²4¿*¿­%¿ “*¿;Ç'¿f â¿;-È¿⥿6œ%¿ŒQ$¿ž=¿„B¿0©¾¸k¾ ˜ä½æ»¾¥F¿ sõ¿n“T¿°Ë¿Å¿’7„¿m°¾†\¾äŠ ¿14“¿U¾R¿„ì¿ VO¿™t’¿Kľ¼Hñ¾_[c¾™Ši¾Ä¸¾Ðø›¿ ®Û¿iåF¿¥š*¿¬Å~¿¾z¿{¼£¿#¿]<Þ¾ühӾĶ¿EÛÍ¿/-¿_ò­¾ ©³¼”0 <)Þ=×áˆ>6<½ÄaÛ¿ ž—¿o¢¾lTx=ºwî½ÙCp¿dã¿SDû¿¤…¾®z ¾õWn¿8G¾ïHÍ>+BÏ>À>§¾ ™·¿&j¿Ò=¾ˆj¦¾¡ÿ’¾ü‹ö¾§Áq¼÷í¸½¹VP¾¿ñ¾¼KN½ ­:>†5ê>ÅñÜ>Ý s>£j½J/ھ؅ö¾ïç¾R¥">¥\>÷ÔÞ?N(f?]q? ûü>"j=¤$>;Bê=î;½È氽غ‰>aŸ>Z¾ƒ<ƒ —½Ý¦8=¾»>ÍŽY=A-˽U˜ì½‚ê+½“Þ³=œf>ƒ>K—N¾DJ¾ç±¨¾×–¾õÕF¿SÈ»¿ˆ“o¿d¿è¿-%D¿r‚f¿ˆD¿jí[¿Á²¾•­Ó½³Ðç¾Bîe¿ _Ë¿cæq¿˜Þ¿½'5¿ËÉG¿¨·Í¿V¿»’¿‘¿@5¿cåð¿Æf¿{Û}¿2œî¾ÃÍÀ¾ª Ö¾åÀ¾¼–"½`N}=¶&¾š\þ¿ˆª½¿Ï©”¿ÈC°¿„+¿XY‚¿R ñ¿9á@¿Yè¿us¿` Š¿5¿‰ »¥–›>.HC=–o_=F…=‡î©¾-Mk¾É é¾7ÍG>µX?$ î>Ímn½¸;¾È r¾Ê™¾Á3[¿N¿7¾å .=GnE=àYò¾´èû¿;ç¾T> Ͼ/÷ó¿"’¾úHs9’Ú=€¾‹C˾¨-=EÏÊ>ÇÏ*>éßP>Ú*>¶å>½¾œ[¾z*…½òs$=ýq¦>÷Ÿ™?W¶&?X‹>Ñ^:^Æ{;Á Ñ>""°=•â½½Ë>fØ?]? ƒá=3ÿ¾w˜]½²žD? H}>=d«½X-¾œ3¾t±Í¾„•L¾M²Ý¾hö+¾¾b ¾ïb¿ ž¿S»i¿«[c¿ÐO¢¿¸ñE¿b…¿ˆ¿‹7£¿‘‹¿„ïq¿T0%¿¾ãÈ¿%]i¿„ƒÎ¿¦[8¿°¢â¿·p¿¹DT¿£¡b¿|ü¿HÛ¿4䢿-UÑ¿56^¿B¯Ñ¿"éX¾¦Ž ¾C?\¾ãtÁ¿3c¨¾í—S=쇩>´éǾW½¿‚…пÇÔ¿¸c¿{1n¿7Àû¿<„5¿>åÏ¿+6’¿=‚Ù¿m«Œ¿Mèu¾€Ð>`$ì>Z« <È<ô{»>'S=Ózh=œ">˜˜?£ ?&š>è`M>Té]¼)ß¾tž¾ú¿3µø¿@)Ù¿ ê×¾ªul¾øÙ¿ay¿[ ¬¾ƒ¦#>Nñ¾OZ ¿)!À¾Ù•6>"Á'>VÛó¾k3À¾¿¸K½ ‚†>nɾ>!v=d‚>=øÔÌ>%—ý=¼#A<«ÆÀ>HÈ>Ç»×?T>â :ÅŠK¾’Ÿ|¾=ó#¼êAÒ½¦‰Æ½÷m>Åg?FƒÙ?¸Ã=9ˆ¾Œ7a¾‘³4?ÿ>Zø„;U½Ÿ>ݾZÞ¾àh¿O–¿$M¿ {]¿*™0¿\¹¿žEà¿Î̱¿Ú¼¿½Æ ¿œ·ž¿”¨õ¿i¿¢g«¿Ÿ·Å¿”›j¿ƒÁz¿~. ¿”˜K¿°´Õ¿µÿE¿©¿¡2¿I5¿¿yÛñ¿g W¿TÑ¿2Ø5¿#µ±¿0Oˆ¿$¿K¾é\¾ÝÚ8¿:oê¿rÙ|¿/¡¾ 0ª<ѹi¾¿·Y¿~³•¿©ì#¿šÚ¿eg7¿:{.¿FÌ¿[.+¿f­â¿{ëq¿H׿)²?½a”Ü>¥4S>rHÿ=“cA>R„>õUU?{b?¶œý›>bšs>–xs>`½v¾"ì0¿Á#¿3罿zï¾ÀÖQ¾¸å³¿%ýt¿C"¿u樾鞾½þ¬Y¾­‚Ê¿ ¾3jY>»¡a>š6¾[¿¾Èº½ÚÏû<ëM(¾YR¾È‘¾s¸C»×%=Ô=¢³1>"R>ä>À†^>¶T³>ƒä¾r¾fK½¦OÊ<ÛuQ<ì/>4ì>ÿÖÕ?£>´ÁÏ=ke½™¾b²? Bí>Š)> rÅ>és<£l¦¾·lË¿1Ÿ¿?Ó¿*‘ò¿<ã©¿‚.¿ 꿜V5¿t»5¿7J¿6ñ¯¿fÍ¿‰n¿“Ø¿™Ö¿Ÿç¿¢3Ô¿£ù¿§¶I¿£¯¿‘ù¿†kC¿ŠÆÛ¿Œ“(¿~¨¿n«T¿‚Üy¿Š”׿wF¿YG”¿j; ¿ƒÁ¿€O¤¿v.\¿‚ä4¿ƒkæ¿R½¿81¿¿Ó¿@ $¿q‰c¿ƒB$¿}à¿_v¿@µ·¿:B*¿M^¿i"P¿{º™¿`]¸¾éñ=ãÜQ>ؘu>ºX˜>’>éG+?1âË?B à?/€¶? > >˜@ž= Ì=Sê>‘›&>…W޾=¼¿Sq¿S;¾÷­½j7·½­N¾¡tì¿J¿-ï^¿ w‡¾ÚT*¾÷¹ê¾ÉCÐ=yáì>à¤õ>sB¾‚T‹¾ÀpU¾ §t½âžäÜô¿0é?¿˜6¾Ã\¾ˆ:½í¼>Ì…>ÇÐ9>ÚD;>›=>-fÈ>ËÍ>`±\>Ç·>«>¯?Ãì?‚Î>˜†v½=3 ¼6ÃÛ>i6=ÞP:?(>ƃ”>©Ä>× >¡¼ ½®ÍF¾ãbž¾õ@¾Ê¾ì¿ ¦à¿bÍ™¿yVH¿"ß¾4MM½~}¾šjû¿-RF¿ey4¿y³ë¿‰ZÝ¿œ2›¿§”п¡þ¿Œä¿`¤S¿<=‡¿Ogƒ¿0 ¿‡·Š¿m†ä¿kû¿™ m¿¸¿®‰¿‘êw¿’zñ¿®Vë¿¿b¿²¿’«¿^¥¿6mP¿Eó¿kÇ¿n;T¿JÊ¿9C@¿L˜Ô¿OßÄ¿)Þ¿×%¿ µ¿!š¿Œ±¾Ù ½Šz>¡M?UŒ>ïŠ2>Ó§¦>àZ0>Ï– >„Œ¨>)UZ>7VÜ=ÿà¼hEš»Œ7>Us=‰;Ⱦ¯Oy¿(h¿ a<¾uô½Ú‘¾ Eæ¾%C¾Hs龪ë>¿½¿#JF¿¾…ÑÇ>?X>˜b³»B£¾ÍŠ¾ë…»¾™øç¾²³'¿$µ—¿Y2n¿X {¿NE¿@2k¾õ¹¼$pã>¨Î>¬îy>MÀ->ZÙ¥>Ɇ>ü@•>Â=#>†Ì>Êݶ?‡£>Ý®5¾ Û’¾êô¾ =j>´h\>µ°Þ?.Ð]>òêÚ>Ýüi?i>àúË> ½êL ¾ ”v½÷½ ¾½(Q¿?â¶¿X®[¿½¬¾„Ó¾•ŸÏ¿Îø¿dVD¿€Æ#¿‰ÐÝ¿ KÏ¿ºË¿»ž¿›.½¿U«³¿ ÞŸ¿oÅ¿;HÅ¿qÜá¿ló¿B¨¿Uør¿¢Ö¿Ó ®¿Ç±x¿™8 ¿ˆ»z¿¦òI¿Æ’&¿»Pf¿‡&…¿!¤"¿æ&¿6-Q¿q¸œ¿btc¿!²Ë¿ .é¿)2®¿0’ì¾þ_Ó¾¥ŠÖ¾ªÚW¾¯zá¾Â>(4>ÃØÇ>áw>ÊtK>£Â÷>iͽ=]¢l¾}(I¿Ѿì[i¾0b=–É;4‹µ¾¤¾B‡¾•¿d¿)ÛG¿ õ(¾Ù×Ì¿ dÁ¿.¬Ç¿ðo¾YãܾCqB¾ïå6¿)ú¾ø ½˜a°>Qkî=ç D¾u®8¿%%¿ªÑ¿ j4¿?þ׿a¦¶¿bq¿\;Ê¿oö2¿räà¿$ºó¾ö©>=”•ǽŸy†;ЮW>Yù[>Hª½£÷¨¾nÒÛ½l> "½ëlX¿&¯¿E$½¾™ò>)ÿk>[?´?B>È÷ò>³&> þ >=¨Ä<Ææ›½Ž§S¾@zQ¾ì죿Jàé¿v–¿ups¿w)2¿ˆ\¹¿“Ç¿’9!¿4Ù¿¡41¿Éxö¿æù¿Ô3$¿“;ž¿ )ؾÎx}¾ü¨7¿*»Á¿2hy¿ ™r¾Ï*Ú¿ÕB¿…᳿´Df¿¥Ô¿bA¿.ì„¿`D'¿“Š¿‘;†¿Qò¾öâ¾Í_Ñ¿ÿ¶¿EÅ“¿:5a¿ ¡ò¾óUæ¿ èl¿7”¾¨úv¾‰4‚¾¸ º¾”uC>ƒÇ?XÎ?,Åj>Ðò;=ص­½GÖþ¾/:¾Üð¥¿EK¸¿dh|¿ ½É»g=óúƽء\¾æ À¿c„¿g³¿`l¿ 90¾Î®î¾ãÍU¿MJ¼¿‹-L¿]¼Ï¾®]޽Ó徕E†¾Üû¾*ØJ>Äž>¹ç[=¸‰}¾|Œõ¾Ü&°¿/•¿X:à¿‹­¿†E¿D¡©¿í"¿Z¿"J†¾Êô2½E¡ô=}ƽñ •¾’yؾyQâ¾;ʹ¾Â†‹¿8¥¿SÓá¿$±’¿Lf¿_Œ!¿’÷¿l®Y¾Ü±–¾U¶¦ >3Û2>ý½>ì:WZz¾Š÷.¿Øf¿EÆâ¿g7«¿‚”ð¿˜V¿¬"¿©¬o¿Ì¶¿^‡W¿V»¿Œg°¿Áµ\¿ßпÁÛ±¿iúŸ¾Ðp«¾›ÎE¾ÞYm¾å²ú¾– ½ä¨ä½ð夾  *¿Á¿DVÄ¿)ò¾Ì“´¾‹è`¾É$5¿Ô©¿å½¾ð/ʾ̨ϾÒË:¾îj±¿Y%¾ùUd¾×ñ¾ºuö¾6¾GÍà½Ü²ô¾XHY¾Ó˜c¾¥}>^8•?FøÑ?R‹F>¾õé½È²œ¾¤B¾îÄ_¿9ï¿n—0¿EÚ¹¾„Å!>+ûƒ=¿ x¾Åòê¿HT¿P°¿*‡Å¿7°¾ÙˆQ¾SN¤¾-…í¿#f¿Z¿<¿7Z®¾N9=Á÷ؼ™н°ý^>fŠö?*T? Ñ>>1Y=½e½ÛãÆ¾•Ÿq¿EF¿”`w¿‡Ln¿~Ô¾V¨u½ó ¶½á.ím¿>Þ˽8‚}¾(¨Ü¾GS¾¦Öå¿)k|¿|,¿…‘Í¿zy?¿‘’§¿ºX+¿¸´¿t”ç¿‹ ¿ ¸¿ ‚„??LM?;/C>ïÜë>\"Ö>G`ˆ>vw$=àh¾ß,­¿_Çr¿23¿i¿_rä¿~쿆ß¿Z£ ¿&¶¾¶-#¾êËA¿>¿‰J.¿¡Ë¿‹ŠT¿"‚r¾¢!¾…ãP¾¥òÚ¾:˜<[ Ò½-´*¾‘ù™¾Ð¦$¾©I÷¾`ñ•¾/ðš¾&wо6—V¾FGó¾(Ž©ÄV¾˜¾´Æ>¾ì]d¾¿tÙ¾€%¾¾my¾mL\½ÿFÛ=^g >?‡Ã>9‡/ºé ¾kœG¾3~÷>¢÷£?aÀ?vm ?ÃÍ»© ¾8·¾üWÊ¿Mu¿q]¯¿¼¾ {>Xˆ‡¾&Ï‘¿<<3¿rÿ@½ï¿ž¿4¿èp¾c” =q”½ *¾šì^¾‹C=„1ò>öì>>–0>î#®?; ? - >µ$<Ö£> ô¹ËBH¿k~¿„`Ñ¿yÌ¿Èë½´7Q=¯ã =ÿ>Æ> +H>1æë>>=øþ5=øæ_=åK¾rF¿.¿Jx¿a¿{dú¿§,(¿ÎH>¿»-¿py#¿ üZ¿65 ¿%ŒP?Oê+?TÐ??ZÙ>ÇaA>Ãñ>ál0>FξÁ£ž¿`lþ¿yÞš¿N,¿4¿9(U¿ Ö¾´§L¾GŒ¾°öÑ¿o€¿<®Å¿KaÊ¿bɰ¿_ö¿&àH¾Û"¼¾¾fC¾œ¢½Ù:—½“P]¾æqá¿dæ¿dGÙ¿3¢¾dåö¾‡¸Õ¾Úr¾õ $¾º¢K¾ å=½®£/¾ÐG¨¿¾Ì²Â¾E¾¾ è½õ=Þ;½>¾“+>ãGR>±ä¾>Qj=‹ ð=£¹>×±?l*×?‹`6?;ÊÙ>gÿ½ÌÓE¾Æ—ï¿K¼¿‚Z™¿8'U¾Fìµ½À.¿ 3?¿tàÒ¿^Úr¿§¬¾çTÛ¿4Àp¿M(оê]»Ž°=÷uã¼M˜p¼ÈÕ >¶§>fþ`>-B5>‹&¹?ã?8ç>μ·½@™Ý>\Ò=i÷‚¾Ô„^¿R”¿O‰Ò¿ßp¾e|¬½¬¹4½G‡½‹zö½¬¼ÍÅd=Ñ­P>JÑ1>,àþºÈª¾ƒjg¿ÈD¿3â¡¿nÛK¿t”¿³Êå¿ $}¿m=ã¿EöÀ¿9L ¾¶ÉÌ?')e?3™+?ô>Äë>×®‡?åÊ>–˜<¾[æÚ¿a'¿,‡à¿Ö3¿"¢Å¿.®?¿‹€¾!½¾Äúó¿TÛÑ¿’*W¿…A‡¿Vá¿`sI¿~‰^¿f ¦¿+‚Ñ¿´¤¾ÂQ±¾sÛ_¾À-ž¿h<\¿®7Ô¿¡G¿SŸÛ¿#dG¿Hጿhð¸¿QÛ뿬ý¾¸ïؾGªr¾‹G?¿ ñŠ¿/v´¿ ä8¾¸Çx¾Ÿùá¾$…Ò>by?ê:>Ü6>Ve>> ¨> eW>M5>­.?LM²?ˆa€?J<ƒ>‚ý—½ÙßV¾¹ºÄ¿=Ò®¿‚ª]¿XÅä¾á5]¾¿(3¿5$,¿fr¿& ‹¾¬û¾ß¡a¿MÉ¿gÔ/¿ª¾æ´<{‡õ¼½l(:“ô=ê­>?þý>Œ0¸?eð?UŠw?M@ü>½^æ½Òa½ Q‚=K§¨¼ñö¾›µ¡¾ó2>¾âŸÍ¾¼JZ¾±pP¾©k›¾¤!e¾¹ѾÃló¾ˆ4½Üþ=* B=Çá¼É×—¾ Q¾¿*,¿66ï¿€> ¿Éø¿Œk±¿}´¿lè“¿d÷e¿'„ÿ½Y?>Ì`ô>Á2><•¡=U•¤>LyV>¼›ó>Tšš¾¡Ê¾šM*¾YG¾s¬ø¿ÄY¿2.Ï¿Ùß¾¥rŽ¿$g¿€s]¿›ã&¿y½¿8·»¿R¿e¨½¿qŸ¾ßqO¾ÅJd¾¸þÜ¿ëv¿`tÿ¿ûÅ¿8è¿KôK¿eí¥¿Ž›¿…1¿9µ¸¿r;¾Ýß3¾Èþ1¾ÖF!¿ÂŒ¿)e—¿¬Œ¾þÔ¾Þ;¾+©Ö>œ‘ä?%‚>‚²î½Ûn¾`h½y;I½‚‡¥=›H ? $?d=_? ßÕ\Áô>ª«?–¢?pK7?•Ñ)?}kƒ?àË>;S¸=ÚÁì=£3å½3.æ½ÝI#»UiŠ=”³º½Ÿ|޾¤\Á¾Øý޾Õ8™¾çVê¾óߣ¾­T½Ø}Þ<¡2z<Ž",< ƒÇ½)·Í¾—am¿7Í¿ƒW¿„Œ£¿aÅ_¿S Q¿i5Ó¿fwª¿ :h=£Ôl>†ø =K(p¾›êľÃû½`[`>Aþ6:{]æ¾…h´½ëVr>w»æ>kãÙ¾W¿sü¾À«Ù½ßdT¾>cÖ¾øŸ¼¿ €¾“¥„¾8¾Õס¿"û4¿qÕ¾qv¾:¿O¾˜Ti¾­V¾Ÿ=¾©Ô¾ª'¬¾’ƒt¾ÏOé¿?«®¿nZª¿&NŸ¾VTG½¡ü¾a:辚5h¾Žßœ¾£ D¾ÊpξÊN(¾´|Û¾„É‹=2©í>ìê? åÇ> µ¾Œ”¾“ɾ ,Ÿ¾2%î½›Ä+>Åæw?BÚÜ>ù8ò¾5`Ö¿ ѾµG1¾cœ½Ö÷é¼âà =ÞÏ =ޱ‡¾)ÍP¾®5¾Ä«ð¿ï¿>¼ ¿O­÷¿Rþ¾\\½Z{3¼‡c|= é(>kŽ÷>®3>ñn¡?CÁ—?ŒC?¡é?‚ýÈ?*om>íê›>¿Ñ>KwìŸm>Àí=›j¾—ÎT¾ÖÄé¾½A5¾ÂLE¾Ï£%¾uöš=´Çƒ>¨Õ,>¼•>—Q±>(ÀE½Ç2’¾ðXj¿@éH¿OI¿>¿@v‚¿UX9¿B—A¾Èñ=LÀ>tíF¾ÃÇ¿äÑ¿z1½è¾=Ò~¾~†Ä¿i©¾ —j>ýw?ªî=ŸØ;¾•ñ¾d>!pX>Oœ=¹]…>T‡>®‚X>œ¦•¼Œ|x¾†¾?ï…¼à $½¹ýоŠ.ξ|{O<8Õ >ƒž>£'Í>2p¾=U¾þðÿ ³G¾HG>[U>cͽ$²ý½NN= ´Û=kW ¼º¤J½Pâ<4ì÷>'>¼??ª¾?åA=üÀ!¾Á½g<2=ÕA=Š[=éè¼?øü?Vÿv?é?<͆¥¾g/<•rC>©v¨>Üy>Ü}¾>ØL9>šJ=x0ɾ ¤¾©¯W¿Ÿ®¿WpÌ¿\g¿ï¾­±Ú¾hž¾6½Žˆ:=^‘ð>ô|>” É?”Ø?]:?n×F?IŸ9?#[? >ò%¢>cÚ;‰?=Çy>aÇ9>ZV´½šËR¾ºS$¾ÑgU¾¥Z,¾¦–ª¾¹é*¾Bñ>xEè?,W±?QÔ0?)Ÿ>É+–>-Ÿ¼ ľR¶¾Ôk‡¿ u¿'¿(ô¿…¾¨C›½t1{>I#6¾0–‚¿ Ì™¿ H†½‹Ç½¬7¿§/¿\Ûú¾ .Ã>íjo?Ƶ=Ç%¾ƒºÜ¾|p=·ÔF=Ìä¹<²êÉ=Û–ú>òÆ>ê\¼·¿Ã¾‹—¾˜¬¾dvö¾‡3©¾¯ó=¾IP>D¢n>ús>Ó!Þ=;aF¾žÈ[¾Ü¸m¾šì^½Émݽ€½õcª¾ó<øÏ<>ŠŒ>µD^>œ§§>œ>s>Ͳ(?µ÷?’?Üe>È2ø>#ÞN>Ýà>¸ü²Ë>È„´>ylÿ=ڜνA1³¾›a=¿«ï¿<0v¿(ïd¿ ¾ßÝÆ¾ç”޾þ“»¾ÿß©¾ÄA½û<Í>BLŸ>Óâ‚?‹:? ‰Y?ué?þ†>µÃ >ë¹¼Kê½{‹_½¤j¾Fs¾ˆ·ž¾©„H¾£e9¾—Çc¾«~¨¾Çªò¾‘m„=èÑ?'p?t¾?P€>ö%ü>©˜k>¿Õ«>”x½LÈd¾Å’{¾ôÕ¾ÛZP¾á¦Y¾÷Tc¾Å²>,®£¼þ“ϾÅÄ&¾¼4¾'!>¾È<]¿zK¯¿ô(¾æ^n>”ƒ>®öf½ÿI¾ÜîݾÁÇg¾æô¾ÚÁÉ¿ tý¾à%…¾R…½Ëžý¾orɾë/y¿ D0¾ë[¾Ä¨¾¼¼÷¾aìe>ð*>ÏØO>a§F¾!À¿)ñ¾±¬í½ß•t¾›f¾¹â{¾çކ¾€3¬=Ü`>Ð6C?•·?!Rí?2®³ùþ>¡rÃ>ñwâ?@~ô?‚2Å?žt­?¿W ?á(w?ìñÃ?Ûþ?Ãq?¸qi?­å?ðÄ?PÞÚ?"S©?óV?'Kœ?')s? ì"? ®>¯†<„§f¾Žnà¾È£¾¯¯8¾­ì¿n¿jOð¿‰âL¿V[Q¾¹–<Šñ>T×î>¿C5?Ò?æ>É—=þ½þ=…«Â>Æç=¯kt½ûƳ¾i¿^½óx»×Ã8½«Õ³¾y¾2¾Ÿë¿¾¤‘c¾¤KS¾+3>Ÿ®J?<ƒü?>D>é_]>®Gþ>î1>Ú?=`OʾŒ€Â¾Œ\ö¾Uоۘ¸¿F‹À¿Ppg>“~Ñ>kȽqñg¾}­Á¾ÓF¿J¤¿•?Œ¿‚iO¾²¹ð>8¢W=ºp9¾¯À ¿öx¿*-ý¿BÊ™¿rë¿€ H¿KÛR¾èo¾\†\¾u¡¦¾ß‰+¿¬»¾¼Q¯¾k>Ÿ¾Öy¾»21½É!”>ƒ9ÿ>PT_¾=Ô¾žvã<Î?¸>œ®R>Õ@¾Q˜¾N†Ð=ä8ñ>Ʋ? Ï?9>Ë?xãf?Ãó?Œ*‘?t³h?Ië?%.? Øß÷=qì=;ؽPµú¿n¿“|¶¿±/Ó¿w‹©¾›qJ=Ä3ù>uœû>æBÌ?3˪?ÃÚ>·P¾0Iß=ž’>è"?”þ>e{É=êR¸>®NX>ë’>6”Ö¾+Õõ¾8Ÿ@¼öÁC½—id¾/åG=r˪?ÒN?)M<>æî,>‘Ÿx>¢ìa>w/A½bÈþ¾Y罄B¼~ó ¾ÌoÍ¿c°&¿w3>ö¥Ô>ã™5>DÈž¾1y¿‹å¿có¿k <¿âõ½†B1>!悽<ˆñ¾ÕV¿/¹¿`ÉS¿Á•¿¢´}¿—÷Ì¿Y ¯¾×!B¾-±½ôáí¾ L1¾ÀâO¾;@¹½¦·Â¾¼ùÅ¿.+ª¾æÅ6>ç>É“>*1ë=¯•1>ãÞC?==Î?¨ž>¼Òò?ó?Lwñ?A$ ?¢â?8!?ó«?­>õ?œO3?|øì?bå>?t§ñ?™Ìÿ?Ëò°@=ç@Ö@Ie@‰Kš@®d@¾Ù@³…Ø@žz@‹ÂK@n»å@5<Ò?òÒ“?°<2?²ÊÚ?Óâ:?ç;?á‡?Ò1f?ÄŠ?­ÉO?;½?Ð7>…Üî>0KD¼Úíü¿¼Ê¿¨M¿»•ï¿l€œ¾wËy=†>™>p>¶‡è?€>½õª¾k—‹¿çp½§¿?ô?MÈ3? Ó>ï ?-­¿?5¾ý>›3½˜¹F=&«ß>º¨]>µXa=Í ž=û# >íï¾?#Ë >å%7>VM÷>ÚÇ=?*½³’Ÿ¼õ€Ñ>G ±> í• >œ[f½'g¾÷ê=¿ìվώ±½Âàd=¸Âw=w*žin¾ÎÇÑ¿,îÓ¿z岿¥+°¿º0¿«ãi¿|ð&¿ Võ¾qÀu¾hI¾âPù¿Ò¾¹¡ï¾›ð¿,B¿…Ý¿@yÄ=î>ò˜÷>šçÈ>)t>é5?>]?-J ?"pã?n£“?‘ßx?Rh¹>ÄA;>òES?wÈŠ?œF…?† ½?X?nG?¢î/?ç­@!˜ÿ@TðÃ@‡K1@¯âH@æÄÌA vyA99AYA\Û@áÄ@¹Y¯@†F,@.·)?ýñe?úðõ@ {@›w?ì”¶?Í??½ž}?«é?l„?8¨>â½8"ƒ¾¥^’¿`¯1¿´!t¿µÇ¿g]h¾Ð‡÷¾ˆ@¾vŸx½ Q>Ü/½µ§¿£ß¿Ež•¾›ª>±'É?ß°>ësc>Ó´ý? †?Ï<>‹ƒ\=a—÷>yh?93?!ö>º">£ó–>úòC?IT>‘9¹={n<4ú~<ШÂ=¶i0>–•g>å™+>[˜¾‚$¾Äðʽ•Ú>žw>¶Z>¯Ó=#®!¾úô¾Ž5\½ª ¤»¦Qx½¡œ‰¾#SȾsÙ»¾Î?ú¿':÷¿o f¿œ7x¿»ç¿Ä ¿ª¨ñ¿lÊ¿ €¾ù§¿7Ä$¿fÝ~¿T®a¿Fw†¿‚ƒ¿›îö¿WTº½©äÃ>ŽÆ¢=V˜é½ù’M>Ê>ÅÓ>U}>ŠÀû?Ô?P¤?äÓ=û~î>o¥“?7•s?qÏø?LÍW?;Ó2?‚fx?Æù@Ô²@Q§ô@“g0@ÇbþA÷‘A(‹æAF6‡AQ/ëAJYA;€ A'ûXA ¥@Çñþ@>=@4]@!±H@à¢@ &?âM™?»Z'?®÷N?¢žé?x\˜?µ“>$ý¾Ëz¾âIp¿b#¿š]¿’´ò¿Tßo¿ Í¿#>«¿ ' ¾íËÛ¾¹¿À¿AÛ7¿B{É¾í¸†¾›ñ¼~7Ÿ¼çW<Ó?=ù…€>@%Û>&vx>+6Ã>ŽÐ>È*>Êhé>½7_>ÐÒ€>Ëwu>Gö½‹Æ¾8>$½Ï y<âË¡>c™>÷ÓØ?gF=_§¾ù^ü¾ïÇ„<¬´>-ø>š™>¹ž=†à¾2ÃY¾Dì2¾*¾u;·Øÿ¾ˆ0¾eµ§¾ÓmÜ¿)žµ¿LvÜ¿sÎ=¿£x¿Ê›ñ¿Æçö¿‘¥L¿'Ïô¾øÙ¿0™¿wí¡¿‡Zƒ¿†ŸF¿‘0P¿ÐO¿@ré¾Qàݽú×¾ƒä2¾ `Ý<¤£Ë>€mi=I3î¾4Ï»Ès¬>b>ˆZ~>Hñ>©ûA?7Žp?]ov?IQg?b?¦o?î:@$—d@mkf@®U‡@øhA(ì«AVîÍAxÝÂA‚’âA€ RAsô³A_·PA;iöAæl@²A¬@pAq@Eä¤@5‚Ô@åø?øÁ?ÎÔÀ?¿ï?®ån?‰>–?7k4>æ5¯>s0½x0…¾×ÞÌ¿$El¿"#µ¿'¿Ýð¿4÷¿6ž¿, ¿7:Q¿IôŽ¿:†|¿ ͹¾éª ¾þœ¿kˆ¾æµh¾­ 5¾ƒç¾ŸM=5ù>øI=¸y¼œ{R=)ê>µu>ÜÁ>uÉ€¾)N¾ÙVϾÅâ8¾vØ™¾ Ì=N«>ª\4>¯N¾.ûοBÀt¿Er{¾Ÿ2ì=‘Ÿ~>,Þ>“Ïén¶? ø>^½ÙÖ.¾¸Ê>¹‡>¬ê>êD¶?+k„?f¼›?we8?{?$?Úb¸@ ‹ú@4à@yMX@¸HA[ÏA=7AsÄ­A{ÍA”øÚA“œòAŽ@&Aƒ¬ÜA^3A$Ñ×@Ù!?@ˆ@aï@L1@5`Ö@’@‘š?ëåñ?Ë-ê? zô?ÂM?eU?HgZ?þà>"½{¤7¾GW¾KÆÕ¾®Å¾êØû¿Ø¿%¿[¦Ö¿oƒI¿8=¾Øßü¾¿¬¬¿  '¿b,¾ú䙾®À¾”V¾M¥{¼êMb<”ã5¾%þ¾£¼µéÖ>Þ¢M?å>4ÚÛ¾®&¤¿[9¾¼o“¾ˆZì¾›mâ¾U‘=Òì´>z[2½Ç³›¿pv¿6^ ¾Þó-½Mô=”Pù;µ×T¾2篾…w ¾­«¿÷y¾þ®a½&ш>ðÅp>ÓÄë¾&¯:¿$¿³¿Hž¿lÕV¿ŸZ‰¿²ê¿Œ@¿(¾A¤½¤D½Ø¹ú¾P¼*¾ÎuÉ¿+a¿[fs¿PDÛ¿ J€¾ˆ~¾?½7¾O/ý¼FY&>Ðv‰?0½Ð?m™>•Š>GdÁ>°¤_? F‰?4••?X±t?tž ?ƒQ÷?™FF?ÌÍî@ôì@%Î+@G$7@‚7Ç@¹o‘Ax¨­A’A›wåAšè½A”ÛAˆæ]AgdA-á@æ˜g@–þÌ@fr@QO·@B£Ê@-($@Qi@ ˜È?íî]?Å =?­Èx?©´?÷?iK?]]>h X>©œ=©© ¼3ø¾lC¾ŒÏ^¾ù~x¿=Öp¿\7£¿95°¾òæô¾·™Å¾ËæD¾Ít¶¾Õ¾ Ä1½U*ò<æŽ=§þ¼Ònd¾h¾)fl>¢È"?PWÝ?CÙP>Fd徜¸¾¦Ú¡½·ú3¼¶Í½î0½HÓ>ˆá >üÜ>¯º÷¶ ¾%Ko½‡$…¾½ðµ¾²öß¾¾|’¾—cj¾)ܾv¾¾êP+¾¶àt>Jà‹?,á??½\=‚Lû¾Â^K¿7¤ï¿‘”¿º–B¿¤éW¿4Ìç¾€âD¾XD¯¾£‘˾†•b¾¾8»ž¾ã0¿&™¿Ÿô¾‡,;2O†<Ø‘ž½â+ʾÄF<"^–>y)Ì>¼ì>Ä»”>ÍSq>òÅK?Ÿi?.{4?JJ?nˆ,?–{2?ÎÈí@—_@5I,@S«Q@s_€@”â@À“ùAJ A0Ä}Ad /A‡NëA‘‚_A‘E½AŠ)£AzñAS¯óA <@ד@Šœ@L×@9‹v@0ɧ@"›Ó@I'@¡S@Ï0@B{@˜R@Ô)@›Â?Ç£?|¢? 5Ø>³“¦>§Ïd>ަ*=·Uî¾ù¾™›h¾²€­¾Ò¿j¿oµ¿?\¾ÑäV¾.ž¾]›x¾GU#½ÙW = eŒ>ƒ6p>‡=&=Œw½°Ô>ì½?*…?3ö?A,ƒ>äܾRP½€ö1>w‹l>Ãn.>ß ¢?{?Vu—?_ç?'VG>¶ä©>;ª>>Üm¿é¿~¾ø[è¾ ï±=¹02½å|´¾ÞŽ ¾¸ðX=E¯»>–X…>`ÛÂ=ÃBZ½R†¾ë¸¿4Œ¿µ<¬¿~å,¾›ï·½{®¾‰©x¿2ð¾Ôìy¾3.i¾™¾˜¹1¾Ì¿6¾YØÑ>W/>´ x>dÿ½õ9þÖ¢¿l¾×%M¾S²f<Í+À>Iª'>œë->Ú[?Å„?cðÈ?¤B?ð°Ø@-à‹@hÉ.@Œ†™@œÝ9@«£0@ÀÌ@Ü>:Ag³AèVA?:ïA^k1Ao6AnAyA`ÒÃAKM‚A,ÆÌAUü@²5@_f]@Fü@—@ G@@ ½ @(d¼@N¼.@q·ÿ@…1<@ˆ+@~ù…@U¨4@+!?Ô^?‹[3?TËš?Öÿ>X}À¾Pd¾iô€=¼–ó>}†¼ãNоËoŒ¾ìîG¾¤À¾ = ¾é‹¾ímè¾ZA½=µpÂ>:kj=‡PÀºy>U¸d?œ?;Ûû>æ÷<<ÍçR½ÓÚ+=çÇ5>Æxî??c‚€?›“œ?¢€m?dx‹>§©ð½ÑŒ¾¡½¾®=}¾Çÿc¾À=¬½ÙíÅ>´×W>ás¼|ò¬¾ß§¾Ë€\¾zey¾¸À[¾é:õ¾=%‚=~z¾÷¿o;¿Œ¾»¿’:´X)=ÒÊ>¾’¦¾‰ú]¾>jM½ƒ½£é€¾E‰I¾IŽc=Ð×>¬QM>ÍÿØ>*£¾dÙÔ¾çM–¿°[¿ æ¾õËœ¾¢U­½ü@Ö=†$Š>«^?Fh«?²>@ ¶@QŒW@“€&@¾_@ÞŠ@ñk@þ¦öAY A ¦A ð’A ªl¾³ð ¾Âiè>Q0¥?Úu>´;nÓ¿ Uk¿Í¿,u¿g“‘¿{`£¿KQÄ¿–‚¾Ÿ£¤¾P¸>¾Ù±¼cF‡> È >Y«ì>}<“Ük=‹=Á>tT{>»¼¤?ms?a+)? Eè?“n™>äP³¾ØP"¿~¨>¿ž‰û¿¢Ð>k?%>w"+? Å”?m©Š?B±8=À[¾•Ú#¾-u6¾>Á¿"fÄ¿aü¸¿û†¾+žõ¾Ï¼d¿W~…¿D̾Š÷=¡=«Ýö=»Áµ>FÈ >h £>’æ<:®ý½Ž8¦½œ:•=¦>ç=!ë´¾ˆå ¾Þꋾ’n²½ˆñ@½ºŠ¾QÐÿ¾‚vú¾ÉÅ=gÌø>à4?¾Q@C@UO@]Ê@Ù&WA ¾ÕAgA+Â]A2É/Á>…>s¾‰ìº¿4Xë¿pôk¿Bš¿;O¿ší¿‘®¿‡˜\¿Yοî´¾ŒEK¾¼p¾zú½Í)]=ªõ>Lôæ>ž[%>Õð”>Ü2Î>Ù†?gí?X»µ?2±2¼'9k¿\¸¿±%_¿Æ»ö¿ÇªE?@5I?:¢Ô?wó?›â?c*Þ>I.z½wÁ>‚)î>|\¾š©¿/1[¿.ø¾Ìu¿'¥¿R‘¾ÿm½«M:; Í3ºƒL>e´³>æœ.>ÁÆ>@”>$&T>SE‰>(=_½©8¾°h÷¿-ˆ”¿0[\¾q…ê>„.>ºù>C§¡=Ý›î>Xæ>ÏŽa?Ci?Àâ@-0¦@Šóß@Ì@6A ‡A/°)AK ÆA[4ïAbÀÈAa#IAP¤tA0sïA |@Ú©@´Uõ@™ú?@†õé@z¦@nÃü@Mr'@ r?•¨Ö?)xå?3Å2?`|t?zøÉ?ždŽ?õI@Cg@‘á;@Ë#ÄA¹«AZÎA áAòRA@ðb @® ’@`y?û š?9)¾9“Ä¿ Ä?¾Ò5é¾T=̾€Yj¿¨¤¿bZ¿£põ¿½s¿¦w¿xíz¿gß꿅οo#.¾ÿèí¾SÛ¾‹?£¾§ ̽™Áè>}ú‡>Àþ>ãô?¨ó?2ñÇ?ª>ì:l>êþ>­%Ù½ý0r¿3!Ò¿L¿y„¿bf¿?T¬?C«ÿ?`Ý\?ÿ³?5 >#)Š=rïý>϶h>ÿšx>|)¾>žDã0¾¸Æ›¿.Vª¿#Ÿ¾év>?T^>hD>œÐ>¼e>á}c>\€¢>*>ÑW3?)åp? sÖ>‚=¼»©D¾Àƒ$¿D4Ú¿PÙÙ¾ÅÚ¤=ÖÆª>Ž.U>™"²>þ‚ ?SŒ¢?Š!?¯¥‹@@Z4@ª(¡@õÒVA% bAMêAlŠAÞ8A„vçA‚ÆAn£˜ACñiAK @ÐÝš@”Î_@Tz@ i`@~à@ Wü?åFK?…àÏ>Þ,|>šXp>áwI>ý§’? À?D™ú?·¼@ûµ@biÜ@¨0@ËœS@ïë:AKºAœ@þF@Ûvù@¥g±@Sò7?ß±¿?3‘Ê>Ëe¾!µ¾à’{¿7³¿M0É¿°ùË>¬L>«A-?&EÉ?tý3?Uèõ>ãƒc>Fõ:=½Ì•½üˆZ¾ÚGz¿h¾šÿœ½’И?UY>ç°>ÆM>åÊ>–Ÿ½6Œ¾€Î=ø„>FÚ=ï¼D=Ž­X<¥޾}òX¿þ ¾ž_1>[Ó1?8Ô?øç?‚ß?%B³>Ñ"®=-WS=øß}?%u2?xµ?Eþ>Â,à=~Iœ¿,–‰¿F‹0¿¤Ø¾T_½GµÜ>gðz?RK¤?½#?ò_ã@Å@<Y@бÏ@Ëa=A A7òkA`…ÄA€I°AŠîAsoAŽ´ Ai¬AQ?þA/ @É‚@|Uß@9 ?¾5[?³Y?ÁKð?Ÿõ?2æ>²KÒ>¹ƒ>ÐQ >†wÄ>!ë;>Îï[?~Y]?Üf@ …@X1?@ˆ]‹@Ÿ%ô@¬ý@²ue@¯»@žÁq@{*"@?Ž>¨ÍÍ>WN3>>ÿ¾Œ§Ò¿owY¿Ž±à¿C¾S¿"n°¿H)¿ÈÆÖ¿  ¥¿¨U¾>y¾yåÀ¾LÃŒ=«ÀÜ=¾C¾vj‘¾©G<Üd¥>”yà>$Fs=èÏ ?S?oÃÛ?H)›>]S¾Fµ¾™?V¾À.€¿d¾úmì¾U ^=±(Ç>µÅÇ=˜¢…¾#Z¿½ÿ–¯½ž’ϾJ:~¾ À±¾œξŠq ¾{X¾@3é¾Sø8¾½q(¾ÒõŸ½ÀVÜ>·ÔÆ?¯[?C‰Ù?o Ž?b„Q>áOï=M&>,:=?MÝ?3Wt>¼Ük¼‘ðH¾š¬¿ûG¿RÊ¢¿@U¾ðP˜¾‘•ó¾%ÞX>†R…?‹˜?ø‰›@! B@?$r@r+î@¤Ý@áy*AÁóA;U.A`QWApA‹YA’A­A‘©A‚ù!AQB?A²Ê@»vl@T„¢?ÔË9?l8?—žx?ºJ ?Ÿùz?E™¯? i?Û ? Ÿ>¯z9>Šbð?úZ?nV?°…0?æŽd@ "¼@&1Ñ@= @O3Ò@TB@L¼6@=4\@Ü„?Ë·Ç>üËž(‚‚½¯*> ;‰¾qsT¿z¥¿—+¶¿(vl¾ŽVh¿'𪿀¿–©¼¿!í ¾—¬¾„Òˆ½¥æÃ>[Ñ…>³®¾uÅ$¾®} ;v?0>{«z=öuø=¬´Ã>á õ?9Êñ>Üj¾mr­¿/âØ¿Fq¿;”U¿/®ù¿ ľ¡AW¾ >>SPn¾>=N¾ãJª¾³Æ~¾l¢½—Ê÷½Ã×£½ï›¾0Ú+¾‘ß ¾Õïä¿ ¤¿~’¾Òôž½êö>ˆY>Ûõ£?&K?d œ?P Z>ÉÔ=·½ü>¯>F½Q½m™€¾ÜÁ¿0,Ñ¿oíš¿œBH¿Ÿ' ¿Z»¬¾Êk¾G_+½n>Õ:|?ž&‰@¬h@,Ö}@Tßz@…¬b@«“@Û¢+A ÍåA(E›AGMAdl$A|àaA†&NA…*)AnÚªA<äAúB@¥I@6iã?±û¿?o"¤?œ9@?»Õ\?˜ã?5ž§?¼Ë?õ?ÖÇ? Î?&ý"?c€ô?ˆÐ?“„?™(è?œ>?¦µ¡?Ê:~?ö´g?üÁÀ?Óð?­´E?¡®ö?ŽÛ>œž¾¶V¾Ý'ľMãh¾¾0œ¿j¨Ž¿ˆ…B¿ 9½ŒQ=¾„R¿BœÄ¿a¯E¿ªÓ¾×Ñv¾žGÃ<…µé>ÑP‡>ÉŒò™Œï>•C–>´¢"?8?0é>}œ¾Ù ƒ¿Z;¿i2¿;iξÕ‰½š¹,=ÇsÛ<Œ¨l=ý=&¾sA=¾£_¾‰t=°Ê¸>rrN>ÔH5?GN? sy>‚˜¶¾0Í-¿ÿš¿>ù¾Ÿ)k=ð7>E0‚>füè>º”Ì?ýŸ>áx>F´=Ÿºi<¶˜‡¾…ó‹¿*ŸZ¿W˜æ¿_䟿‘ô¿¿y¿¸ÿ¿g~¾Ô+‘¾h .¼0I?S? ‰~?ôÓ2@"aÉ@P¼@‚Ï@Ÿé@¾a‰@⎒Ar6A¯ÂA7ýÏAMÙIAZTõAV¹ÈA>‹„AL@ÔK—@Š/±@$³F?¸˜‘?ˆD?›Þ¹? D?Xtä>ÇC>‘N“>Àæ >ÌY©>íü%?: O?wÍ:?nèê?5&}>îÝ¢>v.æ>(¡Ç>ó\s?y?ƒ‰œ>ùË=: ´>}{;?%R„>ü• ½ö‹V¾ÿé<¾é’¾ç‹¬¿1]Z¿E£}¾Þ÷4½›¬£¾,z¾Ö¯}¿¿o¾·/Z¾tß^½ØY–>‡T?2Á?>‰´>Æ-=Ç`×>íä>¦è>ø?8;&?|_Í?uøÎ?·ª½š5„¾ëÍÏ¿ ^û¾§à¾>7×\?7 ƒ?aºÁ?¿>qý½5„…=ì¸>žÃE>­/è>Ôu?@q?ö»?“ñÀ?Jp4>†©(¾:-ø¾·Þl¾ZŸ=el >9@q>ÂÏ>ÔD=»¿³¼ÚÂd½³¯_¼ñL½í`Ѿñ:¿4ìG¿ \¨¾Ä¿/¾ü¿ Þ¿Š0¿/[G¾ì0¡¾Í^+½wZË?“}?Ÿ;Ô?ܦ @€ë@E‡¯@wC-@yz@ yz@³Y¬@ÌÝ@ï„ðA ÌúA2yA®AªAAZè@Ô7õ@›©Š@\Es@¿ì?Ñö?¤“ž?›™?ƒ‹š?Ž6>V%ö>"C£>*Ý<Û¯¿¼°s>…?0,>ÜW÷>dR¾b¾ËXß¿ Fg¾¡'=ìÉ÷=Ä û¿YŸ¿w-¿™®>ªOÖ?¹ç>Õ¨¾Æß…¾ý¤ ¾¹—u¾§‘¾­žî¾oÇž½þ,·¾V¯N¾Çr©¾È~¾KTÌi>6®>Éiz?8Ã?Eð9>ï"p>¾…=}>‡ÿ?i?wšs?¡÷Ñ?žg^?\ì >ÍÎ =ŽØÂ½²~…<(i>ßaÆ?v“J?”8?e©Ç>–·‹>‘äÄ? ±í??N>²l>¤˜?26š?…4®?u©K?v>€ô…½±¾Y¾ÍÕð¾÷Œº¾‰Œ¼¢24<ÆŸ½ðTŸ¾¡Àó¾à:5¾ÂxJ¾]¾£ñ¼¿å,¾ê©è½Ž'=´IcÄ¿Sz¾ÿýྩC\¾éÚã¿#‚¼„TÅ?>ý?ŸCû?Æ‹“@eð@;6*@kæÙ@…Õ@Ž™®@”˜p@ŸUS@´Gp@ËÚ2@ÖíÊ@Ñ—¹@ÃF+@®?@\ï@Zå@(µ@ jú?æ;¾?Ä…F?­^X?Œh)?8a*>í“W>Ï”n>püʾ6Nê¾èBÞ¾›ñ ¼¯2<顽Iž¾xx\¾Ò¤ò¾é|G¾ÆíR¿Ô9¿ˆû¿£ù,¿DŒ=R)>ºÉy;ô E¾çÛ¿M¾†9x½ BÞ=*Gp=HC“¼Px¾{]¿2¿*kÖ¾ßþt¾t‚<Â$‘=ÄzÒ>j9‰>ž.>@Ík;½ãQ<æè> I,?9ÿ?gQ?¦0ƒ?”î“?Dî >Ä\Š>$Qº= ?n¼¡Äß=Ýaþ>Ç]é?¼?†Ì>õEš?‰µ?) j>ø&ò>?ƒ>"Õÿ?<ß??»=ôt*¾…¯¾š.ú¾Ìx÷¿L<š¿‰<¿WξЃq¾„Tª¾âä ¿*¼¿7B2¿!³‘¿Mâ¿*ÛE¿,¬ö¾Âôø¼/D"¼ë7¾ºš¾ëº¾d‘7¾D[k¾ù—Ô¾õOÃ>ÍÝ?[ù?ë>?¤'?é#Û@)Fa@Wö-@vcP@€à@zñÀ@u@°@‚¹z@ŽÈ@Žë@ƒe@wø®@pt@Qòx@  ?ùO$?è+›?å+8?Õ:¢?¿ÖK?¦^%?†»?b¯È?N(G?2Ú¼QÖS¾Ñ¨G¾§x¼Ž"6>…>^Þ%>ƹã?Q–>¸Mø»J4F¾¾SH¿,ÍS¿Y ¿;ñ侺$½§ó¾9®V¿ ó}¿H†Û¿-o¾®Ö¼æ?x>Àò>t†¸>aèO½»Bç¿'èG¿ƒ]î¿^c;ì⾟'Ÿ¾ÛË7¾äï¾yÞù½M:˜=JR>mÁæ?Ë?{5?›ÜÈ?–™¼?Oqš>‰•‚½í&ɾ5Ô¯½ï9¾4À¾šj¾æï¾I`J½Î½û?«? å?2>VQ¨½]á>&A>÷€¨>"Õo¿+À_¿Šÿ¹¿VÄè¿1q¿‰¿î¿²™†¿Œ¾—¿po¾Õp¿ Øþ¿Fæ¿¿\Ov¾Ý1-¾-Û@¾¯—j¿C›¿½M¾'•¾¾cH¿&T¾äkH>`!?>ÈÙ?HÇ"?hë ?¼›@ ý@1…!@O…D@\‚¢@KP{@/×@*rY@2l(@)~‚@ðÞ@$º@;A@.¿Ÿ?ÿ(?ºž±?¹Ó/?ÈÅt?¾ô?«&s?›š?‰•«?zyÝ?nñ(?;Ú²>¤ =8š™>3¦Þ>ëÈÓ?ËA??U\l?‰^Ý?u ò>Û‘F¾&¤ƒ¾×uª¾qùC=÷®‰>^ó"¾6Ø¿*s¿„{G¿‹¼Ä¿gVÈ¿¶2¾šBB½ŽØ~>%(ž>§™3=òÕ"¿ gò¿Bl¿‰\¿c¾é­æ¿.,ø¿ALl¾Ò>Ü=@>š°S?«ý?PSo?ºt?šç¦?„8?ò½G%E¾Ûô&¾Ä…5¾%·"¾o¾šND¾Ðâ@¾ºo¾•/ó>ÐÚ?èV>ØÙ„=U%¨½Úœ>uÉž?&²¼¢²6¿~–¶¿­z¿q ì¿'Ùô¿oëZ¿’î¿I&“¾ŸLÕ¾”Á!¿¢M¿ü*¾ûä2¿)-Ò¿ƒk3¿“ŽD¿M'{¾„ˆs¼uX½¾/ÝŠ¾²[m¾™K¾ û¿"˜L¿Y ¾òÍB> ³µ>È„S>ªÿ?9p?™àÖ?×¢Ã@•9@±¥@3ïU@$p>?ûµ×?ÑÒÍ?ʪ­?²G’?¢V?×O‡@¿^@1\?Ï Û?ãá?IÙ?—ë?‚îˆ?Y-?F?¦?5FB?(©è?+dr?è+>ØM>Ã?5Ù?Y$?JŒ?&ÇY??oÝ?rñÍ?Y…6>²9þ8Ùm¾ {=ÎìÒ>íuÔ>¬À¾›lØ¿\¨è¿Švd¿Ü°¿‰ï£¿kpß¿%›{¾Ç!?½þÃ>>(ê{>*ùƾ·¡ž¿vÇ7¿y÷V¿¾Óg¾û¾g¿ø"¾m>W›š>Ëê¡>ì[£?v®?P-?fÉ}?S;?W±>.ó®½É(6½«&='oº=¦½N ¼ž×}=€óª<€«Ò>jÝ”? 7©>ÝòE<˜ZŸ¾0“]=ÈGX>”I[¾>R™¿x¨¿™ú\¿Q¿¿×/¿8yô¿@¡›¾ÀÜÆ½•cÒ¾FÅ£¾Ê”ø¾¬G±¾Ž%Á¿ ­¼¿nj¿jK\¾Ø<>Š[>϶(>¼!ˆ>ˆè‘>WL¾‚¢%¿ET$¿f]ø¾ë7)¼*j¯¼þ`½Sgá>Ç‚T?~€¿?™¤À?¡F?Û…@¸Y@ Á?Êy?ÎÊ?q»?1‹? ¿¨?lFk?Àâ”?Ìý™?šk%?f’??Xt??;•i? >Ëí˜>λ>·^õ>•„>ø >qµ>$r>^æ?$äí?]xy?6¤)>ïc¬>çž? š}>Æç‚¼Ãž_¾¸¨¾ŸNA< )/>[о.÷l¿…]¿?¡„¿4"¦¿c¦N¿œAT¿œÞ¿Wš¥¿Lø¾­S½Þ¦=UãÛ¾pV*¿Cm‡¿[·³¾Ù­½­¸¾ à=¾ƒwñ½ª‡·=útº=ó‹+=räP>>‚Að>°>Ýl—?ò.?Šå? Ý@>øéq>œu‚=Ÿœ=Hº;>’,#>÷Ñ‹>ÇQÌ=˜ö>çÅÙ>™Âc¾%پጾ¬ÑؾLiϾ۫q¿[Û¬¿€ó}¿Wß÷¿@ù¿N«¿+Õ¾¾®‰¾\`ö¾ËiQ¿‘m¾Ô‚¾Æ„b¿Ï¿4éþ¾ãçÆ=#ç+>Ñ—Ù? ˜¾?Òr>ú¢Û>Umž§®S¿Lõ&¿Eך¾©0€½¯ì¾‰cK¾¦v>µì?&‘°?+£`?xò?…Ñí?ÖÑð?Üè[?›?F?‡?‹Œ>Ï&>ƒj>à`?N;$?vk„?aÅ{?D€ç?Ä(>¥â>.Gî>Š+Ž>½l>‹M >E=H)g½°Bñ¾WY~¼x#‚>ñí?H)6?2`L?¥í?܇>ýÙð>n=˾ôí¾¤ió¾Lä½/ìL¾Aü¿!?¿7cK¾Ô1¾h¿3ÜÁ¿³¯t¿¸<¿O©×¾¬Q©¾¢0m¾Žó¹½’¶õ¾% ¿#Ì¿dŸb¿©½äƒm<êº#½[N6½äR¼¾&`¾|–N¾5¾hx½ð‚ɾ)b½ð–,>Ó? U2?U.[?N‚m>ß?¾= »nbã>šß? 8 >û¯½«> T¾*æ¿C©¿L²v¿Ew'¿#aÉ¿Íó¿?dñ¿eeð¿{2°¿‚qR¿rK™¿1è¾ßžÛ¾Ùÿ[¿š[¿4¤ ¿1šñ¿5¶¿-Œ ¾Ç/ =‚ªù>°‹ƒ>°º>†Šô>pkª>ã1½ý$é¿÷ ¿E¿¿™s¾7Ô5½Pÿˆ¾¡ÜO¾òeý¾:Î->8•„>KµL>+e)?vR?‚…˜?ƒDå?-b>‹‡>” ¡>ÎÍ<>ðçŽ?>õ?-Ž™?MCi?e“à?O…û>ׇJ<.~¢5£>ÎÜ>0<$»Ý˽ TT¾8˾°œU½õãÜ>é?Z"l?\"?Lú?Xj8??¾Ú>Á¼"< Èè½YMî>½O>¤<ƒÔ£¾Âš‚¾Þ쎽®Yô¼Õš¿B9´¿Î|Ä¿Åq±¿.j¾òª¾Šùƒ¾Ý£¾_ ?¾vž¿:׿vüŠ¿M§E¾•„=ÛOôØK?°Ž>¹ïÁ=V9A=,’Q>t¡+>ª  >¬ /½”ê–¾—4¿#{Ê¿\箿eï:¿^@d¿I˜Í¿-÷Ú¿*€~¿J3Ê¿kÂm¿g&¿3TÔ¾ØBľ{Ú¾‰cá¾Üu¿!s¿R5¿^(7¿<;¹&÷>ÝÿË>Ö–‰>(m<<[Ür¼o\½ÛR¿¾ w¿wþ¿|¾Û²á½ë ä=-(¿¾ r¾Â‡Ë¾«{½ªÒF=’”ý>߯>³ÅO? Õ©>Û˜Q=QÏ™¾ ñb>{ò?+d§?4'? ‘a?—9?•÷A?ž“ê?‚ o>Äÿнô6æ½µ6>ƒÔ=ü_›¾Š°é¾«Iõ½éñÛ½Ô¯h¾u´—¼‚ç? _?_ëž?TCá?@}Á?BÚA?ÂÉ>V«é½•„»©QÉ>œN•? qa>êÓÄ=ëQå½â€U½·‡n¾”Ù¿†?õ¿ß‹†¿Ã.¿&E¹¾]Y¿F·¿Nù€¿tj¾¡”>¿X¿rgr¿h‰ç¾öEÀ½ñ÷ª½X¤K¾!Ò¾XW½ñ(<’„;‡é8¾^[X¿mˆ¿6èÚ¿5×l¾íÝ™½Þ€=³0=4h¥;”‚$=÷U4>?OÞ=©¯p=ñàÓ¼âU.¾ÔxS¿=Oµ¿7ÆÄ¿#¿'ó¿6’û¿5pë¿+©R¿*œ¿ p¾Û.‡¾h¹6½ÅËá¼Ð‹¼æ[¾ T¾ã€¿A¦Á¿8Ö/¾k;@>¬ì>ü`Ô>…)Ø=¯×@>#>Vö»=­F½u\½÷H£¾LHº¾‚fu½º¹>Fþ%>tÖô;€Ù¾ŸÞ=I>šù>ÏÎ>ÞgŠ>è÷>ª­=..J½Ô`>ƒtÞ?‡ÝP?äS,?ûë…?ß>:?ʈÞ?ÅKö?™zU>ý¿ <ïŽN=ÑX¡> Á ¾¯bÖ¿T˜¿*!¾$„¼Çt¾$<Û<í¤>Ü­@? é>­ª>‰<ßA¾>8¾ô󱿃¾¼»^;Íþd>½ä>ü׺>r”¾Gr¿ Œ¿Qº¿§°¿Ô¿°¡[¿A9Ü¿;•¿îz¿¥´ê¿„]ü¿-=¿0[_¿fk›¿j&¿/ÐÛ¾ß8E¾ðʾ/Ëh½2Û\=Hc-¼úŸ¾•]W¿°ˆ¿ hœ¾æï¢¾Ð6U¾ÊÈɾ¿_ë¾Õ#Á¾ø¥Î¾º¦o½±U©¼"Uf¾#Kf¼Üˆ!»Ã]g¾¤÷º¾ô&]¾¤‹¨¾>Òû¾¥ÍÜ¿¸Ð¿8Šš¿72¿ £¾‘êν’@¦½ì*ò¾˜S£¾ª ¾b›Ú¾ˆ g¿ο@w¾èÏÎ>“Í>âÆ0>Xéê½Õ=¯0¹>èÔñ?´w>‹Xù>OÒ·>¦æÂ>€\`¼1Ì­? L~>ÞÞï>_ÞÕ>Ÿ0T>û¡>ó™ï>Ò?ãB?Or>éx>£Ð? ¾ÚN³>ý*ñ>~’f¾óĺ¿jè2¿½O½I¾’Ú’¾)þR>ÈÇ=ºV¾=À¿ Ðå¿3†p¿eKÇ¿„¬T¿nvƒ¿'ße¾¶>ß½sz =Àö¯½ýcõ¿Ñ ¿uÚ‰¿‹žá¿™ïÁ¿£ €¿Œã¿RaÏ¿^j¿™žö¿­Áw¿”ð=¿j¦¿Yƒ>¿e/¿lfпgƒ¿I"¿†E¾$‘'=ä²-=ð:®¾QÓ¿#Z”¿Fɾòš½?î> d=d½î¾GÔ…¿´³¿wÇ…¿p¤Ý¿¾±[Ô¾¸‹ø½ŒÀã½þ®{¾}䩾š ݾGo)¾%Ö¾g¢¾×y`¿Åû¿(r¾ð.L¾v½¹V.¾ùm:¿n¸h¿w"Æ¿;ŒŸ¿7J—¿lM¦¿T=l¾SW{>ÎM>´õd¾-#±¾±=íÝ”? ®+>¶{Ô½€sÀãÌs? àÃ>{ÞÚ>EýŠ? ‰É?D“!?wþ>µ¥}>É `>ОÛ>X“A>úœ>çRÁ?PU?NDÔ?öí?Mbå?² ÿ?íÞ?êæ?Æ{Ù?µ+?¬™?ŠÙ7?KÁž?U@?uúï?z½É¶—¾¹·\¼‡ÍX>7T›¾B ¿O¾ªð¼‡<7¾ÿxi¿7ß*¿As}¿Vå¿WçJ¿!©¾·× ¾IêØ½ãE¡¾B•¾í³¿^d¿~øŸ¿T­t¿4©N¿<烿AüO¿:o“¿Bf¿LyÝ¿;Æ`¿*u¿9•ò¿R%O¿Wh¿`L5¿t%Ú¿\Ò¾ï¬Û<­éå>‘{ >B¾+žŠ¿¸¿ žù¾vl=Õzº>‚þ0>€Ëï=mìD¾ó)࿎‡!¿¤JF¿h,Ò¿ c¾áܶ½³Æ ¾¢®Š¾¯ƒ-¾æŠz¾ÿrƾá5š¾žÌ¾|¹…¾»3V¿•-¾ÇH|¾u¾ŒVy¿V"ö¿žÞ’¿Šv¸¿AxJ¿M ç¿4i¿:ON=PÔ•? ›§>j_ྶY ¾¬Ÿ^>|õø>ó2}½Œ ¿S¾³7:>·íƒ?í½>¶®>„H)>ý· ? ©>íä>“®>Õ4r>ÀCç=¤MóÒ G?U ?aÉŽ?QhÃ?ˆ‰¹?Á§?ØÜÃ?Ç%ú?¹J¡?Àr?¶?ˆS??Pë/?xGu?‘sá?TH>­ã+>“ä@?ÄÒ>Œé¤¾Ò­ ¿6a¾ „=¥kѽ°á¾èhR¿©¾½¹½¾˜Éµ¾lðû¼ÎÝ>c•G>ºõP>«Ë²= Û-¾ýz<¿\µ-¿>Ôw¾ÉRZ¾Žv»¾áÀ¿ˆc¿¤¾òê­¾c‹|=Qµ=7™§¾W¿(¿!3 ¿&x\¿6Õ¿©3½ò¾r>>¤ù³>åX<'°…ïó? ˆ"=Î ¾²¾\>ã·D>ïŸl¾D z¿!·_¾bpê>ïôž?¾>A¡4=!G>cša>‹{">ä>t?#R?:ã1>ó±²>¹,ÿ?ý‘?Qƃ?XÃ?z›8?±EÑ?Ú¡?Ò[ ?¼9¤?Ìk*?ìÌ?ÚM5?’5 ?=mp?LÁg?egX?!Æ>§FŒ>ÌŸ?-o=Žšõ¿"¨I¿B’å¾d‡ >kཷŒ™¾øåØ¿…‚¾Š€0½Ð+î½6'=•(>Ц?(S?.¸*>|J±¿Sm¿r›v¿:×z¾»@ò¾Ãü¿£+¿+n¦¿ È*¾À<нܨ°>OU >l½˜t¾£ôÖ¾žàm¾‘¬Z¾¨k¸¾7‹/>*}‘>Œùϼê¾½´¿§>¶¸a?òW=©ž]¿,tG¿Œd¿R5w¾«ƒU¾ÃLr¿aš¤¿†{•¿)dU¾¯Óv¾ÛÐÓ¾Ö+¾–_L¾¤qY¿od¿%pä¿Ù¶¾`­Ö½Æª¾¬R£¿M¾Ú6U¾\ ¾Àsd¿)ݾò/“=á{>²¦¡»Ž¹ž¾¦þϽºë^>¶2>µïx½Ëª1¾·õ4¼ºÓ¼>ÿ®? ¨>Ÿ=3¯:? ,?]UÓ>ÿ`¾:Ez¾ÄK¹½Ú»[=•²ï=§(˜>°Ñ?]t?“?ƒiÔ?X²é?P ª?L¶X?Rô?Œ¼e?Þ?Ò[d?°?ê?œ›€?ÂR?î×”?Ø‘–?ˆä´?øó?wÿ? ¢ù>p‡0½t’ļΠ=u쾃”¿9j‰¿EO¼>óº|=uᢿR0=¿¦7O¿‰2¿EÞA¿^Xq¿|]“¿?¶C¾ë'~¾æO>¾þKW¾¹š¾~»Å¾“½¡¾w@Ÿ½ Å=WN½-ß¼2×Z> õ\½ -Q¿¿hœL¿ Æ2>/§>ì›ý¼ ?þ¿_lh¿§Ö¿~Ÿ-¾òXñ¾ôU?¿^wA¿iF.¿†¾”ÍQ¿êÞ¿~¾]üµ¾*"ü¾1ûh¾4+½ˆàE=bžû½°.F¾ì¿•2¾¹°â¾!¿¾Ë <¿2^{¿û*½ R=–„^¾O˜ò¾‘ ==)i¶>žTÿ=û½G¾—m ¾ÞSü½éݳ>±÷·?Z?Y»?QèD?ŸÕZ? ²€>ôð”¾ë„H¿1‡¾jœ=w Ð=Ÿ‘D>RFL?$…?ƒ÷?‰ÏG?i2?@ ì?.'D?H&£?ˆqÂ?¢xô?Žzb?CRÑ?,Òé?ué±?›¾Ÿ?‰ðZ?0…>í‘>ú4n>ß’x=ÃC¾’¨N¾Â?¡¾â¾«hÑ¿œ¿-#‚¿€¿Û%¿é ¿?€3¿UÏ¿P¿;-‹¿9£~¿REí¿@!D¾§>®<ˆ~¦¾ª!w¿“ 0¿Æ± ¿¦»+¿‡Ò§¿“<þ¿‡4¿x¾;U¾Ë!E¿Ua¿n¶¿Fr³¿¾.Œ=Öc>r•ë=®&¼-¼¼ ð}¾Ž›,¿Y®ƒ¿–£ú¿`'°¾‚Ãã=™ª[¾l¿?û¿Je¾÷¾_­¾‰åá¿Ì׿ ¾Hñ‡¾KJ¿£¿7ø¾£ò¾bƵ½~$Ï= ü.>De¼>=¼_½½Í„¾öDÿ Ë0¾n«½¼A&¾Ù)'¿Kr¿>Ã'¿ ¾þ[¿¿ ?¦¾“$k>Éò>’4Ü;;¾¾«“ؾÍ|ؾš>~°†?-³?W>?‘Ê%?½G”?³-$?Õf¾»º¿$”V¾X¡¦>¤ÔÞö©?/²å?&¿’? ¯—?‡ ?P÷Ñ?{Nƒ?Zux?Nà>ƒ¦>ç?>Æè¢>µ½¨>hN¢>B¹3>¤¼k?c?u)>¥”½\†š¾}=]¾”ϼÕÁ°¾7~¿¿ sR¿TŒ½¿OKÿ7—¾ñÑ'¿ 4~¿(ÒÔ¿ç‘¿ùc¿ ”!¿S=¾²ÿÚ¾)‹\¿›½¿”¬Ž¿´ç¿‘Æ¿tÏ’¿‚³-¿Gé¾'í>TˆÆ¾Eו¿F†Ê¿pTF¿JƒÅ¿¯—¾§É =?ü0>hy}> ½=%e<ª~þ&í¿ Uw¿JŸ ¿9¥ˆ¾ý1V¾ Ù+¾€§Ï¾t%¸½øÿ&>’ >«y‹>tÂ=êI>§>ÛM =˜»:¿š€¿"„¸¿Òê¾äÌB¾‚zµ½èƳ½<û ½–’:¾—ô¿T¿oK¾»ÂS¾{ä ¾ñé¿6ï=¿2,°¿&ƒ¿'G¿ Kù¾G}>XP>'o\¾ƒÿ¾š¨&¾v™ó;ë8Z>ÍÊÀ?F@—?oðô?‚â)?Ÿµ?ª–²?YwU=Ù>¾¬)m½höš>Ušà½8‘,¾ë7¦¾× x=­ç8?j?-•?$FT?E¿·?„¡?‡èÛ?3$&>¢Lü>•³>ß,>¢½ZöÖ¾ƒ•z½ñ´>'©Ñ>Òüa?rà?ºŽ>Å®ñ>QyÛ>Ž2,>Ð+E>HÈ–¾®…å¿>¯|¿1œÌ¾ÃK·¾p×¹¾Ä«4¿ E¯¾ÛѾ-]½Q½„¬ ½‘R½¸1¥¾ù½“¿‚|¿–iÈ¿z r¿d>¿m_¿jS¼·‰Â>JÖ¾H¡ý¿U©¿Ú> i=î‹G<à ѽ»ª)¾%¤B¾‹¿Ž¾îO!¿ þÉY¾$O=d‡>z»Ü>âƒ"?áˆ?SÌv?ŽË#?‡œ}>°õß¿×v¿C‰¿ U»¿ ó¾Ê$¾Ûæê¿T¿%ÖL¿F-¿{Ú¼¿ˆÉÀ¿f…8¿-ëÓ¿[2¿ N‡¾äA¾Þ¿v‹¾ÙQ¾Hj;¾|¾¼›¿ TȾã ^¾-mº>1??wl!?u6î?GT°?a²Õ?“Ú¹?€,>´ǾH6½Û€>5 ž<08¾¤w¾z÷Ë>©Hy?k"è?ŒP?~ŠÚ?‚±s?œ4h?™ÌH?O>ò„7?Ðr?B\5? `h<ŸÓ޾‡Õ¾IWÕ½Eï =ÚÉU>Áwå?–½?‰–?M+? ®n?ÄU>²ÿн¢zN¾“ ¾Eœ’¾W?¾¯^\¿)ûn¿H‹¿.ˆ¾‘ˆ½v'%<ç-<=ݾ4»–¿ é;¿e¿ªbX¿¦)Š¿£ë¿›Pú¿U|˜¾¬Tô¾|‘“¿\ž¿:mǾÿ+¾˜%ð¾Ú¿P¿Éc¾cêT=K=Å!½}‰¾Âw½M´b>$Ç-=Åj꾕¾i¿rm¾îÔ!¾€…¾<÷ä¾xµ=÷çB?©7?çz?°4 ?še¢>®÷¯¿;”¿hn¾Ç_]¾“óU¾MÆÅ¾î(R¿e)¨¿‘‚9¿œ=˜¿¯8¿ÀŸ¿°7 ¿ U¿)ä¾÷D2¾ä*¿x2¿é"¿X¿0V©¿nn¿—ÐU¿‚¿5.…¾‚ŒI>ûX?t5?^"?Jº2?4?íu?ZÅ@?U Ü>Œ‰¾Ce]¾`–<&Ân=¯b§<ÓNe>.¾¦?$¦?‘P?Ÿ°¶?€£ž?Tå+?rÏ>?„Ù?Iïp>é÷1>àJ'?cÓ>ᆡ=h«(¾\_‰¾—D,¾¢ˆ7¾GùU>1?›Æ? Æk>å!Ñ>ê8˜>æ>…e>±>ªL>òQw=êÊ¿(n¿ƒy¿„¢&¿[_¥¿5¼¯¿C¾Ï¡ö¾«P¿pš¿wM¿ºͿߋԿé5 ¿áPÒ¿Á=1¿„wÓ¿ oÁ¿$Ýç¿qq÷¿Ør¿:U¾õA¨¿ ø¿3ØÓ¿$޾²Ö½ª^<½Îv|¾Ë ,¿ 뾩¢ >1uÎ>¦É:Q6¾–K¾ƒCC¾-[~¾†{ñ¾Ÿ«P½°?‡>¦®4?CmÆ?ˆ=–?g×>!(ó¿#Cæ¿W[¾Ué= ×f>m´¾tñO¿XN¿•=¿—¿öµ¿®O‰¿‡º¿L%¼¾ëc¸¾ùÑ¿5©‰¿YG…¿_"K¿qP6¿˜Xٿ¦‘¿Ð ?¿¥Õ%¿93¾‹mñ¼e÷Ô>_}>ÉJ7>®¤ó>:n>f†í>Û >ÒÒ•=XDJ¾«÷§¾âÕ¾Œ"޼¨¿>"“—>„‰Ü>ÐÛ^? Þa?-ù‰>ÐGÚ=ý«Õ>c)»?R>ù|ø>,½îF_½›çã½+µì¾J¾ØKп‚1¿*¾½>dèo?©?>Ã(ª>S”B>hP>[Ö+=²©á>t™Q?Bõ?vl|>°Å¿ ¿u·¿Oäž¿8ÓF¿aù¿v±ú¿OhZ¿(¶N¿G(i¿”žî¿Ì>—¿îD˜¿îØ¢¿Ñü6¿ŸÄÒ¿K̹¿‹€¿=¿f¿‡Ò¿jÓƒ¿0žÊ¿1¨¿ Ÿ°¾é>ì¾’ ¾*ľ6Æ¿¦2¿B‡w¾ËàÑ>s'l? O>Ã^<>;û7>C( >:1«=VÆã=$ì&>vá>íwœ?!ÏI?G5Ð?05>.—¾Òëc¿´a=C€=ÇtÅ>Bó½·ë ¿þ6¿E‡¿º5¿cø¿3GÑ¿‚y¾]‘’½Y'ð¾Èô¯¿VQ¿p°¯¿P‘t¿Y¼~¿‘ :¿°u¿¥Ë‹¿`´¨¾ÊŸÓ½üæž½¹‘¿¾y¾Pœû¾€(,¾tv?¾Âô;ø¸2='ª½q%¸¾ŒIÑ¿#¿k¾¤.j<&6<Ö“+¾:`‡¾hò¥¾ }÷¾ƒ—¿yÖ¾ÑQJ6žv¾¸7¾ì|-¾ê>¾³¾íœð¿@bK¿o$B¿C¯¿¾U';>à ?.*>T=–¹ˆ'Ÿ=À¤Ó=ö¶±¼žÆ>þ?Źœ¾Ë m¿C¾•%ù¾“©3¿&ˆQ¿e ì¿Nئ¿)Ÿµ¿=Ÿæ¿†¦¿²^g¿Ä„¿¬"#¿q¹t¿º‡¾®iX¾u#¾—î¿dl¿<ÚÏ¿I C¿!¸]¾Ì7Ù¾b ¾&T¾Iï¾…º|¾³ ¾æ˜·¾Í2»½MB<>Ø>‘?(¢¸?™?v ?=4>Ì7X>¦r²?R ?Ll?dÖ{?Tª‹?[v#?Wv¬>ú»ð;k¾Hß<ਾ@4÷¾c ¾S=—¾ÐÝc¾§”7»­@=]§å¾=¤0¾K¢=Øíu>4¾—8w¿4$8¿ Ph¾À9¾Ý‚†¿8ˆ"¿V´$¿(æ_¾¿Î5¾'Ha½ª„M¾"bܾÁ¿¾"¿/°,¿iü¾Â:ß¾€Bä½ÔæH=o;¼½?;ê^¿M¿§7½Ø¯½\"†¾Ýg¿ÿŸ¾Ú+ê¾±³[¿Ǻ¿r¿¾)éû=IĹ½É|A¾é‡¾9™S¾0ã#¾ü}û¿o%¿Í¿TøÝ¾x'0>t‘>ŠÜ½9x¾&2Ï=0ߦ>e§½O×4½ÍD>·Û@? >Fʾ¸¾´AŸ½ŠòH½¡‘\¾Ì7¿ I¿ð{¿ ¿ yã¿\å¿”]¿ã¿fsr¾Ø¶@¾Fúï¾n¾T;1+¡¾;>ú¾­ê¾áR˜¾©u3½°`D=²§¡=&A¾H°Ä¾Êåj¾»Bݽù÷Y>*ã?>¼ÒY>æ‚>ì%>üS€?.¨>×ü7>'ŽŒ=Ìý$>î,Š?o¡Ô?ˆŸ|?u”Ë?y?‚ot?Aÿæ>•ˆ†;#y¾[Å¿®Ò¿ N‰¾å“˜¾ÙV5¾)©Ë>b;Á>YáS¾#$a¾(V½Ä:¼fד¾Æ=é¿󽾩 l½ËšW¾šJ˜¿KÛ¿„‹¾Ìé¾¥O¾»ˆ¾´\뾪ƒ¾òø ¿4ëä¿Fg¿h¾â&Ù¾ª†Ç¾ šù>g(=öÕ¾Á´Ç¿Q˜»¿É,<ßë¾>`ð?½ô’ú¾¨ÓÁ¾S<ßY\½ðÇn¾‚Dé¾3>뽬/½j§8=ÂÇÁ>ŽÜ–=Êÿ_¾ïHß¿tj¿„ïx¿RôÏ¿®¾‹lR¾vྻ^b¾±?¤½Ïìü=0.U½š¾ô<ßQæ=÷cY¾2Ǿڶ{¾ÛŸb¾‡‹$¾r!’¾µÁ»¾÷u¿­¤¿%1²¿,Ö¿R‘¿½'¿¡\¿uå¿g5¾ÇkÄ¿Ãä¾òݾT_ǽRO}½æËz¾ðì9Ь>bÞY>¨ä>W â½ï¢7¾ÍG*¾ ~µ>‘Ö? äb? _>|ü =Ÿ[é>NÇò>®4ø>J0¾°Ž¾Õ¡¼{¼?Í?8þ?.hI?=Ã?R^¹?%˜·> 3~=Ð[п _k¿]ì¿>N=¿´¿E羊ì>÷ú>‚M¾`j•¾¨!¾¿ý¾UV»¿ Þ[¿c†¾’`x¾Bém¿$}¿^¨Ÿ¿=N+¿ž²¿`ª¿543¿% £¾òƒC¾è#÷¿b¾¿H„¾ñ.C¾ÅßS¾¹ü–¾‚hç½a™Â½NÐê¾Ákó¿"Ro¾´þÔ>yÍ?ÉÁ>‘C=•´G>ª>~d/> y½nQ;¾s!޾º“U¾‹£©=Ëÿ>«öê:ņ4¿" ¿s˜¿`ßé¿Sð‘¿fß&¿Xß¿#ª¿k`¾ö¡A¾¹À5¾>{—½Ï®•½r×'=,©÷=°M«½~„‹¾”¤á¾ÖeV¾ÛÙ{¾Ä¾´½¾íYç¿7Aà¿]=Ý¿Lwƒ¿RX¿‘H⿱%¿˜¿Núä¿.çý¿.|K¾ÜÍ•½tX=%Šv½¨cJ½s'»>:h°>Ö^ ?÷>ôT†>e@ ½¹ù½²°ø>¨ß?0+5? ä#=ç‰Ó½­;¬=µÑ¬>iˆ8¼Wú¾é9ä¿m„¾™ùã=ÌåÔ>ƒÄ¸>‰$>´ÅÍ>׆Y>œŠ¨>,N.>Jë¿99¦¿c Ç¿9üb¿)|ž¿.s±¾ÈZ=¤i<>Z£=QqW=uçv>'ùƒ½µF¿´×¿)c¾Löø¾R´°¿$I‚¿d Z¿6ñ¿ À½¿ íý¿6ûT¿B¾Êß¾µ)ھljµ¾Ísü¾Ð/o¾áYù¾ôw¢¾øº¢¾î¼í¾èl¾é–1¾Â|õ½ï‹ú>j¨f>ÉeP>•+\=ìN=&ýˆ=aüÃ=“UJ¼%ª0¾‹~ ¿–¿k.½Õæ:= ]¾¤=¿Yzˆ¿o Ò¿JÙº¿oª¿šâF¿ˆ’[¿³¾›L¾Ü/¶¿±º¾³×„½oŒ>ž>…¶Ø>™<&>\6"<Õ°˜¾7,{¾›4¾š£U¾“È­¾õ°¿THj¿xð&¿Me¿:Ñ࿱¿ ¯ñ¿Œõ¿Gˆœ¿%®?¾øý¢½k’>‘>µ¾5ýÀ¾*>ÛÞ>Ò”?ùd?/•ã?!™ˆ>¾bF>åÞ>ñV?2ug?e>e³g=ÅÙ.>1¿„>—-½•³Ò¾ˆ|D¾i"¼×Ф=ùK[>e(>;sð>’Œç>šU>ëÙ=³Û3>®Q¿*)*¿J)9¿1a¡¿9 ä¿9Ã5¾¦¸%>i G>×M>ºBó>èf >ÿϽ=ý$¾§¾ß¾„sY=Û [=À¾’±s¾÷†²¾Ó5p¾ÛÅ´¿ šÚ¾ôyš¾/—¾hÐÁ¾«{_¾Ø³U¿˜ü¿.ÉB¿Eo ¿5æ¿1PŠ¿Sï`¿\ú¿ˆ“¾‡™½ÛþÃ½Ùæ1½­lï½2—œ½Ñ³¾i„ؾlh1½dj‹=\ÿd¾ ÷"¾Û®S¾ÒW½á ½@á~¾ÙÊð¿H©¿I4]¿O<¿•þο³Ó¿vj¾U¶=eÇä¾l;ç¾ß×¾tŠ›=£Ô'>vó™>B–>ˆ©«>L`ª=™VB½~6P¾6Ki¾ˆ#g¾Åy¿Žå¿`:²¿cµ¿.Øä¿$}š¿`¨M¿~«'¿FÅE¿ ÷›¿ð;µð>>Y >ÝFc>w¾£Â¾§¼•0>] ]>±ûI? ®è?-¯]?þÔ>× Q>ëaÎ?>Ù>Ÿg…>6q>'´¾¸¾’x)½lú>‹?>Ú°>¶†¾>”Æé>Å<=? Êf? ˆ>§E>.>†¨i¾þ +¿14ü¿;ú<¿OX«¿%„C½ƒ>ã³¢>ÓU^>4Iø>]Þ<>•VM<¨£O¾‚\r½‚þÔ>’ä>ݽõ¾B:¤¾Ÿ@¿˜(¿9^ü¾çgO¾•í¾e5µ¾ñ7¿«õ¿V¯ù¿– ¿ž±Â¿kuY¿8Ö«¿qÉ£¿"±¿O^Á¾¼¾£zª¿ K¿k0¾°ˆ…¾lsX¾•a'¾¦\¼‚H`>š<@¯Ç=“B½šhÓ¾:hh¾˜RU¾·!¸¾¢“K¾¦ØJ¿lL¿HË¿oê¿¿byÀ¿-³I¿ —ª¿*(¿jSô¿kò’¿0‡¿S¿H¼¿/¾¾)bß>R)w;À²H¾².Þ¾¢d ½=îÄ=q÷Ï= ˜d=ó>†ð;>é>y4>]»=”Þ½¶=a{˜> 9¾Øþ¿¸Ð¿'Ž`¾C–>¹I>â>‡Ó>ix¢>Ù‚ú?*bÿ?9ru?ÿá>UYH>|w¾¶Óš¿“¿:þS¿Nõ’¾æ L>ˆÜr?"Ã>¥;¾ÕÝF¾ðÜö¾Š¯‚¾‘°A¾©IZ¾…_=(.é½ÜV÷¾Ÿ’¾Â[¿ò}¿‡ö¤¿“ à¿+rB¾zâ×¾ \ð¿cÆ¿ y¿hš"¿²A¢¿¹ÍË¿i±·¿ OÈ¿QÌ¿”y)¿p˜¬¿‡¿0»¿C¡¿9¸—¾Çö¾$_€½õV›¼àj/>8=à\Û½Òù ¾Y挽Ç=€¼R<ʾ²‘¾ˆ¹¨¾5Þ;G~ç¿9¥¼¿¹‹¿Ê´™¿€C ¾×󮾘Oÿ¾‡Å'<¬’6>zçS=e¾Ÿø¿~Ô¿)?:¿[õí¿eð?¿,’n¿ ÏØ¿T^¶¿š Z¿™½¿NñQ¾Ú»²¾ÉÜ¿/÷¿|kÏ¿‚n ˜¿…|æ¿¥Ÿ×¿™@/¿D"½©uE¼ã%¾3/½”£¯>G>Öë½¥ˆ¾„¤Ë¾ƒË;½Òy¶=$~·½™^'¾åIë¿(+)¾ØÃK¾ 'I¾Ã2˜¿V1¿Uº¾¨ßI<áEþ½zí¾h”o¾ˆ®>Þ„>˨Ÿ?®‰>Ó Ð=Ýp“½½£'¾3ìv¾Ÿ4¿ û¿'M޾[f‰?Tª?$ÿ̽\b¤¿EC¿PÔ7¾ûh辨©‚¾—zú¾mDª¾Ÿ‰¿P)¿'¦P¿Þ…¿\ØX¿¨ö¿¦ƒ¿/ÿ¨¾O +¾PæX¾‚„ ¾AûX¾ïU¿ˆ¾z¿šÕ¿,5?¾™Ö¢¿ó{¿šý¿W&¾ð#ƾõ±K¿.Ž@¿ _¾²~½èy=†áÞ>VN‘>~*R½¼d¾ÜÄ¿ ¥¾Ï? ¾·2z¾üIL¾ð;'*½f¾õ d¿“a_¿±‰ð¿’Ø›¿mU¼¿W{K¿ Ù¢½¯fñ9î=œ¾Ë°•¿:hW¿7⼿AÕ&¿„Ž¿¯æ¿HV[¿&6¿l—5¿ª'¤¿˜Âv¿$.¼¾†ñß¾£Š7¿£¿\ &¿‚—·¿•žÎ¿¯é©¿ÀìI¿¦ 7¿7•R¾0z=¯‰q=ž±“> ³K>•>•Ÿ;€µ¾ÊDR¿T^¾©Wv¼´B”¾#п3e¿«¿5ã÷¾–Œš¾µ]¿%~¿þ; k¾¡el¿tV¿#ëq¾·Y²½¤ÞÛ<ù Î=°H=qš[¾"Oݾ¶Ö™>ææ=%Ô辂À9¾Ò‹H½£?®?í}½)Ò¿,c¿ Õоdù½ÄTÛ½ôY¾n¯¾Œ£¾á„ؾڄA¾Æeˆ¿03¯¿Œ>¿v ¾«‘=’O¿=“|[>Ã>±KD=½ÀT¿ás¿TÓ·¾öíp¾‚Ø>¿ p¿Fr5¿þ/¾]¾ŸO4¿Å´¾Î­—¾Lé¼r ï=Ÿ>†—Ô>ŽâG½ñf¿'v–¿^ýQ¿@V¿#¯”¿%ÜÌ¿Ь¾V€Õ»œf+¾að¿*P¿s´Å¿† b¿`Ô¿‹ïâ¿Cų¾áAn¿‚¿zWR¿ë¿$*»¿ þþ¿cŽÊ¿ƒS¿8G¿¡}¿g°¿œß­¿€%w¿ `¾½ðѾófÑ¿ B*¿g›¿Pô§¿‘zÜ¿£^¿“£º¿^¯¾ýWE¾*H=  <Ÿ;ê[Ù> Q>q8 ½’”î¾µ¾<¿1D$¿´õ¾=òºæ+©½áþÓ¾m,¾ê8¾ç„~¿ áL>Ï×þ>HÑ ½®¥T¾CQ= ã>ãÉ^>ÑE¼©ãó¾¡ïe¾faø½r޽’`½Ñ²K»îÅ=ÊÀº>+y>*S÷;–q…¾ßí~¿Câ@¿ñ{<¬!Ž>u=B>Ò3>ný8>ÑëN>h¾úN¿HvŒ¿[þó`æ¿Âù¿ Y¾&Ë»¬öྕ€¿ ;¾ß7”¾L¸Ä¾ ʽòú†=‹ù>8cÙ½»5ð¿pµ¿z„¿qáK¿+ì¾É­ƒ¾^A¬½Þú½öD¾E‡¾¡¿sê¿>Qf¿v7>¿ƒw#¿g†!¿^¡¿[¢¿¨°E¿„%¿ct¾ãü­¿J=ÿn—Á¿97¿2/Ÿ¿‚{ñ¿“–´¿X¡è¿$„¿<°5¿Y9 ¿}`¾µ›¿ b,¿`†ß¿R¦U¾ú¾ž¥à¾°ÿ6¾° …¾Œ–a¾”WÀ¾¦µé¾:Bê<‹;+½ÝÞ¿ €4¿@l¾¹å>4=G>X ê¾C-ʾÎñý¾n“Û½½о2ƾü«=ìñ>2K¾*òø¾Ûñò¾^óÒ>=>#7ü¾Ü¾Ï›J¿ËÄ¿( ð¿ ¥0>†t‹½x¾(©Ê½:I>Bé¶>˜©z> ”i¾.¸¾®@¼¾®˜Œ¾ºçþÚã+¾¢˜<Ø1>¸‰2?MŠ>Àäx½™b¿w]¿*T¾ž~6=Ë?i=æëþ½Ž1œ½Uk‹¼ Š5¾®ßK¿\ô˜¿„§Ë¿W˜E¿.Yû¿¸â¾eþ=—X ¼m}ÿ¿ÎØ¿V&¿-"a¾ÇȾªMh¾ÀÔñ¾?¼ÑøË½&Ö¾âû¿r¨Á¿Šo¯¿*Îü½‡ ä>UŠ@=a§Í¾Qõ¾y<¾z—¾»ùC¿ Æ¿Eð¿x©º¿࿚+1¿¤Lñ¿—úå¿Xl¿¿ «ñ¿éí¿P†¯¿T'¿93!¿bDÖ¿——Ó¿$¿@§¿3†ê¿…~¶¿‹ÙÄ¿b¾2#b¾¥ì¿ 3L¾–›Ö=Ö†½<ÿ}Ò¾Í$9¿G¥¾þ«Ñ¾Ç¶¾¾•¾WŒ®<íÉ)½Rξϑl¿` ¾`B>“d>»õ;>hoà>DÕj>UâÃ<„‡¾|La¾{š>L2×>ŠŸº¼@~d¾#É=1Î>_=è=vZš¾Œ:g¿'Õ¿Hƒ©¿Dd¢¾Ír^¾E¯¿6¾ºä~=N>‘é4>YϽ[Ѿ¥Ãc¿©ó¿!)å¿:N¿-舾Álõ=jÌ=>¸w>·=sçÿ¾¾¿!ò8¾úm÷½Ìº>=¿_½[¹Ø¾e(¾` 0¾î•¿5!$¿˜¿Žz¿Söp¿öñ¾¡öw<>&#p¾%3Š¿?¹l¿…öØ¿\’Ñ¿ž$¾Æ÷ø¾Úµ¥¾ÅÖ‰¾&*ºÀb„¾‚~É¿Zµ¿™³¿Tºó½°|w>™{‡=¦¿ì¾©Ýc¿ ¥¿¿Ñ$¿%TM¿VÝD¿’L­¿°/V¿©õ ¿ƒ€¿¿7õ%¿¿-‹z¿M¿BKj¿Õ¸¿•¿TE]¿-¿p,`¿T—¿/ä¿‚³ä¿jྟ°¸=O‡‰¾FÁ¾’Î=ª2„>Ú¶{>´s¾ðʸ¿"›¾¥/½V5‘;Š>¿<9>Ç>#í=Zq7>|µ%>ù?âò?'ì?Bv? >6@Þ¾](;½²t>g¸>‰³p»ß²E½¢ -=_—e=¾ZU¾æ4ø¿$¿UÕ-¿Dü8¾ƒfPcasacore-3.7.1/images/Images/test/input_image_processor_dont_replace_me000066400000000000000000000000001476623553700264660ustar00rootroot00000000000000casacore-3.7.1/images/Images/test/mexinputtest.fits000066400000000000000000001111001476623553700224070ustar00rootroot00000000000000SIMPLE = T / conforms to FITS standard BITPIX = 8 / array data type NAXIS = 0 / number of array dimensions EXTEND = T END XTENSION= 'IMAGE ' / Image extension BITPIX = -32 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 10 NAXIS2 = 10 PCOUNT = 0 / number of parameters GCOUNT = 1 / number of groups EXTNAME = 'SCI ' EXTVER = 1 CRPIX1 = 5.0 CRVAL1 = 53.071 CDELT1 = 0.001 CTYPE1 = 'RA---TAN' CRPIX2 = 5.0 CRVAL2 = -27.709 CDELT2 = 0.001 CTYPE2 = 'DEC--TAN' EQUINOX = 2000.0 END ??À@ @`@@°@Ð@ðAAA(A8AHAXAhAxA„AŒA”AœA¤A¬A´A¼AÄAÌAÔAÜAäAìAôAüBBB BBBBBB"B&B*B.B2B6B:B>BBBFBJBNBRBVBZB^BbBfBjBnBrBvBzB~BBƒB…B‡B‰B‹BBB‘B“B•B—B™B›BBŸB¡B£B¥B§B©B«B­B¯B±B³BµB·B¹B»B½B¿BÁBÃBÅBÇXTENSION= 'IMAGE ' / Image extension BITPIX = -64 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 10 NAXIS2 = 10 PCOUNT = 0 / number of parameters GCOUNT = 1 / number of groups EXTNAME = 'ERR ' EXTVER = 1 CRPIX1 = 5.0 CRVAL1 = 53.071 CDELT1 = 0.001 CTYPE1 = 'RA---TAN' CRPIX2 = 5.0 CRVAL2 = -27.709 CDELT2 = 0.001 CTYPE2 = 'DEC--TAN' EQUINOX = 2000.0 END ?Ó333333?ôÌÌÌÌÌÍ@ffffff@ ffffff@333333@333333@333333@333333@ ™™™™™š@"™™™™™š@$™™™™™š@&™™™™™š@(™™™™™š@*™™™™™š@,™™™™™š@.™™™™™š@0LÌÌÌÌÍ@1LÌÌÌÌÍ@2LÌÌÌÌÍ@3LÌÌÌÌÍ@4LÌÌÌÌÍ@5LÌÌÌÌÍ@6LÌÌÌÌÍ@7LÌÌÌÌÍ@8LÌÌÌÌÍ@9LÌÌÌÌÍ@:LÌÌÌÌÍ@;LÌÌÌÌÍ@LÌÌÌÌÍ@?LÌÌÌÌÍ@@&fffff@@¦fffff@A&fffff@A¦fffff@B&fffff@B¦fffff@C&fffff@C¦fffff@D&fffff@D¦fffff@E&fffff@E¦fffff@F&fffff@F¦fffff@G&fffff@G¦fffff@H&fffff@H¦fffff@I&fffff@I¦fffff@J&fffff@J¦fffff@K&fffff@K¦fffff@L&fffff@L¦fffff@M&fffff@M¦fffff@N&fffff@N¦fffff@O&fffff@O¦fffff@P33333@PS33333@P“33333@PÓ33333@Q33333@QS33333@Q“33333@QÓ33333@R33333@RS33333@R“33333@RÓ33333@S33333@SS33333@S“33333@SÓ33333@T33333@TS33333@T“33333@TÓ33333@U33333@US33333@U“33333@UÓ33333@V33333@VS33333@V“33333@VÓ33333@W33333@WS33333@W“33333@WÓ33333@X33333@XS33333@X“33333@XÓ33333XTENSION= 'IMAGE ' / Image extension BITPIX = 32 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 10 NAXIS2 = 10 PCOUNT = 0 / number of parameters GCOUNT = 1 / number of groups EXTNAME = 'DQ ' EXTVER = 1 CRPIX1 = 5.0 CRVAL1 = 53.071 CDELT1 = 0.001 CTYPE1 = 'RA---TAN' CRPIX2 = 5.0 CRVAL2 = -27.709 CDELT2 = 0.001 CTYPE2 = 'DEC--TAN' EQUINOX = 2000.0 END   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcXTENSION= 'IMAGE ' / Image extension BITPIX = -32 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 10 NAXIS2 = 10 PCOUNT = 0 / number of parameters GCOUNT = 1 / number of groups EXTNAME = 'SCI ' EXTVER = 2 CRPIX1 = 5.0 CRVAL1 = 53.071 CDELT1 = 0.001 CTYPE1 = 'RA---TAN' CRPIX2 = 5.0 CRVAL2 = -27.709 CDELT2 = 0.001 CTYPE2 = 'DEC--TAN' EQUINOX = 2000.0 END >ÌÌÍA&ffA£33Aó33B!™šBI™šBq™šBŒÌÍB ÌÍB´ÌÍ?³33A6ffA«33Aû33B%™šBM™šBu™šBŽÌÍB¢ÌÍB¶ÌÍ@™šAFffA³33B™šB)™šBQ™šBy™šBÌÍB¤ÌÍB¸ÌÍ@Y™šAVffA»33B™šB-™šBU™šB}™šB’ÌÍB¦ÌÍBºÌÍ@ŒÌÍAfffAÃ33B ™šB1™šBY™šB€ÌÍB”ÌÍB¨ÌÍB¼ÌÍ@¬ÌÍAvffAË33B ™šB5™šB]™šB‚ÌÍB–ÌÍBªÌÍB¾ÌÍ@ÌÌÍAƒ33AÓ33B™šB9™šBa™šB„ÌÍB˜ÌÍB¬ÌÍBÀÌÍ@ìÌÍA‹33AÛ33B™šB=™šBe™šB†ÌÍBšÌÍB®ÌÍBÂÌÍAffA“33Aã33B™šBA™šBi™šBˆÌÍBœÌÍB°ÌÍBÄÌÍAffA›33Aë33B™šBE™šBm™šBŠÌÍBžÌÍB²ÌÍBÆÌÍXTENSION= 'IMAGE ' / Image extension BITPIX = -64 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 10 NAXIS2 = 10 PCOUNT = 0 / number of parameters GCOUNT = 1 / number of groups EXTNAME = 'ERR ' EXTVER = 2 CRPIX1 = 5.0 CRVAL1 = 53.071 CDELT1 = 0.001 CTYPE1 = 'RA---TAN' CRPIX2 = 5.0 CRVAL2 = -27.709 CDELT2 = 0.001 CTYPE2 = 'DEC--TAN' EQUINOX = 2000.0 END ?É™™™™™š@$ffffff@4333333@>333333@D™™™™š@I™™™™š@N™™™™š@QŒÌÌÌÌÍ@T ÌÌÌÌÍ@VŒÌÌÌÌÍ?ó333333@&ffffff@5333333@?333333@D™™™™™š@I™™™™™š@N™™™™™š@QÌÌÌÌÌÍ@TLÌÌÌÌÍ@VÌÌÌÌÌÍ@™™™™™š@(ffffff@6333333@@™™™™š@E™™™™š@J™™™™š@O™™™™š@R ÌÌÌÌÍ@TŒÌÌÌÌÍ@W ÌÌÌÌÍ@ ™™™™™š@*ffffff@7333333@@™™™™™š@E™™™™™š@J™™™™™š@O™™™™™š@RLÌÌÌÌÍ@TÌÌÌÌÌÍ@WLÌÌÌÌÍ@ÌÌÌÌÌÍ@,ffffff@8333333@A™™™™š@F™™™™š@K™™™™š@P ÌÌÌÌÍ@RŒÌÌÌÌÍ@U ÌÌÌÌÍ@WŒÌÌÌÌÍ@ÌÌÌÌÌÍ@.ffffff@9333333@A™™™™™š@F™™™™™š@K™™™™™š@PLÌÌÌÌÍ@RÌÌÌÌÌÍ@ULÌÌÌÌÍ@WÌÌÌÌÌÍ@ÌÌÌÌÌÍ@0333333@:333333@B™™™™š@G™™™™š@L™™™™š@PŒÌÌÌÌÍ@S ÌÌÌÌÍ@UŒÌÌÌÌÍ@X ÌÌÌÌÍ@ÌÌÌÌÌÍ@1333333@;333333@B™™™™™š@G™™™™™š@L™™™™™š@PÌÌÌÌÌÍ@SLÌÌÌÌÍ@UÌÌÌÌÌÍ@XLÌÌÌÌÍ@ ffffff@2333333@<333333@C™™™™š@H™™™™š@M™™™™š@Q ÌÌÌÌÍ@SŒÌÌÌÌÍ@V ÌÌÌÌÍ@XŒÌÌÌÌÍ@"ffffff@3333333@=333333@C™™™™™š@H™™™™™š@M™™™™™š@QLÌÌÌÌÍ@SÌÌÌÌÌÍ@VLÌÌÌÌÍ@XÌÌÌÌÌÍXTENSION= 'IMAGE ' / Image extension BITPIX = 32 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 10 NAXIS2 = 10 PCOUNT = 0 / number of parameters GCOUNT = 1 / number of groups EXTNAME = 'DQ ' EXTVER = 2 CRPIX1 = 5.0 CRVAL1 = 53.071 CDELT1 = 0.001 CTYPE1 = 'RA---TAN' CRPIX2 = 5.0 CRVAL2 = -27.709 CDELT2 = 0.001 CTYPE2 = 'DEC--TAN' EQUINOX = 2000.0 END (2<FPZ )3=GQ[  *4>HR\ !+5?IS]",6@JT^#-7AKU_$.8BLV`%/9CMWa&0:DNXb '1;EOYccasacore-3.7.1/images/Images/test/qualityimage.fits000066400000000000000000002145001476623553700223410ustar00rootroot00000000000000SIMPLE = T / conforms to FITS standard BITPIX = 8 / array data type NAXIS = 0 / number of array dimensions EXTEND = T END XTENSION= 'IMAGE ' / Image extension BITPIX = -32 / array data type NAXIS = 3 / number of array dimensions NAXIS1 = 5 NAXIS2 = 5 NAXIS3 = 5 PCOUNT = 0 / number of parameters GCOUNT = 1 / number of groups EXTNAME = 'IFU1.SCI' / Extension name ERRDATA = 'IFU1.ERR' / Name of its error extension QUALDATA= 'IFU1.DQ ' / Name of its data quality extension / World coordinate system start: CRPIX1 = 5.0 / Reference pixel CRPIX2 = 5.0 / Reference pixel CRPIX3 = 50.0 / Reference pixel CRVAL1 = 53.071 / RA at CRPIX1 CRVAL2 = -27.709 / DEC at CRPIX2 CRVAL3 = 8000.0 / Lambda at CRPIX2 EQUINOX = 2000.0 / The equinox CD1_1 = 0.000277777777777777 / Scale of 1 arcsec/pix CD1_2 = 0.0 CD1_3 = 0.0 CD2_1 = 0.0 CD2_2 = 0.000277777777777777 / Scale of 1 arcsec/pix CD2_3 = 0.0 CD3_1 = 0.0 CD3_2 = 0.0 CD3_3 = 9.0 / Dispersion in spectral axis CTYPE1 = 'RA---TAN' / Declaring axis 1 as RA CTYPE2 = 'DEC--TAN' / Declaring axis 2 as DEC CTYPE3 = 'WAVE ' / Declaring axis 3 as air wavelength CUNIT1 = 'deg ' / Unit for RA CUNIT2 = 'deg ' / Unit for DEC CUNIT3 = 'Angstrom' / Unit for spectral axis HDUCLAS2= 'DATA ' / Identification as science extension HDUCLASS= 'ESO ' / class name HDUDOC = 'DICD ' / document with class description HDUVERS = 'DICD version 6' / version number HDUCLAS1= 'IMAGE ' / the FITS type described BUNIT = '10**(-20)*erg/s/cm**2/Angstrom' / unit / World coordinate system end. END ??À@ @`@@°@Ð@ðAAA(A8AHAXAhAxA„AŒA”AœA¤A¬A´A¼AÄAÌAÔAÜAäAìAôAüBBB BBBBBB"B&B*B.B2B6B:B>BBBFBJBNBRBVBZB^BbBfBjBnBrBvBzB~BBƒB…B‡B‰B‹BBB‘B“B•B—B™B›BBŸB¡B£B¥B§B©B«B­B¯B±B³BµB·B¹B»B½B¿BÁBÃBÅBÇBÉBËBÍBÏBÑBÓBÕB×BÙBÛBÝBßBáBãBåBçBéBëBíBïBñBóBõB÷BùXTENSION= 'IMAGE ' / Image extension BITPIX = -32 / array data type NAXIS = 3 / number of array dimensions NAXIS1 = 5 NAXIS2 = 5 NAXIS3 = 5 PCOUNT = 0 / number of parameters GCOUNT = 1 / number of groups EXTNAME = 'IFU1.ERR' / Extension name SCIDATA = 'IFU1.SCI' / Name of its data extension QUALDATA= 'IFU1.DQ ' / Name of its data quality extension / World coordinate system start: CRPIX1 = 5.0 / Reference pixel CRPIX2 = 5.0 / Reference pixel CRPIX3 = 50.0 / Reference pixel CRVAL1 = 53.071 / RA at CRPIX1 CRVAL2 = -27.709 / DEC at CRPIX2 CRVAL3 = 8000.0 / Lambda at CRPIX2 EQUINOX = 2000.0 / The equinox CD1_1 = 0.000277777777777777 / Scale of 1 arcsec/pix CD1_2 = 0.0 CD1_3 = 0.0 CD2_1 = 0.0 CD2_2 = 0.000277777777777777 / Scale of 1 arcsec/pix CD2_3 = 0.0 CD3_1 = 0.0 CD3_2 = 0.0 CD3_3 = 9.0 / Dispersion in spectral axis CTYPE1 = 'RA---TAN' / Declaring axis 1 as RA CTYPE2 = 'DEC--TAN' / Declaring axis 2 as DEC CTYPE3 = 'WAVE ' / Declaring axis 3 as air wavelength CUNIT1 = 'deg ' / Unit for RA CUNIT2 = 'deg ' / Unit for DEC CUNIT3 = 'Angstrom' / Unit for spectral axis HDUCLAS2= 'ERROR ' / Identification as error extension HDUCLAS3= 'MSE ' / Error type: Mean Squared Error HDUCLASS= 'ESO ' / class name HDUDOC = 'DICD ' / document with class description HDUVERS = 'DICD version 6' / version number HDUCLAS1= 'IMAGE ' / the FITS type described BUNIT = '10**(-20)*erg/s/cm**2/Angstrom' / unit / World coordinate system end. END >™™š?¦ff@33@S33@‰™š@©™š@É™š@陚AÌÍAÌÍA$ÌÍA4ÌÍADÌÍATÌÍAdÌÍAtÌÍA‚ffAŠffA’ffAšffA¢ffAªffA²ffAºffAÂffAÊffAÒffAÚffAâffAêffAòffAúffB33B33B 33B 33B33B33B33B33B!33B%33B)33B-33B133B533B933B=33BA33BE33BI33BM33BQ33BU33BY33B]33Ba33Be33Bi33Bm33Bq33Bu33By33B}33B€™šB‚™šB„™šB†™šBˆ™šBŠ™šBŒ™šBŽ™šB™šB’™šB”™šB–™šB˜™šBš™šBœ™šBž™šB ™šB¢™šB¤™šB¦™šB¨™šBª™šB¬™šB®™šB°™šB²™šB´™šB¶™šB¸™šBº™šB¼™šB¾™šBÀ™šB™šBÄ™šBÆ™šBÈ™šBÊ™šBÌ™šBΙšBЙšBÒ™šBÔ™šBÖ™šBØ™šBÚ™šBÜ™šBÞ™šBà™šB♚B䙚B晚B虚BꙚB왚BBð™šBò™šBô™šBö™šBø™šXTENSION= 'IMAGE ' / Image extension BITPIX = 16 / array data type NAXIS = 3 / number of array dimensions NAXIS1 = 5 NAXIS2 = 5 NAXIS3 = 5 PCOUNT = 0 / number of parameters GCOUNT = 1 / number of groups EXTNAME = 'IFU1.DQ ' / Extension name SCIDATA = 'IFU1.SCI' / Name of its data extension ERRDATA = 'IFU1.ERR' / Name of its error extension QUALMASK= 16383 / Mask number to identify bad pixels / World coordinate system start: CRPIX1 = 5.0 / Reference pixel CRPIX2 = 5.0 / Reference pixel CRPIX3 = 50.0 / Reference pixel CRVAL1 = 53.071 / RA at CRPIX1 CRVAL2 = -27.709 / DEC at CRPIX2 CRVAL3 = 8000.0 / Lambda at CRPIX2 EQUINOX = 2000.0 / The equinox CD1_1 = 0.000277777777777777 / Scale of 1 arcsec/pix CD1_2 = 0.0 CD1_3 = 0.0 CD2_1 = 0.0 CD2_2 = 0.000277777777777777 / Scale of 1 arcsec/pix CD2_3 = 0.0 CD3_1 = 0.0 CD3_2 = 0.0 CD3_3 = 9.0 / Dispersion in spectral axis CTYPE1 = 'RA---TAN' / Declaring axis 1 as RA CTYPE2 = 'DEC--TAN' / Declaring axis 2 as DEC CTYPE3 = 'WAVE ' / Declaring axis 3 as air wavelength CUNIT1 = 'deg ' / Unit for RA CUNIT2 = 'deg ' / Unit for DEC CUNIT3 = 'Angstrom' / Unit for spectral axis HDUCLAS2= 'QUALITY ' / Identification as data quality extension HDUCLAS3= 'FLAG16BIT' / Data quality type HDUCLASS= 'ESO ' / class name HDUDOC = 'DICD ' / document with class description HDUVERS = 'DICD version 6' / version number HDUCLAS1= 'IMAGE ' / the FITS type described BUNIT = '10**(-20)*erg/s/cm**2/Angstrom' / unit / World coordinate system end. END XTENSION= 'IMAGE ' / Image extension BITPIX = -32 / array data type NAXIS = 3 / number of array dimensions NAXIS1 = 5 NAXIS2 = 5 NAXIS3 = 5 PCOUNT = 0 / number of parameters GCOUNT = 1 / number of groups EXTNAME = 'IFU2.SCI' / Extension name ERRDATA = 'ifu2.err' / Name of its error extension QUALDATA= 'ifu2.dq ' / Name of its data quality extension / World coordinate system start: CRPIX1 = 5.0 / Reference pixel CRPIX2 = 5.0 / Reference pixel CRPIX3 = 50.0 / Reference pixel CRVAL1 = 53.071 / RA at CRPIX1 CRVAL2 = -27.709 / DEC at CRPIX2 CRVAL3 = 8000.0 / Lambda at CRPIX2 EQUINOX = 2000.0 / The equinox CD1_1 = 0.000277777777777777 / Scale of 1 arcsec/pix CD1_2 = 0.0 CD1_3 = 0.0 CD2_1 = 0.0 CD2_2 = 0.000277777777777777 / Scale of 1 arcsec/pix CD2_3 = 0.0 CD3_1 = 0.0 CD3_2 = 0.0 CD3_3 = 9.0 / Dispersion in spectral axis CTYPE1 = 'RA---TAN' / Declaring axis 1 as RA CTYPE2 = 'DEC--TAN' / Declaring axis 2 as DEC CTYPE3 = 'WAVE ' / Declaring axis 3 as air wavelength CUNIT1 = 'deg ' / Unit for RA CUNIT2 = 'deg ' / Unit for DEC CUNIT3 = 'Angstrom' / Unit for spectral axis HDUCLAS2= 'DATA ' / Identification as science extension HDUCLASS= 'ESO ' / class name HDUDOC = 'DICD ' / document with class description HDUVERS = 'DICD version 6' / version number HDUCLAS1= 'IMAGE ' / the FITS type described BUNIT = '10**(-20)*erg/s/cm**2/Angstrom' / unit / World coordinate system end. END >ÌÌÍ@¬ÌÍA&ffAvffA£33?³33@ÌÌÍA6ffAƒ33A«33@™š@ìÌÍAFffA‹33A³33@Y™šAffAVffA“33A»33@ŒÌÍAffAfffA›33AÃ33AË33Aó33B ™šB!™šB5™šAÓ33Aû33B™šB%™šB9™šAÛ33B™šB™šB)™šB=™šAã33B™šB™šB-™šBA™šAë33B ™šB™šB1™šBE™šBI™šB]™šBq™šB‚ÌÍBŒÌÍBM™šBa™šBu™šB„ÌÍBŽÌÍBQ™šBe™šBy™šB†ÌÍBÌÍBU™šBi™šB}™šBˆÌÍB’ÌÍBY™šBm™šB€ÌÍBŠÌÍB”ÌÍB–ÌÍB ÌÍBªÌÍB´ÌÍB¾ÌÍB˜ÌÍB¢ÌÍB¬ÌÍB¶ÌÍBÀÌÍBšÌÍB¤ÌÍB®ÌÍB¸ÌÍBÂÌÍBœÌÍB¦ÌÍB°ÌÍBºÌÍBÄÌÍBžÌÍB¨ÌÍB²ÌÍB¼ÌÍBÆÌÍBÈÌÍBÒÌÍBÜÌÍBæÌÍBðÌÍBÊÌÍBÔÌÍBÞÌÍBèÌÍBòÌÍBÌÌÍBÖÌÍBàÌÍBêÌÍBôÌÍBÎÌÍBØÌÍBâÌÍBìÌÍBöÌÍBÐÌÍBÚÌÍBäÌÍBîÌÍBøÌÍXTENSION= 'IMAGE ' / Image extension BITPIX = -32 / array data type NAXIS = 3 / number of array dimensions NAXIS1 = 5 NAXIS2 = 5 NAXIS3 = 5 PCOUNT = 0 / number of parameters GCOUNT = 1 / number of groups EXTNAME = 'IFU2.ERR' / Extension name SCIDATA = 'IFU2.SCI' / Name of its data extension QUALDATA= 'IFU2.DQ ' / Name of its data quality extension / World coordinate system start: CRPIX1 = 5.0 / Reference pixel CRPIX2 = 5.0 / Reference pixel CRPIX3 = 50.0 / Reference pixel CRVAL1 = 53.071 / RA at CRPIX1 CRVAL2 = -27.709 / DEC at CRPIX2 CRVAL3 = 8000.0 / Lambda at CRPIX2 EQUINOX = 2000.0 / The equinox CD1_1 = 0.000277777777777777 / Scale of 1 arcsec/pix CD1_2 = 0.0 CD1_3 = 0.0 CD2_1 = 0.0 CD2_2 = 0.000277777777777777 / Scale of 1 arcsec/pix CD2_3 = 0.0 CD3_1 = 0.0 CD3_2 = 0.0 CD3_3 = 9.0 / Dispersion in spectral axis CTYPE1 = 'RA---TAN' / Declaring axis 1 as RA CTYPE2 = 'DEC--TAN' / Declaring axis 2 as DEC CTYPE3 = 'WAVE ' / Declaring axis 3 as air wavelength CUNIT1 = 'deg ' / Unit for RA CUNIT2 = 'deg ' / Unit for DEC CUNIT3 = 'Angstrom' / Unit for spectral axis HDUCLAS2= 'ERROR ' / Identification as error extension HDUCLAS3= 'RMSE ' / Error type: Root Mean Squared Error HDUCLASS= 'ESO ' / class name HDUDOC = 'DICD ' / document with class description HDUVERS = 'DICD version 6' / version number HDUCLAS1= 'IMAGE ' / the FITS type described BUNIT = '10**(-20)*erg/s/cm**2/Angstrom' / unit / World coordinate system end. END >LÌÍ@¦ffA#33As33A¡™š?™™š@ÆffA333A™šA©™š@ ÌÍ@æffAC33A‰™šA±™š@LÌÍA33AS33A‘™šA¹™š@†ffA33Ac33A™™šAÁ™šAÉ™šAñ™šB ÌÍB ÌÍB4ÌÍAÑ™šAù™šBÌÍB$ÌÍB8ÌÍAÙ™šBÌÍBÌÍB(ÌÍB<ÌÍAᙚBÌÍBÌÍB,ÌÍB@ÌÍA陚BÌÍBÌÍB0ÌÍBDÌÍBHÌÍB\ÌÍBpÌÍB‚ffBŒffBLÌÍB`ÌÍBtÌÍB„ffBŽffBPÌÍBdÌÍBxÌÍB†ffBffBTÌÍBhÌÍB|ÌÍBˆffB’ffBXÌÍBlÌÍB€ffBŠffB”ffB–ffB ffBªffB´ffB¾ffB˜ffB¢ffB¬ffB¶ffBÀffBšffB¤ffB®ffB¸ffBÂffBœffB¦ffB°ffBºffBÄffBžffB¨ffB²ffB¼ffBÆffBÈffBÒffBÜffBæffBðffBÊffBÔffBÞffBèffBòffBÌffBÖffBàffBêffBôffBÎffBØffBâffBìffBöffBÐffBÚffBäffBîffBøffXTENSION= 'IMAGE ' / Image extension BITPIX = 16 / array data type NAXIS = 3 / number of array dimensions NAXIS1 = 5 NAXIS2 = 5 NAXIS3 = 5 PCOUNT = 0 / number of parameters GCOUNT = 1 / number of groups EXTNAME = 'IFU2.DQ ' / Extension name SCIDATA = 'IFU2.SCI' / Name of its data extension ERRDATA = 'IFU2.ERR' / Name of its error extension QUALMASK= 16383 / Mask number to identify bad pixels / World coordinate system start: CRPIX1 = 5.0 / Reference pixel CRPIX2 = 5.0 / Reference pixel CRPIX3 = 50.0 / Reference pixel CRVAL1 = 53.071 / RA at CRPIX1 CRVAL2 = -27.709 / DEC at CRPIX2 CRVAL3 = 8000.0 / Lambda at CRPIX2 EQUINOX = 2000.0 / The equinox CD1_1 = 0.000277777777777777 / Scale of 1 arcsec/pix CD1_2 = 0.0 CD1_3 = 0.0 CD2_1 = 0.0 CD2_2 = 0.000277777777777777 / Scale of 1 arcsec/pix CD2_3 = 0.0 CD3_1 = 0.0 CD3_2 = 0.0 CD3_3 = 9.0 / Dispersion in spectral axis CTYPE1 = 'RA---TAN' / Declaring axis 1 as RA CTYPE2 = 'DEC--TAN' / Declaring axis 2 as DEC CTYPE3 = 'WAVE ' / Declaring axis 3 as air wavelength CUNIT1 = 'deg ' / Unit for RA CUNIT2 = 'deg ' / Unit for DEC CUNIT3 = 'Angstrom' / Unit for spectral axis HDUCLAS2= 'QUALITY ' / Identification as data quality extension HDUCLAS3= 'FLAG16BIT' / Data quality type HDUCLASS= 'ESO ' / class name HDUDOC = 'DICD ' / document with class description HDUVERS = 'DICD version 6' / version number HDUCLAS1= 'IMAGE ' / the FITS type described BUNIT = '10**(-20)*erg/s/cm**2/Angstrom' / unit / World coordinate system end. END XTENSION= 'IMAGE ' / Image extension BITPIX = -32 / array data type NAXIS = 3 / number of array dimensions NAXIS1 = 5 NAXIS2 = 5 NAXIS3 = 5 PCOUNT = 0 / number of parameters GCOUNT = 1 / number of groups EXTNAME = 'IFU3.SCI' / Extension name QUALDATA= 'IFU3.DQ ' / Name of its data quality extension / World coordinate system start: CRPIX1 = 5.0 / Reference pixel CRPIX2 = 5.0 / Reference pixel CRPIX3 = 50.0 / Reference pixel CRVAL1 = 53.071 / RA at CRPIX1 CRVAL2 = -27.709 / DEC at CRPIX2 CRVAL3 = 8000.0 / Lambda at CRPIX2 EQUINOX = 2000.0 / The equinox CD1_1 = 0.000277777777777777 / Scale of 1 arcsec/pix CD1_2 = 0.0 CD1_3 = 0.0 CD2_1 = 0.0 CD2_2 = 0.000277777777777777 / Scale of 1 arcsec/pix CD2_3 = 0.0 CD3_1 = 0.0 CD3_2 = 0.0 CD3_3 = 9.0 / Dispersion in spectral axis CTYPE1 = 'RA---TAN' / Declaring axis 1 as RA CTYPE2 = 'DEC--TAN' / Declaring axis 2 as DEC CTYPE3 = 'WAVE ' / Declaring axis 3 as air wavelength CUNIT1 = 'deg ' / Unit for RA CUNIT2 = 'deg ' / Unit for DEC CUNIT3 = 'Angstrom' / Unit for spectral axis HDUCLAS2= 'DATA ' / Identification as science extension HDUCLASS= 'ESO ' / class name HDUDOC = 'DICD ' / document with class description HDUVERS = 'DICD version 6' / version number HDUCLAS1= 'IMAGE ' / the FITS type described BUNIT = '10**(-20)*erg/s/cm**2/Angstrom' / unit / World coordinate system end. END A¦ffA®ffA¶ffA¾ffAÆffA|ÌÍA†ffAŽffA–ffAžffA,ÌÍA<ÌÍALÌÍA\ÌÍAlÌÍ@¹™š@Ù™š@ù™šA ÌÍAÌÍ?LÌÍ?æff@333@s33@™™šB733B;33B?33BC33BG33B#33B'33B+33B/33B333B33B33B33B33B33AöffAþffB33B33B 33AÎffAÖffAÞffAæffAîffB™šB™šB‘™šB“™šB•™šBƒ™šB…™šB‡™šB‰™šB‹™šBs33Bw33B{33B33B™šB_33Bc33Bg33Bk33Bo33BK33BO33BS33BW33B[33B¿™šBÁ™šBÙšBÅ™šBÇ™šBµ™šB·™šB¹™šB»™šB½™šB«™šB­™šB¯™šB±™šB³™šB¡™šB£™šB¥™šB§™šB©™šB—™šB™™šB›™šB™šBŸ™šBñ™šBó™šBõ™šB÷™šBù™šB癚B陚B뙚B홚BBÝ™šBß™šBᙚB㙚B噚BÓ™šBÕ™šB×™šBÙ™šBÛ™šBÉ™šBË™šBÍ™šBÏ™šBÑ™šXTENSION= 'IMAGE ' / Image extension BITPIX = -32 / array data type NAXIS = 3 / number of array dimensions NAXIS1 = 5 NAXIS2 = 5 NAXIS3 = 5 PCOUNT = 0 / number of parameters GCOUNT = 1 / number of groups EXTNAME = 'IFU3.DQ ' / Extension name SCIDATA = 'IFU3.SCI' / Name of its data extension / World coordinate system start: CRPIX1 = 5.0 / Reference pixel CRPIX2 = 5.0 / Reference pixel CRPIX3 = 50.0 / Reference pixel CRVAL1 = 53.071 / RA at CRPIX1 CRVAL2 = -27.709 / DEC at CRPIX2 CRVAL3 = 8000.0 / Lambda at CRPIX2 EQUINOX = 2000.0 / The equinox CD1_1 = 0.000277777777777777 / Scale of 1 arcsec/pix CD1_2 = 0.0 CD1_3 = 0.0 CD2_1 = 0.0 CD2_2 = 0.000277777777777777 / Scale of 1 arcsec/pix CD2_3 = 0.0 CD3_1 = 0.0 CD3_2 = 0.0 CD3_3 = 9.0 / Dispersion in spectral axis CTYPE1 = 'RA---TAN' / Declaring axis 1 as RA CTYPE2 = 'DEC--TAN' / Declaring axis 2 as DEC CTYPE3 = 'WAVE ' / Declaring axis 3 as air wavelength CUNIT1 = 'deg ' / Unit for RA CUNIT2 = 'deg ' / Unit for DEC CUNIT3 = 'Angstrom' / Unit for spectral axis HDUCLAS2= 'QUALITY ' / Identification as data quality extension HDUCLAS3= 'MASKONE ' / Data quality type HDUCLASS= 'ESO ' / class name HDUDOC = 'DICD ' / document with class description HDUVERS = 'DICD version 6' / version number HDUCLAS1= 'IMAGE ' / the FITS type described BUNIT = '10**(-20)*erg/s/cm**2/Angstrom' / unit / World coordinate system end. END ?€?€?€?€?€?€?€?€casacore-3.7.1/images/Images/test/tExtendImage.cc000066400000000000000000000207431476623553700216500ustar00rootroot00000000000000//# tExtendImage.cc: Test program for class ExtendImage //# Copyright (C) 2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void testVectorROIter (const Lattice& extendlat, const Lattice& lattice, Int nnew) { Int nstep; const IPosition latticeShape(extendlat.shape()); const IPosition cursorShape(1,latticeShape(0)); LatticeStepper step(latticeShape, cursorShape); RO_LatticeIterator iter(extendlat, step); LatticeStepper step2(lattice.shape(), cursorShape); RO_LatticeIterator iter2(lattice, step2); // static_cast's added for a workaround for an SGI compiler bug. for (iter2.reset(); !iter2.atEnd(); iter2++) { for (Int i=0; i >(iter.vectorCursor()), static_cast >(iter2.vectorCursor())), AipsError); iter++; } } AlwaysAssert(iter.atEnd(), AipsError); nstep = iter.nsteps(); AlwaysAssert(nstep == latticeShape.product()/latticeShape(0), AipsError); IPosition expectedPos(latticeShape-1); AlwaysAssert(iter.endPosition() == expectedPos, AipsError); expectedPos(0) = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); } void testRest() { CoordinateSystem cSys = CoordinateUtil::defaultCoords2D(); CoordinateSystem cSys2 = CoordinateUtil::defaultCoords3D(); PagedImage pa(IPosition(2,10,10), cSys, "tExtendImage_tmp.pa"); AlwaysAssertExit (pa.isPaged()); AlwaysAssertExit (pa.isPersistent()); AlwaysAssertExit (pa.isWritable()); AlwaysAssertExit (pa.name(True) == "tExtendImage_tmp.pa"); LCPagedMask lcmask(IPosition(2,10,10), "tExtendImage_tmp.pa/mask"); ImageRegion mask(lcmask); { // Make an ExtendImage. ExtendImage sl(pa, IPosition(3,10,10,5), cSys2); AlwaysAssertExit (!sl.isMasked()); AlwaysAssertExit (!sl.hasPixelMask()); AlwaysAssertExit (sl.isPaged()); AlwaysAssertExit (!sl.isPersistent()); // A copy of the ExtendImage. ExtendImage sl1(sl); AlwaysAssertExit (!sl1.isMasked()); AlwaysAssertExit (!sl1.hasPixelMask()); AlwaysAssertExit (sl1.isPaged()); AlwaysAssertExit (!sl1.isPersistent()); AlwaysAssertExit (!sl1.isWritable()); AlwaysAssertExit (sl1.name(True) == "tExtendImage_tmp.pa"); } { // An ExtendImage as a masked Lattice. SubImage sls(pa, mask); ExtendImage sl(sls, IPosition(3,10,10,1), cSys2); AlwaysAssertExit (sl.isMasked()); AlwaysAssertExit (!sl.hasPixelMask()); AlwaysAssertExit (sl.isPaged()); AlwaysAssertExit (!sl.isPersistent()); AlwaysAssertExit (!sl.isWritable()); // A copy of the ExtendImage. ExtendImage sl1(sl); AlwaysAssertExit (sl1.isMasked()); AlwaysAssertExit (!sl1.hasPixelMask()); AlwaysAssertExit (sl1.isPaged()); AlwaysAssertExit (!sl1.isPersistent()); AlwaysAssertExit (!sl1.isWritable()); AlwaysAssertExit (sl1.name(True) == "tExtendImage_tmp.pa"); } { // An ExtendImage with an image mask. pa.defineRegion ("mask1", mask, RegionHandler::Masks); pa.setDefaultMask ("mask1"); ExtendImage sl(pa, IPosition(3,10,10,2), cSys2); AlwaysAssertExit (sl.isMasked()); AlwaysAssertExit (sl.hasPixelMask()); AlwaysAssertExit (sl.isPaged()); AlwaysAssertExit (!sl.isPersistent()); AlwaysAssertExit (!sl.isWritable()); // A copy of the ExtendImage. ExtendImage sl1(sl); AlwaysAssertExit (sl1.isMasked()); AlwaysAssertExit (sl1.hasPixelMask()); AlwaysAssertExit (sl1.isPaged()); AlwaysAssertExit (!sl1.isPersistent()); AlwaysAssertExit (!sl1.isWritable()); } } void testMask() { IPosition latticeShape(3,10,11,12); CoordinateSystem cSys2 = CoordinateUtil::defaultCoords3D(); CoordinateSystem cSys3 = CoordinateUtil::defaultCoords4D(); PagedImage pa(latticeShape, cSys2, "tExtendImage_tmp.pa"); LCPagedMask mask(latticeShape, "tExtendImage_tmp.pa/mask"); Array arr(pa.shape()); indgen(arr); pa.put (arr); Array arrm(pa.shape()); arrm = True; arrm(IPosition(3,0,0,0), IPosition(3,9,10,11), IPosition(3,2,1,1)) = False; mask.put (arrm); pa.defineRegion ("mask1", mask, RegionHandler::Masks); pa.setDefaultMask ("mask1"); ExtendImage extendlat(pa, IPosition(4,10,11,5,12), cSys3); Array arr1 = extendlat.get(); Array arrm1 = extendlat.getMask(); AlwaysAssertExit (arr1.shape() == extendlat.shape()); AlwaysAssertExit (arrm1.shape() == extendlat.shape()); for (Int i=0; i<5; i++) { Array parr = arr1(IPosition(4,0,0,i,0), IPosition(4,10-1,11-1,i,12-1)); AlwaysAssertExit (allEQ(parr.reform(latticeShape), arr)); Array parrm = arrm1(IPosition(4,0,0,i,0), IPosition(4,10-1,11-1,i,12-1)); AlwaysAssertExit (allEQ(parrm.reform(latticeShape), arrm)); } for (Int i=0; i<5; i++) { Array parr = extendlat.getSlice (IPosition(4,0,0,i,0), IPosition(4,10,11,1,12)); AlwaysAssertExit (allEQ(parr.reform(latticeShape), arr)); Array parrm = extendlat.getMaskSlice (IPosition(4,0,0,i,0), IPosition(4,10,11,1,12)); AlwaysAssertExit (allEQ(parrm.reform(latticeShape), arrm)); } } int main () { try { { const IPosition latticeShape(3, 16, 1, 6); CoordinateSystem cSys = CoordinateUtil::defaultCoords3D(); const IPosition newShape(4, 16, 4, 3, 6); CoordinateSystem cSys2 = CoordinateUtil::defaultCoords4D(); Array arr(latticeShape); indgen(arr); PagedImage lattice(latticeShape, cSys, "tExtendImage_tmp.pa"); lattice.put (arr); ExtendImage extimg (lattice, newShape, cSys2); AlwaysAssertExit (extimg.isPaged()); AlwaysAssertExit (!extimg.isPersistent()); AlwaysAssertExit (!extimg.isMasked()); AlwaysAssertExit (!extimg.isWritable()); AlwaysAssertExit (extimg.shape() == newShape); Array arr1 = extimg.get(); AlwaysAssertExit (arr1.shape() == extimg.shape()); for (Int i=0; i<4; i++) { for (Int j=0; j<3; j++) { Array parr = arr1(IPosition(4,0,i,j,0), IPosition(4,16-1,i,j,6-1)); AlwaysAssertExit (allEQ(parr.reform(latticeShape), arr)); } } testVectorROIter (extimg, lattice, 4*3); } // Test some other ExtendImage functions. testRest(); // Test the axes removal.. testMask(); } catch (std::exception& x) { cerr << "Caught exception: " << x.what() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/images/Images/test/tFITSErrorImage.cc000066400000000000000000000332371476623553700222020ustar00rootroot00000000000000//# tFITSImage.cc: test the FITSImage class //# Copyright (C) 1994,1995,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Bool allNear (const Array& data, const Array& dataMask, const Array& fits, const Array& fitsMask, Float tol=1.0e-5); Bool cleanZero(Array& data, Array& dataMask); void printArray(const FITSErrorImage fitsErrImage, const Int size); int main (int argc, const char* argv[]) { try { LogIO os(LogOrigin("tFITSErrorImage", "main()", WHERE)); // Get inputs Input inputs(1); inputs.create("in", "", "Input FITS file"); inputs.create("hdunum", "0", "HDU number"); inputs.create("print", "F", "Print some data"); inputs.create("size", "5", "Size to print"); inputs.create("verbose", "F", "Put feedback on screen"); inputs.readArguments(argc, argv); String in = inputs.getString("in"); uInt hdunum = (uInt)inputs.getInt("hdunum"); const Bool print = inputs.getBool("print"); const Int size = inputs.getInt("size"); const Bool verbose = inputs.getBool("verbose"); // Give a default image and extension if (in.empty()) { in = "qualityimage.fits"; } Path p(in); if (!hdunum) hdunum = 2; // Give some feedback if (verbose) cerr << "Input image: " << in << " extension: " << hdunum << endl; // test the conversion between error types and String AlwaysAssert(FITSErrorImage::stringToErrorType("MSE")==FITSErrorImage::MSE, AipsError); AlwaysAssert(FITSErrorImage::errorTypeToString(FITSErrorImage::RMSE)=="RMSE", AipsError); AlwaysAssert(FITSErrorImage::stringToErrorType("INVMSE")==FITSErrorImage::INVMSE, AipsError); AlwaysAssert(FITSErrorImage::errorTypeToString(FITSErrorImage::INVRMSE)=="INVRMSE", AipsError); AlwaysAssert(FITSErrorImage::MSE==FITSErrorImage::DEFAULT, AipsError); AlwaysAssert(FITSErrorImage::stringToErrorType("WHATSUP")==FITSErrorImage::UNKNOWN, AipsError); // // Open FITSErrorImage as a MSE error image, // which corresponds to the default // FITSErrorImage fitsErrImage(in, 0, hdunum); fitsErrImage.tempClose(); // Checking the image type AlwaysAssert(fitsErrImage.imageType()=="FITSErrorImage", AipsError); if (verbose) cerr << "Checked the image type." << endl; // Checking the default error type FITSErrorImage::ErrorType defErrType=FITSErrorImage::MSE; AlwaysAssert(fitsErrImage.errorType()==defErrType, AipsError); if (verbose) cerr << "Checked for ErrorType." << endl; // Checking the unit Unit unit("Jy/beam"); AlwaysAssert(fitsErrImage.setUnits(unit), AipsError); AlwaysAssert(fitsErrImage.units().getName()=="Jy/beam", AipsError); if (verbose) cerr << "Checked setUnits()." << endl; // Check some basic mask properties fitsErrImage.reopen(); AlwaysAssert(fitsErrImage.hasPixelMask() == fitsErrImage.isMasked(), AipsError); if (fitsErrImage.hasPixelMask()) { Lattice& pMask = fitsErrImage.pixelMask(); AlwaysAssert(pMask.shape()==fitsErrImage.shape(), AipsError); } if (verbose) cerr << "Checked some basic mask properties." << endl; // Check some simple methods, mostly implemented // in the base classes AlwaysAssert(fitsErrImage.getRegionPtr()==0, AipsError); AlwaysAssert(fitsErrImage.isWritable()==False, AipsError); AlwaysAssert(fitsErrImage.name(False)==p.absoluteName(),AipsError); AlwaysAssert(fitsErrImage.ok(), AipsError); if (verbose) cerr << "Checked some simple methods." << endl; // Print some data and mask values if desired fitsErrImage.tempClose(); if (print) printArray(fitsErrImage, size); // Convert from FITS as a comparison String error; ImageInterface* pTempImage = 0; String imageName; if (!ImageFITSConverter::FITSToImage(pTempImage, error, imageName, in, 0, hdunum)) { os << error << LogIO::EXCEPTION; } if (verbose) cerr << "Converted from FITS as comparison." << endl; // Get the data and compare the array, masks and coords Array fitsArray = fitsErrImage.get(); Array dataArray = pTempImage->get(); Array fitsMask = fitsErrImage.getMask(); Array dataMask = pTempImage->getMask(); CoordinateSystem fitsCS = fitsErrImage.coordinates(); CoordinateSystem dataCS = pTempImage->coordinates(); delete pTempImage; AlwaysAssert(allNear(dataArray, dataMask, fitsArray, fitsMask), AipsError); AlwaysAssert(fitsCS.near(dataCS), AipsError); if (verbose) cerr << "Compared data, mask as well as coordinate systems." << endl; // Test Clone // Get the data and compare the array, masks and coords ImageInterface* pFitsImage = fitsErrImage.cloneII(); Array fitsArray2 = pFitsImage->get(); Array fitsMask2 = pFitsImage->getMask(); CoordinateSystem fitsCS2 = pFitsImage->coordinates(); delete pFitsImage; if (verbose) cerr << "Checked the clone operator." << endl; AlwaysAssert(allNear(dataArray, dataMask, fitsArray2, fitsMask2), AipsError); AlwaysAssert(fitsCS2.near(dataCS), AipsError); if (verbose) cerr << "Compared cloned data, mask as well as coordinate systems." << endl; // Test copy FITSErrorImage fitsErrImage2 = FITSErrorImage(fitsErrImage); if (verbose) cerr << "Checked the copy constructor." << endl; AlwaysAssert(fitsErrImage2.errorType()==defErrType, AipsError); if (verbose) cerr << "Checked for ErrorType in copied instance." << endl; // Get the data and compare the array, masks and coords Array fitsArray3 = fitsErrImage2.get(); Array fitsMask3 = fitsErrImage2.getMask(); CoordinateSystem fitsCS3 = fitsErrImage2.coordinates(); AlwaysAssert(allNear(dataArray, dataMask, fitsArray3, fitsMask3), AipsError); AlwaysAssert(fitsCS2.near(dataCS), AipsError); if (verbose) cerr << "Compared copied data, mask and coordinate systems." << endl; // Test assignment fitsErrImage2 = fitsErrImage; if (verbose) cerr << "Checked the assignment operator." << endl; AlwaysAssert(fitsErrImage2.errorType()==defErrType, AipsError); if (verbose) cerr << "Checked for ErrorType in copied instance." << endl; // Get the data and compare the array, masks and coords fitsArray3 = fitsErrImage2.get(); fitsMask3 = fitsErrImage2.getMask(); fitsCS3 = fitsErrImage2.coordinates(); AlwaysAssert(allNear(dataArray, dataMask, fitsArray3, fitsMask3), AipsError); AlwaysAssert(fitsCS2.near(dataCS), AipsError); if (verbose) cerr << "Compared assigned data, mask and coordinate systems." << endl; // // Open FITSErrorImage as a RMSE error image // fitsErrImage2 = FITSErrorImage(in, 0, hdunum, FITSErrorImage::RMSE); fitsErrImage2.tempClose(); if (verbose) cerr << "Opened a RMSE error image." << endl; // Check the image type and the error type AlwaysAssert(fitsErrImage2.imageType()=="FITSErrorImage", AipsError); AlwaysAssert(fitsErrImage2.errorType()==FITSErrorImage::RMSE, AipsError); if (verbose) cerr << "Checked type and error type" << endl; // Get the data and compare the array, masks and coords fitsArray3 = fitsErrImage2.get(); fitsMask3 = fitsErrImage2.getMask(); fitsCS3 = fitsErrImage2.coordinates(); Array tmpData = dataArray*dataArray; AlwaysAssert(allNear(tmpData, dataMask, fitsArray3, fitsMask3), AipsError); AlwaysAssert(fitsCS2.near(dataCS), AipsError); if (verbose) cerr << "Compared data and coordinate systems in RMSE image." << endl; // Print some data and mask values if desired fitsErrImage2.tempClose(); if (print) printArray(fitsErrImage2, size); // // Open FITSErrorImage as a INVMSE (inverse mean squared error) error image // fitsErrImage2 = FITSErrorImage(in, 0, hdunum, FITSErrorImage::INVMSE); fitsErrImage2.tempClose(); if (verbose) cerr << "Opened a INVMSE error image." << endl; // Check the image type and the error type AlwaysAssert(fitsErrImage2.imageType()=="FITSErrorImage", AipsError); AlwaysAssert(fitsErrImage2.errorType()==FITSErrorImage::INVMSE, AipsError); if (verbose) cerr << "Checked type and error type" << endl; // Get the data and compare the array, masks and coords fitsArray3 = fitsErrImage2.get(); fitsMask3 = fitsErrImage2.getMask(); fitsCS3 = fitsErrImage2.coordinates(); tmpData = (Float)1.0/dataArray; // somehow, the (Float) is essential cleanZero(dataArray, dataMask); AlwaysAssert(allNear(tmpData, dataMask, fitsArray3, fitsMask3), AipsError); AlwaysAssert(fitsCS2.near(dataCS), AipsError); if (verbose) cerr << "Compared data and coordinate systems in INVMSE image." << endl; // Print some data and mask values if desired fitsErrImage2.tempClose(); if (print) printArray(fitsErrImage2, size); // // Open FITSErrorImage as a INVRMSE (inverse root mean squared) error image // fitsErrImage2 = FITSErrorImage(in, 0, hdunum, FITSErrorImage::INVRMSE); fitsErrImage2.tempClose(); if (verbose) cerr << "Opened an INVRMSE error image." << endl; // Check the image type and the error type AlwaysAssert(fitsErrImage2.imageType()=="FITSErrorImage", AipsError); AlwaysAssert(fitsErrImage2.errorType()==FITSErrorImage::INVRMSE, AipsError); if (verbose) cerr << "Checked type and error type" << endl; // Get the data and compare the array, masks and coords fitsArray3 = fitsErrImage2.get(); fitsMask3 = fitsErrImage2.getMask(); fitsCS3 = fitsErrImage2.coordinates(); tmpData = (Float)1.0/(dataArray*dataArray); // somehow, the (Float) is essential AlwaysAssert(allNear(tmpData, dataMask, fitsArray3, fitsMask3), AipsError); AlwaysAssert(fitsCS2.near(dataCS), AipsError); if (verbose) cerr << "Compared data and coordinate systems in an INVRMSE image." << endl; // Print some data and mask values if desired fitsErrImage2.tempClose(); if (print) printArray(fitsErrImage2, size); cerr << "ok " << endl; } catch (std::exception& x) { cerr << "aipserror: error " << x.what() << endl; return 1; } return 0; } Bool allNear (const Array& data, const Array& dataMask, const Array& fits, const Array& fitsMask, Float tol) { Bool deletePtrData, deletePtrDataMask, deletePtrFITS, deletePtrFITSMask; const Float* pData = data.getStorage(deletePtrData); const Float* pFITS = fits.getStorage(deletePtrFITS); const Bool* pDataMask = dataMask.getStorage(deletePtrDataMask); const Bool* pFITSMask = fitsMask.getStorage(deletePtrFITSMask); // for (uInt i=0; i& data, Array& dataMask) { Bool deletePtrData, deletePtrDataMask; const Float* pData = data.getStorage(deletePtrData); Bool* pDataMask = dataMask.getStorage(deletePtrDataMask); // for (uInt i=0; i size) shape(i) = size; } cerr << "Data = " << fitsErrImage.getSlice(start, shape) << endl; cerr << "Mask = " << fitsErrImage.getMaskSlice(start, shape) << endl; } casacore-3.7.1/images/Images/test/tFITSExtImage.cc000066400000000000000000000151531476623553700216460ustar00rootroot00000000000000//# tFITSImage.cc: test the FITSImage class //# Copyright (C) 1994,1995,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Bool allNear (const Array& data, const Array& dataMask, const Array& fits, const Array& fitsMask, Float tol=1.0e-5); int main (int argc, const char* argv[]) { try { LogIO os(LogOrigin("tFITSExtImage", "main()", WHERE)); // Get inputs Input inputs(1); inputs.create("in", "", "Input FITS file"); inputs.create("hdunum", "0", "HDU number"); inputs.create("print", "F", "Print some data"); inputs.create("size", "5", "Size to print"); // inputs.readArguments(argc, argv); String in = inputs.getString("in"); uInt hdunum = (uInt)inputs.getInt("hdunum"); const Bool print = inputs.getBool("print"); const Int size = inputs.getInt("size"); // Give a default image and extension if (in.empty()) { in = "mexinputtest.fits"; } Path p(in); if (!hdunum) hdunum = 3; // Open FITSImage FITSImage fitsImage(in, 0, hdunum); fitsImage.tempClose(); AlwaysAssert(fitsImage.imageType()=="FITSImage", AipsError); Unit unit("Jy/beam"); AlwaysAssert(fitsImage.setUnits(unit), AipsError); AlwaysAssert(fitsImage.units().getName()=="Jy/beam", AipsError); Record rec; rec.define("field1", 0.0); rec.define("field2", "doggies"); AlwaysAssert(fitsImage.setMiscInfo(rec), AipsError); fitsImage.reopen(); Record rec2 = fitsImage.miscInfo(); AlwaysAssert(rec.isDefined("field1"), AipsError); AlwaysAssert(rec.isDefined("field2"), AipsError); AlwaysAssert(rec.asFloat("field1")==0.0, AipsError); AlwaysAssert(rec.asString("field2")=="doggies", AipsError); AlwaysAssert(fitsImage.hasPixelMask() == fitsImage.isMasked(), AipsError); if (fitsImage.hasPixelMask()) { Lattice& pMask = fitsImage.pixelMask(); AlwaysAssert(pMask.shape()==fitsImage.shape(), AipsError); } AlwaysAssert(fitsImage.getRegionPtr()==0, AipsError); AlwaysAssert(fitsImage.isWritable()==False, AipsError); AlwaysAssert(fitsImage.name(False)==p.absoluteName(),AipsError); AlwaysAssert(fitsImage.ok(), AipsError); // fitsImage.tempClose(); if (print) { IPosition start (fitsImage.ndim(),0); IPosition shape(fitsImage.shape()); for (uInt i=0; i size) shape(i) = size; } cerr << "Data = " << fitsImage.getSlice(start, shape) << endl; cerr << "Mask = " << fitsImage.getMaskSlice(start, shape) << endl; } // Convert from FITS as a comparison String error; ImageInterface* pTempImage = 0; String imageName; if (!ImageFITSConverter::FITSToImage(pTempImage, error, imageName, in, 0, hdunum)) { os << error << LogIO::EXCEPTION; } // Array fitsArray = fitsImage.get(); Array dataArray = pTempImage->get(); Array fitsMask = fitsImage.getMask(); Array dataMask = pTempImage->getMask(); CoordinateSystem fitsCS = fitsImage.coordinates(); CoordinateSystem dataCS = pTempImage->coordinates(); delete pTempImage; // AlwaysAssert(fitsCS.near(dataCS), AipsError); AlwaysAssert(allNear(dataArray, dataMask, fitsArray, fitsMask), AipsError); // Test Clone ImageInterface* pFitsImage = fitsImage.cloneII(); Array fitsArray2 = pFitsImage->get(); Array fitsMask2 = pFitsImage->getMask(); CoordinateSystem fitsCS2 = pFitsImage->coordinates(); delete pFitsImage; // AlwaysAssert(allNear(dataArray, dataMask, fitsArray2, fitsMask2), AipsError); AlwaysAssert(fitsCS2.near(dataCS), AipsError); // cerr << "ok " << endl; } catch (std::exception& x) { cerr << "aipserror: error " << x.what() << endl; return 1; } return 0; } Bool allNear (const Array& data, const Array& dataMask, const Array& fits, const Array& fitsMask, Float tol) { Bool deletePtrData, deletePtrDataMask, deletePtrFITS, deletePtrFITSMask; const Float* pData = data.getStorage(deletePtrData); const Float* pFITS = fits.getStorage(deletePtrFITS); const Bool* pDataMask = dataMask.getStorage(deletePtrDataMask); const Bool* pFITSMask = fitsMask.getStorage(deletePtrFITSMask); // for (uInt i=0; i #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Bool allNear (const Array& data, const Array& dataMask, const Array& fits, const Array& fitsMask, Float tol=1.0e-5); int main (int argc, const char* argv[]) { try { LogIO os(LogOrigin("tFITSExtImageII", "main()", WHERE)); // Get inputs Input inputs(1); inputs.create("in", "", "Input FITS file"); inputs.create("hdunum", "0", "HDU number"); inputs.create("print", "F", "Print some data"); inputs.create("size", "5", "Size to print"); // inputs.readArguments(argc, argv); String in = inputs.getString("in"); uInt hdunum = 0; const Bool print = inputs.getBool("print"); const Int size = inputs.getInt("size"); // Give a default image and extension if (in.empty()) { in = "mexinputtest.fits[sci,2]"; } Path p(FITSImage::get_fitsname(in)); // Get the HDU index hdunum = FITSImage::get_hdunum(in); // Open FITSImage FITSImage fitsImage(in); fitsImage.tempClose(); AlwaysAssert(fitsImage.imageType()=="FITSImage", AipsError); Unit unit("Jy/beam"); AlwaysAssert(fitsImage.setUnits(unit), AipsError); AlwaysAssert(fitsImage.units().getName()=="Jy/beam", AipsError); Record rec; rec.define("field1", 0.0); rec.define("field2", "doggies"); AlwaysAssert(fitsImage.setMiscInfo(rec), AipsError); fitsImage.reopen(); Record rec2 = fitsImage.miscInfo(); AlwaysAssert(rec.isDefined("field1"), AipsError); AlwaysAssert(rec.isDefined("field2"), AipsError); AlwaysAssert(rec.asFloat("field1")==0.0, AipsError); AlwaysAssert(rec.asString("field2")=="doggies", AipsError); AlwaysAssert(fitsImage.hasPixelMask() == fitsImage.isMasked(), AipsError); if (fitsImage.hasPixelMask()) { Lattice& pMask = fitsImage.pixelMask(); AlwaysAssert(pMask.shape()==fitsImage.shape(), AipsError); } AlwaysAssert(fitsImage.getRegionPtr()==0, AipsError); AlwaysAssert(fitsImage.isWritable()==False, AipsError); AlwaysAssert(fitsImage.name(False)==p.absoluteName(),AipsError); AlwaysAssert(fitsImage.ok(), AipsError); // fitsImage.tempClose(); if (print) { IPosition start (fitsImage.ndim(),0); IPosition shape(fitsImage.shape()); for (uInt i=0; i size) shape(i) = size; } cerr << "Data = " << fitsImage.getSlice(start, shape) << endl; cerr << "Mask = " << fitsImage.getMaskSlice(start, shape) << endl; } // Convert from FITS as a comparison String error; ImageInterface* pTempImage = 0; String imageName; if (!ImageFITSConverter::FITSToImage(pTempImage, error, imageName, FITSImage::get_fitsname(in), 0, hdunum)) { os << error << LogIO::EXCEPTION; } // Array fitsArray = fitsImage.get(); Array dataArray = pTempImage->get(); Array fitsMask = fitsImage.getMask(); Array dataMask = pTempImage->getMask(); CoordinateSystem fitsCS = fitsImage.coordinates(); CoordinateSystem dataCS = pTempImage->coordinates(); delete pTempImage; // AlwaysAssert(fitsCS.near(dataCS), AipsError); AlwaysAssert(allNear(dataArray, dataMask, fitsArray, fitsMask), AipsError); // Test Clone ImageInterface* pFitsImage = fitsImage.cloneII(); Array fitsArray2 = pFitsImage->get(); Array fitsMask2 = pFitsImage->getMask(); CoordinateSystem fitsCS2 = pFitsImage->coordinates(); delete pFitsImage; // AlwaysAssert(allNear(dataArray, dataMask, fitsArray2, fitsMask2), AipsError); AlwaysAssert(fitsCS2.near(dataCS), AipsError); // cerr << "ok " << endl; } catch (std::exception& x) { cerr << "aipserror: error " << x.what() << endl; return 1; } return 0; } Bool allNear (const Array& data, const Array& dataMask, const Array& fits, const Array& fitsMask, Float tol) { Bool deletePtrData, deletePtrDataMask, deletePtrFITS, deletePtrFITSMask; const Float* pData = data.getStorage(deletePtrData); const Float* pFITS = fits.getStorage(deletePtrFITS); const Bool* pDataMask = dataMask.getStorage(deletePtrDataMask); const Bool* pFITSMask = fitsMask.getStorage(deletePtrFITSMask); // for (uInt i=0; i #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Bool allNear (const Array& data, const Array& dataMask, const Array& fits, const Array& fitsMask, Float tol=1.0e-5, Float abstol=-1.); int main (int argc, const char* argv[]) { try { LogIO os(LogOrigin("tFITSImage", "main()", WHERE)); // Get inputs Input inputs(1); inputs.create("in", "", "Input FITS file"); inputs.create("hdunum", "0", "HDU number"); inputs.create("print", "F", "Print some data"); inputs.create("size", "5", "Size to print"); // inputs.readArguments(argc, argv); String in = inputs.getString("in"); const uInt hdunum = (uInt)inputs.getInt("hdunum"); const Bool print = inputs.getBool("print"); const Int size = inputs.getInt("size"); // if (in.empty()) { in = "imagetestimage.fits"; } Path p(in); // Open FITSImage FITSImage fitsImage(in, 0, hdunum); fitsImage.tempClose(); AlwaysAssert(fitsImage.imageType()=="FITSImage", AipsError); Unit unit("Jy/beam"); AlwaysAssert(fitsImage.setUnits(unit), AipsError); AlwaysAssert(fitsImage.units().getName()=="Jy/beam", AipsError); Record rec; rec.define("field1", 0.0); rec.define("field2", "doggies"); AlwaysAssert(fitsImage.setMiscInfo(rec), AipsError); fitsImage.reopen(); Record rec2 = fitsImage.miscInfo(); AlwaysAssert(rec.isDefined("field1"), AipsError); AlwaysAssert(rec.isDefined("field2"), AipsError); AlwaysAssert(rec.asFloat("field1")==0.0, AipsError); AlwaysAssert(rec.asString("field2")=="doggies", AipsError); AlwaysAssert(fitsImage.hasPixelMask() == fitsImage.isMasked(), AipsError); if (fitsImage.hasPixelMask()) { Lattice& pMask = fitsImage.pixelMask(); AlwaysAssert(pMask.shape()==fitsImage.shape(), AipsError); } AlwaysAssert(fitsImage.getRegionPtr()==0, AipsError); AlwaysAssert(fitsImage.isWritable()==False, AipsError); AlwaysAssert(fitsImage.name(False)==p.absoluteName(),AipsError); AlwaysAssert(fitsImage.ok(), AipsError); // fitsImage.tempClose(); if (print) { IPosition start (fitsImage.ndim(),0); IPosition shape(fitsImage.shape()); for (uInt i=0; i size) shape(i) = size; } cerr << "Data = " << fitsImage.getSlice(start, shape) << endl; cerr << "Mask = " << fitsImage.getMaskSlice(start, shape) << endl; } // Convert from FITS as a comparison String error; ImageInterface* pTempImage = 0; String imageName; if (!ImageFITSConverter::FITSToImage(pTempImage, error, imageName, in, 0)) { os << error << LogIO::EXCEPTION; } // Array fitsArray = fitsImage.get(); Array dataArray = pTempImage->get(); Array fitsMask = fitsImage.getMask(); Array dataMask = pTempImage->getMask(); CoordinateSystem fitsCS = fitsImage.coordinates(); CoordinateSystem dataCS = pTempImage->coordinates(); delete pTempImage; // AlwaysAssert(allNear(dataArray, dataMask, fitsArray, fitsMask), AipsError); AlwaysAssert(fitsCS.near(dataCS), AipsError); // Test Clone ImageInterface* pFitsImage = fitsImage.cloneII(); Array fitsArray2 = pFitsImage->get(); Array fitsMask2 = pFitsImage->getMask(); CoordinateSystem fitsCS2 = pFitsImage->coordinates(); delete pFitsImage; // AlwaysAssert(allNear(dataArray, dataMask, fitsArray2, fitsMask2), AipsError); AlwaysAssert(fitsCS2.near(dataCS), AipsError); // // Convert the header to FITS. ImageFITSHeaderInfo fhi; AlwaysAssertExit (ImageFITSConverter::ImageHeaderToFITS (error, fhi, fitsImage)); cout << fhi.kw.toString() << endl; cerr << "ok " << endl; String file = "imagetestimage2.fits"; ImageFITSConverter::ImageToFITS(error, fitsImage, file, 64, True, True, 16, 1.0, -1.0, True); ImageInterface* pLoadImage; ImageFITSConverter::FITSToImage(pLoadImage, error, imageName, file); AlwaysAssert(allNear(pLoadImage->get(), pLoadImage->getMask(), fitsArray2, fitsMask2, 0.0, 0.001), AipsError); delete pLoadImage; } catch (std::exception& x) { cout << "aipserror: error " << x.what() << endl; return 1; } return 0; } Bool allNear (const Array& data, const Array& dataMask, const Array& fits, const Array& fitsMask, Float tol, Float abstol) { Bool deletePtrData, deletePtrDataMask, deletePtrFITS, deletePtrFITSMask; const Float* pData = data.getStorage(deletePtrData); const Float* pFITS = fits.getStorage(deletePtrFITS); const Bool* pDataMask = dataMask.getStorage(deletePtrDataMask); const Bool* pFITSMask = fitsMask.getStorage(deletePtrFITSMask); // for (uInt i=0; i= 0 && !nearAbs(pData[i], pFITS[i], abstol))) { cerr << "data differ, tol = " << tol << endl; cerr << pData[i] << ", " << pFITS[i] << endl; return False; } } } // data.freeStorage(pData, deletePtrData); dataMask.freeStorage(pDataMask, deletePtrDataMask); fits.freeStorage(pFITS, deletePtrFITS); fitsMask.freeStorage(pFITSMask, deletePtrFITSMask); return True; } casacore-3.7.1/images/Images/test/tFITSImage.out000066400000000000000000000066331476623553700214120ustar00rootroot00000000000000SIMPLE = T /Standard FITS BITPIX = -32 /Floating point (32 bit) NAXIS = 2 NAXIS1 = 113 NAXIS2 = 76 EXTEND = T BSCALE = 1.000000000000E+00 /PHYSICAL = PIXEL*BSCALE + BZERO BZERO = 0.000000000000E+00 BMAJ = 1.486111200000E-02 BMIN = 9.499999700000E-03 BPA = 6.000000000000E+00 BTYPE = 'Intensity' OBJECT = ' ' BUNIT = 'Jy/beam ' /Brightness (pixel) unit EQUINOX = 2.000000000000E+03 RADESYS = 'FK5 ' LONPOLE = 1.800000000000E+02 LATPOLE = 0.000000000000E+00 PC1_1 = 1.000000000000E+00 PC2_1 = 0.000000000000E+00 PC1_2 = -0.000000000000E+00 PC2_2 = 1.000000000000E+00 CTYPE1 = 'RA---SIN' CRVAL1 = 0.000000000000E+00 CDELT1 = -2.222222308810E-03 CRPIX1 = 5.600000000000E+01 CUNIT1 = 'deg ' PV2_1 = 0.000000000000E+00 CTYPE2 = 'DEC--SIN' CRVAL2 = 0.000000000000E+00 CDELT2 = 3.333333234031E-03 CRPIX2 = 3.800000000000E+01 CUNIT2 = 'deg ' PV2_2 = 0.000000000000E+00 FIELD1 = 0.000000000000E+00 FIELD2 = 'doggies ' DATE =>>> '2015-12-01T13:03:19.198730' /Date FITS file was written<<< TIMESYS = 'UTC ' /Time system for HDU ORIGIN = 'casacore->>>trunk<<<' HISTORY File modified by user 'dbarnes' with fv on 1999-07-28T14:19:41 HISTORY File modified by user 'dbarnes' with fv on 1999-07-28T14:21:43 END casacore-3.7.1/images/Images/test/tFITSImage.run000066400000000000000000000003351476623553700214000ustar00rootroot00000000000000#!/bin/sh # Make output independent of date and version. $casa_checktool ./tFITSImage | fold -w 80 | sed 's/DATE =\(.*written\) /DATE =>>>\1<<>>\1<<<'/" casacore-3.7.1/images/Images/test/tFITSImgParser.cc000066400000000000000000000277061476623553700220430ustar00rootroot00000000000000//# tFITSImgParser.cc: test the FITSImgParser class //# Copyright (C) 1994,1995,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include int main (int argc, const char* argv[]) { try { LogIO os(LogOrigin("tFITSImgParser", "main()", WHERE)); Bool isdefaultinput; // Get inputs Input inputs(1); inputs.create("in", "", "Input FITS file"); inputs.readArguments(argc, argv); String in1 = inputs.getString("in"); String in2; if (in1.empty()) { in1 = "mexinputtest.fits"; in2 = "qualityimage.fits"; isdefaultinput=True; } else { isdefaultinput = False; } // create the parser FITSImgParser fitsImg(in1); // process the default image if (isdefaultinput){ // check the name information AlwaysAssert(fitsImg.fitsname(True) == String("mexinputtest.fits"), AipsError); // check the number of extensions AlwaysAssert(fitsImg.get_numhdu() == 7, AipsError); // check the first extension with data AlwaysAssert(fitsImg.get_firstdata_index() == 1, AipsError); // check the index of "mexinputtest.fits[sci, 1]" FITSExtInfo extinfo("mexinputtest.fits", 0, "SCI", 1, True); AlwaysAssert(fitsImg.get_index(extinfo) == 1, AipsError); AlwaysAssert(fitsImg.find_extension("SCI", 1) == 1, AipsError); // check the index of "mexinputtest.fits[DQ]" extinfo = FITSExtInfo("mexinputtest.fits", 0, "DQ", -1, True); AlwaysAssert(fitsImg.get_index(extinfo) == 3, AipsError); AlwaysAssert(fitsImg.find_extension("DQ") == 3, AipsError); // check the index of "mexinputtest.fits[dq]" // capitalization should not matter extinfo = FITSExtInfo("mexinputtest.fits", 0, "dq", -1, True); AlwaysAssert(fitsImg.get_index(extinfo) == 3, AipsError); AlwaysAssert(fitsImg.find_extension("dq") == 3, AipsError); // check the non-existing index of "mexinputtest.fits[dq, 3]" extinfo = FITSExtInfo("mexinputtest.fits", 0, "dq", 3, True); AlwaysAssert(fitsImg.get_index(extinfo) == -1, AipsError); AlwaysAssert(fitsImg.find_extension("DQ", 3) == -1, AipsError); // check the non-existing index of "mexinputtest.fits[dq, 3]" extinfo = FITSExtInfo("mexinputtest.fits", 0, "dq", 3, True); AlwaysAssert(fitsImg.get_index(extinfo) == -1, AipsError); AlwaysAssert(fitsImg.find_extension("dq", 3) == -1, AipsError); // open the second image; FITSImgParser fitsImg2(in2); // check the name information AlwaysAssert(fitsImg2.fitsname(True) == in2, AipsError); // check the number of extensions AlwaysAssert(fitsImg2.get_numhdu() == 9, AipsError); // check the first extension with data AlwaysAssert(fitsImg2.get_firstdata_index() == 1, AipsError); // check the index of "qualityimage.fits[IFU1.SCI]" AlwaysAssert(fitsImg2.find_extension("IFU1.SCI") == 1, AipsError); // check the index of "qualityimage.fits[IFU1.DQ]" AlwaysAssert(fitsImg2.find_extension("IFU1.DQ") == 3, AipsError); // check the index of "qualityimage.fits[IFU1.dq]" // capitalization should not matter AlwaysAssert(fitsImg2.find_extension("IFU1.dq") == 3, AipsError); // check the non-existing index of "qualityimage.fits[dq, 3]" AlwaysAssert(fitsImg2.find_extension("dq", 3) == -1, AipsError); // check the non-existing index of "qualityimage.fits[dq]" AlwaysAssert(fitsImg2.find_extension("dq") == -1, AipsError); // check whether there exists a quality image AlwaysAssert(fitsImg2.has_qualityimg(), AipsError); // including mask image use this: //AlwaysAssert(fitsImg2.is_qualityimg("[IFU1.SCI,IFU1.ERR,IFU1.DQ]"), AipsError) // this should be a quality image AlwaysAssert(fitsImg2.is_qualityimg("[IFU1.SCI,IFU1.ERR]"), AipsError) // capitalization is not important AlwaysAssert(fitsImg2.is_qualityimg("[ifu1.sci,IFU1.ERR]"), AipsError) // capitalization is not important AlwaysAssert(fitsImg2.is_qualityimg("[ifu1.sci,ifu1.err]"), AipsError) // using not all extensions should not matter AlwaysAssert(fitsImg2.is_qualityimg("[IFU1.SCI,IFU1.ERR]"), AipsError) // including mask image use this: //AlwaysAssert(fitsImg2.is_qualityimg("[IFU2.SCI,IFU2.ERR,IFU2.DQ]"), AipsError) // check the next quality image // note in the data extension, the keywords pointing // to the error extension are filled in small letters AlwaysAssert(fitsImg2.is_qualityimg("[IFU2.SCI,IFU2.ERR]"), AipsError) // including mask image test this: // neglect braces, add whitespaces // AlwaysAssert(fitsImg2.is_qualityimg(" IFU3.SCI, IFU3.DQ "), AipsError) // the extension order does not matter AlwaysAssert(fitsImg2.is_qualityimg("[IFU1.ERR, IFU1.SCI]"), AipsError) // make sure the two string representations are NOT equal AlwaysAssert(fitsImg2.get_extlist_string(String(""), "", "", False) != fitsImg2.get_extlist_string(String(""), "", "", True), AipsError); /* // this is the full call including a mask image // retrieve all information on an extension expression Int data_HDU, error_HDU, mask_HDU, mask_value; String error_type, mask_type; fitsImg2.get_quality_data("[IFU3.SCI, IFU3.DQ]", data_HDU, error_HDU, error_type, mask_HDU, mask_type, mask_value); // check all return values AlwaysAssert(data_HDU==7, AipsError); AlwaysAssert(error_HDU==-1, AipsError); AlwaysAssert(error_type=="", AipsError); AlwaysAssert(mask_HDU==8, AipsError); AlwaysAssert(mask_type=="MASKONE", AipsError); AlwaysAssert(mask_value==0, AipsError); */ // retrieve all information on an extension expression Int data_HDU, error_HDU, mask_HDU, mask_value; String error_type, mask_type; fitsImg2.get_quality_data("[IFU2.SCI, IFU2.ERR]", data_HDU, error_HDU, error_type, mask_HDU, mask_type, mask_value); // check all return values AlwaysAssert(data_HDU==4, AipsError); AlwaysAssert(error_HDU==5, AipsError); AlwaysAssert(error_type=="RMSE", AipsError); AlwaysAssert(mask_HDU==-1, AipsError); AlwaysAssert(mask_type=="", AipsError); AlwaysAssert(mask_value==0, AipsError); /* // this is the full call including a mask image fitsImg2.get_quality_data("[IFU1.SCI,IFU1.ERR,IFU1.DQ]", data_HDU, error_HDU, error_type, mask_HDU, mask_type, mask_value); // check all return values AlwaysAssert(data_HDU==1, AipsError); AlwaysAssert(error_HDU==2, AipsError); AlwaysAssert(error_type=="MSE", AipsError); AlwaysAssert(mask_HDU==3, AipsError); AlwaysAssert(mask_type=="FLAG16BIT", AipsError); AlwaysAssert(mask_value==16383, AipsError); */ // retrieve all information on an extension expression fitsImg2.get_quality_data("[IFU1.SCI,IFU1.ERR]", data_HDU, error_HDU, error_type, mask_HDU, mask_type, mask_value); // check all return values AlwaysAssert(data_HDU==1, AipsError); AlwaysAssert(error_HDU==2, AipsError); AlwaysAssert(error_type=="MSE", AipsError); AlwaysAssert(mask_HDU==-1, AipsError); AlwaysAssert(mask_type=="", AipsError); AlwaysAssert(mask_value==0, AipsError); // retrieve all information on an extension expression fitsImg2.get_quality_data("[SCIEXT]", data_HDU, error_HDU, error_type, mask_HDU, mask_type, mask_value); // check all return values; // they must be the default ones AlwaysAssert(data_HDU<0, AipsError); AlwaysAssert(error_HDU < 0, AipsError); AlwaysAssert(error_type.size()==0, AipsError); AlwaysAssert(mask_HDU<0, AipsError); AlwaysAssert(mask_type.size()==0, AipsError); AlwaysAssert(mask_value==0, AipsError); // test the copy constructor FITSImgParser fitsImg3(fitsImg2); // test the number of extensions AlwaysAssert(fitsImg3.get_numhdu()==fitsImg2.get_numhdu(), AipsError); /* this would be the full call, including mask images // retrieve all information on an extension expression fitsImg3.get_quality_data("[IFU1.SCI,IFU1.ERR,IFU1.DQ]", data_HDU, error_HDU, error_type, mask_HDU, mask_type, mask_value); // check all return values AlwaysAssert(data_HDU==1, AipsError); AlwaysAssert(error_HDU==2, AipsError); AlwaysAssert(error_type=="MSE", AipsError); AlwaysAssert(mask_HDU==3, AipsError); AlwaysAssert(mask_type=="FLAG16BIT", AipsError); AlwaysAssert(mask_value==16383, AipsError); */ // retrieve all information on an extension expression fitsImg3.get_quality_data("[IFU1.SCI,IFU1.ERR,IFU1.DQ]", data_HDU, error_HDU, error_type, mask_HDU, mask_type, mask_value); // check all return values AlwaysAssert(data_HDU==1, AipsError); AlwaysAssert(error_HDU==2, AipsError); AlwaysAssert(error_type=="MSE", AipsError); AlwaysAssert(mask_HDU==-1, AipsError); AlwaysAssert(mask_type=="", AipsError); AlwaysAssert(mask_value==0, AipsError); // test the assignment FITSImgParser fitsImg4=fitsImg2; // test the number of extensions AlwaysAssert(fitsImg4.get_numhdu()==fitsImg2.get_numhdu(), AipsError); /* this would be the full call, including mask images // retrieve all information on an extension expression fitsImg4.get_quality_data("[IFU1.SCI,IFU1.ERR,IFU1.DQ]", data_HDU, error_HDU, error_type, mask_HDU, mask_type, mask_value); // check all return values AlwaysAssert(data_HDU==1, AipsError); AlwaysAssert(error_HDU==2, AipsError); AlwaysAssert(error_type=="MSE", AipsError); AlwaysAssert(mask_HDU==3, AipsError); AlwaysAssert(mask_type=="FLAG16BIT", AipsError); AlwaysAssert(mask_value==16383, AipsError); */ // retrieve all information on an extension expression fitsImg4.get_quality_data("[IFU1.SCI,IFU1.ERR,IFU1.DQ]", data_HDU, error_HDU, error_type, mask_HDU, mask_type, mask_value); // check all return values AlwaysAssert(data_HDU==1, AipsError); AlwaysAssert(error_HDU==2, AipsError); AlwaysAssert(error_type=="MSE", AipsError); AlwaysAssert(mask_HDU==-1, AipsError); AlwaysAssert(mask_type=="", AipsError); AlwaysAssert(mask_value==0, AipsError); } else { cerr << "Fits file: " << fitsImg.fitsname(True) << "\n"; cerr << "Number of extensions: " << fitsImg.get_numhdu() << "\n"; cerr << "First index with data: " << fitsImg.get_firstdata_index() << "\n"; cerr << "File contains quality image: " << fitsImg.has_qualityimg() << "\n"; cerr << "String representation of all extensions with data:\n" << fitsImg.get_extlist_string(String("")) << endl; } } catch (std::exception& x) { cerr << "aipserror: error " << x.what() << endl; return 1; } cout<< "[tFITSImgParser.cc] End of tFITSImgParser.cc."<< endl; cout << "ok " << endl; return 0; } casacore-3.7.1/images/Images/test/tFITSQualityImage.cc000066400000000000000000000571121476623553700225370ustar00rootroot00000000000000//# tFITSQualityImage.cc: test the tFITSQualityImage class //# Copyright (C) 1994,1995,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Bool allNear (const Array& data, const Array& dataMask, const Array& fits, const Array& fitsMask, Float tol=1.0e-5); Bool checkRecFieldString(String &error, const RecordInterface &theRec, const String &theField, const String &theValue); Bool testQualFITSInfo(const TableRecord &dataInfo, const TableRecord &errorInfo, const String &sciHDU, const String &errHDU, const String &errType); Bool testQualImg(FITSQualityImage &fQualImg, const String &in, const uInt &hdu_sci, const uInt &hdu_err, const Bool &print, const Int &size); template void printArray (T array, Int size, String pre="printArray"); int main (int argc, const char* argv[]) { try { LogIO os(LogOrigin("tFITSQualityImage", "main()", WHERE)); // Get inputs Input inputs(1); inputs.create("in_fits", "", "Input FITS file"); inputs.create("in_ext", "", "Input FITS extension expression"); inputs.create("hdu_sci", "1", "HDU number"); inputs.create("hdu_err", "2", "HDU number"); inputs.create("print", "F", "Print some data"); inputs.create("size", "5", "Size to print"); // inputs.readArguments(argc, argv); String in_fits = inputs.getString("in_fits"); String in_ext = inputs.getString("in_ext"); const uInt hdu_sci = inputs.getInt("hdu_sci"); const uInt hdu_err = inputs.getInt("hdu_err"); const Bool print = inputs.getBool("print"); const Int size = inputs.getInt("size"); // if (in_fits.empty()) { in_fits = "qualityimage.fits"; } Path p(in_fits); if (in_ext.empty()) { // for the entire format including ask images use this: //in_ext = "qualityimage.fits[IFU1.SCI,IFU1.ERR,IFU1.DQ]"; // for data and error extension only use this: in_ext = "qualityimage.fits[IFU1.SCI,IFU1.ERR]"; } Path pII(FITSImage::get_fitsname(in_ext)); // create the quality image with the extension indices FITSQualityImage fitsQI(in_fits, hdu_sci, hdu_err); { // check the file names if (fitsQI.name(False) != p.absoluteName()){ String msg = String("The names differ: " + fitsQI.name(False) + " <<>> " + p.absoluteName()); throw(AipsError(msg)); } } { // create the default header information for data and error TableRecord dataInfo, errorInfo, miscInfo; String sciHDU("DATA"), errHDU("ERROR"), errType("MSE"); String error; // get the default values for data-info and error-info if (!FITSQualityImage::qualFITSInfo(error, dataInfo, errorInfo, miscInfo)){ String msg = String("General problem to define the miscInfo for the data and error extension!"); throw(AipsError(msg)); } // check the data- and error-header information testQualFITSInfo(dataInfo, errorInfo, sciHDU, errHDU, errType); } { // create the header information for data and error // with dedicated extension names TableRecord dataInfo, errorInfo, miscInfo; String sciHDU("IFU1.SCI"), errHDU("IFU1.ERR"), errType("INVMSE"); String error; miscInfo.define("sciextname", sciHDU); miscInfo.define("errextname", errHDU); miscInfo.define("hduclas3", errType); // get the data-info and error-info default values if (!FITSQualityImage::qualFITSInfo(error, dataInfo, errorInfo, miscInfo)){ String msg = String("General problem to define the miscInfo for the data and error extension: "+error); throw(AipsError(msg)); } // check the data- and error-header information testQualFITSInfo(dataInfo, errorInfo, sciHDU, errHDU, errType); } { // try to create header information which includes // a non-existing error type. This has to fail! TableRecord dataInfo, errorInfo, miscInfo; String sciHDU("IFU1.SCI"), errHDU("IFU1.ERR"); String error; // give a non-existing error type String errType("WHATEVER"); miscInfo.define("sciextname", sciHDU); miscInfo.define("errextname", errHDU); miscInfo.define("hduclas3", errType); // this one HAS to bark! if (FITSQualityImage::qualFITSInfo(error, dataInfo, errorInfo, miscInfo)){ String msg = String("The error type: " + errType + " was wrongly accepted"); throw(AipsError(msg)); } } // create the quality image with the extension expression FITSQualityImage fitsQI_II(in_ext); { // check the file names if (fitsQI_II.name(False) != pII.absoluteName()){ String msg = String("The names differ: " + fitsQI_II.name(False) + " <<>> " + pII.absoluteName()); throw(AipsError(msg)); } } // test the quality image testQualImg(fitsQI_II, in_ext, hdu_sci, hdu_err, print, size); // test the FITS info routine //testQualFITSInfo(); cerr << "ok " << endl; } catch (std::exception& x) { cerr << "aipserror: error " << x.what() << endl; return 1; } return 0; } Bool allNear (const Array& data, const Array& dataMask, const Array& fits, const Array& fitsMask, Float tol) { Bool deletePtrData, deletePtrDataMask, deletePtrFITS, deletePtrFITSMask; const Float* pData = data.getStorage(deletePtrData); const Float* pFITS = fits.getStorage(deletePtrFITS); const Bool* pDataMask = dataMask.getStorage(deletePtrDataMask); const Bool* pFITSMask = fitsMask.getStorage(deletePtrFITSMask); // for (uInt i=0; i-1 && theRec.type(theRec.fieldNumber(theField)==TpString))){ error = String("Field "+theField+" does not exits!"); return False; } // check the value of the field theRec.get(String(theField), tmpString); if (tmpString.compare(theValue)){ error = String("Field "+theField+" has NOT the value: "+theValue+ " but: "+tmpString); return False; } return True; } Bool testQualFITSInfo(const TableRecord &dataInfo, const TableRecord &errorInfo, const String &sciHDU, const String &errHDU, const String &errType){ String error; // check the data-info for "HDUCLASS" if (!checkRecFieldString(error, dataInfo, String("hduclass"), String("ESO"))){ throw(AipsError(error)); } // check the data-info for "HDUDOC" if (!checkRecFieldString(error, dataInfo, String("hdudoc"), String("DICD"))){ throw(AipsError(error)); } // check the data-info for "HDUVERS" if (!checkRecFieldString(error, dataInfo, String("hduvers"), String("DICD version 6"))){ throw(AipsError(error)); } // check the data-info for "HDUCLAS1" if (!checkRecFieldString(error, dataInfo, String("hduclas1"), String("IMAGE"))){ throw(AipsError(error)); } // check the data-info for "EXTNAME" if (!checkRecFieldString(error, dataInfo, String("extname"), sciHDU)){ throw(AipsError(error)); } // check the data-info for "HDUTYPE" if (!checkRecFieldString(error, dataInfo, String("hduclas2"), Quality::name(Quality::DATA))){ throw(AipsError(error)); } // check the data-info for "ERRDATA" if (!checkRecFieldString(error, dataInfo, String("errdata"), errHDU)){ throw(AipsError(error)); } // check the error-info for "EXTNAME" if (!checkRecFieldString(error, errorInfo, String("extname"), errHDU)){ throw(AipsError(error)); } // check the error-info for "HDUTYPE" if (!checkRecFieldString(error, errorInfo, String("hduclas2"), Quality::name(Quality::ERROR))){ throw(AipsError(error)); } // check the error-info for "SCIDATA" if (!checkRecFieldString(error, errorInfo, String("scidata"), sciHDU)){ throw(AipsError(error)); } // check the error-info for "ERRTYPE" if (!checkRecFieldString(error, errorInfo, String("hduclas3"), errType)){ throw(AipsError(error)); } return True; } Bool testQualImg(FITSQualityImage &fitsQI, const String &in, const uInt &hdu_sci, const uInt &hdu_err, const Bool &print, const Int &size) { { // make sure the last axis has two pixels uInt ndim = fitsQI.ndim(); IPosition shape = fitsQI.shape(); if (shape(ndim-1)!=2) { String msg = String("Last dimension should be 2 but is: ") + String::toString(shape(ndim-1)); throw(AipsError(msg)); } } { // make sure a quality coordinate axis exists CoordinateSystem cSys = fitsQI.coordinates(); Int qCoord = cSys.findCoordinate(Coordinate::QUALITY); if (qCoord < 0){ String msg = String("The image does not contain a quality coordinate axis!"); throw(AipsError(msg)); } } { // check the image type if (fitsQI.imageType()!="FITSQualityImage"){ String msg = String("The image has wrong type!"); throw(AipsError(msg)); } } { // make sure the object is masked if (!fitsQI.isMasked()){ String msg = String("The object MUST be masked!"); throw(AipsError(msg)); } } { // make sure the object has a pixel mask if (!fitsQI.hasPixelMask()){ String msg = String("The object MUST have a pixel mask!"); throw(AipsError(msg)); } } { // make sure the region pointer returned is 0 if (fitsQI.getRegionPtr()!=0){ String msg = String("The object MUST return a 0 as region pointer!"); throw(AipsError(msg)); } } { // make sure the object is persistent if (!fitsQI.isPersistent()){ String msg = String("The object MUST be persistent!"); throw(AipsError(msg)); } } { // make sure the object is paged if (!fitsQI.isPaged()){ String msg = String("The object MUST be aged!"); throw(AipsError(msg)); } } { // make sure the object is not writable if (fitsQI.isWritable()){ String msg = String("The object MUST NOT be writable!"); throw(AipsError(msg)); } } { // make sure the object is OK if (!fitsQI.ok()){ String msg = String("The object MUST be OK!"); throw(AipsError(msg)); } } { // make sure there is the right science HDU if (fitsQI.whichDataHDU()!=hdu_sci){ String msg = String("The object has the wrong science HDU number!"); throw(AipsError(msg)); } } { // make sure there is the right error HDU if (fitsQI.whichErrorHDU()!=hdu_err){ String msg = String("The object has the wrong error HDU number!"); throw(AipsError(msg)); } } { // get the pointer to the data FITSImage *fromQI = fitsQI.fitsData(); // make sure there is the right science HDU if (fromQI->whichHDU()!=hdu_sci){ String msg = String("The data in the object has the wrong HDU number!"); throw(AipsError(msg)); } } { // get the pointer to the error FITSErrorImage *fromQI = fitsQI.fitsError(); // make sure there is the right science HDU if (fromQI->whichHDU()!=hdu_err){ String msg = String("The error in the object has the wrong HDU number!"); throw(AipsError(msg)); } } // extract the FITS file name String fitsname = FITSImage::get_fitsname(in); // open the data error extensions independently FITSImage fitsDataImg = FITSImage(fitsname, 0, hdu_sci); FITSImage fitsErrorImg = FITSImage(fitsname, 0, hdu_err); { // make sure the quality image and the // extension image have the same units if (fitsQI.units() != fitsDataImg.units()){ String msg = String("Quality image and it data extension must have identical units!"); throw(AipsError(msg)); } } { Array mmData; Array mmMask; // dimension the start and end points // target data and error values IPosition start (fitsQI.ndim(), 0); IPosition end (fitsQI.shape()-1); IPosition stride(fitsQI.ndim(), 1); // get a slicer and get the values and mask Slicer mmSection(start, end, stride, Slicer::endIsLast); fitsQI.doGetSlice(mmData, mmSection); fitsQI.doGetMaskSlice(mmMask, mmSection); if (print){ printArray (mmData,size, "Data = "); printArray (mmMask,size, "Mask = "); } Array fitsDData; Array fitsDMask; Array fitsEData; Array fitsEMask; // dimension the start and end points // for the individual extensions IPosition fStart (fitsQI.ndim()-1, 0); IPosition fStride(fitsQI.ndim()-1, 1); IPosition fEnd (fitsQI.ndim()-1); for (uInt i=0; i mmData; Array mmMask; // dimension the start and end points // target only data values IPosition start (fitsQI.ndim(), 0); IPosition end (fitsQI.shape()-1); IPosition stride(fitsQI.ndim(), 1); end(fitsQI.ndim()-1) = 0; // get a slicer and get the values and mask Slicer mmSection(start, end, stride, Slicer::endIsLast); fitsQI.doGetSlice(mmData, mmSection); fitsQI.doGetMaskSlice(mmMask, mmSection); if (print){ printArray (mmData,size, "DataII = "); printArray (mmMask,size, "MaskII = "); } Array fitsDData; Array fitsDMask; // dimension the start and end points // for the individual extension IPosition fStart (fitsQI.ndim()-1, 0); IPosition fStride(fitsQI.ndim()-1, 1); IPosition fEnd (fitsQI.ndim()-1); for (uInt i=0; i mmData; Array mmMask; // dimension the start and end points // target only error values IPosition start (fitsQI.ndim(), 0); IPosition end (fitsQI.shape()-1); IPosition stride(fitsQI.ndim(), 1); start(fitsQI.ndim()-1) = 1; end(fitsQI.ndim()-1) = 1; // get a slicer and get the values and mask Slicer mmSection(start, end, stride, Slicer::endIsLast); fitsQI.doGetSlice(mmData, mmSection); fitsQI.doGetMaskSlice(mmMask, mmSection); if (print){ printArray (mmData,size, "DataIII = "); printArray (mmMask,size, "MaskIII = "); } Array fitsEData; Array fitsEMask; // dimension the start and end points // for the individual extensions IPosition fStart (fitsQI.ndim()-1, 0); IPosition fStride(fitsQI.ndim()-1, 1); IPosition fEnd (fitsQI.ndim()-1); for (uInt i=0; i mmData; Array mmMask; Array mmDataII; Array mmMaskII; // dimension the start and end points // target data and error values IPosition start (fitsQI.ndim(), 0); IPosition end (fitsQI.shape()-1); IPosition stride(fitsQI.ndim(), 1); // generate a slicer Slicer mmSection(start, end, stride, Slicer::endIsLast); // get the values and mask of the original image fitsQI.doGetSlice(mmData, mmSection); fitsQI.doGetMaskSlice(mmMask, mmSection); // get the values and mask of the original image secImg.doGetSlice(mmDataII, mmSection); secImg.doGetMaskSlice(mmMaskII, mmSection); if (print){ printArray (mmData,size, "Data orig. = "); printArray (mmMask,size, "Mask orig. = "); printArray (mmDataII,size, "Data assig.= "); printArray (mmMaskII,size, "Mask assig.= "); } if (!allNear (mmData, mmMask, mmDataII, mmMaskII)){ String msg = String("The assigned image has different values than the original!"); throw(AipsError(msg)); } } { // test the clone method ImageInterface* pFitsMM = fitsQI.cloneII(); Array fCloneArray = pFitsMM->get(); Array fCloneMask = pFitsMM->getMask(); CoordinateSystem fCloneCS = pFitsMM->coordinates(); Array fOrigArray = fitsQI.get(); Array fOrigMask = fitsQI.getMask(); CoordinateSystem fOrigCS = fitsQI.coordinates(); if (print){ printArray (fOrigArray,size, "Data orig. = "); printArray (fOrigMask,size, "Mask orig. = "); printArray (fCloneArray,size, "Data clone = "); printArray (fCloneMask,size, "Mask clone = "); } if (!allNear (fOrigArray, fOrigMask, fCloneArray, fCloneMask)){ String msg = String("The cloned image has different values than the original!"); throw(AipsError(msg)); } if (!fCloneCS.near(fOrigCS)){ String msg = String("The cloned image has different coord-sys than the original!"); throw(AipsError(msg)); } delete pFitsMM; } // TODO: add some more quantitative tests for the mask!! { // check the pixel mask Lattice &theMask = fitsQI.pixelMask(); if (theMask.shape() != fitsQI.shape()){ String msg = String("The mask shape must be identical to the shape of the data!"); throw(AipsError(msg)); } } { FITSImage *fData = fitsQI.fitsData(); if (fData->shape() != fitsDataImg.shape()){ String msg = String("The FITS data image shapes must be identical!"); throw(AipsError(msg)); } } { FITSErrorImage *fError = fitsQI.fitsError(); if (fError->shape() != fitsErrorImg.shape()){ String msg = String("The FITS error image shapes must be identical!"); throw(AipsError(msg)); } } return True; } template void printArray (T array, Int size, String pre) { T tmpArray; IPosition start (array.ndim(), 0); IPosition end (array.shape()-1); for (uInt i=0; i size-1) end(i) = size-1; tmpArray.reference(array(start, end)); cerr << "\n" << pre << tmpArray; } casacore-3.7.1/images/Images/test/tHDF5Image.cc000066400000000000000000000274121476623553700211070ustar00rootroot00000000000000//# tHDF5Image.cc: test the HDF5Image class //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace casacore; // Remove the dirname from the file name in an error message. String removeDir (const String& msg) { String s = msg; s.gsub (Regex("/.*/t"), "t"); return s; } Float const_arg_func(const Float& val) { return 3.0*val; } Float func(Float val) { return 2.0*val*val; } int main() { // Exit with untested if no HDF5 support. if (! HDF5Object::hasHDF5Support()) { return 3; } try { // Build things to make a HDF5Image CoordinateSystem cSys = CoordinateUtil::defaultCoords2D(); IPosition shape(2,32,64); TiledShape tiledShape(shape); // Test constructors { HDF5Image pIm(tiledShape, cSys, "tHDF5Image_tmp.img1"); } AlwaysAssertExit (isHDF5Image("tHDF5Image_tmp.img1")); AlwaysAssertExit (hdf5imagePixelType("tHDF5Image_tmp.img1") == TpFloat); { HDF5Image pIm2("tHDF5Image_tmp.img1"); } // Test copy constructor. This is by reference so test that // this is so { HDF5Image pIm(String("tHDF5Image_tmp.img1")); HDF5Image pIm2(pIm); pIm.putAt(Float(1.0), IPosition(2,0,0)); AlwaysAssert( (pIm(IPosition(2,0,0))==Float(1.0)), AipsError); pIm2.putAt(Float(10.0), IPosition(2,0,0)); AlwaysAssert((pIm2(IPosition(2,0,0))==Float(10.0)), AipsError); AlwaysAssert((pIm(IPosition(2,0,0))==Float(10.0)), AipsError); AlwaysAssert(pIm(IPosition(2,0,0))==pIm.getAt(IPosition(2,0,0)), AipsError); } // Test assignment. This is by reference so test that // this is so { HDF5Image pIm(String("tHDF5Image_tmp.img1")); HDF5Image pIm2(pIm); pIm2 = pIm; pIm.putAt(Float(1.0), IPosition(2,0,0)); AlwaysAssert( (pIm(IPosition(2,0,0))==Float(1.0)), AipsError); pIm2.putAt(Float(10.0), IPosition(2,0,0)); AlwaysAssert((pIm2.getAt(IPosition(2,0,0))==Float(10.0)), AipsError); AlwaysAssert((pIm.getAt(IPosition(2,0,0))==Float(10.0)), AipsError); AlwaysAssert(pIm(IPosition(2,0,0))==pIm.getAt(IPosition(2,0,0)), AipsError); } // Test clones. There is not much we can do to make sure they are ok ! // They are by reference too. { HDF5Image pIm(String("tHDF5Image_tmp.img1")); pIm.putAt(Float(1.0), IPosition(2,0,0)); AlwaysAssert( (pIm(IPosition(2,0,0))==Float(1.0)), AipsError); Lattice* lat = pIm.clone(); AlwaysAssert(pIm.shape()==lat->shape(), AipsError); lat->putAt(Float(10.0), IPosition(2,0,0)); AlwaysAssert(((*lat).getAt(IPosition(2,0,0))==Float(10.0)), AipsError); AlwaysAssert((pIm.getAt(IPosition(2,0,0))==Float(10.0)), AipsError); delete lat; } { HDF5Image pIm(String("tHDF5Image_tmp.img1")); pIm.putAt(Float(1.0), IPosition(2,0,0)); AlwaysAssert( (pIm.getAt(IPosition(2,0,0))==Float(1.0)), AipsError); Lattice* ii = pIm.cloneII(); AlwaysAssert(pIm.shape()==ii->shape(), AipsError); ii->putAt(Float(10.0), IPosition(2,0,0)); AlwaysAssert(((*ii).getAt(IPosition(2,0,0))==Float(10.0)), AipsError); AlwaysAssert((pIm.getAt(IPosition(2,0,0))==Float(10.0)), AipsError); delete ii; } // Test some miscellaneous little things { HDF5Image pIm(String("tHDF5Image_tmp.img1")); AlwaysAssert(hdf5imagePixelType(String("tHDF5Image_tmp.img1"))==TpFloat, AipsError); AlwaysAssert(pIm.name(True)==String("tHDF5Image_tmp.img1"), AipsError); cout << "Absolute name = " << pIm.name(False) << endl; AlwaysAssert(pIm.isPaged(), AipsError); AlwaysAssert(pIm.isWritable(), AipsError); AlwaysAssert(pIm.ok(), AipsError); AlwaysAssert(pIm.shape()==shape, AipsError); IPosition niceCursorShape = pIm.niceCursorShape(); // Only true for small images AlwaysAssert(niceCursorShape==shape, AipsError); Unit units("Jy"); pIm.setUnits(units); AlwaysAssert(pIm.units().getName()=="Jy", AipsError); Record rec; rec.define("x", Double(1.0)); rec.define("y", Double(2.0)); pIm.setMiscInfo(rec); TableRecord rec2 = pIm.miscInfo(); AlwaysAssert(rec2.nfields()==2, AipsError); AlwaysAssert(rec2.isDefined("x"), AipsError); AlwaysAssert(rec2.isDefined("y"), AipsError); AlwaysAssert(rec2.dataType("x")==TpDouble, AipsError); AlwaysAssert(rec2.dataType("y")==TpDouble, AipsError); AlwaysAssert(rec2.asDouble("x")==Double(1.0), AipsError); AlwaysAssert(rec2.asDouble("y")==Double(2.0), AipsError); CoordinateSystem cSys2 = pIm.coordinates(); Vector axisUnits = cSys2.worldAxisUnits(); axisUnits(0) = "deg"; axisUnits(1) = "deg"; cSys2.setWorldAxisUnits(axisUnits); pIm.setCoordinateInfo(cSys2); CoordinateSystem cSys3 = pIm.coordinates(); AlwaysAssert(cSys2.near(cSys3,1e-6), AipsError); ImageInfo info = pIm.imageInfo(); AlwaysAssert(info.restoringBeam().isNull(), AipsError); Quantity a1(10.0,Unit("arcsec")); Quantity a2(8.0,Unit("arcsec")); Quantity a3(-45.0,Unit("deg")); info.setRestoringBeam(GaussianBeam(a1, a2, a3)); pIm.setImageInfo(info); info = pIm.imageInfo(); AlwaysAssert(info.restoringBeam().getMajor()==a1, AipsError); AlwaysAssert(info.restoringBeam().getMinor()==a2, AipsError); AlwaysAssert(info.restoringBeam().getPA()==a3, AipsError); } // do{Put,Get}Slice tests { IPosition shape2(2,5, 10); TiledShape tiledShape2(shape2); CoordinateSystem cSys2 = CoordinateUtil::defaultCoords2D(); HDF5Image pIm(tiledShape2, cSys2, "tHDF5Image_tmp.img7"); // Fill (in slow way, so use small image) IPosition pos(2); for (Int i=0; i data; pIm.doGetSlice(data, slice); AlwaysAssert(data.shape()==shape2, AipsError); for (Int i=0; i data2(data.copy()); data.resize(IPosition(2,2,2)); data.set(0.0); pIm.doPutSlice(data, IPosition(2,0,0), IPosition(2,1,1)); pos(0) = 0; pos(1) = 0; AlwaysAssert(pIm(IPosition(2,0,0))==Float(0.0), AipsError); AlwaysAssert(pIm(IPosition(2,0,1))==Float(0.0), AipsError); AlwaysAssert(pIm(IPosition(2,1,0))==Float(0.0), AipsError); AlwaysAssert(pIm(IPosition(2,1,1))==Float(0.0), AipsError); for (Int i=2; i pIm(tiledShape2, cSys2, "tHDF5Image_tmp.img8"); IPosition pos(2); Array data(shape2); for (Int i=0; i lat(shape2); lat.set(Float(10.0)); pIm += lat; Array data2(data.copy()); data2 += Float(10.0); Slicer slice(IPosition(2,0,0), shape2, IPosition(2,1,1)); Array data3; pIm.doGetSlice(data3, slice); AlwaysAssert(allNear(data3, data2, 1e-6), AipsError); } // apply functions { IPosition shape2(2,5, 10); TiledShape tiledShape2(shape2); CoordinateSystem cSys2 = CoordinateUtil::defaultCoords2D(); HDF5Image pIm(tiledShape2, cSys2, "tHDF5Image_tmp.img9"); pIm.set(3.0); AlwaysAssert(allEQ(pIm.get(), Float(3.0)), AipsError); // 2 * x * x pIm.apply(&func); AlwaysAssert(allNear(pIm.get(), Float(18.0), Double(1e-6)), AipsError); // 3 * x (const arg) pIm.set(3.0); AlwaysAssert(allEQ(pIm.get(), Float(3.0)), AipsError); pIm.apply(&const_arg_func); AlwaysAssert(allNear(pIm.get(), Float(9.0), Double(1e-6)), AipsError); // Polynomial 1 + 2x + 3x**2 pIm.set(3.0); AlwaysAssert(allEQ(pIm.get(), Float(3.0)), AipsError); Polynomial poly(3); poly.setCoefficient(1, 1.0); poly.setCoefficient(2, 2.0); poly.setCoefficient(3, 3.0); pIm.apply(poly); AlwaysAssert(allNear(pIm.get(), poly(3.0), Double(1e-6)), AipsError); } // Do some iterating to test the makeIter function (indirectly) { IPosition shape2(2, 128, 256); TiledShape tiledShape2(shape2); CoordinateSystem cSys2 = CoordinateUtil::defaultCoords2D(); HDF5Image pIm(tiledShape2, cSys2, "tHDF5Image_tmp.img10"); pIm.set(1.0); LatticeIterator it(pIm); while (!it.atEnd()) { AlwaysAssert(allEQ(it.cursor(), Float(1.0)), AipsError); it++; } } cout<< "ok"<< endl; } catch (std::exception& x) { cerr << "Exception caught: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/images/Images/test/tImageAttrHandler.cc000066400000000000000000000176001476623553700226270ustar00rootroot00000000000000//# tImageAttrHandler.cc: Test program for tImageAttr classes //# Copyright (C) 2012 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include using namespace casacore; using namespace std; void testCreate (ImageInterface& image) { cout << "testCreate ..." << endl; ImageAttrHandler& attrHand (image.attrHandler(True)); cout << "GOT HANDLER"< image(IPosition(2,128,128), CoordinateUtil::defaultCoords2D(), imageName); testCreate (image); } void testCreateHDF5 (const String& imageName) { HDF5Image image(IPosition(2,128,128), CoordinateUtil::defaultCoords2D(), imageName); testCreate (image); } ImageInterface* doOpen (const String& imageName) { LatticeBase* latt = ImageOpener::openImage (imageName); ImageInterface* image = dynamic_cast*>(latt); AlwaysAssertExit (image); return image; } void testRead (const String& imageName) { cout << "testRead ..." << endl; ImageInterface* image = doOpen(imageName); ImageAttrHandler& attrHand (image->attrHandler()); cout << attrHand.groupNames()<* image = doOpen(imageName); ImageAttrHandler& attrHand (image->attrHandler()); ImageAttrGroup& group1 = attrHand.openGroup ("testGroup1"); Array arr1(IPosition(1,4)); indgen (arr1); group1.putData ("attr2", 0, ValueHolder(arr1)); ImageAttrGroup& group2 = attrHand.createGroup ("testGroup2"); Array arr2(IPosition(1,3)); indgen (arr2); Vector measInfo(2); measInfo[0] = "direction"; measInfo[1] = "J2000"; for (uInt rownr=0; rownr<4; ++rownr) { group2.putData ("attr2", rownr, ValueHolder(arr2), Vector(1,"rad"), measInfo); arr2 += 3; } delete image; } void testCopy (const String& nameIn, const String& nameOut, Bool hdf5) { cout << endl << "testCopy " << nameIn << " to " << nameOut << endl; ImageInterface* image = doOpen(nameIn); ImageInterface* newImage = 0; if (hdf5) { cout << ">>> to HDF5<<<" << endl; newImage = new HDF5Image (image->shape(), image->coordinates(), nameOut); } else { cout << ">>> to Casa<<<" << endl; newImage = new PagedImage (image->shape(), image->coordinates(), nameOut); } newImage->copyData (*image); ImageUtilities::copyMiscellaneous (*newImage, *image); delete image; delete newImage; } void testSub (const String& nameIn, const String& nameOut, Bool hdf5) { cout << endl << "testSub " << nameIn << " to " << nameOut << endl; ImageInterface* image = doOpen(nameIn); IPosition shp = image->shape(); SubImage subimg (*image, Slicer(IPosition(shp.size(), 0), (shp+1)/2)); ImageInterface* newImage = 0; if (hdf5) { cout << ">>> to HDF5<<<" << endl; newImage = new HDF5Image (subimg.shape(), subimg.coordinates(), nameOut); } else { cout << ">>> to Casa<<<" << endl; newImage = new PagedImage (subimg.shape(), subimg.coordinates(), nameOut); } newImage->copyData (subimg); ImageUtilities::copyMiscellaneous (*newImage, subimg); delete image; delete newImage; } void showAll (const String& imageName) { cout << endl << "image = " << imageName << endl; ImageInterface* image = doOpen(imageName); ImageAttrHandler& attrHand (image->attrHandler()); Vector groupNames = attrHand.groupNames(); for (uInt i=0; i>> Test Casa image <<<" << endl; testCreateCasa ("tImageAttrHandler_tmp.img1"); testAll ("tImageAttrHandler_tmp.img1", hasHDF5); // Test HDF5. // If not available, do Pagedmage again (to have correct output).. if (hasHDF5) { cout << endl << ">>> Test HDF5 image <<<" << endl; testCreateHDF5 ("tImageAttrHandler_tmp.img2"); } else { cout << endl << ">>> Test Casa image <<<" << endl; testCreateCasa ("tImageAttrHandler_tmp.img2"); } testAll ("tImageAttrHandler_tmp.img2", hasHDF5); // If an image is given, show its attributes. if (argc > 1) { showAll (argv[1]); testCopy (argv[1], argv[1] + String("_cp"), True); showAll (argv[1] + String("_cp")); testSub (argv[1], argv[1] + String("_sub"), False); showAll (argv[1] + String("_sub")); } } catch (std::exception& x) { cout << "Uncaught exception: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/images/Images/test/tImageAttrHandler.out000066400000000000000000000055721476623553700230560ustar00rootroot00000000000000 >>> Test Casa image <<< testCreate ... GOT HANDLER [] 0 GOT GROUP 0 [] 1 [testGroup1] GOT GROUP 1 [attr1] [attr1] testRead ... [testGroup1] GOT GROUP 1 [attr1] image = tImageAttrHandler_tmp.img1 Attribute group testGroup1 nrows=1 attr1: aa, [] [] testUpdate ... image = tImageAttrHandler_tmp.img1 Attribute group testGroup1 nrows=1 attr1: aa, [] [] attr2: [0, 1, 2, 3], [] [] Attribute group testGroup2 nrows=4 attr2: [0, 1, 2],[3, 4, 5],[6, 7, 8],[9, 10, 11], [rad] [direction, J2000] testCopy tImageAttrHandler_tmp.img1 to tImageAttrHandler_tmp.img1_cp1 >>> to Casa<<< image = tImageAttrHandler_tmp.img1_cp1 Attribute group testGroup1 nrows=1 attr1: aa, [] [] attr2: [0, 1, 2, 3], [] [] Attribute group testGroup2 nrows=4 attr2: [0, 1, 2],[3, 4, 5],[6, 7, 8],[9, 10, 11], [rad] [direction, J2000] testCopy tImageAttrHandler_tmp.img1 to tImageAttrHandler_tmp.img1_cp2 >>> to HDF5<<< image = tImageAttrHandler_tmp.img1_cp2 Attribute group testGroup1 nrows=1 attr1: aa, [] [] attr2: [0, 1, 2, 3], [] [] Attribute group testGroup2 nrows=4 attr2: [0, 1, 2],[3, 4, 5],[6, 7, 8],[9, 10, 11], [rad] [direction, J2000] testSub tImageAttrHandler_tmp.img1 to tImageAttrHandler_tmp.img1_sub >>> to Casa<<< image = tImageAttrHandler_tmp.img1_sub Attribute group testGroup1 nrows=1 attr1: aa, [] [] attr2: [0, 1, 2, 3], [] [] Attribute group testGroup2 nrows=4 attr2: [0, 1, 2],[3, 4, 5],[6, 7, 8],[9, 10, 11], [rad] [direction, J2000] >>> Test HDF5 image <<< testCreate ... GOT HANDLER [] 0 GOT GROUP 0 [] 1 [testGroup1] GOT GROUP 1 [attr1] [attr1] testRead ... [testGroup1] GOT GROUP 1 [attr1] image = tImageAttrHandler_tmp.img2 Attribute group testGroup1 nrows=1 attr1: aa, [] [] testUpdate ... image = tImageAttrHandler_tmp.img2 Attribute group testGroup1 nrows=1 attr1: aa, [] [] attr2: [0, 1, 2, 3], [] [] Attribute group testGroup2 nrows=4 attr2: [0, 1, 2],[3, 4, 5],[6, 7, 8],[9, 10, 11], [rad] [direction, J2000] testCopy tImageAttrHandler_tmp.img2 to tImageAttrHandler_tmp.img2_cp1 >>> to Casa<<< image = tImageAttrHandler_tmp.img2_cp1 Attribute group testGroup1 nrows=1 attr1: aa, [] [] attr2: [0, 1, 2, 3], [] [] Attribute group testGroup2 nrows=4 attr2: [0, 1, 2],[3, 4, 5],[6, 7, 8],[9, 10, 11], [rad] [direction, J2000] testCopy tImageAttrHandler_tmp.img2 to tImageAttrHandler_tmp.img2_cp2 >>> to HDF5<<< image = tImageAttrHandler_tmp.img2_cp2 Attribute group testGroup1 nrows=1 attr1: aa, [] [] attr2: [0, 1, 2, 3], [] [] Attribute group testGroup2 nrows=4 attr2: [0, 1, 2],[3, 4, 5],[6, 7, 8],[9, 10, 11], [rad] [direction, J2000] testSub tImageAttrHandler_tmp.img2 to tImageAttrHandler_tmp.img2_sub >>> to Casa<<< image = tImageAttrHandler_tmp.img2_sub Attribute group testGroup1 nrows=1 attr1: aa, [] [] attr2: [0, 1, 2, 3], [] [] Attribute group testGroup2 nrows=4 attr2: [0, 1, 2],[3, 4, 5],[6, 7, 8],[9, 10, 11], [rad] [direction, J2000] casacore-3.7.1/images/Images/test/tImageBeamSet.cc000066400000000000000000000737371476623553700217540ustar00rootroot00000000000000//# tImageConcat.cc: This program tests the ImageConcat class //# Copyright (C) 1996,1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include int main() { try { { cout << "*** Test constructors, operator=" << endl; // empty beam set ImageBeamSet x; AlwaysAssert(x.empty(), AipsError); AlwaysAssert(x.size() == 0, AipsError); AlwaysAssert(x.nelements() == 0, AipsError); AlwaysAssert(!x.hasSingleBeam(), AipsError); AlwaysAssert(!x.hasMultiBeam(), AipsError); // A beam. GaussianBeam beam( Quantity(4, "arcsec"), Quantity(3, "arcsec"), Quantity(40, "deg") ); ImageBeamSet b(20, 4); AlwaysAssert(!b.hasSingleBeam(), AipsError); AlwaysAssert(b.hasMultiBeam(), AipsError); b.set(beam); AlwaysAssert(b.getBeam(2,2) == beam, AipsError); // check operator= ImageBeamSet c = b; AlwaysAssert(c.size() == 20*4, AipsError); AlwaysAssert(b == b, AipsError); AlwaysAssert(c == b, AipsError); // check copy constructor ImageBeamSet d(b); AlwaysAssert(d == b, AipsError); c = x; AlwaysAssert(c.empty(), AipsError); x = b; AlwaysAssert(x.size() == 20*4, AipsError); AlwaysAssert(c != b, AipsError); AlwaysAssert(x == b, AipsError); // check a single beam ImageBeamSet k(beam); AlwaysAssert (k.shape() == IPosition(2,1,1), AipsError); AlwaysAssert (k.getBeam(2,2) == beam, AipsError); // valid for all ImageBeamSet y(IPosition(2, 1, 4)); y.set(beam); AlwaysAssert (y(2,3) == beam, AipsError); // Check assignment a bit more. y = b; AlwaysAssert(y == b, AipsError); y = ImageBeamSet(); AlwaysAssert(y.empty(), AipsError); } { cout << "*** test setBeam()" << endl; GaussianBeam beam0(Quantity(4, "arcsec"), Quantity(3, "arcsec"), Quantity(20, "deg")); ImageBeamSet x(3, 4, beam0); AlwaysAssert (x.nchan() == 3, AipsError); AlwaysAssert (x.nstokes() == 4, AipsError); GaussianBeam beam1(Quantity(5, "arcsec"), Quantity(4, "arcsec"), Quantity(20, "deg")); x.setBeam(1, 2, beam1); IPosition axisPath = IPosition::makeAxisPath(x.shape().size()); ArrayPositionIterator iter(x.shape(), axisPath, False); while (! iter.pastEnd()) { const IPosition pos = iter.pos(); GaussianBeam beam = x.getBeam(pos[0], pos[1]); if (pos == IPosition(2, 1,2)) { AlwaysAssert(beam == beam1, AipsError); } else { AlwaysAssert(beam == beam0, AipsError); } iter.next(); } { cout << "*** test setBeams()" << endl; GaussianBeam beam0(Quantity(4, "arcsec"), Quantity(3, "arcsec"), Quantity(20, "deg")); GaussianBeam beam1(Quantity(8, "arcsec"), Quantity(6, "arcsec"), Quantity(10, "deg")); GaussianBeam beam2(Quantity(5, "arcsec"), Quantity(4, "arcsec"), Quantity(20, "deg")); ImageBeamSet x00; ImageBeamSet x34(3, 4, beam0); x34.setBeam(1,2,beam2); ImageBeamSet x14(1, 4, beam0); x14.setBeam(0,1,beam2); ImageBeamSet x31(3, 1, beam0); x31.setBeam(1,0,beam2); ImageBeamSet x11(1, 1, beam0); ImageBeamSet b; b.setBeams (x00.getBeams()); AlwaysAssert (b==x00, AipsError); b.setBeams (x34.getBeams()); AlwaysAssert (b==x34, AipsError); b.setBeams (x14.getBeams()); { ImageBeamSet t(3,4,beam0); t.setBeam(0,1,beam2); t.setBeam(1,1,beam2); t.setBeam(2,1,beam2); AlwaysAssert (b==t, AipsError); } b.setBeams (x31.getBeams()); { ImageBeamSet t(3,4,beam0); t.setBeam(1,0,beam2); t.setBeam(1,1,beam2); t.setBeam(1,2,beam2); t.setBeam(1,3,beam2); AlwaysAssert (b==t, AipsError); } b.setBeams (x11.getBeams()); { ImageBeamSet t(3,4,beam0); AlwaysAssert (b==t, AipsError); } { ImageBeamSet y(x11); y.setBeams (x34.getBeams()); AlwaysAssert (y==x34, AipsError); } { ImageBeamSet y(x11); y.setBeams (x31.getBeams()); AlwaysAssert (y==x31, AipsError); } { ImageBeamSet y(x31); y.setBeams (x34.getBeams()); AlwaysAssert (y==x34, AipsError); } { ImageBeamSet y(x31); y.setBeams (x14.getBeams()); ImageBeamSet t(3,4,beam0); t.setBeam(0,1,beam2); t.setBeam(1,1,beam2); t.setBeam(2,1,beam2); AlwaysAssert (y==t, AipsError); } { ImageBeamSet y(x14); y.setBeams (x31.getBeams()); ImageBeamSet t(3,4,beam0); t.setBeam(1,0,beam2); t.setBeam(1,1,beam2); t.setBeam(1,2,beam2); t.setBeam(1,3,beam2); AlwaysAssert (y==t, AipsError); } } { cout << "*** test getting max and min area beams" << endl; GaussianBeam init( Quantity(4, "arcsec"), Quantity(2, "arcsec"), Quantity(0, "deg") ); ImageBeamSet x(3, 4, init); AlwaysAssert(x.getMaxAreaBeam() == init, AipsError); AlwaysAssert(x.getMinAreaBeam() == init, AipsError); GaussianBeam maxBeam( Quantity(10, "arcsec"), Quantity(8, "arcsec"), Quantity(0, "deg") ); GaussianBeam minBeam( Quantity(1, "arcsec"), Quantity(1, "arcsec"), Quantity(0, "deg") ); IPosition maxBeamPos(2, 2, 1); IPosition minBeamPos(2, 2, 3); x.setBeam(maxBeamPos[0], maxBeamPos[1], maxBeam); x.setBeam(minBeamPos[0], minBeamPos[1], minBeam); AlwaysAssert(x.getMaxAreaBeam() == maxBeam, AipsError); AlwaysAssert(x.getMinAreaBeam() == minBeam, AipsError); AlwaysAssert(x.getMaxAreaBeamPosition() == maxBeamPos, AipsError); AlwaysAssert(x.getMinAreaBeamPosition() == minBeamPos, AipsError); } { cout << "*** test setBeams()" << endl; GaussianBeam init( Quantity(4, "arcsec"), Quantity(2, "arcsec"), Quantity(0, "deg") ); ImageBeamSet x(1, 5, init); GaussianBeam beam2( Quantity(10, "arcsec"), Quantity(5, "arcsec"), Quantity(70, "deg") ); GaussianBeam beam3( Quantity(11, "arcsec"), Quantity(5, "arcsec"), Quantity(70, "deg") ); Matrix beams(1, 5, beam2); beams(0, 3) = beam3; x.setBeams(beams); AlwaysAssert(x.getBeams().shape() == IPosition(2, 1, 5), AipsError); AlwaysAssert(x.getMaxAreaBeam() == beam3, AipsError); } } { cout << "*** test setBeam()" << endl; GaussianBeam beam0( Quantity(4, "arcsec"), Quantity(3, "arcsec"), Quantity(20, "deg") ); ImageBeamSet x(3, 4, beam0); GaussianBeam beam1( Quantity(5, "arcsec"), Quantity(4, "arcsec"), Quantity(20, "deg") ); x.setBeam(1, 2, beam1); IPosition axisPath = IPosition::makeAxisPath(x.shape().size()); ArrayPositionIterator iter(x.shape(), axisPath, False); while (! iter.pastEnd()) { const IPosition pos = iter.pos(); GaussianBeam beam = x(pos[0], pos[1]); if (pos == IPosition(2, 1, 2)) { AlwaysAssert(beam == beam1, AipsError); } else { AlwaysAssert(beam == beam0, AipsError); } iter.next(); } { cout << "*** Test setBeam(), both chan and stokes < 0" << endl; GaussianBeam beam0( Quantity(4, "arcsec"), Quantity(3, "arcsec"), Quantity(20, "deg") ); ImageBeamSet x(3, 4, beam0); GaussianBeam beam1( Quantity(5, "arcsec"), Quantity(4, "arcsec"), Quantity(20, "deg") ); x.setBeam(-1, -1, beam1); AlwaysAssert(x.getBeams().size() == 1, AipsError); AlwaysAssert(x.getBeam() == beam1, AipsError); } { cout << "*** Test setBeam(), chan < 0 && stokes >= 0" << endl; GaussianBeam beam0( Quantity(4, "arcsec"), Quantity(3, "arcsec"), Quantity(20, "deg") ); ImageBeamSet x(3, 4, beam0); GaussianBeam beam1( Quantity(5, "arcsec"), Quantity(4, "arcsec"), Quantity(20, "deg") ); x.setBeam(-1, 2, beam1); AlwaysAssert(x.getBeams().size() == 12, AipsError); IPosition axisPath = IPosition::makeAxisPath(x.shape().size()); ArrayPositionIterator iter(x.shape(), axisPath, False); while (! iter.pastEnd()) { const IPosition pos = iter.pos(); GaussianBeam beam = x(pos[0], pos[1]); if (pos[1] == 2) { AlwaysAssert(beam == beam1, AipsError); } else { AlwaysAssert(beam == beam0, AipsError); } iter.next(); } } { cout << "*** Test setBeam(), stokes < 0 && chan >= 0" << endl; GaussianBeam beam0( Quantity(4, "arcsec"), Quantity(3, "arcsec"), Quantity(20, "deg") ); ImageBeamSet x(3, 4, beam0); GaussianBeam beam1( Quantity(5, "arcsec"), Quantity(4, "arcsec"), Quantity(20, "deg") ); x.setBeam(2, -1, beam1); AlwaysAssert(x.getBeams().size() == 12, AipsError); IPosition axisPath = IPosition::makeAxisPath(x.shape().size()); ArrayPositionIterator iter(x.shape(), axisPath, False); while (! iter.pastEnd()) { const IPosition pos = iter.pos(); GaussianBeam beam = x(pos[0], pos[1]); if (pos[0] == 2) { AlwaysAssert(beam == beam1, AipsError); } else { AlwaysAssert(beam == beam0, AipsError); } iter.next(); } } { cout << "*** test setBeams()" << endl; GaussianBeam init( Quantity(4, "arcsec"), Quantity(2, "arcsec"), Quantity(0, "deg") ); ImageBeamSet x(1, 5, init); Matrix beams(1, 5); x.setBeams(beams); } } { cout << "*** Test get max, min, median for polarizations" << endl; ImageBeamSet beamSet; IPosition pos; AlwaysAssert( beamSet.getMaxAreaBeamForPol(pos, 1) == GaussianBeam::NULL_BEAM, AipsError ); AlwaysAssert(pos == IPosition(2, 0, 0), AipsError); GaussianBeam beam0( Quantity(4, "arcsec"), Quantity(3, "arcsec"), Quantity(20, "deg") ); beamSet = ImageBeamSet(beam0); beamSet.getMaxAreaBeamForPol(pos, 1); AlwaysAssert(pos==IPosition(2,0,0), AipsError); beamSet = ImageBeamSet(3,4, beam0); IPosition gotPos; for (uInt i=0; i<4; i++) { GaussianBeam gotBeam = beamSet.getMaxAreaBeamForPol(gotPos, i); AlwaysAssert(gotBeam == beam0, AipsError); AlwaysAssert(gotPos == IPosition(2, 0, i), AipsError); gotBeam = beamSet.getMinAreaBeamForPol(gotPos, i); AlwaysAssert(gotBeam == beam0, AipsError); AlwaysAssert(gotPos == IPosition(2, 0, i), AipsError); gotBeam = beamSet.getMedianAreaBeamForPol(gotPos, i); AlwaysAssert(gotBeam == beam0, AipsError); AlwaysAssert(gotPos == IPosition(2, 1, i), AipsError); } GaussianBeam beam1( Quantity(5, "arcsec"), Quantity(3, "arcsec"), Quantity(20, "deg") ); beamSet.setBeam(2, 1, beam1); GaussianBeam beam2( Quantity(3, "arcsec"), Quantity(2, "arcsec"), Quantity(20, "deg") ); beamSet.setBeam(1, 1, beam2); for (uInt i=0; i<4; i++) { GaussianBeam gotBeam = beamSet.getMaxAreaBeamForPol(gotPos, i); if (i == 1) { AlwaysAssert(gotBeam == beam1, AipsError); AlwaysAssert(gotPos == IPosition(2, 2, 1), AipsError); } else { AlwaysAssert(gotBeam == beam0, AipsError); AlwaysAssert(gotPos == IPosition(2, 0, i), AipsError); } gotBeam = beamSet.getMinAreaBeamForPol(gotPos, i); if (i == 1) { AlwaysAssert(gotBeam == beam2, AipsError); AlwaysAssert(gotPos == IPosition(2, 1, i), AipsError); } else { AlwaysAssert(gotBeam == beam0, AipsError); AlwaysAssert(gotPos == IPosition(2, 0, i), AipsError); } gotBeam = beamSet.getMedianAreaBeamForPol(gotPos, i); AlwaysAssert(gotBeam == beam0, AipsError); if (i == 1) { AlwaysAssert(gotPos == IPosition(2, 0, i), AipsError); } else { AlwaysAssert(gotPos == IPosition(2, 1, i), AipsError); } } beamSet = ImageBeamSet(4, 4, beam0); for (uInt i=0; i<4; i++) { GaussianBeam gotBeam = beamSet.getMaxAreaBeamForPol(gotPos, i); AlwaysAssert(gotBeam == beam0, AipsError); AlwaysAssert(gotPos == IPosition(2, 0, i), AipsError); gotBeam = beamSet.getMinAreaBeamForPol(gotPos, i); AlwaysAssert(gotBeam == beam0, AipsError); AlwaysAssert(gotPos == IPosition(2, 0, i), AipsError); gotBeam = beamSet.getMedianAreaBeamForPol(gotPos, i); AlwaysAssert(gotBeam == beam0, AipsError); AlwaysAssert(gotPos == IPosition(2, 2, i), AipsError); } beamSet.setBeam(2, 1, beam1); beamSet.setBeam(1, 1, beam2); GaussianBeam beam3( Quantity(4.5, "arcsec"), Quantity(3, "arcsec"), Quantity(20, "deg") ); beamSet.setBeam(0, 1, beam3); for (uInt i=0; i<4; i++) { GaussianBeam gotBeam = beamSet.getMaxAreaBeamForPol(gotPos, i); if (i == 1) { AlwaysAssert(gotBeam == beam1, AipsError); AlwaysAssert(gotPos == IPosition(2, 2, 1), AipsError); } else { AlwaysAssert(gotBeam == beam0, AipsError); AlwaysAssert(gotPos == IPosition(2, 0, i), AipsError); } gotBeam = beamSet.getMinAreaBeamForPol(gotPos, i); if (i == 1) { AlwaysAssert(gotBeam == beam2, AipsError); AlwaysAssert(gotPos == IPosition(2, 1, i), AipsError); } else { AlwaysAssert(gotBeam == beam0, AipsError); AlwaysAssert(gotPos == IPosition(2, 0, i), AipsError); } gotBeam = beamSet.getMedianAreaBeamForPol(gotPos, i); if (i == 1) { AlwaysAssert(gotBeam == beam3, AipsError); AlwaysAssert(gotPos == IPosition(2, 0, i), AipsError); } else { AlwaysAssert(gotBeam == beam0, AipsError); AlwaysAssert(gotPos == IPosition(2, 2, i), AipsError); } } } { cout << "*** test equivalent()" << endl; GaussianBeam beam(Quantity(4, "arcsec"), Quantity(3, "arcsec"), Quantity(40, "deg")); GaussianBeam beam2(Quantity(4, "arcsec"), Quantity(3, "arcsec"), Quantity(40, "deg")); GaussianBeam beam3(Quantity(5, "arcsec"), Quantity(3, "arcsec"), Quantity(40, "deg")); { ImageBeamSet set1; ImageBeamSet set2; AlwaysAssert(set1.equivalent(set2), AipsError); AlwaysAssert(set2.equivalent(set1), AipsError); } { ImageBeamSet set1; ImageBeamSet set2(1,1,beam); AlwaysAssert(! set1.equivalent(set2), AipsError); AlwaysAssert(! set2.equivalent(set1), AipsError); } { ImageBeamSet set1(4,3,beam); ImageBeamSet set2(3,4,beam); AlwaysAssert(! set1.equivalent(set2), AipsError); AlwaysAssert(! set2.equivalent(set1), AipsError); } { ImageBeamSet set1(1,3,beam); ImageBeamSet set2(3,1,beam); AlwaysAssert(set1.equivalent(set2), AipsError); AlwaysAssert(set2.equivalent(set1), AipsError); } { ImageBeamSet set1(1,3,beam); ImageBeamSet set2(3,1,beam); AlwaysAssert(set1.equivalent(set2), AipsError); AlwaysAssert(set2.equivalent(set1), AipsError); } { ImageBeamSet set1(1,1,beam); ImageBeamSet set2(3,1,beam); AlwaysAssert(set1.equivalent(set2), AipsError); AlwaysAssert(set2.equivalent(set1), AipsError); } { ImageBeamSet set1(1,1,beam); ImageBeamSet set2(3,4,beam); AlwaysAssert(set1.equivalent(set2), AipsError); AlwaysAssert(set2.equivalent(set1), AipsError); } { ImageBeamSet set1(1,4,beam); ImageBeamSet set2(3,4,beam); AlwaysAssert(set1.equivalent(set2), AipsError); AlwaysAssert(set2.equivalent(set1), AipsError); } { ImageBeamSet set1(3,1,beam); ImageBeamSet set2(3,4,beam2); AlwaysAssert(set1.equivalent(set2), AipsError); AlwaysAssert(set2.equivalent(set1), AipsError); set2.setBeam (2,3,beam3); AlwaysAssert(! set1.equivalent(set2), AipsError); AlwaysAssert(! set2.equivalent(set1), AipsError); } } { cout << "*** test getSmallestMinorAxis" << endl; Matrix beams(1, 4); GaussianBeam beam1( Quantity(4, "arcsec"), Quantity(2, "arcsec"), Quantity(0, "deg") ); GaussianBeam beam2( Quantity(4, "arcsec"), Quantity(2, "arcsec"), Quantity(20, "deg") ); GaussianBeam beam3( Quantity(4, "arcsec"), Quantity(2, "arcsec"), Quantity(40, "deg") ); GaussianBeam beam4( Quantity(4, "arcsec"), Quantity(2, "arcsec"), Quantity(60, "deg") ); beams(0, 0) = beam1; beams(0, 1) = beam2; beams(0, 2) = beam3; beams(0, 3) = beam4; // all equal ImageBeamSet beamSet(beams); AlwaysAssert(beamSet.getSmallestMinorAxisBeam() == beam1, AipsError); beam3.setMajorMinor(Quantity(4, "arcsec"), Quantity(1, "arcsec")); beams(0, 2) = beam3; beamSet = ImageBeamSet(beams); GaussianBeam got = beamSet.getSmallestMinorAxisBeam(); AlwaysAssert(got == beam3, AipsError); beam3.setMajorMinor(Quantity(3, "arcsec"), Quantity(2, "arcsec")); beams(0, 2) = beam3; beamSet = ImageBeamSet(beams); got = beamSet.getSmallestMinorAxisBeam(); AlwaysAssert(got == beam3, AipsError); cout << "*** test to/fromRecord()" << endl; Record yy = beamSet.toRecord(); ImageBeamSet gotSet = ImageBeamSet::fromRecord(yy); AlwaysAssert( gotSet.nchan() == beamSet.nchan() && gotSet.nstokes() == beamSet.nstokes() && gotSet.equivalent(beamSet), AipsError ); } { cout << "*** Test getMedianAreaBeam()" << endl; Matrix beams(3, 4); uInt count = 1; Matrix::iterator iter = beams.begin(); Matrix::iterator end = beams.end(); Quantity radius; while (iter != end) { radius = Quantity(count, "arcsec"); iter->setMajorMinor(radius, radius); ++iter; ++count; } radius = Quantity(6.5, "arcsec"); beams(2,2) = GaussianBeam(radius, radius, Quantity(0, "deg")); ImageBeamSet bs(beams); AlwaysAssert(bs.getMedianAreaBeam() == beams(2, 2), AipsError); Matrix beams2(1, 12); count = 1; iter = beams2.begin(); end = beams2.end(); while (iter != end) { radius = Quantity(count, "arcsec"); iter->setMajorMinor(radius, radius); iter++; count++; } radius = Quantity(6.5, "arcsec"); beams2(0,10) = GaussianBeam(radius, radius, Quantity(0, "deg")); ImageBeamSet bs2(beams2); AlwaysAssert(bs2.getMedianAreaBeam() == beams2(0, 10), AipsError); Matrix beams3(12, 1); count = 1; iter = beams3.begin(); end = beams3.end(); while (iter != end) { radius = Quantity(count, "arcsec"); iter->setMajorMinor(radius, radius); iter++; count++; } radius = Quantity(6.5, "arcsec"); beams3(8, 0) = GaussianBeam(radius, radius, Quantity(0, "deg")); ImageBeamSet bs3(beams3); AlwaysAssert(bs3.getMedianAreaBeam() == beams3(8,0), AipsError); } { cout << "*** test rotate()" << endl; GaussianBeam beam( Quantity(4, "arcsec"), Quantity(3, "arcsec"), Quantity(40, "deg") ); ImageBeamSet beamSet(beam); beamSet.rotate(Quantity(30, "deg")); AlwaysAssert( beamSet.getBeam().getPA(True) == Quantity(70, "deg"), AipsError ); AlwaysAssert( beamSet.getMinAreaBeam().getPA(True) == Quantity(70, "deg"), AipsError ); AlwaysAssert( beamSet.getMaxAreaBeam().getPA(True) == Quantity(70, "deg"), AipsError ); Matrix beams(2,2, beam); beams(1, 1).setPA(Quantity(90, "deg")); beamSet = ImageBeamSet(beams); beamSet.rotate(Quantity(50, "deg")); AlwaysAssert( beamSet(0, 0).getPA(True) == Quantity(90, "deg"), AipsError ); AlwaysAssert( beamSet(0, 1).getPA(True) == Quantity(90, "deg"), AipsError ); AlwaysAssert( beamSet(1, 0).getPA(True) == Quantity(90, "deg"), AipsError ); AlwaysAssert( beamSet(1, 1).getPA(True) == Quantity(-40, "deg"), AipsError ); } const Quantity five(5, "arcsec"); const Quantity four(4, "arcsec"); const Quantity two(2, "arcsec"); { cout << "*** check replacing largest beam works when chan specified " << "and stokes negative" << endl; Matrix mat(1, 2); mat[0][0] = GaussianBeam(five, five, five); mat[1][0] = GaussianBeam(four, four, four); ImageBeamSet beams(mat); auto maxbeam = beams.getMaxAreaBeam(); AlwaysAssert(maxbeam.getMajor().getValue() == 5, AipsError); beams.setBeam(0, -1, GaussianBeam(four, four, four)); maxbeam = beams.getMaxAreaBeam(); AlwaysAssert(maxbeam.getMajor().getValue() == 4, AipsError); } { cout << "*** check replacing largest beam works when stokes specified " << "and chan negative" << endl; Matrix mat(2, 1); mat[0][0] = GaussianBeam(five, five, five); mat[0][1] = GaussianBeam(four, four, four); ImageBeamSet beams(mat); auto maxbeam = beams.getMaxAreaBeam(); AlwaysAssert(maxbeam.getMajor().getValue() == 5, AipsError); beams.setBeam(-1, 0, GaussianBeam(four, four, four)); maxbeam = beams.getMaxAreaBeam(); AlwaysAssert(maxbeam.getMajor().getValue() == 4, AipsError); } { cout << "*** test getBeamAreas" << endl; Matrix mat(3, 2); mat[0][0] = GaussianBeam(five, five, five); mat[0][1] = GaussianBeam(four, four, four); mat[0][2] = GaussianBeam(two, two, two); mat[1][0] = GaussianBeam(two, two, four); mat[1][1] = GaussianBeam(four, two, two); mat[1][2] = GaussianBeam(four, two, four); ImageBeamSet beams(mat); const auto areas_as2 = beams.getAreas().getValue("arcsec2"); for (uint i=0; i<3; ++i) { for (uint j=0; j<2; ++j) { AlwaysAssert( areas_as2(i, j) == beams.getBeam(i,j).getArea("arcsec2"), AipsError ); } } } { cout << "*** test paramMatrices" << endl; Matrix mat(3, 2); mat[0][0] = GaussianBeam(five, five, five); mat[0][1] = GaussianBeam(four, four, four); mat[0][2] = GaussianBeam(two, two, two); mat[1][0] = GaussianBeam(two, two, four); mat[1][1] = GaussianBeam(four, two, two); mat[1][2] = GaussianBeam(four, two, four); ImageBeamSet beams(mat); auto matrices = beams.paramMatrices(); const auto majors = matrices["major"].getValue(); const auto minors = matrices["minor"].getValue(); const auto pas = matrices["pa"].getValue(); const auto mUnit = matrices["major"].getUnit(); AlwaysAssert(mUnit == matrices["minor"].getUnit(), AipsError); const auto paUnit = matrices["pa"].getUnit(); for (uint i=0; i<3; ++i) { for (uint j=0; j<2; ++j) { const auto beam = beams.getBeam(i, j); AlwaysAssert( majors(i, j) == beam.getMajor(mUnit), AipsError ); AlwaysAssert( minors(i, j) == beam.getMinor(mUnit), AipsError ); AlwaysAssert( pas(i, j) == beam.getPA(paUnit), AipsError ); } } } } catch (const std::exception& x) { cout << x.what() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/images/Images/test/tImageConcat.cc000066400000000000000000001031561476623553700216300ustar00rootroot00000000000000//# tImageConcat.cc: This program tests the ImageConcat class //# Copyright (C) 1996,1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void check (uInt axis, MaskedLattice& ml, MaskedLattice& ml1, MaskedLattice& ml2); void check (uInt axis, MaskedLattice& ml, MaskedLattice& ml1, MaskedLattice& ml2, MaskedLattice& ml3); void checkMiscInfo (ImageConcat& image, Bool hasExtra); void makeMask (ImageInterface& im, Bool maskValue, Bool set); void testLogger(); int main() { try { // Make some Arrays IPosition shape(2,5,10); Array a1(shape); Array a2(shape); Int i, j; for (i=0; i im1(shape, CoordinateUtil::defaultCoords2D(), "tImageConcat_tmp1.img"); PagedImage im2(shape, CoordinateUtil::defaultCoords2D(), "tImageConcat_tmp2.img"); makeMask(im1, True, True); makeMask(im2, False, True); im1.put(a1); im2.put(a2); // Make a MaskedLattice as well ArrayLattice al1(a1); SubLattice ml1(al1); // { cout << "Axis 0, PagedImages (masks)" << endl; // Concatenate along axis 0 ImageConcat lc (0, True); lc.setImage(im1, True); lc.setImage(im2, True); // Find output shape IPosition outShape = lc.shape(); AlwaysAssert(outShape.nelements()==2, AipsError); AlwaysAssert(outShape(0)==shape(0)+shape(0), AipsError); AlwaysAssert(outShape(1)==shape(1), AipsError); AlwaysAssert(lc.isMasked()==True, AipsError); AlwaysAssert(lc.hasPixelMask()==True, AipsError); // Make output PagedImage ml3(outShape, CoordinateUtil::defaultCoords2D(), "tImageConcat_tmp3.img"); makeMask(ml3, True, False); // Copy to output ml3.copyData(lc); ml3.pixelMask().put(lc.getMask()); // Check values check (0, ml3, im1, im2); // Set some miscInfo TableRecord rec(lc.miscInfo()); rec.define ("i4", 4); TableRecord srec; srec.define ("str", "abcd"); srec.define ("r4", float(1.0)); rec.defineRecord ("srec", srec); AlwaysAssertExit (!lc.isPersistent()); lc.setMiscInfo (rec); cout << "miscinfo=" << endl << lc.miscInfo(); // Save the concatenated image and read it back. lc.save ("tImageConcat_tmp.imgconc"); checkMiscInfo (lc, False); AlwaysAssertExit (lc.isPersistent()); LatticeBase* latt = ImageOpener::openImage ("tImageConcat_tmp.imgconc"); ImageConcat* lc3 = dynamic_cast*>(latt); AlwaysAssertExit (lc3 != 0); AlwaysAssertExit (allEQ(lc3->get(), lc.get())); AlwaysAssertExit (allEQ(lc3->getMask(), lc.getMask())); checkMiscInfo (*lc3, False); } { LatticeBase* latt = ImageOpener::openImage ("tImageConcat_tmp.imgconc"); ImageConcat* lc3 = dynamic_cast*>(latt); TableRecord rec=lc3->miscInfo(); checkMiscInfo (*lc3, False); rec.define("NewKey", "newvalue"); lc3->setMiscInfo(rec); checkMiscInfo (*lc3, True); delete lc3; } { LatticeBase* latt = ImageOpener::openImage ("tImageConcat_tmp.imgconc"); ImageConcat* lc3 = dynamic_cast*>(latt); checkMiscInfo (*lc3, True); delete lc3; } // { cout << "Axis 1, PagedImages (masks)" << endl; // Concatenate along axis 1 ImageConcat lc (1, False); lc.setImage(im1, True); lc.setImage(im2, True); // Find output shape IPosition outShape = lc.shape(); AlwaysAssert(outShape.nelements()==2, AipsError); AlwaysAssert(outShape(0)==shape(0), AipsError); AlwaysAssert(outShape(1)==shape(1)+shape(1), AipsError); AlwaysAssert(lc.isMasked()==True, AipsError); AlwaysAssert(lc.hasPixelMask()==True, AipsError); // Make output PagedImage ml3(outShape, CoordinateUtil::defaultCoords2D(), "tImageConcat_tmp3.img"); makeMask(ml3, True, False); // Copy to output ml3.copyData(lc); ml3.pixelMask().put(lc.getMask()); // Check values check (1, ml3, im1, im2); } { cout << "Axis 0, PagedImages (masks) + MaskedLattice (no mask)" << endl; // Concatenate along axis 0 ImageConcat lc (0); lc.setImage(im1, True); lc.setImage(im2, True); lc.setLattice(ml1); // Find output shape IPosition outShape = lc.shape(); AlwaysAssert(outShape.nelements()==2, AipsError); AlwaysAssert(outShape(0)==3*shape(0), AipsError); AlwaysAssert(outShape(1)==shape(1), AipsError); AlwaysAssert(lc.isMasked()==True, AipsError); AlwaysAssert(lc.hasPixelMask()==True, AipsError); AlwaysAssert(lc.pixelMask().isWritable()==False, AipsError); // Make output PagedImage ml3(outShape, CoordinateUtil::defaultCoords2D(), "tImageConcat_tmp3.img"); makeMask(ml3, True, False); // Copy to output ml3.copyData(lc); ml3.pixelMask().put(lc.getMask()); // Check values check (0, ml3, im1, im2, ml1); } // Contiguity test { cout << "Contiguity, axis 0, PagedImages, no masks" << endl; // Make an image and then chop it up and glue it back together // Thus we can test the coordinate contiguity demands IPosition shape2(2, 30, 10); PagedImage ml3(shape2, CoordinateUtil::defaultCoords2D(), "tImageConcat_tmp3.img"); // Slicer sl1(IPosition(2,0,0), IPosition(2,9,9), Slicer::endIsLast); Slicer sl2(IPosition(2,10,0), IPosition(2,19,9), Slicer::endIsLast); Slicer sl3(IPosition(2,20,0), IPosition(2,29,9), Slicer::endIsLast); // SubImage si1(ml3, sl1, True); si1.set(1.0); SubImage si2(ml3, sl2, True); si2.set(2.0); SubImage si3(ml3, sl3, True); si3.set(3.0); // Concatenate along axis 0 ImageConcat lc (0); lc.setImage(si1, False); lc.setImage(si2, False); lc.setImage(si3, False); // Find output shape IPosition outShape = lc.shape(); AlwaysAssert(outShape.nelements()==2, AipsError); AlwaysAssert(shape2.isEqual(outShape), AipsError); AlwaysAssert(lc.isMasked()==False, AipsError); AlwaysAssert(lc.hasPixelMask()==False, AipsError); // Make output PagedImage ml4(outShape, CoordinateUtil::defaultCoords2D(), "tImageConcat_tmp4.img"); // Copy to output ml4.copyData(lc); // Check values check (0, ml4, si1, si2, si3); } // Test lock etc { cout << "Testing locking" << endl; ImageConcat lc2 (0, False); lc2.setImage(im1, True); lc2.setImage(im2, True); AlwaysAssert(lc2.lock(FileLocker::Read, 1), AipsError); AlwaysAssert(lc2.hasLock(FileLocker::Read), AipsError); AlwaysAssert(lc2.lock(FileLocker::Write, 1), AipsError); AlwaysAssert(lc2.hasLock(FileLocker::Write), AipsError); lc2.unlock(); #ifndef AIPS_TABLE_NOLOCKING AlwaysAssert(!lc2.hasLock(FileLocker::Read), AipsError); AlwaysAssert(!lc2.hasLock(FileLocker::Write), AipsError); #endif } // Test copy constructor { cout << "Testing copy constructor" << endl; ImageConcat lc (0); lc.setImage(im1, True); lc.setImage(im2, True); ImageConcat lc2(lc); // Find output shape AlwaysAssert(lc.shape().isEqual(lc2.shape()), AipsError); AlwaysAssert(lc.isMasked()==lc2.isMasked(), AipsError); AlwaysAssert(lc.hasPixelMask()==lc2.hasPixelMask(), AipsError); // Make output PagedImage ml3(lc2.shape(), CoordinateUtil::defaultCoords2D(), "tImageConcat_tmp3.img"); makeMask(ml3, True, False); // Copy to output ml3.copyData(lc); ml3.pixelMask().put(lc.getMask()); // Check values check (0, ml3, im1, im2); } // Test assignment { cout << "Testing assignment " << endl; ImageConcat lc (0); lc.setImage(im1, True); lc.setImage(im2, True); ImageConcat lc2; lc2 = lc; // Find output shape AlwaysAssert(lc.shape().isEqual(lc2.shape()), AipsError); AlwaysAssert(lc.isMasked()==lc2.isMasked(), AipsError); AlwaysAssert(lc.hasPixelMask()==lc2.hasPixelMask(), AipsError); // Make output PagedImage ml3(lc2.shape(), CoordinateUtil::defaultCoords2D(), "tImageConcat_tmp3.img"); makeMask(ml3, True, False); // Copy to output ml3.copyData(lc); ml3.pixelMask().put(lc.getMask()); // Check values check (0, ml3, im1, im2); } // Some forced errors { cout << "Forced errors" << endl; ImageConcat lc (10); Bool ok = True; try { lc.setImage(im1, True); ok = False; } catch (std::exception& x) { } if (!ok) { throw (AipsError("set forced failure did not work - this was unexpected")); } // try { PagedImage ml4(IPosition(3,10,10,10), CoordinateUtil::defaultCoords3D(), "tImageConcat_tmp3.img"); lc.setImage(ml4, True); ok = False; } catch (std::exception& x) {;} if (!ok) { throw (AipsError("set forced failure did not work - this was unexpected")); } } // Check if the LoggerHolder works fine for concatenated images. testLogger(); { cout << "Noncontiguous spectral axis test - CAS-4317" << endl; CoordinateSystem csys = CoordinateUtil::defaultCoords3D(); Vector freqs(4); freqs[0] = 1.41e9; freqs[1] = 1.42e9; freqs[2] = 1.44e9; freqs[3] = 1.47e9; Double restfreq1 = 1.40e9; SpectralCoordinate sp1(MFrequency::LSRK, freqs, restfreq1); csys.replaceCoordinate(sp1, 1); TempImage t1(TiledShape(IPosition(3, 1, 1, 4)), csys); ImageInfo info1 = t1.imageInfo(); GaussianBeam beam1(Quantity(2, "arcsec"), Quantity(1, "arcsec"), Quantity(2, "deg")); info1.setRestoringBeam(beam1); t1.setImageInfo(info1); Vector gfreqs(3); gfreqs[0] = 1.52e9; gfreqs[1] = 1.53e9; gfreqs[2] = 1.54e9; Double restfreq2 = 1.5e9; SpectralCoordinate sp2(MFrequency::LSRK, gfreqs, restfreq2); csys.replaceCoordinate(sp2, 1); TempImage t2(TiledShape(IPosition(3, 1, 1, 3)), csys); ImageInfo info2 = t2.imageInfo(); GaussianBeam beam2(Quantity(4, "arcsec"), Quantity(3, "arcsec"), Quantity(20, "deg")); info2.setRestoringBeam(beam2); t2.setImageInfo(info2); ImageConcat concat(2); concat.setImage(t1, True); concat.setImage(t2, True); SpectralCoordinate newsp = concat.coordinates().spectralCoordinate(); Double world; for (uInt i=0; i<7; i++) { newsp.toWorld(world, i); GaussianBeam beam = concat.imageInfo().restoringBeam(i, -1); if (i < 4) { AlwaysAssert(world == freqs[i], AipsError); AlwaysAssert(beam == beam1, AipsError); } else { cout << beam< hfreqs(3); hfreqs[0] = 1.61e9; hfreqs[1] = 1.62e9; hfreqs[2] = 1.64e9; Double restfreq3 = 1.6e9; SpectralCoordinate sp3(MFrequency::LSRK, hfreqs, restfreq3); csys.replaceCoordinate(sp3, 1); TempImage t3(TiledShape(IPosition(3, 1, 1, 3)), csys); ImageInfo info3 = t3.imageInfo(); GaussianBeam beam3(Quantity(10, "arcsec"), Quantity(7, "arcsec"), Quantity(80, "deg")); info3.setRestoringBeam(beam3); t3.setImageInfo(info3); concat.setImage(t3, True); newsp = concat.coordinates().spectralCoordinate(); for (uInt i=0; i<10; i++) { newsp.toWorld(world, i); GaussianBeam beam = concat.imageInfo().restoringBeam(i, -1); if (i < 4) { AlwaysAssert(world == freqs[i], AipsError); AlwaysAssert(beam == beam1, AipsError); } else if (i < 7) { AlwaysAssert(world == gfreqs[i-4], AipsError); AlwaysAssert(beam == beam2, AipsError); } else { AlwaysAssert(world == hfreqs[i-7], AipsError); AlwaysAssert(beam == beam3, AipsError); } } AlwaysAssert(newsp.restFrequency() == restfreq1, AipsError); // Change the ImageInfo beams in the ConcatImage in various ways. // First by setting a global beam. GaussianBeam gbeam1(Quantity(12, "arcsec"), Quantity(5, "arcsec"), Quantity(75, "deg")); ImageBeamSet bset1(gbeam1); ImageInfo in1; in1.setObjectName ("obj1"); in1.setImageType (ImageInfo::Beam); in1.setBeams (bset1); concat.setImageInfo (in1); AlwaysAssertExit (concat.imageInfo().objectName() == "obj1"); AlwaysAssertExit (concat.image(0).imageInfo().objectName() == "obj1"); AlwaysAssertExit (concat.image(1).imageInfo().objectName() == "obj1"); AlwaysAssertExit (concat.image(2).imageInfo().objectName() == "obj1"); AlwaysAssertExit (concat.imageInfo().imageType() == ImageInfo::Beam); AlwaysAssertExit (concat.image(0).imageInfo().imageType() == ImageInfo::Beam); AlwaysAssertExit (concat.image(1).imageInfo().imageType() == ImageInfo::Beam); AlwaysAssertExit (concat.image(2).imageInfo().imageType() == ImageInfo::Beam); AlwaysAssertExit (concat.imageInfo().nChannels() == 1); AlwaysAssertExit (concat.imageInfo().restoringBeam() == gbeam1); AlwaysAssertExit (concat.image(0).imageInfo().restoringBeam() == gbeam1); AlwaysAssertExit (concat.image(1).imageInfo().restoringBeam() == gbeam1); AlwaysAssertExit (concat.image(2).imageInfo().restoringBeam() == gbeam1); // Now by setting individual beams (in total 10 channels). GaussianBeam gbeam2a(Quantity(12, "arcsec"), Quantity(5, "arcsec"), Quantity(75, "deg")); GaussianBeam gbeam2b(Quantity(13, "arcsec"), Quantity(6, "arcsec"), Quantity(76, "deg")); GaussianBeam gbeam2c(Quantity(14, "arcsec"), Quantity(7, "arcsec"), Quantity(77, "deg")); ImageBeamSet bset2(10,1); for (uInt i=0; i<10; ++i) { bset2.setBeam(i, 0, GaussianBeam(Quantity(i+5, "arcsec"), Quantity(i+1, "arcsec"), Quantity(i+60, "deg"))); } ImageInfo in2; in2.setBeams (bset2); concat.setImageInfo (in2); AlwaysAssertExit (concat.imageInfo().objectName() == ""); AlwaysAssertExit (concat.image(0).imageInfo().objectName() == ""); AlwaysAssertExit (concat.image(1).imageInfo().objectName() == ""); AlwaysAssertExit (concat.image(2).imageInfo().objectName() == ""); AlwaysAssertExit (concat.imageInfo().imageType() == ImageInfo::Intensity); AlwaysAssertExit (concat.image(0).imageInfo().imageType() == ImageInfo::Intensity); AlwaysAssertExit (concat.image(1).imageInfo().imageType() == ImageInfo::Intensity); AlwaysAssertExit (concat.image(2).imageInfo().imageType() == ImageInfo::Intensity); AlwaysAssertExit (concat.imageInfo().nChannels() == 10); cout<<"nchan0="< i0(TiledShape(IPosition(4, 10, 10, 1, 8)), csys); ImageInfo ii = i0.imageInfo(); ii.setAllBeams( 8, 1, GaussianBeam( Quantity(4, "arcsec"), Quantity(3, "arcsec"), Quantity(0, "deg") ) ); i0.setImageInfo(ii); Vector refVal = csys.referenceValue(); refVal[3] += 1e9; csys.setReferenceValue(refVal); TempImage i1(TiledShape(IPosition(4, 10, 10, 1, 27)), csys); ii = i1.imageInfo(); ii.setAllBeams( 27, 1, GaussianBeam( Quantity(8, "arcsec"), Quantity(6, "arcsec"), Quantity(0, "deg") ) ); i1.setImageInfo(ii); ImageConcat concat(3, False); concat.setImage(i0, True); concat.setImage(i1, True); } { cout << "*** Stokes concatenation" << endl; CoordinateSystem csys = CoordinateUtil::defaultCoords4D(); TempImage i0(TiledShape(IPosition(4, 5, 5, 4, 5)), csys); TempImage i1(TiledShape(IPosition(4, 5, 5, 4, 5)), csys); SubImage s0(i0, Slicer( IPosition(4, 0), IPosition(4, 4, 4, 0, 4), Slicer::endIsLast) ); SubImage s1(i0, Slicer( IPosition(4, 0, 0, 2, 0), IPosition(4, 4, 4, 2, 4), Slicer::endIsLast) ); cout << "first " << s0.coordinates().stokesCoordinate().stokes() << endl; cout << "second " << s1.coordinates().stokesCoordinate().stokes() << endl; ImageConcat concat(2); concat.setImage(s0, False); concat.setImage(s1, False); Vector outStokes = concat.coordinates().stokesCoordinate().stokes(); AlwaysAssert(outStokes.size() == 2, AipsError); AlwaysAssert(outStokes[0] == 1, AipsError); AlwaysAssert(outStokes[1] == 3, AipsError); } { cout << "Test adding first image contiguous, next not produces expected world values" << endl; CoordinateSystem csys = CoordinateUtil::defaultCoords4D(); TempImage i0(TiledShape(IPosition(4, 5, 5, 4, 5)), csys); SubImage s0(i0, Slicer( IPosition(4, 0), IPosition(4, 4, 4, 3, 0), Slicer::endIsLast) ); SubImage s1(i0, Slicer( IPosition(4, 0, 0, 0, 1), IPosition(4, 4, 4, 3, 1), Slicer::endIsLast) ); SubImage s3(i0, Slicer( IPosition(4, 0, 0, 0, 3), IPosition(4, 4, 4, 3, 3), Slicer::endIsLast) ); ImageConcat concat(3); concat.setImage(s0, False); Vector v0(4, 0); Vector v1(4, 0); v1[3] = 1; Vector v2(4, 0); v2[3] = 2; AlwaysAssert( concat.coordinates().toWorld(v0)[3] == s0.coordinates().toWorld(v0)[3], AipsError ); concat.setImage(s1, False); AlwaysAssert( concat.coordinates().toWorld(v0)[3] == s0.coordinates().toWorld(v0)[3], AipsError ); AlwaysAssert( concat.coordinates().toWorld(v1)[3] == s1.coordinates().toWorld(v0)[3], AipsError ); concat.setImage(s3, True); cout << "get " << std::setprecision(10) << concat.coordinates().toWorld(v0)[3] << endl; cout << "exp " << std::setprecision(10) << s0.coordinates().toWorld(v0)[3] << endl; AlwaysAssert( concat.coordinates().toWorld(v0)[3] == s0.coordinates().toWorld(v0)[3], AipsError ); cout << "get " << std::setprecision(10) << concat.coordinates().toWorld(v1)[3] << endl; cout << "exp " << std::setprecision(10) << s1.coordinates().toWorld(v0)[3] << endl; AlwaysAssert( concat.coordinates().toWorld(v1)[3] == s1.coordinates().toWorld(v0)[3], AipsError ); cout << "get " << std::setprecision(10) << concat.coordinates().toWorld(v2)[3] << endl; cout << "exp " << std::setprecision(10) << s3.coordinates().toWorld(v0)[3] << endl; cout << "spec values " << std::setprecision(10) << concat.coordinates().spectralCoordinate().worldValues() << endl; AlwaysAssert( concat.coordinates().toWorld(v2)[3] == s3.coordinates().toWorld(v0)[3], AipsError ); } {///Testing niceCursorShape cout << "Testing niceCursorShape " << endl; { ImageConcat concat(1); concat.setImage(im1, True); concat.setImage(im2, True); AlwaysAssert(concat.niceCursorShape() == im1.niceCursorShape(), AipsError); } { CoordinateSystem csys = CoordinateUtil::defaultCoords4D(); TempImage t0(TiledShape(IPosition(4, 50, 50, 1, 10)), csys, 0); TempImage t1(TiledShape(IPosition(4, 50, 50, 1, 5)), csys, 0); { ImageConcat concat(3); concat.setImage(t0, True); concat.setImage(t1, True); AlwaysAssert(concat.niceCursorShape() == t1.niceCursorShape(), AipsError); } //reverse order of concat { ImageConcat concat(3); concat.setImage(t1, True); concat.setImage(t0, True); AlwaysAssert(concat.niceCursorShape() == t1.niceCursorShape(), AipsError); } } } } catch(const std::exception& x) { cerr << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } void check (uInt axis, MaskedLattice& ml, MaskedLattice& ml1, MaskedLattice& ml2) { IPosition shape1 = ml1.shape(); IPosition shape2 = ml2.shape(); // IPosition blc(2,0,0); IPosition sliceShape(2,shape1(0), shape1(1)); AlwaysAssert(allEQ(ml1.get(), ml.getSlice(blc,shape1)), AipsError); AlwaysAssert(allEQ(ml1.getMask(), ml.getMaskSlice(blc,shape1)), AipsError); // if (axis==0) { blc(0) += shape1(0); AlwaysAssert(allEQ(ml2.get(), ml.getSlice(blc,shape2)), AipsError); AlwaysAssert(allEQ(ml2.getMask(), ml.getMaskSlice(blc,shape2)), AipsError); } else if (axis==1) { blc(1) += shape1(1); AlwaysAssert(allEQ(ml2.get(), ml.getSlice(blc,shape2)), AipsError); AlwaysAssert(allEQ(ml2.getMask(), ml.getMaskSlice(blc,shape2)), AipsError); } else { AlwaysAssert(axis==0||axis==1, AipsError); } } void check (uInt axis, MaskedLattice& ml, MaskedLattice& ml1, MaskedLattice& ml2, MaskedLattice& ml3) { IPosition shape1 = ml1.shape(); IPosition shape2 = ml2.shape(); IPosition shape3 = ml3.shape(); // IPosition blc(2,0,0); IPosition sliceShape(2,shape1(0), shape1(1)); AlwaysAssert(allEQ(ml1.get(), ml.getSlice(blc,shape1)), AipsError); AlwaysAssert(allEQ(ml1.getMask(), ml.getMaskSlice(blc,shape1)), AipsError); // if (axis==0) { blc(0) += shape1(0); AlwaysAssert(allEQ(ml2.get(), ml.getSlice(blc,shape2)), AipsError); AlwaysAssert(allEQ(ml2.getMask(), ml.getMaskSlice(blc,shape2)), AipsError); // blc(0) += shape2(0); AlwaysAssert(allEQ(ml3.get(), ml.getSlice(blc,shape3)), AipsError); AlwaysAssert(allEQ(ml3.getMask(), ml.getMaskSlice(blc,shape3)), AipsError); } else if (axis==1) { blc(1) += shape1(1); AlwaysAssert(allEQ(ml2.get(), ml.getSlice(blc,shape2)), AipsError); AlwaysAssert(allEQ(ml2.getMask(), ml.getMaskSlice(blc,shape2)), AipsError); // blc(1) += shape2(1); AlwaysAssert(allEQ(ml3.get(), ml.getSlice(blc,shape3)), AipsError); AlwaysAssert(allEQ(ml3.getMask(), ml.getMaskSlice(blc,shape3)), AipsError); } else { AlwaysAssert(axis==0||axis==1, AipsError); } } void checkMiscInfo (ImageConcat& img, Bool hasExtraKey) { TableRecord rec = img.miscInfo(); AlwaysAssertExit (rec.asInt("i4") == 4); TableRecord srec(rec.subRecord("srec")); AlwaysAssertExit (srec.size() == 2); AlwaysAssertExit (srec.asString("str") == "abcd"); AlwaysAssertExit (srec.asFloat("r4") == 1.0); if (hasExtraKey) { AlwaysAssertExit (rec.asString("NewKey") == "newvalue"); AlwaysAssertExit (rec.size() == 3); } else { AlwaysAssertExit (rec.size() == 2); } } void makeMask (ImageInterface& im, Bool maskValue, Bool set) { im.makeMask ("mask0", True, True, set, maskValue); } void testLogger() { // Make a concatenated image and make sure the image objects are gone. ImageConcat lc (0, True); ImageConcat lc2 (0, True); { // Make some Arrays IPosition shape(2,5,10); Array a1(shape); Array a2(shape); Int i, j; for (i=0; i im1(shape, CoordinateUtil::defaultCoords2D(), "tImageConcat_tmp1.imga"); PagedImage im2(shape, CoordinateUtil::defaultCoords2D(), "tImageConcat_tmp2.imga"); im1.put(a1); im2.put(a2); lc.setImage(im1, True); lc.setImage(im2, True); lc2.setImage(im2, True); lc2.setImage(im1, True); lc2.setImage(im2, True); im1.logger().logio() << "message1a" << LogIO::POST; im1.logger().logio() << "message1b" << LogIO::POST; im2.logger().logio() << "message2" << LogIO::POST; } // Add a message and check if the concatenation has 4 messages. LoggerHolder& logger = lc.logger(); logger.logio() << "message_conc" << LogIO::POST; uInt nmsg=0; for (LoggerHolder::const_iterator iter = logger.begin(); iter != logger.end(); iter++) { cout << iter->message() << endl; nmsg++; } AlwaysAssertExit (nmsg == 4); // If it also works well with the copy ctor and the assignent operator. { ImageConcat ic2(lc2); { LoggerHolder& logger = ic2.logger(); logger.logio() << "message_conc2" << LogIO::POST; uInt nmsg=0; for (LoggerHolder::const_iterator iter = logger.begin(); iter != logger.end(); iter++) { cout << iter->message() << endl; nmsg++; } AlwaysAssertExit (nmsg == 5); } ic2 = lc; { LoggerHolder& logger = ic2.logger(); uInt nmsg=0; for (LoggerHolder::const_iterator iter = logger.begin(); iter != logger.end(); iter++) { cout << iter->message() << endl; nmsg++; } AlwaysAssertExit (nmsg == 4); } } { cout << "Noncontiguous spectral axis test - CAS-4317" << endl; CoordinateSystem csys = CoordinateUtil::defaultCoords3D(); Vector freqs(4); freqs[0] = 1.41e9; freqs[1] = 1.42e9; freqs[2] = 1.44e9; freqs[3] = 1.47e9; Double restfreq1 = 1.40e9; SpectralCoordinate sp1(MFrequency::LSRK, freqs, restfreq1); csys.replaceCoordinate(sp1, 1); TempImage t1(TiledShape(IPosition(3, 1, 1, 4)), csys); ImageInfo info1 = t1.imageInfo(); GaussianBeam beam1(Quantity(2, "arcsec"), Quantity(1, "arcsec"), Quantity(2, "deg")); info1.setRestoringBeam(beam1); t1.setImageInfo(info1); Vector gfreqs(3); gfreqs[0] = 1.52e9; gfreqs[1] = 1.53e9; gfreqs[2] = 1.54e9; Double restfreq2 = 1.5e9; SpectralCoordinate sp2(MFrequency::LSRK, gfreqs, restfreq2); csys.replaceCoordinate(sp2, 1); TempImage t2(TiledShape(IPosition(3, 1, 1, 3)), csys); ImageInfo info2 = t2.imageInfo(); GaussianBeam beam2(Quantity(4, "arcsec"), Quantity(3, "arcsec"), Quantity(20, "deg")); info2.setRestoringBeam(beam2); t2.setImageInfo(info2); ImageConcat concat(2); concat.setImage(t1, True); AlwaysAssert(concat.shape() == t1.shape(), AipsError); concat.setImage(t2, True); SpectralCoordinate newsp = concat.coordinates().spectralCoordinate(); Double world; for (uInt i=0; i<7; i++) { newsp.toWorld(world, i); GaussianBeam beam = concat.imageInfo().restoringBeam(i, -1); if (i < 4) { AlwaysAssert(world == freqs[i], AipsError); AlwaysAssert(beam == beam1, AipsError); } else { AlwaysAssert(world == gfreqs[i-4], AipsError); AlwaysAssert(beam == beam2, AipsError); } } AlwaysAssert(newsp.restFrequency() == restfreq1, AipsError); cout << "Noncontiguous spectral axis test concating 3 images - CAS-4319" << endl; Vector hfreqs(3); hfreqs[0] = 1.61e9; hfreqs[1] = 1.62e9; hfreqs[2] = 1.64e9; Double restfreq3 = 1.6e9; SpectralCoordinate sp3(MFrequency::LSRK, hfreqs, restfreq3); csys.replaceCoordinate(sp3, 1); TempImage t3(TiledShape(IPosition(3, 1, 1, 3)), csys); ImageInfo info3 = t3.imageInfo(); GaussianBeam beam3(Quantity(10, "arcsec"), Quantity(7, "arcsec"), Quantity(80, "deg")); info3.setRestoringBeam(beam3); t3.setImageInfo(info3); concat.setImage(t3, True); newsp = concat.coordinates().spectralCoordinate(); for (uInt i=0; i<10; i++) { newsp.toWorld(world, i); GaussianBeam beam = concat.imageInfo().restoringBeam(i, -1); if (i < 4) { AlwaysAssert(world == freqs[i], AipsError); AlwaysAssert(beam == beam1, AipsError); } else if (i < 7) { AlwaysAssert(world == gfreqs[i-4], AipsError); AlwaysAssert(beam == beam2, AipsError); } else { AlwaysAssert(world == hfreqs[i-7], AipsError); AlwaysAssert(beam == beam3, AipsError); } } AlwaysAssert(newsp.restFrequency() == restfreq1, AipsError); } } casacore-3.7.1/images/Images/test/tImageEmpty.cc000066400000000000000000000050071476623553700215130ustar00rootroot00000000000000//# tImageEmpty: Test creating an image without writing it explicitly //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include using namespace casacore; // This test program was created because of a problem detected in the // CASA Imager::clone function. It created an image without writing/flushing // it, which worked well until the TSM changes made to support mmap. // It was fixed in the TSM (for all modes) and tested in tTableEmpty and here. void doIt (const IPosition& cubeShape, const IPosition tileShape) { { // Create the image without explicitly writing it. // The destructor should create it all (with arbitrary data). PagedImage newImage(TiledShape(cubeShape, tileShape), CoordinateUtil::defaultCoords2D(), "tImageEmpty_tmp.img"); } { // Check the shapes and if data can be read. PagedImage image ("tImageEmpty_tmp.img"); AlwaysAssertExit (image.shape() == cubeShape); AlwaysAssertExit (image.niceCursorShape() == tileShape); image.get(); } } int main() { // Try it with various tile shapes. doIt (IPosition(2,256,256), IPosition(2,256,256)); doIt (IPosition(2,256,256), IPosition(2,32,32)); doIt (IPosition(2,256,256), IPosition(2,29,31)); } casacore-3.7.1/images/Images/test/tImageExpr.cc000066400000000000000000000134451476623553700213400ustar00rootroot00000000000000//# tImageExpr.cc: This program tests the ImageExpr class //# Copyright (C) 2014 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include void testExpr() { IPosition shape(2,5,10); Array arr(shape); indgen (arr); { // Create the images. PagedImage im1(shape, CoordinateUtil::defaultCoords2D(), "tImageExpr_tmp.img1"); im1.put (arr); PagedImage im2(shape, CoordinateUtil::defaultCoords2D(), "tImageExpr_tmp.img2"); im2.put (arr - float(5)); } { // Form an expression and save it. String expr("'tImageExpr_tmp\\.img1' + tImageExpr_tmp.img2 + 5"); LatticeExprNode node(ImageExprParse::command(expr)); ImageExpr img (node, expr); AlwaysAssertExit (allEQ(img.get(), arr+arr)); AlwaysAssertExit (! img.isPersistent()); TableRecord rec; rec.define ("key", "value"); img.setMiscInfo (rec); img.save ("tImageExpr_tmp.imgexpr"); AlwaysAssertExit (img.isPersistent()); AlwaysAssertExit (ImageExprParse::getImageNames().size() == 2 && ImageExprParse::getImageNames()[0] == "tImageExpr_tmp.img1" && ImageExprParse::getImageNames()[1] == "tImageExpr_tmp.img2"); } { // Reopen the expression from the file. LatticeBase* latt = ImageOpener::openImageExpr ("tImageExpr_tmp.imgexpr"); ImageExpr* img = dynamic_cast*>(latt); AlwaysAssertExit (img != 0); AlwaysAssertExit (allEQ(img->get(), arr+arr)); AlwaysAssertExit (img->isPersistent()); AlwaysAssertExit (ImageExprParse::getImageNames().size() == 2 && ImageExprParse::getImageNames()[0] == "tImageExpr_tmp.img1" && ImageExprParse::getImageNames()[1] == "tImageExpr_tmp.img2"); AlwaysAssertExit (img->miscInfo().asString("key") == "value"); delete img; } { // Do a recursive test. // Form an expression and save it. String expr("tImageExpr_tmp.imgexpr + tImageExpr_tmp.img1"); LatticeExprNode node(ImageExprParse::command(expr)); ImageExpr img (node, expr); AlwaysAssertExit (allEQ(img.get(), arr+arr+arr)); AlwaysAssertExit (! img.isPersistent()); img.save ("tImageExpr_tmp:imgexpr2"); AlwaysAssertExit (img.isPersistent()); } { // Reopen the 2nd expression from the file. LatticeBase* latt = ImageOpener::openImageExpr ("tImageExpr_tmp:imgexpr2"); ImageExpr* img = dynamic_cast*>(latt); AlwaysAssertExit (img != 0); AlwaysAssertExit (allEQ(img->get(), arr+arr+arr)); AlwaysAssertExit (img->isPersistent()); delete img; AlwaysAssertExit (ImageExprParse::getImageNames().size() == 2 && ImageExprParse::getImageNames()[0] == "tImageExpr_tmp.imgexpr" && ImageExprParse::getImageNames()[1] == "tImageExpr_tmp.img1"); } Block nodes; { cout<<"try as expr"<* img = dynamic_cast*>(latt); AlwaysAssertExit (img != 0); AlwaysAssertExit (allEQ(img->get(), arr+arr+arr)); AlwaysAssertExit (! img->isPersistent()); delete img; cout<<"done try as expr"< #include #include #include #include using namespace casacore; int main() { try { { PagedImage a(IPosition(4, 20, 20, 1, 20), CoordinateUtil::defaultCoords4D(), "A.im"); PagedImage b(IPosition(4, 20, 20, 1, 1), CoordinateUtil::defaultCoords4D(), "B.im"); } ImageRegion* reg = 0; reg = ImageRegion::fromLatticeExpression("(A.im + B.im) > 0"); delete reg; reg = ImageRegion::fromLatticeExpression("A.im > 0 && B.im < 0"); delete reg; } catch (const std::exception& x) { std::cout << x.what() << std::endl; } } casacore-3.7.1/images/Images/test/tImageExpr2Gram.cc000066400000000000000000000314451476623553700222310ustar00rootroot00000000000000//# tImageExpr2Gram.cc: Test program for WC regions in image expression parser //# Copyright (C) 1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main (int argc, const char* argv[]) { Bool foundError = False; try { Input inp(1); inp.version(" "); inp.create("nx", "10", "Number of pixels along the x-axis", "int"); inp.create("ny", "10", "Number of pixels along the y-axis", "int"); inp.readArguments(argc, argv); const uInt nx=inp.getInt("nx"); const uInt ny=inp.getInt("ny"); IPosition shape(2, nx, ny); Slicer section(IPosition(2,0), shape); Array arr(shape); indgen (arr); Array arrm1, arrm2; arrm1 = arr; arrm2 = arr; Array m1; Array m2; { PagedImage image (shape, CoordinateUtil::defaultCoords2D(), "tImageExpr2Gram_tmp.img"); image.put (arr); // Define 2 masks for the image and make the first one the default. ImageRegion maskreg1 = image.makeMask ("mask1", True, True); ImageRegion maskreg2 = image.makeMask ("mask2", True, False); LCRegion& mask1 = maskreg1.asMask(); LCRegion& mask2 = maskreg2.asMask(); Matrix mask(shape); mask = True; mask(0,0) = False; arrm1(IPosition(2,0,0)) = -1; mask1.put (mask); m1 = mask; mask = True; mask(0,1) = False; mask(1,1) = False; arrm2(IPosition(2,0,1)) = -1; arrm2(IPosition(2,1,1)) = -1; mask2.put (mask); m2 = mask; } PagedImage image ("tImageExpr2Gram_tmp.img"); Block temps(1); temps[0] = LatticeExprNode(image); PtrBlock tempRegs(1); tempRegs[0] = new ImageRegion (WCBox(LCBox(shape), image.coordinates())); { cout << endl; cout << "Expr: $1" << endl; LatticeExpr expr (ImageExprParse::command ("$1", temps, tempRegs)); Array result; expr.get (result); if (! allEQ (result, arr)) { cout << "Result should be " << arr << endl; cout << "Result is " << result << endl; foundError = True; } } { cout << endl; cout << "Expr: $1[$region || $region && $region]" << endl; LatticeExpr expr (ImageExprParse::command ("$1[$R1 || $r1 && $R1]", temps, tempRegs)); Array result; expr.get (result); if (! allEQ (result, arr)) { cout << "Result should be " << arr << endl; cout << "Result is " << result << endl; foundError = True; } } { cout << endl; cout << "Expr: nelements($1)" << endl; LatticeExprNode expr (ImageExprParse::command ("nelements($1)", temps, tempRegs)); Double result = expr.getDouble(); if (result != shape.product()-1) { cout << "Result should be " << shape.product()-1 << endl; cout << "Result is " << result << endl; foundError = True; } } { cout << endl; cout << "Expr: ndim('tImageExpr2Gram_tmp.img::mask1')" << endl; LatticeExprNode expr (ImageExprParse::command ("ndim('tImageExpr2Gram_tmp.img::mask1')", temps, tempRegs)); Float result = expr.getFloat(); if (result != shape.nelements()) { cout << "Result should be " << shape.nelements() << endl; cout << "Result is " << result << endl; foundError = True; } } { cout << endl; cout << "Expr: ndim($R1)" << endl; LatticeExprNode expr (ImageExprParse::command ("ndim($R1)", temps, tempRegs)); Float result = expr.getFloat(); if (result != shape.nelements()) { cout << "Result should be " << shape.nelements() << endl; cout << "Result is " << result << endl; foundError = True; } } { cout << endl; cout << "Expr: any('tImageExpr2Gram_tmp.img::mask2')" << endl; LatticeExprNode expr (ImageExprParse::command ("any('tImageExpr2Gram_tmp.img::mask2')", temps, tempRegs)); Bool result = expr.getBool(); if (!result) { cout << "Result should be " << True << endl; cout << "Result is " << result << endl; foundError = True; } } { cout << endl; cout << "Expr: all('tImageExpr2Gram_tmp.img::mask2')" << endl; LatticeExprNode expr (ImageExprParse::command ("all('tImageExpr2Gram_tmp.img::mask2')", temps, tempRegs)); Bool result = expr.getBool(); if (result) { cout << "Result should be " << False << endl; cout << "Result is " << result << endl; foundError = True; } } { cout << endl; cout << "Expr: ntrue('tImageExpr2Gram_tmp.img::mask1')" << endl; LatticeExprNode expr (ImageExprParse::command ("ntrue('tImageExpr2Gram_tmp.img::mask1')", temps, tempRegs)); Double result = expr.getDouble(); if (result != shape.product()-1) { cout << "Result should be " << shape.product()-1 << endl; cout << "Result is " << result << endl; foundError = True; } } { cout << endl; cout << "Expr: nfalse('tImageExpr2Gram_tmp.img::mask1')" << endl; LatticeExprNode expr (ImageExprParse::command ("nfalse('tImageExpr2Gram_tmp.img::mask1')", temps, tempRegs)); Double result = expr.getDouble(); if (result != 1) { cout << "Result should be " << 1 << endl; cout << "Result is " << result << endl; foundError = True; } } { cout << endl; cout << "Expr: nelements('tImageExpr2Gram_tmp.img::mask1')" << endl; LatticeExprNode expr (ImageExprParse::command ("nelements('tImageExpr2Gram_tmp.img::mask1')", temps, tempRegs)); Double result = expr.getDouble(); if (result != shape.product()) { cout << "Result should be " << shape.product() << endl; cout << "Result is " << result << endl; foundError = True; } } { cout << endl; cout << "Expr: 'tImageExpr2Gram_tmp.img::mask2' == tImageExpr2Gram_tmp.img::mask2" << endl; LatticeExprNode expr (ImageExprParse::command ("'tImageExpr2Gram_tmp.img::mask2' == tImageExpr2Gram_tmp.img::mask2", temps, tempRegs)); LELArray result(shape); expr.eval (result, section); if (! allEQ (result.value(), True)) { cout << "Result should be " << m2 << endl; cout << "Result is " << result.value() << endl; foundError = True; } } { cout << endl; cout << "Expr: 'tImageExpr2Gram_tmp.img::mask2' && " "tImageExpr2Gram_tmp.img::mask1 && " "(tImageExpr2Gram_tmp.img::mask2==" "tImageExpr2Gram_tmp.img::mask2)" << endl; LatticeExprNode expr (ImageExprParse::command ("'tImageExpr2Gram_tmp.img::mask2' &&" "tImageExpr2Gram_tmp.img::mask1 && " "(tImageExpr2Gram_tmp.img::mask1==" "tImageExpr2Gram_tmp.img::mask2)", temps, tempRegs)); LELArray result(shape); expr.eval (result, section); if (! allEQ (result.value(), m1&&m2)) { cout << "Result should be " << False << endl; cout << "Result is " << result.value() << endl; foundError = True; } } { cout << endl; cout << "Expr: iif ('tImageExpr2Gram_tmp.img::mask2', " "tImageExpr2Gram_tmp.img,-1)" << endl; LatticeExpr expr (ImageExprParse::command ("iif ('tImageExpr2Gram_tmp.img::mask2', " "tImageExpr2Gram_tmp.img,-1)", temps, tempRegs)); Array result; expr.get (result); if (! allEQ (result, arrm2)) { cout << "Result should be " << arr << endl; cout << "Result is " << result << endl; foundError = True; } } { cout << endl; cout << "Expr: nelements('tImageExpr2Gram_tmp.img')" << endl; LatticeExprNode expr (ImageExprParse::command ("nelements('tImageExpr2Gram_tmp.img')", temps, tempRegs)); Double result = expr.getDouble(); if (result != shape.product()-1) { cout << "Result should be " << shape.product()-1 << endl; cout << "Result is " << result << endl; foundError = True; } } { cout << endl; cout << "Expr: nelements('tImageExpr2Gram_tmp.img:nomask')" << endl; LatticeExprNode expr (ImageExprParse::command ("nelements('tImageExpr2Gram_tmp.img:nomask')", temps, tempRegs)); Double result = expr.getDouble(); if (result != shape.product()) { cout << "Result should be " << shape.product() << endl; cout << "Result is " << result << endl; foundError = True; } } { cout << endl; cout << "Expr: nelements(tImageExpr2Gram_tmp.img:mask2)" << endl; LatticeExprNode expr (ImageExprParse::command ("nelements(tImageExpr2Gram_tmp.img:mask2)", temps, tempRegs)); Double result = expr.getDouble(); if (result != shape.product()-2) { cout << "Result should be " << shape.product()-2 << endl; cout << "Result is " << result << endl; foundError = True; } } { cout << endl; cout << "Expr: nelements(tImageExpr2Gram_tmp.img:mask2)" << endl; LatticeExprNode expr (ImageExprParse::command ("nelements(tImageExpr2Gram_tmp.img:mask2)", temps, tempRegs)); Double result = expr.getDouble(); if (result != shape.product()-2) { cout << "Result should be " << shape.product()-2 << endl; cout << "Result is " << result << endl; foundError = True; } } { cout << endl; cout << "Expr: nelements(tImageExpr2Gram_tmp.img[mask2])" << endl; LatticeExprNode expr (ImageExprParse::command ("nelements(tImageExpr2Gram_tmp.img[mask2])", temps, tempRegs)); Double result = expr.getDouble(); if (result != shape.product()-3) { cout << "Result should be " << shape.product()-3 << endl; cout << "Result is " << result << endl; foundError = True; } } { cout << endl; cout << "Expr: nelements(tImageExpr2Gram_tmp.img[::mask2])" << endl; LatticeExprNode expr (ImageExprParse::command ("nelements(tImageExpr2Gram_tmp.img[::mask2])", temps, tempRegs)); Double result = expr.getDouble(); if (result != shape.product()-3) { cout << "Result should be " << shape.product()-3 << endl; cout << "Result is " << result << endl; foundError = True; } } { cout << endl; cout << "Expr: nelements(tImageExpr2Gram_tmp.img" "[tImageExpr2Gram_tmp.img::mask2])" << endl; LatticeExprNode expr (ImageExprParse::command ("nelements(tImageExpr2Gram_tmp.img" "[tImageExpr2Gram_tmp.img::mask2])", temps, tempRegs)); Double result = expr.getDouble(); if (result != shape.product()-3) { cout << "Result should be " << shape.product()-3 << endl; cout << "Result is " << result << endl; foundError = True; } } for (uInt i=0; i #include #include #include #include #include #include #include #include #include #include #include int main (int argc, const char* argv[]) { Bool foundError = False; try { Input inp(1); inp.version(" "); inp.create("nx", "10", "Number of pixels along the x-axis", "int"); inp.create("ny", "11", "Number of pixels along the y-axis", "int"); inp.create("nz", "12", "Number of pixels along the z-axis", "int"); inp.readArguments(argc, argv); const uInt nx=inp.getInt("nx"); const uInt ny=inp.getInt("ny"); const uInt nz=inp.getInt("nz"); IPosition shape2(2, nx, ny); IPosition shape2b(3, nx, ny, 1); IPosition shape3(3, nx, ny, nz); Array arr2(shape2); indgen (arr2); Array arr2b(shape2b); indgen (arr2b); Array arr3(shape3); indgen (arr3); Array arr2a(shape3); { for (uInt i=0; i image2 (shape2, CoordinateUtil::defaultCoords2D(), "tImageExpr3Gram_tmp.img2"); image2.put (arr2); PagedImage image2b (shape2b, CoordinateUtil::defaultCoords3D(), "tImageExpr3Gram_tmp.img2b"); image2b.put (arr2b); PagedImage image3 (shape3, CoordinateUtil::defaultCoords3D(), "tImageExpr3Gram_tmp.img3"); image3.put (arr3); } { cout << endl; cout << "Expr: image3-image2" << endl; LatticeExpr expr (ImageExprParse::command ("tImageExpr3Gram_tmp.img3 - tImageExpr3Gram_tmp.img2")); Array result; expr.get (result); if (! allEQ (result, arr3-arr2a)) { cout << "Result should be " << arr3-arr2a << endl; cout << "Result is " << result << endl; foundError = True; } } { cout << endl; cout << "Expr: image2-image3" << endl; LatticeExpr expr (ImageExprParse::command ("tImageExpr3Gram_tmp.img2 - tImageExpr3Gram_tmp.img3")); Array result; expr.get (result); if (! allEQ (result, arr2a-arr3)) { cout << "Result should be " << arr2a-arr3 << endl; cout << "Result is " << result << endl; foundError = True; } } { cout << endl; cout << "Expr: image3-image2b" << endl; LatticeExpr expr (ImageExprParse::command ("tImageExpr3Gram_tmp.img3 - tImageExpr3Gram_tmp.img2b")); Array result; expr.get (result); if (! allEQ (result, arr3-arr2a)) { cout << "Result should be " << arr3-arr2a << endl; cout << "Result is " << result << endl; foundError = True; } } { cout << endl; cout << "Expr: image2b-image3" << endl; LatticeExpr expr (ImageExprParse::command ("tImageExpr3Gram_tmp.img2b - tImageExpr3Gram_tmp.img3")); Array result; expr.get (result); if (! allEQ (result, arr2a-arr3)) { cout << "Result should be " << arr2a-arr3 << endl; cout << "Result is " << result << endl; foundError = True; } } { cout << endl; cout << "Expr: image2b-image2" << endl; LatticeExpr expr (ImageExprParse::command ("tImageExpr3Gram_tmp.img2b - tImageExpr3Gram_tmp.img2")); Array result; expr.get (result); if (! allEQ (result, arr2b-arr2b)) { cout << "Result should be " << arr2b-arr2b << endl; cout << "Result is " << result << endl; foundError = True; } } { cout << endl; cout << "Expr: image2-image2b" << endl; LatticeExpr expr (ImageExprParse::command ("tImageExpr3Gram_tmp.img2 - tImageExpr3Gram_tmp.img2b")); Array result; expr.get (result); if (! allEQ (result, arr2b-arr2b)) { cout << "Result should be " << arr2b-arr2b << endl; cout << "Result is " << result << endl; foundError = True; } } } catch (std::exception& x) { cerr << "aipserror: error " << x.what() << endl; foundError = True; } if (foundError) { return 1; } return 0; } casacore-3.7.1/images/Images/test/tImageExprGram.cc000066400000000000000000000546231476623553700221520ustar00rootroot00000000000000//# tImageExprGram.cc: Test program for image expression parser //# Copyright (C) 1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //# This function simulates the same function in DOImage2.cc. String substituteOID (Block& nodes, String& exprName, const String& expr) { nodes.resize (0, False, True); exprName = expr; return expr; } void makeRegionBlock (PtrBlock& regions, const Record&, LogIO&) { for (uInt j=0; j. if (expr.empty()) { os << "You must specify an expression" << LogIO::EXCEPTION; } Block temps; String exprName; String newexpr = substituteOID (temps, exprName, expr); PtrBlock tempRegs; makeRegionBlock (tempRegs, regions, os); LatticeExprNode node = ImageExprParse::command (newexpr, temps, tempRegs); // Delete the ImageRegions (by using an empty GlishRecord). makeRegionBlock (tempRegs, Record(), os); // Make the ImageExpr object. It will throw an exception if there // are no true coordinates LatticeExpr latEx(node); ImageInterface* pImage = new ImageExpr(latEx, exprName); if (pImage==0) { os << "Failed to create PagedImage" << LogIO::EXCEPTION; } } int main (int argc, const char* argv[]) { Bool foundError = False; try { cout << ">>>" << endl; Input inp(1); inp.version("1.0"); inp.create("nx", "10", "Number of pixels along the x-axis", "int"); inp.create("ny", "10", "Number of pixels along the y-axis", "int"); inp.readArguments(argc, argv); cout << "<<<" << endl; const uInt nx=inp.getInt("nx"); const uInt ny=inp.getInt("ny"); Double aVal = 0.0; Double bVal = 1.0; Double cVal = 2.0; Double dVal = 3.0; Double eVal = 4.0; Double fVal = 5.0; Double gVal = 6.0; Double hVal = 7.0; Bool aBoolVal = False; Bool bBoolVal = False; IPosition shape(2, nx, ny); TiledShape tshp(shape, IPosition(2,(nx+1)/2, (ny+1)/2)); PagedArray a(tshp, "paa"); PagedArray aBool(tshp, "paab"); a.set(aVal); aBool.set(aBoolVal); { PagedArray b(tshp, "b"); PagedArray c(tshp, "c"); PagedArray d(tshp, "d"); PagedArray e(tshp, "e"); PagedArray f(tshp, "f"); PagedArray g(tshp, "g"); PagedArray h(tshp, "h"); PagedArray bBool(tshp, "bBool"); PagedArray kpa(tshp, "kpa"); b.set(bVal); c.set(cVal); d.set(dVal); e.set(eVal); f.set(fVal); g.set(gVal); h.set(hVal); bBool.set(bBoolVal); Array arr(shape); indgen(arr); kpa.put(arr); } Array aArr(shape); Array aBoolArr(shape); { cout << endl; try { doExpr ("xxx", Record()); } catch (std::exception& x) { cout << x.what() << endl; } try { LatticeExpr expr (ImageExprParse::command ("b/a1")); } catch (std::exception& x) { cout << x.what() << endl; } try { LatticeExpr expr (ImageExprParse::command ("a1/b")); } catch (std::exception& x) { cout << x.what() << endl; } try { LatticeExpr expr (ImageExprParse::command ("b/b*")); } catch (std::exception& x) { cout << x.what() << endl; } try { LatticeExpr expr (ImageExprParse::command ("min(b,b,b)")); } catch (std::exception& x) { cout << x.what() << endl; } } { cout << endl; cout << "Expr: a = 1" << endl; LatticeExpr expr (ImageExprParse::command ("1.0")); cout << "Images used: " << ImageExprParse::getImageNames() << endl; a.copyData(expr); a.getSlice(aArr, IPosition(aArr.ndim(),0), shape, IPosition(aArr.ndim(),1)); Double result = 1.0; if (! allEQ (aArr, result)) { cout << "Result should be " << result << endl; cout << "Result is " << aArr << endl; foundError = True; } } { cout << "Expr: a = b" << endl; LatticeExpr expr(ImageExprParse::command ("b")); cout << "Images used: " << ImageExprParse::getImageNames() << endl; a.copyData(expr); a.getSlice(aArr, IPosition(aArr.ndim(),0), shape, IPosition(aArr.ndim(),1)); Double result = bVal; if (! allEQ (aArr, result)) { cout << "Result should be " << result << endl; cout << "Result is " << aArr << endl; foundError = True; } } { cout << "Expr: a = sin(\\c)" << endl; LatticeExpr expr(ImageExprParse::command ("sin(\\c)")); cout << "Images used: " << ImageExprParse::getImageNames() << endl; a.copyData(expr); a.getSlice(aArr, IPosition(aArr.ndim(),0), shape, IPosition(aArr.ndim(),1)); Double result = sin(cVal); if (! allEQ (aArr, sin(cVal))) { cout << "Result should be " << result << endl; cout << "Result is " << aArr << endl; foundError = True; } } { cout << "Expr: a = 'c'+2" << endl; LatticeExpr expr(ImageExprParse::command ("'c'+2")); cout << "Images used: " << ImageExprParse::getImageNames() << endl; a.copyData(expr); a.getSlice(aArr, IPosition(aArr.ndim(),0), shape, IPosition(aArr.ndim(),1)); Double result = cVal+2; if (! allEQ (aArr, result)) { cout << "Result should be " << result << endl; cout << "Result is " << aArr << endl; foundError = True; } } { cout << "Expr: a = b+('c'+d)[region] (using $n notation)" << endl; PagedArray b("b"); PagedArray c("c"); PagedArray d("d"); Block temps(3); temps[0] = LatticeExprNode(b); temps[1] = LatticeExprNode(c); temps[2] = LatticeExprNode(d); PtrBlock regions(1); regions[0] = new ImageRegion(LCBox(shape)); LatticeExpr expr(ImageExprParse::command ("$1 + ($2 + $3)[$R1]", temps, regions)); delete regions[0]; a.copyData(expr); a.getSlice(aArr, IPosition(aArr.ndim(),0), shape, IPosition(aArr.ndim(),1)); Double result = bVal + cVal + dVal; if (! allEQ (aArr, result)) { cout << "Result should be " << result << endl; cout << "Result is " << aArr << endl; foundError = True; } } { cout << "Expr: a = nelements(b[$region]" << endl; Block temps(0); PtrBlock regions(1); Matrix mask(shape-1); mask = False; mask(0,0) = True; regions[0] = new ImageRegion(LCPixelSet(mask,LCBox(IPosition(2,0), shape-2, shape))); LatticeExpr expr(ImageExprParse::command ("nelements(b[$R1])", temps, regions)); delete regions[0]; a.copyData(expr); a.getSlice(aArr, IPosition(aArr.ndim(),0), shape, IPosition(aArr.ndim(),1)); Double result = 1; if (! allEQ (aArr, result)) { cout << "Result should be " << result << endl; cout << "Result is " << aArr << endl; foundError = True; } } { cout << "Expr: a = nelements(b[$region1 || $region2) - " "length(b[$region1],0)" << endl; Block temps(0); PtrBlock regions(2); Matrix mask1(shape-1); Matrix mask2(shape-1); mask1 = False; mask2 = False; mask1(0,0) = True; mask2(shape-2) = True; regions[0] = new ImageRegion(LCPixelSet(mask1,LCBox(IPosition(2,0), shape-2, shape))); regions[1] = new ImageRegion(LCPixelSet(mask2,LCBox(IPosition(2,0), shape-2, shape))); LatticeExpr expr(ImageExprParse::command ("nelements(b[$R1 || $R2]) - " "length(b[$R1],0)", temps, regions)); cout << "Images used: " << ImageExprParse::getImageNames() << endl; delete regions[0]; delete regions[1]; a.copyData(expr); a.getSlice(aArr, IPosition(aArr.ndim(),0), shape, IPosition(aArr.ndim(),1)); Double result = 2 - (shape(0)-1); if (! allEQ (aArr, result)) { cout << "Result should be " << result << endl; cout << "Result is " << aArr << endl; foundError = True; } } { cout << "Expr: a = 3.5*b + cos('c')-10/min('c',d)*-e*log(b)-pi()" << endl; LatticeExpr expr( ImageExprParse::command ("(3.5*b) + (cos('c')) - (10/min('c',d)*(-e)*log(b)) - (pi()) ")); cout << "Images used: " << ImageExprParse::getImageNames() << endl; a.copyData(expr); a.getSlice(aArr, IPosition(aArr.ndim(),0), shape, IPosition(aArr.ndim(),1)); Double result = 3.5*bVal + cos(cVal) - 10/min(cVal,dVal)*-eVal*log(bVal) - M_PI; if (! allNear (aArr, result, 1.0e-10)) { cout << "Result should be " << result << endl; cout << "Result is " << aArr << endl; foundError = True; } } { cout << "Expr: a = (b+'c'-d/2.0*-b) + pi()" << endl; LatticeExpr expr(ImageExprParse::command ("(b+'c'-d/2.0*-b)+pi()")); a.copyData(expr); a.getSlice(aArr, IPosition(aArr.ndim(),0), shape, IPosition(aArr.ndim(),1)); Double result = (bVal + cVal - dVal / 2*-bVal) + M_PI; if (! allEQ (aArr, result)) { cout << "Result should be " << result << endl; cout << "Result is " << aArr << endl; foundError = True; } } { cout << "Expr: a = pow('c',d)" << endl; LatticeExpr expr(ImageExprParse::command ("pow('c',d)")); a.copyData(expr); a.getSlice(aArr, IPosition(aArr.ndim(),0), shape, IPosition(aArr.ndim(),1)); Double result = pow(cVal,dVal); if (! allEQ (aArr, result)) { cout << "Result should be " << result << endl; cout << "Result is " << aArr << endl; foundError = True; } } { cout << "Expr: a = fmod('c'*2,d)" << endl; LatticeExpr expr(ImageExprParse::command ("fmod('c'*2,d)")); a.copyData(expr); a.getSlice(aArr, IPosition(aArr.ndim(),0), shape, IPosition(aArr.ndim(),1)); Double result = fmod(cVal*2,dVal); if (! allEQ (aArr, result)) { cout << "Result should be " << result << endl; cout << "Result is " << aArr << endl; foundError = True; } } { cout << "Expr: a = pow('c',2.3)" << endl; LatticeExpr expr(ImageExprParse::command ("pow('c',2.3)")); a.copyData(expr); a.getSlice(aArr, IPosition(aArr.ndim(),0), shape, IPosition(aArr.ndim(),1)); Double result = pow(cVal,2.3); if (! allEQ (aArr, result)) { cout << "Result should be " << result << endl; cout << "Result is " << aArr << endl; foundError = True; } } { cout << "Expr: a = cos(b)" << endl; LatticeExpr expr(ImageExprParse::command ("cos(b)")); a.copyData(expr); a.getSlice(aArr, IPosition(aArr.ndim(),0), shape, IPosition(aArr.ndim(),1)); Double result = cos(bVal); if (! allEQ (aArr, result)) { cout << "Result should be " << result << endl; cout << "Result is " << aArr << endl; foundError = True; } } { cout << "Expr: a = cos(sin(b))" << endl; LatticeExpr expr (ImageExprParse::command ("cos(sin(b))")); a.copyData(expr); a.getSlice(aArr, IPosition(aArr.ndim(),0), shape, IPosition(aArr.ndim(),1)); Double result = cos(sin(bVal)); if (! allEQ (aArr, result)) { cout << "Result should be " << result << endl; cout << "Result is " << aArr << endl; foundError = True; } } { cout << "Expr: a = ntrue(b>=1)" << endl; LatticeExpr expr(ImageExprParse::command ("ntrue(b>=1)")); a.copyData(expr); a.getSlice(aArr, IPosition(aArr.ndim(),0), shape, IPosition(aArr.ndim(),1)); Double result = shape.product(); if (! allEQ (aArr, result)) { cout << "Result should be " << result << endl; cout << "Result is " << aArr << endl; foundError = True; } } { cout << "Expr: aBool = T||F" << endl; LatticeExpr expr(ImageExprParse::command ("T||F")); aBool.copyData(expr); aBool.getSlice(aBoolArr, IPosition(aBoolArr.ndim(),0), shape, IPosition(aBoolArr.ndim(),1)); Bool result = (True||False); if (! allEQ (aBoolArr, result)) { cout << "Result should be " << result << endl; cout << "Result is " << aBoolArr << endl; foundError = True; } } { cout << "Expr: aBool = !bBool" << endl; LatticeExpr expr(ImageExprParse::command ("!bBool")); aBool.copyData(expr); aBool.getSlice(aBoolArr, IPosition(aBoolArr.ndim(),0), shape, IPosition(aBoolArr.ndim(),1)); Bool result = (!bBoolVal); if (! allEQ (aBoolArr, result)) { cout << "Result should be " << result << endl; cout << "Result is " << aBoolArr << endl; foundError = True; } } { cout << "Expr: a = sum(e)/nelements(b) + min('c') + max('c', mean('c'+d))" << endl; LatticeExpr expr(ImageExprParse::command ("sum(e)/nelements(b) + min('c') + max('c', mean('c'+d))")); a.copyData(expr); a.getSlice(aArr, IPosition(aArr.ndim(),0), shape, IPosition(aArr.ndim(),1)); Double result = eVal+cVal+(cVal+dVal); if (! allEQ (aArr, result)) { cout << "Result should be " << result << endl; cout << "Result is " << aArr << endl; foundError = True; } } { cout << "Expr: a = sum(e[bBool])" << endl; LatticeExpr expr(ImageExprParse::command ("sum(e[bBool])")); a.copyData(expr); a.getSlice(aArr, IPosition(aArr.ndim(),0), shape, IPosition(aArr.ndim(),1)); Double result = (bBoolVal ? aArr.nelements()*eVal : 0); if (! allEQ (aArr, result)) { cout << "Result should be " << result << endl; cout << "Result is " << aArr << endl; foundError = True; } } { cout << "Expr: a = sum((e + sum(f[b>0]*c)/d)[!bBool])" << endl; LatticeExpr expr(ImageExprParse::command ("sum((e + sum(f[b>0]*c)/d)[!bBool])")); a.copyData(expr); a.getSlice(aArr, IPosition(aArr.ndim(),0), shape, IPosition(aArr.ndim(),1)); Double result = (bVal>0 ? aArr.nelements()*fVal*cVal/dVal : 0); result = (bBoolVal ? 0 : aArr.nelements()*(eVal+result)); if (! allNear (aArr, result, 1e-10)) { cout << "Result should be " << result << endl; cout << "Result is " << aArr << endl; foundError = True; } } { cout << "Expr: a = median(b)" << endl; LatticeExpr expr(ImageExprParse::command ("median(b)")); a.copyData(expr); a.getSlice(aArr, IPosition(aArr.ndim(),0), shape, IPosition(aArr.ndim(),1)); Double result = bVal; if (! allNear (aArr, result, 1e-10)) { cout << "Result should be " << result << endl; cout << "Result is " << aArr << endl; foundError = True; } } { cout << "Expr: a = fractile(b,0.2)" << endl; LatticeExpr expr(ImageExprParse::command ("fractile(b,0.2)")); a.copyData(expr); a.getSlice(aArr, IPosition(aArr.ndim(),0), shape, IPosition(aArr.ndim(),1)); Double result = bVal; if (! allNear (aArr, result, 1e-10)) { cout << "Result should be " << result << endl; cout << "Result is " << aArr << endl; foundError = True; } } { cout << "Expr: a = fractilerange(b,0.2)" << endl; LatticeExpr expr(ImageExprParse::command ("fractilerange(b,0.2)")); a.copyData(expr); a.getSlice(aArr, IPosition(aArr.ndim(),0), shape, IPosition(aArr.ndim(),1)); Double result = 0.0; if (! allNear (aArr, result, 1e-10)) { cout << "Result should be " << result << endl; cout << "Result is " << aArr << endl; foundError = True; } } { cout << "Expr: a = fractilerange(b,0.2,0.6)" << endl; LatticeExpr expr(ImageExprParse::command ("fractilerange(b,0.2,0.6)")); a.copyData(expr); a.getSlice(aArr, IPosition(aArr.ndim(),0), shape, IPosition(aArr.ndim(),1)); Double result = 0.0; if (! allNear (aArr, result, 1e-10)) { cout << "Result should be " << result << endl; cout << "Result is " << aArr << endl; foundError = True; } } { { PagedArray ap (tshp, "a"); ap.copyData (a); } cout << "Expr: a = min(a+10,5)" << endl; LatticeExpr expr(ImageExprParse::command ("min(a+10,5)")); a.copyData(expr); a.getSlice(aArr, IPosition(aArr.ndim(),0), shape, IPosition(aArr.ndim(),1)); Double result = 5; if (! allEQ (aArr, result)) { cout << "Result should be " << result << endl; cout << "Result is " << aArr << endl; foundError = True; } } { cout << "Expr: a = b+sum(kpa[indexin(0,[0:1,7,3:6:2])])" << endl; LatticeExpr expr(ImageExprParse::command ("b+sum(kpa[indexin(0,[0:1,7,3:6:2])])")); a.copyData(expr); a.getSlice(aArr, IPosition(aArr.ndim(),0), shape, IPosition(aArr.ndim(),1)); if (shape(0) > 7) { Int n = shape(1); Double result = 1 + (0+1+7+3+5)*n + 5*shape(0)*n*(n-1)/2; if (! allNear (aArr, result, 1e-10)) { cout << "Result should be " << result << endl; cout << "Result is " << aArr << endl; foundError = True; } } } { cout << "Expr: a = b+sum(kpa[indexnotin(1,[0:1,9,3:6:3,9])])" << endl; LatticeExpr expr(ImageExprParse::command ("b+sum(kpa[indexnotin(1,[0:1,9,3:6:3,9])])")); a.copyData(expr); a.getSlice(aArr, IPosition(aArr.ndim(),0), shape, IPosition(aArr.ndim(),1)); if (shape(1) > 9) { Int n = shape(0); Double result = 1 + (0+1+9+3+6)*n*n + 5*n*(n-1)/2; n *= shape(1); result = n*(n-1)/2 - result + 2*1; if (! allNear (aArr, result, 1e-10)) { cout << "Result should be " << result << endl; cout << "Result is " << aArr << endl; foundError = True; } } } { cout << "Expr: a = b+sum(kpa[index1 in [0:1,9,3:6:3,9]])" << endl; LatticeExpr expr(ImageExprParse::command ("b+sum(kpa[index1 in [0:1,9,3:6:3,9]])")); a.copyData(expr); a.getSlice(aArr, IPosition(aArr.ndim(),0), shape, IPosition(aArr.ndim(),1)); if (shape(1) > 9) { Int n = shape(0); Double result = 1 + (0+1+9+3+6)*n*n + 5*n*(n-1)/2; if (! allNear (aArr, result, 1e-10)) { cout << "Result should be " << result << endl; cout << "Result is " << aArr << endl; foundError = True; } } } { cout << "Expr: a = b+sum(kpa[index1 not in [0:1,9,3:6:3,9]])" << endl; LatticeExpr expr(ImageExprParse::command ("b+sum(kpa[index1 not in [0:1,9,3:6:3,9]])")); a.copyData(expr); a.getSlice(aArr, IPosition(aArr.ndim(),0), shape, IPosition(aArr.ndim(),1)); if (shape(1) > 9) { Int n = shape(0); Double result = 1 + (0+1+9+3+6)*n*n + 5*n*(n-1)/2; n *= shape(1); result = n*(n-1)/2 - result + 2*1; if (! allNear (aArr, result, 1e-10)) { cout << "Result should be " << result << endl; cout << "Result is " << aArr << endl; foundError = True; } } } { cout << "Expr: a = rebin(b,[1,2/2])" << endl; LatticeExpr expr(ImageExprParse::command ("rebin(b,[1,2/2])")); a.copyData(expr); a.getSlice(aArr, IPosition(aArr.ndim(),0), shape, IPosition(aArr.ndim(),1)); Double result = bVal; if (! allEQ (aArr, result)) { cout << "Result should be " << result << endl; cout << "Result is " << aArr << endl; foundError = True; } } cout << endl; } catch (std::exception& x) { cerr << "aipserror: error " << x.what() << endl; foundError = True; } // Delete all created tables (if they exist). Table tab; if (Table::isReadable("a")) tab = Table ("a", Table::Delete); if (Table::isReadable("b")) tab = Table ("b", Table::Delete); if (Table::isReadable("c")) tab = Table ("c", Table::Delete); if (Table::isReadable("d")) tab = Table ("d", Table::Delete); if (Table::isReadable("e")) tab = Table ("e", Table::Delete); if (Table::isReadable("f")) tab = Table ("f", Table::Delete); if (Table::isReadable("g")) tab = Table ("g", Table::Delete); if (Table::isReadable("h")) tab = Table ("h", Table::Delete); if (Table::isReadable("kpa")) tab = Table ("kpa", Table::Delete); if (Table::isReadable("bBool")) tab = Table ("bBool", Table::Delete); if (Table::isReadable("paa")) tab = Table ("paa", Table::Delete); if (Table::isReadable("paab")) tab = Table ("paab", Table::Delete); if (foundError) { return 1; } return 0; } casacore-3.7.1/images/Images/test/tImageExprGram.out000066400000000000000000000031071476623553700223630ustar00rootroot00000000000000>>> /export/home/gvd/aips++/sun4sol_egcs/bindbg/tImageExprGram: Version 1.0 <<< ImageExprParse: 'xxx' is an unknown lattice or image or it is an unqualified region Scanned so far: xxx ImageExprParse: 'a1' is an unknown lattice, image, or region Scanned so far: b/a1 ImageExprParse: 'a1' is an unknown lattice or image or it is an unqualified region Scanned so far: a1/ Image Expression: Parse error at or near '' Scanned so far: b/b* 3-argument function min is unknown Scanned so far: min(b,b,b) Expr: a = 1 Images used: [] Expr: a = b Images used: [b] Expr: a = sin(\c) Images used: [c] Expr: a = 'c'+2 Images used: [c] Expr: a = b+('c'+d)[region] (using $n notation) Expr: a = nelements(b[$region] Expr: a = nelements(b[$region1 || $region2) - length(b[$region1],0) Images used: [b,b] Expr: a = 3.5*b + cos('c')-10/min('c',d)*-e*log(b)-pi() Images used: [b,c,c,d,e,b] Expr: a = (b+'c'-d/2.0*-b) + pi() Expr: a = pow('c',d) Expr: a = fmod('c'*2,d) Expr: a = pow('c',2.3) Expr: a = cos(b) Expr: a = cos(sin(b)) Expr: a = ntrue(b>=1) Expr: aBool = T||F Expr: aBool = !bBool Expr: a = sum(e)/nelements(b) + min('c') + max('c', mean('c'+d)) Expr: a = sum(e[bBool]) Expr: a = sum((e + sum(f[b>0]*c)/d)[!bBool]) Expr: a = median(b) Expr: a = fractile(b,0.2) Expr: a = fractilerange(b,0.2) Expr: a = fractilerange(b,0.2,0.6) Expr: a = min(a+10,5) Expr: a = b+sum(kpa[indexin(0,[0:1,7,3:6:2])]) Expr: a = b+sum(kpa[indexnotin(1,[0:1,9,3:6:3,9])]) Expr: a = b+sum(kpa[index1 in [0:1,9,3:6:3,9]]) Expr: a = b+sum(kpa[index1 not in [0:1,9,3:6:3,9]]) Expr: a = rebin(b,[1,2/2]) casacore-3.7.1/images/Images/test/tImageExprParse.cc000066400000000000000000000033221476623553700223240ustar00rootroot00000000000000//# tImageExprParse.cc //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include int main() { try { Bool thrown = False; try { ImageExprParse::command("''"); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); cout<< "ok"<< endl; } catch (const std::exception& x) { cerr << "Exception caught: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/images/Images/test/tImageExprParse_addDir.cc000066400000000000000000000041161476623553700235750ustar00rootroot00000000000000//# tImageExprParse_addDir.cc: test the PagedImage::addDir function //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include int main() { try { AlwaysAssertExit (ImageExprParse::setAddDir("/c/d", "") == ""); AlwaysAssertExit (ImageExprParse::setAddDir("/c/d", "a/b") == "/c/d/a/b"); AlwaysAssertExit (ImageExprParse::setAddDir("/c/d", "/a/b") == "/a/b"); AlwaysAssertExit (ImageExprParse::setAddDir("/c/d", "$HOME") == "$HOME"); AlwaysAssertExit (ImageExprParse::setAddDir("", "") == ""); AlwaysAssertExit (ImageExprParse::setAddDir("", "a/b") == "a/b"); AlwaysAssertExit (ImageExprParse::setAddDir("", "/a/b") == "/a/b"); AlwaysAssertExit (ImageExprParse::setAddDir("", "$HOME") == "$HOME"); cout<< "ok"<< endl; } catch (std::exception& x) { cerr << "Exception caught: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/images/Images/test/tImageInfo.cc000066400000000000000000000215521476623553700213130ustar00rootroot00000000000000 //# tImageInfo.cc: Miscellaneous information related to an image //# Copyright (C) 1998,1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include void equal (const ImageInfo& ii1, const ImageInfo& ii2) { const GaussianBeam& b1 = ii1.restoringBeam(); const GaussianBeam& b2 = ii2.restoringBeam(); AlwaysAssert(b1 == b2, AipsError); AlwaysAssertExit(ii1.imageType()==ii2.imageType()); AlwaysAssertExit(ii1.objectName()==ii2.objectName()); } int main() { try { // Default constructor ; ImageInfo mii; // // Restoring beam // AlwaysAssert(mii.restoringBeam() == mii.defaultRestoringBeam(), AipsError); GaussianBeam beam; AlwaysAssert(mii.defaultRestoringBeam() == beam, AipsError); // beam = GaussianBeam( Quantum(45.0, "arcsec"), Quantum(45.0, "arcsec"), Quantum(-45.0, "deg") ); mii.setRestoringBeam(beam); AlwaysAssert(mii.restoringBeam() == beam, AipsError); mii.setRestoringBeam(beam); AlwaysAssert(mii.restoringBeam() == beam, AipsError); beam.setMajorMinor(Quantum(1.0, "deg"), beam.getMinor()); mii.setRestoringBeam(beam); AlwaysAssert(mii.restoringBeam() == beam, AipsError); mii.removeRestoringBeam(); AlwaysAssertExit(mii.restoringBeam().isNull()); // // ImageType // for (uInt i=0; i(i); { mii.setImageType(type); AlwaysAssertExit(type==mii.imageType()); } { String typeS = ImageInfo::imageType(type); ImageInfo::ImageTypes type2 = ImageInfo::imageType(typeS); AlwaysAssertExit(type==type2); } } // // ObjectName // { String objectName("PKS133-33"); mii.setObjectName(objectName); AlwaysAssertExit(objectName==mii.objectName()); } // // Copy constructor and assignemnt // mii.setRestoringBeam(beam); mii.setImageType(ImageInfo::SpectralIndex); mii.setObjectName(String("IC4296")); ImageInfo mii2(mii); equal(mii2, mii); // GaussianBeam beam2( Quantum(7.2, "arcsec"), Quantum(3.6, "arcsec"), Quantum(-90.0, "deg") ); mii2.setRestoringBeam(beam2); mii2.setImageType(ImageInfo::Intensity); mii.setObjectName(String("NGC1399")); mii = mii2; equal(mii2, mii); // // Record conversion // Record rec; String error; AlwaysAssertExit(mii.toRecord(error, rec)); ImageInfo mii3; Bool ok = mii3.fromRecord(error, rec); if (!ok) cout << "Error = " << error << endl; equal(mii3, mii); // // FITS // Record header; AlwaysAssertExit(mii3.toFITS(error, header)); // the header delivered by toFITS contains fields as fields, // the header accepted by fromFITS contains fields as subrecords // -- a round trip is therefore not possible directly. // need to construct input record RecordDesc keywordNumRec; keywordNumRec.addField("value", TpDouble); RecordDesc keywordStrRec; keywordStrRec.addField("value", TpString); Record headerb; Record rbmaj(keywordNumRec); Record rbmin(keywordNumRec); Record rbpa(keywordNumRec); Record robject(keywordStrRec); RecordFieldPtr bmajval(rbmaj, 0); RecordFieldPtr bminval(rbmin, 0); RecordFieldPtr bpaval(rbpa, 0); RecordFieldPtr objectval(robject, 0); bmajval.define(0.002); bminval.define(0.001); bpaval.define(-90.); objectval.define("IC4296"); headerb.defineRecord("bmaj", rbmaj); headerb.defineRecord("bmin", rbmin); headerb.defineRecord("bpa", rbpa); headerb.defineRecord("object", robject); // now try to import it ImageInfo mii4; Vector error2; AlwaysAssertExit(mii4.fromFITS(error2, headerb)); equal(mii4, mii3); // output stream // cout << mii3 << endl; cout << mii4 << endl; { // per plane beam tests ImageInfo myinfo; Quantity majAx(5, "arcsec"); Quantity minAx(3, "arcsec"); Quantity pa(60, "deg"); Bool ok = True; try { // no hyper plane beam shape throws exception myinfo.setBeam(1, 1, majAx, minAx, pa); ok = False; } catch (const std::exception& x) { cout << x.what() << endl; } AlwaysAssert(ok, AipsError); myinfo = ImageInfo(); myinfo.setAllBeams(2, 1, GaussianBeam()); try { // inconsistent plane throws exception myinfo.setBeam(2, 1, majAx, minAx, pa); ok = False; } catch (std::exception& x) { cout << "Exception thrown as expected: " << x.what() << endl; } AlwaysAssert(ok, AipsError); myinfo = ImageInfo(); myinfo.setAllBeams(2, 1, GaussianBeam()); try { // incorrect beam spec throws error myinfo.setBeam(0, 0, minAx, majAx, pa); ok = False; } catch (std::exception& x) { cout << "Exception thrown as expected: " << x.what() << endl; } AlwaysAssert(ok, AipsError); myinfo = ImageInfo(); myinfo.setAllBeams(2, 1, GaussianBeam()); myinfo.setBeam(0, 0, majAx, minAx, pa); GaussianBeam beam = myinfo.restoringBeam(0, 0); AlwaysAssert(beam.getMajor() == majAx, AipsError); AlwaysAssert(beam.getMinor() == minAx, AipsError); AlwaysAssert(beam.getPA() == pa, AipsError); Record rec; String err; // not all beams have been set AlwaysAssert(! myinfo.toRecord(error, rec), AipsError); myinfo.setBeam(1, 0, majAx, minAx, pa); AlwaysAssert(myinfo.toRecord(error, rec), AipsError); ImageInfo myinfo2; myinfo2.fromRecord(err, rec); AlwaysAssert(myinfo2.fromRecord(err, rec), AipsError); beam = myinfo2.restoringBeam(1, 0); AlwaysAssert(beam.getMajor() == majAx, AipsError); AlwaysAssert(beam.getMinor() == minAx, AipsError); AlwaysAssert(beam.getPA() == pa, AipsError); cout << "myinfo2 " << myinfo2 << endl; } { ImageBeamSet bset(IPosition(2, 10, 4)); ImageInfo myinfo = ImageInfo(); myinfo.setBeams(bset); ImageBeamSet bset2(IPosition(2, 10, 4)); myinfo.setBeams(bset2); AlwaysAssert(myinfo.getBeamSet() == bset2, AipsError); // check that we can set a different size beam set ImageBeamSet bset3(IPosition(2, 11, 4)); myinfo.setBeams(bset3); AlwaysAssert(myinfo.getBeamSet() == bset3, AipsError); } { cout << "*** Test getBeamAreaInPixels" << endl; ImageInfo myinfo; GaussianBeam beam( Quantity(4, "arcsec"), Quantity(2, "arcsec"), Quantity(30, "deg") ); ImageBeamSet bset(10, 4, beam); bset.setBeam(2, 2, GaussianBeam( Quantity(5, "arcsec"), Quantity(3, "arcsec"), Quantity(30, "deg") ) ); myinfo.setBeams(bset); DirectionCoordinate dc; dc.setWorldAxisUnits(Vector(2, "arcsec")); dc.setIncrement(Vector(2, 0.7)); for (uInt i=0; i<10; i++) { for (uInt j=0; j<4; j++) { Double expec = (i == 2 && j == 2) ? 34.686429656840772 : 18.499429150315081; AlwaysAssert( near(myinfo.getBeamAreaInPixels(i, j, dc), expec), AipsError ); } } } } catch (const std::exception& x) { cout << "Caught error " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/images/Images/test/tImageProxy.cc000066400000000000000000000034661476623553700215450ustar00rootroot00000000000000 //# tImageProxy.cc //# Copyright (C) 1998,1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include int main() { try { ImageProxy proxy("imagetestimage.fits", "", vector()); ImageInfo ii = proxy.imageInfoObject(); AlwaysAssert(ii.getBeamSet().hasSingleBeam(), AipsError); AlwaysAssert(proxy.type() == TpFloat, AipsError); AlwaysAssert(proxy.coordSysObject().nWorldAxes() == 2, AipsError); } catch (const std::exception& x) { cout << "Caught error " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/images/Images/test/tImageRegrid.cc000066400000000000000000000273371476623553700216430ustar00rootroot00000000000000//# tImageRegrid.cc: This program test Measure functionsimage regridding //# Copyright (C) 2001,2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main (int argc, const char* argv[]) { try { Input inputs(1); inputs.version ("$Revision$"); // Get inputs inputs.create("in", "", "Input image name"); inputs.create("axes", "-10", "axes"); inputs.create("method", "linear", "Method"); inputs.create("save", "False", "Save output ?"); inputs.create("shape", "-10", "Shape"); inputs.create("replicate", "False", "Replicate ?"); inputs.create("decimate", "0", "Decimation factor"); inputs.create("disk", "False", "Image on disk"); inputs.create("reuse", "False", "Reuse coordinate grid"); inputs.create("dbg", "0", "Debug level"); inputs.create("double", "0", "Double size ?"); inputs.create("force", "False", "Force regridding ?"); inputs.readArguments(argc, argv); const String in = inputs.getString("in"); const Bool save = inputs.getBool("save"); const String method = inputs.getString("method"); const Block axesU(inputs.getIntArray("axes")); const Block shapeU(inputs.getIntArray("shape")); const Bool replicate = inputs.getBool("replicate"); const Int decimate = inputs.getInt("decimate"); const Bool onDisk = inputs.getBool("disk"); const Bool dbl = inputs.getBool("double"); const Int dbg = inputs.getInt("dbg"); const Bool force = inputs.getBool("force"); const Bool reuse = inputs.getBool("reuse"); // Int maxMBInMemory = -1; if (onDisk) maxMBInMemory = 0; // ImageInterface* pIm = 0; IPosition shapeIn; if (in.empty()) { if (shapeU.nelements()>0) { if (shapeU.nelements()==1 && shapeU[0]==-10) { shapeIn = IPosition(2, 256, 256); } else { shapeIn.resize(shapeU.nelements()); for (uInt i=0; i(shape2, cSys, maxMBInMemory); pIm->set(1.0); // TempLattice inMask(shape2, maxMBInMemory); inMask.set(True); TempImage* pTemp = dynamic_cast*>(pIm); pTemp->attachMask(inMask); } else { pIm = new PagedImage(in); shapeIn = pIm->shape(); } // IPosition axes = IPosition::makeAxisPath(pIm->ndim()); if (axesU.nelements()>0) { if (axesU.nelements()==1 && axesU[0]==-10) { } else { axes.resize(axesU.nelements()); for (uInt i=0; icoordinates(); if (dbl) { Vector incr = cSysOut.increment().copy(); Vector refp = cSysOut.referencePixel().copy(); Vector refv = cSysOut.referenceValue().copy(); // shapeOut = shapeIn; for (uInt i=0; i 0) { for (uInt i=0; i(shapeOut, cSysOut, maxMBInMemory); } String maskName = pImOut->makeUniqueRegionName(String("mask"), 0); pImOut->makeMask(maskName, True, True, True, True); // Interpolate2D::Method emethod = Interpolate2D::stringToMethod(method); regridder.showDebugInfo(dbg); regridder.regrid(*pImOut, emethod, axes, *pIm, replicate, decimate, False, force); delete pImOut; } // if (reuse) { ImageInterface* pImOut = 0; if (save) { pImOut = new PagedImage(shapeOut, cSysOut, String("outFileReused")); } else { pImOut = new TempImage(shapeOut, cSysOut, maxMBInMemory); } String maskName = pImOut->makeUniqueRegionName(String("mask"), 0); pImOut->makeMask(maskName, True, True, True, True); // Interpolate2D::Method emethod = Interpolate2D::stringToMethod(method); Cube grid; Matrix gridMask; regridder.get2DCoordinateGrid(grid, gridMask); regridder.set2DCoordinateGrid(grid, gridMask); regridder.regrid(*pImOut, emethod, axes, *pIm, replicate, decimate, False, force); // grid.resize(); gridMask.resize(); regridder.set2DCoordinateGrid(grid, gridMask); regridder.regrid(*pImOut, emethod, axes, *pIm, replicate, decimate, False, force); // delete pImOut; } { cout << "*** Test makeCoordinateSystem" << endl; CoordinateSystem cIn = CoordinateUtil::defaultCoords2D(); CoordinateSystem cTo = CoordinateUtil::defaultCoords3D(); LogIO os; cout << "1" << endl; std::set coordsToRegrid; CoordinateSystem cOut = ImageRegrid::makeCoordinateSystem(os, coordsToRegrid, cTo, cIn, IPosition(2, 0, 1)); AlwaysAssert(coordsToRegrid.size() == 1, AipsError); AlwaysAssert( coordsToRegrid.find(Coordinate::DIRECTION) != coordsToRegrid.end(), AipsError ); AlwaysAssert( coordsToRegrid.find(Coordinate::SPECTRAL) == coordsToRegrid.end(), AipsError ); cIn = CoordinateUtil::defaultCoords3D(); cTo = CoordinateUtil::defaultCoords2D(); cout << "2" << endl; cOut = ImageRegrid::makeCoordinateSystem(os, coordsToRegrid, cTo, cIn, IPosition(2, 0, 1)); AlwaysAssert(coordsToRegrid.size() == 1, AipsError); AlwaysAssert( coordsToRegrid.find(Coordinate::DIRECTION) != coordsToRegrid.end(), AipsError ); AlwaysAssert( coordsToRegrid.find(Coordinate::SPECTRAL) == coordsToRegrid.end(), AipsError ); cIn = CoordinateUtil::defaultCoords3D(); cTo = CoordinateUtil::defaultCoords3D(); cout << "3" << endl; cOut = ImageRegrid::makeCoordinateSystem(os, coordsToRegrid, cTo, cIn, IPosition(2, 0, 1)); AlwaysAssert(coordsToRegrid.size() == 1, AipsError); AlwaysAssert( coordsToRegrid.find(Coordinate::DIRECTION) != coordsToRegrid.end(), AipsError ); AlwaysAssert( coordsToRegrid.find(Coordinate::SPECTRAL) == coordsToRegrid.end(), AipsError ); cout << "4" << endl; cOut = ImageRegrid::makeCoordinateSystem(os, coordsToRegrid, cTo, cIn, IPosition(1, 2)); AlwaysAssert(coordsToRegrid.size() == 1, AipsError); AlwaysAssert( coordsToRegrid.find(Coordinate::DIRECTION) == coordsToRegrid.end(), AipsError ); AlwaysAssert( coordsToRegrid.find(Coordinate::SPECTRAL) != coordsToRegrid.end(), AipsError ); cout << "5" << endl; cOut = ImageRegrid::makeCoordinateSystem(os, coordsToRegrid, cTo, cIn, IPosition()); AlwaysAssert(coordsToRegrid.size() == 2, AipsError); AlwaysAssert( coordsToRegrid.find(Coordinate::DIRECTION) != coordsToRegrid.end(), AipsError ); AlwaysAssert( coordsToRegrid.find(Coordinate::SPECTRAL) != coordsToRegrid.end(), AipsError ); cout << "6" << endl; cOut = ImageRegrid::makeCoordinateSystem(os, coordsToRegrid, cTo, cIn, IPosition(3, 0, 1, 2)); AlwaysAssert(coordsToRegrid.size() == 2, AipsError); AlwaysAssert( coordsToRegrid.find(Coordinate::DIRECTION) != coordsToRegrid.end(), AipsError ); AlwaysAssert( coordsToRegrid.find(Coordinate::SPECTRAL) != coordsToRegrid.end(), AipsError ); cout << "7" << endl; cIn = CoordinateUtil::defaultCoords4D(); cTo = CoordinateUtil::defaultCoords4D(); cOut = ImageRegrid::makeCoordinateSystem(os, coordsToRegrid, cTo, cIn, IPosition()); AlwaysAssert(coordsToRegrid.size() == 2, AipsError); AlwaysAssert( coordsToRegrid.find(Coordinate::DIRECTION) != coordsToRegrid.end(), AipsError ); AlwaysAssert( coordsToRegrid.find(Coordinate::SPECTRAL) != coordsToRegrid.end(), AipsError ); AlwaysAssert( coordsToRegrid.find(Coordinate::STOKES) == coordsToRegrid.end(), AipsError ); cout << "8" << endl; cOut = ImageRegrid::makeCoordinateSystem(os, coordsToRegrid, cTo, cIn, IPosition(3, 0, 1, 2)); AlwaysAssert(coordsToRegrid.size() == 1, AipsError); AlwaysAssert( coordsToRegrid.find(Coordinate::DIRECTION) != coordsToRegrid.end(), AipsError ); AlwaysAssert( coordsToRegrid.find(Coordinate::SPECTRAL) == coordsToRegrid.end(), AipsError ); AlwaysAssert( coordsToRegrid.find(Coordinate::STOKES) == coordsToRegrid.end(), AipsError ); } // delete pIm; cout << "OK" << endl; } catch (std::exception& x) { cerr << "aipserror: error " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/images/Images/test/tImageStatistics.cc000066400000000000000000000202041476623553700225430ustar00rootroot00000000000000//# tImageFitter.cc: test the PagedImage class //# Copyright (C) 1994,1995,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include void writeTestString(const String& test) { cout << "\n" << "*** " << test << " ***" << endl; } int main() { String test; FITSImage image("imageStats.fits"); try { { writeTestString( "test normal and copy constructors" ); ImageStatistics stats(image); ImageStatistics stats2(stats); AlwaysAssert(stats.getBlc() == stats2.getBlc(), AipsError); stats.setBlc(IPosition(image.coordinates().nPixelAxes(),1)); ImageStatistics stats3(stats); AlwaysAssert(stats.getBlc() == stats3.getBlc(), AipsError); } { writeTestString( "test multi-beam images" ); CoordinateSystem csys = CoordinateUtil::defaultCoords4D(); IPosition shape(4, 10, 15, 4, 20); TempImage tim(TiledShape(shape), csys); Array arr(IPosition(4, shape[0], shape[1], 1, 1)); indgen(arr); for (uInt i=0; i stats(tim); Vector axes(2, 0); axes[1] = 1; stats.setAxes(axes); Vector::AccumType> myStats; Vector::AccumType> exp; for (uInt i=0; i(tim); stats.setAxes(axes); for (uInt i=0; i(tim); stats.setAxes(axes); for (uInt i=0; i(tim); stats.setAxes(axes); for (uInt i=0; i tim(TiledShape(shape), csys); Array arr(shape); indgen(arr); tim.put(arr); tim.setUnits("Jy/beam"); GaussianBeam beam ( Quantity(3, "arcmin"), Quantity(2.5, "arcmin"), Quantity(30, "deg") ); ImageInfo info = tim.imageInfo(); info.setRestoringBeam(beam); tim.setImageInfo(info); ImageStatistics stats(tim); Array flux; AlwaysAssert( stats.getConvertedStatistic (flux, LatticeStatsBase::FLUX), AipsError ); AlwaysAssert(near(*flux.begin(), 111724.9893), AipsError); Vector axes(2, 0); axes[1] = 1; stats.setAxes(axes); AlwaysAssert( stats.getConvertedStatistic (flux, LatticeStatsBase::FLUX), AipsError ); AlwaysAssert(flux.shape() == IPosition(1, 20), AipsError); Vector statVals; Double area = beam.getArea("arcmin2"); for (uInt i=0; i<20; ++i) { Float expFlux = sum(arr(IPosition(3,0,0,i), IPosition(3, 9, 14, i)))/area; AlwaysAssert(near(flux(IPosition(1,i)), expFlux), AipsError); AlwaysAssert( stats.getStats( statVals, IPosition(1, i), False ), AipsError ); AlwaysAssert(near(statVals[LatticeStatsBase::FLUX], expFlux), AipsError); } tim.setUnits("K"); stats = ImageStatistics(tim); AlwaysAssert( stats.getConvertedStatistic (flux, LatticeStatsBase::FLUX), AipsError ); AlwaysAssert(near(*flux.begin(), 3.4180507464507e9), AipsError); axes = Vector(2, 0); axes[1] = 1; stats.setAxes(axes); AlwaysAssert( stats.getConvertedStatistic (flux, LatticeStatsBase::FLUX), AipsError ); AlwaysAssert(flux.shape() == IPosition(1, 20), AipsError); for (uInt i=0; i<20; ++i) { Float expFlux = sum(arr(IPosition(3,0,0,i), IPosition(3, 9, 14, i)))*3600; AlwaysAssert(near(flux(IPosition(1,i)), expFlux), AipsError); AlwaysAssert( stats.getStats( statVals, IPosition(1, i), False ), AipsError ); AlwaysAssert(near(statVals[LatticeStatsBase::FLUX], expFlux), AipsError); } Vector beams(20); Vector::iterator bIter = beams.begin(); Vector::iterator bEnd = beams.end(); uInt count = 0; while (bIter != bEnd) { *bIter = GaussianBeam( Quantity(3 + count, "arcmin"), Quantity(2.5, "arcmin"), Quantity(30, "deg") ); ++count; ++bIter; } ImageBeamSet beamSet(beams); ImageInfo ii = tim.imageInfo(); ii.setBeams(beamSet); tim.setUnits("Jy/beam"); tim.setImageInfo(ii); stats = ImageStatistics (tim); stats.setAxes(indgen(2, 0, 1)); Array fluxDensities; AlwaysAssert( stats.getConvertedStatistic ( fluxDensities, LatticeStatsBase::FLUX ), AipsError ); cout << "flux densities " << fluxDensities << endl; // 0.2110611 is channel width in km/s Double expected = sum(fluxDensities) * 0.2110611; stats.setAxes(Vector()); AlwaysAssert( stats.getConvertedStatistic(flux, LatticeStatsBase::FLUX), AipsError ); AlwaysAssert(near(*flux.begin(), expected), AipsError); } cout << "ok" << endl; } catch (const std::exception& x) { cerr << "Exception caught: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/images/Images/test/tImageStatistics2.cc000066400000000000000000000343121476623553700226320ustar00rootroot00000000000000//# Copyright (C) 1994,1995,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include #include #include #include #include #include #include #include #include #include using namespace casacore; int main() { try { String *parts = new String[2]; split(EnvironmentVariable::get("CASAPATH"), parts, 2, String(" ")); String datadir = parts[0] + "/data/"; delete [] parts; String imageName = datadir + "regression/unittest/stats/stats200M.im"; if (! File(imageName).exists()) { cout << "Cannot find image so tests cannot be run" << endl; return 0; } casacore::PagedImage im(imageName); RO_LatticeIterator imIter(im); /* { std::shared_ptr > dataProvider = new LatticeStatsDataProvider(im); ClassicalStatistics cs; cs.setDataProvider(dataProvider); std::set quartiles; quartiles.insert(0.25); quartiles.insert(0.75); std::map quantileToValue; Double median = cs.getMedianAndQuantiles( quantileToValue, quartiles ); } */ /* { cout << "This should produce the desired results" << endl; imIter.reset(); ClassicalStatistics cs; cs.setCalculateAsAdded(True); Bool deleteIt = False; while (! imIter.atEnd()) { const Float* begin = imIter.cursor().getStorage(deleteIt); cs.addData(begin, imIter.cursor().size()); ++imIter; } Record stats = cs.getStatistics(); cout << stats << endl; } */ /* { cout << endl << "This should produce the desired results" << endl; imIter.reset(); ClassicalStatistics cs; cs.setCalculateAsAdded(True); Bool deleteIt = False; while (! imIter.atEnd()) { const Float* begin = imIter.cursor().getStorage(deleteIt); cs.addData(begin, imIter.cursor().size()); ++imIter; } Double mymin, mymax; cs.getMinMax(mymin, mymax); cout << "min " << mymin << " max " << mymax << endl; } { // This code will produce bogus results because the iterators are // no longer valid when getMinMax is called cout << endl << "This should not produce the desired results." << endl; imIter.reset(); ClassicalStatistics cs; cs.setCalculateAsAdded(False); Bool deleteIt = False; while (! imIter.atEnd()) { const Float* begin = imIter.cursor().copy().getStorage(deleteIt); cs.addData(begin, imIter.cursor().size()); ++imIter; } Double mymin, mymax; cs.getMinMax(mymin, mymax); cout << "min " << mymin << " max " << mymax << endl; //Record stats = cs.getStatistics(); //cout << stats << endl; } { // This will work cout << endl << "This should produce the desired results" << endl; imIter.reset(); ClassicalStatistics cs; cs.setCalculateAsAdded(False); Bool deleteIt = False; vector > chunks; while (! imIter.atEnd()) { Array chunk = imIter.cursor().copy(); const Float* begin = chunk.getStorage(deleteIt); cs.addData(begin, chunk.size()); chunks.push_back(chunk); ++imIter; } Double mymin, mymax; cs.getMinMax(mymin, mymax); cout << "min " << mymin << " max " << mymax << endl; } { // this will work as expected, because all arrays are held // in memory before getStatistics() is called. Note that copies // of the arrays have to be made because imIter.cursor() will overwrite // the same location in memory on subsequent calls. If the arrays are not // copied, getStatistics will iterate over N copies of the same array. cout << endl << "This should produce the desired results" << endl; imIter.reset(); ClassicalStatistics cs; cs.setCalculateAsAdded(False); Bool deleteIt = False; vector > chunks; uInt count = 0; while (! imIter.atEnd()) { chunks.push_back(imIter.cursor().copy()); const Float* begin = chunks.back().getStorage(deleteIt); cs.addData(begin, chunks.back().size()); ++imIter; ++count; } Double mymin, mymax; cs.getMinMax(mymin, mymax); cout << "min " << mymin << " max " << mymax << endl; } */ /* { cout << endl << "This should produce the desired results" << endl; imIter.reset(); ClassicalStatistics cs; cs.setCalculateAsAdded(False); Bool deleteIt = False; vector > chunks; uInt count = 0; while (! imIter.atEnd()) { chunks.push_back(imIter.cursor().copy()); const Float* begin = chunks.back().getStorage(deleteIt); cs.addData(begin, chunks.back().size()); ++imIter; ++count; } cout << "begin quantile computation" << endl; cout << std::setprecision(15) << "0.5 quantile value " << cs.getQuantile(0.5) << endl; vector qs; qs.push_back(0.9); qs.push_back(0.1); qs.push_back(0.5); qs.push_back(0.50000001); cout << std::setprecision(15) << "quantile values " << cs.getQuantiles(qs) << endl; } { cout << endl << "This should produce the desired results" << endl; imIter.reset(); ClassicalStatistics cs; cs.setCalculateAsAdded(False); Bool deleteIt = False; vector > chunks; uInt count = 0; while (! imIter.atEnd()) { chunks.push_back(imIter.cursor().copy()); const Float* begin = chunks.back().getStorage(deleteIt); cs.addData(begin, chunks.back().size()); ++imIter; ++count; } cout << "begin quantile computation" << endl; cout << std::setprecision(15) << "0.5 quantile value " << cs.getQuantile(0.5, 10000) << endl; vector qs; qs.push_back(0.9); qs.push_back(0.1); qs.push_back(0.5); qs.push_back(0.50000001); cout << std::setprecision(15) << "quantile values " << cs.getQuantiles(qs, 10000) << endl; } */ /* { cout << endl << "This should produce the desired results" << endl; imIter.reset(); ClassicalStatistics cs; cs.setCalculateAsAdded(False); Bool deleteIt = False; vector > chunks; uInt count = 0; while (! imIter.atEnd()) { chunks.push_back(imIter.cursor().copy()); const Float* begin = chunks.back().getStorage(deleteIt); cs.addData(begin, chunks.back().size()); ++imIter; ++count; } cout << "begin median computation" << endl; cout << std::setprecision(15) << "median " << cs.getMedian() << endl; } */ /* { cout << endl << "This should produce the desired results" << endl; LatticeStatsDataProvider dataProvider(imIter); ClassicalStatistics cs; StatsDataProvider* dp = dynamic_cast* >( &dataProvider ); ThrowIf(! dp, "unable to dynamic cast"); cs.setDataProvider(dp); cout << "begin stats computation" << endl; cout << std::setprecision(15) << "stats " << cs.getStatistics() << endl; } */ /* { cout << endl << "This should produce the desired results" << endl; LatticeStatsDataProvider dataProvider(imIter); ClassicalStatistics cs; StatsDataProvider* dp = dynamic_cast* >( &dataProvider ); ThrowIf(! dp, "unable to dynamic cast"); cs.setDataProvider(dp); cout << "begin median computation" << endl; cout << std::setprecision(15) << "median " << cs.getMedian() << endl; } */ //im = PagedImage("stats200M.im"); //imIter = RO_LatticeIterator (im); /* { cout << endl << "This should produce the desired results" << endl; imIter.reset(); ClassicalStatistics cs; cs.setCalculateAsAdded(False); Bool deleteIt = False; vector > chunks; uInt count = 0; while (! imIter.atEnd()) { chunks.push_back(imIter.cursor().copy()); const Float* begin = chunks.back().getStorage(deleteIt); cs.addData(begin, chunks.back().size()); ++imIter; ++count; } cout << "begin statistics computation" << endl; Record stats = cs.getStatistics(); cout << std::setprecision(15) << stats << endl; AlwaysAssert(stats.asInt64("npts") == im.size(), AipsError); } */ /* { cout << endl << "This should produce the desired results" << endl; LatticeStatsDataProvider *dataProvider = new LatticeStatsDataProvider(im); ClassicalStatistics cs; StatsDataProvider *dp = dynamic_cast* >( dataProvider ); // there are problems with slightly non-reproducable binning for quantiles because of // finite machine precision when the AccumType is Float //LatticeStatsDataProvider *dataProvider // = new LatticeStatsDataProvider(im); // ClassicalStatistics cs; // StatsDataProvider *dp = // dynamic_cast* >( // dataProvider // ); ThrowIf(! dp, "unable to dynamic cast"); cs.setDataProvider(dp); cout << "begin statistics computation" << endl; Record stats = cs.getStatistics(); cout << std::setprecision(15) << stats << endl; AlwaysAssert(stats.asInt64("npts") == im.size(), AipsError); cout << "begin median computation" << endl; Double median = cs.getMedian(); cout << std::setprecision(15) << median << endl; } */ { LatticeStatistics lattStats(im); Array d; lattStats.getStatistic(d, LatticeStatsBase::SUM); cout << d << endl; /* Array res; lattStats.getStatistic(res, LatticeStatsBase::MEDIAN); AlwaysAssert(near(*res.begin(), -0.00010517791088204831), AipsError); */ } /* { cout << endl << "This should produce the desired results" << endl; std::shared_ptr > dataProvider = new LatticeStatsDataProvider(im); ClassicalStatistics cs; cout << im.name() << endl; // StatsDataProvider *dp = // dynamic_cast* >( // dataProvider // ); cs.setDataProvider(dataProvider); cout << "begin median computation" << endl; Double median = cs.getMedian(); cout << "median " << std::setprecision(15) << median << endl; std::set quantiles; quantiles.insert(0.25); quantiles.insert(0.75); cout << "begin quartile computation" << endl; std::map vals = cs.getQuantiles(quantiles); cout << "first and third quartiles " << vals << endl; } */ /* { cout << endl << "This should produce the desired results" << endl; std::shared_ptr > dataProvider = new LatticeStatsDataProvider(im); ClassicalStatistics cs; cs.setDataProvider(dataProvider); cout << "begin medabsdevmed computation" << endl; Double medabsdevmed = cs.getMedianAbsDevMed(); cout << "medabsdevmed " << std::setprecision(15) << medabsdevmed << endl; } */ /* { String imageName2 = datadir + "regression/unittest/stats/ngc4826.tutorial.16apr98.src.clean.model"; if (! File(imageName2).exists()) { cout << "Cannot find image " << imageName2 << " so some tests cannot be run" << endl; return 0; } casacore::PagedImage im2(imageName2); LatticeStatistics lattStats(im2); Array res; lattStats.getStatistic(res, LatticeStatsBase::MEDIAN); AlwaysAssert(*res.begin() == 0, AipsError); } */ /* { String imageName2 = datadir + "regression/unittest/stats/stats2G.im"; if (! File(imageName2).exists()) { cout << "Cannot find image " << imageName2 << " so some tests cannot be run" << endl; return 0; } casacore::PagedImage im2(imageName2); Slicer slice(IPosition(im2.ndim(), 0), IPosition(im2.ndim(), 800)); SubImage x(im2, slice); LatticeStatistics lattStats(x); Array res; lattStats.getStatistic(res, LatticeStatsBase::MEAN); } */ } catch (const std::exception& x) { cerr << "Exception caught: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/images/Images/test/tImageTiledCache.in000066400000000000000000000000531476623553700224170ustar00rootroot000000000000002 512 16 0.5 16384 100 128 8 0.5 65536 100 casacore-3.7.1/images/Images/test/tImageUtilities.cc000066400000000000000000000177101476623553700223740ustar00rootroot00000000000000//# tImageUtilities.cc: Test program for the static ImageUtilities functions //# Copyright (C) 2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void doOpens() { Directory dir("tImageUtilities_tmp"); dir.create(); LogIO os(LogOrigin("tImageUtilities", "doOpens()", WHERE)); os << "Open Image tests" << LogIO::POST; // { String name1("tImageUtilities_tmp/app.img"); PagedImage img (IPosition(2,10,10), CoordinateUtil::defaultCoords2D(), name1); String error; String name2("tImageUtilities_tmp/fits.img"); ImageFITSConverter::ImageToFITS(error, img, name2, 64, True, True, -32, 1, -1, True); { std::unique_ptr> im; ImageUtilities::openImage(im, name1); } { std::shared_ptr> im; im = ImageUtilities::openImage(name1); } { std::unique_ptr> im; ImageUtilities::openImage(im, name2); } } // dir.removeRecursive(); } void doTypes() { LogIO os(LogOrigin("tImageUtilities", __FUNCTION__, WHERE)); os << "Image Type test" << LogIO::POST; // Directory dir("tImageUtilities_tmp"); dir.create(); { PagedImage img (IPosition(2,10,10), CoordinateUtil::defaultCoords2D(), "tImageUtilities_tmp/app.img"); } AlwaysAssertExit (ImageOpener::imageType ("tImageUtilities_tmp/app.img") == ImageOpener::AIPSPP); { PagedArray arr (IPosition(2,10,10), "tImageUtilities_tmp/app.img"); } AlwaysAssertExit (ImageOpener::imageType ("tImageUtilities_tmp/app.img") == ImageOpener::UNKNOWN); { Directory dir("tImageUtilities_tmp/mir.img"); dir.create(); RegularFile rfile("tImageUtilities_tmp/mir.img/image"); rfile.create(); } AlwaysAssertExit (ImageOpener::imageType ("tImageUtilities_tmp/mir.img") == ImageOpener::UNKNOWN); { RegularFile rfile("tImageUtilities_tmp/mir.img/header"); rfile.create(); } AlwaysAssertExit (ImageOpener::imageType("tImageUtilities_tmp/mir.img") == ImageOpener::MIRIAD); { RegularFile rfile("tImageUtilities_tmp/a.image"); rfile.create(); } AlwaysAssertExit (ImageOpener::imageType("tImageUtilities_tmp/a.image") == ImageOpener::UNKNOWN); char buf[2880]; memset (buf, ' ', 2880); memcpy (buf, "SIMPLE = T ", 15); { RegularFileIO file (RegularFile("tImageUtilities_tmp/a.image"), ByteIO::Update); file.write (2879, buf); } AlwaysAssertExit (ImageOpener::imageType ("tImageUtilities_tmp/a.image") == ImageOpener::UNKNOWN); { RegularFileIO file (RegularFile("tImageUtilities_tmp/a.image"), ByteIO::Update); file.write (2880, buf); } AlwaysAssertExit (ImageOpener::imageType("tImageUtilities_tmp/a.image") == ImageOpener::FITS); { RegularFile rfile("tImageUtilities_tmp/a.descr"); rfile.create(); } AlwaysAssertExit (ImageOpener::imageType ("tImageUtilities_tmp/a.image") == ImageOpener::GIPSY); // dir.removeRecursive(); } void listWorld (const Vector>& wPars) { cerr << "World" << endl; if (wPars.nelements()==3){ cerr << " Major, minor, pa = " << wPars(0) << ", " << wPars(1) << ", " << wPars(2) << endl; } if (wPars.nelements()==5) { cerr << " X, Y = " << wPars(0) << ", " << wPars(1) << endl; cerr << " Major, minor, pa = " << wPars(2) << ", " << wPars(3) << ", " << wPars(4) << endl; } } void listWorld (const GaussianBeam& wPars) { cerr << "World" << endl; if (! wPars.isNull()){ cerr << " Major, minor, pa = " << wPars.getMajor() << ", " << wPars.getMinor() << ", " << wPars.getPA() << endl; } } void listPixel(const Vector& pPars) { cerr << "Pixel" << endl; if (pPars.nelements()==3) { cerr << " Major, minor, pa = " << pPars(0) << ", " << pPars(1) << ", " << pPars(2)/C::degree << endl; } else if (pPars.nelements()==5) { cerr << " X, Y = " << pPars(0) << ", " << pPars(1) << endl; cerr << " Major, minor, pa = " << pPars(2) << ", " << pPars(3) << ", " << pPars(4)/C::degree << endl; } } void doConversions() { LogOrigin lor("tImageUtilities", "doConversions()", WHERE); LogIO os(lor); os << "Conversion Tests" << LogIO::POST; // { CoordinateSystem cSys = CoordinateUtil::defaultCoords2D(); cerr << "inc = " << cSys.increment() << endl; IPosition pixelAxes(2, 0, 1); IPosition worldAxes(2, 0, 1); // Vector world; Vector pixel = cSys.referencePixel().copy(); pixel += 10.0; cSys.toWorld(world, pixel); } } void doBin() { LogOrigin lor("tImageUtilities", __FUNCTION__, WHERE); LogIO os(lor); os << "Binning Tests" << LogIO::POST; // uInt n = 32; IPosition shape(1,n); SpectralCoordinate cIn, cOut; Array data(shape); Array mask(shape); indgen(data); mask = True; MaskedArray maIn(data,mask); MaskedArray maOut; uInt bin = 2; uInt axis = 0; // ImageUtilities::bin(maOut, cOut, maIn, cIn, axis, bin); AlwaysAssert(maOut.nelements()==n/bin, AipsError); Array pOut(maOut.shape()); indgen(pOut); pOut *= Float(bin); pOut += Float(0.5); AlwaysAssert(allNear(pOut,maOut.getArray(),1e-6), AipsError); AlwaysAssert(allEQ(maOut.getMask(),True), AipsError); } int main() { try { doBin(); doTypes(); doOpens(); } catch (const std::exception& x) { cout << "Unexpected exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/images/Images/test/tLELSpectralIndex.cc000066400000000000000000000143011476623553700225510ustar00rootroot00000000000000//# tLELSpectralIndex.cc: Test program for class LELSpectralIndex //# Copyright (C) 2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Bool doIt() { Bool ok = True; IPosition shp1(2,10,10); IPosition shp1a(3,10,10,1); IPosition shp2(3,10,10,15); Double freq, freql, freqstep, scafreq; Array arr1(shp1); Array arr2(shp2); Array arr1a(shp2); Array arr1b(shp1a); indgen(arr1, Float(1)); indgen(arr1b, Float(1)); indgen(arr2, Float(10)); for (Int i=0; i sub = arr1a(IPosition(3,0,0,i), IPosition(3,9,9,i)); sub = arr1b; } { CoordinateSystem cSys1 = CoordinateUtil::defaultCoords3D(); const SpectralCoordinate& scrd = cSys1.spectralCoordinate (1); AlwaysAssertExit (scrd.toWorld (freq, 0.)); AlwaysAssertExit (scrd.toWorld (freql, Float(shp2(2)-1))); freqstep = (freql - freq) / Float(shp2(2)-1); scafreq = freq + freqstep * Double(shp2(2)) / 2; // Remove the pixel axis (and replace by middle element). cSys1.removePixelAxis (2, Double(shp2(2)) / 2); CoordinateSystem cSys2 = CoordinateUtil::defaultCoords3D(); CoordinateSystem cSys3; CoordinateUtil::addFreqAxis(cSys3); CoordinateUtil::addDirAxes(cSys3); cSys3.removePixelAxis (0, Double(shp2(2)) / 2 + 1); PagedImage pa1(shp1, cSys1, "tLELSpectralIndex_tmp.pa1"); PagedImage pa1a(shp1a, cSys2, "tLELSpectralIndex_tmp.pa1a"); PagedImage pa2(TiledShape(shp2, IPosition(3,4,3,6)), cSys2, "tLELSpectralIndex_tmp.pa2"); PagedImage pa3(shp1, cSys3, "tLELSpectralIndex_tmp.pa3"); pa1.put (arr1); pa1a.put (arr1b); pa2.put (arr2); pa3.put (arr1 + Float(100)); } { PagedImage pa1("tLELSpectralIndex_tmp.pa1"); PagedImage pa2("tLELSpectralIndex_tmp.pa2"); LatticeExpr expr = spectralindex(pa1,pa2); Array result = expr.get(); Cube arrf(shp2); for (Int i=0; i expect = log(arr1a / arr2) / arrf; if (! allNear (result, expect, 1e-5)) { cout << expect << endl; cout << result << endl; ok = False; } } { PagedImage pa1("tLELSpectralIndex_tmp.pa1a"); PagedImage pa2("tLELSpectralIndex_tmp.pa2"); LatticeExpr expr = spectralindex(pa1,pa2); Array result = expr.get(); Cube arrf(shp2); for (Int i=0; i expect = log(arr1a / arr2) / arrf; Array subarr = expect(IPosition(3, 0, 0, 0), IPosition(3, shp2(0)-1, shp2(1)-1, 0)); subarr = 0; if (! allNear (result, expect, 1e-5)) { cout << expect << endl; cout << result << endl; ok = False; } } { PagedImage pa3("tLELSpectralIndex_tmp.pa3"); PagedImage pa1("tLELSpectralIndex_tmp.pa1"); LatticeExpr expr = spectralindex(pa1,pa3); Array result = expr.get(); Matrix arrf(shp1); for (Int i=0; i expect = log(arr1 / (arr1+Float(100))) / arrf; if (! allNear (result, expect, 1e-5)) { cout << expect << endl; cout << result << endl; ok = False; } } { PagedImage pa3("tLELSpectralIndex_tmp.pa3"); PagedImage pa2("tLELSpectralIndex_tmp.pa2"); LatticeExpr expr = spectralindex(pa2,pa3); Array result = expr.get(); Cube arrf(shp2); for (Int i=0; i expect = log(arr2 / (arr1a+Float(100))) / arrf; if (! allNear (result, expect, 1e-5)) { cout << expect << endl; cout << result << endl; ok = False; } } return ok; } int main () { Bool ok = True; try { ok = doIt(); } catch (std::exception& x) { cerr << "Caught exception: " << x.what() << endl; ok = False; } if (!ok) { cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/images/Images/test/tMIRIADImage.cc000066400000000000000000000151001476623553700213550ustar00rootroot00000000000000//# tMIRIADImage.cc: test the MIRIADImage class //# Copyright (C) 1994,1995,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Bool allNear (const Array& data, const Array& dataMask, const Array& mir, const Array& mirMask, Float tol=1.0e-5); int main (int argc, const char* argv[]) { try { LogIO os(LogOrigin("tMIRIADImage", "main()", WHERE)); // Get inputs Input inputs(1); inputs.create("in", "", "Input MIRIAD file"); inputs.create("print", "F", "Print some data"); inputs.create("size", "5", "Size to print"); // inputs.readArguments(argc, argv); String in = inputs.getString("in"); const Bool print = inputs.getBool("print"); const Int size = inputs.getInt("size"); // if (in.empty()) { #if 0 in = "imagetestimage.mir"; #else in = "test2.mir"; #endif } Path p(in); cout << p.originalName() << endl; // Open MIRIADImage MIRIADImage mirImage(in); mirImage.tempClose(); AlwaysAssert(mirImage.imageType()=="MIRIADImage", AipsError); Unit unit("Jy/beam"); AlwaysAssert(mirImage.setUnits(unit), AipsError); AlwaysAssert(mirImage.units().getName()=="Jy/beam", AipsError); Record rec; rec.define("field1", 0.0); rec.define("field2", "doggies"); AlwaysAssert(mirImage.setMiscInfo(rec), AipsError); mirImage.reopen(); Record rec2 = mirImage.miscInfo(); AlwaysAssert(rec.isDefined("field1"), AipsError); AlwaysAssert(rec.isDefined("field2"), AipsError); AlwaysAssert(rec.asFloat("field1")==0.0, AipsError); AlwaysAssert(rec.asString("field2")=="doggies", AipsError); AlwaysAssert(mirImage.hasPixelMask() == mirImage.isMasked(), AipsError); #if 0 if (mirImage.hasPixelMask()) { Lattice& pMask = mirImage.pixelMask(); AlwaysAssert(pMask.shape()==mirImage.shape(), AipsError); } #endif AlwaysAssert(mirImage.getRegionPtr()==0, AipsError); AlwaysAssert(mirImage.isWritable()==False, AipsError); AlwaysAssert(mirImage.name(False)==p.absoluteName(),AipsError); AlwaysAssert(mirImage.ok(), AipsError); // mirImage.tempClose(); if (print) { IPosition start (mirImage.ndim(),0); IPosition shape(mirImage.shape()); for (uInt i=0; i size) shape(i) = size; } cerr << "Data = " << mirImage.getSlice(start, shape) << endl; cerr << "Mask = " << mirImage.getMaskSlice(start, shape) << endl; } // Convert from MIRIAD as a comparison String error; ImageInterface* pTempImage = 0; String imageName; #if 1 if (!ImageFITSConverter::FITSToImage(pTempImage, error, imageName, in+".fits", 0)) { os << error << LogIO::EXCEPTION; } // need to fill pTempImage .... Array mirArray = mirImage.get(); Array dataArray = pTempImage->get(); Array mirMask = mirImage.getMask(); Array dataMask = pTempImage->getMask(); CoordinateSystem mirCS = mirImage.coordinates(); CoordinateSystem dataCS = pTempImage->coordinates(); delete pTempImage; // AlwaysAssert(allNear(dataArray, dataMask, mirArray, mirMask), AipsError); AlwaysAssert(mirCS.near(dataCS), AipsError); // Test Clone ImageInterface* pMirImage = mirImage.cloneII(); Array mirArray2 = pMirImage->get(); Array mirMask2 = pMirImage->getMask(); CoordinateSystem mirCS2 = pMirImage->coordinates(); delete pMirImage; // AlwaysAssert(allNear(dataArray, dataMask, mirArray2, mirMask2), AipsError); AlwaysAssert(mirCS2.near(dataCS), AipsError); // #endif cerr << "ok " << endl; } catch (std::exception& x) { cerr << "aipserror: error " << x.what() << endl; return 1; } return 0; } Bool allNear (const Array& data, const Array& dataMask, const Array& mir, const Array& mirMask, Float tol) { Bool deletePtrData, deletePtrDataMask, deletePtrMIRIAD, deletePtrMIRIADMask; const Float* pData = data.getStorage(deletePtrData); const Float* pMIRIAD = mir.getStorage(deletePtrMIRIAD); const Bool* pDataMask = dataMask.getStorage(deletePtrDataMask); const Bool* pMIRIADMask = mirMask.getStorage(deletePtrMIRIADMask); // for (uInt i=0; i #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Remove the dirname from the table name in an error message. String removeDir (const String& msg) { String s = msg; s.gsub (Regex("/.*/t"), "t"); return s; } Float const_arg_func(const Float& val) { return 3.0*val; } Float func(Float val) { return 2.0*val*val; } Table makeScrTable(const String& name) { SetupNewTable setup(name, TableDesc(), Table::Scratch); Table table(setup); return table; } Table makeNewTable(const String& name) { SetupNewTable setup(name, TableDesc(), Table::New); Table table(setup); return table; } void testTempCloseDelete() { IPosition shape(2,32,64); CoordinateSystem cSys = CoordinateUtil::defaultCoords2D(); // Check image gets deleted if marked for delete. { TiledShape tiledShape(shape); PagedImage img (tiledShape, cSys, "tPagedImage_tmp.imgtc"); img.putAt(Float(1.0), IPosition(2,1,1)); img.table().markForDelete(); } // Check image gets deleted if marked for delete, // even after tempClose. AlwaysAssertExit (! File("tPagedImage_tmp.imgtc").exists()); { IPosition shape(2,32,64); TiledShape tiledShape(shape); PagedImage img (tiledShape, cSys, "tPagedImage_tmp.imgtc"); img.putAt(Float(1.0), IPosition(2,1,1)); img.table().markForDelete(); img.tempClose(); } AlwaysAssertExit (! File("tPagedImage_tmp.imgtc").exists()); // Check image gets deleted if marked for delete, // even after tempClose and reopen. { IPosition shape(2,32,64); TiledShape tiledShape(shape); PagedImage img (tiledShape, cSys, "tPagedImage_tmp.imgtc"); img.putAt(Float(1.0), IPosition(2,1,1)); img.table().markForDelete(); img.tempClose(); img.putAt(Float(1.0), IPosition(2,0,0)); } AlwaysAssertExit (! File("tPagedImage_tmp.imgtc").exists()); // Check image does not get deleted if first marked for delete, // but after tempClose and reopen is unmarked for delete. { IPosition shape(2,32,64); TiledShape tiledShape(shape); PagedImage img (tiledShape, cSys, "tPagedImage_tmp.imgtc"); img.putAt(Float(1.0), IPosition(2,1,1)); img.table().markForDelete(); img.tempClose(); img.putAt(Float(1.0), IPosition(2,0,0)); img.table().unmarkForDelete(); } AlwaysAssertExit (File("tPagedImage_tmp.imgtc").exists()); } int main() { try { // Build things to make a PagedImage CoordinateSystem cSys = CoordinateUtil::defaultCoords2D(); IPosition shape(2,32,64); TiledShape tiledShape(shape); // Test constructors { Table table = makeScrTable(String("tPagedImage_tmp.img1")); PagedImage pIm(tiledShape, cSys, table); } { PagedImage pIm(tiledShape, cSys, String("tPagedImage_tmp.img2")); } { PagedImage pIm(tiledShape, cSys, String("tPagedImage_tmp.img3"), TableLock(TableLock::AutoLocking)); } TableUtil::deleteTable(String("tPagedImage_tmp.img3")); { Table table = makeScrTable(String("tPagedImage_tmp.img4")); PagedImage pIm(tiledShape, cSys, table); PagedImage pIm2(table); } { Table table = makeNewTable(String("tPagedImage_tmp.img5")); PagedImage pIm(tiledShape, cSys, table); } { PagedImage pIm(String("tPagedImage_tmp.img5")); } { PagedImage pIm(String("tPagedImage_tmp.img5"), TableLock(TableLock::AutoLocking)); } // // Test copy constructor. This is by reference so test that // this is so // { PagedImage pIm(String("tPagedImage_tmp.img5")); PagedImage pIm2(pIm); pIm.tempClose(); pIm.putAt(Float(1.0), IPosition(2,0,0)); AlwaysAssert( (pIm(IPosition(2,0,0))==Float(1.0)), AipsError); pIm2.putAt(Float(10.0), IPosition(2,0,0)); AlwaysAssert((pIm2(IPosition(2,0,0))==Float(10.0)), AipsError); AlwaysAssert((pIm(IPosition(2,0,0))==Float(10.0)), AipsError); // AlwaysAssert(pIm(IPosition(2,0,0))==pIm.getAt(IPosition(2,0,0)), AipsError); } // // Test assignment. This is by reference so test that // this is so // { PagedImage pIm(String("tPagedImage_tmp.img5")); PagedImage pIm2(pIm); pIm2 = pIm; pIm.putAt(Float(1.0), IPosition(2,0,0)); AlwaysAssert( (pIm(IPosition(2,0,0))==Float(1.0)), AipsError); pIm2.putAt(Float(10.0), IPosition(2,0,0)); AlwaysAssert((pIm2.getAt(IPosition(2,0,0))==Float(10.0)), AipsError); AlwaysAssert((pIm.getAt(IPosition(2,0,0))==Float(10.0)), AipsError); // AlwaysAssert(pIm(IPosition(2,0,0))==pIm.getAt(IPosition(2,0,0)), AipsError); } // // Test clones. There is not much we can do to make sure they are ok ! // They are by reference too. // { PagedImage pIm(String("tPagedImage_tmp.img5")); pIm.putAt(Float(1.0), IPosition(2,0,0)); AlwaysAssert( (pIm(IPosition(2,0,0))==Float(1.0)), AipsError); // Lattice* lat = pIm.clone(); AlwaysAssert(pIm.shape()==lat->shape(), AipsError); // lat->putAt(Float(10.0), IPosition(2,0,0)); AlwaysAssert(((*lat).getAt(IPosition(2,0,0))==Float(10.0)), AipsError); AlwaysAssert((pIm.getAt(IPosition(2,0,0))==Float(10.0)), AipsError); delete lat; } { PagedImage pIm(String("tPagedImage_tmp.img5")); pIm.putAt(Float(1.0), IPosition(2,0,0)); AlwaysAssert( (pIm.getAt(IPosition(2,0,0))==Float(1.0)), AipsError); // Lattice* ii = pIm.cloneII(); AlwaysAssert(pIm.shape()==ii->shape(), AipsError); // ii->putAt(Float(10.0), IPosition(2,0,0)); AlwaysAssert(((*ii).getAt(IPosition(2,0,0))==Float(10.0)), AipsError); AlwaysAssert((pIm.getAt(IPosition(2,0,0))==Float(10.0)), AipsError); delete ii; } // // Test some miscellaneous little things // { PagedImage pIm(String("tPagedImage_tmp.img5")); // AlwaysAssert(imagePixelType(String("tPagedImage_tmp.img5"))==TpFloat, // Global function AipsError); AlwaysAssert(pIm.name(True)==String("tPagedImage_tmp.img5"), AipsError); cout << "Absolute name = " << pIm.name(False) << endl; AlwaysAssert(pIm.isPaged(), AipsError); AlwaysAssert(pIm.isWritable(), AipsError); AlwaysAssert(pIm.ok(), AipsError); // pIm.rename(String("tPagedImage_tmp.img6")); AlwaysAssert(pIm.name(True)==String("tPagedImage_tmp.img6"), AipsError); pIm.rename(String("tPagedImage_tmp.img5")); AlwaysAssert(pIm.name(True)==String("tPagedImage_tmp.img5"), AipsError); // AlwaysAssert(pIm.rowNumber()==0, AipsError); AlwaysAssert(pIm.shape()==shape, AipsError); IPosition niceCursorShape = pIm.niceCursorShape(); // cout << "niceCursorShape=" << niceCursorShape << endl; // Only true for small images AlwaysAssert(niceCursorShape==shape, AipsError); // IPosition shape2(2,10,20); pIm.resize(shape2); IPosition shape0(3,5,10,20); Bool ok = False; try { pIm.resize(shape0); } catch (std::exception& x) { // cout << "Caught error " << x.what() << endl; ok = True; } if (!ok) { throw(AipsError("Resize did not fail. This was unexpected")); } // pIm.tempClose(); Unit units("Jy"); pIm.setUnits(units); AlwaysAssert(pIm.units().getName()=="Jy", AipsError); // TableRecord rec; rec.define("x", Double(1.0)); rec.define("y", Double(2.0)); pIm.tempClose(); pIm.setMiscInfo(rec); pIm.tempClose(); TableRecord rec2 = pIm.miscInfo(); AlwaysAssert(rec2.nfields()==2, AipsError); AlwaysAssert(rec2.isDefined("x"), AipsError); AlwaysAssert(rec2.isDefined("y"), AipsError); AlwaysAssert(rec2.dataType("x")==TpDouble, AipsError); AlwaysAssert(rec2.dataType("y")==TpDouble, AipsError); AlwaysAssert(rec2.asDouble("x")==Double(1.0), AipsError); AlwaysAssert(rec2.asDouble("y")==Double(2.0), AipsError); // // cout << "Cache size = " << pIm.maximumCacheSize() << endl; pIm.setCacheSizeFromPath(shape, IPosition(2,0,0), shape, IPosition(2,0,1)); // cout << "Cache size = " << pIm.maximumCacheSize() << endl; pIm.setMaximumCacheSize(100); AlwaysAssert(pIm.maximumCacheSize()==100, AipsError); // cout << "Cache size = " << pIm.maximumCacheSize() << endl; // pIm.clearCache(); pIm.showCacheStatistics(cout); // CoordinateSystem cSys2 = pIm.coordinates(); Vector axisUnits = cSys2.worldAxisUnits(); axisUnits(0) = "deg"; axisUnits(1) = "deg"; cSys2.setWorldAxisUnits(axisUnits); pIm.setCoordinateInfo(cSys2); CoordinateSystem cSys3 = pIm.coordinates(); AlwaysAssert(cSys2.near(cSys3,1e-6), AipsError); // ImageInfo info = pIm.imageInfo(); AlwaysAssert(info.restoringBeam().isNull(), AipsError); Quantity a1(10.0,Unit("arcsec")); Quantity a2(8.0,Unit("arcsec")); Quantity a3(-45.0,Unit("deg")); info.setRestoringBeam(GaussianBeam(a1, a2, a3)); pIm.setImageInfo(info); info = pIm.imageInfo(); AlwaysAssert(info.restoringBeam().getMajor()==a1, AipsError); AlwaysAssert(info.restoringBeam().getMinor()==a2, AipsError); AlwaysAssert(info.restoringBeam().getPA()==a3, AipsError); } TableUtil::deleteTable(String("tPagedImage_tmp.img5")); // // do{Put,Get}Slice tests // { Table table = makeScrTable(String("tPagedImage_tmp.img7")); IPosition shape2(2,5, 10); TiledShape tiledShape2(shape2); CoordinateSystem cSys2 = CoordinateUtil::defaultCoords2D(); PagedImage pIm(tiledShape2, cSys2, table); // // Fill (slowly so use small image) // IPosition pos(2); for (Int i=0; i data; pIm.doGetSlice(data, slice); AlwaysAssert(data.shape()==shape2, AipsError); // for (Int i=0; i data2(data.copy()); data.resize(IPosition(2,2,2)); data.set(0.0); pIm.doPutSlice(data, IPosition(2,0,0), IPosition(2,1,1)); pos(0) = 0; pos(1) = 0; AlwaysAssert(pIm(IPosition(2,0,0))==Float(0.0), AipsError); AlwaysAssert(pIm(IPosition(2,0,1))==Float(0.0), AipsError); AlwaysAssert(pIm(IPosition(2,1,0))==Float(0.0), AipsError); AlwaysAssert(pIm(IPosition(2,1,1))==Float(0.0), AipsError); for (Int i=2; i pIm(tiledShape2, cSys2, table); // IPosition pos(2); Array data(shape2); for (Int i=0; i lat(shape2); lat.set(Float(10.0)); pIm += lat; Array data2(data.copy()); data2 += Float(10.0); // Slicer slice(IPosition(2,0,0), shape2, IPosition(2,1,1)); Array data3; pIm.doGetSlice(data3, slice); // AlwaysAssert(allNear(data3, data2, 1e-6), AipsError); } // // apply functions // { Table table = makeScrTable(String("tPagedImage_tmp.img9")); IPosition shape2(2,5, 10); TiledShape tiledShape2(shape2); CoordinateSystem cSys2 = CoordinateUtil::defaultCoords2D(); PagedImage pIm(tiledShape2, cSys2, table); pIm.set(3.0); AlwaysAssert(allEQ(pIm.get(), Float(3.0)), AipsError); // // 2 * x * x // pIm.apply(&func); AlwaysAssert(allNear(pIm.get(), Float(18.0), Double(1e-6)), AipsError); // // 3 * x (const arg) // pIm.set(3.0); AlwaysAssert(allEQ(pIm.get(), Float(3.0)), AipsError); pIm.apply(&const_arg_func); AlwaysAssert(allNear(pIm.get(), Float(9.0), Double(1e-6)), AipsError); // // Polynomial 1 + 2x + 3x**2 // pIm.set(3.0); AlwaysAssert(allEQ(pIm.get(), Float(3.0)), AipsError); // Polynomial poly(3); poly.setCoefficient(1, 1.0); poly.setCoefficient(2, 2.0); poly.setCoefficient(3, 3.0); pIm.apply(poly); AlwaysAssert(allNear(pIm.get(), poly(3.0), Double(1e-6)), AipsError); } // // test table function. I don't really know what else to do with it. // { Table table = makeScrTable(String("tPagedImage_tmp.img11")); IPosition shape2(2, 5, 10); TiledShape tiledShape2(shape2); CoordinateSystem cSys2 = CoordinateUtil::defaultCoords2D(); PagedImage pIm(tiledShape2, cSys2, table); pIm.set(1.0); // Table t = pIm.table(); AlwaysAssert(removeDir(t.tableName()) == String("tPagedImage_tmp.img11"), AipsError); } // // Do some iterating to test the makeIter function (indirectly) // { Table table = makeScrTable(String("tPagedImage_tmp.img10")); IPosition shape2(2, 128, 256); TiledShape tiledShape2(shape2); CoordinateSystem cSys2 = CoordinateUtil::defaultCoords2D(); PagedImage pIm(tiledShape2, cSys2, table); pIm.set(1.0); // LatticeIterator it(pIm); while (!it.atEnd()) { AlwaysAssert(allEQ(it.cursor(), Float(1.0)), AipsError); it++; } } { // per plane beam support String name = "tPagedImage_tmp_afsdf.im"; PagedImage temp( TiledShape(IPosition(4, 64 ,64, 4, 16)), CoordinateUtil::defaultCoords4D(), name ); ImageInfo info = temp.imageInfo(); Quantity maj(5, "arcsec"); Quantity min(3, "arcsec"); Quantity pa(30, "deg"); info.setAllBeams(16, 4, GaussianBeam()); Bool ok = True; try { temp.setImageInfo(info); ok = False; } catch (std::exception& x) { cout << "Exception thrown as expected: " << x.what() << endl; } AlwaysAssert(ok, AipsError); info.setBeam(0, 0, maj, min, pa); try { temp.setImageInfo(info); ok = False; } catch (std::exception& x) {} AlwaysAssert(ok, AipsError); for (uInt i=0; i<4; i++) { for (uInt j=0; j<16; j++) { info.setBeam(j, i, maj, min, pa); } } AlwaysAssert(temp.setImageInfo(info), AipsError); GaussianBeam beam2 = temp.imageInfo().restoringBeam(2,2); ok = True; try { GaussianBeam beam = temp.imageInfo().restoringBeam(); ok = False; } catch (std::exception& x) { cout << "Exception thrown as expected: " << x.what() << endl; } AlwaysAssert(ok, AipsError); // AlwaysAssert(beam.size() == 0, AipsError); AlwaysAssert(temp.imageInfo().hasMultipleBeams(), AipsError); min = Quantity(min.getValue() + 0.1, min.getUnit()); info.setBeam(2, 2, maj, min, pa); AlwaysAssert(temp.setImageInfo(info), AipsError); AlwaysAssert(temp.imageInfo().hasMultipleBeams(), AipsError); GaussianBeam beam = temp.imageInfo().restoringBeam(2, 2); AlwaysAssert(beam.getMajor() == maj, AipsError); AlwaysAssert(beam.getMinor() == min, AipsError); AlwaysAssert(beam.getPA() == pa, AipsError); } // Test the temporary close if marked for delete. testTempCloseDelete(); cout<< "ok"<< endl; } catch (std::exception& x) { cerr << "Exception caught: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/images/Images/test/tPagedImage2.cc000066400000000000000000000160071476623553700215210ustar00rootroot00000000000000//# tPagedImage2.cc: test the regions in the PagedImage class //# Copyright (C) 1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main() { try { IPosition shape(2,32,8); LCSlicer box1(IPosition(2,0), shape-1); // Create a PagedImage. { PagedImage pIm(shape, CoordinateUtil::defaultCoords2D(), "tPagedImage2_tmp.img"); AlwaysAssertExit (! pIm.isMasked()); AlwaysAssertExit (pIm.isWritable()); AlwaysAssertExit (pIm.isPaged()); AlwaysAssertExit (pIm.canDefineRegion()); AlwaysAssertExit (! pIm.hasPixelMask()); pIm.set(1); // Create a region as a mask and add it to the image. // The region won't be found in the regions. pIm.defineRegion ("reg1", box1, RegionHandler::Masks); ImageRegion reg = pIm.getRegion("reg1"); AlwaysAssertExit (reg == ImageRegion(box1)); AlwaysAssertExit (pIm.getImageRegionPtr ("reg1", RegionHandler::Regions, False) == 0); // Define the region as the default. pIm.setDefaultMask ("reg1"); AlwaysAssertExit (pIm.getDefaultMask() == "reg1"); AlwaysAssertExit (! pIm.isMasked()); // Slicer does not have mask AlwaysAssertExit (! pIm.hasPixelMask()); // Check number of elements. LatticeExprNode expr (nelements(pIm)); AlwaysAssertExit (expr.getDouble() == shape.product()); } { PagedImage pIm ("tPagedImage2_tmp.img"); AlwaysAssertExit (pIm.getDefaultMask() == "reg1"); AlwaysAssertExit (! pIm.isMasked()); AlwaysAssertExit (! pIm.hasPixelMask()); AlwaysAssertExit (pIm.isWritable()); AlwaysAssertExit (pIm.isPaged()); AlwaysAssertExit (pIm.getRegionPtr() != 0); ImageRegion reg = pIm.getRegion("reg1"); AlwaysAssertExit (reg == ImageRegion(box1)); // Define the region in the regions group and check it can be found. pIm.defineRegion ("regr1", reg, RegionHandler::Regions); const ImageRegion* imregptr; imregptr = pIm.getImageRegionPtr ("regr1", RegionHandler::Regions, False); AlwaysAssertExit (imregptr != 0); delete imregptr; AlwaysAssertExit (pIm.getImageRegionPtr ("regr1", RegionHandler::Masks, False) == 0); imregptr = pIm.getImageRegionPtr ("regr1", RegionHandler::Any, False); AlwaysAssertExit (imregptr != 0); delete imregptr; // Rename the region in the regions group and check it can be found. pIm.renameRegion ("regr2", "regr1", RegionHandler::Regions); imregptr = pIm.getImageRegionPtr ("regr2", RegionHandler::Regions, False); AlwaysAssertExit (imregptr != 0); delete imregptr; AlwaysAssertExit (pIm.getImageRegionPtr ("regr2", RegionHandler::Masks, False) == 0); imregptr = pIm.getImageRegionPtr ("regr2", RegionHandler::Any, False); AlwaysAssertExit (imregptr != 0); delete imregptr; pIm.setDefaultMask (""); AlwaysAssertExit (! pIm.isMasked()); AlwaysAssertExit (pIm.getRegionPtr() == 0); Array mask(shape); mask = True; AlwaysAssertExit (allEQ(pIm.getMask(), mask)); // Create a mask and make it default region. pIm.makeMask ("reg2", True, True); AlwaysAssertExit (pIm.getDefaultMask() == "reg2"); AlwaysAssertExit (File("tPagedImage2_tmp.img/reg2").isDirectory()); AlwaysAssertExit (pIm.isMasked()); AlwaysAssertExit (pIm.hasPixelMask()); AlwaysAssertExit (pIm.pixelMask().isWritable()); // Put a mask and check it is correct. mask(IPosition(2,0,0)) = False; pIm.pixelMask().put (mask); AlwaysAssertExit (allEQ(pIm.getMask(), mask)); // Rename that mask and make sure the table and default mask are renamed too. pIm.renameRegion ("reg2n", "reg2"); AlwaysAssertExit (pIm.getDefaultMask() == "reg2n"); AlwaysAssertExit (File("tPagedImage2_tmp.img/reg2n").isDirectory()); AlwaysAssertExit (! File("tPagedImage2_tmp.img/reg2").exists()); AlwaysAssertExit (pIm.isMasked()); // Make a unique name. AlwaysAssertExit (pIm.makeUniqueRegionName ("reg2n") == "reg2n1"); AlwaysAssertExit (pIm.makeUniqueRegionName ("reg2n", 3) == "reg2n3"); AlwaysAssertExit (pIm.makeUniqueRegionName ("reg2na", 3) == "reg2na3"); // Now get the mask as a region and check it is correct. { ImageRegion reg1 (pIm.getRegion (pIm.getDefaultMask())); AlwaysAssertExit (reg1.isLCRegion()); AlwaysAssertExit (allEQ(reg1.asLCRegion().get(), mask)); } // Check number of elements. { LatticeExprNode expr (nelements(pIm)); AlwaysAssertExit (expr.getDouble() == shape.product()-1); } // Remove the region, which should also remove the default mask. pIm.removeRegion ("reg2n"); AlwaysAssertExit (pIm.getDefaultMask() == ""); AlwaysAssertExit (! pIm.isMasked()); } cout<< "ok"<< endl; } catch (std::exception& x) { cerr << "Exception caught: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/images/Images/test/tRebinImage.cc000066400000000000000000000131721476623553700214560ustar00rootroot00000000000000//# tRebinImage.cc: This program tests RebinImage //# Copyright (C) 2001,2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main (int argc, const char* argv[]) { try { Input inputs(1); inputs.version ("$Revision$"); // Get inputs inputs.create("in", "", "Input image name"); inputs.create("factors", "-10", "factors"); inputs.create("save", "False", "Save output ?"); inputs.create("shape", "-10", "Shape"); inputs.readArguments(argc, argv); const String in = inputs.getString("in"); const Bool save = inputs.getBool("save"); const Block factorsU(inputs.getIntArray("factors")); const Block shapeU(inputs.getIntArray("shape")); // Int maxMBInMemory = -1; ImageInterface* pIm = 0; IPosition shapeIn; if (in.empty()) { if (shapeU.nelements()>0) { if (shapeU.nelements()==1 && shapeU[0]==-10) { shapeIn = IPosition(2, 32, 32); } else { shapeIn.resize(shapeU.nelements()); for (uInt i=0; i(shape2, cSys, maxMBInMemory); pIm->set(1.0); // TempLattice inMask(shape2, maxMBInMemory); inMask.set(True); TempImage* pTemp = dynamic_cast*>(pIm); pTemp->attachMask(inMask); } else { pIm = new PagedImage(in); shapeIn = pIm->shape(); } // IPosition factors(pIm->ndim(), 1); if (factorsU.nelements()>0) { if (factorsU.nelements()==1 && factorsU[0]==-10) { factors = 2; } else { factors.resize(factorsU.nelements()); for (uInt i=0; i rebinner(*pIm, factors); IPosition shapeOut = rebinner.shape(); cerr << "factors = " << factors << endl; cerr << "shapeIn, shapeOut = " << shapeIn << shapeOut << endl; CoordinateSystem cSysOut = rebinner.coordinates(); // { ImageInterface* pImOut = 0; if (save) { pImOut = new PagedImage(shapeOut, cSysOut, String("outFile")); } else { pImOut = new TempImage(shapeOut, cSysOut, maxMBInMemory); } cerr << "Nice shapes = " << rebinner.niceCursorShape() << pImOut->niceCursorShape() << endl; String maskName = pImOut->makeUniqueRegionName(String("mask"), 0); pImOut->makeMask(maskName, True, True, True, True); // Do it LogIO os(LogOrigin("tRebinImage", __FUNCTION__, WHERE)); LatticeUtilities::copyDataAndMask (os, *pImOut, rebinner, False); delete pImOut; } { // verify a spectral axis cannot be regridded if the image has multiple beams CoordinateSystem csys = CoordinateUtil::defaultCoords3D(); TiledShape ts(IPosition(3, 10, 10, 10)); TempImage image(ts, csys); ImageInfo info = image.imageInfo(); info.setAllBeams(10, 1, GaussianBeam(Quantity(4, "arcsec"), Quantity(2, "arcsec"), Quantity(0, "deg"))); cout << "has multip beams " << info.hasMultipleBeams() << endl; image.setImageInfo(info); // rebin non spectral axes should work IPosition axes(3, 2, 2, 1); RebinImage rb(image, axes); axes[2] = 2; Bool exception = False; try { RebinImage rb1(image, axes); } catch (std::exception& x) { cout << "Exception thrown as expected: " << x.what() << endl; exception = True; } AlwaysAssert(exception, AipsError); } delete pIm; } catch (std::exception& x) { cerr << "aipserror: error " << x.what() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/images/Images/test/tSubImage.cc000066400000000000000000000332271476623553700211530ustar00rootroot00000000000000//# tSubImage.cc: Test program for class SubImage //# Copyright (C) 1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void testVectorROIter (const Lattice& sublat, const Lattice& lattice, const Slicer& slicer) { Int nstep; const IPosition latticeShape(sublat.shape()); const IPosition cursorShape(1,latticeShape(0)); LatticeStepper step(latticeShape, cursorShape); RO_LatticeIterator iter(sublat, step); LatticeStepper step2(lattice.shape(), cursorShape); step2.subSection (slicer.start(), slicer.end(), slicer.stride()); RO_LatticeIterator iter2(lattice, step2); for (iter.reset(); !iter.atEnd(); iter++, iter2++){ AlwaysAssert(allEQ(iter.vectorCursor(), iter2.vectorCursor()), AipsError); } nstep = iter.nsteps(); AlwaysAssert(nstep == latticeShape.product()/latticeShape(0), AipsError); IPosition expectedPos(latticeShape-1); AlwaysAssert(iter.endPosition() == expectedPos, AipsError); expectedPos(0) = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); } void testRest() { CoordinateSystem cSys = CoordinateUtil::defaultCoords2D(); PagedImage pa(IPosition(2,10,10), cSys, "tSubImage_tmp.pa"); AlwaysAssertExit (pa.isPaged()); AlwaysAssertExit (pa.isPersistent()); AlwaysAssertExit (pa.isWritable()); AlwaysAssertExit (pa.name(True) == "tSubImage_tmp.pa"); LCPagedMask lcmask(IPosition(2,10,10), "tSubImage_tmp.pa/mask"); ImageRegion mask(lcmask); Slicer slicer(IPosition(2,1,1), IPosition(2,3,3)); Slicer slfull(IPosition(2,0,0), IPosition(2,10,10)); { // A SubImage as a Lattice copy (RO). SubImage sl(pa); AlwaysAssertExit (!sl.isMasked()); AlwaysAssertExit (!sl.hasPixelMask()); AlwaysAssertExit (sl.isPaged()); AlwaysAssertExit (sl.isPersistent()); AlwaysAssertExit (!sl.isWritable()); // A copy of the SubImage. SubImage sl1(sl, True); AlwaysAssertExit (!sl1.isMasked()); AlwaysAssertExit (!sl1.hasPixelMask()); AlwaysAssertExit (sl1.isPaged()); AlwaysAssertExit (sl1.isPersistent()); AlwaysAssertExit (!sl1.isWritable()); AlwaysAssertExit (sl1.name(True) == "tSubImage_tmp.pa"); } { // A SubImage as a Lattice copy (RW). SubImage sl(pa, True); AlwaysAssertExit (!sl.isMasked()); AlwaysAssertExit (!sl.hasPixelMask()); AlwaysAssertExit (sl.isPaged()); AlwaysAssertExit (sl.isPersistent()); AlwaysAssertExit (sl.isWritable()); // A RW copy of the SubImage. SubImage sl1(sl, True); AlwaysAssertExit (!sl1.isMasked()); AlwaysAssertExit (!sl1.hasPixelMask()); AlwaysAssertExit (sl1.isPaged()); AlwaysAssertExit (sl1.isPersistent()); AlwaysAssertExit (sl1.isWritable()); // A RO copy of the SubImage. SubImage sl2(sl, False); AlwaysAssertExit (!sl2.isMasked()); AlwaysAssertExit (!sl2.hasPixelMask()); AlwaysAssertExit (sl2.isPaged()); AlwaysAssertExit (sl2.isPersistent()); AlwaysAssertExit (!sl2.isWritable()); AlwaysAssertExit (sl2.name(True) == "tSubImage_tmp.pa"); } { // A RO SubImage as a masked Lattice. SubImage sl(pa, mask); AlwaysAssertExit (sl.isMasked()); AlwaysAssertExit (!sl.hasPixelMask()); AlwaysAssertExit (sl.isPaged()); AlwaysAssertExit (!sl.isPersistent()); AlwaysAssertExit (!sl.isWritable()); // A copy of the SubImage. SubImage sl1(sl, True); AlwaysAssertExit (sl1.isMasked()); AlwaysAssertExit (!sl1.hasPixelMask()); AlwaysAssertExit (sl1.isPaged()); AlwaysAssertExit (!sl1.isPersistent()); AlwaysAssertExit (!sl1.isWritable()); AlwaysAssertExit (sl1.name(True) == "tSubImage_tmp.pa"); } { // A RW SubImage as a masked Lattice. SubImage sl(pa, mask, True); AlwaysAssertExit (sl.isMasked()); AlwaysAssertExit (!sl.hasPixelMask()); AlwaysAssertExit (sl.isPaged()); AlwaysAssertExit (!sl.isPersistent()); AlwaysAssertExit (sl.isWritable()); // A RW copy of the SubImage. SubImage sl1(sl, True); AlwaysAssertExit (sl1.isMasked()); AlwaysAssertExit (!sl1.hasPixelMask()); AlwaysAssertExit (sl1.isPaged()); AlwaysAssertExit (!sl1.isPersistent()); AlwaysAssertExit (sl1.isWritable()); // A RO copy of the SubImage. SubImage sl2(sl, False); AlwaysAssertExit (sl2.isMasked()); AlwaysAssertExit (!sl2.hasPixelMask()); AlwaysAssertExit (sl2.isPaged()); AlwaysAssertExit (!sl2.isPersistent()); AlwaysAssertExit (!sl2.isWritable()); } { // A small region of a lattice. SubImage sl(pa, slicer, True); AlwaysAssertExit (!sl.isMasked()); AlwaysAssertExit (!sl.hasPixelMask()); AlwaysAssertExit (sl.isPaged()); AlwaysAssertExit (!sl.isPersistent()); AlwaysAssertExit (sl.isWritable()); // A RW copy of the SubImage. SubImage sl1(sl, True); AlwaysAssertExit (!sl1.isMasked()); AlwaysAssertExit (!sl1.hasPixelMask()); AlwaysAssertExit (sl1.isPaged()); AlwaysAssertExit (!sl1.isPersistent()); AlwaysAssertExit (sl1.isWritable()); // A RO copy of the SubImage. SubImage sl2(sl, False); AlwaysAssertExit (!sl2.isMasked()); AlwaysAssertExit (!sl2.hasPixelMask()); AlwaysAssertExit (sl2.isPaged()); AlwaysAssertExit (!sl2.isPersistent()); AlwaysAssertExit (!sl2.isWritable()); } { // A full region of a lattice. SubImage sl(pa, slfull, True); AlwaysAssertExit (!sl.isMasked()); AlwaysAssertExit (!sl.hasPixelMask()); AlwaysAssertExit (sl.isPaged()); AlwaysAssertExit (sl.isPersistent()); AlwaysAssertExit (sl.isWritable()); // A RW copy of the SubImage. SubImage sl1(sl, True); AlwaysAssertExit (!sl1.isMasked()); AlwaysAssertExit (!sl1.hasPixelMask()); AlwaysAssertExit (sl1.isPaged()); AlwaysAssertExit (sl1.isPersistent()); AlwaysAssertExit (sl1.isWritable()); // A RO copy of the SubImage. SubImage sl2(sl, False); AlwaysAssertExit (!sl2.isMasked()); AlwaysAssertExit (!sl2.hasPixelMask()); AlwaysAssertExit (sl2.isPaged()); AlwaysAssertExit (sl2.isPersistent()); AlwaysAssertExit (!sl2.isWritable()); } } void testAxes() { CoordinateSystem cSys = CoordinateUtil::defaultCoords3D(); PagedImage pa(IPosition(3,10,11,12), cSys, "tSubImage_tmp.pa"); LCPagedMask mask(IPosition(3,10,11,12), "tSubImage_tmp.pa/mask"); Array arr(pa.shape()); indgen(arr); pa.put (arr); Array m(pa.shape()); m = True; m(IPosition(3,0,0,0), IPosition(3,9,10,11), IPosition(3,2,1,1)) = False; mask.put (m); Array arrs1 = arr(IPosition(3,3,1,2), IPosition(3,8,1,9)); Array arrsub = arrs1.reform(IPosition(2,6,8)); Array ms1 = m(IPosition(3,3,1,2), IPosition(3,8,1,9)); Array msub = ms1.reform(IPosition(2,6,8)); // Make subimage with a removed axis 1. SubImage ml(pa, mask, True); Array pixmask(IPosition(3,6,1,8)); pixmask = True; pixmask (IPosition(3,0,0,0)) = !msub(IPosition(2,0,0)); LCPixelSet pixset (pixmask, LCBox(IPosition(3,3,1,2), IPosition(3,8,1,9), m.shape())); SubImage sl(ml, pixset, True, AxesSpecifier(False)); // Test its coordinate system. const CoordinateSystem& subcsys = sl.coordinates(); AlwaysAssertExit (subcsys.nWorldAxes() == 2); AlwaysAssertExit (subcsys.nPixelAxes() == 2); AlwaysAssertExit (subcsys.worldAxisNames()(0) == cSys.worldAxisNames()(0)); AlwaysAssertExit (subcsys.worldAxisNames()(1) == cSys.worldAxisNames()(2)); IPosition ncs (pa.niceCursorShape()); // Test if shape and niceCursorShape remove the axis. AlwaysAssertExit (sl.shape() == IPosition(2,6,8)); AlwaysAssertExit (sl.niceCursorShape() == IPosition(2, min(6,ncs(0)), min(8,ncs(2)))); // Test the getting functions. Array arrsl = sl.get(); AlwaysAssertExit (allEQ (arrsl, arrsub)); AlwaysAssertExit (sl.getAt(IPosition(2,1,3)) == arrsub(IPosition(2,1,3))); arrsub(IPosition(2,1,3)) += 10; // Test the put function and see if the result matches. sl.putAt(arrsub(IPosition(2,1,3)), IPosition(2,1,3)); AlwaysAssertExit (sl.getAt(IPosition(2,1,3)) == arrsub(IPosition(2,1,3))); AlwaysAssertExit (allEQ (sl.get(), arrsub)); arrsub(IPosition(2,1,3)) += 10; sl.put (arrsub); AlwaysAssertExit (allEQ (sl.get(), arrsub)); AlwaysAssertExit (sl.getAt(IPosition(2,1,3)) == arrsub(IPosition(2,1,3))); // Now get and put a slice. Array arrsubsub = arrsub(IPosition(2,1,2), IPosition(2,5,7), IPosition(2,3,2)); AlwaysAssertExit (allEQ (sl.getSlice(Slicer(IPosition(2,1,2), IPosition(2,5,7), IPosition(2,3,2), Slicer::endIsLast)), arrsubsub)); arrsubsub += Float(20); sl.putSlice (arrsubsub, IPosition(2,1,2), IPosition(2,3,2)); AlwaysAssertExit (allEQ (sl.get(), arrsub)); // Test the get mask functions. AlwaysAssertExit (allEQ (ml.getMask(), m)); msub(IPosition(2,0,0)) = !msub(IPosition(2,0,0)); AlwaysAssertExit (allEQ (sl.getMask(), msub)); Array msubsub = msub(IPosition(2,1,2), IPosition(2,5,7), IPosition(2,3,2)); AlwaysAssertExit (allEQ (sl.getMaskSlice(Slicer(IPosition(2,1,2), IPosition(2,5,7), IPosition(2,3,2), Slicer::endIsLast)), msubsub)); } void testBeams() { IPosition shape(4, 10, 11, 4, 13); TempImage x( shape, CoordinateUtil::defaultCoords4D() ); ImageInfo info = x.imageInfo(); Quantity pa(5, "deg"); info.setAllBeams(shape[3], shape[2], GaussianBeam()); for (uInt i=0; i blc(4, 1.7); Vector trc(4, 4.2); trc[2] = 3.5; trc[3] = 5.7; LCBox box(blc, trc, shape); Record myboxRec = box.toRecord(""); std::unique_ptr log(new LogIO()); std::unique_ptr outRegionMgr( ImageRegion::fromRecord( log.get(), x.coordinates(), x.shape(), myboxRec ) ); SubImage subim = SubImage( x, *outRegionMgr, False, AxesSpecifier(False) ); for (uInt i=0; i arr(latticeShape); indgen(arr); PagedImage lattice(latticeShape, cSys, "tSubImage_tmp.pa"); lattice.put (arr); Slicer slicer(IPosition(4,4,2,1,3), IPosition(4,14,10,3,23), IPosition(4,2,3,1,4), Slicer::endIsLast); SubImage subimg (lattice, slicer, True); AlwaysAssertExit (subimg.isPaged()); AlwaysAssertExit (!subimg.isPersistent()); AlwaysAssertExit (!subimg.isMasked()); AlwaysAssertExit (subimg.isWritable()); AlwaysAssertExit (subimg.shape() == slicer.length()); Array arr1, arr2; subimg.getSlice (arr1, IPosition(4,0), subimg.shape(), IPosition(4,1)); lattice.getSlice (arr2, slicer); AlwaysAssertExit (allEQ(arr1, arr2)); AlwaysAssertExit (allEQ(arr1, arr(slicer.start(), slicer.end(), slicer.stride()))); testVectorROIter (subimg, lattice, slicer); } // Test some other SubImage functions. testRest(); // Test the axes removal.. testAxes(); // test per plane beams testBeams(); } catch (std::exception& x) { cerr << "Caught exception: " << x.what() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/images/Images/test/tTempImage.cc000066400000000000000000000217441476623553700213300ustar00rootroot00000000000000//# tTempImage.cc: Test program for the TempImage class //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Write and check the image. void doIt (TempImage& scratch) { IPosition shape(3,1); shape(2) = scratch.shape()(2); AlwaysAssertExit (scratch.isWritable()); LatticeIterator li(scratch, shape); Int i = 0; for (li.reset(); !li.atEnd(); li++, i++) { li.woCursor() = i; } shape = scratch.shape(); shape(2) = 1; COWPtr > ptrM; scratch.getSlice(ptrM, IPosition(3,0), shape, IPosition(3,1), False); AlwaysAssertExit (ptrM->shape().isEqual(shape)); Array expectedResult(shape); indgen(expectedResult); AlwaysAssertExit (allEQ(*ptrM, expectedResult)); ptrM.rwRef() = 0; AlwaysAssertExit (allEQ(*ptrM, 0)); Slicer sl(IPosition(3,0,0,5), shape, IPosition(3,1)); scratch.getSlice(ptrM, sl, False); AlwaysAssertExit (allEQ(*ptrM, expectedResult)); scratch.set(0); scratch.putAt (7, IPosition(3,7)); AlwaysAssertExit (scratch.getAt(IPosition(3,0)) == 0); AlwaysAssertExit (scratch.getAt(IPosition(3,7)) == 7); // Check if masking works fine. // To start with there should be no mask. TempLattice mask(scratch.shape()); mask.set (True); mask.putAt (False, IPosition(3,7)); AlwaysAssertExit (! scratch.isMasked()); AlwaysAssertExit (! scratch.hasPixelMask()); Array tm; scratch.getMaskSlice (tm, IPosition(3,1), IPosition(3,6)); AlwaysAssertExit (allEQ (tm, True)); // Now attach a mask and see if it is fine. scratch.attachMask (mask); AlwaysAssertExit (scratch.isMasked()); AlwaysAssertExit (scratch.hasPixelMask()); AlwaysAssertExit (scratch.pixelMask().isWritable()); Array tm1; scratch.getMaskSlice (tm1, IPosition(3,1), IPosition(3,6)); AlwaysAssertExit (allEQ (tm1, True)); // Change the mask and see if it is reflected in the image's mask. mask.putAt (False, IPosition(3,7)); tm1(IPosition(3,6)) = False; Array tm2; scratch.getMaskSlice (tm2, IPosition(3,1), IPosition(3,6)); AlwaysAssertExit (allEQ (tm2, tm1)); // Change the image mask directly and see if it is fine. scratch.pixelMask().putSlice (tm2, IPosition(3,0)); Array tm3(IPosition(3,7)); tm3 = True; tm1(IPosition(3,6)) = False; tm1(IPosition(3,7)) = False; Array tm3a; scratch.getMaskSlice (tm3a, IPosition(3,0), IPosition(3,7)); AlwaysAssertExit (allEQ (tm3a, tm3)); // Delete the mask scratch.removeMask(); AlwaysAssertExit (!scratch.isMasked()); AlwaysAssertExit (!scratch.hasPixelMask()); // Test unit handling. scratch.setUnits (Unit("Jy")); AlwaysAssertExit (scratch.units() == Unit("Jy")); // Test info handling. ImageInfo info = scratch.imageInfo(); AlwaysAssertExit (info.restoringBeam().isNull()); Quantity a1(10.0,Unit("arcsec")); Quantity a2(8.0,Unit("arcsec")); Quantity a3(-45.0,Unit("deg")); info.setRestoringBeam(GaussianBeam(a1, a2, a3)); scratch.setImageInfo(info); info = scratch.imageInfo(); AlwaysAssertExit (info.restoringBeam().getMajor()==a1); AlwaysAssertExit (info.restoringBeam().getMinor()==a2); AlwaysAssertExit (info.restoringBeam().getPA()==a3); } // Stream, unstream, and check the image. void streamImage (ImageInterface& img) { auto membuf = std::make_shared(); auto canio = std::make_shared(membuf); AipsIO os (canio); // Write the image. os.putstart("Image", 1); { Record rec; String msg; AlwaysAssertExit (img.toRecord(msg, rec)); os << rec; } os << img.get(); os << img.isMasked(); if (img.isMasked()) { os << img.getMask(); } os.putend(); // Get the image back. TempImage scratch; os.setpos (0); AlwaysAssertExit (os.getstart("Image") == 0); { Record rec; String msg; os >> rec; AlwaysAssertExit (scratch.fromRecord(msg, rec)); } { Array arr; os >> arr; scratch.put (arr); } Bool isMasked; os >> isMasked; if (isMasked) { Array mask; os >> mask; scratch.attachMask (ArrayLattice(mask)); } // Check the result. AlwaysAssertExit (scratch.getAt(IPosition(3,7)) == 7); scratch.putAt (0, IPosition(3,7)); AlwaysAssertExit (allEQ(scratch.get(), 0)); /* // Check the mask. AlwaysAssertExit (scratch.isMasked()); AlwaysAssertExit (scratch.hasPixelMask()); AlwaysAssertExit (scratch.pixelMask().isWritable()); Array tm1; scratch.getMaskSlice (tm1, IPosition(3,1), IPosition(3,6)); tm1(IPosition(3,6)) = False; AlwaysAssertExit (allEQ (tm1, True)); */ // Test other info. AlwaysAssertExit (scratch.units() == Unit("Jy")); // Test info handling. Quantity a1(10.0,Unit("arcsec")); Quantity a2(8.0,Unit("arcsec")); Quantity a3(-45.0,Unit("deg")); ImageInfo info = scratch.imageInfo(); AlwaysAssertExit (info.restoringBeam().getMajor()==a1); AlwaysAssertExit (info.restoringBeam().getMinor()==a2); AlwaysAssertExit (info.restoringBeam().getPA()==a3); } void testTempCloseDelete() { Int nchan= 10; Int nx=1000; Int ny=1000; TempImage tIm((TiledShape(IPosition(4,nx,ny,1,nchan))), CoordinateUtil::defaultCoords4D(), 0); cerr <<"isPaged " << tIm.isPaged() << endl; tIm.set(0.0); /////////Comment the tempClose it works fine tIm.tempClose(); IPosition blc(4,0 , 0, 0, nchan); IPosition trc(4, nx-1, ny-1, 0, nchan); Array goodplane(IPosition(4, nx,ny,1,1), 0.0f); for (Int k=0; k < nchan ; ++k){ blc(3)=k; trc(3)=k; Slicer sl(blc, trc, Slicer::endIsLast); SubImage imSub(tIm, sl, True); goodplane += Float(k); imSub.put(goodplane); } LatticeExprNode LEN = max( tIm ); cerr << "max " << LEN.getFloat() << endl; } int main() { try { { TempImage scratch((TiledShape(IPosition(3,64,64,257))), CoordinateUtil::defaultCoords3D(), 1); AlwaysAssertExit (scratch.isPaged()); doIt (scratch); } { TempImage small((TiledShape(IPosition(3,64,64,16))), CoordinateUtil::defaultCoords3D(), 1); AlwaysAssertExit (small.ok()); AlwaysAssertExit (! small.isPaged()); doIt (small); } { // per hyper-plane beam support TempImage temp(( TiledShape(IPosition(4,64, 64, 4, 16))), CoordinateUtil::defaultCoords4D() ); ImageInfo info = temp.imageInfo(); Quantity maj(5, "arcsec"); Quantity min(3, "arcsec"); Quantity pa(30, "deg"); info.setAllBeams(16, 4, GaussianBeam()); try { temp.setImageInfo(info); } catch (std::exception& x) { cout << "Exception thrown as expected: " << x.what() << endl; } info.setBeam(0, 0, maj, min, pa); try { temp.setImageInfo(info); } catch (std::exception& x) {} for (uInt i=0; i<4; i++) { for (uInt j=0; j<16; j++) { info.setBeam(j, i, maj, min, pa); } } AlwaysAssert(temp.setImageInfo(info), AipsError); } testTempCloseDelete(); } catch (std::exception& x) { cerr << x.what() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/images/Images/test/test_image.im/000077500000000000000000000000001476623553700215025ustar00rootroot00000000000000casacore-3.7.1/images/Images/test/test_image.im/logtable/000077500000000000000000000000001476623553700232735ustar00rootroot00000000000000casacore-3.7.1/images/Images/test/test_image.im/logtable/table.dat000066400000000000000000000023561476623553700250620ustar00rootroot00000000000000¾¾¾¾êTable PlainTable( TableDescLog message table5 TableRecord RecordDesc5 TableRecord RecordDescScalarColumnDesc5 Array@‹>_K+¿à0³Õª5 Array@V@@V@5 Array¾ÔU¥¿ÿû,>ÔU¥¿ÿû,I Array?ð?ðG ArrayRight Ascension Declination3 Arrayradrad* Array5 Array@‹¿¶öØL¿à4;Ýu'ý* Array5 Array£ TableRecordm RecordDescsystem restfreqtabular RecordDescLSRAÕû` TableRecord’ RecordDesccrval IPositionÿÿÿÿcrpix IPositionÿÿÿÿcdelt IPositionÿÿÿÿpc IPositionÿÿÿÿaxes IPositionÿÿÿÿunits IPositionÿÿÿÿ pixelvalues IPositionÿÿÿÿ worldvalues IPositionÿÿÿÿ- ArrayAÕ€¢ô’‰- Array¾p?€- Array@Ó9Êð¬‚1 Array?ð2 Array Frequency+ ArrayHz% Array% Array& Array- ArrayAÕ€¢ô¥Ã& Array- ArrayUNKNOWNUNKNOWNò TableRecordc RecordDesctype refer m0 RecordDescepochLASTc TableRecord; RecordDescvalueunit d9 ArrayradradHzT ArrayRight Ascension Declination FrequencyK Array Direction DirectionSpectral. Array. Array˜ TableRecorde RecordDescbscalebzerorestfreqcellscal ?€N¨ÛCONSTANTJY/BEAM¯ TableRecordO RecordDescHypercolumn_map RecordDescE TableRecord° RecordDescndimdata IPositionÿÿÿÿcoord IPositionÿÿÿÿid IPositionÿÿÿÿ, Arraymap% Array% ArrayArrayColumnDescTY’>¥èO>³dã>ƒ°Q>' л£ á=Qà»=­kö=Åþ·> \Ç>,•9=ûÁB=N£D;÷òâ<x¼®Þ½*¨c½¿:d` <·ž<½á=lÀ¥=Laß½Ÿn¾I ½Ï¾#<öè=ÎÈè=ôªœ=å/P=ÆÆ=Þl!>:ö”>‘M > 2>o¾ä>· ,À=p²=ÎFe=˜tã=¬_8=é=¾T<ìÄ»Æ\A¼@=óp)=Îl=Íb—>ݤ>c‚ˆ>¹>HsX>"!;ÌçÏ=w(r=ã;Â=oTI=%>®=wû(=}¾E<2oм…³_<p¼µ°½zª½Ð)b½Œm ½b¸½<~­¼Éž«;íϱ½Îç½Ø—©½¬€@<勘=ùŒ>üÎ=ökR=Ê6ú=¬Ä=½E½>³é>:E>H=¼â=<'5I=Y¸c=äãÕ=V8»zI½þ|>¾yi¾±3í¾¬y¾bX½¤vû<Éó=²ºƒ=´EX=̽LÖz½ž…F½N’Ù;·Ò=!í=3]÷<rÖ½û½ëú½ö$4½§1½F]r½Kì{½é'½¯ó <»dÐ=¡ÓV=“²=CDš<ïT;Ô:½ ÛѽÙ¾]™¾£𾨃4¾^î,½‰C€=HN.=ϯi=Ëí;=B£±½*"½µÿi½•îs¼6R:<çZº= ác8´ô½‘ñ,¾ ¨ ¾ß½ç \½ht½:¾NX¾„‡¼ÛJ2=uÝÐ=¢õj=dÛ×=Å<­óW¼:Ý„½‡]¨¾$I-¾…òf¾˜$Ô¾OeÚ½d³†=}ÙX=Íâæ=Èkø=i¼Ì2½¨k›½šf¼6 0<Ïã«<â~»ñ–y½•Z¾Šì¾:[^¾C ½va÷½B%¾$D¾LZ½²Ù·<¦±œ=”Lõ=xÑ=HY•=œZ<]Ó¼€´½¸f¾?“¾šÕ¾9ƒÛ½K=(=€¿j=°¿&=¬Ë3=wźøèH½o B½küŸ;#ÊŒ<ðãŸ<Ãͼ;3轉½¾æD¾G¤u¾ Ò™½uìq½Žm¾%œ÷¾mÀB¾mT½^‹=Y¢o=‚*¿=uêF=ZÌ®=5¥è<ÿ%Ô¼¹Z±½ç}•¾Kù¾#O½I,³=_üÞ=„“=‚2>=mkr<Âê¼¶áþ¼Î¹y<Ç|õ=½§<¼¼û¼)Á)½[×¾«å¾Fƾ)!Žk/½?á¾¥¾wOA¾GŽŸ½¨€‡<ö}Ð=Š/£=–øa=Œ¯=;=o¢ç<¨C½re/¾â¾(š½XQº=3À÷=5â=.‡è=OÇ'==H@<‰qä=·Y=Û–+=¸}É={Ëq<ë@Ö»ËǽcDR½ÿ´•¾+̽kOe=|¸=v <Ýe= ;ô=‡Œd=‘Qä=ŒÁ =9„=+µk<¼£<›Ïî<‰³s½cî*½þBL½ý c½"+¹¼<[½ž5A¾*N ¾I^½ë­<Í÷Â=ÞG2=üÞ0=½ü=>ü±±½’ó˜½Øî^¾ ͽúªA½Rub=;NK=f¤ê=!u=å‹=‚p =Ÿäï=¡:Ñ=³(= Tò<ˆ?‡<½Ù¢=7y¼ã/q½ºE&½Ä½D»-­L½c4¾^¿¾" ]½§É¾=Vd’>‘˜> ô¶=Ä×´<ê½½¥†C¾tƾ5ÿ1¾# û½ïš½Â=€«=¯4]=ƒKÄ="hr=pÁÚ=›œn=£cù=„ÚÆ<¿úó;˜yí<¾‹=®²»È<ˆ½l蘽ˆ!˜¼ÈH©äm>¿î=¿9ø<\nm¾ö-¾mú¾€‘¼¾<¾Â½Üæ§¼nѸ=­ßS=ôF-=ÃP¡=`ßï=kÌw=”µ= ‹‚=~Ê™<€ÏU¼9Ÿ/»¡0¬<׉r;ÐìÛ¼æ±R½!ؼz!= Ž˜¼{Ž>½jà+½”¹»)ë:=ߟe>'I >?Œ=³;¤¡¾$îZ¾‘Ïœ¾_ë¾Pdb½ÀUÞe`>¾Þ=›x¼=„úò=™Ð˜=§³ =†wå)DJ> ¿+=¡Ú7;ª ¾#&l¾—†ó¾¨ño¾V¦Ü½šwy=<™Ù>÷O>6üÌ>þQ=Çî}=ª×ø=»è×=ÆÈ¹=Ÿ¦<<¥Ͻ't’½Ž7½ë;е1PT=úáò=‹å\/ù_>Mê">+TÉ=ë5U=ßÕ¾=ùø=þU›=Ç1ˆ= £°½8,]½ÃÚÖ½‚¨²»¦­Ø<Ý¡®<]ä»+kö=ýýó=­7¼=WIñÊ=Íd=`Dˆ<®«½£ZP¾P8;‰¤E¾5h÷¼ìdB=ëe>Ut >cuW>4e-=ÿWm> ¹¢>#Ë>#=õx=V i½Òнڑ ½±Ž”¼m¤B= ™8<¼ ƒ:õŒÄ>%Á>Ùÿ=½ Ë=8Q<Ì¥=rX%=Ìò€=“¶P=w0<拼ÓLÓ¾Aö¾Q ã¾Ñ«»V1Õ>mH>~øý>|£>:þÐ>L> Q >G>FM9>K =ʪ¼±½Èbœ½½Ò6¼¡DS=A<ïù²<(>Iâ5>3«>³q=| <¼·â<ì¦=~ÑÏ=o—;‘ì>–2>Žå½>GV=þ"‚>'ÃÏ>^Ÿ>`•>’H=­”o:¤÷ì½À½¢|m¼“Ø¢= Ä'=Z/<‘é>f~!>[ëY>,¾D=»y£<÷’¡;½<¾ÆÈºÅé ¼xˆd<^œ~=0ÓO»pü½¤½’{=JóÙ>\;I>­—™>¤cš>^ª=ûÁu> )!>`‹‹>hw>†æ=¸#[<ÂI¼½¯½JÚH¼AÈ<óë= ö}=Šþ>us>xOÄ>K ³>?¼=C˜[»áÁ¼¼Qö½-÷n½SV,¼bA<='M<™3½6ç>½0K’=“Û=>w¦Û>Âç>½Fî>">ì¡> ½“>MJ_>\Zÿ>È‹=«´=="\œ<9w¼(žÈ;FN×<µwÃ=«©=XË>rn>‚Žü>cÆ>#7D=“h]¼(9®½/†»½¨Vn½¾dþ½^UC…>ÑC>ÖHÑ>˜ðX>–¿=ìƒú>*-„>> x=ûÉ=ˆ9æ=4×=*ï†<ßáõ\Ç,>€Ëï>qÞ=>@ë=Áï ¼*b½ˆ±½ï½J¾ W½Ñ9¬¼Ú+v¼$(º½ >´É=ÃÍú>‡ô~>×õ>êļ>²œ,>6°Å=Ð[>>-%=¿ëV=+4¯=s=p£M=W¡Y<,»ÏìÚ<€å=Ï„e>5¦p>mê‚>v†¹>S^1=Þqì¼U.½¬°«¾¿Ž¾0Õ¾Õs½‘kN½L¾½3X¼“Hã=¼Òt>ƒÆN>Õ›Ë>öÏQ>Éîô>aè|=ÕwP=Ì”=Û#=~µ½MŸ>L(È>o-}>V9¯=ÜÙs¼¼öb½Éf¾%“š¾KFH¾AÎN½ÚþW½}qÕ½Y«4¼ÏV´=¡c[>qfÂ>Ê#>÷o#>Ú/ô>ˆäI>â€=µPŠ=›?= Ù†»ü+¾;ºÇ==¡Ä=6v¼˜ ç½{5¼§È=ú˜s=‚f…>8Ñ>Ya>G³¥=»C\½- ¾½ã„¾.q¾Xç¾Zó6¾XB½˜J[½kÙ½¾=l@ó>Nþñ>µ½­>ìj>á(> 7>*éÆ=ĹÏ=f<1<4v–¼­껦٧<åìŠo£>!<=˜|ã=y›œ=ÞmÊ=k¯õ=D5Š=äR>`žõ>d®>;åf>¿#=«·S¼Üо *½ÿQǽ ±¸½Þ ¼Å.¼ëÓ½ƒ¿ý½Ë)½¤—Á¼Íyõ&Â>B0=eDå<µ“Î=Ÿ¹f=œ ›=»–Î>æ>yx>daí>¬C=Ï8ô=7å°½‚hä¾,™Ã¾"×½®LW¼•*L9î·À»›ø½Ah½È€Ã½×A½†x½ ä½Vμ²£½°}@½ö?½~À2½Ö˜Æ½>T‘=”E=ä“/='⻦nÂ=/Š˜=Qõ=å¹j>(Ÿh>s)>LÚÂ=ù°o=‡S<‘bv½®í·¾Ae¨¾.ÑýšŒæ;Z r˜½/­ð½£1‚½•Ľ-Ѿÿ£½õŸÇ¼¤RÉ=‹°N<´ü®¼Ü¤<”¢=‰ÄŒ=æ!í>C´>T#!>)‚=º#o=w;‚…Ó½¹î¾B/¶¾,3j½|HE<© $<¥Û†»1û"½/~½º]¾€¾—ä½ì,f½Ä§Û½i¬%½}ª-½†M ¼ëT¾ M°¾-•ݽÈkP<˜rË»'ýb½9¾ã¼‹ýž=Y-"=Ä•5=ö>#œç>WÀ=’Íd<Þÿí;—†t½¡Q’¾/šƒ¾„þ½MÆ™<íÜ»<´Fi¼ ¥½9ø³½³4 ¾ ¿æ¾M¾ÒÁ½âÅg½„œ,½м½oîY¼Ëùc¾ ô¾BKü¾Õ›½ ¹½ ?¥½4‘½]²= ’V=û=˜hW=×0W=É’B=‹iP= &Þ<‰"€½b˜±¾À¾ ‰½6í;<é}¿<Îô*»Ã}ª½-ÖO½«Ÿó¾ L¾#¦Y¾ºí½î=Õ½Š@x¼)16½jà=½ 9k½ê‘š¾:!œ¾6;š½¥·î½’¼i½¦“#½=<ï¢ =@¹}<ûŽ=aLÙ=Ô«=žÚn=\î= “¼é(½Ñ²•½åaþ½0Â<ÈÛÜ=fR>f=£:“¼¾ðx½×Óß¾ 9T¾c½»kj½9ÔÂ<ûû[½óÖy¾+ï}½>½F™f¾lG¾G•a¾$Ã×½°eϼÉVh<¶§<üÕ<”,¼`/xG!Ô>>Í =å ×;†@½¥Të½ù0½õ°½®ÜV½¿<ø¦½ùU¾HÞ…½“F¼õfÞ½Êݾ6°/¾ YнSÚô:¾Î.<Ê <ñ æ4Nã>rn!>UþÚ>î[<ïôó½@ÝP½Ç®]½à¥@½§&ѼؤK<þkc½Ùn&¾RÀÞ½³¼Ã²·½|£¥¾ X½®÷!»” = Á<ØŠÁ<Š«Tºf£’½D`X½fè¼nM=U¥!=u¥b=®x;ô{ú=™Æ>cø>`¶«>†žø>VÂa>h==BZN¼[½ƒ£½Á)m½¢CÕ¼¸=³=*p½•ÎM¾D„нÕ¾¼âx$¼©§N½…ú¼8D>=l¥ž=7ì<ÎC•¼78ì½2Rq½Äì½Óïä½WKÅ<ÿ\=cÎ=&÷Š<Ñ´=€ÈŠ>´>w†­>ˆEp>B\v=ß =_bú<ò¼É´½’'!½›?¼È¤\= ½´¾"°½íd½Îì<¤Ue<®r„=Ÿh(=úˆ =Ì>—<V½]».½à÷2¾%¼¾G½¥RÕ<–9å=]$b=PÛm=;!=ªèÊ>+€Ú>u‡>}a‚> Ÿé=¥K8=O ä=kít.¸k>=!s=ú%î:æcy½Ýh¯¾B˜¾r(¾Jî½Æ2E&¸Ú>\<>YC=ô*=M[¢=ã}=‘)=<á$¼&’¢½ZL»½ <‹¢<<«®€½—3^½åΊ½E³[=¨˜ž>@vØ> ò>r°w>žŒ¼³Vë¾(ðG¾‰c¾›`g¾l›ô½Ð´<ŸíÖ=oÑP=‹»Ø=wÇ=Òuò>¢À>6Øí>.©V=°wì<Ìó=<£ðí=‰I~=‚ˆ»<¡ü$¼ø…ؽ Ī:"<žËѽ4h4½É—ɽ;é4=Èö>wÍ$> LJ>:> Ùã½Nt{¾_°¾©¾³C¾dܽËhÝ<Ï’ø=|w´=”=©9X=Î]„=þ„>÷>±Ë=}z< ø:é5]=J±=ˆ™=9!¼:ßžÔ¼±û¾¼Š €::CX½/½¨î½Û‹=Ñg|>‡÷>¯øæ>—‹)>]y½›½y¾ƒ¹¾»G¼¾»FB¾~Xؽ½’ü<âã%=€:=–=ò=¨Tµ=¾ÜÜ=Ôm%=â™;=Ò÷™=FAÉ· ¼Zª~<Ç”‡=mùå=„]d=$W-;aÛ¼ô´Š¼×=©½8¡½ŠؼØæ=É;æ>„‹ü>®«R>—wö>  ½º}c¾‹c¡¾½Fk¾²Bš¾ih{½ªx<Æ_=xoI=”uX= Ú=©É™=µh=¾ð =´8E=7„ºB^º¼¾ ú¹Ïë=6k=¢Sú=¦©Q=!–$½ÆÛ½E…z½Oë[½qV·¼b·=·=°>jÍ6>žC[>‹v>ßÔ½³èõ¾…ø—¾°?ù¾›qA¾D@£½ò¾<‡%ê=g¢=’ƒx=˜§=•øÙ=¢L=²)}=¬¢ÿ=@ç~;¹®¼ÞÁÚ¼†¦ô=ß=½s=÷DÞ=¤Zb¼Ö)…½b޽ÿ½bzL¼U4=ŸÐf>@Åá>„(8>xE'>½†A¾gÊd¾˜»ÿ¾{i¾]7½Uˆ%ß=ð|à¼^ï½.CC½Ë}½[¥–¼ˆj=ƒÎv>@>Kd“>KÆ‘>éò¼ã_¾/üß¾vc˜¾>ËR½Í±5¼ØÞÔ<èÚ=uÀŒ=èü=–@={;=‹Þã=¦ÜÖ=©\=SëÅ<& ¶¼®4¼‚¾š=/“Ÿ=ù:>4ö>rN½7FÔ½H0¥¼e†F=Cîý=Ù±Î>Yå>s>ÔË<‚„<½Þ5£¾9u¾ µ½t¨;Ϫò=5=þ=Ÿ|=²o\=šËß=p×u={gÖ=’ªÒ=•]W=D!û lÙ>=U>!'â=!ø5=€¯»ªPB½L‘¼±ÿý<Ò$=‘´}=¹$Â=Ø[»=íCÓ=dÔñ½49ç½ú+n½ÇļÔ4Ã=5ç>8÷>!žå=ƒÝ=§3‰=N‘(¼…ß¼æì•»é¢=ÃL=;½Ü=yP=Àm*=§çÜ<2µd½Œ…€½‹ Úº OÄ=²Š>H]>'YÚ>óõ=ª/J=cEô=- ‹=0<î6•<Åì´<&‹¨¹ºHø<‘ =’«)>¥ò>(>5=¯fx>ix=êfð<ãÞñ¼ö»½ w; gú;Ù?<ŸœÓ=}Sz=µÖê=Hæ´¼À¶‘½9`‘<Šhç=ýàè>_‰À>fUÀ>,X8=½dõ=hk= ¥</;XÕ;õP¾;«C :ê± <‹Ÿ=s6´=Úèb> †ƒ>‹Þ=Ïìj>+ ¼>4ÙÍ=“¼Íeù½‡x½8ç¼ø³(¼“`ì<Ã+@=›Q\=‡d;<æ<¼Ùžd<áAp>>7L>“†:>ZÿB=å1½=„Ž= ѧ;̼‹þ¼I»@°„» ¨°;ËF=Þ5=ŽÍw=Ûõd> 6æ=á@>Gàœ>iÓÇ=íѼlð4½¿•˜½šEê½…™ó½N% ¼U+b=JW=€;¡<Úæ¨¼bV€<ú~Û>"ÂV>Ÿ z>¯ ï>‡›e>$”=©äK=%8;@bq¼èàé¼°¦¼\R—¼E>:º»TÊ<ß„$=%> Eg=à2>X1c>‡ Ô>]ã¹ñÉœ½åÖ—½â •½Ä½š½2½.w)<„þ*=(Ô<Æ$´¼,:Ž<Ê&>ÃÑ>£^>Á1'>¡Á;>F{1=èÇ3=_¹B;Îи¼ûW¶¼î?¼Çï¼×P½Ä?½zy¼<'×=NÙâ>å=È”p>[C >wq>4ai<\Cú½ôÈš¾ ]º½ò{潺a½tëܼwØÁ;øS:» 伩{< 0=þ_ >—â~>ƹš>·ì>€‹à>ãz=—=o==™KM>Q v>Ží…>>›s<Ð$T½î]¸¾j=¾®Œ½Ã˜Ã½„,v½)¦¸½ (½ œy½>Ïܼ²{=ªß¾>dl>¿†V>Ç1>ž&¯>K¼Ô=Ànö<“P¼¡ëҼܨ”½¢Æ½Me5½bj‡½„ó‘½;[H<¼q¥>¿û½ÔA½’E›½;=>a>#Æú>L7ö=\ …½é4ê¾4Óz½á|y½œ9ݽ—g4½h×O=T…=Ó¦¯=Öò=ZÛ=-è=>±< &–½†ê½)½ñ¼üð¼Úf½K ½[©g½Œ¤½­“{½ÂÝy½å¤ä¾¹$½Èq¾õ*½‡S¼ψ\<ÃTT=ö¸><>ý=¦Ûk½Ìo:¾J7ƒ¾ö½û¦½ؼÃ>g=Â+=¸sL=Ã*$=Nd†<àï<žµX<&˼›f ½_½ê´¼˜á¿¼zAѽ=K½y ß½®) ½ÄW‹½æA¢¾À½ÞÊ5¾d½†G»“Mp<‰`H=·}‚>#÷ù=ԟo‹¾=Éî¾';𽓗¦¼0Ò;© ´=F•=•ÓK=¦1J=UE*<Ý”< ´Ê<›é°;5.¢¼• ’¼Ô'¾¼{g°»Æµ#¼¤)+½Ubܽ¤Ø¹½Â€o½àô¨¾iø½Ù…¾½ƒ& S=êüÛXP¾Lñ¾+ÊP½ Y5»ƒTG<ùñ*=-`=pè¿=…æ=Xo!=R<Ýn=” <Ï-;û'¼jl¼8µ»‰ßž¼„h*½5«È½‘1Õ½¸l¼½Ô%½ß7轼±½t5Á<ï´¾<¹e¡=- ¦=¹ˆÛ=Ù[_;ﯽò;l¾#|q½ªÑ:ø€$=Vo==[=R» =SoÔ=OR=Ã=ä=5Ný=:²É<º–:¤ØV»\ã𻼼Œ%–½ ˜×½ox½¤¼Ÿ½¼®Y½°Â½ñµ½üÕá½T}X=Þ<ÇDy<šLn=08C=˜¹©þ½ ‰¼Úc×½"ѼÊU;]–5<¥%)½%|ͽ°‘¼RKн²½±ãª¾ 2ã½û}½ºd ½¤,ª½ºUC½H,=<@ù>Ge>à$=§ã<Õ0×<"ñ»+6Þ;æG€=6ÁÄ=Ót=öSN=ÜP=¤ØÃ=!ø•»á¼ÿVæ¼I§¼=½Ù;pR‚= 9=A«]¼bâ­½,xæ½]D½—¾ü0¾9cu¾7¾ï@½°Æ¿½˜½Uå=Qy>G>r#=®©^<ŠSG»…¥ž¼pXT»³¶=j=ë-:>…B>òí=ÛW=UçÊ9ÊÔr¼×L0»³3<*ÕB<û²=lé=Œm<2@B½;e¹½ÍP½ú.N¾$ð¾Q­}¾Tž}¾è½ª:2½aW°½ O…=&³>˜Å> G=¢—b;|ëØ¼™.›¼¬b¼4,#<ú:o> ­>@ B>A[>Êc=l]ºÔ-̼¶J|Q =}9¼;Á_½;¸¼­^‹¼=F<ø{d>g†>\1>et>|¤=^ÅX¼Gá]¼´Ù=‰™=Œ  =¦$=¢„Ý=²¹_=Pðy¼ß«Á¾ dü¾TCH¾T¿¾9ú^¾Ó½½¯/½mɼj#n½:-½òº<LJg=˜7y=ؼßÓĽ-à’¼†ÜL:ƒÏT=hû> O>l>|ùï>%¿>=2ØE¼÷Tn¼ÞãÕ=ZM·=Òsº=Õª˜=šøß=§M¸=…×v;D¾ò‚¾hfz¾aIˆ¾×½¨ w¼ÙIÒ;›ÿ:¾qE½q¹š½²K#½xGæ:š­¼–½,Y ½ES¼$høi>lð>O>&'­<ð>ù½POݽç‰=«U> –¾>—K=‡ÎK=‰·7=š>Õ=3ºX½Ë޾e•Œ¾i€!¾ ô»Ø´î=E‡‘=FÉ[<7³@½•¨Ê¾ °&¾£ó½©.½il$½fi6½L`»¦ø2<›5w=6ƒ!=ûŠj>^à>uË»>=<…x²½‰;R½JèK=›÷:>$/Õ>¼Ï=i¾=EÛ%=¦7U=³è+½C¬¾L…ï¾of½õ4=Y!=î¼U=¹Yª<‰G{½ª¾5[X¾`òh¾'V½ÛS½‘®—½LÁk»ˆ*u<›È.=(}G=ÜÜý>F À>[ÇÛ> èq<¯½•ŒD½jxÞ=–•ù>1“ >%í=Tѽ<ôª=«h“=þQ =%™â¾#ʨ¾r§<¾ \=¢ ä>'_Â>&”<ºìc½«Ç¾G`ʾ†þ¾fk¾¡s½´U½X ¼r<5óD<òøâ=³Ç~>'¤§>;m)=è>¬;ñCm½…Û$½cYN=‚´Œ>0r >-˜=_='< Î=­fÀ>"=Î^½éeu¾r„²¾13P=ŒyL><þ> ôD=:½´#¾Atj¾#q¾„Ħ¾†>Ÿ=½fË#uú>,à”=ƒœœ¾j>ô ½•õc¾kÛT¾Z*<ð _>9;>=L=‘Ž,½^¹¾$H_¾ƒÖ«¾†a¾M ¾ß3½«XÀ½ ¼ŸT±¼P¨= lÜ=Ëz=øaç=š›ë<¹,.¼€Îϼnt=BqT>½>%3v= /ù<¢…n=³(¬>Ĉ>½;ºÐ¾\o¿¾w|ƒ¼½ÕÙ>#«>WlÅ=ý°d =¼Î¦=Sª=ºpè=ßÉ!=ÅÃ&½;C/ ¾yß$½ŠGþ>½ú>qå>F^D=¬58½[ˆ:¾*˜Þ¾E©£¾5« ¾!ðâ¾mú½¯ ½Jöd½Ä⼩5ö=Ë=—:&=cÇ{="Jõ=)¬w=d ›=‰—6=ç&m> =Ïx0=?ï¸=Ãú$=¯½Ô=L´D½3T¾"¯N¾_ci½¸ÍÐ=äDc>ƒ´>‰¨X>'!¼Šª½>«?u>uó'=œÃ‚½Œ¶/½Ðia½ßÙz¾ Îü¾;#í¾í½–*º½;Ù½léå¼™&<Ì¿Ý=€=5‰=…C)=Õb=ߺN=äö‚=ßð=ÜF=p¿=×ýU=¦¼põĽOƒó½¸tî½ævϽ|¹ë=Â.d>‹r·>¿Ÿõ>™Ò#> ¼òì网Uh½œdñ½âÔK¾3r¾‹°½¯½Gs½‰SÞ½,$m»Î<¹ôá<éE1=~Õ=Þu6=ø¢=ç…£=È¢=¥Öõ=N´=àu¨=ª?¼,IN½7Þ½u>½pÉi¼Ùâ=ÃðH>„k>ÂŒ§>«[P>5ˆº;ªˆ ½Q¥…½QÍR½ŸS¾£ ¾â`½Ê?V½f£‰½“ÿ½yF•¼î‡N;†<ŒÕ³=XçË=Í,~=ôƒ=Û·<=«L={ry= Ö¶=çfØ=Ô—Ï<‰4v¼ßuнÕʼw6;Źž=Ä™Ó>k÷†>³h>«›ˆ>Oë=ùx½&ýŒ½Fo´½ãP¾ ›R½âÄÁ½2m½šeL½—xѽU¸É¼w¦ß;ÅR'=%–®=§>=Ò"¨=»ót=‡“I=&ˆÌ=jbbº. ¼…-x:<ܵ‹=»ÆÄ>DJ>”‰á>šG8>SUÀ=qµj½øÀ½Mê¼öL9½äɽöŽ ½òj9½­ Ὗú”½§½Œ`á½r7»5ÄÇ<áØ=i1=˜-ç=‰š"==×¶<¹κ¡,Ë=î‹„>Ÿ=ÃÀb<ã3è»4cP;Ö¨Ë=µ=¥D>¡Y>[uy>tæ >=á=•ʽ R½>|¼äÕ콪„½Èy@½ñ-t½Èvƒ½¦Ô½­Ù•½d>½(_»ö‹<„ã£=!©=(ÁŽ=±{<ÝB[<2¾b¼¨ý=ëO4>È =õ4=H/ë;°Ò0:üÄ<ʺž=†^ü=ÛÀE> BO>$áà>Y=”µ#½I¼½‚ʸ½'{`¼Ÿ6þ½—„½Û‚½Ó¹½© Û½¯5½¡_L½2™F¼ç…<'I<û¥<9¿„<]Ì!W=þ`’=jYr<¯’»ÄmÕ<1¡^=I =˜:j=•Ä»=¢†}=¬t=_ÿð½)c{½®ªj½„Ë伉„k½RX-½°ñ„½ÅþQ½¥8û½­2c½8P½%M໦’X;H¡:»èÊk¼´Æºò¸©<ìó<…&x<^Æ=Îä->—®=ßDY=O/~;²s|¼Drÿ»9q =Ôì=Y{¶<ð‡ž<àÁ<™Gr<£ ù½^O+½Ú§Ÿ½½Z¼Ü ½ ¬>½r°«½ )¦½— Ž©"½—Ú½ «e¹@£; V¼jl‹¼Š¬±»o’<¹¹= Ž<û‡=·:š=Ú‡Z=¦â°=`ÛºàÄ‚¼t…×¼LH¦<Ø%:=/·,;Τä½CŽp½@5û¼æƒm½˜W ¾S§½ñ*2½2Çþ¼ª´7¼ñ>Ù½O‰½zøy½¡Ì½’'6¼ëa7;¡?þ;¨Yâ¼R<…¼OGy;Ôýø=©=tÂz=m‹v¾A¨*;+mD>Rµ >ðá? rw>Ïát>«{½ þ¢¾=&Ͼ>N3¾`·½± ©»O ÷¼`­p½©Ý=¶ì=×h¢=ð*ñ=mnË<Ÿ¼i¡Ô½Ÿ\A¾iCü¾« s¾µ¾Žê¾‚‹ì¾X½ÌÓ}=Ф¾˜õ½½‚Ï>%õ>ÓÕi?†Ä>ÔÛ>:ã¼7‘Ó¾b®¾+¢¥¾4sˆ¾#Y½â¥¥¼¼½í<·¼’<š±4:7d=“¦X>%ô>O{›>gy=*ž½ 1½‹Ø²¾HȾªëò¾Ê€–¾˜6Ù¾jÑ;5ÿì½Û½<£EÀ¾ÖϾÂÒ=ü¢L>º—]>íqI>̺s>Rþ<‹-¨½æ%í¾-AÛ¾8Òt¾);´½õ‰Ð½”[<½¦=*¡¦=F:=Ï >N{_>‰œà>QÌ=Üâ½Ú½GÉp¾wg¾–Ä2¾ÊÆz¾—Z¾G’q¾Èǽĵµ¨°ì>Ô[Ú>¹>W}’=!äE½Ò‚ì¾?0°¾LîT¾0,O½ëK̽$˜%<š‚È=Iž=‚q{=ðü‹>c…f>Ÿ{ý>Œ#=Ý˼‚ûr¼Þ(C½¢)W¾j¶ë¾¸›ê¾Œ£î¾zd½¥'н†<Âh¯¿`ó¾•E=ºKº>žj{>»¬>šx>@ÁÍ==Þ½½É­ ¾Wêò¾k‡¾9Õ©½ËJ¼öˆ—a¡>¦7´>¥äÏ>ë½;øå¼i›|¼Ð§û¾º¾˜EK¾qÞD½Þ§E¼º±ˆ¼Óœ=d^"¿e)¾Ÿ6æ=ÁO®>–ßB>žóË>_ìª>ü =^½Æ"œ¾iÇ<¾…@÷¾E´v½¡ûÈ»û©ž;ª%Ú<%_ã=6—³=Èrâ>JÿŸ>žÊq>±ðC>C_X<Õ;¼Ÿ±¡;Ž"½µ§¾aš¾¾=~½|Eþ=²Ð=x)æ=Ø/R¿N®¾“±*=Ãm6>‡Ûÿ>sü_=ëf=H™Â¹Þ^p½º‘²¾cÙÔ¾is¾P/,½|Ó<»7W;%»V¼ç̆¹^c€=‰/P>+K >¹9>­ºr>Mã<Òû½I¢?¸¡@½Buñ¾®¾­2¼o]£=Àì2> "ï>'¦½¾éŒ¾tÏ’=ž°:>Mä> kŽ»âxH½ZJ½-¶½˜Ð¾ z>rem>›€Á>:ƒo;O?X½Éír½ 6޽”%½¼àr½£Rƒ<Ø >Ÿ‡>L¥M>[û&¾§ðu¾8«Ê<þÌ=»Ršºz4à¾A¾!”½§6G½3H½ã€#¾[ôT¾;~5½µä=²»7L9Ë>-‹> z¯½u¾ ½ž½³…ü½Mõ·½{vk½3ä1=^p·>+ùV>q¥ë>yü¾Tp¦¾³½4 A½s²È¾*?l¾ŒDо{—½Ú†s9´6€¼U¯Ü¾ݾ Ƽ¢ ý=ç.,<Û›í½ó“O¾%-r¼Žf¼=Ýà>0±>AÁ®=§ÎŸ½«̾T´U¾•.½£c½b¤¼é®=y«e>'Ãz>jÔ)>trÓ½á¾$½ËÆ6¾ оt¶†¾³E¾¾Ì¼R¾œ²f½æo¥=YÏL=É ¼Áv½^Ĩ<0Îû>[h=#ÿ¾iõ¾Ep4¼ìói=Ú×ï>ÚÞ>×(<éLh½ô›³¾r7¾7o½à{⽆غ½ lÈ=&Vª=õ߯>.…2>DyÁ½9п½¢µ½¾e:¢¾Óç¿÷n¿ É¾ª’è½Ì!W=ÔÀ>N>Q>(ß=]A¹=mÔ:>¨=K“œ½øÊv¾Ta ½_=Ú¿v>&=¼æ†»Ý˜%¾Û°¾mœó¾AÚú¾å–½ª%H½_ÅX»ÊÊ=ÞÝ=yt=Úm¼ †;½uc¾Ž‡¿ ²M¿(¾"¿U¾¨ Ù½— Á>Þ³>Žÿo>„è.>5²Ö=õ×Ñ>®B=[̱½äµ,¾U Á½,ñ=Í>|> &æ=‰"¼v4½ás¾DÅc¾,3F¾R½Æžö½ª@½”D½¡ñ>½³õª»ÉÔ@#Ó$>¢ÝO>»¥ä>—‘;>@l”>!ø·=Mº½ÏÊξMôç½c€=¤ –=û s=€ö¨;—sú½rÕå½ý{½øNÔ½ÚJ•½Êèu½Úµ½¾p’¾Té¾ë޾Äé=.È=' ·¾RÌ¿z¶¿cT>¡úû>ÜûA>Æ‚ >|•>)s=!‡H½À?u¾DÀ©½Ÿåï=9±²=ÏŽJ=ˆƒî=*Ü;øÜ½0…޽€ Œ½–±B½².½êÃE¾K0e¾¥׾ϲ€¾pÈ`=§ŠX>mˆ½­Sľ÷Ô‡¿,oD¿¹¾o>¼|iˆ>Ëë>3)>ä³m>ßex>‘#—> ‹<¿;¾½¸D©¾<‡G½Þº×>,=‘„8=”dÕ=© =˜•×=bœ»ÝϽ»†½}j‚½Í¼¾d) ¾Ëüé¿Ⱦž«t> +ù>j>ê=‚>¾ªBµ¿³û¿Î ¾]¡¬¼›zZ=ÓIÝ>cõ>Óoµ>Ý•ì>“»Ž=鈠;˜íš½¶lÿ¾69©¾ |½\é= çZ=”k}=èÇÌ=üí™=®¸=q »“ ¼ì•|½ƒ8ý¾S“¾Ôì4¿™U¾­>DîÝ>¤¶Ï>X¾"ÿ¾ÜÇ*¾ëǤ¾[мþx¬=˜2‘> ì>®×,>Ã|ƒ>†¤=©Fļmh ½¶SW¾/Œè¾0½(½ÙÉÛ¼Ö=|‚F>³>¹=à'~=T„I<Že?;×1μ:+¨¾P¾¾yª¿ +¾£Á¼>z=Ù>Æ’>¦ŠÞ;-¾—~œ¾ÏJs¾dIp½;Ë=d“=Ò¥.>=š>™Kª>YMu=Iê§¼üÞo½µBM¾'ˆÁ¾H³5¾ì½]= »=í~Ý>Ù=ÔT4=I(<Ûi=g=7wt½8î¾ÿ¾åþ†¾†ã§>}‚>ÕU>ÆRO=ÿrk¾4­A¾²3¡¾n䮽j#‰=TÙá=‘ó<>+Ö>Tx‘>Þ•<¥B ½,€e½²…¾\ß¾Vùr¾?©.½Åxª;äQx=³à=ã”ô=™Ž<ô{ò<Í=qÚ=»´«–›ª>ÐZ’>È®š>Fik½¨<0¾’¾o~(½{Ð=}ØT=+L=â">u>=Òõ;3H½>s9½­æC¾¾]b¾XÞ¯¾ÎÙ¼ïÛ´=NF¯=‘ÑD=Ï;{Ì´<‰à¡=8¦=ð€W=£LÖ½EuG¾> ½Ò*Ð>”=1>¿Ëˆ>µp>[µZ¼HRˆ¾Y!¾^H¼½ph¤=¢7p=ÅK=¼½b=ºÌˆ=”EºÑÌâ½5Q½«º¾èw¾aó`¾jM1¾'½€Ô2‹3>«,Ö>–|x>JÁj=P÷¾A¾8–½L·6=Æ‘i>÷m=êºA=¹ÃÒ=‰•;éRk½ì'½­r'¾ؾh—ù¾v«Š¾;Àž½µI0¼[™c<è¼¥Œâ½9¬;Gͪ=…*p=Ùu“=ï¥=¤è<‡Ýß<+ú<>~+>šx¬>p">%f‡=‡à½/™[¾ƒ½—´=Þ)ë>GÙ§>+´Ü>Pö=¨îÒ<·ÿ‰¼ð졽³îŒ¾$>^¾s·o¾õþEƒŸ½ÐîǼàˆºçd ¼¸Dº¼óðž;¸'v={ñ×=°¬Ð=ÉF=´¹ª=nê©<ì4W>biª>ÆÉ>Bjô=õ¥Ú=«`þ= ¬T½n®¼¹/p=ä!U>v‹Ö>mÈÏ>5q’=Ùñ&= 8üÓ<{½¾¥/¾2Së¾€\´¾‚Ù;¾D¿T½Ô€¥¼ôßJ:µ«ù¼u ¼†ŸÛ<„jË=…IÆ=›ƒê= ä'=Ÿ‚=º<ß²†>C™Ž>‰ÈÚ>+îU=¯r=·XW=Ô|`9ØGP»›qŽ=Ùlê>‡“_>ÇJ>c& =úôz=9N½´½ÌÉH¾;c¾„'-ú¾:#ýÄ.¼În‡øy>„—Ó>+›r=‡U‹=°³F>B=_Ö‘<„Æ=Ä%5>„]Ü>˜¬“>rËÊ=î³;ßܽ`CŽÞOо8¸¾]½¾xȾ'U޽§­¼šù]<–'<ý„'<¯]X=U:=Û5µ>s|=ãÅ€=°’¢=‰(g;¤mð=êH:>uU>8h=€É=*(>ËK=¼¥=&–C=®HÀ>`¶Î>†ð^>RÊj=Ÿ¯¸½;P½Â¼ú½ôŠÛ¾%V¼¾hŒÈ¾\딾˜7½ˆ¦Â¼‰È„<‰Ó=·=¿‹=Žç >hö>Kíé>;g> I«=´D;û#l=ŠÃ>RZ¡>D×v=‘j=„)> ã=ã9o=‰pa=¡$4>!šO>=$D>¹¹<"·½ó̾¿f¾À–¾ ²¾9Åt¾2Cï½â<¤½Xu¼©,;•gR=’<ù(=©³Å>?jÆ>‘q5>Ñâ>^o>›N<éôÐ<Ÿ|«>|±>E*æ=ª Š=U.¾=Ç^)=åªø=¾“ =£©Ø=¶{= f÷<Ì ã½ª¾NÀ¾Yœ‹¾¨K½¶É½öK­½ózÿ½Fa½"ò±¼ïÐr¼†]÷fÛX>¼2>Äâû>Ÿ½•>Jüû=ˆÍú¼¶¿¤=Ä>0hÖ=·a˜=(Ñö=q}=ÌRs=ë?’=¹¢¬=oë¼ÝÙ„½®t“¾'~ô¾Aô¾ŠEˆ¾.޹½WB½]ѽb“½W¼Ó/U½m²½$º¼C;z¶=¹4£>€íœ>Ûƒ>ï^e>Ë2*>ˆÄ6=ê )½\¹¬<ö‰Ö>DÀ=§ª)<óÜú<ÁN^=¡;¨>U=ä¥ê;ºÙÕ½ÚŠ¹¾4UB¾nÞ¾§ÈC¾ %ȾB?o¼ÙB7<0<…ãÏ>é‚­?ðœ>çª>¥0È>$²€¾¢N”¾ÖäǾ®{¾HZ½ÙBì½u€;ô¬š=Qê+¼Fð¾Cž¾¾‹=~¾Uk‹½‘"‰;ØÛ=‹®v>ÓÔ>]d9>º<ï—<½Fú¼:í|¼½Y¾‚{¾#¿H<©Oí>´–=Ý­þ=d =Ã˼ÐSˆ¾ÿ¾¬è¾íRؾ×z¢¾† ½ý ´¼æX¶=9¡Â=ÄØ*<ñAÿ¾*µ÷¾®"µ¾£Éó¾sÑ<éÿl=×ö> p>®¹=æJÎUðü>Q =€?Ú:]Aä½ ½ ¾n²¾ªt€¾ísÒ¾íU‰¾Ÿüª¾ ßg;븥=¥äj>)­=†sž½õ6ñ¾µé¾È¾cm¿<«ÿ=û“›=ô^=ž‚=Ry·¼›øX½ÕGC¾ƽ¼èT½¿×a¾­.½Ë ±=‡Íæ>`J>,!=Z5ѼϪ½'¸Ø½Þ›¾ž2¾Ý! ¾ð@̾®½¿¾¨==¼=þÀ>!\ü=Ø«½aJ¾¤Ö¾×? ¾— V¼¾Ö^=ØÓ}=¸Ú;,T¼ (½p 4½÷Õ¾.;L›½ÁU½Çɽ€ =‰D!>Jë>"â=¿’½7ó½—›½NŽÞ¾ˆ†V¾¿·)¾à¾®qü¾¯È=²÷j>4É>Lè>Û¦[Ö=6Ÿ½iø½§Œh½Æa˜¾ X¾Oê¾6þ™½ï ½Šy½7Æ=bB>õQ>ûõ<µ½m¯Ü¼×õF<®ç¾W}n¾™ºE¾Àž¾žÀX½ÝÌ>‹{>r x>>OJö=³6o¾+-L¾·JоÄõ¤¾RÏ»½X¥-¼”Ó ½ÀÉh½úœ¾ ¾|+¾hùD¾kõ ¾'ò”½“!½½ a=´L=ÛÁG=Á„H»ü<~½ˆÁ“¼''=¿ef¾™Î¾`¿–¾–Ž=¾‚´ä½ŒÃ½>2v³>™Z1>Ÿ¯:>Ø>`E½¦¿±¾•0ѾË&¾£¾©¾6ßô½½í½Û}ɾ b'¾d2¾$j6¾€ö¾”>t¾oUݽߋn½=…ö<†eà=Œo/=„d ¼×;ú½—L˜9ƒH>Îѽ¡}¾¨a¾SnH¾>MH¼¨Da>[¶y>¶]ý>¼V{>—›¯>4ǼWè¾d³Ä¾È×¾Ù仾ž_¾-6{½Óá9¾{—¾‰Û¾33 ¾‰%¾³™Ì¾ X¸¾(‹³½ŠtÏ;Àc=<‹ˆ=OEؽç4½¢ÿ–»NSÐ>4ET¼Ÿ‚º½”ãÁ½øSf½ßÎr= `>|:ë>Êò>Ѷ>¥zä>>‚¡<ëÍ¡¾)‘Ǿ½÷¬¿&h¾Ú’оw°½½å°½Ù¾I ¾BMz¾™V¾Ï_¾Åï¾b°ë½´¾7»+¿z=*œö=f³ø¼ÍÛ½¨5¥¼·à>-^Ÿ<͉d¼¡Â½= ½õ=§sY>‡P>ÑêW>Ùê>>©e>2Ù©=,œX¾ƾ¯T“¿{í¿Êо›ë,½²Ľ³îྠœ¾P8`¾ Çоá³¾ÞÒ¾…˜ñ½Ë÷;T®=V®”=¤$M6Ml½ŸË¤½Y!Š>}=^Æë<Ÿv|<¨\ò=6Ú>ëô>‰ T>Èk>Ò“>¢„»>ý=x½Ó˜r¾ž ¿0j¿޾¹J½ÇYk½§‚ª¾ û4¾[¦»¾¡³s¾ã\š¾ä씾ŒCսǭŽ:´>„¨d>®h>º»>’÷Ð>›£¾„`½°Õ<åµ3=Éûº>(Xâ=ÈZż׿·½Ç­“==|=¦8T=t²>m®>H°Â>n’x>zk—>†éÕ>’ÿË>wr%=Þ Ë»œˆÛ½Ó ¾ƒyë¿¿Þ¿?Ô¾ï©K¾C,¡¾%„¾3:¾_Üõ¾‰¾³Ì¢¾³÷è¾b¡o½”Ú=’ð>qÈ>_Õ>-(Ø=Æ\½©g;.rà=ÆÏ=È¿>->ˆ¥–>’¤>n(B>0Ÿ>>•|>;c9=ÎÒh¼˜‚ÿ½õ,¾‚ƾü±I¿–¿t˾ŠÃb¾.+T¾*í¾S»e¾e˜º¾‰}ñ¾‡­#¾4þ཈;X=%Ë>LK>Z´>ƒ½=ñ¾²¼ù«ì;Æ¿W>MM>½M>[¿¿>©AW>­8‹>jxÈ=§ÃÍ=’'u=à´ð=ljŒ¼Çɽ¾\m¾·¾ð¼˜¿e,¿G¾·_‰¾_õ¾2r¾=?.->·`>ºÄå>ke©=b¨R=)Ä]>7ž}>SDž>‰Ð©>Å—>ÄX>>pð¯;÷p½`*>äÜ >ú‰m>¸Ý‘>'®Ê=Îù;>}þj>•Å–>¬Ób>Û>Ôk0>~a½°Ÿ¾Y*½¦¶ =f0˜¼Ñ5 ¾EY·¾»»À¾ñns¿ª{¿Ý`¾÷‚½¾’‹¾)½øšÐ½·Äνp§]½%¾NL¾,<ø¼û=,>C? ¯ã?æÐ>þ<%>Œç=>-Ko>§Ø–>Ê©W>ÙoE>ìÇÚ>ÛmE>…¯d½c>"¾u§à¾JHY¼w“½&³¾X˜ë¾Õ¡ ¾û­»¿Ô¿…â¾û{‰¾”Oˆ¾,K½¶‚¤½rJ¡¼ê·q½}¾@¾wß#½Ÿê>‹dq?!÷7??`?ŸW>¿À×>mª^>Î+>þ¹Ò?c>ú >×§'>†k—½@²x¾šbë¾¡ÈØ½Úì ½€´ü¾dȾè*$¿w=¿¬¿]$¾è?_¾‰‚u¾J2½Å꽬׼dW/½«%ä¾{Xa¾¤W¾MÔ>!4?2•?WèT?7·­>ân.>ŒÊð>é£X?æE?»4?S¯>É5>y²}¼Í‹N¾«Xº¾ÙzÁ¾oë¾½á?l¾jÐê¾î~·¿&¾û Ͼܸ¾ÀFA¾eÕ½ÞK©½>mA¼‘9í»®«½Òã\¾šô^¾ÈWá¾=.Å>†™a?7g¹?b¦?B§Ë>îùD>”,e>ó\“?LL?-'2?{»>±7ž>JÇC»óDÚ¾¯Š}¿pè¾¾ì;4Ê™¾nzâ¾æuâ¿Jξå?ƾ® ²¾ŠÝÒ¾&#m½­½‰eº@¹Š;ùн㺉¾®/%¾àV§¾oV>\E8?+¿i?ZoV?<ÙÅ>âé^>‹²m>éáá?7Û?1åÿ>þ¿>’çÛ>PŸ¼÷x¾«%2¿öš¾þ´¾8¦¾sľÒÓ«¾ÿØh¾Í¦%¾‚ ½¾$¯Š½¶A½^ݼ÷Y<†xƒ<èp?½Ë%p¾°âá¾ç×F¾Œ5"> >?¯?>|ø?&ž/>Áü>lM >Ðc¿?§7?)Š>îä{>cYT="ñ#½ X¾¡‹í¿e¿ˆ%¾¥Ò¬¾{ão¾··ó¾çÞ ¾´è–¾;´t½{­ ¼;í¬¼k>L¼–Ä= ±‘=cÅL½†x¾ œN¾Ýx~¾›Ì5<ÏŽO>Ã';?ÓÓ?`E>X’>.¹>¯T>à´ù?÷>Ö;Ë>$U:½Gb”½­F¾•¢–¿`#¿ºÎ¾¿m¼¾‚š[¾›%¾Çs]¾œj¾ ÉÌ<@¹=~?=Ü{;b×=H›î=©×¨¼ˆâH¾+¾Ã™g¾¥ðm½ÑÖÏ>(E>¯%U>«DF>/øˆ=Ó·q> w>­£Ê>îH„>¶£“=ÚQ¼½ÿlÓ¾i­¾†Î¾ãªM¿9¢¾È J¾…¾²Ø¾¡TJ¾‚Z´½Ó.$=ocI>Û=ÉXe=K=wB>=Ò¼=À;&Ã_¾ž'k¾«O‹¾v³½ ð=¶ª>^N=ko= æó>s¿¤>‡"Ç>³qÜ>”þ9=‡²$¾1K®¾DÓØ¾i ƾ¬4´¾îѾ¼Þ¾ð#¾Qe¹¾uƒM¾Q0¹½«;O=¤Íw>=’ú>'+O=¢ª=…C°=Þûë=¦Ÿ½~ ¾fÑξ«K¾½—Ÿ¾¡ŒÆ¾%5b½O¬½A<6¼ò÷ˆ>i$d>n™>ƒí>kX)='Ü5¾?]‡¾^á¾<½q¾]w—¾¨¾œJ ¾l«¶¾+¯f¾/‡¾v½‹¢=¯„¨>f<>cš=ù¤Y=}Wt=ÉŠ=Þ3>~4Ù>€ŽÝ>RKÔ>8á%<ëçì¾+ؾV,¾ ©½ÕPƾ9ÐÆ¾[z°¾Aî¾ %¢½í?½Úà^½B.Ë=®5>{Ô>†sW>=P´ý=”Uø=èQç=˜½Œù£¾–pпùš¿+b׿k;¾¶'Œ¾H7$¾{¤>‘L¼>šÄÆ>DSM> ú<Lj ¾ï0¾1+¡½®üÒ¼{>½N à½áßø¾0½Ô‚M½–ÎC½~™¸¼‹E=°˜>¡O>‰Œ>2ÖB=ô‚=Œ´=Ì‚†=ãúc¼m¾.ú¿ U¿@=Ü¿,ý̾æß¾¾p+¾##F>¡t!>¹WT>Rç =öš„<¸s‚½«Ú½õî}½-‘Ü=)tU=B›»»ù=½]€@½ˆóZ½1a#¼»ž×u n>VÄ>8t<²êÉ; s°=—œb=ÿÍ!= ㈾Mo¿çN¿C.¿:"“¿x¾¸¢¾8ºb>¤ú>Í%É>jmd=Õa1<˜Fä½_4½‡ðã¼J®W=Ì=ÐOÔ=¡8 <Ì<‚¼¼X޼³Ç;ì¿>=J¾=×à >b¡(>ƒ¿`>5É™<ýݼ£OZ=9& =öÓY=tÁÿ¾¬¾åq„¿6xò¿7á4¿‹Ô¾„ Õ¾D–š>•(@>ËÊ*>w˜z=·È <$µ¼ À/¼¸r·;yÊ;=p =ñ“’>B=Öß<÷*Ì»¢ <Ð0Š=š©ã=ôÓ>P9>s×>6òŒ<É誼ôFš<¥õ$=ØE+=‘\q½ÅVɾ¹^¿I¡¿)‘2¿£¥¾ƒ¥Ü¾JY¼á!(»îÄâ=Aˆ,:é¾#éz¾±Âq¾Ìr„¾¬b̾ˆU‰¾|`.¾a¿h¾½ wh<“‹=zÝ> ÞÆ>–R>e¨>LªÑ=6~L½)-¾ .¾°°¾²¢¾À¡¾Vf¼¦„=:—ì½ ½¹â¾hL¾8&—¼îÔk¼;G¢<þ©{<è5Ÿ½·^/¾ˆÝ.¾½o}¾°û¾­”¾8n¾}A/¾/÷ ½1ôR=`ÆÃ=«ô€=ûÇË>O>j¹°>³A<Š’½yŸ‰½þ‹˜¾Xv¾š…-¾ƒâ½ûu<š´=D÷ѼGê½É¾!ø‹¾6‹¼¤=‚9¿˜ð<¡)=%g&½n°¾<‚€¾§å6¾²²r¾˜‡õ¾vÚD¾td¾75½dâ5=“Úà=¾ó©=œù=¦RÖ=Þ=‘3´¼`¶Î½˜«½àq}¾&‹G¾lô„¾Xþ½Î“¦= âV=ÖÀ¾ çû¾$‘» 9–<‚‚»§H]<ª§<‰¿þ½w¾c¹Þ¾¡A%¾•Ñž¾?Ëí¾Z¾‘Ô½‘®{=GPÿ=i–&½­ð¾&ú¾#—ɽäϽ‹‰c½Í!½˜Uw½¼Ë½àʽ‰7…<«Òc=ºÂC=r/ɼãÀý׶6½å?]¼);O}¼µiÄ»èþ ;j*e½âë¾)Ú³¾‡³ü¾„ɾ¯Ž½ÐiG½·cœ½ê¤¾rþ€­ó¾5'8½¥ñB½NØí½2—Y½:l ½PÖ½—Îнjük;¬¿Õ=§Ž=“ö¯;I ^½’33½œÜé¼å°¢¼×ý"½-½½†¼Ðõy½êq½Þ2V¾E ¾QåR¾•ƽ}Ë>½Y A½~½¼­Ì¼²Kn½äE|¾„z#¾”’ë¾Ur½£ªk¼O*¼kñŸ¼ß^½ ÏW½a½MÛð»¿0•=î=š Ÿ<ǽŸ½%B%½(å½`MX½†ܽy¥^½ƒ«m½fT½9ƽ[êѽ݌<¾yݽÚ9½/¶™¼ü˜j½\ÙѽTN½Sñf½è'M¾n”\¾S¨¾Sk|½ˆö0<›á<“eǼnÍã½ {¤½>ŽÆ½- ʼNq=fK=ލc= Ôµ¼‡Ã¼%\꽬½Ñ½Ök„½•¦½™«·½)a½i”Ò»ùÊí¼Ö)½•Ÿ€½Æ W½E;Ò¼ïú½Fô½‘u½†T†½¾R¾0Ï ¾m–K¾7¦·½=×=pjI=UòQ»‘½%|S½,›Ì¼þÂ}»‚òC=<4¥=nP£= Dº»ý»½ÜLµ¾½ì½•Õµ½„ ¤½žˆ”½„ W<¬6=W¼¤齯+½šoo½L¾½L潚-½| w½p]}½ËÏ ¾*xó¾Þ>¼Ì t=½Tf=®¿G<( ½)Ì:½ û¼„EK;Êù†=ðÔ=8F#<âE;ΆÊ<ðêнê燾›½må>¼ÿwA½h¬¨½ŠœŽ<ÍÇ=‹|Ö—=¹Ú<° Ì<2cN=,y½ÄËF½ü¼îcá<’åC¼­®½˜Ù:ꉠ=…ÑÝ<òŒ½Õ•¾'-¾`½¦3½Y=£¼º°–;¤ðZ<[ (½4e+½u£-9ÅSG=÷Ÿq> }=›?B:æ£ì;æ:t<£IŒ<Í¿M<çzº<ך^<ž/=ß½ ¯v<B=‘ò—<™¾½“¬%½6å[=(<後½Æþ€¾M?¾H9Ö½â•$½} »ž= `ü=ér=A ="0Û=J<੆<ÉÃÛ<ÍY3<»øO<§ñ=(Á!=& ¼'.=I±=å¸=Cï½¢#‰½Ð`¼¹ <™$½š²¾^휾v×j¾žK½T:É=ö;x <{Û <'”b¼I¾š»û©/=£ƒ>> >Ì=ÎŨ=™{j=Agc<Â6Ç<³(<ï­=Vu<Ú’ =$« > ìb=“,Y=ª61>9=`R^½¶ ¾`½†ÑP<N€½*ÕM¾XܾЍž¾?I޽ý•»²°>¼¥.ä¼wT¶>2OP>%=Ü´”=`A^<‚¹”yñ>#7=Ù%É=ât{=S;½ÊGo¾5˜Ã½ÉÐ.:õè»!ˆ¾<¾¬Ý¾fc!½v…¼Ãu½EGk½K:<#";ô¼æöT:é©=ÎÃ1>;7ì>AK>ä=sBÜ;Õ%¼©ùÂ>eÓM=ìÉÅ=†T‚¼)ü½Ø(¾/z½ÙPƺnKW<ÿÞ«¾+j¾Š8œ¾‚³³½½±¾½CŽ ½—A;=¡"<;½\½)â=Œ±ñ>0”µ>Pðº>˜|=€}ºöá< =(©=Vˆ¤<ý.«=D>Å0i>ˆÀp=é[í;€Æç½…¸[½ÝK·¾ ÂV½¸˜R»3ÿð=N² ½Åü‡¾w¦¬¾‹—O¾“½‘×½š5(½£»»8÷;°§m½-Uv½\;ø=öÛ>^â>H7Ï>Þ}=‡n·¼¥¤»O™E=ÄS=Aà¡<ÆÑa<ÓId>Å‚>Šu=Ðêâ½i™6½ø]=½Ý]5½©×ó½qÒV»É¿–=L Ƚh…(¾Nþ8¾‹V¾$d@½±ÂJ½…á ½ƒn»IBŽº£îk½?áK½‚g:<^ˆ=ÚåS>*ƒÝ>Ëa=¬Õ¼.F¼Zü§ÒÞ>fú.=¤2½Ò¿Ý¾'{ƽÞì¼ÿu;¼ÌR¼Rãz<ôÝ¢¼ø¬ô¾!{ž¾€RX¾5Ðí½½9Ò½,q†¼ò®£;¯ˆû»â꫽Ku˜½€·g¼4y}=ˆD=ÿŒ>pÒ=‘<|¼,@s½W¼¼4 "\ø+> m==ZX½þÓ¾;½ëz¹º!çX»‹º¼»–u»©r¼ª±Ï½ízñ¾Xˆ…¾2þ5½³p§¼\O_<}E<<Ωj»±;•½K–-½fἯù=í=§bQ=Þ³†=Žû¼ g½i3½.Ì;¼Š%4¼8i£;§6=V1;ØŒü»C8D¾f¾9Ì^¾Þvº\:í–½ e2½n'¼ÌÛ齨i3¾%á^¾úh½œo¾ ”z¾$Tt½¦´½B“G½e|F½š¦½T$½E›Û½Œ0¾½­àK½K&m==˜=ùœ> X=œáI¼gŠÛ>2×\=÷ʼn<f+½‡©½t¼lþ•={½=ská=9ƒï»´p½ª±¾ õ¾)î2½ÿ¸k½ŒÜ9¾¹ôù¾â¼F¾•l³½öC*½¶8í¾§î¾-OŽñ h½Œ«æ½„3½…°æ½AuJ¼º›„¼Þj½víJÚ>"ÙQ=Ù ½«‘½·È¼¯F=oR÷=v‘=:>»îž½£·K¾&ÆÈ¾N5é¾/6½ß2@¾¶† ¾æs龬о þ’½q½ý× ¾3Ô¾ :½U½:T6½z¤½K†¼œ#–¼X*|¼í¸‚;5‘=Õ«c>Of@>9àõ=KKl½Ï²Ù½óнhq=¤­ä=´m8=N´ª¼*h½œÜ±¾/’¾lºÃ¾_ƒ¾¾¾¾ïÙ¾ÐÑâ¾³1Ú¾¦½[åc½š Ñ¾FÚ¾wü½‰žî¼øß½UK¹½H¦Ÿ¼­h ¼8f¼Ð*#»¼íž=¦9+>?%>:# =h²Ö½ëœ‹¾ù½3âJ=Æ&3=ײÄ=joï¼=í—½™ ,¾4V¬¾^;„]¾HÞt¾u©º¾«7¸¾©¾'FT½4D¼Ÿ!?½ÁA·½Îêɽw·¼¬V6½)!½2]O¼»©¼#ß¼³}¼¼€®=p×6>€$>%ŠÖ=Z¸<½ö!¾÷“½[A!=ÒàØ=êcþ=xíú¼e¿½–Ï‚¾4¡u¾†ËB¾‘Y7¾g–¾.=¾µÈ¾’,ü¾#̼Õì5= € ¼ä¼½x#ܽK¶¼™]¼þÀ­½Ù¼¨ƒ ¼/Û¼™¨c¼¦=+`=ð!¶>äâ=*ôs½éß^¾F)½lç"=ÆI#=áÔ¸=jŠ^¼Š)>½“ q¾/‡à¾…©Ò¾”¶:¾s%t½ï†¾4€ê¾kœÈ¾®÷¼Ž‚p=•‡=÷¼‚~u½'™¼²™ï¼»ñt¼ž‹9¼V@мe„üŠ} »RÎÞ= =¥E¨=²²j<Ü8 ½Åòо—½c%=¡‡D=º=7g¼¤ë“½Œ_v¾$,ò¾{g¯¾4Ô¾hèµ½°Çt¾Rk¾6#³¾Ä;¼y 0=¿6¿=®Œ<Ö’³¼®4ؼÕCÒ¼€8¾»„~»?EÛ¼˜›¼¬_;NjÐ= ¸ü=Q|{=N‰:SK4>©…S>›‰F>˜o½{¨’¾Eøä¾Þp¾§G¯¾”¹Œ¾U.|¾ O½ÿîw½âì(½¯Ð{½}½ßl½ºYN¼†v¬»Lõý<‹œž½¡rô¾•^¾ù—ž¿éK¾ëD<¾®Šê¾Ž‘ ¾‹Rr¾Ñ¾an=½Ðfl=-îµ>4¯{>“Ëš>$¯>óÿ½–à¾S⾸a¾£&¾˜ŒÇ¾a-Z¾ ˆ`½î~4½ñb½×iî½£2U½ÙbX½»®1$>a‚/>fåå=ûÛ,½‡’U¾E·¡¾†t¾—4«¾˜Yº¾sŸí¾ i½ÿ'T½þÔ5½æ¿®½©ø½ÐôÁ½•¢•=Mæ=[U=Âj<<›ìô¾7U¾ï‘.¿ü¿†o¾¹Bº¾<âK½ù²¾ c=¾(Û½eÖ<Ù¼=Ÿ`È>ð‚>)ÇM=ç(h½+"W¾#\ľk¶#¾ˆÅž¾—óI¾†˜²¾B¹7¾‹4¾N_½ã¶{½¢”J½Ç~ª½ ±=³~ÿ=ìN·=ù=E,h¾ s¹¾×¾I¿÷¿ ¤ª¾¦Sl½Ç·›»¹X½ìE½Ÿò„½:þQ;h”)<ÝŽ*=ˆÅ*=à¶Z=؈»»•™½åhL¾Bd^¾vÕ¾™»¾• ¾j…g¾+#³¾=޽Òâ ½”Šâ½¾ZZ<Ø#U=ó—C>HV> Q =€=½Òø"¾´ Ï¿¾ò†ð¾†N”¼ï=ÙÎ=”º[¼Øó ½1p~¼Ç®Ù¼Vô¥<5ÿ8=‡°8=ÎñP=uN½nϰ¾Öû¾b|¾œ¾¢f´¾…ôu¾=€÷¾C€½º©E½„Ä`½¶š=ÄøP>çÆ>¯W> 6Ý=‡ï!½•ëy¾ˆ¼Û¾Åêe¾¹u“¾B´=gúæ>>ôm>"È<ŸæP½/¤K½GIɽj¼£[ = Ý[=Çš=’bs»×Fý½Û;ܾTÀ¾Ÿ]F¾ªò¾Ž »¾B ͽû`;½¡fî½p•‘½®O(>'z>å,>ëm>=‡'½/î¾1,¾€5¾sgy¾1=«>þ>_1a>W©³=kÛ²½¨A½v½-Õh¼ù¤ž<ÈÔo=¼¯=Ë®Š=Ñ ½!‡¾Hð¾Ÿs侫m¾‹|̾6üÿ½Ýf[½ˆ¡½[ཥÒ>\ô>•n=÷,Í=âi=‡à¹¼%íн©Qé½÷z?½ÿP5½©Kú=‡¤ù>Gô>aÎ=±y ¼‹ä,½YÜó½ ™Ç¼ã Á<…C©=¨d=î+b=–‰ñ½üð¾9ľ™E¾¢D¾}оœf½µ6õ½ZѽA™]½›¨ë>zÓÐ>ÉI=Ì×D=ÆEŸ=‘/<Än¨¼WÒ¼¶Òr½=½»½Ž¯·<(,>¡>Aܶ=ÚÑ<ƒ¸:¼Þßð¼Œc¦¼±oî<üE=‹W–=ùŽ=Èr…»ø+4¾"Kí¾ŠÌê¾ ¾VŽÃ½ÿS3½ˆ—œ½°j½,½Y>€®8>Ø=­P¢=µé=£{¡=b¤Ì=2z=±ÿ»å,Y½¦9k½@f<Æ%>¤ÿ=óç=ziu²1> Hÿ=¢äà=±¹,=µ…¾=šïù=…±»=[ðI»”õ½ÛÄ:¾úd½³º==<¯=ú¯\=ݳ~=c1Y<i¼ÞMç¼¹ÿ<êÈ=Ù¶R=÷@‘=P5Ò½›?¾)ñ¾¾4Í«¾l½>¼Ò²Õ»Þú»áª›½dMà>`Ÿg> Î=° ¿=¸ž{=¼‘= 5Ö=j$+=Ñ¡¼åˆ†¾ °æ¾kËȾC§ß½ &…=ïy>¶u=¸ï=<X½,_C½3)Á;¼¨=²ÎY=öIs=¡'¹¼¦¡½Ìš\½úêÔ½Äñ½`pê¼LWGLœs>â…=Îëq=¿9=¨Ÿ=nx<Ñ>ß»ˆ`P½zܾ!{U¾pl¾Š“'½íìƒ=Ëý›>*·~=ÜC»I½ì½z,í½†x¼œIå={@ì=æ.®=Ó¾ò=Ìj½ ß½›oý½žëù½Jœ»îH<ÀìØ<£²5½,»í>=~ƒ> Í=ðê8=·37=fq¯<—_D¼‰|”½>Íç½»„î¾- .¾›þ¾¤´]¾7 ¯=È3>'K=Û31¼—2½ ®r½²ð‡½EÏ]<è¼=Æ€\=÷æe=´ÏH<¯î6½K½‹Zö½Uzÿ¼AÌ%<µ'Å<³Š%½ Ž>1îd>l>Ä=—þ,\=¿â̼﬽·Áˆ½ß½«©;¼4¥(=—‚>9È>Uó=†Kà¼$}½ƒ¹¿½}ä¼ÜL< ýÂ$lÿ> ƒä>®Ì=88b½9½²ú½­ÛĽÅ#Ͻü3o¾/ùʾ–ñľ¯6×¾}]ª¼%»==Ñ|V=ž6‘¼Ýj½»„ü¾r¾ÊK½^/=7µ=ú Ë>=Ámr ]T=æIm=ãè9;Æ…Á½»{U¾b"½Ão™½Çt¾J¾1ç4¾w¾¥‡J¾‚.½NR¶=‚â=‰Ø»Þ1½ªa¾©[¾1ó|½Í»Û=â³2<ëká½|½Æ®m½«½@X—¼ûJ¼½ã=Ä*Ò=“rî=¥õR½ ز¾ fS¾#I5½ºR$½µÍϾÍD¾7tÞ¾‚…•¾–`O¾z·½¤‘œ= zý=Œ¡™<ò½ƒB“¾->¾a¾½¾7ƒ¼âH±=•ž=üt“=íÛ‰=+B¹½w°þ½óØÛ½ñ¥¥½¡L½¦c¼”O= Ób<˜ºE=)^½ˆ’¾ j¾)鯽 ‚½¢–¾f¤¾?¯j¾q`¾„«T¾cp½Â°Ë<…r=¥'i=›×÷½):¾5*¾ƒå&¾9p½„Ã0=•å=ÆyÆ=éUä=P–ø½s ]¾ðú¾¢\½ØvZ½8Q¼J8¶¼ãyâ½&Ñ<ºìü½ª%쾋U¾û]½Œ8¶½ Ô ¾Cg¾F¯p¾` ¾e ¾B?½¾2,<8 =Ä™=ùí:åòˆ¾*çݾT¾Ufú½Ê”y;+l}==Û†_=b4 ½o®ù¾Vø¾.Ô˜½þUs½Rëä»Ì´s½¾¶A½ÄÑh½Ñm½©õ¾7}¾X$½c&½¸œP¾¶÷¾HÅÁ¾N¯»¾Aq³¾…6½¡6<,éF=ÔúÒ> ñ4=9I¾ ¥D¾‰¼—¾eÝ<¾û7¼í§==C÷¾=ÇÒ®=_[2½i‚§¾! a¾8õF¾ e½eªºËžò¾Ôà¾U½‡©Õ½ä7½Ç—(½âžˆ½©#½è›2¾("9¾De?¾<0™¾‘7½ç—½r] ;×_=Æà‡>3D˜=»”Žª‡¾sŠí¾k(4¾tù½fHþ=$==°15=K^·½[Zó¾§R¾7Ô¾.$½º=@¾1ö¬¾&Nû½¬žG½_ ü½‡/½ÁYȽØçྟ"¾4¸¾:@›¾(G ¾¢%½ êŒ½0$b»òq=“Tý>1qæ> šy¼bO¾@:´¾g2þ¾5 ½£<©dî=•õä=-¹þ½@Ÿ¶¾Øå¾/}ξw±½˜ðj»Öо4z˜¾*ž½Ä„½;ùн;Ù½¯ÇH¾0¾/YÒ¾>QB¾,ý¡¾9š½ÈÏ(½P=E½fv½ ï_<ÿI,>  >1&\=ZÁ˜¾T¾\Fe¾I~‹½Ó8w¡>Kgk=ïèνPA¾K»”¾Yý¬¾R‘»MLh=/<༼Ó𵽩<¯¾ Í)¾:æZ¾ž½E©ì½ô ¾ ¹®½í´ô½ÃؽZK½§ù¹¾s^¾Bd¾=éM¾$½ß± ½x¦v¼àȽdv1½Õ˜½yoy=Ä€ö>V u>(bûå¡î¾5þ¾cï/¾‹Ò¼œ…J<Ô9Ì<¤´¼‡p½„©•¾"E޾Vɾ-¿D½«3½¢¨¤½îˆX¾m½Ïv_½•I½«¾ìZ¾0º¾0›¾ :½Ê ü½\\ž½UQ½œÙ¾†?½¾Ž=‰Pï>OðT>Bf|=Jâ¾,)¾cÚ¾'ÝB½Qj< ˜t<1k¼^…o½h_Ò¾)Ç}¾t£S¾Yνý†½>;úì>Gðù=’,½îG…¾Sx̾,ÖÔ½N‚мÉd»8u­¼ ˜Ô½m1P¾2J¡¾†f¹¾€þ¾%öU¼ÙR7½Ó*R¾+Ü÷¾!Ôå½Ïئ½Dè½t+¥½½hV½él!½Ü‘½½ªÁ½^üB½V8E½ßŸŽ¾ ½å <ÈU >)+>=27=Ãú ½™÷¸¾3â*¾#KQ½pÚ ¼ÒQV¼­ 4½ …s½†Á¾6Í­¾ŒO‰¾ŽUP¾D¼1¼´ù½ãçw¾;}—¾.Y“½Îec¼Û²ú¼¤½,ˆ’½‘š½ª€ô½‘:Þ½Y++½pŒ½ê4¾7'½ÕY<*®)=øy„>(Ú¶=æ÷¼ñÇœ¾Šá¾ Œ½tJ‘½$‘½+¸¡½]€½›~0¾2ĉ¾‰¾“-¢¾U€*¼ì6g¾¤‹¾FÛì¾.ùE½»#Ç»·`2<´bÂ<:A¼—|@½@€½Gÿå½3q^½l¦^½å]9¾šÝ½¾‘ƺýÆÖ=±KV>%T=ÿ9<›nî½r ½Î‚²½^½Å½Oàò½x9å½”º½¬,¾$fb¾{I±¾Ž?¾X <~ÜÄ<íµ½N:\<” j=‰Ö<ïC‰½ç p¾nŸ¾‘âT¾‰¹¾i‡¾‰Wp¾´jÞ¾¸r¾s¨1¼ìš×>W;7>é\Ø>òóï>…j³½IMš¾3QF¾9^R¾½Öh½)Ÿc»y¿j½<À콚ûm½®ìŸ½Ì8 ¾8J`¼Ý=5š½Ý ^ÏÊ>å2a>ü–>¡™Ž¼‚7¾;c^¾]ؾ0Çg½÷`½eÐ0¼T½±½–¬Â½Áñ½äR¨¾:Ôu½‰Ir<Šk>¼7·t<î- =š³&=YB¢½èZê¾”[¸¾¸ìr¾•ʾ.ûñ¾37r¾Œ5 ¾£IÞ¾HºG;ŒŸX>LXQ>Î$>ñ>°ÃØ=U4¾2oh¾uÛ ¾F³—¾ I½˜M$½µ}½Búj½©‡½Ùmø½ñü¾1 M½ò9>¼“›×t>¨=‹>ÖWd>³„O=¡Êʾ ø¾~ig¾ZW|¾"½Éc ½qy½ˆ±0½ÑZÒ½ÿV ¾6̾ÜZ¾’´½S=u=Øá³=üèß=S½©G]¾`]…¾‹~™¾[D+½‘¡¼×;ĽɌR¾SÄv¾-wƒ½>†¦=¼³¡>sÖK>°Ÿ€>©zœ=ê½ë ÷¾tûœ¾h¹ ¾:¿)¾–K½´Û2½·QŒ¾©(¾e¾ \4¾ ôÙ¾”'½ŠÍW=kñî>­õ>!?l=T£½u죾Ä—¾8‘“¾0’¼¤P?=4¸*9Bð¾ :¾(Ku½É«à<‹g_>i>…„V>”¾Ë> g ½”Š—¾[‘ž¾pÞ€¾Vž2¾$\Q½î„3½ßur¾eŸ¾1/Ÿ¾à‘½óÔ©½“¤A½ZäÊ=  ÷>F ®>@^õ=¤I½ ÈS½˜ãÚ½¡<½‹:_<œ£=Ãìÿ=±E$½濾ø9¾Ò½`Έ=p+X>6mú>rÝõ>¥Á¼ùºˆ¾8žó¾sºJ¾sC¾Fú¾ó3½ô銾$ Ÿ¾B[(¾(¨½Ûé’<Ë@¼«i=Âx>[‘¦>S8"=¾&¹»þÅV»ºN<=N¥»SŠá=¾=óàí>d<wí¾ \ï¾:ù¾½ßdí¼ Ø=Ø\Ò>8>ö>Ñ›;¥!¾Q¼¾ri9¾…¨7¾cW\¾&Œ½õ.¤¾}³¾C8˜¾/•—½ÔÆÑ> rä=`üÄ=Ùv4>X×>T˜µ=Øøá<—C=?9‰=˜£I=iO<ùI8=ßú›>6‹=”"ø½Â ¾<-œ¾ Yý½Pgy=FËR>Ã…>rª´=ýiH=áÊ>>•B>C9W=îDb=(N=™Äk=ÌC=<}C»‹¿¿=‰àá>1Œ>DF½ “¾Ïð¾*Ú½ö{<–õ=¥ =æ<='m齺´\¾a·¾Œ5ý¾y.»¾&>ȽŷB½ÍLç¾8ê¾3Ò½ýE>M>;iÞ=Ü‹>÷æ>!Ïq=öïý=j¬e=£¥ˆ=µ .<ºñ½1]»U> }¨>*Àï=.“½Ñ—«½ŒúK¼vÎî=1+'=³¿Ç=$¶ø½˜¸b¾N'?¾ƒ.ÿ¾mÃ5¾G–½¦Ÿ½r—½¦ 9½ù£i¾ãÇ>«'F>a¶o=Íð¤=À q=ë*`=éÚ‰=a‰=‚ ñ=Hö¼Ç~ç¾ #཰ ×=¦?¡>DÉŠ=ù.[:˜ïð½n`½f‹C¼ë:N<[Ÿí=qUÑ<÷E½|^¾0ó»¾dú¾Uèl¾'ª½‹—Ѽ¨¼Þ!޳£Ø¾,a¸>£~ä>lN–=»ÂT=;Õ6=‡Â=¿Ù}=e¿„=M˜»èJ€½ªâ·¾S®©¾.`K3•>A®=¦_å¼J '½#Û@½a<¼B"^<Úc‹~ >\þ=¬[";ߺ:åZ>i7>q‡<·7¡¼é&-½9 :½ÿ÷¼Ỉ x½/h½½×n¾%¾ûÚ½ô*ÿ½nœ_<œ–=0´N¼û,€¾T‹Þ>VÇ£>;QØ=¡·¼´%Ô½åu;‚Ô¤;d‚½?F½è~s¾;hd¾˜·¾’U(½ï¶Õ>E¡>m€>-Ë=(j@¼Ô’½p*1½‚iܽ8]­¼Æ™¸¼ÂÄX½P®&½¶8½çL>½ÛWнz‚V<`êÐ=>øŒ¼’Žã¾[Nz>@”>Ò¶=˜=E½'D̽©à¦½n`½m$½¤ì¾H¾Uˆ@¾›û¾œ¸k¾,ª=¬“D>ME>*Ï=5‘.½rÕ½¢ip½Åpr½­4¶½Aãc¼ŠZ‘¼ˆÔ½TÒw½¯2A½Ä/¡½‰åm»ºWâ<ö⼉ê_¾VÝ=·Þë=ÀPç=â½Q ¼½ûJ½õ¼Ù½ŸŠ ½ÖÇj¾&– ¾]ž"¾”üc¾›ü›¾T¥À<& ž>ûW>)î=Òð½>x½ÙÂÞ¾ K¾7н› @¼¦5ï;o1ܼ×j佂ä.½®Ñ<½˜F‚½š7:NXP¼Ë®ä¾E–=>“=I«=y‘=½]w~¾9ݾ/×ß½æË½öÁ¢¾*3¾Z…õ¾ˆ›¾•¾vªD½˜x¿=|\|=É{€<óIà½t"þ¾ ,A¾:4:¾5­¼½ëZ½²o<é&¼:©½@e½™ä«½£ô„½xz½x©½Iœ¾,Bš<‰š <*`B=HW#½DÈ?¾*ʾMµ¾ 3”¾¬Æ¾&ç;¾S$r¾xgƾŽE¾‰ËŸ¾"´N¼»)Â=[<çT_½ƒ§[¾'Ï1¾mþþqp¾*M:½”, »qM »åsнîL½†,Á½¨¦½©-"½‚'ɽSÌO¾µº ºì¼¡+= K¶½C¾ !*¾J¶¾´Ø¾Râ¾#?@¾Mµû¾h¨ ¾‰øâ¾–A£¾p ½Ð=¾  ¾•ìç¾*–ü¼–k=nqÖ¼ÏɈ¾6âš¾¥&S¾»%o¾˜^ë¾2/6½s_&¼¬Å¼Ë'€½H‰`½–²½Ã½¼“Ú½Œ3½¯"B¼qbÕ½:1<°×=˼‚Td½¿ow½ï%œ¾^ɾ-¬†¾RÛa¾jdc¾‹“¼¾¥DS¾©OJ¾]Ió½3—=ª®ï<˜œ0¾÷5¾°ß™¾×óR¾¹_¾h:š½¹µ¹½í¼Å ò½-²B½ƒõÀ½³Ò‡½·˜ª½‹”õ½†_ó¼xØf½&Ö*;Èu†=”´z=ƒ Ö¼6û½´(ß¾šc¾; f¾Xh{¾p‘ó¾‹ì‚¾¤žü¾²9¾€ÝgŸ=ÖÜ=ï½é›¾°X¾êp¾ÒÈ<¾Š¹½íïɽ.“w¼Å#ɽÁK½bOÛ½œ[G½¤Iœ½€.h½RǼdÖ§¼Ýdt<-b›=ß®ò> ™ =ÜݽeH6¾ž(¾H**¾Y»Ö¾pF-¾‡ÕÕ¾—K¾±öC¾Ê½Ámù=èí=õrÔ½o–¾ û^¾íÞ¾ßJ÷¾–뺾_ƒ½D­¼¾*‘½í<½GC½‡J½Þ½\!­½/åù¼K&º¼ ô«<±rÛ> z>>E‡>î¼¾œ'¾袾N„¾R<Ⱦdi÷¾{t]¾Ý3¾«ð°¾˜VP¾1Ú=Ôûñ>!D^;%M¾„:ɾßj`¾Üôã¾™Êd¾ YʽEK–¼ª·z¼õ_†½;QѽvQȽtðF½6?"½Ðâ¼BBJ<5„=è >´>a§œ>4Np=·\¾àš>á>^†>Mè©= ¶÷½ãã=¾2o¾#T\¾+^w¾8m¾]šæ¾—‚ž¾¦o¾V‘=EJ>1†›=á½Ñ )¾™RC¾°F¾ƒÖÕ½ê:¯½|X¼‚>ÿ¼ç}”½G±>½…Ê_½z”\½!Op½/kɼ“-¯<“¥È=I«^=ìž!>?½>Eü =pD2½š(¬¾ Šá½þÜ`¾1%¾•‘¾;n<¾‹ˆq¾§kú¾u™(¼YzÊ>î‰> Ëͼԙó¾Y˜ã¾Ý%¾`jú½Æ[½ å¼™D¼ó¢½Ohc½–Ì@½–Œ,½Hê½H^ʼÎSÝ;J¾=öõ=u¾>b >.Á=¥Ÿî½~.½¾y'½±ÑнÃÔ•½à™¾d¾|¶¾¡L¾jÓ½U•´=öü> ¡=”¾¼Å¾TîA¾6bV½¤½‹"¼»/Ÿ½yl½M¹b½¨\4½ºq½‡cª½i]/½ ¼ÇæÓ–=Ï× á=›¨=½qi¾8¾­9½‰rн$û½\½…ø½ASh½¶x½âŽ ½µhཉZ>½1½vUQ¼ñW¾¼ºæ= Mé=ñPË=ò‚t=iõ7'ļx\½9ã½j¶Å½Ë›!¾;w~¾z•¾Z»,½¦™ž=J§=í!|=ÂjS»·ÝH½¹7i½Û=m½sÏ ½D·*½)Ø‚½jí½.\d½¾‹Ç¾Ø#½äÌA½¢Êr½O¹Õ½Ìüš½¬Žn½œþ|»$w¼=Ë^©>,y=¼¤8=#JW>E½¥$.½fRa½dT=½G2^½ú½´n½ÁZR¾N¾`½Ää?casacore-3.7.1/images/Images/test/test_image.im/table.info000066400000000000000000000000311476623553700234400ustar00rootroot00000000000000Type = Image SubType = casacore-3.7.1/images/Images/test/test_image.im/table.lock000066400000000000000000000005051476623553700234430ustar00rootroot00000000000000=¾¾¾¾9syncBlockcasacore-3.7.1/images/Regions.h000066400000000000000000000036141476623553700163540ustar00rootroot00000000000000//# Regions.h: Regions in N-dimensional astronomical images //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_REGIONS_H #define IMAGES_REGIONS_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // // Regions in N-dimensional astronomical images // // //
      • class ImageInterface // // // // // // // // // //# //# // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Regions/000077500000000000000000000000001476623553700161775ustar00rootroot00000000000000casacore-3.7.1/images/Regions/AipsIOReaderWriter.cc000066400000000000000000000107061476623553700221560ustar00rootroot00000000000000//# AipsIOReaderWriter.cc: Implementation for reading/writing CASA region AIPSIO files. //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include namespace casacore { // AIPSIOREADERWRITER DEFINITIONS // // Static Definitions // //static const pair AipsIOReaderWriter::VERSION("version", "1.0"); // Non-Static Definitions // AipsIOReaderWriter::AipsIOReaderWriter() { } AipsIOReaderWriter::~AipsIOReaderWriter() { } // currently only supports a single region contained within // the file. It would be nice to be able to have multiple // regions in a single file. bool AipsIOReaderWriter::read(Record& region){ ImageRegion *leImgReg=0; read( leImgReg ); try{ // Convert the ImageRegion to a Record //Record * leRecord = new Record(); region.assign( leImgReg->toRecord(String("")) ); String comment = "Created from file: "; // pFilename_p->c_str() ); region.define( "comment", comment ); delete leImgReg; } catch(...) { setError( String( "An error has occurred while reading file " ) + *pFilename_p, True ); return False; } return True; } // currently only supports a single region contained within // the file. It would be nice to be able to have multiple // regions in a single file. bool AipsIOReaderWriter::read(ImageRegion*& region){ try { // open the file AipsIO ios(pFilename_p->c_str(), ByteIO::Old ); // The commented out lines really should be used, but when // uncommented we get exceptions thrown. For some reason // AipsIO finds a type of TableRecord, then a type of RecordDesc // this causes Exceptions to be thrown because `the type we give // and the type found don't match. This could be due to the way // the file is saved or some other quirk in AipsIO. TableRecord leTblRec; //ios.getstart( "TableRecord" ); ios >> leTblRec; //ios.getend(); //if ( regionname.length() > 0 ) // region = ImageRegion::fromRecord( leTblRec, regionname ); //else // TODO strip path part off and use just the tail of // the filename. region = ImageRegion::fromRecord( leTblRec, pFilename_p->c_str() ); //delete leTblRec; } catch(...) { setError( String( "An error has occurred while reading file " ) + *pFilename_p, True ); return False; } return True; } Bool AipsIOReaderWriter::write(const Record& region ) const { // open the file try { AipsIO os( pFilename_p->c_str(), ByteIO::NewNoReplace ); os << region; } catch(...) { setError( String( "An error has occurred while writing file " ) + *pFilename_p, True ); return False; } return True; } Bool AipsIOReaderWriter::write(const ImageRegion& region ) const { // Convert the ImageRegion to a record and call tour // other write method that uses records. try { Record * leRecord = new Record(); leRecord->assign( region.toRecord( *pRegionName_p ) ); write( *leRecord ); } catch(...) { setError( String( "An error has occurred while writing file " ) + *pFilename_p, True ); return False; } return True; } void AipsIOReaderWriter::setOptions(const Record*) { setError( String( "AipsIO region files do not contain any display options, no options to set." ), False ); return; } } //# NAMESPACE CASACORE - END casacore-3.7.1/images/Regions/AipsIOReaderWriter.h000066400000000000000000000053421476623553700220200ustar00rootroot00000000000000//# AipsIOReaderWriter.h: Implementation for reading/writing CASA AipsIO region files produced by the viewer. //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_AIPSIOREADERWRITER_H #define IMAGES_AIPSIOREADERWRITER_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Implementation of CASA region AipsIO file reader and writer // // // // // // // // //
      • RFReaderWriter // // // // // // // // // // // // To provide a class for reading and writing ImageRegion class records that // have been stored into an AipsIO file. // // //# //# class AipsIOReaderWriter : public RFReader, public RFWriter { public: // Constructor. AipsIOReaderWriter(); // Destructor. ~AipsIOReaderWriter(); // RSFileReader methods // // // Implements RSFileReader::read. bool read(Record& region); bool read(ImageRegion*& region); // // RSFileWriter methods // // Implements RSFileWriter::setOptions. void setOptions(const Record* options); // // Implements RSFileWriter::write bool write(const Record& region) const; bool write(const ImageRegion& region) const; // }; } //# end namespace #endif casacore-3.7.1/images/Regions/ImageRegion.cc000066400000000000000000000222501476623553700206750ustar00rootroot00000000000000//# ImageRegion.cc: Class to hold a region of interest in an image //# Copyright (C) 1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ImageRegion::ImageRegion() : LattRegionHolder (uInt(0)), itsWC (0) {} ImageRegion::ImageRegion (const LCRegion& region) : LattRegionHolder (region), itsWC (0) {} ImageRegion::ImageRegion (const LCSlicer& slicer) : LattRegionHolder (slicer), itsWC (0) {} ImageRegion::ImageRegion (const WCRegion& region) : LattRegionHolder (region.ndim()), itsWC (region.cloneRegion()) {} ImageRegion::ImageRegion (LCRegion* region) : LattRegionHolder (region), itsWC (0) {} ImageRegion::ImageRegion (LCSlicer* slicer) : LattRegionHolder (slicer), itsWC (0) {} ImageRegion::ImageRegion (WCRegion* region) : LattRegionHolder (region->ndim()), itsWC (region) {} ImageRegion::ImageRegion (const ImageRegion& other) : LattRegionHolder (other), itsWC (other.itsWC) { if (itsWC != 0) { itsWC = itsWC->cloneRegion(); } } ImageRegion::~ImageRegion() { delete itsWC; } ImageRegion& ImageRegion::operator= (const ImageRegion& other) { if (this != &other) { LattRegionHolder::operator= (other); delete itsWC; itsWC = other.itsWC; if (itsWC != 0) { itsWC = itsWC->cloneRegion(); } } return *this; } ImageRegion* ImageRegion::clone() const { return new ImageRegion (*this); } Bool ImageRegion::operator== (const LattRegionHolder& other) const { if (! LattRegionHolder::operator== (other)) { return False; } if (itsWC != 0) { return (*itsWC == *other.asWCRegionPtr()); } return True; } ImageRegion* ImageRegion::fromLatticeExpression(const String& latticeExpression) { if (latticeExpression.empty()) { return 0; } // Get LatticeExprNode (tree) from parser. LatticeExprNode node = ImageExprParse::command(latticeExpression); WCLELMask region(node); return new ImageRegion(region); } ImageRegion* ImageRegion::fromRecord(LogIO* logger, const CoordinateSystem& coords, const IPosition& imShape, const Record& regionRecord) { if (logger != 0) { *logger << LogOrigin("ImageRegion", __FUNCTION__); } ImageRegion* pRegion = 0; if (regionRecord.nfields() == 0) { IPosition blc(imShape.nelements(), 0); IPosition trc(imShape - 1); LCSlicer slicer(blc, trc, RegionType::Abs); pRegion = new ImageRegion(slicer); if (logger != 0) { *logger << LogIO::NORMAL << "Selected bounding box : " << endl; *logger << LogIO::NORMAL << " " << blc << " to " << trc << " (" << CoordinateUtil::formatCoordinate(blc, coords) << " to " << CoordinateUtil::formatCoordinate(trc, coords) << ")" << LogIO::POST; } } else { pRegion = ImageRegion::fromRecord(TableRecord(regionRecord), ""); if (logger != 0) { LatticeRegion latRegion = pRegion->toLatticeRegion(coords, imShape); Slicer sl = latRegion.slicer(); *logger << LogIO::NORMAL << "Selected bounding box : " << endl; *logger << LogIO::NORMAL << " " << sl.start() << " to " << sl.end() << " (" << CoordinateUtil::formatCoordinate(sl.start(), coords) << " to " << CoordinateUtil::formatCoordinate(sl.end(), coords) << ")" << LogIO::POST; } } return pRegion; } Bool ImageRegion::isWCRegion() const { return (itsWC != 0); } const WCRegion* ImageRegion::asWCRegionPtr() const { AlwaysAssert (isWCRegion(), AipsError); return itsWC; } LCRegion& ImageRegion::asMask() { AlwaysAssert (isLCRegion(), AipsError); LCRegion* regPtr = const_cast(asLCRegionPtr()); AlwaysAssert (regPtr->isWritable(), AipsError); return *regPtr; } LatticeRegion ImageRegion::toLatticeRegion (const CoordinateSystem& cSys, const IPosition& shape) const { if (isLCRegion()) { return LatticeRegion (asLCRegion()); } if (isLCSlicer()) { return LatticeRegion (asLCSlicer().toSlicer (cSys.referencePixel(), shape), shape); } // LatticeRegion takes over the created LCRegion pointer, // so it does not need to be deleted. // This is the top conversion, so use all axes. return LatticeRegion (toLCRegion (cSys, shape)); } LCRegion* ImageRegion::toLCRegion (const CoordinateSystem& cSys, const IPosition& shape) const { // Convert the region to an LCRegion. LCRegion* region = 0; if (isLCRegion()) { region = asLCRegion().cloneRegion(); } else if (isWCRegion()) { region = itsWC->toLCRegion (cSys, shape); } else { throw (AipsError ("ImageRegion::toLCRegion - " " cannot convert its LCSlicer object to LCRegion")); } return region; } TableRecord ImageRegion::toRecord (const String& tableName) const { TableRecord record; if (isLCRegion()) { return asLCRegion().toRecord (tableName); } if (isWCRegion()) { return itsWC->toRecord (tableName); } return asLCSlicer().toRecord (tableName); } ImageRegion* ImageRegion::fromRecord (const TableRecord& record, const String& tableName) { // See if this is a region record. if (! record.isDefined ("isRegion")) { throw (AipsError ("ImageRegion::fromRecord - " "record does not define a region")); } // Convert to correct region object. // Note that in the following the ImageRegion constructors take // over the pointer returned by fromRecord. Int regionType = record.asInt ("isRegion"); if (regionType == RegionType::LC) { return new ImageRegion (LCRegion::fromRecord (record, tableName)); } if (regionType == RegionType::WC) { return new ImageRegion (WCRegion::fromRecord (record, tableName)); } else if (regionType != RegionType::ArrSlicer) { throw (AipsError ("ImageRegion::fromRecord - " "record has an unknown region type")); } return new ImageRegion (LCSlicer::fromRecord (record, tableName)); } LattRegionHolder* ImageRegion::makeUnion (const LattRegionHolder& other) const { if (! isWCRegion()) { return LattRegionHolder::makeUnion (other); } return new ImageRegion (new WCUnion (*asWCRegionPtr(), *other.asWCRegionPtr())); } LattRegionHolder* ImageRegion::makeIntersection (const LattRegionHolder& other) const { if (isWCRegion()) { return new ImageRegion (new WCIntersection (*asWCRegionPtr(), *other.asWCRegionPtr())); } return LattRegionHolder::makeIntersection (other); } LattRegionHolder* ImageRegion::makeDifference (const LattRegionHolder& other) const { if (isWCRegion()) { return new ImageRegion (new WCDifference (*asWCRegionPtr(), *other.asWCRegionPtr())); } return LattRegionHolder::makeDifference (other); } LattRegionHolder* ImageRegion::makeComplement() const { if (isWCRegion()) { return new ImageRegion (new WCComplement (*asWCRegionPtr())); } return LattRegionHolder::makeComplement(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/images/Regions/ImageRegion.h000066400000000000000000000161261476623553700205440ustar00rootroot00000000000000//# ImageRegion.h: Class to hold a region of interest in an image //# Copyright (C) 1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_IMAGEREGION_H #define IMAGES_IMAGEREGION_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class CoordinateSystem; class IPosition; class LCRegion; class LCSlicer; class WCRegion; class String; class TableRecord; // // Class to hold a region of interest in an image. // // // // // //
      • LCSlicer //
      • WCRegion // // // The only purpose of ImageRegion is to have a single object for // the various kinds of regions. It can hold a // LCRegion, // LCSlicer, and // WCRegion. // // // // // // // It was felt that making an abstract base class LatticeRegion for // LCRegion and WCRegion would create undesirable dependencies of // module Lattices on module Coordinates. E.g. it would be impossible // to have a function toWCRegion. // Therefore the container class ImageRegion is chosen. // //# //#
      • //# class ImageRegion : public LattRegionHolder { public: // Default constructor (has no region at all). ImageRegion(); // Construct from a region based on lattice coordinates. ImageRegion (const LCRegion&); // Construct from a slicer based on lattice coordinates. ImageRegion (const LCSlicer&); // Construct from a region based on world coordinates. ImageRegion (const WCRegion&); // Similar constructors as above, but using a pointer. // It takes over the pointer, so the user should not delete the // object. It is deleted by the ImageRegion destructor. // explicit ImageRegion (LCRegion*); explicit ImageRegion (LCSlicer*); explicit ImageRegion (WCRegion*); // // Copy constructor (copy semantics). ImageRegion (const ImageRegion& other); virtual ~ImageRegion(); // Assignment (copy semantics). ImageRegion& operator= (const ImageRegion& other); // Clone the object. virtual ImageRegion* clone() const; // Comparison virtual Bool operator==(const LattRegionHolder& other) const; // Create an ImageRegion from a lattice expression. Returned pointer // is created via new(); it is the caller's responsibility to delete it. static ImageRegion* fromLatticeExpression(const String& latticeExpression); // Create an ImageRegion from a record. The returned pointer is created via // new(). It's the callers responsibility to delete it. // If a null pointer is passed in for logger no logging is done, // otherwise informational messages regarding bounding boxes are emitted // to the logger object. static ImageRegion* fromRecord (LogIO *logger, const CoordinateSystem& coords, const IPosition& imShape, const Record& regionRecord); // Test if the underlying region is an WCRegion. virtual Bool isWCRegion() const; // Get the region as a pointer to WCRegion. // An exception is thrown if the region is not the correct type. // Functions isWCRegion() can be used to test the type. virtual const WCRegion* asWCRegionPtr() const; // Get the region as an LCSlicer or WCRegion. // An exception is thrown if the region is not the correct type. // Functions isWCRegion(), etc. can be used to test the type. // const LCRegion& asLCRegion() const; const LCSlicer& asLCSlicer() const; const WCRegion& asWCRegion() const; // // Get the region as a writable mask. // It throws an exception if the region is not an LCRegion or if // its mask is not writable. LCRegion& asMask(); // Convert to a LatticeRegion using the given coordinate system // (with reference pixel) and shape. // It will also make the region complete (absolute and non-fractional). virtual LatticeRegion toLatticeRegion (const CoordinateSystem& cSys, const IPosition& shape) const; // Convert to an LCRegion using the given coordinate system // (with reference pixel) and shape. // It will also make the region complete (absolute and non-fractional). // An exception is thrown if the region type is a LCSlicer. // The axes argument tells which axes to use from the coordinate // system and shape. LCRegion* toLCRegion (const CoordinateSystem& cSys, const IPosition& shape) const; // Convert the (derived) object to a record. // The record can be used to make the object persistent. TableRecord toRecord (const String& tableName) const; // Convert correct object from a record. static ImageRegion* fromRecord (const TableRecord&, const String& tableName); // Form a compound from this and the other region. // virtual LattRegionHolder* makeUnion (const LattRegionHolder& other) const; virtual LattRegionHolder* makeIntersection (const LattRegionHolder& other) const; virtual LattRegionHolder* makeDifference (const LattRegionHolder& other) const; virtual LattRegionHolder* makeComplement() const; // private: WCRegion* itsWC; }; inline const LCRegion& ImageRegion::asLCRegion() const { return *asLCRegionPtr(); } inline const LCSlicer& ImageRegion::asLCSlicer() const { return *asLCSlicerPtr(); } inline const WCRegion& ImageRegion::asWCRegion() const { return *asWCRegionPtr(); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Regions/RFReaderWriter.cc000066400000000000000000000102441476623553700213360ustar00rootroot00000000000000//# RFReaderWriter.cc: Interfaces for classes that read/write image regions. //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include //#include namespace casacore { // RFERROR DEFINITIONS // RFError::RFError() : fatal_p(false) { } RFError::RFError(const String& error, bool isFatal) : error_p(error), fatal_p(isFatal) { } RFError::~RFError() { } bool RFError::isFatal() const { return fatal_p; } const String& RFError::error() const { return error_p; } void RFError::set(const String& error, bool isFatal) { error_p = error; fatal_p = isFatal; } // RFREADERWRITER DEFINITIONS // // Non-Static Methods // void RFReaderWriter::setFile(const String& filename) { pFilename_p = new String( filename.c_str() ); } void RFReaderWriter::setName(const String& regionName) { pRegionName_p = new String( regionName ); } const RFError& RFReaderWriter::lastError() const{ return lastError_p; } void RFReaderWriter::setError(const String& error, bool isFatal) const { const_cast(lastError_p).set(error, isFatal); } // Static Methods // RFReaderWriter::SupportedType RFReaderWriter::supportedTypes(String t) { t.downcase(); if(t == "aips-box" ) return AIPS_BOX; else if(t == "ds9") return DS9; else if(t == "casa-xml") return CASA_XML; else if(t == "aips-io") return AIPS_IO; else return DS9; // default } String RFReaderWriter::supportedTypes(SupportedType type) { switch(type) { case AIPS_BOX: return "AIPS-BOX"; case DS9: return "DS9"; case CASA_XML: return "CASA-XML"; case AIPS_IO: return "AIPS-IO"; default: return ""; // unknown } } String RFReaderWriter::extensionForType(SupportedType type) { switch(type) { case AIPS_BOX: return ""; case DS9: return "reg"; case CASA_XML: return "xml"; case AIPS_IO: return "rgn"; default: return ""; // unknown } } Vector RFReaderWriter::supportedTypes(){ Vector v(4); v[0] = AIPS_BOX; v[1] = DS9; v[2] = CASA_XML; v[3] = AIPS_IO; return v; } Vector RFReaderWriter::supportedTypeStrings() { Vector types = supportedTypes(); Vector v(types.size()); for(unsigned int i = 0; i < v.size(); i++) v[i] = supportedTypes(types[i]); return v; } RFReader* RFReaderWriter::readerForType(SupportedType type) { switch(type) { //case AIPS_BOX return new AipsBoxReaderWriter(); case AIPS_IO: return new AipsIOReaderWriter(); //case DS9: return new DS9ReaderWriter(); //case CASA_XML: return new XMLReaderWriter(); default: return NULL; // unknown } } RFWriter* RFReaderWriter::writerForType(SupportedType type) { switch(type) { //case AIPS_BOX: return new AipsBoxReaderWriter(); case AIPS_IO: return new AipsIOReaderWriter(); //case DS9: return new DS9ReaderWriter(); //case CASA_XML: return new XMLFileReaderWriter(); default: return NULL; // unknown } } } casacore-3.7.1/images/Regions/RFReaderWriter.h000066400000000000000000000200511476623553700211750ustar00rootroot00000000000000//# RegionFileReaderWriter.h: Interfaces for classes that read/write image regions. //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_RFREADERWRITER_H #define IMAGES_RFREADERWRITER_H //# Includes #include #include #include #include namespace casacore {//# NAMESPACE CASACORE - BEGIN //# Forward declarations class RFReader; class RFWriter; // // Convenience class for a String/bool pair. // // // // // // // // // // // // // // // // // // //# //#
      • //# class RFError { public: // Constructor, blank error. RFError(); // Constructor, error with the given text and isFatal flag. RFError(const String& error, bool isFatal = false); // Destructor. ~RFError(); // Returns whether this error was fatal or not. bool isFatal() const; // Returns this error's text. const String& error() const; // Sets the error. void set(const String& error, bool isFatal = false); private: String error_p; bool fatal_p; }; // // Superclass for readers and writers containing common definitions and // operations. // // // // // // // // // // // // // // // // // // //# //#
      • //# class RFReaderWriter { public: // An enum of all known subclasses/formats supported. enum SupportedType { AIPS_BOX, DS9, CASA_XML, AIPS_IO }; // Converts between enum and String for SupportedType. // static SupportedType supportedTypes(String type); static String supportedTypes(SupportedType type); // // Returns the file extension for the given SupportedType. static String extensionForType(SupportedType type); // Returns all known SupportedTypes. // static Vector supportedTypes(); static Vector supportedTypeStrings(); // // Returns an appropriate child RFReader class for the given // SupportedType, or NULL for an error (shouldn't happen). static RFReader* readerForType(SupportedType type); // Returns an new appropriate child RfWriter class for the given // SupportedType, or NULL for an error (shouldn't happen). static RFWriter* writerForType(SupportedType type); // Returns an new appropriate options widget for the given SupportedType, // or NULL for an error (shouldn't happen). static Record* optionsWidgetForType(SupportedType type); // Constructor. RFReaderWriter() { } // Destructor. virtual ~RFReaderWriter() { } // Sets the file to be read/written to the given. virtual void setFile(const String& filename); // Sets the region name associated withe the file to be read or written. virtual void setName(const String& regionName); // Returns the last error set during read/write. virtual const RFError& lastError() const; protected: // Filename to be read/written. String *pFilename_p; // Name to be assigned to the region String *pRegionName_p; // Last error seen during read/write. RFError lastError_p; // Record containg plotting options for the regions Record options_p; // Convenience method for setting last error during read/write. virtual void setError(const String& error, bool fatal = false) const; }; // // Abstract superclass for any class that reads a format that produces // Regions from a file. // // // // // // // // // // // // Provide a well defined set of operations for reading // region files, regardless of the data format. // // Note that some file formats allow for plotting options // to be defined as well as the regions. These options are // read and stored in a record of ... , the contents // of this record is ill-defined (ie. there is no standard). // // There may come a time where a standard is necessary. // // // // // // // //# //#
      • //# class RFReader : public virtual RFReaderWriter { public: // Constructor. RFReader() { } // Destructor. virtual ~RFReader() { } // Provides access to the plotting options that // were found in the region file. virtual Record* options() { return &options_p; }; // reported, false otherwise. If false is returned, the details can be // found using lastError(). Any valid Regions that were read from the // file are placed in the given vector (which is cleared first). virtual bool read(Record& region) = 0; // Calls setFile() then read(). virtual bool readFile(const String& file, Record& region) { setFile(file); return read(region); } }; // // Abstract superclass for any class that writes Regions to a region // file format. // // // // // // // // // // // // Provide a well defined set of operations that all // region file writers must contain regardless of the // file format of the file being saved. . // // Note that some file formats allow for plotting options // to be stored with the region information. The setOptions // method allows the user to supply this information. // // // // // // // //# //#
      • //# class RFWriter : public virtual RFReaderWriter { public: // Constructor. RFWriter() { } // Destructor. virtual ~RFWriter() { } // Sets the optional to the values. These values are related to // the drawing of regions and not defining the regions themselves. // For example, the colour to draw the region as. virtual void setOptions(const Record* options) { options_p.defineRecord( "regionoptions", *options ); }; // Write the given regions to the filename set with setFile and returns // true if no errors were reported, false otherwise. If false is returned, // the details can be found using lastError(). virtual bool write(const Record& region) const = 0; // Calls setFile then write. virtual bool writeFile(const String& filename, const Record& regions) { setFile(filename); return write(regions); } }; } //# end namespace #endif casacore-3.7.1/images/Regions/RegionHandler.cc000066400000000000000000000074421476623553700212360ustar00rootroot00000000000000//# RegionHandler.cc: Base class for handling regions in an image //# Copyright (C) 2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN RegionHandler::~RegionHandler() {} RegionHandler* RegionHandler::clone() const { return new RegionHandler (*this); } void RegionHandler::setObjectPtr (void*) {} Bool RegionHandler::canDefineRegion() const { return False; } void RegionHandler::setDefaultMask (const String&) { throw AipsError ("RegionHandler::setDefaultMask" " cannot be used for this image type"); } String RegionHandler::getDefaultMask() const { return ""; } Bool RegionHandler::defineRegion (const String&, const ImageRegion&, RegionHandler::GroupType, Bool) { throw AipsError ("RegionHandler::defineRegion" " cannot be used for this image type"); } Bool RegionHandler::hasRegion (const String&, RegionHandler::GroupType) const { return False; } Bool RegionHandler::renameRegion (const String&, const String&, RegionHandler::GroupType, Bool) { throw AipsError ("RegionHandler::renameRegion" " cannot be used for this image type"); return False; } Bool RegionHandler::removeRegion (const String&, RegionHandler::GroupType, Bool throwIfUnknown) { if (throwIfUnknown) { throw AipsError ("RegionHandler::removeRegion" " cannot be used for this image type"); } return False; } Vector RegionHandler::regionNames (RegionHandler::GroupType) const { return Vector(); } ImageRegion* RegionHandler::getRegion (const String&, RegionHandler::GroupType, Bool throwIfUnknown) const { if (throwIfUnknown) { throw AipsError ("RegionHandler::findRegionGroup" " cannot be used for this image type"); } return 0; } ImageRegion RegionHandler::makeMask (const LatticeBase&, const String&) { throw (AipsError ("RegionHandler::makeMask - " "cannot create mask for virtual image")); return ImageRegion(); } String RegionHandler::makeUniqueRegionName (const String& rootName, uInt startNumber) const { while (True) { ostringstream oss; oss << startNumber; String name = rootName + String(oss); if (! hasRegion (name, RegionHandler::Any)) { return name; } startNumber++; } return String(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/images/Regions/RegionHandler.h000066400000000000000000000146741476623553700211050ustar00rootroot00000000000000//# RegionHandler.h: Abstract base class for handling regions in images //# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_REGIONHANDLER_H #define IMAGES_REGIONHANDLER_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class Table; class ImageRegion; class LatticeBase; class LCPagedMask; class String; // // Base class for handling regions in images // // // // // //
      • PagedImage //
      • ImageRegion // // // Persistent regions are stored as subrecords of the table keywords // "regions" and "masks". The user can choose one of both keywords. // Keyword "masks" is meant for true image masks, i.e. telling for // each pixel if it is good or bad. Keyword "regions" is meant for // true regions in an image. //

        // This class handles defining, getting and removing such regions. // It is used by class PagedImage, but it can also // be used by other code to handle regions in other tables. //

        // Another function performed by this class for PagedImage is the // definition of the default region to be used with an image. // // // // // This class has 2 purposes: //

          //
        1. This untemplated code can be factored out from the templated // Image classes. //
        2. The functions can easily be used by other code. //
        // //# //#
      • //# class RegionHandler { public: virtual ~RegionHandler(); // Define the possible group types (regions or masks). enum GroupType { Regions, Masks, Any }; // Make a copy of the object. virtual RegionHandler* clone() const; // Set the object pointer (for RegionHandlerTable's callback). // Default implementation does nothing. virtual void setObjectPtr (void* objectPtr); // Can the class indeed define and handle regions? // The default implementation returns False. virtual Bool canDefineRegion() const; // Set the default mask to the mask with the given name. // It constructs a ImageRegion object for the new default mask. // If the table is writable, the setting is persistent by writing // the name as a keyword. // If the given maskName is the empty string, the default mask is unset. virtual void setDefaultMask (const String& maskName); // Get the name of the default mask. // An empty string is returned if no default mask. virtual String getDefaultMask() const; // Define a region belonging to the table. // The group type determines if it stored as a region or mask. // If overwrite=False, an exception will be thrown if the region // already exists in the "regions" or "masks" keyword. // Otherwise the region will be removed first. //
        A False status is returned if the table is not writable virtual Bool defineRegion (const String& name, const ImageRegion& region, RegionHandler::GroupType, Bool overwrite = False); // Does the table have a region with the given name? virtual Bool hasRegion (const String& name, RegionHandler::GroupType = RegionHandler::Any) const; // Get a region belonging to the table. // A zero pointer is returned if the region does not exist. // The caller has to delete the ImageRegion object created. //
        No exception is thrown if the region does not exist. virtual ImageRegion* getRegion (const String& name, RegionHandler::GroupType = Any, Bool throwIfUnknown = True) const; // Rename a region. // If a region with the new name already exists, it is deleted or // an exception is thrown (depending on overwrite). // The region name is looked up in the given group(s). //
        An exception is thrown if the old region name does not exist. virtual Bool renameRegion (const String& newName, const String& oldName, RegionHandler::GroupType = Any, Bool overwrite = False); // Remove a region belonging to the table. //
        Optionally an exception is thrown if the region does not exist. //
        A False status is returned if the table is not writable virtual Bool removeRegion (const String& name, RegionHandler::GroupType = Any, Bool throwIfUnknown = True); // Get the names of all regions/masks. virtual Vector regionNames (RegionHandler::GroupType = Any) const; // Make a unique region name from the given root name, thus make it such // that the name is not already in use for a region or mask. // The root name is returned if it is already unique. // Otherwise a number is appended to the root name to make it unique. // The number starts at the given number and is incremented until the name // is unique. String makeUniqueRegionName (const String& rootName, uInt startNumber=1) const; // Make a mask for a lattice (e.g. a PagedImage or TempImage). // It creates it with the shape and tile shape of the lattice. virtual ImageRegion makeMask (const LatticeBase& lattice, const String& name); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Regions/RegionHandlerHDF5.cc000066400000000000000000000243211476623553700216400ustar00rootroot00000000000000//# RegionHandlerHDF5.cc: Class for keeping regions in an HDF5 file //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN RegionHandlerHDF5::RegionHandlerHDF5 (GetCallback* callback, void* objectPtr) : itsChanged (False), itsCallback (callback), itsObjectPtr (objectPtr) {} RegionHandlerHDF5::RegionHandlerHDF5 (const RegionHandlerHDF5& that) : RegionHandler(that), itsChanged (that.itsChanged), itsCallback (that.itsCallback), itsObjectPtr (that.itsObjectPtr) {} RegionHandlerHDF5::~RegionHandlerHDF5() {} RegionHandlerHDF5& RegionHandlerHDF5::operator= (const RegionHandlerHDF5& that) { if (this != &that) { itsChanged = that.itsChanged; itsCallback = that.itsCallback; itsObjectPtr = that.itsObjectPtr; } return *this; } RegionHandlerHDF5* RegionHandlerHDF5::clone() const { return new RegionHandlerHDF5 (*this); } void RegionHandlerHDF5::setObjectPtr (void* objectPtr) { itsObjectPtr = objectPtr; } Bool RegionHandlerHDF5::canDefineRegion() const { return True; } void RegionHandlerHDF5::setDefaultMask (const String& regionName) { itsRecord.define ("Image_defaultmask", regionName); itsChanged = True; } String RegionHandlerHDF5::getDefaultMask() const { Int field = itsRecord.fieldNumber ("Image_defaultmask"); if (field < 0) { return String(); } return itsRecord.asString(field); } Bool RegionHandlerHDF5::defineRegion (const String& name, const ImageRegion& region, RegionHandler::GroupType type, Bool overwrite) { // First check if the region is already defined in "regions" or "masks". // If so, remove it if possible. Otherwise throw an exception. Int groupField = findRegionGroup (name, RegionHandler::Any, False); if (groupField >= 0) { if (!overwrite) { throw (AipsError ("RegionHandlerHDF5::defineRegion - file " + file()->getName() + " already has a region or mask with name " + name)); } TableRecord& regs = itsRecord.rwSubRecord(groupField); if (regs.isDefined (name)) { regs.removeField (name); } } // Okay, we can define the region now. // Define the "regions" or "masks" group when needed. String groupName = "regions"; if (type == RegionHandler::Masks) { groupName = "masks"; } if (! itsRecord.isDefined (groupName)) { itsRecord.defineRecord (groupName, TableRecord()); } // Now define the region in the group. itsRecord.rwSubRecord(groupName).defineRecord (name, region.toRecord (file()->getName())); itsChanged = True; return True; } Bool RegionHandlerHDF5::hasRegion (const String& name, RegionHandler::GroupType type) const { return (findRegionGroup (name, type, False) >= 0); } Bool RegionHandlerHDF5::renameRegion (const String& newName, const String& oldName, RegionHandler::GroupType type, Bool overwrite) { // Check that the region exists. Int oldGroupField = findRegionGroup (oldName, type, True); // First check if the region is already defined. // Check that the region is in the same group as the original. // Remove it if overwrite is true. Otherwise throw an exception. Int groupField = findRegionGroup (newName, RegionHandler::Any, False); if (groupField >= 0) { if (groupField != oldGroupField) { throw (AipsError ("RegionHandlerHDF5::renameRegion - file " + file()->getName() + " already has a region or mask with name " + newName + " in another group")); } if (!overwrite) { throw (AipsError ("RegionHandlerHDF5::renameRegion - file " + file()->getName() + " already has a region or mask with name " + newName)); } TableRecord& regs = itsRecord.rwSubRecord(groupField); regs.removeField (newName); } TableRecord& regs = itsRecord.rwSubRecord(oldGroupField); ImageRegion* regPtr = getRegion (oldName, type, True); // First rename a possible mask file, which could in principle fail. // We only need to do that when it is an LCRegion. // We need to clone it to make it non-const. if (regPtr->isLCRegion()) { LCRegion* lcPtr = regPtr->asLCRegion().cloneRegion(); lcPtr->handleRename (newName, overwrite); // The region values might be changed, so redefine it. // Note that the ImageRegion constructor takes over the poiter, // so we do not need to delete lcPtr; TableRecord newValue = ImageRegion(lcPtr).toRecord (file()->getName()); regs.defineRecord (oldName, newValue); } delete regPtr; // Rename the keyword itself. regs.renameField (newName, oldName); // Rename the default mask name if that is the renamed region. if (getDefaultMask() == oldName) { setDefaultMask (newName); } itsChanged = True; return True; } Bool RegionHandlerHDF5::removeRegion (const String& name, RegionHandler::GroupType type, Bool throwIfUnknown) { Int groupField = findRegionGroup (name, type, throwIfUnknown); if (groupField >= 0) { ImageRegion* regPtr = getRegion (name, type, True); // Delete a possible mask file. // We only need to do that when it is an LCRegion. // We need to clone it to make it non-const. if (regPtr->isLCRegion()) { LCRegion* lcPtr = regPtr->asLCRegion().cloneRegion(); String msg; Bool error = False; try { lcPtr->handleDelete(); } catch (std::exception& x) { error = True; msg = x.what(); } delete lcPtr; if (error) { delete regPtr; throw (AipsError("Region " + name + " could not be removed\n" + msg)); } } delete regPtr; itsRecord.rwSubRecord(groupField).removeField (name); } // Clear the default mask name if that is the removed region. if (getDefaultMask() == name) { setDefaultMask (String()); } itsChanged = True; return True; } Vector RegionHandlerHDF5::regionNames (RegionHandler::GroupType type) const { uInt nreg = 0; uInt nmask = 0; const RecordDesc* regs = 0; const RecordDesc* masks = 0; if (type != RegionHandler::Masks) { Int field = itsRecord.fieldNumber ("regions"); if (field >= 0) { regs = &(itsRecord.subRecord(field).description()); nreg = regs->nfields(); } } if (type != RegionHandler::Regions) { Int field = itsRecord.fieldNumber ("masks"); if (field >= 0) { masks = &(itsRecord.subRecord(field).description()); nmask = masks->nfields(); } } Vector names(nreg + nmask); uInt i; for (i=0; iname(i); } for (i=0; iname(i); } return names; } ImageRegion* RegionHandlerHDF5::getRegion (const String& name, RegionHandler::GroupType type, Bool throwIfUnknown) const { Int groupField = findRegionGroup (name, type, throwIfUnknown); if (groupField >= 0) { const TableRecord& regs = itsRecord.subRecord(groupField); Int field = regs.fieldNumber (name); if (field >= 0) { return ImageRegion::fromRecord (regs.subRecord (field), file()->getName()); } } return 0; } Int RegionHandlerHDF5::findRegionGroup (const String& regionName, RegionHandler::GroupType type, Bool throwIfUnknown) const { // Check if the region is defined in "regions" or "masks". // If so, return its groupName. if (type != RegionHandler::Masks) { Int field = itsRecord.fieldNumber ("regions"); if (field >= 0) { const TableRecord& regs = itsRecord.subRecord(field); if (regs.isDefined (regionName)) { return field; } } } if (type != RegionHandler::Regions) { Int field = itsRecord.fieldNumber ("masks"); if (field >= 0) { const TableRecord& regs = itsRecord.subRecord(field); if (regs.isDefined (regionName)) { return field; } } } if (throwIfUnknown) { String typeName = "region/mask "; if (type == RegionHandler::Regions) { typeName = "region "; } else if (type == RegionHandler::Masks) { typeName = "mask "; } throw (AipsError ("RegionHandlerHDF5: " + typeName + regionName + " does not exist in file " + file()->getName())); } return -1; } ImageRegion RegionHandlerHDF5::makeMask (const LatticeBase& lattice, const String& name) { if (! lattice.isPaged()) { throw (AipsError ("RegionHandlerHDF5::makeMask - " "cannot create mask, because image is transient")); } LCHDF5Mask* mask = new LCHDF5Mask (TiledShape (lattice.shape(), lattice.niceCursorShape()), file(), name); return ImageRegion(mask); } void RegionHandlerHDF5::save (Bool always) { if (itsChanged || always) { HDF5Record::writeRecord (*file(), "maskinfo", itsRecord); itsChanged = False; } } void RegionHandlerHDF5::restore() { itsRecord = HDF5Record::readRecord (*file(), "maskinfo"); } } //# NAMESPACE CASACORE - END casacore-3.7.1/images/Regions/RegionHandlerHDF5.h000066400000000000000000000160071476623553700215040ustar00rootroot00000000000000//# RegionHandlerHDF5.h: Class for keeping regions in an HDF5 file //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_REGIONHANDLERHDF5_H #define IMAGES_REGIONHANDLERHDF5_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Class for keeping regions in an HDF5 file. // // // // // //
      • HDF5Image //
      • ImageRegion // // // Persistent regions are stored as subrecords of the table keywords // "regions" and "masks". The user can choose one of both keywords. // Keyword "masks" is meant for true image masks, i.e. telling for // each pixel if it is good or bad. Keyword "regions" is meant for // true regions in an image. //

        // This class handles defining, getting and removing such regions. // It is used by class , but it can also // be used by other code to handle regions in other tables. //

        // Another function performed by this class for PagedImage is the // definition of the default region to be used with an image. // // // // // This class has 2 purposes: //

          //
        1. This untemplated code can be factored out from the templated // Image classes. //
        2. The functions can easily be used by other code. //
        // //# //#
      • //# class RegionHandlerHDF5: public RegionHandler { public: // The HDF5File object needed for the region operations. typedef const std::shared_ptr& GetCallback (void* objectPtr); RegionHandlerHDF5 (GetCallback* callback, void* objectPtr); // Copy constructor (copy semantics). RegionHandlerHDF5 (const RegionHandlerHDF5&); virtual ~RegionHandlerHDF5(); // Assignment (copy semantics). RegionHandlerHDF5& operator= (const RegionHandlerHDF5&); // Make a copy of the object. virtual RegionHandlerHDF5* clone() const; // Set the object pointer for callback function. virtual void setObjectPtr (void* objectPtr); // This class can define and handle regions. virtual Bool canDefineRegion() const; // Set the default mask to the mask with the given name. // It constructs a ImageRegion object for the new default mask. // If the table is writable, the setting is persistent by writing // the name as a keyword. // If the given maskName is the empty string, the default mask is unset. virtual void setDefaultMask (const String& maskName); // Get the name of the default mask. // An empty string is returned if no default mask. virtual String getDefaultMask() const; // Define a region belonging to the table. // The group type determines if it stored as a region or mask. // If overwrite=False, an exception will be thrown if the region // already exists in the "regions" or "masks" keyword. // Otherwise the region will be removed first. //
        A False status is returned if the table is not writable virtual Bool defineRegion (const String& name, const ImageRegion& region, RegionHandler::GroupType, Bool overwrite = False); // Does the table have a region with the given name? virtual Bool hasRegion (const String& name, RegionHandler::GroupType = RegionHandler::Any) const; // Get a region belonging to the table. // A zero pointer is returned if the region does not exist. // The caller has to delete the ImageRegion object created. //
        No exception is thrown if the region does not exist. virtual ImageRegion* getRegion (const String& name, RegionHandler::GroupType = Any, Bool throwIfUnknown = True) const; // Rename a region. // If a region with the new name already exists, it is deleted or // an exception is thrown (depending on overwrite). // The region name is looked up in the given group(s). //
        An exception is thrown if the old region name does not exist. virtual Bool renameRegion (const String& newName, const String& oldName, RegionHandler::GroupType = Any, Bool overwrite = False); // Remove a region belonging to the table. //
        Optionally an exception is thrown if the region does not exist. //
        A False status is returned if the table is not writable virtual Bool removeRegion (const String& name, RegionHandler::GroupType = Any, Bool throwIfUnknown = True); // Get the names of all regions/masks. virtual Vector regionNames (RegionHandler::GroupType = Any) const; // Make a mask (an LCPagedMask) for a stored lattice (a PagedImage). // It creates it with the shape and tile shape of the lattice. virtual ImageRegion makeMask (const LatticeBase& lattice, const String& name); // Save the record containing the masking info in the HDF5 file. // It is only saved if changed or if always is true. void save (Bool always=False); // Restore the record containing the masking info from the HDF5 file. void restore(); private: // Get the file object. const std::shared_ptr& file() const { return itsCallback (itsObjectPtr); } // Find field number of the region group to which a region belongs // (i.e. the field number of the "regions" or "masks" field). // <0 is returned if the region does not exist. //
        Optionally an exception is thrown if the region does not exist. virtual Int findRegionGroup (const String& regionName, RegionHandler::GroupType = Any, Bool throwIfUnknown = True) const; TableRecord itsRecord; Bool itsChanged; //# Has the record changed? GetCallback* itsCallback; void* itsObjectPtr; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Regions/RegionHandlerMemory.cc000066400000000000000000000210531476623553700224210ustar00rootroot00000000000000//# RegionHandlerMemory.cc: Class for keeping regions in memory //# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN RegionHandlerMemory::RegionHandlerMemory() {} RegionHandlerMemory::RegionHandlerMemory (const RegionHandlerMemory& that) : RegionHandler(that) { operator= (that); } RegionHandlerMemory::~RegionHandlerMemory() { clear(); } RegionHandlerMemory& RegionHandlerMemory::operator= (const RegionHandlerMemory& that) { if (this != &that) { clear(); itsDefaultName = that.itsDefaultName; itsMaps[0] = that.itsMaps[0]; itsMaps[1] = that.itsMaps[1]; for (auto& x : itsMaps[0]) { x.second = static_cast(x.second)->clone(); } for (auto& x : itsMaps[1]) { x.second = static_cast(x.second)->clone(); } } return *this; } void RegionHandlerMemory::clear() { for (auto& x : itsMaps[0]) { delete static_cast(x.second); x.second = 0; } for (auto& x : itsMaps[1]) { delete static_cast(x.second); x.second = 0; } } RegionHandlerMemory* RegionHandlerMemory::clone() const { return new RegionHandlerMemory (*this); } Bool RegionHandlerMemory::canDefineRegion() const { return True; } void RegionHandlerMemory::setDefaultMask (const String& regionName) { itsDefaultName = regionName; } String RegionHandlerMemory::getDefaultMask() const { return itsDefaultName; } Bool RegionHandlerMemory::defineRegion (const String& name, const ImageRegion& region, RegionHandler::GroupType type, Bool overwrite) { // First check if the region is already defined in "regions" or "masks". // If so, remove it if possible. Otherwise throw an exception. Int groupField = findRegionGroup (name, RegionHandler::Any, False); if (groupField >= 0) { if (!overwrite) { throw (AipsError ("RegionHandlerMemory::defineRegion -" " a region or mask with name " + name + " already exists")); } itsMaps[groupField].erase (name); } // Okay, we can define the region now. groupField = 0; if (type == RegionHandler::Masks) { groupField = 1; } // Now define the region in the group. itsMaps[groupField][name] = region.clone(); return True; } Bool RegionHandlerMemory::hasRegion (const String& name, RegionHandler::GroupType type) const { return (findRegionGroup (name, type, False) >= 0); } Bool RegionHandlerMemory::renameRegion (const String& newName, const String& oldName, RegionHandler::GroupType type, Bool overwrite) { // Check that the region exists. Int oldGroupField = findRegionGroup (oldName, type, True); // First check if the region is already defined. // Check that the region is in the same group as the original. // Remove it if overwrite is true. Otherwise throw an exception. Int groupField = findRegionGroup (newName, RegionHandler::Any, False); if (groupField >= 0) { if (groupField != oldGroupField) { throw (AipsError ("RegionHandlerMemory::renameRegion -" " a region or mask with name " + newName + " already exists in another group")); } if (!overwrite) { throw (AipsError ("RegionHandlerMemory::renameRegion -" " a region or mask with name " + newName + " already exists")); } itsMaps[groupField].erase (newName); } // Get the old region. ImageRegion* regPtr = findRegion (oldName, type, True); // First rename a possible mask table, which could in principle fail. // We only need to do that when it is an LCRegion. // We need to clone it to make it non-const. if (regPtr->isLCRegion()) { LCRegion* lcPtr = regPtr->asLCRegion().cloneRegion(); lcPtr->handleRename (newName, overwrite); delete lcPtr; } // Rename the keyword itself (remove and insert). void* value = itsMaps[oldGroupField].at (oldName); itsMaps[oldGroupField].erase (oldName); itsMaps[oldGroupField][newName] = value; // Rename the default mask name if that is the renamed region. if (itsDefaultName == oldName) { setDefaultMask (newName); } return True; } Bool RegionHandlerMemory::removeRegion (const String& name, RegionHandler::GroupType type, Bool throwIfUnknown) { Int groupField = findRegionGroup (name, type, throwIfUnknown); if (groupField >= 0) { ImageRegion* regPtr = findRegion (name, type, True); // Delete a possible mask table. // We only need to do that when it is an LCRegion. // We need to clone it to make it non-const. if (regPtr->isLCRegion()) { LCRegion* lcPtr = regPtr->asLCRegion().cloneRegion(); String msg; Bool error = False; try { lcPtr->handleDelete(); } catch (std::exception& x) { error = True; msg = x.what(); } delete lcPtr; if (error) { throw (AipsError("RegionHandlerMemory - region " + name + " could not be removed\n" + msg)); } } // Delete the region object and remove from the map. delete regPtr; itsMaps[groupField].erase (name); } // Clear the default mask name if that is the removed region. if (itsDefaultName == name) { setDefaultMask (""); } return True; } Vector RegionHandlerMemory::regionNames (RegionHandler::GroupType type) const { uInt nreg = 0; uInt nmask = 0; if (type != RegionHandler::Masks) { nreg = itsMaps[0].size(); } if (type != RegionHandler::Regions) { nmask = itsMaps[1].size(); } Vector names(nreg + nmask); uInt i=0; if (nreg > 0) { for (const auto& x : itsMaps[0]) { names[i++] = x.first; } } if (nmask > 0) { for (const auto& x : itsMaps[1]) { names[i++] = x.first; } } return names; } ImageRegion* RegionHandlerMemory::getRegion (const String& name, RegionHandler::GroupType type, Bool throwIfUnknown) const { ImageRegion* regPtr = findRegion (name, type, throwIfUnknown); if (regPtr != 0) { return regPtr->clone(); } return 0; } ImageRegion* RegionHandlerMemory::findRegion (const String& name, RegionHandler::GroupType type, Bool throwIfUnknown) const { Int groupField = findRegionGroup (name, type, throwIfUnknown); if (groupField >= 0) { return static_cast(itsMaps[groupField].at(name)); } return 0; } Int RegionHandlerMemory::findRegionGroup (const String& regionName, RegionHandler::GroupType type, Bool throwIfUnknown) const { // Check if the region is defined in "regions" or "masks". // If so, return its number. if (type != RegionHandler::Masks) { if (itsMaps[0].find (regionName) != itsMaps[0].end()) { return 0; } } if (type != RegionHandler::Regions) { if (itsMaps[1].find (regionName) != itsMaps[1].end()) { return 1; } } if (throwIfUnknown) { String typeName = "region/mask "; if (type == RegionHandler::Regions) { typeName = "region "; } else if (type == RegionHandler::Masks) { typeName = "mask "; } throw (AipsError ("RegionHandlerMemory - " + typeName + regionName + " does not exist")); } return -1; } ImageRegion RegionHandlerMemory::makeMask (const LatticeBase& lattice, const String&) { LCMask* mask = new LCMask (lattice.shape()); return ImageRegion(mask); } } //# NAMESPACE CASACORE - END casacore-3.7.1/images/Regions/RegionHandlerMemory.h000066400000000000000000000147601476623553700222720ustar00rootroot00000000000000//# RegionHandlerMemory.h: Class for keeping regions in memory //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_REGIONHANDLERMEMORY_H #define IMAGES_REGIONHANDLERMEMORY_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Class for keeping regions in memory. // // // // // //
      • PagedImage //
      • ImageRegion // // // Persistent regions are stored as subrecords of the table keywords // "regions" and "masks". The user can choose one of both keywords. // Keyword "masks" is meant for true image masks, i.e. telling for // each pixel if it is good or bad. Keyword "regions" is meant for // true regions in an image. //

        // This class handles defining, getting and removing such regions. // It is used by class , but it can also // be used by other code to handle regions in other tables. //

        // Another function performed by this class for PagedImage is the // definition of the default region to be used with an image. //

        // The class consists of static functions only. // // // // // This class has 2 purposes: //

          //
        1. This untemplated code can be factored out from the templated // Image classes. //
        2. The functions can easily be used by other code. //
        // //# //#
      • //# class RegionHandlerMemory: public RegionHandler { public: RegionHandlerMemory(); // Copy constructor (copy semantics). RegionHandlerMemory (const RegionHandlerMemory&); virtual ~RegionHandlerMemory(); // Assignment (copy semantics). RegionHandlerMemory& operator= (const RegionHandlerMemory&); // Make a copy of the object. virtual RegionHandlerMemory* clone() const; // This class can define and handle regions. virtual Bool canDefineRegion() const; // Set the default mask to the mask with the given name. // If the given maskName is the empty string, the default mask is unset. virtual void setDefaultMask (const String& maskName); // Get the name of the default mask. // An empty string is returned if no default mask. virtual String getDefaultMask() const; // Define a region. // The group type determines if it is kept as a region or a mask. // If overwrite=False, an exception will be thrown if the region // already exists in the "regions" or "masks" group. // Otherwise the region will be removed first. //
        It always returns a True status. virtual Bool defineRegion (const String& name, const ImageRegion& region, RegionHandler::GroupType, Bool overwrite = False); // Is there a region with the given name? virtual Bool hasRegion (const String& name, RegionHandler::GroupType = RegionHandler::Any) const; // Get a region with the given name from the given group. // A zero pointer is returned if the region does not exist. // The caller has to delete the ImageRegion object created. //
        No exception is thrown if the region does not exist. virtual ImageRegion* getRegion (const String& name, RegionHandler::GroupType = Any, Bool throwIfUnknown = True) const; // Rename a region. // If a region with the new name already exists, it is deleted or // an exception is thrown (depending on overwrite). // The region name is looked up in the given group(s). //
        An exception is thrown if the old region name does not exist. //
        It always returns a True status. virtual Bool renameRegion (const String& newName, const String& oldName, RegionHandler::GroupType = Any, Bool overwrite = False); // Remove a region from the given group. //
        Optionally an exception is thrown if the region does not exist. //
        It always returns a True status. virtual Bool removeRegion (const String& name, RegionHandler::GroupType = Any, Bool throwIfUnknown = True); // Get the names of all regions/masks. virtual Vector regionNames (RegionHandler::GroupType = Any) const; // Make a mask (an LCMask) for a temporary lattice (a TempImage). // It creates it with the shape and tile shape of the lattice. virtual ImageRegion makeMask (const LatticeBase& lattice, const String& name); private: // Find group number of the region group to which a region belongs // (i.e. the field number of the "regions" or "masks" field). // -1 is returned if the region does not exist. //
        Optionally an exception is thrown if the region does not exist. Int findRegionGroup (const String& regionName, RegionHandler::GroupType = Any, Bool throwIfUnknown = True) const; // Find a region.. // It is used by getRegion (which makes a clone of the object). // A zero pointer is returned if the region does not exist. virtual ImageRegion* findRegion (const String& name, RegionHandler::GroupType = Any, Bool throwIfUnknown = True) const; // Remove all regions from the maps. void clear(); String itsDefaultName; std::map itsMaps[2]; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Regions/RegionHandlerTable.cc000066400000000000000000000252641476623553700222100ustar00rootroot00000000000000//# RegionHandlerTable.cc: Class for keeping regions in a table //# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN RegionHandlerTable::RegionHandlerTable (GetCallback* callback, void* objectPtr) : itsCallback (callback), itsObjectPtr (objectPtr) {} RegionHandlerTable::RegionHandlerTable (const RegionHandlerTable& that) : RegionHandler(that), itsCallback (that.itsCallback), itsObjectPtr (that.itsObjectPtr) {} RegionHandlerTable::~RegionHandlerTable() {} RegionHandlerTable& RegionHandlerTable::operator= (const RegionHandlerTable& that) { if (this != &that) { itsCallback = that.itsCallback; itsObjectPtr = that.itsObjectPtr; } return *this; } RegionHandlerTable* RegionHandlerTable::clone() const { return new RegionHandlerTable (*this); } void RegionHandlerTable::setObjectPtr (void* objectPtr) { itsObjectPtr = objectPtr; } Bool RegionHandlerTable::canDefineRegion() const { return True; } void RegionHandlerTable::setDefaultMask (const String& regionName) { Table& tab = rwTable(); // Store the new default name (when writable). if (tab.isWritable()) { TableRecord& keys = tab.rwKeywordSet(); if (regionName.empty()) { // Only delete default mask if it exists. if (keys.isDefined ("Image_defaultmask")) { keys.removeField ("Image_defaultmask"); } } else { keys.define ("Image_defaultmask", regionName); } } } String RegionHandlerTable::getDefaultMask() const { const Table& tab = table(); const TableRecord& keys = tab.keywordSet(); Int field = keys.fieldNumber ("Image_defaultmask"); if (field < 0) { return ""; } return keys.asString(field); } Bool RegionHandlerTable::defineRegion (const String& name, const ImageRegion& region, RegionHandler::GroupType type, Bool overwrite) { Table& tab = rwTable(); if (! tab.isWritable()) { return False; } // First check if the region is already defined in "regions" or "masks". // If so, remove it if possible. Otherwise throw an exception. TableRecord& keys = tab.rwKeywordSet(); Int groupField = findRegionGroup (name, RegionHandler::Any, False); if (groupField >= 0) { if (!overwrite) { throw (AipsError ("RegionHandlerTable::defineRegion - table " + tab.tableName() + " already has a region or mask with name " + name)); } TableRecord& regs = keys.rwSubRecord(groupField); if (regs.isDefined (name)) { regs.removeField (name); } } // Okay, we can define the region now. // Define the "regions" or "masks" group when needed. String groupName = "regions"; if (type == RegionHandler::Masks) { groupName = "masks"; } if (! keys.isDefined (groupName)) { keys.defineRecord (groupName, TableRecord()); } // Now define the region in the group. keys.rwSubRecord(groupName).defineRecord (name, region.toRecord (tab.tableName())); return True; } Bool RegionHandlerTable::hasRegion (const String& name, RegionHandler::GroupType type) const { return (findRegionGroup (name, type, False) >= 0); } Bool RegionHandlerTable::renameRegion (const String& newName, const String& oldName, RegionHandler::GroupType type, Bool overwrite) { Table& tab = rwTable(); if (! tab.isWritable()) { return False; } // Check that the region exists. Int oldGroupField = findRegionGroup (oldName, type, True); // First check if the region is already defined. // Check that the region is in the same group as the original. // Remove it if overwrite is true. Otherwise throw an exception. TableRecord& keys = tab.rwKeywordSet(); Int groupField = findRegionGroup (newName, RegionHandler::Any, False); if (groupField >= 0) { if (groupField != oldGroupField) { throw (AipsError ("RegionHandlerTable::renameRegion - table " + tab.tableName() + " already has a region or mask with name " + newName + " in another group")); } if (!overwrite) { throw (AipsError ("RegionHandlerTable::renameRegion - table " + tab.tableName() + " already has a region or mask with name " + newName)); } TableRecord& regs = keys.rwSubRecord(groupField); regs.removeField (newName); } TableRecord& regs = keys.rwSubRecord(oldGroupField); ImageRegion* regPtr = getRegion (oldName, type, True); // First rename a possible mask table, which could in principle fail. // We only need to do that when it is an LCRegion. // We need to clone it to make it non-const. if (regPtr->isLCRegion()) { LCRegion* lcPtr = regPtr->asLCRegion().cloneRegion(); lcPtr->handleRename (newName, overwrite); // The region values might be changed, so redefine it. // Note that the ImageRegion constructor takes over the poiter, // so we do not need to delete lcPtr; TableRecord newValue = ImageRegion(lcPtr).toRecord (tab.tableName()); regs.defineRecord (oldName, newValue); } delete regPtr; // Rename the keyword itself. regs.renameField (newName, oldName); // Rename the default mask name if that is the renamed region. if (getDefaultMask() == oldName) { keys.define ("Image_defaultmask", newName); } return True; } Bool RegionHandlerTable::removeRegion (const String& name, RegionHandler::GroupType type, Bool throwIfUnknown) { Table& tab = rwTable(); if (! tab.isWritable()) { return False; } Int groupField = findRegionGroup (name, type, throwIfUnknown); if (groupField >= 0) { ImageRegion* regPtr = getRegion (name, type, True); // Delete a possible mask table. // We only need to do that when it is an LCRegion. // We need to clone it to make it non-const. if (regPtr->isLCRegion()) { LCRegion* lcPtr = regPtr->asLCRegion().cloneRegion(); String msg; Bool error = False; try { lcPtr->handleDelete(); } catch (std::exception& x) { error = True; msg = x.what(); } delete lcPtr; if (error) { delete regPtr; throw (AipsError("Region " + name + " could not be removed\n" + msg)); } } delete regPtr; TableRecord& keys = tab.rwKeywordSet(); keys.rwSubRecord(groupField).removeField (name); } // Clear the default mask name if that is the removed region. if (getDefaultMask() == name) { setDefaultMask (""); } return True; } Vector RegionHandlerTable::regionNames (RegionHandler::GroupType type) const { const Table& tab = table(); uInt nreg = 0; uInt nmask = 0; const RecordDesc* regs = 0; const RecordDesc* masks = 0; const TableRecord& keys = tab.keywordSet(); if (type != RegionHandler::Masks) { Int field = keys.fieldNumber ("regions"); if (field >= 0) { regs = &(keys.subRecord(field).description()); nreg = regs->nfields(); } } if (type != RegionHandler::Regions) { Int field = keys.fieldNumber ("masks"); if (field >= 0) { masks = &(keys.subRecord(field).description()); nmask = masks->nfields(); } } Vector names(nreg + nmask); uInt i; for (i=0; iname(i); } for (i=0; iname(i); } return names; } ImageRegion* RegionHandlerTable::getRegion (const String& name, RegionHandler::GroupType type, Bool throwIfUnknown) const { const Table& tab = table(); Int groupField = findRegionGroup (name, type, throwIfUnknown); if (groupField >= 0) { const TableRecord& regs = tab.keywordSet().subRecord(groupField); Int field = regs.fieldNumber (name); if (field >= 0) { return ImageRegion::fromRecord (regs.subRecord (field), tab.tableName()); } } return 0; } Int RegionHandlerTable::findRegionGroup (const String& regionName, RegionHandler::GroupType type, Bool throwIfUnknown) const { const Table& tab = table(); // Check if the region is defined in "regions" or "masks". // If so, return its groupName. const TableRecord& keys = tab.keywordSet(); if (type != RegionHandler::Masks) { Int field = keys.fieldNumber ("regions"); if (field >= 0) { const TableRecord& regs = keys.subRecord(field); if (regs.isDefined (regionName)) { return field; } } } if (type != RegionHandler::Regions) { Int field = keys.fieldNumber ("masks"); if (field >= 0) { const TableRecord& regs = keys.subRecord(field); if (regs.isDefined (regionName)) { return field; } } } if (throwIfUnknown) { String typeName = "region/mask "; if (type == RegionHandler::Regions) { typeName = "region "; } else if (type == RegionHandler::Masks) { typeName = "mask "; } throw (AipsError ("RegionHandlerTable: " + typeName + regionName + " does not exist in table " + tab.tableName())); } return -1; } ImageRegion RegionHandlerTable::makeMask (const LatticeBase& lattice, const String& name) { if (! lattice.isPaged()) { throw (AipsError ("RegionHandlerTable::makeMask - " "cannot create mask, because image is transient")); } LCPagedMask* mask = new LCPagedMask (TiledShape (lattice.shape(), lattice.niceCursorShape()), lattice.name() + '/' + name); return ImageRegion(mask); } } //# NAMESPACE CASACORE - END casacore-3.7.1/images/Regions/RegionHandlerTable.h000066400000000000000000000154731476623553700220530ustar00rootroot00000000000000//# RegionHandlerTable.h: Class for keeping regions in memory //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_REGIONHANDLERTABLE_H #define IMAGES_REGIONHANDLERTABLE_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations class Table; // // Class for keeping regions in memory. // // // // // //
      • PagedImage //
      • ImageRegion // // // Persistent regions are stored as subrecords of the table keywords // "regions" and "masks". The user can choose one of both keywords. // Keyword "masks" is meant for true image masks, i.e. telling for // each pixel if it is good or bad. Keyword "regions" is meant for // true regions in an image. //

        // This class handles defining, getting and removing such regions. // It is used by class , but it can also // be used by other code to handle regions in other tables. //

        // Another function performed by this class for PagedImage is the // definition of the default region to be used with an image. // // // // // This class has 2 purposes: //

          //
        1. This untemplated code can be factored out from the templated // Image classes. //
        2. The functions can easily be used by other code. //
        // //# //#
      • //# class RegionHandlerTable: public RegionHandler { public: // Define the signature of the function being called to get // the table object needed for the region operations. typedef Table& GetCallback (void* objectPtr, Bool writable); RegionHandlerTable (GetCallback* callback, void* objectPtr); // Copy constructor (copy semantics). RegionHandlerTable (const RegionHandlerTable&); virtual ~RegionHandlerTable(); // Assignment (copy semantics). RegionHandlerTable& operator= (const RegionHandlerTable&); // Make a copy of the object. virtual RegionHandlerTable* clone() const; // Set the object pointer for callback function. virtual void setObjectPtr (void* objectPtr); // This class can define and handle regions. virtual Bool canDefineRegion() const; // Set the default mask to the mask with the given name. // It constructs a ImageRegion object for the new default mask. // If the table is writable, the setting is persistent by writing // the name as a keyword. // If the given maskName is the empty string, the default mask is unset. virtual void setDefaultMask (const String& maskName); // Get the name of the default mask. // An empty string is returned if no default mask. virtual String getDefaultMask() const; // Define a region belonging to the table. // The group type determines if it stored as a region or mask. // If overwrite=False, an exception will be thrown if the region // already exists in the "regions" or "masks" keyword. // Otherwise the region will be removed first. //
        A False status is returned if the table is not writable virtual Bool defineRegion (const String& name, const ImageRegion& region, RegionHandler::GroupType, Bool overwrite = False); // Does the table have a region with the given name? virtual Bool hasRegion (const String& name, RegionHandler::GroupType = RegionHandler::Any) const; // Get a region belonging to the table. // A zero pointer is returned if the region does not exist. // The caller has to delete the ImageRegion object created. //
        No exception is thrown if the region does not exist. virtual ImageRegion* getRegion (const String& name, RegionHandler::GroupType = Any, Bool throwIfUnknown = True) const; // Rename a region. // If a region with the new name already exists, it is deleted or // an exception is thrown (depending on overwrite). // The region name is looked up in the given group(s). //
        An exception is thrown if the old region name does not exist. virtual Bool renameRegion (const String& newName, const String& oldName, RegionHandler::GroupType = Any, Bool overwrite = False); // Remove a region belonging to the table. //
        Optionally an exception is thrown if the region does not exist. //
        A False status is returned if the table is not writable virtual Bool removeRegion (const String& name, RegionHandler::GroupType = Any, Bool throwIfUnknown = True); // Get the names of all regions/masks. virtual Vector regionNames (RegionHandler::GroupType = Any) const; // Make a mask (an LCPagedMask) for a stored lattice (a PagedImage). // It creates it with the shape and tile shape of the lattice. virtual ImageRegion makeMask (const LatticeBase& lattice, const String& name); private: // Get the table object. // Table& rwTable() { return itsCallback (itsObjectPtr, True); } const Table& table() const { return itsCallback (const_cast(this)->itsObjectPtr, False); } // // Find field number of the region group to which a region belongs // (i.e. the field number of the "regions" or "masks" field). // <0 is returned if the region does not exist. //
        Optionally an exception is thrown if the region does not exist. virtual Int findRegionGroup (const String& regionName, RegionHandler::GroupType = Any, Bool throwIfUnknown = True) const; GetCallback* itsCallback; void* itsObjectPtr; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Regions/RegionManager.cc000066400000000000000000000737111476623553700212350ustar00rootroot00000000000000//# RegionManager.cc: framework independent class that provides //# functionality to tool of same name //# Copyright (C) 2007 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# name space casa begins RegionManager::RegionManager() : itsCSys(nullptr) { itsLog= new LogIO(); } RegionManager::RegionManager(const CoordinateSystem& csys) : itsCSys(new CoordinateSystem(csys)) { itsLog= new LogIO(); //setcoordsys(csys); } RegionManager::~RegionManager() { if(itsLog !=0) delete itsLog; } /************************************************************* ** An assortment of little helper and set/get methods ** *************************************************************/ // Private method String RegionManager::absreltype(const Int absrelval){ *itsLog << LogOrigin("RegionManager", "absreltype"); if(absrelval == RegionType::Abs) return String("abs"); else if(absrelval == RegionType::RelRef) return String("relref"); else if(absrelval == RegionType::RelCen) return String("relcen"); // else if(absrelval == RegionType::RelDir) //return String("reldir"); *itsLog << LogIO::WARN << "absrelvalue " << absrelval << " is not valid" << LogIO::POST; return String("Unknown"); } void RegionManager::setcoordsys(const CoordinateSystem& csys){ itsCSys.reset( new CoordinateSystem(csys) ); } const CoordinateSystem& RegionManager::getcoordsys() const{ if (!itsCSys) { throw(AipsError("CoordinateSystem not set in RegionManager tool")); } return *itsCSys; } Bool RegionManager::isPixelRegion(const ImageRegion& reg ){ return reg.isLCRegion(); } Bool RegionManager::isWorldRegion(const ImageRegion& reg ){ return reg.isWCRegion(); } /************************************************************* ** Make BOX region routines ** *************************************************************/ Record* RegionManager::box(const Vector& blc, const Vector& trc, const Vector& inc, const String& absrel, const Bool frac, const String& comment){ *itsLog << LogOrigin("RegionManager", "box"); /* if(blc.nelements() != trc.nelements()) throw(AipsError("blc and trc do not have the shape")); if(inc.nelements() != trc.nelements()) throw(AipsError("inc and trc do not have the shape")); */ RegionType::AbsRelType leType=RegionType::absRelTypeFromString(absrel); LCSlicer muiSlicer(blc, trc, inc, frac, leType); muiSlicer.setComment( comment ); Record *leRecord= new Record(); leRecord->assign(muiSlicer.toRecord(String(""))); return leRecord; } Record* RegionManager::box(const Vector& blc, const Vector& trc, const Vector& shape, const String& comment){ ThrowIf(blc.nelements() != trc.nelements(), "blc and trc do not have the same shape"); IPosition latShape(shape); LCBox lcbox(blc, trc, latShape); // Note: LCBox is a LCRegionfixed -> LCRegionSingle -> LCRegion Record *leRecord= new Record(); leRecord->assign(lcbox.toRecord(String(""))); leRecord->define("comment", comment); return leRecord; } ImageRegion* RegionManager::wbox(const Vector& blc, const Vector& trc, const Vector& pixelaxes, const CoordinateSystem& csys, const String& absrel){ *itsLog << LogOrigin("RegionManager", "wbox"); RegionType::AbsRelType leType=RegionType::absRelTypeFromString(absrel); Vector absRel(blc.nelements(), leType); WCBox worldbox; if(pixelaxes.nelements() > 0 && pixelaxes[0] <0){ worldbox=WCBox(blc, trc, csys, absRel); } else{ worldbox=WCBox(blc,trc,IPosition(pixelaxes),csys,absRel); } ImageRegion *leRegion = new ImageRegion(worldbox); return leRegion; } Record* RegionManager::wbox(const Vector& blc, const Vector& trc, const Vector& pixelaxes, const CoordinateSystem& csys, const String& absrel, const String& comment){ setcoordsys(csys); return wbox(blc, trc, pixelaxes, absrel, comment); } Record* RegionManager::wbox(const Vector& blc, const Vector& trc, const Vector& pixelaxes, const String& absrel, const String& comment){ if(!itsCSys) { ThrowCc("CoordinateSystem has not been set"); } ImageRegion * leImReg=wbox(blc, trc, pixelaxes, *itsCSys, absrel); Record *leRecord= new Record(); leRecord->assign(leImReg->toRecord(String(""))); delete leImReg; leRecord->define("comment", comment); return leRecord; } void RegionManager::toQuantity(Quantity& out, const String& in){ String leString=in; QuantumHolder qh; if(leString.contains("pix")){ leString=leString.before("pix"); Double value=atof(leString.chars()); out=Quantity(value, "pix"); } else{ String error; if(!qh.fromString(error, leString)){ ostringstream oss; String err="Error " + error + " In converting quantity " + leString; throw( AipsError(err)); } out=qh.asQuantity(); } } Record* RegionManager::wbox(const Vector& blc, const Vector& trc, const Vector& pixelaxes, const String& absrel, const String& comment){ ThrowIf(! itsCSys, "Coordinate system has not been set"); Vector losBlc(blc.nelements()); Vector losTrc(trc.nelements()); QuantumHolder qh; //Stokes is not known in Quantity Int stInd = itsCSys->findCoordinate(Coordinate::STOKES); StokesCoordinate stCoord(Vector(1, Stokes::I)); Int wSt=-1; if(stInd>=0){ wSt= (itsCSys->worldAxes(stInd))[0]; stCoord=itsCSys->stokesCoordinate(stInd); } for (Int k=0; k < blc.shape()(0); ++k){ if(k != wSt){ toQuantity(losBlc[k], blc[k]); toQuantity(losTrc[k], trc[k]); } else{ //Stokes is not known in Quantity...have to convert them to pix Int stpix=-1; if(blc[k].contains("pix")) toQuantity(losBlc[k], blc[k]); else if(stCoord.toPixel(stpix, Stokes::type(blc[k]))) losBlc[k]=Quantity(stpix, "pix"); stpix=-1; if(trc[k].contains("pix")) toQuantity(losTrc[k], trc[k]); else if(stCoord.toPixel(stpix, Stokes::type(trc[k]))) losTrc[k]=Quantity(stpix, "pix"); } } return wbox(losBlc, losTrc, pixelaxes, absrel, comment); } Record* RegionManager::wbox(const Vector& blc, const Vector& trc, const Vector& pixelaxes, const CoordinateSystem& csys, const String& absrel, const String& comment){ setcoordsys(csys); return wbox(blc, trc, pixelaxes, absrel, comment); } /************************************************************* ** Make POLYGON routines ** *************************************************************/ ImageRegion* RegionManager::wpolygon(const Vector& x, const Vector& y, const Vector& pixelaxes, const CoordinateSystem& csys, const String& absrel){ *itsLog << LogOrigin("RegionManager", "wpolygon"); Vector pixax=pixelaxes; if(pixax.nelements() > 0 && pixax[0] <0){ pixax.resize(2); pixax(0)=0; pixax(1)=1; } if(y.nelements() != x.nelements()) throw(AipsError("Y values of vertices not same length as the X values")); //Now lets convert everything to one unit in this instance the the pix unit uInt nvertices=y.nelements(); Vector leX(nvertices); Vector leY(nvertices); String xUnit=csys.worldAxisUnits()[pixax[0]]; String yUnit=csys.worldAxisUnits()[pixax[1]]; // Vector worldaxes(2); // worldaxes(0)=csys.pixelAxisToWorldAxis(pixax[0]); // worldaxes(1)=csys.pixelAxisToWorldAxis(pixax[1]); const DirectionCoordinate& dirCoor=csys.directionCoordinate(csys.findCoordinate(Coordinate::DIRECTION)); Vector world = csys.referenceValue(); Vector pixel(world.nelements()); for (uInt k=0; k < nvertices; ++k){ if(x[k].getUnit().contains("pix") && y[k].getUnit().contains("pix") ){ Vector lepix(2); lepix[0]=x[k].getValue(); lepix[1]=y[k].getValue(); Vector lemonde(2); dirCoor.toWorld(lemonde, lepix); leX[k]=lemonde[0]; leY[k]=lemonde[1]; } else if((x[k].getUnit().contains("pix") && !y[k].getUnit().contains("pix")) || (!x[k].getUnit().contains("pix") && y[k].getUnit().contains("pix"))){ throw(AipsError("Cannot handle cross units pix and non-pix together")); } else{ leX[k]=x[k].getValue(xUnit); leY[k]=y[k].getValue(yUnit); /* csys.toPixel(pixel, world); leX[k]=pixel[pixax[0]]; leY[k]=pixel[pixax[1]]; */ } } Quantum > elX(leX, xUnit); Quantum > elY(leY, yUnit); RegionType::AbsRelType leType=RegionType::absRelTypeFromString(absrel); WCPolygon worldpoly(elX,elY,IPosition(pixax),csys, leType); ImageRegion *leRegion = new ImageRegion(worldpoly); return leRegion; } ImageRegion* RegionManager::wpolygon(const Vector& x, const Vector& y, const Vector& pixelaxes, const String& absrel){ *itsLog << LogOrigin("RegionManager", "wpolygon"); if(itsCSys){ return wpolygon(x, y, pixelaxes, *itsCSys, absrel); } else{ throw(AipsError("CoordinateSystem not set in RegionManager tool")); } return 0; } ImageRegion* RegionManager::wellipse( const Quantity& xc, const Quantity& yc, const Quantity& a, const Quantity& b, const Quantity& pa, const uInt pixelAxis0, const uInt pixelAxis1, const CoordinateSystem& csys, const String& absrel ) { RegionType::AbsRelType leType=RegionType::absRelTypeFromString(absrel); WCEllipsoid wellipse(xc, yc, a, b, pa, pixelAxis0, pixelAxis1, csys, leType); return new ImageRegion(wellipse); } ImageRegion* RegionManager::wellipse( const Quantity& xc, const Quantity& yc, const Quantity& a, const Quantity& b, const Quantity& pa, const uInt pixelAxis0, const uInt pixelAxis1, const String& absrel ) const { *itsLog << LogOrigin("RegionManager", __FUNCTION__); if (!itsCSys) { throw(AipsError("CoordinateSystem not set in RegionManager tool")); } return wellipse(xc, yc, a, b, pa, pixelAxis0, pixelAxis1, *itsCSys, absrel); } ImageRegion* RegionManager::wsphere( const Vector& center, const Quantity& radius, const Vector& pixelAxes, const CoordinateSystem& csys, const String& absrel ) { RegionType::AbsRelType leType=RegionType::absRelTypeFromString(absrel); WCEllipsoid wsphere(center, radius, pixelAxes, csys, leType); return new ImageRegion(wsphere); } ImageRegion* RegionManager::wsphere( const Vector& center, const Quantity& radius, const Vector& pixelAxes, const String& absrel ) const { *itsLog << LogOrigin("RegionManager", __FUNCTION__); if(!itsCSys){ throw(AipsError("CoordinateSystem not set in RegionManager tool")); } return wsphere(center, radius, pixelAxes, *itsCSys, absrel); } ImageRegion* RegionManager::wellipsoid( const Vector& center, const Vector& radii, const Vector& pixelAxes, const CoordinateSystem& csys, const String& absrel ) { RegionType::AbsRelType leType=RegionType::absRelTypeFromString(absrel); WCEllipsoid ellipsoid(center, radii, pixelAxes, csys, leType); return new ImageRegion(ellipsoid); } ImageRegion* RegionManager::wellipsoid( const Vector& center, const Vector& radii, const Vector& pixelAxes, const String& absrel ) const { *itsLog << LogOrigin("RegionManager", __FUNCTION__); if(!itsCSys){ throw(AipsError("CoordinateSystem not set in RegionManager tool")); } return wellipsoid(center, radii, pixelAxes, *itsCSys, absrel); } ImageRegion* RegionManager::wshell( const Vector& center, const Vector& innerRadii, const Vector& outerRadii, const Vector& pixelAxes, const CoordinateSystem& csys, const String& absrel ) { for (uInt i=0; i outerRadii[i].getValue(innerRadii[i].getUnit()) ) { throw AipsError( "RegionManager::" + String(__FUNCTION__) + ": For radius " + String::toString(i) + " inner radius " + String::toString(innerRadii[i]) + " is greater than outer radius " + String::toString(outerRadii[i]) ); } } RegionType::AbsRelType leType=RegionType::absRelTypeFromString(absrel); WCEllipsoid inner(center, innerRadii, pixelAxes, csys, leType); WCEllipsoid outer(center, outerRadii, pixelAxes, csys, leType); WCDifference shell(outer, inner); return new ImageRegion(shell); } ImageRegion* RegionManager::wshell( const Vector& center, const Vector& innerRadii, const Vector& outerRadii, const Vector& pixelAxes, const String& absrel ) const { *itsLog << LogOrigin("RegionManager", __FUNCTION__); if(!itsCSys){ throw(AipsError("CoordinateSystem not set in RegionManager tool")); } return wshell(center, innerRadii, outerRadii, pixelAxes, *itsCSys, absrel); } ImageRegion* RegionManager::wmask(const String& command) { WCLELMask wmask(command); return new ImageRegion(wmask); } /************************************************************* ** UNION routines ** *************************************************************/ ImageRegion* RegionManager::doUnion(const WCRegion& reg1, const WCRegion& reg2) { *itsLog << LogOrigin("RegionManager", String(__FUNCTION__) + "_1"); ImageRegion imageReg1(reg1); ImageRegion imageReg2(reg2); return doUnion(imageReg1, imageReg2); } ImageRegion* RegionManager::doUnion(const PtrBlock& regions) { *itsLog << LogOrigin("RegionManager", String(__FUNCTION__) + "_2"); WCUnion leUnion(False, regions); ImageRegion* leReturn= new ImageRegion(leUnion); return leReturn; } ImageRegion* RegionManager::doUnion(const ImageRegion& reg1, const ImageRegion& reg2) const { *itsLog << LogOrigin("RegionManager", String(__FUNCTION__) + "_3"); *itsLog << LogIO::DEBUGGING << "reg1 type " << reg1.isWCRegion() << " " << reg1.isLCRegion() << " "<< reg1.isLCSlicer() << "\nreg2 type " << reg2.isWCRegion() << " " << reg2.isLCRegion() << " "<< reg2.isLCSlicer() << LogIO::POST; WCUnion leUnion(reg1, reg2); ImageRegion* leReturn= new ImageRegion(leUnion); return leReturn; } /************************************************************* ** INTERSECTION routines ** *************************************************************/ ImageRegion* RegionManager::doIntersection(const WCRegion& reg1, const WCRegion& reg2){ ImageRegion imageReg1(reg1); ImageRegion imageReg2(reg2); return doIntersection(imageReg1, imageReg2); } ImageRegion* RegionManager::doIntersection( const PtrBlock& regions) { WCIntersection leIntersect(False, regions); ImageRegion* leReturn= new ImageRegion(leIntersect); return leReturn; } ImageRegion* RegionManager::doIntersection(const ImageRegion& reg1, const ImageRegion& reg2){ *itsLog << LogOrigin("RegionManager", "doIntersection"); *itsLog << LogIO::DEBUGGING << "reg1 type " << reg1.isWCRegion() << " " << reg1.isLCRegion() << " "<< reg1.isLCSlicer() << "\nreg2 type " << reg2.isWCRegion() << " " << reg2.isLCRegion() << " "<< reg2.isLCSlicer() << LogIO::POST; WCIntersection leIntersection(reg1, reg2); ImageRegion* leReturn= new ImageRegion(leIntersection); return leReturn; } /************************************************************* ** COMPLEMENT routines ** *************************************************************/ ImageRegion* RegionManager::doComplement(const WCRegion& reg){ *itsLog << LogOrigin("RegionManager", "doComplement"); ImageRegion imageReg1(reg); return doComplement(imageReg1); } ImageRegion* RegionManager::doComplement(const PtrBlock& regions){ *itsLog << LogOrigin("RegionManager", "doComplement"); WCComplement leComplement(False, regions); ImageRegion* leReturn= new ImageRegion(leComplement); return leReturn; } ImageRegion* RegionManager::doComplement(const ImageRegion& reg1){ *itsLog << LogOrigin("RegionManager", "doComplement"); *itsLog << LogIO::DEBUGGING << "reg1 type " << reg1.isWCRegion() << " " << reg1.isLCRegion() << " "<< reg1.isLCSlicer() << LogIO::POST; WCComplement leComplement(reg1); ImageRegion* leReturn = new ImageRegion(leComplement); return leReturn; } /************************************************************* ** DIFFERENCE routines ** ** Note, that the we could add support for doing the ** ** difference of multiple regions since the support ** ** exists underneath. ** *************************************************************/ ImageRegion* RegionManager::doDifference(const WCRegion& reg1, const WCRegion& reg2){ ImageRegion imageReg1(reg1); ImageRegion imageReg2(reg2); return doDifference(imageReg1, imageReg2); } ImageRegion* RegionManager::doDifference( const PtrBlock& regions) { WCDifference leDiff(False, regions); ImageRegion* leReturn = new ImageRegion(leDiff); return leReturn; } ImageRegion* RegionManager::doDifference(const ImageRegion& reg1, const ImageRegion& reg2){ *itsLog << LogOrigin("RegionManager", "doDifference"); *itsLog << LogIO::DEBUGGING << "reg1 type " << reg1.isWCRegion() << " " << reg1.isLCRegion() << " "<< reg1.isLCSlicer() << "\nreg2 type " << reg2.isWCRegion() << " " << reg2.isLCRegion() << " "<< reg2.isLCSlicer() << LogIO::POST; WCDifference leDiff(reg1, reg2); ImageRegion* leReturn= new ImageRegion(leDiff); return leReturn; } /************************************************************* ** CONCAT a box to a region(s) routines ** *************************************************************/ ImageRegion* RegionManager::doConcatenation( const WCRegion& region, const WCBox& box ) { PtrBlock imageRegions(1); imageRegions[0]= new ImageRegion(region); TableRecord recordBox = box.toRecord(""); return doConcatenation(imageRegions, recordBox); } ImageRegion* doConcatenation( const PtrBlock& regions, const WCBox& box) { WCConcatenation leConcat(False, regions, box); ImageRegion* leReturn= new ImageRegion(leConcat); return leReturn; } ImageRegion* RegionManager::doConcatenation( const PtrBlock& regions, const TableRecord& box ) { *itsLog << LogOrigin("RegionManager", "doConcatenation"); for ( uInt i=0; regions.nelements(); i++ ) *itsLog << LogIO::DEBUGGING << "\nregion " << i << "'s type (WCRegion/LCRegion/LCSLicer): " << regions[i]->isWCRegion() << "/" << regions[i]->isLCRegion() << "/" << regions[i]->isLCSlicer() << LogIO::POST; const WCBox *lebox = WCBox::fromRecord( box, "" ); WCConcatenation leConcatenation(regions, *lebox ); ImageRegion* leReturn= new ImageRegion(leConcatenation); return leReturn; } ImageRegion* RegionManager::doConcatenation( const Record& regions, const TableRecord& box ) { *itsLog << LogOrigin("RegionManager", "doConcatenation"); // Once we convert the region Record to PtrBlock of // ImageRegions then we just call one of the other // doConcatenation routines. if ( regions.nfields() < 1 ) throw(AipsError(String("No regions have been supplied to concatenation" ) ) ); PtrBlock imageRegions(regions.nfields()); ImageRegion* reg=0; TableRecord tblRec; for( uInt i=0; i < (regions.nfields()); i++ ) { tblRec.assign(regions.asRecord(casacore::RecordFieldId(0))); reg=ImageRegion::fromRecord(tblRec, ""); imageRegions[i]=reg; } // Convert the box table record to a WCBox const WCBox *lebox = WCBox::fromRecord( box, "" ); WCConcatenation leConcatenation(imageRegions, *lebox ); ImageRegion* leReturn= new ImageRegion(leConcatenation); return leReturn; } /************************************************************* ** EXTEND region routines ** *************************************************************/ /************************************************************* ** Regions from file/table routines ** *************************************************************/ Record* RegionManager::readImageFile( String filepath, String regionname ) { // open the file AipsIO ios( filepath, ByteIO::Old ); // Read the file contents and convert it too an ImageRegion. // The commented out lines really should be used, but when // uncommented we get exceptions thrown. For some reason // AipsIO finds a type of TableRecord, then a type of RecordDesc // this causes Exceptions to be thrown because the type we give // and the type found don't match. This could be due to the way // the file is saved or some other quirk in AipsIO. TableRecord leTblRec; ImageRegion *leImReg; //ios.getstart( "TableRecord" ); ios >> leTblRec; //ios.getend(); if ( regionname.length() > 0 ) leImReg = ImageRegion::fromRecord( leTblRec, regionname ); else // TODO strip path part off and use just the tail of // the filename. leImReg = ImageRegion::fromRecord( leTblRec, filepath ); //delete leTblRec; // Convert the ImageRegion to a Record Record * leRecord = new Record(); leRecord->assign( leImReg->toRecord(String("")) ); delete leImReg; String comment = "Created from file: " + filepath; leRecord->define( "comment", comment ); return leRecord; } Bool RegionManager::writeImageFile(const String& file, const String& regionname, const Record& regionRecord){ TableRecord regionTblRecord(regionRecord); ImageRegion *imageReg=ImageRegion::fromRecord(regionTblRecord, ""); try{ AipsIO oos(file, ByteIO::NewNoReplace); oos << imageReg->toRecord(regionname); } catch(...) { throw(AipsError(String("Could not create the region file.\n Please check pathname and directory \n")+ String(" permissions and be sure the file \n does not already exist."))); } delete imageReg; return True; } String RegionManager::imageRegionToTable(const String& tabName, const ImageRegion& imreg, const String& regName, Bool asmask){ tab_p=Table(tabName, Table::Update); RegionHandlerTable regtab (getTable, this); String newName=regName; Bool retval=False; if(regtab.hasRegion(newName) || newName=="") newName=regtab.makeUniqueRegionName(regName, 0); if(asmask){ try{ PagedImage myimage(tabName); SubImage subim(myimage, imreg, True); ImageRegion outreg=myimage.makeMask(newName, False, False); LCRegion& mask=outreg.asMask(); LatticeRegion latReg=imreg.toLatticeRegion(myimage.coordinates(), myimage.shape()); SubLattice subMask(mask, latReg, True); subMask.set(True); myimage.defineRegion (newName, mask, RegionHandler::Masks); retval=myimage.hasRegion(newName); } catch(std::exception& x){ throw(AipsError("Could not write mask in image "+tabName+" because "+x.what())); } catch(...){ throw(AipsError("Could not write mask in image "+tabName)); } } else{ retval=regtab.defineRegion (newName, imreg, RegionHandler::Regions); } tab_p.relinquishAutoLocks(); tab_p=Table(); if(retval) return newName; else return String(""); } String RegionManager::recordToTable(const String& tabName, const RecordInterface& rec, const String& regName, Bool asmask){ if(!Table::isWritable(tabName)){ *itsLog << LogIO::WARN << tabName << " is not valid or writeable table" << LogIO::POST; return String(""); } TableRecord lerec(rec); ImageRegion* imreg=ImageRegion::fromRecord(lerec, "") ; String newName=imageRegionToTable(tabName, *imreg, regName, asmask); delete imreg; return newName; } Record* RegionManager::tableToRecord(const String& tabName, const String& regname){ if(!Table::isReadable(tabName)){ *itsLog << LogIO::WARN << tabName << " is not a valid or readable table" << LogIO::POST; return 0; } tab_p=Table(tabName, Table::Old); RegionHandlerTable regtab (getTable, this); if(!regtab.hasRegion(regname)){ *itsLog << LogIO::WARN << tabName << " does not have region " << regname << LogIO::POST; tab_p=Table(); return 0; } ImageRegion* imreg=regtab.getRegion(regname, RegionHandler::Any, False); Record * leRecord = new Record(); leRecord->assign( imreg->toRecord(String("")) ); delete imreg; tab_p.relinquishAutoLocks(); tab_p=Table(); return leRecord; } Vector RegionManager::namesInTable(const String& tabName){ Vector retval; if(!Table::isReadable(tabName)){ *itsLog << LogIO::WARN << tabName << " is not a valid or readable table" << LogIO::POST; return retval; } tab_p=Table(tabName, Table::Old); RegionHandlerTable regtab (getTable, this); retval=regtab.regionNames(); tab_p.relinquishAutoLocks(); tab_p=Table(); return retval; } Bool RegionManager::removeRegionInTable(const String& tabName, const String& regName){ Bool retval; if(!Table::isWritable(tabName)){ *itsLog << LogIO::WARN << tabName << " is not a valid or writable table" << LogIO::POST; return False; } if(regName==""){ *itsLog << LogIO::WARN << "No region name given to remove...nothing done" << LogIO::POST; return False; } tab_p=Table(tabName, Table::Update); RegionHandlerTable regtab (getTable, this); if(!regtab.hasRegion(regName)){ *itsLog << LogIO::WARN << tabName << " does not have region " << regName << LogIO::POST; tab_p.relinquishAutoLocks(); tab_p=Table(); return False; } retval=regtab.removeRegion(regName, RegionHandler::Any, False); tab_p.relinquishAutoLocks(); tab_p=Table(); return retval; } Table& RegionManager::getTable(void* ptr, Bool) { RegionManager* rg = static_cast(ptr); return rg->tab_p; } } // end of casa namespace casacore-3.7.1/images/Regions/RegionManager.h000066400000000000000000000236601476623553700210750ustar00rootroot00000000000000//# RegionManager.h: framework independent class that provides //# functionality to tool of same name //# Copyright (C) 2007 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_REGIONMANAGER_H #define IMAGES_REGIONMANAGER_H #include #include #include #include #include #include namespace casacore { /** * image component class * * This is a casa based class to provide the funtionality to the * RegionManager Tool * * @author * @version **/ class LogIO; class String; class Record; class WCRegion; class WCBox; template class PtrBlock; class ImageRegion; class RegionManager { public: //blank constructor RegionManager(); RegionManager(const CoordinateSystem& csys); virtual ~RegionManager(); String absreltype(const Int absrelval=0); //Some little but useful tidbits. static Bool isPixelRegion(const ImageRegion& reg); static Bool isWorldRegion(const ImageRegion& reg); void setcoordsys(const CoordinateSystem& csys); const CoordinateSystem& getcoordsys() const ; //LCSlicer box Record* box(const Vector& blc, const Vector& trc, const Vector& inc, const String& absrel, const Bool frac, const String& comment=""); //LCBox box static Record* box(const Vector& blc, const Vector& trc, const Vector& shape, const String& comment=""); Record* wbox(const Vector& blc, const Vector& trc, const Vector& pixelaxes, const CoordinateSystem& csys, const String& absrel, const String& comment); Record* wbox(const Vector& blc, const Vector& trc, const Vector& pixelaxes, const CoordinateSystem& csys, const String& absrel, const String& comment); Record* wbox(const Vector& blc, const Vector& trc, const Vector& pixelaxes, const String& absrel, const String& comment); Record* wbox(const Vector& blc, const Vector& trc, const Vector& pixelaxes, const String& absrel, const String& comment); ImageRegion* wbox(const Vector& blc, const Vector& trc, const Vector& pixelaxes, const CoordinateSystem& csys, const String& absrel="abs" ); //Wpolygon with coordsys and if pixelaxes[0] is -1 then its assumed //to be 0,1,... ImageRegion* wpolygon(const Vector& x, const Vector& y, const Vector& pixelaxes, const CoordinateSystem& csys, const String& absrel); //wpolygon version without csys...throws an exception if //setcoordsys is not run ImageRegion* wpolygon(const Vector& x, const Vector& y, const Vector& pixelaxes, const String& absrel); static ImageRegion* wellipse( const Quantity& xc, const Quantity& yc, const Quantity& a, const Quantity& b, const Quantity& pa, const uInt pixelAxis0, const uInt pixelAxis1, const CoordinateSystem& csys, const String& absrel ); //wellipse version without csys...throws an exception if //setcoordsys is not run ImageRegion* wellipse( const Quantity& xc, const Quantity& yc, const Quantity& a, const Quantity& b, const Quantity& pa, const uInt pixelAxis0, const uInt pixelAxis1, const String& absrel ) const; static ImageRegion* wsphere( const Vector& center, const Quantity& radius, const Vector& pixelaxes, const CoordinateSystem& csys, const String& absrel ); //wsphere version without csys...throws an exception if //setcoordsys is not run ImageRegion* wsphere( const Vector& center, const Quantity& radius, const Vector& pixelaxes, const String& absrel ) const; static ImageRegion* wellipsoid( const Vector& center, const Vector& radii, const Vector& pixelaxes, const CoordinateSystem& csys, const String& absrel ); ImageRegion* wellipsoid( const Vector& center, const Vector& radii, const Vector& pixelaxes, const String& absrel ) const; static ImageRegion* wshell( const Vector& center, const Vector& innerRadii, const Vector& outerRadii, const Vector& pixelaxes, const CoordinateSystem& csys, const String& absrel ); ImageRegion* wshell( const Vector& center, const Vector& innerRadii, const Vector& outerRadii, const Vector& pixelaxes, const String& absrel ) const; static ImageRegion* wmask(const String& command); /************************************************************** ** Routines for combining regions ** ** ** ** Note: Many of the WCXxx classes which are used to do the ** ** work can take multiple regions at once, why not ** ** accept a ptr block of Image Regions then? ** **************************************************************/ //Various versions of creating a complement region ImageRegion* doComplement(const WCRegion& reg1); ImageRegion* doComplement(const PtrBlock& reg1); ImageRegion* doComplement(const ImageRegion& reg1); //Various versions of concatenating a region onto another. ImageRegion* doConcatenation(const WCRegion& region, const WCBox& box); ImageRegion* doconcatenation(const PtrBlock& regions, const WCBox& box); ImageRegion* doConcatenation(const PtrBlock& regions, const TableRecord& box); ImageRegion* doConcatenation(const Record& regions, const TableRecord& box); //Various versions of handling the difference of regions ImageRegion* doDifference(const WCRegion& reg1, const WCRegion& reg2); ImageRegion* doDifference(const PtrBlock& reg1); ImageRegion* doDifference(const ImageRegion& reg1, const ImageRegion& reg2); //Different versions of intersecting regions ImageRegion* doIntersection(const WCRegion& reg1, const WCRegion& reg2); ImageRegion* doIntersection(const PtrBlock& reg1); ImageRegion* doIntersection(const ImageRegion& reg1, const ImageRegion& reg2); //Different versions of unioning regions ImageRegion* doUnion(const WCRegion& reg1, const WCRegion& reg2); ImageRegion* doUnion(const PtrBlock& reg1); ImageRegion* doUnion(const ImageRegion& reg1, const ImageRegion& reg2) const; /************************************************************** ** Routines for reading/writing regions ** **************************************************************/ //Reading of a file containing an ImageRegion in the AipsIO format dump static Record* readImageFile( String filename, String regionname ); //Writing a file of the AipsIO dump of the record representation of the region static Bool writeImageFile(const String& file, const String& regionname, const Record& regionRecord); //save region into a table (image, blank table or any other such) String imageRegionToTable(const String& tabName, const ImageRegion& imreg, const String& regName, Bool asmask=False); String recordToTable(const String& tabName, const RecordInterface& rec, const String& regName="", Bool asmask=False); //recover region from table Record* tableToRecord(const String& tabName, const String& regname); //names of regions in table Vector namesInTable(const String& tabName); //Remove a region from table...refuse is regionname is "" Bool removeRegionInTable(const String& tabName, const String& regName); protected: inline LogIO* _getLog() const { return itsLog; } private: LogIO *itsLog; std::unique_ptr itsCSys; // Function to return the internal Table object to the RegionHandler. static Table& getTable (void* ptr, Bool writable); //Convert a string to Quantity void toQuantity(Quantity& out, const String& in); Table tab_p; }; } // casa namespace #endif casacore-3.7.1/images/Regions/WCBox.cc000066400000000000000000000545341476623553700175030ustar00rootroot00000000000000//# WCBox.cc: Class to define a world coordinate box region of interest in an image //# Copyright (C) 1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN WCBox::WCBox() // //Default constructor // : itsNull(True) { unitInit(); } WCBox::WCBox(const Vector >& blc, const Vector >& trc, const CoordinateSystem& cSys, const Vector& absRel) // // Constructor from Quantities. blc and trc are in the // order of the pixel axes of CS. Currently relative // world coordinates are not handled, only relative pixel // coordinates. // : itsBlc(blc.copy()), itsTrc(trc.copy()), itsCSys(cSys), itsAbsRel(absRel.copy()), itsNull(False) { AlwaysAssert (itsCSys.nWorldAxes() > 0, AipsError); AlwaysAssert (itsCSys.nPixelAxes() > 0, AipsError); // String msg; if (itsBlc.nelements() != itsTrc.nelements()) { msg = String("WCBox - you gave more values for the blc than the trc"); throw (AipsError (msg)); } if (itsAbsRel.nelements() != 0 && itsAbsRel.nelements() != itsBlc.nelements()) { msg = String("WCBox - you must specify as many values for absRel as blc/trc"); throw (AipsError (msg)); } if (itsBlc.nelements() > itsCSys.nPixelAxes()) { msg = String("WCBox - you gave more values for the blc than ") + String("there are axes in the CoordinateSystem"); throw (AipsError (msg)); } if (itsTrc.nelements() > itsCSys.nPixelAxes()) { msg = String("WCBox - you gave more values for the trc than ") + String("there are axes in the CoordinateSystem"); throw (AipsError (msg)); } // Set pixelAxes and absRel (defaults to abs) vectors const uInt nAxes = itsBlc.nelements(); uInt i; if (nAxes > 0) { itsPixelAxes.resize(nAxes); for (i=0; i >& blc, const Vector >& trc, const IPosition& pixelAxes, const CoordinateSystem& cSys, const Vector& absRel) // // Constructor from Quantities with specification of // axes. Currently relative world coordinates are not handled, // only relative pixel coordinates. // : itsBlc(blc.copy()), itsTrc(trc.copy()), itsPixelAxes(pixelAxes), itsCSys(cSys), itsAbsRel(absRel.copy()), itsNull(False) { AlwaysAssert (itsCSys.nWorldAxes() > 0, AipsError); AlwaysAssert (itsCSys.nPixelAxes() > 0, AipsError); // String msg; if (itsBlc.nelements() != itsTrc.nelements()) { msg = String("WCBox - you must specify as many blc as trc values"); throw (AipsError (msg)); } if (itsBlc.nelements() != itsPixelAxes.nelements()) { msg = String("WCBox - you must specify as many blc/trc values as pixel axes"); throw (AipsError (msg)); } if (itsAbsRel.nelements() != 0 && itsAbsRel.nelements() != itsBlc.nelements()) { msg = String("WCBox - you must specify as many values for absRel as blc/trc"); throw (AipsError (msg)); } if (itsPixelAxes.nelements() > itsCSys.nPixelAxes()) { msg = String("WCBox - you gave more pixel axes than ") + String("there are axes in the CoordinateSystem"); throw (AipsError (msg)); } // If the absRel vector is null, it defaults to absolute const uInt nAxes = itsPixelAxes.nelements(); uInt i; if (itsAbsRel.nelements() == 0 && nAxes > 0) { itsAbsRel.resize(nAxes); for (i=0; i 0, AipsError); AlwaysAssert (itsCSys.nPixelAxes() > 0, AipsError); String msg; // Get bounding box Slicer boundingBox = region.boundingBox(); IPosition start = boundingBox.start(); IPosition end = boundingBox.end(); if (start.nelements() != itsCSys.nPixelAxes() || end.nelements() != itsCSys.nPixelAxes()) { msg = String("WCBox - the dimensions of the LCRegion bounding box must ") + String("be the same as the number of pixel axes in the CoordinateSystem"); throw (AipsError (msg)); } unitInit(); // Create vectors for conversions Vector wBlc(itsCSys.nWorldAxes()); Vector wTrc(itsCSys.nWorldAxes()); Vector pixel(itsCSys.nPixelAxes()); // Convert corners. The conversion arranges the world values // in the order corresponding to the pixel axes. uInt i; for (i=0; i(wBlc(i), itsCSys.worldAxisUnits()(worldAxis)); itsTrc(i) = Quantum(wTrc(i), itsCSys.worldAxisUnits()(worldAxis)); } else { throw (AipsError ("WCBox - missing world axis in Coordinate System")); } // itsPixelAxes(i) = i; itsAbsRel(i) = RegionType::Abs; } // Create the axis descriptions. for (i=0; i > blc(nAxes); Vector > trc(nAxes); IPosition pixelAxes(nAxes); Vector absRel(nAxes); for (uInt i=0; i pixelAxes(nAxes); if (nAxes > 0) pixelAxes = (itsPixelAxes+1).asVector(); rec.define("pixelAxes", pixelAxes); // String error; TableRecord recBlc, recTrc, recT; Quantum tmpQ; Double tmpD; // for (uInt j=0; j axes = Vector(rec.toArrayInt ("pixelAxes")); const uInt nAxes = axes.nelements(); IPosition pixelAxes(nAxes); for (uInt i=0; i absRel = Vector(rec.toArrayInt ("absrel")); uInt nAbsRel = absRel.nelements(); // Get the blc and trc quantity vectors String error; Vector > blc, trc; Double tmpD; QuantumHolder h; // uInt j; const RecordInterface& blcRec = rec.asRecord("blc"); const RecordInterface& trcRec = rec.asRecord("trc"); if (blcRec.nfields() != trcRec.nfields()) { throw (AipsError ("WCBox::fromRecord - blc and trc must be the same length")); } // uInt nFields = blcRec.nfields(); if (nAbsRel == 0) { if (nFields > 0) { absRel.resize(nFields); absRel = RegionType::Abs; } } else { if (nAbsRel != nFields) { throw (AipsError ("WCBox::fromRecord - absrel must be same length as blc/trc")); } } // if (nFields > 0) { blc.resize(nFields); trc.resize(nFields); // for (j=0; j wBlc(cSysTmp.referenceValue().copy()); Vector blcUnits(cSysTmp.worldAxisUnits().copy()); Vector wTrc(cSysTmp.referenceValue().copy()); Vector trcUnits(cSysTmp.worldAxisUnits().copy()); // Reorder world coordinates for output CS and set units. // "funny" values and units (default, pix, frac) are handled later and are // ignored at this stage uInt i; for (i=0; i pBlc; if (!cSysTmp.toPixel(pBlc, wBlc)) { throw (AipsError ("WCBox:doToLCregion - conversion of blc to pixel coordinates failed")); } // if (!cSysTmp.setWorldAxisUnits(trcUnits)) { throw (AipsError ("WCBox:doToLCregion - trc units are inconsistent with CoordinateSystem")); } makeWorldAbsolute (wTrc, itsAbsRel, cSysTmp, latticeShape); Vector pTrc; if (!cSysTmp.toPixel(pTrc, wTrc)) { throw (AipsError ("WCBox:doToLCregion - conversion of trc to pixel coordinates failed")); } // Now recover only those values from pBlc that we actually // want. Here we handle frac/pixel/default units as well. Vector refPix = cSysTmp.referencePixel(); const uInt nAxes = outOrder.nelements(); Vector outBlc(nAxes); Vector outTrc(nAxes); IPosition outShape(nAxes); for (i=0; i trc return new LCBox(outBlc, outTrc, outShape); } String WCBox::className() { return "WCBox"; } String WCBox::type() const { return className(); } // Private functions void WCBox::checkUnits (const IPosition& pixelAxes, const Vector >& values, const CoordinateSystem& cSys) // // CHeck the units of the given quanta are consistent // with the CoordinateSystem. The quanta are in the // order of the pixel axes. // { if (pixelAxes.nelements() != values.nelements()) { throw (AipsError ("WCBox::checkUnits - internal error")); } // Vector units = cSys.worldAxisUnits(); Quantum tmp; // Check units for (uInt i=0; i& value, const Int absRel, const Double refPix, const Int shape, const Bool isBlc) const { // Defaults get 0 or shape-1 if (value.getUnit() == "default") { if (isBlc) { pixel = 0; } else { pixel = shape - 1; } } else { // Deal with pixel or fractional coordinates Bool world = True; if (value.getUnit() == "pix") { pixel = value.getValue(); world = False; } else if (value.getUnit() == "frac") { pixel = value.getValue() * shape; if (!isBlc) { pixel -= 1; } world = False; } // Convert to absolute pixel; rel = abs - ref if (!world) { if (absRel == RegionType::RelRef) { pixel += refPix; } else if (absRel == RegionType::RelCen) { pixel += Double(shape)/2; } } } } } //# NAMESPACE CASACORE - END casacore-3.7.1/images/Regions/WCBox.h000066400000000000000000000311351476623553700173350ustar00rootroot00000000000000//# WCBox.h: Class to define a box shaped WC region //# Copyright (C) 1998,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_WCBOX_H #define IMAGES_WCBOX_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class LCRegion; class TableRecord; class IPosition; // // Class to define a world coordinate box region of interest in an image. // // // // // // // // //
      • WCRegion //
      • LCRegion //
      • CoordinateSystem // // // // The corners of the box are specified in world coordinates, but the // region enclosed by those corners is a box in lattice coordinates. // Thus, the volume enclosed does not follow world coordinate contours. // // All this class does, apart from constructing itself, is know // how to save itself to a Record and how to convert itself // to an LCRegion. The conversion allows you to apply // a WCBox constructed with one CoordinateSystem // to another CoordinateSystem. That is, you can apply a // WCBox from this image to that image. // // The flexibility of the CoordinateSystem class should // be kept in mind when using this class. Recall that a // CoordinateSystem has world and pixel axes, and // that these axes can be independently removed and independently // (re)ordered. // // During construction, the length of the world coordinate vectors may be // smaller than the number world axes in the supplied CoordinateSystem. // It is assumed that the units of the world coordinates are the same as those // encapsulated in the construction CoordinateSystem and in the same // order as specified (either intrinsically, or by the world axes // specification vectors). // // The following rules are followed during conversion to an LCRegion. //
          //
        1. The number of elements in the supplied latticeShape must be equal // to the number of pixel axes in the supplied CoordinateSystem. //
        2. The order of the pixel axes in the supplied CoordinateSystem // is assumed to be the order of the axes in the lattice for which the // supplied latticeShape is appropriate. //
        3. The CoordinateSystem supplied to the toLCRegion // function does not have to be identical in structure to that from // which the WCBox was constructed. They can consist // of different numbers of world and pixel axes and be in different // orders. //
        4. For every world axis in the supplied CoordinateSystem // that is also present (somewhere) in the construction CoordinateSystem // the blc/trc corresponding to that world axis will be // converted to pixels appropriate to the supplied CoordinateSystem. // The order of this pixel based blc/trc will be the order of the pixel axes of // the supplied CoordinateSystem //
        5. For every world axis in the supplied CoordinateSystem // that is not present in the construction CoordinateSystem, // the supplied latticeShape value for the corresponding // pixel axis is used, setting blc=0 and trc=latticeShape-1 // for that axis. //
        6. Once the pixel based blc/trc has been created, then, with // the supplied latticeShape, it is used to create the // LCBox, which is supplied as a pointer to the base // class LCRegion. //
        // // Note that when determining whether a world axis from one // CoordinateSystemis present on another, it is // considered to not be a match if two coordinates of the // same type (e.g. DirectionCoordinate) have different // specific types (e.g. J2000 and GALACTIC, or TOPO and LSR for // a SpectralCoordinate) //
        // // // Let us give some examples with pseudo-code. // cSys is the construction CoordinateSystem // and cSys2 is the supplied CoordinateSystem. // We list their world axes in the square brackets. // The construction blc/trc values don't matter // as long as there cSys.nWorldAxes() of them. // Similarly, the values of shape don't matter // as long as there are cSys2.nPixelAxes() of them. // // cSys = [ra, dec, freq]; // cSys2 = [ra, dec]; // blc = [,,]; // trc = [,,]; // shape = [,]; // WCBox box(blc, trc, cSys); // LCRegion* pR = box.toLCRegion(cSys2, shape); // // The resultant LCBox will have corners converted // according to // // blcLC(0) <- blc(0); // blcLC(1) <- blc(1); // trcLC(0) <- trc(0); // trcLC(1) <- trc(1); // // // // // // // cSys = [ra, dec, freq]; // cSys2 = [freq, stokes]; // blc = [,,]; // trc = [,,]; // shape = [,]; // WCBox box(blc, trc, cSys); // LCRegion* pR = box.toLCRegion(cSys2, shape); // // // The resultant LCBox will have corners converted // according to // // // blcLC(0) <- blc(2); // blcLC(1) = 0; // trcLC(0) <- trc(2); // trcLC(1) = shape(1) - 1; // // // // // // // cSys = [ra, dec]; // cSys2 = [ra, dec, freq]; // blc = [,]; // trc = [,]; // shape = [,,]; // WCBox box(blc, trc, cSys); // LCRegion* pR = box.toLCRegion(cSys2, shape); // // // The resultant LCBox will have corners converted // according to // // // blcLC(0) <- blc(0); // blcLC(1) <- blc(1); // blcLC(2) = 0l // trcLC(0) <- trc(0); // trcLC(1) <- trc(1); // trcLC(2) = shape(2)-1; // // // // // // // cSys = [ra, dec, freq]; // cSys2 = [freq, ra, dec]; // blc = [,,]; // trc = [,,]; // shape = [,,]; // WCBox box(blc, trc, cSys); // LCRegion* pR = box.toLCRegion(cSys2, shape); // // // The resultant LCBox will have corners converted // according to // // // blcLC(0) <- blc(2); // blcLC(1) <- blc(0); // blcLC(2) <- blc(1); // trcLC(0) <- trc(2); // trcLC(1) <- trc(0); // trcLC(2) <- trc(1); // // // // // // In this example we make it a bit harder by // reordering the pixel axes too. The new order // of the pixel axes in terms of the original // order [0,1,2] is given after the world axes // // // cSys = [ra, dec, freq], [0, 1, 2]; // cSys2 = [freq, ra, dec, stokes], [3, 0, 2, 1]; // blc = [,,]; // trc = [,,]; // shape = [,,,]; // WCBox box(blc, trc, cSys); // LCRegion* pR = box.toLCRegion(cSys2, shape); // // // Take the first world axis of cSys2 as an example. // First, "freq" is found as the world axis number // 2 in cSys. Then, when it is converted to // a pixel coordinate, it will turn up as // the value on pixel axis 1. The supplied shape // must be appropriate to a [stokes, freq, dec, ra] lattice. // The resultant LCBox will therefore have corners // converted according to // // // blcLC(0) = 0 // blcLC(1) <- blc(2); // blcLC(2) <- blc(1); // blcLC(3) <- blc(0); // // trcLC(0) = shape(0)-1; // trcLC(1) <- trc(2); // trcLC(2) <- trc(1); // trcLC(3) <- trc(0); // // // // // Users must be able to specify regions in world as well as lattice // coordinates. // // // // In all of the constructors, the order of the specified world // coordinates is that of the *PIXEL AXES* (not world axes) in the // CoordinateSystem. This is the natural order for a user to want // to specify them in. // // // // For the constructors specifying the world values as simple doubles, // it is *ASSUMED* that the units of those doubles are the same as // the native units of the CoordinateSystem for each axis. // // // // World coordinates may be specified as absolute or offset. If the // latter, they are offset with respect to the reference pixel of // the CoordinateSystem. // // //
      • Implement offset coordinates // class WCBox : public WCRegion { public: WCBox(); // Construct from vectors of world coordinates // defining the box corners. It is assumed that the // order of the values is in the order of the pixel axes // in the given coordinate system. // WCBox(const Vector >& blc, const Vector >& trc, const CoordinateSystem& cSys, const Vector& absRel); // // Construct from vectors of world coordinates // defining the box corners. You specify the pixel // axis order of the world values. // WCBox(const Vector >& blc, const Vector >& trc, const IPosition& pixelAxes, const CoordinateSystem& cSys, const Vector& absRel); // // Construct from the bounding box of an LCRegion. WCBox(const LCRegion& region, const CoordinateSystem& cSys); // Copy constructor (reference semantics [except for CoordinateSystem]) WCBox (const WCBox& other); // Destructor virtual ~WCBox(); // Assignment (copy semantics) WCBox& operator= (const WCBox& other); // Comparison virtual Bool operator==(const WCRegion& other) const; // Clone a WCBox object. virtual WCRegion* cloneRegion() const; // WCBox can extend a region. virtual Bool canExtend() const; // Make a new box from the given axesin this box. WCBox splitBox (const IPosition& axes) const; // Convert to an LCRegion using the supplied CoordinateSystem // and shape. virtual LCRegion* doToLCRegion (const CoordinateSystem& cSys, const IPosition& latticeShape, const IPosition& pixelAxesMap, const IPosition& outOrder) const; // Convert the WCBox object to a record. // The record can be used to make the object persistent. // The tableName argument can be used by derived // classes (e.g. LCPagedMask) to put very large objects. virtual TableRecord toRecord(const String& tableName) const; // Convert to a WCBox from a record. static WCBox* fromRecord (const TableRecord& rec, const String& tableName); // Returns WCBox static String className(); // Return region type. Returns the class name virtual String type() const; private: Vector > itsBlc; Vector > itsTrc; IPosition itsPixelAxes; CoordinateSystem itsCSys; Vector itsAbsRel; Bool itsNull; // Check units of quanta are consistent with CoordinateSystem void checkUnits (const IPosition& pixelAxes, const Vector >& values, const CoordinateSystem& cSys); // Convert relative pixels to absolute or fill in defaults void convertPixel(Double& pixel, const Quantum& value, const Int absRel, const Double refPix, const Int shape, const Bool isBlc) const; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Regions/WCComplement.cc000066400000000000000000000062271476623553700210520ustar00rootroot00000000000000//# WCComplement.cc: Make the complement of an image region //# Copyright (C) 1998,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN WCComplement::WCComplement (const ImageRegion& region) : WCCompound (®ion) {} WCComplement::WCComplement (Bool takeOver, const PtrBlock& regions) : WCCompound (takeOver, regions) {} WCComplement::WCComplement (const WCComplement& other) : WCCompound (other) {} WCComplement::~WCComplement() {} WCComplement& WCComplement::operator= (const WCComplement& other) { if (this != &other) { WCCompound::operator= (other); } return *this; } Bool WCComplement::operator== (const WCRegion& other) const { return WCCompound::operator== (other); } //Clone needs to be a WCRegion cause the SGI compiler is //not smart enough to do the right thing. WCRegion* WCComplement::cloneRegion() const { return new WCComplement (*this); } LCRegion* WCComplement::doToLCRegion (const CoordinateSystem& cSys, const IPosition& shape, const IPosition& pixelAxesMap, const IPosition& outOrder) const { PtrBlock regions; multiToLCRegion (regions, cSys, shape, pixelAxesMap, outOrder); return new LCComplement (True, regions); } String WCComplement::className() { return "WCComplement"; } String WCComplement::type() const { return className(); } TableRecord WCComplement::toRecord (const String& tableName) const { TableRecord rec; defineRecordFields (rec, className()); rec.defineRecord ("regions", makeRecord(tableName)); return rec; } WCComplement* WCComplement::fromRecord (const TableRecord& rec, const String& tableName) { PtrBlock regions; unmakeRecord (regions, rec.asRecord("regions"), tableName); return new WCComplement (True, regions); } } //# NAMESPACE CASACORE - END casacore-3.7.1/images/Regions/WCComplement.h000066400000000000000000000102471476623553700207110ustar00rootroot00000000000000//# WCComplement.h: Make the complement of an image region //# Copyright (C) 1998,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_WCCOMPLEMENT_H #define IMAGES_WCCOMPLEMENT_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Make the complement of an image region. // // // // // //
      • WCCompound // // // The WCComplement class is a specialization of class // WCCompound. // It makes it possible to take the complement of the given region // (which can be a simple WCBox, but also a complex compound region). // Note that only world coordinate regions can be used in a compound, // thus an LCSlicer object is not allowed in an intersection. //

        // Note that a region consists of all its masked-on pixels inside the // bounding box of the region. Thus the complement consists of all // pixels outside the bounding box and all masked-off pixels inside // the bounding box. So the complement of the complement of a region // is the region itself. // // // // // //# //#

      • //# class WCComplement: public WCCompound { public: WCComplement(); // Construct the complement of the given region. WCComplement (const ImageRegion& region1); // Copy constructor (copy semantics). WCComplement (const WCComplement& other); virtual ~WCComplement(); // Assignment (copy semantics). WCComplement& operator= (const WCComplement& other); // Comparison virtual Bool operator== (const WCRegion& other) const; // Make a copy of the derived object. // cloneRegion needs to return a WCRegion * because the // SGI compiler is smart enough to do the right thing. virtual WCRegion* cloneRegion() const; // Get the class name (to store in the record). static String className(); // Get the region type. Returns className() virtual String type() const; // Convert the (derived) object to a record. virtual TableRecord toRecord (const String& tableName) const; // Convert correct object from a record. static WCComplement* fromRecord (const TableRecord&, const String& tableName); // Construct from multiple regions. // When takeOver is True, the destructor will delete the // given regions. Otherwise a copy of the regions is made. WCComplement (Bool takeOver, const PtrBlock& regions); protected: // Convert to an LCRegion using the given coordinate system and shape. // pixelAxesMap(i) gives the pixel axis in cSys of axes i // in the axesDesc. virtual LCRegion* doToLCRegion (const CoordinateSystem& cSys, const IPosition& shape, const IPosition& pixelAxesMap, const IPosition& outOrder) const; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Regions/WCCompound.cc000066400000000000000000000172371476623553700205360ustar00rootroot00000000000000//# WCCompound.cc: Base class for compound WCRegion objects //# Copyright (C) 1998,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN WCCompound::WCCompound (const ImageRegion& region1, const ImageRegion& region2) { PtrBlock regions(2); regions[0] = ®ion1; regions[1] = ®ion2; makeWCRegion (regions); init (False); } WCCompound::WCCompound (const ImageRegion* region1, const ImageRegion* region2, const ImageRegion* region3, const ImageRegion* region4, const ImageRegion* region5, const ImageRegion* region6, const ImageRegion* region7, const ImageRegion* region8, const ImageRegion* region9, const ImageRegion* region10) { PtrBlock regions(10); uInt n=0; regions[n++] = region1; if (region2 != 0) regions[n++] = region2; if (region3 != 0) regions[n++] = region3; if (region4 != 0) regions[n++] = region4; if (region5 != 0) regions[n++] = region5; if (region6 != 0) regions[n++] = region6; if (region7 != 0) regions[n++] = region7; if (region8 != 0) regions[n++] = region8; if (region9 != 0) regions[n++] = region9; if (region10 != 0) regions[n++] = region10; regions.resize (n, True, True); makeWCRegion (regions); init (False); } WCCompound::WCCompound (const PtrBlock& regions) { makeWCRegion (regions); init (False); } WCCompound::WCCompound (Bool takeOver, const PtrBlock& regions) : itsRegions (regions) { init (takeOver); } WCCompound::WCCompound (const WCCompound& other) : WCRegion (other), itsRegions (other.itsRegions.nelements()), itsAxesUsed (other.itsAxesUsed) { uInt nr = itsRegions.nelements(); for (uInt i=0; icloneRegion(); } } WCCompound::~WCCompound() { uInt nr = itsRegions.nelements(); for (uInt i=0; icloneRegion(); } itsAxesUsed = other.itsAxesUsed; } return *this; } void WCCompound::multiToLCRegion (PtrBlock& regions, const CoordinateSystem& cSys, const IPosition& shape, const IPosition& pixelAxesMap, const IPosition& outOrder) const { uInt nr = itsRegions.nelements(); regions.resize (nr, True); uInt nd = pixelAxesMap.nelements(); IPosition pixAxesMap(pixelAxesMap); IPosition outOrd(outOrder); IPosition axisUsed(nd); for (uInt i=0; itoLCRegionAxes (cSys, shape, pixAxesMap, outOrd); } } Bool WCCompound::operator== (const WCRegion& other) const { // Type check. if (! WCRegion::operator== (other)) { return False; } // Cast is safe since types match. const WCCompound& that = (const WCCompound&)other; // Check the regions. if (itsRegions.nelements() != that.itsRegions.nelements()) { return False; } // The regions do not have to be in the same order. // It makes it a bit slower. uInt nr = itsRegions.nelements(); Vector used(nr, False); for (uInt i=0; i& regions) { uInt nr = regions.nelements(); itsRegions.resize (nr); for (uInt i=0; iisLCSlicer()) { throw (AipsError ("WCCompound::WCCompound - " "an LCSlicer object cannot be part of " "an WCCompound")); } itsRegions[i] = &(regions[i]->asWCRegion()); } } void WCCompound::init (Bool takeOver) { // Copy the region object if takeOver=False. // Compose the axes description of the entire compound. // Find out which compound axes are used in each region. uInt nr = itsRegions.nelements(); itsAxesUsed.resize (nr); for (uInt i=0; icloneRegion(); } // Add axes to description if not already defined. // Fill in the axes used. uInt nd = itsRegions[i]->ndim(); IPosition& axesUsed = itsAxesUsed[i]; axesUsed.resize (nd); for (uInt j=0; jgetAxisDesc(j); // If the axis is already defined, it has that axis number. // Otherwise add its description and use that as axis number. axesUsed(j) = axisNr (desc, getAxesDesc()); if (axesUsed(j) < 0) { axesUsed(j) = getAxesDesc().nfields(); addAxisDesc (desc); } } } } TableRecord WCCompound::makeRecord (const String& tableName) const { TableRecord rec; Int nr = itsRegions.nelements(); for (Int i=0; itoRecord (tableName)); } rec.define ("nr", nr); return rec; } void WCCompound::unmakeRecord (PtrBlock& regions, const TableRecord& rec, const String& tableName) { Int nr = rec.asInt ("nr"); regions.resize (nr, True); for (Int i=0; i #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class ImageRegion; class LCRegion; class CoordinateSystem; class RecordInterface; class TableRecord; class String; // // Base class for compound WCRegion objects. // // // // // //
      • WCRegion // // // WCCompound is the base class for world coordinate regions. // It defines the functionality simply as conversion to an LCRegion. // This is because you need an LCRegion to be able to access the // pixels in a Lattice. // The conversion functions should be flexible in that the // supplied CoordinateSystem does not have to be the same // as that with which the derived class was constructed. // This means that you can apply a WCCompound from one image // to another, provided that operation has some meaning. // // // // // //# //#
      • //# class WCCompound : public WCRegion { public: // Construct from one or more image regions. // The image regions have to contain WCRegion objects, otherwise an // exception is thrown. // WCCompound (const ImageRegion& region1, const ImageRegion& region2); WCCompound (const ImageRegion* region1, const ImageRegion* region2 = 0, const ImageRegion* region3 = 0, const ImageRegion* region4 = 0, const ImageRegion* region5 = 0, const ImageRegion* region6 = 0, const ImageRegion* region7 = 0, const ImageRegion* region8 = 0, const ImageRegion* region9 = 0, const ImageRegion* region10 = 0); WCCompound (const PtrBlock& regions); // // Construct from multiple regions given as a Block. // When takeOver is True, the destructor will delete the // given regions. Otherwise a copy of the regions is made. WCCompound (Bool takeOver, const PtrBlock& regions); // Copy constructor (copy semantics). WCCompound (const WCCompound& other); virtual ~WCCompound(); // Comparison virtual Bool operator==(const WCRegion& other) const; // Get the contributing regions. const PtrBlock& regions() const; protected: // Assignment (copy semantics) makes only sense for a derived class. WCCompound& operator= (const WCCompound& other); // Convert each WCRegion to an LCRegion. // The axes argument tells which axes to use from the coordinate // system and shape. void multiToLCRegion (PtrBlock& regions, const CoordinateSystem& cSys, const IPosition& shape, const IPosition& pixelAxesMap, const IPosition& extendAxes) const; // Store the contributing regions in a record. TableRecord makeRecord (const String& tableName) const; // Retrieve the contributing objects from the record. static void unmakeRecord (PtrBlock&, const TableRecord&, const String& tableName); private: // Check if the ImageRegion's contain WCRegion's and extract them. void makeWCRegion (const PtrBlock&); // Check if the regions are correct. // If needed, make a copy of the region objects. void init (Bool takeOver); //# Member variables. PtrBlock itsRegions; Block itsAxesUsed; }; inline const PtrBlock& WCCompound::regions() const { return itsRegions; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Regions/WCConcatenation.cc000066400000000000000000000144511476623553700215320ustar00rootroot00000000000000//# WCConcatenation.cc: Combine multiple ImageRegion's into a new dimension //# Copyright (C) 1998,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN WCConcatenation::WCConcatenation (const PtrBlock& regions, const WCBox& extendBox) : WCCompound (regions), itsExtendBox (extendBox) { fill(); } WCConcatenation::WCConcatenation (Bool takeOver, const PtrBlock& regions, const WCBox& extendBox) : WCCompound (takeOver, regions), itsExtendBox (extendBox) { fill(); } WCConcatenation::WCConcatenation (const WCConcatenation& other) : WCCompound (other), itsExtendBox (other.itsExtendBox) {} WCConcatenation::~WCConcatenation() {} WCConcatenation& WCConcatenation::operator= (const WCConcatenation& other) { if (this != &other) { WCCompound::operator= (other); itsExtendBox = other.itsExtendBox; } return *this; } Bool WCConcatenation::operator== (const WCRegion& other) const { if (! WCCompound::operator== (other)) { return False; } const WCConcatenation& that = (const WCConcatenation&)other; return (itsExtendBox == that.itsExtendBox); } WCRegion* WCConcatenation::cloneRegion() const { return new WCConcatenation (*this); } void WCConcatenation::fill() { // Check if all regions have the same axes which is true if they // have the same dimensionality as the compound. uInt nr = regions().nelements(); for (uInt i=0; indim() != ndim()) { throw (AipsError ("WCConcatenation::WCConcatenation - " "all its regions should have the same axes")); } } // Check if the box is 1-dimensional. if (itsExtendBox.ndim() != 1) { throw (AipsError ("WCConcatenation::WCConcatenation - " "the extendBox should be 1-dim")); } const Record& desc = itsExtendBox.getAxisDesc(0); // If the axis is already defined, an exception is thrown. // Otherwise add its description. if (axisNr (desc, getAxesDesc()) >= 0) { throw (AipsError ("WCConcatenation::WCConcatenation - " "one or more axes of region to be extended " "is used in the extendBox")); } addAxisDesc (desc); } LCRegion* WCConcatenation::doToLCRegion (const CoordinateSystem& cSys, const IPosition& shape, const IPosition& pixelAxesMap, const IPosition& outOrder) const { uInt i; // Split the pixelAxesMap and outOrder into the parts for the // region and the box (which can be more than the box itself // because it might be extended). uInt ndreg = ndim() - 1; DebugAssert (outOrder.nelements() == ndim(), AipsError); IPosition regPixMap(ndreg); IPosition regOutOrd(ndreg); IPosition boxPixMap(1, pixelAxesMap(ndreg)); IPosition boxOutOrd(1, 0); // In our axesDesc the first axes are used for the region and the // rest for the box. for (i=0; i reginx(ndreg); std::vector tmp(regOutOrd.begin(), regOutOrd.end()); GenSortIndirect::sort (reginx, &(tmp[0]), ndreg); for (i=0; i regions; multiToLCRegion (regions, cSys, shape, regPixMap, regOutOrd); LCRegion* boxptr = itsExtendBox.toLCRegionAxes (cSys, shape, boxPixMap, boxOutOrd); DebugAssert (boxptr->type() == LCBox::className(), AipsError); LCConcatenation* extptr = new LCConcatenation (True, regions, outOrder(ndreg), *(LCBox*)boxptr); delete boxptr; return extptr; } String WCConcatenation::className() { return "WCConcatenation"; } String WCConcatenation::type() const { return className(); } TableRecord WCConcatenation::toRecord (const String& tableName) const { TableRecord rec; defineRecordFields (rec, className()); rec.defineRecord ("regions", makeRecord(tableName)); rec.defineRecord ("box", itsExtendBox.toRecord(tableName)); return rec; } WCConcatenation* WCConcatenation::fromRecord (const TableRecord& rec, const String& tableName) { PtrBlock regions; unmakeRecord (regions, rec.asRecord("regions"), tableName); WCRegion* boxptr = WCRegion::fromRecord (rec.asRecord("box"), tableName); DebugAssert (boxptr->type() == WCBox::className(), AipsError); return new WCConcatenation (True, regions, *(const WCBox*)boxptr); } } //# NAMESPACE CASACORE - END casacore-3.7.1/images/Regions/WCConcatenation.h000066400000000000000000000136531476623553700213770ustar00rootroot00000000000000//# WCConcatenation.h: Combine multiple ImageRegion's into a new dimension //# Copyright (C) 1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_WCCONCATENATION_H #define IMAGES_WCCONCATENATION_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Combine multiple ImageRegion's into a new dimension. // // // // // //
      • WCCompound // // // The WCConcatenation class is a specialization of class // WCCompound. // It makes it possible to combine multiple regions and to add a // dimension on them. The axis and the range (beginning and end) // of that new dimension have to be specified using an // WCBox object. // That axes should not be an axis in the given regions. //

        // WCConcatenation can be seen as a mixture of the classes // WCUnion and // WCExtension. Like WCUnion it // combines regions and like WCExtension it increases the dimensionality // for the new region (be it with only 1). //
        // Unlike WCUnion the axes have to be the same in all regions, // because creating a WCConcatenation means combining similar regions. //

        // E.g. One can define a different polygon in the RA-DEC plane of each // channel. WCConcatenation makes it possible to combine the polygons // to one 3D region in the RA-DEC-Freq cube. // // // This example combines n (relative) circles // given in the RA,DEC plane along the FREQ-axis. // In this example the regions used are circles with the same centers, // but it is also possible to combine differently shaped regions. // Note that WCConcatenation takes over the pointers to the individual regions, // so they do not need to be deleted (the WCConcatenation destructor does it). // // IPosition center (2,10,20); // PtrBlock cirPtr(n); // for (i=0; i blc(1); // Vector trc(1); // blc(0) = Quantity (0.25, "frac"); // trc(0) = Quantity (0.75, "frac"); // WCConcatenation region (True, cirPtr, WCBox(blc, trc, cSys, IPosition(1,2)); // // This example is artificial in the sense that WCEllipsoid does not // exist yet and the WCBox constructor looks a bit different. // One should probably also do a bit more trouble to find out if FREQ // is indeed the 2nd axis in the coordinate system. // //# //#

      • //# class WCConcatenation: public WCCompound { public: // Combine the given regions. // When takeOver is True, the destructor will delete the // given regions. Otherwise a copy of the regions is made. // The extend range has to be given as a 1-dimensional box. // WCConcatenation (const PtrBlock& regions, const WCBox& extendRange); WCConcatenation (Bool takeOver, const PtrBlock& regions, const WCBox& extendRange); // // Copy constructor (copy semantics). WCConcatenation (const WCConcatenation& other); virtual ~WCConcatenation(); // Assignment (copy semantics). WCConcatenation& operator= (const WCConcatenation& other); // Comparison virtual Bool operator== (const WCRegion& other) const; // Make a copy of the derived object. virtual WCRegion* cloneRegion() const; // Get the class name (to store in the record). static String className(); // Get the region type. Returns className() virtual String type() const; // Convert the (derived) object to a record. virtual TableRecord toRecord (const String& tableName) const; // Convert correct object from a record. static WCConcatenation* fromRecord (const TableRecord&, const String& tableName); protected: // Convert to an LCRegion using the given coordinate system and shape. // pixelAxesMap(i) gives the pixel axis in cSys of axes i // in the axesDesc. virtual LCRegion* doToLCRegion (const CoordinateSystem& cSys, const IPosition& shape, const IPosition& pixelAxesMap, const IPosition& outOrder) const; private: // Do a check and fill the remainder of the object. void fill(); //# Variables WCBox itsExtendBox; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Regions/WCDifference.cc000066400000000000000000000062641476623553700210020ustar00rootroot00000000000000//# WCDifference.cc: Make the difference of 2 image regions //# Copyright (C) 1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN WCDifference::WCDifference (const ImageRegion& region1, const ImageRegion& region2) : WCCompound (region1, region2) {} WCDifference::WCDifference (const PtrBlock& regions) : WCCompound (regions) {} WCDifference::WCDifference (Bool takeOver, const PtrBlock& regions) : WCCompound (takeOver, regions) {} WCDifference::WCDifference (const WCDifference& other) : WCCompound (other) {} WCDifference::~WCDifference() {} WCDifference& WCDifference::operator= (const WCDifference& other) { if (this != &other) { WCCompound::operator= (other); } return *this; } Bool WCDifference::operator== (const WCRegion& other) const { return WCCompound::operator== (other); } WCRegion* WCDifference::cloneRegion() const { return new WCDifference (*this); } LCRegion* WCDifference::doToLCRegion (const CoordinateSystem& cSys, const IPosition& shape, const IPosition& pixelAxesMap, const IPosition& outOrder) const { PtrBlock regions; multiToLCRegion (regions, cSys, shape, pixelAxesMap, outOrder); return new LCDifference (True, regions); } String WCDifference::className() { return "WCDifference"; } String WCDifference::type() const { return className(); } TableRecord WCDifference::toRecord (const String& tableName) const { TableRecord rec; defineRecordFields (rec, className()); rec.defineRecord ("regions", makeRecord(tableName)); return rec; } WCDifference* WCDifference::fromRecord (const TableRecord& rec, const String& tableName) { PtrBlock regions; unmakeRecord (regions, rec.asRecord("regions"), tableName); return new WCDifference (True, regions); } } //# NAMESPACE CASACORE - END casacore-3.7.1/images/Regions/WCDifference.h000066400000000000000000000112141476623553700206330ustar00rootroot00000000000000//# WCDifference.h: Make the difference of 2 image regions //# Copyright (C) 1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_WCDIFFERENCE_H #define IMAGES_WCDIFFERENCE_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Make the difference of 2 image regions. // // // // // //
      • WCCompound // // // The WCDifference class is a specialization of class // WCCompound. // It makes it possible to take the difference of 2 regions. // Note that only world coordinate regions can be used in a compound, // thus an LCSlicer object is not allowed in a difference. //

        // The difference consists of all pixels masked-on in the first // region and not masked-on in the second region. //

        // The regions in a difference can have different axes and dimensionalities. // The axes and dimensionality of a difference are determined by the // collection of all different axes in its regions. Each individual region // will be auto-extended along the axes not being part of the region. // E.g. one can define a WCBox with axis RA and another WCBox with // axis DEC. The difference will be 2-dim with axes RA and DEC. The first // box will be auto-extended to cover the DEC axis, which results // in a 2-dim box with its DEC axis the length of the image's DEC axis. // Similarly the second box will be auto-extended to cover the RA axis. // // // // // //# //#

      • //# class WCDifference: public WCCompound { public: // Construct the difference of one or more image regions. // The image regions have to contain WCRegion objects, otherwise an // exception is thrown. // WCDifference (const ImageRegion& region1, const ImageRegion& region2); WCDifference (const PtrBlock& regions); // // Construct from multiple regions given as a Block. // When takeOver is True, the destructor will delete the // given regions. Otherwise a copy of the regions is made. WCDifference (Bool takeOver, const PtrBlock& regions); // Copy constructor (copy semantics). WCDifference (const WCDifference& other); virtual ~WCDifference(); // Assignment (copy semantics). WCDifference& operator= (const WCDifference& other); // Comparison virtual Bool operator== (const WCRegion& other) const; // Make a copy of the derived object. virtual WCRegion* cloneRegion() const; // Get the class name (to store in the record). static String className(); // Get the region type. Returns className() virtual String type() const; // Convert the (derived) object to a record. virtual TableRecord toRecord (const String& tableName) const; // Convert correct object from a record. static WCDifference* fromRecord (const TableRecord&, const String& tableName); protected: // Convert to an LCRegion using the given coordinate system and shape. // pixelAxesMap(i) gives the pixel axis in cSys of axes i // in the axesDesc. virtual LCRegion* doToLCRegion (const CoordinateSystem& cSys, const IPosition& shape, const IPosition& pixelAxesMap, const IPosition& outOrder) const; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Regions/WCEllipsoid.cc000066400000000000000000000400341476623553700206650ustar00rootroot00000000000000//# WCPolygon.cc: Class to define a 2D polygonal world coordinate region of interest //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN WCEllipsoid::WCEllipsoid() {} WCEllipsoid::WCEllipsoid( const Vector& center, const Vector& radii, const IPosition& pixelAxes, const CoordinateSystem& csys, const RegionType::AbsRelType absRel ) : _center(center), _radii(radii), _pixelAxes(pixelAxes), _csys(csys), _absRel(absRel), _theta(Quantity(0, "rad")), _specType(NOT_SPECIAL) { _init(); } WCEllipsoid::WCEllipsoid( const Vector& center, const Quantity& radius, const IPosition& pixelAxes, const CoordinateSystem& csys, const RegionType::AbsRelType absRel ) : _center(center), _radii(_center.size(), radius), _pixelAxes(pixelAxes), _csys(csys), _absRel(absRel), _theta(Quantity(0, "rad")), _specType(SPHERE) { _init(); } WCEllipsoid::WCEllipsoid( const Quantity& xcenter, const Quantity& ycenter, const Quantity& majorAxis, const Quantity& minorAxis, const Quantity& theta, const uInt pixelAxis0, const uInt pixelAxis1, const CoordinateSystem& csys, const RegionType::AbsRelType absRel ) : _csys(csys), _absRel(absRel), _specType(ELLIPSE_2D) { AlwaysAssert (csys.nPixelAxes() >= 2, AipsError); AlwaysAssert (csys.nWorldAxes() >= 2, AipsError); String msg; if (! theta.isConform("rad")) { throw AipsError( String(__FUNCTION__) + ": theta is not an angular quantity" ); } if (! xcenter.isConform(ycenter)) { throw AipsError( String(__FUNCTION__) + ": xcenter and ycenter do not have the same base unit" ); } if (! majorAxis.isConform(minorAxis)) { throw AipsError( String(__FUNCTION__) + ": major and and minor axes do not have the same base unit" ); } if (majorAxis.getValue() < minorAxis.getValue(majorAxis.getUnit())) { throw AipsError( String(__FUNCTION__) + ": major axis is smaller than minor axis." ); } _theta.setValue(fmod(theta.getValue("rad"), M_PI)); _theta.setUnit("rad"); if (_theta.getValue() < 0) { _theta + Quantity(M_PI, "rad"); } _center.resize(2); _center[0] = xcenter; _center[1] = ycenter; _radii.resize(2); _radii[0] = majorAxis; _radii[1] = minorAxis; _pixelAxes.resize(2); _pixelAxes[0] = pixelAxis0; _pixelAxes[1] = pixelAxis1; _init(); } WCEllipsoid::WCEllipsoid(const WCEllipsoid& that) : WCRegion(that), _center(that._center), _radii(that._radii), _pixelAxes(that._pixelAxes), _csys(that._csys), // This one makes a copy _absRel(that._absRel), _theta(that._theta), _specType(that._specType) {} WCEllipsoid& WCEllipsoid::operator= (const WCEllipsoid& that) // // Assignment (copy semantics) // { if (this != &that) { WCRegion::operator= (that); _center = that._center; _radii = that._radii; _pixelAxes = that._pixelAxes; _csys = that._csys; // This one makes a copy _absRel = that._absRel; _theta = that._theta; _specType = that._specType; } return *this; } Bool WCEllipsoid::operator== (const WCRegion& other) const { if (type() != other.type()) { return False; } const WCEllipsoid& that = (const WCEllipsoid&)other; if (_absRel != that._absRel) { return False; } if (! near(_theta.getValue(), that._theta.getValue())) { return False; } if (_theta.getUnit() != that._theta.getUnit()) { return False; } if (_pixelAxes.size() != that._pixelAxes.size()) { return False; } for (uInt i=0; i<_pixelAxes.size(); i++) { if ( ! near(_center[i].getValue(), that._center[i].getValue()) || _center[i].getUnit() != that._center[i].getUnit() || ! near(_radii[i].getValue(), that._radii[i].getValue()) || _radii[i].getUnit() != that._radii[i].getUnit() || _pixelAxes[i] != that._pixelAxes[i] ) { return False; } } if (! _csys.near(that._csys)) { return False; } return True; } WCRegion* WCEllipsoid::cloneRegion() const { return new WCEllipsoid(*this); } Bool WCEllipsoid::canExtend() const { return False; } String WCEllipsoid::type() const { return className(); } String WCEllipsoid::className() { return "WCEllipsoid"; } TableRecord WCEllipsoid::toRecord(const String&) const { unitInit(); TableRecord rec; defineRecordFields(rec, className()); rec.define("oneRel", True); rec.define("type", Int(_specType)); rec.define("absrel", Int(_absRel)); const uInt nAxes = _pixelAxes.nelements(); Vector pixelAxes(nAxes); pixelAxes = (_pixelAxes+1).asVector(); rec.define ("pixelAxes", pixelAxes); // Save ellipsoid. Convert abspix to one rel String error; { // center TableRecord rec2, rec3; for (uInt i=0; i<_center.size(); i++) { Double tmp = _center[i].getValue(); String units = _center[i].getUnit(); if (units == "pix" && _absRel == RegionType::Abs) { tmp += 1.0; } QuantumHolder qh(Quantity(tmp, units)); if (! qh.toRecord(error, rec2)) { throw ( AipsError ( "WCEllipsoid::" + String(__FUNCTION__) + ": could not save center because " + error ) ); } rec3.defineRecord(i, rec2); } rec.defineRecord("center", rec3); } { // radii TableRecord rec2, rec3; QuantumHolder qh; switch (_specType) { case SPHERE: qh =QuantumHolder(_radii[0]); if (! qh.toRecord(error, rec2)) { throw ( AipsError ( "WCEllipsoid::" + String(__FUNCTION__) + ": could not save sphere radius because " + error ) ); } rec.defineRecord("radius", rec2); break; case ELLIPSE_2D: case NOT_SPECIAL: default: // both general and 2-d ellipse go here String error; for (uInt i=0; i<_radii.size(); i++) { qh = QuantumHolder(_radii[i]); if (! qh.toRecord(error, rec2)) { throw ( AipsError ( "WCEllipsoid::" + String(__FUNCTION__) + ": could not save radii because " + error ) ); } rec3.defineRecord(i, rec2); } rec.defineRecord("radii", rec3); } } { // theta TableRecord rec2; QuantumHolder qh(_theta); if (! qh.toRecord(error, rec2)) { throw ( AipsError ( "WCEllipsoid::" + String(__FUNCTION__) + ": could not save theta because " + error ) ); } rec.defineRecord("theta", rec2); } if (! _csys.save(rec, "coordinates")) { throw (AipsError ("WCEllipsoid::toRecord: could not save Coordinate System")); } return rec; } WCEllipsoid* WCEllipsoid::fromRecord ( const TableRecord& rec, const String& ) { // Get CoordinateSystem unitInit(); CoordinateSystem* csys = CoordinateSystem::restore(rec,"coordinates"); Bool oneRel = rec.asBool("oneRel"); RegionType::AbsRelType absRel = RegionType::AbsRelType(rec.asInt("absrel")); SpecialType specType = SpecialType(rec.asInt("type")); // Get pixel axes and convert to zero rel. Vector tmp = Vector(rec.toArrayInt ("pixelAxes")); IPosition pixelAxes(tmp); if (oneRel) { pixelAxes -= 1; } // Get the ellipsoid Vector center(pixelAxes.size()); String error, units; WCEllipsoid *ellipsoid = 0; Vector radii(pixelAxes.size()); Quantity radius, theta; { // center QuantumHolder qh; const RecordInterface& subRecord = rec.asRecord("center"); for (uInt i=0; i wCenter(csys.referenceValue().copy()); Vector centerUnits(csys.worldAxisUnits().copy()); Vector wRadius(csys.nWorldAxes(), 0); Vector radiusUnits(csys.worldAxisUnits().copy()); // Reorder world coordinates for output CS and set units. // "funny" values and units (pix, frac) are handled later and are // ignored at this stage for (uInt i=0; i<_pixelAxes.nelements(); i++) { Int latticePixelAxis = pixelAxesMap[i]; Int worldAxis = csys.pixelAxisToWorldAxis(latticePixelAxis); Quantity value = _center[latticePixelAxis]; if ( value.getUnit() != "pix" && value.getUnit() != "frac" && value.getUnit() != "default" ) { // other WC classes seem to use the reference pixel value and // ignore the actual pixel values if pix or frac which I don't // understand, but for now I'm using that algorithm wCenter[worldAxis] = value.getValue(); centerUnits[worldAxis] = value.getUnit(); } } // Convert to pixels for all pixel axes of csys for center CoordinateSystem mycsys = csys; Vector absRel(wCenter.size(), _absRel); if (! mycsys.setWorldAxisUnits(centerUnits)) { throw (AipsError ("WCEllipsoid::doToLCregion - center units are inconsistent with coordinate system")); } makeWorldAbsolute (wCenter, absRel, mycsys, latticeShape); Vector pCenter; if (! mycsys.toPixel(pCenter, wCenter)) { throw ( AipsError( "WCEllipsoid::doToLCregion - conversion of center to pixel coordinates failed" ) ); } for (uInt i=0; i<_pixelAxes.nelements(); i++) { Int latticePixelAxis = pixelAxesMap[i]; Int worldAxis = csys.pixelAxisToWorldAxis(latticePixelAxis); Quantity value = _radii[latticePixelAxis]; if ( value.getUnit() != "pix" ) { // other WC classes seem to use the reference pixel value and // ignore the actual pixel values if pix or frac which I don't // understand, but for now I'm using that algorithm wRadius[worldAxis] = value.getValue(); radiusUnits[worldAxis] = value.getUnit(); } } if (! mycsys.setWorldAxisUnits(radiusUnits)) { throw (AipsError ("WCEllipsoid::doToLCregion - center units are inconsistent with coordinate system")); } Vector pIncrement = mycsys.increment(); Vector pRadius(_radii.size()); for (uInt i=0; i refPix = mycsys.referencePixel(); const uInt nAxes = outOrder.nelements(); Vector outCenter(nAxes); Vector outRadius(nAxes); IPosition outShape(nAxes); for (uInt i=0; i<_pixelAxes.nelements(); i++) { Int latticePixelAxis = pixelAxesMap[i]; Double pixel = pCenter(latticePixelAxis); convertPixel( pixel, _center[i].getValue(), _center[i].getUnit(), _absRel, refPix[i], latticeShape[latticePixelAxis] ); outCenter[outOrder[i]] = pixel; outRadius[outOrder[i]] = pRadius(latticePixelAxis); outShape[outOrder[i]] = latticeShape(latticePixelAxis); } // Create the LCEllipsoid. switch(_specType) { case SPHERE: return new LCEllipsoid(outCenter, outRadius[0], outShape); case ELLIPSE_2D: // I'm pretty sure theta does not need to be mucked with // if the order of the axes changes. return new LCEllipsoid( outCenter[0], outCenter[1], outRadius[0], outRadius[1], _theta.getValue("rad"), outShape ); default: break; } return new LCEllipsoid(outCenter, outRadius, outShape); } void WCEllipsoid::_init() { if (_pixelAxes.size() != _center.size()) { throw AipsError( "LCEllipsoid::" + String(__FUNCTION__) + ": Different sizes for pixel axes and center vectors" ); } if (_pixelAxes.size() != _radii.size()) { throw AipsError( "LCEllipsoid::" + String(__FUNCTION__) + ": Different sizes for pixel axes and radii vectors" ); } _checkPixelAxes(); unitInit(); _checkUnits(); for (uInt i=0; i<_pixelAxes.nelements(); i++) { addAxisDesc (makeAxisDesc (_csys, _pixelAxes(i))); } } void WCEllipsoid::_checkPixelAxes() const { ostringstream oss; oss << _pixelAxes; String paAsString = oss.str(); for (uInt i=0; i<_pixelAxes.size(); i++) { if ( _pixelAxes[i] > Int(_csys.nPixelAxes()-1) ) { throw ( AipsError( "WCEllipsoid::" + String(__FUNCTION__) + ": the specified pixel axes are greater than" + "the number of pixel axes in the coordinate system" ) ); } if (paAsString.freq(String::toString(_pixelAxes[i])) > 1) { throw ( AipsError( "WCEllipsoid::" + String(__FUNCTION__) + ": You have specified the same pixel axis more than once" ) ); } } } void WCEllipsoid::_checkUnits() const { Vector units(_radii.size()); for (uInt i=0; i #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Class to define an n-dimensional ellipsoid in world coordinates. // // // // // // // // // //
      • WCRegion //
      • WCPolygon //
      • LCRegion //
      • CoordinateSystem // // // // // // // // Users must be able to specify ellipsoids in world as well as lattice // coordinates. // // class WCEllipsoid : public WCRegion { public: // ellipsoid with axes parallel to coordinate axes WCEllipsoid( const Vector& center, const Vector& radii, const IPosition& pixelAxes, const CoordinateSystem& cSys, const RegionType::AbsRelType absRel=RegionType::Abs ); // sphere. pixelAxes must have the same base units // and those pixels musb be square or an exception is thrown. WCEllipsoid( const Vector& center, const Quantity& radius, const IPosition& pixelAxes, const CoordinateSystem& cSys, const RegionType::AbsRelType absRel=RegionType::Abs ); // 2-D ellipse . The axes must have the same base units // and those pixels must be square or an exception is thrown. // theta is the angle between the pixelAxis0 and // the major axis of the ellipse. WCEllipsoid( const Quantity& xcenter, const Quantity& ycenter, const Quantity& majorAxis, const Quantity& minorAxis, const Quantity& theta, const uInt pixelAxis0, const uInt pixelAxis1, const CoordinateSystem& cSys, const RegionType::AbsRelType absRel=RegionType::Abs ); WCEllipsoid(const WCEllipsoid& that); WCEllipsoid& operator= (const WCEllipsoid& that); Bool operator== (const WCRegion& other) const; WCRegion* cloneRegion() const; Bool canExtend() const; String type() const; static String className(); static WCEllipsoid* fromRecord( const TableRecord& rec, const String& ); TableRecord toRecord(const String& tableName) const; LCRegion* doToLCRegion ( const CoordinateSystem& csys, const IPosition& latticeShape, const IPosition& pixelAxesMap, const IPosition& outOrder ) const; private: // WARN do not change the order of the members of this enum // or you will break backward compatibility with records previously // saved persistently. Add new types to the end of the enum. enum SpecialType { NOT_SPECIAL, SPHERE, ELLIPSE_2D }; WCEllipsoid(); Vector _center; Vector _radii; IPosition _pixelAxes; CoordinateSystem _csys; RegionType::AbsRelType _absRel; Quantity _theta; SpecialType _specType; void _checkPixelAxes() const; void _checkUnits() const; void _init(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Regions/WCExtension.cc000066400000000000000000000217661476623553700207300ustar00rootroot00000000000000//# WCExtension.cc: Make the extension of an image region //# Copyright (C) 1998,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN WCExtension::WCExtension (const ImageRegion& region, const WCBox& extendBox) : WCCompound (region, ImageRegion(extendBox)) {} WCExtension::WCExtension (Bool takeOver, const PtrBlock& regions) : WCCompound (takeOver, regions) {} WCExtension::WCExtension (const WCExtension& other) : WCCompound (other) {} WCExtension::~WCExtension() {} WCExtension& WCExtension::operator= (const WCExtension& other) { if (this != &other) { WCCompound::operator= (other); } return *this; } Bool WCExtension::operator== (const WCRegion& other) const { return WCCompound::operator== (other); } WCRegion* WCExtension::cloneRegion() const { return new WCExtension (*this); } Bool WCExtension::canExtend() const { // It can extend itself if the box can do so. DebugAssert (regions().nelements() == 2, AipsError); return regions()[1]->canExtend(); } void WCExtension::findAxes (IPosition& extendBoxAxes, IPosition& stretchBoxAxes, IPosition& stretchRegionAxes) const { const WCRegion& box = *(regions()[1]); uInt nstretch = regions()[0]->ndim() + box.ndim() - ndim(); uInt nextend = box.ndim() - nstretch; extendBoxAxes.resize (nextend); stretchBoxAxes.resize (nstretch); stretchRegionAxes.resize (nstretch); const Record& desc = regions()[0]->getAxesDesc(); uInt nre = 0; uInt nrs = 0; for (uInt i=0; itype() == WCBox::className(), AipsError); uInt ndout = outOrder.nelements(); uInt ndreg = regions()[0]->ndim(); AlwaysAssert (ndreg <= ndout, AipsError); // Split the box into the extend and the stretch part. // The IPositions give the axis numbers in the extend box. const WCBox* bptr = dynamic_cast(regions()[1]); AlwaysAssert (bptr != 0, AipsError); IPosition extendBoxAxes; IPosition stretchBoxAxes; IPosition stretchRegAxes; findAxes (extendBoxAxes, stretchBoxAxes, stretchRegAxes); WCBox extbox = bptr->splitBox (extendBoxAxes); WCBox strbox = bptr->splitBox (stretchBoxAxes); // Split the pixelAxesMap and outOrder into the parts for the // region, the stretch box and the extend box (which can be more than // the box itself because there can be extra extend axes). uInt ndstr = stretchBoxAxes.nelements(); uInt ndext = ndout - ndreg; DebugAssert (ndext >= extendBoxAxes.nelements(), AipsError); IPosition regPixMap(ndreg); IPosition regOutOrd(ndreg); IPosition strPixMap(ndstr); IPosition strOutOrd(ndstr); IPosition extPixMap(ndext); IPosition extOutOrd(ndext); // In our axesDesc the first axes are used for the region. for (uInt i=0; i reginx(ndreg); std::vector tmpreg(regOutOrd.begin(), regOutOrd.end()); GenSortIndirect::sort (reginx, &(tmpreg[0]), ndreg); for (uInt i=0; i 0) { Vector extinx(ndext); std::vector tmpext(extOutOrd.begin(), extOutOrd.end()); GenSortIndirect::sort (extinx, &(tmpext[0]), ndext); for (uInt i=0; i 0) { Vector strinx(ndstr); std::vector tmpstr(strOutOrd.begin(), strOutOrd.end()); GenSortIndirect::sort (strinx, &(tmpstr[0]), ndstr); for (uInt i=0; i tmpstretch(stretchAxes.begin(), stretchAxes.end()); GenSortIndirect::sort (strinx, &(tmpstretch[0]), ndstr); for (uInt i=0; itoLCRegionAxes (cSys, shape, regPixMap, regOutOrd); if (ndstr > 0) { LCRegion* boxptr = strbox.toLCRegionAxes (cSys, shape, strPixMap, strOutOrd); LCBox* dboxptr = dynamic_cast(boxptr); AlwaysAssert (dboxptr != 0, AipsError); LCStretch* extptr = new LCStretch (True, regptr, stretchRegAxes, *dboxptr); delete boxptr; regptr = extptr; } if (ndext > 0) { LCRegion* boxptr = extbox.toLCRegionAxes (cSys, shape, extPixMap, extOutOrd); LCBox* dboxptr = dynamic_cast(boxptr); AlwaysAssert (dboxptr != 0, AipsError); LCExtension* extptr = new LCExtension (True, regptr, extendAxes, *dboxptr); delete boxptr; regptr = extptr; } return regptr; } String WCExtension::className() { return "WCExtension"; } String WCExtension::type() const { return className(); } TableRecord WCExtension::toRecord (const String& tableName) const { TableRecord rec; defineRecordFields (rec, className()); rec.defineRecord ("regions", makeRecord(tableName)); return rec; } WCExtension* WCExtension::fromRecord (const TableRecord& rec, const String& tableName) { PtrBlock regions; unmakeRecord (regions, rec.asRecord("regions"), tableName); return new WCExtension (True, regions); } } //# NAMESPACE CASACORE - END casacore-3.7.1/images/Regions/WCExtension.h000066400000000000000000000117131476623553700205610ustar00rootroot00000000000000//# WCExtension.h: Make the extension an image region //# Copyright (C) 1998,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_WCEXTENSION_H #define IMAGES_WCEXTENSION_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations class WCBox; // // Make the extension of an image region. // // // // // //
      • WCCompound // // // The WCExtension class is a specialization of class // WCCompound. // It makes it possible to extend a region along straight lines to // other dimensions. It is also possible to extend existing axes with // length 1, i.e. to stretch such axes. // E.g. a circle in the RA,DEC plane can be extended to // a cylinder in a RA,DEC,FREQ cube. It is possible to extend over // more than one dimension. One can also limit the extension range // E.g. in the forementioned example the circle can be extended // for a given range of frequencies only. //
        The extension axes and ranges have to be given as a // WCBox object. The axes which are part // of the box and the region are the axes to be stretched. Box axes // which are not part of the region are the extension axes. //

        // Note that regions get automatically extended when a region is used // for a higher dimensioned image. The extension is done for all // unknown axes (for their entire length). // // // // // //# //#

      • //# class WCExtension: public WCCompound { public: // Construct the extension of an image region using the axes // and blc,trc given in the extendBox. // The axes in region and box have to be disjoint. WCExtension (const ImageRegion& region, const WCBox& extendBox); // Copy constructor (copy semantics). WCExtension (const WCExtension& other); virtual ~WCExtension(); // Assignment (copy semantics). WCExtension& operator= (const WCExtension& other); // Comparison virtual Bool operator== (const WCRegion& other) const; // Make a copy of the derived object. virtual WCRegion* cloneRegion() const; // Get the class name (to store in the record). static String className(); // Get the region type. Returns className() virtual String type() const; // Convert the (derived) object to a record. virtual TableRecord toRecord (const String& tableName) const; // Convert correct object from a record. static WCExtension* fromRecord (const TableRecord&, const String& tableName); protected: // WCExtension can extend a region if WCBox can do so. virtual Bool canExtend() const; // Convert to an LCRegion using the given coordinate system and shape. // pixelAxesMap(i) gives the pixel axis in cSys of axes i // in the axesDesc. virtual LCRegion* doToLCRegion (const CoordinateSystem& cSys, const IPosition& shape, const IPosition& pixelAxesMap, const IPosition& outOrder) const; private: // Construct from multiple regions given as a Block. // When takeOver is True, the destructor will delete the // given regions. Otherwise a copy of the regions is made. WCExtension (Bool takeOver, const PtrBlock& regions); // Find the axes to be extended and stretched. // The extend axes are the axis numbers in the box. // For the stretch axes both box and region axes are returned. void findAxes (IPosition& extendBoxAxes, IPosition& stretchBoxAxes, IPosition& stretchRegionAxes) const; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Regions/WCIntersection.cc000066400000000000000000000072771476623553700214230ustar00rootroot00000000000000//# WCIntersection.cc: Make the intersection of 2 or more image regions //# Copyright (C) 1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN WCIntersection::WCIntersection (const ImageRegion& region1, const ImageRegion& region2) : WCCompound (region1, region2) {} WCIntersection::WCIntersection (const ImageRegion* region1, const ImageRegion* region2, const ImageRegion* region3, const ImageRegion* region4, const ImageRegion* region5, const ImageRegion* region6, const ImageRegion* region7, const ImageRegion* region8, const ImageRegion* region9, const ImageRegion* region10) : WCCompound (region1, region2, region3, region4, region5, region6, region7, region8, region9, region10) {} WCIntersection::WCIntersection (const PtrBlock& regions) : WCCompound (regions) {} WCIntersection::WCIntersection (Bool takeOver, const PtrBlock& regions) : WCCompound (takeOver, regions) {} WCIntersection::WCIntersection (const WCIntersection& other) : WCCompound (other) {} WCIntersection::~WCIntersection() {} WCIntersection& WCIntersection::operator= (const WCIntersection& other) { if (this != &other) { WCCompound::operator= (other); } return *this; } Bool WCIntersection::operator== (const WCRegion& other) const { return WCCompound::operator== (other); } WCRegion* WCIntersection::cloneRegion() const { return new WCIntersection (*this); } LCRegion* WCIntersection::doToLCRegion (const CoordinateSystem& cSys, const IPosition& shape, const IPosition& pixelAxesMap, const IPosition& outOrder) const { PtrBlock regions; multiToLCRegion (regions, cSys, shape, pixelAxesMap, outOrder); return new LCIntersection (True, regions); } String WCIntersection::className() { return "WCIntersection"; } String WCIntersection::type() const { return className(); } TableRecord WCIntersection::toRecord (const String& tableName) const { TableRecord rec; defineRecordFields (rec, className()); rec.defineRecord ("regions", makeRecord(tableName)); return rec; } WCIntersection* WCIntersection::fromRecord (const TableRecord& rec, const String& tableName) { PtrBlock regions; unmakeRecord (regions, rec.asRecord("regions"), tableName); return new WCIntersection (True, regions); } } //# NAMESPACE CASACORE - END casacore-3.7.1/images/Regions/WCIntersection.h000066400000000000000000000121131476623553700212460ustar00rootroot00000000000000//# WCIntersection.h: Make the intersection of 2 or more image regions //# Copyright (C) 1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_WCINTERSECTION_H #define IMAGES_WCINTERSECTION_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Make the intersection of 2 or more image regions. // // // // // //
      • WCCompound // // // The WCIntersection class is a specialization of class // WCCompound. // It makes it possible to take the intersection of 2 or more image regions. // Note that only world coordinate regions can be used in a compound, // thus an LCSlicer object is not allowed in an intersection. //

        // The intersection of regions is the collection of the pixels // masked-on in all regions. //

        // The regions in a intersection can have different axes and dimensionalities. // The axes and dimensionality of a intersection are determined by the // collection of all different axes in its regions. Each individual region // will be auto-extended along the axes not being part of the region. // E.g. one can define a WCBox with axis RA and another WCBox with // axis DEC. The intersection will be 2-dim with axes RA and DEC. The first // box will be auto-extended to cover the DEC axis, which results // in a 2-dim box with its DEC axis the length of the image's DEC axis. // Similarly the second box will be auto-extended to cover the RA axis. // // // // // //# //#

      • //# class WCIntersection: public WCCompound { public: // Construct the intersection of one or more image regions. // The image regions have to contain WCRegion objects, otherwise an // exception is thrown. // WCIntersection (const ImageRegion& region1, const ImageRegion& region2); WCIntersection (const ImageRegion* region1, const ImageRegion* region2 = 0, const ImageRegion* region3 = 0, const ImageRegion* region4 = 0, const ImageRegion* region5 = 0, const ImageRegion* region6 = 0, const ImageRegion* region7 = 0, const ImageRegion* region8 = 0, const ImageRegion* region9 = 0, const ImageRegion* region10 = 0); WCIntersection (const PtrBlock& regions); // // Construct from multiple regions given as a Block. // When takeOver is True, the destructor will delete the // given regions. Otherwise a copy of the regions is made. WCIntersection (Bool takeOver, const PtrBlock& regions); // Copy constructor (copy semantics). WCIntersection (const WCIntersection& other); virtual ~WCIntersection(); // Assignment (copy semantics). WCIntersection& operator= (const WCIntersection& other); // Comparison virtual Bool operator== (const WCRegion& other) const; // Make a copy of the derived object. virtual WCRegion* cloneRegion() const; // Get the class name (to store in the record). static String className(); // Get the region type. Returns className() virtual String type() const; // Convert the (derived) object to a record. virtual TableRecord toRecord (const String& tableName) const; // Convert correct object from a record. static WCIntersection* fromRecord (const TableRecord&, const String& tableName); protected: // Convert to an LCRegion using the given coordinate system and shape. // pixelAxesMap(i) gives the pixel axis in cSys of axes i // in the axesDesc. virtual LCRegion* doToLCRegion (const CoordinateSystem& cSys, const IPosition& shape, const IPosition& pixelAxesMap, const IPosition& outOrder) const; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Regions/WCLELMask.cc000066400000000000000000000167061476623553700202020ustar00rootroot00000000000000//# WCLELMask.cc: Class to define a mask as a LEL expression //# Copyright (C) 2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN WCLELMask::WCLELMask() : itsImageExpr (0), itsLattExpr (0), itsLattNode (0) {} WCLELMask::WCLELMask (const String& command) : itsCommand (command), itsImageExpr (0), itsLattExpr (0), itsLattNode (0) { processCommand(); } WCLELMask::WCLELMask (const char* command) : itsCommand (command), itsImageExpr (0), itsLattExpr (0), itsLattNode (0) { processCommand(); } WCLELMask::WCLELMask (const ImageExpr& expr) : itsImageExpr (0), itsLattExpr (0), itsLattNode (0) { itsImageExpr = new ImageExpr (expr); const CoordinateSystem& cSys = itsImageExpr->coordinates(); uInt naxes = itsImageExpr->ndim(); for (uInt i=0; i& expr) : itsImageExpr (0), itsLattExpr (0), itsLattNode (0) { itsLattExpr = new LatticeExpr (expr); } WCLELMask::WCLELMask (const LatticeExprNode& expr) : itsImageExpr (0), itsLattExpr (0), itsLattNode (0) { init (expr); } WCLELMask::WCLELMask (const WCLELMask& that) : WCRegion (), itsImageExpr (0), itsLattExpr (0), itsLattNode (0) { operator= (that); } WCLELMask::~WCLELMask() { delete itsImageExpr; delete itsLattExpr; delete itsLattNode; } void WCLELMask::processCommand() { try { LatticeExprNode expr = ImageExprParse::command (itsCommand); init (expr); } catch (std::exception& x) { throw AipsError (std::string(x.what()) + "\n Error in creating WCLELMask"); } } void WCLELMask::init (const LatticeExprNode& expr) { // Get the shape and CoordinateSystem of the expression const IPosition shapeOut = expr.shape(); const LELAttribute& attr = expr.getAttribute(); const LELLattCoordBase& lattCoord = attr.coordinates().coordinates(); if (! lattCoord.hasCoordinates()) { // No coordinates, so it is a lattice expression. if (expr.shape().nelements() == 0) { // Shape is unknown, so keep it as a plain expression. itsLattNode = new LatticeExprNode(expr); } else { // Turn it into a proper lattice type. itsLattExpr = new LatticeExpr(expr); } } else { // Coordinates are known, so make it a proper Image type. itsImageExpr = new ImageExpr (expr, itsCommand); const CoordinateSystem& cSys = itsImageExpr->coordinates(); uInt naxes = itsImageExpr->ndim(); for (uInt i=0; i (*that.itsImageExpr); } if (that.itsLattExpr != 0) { itsLattExpr = new LatticeExpr (*that.itsLattExpr); } if (that.itsLattNode != 0) { itsLattNode = new LatticeExprNode (*that.itsLattNode); } } return *this; } Bool WCLELMask::operator== (const WCRegion& that) const { // Type check if (type() != that.type()) return False; // Base class if (!WCRegion::operator== (that)) return False; // Cast const WCLELMask& That = dynamic_cast(that); // Check private data if (itsCommand != That.itsCommand) return False; if (itsCommand.empty()) { if (itsImageExpr != That.itsImageExpr) return False; if (itsLattExpr != That.itsLattExpr) return False; if (itsLattNode != That.itsLattNode) return False; } return True; } WCRegion* WCLELMask::cloneRegion() const { return new WCLELMask(*this); } uInt WCLELMask::ndim() const { if (itsLattExpr != 0) { return itsLattExpr->ndim(); } if (itsImageExpr != 0) { return itsImageExpr->ndim(); } return 0; } TableRecord WCLELMask::toRecord(const String&) const { // Create record TableRecord rec; defineRecordFields(rec, className()); rec.define ("expr", itsCommand); return rec; } WCLELMask* WCLELMask::fromRecord (const TableRecord& rec, const String&) { // Get the expression. String command = rec.asString ("expr"); return new WCLELMask(command); } Bool WCLELMask::canExtend() const { return False; } LCRegion* WCLELMask::toLCRegion (const CoordinateSystem& cSys, const IPosition& latticeShape) const { if (itsImageExpr != 0) { return WCRegion::toLCRegion (cSys, latticeShape); } if (itsLattNode != 0) { return new LCLELMask (LatticeExpr(*itsLattNode, latticeShape)); } if (! latticeShape.isEqual (itsLattExpr->shape())) { throw AipsError ("WCLELMask::toLCRegion - " "shapes of mask (lattice) expression and image mismatch"); } return new LCLELMask (*itsLattExpr); } LCRegion* WCLELMask::doToLCRegion (const CoordinateSystem&, const IPosition& latticeShape, const IPosition& pixelAxesMap, const IPosition& outOrder) const { AlwaysAssert (itsImageExpr != 0, AipsError); const uInt naxes = pixelAxesMap.nelements(); const IPosition& shape = itsImageExpr->shape(); AlwaysAssert (naxes == shape.nelements(), AipsError); for (uInt i=1; iexpression()); } String WCLELMask::className() { return "WCLELMask"; } String WCLELMask::type() const { return className(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/images/Regions/WCLELMask.h000066400000000000000000000136261476623553700200420ustar00rootroot00000000000000//# WCLELMask.h: Class to define a mask as a LEL expression //# Copyright (C) 2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_WCLELMASK_H #define IMAGES_WCLELMASK_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class LCRegion; class TableRecord; class IPosition; template class ImageExpr; template class LatticeExpr; class LatticeExprNode; // // Class to define a mask as a LEL expression // // // // // //
      • WCRegion //
      • ImageExpr // // // The WCLELMask class is a specialization of class // WCRegion. //
        // It can be used to define an on-the-fly mask for an image // using a boolean LatticeExpr. // The contents of the mask are calculated on the fly from the expression. // Thus the mask may change if the data in the image(s) used in the // expression change. // // This mask is only persistent if constructed from an expression string. // When constructed from an ImageExpr // the mask is not persistent. // //
        // // // // Users must be able to specify a mask based on an expression. // //# //#
      • //# class WCLELMask : public WCRegion { public: WCLELMask(); // Construct from the given expression command. // The command will be parsed and converted to an ImageExpr. // explicit WCLELMask (const String& command); explicit WCLELMask (const char* command); // // Construct from the given image expression. explicit WCLELMask (const ImageExpr& expr); // Construct from the given lattice expression. explicit WCLELMask (const LatticeExpr& expr); // Construct from the given lattice expression. // This constructor makes it possible to have an expression with an // unknown shape (e.g. using LEL function INDEXIN). // If the shape is known, the LatticeExprNode will be converted to // a LatticeExpr. explicit WCLELMask (const LatticeExprNode& expr); // Copy constructor (copy semantics). WCLELMask (const WCLELMask& other); // Destructor virtual ~WCLELMask(); // Assignment (copy semantics) WCLELMask& operator= (const WCLELMask& other); // Comparison virtual Bool operator== (const WCRegion& other) const; // Clone a WCLELMask object. virtual WCRegion* cloneRegion() const; // Get the dimensionality (i.e. the number of axes). virtual uInt ndim() const; // WCLELMask cannot extend a region. virtual Bool canExtend() const; // Convert to an LCRegion using the given new coordinate system and shape. // If the region has coordinates, the WCRegion implementation will // be called. Otherwise the LatticeExpr is returned after checking // that the shape matches. virtual LCRegion* toLCRegion (const CoordinateSystem& cSys, const IPosition& latticeShape) const; // Convert to an LCRegion using the supplied CoordinateSystem // and shape. // It checks that coordinates match and that axes are not swapped. virtual LCRegion* doToLCRegion (const CoordinateSystem& cSys, const IPosition& latticeShape, const IPosition& pixelAxesMap, const IPosition& outOrder) const; // Convert the WCLELMask object to a record. // The record can be used to make the object persistent. // The tableName argument can be used by derived // classes (e.g. LCPagedMask) to put very large objects. virtual TableRecord toRecord (const String& tableName) const; // Convert to a WCLELMask from a record. static WCLELMask* fromRecord (const TableRecord& rec, const String& tableName); // Returns WCLELMask static String className(); // Return region type. Returns the class name virtual String type() const; const ImageExpr* getImageExpr() const {return itsImageExpr;} private: // Process the command. void processCommand(); // Initialize as a LatticeExprNode if expression's shape is unknown. // Otherwise as a LatticeExpr if coordinates are unknown. // Otherwise as an ImageExpr. void init (const LatticeExprNode& expr); String itsCommand; ImageExpr* itsImageExpr; LatticeExpr* itsLattExpr; LatticeExprNode* itsLattNode; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Regions/WCPolygon.cc000066400000000000000000000357271476623553700204050ustar00rootroot00000000000000//# WCPolygon.cc: Class to define a 2D polygonal world coordinate region of interest //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN WCPolygon::WCPolygon() // //Default constructor // : itsNull(True) { unitInit(); } WCPolygon::WCPolygon(const Quantum >& x, const Quantum >& y, const IPosition& pixelAxes, const CoordinateSystem& cSys, const RegionType::AbsRelType absRel) : itsX(x), itsY(y), itsPixelAxes(pixelAxes), itsCSys(cSys), itsAbsRel(absRel), itsNull(False) // { AlwaysAssert (itsCSys.nPixelAxes() >= 2, AipsError); AlwaysAssert (itsCSys.nWorldAxes() >= 2, AipsError); String msg; // Vector xV = itsX.getValue(); Vector yV = itsY.getValue(); if (xV.nelements() != yV.nelements()) { msg = String("WCPolygon - the X and Y vectors must be the same length"); throw (AipsError (msg)); } if (xV.nelements() < 3) { msg = String("WCPolygon - you must give at least 3 vertices"); throw (AipsError (msg)); } if (itsPixelAxes.nelements() != 2) { msg = String("WCPolygon - you must give 2 pixel axes"); throw (AipsError (msg)); } if (itsPixelAxes(0) > Int(itsCSys.nPixelAxes()-1) || itsPixelAxes(1) > Int(itsCSys.nPixelAxes()-1)) { msg = String("WCPolygon - the specified pixel axes are greater than") + String("the number of pixel axes in the CoordinateSystem"); throw (AipsError (msg)); } if (itsPixelAxes(0) == itsPixelAxes(1)) { msg = String("WCPolygon - you have specified the same pixel axis twice !"); throw (AipsError (msg)); } // Check axes/units unitInit(); Vector units(2); units[0] = itsX.getUnit(); units[1] = itsY.getUnit(); checkAxes (itsPixelAxes, itsCSys, units); // Create the axis descriptions. for (uInt i=0; i= 2, AipsError); AlwaysAssert (itsCSys.nWorldAxes() >= 2, AipsError); String msg; // if (itsPixelAxes.nelements() != 2) { msg = String("WCPolygon - you must give 2 pixel axes"); throw (AipsError (msg)); } if (itsPixelAxes(0) > Int(itsCSys.nPixelAxes()-1) || itsPixelAxes(1) > Int(itsCSys.nPixelAxes()-1)) { msg = String("WCPolygon - the specified pixel axes are greater than") + String("the number of pixel axes in the CoordinateSystem"); throw (AipsError (msg)); } if (itsPixelAxes(0) == itsPixelAxes(1)) { msg = String("WCPolygon - you have specified the same pixel axis twice !"); throw (AipsError (msg)); } Vector worldAxes(2); worldAxes(0) = itsCSys.pixelAxisToWorldAxis(pixelAxes(0)); worldAxes(1) = itsCSys.pixelAxisToWorldAxis(pixelAxes(1)); if (worldAxes(0) == -1) { throw (AipsError ("WCPolygon - pixelAxes(0) has no corresponding world axis")); } if (worldAxes(1) == -1) { throw (AipsError ("WCPolygon - pixelAxes(1) has no corresponding world axis")); } // Get polygon x and y Vector xP = polyLC.x(); Vector yP = polyLC.y(); // Create vectors for conversions Vector world(itsCSys.nWorldAxes()); Vector pixel(itsCSys.referencePixel().copy()); String xUnits = itsCSys.worldAxisUnits()(worldAxes(0)); String yUnits = itsCSys.worldAxisUnits()(worldAxes(1)); // Convert to world Vector xW(xP.nelements()); Vector yW(yP.nelements()); uInt i; for (i=0; i >(xW, xUnits); itsY = Quantum >(yW, yUnits); // Init units unitInit(); // Create the axis descriptions. for (i=0; i x1 = itsX.getValue(); Vector y1 = itsY.getValue(); Vector x2 = that.itsX.getValue(); Vector y2 = that.itsY.getValue(); if (x1.nelements() != x2.nelements()) return False; if (y1.nelements() != y2.nelements()) return False; // uInt i; for (i=0; i pixelAxes(nAxes); pixelAxes = (itsPixelAxes+1).asVector(); rec.define ("pixelAxes", pixelAxes); // Save polygon. Convert abspix to one rel { Vector tmp(itsX.getValue()); String units = itsX.getUnit(); if (units == "pix" && itsAbsRel == RegionType::Abs) { for (uInt i=0; i > tmpQ(itsX); tmpQ.setValue(tmp); // QuantumHolder h(tmpQ); TableRecord rec2; String error; if (!h.toRecord(error, rec2)) { throw (AipsError ("WCPolygon::toRecord - could not save X Quantum vector because "+error)); } rec.defineRecord("x", rec2); } { Vector tmp(itsY.getValue()); String units = itsY.getUnit(); if (units == "pix" && itsAbsRel == RegionType::Abs) { for (uInt i=0; i > tmpQ(itsY); tmpQ.setValue(tmp); // QuantumHolder h(tmpQ); TableRecord rec2; String error; if (!h.toRecord(error, rec2)) { throw (AipsError ("WCPolygon::toRecord - could not save Y Quantum vector because "+error)); } rec.defineRecord("y", rec2); } // rec.define ("absrel", Int(itsAbsRel)); if (!itsCSys.save(rec, "coordinates")) { throw (AipsError ("WCPolygon::toRecord: could not save Coordinate System")); } // return rec; } WCPolygon* WCPolygon::fromRecord (const TableRecord& rec, const String&) { // Get CoordinateSystem unitInit(); CoordinateSystem* pCSys = CoordinateSystem::restore(rec,"coordinates"); Bool oneRel = rec.asBool("oneRel"); RegionType::AbsRelType absRel = RegionType::AbsRelType(rec.asInt("absrel")); // Get pixel axes and convert to zero rel. Vector tmp = Vector(rec.toArrayInt ("pixelAxes")); IPosition pixelAxes(tmp); if (oneRel) pixelAxes -= 1; // Get the polygon Quantum > xQ; Quantum > yQ; String error, units; // { QuantumHolder h; const RecordInterface& subRecord = rec.asRecord("x"); if (!h.fromRecord(error, subRecord)) { throw (AipsError ("WCPolygon::fromRecord - could not recover X Quantum vector because "+error)); } xQ = h.asQuantumVectorDouble(); units = xQ.getUnit(); // Convert from 1-rel to 0-rel for absolute pixel units if (units=="pix" && absRel==RegionType::Abs && oneRel) { Vector x = xQ.getValue(); for (uInt i=0; i y = yQ.getValue(); for (uInt i=0; i units = cSys.worldAxisUnits(); // Bool xIsWorld = True; Bool yIsWorld = True; Vector xValue; if (xUnits!="pix" && xUnits!="frac") { xValue = itsX.getValue(units(xWorldAxis)); } else { xIsWorld = False; xValue = itsX.getValue(); } Vector yValue; if (yUnits!="pix" && yUnits!="frac") { yValue = itsY.getValue(units(yWorldAxis)); } else { yIsWorld = False; yValue = itsY.getValue(); } // Prepare world and pixel vectors for conversion per vertex const uInt nValues = xValue.nelements(); Vector xLC(nValues); Vector yLC(nValues); Vector world(cSys.referenceValue().copy()); Vector pixel(cSys.nPixelAxes()); Vector absRel(cSys.nWorldAxes()); absRel = RegionType::Abs; absRel(xWorldAxis) = absRel(yWorldAxis) = itsAbsRel; // Vector refPix = cSys.referencePixel(); for (uInt i=0; i #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class LCRegion; class LCPolygon; class TableRecord; class IPosition; // // Class to define a 2-D polygonal world coordinate region in an image. // // // // // // // // //
      • WCRegion //
      • LCRegion //
      • CoordinateSystem // // // // The corners of the 2-D polygon are given by world coordinates. The // vertices are connected by straight lines in lattice coordinates. // // All this class does, apart from constructing itself, is know // how to save itself to a Record and how to convert itself // to an LCRegion. The conversion allows you to apply // a WCPolygon constructed with one CoordinateSystem // to another CoordinateSystem. That is, you can apply a // WCPolygon from this image to that image. // // At construction, it is assumed that the units of the world // coordinates are the same as those encapsulated in the // construction CoordinateSystem. You must tell // the constructor, which world axes the x and vectors // are associated with. Make sure you account for reordering. // For example, if you reordered [ra,dec] to be [dec,ra] // with the CoordinateSystem::transpose(,) fuction // and wished the x vector to be ra, and the y vector to // be dec, then worldAxes=[1,0]. // // The CoordinateSystem supplied to the toLCRegion // (which returns a pointer to an LCPolygongon object) // function does not have to be identical in structure to that with // which the WCPolygon was constructed. However, each world // axis given in the worldAxes vector at construction must be present // somewhere (order is unimportant) in the supplied CoordinateSystem. // // // The supplied lattice shape must be 2-D and corresponds to the // pixel axes of the two world axes of the supplied // CoordinateSystem which match those of the construction // CoordinateSystem. // // // // // Let us give some examples with pseudo-code. // cSys is the construction CoordinateSystem // and cSys2 is the supplied CoordinateSystem. // We list their world axes in the square brackets. // The construction polygon values don't matter. // Similarly, the values of shape don't matter // as long as there are 2 of them. // // cSys = [ra, dec, freq]; // cSys2 = [ra, dec]; // axes=[0,1]; // shape = [,]; // WCPolygon poly(x, y, axes, cSys); // LCRegion* pR = poly.toLCRegion(cSys2, shape); // // The resultant LCPolygon will have vertices converted // with the [ra, dec] axes from cSys2 // // // // // // cSys = [ra, dec, freq]; // cSys2 = [ra, dec]; // axes=[0,2]; // shape = [,]; // WCPolygon poly(x, y, axes, cSys); // LCRegion* pR = poly.toLCRegion(cSys2, shape); // // This will throw an exception because the [freq] axis // is missing in cSys2 // // // // In this example we make it a bit harder by // reordering the pixel axes too. The new order // of the pixel axes in terms of the original // order [0,1,2...] is given after the world axes // // // cSys = [ra, dec, freq]; // cSys2 = [stokes, freq, ra, dec], [3,2,1,0]; // axes=[1,2]; // shape = [,]; // WCPolygon poly(x, y, axes, cSys); // LCRegion* pR = poly.toLCRegion(cSys2, shape); // // The resultant LCPolygon will have vertices converted // with the [ra, dec] axes from cSys2. The fact that // the pixel axes of cSys2 were reordered is accounted // for internally, but does not extrude further. // // // // In this example we make it a bit harder by // remove a pixel axis. // // // cSys = [ra, dec, freq]; // cSys2 = [stokes, freq, ra, dec]; // cSys2.removePixelAxis(1, cSys2.referencePixel()(1)); // axes=[1,2]; // shape = [,]; // WCPolygon poly(x, y, axes, cSys); // LCRegion* pR = poly.toLCRegion(cSys2, shape); // // This will throw an exception because the removed // pixel axis, pixel axis number 1, // corresponds to the [freq] world axis // in cSys2, and the [freq] axis is one of those // specified at construction. Although the world // axis is still present, it is not possible to // convert to a pixel coordinate if the pixel axis // is not there. // // // // Users must be able to specify regions in world as well as lattice // coordinates. // // // // In all the constructors, you have to specifiy which plane // the polygon lies in. You do this by specifying the *PIXEL AXES* // (not the world axes) as this is the natural thing the user // will want to specify. // // // // For the constructors specifying the world values as simple doubles, // it is *ASSUMED* that the units of those doubles are the same as // the native units of the CoordinateSystem for each axis. // // // // World coordinates may be specified as absolute or offset. If the // latter, they are offset with respect to the reference pixel of // the CoordinateSystem. // // // //
      • // class WCPolygon : public WCRegion { public: WCPolygon(); // Construct from two vectors of world coordinates // defining the polygon vertices. // WCPolygon(const Quantum >& x, const Quantum >& y, const IPosition& pixelAxes, const CoordinateSystem& cSys, const RegionType::AbsRelType absRel=RegionType::Abs); // // Construct from an LCPolygon. WCPolygon(const LCPolygon& polygon, const IPosition& pixelAxes, const CoordinateSystem& cSys); // Copy constructor (reference semantics). WCPolygon (const WCPolygon& other); // Destructor virtual ~WCPolygon(); // Assignment (copy semantics) WCPolygon& operator= (const WCPolygon& other); // Comparison virtual Bool operator==(const WCRegion& other) const; // Clone a WCPolygon object. virtual WCRegion* cloneRegion() const; // WCPolygon cannot extend a region. virtual Bool canExtend() const; // Convert to an LCRegion using the given coordinate system. virtual LCRegion* doToLCRegion (const CoordinateSystem& cSys, const IPosition& latticeShape, const IPosition& pixelAxesMap, const IPosition& outOrder) const; // Convert the WCPolygon object to a record. // The record can be used to make the object persistent. // The tableName argument can be used by derived // classes (e.g. LCPagedMask) to put very large objects. virtual TableRecord toRecord(const String& tableName) const; // Convert to a WCPolygon from a record. static WCPolygon* fromRecord (const TableRecord& rec, const String& tableName); // Returns "WCPolygon" static String className(); // Return region type. Returns the class name virtual String type() const; protected: Quantum > itsX; Quantum > itsY; IPosition itsPixelAxes; CoordinateSystem itsCSys; RegionType::AbsRelType itsAbsRel; Bool itsNull; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Regions/WCRegion.cc000066400000000000000000000272231476623553700201710ustar00rootroot00000000000000//# WCRegion.cc: Class to define a region of interest in an image //# Copyright (C) 1998,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN WCRegion::WCRegion() {} WCRegion::WCRegion (const WCRegion& other) : itsComment (other.itsComment), itsAxesDesc (other.itsAxesDesc) {} WCRegion& WCRegion::operator= (const WCRegion& other) { if (this != &other) { itsComment = other.itsComment; itsAxesDesc = other.itsAxesDesc; } return *this; } WCRegion::~WCRegion() {} Bool WCRegion::operator== (const WCRegion& other) const { // Type check. return (type() == other.type()); } uInt WCRegion::ndim() const { return itsAxesDesc.nfields(); } void WCRegion::defineRecordFields (RecordInterface& record, const String& className) const { record.define ("isRegion", Int(RegionType::WC)); record.define ("name", className); record.define ("comment", itsComment); } const Record& WCRegion::getAxisDesc (uInt axis) const { AlwaysAssert (axis < itsAxesDesc.nfields(), AipsError); return itsAxesDesc.subRecord (axis); } Int WCRegion::axisNr (const Record& desc, const Record& axesDesc) const { uInt nf = axesDesc.nfields(); for (uInt i=0; i names = cSys.coordinate(coord).worldAxisNames(); axisrec.define ("name", names(axisInCoord)); } return axisrec; } Record WCRegion::makeAxesDesc (const CoordinateSystem& cSys) const { Record desc; for (uInt i=0; i=n). // outOrder(i) gives output axis of axis i. // pixelAxesMap(i) gives cSys/shape axis of axis i. // First determine along which axes the region has to be extended. uInt ndreg = itsAxesDesc.nfields(); uInt ndout = pixelAxesMap.nelements(); DebugAssert (ndout>=ndreg, AipsError); // If no extension is needed or if the region can extend itself, // life is simple. if (ndout == ndreg || canExtend()) { return doToLCRegion (cSys, shape, pixelAxesMap, outOrder); } // We have to make the extension here. // So split the IPositions into the region part and the extension part. IPosition pixAxesMap(ndreg); IPosition outOrd(ndreg); IPosition extendAxes(ndout-ndreg); IPosition extendShape(ndout-ndreg); Vector inx(ndreg); std::vector tmp(outOrder.begin(), outOrder.end()); GenSortIndirect::sort (inx, &(tmp[0]), ndreg); for (i=0; i& world, const Vector& absRel, const CoordinateSystem& cSys, const IPosition& shape) const { // For values that are already absolute, temporarily use rel = 0 // The absrel vector may have any length from 0 to nWorld // Any relative values may be relative to ref val or image centre // First deal with relative to reference value Vector ar(world.nelements()); const uInt nAR = absRel.nelements(); Vector t(world.copy()); // for (uInt i=0; i t2(t.copy()); // Convert to absolute at reference pixel cSys.makeWorldAbsolute(t); // Now deal with relative to the image centre Vector p(shape.nelements()); for (uInt i=0; i w; if (!cSys.toWorld(w,p)) { throw (AipsError (cSys.errorMessage())); } // Make absolute w.r.t. the reference location in 'w' cSys.makeWorldAbsoluteRef (t2, w); // Overwrite result for relative values. for (uInt i=0; i& quantityUnits ) const { // Make sure we have world axes for these pixel axes Vector worldAxes(pixelAxes.size()); Vector units = cSys.worldAxisUnits(); for (uInt i=0; i #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class LCRegion; class RecordInterface; class IPosition; class String; // // Base class to define world coordinate regions of interest in an image. // // // // // // //
      • LCRegion // // // // WCRegion is the base class for world coordinate regions. // The axes in a WCRegion have names (e.g. RA, DEC, FREQ) and // carry sometimes an associated reference frame with it. // An WCRegion object is converted to the appropriate // LCRegion object when they // are used to take a subset from an image. // LCRegion's are pixel based and are // used to access the correct pixels in the image. // The conversion has the following rules: //
          //
        1. All axes of the region must be axes in the image. //
        2. An image axis does not have to be an axis in the region. // Thus the image can have a higher dimensionality than the region. // If that is the case, the region is auto-extended to the image's // dimensionality by using the full range for those axes. //
        3. The order of the axes in region and image do not have to // be the same. They get reordered as needed. //
        //
        // // // // // // // // User should be able to specify their regions in world coordinates // as well as lattice coordinates. // // //# //#
      • //# class WCRegion { public: WCRegion(); // Copy constructor (copy semantics). WCRegion (const WCRegion& other); // Destructor virtual ~WCRegion(); // Comparison // virtual Bool operator==(const WCRegion& other) const; Bool operator!=(const WCRegion& other) const; // // Clone a WCRegion object. virtual WCRegion* cloneRegion() const = 0; // Return region type. // Just returns the class name of the derived class. virtual String type() const = 0; // Get the dimensionality (i.e. the number of axes). // Note that usually all axes have a description, but in some cases // (e.g. WCLELMask) that may not be the case. // The default implementation returns the number of axes in the // axes description. virtual uInt ndim() const; // Get the description of all axes. const Record& getAxesDesc() const; // Get the description of the given axis. // It is a record containing some fields describing the axis. const Record& getAxisDesc (uInt axis) const; // Return the axis number of the description of an axis in the full // axes description. // -1 is returned if not found. Int axisNr (const Record& desc, const Record& axesDesc) const; // Are both axis descriptions equal? Bool isAxisDescEqual (const Record& desc1, const Record& desc2) const; // Can the region extend itself? // By default it cannot. virtual Bool canExtend() const; // Get or set the comment. // const String& comment() const; void setComment (const String& comment); // // Convert to an LCRegion using the given new coordinate system and shape. // An exception is thrown if the region's dimensionality is more // than the length of the shape vector or if an axis in the region // is unknown in the new coordinate system.. // When less, the default implementation extends the region over the // remaining axes. //
        If the region does not need to have coordinates (like WCLELMask) // the function has to be overridden. virtual LCRegion* toLCRegion (const CoordinateSystem& cSys, const IPosition& shape) const; // Convert to an LCRegion using the given coordinate system and shape. // This function is meant for internal use by WCCompound objects. //
        pixelAxesMap(i) is the axis in cSys and shape for region axis i. //
        outOrder(i) is the axis in the output LCRegion for region axis i. //
        The length of pixelAxesMap and outOrder is the dimensionality of // the output LCRegion. It can be more than the dimensionality of this // WCRegion object. In that case the region gets extended along the // latter axes. If the region cannot extend itself, this function // will create an LCExtension object to extend the region. //
        Note that initially pixelAxisMap and outOrder are the same, // but when called for regions in compound regions they may start // to differ. LCRegion* toLCRegionAxes (const CoordinateSystem& cSys, const IPosition& shape, const IPosition& pixelAxesMap, const IPosition& outOrder) const; // Convert the (derived) object to a record. // The record can be used to make the object persistent. // The tableName argument can be used by derived // classes (e.g. LCPagedMask) to put very large objects. virtual TableRecord toRecord(const String& tableName) const = 0; // Convert correct object from a record. static WCRegion* fromRecord (const TableRecord& rec, const String& tableName); // Define the type and class name in the record. void defineRecordFields (RecordInterface& record, const String& className) const; protected: // Assignment (copy semantics) makes only sense for a derived class. WCRegion& operator= (const WCRegion& other); // Add an axis with its description. // An exception is thrown if the axis already exists in this region. void addAxisDesc (const Record& axisDesc); // Make a description of a pixel axis in the coordinate system. Record makeAxisDesc (const CoordinateSystem& cSys, uInt pixelAxis) const; // Make a description of all pixel axes in the coordinate system // (in pixel axes order). Record makeAxesDesc (const CoordinateSystem& cSys) const; // Convert to an LCRegion using the given coordinate system and shape. //
        pixelAxesMap(i) is the axis in cSys and shape for region axis i. //
        outOrder(i) is the axis in the output LCRegion for region axis i. //
        They always have the same length. // If the region can extend itself, the length of pixelAxesMap and // outOrder can be more than the dimensionality of the region. // The latter axes in them are the extension axes. virtual LCRegion* doToLCRegion (const CoordinateSystem& cSys, const IPosition& shape, const IPosition& pixelAxesMap, const IPosition& extendAxes) const = 0; // Convert relative to absolute world as needed void makeWorldAbsolute (Vector& world, const Vector& absRel, const CoordinateSystem& cSys, const IPosition& shape) const; static void unitInit(); void checkAxes ( const IPosition& pixelAxes, const CoordinateSystem& cSys, const Vector& quantityUnits ) const; static void convertPixel( Double& pixel, const Double& value, const String& unit, const Int absRel, const Double refPix, const Int shape ); private: String itsComment; Record itsAxesDesc; }; inline Bool WCRegion::operator!= (const WCRegion& other) const { return (!operator==(other)); } inline const String& WCRegion::comment() const { return itsComment; } inline void WCRegion::setComment (const String& comment) { itsComment = comment; } inline const Record& WCRegion::getAxesDesc() const { return itsAxesDesc; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Regions/WCRegion2.cc000066400000000000000000000073661476623553700202610ustar00rootroot00000000000000//# WCRegion.cc: Implementation of WCRegion::fromRecord //# Copyright (C) 1998,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN WCRegion* WCRegion::fromRecord (const TableRecord& rec, const String& tableName) { if (!rec.isDefined("isRegion") || rec.asInt("isRegion") != RegionType::WC) { throw (AipsError ("WCRegion::fromRecord - " "record does not contain an WC region")); } const String& name = rec.asString ("name"); WCRegion* regPtr = 0; if (name == WCBox::className()) { regPtr = WCBox::fromRecord (rec, tableName); /// } else if (name == WCEllipsoid::className()) { /// regPtr = WCEllipsoid::fromRecord (rec, tableName); } else if (name == WCPolygon::className()) { regPtr = WCPolygon::fromRecord (rec, tableName); } else if (name == WCEllipsoid::className()) { regPtr = WCEllipsoid::fromRecord (rec, tableName); } else if (name == WCLELMask::className()) { regPtr = WCLELMask::fromRecord (rec, tableName); } else if (name == WCUnion::className()) { regPtr = WCUnion::fromRecord (rec, tableName); } else if (name == WCIntersection::className()) { regPtr = WCIntersection::fromRecord (rec, tableName); } else if (name == WCDifference::className()) { regPtr = WCDifference::fromRecord (rec, tableName); } else if (name == WCComplement::className()) { regPtr = WCComplement::fromRecord (rec, tableName); } else if (name == WCExtension::className()) { regPtr = WCExtension::fromRecord (rec, tableName); } else if (name == WCConcatenation::className()) { regPtr = WCConcatenation::fromRecord (rec, tableName); } else { throw (AipsError ("WCRegion::fromRecord - " + name + " is unknown derived WCRegion class")); } if (rec.isDefined ("comment")) { regPtr->setComment (rec.asString ("comment")); } return regPtr; } } //# NAMESPACE CASACORE - END casacore-3.7.1/images/Regions/WCUnion.cc000066400000000000000000000067511476623553700200410ustar00rootroot00000000000000//# WCUnion.cc: Make the union of 2 or more image regions //# Copyright (C) 1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN WCUnion::WCUnion (const ImageRegion& region1, const ImageRegion& region2) : WCCompound (region1, region2) {} WCUnion::WCUnion (const ImageRegion* region1, const ImageRegion* region2, const ImageRegion* region3, const ImageRegion* region4, const ImageRegion* region5, const ImageRegion* region6, const ImageRegion* region7, const ImageRegion* region8, const ImageRegion* region9, const ImageRegion* region10) : WCCompound (region1, region2, region3, region4, region5, region6, region7, region8, region9, region10) {} WCUnion::WCUnion (const PtrBlock& regions) : WCCompound (regions) {} WCUnion::WCUnion (Bool takeOver, const PtrBlock& regions) : WCCompound (takeOver, regions) {} WCUnion::WCUnion (const WCUnion& other) : WCCompound (other) {} WCUnion::~WCUnion() {} WCUnion& WCUnion::operator= (const WCUnion& other) { if (this != &other) { WCCompound::operator= (other); } return *this; } Bool WCUnion::operator== (const WCRegion& other) const { return WCCompound::operator== (other); } WCRegion* WCUnion::cloneRegion() const { return new WCUnion (*this); } LCRegion* WCUnion::doToLCRegion (const CoordinateSystem& cSys, const IPosition& shape, const IPosition& pixelAxesMap, const IPosition& outOrder) const { PtrBlock regions; multiToLCRegion (regions, cSys, shape, pixelAxesMap, outOrder); return new LCUnion (True, regions); } String WCUnion::className() { return "WCUnion"; } String WCUnion::type() const { return className(); } TableRecord WCUnion::toRecord (const String& tableName) const { TableRecord rec; defineRecordFields (rec, className()); rec.defineRecord ("regions", makeRecord(tableName)); return rec; } WCUnion* WCUnion::fromRecord (const TableRecord& rec, const String& tableName) { PtrBlock regions; unmakeRecord (regions, rec.asRecord("regions"), tableName); return new WCUnion (True, regions); } } //# NAMESPACE CASACORE - END casacore-3.7.1/images/Regions/WCUnion.h000066400000000000000000000121441476623553700176740ustar00rootroot00000000000000//# WCUnion.h: Make the union of 2 or more image regions //# Copyright (C) 1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef IMAGES_WCUNION_H #define IMAGES_WCUNION_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Make the union of 2 or more image regions. // // // // // //
      • WCCompound // // // The WCUnion class is a specialization of class // WCCompound. // It makes it possible to take the union of 2 or more image regions. // Note that only world coordinate regions can be used in a compound, // thus an LCSlicer object is not allowed in a union. //

        // The union of regions is the collection of the pixels masked-on // in any region. // Note that the bounding box of a union is determined by the outermost // pixels. It means that if regions are very far apart, the bounding // box of the union is very large and therefore inefficient. //

        // The regions in a union can have different axes and dimensionalities. // The axes and dimensionality of a union are determined by the // collection of all different axes in its regions. Each individual region // will be auto-extended along the axes not being part of the region. // E.g. one can define a WCBox with axis RA and another WCBox with // axis DEC. The union will be 2-dim with axes RA and DEC. The first // box will be auto-extended to cover the DEC axis, which results // in a 2-dim box with its DEC axis the length of the image's DEC axis. // Similarly the second box will be auto-extended to cover the RA axis. // // // // // //# //#

      • //# class WCUnion: public WCCompound { public: // Construct the union of one or more image regions. // The image regions have to contain WCRegion objects, otherwise an // exception is thrown. // WCUnion (const ImageRegion& region1, const ImageRegion& region2); WCUnion (const ImageRegion* region1, const ImageRegion* region2 = 0, const ImageRegion* region3 = 0, const ImageRegion* region4 = 0, const ImageRegion* region5 = 0, const ImageRegion* region6 = 0, const ImageRegion* region7 = 0, const ImageRegion* region8 = 0, const ImageRegion* region9 = 0, const ImageRegion* region10 = 0); WCUnion (const PtrBlock& regions); // // Construct from multiple regions given as a Block. // When takeOver is True, the destructor will delete the // given regions. Otherwise a copy of the regions is made. WCUnion (Bool takeOver, const PtrBlock& regions); // Copy constructor (copy semantics). WCUnion (const WCUnion& other); virtual ~WCUnion(); // Assignment (copy semantics). WCUnion& operator= (const WCUnion& other); // Comparison virtual Bool operator== (const WCRegion& other) const; // Make a copy of the derived object. virtual WCRegion* cloneRegion() const; // Get the class name (to store in the record). static String className(); // Get the region type. Returns className() virtual String type() const; // Convert the (derived) object to a record. virtual TableRecord toRecord (const String& tableName) const; // Convert correct object from a record. static WCUnion* fromRecord (const TableRecord&, const String& tableName); protected: // Convert to an LCRegion using the given coordinate system and shape. // pixelAxesMap(i) gives the pixel axis in cSys of axes i // in the axesDesc. virtual LCRegion* doToLCRegion (const CoordinateSystem& cSys, const IPosition& shape, const IPosition& pixelAxesMap, const IPosition& outOrder) const; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/images/Regions/test/000077500000000000000000000000001476623553700171565ustar00rootroot00000000000000casacore-3.7.1/images/Regions/test/CMakeLists.txt000066400000000000000000000011111476623553700217100ustar00rootroot00000000000000set (datafiles imregion.fits imregion_dironly.fits imregion_nospec.fits ) foreach (file ${datafiles}) configure_file (${CMAKE_CURRENT_SOURCE_DIR}/${file} ${CMAKE_CURRENT_BINARY_DIR}/${file} COPYONLY) endforeach (file) set (tests tImageRegion tRegionHandler tWCBox tWCEllipsoid tWCExtension tWCLELMask tWCUnion ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_images) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/images/Regions/test/imregion.fits000066400000000000000000004103001476623553700216540ustar00rootroot00000000000000SIMPLE = T /Standard FITS BITPIX = -32 /Floating point (32 bit) NAXIS = 4 NAXIS1 = 20 NAXIS2 = 20 NAXIS3 = 20 NAXIS4 = 4 BSCALE = 1.000000000000E+00 /PHYSICAL = PIXEL*BSCALE + BZERO BZERO = 0.000000000000E+00 BTYPE = 'Intensity' OBJECT = ' ' BUNIT = ' ' /Brightness (pixel) unit EQUINOX = 1.979900000000E+03 LONPOLE = 1.800000000000E+02 LATPOLE = 4.492220833333E+01 PC001001= 1.000000000000E+00 PC002001= 0.000000000000E+00 PC003001= 0.000000000000E+00 PC004001= 0.000000000000E+00 PC001002= 0.000000000000E+00 PC002002= 1.000000000000E+00 PC003002= 0.000000000000E+00 PC004002= 0.000000000000E+00 PC001003= 0.000000000000E+00 PC002003= 0.000000000000E+00 PC003003= 1.000000000000E+00 PC004003= 0.000000000000E+00 PC001004= 0.000000000000E+00 PC002004= 0.000000000000E+00 PC003004= 0.000000000000E+00 PC004004= 1.000000000000E+00 CTYPE1 = 'RA---SIN' CRVAL1 = 7.138206250000E+01 CDELT1 = -8.333333333333E-05 CRPIX1 = 1.025000000000E+03 CUNIT1 = 'deg ' CTYPE2 = 'DEC--SIN' CRVAL2 = 4.492220833333E+01 CDELT2 = 8.333333333333E-05 CRPIX2 = 1.025000000000E+03 CUNIT2 = 'deg ' CTYPE3 = 'FREQ ' CRVAL3 = 4.735100000000E+09 CDELT3 = 4.000000000000E+08 CRPIX3 = 1.000000000000E+00 CUNIT3 = 'HZ ' CTYPE4 = 'STOKES ' CRVAL4 = 1.000000000000E+00 CDELT4 = 1.000000000000E+00 CRPIX4 = 1.000000000000E+00 CUNIT4 = ' ' PV2_1 = 0.000000000000E+00 PV2_2 = 0.000000000000E+00 TELESCOP= 'VLA ' OBSERVER= 'unavailable' DATE-OBS= '1994-07-25T07:41:45.000002' TIMESYS = 'UTC ' OBSRA = 7.138206250000E+01 OBSDEC = 4.492220833333E+01 OBSGEO-X= -1.601185365000E+06 OBSGEO-Y= -5.041977547000E+06 OBSGEO-Z= 3.554875870000E+06 DATE = '2011-01-19T18:39:12.605000' /Date FITS file was written ORIGIN = 'CASA casacore alma-evla ' END casacore-3.7.1/images/Regions/test/imregion_dironly.fits000066400000000000000000000207001476623553700234150ustar00rootroot00000000000000SIMPLE = T /Standard FITS BITPIX = -32 /Floating point (32 bit) NAXIS = 2 NAXIS1 = 20 NAXIS2 = 20 BSCALE = 1.000000000000E+00 /PHYSICAL = PIXEL*BSCALE + BZERO BZERO = 0.000000000000E+00 BTYPE = 'Intensity' OBJECT = ' ' BUNIT = ' ' /Brightness (pixel) unit EQUINOX = 1.979900000000E+03 LONPOLE = 1.800000000000E+02 LATPOLE = 4.492220833333E+01 PC001001= 1.000000000000E+00 PC002001= 0.000000000000E+00 PC001002= 0.000000000000E+00 PC002002= 1.000000000000E+00 CTYPE1 = 'RA---SIN' CRVAL1 = 7.138206250000E+01 CDELT1 = -8.333333333333E-05 CRPIX1 = 1.025000000000E+03 CUNIT1 = 'deg ' PV2_1 = 0.000000000000E+00 CTYPE2 = 'DEC--SIN' CRVAL2 = 4.492220833333E+01 CDELT2 = 8.333333333333E-05 CRPIX2 = 1.025000000000E+03 CUNIT2 = 'deg ' PV2_2 = 0.000000000000E+00 TELESCOP= 'VLA ' OBSERVER= 'unavailable' DATE-OBS= '1994-07-25T07:41:45.000002' TIMESYS = 'UTC ' OBSRA = 7.138206250000E+01 OBSDEC = 4.492220833333E+01 OBSGEO-X= -1.601185365000E+06 OBSGEO-Y= -5.041977547000E+06 OBSGEO-Z= 3.554875870000E+06 DATE = '2011-01-24T17:04:20.058000' /Date FITS file was written ORIGIN = 'CASA casacore alma-evla ' END casacore-3.7.1/images/Regions/test/imregion_nospec.fits000066400000000000000000000341001476623553700232230ustar00rootroot00000000000000SIMPLE = T /Standard FITS BITPIX = -32 /Floating point (32 bit) NAXIS = 3 NAXIS1 = 20 NAXIS2 = 20 NAXIS3 = 4 BSCALE = 1.000000000000E+00 /PHYSICAL = PIXEL*BSCALE + BZERO BZERO = 0.000000000000E+00 BTYPE = 'Intensity' OBJECT = ' ' BUNIT = ' ' /Brightness (pixel) unit EQUINOX = 1.979900000000E+03 LONPOLE = 1.800000000000E+02 LATPOLE = 4.492220833333E+01 PC001001= 1.000000000000E+00 PC002001= 0.000000000000E+00 PC003001= 0.000000000000E+00 PC001002= 0.000000000000E+00 PC002002= 1.000000000000E+00 PC003002= 0.000000000000E+00 PC001003= 0.000000000000E+00 PC002003= 0.000000000000E+00 PC003003= 1.000000000000E+00 CTYPE1 = 'RA---SIN' CRVAL1 = 7.138206250000E+01 CDELT1 = -8.333333333333E-05 CRPIX1 = 1.025000000000E+03 CUNIT1 = 'deg ' CTYPE2 = 'DEC--SIN' CRVAL2 = 4.492220833333E+01 CDELT2 = 8.333333333333E-05 CRPIX2 = 1.025000000000E+03 CUNIT2 = 'deg ' CTYPE3 = 'STOKES ' CRVAL3 = 1.000000000000E+00 CDELT3 = 1.000000000000E+00 CRPIX3 = 1.000000000000E+00 CUNIT3 = ' ' PV2_1 = 0.000000000000E+00 PV2_2 = 0.000000000000E+00 TELESCOP= 'VLA ' OBSERVER= 'unavailable' DATE-OBS= '1994-07-25T07:41:45.000002' TIMESYS = 'UTC ' OBSRA = 7.138206250000E+01 OBSDEC = 4.492220833333E+01 OBSGEO-X= -1.601185365000E+06 OBSGEO-Y= -5.041977547000E+06 OBSGEO-Z= 3.554875870000E+06 DATE = '2011-01-24T14:44:33.977000' /Date FITS file was written ORIGIN = 'CASA casacore alma-evla ' END casacore-3.7.1/images/Regions/test/tImageRegion.cc000066400000000000000000000051611476623553700220420ustar00rootroot00000000000000//# Copyright (C) 1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include int main () { String myname = "tmp.im"; int ret = 0; try { PagedImage im( TiledShape(IPosition(4, 1)), CoordinateUtil::defaultCoords4D(), myname ); im.flush(); vector names; names.push_back("tmp.im"); names.push_back("'tmp.im'"); names.push_back("'./tmp.im'"); names.push_back("'$PWD/tmp.im'"); names.push_back("./tmp.im"); names.push_back("$PWD/tmp.im"); // various escaping tests for fromLatticeExpession uInt lastGood = 3; for (uInt i=0; i lastGood || j > lastGood, AipsError); } } } cout << "OK" << endl; } catch (const std::exception& x) { cerr << "Caught exception: " << x.what() << endl; cout << "FAIL" << endl; ret = 1; } Directory d(myname); if (d.exists()) { d.removeRecursive(False); } return ret; } casacore-3.7.1/images/Regions/test/tRegionHandler.cc000066400000000000000000000147101476623553700223750ustar00rootroot00000000000000//# tRegionhandler.cc: test the regions in the Regionhandler classes //# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Table theTable; Table& getTable (void*, Bool) { return theTable; } std::shared_ptr theHDF5File; const std::shared_ptr& getHDF5File (void*) { return theHDF5File; } void doIt (RegionHandler& reghand) { IPosition shape(2,32,8); LCSlicer box1(IPosition(2,0), shape-1); const ImageRegion* regptr; // Create a region as a mask and add it to the image. // The region won't be found in the regions. reghand.defineRegion ("reg1", box1, RegionHandler::Masks); AlwaysAssertExit (reghand.hasRegion ("reg1")); AlwaysAssertExit (! reghand.hasRegion ("reg2")); regptr = reghand.getRegion("reg1"); AlwaysAssertExit (regptr != 0); AlwaysAssertExit (*regptr == ImageRegion(box1)); delete regptr; // Define the region as the default. reghand.setDefaultMask ("reg1"); AlwaysAssertExit (reghand.getDefaultMask() == "reg1"); // Define the region in the regions group and check it can be found. reghand.defineRegion ("regr1", box1, RegionHandler::Regions); regptr = reghand.getRegion ("regr1", RegionHandler::Regions, False); AlwaysAssertExit (regptr != 0); delete regptr; regptr = reghand.getRegion ("regr1", RegionHandler::Masks, False); AlwaysAssertExit (regptr == 0); regptr = reghand.getRegion ("regr1", RegionHandler::Any, False); AlwaysAssertExit (regptr != 0); delete regptr; // Get all region names. Vector names = reghand.regionNames(); AlwaysAssertExit (names.nelements() == 2); AlwaysAssertExit (names(0) == "reg1" || names(1) == "reg1"); AlwaysAssertExit (names(0) == "regr1" || names(1) == "regr1"); Vector rnames = reghand.regionNames (RegionHandler::Regions); AlwaysAssertExit (rnames.nelements() == 1); AlwaysAssertExit (rnames(0) == "regr1"); Vector mnames = reghand.regionNames (RegionHandler::Masks); AlwaysAssertExit (mnames.nelements() == 1); AlwaysAssertExit (mnames(0) == "reg1"); // Rename the region in the regions group and check it can be found. reghand.renameRegion ("regr2", "regr1", RegionHandler::Regions); regptr = reghand.getRegion ("regr2", RegionHandler::Regions, False); AlwaysAssertExit (regptr != 0); delete regptr; regptr = reghand.getRegion ("regr2", RegionHandler::Masks, False); AlwaysAssertExit (regptr == 0); regptr = reghand.getRegion ("regr2", RegionHandler::Any, False); AlwaysAssertExit (regptr != 0); delete regptr; regptr = reghand.getRegion ("regr1", RegionHandler::Any, False); AlwaysAssertExit (regptr == 0); // Create a lattice and mask and make it default region. PagedArray lattice (shape, "tRegionHandler_tmp.lat"); reghand.defineRegion ("reg2", reghand.makeMask (lattice, "reg2"), RegionHandler::Masks); reghand.setDefaultMask ("reg2"); AlwaysAssertExit (reghand.getDefaultMask() == "reg2"); // Rename that mask and make sure the table and default mask are renamed too. reghand.renameRegion ("reg2n", "reg2"); AlwaysAssertExit (reghand.hasRegion ("reg2n")); AlwaysAssertExit (! reghand.hasRegion ("reg2")); AlwaysAssertExit (reghand.getDefaultMask() == "reg2n"); // Make a unique name. AlwaysAssertExit (reghand.makeUniqueRegionName ("reg2n") == "reg2n1"); AlwaysAssertExit (reghand.makeUniqueRegionName ("reg2n", 3) == "reg2n3"); AlwaysAssertExit (reghand.makeUniqueRegionName ("reg2na", 3) == "reg2na3"); // Now get the mask as a region and check it is correct. regptr = reghand.getRegion (reghand.getDefaultMask()); AlwaysAssertExit (regptr != 0); AlwaysAssertExit (regptr->isLCRegion()); delete regptr; // Remove the region, which should also remove the default mask. // If the handler uses a table, the table is also removed. This is checked // in the calling function. reghand.removeRegion ("reg2n"); AlwaysAssertExit (! reghand.hasRegion ("reg2n")); AlwaysAssertExit (reghand.getDefaultMask() == ""); } int main() { try { RegionHandlerMemory regmem; doIt (regmem); SetupNewTable newtab ("tRegionHandler_tmp.data", TableDesc(), Table::New); theTable = Table(newtab); RegionHandlerTable regtab (getTable, 0); doIt (regtab); AlwaysAssertExit (! File("tRegionHandler_tmp.lat/reg2n").exists()); // Test regions in HDF5 only if supported. if (HDF5Object::hasHDF5Support()) { theHDF5File = std::make_shared("tRegionHandler_tmp.hdf5", ByteIO::New); RegionHandlerHDF5 reghdf5 (getHDF5File, 0); doIt (reghdf5); } } catch (std::exception& x) { cerr << "Unexpected exception: " << x.what() << endl; return 1; } cout << "ok" << endl; return 0; } casacore-3.7.1/images/Regions/test/tRegionManager.cc000066400000000000000000000601371476623553700223760ustar00rootroot00000000000000//# tRegionhandler.cc: test the regions in the Regionhandler classes //# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include void writeTestString(const String& test) { cout << "\n" << "*** " << test << " ***" << endl; } Vector recToVec(const Record& rec) { uInt nfields = rec.nfields(); Vector vec(nfields); vec[0] = rec.asRecord("*1").asDouble("value"); vec[1] = rec.asRecord("*2").asDouble("value"); if (nfields >= 3) { vec[2] = rec.asRecord("*3").asDouble("value"); if (nfields >= 4) { vec[3] = rec.asRecord("*4").asDouble("value"); } } return vec; } void compVecs(Vector& got, Vector& exp) { Double epsilon = 1e-8; for (uInt i=0; i *myImage = new FITSImage("imregion.fits"); const ImageInterface *myImageNoSpec = new FITSImage("imregion_nospec.fits"); const ImageInterface *myImageDirOnly = new FITSImage("imregion_dironly.fits"); String test, diagnostics, stokes, chans, box; uInt nSelectedChannels; Vector chanEndPoints, polEndPoints; RegionManager::StokesControl stokesControl; Record regRec; RegionManager rm(myImage->coordinates()); IPosition imShape = myImage->shape(); Double box1 = 1.24795026; Double box2 = 0.782552901; Double box3 = 1.24794616; Double box4 = 0.782555814; Double box5 = 1.24794206; Double box6 = 0.782558727; Double box7 = 1.24793797; Double box8 = 0.782561641; Double chan0 = 4.73510000e+09; Double chan4 = 6.33510000e+09; Double chan15 = 1.07351000e+10; Double chan19 = 1.23351000e+10; try { { diagnostics = ""; nSelectedChannels = 0; stokes = ""; chans = ""; stokesControl = RegionManager::USE_ALL_STOKES; box = ""; writeTestString("Test default gives region of entire image"); regRec = rm.fromBCS( diagnostics, nSelectedChannels, stokes, 0, "", chans, stokesControl, box, imShape ); AlwaysAssert(nSelectedChannels == 20, AipsError); Vector gotblc = recToVec(regRec.asRecord("blc")); Vector expblc(4); expblc[0] = 1.24795230; expblc[1] = 0.782549990; expblc[2] = 4.73510000e+09; expblc[3] = 1.0; compVecs(gotblc, expblc); Vector gottrc = recToVec(regRec.asRecord("trc")); Vector exptrc(4); exptrc[0] = 1.24791339; exptrc[1] = 0.782577665; exptrc[2] = 1.23351000e+10; exptrc[3] = 4.0; compVecs(gottrc, exptrc); } { diagnostics = ""; nSelectedChannels = 0; stokes = "Q"; chans = ""; stokesControl = RegionManager::USE_ALL_STOKES; box = ""; writeTestString("Test setting a single stokes"); regRec = rm.fromBCS( diagnostics, nSelectedChannels, stokes, 0, "", chans, stokesControl, box, imShape ); AlwaysAssert(nSelectedChannels == 20, AipsError); Vector gotblc = recToVec(regRec.asRecord("blc")); Vector expblc(4); expblc[0] = 1.24795230; expblc[1] = 0.782549990; expblc[2] = 4.73510000e+09; expblc[3] = 2.0; compVecs(gotblc, expblc); Vector gottrc = recToVec(regRec.asRecord("trc")); Vector exptrc(4); exptrc[0] = 1.24791339; exptrc[1] = 0.782577665; exptrc[2] = 1.23351000e+10; exptrc[3] = 2.0; compVecs(gottrc, exptrc); } { diagnostics = ""; nSelectedChannels = 0; stokes = "QU"; chans = ""; stokesControl = RegionManager::USE_ALL_STOKES; box = ""; writeTestString("Test setting a contiguous stokes"); regRec = rm.fromBCS( diagnostics, nSelectedChannels, stokes, 0, "", chans, stokesControl, box, imShape ); AlwaysAssert(nSelectedChannels == 20, AipsError); Vector gotblc = recToVec(regRec.asRecord("blc")); Vector expblc(4); expblc[0] = 1.24795230; expblc[1] = 0.782549990; expblc[2] = 4.73510000e+09; expblc[3] = 2.0; compVecs(gotblc, expblc); Vector gottrc = recToVec(regRec.asRecord("trc")); Vector exptrc(4); exptrc[0] = 1.24791339; exptrc[1] = 0.782577665; exptrc[2] = 1.23351000e+10; exptrc[3] = 3.0; compVecs(gottrc, exptrc); } { diagnostics = ""; nSelectedChannels = 0; stokes = ""; chans = "5"; stokesControl = RegionManager::USE_ALL_STOKES; box = ""; writeTestString("Test setting a single channel"); regRec = rm.fromBCS( diagnostics, nSelectedChannels, stokes, 0, "", chans, stokesControl, box, imShape ); AlwaysAssert(nSelectedChannels == 1, AipsError); Vector gotblc = recToVec(regRec.asRecord("blc")); Vector expblc(4); expblc[0] = 1.24795230; expblc[1] = 0.782549990; expblc[2] = 6.73510000e+09; expblc[3] = 1.0; compVecs(gotblc, expblc); Vector gottrc = recToVec(regRec.asRecord("trc")); Vector exptrc(4); exptrc[0] = 1.24791339; exptrc[1] = 0.782577665; exptrc[2] = 6.73510000e+09; exptrc[3] = 4.0; compVecs(gottrc, exptrc); } { diagnostics = ""; nSelectedChannels = 0; stokes = ""; chans = "5~10"; stokesControl = RegionManager::USE_ALL_STOKES; box = ""; writeTestString("Test setting multiple continuous channels"); regRec = rm.fromBCS( diagnostics, nSelectedChannels, stokes, 0, "", chans, stokesControl, box, imShape ); AlwaysAssert(nSelectedChannels == 6, AipsError); Vector gotblc = recToVec(regRec.asRecord("blc")); Vector expblc(4); expblc[0] = 1.24795230; expblc[1] = 0.782549990; expblc[2] = 6.73510000e+09; expblc[3] = 1.0; compVecs(gotblc, expblc); Vector gottrc = recToVec(regRec.asRecord("trc")); Vector exptrc(4); exptrc[0] = 1.24791339; exptrc[1] = 0.782577665; exptrc[2] = 8.73510000e+09; exptrc[3] = 4.0; compVecs(gottrc, exptrc); } { diagnostics = ""; nSelectedChannels = 0; stokes = ""; chans = ""; stokesControl = RegionManager::USE_ALL_STOKES; box = "4,5,8,9"; writeTestString("Test setting box"); regRec = rm.fromBCS( diagnostics, nSelectedChannels, stokes, 0, "", chans, stokesControl, box, imShape ); AlwaysAssert(nSelectedChannels == 20, AipsError); Vector gotblc = recToVec(regRec.asRecord("blc")); Vector expblc(4); expblc[0] = 1.24794411; expblc[1] = 0.782557271; expblc[2] = 4.73510000e+09; expblc[3] = 1.0; compVecs(gotblc, expblc); Vector gottrc = recToVec(regRec.asRecord("trc")); Vector exptrc(4); exptrc[0] = 1.24793592; exptrc[1] = 0.782563097; exptrc[2] = 1.23351000e+10; exptrc[3] = 4; compVecs(gottrc, exptrc); } { diagnostics = ""; nSelectedChannels = 0; stokes = ""; chans = ""; stokesControl = RegionManager::USE_FIRST_STOKES; box = ""; writeTestString("Test using first stokes"); regRec = rm.fromBCS( diagnostics, nSelectedChannels, stokes, 0, "", chans, stokesControl, box, imShape ); AlwaysAssert(nSelectedChannels == 20, AipsError); Vector gotblc = recToVec(regRec.asRecord("blc")); Vector expblc(4); expblc[0] = 1.24795230; expblc[1] = 0.782549990; expblc[2] = 4.73510000e+09; expblc[3] = 1.0; compVecs(gotblc, expblc); Vector gottrc = recToVec(regRec.asRecord("trc")); Vector exptrc(4); exptrc[0] = 1.24791339; exptrc[1] = 0.782577665; exptrc[2] = 1.23351000e+10; exptrc[3] = 1.0; compVecs(gottrc, exptrc); } { diagnostics = ""; nSelectedChannels = 0; stokes = ""; chans = ""; stokesControl = RegionManager::USE_ALL_STOKES; box = "1,2,3,4,5,6,7,8,9,10,11,12"; writeTestString("Test setting multiple boxes"); regRec = rm.fromBCS( diagnostics, nSelectedChannels, stokes, 0, "", chans, stokesControl, box, imShape ); AlwaysAssert(nSelectedChannels == 20, AipsError); Vector gotblc = recToVec(regRec.asRecord("regions").asRecord("*2").asRecord("blc")); Vector expblc(4); expblc[0] = 1.24793387; expblc[1] = 0.782564554; expblc[2] = 4.73510000e+09; expblc[3] = 1.0; compVecs(gotblc, expblc); Vector gottrc = recToVec(regRec.asRecord("regions").asRecord("*2").asRecord("trc")); Vector exptrc(4); exptrc[0] = 1.24792978; exptrc[1] = 0.782567467; exptrc[2] = 1.23351000e+10; exptrc[3] = 4.0; compVecs(gottrc, exptrc); gotblc = recToVec(regRec.asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*1").asRecord("blc")); expblc[0] = box1; expblc[1] = box2; compVecs(gotblc, expblc); gottrc = recToVec(regRec.asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*1").asRecord("trc")); exptrc[0] = box3; exptrc[1] = box4; compVecs(gottrc, exptrc); gotblc = recToVec(regRec.asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*2").asRecord("blc")); expblc[0] = box5; expblc[1] = box6; compVecs(gotblc, expblc); gottrc = recToVec(regRec.asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*2").asRecord("trc")); exptrc[0] = box7; exptrc[1] = box8; compVecs(gottrc, exptrc); } { diagnostics = ""; nSelectedChannels = 0; stokes = "IUV"; chans = ""; stokesControl = RegionManager::USE_ALL_STOKES; box = ""; writeTestString("Test setting multiple stokes ranges"); regRec = rm.fromBCS( diagnostics, nSelectedChannels, stokes, 0, "", chans, stokesControl, box, imShape ); AlwaysAssert(nSelectedChannels == 20, AipsError); Vector gotblc = recToVec(regRec.asRecord("regions").asRecord("*1").asRecord("blc")); Vector expblc(4); expblc[0] = 1.24795230; expblc[1] = 0.782549990; expblc[2] = 4.73510000e+09; expblc[3] = 1.0; compVecs(gotblc, expblc); Vector gottrc = recToVec(regRec.asRecord("regions").asRecord("*1").asRecord("trc")); Vector exptrc(4); exptrc[0] = 1.24791339; exptrc[1] = 0.782577665; exptrc[2] = 1.23351000e+10; exptrc[3] = 1.0; compVecs(gottrc, exptrc); gotblc = recToVec(regRec.asRecord("regions").asRecord("*2").asRecord("blc")); expblc[0] = 1.24795230; expblc[1] = 0.782549990; expblc[2] = 4.73510000e+09; expblc[3] = 3.0; compVecs(gotblc, expblc); gottrc = recToVec(regRec.asRecord("regions").asRecord("*2").asRecord("trc")); exptrc[0] = 1.24791339; exptrc[1] = 0.782577665; exptrc[2] = 1.23351000e+10; exptrc[3] = 4.0; compVecs(gottrc, exptrc); } { diagnostics = ""; nSelectedChannels = 0; stokes = ""; chans = "<5,>=15"; stokesControl = RegionManager::USE_ALL_STOKES; box = ""; writeTestString("Test multiple channel ranges"); regRec = rm.fromBCS( diagnostics, nSelectedChannels, stokes, 0, "", chans, stokesControl, box, imShape ); AlwaysAssert(nSelectedChannels == 10, AipsError); Vector gotblc = recToVec(regRec.asRecord("regions").asRecord("*1").asRecord("blc")); Vector expblc(4); expblc[0] = 1.24795230; expblc[1] = 0.782549990; expblc[2] = 4.73510000e+09; expblc[3] = 1.0; compVecs(gotblc, expblc); Vector gottrc = recToVec(regRec.asRecord("regions").asRecord("*1").asRecord("trc")); Vector exptrc(4); exptrc[0] = 1.24791339; exptrc[1] = 0.782577665; exptrc[2] = 6.33510000e+09; exptrc[3] = 4.0; compVecs(gottrc, exptrc); gotblc = recToVec(regRec.asRecord("regions").asRecord("*2").asRecord("blc")); expblc[0] = 1.24795230; expblc[1] = 0.782549990; expblc[2] = 1.07351000e+10; expblc[3] = 1.0; compVecs(gotblc, expblc); gottrc = recToVec(regRec.asRecord("regions").asRecord("*2").asRecord("trc")); exptrc[0] = 1.24791339; exptrc[1] = 0.782577665; exptrc[2] = 1.23351000e+10; exptrc[3] = 4.0; compVecs(gottrc, exptrc); } { diagnostics = ""; nSelectedChannels = 0; stokes = "IQV"; chans = "<5,>=15"; stokesControl = RegionManager::USE_ALL_STOKES; box = "1,2,3,4,5,6,7,8"; writeTestString("Test multiple channel ranges, multiple stokes ranges, and multiple boxes"); regRec = rm.fromBCS( diagnostics, nSelectedChannels, stokes, 0, "", chans, stokesControl, box, imShape ); AlwaysAssert(nSelectedChannels == 10, AipsError); // box="5,6,7,8", chans="15~19", stokes="V" Vector gotblc = recToVec(regRec.asRecord("regions").asRecord("*2").asRecord("blc")); Vector expblc(4); expblc[0] = box5; expblc[1] = box6; expblc[2] = chan15; expblc[3] = 4.0; compVecs(gotblc, expblc); Vector gottrc = recToVec(regRec.asRecord("regions").asRecord("*2").asRecord("trc")); Vector exptrc(4); exptrc[0] = box7; exptrc[1] = box8; exptrc[2] = chan19; exptrc[3] = 4.0; compVecs(gottrc, exptrc); // box="5,6,7,8", chans="0~4", stokes="V" gotblc = recToVec( regRec.asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*2") .asRecord("blc") ); expblc[0] = box5; expblc[1] = box6; expblc[2] = chan0; expblc[3] = 4.0; compVecs(gotblc, expblc); gottrc = recToVec( regRec.asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*2") .asRecord("trc") ); exptrc[0] = box7; exptrc[1] = box8; exptrc[2] = chan4; exptrc[3] = 4.0; compVecs(gottrc, exptrc); // box="5,6,7,8", chans="15-19", stokes="IQ" gotblc = recToVec( regRec.asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*1") .asRecord("regions").asRecord("*2").asRecord("blc") ); expblc[0] = box5; expblc[1] = box6; expblc[2] = chan15; expblc[3] = 1.0; compVecs(gotblc, expblc); gottrc = recToVec( regRec.asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*1") .asRecord("regions").asRecord("*2").asRecord("trc") ); exptrc[0] = box7; exptrc[1] = box8; exptrc[2] = chan19; exptrc[3] = 2.0; compVecs(gottrc, exptrc); // box="5,6,7,8", chans="0~4", stokes="IQ" gotblc = recToVec( regRec.asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*1") .asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*2").asRecord("blc") ); expblc[0] = box5; expblc[1] = box6; expblc[2] = chan0; expblc[3] = 1.0; compVecs(gotblc, expblc); gottrc = recToVec( regRec.asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*1") .asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*2").asRecord("trc") ); exptrc[0] = box7; exptrc[1] = box8; exptrc[2] = chan4; exptrc[3] = 2.0; compVecs(gottrc, exptrc); // box="1,2,3,4", chans="15-19", stokes="V" gotblc = recToVec( regRec.asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*1") .asRecord("regions").asRecord("*1").asRecord("regions") .asRecord("*1").asRecord("regions").asRecord("*2").asRecord("blc") ); expblc[0] = box1; expblc[1] = box2; expblc[2] = chan15; expblc[3] = 4.0; compVecs(gotblc, expblc); gottrc = recToVec( regRec.asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*1") .asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*1") .asRecord("regions").asRecord("*2").asRecord("trc") ); exptrc[0] = box3; exptrc[1] = box4; exptrc[2] = chan19; exptrc[3] = 4.0; compVecs(gottrc, exptrc); // box="1,2,3,4", chans="0-4", stokes="V" gotblc = recToVec( regRec.asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*1") .asRecord("regions").asRecord("*1").asRecord("regions") .asRecord("*1").asRecord("regions").asRecord("*1").asRecord("regions"). asRecord("*2").asRecord("blc") ); expblc[0] = box1; expblc[1] = box2; expblc[2] = chan0; expblc[3] = 4.0; compVecs(gotblc, expblc); gottrc = recToVec( regRec.asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*1") .asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*1") .asRecord("regions").asRecord("*1").asRecord("regions"). asRecord("*2").asRecord("trc") ); exptrc[0] = box3; exptrc[1] = box4; exptrc[2] = chan4; exptrc[3] = 4.0; compVecs(gottrc, exptrc); // box="1,2,3,4", chans="15-19", stokes="IQ" gotblc = recToVec( regRec.asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*1") .asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*1").asRecord("regions") .asRecord("*1").asRecord("regions").asRecord("*1").asRecord("regions") .asRecord("*2").asRecord("blc") ); expblc[0] = box1; expblc[1] = box2; expblc[2] = chan15; expblc[3] = 1.0; compVecs(gotblc, expblc); gottrc = recToVec( regRec.asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*1") .asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*1") .asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*1").asRecord("regions") .asRecord("*2").asRecord("trc") ); exptrc[0] = box3; exptrc[1] = box4; exptrc[2] = chan19; exptrc[3] = 2.0; compVecs(gottrc, exptrc); // box="1,2,3,4", chans="0-4", stokes="IQ" gotblc = recToVec( regRec.asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*1") .asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*1").asRecord("regions") .asRecord("*1").asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*1").asRecord("blc") ); expblc[0] = box1; expblc[1] = box2; expblc[2] = chan0; expblc[3] = 1.0; compVecs(gotblc, expblc); gottrc = recToVec( regRec.asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*1") .asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*1").asRecord("regions") .asRecord("*1").asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*1").asRecord("trc") ); exptrc[0] = box3; exptrc[1] = box4; exptrc[2] = chan4; exptrc[3] = 2.0; compVecs(gottrc, exptrc); } { RegionManager rm(myImageNoSpec->coordinates()); IPosition imShape = myImageNoSpec->shape(); diagnostics = ""; nSelectedChannels = 0; stokes = "IQV"; chans = ""; stokesControl = RegionManager::USE_ALL_STOKES; box = "1,2,3,4,5,6,7,8"; writeTestString("Test multiple stokes ranges, and multiple boxes on image with no spectral axis"); regRec = rm.fromBCS( diagnostics, nSelectedChannels, stokes, 0, "", chans, stokesControl, box, imShape ); AlwaysAssert(nSelectedChannels == 0, AipsError); // box="5,6,7,8", stokes="V" Vector gotblc = recToVec(regRec.asRecord("regions").asRecord("*2").asRecord("blc")); Vector expblc(3); expblc[0] = box5; expblc[1] = box6; expblc[2] = 4.0; compVecs(gotblc, expblc); Vector gottrc = recToVec(regRec.asRecord("regions").asRecord("*2").asRecord("trc")); Vector exptrc(3); exptrc[0] = box7; exptrc[1] = box8; exptrc[2] = 4.0; compVecs(gottrc, exptrc); // box="5,6,7,8", stokes="IQ" gotblc = recToVec(regRec.asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*2") .asRecord("blc")); expblc(3); expblc[0] = box5; expblc[1] = box6; expblc[2] = 1.0; compVecs(gotblc, expblc); gottrc = recToVec(regRec.asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*2") .asRecord("trc")); exptrc[0] = box7; exptrc[1] = box8; exptrc[2] = 2.0; compVecs(gottrc, exptrc); // box="1,2,3,4", stokes="V" gotblc = recToVec(regRec.asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*1") .asRecord("regions").asRecord("*2").asRecord("blc")); expblc(3); expblc[0] = box1; expblc[1] = box2; expblc[2] = 4.0; compVecs(gotblc, expblc); gottrc = recToVec(regRec.asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*1") .asRecord("regions").asRecord("*2").asRecord("trc")); exptrc[0] = box3; exptrc[1] = box4; exptrc[2] = 4.0; compVecs(gottrc, exptrc); // box="1,2,3,4", stokes="IQ" gotblc = recToVec(regRec.asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*1") .asRecord("regions").asRecord("*1").asRecord("blc")); expblc(3); expblc[0] = box1; expblc[1] = box2; expblc[2] = 1.0; compVecs(gotblc, expblc); gottrc = recToVec(regRec.asRecord("regions").asRecord("*1").asRecord("regions").asRecord("*1") .asRecord("regions").asRecord("*1").asRecord("trc")); exptrc[0] = box3; exptrc[1] = box4; exptrc[2] = 2.0; compVecs(gottrc, exptrc); } { RegionManager rm(myImageDirOnly->coordinates()); IPosition imShape = myImageDirOnly->shape(); String imname = myImageDirOnly->name(); diagnostics = ""; nSelectedChannels = 0; stokes = ""; chans = ""; stokesControl = RegionManager::USE_ALL_STOKES; box = "1,2,3,4,5,6,7,8"; writeTestString("Test multiple boxes on image with direction coordinate only"); regRec = rm.fromBCS( diagnostics, nSelectedChannels, stokes, 0, "", chans, stokesControl, box, imShape ); AlwaysAssert(nSelectedChannels == 0, AipsError); // box="5,6,7,8" Vector gotblc = recToVec(regRec.asRecord("regions").asRecord("*2").asRecord("blc")); Vector expblc(2); expblc[0] = box5; expblc[1] = box6; compVecs(gotblc, expblc); Vector gottrc = recToVec(regRec.asRecord("regions").asRecord("*2").asRecord("trc")); Vector exptrc(2); exptrc[0] = box7; exptrc[1] = box8; compVecs(gottrc, exptrc); // box="1,2,3,4" gotblc = recToVec(regRec.asRecord("regions").asRecord("*1").asRecord("blc")); expblc[0] = box1; expblc[1] = box2; compVecs(gotblc, expblc); gottrc = recToVec(regRec.asRecord("regions").asRecord("*2").asRecord("trc")); exptrc[0] = box7; exptrc[1] = box8; compVecs(gottrc, exptrc); } } catch (std::exception x) { cerr << "Unexpected exception: " << x.what() << endl; return 1; } cout << "ok" << endl; return 0; } casacore-3.7.1/images/Regions/test/tWCBox.cc000066400000000000000000000404051476623553700206360ustar00rootroot00000000000000//# tWCBox.cc: Test program for WCBox class //# Copyright (C) 1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include void setValues (IPosition& blcI, IPosition& trcI, IPosition& shape, LCBox& checkBox, Vector >& wBlc, Vector >& wTrc, const CoordinateSystem& cSys); void listBB(const LCRegion* pLCRegion); void list (const RecordInterface& record); int main() { try { // Create default Coordinate System, [ra, dec, freq] Vector absRel; CoordinateSystem cSys = CoordinateUtil::defaultCoords3D(); // Create vectors IPosition shape, blcI, trcI; Vector > wBlc, wTrc; LCBox checkBox; // Create WCBox setValues (blcI, trcI, shape, checkBox, wBlc, wTrc, cSys); WCBox box(wBlc, wTrc, cSys, absRel); // Create null box WCBox box2; // Test comparison AlwaysAssert(box == box, AipsError); AlwaysAssert(box2 != box, AipsError) // Test assignment box2 = box; AlwaysAssert(box2 == box, AipsError); AlwaysAssert(!(box2 != box), AipsError); // Test copy constructor WCBox box3(box); AlwaysAssert(box3 == box, AipsError); // Test clone region WCRegion* pWCRegion = box.cloneRegion(); const WCBox* pBox = (const WCBox*)pWCRegion; AlwaysAssert(*pBox == box, AipsError); if (pWCRegion != 0) delete pWCRegion; // Test splitBox { IPosition axes(3,2,0,1); WCBox sbox1(box.splitBox (axes)); Vector > blc2(axes.nelements()); Vector > trc2(axes.nelements()); for (uInt i=0; i > blc2a(axesa.nelements()); Vector > trc2a(axesa.nelements()); IPosition axesa2(2); for (uInt i=0; i units = cSys2.worldAxisUnits(); units(0) = "deg"; units(1) = "deg"; AlwaysAssert(cSys2.setWorldAxisUnits(units), AipsError); // cout << "toLCRegion called with shape = " << shape << endl; LCRegion* pLCRegion = box.toLCRegion(cSys2, shape); AlwaysAssert(*pLCRegion==checkBox, AipsError); delete pLCRegion; } // cout << endl; // Test auto extension { LCBox checkBox2; CoordinateSystem cSys2 = CoordinateUtil::defaultCoords2D(); setValues (blcI, trcI, shape, checkBox2, wBlc, wTrc, cSys2); WCBox box2(wBlc, wTrc, cSys2, absRel); // IPosition shape3(3), blc3(3), trc3(3); shape3(0) = shape(0); shape3(1) = shape(1); shape3(2) = 30; CoordinateSystem cSys3 = CoordinateUtil::defaultCoords3D(); // cout << "toLCRegion called with shape = " << shape3 << endl; LCRegion* pLCRegion = box2.toLCRegion(cSys3, shape3); // blc3(0) = blcI(0); blc3(1) = blcI(1); blc3(2) = 0; trc3(0) = trcI(0); trc3(1) = trcI(1); trc3(2) = shape3(2) - 1; LCBox checkBox3(blc3, trc3, shape3); AlwaysAssert(*pLCRegion==checkBox3, AipsError); if (pLCRegion != 0) delete pLCRegion; } // cout << endl; // Test null blc/trc box { // cout << "[ra,dec], [ra,dec]" << endl; // cout << "Null blc/trc" << endl; CoordinateSystem cSys2 = CoordinateUtil::defaultCoords2D(); wBlc.resize(0); wTrc.resize(0); WCBox box2(wBlc, wTrc, cSys2, absRel); // IPosition shape2(2), blc2(2), trc2(2); shape2(0) = 10; shape2(1) = 20; // cout << "toLCRegion called with shape = " << shape2 << endl; LCRegion* pLCRegion = box2.toLCRegion(cSys2, shape2); blc2(0) = 0; blc2(1) = 0; trc2(0) = shape2(0)-1; trc2(1) = shape2(1)-1; LCBox checkBox2(blc2, trc2, shape2); AlwaysAssert(*pLCRegion==checkBox2, AipsError); if (pLCRegion != 0) delete pLCRegion; } // cout << endl; // Test conversion to LCRegion with less world axes { // cout << "[ra,dec,freq], [ra,dec]" << endl; CoordinateSystem cSys3 = CoordinateUtil::defaultCoords3D(); setValues (blcI, trcI, shape, checkBox, wBlc, wTrc, cSys3); WCBox box3(wBlc, wTrc, cSys3, absRel); // CoordinateSystem cSys2 = CoordinateUtil::defaultCoords2D(); IPosition shape2(cSys2.nPixelAxes()); for (uInt i=0; i > blc(2); blc(0) = wBlc(pixelAxes(0)); blc(1) = wBlc(pixelAxes(1)); Vector > trc(2); trc(0) = wTrc(pixelAxes(0)); trc(1) = wTrc(pixelAxes(1)); // cout << "Construction with specified pixel axes" << endl; // cout << "pixelAxes = " << pixelAxes << endl; WCBox box2(blc, trc, pixelAxes, cSys, absRel); // cout << "toLCRegion called with shape = " << shape << endl; LCRegion* pLCRegion = box2.toLCRegion(cSys, shape); // blcI(0) = 0; trcI(0) = shape(0)-1; LCBox checkBox2(blcI, trcI, shape); AlwaysAssert(*pLCRegion==checkBox2, AipsError); if (pLCRegion != 0) delete pLCRegion; } // cout << endl; } catch (std::exception& x) { cerr << "aipserror: error " << x.what() << endl; cout << "not ok" << endl; return 1; } cout << "ok" << endl; return 0; } void listBB(const LCRegion* pLCRegion) { if (pLCRegion == 0) { cout << "You gave me a null pointer" << endl; return; } cout << "Bounding box = " << pLCRegion->boundingBox().start() << pLCRegion->boundingBox().end() << endl; } void setValues (IPosition& blcI, IPosition& trcI, IPosition& shape, LCBox& checkBox, Vector >& wBlc, Vector >& wTrc, const CoordinateSystem& cSys) { uInt nDim = cSys.nPixelAxes(); shape.resize(nDim); blcI.resize(nDim); trcI.resize(nDim); uInt i; for (i=0; i pBlc(nDim); Vector pTrc(nDim); Vector wBlc2(nDim); Vector wTrc2(nDim); for (i=0; i=0) { wBlc(i) = Quantum(wBlc2(i), cSys.worldAxisUnits()(worldAxis)); wTrc(i) = Quantum(wTrc2(i), cSys.worldAxisUnits()(worldAxis)); } } } void list (const RecordInterface& record) { for (uInt j=0; j axes = Vector(record.asArrayInt ("pixelAxes")); Vector absRel = Vector(record.asArrayInt("absrel")); cout << "axes=" << axes << endl; cout << "absRel=" << absRel << endl; } casacore-3.7.1/images/Regions/test/tWCEllipsoid.cc000066400000000000000000000303031476623553700220260ustar00rootroot00000000000000//# tLCPolygon.cc: Test program for LCPolygon class //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include /* void show(const LCEllipsoid& ellipse) { Array mask = ellipse.get(); IPosition shape = mask.shape(); IPosition index = shape-1; uInt j=0; while(True) { for (uInt i=0; i=0; j--) { for (uInt i=0; i center(3, Quantity(0, "rad")); center[1] += Quantity(20.5, "arcmin"); Vector radius(3, Quantity(5, "arcmin")); IPosition pixelAxes(3, 0, 1, 2); try { WCEllipsoid(center, radius, pixelAxes, csys); AlwaysAssert(False, AipsError); } catch (std::exception& x) { cout << "Caught as expected " << x.what() << endl; } radius[2] = Quantity(50, "MHz"); try { WCEllipsoid(center, radius, pixelAxes, csys); AlwaysAssert(False, AipsError); } catch (std::exception& x) { cout << "Caught as expected " << x.what() << endl; } center[2] = Quantity(1415, "GHz"); pixelAxes[2] = 3; try { WCEllipsoid(center, radius, pixelAxes, csys); AlwaysAssert(False, AipsError); } catch (std::exception& x) { cout << "Caught as expected " << x.what() << endl; } pixelAxes = IPosition(3, 0, 0, 1); try { WCEllipsoid(center, radius, pixelAxes, csys); AlwaysAssert(False, AipsError); } catch (std::exception& x) { cout << "Caught as expected " << x.what() << endl; } pixelAxes = IPosition(3, 0, 1, 2); center.resize(2, True); try { WCEllipsoid(center, radius, pixelAxes, csys); AlwaysAssert(False, AipsError); } catch (std::exception& x) { cout << "Caught as expected " << x.what() << endl; } // generic ellipsoid tests center.resize(3, True); center[2] = Quantity(1.41501, "GHz"); radius[1] = Quantity(1200, "arcsec"); radius[2] = Quantity(50, "kHz"); WCEllipsoid ellipse(center, radius, pixelAxes, csys); AlwaysAssert(ellipse == ellipse, AipsError); WCEllipsoid ellipse2 = ellipse; AlwaysAssert(ellipse == ellipse2, AipsError); WCEllipsoid *ellipse3 = dynamic_cast(ellipse.cloneRegion()); AlwaysAssert(ellipse == *ellipse3, AipsError); delete ellipse3; TableRecord rec = ellipse.toRecord(""); ellipse3 = WCEllipsoid::fromRecord(rec, ""); AlwaysAssert(ellipse == *ellipse3, AipsError); delete ellipse3; IPosition latticeShape(3, 20, 30, 40); IPosition pixelAxesMap(3, 0, 1, 2); IPosition outOrder(3, 0, 1, 2); LCRegion *lcReg = ellipse.doToLCRegion( csys, latticeShape, pixelAxesMap, outOrder ); LCEllipsoid *lcEllipse = dynamic_cast(lcReg); IPosition lcShape = lcReg->shape(); Vector lcCenter = lcEllipse->center(); AlwaysAssert(near(lcCenter[0], 0.0), AipsError); AlwaysAssert(near(lcCenter[1], 20.5), AipsError); AlwaysAssert(near(lcCenter[2], 10.0), AipsError); Vector lcRadii = lcEllipse->radii(); Vector pixel(3, 1); Vector world1; csys.toWorld(world1, pixel); pixel = 2; Vector world2; csys.toWorld(world2, pixel); AlwaysAssert(near(lcRadii[0], 5.0), AipsError); AlwaysAssert(near(lcRadii[1], 20.0), AipsError); AlwaysAssert(near(lcRadii[2], 50.0), AipsError); outOrder = IPosition(3, 1, 2, 0); delete lcReg; lcReg = ellipse.doToLCRegion( csys, latticeShape, pixelAxesMap, outOrder ); AlwaysAssert( lcReg->shape() == IPosition(3, lcShape[2], lcShape[0], lcShape[1]), AipsError ); lcEllipse = dynamic_cast(lcReg); lcCenter = lcEllipse->center(); AlwaysAssert(near(lcCenter[1], 0.0), AipsError); AlwaysAssert(near(lcCenter[2], 20.5), AipsError); AlwaysAssert(near(lcCenter[0], 10.0), AipsError); lcRadii = lcEllipse->radii(); AlwaysAssert(near(lcRadii[1], 5.0), AipsError); AlwaysAssert(near(lcRadii[2], 20.0), AipsError); AlwaysAssert(near(lcRadii[0], 50.0), AipsError); outOrder = IPosition(3, 0, 1, 2); pixelAxesMap = IPosition(3, 1, 2, 0); delete lcReg; lcReg = ellipse.doToLCRegion( csys, latticeShape, pixelAxesMap, outOrder ); AlwaysAssert( lcReg->shape() == IPosition(3, lcShape[1], lcShape[2], lcShape[0]), AipsError ); lcEllipse = dynamic_cast(lcReg); lcCenter = lcEllipse->center(); AlwaysAssert(near(lcCenter[2], 0.0), AipsError); AlwaysAssert(near(lcCenter[0], 20.5), AipsError); AlwaysAssert(near(lcCenter[1], 10.0), AipsError); lcRadii = lcEllipse->radii(); AlwaysAssert(near(lcRadii[2], 5.0), AipsError); AlwaysAssert(near(lcRadii[0], 20.0), AipsError); AlwaysAssert(near(lcRadii[1], 50.0), AipsError); // pixelAxesmap and outOrder the same means no net change :) outOrder = IPosition(3, 1, 2, 0); delete lcReg; lcReg = ellipse.doToLCRegion( csys, latticeShape, pixelAxesMap, outOrder ); AlwaysAssert( lcReg->shape() == lcShape, AipsError ); lcEllipse = dynamic_cast(lcReg); lcCenter = lcEllipse->center(); AlwaysAssert(near(lcCenter[0], 0.0), AipsError); AlwaysAssert(near(lcCenter[1], 20.5), AipsError); AlwaysAssert(near(lcCenter[2], 10.0), AipsError); lcRadii = lcEllipse->radii(); AlwaysAssert(near(lcRadii[0], 5.0), AipsError); AlwaysAssert(near(lcRadii[1], 20.0), AipsError); AlwaysAssert(near(lcRadii[2], 50.0), AipsError); delete lcReg; } { // sphere tests Vector center(3, Quantity(1, "rad")); center[2] = Quantity(1415, "GHz"); IPosition pixelAxes = IPosition(3, 0, 1, 2); Quantity r(1, "arcmin"); try { // unit mismatch between center and radius WCEllipsoid sphere( center, r, pixelAxes, csys ); AlwaysAssert(False, AipsError); } catch(std::exception& x) { cout << "Caught as expected " << x.what() << endl; } pixelAxes.resize(2, True); center.resize(2, True); WCEllipsoid sphere( center, r, pixelAxes, csys ); AlwaysAssert(sphere == sphere, AipsError); WCEllipsoid sphere2 = sphere; AlwaysAssert(sphere == sphere2, AipsError); WCEllipsoid *sphere3 = dynamic_cast(sphere.cloneRegion()); AlwaysAssert(sphere == *sphere3, AipsError); delete sphere3; TableRecord rec = sphere.toRecord(""); sphere3 = WCEllipsoid::fromRecord(rec, ""); AlwaysAssert(sphere == *sphere3, AipsError); delete sphere3; } { // 2-D ellipse tests Vector center(3, Quantity(1, "rad")); center[2] = Quantity(1415, "GHz"); Vector radius(3, Quantity(1, "arcmin")); radius[2] = Quantity(50, "MHz"); IPosition pixelAxes = IPosition(3, 0, 1, 2); try { // theta unit issue Quantity theta(4, "Hz"); WCEllipsoid ellipse( center[0], center[1], radius[0], radius[1], theta, pixelAxes[0], pixelAxes[1], csys ); AlwaysAssert(False, AipsError); } catch(std::exception& x) { cout << "Caught as expected " << x.what() << endl; } try { // axes unit mismatch Quantity theta(40, "deg"); WCEllipsoid ellipse( center[0], center[1], radius[0], radius[1], theta, pixelAxes[0], pixelAxes[2], csys ); AlwaysAssert(False, AipsError); } catch(std::exception& x) { cout << "Caught as expected " << x.what() << endl; } radius[0].setValue(2); Quantity theta(40, "deg"); WCEllipsoid ellipse( center[0], center[1], radius[0], radius[1], theta, pixelAxes[0], pixelAxes[1], csys ); AlwaysAssert(ellipse == ellipse, AipsError); WCEllipsoid ellipse2 = ellipse; AlwaysAssert(ellipse == ellipse2, AipsError); WCEllipsoid *ellipse3 = dynamic_cast(ellipse.cloneRegion()); AlwaysAssert(ellipse == *ellipse3, AipsError); delete ellipse3; TableRecord rec = ellipse.toRecord(""); ellipse3 = WCEllipsoid::fromRecord(rec, ""); AlwaysAssert(ellipse == *ellipse3, AipsError); delete ellipse3; // switch axes order try { // major axis smaller than minor axis ellipse = WCEllipsoid( center[1], center[0], radius[1], radius[0], theta, pixelAxes[1], pixelAxes[0], csys ); AlwaysAssert(False, AipsError); } catch(std::exception& x) { cout << "Caught as expected " << x.what() << endl; } ellipse = WCEllipsoid( center[1], center[0], radius[0], radius[1], theta, pixelAxes[1], pixelAxes[0], csys ); AlwaysAssert(ellipse == ellipse, AipsError); ellipse2 = ellipse; AlwaysAssert(ellipse == ellipse2, AipsError); ellipse3 = dynamic_cast(ellipse.cloneRegion()); AlwaysAssert(ellipse == *ellipse3, AipsError); delete ellipse3; rec = ellipse.toRecord(""); ellipse3 = WCEllipsoid::fromRecord(rec, ""); AlwaysAssert(ellipse == *ellipse3, AipsError); delete ellipse3; } } catch (std::exception& x) { cout << "Caught exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/images/Regions/test/tWCExtension.cc000066400000000000000000000273011476623553700220620ustar00rootroot00000000000000//# tWCExtension.cc: Test program for class WCExtension //# Copyright (C) 2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include void doIt() { // Create a dummy box to make the special units known to UnitMap. WCBox dummy; CoordinateSystem cSys (CoordinateUtil::defaultCoords3D()); // Test for region that only gets stretched. { Vector absRel(3); absRel = RegionType::Abs; Vector > blc(3); Vector > trc(3); IPosition axes(3); axes(0) = 1; axes(1) = 2; axes(2) = 0; blc(0) = Quantum (1.0, "pix"); blc(1) = Quantum (1.0, "pix"); blc(2) = Quantum (10.0, "pix"); trc(0) = Quantum (1.0, "pix"); trc(1) = Quantum (1.0, "pix"); trc(2) = Quantum (14.0, "pix"); WCBox box(blc, trc, axes, cSys, absRel); { LCRegion* regptr = box.toLCRegion (cSys, IPosition(3,30,40,50)); const Slicer& bbox = regptr->boundingBox(); cout << bbox.start() << bbox.end() << endl; delete regptr; } Vector absRel2(2); absRel2 = RegionType::Abs; Vector > blc2(2); Vector > trc2(2); IPosition axes2(2); axes2(0) = 1; axes2(1) = 2; blc2(0) = Quantum (3.0, "pix"); blc2(1) = Quantum (5.0, "pix"); trc2(0) = Quantum (10.0, "pix"); trc2(1) = Quantum (15.0, "pix"); WCBox sbox(blc2, trc2, axes2, cSys, absRel2); { LCRegion* regptr = sbox.toLCRegion (cSys, IPosition(3,30,40,50)); const Slicer& bbox = regptr->boundingBox(); cout << bbox.start() << bbox.end() << endl; delete regptr; } WCExtension wcs1(box, sbox); { LCRegion* regptr = wcs1.toLCRegion (cSys, IPosition(3,30,40,50)); const Slicer& bbox = regptr->boundingBox(); cout << bbox.start() << bbox.end() << endl; delete regptr; } } // Test for region that only gets extended. { Vector absRel(1); absRel = RegionType::Abs; Vector > blc(1); Vector > trc(1); IPosition axes(1); axes(0) = 1; blc(0) = Quantum (1.0, "pix"); trc(0) = Quantum (5.0, "pix"); WCBox box(blc, trc, axes, cSys, absRel); { LCRegion* regptr = box.toLCRegion (cSys, IPosition(3,30,40,50)); const Slicer& bbox = regptr->boundingBox(); cout << bbox.start() << bbox.end() << endl; delete regptr; } Vector absRel2(2); absRel2 = RegionType::Abs; Vector > blc2(2); Vector > trc2(2); IPosition axes2(2); axes2(0) = 2; axes2(1) = 0; blc2(0) = Quantum (3.0, "pix"); blc2(1) = Quantum (5.0, "pix"); trc2(0) = Quantum (10.0, "pix"); trc2(1) = Quantum (15.0, "pix"); WCBox sbox(blc2, trc2, axes2, cSys, absRel2); { LCRegion* regptr = sbox.toLCRegion (cSys, IPosition(3,30,40,50)); const Slicer& bbox = regptr->boundingBox(); cout << bbox.start() << bbox.end() << endl; delete regptr; } WCExtension wcs1(box, sbox); { LCRegion* regptr = wcs1.toLCRegion (cSys, IPosition(3,30,40,50)); const Slicer& bbox = regptr->boundingBox(); cout << bbox.start() << bbox.end() << endl; delete regptr; } } // Test for region that gets stretched and extended. { Vector absRel(2); absRel = RegionType::Abs; Vector > blc(2); Vector > trc(2); IPosition axes(2); axes(0) = 2; axes(1) = 0; blc(0) = Quantum (1.0, "pix"); blc(1) = Quantum (2.0, "pix"); trc(0) = Quantum (1.0, "pix"); trc(1) = Quantum (8.0, "pix"); WCBox box(blc, trc, axes, cSys, absRel); { LCRegion* regptr = box.toLCRegion (cSys, IPosition(3,30,40,50)); const Slicer& bbox = regptr->boundingBox(); cout << bbox.start() << bbox.end() << endl; delete regptr; } Vector absRel2(2); absRel2 = RegionType::Abs; Vector > blc2(2); Vector > trc2(2); IPosition axes2(2); axes2(0) = 1; axes2(1) = 2; blc2(0) = Quantum (3.0, "pix"); blc2(1) = Quantum (5.0, "pix"); trc2(0) = Quantum (10.0, "pix"); trc2(1) = Quantum (15.0, "pix"); WCBox sbox(blc2, trc2, axes2, cSys, absRel2); { LCRegion* regptr = sbox.toLCRegion (cSys, IPosition(3,30,40,50)); const Slicer& bbox = regptr->boundingBox(); cout << bbox.start() << bbox.end() << endl; delete regptr; } WCExtension wcs1(box, sbox); { LCRegion* regptr = wcs1.toLCRegion (cSys, IPosition(3,30,40,50)); const Slicer& bbox = regptr->boundingBox(); cout << bbox.start() << bbox.end() << endl; delete regptr; } Vector x(3); Vector y(3); x[0] = 3; x[1] = 3; x[2] = 8; y[0] = 3; y[1] = 8; y[2] = 3; Quantum > xq(x); xq.setUnit("pix"); Quantum > yq(y); yq.setUnit("pix"); IPosition pixelAxes(2); pixelAxes[0] = 0; pixelAxes[1] = 1; WCPolygon poly(xq, yq, pixelAxes, cSys); IPosition axes3(1,2); Vector > blc3(1); Vector > trc3(1); blc3(0) = Quantum (3.0, "pix"); trc3(0) = Quantum (10.0, "pix"); Vector absRel3(1, RegionType::Abs); WCBox ebox(blc3, trc3, axes3, cSys, absRel3); WCExtension wcspoly(poly, ebox); { LCRegion* regptr = wcspoly.toLCRegion (cSys, IPosition(3,30,40,50)); Array mask = regptr->get(); IPosition shape = mask.shape(); for (Int k=0; k=0; j--) { for (Int i=0; i< shape[0]; i++) { cout << regptr->getAt(IPosition(3,i,j,k)) << " "; } cout << endl; } cout << endl; } delete regptr; } } } Int inIPos (Int val, const IPosition& ipos) { for (uInt i=0; i absRel(4); absRel = RegionType::Abs; Vector > blc(4); Vector > trc(4); blc(0) = Quantum (regBlc(0), "pix"); blc(1) = Quantum (regBlc(1), "pix"); blc(2) = Quantum (regBlc(2), "pix"); blc(3) = Quantum (regBlc(3), "pix"); trc(0) = blc(0); trc(1) = blc(1); trc(2) = blc(2); trc(3) = blc(3); WCBox reg1(blc, trc, cSys, absRel); WCBox region (reg1.splitBox (regAxes)); // Set up blc and trc which get the extend box shape. uInt ndbox = boxAxes.nelements(); IPosition boxBlc(ndbox); IPosition boxTrc(ndbox); for (uInt i=0; i= 0) { boxBlc(i) = strBlc(axis); boxTrc(i) = strTrc(axis); } else { boxBlc(i) = extBlc(axis); boxTrc(i) = extTrc(axis); } blc(axis) = Quantum (boxBlc(i), "pix"); trc(axis) = Quantum (boxTrc(i), "pix"); } // Also fill in the expected blc and trc. IPosition resBlc(4, 0, 0, 0, 0); IPosition resTrc (latShape-1); for (Int i=0; i<4; i++) { Int axis = inIPos(i, boxAxes); if (axis >= 0) { resBlc(i) = boxBlc(axis); resTrc(i) = boxTrc(axis); } else if (inIPos(i, regAxes) >= 0) { resBlc(i) = regBlc(i); resTrc(i) = regBlc(i); } } // Make an extension of region and box. WCBox box1(blc, trc, cSys, absRel); WCBox box (box1.splitBox (boxAxes)); WCExtension wcext(region, box); // Now convert to region to an LCRegion and check the boundary box. LCRegion* regptr = wcext.toLCRegion (cSys, latShape); const Slicer& bbox = regptr->boundingBox(); if (! resBlc.isEqual (bbox.start())) { cout << regAxes << boxAxes << " Expected blc " << resBlc << ", found " << bbox.start() << endl; } if (! resTrc.isEqual (bbox.end())) { cout << regAxes << boxAxes << " Expected trc " << resTrc << ", found " << bbox.end() << endl; } delete regptr; } void testRegion (const CoordinateSystem& cSys, const IPosition& regAxes) { // Now test the region with all possible extend boxes. for (Int i0=0; i0<4; i0++) { testRegionBox (cSys, regAxes, IPosition(1,i0)); for (Int i1=0; i1<4; i1++) { if (i1 != i0) { testRegionBox (cSys, regAxes, IPosition(2,i0,i1)); for (Int i2=0; i2<4; i2++) { if (i2 != i0 && i2 != i1) { testRegionBox (cSys, regAxes, IPosition(3,i0,i1,i2)); for (Int i3=0; i3<4; i3++) { if (i3 != i0 && i3 != i1 && i3 != i2) { testRegionBox (cSys, regAxes, IPosition(4,i0,i1,i2,i3)); } } } } } } } } void testAll() { CoordinateSystem cSys (CoordinateUtil::defaultCoords4D()); // Test all possible 1,2,3,4D regions (with axes in all possible orders). for (Int i0=0; i0<4; i0++) { testRegion (cSys, IPosition(1,i0)); for (Int i1=0; i1<4; i1++) { if (i1 != i0) { testRegion (cSys, IPosition(2,i0,i1)); for (Int i2=0; i2<4; i2++) { if (i2 != i0 && i2 != i1) { testRegion (cSys, IPosition(3,i0,i1,i2)); for (Int i3=0; i3<4; i3++) { if (i3 != i0 && i3 != i1 && i3 != i2) { testRegion (cSys, IPosition(4,i0,i1,i2,i3)); } } } } } } } } int main () { try { doIt(); testAll(); } catch (std::exception& x) { cout << "Caught exception: " << x.what() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/images/Regions/test/tWCExtension.out000066400000000000000000000014741476623553700223070ustar00rootroot00000000000000[10, 1, 1][14, 1, 1] [0, 3, 5][29, 10, 15] [10, 3, 5][14, 10, 15] [0, 1, 0][29, 5, 49] [5, 0, 3][15, 39, 10] [5, 1, 3][15, 5, 10] [2, 0, 1][8, 39, 1] [0, 3, 5][29, 10, 15] [2, 3, 5][8, 10, 15] 1 0 0 0 0 0 1 1 0 0 0 0 1 1 1 0 0 0 1 1 1 1 0 0 1 1 1 1 1 0 1 1 1 1 1 1 1 0 0 0 0 0 1 1 0 0 0 0 1 1 1 0 0 0 1 1 1 1 0 0 1 1 1 1 1 0 1 1 1 1 1 1 1 0 0 0 0 0 1 1 0 0 0 0 1 1 1 0 0 0 1 1 1 1 0 0 1 1 1 1 1 0 1 1 1 1 1 1 1 0 0 0 0 0 1 1 0 0 0 0 1 1 1 0 0 0 1 1 1 1 0 0 1 1 1 1 1 0 1 1 1 1 1 1 1 0 0 0 0 0 1 1 0 0 0 0 1 1 1 0 0 0 1 1 1 1 0 0 1 1 1 1 1 0 1 1 1 1 1 1 1 0 0 0 0 0 1 1 0 0 0 0 1 1 1 0 0 0 1 1 1 1 0 0 1 1 1 1 1 0 1 1 1 1 1 1 1 0 0 0 0 0 1 1 0 0 0 0 1 1 1 0 0 0 1 1 1 1 0 0 1 1 1 1 1 0 1 1 1 1 1 1 1 0 0 0 0 0 1 1 0 0 0 0 1 1 1 0 0 0 1 1 1 1 0 0 1 1 1 1 1 0 1 1 1 1 1 1 OK casacore-3.7.1/images/Regions/test/tWCLELMask.cc000066400000000000000000000156511476623553700213430ustar00rootroot00000000000000//# tWCLELMask.cc: mechanical test of the WCLELMask class //# Copyright (C) 2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void testVectorROIter (const Lattice& lattice, Bool firstValue, Bool alternates) { Int nstep; const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(1,latticeShape(0)); LatticeStepper step(latticeShape, cursorShape); RO_LatticeIterator iter(lattice, step); Bool value = firstValue; for (iter.reset(); !iter.atEnd(); iter++){ AlwaysAssert(allEQ(iter.vectorCursor(), value), AipsError); if (alternates) { value = (!value); } } nstep = iter.nsteps(); AlwaysAssert(nstep == latticeShape.product()/latticeShape(0), AipsError); IPosition expectedPos(latticeShape-1); AlwaysAssert(iter.endPosition() == expectedPos, AipsError); expectedPos(0) = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); } int main () { try { CoordinateSystem cSys = CoordinateUtil::defaultCoords3D(); IPosition latticeShape(3, 4, 8, 11); Array arr(latticeShape); indgen (arr); PagedImage image(latticeShape, cSys, "tWCLELMask_tmp.img"); image.put (arr); image.flush(); ArrayLattice arrlat(arr); { WCLELMask mask(String("fmod(floor(tWCLELMask_tmp.img / 4), 2) == 0")); AlwaysAssertExit (mask.ndim() == latticeShape.nelements()); LCRegion* lc = mask.toLCRegion (cSys, latticeShape); AlwaysAssertExit (lc->hasMask()); AlwaysAssertExit (! lc->isWritable()); AlwaysAssertExit (lc->shape() == latticeShape); // Check the mask values using the iterator. testVectorROIter (*lc, True, True); delete lc; } { WCLELMask mask(ImageExprParse::command ("fmod(floor(tWCLELMask_tmp.img / 4), 2) == 0")); AlwaysAssertExit (mask.ndim() == latticeShape.nelements()); LCRegion* lc = mask.toLCRegion (cSys, latticeShape); AlwaysAssertExit (lc->hasMask()); AlwaysAssertExit (! lc->isWritable()); AlwaysAssertExit (lc->shape() == latticeShape); // Check the mask values using the iterator. testVectorROIter (*lc, True, True); delete lc; } { // Test if the auto-extend works fine. CoordinateSystem cSys2 = CoordinateUtil::defaultCoords2D(); IPosition shape2(2, 4, 8); Array arr2(shape2); indgen (arr2); PagedImage image2(shape2, cSys2, "tWCLELMask_tmp.img2"); image2.put (arr2); image2.flush(); WCLELMask mask("fmod(floor(tWCLELMask_tmp.img2 / 4), 2) == 0"); AlwaysAssertExit (mask.ndim() == shape2.nelements()); LCRegion* lc = mask.toLCRegion (cSys, latticeShape); AlwaysAssertExit (lc->hasMask()); AlwaysAssertExit (! lc->isWritable()); AlwaysAssertExit (lc->shape() == latticeShape); // Check the mask values using the iterator. testVectorROIter (*lc, True, True); delete lc; // Should get exception for incorrect shape. try { LCRegion* lc = mask.toLCRegion (cSys, latticeShape-1); delete lc; } catch (std::exception& x) { cout << "Expected exception: " << x.what() << endl; } } { // Test if it works fine for an expression without coordinates. WCLELMask mask(fmod(floor(arrlat / 4), 2) == 0); AlwaysAssertExit (mask.ndim() == latticeShape.nelements()); LCRegion* lc = mask.toLCRegion (cSys, latticeShape); AlwaysAssertExit (lc->hasMask()); AlwaysAssertExit (! lc->isWritable()); AlwaysAssertExit (lc->shape() == latticeShape); // Check the mask values using the iterator. testVectorROIter (*lc, True, True); delete lc; // Should get exception for incorrect shape. try { LCRegion* lc = mask.toLCRegion (cSys, latticeShape-1); delete lc; } catch (std::exception& x) { cout << "Expected exception: " << x.what() << endl; } } { // Test if it works fine for an expression without shape. WCLELMask mask("index0 in [0:3]"); AlwaysAssertExit (mask.ndim() == 0); LCRegion* lc = mask.toLCRegion (cSys, latticeShape); AlwaysAssertExit (lc->hasMask()); AlwaysAssertExit (! lc->isWritable()); AlwaysAssertExit (lc->shape() == latticeShape); // Check the mask values using the iterator. testVectorROIter (*lc, True, False); delete lc; } { // Test if it works fine for an expression without shape. WCLELMask mask("indexnotin(1,[1:7:2])"); AlwaysAssertExit (mask.ndim() == 0); LCRegion* lc = mask.toLCRegion (cSys, latticeShape); AlwaysAssertExit (lc->hasMask()); AlwaysAssertExit (! lc->isWritable()); AlwaysAssertExit (lc->shape() == latticeShape); // Check the mask values using the iterator. testVectorROIter (*lc, True, True); delete lc; } } catch (std::exception& x) { cerr << "Caught exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/images/Regions/test/tWCLELMask.out000066400000000000000000000003051476623553700215530ustar00rootroot00000000000000Expected exception: WCLELMask::toLCRegion - axes lengths of mask expression and image mismatch Expected exception: WCLELMask::toLCRegion - shapes of mask (lattice) expression and image mismatch OK casacore-3.7.1/images/Regions/test/tWCUnion.cc000066400000000000000000000102231476623553700211710ustar00rootroot00000000000000//# tWCUnion.cc: Test program for class WCUnion //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include void doIt() { // Create a dummy box to make the special units known to UnitMap. WCBox dummy; CoordinateSystem cSys (CoordinateUtil::defaultCoords3D()); { Vector absRel(2); absRel = RegionType::Abs; Vector > blc(2); Vector > trc(2); blc(0) = Quantum (10.0, "pix"); blc(1) = Quantum (1.0, "pix"); trc(0) = Quantum (14.0, "pix"); trc(1) = Quantum (3.0, "pix"); WCBox box1(blc, trc, cSys, absRel); { LCRegion* regptr = box1.toLCRegion (cSys, IPosition(3,30,40,50)); const Slicer& bbox = regptr->boundingBox(); cout << bbox.start() << bbox.end() << endl; delete regptr; } IPosition axes(2); axes(0) = 1; axes(1) = 0; blc(1) = Quantum (2.0, "pix"); trc(0) = Quantum (12.0, "pix"); WCBox box2(blc, trc, axes, cSys, absRel); { LCRegion* regptr = box2.toLCRegion (cSys, IPosition(3,30,40,50)); const Slicer& bbox = regptr->boundingBox(); cout << bbox.start() << bbox.end() << endl; delete regptr; } axes(0) = 2; axes(1) = 0; blc(0) = Quantum (10.0, "pix"); trc(0) = Quantum (14.0, "pix"); { WCBox box(blc, trc, axes, cSys, absRel); LCRegion* regptr = box.toLCRegion (cSys, IPosition(3,30,40,50)); const Slicer& bbox = regptr->boundingBox(); cout << bbox.start() << bbox.end() << endl; delete regptr; } WCUnion union1(box1, box2); { LCRegion* regptr = union1.toLCRegion (cSys, IPosition(3,30,40,50)); const Slicer& bbox = regptr->boundingBox(); cout << bbox.start() << bbox.end() << endl; delete regptr; } } { Vector absRel(1); absRel = RegionType::Abs; Vector > blc(1); Vector > trc(1); blc(0) = Quantum (10.0, "pix"); trc(0) = Quantum (14.0, "pix"); { WCBox box1(blc, trc, cSys, absRel); IPosition axes(1); axes(0) = 1; WCBox box2(blc, trc, axes, cSys, absRel); ImageRegion ir1(box1); ImageRegion ir2(box2); WCUnion union1 (&ir1, &ir2); LCRegion* regptr = union1.toLCRegion (cSys, IPosition(3,30,40,50)); const Slicer& bbox = regptr->boundingBox(); cout << bbox.start() << bbox.end() << endl; delete regptr; } } } int main() { try { doIt(); } catch (std::exception& x) { cerr << "Caught exception: " << x.what() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/images/apps/000077500000000000000000000000001476623553700155345ustar00rootroot00000000000000casacore-3.7.1/images/apps/CMakeLists.txt000066400000000000000000000004401476623553700202720ustar00rootroot00000000000000foreach(prog image2fits imagecalc imageregrid imageslice) add_executable (${prog} ${prog}.cc) add_pch_support(${prog}) target_link_libraries (${prog} casa_images ${CASACORE_ARCH_LIBS}) install(TARGETS ${prog}) endforeach(prog image2fits imagecalc imageregrid imageslice) casacore-3.7.1/images/apps/image2fits.cc000066400000000000000000000072231476623553700201010ustar00rootroot00000000000000//# image2fits.cc: Program to convert an image to FITS format //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include int main (int argc, const char* argv[]) { try { // Register the FITS and Miriad image types. casacore::FITSImage::registerOpenFunction(); casacore::MIRIADImage::registerOpenFunction(); // enable input in no-prompt mode Input inputs(1); // define the input structure inputs.version("20090915GvD"); inputs.create ("in", "", "Name of input Image or image expression", "string"); inputs.create ("out", "", "Name of output FITS file", "string"); // Fill the input structure from the command line. inputs.readArguments (argc, argv); // Get and check the input specification. String imgin (inputs.getString("in")); if (imgin == "") { throw AipsError(" an input Image or expression must be given"); } cout << "The input image is: " << imgin << endl; // Get the fits file name. String ffout(inputs.getString("out")); if (ffout == "") { throw AipsError(" an output FITS file name must be given"); } // First try to open as a normal image. ImageInterface* img = 0; String error; Bool res = True; LatticeBase* lattice = ImageOpener::openImage (imgin); if (lattice) { // Succeeded to open as an image. if (lattice->dataType() == TpFloat) { img = dynamic_cast*>(lattice); } else { // If no datatype float, convert to it using LEL. // Enclose the name in quotes. imgin = "float('" + imgin + "')"; delete lattice; } } if (img == 0) { // Try to interpret it as a LEL expression. LatticeExpr lat (ImageExprParse::command(imgin)); img = new ImageExpr (lat, imgin); } // Now write the fits file. res = ImageFITSConverter::ImageToFITS (error, *img, ffout); delete img; if (!res) { throw AipsError(error); } } catch (std::exception& x) { cout << x.what() << endl; return 1; } cout << "image2fits normally ended" << endl; return 0; } casacore-3.7.1/images/apps/imagecalc.cc000066400000000000000000000070301476623553700177500ustar00rootroot00000000000000//# imagecalc.cc: Calculate an output image from an image expression //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include using namespace casacore; int main(int argc, const char* argv[]) { try { // Register the FITS and Miriad image types. casacore::FITSImage::registerOpenFunction(); casacore::MIRIADImage::registerOpenFunction(); // Read the input parameters. Input inputs(1); inputs.version("20080710GvD"); inputs.create("in", "", "Input image or image expression", "string"); inputs.create("out", "", "Output image name (optional)", "string"); inputs.create("hdf5", "F", "output image in HDF5 format?", "bool"); inputs.readArguments(argc, argv); // Get and check the input specification. String imgin (inputs.getString("in")); if (imgin.empty()) { throw AipsError(" an input Image or expression must be given"); } // Get the output file name. String outName(inputs.getString("out")); if (outName.empty()) { outName = "/tmp/image.out"; } Bool hdf5 = inputs.getBool("hdf5"); if (hdf5 && !HDF5Object::hasHDF5Support()) { cerr << "Support for HDF5 has not been compiled in; revert to PagedImage" << endl; hdf5 = False; } LatticeExprNode node(ImageExprParse::command(imgin)); if (node.isScalar()) { if (node.dataType() == TpBool) { cout << "bool result = " << node.getBool() << endl; } else if (node.dataType() == TpFloat) { cout << "float result = " << node.getFloat() << endl; } else if (node.dataType() == TpDouble) { cout << "double result = " << node.getDouble() << endl; } else if (node.dataType() == TpComplex) { cout << "complex result = " << node.getComplex() << endl; } else { cout << "dcomplex result = " << node.getDComplex() << endl; } } else { cout << "Copying '" << imgin << "' to '" << outName << "'" << endl; ImageProxy img(imgin, String(), vector()); img.saveAs (outName, True, hdf5, True); } } catch (std::exception& x) { cout << x.what() << endl; return 1; } return 0; } casacore-3.7.1/images/apps/imageregrid.cc000066400000000000000000000173321476623553700203300ustar00rootroot00000000000000//# casacore_regrid.cc: regrid and image to the new coordsys //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#include #include #include //#include using namespace casacore; int main(int argc, const char* argv[]) { try { Input inputs(1); inputs.version("$Id$"); inputs.create("in", "", "Input image name"); inputs.create("out", "", "Output image name"); inputs.create("decimate", "10", "decimation factor"); inputs.create("dirref", "GALACTIC", "MDirection type"); inputs.create("projection", "AIT", "Projection"); inputs.create("shape", "", "Output image shape", "Block"); inputs.create("refval", "", "New center of the image in degrees (reference value)", "Block"); inputs.create("interpolation", "linear", "Interpolation method (linear, nearest, cubic,lanczos)"); inputs.readArguments(argc, argv); const String in = inputs.getString("in"); if ( in.empty() ) { cerr << "Please specify input image name" << endl; exit(1); } String out = inputs.getString("out"); if ( out.empty() ) { out = "regridded_"+in; cout << "No output name give using '" << out << "'." << endl; } Bool outisfits = downcase(out).after(out.size()-6) == ".fits"; const Int decimate = inputs.getInt("decimate"); const String dirref = inputs.getString("dirref"); Block outshape = inputs.getIntArray("shape"); const Block refval = inputs.getDoubleArray("refval"); if (refval.size() != 2) { cerr << "Please specify valid reference value e.g. refval=0.0,0.0" << endl; exit(1); } const String proj = inputs.getString("projection"); const String interpolation = inputs.getString("interpolation"); FITSImage::registerOpenFunction(); MIRIADImage::registerOpenFunction(); LatticeBase* pLatt = ImageOpener::openImage(in); ImageInterface* pImage = dynamic_cast*>(pLatt); if (!pImage) { cout << "The input image must have data type Float" << endl; exit(1); } Vector itsAxes; ImageInterface* itsImage = pImage; ImageRegrid itsIr; ImageInterface* itsTmp; Interpolate2D::Method itsMethod = Interpolate2D::stringToMethod(interpolation); itsIr.disableReferenceConversions(False); itsIr.showDebugInfo(0); Int itsDecimate = decimate; String itsProj = proj; String itsMDir = dirref; //Bool changeRefFrame = False; //changeRefFrame = (itsProj != "" || itsMDir != ""); CoordinateSystem csys(itsImage->coordinates()); Int dircoordNo = itsImage->coordinates().findCoordinate(Coordinate::DIRECTION, -1); DirectionCoordinate \ dirCoordFrom(itsImage->coordinates().directionCoordinate(dircoordNo)); Projection project(dirCoordFrom.projection()); if (itsProj != "") { project = Projection(Projection::type(itsProj)); } MDirection::Types mdirt = dirCoordFrom.directionType(); if (itsMDir != "") { MDirection::getType(mdirt, itsMDir); } Vector unitsFrom = dirCoordFrom.worldAxisUnits(); Vector radUnits(2); radUnits = String("rad"); if (!dirCoordFrom.setWorldAxisUnits(radUnits)) { cerr << "Failed to set radian units for DirectionCoordinate" << endl; delete itsImage; exit(1); } // HARDCODED IPosition shapeOut = itsImage->shape(); Vector pAx, wAx; CoordinateUtil::findDirectionAxes(pAx, wAx, dircoordNo, csys); //use output shape if valid if (outshape.size() == 2) { shapeOut[pAx(0)] = outshape[0]; shapeOut[pAx(1)] = outshape[1]; } else { cout << "Output shape not specified, using input shape " << shapeOut[pAx(0)] << "," << shapeOut[pAx(1)] << "." << endl; } Vector refPixFrom = dirCoordFrom.referencePixel(); Vector incrFrom = dirCoordFrom.increment(); Vector refValFrom = dirCoordFrom.referenceValue(); refValFrom[0] = Quantity(refval[0], "deg").getValue("rad"); refValFrom[1] = Quantity(refval[1], "deg").getValue("rad"); refPixFrom[0]= Double(outshape[0])/2.0; refPixFrom[1]= Double(outshape[1])/2.0; DirectionCoordinate dirCoordTo(mdirt, project, refValFrom(0), refValFrom(1), incrFrom(0), incrFrom(1), dirCoordFrom.linearTransform(), refPixFrom(0), refPixFrom(1)); csys.replaceCoordinate(dirCoordTo, dircoordNo); IPosition outAxes(2, pAx(0), pAx(1)); itsTmp = new TempImage(shapeOut,csys); cout << "Regridding image..." << endl; itsIr.regrid(*itsTmp, itsMethod, outAxes, *itsImage, False, itsDecimate, False); cout << "Writing " << out << "..." << endl; if (outisfits) { String errMsg; Bool res = ImageFITSConverter::ImageToFITS(errMsg, *itsTmp, out); if (!res) { cerr << errMsg << endl; } } else { ImageInterface* pim = 0; if (dynamic_cast*>(pImage) != 0) { pim = new HDF5Image (itsTmp->shape(), itsTmp->coordinates(), out); } if (pim == 0) { pim = new PagedImage(itsTmp->shape(), itsTmp->coordinates(), out); } pim->copyData(*itsTmp); ImageUtilities::copyMiscellaneous(*pim, *itsTmp); delete pim; } delete itsTmp; } catch (const AipsError &x) { cerr << "Exception caught:" << endl; cerr << x.what() << endl; } } casacore-3.7.1/images/apps/imageslice.cc000066400000000000000000000102051476623553700201430ustar00rootroot00000000000000//# imageslice: extract a subimage using pixel regions //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include using namespace casacore; int main(int argc, const char* argv[]) { try { Input inputs(1); inputs.create("in", "", "Input image name", "string"); inputs.create("out", "sliced_", "Output image name", "string"); inputs.create("outregion", "", "Output image region, specify start/end pairs for each axis and use -1 to use the input image shape", "Block"); inputs.readArguments(argc, argv); const String in = inputs.getString("in"); if ( in.empty() ) { cout << "Please specify input image name" << endl; exit(1); } String out = inputs.getString("out"); if ( out.empty() ) { out = "sliced_"+in; } Bool outisfits = downcase(out).after(out.size()-6) == ".fits"; const Block outregion = inputs.getIntArray("outregion"); FITSImage::registerOpenFunction(); MIRIADImage::registerOpenFunction(); LatticeBase* pLatt = ImageOpener::openImage(in); ImageInterface* pImage = dynamic_cast*>(pLatt); if (!pImage) { cout << "The input image must have data type Float" << endl; exit(1); } IPosition imshape = pImage->shape(); if (outregion.nelements() != pImage->shape().nelements()*2) { cout << "Please specify all start/end pairs for all axes" << endl; cout << "The shape of the image is " << imshape << endl; exit(1); } IPosition start(imshape); start = 0; IPosition end(imshape);end-=1; for (uInt i=0; i < outregion.nelements(); ++i) { if ( outregion[i] > -1 ) { if ( i%2 == 0 ) { start(i/2) = outregion[i]; } else { end(i/2) = outregion[i]; } } } Slicer slice(start, end, Slicer::endIsLast); SubImage subim(*pImage, slice); if (outisfits) { String errMsg; ImageFITSConverter::ImageToFITS(errMsg, subim, out, 128, False, False); } else { ImageInterface* pim = 0; if (dynamic_cast*>(pImage) != 0) { pim = new HDF5Image (subim.shape(), subim.coordinates(), out); } if (pim == 0) { pim = new PagedImage (subim.shape(), subim.coordinates(), out); } pim->copyData(subim); ImageUtilities::copyMiscellaneous(*pim, subim); delete pim; } delete pImage; } catch (const AipsError &x) { cerr << "Exception caught:" << endl; cerr << x.what() << endl; } } casacore-3.7.1/images/images.dox000066400000000000000000000036111476623553700165530ustar00rootroot00000000000000//# images.dox: doxygen description of images package //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ namespace casacore { // \defgroup images images package (libcasa_images) // // The images package handles N-dimensional images, their masks, // coordinates, and auxiliary info like history. // Concrete images can be stored in a Table (as a PagedImage) or in // HDF5 format (as an HDF5Image). //
        Furthermore, it is possible to use virtual images like: //
        // // It is built on top of the // lattices module. } casacore-3.7.1/lattices/000077500000000000000000000000001476623553700151345ustar00rootroot00000000000000casacore-3.7.1/lattices/CMakeLists.txt000066400000000000000000000144451476623553700177040ustar00rootroot00000000000000# # CASA Lattices # add_library (casa_lattices Lattices/Lattices_tmpl.cc Lattices/LatticeBase.cc Lattices/LatticeIndexer.cc Lattices/LatticeLocker.cc Lattices/LatticeNavigator.cc Lattices/LatticeStepper.cc Lattices/PixelCurve1D.cc Lattices/TileStepper.cc Lattices/TiledLineStepper.cc Lattices/TiledShape.cc LatticeMath/Fit2D.cc LatticeMath/LatticeAddNoise.cc LatticeMath/LatticeCleanProgress.cc LatticeMath/LatticeFit.cc LatticeMath/LatticeHistProgress.cc LatticeMath/LatticeHistSpecialize.cc LatticeMath/LatticeProgress.cc LatticeMath/LatticeStatsBase.cc LatticeMath/LattStatsProgress.cc LatticeMath/LattStatsSpecialize.cc LEL/LatticeExprNode.cc LEL/LELArrayBase.cc LEL/LELAttribute.cc LEL/LELBinary2.cc LEL/LELCoordinates.cc LEL/LELFunction2.cc LEL/LELLattCoord.cc LEL/LELLattCoordBase.cc LEL/LELRegion.cc LEL/LELUnary2.cc LRegions/FITSMask.cc LRegions/LatticeRegion.cc LRegions/LattRegionHolder.cc LRegions/LCBox.cc LRegions/LCComplement.cc LRegions/LCConcatenation.cc LRegions/LCDifference.cc LRegions/LCEllipsoid.cc LRegions/LCExtension.cc LRegions/LCHDF5Mask.cc LRegions/LCIntersection.cc LRegions/LCLELMask.cc LRegions/LCMask.cc LRegions/LCPagedMask.cc LRegions/LCPixelSet.cc LRegions/LCPolygon.cc LRegions/LCRegion.cc LRegions/LCRegion2.cc LRegions/LCRegionFixed.cc LRegions/LCRegionMulti.cc LRegions/LCRegionSingle.cc LRegions/LCSlicer.cc LRegions/LCStretch.cc LRegions/LCUnion.cc LRegions/RegionType.cc ) set(top_level_headers LatticeMath.h Lattices.h LEL.h LRegions.h ) init_pch_support(casa_lattices ${top_level_headers}) target_link_libraries ( casa_lattices casa_tables casa_scimath ${CASACORE_ARCH_LIBS} ) install (TARGETS casa_lattices LIBRARY DESTINATION lib${LIB_SUFFIX} ARCHIVE DESTINATION lib${LIB_SUFFIX} LIBRARY PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) install (FILES Lattices/ArrayLattice.h Lattices/ArrayLattice.tcc Lattices/CurvedLattice2D.h Lattices/CurvedLattice2D.tcc Lattices/ExtendLattice.h Lattices/ExtendLattice.tcc Lattices/HDF5LattIter.h Lattices/HDF5LattIter.tcc Lattices/HDF5Lattice.h Lattices/HDF5Lattice.tcc Lattices/Lattice.h Lattices/Lattice.tcc Lattices/LatticeBase.h Lattices/LatticeCache.h Lattices/LatticeCache.tcc Lattices/LatticeConcat.h Lattices/LatticeConcat.tcc Lattices/LatticeIndexer.h Lattices/LatticeIterInterface.h Lattices/LatticeIterInterface.tcc Lattices/LatticeIterator.h Lattices/LatticeIterator.tcc Lattices/LatticeLocker.h Lattices/LatticeNavigator.h Lattices/LatticeStepper.h Lattices/LatticeUtilities.h Lattices/LatticeUtilities.tcc Lattices/MaskedLattice.h Lattices/MaskedLattice.tcc Lattices/MaskedLatticeIterator.h Lattices/MaskedLatticeIterator.tcc Lattices/PagedArrIter.h Lattices/PagedArrIter.tcc Lattices/PagedArray.h Lattices/PagedArray.tcc Lattices/PixelCurve1D.h Lattices/RebinLattice.h Lattices/RebinLattice.tcc Lattices/SubLattice.h Lattices/SubLattice.tcc Lattices/TempLattice.h Lattices/TempLattice.tcc Lattices/TempLatticeImpl.h Lattices/TempLatticeImpl.tcc Lattices/TileStepper.h Lattices/TiledLineStepper.h Lattices/TiledShape.h DESTINATION include/casacore/lattices/Lattices ) install (FILES LatticeMath/StatsTiledCollapser.h LatticeMath/StatsTiledCollapser.tcc LatticeMath/CLIPNearest2D.h LatticeMath/CLIPNearest2D.tcc LatticeMath/CLInterpolator2D.h LatticeMath/CLInterpolator2D.tcc LatticeMath/Fit2D.h LatticeMath/Fit2D2.tcc LatticeMath/LatticeAddNoise.h LatticeMath/LatticeAddNoise2.tcc LatticeMath/LatticeApply.h LatticeMath/LatticeApply.tcc LatticeMath/LatticeCleanProgress.h LatticeMath/LatticeCleaner.h LatticeMath/LatticeCleaner.tcc LatticeMath/LatticeConvolver.h LatticeMath/LatticeConvolver.tcc LatticeMath/LatticeFFT.h LatticeMath/LatticeFFT.tcc LatticeMath/LatticeFit.h LatticeMath/LatticeFractile.h LatticeMath/LatticeFractile.tcc LatticeMath/LatticeHistProgress.h LatticeMath/LatticeHistSpecialize.h LatticeMath/LatticeHistSpecialize2.tcc LatticeMath/LatticeHistograms.h LatticeMath/LatticeHistograms.tcc LatticeMath/LatticeMathUtil.h LatticeMath/LatticeMathUtil.tcc LatticeMath/LatticeProgress.h LatticeMath/LatticeSlice1D.h LatticeMath/LatticeSlice1D.tcc LatticeMath/LatticeStatistics.h LatticeMath/LatticeStatistics.tcc LatticeMath/LatticeStatsBase.h LatticeMath/LatticeStatsDataProvider.h LatticeMath/LatticeStatsDataProvider.tcc LatticeMath/LatticeStatsDataProviderBase.h LatticeMath/LatticeStatsDataProviderBase.tcc LatticeMath/LatticeTwoPtCorr.h LatticeMath/LatticeTwoPtCorr.tcc LatticeMath/LattStatsProgress.h LatticeMath/LattStatsSpecialize.h LatticeMath/LattStatsSpecialize2.tcc LatticeMath/LineCollapser.h LatticeMath/LineCollapser.tcc LatticeMath/MaskedLatticeStatsDataProvider.h LatticeMath/MaskedLatticeStatsDataProvider.tcc LatticeMath/MultiTermLatticeCleaner.h LatticeMath/MultiTermLatticeCleaner.tcc LatticeMath/TiledCollapser.h LatticeMath/TiledCollapser.tcc DESTINATION include/casacore/lattices/LatticeMath ) install (FILES LEL/LatticeExpr.h LEL/LatticeExpr.tcc LEL/LatticeExprNode.h LEL/LELArray.h LEL/LELArray.tcc LEL/LELArrayBase.h LEL/LELAttribute.h LEL/LELBinary.h LEL/LELBinary.tcc LEL/LELBinaryEnums.h LEL/LELCondition.h LEL/LELCondition.tcc LEL/LELConvert.h LEL/LELConvert.tcc LEL/LELCoordinates.h LEL/LELFunction.h LEL/LELFunction.tcc LEL/LELFunctionEnums.h LEL/LELInterface.h LEL/LELInterface.tcc LEL/LELLattCoord.h LEL/LELLattCoordBase.h LEL/LELLattice.h LEL/LELLattice.tcc LEL/LELRegion.h LEL/LELScalar.h LEL/LELScalar.tcc LEL/LELSpectralIndex.h LEL/LELSpectralIndex.tcc LEL/LELUnary.h LEL/LELUnary.tcc LEL/LELUnaryEnums.h DESTINATION include/casacore/lattices/LEL ) install (FILES LRegions/FITSMask.h LRegions/LatticeRegion.h LRegions/LattRegionHolder.h LRegions/LCBox.h LRegions/LCComplement.h LRegions/LCConcatenation.h LRegions/LCDifference.h LRegions/LCEllipsoid.h LRegions/LCExtension.h LRegions/LCHDF5Mask.h LRegions/LCIntersection.h LRegions/LCLELMask.h LRegions/LCMask.h LRegions/LCPagedMask.h LRegions/LCPixelSet.h LRegions/LCPolygon.h LRegions/LCRegion.h LRegions/LCRegionFixed.h LRegions/LCRegionMulti.h LRegions/LCRegionSingle.h LRegions/LCSlicer.h LRegions/LCStretch.h LRegions/LCUnion.h LRegions/RegionType.h DESTINATION include/casacore/lattices/LRegions ) install (FILES ${top_level_headers} DESTINATION include/casacore/lattices ) # The tests add_subdirectory (Lattices/test ${EXCL_ALL}) add_subdirectory (LatticeMath/test ${EXCL_ALL}) add_subdirectory (LEL/test ${EXCL_ALL}) add_subdirectory (LRegions/test ${EXCL_ALL}) casacore-3.7.1/lattices/LEL.h000066400000000000000000000054561476623553700157330ustar00rootroot00000000000000//# LEL.h: Lattice expression //# Copyright (C) 1996,1997,1998,1999,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LEL_H #define LATTICES_LEL_H #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // // Lattice expressions. // // //
      • module Lattices // // // // // LEL: Lattice Expression Language. // // // A LatticeExpr represents // a mathematical expression of lattices. All standard operators, regions, // and many, many functions // can be used in an expression. //
        An expression is calculated on-the-fly. Thus only when // the user gets a part of the lattice, is the expression calculated // for that part. Subexpressions resulting in a scalar are calculated // only once, on a get of the first part of the lattice expression. //
        Note that a lattice expression is not writable, thus using // the put function on such a lattice results in an exception. //
        Note 223 // gives a more detailed // explanation of the capabilities of LEL (Lattice Expression Language). //

        // If the expression consists of images, the result can also be // treated as an image using class ImageExpr. // With the command function in // ImageExprParse it is possible // to parse and execute a LEL expression given as as a string. // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LEL/000077500000000000000000000000001476623553700155505ustar00rootroot00000000000000casacore-3.7.1/lattices/LEL/LELArray.h000066400000000000000000000102661476623553700173410ustar00rootroot00000000000000//# LELArray.h: Hold an array with a mask in LEL //# Copyright (C) 1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LELARRAY_H #define LATTICES_LELARRAY_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //

        // This LEL class holds an array with a mask. // // // // // // This LEL class holds an array with a mask. // The mask can be a single Bool valid for all elements of the array. // Otherwise it is a full mask with the same shape as the array. // // // It makes it possible to handle an array with its mask as a single object. // // // template class LELArray : public LELArrayBase { public: // Constructor takes value. // Its mask is set to all True. LELArray (const Array& value) : itsValue (value) {} // Constructor takes value and mask. LELArray (const Array& value, const Array& mask) : LELArrayBase (mask), itsValue (value) {} // Constructor takes shape. // Its mask is set to all True. LELArray (const IPosition& shape); // Copy constructor (reference semantics). LELArray (const LELArray& other); ~LELArray(); // Assignment (reference semantics). LELArray& operator= (const LELArray& other); // Get shape (of the value). const IPosition& shape() const { return itsValue.shape(); } // Get value. // const Array& value() const { return itsValue; } Array& value() { return itsValue; } // private: Array itsValue; }; // // This LEL class holds a possible referenced array with a mask. // // // // // // This LEL class is derived from LELArray. // Its purpose is to provide only const access to the array value, so // the array can be a reference to another array. // It is meant for optimization, so references can safely be used // when evaluating a subexpression. // // // It makes it possible to use the function evalRef in a safe way. // It would be unsafe to use a LELArray object, because that // gives non-const access to the value. // // // template class LELArrayRef : public LELArray { public: // Constructor takes shape. // Its mask is set to all True. LELArrayRef (const IPosition& shape) : LELArray (shape) {} ~LELArrayRef() {} // Get value. const Array& value() const { return LELArray::value(); } private: // Copy constructor is not needed. LELArrayRef (const LELArrayRef& other); // Assignment is not needed. LELArrayRef& operator= (const LELArrayRef& other); }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/LEL/LELArray.tcc000066400000000000000000000035561476623553700176670ustar00rootroot00000000000000//# LELArray.cc: Hold an array with a mask in LEL //# Copyright (C) 1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LELARRAY_TCC #define LATTICES_LELARRAY_TCC #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template LELArray::LELArray (const IPosition& shape) : itsValue (shape) {} template LELArray::~LELArray() {} template LELArray::LELArray (const LELArray& other) : LELArrayBase() { operator= (other); } template LELArray& LELArray::operator= (const LELArray& other) { if (this != &other) { LELArrayBase::operator= (other); Array temp (other.value()); itsValue.reference (temp); } return *this; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LEL/LELArrayBase.cc000066400000000000000000000130201476623553700202610ustar00rootroot00000000000000//# LELArrayBase.cc: Base class for LELArray holding the mask //# Copyright (C) 1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LELArrayBase::LELArrayBase (const LELArrayBase& other) : itsMaskPtr (0) { operator= (other); } LELArrayBase::~LELArrayBase() { delete itsMaskPtr; } LELArrayBase& LELArrayBase::operator= (const LELArrayBase& other) { if (this != &other) { delete itsMaskPtr; itsMaskPtr = 0; if (other.itsMaskPtr != 0) { itsMaskPtr = new Array (*other.itsMaskPtr); } } return *this; } void LELArrayBase::removeMask() { delete itsMaskPtr; itsMaskPtr = 0; } void LELArrayBase::setMask (const Array& mask) { delete itsMaskPtr; itsMaskPtr = new Array (mask); } void LELArrayBase::setMask (Array& mask) { delete itsMaskPtr; itsMaskPtr = new Array (mask); } void LELArrayBase::setMask (const LELArrayBase& other) { removeMask(); if (other.isMasked()) { itsMaskPtr = new Array (other.mask()); } } void LELArrayBase::combineMask (const Array& mask) { if (!isMasked()) { itsMaskPtr = new Array (mask); } else { Bool del1, del2; Bool* m1 = itsMaskPtr->getStorage (del1); const Bool* m2 = mask.getStorage (del2); uInt nr = itsMaskPtr->nelements(); for (uInt i=0; iputStorage (m1, del1); mask.freeStorage (m2, del2); } } void LELArrayBase::combineOrAnd (Bool desiredValue, const Array& value) { // Combine the mask for an array and a scalar with a false mask. Bool deleteValue, deleteMask; const Bool* val = value.getStorage (deleteValue); uInt nr = value.nelements(); if (itsMaskPtr == 0) { // Entire mask is true, so create one. itsMaskPtr = new Array (value.shape()); *itsMaskPtr = True; } // If value is unequal desiredValue, mask should also be false // (because False || Unknown == Unknown and True && Unknown == Unknown). Bool* m = itsMaskPtr->getStorage (deleteMask); uInt ntrue = 0; for (uInt i=0; iputStorage (m, deleteMask); if (ntrue == nr) { removeMask(); } value.freeStorage (val, deleteValue); } void LELArrayBase::combineOrAnd (Bool desiredValue, Array& value, const Array& temp) { Bool deleteValue, deleteTemp, deleteMask; Bool* val = value.getStorage (deleteValue); const Bool* tmp = temp.getStorage (deleteTemp); uInt nr = value.nelements(); if (itsMaskPtr == 0) { for (uInt i=0; igetStorage (deleteMask); uInt ntrue = 0; for (uInt i=0; iputStorage (m, deleteMask); if (ntrue == nr) { removeMask(); } } value.putStorage (val, deleteValue); temp.freeStorage (tmp, deleteTemp); } void LELArrayBase::combineOrAnd (Bool desiredValue, Array& value, const Array& temp, const Array& tempMask) { Bool deleteValue, deleteTemp, deleteMask, deleteTempMask; Bool* val = value.getStorage (deleteValue); const Bool* tmp = temp.getStorage (deleteTemp); const Bool* tm = tempMask.getStorage (deleteTempMask); uInt nr = value.nelements(); if (itsMaskPtr == 0) { itsMaskPtr = new Array(value.shape()); *itsMaskPtr = True; } Bool* m = itsMaskPtr->getStorage (deleteMask); uInt ntrue = 0; for (uInt i=0; iputStorage (m, deleteMask); if (ntrue == nr) { removeMask(); } value.putStorage (val, deleteValue); temp.freeStorage (tmp, deleteTemp); tempMask.freeStorage (tm, deleteTempMask); } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LEL/LELArrayBase.h000066400000000000000000000105621476623553700201330ustar00rootroot00000000000000//# LELArrayBase.h: Base class for LELArray holding the mask //# Copyright (C) 1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LELARRAYBASE_H #define LATTICES_LELARRAYBASE_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Base class for LELArray holding the mask. // // // // // // This LEL class holds an array with a mask. // The mask can be a single Bool valid for all elements of the array. // Otherwise it is a full mask with the same shape as the array. // // // It maskes it possible to handle an array with its mask as a single object. // // // class LELArrayBase { public: // Default constructor sets to mask all true. LELArrayBase() : itsMaskPtr(0) {} // Constructor takes mask. LELArrayBase (const Array& mask) : itsMaskPtr(new Array(mask)) {} // Copy constructor (reference semantics). LELArrayBase (const LELArrayBase& other); ~LELArrayBase(); // Assignment (reference semantics). LELArrayBase& operator= (const LELArrayBase& other); // Does the value have a mask? Bool isMasked() const { return (itsMaskPtr != 0); } // Get mask. // const Array& mask() const { return *itsMaskPtr; } Array& mask() { return *itsMaskPtr; } // // Remove the mask. void removeMask(); // Set the mask from given array (takes reference). void setMask (const Array& other); // Set the mask from the mask of the other value. void setMask (const LELArrayBase& other); // Set the mask from given array (takes reference). void setMask (Array& other); // Set the mask by combining the masks of both values. void setMask (const LELArrayBase& left, const LELArrayBase& right) { setMask (left); combineMask (right); } // Combine the mask of this and the other value (by anding them). // void combineMask (const LELArrayBase& other) { if (other.isMasked()) combineMask (other.mask()); } void combineMask (const Array& mask); // // Combine the mask with the given value in case of an OR or AND. // It means the mask is set to true if value is desiredValue // (which should be True for OR and False for AND). // // Combine with a single scalar value for which the mask is false. void combineOrAnd (Bool desiredValue, const Array& value); // Combine for two arrays taking the true/false array values into account. // The mask and value are set to desiredValue if the temp value is desiredValue. void combineOrAnd (Bool desiredValue, Array& value, const Array& temp); // Combine for two arrays taking the true/false array values and mask // into account. // The mask and value are set to desiredValue if the temp value is desiredValue // and its temp mask it true. // The mask is set to false if the temp mask is False. void combineOrAnd (Bool desiredValue, Array& value, const Array& temp, const Array& tempMask); // private: Array* itsMaskPtr; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LEL/LELAttribute.cc000066400000000000000000000150341476623553700203620ustar00rootroot00000000000000//# LELAttribute.cc: Ancillary information for the LEL letter classes //# Copyright (C) 1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LELAttribute::LELAttribute() : isScalar_p (True), isReduced_p (True), isRegion_p (False), isMasked_p (False), coords_p (new LELLattCoord()) {} LELAttribute::LELAttribute (Bool isMasked, const IPosition& shape, const IPosition& tileShape, const LELCoordinates& coordinates, Bool isReduced) : isScalar_p (False), isReduced_p (isReduced), isRegion_p (False), isMasked_p (isMasked), shape_p (shape), tileShape_p (tileShape), coords_p (coordinates) { if (coords_p.isNull()) { coords_p = LELCoordinates (new LELLattCoord()); } } LELAttribute::LELAttribute (uInt regionNdim) : isScalar_p (False), isReduced_p (False), isRegion_p (True), isMasked_p (False), shape_p (IPosition(regionNdim, 0)), coords_p (new LELLattCoord()) {} LELAttribute::LELAttribute (const LELAttribute& other) : isScalar_p (other.isScalar_p), isReduced_p (other.isReduced_p), isRegion_p (other.isRegion_p), isMasked_p (other.isMasked_p), shape_p (other.shape_p), tileShape_p (other.tileShape_p), coords_p (other.coords_p) {} LELAttribute::LELAttribute (const LELAttribute& leftAttr, const LELAttribute& rightAttr, Bool matchAxes) { isScalar_p = False; isRegion_p = False; isMasked_p = (leftAttr.isMasked() || rightAttr.isMasked()); if (leftAttr.isRegion() || rightAttr.isRegion()) { throw (AipsError ("LELAttribute: regions cannot be combined here")); } if (leftAttr.isScalar()) { if (rightAttr.isScalar()) { isScalar_p = True; isReduced_p = True; isMasked_p = False; } else { isReduced_p = rightAttr.isReduced(); shape_p = rightAttr.shape(); tileShape_p = rightAttr.tileShape(); coords_p = rightAttr.coordinates(); } } else { isReduced_p = leftAttr.isReduced(); shape_p = leftAttr.shape(); tileShape_p = leftAttr.tileShape(); coords_p = leftAttr.coordinates(); if (!rightAttr.isScalar()) { // Two arrays are combined. // The result is reduced if one of them is reduced. if (rightAttr.isReduced()) { isReduced_p = True; } // Check shapes if both are defined. const IPosition& rShape = rightAttr.shape(); Bool ok = False; if (shape_p.nelements() == 0) { shape_p = rShape; ok = True; } else if (rShape.nelements() == 0) { ok = True; } if (!ok && matchAxes) { ok = shape_p.isEqual (rShape); } else if (shape_p.nelements() > rShape.nelements()) { ok = shape_p.isSubSet (rShape); } else { ok = rShape.isSubSet (shape_p); shape_p.resize(0); shape_p = rShape; tileShape_p.resize(0); tileShape_p = rightAttr.tileShape(); } if (!ok) { throw AipsError ("LELAttribute: " "shapes of operands mismatch"); } if (rightAttr.coordinates().hasCoordinates()) { if (coords_p.hasCoordinates()) { Int result = leftAttr.coordinates().compare (rightAttr.coordinates()); if (matchAxes) { if (result != 0) { throw AipsError ("LELAttribute: " "coordinates of operands mismatch"); } } else { if (result == -1) { // left is subset, so use coordinates of other operand coords_p = rightAttr.coordinates(); } else if (result > 1) { throw AipsError ("LELAttribute: " "coordinates of operands incompatible"); } } } else { coords_p = rightAttr.coordinates(); } } } } } LELAttribute::~LELAttribute() {} LELAttribute& LELAttribute::operator= (const LELAttribute& other) { if (this != &other) { isScalar_p = other.isScalar_p; isReduced_p = other.isReduced_p; isRegion_p = other.isRegion_p; isMasked_p = other.isMasked_p; shape_p.resize (other.shape_p.nelements()); tileShape_p.resize (other.tileShape_p.nelements()); shape_p = other.shape_p; tileShape_p = other.tileShape_p; coords_p = other.coords_p; } return *this; } Int LELAttribute::compareCoord (const LELAttribute& other) const { // Both scalars is always equal. if (isScalar() || other.isScalar()) { return 0; } // Compare coordinates; exit if not equal. Int result = coordinates().compare (other.coordinates()); if (result != 0) { return result; } // The coordinates are equal, check if shapes are also equal. // A length can be 1, thus be a subset of the other. const IPosition& thisShape = shape(); const IPosition& thatShape = other.shape(); if (thisShape.nelements() != thatShape.nelements()) { return 8; } for (uInt i=0; i 1) { // This is subset of that; check if not already the other way. if (result == 1) { return 8; } result = -1; } else if (thisShape(i) > 1 && thatShape(i) == 1) { // That is subset of this; check if not already the other way. if (result == -1) { return 8; } result = 1; } else { // Mismatching shapes return 8; } } } return result; } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LEL/LELAttribute.h000066400000000000000000000115361476623553700202270ustar00rootroot00000000000000//# LELAttribute.h: Ancillary information for the LEL letter classes //# Copyright (C) 1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LELATTRIBUTE_H #define LATTICES_LELATTRIBUTE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Ancillary information for the LEL letter classes. // // // // // // // // //
      • Lattice //
      • LatticeExpr //
      • LatticeExprNode //
      • LELInterface and // derived classes // // // // Holds attribute information for the Lattice Expression // Language letter classes. // // // The Lattice Expression Language letter classes provide // expression objects. There is ancilliary information or // attributes associated with these objects: //
          //
        • Scalar or lattice (i.e. array) or region. //
        • In case of an array, is it a reduced array. I.e. is it an array // that has to be calculated beforehand (e.g. partialMax). // A scalar is always reduced. //
        • Shape and tile shape of a lattice. This can be undefined. //
        • Is the lattice masked? //
        • Optionally coordinates of the lattice. //
        // Two attribute objects can be combined mirroring the combination of two // expressions (like the addition of two lattices). // Regions cannot be combined. //
        class LELAttribute { public: // Default constructor sets it as a scalar. LELAttribute(); // Constructor sets it as lattice with given attributes. // An empty shape indicates that the shape is not known. LELAttribute(Bool isMasked, const IPosition& shape, const IPosition& tileShape, const LELCoordinates& coordinates, Bool isReduced = False); // Constructor sets it as a region with given attributes. explicit LELAttribute(uInt regionNdim); // Copy constructor (copy semantics) LELAttribute(const LELAttribute& attr); // Constructor that combines the two attributes given. // An array can be combined with a scalar. // If matchAxes is True and if two arrays are given, the shapes and // coordinates have to match exactly, otherwise one can be a subset of // the other (and LEL will auto-extend). LELAttribute(const LELAttribute& attrLeft, const LELAttribute& attrRight, Bool matchAxes = True); // Destructor ~LELAttribute(); // Assignment (copy semantics) LELAttribute& operator= (const LELAttribute& other); // Is expression a scalar? Bool isScalar() const { return isScalar_p; } // Is expression a reduced array? A scalar is always reduced. Bool isReduced() const { return isReduced_p; } // Is expression a region? Bool isRegion() const { return isRegion_p; } // Is the expression result masked? Bool isMasked() const { return isMasked_p; } // What is the shape of the expression? const IPosition& shape() const { return shape_p; } // What is the tile shape of the expression? const IPosition& tileShape() const { return tileShape_p; } // What are the coordinates of the expression? const LELCoordinates& coordinates() const { return coords_p; } // Compare the coordinates and shapes to see if this is a subset of other. Int compareCoord (const LELAttribute& other) const; private: Bool isScalar_p; Bool isReduced_p; Bool isRegion_p; Bool isMasked_p; IPosition shape_p; IPosition tileShape_p; LELCoordinates coords_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LEL/LELBinary.h000066400000000000000000000262461476623553700175140ustar00rootroot00000000000000//# LELBinary.h: LELBinary.h //# Copyright (C) 1997,1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LELBINARY_H #define LATTICES_LELBINARY_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // This LEL class handles numerical binary operators // // // // // // // //
      • Lattice //
      • LatticeExpr //
      • LatticeExprNode //
      • LELInterface //
      • LELBinaryEnums // // // // This derived LEL letter class handles numerical binary // operators // // // // This LEL letter class is derived from LELInterface. It // is used to construct LEL objects that apply numerical binary // operators to Lattice expressions. They operate on numerical // Lattice (Float,Double,Complex,DComplex) expressions and return the // same numerical type. The available C++ operators // are +,-,*,/ with equivalents in the enum // of ADD, SUBTRACT, MULTIPLY, and DIVIDE. // // A description of the implementation details of the LEL classes can // be found in // Note 216 // // // // // Examples are not very useful as the user would never use // these classes directly. Look in LatticeExprNode.cc to see // how it invokes these classes. Examples of how the user // would indirectly use this class (through the envelope) are: // // IPosition shape(2,5,10); // ArrayLattice x(shape); x.set(1.0); // ArrayLattice y(shape); y.set(2.0); // ArrayLattice z(shape); // z.copyData(x+y); // z = x + y; // z.copyData(x-y); // z = x - y; // z.copyData(x*y); // z = x * y; // z.copyData(x/y); // z = x / y; // // // // // Numerical binary operations are a basic mathematical expression. // // // // template class LELBinary : public LELInterface { //# Make members of parent class known. protected: using LELInterface::setAttr; public: // Constructor takes operation and left and right expressions // to be operated upon LELBinary(const LELBinaryEnums::Operation op, const std::shared_ptr>& pLeftExpr, const std::shared_ptr>& pRightExpr); // Destructor ~LELBinary(); // Recursively evaluate the expression virtual void eval (LELArray& result, const Slicer& section) const; // Recursively efvaluate the scalar expression virtual LELScalar getScalar() const; // Do further preparations (e.g. optimization) on the expression. virtual Bool prepareScalarExpr(); // Get class name virtual String className() const; // Handle locking/syncing of a lattice in a lattice expression. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; virtual void resync(); // private: LELBinaryEnums::Operation op_p; std::shared_ptr> pLeftExpr_p; std::shared_ptr> pRightExpr_p; }; // This LEL class handles relational binary numerical operators // // // // // // // //
      • Lattice //
      • LatticeExpr //
      • LatticeExprNode //
      • LELInterface //
      • LELBinaryEnums // // // // This derived LEL letter class handles relational numerical binary // operators // // // // This LEL letter class is derived from LELInterface. It // is used to construct LEL objects that apply relational numerical // binary operators to Lattice expressions. They operate on numerical // (Float,Double,Complex,DComplex) Lattice expressions and result // in a Bool. The available C++ operators are // ==,!=>,>=,<,<=, with equivalents in the enum of // EQ, NE, GT, GE, LT, and LE // // A description of the implementation details of the LEL classes can // be found in // Note 216 // // // // // Examples are not very useful as the user would never use // these classes directly. Look in LatticeExprNode.cc to see // how it invokes these classes. Examples of how the user // would indirectly use this class (through the envelope) are: // // IPosition shape(2,5,10); // ArrayLattice x(shape); x.set(1.0); // ArrayLattice y(shape); y.set(2.0); // ArrayLattice z(shape); // z.copyData(x==y); // z = x == y; // z.copyData(x!=y); // z = x != y; // z.copyData(x>y); // z = x > y; // z.copyData(x>=y); // z = x >= y; // z.copyData(x // // // // Numerical relational binary operations are a basic mathematical expression. // // // // template class LELBinaryCmp : public LELInterface { public: // Constructor takes operation and left and right expressions // to be operated upon. It can only handle the comparison operators. LELBinaryCmp(const LELBinaryEnums::Operation op, const std::shared_ptr>& pLeftExpr, const std::shared_ptr>& pRightExpr); // Destructor ~LELBinaryCmp(); // Recursively evaluate the expression virtual void eval (LELArray& result, const Slicer& section) const; // Recursively evaluate the scalar expression virtual LELScalar getScalar() const; // Do further preparations (e.g. optimization) on the expression. virtual Bool prepareScalarExpr(); // Get class name virtual String className() const; // Handle locking/syncing of a lattice in a lattice expression. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; virtual void resync(); // private: LELBinaryEnums::Operation op_p; std::shared_ptr> pLeftExpr_p; std::shared_ptr> pRightExpr_p; }; // This LEL class handles logical binary operators // // // // // // // //
      • Lattice //
      • LatticeExpr //
      • LatticeExprNode //
      • LELInterface //
      • LELBinaryEnums // // // This derived LEL letter class handles logical binary operators // // // // This LEL letter class is derived from LELInterface. It // is used to construct LEL objects that apply logical // binary operators to Lattice expressions. They apply only // to Bool Lattice expressions and result in a Bool. The // available C++ operators are &&,||,==,!= with // equivalents in the enum of AND, OR, EQ, and NE // // A description of the implementation details of the LEL classes can // be found in // Note 216 // // // // // Examples are not very useful as the user would never use // these classes directly. Look in LatticeExprNode.cc to see // how it invokes these classes. Examples of how the user // would indirectly use this class (through the envelope) are: // // IPosition shape(2,5,10); // ArrayLattice x(shape); x.set(False); // ArrayLattice y(shape); y.set(True); // ArrayLattice z(shape); z.set(False); // z.copyData(x&&y); // z = x && y; // z.copyData(x||y); // z = x || y; // z.copyData(x==y); // z = x == y; // z.copyData(x!=y); // z = x != y; // // // // // Logical binary operations are a basic mathematical expression. // // // // class LELBinaryBool : public LELInterface { public: // Constructor takes operation and left and right expressions // to be operated upon. LELBinaryBool(const LELBinaryEnums::Operation op, const std::shared_ptr>& pLeftExpr, const std::shared_ptr>& pRightExpr); // Destructor ~LELBinaryBool(); // Recursively evaluate the expression virtual void eval (LELArray& result, const Slicer& section) const; // Recursively evaluate the scalar expression virtual LELScalar getScalar() const; // Do further preparations (e.g. optimization) on the expression. virtual Bool prepareScalarExpr(); // Get class name virtual String className() const; // Handle locking/syncing of a lattice in a lattice expression. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; virtual void resync(); // private: LELBinaryEnums::Operation op_p; std::shared_ptr> pLeftExpr_p; std::shared_ptr> pRightExpr_p; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/LEL/LELBinary.tcc000066400000000000000000000320741476623553700200320ustar00rootroot00000000000000//# LELBinary.cc: this defines templated classes in LELBinary.h //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LELBINARY_TCC #define LATTICES_LELBINARY_TCC #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template LELBinary::LELBinary(const LELBinaryEnums::Operation op, const std::shared_ptr>& pLeftExpr, const std::shared_ptr>& pRightExpr) : op_p(op) { setAttr (LELAttribute(pLeftExpr->getAttribute(), pRightExpr->getAttribute())); // Fill these variables here, so an exception in setAttr does // not leave them undestructed. pLeftExpr_p = pLeftExpr; pRightExpr_p = pRightExpr; #if defined(AIPS_TRACE) cout << "LELBinary: constructor" << endl; cout << "LELBinary: left.name = " << pLeftExpr->className() << endl; cout << "LELBinary: right.name = " << pRightExpr->className() << endl; #endif } template LELBinary::~LELBinary() { #if defined(AIPS_TRACE) cout << "LELBinary: destructor" << endl; #endif } template void LELBinary::eval(LELArray& result, const Slicer& section) const { #if defined(AIPS_TRACE) cout << "LELBinary: eval " << endl; #endif // Evaluate the expression. // We are sure that the operands do not have an all false mask, // so in the scalar case the possible mask is not changed. // If both operands are arrays, the masks are combined. switch(op_p) { case LELBinaryEnums::ADD : if (pLeftExpr_p->isScalar()) { pRightExpr_p->eval(result, section); result.value() += pLeftExpr_p->getScalar().value(); } else if (pRightExpr_p->isScalar()) { pLeftExpr_p->eval(result, section); result.value() += pRightExpr_p->getScalar().value(); } else { pLeftExpr_p->eval(result, section); LELArrayRef temp(result.shape()); pRightExpr_p->evalRef(temp, section); result.value() += temp.value(); result.combineMask (temp); } break; case LELBinaryEnums::SUBTRACT: if (pLeftExpr_p->isScalar()) { pRightExpr_p->eval(result, section); result.value() = pLeftExpr_p->getScalar().value() - result.value(); } else if (pRightExpr_p->isScalar()) { pLeftExpr_p->eval(result, section); result.value() -= pRightExpr_p->getScalar().value(); } else { pLeftExpr_p->eval(result, section); LELArrayRef temp(result.shape()); pRightExpr_p->evalRef(temp, section); result.value() -= temp.value(); result.combineMask (temp); } break; case LELBinaryEnums::MULTIPLY: if (pLeftExpr_p->isScalar()) { pRightExpr_p->eval(result, section); result.value() *= pLeftExpr_p->getScalar().value(); } else if (pRightExpr_p->isScalar()) { pLeftExpr_p->eval(result, section); result.value() *= pRightExpr_p->getScalar().value(); } else { pLeftExpr_p->eval(result, section); LELArrayRef temp(result.shape()); pRightExpr_p->evalRef(temp, section); result.value() *= temp.value(); result.combineMask (temp); } break; case LELBinaryEnums::DIVIDE: if (pLeftExpr_p->isScalar()) { pRightExpr_p->eval(result, section); result.value() = pLeftExpr_p->getScalar().value() / result.value(); } else if (pRightExpr_p->isScalar()) { pLeftExpr_p->eval(result, section); result.value() /= pRightExpr_p->getScalar().value(); } else { pLeftExpr_p->eval(result, section); LELArrayRef temp(result.shape()); pRightExpr_p->evalRef(temp, section); result.value() /= temp.value(); result.combineMask (temp); } break; default: throw(AipsError("LELBinary::eval - unknown operation")); } } template LELScalar LELBinary::getScalar() const { #if defined(AIPS_TRACE) cout << "LELBinary: getScalar " << endl; #endif LELScalar temp = pLeftExpr_p->getScalar(); switch(op_p) { case LELBinaryEnums::ADD : temp.value() += pRightExpr_p->getScalar().value(); break; case LELBinaryEnums::SUBTRACT : temp.value() -= pRightExpr_p->getScalar().value(); break; case LELBinaryEnums::MULTIPLY : temp.value() *= pRightExpr_p->getScalar().value(); break; case LELBinaryEnums::DIVIDE : temp.value() /= pRightExpr_p->getScalar().value(); break; default: throw(AipsError("LELBinary::getScalar - unknown operation")); } return temp; } template Bool LELBinary::prepareScalarExpr() { #if defined(AIPS_TRACE) cout << "LELBinary::prepare" << endl; #endif if (LELInterface::replaceScalarExpr (pLeftExpr_p)) { return True; } if (LELInterface::replaceScalarExpr (pRightExpr_p)) { return True; } return False; } template String LELBinary::className() const { return String("LELBinary"); } template Bool LELBinary::lock (FileLocker::LockType type, uInt nattempts) { if (! pLeftExpr_p->lock (type, nattempts)) { return False; } return pRightExpr_p->lock (type, nattempts); } template void LELBinary::unlock() { pLeftExpr_p->unlock(); pRightExpr_p->unlock(); } template Bool LELBinary::hasLock (FileLocker::LockType type) const { return pLeftExpr_p->hasLock (type) && pRightExpr_p->hasLock (type); } template void LELBinary::resync() { pLeftExpr_p->resync(); pRightExpr_p->resync(); } // LELBinaryCmp template LELBinaryCmp::LELBinaryCmp(const LELBinaryEnums::Operation op, const std::shared_ptr>& pLeftExpr, const std::shared_ptr>& pRightExpr) : op_p(op) { setAttr (LELAttribute(pLeftExpr->getAttribute(), pRightExpr->getAttribute())); // Fill these variables here, so an exception in setAttr does // not leave them undestructed. pLeftExpr_p = pLeftExpr; pRightExpr_p = pRightExpr; #if defined(AIPS_TRACE) cout << "LELBinaryCmp: constructor" << endl; cout << "LELBinaryCmp: left.name = " << pLeftExpr->className() << endl; cout << "LELBinaryCmp: right.name = " << pRightExpr->className() << endl; #endif } template LELBinaryCmp::~LELBinaryCmp() { #if defined(AIPS_TRACE) cout << "LELBinaryCmp: destructor" << endl; #endif } template void LELBinaryCmp::eval(LELArray& result, const Slicer& section) const { #if defined(AIPS_TRACE) cout << "LELBinaryCmp: eval " << endl; #endif switch(op_p) { case LELBinaryEnums::EQ : if (pLeftExpr_p->isScalar()) { LELArrayRef temp(result.shape()); pRightExpr_p->evalRef (temp, section); Array res(pLeftExpr_p->getScalar().value() == temp.value()); result.value().reference (res); result.setMask (temp); } else if (pRightExpr_p->isScalar()) { LELArrayRef temp(result.shape()); pLeftExpr_p->evalRef (temp, section); Array res(temp.value() == pRightExpr_p->getScalar().value()); result.value().reference (res); result.setMask (temp); } else { LELArrayRef templ(result.shape()); LELArrayRef tempr(result.shape()); pLeftExpr_p->evalRef (templ, section); pRightExpr_p->evalRef(tempr, section); Array res(templ.value() == tempr.value()); result.value().reference (res); result.setMask (templ, tempr); } break; case LELBinaryEnums::GT : if (pLeftExpr_p->isScalar()) { LELArrayRef temp(result.shape()); pRightExpr_p->evalRef (temp, section); Array res(pLeftExpr_p->getScalar().value() > temp.value()); result.value().reference (res); result.setMask (temp); } else if (pRightExpr_p->isScalar()) { LELArrayRef temp(result.shape()); pLeftExpr_p->evalRef (temp, section); Array res(temp.value() > pRightExpr_p->getScalar().value()); result.value().reference (res); result.setMask (temp); } else { LELArrayRef templ(result.shape()); LELArrayRef tempr(result.shape()); pLeftExpr_p->evalRef (templ, section); pRightExpr_p->evalRef(tempr, section); Array res(templ.value() > tempr.value()); result.value().reference (res); result.setMask (templ, tempr); } break; case LELBinaryEnums::GE : if (pLeftExpr_p->isScalar()) { LELArrayRef temp(result.shape()); pRightExpr_p->evalRef (temp, section); Array res(pLeftExpr_p->getScalar().value() >= temp.value()); result.value().reference (res); result.setMask (temp); } else if (pRightExpr_p->isScalar()) { LELArrayRef temp(result.shape()); pLeftExpr_p->evalRef (temp, section); Array res(temp.value() >= pRightExpr_p->getScalar().value()); result.value().reference (res); result.setMask (temp); } else { LELArrayRef templ(result.shape()); LELArrayRef tempr(result.shape()); pLeftExpr_p->evalRef (templ, section); pRightExpr_p->evalRef(tempr, section); Array res(templ.value() >= tempr.value()); result.value().reference (res); result.setMask (templ, tempr); } break; case LELBinaryEnums::NE : if (pLeftExpr_p->isScalar()) { LELArrayRef temp(result.shape()); pRightExpr_p->evalRef (temp, section); Array res(pLeftExpr_p->getScalar().value() != temp.value()); result.value().reference (res); result.setMask (temp); } else if (pRightExpr_p->isScalar()) { LELArrayRef temp(result.shape()); pLeftExpr_p->evalRef (temp, section); Array res(temp.value() != pRightExpr_p->getScalar().value()); result.value().reference (res); result.setMask (temp); } else { LELArrayRef templ(result.shape()); LELArrayRef tempr(result.shape()); pLeftExpr_p->evalRef (templ, section); pRightExpr_p->evalRef(tempr, section); Array res(templ.value() != tempr.value()); result.value().reference (res); result.setMask (templ, tempr); } break; default: throw(AipsError("LELBinaryCmp::eval - unknown operation")); } } template LELScalar LELBinaryCmp::getScalar() const { #if defined(AIPS_TRACE) cout << "LELBinaryCmp: getScalar " << endl; #endif switch(op_p) { case LELBinaryEnums::EQ : return (pLeftExpr_p->getScalar().value() == pRightExpr_p->getScalar().value()); case LELBinaryEnums::GT : return (pLeftExpr_p->getScalar().value() > pRightExpr_p->getScalar().value()); case LELBinaryEnums::GE : return (pLeftExpr_p->getScalar().value() >= pRightExpr_p->getScalar().value()); case LELBinaryEnums::NE : return (pLeftExpr_p->getScalar().value() != pRightExpr_p->getScalar().value()); default: throw(AipsError("LELBinaryCmp::eval - unknown operation")); } return False; } template Bool LELBinaryCmp::prepareScalarExpr() { #if defined(AIPS_TRACE) cout << "LELBinaryCmp::prepare" << endl; #endif if (LELInterface::replaceScalarExpr (pLeftExpr_p)) { return True; } if (LELInterface::replaceScalarExpr (pRightExpr_p)) { return True; } return False; } template String LELBinaryCmp::className() const { return String("LELBinaryCmp"); } template Bool LELBinaryCmp::lock (FileLocker::LockType type, uInt nattempts) { if (! pLeftExpr_p->lock (type, nattempts)) { return False; } return pRightExpr_p->lock (type, nattempts); } template void LELBinaryCmp::unlock() { pLeftExpr_p->unlock(); pRightExpr_p->unlock(); } template Bool LELBinaryCmp::hasLock (FileLocker::LockType type) const { return pLeftExpr_p->hasLock (type) && pRightExpr_p->hasLock (type); } template void LELBinaryCmp::resync() { pLeftExpr_p->resync(); pRightExpr_p->resync(); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LEL/LELBinary2.cc000066400000000000000000000213331476623553700177240ustar00rootroot00000000000000//# LELBinary.cc: this defines non-templated classes in LELBinary.h //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LELBinaryBool::LELBinaryBool(const LELBinaryEnums::Operation op, const std::shared_ptr>& pLeftExpr, const std::shared_ptr>& pRightExpr) : op_p(op) { if (op == LELBinaryEnums::EQ || op == LELBinaryEnums::NE) { if (pLeftExpr->isScalar() != pRightExpr->isScalar()) { throw (AipsError ("LELBinaryBool::constructor - " "comparison between Bool scalar and " "array not possible; use function ANY or ALL")); } } setAttr (LELAttribute(pLeftExpr->getAttribute(), pRightExpr->getAttribute())); // Fill these variables here, so an exception in setAttr does // not leave them undestructed. pLeftExpr_p = pLeftExpr; pRightExpr_p = pRightExpr; #if defined(AIPS_TRACE) cout << "LELBinaryBool: constructor" << endl; cout << "LELBinaryBool: left.name = " << pLeftExpr->className() << endl; cout << "LELBinaryBool: right.name = " << pRightExpr->className() << endl; #endif } LELBinaryBool::~LELBinaryBool() { #if defined(AIPS_TRACE) cout << "LELBinaryBool: destructor" << endl; #endif } void LELBinaryBool::eval(LELArray& result, const Slicer& section) const { #if defined(AIPS_TRACE) cout << "LELBinaryBool: eval " << endl; #endif switch(op_p) { case LELBinaryEnums::EQ : { LELArrayRef templ(result.shape()); LELArrayRef tempr(result.shape()); pLeftExpr_p->evalRef (templ, section); pRightExpr_p->evalRef(tempr, section); Array res(templ.value() == tempr.value()); result.value().reference (res); result.setMask (templ, tempr); } break; case LELBinaryEnums::NE : { LELArrayRef templ(result.shape()); LELArrayRef tempr(result.shape()); pLeftExpr_p->evalRef (templ, section); pRightExpr_p->evalRef(tempr, section); Array res(templ.value() != tempr.value()); result.value().reference (res); result.setMask (templ, tempr); } break; case LELBinaryEnums::OR : if (pLeftExpr_p->isScalar()) { LELScalar temp = pLeftExpr_p->getScalar(); // Note that having a False mask is in fact an Unknown value. // If True scalar value, result is all True. if (temp.value() && temp.mask()) { result.value() = True; result.removeMask(); } else { pRightExpr_p->eval(result, section); // If False scalar value, result array is same as original. // If Unknown scalar, result is Unknown where not True. if (! temp.mask()) { result.combineOrAnd (True, result.value()); } } } else if (pRightExpr_p->isScalar()) { LELScalar temp = pRightExpr_p->getScalar(); if (temp.value() && temp.mask()) { result.value() = True; result.removeMask(); } else { pLeftExpr_p->eval(result, section); if (! temp.mask()) { result.combineOrAnd (True, result.value()); } } } else { LELArrayRef temp(result.shape()); pLeftExpr_p->eval(result, section); pRightExpr_p->evalRef(temp, section); if (temp.isMasked()) { result.combineOrAnd (True, result.value(), temp.value(), temp.mask()); } else { result.combineOrAnd (True, result.value(), temp.value()); } } break; case LELBinaryEnums::AND : if (pLeftExpr_p->isScalar()) { LELScalar temp = pLeftExpr_p->getScalar(); // Note that having a False mask is in fact an Unknown value. // If False scalar value, result is all False. if (!temp.value() && temp.mask()) { result.value() = False; result.removeMask(); } else { pRightExpr_p->eval(result, section); // If True scalar value, result array is same as original. // If Unknown scalar, result is Unknown where not False. if (! temp.mask()) { result.combineOrAnd (False, result.value()); } } } else if (pRightExpr_p->isScalar()) { LELScalar temp = pRightExpr_p->getScalar(); if (!temp.value() && temp.mask()) { result.value() = False; result.removeMask(); } else { pLeftExpr_p->eval(result, section); if (! temp.mask()) { result.combineOrAnd (False, result.value()); } } } else { LELArrayRef temp(result.shape()); pLeftExpr_p->eval(result, section); pRightExpr_p->evalRef(temp, section); if (temp.isMasked()) { result.combineOrAnd (False, result.value(), temp.value(), temp.mask()); } else { result.combineOrAnd (False, result.value(), temp.value()); } } break; default: throw(AipsError("LELBinaryBool::eval - unknown operation")); } } LELScalar LELBinaryBool::getScalar() const { #if defined(AIPS_TRACE) cout << "LELBinaryBool: getScalar " << endl; #endif switch(op_p) { case LELBinaryEnums::EQ : return (pLeftExpr_p->getScalar().value() == pRightExpr_p->getScalar().value()); case LELBinaryEnums::NE : return (pLeftExpr_p->getScalar().value() != pRightExpr_p->getScalar().value()); case LELBinaryEnums::OR : { LELScalar templ = pLeftExpr_p->getScalar(); if (templ.value() && templ.mask()) { return True; } LELScalar tempr = pRightExpr_p->getScalar(); if (tempr.value() && tempr.mask()) { return True; } return LELScalar ( (templ.value() || tempr.value()), (templ.mask() && tempr.mask())); } case LELBinaryEnums::AND : { LELScalar templ = pLeftExpr_p->getScalar(); if (!templ.value() && templ.mask()) { return False; } LELScalar tempr = pRightExpr_p->getScalar(); if (!tempr.value() && tempr.mask()) { return False; } return LELScalar ( (templ.value() && tempr.value()), (templ.mask() && tempr.mask())); } default: throw(AipsError("LELBinaryBool::eval - unknown operation")); } return False; } Bool LELBinaryBool::prepareScalarExpr() { #if defined(AIPS_TRACE) cout << "LELBinaryBool::prepare" << endl; #endif // In case of OR and AND, the result is invalid if both operands are invalid. // In case of EQ and NE, the result is invalid if one operand is invalid. uInt nrinv = 0; if (LELInterface::replaceScalarExpr (pLeftExpr_p)) { nrinv++; if (op_p != LELBinaryEnums::OR && op_p != LELBinaryEnums::AND) { return True; } } if (LELInterface::replaceScalarExpr (pRightExpr_p)) { nrinv++; if (op_p != LELBinaryEnums::OR && op_p != LELBinaryEnums::AND) { return True; } } return (nrinv==2); } String LELBinaryBool::className() const { return String("LELBinaryBool"); } Bool LELBinaryBool::lock (FileLocker::LockType type, uInt nattempts) { if (! pLeftExpr_p->lock (type, nattempts)) { return False; } return pRightExpr_p->lock (type, nattempts); } void LELBinaryBool::unlock() { pLeftExpr_p->unlock(); pRightExpr_p->unlock(); } Bool LELBinaryBool::hasLock (FileLocker::LockType type) const { return pLeftExpr_p->hasLock (type) && pRightExpr_p->hasLock (type); } void LELBinaryBool::resync() { pLeftExpr_p->resync(); pRightExpr_p->resync(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LEL/LELBinaryEnums.h000066400000000000000000000052101476623553700205100ustar00rootroot00000000000000//# LELBinaryEnums.h: Enums of binary arithmetic operation on arrays //# Copyright (C) 1997 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LELBINARYENUMS_H #define LATTICES_LELBINARYENUMS_H #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Each LEL binary operation is described in this enum // // // // // // // //
      • Lattice //
      • LatticeExpr //
      • LatticeExprNode //
      • LELInterface //
      • LELBinary // // // // This enum provides a value for each binary operation accepted // by the Lattice Expression Language classes. // // // // Each binary operator accepted by the bridging class LatticeExprNode // and passed on to the LELBinary letter classes is labelled internally // with a value from this enum. // // // // // class LELBinaryEnums { public: enum Operation{ // Addition ADD, // Subtraction SUBTRACT, // Multiplication MULTIPLY, // Division DIVIDE, // Logical and AND, // Logical or OR, // == EQ, // > (and reversed <) GT, // >= (and reversed <=) GE, // != NE, // Total number NOPS }; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LEL/LELCondition.h000066400000000000000000000104461476623553700202110ustar00rootroot00000000000000//# LELCondition.h: Class to make a mask from a condition //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LELCONDITION_H #define LATTICES_LELCONDITION_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Class to make a mask from a condition. // // // // // //
      • Lattice //
      • LatticeExpr //
      • LatticeExprNode //
      • LELInterface // // // This derived LEL letter class handles a condition as a mask. // // // This LEL letter class is derived from LELInterface. It // is used to construct LEL objects that know how to deal with // a condition (given using operator[]). // The operands cannot be a scalar. // The LELCondition object is embedded in the tree, and the conversion // actually happens at tree evaluation time. //

        // A description of the implementation details of the LEL classes can // be found in // Note 216 // // // Examples are not very useful as the user would never use // these classes directly. Look in LatticeExprNode.cc to see // how it invokes these classes. An example of how the user // would indirectly use this class (through the envelope) is: // // IPosition shape(2,5,10); // ArrayLattice x(shape); x.set(1.0); // ArrayLattice y(shape); // y.copyData(x[x>5]); // y = x; // // The LELCondition class is embedded in the tree at construction time // so as to turn the condition in a mask on x. // //# //# template class LELCondition : public LELInterface { //# Make members of parent class known. protected: using LELInterface::setAttr; public: // Construct the condition on the given expression. LELCondition (const std::shared_ptr>& expr, const std::shared_ptr>& cond); // Destructor does nothing ~LELCondition(); // Recursively evaluate the expression. virtual void eval (LELArray& result, const Slicer& section) const; // Recursively evaluate the scalar virtual LELScalar getScalar() const; // Do further preparations (e.g. optimization) on the expression. virtual Bool prepareScalarExpr(); // Get class name virtual String className() const; // Handle locking/syncing of a lattice in a lattice expression. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; virtual void resync(); // private: std::shared_ptr> pExpr_p; std::shared_ptr> pCond_p; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/LEL/LELCondition.tcc000066400000000000000000000103351476623553700205300ustar00rootroot00000000000000//# LELCondition.cc: Class to make a mask from a condition //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LELCONDITION_TCC #define LATTICES_LELCONDITION_TCC #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template LELCondition::LELCondition (const std::shared_ptr>& expr, const std::shared_ptr>& cond) { #if defined(AIPS_TRACE) cout << "LELCondition:: constructor" << endl; #endif // The operands have to be lattices. if (expr->isScalar() || cond->isScalar()) { throw (AipsError ("LELCondition: when using the [] operator, its " "operands cannot be scalars")); } // Form the attributes (which also checks if both operands conform). LELAttribute attr (expr->getAttribute(), cond->getAttribute()); // The result is always masked, since the condition forms a mask. setAttr (LELAttribute (True, attr.shape(), attr.tileShape(), attr.coordinates())); // Fill these variables here, so an exception in setAttr does // not leave them undestructed. pExpr_p = expr; pCond_p = cond; } template LELCondition::~LELCondition() { #if defined(AIPS_TRACE) cout << "LELCondition:: destructor" << endl; #endif } template void LELCondition::eval (LELArray& result, const Slicer& section) const { #if defined(AIPS_TRACE) cout << "LELCondition::eval" << endl; #endif LELArrayRef condval(result.shape()); pExpr_p->eval (result, section); pCond_p->evalRef (condval, section); result.combineMask (condval); result.combineMask (condval.value()); } template LELScalar LELCondition::getScalar() const { #if defined(AIPS_TRACE) cout << "LELCondition::getScalar" << endl; #endif // This should never be called, because the operands are no scalars. return pExpr_p->getScalar().value(); } template Bool LELCondition::prepareScalarExpr() { #if defined(AIPS_TRACE) cout << "LELCondition::prepare" << endl; #endif if (LELInterface::replaceScalarExpr (pExpr_p)) { return True; } if (LELInterface::replaceScalarExpr (pCond_p)) { return True; } return False; } template String LELCondition::className() const { return "LELCondition"; } template Bool LELCondition::lock (FileLocker::LockType type, uInt nattempts) { if (! pExpr_p->lock (type, nattempts)) { return False; } return pCond_p->lock (type, nattempts); } template void LELCondition::unlock() { pExpr_p->unlock(); pCond_p->unlock(); } template Bool LELCondition::hasLock (FileLocker::LockType type) const { return pExpr_p->hasLock (type) && pCond_p->hasLock (type); } template void LELCondition::resync() { pExpr_p->resync(); pCond_p->resync(); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LEL/LELConvert.h000066400000000000000000000106171476623553700177030ustar00rootroot00000000000000//# LELConvert.h: Class to convert a LEL node from one numerical type to another //# Copyright (C) 1997,1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LELCONVERT_H #define LATTICES_LELCONVERT_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //

        // Class to convert a LEL node from one numerical type to another // // // // // //
      • Lattice //
      • LatticeExpr //
      • LatticeExprNode //
      • LELInterface // // // This derived LEL letter class handles numerical type conversions // // // This LEL letter class is derived from LELInterface. It // is used to construct LEL objects that know how to convert // between numerical types, such as Double to Float. They // operate on numerical Lattices and return a numerical Lattice. // The LELConvert object is embedded in the tree, and the conversion // actually happens at tree evaluation time. //

        // A description of the implementation details of the LEL classes can // be found in // Note 216 // // // Examples are not very useful as the user would never use // these classes directly. Look in LatticeExprNode.cc to see // how it invokes these classes. An example of how the user // would indirectly use this class (through the envelope) is: // // IPosition shape(2,5,10); // ArrayLattice x(shape); x.set(1.0); // ArrayLattice y(shape); // y.copyData(x); // y = x; // // The LELConvert class is embedded in the tree at construction time // so as to handle the conversion from Float to Double at evaluation time // // // We needed to be able to handle mixed types in the LEL classes // //# //# template class LELConvert : public LELInterface { public: // Constructor. is the type we are coinverting from. // is the type we are converting to. LELConvert (const std::shared_ptr>& expr); // Destructor does nothing ~LELConvert(); // Recursively evaluate the expression. virtual void eval (LELArray& result, const Slicer& section) const; // Recursively evaluate the scalar virtual LELScalar getScalar() const; // Do further preparations (e.g. optimization) on the expression. virtual Bool prepareScalarExpr(); // Get class name virtual String className() const; // Handle locking/syncing of a lattice in a lattice expression. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; virtual void resync(); // private: std::shared_ptr> pExpr_p; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/LEL/LELConvert.tcc000066400000000000000000000066551476623553700202340ustar00rootroot00000000000000//# LELConvert.cc: LELConvert.cc //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LELCONVERT_TCC #define LATTICES_LELCONVERT_TCC #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template LELConvert::LELConvert(const std::shared_ptr>& expr) : pExpr_p (expr) // // F is the type we are converting from // T is the type we are converting to. // { this->setAttr (expr->getAttribute()); #if defined(AIPS_TRACE) cout << "LELConvert:: constructor" << endl; #endif } template LELConvert::~LELConvert() { #if defined(AIPS_TRACE) cout << "LELConvert:: destructor" << endl; #endif } template void LELConvert::eval (LELArray& result, const Slicer& section) const { #if defined(AIPS_TRACE) cout << "LELConvert::eval" << endl; #endif LELArrayRef temp(result.shape()); pExpr_p->evalRef (temp, section); result.setMask (temp); convertArray (result.value(), temp.value()); } template LELScalar LELConvert::getScalar() const { #if defined(AIPS_TRACE) cout << "LELConvert::getScalar" << endl; #endif T tmp; convertScalar (tmp, pExpr_p->getScalar().value()); return tmp; } template Bool LELConvert::prepareScalarExpr() { #if defined(AIPS_TRACE) cout << "LELConvert::prepare" << endl; #endif return LELInterface::replaceScalarExpr (pExpr_p); } template String LELConvert::className() const { return "LELConvert"; } template Bool LELConvert::lock (FileLocker::LockType type, uInt nattempts) { return pExpr_p->lock (type, nattempts); } template void LELConvert::unlock() { pExpr_p->unlock(); } template Bool LELConvert::hasLock (FileLocker::LockType type) const { return pExpr_p->hasLock (type); } template void LELConvert::resync() { pExpr_p->resync(); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LEL/LELCoordinates.cc000066400000000000000000000052621476623553700206730ustar00rootroot00000000000000//# LELCoordinates.cc: Envelope class for Lattice coordinates //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Default constructor LELCoordinates::LELCoordinates() {} // Construct the object from the given letter class. // It takes over the pointer and takes care of destructing // the LELLattCoordBase object. LELCoordinates::LELCoordinates (LELLattCoordBase* coordinates) : coords_p (coordinates) {} // Copy constructor LELCoordinates::LELCoordinates (const LELCoordinates& other) : coords_p (other.coords_p) {} // Destructor does nothing LELCoordinates::~LELCoordinates() {} // Assignment LELCoordinates& LELCoordinates::operator= (const LELCoordinates& other) { if (this != &other) { coords_p = other.coords_p; } return *this; } // Return the underlying letter object. const LELLattCoordBase& LELCoordinates::coordinates() const { AlwaysAssert (!isNull(), AipsError); return *coords_p; } // Does it have coordinates ? Bool LELCoordinates::hasCoordinates() const { if (isNull()) { return False; } return coords_p->hasCoordinates(); } // Check if the coordinates of this and that conform. Int LELCoordinates::compare (const LELCoordinates& that) const { if (isNull() || that.isNull()) { return 9; } return coords_p->compare (*(that.coords_p)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LEL/LELCoordinates.h000066400000000000000000000145531476623553700205400ustar00rootroot00000000000000//# LELCoordinates.h: Envelope class for Lattice coordinates in LEL //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LELCOORDINATES_H #define LATTICES_LELCOORDINATES_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class LELLattCoordBase; //

        // Envelope class to handle Lattice Coordinates in LEL. // // // // // //
      • Lattice //
      • LELLattCoordBase // // // The LatticeExpression classes (LatticeExpr, LatticeExprNode, LEL*) // exist so that the C++ programmer can manipulate mathematical // expressions involving Lattices. A further usage of these classes // is to manipulate ImageInterface objects (which inherit from Lattice) such // as PagedImages. These objects have Coordinates as well as the Lattice // pixels. In order that Coordinate conformance be enforcable, we must // give the LatticeExpression classes access to the Coordinates of the // ImageInterface objects. // // This is done through the interface of the LELCoordinates class. // It is actually an envelope class which holds letter classes which // are the actual implementation of the objects which hold the Lattice // CoordinateSystems. // Lattice objects have a member function called lelCoordinates. // This returns a LELCoordinates object. This object contains a // pointer (actually a std::shared_ptr) of type // LELLattCoordBase. This is the // base class of the letter classes. For Lattices such as ImageInterface, // this pointer actually points at the derived letter class LELImageCoord. // This class in turn contains a pointer (a std::shared_ptr) to the actual // CoordinateSystem object. // // Note that every time the lelCoordinates function is called, // the LELLattCoord // and LELImageCoord // (or whatever the letter class actually being invoked is) // objects are constructed. For example // the internals of ImageInterface::lelCoordinates are //
        return LELCoordinates (new LELImageCoord (coords_p)); //
        so that the LELCoordinates constructor invokes the LELImageCoord // constructor with the CoordinateSystem as its argument. However, // the internal use of std::shared_ptrs makes subsequent constructions inexpensive. // // Having a LELCoordinates object in hand, the programmer then has access // to the CoordinateSystem that it ultimately contains. This is via the // LELCoordinates member function coordinates which returns // a reference to the letter base class LELLattCoordBase. // For example, if the actual letter class object was LELImageCoord, // one has to then cast the reference returned by // LELCoordinates::coordinates() to an LELImageCoord. // This is because the LELImageCoord class functions that actually deal // with the CoordinateSystem are not virtual (otherwise LELLattCoordBase // needs to know about Coordinates). //
        // // // PagedImage im("myimage"); // const LELCoordinates* pLatCoord = &(im.lelCoordinates()); // const LELImageCoord* pImCoord = // dynamic_cast(pLatCoord); // CoordinateSystem coords = pImCoord->coordinates(); // // // // We needed access to CoordinateSystems in the Lattice Expression classes // without making the Lattices module dependent on the Images or Coordinates // module. // //# //#
      • //# class LELCoordinates { public: // Define the possible comparison results. // The default constructor creates a null object. LELCoordinates(); // Construct the object from the given letter class. // It takes over the pointer and takes care of destructing // the LELLattCoordBase object. LELCoordinates (LELLattCoordBase* coordinates); // Copy constructor (reference semantics). LELCoordinates (const LELCoordinates& that); ~LELCoordinates(); // Assignment (reference semantics). LELCoordinates& operator= (const LELCoordinates& that); // Is the coordinates a null object? Bool isNull() const { return !coords_p; } // Does the class have true coordinates? // It returns False if this is a null object. Bool hasCoordinates() const; // Check how the coordinates of this and that compare. // The return value tells how they compare. //
        -1: this is subset //
        0: equal //
        1: this is superset //
        9: invalid (mismatch) Int compare (const LELCoordinates& other) const; // Return the underlying letter object. // This should in general not be used, but for specific (Image) cases // it might be needed. const LELLattCoordBase& coordinates() const; private: // The pointer to the underlying object. std::shared_ptr coords_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LEL/LELFunction.h000066400000000000000000000655601476623553700200570ustar00rootroot00000000000000//# LELFunction.h: LELFunction.h //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LELFUNCTION_H #define LATTICES_LELFUNCTION_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // This LEL class handles numerical (real and complex) 1-argument functions // // // // // // // // //
      • Lattice //
      • LatticeExpr //
      • LatticeExprNode //
      • LELInterface //
      • LELFunctionEnums // // // // This derived LEL letter class handles numerical (real and complex) // 1-argument functions // // // // This LEL letter class is derived from LELInterface. It is used to construct // LEL objects that apply numerical 1-argument functions to Lattice // expressions. They operate on numerical (Float,Double,Complex,DComplex) // Lattice expressions and return the same type. The available C++ functions are // sin,sinh,cos,cosh,exp,log,log10,sqrt,min,max,mean,sum with // equivalents in the enum of SIN,SINH,COS,COSH,EXP,LOG,LOG10,SQRT,MIN1D,MAX1D, // MEAN1D, and SUM. // // A description of the implementation details of the LEL classes can // be found in // Note 216 // // // // Examples are not very useful as the user would never use // these classes directly. Look in LatticeExprNode.cc to see // how it invokes these classes. Examples of how the user // would indirectly use this class (through the envelope) are: // // IPosition shape(2,5,10); // ArrayLattice x(shape); x.set(1.0); // ArrayLattice y(shape); // y.copyData(sin(x)); // y = sin(x) // y.copyData(min(x)); // y = min(x) // // Note that the min function returns a scalar, and the output // Lattice is filled with that one value. // // // // Numerical functions are a basic mathematical expression. // // // // template class LELFunction1D : public LELInterface { //# Make members of parent class known. protected: using LELInterface::setAttr; public: // Constructor takes operation and expression to be operated upon LELFunction1D(const LELFunctionEnums::Function function, const std::shared_ptr>& expr); // Destructor ~LELFunction1D(); // Recursively evaluate the expression virtual void eval (LELArray& result, const Slicer& section) const; // Recursively evaluate the scalar expression. virtual LELScalar getScalar() const; // Do further preparations (e.g. optimization) on the expression. virtual Bool prepareScalarExpr(); // Get class name virtual String className() const; // Handle locking/syncing of a lattice in a lattice expression. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; virtual void resync(); // private: LELFunctionEnums::Function function_p; std::shared_ptr> pExpr_p; }; // // This LEL class handles numerical (real only) 1-argument functions // // // // // // // // //
      • Lattice //
      • LatticeExpr //
      • LatticeExprNode //
      • LELInterface //
      • LELFunctionEnums // // // // This derived LEL letter class handles numerical (real only) // 1-argument functions // // // // This LEL letter class is derived from LELInterface. It is used to construct // LEL objects that apply numerical (real only) 1-argument functions to // Lattice expressions. They operate on Float and Double numerical Lattice // expressions and return the same type. The available C++ functions are // asin,acos,tan,tanh,ceil,floor with // equivalents in the enum of ASIN, ACOS, TAN, TANH, CEIL, and FLOOR. // // A description of the implementation details of the LEL classes can // be found in // Note 216 // // // // Examples are not very useful as the user would never use // these classes directly. Look in LatticeExprNode.cc to see // how it invokes these classes. Examples of how the user // would indirectly use this class (through the envelope) are: // // IPosition shape(2,5,10); // ArrayLattice x(shape); x.set(0.05); // ArrayLattice y(shape); // y.copyData(asin(x)); // y = asin(x) // y.copyData(tan(x)); // y = tan(x) // // Note that the min function returns a scalar, and the output // Lattice is filled with that one value. // // // // Numerical functions are a basic mathematical expression. // // // // template class LELFunctionReal1D : public LELInterface { //# Make members of parent class known. protected: using LELInterface::setAttr; public: // Constructor takes operation and expression to be operated upon LELFunctionReal1D(const LELFunctionEnums::Function function, const std::shared_ptr>& expr); // Destructor ~LELFunctionReal1D(); // Recursively evaluate the expression virtual void eval (LELArray& result, const Slicer& section) const; // Recursively evaluate the scalar expression virtual LELScalar getScalar() const; // Do further preparations (e.g. optimization) on the expression. virtual Bool prepareScalarExpr(); // Get class name virtual String className() const; // Handle locking/syncing of a lattice in a lattice expression. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; virtual void resync(); // private: LELFunctionEnums::Function function_p; std::shared_ptr> pExpr_p; }; // // This LEL class handles functions with a variable number of arguments. // // // // // // // // //
      • Lattice //
      • LatticeExpr //
      • LatticeExprNode //
      • LELInterface //
      • LELFunctionEnums // // // // This derived LEL letter class handles numerical functions (arbitrary // number of arguments) which return any data type // // // // This templated LEL letter class is derived from LELInterface. // It is used to construct LEL objects that apply functions of // arbitrary number of arguments to Lattice expressions. // They operate lattices with any type and return the same type. // The available C++ function is // iif with equivalents in the enum of IIF. // // A description of the implementation details of the LEL classes can // be found in // Note 216 // // // // Examples are not very useful as the user would never use // these classes directly. Look in LatticeExprNode.cc to see // how it invokes these classes. Examples of how the user // would indirectly use this class (through the envelope) are: // // IPosition shape(2,5,10); // ArrayLattice w(shape); w.set(Complex(2.0,3.0)); // ArrayLattice x(shape); x.set(0.05); // ArrayLattice y(shape); y.set(2.0); // ArrayLattice z(shape); y.set(2.0); // // z.copyData(iif(x==0, y, x)); // // // Copy x to z, but where x==0, take the correpsonding element from y. // b // // // An "if-then-else" like construction is very useful. // // // // template class LELFunctionND : public LELInterface { //# Make members of parent class known. protected: using LELInterface::setAttr; public: // Constructor takes operation and expressions to be operated upon LELFunctionND(const LELFunctionEnums::Function function, const Block& expr); // Destructor ~LELFunctionND(); // Recursively evaluate the expression virtual void eval (LELArray& result, const Slicer& section) const; // Recursively evaluate the scalar expression virtual LELScalar getScalar() const; // Do further preparations (e.g. optimization) on the expression. virtual Bool prepareScalarExpr(); // Get class name virtual String className() const; // Handle locking/syncing of a lattice in a lattice expression. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; virtual void resync(); // private: LELFunctionEnums::Function function_p; Block arg_p; }; // // This LEL class handles numerical functions whose return type is a Float // // // // // // // // //
      • Lattice //
      • LatticeExpr //
      • LatticeExprNode //
      • LELInterface //
      • LELFunctionEnums // // // // This derived LEL letter class handles numerical functions (arbitrary // number of arguments) which return a Float // // // // This LEL letter class is derived from LELInterface. It is used to construct // LEL objects that apply numerical functions of arbitrary number of // arguments (but only 1 or 2 arguments currently implemented) to Lattice // expressions. They operate on Float or Complex Lattices // and return a Float. The available C++ functions are // min,max,pow,atan2,fmod,abs,arg,real,imag with // equivalents in the enum of MIN,MAX,POW,ATAN2,FMOD,ABS,ARG,REAL, and IMAG. // // A description of the implementation details of the LEL classes can // be found in // Note 216 // // // // Examples are not very useful as the user would never use // these classes directly. Look in LatticeExprNode.cc to see // how it invokes these classes. Examples of how the user // would indirectly use this class (through the envelope) are: // // IPosition shape(2,5,10); // ArrayLattice w(shape); w.set(Complex(2.0,3.0)); // ArrayLattice x(shape); x.set(0.05); // ArrayLattice y(shape); y.set(2.0); // ArrayLattice z(shape); y.set(2.0); // // z.copyData(min(x,y)); // z = min(x,y) // z.copyData(imag(w)); // z = imag(w) // // // Note that this min function takes two arguments and returns // the minimum of the two, pixel by pixel (i.e. it does not // return one scalar from the whole Lattice) // b // // // Numerical functions are a basic mathematical expression. // // // // class LELFunctionFloat : public LELInterface { public: // Constructor takes operation and left and right expressions // to be operated upon LELFunctionFloat(const LELFunctionEnums::Function function, const Block& expr); // Destructor ~LELFunctionFloat(); // Recursively evaluate the expression virtual void eval (LELArray& result, const Slicer& section) const; // Recursively evaluate the scalar expression virtual LELScalar getScalar() const; // Do further preparations (e.g. optimization) on the expression. virtual Bool prepareScalarExpr(); // Get class name virtual String className() const; // Handle locking/syncing of a lattice in a lattice expression. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; virtual void resync(); // private: LELFunctionEnums::Function function_p; Block arg_p; }; // // This LEL class handles numerical functions whose return type is a Double // // // // // // // // //
      • Lattice //
      • LatticeExpr //
      • LatticeExprNode //
      • LELInterface //
      • LELFunctionEnums // // // // This derived LEL letter class handles numerical functions (arbitrary // number of arguments) which return a Double // // // // This LEL letter class is derived from LELInterface. It is used to construct // LEL objects that apply numerical functions of arbitrary number of // arguments (but only 1 or 2 arguments currently implemented) to Lattice // expressions. They operate on Double or DComplex Lattices // and return a Double. The available C++ functions are // min,max,pow,atan2,fmod,abs,arg,real,imag with // equivalents in the enum of MIN,MAX,POW,ATAN2,FMOD,ABS,ARG,REAL, and IMAG. // // There are also two other functions for which the input Lattice expression // type must be a Bool. These are ntrue,nfalse with // equivalents in the enum of NTRUE and NFALSE. // // There is a further function for which the input Lattice expression // type can be anything. This is nelements with // equivalent in the enum of NELEM. // // A description of the implementation details of the LEL classes can // be found in // Note 216 // // // // Examples are not very useful as the user would never use // these classes directly. Look in LatticeExprNode.cc to see // how it invokes these classes. Examples of how the user // would indirectly use this class (through the envelope) are: // // IPosition shape(2,5,10); // ArrayLattice v(shape); v.set(True); // ArrayLattice w(shape); w.set(DComplex(2.0,3.0)); // ArrayLattice x(shape); x.set(0.05); // ArrayLattice y(shape); y.set(2.0); // ArrayLattice z(shape); y.set(2.0); // // z.copyData(min(x,y)); // z = min(x,y) // z.copyData(imag(w)); // z = imag(w) // z.copyData(nelements(v)); // z = nelements(v) // z.copyData(ntrue(v)); // z = ntrue(v) // // // // // Numerical functions are a basic mathematical expression. // // // // class LELFunctionDouble : public LELInterface { public: // Constructor takes operation and left and right expressions // to be operated upon LELFunctionDouble(const LELFunctionEnums::Function function, const Block& expr); // Destructor ~LELFunctionDouble(); // Recursively evaluate the expression virtual void eval (LELArray& result, const Slicer& section) const; // Recursively evaluate the scalar expression virtual LELScalar getScalar() const; // Do further preparations (e.g. optimization) on the expression. virtual Bool prepareScalarExpr(); // Get class name virtual String className() const; // Handle locking/syncing of a lattice in a lattice expression. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; virtual void resync(); // private: // Count number of masked elements in a LatticeExprNode. // uInt nMaskedElements (const LatticeExprNode&) const; uInt nMaskedOn (const Array& mask) const; // LELFunctionEnums::Function function_p; Block arg_p; }; // // This LEL class handles complex numerical functions // // // // // // // // //
      • Lattice //
      • LatticeExpr //
      • LatticeExprNode //
      • LELInterface //
      • LELFunctionEnums // // // // This derived LEL letter class handles complex numerical functions (arbitrary // number of arguments) // // // // This LEL letter class is derived from LELInterface. It is used to construct // LEL objects that apply complex numerical functions of arbitrary number of // arguments (but only 1 or 2 arguments currently implemented) to Lattice // expressions. They operate on Complex Lattice expressions only // and return a Complex. The available C++ functions are // pow,conj with equivalents in the enum of POW and CONJ. // // A description of the implementation details of the LEL classes can // be found in // Note 216 // // // // Examples are not very useful as the user would never use // these classes directly. Look in LatticeExprNode.cc to see // how it invokes these classes. Examples of how the user // would indirectly use this class (through the envelope) are: // // IPosition shape(2,5,10); // ArrayLattice x(shape); x.set(Complex(2.0,3.0)); // ArrayLattice y(shape); // y.copyData(conj(x)); // y = conj(x) // // // // // Numerical functions are a basic mathematical expression. // // // // class LELFunctionComplex : public LELInterface { public: // Constructor takes operation and left and right expressions // to be operated upon LELFunctionComplex(const LELFunctionEnums::Function function, const Block& expr); // Destructor ~LELFunctionComplex(); // Recursively evaluate the expression virtual void eval (LELArray& result, const Slicer& section) const; // Recursively evaluate the scalar expression virtual LELScalar getScalar() const; // Do further preparations (e.g. optimization) on the expression. virtual Bool prepareScalarExpr(); // Get class name virtual String className() const; // Handle locking/syncing of a lattice in a lattice expression. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; virtual void resync(); // private: LELFunctionEnums::Function function_p; Block arg_p; }; // // This LEL class handles double complex numerical functions // // // // // // // // //
      • Lattice //
      • LatticeExpr //
      • LatticeExprNode //
      • LELInterface //
      • LELFunctionEnums // // // // This derived LEL letter class handles double complex numerical functions (arbitrary // number of arguments) // // // // This LEL letter class is derived from LELInterface. It is used to construct // LEL objects that apply double complex numerical functions of arbitrary number of // arguments (but only 1 or 2 arguments currently implemented) to Lattice // expressions. They operate on DComplex Lattice expressions only // and return a DComplex. The available C++ functions are // pow,conj with equivalents in the enum of POW and CONJ. // // A description of the implementation details of the LEL classes can // be found in // Note 216 // // // // Examples are not very useful as the user would never use // these classes directly. Look in LatticeExprNode.cc to see // how it invokes these classes. Examples of how the user // would indirectly use this class (through the envelope) are: // // IPosition shape(2,5,10); // ArrayLattice x(shape); x.set(DComplex(2.0,3.0)); // ArrayLattice y(shape); // y.copyData(conj(x)); // y = conj(x) // // // // // Numerical functions are a basic mathematical expression. // // // // class LELFunctionDComplex : public LELInterface { public: // Constructor takes operation and left and right expressions // to be operated upon LELFunctionDComplex(const LELFunctionEnums::Function function, const Block& expr); // Destructor ~LELFunctionDComplex(); // Recursively evaluate the expression virtual void eval (LELArray& result, const Slicer& section) const; // Recursively evaluate the scalar expression virtual LELScalar getScalar() const; // Do further preparations (e.g. optimization) on the expression. virtual Bool prepareScalarExpr(); // Get class name virtual String className() const; // Handle locking/syncing of a lattice in a lattice expression. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; virtual void resync(); // private: LELFunctionEnums::Function function_p; Block arg_p; }; // // This LEL class handles logical functions // // // // // // // // //
      • Lattice //
      • LatticeExpr //
      • LatticeExprNode //
      • LELInterface //
      • LELFunctionEnums // // // // This derived LEL letter class handles logical functions (arbitrary // number of arguments) // // // // This LEL letter class is derived from LELInterface. It is used to construct // LEL objects that apply logical functions of arbitrary number of // arguments (but only 1 or 2 arguments currently implemented) to Lattice // expressions. They operate on Bool Lattice expressions only // and return a Bool. The available C++ functions are // all,any with equivalents in the enum of ALL and ANY. // // A description of the implementation details of the LEL classes can // be found in // Note 216 // // // // Examples are not very useful as the user would never use // these classes directly. Look in LatticeExprNode.cc to see // how it invokes these classes. Examples of how the user // would indirectly use this class (through the envelope) are: // // IPosition shape(2,5,10); // ArrayLattice x(shape); x.set(True); // ArrayLattice y(shape); // y.copyData(any(x)); // y = any(x) // // The result of the any function (were any of the values True) is // a Bool scalar. So the output Lattice is filled with that one value. // // // // Logical functions are a basic mathematical expression. // // // // class LELFunctionBool : public LELInterface { public: // Constructor takes operation and left and right expressions // to be operated upon LELFunctionBool(const LELFunctionEnums::Function function, const Block& expr); // Destructor ~LELFunctionBool(); // Recursively evaluate the expression virtual void eval (LELArray& result, const Slicer& section) const; // Recursively evaluate the scalar expression virtual LELScalar getScalar() const; // Do further preparations (e.g. optimization) on the expression. virtual Bool prepareScalarExpr(); // Get class name virtual String className() const; // Handle locking/syncing of a lattice in a lattice expression. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; virtual void resync(); // private: LELFunctionEnums::Function function_p; Block arg_p; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/LEL/LELFunction.tcc000066400000000000000000000661041476623553700203740ustar00rootroot00000000000000//# LELFunction.cc: this defines templated classes in LELFunction.h //# Copyright (C) 1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LELFUNCTION_TCC #define LATTICES_LELFUNCTION_TCC #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // LELFunction1D template LELFunction1D::LELFunction1D(const LELFunctionEnums::Function function, const std::shared_ptr>& expr) : function_p(function) { switch (function_p) { case LELFunctionEnums::MIN1D : case LELFunctionEnums::MAX1D : case LELFunctionEnums::MEAN1D : case LELFunctionEnums::SUM : setAttr(LELAttribute()); // these result in a scalar break; case LELFunctionEnums::VALUE : { // The value gets unmasked. const LELAttribute& argAttr = expr->getAttribute(); if (argAttr.isScalar()) { setAttr (LELAttribute()); } else { setAttr (LELAttribute (False, argAttr.shape(), argAttr.tileShape(), argAttr.coordinates())); } break; } default: setAttr(expr->getAttribute()); } // Fill this variable here, so an exception in setAttr does // not leave it undestructed. pExpr_p = expr; #if defined(AIPS_TRACE) cout << "LELFunction1D: constructor" << endl; #endif } template LELFunction1D::~LELFunction1D() { #if defined(AIPS_TRACE) cout << "LELFunction1D: destructor" << endl; #endif } template void LELFunction1D::eval(LELArray& result, const Slicer& section) const { #if defined(AIPS_TRACE) cout << "LELFunction1D:: eval" << endl; #endif // Evaluate the expression pExpr_p->eval(result, section); // Apply the 1D function switch(function_p) { case LELFunctionEnums::SIN : { Array tmp(sin(result.value())); result.value().reference(tmp); break; } case LELFunctionEnums::SINH : { Array tmp(sinh(result.value())); result.value().reference(tmp); break; } case LELFunctionEnums::COS : { Array tmp(cos(result.value())); result.value().reference(tmp); break; } case LELFunctionEnums::COSH : { Array tmp(cosh(result.value())); result.value().reference(tmp); break; } case LELFunctionEnums::EXP : { Array tmp(exp(result.value())); result.value().reference(tmp); break; } case LELFunctionEnums::LOG : { Array tmp(log(result.value())); result.value().reference(tmp); break; } case LELFunctionEnums::LOG10 : { Array tmp(log10(result.value())); result.value().reference(tmp); break; } case LELFunctionEnums::SQRT : { Array tmp(sqrt(result.value())); result.value().reference(tmp); break; } case LELFunctionEnums::VALUE : { result.removeMask(); break; } default: throw(AipsError("LELFunction1D::eval - unknown function")); } } template LELScalar LELFunction1D::getScalar() const { #if defined(AIPS_TRACE) cout << "LELFunction1D:: getScalar" << endl; #endif // Apply the 1D function switch(function_p) { case LELFunctionEnums::SIN : return sin(pExpr_p->getScalar().value()); case LELFunctionEnums::SINH : return sinh(pExpr_p->getScalar().value()); case LELFunctionEnums::COS : return cos(pExpr_p->getScalar().value()); case LELFunctionEnums::COSH : return cosh(pExpr_p->getScalar().value()); case LELFunctionEnums::EXP : return exp(pExpr_p->getScalar().value()); case LELFunctionEnums::LOG : return log(pExpr_p->getScalar().value()); case LELFunctionEnums::LOG10 : return log10(pExpr_p->getScalar().value()); case LELFunctionEnums::SQRT : return sqrt(pExpr_p->getScalar().value()); case LELFunctionEnums::VALUE : return pExpr_p->getScalar(); case LELFunctionEnums::MIN1D : { if (pExpr_p->isScalar()) { return pExpr_p->getScalar(); } Bool firstTime = True; T minVal = T(); LatticeExpr latExpr(pExpr_p); if (! latExpr.isMasked()) { RO_LatticeIterator iter(latExpr); while (! iter.atEnd()) { T minv = min(iter.cursor()); if (firstTime || minv < minVal) { firstTime = False; minVal = minv; } iter++; } } else { RO_MaskedLatticeIterator iter(latExpr); Bool delMask, delData; Array mask; while (! iter.atEnd()) { const Array& array = iter.cursor(); iter.getMask (mask); const Bool* maskPtr = mask.getStorage (delMask); const T* dataPtr = array.getStorage (delData); size_t n = array.nelements(); T lminVal = minVal; for (size_t i=0; i(); // no element found } return minVal; } case LELFunctionEnums::MAX1D : { if (pExpr_p->isScalar()) { return pExpr_p->getScalar(); } Bool firstTime = True; T maxVal = T(); LatticeExpr latExpr(pExpr_p); if (! latExpr.isMasked()) { RO_LatticeIterator iter(latExpr); while (! iter.atEnd()) { T maxv = max(iter.cursor()); if (firstTime || maxv > maxVal) { firstTime = False; maxVal = maxv; } iter++; } } else { RO_MaskedLatticeIterator iter(latExpr); Bool delMask, delData; Array mask; while (! iter.atEnd()) { const Array& array = iter.cursor(); iter.getMask (mask); const Bool* maskPtr = mask.getStorage (delMask); const T* dataPtr = array.getStorage (delData); size_t n = array.nelements(); T lmaxVal = maxVal; for (size_t i=0; i lmaxVal) { firstTime = False; lmaxVal = dataPtr[i]; } } } maxVal = lmaxVal; mask.freeStorage (maskPtr, delMask); array.freeStorage (dataPtr, delData); iter++; } } if (firstTime) { return LELScalar(); // no element found } return maxVal; } case LELFunctionEnums::MEAN1D : { if (pExpr_p->isScalar()) { return pExpr_p->getScalar(); } typename NumericTraits::PrecisionType sumVal = 0; size_t nrVal = 0; LatticeExpr latExpr(pExpr_p); Bool delData, delMask; // Do the sum ourselves to avoid round off if (! latExpr.isMasked()) { RO_LatticeIterator iter(latExpr); while (! iter.atEnd()) { const Array& array = iter.cursor(); const T* dataPtr = array.getStorage(delData); size_t n = array.nelements(); typename NumericTraits::PrecisionType lsumVal = 0; for (size_t i=0; i iter(latExpr); Array mask; while (! iter.atEnd()) { const Array& array = iter.cursor(); iter.getMask (mask); const Bool* maskPtr = mask.getStorage (delMask); const T* dataPtr = array.getStorage (delData); size_t n = array.nelements(); typename NumericTraits::PrecisionType lsumVal = 0; size_t lnrVal = 0; for (size_t i=0; i(); // no element found } return T(sumVal / Double(nrVal)); } case LELFunctionEnums::SUM : { if (pExpr_p->isScalar()) { return pExpr_p->getScalar(); } typename NumericTraits::PrecisionType sumVal = 0; LatticeExpr latExpr(pExpr_p); Bool delData, delMask; // Do the sum ourselves to avoid round off if (! latExpr.isMasked()) { RO_LatticeIterator iter(latExpr); while (! iter.atEnd()) { const Array& array = iter.cursor(); const T* dataPtr = array.getStorage(delData); size_t n = array.nelements(); typename NumericTraits::PrecisionType lsumVal = 0; for (size_t i=0; i iter(latExpr); Array mask; while (! iter.atEnd()) { const Array& array = iter.cursor(); iter.getMask (mask); const Bool* maskPtr = mask.getStorage (delMask); const T* dataPtr = array.getStorage (delData); size_t n = array.nelements(); typename NumericTraits::PrecisionType lsumVal = 0; for (size_t i=0; igetScalar(); // to make compiler happy } template Bool LELFunction1D::prepareScalarExpr() { #if defined(AIPS_TRACE) cout << "LELFunction1D::prepare" << endl; #endif return LELInterface::replaceScalarExpr (pExpr_p); } template String LELFunction1D::className() const { return String("LELFunction1D"); } template Bool LELFunction1D::lock (FileLocker::LockType type, uInt nattempts) { return pExpr_p->lock (type, nattempts); } template void LELFunction1D::unlock() { pExpr_p->unlock(); } template Bool LELFunction1D::hasLock (FileLocker::LockType type) const { return pExpr_p->hasLock (type); } template void LELFunction1D::resync() { pExpr_p->resync(); } // LELFunctionReal1D template LELFunctionReal1D::LELFunctionReal1D (const LELFunctionEnums::Function function, const std::shared_ptr>& expr) : function_p(function) { switch (function_p) { case LELFunctionEnums::MEDIAN1D : setAttr(LELAttribute()); // these result in a scalar break; default: setAttr(expr->getAttribute()); } // Fill this variable here, so an exception in setAttr does // not leave it undestructed. pExpr_p = expr; #if defined(AIPS_TRACE) cout << "LELFunctionReal1D: constructor" << endl; #endif } template LELFunctionReal1D::~LELFunctionReal1D() { #if defined(AIPS_TRACE) cout << "LELFunctionReal1D: destructor" << endl; #endif } template void LELFunctionReal1D::eval(LELArray& result, const Slicer& section) const { #if defined(AIPS_TRACE) cout << "LELFunctionReal1D:: eval" << endl; #endif // Evaluate the expression pExpr_p->eval(result, section); // Apply the Real1D function switch(function_p) { case LELFunctionEnums::ASIN : { Array tmp(asin(result.value())); result.value().reference(tmp); break; } case LELFunctionEnums::ACOS : { Array tmp(acos(result.value())); result.value().reference(tmp); break; } case LELFunctionEnums::TAN : { Array tmp(tan(result.value())); result.value().reference(tmp); break; } case LELFunctionEnums::TANH : { Array tmp(tanh(result.value())); result.value().reference(tmp); break; } case LELFunctionEnums::ATAN : { Array tmp(atan(result.value())); result.value().reference(tmp); break; } case LELFunctionEnums::ROUND : { Bool deleteIt; T* data = result.value().getStorage (deleteIt); size_t nr = result.value().nelements(); for (size_t i=0; i tmp(ceil(result.value())); result.value().reference(tmp); break; } case LELFunctionEnums::FLOOR : { Array tmp(floor(result.value())); result.value().reference(tmp); break; } default: throw(AipsError("LELFunctionReal1D::eval - unknown function")); } } template LELScalar LELFunctionReal1D::getScalar() const { #if defined(AIPS_TRACE) cout << "LELFunctionReal1D:: getScalar" << endl; #endif // Apply the Real1D function switch(function_p) { case LELFunctionEnums::ASIN : return asin(pExpr_p->getScalar().value()); case LELFunctionEnums::ACOS : return acos(pExpr_p->getScalar().value()); case LELFunctionEnums::TAN : return tan(pExpr_p->getScalar().value()); case LELFunctionEnums::TANH : return tanh(pExpr_p->getScalar().value()); case LELFunctionEnums::ATAN : return atan(pExpr_p->getScalar().value()); case LELFunctionEnums::ROUND : { T value = pExpr_p->getScalar().value(); if (value < 0) { return ceil(value - 0.5); } return floor (value + 0.5); } case LELFunctionEnums::CEIL : return ceil(pExpr_p->getScalar().value()); case LELFunctionEnums::FLOOR : return floor(pExpr_p->getScalar().value()); case LELFunctionEnums::MEDIAN1D : { if (pExpr_p->isScalar()) { return pExpr_p->getScalar(); } Vector median = LatticeFractile::maskedFractile (LatticeExpr(pExpr_p), 0.5); if (median.nelements() == 0) { return LELScalar(); // no valid median found } return median(0); } default: throw(AipsError("LELFunctionReal1D::getScalar - unknown function")); } return pExpr_p->getScalar(); // to make compiler happy } template Bool LELFunctionReal1D::prepareScalarExpr() { #if defined(AIPS_TRACE) cout << "LELFunctionReal1D::prepare" << endl; #endif if (pExpr_p) { return LELInterface::replaceScalarExpr (pExpr_p); } return False; } template String LELFunctionReal1D::className() const { return String("LELFunctionReal1D"); } template Bool LELFunctionReal1D::lock (FileLocker::LockType type, uInt nattempts) { return pExpr_p->lock (type, nattempts); } template void LELFunctionReal1D::unlock() { pExpr_p->unlock(); } template Bool LELFunctionReal1D::hasLock (FileLocker::LockType type) const { return pExpr_p->hasLock (type); } template void LELFunctionReal1D::resync() { pExpr_p->resync(); } // LELFunctionND template LELFunctionND::LELFunctionND(const LELFunctionEnums::Function function, const Block& exp) : function_p(function) { switch (function_p) { case LELFunctionEnums::IIF : { if (exp.nelements() != 3) { throw (AipsError ("LELFunctionND - " "function IIF should have 3 arguments")); } //# The 1st argument must be Bool, the 2nd and 3rd must be T. //# The arguments do not need to be lattices. Block argType(3); argType[0] = TpBool; argType[1] = whatType(); argType[2] = whatType(); setAttr (LatticeExprNode::checkArg (exp, argType, False)); break; } case LELFunctionEnums::REPLACE : { if (exp.nelements() != 2) { throw (AipsError ("LELFunctionND - " "function REPLACE should have 2 arguments")); } //# The 1st and 2nd argument must be T. //# The first arguments has to be a lattice. if (exp[0].isScalar()) { throw (AipsError ("LELFunctionND - " "first argument of function REPLACE cannot be " " a scalar")); } Block argType(2); argType[0] = whatType(); argType[1] = whatType(); LatticeExprNode::checkArg (exp, argType, False); setAttr (exp[0].getAttribute()); break; } default: throw (AipsError ("LELFunctionND::constructor - unknown function")); } // Fill the node block here, so an exception does // not leave the nodes undestructed. arg_p = exp; #if defined(AIPS_TRACE) cout << "LELFunctionND: constructor" << endl; #endif } template LELFunctionND::~LELFunctionND() { #if defined(AIPS_TRACE) cout << "LELFunctionND: destructor" << endl; #endif } template void LELFunctionND::eval(LELArray& result, const Slicer& section) const { #if defined(AIPS_TRACE) cout << "LELFunctionND:: eval" << endl; #endif switch (function_p) { case LELFunctionEnums::IIF : { //# Evaluation is not difficult, but there are many scalar/lattice //# combinations. //# The optional masks make life even much more difficult. // If the condition is a scalar, the result is simply the 1st or 2nd operand. // If the operand taken is a scalar, its mask is certainly true. // (otherwise prepareScalarExpr would have tackled it). if (arg_p[0].isScalar()) { T tmp; Bool tmpb; arg_p[0].eval (tmpb); if (tmpb) { if (arg_p[1].isScalar()) { arg_p[1].eval (tmp); result.value() = tmp; } else { arg_p[1].eval (result, section); } } else { if (arg_p[2].isScalar()) { arg_p[2].eval (tmp); result.value() = tmp; } else { arg_p[2].eval (result, section); } } } else { // So the condition is an array. // The result might get a mask. That is the case if one of the operands // is an invalid scalar or an array with mask, LELArrayRef tmpb(result.shape()); arg_p[0].evalRef (tmpb, section); Bool deleteTmpb; const Bool* tmpbData = tmpb.value().getStorage (deleteTmpb); size_t n = tmpb.value().nelements(); Bool deleteRes, deleteMask; T* resData = 0; Bool* maskData = 0; T tmp1, tmp2; // The combination of left and right gets a mask if either // of them has a mask. Array newMask; Bool makeMask = (arg_p[1].isInvalidScalar() || arg_p[1].isMasked() || arg_p[2].isInvalidScalar() || arg_p[2].isMasked()); // There are 4 different scalar/array combinations for 1st and 2nd operand. // Each of them must handle the optional new mask. // Because efficiency is needed, the test for a new mask is done // outside the loop. if (arg_p[1].isScalar()) { arg_p[1].eval (tmp1); Bool mask1 = (!arg_p[1].isInvalidScalar()); if (arg_p[2].isScalar()) { // Handle scalar,scalar case. resData = result.value().getStorage (deleteRes); arg_p[2].eval (tmp2); Bool mask2 = (!arg_p[2].isInvalidScalar()); if (makeMask) { newMask.resize (result.shape()); maskData = newMask.getStorage (deleteMask); for (size_t i=0; i tmp(result.shape()); arg_p[2].evalRef (tmp, section); Bool deleteTmp, deleteTmpMask; const T* tmpData = tmp.value().getStorage (deleteTmp); if (makeMask) { maskData = newMask.getStorage (deleteMask); if (tmp.isMasked()) { const Bool* tmpMaskData = tmp.mask().getStorage (deleteTmpMask); for (size_t i=0; i tmp(result.shape()); arg_p[1].evalRef (tmp, section); Bool deleteTmp; const T* tmpData = tmp.value().getStorage (deleteTmp); for (size_t i=0; i LELScalar LELFunctionND::getScalar() const { #if defined(AIPS_TRACE) cout << "LELFunctionND:: getScalar" << endl; #endif // Apply the ND function T tmp; switch(function_p) { case LELFunctionEnums::IIF : { Bool tmpb; arg_p[0].eval (tmpb); if (tmpb) { arg_p[1].eval (tmp); } else { arg_p[2].eval (tmp); } return tmp; } default: throw(AipsError("LELFunctionND::getScalar - unknown function")); } return tmp; // to make compiler happy } template Bool LELFunctionND::prepareScalarExpr() { #if defined(AIPS_TRACE) cout << "LELFunctionND::prepare" << endl; #endif size_t i; for (i=0; i String LELFunctionND::className() const { return String("LELFunctionND"); } template Bool LELFunctionND::lock (FileLocker::LockType type, uInt nattempts) { for (size_t i=0; i void LELFunctionND::unlock() { for (size_t i=0; i Bool LELFunctionND::hasLock (FileLocker::LockType type) const { for (size_t i=0; i void LELFunctionND::resync() { for (size_t i=0; i #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // LELFunctionFloat LELFunctionFloat::LELFunctionFloat(const LELFunctionEnums::Function function, const Block& exp) : function_p(function) { switch (function_p) { case LELFunctionEnums::ABS : case LELFunctionEnums::ARG : case LELFunctionEnums::REAL : case LELFunctionEnums::IMAG : case LELFunctionEnums::NDIM : { if (exp.nelements() != 1) { throw (AipsError ("LELFunctionFloat::constructor - " "function can only have one argument")); } if (function_p == LELFunctionEnums::NDIM) { setAttr (LELAttribute()); // result is scalar } else { setAttr(exp[0].getAttribute()); } break; } case LELFunctionEnums::LENGTH : { if (exp.nelements() != 2) { throw (AipsError ("LELFunctionFloat::constructor - " "length function should have 2 arguments")); } if (! (exp[1].isScalar() && (exp[1].dataType()==TpFloat || exp[1].dataType()==TpDouble))) { throw (AipsError ("LELFunctionFloat::constructor - " "2nd argument of length function " "should be a real scalar")); } setAttr (LELAttribute()); // result is scalar break; } case LELFunctionEnums::SIGN : { // Expect 1 Float argument Block argType(1); argType[0] = TpFloat; setAttr (LatticeExprNode::checkArg (exp, argType, False)); break; } case LELFunctionEnums::ATAN2 : case LELFunctionEnums::POW : case LELFunctionEnums::FMOD : case LELFunctionEnums::MIN : case LELFunctionEnums::MAX : { // Expect 2 Float arguments Block argType(2); argType[0] = TpFloat; argType[1] = TpFloat; setAttr (LatticeExprNode::checkArg (exp, argType, False)); break; } case LELFunctionEnums::FRACTILE1D : { if (exp.nelements() != 2) { throw (AipsError ("LELFunctionFloat::constructor - " "fractile function should have 2 arguments")); } if (! (exp[1].isScalar() && exp[1].dataType()==TpFloat)) { throw (AipsError ("LELFunctionFloat::constructor - " "2nd argument of fractile function " "should be a float scalar")); } setAttr (LELAttribute()); // result is scalar break; } case LELFunctionEnums::FRACTILERANGE1D : { if (exp.nelements() != 2 && exp.nelements() != 3) { throw (AipsError ("LELFunctionFloat::constructor - " "fractilerange function should have 2 or 3 arguments")); } if (exp[0].isScalar()) { throw (AipsError ("LELFunctionFloat::constructor - " "1st argument of fractilerange function " "should be a lattice")); } for (uInt i=1; i& result, const Slicer& section) const { #if defined(AIPS_TRACE) cout << "LELFunctionFloat:: eval" << endl; #endif if (arg_p.nelements() == 1) { switch (function_p) { case LELFunctionEnums::ABS : { if (arg_p[0].dataType() == TpFloat) { arg_p[0].eval(result, section); Array tmp(abs(result.value())); result.value().reference(tmp); } else { LELArrayRef tmpC(result.shape()); arg_p[0].evalRef(tmpC, section); result.setMask(tmpC); amplitude(result.value(), tmpC.value()); } break; } case LELFunctionEnums::ARG : { LELArrayRef tmpC(result.shape()); arg_p[0].evalRef(tmpC, section); result.setMask(tmpC); phase(result.value(), tmpC.value()); break; } case LELFunctionEnums::REAL : { if (arg_p[0].dataType() == TpFloat) { arg_p[0].eval(result, section); } else { LELArrayRef tmpC(result.shape()); arg_p[0].evalRef(tmpC, section); result.setMask(tmpC); real(result.value(), tmpC.value()); } break; } case LELFunctionEnums::IMAG : { LELArrayRef tmpC(result.shape()); arg_p[0].evalRef(tmpC, section); result.setMask(tmpC); imag(result.value(), tmpC.value()); break; } case LELFunctionEnums::SIGN : { arg_p[0].eval(result, section); Bool deleteIt; Float* data = result.value().getStorage (deleteIt); uInt nr = result.value().nelements(); for (uInt i=0; i 0) { data[i] = 1; } } result.value().putStorage (data, deleteIt); break; } default: throw (AipsError ("LELFunctionFloat::eval - " "unknown Float function")); } } else { if (arg_p[0].isScalar()) { Float scalarTemp; arg_p[0].eval(scalarTemp); arg_p[1].eval(result, section); switch (function_p) { case LELFunctionEnums::ATAN2 : { Array templ (result.shape()); templ = scalarTemp; Array temp (atan2 (templ, result.value())); result.value().reference (temp); break; } case LELFunctionEnums::POW : { Array templ (result.shape()); templ = scalarTemp; Array temp (pow (templ, result.value())); result.value().reference (temp); break; } case LELFunctionEnums::FMOD : { Array templ (result.shape()); templ = scalarTemp; Array temp (fmod (templ, result.value())); result.value().reference (temp); break; } case LELFunctionEnums::MIN : min (result.value(), result.value(), scalarTemp); break; case LELFunctionEnums::MAX : max (result.value(), result.value(), scalarTemp); break; default: throw (AipsError ("LELFunctionFloat::eval - " "unknown Float function")); } } else if (arg_p[1].isScalar()) { Float scalarTemp; arg_p[1].eval(scalarTemp); arg_p[0].eval(result, section); switch (function_p) { case LELFunctionEnums::ATAN2 : { Array tempr (result.shape()); tempr = scalarTemp; Array temp (atan2 (result.value(), tempr)); result.value().reference (temp); break; } case LELFunctionEnums::POW : { if (scalarTemp == 2) { result.value() *= result.value(); } else { Array temp (pow (result.value(), scalarTemp)); result.value().reference (temp); } break; } case LELFunctionEnums::FMOD : { Array tempr (result.shape()); tempr = scalarTemp; Array temp (fmod (result.value(), tempr)); result.value().reference (temp); break; } case LELFunctionEnums::MIN : min (result.value(), result.value(), scalarTemp); break; case LELFunctionEnums::MAX : max (result.value(), result.value(), scalarTemp); break; default: throw (AipsError ("LELFunctionFloat::eval - " "unknown Float function")); } } else { LELArrayRef tempr(result.shape()); arg_p[0].eval(result, section); arg_p[1].evalRef(tempr, section); result.combineMask (tempr); switch(function_p) { case LELFunctionEnums::ATAN2 : { Array temp (atan2 (result.value(), tempr.value())); result.value().reference (temp); break; } case LELFunctionEnums::POW : { Array temp (pow (result.value(), tempr.value())); result.value().reference (temp); break; } case LELFunctionEnums::FMOD : { Array temp (fmod (result.value(), tempr.value())); result.value().reference (temp); break; } case LELFunctionEnums::MIN : min(result.value(), result.value(), tempr.value()); break; case LELFunctionEnums::MAX : max(result.value(), result.value(), tempr.value()); break; default: throw(AipsError("LELFunctionFloat::eval - " "unknown function")); } } } } LELScalar LELFunctionFloat::getScalar() const { #if defined(AIPS_TRACE) cout << "LELFunctionFloat:: getScalar" << endl; #endif switch (function_p) { case LELFunctionEnums::NDIM : { if (arg_p[0].isScalar()) { return 0; } return arg_p[0].shape().nelements(); } case LELFunctionEnums::LENGTH : { Double daxis; if (arg_p[1].dataType() == TpFloat) { daxis = arg_p[1].getFloat(); } else { daxis = arg_p[1].getDouble(); } Int axis = Int(daxis+0.499); // add for rounding if (axis < 0) { throw (AipsError ("Axis argument in length function is < 0; " "(note axis is 0-relative!)")); } if (arg_p[0].isScalar()) { return 1; } const IPosition& shape = arg_p[0].shape(); if (axis >= Int(shape.nelements())) { return 1; } return shape(axis); } case LELFunctionEnums::ABS : { if (arg_p[0].dataType() == TpFloat) { return abs(arg_p[0].getFloat()); } else { return Float(abs(arg_p[0].getComplex())); } } case LELFunctionEnums::ARG : return Float(arg(arg_p[0].getComplex())); case LELFunctionEnums::REAL : if (arg_p[0].dataType() == TpFloat) { return arg_p[0].getFloat(); } else { return Float(real(arg_p[0].getComplex())); } case LELFunctionEnums::IMAG : return Float(imag(arg_p[0].getComplex())); case LELFunctionEnums::SIGN : { Float value = arg_p[0].getFloat(); if (value < 0) { value = -1; } else if (value > 0) { value = 1; } return value; } case LELFunctionEnums::ATAN2 : return atan2(arg_p[0].getFloat(), arg_p[1].getFloat()); case LELFunctionEnums::POW : return pow(arg_p[0].getFloat(), arg_p[1].getFloat()); case LELFunctionEnums::FMOD : return fmod(arg_p[0].getFloat(), arg_p[1].getFloat()); case LELFunctionEnums::MIN : return min(arg_p[0].getFloat(), arg_p[1].getFloat()); case LELFunctionEnums::MAX : return max(arg_p[0].getFloat(), arg_p[1].getFloat()); case LELFunctionEnums::FRACTILE1D : { if (arg_p[0].isScalar()) { return arg_p[0].getFloat(); } Vector fractile = LatticeFractile::maskedFractile (LatticeExpr(arg_p[0]), arg_p[1].getFloat()); if (fractile.nelements() == 0) { return LELScalar(); // no valid fractile found } return fractile(0); } case LELFunctionEnums::FRACTILERANGE1D : { Float fraction1 = arg_p[1].getFloat(); Float fraction2 = fraction1; if (arg_p.nelements() > 2) { fraction2 = arg_p[2].getFloat(); } Vector fractiles = LatticeFractile::maskedFractiles (LatticeExpr(arg_p[0]), fraction1, fraction2); if (fractiles.nelements() < 2) { return LELScalar(); // no valid fractiles found } return fractiles(1) - fractiles(0); } default: throw(AipsError("LELFunctionFloat::getScalar - unknown function")); } return LELScalar(); } Bool LELFunctionFloat::prepareScalarExpr() { #if defined(AIPS_TRACE) cout << "LELFunctionFloat::prepare" << endl; #endif uInt i; for (i=0; i 0 || (function_p != LELFunctionEnums::NDIM && function_p != LELFunctionEnums::LENGTH)) { return True; } } } return False; } String LELFunctionFloat::className() const { return String("LELFunctionFloat"); } Bool LELFunctionFloat::lock (FileLocker::LockType type, uInt nattempts) { for (uInt i=0; i& exp) : function_p(function) { switch (function_p) { case LELFunctionEnums::ABS : case LELFunctionEnums::ARG : case LELFunctionEnums::REAL : case LELFunctionEnums::IMAG : case LELFunctionEnums::NELEM : // Returns a real number { if (exp.nelements() != 1) { throw (AipsError ("LELFunctionDouble::constructor - " "function can only have one argument")); } if (function_p == LELFunctionEnums::NELEM) { setAttr (LELAttribute()); // result is scalar } else { setAttr(exp[0].getAttribute()); } break; } case LELFunctionEnums::NTRUE : case LELFunctionEnums::NFALSE : { Block argType(1); argType[0] = TpBool; LatticeExprNode::checkArg (exp, argType, True); // expect 1 Bool array setAttr (LELAttribute()); // result is scalar break; } case LELFunctionEnums::ATAN2 : case LELFunctionEnums::POW : case LELFunctionEnums::FMOD : case LELFunctionEnums::MIN : case LELFunctionEnums::MAX : { // Expect 2 Double arguments Block argType(2); argType[0] = TpDouble; argType[1] = TpDouble; setAttr (LatticeExprNode::checkArg (exp, argType, False)); break; } case LELFunctionEnums::FRACTILE1D : { if (exp.nelements() != 2) { throw (AipsError ("LELFunctionDouble::constructor - " "fractile function should have 2 arguments")); } if (! (exp[1].isScalar() && exp[1].dataType()==TpFloat)) { throw (AipsError ("LELFunctionDouble::constructor - " "2nd argument of fractile function " "should be a float scalar")); } setAttr (LELAttribute()); // result is scalar break; } case LELFunctionEnums::FRACTILERANGE1D : { if (exp.nelements() != 2 && exp.nelements() != 3) { throw (AipsError ("LELFunctionDouble::constructor - " "fractilerange function should have 2 or 3 arguments")); } if (exp[0].isScalar()) { throw (AipsError ("LELFunctionDouble::constructor - " "1st argument of fractilerange function " "should be a lattice")); } for (uInt i=1; i& result, const Slicer& section) const { #if defined(AIPS_TRACE) cout << "LELFunctionDouble:: eval" << endl; #endif if (arg_p.nelements() == 1) { switch (function_p) { case LELFunctionEnums::ABS : { if (arg_p[0].dataType() == TpDouble) { arg_p[0].eval(result, section); Array tmp(abs(result.value())); result.value().reference(tmp); } else { LELArrayRef tmpC(result.shape()); arg_p[0].evalRef(tmpC, section); result.setMask(tmpC); amplitude(result.value(), tmpC.value()); } break; } case LELFunctionEnums::ARG : { LELArrayRef tmpC(result.shape()); arg_p[0].evalRef(tmpC, section); result.setMask(tmpC); phase(result.value(), tmpC.value()); break; } case LELFunctionEnums::REAL : { if (arg_p[0].dataType() == TpDouble) { arg_p[0].eval(result, section); } else { LELArrayRef tmpC(result.shape()); arg_p[0].evalRef(tmpC, section); result.setMask(tmpC); real(result.value(), tmpC.value()); } break; } case LELFunctionEnums::IMAG : { LELArrayRef tmpC(result.shape()); arg_p[0].evalRef(tmpC, section); result.setMask(tmpC); imag(result.value(), tmpC.value()); break; } default: throw (AipsError ("LELFunctionDouble::eval - " "unknown Double function")); } } else { if (arg_p[0].isScalar()) { Double scalarTemp; arg_p[0].eval(scalarTemp); arg_p[1].eval(result, section); switch (function_p) { case LELFunctionEnums::ATAN2 : { Array templ (result.shape()); templ = scalarTemp; Array temp (atan2 (templ, result.value())); result.value().reference (temp); break; } case LELFunctionEnums::POW : { Array templ (result.shape()); templ = scalarTemp; Array temp (pow (templ, result.value())); result.value().reference (temp); break; } case LELFunctionEnums::FMOD : { Array templ (result.shape()); templ = scalarTemp; Array temp (fmod (templ, result.value())); result.value().reference (temp); break; } case LELFunctionEnums::MIN : min (result.value(), result.value(), scalarTemp); break; case LELFunctionEnums::MAX : max (result.value(), result.value(), scalarTemp); break; default: throw (AipsError ("LELFunctionDouble::eval - " "unknown Double function")); } } else if (arg_p[1].isScalar()) { Double scalarTemp = 0; if (function_p != LELFunctionEnums::FRACTILE1D && function_p != LELFunctionEnums::FRACTILERANGE1D) { arg_p[1].eval(scalarTemp); arg_p[0].eval(result, section); } switch (function_p) { case LELFunctionEnums::ATAN2 : { Array tempr (result.shape()); tempr = scalarTemp; Array temp (atan2 (result.value(), tempr)); result.value().reference (temp); break; } case LELFunctionEnums::POW : { if (scalarTemp == 2) { result.value() *= result.value(); } else { Array temp (pow (result.value(), scalarTemp)); result.value().reference (temp); } break; } case LELFunctionEnums::FMOD : { Array tempr (result.shape()); tempr = scalarTemp; Array temp (fmod (result.value(), tempr)); result.value().reference (temp); break; } case LELFunctionEnums::MIN : min (result.value(), result.value(), scalarTemp); break; case LELFunctionEnums::MAX : max (result.value(), result.value(), scalarTemp); break; default: throw (AipsError ("LELFunctionDouble::eval - " "unknown Double function")); } } else { LELArrayRef tempr(result.shape()); arg_p[0].eval(result, section); arg_p[1].evalRef(tempr, section); result.combineMask (tempr); switch(function_p) { case LELFunctionEnums::ATAN2 : { Array temp (atan2 (result.value(), tempr.value())); result.value().reference (temp); break; } case LELFunctionEnums::POW : { Array temp (pow (result.value(), tempr.value())); result.value().reference (temp); break; } case LELFunctionEnums::FMOD : { Array temp (fmod (result.value(), tempr.value())); result.value().reference (temp); break; } case LELFunctionEnums::MIN : min(result.value(), result.value(), tempr.value()); break; case LELFunctionEnums::MAX : max(result.value(), result.value(), tempr.value()); break; default: throw(AipsError("LELFunctionDouble::eval - " "unknown function")); } } } } LELScalar LELFunctionDouble::getScalar() const { #if defined(AIPS_TRACE) cout << "LELFunctionDouble:: getScalar" << endl; #endif switch (function_p) { case LELFunctionEnums::NTRUE : { uInt ntrue = 0; Bool deleteIt, deleteMask; LatticeExpr latExpr(arg_p[0]); if (! arg_p[0].isMasked()) { RO_LatticeIterator iter(latExpr); while (! iter.atEnd()) { const Array& array = iter.cursor(); const Bool* data = array.getStorage (deleteIt); uInt n = array.nelements(); for (uInt i=0; i iter(latExpr); Array mask; while (! iter.atEnd()) { const Array& array = iter.cursor(); iter.getMask (mask); const Bool* data = array.getStorage (deleteIt); const Bool* maskdata = mask.getStorage (deleteMask); uInt n = array.nelements(); for (uInt i=0; i latExpr(arg_p[0]); if (! arg_p[0].isMasked()) { RO_LatticeIterator iter(latExpr); while (! iter.atEnd()) { const Array& array = iter.cursor(); const Bool* data = array.getStorage (deleteIt); uInt n = array.nelements(); for (uInt i=0; i iter(latExpr); Array mask; while (! iter.atEnd()) { const Array& array = iter.cursor(); iter.getMask (mask); const Bool* data = array.getStorage (deleteIt); const Bool* maskdata = mask.getStorage (deleteMask); uInt n = array.nelements(); for (uInt i=0; i fractile = LatticeFractile::maskedFractile (LatticeExpr(arg_p[0]), arg_p[1].getFloat()); if (fractile.nelements() == 0) { return LELScalar(); // no valid fractile found } return fractile(0); } case LELFunctionEnums::FRACTILERANGE1D : { Float fraction1 = arg_p[1].getFloat(); Float fraction2 = fraction1; if (arg_p.nelements() > 2) { fraction2 = arg_p[2].getFloat(); } Vector fractiles = LatticeFractile::maskedFractiles (LatticeExpr(arg_p[0]), fraction1, fraction2); if (fractiles.nelements() < 2) { return LELScalar(); // no valid fractiles found } return fractiles(1) - fractiles(0); } default: throw(AipsError("LELFunctionDouble::getScalar - unknown function")); } return LELScalar(); // Make compiler happy } uInt LELFunctionDouble::nMaskedElements (const LatticeExprNode& expr) const { uInt nelem = 0; switch (expr.dataType()) { case TpFloat: { LatticeExpr latExpr(expr); RO_MaskedLatticeIterator iter(latExpr); Array mask; while (! iter.atEnd()) { iter.getMask (mask); nelem += nMaskedOn (mask); iter++; } break; } case TpDouble: { LatticeExpr latExpr(expr); RO_MaskedLatticeIterator iter(latExpr); Array mask; while (! iter.atEnd()) { iter.getMask (mask); nelem += nMaskedOn (mask); iter++; } break; } case TpComplex: { LatticeExpr latExpr(expr); RO_MaskedLatticeIterator iter(latExpr); Array mask; while (! iter.atEnd()) { iter.getMask (mask); nelem += nMaskedOn (mask); iter++; } break; } case TpDComplex: { LatticeExpr latExpr(expr); RO_MaskedLatticeIterator iter(latExpr); Array mask; while (! iter.atEnd()) { iter.getMask (mask); nelem += nMaskedOn (mask); iter++; } break; } case TpBool: { LatticeExpr latExpr(expr); RO_MaskedLatticeIterator iter(latExpr); Array mask; while (! iter.atEnd()) { iter.getMask (mask); nelem += nMaskedOn (mask); iter++; } break; } default: throw (AipsError ("LELFunction2::nMaskedElements - unknown data type")); } return nelem; } uInt LELFunctionDouble::nMaskedOn (const Array& mask) const { uInt nelem = 0; Bool deleteMask; const Bool* maskdata = mask.getStorage (deleteMask); uInt n = mask.nelements(); for (uInt i=0; i& exp) : function_p(function) { switch (function_p) { case LELFunctionEnums::CONJ : { // Expect 1 Complex argument if (exp.nelements() != 1) { throw (AipsError ("LELFunctionComplex::constructor - " "function can only have one argument")); } setAttr(exp[0].getAttribute()); break; } case LELFunctionEnums::COMPLEX : { // Expect 2 Float arguments Block argType(2); argType[0] = TpFloat; argType[1] = TpFloat; setAttr (LatticeExprNode::checkArg (exp, argType, False)); break; } case LELFunctionEnums::POW : { // Expect 2 Complex arguments Block argType(2); argType[0] = TpComplex; argType[1] = TpComplex; setAttr (LatticeExprNode::checkArg (exp, argType, False)); break; } default: throw (AipsError ("LELFunctionComplex::constructor - " "unknown Complex function")); } // Fill the node block here, so an exception does // not leave the nodes undestructed. arg_p = exp; #if defined(AIPS_TRACE) cout << "LELFunctionComplex: constructor" << endl; #endif } LELFunctionComplex::~LELFunctionComplex() { #if defined(AIPS_TRACE) cout << "LELFunctionComplex: destructor" << endl; #endif } void LELFunctionComplex::eval(LELArray& result, const Slicer& section) const { #if defined(AIPS_TRACE) cout << "LELFunctionComplex:: eval" << endl; #endif if (arg_p.nelements() == 1) { switch (function_p) { case LELFunctionEnums::CONJ : { arg_p[0].eval(result, section); Array tmpC(conj(result.value())); result.value().reference(tmpC); break; } default: throw (AipsError ("LELFunctionComplex::eval - " "unknown Complex function")); } } else { if (arg_p[0].isScalar()) { switch (function_p) { case LELFunctionEnums::COMPLEX : { Float scalarTemp; LELArrayRef arrayTemp(result.shape()); arg_p[0].eval(scalarTemp); arg_p[1].evalRef(arrayTemp, section); Bool delr, delc; const Float* rptr = arrayTemp.value().getStorage(delr); Complex *cptr = result.value().getStorage(delc); uInt n=arrayTemp.value().nelements(); for (uInt i=0; i templ (result.shape()); templ = scalarTemp; Array temp (pow (templ, result.value())); result.value().reference (temp); break; } default: throw (AipsError ("LELFunctionComplex::eval - " "unknown Complex function")); } } else if (arg_p[1].isScalar()) { switch (function_p) { case LELFunctionEnums::COMPLEX : { Float scalarTemp; LELArrayRef arrayTemp(result.shape()); arg_p[1].eval(scalarTemp); arg_p[0].evalRef(arrayTemp, section); Bool delr, delc; const Float* rptr = arrayTemp.value().getStorage(delr); Complex *cptr = result.value().getStorage(delc); uInt n=arrayTemp.value().nelements(); for (uInt i=0; i temp (pow (result.value(), exponent)); result.value().reference (temp); } } else { Array exponent (result.shape()); exponent = scalarTemp; Array temp (pow (result.value(), exponent)); result.value().reference (temp); } break; } default: throw (AipsError ("LELFunctionComplex::eval - " "unknown Complex function")); } } else { switch(function_p) { case LELFunctionEnums::COMPLEX : { LELArrayRef arrayLeft(result.shape()); LELArrayRef arrayRight(result.shape()); arg_p[0].evalRef(arrayLeft, section); arg_p[1].evalRef(arrayRight, section); Bool dell, delr, delc; const Float* lptr = arrayLeft.value().getStorage(dell); const Float* rptr = arrayRight.value().getStorage(delr); Complex *cptr = result.value().getStorage(delc); uInt n=arrayLeft.value().nelements(); for (uInt i=0; i tempr(result.shape()); arg_p[0].eval(result, section); arg_p[1].evalRef(tempr, section); result.combineMask (tempr); Array temp (pow (result.value(), tempr.value())); result.value().reference (temp); break; } default: throw(AipsError("LELFunctionComplex::eval - " "unknown function")); } } } } LELScalar LELFunctionComplex::getScalar() const { #if defined(AIPS_TRACE) cout << "LELFunctionComplex:: getScalar" << endl; #endif switch (function_p) { case LELFunctionEnums::CONJ : return conj(arg_p[0].getComplex()); case LELFunctionEnums::COMPLEX : return Complex(arg_p[0].getFloat(), arg_p[1].getFloat()); case LELFunctionEnums::POW : return pow(arg_p[0].getComplex(), arg_p[1].getComplex()); default: throw(AipsError("LELFunctionComplex::getScalar - unknown function")); } return LELScalar(); // Make compiler happy } Bool LELFunctionComplex::prepareScalarExpr() { #if defined(AIPS_TRACE) cout << "LELFunctionComplex::prepare" << endl; #endif uInt i; for (i=0; i& exp) : function_p(function) { switch (function_p) { case LELFunctionEnums::CONJ : { // Expect 1 Complex argument if (exp.nelements() != 1) { throw (AipsError ("LELFunctionDComplex::constructor - " "functions can only have one argument")); } setAttr(exp[0].getAttribute()); break; } case LELFunctionEnums::COMPLEX : { // Expect 2 Double arguments Block argType(2); argType[0] = TpDouble; argType[1] = TpDouble; setAttr (LatticeExprNode::checkArg (exp, argType, False)); break; } case LELFunctionEnums::POW : { // Expect 2 DComplex arguments Block argType(2); argType[0] = TpDComplex; argType[1] = TpDComplex; setAttr (LatticeExprNode::checkArg (exp, argType, False)); break; } default: throw (AipsError ("LELFunctionDComplex::constructor - " "unknown DComplex function")); } // Fill the node block here, so an exception does // not leave the nodes undestructed. arg_p = exp; #if defined(AIPS_TRACE) cout << "LELFunctionDComplex: constructor" << endl; #endif } LELFunctionDComplex::~LELFunctionDComplex() { #if defined(AIPS_TRACE) cout << "LELFunctionDComplex: destructor" << endl; #endif } void LELFunctionDComplex::eval(LELArray& result, const Slicer& section) const { #if defined(AIPS_TRACE) cout << "LELFunctionDComplex:: eval" << endl; #endif if (arg_p.nelements() == 1) { switch (function_p) { case LELFunctionEnums::CONJ : { arg_p[0].eval(result, section); Array tmpC(conj(result.value())); result.value().reference(tmpC); break; } default: throw (AipsError ("LELFunctionDComplex::eval - " "unknown Complex function")); } } else { if (arg_p[0].isScalar()) { switch (function_p) { case LELFunctionEnums::COMPLEX : { Double scalarTemp; LELArrayRef arrayTemp(result.shape()); arg_p[0].eval(scalarTemp); arg_p[1].evalRef(arrayTemp, section); Bool delr, delc; const Double* rptr = arrayTemp.value().getStorage(delr); DComplex *cptr = result.value().getStorage(delc); uInt n=arrayTemp.value().nelements(); for (uInt i=0; i templ (result.shape()); templ = scalarTemp; Array temp (pow (templ, result.value())); result.value().reference (temp); break; } default: throw (AipsError ("LELFunctionDComplex::eval - " "unknown DComplex function")); } } else if (arg_p[1].isScalar()) { switch (function_p) { case LELFunctionEnums::COMPLEX : { Double scalarTemp; LELArrayRef arrayTemp(result.shape()); arg_p[1].eval(scalarTemp); arg_p[0].evalRef(arrayTemp, section); Bool delr, delc; const Double* rptr = arrayTemp.value().getStorage(delr); DComplex *cptr = result.value().getStorage(delc); uInt n=arrayTemp.value().nelements(); for (uInt i=0; i temp (pow (result.value(), exponent)); result.value().reference (temp); } } else { Array exponent (result.shape()); exponent = scalarTemp; Array temp (pow (result.value(), exponent)); result.value().reference (temp); } break; } default: throw (AipsError ("LELFunctionDComplex::eval -" "unknown DComplex function")); } } else { switch(function_p) { case LELFunctionEnums::COMPLEX : { LELArrayRef arrayLeft(result.shape()); LELArrayRef arrayRight(result.shape()); arg_p[0].evalRef(arrayLeft, section); arg_p[1].evalRef(arrayRight, section); Bool dell, delr, delc; const Double* lptr = arrayLeft.value().getStorage(dell); const Double* rptr = arrayRight.value().getStorage(delr); DComplex *cptr = result.value().getStorage(delc); uInt n=arrayLeft.value().nelements(); for (uInt i=0; i tempr(result.shape()); arg_p[0].eval(result, section); arg_p[1].evalRef(tempr, section); result.combineMask (tempr); Array temp (pow (result.value(), tempr.value())); result.value().reference (temp); break; } default: throw(AipsError("LELFunctionDComplex::eval - " "unknown function")); } } } } LELScalar LELFunctionDComplex::getScalar() const { #if defined(AIPS_TRACE) cout << "LELFunctionDComplex:: getScalar" << endl; #endif switch (function_p) { case LELFunctionEnums::CONJ : return conj(arg_p[0].getDComplex()); case LELFunctionEnums::COMPLEX : return DComplex(arg_p[0].getDouble(), arg_p[1].getDouble()); case LELFunctionEnums::POW : return pow(arg_p[0].getDComplex(), arg_p[1].getDComplex()); default: throw(AipsError("LELFunctionDComplex::getScalar - unknown function")); } return LELScalar(); // Make compiler happy } Bool LELFunctionDComplex::prepareScalarExpr() { #if defined(AIPS_TRACE) cout << "LELFunctionDComplex::prepare" << endl; #endif uInt i; for (i=0; i& exp) : function_p(function) { switch (function_p) { case LELFunctionEnums::ISNAN : { if (exp.nelements() != 1) { throw (AipsError ("LELFunctionBool::constructor - " "function can only have one argument")); } if (exp[0].dataType() == TpBool) { throw (AipsError ("LELFunctionBool::constructor - " "function isNaN cannot have bool argument")); } setAttr(exp[0].getAttribute()); break; } case LELFunctionEnums::ALL : case LELFunctionEnums::ANY : { Block argType(1); argType[0] = TpBool; LatticeExprNode::checkArg (exp, argType, True); // expect 1 Bool array setAttr (LELAttribute()); // result is scalar break; } case LELFunctionEnums::INDEXIN : { // The first argument must be a real scalar. if (! (exp[0].isScalar() && (exp[0].dataType()==TpFloat || exp[0].dataType()==TpDouble))) { throw (AipsError ("LELFunctionBool::constructor - " "1st argument of INDEXIN function " "should be a real scalar")); } // The second argument must be a bool vector. if (exp[1].isScalar() || exp[1].dataType()!=TpBool || exp[1].shape().nelements() != 1) { throw (AipsError ("LELFunctionBool::constructor - " "2nd argument of INDEXIN function " "should be a bool vector")); } // The output shape is unknown. setAttr (LELAttribute (False, IPosition(), IPosition(), LELCoordinates())); break; } case LELFunctionEnums::MASK : case LELFunctionEnums::VALUE : { if (exp.nelements() != 1) { throw (AipsError ("LELFunctionBool::constructor - " "function can only have one argument")); } // The value or mask itself is unmasked. const LELAttribute& argAttr = exp[0].getAttribute(); if (argAttr.isScalar()) { setAttr (LELAttribute()); } else { setAttr (LELAttribute (False, argAttr.shape(), argAttr.tileShape(), argAttr.coordinates())); } break; } default: throw (AipsError ("LELFunctionBool::constructor - " "unknown Bool function")); } // Fill the node block here, so an exception does // not leave the nodes undestructed. arg_p = exp; #if defined(AIPS_TRACE) cout << "LELFunctionBool: constructor" << endl; #endif } LELFunctionBool::~LELFunctionBool() { #if defined(AIPS_TRACE) cout << "LELFunctionBool: destructor" << endl; #endif } void LELFunctionBool::eval(LELArray& result, const Slicer& section) const { #if defined(AIPS_TRACE) cout << "LELFunctionBool:: eval" << endl; #endif switch (function_p) { case LELFunctionEnums::ISNAN : { Bool deleteIn, deleteOut; Bool* out = result.value().getStorage(deleteOut); uInt nr = result.value().nelements(); if (arg_p[0].dataType() == TpFloat) { LELArrayRef tmp(result.shape()); arg_p[0].evalRef(tmp, section); result.setMask(tmp); const Float* in = tmp.value().getStorage(deleteIn); for (uInt i=0; i tmp(result.shape()); arg_p[0].evalRef(tmp, section); result.setMask(tmp); const Double* in = tmp.value().getStorage(deleteIn); for (uInt i=0; i tmp(result.shape()); arg_p[0].evalRef(tmp, section); result.setMask(tmp); const Complex* in = tmp.value().getStorage(deleteIn); for (uInt i=0; i tmp(result.shape()); arg_p[0].evalRef(tmp, section); result.setMask(tmp); const DComplex* in = tmp.value().getStorage(deleteIn); for (uInt i=0; i= Int(section.ndim())) { throw (AipsError ("Axis argument in INDEXIN function outside array; " "(note axis is 0-relative!)")); } uInt stinx = section.start()[axis]; Array tmp(section.length()); const IPosition& shp = tmp.shape(); uInt nrinx = stinx + shp[axis]; uInt nr1 = 1; for (Int i=0; i pixelArr = arg_p[1].getArrayBool(); Bool deletePix; const Bool* pixels = pixelArr.getStorage (deletePix); uInt nrpix = pixelArr.nelements(); Bool deleteIt; Bool* tmpp = tmp.getStorage (deleteIt); uInt inx = 0; if (nr1 == 1) { for (uInt i1=0; i1 tmp(result.shape()); arg_p[0].evalRef(tmp, section); result.value() = tmp.mask(); break; } case TpDouble: { LELArrayRef tmp(result.shape()); arg_p[0].evalRef(tmp, section); result.value() = tmp.mask(); break; } case TpComplex: { LELArrayRef tmp(result.shape()); arg_p[0].evalRef(tmp, section); result.value() = tmp.mask(); break; } case TpDComplex: { LELArrayRef tmp(result.shape()); arg_p[0].evalRef(tmp, section); result.value() = tmp.mask(); break; } case TpBool: { LELArrayRef tmp(result.shape()); arg_p[0].evalRef(tmp, section); result.value() = tmp.mask(); break; } default: throw (AipsError ("LELFunction2::eval - unknown data type")); } } break; } case LELFunctionEnums::VALUE : { arg_p[0].eval (result, section); result.removeMask(); break; } default: throw(AipsError("LELFunctionBool::eval - unknown function")); } } LELScalar LELFunctionBool::getScalar() const { #if defined(AIPS_TRACE) cout << "LELFunctionBool:: getScalar" << endl; #endif // Apply the function switch(function_p) { case LELFunctionEnums::ISNAN : { if (arg_p[0].dataType() == TpFloat) { return isNaN (arg_p[0].getFloat()); } else if (arg_p[0].dataType() == TpDouble) { return isNaN (arg_p[0].getDouble()); } else if (arg_p[0].dataType() == TpComplex) { return isNaN (arg_p[0].getComplex()); } else { return isNaN (arg_p[0].getDComplex()); } break; } case LELFunctionEnums::ALL : { Bool deleteIt, deleteMask; LatticeExpr latExpr(arg_p[0]); if (! arg_p[0].isMasked()) { RO_LatticeIterator iter(latExpr); while (! iter.atEnd()) { const Array& array = iter.cursor(); const Bool* data = array.getStorage (deleteIt); uInt n = array.nelements(); for (uInt i=0; i iter(latExpr); Array mask; while (! iter.atEnd()) { const Array& array = iter.cursor(); iter.getMask (mask); const Bool* data = array.getStorage (deleteIt); const Bool* maskdata = mask.getStorage (deleteMask); uInt n = array.nelements(); for (uInt i=0; i latExpr(arg_p[0]); if (! arg_p[0].isMasked()) { RO_LatticeIterator iter(latExpr); while (! iter.atEnd()) { const Array& array = iter.cursor(); const Bool* data = array.getStorage (deleteIt); uInt n = array.nelements(); for (uInt i=0; i iter(latExpr); Array mask; while (! iter.atEnd()) { const Array& array = iter.cursor(); iter.getMask (mask); const Bool* data = array.getStorage (deleteIt); const Bool* maskdata = mask.getStorage (deleteMask); uInt n = array.nelements(); for (uInt i=0; i(); // Make compiler happy } Bool LELFunctionBool::prepareScalarExpr() { #if defined(AIPS_TRACE) cout << "LELFunctionBool::prepare" << endl; #endif uInt i; for (i=0; i namespace casacore { //# NAMESPACE CASACORE - BEGIN // Each LEL function is described in this enum // // // // // // // //
      • Lattice //
      • LatticeExpr //
      • LatticeExprNode //
      • LELInterface // // // // This enum provides a value for each function accepted // by the Lattice Expression Language classes. // // // // Each function name accepted by the bridging class LatticeExprNode // and passed on to the // LELFunction // letter classes is labelled internally // with a value from this enum. // // // // class LELFunctionEnums { public: enum Function { // sin SIN, // sinh SINH, // asin ASIN, // cos COS, // cosh COSH, // acos ACOS, // tan TAN, // tanh TANH, // atan; atan(x) returns the arc tangent of x in the range -pi/2 to pi/2. ATAN, // atan2; atan2(y,x) computes an arc tangent of y/x in the range -pi to pi ATAN2, // exp EXP, // log LOG, // log10 LOG10, // power pow(x,y) == x**y or x^y POW, // sqrt SQRT, // round ROUND, // sign (-1 if <0; 0 if 0; 1 if >0) SIGN, // ceil; returns the least integral value greater than or equal to x. CEIL, // floor; returns the greatest integral value less than or equal to x. FLOOR, // abs ABS, // phase (of complex number) ARG, // real (part of number) REAL, // imag (inary part of complex number) IMAG, // conj (ugate complex number) CONJ, // complex (form complex from 2 reals) COMPLEX, // fmod; fmod(x,y) returns the remainder of x with respect to y; that is, // the result r is one of the numbers that differ from x by an integral multiple of y. FMOD, // min; min(x,y) MIN, // max; max(x,y) MAX, // min; min(x) (is a scalar) MIN1D, // max; max(x) (is a scalar) MAX1D, // mean; mean(x) (is a scalar) MEAN1D, // median; median(x) (is a scalar) MEDIAN1D, // fractile; fractile(x,fraction) (is a scalar) FRACTILE1D, // fractilerange; fractilerange(x,fraction1[,fraction2]) (is a scalar) FRACTILERANGE1D, // sum; sum(x) (is a scalar) SUM, // nelements; nelements(x) (is a scalar) NELEM, // all (true) (is a scalar) ALL, // any (true) (is a scalar) ANY, // ntrue NTRUE, // nfalse NFALSE, // mask MASK, // value VALUE, // iif (similar to ?: in C++) IIF, // replace REPLACE, // dimensionality NDIM, // length (of an axis) LENGTH, // is the value a NaN? ISNAN, // a bool array telling which indices of an axis are to be used INDEXIN, // number of functions NFUNCTIONS }; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LEL/LELInterface.h000066400000000000000000000223241476623553700201610ustar00rootroot00000000000000//# LELInterface.h: Abstract base class for lattice expressions //# Copyright (C) 1997,1998,1999,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LELINTERFACE_H #define LATTICES_LELINTERFACE_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class LELScalar; template class LELArray; template class LELArrayRef; class Slicer; // This base class provides the interface for Lattice expressions // // // // //
      • Lattice //
      • LatticeExpr //
      • LatticeExprNode // // // The name means "Lattice Expression Language Interface". // This class provides the declaration for the interface for classes // that are to provide Lattice expression computational functionality // // // This class is part of the Letter/envelope scheme which enables // the C++ programmer to write mathematical expressions involving // Lattices. The envelope class LatticeExpr invokes the bridge // class LatticeExprNode. LatticeExprNode activates the letter // classes which provide the real functionality. // // A description of the implementation details of these classes can // be found in // Note 216 // // This class, LELInterface, is the abstract base class for all of // the letter classes. Its purpose is to declare the interface inherited // by all of its derived classes which are used polymorphically. The derived // classes offer the functionality to create and evaluate the expression // tree that results from the compiler parsing the expression. // For example, these derived classes are activated by LatticeExprNode to // handle operations like reading pixels from a Lattice, applying binary // operations to Lattices, applying mathematical functions to Lattices // and so on. // // The heart of the interface is in the functions eval and // getScalar. These recursively evaluate the result of the // current expression when the result is either an array or a scalar, // respectively. The need for recursion can be understood with a simple // example. // // Consider an expression summing two Lattices such as "2*(b+c)". // The expression tree consists of nodes (leaves) that 1) get Lattice // pixels from the Lattice (expressions "b" and "c"), 2) add the pixel // values of the Lattices together (operator "+"), and 3) multiply a Lattice // by a scalar (operator "*"). At the top of the tree, // we have a scalar (2.0) and a Lattice (the // result of "b+c"). The top-of-the-tree expression has to multiply // them together. That's what the eval function for the "*" // operation needs to do. The key is that each of the "2.0" and // "b+c" are really Lattice expressions themselves and they can be evaluated. // So before the "*" eval function can // multiply its two expressions together, it must individually evaluate them. // Thus, it first calls the getScalar function of // the object housing the expression "2.0". This will in fact return // the scalar value "2.0". Then it calls // eval on the expression object housing "b+c". This // object in turn first calls eval on the left ("b") and // right ("c") expressions which results in the pixels for the Lattices // being returned. It then adds them together, returning the result // to the top of the tree where they are multiplied by 2. You can see // that since all these different expression objects call the // eval or getScalar function that they all inherit // from LELInterface. Indeed for our example above, the actual classes // involved are are LELLattice (get pixels from Lattice) and LELBinary // ("+" and "*" operators) which inherit from LELInterface. When these // objects are constructed, they work out whether the result of their // evaluation is a scalar or not. This is how the classes higher up // the tree know whether to call eval or getScalar. // // The results of the computations are either returned in the buffer in // the eval function or by value by getScalar // // The classes evaluate the expression for each specified Lattice // chunk (usually tile by tile). The section argument // in the eval function specifies the section of the // Lattice being evaluated. The absence of the section // argument in the getScalar function emphasises the // scalar nature; a scalar expression does not have a shape. For most // of the letter classes, the section argument is irrelevant; // the only one it really matters for is LELLattice which fetches the // pixels from the Lattice. The rest only care about the shape of the // buffer in the eval call. // // // // // The many letter classes that actually do the computational work // are used polymorphically. Therefore, they must have a base // class declaring the interface. // // // template class LELInterface { public: // Virtual destructor virtual ~LELInterface(); // Evaluate the expression and fill the result array virtual void eval (LELArray& result, const Slicer& section) const = 0; virtual void evalRef (LELArrayRef& result, const Slicer& section) const; // Get the result of a scalar subexpression. virtual LELScalar getScalar() const = 0; // Get the result of an array subexpression. // It does eval for the entire array. // An exception is thrown if the shape of the subexpression is unknown. LELArray getArray() const; // Do further preparations (e.g. optimization) on the expression. // It returns True if the expression is an invalid scalar // (i.e. with a False mask). // That can happen if the expression has a component with an invalid // scalar value (e.g. min(lattice) where lattice contains no valid elements). virtual Bool prepareScalarExpr() = 0; // Is the result of evaluating this expression a scalar ? Bool isScalar() const {return attr_p.isScalar();} // Get the shape of the expression result. const IPosition& shape() const {return attr_p.shape();} // Get expression attribute const LELAttribute& getAttribute() const {return attr_p;} // Get class name virtual String className() const = 0; // If the given expression is a valid scalar, replace it by its result. // It returns False if the expression is no scalar or if the expression // is an invalid scalar (i.e. with a False mask). static Bool replaceScalarExpr (std::shared_ptr>& expr); // Handle locking/syncing of the parts of a lattice expression. //
        By default the functions do not do anything at all. // lock() and hasLock return True. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; virtual void resync(); // protected: // Set the expression attributes of this object. void setAttr(const LELAttribute& attrib); private: LELAttribute attr_p; }; } //# NAMESPACE CASACORE - END //# There is a problem in including LELInterface.tcc, because it needs //# LELUnary.h which in its turn includes LELInterface.h again. //# So in a source file including LELUnary.h, LELInterface::replaceScalarExpr //# fails to compile, because the LELUnary declarations are not seen yet. //# Therefore LELUnary.h is included here, while LELUnary.h includes //# LELInterface.tcc. #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/LEL/LELInterface.tcc000066400000000000000000000065231476623553700205060ustar00rootroot00000000000000//# LELInterface.cc: this defines LELInterface.cc //# Copyright (C) 1997,1998,1999,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LELINTERFACE_TCC #define LATTICES_LELINTERFACE_TCC #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template LELInterface::~LELInterface() {} template void LELInterface::setAttr (const LELAttribute& attr) { attr_p = attr; } template void LELInterface::evalRef (LELArrayRef& result, const Slicer& section) const { // For one reason or another gcc requires an explicit cast // for LELInterface. eval ((LELArray&)result, section); } template LELArray LELInterface::getArray() const { const IPosition& shp = shape(); if (shp.nelements() == 0) { throw AipsError ("LELInterface::getArray: shape is unknown"); } LELArray result(shp); Slicer slc(IPosition(shp.nelements(),0), shp); eval (result, slc); return result; } template Bool LELInterface::replaceScalarExpr (std::shared_ptr>& expr) { // Recursively prepare (optimize) a scalar subexpression Bool isInvalidScalar = expr->prepareScalarExpr(); // If the value is a valid scalar expression, replace it by its result // (which can be an invalid scalar in itself). if (!isInvalidScalar && expr->isScalar()) { LELScalar tmp = expr->getScalar(); if (tmp.mask()) { expr = std::make_shared>(tmp.value()); } else { isInvalidScalar = True; } } // If the value is an invalid scalar expression, replace by scalar // with false mask. if (isInvalidScalar) { expr = std::make_shared>(); } return isInvalidScalar; } template Bool LELInterface::lock (FileLocker::LockType, uInt) { return True; } template void LELInterface::unlock() {} template Bool LELInterface::hasLock (FileLocker::LockType) const { return True; } template void LELInterface::resync() {} } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LEL/LELLattCoord.cc000066400000000000000000000076461476623553700203240ustar00rootroot00000000000000//# LELLattCoord.cc: The base letter class for lattice coordinates //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LELLattCoord::LELLattCoord() {} LELLattCoord::~LELLattCoord() {} Bool LELLattCoord::hasCoordinates() const { return False; } String LELLattCoord::classname() const { return "LELLattCoord"; } Int LELLattCoord::compare (const LELLattCoordBase&) const { return 0; } Int LELLattCoord::doCompare (const LELImageCoord&) const { return 0; } LatticeExprNode LELLattCoord::makeSubLattice (const LatticeExprNode& expr, const LattRegionHolder& region) const { LatticeRegion latReg (region.toLatticeRegion (expr.shape())); switch (expr.dataType()) { case TpBool: return SubLattice (LatticeExpr(expr), latReg); case TpFloat: return SubLattice (LatticeExpr(expr), latReg); case TpDouble: return SubLattice (LatticeExpr(expr), latReg); case TpComplex: return SubLattice (LatticeExpr(expr), latReg); case TpDComplex: return SubLattice (LatticeExpr(expr), latReg); default: throw (AipsError ("LELLattCoord::makeSubLattice - unknown datatype")); } return LatticeExprNode(); } LatticeExprNode LELLattCoord::makeExtendLattice (const LatticeExprNode&, const IPosition&, const LELLattCoordBase&) const { throw AipsError ("LELCoordinates::getSpectralInfo - " "cannot extend lattice without coordinates"); return LatticeExprNode(); } LatticeExprNode LELLattCoord::makeRebinLattice (const LatticeExprNode& expr, const IPosition& binning) const { switch (expr.dataType()) { case TpFloat: return RebinLattice (LatticeExpr(expr), binning); case TpDouble: return RebinLattice (LatticeExpr(expr), binning); case TpComplex: return RebinLattice (LatticeExpr(expr), binning); case TpDComplex: return RebinLattice (LatticeExpr(expr), binning); default: throw (AipsError ("LELLattCoord::makeRebinLattice - invalid datatype")); } return LatticeExprNode(); } uInt LELLattCoord::getSpectralInfo (Vector&, const IPosition&) const { throw AipsError ("LELCoordinates::getSpectralInfo - " "no spectral coordinates available"); return 0; } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LEL/LELLattCoord.h000066400000000000000000000101131476623553700201450ustar00rootroot00000000000000//# LELLattCoord.h: The base letter class for lattice coordinates in LEL //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LELLATTCOORD_H #define LATTICES_LELLATTCOORD_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class LatticeExprNode; class LattRegionHolder; class IPosition; // // The base letter class for lattice coordinates in LEL. // // // // // //
      • Lattice //
      • LELLattCoordBase // // // This class is a letter class for the envelope class // LELCoordinates. // It acts as the coordinates class for Lattice objects without // coordinates (like PagedArray). // // It does not do anything, but makes it possible that other classes // (like LELImageCoord) // implement their own behaviour. // // // It must be possible to handle image coordinates in a lattice // expression. // //# //#
      • //# class LELLattCoord : public LELLattCoordBase { public: LELLattCoord(); // A virtual destructor is needed so that it will use the actual // destructor in the derived class. virtual ~LELLattCoord(); // The class does not have true coordinates. virtual Bool hasCoordinates() const; // Create a SubLattice for an expression node. virtual LatticeExprNode makeSubLattice (const LatticeExprNode& expr, const LattRegionHolder& region) const; // Create an extension for an expression node. virtual LatticeExprNode makeExtendLattice (const LatticeExprNode& expr, const IPosition& newShape, const LELLattCoordBase& newCoord) const; // Create a rebinning for an expression node. virtual LatticeExprNode makeRebinLattice (const LatticeExprNode& expr, const IPosition& binning) const; // Get the coordinates of the spectral axis for the given shape. // This function throws an exception as a Lattice has no coordinates. virtual uInt getSpectralInfo (Vector& worldCoordinates, const IPosition& shape) const; // The name of the class. virtual String classname() const; // Check how the coordinates of this and that compare. virtual Int compare (const LELLattCoordBase& other) const; // Check how the coordinates of this and that image compare. // This function is used by conform to make a // double virtual dispatch possible. virtual Int doCompare (const LELImageCoord& other) const; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LEL/LELLattCoordBase.cc000066400000000000000000000026301476623553700211030ustar00rootroot00000000000000//# LELLattCoordBase.cc: The base letter class for lattice coordinates //# Copyright (C) 1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LELLattCoordBase::~LELLattCoordBase() {} } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LEL/LELLattCoordBase.h000066400000000000000000000071051476623553700207470ustar00rootroot00000000000000//# LELLattCoordBase.h: The base letter class for lattice coordinates in LEL //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LELLATTCOORDBASE_H #define LATTICES_LELLATTCOORDBASE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class LELImageCoord; class IPosition; // // The base letter class for lattice coordinates in LEL. // // // // // //
      • Lattice //
      • LELCoordinates // // // This abstract base class is the basic letter for the envelope class // LELCoordinates. // It does not do anything, but makes it possible that derived classes // (like LELLattCoord and // LELImageCoord) // implement their own behaviour. // // // It must be possible to handle image coordinates in a lattice // expression. // //# //#
      • //# class LELLattCoordBase { public: LELLattCoordBase() {}; // A virtual destructor is needed so that it will use the actual // destructor in the derived class. virtual ~LELLattCoordBase(); // Does the class have true coordinates? virtual Bool hasCoordinates() const = 0; // The name of the class. virtual String classname() const = 0; // Get the coordinates of the spectral axis for the given shape. // It returns the pixel axis number of the spectral coordinates. // -1 indicates that there is no pixel spectral axis. // An exception is thrown if there are no world spectral coordinates. virtual uInt getSpectralInfo (Vector& worldCoordinates, const IPosition& shape) const = 0; // Check how the coordinates of this and that compare. virtual Int compare (const LELLattCoordBase& other) const = 0; // Check how the coordinates of this and that image compare. // This function is used by conform to make a // double virtual dispatch possible. virtual Int doCompare (const LELImageCoord& other) const = 0; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LEL/LELLattice.h000066400000000000000000000105701476623553700176460ustar00rootroot00000000000000//# LELLattice.h: LELLattice //# Copyright (C) 1997,1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LELLATTICE_H #define LATTICES_LELLATTICE_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Lattice; template class MaskedLattice; // This LEL class handles access to Lattices // // // // // // // //
      • Lattice //
      • LatticeExpr //
      • LatticeExprNode //
      • LELInterface // // // // This derived LEL letter class handles access to the pixels in a Lattice // // // // This LEL letter class is derived from LELInterface. It // is used to construct LEL objects that access the pixels in // a Lattice. It works with Lattices of type // Float,Double,Complex,DComplex and Bool. // // A description of the implementation details of the LEL classes can // be found in // Note 216 // // // // Examples are not very useful as the user would never use // these classes directly. Look in LatticeExprNode.cc to see // how it invokes these classes. An example of how the user // would indirectly use this class (through the envelope) is: // // IPosition shape(2,5,10); // ArrayLattice x(shape); x.set(1.0); // ArrayLattice y(shape); // y.copyData(x); // y = x // // // // // Accessing Lattices is fundamental to manipulating their pixel values // // // // template class LELLattice : public LELInterface { //# Make members of parent class known. public: using LELInterface::getAttribute; protected: using LELInterface::setAttr; public: // Constructor takes lattice to fetch from it // LELLattice (const Lattice& lattice); LELLattice (const MaskedLattice& lattice); // // Destructor does nothing ~LELLattice(); // Evaluate the expression; this means get the chunk of the lattice. virtual void eval(LELArray& result, const Slicer& section) const; virtual void evalRef(LELArrayRef& result, const Slicer& section) const; // Getting a scalar value is not possible (throws exception). virtual LELScalar getScalar() const; // Do further preparations (e.g. optimization) on the expression. virtual Bool prepareScalarExpr(); // Get class name virtual String className() const; // Handle locking/syncing of a lattice in a lattice expression. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; virtual void resync(); // private: MaskedLattice* pLattice_p; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/LEL/LELLattice.tcc000066400000000000000000000110121476623553700201600ustar00rootroot00000000000000//# LELLattice.cc: this defines LELLattice.cc //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LELLATTICE_TCC #define LATTICES_LELLATTICE_TCC #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template LELLattice::LELLattice(const Lattice& lattice) : pLattice_p (new SubLattice (lattice)) { setAttr(LELAttribute(False, lattice.shape(), lattice.niceCursorShape(), lattice.lelCoordinates())); #if defined(AIPS_TRACE) cout << "LELLattice:: constructor, pLattice_p.nrefs() = " << pLattice_p.nrefs() << endl; #endif } template LELLattice::LELLattice(const MaskedLattice& lattice) : pLattice_p (lattice.cloneML()) { setAttr(LELAttribute(lattice.isMasked(), lattice.shape(), lattice.niceCursorShape(), lattice.lelCoordinates())); #if defined(AIPS_TRACE) cout << "LELLattice:: constructor, pLattice_p.nrefs() = " << pLattice_p.nrefs() << endl; #endif } template LELLattice::~LELLattice() { delete pLattice_p; #if defined(AIPS_TRACE) cout << "LELLattice:: destructor, pLattice_p.nrefs() = " << pLattice_p.nrefs() << endl; #endif } template void LELLattice::eval(LELArray& result, const Slicer& section) const { #if defined(AIPS_TRACE) cout << "LELLattice::eval; pLattice_p.nrefs() " << pLattice_p.nrefs() << endl; #endif Array tmp = pLattice_p->getSlice (section); result.value().reference(tmp); if (getAttribute().isMasked()) { Array mask = pLattice_p->getMaskSlice (section); result.setMask (mask); } else { result.removeMask(); } } template void LELLattice::evalRef(LELArrayRef& result, const Slicer& section) const { #if defined(AIPS_TRACE) cout << "LELLattice::evalRef; pLattice_p.nrefs() " << pLattice_p.nrefs() << endl; #endif Array tmp; pLattice_p->getSlice (tmp, section); // Cast to its base class LELArray to use the non-const value function. ((LELArray&)result).value().reference(tmp); if (getAttribute().isMasked()) { Array mask = pLattice_p->getMaskSlice (section); result.setMask (mask); } else { result.removeMask(); } } template LELScalar LELLattice::getScalar() const { throw (AipsError ("LELLattice::getScalar - cannot be used")); return pLattice_p->getAt (IPosition()); // to make compiler happy } template Bool LELLattice::prepareScalarExpr() { return False; } template String LELLattice::className() const { return String("LELLattice"); } template Bool LELLattice::lock (FileLocker::LockType type, uInt nattempts) { return pLattice_p->lock (type, nattempts); } template void LELLattice::unlock() { pLattice_p->unlock(); } template Bool LELLattice::hasLock (FileLocker::LockType type) const { return pLattice_p->hasLock (type); } template void LELLattice::resync() { pLattice_p->resync(); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LEL/LELRegion.cc000066400000000000000000000126341476623553700176450ustar00rootroot00000000000000//# LELRegion.cc: Class to hold a region as a LEL node //# Copyright (C) 1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LELRegion::LELRegion (const LattRegionHolder& region) : region_p (region.clone()) { setAttr (LELAttribute(region.ndim())); } LELRegion::LELRegion (LattRegionHolder* region) : region_p (region) { setAttr (LELAttribute(region->ndim())); } LELRegion::~LELRegion() { delete region_p; } void LELRegion::eval(LELArray&, const Slicer&) const { throw (AipsError ("LELRegion:eval - cannot be used")); } LELScalar LELRegion::getScalar() const { throw (AipsError ("LELRegion::getScalar - cannot be used")); return False; } Bool LELRegion::prepareScalarExpr() { return False; } String LELRegion::className() const { return "LELRegion"; } LELRegion* LELRegion::makeUnion (const LELInterface& left, const LELInterface& right) { const LattRegionHolder& r1 = LELRegion::region (left); const LattRegionHolder& r2 = LELRegion::region (right); // Assure that both types are the same and that no LCSlicer is used. checkTypes (r1, r2); // Return the union of both regions. return new LELRegion (r1.makeUnion (r2)); } LELRegion* LELRegion::makeIntersection (const LELInterface& left, const LELInterface& right) { const LattRegionHolder& r1 = LELRegion::region (left); const LattRegionHolder& r2 = LELRegion::region (right); // Assure that both types are the same and that no LCSlicer is used. checkTypes (r1, r2); // Return the intersection of both regions. return new LELRegion (r1.makeIntersection (r2)); } LELRegion* LELRegion::makeDifference (const LELInterface& left, const LELInterface& right) { const LattRegionHolder& r1 = LELRegion::region (left); const LattRegionHolder& r2 = LELRegion::region (right); // Assure that both types are the same and that no LCSlicer is used. checkTypes (r1, r2); // Return the difference of both regions. return new LELRegion (r1.makeDifference (r2)); } LELRegion* LELRegion::makeComplement (const LELInterface& expr) { const LattRegionHolder& r1 = LELRegion::region (expr); // Assure that both types are the same and that no LCSlicer is used. checkTypes (r1, r1); // Return the complement of the region. return new LELRegion (r1.makeComplement()); } const LattRegionHolder& LELRegion::region (const LELInterface& expr) { AlwaysAssert (expr.className() == "LELRegion", AipsError); return ((const LELRegion&)expr).region(); } void LELRegion::checkTypes (const LattRegionHolder& left, const LattRegionHolder& right) { if (left.isLCRegion() && right.isLCRegion()) { return; } if (left.isWCRegion() && right.isWCRegion()) { return; } if (left.isLCSlicer() || right.isLCSlicer()) { throw (AipsError ("LELRegion::checkTypes - " "a compound of Slicer objects is not possible")); } throw (AipsError ("LELRegion::checkTypes - " "in a compound both regions must have the same " "type of coordinates (pixel or world)")); } LELRegionAsBool::LELRegionAsBool (const LELRegion& region) { const LattRegionHolder& reg = region.region(); if (! reg.isLCRegion()) { throw (AipsError ("LELRegionAsBool cannot handle " "a region in world coordinates")); } region_p = LatticeRegion (*(reg.asLCRegionPtr())); setAttr(LELAttribute(False, region_p.shape(), region_p.niceCursorShape(), region_p.lelCoordinates())); } LELRegionAsBool::~LELRegionAsBool() {} void LELRegionAsBool::eval(LELArray& result, const Slicer& section) const { Array tmp = region_p.getSlice (section); result.value().reference(tmp); } LELScalar LELRegionAsBool::getScalar() const { throw (AipsError ("LELRegionAsBool::getScalar - cannot be used")); return False; } Bool LELRegionAsBool::prepareScalarExpr() { return False; } String LELRegionAsBool::className() const { return "LELRegionAsBool"; } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LEL/LELRegion.h000066400000000000000000000141441476623553700175050ustar00rootroot00000000000000//# LELRegion.h: Class to hold a region as a LEL node //# Copyright (C) 1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LELREGION_H #define LATTICES_LELREGION_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class LattRegionHolder; // // Class to hold a region as a LEL node // // // // // //
      • Lattice //
      • LatticeExpr //
      • LatticeExprNode //
      • LELInterface // // // This derived LEL letter class handles regions. // // // This LEL letter class is derived from LELInterface. It // is used to construct LEL objects from regions. // The internal region is an ImageRegion // object, thus the region can be of any type. // With operator [] a region is applied to an image (expression). // At that stage possible world coordinates are converted to lattice // coordinates. //

        // The attributes of a LELRegion object define an empty shape, // because in general the shape of a region is only known after // it is applied to an image. //

        // A description of the implementation details of the LEL classes can // be found in // Note 216 // // // We needed to be able to handle regions in a LEL expression. // //# //# class LELRegion : public LELInterface { public: // Constructor. LELRegion (const LattRegionHolder& region); // Constructor. It takes over the pointer. LELRegion (LattRegionHolder* region); // Destructor. ~LELRegion(); // Get a pointer to the region object. const LattRegionHolder& region() const { return *region_p; } // Getting region data cannot be done (throws an exception). virtual void eval(LELArray&, const Slicer&) const; // Getting region data cannot be done (throws an exception). virtual LELScalar getScalar() const; // Do further preparations (e.g. optimization) on the expression. virtual Bool prepareScalarExpr(); // Get class name virtual String className() const; // Form a compound from the regions. // static LELRegion* makeUnion (const LELInterface& left, const LELInterface& right); static LELRegion* makeIntersection (const LELInterface& left, const LELInterface& right); static LELRegion* makeDifference (const LELInterface& left, const LELInterface& right); static LELRegion* makeComplement (const LELInterface& expr); // private: // Get the LattRegionHolder after checking that the expression is a region. static const LattRegionHolder& region (const LELInterface& expr); // Check if both regions have the same type (pixel or world) and if // no LCSlicer type of region is used. static void checkTypes (const LattRegionHolder& left, const LattRegionHolder& right); // Member variables. LattRegionHolder* region_p; }; //

        // Class to convert a region to a boolean node // // // // // //
      • Lattice //
      • LatticeExprNode //
      • LELInterface // // // This derived LEL letter class handles a region as a boolean lattice. // // // This class makes it possible to handle a region as a true // boolean lattice without the need to apply the region to an image. // It means that it is only possible if the region has absolute // lattice coordinates. //

        // A description of the implementation details of the LEL classes can // be found in // Note 216 // // // It is useful to be able to handle a mask as a boolean lattice. // //# //# class LELRegionAsBool : public LELInterface { public: // Constructor. LELRegionAsBool (const LELRegion& region); // Destructor. ~LELRegionAsBool(); // Get region data. virtual void eval(LELArray& result, const Slicer& section) const; // Getting region data as a scalar cannot be done (throws an exception). virtual LELScalar getScalar() const; // Do further preparations (e.g. optimization) on the expression. virtual Bool prepareScalarExpr(); // Get class name virtual String className() const; private: // Member variables. LatticeRegion region_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LEL/LELScalar.h000066400000000000000000000045771476623553700175000ustar00rootroot00000000000000//# LELScalar.h: Hold a scalar with a mask in LEL //# Copyright (C) 1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LELSCALAR_H #define LATTICES_LELSCALAR_H //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //

        // This LEL class holds a scalar with a mask. // // // // // // This LEL class holds a scalar with a mask. // // // It maskes it possible to handle a scalar with its mask as a single object. // // // template class LELScalar { public: // Default constructor sets a False mask. LELScalar(); // Constructor takes value and optional mask. LELScalar (const T& value, Bool mask=True) : itsValue (value), itsMask(mask) {} // Get value. // const T& value() const { return itsValue; } T& value() { return itsValue; } // // Get mask. Bool mask() const { return itsMask; } private: T itsValue; Bool itsMask; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/LEL/LELScalar.tcc000066400000000000000000000030431476623553700200050ustar00rootroot00000000000000//# LELScalar.cc: Hold a scalar with a mask in LEL //# Copyright (C) 1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LELSCALAR_TCC #define LATTICES_LELSCALAR_TCC #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template LELScalar::LELScalar() : itsMask(False) { ValType::getUndef (&itsValue); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LEL/LELSpectralIndex.h000066400000000000000000000074701476623553700210330ustar00rootroot00000000000000//# LELSpectralIndex.h: LEL function to calculate spectral index/ //# Copyright (C) 2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LELSPECTRALINDEX_H #define LATTICES_LELSPECTRALINDEX_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // This LEL class handles calculation of the spectral index. // // // // // //
      • Lattice //
      • LatticeExpr //
      • LatticeExprNode //
      • LELInterface //
      • LELFunctionEnums // // // This LEL letter class is derived from LELInterface. It is used to // construct LEL objects that calculate the sepectral index from 2 other // LEL expression (usually images). // It operates on real types (Float,Double) // // // This is a separate class (instead of being part of a LELFunction class), // because the calculation of the spectral index requires extra variables // (the frequencies) and some more complicated code. // template class LELSpectralIndex : public LELInterface { //# Make members of parent class known. protected: using LELInterface::setAttr; public: // Constructor takes operation and expressions to be operated upon LELSpectralIndex (const Block& expr); // Destructor ~LELSpectralIndex(); // Recursively evaluate the expression virtual void eval (LELArray& result, const Slicer& section) const; // Get the result of a scalar subexpression. // Throws an exception as it is not possible. virtual LELScalar getScalar() const; // Do further preparations (e.g. optimization) on the expression. // Returns False. virtual Bool prepareScalarExpr(); // Get class name virtual String className() const; // Handle locking/syncing of a lattice in a lattice expression. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; virtual void resync(); // private: Int itsFreqAxis; Block itsLogFreq; //# log(f0/f1) LatticeExprNode arg0_p; LatticeExprNode arg1_p; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/LEL/LELSpectralIndex.tcc000066400000000000000000000165561476623553700213620ustar00rootroot00000000000000//# LELSpectralIndex.cc: LEL function to calculate spectral index/ //# Copyright (C) 2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LELSPECTRALINDEX_TCC #define LATTICES_LELSPECTRALINDEX_TCC #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template LELSpectralIndex::LELSpectralIndex (const Block& expr) { arg0_p = expr[0]; arg1_p = expr[1]; // Spectralindex cannot handle scalars. // Expect 2 equal data types. Block argType(2); argType[0] = arg0_p.dataType(); argType[1] = argType[0]; setAttr (LatticeExprNode::checkArg (expr, argType, True, False)); // Get the spectral coordinate info of the arguments. const LELAttribute& attr0 = arg0_p.getAttribute(); const LELAttribute& attr1 = arg1_p.getAttribute(); Vector freq0, freq1; itsFreqAxis = attr0.coordinates().coordinates().getSpectralInfo (freq0, attr0.shape()); Int freqAxis1 = attr1.coordinates().coordinates().getSpectralInfo (freq1, attr1.shape()); Vector logFreq; if (freq0.nelements() == 1) { logFreq = log (freq0(0) / freq1); } else if (freq1.nelements() == 1) { logFreq = log (freq0 / freq1(0)); } else { AlwaysAssert (freq0.nelements() == freq1.nelements(), AipsError); logFreq = log (freq0 / freq1); } // Note that a Block is faster than Vector (in function eval), // so we rather use a Block. itsLogFreq.resize (logFreq.nelements()); for (uInt i=0; i(cbptr); AlwaysAssert (cptr != 0, AipsError); arg0_p = cptr->makeExtendLattice (arg0_p, attr1.shape(), attr1.coordinates().coordinates()); itsFreqAxis = freqAxis1; } else if (result == 1) { // right is subset of left, so extend right. const LELLattCoordBase* cbptr = &(attr1.coordinates().coordinates()); const LELLattCoord* cptr = dynamic_cast(cbptr); AlwaysAssert (cptr != 0, AipsError); arg1_p = cptr->makeExtendLattice (arg1_p, attr0.shape(), attr0.coordinates().coordinates()); } else { throw AipsError ("LELSpectralIndex - coordinates of operands mismatch"); } } template LELSpectralIndex::~LELSpectralIndex() {} template void LELSpectralIndex::eval (LELArray& result, const Slicer& section) const { #if defined(AIPS_TRACE) cout << "LELFunctionFloat:: eval" << endl; #endif // Get the values of the left and right operand. // The resulting mask is the combination of the tow. LELArrayRef tempr(result.shape()); arg0_p.eval(result, section); arg1_p.evalRef(tempr, section); result.combineMask (tempr); // For each frequency channel the data has to be replaced by // log(left/right) / log(leftfreq/rightfreq) // Note that the freq factor has already been calculated in the constructor. // 'Split' the data into the part before the frequency axis and // the part after the frequency axis. const IPosition& shp = result.value().shape(); uInt nrt = result.value().nelements(); uInt stf = 0; // start in itsLogFreq uInt endf = 0; // end in itsLogFreq uInt incrf = 1; // step in itsLogFreq uInt nrb = 1; uInt nre = 1; if (itsFreqAxis < 0) { nrb = nrt; } else { stf = section.start()(itsFreqAxis); endf = section.end()(itsFreqAxis); incrf = section.stride()(itsFreqAxis); for (uInt i=0; i itsFreqAxis) { nre *= shp(i); } } } // Loop through all the data in that way. Bool deleteRes, deleteTmp; T* res = result.value().getStorage (deleteRes); T* resd = res; const T* tmp = tempr.value().getStorage (deleteTmp); const T* tmpd = tmp; for (uInt i=0; i String LELSpectralIndex::className() const { return "LELSpectralIndex"; } template Bool LELSpectralIndex::lock (FileLocker::LockType type, uInt nattempts) { if (! arg0_p.lock (type, nattempts)) { return False; } return arg1_p.lock (type, nattempts); } template void LELSpectralIndex::unlock() { arg0_p.unlock(); arg1_p.unlock(); } template Bool LELSpectralIndex::hasLock (FileLocker::LockType type) const { if (! arg0_p.hasLock (type)) { return False; } return arg1_p.hasLock (type); } template void LELSpectralIndex::resync() { arg0_p.resync(); arg1_p.resync(); } template LELScalar LELSpectralIndex::getScalar() const { #if defined(AIPS_TRACE) cout << "LELSpectralIndex::getScalar" << endl; #endif throw AipsError ("LELSpectralIndex::getScalar - invalid operation"); return LELScalar(); } template Bool LELSpectralIndex::prepareScalarExpr() { #if defined(AIPS_TRACE) cout << "LELSpectralIndex::prepare" << endl; #endif return False; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LEL/LELUnary.h000066400000000000000000000232711476623553700173610ustar00rootroot00000000000000//# LELUnary.h: LELUnary.h //# Copyright (C) 1997,1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LELUNARY_H #define LATTICES_LELUNARY_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // This LEL class handles scalar (unary) constants // // // // // // // //
      • Lattice //
      • LatticeExpr //
      • LatticeExprNode //
      • LELInterface //
      • LELUnaryEnums // // // // This derived LEL letter class handles scalar (unary) constants // // // // This LEL letter class is derived from LELInterface. It // is used to construct LEL objects that represent scalars // constants. They can be of type Float,Double,Complex,DComplex // and Bool. // // A description of the implementation details of the LEL classes can // be found in // Note 216 // // // // Examples are not very useful as the user would never use // these classes directly. Look in LatticeExprNode.cc to see // how it invokes these classes. Examples of how the user // would indirectly use this class (through the envelope) are: // // IPosition shape(2,5,10); // ArrayLattice x(shape); x.set(1.0); // ArrayLattice y(shape); // ArrayLattice z(shape); // y.copyData(x+2.0); // y = x + 2.0 // z.copyData(True); // z = True // // // // // Constants are a basic mathematical expression. // // // // template class LELUnaryConst : public LELInterface { //# Make members of parent class known. protected: using LELInterface::setAttr; public: // Default constructor creates a scalar with a false mask. LELUnaryConst(); // Constructor takes a scalar. LELUnaryConst(const T val); // Destructor does nothing ~LELUnaryConst(); // Evaluate the expression. // This throws an exception, since only a scalar can be returned. virtual void eval (LELArray& result, const Slicer& section) const; // Evaluate the scalar expression (get the constant) virtual LELScalar getScalar() const; // Do further preparations (e.g. optimization) on the expression. virtual Bool prepareScalarExpr(); // Get class name virtual String className() const; private: LELScalar val_p; }; // This LEL class handles numerical unary operators // // // // // // // //
      • Lattice //
      • LatticeExpr //
      • LatticeExprNode //
      • LELInterface //
      • LELUnaryEnums // // // // This derived LEL letter class handles numerical unary // operators // // // // This LEL letter class is derived from LELInterface. It // is used to construct LEL objects that apply numerical unary // operators to Lattice expressions. They operate on numerical // Lattice (Float,Double,Complex,DComplex) expressions and return the // same numerical type. The available C++ operators // are +,- with equivalents in the enum // of PLUS and MINUS. // // A description of the implementation details of the LEL classes can // be found in // Note 216 // // // // Examples are not very useful as the user would never use // these classes directly. Look in LatticeExprNode.cc to see // how it invokes these classes. An example of how the user // would indirectly use this class (through the envelope) is: // // IPosition shape(2,5,10); // ArrayLattice x(shape); x.set(1.0); // ArrayLattice y(shape); // y.copyData(-x); // y = -x // // // // // Numerical unary operations are a basic mathematical expression. // // // // template class LELUnary : public LELInterface { public: // Constructor takes operation and expression // to be operated upon LELUnary(const LELUnaryEnums::Operation op, const std::shared_ptr>& pExpr); // Destructor does nothing ~LELUnary(); // Recursively evaluate the expression. virtual void eval (LELArray& result, const Slicer& section) const; // Recursively evaluate the scalar expression. virtual LELScalar getScalar() const; // Do further preparations (e.g. optimization) on the expression. virtual Bool prepareScalarExpr(); // Get class name virtual String className() const; // Handle locking/syncing of a lattice in a lattice expression. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; virtual void resync(); // private: LELUnaryEnums::Operation op_p; std::shared_ptr> pExpr_p; }; // This LEL class handles logical unary operators // // // // // // // //
      • Lattice //
      • LatticeExpr //
      • LatticeExprNode //
      • LELInterface //
      • LELUnaryEnums // // // // This derived LEL letter class handles logical unary // operators // // // // This LEL letter class is derived from LELInterface. It // is used to construct LEL objects that apply logical unary // operators to Lattice expressions. They operate on Bool // Lattice expressions only and return a Bool. // The available C++ operator is ! with the equivalent // in the enum of NOT. // // A description of the implementation details of the LEL classes can // be found in // Note 216 // // // // Examples are not very useful as the user would never use // these classes directly. Look in LatticeExprNode.cc to see // how it invokes these classes. An example of how the user // would indirectly use this class (through the envelope) is: // // IPosition shape(2,5,10); // ArrayLattice x(shape); x.set(True); // ArrayLattice y(shape); // y.copyData(!x); // y = !x // // // // // Logical unary operations are a basic mathematical expression. // // // // class LELUnaryBool : public LELInterface { public: // Constructor takes operation and expression // to be operated upon LELUnaryBool(const LELUnaryEnums::Operation op, const std::shared_ptr>& pExpr); // Destructor does nothing ~LELUnaryBool(); // Recursively evaluate the expression. virtual void eval (LELArray& result, const Slicer& section) const; // Recursively evaluate the scalar expression. virtual LELScalar getScalar() const; // Do further preparations (e.g. optimization) on the expression. virtual Bool prepareScalarExpr(); // Get class name virtual String className() const; // Handle locking/syncing of a lattice in a lattice expression. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; virtual void resync(); // private: LELUnaryEnums::Operation op_p; std::shared_ptr> pExpr_p; }; } //# NAMESPACE CASACORE - END //# See comments in LELInterface why LELInterface.tcc is included here. #ifndef CASACORE_NO_AUTO_TEMPLATES #include #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/LEL/LELUnary.tcc000066400000000000000000000110131476623553700176720ustar00rootroot00000000000000//# LELUnary.cc: this defines templated classes in LELUnary.h //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LELUNARY_TCC #define LATTICES_LELUNARY_TCC #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template LELUnaryConst::LELUnaryConst() { setAttr (LELAttribute()); #if defined(AIPS_TRACE) cout << "LELUnaryConst:: constructor" << endl; #endif } template LELUnaryConst::LELUnaryConst(const T val) : val_p(val) { setAttr (LELAttribute()); #if defined(AIPS_TRACE) cout << "LELUnaryConst:: T constructor" << endl; #endif } template LELUnaryConst::~LELUnaryConst() { #if defined(AIPS_TRACE) cout << "LELUnaryConst:: destructor " << endl; #endif } template void LELUnaryConst::eval(LELArray&, const Slicer&) const { throw (AipsError ("LELUnaryConst::eval - cannot be used")); } template LELScalar LELUnaryConst::getScalar() const { return val_p; } template Bool LELUnaryConst::prepareScalarExpr() { return (!val_p.mask()); } template String LELUnaryConst::className() const { return String("LELUnaryConst"); } template LELUnary::LELUnary(const LELUnaryEnums::Operation op, const std::shared_ptr>& pExpr) : op_p(op), pExpr_p(pExpr) { this->setAttr(pExpr->getAttribute()); #if defined(AIPS_TRACE) cout << "LELUnary:: constructor" << endl; #endif } template LELUnary::~LELUnary() { #if defined(AIPS_TRACE) cout << "LELUnary:: destructor " << endl; #endif } template void LELUnary::eval(LELArray& result, const Slicer& section) const { #if defined(AIPS_TRACE) cout << "LELUnary:: eval " << endl; #endif // Get the value and apply the unary operation pExpr_p->eval(result, section); switch(op_p) { case LELUnaryEnums::MINUS : { Array tmp(-result.value()); result.value().reference(tmp); break; } default: throw(AipsError("LELUnary::eval - unknown operation")); } } template LELScalar LELUnary::getScalar() const { #if defined(AIPS_TRACE) cout << "LELUnary::getScalar" << endl; #endif LELScalar temp (pExpr_p->getScalar()); switch(op_p) { case LELUnaryEnums::MINUS : temp.value() = -temp.value(); break; default: throw(AipsError("LELUnary::getScalar - unknown operation")); } return temp; } template Bool LELUnary::prepareScalarExpr() { #if defined(AIPS_TRACE) cout << "LELUnary::prepare" << endl; #endif return LELInterface::replaceScalarExpr (pExpr_p); } template String LELUnary::className() const { return String("LELUnary"); } template Bool LELUnary::lock (FileLocker::LockType type, uInt nattempts) { return pExpr_p->lock (type, nattempts); } template void LELUnary::unlock() { pExpr_p->unlock(); } template Bool LELUnary::hasLock (FileLocker::LockType type) const { return pExpr_p->hasLock (type); } template void LELUnary::resync() { pExpr_p->resync(); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LEL/LELUnary2.cc000066400000000000000000000065451476623553700176060ustar00rootroot00000000000000//# LELUnary2.cc: this defines non-templated classes in LELUnary.h //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LELUnaryBool::LELUnaryBool(const LELUnaryEnums::Operation op, const std::shared_ptr>& pExpr) : op_p(op), pExpr_p(pExpr) { setAttr(pExpr_p->getAttribute()); #if defined(AIPS_TRACE) cout << "LELUnaryBool:: constructor" << endl; #endif } LELUnaryBool::~LELUnaryBool() { #if defined(AIPS_TRACE) cout << "LELUnaryBool:: destructor " << endl; #endif } void LELUnaryBool::eval(LELArray& result, const Slicer& section) const { #if defined(AIPS_TRACE) cout << "LELUnaryBool:: eval " << endl; #endif // Get the value and apply the unary operation pExpr_p->eval(result, section); switch(op_p) { case LELUnaryEnums::NOT : { Array tmp(!result.value()); result.value().reference(tmp); break; } default: throw(AipsError("LELUnaryBool::eval - unknown operation")); } } LELScalar LELUnaryBool::getScalar() const { #if defined(AIPS_TRACE) cout << "LELUnaryBool::getScalar" << endl; #endif LELScalar temp (pExpr_p->getScalar()); switch(op_p) { case LELUnaryEnums::NOT : temp.value() = (!(temp.value())); break; default: throw(AipsError("LELUnaryBool::getScalar - unknown operation")); } return temp; } Bool LELUnaryBool::prepareScalarExpr() { #if defined(AIPS_TRACE) cout << "LELUnaryBool::prepare" << endl; #endif return LELInterface::replaceScalarExpr (pExpr_p); } String LELUnaryBool::className() const { return String("LELUnaryBool"); } Bool LELUnaryBool::lock (FileLocker::LockType type, uInt nattempts) { return pExpr_p->lock (type, nattempts); } void LELUnaryBool::unlock() { pExpr_p->unlock(); } Bool LELUnaryBool::hasLock (FileLocker::LockType type) const { return pExpr_p->hasLock (type); } void LELUnaryBool::resync() { pExpr_p->resync(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LEL/LELUnaryEnums.h000066400000000000000000000046731476623553700203760ustar00rootroot00000000000000//# LELUnaryEnums.h: Enums of unary operators //# Copyright (C) 1997 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LELUNARYENUMS_H #define LATTICES_LELUNARYENUMS_H #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Each LEL unary operation is described in this enum // // // // // // // //
      • Lattice //
      • LatticeExpr //
      • LatticeExprNode //
      • LELInterface //
      • LELUnary // // // // This enum provides a value for each unary operation accepted // by the Lattice Expression Language classes. // // // // Each unary operator accepted by the bridging class LatticeExprNode // and passed on to the LELUnary letter classes is labelled internally // with a value from this enum. // // // // class LELUnaryEnums { public: enum Operation{ // Unary plus PLUS, // Unary minus MINUS, // Unary not NOT, // Total number NOPS }; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LEL/LatticeExpr.h000066400000000000000000000221661476623553700201540ustar00rootroot00000000000000//# LatticeExpr.h: LatticeExpr.h //# Copyright (C) 1997,1998,1999,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICEEXPR_H #define LATTICES_LATTICEEXPR_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class LELArray; // Class to allow C++ expressions involving lattices // // // // //
      • Lattice //
      • LatticeExprNode // // // // // The name is derived from the fact that this class provides // an expression interface to the user which s/he may use to // write C++ expressions involving Lattices. // // // // This class provides an interface which allows the C++ programmer // to enter expressions such as "sin(a)+b" where "a" and "b" // are Lattices. // // This class is termed an envelope class, and inside it are the // letter classes which do the real work. In reality, the letter // classes are actually accessed via a bridging class called // LatticeExprNode, which exists to handle type conversions. // The letter classes iterate through the Lattices and evaluate the // expression for each chunk of the iteration (usually a tile shape). // // It is in the LatticeExprNode class that all the available expression // operations are defined, so you should look there to see what // functionality is available. // // A description of the implementation details of these classes can // be found in // Note 216 // // // // // ArrayLattice f1(IPosition (2,nx,ny)); // ArrayLattice f2(IPosition (2,nx,ny)); // f2.set(2.0); // f1.copyData(2*f2+f2); // // // In this example, the values of the pixels in Lattice f1 are set // to the values resulting from the expression "2*f2 + f2" // I.e. the expression is evaluated for each pixel in the Lattices // // Note that : // 1) the Lattice::copyData function is expecting a Lattice argument. // 2) LatticeExpr inherits from Lattice and therefore a LatticeExpr // object is a valid argument object type // 3) The expression in the copyData call is automatically converted to // a LatticeExprNode by the constructors and operators in LatticeExprNode // 4) The LatticeExprNode object so created is automatically converted // to a LatticeExpr by casting functions in LatticeExprNode. // // // // // ArrayLattice f1(IPosition (2,nx,ny)); // ArrayLattice f2(IPosition (2,nx,ny)); // ArrayLattice d(IPosition (2,nx,ny)); // ArrayLattice c(IPosition (2,nx,ny)); // ArrayLattice b(IPosition (2,nx,ny)); // // f2.set(1.0); d.set(2.0); c.set(Complex(2.0,3.0)); b.set(True); // f1.copyData( (3.5*f2) + (cos(d)) - (10/min(d,f2)*(-abs(c))*ntrue(b)) - (C::pi) ); // // // In this rather silly example, we fill Lattice "f1" with the result of the // expression. The expression shows the use of constants, unary operations, // binary operations, 1D and 2D functions. It also shows how mixed types can // be handled. The output Lattice is a Float, whereas mixed into the // expression are subexpressions involving Float, Double, Complex and Bool // Lattices. // // // // // The Lattice expression classes enable the C++ programmer much simpler // handling of mathematical expressions involving lattices. In addition, // these classes provide the infrastructure on top of which we can build // an image calculator for Glish users // // //
      • masks //
      • regions // template class LatticeExpr : public MaskedLattice { public: // Default constructor LatticeExpr(); // Constructor from an arbitrary LatticeExprNode expression object. // An exception is thrown if the expression data type cannot be // converted to the template data type. // The shape argument is mandatory if the expression has no shape. // If the expression has a shape and if shape is given, it is checked // if they are equal. LatticeExpr (const LatticeExprNode& expr); LatticeExpr (const LatticeExprNode& expr, const IPosition& latticeShape); // Copy constructor (reference semantics) LatticeExpr (const LatticeExpr& other); // Destructor, does nothing virtual ~LatticeExpr(); // Assignment (reference semantics) LatticeExpr& operator=(const LatticeExpr& other); // Make a copy of the derived object (reference semantics). virtual MaskedLattice* cloneML() const; // Has the object really a mask? virtual Bool isMasked() const; // Get the region used (always returns 0). virtual const LatticeRegion* getRegionPtr() const; // Returns False, as the LatticeExpr lattice is not writable. virtual Bool isWritable() const; // Handle locking of the LatticeExpr which is delegated to all of its parts. //
        hasLock() is True if all parts of the expression return True. //
        It is strongly recommended to use class // LatticeLocker to // handle lattice locking. It also contains a more detailed // explanation of the locking process. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; // // Resynchronize the Lattice object with the lattice file. // This function is only useful if no read-locking is used, ie. // if the table lock option is UserNoReadLocking or AutoNoReadLocking. // In that cases the table system does not acquire a read-lock, thus // does not synchronize itself automatically. //
        By default the function does not do anything at all. virtual void resync(); // Returns the shape of the Lattice including all degenerate axes // (i.e. axes with a length of one) virtual IPosition shape() const; // Return the best cursor shape. virtual IPosition doNiceCursorShape (uInt maxPixels) const; // Returns the coordinates of the lattice expression. virtual LELCoordinates lelCoordinates() const; // Do the actual get of the data. // The return value is always False, thus the buffer does not reference // another array. virtual Bool doGetSlice (Array& buffer, const Slicer& section); // Do the actual get of the mask data. // The return value is always False, thus the buffer does not reference // another array. virtual Bool doGetMaskSlice (Array& buffer, const Slicer& section); // An expression is not writable so this functions throws an exception. virtual void doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride); // Copy the data from this lattice to the given lattice. virtual void copyDataTo (Lattice& to) const; // Handle the Math operators (+=, -=, *=, /=). // They work similarly to copyData(To). // However, they are not defined for Bool types, thus specialized below. virtual void handleMathTo (Lattice& to, int oper) const; private: // Initialize the object from the expression. void init (const LatticeExprNode& expr); LatticeExprNode expr_p; //# its shape can be undefined IPosition shape_p; //# this shape is always defined LELArray* lastChunkPtr_p; Slicer lastSlicer_p; }; template<> inline void LatticeExpr::handleMathTo (Lattice&, int) const { throwBoolMath(); } } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/LEL/LatticeExpr.tcc000066400000000000000000000174131476623553700204750ustar00rootroot00000000000000//# LatticeExpr.cc: this defines LatticeExpr.cc //# Copyright (C) 1997,1998,1999,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICEEXPR_TCC #define LATTICES_LATTICEEXPR_TCC #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template LatticeExpr::LatticeExpr() : lastChunkPtr_p (0) {} template LatticeExpr::LatticeExpr (const LatticeExprNode& expr) : shape_p (expr.shape()), lastChunkPtr_p (0) { // Check if an expression array has a shape. if (!expr.isScalar() && shape_p.nelements() == 0) { throw AipsError ("LatticeExpr cannot be constructed from a lattice " "expression with an undefined shape"); } init (expr); } template LatticeExpr::LatticeExpr (const LatticeExprNode& expr, const IPosition& latticeShape) : shape_p (latticeShape), lastChunkPtr_p (0) // // Construct from a LatticeExprNode object. The LEN type is // converted to match the template type if possible // { // Check if the expression has a shape. if (!expr.isScalar() && expr.shape().nelements() > 0 && !(shape_p.isEqual(expr.shape()))) { throw AipsError ("LatticeExpr::constructor - " "given shape mismatches expression's shape"); } init (expr); } template void LatticeExpr::init (const LatticeExprNode& expr) { DataType thisDT = whatType(); if (expr.dataType() == thisDT) { expr_p = expr; } else { if (expr.dataType() == TpBool) { throw (AipsError ("LatticeExpr::constructor - " "Bool expression cannot be converted to " "a numeric type")); } switch (thisDT) { case TpFloat: expr_p = expr.makeFloat(); break; case TpDouble: expr_p = expr.makeDouble(); break; case TpComplex: expr_p = expr.makeComplex(); break; case TpDComplex: expr_p = expr.makeDComplex(); break; default: throw (AipsError ("LatticeExpr::constructor - " "A numeric type cannot be converted to Bool")); } } } template LatticeExpr::~LatticeExpr() { delete lastChunkPtr_p; } template LatticeExpr::LatticeExpr (const LatticeExpr& other) : MaskedLattice(), expr_p (other.expr_p), shape_p (other.shape_p), lastChunkPtr_p (0) {} template LatticeExpr& LatticeExpr::operator=(const LatticeExpr& other) { if (this != &other) { expr_p = other.expr_p; shape_p = other.shape_p; delete lastChunkPtr_p; lastChunkPtr_p = 0; lastSlicer_p = Slicer(); } return *this; } template MaskedLattice* LatticeExpr::cloneML() const { return new LatticeExpr (*this); } template Bool LatticeExpr::isMasked() const { return expr_p.isMasked(); } template const LatticeRegion* LatticeExpr::getRegionPtr() const { return 0; } template Bool LatticeExpr::isWritable() const { return False; } template Bool LatticeExpr::lock (FileLocker::LockType type, uInt nattempts) { return expr_p.lock (type, nattempts); } template void LatticeExpr::unlock() { expr_p.unlock(); } template Bool LatticeExpr::hasLock (FileLocker::LockType type) const { return expr_p.hasLock (type); } template void LatticeExpr::resync() { expr_p.resync(); } template IPosition LatticeExpr::shape() const { return shape_p; } template IPosition LatticeExpr::doNiceCursorShape (uInt) const { return expr_p.getAttribute().tileShape(); } template LELCoordinates LatticeExpr::lelCoordinates() const { return expr_p.getAttribute().coordinates(); } template Bool LatticeExpr::doGetSlice (Array& buffer, const Slicer& section) { // Evaluate the expression if not accessing the same section again. if (!(section==lastSlicer_p)) { delete lastChunkPtr_p; lastChunkPtr_p = new LELArray (section.length()); lastSlicer_p = section; expr_p.eval (*lastChunkPtr_p, section); } buffer.reference (lastChunkPtr_p->value()); return True; } template Bool LatticeExpr::doGetMaskSlice (Array& buffer, const Slicer& section) { // Evaluate if masked and if different section. if (expr_p.isMasked()) { if (!(section==lastSlicer_p)) { delete lastChunkPtr_p; lastChunkPtr_p = new LELArray (section.length()); lastSlicer_p = section; expr_p.eval (*lastChunkPtr_p, section); } if (lastChunkPtr_p->isMasked()) { buffer.reference (lastChunkPtr_p->mask()); return True; } } // Not masked, so we can simply fill the buffer with True values. buffer.resize (section.length()); buffer = True; return False; } template void LatticeExpr::doPutSlice (const Array&, const IPosition&, const IPosition&) { throw (AipsError ("LatticeExpr::putSlice - is not possible")); } template void LatticeExpr::copyDataTo (Lattice& to) const { // If a scalar, set lattice to its value. // Otherwise use the Lattice copyDataTo function. if (expr_p.isScalar()) { // Check the lattice is writable. AlwaysAssert (to.isWritable(), AipsError); T value; expr_p.eval (value); to.set (value); } else { Lattice::copyDataTo (to); } } template void LatticeExpr::handleMathTo (Lattice& to, int oper) const { // If a scalar, apply its value to the lattice. // Otherwise use the Lattice handleMathTo function. if (expr_p.isScalar()) { T value; expr_p.eval (value); // Check the lattice is writable. AlwaysAssert (to.isWritable(), AipsError); // Create an iterator for the output. // If possible, use reference semantics in the iterator. LatticeIterator iter(to, True); switch (oper) { case 0: for (iter.reset(); !iter.atEnd(); iter++) { iter.rwCursor() += value; } break; case 1: for (iter.reset(); !iter.atEnd(); iter++) { iter.rwCursor() -= value; } break; case 2: for (iter.reset(); !iter.atEnd(); iter++) { iter.rwCursor() *= value; } break; case 3: for (iter.reset(); !iter.atEnd(); iter++) { iter.rwCursor() /= value; } break; default: throw AipsError ("LatticeExpr::handleMathTo - Unknown operator"); } } else { Lattice::handleMathTo (to, oper); } } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LEL/LatticeExprNode.cc000066400000000000000000002147261476623553700211250ustar00rootroot00000000000000//# LatticeExprNode.cc: this defines LatticeExprNode.cc //# Copyright (C) 1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Default constructor LatticeExprNode::LatticeExprNode() : donePrepare_p (False), isInvalid_p (True), pAttr_p (0) { #if defined(AIPS_TRACE) cout << "LatticeExprNode::default constructor; pExpr_p.nrefs() = " << pExprDouble_p.nrefs() << endl; #endif } // Destructor LatticeExprNode::~LatticeExprNode() { #if defined(AIPS_TRACE) cout << "LatticeExprNode::destructor; pExpr_p.nrefs() = " << pExprDouble_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode(const std::shared_ptr>& pExpr) : donePrepare_p (False), dtype_p (TpFloat), isInvalid_p (True), pAttr_p (&pExpr->getAttribute()), pExprFloat_p (pExpr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: constructor (std::shared_ptr>&); pExpr_p.nrefs() = " << pExprFloat_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode(const std::shared_ptr>& pExpr) : donePrepare_p (False), dtype_p (TpDouble), isInvalid_p (True), pAttr_p (&pExpr->getAttribute()), pExprDouble_p (pExpr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: constructor (std::shared_ptr>&); pExpr_p.nrefs() = " << pExprDouble_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode (const std::shared_ptr>& pExpr) : donePrepare_p (False), dtype_p (TpComplex), isInvalid_p (True), pAttr_p (&pExpr->getAttribute()), pExprComplex_p (pExpr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: constructor (std::shared_ptr>&); pExpr_p.nrefs() = " << pExprComplex_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode (const std::shared_ptr>& pExpr) : donePrepare_p (False), dtype_p (TpDComplex), isInvalid_p (True), pAttr_p (&pExpr->getAttribute()), pExprDComplex_p (pExpr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: constructor (std::shared_ptr>&); pExpr_p.nrefs() = " << pExprDComplex_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode(const std::shared_ptr>& pExpr) : donePrepare_p (False), dtype_p (TpBool), isInvalid_p (True), pAttr_p (&pExpr->getAttribute()), pExprBool_p (pExpr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: constructor (std::shared_ptr>&); pExpr_p.nrefs() = " << pExprBool_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode(LELInterface* pExpr) : donePrepare_p (False), dtype_p (TpFloat), isInvalid_p (True), pAttr_p (&pExpr->getAttribute()), pExprFloat_p (pExpr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: constructor (LELInterface*); pExpr_p.nrefs() = " << pExprFloat_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode(LELInterface* pExpr) : donePrepare_p (False), dtype_p (TpDouble), isInvalid_p (True), pAttr_p (&pExpr->getAttribute()), pExprDouble_p (pExpr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: constructor (LELInterface*); pExpr_p.nrefs() = " << pExprDouble_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode(LELInterface* pExpr) : donePrepare_p (False), dtype_p (TpComplex), isInvalid_p (True), pAttr_p (&pExpr->getAttribute()), pExprComplex_p (pExpr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: constructor (LELInterface*); pExpr_p.nrefs() = " << pExprComplex_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode(LELInterface* pExpr) : donePrepare_p (False), dtype_p (TpDComplex), isInvalid_p (True), pAttr_p (&pExpr->getAttribute()), pExprDComplex_p (pExpr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: constructor (LELInterface*); pExpr_p.nrefs() = " << pExprDComplex_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode(LELInterface* pExpr) : donePrepare_p (False), dtype_p (TpBool), isInvalid_p (True), pAttr_p (&pExpr->getAttribute()), pExprBool_p (pExpr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: constructor (LELInterface*); pExpr_p.nrefs() = " << pExprBool_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode (Int64 constant) : donePrepare_p (False), dtype_p (TpFloat), isInvalid_p (False), pExprFloat_p (std::make_shared>(constant)) { pAttr_p = &pExprFloat_p->getAttribute(); #if defined(AIPS_TRACE) cout << "LatticeExprNode:: Unary constructor (T); pExpr_p.nrefs() = " << pExprFloat_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode (Int constant) : donePrepare_p (False), dtype_p (TpFloat), isInvalid_p (False), pExprFloat_p (std::make_shared>(constant)) { pAttr_p = &pExprFloat_p->getAttribute(); #if defined(AIPS_TRACE) cout << "LatticeExprNode:: Unary constructor (T); pExpr_p.nrefs() = " << pExprFloat_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode (uInt constant) : donePrepare_p (False), dtype_p (TpFloat), isInvalid_p (False), pExprFloat_p (std::make_shared>(constant)) { pAttr_p = &pExprFloat_p->getAttribute(); #if defined(AIPS_TRACE) cout << "LatticeExprNode:: Unary constructor (T); pExpr_p.nrefs() = " << pExprFloat_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode (Long constant) : donePrepare_p (False), dtype_p (TpFloat), isInvalid_p (False), pExprFloat_p (std::make_shared>(constant)) { pAttr_p = &pExprFloat_p->getAttribute(); #if defined(AIPS_TRACE) cout << "LatticeExprNode:: Unary constructor (T); pExpr_p.nrefs() = " << pExprFloat_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode (Float constant) : donePrepare_p (False), dtype_p (TpFloat), isInvalid_p (False), pExprFloat_p (std::make_shared>(constant)) { pAttr_p = &pExprFloat_p->getAttribute(); #if defined(AIPS_TRACE) cout << "LatticeExprNode:: Unary constructor (T); pExpr_p.nrefs() = " << pExprFloat_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode (Double constant) : donePrepare_p (False), dtype_p (TpDouble), isInvalid_p (False), pExprDouble_p (std::make_shared>(constant)) { pAttr_p = &pExprDouble_p->getAttribute(); #if defined(AIPS_TRACE) cout << "LatticeExprNode:: Unary constructor (T); pExpr_p.nrefs() = " << pExprDouble_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode (const Complex& constant) : donePrepare_p (False), dtype_p (TpComplex), isInvalid_p (False), pExprComplex_p (std::make_shared>(constant)) { pAttr_p = &pExprComplex_p->getAttribute(); #if defined(AIPS_TRACE) cout << "LatticeExprNode:: Unary constructor (T); pExpr_p.nrefs() = " << pExprComplex_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode (const DComplex& constant) : donePrepare_p (False), dtype_p (TpDComplex), isInvalid_p (False), pExprDComplex_p (std::make_shared>(constant)) { pAttr_p = &pExprDComplex_p->getAttribute(); #if defined(AIPS_TRACE) cout << "LatticeExprNode:: Unary constructor (T); pExpr_p.nrefs() = " << pExprDComplex_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode (Bool constant) : donePrepare_p (False), dtype_p (TpBool), isInvalid_p (False), pExprBool_p (std::make_shared>(constant)) { pAttr_p = &pExprBool_p->getAttribute(); #if defined(AIPS_TRACE) cout << "LatticeExprNode:: Unary constructor (T); pExpr_p.nrefs() = " << pExprBool_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode (const IPosition& iposition) : donePrepare_p (False), dtype_p (TpOther), isInvalid_p (False), iposition_p (iposition) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: IPosition constructor" << endl; #endif } LatticeExprNode::LatticeExprNode (const Lattice& lattice) : donePrepare_p (False), dtype_p (TpFloat), isInvalid_p (False), pExprFloat_p (std::make_shared>(lattice)) { pAttr_p = &pExprFloat_p->getAttribute(); #if defined(AIPS_TRACE) cout << "LatticeExprNode:: constructor (const Lattice&); pExpr_p.nrefs() = " << pExprFloat_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode (const Lattice& lattice) : donePrepare_p (False), dtype_p (TpDouble), isInvalid_p (False), pExprDouble_p (std::make_shared>(lattice)) { pAttr_p = &pExprDouble_p->getAttribute(); #if defined(AIPS_TRACE) cout << "LatticeExprNode:: constructor (const Lattice&); pExpr_p.nrefs() = " << pExprDouble_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode (const Lattice& lattice) : donePrepare_p (False), dtype_p (TpComplex), isInvalid_p (False), pExprComplex_p (std::make_shared>(lattice)) { pAttr_p = &pExprComplex_p->getAttribute(); #if defined(AIPS_TRACE) cout << "LatticeExprNode:: constructor (const Lattice&); pExpr_p.nrefs() = " << pExprComplex_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode (const Lattice& lattice) : donePrepare_p (False), dtype_p (TpDComplex), isInvalid_p (False), pExprDComplex_p (std::make_shared>(lattice)) { pAttr_p = &pExprDComplex_p->getAttribute(); #if defined(AIPS_TRACE) cout << "LatticeExprNode:: constructor (const Lattice&); pExpr_p.nrefs() = " << pExprDComplex_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode (const Lattice& lattice) : donePrepare_p (False), dtype_p (TpBool), isInvalid_p (False), pExprBool_p (std::make_shared>(lattice)) { pAttr_p = &pExprBool_p->getAttribute(); #if defined(AIPS_TRACE) cout << "LatticeExprNode:: constructor (Lattice); pExpr_p.nrefs() = " << pExprBool_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode (const MaskedLattice& lattice) : donePrepare_p (False), dtype_p (TpFloat), isInvalid_p (False), pExprFloat_p (std::make_shared>(lattice)) { pAttr_p = &pExprFloat_p->getAttribute(); #if defined(AIPS_TRACE) cout << "LatticeExprNode:: constructor (const MaskedLattice&); pExpr_p.nrefs() = " << pExprFloat_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode (const MaskedLattice& lattice) : donePrepare_p (False), dtype_p (TpDouble), isInvalid_p (False), pExprDouble_p (std::make_shared>(lattice)) { pAttr_p = &pExprDouble_p->getAttribute(); #if defined(AIPS_TRACE) cout << "LatticeExprNode:: constructor (const MaskedLattice&); pExpr_p.nrefs() = " << pExprDouble_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode (const MaskedLattice& lattice) : donePrepare_p (False), dtype_p (TpComplex), isInvalid_p (False), pExprComplex_p (std::make_shared>(lattice)) { pAttr_p = &pExprComplex_p->getAttribute(); #if defined(AIPS_TRACE) cout << "LatticeExprNode:: constructor (const MaskedLattice&); pExpr_p.nrefs() = " << pExprComplex_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode (const MaskedLattice& lattice) : donePrepare_p (False), dtype_p (TpDComplex), isInvalid_p (False), pExprDComplex_p (std::make_shared>(lattice)) { pAttr_p = &pExprDComplex_p->getAttribute(); #if defined(AIPS_TRACE) cout << "LatticeExprNode:: constructor (const MaskedLattice&); pExpr_p.nrefs() = " << pExprDComplex_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode (const MaskedLattice& lattice) : donePrepare_p (False), dtype_p (TpBool), isInvalid_p (False), pExprBool_p (std::make_shared>(lattice)) { pAttr_p = &pExprBool_p->getAttribute(); #if defined(AIPS_TRACE) cout << "LatticeExprNode:: constructor (MaskedLattice); pExpr_p.nrefs() = " << pExprBool_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode (const LCRegion& region) : donePrepare_p (False), dtype_p (TpBool), isInvalid_p (False), pExprBool_p (std::make_shared(new LattRegionHolder(region))) { pAttr_p = &pExprBool_p->getAttribute(); #if defined(AIPS_TRACE) cout << "LatticeExprNode:: constructor (MaskedLattice); pExpr_p.nrefs() = " << pExprBool_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode (const Slicer& slicer) : donePrepare_p (False), dtype_p (TpBool), isInvalid_p (False), pExprBool_p (std::make_shared(new LattRegionHolder(LCSlicer(slicer)))) { pAttr_p = &pExprBool_p->getAttribute(); #if defined(AIPS_TRACE) cout << "LatticeExprNode:: constructor (MaskedLattice); pExpr_p.nrefs() = " << pExprBool_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode (const LattRegionHolder& region) : donePrepare_p (False), dtype_p (TpBool), isInvalid_p (False), pExprBool_p (std::make_shared(region)) { pAttr_p = &pExprBool_p->getAttribute(); #if defined(AIPS_TRACE) cout << "LatticeExprNode:: constructor (MaskedLattice); pExpr_p.nrefs() = " << pExprBool_p.nrefs() << endl; #endif } LatticeExprNode::LatticeExprNode (const LatticeExprNode& other) : donePrepare_p (other.donePrepare_p), dtype_p (other.dtype_p), isInvalid_p (other.isInvalid_p), pAttr_p (other.pAttr_p), pExprFloat_p (other.pExprFloat_p), pExprDouble_p (other.pExprDouble_p), pExprComplex_p (other.pExprComplex_p), pExprDComplex_p (other.pExprDComplex_p), pExprBool_p (other.pExprBool_p) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: copy constructor (LatticeExprNode); pExpr_p.nrefs() = " << pExprDouble_p.nrefs() << endl; #endif } // Assignment operator LatticeExprNode& LatticeExprNode::operator= (const LatticeExprNode& other) { if (this != &other) { donePrepare_p = other.donePrepare_p; dtype_p = other.dtype_p; isInvalid_p = other.isInvalid_p; pAttr_p = other.pAttr_p; pExprFloat_p = other.pExprFloat_p; pExprDouble_p = other.pExprDouble_p; pExprComplex_p = other.pExprComplex_p; pExprDComplex_p = other.pExprDComplex_p; pExprBool_p = other.pExprBool_p; } #if defined(AIPS_TRACE) cout << "LatticeExprNode:: assignment operator (LatticeExprNode&); pExpr_p.nrefs() = " << pExprDouble_p.nrefs() << endl; #endif return *this; } Bool LatticeExprNode::lock (FileLocker::LockType type, uInt nattempts) { switch (dataType()) { case TpFloat: return pExprFloat_p->lock (type, nattempts); case TpDouble: return pExprDouble_p->lock (type, nattempts); case TpComplex: return pExprComplex_p->lock (type, nattempts); case TpDComplex: return pExprDComplex_p->lock (type, nattempts); case TpBool: return pExprBool_p->lock (type, nattempts); default: throw (AipsError ("LatticeExprNode::lock - " "unknown data type")); } return False; } void LatticeExprNode::unlock() { switch (dataType()) { case TpFloat: pExprFloat_p->unlock(); break; case TpDouble: pExprDouble_p->unlock(); break; case TpComplex: pExprComplex_p->unlock(); break; case TpDComplex: pExprDComplex_p->unlock(); break; case TpBool: pExprBool_p->unlock(); break; default: throw (AipsError ("LatticeExprNode::unlock - " "unknown data type")); } } Bool LatticeExprNode::hasLock (FileLocker::LockType type) const { switch (dataType()) { case TpFloat: return pExprFloat_p->hasLock (type); case TpDouble: return pExprDouble_p->hasLock (type); case TpComplex: return pExprComplex_p->hasLock (type); case TpDComplex: return pExprDComplex_p->hasLock (type); case TpBool: return pExprBool_p->hasLock (type); default: throw (AipsError ("LatticeExprNode::hasLock - " "unknown data type")); } return False; } void LatticeExprNode::resync() { switch (dataType()) { case TpFloat: pExprFloat_p->resync(); break; case TpDouble: pExprDouble_p->resync(); break; case TpComplex: pExprComplex_p->resync(); break; case TpDComplex: pExprDComplex_p->resync(); break; case TpBool: pExprBool_p->resync(); break; default: throw (AipsError ("LatticeExprNode::resync - " "unknown data type")); } } Bool LatticeExprNode::replaceScalarExpr() // // If the current expression evaluates to a scalar, then it can // be optimized in the tree by replacement by a scalar constant // expression such as LELUnaryConst // { switch (dataType()) { case TpFloat: isInvalid_p = LELInterface::replaceScalarExpr (pExprFloat_p); pAttr_p = &pExprFloat_p->getAttribute(); break; case TpDouble: isInvalid_p = LELInterface::replaceScalarExpr (pExprDouble_p); pAttr_p = &pExprDouble_p->getAttribute(); break; case TpComplex: isInvalid_p = LELInterface::replaceScalarExpr (pExprComplex_p); pAttr_p = &pExprComplex_p->getAttribute(); break; case TpDComplex: isInvalid_p = LELInterface::replaceScalarExpr (pExprDComplex_p); pAttr_p = &pExprDComplex_p->getAttribute(); break; case TpBool: isInvalid_p = LELInterface::replaceScalarExpr (pExprBool_p); pAttr_p = &pExprBool_p->getAttribute(); break; default: throw (AipsError ("LatticeExprNode::replaceScalarExpr - " "unknown data type")); } return isInvalid_p; } void LatticeExprNode::doPrepare() const { if (!donePrepare_p) { LatticeExprNode* This = (LatticeExprNode*)this; This->replaceScalarExpr(); This->donePrepare_p = True; } } void LatticeExprNode::eval (LELArray& result, const Slicer& section) const { // If first time, try to do optimization. DebugAssert (dataType() == TpFloat, AipsError); if (!donePrepare_p) { doPrepare(); } // If scalar, remove mask if scalar is valid. Otherwise set False mask. // If array, evaluate for this section. if (isScalar()) { LELScalar value = pExprFloat_p->getScalar(); if (value.mask()) { result.value() = value.value(); result.removeMask(); } else { result.value() = 0; Array mask (result.shape()); mask = False; result.setMask (mask); } } else { pExprFloat_p->eval (result, section); } } void LatticeExprNode::eval (LELArray& result, const Slicer& section) const { // If first time, try to do optimization. DebugAssert (dataType() == TpDouble, AipsError); if (!donePrepare_p) { LatticeExprNode* This = (LatticeExprNode*)this; This->replaceScalarExpr(); This->donePrepare_p = True; } // If scalar, remove mask if scalar is valid. Otherwise set False mask. // If array, evaluate for this section. if (isScalar()) { LELScalar value = pExprDouble_p->getScalar(); if (value.mask()) { result.value() = value.value(); result.removeMask(); } else { result.value() = 0; Array mask (result.shape()); mask = False; result.setMask (mask); } } else { pExprDouble_p->eval (result, section); } } void LatticeExprNode::eval (LELArray& result, const Slicer& section) const { // If first time, try to do optimization. DebugAssert (dataType() == TpComplex, AipsError); if (!donePrepare_p) { LatticeExprNode* This = (LatticeExprNode*)this; This->replaceScalarExpr(); This->donePrepare_p = True; } // If scalar, remove mask if scalar is valid. Otherwise set False mask. // If array, evaluate for this section. if (isScalar()) { LELScalar value = pExprComplex_p->getScalar(); if (value.mask()) { result.value() = value.value(); result.removeMask(); } else { result.value() = 0; Array mask (result.shape()); mask = False; result.setMask (mask); } } else { pExprComplex_p->eval (result, section); } } void LatticeExprNode::eval (LELArray& result, const Slicer& section) const { // If first time, try to do optimization. DebugAssert (dataType() == TpDComplex, AipsError); if (!donePrepare_p) { LatticeExprNode* This = (LatticeExprNode*)this; This->replaceScalarExpr(); This->donePrepare_p = True; } // If scalar, remove mask if scalar is valid. Otherwise set False mask. // If array, evaluate for this section. if (isScalar()) { LELScalar value = pExprDComplex_p->getScalar(); if (value.mask()) { result.value() = value.value(); result.removeMask(); } else { result.value() = 0; Array mask (result.shape()); mask = False; result.setMask (mask); } } else { pExprDComplex_p->eval (result, section); } } void LatticeExprNode::eval (LELArray& result, const Slicer& section) const { // If first time, try to do optimization. DebugAssert (dataType() == TpBool, AipsError); if (!donePrepare_p) { LatticeExprNode* This = (LatticeExprNode*)this; This->replaceScalarExpr(); This->donePrepare_p = True; } // If scalar, remove mask if scalar is valid. Otherwise set False mask. // If array, evaluate for this section. if (isScalar()) { LELScalar value = pExprBool_p->getScalar(); if (value.mask()) { result.value() = value.value(); result.removeMask(); } else { result.value() = False; Array mask (result.shape()); mask = False; result.setMask (mask); } } else { pExprBool_p->eval (result, section); } } void LatticeExprNode::eval (Float& result) const { DebugAssert (dataType() == TpFloat, AipsError); result = pExprFloat_p->getScalar().value(); } void LatticeExprNode::eval (Double& result) const { DebugAssert (dataType() == TpDouble, AipsError); result = pExprDouble_p->getScalar().value(); } void LatticeExprNode::eval (Complex& result) const { DebugAssert (dataType() == TpComplex, AipsError); result = pExprComplex_p->getScalar().value(); } void LatticeExprNode::eval (DComplex& result) const { DebugAssert (dataType() == TpDComplex, AipsError); result = pExprDComplex_p->getScalar().value(); } void LatticeExprNode::eval (Bool& result) const { DebugAssert (dataType() == TpBool, AipsError); result = pExprBool_p->getScalar().value(); } Float LatticeExprNode::getFloat() const { DebugAssert (dataType() == TpFloat, AipsError); return pExprFloat_p->getScalar().value(); } Double LatticeExprNode::getDouble() const { DebugAssert (dataType() == TpDouble, AipsError); return pExprDouble_p->getScalar().value(); } Complex LatticeExprNode::getComplex() const { DebugAssert (dataType() == TpComplex, AipsError); return pExprComplex_p->getScalar().value(); } DComplex LatticeExprNode::getDComplex() const { DebugAssert (dataType() == TpDComplex, AipsError); return pExprDComplex_p->getScalar().value(); } Bool LatticeExprNode::getBool() const { DebugAssert (dataType() == TpBool, AipsError); return pExprBool_p->getScalar().value(); } Array LatticeExprNode::getArrayFloat() const { DebugAssert (dataType() == TpFloat, AipsError); return pExprFloat_p->getArray().value(); } Array LatticeExprNode::getArrayDouble() const { DebugAssert (dataType() == TpDouble, AipsError); return pExprDouble_p->getArray().value(); } Array LatticeExprNode::getArrayComplex() const { DebugAssert (dataType() == TpComplex, AipsError); return pExprComplex_p->getArray().value(); } Array LatticeExprNode::getArrayDComplex() const { DebugAssert (dataType() == TpDComplex, AipsError); return pExprDComplex_p->getArray().value(); } Array LatticeExprNode::getArrayBool() const { DebugAssert (dataType() == TpBool, AipsError); return pExprBool_p->getArray().value(); } LatticeExprNode operator+(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: unary operator +" << endl; #endif AlwaysAssert (expr.dataType() != TpBool, AipsError); return expr; } LatticeExprNode operator-(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: Unary operator -" << endl; #endif AlwaysAssert (expr.dataType() != TpBool, AipsError); return LatticeExprNode::newNumUnary (LELUnaryEnums::MINUS, expr); } LatticeExprNode toFloat(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function float" << endl; #endif return expr.makeFloat(); } LatticeExprNode toDouble(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function double" << endl; #endif return expr.makeDouble(); } LatticeExprNode toComplex(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function complex" << endl; #endif return expr.makeComplex(); } LatticeExprNode toDComplex(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function dcomplex" << endl; #endif return expr.makeDComplex(); } LatticeExprNode toBool(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function bool" << endl; #endif return expr.makeBool(); } LatticeExprNode sin(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function sin" << endl; #endif return LatticeExprNode::newNumFunc1D (LELFunctionEnums::SIN, expr); } LatticeExprNode sinh(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function sinh" << endl; #endif return LatticeExprNode::newNumFunc1D (LELFunctionEnums::SINH, expr); } LatticeExprNode asin(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function asin" << endl; #endif return LatticeExprNode::newRealFunc1D (LELFunctionEnums::ASIN, expr); } LatticeExprNode cos(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function cos" << endl; #endif return LatticeExprNode::newNumFunc1D (LELFunctionEnums::COS, expr); } LatticeExprNode cosh(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function cosh" << endl; #endif return LatticeExprNode::newNumFunc1D (LELFunctionEnums::COSH, expr); } LatticeExprNode acos(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function acos" << endl; #endif return LatticeExprNode::newRealFunc1D (LELFunctionEnums::ACOS, expr); } LatticeExprNode tan(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function tan" << endl; #endif return LatticeExprNode::newRealFunc1D (LELFunctionEnums::TAN, expr); } LatticeExprNode tanh(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function tanh" << endl; #endif return LatticeExprNode::newRealFunc1D (LELFunctionEnums::TANH, expr); } LatticeExprNode atan(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function atan" << endl; #endif return LatticeExprNode::newRealFunc1D (LELFunctionEnums::ATAN, expr); } LatticeExprNode exp(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function exp" << endl; #endif return LatticeExprNode::newNumFunc1D (LELFunctionEnums::EXP, expr); } LatticeExprNode log(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function log" << endl; #endif return LatticeExprNode::newNumFunc1D (LELFunctionEnums::LOG, expr); } LatticeExprNode log10(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function log10" << endl; #endif return LatticeExprNode::newNumFunc1D (LELFunctionEnums::LOG10, expr); } LatticeExprNode sqrt(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function sqrt" << endl; #endif return LatticeExprNode::newNumFunc1D (LELFunctionEnums::SQRT, expr); } LatticeExprNode round(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function round" << endl; #endif return LatticeExprNode::newRealFunc1D (LELFunctionEnums::ROUND, expr); } LatticeExprNode sign(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function sign" << endl; #endif AlwaysAssert (expr.dataType()==TpFloat || expr.dataType()==TpDouble, AipsError); Block arg(1); arg[0] = expr.makeFloat(); return LatticeExprNode (std::make_shared(LELFunctionEnums::SIGN, arg)); } LatticeExprNode ceil(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function ceil" << endl; #endif return LatticeExprNode::newRealFunc1D (LELFunctionEnums::CEIL, expr); } LatticeExprNode floor(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function floor" << endl; #endif return LatticeExprNode::newRealFunc1D (LELFunctionEnums::FLOOR, expr); } LatticeExprNode min(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function min" << endl; #endif return LatticeExprNode::newNumFunc1D (LELFunctionEnums::MIN1D, expr); } LatticeExprNode max(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function max" << endl; #endif return LatticeExprNode::newNumFunc1D (LELFunctionEnums::MAX1D, expr); } LatticeExprNode abs(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function abs" << endl; #endif return LatticeExprNode::newNumReal1D (LELFunctionEnums::ABS, expr); } LatticeExprNode arg(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function arg" << endl; #endif AlwaysAssert (expr.dataType()==TpComplex || expr.dataType()==TpDComplex, AipsError); return LatticeExprNode::newNumReal1D (LELFunctionEnums::ARG, expr); } LatticeExprNode real(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function real" << endl; #endif return LatticeExprNode::newNumReal1D (LELFunctionEnums::REAL, expr); } LatticeExprNode imag(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function imag" << endl; #endif AlwaysAssert (expr.dataType()==TpComplex || expr.dataType()==TpDComplex, AipsError); return LatticeExprNode::newNumReal1D (LELFunctionEnums::IMAG, expr); } LatticeExprNode conj(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function conj" << endl; #endif return LatticeExprNode::newComplexFunc1D (LELFunctionEnums::CONJ, expr); } LatticeExprNode formComplex(const LatticeExprNode& left, const LatticeExprNode& right) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 2d function formComplex" << endl; #endif AlwaysAssert ((left.dataType()==TpFloat || left.dataType()==TpDouble) && (right.dataType()==TpFloat || right.dataType()==TpDouble), AipsError); Block arg(2); if (left.dataType()==TpFloat && right.dataType()==TpFloat) { arg[0] = left.makeFloat(); arg[1] = right.makeFloat(); return LatticeExprNode (std::make_shared(LELFunctionEnums::COMPLEX, arg)); } arg[0] = left.makeDouble(); arg[1] = right.makeDouble(); return LatticeExprNode (std::make_shared(LELFunctionEnums::COMPLEX, arg)); } LatticeExprNode sum(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function sum" << endl; #endif return LatticeExprNode::newNumFunc1D (LELFunctionEnums::SUM, expr); } LatticeExprNode median(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function median" << endl; #endif return LatticeExprNode::newRealFunc1D (LELFunctionEnums::MEDIAN1D, expr); } LatticeExprNode mean(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function mean" << endl; #endif return LatticeExprNode::newNumFunc1D (LELFunctionEnums::MEAN1D, expr); } LatticeExprNode variance(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function variance" << endl; #endif // Use high enough precision for Float and Complex. if (expr.dataType() == TpFloat) { return toFloat(sum(pow(expr - toDouble(mean(expr)), 2)) / max(1, nelements(expr)-1)); } else if (expr.dataType() == TpComplex) { return toComplex(sum(pow(expr - toDComplex(mean(expr)), 2)) / max(1, nelements(expr)-1)); } return sum(pow(expr - mean(expr), 2)) / max(1, nelements(expr)-1); } LatticeExprNode stddev(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function variance" << endl; #endif return sqrt(variance(expr)); } LatticeExprNode avdev(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function variance" << endl; #endif // Use high enough precision for Float and Complex. if (expr.dataType() == TpFloat) { return toFloat(sum(abs(expr - toDouble(mean(expr)))) / max(1, nelements(expr))); } else if (expr.dataType() == TpComplex) { return toComplex(sum(abs(expr - toDComplex(mean(expr)))) / max(1, nelements(expr))); } return sum(abs(expr - mean(expr))) / max(1, nelements(expr)); } LatticeExprNode fractile (const LatticeExprNode& expr, const LatticeExprNode& fraction) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 2d function fractile" << endl; #endif // // Create a new node for this real numerical function with 2 arguments. // The result has the same data type as the input. // DataType dtype = expr.dataType(); Block arg(2); arg[0] = expr; arg[1] = fraction.makeFloat(); switch (dtype) { case TpFloat: return LatticeExprNode (std::make_shared(LELFunctionEnums::FRACTILE1D, arg)); case TpDouble: return LatticeExprNode (std::make_shared(LELFunctionEnums::FRACTILE1D, arg)); default: throw (AipsError ("LatticeExprNode::fractile - " "Bool or complex argument used in real " "numerical function")); } return LatticeExprNode(); } LatticeExprNode fractileRange (const LatticeExprNode& expr, const LatticeExprNode& fraction) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 2d function fractileRange" << endl; #endif // // Create a new node for this real numerical function with 2 arguments. // The result has the same data type as the input. // DataType dtype = expr.dataType(); Block arg(2); arg[0] = expr; arg[1] = fraction.makeFloat(); switch (dtype) { case TpFloat: return LatticeExprNode (std::make_shared(LELFunctionEnums::FRACTILERANGE1D, arg)); case TpDouble: return LatticeExprNode (std::make_shared(LELFunctionEnums::FRACTILERANGE1D, arg)); default: throw (AipsError ("LatticeExprNode::fractileRange - " "Bool or complex argument used in real " "numerical function")); } return LatticeExprNode(); } LatticeExprNode fractileRange (const LatticeExprNode& expr, const LatticeExprNode& fraction1, const LatticeExprNode& fraction2) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 3d function fractileRange" << endl; #endif // // Create a new node for this real numerical function with 2 arguments. // The result has the same data type as the input. // DataType dtype = expr.dataType(); Block arg(3); arg[0] = expr; arg[1] = fraction1.makeFloat(); arg[2] = fraction2.makeFloat(); switch (dtype) { case TpFloat: return LatticeExprNode (std::make_shared(LELFunctionEnums::FRACTILERANGE1D, arg)); case TpDouble: return LatticeExprNode (std::make_shared(LELFunctionEnums::FRACTILERANGE1D, arg)); default: throw (AipsError ("LatticeExprNode::fractileRange - " "Bool or complex argument used in real " "numerical function")); } return LatticeExprNode(); } LatticeExprNode rebin (const LatticeExprNode& lat, const LatticeExprNode& bin) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 2d function rebin" << endl; #endif const IPosition& binning = bin.getIPosition(); const LELLattCoordBase* cbptr = &(lat.getAttribute().coordinates().coordinates()); const LELLattCoord* cptr = dynamic_cast(cbptr); AlwaysAssert (cptr != 0, AipsError); return cptr->makeRebinLattice (lat, binning); } LatticeExprNode atan2 (const LatticeExprNode& left, const LatticeExprNode& right) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 2d function atan2" << endl; #endif return LatticeExprNode::newNumFunc2D (LELFunctionEnums::ATAN2, left, right); } LatticeExprNode pow (const LatticeExprNode& left, const LatticeExprNode& right) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 2d function pow" << endl; #endif return LatticeExprNode::newNumFunc2D (LELFunctionEnums::POW, left, right); } LatticeExprNode fmod (const LatticeExprNode& left, const LatticeExprNode& right) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 2d function fmod" << endl; #endif return LatticeExprNode::newNumFunc2D (LELFunctionEnums::FMOD, left, right); } LatticeExprNode min (const LatticeExprNode& left, const LatticeExprNode& right) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 2d function min" << endl; #endif return LatticeExprNode::newNumFunc2D (LELFunctionEnums::MIN, left, right); } LatticeExprNode max (const LatticeExprNode& left, const LatticeExprNode& right) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 2d function max" << endl; #endif return LatticeExprNode::newNumFunc2D (LELFunctionEnums::MAX, left, right); } LatticeExprNode amp (const LatticeExprNode& left, const LatticeExprNode& right) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 2d function amp" << endl; #endif AlwaysAssert (left.dataType()!=TpBool && right.dataType()!=TpBool, AipsError); return sqrt(pow(left,2) + pow(right,2)); } LatticeExprNode pa (const LatticeExprNode& left, const LatticeExprNode& right) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 2d function pa" << endl; #endif AlwaysAssert (left.dataType()!=TpComplex && left.dataType()!=TpDComplex && left.dataType()!=TpBool, AipsError); AlwaysAssert (right.dataType()!=TpComplex && right.dataType()!=TpDComplex && right.dataType()!=TpBool, AipsError); LatticeExprNode expr(atan2(left,right)); switch (expr.dataType()) { case TpFloat: return Float(90.0/M_PI) * expr; break; case TpDouble: return Double(90.0/M_PI) * expr; break; default: throw (AipsError ("LatticeExprNode::pa - Unknown data type")); } return LatticeExprNode(); // shut compiler up } LatticeExprNode spectralindex (const LatticeExprNode& left, const LatticeExprNode& right) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 2d function spectralindex" << endl; #endif DataType dtype = LatticeExprNode::resultDataType (left.dataType(), right.dataType()); Block arg(2); switch (dtype) { case TpFloat: arg[0] = left.makeFloat(); arg[1] = right.makeFloat(); return LatticeExprNode (std::make_shared>(arg)); case TpDouble: arg[0] = left.makeDouble(); arg[1] = right.makeDouble(); return LatticeExprNode (std::make_shared>(arg)); default: throw (AipsError ("LatticeExprNode::spectralindex - " "Bool or Complex argument used in function")); } return LatticeExprNode(); } LatticeExprNode operator+ (const LatticeExprNode& left, const LatticeExprNode& right) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: binary operator +" << endl; #endif return LatticeExprNode::newNumBinary (LELBinaryEnums::ADD, left, right); } LatticeExprNode operator- (const LatticeExprNode& left, const LatticeExprNode& right) { #if defined(AIPS_TRACE) cout << "LatticeExprNode::binary operator -" << endl; #endif if (left.isRegion() && right.isRegion()) { return LELRegion::makeDifference (*left.pExprBool_p, *right.pExprBool_p); } return LatticeExprNode::newNumBinary (LELBinaryEnums::SUBTRACT, left, right); } LatticeExprNode operator* (const LatticeExprNode& left, const LatticeExprNode& right) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: binary operator *" << endl; #endif return LatticeExprNode::newNumBinary (LELBinaryEnums::MULTIPLY, left, right); } LatticeExprNode operator/ (const LatticeExprNode& left, const LatticeExprNode& right) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: binary operator /" << endl; #endif return LatticeExprNode::newNumBinary (LELBinaryEnums::DIVIDE, left, right); } LatticeExprNode operator== (const LatticeExprNode& left, const LatticeExprNode& right) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: binary operator ==" << endl; #endif return LatticeExprNode::newBinaryCmp (LELBinaryEnums::EQ, left, right); } LatticeExprNode operator> (const LatticeExprNode& left, const LatticeExprNode& right) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: binary operator >" << endl; #endif return LatticeExprNode::newBinaryCmp (LELBinaryEnums::GT, left, right); } LatticeExprNode operator>= (const LatticeExprNode& left, const LatticeExprNode& right) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: binary operator >=" << endl; #endif return LatticeExprNode::newBinaryCmp (LELBinaryEnums::GE, left, right); } LatticeExprNode operator< (const LatticeExprNode& left, const LatticeExprNode& right) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: binary operator <" << endl; #endif // Note we use GT for LT by reversing the order of the arguments // requiring less code in LELBinaryCmp return LatticeExprNode::newBinaryCmp (LELBinaryEnums::GT, right, left); } LatticeExprNode operator<= (const LatticeExprNode& left, const LatticeExprNode& right) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: binary operator <=" << endl; #endif // Note we use GE for LE by reversing the order of the arguments // requiring less code in LELBinaryCmp return LatticeExprNode::newBinaryCmp (LELBinaryEnums::GE, right, left); } LatticeExprNode operator!= (const LatticeExprNode& left, const LatticeExprNode& right) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: binary operator !=" << endl; #endif return LatticeExprNode::newBinaryCmp (LELBinaryEnums::NE, left, right); } LatticeExprNode operator&& (const LatticeExprNode& left, const LatticeExprNode& right) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: binary operator &&" << endl; #endif AlwaysAssert (left.dataType() == TpBool && right.dataType() == TpBool, AipsError); if (LatticeExprNode::areRegions (left, right)) { return LELRegion::makeIntersection (*left.pExprBool_p, *right.pExprBool_p); } return LatticeExprNode::newLogBinary (LELBinaryEnums::AND, left, right); } LatticeExprNode operator|| (const LatticeExprNode& left, const LatticeExprNode& right) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: binary operator ||" << endl; #endif AlwaysAssert (left.dataType() == TpBool && right.dataType() == TpBool, AipsError); if (LatticeExprNode::areRegions (left, right)) { return LELRegion::makeUnion (*left.pExprBool_p, *right.pExprBool_p); } return LatticeExprNode::newLogBinary (LELBinaryEnums::OR, left, right); } LatticeExprNode operator! (const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: unary operator !" << endl; #endif AlwaysAssert (expr.dataType() == TpBool, AipsError); if (expr.isRegion()) { return LELRegion::makeComplement (*expr.pExprBool_p); } return LatticeExprNode (std::make_shared(LELUnaryEnums::NOT, expr.pExprBool_p)); } LatticeExprNode LatticeExprNode::operator[] (const LatticeExprNode& cond) const { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: operator()" << endl; #endif AlwaysAssert (cond.dataType() == TpBool, AipsError); // The condition can be a region or a true boolean expression. // If a region, create a SubLattice/Image. if (cond.isRegion()) { // Cast the condition to a LELRegion object. // Thereafter let the coordinates class create a SubLattice/SubImage // for that region. It results in an exception if a WCRegion is // used without ImageCoordinates. const LELRegion& region = (const LELRegion&)(*cond.pExprBool_p); AlwaysAssert (!isRegion(), AipsError); const LELLattCoordBase* cbptr = &(getAttribute().coordinates().coordinates()); const LELLattCoord* cptr = dynamic_cast(cbptr); AlwaysAssert (cptr != 0, AipsError); return cptr->makeSubLattice (*this, region.region()); } switch (dataType()) { case TpBool: AlwaysAssert (!isRegion(), AipsError); return LatticeExprNode (std::make_shared>(pExprBool_p, cond.pExprBool_p)); case TpFloat: return LatticeExprNode (std::make_shared>(pExprFloat_p, cond.pExprBool_p)); case TpDouble: return LatticeExprNode (std::make_shared>(pExprDouble_p, cond.pExprBool_p)); case TpComplex: return LatticeExprNode (std::make_shared>(pExprComplex_p, cond.pExprBool_p)); case TpDComplex: return LatticeExprNode (std::make_shared>(pExprDComplex_p, cond.pExprBool_p)); default: throw (AipsError ("LatticeExprNode::operator[] - unknown datatype")); } return 0; } LatticeExprNode indexin (const LatticeExprNode& axis, const LatticeExprNode& indexFlags) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 2d function indexin" << endl; #endif Block arg(2); arg[0] = axis; arg[1] = indexFlags; return LatticeExprNode (std::make_shared(LELFunctionEnums::INDEXIN, arg)); } LatticeExprNode all (const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function all" << endl; #endif Block arg(1, toBool(expr)); return LatticeExprNode (std::make_shared(LELFunctionEnums::ALL, arg)); } LatticeExprNode any (const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function any" << endl; #endif Block arg(1, toBool(expr)); return LatticeExprNode (std::make_shared(LELFunctionEnums::ANY, arg)); } LatticeExprNode ntrue (const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function ntrue" << endl; #endif Block arg(1, toBool(expr)); return LatticeExprNode (std::make_shared(LELFunctionEnums::NTRUE, arg)); } LatticeExprNode nfalse (const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function nfalse" << endl; #endif Block arg(1, toBool(expr)); return LatticeExprNode (std::make_shared(LELFunctionEnums::NFALSE, arg)); } LatticeExprNode nelements(const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function nelements" << endl; #endif Block arg(1, expr); if (expr.isRegion()) { arg[0] = toBool (expr); } return LatticeExprNode (std::make_shared(LELFunctionEnums::NELEM, arg)); } LatticeExprNode ndim (const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function ndim" << endl; #endif Block arg(1, expr); return LatticeExprNode (std::make_shared(LELFunctionEnums::NDIM, arg)); } LatticeExprNode length (const LatticeExprNode& expr, const LatticeExprNode& axis) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 2d function length" << endl; #endif Block arg(2); arg[0] = expr; arg[1] = axis; return LatticeExprNode (std::make_shared(LELFunctionEnums::LENGTH, arg)); } LatticeExprNode isNaN (const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function isNaN" << endl; #endif Block arg(1, expr); return LatticeExprNode (std::make_shared(LELFunctionEnums::ISNAN, arg)); } LatticeExprNode mask (const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function mask" << endl; #endif Block arg(1, expr); if (expr.isRegion()) { arg[0] = toBool (expr); } return LatticeExprNode (std::make_shared(LELFunctionEnums::MASK, arg)); } LatticeExprNode value (const LatticeExprNode& expr) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: 1d function value" << endl; #endif if (expr.dataType() == TpBool) { Block arg(1, toBool(expr)); return LatticeExprNode (std::make_shared(LELFunctionEnums::VALUE, arg)); } return LatticeExprNode::newNumFunc1D (LELFunctionEnums::VALUE, expr); } LatticeExprNode iif (const LatticeExprNode& condition, const LatticeExprNode& arg1, const LatticeExprNode& arg2) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: function iif" << endl; #endif AlwaysAssert (condition.dataType() == TpBool, AipsError); DataType dtype = LatticeExprNode::resultDataType (arg1.dataType(), arg2.dataType()); Block arg(3); arg[0] = condition.makeBool(); switch (dtype) { case TpFloat: arg[1] = arg1.makeFloat(); arg[2] = arg2.makeFloat(); return LatticeExprNode (std::make_shared>(LELFunctionEnums::IIF, arg)); case TpDouble: arg[1] = arg1.makeDouble(); arg[2] = arg2.makeDouble(); return LatticeExprNode (std::make_shared>(LELFunctionEnums::IIF, arg)); case TpComplex: arg[1] = arg1.makeComplex(); arg[2] = arg2.makeComplex(); return LatticeExprNode (std::make_shared>(LELFunctionEnums::IIF, arg)); case TpDComplex: arg[1] = arg1.makeDComplex(); arg[2] = arg2.makeDComplex(); return LatticeExprNode (std::make_shared>(LELFunctionEnums::IIF, arg)); case TpBool: arg[1] = arg1.makeBool(); arg[2] = arg2.makeBool(); return LatticeExprNode (std::make_shared>(LELFunctionEnums::IIF, arg)); default: throw (AipsError ("LatticeExprNode::iif - unknown data type")); } return LatticeExprNode(); } LatticeExprNode replace (const LatticeExprNode& arg1, const LatticeExprNode& arg2) { #if defined(AIPS_TRACE) cout << "LatticeExprNode:: function replace" << endl; #endif DataType dtype = LatticeExprNode::resultDataType (arg1.dataType(), arg2.dataType()); Block arg(2); switch (dtype) { case TpFloat: arg[0] = arg1.makeFloat(); arg[1] = arg2.makeFloat(); return LatticeExprNode (std::make_shared>(LELFunctionEnums::REPLACE, arg)); case TpDouble: arg[0] = arg1.makeDouble(); arg[1] = arg2.makeDouble(); return LatticeExprNode (std::make_shared>(LELFunctionEnums::REPLACE, arg)); case TpComplex: arg[0] = arg1.makeComplex(); arg[1] = arg2.makeComplex(); return LatticeExprNode (std::make_shared>(LELFunctionEnums::REPLACE, arg)); case TpDComplex: arg[0] = arg1.makeDComplex(); arg[1] = arg2.makeDComplex(); return LatticeExprNode (std::make_shared>(LELFunctionEnums::REPLACE, arg)); case TpBool: arg[0] = arg1.makeBool(); arg[1] = arg2.makeBool(); return LatticeExprNode (std::make_shared>(LELFunctionEnums::REPLACE, arg)); default: throw (AipsError ("LatticeExprNode::replace - unknown data type")); } return LatticeExprNode(); } Bool LatticeExprNode::areRegions (const LatticeExprNode& left, const LatticeExprNode& right) { return (left.isRegion() && right.isRegion()); } LatticeExprNode LatticeExprNode::newNumUnary (LELUnaryEnums::Operation oper, const LatticeExprNode& expr) // // Create a new node for a numerical unary operation. // The result has the same data type as the input. // { switch (expr.dataType()) { case TpFloat: return LatticeExprNode (std::make_shared>(oper, expr.pExprFloat_p)); case TpDouble: return LatticeExprNode (std::make_shared>(oper, expr.pExprDouble_p)); case TpComplex: return LatticeExprNode (std::make_shared>(oper, expr.pExprComplex_p)); case TpDComplex: return LatticeExprNode (std::make_shared>(oper, expr.pExprDComplex_p)); default: throw (AipsError ("LatticeExprNode::newNumUnary - " "Bool argument used in numerical unary operation")); } return LatticeExprNode(); } LatticeExprNode LatticeExprNode::newNumFunc1D (LELFunctionEnums::Function func, const LatticeExprNode& expr) // // Create a new node for a numerical function with 1 argument. // The result has the same data type as the input. // { switch (expr.dataType()) { case TpFloat: return LatticeExprNode (std::make_shared>(func, expr.pExprFloat_p)); case TpDouble: return LatticeExprNode (std::make_shared>(func, expr.pExprDouble_p)); case TpComplex: return LatticeExprNode (std::make_shared>(func, expr.pExprComplex_p)); case TpDComplex: return LatticeExprNode (std::make_shared>(func, expr.pExprDComplex_p)); default: throw (AipsError ("LatticeExprNode::newNumFunc1D - " "Bool argument used in numerical function")); } return LatticeExprNode(); } LatticeExprNode LatticeExprNode::newRealFunc1D (LELFunctionEnums::Function func, const LatticeExprNode& expr) { // Create a new node for a real numerical function with 1 argument. // The result has the same data type as the input. switch (expr.dataType()) { case TpFloat: return LatticeExprNode (std::make_shared>(func, expr.pExprFloat_p)); case TpDouble: return LatticeExprNode (std::make_shared>(func, expr.pExprDouble_p)); default: throw (AipsError ("LatticeExprNode::newRealFunc1D - " "Bool or complex argument used in real " "numerical function")); } return LatticeExprNode(); } LatticeExprNode LatticeExprNode::newComplexFunc1D (LELFunctionEnums::Function func, const LatticeExprNode& expr) { // Create a new node for a complex numerical function with 1 // argument. The result has the same data type as the input. Block arg(1); arg[0] = expr; switch (expr.dataType()) { case TpComplex: return LatticeExprNode (std::make_shared(func, arg)); case TpDComplex: return LatticeExprNode (std::make_shared(func, arg)); default: throw (AipsError ("LatticeExprNode::newComplexFunc1D - " "only complex arguments allowed")); } return LatticeExprNode(); } LatticeExprNode LatticeExprNode::newNumReal1D (LELFunctionEnums::Function func, const LatticeExprNode& expr) // // Create a new node for a numerical function with 1 arguments that // returns a real number // { DataType dtype = expr.dataType(); Block arg(1); arg[0] = expr; switch (dtype) { case TpFloat: case TpComplex: return LatticeExprNode (std::make_shared(func, arg)); case TpDouble: case TpDComplex: return LatticeExprNode (std::make_shared(func, arg)); default: throw (AipsError ("LatticeExprNode::newNumReal1D - " "output type must be real and numeric")); } return LatticeExprNode(); } LatticeExprNode LatticeExprNode::newNumFunc2D (LELFunctionEnums::Function func, const LatticeExprNode& left, const LatticeExprNode& right) // // Create a new node for a numerical function with 2 arguments. // The result has the same data type as the combined input type. // { DataType dtype = resultDataType (left.dataType(), right.dataType()); Block arg(2); switch (dtype) { case TpFloat: arg[0] = left.makeFloat(); arg[1] = right.makeFloat(); return LatticeExprNode (std::make_shared(func, arg)); case TpDouble: arg[0] = left.makeDouble(); arg[1] = right.makeDouble(); return LatticeExprNode (std::make_shared(func, arg)); case TpComplex: arg[0] = left.makeComplex(); arg[1] = right.makeComplex(); return LatticeExprNode (std::make_shared(func, arg)); case TpDComplex: arg[0] = left.makeDComplex(); arg[1] = right.makeDComplex(); return LatticeExprNode (std::make_shared(func, arg)); default: throw (AipsError ("LatticeExprNode::newNumFunc2D - " "Bool argument used in numerical function")); } return LatticeExprNode(); } LatticeExprNode LatticeExprNode::newNumBinary (LELBinaryEnums::Operation oper, const LatticeExprNode& left, const LatticeExprNode& right) // // Create a new node for a numerical binary operator. // The result has the same data type as the combined input type. // { DataType dtype = resultDataType (left.dataType(), right.dataType()); LatticeExprNode expr0; LatticeExprNode expr1; switch (dtype) { case TpFloat: expr0 = left.makeFloat(); expr1 = right.makeFloat(); break; case TpDouble: expr0 = left.makeDouble(); expr1 = right.makeDouble(); break; case TpComplex: expr0 = left.makeComplex(); expr1 = right.makeComplex(); break; case TpDComplex: expr0 = left.makeDComplex(); expr1 = right.makeDComplex(); break; default: throw (AipsError ("LatticeExprNode::newNumBinary - " "Bool argument used in numerical binary operation")); } // Make the operands the same dimensionality (if needed and possible). makeEqualDim (expr0, expr1); switch (dtype) { case TpFloat: return LatticeExprNode (std::make_shared>(oper, expr0.pExprFloat_p, expr1.pExprFloat_p)); case TpDouble: return LatticeExprNode (std::make_shared>(oper, expr0.pExprDouble_p, expr1.pExprDouble_p)); case TpComplex: return LatticeExprNode (std::make_shared>(oper, expr0.pExprComplex_p, expr1.pExprComplex_p)); default: return LatticeExprNode (std::make_shared>(oper, expr0.pExprDComplex_p, expr1.pExprDComplex_p)); } return LatticeExprNode(); } LatticeExprNode LatticeExprNode::newLogBinary (LELBinaryEnums::Operation oper, const LatticeExprNode& left, const LatticeExprNode& right) // // Create a new node for a logical binary operator. // The result has the same data type as the combined input type. // { DataType dtype = resultDataType (left.dataType(), right.dataType()); LatticeExprNode expr0; LatticeExprNode expr1; switch (dtype) { case TpBool: expr0 = left.makeBool(); expr1 = right.makeBool(); break; default: throw (AipsError ("LatticeExprNode::newLogBinary - " "Non-Bool argument used in logical binary operation")); } // Make the operands the same dimensionality (if needed and possible). makeEqualDim (expr0, expr1); return LatticeExprNode (std::make_shared(oper, expr0.pExprBool_p, expr1.pExprBool_p)); } LatticeExprNode LatticeExprNode::newBinaryCmp (LELBinaryEnums::Operation oper, const LatticeExprNode& left, const LatticeExprNode& right) // // Create a new node for a comparison binary operator. // The result has the same data type as the combined input type. // { DataType dtype = resultDataType (left.dataType(), right.dataType()); LatticeExprNode expr0; LatticeExprNode expr1; switch (dtype) { case TpFloat: expr0 = left.makeFloat(); expr1 = right.makeFloat(); break; case TpDouble: expr0 = left.makeDouble(); expr1 = right.makeDouble(); break; case TpComplex: expr0 = left.makeComplex(); expr1 = right.makeComplex(); break; case TpDComplex: expr0 = left.makeDComplex(); expr1 = right.makeDComplex(); break; case TpBool: if (oper != LELBinaryEnums::EQ && oper != LELBinaryEnums::NE) { throw (AipsError ("LatticeExprNode::newBinaryCmp - " "Bool data type cannot be used with " ">, >=, <, and <= operator")); } expr0 = left.makeBool(); expr1 = right.makeBool(); break; default: throw (AipsError ("LatticeExprNode::newBinaryCmp - " "invalid data type used in comparison")); } // Make the operands the same dimensionality (if needed and possible). makeEqualDim (expr0, expr1); switch (dtype) { case TpFloat: return LatticeExprNode (std::make_shared>(oper, expr0.pExprFloat_p, expr1.pExprFloat_p)); case TpDouble: return LatticeExprNode (std::make_shared>(oper, expr0.pExprDouble_p, expr1.pExprDouble_p)); case TpComplex: return LatticeExprNode (std::make_shared>(oper, expr0.pExprComplex_p, expr1.pExprComplex_p)); case TpDComplex: return LatticeExprNode (std::make_shared>(oper, expr0.pExprDComplex_p, expr1.pExprDComplex_p)); default: return LatticeExprNode (std::make_shared(oper, expr0.pExprBool_p, expr1.pExprBool_p)); } return LatticeExprNode(); } DataType LatticeExprNode::resultDataType (DataType left, DataType right) // // Work out the resultant data type when two expressions are combined // Favours the higher precision // { if (left == right) { return left; } if (left == TpBool || right == TpBool) { throw (AipsError ("LatticeExprNode::resultDataType - " "Bool and numeric operands cannot mixed")); } if (left == TpDComplex || right == TpDComplex) { return TpDComplex; } if (left == TpComplex || right == TpComplex) { if (left == TpDouble || right == TpDouble) { return TpDComplex; } return TpComplex; } if (left == TpDouble || right == TpDouble) { return TpDouble; } return TpFloat; } LELAttribute LatticeExprNode::checkArg (const Block& arg, const Block& argType, Bool expectArray, Bool matchAxes) { if (arg.nelements() != argType.nelements()) { throw (AipsError ("LatticeExprNode::checkArg - " "invalid number of function arguments")); } // Compose the resulting LELAttribute from all arguments. // Each time it is checked if shapes and coordinates conform. LELAttribute attr; for (uInt i=0; i> LatticeExprNode::makeFloat() const { switch (dataType()) { case TpFloat: return pExprFloat_p; case TpDouble: return std::make_shared>(pExprDouble_p); default: throw (AipsError ("LatticeExprNode::makeFloat - " "conversion to Float not possible")); } } std::shared_ptr> LatticeExprNode::makeDouble() const { switch (dataType()) { case TpFloat: return std::make_shared>(pExprFloat_p); case TpDouble: return pExprDouble_p; default: throw (AipsError ("LatticeExprNode::makeDouble - " "conversion to Double not possible")); } } std::shared_ptr> LatticeExprNode::makeComplex() const { switch (dataType()) { case TpFloat: return std::make_shared>(pExprFloat_p); case TpDouble: return std::make_shared>(pExprDouble_p); case TpComplex: return pExprComplex_p; case TpDComplex: return std::make_shared>(pExprDComplex_p); default: throw (AipsError ("LatticeExprNode::makeComplex - " "conversion to Complex not possible")); } } std::shared_ptr> LatticeExprNode::makeDComplex() const { switch (dataType()) { case TpFloat: return std::make_shared>(pExprFloat_p); case TpDouble: return std::make_shared>(pExprDouble_p); case TpComplex: return std::make_shared>(pExprComplex_p); case TpDComplex: return pExprDComplex_p; default: throw (AipsError ("LatticeExprNode::makeDComplex - " "conversion to DComplex not possible")); } } std::shared_ptr> LatticeExprNode::makeBool() const { if (dataType() != TpBool) { throw (AipsError ("LatticeExprNode::makeBool - " "conversion to Bool not possible")); } if (isRegion()) { return std::make_shared(dynamic_cast(*pExprBool_p)); } return pExprBool_p; } Int LatticeExprNode::makeEqualDim (LatticeExprNode& expr0, LatticeExprNode& expr1) { // Compare the coordinates (and shapes). const LELAttribute& attr0 = expr0.getAttribute(); const LELAttribute& attr1 = expr1.getAttribute(); Int result = attr0.compareCoord (attr1); if (result == -1) { // left is subset of right, so extend left. const LELLattCoordBase* cbptr = &(attr0.coordinates().coordinates()); const LELLattCoord* cptr = dynamic_cast(cbptr); AlwaysAssert (cptr != 0, AipsError); expr0 = cptr->makeExtendLattice (expr0, attr1.shape(), attr1.coordinates().coordinates()); } else if (result == 1) { // right is subset of left, so extend right. const LELLattCoordBase* cbptr = &(attr1.coordinates().coordinates()); const LELLattCoord* cptr = dynamic_cast(cbptr); AlwaysAssert (cptr != 0, AipsError); expr1 = cptr->makeExtendLattice (expr1, attr0.shape(), attr0.coordinates().coordinates()); } else if (result == 9) { throw AipsError ("LatticeExprNode - coordinates of operands mismatch"); } else if (result != 0) { throw AipsError ("LatticeExprNode - shapes of operands mismatch"); } return result; } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LEL/LatticeExprNode.h000066400000000000000000001100141476623553700207500ustar00rootroot00000000000000//# LatticeExprNode.h: LatticeExprNode.h //# Copyright (C) 1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICEEXPRNODE_H #define LATTICES_LATTICEEXPRNODE_H //# Includes #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class LatticeExpr; template class Lattice; template class MaskedLattice; template class Block; class LCRegion; class Slicer; class LattRegionHolder; class LatticeExprNode; // Global functions operating on a LatticeExprNode. // // Unary functions. // LatticeExprNode operator+ (const LatticeExprNode& expr); LatticeExprNode operator- (const LatticeExprNode& expr); LatticeExprNode operator! (const LatticeExprNode& expr); // // Numerical binary operators // LatticeExprNode operator+ (const LatticeExprNode& left, const LatticeExprNode& right); LatticeExprNode operator- (const LatticeExprNode& left, const LatticeExprNode& right); LatticeExprNode operator* (const LatticeExprNode& left, const LatticeExprNode& right); LatticeExprNode operator/ (const LatticeExprNode& left, const LatticeExprNode& right); LatticeExprNode operator% (const LatticeExprNode& left, const LatticeExprNode& right); LatticeExprNode operator^ (const LatticeExprNode& left, const LatticeExprNode& right); // // Relational binary operators // LatticeExprNode operator== (const LatticeExprNode& left, const LatticeExprNode& right); LatticeExprNode operator> (const LatticeExprNode& left, const LatticeExprNode& right); LatticeExprNode operator>= (const LatticeExprNode& left, const LatticeExprNode& right); LatticeExprNode operator< (const LatticeExprNode& left, const LatticeExprNode& right); LatticeExprNode operator<= (const LatticeExprNode& left, const LatticeExprNode& right); LatticeExprNode operator!= (const LatticeExprNode& left, const LatticeExprNode& right); // // Logical binary operators // LatticeExprNode operator&& (const LatticeExprNode& left, const LatticeExprNode& right); LatticeExprNode operator|| (const LatticeExprNode& left, const LatticeExprNode& right); // // Numerical 1-argument functions // LatticeExprNode sin (const LatticeExprNode& expr); LatticeExprNode sinh (const LatticeExprNode& expr); LatticeExprNode asin (const LatticeExprNode& expr); LatticeExprNode cos (const LatticeExprNode& expr); LatticeExprNode cosh (const LatticeExprNode& expr); LatticeExprNode acos (const LatticeExprNode& expr); LatticeExprNode tan (const LatticeExprNode& expr); LatticeExprNode tanh (const LatticeExprNode& expr); LatticeExprNode atan (const LatticeExprNode& expr); LatticeExprNode exp (const LatticeExprNode& expr); LatticeExprNode log (const LatticeExprNode& expr); LatticeExprNode log10(const LatticeExprNode& expr); LatticeExprNode sqrt (const LatticeExprNode& expr); LatticeExprNode sign (const LatticeExprNode& expr); LatticeExprNode round(const LatticeExprNode& expr); LatticeExprNode ceil (const LatticeExprNode& expr); LatticeExprNode floor(const LatticeExprNode& expr); LatticeExprNode conj (const LatticeExprNode& expr); // // Numerical 2-argument functions // LatticeExprNode atan2 (const LatticeExprNode& left, const LatticeExprNode& right); LatticeExprNode pow (const LatticeExprNode& left, const LatticeExprNode& right); LatticeExprNode fmod (const LatticeExprNode& left, const LatticeExprNode& right); LatticeExprNode min (const LatticeExprNode& left, const LatticeExprNode& right); LatticeExprNode max (const LatticeExprNode& left, const LatticeExprNode& right); // // Form a complex number from two real numbers. LatticeExprNode formComplex (const LatticeExprNode& left, const LatticeExprNode& right); // Numerical 1-argument functions which result in a real number // regardless of input expression type // LatticeExprNode abs (const LatticeExprNode& expr); LatticeExprNode arg (const LatticeExprNode& expr); LatticeExprNode real (const LatticeExprNode& expr); LatticeExprNode imag (const LatticeExprNode& expr); // // 1-argument functions operating on a numeric expression resulting // in a scalar // LatticeExprNode min (const LatticeExprNode& expr); LatticeExprNode max (const LatticeExprNode& expr); LatticeExprNode sum (const LatticeExprNode& expr); LatticeExprNode median (const LatticeExprNode& expr); LatticeExprNode mean (const LatticeExprNode& expr); LatticeExprNode variance (const LatticeExprNode& expr); LatticeExprNode stddev (const LatticeExprNode& expr); LatticeExprNode avdev (const LatticeExprNode& expr); // // Determine the value of the element at the part fraction // from the beginning of the given lattice. // Thus fraction=0.5 is equal to the median. LatticeExprNode fractile (const LatticeExprNode& expr, const LatticeExprNode& fraction); // Determine the value range of the elements at the part fraction1 // and fraction2 from the beginning of the given lattice. Both fractions // must be >=0 and <=1 and fraction1 must be <= fraction2. // By default fraction2 is equal to 1-fraction1. // Thus fraction=0.25 gives the quartile range of the lattice. // LatticeExprNode fractileRange (const LatticeExprNode& expr, const LatticeExprNode& fraction1, const LatticeExprNode& fraction2); LatticeExprNode fractileRange (const LatticeExprNode& expr, const LatticeExprNode& fraction); // // 1-argument function to get the number of elements in a lattice. // If the lattice is masked, only the True elements are counted. // Results in a scalar Double. LatticeExprNode nelements (const LatticeExprNode& expr); // 1-argument function to get the dimensionality of a lattice. // 0 is returned if it is a scalar. // Results in a scalar Float. LatticeExprNode ndim (const LatticeExprNode& expr); // 2-argument function to get the length of an axis. // Results in a scalar Float. // The 2nd expression (giving the axis number) has to be a real scalar. // // Axes start counting at 0. // If the axis is a number < 0, an exception is thrown. // If the axis is a number exceeding the dimensionality, 1 is returned. // LatticeExprNode length (const LatticeExprNode& expr, const LatticeExprNode& axis); // 2-argument function telling per pixel if its index on the given axis // is contained in the 2nd argument. The 2nd argument should be a boolean // vector where True means that the index is contained. // For indices >= vector_length, the 2nd argument defaults to False. // Results in a Bool array. // // Axes start counting at 0. // If the axis is a number < 0 or >= ndim, an exception is thrown. // LatticeExprNode indexin (const LatticeExprNode& axis, const LatticeExprNode& indexFlags); // 2-argument function rebinning Lattice by given factors. The 2nd argument // should be a vector (preferably Float - really Int but Int not well // supported in LEL yet). Results in a T array. LatticeExprNode rebin (const LatticeExprNode& expr, const LatticeExprNode& bin); // Test if a value is a NaN. LatticeExprNode isNaN (const LatticeExprNode& expr); // Functions operating on a logical expression resulting in a scalar; // Functions "any" (are any pixels "True") and "all" (are all pixels // "True") result in a Bool; functions "ntrue" and "nfalse" result // in a Double. // LatticeExprNode any (const LatticeExprNode& expr); LatticeExprNode all (const LatticeExprNode& expr); LatticeExprNode ntrue (const LatticeExprNode& expr); LatticeExprNode nfalse(const LatticeExprNode& expr); // // This function returns the mask of the given expression. // If it has no mask, the result is an array with all True values. LatticeExprNode mask (const LatticeExprNode& expr); // This function returns the value of the expression without a mask. LatticeExprNode value (const LatticeExprNode& expr); // This function finds sqrt(left^2+right^2). This // could be used to find the (biased) polarized intensity if // left and right are images of Stokes Q and U. LatticeExprNode amp (const LatticeExprNode& left, const LatticeExprNode& right); // This function finds 180/pi*atan2(left,right)/2. This could be // used to find the position of linear polarization if left // and right are images of Stokes U and Q, respectively. LatticeExprNode pa (const LatticeExprNode& left, const LatticeExprNode& right); // This function finds the spectral index // alpha = log(s1/s2) / log(f1/f2). LatticeExprNode spectralindex (const LatticeExprNode& left, const LatticeExprNode& right); // Function resembling the ternary ?: construct in C++. // The argument "condition" has to be a Bool scalar or lattice. // If an element in "condition" is True, the corresponding element from // "arg1" is taken, otherwise it is taken from "arg2". LatticeExprNode iif (const LatticeExprNode& condition, const LatticeExprNode& arg1, const LatticeExprNode& arg2); // This function replaces every masked-off element in the first argument // with the corresponding element from the second argument. // The first argument has to be a lattice (expression), the second can // be a scalar or lattice. The mask of the first argument is not changed. // If the first argument does not have a mask, this function does nothing. LatticeExprNode replace (const LatticeExprNode& arg1, const LatticeExprNode& arg2); // Functions to convert to the given data type. These are mostly // meaningful for down-conversions (e.g. double to float), // since up-conversions are automatically done to get matching data types // when needed. Note that some conversions are not supported, such // as Complex to Double or Float. //
        The conversion to Bool is useful to convert a region to a // boolean lattice, which is only possible if the region is given // in world coordinates. Otherwise an exception is thrown. // LatticeExprNode toFloat (const LatticeExprNode& expr); LatticeExprNode toDouble (const LatticeExprNode& expr); LatticeExprNode toComplex (const LatticeExprNode& expr); LatticeExprNode toDComplex(const LatticeExprNode& expr); LatticeExprNode toBool (const LatticeExprNode& expr); LatticeExprNode convertType (const LatticeExprNode& expr, const Float*); LatticeExprNode convertType (const LatticeExprNode& expr, const Double*); LatticeExprNode convertType (const LatticeExprNode& expr, const Complex*); LatticeExprNode convertType (const LatticeExprNode& expr, const DComplex*); LatticeExprNode convertType (const LatticeExprNode& expr, const Bool*); // //
        // // Bridging class to allow C++ expressions involving lattices // // // // // // // // //
      • Lattice //
      • LatticeExpr //
      • LELInterface // // // // The name is derived from the fact that this class provides // an expression interface to the user which s/he may use to // write C++ expressions involving Lattices. This class actually // constructs the nodes of the expression tree, hence its name. // It is used by the envelope class LatticeExpr and provides a // bridge to the letter classes derived from LELInterface. // // // // This class is part of the interface which allows the C++ programmer // to enter mathematical expressions involving Lattices. It is // is part of a Letter/envelope scheme. It's actually a bridge // between the envelope class (LatticeExpr) and the letter classes // (derived from LELInterface) and it exists largely to handle // type conversions. In a single type environment, the envelope // class could have directly called the letter classes. // // The envelope and bridge provide the interface which the programmer // sees. The letter classes do the real work and are hidden from // the programmer. // // All the expression manipulation functionality that the user has // access to is viewable in this class; it is here that the operators, // functions and constructors are defined. These allow the programmer // to write mathematical expressions which involve Lattices. The // letter classes take care of the optimal traversal of the Lattice // and the memory mangement thereof. Thus the Lattices are iterated // through and the expressions evaluated for each chunk (usually // a tile shape) of the iteration. // // A description of the implementation details of these classes can // be found in // Note 216 // // The available functionality is defined by the global friend functions // and operators, plus the public constructors. The other public members // functions are generally not of interest to the user of this class. // // Generally, if one writes an expression such as a.copyData(sin(b)), // the expression is automatically converted first to a LatticeExprNode and // then to a LatticeExpr (which is a Lattice) before evaluation occurs. // However, it may occur that you wish to build an expression from // subexpressions. To do this, you must explcitly create objects of // class LatticeExprNode. You cannot manipulate subexpressions of type // LatticeExpr. See below for an example. // // // // // ArrayLattice f1(IPosition (2,nx,ny)); // ArrayLattice f2(IPosition (2,nx,ny)); // f2.set(2.0); // f1.copyData(2*f2+f2); // // In this example, the values of the pixels in Lattice f1 are set // to the values resulting from the expression "2*f2 + f2" // I.e. the expression is evaluated for each pixel in the Lattices // // Note that : // // 1) the Lattice::copyData function is expecting a Lattice argument. // 2) LatticeExpr inherits from Lattice and therefore a LatticeExpr // object is a valid argument object type // 3) The expression in the copyData call is automatically converted to // a LatticeExprNode by the constructors and operators in LatticeExprNode // 4) The LatticeExprNode object so created is automatically converted // to a LatticeExpr by casting functions in LatticeExprNode. // // // // // // ArrayLattice f1(IPosition (2,nx,ny)); // ArrayLattice f2(IPosition (2,nx,ny)); // ArrayLattice d(IPosition (2,nx,ny)); // ArrayLattice c(IPosition (2,nx,ny)); // ArrayLattice b(IPosition (2,nx,ny)); // // f2.set(1.0); d.set(2.0); c.set(Complex(2.0,3.0)); b.set(True); // f1.copyData( (3.5*f2) + (cos(d)) - (10/min(d,f2)*(-abs(c))*ntrue(b)) - (C::pi) ); // // // In this rather silly example, we fill Lattice "f1" with the result of the // expression. The expression shows the use of constants, unary operations, // binary operations, 1D and 2D functions. It also shows how mixed types can // be handled. The output Lattice is a Float, whereas mixed into the // expression are subexpressions involving Float, Double, Complex and Bool // Lattices. // // // // // // ArrayLattice f1(IPosition (2,nx,ny)); // ArrayLattice f2(IPosition (2,nx,ny)); // f2.set(2.0); // LatticeExprNode exp1(sin(f2)); // LatticeExprNode exp2(pow(f2,2.0)); // f1.copyData(exp1+exp2); // // In this example, the expression is "sin(f2) + pow(f2,2.0)", // but we have put it together from two subexpressions contained // in LatticeExprNode objects exp1 and exp2. Again the LatticeExprNode // object formed from summing exp1 and exp2 is automatically converted // to a LatticeExpr for consumption by copyData // // // // // The Lattice expression classes enable the C++ programmer much simpler // handling of mathematical expressions involving lattices. In addition, // these classes provide the infrastructure on top of which we can build // an image calculator for Glish users // // // //
      • masks //
      • regions // class LatticeExprNode { // All global functions need to be declared as friends. // friend LatticeExprNode operator+ (const LatticeExprNode& expr); friend LatticeExprNode operator- (const LatticeExprNode& expr); friend LatticeExprNode operator! (const LatticeExprNode& expr); friend LatticeExprNode operator+ (const LatticeExprNode& left, const LatticeExprNode& right); friend LatticeExprNode operator- (const LatticeExprNode& left, const LatticeExprNode& right); friend LatticeExprNode operator* (const LatticeExprNode& left, const LatticeExprNode& right); friend LatticeExprNode operator/ (const LatticeExprNode& left, const LatticeExprNode& right); friend LatticeExprNode operator% (const LatticeExprNode& left, const LatticeExprNode& right); friend LatticeExprNode operator^ (const LatticeExprNode& left, const LatticeExprNode& right); friend LatticeExprNode operator== (const LatticeExprNode& left, const LatticeExprNode& right); friend LatticeExprNode operator> (const LatticeExprNode& left, const LatticeExprNode& right); friend LatticeExprNode operator>= (const LatticeExprNode& left, const LatticeExprNode& right); friend LatticeExprNode operator< (const LatticeExprNode& left, const LatticeExprNode& right); friend LatticeExprNode operator<= (const LatticeExprNode& left, const LatticeExprNode& right); friend LatticeExprNode operator!= (const LatticeExprNode& left, const LatticeExprNode& right); friend LatticeExprNode operator&& (const LatticeExprNode& left, const LatticeExprNode& right); friend LatticeExprNode operator|| (const LatticeExprNode& left, const LatticeExprNode& right); friend LatticeExprNode sin (const LatticeExprNode& expr); friend LatticeExprNode sinh (const LatticeExprNode& expr); friend LatticeExprNode asin (const LatticeExprNode& expr); friend LatticeExprNode cos (const LatticeExprNode& expr); friend LatticeExprNode cosh (const LatticeExprNode& expr); friend LatticeExprNode acos (const LatticeExprNode& expr); friend LatticeExprNode tan (const LatticeExprNode& expr); friend LatticeExprNode tanh (const LatticeExprNode& expr); friend LatticeExprNode atan (const LatticeExprNode& expr); friend LatticeExprNode exp (const LatticeExprNode& expr); friend LatticeExprNode log (const LatticeExprNode& expr); friend LatticeExprNode log10(const LatticeExprNode& expr); friend LatticeExprNode sqrt (const LatticeExprNode& expr); friend LatticeExprNode sign (const LatticeExprNode& expr); friend LatticeExprNode round(const LatticeExprNode& expr); friend LatticeExprNode ceil (const LatticeExprNode& expr); friend LatticeExprNode floor(const LatticeExprNode& expr); friend LatticeExprNode conj (const LatticeExprNode& expr); friend LatticeExprNode atan2 (const LatticeExprNode& left, const LatticeExprNode& right); friend LatticeExprNode pow (const LatticeExprNode& left, const LatticeExprNode& right); friend LatticeExprNode fmod (const LatticeExprNode& left, const LatticeExprNode& right); friend LatticeExprNode min (const LatticeExprNode& left, const LatticeExprNode& right); friend LatticeExprNode max (const LatticeExprNode& left, const LatticeExprNode& right); friend LatticeExprNode formComplex (const LatticeExprNode& left, const LatticeExprNode& right); friend LatticeExprNode abs (const LatticeExprNode& expr); friend LatticeExprNode arg (const LatticeExprNode& expr); friend LatticeExprNode real (const LatticeExprNode& expr); friend LatticeExprNode imag (const LatticeExprNode& expr); friend LatticeExprNode min (const LatticeExprNode& expr); friend LatticeExprNode max (const LatticeExprNode& expr); friend LatticeExprNode sum (const LatticeExprNode& expr); friend LatticeExprNode median (const LatticeExprNode& expr); friend LatticeExprNode mean (const LatticeExprNode& expr); friend LatticeExprNode variance (const LatticeExprNode& expr); friend LatticeExprNode stddev (const LatticeExprNode& expr); friend LatticeExprNode avdev (const LatticeExprNode& expr); friend LatticeExprNode fractile (const LatticeExprNode& expr, const LatticeExprNode& fraction); friend LatticeExprNode fractileRange (const LatticeExprNode& expr, const LatticeExprNode& fraction1, const LatticeExprNode& fraction2); friend LatticeExprNode fractileRange (const LatticeExprNode& expr, const LatticeExprNode& fraction); friend LatticeExprNode nelements (const LatticeExprNode& expr); friend LatticeExprNode ndim (const LatticeExprNode& expr); friend LatticeExprNode length (const LatticeExprNode& expr, const LatticeExprNode& axis); friend LatticeExprNode indexin (const LatticeExprNode& axis, const LatticeExprNode& indexFlags); friend LatticeExprNode rebin (const LatticeExprNode& expr, const LatticeExprNode& bin); friend LatticeExprNode isNaN (const LatticeExprNode& expr); friend LatticeExprNode any (const LatticeExprNode& expr); friend LatticeExprNode all (const LatticeExprNode& expr); friend LatticeExprNode ntrue (const LatticeExprNode& expr); friend LatticeExprNode nfalse(const LatticeExprNode& expr); friend LatticeExprNode mask (const LatticeExprNode& expr); friend LatticeExprNode value (const LatticeExprNode& expr); friend LatticeExprNode amp (const LatticeExprNode& left, const LatticeExprNode& right); friend LatticeExprNode pa (const LatticeExprNode& left, const LatticeExprNode& right); friend LatticeExprNode spectralindex (const LatticeExprNode& left, const LatticeExprNode& right); friend LatticeExprNode iif (const LatticeExprNode& condition, const LatticeExprNode& arg1, const LatticeExprNode& arg2); friend LatticeExprNode replace (const LatticeExprNode& arg1, const LatticeExprNode& arg2); friend LatticeExprNode toFloat (const LatticeExprNode& expr); friend LatticeExprNode toDouble (const LatticeExprNode& expr); friend LatticeExprNode toComplex (const LatticeExprNode& expr); friend LatticeExprNode toDComplex(const LatticeExprNode& expr); friend LatticeExprNode toBool (const LatticeExprNode& expr); // public: // Default constructor LatticeExprNode(); // Unary constant expression constructors. // LatticeExprNode (Int64 constant); LatticeExprNode (Int constant); LatticeExprNode (uInt constant); LatticeExprNode (Long constant); LatticeExprNode (Float constant); LatticeExprNode (Double constant); LatticeExprNode (const Complex& constant); LatticeExprNode (const DComplex& constant); LatticeExprNode (Bool constant); // // Constructor from an IPosition (containing indices or axes). LatticeExprNode (const IPosition&); // Lattice expression (gets Lattice pixels) constructors. // LatticeExprNode (const Lattice& lattice); LatticeExprNode (const Lattice& lattice); LatticeExprNode (const Lattice& lattice); LatticeExprNode (const Lattice& lattice); LatticeExprNode (const Lattice& lattice); LatticeExprNode (const MaskedLattice& lattice); LatticeExprNode (const MaskedLattice& lattice); LatticeExprNode (const MaskedLattice& lattice); LatticeExprNode (const MaskedLattice& lattice); LatticeExprNode (const MaskedLattice& lattice); // // Create a lattice expression from a region. // It results in a boolean expression node. // LatticeExprNode (const LCRegion& region); LatticeExprNode (const Slicer& slicer); LatticeExprNode (const LattRegionHolder& region); // // Masking operator using a condition. // The given boolean expression forms a mask/region for this expression node. LatticeExprNode operator[] (const LatticeExprNode& cond) const; // Copy constructor (reference semantics) LatticeExprNode (const LatticeExprNode& other); // Destructor, does nothing virtual ~LatticeExprNode(); // Assignment (reference semantics) LatticeExprNode& operator= (const LatticeExprNode& other); // Get the IPosition. // It throws an exception if the node does not contain an IPosition. const IPosition& getIPosition() const; // Convert the expression to another data type. // std::shared_ptr> makeFloat() const; std::shared_ptr> makeDouble() const; std::shared_ptr> makeComplex() const; std::shared_ptr> makeDComplex() const; std::shared_ptr> makeBool() const; // // Evaluate the expression. // One can be sure that the result is not a reference to another array. // This function should be used by LatticeExpr and other users. // void eval (LELArray& result, const Slicer& section) const; void eval (LELArray& result, const Slicer& section) const; void eval (LELArray& result, const Slicer& section) const; void eval (LELArray& result, const Slicer& section) const; void eval (LELArray& result, const Slicer& section) const; // // Evaluate the expression. // The result can be a reference to some internal array (in particular // to an array in an ArrayLattice object used as a lattice). // This function is meant for internal use by the LEL classes and // should not be used externally. // void evalRef (LELArrayRef& result, const Slicer& section) const { pExprFloat_p->evalRef (result, section); } void evalRef (LELArrayRef& result, const Slicer& section) const { pExprDouble_p->evalRef (result, section); } void evalRef (LELArrayRef& result, const Slicer& section) const { pExprComplex_p->evalRef (result, section); } void evalRef (LELArrayRef& result, const Slicer& section) const { pExprDComplex_p->evalRef (result, section); } void evalRef (LELArrayRef& result, const Slicer& section) const { pExprBool_p->evalRef (result, section); } // // Evaluate the expression (in case it is a scalar). The "eval" // and "get*" functions do the same thing, they just have // a slightly different interface. // void eval (Float& result) const; void eval (Double& result) const; void eval (Complex& result) const; void eval (DComplex& result) const; void eval (Bool& result) const; Float getFloat() const; Double getDouble() const; Complex getComplex() const; DComplex getDComplex() const; Bool getBool() const; // // Evaluate the expression (in case it is a constant array). // Array getArrayFloat() const; Array getArrayDouble() const; Array getArrayComplex() const; Array getArrayDComplex() const; Array getArrayBool() const; // // Get the data type of the expression. DataType dataType() const {return dtype_p;} // Is the expression node a region? Bool isRegion() const {return pAttr_p->isRegion();} // Is the result of "eval" a scalar? Bool isScalar() const {return pAttr_p->isScalar();} // Is the result of "eval" masked? Bool isMasked() const {return pAttr_p->isMasked();} // Holds the node an invalid scalar? Bool isInvalidScalar() const { if (!donePrepare_p) doPrepare(); return isInvalid_p; } // Return the shape of the Lattice including all degenerate axes // (ie. axes with a length of one) const IPosition& shape() const {return pAttr_p->shape();} // Get the attribute object of the expression. const LELAttribute& getAttribute() const {return *pAttr_p;} // Replace a scalar subexpression by its result. Bool replaceScalarExpr(); // Make the object from a std::shared_ptr pointer. // Ideally this function is private, but alas it is needed in LELFunction1D, // operator==, and more (too many to make them friend). // LatticeExprNode(const std::shared_ptr>& expr); LatticeExprNode(const std::shared_ptr>& expr); LatticeExprNode(const std::shared_ptr>& expr); LatticeExprNode(const std::shared_ptr>& expr); LatticeExprNode(const std::shared_ptr>& expr); // // Determine the resulting data type from the given data types. // An exception is thrown if they are incompatible. static DataType resultDataType (DataType left, DataType right); // Check the arguments of a function and return the resulting attribute object. // The matchAxes argument tells if the axes have to match exactly or // whether it is possible that one expression is a subset of another // (i.e. that axes may be missing). //
        The expectArray argument tells if the result should be an array // which is the case if one of the arguments is an array. static LELAttribute checkArg (const Block& arg, const Block& argType, Bool expectArray, Bool matchAxes = True); // Handle locking of the LatticeExpr which is delegated to all of its parts. // Bool lock (FileLocker::LockType, uInt nattempts); void unlock(); Bool hasLock (FileLocker::LockType) const; void resync(); // private: // Make the object from a LELInterface* pointer. // LatticeExprNode(LELInterface* expr); LatticeExprNode(LELInterface* expr); LatticeExprNode(LELInterface* expr); LatticeExprNode(LELInterface* expr); LatticeExprNode(LELInterface* expr); // // Test if both operands represent a region. // An exception is thrown if only one of them is a region. static Bool areRegions (const LatticeExprNode& left, const LatticeExprNode& right); // Create a new node for a numerical unary operation. // The result has the same data type as the input. static LatticeExprNode newNumUnary (LELUnaryEnums::Operation oper, const LatticeExprNode& expr); // Create a new node for a numerical function with 1 argument. // The result has the same data type as the input. static LatticeExprNode newNumFunc1D (LELFunctionEnums::Function func, const LatticeExprNode& expr); // Create a new node for a real numerical function with 1 argument. // The result has the same data type as the input. static LatticeExprNode newRealFunc1D (LELFunctionEnums::Function func, const LatticeExprNode& expr); // Create a new node for a complex numerical function with 1 argument. // The result has the same data type as the input. static LatticeExprNode newComplexFunc1D (LELFunctionEnums::Function func, const LatticeExprNode& expr); // Create a new node for a numerical function with 1 argument that // returns a real number static LatticeExprNode newNumReal1D (LELFunctionEnums::Function func, const LatticeExprNode& expr); // Create a new node for a numerical function with 2 arguments. // The result has the same data type as the combined input type. static LatticeExprNode newNumFunc2D (LELFunctionEnums::Function func, const LatticeExprNode& left, const LatticeExprNode& right); // Create a new node for a numerical binary operator. // The result has the same data type as the combined input type. static LatticeExprNode newNumBinary (LELBinaryEnums::Operation oper, const LatticeExprNode& left, const LatticeExprNode& right); // Create a new node for a logical binary operator. // The result has the same data type as the combined input type. static LatticeExprNode newLogBinary (LELBinaryEnums::Operation oper, const LatticeExprNode& left, const LatticeExprNode& right); // Create a new node for a comparison binary operator. // The result has the same data type as the combined input type. static LatticeExprNode newBinaryCmp (LELBinaryEnums::Operation oper, const LatticeExprNode& left, const LatticeExprNode& right); // Make (if needed and if possible) the expression nodes such that // the dimensionalities are equal. This is only possible if both // nodes have a coordinate system. // It is done by creating an ExtendLattice object for the node // with the lower dimensionality. static Int makeEqualDim (LatticeExprNode& expr0, LatticeExprNode& expr1); // Do the preparation for the evaluation. void doPrepare() const; // Member variables. Bool donePrepare_p; DataType dtype_p; Bool isInvalid_p; IPosition iposition_p; const LELAttribute* pAttr_p; std::shared_ptr> pExprFloat_p; std::shared_ptr> pExprDouble_p; std::shared_ptr> pExprComplex_p; std::shared_ptr> pExprDComplex_p; std::shared_ptr> pExprBool_p; }; inline LatticeExprNode operator% (const LatticeExprNode& left, const LatticeExprNode& right) { return fmod (left, right); } inline LatticeExprNode operator^ (const LatticeExprNode& left, const LatticeExprNode& right) { return pow (left, right); } inline LatticeExprNode convertType(const LatticeExprNode& expr, const Float*) { return toFloat (expr); } inline LatticeExprNode convertType(const LatticeExprNode& expr, const Double*) { return toDouble (expr); } inline LatticeExprNode convertType(const LatticeExprNode& expr, const Complex*) { return toComplex (expr); } inline LatticeExprNode convertType(const LatticeExprNode& expr, const DComplex*) { return toDComplex (expr); } inline LatticeExprNode convertType(const LatticeExprNode& expr, const Bool*) { return toBool (expr); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LEL/test/000077500000000000000000000000001476623553700165275ustar00rootroot00000000000000casacore-3.7.1/lattices/LEL/test/CMakeLists.txt000066400000000000000000000006261476623553700212730ustar00rootroot00000000000000set (tests tLEL tLELAttribute tLELMedian tLatticeExpr tLatticeExpr2 tLatticeExpr3 tLatticeExprNode tLatticeExpr2Node tLatticeExpr3Node ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_lattices) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/lattices/LEL/test/tLEL.cc000066400000000000000000002500331476623553700176410ustar00rootroot00000000000000//# tLEL.cc: Tests the LEL* classes directly //# Copyright (C) 1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Bool checkAttribute (const LELAttribute& attr, const Bool isMasked, const Bool isScalar, const IPosition& shape, const IPosition& tileShape, const LELCoordinates& lattCoord); Bool checkFloat (LELInterface& expr, const Float Result, const String& name, const IPosition& shape, const Bool shouldBeScalar, const Bool suppress); Bool checkDouble (LELInterface& expr, const Double Result, const String& name, const IPosition& shape, const Bool shouldBeScalar, const Bool suppress); Bool checkComplex (LELInterface& expr, const Complex& Result, const String& name, const IPosition& shape, const Bool shouldBeScalar, const Bool suppress); Bool checkDComplex (LELInterface& expr, const DComplex& Result, const String& name, const IPosition& shape, const Bool shouldBeScalar, const Bool suppress); Bool checkBool (LELInterface& expr, const Bool Result, const String& name, const IPosition& shape, const Bool shouldBeScalar, const Bool suppress, const Bool emptyShape=False); int main (int argc, const char* argv[]) { try { cout << ">>>" << endl; Input inp(1); inp.version(" "); inp.create("nx", "2", "Number of pixels along the x-axis", "int"); inp.create("ny", "2", "Number of pixels along the y-axis", "int"); inp.create("sup", "False", "Suppress expected exception messages", "Bool"); inp.readArguments(argc, argv); cout << "<<<" << endl; const uInt nx=inp.getInt("nx"); const uInt ny=inp.getInt("ny"); const Bool suppress =inp.getBool("sup"); // // The use of these tiny ArrayLattices means this test program // does not computationally stress the classes. Here we just // test them logically. See other test programs for stress tests // IPosition shape(2,nx,ny); // Bool Lattices Bool BResult; ArrayLattice aB(shape); ArrayLattice bB(shape); ArrayLattice cB(shape); Bool aBVal = True; aB.set(aBVal); Bool bBVal = False; bB.set(bBVal); Bool cBVal = True; cB.set(cBVal); // FLoat Lattices Array FArr(shape); Float FResult; ArrayLattice aF(shape); ArrayLattice bF(shape); ArrayLattice cF(shape); ArrayLattice nanF(shape); Float aFVal = 0.0; aF.set(aFVal); Float bFVal = 2.0; bF.set(bFVal); Float cFVal = 3.0; cF.set(cFVal); Float nanFVal; setNaN(nanFVal); nanF.set(nanFVal); // Double Lattices Array DArr(shape); Double DResult; ArrayLattice aD(shape); ArrayLattice bD(shape); ArrayLattice cD(shape); Double aDVal = 0.0; aD.set(aDVal); Double bDVal = 2.0; bD.set(bDVal); Double cDVal = 3.0; cD.set(cDVal); // Complex Lattices Array CArr(shape); Complex CResult; ArrayLattice aC(shape); ArrayLattice bC(shape); ArrayLattice cC(shape); Complex aCVal = Complex(0.0,0.0); aC.set(aCVal); Complex bCVal = Complex(2.0,2.0); bC.set(bCVal); Complex cCVal = Complex(3.0,3.0); cC.set(cCVal); // DComplex Lattices Array DCArr(shape); DComplex DCResult; ArrayLattice aDC(shape); ArrayLattice bDC(shape); ArrayLattice cDC(shape); DComplex aDCVal = DComplex(0.0,0.0); aDC.set(aDCVal); DComplex bDCVal = DComplex(2.0,2.0); bDC.set(bDCVal); DComplex cDCVal = DComplex(3.0,3.0); cDC.set(cDCVal); Bool ok = True; //************************************************************************ // // LELAttribute // { cout << "LELAttribute" << endl; // First a scalar attribute const IPosition nullIPos = IPosition(); LELAttribute attr1; LELCoordinates lattCoord2 (new LELLattCoord()); if (!checkAttribute(attr1, False, True, nullIPos, nullIPos, lattCoord2)) ok = False; // Now a non-scalar one; this only tests null LELCoordinates Bool isScalar2 = False; IPosition shape2 = shape; IPosition tileShape2 = shape; LELAttribute attr2(True, shape2, tileShape2, lattCoord2); if (!checkAttribute(attr2, True, isScalar2, shape2, tileShape2, lattCoord2)) ok = False; LELAttribute attr3 = attr2; if (!checkAttribute(attr3, attr2.isMasked(), attr2.isScalar(), attr2.shape(), attr2.tileShape(), attr2.coordinates())) { cout << " Assignment failed" << endl; ok = False; } // Result of scalar and non-scalar is non-scalar LELAttribute attr4(attr1, attr2); if (!checkAttribute(attr4, attr2.isMasked(), attr2.isScalar(), attr2.shape(), attr2.tileShape(), attr2.coordinates())) { cout << " binary constructor failed" << endl; ok = False; } } //************************************************************************ // // LELLattice // { cout << endl << "LELLattice " << endl; LELLattice expr(bF); FResult = bFVal; if (!checkFloat (expr, FResult, String("LELLattice"), shape, False, suppress)) ok = False; } { cout << "LELLattice " << endl; LELLattice expr(bD); DResult = bDVal; if (!checkDouble(expr, DResult, String("LELLattice"), shape, False, suppress)) ok = False; } { cout << "LELLattice " << endl; LELLattice expr(bC); CResult = bCVal; if (!checkComplex(expr, CResult, String("LELLattice"), shape, False, suppress)) ok = False; } { cout << "LELLattice " << endl; LELLattice expr(bDC); DCResult = bDCVal; if (!checkDComplex(expr, DCResult, String("LELLattice"), shape, False, suppress)) ok = False; } { cout << "LELLattice " << endl; LELLattice expr(bB); BResult = bBVal; if (!checkBool(expr, BResult, String("LELLattice"), shape, False, suppress)) ok = False; } //************************************************************************ // // LELUnaryConst // { cout << endl << "LELUnaryConst" << endl; LELUnaryConst expr(aFVal); if (!checkFloat (expr, aFVal, String("LELUnaryConst"), shape, True, suppress)) ok = False; } { cout << "LELUnaryConst" << endl; LELUnaryConst expr(aDVal); if (!checkDouble(expr, aDVal, String("LELUnaryConst"), shape, True, suppress)) ok = False; } { cout << "LELUnaryConst" << endl; LELUnaryConst expr(aCVal); if (!checkComplex(expr, aCVal, String("LELUnaryConst"), shape, True, suppress)) ok = False; } { cout << "LELUnaryConst" << endl; LELUnaryConst expr(aDCVal); if (!checkDComplex(expr, aDCVal, String("LELUnaryConst"), shape, True, suppress)) ok = False; } { cout << "LELUnaryConst" << endl; LELUnaryConst expr(aBVal); if (!checkBool(expr, aBVal, String("LELUnaryConst"), shape, True, suppress)) ok = False; } // //************************************************************************ // // LELUnary // cout << endl << "LELUnary" << endl; { auto pExpr = std::make_shared>(bF); // Note that operator+ is not actually implemented in LELUnary because it // wouldn't do anything ! It is implemented in LatticeExprNode though cout << " Operator -" << endl; LELUnary expr(LELUnaryEnums::MINUS, pExpr); if (!checkFloat (expr, -bFVal, String("LELUnary"), shape, False, suppress)) ok = False; } cout << "LELUnary" << endl; { // Note that operator+ is not actually implemented in LELUnary because it // wouldn't do anything ! It is implemented in LatticeExprNode though cout << " Operator -" << endl; auto pExpr = std::make_shared>(bD); LELUnary expr(LELUnaryEnums::MINUS, pExpr); if (!checkDouble(expr, -bDVal, String("LELUnary"), shape, False, suppress)) ok = False; } cout << "LELUnary" << endl; { // Note that operator+ is not actually implemented in LELUnary because it // wouldn't do anything ! It is implemented in LatticeExprNode though cout << " Operator -" << endl; auto pExpr = std::make_shared>(bC); LELUnary expr(LELUnaryEnums::MINUS, pExpr); if (!checkComplex(expr, -bCVal, String("LELUnary"), shape, False, suppress)) ok = False; } cout << "LELUnary" << endl; { // Note that operator+ is not actually implemented in LELUnary because it // wouldn't do anything ! It is implemented in LatticeExprNode though cout << " Operator -" << endl; auto pExpr = std::make_shared>(bDC); LELUnary expr(LELUnaryEnums::MINUS, pExpr); if (!checkDComplex(expr, -bDCVal, String("LELUnary"), shape, False, suppress)) ok = False; } //************************************************************************ // // LELUnaryBool // { cout << endl << "LELUnaryBool" << endl; { cout << " Operator !" << endl; auto pExpr = std::make_shared>(aB); LELUnaryBool expr(LELUnaryEnums::NOT, pExpr); if (!checkBool(expr, (!aBVal), String("LELUnaryBool"), shape, False, suppress)) ok = False; } } // //************************************************************************ // // LELBinary // { cout << endl << "LELBinary" << endl; auto pExprLeft = std::make_shared>(bF); auto pExprRight = std::make_shared>(cF); { cout << " Operator +" << endl; LELBinary expr(LELBinaryEnums::ADD, pExprLeft, pExprRight); FResult = bFVal + cFVal; if (!checkFloat (expr, FResult, String("LELBinary"), shape, False, suppress)) ok = False; } { cout << " Operator -" << endl; LELBinary expr(LELBinaryEnums::SUBTRACT, pExprLeft, pExprRight); FResult = bFVal - cFVal; if (!checkFloat (expr, FResult, String("LELBinary"), shape, False, suppress)) ok = False; } { cout << " Operator *" << endl; LELBinary expr(LELBinaryEnums::MULTIPLY, pExprLeft, pExprRight); FResult = bFVal * cFVal; if (!checkFloat (expr, FResult, String("LELBinary"), shape, False, suppress)) ok = False; } { cout << " Operator /" << endl; LELBinary expr(LELBinaryEnums::DIVIDE, pExprLeft, pExprRight); FResult = bFVal / cFVal; if (!checkFloat (expr, FResult, String("LELBinary"), shape, False, suppress)) ok = False; } } // // //************************************************************************ // // LELBinary // { cout << endl << "LELBinary" << endl; auto pExprLeft = std::make_shared>(bD); auto pExprRight = std::make_shared>(cD); { cout << " Operator +" << endl; LELBinary expr(LELBinaryEnums::ADD, pExprLeft, pExprRight); DResult = bDVal + cDVal; if (!checkDouble (expr, DResult, String("LELBinary"), shape, False, suppress)) ok = False; } { cout << " Operator -" << endl; LELBinary expr(LELBinaryEnums::SUBTRACT, pExprLeft, pExprRight); DResult = bDVal - cDVal; if (!checkDouble (expr, DResult, String("LELBinary"), shape, False, suppress)) ok = False; } { cout << " Operator *" << endl; LELBinary expr(LELBinaryEnums::MULTIPLY, pExprLeft, pExprRight); DResult = bDVal * cDVal; if (!checkDouble (expr, DResult, String("LELBinary"), shape, False, suppress)) ok = False; } { cout << " Operator /" << endl; LELBinary expr(LELBinaryEnums::DIVIDE, pExprLeft, pExprRight); DResult = bDVal / cDVal; if (!checkDouble (expr, DResult, String("LELBinary"), shape, False, suppress)) ok = False; } } // //************************************************************************ // // LELBinary // { cout << endl << "LELBinary" << endl; auto pExprLeft = std::make_shared>(bC); auto pExprRight = std::make_shared>(cC); { cout << " Operator +" << endl; LELBinary expr(LELBinaryEnums::ADD, pExprLeft, pExprRight); CResult = bCVal + cCVal; if (!checkComplex (expr, CResult, String("LELBinary"), shape, False, suppress)) ok = False; } { cout << " Operator -" << endl; LELBinary expr(LELBinaryEnums::SUBTRACT, pExprLeft, pExprRight); CResult = bCVal - cCVal; if (!checkComplex (expr, CResult, String("LELBinary"), shape, False, suppress)) ok = False; } { cout << " Operator *" << endl; LELBinary expr(LELBinaryEnums::MULTIPLY, pExprLeft, pExprRight); CResult = bCVal * cCVal; if (!checkComplex (expr, CResult, String("LELBinary"), shape, False, suppress)) ok = False; } { cout << " Operator /" << endl; LELBinary expr(LELBinaryEnums::DIVIDE, pExprLeft, pExprRight); CResult = bCVal / cCVal; if (!checkComplex (expr, CResult, String("LELBinary"), shape, False, suppress)) ok = False; } } // //************************************************************************ // // LELBinary // { cout << endl << "LELBinary" << endl; auto pExprLeft = std::make_shared>(bDC); auto pExprRight = std::make_shared>(cDC); { cout << " Operator +" << endl; LELBinary expr(LELBinaryEnums::ADD, pExprLeft, pExprRight); DCResult = bDCVal + cDCVal; if (!checkDComplex (expr, DCResult, String("LELBinary"), shape, False, suppress)) ok = False; } { cout << " Operator -" << endl; LELBinary expr(LELBinaryEnums::SUBTRACT, pExprLeft, pExprRight); DCResult = bDCVal - cDCVal; if (!checkDComplex (expr, DCResult, String("LELBinary"), shape, False, suppress)) ok = False; } { cout << " Operator *" << endl; LELBinary expr(LELBinaryEnums::MULTIPLY, pExprLeft, pExprRight); DCResult = bDCVal * cDCVal; if (!checkDComplex (expr, DCResult, String("LELBinary"), shape, False, suppress)) ok = False; } { cout << " Operator /" << endl; LELBinary expr(LELBinaryEnums::DIVIDE, pExprLeft, pExprRight); DCResult = bDCVal / cDCVal; if (!checkDComplex (expr, DCResult, String("LELBinary"), shape, False, suppress)) ok = False; } } // //************************************************************************ // // LELBinaryCmp // { cout << endl << "LELBinaryCmp" << endl; auto pExprLeft = std::make_shared>(bF); auto pExprRight = std::make_shared>(cF); { cout << " Operator ==" << endl; LELBinaryCmp expr(LELBinaryEnums::EQ, pExprLeft, pExprRight); BResult = (bFVal==cFVal); if (!checkBool(expr, BResult, String("LELBinaryCmp"), shape, False, suppress)) ok = False; } { cout << " Operator !=" << endl; LELBinaryCmp expr(LELBinaryEnums::NE, pExprLeft, pExprRight); BResult = (bFVal!=cFVal); if (!checkBool(expr, BResult, String("LELBinaryCmp"), shape, False, suppress)) ok = False; } { cout << " Operator >" << endl; LELBinaryCmp expr(LELBinaryEnums::GT, pExprLeft, pExprRight); BResult = (bFVal>cFVal); if (!checkBool(expr, BResult, String("LELBinaryCmp"), shape, False, suppress)) ok = False; } { cout << " Operator >=" << endl; LELBinaryCmp expr(LELBinaryEnums::GE, pExprLeft, pExprRight); BResult = (bFVal>=cFVal); if (!checkBool(expr, BResult, String("LELBinaryCmp"), shape, False, suppress)) ok = False; } } // //************************************************************************ // // LELBinaryCmp // { cout << endl << "LELBinaryCmp" << endl; auto pExprLeft = std::make_shared>(bD); auto pExprRight = std::make_shared>(cD); { cout << " Operator ==" << endl; LELBinaryCmp expr(LELBinaryEnums::EQ, pExprLeft, pExprRight); BResult = (bDVal==cDVal); if (!checkBool(expr, BResult, String("LELBinaryCmp"), shape, False, suppress)) ok = False; } { cout << " Operator !=" << endl; LELBinaryCmp expr(LELBinaryEnums::NE, pExprLeft, pExprRight); BResult = (bDVal!=cDVal); if (!checkBool(expr, BResult, String("LELBinaryCmp"), shape, False, suppress)) ok = False; } { cout << " Operator >" << endl; LELBinaryCmp expr(LELBinaryEnums::GT, pExprLeft, pExprRight); BResult = (bDVal>cDVal); if (!checkBool(expr, BResult, String("LELBinaryCmp"), shape, False, suppress)) ok = False; } { cout << " Operator >=" << endl; LELBinaryCmp expr(LELBinaryEnums::GE, pExprLeft, pExprRight); BResult = (bDVal>=cDVal); if (!checkBool(expr, BResult, String("LELBinaryCmp"), shape, False, suppress)) ok = False; } } // //************************************************************************ // // LELBinaryCmp // { cout << endl << "LELBinaryCmp" << endl; auto pExprLeft = std::make_shared>(bC); auto pExprRight = std::make_shared>(cC); { cout << " Operator ==" << endl; LELBinaryCmp expr(LELBinaryEnums::EQ, pExprLeft, pExprRight); BResult = (bCVal==cCVal); if (!checkBool(expr, BResult, String("LELBinaryCmp"), shape, False, suppress)) ok = False; } { cout << " Operator !=" << endl; LELBinaryCmp expr(LELBinaryEnums::NE, pExprLeft, pExprRight); BResult = (bCVal!=cCVal); if (!checkBool(expr, BResult, String("LELBinaryCmp"), shape, False, suppress)) ok = False; } { cout << " Operator >" << endl; LELBinaryCmp expr(LELBinaryEnums::GT, pExprLeft, pExprRight); BResult = (bCVal>cCVal); if (!checkBool(expr, BResult, String("LELBinaryCmp"), shape, False, suppress)) ok = False; } { cout << " Operator >=" << endl; LELBinaryCmp expr(LELBinaryEnums::GE, pExprLeft, pExprRight); BResult = (bCVal>=cCVal); if (!checkBool(expr, BResult, String("LELBinaryCmp"), shape, False, suppress)) ok = False; } } // //************************************************************************ // // LELBinaryCmp // { cout << endl << "LELBinaryCmp" << endl; auto pExprLeft = std::make_shared>(bDC); auto pExprRight = std::make_shared>(cDC); { cout << " Operator ==" << endl; LELBinaryCmp expr(LELBinaryEnums::EQ, pExprLeft, pExprRight); BResult = (bDCVal==cDCVal); if (!checkBool(expr, BResult, String("LELBinaryCmp"), shape, False, suppress)) ok = False; } { cout << " Operator !=" << endl; LELBinaryCmp expr(LELBinaryEnums::NE, pExprLeft, pExprRight); BResult = (bDCVal!=cDCVal); if (!checkBool(expr, BResult, String("LELBinaryCmp"), shape, False, suppress)) ok = False; } { cout << " Operator >" << endl; LELBinaryCmp expr(LELBinaryEnums::GT, pExprLeft, pExprRight); BResult = (bDCVal>cDCVal); if (!checkBool(expr, BResult, String("LELBinaryCmp"), shape, False, suppress)) ok = False; } { cout << " Operator >=" << endl; LELBinaryCmp expr(LELBinaryEnums::GE, pExprLeft, pExprRight); BResult = (bDCVal>=cDCVal); if (!checkBool(expr, BResult, String("LELBinaryCmp"), shape, False, suppress)) ok = False; } } // //************************************************************************ // // LELBinaryBool // { cout << endl << "LELBinaryBool" << endl; auto pExprLeft = std::make_shared>(bB); auto pExprRight = std::make_shared>(cB); { cout << " Operator ==" << endl; LELBinaryBool expr(LELBinaryEnums::EQ, pExprLeft, pExprRight); BResult = (bBVal==cBVal); if (!checkBool(expr, BResult, String("LELBinaryBool"), shape, False, suppress)) ok = False; } { cout << " Operator !=" << endl; LELBinaryBool expr(LELBinaryEnums::NE, pExprLeft, pExprRight); BResult = (bBVal!=cBVal); if (!checkBool(expr, BResult, String("LELBinaryBool"), shape, False, suppress)) ok = False; } { cout << " Operator &&" << endl; LELBinaryBool expr(LELBinaryEnums::AND, pExprLeft, pExprRight); BResult = (bBVal&&cBVal); if (!checkBool(expr, BResult, String("LELBinaryBool"), shape, False, suppress)) ok = False; } } // //************************************************************************ // // LELFunction1D // { cout << endl << "LELFunction1D" << endl; auto pExpr = std::make_shared>(bF); { cout << " Function sin" << endl; LELFunction1D expr(LELFunctionEnums::SIN, pExpr); FResult = sin(bFVal); if (!checkFloat (expr, FResult, String("LELFunction1D"), shape, False, suppress)) ok = False; } { cout << " Function sinh" << endl; LELFunction1D expr(LELFunctionEnums::SINH, pExpr); FResult = sinh(bFVal); if (!checkFloat (expr, FResult, String("LELFunction1D"), shape, False, suppress)) ok = False; } { cout << " Function cos" << endl; LELFunction1D expr(LELFunctionEnums::COS, pExpr); FResult = cos(bFVal); if (!checkFloat (expr, FResult, String("LELFunction1D"), shape, False, suppress)) ok = False; } { cout << " Function cosh" << endl; LELFunction1D expr(LELFunctionEnums::COSH, pExpr); FResult = cosh(bFVal); if (!checkFloat (expr, FResult, String("LELFunction1D"), shape, False, suppress)) ok = False; } { cout << " Function exp" << endl; LELFunction1D expr(LELFunctionEnums::EXP, pExpr); FResult = exp(bFVal); if (!checkFloat (expr, FResult, String("LELFunction1D"), shape, False, suppress)) ok = False; } { cout << " Function log" << endl; LELFunction1D expr(LELFunctionEnums::LOG, pExpr); FResult = log(bFVal); if (!checkFloat (expr, FResult, String("LELFunction1D"), shape, False, suppress)) ok = False; } { cout << " Function log10" << endl; LELFunction1D expr(LELFunctionEnums::LOG10, pExpr); FResult = log10(bFVal); if (!checkFloat (expr, FResult, String("LELFunction1D"), shape, False, suppress)) ok = False; } { cout << " Function sqrt" << endl; LELFunction1D expr(LELFunctionEnums::SQRT, pExpr); FResult = sqrt(bFVal); if (!checkFloat (expr, FResult, String("LELFunction1D"), shape, False, suppress)) ok = False; } { cout << " Function min" << endl; LELFunction1D expr(LELFunctionEnums::MIN1D, pExpr); bF.getSlice(FArr, IPosition(FArr.ndim(),0), FArr.shape(), IPosition(FArr.ndim(),1)); FResult = min(FArr); if (!checkFloat (expr, FResult, String("LELFunction1D"), shape, True, suppress)) ok = False; } { cout << " Function max" << endl; LELFunction1D expr(LELFunctionEnums::MAX1D, pExpr); bF.getSlice(FArr, IPosition(FArr.ndim(),0), FArr.shape(), IPosition(FArr.ndim(),1)); FResult = max(FArr); if (!checkFloat (expr, FResult, String("LELFunction1D"), shape, True, suppress)) ok = False; } { cout << " Function median" << endl; LELFunctionReal1D expr(LELFunctionEnums::MEDIAN1D, pExpr); bF.getSlice(FArr, IPosition(FArr.ndim(),0), FArr.shape(), IPosition(FArr.ndim(),1)); FResult = median(FArr); if (!checkFloat (expr, FResult, String("LELFunctionReal1D"), shape, True, suppress)) ok = False; } { cout << " Function mean" << endl; LELFunction1D expr(LELFunctionEnums::MEAN1D, pExpr); bF.getSlice(FArr, IPosition(FArr.ndim(),0), FArr.shape(), IPosition(FArr.ndim(),1)); FResult = mean(FArr); if (!checkFloat (expr, FResult, String("LELFunction1D"), shape, True, suppress)) ok = False; } { cout << " Function sum" << endl; LELFunction1D expr(LELFunctionEnums::SUM, pExpr); bF.getSlice(FArr, IPosition(FArr.ndim(),0), FArr.shape(), IPosition(FArr.ndim(),1)); FResult = sum(FArr); if (!checkFloat (expr, FResult, String("LELFunction1D"), shape, True, suppress)) ok = False; } } // // //************************************************************************ // // LELFunction1D // { cout << endl << "LELFunction1D" << endl; auto pExpr = std::make_shared>(bD); { cout << " Function sin" << endl; LELFunction1D expr(LELFunctionEnums::SIN, pExpr); DResult = sin(bDVal); if (!checkDouble (expr, DResult, String("LELFunction1D"), shape, False, suppress)) ok = False; } { cout << " Function sinh" << endl; LELFunction1D expr(LELFunctionEnums::SINH, pExpr); DResult = sinh(bDVal); if (!checkDouble (expr, DResult, String("LELFunction1D"), shape, False, suppress)) ok = False; } { cout << " Function cos" << endl; LELFunction1D expr(LELFunctionEnums::COS, pExpr); DResult = cos(bDVal); if (!checkDouble (expr, DResult, String("LELFunction1D"), shape, False, suppress)) ok = False; } { cout << " Function cosh" << endl; LELFunction1D expr(LELFunctionEnums::COSH, pExpr); DResult = cosh(bDVal); if (!checkDouble (expr, DResult, String("LELFunction1D"), shape, False, suppress)) ok = False; } { cout << " Function exp" << endl; LELFunction1D expr(LELFunctionEnums::EXP, pExpr); DResult = exp(bDVal); if (!checkDouble (expr, DResult, String("LELFunction1D"), shape, False, suppress)) ok = False; } { cout << " Function log" << endl; LELFunction1D expr(LELFunctionEnums::LOG, pExpr); DResult = log(bDVal); if (!checkDouble (expr, DResult, String("LELFunction1D"), shape, False, suppress)) ok = False; } { cout << " Function log10" << endl; LELFunction1D expr(LELFunctionEnums::LOG10, pExpr); DResult = log10(bDVal); if (!checkDouble (expr, DResult, String("LELFunction1D"), shape, False, suppress)) ok = False; } { cout << " Function sqrt" << endl; LELFunction1D expr(LELFunctionEnums::SQRT, pExpr); DResult = sqrt(bDVal); if (!checkDouble (expr, DResult, String("LELFunction1D"), shape, False, suppress)) ok = False; } { cout << " Function min" << endl; LELFunction1D expr(LELFunctionEnums::MIN1D, pExpr); bD.getSlice(DArr, IPosition(DArr.ndim(),0), DArr.shape(), IPosition(DArr.ndim(),1)); DResult = min(DArr); if (!checkDouble (expr, DResult, String("LELFunction1D"), shape, True, suppress)) ok = False; } { cout << " Function max" << endl; LELFunction1D expr(LELFunctionEnums::MAX1D, pExpr); bD.getSlice(DArr, IPosition(DArr.ndim(),0), DArr.shape(), IPosition(DArr.ndim(),1)); DResult = max(DArr); if (!checkDouble (expr, DResult, String("LELFunction1D"), shape, True, suppress)) ok = False; } { cout << " Function median" << endl; LELFunctionReal1D expr(LELFunctionEnums::MEDIAN1D, pExpr); bD.getSlice(DArr, IPosition(DArr.ndim(),0), DArr.shape(), IPosition(DArr.ndim(),1)); DResult = median(DArr); if (!checkDouble (expr, DResult, String("LELFunctionReal1D"), shape, True, suppress)) ok = False; } { cout << " Function mean" << endl; LELFunction1D expr(LELFunctionEnums::MEAN1D, pExpr); bD.getSlice(DArr, IPosition(DArr.ndim(),0), DArr.shape(), IPosition(DArr.ndim(),1)); DResult = mean(DArr); if (!checkDouble (expr, DResult, String("LELFunction1D"), shape, True, suppress)) ok = False; } { cout << " Function sum" << endl; LELFunction1D expr(LELFunctionEnums::SUM, pExpr); bD.getSlice(DArr, IPosition(DArr.ndim(),0), DArr.shape(), IPosition(DArr.ndim(),1)); DResult = sum(DArr); if (!checkDouble (expr, DResult, String("LELFunction1D"), shape, True, suppress)) ok = False; } } // // //************************************************************************ // // LELFunction1D // { cout << endl << "LELFunction1D" << endl; auto pExpr = std::make_shared>(bC); { cout << " Function sin" << endl; LELFunction1D expr(LELFunctionEnums::SIN, pExpr); CResult = sin(bCVal); if (!checkComplex (expr, CResult, String("LELFunction1D"), shape, False, suppress)) ok = False; } { cout << " Function sinh" << endl; LELFunction1D expr(LELFunctionEnums::SINH, pExpr); CResult = sinh(bCVal); if (!checkComplex (expr, CResult, String("LELFunction1D"), shape, False, suppress)) ok = False; } { cout << " Function cos" << endl; LELFunction1D expr(LELFunctionEnums::COS, pExpr); CResult = cos(bCVal); if (!checkComplex (expr, CResult, String("LELFunction1D"), shape, False, suppress)) ok = False; } { cout << " Function cosh" << endl; LELFunction1D expr(LELFunctionEnums::COSH, pExpr); CResult = cosh(bCVal); if (!checkComplex (expr, CResult, String("LELFunction1D"), shape, False, suppress)) ok = False; } { cout << " Function exp" << endl; LELFunction1D expr(LELFunctionEnums::EXP, pExpr); CResult = exp(bCVal); if (!checkComplex (expr, CResult, String("LELFunction1D"), shape, False, suppress)) ok = False; } { cout << " Function log" << endl; LELFunction1D expr(LELFunctionEnums::LOG, pExpr); CResult = log(bCVal); if (!checkComplex (expr, CResult, String("LELFunction1D"), shape, False, suppress)) ok = False; } { cout << " Function log10" << endl; LELFunction1D expr(LELFunctionEnums::LOG10, pExpr); CResult = log10(bCVal); if (!checkComplex (expr, CResult, String("LELFunction1D"), shape, False, suppress)) ok = False; } { cout << " Function sqrt" << endl; LELFunction1D expr(LELFunctionEnums::SQRT, pExpr); CResult = sqrt(bCVal); if (!checkComplex (expr, CResult, String("LELFunction1D"), shape, False, suppress)) ok = False; } { cout << " Function min" << endl; LELFunction1D expr(LELFunctionEnums::MIN1D, pExpr); bC.getSlice(CArr, IPosition(CArr.ndim(),0), CArr.shape(), IPosition(CArr.ndim(),1)); CResult = min(CArr); if (!checkComplex (expr, CResult, String("LELFunction1D"), shape, True, suppress)) ok = False; } { cout << " Function max" << endl; LELFunction1D expr(LELFunctionEnums::MAX1D, pExpr); bC.getSlice(CArr, IPosition(CArr.ndim(),0), CArr.shape(), IPosition(CArr.ndim(),1)); CResult = max(CArr); if (!checkComplex (expr, CResult, String("LELFunction1D"), shape, True, suppress)) ok = False; } } // // //************************************************************************ // // LELFunction1D // { cout << endl << "LELFunction1D" << endl; auto pExpr = std::make_shared>(bDC); { cout << " Function sin" << endl; LELFunction1D expr(LELFunctionEnums::SIN, pExpr); DCResult = sin(bDCVal); if (!checkDComplex (expr, DCResult, String("LELFunction1D"), shape, False, suppress)) ok = False; } { cout << " Function sinh" << endl; LELFunction1D expr(LELFunctionEnums::SINH, pExpr); DCResult = sinh(bDCVal); if (!checkDComplex (expr, DCResult, String("LELFunction1D"), shape, False, suppress)) ok = False; } { cout << " Function cos" << endl; LELFunction1D expr(LELFunctionEnums::COS, pExpr); DCResult = cos(bDCVal); if (!checkDComplex (expr, DCResult, String("LELFunction1D"), shape, False, suppress)) ok = False; } { cout << " Function cosh" << endl; LELFunction1D expr(LELFunctionEnums::COSH, pExpr); DCResult = cosh(bDCVal); if (!checkDComplex (expr, DCResult, String("LELFunction1D"), shape, False, suppress)) ok = False; } { cout << " Function exp" << endl; LELFunction1D expr(LELFunctionEnums::EXP, pExpr); DCResult = exp(bDCVal); if (!checkDComplex (expr, DCResult, String("LELFunction1D"), shape, False, suppress)) ok = False; } { cout << " Function log" << endl; LELFunction1D expr(LELFunctionEnums::LOG, pExpr); DCResult = log(bDCVal); if (!checkDComplex (expr, DCResult, String("LELFunction1D"), shape, False, suppress)) ok = False; } { cout << " Function log10" << endl; LELFunction1D expr(LELFunctionEnums::LOG10, pExpr); DCResult = log10(bDCVal); if (!checkDComplex (expr, DCResult, String("LELFunction1D"), shape, False, suppress)) ok = False; } { cout << " Function sqrt" << endl; LELFunction1D expr(LELFunctionEnums::SQRT, pExpr); DCResult = sqrt(bDCVal); if (!checkDComplex (expr, DCResult, String("LELFunction1D"), shape, False, suppress)) ok = False; } { cout << " Function min" << endl; LELFunction1D expr(LELFunctionEnums::MIN1D, pExpr); bDC.getSlice(DCArr, IPosition(DCArr.ndim(),0), DCArr.shape(), IPosition(DCArr.ndim(),1)); DCResult = min(DCArr); if (!checkDComplex (expr, DCResult, String("LELFunction1D"), shape, True, suppress)) ok = False; } { cout << " Function max" << endl; LELFunction1D expr(LELFunctionEnums::MAX1D, pExpr); bDC.getSlice(DCArr, IPosition(DCArr.ndim(),0), DCArr.shape(), IPosition(DCArr.ndim(),1)); DCResult = max(DCArr); if (!checkDComplex (expr, DCResult, String("LELFunction1D"), shape, True, suppress)) ok = False; } } // //************************************************************************ // // LELFunctionND // { cout << endl << "LELFunctionND" << endl; cout << " Function iif" << endl; { cout << " Scalar, scalar, scalar" << endl; Block arga(3); arga[0] = LatticeExprNode(True); arga[1] = LatticeExprNode(bFVal); arga[2] = LatticeExprNode(cFVal); LELFunctionND expr1(LELFunctionEnums::IIF, arga); FResult = bFVal; if (!checkFloat (expr1, FResult, String("LELFunctionND"), shape, True, suppress)) ok = False; arga[0] = LatticeExprNode(False); LELFunctionND expr2(LELFunctionEnums::IIF, arga); FResult = cFVal; if (!checkFloat (expr2, FResult, String("LELFunctionND"), shape, True, suppress)) ok = False; } { cout << " Scalar, scalar, array" << endl; Block arga(3); arga[0] = LatticeExprNode(True); arga[1] = LatticeExprNode(bFVal); arga[2] = LatticeExprNode(cF); LELFunctionND expr1(LELFunctionEnums::IIF, arga); FResult = bFVal; // Although the conditional is scalar, the result is still an array // because one of the evaluation expressions is an array if (!checkFloat (expr1, FResult, String("LELFunctionND"), shape, False, suppress)) ok = False; arga[0] = LatticeExprNode(False); LELFunctionND expr2(LELFunctionEnums::IIF, arga); FResult = cFVal; if (!checkFloat (expr2, FResult, String("LELFunctionND"), shape, False, suppress)) ok = False; } { cout << " Scalar, array, scalar" << endl; Block arga(3); arga[0] = LatticeExprNode(True); arga[1] = LatticeExprNode(bF); arga[2] = LatticeExprNode(cFVal); LELFunctionND expr1(LELFunctionEnums::IIF, arga); FResult = bFVal; if (!checkFloat (expr1, FResult, String("LELFunctionND"), shape, False, suppress)) ok = False; arga[0] = LatticeExprNode(False); LELFunctionND expr2(LELFunctionEnums::IIF, arga); FResult = cFVal; if (!checkFloat (expr2, FResult, String("LELFunctionND"), shape, False, suppress)) ok = False; } { cout << " Array, scalar, scalar" << endl; Block arga(3); arga[0] = LatticeExprNode(aB); arga[1] = LatticeExprNode(bFVal); arga[2] = LatticeExprNode(cFVal); LELFunctionND expr1(LELFunctionEnums::IIF, arga); if (aBVal) { FResult = bFVal; } else { FResult = cFVal; } if (!checkFloat (expr1, FResult, String("LELFunctionND"), shape, False, suppress)) ok = False; arga[0] = LatticeExprNode(bB); LELFunctionND expr2(LELFunctionEnums::IIF, arga); if (bBVal) { FResult = bFVal; } else { FResult = cFVal; } if (!checkFloat (expr2, FResult, String("LELFunctionND"), shape, False, suppress)) ok = False; } { cout << " Array, Array, scalar" << endl; Block arga(3); arga[0] = LatticeExprNode(aB); arga[1] = LatticeExprNode(bF); arga[2] = LatticeExprNode(cFVal); LELFunctionND expr1(LELFunctionEnums::IIF, arga); if (aBVal) { FResult = bFVal; } else { FResult = cFVal; } if (!checkFloat (expr1, FResult, String("LELFunctionND"), shape, False, suppress)) ok = False; arga[0] = LatticeExprNode(bB); LELFunctionND expr2(LELFunctionEnums::IIF, arga); if (bBVal) { FResult = bFVal; } else { FResult = cFVal; } if (!checkFloat (expr2, FResult, String("LELFunctionND"), shape, False, suppress)) ok = False; } { cout << " Array, scalar, array" << endl; Block arga(3); arga[0] = LatticeExprNode(aB); arga[1] = LatticeExprNode(bFVal); arga[2] = LatticeExprNode(cF); LELFunctionND expr1(LELFunctionEnums::IIF, arga); if (aBVal) { FResult = bFVal; } else { FResult = cFVal; } if (!checkFloat (expr1, FResult, String("LELFunctionND"), shape, False, suppress)) ok = False; arga[0] = LatticeExprNode(bB); LELFunctionND expr2(LELFunctionEnums::IIF, arga); if (bBVal) { FResult = bFVal; } else { FResult = cFVal; } if (!checkFloat (expr2, FResult, String("LELFunctionND"), shape, False, suppress)) ok = False; } } // //************************************************************************ // // LELFunctionND // { cout << endl << "LELFunctionND" << endl; cout << " Function iif" << endl; { cout << " Scalar, scalar, scalar" << endl; Block arga(3); arga[0] = LatticeExprNode(True); arga[1] = LatticeExprNode(bDVal); arga[2] = LatticeExprNode(cDVal); LELFunctionND expr1(LELFunctionEnums::IIF, arga); DResult = bDVal; if (!checkDouble (expr1, DResult, String("LELFunctionND"), shape, True, suppress)) ok = False; arga[0] = LatticeExprNode(False); LELFunctionND expr2(LELFunctionEnums::IIF, arga); DResult = cDVal; if (!checkDouble (expr2, DResult, String("LELFunctionND"), shape, True, suppress)) ok = False; } { cout << " Scalar, scalar, array" << endl; Block arga(3); arga[0] = LatticeExprNode(True); arga[1] = LatticeExprNode(bDVal); arga[2] = LatticeExprNode(cD); LELFunctionND expr1(LELFunctionEnums::IIF, arga); DResult = bDVal; // Although the conditional is scalar, the result is still an array // because one of the evaluation expressions is an array if (!checkDouble (expr1, DResult, String("LELFunctionND"), shape, False, suppress)) ok = False; arga[0] = LatticeExprNode(False); LELFunctionND expr2(LELFunctionEnums::IIF, arga); DResult = cDVal; if (!checkDouble (expr2, DResult, String("LELFunctionND"), shape, False, suppress)) ok = False; } { cout << " Scalar, array, scalar" << endl; Block arga(3); arga[0] = LatticeExprNode(True); arga[1] = LatticeExprNode(bD); arga[2] = LatticeExprNode(cDVal); LELFunctionND expr1(LELFunctionEnums::IIF, arga); DResult = bDVal; if (!checkDouble (expr1, DResult, String("LELFunctionND"), shape, False, suppress)) ok = False; arga[0] = LatticeExprNode(False); LELFunctionND expr2(LELFunctionEnums::IIF, arga); DResult = cDVal; if (!checkDouble (expr2, DResult, String("LELFunctionND"), shape, False, suppress)) ok = False; } { cout << " Array, scalar, scalar" << endl; Block arga(3); arga[0] = LatticeExprNode(aB); arga[1] = LatticeExprNode(bDVal); arga[2] = LatticeExprNode(cDVal); LELFunctionND expr1(LELFunctionEnums::IIF, arga); if (aBVal) { DResult = bDVal; } else { DResult = cDVal; } if (!checkDouble (expr1, DResult, String("LELFunctionND"), shape, False, suppress)) ok = False; arga[0] = LatticeExprNode(bB); LELFunctionND expr2(LELFunctionEnums::IIF, arga); if (bBVal) { DResult = bDVal; } else { DResult = cDVal; } if (!checkDouble (expr2, DResult, String("LELFunctionND"), shape, False, suppress)) ok = False; } { cout << " Array, Array, scalar" << endl; Block arga(3); arga[0] = LatticeExprNode(aB); arga[1] = LatticeExprNode(bD); arga[2] = LatticeExprNode(cDVal); LELFunctionND expr1(LELFunctionEnums::IIF, arga); if (aBVal) { DResult = bDVal; } else { DResult = cDVal; } if (!checkDouble (expr1, DResult, String("LELFunctionND"), shape, False, suppress)) ok = False; arga[0] = LatticeExprNode(bB); LELFunctionND expr2(LELFunctionEnums::IIF, arga); if (bBVal) { DResult = bDVal; } else { DResult = cDVal; } if (!checkDouble (expr2, DResult, String("LELFunctionND"), shape, False, suppress)) ok = False; } { cout << " Array, scalar, array" << endl; Block arga(3); arga[0] = LatticeExprNode(aB); arga[1] = LatticeExprNode(bDVal); arga[2] = LatticeExprNode(cD); LELFunctionND expr1(LELFunctionEnums::IIF, arga); if (aBVal) { DResult = bDVal; } else { DResult = cDVal; } if (!checkDouble (expr1, DResult, String("LELFunctionND"), shape, False, suppress)) ok = False; arga[0] = LatticeExprNode(bB); LELFunctionND expr2(LELFunctionEnums::IIF, arga); if (bBVal) { DResult = bDVal; } else { DResult = cDVal; } if (!checkDouble (expr2, DResult, String("LELFunctionND"), shape, False, suppress)) ok = False; } } // //************************************************************************ // // LELFunctionND // { cout << endl << "LELFunctionND" << endl; cout << " Function iif" << endl; { cout << " Scalar, scalar, scalar" << endl; Block arga(3); arga[0] = LatticeExprNode(True); arga[1] = LatticeExprNode(bCVal); arga[2] = LatticeExprNode(cCVal); LELFunctionND expr1(LELFunctionEnums::IIF, arga); CResult = bCVal; if (!checkComplex (expr1, CResult, String("LELFunctionND"), shape, True, suppress)) ok = False; arga[0] = LatticeExprNode(False); LELFunctionND expr2(LELFunctionEnums::IIF, arga); CResult = cCVal; if (!checkComplex (expr2, CResult, String("LELFunctionND"), shape, True, suppress)) ok = False; } { cout << " Scalar, scalar, array" << endl; Block arga(3); arga[0] = LatticeExprNode(True); arga[1] = LatticeExprNode(bCVal); arga[2] = LatticeExprNode(cC); LELFunctionND expr1(LELFunctionEnums::IIF, arga); CResult = bCVal; // Although the conditional is scalar, the result is still an array // because one of the evaluation expressions is an array if (!checkComplex (expr1, CResult, String("LELFunctionND"), shape, False, suppress)) ok = False; arga[0] = LatticeExprNode(False); LELFunctionND expr2(LELFunctionEnums::IIF, arga); CResult = cCVal; if (!checkComplex (expr2, CResult, String("LELFunctionND"), shape, False, suppress)) ok = False; } { cout << " Scalar, array, scalar" << endl; Block arga(3); arga[0] = LatticeExprNode(True); arga[1] = LatticeExprNode(bC); arga[2] = LatticeExprNode(cCVal); LELFunctionND expr1(LELFunctionEnums::IIF, arga); CResult = bCVal; if (!checkComplex (expr1, CResult, String("LELFunctionND"), shape, False, suppress)) ok = False; arga[0] = LatticeExprNode(False); LELFunctionND expr2(LELFunctionEnums::IIF, arga); CResult = cCVal; if (!checkComplex (expr2, CResult, String("LELFunctionND"), shape, False, suppress)) ok = False; } { cout << " Array, scalar, scalar" << endl; Block arga(3); arga[0] = LatticeExprNode(aB); arga[1] = LatticeExprNode(bCVal); arga[2] = LatticeExprNode(cCVal); LELFunctionND expr1(LELFunctionEnums::IIF, arga); if (aBVal) { CResult = bCVal; } else { CResult = cCVal; } if (!checkComplex (expr1, CResult, String("LELFunctionND"), shape, False, suppress)) ok = False; arga[0] = LatticeExprNode(bB); LELFunctionND expr2(LELFunctionEnums::IIF, arga); if (bBVal) { CResult = bCVal; } else { CResult = cCVal; } if (!checkComplex (expr2, CResult, String("LELFunctionND"), shape, False, suppress)) ok = False; } { cout << " Array, Array, scalar" << endl; Block arga(3); arga[0] = LatticeExprNode(aB); arga[1] = LatticeExprNode(bC); arga[2] = LatticeExprNode(cCVal); LELFunctionND expr1(LELFunctionEnums::IIF, arga); if (aBVal) { CResult = bCVal; } else { CResult = cCVal; } if (!checkComplex (expr1, CResult, String("LELFunctionND"), shape, False, suppress)) ok = False; arga[0] = LatticeExprNode(bB); LELFunctionND expr2(LELFunctionEnums::IIF, arga); if (bBVal) { CResult = bCVal; } else { CResult = cCVal; } if (!checkComplex (expr2, CResult, String("LELFunctionND"), shape, False, suppress)) ok = False; } { cout << " Array, scalar, array" << endl; Block arga(3); arga[0] = LatticeExprNode(aB); arga[1] = LatticeExprNode(bCVal); arga[2] = LatticeExprNode(cC); LELFunctionND expr1(LELFunctionEnums::IIF, arga); if (aBVal) { CResult = bCVal; } else { CResult = cCVal; } if (!checkComplex (expr1, CResult, String("LELFunctionND"), shape, False, suppress)) ok = False; arga[0] = LatticeExprNode(bB); LELFunctionND expr2(LELFunctionEnums::IIF, arga); if (bBVal) { CResult = bCVal; } else { CResult = cCVal; } if (!checkComplex (expr2, CResult, String("LELFunctionND"), shape, False, suppress)) ok = False; } } // //************************************************************************ // // LELFunctionND // { cout << endl << "LELFunctionND" << endl; cout << " Function iif" << endl; { cout << " Scalar, scalar, scalar" << endl; Block arga(3); arga[0] = LatticeExprNode(True); arga[1] = LatticeExprNode(bDCVal); arga[2] = LatticeExprNode(cDCVal); LELFunctionND expr1(LELFunctionEnums::IIF, arga); DCResult = bDCVal; if (!checkDComplex (expr1, DCResult, String("LELFunctionND"), shape, True, suppress)) ok = False; arga[0] = LatticeExprNode(False); LELFunctionND expr2(LELFunctionEnums::IIF, arga); DCResult = cDCVal; if (!checkDComplex (expr2, DCResult, String("LELFunctionND"), shape, True, suppress)) ok = False; } { cout << " Scalar, scalar, array" << endl; Block arga(3); arga[0] = LatticeExprNode(True); arga[1] = LatticeExprNode(bDCVal); arga[2] = LatticeExprNode(cDC); LELFunctionND expr1(LELFunctionEnums::IIF, arga); DCResult = bDCVal; // Although the conditional is scalar, the result is still an array // because one of the evaluation expressions is an array if (!checkDComplex (expr1, DCResult, String("LELFunctionND"), shape, False, suppress)) ok = False; arga[0] = LatticeExprNode(False); LELFunctionND expr2(LELFunctionEnums::IIF, arga); DCResult = cDCVal; if (!checkDComplex (expr2, DCResult, String("LELFunctionND"), shape, False, suppress)) ok = False; } { cout << " Scalar, array, scalar" << endl; Block arga(3); arga[0] = LatticeExprNode(True); arga[1] = LatticeExprNode(bDC); arga[2] = LatticeExprNode(cDCVal); LELFunctionND expr1(LELFunctionEnums::IIF, arga); DCResult = bDCVal; if (!checkDComplex (expr1, DCResult, String("LELFunctionND"), shape, False, suppress)) ok = False; arga[0] = LatticeExprNode(False); LELFunctionND expr2(LELFunctionEnums::IIF, arga); DCResult = cDCVal; if (!checkDComplex (expr2, DCResult, String("LELFunctionND"), shape, False, suppress)) ok = False; } { cout << " Array, scalar, scalar" << endl; Block arga(3); arga[0] = LatticeExprNode(aB); arga[1] = LatticeExprNode(bDCVal); arga[2] = LatticeExprNode(cDCVal); LELFunctionND expr1(LELFunctionEnums::IIF, arga); if (aBVal) { DCResult = bDCVal; } else { DCResult = cDCVal; } if (!checkDComplex (expr1, DCResult, String("LELFunctionND"), shape, False, suppress)) ok = False; arga[0] = LatticeExprNode(bB); LELFunctionND expr2(LELFunctionEnums::IIF, arga); if (bBVal) { DCResult = bDCVal; } else { DCResult = cDCVal; } if (!checkDComplex (expr2, DCResult, String("LELFunctionND"), shape, False, suppress)) ok = False; } { cout << " Array, Array, scalar" << endl; Block arga(3); arga[0] = LatticeExprNode(aB); arga[1] = LatticeExprNode(bDC); arga[2] = LatticeExprNode(cDCVal); LELFunctionND expr1(LELFunctionEnums::IIF, arga); if (aBVal) { DCResult = bDCVal; } else { DCResult = cDCVal; } if (!checkDComplex (expr1, DCResult, String("LELFunctionND"), shape, False, suppress)) ok = False; arga[0] = LatticeExprNode(bB); LELFunctionND expr2(LELFunctionEnums::IIF, arga); if (bBVal) { DCResult = bDCVal; } else { DCResult = cDCVal; } if (!checkDComplex (expr2, DCResult, String("LELFunctionND"), shape, False, suppress)) ok = False; } { cout << " Array, scalar, array" << endl; Block arga(3); arga[0] = LatticeExprNode(aB); arga[1] = LatticeExprNode(bDCVal); arga[2] = LatticeExprNode(cDC); LELFunctionND expr1(LELFunctionEnums::IIF, arga); if (aBVal) { DCResult = bDCVal; } else { DCResult = cDCVal; } if (!checkDComplex (expr1, DCResult, String("LELFunctionND"), shape, False, suppress)) ok = False; arga[0] = LatticeExprNode(bB); LELFunctionND expr2(LELFunctionEnums::IIF, arga); if (bBVal) { DCResult = bDCVal; } else { DCResult = cDCVal; } if (!checkDComplex (expr2, DCResult, String("LELFunctionND"), shape, False, suppress)) ok = False; } } // //************************************************************************ // // LELFunctionReal1D // { cout << endl << "LELFunctionReal1D" << endl; auto pExpr = std::make_shared>(bF); auto pExpra = std::make_shared>(aF); { cout << " Function asin" << endl; LELFunctionReal1D expr(LELFunctionEnums::ASIN, pExpra); FResult = asin(aFVal); if (!checkFloat (expr, FResult, String("LELFunctionReal1D"), shape, False, suppress)) ok = False; } { cout << " Function acos" << endl; LELFunctionReal1D expr(LELFunctionEnums::ACOS, pExpra); FResult = acos(aFVal); if (!checkFloat (expr, FResult, String("LELFunctionReal1D"), shape, False, suppress)) ok = False; } { cout << " Function tan" << endl; LELFunctionReal1D expr(LELFunctionEnums::TAN, pExpr); FResult = tan(bFVal); if (!checkFloat (expr, FResult, String("LELFunctionReal1D"), shape, False, suppress)) ok = False; } { cout << " Function tanh" << endl; LELFunctionReal1D expr(LELFunctionEnums::TANH, pExpr); FResult = tanh(bFVal); if (!checkFloat (expr, FResult, String("LELFunctionReal1D"), shape, False, suppress)) ok = False; } { cout << " Function ceil" << endl; LELFunctionReal1D expr(LELFunctionEnums::CEIL, pExpr); FResult = ceil(bFVal); if (!checkFloat (expr, FResult, String("LELFunctionReal1D"), shape, False, suppress)) ok = False; } { cout << " Function floor" << endl; LELFunctionReal1D expr(LELFunctionEnums::FLOOR, pExpr); FResult = floor(bFVal); if (!checkFloat (expr, FResult, String("LELFunctionReal1D"), shape, False, suppress)) ok = False; } } // //************************************************************************ // // LELFunctionReal1D // { cout << endl << "LELFunctionReal1D" << endl; auto pExpr = std::make_shared>(bD); auto pExpra = std::make_shared>(aD); { cout << " Function asin" << endl; LELFunctionReal1D expr(LELFunctionEnums::ASIN, pExpra); DResult = asin(aDVal); if (!checkDouble (expr, DResult, String("LELFunctionReal1D"), shape, False, suppress)) ok = False; } { cout << " Function acos" << endl; LELFunctionReal1D expr(LELFunctionEnums::ACOS, pExpra); DResult = acos(aDVal); if (!checkDouble (expr, DResult, String("LELFunctionReal1D"), shape, False, suppress)) ok = False; } { cout << " Function tan" << endl; LELFunctionReal1D expr(LELFunctionEnums::TAN, pExpr); DResult = tan(bDVal); if (!checkDouble (expr, DResult, String("LELFunctionReal1D"), shape, False, suppress)) ok = False; } { cout << " Function tanh" << endl; LELFunctionReal1D expr(LELFunctionEnums::TANH, pExpr); DResult = tanh(bDVal); if (!checkDouble (expr, DResult, String("LELFunctionReal1D"), shape, False, suppress)) ok = False; } { cout << " Function ceil" << endl; LELFunctionReal1D expr(LELFunctionEnums::CEIL, pExpr); DResult = ceil(bDVal); if (!checkDouble (expr, DResult, String("LELFunctionReal1D"), shape, False, suppress)) ok = False; } { cout << " Function floor" << endl; LELFunctionReal1D expr(LELFunctionEnums::FLOOR, pExpr); DResult = floor(bDVal); if (!checkDouble (expr, DResult, String("LELFunctionReal1D"), shape, False, suppress)) ok = False; } } // //************************************************************************ // // LELFunctionFloat // { cout << endl << "LELFunctionFloat" << endl; Block arga(2); arga[0] = LatticeExprNode(bF); arga[1] = LatticeExprNode(cF); { cout << " Function min" << endl; LELFunctionFloat expr(LELFunctionEnums::MIN, arga); FResult = min(bFVal,cFVal); if (!checkFloat (expr, FResult, String("LELFunctionFloat"), shape, False, suppress)) ok = False; } { cout << " Function max" << endl; LELFunctionFloat expr(LELFunctionEnums::MAX, arga); FResult = max(bFVal,cFVal); if (!checkFloat (expr, FResult, String("LELFunctionFloat"), shape, False, suppress)) ok = False; } { cout << " Function pow" << endl; LELFunctionFloat expr(LELFunctionEnums::POW, arga); FResult = pow(bFVal,cFVal); if (!checkFloat (expr, FResult, String("LELFunctionFloat"), shape, False, suppress)) ok = False; } { cout << " Function atan2" << endl; LELFunctionFloat expr(LELFunctionEnums::ATAN2, arga); FResult = atan2(bFVal,cFVal); if (!checkFloat (expr, FResult, String("LELFunctionFloat"), shape, False, suppress)) ok = False; } { cout << " Function fmod" << endl; LELFunctionFloat expr(LELFunctionEnums::FMOD, arga); FResult = fmod(bFVal,cFVal); if (!checkFloat (expr, FResult, String("LELFunctionFloat"), shape, False, suppress)) ok = False; } Block argb(1); argb[0] = LatticeExprNode(bC); { cout << " Function abs" << endl; LELFunctionFloat expr(LELFunctionEnums::ABS, argb); FResult = abs(bCVal); if (!checkFloat (expr, FResult, String("LELFunctionFloat"), shape, False, suppress)) ok = False; } { cout << " Function arg" << endl; LELFunctionFloat expr(LELFunctionEnums::ARG, argb); FResult = Float(arg(bCVal)); if (!checkFloat (expr, FResult, String("LELFunctionFloat"), shape, False, suppress)) ok = False; } { cout << " Function real" << endl; LELFunctionFloat expr(LELFunctionEnums::REAL, argb); FResult = real(bCVal); if (!checkFloat (expr, FResult, String("LELFunctionFloat"), shape, False, suppress)) ok = False; } { cout << " Function imag" << endl; LELFunctionFloat expr(LELFunctionEnums::IMAG, argb); FResult = imag(bCVal); if (!checkFloat (expr, FResult, String("LELFunctionFloat"), shape, False, suppress)) ok = False; } { cout << " Function fractile" << endl; Block arg(2); arg[0] = LatticeExprNode(bF); arg[1] = LatticeExprNode(Float(0.5)); LELFunctionFloat expr(LELFunctionEnums::FRACTILE1D, arg); bF.getSlice(FArr, IPosition(FArr.ndim(),0), FArr.shape(), IPosition(FArr.ndim(),1)); FResult = fractile(FArr, 0.5); if (!checkFloat (expr, FResult, String("LELFunctionFloat"), shape, True, suppress)) ok = False; } { cout << " Function fractilerange 2" << endl; Block arg(2); arg[0] = LatticeExprNode(bF); arg[1] = LatticeExprNode(Float(0.2)); LELFunctionFloat expr(LELFunctionEnums::FRACTILERANGE1D, arg); bF.getSlice(FArr, IPosition(FArr.ndim(),0), FArr.shape(), IPosition(FArr.ndim(),1)); FResult = fractile(FArr, 0.8) - fractile(FArr, 0.2); if (!checkFloat (expr, FResult, String("LELFunctionFloat"), shape, True, suppress)) ok = False; } { cout << " Function fractilerange 3" << endl; Block arg(3); arg[0] = LatticeExprNode(bF); arg[1] = LatticeExprNode(Float(0.2)); arg[2] = LatticeExprNode(Float(0.7)); LELFunctionFloat expr(LELFunctionEnums::FRACTILERANGE1D, arg); bF.getSlice(FArr, IPosition(FArr.ndim(),0), FArr.shape(), IPosition(FArr.ndim(),1)); FResult = fractile(FArr, 0.7) - fractile(FArr, 0.2); if (!checkFloat (expr, FResult, String("LELFunctionFloat"), shape, True, suppress)) ok = False; } } // //************************************************************************ // // LELFunctionDouble // { cout << endl << "LELFunctionDouble" << endl; Block arga(2); arga[0] = LatticeExprNode(bD); arga[1] = LatticeExprNode(cD); { cout << " Function min" << endl; LELFunctionDouble expr(LELFunctionEnums::MIN, arga); DResult = min(bDVal,cDVal); if (!checkDouble(expr, DResult, String("LELFunctionDouble"), shape, False, suppress)) ok = False; } { cout << " Function max" << endl; LELFunctionDouble expr(LELFunctionEnums::MAX, arga); DResult = max(bDVal,cDVal); if (!checkDouble(expr, DResult, String("LELFunctionDouble"), shape, False, suppress)) ok = False; } { cout << " Function pow" << endl; LELFunctionDouble expr(LELFunctionEnums::POW, arga); DResult = pow(bDVal,cDVal); if (!checkDouble(expr, DResult, String("LELFunctionDouble"), shape, False, suppress)) ok = False; } { cout << " Function atan2" << endl; LELFunctionDouble expr(LELFunctionEnums::ATAN2, arga); DResult = atan2(bDVal,cDVal); if (!checkDouble(expr, DResult, String("LELFunctionDouble"), shape, False, suppress)) ok = False; } { cout << " Function fmod" << endl; LELFunctionDouble expr(LELFunctionEnums::FMOD, arga); DResult = fmod(bDVal,cDVal); if (!checkDouble(expr, DResult, String("LELFunctionDouble"), shape, False, suppress)) ok = False; } Block argb(1); argb[0] = LatticeExprNode(bDC); { cout << " Function abs" << endl; DResult = abs(bDCVal); LELFunctionDouble expr(LELFunctionEnums::ABS, argb); if (!checkDouble(expr, DResult, String("LELFunctionDouble"), shape, False, suppress)) ok = False; } { cout << " Function arg" << endl; LELFunctionDouble expr(LELFunctionEnums::ARG, argb); DResult = Double(arg(bDCVal)); if (!checkDouble(expr, DResult, String("LELFunctionDouble"), shape, False, suppress)) ok = False; } { cout << " Function real" << endl; LELFunctionDouble expr(LELFunctionEnums::REAL, argb); DResult = real(bDCVal); if (!checkDouble(expr, DResult, String("LELFunctionDouble"), shape, False, suppress)) ok = False; } { cout << " Function imag" << endl; LELFunctionDouble expr(LELFunctionEnums::IMAG, argb); DResult = imag(bDCVal); if (!checkDouble(expr, DResult, String("LELFunctionDouble"), shape, False, suppress)) ok = False; } Block argc(1); argc[0] = LatticeExprNode(bB); { cout << " Function ntrue" << endl; LELFunctionDouble expr(LELFunctionEnums::NTRUE, argc); if (bBVal) { DResult = shape.product(); } else { DResult = 0.0; } if (!checkDouble(expr, DResult, String("LELFunctionDouble"), shape, True, suppress)) ok = False; } { cout << " Function nfalse" << endl; LELFunctionDouble expr(LELFunctionEnums::NFALSE, argc); if (!bBVal) { DResult = shape.product(); } else { DResult = 0.0; } if (!checkDouble(expr, DResult, String("LELFunctionDouble"), shape, True, suppress)) ok = False; } { cout << " Function nelements" << endl; LELFunctionDouble expr(LELFunctionEnums::NELEM, argc); DResult = shape.product(); if (!checkDouble(expr, DResult, String("LELFunctionDouble"), shape, True, suppress)) ok = False; } { cout << " Function fractile" << endl; Block arg(2); arg[0] = LatticeExprNode(bD); arg[1] = LatticeExprNode(Float(0.5)); LELFunctionDouble expr(LELFunctionEnums::FRACTILE1D, arg); bD.getSlice(DArr, IPosition(DArr.ndim(),0), DArr.shape(), IPosition(DArr.ndim(),1)); DResult = fractile(DArr, 0.5); if (!checkDouble (expr, DResult, String("LELFunctionDouble"), shape, True, suppress)) ok = False; } { cout << " Function fractilerange 2" << endl; Block arg(2); arg[0] = LatticeExprNode(bD); arg[1] = LatticeExprNode(Float(0.2)); LELFunctionDouble expr(LELFunctionEnums::FRACTILERANGE1D, arg); bD.getSlice(DArr, IPosition(DArr.ndim(),0), DArr.shape(), IPosition(DArr.ndim(),1)); DResult = fractile(DArr, 0.8) - fractile(DArr, 0.2); if (!checkDouble (expr, DResult, String("LELFunctionDouble"), shape, True, suppress)) ok = False; } { cout << " Function fractilerange 3" << endl; Block arg(3); arg[0] = LatticeExprNode(bD); arg[1] = LatticeExprNode(Float(0.2)); arg[2] = LatticeExprNode(Float(0.7)); LELFunctionDouble expr(LELFunctionEnums::FRACTILERANGE1D, arg); bD.getSlice(DArr, IPosition(DArr.ndim(),0), DArr.shape(), IPosition(DArr.ndim(),1)); DResult = fractile(DArr, 0.7) - fractile(DArr, 0.2); if (!checkDouble (expr, DResult, String("LELFunctionDouble"), shape, True, suppress)) ok = False; } } // //************************************************************************ // // LELFunctionComplex // { cout << endl << "LELFunctionComplex" << endl; Block arga(2); arga[0] = LatticeExprNode(bC); arga[1] = LatticeExprNode(cC); { cout << " Function pow" << endl; LELFunctionComplex expr(LELFunctionEnums::POW, arga); CResult = pow(bCVal,cCVal); if (!checkComplex(expr, CResult, String("LELFunctionComplex"), shape, False, suppress)) ok = False; } Block argb(1); argb[0] = LatticeExprNode(bC); { cout << " Function conj" << endl; LELFunctionComplex expr(LELFunctionEnums::CONJ, argb); CResult = conj(bCVal); if (!checkComplex(expr, CResult, String("LELFunctionComplex"), shape, False, suppress)) ok = False; } } // //************************************************************************ // // LELFunctionDComplex // { cout << endl << "LELFunctionDComplex" << endl; Block arga(2); arga[0] = LatticeExprNode(bDC); arga[1] = LatticeExprNode(cDC); { cout << " Function pow" << endl; LELFunctionDComplex expr(LELFunctionEnums::POW, arga); DCResult = pow(bDCVal,cDCVal); if (!checkDComplex(expr, DCResult, String("LELFunctionDComplex"), shape, False, suppress)) ok = False; } Block argb(1); argb[0] = LatticeExprNode(bDC); { cout << " Function conj" << endl; LELFunctionDComplex expr(LELFunctionEnums::CONJ, argb); DCResult = conj(bDCVal); if (!checkDComplex(expr, DCResult, String("LELFunctionDComplex"), shape, False, suppress)) ok = False; } } // //************************************************************************ // // LELFunctionBool // { cout << endl << "LELFunctionBool" << endl; Block arga(1); arga[0] = LatticeExprNode(bB); { cout << " Function all" << endl; LELFunctionBool expr(LELFunctionEnums::ALL, arga); BResult = bBVal; if (!checkBool(expr, BResult, String("LELFunctionBool"), shape, True, suppress)) ok = False; } { cout << " Function any" << endl; LELFunctionBool expr(LELFunctionEnums::ANY, arga); BResult = bBVal; if (!checkBool(expr, BResult, String("LELFunctionBool"), shape, True, suppress)) ok = False; } { Block argb(1); cout << " Function isNaN" << endl; { argb[0] = LatticeExprNode(bF); LELFunctionBool expr(LELFunctionEnums::ISNAN, argb); BResult = False; if (!checkBool(expr, BResult, String("LELFunctionBool"), shape, False, suppress)) ok = False; } { argb[0] = LatticeExprNode(nanF); LELFunctionBool expr(LELFunctionEnums::ISNAN, argb); BResult = True; if (!checkBool(expr, BResult, String("LELFunctionBool"), shape, False, suppress)) ok = False; } { argb[0] = LatticeExprNode(bD); LELFunctionBool expr(LELFunctionEnums::ISNAN, argb); BResult = False; if (!checkBool(expr, BResult, String("LELFunctionBool"), shape, False, suppress)) ok = False; } { argb[0] = LatticeExprNode(bC); LELFunctionBool expr(LELFunctionEnums::ISNAN, argb); BResult = False; if (!checkBool(expr, BResult, String("LELFunctionBool"), shape, False, suppress)) ok = False; } { argb[0] = LatticeExprNode(bDC); LELFunctionBool expr(LELFunctionEnums::ISNAN, argb); BResult = False; if (!checkBool(expr, BResult, String("LELFunctionBool"), shape, False, suppress)) ok = False; } } { Block argb(2); cout << " Function indexin" << endl; { Vector flags(2,True); argb[0] = LatticeExprNode(0); argb[1] = LatticeExprNode(ArrayLattice(flags)); LELFunctionBool expr(LELFunctionEnums::INDEXIN, argb); BResult = True; if (!checkBool (expr, BResult, "LELFunctionBool", IPosition(2,2,4), False, suppress, True)) ok = False; } { Vector flags(2,True); argb[0] = LatticeExprNode(1); argb[1] = LatticeExprNode(ArrayLattice(flags)); LELFunctionBool expr(LELFunctionEnums::INDEXIN, argb); BResult = True; if (!checkBool (expr, BResult, "LELFunctionBool", IPosition(2,10,2), False, suppress, True)) ok = False; } { Vector flags(3,False); argb[0] = LatticeExprNode(0); argb[1] = LatticeExprNode(ArrayLattice(flags)); LELFunctionBool expr(LELFunctionEnums::INDEXIN, argb); BResult = False; if (!checkBool (expr, BResult, "LELFunctionBool", IPosition(2,6,2), False, suppress, True)) ok = False; if (!checkBool (expr, BResult, "LELFunctionBool", IPosition(2,2,6), False, suppress, True)) ok = False; } } } // //************************************************************************ // // LELConvert // { { cout << endl << "LELConvert " << endl; auto pExpr = std::make_shared>(bD); LELConvert expr(pExpr); FResult = Float(bDVal); if (!checkFloat (expr, FResult, String("LELConvert"), shape, False, suppress)) ok = False; } { cout << "LELConvert " << endl; auto pExpr = std::make_shared>(bF); LELConvert expr(pExpr); DResult = Double(bFVal); if (!checkDouble(expr, DResult, String("LELConvert"), shape, False, suppress)) ok = False; } { cout << "LELConvert " << endl; auto pExpr = std::make_shared>(bDC); LELConvert expr(pExpr); CResult = Complex(bDCVal.real(), bDCVal.imag()); if (!checkComplex(expr, CResult, String("LELConvert"), shape, False, suppress)) ok = False; } { cout << "LELConvert " << endl; auto pExpr = std::make_shared>(bC); LELConvert expr(pExpr); DCResult = bCVal; if (!checkDComplex(expr, DCResult, String("LELConvert"), shape, False, suppress)) ok = False; } { cout << "LELConvert " << endl; auto pExpr = std::make_shared>(bF); LELConvert expr(pExpr); CResult = Complex(bFVal,0.0); if (!checkComplex(expr, CResult, String("LELConvert"), shape, False, suppress)) ok = False; } { cout << "LELConvert " << endl; auto pExpr = std::make_shared>(bD); LELConvert expr(pExpr); CResult = Complex(bDVal,0.0); if (!checkComplex(expr, CResult, String("LELConvert"), shape, False, suppress)) ok = False; } { cout << "LELConvert " << endl; auto pExpr = std::make_shared>(bF); LELConvert expr(pExpr); DCResult = DComplex(bFVal,0.0); if (!checkDComplex(expr, DCResult, String("LELConvert"), shape, False, suppress)) ok = False; } { cout << "LELConvert " << endl; auto pExpr = std::make_shared>(bD); LELConvert expr(pExpr); DCResult = DComplex(bDVal,0.0); if (!checkDComplex(expr, DCResult, String("LELConvert"), shape, False, suppress)) ok = False; } } if (!ok) { cout << "not ok" << endl; return 1; } else { cout << endl << "ok" << endl; } } catch (std::exception& x) { cerr << "aipserror: error " << x.what() << endl; return 1; } return 0; } Bool checkFloat (LELInterface& expr, const Float Result, const String& name, const IPosition& shape, const Bool shouldBeScalar, const Bool suppress) { LELArray Arr(shape); Bool ok = True; IPosition origin(2,0,0); Slicer region(origin, shape); if (expr.className() != name) { cout << " Class name is wrong" << endl; ok = False; } if (shouldBeScalar) { if (!expr.isScalar()) { cout << " Expression is not a scalar but should be" << endl; ok = False; } if (expr.shape() != IPosition()) { cout << " Expression has wrong shape" << endl; ok = False; } if (expr.getScalar().value() != Result) { cout << " Result should be " << Result << endl; cout << " Result is " << expr.getScalar().value() << endl; ok = False; } try { expr.eval(Arr, region); } catch (std::exception& x) { if (!suppress) cout << " Caught expected exception; message is: " << x.what() << endl; } } else { if (expr.isScalar()) { cout << " Expression is a scalar but shouldn't be" << endl; ok = False; } if (expr.shape() != shape) { cout << " Expression has wrong shape" << endl; ok = False; } expr.eval(Arr, region); if (!allEQ (Arr.value(), Result)) { cout << " Result should be " << Result << endl; cout << " Result is " << Arr.value()(origin) << endl; ok = False; } try { expr.getScalar(); } catch (std::exception& x) { if (!suppress) cout << " Caught expected exception; message is: " << x.what() << endl; } } expr.prepareScalarExpr(); return ok; } Bool checkDouble (LELInterface& expr, const Double Result, const String& name, const IPosition& shape, const Bool shouldBeScalar, const Bool suppress) { LELArray Arr(shape); Bool ok = True; IPosition origin(2,0,0); Slicer region(origin, shape); if (expr.className() != name) { cout << " Class name is wrong" << endl; ok = False; } if (shouldBeScalar) { if (!expr.isScalar()) { cout << " Expression is not a scalar but should be" << endl; ok = False; } if (expr.shape() != IPosition()) { cout << " Expression has wrong shape" << endl; ok = False; } if (expr.getScalar().value() != Result) { cout << " Result should be " << Result << endl; cout << " Result is " << expr.getScalar().value() << endl; ok = False; } try { expr.eval(Arr, region); } catch (std::exception& x) { if (!suppress) cout << " Caught expected exception; message is: " << x.what() << endl; } } else { if (expr.isScalar()) { cout << " Expression is a scalar but shouldn't be" << endl; ok = False; } if (expr.shape() != shape) { cout << " Expression has wrong shape" << endl; ok = False; } expr.eval(Arr, region); if (!allEQ (Arr.value(), Result)) { cout << " Result should be " << Result << endl; cout << " Result is " << Arr.value()(origin) << endl; ok = False; } try { expr.getScalar(); } catch (std::exception& x) { if (!suppress) cout << " Caught expected exception; message is: " << x.what() << endl; } } expr.prepareScalarExpr(); return ok; } Bool checkComplex (LELInterface& expr, const Complex& Result, const String& name, const IPosition& shape, const Bool shouldBeScalar, const Bool suppress) { LELArray Arr(shape); Bool ok = True; IPosition origin(2,0,0); Slicer region(origin, shape); if (expr.className() != name) { cout << " Class name is wrong" << endl; ok = False; } if (shouldBeScalar) { if (!expr.isScalar()) { cout << " Expression is not a scalar but should be" << endl; ok = False; } if (expr.shape() != IPosition()) { cout << " Expression has wrong shape" << endl; ok = False; } if (expr.getScalar().value() != Result) { cout << " Result should be " << Result << endl; cout << " Result is " << expr.getScalar().value() << endl; ok = False; } try { expr.eval(Arr, region); } catch (std::exception& x) { if (!suppress) cout << " Caught expected exception; message is: " << x.what() << endl; } } else { if (expr.isScalar()) { cout << " Expression is a scalar but shouldn't be" << endl; ok = False; } if (expr.shape() != shape) { cout << " Expression has wrong shape" << endl; ok = False; } expr.eval(Arr, region); if (!allEQ (Arr.value(), Result)) { cout << " Result should be " << Result << endl; cout << " Result is " << Arr.value()(origin) << endl; ok = False; } try { expr.getScalar(); } catch (std::exception& x) { if (!suppress) cout << " Caught expected exception; message is: " << x.what() << endl; } } expr.prepareScalarExpr(); return ok; } Bool checkDComplex (LELInterface& expr, const DComplex& Result, const String& name, const IPosition& shape, const Bool shouldBeScalar, const Bool suppress) { LELArray Arr(shape); Bool ok = True; IPosition origin(2,0,0); Slicer region(origin, shape); if (expr.className() != name) { cout << " Class name is wrong" << endl; ok = False; } if (shouldBeScalar) { if (!expr.isScalar()) { cout << " Expression is not a scalar but should be" << endl; ok = False; } if (expr.shape() != IPosition()) { cout << " Expression has wrong shape" << endl; ok = False; } if (expr.getScalar().value() != Result) { cout << " Result should be " << Result << endl; cout << " Result is " << expr.getScalar().value() << endl; ok = False; } try { expr.eval(Arr, region); } catch (std::exception& x) { if (!suppress) cout << " Caught expected exception; message is: " << x.what() << endl; } } else { if (expr.isScalar()) { cout << " Expression is a scalar but shouldn't be" << endl; ok = False; } if (expr.shape() != shape) { cout << " Expression has wrong shape" << endl; ok = False; } expr.eval(Arr, region); if (!allEQ (Arr.value(), Result)) { cout << " Result should be " << Result << endl; cout << " Result is " << Arr.value()(origin) << endl; ok = False; } try { expr.getScalar(); } catch (std::exception& x) { if (!suppress) cout << " Caught expected exception; message is: " << x.what() << endl; } } expr.prepareScalarExpr(); return ok; } Bool checkBool (LELInterface& expr, const Bool Result, const String& name, const IPosition& shape, const Bool shouldBeScalar, const Bool suppress, const Bool emptyShape) { LELArray Arr(shape); Bool ok = True; IPosition origin(2,0,0); Slicer region(origin, shape); if (expr.className() != name) { cout << " Class name is wrong" << endl; ok = False; } if (shouldBeScalar) { if (!expr.isScalar()) { cout << " Expression is not a scalar but should be" << endl; ok = False; } if (expr.shape() != IPosition()) { cout << " Expression has wrong shape" << endl; ok = False; } if (expr.getScalar().value() != Result) { cout << " Result should be " << Result << endl; cout << " Result is " << expr.getScalar().value() << endl; ok = False; } try { expr.eval(Arr, region); } catch (std::exception& x) { if (!suppress) cout << " Caught expected exception; message is: " << x.what() << endl; } } else { if (expr.isScalar()) { cout << " Expression is a scalar but shouldn't be" << endl; ok = False; } if (emptyShape) { if (expr.shape() != IPosition()) { cout << " Expression has no empty shape" << endl; ok = False; } } else { if (expr.shape() != shape) { cout << " Expression has wrong shape" << endl; ok = False; } } expr.eval(Arr, region); if (!allEQ (Arr.value(), Result)) { cout << " Result should be " << Result << endl; cout << " Result is " << Arr.value()(origin) << endl; ok = False; } try { expr.getScalar(); } catch (std::exception& x) { if (!suppress) cout << " Caught expected exception; message is: " << x.what() << endl; } } expr.prepareScalarExpr(); return ok; } Bool checkAttribute (const LELAttribute& attr, const Bool isMasked, const Bool isScalar, const IPosition& shape, const IPosition& tileShape, const LELCoordinates& lattCoord) { Bool ok = True; if (attr.isMasked() != isMasked) { cout << " isMasked function failed" << endl; ok = False; } if (attr.isScalar() != isScalar) { cout << " isScalar function failed" << endl; ok = False; } if (attr.shape() != shape) { cout << " shape function failed" << endl; ok = False; } if (attr.tileShape() != tileShape) { cout << " tileShape function failed" << endl; ok = False; } if (attr.coordinates().compare(lattCoord) != 0) { cout << " coordinates function failed" << endl; ok = False; } return ok; } casacore-3.7.1/lattices/LEL/test/tLEL.out000066400000000000000000000472131476623553700200670ustar00rootroot00000000000000>>> /export/home/gvd/aips++/sun4sol_egcs/bindbg/tLEL: Version <<< LELAttribute LELLattice Caught expected exception; message is: LELLattice::getScalar - cannot be used LELLattice Caught expected exception; message is: LELLattice::getScalar - cannot be used LELLattice Caught expected exception; message is: LELLattice::getScalar - cannot be used LELLattice Caught expected exception; message is: LELLattice::getScalar - cannot be used LELLattice Caught expected exception; message is: LELLattice::getScalar - cannot be used LELUnaryConst Caught expected exception; message is: LELUnaryConst::eval - cannot be used LELUnaryConst Caught expected exception; message is: LELUnaryConst::eval - cannot be used LELUnaryConst Caught expected exception; message is: LELUnaryConst::eval - cannot be used LELUnaryConst Caught expected exception; message is: LELUnaryConst::eval - cannot be used LELUnaryConst Caught expected exception; message is: LELUnaryConst::eval - cannot be used LELUnary Operator - Caught expected exception; message is: LELLattice::getScalar - cannot be used LELUnary Operator - Caught expected exception; message is: LELLattice::getScalar - cannot be used LELUnary Operator - Caught expected exception; message is: LELLattice::getScalar - cannot be used LELUnary Operator - Caught expected exception; message is: LELLattice::getScalar - cannot be used LELUnaryBool Operator ! Caught expected exception; message is: LELLattice::getScalar - cannot be used LELBinary Operator + Caught expected exception; message is: LELLattice::getScalar - cannot be used Operator - Caught expected exception; message is: LELLattice::getScalar - cannot be used Operator * Caught expected exception; message is: LELLattice::getScalar - cannot be used Operator / Caught expected exception; message is: LELLattice::getScalar - cannot be used LELBinary Operator + Caught expected exception; message is: LELLattice::getScalar - cannot be used Operator - Caught expected exception; message is: LELLattice::getScalar - cannot be used Operator * Caught expected exception; message is: LELLattice::getScalar - cannot be used Operator / Caught expected exception; message is: LELLattice::getScalar - cannot be used LELBinary Operator + Caught expected exception; message is: LELLattice::getScalar - cannot be used Operator - Caught expected exception; message is: LELLattice::getScalar - cannot be used Operator * Caught expected exception; message is: LELLattice::getScalar - cannot be used Operator / Caught expected exception; message is: LELLattice::getScalar - cannot be used LELBinary Operator + Caught expected exception; message is: LELLattice::getScalar - cannot be used Operator - Caught expected exception; message is: LELLattice::getScalar - cannot be used Operator * Caught expected exception; message is: LELLattice::getScalar - cannot be used Operator / Caught expected exception; message is: LELLattice::getScalar - cannot be used LELBinaryCmp Operator == Caught expected exception; message is: LELLattice::getScalar - cannot be used Operator != Caught expected exception; message is: LELLattice::getScalar - cannot be used Operator > Caught expected exception; message is: LELLattice::getScalar - cannot be used Operator >= Caught expected exception; message is: LELLattice::getScalar - cannot be used LELBinaryCmp Operator == Caught expected exception; message is: LELLattice::getScalar - cannot be used Operator != Caught expected exception; message is: LELLattice::getScalar - cannot be used Operator > Caught expected exception; message is: LELLattice::getScalar - cannot be used Operator >= Caught expected exception; message is: LELLattice::getScalar - cannot be used LELBinaryCmp Operator == Caught expected exception; message is: LELLattice::getScalar - cannot be used Operator != Caught expected exception; message is: LELLattice::getScalar - cannot be used Operator > Caught expected exception; message is: LELLattice::getScalar - cannot be used Operator >= Caught expected exception; message is: LELLattice::getScalar - cannot be used LELBinaryCmp Operator == Caught expected exception; message is: LELLattice::getScalar - cannot be used Operator != Caught expected exception; message is: LELLattice::getScalar - cannot be used Operator > Caught expected exception; message is: LELLattice::getScalar - cannot be used Operator >= Caught expected exception; message is: LELLattice::getScalar - cannot be used LELBinaryBool Operator == Caught expected exception; message is: LELLattice::getScalar - cannot be used Operator != Caught expected exception; message is: LELLattice::getScalar - cannot be used Operator && Caught expected exception; message is: LELLattice::getScalar - cannot be used LELFunction1D Function sin Caught expected exception; message is: LELLattice::getScalar - cannot be used Function sinh Caught expected exception; message is: LELLattice::getScalar - cannot be used Function cos Caught expected exception; message is: LELLattice::getScalar - cannot be used Function cosh Caught expected exception; message is: LELLattice::getScalar - cannot be used Function exp Caught expected exception; message is: LELLattice::getScalar - cannot be used Function log Caught expected exception; message is: LELLattice::getScalar - cannot be used Function log10 Caught expected exception; message is: LELLattice::getScalar - cannot be used Function sqrt Caught expected exception; message is: LELLattice::getScalar - cannot be used Function min Caught expected exception; message is: LELFunction1D::eval - unknown function Function max Caught expected exception; message is: LELFunction1D::eval - unknown function Function median Caught expected exception; message is: LELFunctionReal1D::eval - unknown function Function mean Caught expected exception; message is: LELFunction1D::eval - unknown function Function sum Caught expected exception; message is: LELFunction1D::eval - unknown function LELFunction1D Function sin Caught expected exception; message is: LELLattice::getScalar - cannot be used Function sinh Caught expected exception; message is: LELLattice::getScalar - cannot be used Function cos Caught expected exception; message is: LELLattice::getScalar - cannot be used Function cosh Caught expected exception; message is: LELLattice::getScalar - cannot be used Function exp Caught expected exception; message is: LELLattice::getScalar - cannot be used Function log Caught expected exception; message is: LELLattice::getScalar - cannot be used Function log10 Caught expected exception; message is: LELLattice::getScalar - cannot be used Function sqrt Caught expected exception; message is: LELLattice::getScalar - cannot be used Function min Caught expected exception; message is: LELFunction1D::eval - unknown function Function max Caught expected exception; message is: LELFunction1D::eval - unknown function Function median Caught expected exception; message is: LELFunctionReal1D::eval - unknown function Function mean Caught expected exception; message is: LELFunction1D::eval - unknown function Function sum Caught expected exception; message is: LELFunction1D::eval - unknown function LELFunction1D Function sin Caught expected exception; message is: LELLattice::getScalar - cannot be used Function sinh Caught expected exception; message is: LELLattice::getScalar - cannot be used Function cos Caught expected exception; message is: LELLattice::getScalar - cannot be used Function cosh Caught expected exception; message is: LELLattice::getScalar - cannot be used Function exp Caught expected exception; message is: LELLattice::getScalar - cannot be used Function log Caught expected exception; message is: LELLattice::getScalar - cannot be used Function log10 Caught expected exception; message is: LELLattice::getScalar - cannot be used Function sqrt Caught expected exception; message is: LELLattice::getScalar - cannot be used Function min Caught expected exception; message is: LELFunction1D::eval - unknown function Function max Caught expected exception; message is: LELFunction1D::eval - unknown function LELFunction1D Function sin Caught expected exception; message is: LELLattice::getScalar - cannot be used Function sinh Caught expected exception; message is: LELLattice::getScalar - cannot be used Function cos Caught expected exception; message is: LELLattice::getScalar - cannot be used Function cosh Caught expected exception; message is: LELLattice::getScalar - cannot be used Function exp Caught expected exception; message is: LELLattice::getScalar - cannot be used Function log Caught expected exception; message is: LELLattice::getScalar - cannot be used Function log10 Caught expected exception; message is: LELLattice::getScalar - cannot be used Function sqrt Caught expected exception; message is: LELLattice::getScalar - cannot be used Function min Caught expected exception; message is: LELFunction1D::eval - unknown function Function max Caught expected exception; message is: LELFunction1D::eval - unknown function LELFunctionND Function iif Scalar, scalar, scalar Scalar, scalar, array Caught expected exception; message is: LELLattice::getScalar - cannot be used Scalar, array, scalar Caught expected exception; message is: LELLattice::getScalar - cannot be used Array, scalar, scalar Caught expected exception; message is: LELLattice::getScalar - cannot be used Caught expected exception; message is: LELLattice::getScalar - cannot be used Array, Array, scalar Caught expected exception; message is: LELLattice::getScalar - cannot be used Caught expected exception; message is: LELLattice::getScalar - cannot be used Array, scalar, array Caught expected exception; message is: LELLattice::getScalar - cannot be used Caught expected exception; message is: LELLattice::getScalar - cannot be used LELFunctionND Function iif Scalar, scalar, scalar Scalar, scalar, array Caught expected exception; message is: LELLattice::getScalar - cannot be used Scalar, array, scalar Caught expected exception; message is: LELLattice::getScalar - cannot be used Array, scalar, scalar Caught expected exception; message is: LELLattice::getScalar - cannot be used Caught expected exception; message is: LELLattice::getScalar - cannot be used Array, Array, scalar Caught expected exception; message is: LELLattice::getScalar - cannot be used Caught expected exception; message is: LELLattice::getScalar - cannot be used Array, scalar, array Caught expected exception; message is: LELLattice::getScalar - cannot be used Caught expected exception; message is: LELLattice::getScalar - cannot be used LELFunctionND Function iif Scalar, scalar, scalar Scalar, scalar, array Caught expected exception; message is: LELLattice::getScalar - cannot be used Scalar, array, scalar Caught expected exception; message is: LELLattice::getScalar - cannot be used Array, scalar, scalar Caught expected exception; message is: LELLattice::getScalar - cannot be used Caught expected exception; message is: LELLattice::getScalar - cannot be used Array, Array, scalar Caught expected exception; message is: LELLattice::getScalar - cannot be used Caught expected exception; message is: LELLattice::getScalar - cannot be used Array, scalar, array Caught expected exception; message is: LELLattice::getScalar - cannot be used Caught expected exception; message is: LELLattice::getScalar - cannot be used LELFunctionND Function iif Scalar, scalar, scalar Scalar, scalar, array Caught expected exception; message is: LELLattice::getScalar - cannot be used Scalar, array, scalar Caught expected exception; message is: LELLattice::getScalar - cannot be used Array, scalar, scalar Caught expected exception; message is: LELLattice::getScalar - cannot be used Caught expected exception; message is: LELLattice::getScalar - cannot be used Array, Array, scalar Caught expected exception; message is: LELLattice::getScalar - cannot be used Caught expected exception; message is: LELLattice::getScalar - cannot be used Array, scalar, array Caught expected exception; message is: LELLattice::getScalar - cannot be used Caught expected exception; message is: LELLattice::getScalar - cannot be used LELFunctionReal1D Function asin Caught expected exception; message is: LELLattice::getScalar - cannot be used Function acos Caught expected exception; message is: LELLattice::getScalar - cannot be used Function tan Caught expected exception; message is: LELLattice::getScalar - cannot be used Function tanh Caught expected exception; message is: LELLattice::getScalar - cannot be used Function ceil Caught expected exception; message is: LELLattice::getScalar - cannot be used Function floor Caught expected exception; message is: LELLattice::getScalar - cannot be used LELFunctionReal1D Function asin Caught expected exception; message is: LELLattice::getScalar - cannot be used Function acos Caught expected exception; message is: LELLattice::getScalar - cannot be used Function tan Caught expected exception; message is: LELLattice::getScalar - cannot be used Function tanh Caught expected exception; message is: LELLattice::getScalar - cannot be used Function ceil Caught expected exception; message is: LELLattice::getScalar - cannot be used Function floor Caught expected exception; message is: LELLattice::getScalar - cannot be used LELFunctionFloat Function min Caught expected exception; message is: LELLattice::getScalar - cannot be used Function max Caught expected exception; message is: LELLattice::getScalar - cannot be used Function pow Caught expected exception; message is: LELLattice::getScalar - cannot be used Function atan2 Caught expected exception; message is: LELLattice::getScalar - cannot be used Function fmod Caught expected exception; message is: LELLattice::getScalar - cannot be used Function abs Caught expected exception; message is: LELLattice::getScalar - cannot be used Function arg Caught expected exception; message is: LELLattice::getScalar - cannot be used Function real Caught expected exception; message is: LELLattice::getScalar - cannot be used Function imag Caught expected exception; message is: LELLattice::getScalar - cannot be used Function fractile Caught expected exception; message is: LELFunctionFloat::eval - unknown Float function Function fractilerange 2 Caught expected exception; message is: LELFunctionFloat::eval - unknown Float function Function fractilerange 3 Caught expected exception; message is: LELFunctionFloat::eval - unknown Float function LELFunctionDouble Function min Caught expected exception; message is: LELLattice::getScalar - cannot be used Function max Caught expected exception; message is: LELLattice::getScalar - cannot be used Function pow Caught expected exception; message is: LELLattice::getScalar - cannot be used Function atan2 Caught expected exception; message is: LELLattice::getScalar - cannot be used Function fmod Caught expected exception; message is: LELLattice::getScalar - cannot be used Function abs Caught expected exception; message is: LELLattice::getScalar - cannot be used Function arg Caught expected exception; message is: LELLattice::getScalar - cannot be used Function real Caught expected exception; message is: LELLattice::getScalar - cannot be used Function imag Caught expected exception; message is: LELLattice::getScalar - cannot be used Function ntrue Caught expected exception; message is: LELFunctionDouble::eval - unknown Double function Function nfalse Caught expected exception; message is: LELFunctionDouble::eval - unknown Double function Function nelements Caught expected exception; message is: LELFunctionDouble::eval - unknown Double function Function fractile Caught expected exception; message is: LELFunctionDouble::eval - unknown Double function Function fractilerange 2 Caught expected exception; message is: LELFunctionDouble::eval - unknown Double function Function fractilerange 3 Caught expected exception; message is: LELFunctionDouble::eval - unknown Double function LELFunctionComplex Function pow Caught expected exception; message is: LELLattice::getScalar - cannot be used Function conj Caught expected exception; message is: LELLattice::getScalar - cannot be used LELFunctionDComplex Function pow Caught expected exception; message is: LELLattice::getScalar - cannot be used Function conj Caught expected exception; message is: LELLattice::getScalar - cannot be used LELFunctionBool Function all Caught expected exception; message is: LELFunctionBool::eval - unknown function Function any Caught expected exception; message is: LELFunctionBool::eval - unknown function Function isNaN Caught expected exception; message is: LELLattice::getScalar - cannot be used Caught expected exception; message is: LELLattice::getScalar - cannot be used Caught expected exception; message is: LELLattice::getScalar - cannot be used Caught expected exception; message is: LELLattice::getScalar - cannot be used Caught expected exception; message is: LELLattice::getScalar - cannot be used Function indexin Caught expected exception; message is: LELFunctionBool::getScalar - unknown function Caught expected exception; message is: LELFunctionBool::getScalar - unknown function Caught expected exception; message is: LELFunctionBool::getScalar - unknown function Caught expected exception; message is: LELFunctionBool::getScalar - unknown function LELConvert Caught expected exception; message is: LELLattice::getScalar - cannot be used LELConvert Caught expected exception; message is: LELLattice::getScalar - cannot be used LELConvert Caught expected exception; message is: LELLattice::getScalar - cannot be used LELConvert Caught expected exception; message is: LELLattice::getScalar - cannot be used LELConvert Caught expected exception; message is: LELLattice::getScalar - cannot be used LELConvert Caught expected exception; message is: LELLattice::getScalar - cannot be used LELConvert Caught expected exception; message is: LELLattice::getScalar - cannot be used LELConvert Caught expected exception; message is: LELLattice::getScalar - cannot be used ok casacore-3.7.1/lattices/LEL/test/tLELAttribute.cc000066400000000000000000000142621476623553700215270ustar00rootroot00000000000000//# tLELAttribute.cc: Test program for class LELAttribute //# Copyright (C) 2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include void doIt() { { // Test scalar LELAttribute attr1; AlwaysAssertExit (!attr1.isMasked()); AlwaysAssertExit (attr1.isScalar()); AlwaysAssertExit (attr1.isReduced()); AlwaysAssertExit (!attr1.isRegion()); AlwaysAssertExit (attr1.shape() == IPosition()); AlwaysAssertExit (attr1.tileShape() == IPosition()); } { // Test array LELAttribute attr1(True, IPosition(1,3), IPosition(1,4), LELCoordinates()); AlwaysAssertExit (attr1.isMasked()); AlwaysAssertExit (!attr1.isScalar()); AlwaysAssertExit (!attr1.isReduced()); AlwaysAssertExit (!attr1.isRegion()); AlwaysAssertExit (attr1.shape() == IPosition(1,3)); AlwaysAssertExit (attr1.tileShape() == IPosition(1,4)); } { // Test region LELAttribute attr1(3); AlwaysAssertExit (!attr1.isMasked()); AlwaysAssertExit (!attr1.isScalar()); AlwaysAssertExit (!attr1.isReduced()); AlwaysAssertExit (attr1.isRegion()); AlwaysAssertExit (attr1.shape() == IPosition(3,0)); AlwaysAssertExit (attr1.tileShape() == IPosition()); } { // Combine scalar and array LELAttribute attr1; LELAttribute attr2(True, IPosition(1,3), IPosition(1,4), LELCoordinates()); { LELAttribute attr3 (attr1, attr2); AlwaysAssertExit (attr3.isMasked()); AlwaysAssertExit (!attr3.isScalar()); AlwaysAssertExit (!attr3.isReduced()); AlwaysAssertExit (!attr3.isRegion()); AlwaysAssertExit (attr3.shape() == IPosition(1,3)); AlwaysAssertExit (attr3.tileShape() == IPosition(1,4)); } { LELAttribute attr3 (attr2, attr1); AlwaysAssertExit (attr3.isMasked()); AlwaysAssertExit (!attr3.isScalar()); AlwaysAssertExit (!attr3.isReduced()); AlwaysAssertExit (!attr3.isRegion()); AlwaysAssertExit (attr3.shape() == IPosition(1,3)); AlwaysAssertExit (attr3.tileShape() == IPosition(1,4)); } } { // Combine scalar and scalar LELAttribute attr1; LELAttribute attr2; { LELAttribute attr3 (attr1, attr2); AlwaysAssertExit (!attr3.isMasked()); AlwaysAssertExit (attr3.isScalar()); AlwaysAssertExit (attr3.isReduced()); AlwaysAssertExit (!attr3.isRegion()); AlwaysAssertExit (attr3.shape() == IPosition()); AlwaysAssertExit (attr3.tileShape() == IPosition()); } { LELAttribute attr3 (attr2, attr1); AlwaysAssertExit (!attr3.isMasked()); AlwaysAssertExit (attr3.isScalar()); AlwaysAssertExit (attr3.isReduced()); AlwaysAssertExit (!attr3.isRegion()); AlwaysAssertExit (attr3.shape() == IPosition()); AlwaysAssertExit (attr3.tileShape() == IPosition()); } } { // Combine arrays with different shapes LELAttribute attr1(False, IPosition(2,3,5), IPosition(2,4,6), LELCoordinates(), True); LELAttribute attr2(True, IPosition(1,3), IPosition(1,4), LELCoordinates()); { LELAttribute attr3 (attr1, attr2, False); AlwaysAssertExit (attr3.isMasked()); AlwaysAssertExit (!attr3.isScalar()); AlwaysAssertExit (attr3.isReduced()); AlwaysAssertExit (!attr3.isRegion()); AlwaysAssertExit (attr3.shape() == IPosition(2,3,5)); AlwaysAssertExit (attr3.tileShape() == IPosition(2,4,6)); } { LELAttribute attr3 (attr2, attr1, False); AlwaysAssertExit (attr3.isMasked()); AlwaysAssertExit (!attr3.isScalar()); AlwaysAssertExit (attr3.isReduced()); AlwaysAssertExit (!attr3.isRegion()); AlwaysAssertExit (attr3.shape() == IPosition(2,3,5)); AlwaysAssertExit (attr3.tileShape() == IPosition(2,4,6)); } Bool caught = False; try { LELAttribute attr3 (attr2, attr1); } catch (std::exception& x) { caught = True; // mismatching axes } AlwaysAssertExit (caught); } { // Combine array with an array with unknown shape. LELAttribute attr1(False, IPosition(2,3,5), IPosition(2,4,6), LELCoordinates(), True); LELAttribute attr2(True, IPosition(), IPosition(), LELCoordinates()); { LELAttribute attr3 (attr1, attr2, True); AlwaysAssertExit (attr3.isMasked()); AlwaysAssertExit (!attr3.isScalar()); AlwaysAssertExit (attr3.isReduced()); AlwaysAssertExit (!attr3.isRegion()); AlwaysAssertExit (attr3.shape() == IPosition(2,3,5)); AlwaysAssertExit (attr3.tileShape() == IPosition(2,4,6)); } { LELAttribute attr3 (attr2, attr1, False); AlwaysAssertExit (attr3.isMasked()); AlwaysAssertExit (!attr3.isScalar()); AlwaysAssertExit (attr3.isReduced()); AlwaysAssertExit (!attr3.isRegion()); AlwaysAssertExit (attr3.shape() == IPosition(2,3,5)); AlwaysAssertExit (attr3.tileShape() == IPosition(2,4,6)); } } } int main() { try { doIt(); } catch (std::exception& x) { cout << "Caught exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/lattices/LEL/test/tLELMedian.cc000066400000000000000000000156131476623553700207620ustar00rootroot00000000000000//# tLELMedian.cc: Tests the fractile function in LELFunction //# Copyright (C) 1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include int main (int argc, const char* argv[]) { try { cout << ">>>" << endl; Input inp(1); inp.version(" "); inp.create("nx", "16", "Number of pixels along the x-axis", "int"); inp.create("ny", "16", "Number of pixels along the y-axis", "int"); inp.readArguments(argc, argv); cout << "<<<" << endl; const uInt nx=inp.getInt("nx"); const uInt ny=inp.getInt("ny"); // // Test various sized arrays. // { // Test lattice with all equal values. IPosition shape(2, nx, ny); Array arr(shape); arr = 1.; ArrayLattice aF(arr); cout << median (arr) << endl; cout << LatticeFractile::unmaskedFractile(aF, 0.5) << endl; cout << LatticeFractile::unmaskedFractile(aF, 0.5, 16) << endl; cout << LatticeFractile::unmaskedFractile(aF, 0.5, 4) << endl; cout << LatticeFractile::unmaskedFractile(aF, 0.5, 2) << endl; LatticeExprNode afExpr(aF); LatticeExpr expr(afExpr[aF>4]); cout << LatticeFractile::maskedFractile(expr, 0.5) << endl; } { IPosition shape(2, nx, ny); Array arr(shape); indgen (arr); ArrayLattice aF(arr); cout << median (arr) << endl; cout << LatticeFractile::unmaskedFractile(aF, 0.5) << endl; cout << LatticeFractile::unmaskedFractile(aF, 0.5, 16) << endl; cout << LatticeFractile::unmaskedFractile(aF, 0.5, 4) << endl; cout << LatticeFractile::unmaskedFractile(aF, 0.5, 2) << endl; LatticeExprNode afExpr(aF); LatticeExpr expr(afExpr[aF>4]); cout << LatticeFractile::maskedFractile(expr, 0.5) << endl; } { IPosition shape(2, 10*nx, 10*ny); Array arr(shape); indgen (arr); ArrayLattice aF(arr); cout << median (arr) << endl; cout << LatticeFractile::unmaskedFractile(aF, 0.5) << endl; cout << LatticeFractile::unmaskedFractile(aF, 0.5, 16) << endl; cout << LatticeFractile::unmaskedFractile(aF, 0.5, 4) << endl; cout << LatticeFractile::unmaskedFractile(aF, 0.5, 2) << endl; LatticeExprNode afExpr(aF); LatticeExpr expr(afExpr[aF>4]); cout << LatticeFractile::maskedFractile(expr, 0.5) << endl; } { IPosition shape(2, 32*nx, 32*ny); Array arr(shape); indgen (arr); ArrayLattice aF(arr); cout << median (arr) << endl; cout << LatticeFractile::unmaskedFractile(aF, 0.5) << endl; cout << LatticeFractile::unmaskedFractile(aF, 0.5, 128) << endl; LatticeExprNode afExpr(aF); LatticeExpr expr(afExpr[aF>4]); cout << LatticeFractile::maskedFractile(expr, 0.5) << endl; } // Hereafter numbers can be different on different machines. // So 'outcomment' it for assay (and also outcomment the timings). cout << ">>>" << endl; { IPosition shape(2, 100*nx, 100*ny); Array arr(shape); indgen (arr, float(0), float(0.01)); ArrayLattice aF(arr); cout << "Last value = " << arr(shape-1) << endl; Timer timer; cout << median (arr) << endl; timer.show ("normal median"); timer.mark(); cout << LatticeFractile::unmaskedFractile(aF, 0.5) << endl; timer.show ("ArrayLattice "); timer.mark(); cout << LatticeFractile::unmaskedFractile(aF, 0.5, 1280) << endl; timer.show ("ArrayLat 1280"); timer.mark(); LatticeExprNode afExpr(aF); LatticeExpr expr(afExpr[aF>4]); cout << LatticeFractile::maskedFractile(expr, 0.5) << endl; timer.show ("MaskedLattice"); } { IPosition shape(2, 100*nx, 100*ny); Array arr(shape); indgen (arr, float(0), float(0.01)); TempLattice aF(shape); aF.put (arr); Timer timer; cout << LatticeFractile::unmaskedFractile(aF, 0.5) << endl; timer.show ("PagedArray "); timer.mark(); cout << LatticeFractile::unmaskedFractile(aF, 0.5, 4) << endl; timer.show ("PagedArr 4 "); timer.mark(); cout << LatticeFractile::unmaskedFractile(aF, 0.5, 1280) << endl; timer.show ("PagedArr 1280"); } { IPosition fshape(2, 500*nx, 500*ny); TempLattice aF(fshape); IPosition shape = aF.niceCursorShape(); cout << "tileshape = " << shape << endl; Array arr(shape); indgen (arr, float(0), float(0.01)); Timer timer; LatticeIterator iter(aF, shape); while (! iter.atEnd()) { iter.woCursor() = arr; iter++; } timer.show ("Fill PagedArr"); timer.mark(); cout << LatticeFractile::unmaskedFractile(aF, 0.5) << endl; timer.show ("BigPagedArray"); timer.mark(); cout << LatticeFractile::unmaskedFractile(aF, 0.5, 1280*25) << endl; timer.show ("PagedArr 1280"); } cout << "<<<" << endl; } catch (const std::exception& x) { cout << "Unexpected exception: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/lattices/LEL/test/tLELMedian.out000066400000000000000000000017561476623553700212070ustar00rootroot00000000000000>>> /export/home/gvd/aips++/sun4sol_egcs/bin/tLELMedian: Version <<< 1 [1] [1] [1] [1] [] 127 [127] [127] [127] [127] [130] 12799 [12799] [12799] [12799] [12799] [12802] 131071 [131071] [131071] [131074] >>> Last value = 25099.6 12599.6 normal median 0.27 real 0.16 user 0.11 system [12599.6] ArrayLattice 1.2 real 1.2 user 0 system [12599.6] ArrayLat 1280 1.2 real 1.2 user 0 system [12601.6] MaskedLattice 3.59 real 2.63 user 0.8 system [12599.6] PagedArray 1.2 real 1.2 user 0 system [12599.6] PagedArr 4 1.46 real 1.44 user 0 system [12599.6] PagedArr 1280 1.19 real 1.19 user 0 system tileshape = [200, 160] Fill PagedArr 42.74 real 6.76 user 6.79 system [159.991] BigPagedArray 88.78 real 19.81 user 9.85 system [159.991] PagedArr 1280 167.55 real 26.13 user 19.5 system <<< casacore-3.7.1/lattices/LEL/test/tLatticeExpr.cc000066400000000000000000000445201476623553700214530ustar00rootroot00000000000000//# tLatticeExpr.cc: //# Copyright (C) 1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include Bool checkFloat(Lattice& expr, const Float result, const IPosition shape, const Bool supress); Bool checkDouble(Lattice& expr, const Double result, const IPosition shape, const Bool supress); Bool checkComplex(Lattice& expr, const Complex result, const IPosition shape, const Bool supress); Bool checkDComplex(Lattice& expr, const DComplex result, const IPosition shape, const Bool supress); Bool checkBool(Lattice& expr, const Bool result, const IPosition shape, const Bool supress); int main (int argc, const char* argv[]) { try { Input inp(1); inp.version(" "); inp.create("nx", "2", "Number of pixels along the x-axis", "int"); inp.create("ny", "2", "Number of pixels along the y-axis", "int"); inp.create("sup", "False", "Supress expected exceptions messages", "Bool"); inp.readArguments(argc, argv); const uInt nx=inp.getInt("nx"); const uInt ny=inp.getInt("ny"); const Bool supress=inp.getBool("sup"); IPosition shape(2,nx,ny); Bool ok = True; // Bool Lattices ArrayLattice aB(shape); Bool aBVal = True; aB.set(aBVal); // FLoat Lattices ArrayLattice aF(shape); Float aFVal = 2.0; aF.set(aFVal); // Double Lattices ArrayLattice aD(shape); Double aDVal = 2.0; aD.set(aDVal); // Complex Lattices ArrayLattice aC(shape); Complex aCVal = Complex(2.0,2.0); aC.set(aCVal); // DComplex Lattices ArrayLattice aDC(shape); DComplex aDCVal = DComplex(2.0,2.0); aDC.set(aDCVal); // // // { cout << "Float" << endl; LatticeExprNode node(aF); LatticeExpr expr(node); if (!checkFloat(expr, aFVal, shape, supress)) ok = False; LatticeExpr expr2(expr); if (!checkFloat(expr2, aFVal, shape, supress)) ok = False; LatticeExpr expr3; expr3 = expr; if (!checkFloat(expr2, aFVal, shape, supress)) ok = False; Lattice* pExpr; pExpr = expr.clone(); if (!checkFloat(*pExpr, aFVal, shape, supress)) ok = False; delete pExpr; } // // // { cout << "Double" << endl; LatticeExprNode node(aD); LatticeExpr expr(node); if (!checkDouble(expr, aDVal, shape, supress)) ok = False; LatticeExpr expr2(expr); if (!checkDouble(expr2, aDVal, shape, supress)) ok = False; LatticeExpr expr3; expr3 = expr; if (!checkDouble(expr2, aDVal, shape, supress)) ok = False; Lattice* pExpr; pExpr = expr.clone(); if (!checkDouble(*pExpr, aDVal, shape, supress)) ok = False; delete pExpr; } // // // { cout << "Complex" << endl; LatticeExprNode node(aC); LatticeExpr expr(node); if (!checkComplex(expr, aCVal, shape, supress)) ok = False; LatticeExpr expr2(expr); if (!checkComplex(expr2, aCVal, shape, supress)) ok = False; LatticeExpr expr3; expr3 = expr; if (!checkComplex(expr2, aCVal, shape, supress)) ok = False; Lattice* pExpr; pExpr = expr.clone(); if (!checkComplex(*pExpr, aCVal, shape, supress)) ok = False; delete pExpr; } // // // { cout << "DComplex" << endl; LatticeExprNode node(aDC); LatticeExpr expr(node); if (!checkDComplex(expr, aDCVal, shape, supress)) ok = False; LatticeExpr expr2(expr); if (!checkDComplex(expr2, aDCVal, shape, supress)) ok = False; LatticeExpr expr3; expr3 = expr; if (!checkDComplex(expr2, aDCVal, shape, supress)) ok = False; Lattice* pExpr; pExpr = expr.clone(); if (!checkDComplex(*pExpr, aDCVal, shape, supress)) ok = False; delete pExpr; } // // // { cout << "Bool" << endl; LatticeExprNode node(aB); LatticeExpr expr(node); if (!checkBool(expr, aBVal, shape, supress)) ok = False; LatticeExpr expr2(expr); if (!checkBool(expr2, aBVal, shape, supress)) ok = False; LatticeExpr expr3; expr3 = expr; if (!checkBool(expr2, aBVal, shape, supress)) ok = False; Lattice* pExpr; pExpr = expr.clone(); if (!checkBool(*pExpr, aBVal, shape, supress)) ok = False; delete pExpr; } cout << endl; if (!ok) { cout << "not ok" << endl; return 1; } else { cout << "ok" << endl; } } catch (std::exception& x) { cerr << "aipserror: error " << x.what() << endl; return 1; } return 0; } Bool checkFloat(Lattice& expr, const Float result, const IPosition shape, const Bool supress) { Bool ok = True; Array outArr(shape); ArrayLattice outLat(shape); IPosition origin(shape); origin = 0; IPosition stride(outArr.ndim(),1); if (expr.shape() != shape) { cout << " Shape should be " << shape << endl; cout << " Shape is " << expr.shape() << endl; ok = False; } if (expr.isWritable()) { cout << " LatticeExpr should not be writable" << endl; ok = False; } try { expr.putSlice(outArr, origin, stride); } catch (std::exception& x) { if (!supress) cout << " Caught expected exception; message is: " << x.what() << endl; } try { expr.putSlice(outArr, origin); } catch (std::exception& x) { if (!supress) cout << " Caught expected exception; message is: " << x.what() << endl; } outLat.copyData(expr); outLat.getSlice(outArr, origin, shape, stride); if (!allEQ (outArr, result)) { cout << " Result should be " << result << endl; cout << " Result is " << outArr(origin) << endl; ok = False; } expr.getSlice(outArr, origin, shape, stride); if (!allEQ (outArr, result)) { cout << " Result should be " << result << endl; cout << " Result is " << outArr(origin) << endl; ok = False; } Slicer slicer(origin, shape, stride); expr.getSlice(outArr, slicer); if (!allEQ (outArr, result)) { cout << " Result should be " << result << endl; cout << " Result is " << outArr(origin) << endl; ok = False; } COWPtr > moo; expr.getSlice(moo, origin, shape, stride); outArr.reference(moo.rwRef()); if (!allEQ (outArr, result)) { cout << " Result should be " << result << endl; cout << " Result is " << outArr(origin) << endl; ok = False; } expr.getSlice(moo, slicer); outArr.reference(moo.rwRef()); if (!allEQ (outArr, result)) { cout << " Result should be " << result << endl; cout << " Result is " << outArr(origin) << endl; ok = False; } expr.copyDataTo(outLat); outLat.getSlice(outArr, origin, shape, stride); if (!allEQ (outArr, result)) { cout << " Result should be " << result << endl; cout << " Result is " << outArr(origin) << endl; ok = False; } return ok; } Bool checkDouble(Lattice& expr, const Double result, const IPosition shape, const Bool supress) { Bool ok = True; Array outArr(shape); ArrayLattice outLat(shape); IPosition origin(shape); origin = 0; IPosition stride(outArr.ndim(),1); if (expr.shape() != shape) { cout << " Shape should be " << shape << endl; cout << " Shape is " << expr.shape() << endl; ok = False; } if (expr.isWritable()) { cout << " LatticeExpr should not be writable" << endl; ok = False; } try { expr.putSlice(outArr, origin, stride); } catch (std::exception& x) { if (!supress) cout << " Caught expected exception; message is: " << x.what() << endl; } try { expr.putSlice(outArr, origin); } catch (std::exception& x) { if (!supress) cout << " Caught expected exception; message is: " << x.what() << endl; } outLat.copyData(expr); outLat.getSlice(outArr, origin, shape, stride); if (!allEQ (outArr, result)) { cout << " Result should be " << result << endl; cout << " Result is " << outArr(origin) << endl; ok = False; } expr.getSlice(outArr, origin, shape, stride); if (!allEQ (outArr, result)) { cout << " Result should be " << result << endl; cout << " Result is " << outArr(origin) << endl; ok = False; } Slicer slicer(origin, shape, stride); expr.getSlice(outArr, slicer); if (!allEQ (outArr, result)) { cout << " Result should be " << result << endl; cout << " Result is " << outArr(origin) << endl; ok = False; } COWPtr > moo; expr.getSlice(moo, origin, shape, stride); outArr.reference(moo.rwRef()); if (!allEQ (outArr, result)) { cout << " Result should be " << result << endl; cout << " Result is " << outArr(origin) << endl; ok = False; } expr.getSlice(moo, slicer); outArr.reference(moo.rwRef()); if (!allEQ (outArr, result)) { cout << " Result should be " << result << endl; cout << " Result is " << outArr(origin) << endl; ok = False; } expr.copyDataTo(outLat); outLat.getSlice(outArr, origin, shape, stride); if (!allEQ (outArr, result)) { cout << " Result should be " << result << endl; cout << " Result is " << outArr(origin) << endl; ok = False; } return ok; } Bool checkComplex(Lattice& expr, const Complex result, const IPosition shape, const Bool supress) { Bool ok = True; Array outArr(shape); ArrayLattice outLat(shape); IPosition origin(shape); origin = 0; IPosition stride(outArr.ndim(),1); if (expr.shape() != shape) { cout << " Shape should be " << shape << endl; cout << " Shape is " << expr.shape() << endl; ok = False; } if (expr.isWritable()) { cout << " LatticeExpr should not be writable" << endl; ok = False; } try { expr.putSlice(outArr, origin, stride); } catch (std::exception& x) { if (!supress) cout << " Caught expected exception; message is: " << x.what() << endl; } try { expr.putSlice(outArr, origin); } catch (std::exception& x) { if (!supress) cout << " Caught expected exception; message is: " << x.what() << endl; } outLat.copyData(expr); outLat.getSlice(outArr, origin, shape, stride); if (!allEQ (outArr, result)) { cout << " Result should be " << result << endl; cout << " Result is " << outArr(origin) << endl; ok = False; } expr.getSlice(outArr, origin, shape, stride); if (!allEQ (outArr, result)) { cout << " Result should be " << result << endl; cout << " Result is " << outArr(origin) << endl; ok = False; } Slicer slicer(origin, shape, stride); expr.getSlice(outArr, slicer); if (!allEQ (outArr, result)) { cout << " Result should be " << result << endl; cout << " Result is " << outArr(origin) << endl; ok = False; } COWPtr > moo; expr.getSlice(moo, origin, shape, stride); outArr.reference(moo.rwRef()); if (!allEQ (outArr, result)) { cout << " Result should be " << result << endl; cout << " Result is " << outArr(origin) << endl; ok = False; } expr.getSlice(moo, slicer); outArr.reference(moo.rwRef()); if (!allEQ (outArr, result)) { cout << " Result should be " << result << endl; cout << " Result is " << outArr(origin) << endl; ok = False; } expr.copyDataTo(outLat); outLat.getSlice(outArr, origin, shape, stride); if (!allEQ (outArr, result)) { cout << " Result should be " << result << endl; cout << " Result is " << outArr(origin) << endl; ok = False; } return ok; } Bool checkDComplex(Lattice& expr, const DComplex result, const IPosition shape, const Bool supress) { Bool ok = True; Array outArr(shape); ArrayLattice outLat(shape); IPosition origin(shape); origin = 0; IPosition stride(outArr.ndim(),1); if (expr.shape() != shape) { cout << " Shape should be " << shape << endl; cout << " Shape is " << expr.shape() << endl; ok = False; } if (expr.isWritable()) { cout << " LatticeExpr should not be writable" << endl; ok = False; } try { expr.putSlice(outArr, origin, stride); } catch (std::exception& x) { if (!supress) cout << " Caught expected exception; message is: " << x.what() << endl; } try { expr.putSlice(outArr, origin); } catch (std::exception& x) { if (!supress) cout << " Caught expected exception; message is: " << x.what() << endl; } outLat.copyData(expr); outLat.getSlice(outArr, origin, shape, stride); if (!allEQ (outArr, result)) { cout << " Result should be " << result << endl; cout << " Result is " << outArr(origin) << endl; ok = False; } expr.getSlice(outArr, origin, shape, stride); if (!allEQ (outArr, result)) { cout << " Result should be " << result << endl; cout << " Result is " << outArr(origin) << endl; ok = False; } Slicer slicer(origin, shape, stride); expr.getSlice(outArr, slicer); if (!allEQ (outArr, result)) { cout << " Result should be " << result << endl; cout << " Result is " << outArr(origin) << endl; ok = False; } COWPtr > moo; expr.getSlice(moo, origin, shape, stride); outArr.reference(moo.rwRef()); if (!allEQ (outArr, result)) { cout << " Result should be " << result << endl; cout << " Result is " << outArr(origin) << endl; ok = False; } expr.getSlice(moo, slicer); outArr.reference(moo.rwRef()); if (!allEQ (outArr, result)) { cout << " Result should be " << result << endl; cout << " Result is " << outArr(origin) << endl; ok = False; } expr.copyDataTo(outLat); outLat.getSlice(outArr, origin, shape, stride); if (!allEQ (outArr, result)) { cout << " Result should be " << result << endl; cout << " Result is " << outArr(origin) << endl; ok = False; } return ok; } Bool checkBool(Lattice& expr, const Bool result, const IPosition shape, const Bool supress) { Bool ok = True; Array outArr(shape); ArrayLattice outLat(shape); IPosition origin(shape); origin = 0; IPosition stride(outArr.ndim(),1); if (expr.shape() != shape) { cout << " Shape should be " << shape << endl; cout << " Shape is " << expr.shape() << endl; ok = False; } if (expr.isWritable()) { cout << " LatticeExpr should not be writable" << endl; ok = False; } try { expr.putSlice(outArr, origin, stride); } catch (std::exception& x) { if (!supress) cout << " Caught expected exception; message is: " << x.what() << endl; } try { expr.putSlice(outArr, origin); } catch (std::exception& x) { if (!supress) cout << " Caught expected exception; message is: " << x.what() << endl; } outLat.copyData(expr); outLat.getSlice(outArr, origin, shape, stride); if (!allEQ (outArr, result)) { cout << " Result should be " << result << endl; cout << " Result is " << outArr(origin) << endl; ok = False; } expr.getSlice(outArr, origin, shape, stride); if (!allEQ (outArr, result)) { cout << " Result should be " << result << endl; cout << " Result is " << outArr(origin) << endl; ok = False; } Slicer slicer(origin, shape, stride); expr.getSlice(outArr, slicer); if (!allEQ (outArr, result)) { cout << " Result should be " << result << endl; cout << " Result is " << outArr(origin) << endl; ok = False; } COWPtr > moo; expr.getSlice(moo, origin, shape, stride); outArr.reference(moo.rwRef()); if (!allEQ (outArr, result)) { cout << " Result should be " << result << endl; cout << " Result is " << outArr(origin) << endl; ok = False; } expr.getSlice(moo, slicer); outArr.reference(moo.rwRef()); if (!allEQ (outArr, result)) { cout << " Result should be " << result << endl; cout << " Result is " << outArr(origin) << endl; ok = False; } expr.copyDataTo(outLat); outLat.getSlice(outArr, origin, shape, stride); if (!allEQ (outArr, result)) { cout << " Result should be " << result << endl; cout << " Result is " << outArr(origin) << endl; ok = False; } return ok; } casacore-3.7.1/lattices/LEL/test/tLatticeExpr2.cc000066400000000000000000000237421476623553700215400ustar00rootroot00000000000000//# LELFunction.cc: this defines non-templated classes in LELFunction.h //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main(int argc, const char* argv[]) { try { Input inp(1); inp.version(" "); inp.create("nx", "128", "Number of pixels along the x-axis", "int"); inp.create("ny", "128", "Number of pixels along the y-axis", "int"); inp.create("nz", "128", "Number of pixels along the z-axis", "int"); inp.create("tx", "0", "Number of pixels along the x-axis tile", "int"); inp.create("ty", "0", "Number of pixels along the y-axis tile", "int"); inp.create("tz", "0", "Number of pixels along the z-axis tile", "int"); inp.readArguments(argc, argv); const uInt nx=inp.getInt("nx"); const uInt ny=inp.getInt("ny"); const uInt nz=inp.getInt("nz"); const uInt tx=inp.getInt("tx"); const uInt ty=inp.getInt("ty"); const uInt tz=inp.getInt("tz"); const IPosition latticeShape(3, nx, ny, nz); IPosition tileShape(3, tx, ty, tz); if (tileShape.product() == 0) { tileShape = TiledShape(latticeShape).tileShape(); } cout << "Data Type: Float"; cout << " Lattice shape:" << latticeShape; cout << " Tile shape:" << tileShape << endl; { SetupNewTable paSetup("tLatticeExpr2_tmp.tab", TableDesc(), Table::New); Table paTable(paSetup); PagedArray lat(TiledShape(latticeShape, tileShape), paTable); Array arr(tileShape); indgen(arr); LatticeIterator iter(lat, tileShape); Timer timer; for (iter.reset(); !iter.atEnd(); iter++) { iter.woCursor() = arr; arr += Float(tileShape.product()); } timer.show ("filling "); } { Table t("tLatticeExpr2_tmp.tab"); PagedArray lat(t); SetupNewTable paSetup("tLatticeExpr2_tmp.tab2", TableDesc(), Table::New); Table paTable(paSetup); PagedArray latout(TiledShape(latticeShape, tileShape), paTable); Timer timer; LatticeExpr expr(2*lat); latout.copyData (expr); timer.show ("2*lat "); } { Table t("tLatticeExpr2_tmp.tab"); PagedArray lat(t); SetupNewTable paSetup("tLatticeExpr2_tmp.tab2", TableDesc(), Table::New); Table paTable(paSetup); PagedArray latout(TiledShape(latticeShape, tileShape), paTable); Timer timer; LatticeExpr expr((mean(lat)-lat.shape().product()/3)*lat); latout.copyData (expr); timer.show ("mean(lat)*lat "); } { Table t("tLatticeExpr2_tmp.tab"); PagedArray lat(t); SetupNewTable paSetup("tLatticeExpr2_tmp.tab2", TableDesc(), Table::New); Table paTable(paSetup); PagedArray latout(TiledShape(latticeShape, tileShape), paTable); Timer timer; LatticeExpr expr(amp(lat,lat)); latout.copyData (expr); timer.show ("amp(lat,lat) "); } { Table t("tLatticeExpr2_tmp.tab"); PagedArray lat(t); SetupNewTable paSetup("tLatticeExpr2_tmp.tab2", TableDesc(), Table::New); Table paTable(paSetup); PagedArray latout(TiledShape(latticeShape, tileShape), paTable); Timer timer; LatticeExpr expr(lat+lat); latout.copyData (expr); timer.show ("lat+lat "); } { Table t("tLatticeExpr2_tmp.tab"); PagedArray lat(t); SetupNewTable paSetup("tLatticeExpr2_tmp.tab3", TableDesc(), Table::New); Table paTable(paSetup); PagedArray latout(TiledShape(latticeShape, tileShape), paTable); Timer timer; LatticeExpr expr(2*min(lat)*2*lat); latout.copyData (expr); timer.show ("2*min(lat)*2*lat"); } { Table t("tLatticeExpr2_tmp.tab"); PagedArray lat(t); SetupNewTable paSetup("tLatticeExpr2_tmp.tab3", TableDesc(), Table::New); Table paTable(paSetup); PagedArray latout(TiledShape(latticeShape, tileShape), paTable); Timer timer; LatticeExpr expr(lat*2*min(lat)*2); latout.copyData (expr); timer.show ("lat*2*min(lat)*2"); } { Table t1("tLatticeExpr2_tmp.tab"); PagedArray lat1(t1); Table t2("tLatticeExpr2_tmp.tab2"); PagedArray lat2(t2); SetupNewTable paSetup("tLatticeExpr2_tmp.tab3", TableDesc(), Table::New); Table paTable(paSetup); PagedArray latout(TiledShape(latticeShape, tileShape), paTable); Timer timer; LatticeExpr expr(2*lat1 + 3*lat2); latout.copyData (expr); timer.show ("2*lat1 + 3*lat2 "); } { Table t("tLatticeExpr2_tmp.tab3"); PagedArray lat(t); Array arr(tileShape); indgen(arr); RO_LatticeIterator iter(lat, tileShape); Timer timer; for (iter.reset(); !iter.atEnd(); iter++) { if (! allEQ (iter.cursor(), float(8)*arr)) { cout << "result mismatches" << endl; return 1; } arr += Float(tileShape.product()); } timer.show ("checking "); } { Table t("tLatticeExpr2_tmp.tab"); PagedArray lat(t); SetupNewTable paSetup("tLatticeExpr2_tmp.tab2", TableDesc(), Table::New); Table paTable(paSetup); PagedArray latout(TiledShape(latticeShape, tileShape), paTable); Timer timer; LCPagedMask mask (lat.shape(), lat.tableName() + "/mask"); mask.set (True); timer.show ("filling mask "); SubLattice sublat (lat, mask); timer.mark(); LatticeExpr expr(sublat+sublat); latout.copyData (expr); timer.show ("sublat+sublat "); timer.mark(); LatticeExpr expr2((mean(sublat)-lat.shape().product()/3)*lat); latout.copyData (expr2); timer.show ("mean(sublat)*lat"); timer.mark(); LatticeExpr expr3(amp(sublat,lat)); latout.copyData (expr3); timer.show ("amp(sublat,lat) "); } if (latticeShape.product() <= 1024*1024) { Array arr1(latticeShape); Array arr5(latticeShape); indgen(arr1); ArrayLattice al1(arr1); ArrayLattice al5(latticeShape); cout << "arr1+arr1" << endl; Timer timer; arr5 = arr1 + arr1; timer.show ("as array "); timer.mark(); al5.copyData (LatticeExpr(al1+al1)); timer.show ("as lattice"); cout << "arr1+arr1+arr1" << endl; timer.mark(); arr5 = arr1 + arr1 + arr1; timer.show ("as array "); timer.mark(); al5.copyData (LatticeExpr(al1+al1+al1)); timer.show ("as lattice"); cout << "arr1+arr1+arr1+arr1" << endl; timer.mark(); arr5 = arr1 + arr1 + arr1 + arr1; timer.show ("as array "); timer.mark(); al5.copyData (LatticeExpr(al1+al1+al1+al1)); timer.show ("as lattice"); cout << "arr1+arr1+arr1+arr1+arr1" << endl; timer.mark(); arr5 = arr1 + arr1 + arr1 + arr1 + arr1; timer.show ("as array "); timer.mark(); al5.copyData (LatticeExpr(al1+al1+al1+al1+al1)); timer.show ("as lattice"); cout << "((((arr1+arr1)+arr1)+arr1)+arr1)+arr1" << endl; timer.mark(); arr5 = ((((arr1 + arr1) + arr1) + arr1) + arr1) + arr1; timer.show ("as array "); timer.mark(); al5.copyData (LatticeExpr(((((al1+al1)+al1)+al1)+al1)+al1)); timer.show ("as lattice"); cout << "arr1+(arr1+(arr1+(arr1+(arr1+arr1))))" << endl; timer.mark(); arr5 = arr1 + (arr1 + (arr1 + (arr1 + (arr1 + arr1)))); timer.show ("as array "); timer.mark(); al5.copyData (LatticeExpr(al1+(al1+(al1+(al1+(al1+al1)))))); timer.show ("as lattice"); cout << "arr1+arr1+arr1+arr1+arr1+arr1+arr1+arr1+arr1+arr1" << endl; timer.mark(); arr5 = arr1+arr1+arr1+arr1+arr1+arr1+arr1+arr1+arr1+arr1; timer.show ("as array "); timer.mark(); al5.copyData (LatticeExpr(al1+al1+al1+al1+al1+al1+al1+al1+al1+al1)); timer.show ("as lattice"); } } catch (std::exception& x) { cerr << "aipserror: error " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/lattices/LEL/test/tLatticeExpr2Node.cc000066400000000000000000000672251476623553700223520ustar00rootroot00000000000000//# tLatticeExprNode2.cc: Test program for masks in LatticeExprNode //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Bool checkFloat (const LatticeExprNode& expr, const Array& result, Float scalarResult, Bool isInvalid, const Array& mask) { // Test if result is indeed a scalar. // If so, test if invalid if it should be. // If not invalid, test if result matches. if (result.nelements() == 0) { if (! expr.isScalar()) { cout << " expected scalar result" << endl; return False; } if (isInvalid != expr.isInvalidScalar()) { cout << " mismatch in invalid; expected " << isInvalid << endl; return False; } if (!isInvalid) { if (expr.getFloat() != scalarResult) { cout << " expected value " << scalarResult << endl; cout << " got scalar " << expr.getFloat() << endl; return False; } } return True; } // The result is an array. // Test if the shape matches. IPosition shape = result.shape(); if (expr.shape() != shape) { cout << " mismatch in result shape" << endl; return False; } // Get the result (value and optional mask). LELArray arr(shape); IPosition origin(shape); origin = 0; Slicer region(origin, shape); expr.eval (arr, region); // Check if there is a mask if it should be. if ((mask.nelements()==0) == arr.isMasked()) { cout << " mismatch in arr.isMasked" << endl; return False; } // If masked, test if matches. // If entire mask is false, the values can be anything (thus not checked). if (arr.isMasked()) { if (! allEQ (arr.mask(), mask)) { cout << " expected mask " << mask << endl; cout << " got " << arr.mask() << endl; return False; } else if (allEQ (mask, False)) { return True; } } // Check if the values match. if (! allEQ (arr.value(), result)) { cout << " expected value " << result << endl; cout << " got array " << arr.value() << endl; return False; } return True; } Bool checkComplex (const LatticeExprNode& expr, const Array& result, Complex scalarResult, Bool isInvalid, const Array& mask) { // Test if result is indeed a scalar. // If so, test if invalid if it should be. // If not invalid, test if result matches. if (result.nelements() == 0) { if (! expr.isScalar()) { cout << " expected scalar result" << endl; return False; } if (isInvalid != expr.isInvalidScalar()) { cout << " mismatch in invalid; expected " << isInvalid << endl; return False; } if (!isInvalid) { if (expr.getComplex() != scalarResult) { cout << " expected value " << scalarResult << endl; cout << " got scalar " << expr.getComplex() << endl; return False; } } return True; } // The result is an array. // Test if the shape matches. IPosition shape = result.shape(); if (expr.shape() != shape) { cout << " mismatch in result shape" << endl; return False; } // Get the result (value and optional mask). LELArray arr(shape); IPosition origin(shape); origin = 0; Slicer region(origin, shape); expr.eval (arr, region); // Check if there is a mask if it should be. if ((mask.nelements()==0) == arr.isMasked()) { cout << " mismatch in arr.isMasked" << endl; return False; } // If masked, test if matches. // If entire mask is false, the values can be anything (thus not checked). if (arr.isMasked()) { if (! allEQ (arr.mask(), mask)) { cout << " expected mask " << mask << endl; cout << " got " << arr.mask() << endl; return False; } else if (allEQ (mask, False)) { return True; } } // Check if the values match. if (! allEQ (arr.value(), result)) { cout << " expected value " << result << endl; cout << " got array " << arr.value() << endl; return False; } return True; } Bool checkBool (const LatticeExprNode& expr, const Array& result, Bool scalarResult, Bool isInvalid, const Array& mask, Bool orand) { // Test if result is indeed a scalar. // If so, test if invalid if it should be. // If not invalid, test if result matches. if (result.nelements() == 0) { if (! expr.isScalar()) { cout << " expected scalar result" << endl; return False; } if (isInvalid != expr.isInvalidScalar()) { cout << " mismatch in invalid; expected " << isInvalid << endl; return False; } if (!isInvalid) { if (expr.getBool() != scalarResult) { cout << " expected value " << scalarResult << endl; cout << " got scalar " << expr.getBool() << endl; return False; } } return True; } // The result is an array. // Test if the shape matches. IPosition shape = result.shape(); if (expr.shape() != shape) { cout << " mismatch in result shape" << endl; return False; } // Get the result (value and optional mask). LELArray arr(shape); IPosition origin(shape); origin = 0; Slicer region(origin, shape); expr.eval (arr, region); // Check if there is a mask if it should be. // When an or/and was done, it can be the case that no mask is // present if entire mask is true. if ((mask.nelements()==0) == arr.isMasked()) { if (!orand || mask.nelements() == 0) { cout << " mismatch in arr.isMasked" << endl; return False; } else if (!allEQ(mask, True)) { cout << " expected or/and mask " << mask << endl; return False; } } // If masked, test if matches. // If entire mask is false, the values can be anything (thus not checked). if (arr.isMasked()) { if (! allEQ (arr.mask(), mask)) { cout << " expected mask " << mask << endl; cout << " got " << arr.mask() << endl; return False; } else if (allEQ (mask, False)) { return True; } } // Check if the values match. if (! allEQ (arr.value(), result)) { cout << " expected value " << result << endl; cout << " got array " << arr.value() << endl; return False; } return True; } Bool doIt (const SubLattice& aF, const SubLattice& bF) { Array lmask; Array emptyFArr; Array emptyBArr; Array emptyCArr; Array emptyMask; Array arra; arra = aF.get(); Array arrb; arrb = bF.get(); Array aMask = aF.getMask().copy(); Array bMask = bF.getMask().copy(); Array mask = aMask && bMask; Array asMask = aMask.copy(); Array bsMask = bMask.copy(); Array abMask = aMask || bMask; Bool aInvalid = False; Bool bInvalid = False; if (allEQ(aMask,False)) { aInvalid = True; bsMask = False; } else if (! bF.isMasked()) { bsMask.reference (emptyMask); } if (allEQ(bMask,False)) { bInvalid = True; asMask = False; } else if (! aF.isMasked()) { asMask.reference (emptyMask); } Bool invalid = (aInvalid || bInvalid); if (!aF.isMasked() && !bF.isMasked()) { mask.reference (emptyMask); abMask.reference (emptyMask); } if (!aF.isMasked()) { aMask.reference (emptyMask); } if (!bF.isMasked()) { bMask.reference (emptyMask); } Bool ok = True; if (!checkFloat(aF+bF, arra+arrb, 0, False, mask)) ok = False; if (!checkFloat(aF+min(bF), arra+min(arrb), 0, False, asMask)) ok = False; if (!checkFloat(max(aF)+bF, arrb+max(arra), 0, False, bsMask)) ok = False; if (!checkFloat(min(aF)+max(bF), emptyFArr, min(arra)+max(arrb), invalid, emptyMask)) ok = False; if (!checkFloat(aF-bF, arra-arrb, 0, False, mask)) ok = False; if (!checkFloat(aF-min(bF), arra-min(arrb), 0, False, asMask)) ok = False; if (!checkFloat(max(aF)-bF, max(arra)-arrb, 0, False, bsMask)) ok = False; if (!checkFloat(min(aF)-max(bF), emptyFArr, min(arra)-max(arrb), invalid, emptyMask)) ok = False; if (!checkFloat(aF*bF, arra*arrb, 0, False, mask)) ok = False; if (!checkFloat(aF*min(bF), arra*min(arrb), 0, False, asMask)) ok = False; if (!checkFloat(max(aF)*bF, arrb*max(arra), 0, False, bsMask)) ok = False; if (!checkFloat(min(aF)*max(bF), emptyFArr, min(arra)*max(arrb), invalid, emptyMask)) ok = False; if (!checkFloat(aF/bF, arra/arrb, 0, False, mask)) ok = False; if (!checkFloat(aF/min(bF), arra/min(arrb), 0, False, asMask)) ok = False; if (!checkFloat(max(aF)/bF, max(arra)/arrb, 0, False, bsMask)) ok = False; if (!checkFloat(min(aF)/max(bF), emptyFArr, min(arra)/max(arrb), invalid, emptyMask)) ok = False; Array maxarra(arra.shape()); maxarra = max(arra); Array minarrb(arrb.shape()); minarrb = min(arrb); if (!checkFloat(atan2(aF,bF), atan2(arra,arrb), 0, False, mask)) ok = False; if (!checkFloat(atan2(aF,min(bF)), atan2(arra,minarrb), 0, False, asMask)) ok = False; if (!checkFloat(atan2(max(aF),bF), atan2(maxarra,arrb), 0, False, bsMask)) ok = False; if (!checkFloat(atan2(min(aF),max(bF)), emptyFArr, atan2(min(arra),max(arrb)), invalid, emptyMask)) ok = False; if (!checkFloat(pow(aF,bF), pow(arra,arrb), 0, False, mask)) ok = False; if (!checkFloat(pow(aF,min(bF)), pow(arra,minarrb), 0, False, asMask)) ok = False; if (!checkFloat(pow(max(aF),bF), pow(maxarra,arrb), 0, False, bsMask)) ok = False; if (!checkFloat(pow(min(aF),max(bF)), emptyFArr, pow(min(arra),max(arrb)), invalid, emptyMask)) ok = False; if (!checkFloat(fmod(aF,bF), fmod(arra,arrb), 0, False, mask)) ok = False; if (!checkFloat(fmod(aF,min(bF)), fmod(arra,minarrb), 0, False, asMask)) ok = False; if (!checkFloat(fmod(max(aF),bF), fmod(maxarra,arrb), 0, False, bsMask)) ok = False; if (!checkFloat(fmod(min(aF),max(bF)), emptyFArr, fmod(min(arra),max(arrb)), invalid, emptyMask)) ok = False; // SGI needs LatticeExprNode(SubLattice) for min(SubLattice, SubLattice) and // max(SubLattice, SubLattice), It's scoping problem with -LANG:std if (!checkFloat((min(LatticeExprNode(aF),LatticeExprNode(bF))), min(arra,arrb), 0, False, static_cast >(mask))) ok = False; if (!checkFloat(min(aF,min(bF)), min(arra,minarrb), 0, False, asMask)) ok = False; if (!checkFloat(min(max(aF),bF), min(maxarra,arrb), 0, False, bsMask)) ok = False; if (!checkFloat(min(min(aF),max(bF)), emptyFArr, min(min(arra),max(arrb)), invalid, emptyMask)) ok = False; if (!checkFloat(max(LatticeExprNode(aF),LatticeExprNode(bF)), max(arra,arrb), 0, False, mask)) ok = False; if (!checkFloat(max(aF,min(bF)), max(arra,minarrb), 0, False, asMask)) ok = False; if (!checkFloat(max(max(aF),bF), max(maxarra,arrb), 0, False, bsMask)) ok = False; if (!checkFloat(max(min(aF),max(bF)), emptyFArr, max(min(arra),max(arrb)), invalid, emptyMask)) ok = False; Array arrc(arra.shape()); Bool delc, dela, delb; uInt nr = arrc.nelements(); Complex* cptr = arrc.getStorage(delc); const Float* aptr = arra.getStorage(dela); const Float* bptr = arrb.getStorage(delb); for (uInt i=0; i arrc2; if (!checkComplex(formComplex(min(aF),max(bF)), arrc2, Complex(min(arra),max(arrb)), invalid, emptyMask)) ok = False; // Test comparison operators. if (!checkBool (aF==bF, arra==arrb, 0, False, mask, False)) ok = False; if (!checkBool (aF==min(bF), arra==min(arrb), 0, False, asMask, False)) ok = False; if (!checkBool (max(aF)==bF, arrb==max(arra), 0, False, bsMask, False)) ok = False; if (!checkBool (min(aF)==max(bF), emptyBArr, min(arra)==max(arrb), invalid, emptyMask, False)) ok = False; if (!checkBool (LatticeExprNode(aF)!=bF, arra!=arrb, 0, False, mask, False)) ok = False; if (!checkBool (aF!=min(bF), arra!=min(arrb), 0, False, asMask, False)) ok = False; if (!checkBool (max(aF)!=bF, arrb!=max(arra), 0, False, bsMask, False)) ok = False; if (!checkBool (min(aF)!=max(bF), emptyBArr, min(arra)!=max(arrb), invalid, emptyMask, False)) ok = False; if (!checkBool (aFbF, arra>arrb, 0, False, mask, False)) ok = False; if (!checkBool (aF>min(bF), arra>min(arrb), 0, False, asMask, False)) ok = False; if (!checkBool (max(aF)>bF, max(arra)>arrb, 0, False, bsMask, False)) ok = False; if (!checkBool (min(aF)>max(bF), emptyBArr, min(arra)>max(arrb), invalid, emptyMask, False)) ok = False; if (!checkBool (LatticeExprNode(aF)>=bF, arra>=arrb, 0, False, mask, False)) ok = False; if (!checkBool (aF>=min(bF), arra>=min(arrb), 0, False, asMask, False)) ok = False; if (!checkBool (max(aF)>=bF, max(arra)>=arrb, 0, False, bsMask, False)) ok = False; if (!checkBool (min(aF)>=max(bF), emptyBArr, min(arra)>=max(arrb), invalid, emptyMask, False)) ok = False; // Test anding of array and array. // This is already tested more extensively in tLatticeExprNode. if (!checkBool (aF==aF && bF==bF, arra==arra && arrb==arrb, 0, False, mask, True)) ok = False; // Test anding of array and scalar. lmask.reference (emptyMask); if (asMask.nelements() > 0) { if (bInvalid || min(arrb)==0) { lmask.reference (asMask); } } if (!checkBool (aF==aF && min(bF)==0, arra==arra && min(arrb)==0, 0, False, lmask, True)) ok = False; lmask.reference (emptyMask); if (asMask.nelements() > 0) { if (bInvalid || min(arrb)==0) { lmask.reference (aMask); } } if (!checkBool (LatticeExprNode(aF)!=aF && min(bF)==0, arra!=arra && min(arrb)==0, 0, False, lmask, True)) ok = False; lmask.reference (emptyMask); if (asMask.nelements() > 0) { if (bInvalid || min(arrb)!=0) { lmask.reference (asMask); } } if (!checkBool (aF==aF && min(bF)!=0, arra==arra && min(arrb)!=0, 0, False, lmask, True)) ok = False; lmask.reference (emptyMask); if (asMask.nelements() > 0) { if (bInvalid || min(arrb)!=0) { lmask.reference (aMask); } } if (!checkBool (LatticeExprNode(aF)!=aF && min(bF)!=0, arra!=arra && min(arrb)!=0, 0, False, lmask, True)) ok = False; // Test anding of scalar and array. lmask.reference (emptyMask); if (bsMask.nelements() > 0) { if (aInvalid || max(arra)==0) { lmask.reference (bsMask); } } if (!checkBool (max(aF)==0 && bF==bF, max(arra)==0 && arrb==arrb, 0, False, lmask, True)) ok = False; lmask.reference (emptyMask); if (bsMask.nelements() > 0) { if (aInvalid || max(arra)==0) { lmask.reference (bMask); } } if (!checkBool (max(aF)==0 && LatticeExprNode(bF)!=bF, max(arra)==0 && arrb!=arrb, 0, False, lmask, True)) ok = False; lmask.reference (emptyMask); if (bsMask.nelements() > 0) { if (aInvalid || max(arra)!=0) { lmask.reference (bsMask); } } if (!checkBool (max(aF)!=0 && LatticeExprNode(bF)==bF, max(arra)!=0 && arrb==arrb, 0, False, lmask, True)) ok = False; lmask.reference (emptyMask); if (bsMask.nelements() > 0) { if (aInvalid || max(arra)!=0) { lmask.reference (bMask); } } if (!checkBool (max(aF)!=0 && LatticeExprNode(bF)!=bF, max(arra)!=0 && arrb!=arrb, 0, False, lmask, True)) ok = False; // Test anding of scalar and scalar. invalid = (!((min(arra)==0 && !aInvalid) || (max(arrb)==0 && !bInvalid) || (!aInvalid && !bInvalid))); if (!checkBool (min(aF)!=0 && max(bF)!=0, emptyBArr, min(arra)!=0 && max(arrb)!=0, invalid, emptyMask, True)) ok = False; invalid = (!((min(arra)==0 && !aInvalid) || (max(arrb)!=0 && !bInvalid) || (!aInvalid && !bInvalid))); if (!checkBool (min(aF)!=0 && max(bF)==0, emptyBArr, min(arra)!=0 && max(arrb)==0, invalid, emptyMask, True)) ok = False; invalid = (!((min(arra)!=0 && !aInvalid) || (max(arrb)==0 && !bInvalid) || (!aInvalid && !bInvalid))); if (!checkBool (min(aF)==0 && max(bF)!=0, emptyBArr, min(arra)==0 && max(arrb)!=0, invalid, emptyMask, True)) ok = False; invalid = (!((min(arra)!=0 && !aInvalid) || (max(arrb)!=0 && !bInvalid) || (!aInvalid && !bInvalid))); if (!checkBool (min(aF)==0 && max(bF)==0, emptyBArr, min(arra)==0 && max(arrb)==0, invalid, emptyMask, True)) ok = False; // Test oring of array and array. // This is already tested more extensively in tLatticeExprNode. if (!checkBool (LatticeExprNode(aF)==aF || LatticeExprNode(bF)==bF, arra==arra || arrb==arrb, 0, False, abMask, True)) ok = False; // Test oring of array and scalar. lmask.reference (emptyMask); if (asMask.nelements() > 0) { if (bInvalid || min(arrb)!=0) { lmask.reference (aMask); } } if (!checkBool (LatticeExprNode(aF)==aF || min(bF)==0, arra==arra || min(arrb)==0, 0, False, lmask, True)) ok = False; lmask.reference (emptyMask); if (asMask.nelements() > 0) { if (bInvalid || min(arrb)!=0) { lmask.reference (asMask); } } if (!checkBool (LatticeExprNode(aF)!=aF || min(bF)==0, arra!=arra || min(arrb)==0, 0, False, lmask, True)) ok = False; lmask.reference (emptyMask); if (asMask.nelements() > 0) { if (bInvalid || min(arrb)==0) { lmask.reference (aMask); } } if (!checkBool (LatticeExprNode(aF)==aF || min(bF)!=0, arra==arra || min(arrb)!=0, 0, False, lmask, True)) ok = False; lmask.reference (emptyMask); if (asMask.nelements() > 0) { if (bInvalid || min(arrb)==0) { lmask.reference (asMask); } } if (!checkBool (LatticeExprNode(aF)!=aF || min(bF)!=0, arra!=arra || min(arrb)!=0, 0, False, lmask, True)) ok = False; // Test oring of scalar and array. lmask.reference (emptyMask); if (bsMask.nelements() > 0) { if (aInvalid || max(arra)!=0) { lmask.reference (bMask); } } if (!checkBool (max(aF)==0 || LatticeExprNode(bF)==bF, max(arra)==0 || arrb==arrb, 0, False, lmask, True)) ok = False; lmask.reference (emptyMask); if (bsMask.nelements() > 0) { if (aInvalid || max(arra)!=0) { lmask.reference (bsMask); } } if (!checkBool (max(aF)==0 || LatticeExprNode(bF)!=bF, max(arra)==0 || arrb!=arrb, 0, False, lmask, True)) ok = False; lmask.reference (emptyMask); if (bsMask.nelements() > 0) { if (aInvalid || max(arra)==0) { lmask.reference (bMask); } } if (!checkBool (max(aF)!=0 || LatticeExprNode(bF)==bF, max(arra)!=0 || arrb==arrb, 0, False, lmask, True)) ok = False; lmask.reference (emptyMask); if (bsMask.nelements() > 0) { if (aInvalid || max(arra)==0) { lmask.reference (bsMask); } } if (!checkBool (max(aF)!=0 || LatticeExprNode(bF)!=bF, max(arra)!=0 || arrb!=arrb, 0, False, lmask, True)) ok = False; // Test oring of scalar and scalar. invalid = ((min(arra)==0 && bInvalid) || (max(arrb)==0 && aInvalid) || (aInvalid && bInvalid)); if (!checkBool (min(aF)!=0 || max(bF)!=0, emptyBArr, min(arra)!=0 || max(arrb)!=0, invalid, emptyMask, True)) ok = False; invalid = ((min(arra)==0 && bInvalid) || (max(arrb)!=0 && aInvalid) || (aInvalid && bInvalid)); if (!checkBool (min(aF)!=0 || max(bF)==0, emptyBArr, min(arra)!=0 || max(arrb)==0, invalid, emptyMask, True)) ok = False; invalid = ((min(arra)!=0 && bInvalid) || (max(arrb)==0 && aInvalid) || (aInvalid && bInvalid)); if (!checkBool (min(aF)==0 || max(bF)!=0, emptyBArr, min(arra)==0 || max(arrb)!=0, invalid, emptyMask, True)) ok = False; invalid = ((min(arra)!=0 && bInvalid) || (max(arrb)!=0 && aInvalid) || (aInvalid && bInvalid)); if (!checkBool (min(aF)==0 || max(bF)==0, emptyBArr, min(arra)==0 || max(arrb)==0, invalid, emptyMask, True)) ok = False; // Test the iif function in all possible ways. // First with a scalar condition. Array iifMask; if (aInvalid || min(arra)==0) { iifMask.reference (aMask); } else { iifMask.reference (bMask); } if (!checkFloat (iif(min(aF)==0,aF,bF), min(arra)==0?arra:arrb, 0, False, iifMask)) ok = False; if (aInvalid || min(arra)!=0) { iifMask.reference (aMask); } else { iifMask.reference (bMask); } if (!checkFloat (iif(min(aF)!=0,aF,bF), min(arra)!=0?arra:arrb, 0, False, iifMask)) ok = False; if (aInvalid) { iifMask.reference (aMask); } else if (min(arra)==0) { iifMask.reference (emptyMask); } else { iifMask.reference (bMask); } if (!checkFloat (iif(min(aF)==0,max(aF),bF), min(arra)==0?maxarra:arrb, 0, False, iifMask)) ok = False; if (aInvalid) { iifMask.reference (aMask); } else if (min(arra)!=0) { iifMask.reference (emptyMask); } else { iifMask.reference (bMask); } if (!checkFloat (iif(min(aF)!=0,max(aF),bF), min(arra)!=0?maxarra:arrb, 0, False, iifMask)) ok = False; if (aInvalid || min(arra)==0) { iifMask.reference (aMask); } else if (bInvalid) { iifMask.reference (bMask); } else { iifMask.reference (emptyMask); } if (!checkFloat (iif(min(aF)==0,aF,min(bF)), min(arra)==0?arra:minarrb, 0, False, iifMask)) ok = False; if (aInvalid || min(arra)!=0) { iifMask.reference (aMask); } else if (bInvalid) { iifMask.reference (bMask); } else { iifMask.reference (emptyMask); } if (!checkFloat (iif(min(aF)!=0,aF,min(bF)), min(arra)!=0?arra:minarrb, 0, False, iifMask)) ok = False; if (!checkFloat (iif(min(aF)==0,max(aF),min(bF)), emptyFArr, min(arra)==0?max(arra):min(arrb), (aInvalid || (min(arra)!=0 && bInvalid)), emptyMask)) ok = False; if (!checkFloat (iif(min(aF)!=0,max(aF),min(bF)), emptyFArr, min(arra)!=0?max(arra):min(arrb), (aInvalid || (min(arra)==0 && bInvalid)), emptyMask)) ok = False; // Now test iif with an array condition. // Note that aF is always filled with positive values, // so we can be sure that the result is not a mix of arra and arrb. Array aiMask(aMask.copy()); if (!aF.isMasked() && bF.isMasked()) { aiMask.resize (arra.shape()); aiMask = True; } if (!checkFloat (iif(aF<0,aF,bF), arrb, 0, False, mask)) ok = False; if (!checkFloat (iif(aF>=0,aF,bF), arra, 0, False, aiMask)) ok = False; if (!checkFloat (iif(aF<0,max(aF),bF), arrb, 0, False, mask)) ok = False; if (aInvalid) { aiMask = False; } if (!checkFloat (iif(aF>=0,max(aF),bF), maxarra, 0, False, aiMask)) ok = False; if (!checkFloat (iif(aF<0,aF,min(bF)), minarrb, 0, False, asMask)) ok = False; if (!bInvalid) { aiMask.reference (aMask); } if (!checkFloat (iif(aF>=0,aF,min(bF)), arra, 0, False, aiMask)) ok = False; if (!checkFloat (iif(aF<0,max(aF),min(bF)), minarrb, 0, False, asMask)) ok = False; if (!checkFloat (iif(aF>=0,max(aF),min(bF)), maxarra, 0, False, aiMask)) ok = False; return ok; } int main() { Bool ok = True; try { IPosition shape(2,2,2); Array arra(shape); Array arrb(shape); // Make sure to fill the arrays with positive values, // otherwise some iif tests in doIt will fail. indgen (arra, Float(1), Float(1)); indgen (arrb, Float(11), Float(1)); ArrayLattice aF(arra); ArrayLattice bF(arrb); Array mat1(shape); Array mat2(shape); mat1 = True; mat1(IPosition(2,1,0)) = False; mat2 = False; LCBox box(shape); LCPixelSet mask1 (mat1, box); LCPixelSet mask2 (mat2, box); if (!doIt(SubLattice(aF), SubLattice(bF))) { ok = False; } if (!doIt(SubLattice(aF), SubLattice(bF,mask1))) { ok = False; } if (!doIt(SubLattice(aF,mask1), SubLattice(bF))) { ok = False; } if (!doIt(SubLattice(aF,mask1), SubLattice(bF,mask1))) { ok = False; } if (!doIt(SubLattice(aF), SubLattice(bF,mask2))) { ok = False; } if (!doIt(SubLattice(aF,mask2), SubLattice(bF))) { ok = False; } if (!doIt(SubLattice(aF,mask2), SubLattice(bF,mask2))) { ok = False; } if (!doIt(SubLattice(aF,mask1), SubLattice(bF,mask2))) { ok = False; } if (!doIt(SubLattice(aF,mask2), SubLattice(bF,mask1))) { ok = False; } } catch (std::exception& x) { cout << "Caught exception: " << x.what() << endl; ok = False; } if (ok) { cout << "OK" << endl; return 0; } return 1; } casacore-3.7.1/lattices/LEL/test/tLatticeExpr3.cc000066400000000000000000000305141476623553700215340ustar00rootroot00000000000000//# LELFunction.cc: this defines non-templated classes in LELFunction.h //# Copyright (C) 1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main(int argc, const char* argv[]) { try { Input inp(1); inp.version(" "); inp.create("nx", "128", "Number of pixels along the x-axis", "int"); inp.create("ny", "128", "Number of pixels along the y-axis", "int"); inp.create("nz", "128", "Number of pixels along the z-axis", "int"); inp.create("tx", "0", "Number of pixels along the x-axis tile", "int"); inp.create("ty", "0", "Number of pixels along the y-axis tile", "int"); inp.create("tz", "0", "Number of pixels along the z-axis tile", "int"); inp.readArguments(argc, argv); const uInt nx=inp.getInt("nx"); const uInt ny=inp.getInt("ny"); const uInt nz=inp.getInt("nz"); const uInt tx=inp.getInt("tx"); const uInt ty=inp.getInt("ty"); const uInt tz=inp.getInt("tz"); const IPosition latticeShape(3, nx, ny, nz); IPosition tileShape(3, tx, ty, tz); if (tileShape.product() == 0) { tileShape = TiledShape(latticeShape).tileShape(); } cout << "Data Type: Complex"; cout << " Lattice shape:" << latticeShape; cout << " Tile shape:" << tileShape << endl; { SetupNewTable paSetup("tLatticeExpr3_tmp.tab", TableDesc(), Table::New); Table paTable(paSetup); PagedArray lat(TiledShape(latticeShape, tileShape), paTable); Array arr(tileShape); indgen(arr, Complex(0,0), Complex(1,1)); LatticeIterator iter(lat, tileShape); Timer timer; for (iter.reset(); !iter.atEnd(); iter++) { iter.woCursor() = arr; arr += Complex(tileShape.product()); } timer.show ("filling "); } { Table t("tLatticeExpr3_tmp.tab"); PagedArray lat(t); SetupNewTable paSetup("tLatticeExpr3_tmp.tab2", TableDesc(), Table::New); Table paTable(paSetup); PagedArray latout(TiledShape(latticeShape, tileShape), paTable); Timer timer; LatticeExpr expr(2*lat); latout.copyData (expr); timer.show ("2*lat "); } { Table t("tLatticeExpr3_tmp.tab"); PagedArray lat(t); SetupNewTable paSetup("tLatticeExpr3_tmp.tab2", TableDesc(), Table::New); Table paTable(paSetup); PagedArray latout(TiledShape(latticeShape, tileShape), paTable); Timer timer; LatticeExpr expr((mean(lat)-lat.shape().product()/3)*lat); latout.copyData (expr); timer.show ("mean(lat)*lat "); } { Table t("tLatticeExpr3_tmp.tab"); PagedArray lat(t); SetupNewTable paSetup("tLatticeExpr3_tmp.tab2", TableDesc(), Table::New); Table paTable(paSetup); PagedArray latout(TiledShape(latticeShape, tileShape), paTable); Timer timer; LatticeExpr expr(amp(lat,lat)); latout.copyData (expr); timer.show ("amp(lat,lat) "); } { Table t("tLatticeExpr3_tmp.tab"); PagedArray lat(t); SetupNewTable paSetup("tLatticeExpr3_tmp.tab2", TableDesc(), Table::New); Table paTable(paSetup); PagedArray latout(TiledShape(latticeShape, tileShape), paTable); Timer timer; LatticeExpr expr(lat+lat); latout.copyData (expr); timer.show ("lat+lat "); } { Table t("tLatticeExpr3_tmp.tab"); PagedArray lat(t); SetupNewTable paSetup("tLatticeExpr3_tmp.tab3", TableDesc(), Table::New); Table paTable(paSetup); PagedArray latout(TiledShape(latticeShape, tileShape), paTable); Timer timer; LatticeExpr expr(2*min(lat)*2*lat); latout.copyData (expr); timer.show ("2*min(lat)*2*lat"); } { Table t("tLatticeExpr3_tmp.tab"); PagedArray lat(t); SetupNewTable paSetup("tLatticeExpr3_tmp.tab3", TableDesc(), Table::New); Table paTable(paSetup); PagedArray latout(TiledShape(latticeShape, tileShape), paTable); Timer timer; LatticeExpr expr(lat*2*min(lat)*2); latout.copyData (expr); timer.show ("lat*2*min(lat)*2"); } { Table t1("tLatticeExpr3_tmp.tab"); PagedArray lat1(t1); Table t2("tLatticeExpr3_tmp.tab2"); PagedArray lat2(t2); SetupNewTable paSetup("tLatticeExpr3_tmp.tab3", TableDesc(), Table::New); Table paTable(paSetup); PagedArray latout(TiledShape(latticeShape, tileShape), paTable); Timer timer; LatticeExpr expr(lat1 * lat2); latout.copyData (expr); timer.show ("lat1 * lat2 "); } { Table t1("tLatticeExpr3_tmp.tab"); PagedArray lat1(t1); Table t2("tLatticeExpr3_tmp.tab2"); PagedArray lat2(t2); SetupNewTable paSetup("tLatticeExpr3_tmp.tab3", TableDesc(), Table::New); Table paTable(paSetup); PagedArray latout(TiledShape(latticeShape, tileShape), paTable); Timer timer; LatticeExpr expr(2*lat1 + 3*lat2); latout.copyData (expr); timer.show ("2*lat1 + 3*lat2 "); } { Table t("tLatticeExpr3_tmp.tab3"); PagedArray lat(t); Array arr(tileShape); indgen(arr, Complex(0,0), Complex(1,1)); RO_LatticeIterator iter(lat, tileShape); Timer timer; for (iter.reset(); !iter.atEnd(); iter++) { if (! allEQ (iter.cursor(), arr*Complex(8))) { cout << "result mismatches" << endl; return 1; } arr += Complex(tileShape.product()); } timer.show ("checking "); } { Table t("tLatticeExpr3_tmp.tab"); PagedArray lat(t); SetupNewTable paSetup("tLatticeExpr3_tmp.tab2", TableDesc(), Table::New); Table paTable(paSetup); PagedArray latout(TiledShape(latticeShape, tileShape), paTable); Timer timer; LCPagedMask mask (lat.shape(), lat.tableName() + "/mask"); mask.set (True); timer.show ("filling mask "); SubLattice sublat (lat, mask); timer.mark(); LatticeExpr expr(sublat+sublat); latout.copyData (expr); timer.show ("sublat+sublat "); timer.mark(); LatticeExpr expr2((mean(sublat)-lat.shape().product()/3)*lat); latout.copyData (expr2); timer.show ("mean(sublat)*lat"); timer.mark(); LatticeExpr expr3(amp(sublat,lat)); latout.copyData (expr3); timer.show ("amp(sublat,lat) "); } if (latticeShape.product() <= 1024*1024) { Array arr1(latticeShape); Array arr5(latticeShape); indgen(arr1, Complex(0,0), Complex(1,1)); ArrayLattice al1(arr1); ArrayLattice al5(latticeShape); cout << "arr1+arr1" << endl; Timer timer; arr5 = arr1 + arr1; timer.show ("as array "); timer.mark(); al5.copyData (LatticeExpr(al1+al1)); timer.show ("as lattice"); cout << "arr1+arr1+arr1" << endl; timer.mark(); arr5 = arr1 + arr1 + arr1; timer.show ("as array "); timer.mark(); al5.copyData (LatticeExpr(al1+al1+al1)); timer.show ("as lattice"); cout << "arr1+arr1+arr1+arr1" << endl; timer.mark(); arr5 = arr1 + arr1 + arr1 + arr1; timer.show ("as array "); timer.mark(); al5.copyData (LatticeExpr(al1+al1+al1+al1)); timer.show ("as lattice"); cout << "arr1+arr1+arr1+arr1+arr1" << endl; timer.mark(); arr5 = arr1 + arr1 + arr1 + arr1 + arr1; timer.show ("as array "); timer.mark(); al5.copyData (LatticeExpr(al1+al1+al1+al1+al1)); timer.show ("as lattice"); cout << "((((arr1+arr1)+arr1)+arr1)+arr1)+arr1" << endl; timer.mark(); arr5 = ((((arr1 + arr1) + arr1) + arr1) + arr1) + arr1; timer.show ("as array "); timer.mark(); al5.copyData (LatticeExpr(((((al1+al1)+al1)+al1)+al1)+al1)); timer.show ("as lattice"); cout << "arr1+(arr1+(arr1+(arr1+(arr1+arr1))))" << endl; timer.mark(); arr5 = arr1 + (arr1 + (arr1 + (arr1 + (arr1 + arr1)))); timer.show ("as array "); timer.mark(); al5.copyData (LatticeExpr(al1+(al1+(al1+(al1+(al1+al1)))))); timer.show ("as lattice"); cout << "arr1+arr1+arr1+arr1+arr1+arr1+arr1+arr1+arr1+arr1" << endl; timer.mark(); arr5 = arr1+arr1+arr1+arr1+arr1+arr1+arr1+arr1+arr1+arr1; timer.show ("as array "); timer.mark(); al5.copyData (LatticeExpr(al1+al1+al1+al1+al1+al1+al1+al1+al1+al1)); timer.show ("as lattice"); } { // Force TempLattice on disk. TempLattice pa1(latticeShape, 0); TempLattice pa2(latticeShape, 0); pa1.set (1); pa2.set (2); LatticeExpr expr(2*pa2); Timer timer; pa1 += expr; timer.show ("+= disk "); AlwaysAssertExit (allEQ(pa1.get(), float(5))); } { // Force TempLattice in memory. TempLattice pa1(latticeShape, 100); TempLattice pa2(latticeShape, 100); pa1.set (1); pa2.set (2); LatticeExpr expr(2*pa2); Timer timer; pa1 += expr; timer.show ("+= memory "); AlwaysAssertExit (allEQ(pa1.get(), float(5))); } { // Force TempLattice on disk. TempLattice pa1(latticeShape, 0); TempLattice pa2(latticeShape, 0); pa1.set (1); pa2.set (2); LatticeExpr expr(pa1+2*pa2); Timer timer; pa1.copyData (expr); timer.show (" = disk "); AlwaysAssertExit (allEQ(pa1.get(), float(5))); } { // Force TempLattice in memory. TempLattice pa1(latticeShape, 100); TempLattice pa2(latticeShape, 100); pa1.set (1); pa2.set (2); LatticeExpr expr(pa1+2*pa2); Timer timer; pa1.copyData (expr); timer.show (" = memory "); AlwaysAssertExit (allEQ(pa1.get(), float(5))); } { // Add a scalar to the lattice. TempLattice pa1(latticeShape, 100); pa1.set (3); LatticeExpr expr(4); Timer timer; pa1 *= expr; timer.show ("+= sca mem"); AlwaysAssertExit (allEQ(pa1.get(), float(12))); } } catch (std::exception& x) { cerr << "aipserror: error " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/lattices/LEL/test/tLatticeExpr3Node.cc000066400000000000000000000123611476623553700223420ustar00rootroot00000000000000//# tLatticeExprNode3.cc: Test program for regions in LatticeExprNode //# Copyright (C) 1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Bool checkFloat (const LatticeExprNode& expr, const SubLattice& lat, const LCRegion& region) { SubLattice sublat (lat, region); Array result; result = sublat.get(); Array mask; if (sublat.isMasked()) { mask = sublat.getMask(); } // Test if result is no scalar. if (expr.isScalar()) { cout << " result should be an array" << endl; return False; } // Test if the shape matches. IPosition shape = result.shape(); if (expr.shape() != shape) { cout << " mismatch in result shape" << endl; return False; } // Get the result (value and optional mask). LELArray arr(shape); IPosition origin(shape); origin = 0; Slicer slice(origin, shape); expr.eval (arr, slice); // Check if the values match. if (! allEQ (arr.value(), result)) { cout << " expected value " << result << endl; cout << " got array " << arr.value() << endl; return False; } // Check if there is a mask if it should be. if (sublat.isMasked() != arr.isMasked()) { cout << " mismatch in arr.isMasked" << endl; return False; } // If masked, test if matches. // If entire mask is false, the values can be anything (thus not checked). if (arr.isMasked()) { if (! allEQ (arr.mask(), mask)) { cout << " expected mask " << mask << endl; cout << " got " << arr.mask() << endl; return False; } } return True; } Bool doIt (const SubLattice& aF, const LCRegion& region1, const LCRegion& region2) { Bool ok = True; LatticeExprNode node(aF); if (!checkFloat (node[region1], aF, region1)) ok = False; if (!checkFloat (node[region2], aF, region2)) ok = False; if (!checkFloat (node[region1 && region2], aF, LCIntersection(region1,region2))) ok = False; if (!checkFloat (node[region1 || region2], aF, LCUnion(region1,region2))) ok = False; if (!checkFloat (node[region1 - region2], aF, LCDifference(region1,region2))) ok = False; if (!checkFloat (node[!region1], aF, LCComplement(region1))) ok = False; return ok; } int main() { Bool ok = True; try { IPosition shape(2,5,5); Array arra(shape); indgen (arra, Float(1), Float(1)); ArrayLattice aF(arra); LCBox box1(shape); LCBox box2(IPosition(2,0,0), shape-2, shape); LCEllipsoid cir1(IPosition(2,2,2),3,shape); LCEllipsoid cir2(IPosition(2,1,3),3,shape); if (!doIt(SubLattice(aF), box1, box2)) { ok = False; } if (!doIt(SubLattice(aF), cir1, box1)) { ok = False; } if (!doIt(SubLattice(aF), box2, cir2)) { ok = False; } Array mat1(shape); mat1 = True; mat1(IPosition(2,1,0)) = False; LCPixelSet mask1 (mat1, LCBox(shape)); SubLattice sublat (aF, mask1); if (!doIt(sublat, box1, box2)) { ok = False; } if (!doIt(sublat, cir1, box1)) { ok = False; } if (!doIt(sublat, box2, cir2)) { ok = False; } } catch (std::exception& x) { cout << "Caught exception: " << x.what() << endl; ok = False; } if (ok) { cout << "OK" << endl; return 0; } return 1; } casacore-3.7.1/lattices/LEL/test/tLatticeExprNode.cc000066400000000000000000003562761476623553700222770ustar00rootroot00000000000000//# tLatticeExprNode.cc: Basic test program for LEL classes //# Copyright (C) 1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Bool checkInfo (const LatticeExprNode& expr, const IPosition& shape, const Bool shouldBeScalar, const Bool undefinedScalar, const DataType dtype, const Bool emptyShape=False); Bool compareScalarFloat (const LatticeExprNode expr, const LatticeExprNode expr2, const Float bFVal, const IPosition shape); Bool compareScalarDouble (const LatticeExprNode expr, const LatticeExprNode expr2, const Double bDVal, const IPosition shape); Bool compareScalarComplex(const LatticeExprNode expr, const LatticeExprNode expr2, const Complex bCVal, const IPosition shape); Bool compareScalarDComplex(const LatticeExprNode expr, const LatticeExprNode expr2, const DComplex bDCVal, const IPosition shape); Bool compareScalarBool (const LatticeExprNode expr, const LatticeExprNode expr2, const Bool bBVal, const IPosition shape); Bool checkFloat (const LatticeExprNode& expr, const Float result, const IPosition& shape, const Bool shouldBeScalar, const Bool undefinedScalar); Bool checkFloatRepl (const LatticeExprNode& expr, const Float result, const IPosition& shape, const Array& replArray, Float replScalar, Bool isReplScalar); Bool checkDouble (const LatticeExprNode& expr, const Double result, const IPosition& shape, const Bool shouldBeScalar, const Bool undefinedScalar); Bool checkComplex (const LatticeExprNode& expr, const Complex result, const IPosition& shape, const Bool shouldBeScalar, const Bool undefinedScalar); Bool checkDComplex (const LatticeExprNode& expr, const DComplex result, const IPosition& shape, const Bool shouldBeScalar, const Bool undefinedScalar); Bool checkBool (const LatticeExprNode& expr, const Bool result, const IPosition& shape, const Bool shouldBeScalar, const Bool undefinedScalar, const Bool emptyShape=False); Bool checkBoolRepl (const LatticeExprNode& expr, const Bool result, const IPosition& shape, const Array& replArray, Bool replScalar, Bool isReplScalar); Bool checkMask (const LatticeExprNode& expr, Bool hasMask, const Array& result); Bool doIt (const MaskedLattice& aF, const MaskedLattice& bF, const MaskedLattice& cF, const MaskedLattice& bD, const MaskedLattice& cD, const MaskedLattice& bC, const MaskedLattice& cC, const MaskedLattice& bDC, const MaskedLattice& cDC, const MaskedLattice& aB, const MaskedLattice& bB, const MaskedLattice& cB, Float bFVal, Float cFVal, Double bDVal, Double cDVal, Complex bCVal, Complex cCVal, DComplex bDCVal, DComplex cDCVal, Bool aBVal, Bool bBVal, Bool cBVal, uInt nb) { Bool ok = True; IPosition shape = aF.shape(); // //************************************************************************ // // Test constructors, get*, eval, shape, dataType, isScalar functions // cout << endl << "Constant contructors, get*, eval, shape, dataType, isScalar" << endl; cout << "LatticeExprNode (constant T) " << endl; { cout << " Int " << endl; Int bIVal = Int(bFVal); LatticeExprNode expr(bIVal); if (!checkFloat (expr, Float(bIVal), shape, True, False)) ok = False; } { cout << " Float" << endl; LatticeExprNode expr(bFVal); if (!checkFloat (expr, bFVal, shape, True, False)) ok = False; } { cout << " Double" << endl; LatticeExprNode expr(bDVal); if (!checkDouble(expr, bDVal, shape, True, False)) ok = False; } { cout << " Complex" << endl; LatticeExprNode expr(bCVal); if (!checkComplex(expr, bCVal, shape, True, False)) ok = False; } { cout << " DComplex" << endl; LatticeExprNode expr(bDCVal); if (!checkDComplex(expr, bDCVal, shape, True, False)) ok = False; } { cout << " Bool" << endl; LatticeExprNode expr(bBVal); if (!checkBool(expr, bBVal, shape, True, False)) ok = False; } // //************************************************************************ // // Test LELInterface constructors // // Assume non-LELInterface constructor gives the right result // and compare // cout << endl << "LELInterface constructors" << endl; cout << "LatticeExprNode(std::shared_ptr>&) " << endl; { cout << " Float" << endl; auto pExpr = std::make_shared>(bFVal); LatticeExprNode expr(pExpr); LatticeExprNode expr2(bFVal); if (!compareScalarFloat (expr, expr2, bFVal, shape)) ok = False; } { cout << " Double" << endl; auto pExpr = std::make_shared>(bDVal); LatticeExprNode expr(pExpr); LatticeExprNode expr2(bDVal); if (!compareScalarDouble (expr, expr2, bDVal, shape)) ok = False; } { cout << " Complex" << endl; auto pExpr = std::make_shared>(bCVal); LatticeExprNode expr(pExpr); LatticeExprNode expr2(bCVal); if (!compareScalarComplex (expr, expr2, bCVal, shape)) ok = False; } { cout << " DComplex" << endl; auto pExpr = std::make_shared>(bDCVal); LatticeExprNode expr(pExpr); LatticeExprNode expr2(bDCVal); if (!compareScalarDComplex (expr, expr2, bDCVal, shape)) ok = False; } { cout << " Bool" << endl; auto pExpr = std::make_shared>(bBVal); LatticeExprNode expr(pExpr); LatticeExprNode expr2(bBVal); if (!compareScalarBool (expr, expr2, bBVal, shape)) ok = False; } // //************************************************************************ // // Test assignment and copy constructor // cout << endl; { cout << "Copy constructor " << endl; LatticeExprNode expr(bDVal); LatticeExprNode expr2(expr); if (!compareScalarDouble (expr, expr2, bDVal, shape)) ok = False; } { cout << "Assignment " << endl; LatticeExprNode expr(bDVal); LatticeExprNode expr2; expr2 = expr; if (!compareScalarDouble (expr, expr2, bDVal, shape)) ok = False; } // //************************************************************************ // // Check unary operators. // cout << endl << "Unary operator +" << endl; { cout << " Float Scalar" << endl; LatticeExprNode expr1(bFVal); LatticeExprNode expr2 = +expr1; if (!checkFloat (expr2, bFVal, shape, True, False)) ok = False; } { cout << " Double Scalar" << endl; LatticeExprNode expr1(bDVal); LatticeExprNode expr2 = +expr1; if (!checkDouble (expr2, bDVal, shape, True, False)) ok = False; } { cout << " Complex Scalar" << endl; LatticeExprNode expr1(bCVal); LatticeExprNode expr2 = +expr1; if (!checkComplex(expr2, bCVal, shape, True, False)) ok = False; } { cout << " DComplex Scalar" << endl; LatticeExprNode expr1(bDCVal); LatticeExprNode expr2 = +expr1; if (!checkDComplex(expr2, bDCVal, shape, True, False)) ok = False; } { cout << " Float Array" << endl; LatticeExprNode expr1(bF); LatticeExprNode expr2 = +expr1; if (!checkFloat (expr2, bFVal, shape, False, False)) ok = False; if (!checkMask (expr2, bF.isMasked(), bF.getMask())) ok = False; } { cout << " Double Array" << endl; LatticeExprNode expr1(bD); LatticeExprNode expr2 = +expr1; if (!checkDouble (expr2, bDVal, shape, False, False)) ok = False; } { cout << " Complex Array" << endl; LatticeExprNode expr1(bC); LatticeExprNode expr2 = +expr1; if (!checkComplex(expr2, bCVal, shape, False, False)) ok = False; } { cout << " DComplex Array" << endl; LatticeExprNode expr1(bDC); LatticeExprNode expr2 = +expr1; if (!checkDComplex(expr2, bDCVal, shape, False, False)) ok = False; } cout << "Unary operator -" << endl; { cout << " Float Scalar" << endl; LatticeExprNode expr1(bFVal); LatticeExprNode expr2 = -expr1; if (!checkFloat (expr2, -bFVal, shape, True, False)) ok = False; } { cout << " Complex Scalar" << endl; LatticeExprNode expr1(bCVal); LatticeExprNode expr2 = -expr1; if (!checkComplex (expr2, -bCVal, shape, True, False)) ok = False; } { cout << " Float Array" << endl; LatticeExprNode expr1(bF); LatticeExprNode expr2 = -expr1; if (!checkFloat (expr2, -bFVal, shape, False, False)) ok = False; if (!checkMask (expr2, bF.isMasked(), bF.getMask())) ok = False; } { cout << " Complex Array" << endl; LatticeExprNode expr1(bC); LatticeExprNode expr2 = -expr1; if (!checkComplex (expr2, -bCVal, shape, False, False)) ok = False; } cout << "Unary operator !" << endl; { cout << " Bool Scalar" << endl; LatticeExprNode expr1(bBVal); LatticeExprNode expr2 = !expr1; if (!checkBool(expr2, (!bBVal), shape, True, False)) ok = False; } { cout << " Bool Array" << endl; LatticeExprNode expr1(bB); LatticeExprNode expr2 = !expr1; if (!checkBool(expr2, (!bBVal), shape, False, False)) ok = False; } // //************************************************************************ // // Check binary operators. // cout << endl << "Binary operator +" << endl; { cout << " Float Scalar" << endl; LatticeExprNode expr1(bFVal); LatticeExprNode expr2(cFVal); LatticeExprNode expr3 = expr1+expr2; if (!checkFloat (expr3, bFVal+cFVal, shape, True, False)) ok = False; } { cout << " Double Scalar" << endl; LatticeExprNode expr1(bDVal); LatticeExprNode expr2(cDVal); LatticeExprNode expr3 = expr1+expr2; if (!checkDouble(expr3, bDVal+cDVal, shape, True, False)) ok = False; } { cout << " Complex Scalar" << endl; LatticeExprNode expr1(bCVal); LatticeExprNode expr2(cCVal); LatticeExprNode expr3 = expr1+expr2; if (!checkComplex (expr3, bCVal+cCVal, shape, True, False)) ok = False; } { cout << " DComplex Scalar" << endl; LatticeExprNode expr1(bDCVal); LatticeExprNode expr2(cDCVal); LatticeExprNode expr3 = expr1+expr2; if (!checkDComplex (expr3, bDCVal+cDCVal, shape, True, False)) ok = False; } { cout << " Float Array" << endl; LatticeExprNode expr1(bF); LatticeExprNode expr2(cF); LatticeExprNode expr3 = expr1+expr2; if (!checkFloat (expr3, bFVal+cFVal, shape, False, False)) ok = False; if (!checkMask (expr3, (bF.isMasked() || cF.isMasked()), bF.getMask() && cF.getMask())) ok = False; } { cout << " Double Array" << endl; LatticeExprNode expr1(bD); LatticeExprNode expr2(cD); LatticeExprNode expr3 = expr1+expr2; if (!checkDouble(expr3, bDVal+cDVal, shape, False, False)) ok = False; } { cout << " Complex Array" << endl; LatticeExprNode expr1(bC); LatticeExprNode expr2(cC); LatticeExprNode expr3 = expr1+expr2; if (!checkComplex (expr3, bCVal+cCVal, shape, False, False)) ok = False; } { cout << " DComplex Array" << endl; LatticeExprNode expr1(bDC); LatticeExprNode expr2(cDC); LatticeExprNode expr3 = expr1+expr2; if (!checkDComplex (expr3, bDCVal+cDCVal, shape, False, False)) ok = False; } cout << "Binary operator -" << endl; { cout << " Float Scalar" << endl; LatticeExprNode expr1(bFVal); LatticeExprNode expr2(cFVal); LatticeExprNode expr3 = expr1-expr2; if (!checkFloat (expr3, bFVal-cFVal, shape, True, False)) ok = False; } { cout << " Complex Scalar" << endl; LatticeExprNode expr1(bCVal); LatticeExprNode expr2(cCVal); LatticeExprNode expr3 = expr1-expr2; if (!checkComplex (expr3, bCVal-cCVal, shape, True, False)) ok = False; } { cout << " Float Array" << endl; LatticeExprNode expr1(bF); LatticeExprNode expr2(cF); LatticeExprNode expr3 = expr1-expr2; if (!checkFloat (expr3, bFVal-cFVal, shape, False, False)) ok = False; if (!checkMask (expr3, (bF.isMasked() || cF.isMasked()), bF.getMask() && cF.getMask())) ok = False; } { cout << " Complex Array" << endl; LatticeExprNode expr1(bC); LatticeExprNode expr2(cC); LatticeExprNode expr3 = expr1-expr2; if (!checkComplex (expr3, bCVal-cCVal, shape, False, False)) ok = False; } cout << "Binary operator *" << endl; { cout << " Float Scalar" << endl; LatticeExprNode expr1(bFVal); LatticeExprNode expr2(cFVal); LatticeExprNode expr3 = expr1*expr2; if (!checkFloat (expr3, bFVal*cFVal, shape, True, False)) ok = False; } { cout << " Complex Scalar" << endl; LatticeExprNode expr1(bCVal); LatticeExprNode expr2(cCVal); LatticeExprNode expr3 = expr1*expr2; if (!checkComplex(expr3, bCVal*cCVal, shape, True, False)) ok = False; } { cout << " Float Array" << endl; LatticeExprNode expr1(bF); LatticeExprNode expr2(cF); LatticeExprNode expr3 = expr1*expr2; if (!checkFloat (expr3, bFVal*cFVal, shape, False, False)) ok = False; if (!checkMask (expr3, (bF.isMasked() || cF.isMasked()), bF.getMask() && cF.getMask())) ok = False; } { cout << " Complex Array" << endl; LatticeExprNode expr1(bC); LatticeExprNode expr2(cC); LatticeExprNode expr3 = expr1*expr2; if (!checkComplex(expr3, bCVal*cCVal, shape, False, False)) ok = False; } cout << "Binary operator /" << endl; { cout << " Float Scalar" << endl; LatticeExprNode expr1(bFVal); LatticeExprNode expr2(cFVal); LatticeExprNode expr3 = expr1/expr2; if (!checkFloat (expr3, bFVal/cFVal, shape, True, False)) ok = False; } { cout << " Complex Scalar" << endl; LatticeExprNode expr1(bCVal); LatticeExprNode expr2(cCVal); LatticeExprNode expr3 = expr1/expr2; if (!checkComplex (expr3, bCVal/cCVal, shape, True, False)) ok = False; } { cout << " Float Array" << endl; LatticeExprNode expr1(bF); LatticeExprNode expr2(cF); LatticeExprNode expr3 = expr1/expr2; if (!checkFloat (expr3, bFVal/cFVal, shape, False, False)) ok = False; if (!checkMask (expr3, (bF.isMasked() || cF.isMasked()), bF.getMask() && cF.getMask())) ok = False; } { cout << " Complex Scalar" << endl; LatticeExprNode expr1(bC); LatticeExprNode expr2(cC); LatticeExprNode expr3 = expr1/expr2; if (!checkComplex (expr3, bCVal/cCVal, shape, False, False)) ok = False; } cout << "Binary operator ==" << endl; { cout << " Bool Scalar" << endl; LatticeExprNode expr1(bBVal); LatticeExprNode expr2(cBVal); LatticeExprNode expr3(expr1==expr2); if (!checkBool (expr3, (bBVal==cBVal), shape, True, False)) ok = False; LatticeExprNode expr4(expr1==expr1); if (!checkBool (expr4, True, shape, True, False)) ok = False; } { cout << " Float Scalar" << endl; LatticeExprNode expr1(bFVal); LatticeExprNode expr2(cFVal); LatticeExprNode expr3(expr1==expr2); if (!checkBool (expr3, (bFVal==cFVal), shape, True, False)) ok = False; LatticeExprNode expr4(expr1==expr1); if (!checkBool (expr4, True, shape, True, False)) ok = False; } { cout << " Complex Scalar" << endl; LatticeExprNode expr1(bCVal); LatticeExprNode expr2(cCVal); LatticeExprNode expr3(expr1==expr2); if (!checkBool (expr3, (bCVal==cCVal), shape, True, False)) ok = False; LatticeExprNode expr4(expr1==expr1); if (!checkBool (expr4, True, shape, True, False)) ok = False; } { cout << " Bool Array" << endl; LatticeExprNode expr1(bB); LatticeExprNode expr2(cB); LatticeExprNode expr3(expr1==expr2); if (!checkBool (expr3, (bBVal==cBVal), shape, False, False)) ok = False; if (!checkMask (expr3, (bB.isMasked() || cB.isMasked()), bB.getMask() && cB.getMask())) ok = False; LatticeExprNode expr4(expr1==expr1); if (!checkBool (expr4, True, shape, False, False)) ok = False; if (!checkMask (expr4, bB.isMasked(), bB.getMask())) ok = False; } { cout << " Float Array" << endl; LatticeExprNode expr1(bF); LatticeExprNode expr2(cF); LatticeExprNode expr3(expr1==expr2); if (!checkBool (expr3, (bFVal==cFVal), shape, False, False)) ok = False; if (!checkMask (expr3, (bF.isMasked() || cF.isMasked()), bF.getMask() && cF.getMask())) ok = False; LatticeExprNode expr4(expr1==expr1); if (!checkBool (expr4, True, shape, False, False)) ok = False; if (!checkMask (expr4, bF.isMasked(), bF.getMask())) ok = False; } { cout << " Complex Array" << endl; LatticeExprNode expr1(bC); LatticeExprNode expr2(bC); LatticeExprNode expr3(expr1==expr2); if (!checkBool (expr3, (bCVal==bCVal), shape, False, False)) ok = False; } cout << "Binary operator !=" << endl; { cout << " Bool Scalar" << endl; LatticeExprNode expr1(bBVal); LatticeExprNode expr2(cBVal); LatticeExprNode expr3(expr1!=expr2); if (!checkBool (expr3, (bBVal!=cBVal), shape, True, False)) ok = False; LatticeExprNode expr4(expr1!=expr1); if (!checkBool (expr4, False, shape, True, False)) ok = False; } { cout << " Float Scalar" << endl; LatticeExprNode expr1(bFVal); LatticeExprNode expr2(cFVal); LatticeExprNode expr3(expr1!=expr2); if (!checkBool (expr3, (bFVal!=cFVal), shape, True, False)) ok = False; LatticeExprNode expr4(expr1!=expr1); if (!checkBool (expr4, False, shape, True, False)) ok = False; } { cout << " Complex Scalar" << endl; LatticeExprNode expr1(bCVal); LatticeExprNode expr2(cCVal); LatticeExprNode expr3(expr1!=expr2); if (!checkBool (expr3, (bCVal!=cCVal), shape, True, False)) ok = False; LatticeExprNode expr4(expr1!=expr1); if (!checkBool (expr4, False, shape, True, False)) ok = False; } { cout << " Bool Array" << endl; LatticeExprNode expr1(bB); LatticeExprNode expr2(cB); LatticeExprNode expr3(expr1!=expr2); if (!checkBool (expr3, (bBVal!=cBVal), shape, False, False)) ok = False; if (!checkMask (expr3, (bB.isMasked() || cB.isMasked()), bB.getMask() && cB.getMask())) ok = False; LatticeExprNode expr4(expr1!=expr1); if (!checkBool (expr4, False, shape, False, False)) ok = False; if (!checkMask (expr4, bB.isMasked(), bB.getMask())) ok = False; } { cout << " Float Array" << endl; LatticeExprNode expr1(bF); LatticeExprNode expr2(cF); LatticeExprNode expr3(expr1!=expr2); if (!checkBool (expr3, (bFVal!=cFVal), shape, False, False)) ok = False; if (!checkMask (expr3, (bF.isMasked() || cF.isMasked()), bF.getMask() && cF.getMask())) ok = False; LatticeExprNode expr4(expr1!=expr1); if (!checkBool (expr4, False, shape, False, False)) ok = False; if (!checkMask (expr4, bF.isMasked(), bF.getMask())) ok = False; } { cout << " Complex Array" << endl; LatticeExprNode expr1(bC); LatticeExprNode expr2(cC); LatticeExprNode expr3(expr1!=expr2); if (!checkBool (expr3, (bCVal!=cCVal), shape, False, False)) ok = False; LatticeExprNode expr4(expr1!=expr1); if (!checkBool (expr4, False, shape, False, False)) ok = False; } cout << "Binary operator >" << endl; { cout << " Float Scalar" << endl; LatticeExprNode expr1(bFVal); LatticeExprNode expr2(cFVal); LatticeExprNode expr3(expr1>expr2); if (!checkBool (expr3, (bFVal>cFVal), shape, True, False)) ok = False; LatticeExprNode expr4(expr1>expr1); if (!checkBool (expr4, False, shape, True, False)) ok = False; } { cout << " Complex Scalar" << endl; LatticeExprNode expr1(bCVal); LatticeExprNode expr2(cCVal); LatticeExprNode expr3(expr1>expr2); if (!checkBool (expr3, (bCVal>cCVal), shape, True, False)) ok = False; LatticeExprNode expr4(expr1>expr1); if (!checkBool (expr4, False, shape, True, False)) ok = False; } { cout << " Float Array" << endl; LatticeExprNode expr1(bF); LatticeExprNode expr2(cF); LatticeExprNode expr3(expr1>expr2); if (!checkBool (expr3, (bFVal>cFVal), shape, False, False)) ok = False; if (!checkMask (expr3, (bF.isMasked() || cF.isMasked()), bF.getMask() && cF.getMask())) ok = False; LatticeExprNode expr4(expr1>expr1); if (!checkBool (expr4, False, shape, False, False)) ok = False; if (!checkMask (expr4, bF.isMasked(), bF.getMask())) ok = False; } { cout << " Complex Array" << endl; LatticeExprNode expr1(bC); LatticeExprNode expr2(cC); LatticeExprNode expr3(expr1>expr2); if (!checkBool (expr3, (bCVal>cCVal), shape, False, False)) ok = False; LatticeExprNode expr4(expr1>expr1); if (!checkBool (expr4, False, shape, False, False)) ok = False; } cout << "Binary operator >=" << endl; { cout << " Float Scalar" << endl; LatticeExprNode expr1(bFVal); LatticeExprNode expr2(cFVal); LatticeExprNode expr3(expr1>=expr2); if (!checkBool (expr3, (bFVal>=cFVal), shape, True, False)) ok = False; LatticeExprNode expr4(expr1>=expr1); if (!checkBool (expr4, True, shape, True, False)) ok = False; } { cout << " Complex Scalar" << endl; LatticeExprNode expr1(bCVal); LatticeExprNode expr2(cCVal); LatticeExprNode expr3(expr1>=expr2); if (!checkBool (expr3, (bCVal>=cCVal), shape, True, False)) ok = False; LatticeExprNode expr4(expr1>=expr1); if (!checkBool (expr4, True, shape, True, False)) ok = False; } { cout << " Float Array" << endl; LatticeExprNode expr1(bF); LatticeExprNode expr2(cF); LatticeExprNode expr3(expr1>=expr2); if (!checkBool (expr3, (bFVal>=cFVal), shape, False, False)) ok = False; if (!checkMask (expr3, (bF.isMasked() || cF.isMasked()), bF.getMask() && cF.getMask())) ok = False; LatticeExprNode expr4(expr1>=expr1); if (!checkBool (expr4, True, shape, False, False)) ok = False; if (!checkMask (expr4, bF.isMasked(), bF.getMask())) ok = False; } { cout << " Complex Array" << endl; LatticeExprNode expr1(bC); LatticeExprNode expr2(cC); LatticeExprNode expr3(expr1>=expr2); if (!checkBool (expr3, (bCVal>=cCVal), shape, False, False)) ok = False; LatticeExprNode expr4(expr1>=expr1); if (!checkBool (expr4, True, shape, False, False)) ok = False; } cout << "Binary operator <" << endl; { cout << " Float Scalar" << endl; LatticeExprNode expr1(bFVal); LatticeExprNode expr2(cFVal); LatticeExprNode expr3(expr10 && bBVal), shape, True, False)) ok = False; } cout << "all" << endl; { cout << " Bool Array" << endl; LatticeExprNode expr1(bB); LatticeExprNode expr2 = all(expr1); if (!checkBool(expr2, (nb==0 || bBVal), shape, True, False)) ok = False; } cout << "ntrue" << endl; { cout << " Bool Array" << endl; LatticeExprNode expr1(bB); LatticeExprNode expr2 = ntrue(expr1); Double result; if (bBVal) { result = nb; } else { result = 0.0; } if (!checkDouble(expr2, result, shape, True, False)) ok = False; } cout << "nfalse" << endl; { cout << " Bool Array" << endl; LatticeExprNode expr1(bB); LatticeExprNode expr2 = nfalse(expr1); Double result; if (!bBVal) { result = nb; } else { result = 0.0; } if (!checkDouble(expr2, result, shape, True, False)) ok = False; } cout << "isNaN" << endl; { cout << " Float Scalar" << endl; LatticeExprNode expr1(bFVal); LatticeExprNode expr2 = isNaN(expr1); if (!checkBool(expr2, False, shape, True, False)) ok = False; } { cout << " Complex Scalar" << endl; LatticeExprNode expr1(bCVal); LatticeExprNode expr2 = isNaN(expr1); if (!checkBool(expr2, False, shape, True, False)) ok = False; } { cout << " Float Array" << endl; LatticeExprNode expr1(bF); LatticeExprNode expr2 = isNaN(expr1); if (!checkBool(expr2, False, shape, False, False)) ok = False; if (!checkMask (expr2, bF.isMasked(), bF.getMask())) ok = False; } { cout << " Complex Array" << endl; LatticeExprNode expr1(bC); LatticeExprNode expr2 = isNaN(expr1); if (!checkBool(expr2, False, shape, False, False)) ok = False; if (!checkMask (expr2, bC.isMasked(), bC.getMask())) ok = False; } cout << "indexin" << endl; { Vector flags(2,True); LatticeExprNode expr1(0); LatticeExprNode expr2((ArrayLattice(flags))); LatticeExprNode expr = indexin(expr1,expr2); if (!checkBool (expr, True, IPosition(2,2,4), False, False, True)) ok = False; } { Vector flags(2,True); LatticeExprNode expr1(1); LatticeExprNode expr2((ArrayLattice(flags))); LatticeExprNode expr = indexin(expr1,expr2); if (!checkBool (expr, True, IPosition(2,10,2), False, False, True)) ok = False; } { Vector flags(3,False); LatticeExprNode expr1(0); LatticeExprNode expr2((ArrayLattice(flags))); LatticeExprNode expr = indexin(expr1,expr2); if (!checkBool (expr, False, IPosition(2,6,2), False, False, True)) ok = False; if (!checkBool (expr, False, IPosition(2,2,6), False, False, True)) ok = False; } // //************************************************************************ // // Check 2D functions // cout << endl << "2-argument functions " << endl; cout << "atan2" << endl; { cout << " Float Scalar" << endl; LatticeExprNode expr1(bFVal); LatticeExprNode expr2(cFVal); LatticeExprNode expr3 = atan2(expr1,expr2); if (!checkFloat (expr3, atan2(bFVal,cFVal), shape, True, False)) ok = False; } { cout << " Float Array" << endl; LatticeExprNode expr1(bF); LatticeExprNode expr2(cF); LatticeExprNode expr3 = atan2(expr1,expr2); if (!checkFloat (expr3, atan2(bFVal,cFVal), shape, False, False)) ok = False; if (!checkMask (expr3, (bF.isMasked() || cF.isMasked()), bF.getMask() && cF.getMask())) ok = False; } cout << "pow" << endl; { cout << " Float Scalar" << endl; LatticeExprNode expr1(bFVal); LatticeExprNode expr2(cFVal); LatticeExprNode expr3 = pow(expr1,expr2); if (!checkFloat (expr3, pow(bFVal,cFVal), shape, True, False)) ok = False; } { cout << " Complex Scalar" << endl; LatticeExprNode expr1(bCVal); LatticeExprNode expr2(cCVal); LatticeExprNode expr3 = pow(expr1,expr2); if (!checkComplex(expr3, pow(bCVal,cCVal), shape, True, False)) ok = False; } { cout << " Float Array" << endl; LatticeExprNode expr1(bF); LatticeExprNode expr2(cF); LatticeExprNode expr3 = pow(expr1,expr2); if (!checkFloat (expr3, pow(bFVal,cFVal), shape, False, False)) ok = False; if (!checkMask (expr3, (bF.isMasked() || cF.isMasked()), bF.getMask() && cF.getMask())) ok = False; } { cout << " Complex Array" << endl; LatticeExprNode expr1(bC); LatticeExprNode expr2(cC); LatticeExprNode expr3 = pow(expr1,expr2); if (!checkComplex(expr3, pow(bCVal,cCVal), shape, False, False)) ok = False; } cout << "fmod" << endl; { cout << " Float Scalar" << endl; LatticeExprNode expr1(bFVal); LatticeExprNode expr2(cFVal); LatticeExprNode expr3 = fmod(expr1,expr2); if (!checkFloat (expr3, fmod(bFVal,cFVal), shape, True, False)) ok = False; } { cout << " Float Array" << endl; LatticeExprNode expr1(bF); LatticeExprNode expr2(cF); LatticeExprNode expr3 = fmod(expr1,expr2); if (!checkFloat (expr3, fmod(bFVal,cFVal), shape, False, False)) ok = False; if (!checkMask (expr3, (bF.isMasked() || cF.isMasked()), bF.getMask() && cF.getMask())) ok = False; } cout << "min" << endl; { cout << " Float Scalar" << endl; LatticeExprNode expr1(bFVal); LatticeExprNode expr2(cFVal); LatticeExprNode expr3 = min(expr1,expr2); if (!checkFloat (expr3, min(bFVal,cFVal), shape, True, False)) ok = False; } { cout << " Float Array" << endl; LatticeExprNode expr1(bF); LatticeExprNode expr2(cF); LatticeExprNode expr3 = min(expr1,expr2); if (!checkFloat (expr3, min(bFVal,cFVal), shape, False, False)) ok = False; if (!checkMask (expr3, (bF.isMasked() || cF.isMasked()), bF.getMask() && cF.getMask())) ok = False; } cout << "max" << endl; { cout << " Float Scalar" << endl; LatticeExprNode expr1(bFVal); LatticeExprNode expr2(cFVal); LatticeExprNode expr3 = max(expr1,expr2); if (!checkFloat (expr3, max(bFVal,cFVal), shape, True, False)) ok = False; } { cout << " Float Array" << endl; LatticeExprNode expr1(bF); LatticeExprNode expr2(cF); LatticeExprNode expr3 = max(expr1,expr2); if (!checkFloat (expr3, max(bFVal,cFVal), shape, False, False)) ok = False; if (!checkMask (expr3, (bF.isMasked() || cF.isMasked()), bF.getMask() && cF.getMask())) ok = False; } cout << "amp" << endl; { cout << " Float Scalar" << endl; LatticeExprNode expr1(bFVal); LatticeExprNode expr2(cFVal); LatticeExprNode expr3 = amp(expr1,expr2); Float result = sqrt(bFVal*bFVal+cFVal*cFVal); if (!checkFloat (expr3, result, shape, True, False)) ok = False; } { cout << " Complex Scalar" << endl; LatticeExprNode expr1(bCVal); LatticeExprNode expr2(cCVal); LatticeExprNode expr3 = amp(expr1,expr2); Complex result = sqrt(bCVal*bCVal+cCVal*cCVal); if (!checkComplex (expr3, result, shape, True, False)) ok = False; } { cout << " Float Array" << endl; LatticeExprNode expr1(bF); LatticeExprNode expr2(cF); LatticeExprNode expr3 = amp(expr1,expr2); Float result = sqrt(bFVal*bFVal+cFVal*cFVal); if (!checkFloat (expr3, result, shape, False, False)) ok = False; if (!checkMask (expr3, (bF.isMasked() || cF.isMasked()), bF.getMask() && cF.getMask())) ok = False; } { cout << " Complex Array" << endl; LatticeExprNode expr1(bC); LatticeExprNode expr2(cC); LatticeExprNode expr3 = amp(expr1,expr2); Complex result = sqrt(bCVal*bCVal+cCVal*cCVal); if (!checkComplex (expr3, result, shape, False, False)) ok = False; } cout << "pa" << endl; { cout << " Float Scalar" << endl; LatticeExprNode expr1(bFVal); LatticeExprNode expr2(cFVal); LatticeExprNode expr3 = pa(expr1,expr2); Float result = 90.0/M_PI*atan2(bFVal,cFVal); if (!checkFloat (expr3, result, shape, True, False)) ok = False; } { cout << " Float Array" << endl; LatticeExprNode expr1(bF); LatticeExprNode expr2(cF); LatticeExprNode expr3 = pa(expr1,expr2); Float result = 90.0/M_PI*atan2(bFVal,cFVal); if (!checkFloat (expr3, result, shape, False, False)) ok = False; if (!checkMask (expr3, (bF.isMasked() || cF.isMasked()), bF.getMask() && cF.getMask())) ok = False; } { cout << " Double Scalar" << endl; LatticeExprNode expr1(bDVal); LatticeExprNode expr2(cDVal); LatticeExprNode expr3 = pa(expr1,expr2); Double result = 90.0/M_PI*atan2(bDVal,cDVal); if (!checkDouble (expr3, result, shape, True, False)) ok = False; } { cout << " Double Array" << endl; LatticeExprNode expr1(bD); LatticeExprNode expr2(cD); LatticeExprNode expr3 = pa(expr1,expr2); Double result = 90.0/M_PI*atan2(bDVal,cDVal); if (!checkDouble (expr3, result, shape, False, False)) ok = False; } cout << "mask" << endl; { cout << " Float Scalar 1" << endl; LatticeExprNode expr1(bFVal); LatticeExprNode expr3 = mask(expr1); if (! checkInfo (expr3, shape, True, False, TpBool)) { ok = False; } else { if (expr3.getBool() != True) { cout << " expected True; result is " << expr3.getBool() << endl; ok = False; } } } { cout << " Float Scalar 2" << endl; LatticeExprNode expr1(min(bF)); LatticeExprNode expr3 = mask(expr1); if (! checkInfo (expr3, shape, True, False, TpBool)) { ok = False; } else { Bool result = (nb!=0); if (expr3.getBool() != result) { cout << " expected " << result << "; result is " << expr3.getBool() << endl; ok = False; } } } { cout << " Float Array" << endl; LatticeExprNode expr1(bF); LatticeExprNode expr3 = mask(expr1); if (! checkInfo (expr3, shape, False, False, TpBool)) { ok = False; } else { LELArray result(shape); Slicer section(IPosition(shape.nelements(), 0), shape); expr3.eval (result, section); if (! allEQ(result.value(), bF.getMask())) { cout << " expected " << bF.getMask() << " result is " << result.value() << endl; ok = False; } } if (!checkMask (expr3, False, bF.getMask())) ok = False; } { cout << " Bool Scalar" << endl; LatticeExprNode expr1(bBVal); LatticeExprNode expr3 = mask(expr1); if (! checkInfo (expr3, shape, True, False, TpBool)) { ok = False; } else { if (expr3.getBool() != True) { cout << " expected True; result is " << expr3.getBool() << endl; ok = False; } } } { cout << " Bool Array" << endl; LatticeExprNode expr1(bB); LatticeExprNode expr3 = mask(expr1); if (! checkInfo (expr3, shape, False, False, TpBool)) { ok = False; } else { LELArray result(shape); Slicer section(IPosition(shape.nelements(), 0), shape); expr3.eval (result, section); if (! allEQ(result.value(), bB.getMask())) { cout << " expected " << bB.getMask() << " result is " << result.value() << endl; ok = False; } } if (!checkMask (expr3, False, bB.getMask())) ok = False; } cout << "value" << endl; { cout << " Float Scalar 1" << endl; LatticeExprNode expr1(bFVal); LatticeExprNode expr3 = value(expr1); if (!checkFloat (expr3, bFVal, shape, True, False)) ok = False; } { cout << " Float Scalar 2" << endl; LatticeExprNode expr1(min(bF)); LatticeExprNode expr3 = value(expr1); if (!checkFloat (expr3, bFVal, shape, True, (nb==0))) ok = False; } { cout << " Float Array" << endl; LatticeExprNode expr1(bF); LatticeExprNode expr3 = value(expr1); if (!checkFloat (expr3, bFVal, shape, False, False)) ok = False; if (!checkMask (expr3, False, bF.getMask())) ok = False; } { cout << " Bool Scalar" << endl; LatticeExprNode expr1(bBVal); LatticeExprNode expr3 = value(expr1); if (!checkBool (expr3, bBVal, shape, True, False)) ok = False; } { cout << " Bool Array" << endl; LatticeExprNode expr1(bB); LatticeExprNode expr3 = value(expr1); if (!checkBool (expr3, bBVal, shape, False, False)) ok = False; if (!checkMask (expr3, False, bB.getMask())) ok = False; } cout << "replace" << endl; { cout << " Float Scalar" << endl; LatticeExprNode expr1(bF); LatticeExprNode expr2(cFVal); LatticeExprNode expr3 = replace(expr1,expr2); if (!checkFloatRepl (expr3, bFVal, shape, cF.get(), cFVal, True)) ok = False; if (!checkMask (expr3, bF.isMasked(), bF.getMask())) ok = False; } { cout << " Float Array" << endl; LatticeExprNode expr1(bF); LatticeExprNode expr2(cF); LatticeExprNode expr3 = replace(expr1,expr2); if (!checkFloatRepl (expr3, bFVal, shape, cF.get(), cFVal, False)) ok = False; if (!checkMask (expr3, bF.isMasked(), bF.getMask())) ok = False; } { cout << " Bool Scalar" << endl; LatticeExprNode expr1(bB); LatticeExprNode expr2(cBVal); LatticeExprNode expr3 = replace(expr1,expr2); if (!checkBoolRepl (expr3, bBVal, shape, cB.get(), cBVal, True)) ok = False; if (!checkMask (expr3, bB.isMasked(), bB.getMask())) ok = False; } { cout << " Bool Array" << endl; LatticeExprNode expr1(bB); LatticeExprNode expr2(cB); LatticeExprNode expr3 = replace(expr1,expr2); if (!checkBoolRepl (expr3, bBVal, shape, cB.get(), cBVal, False)) ok = False; if (!checkMask (expr3, bB.isMasked(), bB.getMask())) ok = False; } { cout << "Rebin" << endl; IPosition shapeIn(2, 10, 20); IPosition binfac(2, 2, 2); LatticeExprNode nodeBin(binfac); // IPosition binI(shapeIn.nelements()); for (uInt i=0; i lat(shapeIn); lat.set(1.0); SubLattice mLat(lat); RebinLattice rL(mLat, binI); IPosition shapeBin = rL.shape(); // LatticeExprNode expr = rebin(mLat,nodeBin); const Array& data = expr.getArrayFloat(); AlwaysAssert(data.shape().isEqual(shapeBin), AipsError); AlwaysAssert(allNear(data, Float(1.0), Float(1.0e-6)), AipsError); AlwaysAssert(allNear(data, rL.get(), 1.0e-6), AipsError); // Bool hasMask = rL.isMasked(); checkMask (expr, hasMask, rL.getMask()); } { cout << " Double" << endl; ArrayLattice lat(shapeIn); lat.set(1.0); SubLattice mLat(lat); RebinLattice rL(mLat, binI); IPosition shapeBin = rL.shape(); // LatticeExprNode expr = rebin(mLat,nodeBin); const Array& data = expr.getArrayDouble(); AlwaysAssert(data.shape().isEqual(shapeBin), AipsError); AlwaysAssert(allNear(data, Double(1.0), Double(1.0e-6)), AipsError); AlwaysAssert(allNear(data, rL.get(), 1.0e-6), AipsError); // Bool hasMask = rL.isMasked(); checkMask (expr, hasMask, rL.getMask()); } { cout << " Complex" << endl; ArrayLattice lat(shapeIn); Complex val(1.0, 1.0); lat.set(val); SubLattice mLat(lat); RebinLattice rL(mLat, binI); IPosition shapeBin = rL.shape(); // LatticeExprNode expr = rebin(mLat,nodeBin); const Array& data = expr.getArrayComplex(); AlwaysAssert(data.shape().isEqual(shapeBin), AipsError); AlwaysAssert(allNear(data, val, 1.0e-6), AipsError); AlwaysAssert(allNear(data, rL.get(), 1.0e-6), AipsError); // Bool hasMask = rL.isMasked(); checkMask (expr, hasMask, rL.getMask()); } { cout << " DComplex" << endl; ArrayLattice lat(shapeIn); DComplex val(1.0, 1.0); lat.set(val); SubLattice mLat(lat); RebinLattice rL(mLat, binI); IPosition shapeBin = rL.shape(); // LatticeExprNode expr = rebin(mLat,nodeBin); const Array& data = expr.getArrayDComplex(); AlwaysAssert(data.shape().isEqual(shapeBin), AipsError); AlwaysAssert(allNear(data, val, 1.0e-6), AipsError); AlwaysAssert(allNear(data, rL.get(), 1.0e-6), AipsError); // Bool hasMask = rL.isMasked(); checkMask (expr, hasMask, rL.getMask()); } } // //************************************************************************ // // Check 3D functions // cout << endl << "3-argument functions " << endl; cout << "iif" << endl; { cout << " Float Scalar,Scalar,Scalar" << endl; LatticeExprNode expr1(bFVal); LatticeExprNode expr2(cFVal); LatticeExprNode expr3 = iif(aBVal,expr1,expr2); if (!checkFloat (expr3, bFVal, shape, True, False)) ok = False; LatticeExprNode expr4 = iif(bBVal,expr1,expr2); if (!checkFloat (expr4, cFVal, shape, True, False)) ok = False; } { cout << " Double Scalar,Array,Scalar" << endl; LatticeExprNode expr1(bD); LatticeExprNode expr2(cDVal); LatticeExprNode expr3 = iif(aBVal,expr1,expr2); if (!checkDouble (expr3, bDVal, shape, False, False)) ok = False; LatticeExprNode expr4 = iif(bBVal,expr1,expr2); if (!checkDouble (expr4, cDVal, shape, False, False)) ok = False; } { cout << " Complex Scalar,Scalar,Array" << endl; LatticeExprNode expr1(bCVal); LatticeExprNode expr2(cC); LatticeExprNode expr3 = iif(aBVal,expr1,expr2); if (!checkComplex (expr3, bCVal, shape, False, False)) ok = False; LatticeExprNode expr4 = iif(bBVal,expr1,expr2); if (!checkComplex (expr4, cCVal, shape, False, False)) ok = False; } { cout << " DComplex Scalar,Array,Array" << endl; LatticeExprNode expr1(bDC); LatticeExprNode expr2(cDC); LatticeExprNode expr3 = iif(aBVal,expr1,expr2); if (!checkDComplex (expr3, bDCVal, shape, False, False)) ok = False; LatticeExprNode expr4 = iif(bBVal,expr1,expr2); if (!checkDComplex (expr4, cDCVal, shape, False, False)) ok = False; } { cout << " Bool Scalar,Array,Array" << endl; LatticeExprNode expr1(bB); LatticeExprNode expr2(cB); LatticeExprNode expr3 = iif(aBVal,expr1,expr2); if (!checkBool (expr3, bBVal, shape, False, False)) ok = False; LatticeExprNode expr4 = iif(bBVal,expr1,expr2); if (!checkBool (expr4, cBVal, shape, False, False)) ok = False; } { cout << " Float/Double Array,Scalar,Scalar" << endl; LatticeExprNode expr1(bFVal); LatticeExprNode expr2(cDVal); LatticeExprNode expr3 = iif(aB,expr1,expr2); if (!checkDouble (expr3, bDVal, shape, False, False)) ok = False; LatticeExprNode expr4 = iif(bB,expr1,expr2); if (!checkDouble (expr4, cDVal, shape, False, False)) ok = False; } { cout << " Complex/Double Array,Scalar,Array" << endl; LatticeExprNode expr1(bCVal); LatticeExprNode expr2(cD); LatticeExprNode expr3 = iif(aB,expr1,expr2); if (!checkDComplex (expr3, bDCVal, shape, False, False)) ok = False; LatticeExprNode expr4 = iif(bB,expr1,expr2); if (!checkDComplex (expr4, DComplex(cDVal,0), shape, False, False)) ok = False; } { cout << " Double Array,Array,Scalar" << endl; LatticeExprNode expr1(bD); LatticeExprNode expr2(cDVal); LatticeExprNode expr3 = iif(aB,expr1,expr2); if (!checkDouble (expr3, bDVal, shape, False, False)) ok = False; LatticeExprNode expr4 = iif(bB,expr1,expr2); if (!checkDouble (expr4, cDVal, shape, False, False)) ok = False; } { cout << " Double Array,Array,Array" << endl; LatticeExprNode expr1(bD); LatticeExprNode expr2(cD); LatticeExprNode expr3 = iif(aB,expr1,expr2); if (!checkDouble (expr3, bDVal, shape, False, False)) ok = False; LatticeExprNode expr4 = iif(bB,expr1,expr2); if (!checkDouble (expr4, cDVal, shape, False, False)) ok = False; } { cout << " Bool Array,Array,Scalar" << endl; LatticeExprNode expr1(bB); LatticeExprNode expr2(cBVal); LatticeExprNode expr3 = iif(aB,expr1,expr2); if (!checkBool (expr3, bBVal, shape, False, False)) ok = False; LatticeExprNode expr4 = iif(bB,expr1,expr2); if (!checkBool (expr4, cBVal, shape, False, False)) ok = False; } { cout << " Bool Array,Array,Array" << endl; LatticeExprNode expr1(bB); LatticeExprNode expr2(cB); LatticeExprNode expr3 = iif(aB,expr1,expr2); if (!checkBool (expr3, bBVal, shape, False, False)) ok = False; LatticeExprNode expr4 = iif(bB,expr1,expr2); if (!checkBool (expr4, cBVal, shape, False, False)) ok = False; } // //************************************************************************ // // Test conversion functions. The to* functions call // the public members makeFloat, makeDouble, makeComplex, // and makeDComplex so we don't have to test them again // explicitly // cout << endl << "Conversion functions" << endl; cout << "toFloat" << endl; { cout << " from Float Scalar" << endl; LatticeExprNode expr1(bFVal); LatticeExprNode expr2 = toFloat(expr1); if (!checkFloat (expr2, bFVal, shape, True, False)) ok = False; } { cout << " from Double Scalar" << endl; LatticeExprNode expr1(bDVal); LatticeExprNode expr2 = toFloat(expr1); if (!checkFloat (expr2, Float(bDVal), shape, True, False)) ok = False; } { cout << " from Float Array" << endl; LatticeExprNode expr1(bF); LatticeExprNode expr2 = toFloat(expr1); if (!checkFloat (expr2, bFVal, shape, False, False)) ok = False; if (!checkMask (expr2, bF.isMasked(), bF.getMask())) ok = False; } { cout << " from Double Array" << endl; LatticeExprNode expr1(bD); LatticeExprNode expr2 = toFloat(expr1); if (!checkFloat (expr2, Float(bDVal), shape, False, False)) ok = False; if (!checkMask (expr2, bF.isMasked(), bF.getMask())) ok = False; } cout << "toDouble" << endl; { cout << " from Float Scalar" << endl; LatticeExprNode expr1(bFVal); LatticeExprNode expr2 = toDouble(expr1); if (!checkDouble(expr2, Double(bFVal), shape, True, False)) ok = False; } { cout << " from Double Scalar" << endl; LatticeExprNode expr1(bDVal); LatticeExprNode expr2 = toDouble(expr1); if (!checkDouble(expr2, bDVal, shape, True, False)) ok = False; } { cout << " from Float Array" << endl; LatticeExprNode expr1(bF); LatticeExprNode expr2 = toDouble(expr1); if (!checkDouble(expr2, Double(bFVal), shape, False, False)) ok = False; } { cout << " from Double Scalar" << endl; LatticeExprNode expr1(bD); LatticeExprNode expr2 = toDouble(expr1); if (!checkDouble(expr2, bDVal, shape, False, False)) ok = False; } cout << "toComplex" << endl; { cout << " from Float Scalar" << endl; LatticeExprNode expr1(bFVal); LatticeExprNode expr2 = toComplex(expr1); if (!checkComplex(expr2, Complex(bFVal,0.0), shape, True, False)) ok = False; } { cout << " from Double Scalar" << endl; LatticeExprNode expr1(bDVal); LatticeExprNode expr2 = toComplex(expr1); if (!checkComplex(expr2, Complex(bDVal,0.0), shape, True, False)) ok = False; } { cout << " from Complex Scalar" << endl; LatticeExprNode expr1(bCVal); LatticeExprNode expr2 = toComplex(expr1); if (!checkComplex(expr2, Complex(bCVal), shape, True, False)) ok = False; } { cout << " from DComplex Scalar" << endl; LatticeExprNode expr1(bDCVal); LatticeExprNode expr2 = toComplex(expr1); if (!checkComplex(expr2, Complex(bDCVal), shape, True, False)) ok = False; } { cout << " from Float Array" << endl; LatticeExprNode expr1(bF); LatticeExprNode expr2 = toComplex(expr1); if (!checkComplex(expr2, Complex(bFVal,0.0), shape, False, False)) ok = False; } { cout << " from Double Scalar" << endl; LatticeExprNode expr1(bD); LatticeExprNode expr2 = toComplex(expr1); if (!checkComplex(expr2, Complex(bDVal,0.0), shape, False, False)) ok = False; } { cout << " from Complex Array" << endl; LatticeExprNode expr1(bC); LatticeExprNode expr2 = toComplex(expr1); if (!checkComplex(expr2, bCVal, shape, False, False)) ok = False; } { cout << " from DComplex Array" << endl; LatticeExprNode expr1(bDC); LatticeExprNode expr2 = toComplex(expr1); if (!checkComplex(expr2, Complex(bDCVal), shape, False, False)) ok = False; } cout << "toDComplex" << endl; { cout << " from Float Scalar" << endl; LatticeExprNode expr1(bFVal); LatticeExprNode expr2 = toDComplex(expr1); if (!checkDComplex(expr2, DComplex(bFVal,0.0), shape, True, False)) ok = False; } { cout << " from Double Scalar" << endl; LatticeExprNode expr1(bDVal); LatticeExprNode expr2 = toDComplex(expr1); if (!checkDComplex(expr2, DComplex(bDVal,0.0), shape, True, False)) ok = False; } { cout << " from Complex Scalar" << endl; LatticeExprNode expr1(bCVal); LatticeExprNode expr2 = toDComplex(expr1); DComplex result; result = bCVal; if (!checkDComplex(expr2, result, shape, True, False)) ok = False; } { cout << " from DComplex Scalar" << endl; LatticeExprNode expr1(bDCVal); LatticeExprNode expr2 = toDComplex(expr1); if (!checkDComplex(expr2, DComplex(bDCVal), shape, True, False)) ok = False; } { cout << " from Float Array" << endl; LatticeExprNode expr1(bF); LatticeExprNode expr2 = toDComplex(expr1); if (!checkDComplex(expr2, DComplex(bFVal,0.0), shape, False, False)) ok = False; } { cout << " from Double Scalar" << endl; LatticeExprNode expr1(bD); LatticeExprNode expr2 = toDComplex(expr1); if (!checkDComplex(expr2, DComplex(bDVal,0.0), shape, False, False)) ok = False; } { cout << " from DComplex Array" << endl; LatticeExprNode expr1(bC); LatticeExprNode expr2 = toDComplex(expr1); DComplex result; result = bCVal; if (!checkDComplex(expr2, result, shape, False, False)) ok = False; } { cout << " from DComplex Array" << endl; LatticeExprNode expr1(bDC); LatticeExprNode expr2 = toDComplex(expr1); if (!checkDComplex(expr2, DComplex(bDCVal), shape, False, False)) ok = False; } // //************************************************************************ // // Check casting functions. These return a LatticeExpr. Now // since a LatticeExpr isA Lattice, we can use the LatticeExprNode(Lattice) // constrcutors to convert it back to a LatticeExprNode and hence test it for validity // However, this never returns a scalar so we can't test scalar functionality // cout << endl << "Casting operators" << endl; cout << "LatticeExpr()" << endl; { cout << " from Float" << endl; LatticeExprNode expr1(bF); LatticeExpr expr2 = (LatticeExpr)expr1; LatticeExprNode expr3(expr2); if (!checkFloat (expr3, Float(bFVal), shape, False, False)) ok = False; if (!checkMask (expr3, bF.isMasked(), bF.getMask())) ok = False; } { cout << " from Double" << endl; LatticeExprNode expr1(bD); LatticeExpr expr2 = (LatticeExpr)expr1; LatticeExprNode expr3(expr2); if (!checkFloat (expr3, Float(bDVal), shape, False, False)) ok = False; if (!checkMask (expr3, bF.isMasked(), bF.getMask())) ok = False; } cout << "LatticeExpr()" << endl; { cout << " from Float" << endl; LatticeExprNode expr1(bF); LatticeExpr expr2 = (LatticeExpr)expr1; LatticeExprNode expr3(expr2); if (!checkDouble(expr3, Double(bFVal), shape, False, False)) ok = False; } { cout << " from Double" << endl; LatticeExprNode expr1(bD); LatticeExpr expr2 = (LatticeExpr)expr1; LatticeExprNode expr3(expr2); if (!checkDouble(expr3, Double(bDVal), shape, False, False)) ok = False; } cout << "LatticeExpr()" << endl; { cout << " from Float" << endl; LatticeExprNode expr1(bF); LatticeExpr expr2 = (LatticeExpr)expr1; LatticeExprNode expr3(expr2); if (!checkComplex(expr3, Complex(bFVal,0.0), shape, False, False)) ok = False; } { cout << " from Double" << endl; LatticeExprNode expr1(bD); LatticeExpr expr2 = (LatticeExpr)expr1; LatticeExprNode expr3(expr2); if (!checkComplex(expr3, Complex(bDVal,0.0), shape, False, False)) ok = False; } { cout << " from Complex" << endl; LatticeExprNode expr1(bC); LatticeExpr expr2 = (LatticeExpr)expr1; LatticeExprNode expr3(expr2); if (!checkComplex(expr3, Complex(bCVal), shape, False, False)) ok = False; } { cout << " from DComplex" << endl; LatticeExprNode expr1(bDC); LatticeExpr expr2 = (LatticeExpr)expr1; LatticeExprNode expr3(expr2); if (!checkComplex(expr3, Complex(bDCVal), shape, False, False)) ok = False; } cout << "LatticeExpr()" << endl; { cout << " from Float" << endl; LatticeExprNode expr1(bF); LatticeExpr expr2 = (LatticeExpr)expr1; LatticeExprNode expr3(expr2); if (!checkDComplex(expr3, DComplex(bFVal,0.0), shape, False, False)) ok = False; } { cout << " from Double" << endl; LatticeExprNode expr1(bD); LatticeExpr expr2 = (LatticeExpr)expr1; LatticeExprNode expr3(expr2); if (!checkDComplex(expr3, DComplex(bDVal,0.0), shape, False, False)) ok = False; } { cout << " from Complex" << endl; LatticeExprNode expr1(bC); LatticeExpr expr2 = (LatticeExpr)expr1; LatticeExprNode expr3(expr2); DComplex result; result = bCVal; if (!checkDComplex(expr3, result, shape, False, False)) ok = False; } { cout << " from DComplex" << endl; LatticeExprNode expr1(bDC); LatticeExpr expr2 = (LatticeExpr)expr1; LatticeExprNode expr3(expr2); if (!checkDComplex(expr3, DComplex(bDCVal), shape, False, False)) ok = False; } cout << "LatticeExpr()" << endl; { cout << " from Bool" << endl; LatticeExprNode expr1(bB); LatticeExpr expr2 = (LatticeExpr)expr1; LatticeExprNode expr3(expr2); if (!checkBool(expr3, bBVal, shape, False, False)) ok = False; } return ok; } Bool compareScalarFloat (const LatticeExprNode expr, const LatticeExprNode expr2, const Float bVal, const IPosition shape) { LELArray Arr(shape); LELArray Arr2(shape); // Test LELArray copy constructor and assignment. LELArray Arrt(Arr); Arrt = Arr2; Float result, result2; IPosition origin(shape); origin = 0; Slicer region(origin, shape); Bool ok = True; if (expr.isScalar() != expr2.isScalar()) { cout << " result should be a scalar" << endl; cout << " results are " << expr.isScalar() << ", " << expr2.isScalar() << endl; ok = False; } if (expr.getFloat() != expr2.getFloat()) { cout << " result should be " << bVal << endl; cout << " Scalar results are " << expr.getFloat() << ", " << expr2.getFloat() << endl; ok = False; } expr.eval(result); expr2.eval(result2); if (result != result2) { cout << " result should be " << bVal << endl; cout << " Scalar results are " << result << ", " << result2 << endl; ok = False; } expr.eval(Arr, region); expr2.eval(Arr2, region); if (! allEQ (Arr.value(), Arr2.value())) { cout << " result should be " << bVal << endl; cout << " Array results are " << Arr.value()(origin) << ", " << Arr2.value()(origin) << endl; ok = False; } if (expr.dataType() != expr2.dataType()) { cout << " Data type should be " << TpFloat << endl; cout << " Data types are " << expr.dataType() << ", " << expr2.dataType() << endl; } if (expr.shape() != expr2.shape()) { cout << " Shape should be " << shape << endl; cout << " Shapes are " << expr.shape() << ", " << expr2.shape() << endl; ok = False; } return ok; } Bool compareScalarDouble (const LatticeExprNode expr, const LatticeExprNode expr2, const Double bVal, const IPosition shape) { LELArray Arr(shape); LELArray Arr2(shape); Double result, result2; IPosition origin(shape); origin = 0; Slicer region(origin, shape); Bool ok = True; if (expr.isScalar() != expr2.isScalar()) { cout << " result should be a scalar" << endl; cout << " results are " << expr.isScalar() << ", " << expr2.isScalar() << endl; ok = False; } if (expr.getDouble() != expr2.getDouble()) { cout << " result should be " << bVal << endl; cout << " Scalar results are " << expr.getDouble() << ", " << expr2.getDouble() << endl; ok = False; } expr.eval(result); expr2.eval(result2); if (result != result2) { cout << " result should be " << bVal << endl; cout << " Scalar results are " << result << ", " << result2 << endl; ok = False; } expr.eval(Arr, region); expr2.eval(Arr2, region); if (! allEQ (Arr.value(), Arr2.value())) { cout << " result should be " << bVal << endl; cout << " Array results are " << Arr.value()(origin) << ", " << Arr2.value()(origin) << endl; ok = False; } if (expr.dataType() != expr2.dataType()) { cout << " Data type should be " << TpDouble << endl; cout << " Data types are " << expr.dataType() << ", " << expr2.dataType() << endl; } if (expr.shape() != expr2.shape()) { cout << " Shape should be " << shape << endl; cout << " Shapes are " << expr.shape() << ", " << expr2.shape() << endl; ok = False; } return ok; } Bool compareScalarComplex (const LatticeExprNode expr, const LatticeExprNode expr2, const Complex bVal, const IPosition shape) { LELArray Arr(shape); LELArray Arr2(shape); Complex result, result2; IPosition origin(shape); origin = 0; Slicer region(origin, shape); Bool ok = True; if (expr.isScalar() != expr2.isScalar()) { cout << " result should be a scalar" << endl; cout << " results are " << expr.isScalar() << ", " << expr2.isScalar() << endl; ok = False; } if (expr.getComplex() != expr2.getComplex()) { cout << " result should be " << bVal << endl; cout << " Scalar results are " << expr.getComplex() << ", " << expr2.getComplex() << endl; ok = False; } expr.eval(result); expr2.eval(result2); if (result != result2) { cout << " result should be " << bVal << endl; cout << " Scalar results are " << result << ", " << result2 << endl; ok = False; } expr.eval(Arr, region); expr2.eval(Arr2, region); if (! allEQ (Arr.value(), Arr2.value())) { cout << " result should be " << bVal << endl; cout << " Array results are " << Arr.value()(origin) << ", " << Arr2.value()(origin) << endl; ok = False; } if (expr.dataType() != expr2.dataType()) { cout << " Data type should be " << TpComplex << endl; cout << " Data types are " << expr.dataType() << ", " << expr2.dataType() << endl; } if (expr.shape() != expr2.shape()) { cout << " Shape should be " << shape << endl; cout << " Shapes are " << expr.shape() << ", " << expr2.shape() << endl; ok = False; } return ok; } Bool compareScalarDComplex (const LatticeExprNode expr, const LatticeExprNode expr2, const DComplex bVal, const IPosition shape) { LELArray Arr(shape); LELArray Arr2(shape); DComplex result, result2; IPosition origin(shape); origin = 0; Slicer region(origin, shape); Bool ok = True; if (expr.isScalar() != expr2.isScalar()) { cout << " result should be a scalar" << endl; cout << " results are " << expr.isScalar() << ", " << expr2.isScalar() << endl; ok = False; } if (expr.getDComplex() != expr2.getDComplex()) { cout << " result should be " << bVal << endl; cout << " Scalar results are " << expr.getDComplex() << ", " << expr2.getDComplex() << endl; ok = False; } expr.eval(result); expr2.eval(result2); if (result != result2) { cout << " result should be " << bVal << endl; cout << " Scalar results are " << result << ", " << result2 << endl; ok = False; } expr.eval(Arr, region); expr2.eval(Arr2, region); if (! allEQ (Arr.value(), Arr2.value())) { cout << " result should be " << bVal << endl; cout << " Array results are " << Arr.value()(origin) << ", " << Arr2.value()(origin) << endl; ok = False; } if (expr.dataType() != expr2.dataType()) { cout << " Data type should be " << TpDComplex << endl; cout << " Data types are " << expr.dataType() << ", " << expr2.dataType() << endl; } if (expr.shape() != expr2.shape()) { cout << " Shape should be " << shape << endl; cout << " Shapes are " << expr.shape() << ", " << expr2.shape() << endl; ok = False; } return ok; } Bool compareScalarBool (const LatticeExprNode expr, const LatticeExprNode expr2, const Bool bVal, const IPosition shape) { LELArray Arr(shape); LELArray Arr2(shape); Bool result, result2; IPosition origin(shape); origin = 0; Slicer region(origin, shape); Bool ok = True; if (expr.isScalar() != expr2.isScalar()) { cout << " result should be a scalar" << endl; cout << " results are " << expr.isScalar() << ", " << expr2.isScalar() << endl; ok = False; } if (expr.getBool() != expr2.getBool()) { cout << " result should be " << bVal << endl; cout << " Scalar results are " << expr.getBool() << ", " << expr2.getBool() << endl; ok = False; } expr.eval(result); expr2.eval(result2); if (result != result2) { cout << " result should be " << bVal << endl; cout << " Scalar results are " << result << ", " << result2 << endl; ok = False; } expr.eval(Arr, region); expr2.eval(Arr2, region); if (! allEQ (Arr.value(), Arr2.value())) { cout << " result should be " << bVal << endl; cout << " Array results are " << Arr.value()(origin) << ", " << Arr2.value()(origin) << endl; ok = False; } if (expr.dataType() != expr2.dataType()) { cout << " Data type should be " << TpBool << endl; cout << " Data types are " << expr.dataType() << ", " << expr2.dataType() << endl; } if (expr.shape() != expr2.shape()) { cout << " Shape should be " << shape << endl; cout << " Shapes are " << expr.shape() << ", " << expr2.shape() << endl; ok = False; } return ok; } Bool checkInfo (const LatticeExprNode& expr, const IPosition& shape, const Bool shouldBeScalar, const Bool undefinedScalar, const DataType dtype, const Bool emptyShape) { Bool ok = True; if (shouldBeScalar && !expr.isScalar()) { cout << " result should be scalar" << endl; ok = False; } if (!shouldBeScalar && expr.isScalar()) { cout << " result should be array" << endl; ok = False; } if (expr.dataType() != dtype) { cout << " Data type is " << expr.dataType() << endl; cout << " Data type should be " << dtype << endl; ok = False; } if (expr.isScalar()) { if (!expr.shape().isEqual(IPosition())) { cout << " Shape should be " << shape << endl; cout << " Shape is " << expr.shape() << endl; ok = False; } if (undefinedScalar != expr.isInvalidScalar()) { cout << " Incorrect (in)valid scalar result" << endl; } } else { if (emptyShape) { if (!expr.shape().isEqual(IPosition())) { cout << " Shape should be empty" << endl; cout << " Shape is " << expr.shape() << endl; } } else { if (!expr.shape().isEqual(shape)) { cout << " Shape should be " << shape << endl; cout << " Shape is " << expr.shape() << endl; ok = False; } } } return ok; } Bool checkFloat (const LatticeExprNode& expr, const Float result, const IPosition& shape, const Bool shouldBeScalar, const Bool undefinedScalar) { Bool ok = checkInfo (expr, shape, shouldBeScalar, undefinedScalar, TpFloat); Float result2; LELArray Arr(shape); IPosition origin(shape); origin = 0; Slicer region(origin, shape); if (! expr.isInvalidScalar()) { if (expr.isScalar()) { result2 = expr.getFloat(); if (!near(result2, result)) { cout << " result should be " << result << endl; cout << " Scalar result is " << result2 << endl; ok = False; } expr.eval(result2); if (!near(result2, result)) { cout << " result should be " << result << endl; cout << " Scalar result is " << result2 << endl; ok = False; } } expr.eval(Arr, region); if (! allNear (Arr.value(), result, 1.e-06)) { cout << " result should be " << result << endl; cout << " Array result is " << Arr.value() << endl; ok = False; } } return ok; } Bool checkFloatRepl (const LatticeExprNode& expr, const Float result, const IPosition& shape, const Array& replArray, Float replScalar, Bool isReplScalar) { Bool ok = checkInfo (expr, shape, False, False, TpFloat); LELArray Arr(shape); IPosition origin(shape); origin = 0; Slicer region(origin, shape); expr.eval(Arr, region); if (! Arr.isMasked()) { if (! allNear (Arr.value(), result,1.e-06)) { cout << " result should be " << result << endl; cout << " Array result is " << Arr.value() << endl; ok = False; } } else { Bool delres, delmask, delrepl; const Bool* mask = Arr.mask().getStorage (delmask); const Float* res = Arr.value().getStorage (delres); const Float* repl = 0; if (!isReplScalar) { repl = replArray.getStorage (delrepl); } uInt n = Arr.value().nelements(); for (uInt i=0; i Arr(shape); IPosition origin(shape); origin = 0; Slicer region(origin, shape); if (shouldBeScalar && !expr.isScalar()) { cout << " result should be scalar" << endl; ok = False; } if (expr.dataType() != TpDouble) { cout << " Data type is " << expr.dataType() << endl; cout << " Data type should be " << TpDouble << endl; ok = False; } if (expr.isScalar()) { if (!expr.shape().isEqual(IPosition())) { cout << " Shape should be " << shape << endl; cout << " Shape is " << expr.shape() << endl; ok = False; } if (undefinedScalar != expr.isInvalidScalar()) { cout << " Incorrect (in)valid scalar result" << endl; } } else { if (!expr.shape().isEqual(shape)) { cout << " Shape should be " << shape << endl; cout << " Shape is " << expr.shape() << endl; ok = False; } } if (! expr.isInvalidScalar()) { if (expr.isScalar()) { result2 = expr.getDouble(); if (result2 != result) { cout << " result should be " << result << endl; cout << " Scalar result is " << result2 << endl; ok = False; } expr.eval(result2); if (result2 != result) { cout << " result should be " << result << endl; cout << " Scalar result is " << result2 << endl; ok = False; } } expr.eval(Arr, region); if (! allEQ (Arr.value(), result)) { cout << " result should be " << result << endl; cout << " Array result is " << Arr.value() << endl; ok = False; } } return ok; } Bool checkComplex (const LatticeExprNode& expr, const Complex result, const IPosition& shape, const Bool shouldBeScalar, const Bool undefinedScalar) { Bool ok = True; Complex result2; LELArray Arr(shape); IPosition origin(shape); origin = 0; Slicer region(origin, shape); if (shouldBeScalar && !expr.isScalar()) { cout << " result should be scalar" << endl; ok = False; } if (expr.dataType() != TpComplex) { cout << " Data type is " << expr.dataType() << endl; cout << " Data type should be " << TpComplex << endl; ok = False; } if (expr.isScalar()) { if (!expr.shape().isEqual(IPosition())) { cout << " Shape should be " << shape << endl; cout << " Shape is " << expr.shape() << endl; ok = False; } if (undefinedScalar != expr.isInvalidScalar()) { cout << " Incorrect (in)valid scalar result" << endl; } } else { if (!expr.shape().isEqual(shape)) { cout << " Shape should be " << shape << endl; cout << " Shape is " << expr.shape() << endl; ok = False; } } if (! expr.isInvalidScalar()) { if (expr.isScalar()) { result2 = expr.getComplex(); if (!near (result2, result)) { cout << " result should be " << result << endl; cout << " Scalar result is " << result2 << endl; ok = False; } expr.eval(result2); if (!near (result2, result)) { cout << " result should be " << result << endl; cout << " Scalar result is " << result2 << endl; ok = False; } } expr.eval(Arr, region); if (! allNear (Arr.value(), result, 1.0e-5)) { cout << " result should be " << result << endl; cout << " Array result is " << Arr.value() << endl; ok = False; } } return ok; } Bool checkDComplex (const LatticeExprNode& expr, const DComplex result, const IPosition& shape, const Bool shouldBeScalar, const Bool undefinedScalar) { Bool ok = True; DComplex result2; LELArray Arr(shape); IPosition origin(shape); origin = 0; Slicer region(origin, shape); if (shouldBeScalar && !expr.isScalar()) { cout << " result should be scalar" << endl; ok = False; } if (expr.dataType() != TpDComplex) { cout << " Data type is " << expr.dataType() << endl; cout << " Data type should be " << TpDComplex << endl; ok = False; } if (expr.isScalar()) { if (!expr.shape().isEqual(IPosition())) { cout << " Shape should be " << shape << endl; cout << " Shape is " << expr.shape() << endl; ok = False; } if (undefinedScalar != expr.isInvalidScalar()) { cout << " Incorrect (in)valid scalar result" << endl; } } else { if (!expr.shape().isEqual(shape)) { cout << " Shape should be " << shape << endl; cout << " Shape is " << expr.shape() << endl; ok = False; } } if (! expr.isInvalidScalar()) { if (expr.isScalar()) { result2 = expr.getDComplex(); if (!near (result2, result)) { cout << " result should be " << result << endl; cout << " Scalar result is " << result2 << endl; ok = False; } expr.eval(result2); if (!near (result2, result)) { cout << " result should be " << result << endl; cout << " Scalar result is " << result2 << endl; ok = False; } } expr.eval(Arr, region); if (! allNear (Arr.value(), result, 1.0e-13)) { cout << " result should be " << result << endl; cout << " Array result is " << Arr.value() << endl; ok = False; } } return ok; } Bool checkBool (const LatticeExprNode& expr, const Bool result, const IPosition& shape, const Bool shouldBeScalar, const Bool undefinedScalar, const Bool emptyShape) { Bool ok = checkInfo (expr, shape, shouldBeScalar, undefinedScalar, TpBool, emptyShape); Bool result2; LELArray Arr(shape); IPosition origin(shape); origin = 0; Slicer region(origin, shape); if (! expr.isInvalidScalar()) { if (expr.isScalar()) { result2 = expr.getBool(); if (result2 != result) { cout << " result should be " << result << endl; cout << " Scalar result is " << result2 << endl; ok = False; } expr.eval(result2); if (result2 != result) { cout << " result should be " << result << endl; cout << " Scalar result is " << result2 << endl; ok = False; } } expr.eval(Arr, region); if (! allEQ (Arr.value(), result)) { cout << " result should be " << result << endl; cout << " Array result is " << Arr.value() << endl; ok = False; } } return ok; } Bool checkBoolRepl (const LatticeExprNode& expr, const Bool result, const IPosition& shape, const Array& replArray, Bool replScalar, Bool isReplScalar) { Bool ok = checkInfo (expr, shape, False, False, TpBool); LELArray Arr(shape); IPosition origin(shape); origin = 0; Slicer region(origin, shape); expr.eval(Arr, region); if (! Arr.isMasked()) { if (! allEQ (Arr.value(), result)) { cout << " result should be " << result << endl; cout << " Array result is " << Arr.value() << endl; ok = False; } } else { Bool delres, delmask, delrepl; const Bool* mask = Arr.mask().getStorage (delmask); const Bool* res = Arr.value().getStorage (delres); const Bool* repl = 0; if (!isReplScalar) { repl = replArray.getStorage (delrepl); } uInt n = Arr.value().nelements(); for (uInt i=0; i& result) { if (hasMask != expr.isMasked()) { cout << " expr should have " << hasMask << " a mask" << endl; return False; } IPosition origin(result.shape()); origin = 0; Slicer region(origin, result.shape()); Array mask; Bool isMasked; switch (expr.dataType()) { case TpBool: { LELArray arr(result.shape()); expr.eval (arr, region); isMasked = arr.isMasked(); if (isMasked) mask = arr.mask(); break; } case TpFloat: { LELArray arr(result.shape()); expr.eval (arr, region); isMasked = arr.isMasked(); if (isMasked) mask = arr.mask(); break; } case TpDouble: { LELArray arr(result.shape()); expr.eval (arr, region); isMasked = arr.isMasked(); if (isMasked) mask = arr.mask(); break; } case TpComplex: { LELArray arr(result.shape()); expr.eval (arr, region); isMasked = arr.isMasked(); if (isMasked) mask = arr.mask(); break; } case TpDComplex: { LELArray arr(result.shape()); expr.eval (arr, region); isMasked = arr.isMasked(); if (isMasked) mask = arr.mask(); break; } default: cout << "Unknown data type in checkMask" << endl; return False; } if (hasMask != isMasked) { cout << " result should have " << hasMask << " a mask" << endl; return False; } if (hasMask) { if (anyNE (result, mask)) { cout << " result should have mask " << result << endl; cout << " but has mask " << mask << endl; return False; } } return True; } int main (int argc, const char* argv[]) { Bool ok = True; try { cout << ">>>" << endl; Input inp(1); inp.version(" "); inp.create("nx", "2", "Number of pixels along the x-axis", "int"); inp.create("ny", "2", "Number of pixels along the y-axis", "int"); inp.readArguments(argc, argv); cout << "<<<" << endl; const uInt nx=inp.getInt("nx"); const uInt ny=inp.getInt("ny"); // // The use of these tiny ArrayLattices means this test program // does not computationally stress the classes. Here we just // test them logically. See other test programs for stress tests // IPosition shape(2,nx,ny); // Bool Lattices ArrayLattice aB(shape); ArrayLattice bB(shape); ArrayLattice cB(shape); Bool aBVal = True; aB.set(aBVal); Bool bBVal = False; bB.set(bBVal); Bool cBVal = True; cB.set(cBVal); // FLoat Lattices ArrayLattice aF(shape); ArrayLattice bF(shape); ArrayLattice cF(shape); Float aFVal = 0.0; aF.set(aFVal); Float bFVal = 1.0; bF.set(1.0); Float cFVal = 2.0; cF.set(cFVal); // Double Lattices ArrayLattice aD(shape); ArrayLattice bD(shape); ArrayLattice cD(shape); Double aDVal = 0.0; aD.set(aDVal); Double bDVal = 1.0; bD.set(1.0); Double cDVal = 2.0; cD.set(cDVal); // Complex Lattices ArrayLattice aC(shape); ArrayLattice bC(shape); ArrayLattice cC(shape); ArrayLattice dC(shape); Complex aCVal = Complex(0.0,10.0); aC.set(aCVal); Complex bCVal = Complex(1.0,21.0); bC.set(bCVal); Complex cCVal = Complex(2.0,32.0); cC.set(cCVal); Complex dCVal = Complex(3.0,43.0); dC.set(dCVal); // DComplex Lattices ArrayLattice aDC(shape); ArrayLattice bDC(shape); ArrayLattice cDC(shape); ArrayLattice dDC(shape); DComplex aDCVal = DComplex(0.0,10.0); aDC.set(aDCVal); DComplex bDCVal = DComplex(1.0,21.0); bDC.set(bDCVal); DComplex cDCVal = DComplex(2.0,32.0); cDC.set(cDCVal); DComplex dDCVal = DComplex(3.0,43.0); dDC.set(dDCVal); // Define some array masks. Matrix mat1(shape); Matrix mat2(shape); Matrix mat3(shape); mat1 = True; mat2 = True; mat3 = True; mat1(0,0) = False; mat2(1,0) = False; mat3(0,1) = False; LCBox box(shape); LCPixelSet mask1 (mat1, box); LCPixelSet mask2 (mat2, box); LCPixelSet mask3 (mat3, box); if (!doIt (SubLattice(aF), SubLattice(bF), SubLattice(cF), SubLattice(bD), SubLattice(cD), SubLattice(bC), SubLattice(cC), SubLattice(bDC), SubLattice(cDC), SubLattice(aB), SubLattice(bB), SubLattice(cB), bFVal,cFVal,bDVal,cDVal,bCVal,cCVal, bDCVal,cDCVal,aBVal,bBVal,cBVal, 4)) { ok = False; } if (!doIt (SubLattice(aF,mask1), SubLattice(bF,mask2), SubLattice(cF,mask3), SubLattice(bD,mask2), SubLattice(cD,mask3), SubLattice(bC,mask2), SubLattice(cC,mask3), SubLattice(bDC,mask2), SubLattice(cDC,mask3), SubLattice(aB,mask1), SubLattice(bB,mask2), SubLattice(cB,mask3), bFVal,cFVal,bDVal,cDVal,bCVal,cCVal, bDCVal,cDCVal,aBVal,bBVal,cBVal, 3)) { ok = False; } mat2 = False; mask2 = LCPixelSet (mat2, box); if (!doIt (SubLattice(aF,mask1), SubLattice(bF,mask2), SubLattice(cF,mask3), SubLattice(bD,mask2), SubLattice(cD,mask3), SubLattice(bC,mask2), SubLattice(cC,mask3), SubLattice(bDC,mask2), SubLattice(cDC,mask3), SubLattice(aB,mask1), SubLattice(bB,mask2), SubLattice(cB,mask3), bFVal,cFVal,bDVal,cDVal,bCVal,cCVal, bDCVal,cDCVal,aBVal,bBVal,cBVal, 0)) { ok = False; } } catch (std::exception& x) { cerr << "aipserror: error " << x.what() << endl; ok = False; } if (!ok) { return 1; } cout << endl << "ok" << endl; return 0; } casacore-3.7.1/lattices/LEL/test/tLatticeExprNode.out000066400000000000000000000410531476623553700225010ustar00rootroot00000000000000>>> tLatticeExprNode: Version <<< Constant contructors, get*, eval, shape, dataType, isScalar LatticeExprNode (constant T) Int Float Double Complex DComplex Bool LELInterface constructors LatticeExprNode(std::shared_ptr>&) Float Double Complex DComplex Bool Copy constructor Assignment Unary operator + Float Scalar Double Scalar Complex Scalar DComplex Scalar Float Array Double Array Complex Array DComplex Array Unary operator - Float Scalar Complex Scalar Float Array Complex Array Unary operator ! Bool Scalar Bool Array Binary operator + Float Scalar Double Scalar Complex Scalar DComplex Scalar Float Array Double Array Complex Array DComplex Array Binary operator - Float Scalar Complex Scalar Float Array Complex Array Binary operator * Float Scalar Complex Scalar Float Array Complex Array Binary operator / Float Scalar Complex Scalar Float Array Complex Scalar Binary operator == Bool Scalar Float Scalar Complex Scalar Bool Array Float Array Complex Array Binary operator != Bool Scalar Float Scalar Complex Scalar Bool Array Float Array Complex Array Binary operator > Float Scalar Complex Scalar Float Array Complex Array Binary operator >= Float Scalar Complex Scalar Float Array Complex Array Binary operator < Float Scalar Complex Scalar Float Array Complex Array Binary operator <= Float Scalar Complex Scalar Float Array Complex Array Binary operator && Bool Scalar Bool Array Binary operator || Bool Scalar Bool Array operator [] Bool Array Float Array Double Array Complex Array DComplex Array 1-argument functions sin Float Scalar Double Scalar Complex Scalar DComplex Scalar Float Array Double Array Complex Array DComplex Array sinh Float Scalar Complex Scalar Float Array Complex Array asin Float Scalar Float Array cos Float Scalar Complex Scalar Float Array Complex Array cosh Float Scalar Complex Scalar Float Array Complex Array acos Float Scalar Float Array tan Float Scalar Float Array tanh Float Scalar Float Array atan Float Scalar Float Array exp Float Scalar Complex Scalar Float Array Complex Array log Float Scalar Complex Scalar Float Array Complex Array log10 Float Scalar Complex Scalar Float Array Complex Array sqrt Float Scalar Complex Scalar Float Array Complex Array ceil Float Scalar Float Array floor Float Scalar Float Array conj Complex Scalar Complex Array complex Float Scalar Float Array Float Array,Scalar Float Scalar,Array abs Float Scalar Complex Scalar Float Array Complex Array arg Complex Scalar Complex Array real Complex Scalar Complex Array imag Complex Scalar Complex Array min Float Scalar Complex Scalar Float Array Complex Array max Float Scalar Complex Scalar Float Array Complex Array sign Float Scalar Double Scalar Float Array Double Array round Float Scalar Float Array median Float Scalar Float Array fractile Float Scalar Double Scalar Float Array Double Array fractileRange 2 Float Array Double Array fractileRange 3 Float Array Double Array mean Float Scalar Complex Scalar Float Array Complex Array variance Float Array stddev Float Array avdev Float Array sum Float Scalar Complex Scalar Float Array Complex Array nelements Float Scalar Complex Scalar Float Array Complex Array ndim Float Scalar Complex Scalar Float Array Complex Array length Float Scalar Complex Scalar Float Array Complex Array Bool Array any Bool Array all Bool Array ntrue Bool Array nfalse Bool Array isNaN Float Scalar Complex Scalar Float Array Complex Array indexin 2-argument functions atan2 Float Scalar Float Array pow Float Scalar Complex Scalar Float Array Complex Array fmod Float Scalar Float Array min Float Scalar Float Array max Float Scalar Float Array amp Float Scalar Complex Scalar Float Array Complex Array pa Float Scalar Float Array Double Scalar Double Array mask Float Scalar 1 Float Scalar 2 Float Array Bool Scalar Bool Array value Float Scalar 1 Float Scalar 2 Float Array Bool Scalar Bool Array replace Float Scalar Float Array Bool Scalar Bool Array Rebin Float Double Complex DComplex 3-argument functions iif Float Scalar,Scalar,Scalar Double Scalar,Array,Scalar Complex Scalar,Scalar,Array DComplex Scalar,Array,Array Bool Scalar,Array,Array Float/Double Array,Scalar,Scalar Complex/Double Array,Scalar,Array Double Array,Array,Scalar Double Array,Array,Array Bool Array,Array,Scalar Bool Array,Array,Array Conversion functions toFloat from Float Scalar from Double Scalar from Float Array from Double Array toDouble from Float Scalar from Double Scalar from Float Array from Double Scalar toComplex from Float Scalar from Double Scalar from Complex Scalar from DComplex Scalar from Float Array from Double Scalar from Complex Array from DComplex Array toDComplex from Float Scalar from Double Scalar from Complex Scalar from DComplex Scalar from Float Array from Double Scalar from DComplex Array from DComplex Array Casting operators LatticeExpr() from Float from Double LatticeExpr() from Float from Double LatticeExpr() from Float from Double from Complex from DComplex LatticeExpr() from Float from Double from Complex from DComplex LatticeExpr() from Bool Constant contructors, get*, eval, shape, dataType, isScalar LatticeExprNode (constant T) Int Float Double Complex DComplex Bool LELInterface constructors LatticeExprNode(std::shared_ptr>&) Float Double Complex DComplex Bool Copy constructor Assignment Unary operator + Float Scalar Double Scalar Complex Scalar DComplex Scalar Float Array Double Array Complex Array DComplex Array Unary operator - Float Scalar Complex Scalar Float Array Complex Array Unary operator ! Bool Scalar Bool Array Binary operator + Float Scalar Double Scalar Complex Scalar DComplex Scalar Float Array Double Array Complex Array DComplex Array Binary operator - Float Scalar Complex Scalar Float Array Complex Array Binary operator * Float Scalar Complex Scalar Float Array Complex Array Binary operator / Float Scalar Complex Scalar Float Array Complex Scalar Binary operator == Bool Scalar Float Scalar Complex Scalar Bool Array Float Array Complex Array Binary operator != Bool Scalar Float Scalar Complex Scalar Bool Array Float Array Complex Array Binary operator > Float Scalar Complex Scalar Float Array Complex Array Binary operator >= Float Scalar Complex Scalar Float Array Complex Array Binary operator < Float Scalar Complex Scalar Float Array Complex Array Binary operator <= Float Scalar Complex Scalar Float Array Complex Array Binary operator && Bool Scalar Bool Array Binary operator || Bool Scalar Bool Array operator [] Bool Array Float Array Double Array Complex Array DComplex Array 1-argument functions sin Float Scalar Double Scalar Complex Scalar DComplex Scalar Float Array Double Array Complex Array DComplex Array sinh Float Scalar Complex Scalar Float Array Complex Array asin Float Scalar Float Array cos Float Scalar Complex Scalar Float Array Complex Array cosh Float Scalar Complex Scalar Float Array Complex Array acos Float Scalar Float Array tan Float Scalar Float Array tanh Float Scalar Float Array atan Float Scalar Float Array exp Float Scalar Complex Scalar Float Array Complex Array log Float Scalar Complex Scalar Float Array Complex Array log10 Float Scalar Complex Scalar Float Array Complex Array sqrt Float Scalar Complex Scalar Float Array Complex Array ceil Float Scalar Float Array floor Float Scalar Float Array conj Complex Scalar Complex Array complex Float Scalar Float Array Float Array,Scalar Float Scalar,Array abs Float Scalar Complex Scalar Float Array Complex Array arg Complex Scalar Complex Array real Complex Scalar Complex Array imag Complex Scalar Complex Array min Float Scalar Complex Scalar Float Array Complex Array max Float Scalar Complex Scalar Float Array Complex Array sign Float Scalar Double Scalar Float Array Double Array round Float Scalar Float Array median Float Scalar Float Array fractile Float Scalar Double Scalar Float Array Double Array fractileRange 2 Float Array Double Array fractileRange 3 Float Array Double Array mean Float Scalar Complex Scalar Float Array Complex Array variance Float Array stddev Float Array avdev Float Array sum Float Scalar Complex Scalar Float Array Complex Array nelements Float Scalar Complex Scalar Float Array Complex Array ndim Float Scalar Complex Scalar Float Array Complex Array length Float Scalar Complex Scalar Float Array Complex Array Bool Array any Bool Array all Bool Array ntrue Bool Array nfalse Bool Array isNaN Float Scalar Complex Scalar Float Array Complex Array indexin 2-argument functions atan2 Float Scalar Float Array pow Float Scalar Complex Scalar Float Array Complex Array fmod Float Scalar Float Array min Float Scalar Float Array max Float Scalar Float Array amp Float Scalar Complex Scalar Float Array Complex Array pa Float Scalar Float Array Double Scalar Double Array mask Float Scalar 1 Float Scalar 2 Float Array Bool Scalar Bool Array value Float Scalar 1 Float Scalar 2 Float Array Bool Scalar Bool Array replace Float Scalar Float Array Bool Scalar Bool Array Rebin Float Double Complex DComplex 3-argument functions iif Float Scalar,Scalar,Scalar Double Scalar,Array,Scalar Complex Scalar,Scalar,Array DComplex Scalar,Array,Array Bool Scalar,Array,Array Float/Double Array,Scalar,Scalar Complex/Double Array,Scalar,Array Double Array,Array,Scalar Double Array,Array,Array Bool Array,Array,Scalar Bool Array,Array,Array Conversion functions toFloat from Float Scalar from Double Scalar from Float Array from Double Array toDouble from Float Scalar from Double Scalar from Float Array from Double Scalar toComplex from Float Scalar from Double Scalar from Complex Scalar from DComplex Scalar from Float Array from Double Scalar from Complex Array from DComplex Array toDComplex from Float Scalar from Double Scalar from Complex Scalar from DComplex Scalar from Float Array from Double Scalar from DComplex Array from DComplex Array Casting operators LatticeExpr() from Float from Double LatticeExpr() from Float from Double LatticeExpr() from Float from Double from Complex from DComplex LatticeExpr() from Float from Double from Complex from DComplex LatticeExpr() from Bool Constant contructors, get*, eval, shape, dataType, isScalar LatticeExprNode (constant T) Int Float Double Complex DComplex Bool LELInterface constructors LatticeExprNode(std::shared_ptr>&) Float Double Complex DComplex Bool Copy constructor Assignment Unary operator + Float Scalar Double Scalar Complex Scalar DComplex Scalar Float Array Double Array Complex Array DComplex Array Unary operator - Float Scalar Complex Scalar Float Array Complex Array Unary operator ! Bool Scalar Bool Array Binary operator + Float Scalar Double Scalar Complex Scalar DComplex Scalar Float Array Double Array Complex Array DComplex Array Binary operator - Float Scalar Complex Scalar Float Array Complex Array Binary operator * Float Scalar Complex Scalar Float Array Complex Array Binary operator / Float Scalar Complex Scalar Float Array Complex Scalar Binary operator == Bool Scalar Float Scalar Complex Scalar Bool Array Float Array Complex Array Binary operator != Bool Scalar Float Scalar Complex Scalar Bool Array Float Array Complex Array Binary operator > Float Scalar Complex Scalar Float Array Complex Array Binary operator >= Float Scalar Complex Scalar Float Array Complex Array Binary operator < Float Scalar Complex Scalar Float Array Complex Array Binary operator <= Float Scalar Complex Scalar Float Array Complex Array Binary operator && Bool Scalar Bool Array Binary operator || Bool Scalar Bool Array operator [] Bool Array Float Array Double Array Complex Array DComplex Array 1-argument functions sin Float Scalar Double Scalar Complex Scalar DComplex Scalar Float Array Double Array Complex Array DComplex Array sinh Float Scalar Complex Scalar Float Array Complex Array asin Float Scalar Float Array cos Float Scalar Complex Scalar Float Array Complex Array cosh Float Scalar Complex Scalar Float Array Complex Array acos Float Scalar Float Array tan Float Scalar Float Array tanh Float Scalar Float Array atan Float Scalar Float Array exp Float Scalar Complex Scalar Float Array Complex Array log Float Scalar Complex Scalar Float Array Complex Array log10 Float Scalar Complex Scalar Float Array Complex Array sqrt Float Scalar Complex Scalar Float Array Complex Array ceil Float Scalar Float Array floor Float Scalar Float Array conj Complex Scalar Complex Array complex Float Scalar Float Array Float Array,Scalar Float Scalar,Array abs Float Scalar Complex Scalar Float Array Complex Array arg Complex Scalar Complex Array real Complex Scalar Complex Array imag Complex Scalar Complex Array min Float Scalar Complex Scalar Float Array Complex Array max Float Scalar Complex Scalar Float Array Complex Array sign Float Scalar Double Scalar Float Array Double Array round Float Scalar Float Array median Float Scalar Float Array fractile Float Scalar Double Scalar Float Array Double Array fractileRange 2 Float Array Double Array fractileRange 3 Float Array Double Array mean Float Scalar Complex Scalar Float Array Complex Array variance Float Array stddev Float Array avdev Float Array sum Float Scalar Complex Scalar Float Array Complex Array nelements Float Scalar Complex Scalar Float Array Complex Array ndim Float Scalar Complex Scalar Float Array Complex Array length Float Scalar Complex Scalar Float Array Complex Array Bool Array any Bool Array all Bool Array ntrue Bool Array nfalse Bool Array isNaN Float Scalar Complex Scalar Float Array Complex Array indexin 2-argument functions atan2 Float Scalar Float Array pow Float Scalar Complex Scalar Float Array Complex Array fmod Float Scalar Float Array min Float Scalar Float Array max Float Scalar Float Array amp Float Scalar Complex Scalar Float Array Complex Array pa Float Scalar Float Array Double Scalar Double Array mask Float Scalar 1 Float Scalar 2 Float Array Bool Scalar Bool Array value Float Scalar 1 Float Scalar 2 Float Array Bool Scalar Bool Array replace Float Scalar Float Array Bool Scalar Bool Array Rebin Float Double Complex DComplex 3-argument functions iif Float Scalar,Scalar,Scalar Double Scalar,Array,Scalar Complex Scalar,Scalar,Array DComplex Scalar,Array,Array Bool Scalar,Array,Array Float/Double Array,Scalar,Scalar Complex/Double Array,Scalar,Array Double Array,Array,Scalar Double Array,Array,Array Bool Array,Array,Scalar Bool Array,Array,Array Conversion functions toFloat from Float Scalar from Double Scalar from Float Array from Double Array toDouble from Float Scalar from Double Scalar from Float Array from Double Scalar toComplex from Float Scalar from Double Scalar from Complex Scalar from DComplex Scalar from Float Array from Double Scalar from Complex Array from DComplex Array toDComplex from Float Scalar from Double Scalar from Complex Scalar from DComplex Scalar from Float Array from Double Scalar from DComplex Array from DComplex Array Casting operators LatticeExpr() from Float from Double LatticeExpr() from Float from Double LatticeExpr() from Float from Double from Complex from DComplex LatticeExpr() from Float from Double from Complex from DComplex LatticeExpr() from Bool ok casacore-3.7.1/lattices/LRegions.h000066400000000000000000000072401476623553700170320ustar00rootroot00000000000000//# LRegions.h: Regions in a lattice. //# Copyright (C) 1996,1997,1998,1999,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LREGIONS_H #define LATTICES_LREGIONS_H //#include //#include //#include //#include //#include //#include //#include //#include //#include //#include //#include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // // Regions in a lattice. // // //
      • module Lattices // // // // // There is a rich variety of region // classes which can be used to define a LatticeRegion in pixel coordinates. // The elementary ones are: //
          //
        • box //
        • ellipsoid //
        • polygon //
        • pixelset //
        • good/bad mask //
        // Compound region classes can be used to make a combination of one or more // regions. //
          //
        • union //
        • intersection //
        • difference //
        • concatenation //
        • complement //
        • extension //
        // Apart from these region classes, class // LCSlicer can be used to define // a box with optional strides. It also offers the opportunity to // define the box in fractions or to define it relative to the // center of the lattice or relative to a reference pixel. //
        The final, and most general way, to define regions is by // means of the world coordinates region classes in the // Images module, in particular // the WCRegion class. // However, world coordinate regions can only be used with images. //
        // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LRegions/000077500000000000000000000000001476623553700166565ustar00rootroot00000000000000casacore-3.7.1/lattices/LRegions/FITSMask.cc000066400000000000000000000150131476623553700205460ustar00rootroot00000000000000//# FITSMask.cc: an on-the-fly mask for FITS images //# Copyright (C) 1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN FITSMask::FITSMask (TiledFileAccess* tiledFile) : itsTiledFilePtr(tiledFile), itsScale(1.0), itsOffset(0.0), itsUCharMagic(0), itsShortMagic(0), itsLongMagic(0), itsHasIntBlanks(False), itsFilterZero(False) { AlwaysAssert(itsTiledFilePtr->dataType()==TpFloat || itsTiledFilePtr->dataType()==TpDouble, AipsError); } FITSMask::FITSMask (TiledFileAccess* tiledFile, Float scale, Float offset, uChar magic, Bool hasBlanks) : itsTiledFilePtr(tiledFile), itsScale(scale), itsOffset(offset), itsUCharMagic(magic), itsShortMagic(0), itsLongMagic(0), itsHasIntBlanks(hasBlanks), itsFilterZero(False) { AlwaysAssert(itsTiledFilePtr->dataType()==TpUChar, AipsError); } FITSMask::FITSMask (TiledFileAccess* tiledFile, Float scale, Float offset, Short magic, Bool hasBlanks) : itsTiledFilePtr(tiledFile), itsScale(scale), itsOffset(offset), itsUCharMagic(0), itsShortMagic(magic), itsLongMagic(0), itsHasIntBlanks(hasBlanks), itsFilterZero(False) { AlwaysAssert(itsTiledFilePtr->dataType()==TpShort, AipsError); } FITSMask::FITSMask (TiledFileAccess* tiledFile, Float scale, Float offset, Int magic, Bool hasBlanks) : itsTiledFilePtr(tiledFile), itsScale(scale), itsOffset(offset), itsUCharMagic(0), itsShortMagic(0), itsLongMagic(magic), itsHasIntBlanks(hasBlanks), itsFilterZero(False) { AlwaysAssert(itsTiledFilePtr->dataType()==TpInt, AipsError); } FITSMask::FITSMask (const FITSMask& other) : Lattice(other), itsTiledFilePtr(other.itsTiledFilePtr), itsScale(other.itsScale), itsOffset(other.itsOffset), itsUCharMagic(other.itsUCharMagic), itsShortMagic(other.itsShortMagic), itsLongMagic(other.itsLongMagic), itsHasIntBlanks(other.itsHasIntBlanks), itsFilterZero(other.itsFilterZero) {} FITSMask::~FITSMask() {} FITSMask& FITSMask::operator= (const FITSMask& other) { if (this != &other) { itsTiledFilePtr = other.itsTiledFilePtr; itsBuffer.resize(); itsBuffer = other.itsBuffer.copy(); itsScale = other.itsScale; itsOffset = other.itsOffset; itsUCharMagic = other.itsUCharMagic; itsShortMagic = other.itsShortMagic; itsLongMagic = other.itsLongMagic; itsHasIntBlanks = other.itsHasIntBlanks; itsFilterZero = other.itsFilterZero; } return *this; } Lattice* FITSMask::clone() const { return new FITSMask (*this); } Bool FITSMask::isWritable() const { return False; } IPosition FITSMask::shape() const { return itsTiledFilePtr->shape(); } Bool FITSMask::doGetSlice (Array& mask, const Slicer& section) { IPosition shp = section.length(); if (!mask.shape().isEqual(shp)) mask.resize(shp); if (!itsBuffer.shape().isEqual(shp)) itsBuffer.resize(shp); // if (itsTiledFilePtr->dataType()==TpFloat) { itsTiledFilePtr->get(itsBuffer, section); } else if (itsTiledFilePtr->dataType()==TpDouble) { Array tmp(shp); itsTiledFilePtr->get(tmp, section); convertArray(itsBuffer, tmp); } else if (itsTiledFilePtr->dataType()==TpInt) { itsTiledFilePtr->get(itsBuffer, section, itsScale, itsOffset, itsLongMagic, itsHasIntBlanks); } else if (itsTiledFilePtr->dataType()==TpShort) { itsTiledFilePtr->get(itsBuffer, section, itsScale, itsOffset, itsShortMagic, itsHasIntBlanks); } else if (itsTiledFilePtr->dataType()==TpUChar) { itsTiledFilePtr->get(itsBuffer, section, itsScale, itsOffset, itsUCharMagic, itsHasIntBlanks); } // Bool deletePtrD; const Float* pData = itsBuffer.getStorage(deletePtrD); Bool deletePtrM; Bool* pMask = mask.getStorage(deletePtrM); // // Apply the according filtering if (!itsFilterZero) { filterNaN(pMask, pData, mask.nelements()); } else { filterZeroNaN(pMask, pData, mask.nelements()); } // itsBuffer.freeStorage(pData, deletePtrD); mask.putStorage(pMask, deletePtrM); // return False; // Not a reference } void FITSMask::filterNaN (Bool *pMask, const Float *pData, uInt nelems) { // loop over all elements for (uInt i=0; i&, const IPosition&, const IPosition&) { throw(AipsError("FITSMask object is not writable")); } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LRegions/FITSMask.h000066400000000000000000000132661476623553700204200ustar00rootroot00000000000000 //# FITSMask.h: A Lattice that can be used for temporary storage //# Copyright (C) 1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_FITSMASK_H #define LATTICES_FITSMASK_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TiledFileAccess; // // Provides an on-the-fly mask for FITS images // // // // // //
      • Lattice //
      • FITSImage // // // This class provides a pixel mask for the FITSImage class. // // // Masked values are indicated in FITS images via magic // value blanking. This class provides an on-the-fly mask. // The doGetSlice function reads the data values and returns // an Array which is True (good) or False (bad - blanked) // // Because FITSMask inherits from Lattice it can be // used as the private pixel mask data member for FITSImage // returned by the MaskedLattice::pixelMask() functions // // The FITSMask object is constructed from a TiledFileAccess // object. This must be the same one that the FITSImage // object constructs internally. It is shared by both // FITSImage and FITSMask. // // // // // // // // // FITSImage provides native access to FITS image files // and needede an efficient way to handle the pixel mask // other than iterating all the way through the image // first to set a mask. // //# //#
      • add this feature //#
      • fix this bug //#
      • start discussion of this possible extension //# class FITSMask : public Lattice { public: // Constructor (for 32 bit floating point). The pointer is not cloned, // just copied. FITSMask (TiledFileAccess* tiledFileAccess); // Constructor (for 8 bit integers). The pointer is not cloned, just copied // The scale, offset, magic blanking values must come from // the FITS header ('bscale', 'bzero', 'blank') FITSMask (TiledFileAccess* tiledFileAccess, Float scale, Float offset, uChar magic, Bool hasBlanks); // Constructor (for 16 bit integers). The pointer is not cloned, just copied // The scale, offset, magic blanking values must come from // the FITS header ('bscale', 'bzero', 'blank') FITSMask (TiledFileAccess* tiledFileAccess, Float scale, Float offset, Short magic, Bool hasBlanks); // Constructor (for 32 bit integers). The pointer is not cloned, just copied // The scale, offset, magic blanking values must come from // the FITS header ('bscale', 'bzero', 'blank') FITSMask (TiledFileAccess* tiledFileAccess, Float scale, Float offset, Int magic, Bool hasBlanks); // Copy constructor (reference semantics). The TiledFileAccess pointer // is just copied. FITSMask (const FITSMask& other) ; // Destructor virtual ~FITSMask(); // The assignment operator with reference semantics. // The TiledFileAccess pointer is just copied. FITSMask& operator= (const FITSMask& other); // Make a copy of the object (reference semantics). virtual Lattice* clone() const; // Is the FITSMask writable? Returns False. Although it is not hard // to implement writing of the mask, data values would be lost // because of magic blanking. virtual Bool isWritable() const; // Return the shape of the Lattice including all degenerate // axes (ie. axes with a length of one) IPosition shape() const; // Do the actual getting of an array of values. virtual Bool doGetSlice (Array& buffer, const Slicer& section); // Do the actual getting of an array of values. Throws an exception. virtual void doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride); // Set the switch for also filtering 0.0 (besides NaNs). virtual void setFilterZero (Bool filterZero); private: // Mask out ONLY NaN's void filterNaN (Bool* pMask, const float* pData, uInt nelems); // Mask out NaN's and values 0.0 void filterZeroNaN (Bool* pMask, const Float* pData, uInt nelems); // TiledFileAccess* itsTiledFilePtr; Array itsBuffer; Float itsScale, itsOffset; Short itsUCharMagic; Short itsShortMagic; Int itsLongMagic; Bool itsHasIntBlanks; Bool itsFilterZero; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LRegions/LCBox.cc000066400000000000000000000211401476623553700201320ustar00rootroot00000000000000//# LCBox.cc: Class to define a rectangular box of interest //# Copyright (C) 1997,1998,1999,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LCBox::LCBox() {} LCBox::LCBox (const IPosition& latticeShape) : LCRegionFixed (latticeShape) { // Set the box to the full lattice. setBoundingBox (Slicer (IPosition(latticeShape.nelements(), 0), latticeShape)); // Fill the blc and trc vectors. fillBlcTrc(); } LCBox::LCBox (const Slicer& box, const IPosition& latticeShape) : LCRegionFixed (latticeShape) { // Make sure no stride is given. if (box.stride() != 1) { throw (AipsError ("LCBox::LCBox - " "stride in given Slicer has to be 1")); } // When the slicer is fixed (i.e. blc and trc explicitly given), // it is possible that it partly exceeds the lattice boundaries. if (box.isFixed()) { setSlicerBox (box.start(), box.end()); } else { setBoundingBox (box); } // Fill the blc and trc vectors. fillBlcTrc(); } // Construct from the IPosition's defining the bottom-left and // top-right corner of the box. LCBox::LCBox (const IPosition& blc, const IPosition& trc, const IPosition& latticeShape) : LCRegionFixed (latticeShape) { setSlicerBox (blc, trc); fillBlcTrc(); } LCBox::LCBox (const Vector& blc, const Vector& trc, const IPosition& latticeShape) : LCRegionFixed (latticeShape), itsBlc (blc.copy()), itsTrc (trc.copy()) { uInt i; IPosition bl(blc.nelements()); for (i=0; i& blc, const Vector& trc, const IPosition& latticeShape) : LCRegionFixed (latticeShape), itsBlc (blc.nelements()), itsTrc (trc.nelements()) { uInt i; IPosition bl(blc.nelements()); for (i=0; i& translateVector, const IPosition& newLatticeShape) const { uInt ndim = latticeShape().nelements(); Vector blc (itsBlc.copy()); Vector trc (itsTrc.copy()); for (uInt i=0; i blc (rec.toArrayFloat ("blc")); Array trc (rec.toArrayFloat ("trc")); return new LCBox (blc-off, trc-off, Vector(rec.toArrayInt ("shape"))); } void LCBox::setSlicerBox (const IPosition& blc, const IPosition& trc) { const IPosition& shape = latticeShape(); uInt ndim = shape.nelements(); if (blc.nelements() != ndim || trc.nelements() != ndim) { throw (AipsError ("LCBox::LCBox - " "length of blc and trc vectors have to match " "dimensionality of lattice")); } IPosition bl(blc); IPosition tr(trc); for (uInt i=0; i= shape(i)) { tr(i) = shape(i) - 1; } if (bl(i) > tr(i)) { ostringstream bstr, tstr; bstr << bl; tstr << tr; throw (AipsError ("LCBox::LCBox - " "blc " + String(bstr) + " must be <= trc " + String(tstr))); } } setBoundingBox (Slicer(bl, tr, Slicer::endIsLast)); } void LCBox::fillBlcTrc() { const Slicer& sl = boundingBox(); uInt nd = sl.ndim(); itsBlc.resize (nd); itsTrc.resize (nd); for (uInt i=0; i blcDim-1) { blc(i) = 0; } else { if (blc(i) < 0 || blc(i) > shape(i)-1) blc(i) = 0; } } } // Check trc const Int trcDim = trc.nelements(); trc.resize(nDim,True); if (trcDim == 0) { trc = shape- 1; } else { for (Int i=0; i trcDim-1) { trc(i) = shape(i) - 1; } else { if (trc(i) < 0 || trc(i) > shape(i)-1) { trc(i) = shape(i) - 1; } } } } // Check increment const Int incDim = inc.nelements(); inc.resize(nDim,True); if (incDim == 0) { inc = 1; } else { for (Int i=0; i incDim-1) { inc(i) = 1; } else { if (inc(i) < 1 || inc(i) > trc(i)-blc(i)+1) inc(i) = 1; } } } // Check blc trc(i)) { blc(i) = 0; trc(i) = shape(i) - 1; } } // Bool changed = (blc.nelements()!=inBlc.nelements() || trc.nelements()!=inTrc.nelements() || inc.nelements()!=inInc.nelements()); if (!changed) changed = (blc!=inBlc || trc!=inTrc || inc!=inInc); // return changed; } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LRegions/LCBox.h000066400000000000000000000116701476623553700200030ustar00rootroot00000000000000//# LCBox.h: Class to define a rectangular box of interest //# Copyright (C) 1997,1998,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LCBOX_H #define LATTICES_LCBOX_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Class to define a rectangular box of interest. // // // // // //
      • LCRegion // // // The LCBox class is a specialization of class // LCRegion. // It makes it possible to define a rectangular region of interest. // // // // // // // class LCBox: public LCRegionFixed { public: LCBox(); // Construct a box for the full lattice shape. explicit LCBox (const IPosition& latticeShape); // Construct from the Slicer defining the box. // The slicer may not contain a stride. LCBox (const Slicer& box, const IPosition& latticeShape); // Construct from the IPosition's defining the bottom-left and // top-right corner of the box. LCBox (const IPosition& blc, const IPosition& trc, const IPosition& latticeShape); // Construct from the Vector's defining the bottom-left and // top-right corner of the box. // LCBox (const Vector& blc, const Vector& trc, const IPosition& latticeShape); LCBox (const Vector& blc, const Vector& trc, const IPosition& latticeShape); // // Copy constructor (reference semantics). LCBox (const LCBox& other); virtual ~LCBox(); // Assignment (copy semantics). LCBox& operator= (const LCBox& other); // Comparison. Mask not checked. Use function // LRegionSingle::maskEqual to do this virtual Bool operator== (const LCRegion& other) const; // Make a copy of the derived object. virtual LCRegion* cloneRegion() const; // Get the class name (to store in the record). static String className(); // Get the region type. Returns className() virtual String type() const; // Convert the (derived) object to a record. virtual TableRecord toRecord (const String& tableName) const; // Convert correct object from a record. static LCBox* fromRecord (const TableRecord&, const String& tablename); // Get the box blc Vector blc() const; // Get the box trc Vector trc() const; // Verify a box specification. Illegal (inlcuding blc > trc) or // unspecified values are given 0 (blc) shape (trc) or // unity (inc). Returns True if any of the blc/trc/inc // are changed from their input values, else returns False static Bool verify (IPosition& blc, IPosition& trc, IPosition& inc, const IPosition& shape); protected: // Construct another LCBox (for e.g. another lattice) by moving // this one. It recalculates the bounding box. // A positive translation value indicates "to right". virtual LCRegion* doTranslate (const Vector& translateVector, const IPosition& newLatticeShape) const; private: // Make a box from the blc,trc such that it does not exceed the // lattice boundaries. void setSlicerBox (const IPosition& blc, const IPosition& trc); // Fill the blc and trc vector from IPositions. void fillBlcTrc(); //# Variables Vector itsBlc; Vector itsTrc; }; inline Vector LCBox::blc() const { return itsBlc; } inline Vector LCBox::trc() const { return itsTrc; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LRegions/LCComplement.cc000066400000000000000000000100351476623553700215060ustar00rootroot00000000000000//# LCComplement.cc: Make the complement of a region //# Copyright (C) 1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LCComplement::LCComplement() {} LCComplement::LCComplement (const LCRegion& region) : LCRegionMulti (False, ®ion) { defineBox(); } LCComplement::LCComplement (Bool takeOver, const PtrBlock& regions) : LCRegionMulti (takeOver, regions) { defineBox(); } LCComplement::LCComplement (const LCComplement& other) : LCRegionMulti (other) {} LCComplement::~LCComplement() {} LCComplement& LCComplement::operator= (const LCComplement& other) { if (this != &other) { LCRegionMulti::operator= (other); } return *this; } Bool LCComplement::operator== (const LCRegion& other) const { return LCRegionMulti::operator== (other); } LCRegion* LCComplement::cloneRegion() const { return new LCComplement (*this); } LCRegion* LCComplement::doTranslate (const Vector& translateVector, const IPosition& newLatticeShape) const { PtrBlock regions; multiTranslate (regions, translateVector, newLatticeShape); return new LCComplement (True, regions); } String LCComplement::className() { return "LCComplement"; } String LCComplement::type() const { return className(); } TableRecord LCComplement::toRecord (const String& tableName) const { TableRecord rec; defineRecordFields (rec, className()); rec.defineRecord ("regions", makeRecord(tableName)); return rec; } LCComplement* LCComplement::fromRecord (const TableRecord& rec, const String& tableName) { PtrBlock regions; unmakeRecord (regions, rec.asRecord("regions"), tableName); return new LCComplement (True, regions); } void LCComplement::defineBox() { const IPosition& shape = latticeShape(); // The bounding box is the full lattice. setBoundingBox (Slicer(IPosition(shape.nelements(),0), shape)); } void LCComplement::multiGetSlice (Array& buffer, const Slicer& section) { buffer.resize (section.length()); // Initialize to all true. buffer = True; // Determine which part to get from the region (which is region 0). // Get and store negation in buffer when anything found. const IPosition& shape = buffer.shape(); uInt nrdim = shape.nelements(); IPosition stbuf(nrdim); IPosition endbuf(nrdim); IPosition streg(nrdim); IPosition endreg(nrdim); const IPosition& inc = section.stride(); if (findAreas (stbuf, endbuf, streg, endreg, section, 0)) { Array tmpbuf; ((LCRegion*)(regions()[0]))->doGetSlice (tmpbuf, Slicer(streg, endreg, inc, Slicer::endIsLast)); buffer(stbuf,endbuf) = !tmpbuf; } } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LRegions/LCComplement.h000066400000000000000000000075321476623553700213600ustar00rootroot00000000000000//# LCComplement.h: Make the complement of a region //# Copyright (C) 1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LCCOMPLEMENT_H #define LATTICES_LCCOMPLEMENT_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Make the complement of a region. // // // // // //
      • LCRegion // // // The LCComplement class is a specialization of class // LCRegion. // It makes it possible to take the complement of a region with // respect to a given lattice shape. //

        // The center of the complement must be inside the lattice // // // // // // //

      • Expand along (slanted) cone lines // class LCComplement: public LCRegionMulti { public: LCComplement(); // Construct the complement of the given region. LCComplement (const LCRegion& region1); // Construct from multiple regions given as a Block. // When takeOver is True, the destructor will delete the // given regions. Otherwise a copy of the regions is made. LCComplement (Bool takeOver, const PtrBlock& regions); // Copy constructor (copy semantics). LCComplement (const LCComplement& other); virtual ~LCComplement(); // Assignment (copy semantics). LCComplement& operator= (const LCComplement& other); // Comparison virtual Bool operator== (const LCRegion& other) const; // Make a copy of the derived object. virtual LCRegion* cloneRegion() const; // Get the class name (to store in the record). static String className(); // Get the region type. Returns className() virtual String type() const; // Convert the (derived) object to a record. virtual TableRecord toRecord (const String& tableName) const; // Convert correct object from a record. static LCComplement* fromRecord (const TableRecord&, const String& tableName); protected: // Construct another LCRegion (for e.g. another lattice) by moving // this one. It recalculates the bounding box and mask. // A positive translation value indicates "to right". virtual LCRegion* doTranslate (const Vector& translateVector, const IPosition& newLatticeShape) const; // Do the actual getting of the mask. virtual void multiGetSlice (Array& buffer, const Slicer& section); private: // Make the bounding box and determine the offsets. void defineBox(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LRegions/LCConcatenation.cc000066400000000000000000000234541476623553700222010ustar00rootroot00000000000000//# LCConcatenation.cc: Combine multiple LCRegion's into a new dimension //# Copyright (C) 1998,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LCConcatenation::LCConcatenation() {} LCConcatenation::LCConcatenation (Bool takeOver, const PtrBlock& regions, Int extendAxis) : LCRegionMulti (takeOver, regions), itsExtendAxis (extendAxis) { // Define a box for the entire shape (is length of regions vector).. itsExtendBox = LCBox(IPosition(1,0), IPosition(1,regions.nelements()-1), IPosition(1,regions.nelements())); // Fill the other members variables and determine the bounding box. fill(); } LCConcatenation::LCConcatenation (Bool takeOver, const PtrBlock& regions, Int extendAxis, const LCBox& extendBox) : LCRegionMulti (takeOver, regions), itsExtendAxis (extendAxis), itsExtendBox (extendBox) { // Fill the other members variables and determine the bounding box. fill(); } LCConcatenation::LCConcatenation (const LCConcatenation& other) : LCRegionMulti (other), itsExtendAxis (other.itsExtendAxis), itsRegionAxes (other.itsRegionAxes), itsExtendBox (other.itsExtendBox) {} LCConcatenation::~LCConcatenation() {} LCConcatenation& LCConcatenation::operator= (const LCConcatenation& other) { if (this != &other) { LCRegionMulti::operator= (other); itsRegionAxes.resize (other.itsRegionAxes.nelements()); itsExtendAxis = other.itsExtendAxis; itsRegionAxes = other.itsRegionAxes; itsExtendBox = other.itsExtendBox; } return *this; } Bool LCConcatenation::operator== (const LCRegion& other) const { // Check if parent class matches. // If so, we can safely cast. if (! LCRegionMulti::operator== (other)) { return False; } const LCConcatenation& that = (const LCConcatenation&)other; // Check the private data if (! (itsExtendAxis == that.itsExtendAxis) || ! itsRegionAxes.isEqual (that.itsRegionAxes) || !(itsExtendBox == that.itsExtendBox)) { return False; } return True; } LCRegion* LCConcatenation::cloneRegion() const { return new LCConcatenation (*this); } LCRegion* LCConcatenation::doTranslate (const Vector& translateVector, const IPosition& newLatticeShape) const { uInt i; // First translate extendBox. // Take appropriate elements from the vectors. Vector boxTransVec (1); IPosition boxLatShape (1); boxTransVec(0) = translateVector(itsExtendAxis); boxLatShape(0) = newLatticeShape(itsExtendAxis); LCBox* boxPtr = (LCBox*)(itsExtendBox.translate (boxTransVec, boxLatShape)); // Now translate regions. uInt nrr = itsRegionAxes.nelements(); Vector regTransVec (nrr); IPosition regLatShape (nrr); for (i=0; i regions; multiTranslate (regions, regTransVec, regLatShape); // Create the new LCConcatenation object. LCConcatenation* extPtr = new LCConcatenation (True, regions, itsExtendAxis, *boxPtr); delete boxPtr; return extPtr; } String LCConcatenation::className() { return "LCConcatenation"; } String LCConcatenation::type() const { return className(); } TableRecord LCConcatenation::toRecord (const String& tableName) const { TableRecord rec; defineRecordFields (rec, className()); rec.defineRecord ("regions", makeRecord(tableName)); rec.define ("axis", itsExtendAxis); rec.defineRecord ("box", itsExtendBox.toRecord (tableName)); return rec; } LCConcatenation* LCConcatenation::fromRecord (const TableRecord& rec, const String& tableName) { PtrBlock regions; unmakeRecord (regions, rec.asRecord("regions"), tableName); LCBox* boxPtr = (LCBox*)(LCRegion::fromRecord (rec.asRecord("box"), tableName)); LCConcatenation* extPtr = new LCConcatenation (True, regions, rec.asInt ("axis"), *boxPtr); delete boxPtr; return extPtr; } void LCConcatenation::fillRegionAxes() { // Extend the axes to all of them. // The specified axis is the first one, thereafter the remaining axes. uInt nrdim = 1 + regions()[0]->ndim(); IPosition allAxes = IPosition::makeAxisPath (nrdim, IPosition(1, itsExtendAxis)); itsRegionAxes.resize (nrdim-1); for (uInt i=1; iboundingBox().start()); IPosition regionTrc(regions()[0]->boundingBox().end()); uInt nr = regions().nelements(); for (i=1; iboundingBox().start(); const IPosition& regtrc = regions()[i]->boundingBox().end(); for (uInt j=0; j regionTrc(j)) { regionTrc(j) = regtrc(j); } } } // Make up the lattice shape from the first region and box latticeshape. // Fill the bounding box from blc/trc in regions and box. uInt nrdim = nrr+1; IPosition latShape(nrdim); IPosition blc (nrdim); IPosition trc (nrdim); const IPosition& regionShp = regions()[0]->latticeShape(); for (i=0; i& buffer, const Slicer& section) { buffer.resize (section.length()); buffer = False; uInt i; // Construct a slicer for the regions axes only, since the concatenation // has one more axis. uInt nrr = itsRegionAxes.nelements(); IPosition blc(nrr); IPosition len(nrr); IPosition inc(nrr); for (i=0; i tmpbuf; LCRegion* reg = (LCRegion*)(regions()[i]); reg->doGetSlice (tmpbuf, Slicer(streg, endreg, inc, Slicer::endIsLast)); // The buffer dimensionality is 1 more than the region's. // So the extendAxis needs to be inserted into the IPositions. for (uInt j=0; j reformBuf (tmpbuf.reform (tmpShape)); Array bufsect (buffer(bufStart,bufEnd)); DebugAssert (bufsect.shape() == reformBuf.shape(), AipsError); bufsect = reformBuf; } } } IPosition LCConcatenation::doNiceCursorShape (uInt maxPixels) const { return Lattice::doNiceCursorShape (maxPixels); } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LRegions/LCConcatenation.h000066400000000000000000000146141476623553700220410ustar00rootroot00000000000000//# LCConcatenation.h: Combine multiple LCRegion's into a new dimension //# Copyright (C) 1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LCCONCATENATION_H #define LATTICES_LCCONCATENATION_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Combine multiple LCRegion's into a new dimension. // // // // // //
      • LCRegion // // // The LCConcatenation class is a specialization of class // LCRegion. // It makes it possible to combine multiple LCRegion's and to add a // dimension on them. The range (beginning and end) in that new // dimension have to be specified using an // LCBox object. // When the LCBox is complete, it will be checked if the given number // of regions matches the length of the given range (so it could only // be done after the makeComplete call). // Using a fractional box does not make much sense, because it results // in a varying length range when used with varying shaped lattices. // However, one can use it if wanted. //
        // LCConcatenation can be seen as a mixture of the classes // LCUnion and // LCExtension. Like LCUnion it // combines regions and like LCExtension it increases the dimensionality // for the new region (be it with only 1). //
        // E.g. One can define a different polygon in the RA-DEC plane of each // channel. LCConcatenation makes it possible to combine the polygons // to one 3D region in the RA-DEC-Freq cube. //
        // // This example combines n (relative) circles // given in the x-z plane along the y-axis. // In this example the regions used are circles with the same centers, // but it is also possible to combine differently shaped regions. // Note that LCConcatenation takes over the pointers to the individual regions, // so they do not need to be deleted (the LCConcatenation destructor does it). // // IPosition center (2,10,20); // PtrBlock cirPtr(n); // for (i=0; i // //# //#
      • //# class LCConcatenation: public LCRegionMulti { public: LCConcatenation(); // Combine the given regions. // When takeOver is True, the destructor will delete the // given regions. Otherwise a copy of the regions is made. // The extend range has to be given as a 1-dimensional box. // The default range is the entire axis. // LCConcatenation (Bool takeOver, const PtrBlock& regions, Int extendAxis); LCConcatenation (Bool takeOver, const PtrBlock& regions, Int extendAxis, const LCBox& extendRange); // // Copy constructor (copy semantics). LCConcatenation (const LCConcatenation& other); virtual ~LCConcatenation(); // Assignment (copy semantics). LCConcatenation& operator= (const LCConcatenation& other); // Comparison virtual Bool operator== (const LCRegion& other) const; // Make a copy of the derived object. virtual LCRegion* cloneRegion() const; // Get the extend axis. Int extendAxis() const; // Get the extend box. const LCBox& extendBox() const; // Get the class name (to store in the record). static String className(); // Get the region type. Returns the class name. virtual String type() const; // Convert the (derived) object to a record. virtual TableRecord toRecord (const String& tableName) const; // Convert correct object from a record. static LCConcatenation* fromRecord (const TableRecord&, const String& tableName); protected: // Construct another LCRegion (for e.g. another lattice) by moving // this one. It recalculates the bounding box and mask. // A positive translation value indicates "to right". virtual LCRegion* doTranslate (const Vector& translateVector, const IPosition& newLatticeShape) const; // Do the actual getting of the mask. virtual void multiGetSlice (Array& buffer, const Slicer& section); // This function is needed here because the niceCursorShape of the // contributing region does not make any sense (other dimensionality). virtual IPosition doNiceCursorShape (uInt maxPixels) const; private: // Fill the object. // void fillRegionAxes(); void fill(); // Int itsExtendAxis; IPosition itsRegionAxes; LCBox itsExtendBox; }; inline Int LCConcatenation::extendAxis() const { return itsExtendAxis; } inline const LCBox& LCConcatenation::extendBox() const { return itsExtendBox; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LRegions/LCDifference.cc000066400000000000000000000113171476623553700214410ustar00rootroot00000000000000//# LCDifference.cc: Make the difference of 2 region //# Copyright (C) 1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LCDifference::LCDifference() {} LCDifference::LCDifference (const LCRegion& region1, const LCRegion& region2) : LCRegionMulti (False, ®ion1, ®ion2) { defineBox(); } LCDifference::LCDifference (Bool takeOver, const PtrBlock& regions) : LCRegionMulti (takeOver, regions) { defineBox(); } LCDifference::LCDifference (const LCDifference& other) : LCRegionMulti (other) {} LCDifference::~LCDifference() {} LCDifference& LCDifference::operator= (const LCDifference& other) { if (this != &other) { LCRegionMulti::operator= (other); } return *this; } Bool LCDifference::operator== (const LCRegion& other) const { return LCRegionMulti::operator== (other); } LCRegion* LCDifference::cloneRegion() const { return new LCDifference (*this); } LCRegion* LCDifference::doTranslate (const Vector& translateVector, const IPosition& newLatticeShape) const { PtrBlock regions; multiTranslate (regions, translateVector, newLatticeShape); return new LCDifference (True, regions); } String LCDifference::className() { return "LCDifference"; } String LCDifference::type() const { return className(); } TableRecord LCDifference::toRecord (const String& tableName) const { TableRecord rec; defineRecordFields (rec, className()); rec.defineRecord ("regions", makeRecord(tableName)); return rec; } LCDifference* LCDifference::fromRecord (const TableRecord& rec, const String& tableName) { PtrBlock regions; unmakeRecord (regions, rec.asRecord("regions"), tableName); return new LCDifference (True, regions); } void LCDifference::defineBox() { // The bounding box is the bounding box of the first lattice. setBoundingBox (regions()[0]->boundingBox()); } void LCDifference::multiGetSlice (Array& buffer, const Slicer& section) { // Get the required part from region1. // Note this getSlice version ensures that the result is not // referencing some internal Lattice array. Array tmp = regions()[0]->getSlice(section); buffer.reference(tmp); // Determine which part to get from region2. // Get and store negation in buffer when anything found. const IPosition& shape = buffer.shape(); uInt nrdim = shape.nelements(); IPosition stbuf(nrdim); IPosition endbuf(nrdim); IPosition streg(nrdim); IPosition endreg(nrdim); const IPosition& inc = section.stride(); if (findAreas (stbuf, endbuf, streg, endreg, section, 1)) { Array tmpbuf; LCRegion* reg = (LCRegion*)(regions()[1]); reg->doGetSlice (tmpbuf, Slicer(streg, endreg, inc, Slicer::endIsLast)); Array bufreg = buffer(stbuf,endbuf); DebugAssert (bufreg.shape() == tmpbuf.shape(), AipsError); // Make pixel in buffer False when tmpbuf has a True pixel. Bool deleteBuf, deleteTmp; Bool* buf = bufreg.getStorage (deleteBuf); Bool* bufptr = buf; Bool* bufend = buf + bufreg.nelements(); const Bool* tmp = tmpbuf.getStorage (deleteTmp); const Bool* tmpptr = tmp; while (bufptr < bufend) { if (*tmpptr++) { *bufptr = False; } bufptr++; } bufreg.putStorage (buf, deleteBuf); tmpbuf.freeStorage (tmp, deleteTmp); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LRegions/LCDifference.h000066400000000000000000000100171476623553700212770ustar00rootroot00000000000000//# LCDifference.h: Make the difference of 2 regions //# Copyright (C) 1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LCDIFFERENCE_H #define LATTICES_LCDIFFERENCE_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Make the difference of 2 regions. // // // // // //
      • LCRegion // // // The LCDifference class is a specialization of class // LCRegion. // It makes it possible to "subtract" one region from // another. For example, imagine an overlapping box // and circle. The box - circle is the box with the // chunk taken out of it where the circle overlaps. // The circle - box is the circle with the chunk // taken out of it where the box overlaps. //

        // The center of the difference must be inside the lattice // // // // // // // class LCDifference: public LCRegionMulti { public: LCDifference(); // Construct the difference region1 - region2. LCDifference (const LCRegion& region1, const LCRegion& region2); // Construct from multiple regions given as a Block. // When takeOver is True, the destructor will delete the // given regions. Otherwise a copy of the regions is made. LCDifference (Bool takeOver, const PtrBlock& regions); // Copy constructor (copy semantics). LCDifference (const LCDifference& other); virtual ~LCDifference(); // Assignment (copy semantics). LCDifference& operator= (const LCDifference& other); // Comparison virtual Bool operator== (const LCRegion& other) const; // Make a copy of the derived object. virtual LCRegion* cloneRegion() const; // Get the class name (to store in the record). static String className(); // Get the region type. Returns className() virtual String type() const; // Convert the (derived) object to a record. virtual TableRecord toRecord (const String& tableName) const; // Convert correct object from a record. static LCDifference* fromRecord (const TableRecord&, const String& tableName); protected: // Construct another LCRegion (for e.g. another lattice) by moving // this one. It recalculates the bounding box and mask. // A positive translation value indicates "to right". virtual LCRegion* doTranslate (const Vector& translateVector, const IPosition& newLatticeShape) const; // Do the actual getting of the mask. virtual void multiGetSlice (Array& buffer, const Slicer& section); private: // Make the bounding box and determine the offsets. void defineBox(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LRegions/LCEllipsoid.cc000066400000000000000000000371751476623553700213450ustar00rootroot00000000000000//# LCEllipsoid.cc: Define an N-dimensional ellipsoidal region of interest //# Copyright (C) 1997,1998,1999,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LCEllipsoid::LCEllipsoid() : _theta(0) {} LCEllipsoid::LCEllipsoid (const IPosition& center, Float radius, const IPosition& latticeShape) : LCRegionFixed (latticeShape), itsRadii (latticeShape.nelements(), radius), _theta (0) { fillCenter (center); setBoundingBox (makeBox(itsRadii, latticeShape)); defineMask(); } LCEllipsoid::LCEllipsoid (const Vector& center, Float radius, const IPosition& latticeShape) : LCRegionFixed (latticeShape), itsCenter(center.copy()), itsRadii (latticeShape.nelements(), radius), _theta (0) { setBoundingBox(makeBox(itsRadii, latticeShape)); defineMask(); } LCEllipsoid::LCEllipsoid(const Vector& center, Double radius, const IPosition& latticeShape) : LCRegionFixed (latticeShape), itsCenter (center.size()), itsRadii (center.size(), radius), _theta (0) { for (uInt i=0; i& center, const Vector& radii, const IPosition& latticeShape) : LCRegionFixed (latticeShape), itsCenter (center.copy()), itsRadii (radii.copy()), _theta(0) { setBoundingBox(makeBox(itsRadii, latticeShape)); defineMask(); } LCEllipsoid::LCEllipsoid(const Vector& center, const Vector& radii, const IPosition& latticeShape) : LCRegionFixed (latticeShape), itsCenter (center.size()), itsRadii (radii.size()), _theta (0) { for (uInt i=0; i proj(itsRadii.size(), max(itsRadii)); setBoundingBox(makeBox(proj, latticeShape)); _defineMask2D(); } } LCEllipsoid::LCEllipsoid (const LCEllipsoid& other) : LCRegionFixed(other), itsCenter(other.itsCenter), itsRadii(other.itsRadii), _epsilon(other._epsilon), _theta(other._theta), _centerIsInside(other._centerIsInside) {} LCEllipsoid::~LCEllipsoid() {} LCEllipsoid& LCEllipsoid::operator= (const LCEllipsoid& other) { if (this != &other) { LCRegionFixed::operator= (other); itsCenter.resize (other.itsCenter.nelements()); itsRadii.resize (other.itsCenter.nelements()); itsCenter = other.itsCenter; itsRadii = other.itsRadii; _epsilon = other._epsilon; _theta = other._theta; _centerIsInside = other._centerIsInside; } return *this; } Bool LCEllipsoid::operator== (const LCRegion& other) const { // Check if parent class matches. // If so, we can safely cast. if (! LCRegionFixed::operator== (other)) { return False; } const LCEllipsoid& that = (const LCEllipsoid&)other; // Compare private data. if ( itsCenter.nelements() != that.itsCenter.nelements() || itsRadii.nelements() != that.itsRadii.nelements() ) { return False; } for (uInt i=0; i& translateVector, const IPosition& newLatticeShape) const { uInt ndim = latticeShape().nelements(); Vector center; center = itsCenter; for (uInt i=0; i center (rec.toArrayFloat ("center")); if (center.size() != 2 || ! rec.isDefined("theta")) { return new LCEllipsoid (center-off, Vector(rec.toArrayFloat ("radii")), Vector(rec.toArrayInt ("shape")) ); } else { Vector radii (rec.toArrayFloat ("radii")); return new LCEllipsoid( center(IPosition(1,0))-off, center(IPosition(1,1))-off, radii[0], radii[1], rec.asFloat("theta"), Vector(rec.toArrayInt ("shape")) ); } } void LCEllipsoid::fillCenter(const IPosition& center) { itsCenter.resize (center.nelements()); for (uInt i=0; i& radii, const IPosition& latticeShape ) { uInt nrdim = itsCenter.size(); // First make sure dimensionalities conform. if (latticeShape.size() != nrdim || radii.size() != nrdim) { ThrowCc("dimensionality of center,radii,lattice mismatch"); } // Determine blc and trc. IPosition blc(nrdim); IPosition trc(nrdim); _epsilon.resize(nrdim); _centerIsInside = True; for (uInt i=0; i latticeShape[i]-1 || itsCenter[i] < 0) { _centerIsInside = False; ThrowIf( itsCenter[i] + radii[i] < 0 || itsCenter[i] - radii[i] > latticeShape[i] - 1, "Ellipsoid lies completely outside the lattice" ); } _epsilon[i] = powf(10.0, int(log10(2*radii[i]))-5); blc[i] = max(Int(itsCenter[i] - radii[i] + 1 - _epsilon[i]), 0); trc[i] = min(Int(itsCenter[i] + radii[i] + _epsilon[i]), latticeShape[i] - 1); if (blc[i] > trc[i]) { ostringstream rstr; rstr << radii; ThrowCc( "ellipsoid is empty (radii " + rstr.str() + " too small)" ); } } return Slicer(blc, trc, Slicer::endIsLast); } const Float& LCEllipsoid::theta() const { ThrowIf( itsRadii.size() != 2, "Angle can only be gotten for 2-D ellipses" ); return _theta; } void LCEllipsoid::defineMask() { if (! _centerIsInside) { _doOutside(); return; } uInt i; // Create the mask with the shape of the bounding box. // Set the mask initially to False. const IPosition& length = boundingBox().length(); uInt nrdim = length.nelements(); Array mask(length); mask = False; // Get access to the mask storage. Bool deleteIt; Bool* maskData = mask.getStorage (deleteIt); // Initialize some variables for the loop below. Float center0 = itsCenter[0] - boundingBox().start()[0]; Float radsq0 = itsRadii[0] * itsRadii[0]; Int np = length(0); IPosition pos (nrdim, 0); Vector center (nrdim); Vector radsq (nrdim); Vector dist (nrdim, 0.0); Float distsq = 0; for (i=1; i= 0) { d = sqrt(d * radsq0); d += _epsilon[0]; Int start = max(Int(center0 - d + 1 - _epsilon[i]), 0); Int end = min(Int(center0 + d + _epsilon[i]), np-1); for (Int j=start; j<=end; j++) { maskData[j] = True; } } // Go to the next line and update the line distance. maskData += np; for (i=1; i mask(length); mask = False; // Get access to the mask storage. Bool deleteIt; Bool* maskData = mask.getStorage (deleteIt); Vector center(ndim); Vector rad2(ndim); for (uInt i=0; i prevSum) { break; } prevSum = sum; } maskData += length[0]; } mask.putStorage (maskData, deleteIt); ThrowIf( ! _centerIsInside && ! casacore::anyTrue(mask), "Ellipsoid lies entirely outside the lattice" ); setMask (mask); } void LCEllipsoid::_doOutside() { // Create the mask with the shape of the bounding box. // Set the mask initially to False. const IPosition& length = boundingBox().length(); Float center0 = itsCenter[0] - boundingBox().start()[0]; uInt ndim = length.size(); Array mask(length); Int np = length[0]; mask = False; // Get access to the mask storage. Bool deleteIt; Bool* maskData = mask.getStorage (deleteIt); Vector center(ndim); Vector rad2 = itsRadii * itsRadii; IPosition pos(ndim, 0); Vector d2(ndim); Float curD2 = 0; for (uInt i=1; i= 0) { // x**2/rad2[0] = 1 - curD2 Float maxXDiff = itsRadii[0] * sqrt(1 - curD2); Int start = max(Int(center0 - maxXDiff + 1 - _epsilon[0]), 0); Int end = min(Int(center0 + maxXDiff + _epsilon[0]), np-1); for (Int j=start; j<=end; ++j) { maskData[j] = True; } } maskData += np; for (i=1; i #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //

        // Define an N-dimensional ellipsoidal region of interest. // // // // // //
      • LCRegion // // // The LCEllipsoid class is a specialization of class // LCRegion. // It makes it possible to define an N-dimensional ellipsoidal region // of interest, which includes the border. A separate constructor exists // to define the special case of an N-dimensional sphere. //
        // The center and the radii of the ellipsoid do not need to be pixel aligned. // The center of the ellipsoid may be outside the lattice. // The current implementation only supports ellipsoids with axes parallel // to the lattice axes except in the case of a 2-D ellipse for which a // constructor is provided for specifying the angle between the x-axis // and major axis of the ellipse. //

        // It can only be used for a lattice of any dimensionality as long as the // dimensionality of the (hyper-)ellipsoid matches the dimensionality of // the lattice. // // // // // // //

      • Arguments to have ellipsoid axes not parallel to lattice axes for // dimensions greater than 2. This is a nontrivial problem because of the // complexity of the rotation matrices involved. // class LCEllipsoid: public LCRegionFixed { public: LCEllipsoid(); // Construct an N-dimensional sphere with the given center and // radius (in pixels). The center is pixel-aligned. LCEllipsoid (const IPosition& center, Float radius, const IPosition& latticeShape); // Construct an N-dimensional sphere with the given center and // radius (in pixels). The center does not need to be pixel-aligned. // LCEllipsoid (const Vector& center, Float radius, const IPosition& latticeShape); LCEllipsoid (const Vector& center, Double radius, const IPosition& latticeShape); // // Construct an N-dimensional ellipsoid with the given center and // radii (in pixels). The center does not need to be pixel-aligned. // (the radii are half the length of the axes of the ellipsoid). // LCEllipsoid (const Vector& center, const Vector& radii, const IPosition& latticeShape); LCEllipsoid (const Vector& center, const Vector& radii, const IPosition& latticeShape); // // Construct a two dimensional ellipse with theta being the angle from // the x-axis to the major axis of the ellipse in radians. LCEllipsoid ( const Float xcenter, const Float ycenter, const Float majorAxis, const Float minorAxis, const Float theta, const IPosition& latticeShape ); // Copy constructor (reference semantics). LCEllipsoid (const LCEllipsoid& other); virtual ~LCEllipsoid(); // Assignment (copy semantics). LCEllipsoid& operator= (const LCEllipsoid& other); // Comparison virtual Bool operator== (const LCRegion& other) const; // Make a copy of the derived object. virtual LCRegion* cloneRegion() const; // Get the center. const Vector& center() const; // Get the radii. const Vector& radii() const; // Get the angle of the major axis of the ellipse relative to the x-axis // 2-D only, throws exception if ellipse is not 2-D. const Float& theta() const; // Get the class name (to store in the record). static String className(); // Get the region type. Returns className() virtual String type() const; // Convert the (derived) object to a record. virtual TableRecord toRecord (const String& tableName) const; // Convert correct object from a record. static LCEllipsoid* fromRecord (const TableRecord&, const String& tableName); protected: // Construct another LCBox (for e.g. another lattice) by moving // this one. It recalculates the bounding box. // A positive translation value indicates "to right". virtual LCRegion* doTranslate (const Vector& translateVector, const IPosition& newLatticeShape) const; private: // Fill the itsCenter vector from an IPosition. void fillCenter (const IPosition& center); // Make the bounding box from center, radii, and shape. Slicer makeBox (const Vector& radii, const IPosition& latticeShape); // Define the mask to indicate which elements are inside the ellipsoid. void defineMask(); //for 2-D ellipse with non-zero theta. Works for both cases center // inside or outside the lattice. void _defineMask2D(); // set the mask in the case the center lies outside the lattice void _doOutside(); Vector itsCenter; Vector itsRadii; // small offset to guard against roundoff error Vector _epsilon; // for 2-D case only Float _theta; // is center inside the lattice? Bool _centerIsInside; }; inline const Vector& LCEllipsoid::center() const { return itsCenter; } inline const Vector& LCEllipsoid::radii() const { return itsRadii; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LRegions/LCExtension.cc000066400000000000000000000241231476623553700213620ustar00rootroot00000000000000//# LCExtension.cc: Extend an LCRegion along straight lines to other dimensions //# Copyright (C) 1998,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LCExtension::LCExtension() {} LCExtension::LCExtension (const LCRegion& region, const IPosition& extendAxes, const LCBox& extendBox) : LCRegionMulti (True, region.cloneRegion()) { // Fill the other members variables and determine the bounding box. fill (extendAxes, extendBox); } LCExtension::LCExtension (Bool takeOver, const LCRegion* region, const IPosition& extendAxes, const LCBox& extendBox) : LCRegionMulti (takeOver, region) { // Fill the other members variables and determine the bounding box. fill (extendAxes, extendBox); } LCExtension::LCExtension (const LCExtension& other) : LCRegionMulti (other), itsExtendAxes (other.itsExtendAxes), itsRegionAxes (other.itsRegionAxes), itsExtendBox (other.itsExtendBox) {} LCExtension::~LCExtension() {} LCExtension& LCExtension::operator= (const LCExtension& other) { if (this != &other) { LCRegionMulti::operator= (other); itsExtendAxes.resize (other.itsExtendAxes.nelements()); itsRegionAxes.resize (other.itsRegionAxes.nelements()); itsExtendAxes = other.itsExtendAxes; itsRegionAxes = other.itsRegionAxes; itsExtendBox = other.itsExtendBox; } return *this; } Bool LCExtension::operator== (const LCRegion& other) const { // Check if parent class matches. // If so, we can safely cast. if (! LCRegionMulti::operator== (other)) { return False; } const LCExtension& that = (const LCExtension&)other; // Check the private data if (! itsExtendAxes.isEqual (that.itsExtendAxes) || ! itsRegionAxes.isEqual (that.itsRegionAxes) || !(itsExtendBox == that.itsExtendBox)) { return False; } return True; } LCRegion* LCExtension::cloneRegion() const { return new LCExtension (*this); } LCRegion* LCExtension::doTranslate (const Vector& translateVector, const IPosition& newLatticeShape) const { uInt i; // First translate the extendBox. // Take appropriate elements from the vectors. uInt nre = itsExtendAxes.nelements(); Vector boxTransVec (nre); IPosition boxLatShape (nre); for (i=0; i regTransVec (nrr); IPosition regLatShape (nrr); for (i=0; i(rec.toArrayInt ("axes")), *boxPtr); delete boxPtr; return extPtr; } void LCExtension::fillRegionAxes() { uInt nre = itsExtendAxes.nelements(); uInt nrr = region().ndim(); uInt nrdim = nre+nrr; // allAxes will get the remaining (thus region) axes at the end. IPosition allAxes = IPosition::makeAxisPath (nrdim, itsExtendAxes); itsRegionAxes.resize (nrr); for (uInt i=nre; i boxLatBlc(nre); Vector boxLatTrc(nre); Vector reginx(nre); GenSortIndirect::sort (reginx, extendAxes.storage(), nre); Int first = -1; for (uInt i=0; i& buffer, const Slicer& section) { buffer.resize (section.length()); uInt i; uInt nre = itsExtendAxes.nelements(); uInt nrr = itsRegionAxes.nelements(); // Read the required region section. // This means we have to create a Slicer for those axes only. IPosition blc(nrr); IPosition len(nrr); IPosition inc(nrr); IPosition shape(buffer.ndim(), 1); for (i=0; i tmpbuf(len); LCRegion* reg = (LCRegion*)(regions()[0]); reg->doGetSlice (tmpbuf, Slicer(blc, len, inc)); // Reform tmpbuf, so it has the same dimensionality as buffer. Array mask = tmpbuf.reform (shape); // Now we have to extend tmpbuf along all extend axes. const IPosition& length = section.length(); IPosition pos (buffer.ndim(), 0); IPosition end (buffer.shape() - 1); //# Iterate along itsExtendAxes (the new axes) through the new mask. for (;;) { for (i=0; i::doNiceCursorShape (maxPixels); } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LRegions/LCExtension.h000066400000000000000000000126051476623553700212260ustar00rootroot00000000000000//# LCExtension.h: Extend an LCRegion along straight lines to other dimensions //# Copyright (C) 1998,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LCEXTENSION_H #define LATTICES_LCEXTENSION_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Extend an LCRegion along straight lines to other dimensions // // // // // //
      • LCRegion // // // The LCExtension class is a specialization of class // LCRegion. // It makes it possible to extend a LCRegion along straight lines to // other dimensions. E.g. a circle in the xy-plane can be extended to // a cylinder in the xyz-space. // It can be used for a lattice of any dimensionality as long as the // dimensionality of the (hyper-)extension matches the dimensionality of // the lattice. // // // // // // //
      • Extend along (slanted) cone lines // class LCExtension: public LCRegionMulti { public: LCExtension(); // Extend the given region along axes as given by extendAxes // from the bottom left corner (blc) to the top right corner (trc) // as given by extendBox. // Every kind of box (absolute, relative, fractional, unspecified) // can be used to define the extension blc and trc. // The sum of the dimensionality of the region and the extend box // make up the dimensionality of the LCExtension region. // Similarly the lattice shapes in region and box are combined. //
        // The second version takes over the pointer when the switch is true. // LCExtension (const LCRegion& region, const IPosition& extendAxes, const LCBox& extendBox); LCExtension (Bool takeOver, const LCRegion* region, const IPosition& extendAxes, const LCBox& extendBox); // // Copy constructor (copy semantics). LCExtension (const LCExtension& other); virtual ~LCExtension(); // Assignment (copy semantics). LCExtension& operator= (const LCExtension& other); // Comparison virtual Bool operator== (const LCRegion& other) const; // Make a copy of the derived object. virtual LCRegion* cloneRegion() const; // Get the original region. const LCRegion& region() const; // Get the extend axes. const IPosition& extendAxes() const; // Get the extend box. const LCBox& extendBox() const; // Get the class name (to store in the record). static String className(); // Get the region type. Returns the class name. virtual String type() const; // Convert the (derived) object to a record. virtual TableRecord toRecord (const String& tableName) const; // Convert correct object from a record. static LCExtension* fromRecord (const TableRecord&, const String& tableName); protected: // Construct another LCRegion (for e.g. another lattice) by moving // this one. It recalculates the bounding box and mask. // A positive translation value indicates "to right". virtual LCRegion* doTranslate (const Vector& translateVector, const IPosition& newLatticeShape) const; // Do the actual getting of the mask. virtual void multiGetSlice (Array& buffer, const Slicer& section); // This function is needed here because the niceCursorShape of the // contributing region does not make any sense (other dimensionality). virtual IPosition doNiceCursorShape (uInt maxPixels) const; private: // Fill the object. // void fillRegionAxes(); void fill (const IPosition& stretchAxes, const LCBox& stretchBox); // IPosition itsExtendAxes; IPosition itsRegionAxes; LCBox itsExtendBox; }; inline const LCRegion& LCExtension::region() const { return *(regions()[0]); } inline const IPosition& LCExtension::extendAxes() const { return itsExtendAxes; } inline const LCBox& LCExtension::extendBox() const { return itsExtendBox; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LRegions/LCHDF5Mask.cc000066400000000000000000000126151476623553700207130ustar00rootroot00000000000000//# LCHDF5Mask.cc: Class to define a rectangular mask of interest //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LCHDF5Mask::LCHDF5Mask() {} LCHDF5Mask::LCHDF5Mask (const TiledShape& latticeShape, const std::shared_ptr& file, const String& maskName) : LCRegionSingle (latticeShape.shape()), itsBox (IPosition(latticeShape.shape().nelements(), 0), latticeShape.shape()-1, latticeShape.shape()) { setBoundingBox (itsBox.boundingBox()); itsMask = HDF5Lattice (latticeShape, file, maskName, "masks"); setMaskPtr (itsMask); } LCHDF5Mask::LCHDF5Mask (const TiledShape& maskShape, const LCBox& box, const std::shared_ptr& file, const String& maskName) : LCRegionSingle (box.latticeShape()), itsBox (box) { // Check if box shape and mask shape are equal. if (itsBox.shape() != maskShape.shape()) { throw (AipsError ("LCHDF5Mask::LCHDF5Mask- " "shape of mask and box differ")); } setBoundingBox (itsBox.boundingBox()); itsMask = HDF5Lattice (box.latticeShape(), file, maskName, "masks"); setMaskPtr (itsMask); } LCHDF5Mask::LCHDF5Mask (HDF5Lattice& mask, const LCBox& box) : LCRegionSingle (box.latticeShape()), itsBox (box) { // Check if box shape and mask shape are equal. if (itsBox.shape() != mask.shape()) { throw (AipsError ("LCHDF5Mask::LCHDF5Mask- " "shape of mask and box differ")); } setBoundingBox (itsBox.boundingBox()); itsMask = mask; setMaskPtr (itsMask); } LCHDF5Mask::LCHDF5Mask (const LCHDF5Mask& other) : LCRegionSingle (other), itsBox (other.itsBox), itsMask(other.itsMask) { setMaskPtr (itsMask); } LCHDF5Mask::~LCHDF5Mask() {} LCHDF5Mask& LCHDF5Mask::operator= (const LCHDF5Mask& that) { if (this != &that) { LCRegionSingle::operator= (that); itsBox = that.itsBox; itsMask = that.itsMask; setMaskPtr (itsMask); } return *this; } Bool LCHDF5Mask::operator== (const LCRegion& other) const { // Check if parent class matches. // If so, we can safely cast. if (! LCRegionSingle::operator== (other)) { return False; } const LCHDF5Mask& that = (const LCHDF5Mask&)other; // Check the box and mask. return (itsBox == that.itsBox && masksEqual (that)); } LCRegion* LCHDF5Mask::cloneRegion() const { return new LCHDF5Mask(*this); } uInt LCHDF5Mask::advisedMaxPixels() const { return itsMask.advisedMaxPixels(); } IPosition LCHDF5Mask::doNiceCursorShape (uInt maxPixels) const { return itsMask.niceCursorShape (maxPixels); } LatticeIterInterface* LCHDF5Mask::makeIter (const LatticeNavigator& navigator, Bool useRef) const { return itsMask.makeIter (navigator, useRef); } void LCHDF5Mask::flush() { itsMask.flush(); } LCRegion* LCHDF5Mask::doTranslate (const Vector&, const IPosition&) const { // An LCHDF5Mask cannot be translated. throw (AipsError ("LCHDF5Mask::translate is not supported")); return 0; } String LCHDF5Mask::className() { return "LCHDF5Mask"; } String LCHDF5Mask::type() const { return className(); } TableRecord LCHDF5Mask::toRecord (const String& tableName) const { TableRecord rec; defineRecordFields (rec, className()); rec.define ("filename", itsMask.file()->getName()); rec.define ("maskname", itsMask.arrayName()); rec.defineRecord ("box", itsBox.toRecord (tableName)); return rec; } LCHDF5Mask* LCHDF5Mask::fromRecord (const TableRecord& rec, const String& tableName) { HDF5Lattice mask(rec.asString("filename"), rec.asString("maskname"), "masks"); LCBox* boxPtr = (LCBox*)(LCRegion::fromRecord (rec.asRecord("box"), tableName)); LCHDF5Mask* regPtr = new LCHDF5Mask (mask, *boxPtr); delete boxPtr; return regPtr; } Bool LCHDF5Mask::isWritable() const { return itsMask.isWritable(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LRegions/LCHDF5Mask.h000066400000000000000000000113551476623553700205550ustar00rootroot00000000000000//# LCHDF5Mask.h: Class to define a rectangular mask of interest //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LCHDF5MASK_H #define LATTICES_LCHDF5MASK_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Class to define a rectangular mask as a region // // // // // //
      • LCRegionSingle // // // The LCHDF5Mask class is a specialization of class // LCRegionSingle. // It holds a mask for an HDF5Image in an HDF5Lattice object. // class LCHDF5Mask: public LCRegionSingle { public: LCHDF5Mask(); // Construct an HDF5Mask object for (part of) a lattice. // It is put in group Masks of the HDF5 file. // The group is created if not existing yet. // The box defines the position of the mask. // The default mask shape is the lattice shape. // LCHDF5Mask (const TiledShape& latticeShape, const std::shared_ptr& file, const String& maskName); LCHDF5Mask (const TiledShape& maskShape, const LCBox& box, const std::shared_ptr& file, const String& maskName); LCHDF5Mask (HDF5Lattice& mask, const LCBox& box); // // Copy constructor (copy semantics). LCHDF5Mask (const LCHDF5Mask& other); // Destructor virtual ~LCHDF5Mask(); // Assignment (reference semantics). LCHDF5Mask& operator= (const LCHDF5Mask& other); // Comparison virtual Bool operator==(const LCRegion& other) const; // Make a copy of the derived object. virtual LCRegion* cloneRegion() const; // This function is used by the LatticeIterator class to generate an // iterator of the correct type for this Lattice. Not recommended // for general use. virtual LatticeIterInterface* makeIter (const LatticeNavigator& navigator, Bool useRef) const; // Returns the maximum recommended number of pixels for a cursor. // This is the number of pixels in a tile. virtual uInt advisedMaxPixels() const; // Help the user pick a cursor for most efficient access. virtual IPosition doNiceCursorShape (uInt maxPixels) const; // Flush the data (but do not unlock). virtual void flush(); // Get the class name (to store in the record). static String className(); // Region type. Returns class name. virtual String type() const; // Convert the (derived) object to a record. virtual TableRecord toRecord (const String& tableName) const; // Convert correct object from a record. static LCHDF5Mask* fromRecord (const TableRecord&, const String& tablename); // An LCHDF5Mask is writable if the underlying HDF5Lattice is. virtual Bool isWritable() const; protected: // Construct another LCHDF5Mask (for e.g. another lattice) by moving // this one. It recalculates the bounding mask. // A positive translation value indicates "to right". virtual LCRegion* doTranslate (const Vector& translateVector, const IPosition& newLatticeShape) const; private: // Create the object from a record (for an existing mask). LCHDF5Mask (HDF5Lattice& mask, const IPosition& blc, const IPosition& latticeShape); LCBox itsBox; HDF5Lattice itsMask; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LRegions/LCIntersection.cc000066400000000000000000000137671476623553700220700ustar00rootroot00000000000000//# LCIntersection.cc: Make the intersection of 2 or more regions //# Copyright (C) 1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LCIntersection::LCIntersection() {} LCIntersection::LCIntersection (const LCRegion& region1, const LCRegion& region2) : LCRegionMulti (region1, region2) { defineBox(); } LCIntersection::LCIntersection (Bool takeOver, const LCRegion* region1, const LCRegion* region2, const LCRegion* region3, const LCRegion* region4, const LCRegion* region5, const LCRegion* region6, const LCRegion* region7, const LCRegion* region8, const LCRegion* region9, const LCRegion* region10) : LCRegionMulti (takeOver, region1, region2, region3, region4, region5, region6, region7, region8, region9, region10) { defineBox(); } LCIntersection::LCIntersection (Bool takeOver, const PtrBlock& regions) : LCRegionMulti (takeOver, regions) { defineBox(); } LCIntersection::LCIntersection (const LCIntersection& other) : LCRegionMulti (other), itsOffsets (other.itsOffsets) {} LCIntersection::~LCIntersection() {} LCIntersection& LCIntersection::operator= (const LCIntersection& other) { if (this != &other) { LCRegionMulti::operator= (other); itsOffsets = other.itsOffsets; } return *this; } Bool LCIntersection::operator== (const LCRegion& other) const { return LCRegionMulti::operator== (other); } LCRegion* LCIntersection::cloneRegion() const { return new LCIntersection (*this); } LCRegion* LCIntersection::doTranslate (const Vector& translateVector, const IPosition& newLatticeShape) const { PtrBlock regions; multiTranslate (regions, translateVector, newLatticeShape); return new LCIntersection (True, regions); } String LCIntersection::className() { return "LCIntersection"; } String LCIntersection::type() const { return className(); } TableRecord LCIntersection::toRecord (const String& tableName) const { TableRecord rec; defineRecordFields (rec, className()); rec.defineRecord ("regions", makeRecord(tableName)); return rec; } LCIntersection* LCIntersection::fromRecord (const TableRecord& rec, const String& tableName) { PtrBlock regions; unmakeRecord (regions, rec.asRecord("regions"), tableName); return new LCIntersection (True, regions); } void LCIntersection::defineBox() { uInt i; // Get the intersection of blc and trc. const IPosition& shape = latticeShape(); uInt nrdim = shape.nelements(); IPosition blc(nrdim, 0); IPosition trc(shape - 1); uInt nr = regions().nelements(); itsOffsets.resize (nr, True); for (i=0; iboundingBox().start(); const IPosition& regtrc = regions()[i]->boundingBox().end(); for (uInt j=0; j trc(j)) { throw (AipsError ("LCIntersection::LCIntersection - " "regions do not overlap")); } if (regblc(j) > blc(j)) { blc(j) = regblc(j); } if (regtrc(j) < trc(j)) { trc(j) = regtrc(j); } } } // Set the bounding box in the parent object. setBoundingBox (Slicer(blc, trc, Slicer::endIsLast)); // Now determine where bounding box starts in constituting regions. itsOffsets.resize (nr); for (i=0; iboundingBox().start(); } // Fill the hasMask switch. fillHasMask(); } void LCIntersection::multiGetSlice (Array& buffer, const Slicer& section) { // Get the required part from the first region. // Note this getSlice version ensures that the result is not // referencing some internal Lattice array. Array tmp = regions()[0]->getSlice (Slicer(section.start()+itsOffsets[0], section.length(), section.stride())); buffer.reference(tmp); Bool deleteBuf, deleteTmp; Bool* buf = buffer.getStorage (deleteBuf); Bool* bufend = buf + section.length().product(); Array tmpbuf (buffer.shape()); uInt nr = regions().nelements(); for (uInt i=1; idoGetSlice (tmpbuf, Slicer(section.start()+itsOffsets[i], section.length(), section.stride())); const Bool* tmp = tmpbuf.getStorage (deleteTmp); const Bool* tmpptr = tmp; Bool* bufptr = buf; // Take the 'and' of all elements. while (bufptr < bufend) { if (*bufptr) { *bufptr = *tmpptr; } bufptr++; tmpptr++; } tmpbuf.freeStorage (tmp, deleteTmp); } buffer.putStorage (buf, deleteBuf); } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LRegions/LCIntersection.h000066400000000000000000000106401476623553700217150ustar00rootroot00000000000000//# LCIntersection.h: Make the intersection of 2 or more regions //# Copyright (C) 1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LCINTERSECTION_H #define LATTICES_LCINTERSECTION_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Make the intersection of 2 or more regions. // // // // // //
      • LCRegion // // // The LCIntersection class is a specialization of class // LCRegion. // It makes it possible to find the intersection of // given regions. //

        // The center of the intersection must be inside the lattice // // // // // // //

      • Expand along (slanted) cone lines // class LCIntersection: public LCRegionMulti { public: LCIntersection(); // Construct the intersection of the given regions. LCIntersection (const LCRegion& region1, const LCRegion& region2); // Construct from multiple regions. LCIntersection (Bool takeOver, const LCRegion* region1, const LCRegion* region2 = 0, const LCRegion* region3 = 0, const LCRegion* region4 = 0, const LCRegion* region5 = 0, const LCRegion* region6 = 0, const LCRegion* region7 = 0, const LCRegion* region8 = 0, const LCRegion* region9 = 0, const LCRegion* region10 = 0); // Construct from multiple regions given as a Block. // When takeOver is True, the destructor will delete the // given regions. Otherwise a copy of the regions is made. LCIntersection (Bool takeOver, const PtrBlock& regions); // Copy constructor (copy semantics). LCIntersection (const LCIntersection& other); virtual ~LCIntersection(); // Assignment (copy semantics). LCIntersection& operator= (const LCIntersection& other); // Comparison virtual Bool operator== (const LCRegion& other) const; // Make a copy of the derived object. virtual LCRegion* cloneRegion() const; // Get the class name (to store in the record). static String className(); // Get the region type. Returns className() virtual String type() const; // Convert the (derived) object to a record. virtual TableRecord toRecord (const String& tableName) const; // Convert correct object from a record. static LCIntersection* fromRecord (const TableRecord&, const String& tableName); protected: // Construct another LCRegion (for e.g. another lattice) by moving // this one. It recalculates the bounding box and mask. // A positive translation value indicates "to right". virtual LCRegion* doTranslate (const Vector& translateVector, const IPosition& newLatticeShape) const; // Do the actual getting of the mask. virtual void multiGetSlice (Array& buffer, const Slicer& section); private: // Make the bounding box and determine the offsets. void defineBox(); //# Define the offsets where to start reading from constituting regions. Block itsOffsets; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LRegions/LCLELMask.cc000066400000000000000000000066171476623553700206460ustar00rootroot00000000000000//# LCLELMask.cc: Class to define a mask as a LEL expression //# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LCLELMask::LCLELMask() {} LCLELMask::LCLELMask (const LatticeExpr& expr) : LCRegionSingle (expr.shape()), itsExpr (expr) { IPosition shp = expr.shape(); itsBox = LCBox(IPosition(shp.nelements(), 0), shp-1, shp); setBoundingBox (itsBox.boundingBox()); setMaskPtr (itsExpr); } LCLELMask::LCLELMask (const LCLELMask& that) : LCRegionSingle (that), itsBox (that.itsBox), itsExpr(that.itsExpr) { setMaskPtr (itsExpr); } LCLELMask::~LCLELMask() {} LCLELMask& LCLELMask::operator= (const LCLELMask& that) { if (this != &that) { LCRegionSingle::operator= (that); itsBox = that.itsBox; itsExpr = that.itsExpr; setMaskPtr (itsExpr); } return *this; } Bool LCLELMask::operator== (const LCRegion& that) const { // Check if parent class matches. // If so, we can safely cast. if (! LCRegionSingle::operator== (that)) { return False; } const LCLELMask& That = dynamic_cast(that); // Check the box and mask. return (itsBox == That.itsBox && masksEqual (That)); } LCRegion* LCLELMask::cloneRegion() const { return new LCLELMask(*this); } Bool LCLELMask::lock (FileLocker::LockType type, uInt nattempts) { return itsExpr.lock (type, nattempts); } void LCLELMask::unlock() { itsExpr.unlock(); } Bool LCLELMask::hasLock (FileLocker::LockType type) const { return itsExpr.hasLock (type); } void LCLELMask::resync() { itsExpr.resync(); } void LCLELMask::tempClose() { itsExpr.tempClose(); } void LCLELMask::reopen() { itsExpr.reopen(); } LCRegion* LCLELMask::doTranslate (const Vector&, const IPosition&) const { // An LCLELMask cannot be translated. throw (AipsError ("LCLELMask::translate is not supported")); return 0; } String LCLELMask::className() { return "LCLELMask"; } String LCLELMask::type() const { return className(); } TableRecord LCLELMask::toRecord (const String&) const { throw (AipsError ("LCLELMask::toRecord is not supported")); return TableRecord(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LRegions/LCLELMask.h000066400000000000000000000115201476623553700204750ustar00rootroot00000000000000//# LCLELMask.h: Class to define a mask as a LEL expression //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LCLELMASK_H #define LATTICES_LCLELMASK_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableRecord; class IPosition; // // Class to define a mask as a LEL expression // // // // // //
      • LCRegion //
      • ImageExpr // // // The LCLELMask class is a specialization of class // LCRegion. //
        // It can be used to define an on-the-fly mask for a lattice // using a boolean LatticeExpr. // The contents of the mask are calculated on the fly from the expression. // Thus the mask may change if the data in the lattice(s) used in the // expression change. // // This mask is not persistent, thus it cannot be saved with an image. // Use class WCLELMask to have a // persistent on-the-fly mask. It means that normally a WCLELMask should // be used (which gets converted to an LCLELMask when applied to an image). // //
        // // // // LCLELMask is needed to make // //# //#
      • //# class LCLELMask : public LCRegionSingle { public: LCLELMask(); // Construct from vectors of world coordinates // defining the box corners. It is assumed that the // order of the values is in the order of the pixel axes. explicit LCLELMask (const LatticeExpr& expr); // Copy constructor (copy semantics). LCLELMask (const LCLELMask& other); // Destructor virtual ~LCLELMask(); // Assignment (copy semantics) LCLELMask& operator= (const LCLELMask& other); // Comparison virtual Bool operator== (const LCRegion& other) const; // Clone a LCLELMask object. virtual LCRegion* cloneRegion() const; // Handle the (un)locking. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; // // Resynchronize the PagedArray object with the lattice file. // This function is only useful if no read-locking is used, ie. // if the table lock option is UserNoReadLocking or AutoNoReadLocking. // In that cases the table system does not acquire a read-lock, thus // does not synchronize itself automatically. virtual void resync(); // Temporarily close the lattice. // It will be reopened automatically on the next access. virtual void tempClose(); // Explicitly reopen the temporarily closed lattice. virtual void reopen(); // Returns LCLELMask static String className(); // Return region type. Returns the class name virtual String type() const; // Convert the LCLELMask object to a record. // This cannot be done as a Lattice expression cannot be made persistent // (only Image expressions can, thus only WCLELMask is persistent). //
        So this function throws an exception. virtual TableRecord toRecord (const String& tableName) const; protected: // Translating an LCLELMask is not possible, so it throws an exception. virtual LCRegion* doTranslate (const Vector& translateVector, const IPosition& newLatticeShape) const; private: LCBox itsBox; LatticeExpr itsExpr; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LRegions/LCMask.cc000066400000000000000000000135111476623553700203000ustar00rootroot00000000000000//# LCMask.cc: Class to define a rectangular mask of interest //# Copyright (C) 2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LCMask::LCMask() {} LCMask::LCMask (const IPosition& lattShape) : LCRegionSingle (lattShape), itsBox (IPosition(lattShape.nelements(), 0), lattShape-1, lattShape), itsMask (0) { setBoundingBox (itsBox.boundingBox()); itsMask = new TempLattice (lattShape); setMaskPtr (*itsMask); } LCMask::LCMask (const IPosition& maskShape, const LCBox& box) : LCRegionSingle (box.latticeShape()), itsBox (box), itsMask (0) { // Check if box shape and mask shape are equal. if (itsBox.shape() != maskShape) { throw (AipsError ("LCMask::LCMask- " "shape of mask and box differ")); } setBoundingBox (itsBox.boundingBox()); itsMask = new TempLattice (maskShape); setMaskPtr (*itsMask); } LCMask::LCMask (Lattice& mask) : LCRegionSingle (mask.shape()), itsBox (IPosition(mask.shape().nelements(), 0), mask.shape()-1, mask.shape()), itsMask (0) { setBoundingBox (itsBox.boundingBox()); itsMask = mask.clone(); setMaskPtr (*itsMask); } LCMask::LCMask (Lattice& mask, const LCBox& box) : LCRegionSingle (box.latticeShape()), itsBox (box) { // Check if box shape and mask shape are equal. if (itsBox.shape() != mask.shape()) { throw (AipsError ("LCMask::LCMask- " "shape of mask and box differ")); } setBoundingBox (itsBox.boundingBox()); itsMask = mask.clone(); setMaskPtr (*itsMask); } LCMask::LCMask (const LCMask& other) : LCRegionSingle (other), itsBox (other.itsBox), itsMask (0) { itsMask = other.itsMask->clone(); setMaskPtr (*itsMask); } LCMask::~LCMask() { delete itsMask; } LCMask& LCMask::operator= (const LCMask& that) { if (this != &that) { LCRegionSingle::operator= (that); itsBox = that.itsBox; delete itsMask; itsMask = 0; itsMask = that.itsMask->clone(); setMaskPtr (*itsMask); } return *this; } Bool LCMask::operator== (const LCRegion& other) const { // Check if parent class matches. // If so, we can safely cast. if (! LCRegionSingle::operator== (other)) { return False; } const LCMask& that = (const LCMask&)other; // Check the box and mask. return (itsBox == that.itsBox && masksEqual (that)); } LCRegion* LCMask::cloneRegion() const { return new LCMask(*this); } uInt LCMask::advisedMaxPixels() const { return itsMask->advisedMaxPixels(); } IPosition LCMask::doNiceCursorShape (uInt maxPixels) const { return itsMask->niceCursorShape (maxPixels); } uInt LCMask::maximumCacheSize() const { return itsMask->maximumCacheSize(); } void LCMask::setMaximumCacheSize (uInt howManyPixels) { itsMask->setMaximumCacheSize (howManyPixels); } void LCMask::setCacheSizeFromPath (const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath) { itsMask->setCacheSizeFromPath (sliceShape, windowStart, windowLength, axisPath); } void LCMask::setCacheSizeInTiles (uInt howManyTiles) { itsMask->setCacheSizeInTiles (howManyTiles); } void LCMask::clearCache() { itsMask->clearCache(); } void LCMask::showCacheStatistics (ostream& os) const { itsMask->showCacheStatistics (os); } LatticeIterInterface* LCMask::makeIter (const LatticeNavigator& navigator, Bool useRef) const { return itsMask->makeIter (navigator, useRef); } Bool LCMask::lock (FileLocker::LockType type, uInt nattempts) { // Lock the PagedArray containing the mask. return itsMask->lock (type, nattempts); } void LCMask::unlock() { // Unlock the PagedArray containing the mask. itsMask->unlock(); } Bool LCMask::hasLock (FileLocker::LockType type) const { return itsMask->hasLock (type); } void LCMask::resync() { itsMask->resync(); } void LCMask::flush() { itsMask->flush(); } void LCMask::tempClose() { itsMask->tempClose(); } void LCMask::reopen() { itsMask->reopen(); } LCRegion* LCMask::doTranslate (const Vector&, const IPosition&) const { // An LCMask cannot be translated. throw (AipsError ("LCMask::translate is not supported")); return 0; } String LCMask::className() { return "LCMask"; } String LCMask::type() const { return className(); } TableRecord LCMask::toRecord (const String&) const { throw AipsError ("LCMask::toRecord is not supported"); return TableRecord();; } Bool LCMask::isWritable() const { return itsMask->isWritable(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LRegions/LCMask.h000066400000000000000000000155301476623553700201450ustar00rootroot00000000000000//# LCMask.h: Class to define a rectangular mask as a temporary region //# Copyright (C) 2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LCMASK_H #define LATTICES_LCMASK_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Class to define a rectangular mask as a temporary region // // // // // //
      • LCRegion // // // The LCMask class is a specialization of class // LCRegion. //
        // It can be used to define a temporary mask (e.g. for a // TempImage). // It is possible to define the mask for the full lattice, but one // can also define it for part of a lattice. In the latter case a // LCBox has to be given as well to // define for which part of the image the mask has to be used. //
        // // // // // // class LCMask: public LCRegionSingle { public: LCMask(); // Construct an LCMask object for a full lattice with the given shape. // It creates a TempLattice to hold the mask. explicit LCMask (const IPosition& latticeShape); // Construct an LCMask object for a full lattice with the shape of the mask. // It clones the mask object. explicit LCMask (Lattice& mask); // Construct an LCMask object for the part of a lattice given by the box. // The box defines the position of the mask in the lattice. // The box shape and given mask shape should be equal. // It creates a TempImage to hold the mask. LCMask (const IPosition& maskShape, const LCBox& box); // Construct an LCMask object for the part of a lattice given by the box. // The box defines the position of the mask in the lattice. // The box shape and given mask shape should be equal. // It clones the mask object. LCMask (Lattice& mask, const LCBox& box); // Copy constructor (copy semantics). LCMask (const LCMask& other); // Destructor virtual ~LCMask(); // Assignment (reference semantics). LCMask& operator= (const LCMask& other); // Comparison virtual Bool operator==(const LCRegion& other) const; // Make a copy of the derived object. virtual LCRegion* cloneRegion() const; // This function is used by the LatticeIterator class to generate an // iterator of the correct type for this Lattice. Not recommended // for general use. virtual LatticeIterInterface* makeIter (const LatticeNavigator& navigator, Bool useRef) const; // Returns the maximum recommended number of pixels for a cursor. // This is the number of pixels in a tile. virtual uInt advisedMaxPixels() const; // Help the user pick a cursor for most efficient access. virtual IPosition doNiceCursorShape (uInt maxPixels) const; // Maximum size - not necessarily all used. In pixels. virtual uInt maximumCacheSize() const; // Set the maximum (allowed) cache size as indicated. virtual void setMaximumCacheSize (uInt howManyPixels); // Set the cache size as to "fit" the indicated path. virtual void setCacheSizeFromPath (const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath); // Set the actual cache size for this Array to be be big enough for the // indicated number of tiles. This cache is not shared with PagedArrays // in other rows and is always clipped to be less than the maximum value // set using the setMaximumCacheSize member function. // tiles. Tiles are cached using a first in first out algorithm. virtual void setCacheSizeInTiles (uInt howManyTiles); // Clears and frees up the caches, but the maximum allowed cache size is // unchanged from when setCacheSize was called virtual void clearCache(); // Report on cache success. virtual void showCacheStatistics (ostream& os) const; // Handle the (un)locking. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; // // Resynchronize the object with the contenta tof the possible file. // This function is only useful if no read-locking is used, ie. // if the table lock option is UserNoReadLocking or AutoNoReadLocking. // In that cases the table system does not acquire a read-lock, thus // does not synchronize itself automatically. virtual void resync(); // Flush the data (but do not unlock). virtual void flush(); // Temporarily close the lattice. // It will be reopened automatically on the next access. virtual void tempClose(); // Explicitly reopen the temporarily closed lattice. virtual void reopen(); // Get the class name (to store in the record). static String className(); // Region type. Returns class name. virtual String type() const; // Convert the (derived) object to a record. // This cannot be done and results in an exception. virtual TableRecord toRecord (const String& tableName) const; // An LCMask is writable if the underlying Lattice is. virtual Bool isWritable() const; protected: // Construct another LCMask (for e.g. another lattice) by moving // this one. It recalculates the bounding mask. // A positive translation value indicates "to right". virtual LCRegion* doTranslate (const Vector& translateVector, const IPosition& newLatticeShape) const; private: LCBox itsBox; Lattice* itsMask; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LRegions/LCPagedMask.cc000066400000000000000000000164651476623553700212540ustar00rootroot00000000000000//# LCPagedMask.cc: Class to define a rectangular mask of interest //# Copyright (C) 1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LCPagedMask::LCPagedMask() {} LCPagedMask::LCPagedMask (const TiledShape& latticShape, const String& tableName) : LCRegionSingle (latticShape.shape()), itsBox (IPosition(latticShape.shape().nelements(), 0), latticShape.shape()-1, latticShape.shape()) { setBoundingBox (itsBox.boundingBox()); itsMask = PagedArray (latticShape, tableName); setMaskPtr (itsMask); } LCPagedMask::LCPagedMask (const TiledShape& maskShape, const LCBox& box, const String& tableName) : LCRegionSingle (box.latticeShape()), itsBox (box) { // Check if box shape and mask shape are equal. if (itsBox.shape() != maskShape.shape()) { throw (AipsError ("LCPagedMask::LCPagedMask- " "shape of mask and box differ")); } setBoundingBox (itsBox.boundingBox()); itsMask = PagedArray (maskShape, tableName); setMaskPtr (itsMask); } LCPagedMask::LCPagedMask (PagedArray& mask, const LCBox& box) : LCRegionSingle (box.latticeShape()), itsBox (box) { // Check if box shape and mask shape are equal. if (itsBox.shape() != mask.shape()) { throw (AipsError ("LCPagedMask::LCPagedMask- " "shape of mask and box differ")); } setBoundingBox (itsBox.boundingBox()); itsMask = mask; setMaskPtr (itsMask); } LCPagedMask::LCPagedMask (const LCPagedMask& other) : LCRegionSingle (other), itsBox (other.itsBox), itsMask(other.itsMask) { setMaskPtr (itsMask); } LCPagedMask::~LCPagedMask() {} LCPagedMask& LCPagedMask::operator= (const LCPagedMask& that) { if (this != &that) { LCRegionSingle::operator= (that); itsBox = that.itsBox; itsMask = that.itsMask; setMaskPtr (itsMask); } return *this; } Bool LCPagedMask::operator== (const LCRegion& other) const { // Check if parent class matches. // If so, we can safely cast. if (! LCRegionSingle::operator== (other)) { return False; } const LCPagedMask& that = (const LCPagedMask&)other; // Check the box and mask. return (itsBox == that.itsBox && masksEqual (that)); } LCRegion* LCPagedMask::cloneRegion() const { return new LCPagedMask(*this); } uInt LCPagedMask::advisedMaxPixels() const { return itsMask.advisedMaxPixels(); } IPosition LCPagedMask::doNiceCursorShape (uInt maxPixels) const { return itsMask.niceCursorShape (maxPixels); } uInt LCPagedMask::maximumCacheSize() const { return itsMask.maximumCacheSize(); } void LCPagedMask::setMaximumCacheSize (uInt howManyPixels) { itsMask.setMaximumCacheSize (howManyPixels); } void LCPagedMask::setCacheSizeFromPath (const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath) { itsMask.setCacheSizeFromPath (sliceShape, windowStart, windowLength, axisPath); } void LCPagedMask::setCacheSizeInTiles (uInt howManyTiles) { itsMask.setCacheSizeInTiles (howManyTiles); } void LCPagedMask::clearCache() { itsMask.clearCache(); } void LCPagedMask::showCacheStatistics (ostream& os) const { itsMask.showCacheStatistics (os); } LatticeIterInterface* LCPagedMask::makeIter (const LatticeNavigator& navigator, Bool useRef) const { return itsMask.makeIter (navigator, useRef); } void LCPagedMask::handleDelete() { // Test if the table can be deleted (i.e. is not used elsewhere). Table& tab(itsMask.table()); if (tab.isMultiUsed (True)) { throw (AipsError("Cannot delete the mask (used in another process)")); } // Mark the table for delete, so the destructor will delete it. tab.markForDelete(); } void LCPagedMask::handleRename (const String& newName, Bool overwrite) { // Rename the underlying table. // Make sure the directory does not change. Table tab(itsMask.tableName(), Table::Update); String newnm = Path(tab.tableName()).dirName() + '/' + newName; if (overwrite) { tab.rename (newnm, Table::New); } else { tab.rename (newnm, Table::NewNoReplace); } } Bool LCPagedMask::lock (FileLocker::LockType type, uInt nattempts) { // Llock the PagedArray containing the mask. return itsMask.lock (type, nattempts); } void LCPagedMask::unlock() { // Unlock the PagedArray containing the mask. itsMask.unlock(); } Bool LCPagedMask::hasLock (FileLocker::LockType type) const { return itsMask.hasLock (type); } void LCPagedMask::resync() { itsMask.resync(); } void LCPagedMask::flush() { itsMask.flush(); } void LCPagedMask::tempClose() { itsMask.tempClose(); } void LCPagedMask::reopen() { itsMask.reopen(); } LCRegion* LCPagedMask::doTranslate (const Vector&, const IPosition&) const { // An LCPagedMask cannot be translated. throw (AipsError ("LCPagedMask::translate is not supported")); return 0; } String LCPagedMask::className() { return "LCPagedMask"; } String LCPagedMask::type() const { return className(); } TableRecord LCPagedMask::toRecord (const String& tableName) const { TableRecord rec; defineRecordFields (rec, className()); rec.defineTable ("mask", itsMask.table()); rec.defineRecord ("box", itsBox.toRecord (tableName)); return rec; } LCPagedMask* LCPagedMask::fromRecord (const TableRecord& rec, const String& tableName) { TableLock lockOptions(TableLock::AutoNoReadLocking); if (rec.tableAttributes("mask").lockOptions().readLocking()) { lockOptions = TableLock::AutoLocking; } Table table (rec.asTable ("mask", lockOptions)); PagedArray mask(table); LCBox* boxPtr = (LCBox*)(LCRegion::fromRecord (rec.asRecord("box"), tableName)); LCPagedMask* regPtr = new LCPagedMask (mask, *boxPtr); delete boxPtr; return regPtr; } Bool LCPagedMask::isWritable() const { return itsMask.isWritable(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LRegions/LCPagedMask.h000066400000000000000000000153751476623553700211150ustar00rootroot00000000000000//# LCPagedMask.h: Class to define a rectangular mask as a region //# Copyright (C) 1998,1999,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LCPAGEDMASK_H #define LATTICES_LCPAGEDMASK_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Class to define a rectangular mask as a region // // // // // //
      • LCRegion // // // The LCPagedMask class is a specialization of class // LCRegion. // // // // // // // class LCPagedMask: public LCRegionSingle { public: LCPagedMask(); // Construct a PagedMask object for (part of) a lattice. // The box defines the position of the mask. // The default mask shape is the lattice shape. // LCPagedMask (const TiledShape& latticeShape, const String& tableName); LCPagedMask (const TiledShape& maskShape, const LCBox& box, const String& tableName); LCPagedMask (PagedArray& mask, const LCBox& box); // // Copy constructor (copy semantics). LCPagedMask (const LCPagedMask& other); // Destructor virtual ~LCPagedMask(); // Assignment (reference semantics). LCPagedMask& operator= (const LCPagedMask& other); // Comparison virtual Bool operator==(const LCRegion& other) const; // Make a copy of the derived object. virtual LCRegion* cloneRegion() const; // This function is used by the LatticeIterator class to generate an // iterator of the correct type for this Lattice. Not recommended // for general use. virtual LatticeIterInterface* makeIter (const LatticeNavigator& navigator, Bool useRef) const; // Returns the maximum recommended number of pixels for a cursor. // This is the number of pixels in a tile. virtual uInt advisedMaxPixels() const; // Help the user pick a cursor for most efficient access. virtual IPosition doNiceCursorShape (uInt maxPixels) const; // Maximum size - not necessarily all used. In pixels. virtual uInt maximumCacheSize() const; // Set the maximum (allowed) cache size as indicated. virtual void setMaximumCacheSize (uInt howManyPixels); // Set the cache size as to "fit" the indicated path. virtual void setCacheSizeFromPath (const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath); // Set the actual cache size for this Array to be be big enough for the // indicated number of tiles. This cache is not shared with PagedArrays // in other rows and is always clipped to be less than the maximum value // set using the setMaximumCacheSize member function. // tiles. Tiles are cached using a first in first out algorithm. virtual void setCacheSizeInTiles (uInt howManyTiles); // Clears and frees up the caches, but the maximum allowed cache size is // unchanged from when setCacheSize was called virtual void clearCache(); // Report on cache success. virtual void showCacheStatistics (ostream& os) const; // Handle deletion of the region by deleting the associated table. virtual void handleDelete(); // Handle renaming the region by renaming the associated table. // If overwrite=False, an exception will be thrown if a table with the // new name already exists. virtual void handleRename (const String& newName, Bool overwrite); // Handle the (un)locking. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; // // Resynchronize the PagedArray object with the lattice file. // This function is only useful if no read-locking is used, ie. // if the table lock option is UserNoReadLocking or AutoNoReadLocking. // In that cases the table system does not acquire a read-lock, thus // does not synchronize itself automatically. virtual void resync(); // Flush the data (but do not unlock). virtual void flush(); // Temporarily close the lattice. // It will be reopened automatically on the next access. virtual void tempClose(); // Explicitly reopen the temporarily closed lattice. virtual void reopen(); // Get the class name (to store in the record). static String className(); // Region type. Returns class name. virtual String type() const; // Convert the (derived) object to a record. virtual TableRecord toRecord (const String& tableName) const; // Convert correct object from a record. static LCPagedMask* fromRecord (const TableRecord&, const String& tablename); // An LCPagedMask is writable if the underlying PagedArray is. virtual Bool isWritable() const; protected: // Construct another LCPagedMask (for e.g. another lattice) by moving // this one. It recalculates the bounding mask. // A positive translation value indicates "to right". virtual LCRegion* doTranslate (const Vector& translateVector, const IPosition& newLatticeShape) const; private: // Create the object from a record (for an existing mask). LCPagedMask (PagedArray& mask, const IPosition& blc, const IPosition& latticeShape); LCBox itsBox; PagedArray itsMask; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LRegions/LCPixelSet.cc000066400000000000000000000071161476623553700211460ustar00rootroot00000000000000//# LCPixelSet.cc: Class to define a rectangular mask of interest //# Copyright (C) 1998,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LCPixelSet::LCPixelSet() {} LCPixelSet::LCPixelSet (const Array& mask, const LCBox& box) : LCRegionFixed (box.latticeShape()), itsBox (box) { if (! mask.shape().isEqual (itsBox.shape())) { throw (AipsError ("LCPixelSet::LCPixelSet - " "shape of mask and box differ")); } setBoundingBox (itsBox.boundingBox()); setMask (mask); } LCPixelSet::LCPixelSet (const LCPixelSet& that) : LCRegionFixed (that), itsBox (that.itsBox) {} LCPixelSet::~LCPixelSet() {} LCPixelSet& LCPixelSet::operator= (const LCPixelSet& that) { if (this != &that) { LCRegionFixed::operator= (that); itsBox = that.itsBox; } return *this; } Bool LCPixelSet::operator== (const LCRegion& other) const { // Check if parent class matches. // If so, we can safely cast. if (! LCRegionFixed::operator== (other)) { return False; } const LCPixelSet& that = (const LCPixelSet&)other; // Check the box and mask. return (itsBox == that.itsBox && masksEqual (that)); } LCRegion* LCPixelSet::cloneRegion() const { return new LCPixelSet(*this); } LCRegion* LCPixelSet::doTranslate (const Vector& translateVector, const IPosition& newLatticeShape) const { LCBox* boxPtr = (LCBox*)(itsBox.translate (translateVector, newLatticeShape)); LCPixelSet* regPtr = new LCPixelSet (maskArray(), *boxPtr); delete boxPtr; return regPtr; } String LCPixelSet::className() { return "LCPixelSet"; } String LCPixelSet::type() const { return className(); } TableRecord LCPixelSet::toRecord (const String& tableName) const { TableRecord rec; defineRecordFields (rec, className()); rec.define ("mask", maskArray()); rec.defineRecord ("box", itsBox.toRecord (tableName)); return rec; } LCPixelSet* LCPixelSet::fromRecord (const TableRecord& rec, const String& tableName) { LCBox* boxPtr = (LCBox*)(LCRegion::fromRecord (rec.asRecord("box"), tableName)); LCPixelSet* regPtr = new LCPixelSet (rec.toArrayBool ("mask"), *boxPtr); delete boxPtr; return regPtr; } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LRegions/LCPixelSet.h000066400000000000000000000066221476623553700210110ustar00rootroot00000000000000//# LCPixelSet.h: Class to define a rectangular set of pixels as a region //# Copyright (C) 1998,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LCPIXELSET_H #define LATTICES_LCPIXELSET_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Class to define a rectangular mask as a region // // // // // //
      • LCRegion // // // The LCPixelSet class is a specialization of class // LCRegion. // It makes it possible to define a rectangular region of interest. // // // // // // // class LCPixelSet: public LCRegionFixed { public: LCPixelSet(); // Construct from the box defining the position of the mask. // The shape of the region and mask must be the same. LCPixelSet (const Array& mask, const LCBox& region); // Copy constructor (copy semantics). LCPixelSet (const LCPixelSet& other); virtual ~LCPixelSet(); // Assignment (copy semantics). LCPixelSet& operator= (const LCPixelSet& other); // Comparison virtual Bool operator== (const LCRegion& other) const; // Make a copy of the derived object. virtual LCRegion* cloneRegion() const; // Get the class name (to store in the record). static String className(); // Get the region type. Returns className(). virtual String type() const; // Convert the (derived) object to a record. virtual TableRecord toRecord (const String& tableName) const; // Convert correct object from a record. static LCPixelSet* fromRecord (const TableRecord&, const String& tablename); protected: // Construct another LCPixelSet (for e.g. another lattice) by moving // this one. It recalculates the bounding mask. // A positive translation value indicates "to right". virtual LCRegion* doTranslate (const Vector& translateVector, const IPosition& newLatticeShape) const; private: LCBox itsBox; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LRegions/LCPolygon.cc000066400000000000000000000306251476623553700210410ustar00rootroot00000000000000//# LCPolygon.cc: Define a 2-dimensional region by a polygon //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LCPolygon::LCPolygon() {} LCPolygon::LCPolygon (const Vector& x, const Vector& y, const IPosition& latticeShape) : LCRegionFixed (latticeShape), itsX (x.copy()), itsY (y.copy()) { defineBox(); defineMask(); } LCPolygon::LCPolygon (const Vector& x, const Vector& y, const IPosition& latticeShape) : LCRegionFixed (latticeShape), itsX (x.nelements()), itsY (y.nelements()) { for (uInt i=0; i& translateVector, const IPosition& newLatticeShape) const { Vector x, y; x = itsX; y = itsY; uInt n = x.nelements(); for (uInt i=0; i x (rec.toArrayFloat ("x")); Array y (rec.toArrayFloat ("y")); return new LCPolygon (x-off, y-off, Vector(rec.toArrayInt ("shape"))); } // Truncate start such that edge is taken if very close to a pixel point. // Take care of fact that Float is not always exact. Int LCPolygon::truncateStart (Float v) { Int res; Float vt = floor(v+0.1); if (_isNear(vt, v)) { res = static_cast(v+0.1); } else { res = static_cast(v+1); } return std::max (res, 0); } Bool LCPolygon::_isNear(Float val1, Float val2) { return val1 == 0 || val2 == 0 ? nearAbs(val1, val2) : near(val1, val2); } // Truncate end such that edge is taken if very close to a pixel point. Int LCPolygon::truncateEnd (Float v, Int maxEnd) { Int res; Float vt = floor(v+0.1); if (_isNear(vt, v)) { res = static_cast(v+0.1); } else { res = static_cast(v); } return std::min (res, maxEnd); } void LCPolygon::defineBox() { const IPosition& shape = latticeShape(); uInt i; uInt nrp = itsX.nelements(); // First make sure basic things are right. if (itsY.nelements() != nrp) { throw AipsError ("LCPolygon - x and y vectors must have equal length"); } if (shape.nelements() != 2) { throw AipsError ("LCPolygon - can only be used as a 2-dim region"); } // If the last point is not equal to the first one, add it. if (!_isNear (itsX[nrp-1], itsX[0]) || !_isNear(itsY[nrp-1], itsY[0])) { itsX.resize (nrp+1, True); itsY.resize (nrp+1, True); nrp++; } itsX[nrp-1] = itsX[0]; // Make sure they are always equal. itsY[nrp-1] = itsY[0]; if (nrp < 3) { throw AipsError ("LCPolygon - " "at least 3 different points have to be specified"); } // Determine the maximum and minimum x,y. // They form the bounding box. // Check if at least one point is inside lattice. Float minx = itsX[0]; Float maxx = itsX[0]; Float miny = itsY[0]; Float maxy = itsY[0]; for (i=1; i maxx) maxx = itsX[i]; if (itsY[i] < miny) miny = itsY[i]; if (itsY[i] > maxy) maxy = itsY[i]; } // Get boundingbox; truncate values in the right way. IPosition blc(2, 0); IPosition trc(shape-1); blc[0] = truncateStart(minx); blc[1] = truncateStart(miny); trc[0] = truncateEnd(maxx, shape[0]-1); trc[1] = truncateEnd(maxy, shape[1]-1); if (trc[0] < blc[0] || trc[1] < blc[1]) { throw AipsError ("LCPolygon - entire polygon is outside the lattice"); } setBoundingBox (Slicer(blc, trc, Slicer::endIsLast)); } void LCPolygon::defineMask() { // Create and initialize the mask. IPosition blc = boundingBox().start(); const IPosition& shape = boundingBox().length(); Matrix mask(shape); mask = False; uInt nrline = itsX.nelements() - 1; Bool delX, delY, delM; const Float* ptrX = itsX.getStorage (delX); const Float* ptrY = itsY.getStorage (delY); Bool* ptrM = mask.getStorage (delM); // Fill the mask. // Note that fillMask is now called such that the outer loop is over y. // This is usually most efficient; however if ny>>nx it might be // more efficient to call fillMask with x and y swapped. That also // means that the mask should be transposed after fillMask. fillMask (ptrM, shape[1], shape[0], blc[1], blc[0], ptrY, ptrX, nrline); itsX.freeStorage (ptrX, delX); itsY.freeStorage (ptrY, delY); mask.putStorage (ptrM, delM); // Test if rows/columns at the edges are all False. // If so, remove them and adjust the bounding box. Int stx = 0; Int sty = 0; Int endx = shape[0]; Int endy = shape[1]; for (; stxstx && allEQ(mask.row(endx-1), False); --endx) {} for (; stysty && allEQ(mask.column(endy-1), False); --endy) {} if (stx>0 || sty>0 || endx= endx || sty >= endy) { throw AipsError ("LCPolygon - polygon does not contain any pixel"); } Matrix mask2; mask2 = mask(Slice(stx,endx-stx), Slice(sty,endy-sty)); mask.reference (mask2); blc[0] += stx; blc[1] += sty; setBoundingBox (Slicer(blc, mask.shape())); } setMask (mask); } void LCPolygon::fillMask (Bool* mask, Int ny, Int nx, Int blcy, Int blcx, const Float* ptrY, const Float* ptrX, uInt nrline) { uInt i; Block a(nrline); Block b(nrline); Block dir(nrline, -1); // -1=same dir, 0=vertical, 1=change dir // Fill the mask for all vertical lines. // Also determine the index of the last non-vertical line, which // is used in the slope-calculation loop. Int prev = -1; for (i=0; i(ptrY[i]); if (y >= blcy && y < ny+blcy && _isNear(Float(y), ptrY[i])) { Int xs, xe; if (ptrX[i] < ptrX[i+1]) { xs = truncateStart (ptrX[i] - blcx); xe = truncateEnd (ptrX[i+1] - blcx, nx-1); } else { xs = truncateStart (ptrX[i+1] - blcx); xe = truncateEnd (ptrX[i] - blcx, nx-1); } Bool* maskPtr = mask + (y-blcy)*nx; while (xs <= xe) { maskPtr[xs++] = True; } } } else { prev = i; } } // Calculate the slope (a) and offset (b) for all line segments. // Vertical line segments are ignored for this. // Determine if a line segment changes direction with respect to // the last non-vertical line segment. for (i=0; i ptrY[i+1] && ptrY[prev] < ptrY[prev+1]) || (ptrY[i] < ptrY[i+1] && ptrY[prev] > ptrY[prev+1])) { dir[i] = 1; } prev = i; } } // Loop through all y-es and mask the x-points inside or on the polygon. // This is done by determining the crossing point of all the y-lines // with all line segments (the in/out algorithm). Block cross(nrline); Bool* maskPtr = mask; for (Int y=0; y ptrY[i] && yf < ptrY[i+1]) || (yf < ptrY[i] && yf > ptrY[i+1]) || _isNear(yf, ptrY[i]) || _isNear(yf, ptrY[i+1])) { Float cr = a[i] * yf + b[i] - blcx; take = True; // If a polygon point is a pixel point (in y), always // count the ending point of the line segment. // Do not count the starting point if the direction has // not changed with respect to the previous non-vertical // line segment. if (_isNear (yf, ptrY[i])) { if (dir[i] != 1) { take = False; } } if (take) { cross[nrcross++] = cr; } } } } DebugAssert (nrcross >= 2, AipsError); DebugAssert (nrcross%2 == 0, AipsError); GenSort::sort (cross, nrcross); for (i=0; i #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Define a 2-dimensional region by a polygon. // // // // // //
      • LCRegion // // // The LCPolygon class is a specialization of class // LCRegion. // It makes it possible to define a 2-dimensional region by means // an ordered collection of points with straight lines connecting // adjacent points. The last point can be equal to the first one. // If not, an extra point gets added to form the closing line. //

        // The polygon can be as complex as one likes. E.g. it is possible to // have a rectangle with an inner rectangle to exclude interior points. //

        // The points defining the polygon do not need to coincide with pixel points. // Points may be outside the lattice meaning that only part of the // polygon surface is actually used. However, at least some part of the // polygon surface has to intersect with the lattice. //
        A lattice pixel is part of the polygon surface if the center of // the pixel is on or inside the polygon. Note that 0 is the beginning ond // 1 is the end of the first pixel. Thus 0.5 is its center. // // // // // A simple (tilted) square. // Vector x(4), y(4); // x(0)=3; y(0)=3; // x(1)=6; y(1)=6; // x(2)=3; y(2)=9; // x(3)=0; y(3)=6; // LCPolygon region(x, y, IPosition(2,128,128)); // // // A rectangle with an inner region to exclude interior points. // // Note that the last point is equal to the first point, thus // // the last line is given explicitly. // Vector x(11), y(11); // x(0)=3; y(0)=3; // x(1)=9; y(1)=3; // x(2)=9; y(2)=8; // x(3)=3; y(3)=8; // x(4)=3; y(4)=3; // x(5)=5; y(5)=5; // x(6)=8; y(6)=4; // x(7)=7; y(7)=7; // x(8)=5; y(8)=7; // x(9)=5; y(9)=5; // x(10)=3; y(10)=3; // LCPolygon region(x, y, IPosition(2,128,128)); // // //# //#

      • //# class LCPolygon: public LCRegionFixed { public: LCPolygon(); // Construct from the given x and y values. // The latticeShape must define a 2-dimensional lattice. //
        LCPolygon can be used for an N-dimensional lattice by making // another lattice representing any 2 axes from the original lattice. // LCPolygon (const Vector& x, const Vector& y, const IPosition& latticeShape); LCPolygon (const Vector& x, const Vector& y, const IPosition& latticeShape); // // Copy constructor (reference semantics). LCPolygon (const LCPolygon& other); virtual ~LCPolygon(); // Assignment (copy semantics). LCPolygon& operator= (const LCPolygon& other); // Comparison virtual Bool operator== (const LCRegion& other) const; // Make a copy of the derived object. virtual LCRegion* cloneRegion() const; // Get the X-values. const Vector& x() const; // Get the Y-values. const Vector& y() const; // Get the class name (to store in the record). static String className(); // Get the region type. Returns className() virtual String type() const; // Convert the (derived) object to a record. virtual TableRecord toRecord (const String& tableName) const; // Convert correct object from a record. static LCPolygon* fromRecord (const TableRecord&, const String& tablename); protected: // Construct another LCPolygon (for e.g. another lattice) by moving // this one. It recalculates the bounding box. // A positive translation value indicates "to right". virtual LCRegion* doTranslate (const Vector& translateVector, const IPosition& newLatticeShape) const; private: // Make the bounding box. void defineBox(); // Define the mask to indicate which elements are inside the polygon. void defineMask(); // Fill the mask from the given points. void fillMask (Bool* mask, Int nx, Int ny, Int blcx, Int blcy, const Float* ptrX, const Float* ptrY, uInt nrline); // Truncate a start value to a pixel point. // A pixel point is taken if near the value, otherwise floor(value+1). // The returned value is never < 0. Int truncateStart (Float v); // Truncate an end value to a pixel point. // A pixel point is taken if near the value, otherwise floor(value). // The returned value is never > maxEnd. Int truncateEnd (Float v, Int maxEnd); // takes into account when one value is zero and the other is absolutely (as // opposed to relatively) near zero. static Bool _isNear(Float val1, Float val2); Vector itsX; Vector itsY; }; inline const Vector& LCPolygon::x() const { return itsX; } inline const Vector& LCPolygon::y() const { return itsY; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LRegions/LCRegion.cc000066400000000000000000000141411476623553700206300ustar00rootroot00000000000000//# LCRegion.cc: Abstract base class to define a region of interest //# Copyright (C) 1997,1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LCRegion::LCRegion() {} LCRegion::LCRegion (const IPosition& latticeShape) : itsShape (latticeShape) {} LCRegion::LCRegion (const LCRegion& other) : Lattice(), itsShape (other.itsShape), itsBoundingBox (other.itsBoundingBox), itsComment (other.itsComment) {} LCRegion& LCRegion::operator= (const LCRegion& other) { if (this != &other) { itsShape.resize (other.itsShape.nelements()); itsShape = other.itsShape; itsBoundingBox = other.itsBoundingBox; itsComment = other.itsComment; } return *this; } Bool LCRegion::operator== (const LCRegion& other) const { // Type check. if (type() != other.type()) { return False; } // Compare bounding boxes, which also takes care of dimensionality. if (! itsBoundingBox.length().isEqual (other.itsBoundingBox.length()) || ! itsBoundingBox.start().isEqual (other.itsBoundingBox.start())) { return False; } return itsShape.isEqual (other.itsShape); } LCRegion::~LCRegion() {} Lattice* LCRegion::clone() const { return cloneRegion(); } void LCRegion::handleDelete() {} void LCRegion::handleRename (const String&, Bool) {} LCRegion* LCRegion::translate (const IPosition& translateVector, const IPosition& newLatticeShape) const { uInt nr = translateVector.nelements(); Vector vec (nr); for (uInt i=0; i& translateVector, const IPosition& newLatticeShape) const { if (translateVector.nelements() != newLatticeShape.nelements()) { throw (AipsError ("LCRegion::translate - " "translateVector and newLatticeShape vectors " "do not have same length")); } if (newLatticeShape.nelements() < latticeShape().nelements()) { throw (AipsError ("LCRegion::translate - " "length of newLatticeShape vector less than " "dimensionality of region")); } return doTranslate (translateVector, newLatticeShape); } void LCRegion::setBoundingBox (const Slicer& box) { IPosition blc, trc, inc; box.inferShapeFromSource (itsShape, blc, trc, inc); itsBoundingBox = Slicer (blc, trc, inc, Slicer::endIsLast); } void LCRegion::setShapeAndBoundingBox (const IPosition& latticeShape, const Slicer& boundingBox) { AlwaysAssert (latticeShape.nelements() == boundingBox.ndim(), AipsError); itsShape.resize (latticeShape.nelements()); itsShape = latticeShape; setBoundingBox (boundingBox); } Slicer LCRegion::expand (const Slicer& slicer) const { IPosition blc, trc, inc; IPosition shape = slicer.inferShapeFromSource (itsBoundingBox.length(), blc, trc, inc); const IPosition& start = itsBoundingBox.start(); uInt ndim = itsShape.nelements(); for (uInt i=0; i&, const IPosition&, const IPosition&) { throw (AipsError ("LCRegion::putSlice is not possible")); } void LCRegion::set (const Bool&) { throw (AipsError ("LCRegion: set is not possible")); } void LCRegion::apply (Bool (*)(Bool)) { throw (AipsError ("LCRegion: apply is not possible")); } void LCRegion::apply (Bool (*)(const Bool&)) { throw (AipsError ("LCRegion: apply is not possible")); } void LCRegion::apply (const Functional&) { throw (AipsError ("LCRegion: apply is not possible")); } void LCRegion::putAt (const Bool&, const IPosition&) { throw (AipsError ("LCRegion: putAt is not possible")); } void LCRegion::copyData (const Lattice&) { throw (AipsError ("LCRegion: copyData is not possible")); } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LRegions/LCRegion.h000066400000000000000000000216031476623553700204730ustar00rootroot00000000000000//# LCRegion.h: Abstract base class to define a region of interest in lattice coordinates //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LCREGION_H #define LATTICES_LCREGION_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableRecord; class RecordInterface; // // Abstract base class to define a region of interest in lattice coordinates. // // // // // //
      • Slicer // // // The LCRegion class is the abstract base class for various types // of LCRegion's (e.g. LCEllipsoid, // LCBox). // It contains the minimal bounding box of the region and, if needed, // a mask with the same shape as the bounding box. A mask element // is true if the element is inside the box. //

        // Each LCRegion object must be able to convert itself to and from a record. // In that way they can be made persistent (in for example a Table). //

        // The LCRegion can be used in several Lattices and Images classes and // functions to limit the area to operate on. // // // // // // // The Slicer class is too limited as a region, because it can only // describe a rectangular region. Specialized classes are needed to // describe arbitrary regions. They need a base class to combine them. // //# //#

      • //# class LCRegion : public Lattice { public: LCRegion(); // Construct with the lattice shape only. LCRegion (const IPosition& latticeShape); // Copy constructor (copy semantics). LCRegion (const LCRegion& other); virtual ~LCRegion(); // Equality virtual Bool operator== (const LCRegion& other) const; // Non-equality. Be careful, do not use this anywhere in the derived // class structure. You must use, e.g., // if (! LCRegion::operator== (...)) // rather than if (LCRegion::operator!= (...)) as the // latter will invoke an infinite loop. It is ok to use when applying // to a concrete class object. Bool operator!= (const LCRegion& other) const; // Make a copy of the derived object. // virtual Lattice* clone() const; virtual LCRegion* cloneRegion() const = 0; // // Handle deletion of the region by deleting possible tables. // The default implementation does nothing. virtual void handleDelete(); // Handle renaming the region by renaming possible tables. // The default implementation does nothing. virtual void handleRename (const String& newName, Bool overwrite); // Region type. Returns className() of derived class. virtual String type() const = 0; // Get or set the comment. // const String& comment() const; void setComment (const String& comment); // // Does the region have a mask? virtual Bool hasMask() const = 0; // Construct another LCRegion (for e.g. another lattice) by moving // this one. It recalculates the bounding box and mask. // A positive translation value indicates "to right". // LCRegion* translate (const IPosition& translateVector) const; LCRegion* translate (const IPosition& translateVector, const IPosition& newLatticeShape) const; LCRegion* translate (const Vector& translateVector) const; LCRegion* translate (const Vector& translateVector, const IPosition& newLatticeShape) const; // // Give the full lattice shape. const IPosition& latticeShape() const; // Give the bounding box. const Slicer& boundingBox() const; // Expand a slicer or position in the region to the full lattice. // This converts the positions in the region to positions // in the entire lattice. // Slicer expand (const Slicer& slicer) const; IPosition expand (const IPosition& index) const; // // Convert the (derived) object to a record. // The record can be used to make the object persistent. // The tableName argument can be used by derived // classes (e.g. LCPagedMask) to put very large objects. virtual TableRecord toRecord (const String& tableName) const = 0; // Convert correct object from a record. static LCRegion* fromRecord (const TableRecord&, const String& tableName); // Return the dimensionality of the region. virtual uInt ndim() const; // Return the shape of the region (i.e. of its bounding box). virtual IPosition shape() const; // Usually the lattice (i.e. the region mask) is not writable. virtual Bool isWritable() const; // Regions can usually not be put; i.e. no putSlice, etc. can be // done on their masks. // Hence LCRegion throws by default an exception for the // following functions. // virtual void doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride); virtual void set (const Bool& value); virtual void apply (Bool (*function)(Bool)); virtual void apply (Bool (*function)(const Bool&)); virtual void apply (const Functional& function); virtual void putAt (const Bool& value, const IPosition& where); virtual void copyData (const Lattice& from); // protected: // Assignment (copy semantics) is only useful for derived classes. LCRegion& operator= (const LCRegion& other); // Sometimes it is inconvenient for a derived class to set the bounding // box in the constructor. So it can be set explicitly. // It fills in the possibly undefined Slicer values. // It may even be needed to set the lattice shape. // void setBoundingBox (const Slicer& boundingBox); void setShapeAndBoundingBox (const IPosition& latticeShape, const Slicer& boundingBox); // // Do the actual translate in a derived class. virtual LCRegion* doTranslate (const Vector& translateVector, const IPosition& newLatticeShape) const = 0; // Define the type and class name in the record. void defineRecordFields (RecordInterface& record, const String& className) const; private: IPosition itsShape; Slicer itsBoundingBox; String itsComment; }; inline const Slicer& LCRegion::boundingBox() const { return itsBoundingBox; } inline const IPosition& LCRegion::latticeShape() const { return itsShape; } inline LCRegion* LCRegion::translate (const IPosition& translateVector) const { return translate (translateVector, itsShape); } inline LCRegion* LCRegion::translate (const Vector& translateVector) const { return translate (translateVector, itsShape); } inline const String& LCRegion::comment() const { return itsComment; } inline void LCRegion::setComment (const String& comment) { itsComment = comment; } inline Bool LCRegion::operator!= (const LCRegion& other) const // // Watch out ! You must not, in the derived class structure, // invoke LCRegion::operator!= If you do, you will be stuck // in a time warp, as this will just fetch the // operator== of the concrete class and you start all over again. // You must use always use !LCRegion::operator==. It is ok in application // code using the concrete class to say // if (x != y) where x and y are, say, LCBoxes. { return (!operator==(other)); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LRegions/LCRegion2.cc000066400000000000000000000100671476623553700207150ustar00rootroot00000000000000//# LCRegion2.cc: Implementation of LCRegion::fromRecord //# Copyright (C) 1997,1998,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LCRegion* LCRegion::fromRecord (const TableRecord& rec, const String& tableName) { if (!rec.isDefined("isRegion") || rec.asInt("isRegion") != RegionType::LC) { throw (AipsError ("LCRegion::fromRecord - " "record does not contain an LC region")); } const String& name = rec.asString ("name"); LCRegion* regPtr = 0; if (name == LCBox::className()) { regPtr = LCBox::fromRecord (rec, tableName); } else if (name == LCEllipsoid::className()) { regPtr = LCEllipsoid::fromRecord (rec, tableName); } else if (name == LCPolygon::className()) { regPtr = LCPolygon::fromRecord (rec, tableName); } else if (name == LCPixelSet::className()) { regPtr = LCPixelSet::fromRecord (rec, tableName); } else if (name == LCPagedMask::className()) { regPtr = LCPagedMask::fromRecord (rec, tableName); } else if (name == LCIntersection::className()) { regPtr = LCIntersection::fromRecord (rec, tableName); } else if (name == LCUnion::className()) { regPtr = LCUnion::fromRecord (rec, tableName); } else if (name == LCConcatenation::className()) { regPtr = LCConcatenation::fromRecord (rec, tableName); } else if (name == LCComplement::className()) { regPtr = LCComplement::fromRecord (rec, tableName); } else if (name == LCDifference::className()) { regPtr = LCDifference::fromRecord (rec, tableName); } else if (name == LCExtension::className()) { regPtr = LCExtension::fromRecord (rec, tableName); } else if (name == LCStretch::className()) { regPtr = LCStretch::fromRecord (rec, tableName); } else if (name == LCHDF5Mask::className()) { regPtr = LCHDF5Mask::fromRecord (rec, tableName); } else { throw (AipsError ("LCRegion::fromRecord - " + name + " is unknown derived LCRegion class")); } if (rec.isDefined ("comment")) { regPtr->setComment (rec.asString ("comment")); } return regPtr; } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LRegions/LCRegionFixed.cc000066400000000000000000000042331476623553700216110ustar00rootroot00000000000000//# LCRegionFixed.cc: Abstract base class to define a fixed region //# Copyright (C) 1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LCRegionFixed::LCRegionFixed() {} LCRegionFixed::LCRegionFixed (const IPosition& latticeShape) : LCRegionSingle (latticeShape) {} LCRegionFixed::LCRegionFixed (const LCRegionFixed& other) : LCRegionSingle (other), itsMask (other.itsMask) { setMaskPtr (itsMask); } LCRegionFixed::~LCRegionFixed() {} LCRegionFixed& LCRegionFixed::operator= (const LCRegionFixed& other) { if (this != &other) { LCRegionSingle::operator= (other); itsMask = other.itsMask; setMaskPtr (itsMask); } return *this; } Bool LCRegionFixed::operator== (const LCRegion& other) const { return LCRegion::operator== (other); } void LCRegionFixed::setMask (const Array& mask) { itsMask = mask; setMaskPtr (itsMask); } const ArrayLattice& LCRegionFixed::getMask() const { return itsMask; } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LRegions/LCRegionFixed.h000066400000000000000000000070311476623553700214520ustar00rootroot00000000000000//# LCRegionFixed.h: Abstract base class to define a fixed region //# Copyright (C) 1998,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LCREGIONFIXED_H #define LATTICES_LCREGIONFIXED_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Abstract base class to define a fixed region. // // // // // //
      • Slicer // // // The LCRegion class is the abstract base class for various types // of LCRegion's (e.g. LCRegionEllipsoid, LCRegionBox). // It contains the minimal bounding box of the region and, if needed, // a mask with the same shape as the bounding box. A mask element // is true if the element is inside the box. //

        // Each LCRegion object must be able to convert itself to and from a record. // In that way they can be made persistent (in for example a Table). //

        // The LCRegion can be used in several Lattices and Images classes and // functions to limit the area to operate on. // // // // // // // The Slicer class is too limited as a region, because it can only // describe a rectangular region. Specialized classes are needed to // describe arbitrary regions. They need a base class to combine them. // //# //#

      • //# class LCRegionFixed : public LCRegionSingle { public: LCRegionFixed(); // Construct with the lattice shape only. LCRegionFixed (const IPosition& latticeShape); // Copy constructor (copy semantics). LCRegionFixed (const LCRegionFixed& other); // Destructor virtual ~LCRegionFixed(); // Comparison. Mask is not checked. Use the // LCRegionSingle::masksEqual function as well if // you want to check the masks virtual Bool operator== (const LCRegion& other) const; // Return the mask const ArrayLattice& getMask() const; protected: // Assignment (copy semantics) is only useful for derived classes. LCRegionFixed& operator= (const LCRegionFixed& other); // Set the mask. void setMask (const Array& mask); private: ArrayLattice itsMask; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LRegions/LCRegionMulti.cc000066400000000000000000000247111476623553700216470ustar00rootroot00000000000000//# LCRegionMulti.cc: Abstract base class for regions composed of other regions //# Copyright (C) 1998,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LCRegionMulti::LCRegionMulti() {} LCRegionMulti::LCRegionMulti (const LCRegion& region1, const LCRegion& region2) : LCRegion (region1.latticeShape()), itsRegions (2) { itsRegions[0] = ®ion1; itsRegions[1] = ®ion2; init (False); } LCRegionMulti::LCRegionMulti (Bool takeOver, const LCRegion* region1, const LCRegion* region2, const LCRegion* region3, const LCRegion* region4, const LCRegion* region5, const LCRegion* region6, const LCRegion* region7, const LCRegion* region8, const LCRegion* region9, const LCRegion* region10) : LCRegion (region1->latticeShape()), itsRegions (10) { uInt n=0; itsRegions[n++] = region1; if (region2 != 0) itsRegions[n++] = region2; if (region3 != 0) itsRegions[n++] = region3; if (region4 != 0) itsRegions[n++] = region4; if (region5 != 0) itsRegions[n++] = region5; if (region6 != 0) itsRegions[n++] = region6; if (region7 != 0) itsRegions[n++] = region7; if (region8 != 0) itsRegions[n++] = region8; if (region9 != 0) itsRegions[n++] = region9; if (region10 != 0) itsRegions[n++] = region10; itsRegions.resize (n, True, True); init (takeOver); } LCRegionMulti::LCRegionMulti (Bool takeOver, const PtrBlock& regions) : LCRegion (regions[0]->latticeShape()), itsRegions (regions) { init (takeOver); } LCRegionMulti::LCRegionMulti (const LCRegion* regionPtr, const IPosition& latticeShape) : LCRegion (latticeShape), itsRegions (1) { itsRegions[0] = regionPtr; itsHasMask = (regionPtr->hasMask() ? 0 : -1); } LCRegionMulti::LCRegionMulti (const LCRegionMulti& other) : LCRegion (other), itsHasMask (other.itsHasMask), itsRegions (other.itsRegions.nelements()) { uInt nr = itsRegions.nelements(); for (uInt i=0; icloneRegion(); } } LCRegionMulti::~LCRegionMulti() { uInt nr = itsRegions.nelements(); for (uInt i=0; icloneRegion(); } } return *this; } Bool LCRegionMulti::hasMask() const { return (itsHasMask >= 0); } void LCRegionMulti::multiTranslate (PtrBlock& regions, const Vector& translateVector, const IPosition& newLatticeShape) const { regions.resize (itsRegions.nelements(), True); for (uInt i=0; itranslate (translateVector, newLatticeShape); } } Bool LCRegionMulti::operator== (const LCRegion& other) const { // Check if parent class matches. // If so, we can safely cast. if (! LCRegion::operator== (other)) { return False; } const LCRegionMulti& that = (const LCRegionMulti&)other; // Check the regions. if (itsRegions.nelements() != that.itsRegions.nelements()) { return False; } // The regions do not have to be in the same order. // It makes it a bit slower. uInt nr = itsRegions.nelements(); Vector used(nr, False); for (uInt i=0; ilatticeShape() != latticeShape()) { throw (AipsError ("LCRegionMulti::init - " "all regions must have same lattice shape")); } if (!takeOver) { itsRegions[i] = itsRegions[i]->cloneRegion(); } } } void LCRegionMulti::fillHasMask() { itsHasMask = -1; uInt maxNelem = 0; for (uInt i=0; ihasMask() && itsRegions[i]->nelements() > maxNelem) { itsHasMask = i; } } } Bool LCRegionMulti::findAreas (IPosition& bufStart, IPosition& bufEnd, IPosition& regStart, IPosition& regEnd, const Slicer& section, uInt regNr) const { DebugAssert (regNr < itsRegions.nelements(), AipsError); uInt nrdim = section.ndim(); bufStart.resize (nrdim); bufEnd.resize (nrdim); regStart.resize (nrdim); regEnd.resize (nrdim); const IPosition& bboxstart = boundingBox().start(); const IPosition& rstart = itsRegions[regNr]->boundingBox().start(); const IPosition& rend = itsRegions[regNr]->boundingBox().end(); Bool overlap = True; for (uInt j=0; j secend || regend < secst) { overlap = False; break; } // Fine, there is overlap. // Now find out where the section starts in the region and in // the receiving buffer. There are several cases: // - section starts before or after region // - section ends before or after region // Life is complicated by the fact that the section can have a // stride. This less common case is handled separately to // not to affect performance of common case stride==1. // At the end we get: // - bufStart gives the offset of the first pixel in the buffer // - bufEnd gives the offset of the last pixel in the buffer // - regStart gives the offset of the first pixel in the region // - regEnd gives the offset of the last pixel in the region // They tell the caller where to get the required pixels from // this region and where to store them in the buffer. if (secinc == 1) { Int diff = secst - regst; if (diff >= 0) { regStart(j) = diff; // section starts at or after region bufStart(j) = 0; } else { regStart(j) = 0; // section starts before region bufStart(j) = -diff; } if (regend < secend) { regEnd(j) = regend-regst; // section ends after region bufEnd(j) = regend-secst; } else { regEnd(j) = secend-regst; // section ends at or before region bufEnd(j) = secend-secst; } } else { // The case with stride>1 is a bit more complicated. // The buffer has no stride (it receives the required pixels only), // so when determining its starts, we must take the increment // into account. When the section starts before the boundary, // the actual start in the region must be on a stride alignment. // It is also possible that the increment is such that the // entire region is skipped. Int diff = secst - regst; if (diff >= 0) { regStart(j) = diff; bufStart(j) = 0; } else { Int diffalign = 1 + (-1-diff)/secinc; regStart(j) = diffalign*secinc + diff; bufStart(j) = diffalign; } if (regend < secend) { regEnd(j) = regend-regst; } else { regEnd(j) = secend-regst; } if (regEnd(j) < regStart(j)) { overlap = False; break; } bufEnd(j) = bufStart(j) + (regEnd(j)-regStart(j))/secinc; } DebugAssert (bufEnd(j)-bufStart(j) == (regEnd(j)-regStart(j))/secinc, AipsError); } return overlap; } TableRecord LCRegionMulti::makeRecord (const String& tableName) const { TableRecord rec; Int nr = itsRegions.nelements(); for (Int i=0; itoRecord (tableName)); } rec.define ("nr", nr); return rec; } void LCRegionMulti::unmakeRecord (PtrBlock& regions, const TableRecord& rec, const String& tableName) { Int nr = rec.asInt ("nr"); regions.resize (nr, True); for (Int i=0; i& buffer, const Slicer& section) { if (itsHasMask >= 0) { multiGetSlice (buffer, section); } else { buffer.resize (section.length()); buffer = True; } return False; } IPosition LCRegionMulti::doNiceCursorShape (uInt maxPixels) const { if (itsHasMask >= 0) { return itsRegions[itsHasMask]->niceCursorShape (maxPixels); } return Lattice::doNiceCursorShape (maxPixels); } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LRegions/LCRegionMulti.h000066400000000000000000000132331476623553700215060ustar00rootroot00000000000000//# LCRegionMulti.h: Make the intersection of 2 or more regions //# Copyright (C) 1998,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LCREGIONMULTI_H #define LATTICES_LCREGIONMULTI_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Make the intersection of 2 or more regions. // // // // // //
      • LCRegion // // // The LCRegionMulti class is a specialization of class // LCRegion. // It makes it possible to extend a LCRegion along straight lines to // other dimensions. E.g. a circle in the xy-plane can be extended to // a cylinder in the xyz-space. // includes the intersection border. // It can only be used for a lattice of any dimensionality as long as the // dimensionality of the (hyper-)intersection matches the dimensionality of // the lattice. //

        // The center of the intersection must be inside the lattice // // // // // // //

      • // class LCRegionMulti: public LCRegion { public: LCRegionMulti(); // Construct from 2 regions. LCRegionMulti (const LCRegion& region1, const LCRegion& region2); // Construct from multiple regions. LCRegionMulti (Bool takeOver, const LCRegion* region1, const LCRegion* region2 = 0, const LCRegion* region3 = 0, const LCRegion* region4 = 0, const LCRegion* region5 = 0, const LCRegion* region6 = 0, const LCRegion* region7 = 0, const LCRegion* region8 = 0, const LCRegion* region9 = 0, const LCRegion* region10 = 0); // Construct from multiple regions given as a Block. // When takeOver is True, the destructor will delete the // given regions. Otherwise a copy of the regions is made. LCRegionMulti (Bool takeOver, const PtrBlock& regions); // Copy constructor (copy semantics). LCRegionMulti (const LCRegionMulti& other); virtual ~LCRegionMulti(); // Assignment (copy semantics). LCRegionMulti& operator= (const LCRegionMulti& other); // Comparison virtual Bool operator== (const LCRegion& other) const; // Does the region have a mask? virtual Bool hasMask() const; protected: // Store the contributing regions in a record. TableRecord makeRecord (const String& tableName) const; // Retrieve the contributing objects from the record. static void unmakeRecord (PtrBlock&, const TableRecord&, const String& tableName); // Translate all regions. void multiTranslate (PtrBlock&, const Vector& translateVector, const IPosition& newLatticeShape) const; // Determine if all regions have mask (used by LCIntersection). void fillHasMask(); // Find which area of the section and region are needed. // False is returned if no part of the region is included in the section. Bool findAreas (IPosition& bufStart, IPosition& bufEnd, IPosition& regStart, IPosition& regEnd, const Slicer& section, uInt regNr) const; // Get the contributing regions. const PtrBlock& regions() const; protected: // Construct from lattice shape and region pointer, which is // taken over. // Primarily meant for LCExtension. LCRegionMulti (const LCRegion* region, const IPosition& latticeShape); // Do the actual getting of an array of values. virtual Bool doGetSlice (Array& buffer, const Slicer& section); // Get the values from the class derived from Multi. // It is called when there is a mask. Note that it is not sure // whether the buffer has the correct size. virtual void multiGetSlice (Array& buffer, const Slicer& section) = 0; // Get the best cursor shape. virtual IPosition doNiceCursorShape (uInt maxPixels) const; private: // Check if the regions are correct. // If needed, make a copy of the region objects. void init (Bool takeOver); //# >=0 means this region has a mask. //# Its value gives the region with the biggest mask. Int itsHasMask; PtrBlock itsRegions; }; inline const PtrBlock& LCRegionMulti::regions() const { return itsRegions; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LRegions/LCRegionSingle.cc000066400000000000000000000126701476623553700217770ustar00rootroot00000000000000//# LCRegionSingle.cc: Abstract base class to define a single region //# Copyright (C) 1998,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LCRegionSingle::LCRegionSingle() {} LCRegionSingle::LCRegionSingle (const IPosition& latticeShape) : LCRegion (latticeShape), itsHasMask (False), itsMaskPtr (0) {} LCRegionSingle::LCRegionSingle (const LCRegionSingle& other) : LCRegion (other), itsHasMask (False), itsMaskPtr (0) {} LCRegionSingle::~LCRegionSingle() {} LCRegionSingle& LCRegionSingle::operator= (const LCRegionSingle& other) { LCRegion::operator= (other); return *this; } void LCRegionSingle::setMaskPtr (Lattice& mask) { itsMaskPtr = &mask; if (mask.nelements() != 0) { itsHasMask = True; } } Bool LCRegionSingle::masksEqual (const LCRegion& other) const { // Object type check. if (type() != other.type()) { return False; } // Not equal if one has a mask and the other has not. if (hasMask() != other.hasMask()) { return False; } // True if both do not have a mask. if (!hasMask() && !other.hasMask()) { return True; } // Cast (is safe because object types are equal). const LCRegionSingle& that = (const LCRegionSingle&)other; // See if masks are the same shape. if (itsMaskPtr->shape() != that.itsMaskPtr->shape()) { return False; } // Now we must check the values. RO_LatticeIterator iter1(*itsMaskPtr, itsMaskPtr->niceCursorShape()); RO_LatticeIterator iter2(*(that.itsMaskPtr), itsMaskPtr->niceCursorShape()); while (!iter1.atEnd()) { if (anyNE (iter1.cursor(), iter2.cursor())) { return False; } iter1++; iter2++; } return True; } const Array LCRegionSingle::maskArray() const { // Return a [] shaped array if there is no mask IPosition shape; if (hasMask()) { shape = itsMaskPtr->shape(); } COWPtr > pMask(new Array(shape)); if (hasMask()) { itsMaskPtr->get (pMask); } return *pMask; } Bool LCRegionSingle::hasMask() const { return itsHasMask; } Bool LCRegionSingle::doGetSlice (Array& buffer, const Slicer& section) { if (itsHasMask != 0) { return itsMaskPtr->getSlice (buffer, section); } buffer.resize (section.length()); buffer = True; return False; } IPosition LCRegionSingle::doNiceCursorShape (uInt maxPixels) const { if (itsHasMask != 0) { return itsMaskPtr->niceCursorShape (maxPixels); } return Lattice::doNiceCursorShape (maxPixels); } LatticeIterInterface* LCRegionSingle::makeIter (const LatticeNavigator& navigator, Bool useRef) const { if (itsHasMask != 0) { return itsMaskPtr->makeIter (navigator, useRef); } return Lattice::makeIter (navigator, useRef); } void LCRegionSingle::doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride) { AlwaysAssert (hasMask() && isWritable(), AipsError); itsMaskPtr->putSlice (sourceBuffer, where, stride); } void LCRegionSingle::set (const Bool& value) { AlwaysAssert (hasMask() && isWritable(), AipsError); itsMaskPtr->set (value); } void LCRegionSingle::apply (Bool (*function)(Bool)) { AlwaysAssert (hasMask() && isWritable(), AipsError); itsMaskPtr->apply (function); } void LCRegionSingle::apply (Bool (*function)(const Bool&)) { AlwaysAssert (hasMask() && isWritable(), AipsError); itsMaskPtr->apply (function); } void LCRegionSingle::apply (const Functional& function) { AlwaysAssert (hasMask() && isWritable(), AipsError); itsMaskPtr->apply (function); } void LCRegionSingle::putAt (const Bool& value, const IPosition& where) { AlwaysAssert (hasMask() && isWritable(), AipsError); itsMaskPtr->putAt (value, where); } void LCRegionSingle::copyData (const Lattice& from) { AlwaysAssert (hasMask() && isWritable(), AipsError); itsMaskPtr->copyData (from); } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LRegions/LCRegionSingle.h000066400000000000000000000114311476623553700216330ustar00rootroot00000000000000//# LCRegionSingle.h: Abstract base class to define a single region //# Copyright (C) 1998,1999,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LCREGIONSINGLE_H #define LATTICES_LCREGIONSINGLE_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Abstract base class to define a single region. // // // // // //
      • Slicer // // // The LCRegion class is the abstract base class for various types // of LCRegion's (e.g. LCRegionEllipsoid, LCRegionBox). // It contains the minimal bounding box of the region and, if needed, // a mask with the same shape as the bounding box. A mask element // is true if the element is inside the box. //

        // Each LCRegion object must be able to convert itself to and from a Record. // In that way they can be made persistent (in for example a Table). //

        // The LCRegion can be used in several Lattices and Images classes and // functions to limit the area to operate on. // // // // // // // The Slicer class is too limited as a region, because it can only // describe a rectangular region. Specialized classes are needed to // describe arbitrary regions. They need a base class to combine them. // //# //#

      • //# class LCRegionSingle : public LCRegion { public: LCRegionSingle(); // Construct with the lattice shape only. LCRegionSingle (const IPosition& latticeShape); // Copy constructor (copy semantics). LCRegionSingle (const LCRegionSingle& other); virtual ~LCRegionSingle(); // Does the region have a mask? virtual Bool hasMask() const; // Get the mask (as an array). const Array maskArray() const; // Is the mask of this region the same as the mask of the other Bool masksEqual (const LCRegion& other) const; // The following "put" functions are described in detail in class // Lattice. // They'll throw an exception is no mask is available or if // the mask is not writable. // virtual void set (const Bool& value); virtual void apply (Bool (*function)(Bool)); virtual void apply (Bool (*function)(const Bool&)); virtual void apply (const Functional& function); virtual void putAt (const Bool& value, const IPosition& where); virtual void copyData (const Lattice& from); // protected: // Assignment (copy semantics) is only useful for derived classes. LCRegionSingle& operator= (const LCRegionSingle& other); // Set the pointer to the mask in the derived class. void setMaskPtr (Lattice& mask); // Do the actual getting of the mask. virtual Bool doGetSlice (Array& buffer, const Slicer& section); // Do the actual putting of the mask. Only possible if region is writable. virtual void doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride); // Get the best cursor shape. virtual IPosition doNiceCursorShape (uInt maxPixels) const; // Make an iterator. // When the underlying region has a mask, an iterator for that region // is returned. Otherwise the standard iterator is returned. virtual LatticeIterInterface* makeIter (const LatticeNavigator& navigator, Bool useRef) const; private: Bool itsHasMask; Lattice* itsMaskPtr; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LRegions/LCSlicer.cc000066400000000000000000000415171476623553700206350ustar00rootroot00000000000000//# LCSlicer.cc: Class to define a rectangular box of interest with strides //# Copyright (C) 1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LCSlicer::LCSlicer() : itsIsFractional (False), itsIsAbsolute (False), itsIsUnspecified (True), itsIsStrided (False) {} LCSlicer::LCSlicer (const Vector& blc, const Vector& trc, Bool fractional, RegionType::AbsRelType absRel) : itsBlc (blc.copy()), itsTrc (trc.copy()), itsInc (blc.size()) { itsInc = 1; fillFlags (fractional, absRel, blc.size(), trc.size(), itsInc.size()); fill(); } LCSlicer::LCSlicer (const Vector& blc, const Vector& trc, const Vector& inc, Bool fractional, RegionType::AbsRelType absRel) : itsBlc (blc.copy()), itsTrc (trc.copy()), itsInc (inc.copy()) { fillFlags (fractional, absRel, blc.size(), trc.size(), inc.size()); fill(); } LCSlicer::LCSlicer (const Vector& blc, const Vector& trc, const Vector& inc, const Vector& fractionalBlc, const Vector& fractionalTrc, const Vector& fractionalInc, const Vector& absRelBlc, const Vector& absRelTrc) : itsBlc (blc.copy()), itsTrc (trc.copy()), itsInc (inc.copy()), itsFracBlc (fractionalBlc.copy()), itsFracTrc (fractionalTrc.copy()), itsFracInc (fractionalInc.copy()), itsAbsRelBlc (absRelBlc.copy()), itsAbsRelTrc (absRelTrc.copy()) { fill(); } LCSlicer::LCSlicer (const Vector& blc, const Vector& trc, Bool fractional, RegionType::AbsRelType absRel) { Vector inc(blc.size()); inc = 1; fillFlags (fractional, absRel, blc.size(), trc.size(), inc.size()); fillFromDouble (blc, trc, inc); } LCSlicer::LCSlicer (const Vector& blc, const Vector& trc, const Vector& inc, Bool fractional, RegionType::AbsRelType absRel) { fillFlags (fractional, absRel, blc.size(), trc.size(), inc.size()); fillFromDouble (blc, trc, inc); } LCSlicer::LCSlicer (const Vector& blc, const Vector& trc, const Vector& inc, const Vector& fractionalBlc, const Vector& fractionalTrc, const Vector& fractionalInc, const Vector& absRelBlc, const Vector& absRelTrc) : itsFracBlc (fractionalBlc.copy()), itsFracTrc (fractionalTrc.copy()), itsFracInc (fractionalInc.copy()), itsAbsRelBlc (absRelBlc.copy()), itsAbsRelTrc (absRelTrc.copy()) { fillFromDouble (blc, trc, inc); } LCSlicer::LCSlicer (const Slicer& slicer) { uInt ndim = slicer.ndim(); fillFlags (False, False, ndim, ndim, ndim); fillFromIPosition (slicer.start(), slicer.end(), slicer.stride()); } LCSlicer::LCSlicer (const IPosition& blc, const IPosition& trc, RegionType::AbsRelType absRel) { IPosition inc(blc.size()); inc = 1; fillFlags (False, absRel, blc.size(), trc.size(), inc.size()); fillFromIPosition (blc, trc, inc); } LCSlicer::LCSlicer (const IPosition& blc, const IPosition& trc, const IPosition& inc, RegionType::AbsRelType absRel) { fillFlags (False, absRel, blc.size(), trc.size(), inc.size()); fillFromIPosition (blc, trc, inc); } LCSlicer::LCSlicer (const IPosition& blc, const IPosition& trc, const IPosition& inc, const Vector& absRelBlc, const Vector& absRelTrc) : itsFracBlc (blc.size()), itsFracTrc (trc.size()), itsFracInc (inc.size()), itsAbsRelBlc (absRelBlc.copy()), itsAbsRelTrc (absRelTrc.copy()) { itsFracBlc = False; itsFracTrc = False; itsFracInc = False; fillFromIPosition (blc, trc, inc); } LCSlicer::LCSlicer (const LCSlicer& other) : itsBlc (other.itsBlc), itsTrc (other.itsTrc), itsInc (other.itsInc), itsFracBlc (other.itsFracBlc), itsFracTrc (other.itsFracTrc), itsFracInc (other.itsFracInc), itsAbsRelBlc (other.itsAbsRelBlc), itsAbsRelTrc (other.itsAbsRelTrc), itsIsFractional (other.itsIsFractional), itsIsAbsolute (other.itsIsAbsolute), itsIsUnspecified (other.itsIsUnspecified), itsIsStrided (other.itsIsStrided), itsComment (other.itsComment) {} LCSlicer::~LCSlicer() {} LCSlicer& LCSlicer::operator= (const LCSlicer& other) { if (this != &other) { uInt nr = other.itsBlc.size(); itsBlc.resize (nr); itsTrc.resize (nr); itsInc.resize (nr); itsBlc = other.itsBlc; itsTrc = other.itsTrc; itsInc = other.itsInc; itsFracBlc.resize (nr); itsFracTrc.resize (nr); itsFracInc.resize (nr); itsAbsRelBlc.resize (nr); itsAbsRelTrc.resize (nr); itsFracBlc = other.itsFracBlc; itsFracTrc = other.itsFracTrc; itsFracInc = other.itsFracInc; itsAbsRelBlc = other.itsAbsRelBlc; itsAbsRelTrc = other.itsAbsRelTrc; itsIsFractional = other.itsIsFractional; itsIsAbsolute = other.itsIsAbsolute; itsIsUnspecified = other.itsIsUnspecified; itsIsStrided = other.itsIsStrided; itsComment = other.itsComment; } return *this; } Bool LCSlicer::operator== (const LCSlicer& other) const { // Compare private data. if (itsBlc.size() != other.itsBlc.size() || itsIsFractional != other.itsIsFractional || itsIsAbsolute != other.itsIsAbsolute || itsIsUnspecified != other.itsIsUnspecified || itsIsStrided != other.itsIsStrided) { return False; } for (uInt i=0; i& blc, const Vector& trc, const Vector& inc) { uInt i; itsBlc.resize (blc.size()); for (i=0; i(itsBlc[i]) < Slicer::MimicSource+10) { itsIsUnspecified = True; itsFracBlc[i] = False; itsAbsRelBlc[i] = RegionType::Abs; } if (static_cast(itsTrc[i]) < Slicer::MimicSource+10) { itsIsUnspecified = True; itsFracTrc[i] = False; itsAbsRelTrc[i] = RegionType::Abs; } if (itsFracBlc[i] || itsFracTrc[i] || itsFracInc[i]) { itsIsFractional = True; } if (itsAbsRelBlc != RegionType::Abs || itsAbsRelTrc != RegionType::Abs) { itsIsAbsolute = False; } if (itsInc[i] != 1 || itsFracInc[i]) { itsIsStrided = True; } } } Slicer LCSlicer::toSlicer (const IPosition& referencePixel, const IPosition& newLatticeShape) const { uInt nr = referencePixel.size(); Vector vec (nr); for (uInt i=0; i& referencePixel, const IPosition& newLatticeShape) const { uInt nr = referencePixel.size(); Vector vec (nr); for (uInt i=0; i& referencePixel, const IPosition& newLatticeShape) const { uInt i; if (referencePixel.size() != newLatticeShape.size()) { throw (AipsError ("LCSlicer::makeComplete - " "referencePixel and newLatticeShape vectors " "do not have same length")); } uInt ndreg = ndim(); uInt ndout = newLatticeShape.size(); if (ndout < ndreg) { throw (AipsError ("LCSlicer::makeComplete - " "length of newLatticeShape vector less than " "dimensionality of region")); } // The output result can have a higher dimensionality than the region // (resulting in auto-extension). // So create the results with default values which are suitable // for axes to be added. IPosition blc(ndout, 0); IPosition trc(newLatticeShape-1); IPosition inc(ndout, 1); // Now convert the region's blc,trc,inc to a normal blc,trc,inc. // The float representation of MimicSource (-2147483646) is inaccurate in the // last digits, so be relaxed in testing it. Use a margin of 10. for (i=0; i(v) < Slicer::MimicSource+10) { v = 0; } else { if (itsFracBlc[i]) { v *= newLatticeShape[i]; } if (itsAbsRelBlc[i] == RegionType::RelRef) { v += referencePixel[i]; } else if (itsAbsRelBlc[i] == RegionType::RelCen) { v += Float(newLatticeShape[i]) / 2; } } blc[i] = Int(v + 0.5); v = itsTrc[i]; if (static_cast(v) < Slicer::MimicSource+10) { v = newLatticeShape[i] - 1; } else { if (itsFracTrc[i]) { v *= newLatticeShape[i]; v -= 1; } if (itsAbsRelTrc[i] == RegionType::RelRef) { v += referencePixel[i]; } else if (itsAbsRelTrc[i] == RegionType::RelCen) { v += Float(newLatticeShape[i]) / 2; } } trc[i] = Int(v + 0.5); v = itsInc[i]; if (static_cast(v) < Slicer::MimicSource+10) { v = 1; } else { if (itsFracInc[i]) { v *= newLatticeShape[i]; } } inc[i] = Int(v + 0.5); } for (i=0; i= newLatticeShape[i]) { trc[i] = newLatticeShape[i] - 1; } if (blc[i] > trc[i]) { ostringstream bstr, tstr; bstr << blc; tstr << trc; throw (AipsError ("LCSlicer::toSlicer - " "blc " + String(bstr) + " must be <= trc " + String(tstr))); } } // Return the completed LCSlicer as a Slicer. return Slicer (blc, trc, inc, Slicer::endIsLast); } Bool LCSlicer::isComplete() const { return (!itsIsFractional && itsIsAbsolute && !itsIsUnspecified); } String LCSlicer::className() { return "LCSlicer"; } String LCSlicer::type() const { return className(); } TableRecord LCSlicer::toRecord (const String&) const { TableRecord rec; rec.define ("isRegion", Int(RegionType::ArrSlicer)); rec.define ("name", className()); rec.define ("comment", itsComment); // Write 1-relative. rec.define ("oneRel", True); Vector blc(itsBlc.copy()); Vector trc(itsTrc.copy()); for (uInt i=0; i blc (rec.toArrayFloat ("blc").copy()); Vector trc (rec.toArrayFloat ("trc").copy()); Vector fracblc (rec.toArrayBool ("fracblc")); Vector fractrc (rec.toArrayBool ("fractrc")); Vector arblc (rec.toArrayInt ("arblc")); Vector artrc (rec.toArrayInt ("artrc")); // If blc,trc is 1-relative, make it 0-relative by subtracting 1. // Do it only if absolute non-fractional. // The float representation of MimicSource is inaccurate, so // it does not harm subtracting one. if (oneRel) { for (i=0; isetComment (rec.asString ("comment")); } return regPtr; } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LRegions/LCSlicer.h000066400000000000000000000224311476623553700204710ustar00rootroot00000000000000//# LCSlicer.h: Class to define a rectangular box of interest with strides //# Copyright (C) 1998,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LCSLICER_H #define LATTICES_LCSLICER_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class IPosition; class Slicer; class TableRecord; // // Class to define a rectangular box of interest with strides. // // // // // //
      • Slicer // // // The LCSlicer makes it possible to define a rectangular box // with strides. Note that this class is not derived from // LCRegion, so it cannot be used in // a compound region object like LCUnion. // The reason is that strides make it impossible to use a region // in a compound. //
        // The slicer region can be defined from an // Slicer object defining the blc/trc // and a vector (of the same length) containing the strides. // The LCSlicer can be of any type (thus relative, fractional, unspecified), // while the strides can be defined as a number or a fraction. //
        // It is also possible to construct it directly from a // Slicer object, //
        // // // // // // class LCSlicer { public: LCSlicer(); // Construct a slicer from the blc, trc, and stride (default 1). // The vectors can be different in lengths. The longest determines // the dimensionality of the region. The shorter ones get padded // with default values. //
        For each axis (or all axes) it can be defined if the blc/trc are // given as pixel coordinates or as fractional values between 0 and 1. In the // latter case the true pixel coordinate is derived from the image shape. //
        Also the region type can be given, if needed per axis. //
          //
        • RegionType::Abs is absolute //
        • RegionType::RelRef is relative to reference pixel given in toSlice(). //
        • RegionType::RelCen is relative to image center. //
        // LCSlicer (const Vector& blc, const Vector& trc, Bool fractionalBlcTrc = False, RegionType::AbsRelType = RegionType::Abs); LCSlicer (const Vector& blc, const Vector& trc, const Vector& inc, Bool fractionalBlcTrc = False, RegionType::AbsRelType = RegionType::Abs); LCSlicer (const Vector& blc, const Vector& trc, const Vector& inc, const Vector& fractionalBlc, const Vector& fractionalTrc, const Vector& fractionalInc, const Vector& absRelBlc, const Vector& absRelTrc); LCSlicer (const Vector& blc, const Vector& trc, Bool fractionalBlcTrc = False, RegionType::AbsRelType = RegionType::Abs); LCSlicer (const Vector& blc, const Vector& trc, const Vector& inc, Bool fractionalBlcTrc = False, RegionType::AbsRelType = RegionType::Abs); LCSlicer (const Vector& blc, const Vector& trc, const Vector& inc, const Vector& fractionalBlc, const Vector& fractionalTrc, const Vector& fractionalInc, const Vector& absRelBlc, const Vector& absRelTrc); LCSlicer (const Slicer& slicer); LCSlicer (const IPosition& blc, const IPosition& trc, RegionType::AbsRelType = RegionType::Abs); LCSlicer (const IPosition& blc, const IPosition& trc, const IPosition& inc, RegionType::AbsRelType = RegionType::Abs); LCSlicer (const IPosition& blc, const IPosition& trc, const IPosition& inc, const Vector& absRelBlc, const Vector& absRelTrc); // // Copy constructor (reference semantics). LCSlicer (const LCSlicer& other); ~LCSlicer(); // Assignment (copy semantics). LCSlicer& operator= (const LCSlicer& other); // Test for equality. // True is returned when the given region is a slicer with exactly // the same specification as this slicer. // It does not compare the comment. // Bool operator== (const LCSlicer& other) const; Bool operator!= (const LCSlicer& other) const; // // The region is completely specified if it is absolute, not fractional, // and has no unspecified values. Bool isComplete() const; // Get the dimensionality of the region. uInt ndim() const; // Simple accessor functions. // const Vector& blc() const; const Vector& trc() const; const Vector& inc() const; Bool isFractional() const; Bool isAbsolute() const; Bool isUnspecified() const; Bool isStrided() const; // // Get the class name (to store in the record). static String className(); // Get the region type. Returns className(). String type() const; // Get or set the comment. // const String& comment() const; void setComment (const String& comment); // // Make the region complete using the given reference pixel // and shape. It returns a new region where the relative regions // are made absolute by translating them with respect to the // reference pixel. Furthermore unspecified values are filled // in and fractional values are turned into absolute ones. // Slicer toSlicer (const IPosition& referencePixel, const IPosition& latticeShape) const; Slicer toSlicer (const Vector& referencePixel, const IPosition& latticeShape) const; Slicer toSlicer (const Vector& referencePixel, const IPosition& newLatticeShape) const; // // Convert the object to a record. TableRecord toRecord (const String& tableName) const; // Convert to correct object from a record. static LCSlicer* fromRecord (const TableRecord&, const String& tablename); private: // Fill the pixel based flags from the general ones. void fillFlags (Bool fractional, Int absRel, uInt nrblc, uInt nrtrc, uInt nrinc); // Fill the vectors from the values given as doubles. void fillFromDouble (const Vector& blc, const Vector& trc, const Vector& inc); // Fill the vectors from the values given as IPositions. void fillFromIPosition (const IPosition& blc, const IPosition& trc, const IPosition& inc); // Fill the remaining variables. // It also adjust the lengths of the vectors if they are different. // Check if everything is given correctly. void fill(); //# Variables Vector itsBlc; Vector itsTrc; Vector itsInc; Vector itsFracBlc; Vector itsFracTrc; Vector itsFracInc; Vector itsAbsRelBlc; Vector itsAbsRelTrc; Bool itsIsFractional; Bool itsIsAbsolute; Bool itsIsUnspecified; Bool itsIsStrided; String itsComment; }; inline Bool LCSlicer::operator!= (const LCSlicer& other) const { return (! operator==(other)); } inline uInt LCSlicer::ndim() const { return itsBlc.nelements(); } inline const Vector& LCSlicer::blc() const { return itsBlc; } inline const Vector& LCSlicer::trc() const { return itsTrc; } inline const Vector& LCSlicer::inc() const { return itsInc; } inline Bool LCSlicer::isFractional() const { return itsIsFractional; } inline Bool LCSlicer::isAbsolute() const { return itsIsAbsolute; } inline Bool LCSlicer::isUnspecified() const { return itsIsUnspecified; } inline Bool LCSlicer::isStrided() const { return itsIsStrided; } inline const String& LCSlicer::comment() const { return itsComment; } inline void LCSlicer::setComment (const String& comment) { itsComment = comment; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LRegions/LCStretch.cc000066400000000000000000000213631476623553700210250ustar00rootroot00000000000000//# LCStretch.cc: Stretch length 1 axes in an LCRegion along straight lines //# Copyright (C) 2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LCStretch::LCStretch() {} LCStretch::LCStretch (const LCRegion& region, const IPosition& stretchAxes, const LCBox& stretchBox) : LCRegionMulti (True, region.cloneRegion()) { // Fill the other members variables and determine the bounding box. fill (stretchAxes, stretchBox); } LCStretch::LCStretch (Bool takeOver, const LCRegion* region, const IPosition& stretchAxes, const LCBox& stretchBox) : LCRegionMulti (takeOver, region) { // Fill the other members variables and determine the bounding box. fill (stretchAxes, stretchBox); } LCStretch::LCStretch (const LCStretch& other) : LCRegionMulti (other), itsStretchAxes (other.itsStretchAxes), itsStretchBox (other.itsStretchBox) {} LCStretch::~LCStretch() {} LCStretch& LCStretch::operator= (const LCStretch& other) { if (this != &other) { LCRegionMulti::operator= (other); itsStretchAxes.resize (other.itsStretchAxes.nelements()); itsStretchAxes = other.itsStretchAxes; itsStretchBox = other.itsStretchBox; } return *this; } Bool LCStretch::operator== (const LCRegion& other) const { // Check if parent class matches. // If so, we can safely cast. if (! LCRegionMulti::operator== (other)) { return False; } const LCStretch& that = (const LCStretch&)other; // Check the private data if (! itsStretchAxes.isEqual (that.itsStretchAxes) || !(itsStretchBox == that.itsStretchBox)) { return False; } return True; } LCRegion* LCStretch::cloneRegion() const { return new LCStretch (*this); } LCRegion* LCStretch::doTranslate (const Vector& translateVector, const IPosition& newLatticeShape) const { uInt i; // First translate the stretchBox. // Take appropriate elements from the vectors. uInt nre = itsStretchAxes.nelements(); Vector boxTransVec (nre); IPosition boxLatShape (nre); for (i=0; i(itsStretchBox.translate (boxTransVec, boxLatShape)); // Now translate the region. LCRegion* regPtr = region().translate (translateVector, newLatticeShape); // Create the new LCStretch object. LCStretch* extPtr = new LCStretch (*regPtr, itsStretchAxes, *boxPtr); delete boxPtr; delete regPtr; return extPtr; } String LCStretch::className() { return "LCStretch"; } String LCStretch::type() const { return className(); } TableRecord LCStretch::toRecord (const String& tableName) const { TableRecord rec; defineRecordFields (rec, className()); rec.defineRecord ("region", region().toRecord (tableName)); rec.define ("axes", itsStretchAxes.asVector()); rec.defineRecord ("box", itsStretchBox.toRecord (tableName)); return rec; } LCStretch* LCStretch::fromRecord (const TableRecord& rec, const String& tableName) { // Initialize pointers to 0 to get rid of gcc-2.95 warnings. LCRegion* regPtr = 0; regPtr = LCRegion::fromRecord (rec.asRecord("region"), tableName); LCBox* boxPtr = 0; boxPtr = (LCBox*)(LCRegion::fromRecord (rec.asRecord("box"), tableName)); LCStretch* extPtr = new LCStretch (True, regPtr, Vector(rec.toArrayInt ("axes")), *boxPtr); delete boxPtr; return extPtr; } void LCStretch::fill (const IPosition& stretchAxes, const LCBox& stretchBox) { // Check if stretch axes are specified correctly. // They do not need to be in ascending order, but duplicates are // not allowed. IPosition regionShape = region().shape(); uInt nrdim = regionShape.nelements(); uInt nrs = stretchAxes.nelements(); if (nrs == 0) { throw (AipsError ("LCStretch::LCStretch - " "no stretch axes have been specified")); } if (nrs != stretchBox.blc().nelements()) { throw (AipsError ("LCStretch::LCStretch - " "number of axes in stretch box mismatches " "number of stretch axes")); } itsStretchAxes.resize (nrs); IPosition boxLatShape(nrs); Vector boxLatBlc(nrs); Vector boxLatTrc(nrs); Vector reginx(nrs); GenSortIndirect::sort (reginx, stretchAxes.storage(), nrs); Int first = -1; for (uInt i=0; i= Int(nrdim)) { throw (AipsError ("LCStretch::LCStretch - " "stretch axes multiply specified " "or exceed nrdim")); } first = itsStretchAxes(i); if (regionShape(itsStretchAxes(i)) != 1) { throw (AipsError ("LCStretch::LCStretch - " "a stretch axis does not have length 1")); } } itsStretchBox = LCBox (boxLatBlc, boxLatTrc, boxLatShape); // Make up the lattice shape from the region and box latticeshape. // Fill the bounding box from blc/trc in region and box. IPosition latShape = region().latticeShape(); IPosition blc = region().boundingBox().start(); IPosition trc = region().boundingBox().end(); const IPosition& boxShp = itsStretchBox.latticeShape(); const IPosition& boxBlc = itsStretchBox.boundingBox().start(); const IPosition& boxTrc = itsStretchBox.boundingBox().end(); for (uInt i=0; i& buffer, const Slicer& section) { buffer.resize (section.length()); // Read the required region section. // This means we have to create a Slicer with length 1 for stretched axes. IPosition blc(section.start()); IPosition len(section.length()); IPosition inc(section.stride()); uInt nrs = itsStretchAxes.nelements(); for (uInt i=0; i tmpbuf(len); LCRegion* reg = (LCRegion*)(regions()[0]); reg->doGetSlice (tmpbuf, Slicer(blc, len, inc)); // Now we have to stretch tmpbuf along all stretch axes. const IPosition& length = section.length(); IPosition pos (buffer.ndim(), 0); IPosition end (buffer.shape() - 1); //# Iterate along itsStretchAxes through the new mask. for (;;) { for (uInt i=0; i::doNiceCursorShape (maxPixels); } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LRegions/LCStretch.h000066400000000000000000000124451476623553700206700ustar00rootroot00000000000000//# LCStretch.h: Stretch length 1 axes in an LCRegion along straight lines //# Copyright (C) 2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LCSTRETCH_H #define LATTICES_LCSTRETCH_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Stretch length 1 axes in an LCRegion along straight lines // // // // // //
      • LCRegion // // // The LCStretch class is a specialization of class // LCRegion. // It makes it possible to stretch a LCRegion along straight lines to // other dimensions. E.g. a circle in the xy-plane can be stretched to // a cylinder in the xyz-space. // It can be used for a lattice of any dimensionality as long as the // dimensionality of the (hyper-)stretch matches the dimensionality of // the lattice. // // // // // // //
      • Stretch along (slanted) cone lines // class LCStretch: public LCRegionMulti { public: LCStretch(); // Stretch the given region along axes as given by stretchAxes // from the bottom left corner (blc) to the top right corner (trc) // as given by stretchBox. // The axes to be stretched need to have length 1 in the original region. // Every kind of box (absolute, relative, fractional, unspecified) // can be used to define the stretch blc and trc. // The dimensionality of the LCStretch region is the same as the // dimensionality of the original region. //
        // The second version takes over the pointer when the switch is true. // LCStretch (const LCRegion& region, const IPosition& stretchAxes, const LCBox& stretchBox); LCStretch (Bool takeOver, const LCRegion* region, const IPosition& stretchAxes, const LCBox& stretchBox); // // Copy constructor (copy semantics). LCStretch (const LCStretch& other); virtual ~LCStretch(); // Assignment (copy semantics). LCStretch& operator= (const LCStretch& other); // Comparison virtual Bool operator== (const LCRegion& other) const; // Make a copy of the derived object. virtual LCRegion* cloneRegion() const; // Get the original region. const LCRegion& region() const; // Get the stretch axes. const IPosition& stretchAxes() const; // Get the stretch box. const LCBox& stretchBox() const; // Get the class name (to store in the record). static String className(); // Get the region type. Returns the class name. virtual String type() const; // Convert the (derived) object to a record. virtual TableRecord toRecord (const String& tableName) const; // Convert correct object from a record. static LCStretch* fromRecord (const TableRecord&, const String& tableName); protected: // Construct another LCRegion (for e.g. another lattice) by moving // this one. It recalculates the bounding box and mask. // A positive translation value indicates "to right". virtual LCRegion* doTranslate (const Vector& translateVector, const IPosition& newLatticeShape) const; // Do the actual getting of the mask. virtual void multiGetSlice (Array& buffer, const Slicer& section); // This function is needed here because the niceCursorShape of the // contributing region does not make any sense (other dimensionality). virtual IPosition doNiceCursorShape (uInt maxPixels) const; private: // Fill the object. void fill (const IPosition& stretchAxes, const LCBox& stretchBox); IPosition itsStretchAxes; LCBox itsStretchBox; }; inline const LCRegion& LCStretch::region() const { return *(regions()[0]); } inline const IPosition& LCStretch::stretchAxes() const { return itsStretchAxes; } inline const LCBox& LCStretch::stretchBox() const { return itsStretchBox; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LRegions/LCUnion.cc000066400000000000000000000126141476623553700205000ustar00rootroot00000000000000//# LCUnion.cc: Make the union of 2 or more regions //# Copyright (C) 1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LCUnion::LCUnion() {} LCUnion::LCUnion (const LCRegion& region1, const LCRegion& region2) : LCRegionMulti (region1, region2) { defineBox(); } LCUnion::LCUnion (Bool takeOver, const LCRegion* region1, const LCRegion* region2, const LCRegion* region3, const LCRegion* region4, const LCRegion* region5, const LCRegion* region6, const LCRegion* region7, const LCRegion* region8, const LCRegion* region9, const LCRegion* region10) : LCRegionMulti (takeOver, region1, region2, region3, region4, region5, region6, region7, region8, region9, region10) { defineBox(); } LCUnion::LCUnion (Bool takeOver, const PtrBlock& regions) : LCRegionMulti (takeOver, regions) { defineBox(); } LCUnion::LCUnion (const LCUnion& other) : LCRegionMulti (other) {} LCUnion::~LCUnion() {} LCUnion& LCUnion::operator= (const LCUnion& other) { if (this != &other) { LCRegionMulti::operator= (other); } return *this; } Bool LCUnion::operator== (const LCRegion& other) const { return LCRegionMulti::operator== (other); } LCRegion* LCUnion::cloneRegion() const { return new LCUnion (*this); } LCRegion* LCUnion::doTranslate (const Vector& translateVector, const IPosition& newLatticeShape) const { PtrBlock regions; multiTranslate (regions, translateVector, newLatticeShape); return new LCUnion (True, regions); } String LCUnion::className() { return "LCUnion"; } String LCUnion::type() const { return className(); } TableRecord LCUnion::toRecord (const String& tableName) const { TableRecord rec; defineRecordFields (rec, className()); rec.defineRecord ("regions", makeRecord(tableName)); return rec; } LCUnion* LCUnion::fromRecord (const TableRecord& rec, const String& tableName) { PtrBlock regions; unmakeRecord (regions, rec.asRecord("regions"), tableName); return new LCUnion (True, regions); } void LCUnion::defineBox() { uInt i; // Get the union of blc and trc. const IPosition& shape = latticeShape(); uInt nrdim = shape.nelements(); IPosition blc (regions()[0]->boundingBox().start()); IPosition trc (regions()[0]->boundingBox().end()); uInt nr = regions().nelements(); for (i=1; iboundingBox().start(); const IPosition& regtrc = regions()[i]->boundingBox().end(); for (uInt j=0; j trc(j)) { trc(j) = regtrc(j); } } } // Set the bounding box in the parent object. setBoundingBox (Slicer(blc, trc, Slicer::endIsLast)); } void LCUnion::multiGetSlice (Array& buffer, const Slicer& section) { buffer.resize (section.length()); uInt nrdim = buffer.ndim(); buffer = False; IPosition stbuf(nrdim); IPosition endbuf(nrdim); IPosition streg(nrdim); IPosition endreg(nrdim); const IPosition& inc = section.stride(); uInt nr = regions().nelements(); for (uInt i=0; i tmpbuf; LCRegion* reg = (LCRegion*)(regions()[i]); reg->doGetSlice (tmpbuf, Slicer(streg, endreg, inc, Slicer::endIsLast)); Array bufreg = buffer(stbuf,endbuf); DebugAssert (bufreg.shape() == tmpbuf.shape(), AipsError); // Make pixel in buffer True when tmpbuf has a True pixel. Bool deleteBuf, deleteTmp; Bool* buf = bufreg.getStorage (deleteBuf); Bool* bufptr = buf; Bool* bufend = buf + bufreg.nelements(); const Bool* tmp = tmpbuf.getStorage (deleteTmp); const Bool* tmpptr = tmp; while (bufptr < bufend) { if (*tmpptr++) { *bufptr = True; } bufptr++; } bufreg.putStorage (buf, deleteBuf); tmpbuf.freeStorage (tmp, deleteTmp); } } } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LRegions/LCUnion.h000066400000000000000000000106511476623553700203410ustar00rootroot00000000000000//# LCUnion.h: Make the union of 2 or more regions //# Copyright (C) 1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LCUNION_H #define LATTICES_LCUNION_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Make the union of 2 or more regions. // // // // // //
      • LCRegion // // // The LCUnion class is a specialization of class // LCRegion. // It makes it possible to extend a LCRegion along straight lines to // other dimensions. E.g. a circle in the xy-plane can be extended to // a cylinder in the xyz-space. // includes the union border. // It can only be used for a lattice of any dimensionality as long as the // dimensionality of the (hyper-)union matches the dimensionality of // the lattice. //

        // The center of the union must be inside the lattice // // // // // // //

      • Expand along (slanted) cone lines // class LCUnion: public LCRegionMulti { public: LCUnion(); // Construct the union of the given regions. LCUnion (const LCRegion& region1, const LCRegion& region2); // Construct from multiple regions. // When takeOver is True, the destructor will delete the // given regions. Otherwise a copy of the regions is made. // LCUnion (Bool takeOver, const LCRegion* region1, const LCRegion* region2 = 0, const LCRegion* region3 = 0, const LCRegion* region4 = 0, const LCRegion* region5 = 0, const LCRegion* region6 = 0, const LCRegion* region7 = 0, const LCRegion* region8 = 0, const LCRegion* region9 = 0, const LCRegion* region10 = 0); LCUnion (Bool takeOver, const PtrBlock& regions); // // Copy constructor (copy semantics). LCUnion (const LCUnion& other); virtual ~LCUnion(); // Assignment (copy semantics). LCUnion& operator= (const LCUnion& other); // Comparison virtual Bool operator== (const LCRegion& other) const; // Make a copy of the derived object. virtual LCRegion* cloneRegion() const; // Get the class name (to store in the record). static String className(); // Get the region type. Returns className() virtual String type() const; // Convert the (derived) object to a record. virtual TableRecord toRecord (const String& tableName) const; // Convert correct object from a record. static LCUnion* fromRecord (const TableRecord&, const String& tableName); protected: // Construct another LCRegion (for e.g. another lattice) by moving // this one. It recalculates the bounding box and mask. // A positive translation value indicates "to right". virtual LCRegion* doTranslate (const Vector& translateVector, const IPosition& newLatticeShape) const; // Do the actual getting of the mask. virtual void multiGetSlice (Array& buffer, const Slicer& section); private: // Make the bounding box and determine the offsets. void defineBox(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LRegions/LattRegionHolder.cc000066400000000000000000000131671476623553700224030ustar00rootroot00000000000000//# LattRegionHolder.cc: Class to hold a region of interest in a lattice //# Copyright (C) 1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LattRegionHolder::LattRegionHolder (uInt ndim) : itsLC (0), itsSlicer (0), itsNdim (ndim) {} LattRegionHolder::LattRegionHolder (const LCRegion& region) : itsLC (region.cloneRegion()), itsSlicer (0), itsNdim (region.ndim()) {} LattRegionHolder::LattRegionHolder (const LCSlicer& slicer) : itsLC (0), itsSlicer (new LCSlicer(slicer)), itsNdim (slicer.ndim()) {} LattRegionHolder::LattRegionHolder (LCRegion* region) : itsLC (region), itsSlicer (0), itsNdim (region->ndim()) {} LattRegionHolder::LattRegionHolder (LCSlicer* slicer) : itsLC (0), itsSlicer (slicer), itsNdim (slicer->ndim()) {} LattRegionHolder::LattRegionHolder (const LattRegionHolder& other) : itsLC (0), itsSlicer (0) { operator= (other); } LattRegionHolder::~LattRegionHolder() { delete itsLC; delete itsSlicer; } LattRegionHolder& LattRegionHolder::operator= (const LattRegionHolder& other) { if (this != &other) { delete itsLC; delete itsSlicer; itsLC = other.itsLC; itsSlicer = other.itsSlicer; itsNdim = other.itsNdim; if (itsLC != 0) { itsLC = itsLC->cloneRegion(); } if (itsSlicer != 0) { itsSlicer = new LCSlicer(*itsSlicer); } } return *this; } LattRegionHolder* LattRegionHolder::clone() const { return new LattRegionHolder (*this); } Bool LattRegionHolder::operator== (const LattRegionHolder& other) const { if (isWCRegion() != other.isWCRegion() || isLCRegion() != other.isLCRegion() || isLCSlicer() != other.isLCSlicer()) { return False; } Bool match = True; if (isLCRegion()) { match = (*itsLC == *other.asLCRegionPtr()); } else if (isLCSlicer()) { match = (*itsSlicer == *other.asLCSlicerPtr()); } return match; } Bool LattRegionHolder::isWCRegion() const { return False; } const LCRegion* LattRegionHolder::asLCRegionPtr() const { AlwaysAssert (isLCRegion(), AipsError); return itsLC; } const LCSlicer* LattRegionHolder::asLCSlicerPtr() const { AlwaysAssert (isLCSlicer(), AipsError); return itsSlicer; } const WCRegion* LattRegionHolder::asWCRegionPtr() const { return 0; } LatticeRegion LattRegionHolder::toLatticeRegion (const CoordinateSystem&, const IPosition& shape) const { return toLatticeRegion (shape); } LatticeRegion LattRegionHolder::toLatticeRegion (const IPosition& shape) const { if (isLCRegion()) { return LatticeRegion (*itsLC); } if (isWCRegion()) { throw (AipsError ("LattRegionHolder::toLatticeRegion - " "using a region in world coordinates requires " "image coordinates")); } if (! itsSlicer->isAbsolute()) { throw (AipsError ("LattRegionHolder::toLatticeRegion - " "cannot convert a relative LCSlicer")); } Vector refpix(shape.nelements()); refpix = 0; return LatticeRegion (itsSlicer->toSlicer (refpix, shape), shape); } LattRegionHolder* LattRegionHolder::makeUnion (const LattRegionHolder& other) const { return new LattRegionHolder (new LCUnion (*asLCRegionPtr(), *other.asLCRegionPtr())); } LattRegionHolder* LattRegionHolder::makeIntersection (const LattRegionHolder& other) const { return new LattRegionHolder (new LCIntersection (*asLCRegionPtr(), *other.asLCRegionPtr())); } LattRegionHolder* LattRegionHolder::makeDifference (const LattRegionHolder& other) const { return new LattRegionHolder (new LCDifference (*asLCRegionPtr(), *other.asLCRegionPtr())); } LattRegionHolder* LattRegionHolder::makeComplement() const { return new LattRegionHolder (new LCComplement (*asLCRegionPtr())); } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LRegions/LattRegionHolder.h000066400000000000000000000133271476623553700222430ustar00rootroot00000000000000//# LattRegionHolder.h: Class to hold a region of interest in an image //# Copyright (C) 1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTREGIONHOLDER_H #define LATTICES_LATTREGIONHOLDER_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class CoordinateSystem; class IPosition; class LCRegion; class LCSlicer; class WCRegion; class String; class TableRecord; // // Class to hold a region of interest in an image. // // // // // //
      • LCSlicer //
      • LCRegion // // // The only purpose of LattRegionHolder is to have a single object for // the various kinds of regions. It can hold a // LCRegion, and // LCSlicer. // // // // // // // It was felt that making an abstract base class LatticeRegion for // LCRegion and WCRegion would create undesirable dependencies of // module Lattices on module Coordinates. E.g. it would be impossible // to have a function toWCRegion. // Therefore the container class LattRegionHolder is chosen, from which // the container ImageRegion is derived. // //# //#
      • //# class LattRegionHolder { public: // Construct from a region based on lattice coordinates. LattRegionHolder (const LCRegion&); // Construct from a slicer based on lattice coordinates. LattRegionHolder (const LCSlicer&); // Similar constructors as above, but using a pointer. // It takes over the pointer, so the user should not delete the // object. It is deleted by the LattRegionHolder destructor. // explicit LattRegionHolder (LCRegion*); explicit LattRegionHolder (LCSlicer*); // // Copy constructor (copy semantics). LattRegionHolder (const LattRegionHolder& other); virtual ~LattRegionHolder(); // Assignment (copy semantics). LattRegionHolder& operator= (const LattRegionHolder& other); // Clone the object. virtual LattRegionHolder* clone() const; // Comparison // virtual Bool operator==(const LattRegionHolder& other) const; Bool operator!=(const LattRegionHolder& other) const; // // Test if the underlying region is an LCRegion, etc. // Bool isLCRegion() const; Bool isLCSlicer() const; virtual Bool isWCRegion() const; // // Get the region as a pointer to a LCRegion, LCSlicer, or WCRegion. // An exception is thrown if the region is not the correct type. // Functions isWCRegion(), etc. can be used to test the type. // const LCRegion* asLCRegionPtr() const; const LCSlicer* asLCSlicerPtr() const; virtual const WCRegion* asWCRegionPtr() const; // // Get the dimensionality. uInt ndim() const; // Convert to a LatticeRegion using the given shape. LatticeRegion toLatticeRegion (const IPosition& shape) const; // Convert to a LatticeRegion using the given coordinate system // (with reference pixel) and shape. // It will also make the region complete (absolute and non-fractional). virtual LatticeRegion toLatticeRegion (const CoordinateSystem& cSys, const IPosition& shape) const; // Form a compound from this and the other region. // virtual LattRegionHolder* makeUnion (const LattRegionHolder& other) const; virtual LattRegionHolder* makeIntersection (const LattRegionHolder& other) const; virtual LattRegionHolder* makeDifference (const LattRegionHolder& other) const; virtual LattRegionHolder* makeComplement() const; // protected: // Construct for the given dimensionality (for derived classes). explicit LattRegionHolder (uInt ndim); private: LCRegion* itsLC; LCSlicer* itsSlicer; uInt itsNdim; }; inline Bool LattRegionHolder::isLCRegion() const { return (itsLC != 0); } inline Bool LattRegionHolder::isLCSlicer() const { return (itsSlicer != 0); } inline Bool LattRegionHolder::operator!= (const LattRegionHolder& other) const { return (! operator== (other)); } inline uInt LattRegionHolder::ndim() const { return itsNdim; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LRegions/LatticeRegion.cc000066400000000000000000000171311476623553700217210ustar00rootroot00000000000000//# LatticeRegion.cc: An optionally strided region in a lattice //# Copyright (C) 1998,1999,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LatticeRegion::LatticeRegion() : itsRegion (0), itsHasRegionMask (False) {} LatticeRegion::LatticeRegion (const LCRegion& region) : itsRegion (region.cloneRegion()), itsSlicer (region.boundingBox()), itsHasRegionMask (region.hasMask()) {} LatticeRegion::LatticeRegion (LCRegion* region) : itsRegion (region), itsSlicer (region->boundingBox()), itsHasRegionMask (region->hasMask()) {} LatticeRegion::LatticeRegion (const Slicer& slicer, const IPosition& latticeShape) : itsRegion (0), itsHasRegionMask (False) { // Make sure that the slicer has blc,trc filled in. IPosition blc, trc, inc; slicer.inferShapeFromSource (latticeShape, blc, trc, inc); itsSlicer = Slicer (blc, trc, inc, Slicer::endIsLast); itsRegion = new LCBox (Slicer (slicer.start(), slicer.length()), latticeShape); } LatticeRegion::LatticeRegion (const LatticeRegion& other) : Lattice(), itsRegion (other.itsRegion->cloneRegion()), itsSlicer (other.itsSlicer), itsHasRegionMask (other.itsHasRegionMask) {} LatticeRegion::~LatticeRegion() { delete itsRegion; } LatticeRegion& LatticeRegion::operator= (const LatticeRegion& other) { if (this != &other) { delete itsRegion; itsRegion = other.itsRegion; if (itsRegion != 0) { itsRegion = itsRegion->cloneRegion(); } itsSlicer = other.itsSlicer; itsHasRegionMask = other.itsHasRegionMask; } return *this; } Lattice* LatticeRegion::clone() const { return new LatticeRegion (*this); } Bool LatticeRegion::isWritable() const { return itsRegion->isWritable(); } uInt LatticeRegion::advisedMaxPixels() const { return itsRegion->advisedMaxPixels(); } IPosition LatticeRegion::doNiceCursorShape (uInt maxPixels) const { if (itsHasRegionMask) { return itsRegion->niceCursorShape (maxPixels); } return LatticeBase::doNiceCursorShape (maxPixels); } uInt LatticeRegion::maximumCacheSize() const { return itsRegion->maximumCacheSize(); } void LatticeRegion::setMaximumCacheSize (uInt howManyPixels) { itsRegion->setMaximumCacheSize (howManyPixels); } void LatticeRegion::setCacheSizeFromPath (const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath) { itsRegion->setCacheSizeFromPath (sliceShape, windowStart, windowLength, axisPath); } void LatticeRegion::setCacheSizeInTiles (uInt howManyTiles) { itsRegion->setCacheSizeInTiles (howManyTiles); } void LatticeRegion::clearCache() { itsRegion->clearCache(); } void LatticeRegion::showCacheStatistics (ostream& os) const { itsRegion->showCacheStatistics (os); } Bool LatticeRegion::lock (FileLocker::LockType type, uInt nattempts) { // Llock the PagedArray containing the mask. return itsRegion->lock (type, nattempts); } void LatticeRegion::unlock() { // Unlock the PagedArray containing the mask. itsRegion->unlock(); } Bool LatticeRegion::hasLock (FileLocker::LockType type) const { return itsRegion->hasLock (type); } void LatticeRegion::resync() { itsRegion->resync(); } void LatticeRegion::flush() { itsRegion->flush(); } void LatticeRegion::tempClose() { itsRegion->tempClose(); } void LatticeRegion::reopen() { itsRegion->reopen(); } IPosition LatticeRegion::shape() const { return itsSlicer.length(); } uInt LatticeRegion::ndim() const { return itsSlicer.ndim(); } size_t LatticeRegion::nelements() const { return itsRegion->nelements(); } LatticeIterInterface* LatticeRegion::makeIter (const LatticeNavigator& navigator, Bool useRef) const { return itsRegion->makeIter (navigator, useRef); } Bool LatticeRegion::doGetSlice (Array& buffer, const Slicer& section) { // When no mask at all, simply return all true. if (! hasMask()) { buffer.resize (section.length()); buffer = True; return False; } // Return the required section. /// LCRegion* reg = (LCRegion*)itsRegion; return itsRegion->doGetSlice (buffer, section); } void LatticeRegion::doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride) { AlwaysAssert (hasMask() && isWritable(), AipsError); itsRegion->putSlice (sourceBuffer, where, stride); } void LatticeRegion::set (const Bool& value) { AlwaysAssert (hasMask() && isWritable(), AipsError); itsRegion->set (value); } void LatticeRegion::apply (Bool (*function)(Bool)) { AlwaysAssert (hasMask() && isWritable(), AipsError); itsRegion->apply (function); } void LatticeRegion::apply (Bool (*function)(const Bool&)) { AlwaysAssert (hasMask() && isWritable(), AipsError); itsRegion->apply (function); } void LatticeRegion::apply (const Functional& function) { AlwaysAssert (hasMask() && isWritable(), AipsError); itsRegion->apply (function); } void LatticeRegion::putAt (const Bool& value, const IPosition& where) { AlwaysAssert (hasMask() && isWritable(), AipsError); itsRegion->putAt (value, where); } void LatticeRegion::copyData (const Lattice& from) { AlwaysAssert (hasMask() && isWritable(), AipsError); itsRegion->copyData (from); } Bool LatticeRegion::ok() const { return itsRegion->ok(); } Slicer LatticeRegion::convert (const Slicer& slicer) const { IPosition blc, trc, inc; IPosition shape = slicer.inferShapeFromSource (itsSlicer.length(), blc, trc, inc); const IPosition& start = itsSlicer.start(); const IPosition& incr = itsSlicer.stride(); uInt ndim = shape.nelements(); for (uInt i=0; i #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class LCRegion; // // An optionally strided region in a Lattice // // // // // //
      • LCRegion // // // A LatticeRegion is a lattice referencing a subset of another lattice // by means of a Slicer object. //
        It is useful when only a subset of a lattice needs to be accessed. //

        // When the LatticeRegion is created from a const Lattice object, // it is not writable, thus it can only be used as an rvalue. // // // // // //# //# class LatticeRegion: public Lattice { public: // The default constructor creates a LatticeRegion that is useless for just // about everything, except that it can be assigned to with the assignment // operator. LatticeRegion(); // Create from the given region. // The pointer to the parent can be 0. LatticeRegion (const LCRegion& region); // Create from the given region and take over the pointer. // This means the user should not delete the region object pointed to, // because it will be deleted by the LatticeRegion destructor. // The pointer to the parent is set to 0. LatticeRegion (LCRegion* region); // Construct from the given slicer. The lattice shape has to be // the lattice shape of the lattice where the region is taken from. LatticeRegion (const Slicer& slicer, const IPosition& latticeShape); // Copy constructor (reference semantics). LatticeRegion (const LatticeRegion& other); virtual ~LatticeRegion(); // Assignment (reference semantics). LatticeRegion& operator= (const LatticeRegion& other); // Make a copy of the object (reference semantics). virtual Lattice* clone() const; // Is the LatticeRegion writable? virtual Bool isWritable() const; // Has the region a mask? Bool hasMask() const; // Handle the (un)locking. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; // // Resynchronize the PagedArray object with the lattice file. // This function is only useful if no read-locking is used, ie. // if the table lock option is UserNoReadLocking or AutoNoReadLocking. // In that cases the table system does not acquire a read-lock, thus // does not synchronize itself automatically. virtual void resync(); // Flush the data (but do not unlock). virtual void flush(); // Temporarily close the lattice. // It will be reopened automatically on the next access. virtual void tempClose(); // Explicitly reopen the temporarily closed lattice. virtual void reopen(); // Get the LCRegion object describing the region. // Note that it does not contain strides, even if this LatticeRegion // object was constructed from a Slicer with strides. In that case // the region only defines the resulting shape. const LCRegion& region() const; // Get the Slicer object describing the region. // Note that it may contain strides. const Slicer& slicer() const; // Returns the shape of the LatticeRegion including all degenerate axes // (i.e. axes with a length of one). virtual IPosition shape() const; // Returns the number of axes in this LatticeRegion. This includes all // degenerate axes. virtual uInt ndim() const; // Returns the total number of elements in this LatticeRegion. virtual size_t nelements() const; // Check class internals - used for debugging. Should always return True virtual Bool ok() const; // This function is used by the LatticeIterator class to generate an // iterator of the correct type for this Lattice. Not recommended // for general use. virtual LatticeIterInterface* makeIter (const LatticeNavigator& navigator, Bool useRef) const; // Returns the maximum recommended number of pixels for a cursor. // This is the number of pixels in a tile. virtual uInt advisedMaxPixels() const; // Help the user pick a cursor for most efficient access. virtual IPosition doNiceCursorShape (uInt maxPixels) const; // Maximum size - not necessarily all used. In pixels. virtual uInt maximumCacheSize() const; // Set the maximum (allowed) cache size as indicated. virtual void setMaximumCacheSize (uInt howManyPixels); // Set the cache size as to "fit" the indicated path. virtual void setCacheSizeFromPath (const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath); // Set the actual cache size for this Array to be be big enough for the // indicated number of tiles. This cache is not shared with PagedArrays // in other rows and is always clipped to be less than the maximum value // set using the setMaximumCacheSize member function. // tiles. Tiles are cached using a first in first out algorithm. virtual void setCacheSizeInTiles (uInt howManyTiles); // Clears and frees up the caches, but the maximum allowed cache size is // unchanged from when setCacheSize was called virtual void clearCache(); // Report on cache success. virtual void showCacheStatistics (ostream& os) const; // The following "put" functions are described in detail in class // Lattice. // They'll throw an exception is no mask is available or if // the mask is not writable. // virtual void set (const Bool& value); virtual void apply (Bool (*function)(Bool)); virtual void apply (Bool (*function)(const Bool&)); virtual void apply (const Functional& function); virtual void putAt (const Bool& value, const IPosition& where); virtual void copyData (const Lattice& from); // // Convert positions to positions in the parent object. // Slicer convert (const Slicer& slicer) const; IPosition convert (const IPosition& position) const; // // Do the actual getting of the mask. virtual Bool doGetSlice (Array& buffer, const Slicer& section); // Do the actual putting of the mask. Only possible if region is writable. virtual void doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride); private: LCRegion* itsRegion; Slicer itsSlicer; Bool itsHasRegionMask; }; inline Bool LatticeRegion::hasMask() const { return itsHasRegionMask; } inline const LCRegion& LatticeRegion::region() const { return *itsRegion; } inline const Slicer& LatticeRegion::slicer() const { return itsSlicer; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LRegions/RegionType.cc000066400000000000000000000033331476623553700212540ustar00rootroot00000000000000//# RegionType.h: Define the various region types in an enum. //# Copyright (C) 1998,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { RegionType::AbsRelType RegionType::absRelTypeFromString(const String& absrel) { String str(absrel); str.upcase(); if(str.contains("ABS")) return RegionType::Abs; else if(str.contains("RELREF")) return RegionType::RelRef; else if(str.contains("RELCEN")) return RegionType::RelCen; else throw(AipsError(String("Undefined region type")+absrel)); } } casacore-3.7.1/lattices/LRegions/RegionType.h000066400000000000000000000052611476623553700211200ustar00rootroot00000000000000//# RegionType.h: Define the various region types in an enum. //# Copyright (C) 1998,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_REGIONTYPE_H #define LATTICES_REGIONTYPE_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //

        // Define the various region types. // // // // // // This class defines 2 enum's used by the region classes in module // Lattices and Images. // class RegionType { public: // Define the type of region. // The values are used in regionmanager(gui).g, so they should // not be changed. enum Type { // Not used yet. Invalid = -1, // Other type is not used yet. Other = 0, // lattice region (pixel coordinates) LC = 1, // image region (world coordinates) WC = 2, // array slicer (pixel based with optional stride) ArrSlicer = 3, // Number of recognized types only nRegionTypes }; // Define if a region is absolute or relative. // The values are used in regionmanager(gui).g, so they should // not be changed. enum AbsRelType { // absolute Abs = 1, // relative to reference pixel RelRef = 2, // relative to center RelCen = 3, //# relative to a direction //# RelDir = 4, // Number of recognized types only nAbsRelTypes }; static AbsRelType absRelTypeFromString(const String& absreltype); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LRegions/test/000077500000000000000000000000001476623553700176355ustar00rootroot00000000000000casacore-3.7.1/lattices/LRegions/test/CMakeLists.txt000066400000000000000000000007531476623553700224020ustar00rootroot00000000000000set (tests tLatticeRegion tLCComplement tLCConcatenation tLCDifference tLCEllipsoid tLCExtension tLCIntersection tLCLELMask tLCMask tLCPagedMask tLCPixelSet tLCPolygon tLCPolygon2 tLCRegion tLCSlicer tLCStretch tLCUnion ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_lattices) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/lattices/LRegions/test/tLCComplement.cc000066400000000000000000000112011476623553700226450ustar00rootroot00000000000000//# tLCComplement.cc: mechanical test of the LCComplement class //# Copyright (C) 1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include void doIt (const IPosition& latticeShape, const IPosition& start, const IPosition& end, const IPosition& center, Float radius) { uInt ndim = latticeShape.nelements(); LCEllipsoid cir (center, radius, latticeShape); LCBox box (start, end, latticeShape); LCComplement compl0 (cir); AlwaysAssertExit (compl0.hasMask()); AlwaysAssertExit (! compl0.isWritable()); cout << compl0.hasMask() << ' ' << endl; cout << compl0.boundingBox().start() << compl0.boundingBox().end() << compl0.boundingBox().length() << compl0.latticeShape() << endl; Array mask; compl0.getSlice (mask, IPosition(ndim,0), compl0.boundingBox().length(), IPosition(ndim,1)); cout << mask << endl; LCComplement compl1 (box); AlwaysAssertExit (compl1.hasMask()); AlwaysAssertExit (! compl1.isWritable()); Array mask1; compl1.getSlice (mask1, IPosition(ndim,0), compl1.boundingBox().length(), IPosition(ndim,1)); cout << mask1 << endl; { // Test cloning. LCRegion* complcop = compl0.cloneRegion(); AlwaysAssertExit (compl0.hasMask() == complcop->hasMask()); AlwaysAssertExit (compl0.boundingBox().start() == complcop->boundingBox().start()); AlwaysAssertExit (compl0.boundingBox().end() == complcop->boundingBox().end()); AlwaysAssertExit (compl0.boundingBox().stride() == complcop->boundingBox().stride()); AlwaysAssertExit (compl0.boundingBox().length() == complcop->boundingBox().length()); Array arr; complcop->getSlice (arr, IPosition(ndim,0), compl0.boundingBox().length(), IPosition(ndim,1)); AlwaysAssertExit (allEQ (arr, mask)); delete complcop; } { // Test persistency. LCRegion* complcop = (LCRegion::fromRecord (compl0.toRecord(""), "")); AlwaysAssertExit (compl0.hasMask() == complcop->hasMask()); AlwaysAssertExit (compl0.boundingBox().start() == complcop->boundingBox().start()); AlwaysAssertExit (compl0.boundingBox().end() == complcop->boundingBox().end()); AlwaysAssertExit (compl0.boundingBox().stride() == complcop->boundingBox().stride()); AlwaysAssertExit (compl0.boundingBox().length() == complcop->boundingBox().length()); Array arr; complcop->getSlice (arr, IPosition(ndim,0), compl0.boundingBox().length(), IPosition(ndim,1)); AlwaysAssertExit (allEQ (arr, mask)); delete complcop; } { // Test equality. LCComplement comp1(box); LCComplement comp2(comp1); AlwaysAssertExit (comp2 == comp1); LCComplement comp3(cir); AlwaysAssertExit (comp3 != comp1); } } int main() { try { doIt (IPosition (2,11,20), IPosition (2,3,4), IPosition (2,7,8), IPosition (2,5,10), 5.); doIt (IPosition (2,10,20), IPosition (2,3,4), IPosition (2,7,8), IPosition (2,4,16), 5.); } catch (std::exception& x) { cout << "Caught exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/lattices/LRegions/test/tLCComplement.out000066400000000000000000000054121476623553700230760ustar00rootroot000000000000001 [0, 0][10, 19][11, 20][11, 20] Axis Lengths: [11, 20] (NB: Matrix in Row/Column order) [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1] Axis Lengths: [11, 20] (NB: Matrix in Row/Column order) [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] 1 [0, 0][9, 19][10, 20][10, 20] Axis Lengths: [10, 20] (NB: Matrix in Row/Column order) [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1] Axis Lengths: [10, 20] (NB: Matrix in Row/Column order) [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] OK casacore-3.7.1/lattices/LRegions/test/tLCConcatenation.cc000066400000000000000000000144671476623553700233500ustar00rootroot00000000000000//# tLCConcatenation.cc: mechanical test of the LCConcatenation class //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include void doIt (const IPosition& latticeShape, const IPosition& start, const IPosition& end, const IPosition& center, Float radius) { uInt ndim = 1 + latticeShape.nelements(); LCBox box (start, end, latticeShape); LCEllipsoid cir (center, radius, latticeShape); LCEllipsoid cir1 (center, radius-1, latticeShape); PtrBlock ptrs(3); ptrs[0] = ○ ptrs[1] = &box; ptrs[2] = &cir1; // Extend along the last axis LCConcatenation inters (False, ptrs, ndim-1); AlwaysAssertExit (inters.hasMask()); AlwaysAssertExit (! inters.isWritable()); cout << inters.hasMask() << ' ' << endl; cout << inters.boundingBox().start() << inters.boundingBox().end() << inters.boundingBox().length() << inters.latticeShape() << endl; Array mask; inters.getSlice (mask, IPosition(ndim,0), inters.boundingBox().length(), IPosition(ndim,1)); cout << mask << endl; Array mask1; ptrs.resize (1, True, True); ptrs[0] = &box; LCConcatenation inters1 (False, ptrs, start.nelements()); AlwaysAssertExit (inters1.hasMask()); AlwaysAssertExit (! inters1.isWritable()); inters1.getSlice (mask1, IPosition(ndim,0), inters1.boundingBox().length(), IPosition(ndim,1)); AlwaysAssertExit (allEQ(mask1, True)); // Test slicing in various ways. // This is also a test for LCRegionMulti::findAreas. { IPosition start(ndim,0); IPosition end(inters.boundingBox().length() - 1); IPosition inc(ndim,2); inters.getSlice (mask1, Slicer(start, end, inc, Slicer::endIsLast)); AlwaysAssertExit (allEQ(mask1, mask(start, end, inc))); IPosition start2(start+5); start2(ndim-1) = 0; inters.getSlice (mask1, Slicer(start2, end, inc, Slicer::endIsLast)); AlwaysAssertExit (allEQ(mask1, mask(start2, end, inc))); IPosition end2(end-5); end2(ndim-1) = end(ndim-1); inters.getSlice (mask1, Slicer(start, end2, inc, Slicer::endIsLast)); AlwaysAssertExit (allEQ(mask1, mask(start, end2, inc))); IPosition inc2(inc+10); inc2(ndim-1) = 1; inters.getSlice (mask1, Slicer(start, end, inc2, Slicer::endIsLast)); AlwaysAssertExit (allEQ(mask1, mask(start, end, inc2))); inc2 = inc+8; inc2(ndim-1) = 1; inters.getSlice (mask1, Slicer(start, end, inc2, Slicer::endIsLast)); AlwaysAssertExit (allEQ(mask1, mask(start, end, inc2))); } { // Test cloning. LCRegion* interscop = inters.cloneRegion(); AlwaysAssertExit (inters.hasMask() == interscop->hasMask()); AlwaysAssertExit (inters.boundingBox().start() == interscop->boundingBox().start()); AlwaysAssertExit (inters.boundingBox().end() == interscop->boundingBox().end()); AlwaysAssertExit (inters.boundingBox().stride() == interscop->boundingBox().stride()); AlwaysAssertExit (inters.boundingBox().length() == interscop->boundingBox().length()); Array arr; interscop->getSlice (arr, IPosition(ndim,0), inters.boundingBox().length(), IPosition(ndim,1)); AlwaysAssertExit (allEQ (arr, mask)); delete interscop; } { // Test persistency. LCRegion* interscop = (LCRegion::fromRecord (inters.toRecord(""), "")); AlwaysAssertExit (inters.hasMask() == interscop->hasMask()); AlwaysAssertExit (inters.boundingBox().start() == interscop->boundingBox().start()); AlwaysAssertExit (inters.boundingBox().end() == interscop->boundingBox().end()); AlwaysAssertExit (inters.boundingBox().stride() == interscop->boundingBox().stride()); AlwaysAssertExit (inters.boundingBox().length() == interscop->boundingBox().length()); Array arr; interscop->getSlice (arr, IPosition(ndim,0), inters.boundingBox().length(), IPosition(ndim,1)); AlwaysAssertExit (allEQ (arr, mask)); delete interscop; } { // Test ordered equality. PtrBlock ptrs(2); ptrs[0] = &box; ptrs[1] = ○ LCConcatenation union1 (False, ptrs, start.nelements()); LCConcatenation union2 (False, ptrs, start.nelements()); AlwaysAssertExit (union1 == union2); } { // Test unordered equality. PtrBlock ptrs(2); ptrs[0] = &box; ptrs[1] = ○ LCConcatenation union1 (False, ptrs, start.nelements()); ptrs[0] = ○ ptrs[1] = &box; LCConcatenation union2 (False, ptrs, start.nelements()); AlwaysAssertExit (union1 == union2); } } int main() { try { doIt (IPosition (2,11,20), IPosition (2,3,4), IPosition (2,7,8), IPosition (2,5,10), 5.); doIt (IPosition (2,10,20), IPosition (2,3,4), IPosition (2,7,8), IPosition (2,4,16), 5.); } catch (std::exception& x) { cout << "Caught exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/lattices/LRegions/test/tLCConcatenation.out000066400000000000000000000071101476623553700235550ustar00rootroot000000000000001 [0, 4, 0][10, 15, 2][11, 12, 3][11, 20, 3] Ndim=3 Axis Lengths: [11, 12, 3] [0, 0, 0][0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 1, 0][0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0] [0, 2, 0][0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0] [0, 3, 0][0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0] [0, 4, 0][0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0] [0, 5, 0][0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0] [0, 6, 0][1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] [0, 7, 0][0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0] [0, 8, 0][0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0] [0, 9, 0][0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0] [0, 10, 0][0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0] [0, 11, 0][0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0] [0, 0, 1][0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0] [0, 1, 1][0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0] [0, 2, 1][0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0] [0, 3, 1][0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0] [0, 4, 1][0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0] [0, 5, 1][0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 6, 1][0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 7, 1][0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 8, 1][0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 9, 1][0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 10, 1][0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 11, 1][0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 0, 2][0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 1, 2][0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 2, 2][0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0] [0, 3, 2][0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0] [0, 4, 2][0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0] [0, 5, 2][0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0] [0, 6, 2][0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0] [0, 7, 2][0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0] [0, 8, 2][0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0] [0, 9, 2][0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0] [0, 10, 2][0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0] [0, 11, 2][0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 1 [0, 4, 0][9, 19, 2][10, 16, 3][10, 20, 3] Ndim=3 Axis Lengths: [10, 16, 3] [0, 0, 0][0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 1, 0][0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 2, 0][0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 3, 0][0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 4, 0][0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 5, 0][0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 6, 0][0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 7, 0][0, 0, 0, 0, 1, 0, 0, 0, 0, 0] [0, 8, 0][0, 1, 1, 1, 1, 1, 1, 1, 0, 0] [0, 9, 0][1, 1, 1, 1, 1, 1, 1, 1, 1, 0] [0, 10, 0][1, 1, 1, 1, 1, 1, 1, 1, 1, 0] [0, 11, 0][1, 1, 1, 1, 1, 1, 1, 1, 1, 0] [0, 12, 0][1, 1, 1, 1, 1, 1, 1, 1, 1, 1] [0, 13, 0][1, 1, 1, 1, 1, 1, 1, 1, 1, 0] [0, 14, 0][1, 1, 1, 1, 1, 1, 1, 1, 1, 0] [0, 15, 0][1, 1, 1, 1, 1, 1, 1, 1, 1, 0] [0, 0, 1][0, 0, 0, 1, 1, 1, 1, 1, 0, 0] [0, 1, 1][0, 0, 0, 1, 1, 1, 1, 1, 0, 0] [0, 2, 1][0, 0, 0, 1, 1, 1, 1, 1, 0, 0] [0, 3, 1][0, 0, 0, 1, 1, 1, 1, 1, 0, 0] [0, 4, 1][0, 0, 0, 1, 1, 1, 1, 1, 0, 0] [0, 5, 1][0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 6, 1][0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 7, 1][0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 8, 1][0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 9, 1][0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 10, 1][0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 11, 1][0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 12, 1][0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 13, 1][0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 14, 1][0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 15, 1][0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 0, 2][0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 1, 2][0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 2, 2][0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 3, 2][0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 4, 2][0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 5, 2][0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 6, 2][0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 7, 2][0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 8, 2][0, 0, 0, 0, 1, 0, 0, 0, 0, 0] [0, 9, 2][0, 0, 1, 1, 1, 1, 1, 0, 0, 0] [0, 10, 2][0, 1, 1, 1, 1, 1, 1, 1, 0, 0] [0, 11, 2][0, 1, 1, 1, 1, 1, 1, 1, 0, 0] [0, 12, 2][1, 1, 1, 1, 1, 1, 1, 1, 1, 0] [0, 13, 2][0, 1, 1, 1, 1, 1, 1, 1, 0, 0] [0, 14, 2][0, 1, 1, 1, 1, 1, 1, 1, 0, 0] [0, 15, 2][0, 0, 1, 1, 1, 1, 1, 0, 0, 0] OK casacore-3.7.1/lattices/LRegions/test/tLCDifference.cc000066400000000000000000000117561476623553700226130ustar00rootroot00000000000000//# tLCDifference.cc: mechanical test of the LCDifference class //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include void doIt (const IPosition& latticeShape, const IPosition& start, const IPosition& end, const IPosition& center, Float radius) { uInt ndim = latticeShape.nelements(); LCBox box (start, end, latticeShape); LCEllipsoid cir (center, radius, latticeShape); LCDifference inters (box, cir); AlwaysAssertExit (inters.hasMask()); AlwaysAssertExit (! inters.isWritable()); cout << inters.hasMask() << ' ' << endl; cout << inters.boundingBox().start() << inters.boundingBox().end() << inters.boundingBox().length() << inters.latticeShape() << endl; Array mask; inters.getSlice (mask, IPosition(ndim,0), inters.boundingBox().length(), IPosition(ndim,1)); cout << mask << endl; { LCDifference inters (cir, box); AlwaysAssertExit (inters.hasMask()); AlwaysAssertExit (! inters.isWritable()); cout << inters.hasMask() << ' ' << endl; cout << inters.boundingBox().start() << inters.boundingBox().end() << inters.boundingBox().length() << inters.latticeShape() << endl; Array mask; inters.getSlice (mask, IPosition(ndim,0), inters.boundingBox().length(), IPosition(ndim,1)); cout << mask << endl; } { // Test cloning. LCRegion* interscop = inters.cloneRegion(); AlwaysAssertExit (inters.hasMask() == interscop->hasMask()); AlwaysAssertExit (inters.boundingBox().start() == interscop->boundingBox().start()); AlwaysAssertExit (inters.boundingBox().end() == interscop->boundingBox().end()); AlwaysAssertExit (inters.boundingBox().stride() == interscop->boundingBox().stride()); AlwaysAssertExit (inters.boundingBox().length() == interscop->boundingBox().length()); Array arr; interscop->getSlice (arr, IPosition(ndim,0), inters.boundingBox().length(), IPosition(ndim,1)); AlwaysAssertExit (allEQ (arr, mask)); delete interscop; } { // Test persistency. LCRegion* interscop = (LCRegion::fromRecord (inters.toRecord(""), "")); AlwaysAssertExit (inters.hasMask() == interscop->hasMask()); AlwaysAssertExit (inters.boundingBox().start() == interscop->boundingBox().start()); AlwaysAssertExit (inters.boundingBox().end() == interscop->boundingBox().end()); AlwaysAssertExit (inters.boundingBox().stride() == interscop->boundingBox().stride()); AlwaysAssertExit (inters.boundingBox().length() == interscop->boundingBox().length()); Array arr; interscop->getSlice (arr, IPosition(ndim,0), inters.boundingBox().length(), IPosition(ndim,1)); AlwaysAssertExit (allEQ (arr, mask)); delete interscop; } { // Test ordered equality. LCDifference diff1(box, cir); LCDifference diff2(diff1); AlwaysAssertExit (diff1 == diff2); } { // Test unordered equality. The bounding boxes will differ. LCDifference diff1(box, cir); LCDifference diff2(cir, box); AlwaysAssertExit (diff1 != diff2); } } int main() { try { doIt (IPosition (2,11,20), IPosition (2,3,4), IPosition (2,7,8), IPosition (2,5,10), 5.); doIt (IPosition (2,10,20), IPosition (2,3,4), IPosition (2,7,8), IPosition (2,4,16), 5.); } catch (std::exception& x) { cout << "Caught exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/lattices/LRegions/test/tLCDifference.out000066400000000000000000000021701476623553700230230ustar00rootroot000000000000001 [3, 4][7, 8][5, 5][11, 20] Axis Lengths: [5, 5] (NB: Matrix in Row/Column order) [1, 1, 0, 0, 0 1, 1, 0, 0, 0 1, 0, 0, 0, 0 1, 1, 0, 0, 0 1, 1, 0, 0, 0] 1 [0, 5][10, 15][11, 11][11, 20] Axis Lengths: [11, 11] (NB: Matrix in Row/Column order) [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0] 1 [3, 4][7, 8][5, 5][10, 20] Axis Lengths: [5, 5] (NB: Matrix in Row/Column order) [1, 1, 1, 1, 1 1, 1, 1, 1, 1 1, 1, 1, 1, 1 1, 1, 1, 1, 1 1, 1, 1, 1, 1] 1 [0, 11][9, 19][10, 9][10, 20] Axis Lengths: [10, 9] (NB: Matrix in Row/Column order) [0, 0, 1, 1, 1, 1, 1, 1, 1 0, 1, 1, 1, 1, 1, 1, 1, 1 0, 1, 1, 1, 1, 1, 1, 1, 1 0, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1, 1 0, 1, 1, 1, 1, 1, 1, 1, 1 0, 1, 1, 1, 1, 1, 1, 1, 1 0, 1, 1, 1, 1, 1, 1, 1, 1 0, 0, 1, 1, 1, 1, 1, 1, 1 0, 0, 0, 0, 0, 1, 0, 0, 0] OK casacore-3.7.1/lattices/LRegions/test/tLCEllipsoid.cc000066400000000000000000000201251476623553700224730ustar00rootroot00000000000000//# tLCPolygon.cc: Test program for LCPolygon class //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include void show(const LCEllipsoid& ellipse) { Array mask = ellipse.get(); IPosition shape = mask.shape(); IPosition index = shape-1; uInt j=0; while(True) { for (Int i=0; i(LCEllipsoid::fromRecord(ellipse.toRecord(""), "")); AlwaysAssert(ellipse == *copy, AipsError); near(ellipse.theta(), copy->theta()); delete copy; Float theta2 = theta + M_PI; LCEllipsoid ellipse2( xcenter, ycenter, major, minor, theta2, latticeShape ); AlwaysAssert(ellipse == ellipse2, AipsError); near(ellipse.theta(), ellipse2.theta()); Float theta3 = theta - M_PI; LCEllipsoid ellipse3( xcenter, ycenter, major, minor, theta3, latticeShape ); AlwaysAssert(ellipse == ellipse3, AipsError); near(ellipse.theta(), ellipse3.theta()); } { Float theta = 0; Float xcenter = 30; Float ycenter = 30; Vector center(2,xcenter); center[1] = ycenter; IPosition latticeShape(2,60); Float major = 20; Float minor = 10; Vector radii(2, major); radii[1] = minor; LCEllipsoid ellipse( xcenter, ycenter, major, minor, theta, latticeShape ); LCEllipsoid ellipse2( center, radii, latticeShape ); show(ellipse); show(ellipse2); LCEllipsoid *copy = dynamic_cast(LCEllipsoid::fromRecord(ellipse2.toRecord(""), "")); AlwaysAssert(ellipse == ellipse2, AipsError); AlwaysAssert(ellipse == *copy, AipsError); near(ellipse.theta(), ellipse2.theta()); delete copy; Float theta2 = M_PI/2; LCEllipsoid ellipse3( xcenter, ycenter, major, minor, theta2, latticeShape ); radii[0] = minor; radii[1] = major; LCEllipsoid ellipse4( center, radii, latticeShape ); AlwaysAssert(ellipse3 == ellipse4, AipsError); near(ellipse3.theta(), ellipse4.theta()); } { Float theta = M_PI/4; Float xcenter = 40; Float ycenter = 40; IPosition latticeShape(2,60); Float major = 10; Float minor = 5; // off center ellipse LCEllipsoid ellipse( xcenter, ycenter, major, minor, theta, latticeShape ); show(ellipse); } { // all of ellipse outside lattice Float xcenter = 80; Float ycenter = 80; Vector center(2,xcenter); center[1] = ycenter; IPosition latticeShape(2,60); Float major = 20; Float minor = 10; Vector radii(2, major); radii[1] = minor; Bool thrown = False; try { LCEllipsoid e0(center, radii, latticeShape); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); // 2-D with non-zero theta, test exception is thrown from _define2D() // since ellipse totally outside lattice xcenter = 40; ycenter = 80; major = 40; minor = 10; // 5 degrees Float theta = M_PI/36; thrown = False; try { LCEllipsoid ellipse( xcenter, ycenter, major, minor, theta, latticeShape ); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); } { // center outside lattice, but part of the ellipse in inside // lattice Float xcenter = -10; Float ycenter = -10; Vector center(2,xcenter); center[1] = ycenter; IPosition latticeShape(2,60); Float major = 30; Float minor = 20; Vector radii(2, major); radii[1] = minor; LCEllipsoid ellipse(center, radii, latticeShape); show(ellipse); center[0] = 69; LCEllipsoid ellipse1(center, radii, latticeShape); show(ellipse1); center[1] = 69; LCEllipsoid ellipse2(center, radii, latticeShape); show(ellipse2); center[0] = -10; LCEllipsoid ellipse3(center, radii, latticeShape); show(ellipse3); Float theta = M_PI/4; major = 36.01; minor = 16.01; xcenter = -1; ycenter = -1; LCEllipsoid ellipse4( xcenter, ycenter, major, minor, theta, latticeShape ); show(ellipse4); xcenter = 60; ycenter = -1; LCEllipsoid ellipse5( xcenter, ycenter, major, minor, theta, latticeShape ); show(ellipse5); xcenter = 60; ycenter = 60; LCEllipsoid ellipse6( xcenter, ycenter, major, minor, theta, latticeShape ); show(ellipse6); xcenter = -1; ycenter = 60; LCEllipsoid ellipse7( xcenter, ycenter, major, minor, theta, latticeShape ); show(ellipse7); } } catch (const std::exception& x) { cout << "Caught exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/lattices/LRegions/test/tLCEllipsoid.out000066400000000000000000000536331476623553700227270ustar00rootroot000000000000000 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 0 0 0 0 0 0 0 0 [40, 40] 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 0 0 0 0 0 0 0 0 0 [40, 39] 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 1 0 0 0 0 0 0 0 0 0 0 0 0 0 [40, 38] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 [40, 37] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 [40, 36] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 [40, 35] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 [40, 34] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 [40, 33] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 [40, 32] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 [40, 31] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 [40, 30] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 [40, 29] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 [40, 28] 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 [40, 27] 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 [40, 26] 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 [40, 25] 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 [40, 24] 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 [40, 23] 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 [40, 22] 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 [40, 21] 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 [40, 20] 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 [40, 19] 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 [40, 18] 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 [40, 17] 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 [40, 16] 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 [40, 15] 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 [40, 14] 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 [40, 13] 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [40, 12] 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [40, 11] 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [40, 10] 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [40, 9] 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [40, 8] 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [40, 7] 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [40, 6] 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [40, 5] 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [40, 4] 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [40, 3] 0 0 0 0 0 0 0 0 0 0 0 0 0 1 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 [40, 2] 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 0 0 0 0 0 0 0 0 0 [40, 1] 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 0 0 0 0 0 0 0 0 0 [40, 0] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [40, 20] 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 [40, 19] 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 [40, 18] 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 [40, 17] 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 [40, 16] 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 [40, 15] 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 [40, 14] 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 [40, 13] 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 [40, 12] 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 [40, 11] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [40, 10] 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 [40, 9] 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 [40, 8] 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 [40, 7] 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 [40, 6] 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 [40, 5] 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 [40, 4] 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 [40, 3] 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 [40, 2] 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 [40, 1] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [40, 0] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [40, 20] 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 [40, 19] 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 [40, 18] 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 [40, 17] 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 [40, 16] 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 [40, 15] 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 [40, 14] 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 [40, 13] 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 [40, 12] 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 [40, 11] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [40, 10] 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 [40, 9] 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 [40, 8] 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 [40, 7] 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 [40, 6] 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 [40, 5] 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 [40, 4] 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 [40, 3] 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 [40, 2] 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 [40, 1] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [40, 0] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [20, 20] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [20, 19] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [20, 18] 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 [20, 17] 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 [20, 16] 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 0 0 0 [20, 15] 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 0 0 [20, 14] 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 [20, 13] 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 [20, 12] 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 [20, 11] 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 [20, 10] 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 [20, 9] 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 [20, 8] 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 [20, 7] 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 [20, 6] 0 0 0 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 [20, 5] 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 [20, 4] 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 [20, 3] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [20, 2] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [20, 1] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [20, 0] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [20, 10] 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [20, 9] 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [20, 8] 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [20, 7] 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 [20, 6] 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 [20, 5] 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 [20, 4] 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 [20, 3] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 [20, 2] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 [20, 1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 [20, 0] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [20, 10] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [20, 9] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 [20, 8] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 [20, 7] 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 [20, 6] 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 [20, 5] 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 [20, 4] 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 [20, 3] 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [20, 2] 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [20, 1] 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [20, 0] 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [20, 10] 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [20, 9] 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [20, 8] 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 [20, 7] 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 [20, 6] 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 [20, 5] 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 [20, 4] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 [20, 3] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 [20, 2] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [20, 1] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [20, 0] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 [20, 10] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 [20, 9] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 [20, 8] 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 [20, 7] 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 [20, 6] 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 [20, 5] 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 [20, 4] 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [20, 3] 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [20, 2] 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [20, 1] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [20, 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 0 0 0 0 0 [35, 35] 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 0 0 0 0 [35, 34] 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 0 0 0 0 [35, 33] 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 0 0 0 0 [35, 32] 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 0 0 0 0 [35, 31] 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 0 0 0 0 [35, 30] 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 0 0 0 0 [35, 29] 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 0 0 0 0 [35, 28] 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 0 0 0 0 [35, 27] 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 [35, 26] 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 [35, 25] 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 [35, 24] 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 [35, 23] 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 [35, 22] 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 [35, 21] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 [35, 20] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 [35, 19] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 [35, 18] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 [35, 17] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 [35, 16] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 [35, 15] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 [35, 14] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 [35, 13] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 [35, 12] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 [35, 11] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 [35, 10] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 [35, 9] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 [35, 8] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 [35, 7] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 [35, 6] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 [35, 5] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 [35, 4] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 [35, 3] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [35, 2] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [35, 1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [35, 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 0 0 0 0 0 [35, 35] 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 0 0 0 0 [35, 34] 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 0 0 0 0 [35, 33] 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 0 0 0 0 [35, 32] 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 0 0 0 0 [35, 31] 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 0 0 0 0 [35, 30] 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 0 0 0 0 [35, 29] 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 0 0 0 0 [35, 28] 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 0 0 0 0 [35, 27] 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 0 0 0 0 [35, 26] 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 0 0 0 0 [35, 25] 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 0 0 0 0 [35, 24] 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 0 0 0 0 [35, 23] 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 0 0 0 0 [35, 22] 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 0 0 0 0 [35, 21] 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 0 0 0 0 [35, 20] 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 0 0 0 1 [35, 19] 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 0 0 1 1 [35, 18] 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 0 1 1 1 [35, 17] 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 1 1 1 1 1 [35, 16] 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 1 1 1 1 1 1 [35, 15] 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 1 1 1 1 1 1 1 [35, 14] 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 1 1 1 1 1 1 1 1 [35, 13] 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 1 1 1 1 1 1 1 1 1 [35, 12] 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 1 1 1 1 1 1 1 1 1 1 [35, 11] 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 1 1 1 1 1 1 1 1 1 1 1 [35, 10] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 [35, 9] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 [35, 8] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [35, 7] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [35, 6] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [35, 5] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [35, 4] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [35, 3] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [35, 2] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [35, 1] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [35, 0] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [35, 35] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [35, 34] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [35, 33] 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [35, 32] 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [35, 31] 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [35, 30] 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [35, 29] 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [35, 28] 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [35, 27] 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [35, 26] 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [35, 25] 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [35, 24] 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [35, 23] 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [35, 22] 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [35, 21] 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [35, 20] 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [35, 19] 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [35, 18] 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [35, 17] 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [35, 16] 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 [35, 15] 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 [35, 14] 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 [35, 13] 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 [35, 12] 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 [35, 11] 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 [35, 10] 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 [35, 9] 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 0 0 0 0 [35, 8] 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 0 0 0 0 [35, 7] 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 0 0 0 0 [35, 6] 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 0 0 0 0 [35, 5] 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 0 0 0 0 [35, 4] 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 0 0 0 0 [35, 3] 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 0 0 0 0 [35, 2] 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 0 0 0 0 [35, 1] 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 0 0 0 0 [35, 0] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [35, 35] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [35, 34] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [35, 33] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [35, 32] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [35, 31] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [35, 30] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [35, 29] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [35, 28] 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [35, 27] 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [35, 26] 1 1 1 1 1 1 1 1 1 1 1 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 [35, 25] 1 1 1 1 1 1 1 1 1 1 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 [35, 24] 1 1 1 1 1 1 1 1 1 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 [35, 23] 1 1 1 1 1 1 1 1 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 [35, 22] 1 1 1 1 1 1 1 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 [35, 21] 1 1 1 1 1 1 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 [35, 20] 1 1 1 1 1 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 [35, 19] 1 1 1 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 0 [35, 18] 1 1 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 0 0 [35, 17] 1 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 0 0 0 [35, 16] 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 0 0 0 0 [35, 15] 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 0 0 0 0 [35, 14] 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 0 0 0 0 [35, 13] 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 0 0 0 0 [35, 12] 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 0 0 0 0 [35, 11] 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 0 0 0 0 [35, 10] 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 0 0 0 0 [35, 9] 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 0 0 0 0 [35, 8] 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 0 0 0 0 [35, 7] 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 0 0 0 0 [35, 6] 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 0 0 0 0 [35, 5] 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 0 0 0 0 [35, 4] 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 0 0 0 0 [35, 3] 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 0 0 0 0 [35, 2] 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 0 0 0 0 [35, 1] 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 0 0 0 0 [35, 0] OK casacore-3.7.1/lattices/LRegions/test/tLCExtension.cc000066400000000000000000000132011476623553700225200ustar00rootroot00000000000000//# tLCExtension.cc: Test program for LCExtension class //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include void doIt (const LCRegion& region, const IPosition& axes, const IPosition& blc, const IPosition& trc, const IPosition& latticeShape) { try { LCExtension prism (region, axes, LCBox(blc, trc, latticeShape)); AlwaysAssertExit (prism.hasMask() == region.hasMask()); AlwaysAssertExit (! prism.isWritable()); Array regmask; uInt ndimr = region.boundingBox().ndim(); uInt ndim = ndimr + latticeShape.nelements(); ((LCRegion&)region).getSlice (regmask, IPosition(ndimr,0), region.boundingBox().length(), IPosition(ndimr,1)); cout << regmask << endl; Array mask; prism.getSlice (mask, IPosition(ndim,0), prism.boundingBox().length(), IPosition(ndim,1)); cout << mask << endl; cout << prism.hasMask() << ' ' << endl; cout << prism.boundingBox().start() << prism.boundingBox().end() << prism.boundingBox().length() << prism.latticeShape() << endl; cout << prism.extendAxes() << prism.extendBox().blc() << prism.extendBox().trc() << endl; { // Test cloning. LCRegion* prismcop = prism.cloneRegion(); AlwaysAssertExit (prism.hasMask() == prismcop->hasMask()); AlwaysAssertExit (prism.boundingBox().start() == prismcop->boundingBox().start()); AlwaysAssertExit (prism.boundingBox().end() == prismcop->boundingBox().end()); AlwaysAssertExit (prism.boundingBox().stride() == prismcop->boundingBox().stride()); AlwaysAssertExit (prism.boundingBox().length() == prismcop->boundingBox().length()); Array arr; prismcop->getSlice (arr, IPosition(ndim,0), prism.boundingBox().length(), IPosition(ndim,1)); AlwaysAssertExit (allEQ (arr, mask)); delete prismcop; } { // Test persistency. LCRegion* prismcop = LCRegion::fromRecord (prism.toRecord(""), ""); AlwaysAssertExit (prism.hasMask() == prismcop->hasMask()); AlwaysAssertExit (prism.boundingBox().start() == prismcop->boundingBox().start()); AlwaysAssertExit (prism.boundingBox().end() == prismcop->boundingBox().end()); AlwaysAssertExit (prism.boundingBox().stride() == prismcop->boundingBox().stride()); AlwaysAssertExit (prism.boundingBox().length() == prismcop->boundingBox().length()); Array arr; prismcop->getSlice (arr, IPosition(ndim,0), prism.boundingBox().length(), IPosition(ndim,1)); AlwaysAssertExit (allEQ (arr, mask)); delete prismcop; } { // Test ordered equality. LCExtension prism2(prism); AlwaysAssertExit (prism2 == prism); } { // Test unordered equality. LCExtension prism2 (region, axes, LCBox(blc-1, trc, latticeShape)); AlwaysAssertExit (prism2 != prism); } } catch (std::exception& x) { cout << x.what() << endl; } } int main() { try { // A simple box (having no mask). LCBox box (IPosition(2,1,4), IPosition(2,5,6), IPosition(2,12,14)); // A cross-like figure. Vector x(4), y(4); x(0)=3; y(0)=3; x(1)=9; y(1)=3; x(2)=3; y(2)=8; x(3)=9; y(3)=8; LCPolygon polygon(x, y, IPosition(2,12,14)); doIt (box, IPosition(1,1), IPosition(1,2), IPosition(1,3), IPosition(1,20)); doIt (polygon, IPosition(1,2), IPosition(1,2), IPosition(1,3), IPosition(1,4)); doIt (polygon, IPosition(1,1), IPosition(1,2), IPosition(1,3), IPosition(1,20)); doIt (polygon, IPosition(2,2,0), IPosition(2,0,2), IPosition(2,2,3), IPosition(2,10,20)); // Trc outside lattice, is silently adjusted doIt (polygon, IPosition(1,2), IPosition(1,2), IPosition(1,5), IPosition(1,3)); // Error; no extendaxes doIt (polygon, IPosition(), IPosition(1,2), IPosition(1,3), IPosition(1,20)); // Error; #extendAxes mismatches blc/trc doIt (polygon, IPosition(2,1,2), IPosition(1,2), IPosition(1,3), IPosition(1,20)); // Error; incorrect order of extendAxes doIt (polygon, IPosition(2,1), IPosition(2,2), IPosition(2,3), IPosition(2,20)); } catch (std::exception& x) { cout << "Caught exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/lattices/LRegions/test/tLCExtension.out000066400000000000000000000115551476623553700227540ustar00rootroot00000000000000Axis Lengths: [5, 3] (NB: Matrix in Row/Column order) [1, 1, 1 1, 1, 1 1, 1, 1 1, 1, 1 1, 1, 1] Ndim=3 Axis Lengths: [5, 2, 3] [0, 0, 0][1, 1, 1, 1, 1] [0, 1, 0][1, 1, 1, 1, 1] [0, 0, 1][1, 1, 1, 1, 1] [0, 1, 1][1, 1, 1, 1, 1] [0, 0, 2][1, 1, 1, 1, 1] [0, 1, 2][1, 1, 1, 1, 1] 0 [1, 2, 4][5, 3, 6][5, 2, 3][12, 20, 14] [1][2][3] Axis Lengths: [7, 6] (NB: Matrix in Row/Column order) [1, 0, 0, 0, 0, 1 1, 0, 0, 0, 0, 1 1, 1, 0, 0, 1, 1 1, 1, 1, 1, 1, 1 1, 1, 0, 0, 1, 1 1, 0, 0, 0, 0, 1 1, 0, 0, 0, 0, 1] Ndim=3 Axis Lengths: [7, 6, 2] [0, 0, 0][1, 1, 1, 1, 1, 1, 1] [0, 1, 0][0, 0, 1, 1, 1, 0, 0] [0, 2, 0][0, 0, 0, 1, 0, 0, 0] [0, 3, 0][0, 0, 0, 1, 0, 0, 0] [0, 4, 0][0, 0, 1, 1, 1, 0, 0] [0, 5, 0][1, 1, 1, 1, 1, 1, 1] [0, 0, 1][1, 1, 1, 1, 1, 1, 1] [0, 1, 1][0, 0, 1, 1, 1, 0, 0] [0, 2, 1][0, 0, 0, 1, 0, 0, 0] [0, 3, 1][0, 0, 0, 1, 0, 0, 0] [0, 4, 1][0, 0, 1, 1, 1, 0, 0] [0, 5, 1][1, 1, 1, 1, 1, 1, 1] 1 [3, 3, 2][9, 8, 3][7, 6, 2][12, 14, 4] [2][2][3] Axis Lengths: [7, 6] (NB: Matrix in Row/Column order) [1, 0, 0, 0, 0, 1 1, 0, 0, 0, 0, 1 1, 1, 0, 0, 1, 1 1, 1, 1, 1, 1, 1 1, 1, 0, 0, 1, 1 1, 0, 0, 0, 0, 1 1, 0, 0, 0, 0, 1] Ndim=3 Axis Lengths: [7, 2, 6] [0, 0, 0][1, 1, 1, 1, 1, 1, 1] [0, 1, 0][1, 1, 1, 1, 1, 1, 1] [0, 0, 1][0, 0, 1, 1, 1, 0, 0] [0, 1, 1][0, 0, 1, 1, 1, 0, 0] [0, 0, 2][0, 0, 0, 1, 0, 0, 0] [0, 1, 2][0, 0, 0, 1, 0, 0, 0] [0, 0, 3][0, 0, 0, 1, 0, 0, 0] [0, 1, 3][0, 0, 0, 1, 0, 0, 0] [0, 0, 4][0, 0, 1, 1, 1, 0, 0] [0, 1, 4][0, 0, 1, 1, 1, 0, 0] [0, 0, 5][1, 1, 1, 1, 1, 1, 1] [0, 1, 5][1, 1, 1, 1, 1, 1, 1] 1 [3, 2, 3][9, 3, 8][7, 2, 6][12, 20, 14] [1][2][3] Axis Lengths: [7, 6] (NB: Matrix in Row/Column order) [1, 0, 0, 0, 0, 1 1, 0, 0, 0, 0, 1 1, 1, 0, 0, 1, 1 1, 1, 1, 1, 1, 1 1, 1, 0, 0, 1, 1 1, 0, 0, 0, 0, 1 1, 0, 0, 0, 0, 1] Ndim=4 Axis Lengths: [2, 7, 3, 6] [0, 0, 0, 0][1, 1] [0, 1, 0, 0][1, 1] [0, 2, 0, 0][1, 1] [0, 3, 0, 0][1, 1] [0, 4, 0, 0][1, 1] [0, 5, 0, 0][1, 1] [0, 6, 0, 0][1, 1] [0, 0, 1, 0][1, 1] [0, 1, 1, 0][1, 1] [0, 2, 1, 0][1, 1] [0, 3, 1, 0][1, 1] [0, 4, 1, 0][1, 1] [0, 5, 1, 0][1, 1] [0, 6, 1, 0][1, 1] [0, 0, 2, 0][1, 1] [0, 1, 2, 0][1, 1] [0, 2, 2, 0][1, 1] [0, 3, 2, 0][1, 1] [0, 4, 2, 0][1, 1] [0, 5, 2, 0][1, 1] [0, 6, 2, 0][1, 1] [0, 0, 0, 1][0, 0] [0, 1, 0, 1][0, 0] [0, 2, 0, 1][1, 1] [0, 3, 0, 1][1, 1] [0, 4, 0, 1][1, 1] [0, 5, 0, 1][0, 0] [0, 6, 0, 1][0, 0] [0, 0, 1, 1][0, 0] [0, 1, 1, 1][0, 0] [0, 2, 1, 1][1, 1] [0, 3, 1, 1][1, 1] [0, 4, 1, 1][1, 1] [0, 5, 1, 1][0, 0] [0, 6, 1, 1][0, 0] [0, 0, 2, 1][0, 0] [0, 1, 2, 1][0, 0] [0, 2, 2, 1][1, 1] [0, 3, 2, 1][1, 1] [0, 4, 2, 1][1, 1] [0, 5, 2, 1][0, 0] [0, 6, 2, 1][0, 0] [0, 0, 0, 2][0, 0] [0, 1, 0, 2][0, 0] [0, 2, 0, 2][0, 0] [0, 3, 0, 2][1, 1] [0, 4, 0, 2][0, 0] [0, 5, 0, 2][0, 0] [0, 6, 0, 2][0, 0] [0, 0, 1, 2][0, 0] [0, 1, 1, 2][0, 0] [0, 2, 1, 2][0, 0] [0, 3, 1, 2][1, 1] [0, 4, 1, 2][0, 0] [0, 5, 1, 2][0, 0] [0, 6, 1, 2][0, 0] [0, 0, 2, 2][0, 0] [0, 1, 2, 2][0, 0] [0, 2, 2, 2][0, 0] [0, 3, 2, 2][1, 1] [0, 4, 2, 2][0, 0] [0, 5, 2, 2][0, 0] [0, 6, 2, 2][0, 0] [0, 0, 0, 3][0, 0] [0, 1, 0, 3][0, 0] [0, 2, 0, 3][0, 0] [0, 3, 0, 3][1, 1] [0, 4, 0, 3][0, 0] [0, 5, 0, 3][0, 0] [0, 6, 0, 3][0, 0] [0, 0, 1, 3][0, 0] [0, 1, 1, 3][0, 0] [0, 2, 1, 3][0, 0] [0, 3, 1, 3][1, 1] [0, 4, 1, 3][0, 0] [0, 5, 1, 3][0, 0] [0, 6, 1, 3][0, 0] [0, 0, 2, 3][0, 0] [0, 1, 2, 3][0, 0] [0, 2, 2, 3][0, 0] [0, 3, 2, 3][1, 1] [0, 4, 2, 3][0, 0] [0, 5, 2, 3][0, 0] [0, 6, 2, 3][0, 0] [0, 0, 0, 4][0, 0] [0, 1, 0, 4][0, 0] [0, 2, 0, 4][1, 1] [0, 3, 0, 4][1, 1] [0, 4, 0, 4][1, 1] [0, 5, 0, 4][0, 0] [0, 6, 0, 4][0, 0] [0, 0, 1, 4][0, 0] [0, 1, 1, 4][0, 0] [0, 2, 1, 4][1, 1] [0, 3, 1, 4][1, 1] [0, 4, 1, 4][1, 1] [0, 5, 1, 4][0, 0] [0, 6, 1, 4][0, 0] [0, 0, 2, 4][0, 0] [0, 1, 2, 4][0, 0] [0, 2, 2, 4][1, 1] [0, 3, 2, 4][1, 1] [0, 4, 2, 4][1, 1] [0, 5, 2, 4][0, 0] [0, 6, 2, 4][0, 0] [0, 0, 0, 5][1, 1] [0, 1, 0, 5][1, 1] [0, 2, 0, 5][1, 1] [0, 3, 0, 5][1, 1] [0, 4, 0, 5][1, 1] [0, 5, 0, 5][1, 1] [0, 6, 0, 5][1, 1] [0, 0, 1, 5][1, 1] [0, 1, 1, 5][1, 1] [0, 2, 1, 5][1, 1] [0, 3, 1, 5][1, 1] [0, 4, 1, 5][1, 1] [0, 5, 1, 5][1, 1] [0, 6, 1, 5][1, 1] [0, 0, 2, 5][1, 1] [0, 1, 2, 5][1, 1] [0, 2, 2, 5][1, 1] [0, 3, 2, 5][1, 1] [0, 4, 2, 5][1, 1] [0, 5, 2, 5][1, 1] [0, 6, 2, 5][1, 1] 1 [2, 3, 0, 3][3, 9, 2, 8][2, 7, 3, 6][20, 12, 10, 14] [0, 2][2, 0][3, 2] Axis Lengths: [7, 6] (NB: Matrix in Row/Column order) [1, 0, 0, 0, 0, 1 1, 0, 0, 0, 0, 1 1, 1, 0, 0, 1, 1 1, 1, 1, 1, 1, 1 1, 1, 0, 0, 1, 1 1, 0, 0, 0, 0, 1 1, 0, 0, 0, 0, 1] Ndim=3 Axis Lengths: [7, 6, 1] [0, 0, 0][1, 1, 1, 1, 1, 1, 1] [0, 1, 0][0, 0, 1, 1, 1, 0, 0] [0, 2, 0][0, 0, 0, 1, 0, 0, 0] [0, 3, 0][0, 0, 0, 1, 0, 0, 0] [0, 4, 0][0, 0, 1, 1, 1, 0, 0] [0, 5, 0][1, 1, 1, 1, 1, 1, 1] 1 [3, 3, 2][9, 8, 2][7, 6, 1][12, 14, 3] [2][2][2] LCExtension::LCExtension - no extend axes have been specified LCExtension::LCExtension - number of axes in extend box mismatches number of extend axes LCExtension::LCExtension - extend axes multiply specified OK casacore-3.7.1/lattices/LRegions/test/tLCIntersection.cc000066400000000000000000000116011476623553700232140ustar00rootroot00000000000000//# tLCIntersection.cc: mechanical test of the LCIntersection class //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include void doIt (const IPosition& latticeShape, const IPosition& start, const IPosition& end, const IPosition& center, Float radius) { uInt ndim = latticeShape.nelements(); LCBox box (start, end, latticeShape); LCEllipsoid cir (center, radius, latticeShape); LCIntersection inters (box, cir); AlwaysAssertExit (inters.hasMask()); AlwaysAssertExit (! inters.isWritable()); cout << inters.hasMask() << ' ' << endl; cout << inters.boundingBox().start() << inters.boundingBox().end() << inters.boundingBox().length() << inters.latticeShape() << endl; Array mask; inters.getSlice (mask, IPosition(ndim,0), inters.boundingBox().length(), IPosition(ndim,1)); cout << mask << endl; LCIntersection inters1 (False, &box); AlwaysAssertExit (! inters1.hasMask()); AlwaysAssertExit (! inters1.isWritable()); Array mask1; inters1.getSlice (mask1, IPosition(ndim,0), inters1.boundingBox().length(), IPosition(ndim,1)); AlwaysAssertExit (allEQ(mask1, True)); { // Test cloning. LCRegion* interscop = inters.cloneRegion(); AlwaysAssertExit (inters.hasMask() == interscop->hasMask()); AlwaysAssertExit (inters.boundingBox().start() == interscop->boundingBox().start()); AlwaysAssertExit (inters.boundingBox().end() == interscop->boundingBox().end()); AlwaysAssertExit (inters.boundingBox().stride() == interscop->boundingBox().stride()); AlwaysAssertExit (inters.boundingBox().length() == interscop->boundingBox().length()); Array arr; interscop->getSlice (arr, IPosition(ndim,0), inters.boundingBox().length(), IPosition(ndim,1)); AlwaysAssertExit (allEQ (arr, mask)); delete interscop; } { // Test persistency. LCRegion* interscop = (LCRegion::fromRecord (inters.toRecord(""), "")); AlwaysAssertExit (inters.hasMask() == interscop->hasMask()); AlwaysAssertExit (inters.boundingBox().start() == interscop->boundingBox().start()); AlwaysAssertExit (inters.boundingBox().end() == interscop->boundingBox().end()); AlwaysAssertExit (inters.boundingBox().stride() == interscop->boundingBox().stride()); AlwaysAssertExit (inters.boundingBox().length() == interscop->boundingBox().length()); Array arr; interscop->getSlice (arr, IPosition(ndim,0), inters.boundingBox().length(), IPosition(ndim,1)); AlwaysAssertExit (allEQ (arr, mask)); delete interscop; } { // Test ordered equality. LCIntersection inters1(box, cir); LCIntersection inters2(inters1); AlwaysAssertExit (inters1 == inters2); } { // Test unordered equality. LCIntersection inters1(box, cir); LCIntersection inters2(cir, box); AlwaysAssertExit (inters1 == inters2); } } int main() { try { doIt (IPosition (2,11,20), IPosition (2,3,4), IPosition (2,7,8), IPosition (2,5,10), 5.); try { doIt (IPosition (2,10,20), IPosition (2,3,4), IPosition (2,7,8), IPosition (2,4,16), 5.); } catch (std::exception& x) { cout << x.what() << endl; } } catch (std::exception& x) { cout << "Caught exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/lattices/LRegions/test/tLCIntersection.out000066400000000000000000000003161476623553700234370ustar00rootroot000000000000001 [3, 5][7, 8][5, 4][11, 20] Axis Lengths: [5, 4] (NB: Matrix in Row/Column order) [0, 1, 1, 1 0, 1, 1, 1 1, 1, 1, 1 0, 1, 1, 1 0, 1, 1, 1] LCIntersection::LCIntersection - regions do not overlap OK casacore-3.7.1/lattices/LRegions/test/tLCLELMask.cc000066400000000000000000000073421476623553700220050ustar00rootroot00000000000000//# tLCLELMask.cc: mechanical test of the LCLELMask class //# Copyright (C) 2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include void testVectorROIter (const Lattice& lattice, Bool firstValue, Bool alternates) { Int nstep; const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(1,latticeShape(0)); LatticeStepper step(latticeShape, cursorShape); RO_LatticeIterator iter(lattice, step); Bool value = firstValue; for (iter.reset(); !iter.atEnd(); iter++){ // Static cast avoids an SGI compiler bug. AlwaysAssert(allEQ(static_cast >(iter.vectorCursor()), value), AipsError); if (alternates) { value = (!value); } } nstep = iter.nsteps(); AlwaysAssert(nstep == latticeShape.product()/latticeShape(0), AipsError); IPosition expectedPos(latticeShape-1); AlwaysAssert(iter.endPosition() == expectedPos, AipsError); expectedPos(0) = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); } int main () { try { IPosition latticeShape(2, 4, 8); Array arr(latticeShape); indgen (arr); ArrayLattice arrlat(arr); { LCLELMask mask(fmod(floor(arrlat/4), 2) == 0); AlwaysAssertExit (mask.hasMask()); AlwaysAssertExit (! mask.isWritable()); AlwaysAssertExit (mask.shape() == latticeShape); // Check the mask values using the iterator. testVectorROIter (mask, True, True); LCLELMask mask1(fmod(floor(arrlat/4), 2) != 0); LCLELMask mask2(mask1); AlwaysAssertExit (mask2.hasMask()); AlwaysAssertExit (! mask2.isWritable()); AlwaysAssertExit (mask2.shape() == latticeShape); testVectorROIter (mask2, False, True); mask1 = mask; AlwaysAssertExit (mask1.hasMask()); AlwaysAssertExit (! mask1.isWritable()); AlwaysAssertExit (mask1.shape() == latticeShape); testVectorROIter (mask, True, True); AlwaysAssertExit (mask1 != mask2); } } catch (std::exception& x) { cerr << "Caught exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/lattices/LRegions/test/tLCMask.cc000066400000000000000000000102641476623553700214450ustar00rootroot00000000000000//# tLCMask.cc: mechanical test of the LCMask class //# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include void testVectorROIter (const Lattice& lattice, Bool firstValue, Bool alternates) { Int nstep; const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(1,latticeShape(0)); LatticeStepper step(latticeShape, cursorShape); RO_LatticeIterator iter(lattice, step); Bool value = firstValue; for (iter.reset(); !iter.atEnd(); iter++){ // static_cast is a work around for an SGI compiler bug AlwaysAssert(allEQ(static_cast >(iter.vectorCursor()), value), AipsError); if (alternates) { value = (!value); } } nstep = iter.nsteps(); AlwaysAssert(nstep == latticeShape.product()/latticeShape(0), AipsError); IPosition expectedPos(latticeShape-1); AlwaysAssert(iter.endPosition() == expectedPos, AipsError); expectedPos(0) = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); } void testArrayRWIter (Lattice& lattice) { const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(latticeShape); LatticeIterator iter(lattice, cursorShape); for (iter.reset(); !iter.atEnd(); ++iter){ iter.rwCursor() = !(iter.cursor()); } } int main () { try { { IPosition latticeShape(2, 4, 8); Array arr(latticeShape); arr.set(True); arr(IPosition(2,0,0)) = False; LCMask mask(latticeShape); mask.put (arr); cout << mask.hasMask() << mask.maskArray() << endl; } { IPosition latticeShape(4, 16, 12, 4, 32); Array arr(latticeShape); arr(IPosition(4,0,0,0,0), latticeShape-1, IPosition(4,1,2,1,1)) = True; arr(IPosition(4,0,1,0,0), latticeShape-1, IPosition(4,1,2,1,1)) = False; LCMask mask(latticeShape); mask.put (arr); AlwaysAssertExit (mask.isWritable()); AlwaysAssertExit (mask.hasMask()); AlwaysAssertExit (mask.shape() == latticeShape); // Check the mask functions using the iterator. testVectorROIter (mask, True, True); testArrayRWIter (mask); testVectorROIter (mask, False, True); LCMask mask2(mask); AlwaysAssertExit (mask2 == mask); LCMask mask3(latticeShape-1); Array arr3(latticeShape-1); arr3.set(True); AlwaysAssertExit (mask3 != mask); } } catch (std::exception& x) { cerr << "Caught exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/lattices/LRegions/test/tLCMask.out000066400000000000000000000002351476623553700216640ustar00rootroot000000000000001Axis Lengths: [4, 8] (NB: Matrix in Row/Column order) [0, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1] OK casacore-3.7.1/lattices/LRegions/test/tLCPagedMask.cc000066400000000000000000000111571476623553700224100ustar00rootroot00000000000000//# tLCPagedMask.cc: mechanical test of the LCPagedMask class //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include void testVectorROIter (const Lattice& lattice, Bool firstValue, Bool alternates) { Int nstep; const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(1,latticeShape(0)); LatticeStepper step(latticeShape, cursorShape); RO_LatticeIterator iter(lattice, step); Bool value = firstValue; for (iter.reset(); !iter.atEnd(); iter++){ // static_cast is a workaround for an SGI compiler bug AlwaysAssert(allEQ(static_cast >(iter.vectorCursor()), value), AipsError); if (alternates) { value = (!value); } } nstep = iter.nsteps(); AlwaysAssert(nstep == latticeShape.product()/latticeShape(0), AipsError); IPosition expectedPos(latticeShape-1); AlwaysAssert(iter.endPosition() == expectedPos, AipsError); expectedPos(0) = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); } void testArrayRWIter (Lattice& lattice) { const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(latticeShape); LatticeIterator iter(lattice, cursorShape); for (iter.reset(); !iter.atEnd(); ++iter){ iter.rwCursor() = !(iter.cursor()); } } int main () { try { { IPosition latticeShape(2, 4, 8); Array arr(latticeShape); arr.set(True); arr(IPosition(2,0,0)) = False; LCPagedMask mask(latticeShape, "tLCPagedMask_tmp.data"); mask.put (arr); cout << mask.hasMask() << mask.maskArray() << endl; } { IPosition latticeShape(4, 16, 12, 4, 32); Array arr(latticeShape); arr(IPosition(4,0,0,0,0), latticeShape-1, IPosition(4,1,2,1,1)) = True; arr(IPosition(4,0,1,0,0), latticeShape-1, IPosition(4,1,2,1,1)) = False; LCPagedMask mask(latticeShape, "tLCPagedMask_tmp.data"); mask.put (arr); AlwaysAssertExit (mask.isWritable()); AlwaysAssertExit (mask.hasMask()); AlwaysAssertExit (mask.shape() == latticeShape); // Check the mask functions using the iterator. testVectorROIter (mask, True, True); testArrayRWIter (mask); testVectorROIter (mask, False, True); TableRecord rec = mask.toRecord(""); LCRegion* copmask = LCRegion::fromRecord (rec, ""); AlwaysAssertExit (copmask->isWritable()); AlwaysAssertExit (copmask->hasMask()); AlwaysAssertExit (copmask->shape() == latticeShape); testVectorROIter (*copmask, False, True); delete copmask; LCPagedMask mask2(mask); AlwaysAssertExit (mask2 == mask); LCPagedMask mask3(latticeShape-1, "tLCPagedMask_tmp3.data"); Array arr3(latticeShape-1); arr3.set(True); AlwaysAssertExit (mask3 != mask); } } catch (std::exception& x) { cerr << "Caught exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/lattices/LRegions/test/tLCPagedMask.out000066400000000000000000000002351476623553700226250ustar00rootroot000000000000001Axis Lengths: [4, 8] (NB: Matrix in Row/Column order) [0, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1] OK casacore-3.7.1/lattices/LRegions/test/tLCPixelSet.cc000066400000000000000000000135541476623553700223140ustar00rootroot00000000000000//# tLCPixelSet.cc: mechanical test of the LCPixelSet class //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include void testVectorROIter (const Lattice& lattice, Bool firstValue, Bool alternates) { Int nstep; const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(1,latticeShape(0)); LatticeStepper step(latticeShape, cursorShape); RO_LatticeIterator iter(lattice, step); Bool value = firstValue; for (iter.reset(); !iter.atEnd(); iter++){ // static_cast is a workaround for an SGI compiler bug AlwaysAssert(allEQ(static_cast >(iter.vectorCursor()), value), AipsError); if (alternates) { value = (!value); } } nstep = iter.nsteps(); AlwaysAssert(nstep == latticeShape.product()/latticeShape(0), AipsError); IPosition expectedPos(latticeShape-1); AlwaysAssert(iter.endPosition() == expectedPos, AipsError); expectedPos(0) = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); } void testArrayRWIter (Lattice& lattice) { const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(latticeShape); LatticeIterator iter(lattice, cursorShape); for (iter.reset(); !iter.atEnd(); ++iter){ iter.rwCursor() = !(iter.cursor()); } } int main () { try { { IPosition latticeShape(2, 4, 8); Array arr(latticeShape); arr.set(True); arr(IPosition(2,0,0)) = False; LCPixelSet mask(arr, LCBox(IPosition(2,0), latticeShape-1, latticeShape)); cout << mask.hasMask() << mask.maskArray() << endl; } { IPosition latticeShape(4, 16, 12, 4, 32); Array arr(latticeShape); arr(IPosition(4,0,0,0,0), latticeShape-1, IPosition(4,1,2,1,1)) = True; arr(IPosition(4,0,1,0,0), latticeShape-1, IPosition(4,1,2,1,1)) = False; LCPixelSet mask(arr, LCBox (IPosition(4,0), latticeShape-1, latticeShape)); AlwaysAssertExit (! mask.isWritable()); AlwaysAssertExit (mask.hasMask()); AlwaysAssertExit (mask.shape() == latticeShape); // Check the mask functions using the iterator. testVectorROIter (mask, True, True); /// testArrayRWIter (mask); /// testVectorROIter (mask, False, True); TableRecord rec = mask.toRecord(""); LCRegion* copmask = LCRegion::fromRecord (rec, ""); AlwaysAssertExit (! copmask->isWritable()); AlwaysAssertExit (copmask->hasMask()); AlwaysAssertExit (copmask->shape() == latticeShape); testVectorROIter (*copmask, True, True); /// LCRegion* trmask = copmask->translate (IPosition(4,2,0,0,0)); /// AlwaysAssertExit (trmask->isWritable()); /// AlwaysAssertExit (trmask->hasMask()); /// AlwaysAssertExit (trmask->latticeShape() == latticeShape); /// latticeShape(0) -= 2; /// AlwaysAssertExit (trmask->shape() == latticeShape); /// AlwaysAssertExit (trmask->boundingBox().start() /// == IPosition(4,2,0,0,0)); /// testVectorROIter (*trmask, False, True); delete copmask; /// delete trmask; } { const IPosition latticeShape(4, 16, 12, 4, 32); // Construct with a trc which is 1 too high. The ctor corrects it. LCBox region(IPosition(4,0), latticeShape, latticeShape); AlwaysAssertExit (! region.isWritable()); AlwaysAssertExit (! region.hasMask()); AlwaysAssertExit (region.shape() == latticeShape); // Check the region functions using the iterator. testVectorROIter (region, True, False); } { IPosition latticeShape(2, 4, 8); Array arr(latticeShape); arr.set(True); arr(IPosition(2,0)) = False; arr(latticeShape-1) = False; LCPixelSet mask1(arr, LCBox(IPosition(2,0), latticeShape-1, latticeShape)); LCPixelSet mask2(mask1); AlwaysAssertExit (mask2 == mask1); arr(latticeShape-1) = True; LCPixelSet mask3(arr, LCBox(IPosition(2,0), latticeShape-1, latticeShape)); AlwaysAssertExit (mask3 != mask1); } } catch (std::exception& x) { cerr << "Caught exception: " << x.what() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/lattices/LRegions/test/tLCPixelSet.out000066400000000000000000000002351476623553700225260ustar00rootroot000000000000001Axis Lengths: [4, 8] (NB: Matrix in Row/Column order) [0, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1] OK casacore-3.7.1/lattices/LRegions/test/tLCPolygon.cc000066400000000000000000000221631476623553700222020ustar00rootroot00000000000000//# tLCPolygon.cc: Test program for LCPolygon class //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include void doIt (const IPosition& latticeShape, const Vector& x, const Vector& y) { LCPolygon polygon (x, y, latticeShape); cout << polygon.boundingBox().start() << polygon.boundingBox().end() << polygon.boundingBox().length() << polygon.latticeShape() << endl; cout << polygon.x() << polygon.y() << endl; cout << polygon.hasMask() << ' ' << polygon.maskArray() << endl; } int main() { try { { // A simple rectangle. Vector x(4), y(4); x(0)=3; y(0)=3; x(1)=6; y(1)=3; x(2)=6; y(2)=5; x(3)=3; y(3)=5; doIt (IPosition (2,11,20), x, y); // Make right side a bit different. x(2)=7.9; doIt (IPosition (2,11,20), x, y); x(2)=8; doIt (IPosition (2,11,20), x, y); x(2)=8.1; doIt (IPosition (2,11,20), x, y); } { // A rectangle with bottom-right corner cut off. Vector x(5), y(5); x(0)=3; y(0)=3; x(1)=5; y(1)=3; x(2)=6; y(2)=4.1; x(3)=6; y(3)=5; x(4)=3; y(4)=5; doIt (IPosition (2,11,20), x, y); // Make right side a bit different. x(2)=8; doIt (IPosition (2,11,20), x, y); } { // A rectangle where the left side is a bit strange. Vector x(7), y(7); x(0)=3; y(0)=3; x(1)=5; y(1)=3; x(2)=5; y(2)=5; x(3)=2; y(3)=5; x(4)=3.6; y(4)=4; x(5)=2; y(5)=3; x(6)=2; y(6)=2.1; doIt (IPosition (2,11,20), x, y); x(3)=3; doIt (IPosition (2,11,20), x, y); } { // A rectangle with an inner rectangle. Vector x(11), y(11); x(0)=3; y(0)=3; x(1)=9; y(1)=3; x(2)=9; y(2)=8; x(3)=3; y(3)=8; x(4)=3; y(4)=3; x(5)=5; y(5)=4.8; x(6)=7; y(6)=5; x(7)=7; y(7)=7; x(8)=5; y(8)=7; x(9)=5; y(9)=5; x(10)=3; y(10)=3; doIt (IPosition (2,11,20), x, y); x(6)=8.1; y(6)=4; doIt (IPosition (2,11,20), x, y); } { // A cross-like figure Vector x(4), y(4); x(0)=3; y(0)=3; x(1)=9; y(1)=3; x(2)=3; y(2)=8; x(3)=9; y(3)=8; doIt (IPosition (2,11,20), x, y); } { // A pentagram (with the inner pentagon excluded) Vector x(5), y(5); x(0)=0.8; y(0)=0; x(1)=3; y(1)=4; x(2)=5.2; y(2)=0; x(3)=0; y(3)=2.8; x(4)=6; y(4)=2.8; doIt (IPosition (2,11,20), x, y); } { // A circle like polygon. Float x[] = {523.974, 523.933, 523.809, 523.604, 523.317, 522.953, 522.515, 522.002, 521.422, 520.776, 520.068, 519.306, 518.493, 517.635, 516.738, 515.809, 514.852, 513.877, 512.889, 511.893, 510.901, 509.913, 508.941, 507.991, 507.068, 506.179, 505.329, 504.527, 503.775, 503.082, 502.448, 501.882, 501.386, 500.962, 500.614, 500.347, 500.157, 500.052, 500.028, 500.087, 500.229, 500.452, 500.754, 501.133, 501.59, 502.117, 502.711, 503.372, 504.09, 504.864, 505.687, 506.554, 507.457, 508.394, 509.355, 510.333, 511.325, 512.319, 513.313, 514.297, 515.264, 516.209, 517.127, 518.007, 518.847, 519.638, 520.378, 521.06, 521.677, 522.231, 522.712, 523.12, 523.449, 523.7, 523.871, 523.96}; Float y[] = {512.001, 512.997, 513.985, 514.963, 515.918, 516.846, 517.741, 518.595, 519.406, 520.165, 520.866, 521.508, 522.083, 522.589, 523.02, 523.377, 523.655, 523.851, 523.966, 523.999, 523.95, 523.816, 523.601, 523.306, 522.935, 522.485, 521.966, 521.376, 520.722, 520.006, 519.237, 518.418, 517.554, 516.65, 515.716, 514.755, 513.775, 512.785, 511.787, 510.79, 509.804, 508.831, 507.882, 506.96, 506.073, 505.227, 504.427, 503.68, 502.991, 502.364, 501.803, 501.312, 500.898, 500.557, 500.298, 500.116, 500.019, 500.005, 500.073, 500.223, 500.454, 500.766, 501.156, 501.62, 502.156, 502.758, 503.427, 504.153, 504.934, 505.764, 506.638, 507.548, 508.488, 509.454, 510.435, 511.43}; IPosition shape(2, 1024,1024); Vector xv(IPosition(1,sizeof(x)/sizeof(Float)), x, SHARE); Vector yv(IPosition(1,sizeof(y)/sizeof(Float)), y, SHARE); doIt (shape, xv, yv); } { // The letter E. Float x[] = {1,11,11,4, 4,11,11, 4, 4,11,11, 1}; Float y[] = {1, 1, 4,4,10,10,13,13,19,19,22,22}; IPosition shape(2, 1024,1024); Vector xv(IPosition(1,sizeof(x)/sizeof(Float)), x, SHARE); Vector yv(IPosition(1,sizeof(y)/sizeof(Float)), y, SHARE); doIt (shape, xv, yv); } { // An almost empty diamond. Float x[] = {0.1, 4.0, 7.9, 4.0}; Float y[] = {1.5, 1.1, 1.5, 2.1}; IPosition shape(2, 1024,1024); Vector xv(IPosition(1,sizeof(x)/sizeof(Float)), x, SHARE); Vector yv(IPosition(1,sizeof(y)/sizeof(Float)), y, SHARE); doIt (shape, xv, yv); } { // A box with a negative start. Vector x(4), y(4); x[0] = -13; y[0] = 1; x[1] = 4; y[1] = 1; x[2] = 4; y[2] = 6; x[3] = -13; y[3] = 6; IPosition shape(2, 11, 6); doIt(shape, x, y); } { // A tilted box with all points outside lattice. Vector x(4), y(4); x[0] = -2; y[0] = 3; x[1] = 3; y[1] = -1; x[2] = 8; y[2] = 3; x[3] = 3; y[3] = 7; IPosition shape(2, 7, 7); doIt(shape, x, y); } { // A box entirely outside lattice. Vector x(4), y(4); x[0] = 12; y[0] = 12; x[1] = 14; y[1] = 12; x[2] = 14; y[2] = 14; x[3] = 12; y[3] = 14; IPosition shape(2, 7, 7); try { doIt(shape, x, y); } catch (std::exception& x) { cout << x.what() << endl; } } { // A polygon just inside lattice, but no points matching. Vector x(3), y(3); x[0] = 2.5; y[0] = 1.1; x[1] = 3; y[1] = 5; x[2] = 2; y[2] = 5; IPosition shape(2, 5, 3); try { doIt(shape, x, y); } catch (std::exception& x) { cout << x.what() << endl; } } { // Test some other functions IPosition latticeShape(2,100,200); Vector x(4), y(4); x(0)=3; y(0)=3; x(1)=6; y(1)=3; x(2)=6; y(2)=5; x(3)=3; y(3)=5; LCPolygon p1(x, y, latticeShape); LCPolygon p2(p1); AlwaysAssertExit (p2 == p1); p2 = p1; AlwaysAssertExit (p2 == p1); y(3) = 8; LCPolygon p3(x, y, latticeShape); AlwaysAssertExit (p3 != p1); TableRecord rec = p3.toRecord(""); LCPolygon* pP3 = LCPolygon::fromRecord(rec,""); AlwaysAssertExit (*pP3 == p3); delete pP3; LCRegion* pRegion = p3.cloneRegion(); pP3 = (LCPolygon*)pRegion; AlwaysAssertExit (*pP3 == p3); delete pP3; } { // verify fix for CAS-11230 Vector x(4, 1e-10); x[1] = 127; x[2] = 127; Vector y(4, 1e-10); y[2] = 127; y[3] = 127; IPosition latticeShape(2, 128, 128); LCPolygon poly(x, y, latticeShape); Slicer sl = poly.boundingBox(); // was IPosiiton(2, 1) before fix AlwaysAssert(sl.start() == IPosition(2, 0), AipsError); AlwaysAssert(sl.end() == IPosition(2, 127), AipsError); } } catch (const std::exception& x) { cout << "Caught exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/lattices/LRegions/test/tLCPolygon.out000066400000000000000000000155241476623553700224270ustar00rootroot00000000000000[3, 3][6, 5][4, 3][11, 20] [3, 6, 6, 3, 3][3, 3, 5, 5, 3] 1 Axis Lengths: [4, 3] (NB: Matrix in Row/Column order) [1, 1, 1 1, 1, 1 1, 1, 1 1, 1, 1] [3, 3][7, 5][5, 3][11, 20] [3, 6, 7.9, 3, 3][3, 3, 5, 5, 3] 1 Axis Lengths: [5, 3] (NB: Matrix in Row/Column order) [1, 1, 1 1, 1, 1 1, 1, 1 1, 1, 1 0, 0, 1] [3, 3][8, 5][6, 3][11, 20] [3, 6, 8, 3, 3][3, 3, 5, 5, 3] 1 Axis Lengths: [6, 3] (NB: Matrix in Row/Column order) [1, 1, 1 1, 1, 1 1, 1, 1 1, 1, 1 0, 1, 1 0, 0, 1] [3, 3][8, 5][6, 3][11, 20] [3, 6, 8.1, 3, 3][3, 3, 5, 5, 3] 1 Axis Lengths: [6, 3] (NB: Matrix in Row/Column order) [1, 1, 1 1, 1, 1 1, 1, 1 1, 1, 1 0, 1, 1 0, 0, 1] [3, 3][6, 5][4, 3][11, 20] [3, 5, 6, 6, 3, 3][3, 3, 4.1, 5, 5, 3] 1 Axis Lengths: [4, 3] (NB: Matrix in Row/Column order) [1, 1, 1 1, 1, 1 1, 1, 1 0, 0, 1] [3, 3][7, 5][5, 3][11, 20] [3, 5, 8, 6, 3, 3][3, 3, 4.1, 5, 5, 3] 1 Axis Lengths: [5, 3] (NB: Matrix in Row/Column order) [1, 1, 1 1, 1, 1 1, 1, 1 0, 1, 1 0, 1, 0] [2, 3][5, 5][4, 3][11, 20] [3, 5, 5, 2, 3.6, 2, 2, 3][3, 3, 5, 5, 4, 3, 2.1, 3] 1 Axis Lengths: [4, 3] (NB: Matrix in Row/Column order) [1, 0, 1 1, 0, 1 1, 1, 1 1, 1, 1] [2, 3][5, 5][4, 3][11, 20] [3, 5, 5, 3, 3.6, 2, 2, 3][3, 3, 5, 5, 4, 3, 2.1, 3] 1 Axis Lengths: [4, 3] (NB: Matrix in Row/Column order) [1, 0, 0 1, 0, 1 1, 1, 1 1, 1, 1] [3, 3][9, 8][7, 6][11, 20] [3, 9, 9, 3, 3, 5, 7, 7, 5, 5, 3][3, 3, 8, 8, 3, 4.8, 5, 7, 7, 5, 3] 1 Axis Lengths: [7, 6] (NB: Matrix in Row/Column order) [1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1 1, 1, 0, 0, 1, 1 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1] [3, 3][9, 8][7, 6][11, 20] [3, 9, 9, 3, 3, 5, 8.1, 7, 5, 5, 3][3, 3, 8, 8, 3, 4.8, 4, 7, 7, 5, 3] 1 Axis Lengths: [7, 6] (NB: Matrix in Row/Column order) [1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1 1, 1, 0, 0, 1, 1 1, 1, 0, 0, 1, 1 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1] [3, 3][9, 8][7, 6][11, 20] [3, 9, 3, 9, 3][3, 3, 8, 8, 3] 1 Axis Lengths: [7, 6] (NB: Matrix in Row/Column order) [1, 0, 0, 0, 0, 1 1, 0, 0, 0, 0, 1 1, 1, 0, 0, 1, 1 1, 1, 1, 1, 1, 1 1, 1, 0, 0, 1, 1 1, 0, 0, 0, 0, 1 1, 0, 0, 0, 0, 1] [2, 1][4, 4][3, 4][11, 20] [0.8, 3, 5.2, 0, 6, 0.8][0, 4, 0, 2.8, 2.8, 0] 1 Axis Lengths: [3, 4] (NB: Matrix in Row/Column order) [1, 0, 0, 0 0, 0, 1, 1 1, 0, 0, 0] [501, 501][523, 523][23, 23][1024, 1024] [523.974, 523.933, 523.809, 523.604, 523.317, 522.953, 522.515, 522.002, 521.422, 520.776, 520.068, 519.306, 518.493, 517.635, 516.738, 515.809, 514.852, 513.877, 512.889, 511.893, 510.901, 509.913, 508.941, 507.991, 507.068, 506.179, 505.329, 504.527, 503.775, 503.082, 502.448, 501.882, 501.386, 500.962, 500.614, 500.347, 500.157, 500.052, 500.028, 500.087, 500.229, 500.452, 500.754, 501.133, 501.59, 502.117, 502.711, 503.372, 504.09, 504.864, 505.687, 506.554, 507.457, 508.394, 509.355, 510.333, 511.325, 512.319, 513.313, 514.297, 515.264, 516.209, 517.127, 518.007, 518.847, 519.638, 520.378, 521.06, 521.677, 522.231, 522.712, 523.12, 523.449, 523.7, 523.871, 523.96, 523.974][512.001, 512.997, 513.985, 514.963, 515.918, 516.846, 517.741, 518.595, 519.406, 520.165, 520.866, 521.508, 522.083, 522.589, 523.02, 523.377, 523.655, 523.851, 523.966, 523.999, 523.95, 523.816, 523.601, 523.306, 522.935, 522.485, 521.966, 521.376, 520.722, 520.006, 519.237, 518.418, 517.554, 516.65, 515.716, 514.755, 513.775, 512.785, 511.787, 510.79, 509.804, 508.831, 507.882, 506.96, 506.073, 505.227, 504.427, 503.68, 502.991, 502.364, 501.803, 501.312, 500.898, 500.557, 500.298, 500.116, 500.019, 500.005, 500.073, 500.223, 500.454, 500.766, 501.156, 501.62, 502.156, 502.758, 503.427, 504.153, 504.934, 505.764, 506.638, 507.548, 508.488, 509.454, 510.435, 511.43, 512.001] 1 Axis Lengths: [23, 23] (NB: Matrix in Row/Column order) [0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0] [1, 1][11, 22][11, 22][1024, 1024] [1, 11, 11, 4, 4, 11, 11, 4, 4, 11, 11, 1, 1][1, 1, 4, 4, 10, 10, 13, 13, 19, 19, 22, 22, 1] 1 Axis Lengths: [11, 22] (NB: Matrix in Row/Column order) [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1] [4, 2][4, 2][1, 1][1024, 1024] [0.1, 4, 7.9, 4, 0.1][1.5, 1.1, 1.5, 2.1, 1.5] 1 Axis Lengths: [1, 1] (NB: Matrix in Row/Column order) [1] [0, 1][4, 5][5, 5][11, 6] [-13, 4, 4, -13, -13][1, 1, 6, 6, 1] 1 Axis Lengths: [5, 5] (NB: Matrix in Row/Column order) [1, 1, 1, 1, 1 1, 1, 1, 1, 1 1, 1, 1, 1, 1 1, 1, 1, 1, 1 1, 1, 1, 1, 1] [0, 0][6, 6][7, 7][7, 7] [-2, 3, 8, 3, -2][3, -1, 3, 7, 3] 1 Axis Lengths: [7, 7] (NB: Matrix in Row/Column order) [0, 0, 1, 1, 1, 0, 0 0, 1, 1, 1, 1, 1, 0 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1 0, 1, 1, 1, 1, 1, 0 0, 0, 1, 1, 1, 0, 0] LCPolygon - entire polygon is outside the lattice LCPolygon - polygon does not contain any pixel OK casacore-3.7.1/lattices/LRegions/test/tLCPolygon2.cc000066400000000000000000000102321476623553700222560ustar00rootroot00000000000000//# tLCPolygon.cc: Test program for LCPolygon class //# Copyright (C) 2011 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include void doIt (const IPosition& latticeShape, const Vector& x, const Vector& y) { LCPolygon polygon (x, y, latticeShape); Array mask(polygon.maskArray()); //cout << mask(IPosition(2,498,498), IPosition(2,525,525)); cout << mask; } int main() { try { { // A circle like polygon. Float x[] = {523.974, 523.933, 523.809, 523.604, 523.317, 522.953, 522.515, 522.002, 521.422, 520.776, 520.068, 519.306, 518.493, 517.635, 516.738, 515.809, 514.852, 513.877, 512.889, 511.893, 510.901, 509.913, 508.941, 507.991, 507.068, 506.179, 505.329, 504.527, 503.775, 503.082, 502.448, 501.882, 501.386, 500.962, 500.614, 500.347, 500.157, 500.052, 500.028, 500.087, 500.229, 500.452, 500.754, 501.133, 501.59, 502.117, 502.711, 503.372, 504.09, 504.864, 505.687, 506.554, 507.457, 508.394, 509.355, 510.333, 511.325, 512.319, 513.313, 514.297, 515.264, 516.209, 517.127, 518.007, 518.847, 519.638, 520.378, 521.06, 521.677, 522.231, 522.712, 523.12, 523.449, 523.7, 523.871, 523.96}; Float y[] = {512.001, 512.997, 513.985, 514.963, 515.918, 516.846, 517.741, 518.595, 519.406, 520.165, 520.866, 521.508, 522.083, 522.589, 523.02, 523.377, 523.655, 523.851, 523.966, 523.999, 523.95, 523.816, 523.601, 523.306, 522.935, 522.485, 521.966, 521.376, 520.722, 520.006, 519.237, 518.418, 517.554, 516.65, 515.716, 514.755, 513.775, 512.785, 511.787, 510.79, 509.804, 508.831, 507.882, 506.96, 506.073, 505.227, 504.427, 503.68, 502.991, 502.364, 501.803, 501.312, 500.898, 500.557, 500.298, 500.116, 500.019, 500.005, 500.073, 500.223, 500.454, 500.766, 501.156, 501.62, 502.156, 502.758, 503.427, 504.153, 504.934, 505.764, 506.638, 507.548, 508.488, 509.454, 510.435, 511.43}; IPosition shape(2, 1024,1024); Vector xv(IPosition(1,sizeof(x)/sizeof(Float)), x, SHARE); Vector yv(IPosition(1,sizeof(y)/sizeof(Float)), y, SHARE); doIt (shape, xv, yv); } { // The letter E. Float x[] = {1,11,11,4, 4,11,11, 4, 4,11,11, 1}; Float y[] = {1, 1, 4,4,10,10,13,13,19,19,22,22}; IPosition shape(2, 1024,1024); Vector xv(IPosition(1,sizeof(x)/sizeof(Float)), x, SHARE); Vector yv(IPosition(1,sizeof(y)/sizeof(Float)), y, SHARE); doIt (shape, xv, yv); } { // A diabolo. Float x[] = {1, 5, 1, 5}; Float y[] = {1, 5, 5, 1}; IPosition shape(2, 1024,1024); Vector xv(IPosition(1,sizeof(x)/sizeof(Float)), x, SHARE); Vector yv(IPosition(1,sizeof(y)/sizeof(Float)), y, SHARE); doIt (shape, xv, yv); } } catch (std::exception& x) { cout << "Caught exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/lattices/LRegions/test/tLCRegion.cc000066400000000000000000000151411476623553700217740ustar00rootroot00000000000000//# tLCRegion.cc: Test program for derived LCRegion classes. //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include void doIt (const IPosition& latticeShape, const Vector& center, const Vector& radii) { LCEllipsoid cir (center, radii, latticeShape); cout << cir.hasMask() << ' ' << cir.maskArray() << endl; cout << cir.boundingBox().start() << cir.boundingBox().end() << cir.boundingBox().length() << cir.latticeShape() << endl; cout << cir.center() << cir.radii() << endl; } void doIt (const IPosition& latticeShape, const IPosition& start, const IPosition& end, const IPosition& center, Float radius) { LCBox box (start, end, latticeShape); box.setComment ("com1"); cout << box.hasMask() << ' ' << box.maskArray() << endl; cout << box.boundingBox().start() << box.boundingBox().end() << box.boundingBox().length() << box.latticeShape() << box.comment() << endl; LCEllipsoid cir (center, radius, latticeShape); cout << cir.hasMask() << ' ' << cir.maskArray() << endl; cout << cir.boundingBox().start() << cir.boundingBox().end() << cir.boundingBox().length() << cir.latticeShape() << endl; cout << cir.center() << cir.radii() << endl; { // Test cloning. LCRegionFixed* boxcop = (LCRegionFixed*)(box.cloneRegion()); AlwaysAssertExit (box.hasMask() == boxcop->hasMask()); AlwaysAssertExit (allEQ (box.maskArray(), boxcop->maskArray())); AlwaysAssertExit (box.boundingBox().start() == boxcop->boundingBox().start()); AlwaysAssertExit (box.boundingBox().end() == boxcop->boundingBox().end()); AlwaysAssertExit (box.boundingBox().stride() == boxcop->boundingBox().stride()); AlwaysAssertExit (box.boundingBox().length() == boxcop->boundingBox().length()); AlwaysAssertExit (box.comment() == boxcop->comment()); delete boxcop; LCRegionFixed* circop = (LCRegionFixed*)(cir.cloneRegion()); AlwaysAssertExit (cir.hasMask() == circop->hasMask()); AlwaysAssertExit (allEQ (cir.maskArray(), circop->maskArray())); AlwaysAssertExit (cir.boundingBox().start() == circop->boundingBox().start()); AlwaysAssertExit (cir.boundingBox().end() == circop->boundingBox().end()); AlwaysAssertExit (cir.boundingBox().stride() == circop->boundingBox().stride()); AlwaysAssertExit (cir.boundingBox().length() == circop->boundingBox().length()); AlwaysAssertExit (cir.comment() == circop->comment()); AlwaysAssertExit (allEQ (cir.center(), ((LCEllipsoid*)circop)->center())); AlwaysAssertExit (allEQ (cir.radii(), ((LCEllipsoid*)circop)->radii())); delete circop; } { // Test persistency. LCRegionFixed* boxcop = (LCRegionFixed*) (LCRegion::fromRecord (box.toRecord(""), "")); AlwaysAssertExit (box.hasMask() == boxcop->hasMask()); AlwaysAssertExit (allEQ (box.maskArray(), boxcop->maskArray())); AlwaysAssertExit (box.boundingBox().start() == boxcop->boundingBox().start()); AlwaysAssertExit (box.boundingBox().end() == boxcop->boundingBox().end()); AlwaysAssertExit (box.boundingBox().stride() == boxcop->boundingBox().stride()); AlwaysAssertExit (box.boundingBox().length() == boxcop->boundingBox().length()); AlwaysAssertExit (box.comment() == boxcop->comment()); delete boxcop; LCRegionFixed* circop = (LCRegionFixed*) (LCRegion::fromRecord (cir.toRecord(""), "")); AlwaysAssertExit (cir.hasMask() == circop->hasMask()); AlwaysAssertExit (allEQ (cir.maskArray(), circop->maskArray())); AlwaysAssertExit (cir.boundingBox().start() == circop->boundingBox().start()); AlwaysAssertExit (cir.boundingBox().end() == circop->boundingBox().end()); AlwaysAssertExit (cir.boundingBox().stride() == circop->boundingBox().stride()); AlwaysAssertExit (cir.boundingBox().length() == circop->boundingBox().length()); AlwaysAssertExit (cir.comment() == circop->comment()); AlwaysAssertExit (allEQ (cir.center(), ((LCEllipsoid*)circop)->center())); AlwaysAssertExit (allEQ (cir.radii(), ((LCEllipsoid*)circop)->radii())); delete circop; } { // test comparison LCBox box1(start, end, latticeShape); LCBox box2(box1); AlwaysAssertExit (box2 == box1); LCBox box3(start, end-2, latticeShape); AlwaysAssertExit (box3 != box1); LCEllipsoid cir1 (center, radius, latticeShape); LCEllipsoid cir2 (cir1); AlwaysAssertExit (cir2 == cir1); LCEllipsoid cir3 (center, radius-0.01, latticeShape); AlwaysAssertExit (cir3 != cir1); AlwaysAssertExit (cir1 != box1); } } int main() { try { doIt (IPosition (2,11,20), IPosition (2,3,4), IPosition (2,7,8), IPosition (2,5,10), 5.); doIt (IPosition (2,10,20), IPosition (2,3,4), IPosition (2,7,8), IPosition (2,4,16), 5.); Vector center(2), radii(2); radii(0) = radii(1) = 5.01; center(0) = 5; center(1) = 10.5; doIt (IPosition (2,11,20), center, radii); radii(0) = 4; radii(1) = 8; center(1) = 10; doIt (IPosition (2,11,20), center, radii); } catch (std::exception& x) { cout << "Caught exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/lattices/LRegions/test/tLCRegion.out000066400000000000000000000036041476623553700222170ustar00rootroot000000000000000 [] [3, 4][7, 8][5, 5][11, 20]com1 1 Axis Lengths: [11, 11] (NB: Matrix in Row/Column order) [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0] [0, 5][10, 15][11, 11][11, 20] [5, 10][5, 5] 0 [] [3, 4][7, 8][5, 5][10, 20]com1 1 Axis Lengths: [10, 9] (NB: Matrix in Row/Column order) [0, 0, 1, 1, 1, 1, 1, 1, 1 0, 1, 1, 1, 1, 1, 1, 1, 1 0, 1, 1, 1, 1, 1, 1, 1, 1 0, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1, 1 0, 1, 1, 1, 1, 1, 1, 1, 1 0, 1, 1, 1, 1, 1, 1, 1, 1 0, 1, 1, 1, 1, 1, 1, 1, 1 0, 0, 1, 1, 1, 1, 1, 1, 1 0, 0, 0, 0, 0, 1, 0, 0, 0] [0, 11][9, 19][10, 9][10, 20] [4, 16][5, 5] 1 Axis Lengths: [11, 10] (NB: Matrix in Row/Column order) [0, 0, 0, 0, 0, 0, 0, 0, 0, 0 0, 0, 1, 1, 1, 1, 1, 1, 0, 0 0, 1, 1, 1, 1, 1, 1, 1, 1, 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 0, 1, 1, 1, 1, 1, 1, 1, 1, 0 0, 0, 1, 1, 1, 1, 1, 1, 0, 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 6][10, 15][11, 10][11, 20] [5, 10.5][5.01, 5.01] 1 Axis Lengths: [9, 17] (NB: Matrix in Row/Column order) [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0] [1, 2][9, 18][9, 17][11, 20] [5, 10][4, 8] OK casacore-3.7.1/lattices/LRegions/test/tLCSlicer.cc000066400000000000000000000227051476623553700217760ustar00rootroot00000000000000//# tLCSlicer.cc: Test program the LCSlicer class //# Copyright (C) 1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include void doIt() { // Construct a simple LCSlicer. // Test comment functions, copy constructor, default constructor, // assignment, and to/fromRecord. { LCSlicer sl1 (IPosition(3,5,8,3), IPosition(3,10,20,30)); sl1.setComment ("comm1"); AlwaysAssertExit (sl1.isComplete()); AlwaysAssertExit (sl1.isAbsolute()); AlwaysAssertExit (! sl1.isFractional()); AlwaysAssertExit (! sl1.isStrided()); AlwaysAssertExit (! sl1.isUnspecified()); AlwaysAssertExit (sl1.ndim() == 3); AlwaysAssertExit (sl1.comment() == "comm1"); Slicer sl (sl1.toSlicer (IPosition(3,0,0,0), IPosition(3,40,50,60))); cout << sl.start() << sl.end() << sl.stride() << endl; LCSlicer sl2 (sl1); AlwaysAssertExit (sl2 == sl1); AlwaysAssertExit (sl2.comment() == sl1.comment()); LCSlicer sl3; AlwaysAssertExit (! sl3.isComplete()); AlwaysAssertExit (sl3 != sl1); sl2 = sl3; AlwaysAssertExit (sl2 == sl3); AlwaysAssertExit (sl2.comment() == ""); sl2 = sl1; AlwaysAssertExit (sl2 == sl1); AlwaysAssertExit (sl2.comment() == sl1.comment()); TableRecord rec = sl1.toRecord (""); LCSlicer* sl4 = LCSlicer::fromRecord (rec, ""); AlwaysAssertExit (! (*sl4 != sl1)); AlwaysAssertExit (sl4->comment() == sl1.comment()); delete sl4; } // Test if stride gets padded. // Also test if toSlicer works well (higher dimensionality and truncating // trc to shape). { LCSlicer sl1 (IPosition(3,5,8,3), IPosition(3,10,20,30), IPosition(1,2)); AlwaysAssertExit (sl1.isComplete()); AlwaysAssertExit (sl1.isAbsolute()); AlwaysAssertExit (! sl1.isFractional()); AlwaysAssertExit (sl1.isStrided()); AlwaysAssertExit (! sl1.isUnspecified()); AlwaysAssertExit (sl1.ndim() == 3); Slicer sl (sl1.toSlicer (IPosition(4,0,0,0,0), IPosition(4,8,50,60,7))); cout << sl.start() << sl.end() << sl.stride() << endl; } // Test if trc gets padded. { LCSlicer sl1 (IPosition(3,5,8,3), IPosition(1,10), IPosition(1,2)); AlwaysAssertExit (! sl1.isComplete()); AlwaysAssertExit (sl1.isAbsolute()); AlwaysAssertExit (! sl1.isFractional()); AlwaysAssertExit (sl1.isStrided()); AlwaysAssertExit (sl1.isUnspecified()); AlwaysAssertExit (sl1.ndim() == 3); Slicer sl (sl1.toSlicer (IPosition(3,0,0,0), IPosition(3,40,50,60))); cout << sl.start() << sl.end() << sl.stride() << endl; } // Test if pixel reference works fine. { LCSlicer sl1 (IPosition(3,5,8,3), IPosition(1,10), IPosition(1,2), RegionType::RelRef); AlwaysAssertExit (! sl1.isComplete()); AlwaysAssertExit (! sl1.isAbsolute()); AlwaysAssertExit (! sl1.isFractional()); AlwaysAssertExit (sl1.isStrided()); AlwaysAssertExit (sl1.isUnspecified()); AlwaysAssertExit (sl1.ndim() == 3); Slicer sl (sl1.toSlicer (IPosition(3,10,11,12), IPosition(3,40,50,60))); cout << sl.start() << sl.end() << sl.stride() << endl; } // Test if center reference works fine. { LCSlicer sl1 (IPosition(3,5,8,3), IPosition(1,10), IPosition(1,2), RegionType::RelCen); AlwaysAssertExit (! sl1.isComplete()); AlwaysAssertExit (! sl1.isAbsolute()); AlwaysAssertExit (! sl1.isFractional()); AlwaysAssertExit (sl1.isStrided()); AlwaysAssertExit (sl1.isUnspecified()); AlwaysAssertExit (sl1.ndim() == 3); Slicer sl (sl1.toSlicer (IPosition(3,10,11,12), IPosition(3,40,50,60))); cout << sl.start() << sl.end() << sl.stride() << endl; } // Test using vectors with combination of fractional and absolute/relative. Vector blc(3); Vector trc(3); Vector inc(3); Vector fracblc(3); Vector fractrc(3); Vector fracinc(3); Vector relblc(3); Vector reltrc(3); blc(0) = 0.125; blc(1) = 8; blc(2) = 3; trc(0) = 10; trc(1) = 0.4; trc(2) = 25; inc(0) = 1; inc(1) = 1; inc(2) = 0.1; fracblc = False; fracblc(0) = True; fractrc = False; fractrc(1) = True; fracinc = False; fracinc(2) = True; relblc = RegionType::Abs; relblc(0) = RegionType::RelRef; reltrc = RegionType::Abs; reltrc(0) = RegionType::RelCen; { LCSlicer sl1 (blc, trc, inc, fracblc, fractrc, fracinc, relblc, reltrc); AlwaysAssertExit (! sl1.isComplete()); AlwaysAssertExit (! sl1.isAbsolute()); AlwaysAssertExit (sl1.isFractional()); AlwaysAssertExit (sl1.isStrided()); AlwaysAssertExit (! sl1.isUnspecified()); AlwaysAssertExit (sl1.ndim() == 3); Slicer sl (sl1.toSlicer (IPosition(3,10,11,12), IPosition(3,40,50,60))); cout << sl1.blc() << sl1.trc() << sl1.inc() << endl; cout << sl.start() << sl.end() << sl.stride() << endl; } { Vector blc(3); Vector trc(2); blc(0) = 0.4; blc(1) = 0.1; blc(2) = 0.3; trc(0) = 0.5; trc(1) = 0.6; LCSlicer sl1 (blc, trc, True); AlwaysAssertExit (! sl1.isComplete()); AlwaysAssertExit (sl1.isAbsolute()); AlwaysAssertExit (sl1.isFractional()); AlwaysAssertExit (! sl1.isStrided()); AlwaysAssertExit (sl1.isUnspecified()); AlwaysAssertExit (sl1.ndim() == 3); Slicer sl (sl1.toSlicer (IPosition(3,10,11,12), IPosition(3,40,50,60))); cout << sl.start() << sl.end() << sl.stride() << endl; } { // Test if constructing from a record works fine. // Such a record is created by the quarter function in regionmanager.g. Vector vec(2); Vector flags(2); flags = True; Vector absrel(2); absrel = RegionType::Abs; vec = 0.25; TableRecord rec; rec.define ("name", "LCSLicer"); rec.define ("isRegion", Int(RegionType::ArrSlicer)); vec = 0.25; rec.define ("blc", vec); vec = 0.75; rec.define ("trc", vec); rec.define ("inc", Vector()); rec.define ("fracblc", flags); rec.define ("fractrc", flags); rec.define ("fracinc", Vector()); rec.define ("arblc", absrel); rec.define ("artrc", absrel); rec.define ("oneRel", True); rec.define ("comment", ""); LCSlicer* lc = LCSlicer::fromRecord (rec, ""); Slicer sl (lc->toSlicer (IPosition(3,0,0,0), IPosition(3,40,50,60))); cout << sl.start() << sl.end() << sl.stride() << endl; delete lc; } { Vector blc(4, 0), trc(4,0), refPix(4, 0); blc[3] = 23; trc[0] = 399; trc[1] = 399; trc[3] = 23; refPix[0] = 100; refPix[1] = 100; refPix[3] = -23; IPosition newLatticeShape(4, 400, 400, 1, 1); LCSlicer lcslicer(blc, trc, False, RegionType::RelRef); Slicer sl = lcslicer.toSlicer(refPix, newLatticeShape); AlwaysAssert(sl.start() == IPosition(4, 100, 100, 0, 0), AipsError); AlwaysAssert(sl.end() == IPosition(4, 399, 399, 0, 0), AipsError); } // Test if MimicSource works fine, also in to/fromRecord. { Vector blc(1,Slicer::MimicSource); Vector trc(1,Slicer::MimicSource); Vector inc(1,Slicer::MimicSource); Vector refPix(1,100); IPosition newLatticeShape(1, 400); { LCSlicer lcslicer(blc, trc, False, RegionType::RelRef); Slicer sl = lcslicer.toSlicer(refPix, newLatticeShape); AlwaysAssert(sl.start() == IPosition(1, 0), AipsError); AlwaysAssert(sl.end() == IPosition(1, 399), AipsError); AlwaysAssert(sl.stride() == IPosition(1, 1), AipsError); TableRecord rec = lcslicer.toRecord (""); LCSlicer* sl4 = LCSlicer::fromRecord (rec, ""); AlwaysAssertExit (! (*sl4 != lcslicer)); delete sl4; } { LCSlicer lcslicer(blc, trc, True, RegionType::Abs); Slicer sl = lcslicer.toSlicer(refPix, newLatticeShape); AlwaysAssert(sl.start() == IPosition(1, 0), AipsError); AlwaysAssert(sl.end() == IPosition(1, 399), AipsError); AlwaysAssert(sl.stride() == IPosition(1, 1), AipsError); TableRecord rec = lcslicer.toRecord (""); LCSlicer* sl4 = LCSlicer::fromRecord (rec, ""); AlwaysAssertExit (! (*sl4 != lcslicer)); delete sl4; } } } int main() { try { doIt(); } catch (std::exception& x) { cout << "Caught exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/lattices/LRegions/test/tLCSlicer.out000066400000000000000000000004641476623553700222160ustar00rootroot00000000000000[5, 8, 3][10, 20, 30][1, 1, 1] [5, 8, 3, 0][7, 20, 30, 6][2, 1, 1, 1] [5, 8, 3][10, 49, 59][2, 1, 1] [15, 19, 15][20, 49, 59][2, 1, 1] [25, 33, 33][30, 49, 59][2, 1, 1] [0.125, 8, 3][10, 0.4, 25][1, 1, 0.1] [15, 8, 3][30, 19, 25][1, 1, 6] [16, 5, 18][19, 29, 59][1, 1, 1] [10, 13, 0][29, 37, 59][1, 1, 1] OK casacore-3.7.1/lattices/LRegions/test/tLCStretch.cc000066400000000000000000000145651476623553700221760ustar00rootroot00000000000000//# tLCStretch.cc: Test program for LCStretch class //# Copyright (C) 2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include void doIt (const LCRegion& region, const IPosition& axes, const IPosition& blc, const IPosition& trc, const IPosition& latticeShape) { try { LCExtension ext1 (region, axes, LCBox(IPosition(latticeShape.nelements(), 1))); LCStretch prism (ext1, axes, LCBox(blc, trc, latticeShape)); AlwaysAssertExit (prism.hasMask() == region.hasMask()); AlwaysAssertExit (! prism.isWritable()); Array regmask; uInt ndimr = region.boundingBox().ndim(); uInt ndim = ndimr + latticeShape.nelements(); ((LCRegion&)region).getSlice (regmask, IPosition(ndimr,0), region.boundingBox().length(), IPosition(ndimr,1)); cout << regmask << endl; Array mask; prism.getSlice (mask, IPosition(ndim,0), prism.boundingBox().length(), IPosition(ndim,1)); cout << mask << endl; cout << prism.hasMask() << ' ' << endl; cout << prism.boundingBox().start() << prism.boundingBox().end() << prism.boundingBox().length() << prism.latticeShape() << endl; cout << prism.stretchAxes() << prism.stretchBox().blc() << prism.stretchBox().trc() << endl; { // Test cloning. LCRegion* prismcop = prism.cloneRegion(); AlwaysAssertExit (prism.hasMask() == prismcop->hasMask()); AlwaysAssertExit (prism.boundingBox().start() == prismcop->boundingBox().start()); AlwaysAssertExit (prism.boundingBox().end() == prismcop->boundingBox().end()); AlwaysAssertExit (prism.boundingBox().stride() == prismcop->boundingBox().stride()); AlwaysAssertExit (prism.boundingBox().length() == prismcop->boundingBox().length()); Array arr; prismcop->getSlice (arr, IPosition(ndim,0), prism.boundingBox().length(), IPosition(ndim,1)); AlwaysAssertExit (allEQ (arr, mask)); delete prismcop; } { // Test persistency. LCRegion* prismcop = LCRegion::fromRecord (prism.toRecord(""), ""); AlwaysAssertExit (prism.hasMask() == prismcop->hasMask()); AlwaysAssertExit (prism.boundingBox().start() == prismcop->boundingBox().start()); AlwaysAssertExit (prism.boundingBox().end() == prismcop->boundingBox().end()); AlwaysAssertExit (prism.boundingBox().stride() == prismcop->boundingBox().stride()); AlwaysAssertExit (prism.boundingBox().length() == prismcop->boundingBox().length()); Array arr; prismcop->getSlice (arr, IPosition(ndim,0), prism.boundingBox().length(), IPosition(ndim,1)); AlwaysAssertExit (allEQ (arr, mask)); delete prismcop; } { // Test equality. LCStretch prism2(prism); AlwaysAssertExit (prism2 == prism); } { // Test unequality. LCExtension ext2 (region, axes, LCBox(IPosition(latticeShape.nelements(), 1))); LCStretch prism2 (ext2, axes, LCBox(blc-1, trc, latticeShape)); AlwaysAssertExit (prism2 != prism); } } catch (std::exception& x) { cout << x.what() << endl; } } void doItError (const LCRegion& region, const IPosition& axes, const IPosition& blc, const IPosition& trc, const IPosition& latticeShape) { try { LCStretch prism (region, axes, LCBox(blc, trc, latticeShape)); } catch (std::exception& x) { cout << x.what() << endl; } } int main() { try { // A simple box (having no mask). LCBox box (IPosition(2,1,4), IPosition(2,5,6), IPosition(2,12,14)); // A cross-like figure. Vector x(4), y(4); x(0)=3; y(0)=3; x(1)=9; y(1)=3; x(2)=3; y(2)=8; x(3)=9; y(3)=8; LCPolygon polygon(x, y, IPosition(2,12,14)); doIt (box, IPosition(1,1), IPosition(1,2), IPosition(1,3), IPosition(1,20)); doIt (polygon, IPosition(1,2), IPosition(1,2), IPosition(1,3), IPosition(1,4)); doIt (polygon, IPosition(1,1), IPosition(1,2), IPosition(1,3), IPosition(1,20)); doIt (polygon, IPosition(2,2,0), IPosition(2,0,2), IPosition(2,2,3), IPosition(2,10,20)); // Trc outside lattice, is silently adjusted doIt (polygon, IPosition(1,2), IPosition(1,2), IPosition(1,5), IPosition(1,3)); // Error; no stretchaxes doItError (polygon, IPosition(), IPosition(1,2), IPosition(1,3), IPosition(1,20)); // Error; #stretchAxes mismatches blc/trc length doItError (polygon, IPosition(2,0,1), IPosition(1,2), IPosition(1,3), IPosition(1,20)); // Error; #stretchAxes exceed nrdim doItError (polygon, IPosition(1,2), IPosition(1,2), IPosition(1,3), IPosition(1,20)); // Error; incorrect order of stretchAxes doItError (polygon, IPosition(2,1), IPosition(2,0), IPosition(2,0), IPosition(2,1)); // Error; stretched axis has not length 1 doItError (polygon, IPosition(1,0), IPosition(1,2), IPosition(1,3), IPosition(1,20)); } catch (std::exception& x) { cout << "Caught exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/lattices/LRegions/test/tLCStretch.out000066400000000000000000000117571476623553700224200ustar00rootroot00000000000000Axis Lengths: [5, 3] (NB: Matrix in Row/Column order) [1, 1, 1 1, 1, 1 1, 1, 1 1, 1, 1 1, 1, 1] Ndim=3 Axis Lengths: [5, 2, 3] [0, 0, 0][1, 1, 1, 1, 1] [0, 1, 0][1, 1, 1, 1, 1] [0, 0, 1][1, 1, 1, 1, 1] [0, 1, 1][1, 1, 1, 1, 1] [0, 0, 2][1, 1, 1, 1, 1] [0, 1, 2][1, 1, 1, 1, 1] 0 [1, 2, 4][5, 3, 6][5, 2, 3][12, 20, 14] [1][2][3] Axis Lengths: [7, 6] (NB: Matrix in Row/Column order) [1, 0, 0, 0, 0, 1 1, 0, 0, 0, 0, 1 1, 1, 0, 0, 1, 1 1, 1, 1, 1, 1, 1 1, 1, 0, 0, 1, 1 1, 0, 0, 0, 0, 1 1, 0, 0, 0, 0, 1] Ndim=3 Axis Lengths: [7, 6, 2] [0, 0, 0][1, 1, 1, 1, 1, 1, 1] [0, 1, 0][0, 0, 1, 1, 1, 0, 0] [0, 2, 0][0, 0, 0, 1, 0, 0, 0] [0, 3, 0][0, 0, 0, 1, 0, 0, 0] [0, 4, 0][0, 0, 1, 1, 1, 0, 0] [0, 5, 0][1, 1, 1, 1, 1, 1, 1] [0, 0, 1][1, 1, 1, 1, 1, 1, 1] [0, 1, 1][0, 0, 1, 1, 1, 0, 0] [0, 2, 1][0, 0, 0, 1, 0, 0, 0] [0, 3, 1][0, 0, 0, 1, 0, 0, 0] [0, 4, 1][0, 0, 1, 1, 1, 0, 0] [0, 5, 1][1, 1, 1, 1, 1, 1, 1] 1 [3, 3, 2][9, 8, 3][7, 6, 2][12, 14, 4] [2][2][3] Axis Lengths: [7, 6] (NB: Matrix in Row/Column order) [1, 0, 0, 0, 0, 1 1, 0, 0, 0, 0, 1 1, 1, 0, 0, 1, 1 1, 1, 1, 1, 1, 1 1, 1, 0, 0, 1, 1 1, 0, 0, 0, 0, 1 1, 0, 0, 0, 0, 1] Ndim=3 Axis Lengths: [7, 2, 6] [0, 0, 0][1, 1, 1, 1, 1, 1, 1] [0, 1, 0][1, 1, 1, 1, 1, 1, 1] [0, 0, 1][0, 0, 1, 1, 1, 0, 0] [0, 1, 1][0, 0, 1, 1, 1, 0, 0] [0, 0, 2][0, 0, 0, 1, 0, 0, 0] [0, 1, 2][0, 0, 0, 1, 0, 0, 0] [0, 0, 3][0, 0, 0, 1, 0, 0, 0] [0, 1, 3][0, 0, 0, 1, 0, 0, 0] [0, 0, 4][0, 0, 1, 1, 1, 0, 0] [0, 1, 4][0, 0, 1, 1, 1, 0, 0] [0, 0, 5][1, 1, 1, 1, 1, 1, 1] [0, 1, 5][1, 1, 1, 1, 1, 1, 1] 1 [3, 2, 3][9, 3, 8][7, 2, 6][12, 20, 14] [1][2][3] Axis Lengths: [7, 6] (NB: Matrix in Row/Column order) [1, 0, 0, 0, 0, 1 1, 0, 0, 0, 0, 1 1, 1, 0, 0, 1, 1 1, 1, 1, 1, 1, 1 1, 1, 0, 0, 1, 1 1, 0, 0, 0, 0, 1 1, 0, 0, 0, 0, 1] Ndim=4 Axis Lengths: [2, 7, 3, 6] [0, 0, 0, 0][1, 1] [0, 1, 0, 0][1, 1] [0, 2, 0, 0][1, 1] [0, 3, 0, 0][1, 1] [0, 4, 0, 0][1, 1] [0, 5, 0, 0][1, 1] [0, 6, 0, 0][1, 1] [0, 0, 1, 0][1, 1] [0, 1, 1, 0][1, 1] [0, 2, 1, 0][1, 1] [0, 3, 1, 0][1, 1] [0, 4, 1, 0][1, 1] [0, 5, 1, 0][1, 1] [0, 6, 1, 0][1, 1] [0, 0, 2, 0][1, 1] [0, 1, 2, 0][1, 1] [0, 2, 2, 0][1, 1] [0, 3, 2, 0][1, 1] [0, 4, 2, 0][1, 1] [0, 5, 2, 0][1, 1] [0, 6, 2, 0][1, 1] [0, 0, 0, 1][0, 0] [0, 1, 0, 1][0, 0] [0, 2, 0, 1][1, 1] [0, 3, 0, 1][1, 1] [0, 4, 0, 1][1, 1] [0, 5, 0, 1][0, 0] [0, 6, 0, 1][0, 0] [0, 0, 1, 1][0, 0] [0, 1, 1, 1][0, 0] [0, 2, 1, 1][1, 1] [0, 3, 1, 1][1, 1] [0, 4, 1, 1][1, 1] [0, 5, 1, 1][0, 0] [0, 6, 1, 1][0, 0] [0, 0, 2, 1][0, 0] [0, 1, 2, 1][0, 0] [0, 2, 2, 1][1, 1] [0, 3, 2, 1][1, 1] [0, 4, 2, 1][1, 1] [0, 5, 2, 1][0, 0] [0, 6, 2, 1][0, 0] [0, 0, 0, 2][0, 0] [0, 1, 0, 2][0, 0] [0, 2, 0, 2][0, 0] [0, 3, 0, 2][1, 1] [0, 4, 0, 2][0, 0] [0, 5, 0, 2][0, 0] [0, 6, 0, 2][0, 0] [0, 0, 1, 2][0, 0] [0, 1, 1, 2][0, 0] [0, 2, 1, 2][0, 0] [0, 3, 1, 2][1, 1] [0, 4, 1, 2][0, 0] [0, 5, 1, 2][0, 0] [0, 6, 1, 2][0, 0] [0, 0, 2, 2][0, 0] [0, 1, 2, 2][0, 0] [0, 2, 2, 2][0, 0] [0, 3, 2, 2][1, 1] [0, 4, 2, 2][0, 0] [0, 5, 2, 2][0, 0] [0, 6, 2, 2][0, 0] [0, 0, 0, 3][0, 0] [0, 1, 0, 3][0, 0] [0, 2, 0, 3][0, 0] [0, 3, 0, 3][1, 1] [0, 4, 0, 3][0, 0] [0, 5, 0, 3][0, 0] [0, 6, 0, 3][0, 0] [0, 0, 1, 3][0, 0] [0, 1, 1, 3][0, 0] [0, 2, 1, 3][0, 0] [0, 3, 1, 3][1, 1] [0, 4, 1, 3][0, 0] [0, 5, 1, 3][0, 0] [0, 6, 1, 3][0, 0] [0, 0, 2, 3][0, 0] [0, 1, 2, 3][0, 0] [0, 2, 2, 3][0, 0] [0, 3, 2, 3][1, 1] [0, 4, 2, 3][0, 0] [0, 5, 2, 3][0, 0] [0, 6, 2, 3][0, 0] [0, 0, 0, 4][0, 0] [0, 1, 0, 4][0, 0] [0, 2, 0, 4][1, 1] [0, 3, 0, 4][1, 1] [0, 4, 0, 4][1, 1] [0, 5, 0, 4][0, 0] [0, 6, 0, 4][0, 0] [0, 0, 1, 4][0, 0] [0, 1, 1, 4][0, 0] [0, 2, 1, 4][1, 1] [0, 3, 1, 4][1, 1] [0, 4, 1, 4][1, 1] [0, 5, 1, 4][0, 0] [0, 6, 1, 4][0, 0] [0, 0, 2, 4][0, 0] [0, 1, 2, 4][0, 0] [0, 2, 2, 4][1, 1] [0, 3, 2, 4][1, 1] [0, 4, 2, 4][1, 1] [0, 5, 2, 4][0, 0] [0, 6, 2, 4][0, 0] [0, 0, 0, 5][1, 1] [0, 1, 0, 5][1, 1] [0, 2, 0, 5][1, 1] [0, 3, 0, 5][1, 1] [0, 4, 0, 5][1, 1] [0, 5, 0, 5][1, 1] [0, 6, 0, 5][1, 1] [0, 0, 1, 5][1, 1] [0, 1, 1, 5][1, 1] [0, 2, 1, 5][1, 1] [0, 3, 1, 5][1, 1] [0, 4, 1, 5][1, 1] [0, 5, 1, 5][1, 1] [0, 6, 1, 5][1, 1] [0, 0, 2, 5][1, 1] [0, 1, 2, 5][1, 1] [0, 2, 2, 5][1, 1] [0, 3, 2, 5][1, 1] [0, 4, 2, 5][1, 1] [0, 5, 2, 5][1, 1] [0, 6, 2, 5][1, 1] 1 [2, 3, 0, 3][3, 9, 2, 8][2, 7, 3, 6][20, 12, 10, 14] [0, 2][2, 0][3, 2] Axis Lengths: [7, 6] (NB: Matrix in Row/Column order) [1, 0, 0, 0, 0, 1 1, 0, 0, 0, 0, 1 1, 1, 0, 0, 1, 1 1, 1, 1, 1, 1, 1 1, 1, 0, 0, 1, 1 1, 0, 0, 0, 0, 1 1, 0, 0, 0, 0, 1] Ndim=3 Axis Lengths: [7, 6, 1] [0, 0, 0][1, 1, 1, 1, 1, 1, 1] [0, 1, 0][0, 0, 1, 1, 1, 0, 0] [0, 2, 0][0, 0, 0, 1, 0, 0, 0] [0, 3, 0][0, 0, 0, 1, 0, 0, 0] [0, 4, 0][0, 0, 1, 1, 1, 0, 0] [0, 5, 0][1, 1, 1, 1, 1, 1, 1] 1 [3, 3, 2][9, 8, 2][7, 6, 1][12, 14, 3] [2][2][2] LCStretch::LCStretch - no stretch axes have been specified LCStretch::LCStretch - number of axes in stretch box mismatches number of stretch axes LCStretch::LCStretch - stretch axes multiply specified or exceed nrdim LCStretch::LCStretch - a stretch axis does not have length 1 LCStretch::LCStretch - a stretch axis does not have length 1 OK casacore-3.7.1/lattices/LRegions/test/tLCUnion.cc000066400000000000000000000113551476623553700216440ustar00rootroot00000000000000//# tLCUnion.cc: mechanical test of the LCUnion class //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include void doIt (const IPosition& latticeShape, const IPosition& start, const IPosition& end, const IPosition& center, Float radius) { uInt ndim = latticeShape.nelements(); LCBox box (start, end, latticeShape); LCEllipsoid cir (center, radius, latticeShape); LCUnion inters (box, cir); AlwaysAssertExit (inters.hasMask()); AlwaysAssertExit (! inters.isWritable()); cout << inters.hasMask() << ' ' << endl; cout << inters.boundingBox().start() << inters.boundingBox().end() << inters.boundingBox().length() << inters.latticeShape() << endl; Array mask; inters.getSlice (mask, IPosition(ndim,0), inters.boundingBox().length(), IPosition(ndim,1)); cout << mask << endl; LCUnion inters1 (False, &box); AlwaysAssertExit (inters1.hasMask()); AlwaysAssertExit (! inters1.isWritable()); Array mask1; inters1.getSlice (mask1, IPosition(ndim,0), inters1.boundingBox().length(), IPosition(ndim,1)); AlwaysAssertExit (allEQ(mask1, True)); { // Test cloning. LCRegion* interscop = inters.cloneRegion(); AlwaysAssertExit (inters.hasMask() == interscop->hasMask()); AlwaysAssertExit (inters.boundingBox().start() == interscop->boundingBox().start()); AlwaysAssertExit (inters.boundingBox().end() == interscop->boundingBox().end()); AlwaysAssertExit (inters.boundingBox().stride() == interscop->boundingBox().stride()); AlwaysAssertExit (inters.boundingBox().length() == interscop->boundingBox().length()); Array arr; interscop->getSlice (arr, IPosition(ndim,0), inters.boundingBox().length(), IPosition(ndim,1)); AlwaysAssertExit (allEQ (arr, mask)); delete interscop; } { // Test persistency. LCRegion* interscop = (LCRegion::fromRecord (inters.toRecord(""), "")); AlwaysAssertExit (inters.hasMask() == interscop->hasMask()); AlwaysAssertExit (inters.boundingBox().start() == interscop->boundingBox().start()); AlwaysAssertExit (inters.boundingBox().end() == interscop->boundingBox().end()); AlwaysAssertExit (inters.boundingBox().stride() == interscop->boundingBox().stride()); AlwaysAssertExit (inters.boundingBox().length() == interscop->boundingBox().length()); Array arr; interscop->getSlice (arr, IPosition(ndim,0), inters.boundingBox().length(), IPosition(ndim,1)); AlwaysAssertExit (allEQ (arr, mask)); delete interscop; } { // Test ordered equality. LCUnion union1(box, cir); LCUnion union2(union1); AlwaysAssertExit (union1 == union2); } { // Test unordered equality. LCUnion union1(box, cir); LCUnion union2(cir, box); AlwaysAssertExit (union1 == union2); } } int main() { try { doIt (IPosition (2,11,20), IPosition (2,3,4), IPosition (2,7,8), IPosition (2,5,10), 5.); doIt (IPosition (2,10,20), IPosition (2,3,4), IPosition (2,7,8), IPosition (2,4,16), 5.); } catch (std::exception& x) { cout << "Caught exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/lattices/LRegions/test/tLCUnion.out000066400000000000000000000020501476623553700220560ustar00rootroot000000000000001 [0, 4][10, 15][11, 12][11, 20] Axis Lengths: [11, 12] (NB: Matrix in Row/Column order) [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0] 1 [0, 4][9, 19][10, 16][10, 20] Axis Lengths: [10, 16] (NB: Matrix in Row/Column order) [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0] OK casacore-3.7.1/lattices/LRegions/test/tLatticeRegion.cc000066400000000000000000000057071476623553700230720ustar00rootroot00000000000000//# tLatticeRegion.cc: Test program for LatticeRegion class //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include void doIt (const IPosition& latticeShape, const IPosition& start, const IPosition& end, const IPosition& center, Int radius) { uInt ndim = start.nelements(); // Show output of simple circle. LCEllipsoid cir (center, radius, latticeShape); LatticeRegion reg1(cir); AlwaysAssertExit (reg1.hasMask()); AlwaysAssertExit (cir.shape() == reg1.shape()); AlwaysAssertExit (allEQ (cir.get(), reg1.get())); cout << "circle: " << reg1.get() << endl; // Be sure that a slicer gives LatticeRegion reg2(Slicer(start,end,IPosition(2,2),Slicer::endIsLast), latticeShape); AlwaysAssertExit (! reg2.hasMask()); AlwaysAssertExit (reg2.get().shape() == 1+(end-start)/2); AlwaysAssertExit (allEQ (reg2.get(), True)); cout << "slicer: " << reg2.get() << endl; // Take a slicer of the slicer. LatticeRegion reg2a((Slicer(IPosition(ndim,0), reg2.shape()-1, IPosition(2,1,2), Slicer::endIsLast)), reg2.shape()); AlwaysAssertExit (! reg2a.hasMask()); AlwaysAssertExit (allEQ (reg2a.get(), True)); cout << "strided slicer: " << reg2a.get() << endl; } int main() { try { doIt (IPosition (2,11,20), IPosition (2,3,4), IPosition (2,7,8), IPosition (2,5,10), 5); } catch (std::exception& x) { cout << "Caught exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/lattices/LRegions/test/tLatticeRegion.out000066400000000000000000000011501476623553700233000ustar00rootroot00000000000000circle: Axis Lengths: [11, 11] (NB: Matrix in Row/Column order) [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0] slicer: Axis Lengths: [3, 3] (NB: Matrix in Row/Column order) [1, 1, 1 1, 1, 1 1, 1, 1] strided slicer: Axis Lengths: [3, 2] (NB: Matrix in Row/Column order) [1, 1 1, 1 1, 1] OK casacore-3.7.1/lattices/LatticeMath.h000066400000000000000000000037411476623553700175110ustar00rootroot00000000000000//# LatticeMath.h: Mathematical operations on lattices //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICEMATH_H #define LATTICES_LATTICEMATH_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // // Mathematical operations on lattices. // // //
      • module Lattices // // // // // LatticeMath contains a various groups of classes to do mathematics // on a lattice. //
          //
        • Statistics. //
        • Least squares fitting. //
        • FFT. //
        • Interpolation. //
        • Histogram. //
        • Clean. //
        //
        // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LatticeMath/000077500000000000000000000000001476623553700173335ustar00rootroot00000000000000casacore-3.7.1/lattices/LatticeMath/CLIPNearest2D.h000066400000000000000000000070651476623553700217530ustar00rootroot00000000000000//# CLIPNearest2D.h: Nearest neighbour interpolator for CurvedLattice2D //# Copyright (C) 2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have receied a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_CLIPNEAREST2D_H #define LATTICES_CLIPNEAREST2D_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Lattice; // // Arbitrarily shaped 1-dim lattice crosscut // // // // // //# Classes you should understand before using this one. //
      • CLInterpolator2D // // // CLIP means CLInterpolator (its base class). // The 2D means that interpolation in 2 dimensions needs to be done. // // // CLIPNearest2D is a realisation of the abstract base class CLInterpolator2D. // This class interpolates in a very simple way by taking the nearest // neighbour. // // Note that the base class contains the lattice to be interpolated and // the axis to be used in the interpolation. // template class CLIPNearest2D: public CLInterpolator2D { public: // Only default constructor is needed. // The set function in the base class defines the lattice and axes. CLIPNearest2D(); // Make a copy of the object. virtual CLIPNearest2D* clone() const; // Get the data for the given pixel points (on axis1 and axis2) and // the chunk in the other axes as given by the section. virtual void getData (Array& buffer, const Vector& x, const Vector& y, const Slicer& section); // Get the mask for the given pixel points (on axis1 and axis2) and // the chunk in the other axes as given by the section. virtual void getMask (Array& buffer, const Vector& x, const Vector& y, const Slicer& section); //# Make members of parent class known. protected: using CLInterpolator2D::itsAxesMap; using CLInterpolator2D::itsAxis1; using CLInterpolator2D::itsAxis2; using CLInterpolator2D::itsCurveAxis; using CLInterpolator2D::itsIsRef; using CLInterpolator2D::itsLatticePtr; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/LatticeMath/CLIPNearest2D.tcc000066400000000000000000000134651476623553700222760ustar00rootroot00000000000000//# CLIPNearest2D.h: Nearest neighbour interpolator for CurvedLattice2D //# Copyright (C) 2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have receied a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_CLIPNEAREST2D_TCC #define LATTICES_CLIPNEAREST2D_TCC #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template CLIPNearest2D::CLIPNearest2D() {} template CLIPNearest2D* CLIPNearest2D::clone() const { return new CLIPNearest2D (*this); } template void CLIPNearest2D::getData (Array& buffer, const Vector& x, const Vector& y, const Slicer& section) { // Determine the shape and positions w.r.t. the original lattice. IPosition shp = itsAxesMap.shapeToOld (buffer.shape()); shp[itsAxis1] = 1; shp[itsAxis2] = 1; IPosition blc = itsAxesMap.posToOld (section.start()); IPosition leng = itsAxesMap.shapeToOld (section.length()); leng[itsAxis1] = 1; leng[itsAxis2] = 1; IPosition incr = itsAxesMap.shapeToOld (section.stride()); // We have to get the data from the lattice chunk by chunk. // Each chunk is determined by a pixel on the curve. // If the curve axis is the last axis, we can use ArrayIterator // (which is faster (I think) than slicing arrays ourselves). if (itsCurveAxis == buffer.ndim() - 1) { shp.append (IPosition(1, buffer.shape()[itsCurveAxis])); Array data = buffer.reform (shp); ArrayIterator iter(data, shp.nelements()-1); for (uInt i=0; igetSlice (blc, leng, incr); } else { Bool isRef = itsLatticePtr->getSlice (iter.array(), blc, leng, incr); // Just make sure it is not a reference. AlwaysAssert (!isRef, AipsError); } iter.next(); } } else { IPosition start = IPosition(buffer.ndim(), 0); IPosition end = buffer.shape() - 1; for (uInt i=0; i data = buffer(start,end).reform(shp); blc[itsAxis1] = Int(x[i]+0.5); blc[itsAxis2] = Int(y[i]+0.5); if (itsIsRef) { data = itsLatticePtr->getSlice (blc, leng, incr); } else { Bool isRef = itsLatticePtr->getSlice (data, blc, leng, incr); AlwaysAssert (!isRef, AipsError); } itsLatticePtr->getSlice (data, blc, leng, incr); } } } template void CLIPNearest2D::getMask (Array& buffer, const Vector& x, const Vector& y, const Slicer& section) { // Determine the shape and positions w.r.t. the original lattice. IPosition shp = itsAxesMap.shapeToOld (buffer.shape()); shp[itsAxis1] = 1; shp[itsAxis2] = 1; IPosition blc = itsAxesMap.posToOld (section.start()); IPosition leng = itsAxesMap.shapeToOld (section.length()); leng[itsAxis1] = 1; leng[itsAxis2] = 1; IPosition incr = itsAxesMap.shapeToOld (section.stride()); // We have to get the data from the lattice chunk by chunk. // Each chunk is determined by a pixel on the curve. // If the curve axis is the last axis, we can use ArrayIterator // (which is faster (I think) than slicing arrays ourselves). if (itsCurveAxis == buffer.ndim() - 1) { shp.append (IPosition(1, buffer.shape()[itsCurveAxis])); Array data = buffer.reform (shp); ArrayIterator iter(data, shp.nelements()-1); for (uInt i=0; i ref(iter.array()); Bool isRef = itsLatticePtr->getMaskSlice (ref, blc, leng, incr); if (isRef) { iter.array() = ref; } iter.next(); } } else { IPosition start = IPosition(buffer.ndim(), 0); IPosition end = buffer.shape() - 1; for (uInt i=0; i data = buffer(start,end).reform(shp); blc[itsAxis1] = Int(x[i]+0.5); blc[itsAxis2] = Int(y[i]+0.5); Array ref(data); Bool isRef = itsLatticePtr->getMaskSlice (ref, blc, leng, incr); if (isRef) { data = ref; } } } } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LatticeMath/CLInterpolator2D.h000066400000000000000000000121561476623553700226000ustar00rootroot00000000000000//# CLInterpolator2D.h: Abstract base class for interpolator used by CurvedLattice2D //# Copyright (C) 2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have receied a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_CLINTERPOLATOR2D_H #define LATTICES_CLINTERPOLATOR2D_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class MaskedLattice; // // Abstract base class for interpolator used by CurvedLattice2D. // // // // // //# Classes you should understand before using this one. //
      • CurvedLattice2D // // // The CL in CLInterpolator2D means CurvedLattice. // The 2D means that interpolation in 2 dimensions needs to be done. // // // CurvedLattice2D needs lattice data which are not on exact grid points. // Therefore some interpolation scheme is needed. The abstract base class // CLInterpolation2D makes it possible for CurvedLattice2D to use any // interpolation scheme. // Currently the only derived class is // CLIPNearest2D //
        // Apart from interpolating and returning data, a derived class also has to // return a mask. For instance, in a possible derived class using // 4-point interpolation, the interpolation scheme has to take the // image mask into account, and make a mask for its output data (say that // the output point is masked off if its 4 input points are masked off). // // This base class has some data members defining the lattice and the // lattice axes to be interpolated. When these data members are set, // the virtual function preset is called. A derived class // can implement this function to do some precalculations, etc.. //
        // // This class makes it possible to hide the interpolation to be used // from the CurvedLattice2D class. // template class CLInterpolator2D { public: CLInterpolator2D() : itsLatticePtr(0) {;} virtual ~CLInterpolator2D(); // Let a derived class make a copy of itself. virtual CLInterpolator2D* clone() const = 0; // Set the internals to the values of the CurvedLattice using it. // Note that only a copy of the lattice pointer is made. // Thereafter the virtual function preset() is called to give a derived // class the opportunity to do some initial work. void set (MaskedLattice* lattice, const AxesMapping& axesMap, uInt axis1, uInt axis2, uInt curveAxis); // Get the data for the given pixel points (on axis1 and axis2) and // the chunk in the other axes as given by the section. // The Slicer is fixed and the buffer has the correct shape. virtual void getData (Array& buffer, const Vector& x, const Vector& y, const Slicer& section) = 0; // Get the mask for the given pixel points (on axis1 and axis2) and // the chunk in the other axes as given by the section. // The Slicer is fixed and the buffer has the correct shape. virtual void getMask (Array& buffer, const Vector& x, const Vector& y, const Slicer& section) = 0; protected: // Copy constructor can only be used by derived classes. CLInterpolator2D (const CLInterpolator2D&); // Assignment can only be used by derived classes. CLInterpolator2D& operator= (const CLInterpolator2D&); // Let a derived class do some initial work after set is called. // The default implementation does nothing. virtual void preset(); MaskedLattice* itsLatticePtr; AxesMapping itsAxesMap; uInt itsAxis1; uInt itsAxis2; uInt itsCurveAxis; Bool itsIsRef; // True = lattice returns array reference }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/LatticeMath/CLInterpolator2D.tcc000066400000000000000000000046751476623553700231310ustar00rootroot00000000000000//# CLInterpolator2D.h: Base class interpolator for CurvedLattice2D //# Copyright (C) 2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have receied a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_CLINTERPOLATOR2D_TCC #define LATTICES_CLINTERPOLATOR2D_TCC #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template CLInterpolator2D::~CLInterpolator2D() {} template CLInterpolator2D::CLInterpolator2D (const CLInterpolator2D& that) { set (that.itsLatticePtr, that.itsAxesMap, that.itsAxis1, that.itsAxis2, that.itsCurveAxis); } template CLInterpolator2D& CLInterpolator2D::operator= (const CLInterpolator2D& that) { if (this != &that) { set (that.itsLatticePtr, that.itsAxesMap, that.itsAxis1, that.itsAxis2, that.itsCurveAxis); } return *this; } template void CLInterpolator2D::set (MaskedLattice* lattice, const AxesMapping& axesMap, uInt axis1, uInt axis2, uInt curveAxis) { itsLatticePtr = lattice; itsAxesMap = axesMap; itsAxis1 = axis1; itsAxis2 = axis2; itsCurveAxis = curveAxis; if (lattice) { itsIsRef = lattice->canReferenceArray(); preset(); } else { itsIsRef = False; } } template void CLInterpolator2D::preset() {} } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LatticeMath/Fit2D.cc000066400000000000000000000446341476623553700205650ustar00rootroot00000000000000//# Fit2D.cc: Class to fit 2D objects to a Lattice or Array //# Copyright (C) 1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Fit2D::Fit2D(LogIO& logger) : itsLogger(logger), itsValid(False), itsValidSolution(False), itsChiSquared(0.0) { } Fit2D::Fit2D(const Fit2D& other) : itsLogger(other.itsLogger), // Reference semantics itsValid(other.itsValid), itsValidSolution(other.itsValidSolution), itsHasSigma(other.itsHasSigma), itsInclude(other.itsInclude), itsPixelRange(other.itsPixelRange.copy()), // Copy semantics itsFunction(other.itsFunction), itsSolution(other.itsSolution.copy()), itsErrors(other.itsErrors.copy()), itsChiSquared(other.itsChiSquared), itsErrorMessage(other.itsErrorMessage), itsNumberPoints(other.itsNumberPoints), itsTypeList(other.itsTypeList.copy()) // Copy semantics { // // Note that the variable itsFitter is not copied. // This is because the fitting classes have no // assignment operator or copy constructor. However, // it doesn't matter, because the fitter is always // set as needed by the "fit" function. The fact that it // is private is just to avoid creating it over and over } Fit2D::~Fit2D() { } Fit2D& Fit2D::operator=(const Fit2D& other) // // Note that the variable itsFitter is not copied. // This is because the fitting classes have no // assignment operator or copy constructor. However, // it doesn't matter, because the fitter is always // set as needed by the "fit" function. The fact that it // is private is just to avoid creating it over and over // { if (this != &other) { itsLogger = other.itsLogger; // Reference semantics itsValid = other.itsValid; itsValidSolution = other.itsValidSolution; itsHasSigma = other.itsHasSigma; itsInclude = other.itsInclude; itsPixelRange = other.itsPixelRange.copy(); // Copy semantics itsFunction = other.itsFunction; itsSolution = other.itsSolution.copy(); itsErrors = other.itsErrors.copy(); itsChiSquared = other.itsChiSquared; itsErrorMessage = other.itsErrorMessage; itsNumberPoints = other.itsNumberPoints; itsTypeList = other.itsTypeList.copy(); // Copy semantics } return *this; } uInt Fit2D::addModel (Fit2D::Types type, const Vector& parameters, const Vector& parameterMask) { const uInt nModels = itsTypeList.nelements() + 1; itsTypeList.resize(nModels,True); // if (type==Fit2D::LEVEL) { ConstantND > myconst(2); myconst[0] = AutoDiff(parameters(0), 1, 0); myconst.mask(0) = parameterMask(0); itsFunction.addFunction(myconst); itsTypeList(nModels-1) = Fit2D::LEVEL; } else if (type==Fit2D::DISK) { itsLogger << "Fit2D - Disk fitting not yet implemented" << LogIO::EXCEPTION; } else if (type==Fit2D::PLANE) { HyperPlane > plane(3); if (parameters.nelements() != 3) { itsLogger << "Fit2D - illegal number of parameters in addModel" << LogIO::EXCEPTION; } } else if (type==Fit2D::GAUSSIAN) { // // Create functional // Gaussian2D > gauss2d; if (parameters.nelements() != gauss2d.nparameters()) { itsLogger << "Fit2D - illegal number of parameters in addModel" << LogIO::EXCEPTION; } if (parameterMask.nelements() != gauss2d.nparameters()) { itsLogger << "Fit2D - illegal number of mask parameters in addModel" << LogIO::EXCEPTION; } // // Set parameters. 0 (flux), 1 (x), 2 (y), 3 (FWHM major), 4 (FWHM minor), // 5 (pa - in radians). Convert p.a. from positive +x -> +y // to +y -> -x for Gaussian2D. Note that fixing the ratio is not // the same as fixing the minor axis, which is what the Fit2D interface // claims to do. I don't know how to solve this presently. // Int ii = Gaussian2D::HEIGHT; gauss2d[ii] = AutoDiff(parameters(0), gauss2d.nparameters(), ii); // flux gauss2d.mask(ii) = parameterMask(0); ii = Gaussian2D::XCENTER; gauss2d[ii] = AutoDiff(parameters(1), gauss2d.nparameters(), ii); // x gauss2d.mask(ii) = parameterMask(1); ii = Gaussian2D::YCENTER; gauss2d[ii] = AutoDiff(parameters(2), gauss2d.nparameters(), ii); // y gauss2d.mask(ii) = parameterMask(2); ii = Gaussian2D::YWIDTH; gauss2d[ii] = AutoDiff(parameters(3), gauss2d.nparameters(), ii); // major gauss2d.mask(ii) = parameterMask(3); ii = Gaussian2D::RATIO; Double ratio = parameters(4) / parameters(3); gauss2d[ii] = AutoDiff(ratio, gauss2d.nparameters(), ii); // ratio gauss2d.mask(ii) = parameterMask(4); ii = Gaussian2D::PANGLE; Double pa = paToGauss2D(parameters(5)); piRange(pa); gauss2d[ii] = AutoDiff(pa, gauss2d.nparameters(), ii); // p.a. gauss2d.mask(ii) = parameterMask(5); // // Add it to function we are going to fit // itsFunction.addFunction(gauss2d); itsTypeList(nModels-1) = Fit2D::GAUSSIAN; } itsValid = True; return nModels - 1; } uInt Fit2D::addModel (Fit2D::Types type, const Vector& parameters) { Vector parameterMask(parameters.nelements(),True); return addModel(type, parameters, parameterMask); } uInt Fit2D::nModels() const { return itsFunction.nFunctions(); } Vector Fit2D::convertMask (const String mask, Fit2D::Types type) { Vector parameterMask; String cmask = mask; cmask.downcase(); if (type==Fit2D::LEVEL) { parameterMask.resize(1); parameterMask = True; if (cmask.contains("l")) { parameterMask(0) = False; } } else if (type==Fit2D::DISK || type==Fit2D::GAUSSIAN) { parameterMask.resize(6); parameterMask = True; if (cmask.contains("f")) { parameterMask(0) = False; } if (cmask.contains("x")) { parameterMask(1) = False; } if (cmask.contains("y")) { parameterMask(2) = False; } if (cmask.contains("a")) { parameterMask(3) = False; } if (cmask.contains("b")) { parameterMask(4) = False; } if (cmask.contains("p")) { parameterMask(5) = False; } } return parameterMask; } uInt Fit2D::nParameters(Fit2D::Types type) { uInt n = 0; if (type==Fit2D::LEVEL) { throw (AipsError("Fit2D - Level fitting not yet implemented")); } else if (type==Fit2D::DISK) { throw (AipsError("Fit2D - Disk fitting not yet implemented")); } else if (type==Fit2D::GAUSSIAN) { n = 6; } return n; } Fit2D::ErrorTypes Fit2D::residual(Array& resid, Array& model, const MaskedLattice& data) { Array pixels = data.get(True); return residual(resid, model, pixels); } Fit2D::ErrorTypes Fit2D::residual(Array& resid, Array& model, const Lattice& data) { Array pixels = data.get(True); return residual(resid, model, pixels); } void Fit2D::setIncludeRange (Double minVal, Double maxVal) { itsPixelRange.resize(2); itsPixelRange(0) = min(minVal, maxVal); itsPixelRange(1) = max(minVal, maxVal); itsInclude = True; } void Fit2D::setExcludeRange (Double minVal, Double maxVal) { itsPixelRange.resize(2); itsPixelRange(0) = min(minVal, maxVal); itsPixelRange(1) = max(minVal, maxVal); itsInclude = False; } void Fit2D::resetRange() { itsPixelRange.resize(0); } String Fit2D::type(Fit2D::Types type) { if (type==Fit2D::LEVEL) { return String("Level"); } else if (type==Fit2D::DISK) { return String("Disk"); } else if (type==Fit2D::GAUSSIAN) { return String("Gaussian"); } return String(""); } Fit2D::Types Fit2D::type(const String& type) { String t0 = type; String tmp = upcase(t0.at(0,1)); Fit2D::Types tmp2; if (tmp==String("L")) { tmp2 = Fit2D::LEVEL; } else if (tmp==String("D")) { tmp2 = Fit2D::DISK; } else if (tmp==String("G")) { tmp2 = Fit2D::GAUSSIAN; } else { throw(AipsError("Fit2D::type - illegal model type")); } return tmp2; } Fit2D::Types Fit2D::type(uInt which) { if (which >= itsFunction.nFunctions()) { itsLogger << "Fit2D::type - illegal model index" << LogIO::EXCEPTION; } return (Fit2D::Types)itsTypeList(which); } Vector Fit2D::availableSolution () const // // Conversion of Gaussian models from axial ratio // to minor axis is done // { const uInt nF = itsFunction.nFunctions(); Vector sol(itsFunction.nparameters()); for (uInt i=0, l=0; i sol2 = availableSolution(i).copy(); for (uInt j=0; j Fit2D::availableSolution (uInt which) const // // For Gaussian models, convert axial ratio to minor axis // and fiddle position angle to be that of the major axis, // positive +x -> +y // { if (!itsValidSolution) { Vector tmp; return tmp; } // if (which >= itsFunction.nFunctions()) { itsLogger << "Fit2D::availableSolution - illegal model index" << LogIO::EXCEPTION; } // uInt iStart; Vector sol = availableSolution(iStart, which).copy(); // // Convert Gaussian solution axial ratio to major/minor axis. // sol2(3) may be the major or minor axis after fitting. // The solution may have a negative axial ratio // if (itsTypeList(which)==Fit2D::GAUSSIAN) { Int iY = Gaussian2D::YWIDTH; Int iR = Gaussian2D::RATIO; Int iPA = Gaussian2D::PANGLE; // Double other = abs(sol(iY) * sol(iR)); Double ywidth = abs(sol(iY)); Double major, minor, pa; if (ywidth > other) { major = ywidth; minor = other; pa = sol(iPA); } else { major = other; minor = ywidth; pa = sol(iPA) + M_PI_2; // pa off by 90 } // // Convert the position angle from positive // +y -> -x to positive +x -> +y // sol(3) = major; sol(4) = minor; sol(5) = paFromGauss2D(pa); piRange(sol(5)); } // return sol; } Vector Fit2D::availableSolution(uInt& iStart, uInt which) const { // // Loop over models and figure out where the model of // interest starts in the solution vector. Returns // all (adjustable + fixed) parameters directly as solved // for; no axial or position angle conversion // iStart = itsFunction.parameterOffset(which); // // Find the number of available parameters for the model of interest // uInt nP = itsFunction.function(which).nparameters(); if (itsSolution.nelements() < iStart+nP) { itsLogger << LogIO::SEVERE << "Fit2D::availableSolution - " "solution vector is not long enough; did you call function fit ?" << LogIO::POST; } // Vector sol(nP); for (uInt i=0; i Fit2D::availableErrors () const // // Conversion of Gaussian models from axial ratio // to minor axis is done // { const uInt nF = itsFunction.nFunctions(); Vector errors(itsFunction.nparameters()); for (uInt i=0, l=0; i errors2 = availableErrors(i).copy(); for (uInt j=0; j Fit2D::availableErrors (uInt which) const // // For Gaussian models, convert axial ratio to minor axis // { if (!itsValidSolution) { Vector tmp; return tmp; } // if (which >= itsFunction.nFunctions()) { itsLogger << "Fit2D::availableErrors - illegal model index" << LogIO::EXCEPTION; } // uInt iStart; Vector errors = availableErrors (iStart, which).copy(); Vector sol = availableSolution (iStart, which).copy(); // // Convert Gaussian solution axial ratio to major/minor axis. // ratio = other / YWIDTH // sol(4) = other / sol(3) // // if (itsTypeList(which)==Fit2D::GAUSSIAN) { Int iY = Gaussian2D::YWIDTH; Int iR = Gaussian2D::RATIO; // Double other = abs(sol(iY) * sol(iR)); Double yWidth = abs(sol(iY)); Double ratio = abs(sol(iR)); // Double sigRatio = errors(iR); Double sigYWidth = errors(iY); /* // Use standard propagation of errors to get error in other Double f1 = sigRatio * sigRatio / ratio / ratio; Double f2 = sigYWidth * sigYWidth / yWidth / yWidth; Double sigOther = other * sqrt(f1 + f2); */ // The propagation errors are too large. Try using // same fractional error... I need to find better ways // to deal with the Gaussian as wdith and ratio Double sigOther = other * (sigRatio/ratio); /* cerr << "ratio, major, other = " << ratio << ", " << yWidth << ", " << other << endl; cerr << "sigRatio, sigMajor, sigOther = " << sigRatio << ", " << sigYWidth << ", " << sigOther << endl; */ if (yWidth > other) { // ywidth is major, other is minor errors(4) = sigOther; // minor errors(3) = sigYWidth; // major } else { // ywidth is minor, other is major errors(4) = sigYWidth; // minor errors(3) = sigOther; // major } } // return errors; } Vector Fit2D::availableErrors (uInt& iStart, uInt which) const { // // Loop over models and figure out where the model of // interest starts in the solution vector. // iStart = itsFunction.parameterOffset(which); // // Find the number of available parameters for the model of interest // uInt nP = itsFunction.function(which).nparameters(); if (itsErrors.nelements() < iStart+nP) { itsLogger << LogIO::SEVERE << "Fit2D::availableErrors - " "errors vector is not long enough; did you call function fit ?" << LogIO::POST; } // Vector errors(nP,0.0); for (uInt i=0; i Fit2D::getParams(uInt which) const // // Recover the available parameters for this model // from the SumFunction // { Vector params(itsFunction.function(which).nparameters()); for (uInt i=0; i ¶ms, uInt which) // // Set the available parameters for this model // from the SumFunction // { for (uInt i=0; i& values, const Matrix& pos, const Vector& weights) // // Do the actual fit // { // Set maximum number of iterations to 1000 itsFitter.setMaxIter(1000); // Set converge criteria. Default is 0.001 itsFitter.setCriteria(0.001); // Set the function and initial values itsFitter.setFunction(itsFunction); // Find the solution itsChiSquared = 0; itsErrorMessage = ""; Fit2D::ErrorTypes status = Fit2D::OK; itsSolution.resize(0); try { // itsSolution and itsErrors holds values and errors for adjustable and fixed parameters itsSolution = itsFitter.fit(pos, values, weights); itsErrors = itsFitter.errors(); if(!itsFitter.converged()) { itsErrorMessage = String("The fit did not converge"); status = Fit2D::NOCONVERGE; } // // Find chi-squared. // itsChiSquared = itsFitter.chiSquare(); // // A valid solution includes non-convergence // itsValidSolution = True; } catch (std::exception& x) { itsErrorMessage = String("Fitting failed because ") + x.what(); status = Fit2D::FAILED; } // return status; } void Fit2D::piRange (Double& pa) const // // Put angle in radians in range +/- pi // { MVAngle pa2(pa); pa2(); pa = pa2.radian(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LatticeMath/Fit2D.h000066400000000000000000000303351476623553700204200ustar00rootroot00000000000000//# Fit2D.h: Class to fit 2-D objects to Lattices or Arrays //# Copyright (C) 1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_FIT2D_H #define LATTICES_FIT2D_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template class Lattice; template class MaskedLattice; // // Fit 2-D objects to 2-D Lattices or Arrays // // // // // //
      • Lattice // // // This class allows you to fit different types of 2-D models // to either Lattices or Arrays. These must be 2 dimensional; // for Lattices, the appropriate 2-D Lattice can be made with // the SubLattice class. // // You may fit more than one model simultaneously to the data. // Models are added with the addModel method. With this method, // you also specify the initial guesses of the parameters of // the model. Any parameters involving coordinates are // expected in zero-relative absolute pixel coordinates (e.g. the centre of // a model). Additionally with the addModel method, // you may specify which parameters are to be held fixed // during the fitting process. This is done with the // parameterMask Vector which is in the same order as the // parameter Vector. A value of True indicates the parameter // will be fitted for. Presently, when you say fix the minor axis, // you really end up fixing the axial ratio (internals). I don't // have a solution for this presently. // // For Gaussians, the parameter Vector (input or output) consists, in order, of // the peak, x location, y location, FWHM of major axis, FWHM of minor axis, // and position angle of the major axis (in radians). The // position angle is positive +x to +y // in the pixel coordinate system ([0,0] in center of image) and // in the range -2pi to 2pi. When the solution is recovered, the // position angle will be in the range 0 to pi. // // // // // // // //
      • template it //
      • Speed up some Array calculations indexed with IPositions //
      • Don't handle Lattices simply by getting pixels into Arrays //
      • make an addModel interface taking functionals // class Fit2D { public: // Enum describing the different models you can fit enum Types { GAUSSIAN = 0, DISK = 1, LEVEL=2, PLANE=3, nTypes }; // Enum describing output error conditions enum ErrorTypes { // ok OK = 0, // Did not converge NOCONVERGE = 1, // Solution failed FAILED = 2, // There were no unmasked points NOGOOD = 3, // No models set NOMODELS = 4, // Number of conditions nErrorTypes }; // Constructor explicit Fit2D(LogIO& logger); // Destructor ~Fit2D(); // Copy constructor. Uses copy semantics except for the logger // for which a reference copy is made Fit2D(const Fit2D& other); // Assignment operator. Uses copy semantics except for the logger // for which a reference copy is made Fit2D& operator=(const Fit2D& other); // Add a model to the list to be simultaneously fit and // return its index. Specify the initial guesses for // the model and a mask indicating whether the parameter // is fixed (False) during the fit or not. Returns the // the model number added (0, 1, 2 etc) // uInt addModel (Fit2D::Types type, const Vector& parameters, const Vector& parameterMask); uInt addModel(Fit2D::Types type, const Vector& parameters); // // Convert mask from a string to a vector. The string gives the parameters // to keep fixed in the fit (f (flux), x (x position), y (y position), // a (FWHM major axis), b (FWHM minor axis), p (position angle) static Vector convertMask (const String fixedmask, Fit2D::Types type); // Set a pixel selection range. When the fit is done, only // pixels in the specified range are included/excluded. // Only the last call of either of these will be active. // void setIncludeRange (Double minVal, Double maxVal); void setExcludeRange (Double minVal, Double maxVal); void resetRange(); // // Return number of parameters for this type of model static uInt nParameters (Fit2D::Types type); // Recover number of models uInt nModels() const; // Determine an initial estimate for the solution of the specified // model type to the given data - no compound models are allowable // in this function. If you have specified an include // or exclude pixel range to the fitter, that will be honoured. // This function does not interact with the addModel function. // Returns a zero length vector if it fails to make an estimate. // template Vector estimate(Fit2D::Types type, const MaskedLattice& data); template Vector estimate( Fit2D::Types type, const Lattice& data ); template Vector estimate( Fit2D::Types type, const Array& data ); template Vector estimate( Fit2D::Types type, const Array& data, const Array& mask ); // // Do the fit. Returns an enum value to tell you what happened if the fit failed // for some reasons. A message can also be found with function errorMessage if // the fit was not successful. For Array(i,j) i is x and j is y // template Fit2D::ErrorTypes fit(const MaskedLattice& data, const Lattice& sigma); template Fit2D::ErrorTypes fit(const Lattice& data, const Lattice& sigma); template Fit2D::ErrorTypes fit(const Array& data, const Array& sigma); template Fit2D::ErrorTypes fit(const Array& data, const Array& mask, const Array& sigma); // // Find the residuals to the fit. xOffset and yOffset allow one to provide a data // array that is offset in space from the grid that was fit. In this way, one // can fill out a larger image than the subimage that was fit, for example. A negative // value of xOffset means the supplied data array represents a grid that has a y axis left // of the grid of pixels that was fit. A negative yOffset value means the supplied data // array represents a grid that has an x axis that is below the x axis of the grid of pixels // that was fit. // NOTE these may need to be templated at some point in the future. My // current need does not require they be templated. - dmehring 29jun2018 // template Fit2D::ErrorTypes residual( Array& resid, Array& model, const Array& data, Int xOffset=0, int yOffset=0 ) const; Fit2D::ErrorTypes residual(Array& resid, Array& model, const MaskedLattice& data); Fit2D::ErrorTypes residual(Array& resid, Array& model, const Lattice& data); // // If function fit failed, you will find a message here // saying why it failed String errorMessage () const; // Recover solution for either all model components or // a specific one. These functions will return an empty vector // if there is no valid solution. All available parameters (fixed and // adjustable) are included in the solution vectors. // Vector availableSolution () const; Vector availableSolution (uInt which) const; // // The errors. All available parameters (fixed and adjustable) are // included in the error vectors. Unsolved for parameters will // have error 0. // Vector availableErrors() const; Vector availableErrors(uInt which) const; // // The number of iterations that the fitter finished with uInt numberIterations() const; // The chi squared of the fit. Returns 0 if fit has been done. Double chiSquared () const; // The number of points used for the last fit uInt numberPoints () const; // Return type as a string static String type(Fit2D::Types type); // Return string type as enum (min match) static Fit2D::Types type(const String& type); // Find type of specific model Fit2D::Types type(uInt which); // Convert p.a. (radians) from positive +x -> +y // (Fit2D) to positive +y -> -x (Gaussian2D) static Double paToGauss2D (Double pa) {return pa - C::pi_2;}; // Convert p.a. (radians) from positive +y -> -x // (Gaussian2D) to positive +x -> +y (Fit2D) static Double paFromGauss2D (Double pa) {return pa + C::pi_2;}; private: mutable LogIO itsLogger; Bool itsValid, itsValidSolution, itsHasSigma; Bool itsInclude; Vector itsPixelRange; CompoundFunction> itsFunction; NonLinearFitLM itsFitter; Vector itsSolution; Vector itsErrors; Double itsChiSquared; String itsErrorMessage; uInt itsNumberPoints; Vector itsTypeList; Fit2D::ErrorTypes fitData(const Vector& values, const Matrix& pos, const Vector& sigma); // Returns available (adjustable + fixed) solution for model of // interest and tells you where it began in the full solution vector // Does no axial ratio nor position angle conversions from direct // fit solution vector // Vector availableSolution (uInt& iStart, uInt which) const; Vector availableErrors (uInt& iStart, uInt which) const; // Vector getParams(uInt which) const; void setParams(const Vector& params, uInt which); Bool includeIt (Double value, const Vector& range, Int includeIt) const; template Bool selectData ( Matrix& pos, Vector& values, Vector& weights, const Array& pixels, const Array& mask, const Array& sigma ); void piRange (Double& pa) const; }; inline Bool Fit2D::includeIt (Double value, const Vector& range, Int includeIt) const { if (includeIt==0) return True; // if (includeIt==1) { if (value >= range(0) && value <= range(1)) return True; } else if (value < range(0) || value > range(1)) { return True; } // return False; } } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/LatticeMath/Fit2D2.tcc000066400000000000000000000331441476623553700210250ustar00rootroot00000000000000//# Fit2D2.tcc: Class to fit 2D objects to a Lattice or Array //# Copyright (C) 1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_FIT2D2_TCC #define LATTICES_FIT2D2_TCC #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template Vector Fit2D::estimate(Fit2D::Types type, const MaskedLattice& data) { if (data.shape().nelements() !=2) { itsLogger << "Fit2D::estimate - Lattice must be 2-dimensional" << LogIO::EXCEPTION; } auto pixels = data.get(True); auto mask = data.getMask(True); return estimate(type, pixels, mask); } template Vector Fit2D::estimate(Fit2D::Types type, const Lattice& data) { if (data.shape().nelements() !=2) { itsLogger << "Fit2D::estimate - Lattice must be 2-dimensional" << LogIO::EXCEPTION; } auto pixels = data.get(True); Array mask(pixels.shape(),True); return estimate(type, pixels, mask); } template Vector Fit2D::estimate(Fit2D::Types type, const Array& data) { if (data.shape().nelements() !=2) { itsLogger << "Fit2D::estimate - Array must be 2-dimensional" << LogIO::EXCEPTION; } Array mask(data.shape(),True); return estimate(type, data, mask); } template Vector Fit2D::estimate(Fit2D::Types type, const Array& data, const Array& mask) // // Work out an initial estimate to the solution using Bob Sault's // probabilistic approach from Miriad imfit.for Only works // for single models. Honours and inclusion/exclusion pixel range // // PA sign convention in pixel coordinate is +x -> +y is positive // { if (type!=Fit2D::GAUSSIAN && type==Fit2D::DISK) { itsLogger << "Only Gaussian and disk models are currently supported" << LogIO::EXCEPTION; } // Vector parameters; auto shape = data.shape(); if (shape.nelements() !=2) { itsLogger << "Fit2D::estimate - Array must be 2-dimensional" << LogIO::EXCEPTION; } if (mask.shape().nelements() !=2) { itsLogger << "Fit2D::estimate - Mask must be 2-dimensional" << LogIO::EXCEPTION; } // // Find min and max // MaskedArray pixels(data, mask); T minVal, maxVal; IPosition minPos(2), maxPos(2); minMax(minVal, maxVal, minPos, maxPos, pixels); // // For the purposed of the estimate, chuck away pixels // below abs(5%) of the peak // T clip = 0.05 * max(abs(minVal), abs(maxVal)); // // Accumulate sums. Array indexing is not fast. // Int includeThem = 0; if (itsPixelRange.nelements()==2) { if (itsInclude) { includeThem = 1; } else { includeThem = 2; } } // Double P, XP, YP, XYP, XXP, YYP; Double t, fac, SP; P = XP = YP = XYP = XXP = YYP = 0.0; SP = 0.0; // IPosition pos(2); Double ri, rj; uInt nPts = 0; for (Int j=0; jclip) { ri = i; rj = j; // SP += val; P += t; XP += t*ri; YP += t*rj; XYP += t*ri*rj; XXP += t*ri*ri; YYP += t*rj*rj; nPts++; } } } if (nPts==0) { itsLogger << LogIO::WARN << "There are not enough good points in the array for a good estimate" << LogIO::POST; return parameters; } // Double t2; if (type==Fit2D::GAUSSIAN || type==Fit2D::DISK) { parameters.resize(6); // fac = 4*log(2.0); XP = XP / P; YP = YP / P; XYP = XYP / P - XP*YP; XXP = XXP / P - XP*XP; YYP = YYP / P - YP*YP; // parameters(1) = XP; parameters(2) = YP; // parameters(3) = sqrt(fac*(XXP + YYP + sqrt( square(XXP-YYP) + 4*square(XYP) ))); parameters(4) = sqrt(fac*(XXP + YYP - sqrt( square(XXP-YYP) + 4*square(XYP) ))); t2 = 0.5*atan2(2*XYP,YYP-XXP); parameters(5) = paFromGauss2D(-t2); piRange(parameters(5)); // Double sn = 1.0; if (SP<0) sn = -1.0; parameters(0) = sn * fac * P / ( C::pi * parameters(3) * parameters(4)); } else if (type==Fit2D::LEVEL) { itsLogger << "Level models are not currently supported" << LogIO::EXCEPTION; } // parameters(3) *= 0.95; // In case estimate is circular // return parameters; } template Fit2D::ErrorTypes Fit2D::fit( const MaskedLattice& data, const Lattice& sigma ) { if (!itsValid) { itsErrorMessage = "No models have been set - use function addModel"; return Fit2D::NOMODELS; } // Get data auto pixels = data.get(True); auto shape = pixels.shape(); if (shape.nelements() !=2) { itsLogger << "Fit2D::fit - Region must be 2-dimensional" << LogIO::EXCEPTION; } auto mask = data.getMask(True); // // Do fit // if (sigma.ndim()==0) { Array sigma2; return fit(pixels, mask, sigma2); } else { auto sigma2 = sigma.get(True); return fit(pixels, mask, sigma2); } } template Fit2D::ErrorTypes Fit2D::fit(const Lattice& data, const Lattice& sigma) { if (!itsValid) { itsErrorMessage = "No models have been set - use function addModel"; return Fit2D::NOMODELS; } auto pixels = data.get(True); IPosition shape = pixels.shape(); if (shape.nelements() !=2) { itsLogger << "Fit2D::fit - Region must be 2-dimensional" << LogIO::EXCEPTION; } Array mask; if (sigma.ndim()==0) { Array sigma2; return fit(pixels, mask, sigma2); } else { auto sigma2 = sigma.get(True); return fit(pixels, mask, sigma2); } } template Fit2D::ErrorTypes Fit2D::fit(const Array& data, const Array& sigma) { if (!itsValid) { itsErrorMessage = "No models have been set - use function addModel"; return Fit2D::NOMODELS; } if (data.ndim() !=2) { itsLogger << "Fit2D::fit - Array must be 2-dimensional" << LogIO::EXCEPTION; } if (sigma.nelements() !=0) { if (!data.shape().isEqual(sigma.shape())) { itsLogger << "Fit2D::fit - Sigma and pixel arrays must " "have the same shape" << LogIO::EXCEPTION; } } // Matrix pos; Vector values; Vector weights; Array mask; if (!selectData (pos, values, weights, data, mask, sigma)) { itsErrorMessage = String("There were no selected data points"); return Fit2D::NOGOOD; } // return fitData(values, pos, weights); } template Fit2D::ErrorTypes Fit2D::fit(const Array& data, const Array& mask, const Array& sigma) { if (!itsValid) { itsErrorMessage = "No models have been set - use function addModel"; return Fit2D::NOMODELS; } if (data.ndim() !=2) { itsLogger << "Fit2D::fit - Array must be 2-dimensional" << LogIO::EXCEPTION; } if (mask.nelements() !=0) { if (!data.shape().isEqual(mask.shape())) { itsLogger << "Fit2D::fit - Mask and pixel arrays must " "have the same shape" << LogIO::EXCEPTION; } } if (sigma.nelements() !=0) { if (!data.shape().isEqual(sigma.shape())) { itsLogger << "Fit2D::fit - Sigma and pixel arrays must " "have the same shape" << LogIO::EXCEPTION; } } Matrix pos; Vector values; Vector weights; if (!selectData (pos, values, weights, data, mask, sigma)) { itsErrorMessage = String("There were no selected data points"); return Fit2D::NOGOOD; } return fitData(values, pos, weights); } template Fit2D::ErrorTypes Fit2D::residual( Array& resid, Array& model, const Array& data, Int xOffset, int yOffset ) const { ThrowIf( ! itsValid, "No models have been set - use function addModel" ); if (!itsValidSolution) { return Fit2D::FAILED; } ThrowIf(data.ndim() !=2, "Array must be 2-dimensional"); IPosition shape = data.shape(); if (resid.nelements() ==0) { resid.resize(shape); } else { ThrowIf( ! shape.isEqual(resid.shape()), "Residual and pixel arrays must be the same shape" ); } if (model.nelements() ==0) { model.resize(shape); } else { ThrowIf( !shape.isEqual(model.shape()), "Residual and pixel arrays must " ); } // Create a functional with the solution (no axis conversion // necessary because functional interface takes axial ratio) std::unique_ptr>> sumFunction(itsFunction.clone()); for (uInt i=0; i Bool Fit2D::selectData( Matrix& pos, Vector& values, Vector& weights, const Array& pixels, const Array& mask, const Array& sigma ) // // Fish out the unmasked data. // // If the mask is of zero length all pixels are assumed good. // If the sigma array is of zero length the weights are given // the value 1.0 // // If there are no good pixels returns False // { auto shape = pixels.shape(); auto nPoints = shape.product(); // // Handle pixel ranges // Vector pixelRange(2); Int includeThem = 0; if (itsPixelRange.nelements()==2) { pixelRange(0) = itsPixelRange(0); pixelRange(1) = itsPixelRange(1); if (itsInclude) { includeThem = 1; } else { includeThem = -1; } } // // Do we have sigmas ? // itsHasSigma = False; if (sigma.nelements() != 0) itsHasSigma = True; // // Find first unmasked point // auto hasMask = True; if (mask.nelements()==0) hasMask = False; Double minVal(0); Double maxVal(0); if (hasMask) { Bool deleteIt1, deleteIt2; const auto* p1 = mask.getStorage(deleteIt1); const auto* p2 = pixels.getStorage(deleteIt2); for (uInt i=0; i locX(nPoints); Vector locY(nPoints); IPosition loc(2); // itsNumberPoints = 0; for (Int j=0; jupdate (_currentStep); } void LattStatsProgress::done() { _meter = NULL; } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LatticeMath/LattStatsProgress.h000066400000000000000000000062361476623553700231630ustar00rootroot00000000000000//# LattStatsProgress.h: progress meter for LatticeStatistics //# Copyright (C) 1996,1997,1998,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTSTATSPROGRESS_H #define LATTICES_LATTSTATSPROGRESS_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class ProgressMeter; // Provides a progress meter for the LatticeStatistics class // // // // // // //
      • LatticeProgress // // // // Display a progress meter for the class LatticeStatistics // // // // Progress meters can be displayed by the LatticeApply class // which is used by LatticeStatistics in order to optimally iterate // through the lattice. To do this, one must derive a // class from LatticeProgress. LatticeApply calls // methods declared in LatticeProgress and implemented in // the derived class. // // // // I like progress meters ! // // // // class LattStatsProgress : public LatticeProgress { public: // Constructor makes a null object LattStatsProgress() : _meter(), _currentStep(0) {}; // Destructor deletes the ProgressMeter pointer virtual ~LattStatsProgress(); // increment the current step (postfix version) void operator++(Int); // Initialize this object. Here we create the ProgressMeter // This function is called by the init in LatticeProgress virtual void initDerived(); // Tell the number of steps done so far. virtual void nstepsDone (uInt nsteps); // The process has ended so clean things up. virtual void done(); private: std::shared_ptr _meter; uInt _currentStep; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LatticeMath/LattStatsSpecialize.cc000066400000000000000000000253311476623553700236020ustar00rootroot00000000000000//# LattStatsSpecialize.cc: //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Double LattStatsSpecialize::getMean (Double sum, Double n) { Double tmp = 0.0; if (n > 0.5) tmp = sum / n; return tmp; } DComplex LattStatsSpecialize::getMean (DComplex sum, DComplex n) { Double vR = 0.0; Double vI = 0.0; // if (real(n) > 0.5) vR = real(sum)/real(n); if (imag(n) > 0.5) vI = imag(sum)/imag(n); // return DComplex(vR, vI); } Double LattStatsSpecialize::getVariance (Double sum, Double sumsq, Double n) { Double tmp = 0.0; if (n > 1.5) tmp = (sumsq - (sum*sum/n)) / (n-1); return tmp; } DComplex LattStatsSpecialize::getVariance (DComplex sum, DComplex sumsq, DComplex n) { return DComplex(getVariance(real(sum), real(sumsq), real(n)), getVariance(imag(sum), imag(sumsq), imag(n))); } Double LattStatsSpecialize::getSigma (Double sum, Double sumsq, Double n) { Double var = getVariance (sum, sumsq, n); if (var>0) { return sqrt(var); } else { return 0.0; } return 0.0; } Double LattStatsSpecialize::getSigma (Double var) { if (var>0) { return sqrt(var); } else { return 0.0; } return 0.0; } Double LattStatsSpecialize::getRms (Double sumsq, Double n) { Float tmp = 0.0; if (n > 0.5) tmp = sqrt(sumsq/n); return tmp; } DComplex LattStatsSpecialize::getSigma (DComplex sum, DComplex sumsq, DComplex n) { return DComplex(getVariance(real(sum), real(sumsq), real(n)), getVariance(imag(sum), imag(sumsq), imag(n))); } DComplex LattStatsSpecialize::getSigma (DComplex var) { return DComplex(getSigma(real(var)), getSigma(imag(var))); } DComplex LattStatsSpecialize::getRms (DComplex sumsq, DComplex n) { return DComplex(getRms(real(sumsq),real(n)), getRms(imag(sumsq), imag(n))); } Float LattStatsSpecialize::min(Float v1, Float v2) { return std::min(v1, v2); } Complex LattStatsSpecialize::min(Complex v1, Complex v2) { return Complex(std::min(real(v1),real(v2)),std::min(imag(v1),imag(v2))); } Float LattStatsSpecialize::max(Float v1, Float v2) { return std::max(v1, v2); } Complex LattStatsSpecialize::max(Complex v1, Complex v2) { return Complex(std::max(real(v1),real(v2)),std::max(imag(v1),imag(v2))); } Float LattStatsSpecialize::getNodeScalarValue(const LatticeExprNode& node, Float) { return node.getFloat(); } Complex LattStatsSpecialize::getNodeScalarValue(const LatticeExprNode& node, Complex) { return node.getComplex(); } Float LattStatsSpecialize::usePixelInc (Float dMin, Float dMax, Float datum) { return ( (datum >= dMin && datum <= dMax) ? 1.0 : -1.0 ); } Complex LattStatsSpecialize::usePixelInc (Complex dMin, Complex dMax, Complex datum) { return Complex(usePixelInc(real(dMin), real(dMax), real(datum)), usePixelInc(imag(dMin), imag(dMax), imag(datum))); } Float LattStatsSpecialize::usePixelExc (Float dMin, Float dMax, Float datum) { return ( (datum < dMin || datum > dMax) ? 1.0 : -1.0 ); } Complex LattStatsSpecialize::usePixelExc (Complex dMin, Complex dMax, Complex datum) { return Complex(usePixelExc(real(dMin), real(dMax), real(datum)), usePixelExc(imag(dMin), imag(dMax), imag(datum))); } void LattStatsSpecialize::setUseItTrue (Float& useIt) { useIt = 1.0; } void LattStatsSpecialize::setUseItTrue (Complex& useIt) { /// useIt.real() = 1.0; /// useIt.imag() = 1.0; useIt = Complex(1.0, 1.0); } Bool LattStatsSpecialize::hasSomePoints (Double npts) { return (npts > 0.5); } Bool LattStatsSpecialize::hasSomePoints (DComplex npts) { return (real(npts) > 0.5 || imag(npts)>0.5); } Bool LattStatsSpecialize::setIncludeExclude (String& errorMessage, Vector& range, Bool& noInclude, Bool& noExclude, const Vector& include, const Vector& exclude) { Vector rangeReal; Bool okReal = LattStatsSpecialize::setIncludeExclude (errorMessage, rangeReal, noInclude, noExclude, real(include), real(exclude)); if (!okReal) return False; // Vector rangeImag; Bool okImag = LattStatsSpecialize::setIncludeExclude (errorMessage, rangeImag, noInclude, noExclude, imag(include), imag(exclude)); if (!okImag) return False; // if (rangeReal.nelements() != rangeImag.nelements()) { throw (AipsError("Internal error in LattStatsSpecialize")); } // range.resize(rangeReal.nelements()); for (uInt i=0; i* pLattice, const Vector& range, Bool noInclude, Bool noExclude) { RO_LatticeIterator it(*pLattice); // dataMin = 1.e30; dataMax = -1.0e30; // const Float* pData = 0; Bool deleteData; // if (pLattice->isMasked()) { const Bool* pMask = 0; Bool deleteMask; // for (it.reset(); !it.atEnd(); it++) { const Array& data = it.cursor(); const Array& mask = pLattice->getMaskSlice(it.position(), it.cursor().shape(), False); pData = data.getStorage(deleteData); pMask = mask.getStorage(deleteMask); uInt n = data.nelements(); if (!noInclude) { for (uInt i=0; i 0) { dataMin = (dataMin < (pData[i])) ? dataMin : (pData[i]); dataMax = (dataMax > (pData[i])) ? dataMax : (pData[i]); } } } else if (!noExclude) { for (uInt i=0; i 0) { dataMin = (dataMin < (pData[i])) ? dataMin : (pData[i]); dataMax = (dataMax > (pData[i])) ? dataMax : (pData[i]); } } } else { for (uInt i=0; i (pData[i])) ? dataMax : (pData[i]); } } } // data.freeStorage(pData, deleteData); mask.freeStorage(pMask, deleteMask); } } else { for (it.reset(); !it.atEnd(); it++) { const Array& data = it.cursor(); pData = data.getStorage(deleteData); uInt n = data.nelements(); if (!noInclude) { for (uInt i=0; i 0) { dataMin = (dataMin < (pData[i])) ? dataMin : (pData[i]); dataMax = (dataMax > (pData[i])) ? dataMax : (pData[i]); } } } else if (!noExclude) { for (uInt i=0; i 0) { dataMin = (dataMin < (pData[i])) ? dataMin : (pData[i]); dataMax = (dataMax > (pData[i])) ? dataMax : (pData[i]); } } } else { for (uInt i=0; i (pData[i])) ? dataMax : (pData[i]); } } data.freeStorage(pData, deleteData); } } // return (dataMax > dataMin); } Bool LattStatsSpecialize::minMax(Complex& dataMin, Complex& dataMax, const MaskedLattice* pLattice, const Vector& range, Bool noInclude, Bool noExclude) { LatticeExprNode nodeR(real(*pLattice)); LatticeExprNode nodeI(imag(*pLattice)); LatticeExpr latR(nodeR); LatticeExpr latI(nodeR); // Vector realRange, imagRange; if (!noInclude && !noExclude) { realRange.resize(2); imagRange.resize(2); // realRange[0] = real(range[0]); realRange[1] = real(range[1]); imagRange[0] = imag(range[0]); imagRange[1] = imag(range[1]); } // Float realMin, realMax, imagMin, imagMax; Bool ok = LattStatsSpecialize::minMax(realMin, realMax, &latR, realRange, noInclude, noExclude); if (ok) { ok = LattStatsSpecialize::minMax(imagMin, imagMax, &latI, imagRange, noInclude, noExclude); } // if (ok) { dataMin = Complex(realMin, imagMin); dataMax = Complex(realMax, imagMax); } return ok; } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LatticeMath/LattStatsSpecialize.h000066400000000000000000000107741476623553700234510ustar00rootroot00000000000000//# LattStatsSpecialize.h: specialized functions for LatticeStatistics //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTSTATSSPECIALIZE_H #define LATTICES_LATTSTATSSPECIALIZE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template class Lattice; template class MaskedLattice; class LatticeExprNode; class String; class IPosition; // // // // // // // // // // // // // // // // // // // // class LattStatsSpecialize { public: static Bool hasSomePoints (Double npts); static Bool hasSomePoints (DComplex npts); // static void setUseItTrue (Float& useIt); static void setUseItTrue (Complex& useIt); // static Float usePixelInc (Float dMin, Float dMax, Float datum); static Complex usePixelInc (Complex dMin, Complex dMax, Complex datum); // static Float usePixelExc (Float dMin, Float dMax, Float datum); static Complex usePixelExc (Complex dMin, Complex dMax, Complex datum); // static Double getMean (Double sum, Double n); static DComplex getMean (DComplex sum, DComplex n); // static Double getVariance (Double sum, Double sumsq, Double n); static DComplex getVariance (DComplex sum, DComplex sumsq, DComplex n); // static Double getSigma (Double sum, Double sumsq, Double n); static DComplex getSigma (DComplex sum, DComplex sumsq, DComplex n); // static Double getSigma (Double var); static DComplex getSigma (DComplex var); // static Double getRms (Double sumsq, Double n); static DComplex getRms (DComplex sumsq, DComplex n); // static Float min(Float v1, Float v2); static Complex min(Complex v1, Complex v2); // static Float max(Float v1, Float v2); static Complex max(Complex v1, Complex v2); // static Float getNodeScalarValue(const LatticeExprNode& node, Float); static Complex getNodeScalarValue(const LatticeExprNode& node, Complex); template static Bool setIncludeExclude (String& errorMessage, Vector& range, Bool& noInclude, Bool& noExclude, const Vector& include, const Vector& exclude); static Bool setIncludeExclude (String& errorMessage, Vector& range, Bool& noInclude, Bool& noExclude, const Vector& include, const Vector& exclude); // static Bool minMax (Float& dataMin, Float& dataMax, const MaskedLattice* pLattice, const Vector& range, Bool noInclude, Bool noExclude); static Bool minMax (Complex& dataMin, Complex& dataMax, const MaskedLattice* pLattice, const Vector& range, Bool noInclude, Bool noExclude); }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/LatticeMath/LattStatsSpecialize2.tcc000066400000000000000000000074541476623553700240560ustar00rootroot00000000000000//# Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# // #ifndef LATTICES_LATTSTATSSPECIALIZE2_TCC #define LATTICES_LATTSTATSSPECIALIZE2_TCC #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template Bool LattStatsSpecialize::setIncludeExclude( String& errorMessage, Vector& range, Bool& noInclude, Bool& noExclude, const Vector& include, const Vector& exclude ) { // // Take the user's data inclusion and exclusion data ranges and // generate the range and Booleans to say what sort it is // // Inputs: // include Include range given by user. Zero length indicates // no include range // exclude Exclude range given by user. As above. // Outputs: // noInclude If True user did not give an include range // noExclude If True user did not give an exclude range // range A pixel value selection range. Will be resized to // zero length if both noInclude and noExclude are True // Bool True if successfull, will fail if user tries to give too // many values for includeB or excludeB, or tries to give // values for both ThrowIf ( ! isReal(whatType()), "Logic error, this method is for real data types only" ); noInclude = True; range.resize(0); if (include.empty()) { // do nothing } else if (include.size() == 1) { range.resize(2); range(0) = -abs(include(0)); range(1) = abs(include(0)); noInclude = False; } else if (include.size() == 2) { range.resize(2); range(0) = min(include(0),include(1)); range(1) = max(include(0),include(1)); noInclude = False; } else { errorMessage = String("Too many elements for argument include"); return False; } noExclude = True; if (exclude.empty()) { // do nothing } else if (exclude.size() == 1) { range.resize(2); range(0) = -abs(exclude(0)); range(1) = abs(exclude(0)); noExclude = False; } else if (exclude.size() == 2) { range.resize(2); range(0) = min(exclude(0),exclude(1)); range(1) = max(exclude(0),exclude(1)); noExclude = False; } else { errorMessage = String("Too many elements for argument exclude"); return False; } if (! noInclude && ! noExclude) { errorMessage = String("You can only give one of arguments include or exclude"); return False; } return True; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LatticeMath/LatticeAddNoise.cc000066400000000000000000000113441476623553700226410ustar00rootroot00000000000000//# LatticeAddNoise.cc: add noise to a lattice //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LatticeAddNoise::LatticeAddNoise() : itsParameters(0), itsNoise(0) {} LatticeAddNoise::LatticeAddNoise( Random::Types type, const Vector& parameters, Int seed1, Int seed2 ) : itsType(type), itsParameters(parameters.copy()), itsGen(seed1, seed2), itsNoise(NULL) { makeDistribution(); } LatticeAddNoise::LatticeAddNoise (const LatticeAddNoise& other) : itsType(other.itsType), itsParameters(other.itsParameters.copy()), itsGen(other.itsGen), itsNoise(NULL) { makeDistribution(); } LatticeAddNoise& LatticeAddNoise::operator=(const LatticeAddNoise& other) { if (this != &other) { itsType = other.itsType; itsParameters.resize(0); itsParameters = other.itsParameters; itsGen = other.itsGen; makeDistribution(); } return *this; } LatticeAddNoise::~LatticeAddNoise() { if (itsNoise) { delete itsNoise; itsNoise = 0; } } void LatticeAddNoise::set (Random::Types type, const Vector& parameters) { itsType = type; itsParameters.resize(0); itsParameters = parameters; makeDistribution(); } // Private void LatticeAddNoise::addNoiseToArray (Array& data) { Bool deleteIt; auto* p = data.getStorage(deleteIt); std::for_each(p, p + data.nelements(), [&](Float& datum) { datum += (*itsNoise)(); }); data.putStorage(p, deleteIt); } void LatticeAddNoise::addNoiseToArray (Array& data) { Bool deleteIt; auto* p = data.getStorage(deleteIt); std::for_each(p, p + data.nelements(), [&](Double& datum) { datum += (*itsNoise)(); }); data.putStorage(p, deleteIt); } void LatticeAddNoise::addNoiseToArray (Array& data) { Bool deleteIt; auto* p = data.getStorage(deleteIt); Float rr, ii; std::for_each(p, p + data.nelements(), [&](Complex& datum) { // Add noise to real and imag separately rr = real(datum) + (*itsNoise)(); ii = imag(datum) + (*itsNoise)(); datum = Complex(rr,ii); }); data.putStorage(p, deleteIt); } void LatticeAddNoise::addNoiseToArray (Array& data) { Bool deleteIt; auto* p = data.getStorage(deleteIt); Double rr, ii; std::for_each(p, p + data.nelements(), [&](DComplex& datum) { // Add noise to real and imag separately rr = real(datum) + (*itsNoise)(); ii = imag(datum) + (*itsNoise)(); datum = Complex(rr,ii); }); data.putStorage(p, deleteIt); } void LatticeAddNoise::makeDistribution () { if (itsNoise) { delete itsNoise; itsNoise = 0; } itsNoise = Random::construct(itsType, &itsGen); if (itsNoise) { if (!itsNoise->checkParameters(itsParameters)) { delete itsNoise; itsNoise = 0; LogIO os(LogOrigin("LatticeAddNoise", "makeDistribution", WHERE)); os << "The distribution parameters are illegal" << LogIO::EXCEPTION; } else { itsNoise->setParameters(itsParameters); } } } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LatticeMath/LatticeAddNoise.h000066400000000000000000000102731476623553700225030ustar00rootroot00000000000000//# LatticeAddNoise.h: add noise to a Lattice //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICEADDNOISE_H #define LATTICES_LATTICEADDNOISE_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class MaskedLattice; template class Lattice; // // Add noise from specified distribution to a lattice // // // // // //
      • Lattice //
      • Random // // // This class allows you to add noise from one of many enumerated // types to a Lattice. If the Lattice is Complex, then the noise // is added to real and imaginary separately. // // // // Vector pars(2): // pars(0) = 0.5; // Mean // pars(1) = 0.2; // Variance // LatticeAddNoise lan(Random::NORMAL, pars); // ArrayLattice lat(IPosition(2,100,100)); // lan.add(lat); // // //# //# class LatticeAddNoise { public: // Default constructor LatticeAddNoise(); // Constructor. An exception will occur if we cannot generate // the distribution (e.g. illegal parameters). seed1 and seed2 // are used to seed the MLCG object. LatticeAddNoise ( Random::Types type, const Vector& parameters, Int seed1=0, Int seed2=1 ); // Copy constructor (copy semantics) LatticeAddNoise (const LatticeAddNoise& other); // Assignment (copy semantics) LatticeAddNoise& operator=(const LatticeAddNoise& other); // Destructor ~LatticeAddNoise(); // Set a new distribution. An exception will occur if we cannot generate // the distribution (e.g. illegal parameters). void set (Random::Types type, const Vector& parameters); // Add noise of given type to lattice. For complex types, the // noise is added to real and imaginary separately. // Any mask is ignored when adding the noise. I.e. // noise is added to masked pixels. // template void add (Lattice& lattice); template void add (MaskedLattice& lattice); // private: Random::Types itsType; Vector itsParameters; MLCG itsGen; Random* itsNoise; // Add noise to array. For Complex, noise is added to // real and imaginary separately. // void addNoiseToArray (Array& data); void addNoiseToArray (Array& data); void addNoiseToArray (Array& data); void addNoiseToArray (Array& data); // // Make noise generator void makeDistribution (); }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/LatticeMath/LatticeAddNoise2.tcc000066400000000000000000000043011476623553700231020ustar00rootroot00000000000000//# LatticeAddNoise2.tcc: add noise to a lattice //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template void LatticeAddNoise::add (MaskedLattice& lattice) { ThrowIf(! itsNoise, "You have not yet called function 'set'"); LatticeIterator it(lattice); for (it.reset(); !it.atEnd(); it++) { addNoiseToArray(it.rwCursor()); } } template void LatticeAddNoise::add (Lattice& lattice) { SubLattice ml(lattice, True); add(ml); } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LatticeMath/LatticeApply.h000066400000000000000000000253621476623553700221070ustar00rootroot00000000000000//# LatticeApply.h: Optimally iterate through a Lattice and apply provided function object //# Copyright (C) 1997,1998,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICEAPPLY_H #define LATTICES_LATTICEAPPLY_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class TiledCollapser; template class LineCollapser; template class Lattice; template class MaskedLattice; class LatticeProgress; class IPosition; class LatticeRegion; // // Optimally iterate through a Lattice and apply provided function object // // // // // //
      • MaskedLattice //
      • LineCollapser //
      • TiledCollapser // // // This function iterates through a Lattice and applies a user given // function object to chunks along the specified axes. Usually the // function collapses the chunk to 1 or a few values (e.g. get min/max). // The result of the function is written into the output Lattice(s) at the // location of the collapsed chunk. The output lattice(s) must be supplied // with the correct shape. E.g. when a lattice with shape [nx,ny,nz] is // collapsed by calculating the mean of each y-line, the output lattice // has to have shape [nx,nz]. It is also possible to have output shape // [nx,1,nz], [1,nx,nz], [nx,nz,1] or even e.g. [nx,1,1,1,nz]. //

        // By specifying a region it is possible to apply the function object // to a subset of the lattice. Of course, the shape of the output lattice(s) // have to match the shape of the region. //

        // The iteration is done in an optimal way. To keep memory usage down, // it caches as few tiles as possible. // There are 2 ways to iterate. //

          //
        1. For some applications an entire line is needed. An example is // the calculation of the moment. The functions lineApply // and lineMultiApply can be used for that purpose. // Internally they use the // TiledLineStepper // navigator, so only a few tiles are kept in the cache. //
          One can also think of applications where an entire plane (or cube) // is needed. This is not supported, but can be implemented when needed. //
        2. Other applications do not care how the data are traversed, // making it possible to iterate tile by tile (which is optimal). // An example is the calculation of the minimum, maximum, mean of // a line, plane, etc.. // For this purpose the function tiledApply can be used. // This function is faster and uses less memory than lineApply, // so whenever possible this one should be used. Another advantage of // this function is that it is possible to operate per line, plane, etc. // or even for the entire lattice. //
        // The user has to supply a function object derived from the abstract base // class LineCollapser or // TiledCollapser, resp.. // The process function in these classes has to process // the chunk of data passed in. The nstepsDone function // in these classes can be used to monitor the progress. //

        // The class is Doubly templated. Ths first template type // is for the data type you are processing. The second type is // for what type you want the results of the processing assigned to. // For example, if you are computing sums of squares for statistical // purposes, you might use higher precision (Float->Double) for this. // No check is made that the template types are self-consistent. // // // Collapse each line in the y-direction using my collapser function object. // // MyLineCollapser collapser; // PagedArray latticeIn("lattice.file"); // IPosition shape = latticeIn.shape(); // shape(1) = 1; // ArrayLattice latticeOut(shape); // LatticeApply::lineApply (latticeOut, latticeIn, collapser, 1); // // // // This class makes it possible that a user can apply functions to // a lattice in an optimal way, without having to know all the details // of iterating through a lattice. // //# //#

      • //# template class LatticeApply { public: // This function iterates line by line through an input lattice and applies // a user supplied function object to each line along the specified axis. // The scalar result of the function object is written into the output // lattice at the location of the collapsed line. The output lattice must // be supplied with the correct shape (the shape of the supplied region). // The default region is the entire input lattice. // static void lineApply (MaskedLattice& latticeOut, const MaskedLattice& latticeIn, LineCollapser& collapser, uInt collapseAxis, LatticeProgress* tellProgress = 0); static void lineApply (MaskedLattice& latticeOut, const MaskedLattice& latticeIn, const LatticeRegion& region, LineCollapser& collapser, uInt collapseAxis, LatticeProgress* tellProgress = 0); // // This function iterates line by line through an input lattice and applies // a user supplied function object to each line along the specified axis. // The vector result of the function object is written into the output // lattices at the location of the collapsed line (1 value per lattice). // The output lattices must be supplied with the correct shape (the shape // of the supplied region). // The default region is the entire input lattice. // static void lineMultiApply (PtrBlock*>& latticeOut, const MaskedLattice& latticeIn, LineCollapser& collapser, uInt collapseAxis, LatticeProgress* tellProgress = 0); static void lineMultiApply (PtrBlock*>& latticeOut, const MaskedLattice& latticeIn, const LatticeRegion& region, LineCollapser& collapser, uInt collapseAxis, LatticeProgress* tellProgress = 0); // // This function iterates tile by tile through an input lattice and applies // a user supplied function object to each chunk along the specified axes. // A chunk can be a line, plane, etc. which is determined by the argument // collapseAxes. E.g. IPosition(2,1,2) means planes along // axes 1 and 2 (thus y,z planes). // The result of the function object is written into the output // lattice at the location of the collapsed chunk. The output lattice must // be supplied with the correct shape (the shape of the supplied region // plus the number of values resulting from the collapse). // The default region is the entire input lattice. // static void tiledApply (MaskedLattice& latticeOut, const MaskedLattice& latticeIn, TiledCollapser& collapser, const IPosition& collapseAxes, Int newOutAxis = -1, LatticeProgress* tellProgress = 0); static void tiledApply (MaskedLattice& latticeOut, const MaskedLattice& latticeIn, const LatticeRegion& region, TiledCollapser& collapser, const IPosition& collapseAxes, Int newOutAxis = -1, LatticeProgress* tellProgress = 0); // // This function iterates tile by tile through an input lattice and applies // a user supplied function object to each chunk along the specified axes. // A chunk can be a line, plane, etc. which is determined by the argument // collapseAxes. E.g. IPosition(2,1,2) means planes along // axes 1 and 2 (thus y,z planes). // The result of the function object is written into the output // lattices at the location of the collapsed chunk. The output lattices must // be supplied with the correct shape (the shape of the supplied region). // The default region is the entire input lattice. // // These functions are only declared, but not implemented yet. // Thus they cannot be used yet. // // static void tiledMultiApply (PtrBlock*>& latticeOut, const MaskedLattice& latticeIn, TiledCollapser& collapser, const IPosition& collapseAxes, LatticeProgress* tellProgress = 0); static void tiledMultiApply (PtrBlock*>& latticeOut, const MaskedLattice& latticeIn, const LatticeRegion& region, TiledCollapser& collapser, const IPosition& collapseAxes, LatticeProgress* tellProgress = 0); // private: // Do some checks on the given arguments. // It returns an IPosition with the same length as shapeOut. // It contains a mapping of output to input axes. A value of -1 // indicates that the axis is new (to contain the collapse result). //
        Argument newOutAxis tells the output axis to store the results. // -1 means that the function has to find it out itself; it takes the // first axis with a length mismatching the corresponding input axis. static IPosition prepare (const IPosition& shapeIn, const IPosition& shapeOut, const IPosition& collapseAxes, Int newOutAxis); static IPosition _chunkShape( uInt axis, const MaskedLattice& latticeIn ); }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/LatticeMath/LatticeApply.tcc000066400000000000000000000572741476623553700224400ustar00rootroot00000000000000//# LatticeApply.cc: Optimally iterate through lattices and apply supplied function //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICEAPPLY_TCC #define LATTICES_LATTICEAPPLY_TCC #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template void LatticeApply::lineApply (MaskedLattice& latticeOut, const MaskedLattice& latticeIn, const LatticeRegion& region, LineCollapser& collapser, uInt collapseAxis, LatticeProgress* tellProgress) { lineApply (latticeOut, SubLattice(latticeIn, region), collapser, collapseAxis, tellProgress); } template void LatticeApply::lineMultiApply (PtrBlock*>& latticeOut, const MaskedLattice& latticeIn, const LatticeRegion& region, LineCollapser& collapser, uInt collapseAxis, LatticeProgress* tellProgress) { lineMultiApply (latticeOut, SubLattice(latticeIn, region), collapser, collapseAxis, tellProgress); } template void LatticeApply::tiledApply (MaskedLattice& latticeOut, const MaskedLattice& latticeIn, const LatticeRegion& region, TiledCollapser& collapser, const IPosition& collapseAxes, Int newOutAxis, LatticeProgress* tellProgress) { tiledApply (latticeOut, SubLattice(latticeIn, region), collapser, collapseAxes, newOutAxis, tellProgress); } template void LatticeApply::lineApply (MaskedLattice& latticeOut, const MaskedLattice& latticeIn, LineCollapser& collapser, uInt collapseAxis, LatticeProgress* tellProgress) { // Make veracity check on input and output lattice // and work out map to translate input and output axes. IPosition ioMap = prepare (latticeIn.shape(), latticeOut.shape(), IPosition(1,collapseAxis), -1); // Does the input has a mask? // If not, can the collapser handle a null mask. Bool useMask = latticeIn.isMasked(); if (!useMask) { useMask = (! collapser.canHandleNullMask()); } // Input lines are extracted with the TiledLineStepper. const IPosition& inShape = latticeIn.shape(); IPosition inTileShape = latticeIn.niceCursorShape(); TiledLineStepper inNav(inShape, inTileShape, collapseAxis); RO_LatticeIterator inIter(latticeIn, inNav); const IPosition blc = IPosition(inShape.nelements(), 0); const IPosition trc = inShape - 1; const IPosition inc = IPosition(inShape.nelements(), 1); const IPosition len = inShape; const uInt outDim = latticeOut.ndim(); IPosition outPos(outDim, 0); IPosition outShape(outDim, 1); for (uInt i=0; i= 0) { outShape(i) = len(ioMap(i)); } } // See if the output lattice has a writable pixelmask. // If so, it will later be used to write the resulting mask to. Lattice* maskOut = 0; if (latticeOut.hasPixelMask()) { maskOut = &(latticeOut.pixelMask()); if (! maskOut->isWritable()) { maskOut = 0; } } // Set the number of expected steps. // This is the number of lines to process. // Also give the number of resulting output pixels per line, so the // collapser can check it. Int nLine = outShape.product(); Int nResult = latticeOut.shape().product() / nLine; AlwaysAssert (nResult==1, AipsError); collapser.init (nResult); if (tellProgress != 0) tellProgress->init (nLine); // Iterate through all the lines. // Per tile the lines (in the collapseAxis direction) are // assembled into a single array, which is put thereafter. while (! inIter.atEnd()) { // Calculate output buffer shape. Has to be done inside the loop // as the tile shape may not fit integrally into the lattice. // It takes care of blc, trc, and inc. IPosition pos = inIter.position(); for (uInt j=0; j= 0) { uInt i = ioMap(j); uInt stPos = (pos(j) - blc(j)) % inc(j); if (stPos != 0) { stPos = inc(j) - stPos; } Int sz = inTileShape(i) - pos(i) % inTileShape(i); sz = min (sz, 1 + trc(i) - pos(i)) - stPos; AlwaysAssert (sz > 0, AipsError); outShape(j) = (sz + inc(i) - 1) / inc(i); outPos(j) = (pos(i) - blc(i)) / inc(i); } } // cout << outShape << " put at " << outPos << endl; // Put the collapsed lines into an output buffer Array array(outShape); Array arrayMask(outShape); Bool deleteIt, deleteMask; U* result = array.getStorage (deleteIt); Bool* resultMask = arrayMask.getStorage (deleteMask); uInt n = array.nelements() / nResult; for (uInt i=0; i mask; if (useMask) { // Casting const away is innocent. // Remove degenerate axes to get a 1D array. Array tmp; ((MaskedLattice&)latticeIn).getMaskSlice (tmp, Slicer(pos, inIter.cursorShape()), True); mask.reference (tmp); } collapser.process (result[i], resultMask[i], inIter.vectorCursor(), mask, pos); ++inIter; if (tellProgress != 0) tellProgress->nstepsDone (inIter.nsteps()); } array.putStorage (result, deleteIt); arrayMask.putStorage (resultMask, deleteMask); latticeOut.putSlice (array, outPos); if (maskOut != 0) { maskOut->putSlice (arrayMask, outPos); } } if (tellProgress != 0) tellProgress->done(); } template void LatticeApply::lineMultiApply (PtrBlock*>& latticeOut, const MaskedLattice& latticeIn, LineCollapser& collapser, uInt collapseAxis, LatticeProgress* tellProgress) { // First verify that all the output lattices have the same shape and tile shape const uInt nOut = latticeOut.nelements(); AlwaysAssert(nOut > 0, AipsError); const IPosition shape(latticeOut[0]->shape()); const uInt outDim = shape.nelements(); for (uInt i=1; ishape() == shape, AipsError); } const IPosition& inShape = latticeIn.shape(); IPosition outPos(outDim, 0); IPosition outShape(outDim, 1); // Does the input has a mask? // If not, can the collapser handle a null mask. Bool useMask = latticeIn.isMasked() ? True : (! collapser.canHandleNullMask()); const uInt inNDim = inShape.size(); const IPosition displayAxes = IPosition::makeAxisPath(inNDim).otherAxes( inNDim, IPosition(1, collapseAxis) ); const uInt nDisplayAxes = displayAxes.size(); Vector result(nOut); Vector resultMask(nOut); // read in larger chunks than before, because that was very // Inefficient and brought NRAO cluster to a snail's pace, // and then do the accounting for the input lines in memory IPosition chunkSliceStart(inNDim, 0); IPosition chunkSliceEnd = chunkSliceStart; chunkSliceEnd[collapseAxis] = inShape[collapseAxis] - 1; const IPosition chunkSliceEndAtChunkIterBegin = chunkSliceEnd; IPosition chunkShapeInit = _chunkShape(collapseAxis, latticeIn); LatticeStepper myStepper(inShape, chunkShapeInit, LatticeStepper::RESIZE); RO_MaskedLatticeIterator latIter(latticeIn, myStepper); IPosition curPos; static const Vector noMask; if (tellProgress) { uInt nExpectedIters = inShape.product()/chunkShapeInit.product(); tellProgress->init(nExpectedIters); } uInt nDone = 0; for (latIter.reset(); ! latIter.atEnd(); ++latIter) { const IPosition cp = latIter.position(); const Array& chunk = latIter.cursor(); IPosition chunkShape = chunk.shape(); const Array maskChunk = useMask ? latIter.getMask() : Array(); chunkSliceStart = 0; chunkSliceEnd = chunkSliceEndAtChunkIterBegin; IPosition resultArrayShape = chunkShape; resultArrayShape[collapseAxis] = 1; std::vector > resultArray(nOut); std::vector > resultArrayMask(nOut); // need to initialize this way rather than doing it in the constructor, // because using a single Array in the constructor means that all Arrays // in the vector reference the same Array. for (uInt k=0; k(resultArrayShape); resultArrayMask[k] = Array(resultArrayShape); } Bool done = False; while (! done) { Vector data(chunk(chunkSliceStart, chunkSliceEnd)); Vector mask = useMask ? Vector(maskChunk(chunkSliceStart, chunkSliceEnd)) : noMask; curPos = cp + chunkSliceStart; collapser.multiProcess(result, resultMask, data, mask, curPos); for (uInt k=0; kndim(); if (! keepAxis) { resultArray[k].removeDegenerate(displayAxes); } latticeOut[k]->putSlice(resultArray[k], outpos); if (latticeOut[k]->hasPixelMask()) { Lattice& maskOut = latticeOut[k]->pixelMask(); if (maskOut.isWritable()) { if (! keepAxis) { resultArrayMask[k].removeDegenerate(displayAxes); } maskOut.putSlice (resultArrayMask[k], outpos); } } } if (tellProgress != 0) { ++nDone; tellProgress->nstepsDone(nDone); } } if (tellProgress != 0) { tellProgress->done(); } } template IPosition LatticeApply::_chunkShape( uInt axis, const MaskedLattice& latticeIn ) { uInt ndim = latticeIn.ndim(); IPosition chunkShape(ndim, 1); IPosition latShape = latticeIn.shape(); uInt nPixColAxis = latShape[axis]; chunkShape[axis] = nPixColAxis; // arbitrary, but reasonable, max memory limit in bytes for storing arrays in bytes static const uInt limit = 2e7; static const uInt sizeT = sizeof(T); static const uInt sizeBool = sizeof(Bool); uInt chunkMult = latticeIn.isMasked() ? sizeT + sizeBool : sizeT; uInt subChunkSize = chunkMult*nPixColAxis; // integer division const uInt maxChunkSize = limit/subChunkSize; if (maxChunkSize <= 1) { // can only go row by row return chunkShape; } ssize_t x = maxChunkSize; for (uInt i=0; i void LatticeApply::tiledApply ( MaskedLattice& latticeOut, const MaskedLattice& latticeIn, TiledCollapser& collapser, const IPosition& collapseAxes, Int newOutAxis, LatticeProgress* tellProgress ) { // Make veracity check on input and first output lattice // and work out map to translate input and output axes. uInt i,j; IPosition ioMap = prepare ( latticeIn.shape(), latticeOut.shape(), collapseAxes, newOutAxis ); // Does the input has a mask? // If not, can the collapser handle a null mask. Bool useMask = latticeIn.isMasked(); if (!useMask) { useMask = (! collapser.canHandleNullMask()); } // The input is traversed using a TileStepper. const IPosition& inShape = latticeIn.shape(); const uInt inDim = inShape.nelements(); IPosition inTileShape = latticeIn.niceCursorShape(1024*1024); TileStepper inNav(inShape, inTileShape, collapseAxes); RO_LatticeIterator inIter(latticeIn, inNav); // Precalculate various variables. const IPosition blc = IPosition(inShape.nelements(), 0); const IPosition trc = inShape - 1; const IPosition inc = IPosition(inShape.nelements(), 1); const uInt collDim = collapseAxes.nelements(); const uInt iterDim = inDim - collDim; IPosition iterAxes(iterDim); IPosition outShape(latticeOut.shape()); const uInt outDim = outShape.nelements(); j = 0; for (i=0; i= 0) { outShape(i) = 1; iterAxes(j++) = i; } } // Find the first collapse axis which is not immediately after // the previous collapse axis. uInt collStart; for (collStart=1; collStart* maskOut = 0; if (latticeOut.hasPixelMask()) { maskOut = &(latticeOut.pixelMask()); if (! maskOut->isWritable()) { maskOut = 0; } } // Set the number of expected steps. // This is the number of tiles to process. // Also give the number of resulting output pixels per line, so the // collapser can check it. uInt nsteps = 1; for (j=0; jinit (nsteps); } // Determine the axis where the collapsed values are stored in the output. // This is the first unmapped axis (the first axis when all axes are mapped). uInt resultAxis = 0; for (j=0; j& iterCursor = inIter.cursor(); // In order to use the pointers-to-array-data below, the array *must* // be contiguous or the results will in general be incorrect. // Ditto for the mask const Array& cursor = iterCursor.contiguousStorage() ? iterCursor : iterCursor.copy(); ThrowIf( ! cursor.contiguousStorage(), "cursor array is not contiguous" ); const IPosition& cursorShape = cursor.shape(); IPosition pos = inIter.position(); IPosition latPos = pos; Array mask; if (useMask) { // Casting const away is innocent. ((MaskedLattice&)latticeIn).getMaskSlice(mask, Slicer(pos, cursorShape)); if (! mask.contiguousStorage()) { mask = mask.copy(); ThrowIf( ! mask.contiguousStorage(), "mask array is not contiguous" ); } } for (j=0; j= 0) { uInt axis = ioMap(j); iterPos(j) = pos(axis); } } if (firstTime || outPos != iterPos) { if (!firstTime) { Array result; Array resultMask; collapser.endAccumulator (result, resultMask, outShape); latticeOut.putSlice (result, outPos); if (maskOut != 0) { maskOut->putSlice (resultMask, outPos); } } firstTime = False; outPos = iterPos; uInt64 n1 = 1; uInt64 n3 = 1; for (j=0; j= 0) { outShape(j) = cursorShape(ioMap(j)); if (j < resultAxis) { n1 *= outShape(j); } else { n3 *= outShape(j); } } } collapser.initAccumulator (n1, n3); } // Put the collapsed lines into an output buffer // Initialize the cursor position needed in the loop. IPosition curPos (inDim, 0); // Determine the increment for the first collapse axes. // This is done by taking the difference between the adresses of two pixels // in the cursor (if there are 2 pixels). IPosition chunkShape (inDim, 1); for (j=0; jnstepsDone (inIter.nsteps()); } } // Write out the last output array. Array result; Array resultMask; collapser.endAccumulator (result, resultMask, outShape); latticeOut.putSlice (result, outPos); if (maskOut != 0) { maskOut->putSlice (resultMask, outPos); } if (tellProgress != 0) tellProgress->done(); } template IPosition LatticeApply::prepare (const IPosition& inShape, const IPosition& outShape, const IPosition& collapseAxes, Int newOutAxis) { uInt i; // Check if the dimensionality of input and output match. const uInt inDim = inShape.nelements(); const uInt outDim = outShape.nelements(); const uInt collDim = collapseAxes.nelements(); uInt ndim = inDim - collDim; if (outDim < ndim) { throw (AipsError ("LatticeApply::prepare - dimensionalities mismatch")); } // Check the collapseAxes specification (using the makeAxisPath logic). // Also check if they are ascending. IPosition allAxes = IPosition::makeAxisPath (inDim, collapseAxes); for (i=1; i collapseAxes(i-1), AipsError); } // Get the first new output axis (i.e. the axis containing // the collapsed values). If not given, it is the first axis // for which input and output length mismatch. if (newOutAxis < 0) { newOutAxis = 0; for (i=collDim; i Int(ndim)) { throw (AipsError ("LatticeApply::prepare - newOutAxis too high")); } // Make a little map of the input to the output axes. // ioMap(j) is the axis of the input that goes on output axis j. // -1 indicates that an output axis is a new axis (containing the // result of the collapse). // It checks if the length of axes match for input and output. IPosition ioMap(outDim, -1); uInt k=0; for (i=collDim; i #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LatticeCleanProgress::LatticeCleanProgress(PGPlotter* pgplotter) : itsPgplotter(pgplotter), currentIndex(0), currentTotalIterations(0), currentFluxScale(1.0), currentMinFluxScale(0.0), currentMaxResidual(1.0), currentMinResidual(0.1), logMinRes(-1.0), logMaxRes(0.0), deltaY(0.1), xMin(0), xMax(1), fluxScaleJump(1.8), residScaleJump(3.0), forbidden(99999999.0) { } LatticeCleanProgress::~LatticeCleanProgress() { // if(itsPgplotter) delete itsPgplotter; itsPgplotter=0; } // Call back function Bool LatticeCleanProgress::info(const Bool lastcall, const Int iteration, const Int numberIterations, const Vector& maxima, const Block& posMaximum, const Float strengthOptimum, const Int optimumScale, const IPosition&, const Float&, const Vector& totalFluxScale, const Bool resetBase) { uInt nScales = maxima.nelements(); // "And this little piggy built his house out of straw..." // When you remove the myDebug and cout statements, this core dumps. // A veggie burger to the wolf who fixes this -- Mark H. Bool myDebug = False; if (myDebug) cout << "A" << endl; // initialize some things here! if (iterationNumber.nelements() == 0) { initialize( maxima.nelements(), abs(max(maxima)), numberIterations ); } // check to see if we need to increment baseFluxes if (resetBase && currentIndex > 0) { for (uInt i=0; i< nScales; i++) { baseFluxes(i) = totalFluxesPer(i, currentIndex - 1); } baseFluxes(nScales) = totalFluxes(currentIndex-1); } // Do we need to resize the data storage? if (currentIndex >= totalFluxes.nelements() ) { resizeDataStorage(); } // Fill in data storage if (myDebug) cout << "B" << endl; Vector myTotalFluxScale(totalFluxScale.nelements()); Float myTotalFlux = 0; Float myMinFlux = 0.0; iterationNumber(currentIndex) = iteration+1; for (uInt i=0;i 0.0) { posResiduals(k, currentIndex) = log10( maxima(k) ); } else if ( maxima(k) < 0.0) { negResiduals(k, currentIndex) = log10( abs(maxima(k)) ); } } currentIndex++; if (myDebug) cout << "C" << endl; if(itsPgplotter) { // Check for reploting conditions Bool rePlot = False; if ( myTotalFlux > currentFluxScale) { rePlot = True; currentFluxScale *= fluxScaleJump; } if (min(abs(maxima)) < currentMinResidual) { rePlot = True; currentMinResidual /= residScaleJump; } if ( numberIterations > (Int)currentTotalIterations) { currentTotalIterations = numberIterations; rePlot = True; } if (myMinFlux < currentMinFluxScale) { currentMinFluxScale = -abs( fluxScaleJump * myMinFlux); rePlot = True; } if (rePlot) { basicSetUp(True); } else { plotOne(iteration+1, maxima, myTotalFluxScale); } } if (myDebug) cout << "D" << endl; ////////////////////////////////////// // From here down: just Log Messages ////////////////////////////////////// LogIO os(LogOrigin("ClarkCleanProgress", "info()", WHERE)); // Always output this information if(!lastcall) { if(maxima.nelements()==1) { os << "Maximum abs = " << maxima(0) << " at " << posMaximum[0]+1 << endl; os << "Iteration " << iteration+1 << " most significant residual = " << strengthOptimum << " Jy, flux = " << myTotalFlux << endl; } else { for(uInt scale=0;scalesch(0.6); itsPgplotter->sci(1); itsPgplotter->page(); itsPgplotter->svp(0.06, 0.94, 0.64, 0.92); itsPgplotter->swin(xMin, xMax, logMinRes, logMaxRes); itsPgplotter->box("BCST", 0, 0, "BCNLST", 0, 0); itsPgplotter->lab(" ", "+ Peak Resid (Jy)", "Components subtracted"); uInt scale; uInt nScales = posResiduals.nrow(); itsPgplotter->iden(); for (scale=0;scalesci(scale+2); ostringstream oos; oos << "Scale " << scale+1; itsPgplotter->text(0.85*xMax, (logMaxRes - 0.1*(1+scale)*deltaY), oos); } if (doPlot) { for (scale=0;scalesci(scale+2); itsPgplotter->pt(iterationNumber, posResiduals.row(scale), 2); } } // middle graph itsPgplotter->sci(1); itsPgplotter->svp(0.06, 0.94, 0.36, 0.64); itsPgplotter->swin(xMin, xMax, logMaxRes, logMinRes); itsPgplotter->box("BCST", 0, 0, "BCNLST", 0, 0); itsPgplotter->lab(" ", "- Peak Resid (Jy)", " "); if (doPlot) { for (scale=0;scalesci(scale+2); itsPgplotter->pt(iterationNumber, negResiduals.row(scale), 2); } } // lower graph itsPgplotter->sci(1); itsPgplotter->svp(0.06, 0.94, 0.09, 0.36); itsPgplotter->swin(xMin, xMax, currentMinFluxScale, currentFluxScale); itsPgplotter->box("BCNST", 0, 0, "BCNST", 0, 0); itsPgplotter->lab("Number of iterations", "Total Flux", " "); { itsPgplotter->sci(1); ostringstream oos; oos << "Total Flux "; itsPgplotter->text(0.85*xMax, (0.5*(currentFluxScale - currentMinFluxScale)), oos); } if (doPlot) { for (scale=0;scalesci(scale+2); itsPgplotter->pt(iterationNumber, totalFluxesPer.row(scale), 2); } itsPgplotter->sci(1); itsPgplotter->pt(iterationNumber, totalFluxes, 2); } } void LatticeCleanProgress::plotOne(const Int iteration, const Vector& resid, const Vector& flux) { // assuming we've already called basicSetUp, the scaling variables // are all setup already; else, we'd better call them Vector x(1); Vector y(1); x(0) = iteration; itsPgplotter->sch(0.6); for (uInt i=0; isci(i+2); if (resid(i) > 0) { // top graph itsPgplotter->svp(0.06, 0.94, 0.64, 0.92); itsPgplotter->swin(xMin, xMax, logMinRes, logMaxRes); y(0) = log10(resid(i)); itsPgplotter->pt(x,y,2); } else if (resid(i) < 0) { // middle graph itsPgplotter->svp(0.06, 0.94, 0.36, 0.64); itsPgplotter->swin(xMin, xMax, logMaxRes, logMinRes); y(0) = log10(abs(resid(i))); itsPgplotter->pt(x,y,2); } } // lower graph itsPgplotter->sci(1); itsPgplotter->svp(0.06, 0.94, 0.09, 0.36); itsPgplotter->swin(xMin, xMax, currentMinFluxScale, currentFluxScale); Float sumf = sum(flux); for (uInt i=0; isci(i+2); y(0) = flux(i); itsPgplotter->pt(x,y,2); } itsPgplotter->sci(1); y(0) = sumf; itsPgplotter->pt(x,y,2); } void LatticeCleanProgress::resizeDataStorage() { uInt nn = totalFluxesPer.ncolumn(); uInt nScales = totalFluxesPer.nrow(); Vector tfr(totalFluxes); Vector inr(iterationNumber); Matrix tfpr(totalFluxesPer); Matrix mrr(maxResiduals); Matrix nrr(negResiduals); Matrix prr(posResiduals); totalFluxes.resize(2*nn+1); iterationNumber.resize(2*nn+1); totalFluxesPer.resize(nScales, 2*nn+1); maxResiduals.resize(nScales, 2*nn+1); negResiduals.resize(nScales, 2*nn+1); posResiduals.resize(nScales, 2*nn+1); // to prevent trailing (or invalid) vector elements from being plotted iterationNumber = forbidden; posResiduals = forbidden; negResiduals = forbidden; // this is not precisely correct, as posRes and negRes have different // number of valid elements than totalFluxes; but should be safe uInt i, j; for (i=0;i #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class PGPlotter; // // Abstract base class to monitor progress in lattice operations // // // // // // This is an abstract base class for classes to monitor the // progress of an operation on a Lattice. The default implementation // offered by this class does nothing. // However, a derived class could show the progress using for example // a ProgressMeter. A derived // class should override the virtual functions from this class. // // The user of the LatticeCleanProgress object should first call // function init with the total number of steps // that are to be done. Thereafter, after each step has been // executed, function nstepsDone should be called // after each step. Finally, function done should // be called. // // // // // // // Since operations on Lattices can take a while, it can be useful // to show the progress. However, making module Lattices dependent on // the class ProgressMeter sounded bad. This abstract class serves // as a bridge between the Lattice module and the ProgressMeter class // (or any other class showing the progress). // // //# //#
      • //# class LatticeCleanProgress { public: LatticeCleanProgress(PGPlotter* pgplotter=0); virtual ~LatticeCleanProgress(); // Print and plot the information. // Currently, not all information is utilized. Bool info(const Bool lastcall, const Int iteration, const Int numberIterations, const Vector& maxima, const Block& posMaximum, const Float strengthOptimum, const Int optimumScale, const IPosition& positionOptimum, const Float& totalFlux, const Vector& totalFluxScale, const Bool resetBase=False); protected: private: // initizalize the arrays and such void initialize(const uInt nScales, const Float& maxResidual, const uInt numIterations); // As the iterations trickle in, we will from time to time // need to make the Matrices larger. Increase to 2*n+1 void resizeDataStorage(); // this will redraw the plot with a new scale; // if plotMatrices = False, just draw the boxes, // else, replot all past data. // void basicSetUp(Bool plotMatrices = False); // Note: you MUST call basicSetUp before calling this. void plotOne(const Int iteration, const Vector& resid, const Vector& flux); PGPlotter* itsPgplotter; Vector iterationNumber; Matrix maxResiduals; Matrix posResiduals; Matrix negResiduals; Matrix totalFluxesPer; Vector totalFluxes; uInt currentIndex; uInt currentTotalIterations; Float currentFluxScale; Float currentMinFluxScale; Float currentMaxResidual; Float currentMinResidual; Float logMinRes; Float logMaxRes; Float deltaY; Float xMin; Float xMax; Float fluxScaleJump; Float residScaleJump; Float forbidden; Vector baseFluxes; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LatticeMath/LatticeCleaner.h000066400000000000000000000277261476623553700224010ustar00rootroot00000000000000//# Cleaner.h: this defines Cleaner a class for doing convolution //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICECLEANER_H #define LATTICES_LATTICECLEANER_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class LatticeCleanProgress; template class TempLattice; // Lists the different types of Convolutions that can be done // This enumerator is brought out as a separate class because g++ // currently cannot handle enumerators in a templated class. When it can this // class will go away and this enumerator moved into the Cleaner // class class CleanEnums { public: enum CleanType { // Hogbom HOGBOM, // Multi-scale MULTISCALE, // Clark CLARK }; }; // A class for doing multi-dimensional cleaning // // // // //
      • The mathematical concept of deconvolution // // // // The LatticeCleaner class will deconvolve Lattices. // // // // This class will perform various types of Clean deconvolution // on Lattices. // // // // // // // // // // // // //
      • AipsError: if psf has more dimensions than the model. // // // //
      • Allow the psf to be specified with a // Function. // template class LatticeCleaner { public: // Create a cleaner : default constructor LatticeCleaner(); // Create a cleaner for a specific dirty image and PSF LatticeCleaner(const Lattice & psf, const Lattice & dirty); // The copy constructor uses reference semantics LatticeCleaner(const LatticeCleaner & other); // The assignment operator also uses reference semantics LatticeCleaner & operator=(const LatticeCleaner & other); // The destructor does nothing special. ~LatticeCleaner(); // Update the dirty image only void update(const Lattice & dirty); // Set a number of scale sizes. The units of the scale are pixels. Bool setscales(const Int nscales, const Float scaleInc=1.0); // Set a specific set of scales Bool setscales(const Vector & scales); // Set up control parameters // cleanType - type of the cleaning algorithm to use (HOGBOM, MULTISCALE) // niter - number of iterations // gain - loop gain used in cleaning (a fraction of the maximum // subtracted at every iteration) // aThreshold - absolute threshold to stop iterations // fThreshold - fractional threshold (i.e. given w.r.t. maximum residual) // to stop iterations. This parameter is specified as // Quantity so it can be given in per cents. // choose - unused at the moment, specify False. Original meaning is // to allow interactive decision on whether to continue iterations. // This method always returns True. Bool setcontrol(CleanEnums::CleanType cleanType, const Int niter, const Float gain, const Quantity& aThreshold, const Quantity& fThreshold, const Bool choose=True); // This version of the method disables stopping on fractional threshold Bool setcontrol(CleanEnums::CleanType cleanType, const Int niter, const Float gain, const Quantity& threshold, const Bool choose=True); // return how many iterations we did do Int iteration() const { return itsIteration; } Int numberIterations() const { return itsIteration; } // what iteration number to start on void startingIteration(const Int starting = 0) {itsStartingIter = starting; } // Clean an image. //return value gives you a hint of what's happening // 1 = converged // 0 = not converged but behaving normally // -1 = not converged and stopped on cleaning consecutive smallest scale // -2 = not converged and either large scale hit negative or diverging // -3 = clean is diverging rather than converging Int clean(Lattice & model, LatticeCleanProgress* progress=0); // Set the mask // mask - input mask lattice // maskThreshold - if positive, the value is treated as a threshold value to determine // whether a pixel is good (mask value is greater than the threshold) or has to be // masked (mask value is below the threshold). Negative threshold switches mask clipping // off. The mask value is used to weight the flux during cleaning. This mode is used // to implement cleaning based on the signal-to-noise as opposed to the standard cleaning // based on the flux. The default threshold value is 0.9, which ensures the behavior of the // code is exactly the same as before this parameter has been introduced. void setMask(const Lattice & mask, const T& maskThreshold = T(0.9)); // Tell the algorithm to NOT clean just the inner quarter // (This is useful when multiscale clean is being used // inside a major cycle for MF or WF algorithms) // if True, the full image deconvolution will be attempted void ignoreCenterBox(Bool huh) { itsIgnoreCenterBox = huh; } // Consider the case of a point source: // the flux on all scales is the same, and the first scale will be chosen. // Now, consider the case of a point source with a *little* bit of extended structure: // thats right, the largest scale will be chosen. In this case, we should provide some // bias towards the small scales, or against the large scales. We do this in // an ad hoc manner, multiplying the maxima found at each scale by // 1.0 - itsSmallScaleBias * itsScaleSizes(scale)/itsScaleSizes(nScalesToClean-1); // Typical bias values range from 0.2 to 1.0. void setSmallScaleBias(const Float x=0.5) { itsSmallScaleBias = x; } // During early iterations of a cycled MS Clean in mosaicing, it common // to come across an ocsilatory pattern going between positive and // negative in the large scale. If this is set, we stop at the first // negative in the largest scale. void stopAtLargeScaleNegative() {itsStopAtLargeScaleNegative = True; } // Some algorithms require that the cycles be terminated when the image // is dominated by point sources; if we get nStopPointMode of the // smallest scale components in a row, we terminate the cycles void stopPointMode(Int nStopPointMode) {itsStopPointMode = nStopPointMode; } // After completion of cycle, querry this to find out if we stopped because // of stopPointMode Bool queryStopPointMode() const {return itsDidStopPointMode; } // speedup() will speed the clean iteration by raising the // threshold. This may be required if the threshold is // accidentally set too low (ie, lower than can be achieved // given errors in the approximate PSF). // // threshold(iteration) = threshold(0) // * ( exp( (iteration - startingiteration)/Ndouble )/ 2.718 ) // If speedup() is NOT invoked, no effect on threshold void speedup(const Float Ndouble); // Look at what WE think the residuals look like // Assumes the first scale is zero-sized Lattice* residual() { return itsDirtyConvScales[0]; } // Method to return threshold, including any speedup factors Float threshold() const; // Method to return the strength optimum achieved at the last clean iteration // The output of this method makes sense only if it is called after clean T strengthOptimum() const { return itsStrengthOptimum; } // Helper function to optimize adding static void addTo(Lattice& to, const Lattice& add); protected: // Make sure that the peak of the Psf is within the image Bool validatePsf(const Lattice & psf); // Make an lattice of the specified scale void makeScale(Lattice& scale, const Float& scaleSize); // Make Spheroidal function for scale images Float spheroidal(Float nu); // Find the Peak of the Lattice static Bool findMaxAbsLattice(const Lattice& lattice, T& maxAbs, IPosition& posMax); // Find the Peak of the lattice, applying a mask Bool findMaxAbsMaskLattice(const Lattice& lattice, const Lattice& mask, T& maxAbs, IPosition& posMax); // Helper function to reduce the box sizes until the have the same // size keeping the centers intact static void makeBoxesSameSize(IPosition& blc1, IPosition& trc1, IPosition &blc2, IPosition& trc2); CleanEnums::CleanType itsCleanType; Float itsGain; Int itsMaxNiter; // maximum possible number of iterations Quantum itsThreshold; TempLattice* itsMask; IPosition itsPositionPeakPsf; private: //# The following functions are used in various places in the code and are //# documented in the .cc file. Static functions are used when the functions //# do not modify the object state. They ensure that implicit assumptions //# about the current state and implicit side-effects are not possible //# because all information must be supplied in the input arguments TempLattice* itsDirty; TempLattice* itsXfr; Int itsNscales; Vector itsScaleSizes; PtrBlock* > itsScales; PtrBlock* > itsScaleXfrs; PtrBlock* > itsPsfConvScales; PtrBlock* > itsDirtyConvScales; PtrBlock* > itsScaleMasks; Bool itsScalesValid; Int itsIteration; // what iteration did we get to? Int itsStartingIter; // what iteration did we get to? Quantum itsFracThreshold; Float itsMaximumResidual; T itsStrengthOptimum; Vector itsTotalFluxScale; Float itsTotalFlux; // Memory to be allocated per TempLattice Double itsMemoryMB; // Let the user choose whether to stop Bool itsChoose; // Threshold speedup factors: Bool itsDoSpeedup; // if false, threshold does not change with iteration Float itsNDouble; //# Stop now? //#// Bool stopnow(); Removed on 8-Apr-2004 by GvD // Calculate index into PsfConvScales Int index(const Int scale, const Int otherscale); Bool destroyScales(); Bool destroyMasks(); Bool makeScaleMasks(); Bool itsIgnoreCenterBox; Float itsSmallScaleBias; Bool itsStopAtLargeScaleNegative; Int itsStopPointMode; Bool itsDidStopPointMode; Bool itsJustStarting; // threshold for masks. If negative, mask values are used as weights and no pixels are // discarded (although effectively they would be discarded if the mask value is 0.) T itsMaskThreshold; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/LatticeMath/LatticeCleaner.tcc000066400000000000000000001111371476623553700227110ustar00rootroot00000000000000//# Copyright (C) 1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICECLEANER_TCC #define LATTICES_LATTICECLEANER_TCC #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template Bool LatticeCleaner::validatePsf(const Lattice & psf) { LogIO os(LogOrigin("LatticeCleaner", "validatePsf()", WHERE)); // Find the peak of the raw Psf AlwaysAssert(psf.shape().product() != 0, AipsError); T maxPsf=0; itsPositionPeakPsf=IPosition(psf.shape().nelements(), 0); findMaxAbsLattice(psf, maxPsf, itsPositionPeakPsf); os << "Peak of PSF = " << maxPsf << " at " << itsPositionPeakPsf+1 << LogIO::POST; return True; } template LatticeCleaner::LatticeCleaner(): itsMask(0), itsDirty(0), itsXfr(0), itsScaleSizes(0), itsMaximumResidual(0.0), itsStrengthOptimum(0.0), itsChoose(True), itsDoSpeedup(False), itsIgnoreCenterBox(False), itsSmallScaleBias(0.6), itsStopAtLargeScaleNegative(False), itsStopPointMode(-1), itsDidStopPointMode(False), itsJustStarting(True), itsMaskThreshold(T(0.9)) { itsMemoryMB=Double(HostInfo::memoryTotal()/1024)/16.0; itsScales.resize(0); itsScaleXfrs.resize(0); itsDirtyConvScales.resize(0); itsPsfConvScales.resize(0); itsScaleMasks.resize(0); itsScalesValid = False; itsStartingIter = 0; } template LatticeCleaner::LatticeCleaner(const Lattice & psf, const Lattice &dirty): itsMask(0), itsScaleSizes(0), itsMaximumResidual(0.0), itsStrengthOptimum(0.), itsChoose(True), itsDoSpeedup(False), itsIgnoreCenterBox(False), itsSmallScaleBias(0.6), itsStopAtLargeScaleNegative(False), itsStopPointMode(-1), itsDidStopPointMode(False), itsJustStarting(True) { AlwaysAssert(validatePsf(psf), AipsError); // Check that everything is the same dimension and that none of the // dimensions is zero length. AlwaysAssert(psf.shape().nelements() == dirty.shape().nelements(), AipsError); AlwaysAssert(dirty.shape().product() != 0, AipsError); // looks OK so make the convolver // We need to guess the memory use. For the moment, we'll assume // that about 4 scales will be used, giving about 32 TempLattices // in all. Also we'll try not to take more that half of the memory // Ah, but when we are doing a mosaic, its actually worse than this! // So, we pass it in itsMemoryMB=Double(HostInfo::memoryTotal()/1024)/16.0; itsDirty = new TempLattice(dirty.shape(), itsMemoryMB); itsDirty->copyData(dirty); itsXfr=new TempLattice(psf.shape(), itsMemoryMB); itsXfr->copyData(LatticeExpr(toComplex(psf))); LatticeFFT::cfft2d(*itsXfr, True); itsScales.resize(0); itsScaleXfrs.resize(0); itsDirtyConvScales.resize(0); itsPsfConvScales.resize(0); itsScaleMasks.resize(0); itsScalesValid = False; itsStartingIter = 0; } template LatticeCleaner:: LatticeCleaner(const LatticeCleaner & other): itsCleanType(other.itsCleanType), itsMask(other.itsMask), itsDirty(other.itsDirty), itsXfr(other.itsXfr), itsScales(other.itsScales), itsScaleXfrs(other.itsScaleXfrs), itsPsfConvScales(other.itsPsfConvScales), itsDirtyConvScales(other.itsDirtyConvScales), itsScaleMasks(other.itsScaleMasks), itsStartingIter(other.itsStartingIter), itsMaximumResidual(other.itsMaximumResidual), itsStrengthOptimum(other.itsStrengthOptimum), itsIgnoreCenterBox(other.itsIgnoreCenterBox), itsSmallScaleBias(other.itsSmallScaleBias), itsStopAtLargeScaleNegative(other.itsStopAtLargeScaleNegative), itsStopPointMode(other.itsStopPointMode), itsDidStopPointMode(other.itsDidStopPointMode), itsJustStarting(other.itsJustStarting), itsMaskThreshold(other.itsMaskThreshold) { } template LatticeCleaner & LatticeCleaner:: operator=(const LatticeCleaner & other) { if (this != &other) { itsCleanType = other.itsCleanType; itsXfr = other.itsXfr; itsMask = other.itsMask; itsDirty = other.itsDirty; itsScales = other.itsScales; itsScaleXfrs = other.itsScaleXfrs; itsPsfConvScales = other.itsPsfConvScales; itsDirtyConvScales = other.itsDirtyConvScales; itsScaleMasks = other.itsScaleMasks; itsStartingIter = other.itsStartingIter; itsMaximumResidual = other.itsMaximumResidual; itsIgnoreCenterBox = other.itsIgnoreCenterBox; itsSmallScaleBias = other.itsSmallScaleBias; itsStopAtLargeScaleNegative = other.itsStopAtLargeScaleNegative; itsStopPointMode = other.itsStopPointMode; itsDidStopPointMode = other.itsDidStopPointMode; itsJustStarting = other.itsJustStarting; itsStrengthOptimum = other.itsStrengthOptimum; itsMaskThreshold = other.itsMaskThreshold; } return *this; } template LatticeCleaner:: ~LatticeCleaner() { destroyScales(); if(itsDirty) delete itsDirty; if(itsXfr) delete itsXfr; if(itsMask) delete itsMask; } template void LatticeCleaner::update(const Lattice &dirty) { AlwaysAssert(dirty.shape()==itsDirty->shape(), AipsError); itsDirty->copyData(dirty); LogIO os(LogOrigin("LatticeCleaner", "clean()", WHERE)); TempLattice dirtyFT(itsDirty->shape(), itsMemoryMB); dirtyFT.copyData(LatticeExpr(toComplex(*itsDirty))); LatticeFFT::cfft2d(dirtyFT, True); // Now we can redo the relevant convolutions TempLattice cWork(itsDirty->shape(), itsMemoryMB); for (Int scale=0; scale dpsExpr( (dirtyFT)*(*itsScaleXfrs[scale])); cWork.copyData(dpsExpr); LatticeFFT::cfft2d(cWork, False); AlwaysAssert(itsDirtyConvScales[scale], AipsError); LatticeExpr realWork2(real(cWork)); itsDirtyConvScales[scale]->copyData(realWork2); } } // add a mask image template void LatticeCleaner::setMask(const Lattice & mask, const T& maskThreshold) { itsMaskThreshold = maskThreshold; const IPosition maskShape = mask.shape(); const IPosition dirtyShape = itsDirty->shape(); AlwaysAssert((maskShape == dirtyShape), AipsError); // This is not needed after the first steps itsMask = new TempLattice(maskShape, itsMemoryMB); itsMask->copyData(mask); if (itsScalesValid) { makeScaleMasks(); } } template Bool LatticeCleaner::setcontrol(CleanEnums::CleanType cleanType, const Int niter, const Float gain, const Quantity& threshold, const Bool choose) { return setcontrol(cleanType, niter, gain, threshold, Quantity(0.0, "%"), choose); } // Set up the control parameters template Bool LatticeCleaner::setcontrol(CleanEnums::CleanType cleanType, const Int niter, const Float gain, const Quantity& aThreshold, const Quantity& fThreshold, const Bool choose) { itsCleanType=cleanType; itsMaxNiter=niter; itsGain=gain; itsThreshold=aThreshold; itsFracThreshold=fThreshold; itsChoose=choose; return True; } // Set up speedup parameters template void LatticeCleaner::speedup(const Float nDouble) { itsDoSpeedup=True; itsNDouble = nDouble; }; // Do the clean as set up template Int LatticeCleaner::clean(Lattice& model, LatticeCleanProgress* progress) { AlwaysAssert(model.shape()==itsDirty->shape(), AipsError); LogIO os(LogOrigin("LatticeCleaner", "clean()", WHERE)); T tmpMaximumResidual; tmpMaximumResidual=T(); Int nScalesToClean=itsNscales; if (itsCleanType==CleanEnums::HOGBOM) { os << LogIO::NORMAL1 << "Hogbom clean algorithm" << LogIO::POST; nScalesToClean=1; } else if (itsCleanType==CleanEnums::MULTISCALE) { if (nScalesToClean==1) { os << LogIO::NORMAL1 << "Multi-scale clean with only one scale" << LogIO::POST; } else { os << LogIO::NORMAL1 << "Multi-scale clean algorithm" << LogIO::POST; } } Int scale; Vector scaleBias(nScalesToClean); if (nScalesToClean > 1) { os << LogIO::NORMAL1 << "Scale biases ="; for (scale=0;scale maxPsfConvScales(nScalesToClean); for (scale=0;scaleshape()(0)==nx, AipsError); AlwaysAssert(itsMask->shape()(1)==ny, AipsError); LatticeStepper mls(itsMask->shape(), IPosition(4, nx, ny, 1, 1), IPosition(4, 0, 1, 3, 2)); RO_LatticeIterator maskli(*itsMask, mls); maskli.reset(); Int xbeg=nx-1; Int ybeg=ny-1; Int xend=0; Int yend=0; for (Int iy=0;iy0.000001) { xbeg=min(xbeg,ix); ybeg=min(ybeg,iy); xend=max(xend,ix); yend=max(yend,iy); } } } if (!itsIgnoreCenterBox) { if((xend - xbeg)>nx/2) { xbeg=nx/4-1; //if larger than quarter take inner of mask os << LogIO::WARN << "Mask span over more than half the x-axis: Considering inner half of the x-axis" << LogIO::POST; } if((yend - ybeg)>ny/2) { ybeg=ny/4-1; os << LogIO::WARN << "Mask span over more than half the y-axis: Considering inner half of the y-axis" << LogIO::POST; } xend=min(xend,xbeg+nx/2-1); yend=min(yend,ybeg+ny/2-1); } blcDirty(0)=xbeg; blcDirty(1)=ybeg; trcDirty(0)=xend; trcDirty(1)=yend; } else { if (itsIgnoreCenterBox) { os << LogIO::NORMAL << "Cleaning entire image" << LogIO::POST; os << LogIO::NORMAL1 << "as per MF/WF" << LogIO::POST; // ??? } else { os << "Cleaning inner quarter of the image" << LogIO::POST; for (Int i=0;i* > scaleMaskSubs; if (itsMask) { scaleMaskSubs.resize(itsNscales); for (Int is=0; is < itsNscales; is++) { scaleMaskSubs[is] = new SubLattice(*(itsScaleMasks[is]), centerBox); } } // Start the iteration Vector maxima(nScalesToClean); Block posMaximum(nScalesToClean); Vector totalFluxScale(nScalesToClean); totalFluxScale=0.0; T totalFlux=0.0; Int converged=0; Int stopPointModeCounter = 0; Int optimumScale=0; itsStrengthOptimum=0.0; IPosition positionOptimum(model.shape().nelements(), 0); os << "Starting iteration"<< LogIO::POST; itsIteration = itsStartingIter; for (Int ii=itsStartingIter; ii < itsMaxNiter; ii++) { itsIteration++; // Find the peak residual itsStrengthOptimum = 0.0; optimumScale = 0; for (scale=0; scale dirtySub(*itsDirtyConvScales[scale], centerBox); maxima(scale)=0; posMaximum[scale]=IPosition(model.shape().nelements(), 0); if (itsMask) { findMaxAbsMaskLattice(dirtySub, *(scaleMaskSubs[scale]), maxima(scale), posMaximum[scale]); } else { findMaxAbsLattice(dirtySub, maxima(scale), posMaximum[scale]); } // Remember to adjust the position for the window and for // the flux scale maxima(scale)/=maxPsfConvScales(scale); maxima(scale) *= scaleBias(scale); posMaximum[scale]+=blcDirty; if(abs(maxima(scale))>abs(itsStrengthOptimum)) { optimumScale=scale; itsStrengthOptimum=maxima(scale); positionOptimum=posMaximum[scale]; } } AlwaysAssert(optimumScale 1) && itsStopAtLargeScaleNegative && optimumScale == (nScalesToClean-1) && itsStrengthOptimum < 0.0) { os << "Reached negative on largest scale" << LogIO::POST; converged = -2; break; } // 3. stop point mode at work if (itsStopPointMode > 0) { if (optimumScale == 0) { stopPointModeCounter++; } else { stopPointModeCounter = 0; } if (stopPointModeCounter >= itsStopPointMode) { os << "Cleaned " << stopPointModeCounter << " consecutive components from the smallest scale, stopping prematurely" << LogIO::POST; itsDidStopPointMode = True; converged = -1; break; } } //4. Diverging large scale //If actual value is 50% above the maximum residual. ..good chance it will not recover at this stage if(((abs(itsStrengthOptimum)-abs(tmpMaximumResidual)) > (abs(tmpMaximumResidual)/2.0)) && !(itsStopAtLargeScaleNegative)){ os << "Diverging due to large scale?" << LogIO::POST; //clean is diverging most probably due to the large scale converged=-2; break; } //5. Diverging for some other reason; may just need another CS-style reconciling if((abs(itsStrengthOptimum)-abs(tmpMaximumResidual)) > (abs(tmpMaximumResidual)/2.0)){ os << "Diverging due to unknown reason" << LogIO::POST; converged=-3; break; } if(progress) { progress->info(False, itsIteration, itsMaxNiter, maxima, posMaximum, itsStrengthOptimum, optimumScale, positionOptimum, totalFlux, totalFluxScale, itsJustStarting ); itsJustStarting = False; } else { if (itsIteration == itsStartingIter + 1) { os << "iteration MaximumResidual CleanedFlux" << LogIO::POST; } if ((itsIteration % (itsMaxNiter/10 > 0 ? itsMaxNiter/10 : 1)) == 0) { //Good place to re-up the fiducial maximum residual //tmpMaximumResidual=abs(itsStrengthOptimum); os << itsIteration <<" "< modelSub(model, subRegion, True); SubLattice scaleSub(*itsScales[optimumScale], subRegionPsf, True); // Now do the addition of this scale to the model image.... LatticeExpr add(scaleFactor*scaleSub); addTo(modelSub, add); // and then subtract the effects of this scale from all the precomputed // dirty convolutions. for (scale=0;scale dirtySub(*itsDirtyConvScales[scale], subRegion, True); AlwaysAssert(itsPsfConvScales[index(scale,optimumScale)], AipsError); SubLattice psfSub(*itsPsfConvScales[index(scale,optimumScale)], subRegionPsf, True); LatticeExpr sub((-scaleFactor)*psfSub); addTo(dirtySub, sub); } } // End of iteration for (scale=0;scaleinfo(True, itsIteration, itsMaxNiter, maxima, posMaximum, itsStrengthOptimum, optimumScale, positionOptimum, totalFlux, totalFluxScale); } if(!converged) { os << "Failed to reach stopping threshold" << LogIO::POST; } return converged; } template Bool LatticeCleaner::findMaxAbsLattice(const Lattice& lattice, T& maxAbs, IPosition& posMaxAbs) { posMaxAbs = IPosition(lattice.shape().nelements(), 0); maxAbs=0.0; const IPosition tileShape = lattice.niceCursorShape(); TiledLineStepper ls(lattice.shape(), tileShape, 0); { RO_LatticeIterator li(lattice, ls); for(li.reset();!li.atEnd();li++) { IPosition posMax=li.position(); IPosition posMin=li.position(); T maxVal=0.0; T minVal=0.0; minMax(minVal, maxVal, posMin, posMax, li.cursor()); if(abs(minVal)>abs(maxAbs)) { maxAbs=minVal; posMaxAbs=li.position(); posMaxAbs(0)=posMin(0); } if(abs(maxVal)>abs(maxAbs)) { maxAbs=maxVal; posMaxAbs=li.position(); posMaxAbs(0)=posMax(0); } } } return True; } template Bool LatticeCleaner::findMaxAbsMaskLattice(const Lattice& lattice, const Lattice& mask, T& maxAbs, IPosition& posMaxAbs) { posMaxAbs = IPosition(lattice.shape().nelements(), 0); maxAbs=0.0; const IPosition tileShape = lattice.niceCursorShape(); TiledLineStepper ls(lattice.shape(), tileShape, 0); { RO_LatticeIterator li(lattice, ls); RO_LatticeIterator mi(mask, ls); for(li.reset(),mi.reset();!li.atEnd();li++, mi++) { IPosition posMax=li.position(); IPosition posMin=li.position(); IPosition posMaxMask=li.position(); IPosition posMinMask=li.position(); T maxVal=0.0; T minVal=0.0; minMaxMasked(minVal, maxVal, posMin, posMax, li.cursor(), mi.cursor()); if (itsMaskThreshold<0) { // Mask threhsolding is not used, i.e. mask values are interpreted as weights. // This means that minVal and maxVal are optima of the mask * lattice product, // we need just values of lattice and have to redetermine them. minVal = li.cursor()(posMin); maxVal = li.cursor()(posMax); } if(abs(minVal)>abs(maxAbs)) { maxAbs=minVal; posMaxAbs=li.position(); posMaxAbs(0)=posMin(0); } if(abs(maxVal)>abs(maxAbs)) { maxAbs=maxVal; posMaxAbs=li.position(); posMaxAbs(0)=posMax(0); } } } return True; } template Bool LatticeCleaner::setscales(const Int nscales, const Float scaleInc) { LogIO os(LogOrigin("deconvolver", "setscales()", WHERE)); itsNscales=nscales; if(itsNscales<1) { os << "Using default of 5 scales" << LogIO::POST; itsNscales=5; } Vector scaleSizes(itsNscales); // Validate scales os << "Creating " << itsNscales << " scales" << LogIO::POST; scaleSizes(0) = 0.00001 * scaleInc; os << "scale 1 = 0.0 arcsec" << LogIO::POST; for (Int scale=1; scale Bool LatticeCleaner::setscales(const Vector& scaleSizes) { LogIO os(LogOrigin("deconvolver", "setscales()", WHERE)); Int scale; if(itsScales.nelements()>0) { destroyScales(); } destroyMasks(); itsNscales=scaleSizes.nelements(); // Residual, psf, and mask, plus cross terms // e.g. for 5 scales this is 45. for 6 it is 60. Int nImages=3*itsNscales+itsNscales*(itsNscales+1); os << "Expect to use " << nImages << " scratch images" << LogIO::POST; // Now we can update the size of memory allocated itsMemoryMB=0.5*Double(HostInfo::memoryTotal()/1024)/Double(nImages); os << "Maximum memory allocated per image " << itsMemoryMB << "MB" << LogIO::POST; itsScaleSizes.resize(itsNscales); itsScaleSizes=scaleSizes; // make a copy that we can call our own GenSort::sort(itsScaleSizes); itsScales.resize(itsNscales); itsDirtyConvScales.resize(itsNscales); itsScaleMasks.resize(itsNscales); itsScaleXfrs.resize(itsNscales); itsPsfConvScales.resize((itsNscales+1)*(itsNscales+1)); for(scale=0; scale dirtyFT(itsDirty->shape(), itsMemoryMB); dirtyFT.copyData(LatticeExpr(toComplex(*itsDirty))); LatticeFFT::cfft2d(dirtyFT, True); for (scale=0; scale(itsDirty->shape(), itsMemoryMB); AlwaysAssert(itsScales[scale], AipsError); // First make the scale makeScale(*itsScales[scale], scaleSizes(scale)); itsScaleXfrs[scale] = new TempLattice (itsScales[scale]->shape(), itsMemoryMB); // Now store the XFR itsScaleXfrs[scale]->copyData(LatticeExpr(toComplex(*itsScales[scale]))); // Now FFT LatticeFFT::cfft2d(*itsScaleXfrs[scale], True); } // Now we can do all the convolutions TempLattice cWork(itsDirty->shape(), itsMemoryMB); for (scale=0; scale ppsExpr( (*itsXfr)*(*itsScaleXfrs[scale])); cWork.copyData(ppsExpr); LatticeFFT::cfft2d(cWork, False); itsPsfConvScales[scale] = new TempLattice(itsDirty->shape(), itsMemoryMB); AlwaysAssert(itsPsfConvScales[scale], AipsError); LatticeExpr realWork(real(cWork)); itsPsfConvScales[scale]->copyData(realWork); // Dirty * scale LatticeExpr dpsExpr( (dirtyFT)*(*itsScaleXfrs[scale])); cWork.copyData(dpsExpr); LatticeFFT::cfft2d(cWork, False); itsDirtyConvScales[scale] = new TempLattice(itsDirty->shape(), itsMemoryMB); AlwaysAssert(itsDirtyConvScales[scale], AipsError); LatticeExpr realWork2(real(cWork)); itsDirtyConvScales[scale]->copyData(realWork2); for (Int otherscale=scale;otherscale ppsoExpr( (*itsXfr)*conj(*itsScaleXfrs[scale])*(*itsScaleXfrs[otherscale])); cWork.copyData(ppsoExpr); LatticeFFT::cfft2d(cWork, False); itsPsfConvScales[index(scale,otherscale)] = new TempLattice(itsDirty->shape(), itsMemoryMB); AlwaysAssert(itsPsfConvScales[index(scale,otherscale)], AipsError); LatticeExpr realWork3(real(cWork)); itsPsfConvScales[index(scale,otherscale)]->copyData(realWork3); } } itsScalesValid=True; if (itsMask) { makeScaleMasks(); } return True; } // Make a single scale size image template void LatticeCleaner::makeScale(Lattice& scale, const Float& scaleSize) { Int nx=scale.shape()(0); Int ny=scale.shape()(1); Matrix iscale(nx, ny); iscale=0.0; Double refi=nx/2; Double refj=ny/2; if(scaleSize==0.0) { iscale(Int(refi), Int(refj)) = 1.0; } else { AlwaysAssert(scaleSize>0.0,AipsError); Int mini = max( 0, (Int)(refi-scaleSize)); Int maxi = min(nx-1, (Int)(refi+scaleSize)); Int minj = max( 0, (Int)(refj-scaleSize)); Int maxj = min(ny-1, (Int)(refj+scaleSize)); Float ypart=0.0; Float volume=0.0; Float rad2=0.0; Float rad=0.0; for (Int j=minj;j<=maxj;j++) { ypart = square( (refj - (Double)(j)) / scaleSize ); for (Int i=mini;i<=maxi;i++) { rad2 = ypart + square( (refi - (Double)(i)) / scaleSize ); if (rad2 < 1.0) { if (rad2 <= 0.0) { rad = 0.0; } else { rad = sqrt(rad2); } iscale(i,j) = (1.0 - rad2) * spheroidal(rad); volume += iscale(i,j); } else { iscale(i,j) = 0.0; } } } iscale/=volume; } scale.putSlice(iscale, IPosition(scale.ndim(),0), IPosition(scale.ndim(),1)); } // Calculate the spheroidal function template Float LatticeCleaner::spheroidal(Float nu) { if (nu <= 0) { return 1.0; } else if (nu >= 1.0) { return 0.0; } else { uInt np = 5; uInt nq = 3; Matrix p(np, 2); Matrix q(nq, 2); p(0,0) = 8.203343e-2; p(1,0) = -3.644705e-1; p(2,0) = 6.278660e-1; p(3,0) = -5.335581e-1; p(4,0) = 2.312756e-1; p(0,1) = 4.028559e-3; p(1,1) = -3.697768e-2; p(2,1) = 1.021332e-1; p(3,1) = -1.201436e-1; p(4,1) = 6.412774e-2; q(0,0) = 1.0000000e0; q(1,0) = 8.212018e-1; q(2,0) = 2.078043e-1; q(0,1) = 1.0000000e0; q(1,1) = 9.599102e-1; q(2,1) = 2.918724e-1; uInt part = 0; Float nuend = 0.0; if (nu >= 0.0 && nu < 0.75) { part = 0; nuend = 0.75; } else if (nu >= 0.75 && nu <= 1.00) { part = 1; nuend = 1.0; } Float top = p(0,part); Float delnusq = pow(nu,2.0) - pow(nuend,2.0); uInt k; for (k=1; k Int LatticeCleaner::index(const Int scale, const Int otherscale) { if(otherscale>scale) { return scale + itsNscales*(otherscale+1); } else { return otherscale + itsNscales*(scale+1); } } template Bool LatticeCleaner::destroyScales() { if(!itsScalesValid) return True; for(uInt scale=0; scale Bool LatticeCleaner::destroyMasks() { for(uInt scale=0; scale // Bool LatticeCleaner::stopnow() { // if(itsChoose) { // LogIO os(LogOrigin("LatticeCleaner", "stopnow()", WHERE)); // Bool stop = ApplicationEnvironment::stop(); // if(stop) { // os << "Lattice clean stopped at user request" << LogIO::POST; // return True; // } // Vector choices(2); // choices(0)="Continue"; // choices(1)="Stop Now"; // choices(2)="Don't ask again"; // String choice = // ApplicationEnvironment::choice("Do you want to continue or stop?", // choices); // if (choice==choices(0)) { // return False; // } // else if (choice==choices(2)) { // itsChoose=False; // os << "Continuing: won't ask again" << LogIO::POST; // return False; // } // else { // os << "Lattice clean stopped at user request" << LogIO::POST; // return True; // } // } // else { // return False; // } // } // Set up the masks for the various scales // This really only works for well behaved (ie, non-concave) masks. // with only 1.0 or 0.0 values, and assuming the Scale images have // a finite extent equal to +/- itsScaleSizes(scale) template Bool LatticeCleaner::makeScaleMasks() { LogIO os(LogOrigin("deconvolver", "makeScaleMasks()", WHERE)); Int scale; if(!itsScalesValid) { os << "Scales are not yet set - cannot set scale masks" << LogIO::EXCEPTION; } destroyMasks(); AlwaysAssert(itsMask, AipsError); TempLattice maskFT(itsMask->shape(), itsMemoryMB); maskFT.copyData(LatticeExpr(toComplex(*itsMask))); LatticeFFT::cfft2d(maskFT, True); // Now we can do all the convolutions TempLattice cWork(itsScaleXfrs[0]->shape(), itsMemoryMB); for (scale=0; scale maskExpr((maskFT)*(*itsScaleXfrs[scale])); cWork.copyData(maskExpr); LatticeFFT::cfft2d(cWork, False); // Allow only 10% overlap by default, hence 0.9 is a default mask threshold // if thresholding is not used, just extract the real part of the complex mask LatticeExpr maskWork( itsMaskThreshold < 0 ? real(cWork) : iif(real(cWork)>itsMaskThreshold,1.0,0.0)); itsScaleMasks[scale] = new TempLattice(itsMask->shape(), itsMemoryMB); AlwaysAssert(itsScaleMasks[scale], AipsError); itsScaleMasks[scale]->copyData(maskWork); LatticeExprNode LEN; LEN = sum( *itsScaleMasks[scale] ); Float mysum = LEN.getFloat(); if (mysum <= 0.1) { os << LogIO::WARN << "Ignoring scale " << scale+1 << " since it is too large to fit within the mask" << LogIO::POST; } } return True; } template Float LatticeCleaner::threshold() const { if (! itsDoSpeedup) { return max(itsFracThreshold.get("%").getValue() * itsMaximumResidual /100.0, itsThreshold.get("Jy").getValue()); } else { const Float factor = exp( (Float)( itsIteration - itsStartingIter )/ itsNDouble ) / 2.7182818; return factor * max(itsFracThreshold.get("%").getValue() * itsMaximumResidual /100.0, itsThreshold.get("Jy").getValue()); } } template void LatticeCleaner::addTo(Lattice& to, const Lattice& add) { // Check the lattice is writable. // Check the shape conformance. AlwaysAssert (to.isWritable(), AipsError); const IPosition shapeIn = add.shape(); const IPosition shapeOut = to.shape(); AlwaysAssert (shapeIn.isEqual (shapeOut), AipsError); IPosition cursorShape = to.niceCursorShape(); LatticeStepper stepper (shapeOut, cursorShape, LatticeStepper::RESIZE); LatticeIterator toIter(to, stepper); RO_LatticeIterator addIter(add, stepper); for (addIter.reset(), toIter.reset(); !addIter.atEnd(); addIter++, toIter++) { toIter.rwCursor()+=addIter.cursor(); } } template void LatticeCleaner::makeBoxesSameSize(IPosition& blc1, IPosition& trc1, IPosition &blc2, IPosition& trc2) { const IPosition shape1 = trc1 - blc1; const IPosition shape2 = trc2 - blc2; AlwaysAssert(shape1.nelements() == shape2.nelements(), AipsError); if (shape1 == shape2) { return; } for (uInt i=0;i=0, AipsError); //if (minLength % 2 != 0) { // if the number of pixels is odd, ensure that the centre stays // the same by making this number even //--minLength; // this code is a mistake and should be removed //} const Int increment1 = shape1[i] - minLength; const Int increment2 = shape2[i] - minLength; blc1[i] += increment1/2; trc1[i] -= increment1/2 + (increment1 % 2 != 0 ? 1 : 0); blc2[i] += increment2/2; trc2[i] -= increment2/2 + (increment2 % 2 != 0 ? 1 : 0); } } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LatticeMath/LatticeConvolver.h000066400000000000000000000225411476623553700227730ustar00rootroot00000000000000//# Convolver.h: this defines Convolver a class for doing convolution //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICECONVOLVER_H #define LATTICES_LATTICECONVOLVER_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations //template class LatticeConvolver; class IPosition; // Lists the different types of Convolutions that can be done // This enumerator is brought out as a separate class because g++ // currently cannot handle enumerators in a templated class. When it can this // class will go away and this enumerator moved into the Convolver // class class ConvEnums { public: enum ConvType { // Linear convolution LINEAR, // Circular Convolution CIRCULAR //# Assume the point spread function is symmetric //#REALSYMMETRIC }; }; // A class for doing multi-dimensional convolution // // // // //
      • The mathematical concept of convolution // // // // The LatticeConvolver class will convolve Lattices. This class // complements the Convolver class which will convolve Arrays. // // // // This class performs linear or circular convolution on Lattices. See the // Convolver class description of the // difference between linear and circular convolution. // This class does convolutions by multiplying the Fourier transforms of the // supplied Lattices and returning the inverse transform of the product. This // is the best algorithm to use when the point spread function is large. This // class does all the padding with zeros necessary to implement this // algorithm. Hence the // // // // // // // // // // // // //
      • AipsError: if psf and model have a differing numbers of dimensions // // // //
      • the class should detect if the psf or image is small and do the // convolution directly rather than use the Fourier domain //
      • Allow the psf to be specified with a // Function. // template class LatticeConvolver { public: LatticeConvolver() = delete; // Create a convolver that is initialised to do circular convolution with the // specified point spread function. It is assumed that the supplied model // will be the same shape as the point spread function. LatticeConvolver(const Lattice & psf, Bool doFast=False); // Create a convolver that is initialised to do linear convolution with the // specified point spread function. The size of the model you will convolve // with must be specified. LatticeConvolver(const Lattice & psf, const IPosition & modelShape, Bool doFast=False); // Create a convolver that is initialised to do the specified type of // convolution with the specified point spread function. The size of the // model you expect to convolve with must be specified. LatticeConvolver(const Lattice & psf, const IPosition & modelShape, ConvEnums::ConvType type, Bool doFast=False); // The copy constructor uses reference semantics LatticeConvolver(const LatticeConvolver & other); // The assignment operator also uses reference semantics LatticeConvolver & operator=(const LatticeConvolver & other); // The destructor does nothing special. ~LatticeConvolver(); // Perform linear convolution of the model with the previously specified // psf. The supplied Lattices must be the same shape. void linear(Lattice & result, const Lattice & model); // Perform in-place linear convolution of the model with the previously // specified psf. Return the result in the same Lattice as the // model. void linear(Lattice & modelAndResult); // Perform circular convolution of the model with the previously // specified psf. Return the answer in result. void circular(Lattice & result, const Lattice & model); // Perform in-place linear convolution of the model with the previously // specified psf. Return the result in the same Lattice as the model. void circular(Lattice & modelAndResult); // Perform convolution on the specified model using the currently initialised // convolution type (linear or circular). These functions will not resize the // LatticeConvolver if the supplied Lattice is the wrong shape. // // If the LatticeConvolver is setup for circular Convolution then the size of // the supplied model must be less than or equal to the shape returned by the // fftshape() function, which is usually the same as the shape of the psf. // // If the LatticeConvolver is setup to do linear convolution the the // input and output Lattices must have the same shape as the result from the // shape() member function. The convolution may be either in-place or not. // void convolve(Lattice & modelAndResult) const; void convolve(Lattice & result, const Lattice & model) const; // // Return the psf currently used by this convolver. The supplied Lattice must // be the correct shape ie., the same as returned by the psfShape member // function. void getPsf(Lattice & psf) const; // Resize the LatticeConvolver to do convolutions of the specified type and // shape. The supplied function must always have the same number of // dimensions as the internal point spread function (which can be found using // the shape member function). The LatticeConvolver will be set up to do // circular or linear convolutions depending on the supplied type void resize(const IPosition & modelShape, ConvEnums::ConvType type); // Returns the shape of the Lattices that the convolver will convolve. This // shape will always have as many dimensions as the psf that was used to // initialise the LatticeConvolver. If the LatticeConvolver is setup to do // circular convolutions then every axis of the returned IPosition will be // zero length. If the LatticeConvolver is setup to do linear convolutions // then the returned IPosition will have a positive values on each axis that // indicate the expected shape of the input model. IPosition shape() const; // Returns the shape of the point spread function that the LatticeConvolver // was initialised with. IPosition psfShape() const; // Returns the type of convolution the LatticeConvolver is currently set up // to do. ConvEnums::ConvType type() const; // Returns the shape of the FFT's that the LatticeConvolver will do when // performing the convolution. Not really useful except as a diagnostic // tool. If the shape contains a lot of poorly factorisable lengths then the // convolution will be slow. IPosition fftShape() const; // Set usage of fast convolve with lesser flips void setFastConvolve(); private: //# The following functions are used in various places in the code and are //# documented in the .cc file. Static functions are used when the functions //# do not use the object state. They ensure that implicit assumptions //# about the current state and implicit side-effects are not possible //# because all information must be suplied in the input arguments static void pad(Lattice & paddedLat, const Lattice & inLat); static void unpad(Lattice & result, const Lattice & paddedResult); void makeXfr(const Lattice & psf); void makePsf(Lattice & psf) const; static IPosition calcFFTShape(const IPosition & psfShape, const IPosition & modelShape, ConvEnums::ConvType type); IPosition itsPsfShape; IPosition itsModelShape; ConvEnums::ConvType itsType; IPosition itsFFTShape; TempLattice::ConjugateType>* itsXfr; TempLattice* itsPsf; Bool itsCachedPsf; Bool doFast_p; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/LatticeMath/LatticeConvolver.tcc000066400000000000000000000365141476623553700233220ustar00rootroot00000000000000// -*- C++ -*- //# Copyright (C) 1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICECONVOLVER_TCC #define LATTICES_LATTICECONVOLVER_TCC #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN const Int maxLatSize = HostInfo::memoryTotal()/1024/8; template LatticeConvolver:: LatticeConvolver(const Lattice & psf, Bool doFast) :itsPsfShape(psf.shape()), itsModelShape(itsPsfShape), itsType(ConvEnums::CIRCULAR), itsFFTShape(psf.ndim(), 0), itsXfr(0), itsPsf(0), itsCachedPsf(False) { DebugAssert(itsPsfShape.product() != 0, AipsError); doFast_p=doFast; makeXfr(psf); } template LatticeConvolver:: LatticeConvolver(const Lattice & psf, const IPosition & modelShape, Bool doFast) :itsPsfShape(psf.shape()), itsModelShape(modelShape), itsType(ConvEnums::LINEAR), itsFFTShape(psf.ndim(), 0), itsXfr(0), itsPsf(0), itsCachedPsf(False) { // Check that everything is the same dimension and that none of the // dimensions is zero length. DebugAssert(itsPsfShape.nelements() == itsModelShape.nelements(),AipsError); DebugAssert(itsPsfShape.product() != 0, AipsError); DebugAssert(itsModelShape.product() != 0, AipsError); // looks OK so make the transfer function doFast_p=doFast; makeXfr(psf); } template LatticeConvolver:: LatticeConvolver(const Lattice & psf, const IPosition & modelShape, ConvEnums::ConvType type, Bool doFast) :itsPsfShape(psf.shape()), itsModelShape(modelShape), itsType(type), itsFFTShape(psf.ndim(), 0), itsXfr(0), itsPsf(0), itsCachedPsf(False) { // Check that everything is the same dimension and that none of the // dimensions is zero length. DebugAssert(itsPsfShape.nelements() == itsModelShape.nelements(),AipsError); DebugAssert(itsPsfShape.product() != 0, AipsError); DebugAssert(itsModelShape.product() != 0, AipsError); // looks OK so make the psf doFast_p=doFast; makeXfr(psf); } template LatticeConvolver:: LatticeConvolver(const LatticeConvolver & other) :itsPsfShape(other.itsPsfShape), itsModelShape(other.itsModelShape), itsType(other.itsType), itsFFTShape(other.itsFFTShape), itsXfr(other.itsXfr), itsPsf(other.itsPsf), itsCachedPsf(other.itsCachedPsf) { } template LatticeConvolver & LatticeConvolver:: operator=(const LatticeConvolver & other) { if (this != &other) { itsModelShape = other.itsModelShape; itsPsfShape = other.itsPsfShape; itsType = other.itsType; itsFFTShape = other.itsFFTShape; itsXfr = other.itsXfr; itsPsf = other.itsPsf; itsCachedPsf = other.itsCachedPsf; doFast_p=other.doFast_p; } return *this; } template LatticeConvolver:: ~LatticeConvolver() { if(itsPsf) { delete itsPsf; itsPsf = 0; } if(itsXfr) { delete itsXfr; itsXfr = 0; } } template void LatticeConvolver:: getPsf(Lattice & psf) const { DebugAssert(psf.ndim() == itsPsfShape.nelements(), AipsError); DebugAssert(psf.shape() == itsPsfShape, AipsError); if (itsCachedPsf) { // used the cached Psf if possible itsPsf->copyDataTo(psf); } else { // reconstruct the psf from the transfer function makePsf(psf); } } template void LatticeConvolver:: linear(Lattice & result, const Lattice & model) { resize(model.shape(), ConvEnums::LINEAR); convolve(result, model); } template void LatticeConvolver:: linear(Lattice & modelAndResult){ linear(modelAndResult, modelAndResult); } template void LatticeConvolver:: circular(Lattice & result, const Lattice & model) { resize(model.shape(), ConvEnums::CIRCULAR); convolve(result, model); } template void LatticeConvolver:: circular(Lattice & modelAndResult){ circular(modelAndResult, modelAndResult); } template void LatticeConvolver:: convolve(Lattice & result, const Lattice & model) const { // cerr << "convolve: " << model.shape() << " " << itsXfr->shape() << endl; const uInt ndim = itsFFTShape.nelements(); DebugAssert(result.ndim() == ndim, AipsError); DebugAssert(model.ndim() == ndim, AipsError); const IPosition modelShape = model.shape(); DebugAssert(result.shape() == modelShape, AipsError); DebugAssert(modelShape == itsModelShape, AipsError); // Create a lattice that will hold the transform. Do this before creating the // paddedModel TempLattice so that it is more likely to be memory based. IPosition XFRShape(itsFFTShape); XFRShape(0) = (XFRShape(0)+2)/2; TempLattice::ConjugateType> fftModel(XFRShape, maxLatSize); // Copy the model into a larger Lattice that has the appropriate padding. // (if necessary) Bool doPadding = False; const Lattice* modelPtr = 0; Lattice* resultPtr = 0; if (!(itsFFTShape <= modelShape)) { doPadding = True; resultPtr = new TempLattice(itsFFTShape, maxLatSize); modelPtr = resultPtr; } IPosition sliceShape(ndim,1); for (uInt n = 0; n < ndim; n++) { if (itsFFTShape(n) > 1) { sliceShape(n) = modelShape(n); } } LatticeStepper ls(modelShape, sliceShape); for (ls.reset(); !ls.atEnd(); ls++) { const Slicer sl(ls.position(), sliceShape); const SubLattice modelSlice(model, sl); SubLattice resultSlice(result, sl, True); if (doPadding) { pad(*resultPtr, modelSlice); } else { modelPtr = &modelSlice; resultPtr = &resultSlice; } // Do the forward transform LatticeFFT::rcfft(fftModel, *modelPtr, True, doFast_p); { // Multiply the transformed model with the transfer function IPosition tileShape(itsXfr->niceCursorShape()); const IPosition otherTileShape(fftModel.niceCursorShape()); for (uInt i = 0; i < ndim; i++) { if (tileShape(i) > otherTileShape(i)) tileShape(i) = otherTileShape(i); } TileStepper tiledNav(XFRShape, tileShape); RO_LatticeIterator::ConjugateType> xfrIter(*itsXfr, tiledNav); LatticeIterator::ConjugateType> fftModelIter(fftModel, tiledNav); for (xfrIter.reset(), fftModelIter.reset(); !fftModelIter.atEnd(); xfrIter++, fftModelIter++) { fftModelIter.rwCursor() *= xfrIter.cursor(); } } // Do the inverse transform // We have done a fft with no shift to the psf and the incoming // image to be convolved now we fft back and shift for the final // image. LatticeFFT::crfft(*resultPtr, fftModel, True, doFast_p); if (doPadding) { // Unpad the result unpad(resultSlice, *resultPtr); } // { // int kkk=0; // for (int i=0;i void LatticeConvolver:: convolve(Lattice & modelAndResult) const { convolve(modelAndResult, modelAndResult); } template void LatticeConvolver:: resize(const IPosition & modelShape, ConvEnums::ConvType type) { DebugAssert(itsXfr->ndim() == modelShape.nelements(), AipsError); itsType = type; itsModelShape = modelShape; { const IPosition newFFTShape = calcFFTShape(itsPsfShape, modelShape, itsType); if (newFFTShape == itsFFTShape) return; } // need to know the psf. if (itsCachedPsf == False) { // calculate the psf from the transfer function TempLattice psf(itsPsfShape, maxLatSize); makePsf(psf); makeXfr(psf); } else { makeXfr(*itsPsf); } } template IPosition LatticeConvolver:: shape() const { return itsModelShape; } template IPosition LatticeConvolver:: psfShape() const { return itsPsfShape; } template IPosition LatticeConvolver:: fftShape() const { return itsFFTShape; } template ConvEnums::ConvType LatticeConvolver:: type() const { return itsType; } // copy the centre portion of the input Lattice to the padded Lattice. No // assumptions are made about the padded Lattice except that it is the right // shape (including the correct number of dimensions). template void LatticeConvolver:: pad(Lattice & paddedLat, const Lattice & inLat) { paddedLat.set(T(0)); const uInt ndim = inLat.ndim(); const IPosition inLatShape = inLat.shape(); const IPosition FFTShape = paddedLat.shape(); IPosition inBlc(ndim, 0); IPosition patchShape(inLatShape); for (uInt k = 0; k < ndim; k++) { if (FFTShape(k) < inLatShape(k)) { inBlc(k) = inLatShape(k)/2 - FFTShape(k)/2; patchShape(k) = FFTShape(k); } } const Slicer inLatSlice(inBlc, patchShape); const SubLattice inLatPatch(inLat, inLatSlice); const IPosition outBlc = FFTShape/2 - patchShape/2; const Slicer paddedSlice(outBlc, patchShape); SubLattice paddedPatch(paddedLat, paddedSlice, True); paddedPatch.copyData(inLatPatch); } template void LatticeConvolver:: unpad(Lattice & result, const Lattice & paddedResult) { const IPosition resultShape = result.shape(); const IPosition inBlc = paddedResult.shape()/2 - resultShape/2; const Slicer paddedSlice(inBlc, resultShape); const SubLattice resultPatch(paddedResult, paddedSlice); result.copyData(resultPatch); } // Requires that the itsType, itsPsfShape and itsModelShape data members are // initialised correctly and will initialise the itsFFTShape, itsXfr, itsPsf & // itsCachedPsf data members. template void LatticeConvolver:: makeXfr(const Lattice & psf) { // cerr << "makeXfr" << endl; DebugAssert(itsPsfShape == psf.shape(), AipsError); itsFFTShape = calcFFTShape(itsPsfShape, itsModelShape, itsType); // for (int i=0;i::ConjugateType>(XFRShape, maxLatSize); if (itsFFTShape == itsPsfShape) { // no need to pad the psf LatticeFFT::rcfft(*itsXfr, psf, True, doFast_p); } else { // need to pad the psf TempLattice paddedPsf(itsFFTShape, maxLatSize); pad(paddedPsf, psf); LatticeFFT::rcfft(*itsXfr, paddedPsf, True, doFast_p); } } // Only cache the psf if it cannot be reconstructed from the transfer // function. if (itsFFTShape < itsPsfShape) { if(itsPsf) { delete itsPsf; itsPsf = 0; } itsPsf = new TempLattice(itsPsfShape, 1); // Prefer to put this on disk itsPsf->copyData(psf); itsCachedPsf = True; } else { if(itsPsf) { delete itsPsf; itsPsf = 0; } itsPsf = new TempLattice(); itsCachedPsf = False; } // cerr << "makeXfr" << endl; } // Construct a psf from the transfer function (itsXFR). template void LatticeConvolver:: makePsf(Lattice & psf) const { DebugAssert(itsPsfShape == psf.shape(), AipsError); if (itsFFTShape == itsPsfShape) { // If the Transfer function has not been // padded so no unpadding is necessary LatticeFFT::crfft(psf, *itsXfr, True, doFast_p); } else { // need to unpad the transfer function TempLattice paddedPsf(itsFFTShape, maxLatSize); LatticeFFT::crfft(paddedPsf, *itsXfr, True, doFast_p); unpad(psf, paddedPsf); } } // Calculate the minimum FFTShape necessary to do a convolution of the // specified type with the supplied mode and psf shapes. Will try and avoid odd // length FFT's. template IPosition LatticeConvolver:: calcFFTShape(const IPosition & psfShape, const IPosition & modelShape, ConvEnums::ConvType type) { if (type == ConvEnums::CIRCULAR) { // All the books (eg Bracewell) only define circular convolution for two // Arrays that are the same length. So I always pad the smaller one to make // it the same size as the bigger one. return max(psfShape, modelShape); } // When doing linear convolution the formulae is more complicated. In // general the shape is given by modelShape + psfShape - 1. But if we are // only to return an Array of size modelShape you can do smaller // transforms. I deduced the following formulae empirically. If the length on // any axis is one for either the model or the psf you do not need to do an // FFT along this axis. All you need to do is iterate through it hence the // FFTShape on this axis is set to one. The iteration is done in the convolve // function. IPosition FFTShape = modelShape + psfShape/2; const uInt ndim = FFTShape.nelements(); for (uInt i = 0; i < ndim; i++) { if (psfShape(i) == 1 || modelShape(i) == 1) { FFTShape(i) = 1; } else if (FFTShape(i) < psfShape(i)) { FFTShape(i) = 2 * modelShape(i); // FFTShape(i) = 2 * modelShape(i) - 1; } } return FFTShape; } template void LatticeConvolver:: setFastConvolve(){ doFast_p=True; } // Local Variables: // compile-command: "cd test; gmake OPTLIB=1 inst tLatticeConvolver" // End: } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LatticeMath/LatticeFFT.h000066400000000000000000000245521476623553700214410ustar00rootroot00000000000000//# LatticeFFT.h: Definitions for Lattice FFT functions //# Copyright (C) 1996,1997,1998,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICEFFT_H #define LATTICES_LATTICEFFT_H #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template class Lattice; // Functions for Fourier transforming Lattices // // // // // // // // // // // // // // // // class LatticeFFT { public: // 2-D in-place complex->complex FFT. Transforms over the first two // dimensions and iterates over all the others. The Lattice must have two or // more dimensions otherwise an AipsError is thrown. template static void cfft2d( Lattice & cLattice, const Bool toFrequency=True ); // N-D in-place complex->complex FFT. Only transform over selected // dimensions. Iterate over the others. whichAxes must be the same length as // the number of dimensions in the Lattice otherwise an AipsError is thrown. template static void cfft(Lattice & cLattice, const Vector & whichAxes, const Bool toFrequency=True); // Non-folded version template static void cfft0(Lattice & cLattice, const Vector & whichAxes, const Bool toFrequency=True); // N-D in-place complex->complex FFT. Transform over all axes. template static void cfft( Lattice & cLattice, const Bool toFrequency=True ); // N-D real->complex FFT. Only one half of the Hermition result is // returned. Transforms are only done on selected dimensions. The origin of // the transform is the center of the Lattice ie., [nx/2,ny/2,...] if // doShift is True. Otherwise it is the first element ie., [0,0,...] template static void rcfft( Lattice & out, const Lattice::ConjugateType> & in, const Vector & whichAxes, const Bool doShift=True, Bool doFast=False ); template static void myrcfft( Lattice & out, const Lattice::ConjugateType> & in, const Vector & whichAxes, const Bool doShift=True ); // N-D real->complex FFT. Only one half of the Hermition result is // returned. Transform over all dimensions. The origin of // the transform is the center of the Lattice ie., [nx/2,ny/2,...] if // doShift is True. Otherwise it is the first element ie., [0,0,...] template static void rcfft( Lattice & out, const Lattice::ConjugateType> & in, const Bool doShift=True, Bool doFast=False ); template static void myrcfft( Lattice & out, const Lattice::ConjugateType> & in, const Bool doShift=True ); // N-D complex->real FFT. Only one half of the Hermition input is // required. If whichAxis is specified Transforms are only done on selected // dimensions otherwise they are done on all axes. The origin of the // transform is the center of the Lattice ie., [nx/2,ny/2,...] if doShift is // True, otherwise it is the first element ie., [0,0,...] // These functions will scramble the input Lattice unless the versions // with const inputs are used. The const input versions are less efficient as // they create a temporary Lattice and copy the input data into it. // template static void crfft( Lattice::ConjugateType> & out, Lattice & in, const Vector & whichAxes, const Bool doShift=True, Bool doFast=False ); template static void crfft( Lattice::ConjugateType> & out, Lattice & in, const Bool doShift=True, Bool doFast=False ); template static void crfft( Lattice::ConjugateType> & out, const Lattice & in, const Bool doShift=True, Bool doFast=False ); // }; // implement template specializations to throw exceptions in the relevant cases. template <> inline void LatticeFFT::cfft2d(Lattice&, const Bool) { ThrowCc( String(__func__) +": This method does not support real-valued lattices" ); } template <> inline void LatticeFFT::cfft2d(Lattice&, const Bool) { ThrowCc( String(__func__) + ": This method does not support real-valued lattices" ); } template <> inline void LatticeFFT::cfft( Lattice&, const Vector&, const Bool ) { ThrowCc( String(__func__) + ": This method does not support real-valued lattices" ); } template <> inline void LatticeFFT::cfft( Lattice&, const Vector&, const Bool ) { ThrowCc( String(__func__) + ": This method does not support real-valued lattices" ); } template <> inline void LatticeFFT::rcfft( Lattice &, const Lattice &, const Vector & , const Bool, Bool ) { ThrowCc( String(__func__) + ": This is the real -> complex version, you've " "called it with the wrong parameters" ); } template <> inline void LatticeFFT::rcfft( Lattice &, const Lattice & , const Vector &, const Bool, Bool ) { ThrowCc( String(__func__) + ": This is the real -> complex version, you've " "called it with the wrong parameters" ); } template <> inline void LatticeFFT::myrcfft( Lattice &, const Lattice &, const Vector &, const Bool ) { ThrowCc( String(__func__) + ": This is the real -> complex version, you've " "called it with the wrong parameters" ); } template <> inline void LatticeFFT::myrcfft( Lattice &, const Lattice &, const Vector &, const Bool ) { ThrowCc( String(__func__) + ": This is the real -> complex version, you've " "called it with the wrong parameters" ); } template <> inline void LatticeFFT::rcfft( Lattice &, const Lattice &, const Bool, Bool ) { ThrowCc( String(__func__) + ": This is the real -> complex version, you've " "called it with the wrong parameters" ); } template <> inline void LatticeFFT::rcfft( Lattice &, const Lattice &, const Bool, Bool ) { ThrowCc( String(__func__) + ": This is the real -> complex version, you've " "called it with the wrong parameters" ); } template <> inline void LatticeFFT::myrcfft( Lattice &, const Lattice &, const Bool ) { ThrowCc( String(__func__) + ": This is the real -> complex version, you've " "called it with the wrong parameters" ); } template <> inline void LatticeFFT::myrcfft( Lattice &, const Lattice &, const Bool ) { ThrowCc( String(__func__) + ": This is the real -> complex version, you've " "called it with the wrong parameters" ); } template <> inline void LatticeFFT::crfft( Lattice&, Lattice &, const Vector &, const Bool, Bool ) { ThrowCc( String(__func__) + ": This is the complex -> real version, you've " "called it with the wrong parameters" ); } template <> inline void LatticeFFT::crfft( Lattice &, Lattice &, const Vector &, const Bool, Bool ) { ThrowCc( String(__func__) + ": This is the complex -> real version, you've " "called it with the wrong parameters" ); } template <> inline void LatticeFFT::crfft( Lattice &, Lattice &, const Bool, Bool ) { ThrowCc( String(__func__) + ": This is the complex -> real version, you've " "called it with the wrong parameters" ); } template <> inline void LatticeFFT::crfft( Lattice &, Lattice &, const Bool, Bool ) { ThrowCc( String(__func__) + ": This is the complex -> real version, you've " "called it with the wrong parameters" ); } template <> inline void LatticeFFT::crfft( Lattice &, const Lattice &, const Bool, Bool ) { ThrowCc( String(__func__) + ": This is the complex -> real version, you've " "called it with the wrong parameters" ); } template <> inline void LatticeFFT::crfft( Lattice &, const Lattice &, const Bool, Bool ) { ThrowCc( String(__func__) + ": This is the complex -> real version, you've " "called it with the wrong parameters" ); } } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include "LatticeFFT.tcc" #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/LatticeMath/LatticeFFT.tcc000066400000000000000000000344431476623553700217630ustar00rootroot00000000000000#ifndef LATTICES_LATTICEFFT_TCC #define LATTICES_LATTICEFFT_TCC // -*- C++ -*- //# LatticeFFT.cc: functions for doing FFT's on Lattices. //# Copyright (C) 1996,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template void LatticeFFT::cfft2d( Lattice& cLattice, const Bool toFrequency) { const uInt ndim = cLattice.ndim(); DebugAssert(ndim > 1, AipsError); const IPosition& latticeShape = cLattice.shape(); const uInt maxPixels = cLattice.advisedMaxPixels(); IPosition slabShape = cLattice.niceCursorShape(maxPixels); const uInt nx = slabShape(0) = latticeShape(0); const uInt ny = slabShape(1) = latticeShape(1); // use 1/8 of memory for FFT of a plane at most //Long cacheSize = (HostInfo::memoryTotal()/(sizeof(Complex)*8))*1024; //use memory Free and use a quarter of that Long cacheSize = (HostInfo::memoryFree()/(sizeof(ComplexType)*4))*1024; // For small transforms, we do everything in one plane if (((Long)(nx)*(Long)(ny)) <= cacheSize) { const IPosition cursorShape(2, nx, ny); LatticeStepper ls(latticeShape, cursorShape); LatticeIterator li(cLattice, ls); FFTServer::ConjugateType,ComplexType> ffts(cursorShape); for (li.reset(); !li.atEnd(); li++) { ffts.fft(li.rwMatrixCursor(), toFrequency); } } // For large transforms , we do line by line FFT's else { Vector whichAxes(ndim, False); whichAxes(0) = whichAxes(1) = True; LatticeFFT::cfft(cLattice, whichAxes, toFrequency); } } template void LatticeFFT::cfft(Lattice& cLattice, const Vector& whichAxes, const Bool toFrequency) { const uInt ndim = cLattice.ndim(); DebugAssert(ndim > 0, AipsError); DebugAssert(ndim == whichAxes.nelements(), AipsError); FFTServer::ConjugateType,ComplexType> ffts; const IPosition latticeShape = cLattice.shape(); const IPosition tileShape = cLattice.niceCursorShape(); for (uInt dim = 0; dim < ndim; dim++) { if (whichAxes(dim) == True) { TiledLineStepper ts(latticeShape, tileShape, dim); LatticeIterator li(cLattice, ts); for (li.reset(); !li.atEnd(); li++) { ffts.fft(li.rwVectorCursor(), toFrequency); } } } } template void LatticeFFT::cfft0(Lattice& cLattice, const Vector& whichAxes, const Bool toFrequency) { const uInt ndim = cLattice.ndim(); DebugAssert(ndim > 0, AipsError); DebugAssert(ndim == whichAxes.nelements(), AipsError); FFTServer::ConjugateType,ComplexType> ffts; const IPosition latticeShape = cLattice.shape(); const IPosition tileShape = cLattice.niceCursorShape(); for (uInt dim = 0; dim < ndim; dim++) { if (whichAxes(dim) == True) { TiledLineStepper ts(latticeShape, tileShape, dim); LatticeIterator li(cLattice, ts); for (li.reset(); !li.atEnd(); li++) { ffts.fft0(li.rwVectorCursor(), toFrequency); } } } } template void LatticeFFT::cfft( Lattice& cLattice, const Bool toFrequency ) { const Vector whichAxes(cLattice.ndim(), True); LatticeFFT::cfft(cLattice, whichAxes, toFrequency); } template void LatticeFFT::rcfft( Lattice& out, const Lattice::ConjugateType>& in, const Vector& whichAxes, const Bool doShift, Bool doFast){ const uInt ndim = in.ndim(); DebugAssert(ndim > 0, AipsError); DebugAssert(ndim == whichAxes.nelements(), AipsError); // find the required shape of the output Array const IPosition inShape = in.shape(); IPosition outShape = in.shape(); uInt i = 0, firstAxis = ndim; while (i < ndim && firstAxis == ndim) { if (whichAxes(i) == True) firstAxis = i; i++; } DebugAssert(firstAxis < ndim, AipsError); // At least one axis must be given outShape(firstAxis) = (outShape(firstAxis)+2)/2; DebugAssert(outShape.isEqual(out.shape()), AipsError); const IPosition tileShape = out.niceCursorShape(); TempLattice::ConjugateType> inlocal( TiledShape(in.shape(), tileShape) ); inlocal.put(in.get()); FFTServer::ConjugateType,ComplexType> ffts; { for (uInt dim = 0; dim < ndim; dim++) { if (whichAxes(dim) == True) { if (dim == firstAxis) { if (inShape(dim) != 1) { // Do real->complex Transforms LatticeIterator::ConjugateType> inIter(inlocal, TiledLineStepper(inShape, tileShape,dim)); LatticeIterator outIter(out, TiledLineStepper(outShape, tileShape,dim)); for (inIter.reset(), outIter.reset(); !inIter.atEnd() && !outIter.atEnd(); inIter++, outIter++) { if (doShift) { if(doFast){ // ffts.flip(inIter.rwVectorCursor(), True, False); ffts.fft0(outIter.woVectorCursor(), inIter.vectorCursor()); } else{ ffts.fft(outIter.woVectorCursor(), inIter.vectorCursor()); } } else { ffts.fft0(outIter.woVectorCursor(), inIter.vectorCursor()); } } } else { // just copy the data out.copyData(LatticeExpr(in)); } } else { // Do complex->complex transforms if (inShape(dim) != 1) { LatticeIterator iter(out, TiledLineStepper(outShape, tileShape, dim)); for (iter.reset(); !iter.atEnd(); iter++) { if (doShift) { if(doFast){ ffts.fft0(iter.rwVectorCursor(),True); } else{ ffts.fft(iter.rwVectorCursor(),True); } } else { ffts.fft0(iter.rwVectorCursor(), True); } } } } } } } } // // ----------------MYRCFFT-------------------------------------- // template void LatticeFFT::myrcfft( Lattice& out, const Lattice::ConjugateType>& in, const Vector& whichAxes, const Bool doShift){ // cerr << "####myrcfft" << endl; const uInt ndim = in.ndim(); DebugAssert(ndim > 0, AipsError); DebugAssert(ndim == whichAxes.nelements(), AipsError); // find the required shape of the output Array const IPosition inShape = in.shape(); IPosition outShape = in.shape(); uInt i = 0, firstAxis = ndim; while (i < ndim && firstAxis == ndim) { if (whichAxes(i) == True) firstAxis = i; i++; } DebugAssert(firstAxis < ndim, AipsError); // At least one axis must be given outShape(firstAxis) = (outShape(firstAxis)+2)/2; DebugAssert(outShape.isEqual(out.shape()), AipsError); const IPosition tileShape = out.niceCursorShape(); FFTServer::ConjugateType,ComplexType> ffts; { for (uInt dim = 0; dim < ndim; dim++) { if (whichAxes(dim) == True) { if (dim == firstAxis) { if (inShape(dim) != 1) { // Do real->complex Transforms RO_LatticeIterator::ConjugateType> inIter(in, TiledLineStepper(inShape,tileShape,dim)); LatticeIterator outIter(out, TiledLineStepper(outShape,tileShape,dim)); for (inIter.reset(), outIter.reset(); !inIter.atEnd() && !outIter.atEnd(); inIter++, outIter++) { if (doShift) { // ffts.myfft(outIter.woVectorCursor(), inIter.vectorCursor()); ffts.flip((Vector::ConjugateType> &)inIter.vectorCursor(),True,False); ffts.fft0(outIter.woVectorCursor(), inIter.vectorCursor()); } else { ffts.fft0(outIter.woVectorCursor(), inIter.vectorCursor()); } } } else { // just copy the data out.copyData(LatticeExpr(in)); } } else { // Do complex->complex transforms if (inShape(dim) != 1) { LatticeIterator iter(out, TiledLineStepper(outShape, tileShape, dim)); for (iter.reset(); !iter.atEnd(); iter++) { if (doShift) { // ffts.fft(iter.rwVectorCursor(), 1, True); ffts.flip(iter.rwVectorCursor(),True,False); ffts.fft0(iter.rwVectorCursor(),True); } else { ffts.fft0(iter.rwVectorCursor(), True); } } } } } } } } // //------------------------------------------------------------------------- // template void LatticeFFT::rcfft( Lattice& out, const Lattice::ConjugateType>& in, const Bool doShift, Bool doFast){ const Vector whichAxes(in.ndim(), True); LatticeFFT::rcfft(out, in, whichAxes, doShift, doFast); } template void LatticeFFT::myrcfft( Lattice& out, const Lattice::ConjugateType>& in, const Bool doShift){ const Vector whichAxes(in.ndim(), True); LatticeFFT::myrcfft(out, in, whichAxes, doShift); } template void LatticeFFT::crfft( Lattice::ConjugateType>& out, Lattice& in, const Vector& whichAxes, const Bool doShift, Bool doFast){ const uInt ndim = in.ndim(); DebugAssert(ndim > 0, AipsError); DebugAssert(ndim == whichAxes.nelements(), AipsError); // find the required shape of the output Array const IPosition inShape = in.shape(); IPosition outShape = in.shape(); uInt i = 0, firstAxis = ndim; while (i < ndim && firstAxis == ndim) { if (whichAxes(i) == True) firstAxis = i; i++; } DebugAssert(firstAxis < ndim, AipsError); // At least one axis must be given outShape(firstAxis) = outShape(firstAxis)*2 - 2; if (!outShape.isEqual(out.shape())) outShape(firstAxis) += 1; DebugAssert(outShape.isEqual(out.shape()), AipsError); // if (outShape.product() == 1) { // const IPosition origin(ndim, 0); // const Complex val = in.getAt(origin); // out.set(val.re); // return; // } const IPosition tileShape = in.niceCursorShape(); FFTServer::ConjugateType,ComplexType> ffts; uInt dim = ndim; while (dim != 0) { dim--; if (whichAxes(dim) == True) { if (dim != firstAxis) { // Do complex->complex Transforms if (inShape(dim) != 1) { // no need to do anything unless len > 1 LatticeIterator iter(in, TiledLineStepper(inShape, tileShape, dim)); for (iter.reset(); !iter.atEnd(); iter++) { if (doShift) { if(doFast){ ffts.fft0(iter.rwVectorCursor(), False); ffts.flip(iter.rwVectorCursor(), False, False); } else{ // ffts.fft(iter.rwVectorCursor(), 2, False); ffts.fft(iter.rwVectorCursor(),False); } } else { ffts.fft0(iter.rwVectorCursor(), False); } } } } else { // the first axis is treated specially if (inShape(dim) != 1) { // Do complex->real transforms RO_LatticeIterator inIter(in, TiledLineStepper(inShape, tileShape, dim)); LatticeIterator::ConjugateType> outIter(out, TiledLineStepper(outShape, tileShape, dim)); for (inIter.reset(), outIter.reset(); !inIter.atEnd() && !outIter.atEnd(); inIter++, outIter++) { if (doShift) { if(doFast){ ffts.fft0(outIter.woVectorCursor(), inIter.vectorCursor()); ffts.flip(outIter.rwVectorCursor(), False, False); }else{ ffts.fft(outIter.woVectorCursor(), inIter.vectorCursor()); } } else { ffts.fft0(outIter.woVectorCursor(), inIter.vectorCursor()); } } } else { // just copy the data truncating the imaginary parts. out.copyData(LatticeExpr::ConjugateType>(real(in))); } } } } } template void LatticeFFT::crfft( Lattice::ConjugateType>& out, Lattice& in, const Bool doShift, Bool doFast){ const Vector whichAxes(in.ndim(), True); LatticeFFT::crfft(out, in, whichAxes, doShift, doFast); } template void LatticeFFT::crfft( Lattice::ConjugateType>& out, const Lattice& in, const Bool doShift, Bool doFast){ TempLattice inCopy(in.shape()); inCopy.copyData(in); LatticeFFT::crfft(out, inCopy, doShift, doFast); } // Local Variables: // compile-command: "gmake OPTLIB=1 LatticeFFT" // End: } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LatticeMath/LatticeFit.cc000066400000000000000000000223171476623553700216770ustar00rootroot00000000000000//# LatticeFit.cc: Fit every line of pixels parallel to any axis in a Lattice. //# Copyright (C) 1994,1995,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN uInt LatticeFit::fitProfiles (Lattice &outImage, Vector &fittedParameters, LinearFit &fitter, const Lattice &inImage, uInt whichAxis, const Vector &fitMask, Bool returnResiduals) { IPosition outShape = outImage.shape(); IPosition inShape = inImage.shape(); if (outShape != inShape) { throw(AipsError("::baselineFit - outImage.shape() != inImage.shape()")); } if (whichAxis >= outImage.ndim()) { throw(AipsError("::baselineFit - whichAxis does not exist in image")); } if (Int(fitMask.nelements()) != outShape(whichAxis)) { throw(AipsError("::baselineFit - improperly specified mask")); } // These selections etc will get easier when masked arrays are available. Int nPointsToFit = fitMask.nelements(); // Set up x and sigma Vector x(nPointsToFit); Vector y(nPointsToFit); Vector sigma(nPointsToFit); Int count, i; // data points with sigma = -1.0 are ignored in fitting for (count = 0, i = 0; i < nPointsToFit; i++) { if (fitMask(i)) { x(i) = count; count++; sigma(i) = 1.0; } else { sigma(i) = -1.0; } } // For simplicity this now just iterates through the cube "line by line". // It might be considerably more efficient to iterate through plane by // plane though (earlier versions of the code did this, however it has // been changed to get it working quickly). IPosition cursorShape(outShape.nelements()); cursorShape = 1; cursorShape(whichAxis) = inShape(whichAxis); LatticeIterator outIter(outImage, cursorShape); RO_LatticeIterator inIter(inImage, cursorShape); Vector xall(inShape(whichAxis)); indgen(xall); Vector solution(xall.nelements()); Vector yall(xall.nelements()); count = 0; fittedParameters.resize(0); for (inIter.reset(), outIter.reset(); ! inIter.atEnd(); inIter++, outIter++, count++) { yall = inIter.vectorCursor(); fittedParameters=fitter.fit(x, yall, sigma); for (uInt ii=0; ii < solution.nelements(); ii++) { solution(ii) = (*fitter.fittedFunction())(xall(ii)).value(); } if (returnResiduals) { outIter.woVectorCursor() = (yall - solution); } else { outIter.woVectorCursor() = solution; } } return count; } uInt LatticeFit::fitProfiles (MaskedLattice* pFit, MaskedLattice* pResid, MaskedLattice& in, Lattice* pSigma, LinearFit& fitter, uInt axis, Bool showProgress) { LogIO os(LogOrigin("LatticeFit", "fitProfiles")); // IPosition inShape = in.shape(); if (pFit!=0) { AlwaysAssert(inShape.isEqual(pFit->shape()), AipsError); } if (pResid!=0) { AlwaysAssert(inShape.isEqual(pResid->shape()), AipsError); } // Setup iterators IPosition inTileShape = in.niceCursorShape(); TiledLineStepper stepper (in.shape(), inTileShape, axis); RO_MaskedLatticeIterator inIter(in, stepper); // LatticeIterator* pFitIter = 0; LatticeIterator* pFitMaskIter = 0; LatticeIterator* pResidIter = 0; LatticeIterator* pResidMaskIter = 0; // if (pFit) { pFitIter = new LatticeIterator(*pFit, stepper); if (pFit->hasPixelMask()) { pFitMaskIter = new LatticeIterator(pFit->pixelMask(), stepper); } } if (pResid) { pResidIter = new LatticeIterator(*pResid, stepper); if (pResid->hasPixelMask()) { pResidMaskIter = new LatticeIterator(pResid->pixelMask(), stepper); } } // Int nProfiles = inShape.product()/inIter.vectorCursor().nelements(); ProgressMeter* pProgress = 0; Double meterValue = 0.0; if (showProgress) { pProgress = new ProgressMeter(0.0, Double(nProfiles), "Profile fitting", "Profiles fitted", "", "", True, max(1,Int(nProfiles/20))); } // const uInt n = inShape(axis); Vector x(n); Vector y(n); for (uInt i=0; i::DiffType, FunctionTraits::DiffType>* pFunc = fitter.fittedFunction(); // Vector inMask; Vector inSigma; Bool ok = False; uInt nFail = 0; // while (!inIter.atEnd()) { // Get data and mask (reflects pixelMask and region mask of SubImage) const Vector& data = inIter.vectorCursor(); inMask = inIter.getMask(True); // ok = True; Vector sol; if (pSigma) { inSigma = pSigma->getSlice(inIter.position(), inIter.cursorShape(), True); try { sol.assign(fitter.fit(x, data, inSigma, &inMask)); } catch (std::exception& x) { ok = False; } } else { try { sol.assign(fitter.fit(x, data, &inMask)); } catch (std::exception& x) { ok = False; } } for (Vector::const_iterator iter=sol.begin(); iter!=sol.end(); iter++) { if (isNaN(*iter)) { ok = False; } } // Evaluate if (ok) { if (pFit) { for (uInt i=0; irwVectorCursor()[i] = (*pFunc)(x(i)).value(); } } if (pFitMaskIter) { pFitMaskIter->rwVectorCursor() = inMask; } if (pResid) { if (pFit) { pResidIter->rwVectorCursor() = data - pFitIter->rwVectorCursor(); } else { for (uInt i=0; irwVectorCursor()[i] = data[i] - (*pFunc)(x(i)).value(); } } } if (pResidMaskIter) { pResidMaskIter->rwVectorCursor() = inMask; } } else { nFail++; if (pFit) { pFitIter->rwVectorCursor() = 0.0; } if (pFitMaskIter) { pFitMaskIter->rwVectorCursor() = False; } if (pResid) { pResidIter->rwVectorCursor() = 0.0; } if (pResidMaskIter) { pResidMaskIter->rwVectorCursor() = False; } } // inIter++; if (pFitIter) (*pFitIter)++; if (pResidIter) (*pResidIter)++; if (pFitMaskIter) (*pFitMaskIter)++; if (pResidMaskIter) (*pResidMaskIter)++; // if (pProgress) { meterValue += 1.0; pProgress->update(meterValue); } } // if (pFitIter) delete pFitIter; if (pResidIter) delete pResidIter; if (pFitMaskIter) delete pFitMaskIter; if (pResidMaskIter) delete pResidMaskIter; if (pProgress) delete pProgress; // os << "Number of profiles = " << nProfiles << LogIO::POST; os << "Number of good fits = " << nProfiles - nFail << LogIO::POST; os << "Number of failed fits = " << nFail << LogIO::POST; // return nFail; } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LatticeMath/LatticeFit.h000066400000000000000000000111641476623553700215370ustar00rootroot00000000000000//# LatticeFit.h: Fit every line of pixels parallel to any axis in a Lattice. //# Copyright (C) 1994,1995,1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICEFIT_H #define LATTICES_LATTICEFIT_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Fit every line of pixels parallel to any axis in a Lattice. // // // //
      • LinearFit //
      • Lattice // // // // For every line in the lattice parallel to axis number whichAxis // (often axis number 2, typically the frequency axis in a spectral line cube) // independently fit the functions in fitter at the positions where // fitMask is true. // // // // Suppose one wanted to subtract a linear polynomial from every spectrum (3d // axis) in an image. One could do this as follows: // // Image myImage("myimage"); // Get the image // uInt nchan = myImage.shape()(2); // 0 relative axis number // // Set up the fitter // Polynomial > linear(1); // LinearFitSVD fitter; // fitter.setFunction(linear); // Vector fittedParameters, // // // Set up a mask indicating what channels we want to fit over. We want // // to fit over all channels. // Vector fitMask(nchan); fitMask = True; // // // Do the fit. True means subtract the fit from the model. In this case, // // We overwrite the input with the output. // fitProfiles (myImage, fittedParameters,fitter, myImage, 2, fitMask, True); // // // // // Baseline fitting/continuum subtraction are important functions. This // function essentially implements the IMLIN algorithm. // // // //
      • Save the model parameters in an (optional) other lattice. //
      • Use logging classes, rather than the raw GlishSysEventSource. //
      • Allow per-pixel weights. //
      • Allow non-linear as well as linear LSQ fits. // // fitting functions class LatticeFit { public: // Fit baseline to lattice. Presently the fit parameters, other than the last // one(s) in fitter, are lost. If returnResiduals is True, // return data-fit, otherwise return the fit. For baseline and continuum // subtraction, returnResiduals would normally be True. static uInt fitProfiles (Lattice& outImage, Vector& fittedParameters, LinearFit& fitter, const Lattice& inImage, uInt whichAxis, const Vector& fitMask, Bool returnResiduals); // Fit baseline to MaskedLattice. Fit and residuals can be optionally // written (leave pointers at zero to not write out these lattices) // You can optionally specify a weights lattice (1.0 if not given). static uInt fitProfiles (MaskedLattice* pOutFit, MaskedLattice* pOutResid, MaskedLattice& in, Lattice* pSigma, LinearFit& fitter, uInt axis, Bool showProgress=False); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LatticeMath/LatticeFractile.h000066400000000000000000000161151476623553700225470ustar00rootroot00000000000000//# LatticeFractile.cc: Static functions to get median and fractiles //# Copyright (C) 1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICEFRACTILE_H #define LATTICES_LATTICEFRACTILE_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Lattice; template class MaskedLattice; template class Block; // // Static functions to get median and fractiles of a lattice // // // // // //
      • Lattice // // // This class contains a few static functions to find 1 or 2 fractiles // in a lattice. They are primarily used by the LEL classes, but can // also be used standalone. //
        // A fractile is the same as a percentile be it that it is given as a // fraction instead of a percentage. A fraction of 0.5 yields the median. //
        // When the lattice has a mask, only the masked-on elements are taken into // account. If all elements are masked_off, an empty Vector is returned // indicating that no fractiles were found. //

        // The algorithm used depends on the size of the lattice. // Smallish lattices (i.e. not exceeding the argument smallSize) // are handled in one pass im memory. // For bigger lattices a multi-pass algorithm is used. First the // lattices is binned. Thereafter the algorithm continues with the // elements of the bins containing the fractiles. This continues // until the number of elements left is less than smallSize. // Typically only 2 passes are needed for a big image. //
        // The algorithm is robust and takes possible rounding errors into account. // It also takes into account that the lattice can contain many equal values. // // // Separated from file LELFunction.h to make it more commonly usable // and to make the source files more readable. // //# //# template class LatticeFractile { public: // Determine the fractile of the given lattice. It returns the value // of the lattice at the given fraction. A fraction of 0.5 returns // the median. If the lattice has an even number of elements and if // the lattice is small enough (< 100 elements), the median is the // mean of the 2 middle elements. //
        If the lattice is masked, only masked-on elements are taken // into account. //
        If the lattice is large, successive histograms are made until // smallSize elements are left. Thereafter an in-memory // algorithm will be used to finish. // The number of passes made over the data is undetermined, but // a typical number is 2 passes. //
        Normally a vector with 1 element is returned. // If the lattice has no masked-on elements, an empty vector is returned. // static Vector unmaskedFractile (const Lattice& lattice, Float fraction, uInt smallSize = 4096*4096); static Vector maskedFractile (const MaskedLattice& lattice, Float fraction, uInt smallSize = 4096*4096); // // Determine the values of the 2 elements at the given fractiles. // Thus left=0.25; right=0.75 gives the quartiles of the lattice. //
        If the lattice is masked, onlu masked-on elements are taken // into account. //
        If the lattice is large, successive histograms are made until // smallSize elements are left. Thereafter an in-memory // algorithm will be used to finish. // The number of passes made over the data is undetermined, but // a typical number is 2 passes. //
        Normally a vector with 2 elements is returned. // If the lattice has no masked-on elements, an empty vector is returned. // static Vector unmaskedFractiles (const Lattice& lattice, Float left, Float right, uInt smallSize = 4096*4096); static Vector maskedFractiles (const MaskedLattice& lattice, Float left, Float right, uInt smallSize = 4096*4096); // private: // Determine the fractile for a small masked lattice. static Vector smallMaskedFractile (const MaskedLattice& lattice, Float fraction); // Determine the fractiles for a small masked lattice. static Vector smallMaskedFractiles (const MaskedLattice& lattice, Float left, Float right); // Calculate the first histogram (with 10000 bins). // Also calculate the minimum and maximum. It returns the number // of masked-on values. Masked-off values are ignored. // static uInt maskedHistogram (T& stv, T& endv, T& minv, T& maxv, Block& hist, Block& boundaries, const MaskedLattice& lattice); static void unmaskedHistogram (T& stv, T& endv, T& minv, T& maxv, Block& hist, Block& boundaries, const Lattice& lattice); // // Helper function which determines which bin in the histogram // contains the passed index. // On input fractileInx gives the index of the fractile in the entire // histogram. // On output stv and endv are set to the boundaries of the bin containing // the index and fractileInx is set to the index in that bin. // The nr of values in that bin is returned as the function value. // minv and maxv are used as the outer limits, thus the first bin extends // to minv and the last bin to maxv. // If the bins are getting too small (i.e. if stv is nearly endv), 0 is // returned. In that case endv contains the fractile. static uInt findBin (uInt& fractileInx, T& stv, T& endv, T minv, T maxv, const Block& hist, const Block& boundaries); }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/LatticeMath/LatticeFractile.tcc000066400000000000000000000762221476623553700230760ustar00rootroot00000000000000//# LatticeFractile.cc: Static functions to get fractiles //# Copyright (C) 1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICEFRACTILE_TCC #define LATTICES_LATTICEFRACTILE_TCC #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template uInt LatticeFractile::findBin (uInt& fractileInx, T& stv, T& endv, T minv, T maxv, const Block& hist, const Block& boundaries) { // Return 0 if minimum and maximum value are about equal. if (near (minv, maxv)) { endv = (minv+maxv)/2; return 0; } uInt foundBin = 0; uInt ndone = 0; const uInt nbins = hist.nelements()-1; // First determine the index of the bin containing the specified index. // If not found (rounding problems are possible) 0 is returned. while (ndone <= fractileInx) { if (foundBin == nbins) { endv = maxv; return 0; } ndone += hist[foundBin++]; } foundBin--; // Now foundBin is the bin containing the requested index. // The nr of values in there have to be examined again. // Determine the offset of the fractile in the bin. // The start/end values are reset to the boundaries of this bin. uInt ntodo = hist[foundBin]; ndone -= ntodo; fractileInx -= ndone; stv = boundaries[foundBin]; endv = boundaries[foundBin+1]; if (foundBin == 0 || stv < minv) { stv = minv; } if (foundBin == nbins-1 || endv > maxv) { endv = maxv; } // Return 0 if bin gets too narrow. if (near (stv, endv)) { endv = (stv+endv)/2; ntodo = 0; } return ntodo; } template void LatticeFractile::unmaskedHistogram (T& stv, T& endv, T& minv, T& maxv, Block& hist, Block& boundaries, const Lattice& lattice) { AlwaysAssert (hist.nelements() == boundaries.nelements(), AipsError); // Find number of bins (last one is for extraneous values). // Scale between -50 and +50 (which is usually okay for // radio-astronomical images, both for the image itself and for // difference of image with median or so). // It is only a first guess, so nothing goes wrong if grossly incorrect. // It may only result in one more iteration. const uInt nbins = hist.nelements() - 1; T step = 2*50./nbins; minv = 0; maxv = 0; for (uInt i=0; i<=nbins; ++i) { boundaries[i] = i*step - 50.; } stv = boundaries[0]; endv = boundaries[nbins]; Bool firstTime = True; // Iterate through the lattice. RO_LatticeIterator iter(lattice); while (! iter.atEnd()) { Bool delData; const Array& array = iter.cursor(); const T* dataPtr = array.getStorage (delData); uInt n = array.nelements(); if (firstTime) { firstTime = False; minv = dataPtr[0]; maxv = dataPtr[0]; } for (uInt i=0; i maxv) { maxv = dataPtr[i]; } Int bin = Int((dataPtr[i] - stv)/step); if (bin < 0) { hist[0]++; } else if (bin >= Int(nbins)) { hist[nbins-1]++; } else { if (dataPtr[i] < boundaries[bin] && bin > 0) { bin--; } else if (dataPtr[i] >= boundaries[bin+1]) { bin++; } hist[bin]++; } } array.freeStorage (dataPtr, delData); iter++; } } template uInt LatticeFractile::maskedHistogram (T& stv, T& endv, T& minv, T& maxv, Block& hist, Block& boundaries, const MaskedLattice& lattice) { AlwaysAssert (hist.nelements() == boundaries.nelements(), AipsError); uInt ntodo = 0; // Find number of bins (last one is for extraneous values). // Scale between -50 and +50 (which is usually okay for // radio-astronomical images, both for the image itself and for // difference of image with median or so). // It is only a first guess, so nothing goes wrong if grossly incorrect. // It may only result in one more iteration. const uInt nbins = hist.nelements() - 1; T step = 2*50./nbins; minv = 0; maxv = 0; for (uInt i=0; i<=nbins; ++i) { boundaries[i] = i*step - 50.; } stv = boundaries[0]; endv = boundaries[nbins]; Bool firstTime = True; // Iterate through the lattice. COWPtr> mask; RO_MaskedLatticeIterator iter(lattice); while (! iter.atEnd()) { Bool delData, delMask; const Array& array = iter.cursor(); iter.getMask (mask); const Bool* maskPtr = mask->getStorage (delMask); const T* dataPtr = array.getStorage (delData); uInt n = array.nelements(); for (uInt i=0; i maxv) { maxv = dataPtr[i]; } } Int bin = Int((dataPtr[i] - stv)/step); if (bin < 0) { hist[0]++; } else if (bin >= Int(nbins)) { hist[nbins-1]++; } else { if (dataPtr[i] < boundaries[bin] && bin > 0 ) { bin--; } else if (dataPtr[i] >= boundaries[bin+1] && bin < Int(nbins)-1) { bin++; } hist[bin]++; } } } array.freeStorage (dataPtr, delData); mask->freeStorage (maskPtr, delMask); iter++; } return ntodo; } template Vector LatticeFractile::unmaskedFractile (const Lattice& lattice, Float fraction, uInt smallSize) { AlwaysAssert (fraction >= 0 && fraction <= 1, AipsError); // Determine the number of elements in the lattice. // If empty, return empty vector. // If small enough, we read them all and do it in memory. uInt ntodo = lattice.shape().product(); if (ntodo == 0) { return Vector(); } Vector result(1); if (ntodo <= smallSize) { if (fraction == 0.5) { result(0) = median (lattice.get()); } else { result(0) = fractile (lattice.get(), fraction); } return result; } // Bad luck. We have to do some more work. // Do a first binning while determining min/max at the same time. // Hopefully the start and end values make some sense. // Make the block 1 element larger, because possible roundoff errors // could result in a binnr just beyond the end. const uInt nbins = 10000; Block hist(nbins+1, 0u); Block boundaries(nbins+1); T stv, endv, minv, maxv; unmaskedHistogram (stv, endv, minv, maxv, hist, boundaries, lattice); // The index of the fractile in the lattice is the middle one. // In case of an even nr of elements, it is the first one of the // two middle ones. uInt fractileInx = uInt(fraction * (ntodo-1)); // Iterate until the bin containing the fractile does not // contain too many values anymore. RO_LatticeIterator iter(lattice); while (True) { // Determine which bin contains the fractile and update the various values. // On return fractileInx,stv,endv form the basis of the new histogram. ntodo = findBin (fractileInx, stv, endv, minv, maxv, hist, boundaries); // If only a 'few' more points to do, stop making histograms. // Exit if nothing left to do. if (ntodo <= smallSize) { if (ntodo == 0) { result(0) = endv; return result; } break; } // Histogram the fractile bin with a much smaller bin size. // Determine the min and max of the remaining values. minv = endv; maxv = stv; hist = 0; T step = (endv - stv) / nbins; for (uInt i=0; i<=nbins; i++) { boundaries[i] = stv + i*step; } uInt ndone = 0; iter.reset(); while (! iter.atEnd() && ndone& array = iter.cursor(); Bool delData; const T* dataPtr = array.getStorage (delData); uInt n = array.nelements(); for (uInt i=0; i= stv && dataPtr[i] < endv) { Int bin = Int((dataPtr[i] - stv) / step); // Due to rounding the bin number might get one too low or high. if (dataPtr[i] < boundaries[bin]) { bin--; } else if (dataPtr[i] >= boundaries[bin+1]) { bin++; } hist[bin]++; if (dataPtr[i] < minv) { minv = dataPtr[i]; } if (dataPtr[i] > maxv) { maxv = dataPtr[i]; } ndone++; } } array.freeStorage (dataPtr, delData); iter++; } // In principle the last bin should be empty, but roundoff errors // might have put a few in there. So add them to previous one. hist[nbins-1] += hist[nbins]; } // There are only a 'few' points left. // So read them all in and determine the fractileInx'th-largest. // Again, due to rounding we might find a few elements more or less. // So take care that the receiving block is not exceeded and that // the number of elements found are used in kthLargest. // Note it also makes sense to stop the iteration when we found all // elements. It may save a few reads from the lattice. Block tmp(ntodo); T* tmpPtr = tmp.storage(); uInt ndone = 0; iter.reset(); while (! iter.atEnd() && ndone& array = iter.cursor(); Bool delData; const T* dataPtr = array.getStorage (delData); uInt n = array.nelements(); for (uInt i=0; i= stv && dataPtr[i] < endv) { tmpPtr[ndone++] = dataPtr[i]; if (ndone == ntodo) { break; } } } array.freeStorage (dataPtr, delData); iter++; } // By rounding it is possible that not enough elements were found. // In that case return the middle of the (very small) interval. if (fractileInx >= ndone) { result(0) = (stv+endv)/2; } else { result(0) = GenSort::kthLargest (tmp.storage(), ndone, fractileInx); } return result; } template Vector LatticeFractile::maskedFractile (const MaskedLattice& lattice, Float fraction, uInt smallSize) { AlwaysAssert (fraction >= 0 && fraction <= 1, AipsError); // If unmasked, a simpler way can be used. if (! lattice.isMasked()) { return unmaskedFractile (lattice, fraction, smallSize); } // Determine the number of elements in the lattice. // If small enough, we read them all and do it in memory. uInt ntodo = lattice.shape().product(); if (ntodo <= smallSize) { return smallMaskedFractile (lattice, fraction); } Vector result(1); // Bad luck. We have to do some more work. // Do a first binning while determining min/max at the same time. // Hopefully the start and end values make some sense. // Make the block 1 element larger, because possible roundoff errors // could result in a binnr just beyond the end. const uInt nbins = 10000; Block hist(nbins+1, 0u); Block boundaries(nbins+1); T stv, endv, minv, maxv; ntodo = maskedHistogram (stv, endv, minv, maxv, hist, boundaries, lattice); if (ntodo == 0) { return Vector(); } // The index of the fractile in the lattice is the middle one. // In case of an even nr of elements, it is the first one of the // two middle ones. uInt fractileInx = uInt(fraction * (ntodo-1)); // Iterate until the bin containing the fractile does not // contain too many values anymore. COWPtr> mask; RO_MaskedLatticeIterator iter(lattice); while (True) { // Determine which bin contains the fractile and update the various values. // On return fractileInx,stv,endv form the basis of the new histogram. ntodo = findBin (fractileInx, stv, endv, minv, maxv, hist, boundaries); // If only a 'few' more points to do, stop making histograms. // Exit if nothing left to do. if (ntodo <= smallSize) { if (ntodo == 0) { result(0) = endv; return result; } break; } // Histogram the fractile bin with a much smaller bin size. // Determine the min and max of the remaining values. minv = endv; maxv = stv; hist = 0; T step = (endv - stv) / nbins; for (uInt i=0; i<=nbins; i++) { boundaries[i] = stv + i*step; } uInt ndone = 0; iter.reset(); while (! iter.atEnd() && ndone& array = iter.cursor(); iter.getMask (mask); const Bool* maskPtr = mask->getStorage (delMask); const T* dataPtr = array.getStorage (delData); uInt n = array.nelements(); for (uInt i=0; i= stv && dataPtr[i] < endv) { Int bin = Int((dataPtr[i] - stv) / step); // Due to rounding the bin number might get one too low or high. if (dataPtr[i] < boundaries[bin]) { bin--; } else if (dataPtr[i] >= boundaries[bin+1]) { bin++; } hist[bin]++; if (dataPtr[i] < minv) { minv = dataPtr[i]; } if (dataPtr[i] > maxv) { maxv = dataPtr[i]; } ndone++; } } array.freeStorage (dataPtr, delData); mask->freeStorage (maskPtr, delMask); iter++; } // In principle the last bin should be empty, but roundoff errors // might have put a few in there. So add them to previous one. hist[nbins-1] += hist[nbins]; } // There are only a 'few' points left. // So read them all in and determine the fractileInx'th-largest. // Again, due to rounding we might find a few elements more or less. // So take care that the receiving block is not exceeded and that // the number of elements found are used in kthLargest. // Note it also makes sense to stop the iteration when we found all // elements. It may save a few reads from the lattice. Block tmp(ntodo); T* tmpPtr = tmp.storage(); uInt ndone = 0; iter.reset(); while (! iter.atEnd() && ndone& array = iter.cursor(); iter.getMask (mask); const Bool* maskPtr = mask->getStorage (delMask); const T* dataPtr = array.getStorage (delData); uInt n = array.nelements(); for (uInt i=0; i= stv && dataPtr[i] < endv) { tmpPtr[ndone++] = dataPtr[i]; if (ndone == ntodo) { break; } } } array.freeStorage (dataPtr, delData); mask->freeStorage (maskPtr, delMask); iter++; } // By rounding it is possible that not enough elements were found. // In that case return the middle of the (very small) interval. if (fractileInx >= ndone) { result(0) = (stv+endv)/2; } else { result(0) = GenSort::kthLargest (tmp.storage(), ndone, fractileInx); } return result; } template Vector LatticeFractile::smallMaskedFractile (const MaskedLattice& lattice, Float fraction) { // Make a buffer to hold all masked-on elements. // The number of values is not more than the number of elements in the // lattice, so make the buffer that long. uInt size = lattice.shape().product(); Block buffer(size); uInt npts = 0; // Iterate through the lattice and assemble all masked-on elements. COWPtr> mask; RO_MaskedLatticeIterator iter(lattice); while (! iter.atEnd()) { Bool delData, delMask; const Array& array = iter.cursor(); iter.getMask (mask); const Bool* maskPtr = mask->getStorage (delMask); const T* dataPtr = array.getStorage (delData); uInt n = array.nelements(); for (uInt i=0; ifreeStorage (maskPtr, delMask); iter++; } if (npts == 0) { return Vector(); } // Use median of an Array instead of kthLargest directly, because // for a small array with an even number of elements, median takes // the average of the 2 middle elements. Vector result(1); if (fraction == 0.5) { result(0) = median (Array (IPosition(1,npts), buffer.storage(), SHARE)); } else { uInt fractileInx = uInt (fraction * (npts-1)); result(0) = GenSort::kthLargest (buffer.storage(), npts, fractileInx); } return result; } template Vector LatticeFractile::unmaskedFractiles (const Lattice& lattice, Float left, Float right, uInt smallSize) { AlwaysAssert (left >= 0 && left <= right && right <= 1, AipsError); // Determine the number of elements in the lattice. // If small enough, we read them all and do it in memory. uInt ntodo1 = lattice.shape().product(); if (ntodo1 == 0) { return Vector(); } // Find which elements are left and right fractile. uInt leftInx = uInt (left * (lattice.nelements()-1)); uInt rightInx = uInt (right * (lattice.nelements()-1)); Vector result(2); if (ntodo1 <= smallSize) { // We can hold all data in memory. Bool delData; Array array = lattice.get(); T* dataPtr = array.getStorage (delData); result(0) = GenSort::kthLargest (dataPtr, ntodo1, leftInx); result(1) = GenSort::kthLargest (dataPtr, ntodo1, rightInx); // Storage only needs to be freed, but we need a const pointer for that. const T* constDataPtr = dataPtr; array.freeStorage (constDataPtr, delData); return result; } // Bad luck. We have to do some more work. // Do a first binning while determining min/max at the same time. // Hopefully the start and end values make some sense. // Make the block 1 element larger, because possible roundoff errors // could result in a binnr just beyond the end. const uInt nbins = 10000; Block hist1(nbins+1, 0u); Block boundaries1(nbins+1); T stv1, endv1, minv1, maxv1; unmaskedHistogram (stv1, endv1, minv1, maxv1, hist1, boundaries1, lattice); // Init variables for both fractiles. uInt ntodo2 = ntodo1; T stv2 = stv1; T endv2 = endv1; T minv2 = minv1; T maxv2 = maxv1; Block hist2 (hist1); Block boundaries2 (boundaries1); Bool finished1 = False; Bool finished2 = False; // Iterate until the bins containing the fractiles do not // contain too many values anymore. RO_LatticeIterator iter(lattice); while (True) { // Determine which bin contains the requested values, determine // new boundaries, max/min and offset in bin. // Do that for left and right fractile. uInt ntodo = 0; if (!finished1) { ntodo1 = findBin (leftInx, stv1, endv1, minv1, maxv1, hist1, boundaries1); // If only a 'few' more points to do, stop making histograms. // Otherwise histogram the fractile bin with a much smaller bin size. if (ntodo1 <= smallSize) { finished1 = True; if (ntodo1 == 0) { result(0) = endv1; } } else { ntodo += ntodo1; } } if (!finished2) { ntodo2 = findBin (rightInx, stv2, endv2, minv2, maxv2, hist2, boundaries2); if (ntodo2 <= smallSize) { finished2 = True; if (ntodo2 == 0) { result(1) = endv2; } } else { ntodo += ntodo2; } } // Stop if both fractiles have small enough bins. if (finished1 && finished2) { break; } // Build new histograms with determined subsets minv1 = endv1; minv2 = endv2; maxv1 = stv1; maxv2 = stv2; hist1 = 0; hist2 = 0; T step1 = (endv1 - stv1) / nbins; T step2 = (endv2 - stv2) / nbins; for (uInt i=0; i<=nbins; i++) { boundaries1[i] = stv1 + i*step1; boundaries2[i] = stv2 + i*step2; } uInt ndone = 0; iter.reset(); while (! iter.atEnd() && ndone& array = iter.cursor(); Bool delData; const T* dataPtr = array.getStorage (delData); uInt n = array.nelements(); for (uInt i=0; i= stv1 && dataPtr[i] < endv1) { Int bin = Int((dataPtr[i] - stv1) / step1); // Due to rounding the bin number might get one too low or high. if (dataPtr[i] < boundaries1[bin]) { bin--; } else if (dataPtr[i] >= boundaries1[bin+1]) { bin++; } hist1[bin]++; if (dataPtr[i] < minv1) { minv1 = dataPtr[i]; } if (dataPtr[i] > maxv1) { maxv1 = dataPtr[i]; } ndone++; } if (!finished2 && dataPtr[i] >= stv2 && dataPtr[i] < endv2) { Int bin = Int((dataPtr[i] - stv2) / step2); // Due to rounding the bin number might get one too low or high. if (dataPtr[i] < boundaries2[bin]) { bin--; } else if (dataPtr[i] >= boundaries2[bin+1]) { bin++; } hist2[bin]++; if (dataPtr[i] < minv2) { minv2 = dataPtr[i]; } if (dataPtr[i] > maxv2) { maxv2 = dataPtr[i]; } ndone++; } } array.freeStorage (dataPtr, delData); iter++; } // In principle the last bins should be empty, but roundoff errors // might have put a few in there. So add them to previous one. hist1[nbins-1] += hist1[nbins]; hist2[nbins-1] += hist2[nbins]; } if (ntodo1 == 0 && ntodo2 == 0) { return result; } // There are only a 'few' points left in both histograms. // So read them all in and determine the Inx'th-largest. // Again, due to rounding we might find a few elements more or less. // So take care that the receiving block is not exceeded and that // the number of elements found are used in kthLargest. // Note it also makes sense to stop the iteration when we found all // elements. It may save a few reads from the lattice. Block tmp1(ntodo1); Block tmp2(ntodo2); T* tmpPtr1 = tmp1.storage(); T* tmpPtr2 = tmp2.storage(); uInt ndone1 = 0; uInt ndone2 = 0; iter.reset(); while (! iter.atEnd() && (ndone1& array = iter.cursor(); Bool delData; const T* dataPtr = array.getStorage (delData); uInt n = array.nelements(); for (uInt i=0; i= stv1 && dataPtr[i] < endv1) { tmpPtr1[ndone1++] = dataPtr[i]; } if (ndone2 < ntodo2 && dataPtr[i] >= stv2 && dataPtr[i] < endv2) { tmpPtr2[ndone2++] = dataPtr[i]; } } array.freeStorage (dataPtr, delData); iter++; } // By rounding it is possible that not enough elements were found. if (leftInx >= ndone1) { result(0) = endv1; } else { result(0) = GenSort::kthLargest (tmp1.storage(), ndone1, leftInx); } if (rightInx >= ndone2) { result(1) = endv2; } else { result(1) = GenSort::kthLargest (tmp2.storage(), ndone2, rightInx); } return result; } template Vector LatticeFractile::maskedFractiles (const MaskedLattice& lattice, Float left, Float right, uInt smallSize) { AlwaysAssert (left >= 0 && left <= right && right <= 1, AipsError); // If unmasked, a simpler way can be used. if (! lattice.isMasked()) { return unmaskedFractiles (lattice, left, right, smallSize); } // Determine the number of elements in the lattice. // If small enough, we read them all and do it in memory. uInt ntodo1 = lattice.shape().product(); if (ntodo1 <= smallSize) { return smallMaskedFractiles (lattice, left, right); } Vector result(2); // Bad luck. We have to do some more work. // Do a first binning while determining min/max at the same time. // Hopefully the start and end values make some sense. // Make the block 1 element larger, because possible roundoff errors // could result in a binnr just beyond the end. const uInt nbins = 10000; Block hist1(nbins+1, 0u); Block boundaries1(nbins+1); T stv1, endv1, minv1, maxv1; ntodo1 = maskedHistogram (stv1, endv1, minv1, maxv1, hist1, boundaries1, lattice); if (ntodo1 == 0) { return Vector(); } // Find which elements are left and right fractile. uInt leftInx = uInt (left * (ntodo1-1)); uInt rightInx = uInt (right * (ntodo1-1)); // Init variables for both fractiles. uInt ntodo2 = ntodo1; T stv2 = stv1; T endv2 = endv1; T minv2 = minv1; T maxv2 = maxv1; Block hist2 (hist1); Block boundaries2 (boundaries1); Bool finished1 = False; Bool finished2 = False; // Iterate until the bins containing the fractiles do not // contain too many values anymore. COWPtr> mask; RO_MaskedLatticeIterator iter(lattice); while (True) { // Determine which bin contains the requested values, determine // new boundaries, max/min and offset in bin. // Do that for left and right fractile. uInt ntodo = 0; if (!finished1) { ntodo1 = findBin (leftInx, stv1, endv1, minv1, maxv1, hist1, boundaries1); // If only a 'few' more points to do, stop making histograms. // Otherwise histogram the fractile bin with a much smaller bin size. if (ntodo1 <= smallSize) { finished1 = True; if (ntodo1 == 0) { result(0) = endv1; } } else { ntodo += ntodo1; } } if (!finished2) { ntodo2 = findBin (rightInx, stv2, endv2, minv2, maxv2, hist2, boundaries2); if (ntodo2 <= smallSize) { finished2 = True; if (ntodo2 == 0) { result(1) = endv2; } } else { ntodo += ntodo2; } } // Stop if both fractiles have small enough bins. if (finished1 && finished2) { break; } // Build new histograms with determined subsets minv1 = endv1; minv2 = endv2; maxv1 = stv1; maxv2 = stv2; hist1 = 0; hist2 = 0; T step1 = (endv1 - stv1) / nbins; T step2 = (endv2 - stv2) / nbins; for (uInt i=0; i<=nbins; i++) { boundaries1[i] = stv1 + i*step1; boundaries2[i] = stv2 + i*step2; } uInt ndone = 0; iter.reset(); while (! iter.atEnd() && ndone& array = iter.cursor(); iter.getMask (mask); const Bool* maskPtr = mask->getStorage (delMask); const T* dataPtr = array.getStorage (delData); uInt n = array.nelements(); for (uInt i=0; i= stv1 && dataPtr[i] < endv1) { Int bin = Int((dataPtr[i] - stv1) / step1); // Due to rounding the bin number might get one too low or high. if (dataPtr[i] < boundaries1[bin]) { bin--; } else if (dataPtr[i] >= boundaries1[bin+1]) { bin++; } hist1[bin]++; if (dataPtr[i] < minv1) { minv1 = dataPtr[i]; } if (dataPtr[i] > maxv1) { maxv1 = dataPtr[i]; } ndone++; } if (!finished2 && dataPtr[i] >= stv2 && dataPtr[i] < endv2) { Int bin = Int((dataPtr[i] - stv2) / step2); // Due to rounding the bin number might get one too low or high. if (dataPtr[i] < boundaries2[bin]) { bin--; } else if (dataPtr[i] >= boundaries2[bin+1]) { bin++; } hist2[bin]++; if (dataPtr[i] < minv2) { minv2 = dataPtr[i]; } if (dataPtr[i] > maxv2) { maxv2 = dataPtr[i]; } ndone++; } } } array.freeStorage (dataPtr, delData); mask->freeStorage (maskPtr, delMask); iter++; } // In principle the last bins should be empty, but roundoff errors // might have put a few in there. So add them to previous one. hist1[nbins-1] += hist1[nbins]; hist2[nbins-1] += hist2[nbins]; } if (ntodo1 == 0 && ntodo2 == 0) { return result; } // There are only a 'few' points left in both histograms. // So read them all in and determine the Inx'th-largest. // Again, due to rounding we might find a few elements more or less. // So take care that the receiving block is not exceeded and that // the number of elements found are used in kthLargest. // Note it also makes sense to stop the iteration when we found all // elements. It may save a few reads from the lattice. Block tmp1(ntodo1); Block tmp2(ntodo2); T* tmpPtr1 = tmp1.storage(); T* tmpPtr2 = tmp2.storage(); uInt ndone1 = 0; uInt ndone2 = 0; iter.reset(); while (! iter.atEnd() && (ndone1& array = iter.cursor(); iter.getMask (mask); const Bool* maskPtr = mask->getStorage (delMask); const T* dataPtr = array.getStorage (delData); uInt n = array.nelements(); for (uInt i=0; i= stv1 && dataPtr[i] < endv1) { tmpPtr1[ndone1++] = dataPtr[i]; } if (ndone2 < ntodo2 && dataPtr[i] >= stv2 && dataPtr[i] < endv2) { tmpPtr2[ndone2++] = dataPtr[i]; } } } array.freeStorage (dataPtr, delData); mask->freeStorage (maskPtr, delMask); iter++; } // By rounding it is possible that not enough elements were found. // In that case return the middle of the (very small) interval. if (leftInx >= ndone1) { result(0) = endv1; } else { result(0) = GenSort::kthLargest (tmp1.storage(), ndone1, leftInx); } if (rightInx >= ndone2) { result(1) = endv2; } else { result(1) = GenSort::kthLargest (tmp2.storage(), ndone2, rightInx); } return result; } template Vector LatticeFractile::smallMaskedFractiles (const MaskedLattice& lattice, Float left, Float right) { // Make a buffer to hold all masked-on elements. // The number of values is not more than the number of elements in the // lattice, so make the buffer that long. uInt size = lattice.shape().product(); Block buffer(size); uInt npts = 0; // Iterate through the lattice and assemble all masked-on elements. COWPtr> mask; RO_MaskedLatticeIterator iter(lattice); while (! iter.atEnd()) { Bool delData, delMask; const Array& array = iter.cursor(); iter.getMask (mask); const Bool* maskPtr = mask->getStorage (delMask); const T* dataPtr = array.getStorage (delData); uInt n = array.nelements(); for (uInt i=0; ifreeStorage (maskPtr, delMask); iter++; } if (npts == 0) { return Vector(); } // Use median of an Array instead of kthLargest directly, because // for a small array with an even number of elements, median takes // the average of the 2 middle elements. uInt leftInx = uInt (left * (npts-1)); uInt rightInx = uInt (right * (npts-1)); Vector result(2); result(0) = GenSort::kthLargest (buffer.storage(), npts, leftInx); result(1) = GenSort::kthLargest (buffer.storage(), npts, rightInx); return result; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LatticeMath/LatticeHistProgress.cc000066400000000000000000000045441476623553700236130ustar00rootroot00000000000000//# LatticeHistProgress.cc: progress meter for LatticeHistograms //# Copyright (C) 1995,1996,1997,1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LatticeHistProgress::~LatticeHistProgress() { delete itsMeter; } void LatticeHistProgress::initDerived() // // Initialize meter // { delete itsMeter; // The expectedNSteps function will return the number of // expected steps. The number of expected steps // is set by calling LatticeProgress::init which then // calls this initDerived function // itsMeter = new ProgressMeter(0.0, Double(expectedNsteps()), String("Generate Storage Image"), String("Accumulation Iterations"), String(""), String(""), True, max(1,Int(expectedNsteps()/20))); } void LatticeHistProgress::nstepsDone (uInt nsteps) // // Update the meter with the number of steps taken so far { itsMeter->update (nsteps); } void LatticeHistProgress::done() { delete itsMeter; itsMeter = 0; } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LatticeMath/LatticeHistProgress.h000066400000000000000000000060111476623553700234440ustar00rootroot00000000000000//# LatticeHistProgress.h: progress meter for LatticeHistograms //# Copyright (C) 1996,1997,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICEHISTPROGRESS_H #define LATTICES_LATTICEHISTPROGRESS_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class ProgressMeter; //

        Provides a progress meter for the LatticeHistograms class // // // // // // //
      • LatticeProgress // // // // Display a progress meter for the class LatticeHistograms // // // // Progress meters can be displayed by the LatticeApply class // which is used by LatticeHistograms in order to optimally iterate // through the image. To do this, one must derive a // class from LatticeProgress. LatticeApply calls // methods declared in LatticeProgress and implemented in // the derived class. // // // // I like progress meters ! // // // // class LatticeHistProgress : public LatticeProgress { public: // Constructor makes a null object LatticeHistProgress() : itsMeter(0) {}; // Destructor deletes the ProgressMeter pointer virtual ~LatticeHistProgress(); // Initialize this object. Here we create the ProgressMeter // This function is called by the init in LatticeProgress virtual void initDerived(); // Tell the number of steps done so far. virtual void nstepsDone (uInt nsteps); // The process has ended so clean things up. virtual void done(); private: ProgressMeter* itsMeter; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LatticeMath/LatticeHistSpecialize.cc000066400000000000000000000247671476623553700241100ustar00rootroot00000000000000//# LatticeHistSpecialize.cc: Defines non-templated classes for LatticeHistograms //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN uInt LatticeHistSpecialize::bin(Float datum, Float dmin, Float width, uInt nBins) { return min(nBins-1, uInt((datum-dmin)/width)); } void LatticeHistSpecialize::process( const Complex* pInData, const Bool* pInMask, Block* pHist, const Vector& clip, Complex binWidth, uInt offset, uInt nrval, uInt nBins, uInt dataIncr, uInt maskIncr ) { Complex datum, useIt; uInt rbin; uInt index; // if (pInMask==0) { for (uInt i=0; i 0.5) { rbin = bin(real(datum), real(clip(0)), real(binWidth), nBins); // index = rbin + offset; Complex& hist1 = (*pHist)[index]; /// hist1.real() += 1.0; hist1 += Complex(1.0, 0.0); } if (imag(useIt) > 0.5) { rbin = bin(imag(datum), imag(clip(0)), imag(binWidth), nBins); index = rbin + offset; Complex& hist2 = (*pHist)[index]; /// hist2.imag() += 1.0; hist2 += Complex(0.0, 1.0); } pInData += dataIncr; } } else { for (uInt i=0; i 0.5) { rbin = bin(real(datum), real(clip(0)), real(binWidth), nBins); index = rbin + offset; Complex& hist1 = (*pHist)[index]; /// hist1.real() += 1.0; hist1 += Complex(1.0, 0.0); } if (imag(useIt) > 0.5) { rbin = bin(imag(datum), imag(clip(0)), imag(binWidth), nBins); index = rbin + offset; Complex& hist2 = (*pHist)[index]; /// hist2.imag() += 1.0; hist2 += Complex(0.0, 1.0); } } pInData += dataIncr; pInMask += maskIncr; } } } void LatticeHistSpecialize::makeGauss(uInt& nGPts, Float& gMax, Vector& gX, Vector& gY, Float dMean, Float dSigma, Float dSum, Float xMin, Float xMax, Float binWidth, Bool doCumu, Bool doLog) // // Make overlay Gaussian with the given parameters // { // 100 points please nGPts = 100; gX.resize(nGPts); gY.resize(nGPts); // Set up Gaussian functional const Float gaussAmp = dSum * M_SQRT1_2 * (0.5*M_2_SQRTPI) / dSigma; const Float gWidth = sqrt(8.0*M_LN2) * dSigma; const Gaussian1D gauss(gaussAmp, dMean, gWidth); // Generate Gaussian. Float dgx = (xMax - xMin) / Float(nGPts); Float xx; uInt i; for (i=0,xx=xMin,gMax=0.0; i& counts, Complex& yMax, uInt nBins, Float scale) // // Code is the same as Float. Could really make this // templated, but still need access to this function // from IHS, so leave it here // { counts(0) = scale * counts(0); for (uInt i=1; i& counts, Complex& yMax, uInt nBins) { yMax = 0.0; for (uInt i=0; i 0.0) counts(i).real() = log10(counts(i).real()); /// if (imag(counts(i)) > 0.0) counts(i).imag() = log10(counts(i).imag()); if (real(counts(i)) > 0.0) { counts(i) = Complex(log10(counts(i).real()), counts(i).imag()); } if (imag(counts(i)) > 0.0) { counts(i) = Complex(counts(i).real(), log10(counts(i).imag())); } // /// if (real(counts(i)) > real(yMax)) yMax.real() = real(counts(i)); /// if (imag(counts(i)) > imag(yMax)) yMax.imag() = imag(counts(i)); if (real(counts(i)) > real(yMax)) { yMax = Complex(real(counts(i)), yMax.imag()); } if (imag(counts(i)) > imag(yMax)) { yMax = Complex(yMax.real(), imag(counts(i))); } } } Float LatticeHistSpecialize::mul(Float v1, Float v2) { return v1*v2; } Complex LatticeHistSpecialize::mul(Complex v1, Complex v2) { return Complex(real(v1)*real(v2),imag(v1)*imag(v2)); } void LatticeHistSpecialize::plot(PGPlotter& plotter, Bool doGauss, Bool doCumu, Bool doLog, Float linearSum, Float yMax, Float binWidth, const Vector& values, const Vector& counts, const Vector& stats, uInt label, uInt ci, Bool page) // // The histogram is already in its desired form - linear, log, cumu // yMax is in that form too. // // label == 0 -> Both // 1 Bottom/left // 2 Top/right // { Float xMin = stats(LatticeStatsBase::MIN); Float xMax = stats(LatticeStatsBase::MAX); Float yMin = 0.0; Float yMax2 = yMax; // Vector gX, gY; if (doGauss) { uInt nGPts = 0; Float gMax; makeGauss (nGPts, gMax, gX, gY, stats(LatticeStatsBase::MEAN), stats(LatticeStatsBase::SIGMA), linearSum, xMin, xMax, binWidth, doCumu, doLog); yMax2 = max(yMax2, gMax); } // Stretch extrema by 5% LatticeStatsBase::stretchMinMax(xMin, xMax); LatticeStatsBase::stretchMinMax(yMin, yMax2); // if (page) plotter.page(); plotter.bbuf(); plotter.swin(xMin, xMax, 0.0, yMax2); plotter.sci(ci); if (label==0) { plotter.box("BCNST", 0.0, 0, "BCNST", 0.0, 0); } else if (label==1) { plotter.box("BNST", 0.0, 0, "BNST", 0.0, 0); } else if (label==2) { plotter.box("CMST", 0.0, 0, "CMST", 0.0, 0); } // plotHist (values, counts, plotter); if (doGauss) plotter.line (gX, gY); // Label plotter.sci(1); if (doCumu) { if (doLog) { plotter.lab("Pixel Value", "Log10 (Cumulative Counts)", ""); } else { plotter.lab("Pixel Value", "Cumulative Counts", ""); } } else { if (doLog) { plotter.lab("Pixel Value", "Log10 (Counts)", ""); } else { plotter.lab("Pixel Value", "Counts", ""); } } plotter.ebuf(); } void LatticeHistSpecialize::plot(PGPlotter& plotter, Bool doGauss, Bool doCumu, Bool doLog, Complex linearSum, Complex yMax, Complex binWidth, const Vector& values, const Vector& counts, const Vector& stats, uInt, uInt, Bool) // // The histogram is already in its desired form - linear, log, cumu // yMax is in that form too. // { plot(plotter, doGauss, doCumu, doLog, real(linearSum), real(yMax), real(binWidth), real(values), real(counts), real(stats), 1, 1, True); plot(plotter, doGauss, doCumu, doLog, imag(linearSum), imag(yMax), imag(binWidth), imag(values), imag(counts), imag(stats), 2, 7, False); } void LatticeHistSpecialize::plotHist (const Vector& x, const Vector& y, PGPlotter& plotter) { const Float width = (x(1) - x(0)) / 2.0; Float xx, yy; for (uInt i=0; i #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class PGPlotter; // Specialized functions for LatticeHistograms // // // // // // //
      • LatticeHistograms // // // // Specialized code is type specific. This code is for LatticeHistograms. // // // // This class provides specialized static functions to handle Type // dependent (Float, Complex) processing for LatticeHistograms. // I couldn't do it all with templated functions. // // // // // // // class LatticeHistSpecialize { public: // Make historgam cumulative template static void makeCumulative (Vector& counts, T& yMax, uInt nBins, typename NumericTraits::BaseType scale); static void makeCumulative (Vector& counts, Complex& yMax, uInt nBins, Float scale); // Make histogram logarithmic template static void makeLogarithmic (Vector& counts, T& yMax, uInt nBins); static void makeLogarithmic (Vector& counts, Complex& yMax, uInt nBins); // Multiply. Real and imaginary treated as independent // C1*C2 = (r1*r2,i1*i2) static Float mul(Float v1, Float v2); static Complex mul(Complex v1, Complex v2); // Plot histograms static void plot(PGPlotter& plot, Bool doGauss, Bool doCumu, Bool doLog, Float linearSum, Float yMax, Float binWidth, const Vector& values, const Vector& counts, const Vector& stats, uInt whereLabel, uInt ci, Bool page); static void plot(PGPlotter& plot, Bool doGauss, Bool doCumu, Bool doLog, Complex linearSum, Complex yMax, Complex binWidth, const Vector& values, const Vector& counts, const Vector& stats, uInt whereLabel, uInt ci, Bool page); // Process data chunk creating histogram. template static void process( const T* pInData, const Bool* pInMask, Block* pHist, const Vector& clip, T binWidth, uInt offset, uInt nrval, uInt nBins, uInt dataIncr, uInt maskIncr ); // static void process ( const Complex* pInData, const Bool* pInMask, Block* pHist, const Vector& clip, Complex binWidth, uInt offset, uInt nrval, uInt nBins, uInt dataIncr, uInt maskIncr ); // Set bin width. For complex, real and imaginary treated separately static Float setBinWidth (Float dmin, Float dmax, uInt nBins); // static Complex setBinWidth(Complex dmin, Complex dmax, uInt nBins); private: static uInt bin(Float datum, Float min, Float width, uInt nBins); // static void makeGauss(uInt& nGPts, Float& gMax, Vector& gX, Vector& gY, Float dMean, Float dSigma, Float dSum, Float xMin, Float xMax, Float binWidth, Bool doCumu, Bool doLog); // static void plotHist (const Vector& x, const Vector& y, PGPlotter& plotter); }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif #endif casacore-3.7.1/lattices/LatticeMath/LatticeHistSpecialize2.tcc000066400000000000000000000064621476623553700243460ustar00rootroot00000000000000//# LatticeHistSpecialize.cc: Defines non-templated classes for LatticeHistograms //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // #ifndef LATTICEMATH_LATTICEHISTSPECIALIZE2_TCC #define LATTICEMATH_LATTICEHISTSPECIALIZE2_TCC #include #include namespace casacore { template void LatticeHistSpecialize::makeCumulative (Vector& counts, T& yMax, uInt nBins, typename NumericTraits::BaseType scale) { counts(0) = scale * counts(0); for (uInt i=1; i void LatticeHistSpecialize::makeLogarithmic (Vector& counts, T& yMax, uInt nBins) { yMax = 0.0; for (uInt i=0; i 0.0) counts(i) = std::log10(counts(i)); yMax = std::max(yMax, counts(i)); } } template void LatticeHistSpecialize::process( const T* pInData, const Bool* pInMask, Block* pHist, const Vector& clip, T binWidth, uInt offset, uInt nrval, uInt nBins, uInt dataIncr, uInt maskIncr ) { T datum; uInt rBin; uInt index; // if (pInMask==0) { for (uInt i=0; i 0.5) { rBin = bin(datum, clip(0), binWidth, nBins); index = rBin + offset; auto& hist = (*pHist)[index]; hist += 1.0; } pInData += dataIncr; } } else { for (uInt i=0; i 0.5)) { rBin = bin(datum, clip(0), binWidth, nBins); index = rBin + offset; auto& hist = (*pHist)[index]; hist += 1.0; } pInData += dataIncr; pInMask += maskIncr; } } } } #endif casacore-3.7.1/lattices/LatticeMath/LatticeHistograms.h000066400000000000000000000464521476623553700231450ustar00rootroot00000000000000//# LatticeHistograms.h: generate histograms from a lattice //# Copyright (C) 1996,1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICEHISTOGRAMS_H #define LATTICES_LATTICEHISTOGRAMS_H //# Includes #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class MaskedLattice; template class TempLattice; class IPosition; class PGPlotter; // // Displays histograms of regions from a lattice. // // // // // //
      • MaskedLattice // // // This is a class designed to display histograms from MaskedLattices // // // This class enable you to display and/or retrieve histograms evaluated over // specified regions from a MaskedLattice. The dimension of the region is arbitrary, but // the size of each dimension is always the size of the corresponding lattice axis. // The histograms are displayed as a function of location of the axes not // used to evaluate the histograms over. The axes which you evaluate the histograms // over are called the cursor axes, the others are called the display axes. // // For example, consider a lattice cube (call the axes xyz or [0,1,2]). You could // display histograms from xy planes (cursor axes [0,1]) as a function of z (display // axes [2]). Or you could retrieve histograms from the z axis (cursor axes [2]) // for each [x,y] location (display axes [0,1]). // // This class generates a "storage lattice" into which it writes the histograms. // It is from this storage lattice that the plotting and retrieval // arrays are drawn. The storage lattice is either in core or on disk // depending upon its size (if > 10% of memory given by .aipsrc system.resources.memory // then it goes into a disk-based PagedArray). If on disk, the // storage lattice is deleted when the LatticeHistograms // object destructs. // // // // Note that for complex lattices, real and imaginary are treated independently. // They are binned and plotted separately. // // // // If you ignore return error statuses from the functions that set the // state of the class, the internal status of the class is set to bad. // This means it will just keep on returning error conditions until you // explicitly recover the situation. A message describing the last // error condition can be recovered with function errorMessage. // // // // //// Construct PagedImage from file name // // PagedImage inImage(inName); // //// Construct histogram object // // LogOrigin or("myClass", "myFunction(...)", WHERE); // LogIO os(or); // ImageHistograms histo(inImage, os); // //// Set cursor axes to see statistics of yz planes (0 relative) // // Vector cursorAxes(2) // cursorAxes(0) = 1; // cursorAxes(1) = 2; // if (!histo.setAxes(cursorAxes)) return 1; // //// Set to list and plot mean, sigma and rms // // if (!histo.setList(True)) return 1; // String device = "/xs"; // Vector nxy(2); // nxy(0) = 3; // nxy(1) = 3; // if (!histo.setPlotting(device, nxy)) return 1; // //// Now activate actual listing and plotting // // if (!histo.display ()) return 1; // //// Retrieve histograms into array // // Array values, counts; // if (!histo.getHistograms(values, counts)) return 1; // // // In this example, a PagedImage is constructed. We set the cursor axes // to be the y and z axes so we make a histogram of each yz plane as a function // of x location on the PGPLOT device "/xs" with 9 subplots per page. // After the plotting we also retrieve the histograms into an array. // // // The generation of histograms from an image is a basic and necessary capability. // // // //
      • Make ascii listing of histograms as well as plots if desired // // template class LatticeHistograms { public: // Constructor takes the MaskedLattice and a LogIO object for logging. // You can also specify whether you want to see progress meters or not. // You can force the storage lattice to be disk based, otherwise // the decision for core or disk is taken for you. LatticeHistograms(const MaskedLattice& lattice, LogIO& os, Bool showProgress=True, Bool forceDisk=False); // Constructor takes the MaskedLattice only. In the absence of a logger you get no messages. // This includes error messages and potential listing of statistics. // You can specify whether you want to see progress meters or not. // You can force the storage lattice to be disk based, otherwise // the decision for core or disk is taken for you. LatticeHistograms(const MaskedLattice& lattice, Bool showProgress=True, Bool forceDisk=False); // Copy constructor (copy semantics) LatticeHistograms(const LatticeHistograms &other); // Destructor virtual ~LatticeHistograms (); // Assignment operator (copy semantics) LatticeHistograms &operator=(const LatticeHistograms &other); // Set the cursor axes (0 relative). A return value of False // indicates you have asked for an invalid axis or that the internal // status of the class is bad. The default state of the class is to set // the cursor axes to all axes in the lattice. Bool setAxes (const Vector& cursorAxes); // Set the number of bins for the histogram. Note that the bin width is // worked out for each histogram separately from the data minimum and maximum. // The default state of the class is to set 25 bins. A return value of False // indicates you gave a non-positive bin width or that the internal status of the // class is bad. Bool setNBins (const uInt& nBins); // Specify a pixel intensity range for which all pixels in that range are // included. A vector of length 1 for include means that the // range will be set to -abs(include(0)) to abs(include(0)). // A return value of False indicates that the internal // status of the class is bad. If you don't call this function, the default // state of the class is to include all pixels. Bool setIncludeRange (const Vector& include); // Specify that a Gaussian overlay should be plotted on the histogram. This // Gaussian has the same mean and standard deviation as the data that were // binned, and the same integral as the histogram. A return value of False // indicates that the internal status of the class is bad. The default state of // the class is to not draw a Gaussian overlay. Bool setGaussian (const Bool& doGauss); // Specify the form of the histogram. It can be plotted linearly or // logarithmically, and cumulatively or non-cumulatively. A return value // of False indicates that the internal status of the class is bad. // The default state of the class is to draw the histograms linearly and // non-cumulatively. Bool setForm (const Bool& doLog, const Bool& doCumu); // This function allows you to control whether some statistics of the // data that contributed to the histogram are written to the output // stream. A return value of False indicates that the internal // status of the class is bad. The default state of the class is to not // list statistics. Bool setStatsList(const Bool& doList); // This function sets the name of the PGPLOT plotting device and the number of // subplots in x and y per page. If you set plotter but offer // a zero length array for nxy then nxy is set // to [1,1]. A return value of False indicates invalid // plotting arguments or that the internal status of the class is bad. If you // don't call this function, the default state of the class is to not set // a plotting device. Bool setPlotting(PGPlotter& plotter, const Vector& nxy); // Display the histograms by plotting them. A return value of False // indicates an invalid plotting device, or that the internal status of the class is bad. // If you don't call this function you won't see any histograms. Bool display (); // CLose the plotter void closePlotting(); // Return the display axes Vector displayAxes() const {return displayAxes_p;} // This function retrieves the histograms into Array. The shape of the first // dimension of this array is the number of bins. The rest of the shape of the // array is the shape of the display axes (e.g. if the shape of the lattice is // [nx,ny,nz] and you ask for histograms of the y axis the shape of the returned // array would be [nbins,nx,nz]. The histograms are retrieved in the form // specified by the setForm function. The arrays are resized internally. // A return value of False indicates that the internal status of the class is bad. Bool getHistograms (Array& values, Array& counts); // in this version, the set of stats for each histogram is also returned. The // stats array has the shape of the display axes. Bool getHistograms (Array& values, Array& counts, Array >& stats); // This function retrieves the histogram at the specified location // into Vectors. The histogram is retrieved in the form // specified by the setForm function. The vectors are resized // internally. If posInLattice=True then the location is a // location in the input lattice. Any positions on the display axes // are ignored. Otherwise, you should just give locations for // the display axes only. A return value of False indicates that // the internal status of the class is bad. Bool getHistogram (Vector& values, Vector& counts, const IPosition& pos, const Bool posInLattice=False); // Reset argument error condition. If you specify invalid arguments to // one of the above set functions, an internal flag will be set which will // prevent the work functions from doing anything (should you have chosen // to ignore the Boolean return values of the set functions). // This function allows you to reset that internal state to good. void resetError () {goodParameterStatus_p = True;}; // Recover last error message String errorMessage() const {return error_p;}; // Set a MaskedLattice. A return value of False indicates the // lattice had an invalid type or that the internal status of the class is bad. Bool setNewLattice (const MaskedLattice& lattice); // These things are protected only so that they are available to ImageHistograms // which inherits from LatticeHistograms protected: LogIO os_p; Bool goodParameterStatus_p; Vector cursorAxes_p, displayAxes_p; String error_p; // Given a location in the histogram storage lattice, convert those locations on the // non-histogram axis (the first one) relative to the parent or current lattice IPosition locHistInLattice (const IPosition& histPosition, Bool relativeToParent=True) const; private: // A useful typedef typedef typename NumericTraits::PrecisionType AccumType; const MaskedLattice* pInLattice_p; TempLattice* pStoreLattice_p; LatticeStatistics* pStats_p; Bool binAll_p, needStorageLattice_p; Bool doCumu_p, doGauss_p, doList_p, doLog_p; Bool haveLogger_p, showProgress_p, forceDisk_p; uInt nBins_p; PGPlotter plotter_p; Vector nxy_p; Vector range_p; IPosition blcParent_p; // Convert a T to a Float for plotting static Float convertT (const T value) {return Float(std::real(value));}; // Convert a Float (from plotting) to a T static T convertF (const Float value) {return T(value);}; // Display histograms as a function of display axis Bool displayHistograms (); // Display one histogram Bool displayOneHistogram (const T&linearSum, const T&linearYMax, const IPosition& histPos, const Vector &stats, const Vector& values, const Vector& counts, PGPlotter& plotter); // Fish out and convert to the appropriate form one histogram from the // storage lattice void extractOneHistogram (T& linearSum, T& linearYMax, Vector& values, Vector& counts, const Vector& stats, const Vector& intCounts); // Iterate through the lattice and generate the histogram accumulation lattice Bool generateStorageLattice(); // Get the statistics from the statistics object for the current // location of either the input lattice, or the histogram storage lattice void getStatistics (Vector &stats, const IPosition &pos) const; // List statistics void listStatistics(LogIO& os, const Vector& stats, T binWidth); // Fill histograms storage lattice void makeHistograms(); // Create and fill statistics object Bool makeStatistics(); // Check/set include pixel range Bool setInclude (Vector& range, Bool& noInclude, const Vector& include, ostream& os); // Set stream attributes void setStream (ostream& os, Int oPrec); // Make a string with pixel coordinates of display axes. This function // is over-ridden by ImageHistograms which inherits from LatticeHistograms. virtual String writeCoordinates(const IPosition& histPos) const; // Write values of display axes on plots Bool writeDispAxesValues (const String& coords, PGPlotter& plotter, Float nchar) const; }; // Generate histograms, tile by tile, from a masked lattice // // // // // // // //
      • LatticeApply //
      • TiledCollapser // // // // This class is used by LatticeHistograms to generate // histograms from an input MaskedLattice. // The input lattice is iterated through in tile-sized chunks // and fed to an object of this class. // // // // HistTiledCollapser is derived from TiledCollapser which // is a base class used to define methods. Objects of this base class are // used by LatticeApply functions. In this particular case, // we are interested in LatticeApply::tiledApply. This function iterates // through a MaskedLattice and allows you to collapse one or more // axes, computing some values from it, and placing those values into // an output MaskedLattice. It iterates through the input // lattice in optimal tile-sized chunks. LatticeHistograms // uses a HistTiledCollapser object which it gives to // LatticeApply::tiledApply for digestion. After it has // done its work, LatticeHistograms then accesses the output // Lattice that it made. // // // // //// Created collapser. Control information is passed in via the constructor. // // HistTiledCollapser collapser(pStats, nBins_p); // //// This is the first output axis getting collapsed values. In LatticeHistograms //// this is the first axis of the output lattice // // Int newOutAxis = 0; // //// tiledApply does the work by passing the collapser data in chunks //// and by writing the results into the output lattice // // LatticeApply::tiledApply(outLattice, inLattice, // collapser, collapseAxes, // newOutAxis); // // // In this example, a collapser is made and passed to LatticeApply. // Afterwards, the output Lattice is available for use. // The Lattices must all be the correct shapes on input to tiledApply // // // // The LatticeApply classes enable the ugly details of optimal // Lattice iteration to be hidden from the user. // // // //
      • // template class HistTiledCollapser : public TiledCollapser { public: // Constructor HistTiledCollapser(LatticeStatistics* pStats, uInt nBins); virtual ~HistTiledCollapser(); // Initialize process, making some checks virtual void init (uInt nOutPixelsPerCollapse); // Initialize the accumulator virtual void initAccumulator (uInt64 n1, uInt64 n3); // Process the data in the current chunk. virtual void process ( uInt accumIndex1, uInt accumIndex3, const T* inData, const Bool* inMask, uInt inDataIncr, uInt inMaskIncr, uInt nrval, const IPosition& startPos, const IPosition& shape ); // End the accumulation process and return the result arrays virtual void endAccumulator(Array& result, Array& resultMask, const IPosition& shape); // Can handle null mask virtual Bool canHandleNullMask() const {return True;}; private: LatticeStatistics* pStats_p; Block* pHist_p; uInt nBins_p; uInt64 n1_p; uInt64 n3_p; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/LatticeMath/LatticeHistograms.tcc000066400000000000000000001071131476623553700234570ustar00rootroot00000000000000//# LatticeHistograms.cc: generate histograms from a Lattice //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICEHISTOGRAMS_TCC #define LATTICES_LATTICEHISTOGRAMS_TCC #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Public functions template LatticeHistograms::LatticeHistograms (const MaskedLattice& lattice, LogIO &os, Bool showProgress, Bool forceDisk) : os_p(os), goodParameterStatus_p(True), error_p(""), pInLattice_p(0), pStoreLattice_p(0), pStats_p(0), binAll_p(True), needStorageLattice_p(True), doCumu_p(False), doGauss_p(False), doList_p(False), doLog_p(False), haveLogger_p(True), showProgress_p(showProgress), forceDisk_p(forceDisk), nBins_p(25) // // Constructor. // { nxy_p.resize(0); range_p.resize(0); blcParent_p.resize(0); if (setNewLattice(lattice)) { // Cursor axes defaults to all Vector cursorAxes; goodParameterStatus_p = setAxes(cursorAxes); } else { os_p << error_p << LogIO::EXCEPTION; } } template LatticeHistograms::LatticeHistograms (const MaskedLattice& lattice, Bool showProgress, Bool forceDisk) : goodParameterStatus_p(True), error_p(""), pInLattice_p(0), pStoreLattice_p(0), pStats_p(0), binAll_p(True), needStorageLattice_p(True), doCumu_p(False), doGauss_p(False), doList_p(False), doLog_p(False), haveLogger_p(False), showProgress_p(showProgress), forceDisk_p(forceDisk), nBins_p(25) // // Constructor. // { nxy_p.resize(0); range_p.resize(0); blcParent_p.resize(0); if (setNewLattice(lattice)) { // Cursor axes defaults to all Vector cursorAxes; goodParameterStatus_p = setAxes(cursorAxes); } else { os_p << error_p << LogIO::EXCEPTION; } } template LatticeHistograms::LatticeHistograms(const LatticeHistograms &other) : pInLattice_p(0), pStoreLattice_p(0), pStats_p(0) // // Copy constructor. Storage lattice not copied. // { operator=(other); } template LatticeHistograms &LatticeHistograms::operator=(const LatticeHistograms &other) // // Assignment operator. Storage lattices not copied. // { if (this != &other) { // Deal with pointer if (pInLattice_p!=0) delete pInLattice_p; pInLattice_p = other.pInLattice_p->cloneML(); // Delete storage and statistics objects. if (pStoreLattice_p != 0) { delete pStoreLattice_p; pStoreLattice_p = 0; } // if (pStats_p != 0) { delete pStats_p; pStats_p = 0; } needStorageLattice_p = True; // Do the rest os_p = other.os_p; binAll_p = other.binAll_p; goodParameterStatus_p = other.goodParameterStatus_p; doCumu_p = other.doCumu_p; doGauss_p = other.doGauss_p; doList_p = other.doList_p; doLog_p = other.doLog_p; haveLogger_p = other.haveLogger_p; showProgress_p = other.showProgress_p; nBins_p = other.nBins_p; cursorAxes_p = other.cursorAxes_p; displayAxes_p = other.displayAxes_p; plotter_p = other.plotter_p; nxy_p = other.nxy_p; range_p = other.range_p; blcParent_p = other.blcParent_p; forceDisk_p = other.forceDisk_p; error_p = other.error_p; } return *this; } template LatticeHistograms::~LatticeHistograms() // // Destructor. // { delete pInLattice_p; pInLattice_p = 0; if (pStoreLattice_p != 0) { delete pStoreLattice_p; pStoreLattice_p = 0; } if (pStats_p != 0) { delete pStats_p; pStats_p = 0; } } template Bool LatticeHistograms::setAxes (const Vector& axes) // // This function sets the cursor axes and the display axes // { if (!goodParameterStatus_p) { return False; } // Save current cursor axes Vector saveAxes(cursorAxes_p.copy()); // Set cursor arrays (can't assign to potentially zero length array) cursorAxes_p.resize(0); cursorAxes_p = axes; if (cursorAxes_p.nelements() == 0) { // User didn't give any axes. Set them to all. cursorAxes_p.resize(pInLattice_p->ndim()); for (uInt i=0; indim(); i++) cursorAxes_p(i) = i; } else { for (uInt i=0; i Int(pInLattice_p->ndim()-1)) { error_p = "Invalid cursor axes"; return False; } } } // Set the display axes displayAxes_p.resize(0); displayAxes_p = IPosition::otherAxes(pInLattice_p->ndim(), cursorAxes_p).asVector(); // Signal that we have changed the axes and need new accumulation lattices if (saveAxes.nelements() != cursorAxes_p.nelements() || !allEQ(saveAxes, cursorAxes_p)) needStorageLattice_p = True; return True; } template Bool LatticeHistograms::setNBins (const uInt& nBins) // // Set the number of bins // { if (!goodParameterStatus_p) { return False; } // Save number of bins const uInt saveNBins = nBins_p; if (nBins < 1) { error_p = "Invalid number of bins"; goodParameterStatus_p = False; return False; } else { nBins_p = nBins; } // Signal that we need a new accumulation lattice if (saveNBins != nBins_p) needStorageLattice_p = True; return True; } template Bool LatticeHistograms::setIncludeRange(const Vector& include) // // Assign the desired inclusion range // { if (!goodParameterStatus_p) { return False; } // Save current ranges Vector saveRange(range_p.copy()); // CHeck Bool noInclude; ostringstream os; if (!setInclude(range_p, noInclude, include, os)) { error_p = "Invalid pixel inclusion range"; goodParameterStatus_p = False; return False; } binAll_p = noInclude; // Signal that we need new accumulation lattices if (saveRange.nelements() != range_p.nelements() || !allEQ(saveRange, range_p)) needStorageLattice_p = True; return True; } template Bool LatticeHistograms::setGaussian (const Bool& doGauss) // // Specify whether there should be a Gaussian overlay or not // { if (!goodParameterStatus_p) { return False; } doGauss_p = doGauss; return True; } template Bool LatticeHistograms::setForm (const Bool& doLog, const Bool& doCumu) // // Specify whether the form of the histogram should be linear/log // or cumulative or not. // { if (!goodParameterStatus_p) { return False; } doLog_p = doLog; doCumu_p = doCumu; return True; } template Bool LatticeHistograms::setStatsList (const Bool& doList) // // See if user wants to list statistics as well // { if (!goodParameterStatus_p) { return False; } doList_p = doList; return True; } template Bool LatticeHistograms::setPlotting(PGPlotter& plotter, const Vector& nxy) // // Assign the desired PGPLOT device name and number // of subplots // { if (!goodParameterStatus_p) { return False; } // Is new plotter attached ? if (!plotter.isAttached()) { error_p = "Input plotter is not attached"; goodParameterStatus_p = False; return False; } // Don't reattach to the same plotter. The assignment will // close the previous device if (plotter_p.isAttached()) { if (plotter_p.qid() != plotter.qid()) plotter_p = plotter; } else { plotter_p = plotter; } // Plotting device and subplots. nxy_p is set to [1,1] if zero length nxy_p.resize(0); nxy_p = nxy; ostringstream os; if (!LatticeStatsBase::setNxy(nxy_p, os)) { error_p = "Invalid number of subplots"; goodParameterStatus_p = False; return False; } return True; } template Bool LatticeHistograms::setNewLattice(const MaskedLattice& lattice) // // Assign pointer to lattice // { if (!goodParameterStatus_p) { return False; } DataType latticeType = whatType(); if (latticeType !=TpFloat && latticeType != TpComplex && latticeType != TpDouble) { ostringstream oss; oss << "Lattices of type " << latticeType << " are not currently supported" << endl; error_p = String(oss); goodParameterStatus_p = False; pInLattice_p = 0; return False; } // Clone pointer if (pInLattice_p!=0) delete pInLattice_p; pInLattice_p = lattice.cloneML(); // This is the location of the input SubLattice in // the parent Lattice blcParent_p = pInLattice_p->region().slicer().start(); // Signal that we have changed the lattice and need a new accumulation // lattice needStorageLattice_p = True; return True; } template void LatticeHistograms::closePlotting() { if (plotter_p.isAttached()) plotter_p.detach(); } template Bool LatticeHistograms::display() // // This function displays (plotting and listing) the requested // histograms as a function of the display axes // { if (!goodParameterStatus_p) { return False; } // Generate storage lattices if required if (needStorageLattice_p) { if (!generateStorageLattice()) return False; } // Display histograms displayHistograms (); return True; } template Bool LatticeHistograms::getHistograms( Array& values, Array& counts ) { Array > stats; return getHistograms(values, counts, stats); } template Bool LatticeHistograms::getHistograms( Array& values, Array& counts, Array >& stats ) { if (!goodParameterStatus_p) { return False; } // Generate storage lattices if required if (needStorageLattice_p) { if (!generateStorageLattice()) return False; } // Set up iterator to work through histogram storage lattice line by line // Use the LatticeStepper (default) which will guarantee the access pattern. // There will be no overhang (as tile shape for first axis is length of axis) IPosition cursorShape(pStoreLattice_p->ndim(),1); cursorShape(0) = pStoreLattice_p->shape()(0); IPosition vectorAxis(1,0); // Make the stepper explicitly so we can specify the cursorAxes // and then vectorCursor will cope with an axis of length 1 // (it is possible the user could ask for a histogram with one bin !) LatticeStepper histStepper( pStoreLattice_p->shape(), cursorShape, vectorAxis, IPosition::makeAxisPath(pStoreLattice_p->ndim()) ); RO_LatticeIterator histIterator(*pStoreLattice_p, histStepper); // Resize output arrays and setup vector iterators IPosition shape = pStoreLattice_p->shape(); counts.resize(shape); values.resize(shape); static const IPosition removeAxis(1, 0); stats.resize( shape.size() == 1 ? IPosition(1,1) : shape.removeAxes(removeAxis) ); VectorIterator valuesIterator(values); VectorIterator countsIterator(counts); Vector stat; T linearSum, linearYMax; // Iterate through histogram storage lattice for ( histIterator.reset(),valuesIterator.origin(),countsIterator.origin(); ! histIterator.atEnd(); histIterator++,valuesIterator.next(),countsIterator.next() ) { // Find statistics from the data that made this histogram IPosition pos = histIterator.position(); getStatistics (stat, pos); stats(pos.size() == 1 ? IPosition(1, 0) : pos.removeAxes(removeAxis)).assign(stat); // Extract the histogram in the appropriate form extractOneHistogram( linearSum, linearYMax, valuesIterator.vector(), countsIterator.vector(), stat, histIterator.vectorCursor() ); } return True; } template Bool LatticeHistograms::getHistogram (Vector& values, Vector& counts, const IPosition& pos, const Bool posInLattice) // // Retrieve histogram values and counts from specified // location into vectors // // Inputs: // posInLattice If true the location is given as lattice coordinates // The non-display axis values will be ignored. // Otherwise the position should be for the // display axes only. // { if (!goodParameterStatus_p) { return False; } // Make sure we have a correctly size position if (posInLattice) { if (pos.nelements() != pInLattice_p->ndim()) { error_p = "Incorrectly sized position given"; values.resize(0); counts.resize(0); return False; } } else { if (pos.nelements() != displayAxes_p.nelements()) { error_p = "Incorrectly sized position given"; values.resize(0); counts.resize(0); return False; } } // Generate storage lattices if required if (needStorageLattice_p) { if (!generateStorageLattice()) return False; } // Set position for getting slice from storage lattice const uInt nDim = displayAxes_p.nelements(); IPosition histPos(nDim+1,0); if (posInLattice) { // Discard non display axes for (uInt i=0; i intCounts; pStoreLattice_p->getSlice(intCounts, histPos, sliceShape, IPosition(nDim+1,1), False); // Copy integer counts to a Vector Vector intCountsV(nBins_p); histPos = 0; for (uInt i=0; i statsA; Vector statsT; pStats_p->getStats(statsA, pos, posInLattice); statsT.resize(statsA.nelements()); convertArray (statsT, statsA); // Convert to desired form and make values vector too counts.resize(nBins_p); values.resize(nBins_p); T linearSum, linearYMax; extractOneHistogram (linearSum, linearYMax, values, counts, statsT, intCountsV); return True; } // Private functions template Bool LatticeHistograms::displayHistograms () // // Display the histograms as a function of the display axes // { // Set up for plotting if (plotter_p.isAttached()) { plotter_p.subp(nxy_p(0), nxy_p(1)); plotter_p.ask(True); plotter_p.sch(1.2); plotter_p.svp(0.1,0.9,0.1,0.9); } else { error_p = "Plotter is not attached"; return False; } // Set up iterator to work through histogram storage lattice line by line. // We don't use the TiledLineStepper to guarentee the access pattern is // row based rather than tile based. There will be no overhang because // the tile shape for the histogram axis is the size of the histogram IPosition cursorShape(pStoreLattice_p->ndim(),1); cursorShape(0) = pStoreLattice_p->shape()(0); IPosition vectorAxis(1); vectorAxis(0) = 0; // Make the stepper explicitly so we can specify the cursorAxes // and then vectorCursor will cope with an axis of length 1 // (it is possible the user could ask for a histogram with one bin !) LatticeStepper histStepper(pStoreLattice_p->shape(), cursorShape, vectorAxis, IPosition::makeAxisPath(pStoreLattice_p->ndim())); RO_LatticeIterator histIterator(*pStoreLattice_p, histStepper); // Histogram vectors and other bits and pieces Vector counts(pStoreLattice_p->shape()(0)); Vector values(pStoreLattice_p->shape()(0)); Vector stats; T linearSum, linearYMax; IPosition latticePos(pInLattice_p->ndim(),0); // Iterate through histogram storage lattice for (histIterator.reset(); !histIterator.atEnd(); histIterator++) { // Find statistics from the data that made this histogram getStatistics (stats, histIterator.position()); // Extract histogram in the form requested for plotting extractOneHistogram (linearSum, linearYMax, values, counts, stats, histIterator.vectorCursor()); // Display the histogram if (!displayOneHistogram (linearSum, linearYMax, histIterator.position(), stats, values, counts, plotter_p)) return False; } return True; } template Bool LatticeHistograms::displayOneHistogram (const T& linearSum, const T& linearYMax, const IPosition& histPos, const Vector& stats, const Vector& values, const Vector& counts, PGPlotter& plotter) // // Display the histogram and optionally the equivalent Gaussian // // Inputs // histPos location in histogram storage lattice of start of // this histogram. Remember that the first axis // of the storage lattice has the counts. // { // Are we going to see the Gaussian ? Bool doGauss2 = False; if (doGauss_p && stats(LatticeStatsBase::SIGMA)>0) doGauss2 = True; // Set binwidth const T binWidth = LatticeHistSpecialize::setBinWidth(stats(LatticeStatsBase::MIN), stats(LatticeStatsBase::MAX), nBins_p); // Do plots LatticeHistSpecialize::plot(plotter, doGauss_p, doCumu_p, doLog_p, linearSum, linearYMax, binWidth, values, counts, stats, 0, 1, True); // Write values of the display axes on the plot DataType type = whatType(); Float nchar = 0.5; if (type==TpComplex) nchar = 1.5; String coords = writeCoordinates(histPos); if (!writeDispAxesValues (coords, plotter, nchar)) return False; if (haveLogger_p && doList_p) { // List pixel coordinates of display axes for this histogram // Write statistics to a LogIO object os_p << coords << endl; listStatistics(os_p, stats, binWidth); } return True; } template void LatticeHistograms::extractOneHistogram (T& linearSum, T& linearYMax, Vector& values, Vector& counts, const Vector& stats, const Vector& intCounts) // // Extract this histogram, convert to the appropriate form // and return the values and counts // { // FIsh out min and max Vector range(2); range(0) = stats(LatticeStatsBase::MIN); range(1) = stats(LatticeStatsBase::MAX); // Set bin width const uInt nBins = nBins_p; const T binWidth = LatticeHistSpecialize::setBinWidth(range(0), range(1), nBins); // Copy histogram counts into output T array and generate // values (abcissa) array T xx = range(0) + binWidth/2.0; linearYMax = -1.0; linearSum = 0.0; for (uInt i=0; i::BaseType(1.0) ); } // Make histogram logarithmic if desired if (doLog_p) LatticeHistSpecialize::makeLogarithmic (counts, linearYMax, nBins); } template Bool LatticeHistograms::generateStorageLattice() // // Generate the histogram, and statistics storage lattices. // { // Set the display axes vector if needed if (displayAxes_p.nelements()==0) { displayAxes_p.resize(0); displayAxes_p = IPosition::otherAxes(pInLattice_p->ndim(), cursorAxes_p).asVector(); } // Make the statistics object if (!makeStatistics()) return False; // Fill the histogram storage lattice makeHistograms(); needStorageLattice_p = False; return True; } template void LatticeHistograms::getStatistics (Vector &stats, const IPosition& histPos) const // // Extract statistics slice for the given position in the // histogram storage lattice. // // Input: // histPos The location in the histogram storage lattice // Outputs // stats The statistics for this chunk. { // Discard the histogram axis location uInt n = displayAxes_p.nelements(); IPosition pos; if (n > 0) { pos.resize(n); for (uInt i=0; i statsA; pStats_p->getStats(statsA, pos, False); stats.resize(statsA.nelements()); convertArray (stats, statsA); } template void LatticeHistograms::listStatistics(LogIO& os, const Vector& stats, T binWidth) { // Have to convert LogIO object to ostream before can apply // the manipulators const Int oPrec = 6; setStream(os.output(), oPrec); ostringstream os0, os1, os2, os3, os4, os5, os6, os7; setStream(os0, oPrec); setStream(os1, oPrec); setStream(os2, oPrec); setStream(os3, oPrec); setStream(os4, oPrec); setStream(os5, oPrec); setStream(os6, oPrec); setStream(os7, oPrec); // DataType type = whatType(); Int oWidth; if (type==TpFloat) { oWidth = 15; // } else if (type==TpComplex) { oWidth = 33; // (x, y) } // os << "No. binned = "; os.output() << setw(oWidth) << Int64(std::real(stats(LatticeStatsBase::NPTS))+0.1) << endl; os << "Sum = "; os0 << stats(LatticeStatsBase::SUM); os.output() << setw(oWidth) << String(os0) << " Mean = "; os1 << stats(LatticeStatsBase::MEAN); os.output() << setw(oWidth) << String(os1) << endl; // os << "Variance = "; os2 << stats(LatticeStatsBase::VARIANCE); os.output() << setw(oWidth) << String(os2); // if (stats(LatticeStatsBase::VARIANCE)> 0.0) { os << " Sigma = "; os3 << stats(LatticeStatsBase::SIGMA); os.output() << setw(oWidth) << String(os3) << endl; } else { os << endl; } os << "Rms = "; os4 << stats(LatticeStatsBase::RMS); os.output() << setw(oWidth) << String(os4) << endl; os << endl; os << "Bin width = "; os5 << binWidth; os.output() << setw(oWidth) << String(os5) << endl; os << "Min binned = "; os6 << stats(LatticeStatsBase::MIN); os.output() << setw(oWidth) << String(os6) << " Max binned = "; os7 << stats(LatticeStatsBase::MAX); os.output() << setw(oWidth) << String(os7) << endl << endl << endl; os.post(); } template IPosition LatticeHistograms::locHistInLattice(const IPosition& storagePosition, Bool relativeToParent) const // // Given a location in the histogram storage lattice, convert those locations on // the non-histogram axis (the histogram axis is the first one) to locations // in the original parent lattice. Optionally account for the location of the // subLattice in the parent lattice // { IPosition pos(storagePosition); for (uInt j=1; j Bool LatticeHistograms::makeStatistics() { // Create LatticeStatistics object. Show progress meter. if (pStats_p != 0) delete pStats_p; pStats_p = new LatticeStatistics(*pInLattice_p, os_p, showProgress_p, forceDisk_p); // Set state. Make sure that the min/max is set to the // user's include range if there is one. LatticeHistograms // only allows an inclusion range, and range_p is already // filled with it. Vector exclude; if (!pStats_p->setInExCludeRange(range_p, exclude, True)) return False; if (!pStats_p->setAxes(cursorAxes_p)) return False; // We get an arbitary statistics slice here so as to // activate the statistics object and make it a bit // more obvious to the user the order in which things are done. Vector stats; IPosition pos(displayAxes_p.nelements(),0); if (!pStats_p->getStats(stats, pos, False)) return False; return True; } template void LatticeHistograms::makeHistograms() { if (haveLogger_p) { os_p << LogIO::DEBUG1 << "Creating new histogram storage lattice" << LogIO::POST; } // Set storage lattice shape. The first axis is the histogram axis IPosition storeLatticeShape; LatticeStatsBase::setStorageImageShape(storeLatticeShape, False, Int(nBins_p), displayAxes_p, pInLattice_p->shape()); // Set the storage lattice tile shape to the tile shape of the // axes of the parent lattice from which it is created. // For the histogram axis, set the tile shape to the number of bins // (which probably won't be too big, but could be !) IPosition tileShape(storeLatticeShape.nelements(),1); for (uInt i=1; iniceCursorShape()(displayAxes_p(i-1)); } tileShape(0) = storeLatticeShape(0); // Delete old histogram storage lattice if (pStoreLattice_p != 0) delete pStoreLattice_p; // Create storage lattice uInt memory = HostInfo::memoryTotal()/1024; Double useMemory = Double(memory)/10.0; if (forceDisk_p) useMemory = 0.0; pStoreLattice_p = new TempLattice(TiledShape(storeLatticeShape, tileShape), useMemory); // Create collapser for LatticeApply HistTiledCollapser collapser(pStats_p, nBins_p); LatticeHistProgress* pProgressMeter = 0; if (showProgress_p) pProgressMeter = new LatticeHistProgress(); // This is the first output axis (there is only one in IH) getting // collapsed values Int newOutAxis = 0; // Iterate through lattice and create histograms // Output has to be a MaskedLattice, so make a writable SubLattice. SubLattice outLatt (*pStoreLattice_p, True); LatticeApply::tiledApply(outLatt, *pInLattice_p, collapser, IPosition(cursorAxes_p), newOutAxis, pProgressMeter); if (pProgressMeter != 0) { delete pProgressMeter; pProgressMeter = 0; } } template Bool LatticeHistograms::setInclude(Vector& range, Bool& noInclude, const Vector& include, ostream& os) // // Take the user's data inclusion range // // Inputs: // include Include range given by user. Zero length indicates // no include range // os Output stream for reporting // Outputs: // noInclude If True user did not give an include range // range A pixel value selection range. Will be resized to // zero length if both noInclude and noExclude are True // Bool True if successfull, will fail if user tries to give too // many values for includeB or excludeB, or tries to give // values for both { noInclude = True; range.resize(0); if (include.nelements() == 0) { ; } else if (include.nelements() == 1) { range.resize(2); range(0) = -abs(include(0)); range(1) = abs(include(0)); noInclude = False; } else if (include.nelements() == 2) { range.resize(2); range(0) = min(include(0),include(1)); range(1) = max(include(0),include(1)); noInclude = False; } else { os << endl << "Too many elements for argument include" << endl; return False; } return True; } template String LatticeHistograms::writeCoordinates(const IPosition& histPos) const // // Write pixel coordinates relative to parent lattice // { ostringstream oss; const Int nDisplayAxes = displayAxes_p.nelements(); if (nDisplayAxes > 0) { for (Int j=0; j Bool LatticeHistograms::writeDispAxesValues (const String& coords, PGPlotter& plotter, Float nchar) const { // Fill the string stream with the name and value of each display axis const Int nDisplayAxes = displayAxes_p.nelements(); if (nDisplayAxes > 0) { // Write on plot Vector box(8); box = plotter.qtxt (0.0, 0.0, 0.0, 0.0, "X"); Float dx = box(3) - box(0); const char* tLabel = coords.chars(); box = plotter.qtxt (0.0, 0.0, 0.0, 0.0, tLabel); Float dy = box(5) - box(4); Vector win = plotter.qwin(); Float mx = win(0) + dx; Float my = win(3) + nchar*dy; // Int tbg = plotter.qtbg(); plotter.stbg(0); plotter.ptxt (mx, my, 0.0, 0.0, tLabel); plotter.stbg(tbg); } return True; } template void LatticeHistograms::setStream (ostream& os, Int oPrec) { os.fill(' '); os.precision(oPrec); os.setf(ios::scientific, ios::floatfield); os.setf(ios::left, ios::adjustfield); } // HistTiledCollapser template HistTiledCollapser::HistTiledCollapser(LatticeStatistics* pStats, uInt nBins) : pStats_p(pStats), nBins_p(nBins) {;} template HistTiledCollapser::~HistTiledCollapser() {} template void HistTiledCollapser::init (uInt nOutPixelsPerCollapse) { AlwaysAssert (nOutPixelsPerCollapse == nBins_p, AipsError); } template void HistTiledCollapser::initAccumulator (uInt64 n1, uInt64 n3) // // pHist_p contains the histograms for each chunk // It is T not uInt so we can handle Complex types { pHist_p = new Block(nBins_p*n1*n3); pHist_p->set(0); // n1_p = n1; n3_p = n3; } template void HistTiledCollapser::process ( uInt index1, uInt index3, const T* pInData, const Bool* pInMask, uInt dataIncr, uInt maskIncr, uInt nrval, const IPosition& startPos, const IPosition& ) { // // Process the data in the current chunk. Everything in this // chunk belongs in one output location in the accumulation // lattices // Fish out the min and max for this chunk of the data // from the statistics object typedef typename NumericTraits::PrecisionType AccumType; Vector stats; pStats_p->getStats(stats, startPos, True); ThrowIf( stats.empty(), "Failed to compute statistics, if you set a range you have likely excluded all valid pixels" ); // Assignment from AccumType to T ok (e.g. Double to FLoat) Vector clip(2); clip(0) = stats(LatticeStatsBase::MIN); clip(1) = stats(LatticeStatsBase::MAX); // Set histogram bin width const T binWidth = LatticeHistSpecialize::setBinWidth(clip(0), clip(1), nBins_p); // Fill histograms. uInt offset = (nBins_p*index1) + (nBins_p*n1_p*index3); LatticeHistSpecialize::process( pInData, pInMask, pHist_p, clip, binWidth, offset, nrval, nBins_p, dataIncr, maskIncr ); } template void HistTiledCollapser::endAccumulator(Array& result, Array& resultMask, const IPosition& shape) { // Reshape arrays. The mask is always true. Any locations // in the storage lattice for which there were no valid points // will have the NPTS field set to zero. That is what // we use to effectively mask it. resultMask.resize(shape); resultMask.set(True); result.resize(shape); // Bool deleteRes; T* res = result.getStorage (deleteRes); T* resptr = res; const T* histPtr = pHist_p->storage(); // The histogram storage lattice has the logical shape // [nBins, n1, n3]. for (uInt k=0; k #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template class Lattice; template class MaskedLattice; class IPosition; class LogIO; class Slicer; // Static math functions for Lattices // // // // // //
      • Lattice // // // // Some static helper math functions for Lattices // // // // Common functionality not appropriate for Lattice member functions // // // //
      • nothing I know of // // class LatticeMathUtil { public: // Collapse the specified axes by averaging and recover the // pixel values. If axes is empty, then the data just contains // all of the lattice (i.e. no collapse), // but dropDegenerateAxes is stil honoured template static void collapse (Array& data, const IPosition& axes, const MaskedLattice& in, Bool dropDegenerateAxes); // // Collapse the specified axes by averaging and recover either/and // the pixel values and mask. If axes is empty, then the data and mask just contains // all of the lattice (i.e. no collapse) // but dropDegenerateAxes is stil honoured template static void collapse ( Array& data, Array& mask, const IPosition& axes, const MaskedLattice& lat, Bool dropDegenerateAxes, Bool getPixels=True, Bool getMask=True, const LatticeStatsBase::StatisticsTypes stat=LatticeStatsBase::MEAN ); }; // Global functions on Lattices // // // // // //
      • Lattice // // // // Global functions using Lattices // // // //

        Example 1:

        // Copy the lattice-type data between two Images.// // // PagedImage myImg ("myimagefile"); // Float lmin; // Float lmax; // IPosition posMin = myImg.shape(); // IPosition posMax = myImg.shape(); // minMax( lmin, lmax, posMin, posMax, myImg ); // // //
        // // // // Algorithms like CLEAN need to know the position of the MIN and MAX // of an image, but easy things like LEL's min and max don't tell you // the location of the min and max. It seems there may be other global // functions involving lattices. // // // //
      • nothing I know of // // // // This global function finds the max of a Lattice, and also // the IPositions of the max. (LEL does not get you the IPositions of the // min and max) template void minMax(T & min, T & max, IPosition & posMin, IPosition & posMax, const Lattice& lat); // } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/LatticeMath/LatticeMathUtil.tcc000066400000000000000000000124041476623553700230640ustar00rootroot00000000000000//# LatticeMathUtil.cc: defines the Lattice Utilities global functions//# Copyright (C) 1995,1996,1997,1999,2000,2001,2002,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICEMATHUTIL_TCC #define LATTICES_LATTICEMATHUTIL_TCC #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# namespace casacore begin template void minMax(T & globalMin, T & globalMax, IPosition & globalMinPos, IPosition & globalMaxPos, const Lattice & lat) { //check if IPositions are conformant IPosition zeroPos = IPosition( lat.shape().nelements(), 0); DebugAssert((zeroPos.nelements() == globalMinPos.nelements()), AipsError); DebugAssert((zeroPos.nelements() == globalMaxPos.nelements()), AipsError); IPosition cursorShape(lat.niceCursorShape()); RO_LatticeIterator latIter(lat, cursorShape); globalMin = lat.getAt( zeroPos ); globalMinPos = zeroPos; globalMax = lat.getAt( zeroPos ); globalMaxPos = zeroPos; for(latIter.reset(); !latIter.atEnd(); latIter++) { T localMin; IPosition localMinPos( latIter.cursor().ndim() ); T localMax; IPosition localMaxPos( latIter.cursor().ndim() ); Array arr = latIter.cursor(); minMax(localMin, localMax, localMinPos, localMaxPos, arr); IPosition loc (latIter.position()); if (localMin < globalMin) { globalMin = localMin; globalMinPos = loc + localMinPos; } if (localMax > globalMax) { globalMax = localMax; globalMaxPos = loc + localMaxPos; } } } // LatticeMathUtil template void LatticeMathUtil::collapse (Array& out, const IPosition& axes, const MaskedLattice& in, Bool dropDegenerateAxes) { out.resize(); if (axes.nelements()==0) { out = in.get(dropDegenerateAxes); } else { LatticeStatistics stats(in, False, False); AlwaysAssert(stats.setAxes(axes.asVector()), AipsError); stats.getConvertedStatistic(out, LatticeStatsBase::MEAN, dropDegenerateAxes); } } template void LatticeMathUtil::collapse( Array& data, Array& mask, const IPosition& axes, const MaskedLattice& in, Bool dropDegenerateAxes, Bool getPixels, Bool getMask, const LatticeStatsBase::StatisticsTypes stat ) { data.resize(); mask.resize(); if (axes.nelements()==0) { if (getPixels) data = in.get(dropDegenerateAxes); if (getMask) mask = in.getMask(dropDegenerateAxes); return; } // These lattice are all references so should be reasonably // fast. I can't do it the otherway around, i.e. drop degenerate // axes first with an axes specifier because then the 'axes' // argument won't match one to one with the lattice axes and // that would be confusing. Pity. LatticeStatistics stats(in, False, False); stats.setAxes(axes.asVector()); // if (getPixels) { stats.getConvertedStatistic(data, stat, dropDegenerateAxes); } else { data.resize(IPosition(0,0)); } // CLumsy way to get mask. I should add it to LS if (getMask) { Array n; stats.getConvertedStatistic(n, LatticeStatsBase::NPTS, dropDegenerateAxes); mask.resize(n.shape()); // T lim = ( stat == LatticeStatsBase::SIGMA || stat == LatticeStatsBase::VARIANCE ) ? 1.5 : 0.5; typename Array::const_iterator itend = n.end(); typename Array::const_iterator it; typename Array::iterator mIt; for (it=n.begin(),mIt=mask.begin(); it!=itend; ++it,++mIt) { *mIt = *it >= lim; } } else { mask.resize(); } } } //# End namespace casacore #endif casacore-3.7.1/lattices/LatticeMath/LatticeProgress.cc000066400000000000000000000032251476623553700227560ustar00rootroot00000000000000//# LatticeProgress.cc: Abstract base class to monitor progress in lattice operations //# Copyright (C) 1997 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LatticeProgress::~LatticeProgress() {} void LatticeProgress::init (uInt expectedNsteps) { itsExpectedNsteps = expectedNsteps; initDerived(); } void LatticeProgress::initDerived() {} void LatticeProgress::nstepsDone (uInt) {} void LatticeProgress::done() {} } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LatticeMath/LatticeProgress.h000066400000000000000000000074441476623553700226270ustar00rootroot00000000000000//# LatticeProgress.h: Abstract base class to monitor progress in lattice operations //# Copyright (C) 1997 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICEPROGRESS_H #define LATTICES_LATTICEPROGRESS_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class IPosition; // // Abstract base class to monitor progress in lattice operations // // // // // // This is an abstract base class for classes to monitor the // progress of an operation on a Lattice. The default implementation // offered by this class does nothing. // However, a derived class could show the progress using for example // a ProgressMeter. A derived // class should override the virtual functions from this class. // // The user of the LatticeProgress object should first call // function init with the total number of steps // that are to be done. Thereafter, after each step has been // executed, function nstepsDone should be called // after each step. Finally, function done should // be called. // // // // // // // Since operations on Lattices can take a while, it can be useful // to show the progress. However, making module Lattices dependent on // the class ProgressMeter sounded bad. This abstract class serves // as a bridge between the Lattice module and the ProgressMeter class // (or any other class showing the progress). // // //# //#
      • //# class LatticeProgress { public: LatticeProgress() : itsExpectedNsteps(0) {} virtual ~LatticeProgress(); // Initialize the process. // It sets the expected number of steps and // calls initDerived, so a derived class can initialize itself. void init (uInt expectedNsteps); // Tell the number of steps done so far. // The default implementation does nothing. A derived class // should call the ProgressMeter function update virtual void nstepsDone (uInt nsteps); // The process has ended. virtual void done(); // Recovers the expected number of total steps. uInt expectedNsteps() const { return itsExpectedNsteps; } protected: // Let a derived class initialize itself. // This function is called by init. // The derived class should create the ProgressMeter // in here. virtual void initDerived(); private: uInt itsExpectedNsteps; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LatticeMath/LatticeSlice1D.h000066400000000000000000000124771476623553700222510ustar00rootroot00000000000000//# LatticeSlice1D.h: 1-D slice from a Lattice //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICESLICE1D_H #define LATTICES_LATTICESLICE1D_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class MaskedLattice; class IPosition; class Interpolate2D; class PixelCurve1D; class String; // // Extract a 1-D slice from a Lattice // // // // // //
      • MaskedLattice // // // // // This class extracts an interpolated 1-D slice from a Lattice // with a range of interpolation schemes available. The slice must lie in // the plane of two cardinal axes. // // // // // // // // IPosition shape(2, 20, 30); // Create MaskedLattice // ArrayLattice arrLat(shape); // SubLattice subLat(arrLat); // LatticeSlice1D slicer(subLat); // // IPosition blc(2); blc = 0; // Extract slice between corners // IPosition trc(shape-1); // Vector data; // Vector mask; // slicer.getSlice (data, mask, blc, trc); // // // // Users often want to see cross-cuts through their data. // // //
      • Handle curves not in cardinal axis plane //
      • Derive from MaskedLattice ? // template class LatticeSlice1D { public: // Interpolation method enum Method {NEAREST=0, LINEAR=1, CUBIC=2, N_TYPES}; // Default constructor - object useless LatticeSlice1D (); // Constructor LatticeSlice1D (const MaskedLattice& lattice, Method method=LINEAR); // Copy constructor (reference semantics) LatticeSlice1D(const LatticeSlice1D &other); // Destructor virtual ~LatticeSlice1D (); // Assignment operator (reference semantics) LatticeSlice1D& operator=(const LatticeSlice1D &other); // Get 1-D slice. PixelCurve1D supplies the locus of the slice in // the plane specified by axis0 and axis1. The pixel coordinate for // the rest of the lattice is specified in coord. void getSlice (Vector& data, Vector& mask, const PixelCurve1D& curve, uInt axis0, uInt axis1, const IPosition& coord); // Get 1-D slice between blc & trc. These start and end points must be // in a cardinal plane of the lattice. If nPts is 0 it is set automatically to // the length of the slice. void getSlice (Vector& data, Vector& mask, const IPosition& blc, const IPosition& trc, uInt nPts=0); // Get the (x,y) pixel coordinates from the last slice and the distance along // the slice in pixels.. Also recover the axes of the slice plane void getPosition (uInt& axis0, uInt& axis1, Vector& x, Vector& y, Vector& distance) const; // Recover interpolation method Method interpolationMethod () const {return itsMethod;}; static Method stringToMethod (const String& method); private: // Check the suppliec curve is valid. void checkCurve (IPosition& blc, IPosition& trc, const IPosition& coord, const PixelCurve1D& curve); // Find the slice plane. void findPlane (const IPosition& blc, const IPosition& trc); // Get the interpolated slice void doGetSlice (Vector& data, Vector& mask, const PixelCurve1D& curve, const IPosition& blc, const IPosition& trc); // Make Interpolator void makeInterpolator (Method method); // MaskedLattice* itsLatticePtr; Interpolate2D* itsInterpPtr; Method itsMethod; Vector itsX; Vector itsY; Vector itsPos; uInt itsAxis0; uInt itsAxis1; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/LatticeMath/LatticeSlice1D.tcc000066400000000000000000000211131476623553700225560ustar00rootroot00000000000000//# LatticeSlice1D.cc: 1-D slice from a Lattice //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICESLICE1D_TCC #define LATTICES_LATTICESLICE1D_TCC #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template LatticeSlice1D::LatticeSlice1D() : itsLatticePtr(0), itsInterpPtr(0) {} template LatticeSlice1D::LatticeSlice1D(const MaskedLattice& lattice, Method method) : itsLatticePtr(lattice.cloneML()) { makeInterpolator (method); itsPos.resize(2); } template LatticeSlice1D::LatticeSlice1D (const LatticeSlice1D& other) : itsLatticePtr(0), itsInterpPtr(0) { operator= (other); } template LatticeSlice1D::~LatticeSlice1D() { delete itsLatticePtr; itsLatticePtr = 0; // delete itsInterpPtr; itsInterpPtr = 0; } template LatticeSlice1D& LatticeSlice1D::operator=(const LatticeSlice1D& other) { if (this != &other) { delete itsLatticePtr; itsLatticePtr = other.itsLatticePtr->cloneML(); // delete itsInterpPtr; makeInterpolator (other.interpolationMethod()); // itsPos.resize(0); itsPos = other.itsPos; // itsX.resize(0); itsX = other.itsX; itsY.resize(0); itsY = other.itsY; // itsAxis0 = other.itsAxis0; itsAxis1 = other.itsAxis1; } return *this; } template void LatticeSlice1D::getSlice (Vector& data, Vector& mask, const PixelCurve1D& curve, uInt axis0, uInt axis1, const IPosition& coord) { AlwaysAssert(itsLatticePtr, AipsError); AlwaysAssert(axis0ndim(), AipsError); AlwaysAssert(axis1ndim(), AipsError); // Check PixelCurve is in lattice domain, set blc/trc in plane // and set x,y vectors itsAxis0 = axis0; itsAxis1 = axis1; IPosition blcFull, trcFull; checkCurve (blcFull, trcFull, coord, curve); // Get Slice doGetSlice (data, mask, curve, blcFull, trcFull); } template void LatticeSlice1D::getSlice (Vector& data, Vector& mask, const IPosition& blc, const IPosition& trc, uInt nPts) { AlwaysAssert(itsLatticePtr, AipsError); // Find plane of slice findPlane (blc, trc); // Generate PixelCurve double x1(blc(itsAxis0)), x2(trc(itsAxis0)); double y1(blc(itsAxis1)), y2(trc(itsAxis1)); PixelCurve1D curve(x1, y1, x2, y2, nPts); curve.getPixelCoord (itsX, itsY, 0, curve.npoints()-1, 1); // Get Slice doGetSlice (data, mask, curve, blc, trc); } template void LatticeSlice1D::getPosition (uInt& axis0, uInt& axis1, Vector& x, Vector& y, Vector& distance) const { x.resize(0); x = itsX; y.resize(0); y = itsY; // distance.resize(x.nelements()); distance[0] = 0.0; for (uInt i=1; i void LatticeSlice1D::checkCurve (IPosition& blc, IPosition& trc, const IPosition& coord, const PixelCurve1D& curve) { // Check const uInt nDim = itsLatticePtr->ndim(); if (coord.nelements() != nDim) { throw(AipsError("coord must be of length number of image dimensions")); } // Check curve in domain of lattice [-0.5 -> shape-0.5] const IPosition shape = itsLatticePtr->shape(); const uInt nPts = curve.npoints(); curve.getPixelCoord (itsX, itsY, 0u, nPts-1, 1u); if (itsX[0]<-0.5 || itsY[0]<-0.5) { throw(AipsError("x or y start of curve falls outside of lattice")); } if (itsX[nPts-1]>(shape(itsAxis0)-0.5) || itsY[nPts-1]>(shape(itsAxis1)-0.5)) { throw(AipsError("x or y end of curve falls outside of lattice")); } // Fill in the blc/trc for the slice blc.resize(nDim); trc.resize(nDim); for (uInt i=0; i void LatticeSlice1D::findPlane (const IPosition& blc, const IPosition& trc) { const uInt nDim = itsLatticePtr->ndim(); // if (blc.nelements() != nDim) { throw(AipsError("blc must be of length number of image dimensions")); } if (trc.nelements() != nDim) { throw(AipsError("trc must be of length number of image dimensions")); } // Find the plane; first two axes with non-unit shapes are // assumed to hold the plane to extract the slice from. IPosition shape = trc - blc + 1; Int axis0 = -1; Int axis1 = -1; uInt n = 0; for (uInt i=0; i 1) { n++; if (axis0==-1) { axis0 = i; } else { if (axis1==-1) axis1 = i; } } } // if (n > 2) { throw (AipsError("blc & trc must lie in a plane")); } // if (axis0==-1 || axis1==-1) { throw (AipsError("Could not find plane of slice from blc/trc coordinates")); } // itsAxis0 = axis0; itsAxis1 = axis1; } template void LatticeSlice1D::doGetSlice (Vector& data, Vector& mask, const PixelCurve1D&, const IPosition& blc, const IPosition& trc) { // Get plane holding slice - we know the returned Arrays will be // 2D (checked) so can assign to Matrix safely const IPosition shape = trc - blc + 1; const Matrix& dataIn = itsLatticePtr->getSlice (blc, shape, True); const Matrix& maskIn = itsLatticePtr->getMaskSlice (blc, shape, True); // Interpolate const uInt nPts = itsX.nelements(); data.resize(nPts); mask.resize(nPts); for (uInt i=0; iinterp (data[i], itsPos, dataIn, maskIn); } } template void LatticeSlice1D::makeInterpolator (Method method) { if (method==NEAREST) { itsInterpPtr = new Interpolate2D(Interpolate2D::NEAREST); } else if (method==LINEAR) { itsInterpPtr = new Interpolate2D(Interpolate2D::LINEAR); } else if (method==CUBIC) { itsInterpPtr = new Interpolate2D(Interpolate2D::CUBIC); } itsMethod = method; } template typename LatticeSlice1D::Method LatticeSlice1D::stringToMethod (const String& method) { String typeU = method; typeU.upcase(); // Method method2; String tmp = String(typeU.at(0,1)); if (tmp==String("N")) { method2 = NEAREST; } else if (tmp==String("L")) { method2 = LINEAR; } else if (tmp==String("C")) { method2 = CUBIC; } else { throw (AipsError("Illegal interpolation method")); } // return method2; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LatticeMath/LatticeStatistics.h000066400000000000000000000714461476623553700231600ustar00rootroot00000000000000//# LatticeStatistics.h: generate statistics from a Lattice //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICESTATISTICS_H #define LATTICES_LATTICESTATISTICS_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class MaskedLattice; template class SubLattice; template class TempLattice; class IPosition; #include // // Compute and display various statistics from a lattice // // // // // //
      • LatticeStatsBase //
      • MaskedLattice // // // This is a class designed to display and retrieve statistics from lattices // // // This class enable you to display and/or retrieve statistics evaluated over // specified regions of a lattice. The dimension of the region is arbitrary, but // the size of each dimension is always the shape of the corresponding lattice axis. // The statistics are displayed as a function of location of the axes not // used to evaluate the statistics over. The axes which you evaluate the statistics // over are called the cursor axes, the others are called the display axes. // // For example, consider a lattice cube (call the axes xyz or [0,1,2]). You could // display statistics from xy planes (cursor axes [0,1]) as a function of z (display // axes [2]). Or you could retrieve statistics from the z axis (cursor axes [2]) // for each [x,y] location (display axes [0,1]). // // This class inherits from LatticeStatsBase // This base class provides an enum defining allowed statistics types and a // helper function to convert between a String and a // Vector describing the desired statistics to plot. // An example is shown below. // // This class can list, plot and retrieve statistics. When it lists statistics, // it always lists all the available statistics. When you plot statistics, // you must specify which ones you would like to see. // // This class generates a "storage lattice" into which it writes the accumulated // statistical sums. It is from this storage lattice that the plotting and retrieval // arrays are drawn. The storage lattice is either in core or on disk // depending upon its size (if > 10% of memory given by .aipsrc system.resources.memory // then it goes into a disk-based PagedArray). If on disk, the // storage lattice is deleted when the LatticeStatistics class // object destructs. However, currently, if the process is terminated ungracefully, // the storage lattice will be left over. // // // // This class has a few virtual functions; they are not part of a nice general // polymorphic interface; rather they have specialized functionality. The idea // of these is that you can derive a class from LatticeStatistics, such as // ImageStatistics which provides // you with a little more information when displaying/logging the // statistics (such as world coordinates) // The virtual functions are //
          //
        • getBeamArea can be used to return the synthesized beam // area so that the FLUX statistic can be computed //
        • listStats is used to list the statistics to the logger //
        • getLabelsM find the X-axis label and the title label // for the plotting. //
        //
        // // // If you ignore return error statuses from the functions that set the // state of the class, the internal status of the class is set to bad. // This means it will just keep on returning error conditions until you // explicitly recover the situation. A message describing the last // error condition can be recovered with function errorMessage. // // // //// Construct PagedImage (which isA MaskedLattice) from file name // // PagedImage inImage(inName); // //// Construct statistics object // // LogOrigin or("myClass", "myFunction(...)", WHERE); // LogIO os(or); // LatticeStatistics stats(SubImage(inImage), os); // //// Set cursor axes to see statistics of yz planes (0 relative) // // Vector cursorAxes(2) // cursorAxes(0) = 1; // cursorAxes(1) = 2; // if (!stats.setAxes(cursorAxes)) return 1; // //// Set to list and plot mean, sigma and rms // // if (!stats.setList(True)) return 1; // String device = "/xs"; // Vector nxy(2); // nxy(0) = 1; // nxy(1) = 1; // Vector statsToPlot = LatticeStatsBase::toStatisticTypes("mean,rms,sigma"); // if (!stats.setPlotting(statsToPlot, device, nxy)) return 1; // //// Now activate actual listing and plotting // // if (!stats.display ()) return 1; // //// Retrieve statistics into array // // Array sum; // if (!stats.getStatistic(sum, LatticeStatsBase::SUM)) return 1; // // // In this example, a PagedImage is constructed (which isA // MaskedLattice) with . We set the cursor axes // to be the y and z axes, we specify to list the statistics if we plot them, // and we ask to plot the mean, standard deviation, and root mean square of each // yz plane as a function of x location on the PGPLOT device "/xs" with // 1 subplot per page (there will be only one in this case). After the // plotting and listing, we also retrieve the sum of the selected pixels // as a function of x location into an array. // // // The generation of statistical information from a lattice is a basic // and necessary capability. // // //
      • Implement plotting for complex lattices //
      • Retrieve statistics at specified location of display axes // template class LatticeStatistics : public LatticeStatsBase { public: typedef typename NumericTraits::PrecisionType AccumType; // Constructor takes the lattice and a LogIO object for logging. // You can specify whether you want to see progress meters or not. // You can force the storage lattice to be disk based, otherwise // the decision for core or disk is taken for you. // If clone is True, the input lattice will be cloned, so the caller // can make changes to the input lattice, but the statistics will reflect the // lattice as it was at construction. If False, a reference to the input lattice // is used, and so the caller shouldn't make changes to the input lattice between // construction and calling statistics computation methods, unless it calls setNewLattice() // to update the changed lattice. Obviously, cloning the lattice impacts performance // and memory usage. LatticeStatistics (const MaskedLattice& lattice, LogIO& os, Bool showProgress=True, Bool forceDisk=False, Bool clone=True); // Constructor takes the lattice only. In the absence of a logger you get no messages. // This includes error messages and potential listing of the statistics. // You can specify whether you want to see progress meters or not. // You can force the storage lattice to be disk based, otherwise // the decision for core or disk is taken for you. LatticeStatistics (const MaskedLattice& lattice, Bool showProgress=True, Bool forceDisk=False, Bool clone=True); // Copy constructor. Copy semantics are followed. Therefore any storage lattice // that has already been created for other is copied to *this LatticeStatistics(const LatticeStatistics &other); // Destructor virtual ~LatticeStatistics (); // Assignment operator. Deletes any storage lattice associated with // the object being assigned to and copies any storage lattice that has // already been created for "other". LatticeStatistics &operator=(const LatticeStatistics &other); // Set the cursor axes (0 relative). A return value of False // indicates you have asked for an invalid axis. The default state of the class // is to set the cursor axes to all axes in the lattice. Bool setAxes (const Vector& cursorAxes); // You may specify a pixel intensity range as either one for which // all pixels in that range are included or one for which all pixels // in that range are excluded. One or the other of include // and exclude must therefore be a zero length vector if you // call this function. If you are setting an include // range, then if you set setMinMaxToInclude=True, the // minimum and maximum values that this class returns will always be // the minimum and maximum of the include range, respectively. // A return value of False indicates that // you have given both an include and an exclude // range. A vector of length 1 for include and/or exclude // means that the range will be set to (say for include) // -abs(include(0)) to abs(include(0)). A return value // of False indicates that both an inclusion and exclusion // range were given or that the internal state of the class is bad. If you don't // call this function, the default state of the class is to include all pixels. Bool setInExCludeRange(const Vector& include, const Vector& exclude, Bool setMinMaxToInclude=False); // This function allows you to control whether the statistics are written to // the output stream if you are also making a plot. A return value of // False indicates that the internal state of the class is bad. // If you have created the LatticeStatistics object without // a LogIO object, you won't see any listings, but no error // conditions will be generated. The default state of the class is to // not list the output when making a plot. Bool setList(const Bool& doList); // Display the statistics by listing and/or plotting them. If you don't call // this function then you won't see anything ! A return value of False // indicates an invalid plotting device, or that the internal state of the class is bad. Bool display(); Bool getLayerStats(String& stats, Double area, Int zAxis=-1, Int zLayer=-1, Int hAxis=-1, Int hLayer=-1); typedef std::pair stat_element; typedef std::list stat_list; Bool getLayerStats( stat_list &stats, Double area, Int zAxis=-1, Int zLayer=-1, Int hAxis=-1, Int hLayer=-1); // Return the display axes. The returned vector will be valid only if setAxes // has been called, or if one of the active "display" or "get*" methods has been called. Vector displayAxes() const {return displayAxes_p;} // Recover the desired Statistic into an array. If you choose to use // the T version, be aware that the values in the AccumType version of the // Array may not be representable in the T version (e.g. large values for // SumSq). The shape of the // array is the shape of the display axes (e.g. if the shape of the lattice is // [nx,ny,nz] and you ask for the mean of the y axis the shape of the returned // array would be [nx,nz]. A returned array of zero shape indicates that there // were no good values. A return value of False // indicates that the internal state of the class is bad. // Bool getStatistic (Array& stat, LatticeStatsBase::StatisticsTypes type, Bool dropDeg=True); Bool getConvertedStatistic (Array& stat, LatticeStatsBase::StatisticsTypes type, Bool dropDeg=True); // // Recover position of min and max. Only works if there are no // display axes (i.e. statistics found over entire image), otherwise, // the returned values are resized to 0 shape. A return // value of False indicates that the internal state of // the class is bad. Bool getMinMaxPos(IPosition& minPos, IPosition& maxPos); // This function gets a vector containing all the statistics // for a given location. If posInLattice=True then // the location is a location in the input lattice. Any // positions on the display axes are ignored. Otherwise, you // should just give locations for the display axes only. // Use can use the enum in class LatticeStatsBase to find out // which locations in the vector contain which statistics. // A returned vector of zero shape indicates that there // were no good values. A return value of False // indicates that the internal state of the class is bad. Bool getStats (Vector&, const IPosition& pos, const Bool posInLattice=False); // Reset argument error condition. If you specify invalid arguments to // one of the above set functions, an internal flag will be set which will // prevent the work functions from doing anything (should you have chosen // to ignore the Boolean return values of the set functions). // This function allows you to reset that internal state to good. void resetError () {goodParameterStatus_p = True;}; // Get full lattice min and max only. Returns False if no unmasked data, else returns True. // Honours any include or exclude range if set. Bool getFullMinMax (T& dataMin, T& dataMax); // Recover last error message String errorMessage() const {return error_p;}; // Set a new MaskedLattice object. A return value of False indicates the // lattice had an invalid type or that the internal state of the class is bad. // If clone is True, the input lattice will be cloned, so the caller // can make changes to the input lattice, but the statistics will reflect the // lattice as it was at construction. If False, a reference to the input lattice // is used, and so the caller shouldn't make changes to the input lattice between // construction and calling statistics computation methods, unless it calls setNewLattice() // to update the changed lattice. Obviously, cloning the lattice impacts performance // and memory usage. Bool setNewLattice(const MaskedLattice& lattice, Bool clone=True); // Did we construct with a logger ? Bool hasLogger () const {return haveLogger_p;}; // The configure methods return True if reconfiguration is actually // necessary (ie if the underlying storage lattice needs to be recomputed). // If no reconfiguration is necessary, False is returned. // configure to use biweight algorithm. Bool configureBiweight(Int maxIter, Double c); // configure object to use Classical Statistics // The time, t_x, it takes to compute classical statistics using algorithm x, can // be modeled by // t_x = n_sets*(a_x + b_x*n_el) // where n_sets is the number of independent sets of data to compute stats on, // each containing n_el number of elements. a_x is the time it takes to compute // stats a a single set of data, and b_x is the time it takes to accumulate // a single point. // The old algorithm was developed in the early history of the project, I'm guessing // by Neil Kileen, while the new algorithm was developed in 2015 by Dave Mehringer // as part of the stats framework project. The old algorithm is faster in the regime // of large n_sets and small n_el, while the new algorithm is faster in the // regime of small n_sets and large n_el. // If one always wants to use one of these algorithms, that algorithm's coefficients // should be set to 0, while setting the other algorithm's coefficients to positive // values. Note that it's the relative, not the absolute, values of these // coeffecients that is important // The version that takes no parameters uses the default values of the coefficients; // Bool configureClassical(); Bool configureClassical(Double aOld, Double bOld, Double aNew, Double bNew); // // configure to use fit to half algorithm. Bool configureFitToHalf( FitToHalfStatisticsData::CENTER centerType=FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::USE_DATA useData=FitToHalfStatisticsData::LE_CENTER, AccumType centerValue=0 ); // configure to use hinges-fences algorithm Bool configureHingesFences(Double f); // configure to use Chauvenet's criterion Bool configureChauvenet( Double zscore=-1, Int maxIterations=-1 ); // // The force* methods are really only for testing. They in general shouldn't // be called in production code. The last one to be called will be the one to // be attempted to be used. void forceUseStatsFrameworkUsingDataProviders(); void forceUseStatsFrameworkUsingArrays(); void forceUseOldTiledApplyMethod(); // void forceAllowCodeDecideWhichAlgortihmToUse(); // get number of iterations associated with Chauvenet criterion algorithm std::map getChauvenetNiter() const { return _chauvIters; } // should quantile-like stats (median, quartiles, medabsdevmed) be computed? // When the stats framework is used, It is better to set this before computing // any statistics, to avoid unnecessary duplicate creations of the // stats algorithm objects. Unnecessary recreation of these is a performance // bottleneck for iterative stats algorithms (eg Chauvenet), especially for // large images (CAS-10947/10948). void setComputeQuantiles(Bool b); protected: LogIO os_p; Vector cursorAxes_p, displayAxes_p; Bool goodParameterStatus_p; Bool haveLogger_p, fixedMinMax_p; // doRobust means that when the storage lattice is generated, the // robust statistics are generated as well Bool doRobust_p; Bool doList_p; IPosition minPos_p, maxPos_p, blcParent_p; String error_p; // // Virtual Functions. See implementation to figure it all out ! // FIXME The indirect dependence of this class on ImageInterface related // issues (eg flux density) breaks encapsulation. All the ImageInterface related code should be // encapsulated in ImageStatistics. Unfortunately, that requires significantly // more time than I have atm. A return value of False means that the object in // question cannot compute flux density values. The default implementation returns False. virtual Bool _canDoFlux() const { return False; } virtual Quantum _flux(Bool&, AccumType, Double) const { ThrowCc("Logic Error: This object cannot compute flux density"); } virtual void listMinMax (ostringstream& osMin, ostringstream& osMax, Int oWidth, DataType type); // // List the statistics to the logger. The implementation here // is adequate for all lattices. See ImageStatistics for an // example of where extra information is provided. hasBeam is // the return value of getBeamArea. If it is true, that means // that the FLUX statistics will be available in the storage // lattice. dPos is the location of the start of the cursor in the // storage image for this row. stats(j,i) is the statistics matrix. // for the jth point and the ith statistic. // The return value is False if something goes wrong ! // Have a look at the implementation to see what you really // have to do. virtual Bool listStats (Bool hasBeam, const IPosition& dPos, const Matrix& ord); virtual Bool listLayerStats ( const Matrix& ord, ostringstream& rslt, Int zLayer); // Given a location in the storage lattice, convert those locations on the // non-statistics axis (the last one) and optionally account for the // lattice subsectioning IPosition locInLattice (const IPosition& storagePosition, Bool relativeToParent=True) const; // Non-virtual functions // // set stream manipulators void setStream (ostream& os, Int oPrec); // get the storage lattice shape inline IPosition _storageLatticeShape() const { return pStoreLattice_p->shape(); } virtual Bool _computeFlux( Array& flux, const Array& npts, const Array& sum ); virtual Bool _computeFlux( Quantum& flux, AccumType sum, const IPosition& pos, Bool posInLattice ); // convert a position in the input lattice to the corresponding // position in the stats storage lattice. The number of elements // in storagePos will not be changed and only the first N elements // will be modified where N = the number of elements in latticePos. // storagePos must therefore have at least as many elements // as latticePos. Returns False if //latticePos is inconsistent with the input lattice. void _latticePosToStoragePos( IPosition& storagePos, const IPosition& latticePos ); StatisticsData::ALGORITHM _getAlgorithm() const; private: enum LatticeStatsAlgorithm { STATS_FRAMEWORK_ARRAYS, STATS_FRAMEWORK_DATA_PROVIDERS, TILED_APPLY }; const MaskedLattice* pInLattice_p; std::shared_ptr> _inLatPtrMgr; std::shared_ptr> pStoreLattice_p; Vector nxy_p, statsToPlot_p; Vector range_p; Bool noInclude_p, noExclude_p; Bool needStorageLattice_p, doneSomeGoodPoints_p, someGoodPointsValue_p; Bool showProgress_p, forceDisk_p; T minFull_p, maxFull_p; Bool doneFullMinMax_p; StatisticsAlgorithmFactory _saf; std::map _chauvIters; Double _aOld, _bOld, _aNew, _bNew; // unset means let the code decide std::unique_ptr _latticeStatsAlgortihm; void _setDefaultCoeffs() { // coefficients from timings run on PagedImages on // etacarinae.cv.nrao.edu (dmehring's development // machine) _aOld = 4.7e-7; _bOld = 2.3e-8; _aNew = 1.6e-5; _bNew = 1.5e-8; } // Summarize the statistics found over the entire lattice virtual void summStats(); virtual void displayStats( AccumType nPts, AccumType sum, AccumType median, AccumType medAbsDevMed, AccumType quartile, AccumType sumSq, AccumType mean, AccumType var, AccumType rms, AccumType sigma, AccumType dMin, AccumType dMax, AccumType q1, AccumType q3 ); // Calculate statistic from storage lattice and return in an array Bool calculateStatistic (Array& slice, LatticeStatsBase::StatisticsTypes type, Bool dropDeg); template void _computeQuantiles( AccumType& median, AccumType& medAbsDevMed, AccumType& q1, AccumType& q3, std::shared_ptr> statsAlg, uInt64 knownNpts, AccumType knownMin, AccumType knownMax ) const; template void _computeQuantilesForStatsFramework( StatsData& stats, AccumType& q1, AccumType& q3, std::shared_ptr> statsAlg ) const; // Find the median per cursorAxes chunk void generateRobust (); // Create a new storage lattice Bool generateStorageLattice (); // Given a location in the lattice and a statistic type, work // out where to put it in the storage lattice IPosition locInStorageLattice(const IPosition& latticePosition, LatticeStatsBase::StatisticsTypes type) const; // Find min and max of good data in arrays specified by pointers void minMax (Bool& none, AccumType& dMin, AccumType& dMax, const Vector& d, const Vector& n) const; // Retrieve a statistic from the storage lattice and return in an array Bool retrieveStorageStatistic (Array& slice, const LatticeStatsBase::StatisticsTypes type, const Bool dropDeg); // Retrieve a statistic from the storage lattice at the specified // location and return in an array Bool retrieveStorageStatistic (Vector& slice, const IPosition& pos, const Bool posInLattice); // Find the shape of slice from the statistics lattice at one // spatial pixel IPosition statsSliceShape () const; // See if there were some valid points found in the storage lattice Bool someGoodPoints (); // Stretch min and max by 5% void stretchMinMax (AccumType& dMin, AccumType& dMax) const; void _configureDataProviders( LatticeStatsDataProvider& lattDP, MaskedLatticeStatsDataProvider& maskedLattDP ) const; void _doStatsLoop(uInt nsets, std::shared_ptr progressMeter); void _computeStatsUsingArrays( std::shared_ptr progressMeter, const IPosition& cursorShape ); void _computeStatsUsingLattDataProviders( LatticeStepper& stepper, SubLattice subLat, Slicer& slicer, std::shared_ptr progressMeter, uInt nsets ); IPosition _cursorShapeForArrayMethod(uInt64 setSize) const; void _doComputationUsingArrays( std::vector< std::shared_ptr< StatisticsAlgorithm< AccumType, typename Array::const_iterator, Array::const_iterator > > >& sa, T& overallMin, T& overallMax, IPosition& arrayShape, std::vector>& dataArray, std::vector>& maskArray, std::vector& curPos, uInt nthreads, Bool isChauv, Bool isMasked, Bool isReal, std::shared_ptr range ); void _fillStorageLattice( T currentMin, T currentMax, const IPosition& curPos, const StatsData& stats, Bool doQuantiles, AccumType q1=0, AccumType q3=0 ); inline static AccumType _mean(const AccumType& sum, const AccumType& npts) { return npts <= 0 ? 0 : sum/npts; } inline static AccumType _rms(const AccumType& sumsq, const AccumType& npts) { return npts <= 0 ? 0 : sqrt(sumsq/npts); } void _updateMinMaxPos( T& overallMin, T& overallMax, T currentMin, T currentMax, const IPosition& minPos, const IPosition& maxPos, Bool atStart ); }; //# Declare extern templates for often used types. extern template class LatticeStatistics; } //# NAMESPACE CASA - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/LatticeMath/LatticeStatistics.tcc000066400000000000000000002671451476623553700235050ustar00rootroot00000000000000//# LatticeStatistics.cc: generate statistics from a MaskedLattice //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICESTATISTICS_TCC #define LATTICES_LATTICESTATISTICS_TCC #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template LatticeStatistics::LatticeStatistics (const MaskedLattice& lattice, LogIO& os, Bool showProgress, Bool forceDisk, Bool clone) // // Constructor // : os_p(os), goodParameterStatus_p(True), haveLogger_p(True), fixedMinMax_p(False), doRobust_p(False), doList_p(False), error_p(""), pInLattice_p(0), pStoreLattice_p(0), noInclude_p(True), noExclude_p(True), needStorageLattice_p(True), doneSomeGoodPoints_p(False), someGoodPointsValue_p(False), showProgress_p(showProgress), forceDisk_p(forceDisk), doneFullMinMax_p(False), _saf(), _chauvIters(), _latticeStatsAlgortihm() { nxy_p.resize(0); statsToPlot_p.resize(0); range_p.resize(0); minPos_p.resize(0); maxPos_p.resize(0); blcParent_p.resize(0); configureClassical(); if (setNewLattice(lattice, clone)) { // Cursor axes defaults to all Vector cursorAxes; goodParameterStatus_p = setAxes(cursorAxes); } else { goodParameterStatus_p = False; } } template LatticeStatistics::LatticeStatistics (const MaskedLattice& lattice, Bool showProgress, Bool forceDisk, Bool clone) // // Constructor // : goodParameterStatus_p(True), haveLogger_p(False), fixedMinMax_p(False), doRobust_p(False), doList_p(False), error_p(""), pInLattice_p(0), pStoreLattice_p(0), noInclude_p(True), noExclude_p(True), needStorageLattice_p(True), doneSomeGoodPoints_p(False), someGoodPointsValue_p(False), showProgress_p(showProgress), forceDisk_p(forceDisk), doneFullMinMax_p(False), _saf(), _chauvIters(), _latticeStatsAlgortihm() { nxy_p.resize(0); statsToPlot_p.resize(0); range_p.resize(0); minPos_p.resize(0); maxPos_p.resize(0); blcParent_p.resize(0); configureClassical(); if (setNewLattice(lattice, clone)) { // Cursor axes defaults to all Vector cursorAxes; goodParameterStatus_p = setAxes(cursorAxes); } else { goodParameterStatus_p = False; } } template LatticeStatistics::LatticeStatistics(const LatticeStatistics &other) : pInLattice_p(0), pStoreLattice_p(0), _saf(other._saf), _chauvIters(other._chauvIters), _aOld(other._aOld), _bOld(other._bOld), _aNew(other._aNew), _bNew(other._bNew), _latticeStatsAlgortihm( other._latticeStatsAlgortihm ? new LatticeStatsAlgorithm(*other._latticeStatsAlgortihm) : nullptr ) // // Copy constructor. Storage lattice is not copied. // { operator=(other); } template LatticeStatistics &LatticeStatistics::operator=(const LatticeStatistics &other) // // Assignment operator. Storage lattice is not copied // { if (this != &other) { // Deal with lattice pointer _inLatPtrMgr.reset(other.pInLattice_p->cloneML()); pInLattice_p = _inLatPtrMgr.get(); // Delete storage lattice pStoreLattice_p.reset(); needStorageLattice_p = True; // Do the rest os_p = other.os_p; cursorAxes_p.resize(other.cursorAxes_p.shape()); cursorAxes_p = other.cursorAxes_p; displayAxes_p.resize(other.displayAxes_p.size()); displayAxes_p = other.displayAxes_p; nxy_p.resize(other.nxy_p.size()); nxy_p = other.nxy_p; statsToPlot_p.resize(other.statsToPlot_p.size()); statsToPlot_p = other.statsToPlot_p; range_p.resize(other.range_p.size()); range_p = other.range_p; doList_p = other.doList_p; noInclude_p = other.noInclude_p; noExclude_p = other.noExclude_p; goodParameterStatus_p = other.goodParameterStatus_p; doneSomeGoodPoints_p = other.doneSomeGoodPoints_p; someGoodPointsValue_p = other.someGoodPointsValue_p; haveLogger_p = other.haveLogger_p; showProgress_p = other.showProgress_p; fixedMinMax_p = other.fixedMinMax_p; minPos_p.resize(other.minPos_p.size()); minPos_p = other.minPos_p; maxPos_p.resize(other.maxPos_p.size()); maxPos_p = other.maxPos_p; blcParent_p.resize(other.blcParent_p.size()); blcParent_p = other.blcParent_p; forceDisk_p = other.forceDisk_p; doRobust_p = other.doRobust_p; doList_p = other.doList_p; error_p = other.error_p; doneFullMinMax_p= other.doneFullMinMax_p; minFull_p = other.minFull_p; maxFull_p = other.maxFull_p; _saf = other._saf; _chauvIters = other._chauvIters; _aNew = other._aNew; _bNew = other._bNew; _aOld = other._aOld; _bOld = other._bOld; _latticeStatsAlgortihm.reset( other._latticeStatsAlgortihm ? new LatticeStatsAlgorithm(*other._latticeStatsAlgortihm) : nullptr ); } return *this; } template LatticeStatistics::~LatticeStatistics() {} template Bool LatticeStatistics::setAxes (const Vector& axes) // // This function sets the cursor axes and the display axes // { if (!goodParameterStatus_p) { return False; } // Save current cursor axes Vector saveAxes(cursorAxes_p.copy()); // Assign cursor axes. cursorAxes_p.resize(0); cursorAxes_p = axes; uInt ndim = pInLattice_p->ndim(); if (cursorAxes_p.nelements() == 0) { // User didn't give any axes. Set them to all. cursorAxes_p.resize(ndim); for (uInt i=0; i::sort(cursorAxes_p, Sort::Ascending, Sort::QuickSort|Sort::NoDuplicates); // for (uInt i=0; i Int(ndim - 1)) { ostringstream oss; oss << "Invalid cursor axes: " << axes; error_p = oss.str(); return False; } } } // Signal that we have changed the axes and need a new storage lattice if (saveAxes.nelements() != cursorAxes_p.nelements() || !allEQ(saveAxes, cursorAxes_p)) needStorageLattice_p = True; // Set the display axes vector. We also do this in generateStorageLattice // but it is possible the user will want to see the display axes // via the public function "displayAxes" before any real work is done // so poke this in here too. displayAxes_p.resize(0); displayAxes_p = IPosition::otherAxes(ndim, cursorAxes_p).asVector(); return True; } template void LatticeStatistics::setComputeQuantiles(Bool b) { doRobust_p = b; } template Bool LatticeStatistics::setInExCludeRange(const Vector& include, const Vector& exclude, Bool setMinMaxToInclude) // Assign the desired exclude range { if (!goodParameterStatus_p) { return False; } // Save current ranges Vector saveRange(range_p.copy()); Bool saveFixedMinMax = fixedMinMax_p; // Check ostringstream os; Bool saveNoInclude = noInclude_p; Bool saveNoExclude = noExclude_p; if (!LattStatsSpecialize::setIncludeExclude(error_p, range_p, noInclude_p, noExclude_p, include, exclude)) { goodParameterStatus_p = False; return False; } // Can't have fixed min and max with an exclusion range fixedMinMax_p = setMinMaxToInclude; if (!noExclude_p && fixedMinMax_p) { if (haveLogger_p) { error_p = "Can't have a fixed min and max with an exclusion range"; } goodParameterStatus_p = False; return False; } // Can only have fixed min and max range if user gives it if (noInclude_p) fixedMinMax_p = False; // Signal that we have changed the pixel range and need a new storage lattice if ( saveNoInclude != noInclude_p || saveNoExclude != noExclude_p || saveFixedMinMax != fixedMinMax_p || saveRange.size() != range_p.size() || !allEQ(saveRange, range_p) ) { needStorageLattice_p = True; doneFullMinMax_p = False; } return True; } template Bool LatticeStatistics::setList (const Bool& doList) // // See if user wants to list statistics as well as plot them // { if (!goodParameterStatus_p) { return False; } doList_p = doList; return True; } template Bool LatticeStatistics::setNewLattice( const MaskedLattice& lattice, Bool clone ) { if (!goodParameterStatus_p) { return False; } DataType latticeType = whatType(); if (latticeType != TpFloat && latticeType != TpComplex && latticeType != TpDouble) { ostringstream oss; oss << "Statistics cannot yet be evaluated from lattices of type : " << latticeType << endl; error_p = oss.str(); goodParameterStatus_p = False; return False; } if (clone) { _inLatPtrMgr.reset(lattice.cloneML()); pInLattice_p = _inLatPtrMgr.get(); } else { _inLatPtrMgr.reset(); pInLattice_p = &lattice; } // This is the location of the input SubLattice in // the parent Lattice blcParent_p = pInLattice_p->region().slicer().start(); // Signal that we have changed the lattice and need a new storage lattice needStorageLattice_p = True; return True; } template Bool LatticeStatistics::getConvertedStatistic (Array& stats, LatticeStatsBase::StatisticsTypes type, Bool dropDeg) { Array tmp; Bool ok = getStatistic(tmp, type, dropDeg); stats.resize(tmp.shape()); convertArray(stats, tmp); return ok; } template StatisticsData::ALGORITHM LatticeStatistics::_getAlgorithm() const { return _saf.algorithm(); } template Bool LatticeStatistics::getStatistic( Array& stats, LatticeStatsBase::StatisticsTypes type, Bool dropDeg ) { if (_getAlgorithm() == StatisticsData::BIWEIGHT) { ThrowIf( type == LatticeStatsBase::FLUX, "The biweight algorithm does not support" "computation of the flux" ); ThrowIf( type == LatticeStatsBase::RMS, "The biweight algorithm does not support" "computation of the rms" ); ThrowIf( type == LatticeStatsBase::SUM, "The biweight algorithm does not support" "computation of the sum" ); ThrowIf( type == LatticeStatsBase::SUMSQ, "The biweight algorithm does not support" "computation of the sum of squres" ); ThrowIf( type == LatticeStatsBase::VARIANCE, "The biweight algorithm does not support" "computation of the variance" ); ThrowIf( type == LatticeStatsBase::MEDIAN || type == LatticeStatsBase::MEDABSDEVMED || type == LatticeStatsBase::QUARTILE || type == LatticeStatsBase::Q1 || type == LatticeStatsBase::Q3, "The biweight algorithm does not support" "computation of quantile or quantile-like values" ); } if (!goodParameterStatus_p) { return False; } if (needStorageLattice_p) { generateStorageLattice(); } if (type==LatticeStatsBase::NPTS) { return retrieveStorageStatistic(stats, NPTS, dropDeg); } else if (type==LatticeStatsBase::SUM) { return retrieveStorageStatistic(stats, SUM, dropDeg); } else if (type==LatticeStatsBase::SUMSQ) { return retrieveStorageStatistic(stats, SUMSQ, dropDeg); } else if ( type == LatticeStatsBase::MEDIAN || type == LatticeStatsBase::MEDABSDEVMED || type == LatticeStatsBase::QUARTILE || type == LatticeStatsBase::Q1 || type == LatticeStatsBase::Q3 ) { if (!doRobust_p) { doRobust_p = True; generateRobust(); } return retrieveStorageStatistic(stats, type, dropDeg); } else if (type==LatticeStatsBase::MIN) { return retrieveStorageStatistic(stats, MIN, dropDeg); } else if (type==LatticeStatsBase::MAX) { return retrieveStorageStatistic(stats, MAX, dropDeg); } else if (type==LatticeStatsBase::MEAN) { if (_saf.algorithm() == StatisticsData::BIWEIGHT) { return retrieveStorageStatistic(stats, MEAN, dropDeg); } // we prefer to calculate the mean rather than use the accumulated value // because the accumulated value may include accumulated finite precision errors // return retrieveStorageStatistic(stats, MEAN, dropDeg); return calculateStatistic(stats, MEAN, dropDeg); } else if (type==LatticeStatsBase::VARIANCE) { return retrieveStorageStatistic(stats, VARIANCE, dropDeg); } else if (type==LatticeStatsBase::SIGMA) { retrieveStorageStatistic(stats, SIGMA, dropDeg); } else if (type==LatticeStatsBase::RMS) { return calculateStatistic (stats, RMS, dropDeg); } else if (type==LatticeStatsBase::FLUX) { return calculateStatistic (stats, FLUX, dropDeg); } return True; } template Bool LatticeStatistics::getStats( Vector& stats, const IPosition& pos, const Bool posInLattice ) { // This function retrieves the statistics from the storage // lattice at the specified location. // Inputs // posInLattice If true the location is given as image coordinates // The non-display axis values will be ignored. // Otherwise the position should be for the // display axes only. // Check class status if (!goodParameterStatus_p) { return False; } // Retrieve storage array statistics stats.resize(NSTATS); if (!retrieveStorageStatistic(stats, pos, posInLattice)) { return False; } // Compute the rest const AccumType& n = stats(NPTS); if (n <= 0) { stats.resize(0); return True; } stats(RMS) = _rms(stats(SUMSQ), n); stats(FLUX) = 0; if (_canDoFlux()) { Quantum q; if (! _computeFlux(q, stats(SUM), pos, posInLattice)) { return False; } stats(FLUX) = q.getValue(); } return True; } template Bool LatticeStatistics::getMinMaxPos(IPosition& minPos, IPosition& maxPos) { ThrowIf( _saf.algorithm() == StatisticsData::BIWEIGHT, "The biweight algorithm does not support " "computing minimum and maximum positions" ); if (!goodParameterStatus_p) { return False; } // Generate storage lattice if required if (needStorageLattice_p) { if (!generateStorageLattice()) return False; } if (displayAxes_p.nelements() == 0) { minPos.resize(minPos_p.nelements()); minPos = minPos_p; maxPos.resize(maxPos_p.nelements()); maxPos = maxPos_p; } else { minPos.resize(0); maxPos.resize(0); } return True; } template Bool LatticeStatistics::getFullMinMax(T& dataMin, T& dataMax) { if (!doneFullMinMax_p) { // Specialize LattStatsSpecialize::minMax (minFull_p, maxFull_p, pInLattice_p, range_p, noInclude_p, noExclude_p); doneFullMinMax_p = True; } // dataMin = minFull_p; dataMax = maxFull_p; // return (maxFull_p > minFull_p); } // Private functions template Bool LatticeStatistics::_computeFlux( Array&, const Array&, const Array& ) { ThrowCc("This object does not support computing fluxes"); } template Bool LatticeStatistics::_computeFlux( Quantum&, AccumType, const IPosition&, Bool ) { ThrowCc("This object does not support computing fluxes"); } template Bool LatticeStatistics::calculateStatistic (Array& slice, LatticeStatsBase::StatisticsTypes type, Bool dropDeg) // // Calculate desired statistic from storage lattice and return in array // // Input/output: // slice The statistics are returned in this array. WIll be of zero // size on output if there were no good points. // { // Rezize slice to nothing first slice.resize(IPosition(0,0)); // Generate storage lattice if required if (needStorageLattice_p) { if (!generateStorageLattice()) return False; } // Return asap if no good points if (!someGoodPoints()) return True; // Retrieve nPts statistics Array nPts; retrieveStorageStatistic (nPts, NPTS, dropDeg); ReadOnlyVectorIterator nPtsIt(nPts); const uInt n1 = nPtsIt.vector().nelements(); // Setup slice.resize(nPts.shape()); slice = 0.0; VectorIterator sliceIt(slice); // Do it Array sum; Array sumSq; if (type==MEAN) { retrieveStorageStatistic (sum, SUM, dropDeg); ReadOnlyVectorIterator sumIt(sum); AccumType npts(0); while (!nPtsIt.pastEnd()) { for (uInt i=0; i sumSqIt(sumSq); AccumType npts = 0; while (!nPtsIt.pastEnd()) { for (uInt i=0; i Bool LatticeStatistics::configureBiweight(Int maxIter, Double c) { Bool reconfig = _saf.algorithm() != StatisticsData::BIWEIGHT; if (! reconfig) { StatisticsAlgorithmFactoryData::BiweightData data = _saf.biweightData(); reconfig = maxIter != data.maxIter || ! near(c, data.c); } if (reconfig) { _saf.configureBiweight(maxIter, c); needStorageLattice_p = True; } return reconfig; } template Bool LatticeStatistics::configureClassical() { Bool reconfig = False; if (_saf.algorithm() != StatisticsData::CLASSICAL) { _saf.configureClassical(); needStorageLattice_p = True; reconfig = True; } _setDefaultCoeffs(); return reconfig; } template Bool LatticeStatistics::configureClassical( Double aOld, Double bOld, Double aNew, Double bNew ) { Bool reconfig = False; if (_saf.algorithm() != StatisticsData::CLASSICAL) { _saf.configureClassical(); needStorageLattice_p = True; reconfig = True; } _aOld = aOld; _bOld = bOld; _aNew = aNew; _bNew = bNew; return reconfig; } template Bool LatticeStatistics::configureHingesFences(Double f) { Bool reconfig = False; if ( _saf.algorithm() != StatisticsData::HINGESFENCES || ! near(f, _saf.hingesFencesFactor()) ) { _saf.configureHingesFences(f); needStorageLattice_p = True; reconfig = True; } return reconfig; } template Bool LatticeStatistics::configureFitToHalf( FitToHalfStatisticsData::CENTER centerType, FitToHalfStatisticsData::USE_DATA useData, AccumType centerValue ) { Bool reconfig = _saf.algorithm() != StatisticsData::FITTOHALF; if (! reconfig) { StatisticsAlgorithmFactoryData::FitToHalfData data = _saf.fitToHalfData(); reconfig = centerType != data.center || useData != data.side || ( centerType == FitToHalfStatisticsData::CVALUE && ! near(centerValue, data.centerValue) ); } if (reconfig) { _saf.configureFitToHalf(centerType, useData, centerValue); needStorageLattice_p = True; } return reconfig; } template Bool LatticeStatistics::configureChauvenet( Double zscore, Int maxIterations ) { Bool reconfig = _saf.algorithm() != StatisticsData::CHAUVENETCRITERION; if (! reconfig) { typename StatisticsAlgorithmFactoryData::ChauvenetData data = _saf.chauvenetData(); reconfig = ! near(zscore, data.zScore) || maxIterations != data.maxIter; } if (reconfig) { _saf.configureChauvenet(zscore, maxIterations); needStorageLattice_p = True; } return reconfig; } template void LatticeStatistics::forceUseStatsFrameworkUsingDataProviders() { _latticeStatsAlgortihm.reset( new LatticeStatsAlgorithm(STATS_FRAMEWORK_DATA_PROVIDERS) ); } template void LatticeStatistics::forceUseStatsFrameworkUsingArrays() { _latticeStatsAlgortihm.reset( new LatticeStatsAlgorithm(STATS_FRAMEWORK_ARRAYS) ); } template void LatticeStatistics::forceUseOldTiledApplyMethod() { _latticeStatsAlgortihm.reset( new LatticeStatsAlgorithm(TILED_APPLY) ); } template void LatticeStatistics::forceAllowCodeDecideWhichAlgortihmToUse() { _latticeStatsAlgortihm.reset(nullptr); } template Bool LatticeStatistics::generateStorageLattice() { // Iterate through the lattice and generate the storage lattice // The shape of the storage lattice is n1, n2, ..., NACCUM // where n1, n2 etc are the display axes // Set the display axes vector (possibly already set in ::setAxes) displayAxes_p.resize(0); displayAxes_p = IPosition::otherAxes( pInLattice_p->ndim(), cursorAxes_p ).asVector(); // Work out dimensions of storage lattice (statistics accumulations // are along the last axis) IPosition storeLatticeShape; IPosition shape = pInLattice_p->shape(); LatticeStatsBase::setStorageImageShape( storeLatticeShape, True, Int(LatticeStatsBase::NACCUM), displayAxes_p, shape ); // Set the storage lattice tile shape to the tile shape of the // axes of the parent lattice from which it is created. // For the statistics axis, set the tile shape to NACCUM (small). IPosition tileShape(storeLatticeShape.nelements(),1); for (uInt i=0; iniceCursorShape()(displayAxes_p(i)); } tileShape(tileShape.nelements()-1) = storeLatticeShape(storeLatticeShape.nelements()-1); // Create storage lattice. If lattice is > 10% of available memory, // put it on disk. uInt memory = HostInfo::memoryTotal()/1024; Double useMemory = Double(memory)/10.0; if (forceDisk_p) useMemory = 0.0; if (haveLogger_p) { os_p << LogIO::NORMAL1 << "Creating new statistics storage lattice of shape " << storeLatticeShape << endl << LogIO::POST; } pStoreLattice_p = std::make_shared>( TiledShape(storeLatticeShape, tileShape), useMemory ); // Set up min/max location variables std::shared_ptr pProgressMeter( showProgress_p ? std::make_shared() : NULL ); Double timeOld = 0; Double timeNew = 0; uInt nsets = pStoreLattice_p->size()/storeLatticeShape.getLast(1)[0]; Bool forceTiledApply = _latticeStatsAlgortihm && *_latticeStatsAlgortihm == TILED_APPLY; ThrowIf( forceTiledApply && _saf.algorithm() != StatisticsData::CLASSICAL, "Tiled Apply method can only be run using the Classical Statistics algorithm" ); Bool skipTiledApply = _latticeStatsAlgortihm && *_latticeStatsAlgortihm != TILED_APPLY; Bool tryOldMethod = _saf.algorithm() == StatisticsData::CLASSICAL && ! skipTiledApply; if (tryOldMethod) { if (! forceTiledApply) { uInt nel = pInLattice_p->size()/nsets; timeOld = nsets*(_aOld + _bOld*nel); timeNew = nsets*(_aNew + _bNew*nel); tryOldMethod = timeOld < timeNew; } } Bool ranOldMethod = False; uInt ndim = shape.size(); if (tryOldMethod) { if (forceTiledApply && haveLogger_p) { os_p << LogIO::NORMAL << "Forcing use of Tiled Apply method" << LogIO::POST; } // use older method for higher performance in the large loop count // regime minPos_p.resize(ndim); maxPos_p.resize(ndim); StatsTiledCollapser collapser( range_p, noInclude_p, noExclude_p, fixedMinMax_p ); Int newOutAxis = pStoreLattice_p->ndim()-1; SubLattice outLatt(*pStoreLattice_p, True); try { LatticeApply::tiledApply( outLatt, *pInLattice_p, collapser, IPosition(cursorAxes_p), newOutAxis, pProgressMeter.get() ); collapser.minMaxPos(minPos_p, maxPos_p); ranOldMethod = True; } catch (const AipsError& x) { // if the data or mask arrays are not contiguous, // an exception will be thrown. Catch it here, so // _doStatsLoop() can be run instead. ThrowIf( forceTiledApply, "Forced TileApply method, but underlying arrays are " "non-contiguous, so it failed." ); } if (ranOldMethod && doRobust_p) { // Do "robust" (quantile) statistics separately if required. // In the current method, we only call generateRobust() // if the old tiled apply method was used to compute the // accumulated stats. Quantile stats are now // computed in concert with accumulated stats when // the statistics framework is used (when _doStatsLoop() // is called) generateRobust(); } } if (! ranOldMethod) { _doStatsLoop(nsets, pProgressMeter); } needStorageLattice_p = False; doneSomeGoodPoints_p = False; return True; } template void LatticeStatistics::_doStatsLoop( uInt nsets, std::shared_ptr progressMeter ) { maxPos_p.resize(0); minPos_p.resize(0); const auto nCursorAxes = cursorAxes_p.size(); const auto latticeShape(pInLattice_p->shape()); IPosition cursorShape(pInLattice_p->ndim(),1); for (uInt i=0; i subLat(*pInLattice_p, slicer); stepper.reset(); slicer.setStart(stepper.position()); slicer.setEnd(stepper.endPosition()); subLat.setRegion(slicer); const auto setSize = subLat.size(); const auto nMaxThreads = OMP::nMaxThreads(); const auto nDPMaxThreads = min( nMaxThreads, setSize/ClassicalStatisticsData::BLOCK_SIZE + 1 ); const auto nArrMaxThreads = min(nMaxThreads, nsets); auto computed = False; const auto forceUsingArrays = _latticeStatsAlgortihm && *_latticeStatsAlgortihm == STATS_FRAMEWORK_ARRAYS; if (nArrMaxThreads >= nDPMaxThreads || forceUsingArrays) { if (forceUsingArrays && haveLogger_p) { os_p << LogIO::NORMAL << "Forcing use of Stats Framework using Arrays method" << LogIO::POST; } const auto subCursorShape = _cursorShapeForArrayMethod(setSize); if (subCursorShape.product() >= nDPMaxThreads || forceUsingArrays) { _computeStatsUsingArrays(progressMeter, subCursorShape); computed = True; } } const auto forceUsingDP = _latticeStatsAlgortihm && *_latticeStatsAlgortihm == STATS_FRAMEWORK_DATA_PROVIDERS; if (! computed || forceUsingDP) { if (forceUsingDP && haveLogger_p) { os_p << LogIO::NORMAL << "Forcing use of Stats Framework using Data Providers method" << LogIO::POST; } _computeStatsUsingLattDataProviders( stepper, subLat, slicer, progressMeter, nsets ); } if (! doRobust_p) { // zero out the quantile stats since they will not be computed. // For the old TiledApply method, this is done by zeroing out // the array prior to computing the stats const auto ndim = pStoreLattice_p->ndim(); const auto arrShape = pStoreLattice_p->shape().removeAxes( IPosition(1, ndim - 1) ); Array zeros(arrShape, AccumType(0)); IPosition start(ndim, 0); start[ndim - 1] = LatticeStatsBase::MEDABSDEVMED; pStoreLattice_p->putSlice(zeros, start); start[ndim - 1] = LatticeStatsBase::MEDIAN; pStoreLattice_p->putSlice(zeros, start); start[ndim - 1] = LatticeStatsBase::Q1; pStoreLattice_p->putSlice(zeros, start); start[ndim - 1] = LatticeStatsBase::Q3; pStoreLattice_p->putSlice(zeros, start); start[ndim - 1] = LatticeStatsBase::QUARTILE; pStoreLattice_p->putSlice(zeros, start); } } template IPosition LatticeStatistics::_cursorShapeForArrayMethod(uInt64 setSize) const { const uInt ndim = pInLattice_p->ndim(); IPosition cursorShape(ndim, 1); const auto isChauv = _saf.algorithm() == StatisticsData::CHAUVENETCRITERION; // arbitrary, but reasonable, max memory limit in bytes for storing arrays in bytes static const uInt64 limit = 2e7; static const uInt sizeT = sizeof(T); static const uInt sizeBool = sizeof(Bool); static const uInt sizeInt = sizeof(Int); static const uInt sizeStats = sizeof(StatsData); const uInt posSize = sizeof(Int) * ndim; uInt chunkMult = pInLattice_p->isMasked() ? sizeT + sizeBool : sizeT; uInt64 chunkSize = chunkMult*setSize + sizeStats + posSize; if (isChauv) { chunkSize += sizeInt; } const uInt64 nIterToAccum = limit/chunkSize; if (nIterToAccum == 0) { // chunk size is too big, we cannot use this method return IPosition(0); } auto latShape = pInLattice_p->shape(); const auto nCursorAxes = cursorAxes_p.size(); for (uInt i=0; i void LatticeStatistics::_computeStatsUsingArrays( std::shared_ptr progressMeter, const IPosition& cursorShape ) { T overallMax = 0; T overallMin = 0; Bool isReal = whatType(); const uInt nMaxThreads = OMP::nMaxThreads(); IPosition displayAxes(displayAxes_p); uInt nArraysMax = cursorShape.keepAxes(displayAxes).product(); uInt nSA = min(nMaxThreads, nArraysMax); StatisticsAlgorithmFactory< AccumType, typename Array::const_iterator, Array::const_iterator > saf2; _saf.copy(saf2); std::vector< std::shared_ptr< StatisticsAlgorithm< AccumType, typename Array::const_iterator, Array::const_iterator > > > sa(nSA); for (uInt i=0; i range; if (! noInclude_p || ! noExclude_p) { range = std::make_shared(); range->push_back(std::pair(range_p[0], range_p[1])); } const Bool isChauv = _saf.algorithm() == StatisticsData::CHAUVENETCRITERION; std::vector> dataArray; std::vector> maskArray; std::vector curPos; Bool isMasked = pInLattice_p->isMasked(); IPosition latShape = pInLattice_p->shape(); const uInt nCursorAxes = cursorAxes_p.size(); IPosition chunkSliceStart(latShape.size(), 0); IPosition chunkSliceEnd = chunkSliceStart; for (uInt i=0; iinit(nIter); } RO_MaskedLatticeIterator latIter(*pInLattice_p, myStepper); for (latIter.reset(); ! latIter.atEnd(); ++latIter) { cp = latIter.position(); const Array& chunk = latIter.cursor(); IPosition chunkShape = chunk.shape(); const Array maskChunk = isMasked ? latIter.getMask() : Array(); uInt nSets = chunkShape.keepAxes(displayAxes).product(); if (dataArray.size() != nSets) { dataArray.resize(nSets); curPos.resize(nSets); if (isMasked) { maskArray.resize(nSets); } } chunkSliceStart = 0; chunkSliceEnd = chunkSliceEndAtChunkIterBegin; Bool done = False; uInt setIndex = 0; while (! done) { // use assign rather than = because array shapes can differ, throwing // a conformance exception if = is used dataArray[setIndex].assign(chunk(chunkSliceStart, chunkSliceEnd)); if (isMasked) { Array maskSlice = maskChunk(chunkSliceStart, chunkSliceEnd); // use assign rather than = because array shapes can differ maskArray[setIndex].assign(allTrue(maskSlice) ? Array() : maskSlice); } curPos[setIndex] = cp + chunkSliceStart; done = True; for (uInt i=0; i void LatticeStatistics::_doComputationUsingArrays( std::vector< std::shared_ptr< StatisticsAlgorithm< AccumType, typename Array::const_iterator, Array::const_iterator > > >& sa, T& overallMin, T& overallMax, IPosition& arrayShape, std::vector>& dataArray, std::vector>& maskArray, std::vector& curPos, uInt #ifdef _OPENMP nthreads #endif , Bool isChauv, Bool isMasked, Bool isReal, std::shared_ptr range ) { uInt nArrays = dataArray.size(); Bool fixedCurMinMax = (fixedMinMax_p && ! noInclude_p); T currentMin = fixedCurMinMax ? range_p[0] : 0; T currentMax = fixedCurMinMax ? range_p[1] : 0; std::vector> statsArray(nArrays); std::vector q1(doRobust_p ? nArrays : 0); std::vector q3(doRobust_p ? nArrays : 0); std::vector chauvIterArray(isChauv ? nArrays : 0); ostringstream chos; #ifdef _OPENMP # pragma omp parallel for num_threads(nthreads) #endif for (uInt i=0; i 0) { if (! range) { sa[tid]->setData( dataArray[i].begin(), maskArray[i].begin(), dataArray[i].size() ); } else { sa[tid]->setData( dataArray[i].begin(), maskArray[i].begin(), dataArray[i].size(), *range, ! noInclude_p ); } } else { if (! range) { sa[tid]->setData(dataArray[i].begin(), dataArray[i].size()); } else { sa[tid]->setData( dataArray[i].begin(), dataArray[i].size(), *range, ! noInclude_p ); } } statsArray[i] = sa[tid]->getStatistics(); if (doRobust_p) { _computeQuantilesForStatsFramework( statsArray[i], q1[i], q3[i], sa[tid] ); } if (isChauv) { ChauvenetCriterionStatistics< AccumType, typename Array::const_iterator, Array::const_iterator > *ch = dynamic_cast< ChauvenetCriterionStatistics< AccumType, typename Array::const_iterator, Array::const_iterator > * >(&*sa[tid]); chauvIterArray[i] = ch->getNiter(); } } for (uInt i=0; i stats = statsArray[i]; IPosition mypos = curPos[i]; AccumType qq1 = doRobust_p ? q1[i] : 0; AccumType qq3 = doRobust_p ? q3[i] : 0; if (! fixedCurMinMax) { currentMin = stats.min ? *stats.min : 0; currentMax = stats.max ? *stats.max : 0; } _fillStorageLattice( currentMin, currentMax, mypos, stats, doRobust_p, qq1, qq3 ); if (isChauv) { chos.str(""); chos << mypos; _chauvIters[chos.str()] = chauvIterArray[i]; } if (isReal && (! fixedMinMax_p || noInclude_p)) { Bool atStart = arrayShape.empty(); if (atStart) { arrayShape = dataArray[0].shape(); } // toIPositionInArray() is expensive, so only do it if necessary if (atStart || currentMin < overallMin || currentMax > overallMax) { IPosition minPos, maxPos; // in FitToHalf, one of minpos, maxpos will not be defined if (stats.minpos.first >= 0) { minPos = mypos + toIPositionInArray(stats.minpos.second, arrayShape); } if (stats.maxpos.first >= 0) { maxPos = mypos + toIPositionInArray(stats.maxpos.second, arrayShape); } _updateMinMaxPos( overallMin, overallMax, currentMin, currentMax, minPos, maxPos, atStart ); } } } } template void LatticeStatistics::_computeStatsUsingLattDataProviders( LatticeStepper& stepper, SubLattice subLat, Slicer& slicer, std::shared_ptr progressMeter, uInt nsets ) { Bool fixedCurMinMax = (fixedMinMax_p && ! noInclude_p); T currentMin = fixedCurMinMax ? range_p[0] : 0; T currentMax = fixedCurMinMax ? range_p[1] : 0; T overallMax = 0; T overallMin = 0; Bool isReal = whatType(); IPosition curPos; LatticeStatsDataProvider lattDP; MaskedLatticeStatsDataProvider maskedLattDP; LatticeStatsDataProviderBase *dataProvider; _configureDataProviders(lattDP, maskedLattDP); Bool nsetsIsLarge = nsets > 50; if (progressMeter) { if (nsetsIsLarge) { progressMeter->init(nsets); } else { lattDP.setProgressMeter(progressMeter); if (pInLattice_p->isMasked()) { maskedLattDP.setProgressMeter(progressMeter); } } } std::shared_ptr> sa = _saf.createStatsAlgorithm(); Bool isChauv = _saf.algorithm() == StatisticsData::CHAUVENETCRITERION; AccumType q1, q3; for (stepper.reset(); ! stepper.atEnd(); stepper++) { curPos = stepper.position(); slicer.setStart(curPos); slicer.setEnd(stepper.endPosition()); // the setRegion() call is a bottleneck when nsets is large subLat.setRegion(slicer); if(subLat.isMasked()) { maskedLattDP.setLattice(subLat); dataProvider = &maskedLattDP; } else { lattDP.setLattice(subLat); dataProvider = &lattDP; } if ( stepper.atStart() && progressMeter && ! nsetsIsLarge ) { // if _doRobust_p = True, one scan for accumulated stats // + one scan for median and quantiles + one scan for // medabsdevmed = at least 3. In practice this can be more // because there can be multiple scans for median/quantiles // and medabsdevmed for large data sets. uInt mult = doRobust_p ? 3 : 1; progressMeter->init(mult*nsets*dataProvider->estimatedSteps()); } sa->setDataProvider(dataProvider); StatsData stats = sa->getStatistics(); if (! fixedCurMinMax) { currentMin = stats.min ? *stats.min : 0; currentMax = stats.max ? *stats.max : 0; } if (isChauv) { ChauvenetCriterionStatistics *ch = dynamic_cast< ChauvenetCriterionStatistics *>( &*sa ); ostringstream os; os << curPos; // using strings as keys rather than the IPosition objects directly because for some reason, // only one IPosition gets added to the map, and then no other ones get added. // I don't understand, things seem to work OK when I try this in tIPosition, but not here. _chauvIters[os.str()] = ch->getNiter(); } if (isReal && (! fixedMinMax_p || noInclude_p)) { IPosition maxPos, minPos; Bool atStart = stepper.atStart(); if (atStart || currentMin < overallMin || currentMax > overallMax) { dataProvider->minMaxPos(minPos, maxPos); _updateMinMaxPos( overallMin, overallMax, currentMin, currentMax, minPos, maxPos, atStart ); } } // quantile computation must come after minpos/maxpos update because the // data provider is reset for the quantile computation and the min/max pos // info is lost when that happens if (doRobust_p) { _computeQuantilesForStatsFramework( stats, q1, q3, sa ); } _fillStorageLattice(currentMin, currentMax, curPos, stats, doRobust_p, q1, q3); if(progressMeter && nsetsIsLarge) { (*progressMeter)++; } } } template void LatticeStatistics::_updateMinMaxPos( T& overallMin, T& overallMax, T currentMin, T currentMax, const IPosition& minPos, const IPosition& maxPos, Bool atStart ) { // CAUTION The way this has worked in the past apparently for // lattices is that the max and min positions are representative // of the *entire* lattice, and were not stored on a sublattice // by sublattice basis. This is easy to fix now, // but for backward compatibility, I'm leaving this functionality as // it has been. if (atStart) { if (! minPos.empty()) { minPos_p = minPos; } if (! maxPos.empty()) { maxPos_p = maxPos; } overallMin = currentMin; overallMax = currentMax; } else if ( currentMax > overallMax || currentMin < overallMin ) { if (currentMin < overallMin) { if (! minPos.empty()) { minPos_p = minPos; } overallMin = currentMin; } if (currentMax > overallMax) { if (! maxPos.empty()) { maxPos_p = maxPos; } overallMax = currentMax; } } } template void LatticeStatistics::_fillStorageLattice( T currentMin, T currentMax, const IPosition& curPos, const StatsData& stats, Bool doQuantiles, AccumType q1, AccumType q3 ) { const uInt ndim = pStoreLattice_p->ndim(); IPosition pos(ndim,0); const uInt nDispAxes = displayAxes_p.size(); for (uInt j=0; j statsMap; statsMap[MAX] = currentMax; statsMap[MIN] = currentMin; statsMap[MEAN] = stats.mean; statsMap[NPTS] = stats.npts; statsMap[SUM] = stats.sum; statsMap[SUMSQ] = stats.sumsq; statsMap[VARIANCE] = stats.variance; statsMap[SIGMA] = stats.stddev; if (doQuantiles) { statsMap[MEDIAN] = *stats.median; statsMap[MEDABSDEVMED] = *stats.medAbsDevMed; statsMap[Q1] = q1; statsMap[Q3] = q3; statsMap[QUARTILE] = q3 - q1; } typename std::map::const_iterator iter = statsMap.begin(); typename std::map::const_iterator end = statsMap.end(); uInt last = ndim - 1; for (; iter!=end; ++iter) { const LatticeStatsBase::StatisticsTypes key = iter->first; pos[last] = key; pStoreLattice_p->putAt(iter->second, pos); } } template void LatticeStatistics::generateRobust () { Bool showMsg = haveLogger_p && displayAxes_p.empty(); if (showMsg) { os_p << LogIO::NORMAL << "Computing quantiles..." << LogIO::POST; } const uInt nCursorAxes = cursorAxes_p.size(); const IPosition latticeShape(pInLattice_p->shape()); IPosition cursorShape(pInLattice_p->ndim(),1); for (uInt i=0; i> sa; LatticeStatsDataProvider lattDP; MaskedLatticeStatsDataProvider maskedLattDP; IPosition curPos, pos, pos2, pos3, posQ1, posQ3, posNpts, posMax, posMin; Slicer slicer; SubLattice subLat; uInt64 knownNpts; AccumType knownMax, knownMin; sa = _saf.createStatsAlgorithm(); _configureDataProviders(lattDP, maskedLattDP); slicer = Slicer(stepper.position(), stepper.endPosition(), Slicer::endIsLast); subLat = SubLattice(*pInLattice_p, slicer); AccumType median, medAbsDevMed, q1, q3; for (stepper.reset(); ! stepper.atEnd(); stepper++) { curPos = stepper.position(); pos = locInStorageLattice(stepper.position(), LatticeStatsBase::MEDIAN); pos2 = locInStorageLattice(stepper.position(), LatticeStatsBase::MEDABSDEVMED); pos3 = locInStorageLattice(stepper.position(), LatticeStatsBase::QUARTILE); posQ1 = locInStorageLattice(stepper.position(), LatticeStatsBase::Q1); posQ3 = locInStorageLattice(stepper.position(), LatticeStatsBase::Q3); posNpts = locInStorageLattice(stepper.position(), LatticeStatsBase::NPTS); knownNpts = (uInt64)abs(pStoreLattice_p->getAt(posNpts)); if (knownNpts == 0) { // Stick zero in storage lattice (it's not initialized) static const AccumType val(0); pStoreLattice_p->putAt(val, pos); pStoreLattice_p->putAt(val, pos2); pStoreLattice_p->putAt(val, pos3); pStoreLattice_p->putAt(val, posQ1); pStoreLattice_p->putAt(val, posQ3); continue; } posMax = locInStorageLattice(stepper.position(), LatticeStatsBase::MAX); posMin = locInStorageLattice(stepper.position(), LatticeStatsBase::MIN); slicer.setStart(curPos); slicer.setEnd(stepper.endPosition()); subLat.setRegion(slicer); if (subLat.isMasked()) { maskedLattDP.setLattice(subLat); sa->setDataProvider(&maskedLattDP); } else { lattDP.setLattice(subLat); sa->setDataProvider(&lattDP); } knownMin = pStoreLattice_p->getAt(posMin); knownMax = pStoreLattice_p->getAt(posMax); _computeQuantiles( median, medAbsDevMed, q1, q3, sa, knownNpts, knownMin, knownMax ); pStoreLattice_p->putAt(median, pos); pStoreLattice_p->putAt(medAbsDevMed, pos2); pStoreLattice_p->putAt(q3 - q1, pos3); pStoreLattice_p->putAt(q1, posQ1); pStoreLattice_p->putAt(q3, posQ3); } } template template void LatticeStatistics::_computeQuantiles( AccumType& median, AccumType& medAbsDevMed, AccumType& q1, AccumType& q3, std::shared_ptr> statsAlg, uInt64 knownNpts, AccumType knownMin, AccumType knownMax ) const { static const std::set fracs = quartileFracs(); std::map quantiles; static const uInt maxArraySizeBytes = 1e8; // try to prevent multiple passes for // large images uInt64 nBins = max((uInt64)10000, knownNpts/1000); // computing the median and the quartiles simultaneously minimizes // the number of necessary data scans, as opposed to first calling // getMedian() and getQuartiles() separately std::shared_ptr npts = std::make_shared(knownNpts); std::shared_ptr mymin = std::make_shared(knownMin); std::shared_ptr mymax = std::make_shared(knownMax); median = statsAlg->getMedianAndQuantiles( quantiles, fracs, npts, mymin, mymax, maxArraySizeBytes, False, nBins ); q1 = quantiles[0.25]; q3 = quantiles[0.75]; medAbsDevMed = statsAlg->getMedianAbsDevMed( npts, mymin, mymax, maxArraySizeBytes, False, nBins ); } template template void LatticeStatistics::_computeQuantilesForStatsFramework( StatsData& stats, AccumType& q1, AccumType& q3, std::shared_ptr> statsAlg ) const { if (stats.npts > 0) { AccumType median, medAbsDevMed; _computeQuantiles( median, medAbsDevMed, q1, q3, statsAlg, stats.npts, *stats.min, *stats.max ); stats.median = std::make_shared(median); stats.medAbsDevMed = std::make_shared(medAbsDevMed); } else { stats.median = std::make_shared(0); stats.medAbsDevMed = std::make_shared(0); q1 = 0; q3 = 0; } } template void LatticeStatistics::_configureDataProviders( LatticeStatsDataProvider& lattDP, MaskedLatticeStatsDataProvider& maskedLattDP ) const { if (! noInclude_p || ! noExclude_p) { DataRanges range; if (! noInclude_p || ! noExclude_p) { range.push_back(std::pair(range_p[0], range_p[1])); } lattDP.setRanges(range, ! noInclude_p); if (pInLattice_p->isMasked()) { maskedLattDP.setRanges(range, ! noInclude_p); } } } template void LatticeStatistics::listMinMax(ostringstream& osMin, ostringstream& osMax, Int oWidth, DataType type) // Min/max locations only meaningful for Float images currently. // We report locations relative to the start of the parent lattice { if (!fixedMinMax_p) { os_p << LogIO::NORMAL << "Minimum value "; os_p.output() << setw(oWidth) << String(osMin); if (type==TpFloat && minPos_p.size() > 0) { os_p << " at " << blcParent_p + minPos_p+1; } os_p.post(); os_p << "Maximum value "; os_p.output() << setw(oWidth) << String(osMax); if (type==TpFloat && maxPos_p.size() > 0) { os_p << " at " << blcParent_p + maxPos_p+1 << endl; } os_p << endl; os_p.post(); } } template Bool LatticeStatistics::listStats (Bool hasBeam, const IPosition& dPos, const Matrix& stats) // // List the statistics for this row to the logger // // Inputs: // dPos The location of the start of the cursor in the // storage lattice for this row // stats Statistics matrix // Outputs: // Bool Indicates coordinate transformations failed // { if (!haveLogger_p) { // We will consider this situation as successful return True; } os_p << endl; // Get number of statistics and display axes const uInt nDisplayAxes = displayAxes_p.nelements(); const uInt nStatsAxes = cursorAxes_p.nelements(); // Set up the manipulators. We list the number of points as an integer so find // out how big the field width needs to be. Min of 6 so label fits. Int oDWidth = 15; DataType type = whatType(); if (type==TpComplex) { oDWidth = 2*oDWidth + 3; // (x,y) } // Have to convert LogIO object to ostream before can apply // the manipulators. Also formatting Complex numbers with // the setw manipulator fails, so I go to a lot of trouble // with ostringstreams (which are useable only once). Int oPrec = 6; setStream(os_p.output(), oPrec); // Write the pixel and world coordinate of the higher order display axes to the logger uInt ndim = pInLattice_p->ndim(); IPosition shape = pInLattice_p->shape(); if (nDisplayAxes > 1) { Vector sWorld(1); Vector pixels(1); IPosition blc(ndim, 0); IPosition trc(shape - 1); // os_p << LogIO::NORMAL; for (uInt j=1; j sWorld(1); Vector pixels(1); pixels(0) = 1.0; IPosition blc(ndim, 0); IPosition trc(shape - 1); // Write headers os_p << LogIO::NORMAL << endl; Int len0; if (nStatsAxes == 1) { os_p << "Profile "; len0 = 8; } else if (nStatsAxes == 2) { os_p << "Plane "; len0 = 6; } else if (nStatsAxes == 3) { os_p << "Cube "; len0 = 5; } else { os_p << "Hyper-cube "; len0 = 11; } os_p.output() << setw(oDWidth) << "Npts"; os_p.output() << setw(oDWidth) << "Sum"; if (_canDoFlux()) os_p.output() << setw(oDWidth) << "FluxDensity"; os_p.output() << setw(oDWidth) << "Mean"; if (doRobust_p) os_p.output() << setw(oDWidth) << "Median"; os_p.output() << setw(oDWidth) << "Rms"; os_p.output() << setw(oDWidth) << "Std dev"; os_p.output() << setw(oDWidth) << "Minimum"; os_p.output() << setw(oDWidth) << "Maximum" << endl; // Write statistics to logger. We write the pixel location // relative to the parent lattice const uInt n1 = stats.shape()(0); for (uInt j=0; j 0) { // Convert to strings. ostringstream os0, os1, os2, os3, os4, os5, os6, os7, os8, os9; setStream(os0, oPrec); setStream(os1, oPrec); setStream(os2, oPrec); setStream(os3, oPrec); setStream(os4, oPrec); setStream(os5, oPrec); setStream(os6, oPrec); setStream(os7, oPrec); setStream(os8, oPrec); setStream(os9, oPrec); os0 << stats.column(SUM)(j); if (_canDoFlux()) os1 << stats.column(FLUX)(j); os2 << stats.column(MEAN)(j); if (doRobust_p) os8 << stats.column(MEDIAN)(j); os3 << stats.column(RMS)(j); os4 << stats.column(SIGMA)(j); os5 << stats.column(MIN)(j); os6 << stats.column(MAX)(j); os_p.output() << setw(oDWidth) << String(os0); if (hasBeam) os_p.output() << setw(oDWidth) << String(os1); os_p.output() << setw(oDWidth) << String(os2); if (doRobust_p) os_p.output() << setw(oDWidth) << String(os8); os_p.output() << setw(oDWidth) << String(os3); os_p.output() << setw(oDWidth) << String(os4); os_p.output() << setw(oDWidth) << String(os5); os_p.output() << setw(oDWidth) << String(os6); } os_p.output() << endl; } os_p.post(); return True; } template Bool LatticeStatistics::getLayerStats( String& stats, Double area, Int zAxis, Int zLayer, Int hAxis, Int hLayer) { if (!goodParameterStatus_p) { return False; } if (needStorageLattice_p) { if (!generateStorageLattice()) { return False; } } if (displayAxes_p.nelements() == 0) { const IPosition shape = statsSliceShape(); Array statsV(shape); pStoreLattice_p->getSlice (statsV, IPosition(1,0), shape, IPosition(1,1)); IPosition pos(1); pos(0) = NPTS; AccumType nPts = statsV(pos); pos(0) = SUM; AccumType sum = statsV(pos); pos(0) = MEDIAN; AccumType median = statsV(pos); pos(0) = MEDABSDEVMED; //AccumType medAbsDevMed = statsV(pos); pos(0) = QUARTILE; //AccumType quartile= statsV(pos); pos(0) = SUMSQ; AccumType sumSq = statsV(pos); // pos(0) = MEAN; // AccumType mean = statsV(pos); pos(0) = VARIANCE; AccumType var = statsV(pos); // prefer the calculated mean over the accumulated mean because // the accumulated mean can have accumulated precision errors AccumType mean = _mean(sum, nPts); AccumType rms = _rms(sumSq, nPts); AccumType sigma = sqrt(var); pos(0) = MIN; AccumType dMin = statsV(pos); pos(0) = MAX; AccumType dMax = statsV(pos); if (nPts <= 0) { return False; } stringstream os; const Int oPrec = 6; Int oDWidth = 15; DataType type = whatType(); if (type==TpComplex) { oDWidth = 2*oDWidth + 3; } os.setf(ios::left, ios::adjustfield); os << setw(10) << "Npts"; os << setw(oDWidth) << "Sum"; if (_canDoFlux()) os << setw(oDWidth) << "Flux (Jy)"; os << setw(oDWidth) << "Mean"; if (doRobust_p) os << setw(oDWidth) << "Median"; os << setw(oDWidth) << "Rms"; os << setw(oDWidth) << "Std dev"; os << setw(oDWidth) << "Minimum"; os << setw(oDWidth) << "Maximum" << endl; os.fill(' '); os.precision(0); os.setf(ios::fixed, ios::floatfield); os.setf(ios::left, ios::adjustfield); os << setw(10) << nPts; setStream(os, oPrec); os << setw(oDWidth) << sum; if (_canDoFlux()) { Bool unused; setStream(os, oPrec); os << setw(oDWidth) << _flux(unused, sum, area); } setStream(os, oPrec); os << setw(oDWidth) << mean; if (doRobust_p){ setStream(os, oPrec); os << setw(oDWidth) << median; } setStream(os, oPrec); os << setw(oDWidth) << rms; setStream(os, oPrec); os << setw(oDWidth) << sigma; setStream(os, oPrec); os << setw(oDWidth) << dMin; setStream(os, oPrec); os << setw(oDWidth) << dMax; stats += os.str(); stats += '\n'; return True; } const uInt n1 = pStoreLattice_p->shape()(0); Matrix ord(n1,NSTATS); IPosition cursorShape(pStoreLattice_p->ndim(),1); cursorShape(0) = pStoreLattice_p->shape()(0); cursorShape(pStoreLattice_p->ndim()-1) = pStoreLattice_p->shape()(pStoreLattice_p->ndim()-1); IPosition matrixAxes(2); matrixAxes(0) = 0; matrixAxes(1) = pStoreLattice_p->ndim()-1; LatticeStepper stepper(pStoreLattice_p->shape(), cursorShape, matrixAxes, IPosition::makeAxisPath(pStoreLattice_p->ndim())); RO_LatticeIterator pixelIterator(*pStoreLattice_p, stepper); uInt zAx = -1; uInt hAx = -1; for (uInt j=0; j matrix(pixelIterator.matrixCursor()); Bool canDoFlux = _canDoFlux(); Bool unused; for (uInt i=0; i 0) { ord(i,MEAN) = _mean(matrix(i,SUM), nPts); if (canDoFlux) { ord(i,FLUX) = _flux(unused, matrix(i,SUM), area).getValue(); } ord(i,SIGMA) = sqrt(matrix(i,VARIANCE)); ord(i,RMS) = _rms(matrix(i,SUMSQ), nPts); } } for (uInt i=0; i Bool LatticeStatistics::getLayerStats( stat_list &stats, Double area, Int zAxis, Int zLayer, Int hAxis, Int hLayer) { char buffer[256]; if (!goodParameterStatus_p) { return False; } if (needStorageLattice_p) { if (!generateStorageLattice()) { return False; } } if (displayAxes_p.nelements() == 0) { const IPosition shape = statsSliceShape(); Array statsV(shape); pStoreLattice_p->getSlice (statsV, IPosition(1,0), shape, IPosition(1,1)); IPosition pos(1); pos(0) = NPTS; int nPts = (int)statsV(pos); pos(0) = SUM; AccumType sum = statsV(pos); pos(0) = MEDIAN; AccumType median = statsV(pos); pos(0) = MEDABSDEVMED; //AccumType medAbsDevMed = statsV(pos); pos(0) = QUARTILE; //AccumType quartile= statsV(pos); pos(0) = SUMSQ; AccumType sumSq = statsV(pos); // pos(0) = MEAN; // AccumType mean = statsV(pos); pos(0) = VARIANCE; AccumType var = statsV(pos); // prefer the calculated mean over the accumulated mean because // the accumulated mean can have accumulated precision errors AccumType mean = _mean(sum, nPts); AccumType rms = _rms(sumSq, nPts); AccumType sigma = sqrt(var); pos(0) = MIN; AccumType dMin = statsV(pos); pos(0) = MAX; AccumType dMax = statsV(pos); if (nPts <= 0) { return False; } //const Int oPrec = 6; Int oDWidth = 15; DataType type = whatType(); if (type==TpComplex) { oDWidth = 2*oDWidth + 3; } snprintf( buffer, sizeof(buffer), "%d", nPts ); stats.push_back(stat_element("Npts",buffer)); snprintf( buffer, sizeof(buffer), "%e", sum ); stats.push_back(stat_element("Sum",buffer)); if ( _canDoFlux()) { Bool unused; snprintf( buffer, sizeof(buffer), "%e", _flux(unused, sum, area ).getValue()); stats.push_back(stat_element("FluxDensity",buffer)); } snprintf( buffer, sizeof(buffer), "%e", mean ); stats.push_back(stat_element("Mean",buffer)); if (doRobust_p) { snprintf( buffer, sizeof(buffer), "%e", median ); stats.push_back(stat_element("Median",buffer)); } snprintf( buffer, sizeof(buffer), "%e", rms ); stats.push_back(stat_element("Rms",buffer)); snprintf( buffer, sizeof(buffer), "%e", sigma ); stats.push_back(stat_element("Std dev",buffer)); snprintf( buffer, sizeof(buffer), "%e", dMin ); stats.push_back(stat_element("Minimum",buffer)); snprintf( buffer, sizeof(buffer), "%e", dMax ); stats.push_back(stat_element("Maximum",buffer)); return True; } const uInt n1 = pStoreLattice_p->shape()(0); Matrix ord(n1,NSTATS); IPosition cursorShape(pStoreLattice_p->ndim(),1); cursorShape(0) = pStoreLattice_p->shape()(0); cursorShape(pStoreLattice_p->ndim()-1) = pStoreLattice_p->shape()(pStoreLattice_p->ndim()-1); IPosition matrixAxes(2); matrixAxes(0) = 0; matrixAxes(1) = pStoreLattice_p->ndim()-1; LatticeStepper stepper( pStoreLattice_p->shape(), cursorShape, matrixAxes, IPosition::makeAxisPath(pStoreLattice_p->ndim())); RO_LatticeIterator pixelIterator(*pStoreLattice_p, stepper); uInt zAx = -1; uInt hAx = -1; for (uInt j=0; j matrix(pixelIterator.matrixCursor()); Bool unused; for (uInt i=0; i 0) { ord(i,MEAN) = _mean(matrix(i,SUM), nPts); if (_canDoFlux()) { ord(i,FLUX) = _flux(unused, matrix(i,SUM), area).getValue(); } ord(i,SIGMA) = sqrt(matrix(i,VARIANCE)); ord(i,RMS) = _rms(matrix(i,SUMSQ), nPts); } } for (uInt i=0; i(); if (type==TpComplex) { oDWidth = 2*oDWidth + 3; } //Int oPrec = 6; Vector sWorld(1); Vector pixels(1); pixels(0) = 1.0; IPosition blc(pInLattice_p->ndim(),0); IPosition trc(pInLattice_p->shape()-1); //Write statistics to logger. We write the pixel location //relative to the parent lattice for (uInt j=0; j 0){ snprintf( buffer, sizeof(buffer), "%e", ord.column(SUM)(j) ); stats.push_back(stat_element("Sum",buffer)); if (_canDoFlux()) { snprintf( buffer, sizeof(buffer), "%e", ord.column(FLUX)(j) ); stats.push_back(stat_element("FluxDensity",buffer)); } snprintf( buffer, sizeof(buffer), "%e", ord.column(MEAN)(j) ); stats.push_back(stat_element("Mean",buffer)); if (doRobust_p){ snprintf( buffer, sizeof(buffer), "%e", ord.column(MEDIAN)(j) ); stats.push_back(stat_element("Median",buffer)); } snprintf( buffer, sizeof(buffer), "%e", ord.column(RMS)(j) ); stats.push_back(stat_element("Rms",buffer)); snprintf( buffer, sizeof(buffer), "%e", ord.column(SIGMA)(j) ); stats.push_back(stat_element("Std dev",buffer)); snprintf( buffer, sizeof(buffer), "%e", ord.column(MIN)(j) ); stats.push_back(stat_element("Minimum",buffer)); snprintf( buffer, sizeof(buffer), "%e", ord.column(MAX)(j) ); stats.push_back(stat_element("Maximum",buffer)); } } } break; } return True; } template Bool LatticeStatistics::listLayerStats ( const Matrix& stats, ostringstream& os, Int zLayer) { //const uInt nDisplayAxes = displayAxes_p.nelements(); const uInt n1 = stats.shape()(0); Int oDWidth = 15; DataType type = whatType(); if (type==TpComplex) { oDWidth = 2*oDWidth + 3; } Int oPrec = 6; setStream(os, oPrec); Vector sWorld(1); Vector pixels(1); pixels(0) = 1.0; IPosition blc(pInLattice_p->ndim(),0); IPosition trc(pInLattice_p->shape()-1); os << setw(10) << "Npts"; os << setw(oDWidth) << "Sum"; if (_canDoFlux()) { //FIXME Unit not correct in all cases os << setw(oDWidth) << "Flux (Jy)"; } os << setw(oDWidth) << "Mean"; if (doRobust_p) os << setw(oDWidth) << "Median"; os << setw(oDWidth) << "Rms"; os << setw(oDWidth) << "Std dev"; os << setw(oDWidth) << "Minimum"; os << setw(oDWidth) << "Maximum" << endl; //Write statistics to logger. We write the pixel location //relative to the parent lattice for (uInt j=0; j 0){ setStream(os, oPrec); os << setw(oDWidth) << stats.column(SUM)(j); if (_canDoFlux()) { setStream(os, oPrec); os << setw(oDWidth) << stats.column(FLUX)(j); } setStream(os, oPrec); os << setw(oDWidth) << stats.column(MEAN)(j); if (doRobust_p){ setStream(os, oPrec); os << setw(oDWidth) << stats.column(MEDIAN)(j); } setStream(os, oPrec); os << setw(oDWidth) << stats.column(RMS)(j); setStream(os, oPrec); os << setw(oDWidth) << stats.column(SIGMA)(j); setStream(os, oPrec); os << setw(oDWidth) << stats.column(MIN)(j); setStream(os, oPrec); os << setw(oDWidth) << stats.column(MAX)(j); } os << endl; } } return True; } template IPosition LatticeStatistics::locInLattice(const IPosition& storagePosition, Bool relativeToParent) const // // Given a location in the storage lattice, convert those locations on // the non-statistics axis (the statistics axis is the last one) to // account for the location of the subLattice in the parent lattice // { IPosition pos(storagePosition); for (uInt j=0; j IPosition LatticeStatistics::locInStorageLattice(const IPosition& latticePosition, LatticeStatsBase::StatisticsTypes type) const // // Given a location in the input lattice, figure out where it lives // in the storage lattice // { uInt iType = uInt(type); ThrowIf( iType >= uInt(LatticeStatsBase::NACCUM), "Illegal statistics accumulation type " + String::toString(type) ); const uInt nDim = pStoreLattice_p->ndim(); IPosition pos(nDim,0); pos(nDim-1) = iType; for (uInt j=0; j void LatticeStatistics::minMax (Bool& none, AccumType& dMin, AccumType& dMax, const Vector& d, const Vector& n) const // // // Inputs: // d Vector to find min and max of // n Vector which gives the number of points // that were used to compute the value in pt. If zero, // that means there were no valid points and we don't // want to consider the corresponding pd[i] value // Outputs: // none No valid points in array // dMin,DMax Min and max of array pd { Bool init = True; none = True; const Int n1 = d.nelements(); for (Int i=0; i 0.5) { if (init) { dMin = d(i); dMax = d(i); init = False; } else { dMin = min(dMin, d(i)); dMax = max(dMax, d(i)); } none = False; } } } template Bool LatticeStatistics::display() // This function displays (plotting and listing) the requested // statistics as a function of the display axes { if (!goodParameterStatus_p) { return False; } // Do we have anything to do if (!doList_p && haveLogger_p) { os_p << LogIO::NORMAL1 << "There is nothing to plot or list" << LogIO::POST; return True; } // Generate storage lattice if required if (needStorageLattice_p) { if (!generateStorageLattice()) return False; } // If we don't have any display axes just summarise the lattice statistics if (displayAxes_p.nelements() == 0) { summStats (); return True; } // Size of plotting abcissa axis const uInt n1 = pStoreLattice_p->shape()(0); // Allocate ordinate arrays for plotting and listing. Try to preserve // the true Type of the data as long as we can. Eventually, for // plotting we have to make it real valued Matrix ord(n1,NSTATS); // Iterate through storage lattice by planes (first and last axis of storage lattice) // Specify which axes are the matrix axes so that we can discard other // degenerate axes with the matrixCursor function. n1 is only // constrained to be n1 >= 1 IPosition cursorShape(pStoreLattice_p->ndim(),1); cursorShape(0) = pStoreLattice_p->shape()(0); cursorShape(pStoreLattice_p->ndim()-1) = pStoreLattice_p->shape()(pStoreLattice_p->ndim()-1); IPosition matrixAxes(2); matrixAxes(0) = 0; matrixAxes(1) = pStoreLattice_p->ndim()-1; LatticeStepper stepper(pStoreLattice_p->shape(), cursorShape, matrixAxes, IPosition::makeAxisPath(pStoreLattice_p->ndim())); RO_LatticeIterator pixelIterator(*pStoreLattice_p, stepper); // Get beam area Bool hasBeam = False; // for (pixelIterator.reset(); !pixelIterator.atEnd(); pixelIterator++) { // Convert accumulations to mean, sigma, and rms. Matrix matrix(pixelIterator.matrixCursor()); // Reference semantics for (uInt i=0; i 0) { ord(i,MEAN) = _mean(matrix(i,SUM), nPts); ord(i,SIGMA) = sqrt(ord(i,VARIANCE)); ord(i,RMS) = _rms(matrix(i,SUMSQ), nPts); } } // Extract the direct (NPTS, SUM etc) values from the cursor matrix into the plot matrix // There is no easy way to do this other than as I have for (uInt i=0; i Bool LatticeStatistics::retrieveStorageStatistic(Array& slice, const LatticeStatsBase::StatisticsTypes type, const Bool dropDeg) // // Retrieve values from storage lattice // // Input // type Statistics type // Input/output // slice The statistics; should be of zero size on input // // Returns false if internal class state is bad. { // Generate storage lattice if required if (needStorageLattice_p) { if (!generateStorageLattice()) { return False; } } // Were there some good points ? const Int nDim = pStoreLattice_p->ndim(); slice.resize(IPosition(0,0)); if (someGoodPoints()) { // Get desired statistic slice. Discard degenerate axes (requires // empty array on input) IPosition sliceShape(pStoreLattice_p->shape()); sliceShape(nDim-1) = 1; Int ISTAT = Int(type); IPosition pos(nDim,0); pos(nDim-1) = ISTAT; pStoreLattice_p->getSlice(slice, pos, sliceShape, IPosition(nDim,1), dropDeg); } return True; } template Bool LatticeStatistics::retrieveStorageStatistic( Vector& slice, const IPosition& pos, const Bool posInLattice ) { // // Retrieve values from storage lattice // // Input // pos Locations for the display axes in the storage lattice // posInLattice If true the location is given as lattice coordinates // The non-display axis values will be ignored. // Otherwise the position should be for the // display axes only. // // Input/output // slice The statistics; should be long enough on input // if (! posInLattice) { if (pos.nelements() != displayAxes_p.nelements()) { error_p = "Incorrectly sized position given"; slice.resize(0); return False; } } // Generate storage lattice if required if (needStorageLattice_p) { if (!generateStorageLattice()) { return False; } } // Get accumulation sums slice from storage lattice. // Last axis is statistics axis const uInt nDim = displayAxes_p.nelements(); IPosition slicePos(nDim+1,0); if (posInLattice) { _latticePosToStoragePos(slicePos, pos); } else { // Use position as is for (uInt i=0; i tSlice; pStoreLattice_p->getSlice(tSlice, slicePos, sliceShape, IPosition(nDim+1,1), False); // Copy to vector slicePos = 0; for (uInt i=0; i void LatticeStatistics::_latticePosToStoragePos( IPosition& storagePos, const IPosition& latticePos ) { ThrowIf( latticePos.nelements() != pInLattice_p->ndim(), "Incorrectly sized position given" ); ThrowIf( storagePos.size() < displayAxes_p.size(), "storage position does not have enough elements" );; ThrowIf( latticePos.size() < displayAxes_p.size(), "lattice position does not have enough elements" ); // do NOT resize storagePos. It can have more elements than // latticePos as defined by the caller. for (uInt i=0; i Bool LatticeStatistics::someGoodPoints () // // If any of the locations in the statistics storage array contain // some valid points return true straight away. DOn't bother // looking again if we already looked ! // { if (doneSomeGoodPoints_p) { return someGoodPointsValue_p; } else { doneSomeGoodPoints_p = True; if (pStoreLattice_p->ndim() == 1) { // If storage lattice only 1D take cheap way out. Can't invoke // retrieveStorageStatistic or we will be stuck in a time loop const IPosition shape = statsSliceShape(); Array stats(shape); IPosition pos(1,0); pStoreLattice_p->getSlice(stats, pos, shape, IPosition(1,1)); pos(0) = NPTS; // this needs to be Int64, not Int as it was, to support > 2.1 Gpixel images // of course it will still fail for > 9.1 Epixel images, but hopefully we // won't have to worry about those for a few more Moore timescales. someGoodPointsValue_p = Int64(real(stats(pos))+0.1) > 0; return someGoodPointsValue_p; } else { // Iterate through storage lattice by planes (first and last axis of storage lattice) // Specify which axes are the matrix axes so that we can discard other // degenerate axes with the matrixCursor function. n1 is only // constrained to be n1 >= 1 IPosition cursorShape(pStoreLattice_p->ndim(),1); const Int n1 = pStoreLattice_p->shape()(0); cursorShape(0) = n1; cursorShape(pStoreLattice_p->ndim()-1) = pStoreLattice_p->shape()(pStoreLattice_p->ndim()-1); // IPosition matrixAxes(2); matrixAxes(0) = 0; matrixAxes(1) = pStoreLattice_p->ndim()-1; // LatticeStepper stepper(pStoreLattice_p->shape(), cursorShape, matrixAxes, IPosition::makeAxisPath(pStoreLattice_p->ndim())); RO_LatticeIterator pixelIterator(*pStoreLattice_p, stepper); for (pixelIterator.reset(); !pixelIterator.atEnd(); pixelIterator++) { for (Int i=0; i 0) { someGoodPointsValue_p = True; return someGoodPointsValue_p; } } } someGoodPointsValue_p = False; return someGoodPointsValue_p; } } } template IPosition LatticeStatistics::statsSliceShape () const // // Return the shape of a slice from the statistics storage // lattice for a single spatial location. The last axis // is the statistics axis { IPosition shape(pStoreLattice_p->ndim(),1); shape(pStoreLattice_p->ndim()-1) = pStoreLattice_p->shape()(pStoreLattice_p->ndim()-1); return shape; } template void LatticeStatistics::summStats () // // List the summary of the statistics to the logger in the // case that the statistics storage lattice is 1D only // { // Fish out statistics with a slice const IPosition shape = statsSliceShape(); Array stats(shape); pStoreLattice_p->getSlice (stats, IPosition(1,0), shape, IPosition(1,1)); IPosition pos(1); pos(0) = NPTS; AccumType nPts = stats(pos); pos(0) = SUM; AccumType sum = stats(pos); pos(0) = MEDIAN; AccumType median = stats(pos); pos(0) = MEDABSDEVMED; AccumType medAbsDevMed = stats(pos); pos(0) = QUARTILE; AccumType quartile= stats(pos); pos(0) = Q1; AccumType q1 = stats(pos); pos(0) = Q3; AccumType q3 = stats(pos); // pos(0) = SUMSQ; AccumType sumSq = stats(pos); pos(0) = MEAN; AccumType mean = stats(pos); pos(0) = VARIANCE; AccumType var = stats(pos); AccumType rms = _rms(sumSq, nPts); pos(0) = SIGMA; AccumType sigma = stats(pos); pos(0) = MIN; AccumType dMin = stats(pos); pos(0) = MAX; AccumType dMax = stats(pos); // Do this check so that we only print the stats when we have values. if (nPts > 0) { displayStats( nPts, sum, median, medAbsDevMed, quartile, sumSq, mean, var, rms, sigma, dMin, dMax, q1, q3 ); } } template void LatticeStatistics::displayStats ( AccumType nPts, AccumType sum, AccumType median, AccumType medAbsDevMed, AccumType quartile, AccumType /*sumSq*/, AccumType mean, AccumType var, AccumType rms, AccumType sigma, AccumType dMin, AccumType dMax, AccumType q1, AccumType q3 ) { // Have to convert LogIO object to ostream before can apply // the manipulators. Also formatting Complex numbers with // the setw manipulator fails, so I go to a lot of trouble // with ostringstreams (which are useable only once). const Int oPrec = 6; Int oWidth = 14; DataType type = whatType(); if (type==TpComplex) { oWidth = 32; } setStream(os_p.output(), oPrec); ostringstream os00, os0, os1, os2, os3, os4, os5, os6, os7, os8; ostringstream os9, os10, os11, os12, os13; setStream(os00, oPrec); setStream(os0, oPrec); setStream(os1, oPrec); setStream(os2, oPrec); setStream(os3, oPrec); setStream(os4, oPrec); setStream(os5, oPrec); setStream(os6, oPrec); setStream(os7, oPrec); setStream(os8, oPrec); setStream(os9, oPrec); setStream(os10, oPrec), setStream(os11, oPrec); setStream(os12, oPrec); setStream(os13, oPrec); // os_p << LogIO::NORMAL << endl << LogIO::POST; if (nPts > 0) { os00 << nPts; os1 << sum; os2 << mean; os3 << var; os4 << sigma; os5 << rms; os6 << dMin; os7 << dMax; os8 << median; os9 << medAbsDevMed; os10 << quartile; os12 << q1; os13 << q3; os_p << "Number points = "; os_p.output() << setw(oWidth) << String(os00) << " Sum = "; os_p.output() << setw(oWidth) << String(os1) << endl; os_p.post(); os_p << "Mean = "; os_p.output() << setw(oWidth) << String(os2); if (doRobust_p) { os_p.output() << " Median = "; os_p.output() << setw(oWidth) << String(os8) << endl; } os_p.post(); // os_p << "Variance = "; os_p.output() << setw(oWidth) << String(os3); // if (var > 0.0) { os_p << " Std dev = "; os_p.output() << setw(oWidth) << String(os4) << endl; os_p.post(); } else { os_p.post(); } // os_p << "Rms = "; os_p.output() << setw(oWidth) << String(os5) << endl; os_p << endl; os_p.post(); // if (doRobust_p) { os_p << "MedAbsDevMed = "; os_p.output() << setw(oWidth) << String(os9); os_p.output() << " IQR = "; os_p.output() << setw(oWidth) << String(os10) << endl; os_p.output() << " First Quartile = "; os_p.output() << setw(oWidth) << String(os12) << endl; os_p.output() << " Third Quartile = "; os_p.output() << setw(oWidth) << String(os13) << endl; os_p.post(); } os_p << endl << LogIO::POST; listMinMax(os6, os7, oWidth, type); } else { os_p << "No valid points found " << LogIO::POST; } os_p << endl << LogIO::POST; } template void LatticeStatistics::stretchMinMax (AccumType& dMin, AccumType& dMax) const // // Stretch a range by 5% // // Input/output: // dMin,Max The range to stretch // { AccumType delta = AccumType(0.05)*(dMax-dMin); AccumType absmax = max(abs(dMax),abs(dMin)); if (delta < AccumType(1.0e-5)*absmax) delta = AccumType(0.01) * absmax; if (dMin==dMax) { if (dMin==AccumType(0.0)) { dMin = AccumType(-1.0); dMax = AccumType(1.0); } else { dMin = dMin - AccumType(0.05)*dMin; dMax = dMax + AccumType(0.05)*dMax; } } else { dMin = dMin - delta; dMax = dMax + delta; } } template void LatticeStatistics::setStream (ostream& os, Int oPrec) { os.fill(' '); os.precision(oPrec); os.setf(ios::scientific, ios::floatfield); os.setf(ios::left, ios::adjustfield); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LatticeMath/LatticeStatsBase.cc000066400000000000000000000141461476623553700230470ustar00rootroot00000000000000//# LatticeStatsBase.cc: base class for LatticeStatistics.cc //# Copyright (C) 1996,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Vector LatticeStatsBase::toStatisticTypes (const String& statsU, const std::regex& delimiter) { Vector statsStrings = stringToVector(statsU, delimiter); return LatticeStatsBase::toStatisticTypes(statsStrings); } Vector LatticeStatsBase::toStatisticTypes (const Vector& statsU) { const uInt n = statsU.nelements(); Vector statsToPlot(n); Int n2 = 0; for (uInt i=0; i& nxy, ostream& os) { Int n = nxy.nelements(); nxy.resize(2,True); if (n > 2) { os << "Too many elements for argument nxy" << endl; return False; } else if (n == 2) { nxy(0) = max(1,nxy(0)); nxy(1) = max(1,nxy(1)); } else if (n == 1) { nxy(0) = max(1,nxy(0)); nxy(1) = nxy(0); } else { nxy(0) = 1; nxy(1) = 1; } return True; } void LatticeStatsBase::setStorageImageShape(IPosition& storeImageShape, const Bool& last, const Int& axisSize, const Vector& displayAxes, const IPosition& imageShape) { Int nStoreImageDim = displayAxes.nelements() + 1; storeImageShape.resize(nStoreImageDim); if (last) { for (Int i=0; i LatticeStatsBase::quartileFracs() { const static Double fracs[] {0.25, 0.75}; return std::set(fracs, fracs+2); } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/LatticeMath/LatticeStatsBase.h000066400000000000000000000137111476623553700227060ustar00rootroot00000000000000//# LatticeStatsBase.h: base class for LatticeStatistics class //# Copyright (C) 1996,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICESTATSBASE_H #define LATTICES_LATTICESTATSBASE_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class IPosition; class Regex; // Base class for LatticeStatistics class // // // // // // //
      • Vector //
      • String // // // // A simple base class for the LatticeStatistics class // // // // This base class provides an enum defining allowed statistics types // and a helper function to convert between a String and a // Vector describing the desired statistics to plot. The reason for // having it as a base class rather than just part of LatticeStatistics is that // the latter is templated, and it doesn't make much sense to invoke the static function // setStatisticTypes function with a templated type. // // // // // Vector statsToPlot = LatticeStatsBase::toStatisticTypes("mean,rms,sigma"); // // // // // class LatticeStatsBase { public: // This enum StatisticTypes is provided for use with the // LatticeStatistics\::setPlotting function. It gives the allowed // statistics types that you can ask for. enum StatisticsTypes { // The number of points NPTS, // The sum SUM, // The sum squared SUMSQ, // The median - the robust stats does not fit well into storage lattice approach MEDIAN, // median of absolute deviation from median MEDABSDEVMED, // inter-quartile range QUARTILE, // The first and third quartiles Q1, Q3, // The minimum MIN, // The maximum MAX, // The mean MEAN, // The variance about the mean VARIANCE, // The standard deviation about the mean SIGMA, // The rms RMS, // The flux density (can't always compute this - needs the beam) FLUX, // The total number of available statistics to plot NSTATS, // The total number of accumulation image items (not for general use: // note that the accumulation items MUST come first in this enum) // dmehring changed from VARIANCE+1 to SIGMA+1 because the standard // deviation should be stored rather than taking the square root of // the same value multiple times. Not to mention the biweight // algorithm does not compute the variance, so that the standard // deviation must be explicitly stored for it. NACCUM = SIGMA + 1 }; // Helper function to convert a String containing a list of desired statistics to // the correct Vector required for the LatticeStatistics::setPlotting // function. This may be usful if your user interface involves strings rather than integers. // A new value is added to the output vector (which is resized appropriately) if any of the // substrings "npts", "min", "max", "sum", "sumsq", "mean", "sigma", "rms", // and "flux" is present. An empty vector results if there are no matches // static Vector toStatisticTypes (const String& statistics, const std::regex& delimiter); static Vector toStatisticTypes (const Vector& statistics); // // Convert type to string. // static String toStatisticName (StatisticsTypes type); static String toStatisticName (Int type); // // Returns -1 if the statistic string is not valid static Int toStatisticType (const String& statistic); // Check and fill in defaults for a Vector containing the // number of subplots in x and y to be put on a plot. The Vector // is resized to 2 before assignment. A return value of False indicates // invalid arguments. static Bool setNxy (Vector& nxy, ostream& os); // A storage image is used to accumulate information as a function of the display // axes as an image is iterated through. This function sets the storage image shape // to that appropriate to the shape of the display axes and the desired size of the first // or last dimension. static void setStorageImageShape (IPosition& storeImageShape, const Bool& last, const Int& axisSize, const Vector& displayAxes, const IPosition& shape); // Stretch a range by 10% static void stretchMinMax (Float& min, Float& max); static std::set quartileFracs(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LatticeMath/LatticeStatsDataProvider.h000066400000000000000000000117561476623553700244270ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICESTATSDATAPROVIDER_H #define LATTICES_LATTICESTATSDATAPROVIDER_H #include #include #include namespace casacore { // Data provider which allows stats framework to iterate through an unmasked lattice. template class LatticeStatsDataProvider : public LatticeStatsDataProviderBase { public: // default constructor, must set lattice after construction but before // using the object LatticeStatsDataProvider(); // iteratorLimitBytes is related to the size of the lattice. // If the lattice is greater than this size, then a lattice iterator will // be used to step through the lattice. If less, then all the data in the // values in the lattice are retrieved in a single chunk. The advantage of // the iterator is that less memory is used. The disadvantage is there is // a significant performace cost, so if the lattice is small, it is better to // get all its values in a single chunk and forgo the iterator. This is particularly // true when looping for a large number of iterations and creating a // LatticeStatsDataProvider each loop (in that case, you probably will want // to create a single object before the loop and use setLattice() to update // its lattice). LatticeStatsDataProvider( const Lattice& lattice, uInt iteratorLimitBytes=4096*4096 ); ~LatticeStatsDataProvider(); void operator++(); // estimated number of steps to iterate through the the lattice uInt estimatedSteps() const; // Are there any data sets left to provide? Bool atEnd() const; // Take any actions necessary to finalize the provider. This will be called when // atEnd() returns True. void finalize(); // get the count of elements in the current data set. When implementing this method, be // certain to take stride into account; ie for a data set with nominally 100 elements that // is to have a stride of two, this method should return 50. uInt64 getCount(); // get the current data set const T* getData(); // Get the associated mask of the current dataset. Only called if hasMask() returns True; const Bool* getMask(); // returns something reasonable based on the lattice size. uInt getNMaxThreads() const; // Does the current data set have an associated mask? Bool hasMask() const; // reset the provider to point to the first data set it manages. void reset(); // set the lattice. Automatically resets the lattice iterator // iteratorLimitBytes is related to the size of the lattice. // If the lattice is greater than this size, then a lattice iterator will // be used to step through the lattice. If less, then all the data in the // values in the lattice are retrieved in a single chunk. The advantage of // the iterator is that less memory is used. The disadvantage is there is // a significant performace cost, so if the lattice is small, it is better to // get all its values in a single chunk and forgo the iterator. This is particularly // true when looping for a large number of iterations and creating a // LatticeStatsDataProvider each loop (in that case, you probably will want // to create a single object before the loop and use setLattice() to update // its lattice). void setLattice( const Lattice& lattice, uInt iteratorLimitBytes=4096*4096 ); // // see base class documentation. void updateMaxPos(const std::pair& maxpos); void updateMinPos(const std::pair& minpos); // private: std::shared_ptr> _iter; Array _currentSlice; const T* _currentPtr; Bool _delData, _atEnd; uInt _nMaxThreads; void _freeStorage(); }; } #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/LatticeMath/LatticeStatsDataProvider.tcc000066400000000000000000000122551476623553700247440ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef LATTICES_LATTICESTATSDATAPROVIDER_TCC #define LATTICES_LATTICESTATSDATAPROVIDER_TCC #include #include namespace casacore { template LatticeStatsDataProvider::LatticeStatsDataProvider() : LatticeStatsDataProviderBase(), _iter(), _currentSlice(), _currentPtr(0), _delData(False), _atEnd(False), _nMaxThreads(0) {} template LatticeStatsDataProvider::LatticeStatsDataProvider( const Lattice& lattice, uInt iteratorLimitBytes ) : LatticeStatsDataProviderBase(), _iter(), _currentSlice(), _currentPtr(0), _delData(False), _atEnd(False) { setLattice(lattice, iteratorLimitBytes); } template LatticeStatsDataProvider::~LatticeStatsDataProvider() {} template void LatticeStatsDataProvider::operator++() { _freeStorage(); if (! _iter) { _atEnd = True; } else { ++(*_iter); } this->_updateProgress(); } template uInt LatticeStatsDataProvider::estimatedSteps() const { if (! _iter) { return 1; } IPosition lattShape = _iter->latticeShape(); IPosition cursShape = _iter->cursor().shape(); uInt ndim = lattShape.size(); uInt count = 1; for (uInt i=0; i Bool LatticeStatsDataProvider::atEnd() const { if (! _iter) { return _atEnd; } return _iter->atEnd(); } template void LatticeStatsDataProvider::finalize() { _freeStorage(); LatticeStatsDataProviderBase::finalize(); } template uInt64 LatticeStatsDataProvider::getCount() { if (! _iter) { return _currentSlice.size(); } return _iter->cursor().size(); } template const T* LatticeStatsDataProvider::getData() { if (_iter) { _currentSlice.assign(_iter->cursor()); } _currentPtr = _currentSlice.getStorage(_delData); return _currentPtr; } template const Bool* LatticeStatsDataProvider::getMask() { return NULL; } template uInt LatticeStatsDataProvider::getNMaxThreads() const { #ifdef _OPENMP return _nMaxThreads; #else return 0; #endif } template Bool LatticeStatsDataProvider::hasMask() const { return False; } template void LatticeStatsDataProvider::reset() { LatticeStatsDataProviderBase::reset(); if (_iter) { _iter->reset(); } } template void LatticeStatsDataProvider::setLattice( const Lattice& lattice, uInt iteratorLimitBytes ) { finalize(); if (lattice.size() > iteratorLimitBytes/sizeof(T)) { TileStepper stepper( lattice.shape(), lattice.niceCursorShape( lattice.advisedMaxPixels() ) ); _iter = std::make_shared>(lattice, stepper); } else { _iter = NULL; _currentSlice.assign(lattice.get()); _atEnd = False; } #ifdef _OPENMP _nMaxThreads = min( omp_get_max_threads(), (Int)ceil((Float)lattice.size()/ClassicalStatisticsData::BLOCK_SIZE) ); #endif } template void LatticeStatsDataProvider::updateMaxPos( const std::pair& maxpos ) { IPosition p = toIPositionInArray(maxpos.second, _currentSlice.shape()); if (_iter) { p += _iter->position(); } this->_updateMaxPos(p); } template void LatticeStatsDataProvider::updateMinPos( const std::pair& minpos ) { IPosition p = toIPositionInArray(minpos.second, _currentSlice.shape()); if (_iter) { p += _iter->position(); } this->_updateMinPos(p); } template void LatticeStatsDataProvider::_freeStorage() { _currentSlice.freeStorage (_currentPtr, _delData); _delData = False; } } #endif casacore-3.7.1/lattices/LatticeMath/LatticeStatsDataProviderBase.h000066400000000000000000000073221476623553700252140ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICESTATSDATAPROVIDERBASE_H #define LATTICES_LATTICESTATSDATAPROVIDERBASE_H #include #include #include #include #include namespace casacore { class LatticeProgress; // Abstract base class of data providers which allows stats framework to iterate through a lattice. template class LatticeStatsDataProviderBase : public StatsDataProvider::PrecisionType, const T*, const Bool*> { public: virtual ~LatticeStatsDataProviderBase(); // estimated number of steps to iterate through the the lattice virtual uInt estimatedSteps() const = 0; virtual void finalize(); // Get the stride for the current mask (only called if hasMask() returns True). uInt getMaskStride(); // Get the associated range(s) of the current dataset. Only called if hasRanges() returns True; std::vector::PrecisionType, typename NumericTraits::PrecisionType> > getRanges(); // Get the stride for the current data set. uInt getStride(); // Returns NULL; lattices do not have associated weights. const T* getWeights(); // Does the current data set have associated range(s)? Bool hasRanges() const; // returns False; lattices do not have associated weights. Bool hasWeights() const; // If the associated data set has ranges, are these include (return True) or // exclude (return False) ranges? Bool isInclude() const; // get the positions of the min and max void minMaxPos(IPosition& minpos, IPosition& maxpos) const; virtual void reset(); void setProgressMeter(std::shared_ptr pm); // set the data ranges void setRanges( const std::vector::PrecisionType, typename NumericTraits::PrecisionType> >& ranges, Bool isInclude ); protected: LatticeStatsDataProviderBase(); void _updateMaxPos(const IPosition& maxPos) { _maxPos = maxPos; } void _updateMinPos(const IPosition& minPos) { _minPos = minPos; } void _updateProgress(); private: Bool _hasRanges, _isInclude; std::vector::PrecisionType, typename NumericTraits::PrecisionType> > _ranges; std::shared_ptr _progressMeter; IPosition _minPos, _maxPos; }; } #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/LatticeMath/LatticeStatsDataProviderBase.tcc000066400000000000000000000064511476623553700255400ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICESTATSDATAPROVIDERBASE_TCC #define LATTICES_LATTICESTATSDATAPROVIDERBASE_TCC #include #include namespace casacore { template LatticeStatsDataProviderBase::LatticeStatsDataProviderBase() : _hasRanges(False), _isInclude(True), _ranges(), _progressMeter(NULL), _minPos(), _maxPos() {} template LatticeStatsDataProviderBase::~LatticeStatsDataProviderBase() {} template uInt LatticeStatsDataProviderBase::getMaskStride() { return 1; } template void LatticeStatsDataProviderBase::finalize() {} template std::vector::PrecisionType, typename NumericTraits::PrecisionType> > LatticeStatsDataProviderBase::getRanges() { return _ranges; } template uInt LatticeStatsDataProviderBase::getStride() { return 1; } template const T* LatticeStatsDataProviderBase::getWeights() { return NULL; } template Bool LatticeStatsDataProviderBase::hasRanges() const { return _hasRanges; } template Bool LatticeStatsDataProviderBase::hasWeights() const { return False; } template Bool LatticeStatsDataProviderBase::isInclude() const { return _isInclude; } template void LatticeStatsDataProviderBase::minMaxPos( IPosition& minPos, IPosition& maxPos) const { minPos = _minPos; maxPos = _maxPos; } template void LatticeStatsDataProviderBase::reset() { _minPos.resize(0); _maxPos.resize(0); } template void LatticeStatsDataProviderBase::setProgressMeter( std::shared_ptr pm ) { _progressMeter = pm; } template void LatticeStatsDataProviderBase::setRanges( const std::vector::PrecisionType, typename NumericTraits::PrecisionType> >& ranges, Bool isInclude ) { _hasRanges = ! ranges.empty(); _ranges = ranges; _isInclude = isInclude; } template void LatticeStatsDataProviderBase::_updateProgress() { if (_progressMeter) { (*_progressMeter)++; } } } #endif casacore-3.7.1/lattices/LatticeMath/LatticeTwoPtCorr.h000066400000000000000000000105511476623553700227170ustar00rootroot00000000000000//# LatticeTwoPtCorr.h: compute two-point correlation functions from a lattice //# Copyright (C) 1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICETWOPTCORR_H #define LATTICES_LATTICETWOPTCORR_H //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class MaskedLattice; template class Lattice; class IPosition; class LogIO; class String; // // Compute two point auto-correlation functions from a lattice // // // // // //
      • MaskedLattice // // // This class allows you to compute two point correlation functions // from lattices over planes of the specified two axes. // At present, only autocorrelation is implemented and only // the structure function is available. // // The structure function is // S(x,y) = < [lat(i,j) - lat(i+x,j+y)]**2 > // where x and y are absolute integer shifts (or lags). // // // // // // //
      • Add additional algorithms other than the structure function //
      • Allow cross correlation algorithms as well as autocorrelation // template class LatticeTwoPtCorr { public: enum Method { // Undefined UNDEFINED, // Structure Function STRUCTUREFUNCTION, // nMethods NMETHODS }; // Default constructor LatticeTwoPtCorr() {} // Destructor ~LatticeTwoPtCorr() {} // Compute specified autocorrelation function for the planes of the given TWO axes. // If the output lattice has a mask, it will first be set to False (bad) // and then any output pixel with some contributing values will be set to // True (good). // void autoCorrelation (MaskedLattice& out, const MaskedLattice& in, const IPosition& axes, Method method, Bool showProgress=True) const; // // Helper function to provide output lattice shape give the input shape // and the axes to find the structure function over. static IPosition setUpShape (const IPosition& inShape, const IPosition& axes); // Helper functions to convert method types to and from strings // static Method fromString (const String& method); static String toString (Method method); // private: // Function Pointer typedef typedef T (LatticeTwoPtCorr::*FuncPtr)(T d1, T d2) const; // Do the iteration work void autoCorrelation (MaskedLattice& out, const MaskedLattice& in, const IPosition& axes, FuncPtr, Bool showProgress) const; // Check Output lattice shape void check (LogIO& os, const MaskedLattice& latOut, const MaskedLattice& latIn, const IPosition& axes) const; // Compute structure function T structureFunction (T d1, T d2) const {return (d1-d2)*(d1-d2);} }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/LatticeMath/LatticeTwoPtCorr.tcc000066400000000000000000000242411476623553700232420ustar00rootroot00000000000000//# LatticeTwoPtCorr.cc: compute two point correlation functions of a lattice //# Copyright (C) 1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICETWOPTCORR_TCC #define LATTICES_LATTICETWOPTCORR_TCC #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* #include #include */ namespace casacore { //# NAMESPACE CASACORE - BEGIN template void LatticeTwoPtCorr::autoCorrelation (MaskedLattice& latOut, const MaskedLattice& latIn, const IPosition& axes, Method method, Bool showProgress) const { LogIO os(LogOrigin("LatticeTwoPtCorr", "autoCorrelation(...)", WHERE)); // Set up function pointer FuncPtr funcPtr=0; if (method==STRUCTUREFUNCTION) { funcPtr = &LatticeTwoPtCorr::structureFunction; } else { os << "Unimplemented method" << LogIO::EXCEPTION; } // Do the work autoCorrelation (latOut, latIn, axes, funcPtr, showProgress); } template IPosition LatticeTwoPtCorr::setUpShape (const IPosition& inShape, const IPosition& axes) { AlwaysAssert (axes.nelements()==2, AipsError); AlwaysAssert (inShape.nelements()>=2, AipsError); // IPosition outShape = inShape; outShape(axes(0)) = (inShape(axes(0))-1)*2 + 1; outShape(axes(1)) = (inShape(axes(1))-1)*2 + 1; // return outShape; } template typename LatticeTwoPtCorr::Method LatticeTwoPtCorr::fromString (const String& methodU) { String method = methodU; method.upcase(); typename LatticeTwoPtCorr::Method m = LatticeTwoPtCorr::UNDEFINED; // if (method.contains("STR")) { m = LatticeTwoPtCorr::STRUCTUREFUNCTION; } // return m; } template String LatticeTwoPtCorr::toString (Method method) { String m; if (method==LatticeTwoPtCorr::STRUCTUREFUNCTION) { m = String("structurefunction"); } else { m = String("undefined"); } // return m; } // Private functions template void LatticeTwoPtCorr::autoCorrelation (MaskedLattice& latOut, const MaskedLattice& latIn, const IPosition& axes, FuncPtr funcPtr, Bool showProgress) const { LogIO os(LogOrigin("LatticeTwoPtCorr", "autoCorrelation(...)", WHERE)); // Check output lattice shape and axes check (os, latOut, latIn, axes); // IPosition shapeIn = latIn.shape(); IPosition shapeOut = latOut.shape(); uInt nDim = shapeIn.nelements(); IPosition axisPath = IPosition::makeAxisPath (nDim, axes); // Make input iterator Int nxIn = shapeIn(axes(0)); Int nyIn = shapeIn(axes(1)); IPosition cursorShapeIn(2, nxIn, nyIn); LatticeStepper stepIn(shapeIn, cursorShapeIn, axes, axisPath); RO_MaskedLatticeIterator itIn(latIn, stepIn); Bool inIsMasked = latIn.hasPixelMask(); // Make output iterators Int nxOut = shapeOut(axes(0)); Int nyOut = shapeOut(axes(1)); IPosition cursorShapeOut(2, nxOut, nyOut); LatticeStepper stepOut(shapeOut, cursorShapeOut, axes, axisPath); LatticeIterator itOut(latOut, stepOut); Bool outIsMasked = latOut.hasPixelMask() && latOut.pixelMask().isWritable(); LatticeIterator* itOutMaskPtr = 0; if (outIsMasked) { Lattice& outMask = latOut.pixelMask(); itOutMaskPtr = new LatticeIterator(outMask, stepOut); } // Matrices for plane by plane iteration results Matrix sumOut(nxOut, nyOut); Matrix nPtsOut(nxOut, nyOut); Matrix maskOut(nxOut,nyOut); // Iterate through image, plane by plane. The algorithm is too // complicated if I iterate tile by tile Int lxOff = (nxOut-1) / 2; Int lyOff = (nyOut-1) / 2; Int lx = 0; Int ly = 0; // for (itIn.reset(),itOut.reset(); !itIn.atEnd(); itIn++,itOut++) { if (showProgress) { os << LogIO::NORMAL << "Processing position " << itIn.position() << LogIO::POST; } // Get data and mask const Matrix& dataIn(itIn.matrixCursor()); const Matrix& maskIn(itIn.getMask(True)); // Initialize output T zero(0.0); sumOut.set (zero); nPtsOut.set(0.0); maskOut.set(False); // Create ArrayAccessors to optimize access to Matricies ArrayAccessor > jIt(dataIn); // Outer loops ArrayAccessor > iIt; ArrayAccessor > jjIt(dataIn); // Inner loops ArrayAccessor > iiIt; // ArrayAccessor > jjItS(sumOut); // Inner loops ArrayAccessor > iiItS(sumOut); ArrayAccessor > jjItN(nPtsOut); // Inner loops ArrayAccessor > iiItN(nPtsOut); // Int i,j,ii,jj,id,jd; if (inIsMasked) { // Create Mask accessors ArrayAccessor > jItM(maskIn); // Outer loops ArrayAccessor > iItM; ArrayAccessor > jjItM(maskIn); // Inner loops ArrayAccessor > iiItM; // ArrayAccessor > jjItMOut(maskOut); // Inner loops ArrayAccessor > iiItMOut(maskOut); // for (j=0; j::iterator outIter; typename Array::iterator sumIter; typename Array::iterator nIter; typename Array::iterator nIterEnd = nPtsOut.end(); for (outIter=itOut.rwMatrixCursor().begin(),sumIter=sumOut.begin(),nIter=nPtsOut.begin(); nIter!=nIterEnd; ++nIter,++sumIter,++outIter) { if (*nIter > 0.5) { *outIter = *sumIter / *nIter; } } // if (itOutMaskPtr) itOutMaskPtr->rwMatrixCursor() = maskOut; // Increment output mask iterator if (itOutMaskPtr) (*itOutMaskPtr)++; } // Cleanup if (itOutMaskPtr) delete itOutMaskPtr; } template void LatticeTwoPtCorr::check (LogIO& os, const MaskedLattice& latOut, const MaskedLattice& latIn, const IPosition& axes) const { AlwaysAssert (latIn.ndim() == latOut.ndim(), AipsError); IPosition inShape = latIn.shape(); IPosition outShape = LatticeTwoPtCorr::setUpShape (inShape, axes); // if (!outShape.isEqual(latOut.shape())) { os << "Input shape = " << inShape << LogIO::POST; os << "Actual output shape = " << latOut.shape() << LogIO::POST; os << "Expected output shape = " << outShape << LogIO::POST; os << "Output lattice has wrong shape" << LogIO::EXCEPTION; } } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LatticeMath/LineCollapser.h000066400000000000000000000123601476623553700222420ustar00rootroot00000000000000//# LineCollapser.h: Abstract base class to collapse lines for LatticeApply //# Copyright (C) 1996,1997,1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LINECOLLAPSER_H #define LATTICES_LINECOLLAPSER_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class IPosition; // // Abstract base class for LatticeApply function signatures // // // // // //
      • LatticeApply // // // // // This is an abstract base class for the collapsing of lines to // be used in function lineApply or lineMultiApply // in class LatticeApply. // It is meant for cases where the entire line is needed (e.g. moment // calculation). If that is not needed (e.g. to calculate maximum), // it is better to use function LatticeApply::tiledApply // with class TiledCollapser. //

        // The user has to derive a concrete class from this base class // and implement the (pure) virtual functions. //
        The main function is process, which needs to do the // calculation. //
        Other functions make it possible to perform an initial check. //

        // The class is Doubly templated. Ths first template type // is for the data type you are processing. The second type is // for what type you want the results of the processing assigned to. // For example, if you are computing sums of squares for statistical // purposes, you might use higher precision (FLoat->Double) for this. // No check is made that the template types are self-consistent. // // // // // // // // //

      • // template class LineCollapser { public: // Destructor virtual ~LineCollapser(); // The init function for a derived class. // It can be used to check if nOutPixelsPerCollapse // corresponds with the number of pixels produced per collapsed line. virtual void init (uInt nOutPixelsPerCollapse) = 0; // Can the process function in the derived class handle a null mask? // If not, LatticeApply ensures that it'll always pass a filled mask vector, // even if the lattice does not have a mask (in that case that mask // contains all True values). //
        The default implementation returns False. //
        The function is there to make optimization possible when no masks // are involved. On the other side, it allows the casual user to ignore // optimization. virtual Bool canHandleNullMask() const; // Collapse the given line and return one value from that operation. // The position in the Lattice at the start of the line is input // as well. //
        When function canHandleNullMask returned True, // it is possible that mask is an empty vector indicating // that the input has no mask, thus all values are valid. // If not empty, the mask has the same length as the line. virtual void process (U& result, Bool& resultMask, const Vector& line, const Vector& mask, const IPosition& pos) = 0; // Collapse the given line and return a line of values from that operation. // The position in the Lattice at the start of the line is input // as well. //
        When function canHandleNullMask returned True, // it is possible that mask is an empty vector indicating // that the input has no mask, thus all values are valid. // If not empty, the mask has the same length as the line. virtual void multiProcess (Vector& result, Vector& resultMask, const Vector& line, const Vector& mask, const IPosition& pos) = 0; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/LatticeMath/LineCollapser.tcc000066400000000000000000000031331476623553700225620ustar00rootroot00000000000000//# LineCollapser.cc: Abstract base class to collapse lines //# Copyright (C) 1996,1997 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LINECOLLAPSER_TCC #define LATTICES_LINECOLLAPSER_TCC #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template LineCollapser::~LineCollapser() {} template Bool LineCollapser::canHandleNullMask() const { return False; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LatticeMath/MaskedLatticeStatsDataProvider.h000066400000000000000000000121551476623553700255460ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_MASKEDLATTICESTATSDATAPROVIDER_H #define LATTICES_MASKEDLATTICESTATSDATAPROVIDER_H #include #include #include #include namespace casacore { // Data provider which allows stats framework to iterate through a masked lattice. template class MaskedLatticeStatsDataProvider : public LatticeStatsDataProviderBase { public: // default constructor. Must set lattice after construction but before // using the object MaskedLatticeStatsDataProvider(); // iteratorLimitBytes is related to the size of the lattice. // If the lattice is greater than this size, then a lattice iterator will // be used to step through the lattice. If less, then all the data in the // values in the lattice are retrieved in a single chunk. The advantage of // the iterator is that less memory is used. The disadvantage is there is // a significant performace cost, so if the lattice is small, it is better to // get all its values in a single chunk and forgo the iterator. This is particularly // true when looping for a large number of iterations and creating a // MaskedLatticeStatsDataProvider each loop (in that case, you probably will want // to create a single object before the loop and use setLattice() to update // its lattice). MaskedLatticeStatsDataProvider( MaskedLattice& lattice, uInt iteratorLimitBytes=4096*4096 ); ~MaskedLatticeStatsDataProvider(); void operator++(); uInt estimatedSteps() const; // Are there any data sets left to provide? Bool atEnd() const; // Take any actions necessary to finalize the provider. This will be called when // atEnd() returns True. void finalize(); // get the count of elements in the current data set. When implementing this method, be // certain to take stride into account; ie for a data set with nominally 100 elements that // is to have a stride of two, this method should return 50. uInt64 getCount(); // get the current data set const T* getData(); // Get the associated mask of the current dataset. Only called if hasMask() returns True; const Bool* getMask(); // returns something reasonable based on the lattice size. uInt getNMaxThreads() const; // Does the current data set have an associated mask? Bool hasMask() const; // reset the provider to point to the first data set it manages. void reset(); // set the lattice. Automatically resets the lattice iterator. // iteratorLimitBytes is related to the size of the lattice. // If the lattice is greater than this size, then a lattice iterator will // be used to step through the lattice. If less, then all the data in the // values in the lattice are retrieved in a single chunk. The advantage of // the iterator is that less memory is used. The disadvantage is there is // a significant performace cost, so if the lattice is small, it is better to // get all its values in a single chunk and forgo the iterator. This is particularly // true when looping for a large number of iterations and creating a // MaskedLatticeStatsDataProvider each loop (in that case, you probably will want // to create a single object before the loop and use setLattice() to update // its lattice). void setLattice(const MaskedLattice& lattice, uInt iteratorLimitBytes=4096*4096); // // see base class documentation. void updateMaxPos(const std::pair& maxpos); void updateMinPos(const std::pair& minpos); // private: std::shared_ptr> _iter; Array _currentSlice; Array _currentMaskSlice; const T* _currentPtr; const Bool* _currentMaskPtr; Bool _delData, _delMask, _atEnd; uInt _nMaxThreads; void _freeStorage(); }; } #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/LatticeMath/MaskedLatticeStatsDataProvider.tcc000066400000000000000000000132401476623553700260640ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_MASKEDLATTICESTATSDATAPROVIDER_TCC #define LATTICES_MASKEDLATTICESTATSDATAPROVIDER_TCC #include namespace casacore { template MaskedLatticeStatsDataProvider::MaskedLatticeStatsDataProvider() : LatticeStatsDataProviderBase(), _iter(), /* _ary(), _mask(), */ _currentSlice(), _currentMaskSlice(), _currentPtr(0), _currentMaskPtr(0), _delData(False), _delMask(False), _atEnd(False), _nMaxThreads(0) {} template MaskedLatticeStatsDataProvider::MaskedLatticeStatsDataProvider( MaskedLattice& lattice, uInt ) : LatticeStatsDataProviderBase(), _iter(), _currentSlice(), _currentMaskSlice(), _currentPtr(0), _currentMaskPtr(0), _delData(False), _delMask(False) { setLattice(lattice); } template MaskedLatticeStatsDataProvider::~MaskedLatticeStatsDataProvider() {} template void MaskedLatticeStatsDataProvider::operator++() { _freeStorage(); if (! _iter) { _atEnd = True; } else { ++(*_iter); } this->_updateProgress(); } template uInt MaskedLatticeStatsDataProvider::estimatedSteps() const { if (! _iter) { return 1; } IPosition lattShape = _iter->latticeShape(); IPosition cursShape = _iter->cursor().shape(); uInt ndim = lattShape.size(); uInt count = 1; for (uInt i=0; i Bool MaskedLatticeStatsDataProvider::atEnd() const { if (! _iter) { return _atEnd; } else { return _iter->atEnd(); } } template void MaskedLatticeStatsDataProvider::finalize() { LatticeStatsDataProviderBase::finalize(); _freeStorage(); } template uInt64 MaskedLatticeStatsDataProvider::getCount() { if (! _iter) { return _currentSlice.size(); } else { return _iter->cursor().size(); } } template const T* MaskedLatticeStatsDataProvider::getData() { if (_iter) { _currentSlice.assign(_iter->cursor()); } _currentPtr = _currentSlice.getStorage(_delData); return _currentPtr; } template const Bool* MaskedLatticeStatsDataProvider::getMask() { if (_iter) { _currentMaskSlice.assign(_iter->getMask()); } _currentMaskPtr = _currentMaskSlice.getStorage(_delMask); return _currentMaskPtr; } template uInt MaskedLatticeStatsDataProvider::getNMaxThreads() const { #ifdef _OPENMP return _nMaxThreads; #else return 0; #endif } template Bool MaskedLatticeStatsDataProvider::hasMask() const { return True; } template void MaskedLatticeStatsDataProvider::reset() { LatticeStatsDataProviderBase::reset(); if (_iter) { _iter->reset(); } } template void MaskedLatticeStatsDataProvider::setLattice( const MaskedLattice& lattice, uInt iteratorLimitBytes ) { finalize(); if (lattice.size() > iteratorLimitBytes/sizeof(T)) { TileStepper stepper( lattice.shape(), lattice.niceCursorShape( lattice.advisedMaxPixels() ) ); _iter = std::make_shared>(lattice, stepper); } else { _iter = NULL; _currentSlice.assign(lattice.get()); _currentMaskSlice.assign(lattice.getMask()); _atEnd = False; } #ifdef _OPENMP _nMaxThreads = min( omp_get_max_threads(), (Int)ceil((Float)lattice.size()/ClassicalStatisticsData::BLOCK_SIZE) ); #endif } template void MaskedLatticeStatsDataProvider::updateMaxPos( const std::pair& maxpos ) { IPosition p = toIPositionInArray(maxpos.second, _currentSlice.shape()); if (_iter) { p += _iter->position(); } this->_updateMaxPos(p); } template void MaskedLatticeStatsDataProvider::updateMinPos( const std::pair& minpos ) { IPosition p = toIPositionInArray(minpos.second, _currentSlice.shape()); if (_iter) { p += _iter->position(); } this->_updateMinPos(p); } template void MaskedLatticeStatsDataProvider::_freeStorage() { _currentSlice.freeStorage (_currentPtr, _delData); _delData = False; _currentMaskSlice.freeStorage(_currentMaskPtr, _delMask); _delMask = False; } } #endif casacore-3.7.1/lattices/LatticeMath/MultiTermLatticeCleaner.h000066400000000000000000000166051476623553700242360ustar00rootroot00000000000000//# MultiTermLatticeCleaner.h: Minor Cycle for MSMFS deconvolution //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# Urvashi Rau #ifndef LATTICES_MULTITERMLATTICECLEANER_H #define LATTICES_MULTITERMLATTICECLEANER_H #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template class MultiTermLatticeCleaner : public LatticeCleaner { public: // Create a cleaner for a specific dirty image and PSF MultiTermLatticeCleaner(); // The copy constructor uses reference semantics MultiTermLatticeCleaner(const MultiTermLatticeCleaner & other); // The assignment operator also uses reference semantics MultiTermLatticeCleaner & operator=(const MultiTermLatticeCleaner & other); // The destructor does nothing special. ~MultiTermLatticeCleaner(); // Input : number of Taylor terms // Reshapes PtrBlocks to hold the correct number of PSFs and Residual images Bool setntaylorterms(const int & nterms); // Input : scales Bool setscales(const Vector & scales); // Initialize all the memory being used. Bool initialise(Int nx,Int ny); // Set control parameters. Bool setcontrol(CleanEnums::CleanType cleanType,const Int niter,const Float gain,const Quantity& aThreshold,const Bool choose); //# This function is defined in the base class LatticeCleaner, but was not //# defined in the new MultiTermLatticeCleaner. //# I (GvD) have added it for the time being. Bool setcontrol(CleanEnums::CleanType cleanType, const Int niter, const Float gain, const Quantity& aThreshold, const Quantity& /*fThreshold*/, const Bool choose=True) { return setcontrol (cleanType, niter, gain, aThreshold, choose); } // Input : psfs and dirty images Bool setpsf(int order, Lattice & psf); // Input : psfs and dirty images Bool setresidual(int order, Lattice & dirty); // Input : model images Bool setmodel(int order, Lattice & model); // Input : mask Bool setmask(Lattice & mask); // Run the minor cycle Int mtclean(LatticeCleanProgress* progress=0); // Output : Model images Bool getmodel(int order, Lattice & model); // Ouput : psfs and dirty images Bool getresidual(int order, Lattice & residual); // Output : Hessian matrix Bool getinvhessian(Matrix & invhessian); private: LogIO os; using LatticeCleaner::itsCleanType; using LatticeCleaner::itsMaxNiter; using LatticeCleaner::itsGain; using LatticeCleaner::itsThreshold; using LatticeCleaner::itsMask; using LatticeCleaner::itsPositionPeakPsf; using LatticeCleaner::findMaxAbsLattice; using LatticeCleaner::findMaxAbsMaskLattice; using LatticeCleaner::makeScale; using LatticeCleaner::addTo; using LatticeCleaner::makeBoxesSameSize; using LatticeCleaner::validatePsf; Int ntaylor_p; // Number of terms in the Taylor expansion to use. Int psfntaylor_p; // Number of terms in the Taylor expansion for PSF. Int nscales_p; // Number of scales to use for the multiscale part. Int nx_p; Int ny_p; Int totalIters_p; // Image mask TempLattice* dirty_p; TempLattice* dirtyFT_p; TempLattice* mask_p; TempLattice* fftmask_p; Vector scaleSizes_p; // Vector of scale sizes in pixels. Vector scaleBias_p; // Vector of scale biases !! Vector totalScaleFlux_p; // Vector of total scale fluxes. Vector totalTaylorFlux_p; // Vector of total flux in each taylor term. Float weightScaleFactor_p; Float maxPsf_p; IPosition gip,imshape; Int nx,ny,npol_p,nchan; Bool donePSF_p,donePSP_p,doneCONV_p; // h(s) [nx,ny,nscales] PtrBlock* > vecScales_p; PtrBlock* > vecScalesFT_p; // B_k [nx,ny,ntaylor] PtrBlock* > vecPsf_p; PtrBlock* > vecPsfFT_p; // I_D : Residual/Dirty Images [nx,ny,ntaylor] PtrBlock* > vecDirty_p; // I_M : Model Images [nx,ny,ntaylor] PtrBlock* > vecModel_p; // A_{smn} = B_{sm} * B{sn} [nx,ny,ntaylor,ntaylor,nscales,nscales] // A_{s1s2mn} = B_{s1m} * B{s2n} [nx,ny,ntaylor,ntaylor,nscales,nscales] PtrBlock* > cubeA_p; PtrBlock* > itercubeA_p; // R_{sk} = I_D * B_{sk} [nx,ny,ntaylor,nscales] PtrBlock* > matR_p; PtrBlock* > itermatR_p; // a_{sk} = Solution vectors. [nx,ny,ntaylor,nscales] PtrBlock* > matCoeffs_p; PtrBlock* > itermatCoeffs_p; // Memory to be allocated per TempLattice Double memoryMB_p; // Solve [A][Coeffs] = [I_D * B] // Shape of A : [ntaylor,ntaylor] PtrBlock*> matA_p; // 2D matrix to be inverted. PtrBlock*> invMatA_p; // Inverse of matA_p; // Scratch Lattices and iterators. TempLattice* cWork_p; TempLattice* tWork_p; LatticeIterator* itertWork_p; LatticeExprNode len_p; Float lambda_p; Int numberOfTempLattices(Int nscales,Int ntaylor); Int manageMemory(Bool allocate); Bool findMaxAbsLattice(const TempLattice& masklat,const Lattice& lattice,Float& maxAbs,IPosition& posMaxAbs, Bool flip=False); Int addTo(Lattice& to, const Lattice& add, Float multiplier); Int setupFFTMask(); Int setupUserMask(); Int setupBlobs(); Int computeFluxLimit(Float &fluxlimit, Float threshold); Int computeMatrixA(); Int computeRHS(); Int solveMatrixEqn(Int scale); Int computePenaltyFunction(Int scale, Float &loopgain, Bool choosespec); Int updateSolution(IPosition globalmaxpos, Int maxscaleindex, Float loopgain); Int checkConvergence(Bool choosespec, Float thresh, Float fluxlimit); Int IND2(Int taylor,Int scale); Int IND4(Int taylor1, Int taylor2, Int scale1, Int scale2); Bool adbg; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/LatticeMath/MultiTermLatticeCleaner.tcc000066400000000000000000001163761476623553700245660ustar00rootroot00000000000000//# Copyright (C) 1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_MULTITERMLATTICECLEANER_TCC #define LATTICES_MULTITERMLATTICECLEANER_TCC #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN #define MIN(a,b) ((a)<=(b) ? (a) : (b)) #define MAX(a,b) ((a)>=(b) ? (a) : (b)) template MultiTermLatticeCleaner::MultiTermLatticeCleaner(): ntaylor_p(2),donePSF_p(False),donePSP_p(False),doneCONV_p(False) { adbg=False; } template MultiTermLatticeCleaner:: MultiTermLatticeCleaner(const MultiTermLatticeCleaner & other): ntaylor_p(other.ntaylor_p) //And others... minus some... { } template MultiTermLatticeCleaner & MultiTermLatticeCleaner:: operator=(const MultiTermLatticeCleaner & other) { if (this != &other) { ntaylor_p = other.ntaylor_p; // and others..... minus some } return *this; } template MultiTermLatticeCleaner:: ~MultiTermLatticeCleaner() { manageMemory(False); } template Bool MultiTermLatticeCleaner::setscales(const Vector & scales) { nscales_p = scales.nelements(); scaleSizes_p.resize(); scaleSizes_p = scales; totalScaleFlux_p.resize(nscales_p); totalScaleFlux_p.set(0.0); return True; } template Bool MultiTermLatticeCleaner::setntaylorterms(const int & nterms) { ntaylor_p = nterms; psfntaylor_p = 2*nterms-1; totalTaylorFlux_p.resize(ntaylor_p); totalTaylorFlux_p.set(0.0); return True; } // Allocate memory, based on nscales and ntaylor template Bool MultiTermLatticeCleaner::initialise(Int nx, Int ny) { LogIO os(LogOrigin("MultiTermLatticeCleaner", "initialise()", WHERE)); /* Verify Image Shapes */ // nx_p = model.shape(0); nx_p = nx; ny_p = ny; if(adbg) os << "Checking shapes" << LogIO::POST; /* Verify nscales_p and ntaylor_p */ AlwaysAssert(nscales_p>0, AipsError); AlwaysAssert(ntaylor_p>0, AipsError); if(adbg) os << "Start allocating mem" << LogIO::POST; /* Allocate memory for many many TempLattices. */ manageMemory(True); /* Set up the default Mask image */ setupFFTMask(); /* Create the scaled blobs and their FTs */ setupBlobs(); if(adbg) os << "Finished initializing MultiTermLatticeCleaner" << LogIO::POST; return True; } template Bool MultiTermLatticeCleaner::setcontrol(CleanEnums::CleanType cleanType, const Int niter, const Float gain, const Quantity& aThreshold, const Bool choose) { itsCleanType=cleanType; itsMaxNiter=niter; itsGain=gain; itsThreshold=aThreshold; totalIters_p=0; return True; } template Bool MultiTermLatticeCleaner::setpsf(int order, Lattice & psf) { AlwaysAssert((order>=(int)0 && order<(int)vecPsf_p.nelements()), AipsError); if(order==0) AlwaysAssert(validatePsf(psf), AipsError); //AlwaysAssert(psf, AipsError); vecPsf_p[order]->copyData(LatticeExpr(psf)); vecPsfFT_p[order]->copyData(LatticeExpr(toComplex((*fftmask_p)*(*vecPsf_p[order])))); LatticeFFT::cfft2d(*vecPsfFT_p[order], True); return True; } /* Input : Dirty Images */ template Bool MultiTermLatticeCleaner::setresidual(int order, Lattice & dirty) { AlwaysAssert((order>=(int)0 && order<(int)vecDirty_p.nelements()), AipsError); //AlwaysAssert(dirty, AipsError); vecDirty_p[order]->copyData(LatticeExpr(dirty)); return True; } /* Input : Model Component Image */ template Bool MultiTermLatticeCleaner::setmodel(int order, Lattice & model) { AlwaysAssert((order>=(int)0 && order<(int)vecModel_p.nelements()), AipsError); //AlwaysAssert(model, AipsError); vecModel_p[order]->copyData(LatticeExpr(model)); totalTaylorFlux_p[order] = (sum( LatticeExpr(*vecModel_p[order]) )).getFloat(); return True; } /* Input : Mask */ template Bool MultiTermLatticeCleaner::setmask(Lattice & mask) { //AlwaysAssert(mask, AipsError); if(!itsMask) itsMask = new TempLattice(mask.shape(), memoryMB_p); itsMask->copyData(LatticeExpr(mask)); return True; } /* Output : Model Component Image */ template Bool MultiTermLatticeCleaner::getmodel(int order, Lattice & model) { AlwaysAssert((order>=(int)0 && order<(int)vecModel_p.nelements()), AipsError); //AlwaysAssert(model, AipsError); model.copyData(LatticeExpr(*vecModel_p[order])); return True; } /* Output Residual Image */ template Bool MultiTermLatticeCleaner::getresidual(int order, Lattice & residual) { AlwaysAssert((order>=(int)0 && order<(int)vecDirty_p.nelements()), AipsError); //AlwaysAssert(residual, AipsError); residual.copyData(LatticeExpr(*vecDirty_p[order])); return True; } /* Output Hessian matrix */ template Bool MultiTermLatticeCleaner::getinvhessian(Matrix & invhessian) { invhessian.resize((*invMatA_p[0]).shape()); invhessian = (*invMatA_p[0]); //*(*matA_p[0])(0,0); return True; } /* Do the deconvolution */ template Int MultiTermLatticeCleaner::mtclean(LatticeCleanProgress* progress) { LogIO os(LogOrigin("MultiTermLatticeCleaner", "mtclean()", WHERE)); if(adbg)os << "SOLVER for Multi-Frequency Synthesis deconvolution" << LogIO::POST; Int convergedflag = 0; Bool choosespec = True; //static Int totalIters=0; /* Set up the Mask image */ setupUserMask(); /* Compute the current peak residual */ Float zmaxval=0.0; IPosition zmaxpos; findMaxAbsLattice((*mask_p),(*vecDirty_p[0]),zmaxval,zmaxpos); os << "Initial Max Residual at iteration " << totalIters_p << " : " << zmaxval << " at " << zmaxpos << LogIO::POST; if(totalIters_p==0) { for(Int i=0;i<2*ntaylor_p-1;i++) { findMaxAbsLattice((*mask_p),(*vecPsf_p[i]),zmaxval,zmaxpos); os << "Psf " << i << " : " << zmaxval << " at " << zmaxpos << LogIO::POST; } } /* Compute all convolutions and the matrix A */ /* If matrix is not invertible, return ! */ if( computeMatrixA() == -2 ) return -2; /* Compute the convolutions of the current residual with all PSFs and scales */ computeRHS(); /* Compute the flux limits that determine the depth of the minor cycles. */ Float fluxlimit =0.0; Float loopgain = itsGain; Float thresh = itsThreshold.getValue("Jy"); computeFluxLimit(fluxlimit,thresh); /* Initialize persistent variables */ gip = IPosition(4,nx_p,ny_p,1,1); Float maxval,globalmaxval=-1e+10; IPosition maxpos(4,0),globalmaxpos(4,0); Int maxscaleindex=0; Int niters = itsMaxNiter; /********************** START MINOR CYCLE ITERATIONS ***********************/ //Int numiters = MIN(40,niters-totalIters_p); Int numiters = niters-totalIters_p; //cout << "niters,itsMaxiter : " << niters << " ,totalIters_p : " << totalIters_p << " , numiters : " << numiters << endl; /* If no iterations */ if(numiters<=0) { os << "Reached max number of iterations" << LogIO::POST; convergedflag=-1; return (convergedflag); } for(Int itercount=0;itercount globalmaxval) if((maxval*scaleBias_p[scale]) > globalmaxval) { globalmaxval = maxval; globalmaxpos = maxpos; maxscaleindex = scale; } }// end of for scale /* Update the current solution by this chosen step */ updateSolution(globalmaxpos,maxscaleindex,loopgain); /* Compute peak residuals */ Float maxres=0.0; IPosition maxrespos; findMaxAbsLattice((*mask_p),(*matR_p[IND2(0,0)]),maxres,maxrespos); Float norma = (1.0/(*matA_p[0])(0,0)); Float rmaxval = maxres*norma; /* Print out coefficients at each iteration */ //if(adbg) { //os << "[" << totalIters_p << "] Res: " << rmaxval << " Max: " << globalmaxval; os << "[" << totalIters_p << "] Res: " << rmaxval; os << " Pos: " << globalmaxpos << " Scale: " << scaleSizes_p[maxscaleindex]; os << " Coeffs: "; for(Int taylor=0;taylor(tgip); invMatA_p[i] = new Matrix(tgip); } else { delete matA_p[i] ; delete invMatA_p[i] ; } } /// Make this read from model.shape() or image.shape() gip = IPosition(4,nx_p,ny_p,1,1); // I_D and mask if(direction) { dirty_p = new TempLattice(gip, memoryMB_p); dirtyFT_p = new TempLattice(gip, memoryMB_p); mask_p = new TempLattice(gip, memoryMB_p); fftmask_p = new TempLattice(gip, memoryMB_p); // Temporary work-holder cWork_p = new TempLattice(gip,memoryMB_p); tWork_p = new TempLattice(gip,memoryMB_p); } else { delete dirty_p; delete dirtyFT_p; delete fftmask_p; delete mask_p; delete cWork_p; delete tWork_p; } // Mask if(direction) itsMask=0; else { if(itsMask){ delete itsMask; itsMask=0;} } // Scales vecScales_p.resize(nscales_p); vecScalesFT_p.resize(nscales_p); for(Int i=0;i(gip,memoryMB_p); vecScalesFT_p[i] = new TempLattice(gip,memoryMB_p); } else { delete vecScales_p[i]; delete vecScalesFT_p[i]; } } // Psfs and Models vecPsf_p.resize(psfntaylor_p); vecPsfFT_p.resize(psfntaylor_p); for(Int i=0;i(gip,memoryMB_p); vecPsfFT_p[i] = new TempLattice(gip,memoryMB_p); } else { delete vecPsf_p[i]; delete vecPsfFT_p[i]; } } // Dirty/Residual Images vecDirty_p.resize(ntaylor_p); vecModel_p.resize(ntaylor_p); for(Int i=0;i(gip,memoryMB_p); vecModel_p[i] = new TempLattice(gip,memoryMB_p); } else { delete vecDirty_p[i]; delete vecModel_p[i]; } } // Psf * Scales // matPsfConvScales_p.resize(ntaylor_p*nscales_p); // for(Int i=0;i(gip,memoryMB_p); // Set up the latticeiterators also IPosition shapeOut; IPosition cursorShape; if(direction) { AlwaysAssert (tWork_p->isWritable(), AipsError); shapeOut = IPosition(tWork_p->shape()); cursorShape = IPosition(tWork_p->niceCursorShape()); } else { shapeOut = gip; cursorShape = gip; } LatticeStepper stepper(shapeOut, cursorShape, LatticeStepper::RESIZE); if(direction)itertWork_p = new LatticeIterator((*tWork_p), stepper); else delete itertWork_p; // (Psf * Scales) * (Psf * Scales) cubeA_p.resize(ntotal4d); itercubeA_p.resize(ntotal4d); for(Int i=0;i(gip,memoryMB_p); itercubeA_p[i] = new LatticeIterator((*cubeA_p[i]),stepper); } else { delete cubeA_p[i]; delete itercubeA_p[i]; } } // I_D * (Psf * Scales) matR_p.resize(ntaylor_p*nscales_p); itermatR_p.resize(ntaylor_p*nscales_p); // Coefficients to be solved for. matCoeffs_p.resize(ntaylor_p*nscales_p); itermatCoeffs_p.resize(ntaylor_p*nscales_p); for(Int i=0;i(gip,memoryMB_p); itermatR_p[i] = new LatticeIterator((*matR_p[i]),stepper); matCoeffs_p[i] = new TempLattice(gip,memoryMB_p); itermatCoeffs_p[i] = new LatticeIterator((*matCoeffs_p[i]),stepper); } else { delete matR_p[i]; delete itermatR_p[i]; delete matCoeffs_p[i]; delete itermatCoeffs_p[i]; } } if(adbg) os << "done" << LogIO::POST; return 0; } /************************************* * Add two subLattices.. -- same code as in copyData. *************************************/ template Int MultiTermLatticeCleaner::addTo(Lattice& to, const Lattice& add, Float multiplier) { // Check the lattice is writable. // Check the shape conformance. AlwaysAssert (to.isWritable(), AipsError); const IPosition shapeIn = add.shape(); const IPosition shapeOut = to.shape(); AlwaysAssert (shapeIn.isEqual (shapeOut), AipsError); IPosition cursorShape = to.niceCursorShape(); LatticeStepper stepper (shapeOut, cursorShape, LatticeStepper::RESIZE); LatticeIterator toIter(to, stepper); RO_LatticeIterator addIter(add, stepper); for (addIter.reset(), toIter.reset(); !addIter.atEnd();addIter++, toIter++) { toIter.rwCursor()+=addIter.cursor()*multiplier; } return 0; } /*************************************** * Set up the Masks. ****************************************/ template Int MultiTermLatticeCleaner::setupFFTMask() { /* Set up fftmask - inner quarter */ (*fftmask_p).set(0.0); IPosition mblc(4,nx_p/4,ny_p/4,0,0); IPosition mtrc(4,3*nx_p/4,3*ny_p/4,0,0); IPosition minc(4, 1); LCBox::verify(mblc,mtrc,minc,(*fftmask_p).shape()); LCBox regmask(mblc,mtrc,(*fftmask_p).shape()); SubLattice smask((*fftmask_p),regmask,True); smask.set(1.0); return 0; }/* end of setupFFTMask() */ template Int MultiTermLatticeCleaner::setupUserMask() { /* Copy the input mask */ if(itsMask) { Int pol=0; IPosition blc1(4,0,0,pol,0); IPosition trc1(4,nx_p,ny_p,pol,0); IPosition inc1(4, 1); LCBox::verify(blc1,trc1,inc1,itsMask->shape()); LCBox singlepolmask(blc1,trc1,itsMask->shape()); (mask_p)->copyData(SubLattice(*itsMask,singlepolmask,True)); /* Reconcile the two masks */ (*mask_p).copyData(LatticeExpr((*mask_p)*(*fftmask_p))); } else { (*mask_p).copyData(LatticeExpr((*fftmask_p))); } return 0; }/* end of setupUserMask() */ /*************************************** * Set up the Blobs of various scales. ****************************************/ template Int MultiTermLatticeCleaner::setupBlobs() { LogIO os(LogOrigin("MultiTermLatticeCleaner", "setupBlobs", WHERE)); // Set the scale sizes if(scaleSizes_p.nelements()==0) { scaleSizes_p.resize(nscales_p); Float scaleInc = 2.0; scaleSizes_p[0] = 0.0; //os << "scale 1 = " << scaleSizes_p(0) << " pixels" << LogIO::POST; for (Int scale=1; scale1) { for(Int scale=0;scalecopyData(LatticeExpr(toComplex((*fftmask_p)*(*vecScales_p[scale])))); // Now FFT LatticeFFT::cfft2d(*vecScalesFT_p[scale], True); if(0)//(adbg) { String llab("blob_"+String::toString((Int)scaleSizes_p(scale))+".im"); gip = IPosition(4,nx_p,ny_p,1,1); TempLattice store(gip,memoryMB_p); store.copyData(LatticeExpr(real(*vecScalesFT_p[scale]))); String fllab("blobft_"+String::toString((Int)scaleSizes_p(scale))+".im"); } } donePSP_p=True; } return 0; }/* end of setupBlobs() */ /*************************************** * Compute convolutions and the A matrix. ****************************************/ template Int MultiTermLatticeCleaner::computeMatrixA() { LogIO os(LogOrigin("MultiTermLatticeCleaner", "computeMatrixA", WHERE)); gip = IPosition(4,nx_p,ny_p,1,1); if(!doneCONV_p) { // Compute the convolutions of the smoothed psfs with each other. // Compute Assxx // Compute A100, A101, A102 // A110, A111, A112 // A120, A121, A122 for h(s1) // Compute A200, A201, A202 // A210, A211, A212 // A220, A221, A222 for h(s2) //... depending on the number of scales chosen // (PSF * scale) * (PSF * scale) -> cubeA_p [nx_p,ny_p,ntaylor,ntaylor,nscales] os << "Calculating PSF and Scale convolutions " << LogIO::POST; for (Int taylor1=0; taylor1 dpsExpr(((*vecPsfFT_p[ttay1]) *(*vecPsfFT_p[0]))*(*vecScalesFT_p[scale1])*(*vecScalesFT_p[scale2])); cWork_p->copyData(dpsExpr); LatticeFFT::cfft2d(*cWork_p, False); AlwaysAssert(cubeA_p[IND4(taylor1,taylor2,scale1,scale2)], AipsError); LatticeExpr realWork2(real(*cWork_p)); cubeA_p[IND4(taylor1,taylor2,scale1,scale2)]->copyData(realWork2); Float zmaxval=0.0; IPosition zmaxpos; findMaxAbsLattice((*mask_p),(*cubeA_p[IND4(taylor1,taylor2,scale1,scale2)]),zmaxval,zmaxpos); //if(adbg) os << "Max (result) : " << zmaxval << " at " << zmaxpos << LogIO::POST; } // Construct A, invA for each scale. IPosition wip(4,0,0,0,0); wip[0]=(nx_p/2); wip[1]=(ny_p/2); Int stopnow=False; for (Int scale=0; scale ratios(ntaylor_p); Float tsum=0.0; for(Int taylor1=0; taylor1 the Right-Hand-Side of the matrix equation. ****************************************/ template Int MultiTermLatticeCleaner::computeRHS() { LogIO os(LogOrigin("MultiTermLatticeCleaner", "computeRHS()", WHERE)); IPosition blc1(4,0,0,0,0); IPosition trc1(4,nx_p,ny_p,0,0); IPosition inc1(4, 1); /* Compute R10 = I_D*B10, R11 = I_D*B11, R12 = I_D*B12 * Compute R20 = I_D*B20, R21 = I_D*B21, R22 = I_D*B22 * ... depending on the number of scales chosen. */ //cout << "Writing residual images to disk..." << endl; //storeAsImg("temp_residual_0",residual(0)); //storeAsImg("temp_residual_1",residualspec(0,1)); /* I_D * (PSF * scale) -> matR_p [nx_p,ny_p,ntaylor,nscales] */ os << "Calculating convolutions of dirty image with scales and PSFs " << LogIO::POST; for (Int taylor=0; taylorcopyData(LatticeExpr(toComplex((*fftmask_p)*(*vecDirty_p[taylor])))); LatticeFFT::cfft2d(*dirtyFT_p, True); for (Int scale=0; scale dpsExpr( (*dirtyFT_p)*(*vecPsfFT_p[0])*(*vecScalesFT_p[scale])); cWork_p->copyData(dpsExpr); LatticeFFT::cfft2d(*cWork_p, False); AlwaysAssert(matR_p[IND2(taylor,scale)], AipsError); LatticeExpr realWork2(real(*cWork_p)); matR_p[IND2(taylor,scale)]->copyData(realWork2); //String lab("_"+String::toString(taylor)+"_"+String::toString(scale)); } } return 0; }/* end of computeRHS() */ /*************************************** * Compute flux limit for minor cycles ****************************************/ template Int MultiTermLatticeCleaner::computeFluxLimit(Float &fluxlimit, Float threshold) { LogIO os(LogOrigin("MultiTermLatticeCleaner", "computeFluxLimit", WHERE)); // Find max residual ( from all scale and taylor convos of the residual image ) // Find max ext PSF value ( from all scale convos of all the PSFs ) // factor = 0.01; // fluxlimit = maxRes * maxExtPsf * factor; /* Float maxRes=0.0; Float maxExtPsf=0.0; Float tmax=0.0; IPosition tmaxpos; Float ffactor=0.01; Int maxscale=0; for(Int taylor=0;taylor maxRes) maxscale = scale; maxRes = MAX(maxRes,tmax); cout << "MaxRes for taylor " << taylor << " and scale " << scale << " : " << maxRes << endl; } for (Int taylor1=0; taylor1 Int MultiTermLatticeCleaner::solveMatrixEqn(Int scale) { /* Solve for the coefficients */ for(Int taylor1=0;taylor1(len_p)); } return 0; }/* end of solveMatrixEqn() */ /*************************************** * Compute the penalty function ****************************************/ template Int MultiTermLatticeCleaner::computePenaltyFunction(Int scale, Float &loopgain, Bool choosespec) { tWork_p->set(0.0); for(Int i=0;i<(Int)itermatCoeffs_p.nelements();i++) itermatCoeffs_p[i]->reset(); for(Int i=0;i<(Int)itercubeA_p.nelements();i++) itercubeA_p[i]->reset(); for(Int i=0;i<(Int)itermatR_p.nelements();i++) itermatR_p[i]->reset(); for(itertWork_p->reset(); !(itertWork_p->atEnd()); (*itertWork_p)++) { if(choosespec) { for(Int taylor1=0;taylor1rwCursor() += (Float)2.0*((itermatCoeffs_p[IND2(taylor1,scale)])->rwCursor())*((itermatR_p[IND2(taylor1,scale)])->rwCursor()); for(Int taylor2=0;taylor2rwCursor() -= ((itermatCoeffs_p[IND2(taylor1,scale)])->rwCursor())*((itermatCoeffs_p[IND2(taylor2,scale)])->rwCursor())*((itercubeA_p[IND4(taylor1,taylor2,scale,scale)])->rwCursor()); } // Constrain location too, based on the I0 flux being > thresh*5 or something.. } else { if(loopgain > 0.5) loopgain*=0.5; Float norm = sqrt((1.0/(*matA_p[scale])(0,0))); itertWork_p->rwCursor() += norm*((itermatR_p[IND2(0,scale)])->rwCursor()); } for(Int i=0;i<(Int)itermatCoeffs_p.nelements();i++) (*itermatCoeffs_p[i])++; for(Int i=0;i<(Int)itercubeA_p.nelements();i++) (*itercubeA_p[i])++; for(Int i=0;i<(Int)itermatR_p.nelements();i++) (*itermatR_p[i])++; } return 0; }/* end of computePenaltyFunction() */ /*************************************** * Update the model images and the convolved residuals ****************************************/ template Int MultiTermLatticeCleaner::updateSolution(IPosition globalmaxpos, Int maxscaleindex, Float loopgain) { gip = IPosition(4,nx_p,ny_p,1,1); IPosition support(4,nx_p/2,ny_p/2,0,0); //IPosition psfpeak(support); IPosition psfpeak(itsPositionPeakPsf); globalmaxpos[2]=0; globalmaxpos[3]=0; /* Region for the inner quarter..... the update region. */ IPosition inc(4,1,1,0,0); IPosition blc(psfpeak-support/2); IPosition trc(psfpeak+support/2-IPosition(4,1,1,0,0)); LCBox::verify(blc, trc, inc, gip); /* Shifted region, with the psf at the globalmaxpos. */ IPosition blcPsf(2*psfpeak-support/2-globalmaxpos); IPosition trcPsf(2*psfpeak+support/2-globalmaxpos-IPosition(4,1,1,0,0)); LCBox::verify(blcPsf, trcPsf, inc, gip); makeBoxesSameSize(blc,trc,blcPsf,trcPsf); LCBox subRegion(blc,trc,gip); LCBox subRegionPsf(blcPsf,trcPsf,gip); /* Update the model image */ for(Int taylor=0;taylor modelSub(*vecModel_p[taylor],subRegion,True); SubLattice scaleSub((*vecScales_p[maxscaleindex]),subRegionPsf,True); addTo(modelSub,scaleSub,loopgain*(*matCoeffs_p[IND2(taylor,maxscaleindex)]).getAt(globalmaxpos)); } /* Update the convolved residuals */ for(Int scale=0;scale residSub((*matR_p[IND2(taylor1,scale)]),subRegion,True); for(Int taylor2=0;taylor2 smoothSub((*cubeA_p[IND4(taylor1,taylor2,scale,maxscaleindex)]),subRegionPsf,True); addTo(residSub,smoothSub,-1*loopgain*(*matCoeffs_p[IND2(taylor2,maxscaleindex)]).getAt(globalmaxpos)); } } /* Update flux counters */ for(Int taylor=0;taylor Int MultiTermLatticeCleaner::checkConvergence(Bool choosespec, Float thresh, Float fluxlimit) { /* Calculate convergence thresholds..... */ Float rmaxval=0.0; #if 0 /* Use the strongest I0 component, to compare against the convergence threshold */ Float compval = fabs((*matCoeffs_p[IND2(0,maxscaleindex)]).getAt(globalmaxpos)); //Float compval = fabs((*matCoeffs_p[IND2(0,maxscaleindex)]).getAt(globalmaxpos)) * (scaleSizes_p[maxscaleindex]+1); rmaxval = MAX( rmaxval , compval ); #endif #if 1 /* Use the maximum residual (current), to compare against the convergence threshold */ Float maxres=0.0; IPosition maxrespos; findMaxAbsLattice((*mask_p),(*matR_p[IND2(0,0)]),maxres,maxrespos); Float norma = (1.0/(*matA_p[0])(0,0)); //rmaxval = MAX(rmaxval, maxres*norma/5.0); rmaxval = maxres*norma; #endif /* Check for convergence */ /* Switch between penalty functions, after a I0 component lower than the threshold is picked. Until then, pick components that minimize chi-sq. After switching, pick components that correspond to the peak I0 residual */ Int convergedflag = 0; // 0 : continue // 1 : converged because of fluxlimit for this cycle // 2 : converged because of threshold // -1 : stopped because of iteration limit. if( (fabs(rmaxval) < thresh) ){ convergedflag = 2;} else { if( fabs(rmaxval) < fluxlimit ) { convergedflag=1; } } //if((fabs(rmaxval) < fluxlimit) || (fabs(rmaxval) < thresh*1.5 && !choosespec)) //{convergedflag=1;} //else //{ // if(fabs(rmaxval) < thresh*5.0 && choosespec) // {convergedflag=0; choosespec=False; if(adbg)os << "Switching stopping criterion" << LogIO::POST;} //} /* Stop, if there are negatives on the largest scale in the Io image */ //if(nscales_p>1 && maxscaleindex == nscales_p-2) // if((*matCoeffs_p[IND2(0,maxscaleindex)]).getAt(globalmaxpos) < 0.0) // {converged = False;break;} return convergedflag; }/* end of checkConvergence */ /************************************* * Find the max and position * - restrict this to within the inner quarter. *************************************/ template Bool MultiTermLatticeCleaner::findMaxAbsLattice(const TempLattice& masklat,const Lattice& lattice,Float& maxAbs,IPosition& posMaxAbs, Bool flip) { AlwaysAssert(masklat.shape()==lattice.shape(), AipsError); Array msk; posMaxAbs = IPosition(lattice.shape().nelements(), 0); maxAbs=0.0; //maxAbs=-1.0e+10; const IPosition tileShape = lattice.niceCursorShape(); TiledLineStepper ls(lattice.shape(), tileShape, 0); TiledLineStepper lsm(masklat.shape(), tileShape, 0); { RO_LatticeIterator li(lattice, ls); RO_LatticeIterator lim(masklat, lsm); for(li.reset(),lim.reset();!li.atEnd();li++,lim++) { IPosition posMax=li.position(); IPosition posMin=li.position(); Float maxVal=0.0; Float minVal=0.0; msk = lim.cursor(); if(flip) msk = (Float)1.0 - msk; //minMaxMasked(minVal, maxVal, posMin, posMax, li.cursor(),lim.cursor()); minMaxMasked(minVal, maxVal, posMin, posMax, li.cursor(),msk); if((maxVal)>(maxAbs)) { maxAbs=maxVal; posMaxAbs=li.position(); posMaxAbs(0)=posMax(0); } } } return True; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LatticeMath/StatsTiledCollapser.h000066400000000000000000000153261476623553700234400ustar00rootroot00000000000000//# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_STATSTILEDCOLLAPSER_H #define LATTICES_STATSTILEDCOLLAPSER_H //# Includes #include #include namespace casacore { // Generate statistics, tile by tile, from a masked lattice // NOTE this version was moved from LatticeStatistics (early Dec 2014 version) // and slightly modified mostly for style issues (no significant semantic differences // from that version). For a large number of statistics sets that need to be computed // simultaneously, this version is more efficient than using the new stats framework, // because creating large numbers of eg ClassicalStatistics objects is much less efficient // than the direct manipulation of pointers to primitive types that this class does. // // // // // // // //
      • LatticeApply //
      • TiledCollapser // // // // This class is used by LatticeStatistics to generate // statistical sum from an input MaskedLattice. // The input lattice is iterated through in tile-sized chunks // and fed to an object of this class. // // // // StatsTiledCollapser is derived from TiledCollapser which // is a base class used to define methods. Objects of this base class are // used by LatticeApply functions. In this particular case, // we are interested in LatticeApply::tiledApply. This function iterates // through a MaskedLattice and allows you to collapse one or more // axes, computing some values from it, and placing those values into // an output MaskedLattice. It iterates through the input // lattice in optimal tile-sized chunks. LatticeStatistics // uses a StatsTiledCollapser object which it gives to // LatticeApply::tiledApply for digestion. After it has // done its work, LatticeStatistics then accesses the output // Lattice that it made. // // // // //// Create collapser. Control information is passed in via the constructor // // StatsTiledCollapser collapser(range_p, noInclude_p, noExclude_p, // fixedMinMax_p, blcParent_p); // //// This is the first output axis getting collapsed values. In LatticeStatistics //// this is the last axis of the output lattice // // Int newOutAxis = outLattice.ndim()-1; // //// tiledApply does the work by passing the collapser data in chunks //// and by writing the results into the output lattice // // LatticeApply::tiledApply(outLattice, inLattice, // collapser, collapseAxes, // newOutAxis); // // // In this example, a collapser is made and passed to LatticeApply. // Afterwards, the output Lattice is available for use. // The Lattices must all be the correct shapes on input to tiledApply // // // // The LatticeApply classes enable the ugly details of optimal // Lattice iteration to be hidden from the user. // // // //
      • // template class StatsTiledCollapser : public TiledCollapser { public: // Constructor provides pixel selection range and whether that // range is an inclusion or exclusion range. If fixedMinMax=True // and an inclusion range is given, the min and max is set to // that inclusion range. StatsTiledCollapser( const Vector& pixelRange, Bool noInclude, Bool noExclude, Bool fixedMinMax ); virtual ~StatsTiledCollapser() {} // Initialize process, making some checks virtual void init (uInt nOutPixelsPerCollapse); // Initialiaze the accumulator virtual void initAccumulator (uInt64 n1, uInt64 n3); // Process the data in the current chunk. virtual void process ( uInt accumIndex1, uInt accumIndex3, const T* inData, const Bool* inMask, uInt dataIncr, uInt maskIncr, uInt nrval, const IPosition& startPos, const IPosition& shape ); // End the accumulation process and return the result arrays virtual void endAccumulator(Array& result, Array& resultMask, const IPosition& shape); // Can handle null mask virtual Bool canHandleNullMask() const {return True;}; // Find the location of the minimum and maximum data values // in the input lattice. void minMaxPos(IPosition& minPos, IPosition& maxPos); private: Vector _range; Bool _include, _exclude, _fixedMinMax, _isReal; IPosition _minpos, _maxpos; // Accumulators for sum, sum squared, number of points // minimum, and maximum std::shared_ptr> _npts; std::shared_ptr> _sum, _sumSq, _mean, _variance, _nvariance, _sigma; std::shared_ptr> _min, _max; std::shared_ptr> _initMinMax; uInt64 _n1, _n3; void _convertNPts( Double*& nptsPtr, std::shared_ptr> npts, std::shared_ptr> nptsComplex ) const; void _convertNPts( DComplex*& nptsPtr, std::shared_ptr> npts, std::shared_ptr> nptsComplex ) const; }; } #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif #endif casacore-3.7.1/lattices/LatticeMath/StatsTiledCollapser.tcc000066400000000000000000000237731476623553700237670ustar00rootroot00000000000000//# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include namespace casacore { template StatsTiledCollapser::StatsTiledCollapser( const Vector& pixelRange, Bool noInclude, Bool noExclude, Bool fixedMinMax ) : _range(pixelRange), _include(! noInclude), _exclude(! noExclude), _fixedMinMax(fixedMinMax), _isReal(isReal(whatType())), _minpos(0), _maxpos(0) {} template void StatsTiledCollapser::init (uInt nOutPixelsPerCollapse) { AlwaysAssert (nOutPixelsPerCollapse == LatticeStatsBase::NACCUM, AipsError); } template void StatsTiledCollapser::initAccumulator (uInt64 n1, uInt64 n3) { _sum = std::make_shared>(n1*n3); _sumSq = std::make_shared>(n1*n3); _npts = std::make_shared>(n1*n3); _mean = std::make_shared>(n1*n3); _variance = std::make_shared>(n1*n3); _sigma = std::make_shared>(n1*n3); _nvariance = std::make_shared>(n1*n3); _min = std::make_shared>(n1*n3); _max = std::make_shared>(n1*n3); _initMinMax = std::make_shared>(n1*n3); _sum->set(0); _sumSq->set(0); _npts->set(0); _mean->set(0); _variance->set(0); _sigma->set(0); _nvariance->set(0); _min->set(0); _max->set(0); _initMinMax->set(True); _n1 = n1; _n3 = n3; } template void StatsTiledCollapser::process ( uInt index1, uInt index3, const T* pInData, const Bool* pInMask, uInt dataIncr, uInt maskIncr, uInt nrval, const IPosition& startPos, const IPosition& shape ) { // Process the data in the current chunk. Everything in this // chunk belongs in one output location in the storage // lattices uInt64 index = index1 + index3*_n1; U& sum = (*_sum)[index]; U& sumSq = (*_sumSq)[index]; Double& nPts = (*_npts)[index]; T& dataMin = (*_min)[index]; T& dataMax = (*_max)[index]; U& mean = (*_mean)[index]; U& variance = (*_variance)[index]; U& sigma = (*_sigma)[index]; U& nvariance = (*_nvariance)[index]; // If these are != -1 after the accumulating, then // the min and max were updated Int64 minLoc = -1; Int64 maxLoc = -1; std::vector> ranges; Bool isInclude = False; Bool hasRange = _include || _exclude; if (hasRange) { ranges.resize(1); ranges[0] = std::make_pair(_range[0], _range[1]); isInclude = _include; } typename vector>::const_iterator beginRange = ranges.begin(); typename vector>::const_iterator endRange = ranges.end(); Int64 i = 0; if (pInMask == 0) { // All pixels are unmasked if (hasRange) { for (i=0; i<(Int64)nrval; ++i) { if ( StatisticsUtilities::includeDatum( *pInData, beginRange, endRange, isInclude ) ) { StatisticsUtilities::accumulate( nPts, sum, mean, nvariance, sumSq, dataMin, dataMax, minLoc, maxLoc, *pInData, i ); } pInData += dataIncr; } if (_include && _fixedMinMax) { dataMin = _range(0); dataMax = _range(1); } } else { // no range for (Int64 i=0; i<(Int64)nrval; ++i) { StatisticsUtilities::accumulate( nPts, sum, mean, nvariance, sumSq, dataMin, dataMax, minLoc, maxLoc, *pInData, i ); pInData += dataIncr; } } } else { // Some pixels are masked if (hasRange) { for (i=0; i<(Int64)nrval; ++i) { if ( *pInMask && StatisticsUtilities::includeDatum( *pInData, beginRange, endRange, isInclude ) ) { StatisticsUtilities::accumulate( nPts, sum, mean, nvariance, sumSq, dataMin, dataMax, minLoc, maxLoc, *pInData, i ); } pInData += dataIncr; pInMask += maskIncr; } if (_include && _fixedMinMax) { dataMin = _range(0); dataMax = _range(1); } } else { // no ranges for (i=0; i<(Int64)nrval; ++i) { if (*pInMask) { StatisticsUtilities::accumulate( nPts, sum, mean, nvariance, sumSq, dataMin, dataMax, minLoc, maxLoc, *pInData, i ); } pInData += dataIncr; pInMask += maskIncr; } } } variance = nPts > 1 ? nvariance/(nPts - 1) : 0; sigma = sqrt(variance); // Update overall min and max location. These are never updated // if fixedMinMax is true. These values are only meaningful for // Float images. For Complex they are useless currently. if (_isReal) { if (minLoc != -1) { _minpos = startPos + toIPositionInArray(minLoc, shape); } if (maxLoc != -1) { _maxpos = startPos + toIPositionInArray(maxLoc, shape); } } } template void StatsTiledCollapser::endAccumulator( Array& result, Array& resultMask, const IPosition& shape ) { // Reshape arrays. The mask is always true. Any locations // in the storage lattice for which there were no valid points // will have the NPTS field set to zero. That is what // we use to effectively mask it. result.resize(shape); result.set(U(0)); resultMask.resize(shape); resultMask.set(True); Bool deleteRes; U* res = result.getStorage (deleteRes); U* resptr = res; U* sumPtr = _sum->storage(); U* sumSqPtr = _sumSq->storage(); std::shared_ptr> nptsComplex; if (! isReal(whatType())) { nptsComplex = std::make_shared>(_n1*_n3); } U* nPtsPtr; _convertNPts(nPtsPtr, _npts, nptsComplex); U* meanPtr = _mean->storage(); U* variancePtr = _variance->storage(); U* sigmaPtr = _sigma->storage(); const T* minPtr = _min->storage(); const T* maxPtr = _max->storage(); uInt64 i, j; U* resptr_root = resptr; for (i=0; i<_n3; ++i) { resptr = resptr_root + (Int(LatticeStatsBase::NPTS) * _n1); objcopy (resptr, nPtsPtr, _n1); nPtsPtr += _n1; resptr = resptr_root + (Int(LatticeStatsBase::SUM) * _n1); objcopy (resptr, sumPtr, _n1); sumPtr += _n1; resptr = resptr_root + (Int(LatticeStatsBase::SUMSQ) * _n1); objcopy (resptr, sumSqPtr, _n1); sumSqPtr += _n1; resptr = resptr_root + (Int(LatticeStatsBase::MEAN) * _n1); objcopy (resptr, meanPtr, _n1); meanPtr += _n1; resptr = resptr_root + (Int(LatticeStatsBase::VARIANCE) * _n1); objcopy (resptr, variancePtr, _n1); variancePtr += _n1; resptr = resptr_root + (Int(LatticeStatsBase::SIGMA) * _n1); objcopy (resptr, sigmaPtr, _n1); sigmaPtr += _n1; resptr = resptr_root + (Int(LatticeStatsBase::MIN) * _n1); for (j=0; j<_n1; ++j) { convertScalar (*resptr++, *minPtr++); } resptr = resptr_root + (Int(LatticeStatsBase::MAX) * _n1); for (j=0; j<_n1; ++j) { convertScalar (*resptr++, *maxPtr++); } resptr_root += _n1 * Int(LatticeStatsBase::NACCUM); } result.putStorage (res, deleteRes); } template void StatsTiledCollapser::_convertNPts( Double*& nptsPtr, std::shared_ptr> npts, std::shared_ptr> ) const { nptsPtr = npts->storage(); } template void StatsTiledCollapser::_convertNPts( DComplex*& nptsPtr, std::shared_ptr> npts, std::shared_ptr> nptsComplex ) const { DComplex* storage = nptsComplex->storage(); Double* realStorage = npts->storage(); for (uInt64 i=0; i<_n1*_n3; ++i) { ///C++11 storage[i].real(realStorage[i]); ///C++11 storage[i].imag(0); storage[i] = DComplex(realStorage[i], 0); } nptsPtr = storage; } template void StatsTiledCollapser::minMaxPos(IPosition& minPos, IPosition& maxPos) { minPos.resize(_minpos.nelements()); minPos = _minpos; maxPos.resize(_maxpos.nelements()); maxPos = _maxpos; } } casacore-3.7.1/lattices/LatticeMath/TiledCollapser.h000066400000000000000000000144041476623553700224150ustar00rootroot00000000000000//# TiledCollapser.h: Abstract base class to collapse chunks for LatticeApply //# Copyright (C) 1996,1997,1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_TILEDCOLLAPSER_H #define LATTICES_TILEDCOLLAPSER_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class IPosition; // // Abstract base class to collapse chunks for LatticeApply // // // // // //
      • LatticeApply // // // // // This is an abstract base class for the collapsing of chunks to // be used in function tiledApply // in class LatticeApply. // It is meant for cases where an entire line or plane is not needed // (e.g. calculation of maximum). If that is needed (e.g. to calculate moment), // it is better to use function LatticeApply::lineApply // with class LineCollapser. //

        // The user has to derive a concrete class from this base class // and implement the (pure) virtual functions. //
        The main function is process, which needs to do the // calculation. //
        Other functions make it possible to perform an initial check. //

        // The class is Doubly templated. Ths first template type // is for the data type you are processing. The second type is // for what type you want the results of the processing assigned to. // For example, if you are computing sums of squares for statistical // purposes, you might use higher precision (FLoat->Double) for this. // No check is made that the template types are self-consistent. // // // // // // // // //

      • // template class TiledCollapser { public: // Destructor virtual ~TiledCollapser(); // The init function for a derived class. // It can be used to check if nOutPixelsPerCollapse // corresponds with the number of pixels produced per collapsed chunk. //
        processAxis is the axis of the line being passed // to the process function. virtual void init (uInt nOutPixelsPerCollapse) = 0; // Can the process function in the derived class handle a null mask pointer? // If not, LatticeApply ensures that it'll always pass a mask block, // even if the lattice does not have a mask (in that case that mask block // contains all True values). //
        The default implementation returns False. //
        The function is there to make optimization possible when no masks // are involved. On the other side, it allows the casual user to ignore // optimization. virtual Bool canHandleNullMask() const; // Create and initialize the accumulator. // The accumulator can be a cube with shape [n1,n2,n3], // where n2 is equal to nOutPixelsPerCollapse. // However, one can also use several matrices as accumulator. //
        The data type of the accumulator can be any. E.g. when // accumulating Float lattices, the accumulator could be of // type Double to have enough precision. //
        In the endAccumulator function the accumulator // data has to be copied into an Array object with the correct // shape and data type. virtual void initAccumulator (uInt64 n1, uInt64 n3) = 0; // Collapse the given input data containing (nrval values // with an increment of inDataIncr elements). // inMask is a Bool block representing a mask with the // same nr of values and increment as the input data. If a mask // value is False, the corresponding input value is masked off. //
        When function canHandleNullMask returned True, // it is possible that inMask is a null pointer indicating // that the input has no mask, thus all values are valid. //
        // The result(s) have to be stored in the accumulator at the given indices. //
        startPos gives the lattice position of the first value. // The position of other values can be calculated from index and shape // using function toPositionInArray in class // IPosition. virtual void process (uInt accumIndex1, uInt accumIndex3, const T* inData, const Bool* inMask, uInt inDataIncr, uInt inMaskIncr, uInt nrval, const IPosition& startPos, const IPosition& shape) = 0; // End the accumulator. It should return the accumulator as an // Array of datatype U (e.g. double the precision of type T) // with the given shape. The accumulator should thereafter be deleted when needed. virtual void endAccumulator (Array& result, Array& resultMask, const IPosition& shape) = 0; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/LatticeMath/TiledCollapser.tcc000066400000000000000000000031421476623553700227340ustar00rootroot00000000000000//# TiledCollapser.cc: Abstract base class to collapse lines //# Copyright (C) 1996,1997 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_TILEDCOLLAPSER_TCC #define LATTICES_TILEDCOLLAPSER_TCC #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template TiledCollapser::~TiledCollapser() {} template Bool TiledCollapser::canHandleNullMask() const { return False; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/LatticeMath/test/000077500000000000000000000000001476623553700203125ustar00rootroot00000000000000casacore-3.7.1/lattices/LatticeMath/test/CMakeLists.txt000066400000000000000000000007751476623553700230630ustar00rootroot00000000000000set (tests tFit2D tLatticeAddNoise tLatticeApply tLatticeApply2 tLatticeConvolver tLatticeFFT tLatticeFit tLatticeFractile tLatticeHistograms tLatticeMathUtil tLatticeSlice1D tLatticeStatistics tLatticeStatsDataProvider tLatticeTwoPtCorr ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_lattices) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/lattices/LatticeMath/test/tFit2D.cc000066400000000000000000000320331476623553700217160ustar00rootroot00000000000000//# tFit2D.cc: Test nonlinear least squares classes for 2D Gaussian //# Copyright (C) 1995,1996,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Gaussian2D addModel (Array& pixels, Double height, Double x, Double y, Double major, Double minor, Double pa); void addNoise (Array& pixels, Array& sigma, Double noise); int main(int argc, const char *argv[]) { try { // // Inputs // Input inputs(1); inputs.version ("$Revision$"); inputs.create("nmodels", "1", "nmodels"); inputs.create("noise", "0.0001", "Noise"); inputs.create("major", "10.0", "major"); inputs.create("minor", "5.0", "minor"); inputs.create("pa", "45", "pa"); // +x -> +y inputs.create("nx", "64", "nx"); inputs.create("ny", "64", "ny"); inputs.create("norm", "False", "Normalize"); inputs.create("mask", "1,1,1,1,1,1", "Mask"); inputs.create("include", "0.0", "include"); inputs.create("exclude", "0.0", "exclude"); // inputs.readArguments(argc, argv); const Int nModels = inputs.getInt("nmodels"); const Double noise = inputs.getDouble("noise"); Double major = inputs.getDouble("major"); Double minor= inputs.getDouble("minor"); Double pa = inputs.getDouble("pa") * M_PI / 180.0; // +x -> +y const Int nx = inputs.getInt("nx"); const Int ny = inputs.getInt("ny"); ///const Bool norm = inputs.getBool("norm"); const Block mask = inputs.getIntArray("mask"); const Block includeRange = inputs.getDoubleArray("include"); const Block excludeRange = inputs.getDoubleArray("exclude"); // LogOrigin lor("tFit2D", "main()", WHERE); LogIO logger(lor); // Fit2D fitter(logger); // IPosition shape(2,nx,ny); Array pixels(shape, Float(0)); Array sigma(shape); Matrix saveEstimate(nModels, 6); // Double xsep = nx / nModels; Double ysep = ny / nModels; Double xPos, yPos; if (nModels==1) { xPos = nx / 2.0; yPos = ny / 2.0; } else { xPos = xsep / 2.0; yPos = ysep / 2.0; } Double height = 1.0; // Vector trueHeight(nModels); Vector trueX(nModels); Vector trueY(nModels); Vector trueMajor(nModels); Vector trueMinor(nModels); Vector truePA(nModels); // Vector saveMask; Vector startParameters; Vector parameterMask; for (Int i=0; i gauss2d = addModel(pixels, height, xPos, yPos, major, minor, pa); trueHeight(i) = height; trueX(i) = xPos; trueY(i) = yPos; trueMajor(i) = major; trueMinor(i) = minor; truePA(i) = pa; // Set Parameters mask Vector parameters(gauss2d.nparameters()); parameterMask = Vector(gauss2d.nparameters(), True); for (uInt j=0; j +y) = " << parameters(5) * 180.0 / C::pi << endl; */ // Set starting guess startParameters = parameters.copy(); for (uInt j=0; j +y) = " << startParameters(5) * 180.0 / C::pi << endl; */ // Add model to fitter fitter.addModel (Fit2D::GAUSSIAN, startParameters, parameterMask); // Update model height *= 0.75; xPos += xsep; yPos += ysep; // major *= 0.9; minor *= 0.9; pa += M_PI / 180 * 20.0; if (pa > M_PI) pa -= M_PI; cerr << endl; } // Add noise addNoise (pixels, sigma, noise); // Set other state of fitter if (includeRange.nelements()==2) { fitter.setIncludeRange(includeRange[0], includeRange[1]); } if (excludeRange.nelements()==2) { fitter.setExcludeRange(excludeRange[0], excludeRange[1]); } // Make fit Fit2D::ErrorTypes status = fitter.fit(pixels, sigma); if (status==Fit2D::OK) { cout << "Chi squared = " << fitter.chiSquared() << endl << endl; cout << "Number of iterations = " << fitter.numberIterations() << endl; cout << "Number of points = " << fitter.numberPoints() << endl; // // when i return errors, make a test to 3sigma or summfink // if (!allNear(fitter.availableSolution(), parameters, 1e-6)) { // throw (AipsError("Solution not accurate to 1e-6")); // } // cout << endl << "Number of models = " << fitter.nModels() << endl; for (uInt i=0; i xx(5); xx(0) = trueHeight(i); xx(1) = trueX(i); xx(2) = trueY(i); xx(3) = trueMajor(i); xx(4) = truePA(i); // Vector solution = fitter.availableSolution(i); Vector errors = fitter.availableErrors(i); cout << "Model " << i << " of type " << Fit2D::type(fitter.type(i)) << endl; cout << " Estimate = " << saveEstimate.row(i) << endl; cout << " Mask = " << saveMask << endl; cout << " Actual values = " << xx << endl; cout << " Solution = " << solution << endl; cout << " Errors = " << errors << endl; cout << " SNR = " << solution / errors << endl; } // Array resid; Array model; fitter.residual(resid, model, pixels); cout << "Residual min and max = " << min(resid) << " " << max(resid) << endl; } else { logger << fitter.errorMessage() << endl; } // Test copy constructor { cout << endl << endl << "Test copy constructor" << endl; Fit2D fitter2(fitter); fitter2.fit(pixels, sigma); if (!allEQ(fitter.availableSolution(),fitter2.availableSolution()) || fitter.numberIterations() != fitter2.numberIterations() || fitter.chiSquared() != fitter2.chiSquared() || fitter.numberPoints() != fitter2.numberPoints()) { cout << "Failed copy constructor test" << endl; } else { cout << "Copy constructor test ok" << endl; } } // Test assignment { cout << endl << endl << "Test assignment operator" << endl; Fit2D fitter2(logger); fitter2 = fitter; fitter2.fit(pixels, sigma); if (!allEQ(fitter.availableSolution(),fitter2.availableSolution()) || fitter.numberIterations() != fitter2.numberIterations() || fitter.chiSquared() != fitter2.chiSquared() || fitter.numberPoints() != fitter2.numberPoints()) { cout << "Failed assignment test" << endl; } else { cout << "Assignment test ok" << endl; } } // Test Estimate { cout << endl << endl << "Test estimator" << endl; IPosition shape(2,128,128); Array psf(shape); IPosition index(2); const double fwhm2sigma = sqrt(8.*log(2.)); for (index[0] = 0; index[0]<128; ++index[0]) { for (index[1] = 0; index[1]<128; ++index[1]) { const double xOffset = (double(index[0])-64.); const double yOffset = (double(index[1])-64.); const double expFactor = exp(-square(xOffset/2*fwhm2sigma)/2.- square(yOffset/2.*fwhm2sigma)/2.); psf(index) = expFactor; } } LogIO logger; Fit2D fitter2(logger); Vector param = fitter2.estimate(Fit2D::GAUSSIAN, psf); cout << "Estimate " << param << endl; if (abs(param[1]-64)>1e-5 || abs(param[2]-64)>1e-6) throw (AipsError("Estimate position not accurate to 1e-6")); fitter2.addModel(Fit2D::GAUSSIAN, param); Array sigma(psf.shape()); sigma.set(1.); if (fitter2.fit(psf,sigma) == Fit2D::OK) { param = fitter2.availableSolution(); cout << "Fitted: " << param << endl; if (abs(param[1]-64)>1e-5 || abs(param[2]-64)>1e-6) throw (AipsError("Fit position not accurate to 1e-6")); } else { cerr << "Failed to fit" << endl; } cout << endl << endl << "Estimate test ok" << endl; } Fit2D fitter3(logger); fitter3.addModel(Fit2D::LEVEL, Vector(1, 4.5)); Array pixels3 = pixels.copy(); pixels3.set(4.5); Double noise3 = 1; //cout << "noise " << noise3 << endl; addNoise (pixels3, sigma, noise3); fitter3.fit(pixels3, sigma); cout << "const solution " << fitter3.availableSolution() << endl; cout << "const error " << fitter3.availableErrors() << endl; cout << "Chi squared = " << fitter3.chiSquared() << endl << endl; cout << "Number of iterations = " << fitter3.numberIterations() << endl; cout << "Number of points = " << fitter3.numberPoints() << endl; Fit2D fitter4(logger); Array pixels4 = pixels3; pixels4.set(5); pixels4 += pixels.copy(); fitter4.addModel (Fit2D::GAUSSIAN, startParameters, parameterMask); fitter4.addModel(Fit2D::LEVEL, Vector(1, 4.5)); fitter4.fit(pixels4, sigma); cout << "const solution " << fitter4.availableSolution() << endl; cout << "const error " << fitter4.availableErrors() << endl; cout << "Chi squared = " << fitter4.chiSquared() << endl << endl; cout << "Number of iterations = " << fitter4.numberIterations() << endl; cout << "Number of points = " << fitter4.numberPoints() << endl; /* fitter.addModel(Fit2D::LEVEL, Vector(1, 4.5)); Array pixels4 = pixels + pixels3; fitter.fit(pixels4, sigma); cout << "const solution " << fitter.availableSolution() << endl; cout << "const error " << fitter.availableErrors() << endl; cout << "Chi squared = " << fitter.chiSquared() << endl << endl; cout << "Number of iterations = " << fitter.numberIterations() << endl; cout << "Number of points = " << fitter.numberPoints() << endl; */ } catch (std::exception& x) { cout << "Failed with message " << x.what() << endl; } } Gaussian2D addModel (Array& pixels, Double height, Double xcen, Double ycen, Double major, Double minor, Double pa) { Gaussian2D gauss2d; gauss2d.setHeight(height); gauss2d.setMajorAxis(major); gauss2d.setMinorAxis(minor); gauss2d.setXcenter(xcen); gauss2d.setYcenter(ycen); gauss2d.setPA(Fit2D::paToGauss2D(pa)); // +y -> -x // IPosition shape = pixels.shape(); IPosition loc(2); for (Int j=0; j& pixels, Array& sigma, Double noise) { sigma = 1.0; if (noise>0.0) sigma = noise; // MLCG generator; Normal noiseGen(&generator, 0.0, noise); // Bool deleteIt; Float* pData = pixels.getStorage(deleteIt); for (Int k=0; k #include #include #include #include #include #include #include #include #include #include #include #include #include #include void test0 (); void test0Complex (); void test1 (Random::Types type); void test1Complex (Random::Types type); void checkStats (Float av0, const Lattice& data, Random::Types type); void checkStatsComplex (Float av0, const Lattice& data, Random::Types type); int main () { try { // Float cerr << "Float" << endl; cerr << "Test 0" << endl; test0(); // Bug in Geometric distribution (defected) so left out for now cerr << "Test 1" << endl; const uInt n = Random::NUMBER_TYPES; for (uInt i=0; i(i); cerr << "Type = " << Random::asString(type) << endl; if (type!=Random::GEOMETRIC && type!=Random::UNKNOWN) test1 (type); } // Complex cerr << "Complex" << endl; cerr << "Test 0" << endl; test0Complex(); // cerr << "Test 1" << endl; for (uInt i=0; i(i); cerr << "Type = " << Random::asString(type) << endl; if (type!=Random::GEOMETRIC && type!=Random::UNKNOWN) test1Complex (type); } } catch (std::exception& x) { cerr << "Caught exception: " << x.what() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } void test0 () { IPosition shape(2, 1024, 1024); ArrayLattice lat(shape); lat.set(0.0); // Constructor Vector pars(2); pars(0) = 0.5; pars(1) = 1.0; LatticeAddNoise lan(Random::NORMAL, pars); lan.add(lat); checkStats(pars(0), lat, Random::NORMAL); // Default constructor and set lat.set(0.0); LatticeAddNoise lan2; lan2.set (Random::NORMAL, pars); lan2.add(lat); checkStats(pars(0), lat, Random::NORMAL); // Assigment LatticeAddNoise lan3; lan3 = lan2; lat.set(0.0); lan3.add(lat); checkStats(pars(0), lat, Random::NORMAL); } void test0Complex () { IPosition shape(2, 1024, 1024); ArrayLattice lat(shape); lat.set(Complex(0,0)); // Constructor Vector pars(2); pars(0) = 0.5; pars(1) = 1.0; LatticeAddNoise lan(Random::NORMAL, pars); lan.add(lat); checkStatsComplex (pars(0), lat, Random::NORMAL); // Default constructor and set LatticeAddNoise lan2; lan2.set(Random::NORMAL, pars); lat.set(Complex(0.0,0.0)); lan2.add(lat); checkStatsComplex (pars(0), lat, Random::NORMAL); // Assigment LatticeAddNoise lan3; lan3 = lan2; lat.set(Complex(0.0,0.0)); lan3.add(lat); checkStatsComplex (pars(0), lat, Random::NORMAL); } void test1 (Random::Types type) { Vector pars; pars = Random::defaultParameters(type); cerr << "pars = " << pars << endl; LatticeAddNoise lan(type, pars); // IPosition shape(2, 1024, 1024); ArrayLattice lat(shape); lat.set(0.0); lan.add(lat); // Float av = pars(0); if (type==Random::DISCRETEUNIFORM || type==Random::UNIFORM) { av = (pars(0) + pars(1)) / 2.0; } else if (type==Random::WEIBULL || type==Random::BINOMIAL) { av = -1.1e30; } checkStats(av, lat, type); } void test1Complex (Random::Types type) { Vector pars; pars = Random::defaultParameters(type); LatticeAddNoise lan(type, pars); // IPosition shape(2, 1024, 1024); ArrayLattice lat(shape); lat.set(Complex(0.0, 0.0)); lan.add(lat); // Float av = pars(0); if (type==Random::DISCRETEUNIFORM || type==Random::UNIFORM) { av = (pars(0) + pars(1)) / 2.0; } else if (type==Random::WEIBULL || type==Random::BINOMIAL) { av = -1.1e30; } checkStatsComplex(av, lat, type); } void checkStats (Float av0, const Lattice& data, Random::Types type) { Double n = data.shape().product(); Float av = mean(data.get()); Float var = variance(data.get()); Float sig = sqrt(var); LogIO os(LogOrigin("tLatticeAddNoise", "checkStats", WHERE)); // if (av0 > -1e30) { if (abs(av - av0) > 3*sig/sqrt(n)) { os << "Expected, observed and error in mean = " << av0 << ", " << av << ", " << sig/sqrt(n) << " for distribution " << Random::asString(type) << LogIO::EXCEPTION; } } } void checkStatsComplex (Float av0, const Lattice& data, Random::Types type) { ArrayLattice realLattice(real(data.get())); ArrayLattice imagLattice(imag(data.get())); // checkStats(av0, realLattice, type); checkStats(av0, imagLattice, type); } casacore-3.7.1/lattices/LatticeMath/test/tLatticeApply.cc000066400000000000000000000446431476623553700234130ustar00rootroot00000000000000//# tLatticeApply.cc: Test program for class LatticeApply //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class MyLineCollapser : public LineCollapser { public: MyLineCollapser() {} virtual void init (uInt nOutPixelsPerCollapse); virtual Bool canHandleNullMask() const; virtual void process (Int& result, Bool& resultMask, const Vector& vector, const Vector& arrayMask, const IPosition& pos); virtual void multiProcess (Vector& result, Vector& resultMask, const Vector& vector, const Vector& arrayMask, const IPosition& pos); }; void MyLineCollapser::init (uInt nOutPixelsPerCollapse) { AlwaysAssert (nOutPixelsPerCollapse == 1, AipsError); } Bool MyLineCollapser::canHandleNullMask() const { return False; } void MyLineCollapser::process (Int& result, Bool& resultMask, const Vector& vector, const Vector& mask, const IPosition&) { DebugAssert (vector.nelements() == mask.nelements(), AipsError); Int sum = 0; Bool fnd = False; uInt n = vector.nelements(); for (uInt i=0; i& result, Vector& resultMask, const Vector& vector, const Vector& mask, const IPosition&) { DebugAssert (vector.nelements() == mask.nelements(), AipsError); Int sum = 0; Bool fnd = False; uInt n = vector.nelements(); for (uInt i=0; i { public: MyTiledCollapser() : itsSum1(0),itsSum2(0),itsNpts(0) {} virtual ~MyTiledCollapser(); virtual void init (uInt nOutPixelsPerCollapse); virtual Bool canHandleNullMask() const; virtual void initAccumulator (uInt64 n1, uInt64 n3); virtual void process (uInt index1, uInt index3, const Int* inData, const Bool* inMask, uInt inDataIncr, uInt inMaskIncr, uInt nrval, const IPosition& pos, const IPosition& shape); virtual void endAccumulator (Array& result, Array& resultMask, const IPosition& shape); private: Matrix* itsSum1; Block* itsSum2; Matrix* itsNpts; uInt64 itsn1; uInt64 itsn3; }; MyTiledCollapser::~MyTiledCollapser() { delete itsSum1; delete itsSum2; delete itsNpts; } void MyTiledCollapser::init (uInt nOutPixelsPerCollapse) { AlwaysAssert (nOutPixelsPerCollapse == 2, AipsError); } void MyTiledCollapser::initAccumulator (uInt64 n1, uInt64 n3) { itsSum1 = new Matrix (n1, n3); itsSum2 = new Block (n1*n3); itsNpts = new Matrix (n1, n3); itsSum1->set (0); itsSum2->set (0); itsNpts->set (0); itsn1 = n1; itsn3 = n3; } Bool MyTiledCollapser::canHandleNullMask() const { return False; } void MyTiledCollapser::process (uInt index1, uInt index3, const Int* inData, const Bool* inMask, uInt inDataIncr, uInt inMaskIncr, uInt nrval, const IPosition&, const IPosition&) { uInt& sum1 = (*itsSum1)(index1, index3); Int& sum2 = (*itsSum2)[index1 + index3*itsn1]; uInt& npts = (*itsNpts)(index1, index3); for (uInt i=0; i& result, Array& resultMask, const IPosition& shape) { result.resize (shape); resultMask.resize (shape); Bool deleteRes, deleteSum1; Bool deleteMask, deleteNpts; Int* res = result.getStorage (deleteRes); Int* resptr = res; Bool* mask = resultMask.getStorage (deleteMask); Bool* maskptr = mask; const uInt* sum1 = itsSum1->getStorage (deleteSum1); const uInt* sum1ptr = sum1; const Int* sum2ptr = itsSum2->storage(); const uInt* npts = itsNpts->getStorage (deleteNpts); const uInt* nptsptr = npts; for (uInt i=0; ifreeStorage (sum1, deleteSum1); itsNpts->freeStorage (npts, deleteNpts); result.putStorage (res, deleteRes); resultMask.putStorage (mask, deleteMask); delete itsSum1; itsSum1 = 0; delete itsSum2; itsSum2 = 0; delete itsNpts; itsNpts = 0; } class MyLatticeProgress : public LatticeProgress { public: MyLatticeProgress() : itsMeter(0) {} virtual ~MyLatticeProgress(); virtual void initDerived(); virtual void nstepsDone (uInt nsteps); virtual void done(); private: ProgressMeter* itsMeter; }; MyLatticeProgress::~MyLatticeProgress() { delete itsMeter; } void MyLatticeProgress::initDerived() { delete itsMeter; itsMeter = new ProgressMeter(0.0, expectedNsteps(), "tLatticeApply", "Vectors extracted", "", "", True, max(1,Int(expectedNsteps()/100))); } void MyLatticeProgress::nstepsDone (uInt nsteps) { itsMeter->update (nsteps); } void MyLatticeProgress::done() { delete itsMeter; itsMeter = 0; } void doIt (int argc, const char* argv[]) { Input inp(1); inp.version(" "); inp.create("nx", "128", "Number of pixels along the x-axis", "int"); inp.create("ny", "128", "Number of pixels along the y-axis", "int"); inp.create("nz", "128", "Number of pixels along the z-axis", "int"); inp.create("tx", "0", "Number of pixels along the x-axis tile", "int"); inp.create("ty", "0", "Number of pixels along the y-axis tile", "int"); inp.create("tz", "0", "Number of pixels along the z-axis tile", "int"); inp.readArguments(argc, argv); const uInt nx=inp.getInt("nx"); const uInt ny=inp.getInt("ny"); const uInt nz=inp.getInt("nz"); const uInt tx=inp.getInt("tx"); const uInt ty=inp.getInt("ty"); const uInt tz=inp.getInt("tz"); IPosition latticeShape(3, nx, ny, nz); IPosition tileShape(3, tx, ty, tz); if (tileShape.product() == 0) { tileShape = TiledShape(latticeShape).tileShape(); } cout << "Data Type: Int"; cout << " Lattice shape:" << latticeShape; cout << " Tile shape:" << tileShape << endl; MyLatticeProgress showProgress; { SetupNewTable paSetup("tLatticeApply_tmp.array", TableDesc(), Table::New); Table paTable(paSetup); PagedArray lat(TiledShape(latticeShape, tileShape), paTable); Array arr(IPosition(3,nx,ny,1)); indgen(arr); LatticeIterator iter(lat, LatticeStepper(latticeShape, IPosition(3,nx,ny,1))); Timer tim; for (iter.reset(); !iter.atEnd(); iter++, arr += Int(nx*ny)) { iter.woCursor() = arr; } tim.show("fill "); } IPosition l1Shape (latticeShape); IPosition t1Shape (tileShape); l1Shape(2) = 1; t1Shape(2) = 1; { Table t("tLatticeApply_tmp.array"); PagedArray lat(t); SetupNewTable paSetup("tLatticeApply_tmp.array1", TableDesc(), Table::New); Table paTable(paSetup); PagedArray arrout(TiledShape(l1Shape,t1Shape), paTable); SubLattice latout(arrout, True); MyLineCollapser collapser; Timer tim; LatticeApply::lineApply (latout, SubLattice(lat), collapser, 2, &showProgress); tim.show("line 2 "); } { Table t("tLatticeApply_tmp.array1"); PagedArray lat(t); Int sum = (nz-1)*nz/2*nx*ny; IPosition pos(3,0); Timer tim; for (Int i=0; i lat(t); SetupNewTable paSetup0("tLatticeApply_tmp.array2a", TableDesc(), Table::New); Table paTable0(paSetup0); PagedArray arrout0(TiledShape(l2Shape, t2Shape), paTable0); SubLattice latout0(arrout0, True); SetupNewTable paSetup1("tLatticeApply_tmp.array2b", TableDesc(), Table::New); Table paTable1(paSetup1); PagedArray arrout1(TiledShape(l2Shape, t2Shape), paTable1); SubLattice latout1(arrout1, True); PtrBlock*> blat(2); blat[0] = &latout0; blat[1] = &latout1; MyLineCollapser collapser; Timer tim; LatticeApply::lineMultiApply (blat, SubLattice(lat), collapser, 0); tim.show("multiline 0"); } { Table t("tLatticeApply_tmp.array2b"); PagedArray lat(t); Int sum = (nx-1)*nx/2; IPosition pos(3,0); Timer tim; for (Int i=0; i lat(t); SetupNewTable paSetup("tLatticeApply_tmp.array2t", TableDesc(), Table::New); Table paTable(paSetup); PagedArray arrout(TiledShape(l2Shape,t2Shape), paTable); SubLattice latout(arrout, True); MyTiledCollapser collapser; Timer tim; LatticeApply::tiledApply (latout, SubLattice(lat), collapser, IPosition(1,0)); tim.show("tiled 0 "); } { Table t("tLatticeApply_tmp.array2t"); PagedArray lat(t); Int sum = (nx-1)*nx/2; IPosition pos(3,0); Timer tim; for (Int i=0; i lat(t); SetupNewTable paSetup("tLatticeApply_tmp.array2tb", TableDesc(), Table::New); Table paTable(paSetup); PagedArray arrout(l2Shape, paTable); SubLattice latout(arrout, True); MyTiledCollapser collapser; Timer tim; LatticeApply::tiledApply (latout, SubLattice(lat), collapser, IPosition(2,0,2)); tim.show("tiled 0,2 "); } { Table t("tLatticeApply_tmp.array2tb"); PagedArray lat(t); Int sum = nz*(nx-1)*nx/2 + nx*nx*ny*(nz-1)*nz/2; IPosition pos(3,0); Timer tim; for (Int j=0; j lat(t); SetupNewTable paSetup("tLatticeApply_tmp.array2tc", TableDesc(), Table::New); Table paTable(paSetup); PagedArray arrout(l2Shape, paTable); SubLattice latout(arrout, True); MyTiledCollapser collapser; Timer tim; LatticeApply::tiledApply (latout, SubLattice(lat), collapser, IPosition(3,0,1,2)); tim.show("tiled 0,1,2"); } { Table t("tLatticeApply_tmp.array2tc"); PagedArray lat(t); //# Use uInt to avoid possible overflow. uInt s = nx*ny*nz; s = s * (s-1) / 2; Int sum = s; IPosition pos(3,0); Timer tim; Int value = lat.getAt (pos); pos(0) = 1; Int value1 = lat.getAt (pos); if (value != sum || value1 != -sum) { cout << "Value=" << value << ',' << value1 << ", expected +-" << sum << " at position " << pos << endl; } tim.show("check "); } } IPosition l3Shape(2); Slicer slicer3 (IPosition(3,1,2,3), latticeShape-IPosition(3,8,7,6), IPosition(3,2,5,3), Slicer::endIsLast); l3Shape(0) = slicer3.length()(0); l3Shape(1) = slicer3.length()(2); { Table t("tLatticeApply_tmp.array"); PagedArray lat(t); SetupNewTable paSetup("tLatticeApply_tmp.array3", TableDesc(), Table::New); Table paTable(paSetup); PagedArray arrout(l3Shape, paTable); SubLattice latout(arrout, True); MyLineCollapser collapser; LatticeRegion region (slicer3, lat.shape()); Timer tim; LatticeApply::lineApply (latout, SubLattice(lat), region, collapser, 1); tim.show("lsliced 1 "); } { Table t("tLatticeApply_tmp.array"); PagedArray lat(t); SetupNewTable paSetup0("tLatticeApply_tmp.array4a", TableDesc(), Table::New); Table paTable0(paSetup0); PagedArray arrout0(l3Shape, paTable0); SubLattice latout0(arrout0, True); SetupNewTable paSetup1("tLatticeApply_tmp.array4b", TableDesc(), Table::New); Table paTable1(paSetup1); PagedArray arrout1(l3Shape, paTable1); SubLattice latout1(arrout1, True); PtrBlock*> blat(2); blat[0] = &latout0; blat[1] = &latout1; MyLineCollapser collapser; LatticeRegion region (slicer3, lat.shape()); Timer tim; LatticeApply::lineMultiApply (blat, SubLattice(lat), region, collapser, 1); tim.show("msliced 1 "); } IPosition l5Shape(5,1); l5Shape(0) = slicer3.length()(0); l5Shape(2) = 2; l5Shape(4) = slicer3.length()(2); { Table t("tLatticeApply_tmp.array"); PagedArray lat(t); SetupNewTable paSetup("tLatticeApply_tmp.array5t", TableDesc(), Table::New); Table paTable(paSetup); PagedArray arrout(l5Shape, paTable); SubLattice latout(arrout, True); MyTiledCollapser collapser; LatticeRegion region (slicer3, lat.shape()); Timer tim; LatticeApply::tiledApply (latout, SubLattice(lat), region, collapser, IPosition(1,1)); tim.show("tsliced 1 "); } { Table t("tLatticeApply_tmp.array3"); PagedArray lat(t); Table t1("tLatticeApply_tmp.array4a"); PagedArray lat1(t1); Table t2("tLatticeApply_tmp.array5t"); PagedArray lat2(t2); Int sx = slicer3.start()(0); Int sy = slicer3.start()(1); Int sz = slicer3.start()(2); Int mx = slicer3.length()(0); Int my = slicer3.length()(1); Int mz = slicer3.length()(2); Int ix = slicer3.stride()(0); Int iy = slicer3.stride()(1); Int iz = slicer3.stride()(2); Int iniSum = (my-1) * iy * my / 2 * nx + (sy*nx + sx) * my; IPosition pos(2,0); IPosition pos2(5,0); Timer tim; for (Int i=0; i #include #include #include #include // #include #include #include #include #include #include // #include #include #include #include #include #include #include #include #include #include #include #include class MyLineCollapser : public LineCollapser { public: MyLineCollapser() {} virtual void init (uInt nOutPixelsPerCollapse); virtual Bool canHandleNullMask() const; virtual void process (Float& result, Bool& resultMask, const Vector& vector, const Vector& arrayMask, const IPosition& pos); virtual void multiProcess (Vector& result, Vector& resultMask, const Vector& vector, const Vector& arrayMask, const IPosition& pos); }; void MyLineCollapser::init (uInt nOutPixelsPerCollapse) { AlwaysAssert (nOutPixelsPerCollapse == 1, AipsError); } Bool MyLineCollapser::canHandleNullMask() const { return False; } void MyLineCollapser::process (Float& result, Bool& resultMask, const Vector& vector, const Vector& mask, const IPosition&) { DebugAssert (vector.nelements() == mask.nelements(), AipsError); Float sum = 0; Bool fnd = False; uInt n = vector.nelements(); for (uInt i=0; i& result, Vector& resultMask, const Vector& vector, const Vector& mask, const IPosition&) { DebugAssert (vector.nelements() == mask.nelements(), AipsError); Float sum = 0; Bool fnd = False; uInt n = vector.nelements(); for (uInt i=0; i { public: MyTiledCollapser() : itsSum1(0),itsSum2(0),itsNpts(0) {} virtual ~MyTiledCollapser(); virtual void init (uInt nOutPixelsPerCollapse); virtual Bool canHandleNullMask() const; virtual void initAccumulator (uInt64 n1, uInt64 n3); virtual void process (uInt index1, uInt index3, const Float* inData, const Bool* inMask, uInt inDataIncr, uInt inMaskIncr, uInt nrval, const IPosition& pos, const IPosition& shape); virtual void endAccumulator (Array& result, Array& resultMask, const IPosition& shape); private: Matrix* itsSum1; Block* itsSum2; Matrix* itsNpts; uInt itsn1; uInt itsn3; }; MyTiledCollapser::~MyTiledCollapser() { delete itsSum1; delete itsSum2; delete itsNpts; } void MyTiledCollapser::init (uInt nOutPixelsPerCollapse) { AlwaysAssert (nOutPixelsPerCollapse == 2, AipsError); } void MyTiledCollapser::initAccumulator (uInt64 n1, uInt64 n3) { itsSum1 = new Matrix (n1, n3); itsSum2 = new Block (n1*n3); itsNpts = new Matrix (n1, n3); itsSum1->set (0.0); itsSum2->set (0.0); itsNpts->set (0); itsn1 = n1; itsn3 = n3; } Bool MyTiledCollapser::canHandleNullMask() const { return False; } void MyTiledCollapser::process (uInt index1, uInt index3, const Float* inData, const Bool* inMask, uInt inDataIncr, uInt inMaskIncr, uInt nrval, const IPosition&, const IPosition&) { Float& sum1 = (*itsSum1)(index1, index3); Float& sum2 = (*itsSum2)[index1 + index3*itsn1]; uInt& npts = (*itsNpts)(index1, index3); for (uInt i=0; i& result, Array& resultMask, const IPosition& shape) { result.resize (shape); resultMask.resize (shape); Bool deleteRes, deleteSum1; Bool deleteMask, deleteNpts; Float* res = result.getStorage (deleteRes); Float* resptr = res; Bool* mask = resultMask.getStorage (deleteMask); Bool* maskptr = mask; const Float* sum1 = itsSum1->getStorage (deleteSum1); const Float* sum1ptr = sum1; const Float* sum2ptr = itsSum2->storage(); const uInt* npts = itsNpts->getStorage (deleteNpts); const uInt* nptsptr = npts; for (uInt i=0; ifreeStorage (sum1, deleteSum1); itsNpts->freeStorage (npts, deleteNpts); result.putStorage (res, deleteRes); resultMask.putStorage (mask, deleteMask); delete itsSum1; itsSum1 = 0; delete itsSum2; itsSum2 = 0; delete itsNpts; itsNpts = 0; } class MyLatticeProgress : public LatticeProgress { public: MyLatticeProgress() : itsMeter(0) {} virtual ~MyLatticeProgress(); virtual void initDerived(); virtual void nstepsDone (uInt nsteps); virtual void done(); private: ProgressMeter* itsMeter; }; MyLatticeProgress::~MyLatticeProgress() { delete itsMeter; } void MyLatticeProgress::initDerived() { delete itsMeter; itsMeter = new ProgressMeter(0.0, expectedNsteps(), "tLatticeApply", "Vectors extracted", "", "", True, max(1,Int(expectedNsteps()/100))); } void MyLatticeProgress::nstepsDone (uInt nsteps) { itsMeter->update (nsteps); } void MyLatticeProgress::done() { delete itsMeter; itsMeter = 0; } void doIt (int argc, const char* argv[]) { Input inp(1); inp.version(" "); inp.create("nx", "32", "Number of pixels along the x-axis", "int"); inp.create("ny", "32", "Number of pixels along the y-axis", "int"); inp.create("nz", "64", "Number of pixels along the z-axis", "int"); inp.create("tx", "0", "Number of pixels along the x-axis tile", "int"); inp.create("ty", "0", "Number of pixels along the y-axis tile", "int"); inp.create("tz", "0", "Number of pixels along the z-axis tile", "int"); inp.readArguments(argc, argv); const uInt nx=inp.getInt("nx"); const uInt ny=inp.getInt("ny"); const uInt nz=inp.getInt("nz"); const uInt tx=inp.getInt("tx"); const uInt ty=inp.getInt("ty"); const uInt tz=inp.getInt("tz"); IPosition latticeShape(3, nx, ny, nz); IPosition tileShape(3, tx, ty, tz); if (tileShape.product() == 0) { tileShape = TiledShape(latticeShape).tileShape(); } cout << "Data Type: Float"; cout << " Lattice shape:" << latticeShape; cout << " Tile shape:" << tileShape << endl; MyLatticeProgress showProgress; { // // Make a ML with the corner x profiles all False // ArrayLattice lat(latticeShape); ArrayLattice mask(latticeShape); mask.set(True); // Array slice(IPosition(3,nx,1,1)); slice = False; mask.putSlice(slice, IPosition(3,0,0,0)); mask.putSlice(slice, IPosition(3,0,0,nz-1)); mask.putSlice(slice, IPosition(3,0,ny-1,nz-1)); mask.putSlice(slice, IPosition(3,0,ny-1,0)); SubLattice mLat(lat,True); mLat.setPixelMask(mask,False); // Array arr(IPosition(3,nx,ny,1)); indgen(arr); LatticeIterator iter(mLat, LatticeStepper(latticeShape, IPosition(3,nx,ny,1))); Timer tim; for (iter.reset(); !iter.atEnd(); iter++, arr += Float(nx*ny)) { iter.woCursor() = arr; } tim.show("fill "); // IPosition l2Shape (latticeShape); IPosition t2Shape (tileShape); l2Shape(0) = 1; t2Shape(0) = 1; // ArrayLattice lat0(l2Shape); SubLattice mLatOut0(lat0,True); ArrayLattice mask0(l2Shape); mask0.set(True); mLatOut0.setPixelMask(mask0,False); // ArrayLattice lat1(l2Shape); SubLattice mLatOut1(lat1,True); ArrayLattice mask1(l2Shape); mask0.set(True); mLatOut1.setPixelMask(mask1,False); // PtrBlock*> blat(2); blat[0] = &mLatOut0; blat[1] = &mLatOut1; MyLineCollapser collapser; tim.mark(); LatticeApply::lineMultiApply (blat, mLat, collapser, 0); tim.show("multiline 0"); // Float sum = (nx-1)*nx/2; IPosition pos(3,0); tim.mark(); for (uInt i=0; i #include #include #include #include #include #include //#include #include #include #include #include #include #include #include #include #include #include void print(const Lattice & psf, const Lattice & model, const Lattice & result) { cout << "Psf: " << psf.get() << endl; cout << "Model: " << model.get() << endl; cout << "Result: " << result.get() << endl; } int main() { try { { TempLattice psf(IPosition(4,16,5,1,9)); psf.set(0.0f); psf.putAt(1.0f, psf.shape()/2); const LatticeConvolver c(psf); AlwaysAssert(c.shape() == psf.shape(), AipsError); AlwaysAssert(c.fftShape() == psf.shape(), AipsError); AlwaysAssert(c.psfShape() == psf.shape(), AipsError); AlwaysAssert(c.type() == ConvEnums::CIRCULAR , AipsError); TempLattice extractedPsf(psf.shape()); // test the getPsf function (tests the FFT's but not padding) c.getPsf(extractedPsf); AlwaysAssert(allNear(extractedPsf.get(), psf.get(), NumericTraits::epsilon), AipsError); } { // test 1-D convolution with a large variety of model/psf shapes const IPosition evenPsfShape(1,10); TempLattice evenPsf1D(evenPsfShape); evenPsf1D.set(0.0f); evenPsf1D.putAt(0.2f, evenPsfShape*0); evenPsf1D.putAt(0.5f, evenPsfShape/2-1); evenPsf1D.putAt(1.0f, evenPsfShape/2); evenPsf1D.putAt(0.3f, evenPsfShape/2+1); evenPsf1D.putAt(0.1f, evenPsfShape-1); { // psfShape = 10, imageShape = 4, linear IPosition imageShape(1,4); LatticeConvolver c(evenPsf1D, imageShape); AlwaysAssert(c.shape() == imageShape, AipsError); AlwaysAssert(c.fftShape() == IPosition(1,7) , AipsError); AlwaysAssert(c.psfShape() == evenPsfShape , AipsError); AlwaysAssert(c.type() == ConvEnums::LINEAR , AipsError); { TempLattice model(imageShape); model.set(0.0); model.putAt(2.0, IPosition(1,0)); model.putAt(5.0, model.shape()-1); TempLattice result(model.shape()); c.linear(result, model); AlwaysAssert(near(result(IPosition(1,0)), 2.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,1)), 0.6f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,2)), 2.5f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,3)), 5.0f, 10*NumericTraits::epsilon), AipsError); TempLattice psf(evenPsfShape); c.getPsf(psf); Array psfArr = psf.get(); AlwaysAssert(allNear(psf.get(), evenPsf1D.get(), NumericTraits::epsilon), AipsError); // psfShape = 10, imageShape = 4, circular model.set(0.0); model.putAt(2.0, IPosition(1,0)); model.putAt(5.0, model.shape()-1); c.circular(result, model); AlwaysAssert(c.shape() == imageShape, AipsError); AlwaysAssert(c.fftShape() == IPosition(1,10) , AipsError); AlwaysAssert(c.psfShape() == evenPsfShape , AipsError); AlwaysAssert(c.type() == ConvEnums::CIRCULAR , AipsError); AlwaysAssert(near(result(IPosition(1,0)), 2.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,1)), 0.6f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,2)), 2.5f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,3)), 5.0f, 10*NumericTraits::epsilon), AipsError); } // psfShape = 10, imageShape = 5, linear imageShape = IPosition(1,5); c.resize(imageShape, ConvEnums::LINEAR); AlwaysAssert(c.shape() == imageShape, AipsError); AlwaysAssert(c.fftShape() == IPosition(1,10) , AipsError); AlwaysAssert(c.psfShape() == evenPsfShape , AipsError); AlwaysAssert(c.type() == ConvEnums::LINEAR , AipsError); { TempLattice model(imageShape); model.set(0.0); model.putAt(2.0, IPosition(1,0)); model.putAt(5.0, model.shape()-1); TempLattice result(model.shape()); c.linear(result, model); AlwaysAssert(near(result(IPosition(1,0)), 2.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,1)), 0.6f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,2)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,3)), 2.5f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,4)), 5.2f, 10*NumericTraits::epsilon), AipsError); // psfShape = 10, imageShape = 5, circular model.set(0.0); model.putAt(2.0, IPosition(1,0)); model.putAt(5.0, model.shape()-1); c.circular(result, model); AlwaysAssert(c.shape() == imageShape, AipsError); AlwaysAssert(c.fftShape() == IPosition(1,10) , AipsError); AlwaysAssert(c.psfShape() == evenPsfShape , AipsError); AlwaysAssert(c.type() == ConvEnums::CIRCULAR , AipsError); AlwaysAssert(near(result(IPosition(1,0)), 2.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,1)), 0.6f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,2)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,3)), 2.5f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,4)), 5.2f, 10*NumericTraits::epsilon), AipsError); } // psfShape = 10, imageShape = 6, linear imageShape = IPosition(1,6); c.resize(imageShape, ConvEnums::LINEAR); AlwaysAssert(c.shape() == imageShape, AipsError); AlwaysAssert(c.fftShape() == IPosition(1,11) , AipsError); AlwaysAssert(c.psfShape() == evenPsfShape , AipsError); AlwaysAssert(c.type() == ConvEnums::LINEAR , AipsError); { TempLattice model(imageShape); model.set(0.0); model.putAt(2.0, IPosition(1,0)); model.putAt(5.0, model.shape()-1); TempLattice result(model.shape()); c.linear(result, model); AlwaysAssert(near(result(IPosition(1,0)), 3.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,1)), 0.6f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,2)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,3)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,4)), 2.7f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,5)), 5.0f, 10*NumericTraits::epsilon), AipsError); } // psfShape = 10, imageShape = 10, linear imageShape = IPosition(1,10); c.resize(imageShape, ConvEnums::LINEAR); AlwaysAssert(c.shape() == imageShape, AipsError); AlwaysAssert(c.fftShape() == IPosition(1,15) , AipsError); AlwaysAssert(c.psfShape() == evenPsfShape , AipsError); AlwaysAssert(c.type() == ConvEnums::LINEAR , AipsError); { TempLattice result(imageShape); result.set(0.0); result.putAt(2.0, IPosition(1,0)); result.putAt(5.0, result.shape()-1); c.linear(result); AlwaysAssert(near(result(IPosition(1,0)), 2.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,1)), 0.6f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,2)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,3)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,4)), 1.2f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,5)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,6)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,7)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,8)), 2.5f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,9)), 5.0f, 10*NumericTraits::epsilon), AipsError); } // psfShape = 10, imageShape = 11, linear imageShape = IPosition(1,11); c.resize(imageShape, ConvEnums::LINEAR); AlwaysAssert(c.shape() == imageShape, AipsError); AlwaysAssert(c.fftShape() == IPosition(1,16) , AipsError); AlwaysAssert(c.psfShape() == evenPsfShape, AipsError); AlwaysAssert(c.type() == ConvEnums::LINEAR, AipsError); { TempLattice result(imageShape); result.set(0.0); result.putAt(2.0, IPosition(1,0)); result.putAt(5.0, result.shape()-1); c.linear(result); AlwaysAssert(near(result(IPosition(1,0)), 2.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,1)), 0.6f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,2)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,3)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,4)), 0.2f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,5)), 1.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,6)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,7)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,8)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,9)), 2.5f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,10)), 5.0f, 10*NumericTraits::epsilon), AipsError); } // psfShape = 10, imageShape = 9, linear imageShape = IPosition(1,9); c.resize(imageShape, ConvEnums::LINEAR); AlwaysAssert(c.shape() == imageShape, AipsError); AlwaysAssert(c.fftShape() == IPosition(1,14) , AipsError); AlwaysAssert(c.psfShape() == evenPsfShape , AipsError); AlwaysAssert(c.type() == ConvEnums::LINEAR , AipsError); { TempLattice result(imageShape); result.set(0.0); result.putAt(2.0, IPosition(1,0)); result.putAt(5.0, result.shape()-1); c.linear(result); AlwaysAssert(near(result(IPosition(1,0)), 2.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,1)), 0.6f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,2)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,3)), 1.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,4)), 0.2f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,5)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,6)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,7)), 2.5f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,8)), 5.0f, 10*NumericTraits::epsilon), AipsError); } } const IPosition oddPsfShape(1,11); TempLattice oddPsf1D(oddPsfShape); oddPsf1D.set(0.0f); oddPsf1D.putAt(0.2f, oddPsfShape*0); oddPsf1D.putAt(0.5f, oddPsfShape/2-1); oddPsf1D.putAt(1.0f, oddPsfShape/2); oddPsf1D.putAt(0.3f, oddPsfShape/2+1); oddPsf1D.putAt(0.1f, oddPsfShape-1); { // psfShape = 11, imageShape = 4, linear IPosition imageShape(1,4); LatticeConvolver c(oddPsf1D, imageShape); AlwaysAssert(c.shape() == imageShape, AipsError); AlwaysAssert(c.fftShape() == IPosition(1,7) , AipsError); AlwaysAssert(c.psfShape() == oddPsfShape , AipsError); AlwaysAssert(c.type() == ConvEnums::LINEAR , AipsError); { TempLattice result(imageShape); result.set(0.0); result.putAt(2.0, IPosition(1,0)); result.putAt(5.0, result.shape()-1); c.linear(result); AlwaysAssert(near(result(IPosition(1,0)), 2.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,1)), 0.6f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,2)), 2.5f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,3)), 5.0f, 10*NumericTraits::epsilon), AipsError); } // psfShape = 11, imageShape = 5, linear imageShape = IPosition(1,5); c.resize(imageShape, ConvEnums::LINEAR); AlwaysAssert(c.shape() == imageShape, AipsError); AlwaysAssert(c.fftShape() == IPosition(1,9) , AipsError); AlwaysAssert(c.psfShape() == oddPsfShape , AipsError); AlwaysAssert(c.type() == ConvEnums::LINEAR , AipsError); { TempLattice result(imageShape); result.set(0.0); result.putAt(2.0, IPosition(1,0)); result.putAt(5.0, result.shape()-1); c.linear(result); AlwaysAssert(near(result(IPosition(1,0)), 2.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,1)), 0.6f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,2)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,3)), 2.5f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,4)), 5.0f, 10*NumericTraits::epsilon), AipsError); } // psfShape = 11, imageShape = 6, linear imageShape = IPosition(1,6); c.resize(imageShape, ConvEnums::LINEAR); AlwaysAssert(c.shape() == imageShape, AipsError); AlwaysAssert(c.fftShape() == IPosition(1,11) , AipsError); AlwaysAssert(c.psfShape() == oddPsfShape , AipsError); AlwaysAssert(c.type() == ConvEnums::LINEAR , AipsError); { TempLattice result(imageShape); result.set(0.0); result.putAt(2.0, IPosition(1,0)); result.putAt(5.0, result.shape()-1); c.linear(result); AlwaysAssert(near(result(IPosition(1,0)), 3.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,1)), 0.6f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,2)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,3)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,4)), 2.5f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,5)), 5.2f, 10*NumericTraits::epsilon), AipsError); } // psfShape = 11, imageShape = 11, linear imageShape = IPosition(1,11); c.resize(imageShape, ConvEnums::LINEAR); AlwaysAssert(c.shape() == imageShape, AipsError); AlwaysAssert(c.fftShape() == IPosition(1,16) , AipsError); AlwaysAssert(c.psfShape() == oddPsfShape , AipsError); AlwaysAssert(c.type() == ConvEnums::LINEAR , AipsError); { TempLattice result(imageShape); result.set(0.0); result.putAt(2.0, IPosition(1,0)); result.putAt(5.0, result.shape()-1); c.linear(result); AlwaysAssert(near(result(IPosition(1,0)), 2.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,1)), 0.6f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,2)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,3)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,4)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,5)), 1.2f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,6)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,7)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,8)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,9)), 2.5f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,10)), 5.0f, 10*NumericTraits::epsilon), AipsError); } // psfShape = 11, imageShape = 12, linear imageShape = IPosition(1,12); c.resize(imageShape, ConvEnums::LINEAR); AlwaysAssert(c.shape() == imageShape, AipsError); AlwaysAssert(c.fftShape() == IPosition(1,17) , AipsError); AlwaysAssert(c.psfShape() == oddPsfShape, AipsError); AlwaysAssert(c.type() == ConvEnums::LINEAR, AipsError); { TempLattice result(imageShape); result.set(0.0); result.putAt(2.0, IPosition(1,0)); result.putAt(5.0, result.shape()-1); c.linear(result); AlwaysAssert(near(result(IPosition(1,0)), 2.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,1)), 0.6f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,2)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,3)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,4)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,5)), 0.2f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,6)), 1.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,7)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,8)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,9)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,10)), 2.5f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,11)), 5.0f, 10*NumericTraits::epsilon), AipsError); } // psfShape = 11, imageShape = 10, linear imageShape = IPosition(1,10); c.resize(imageShape, ConvEnums::LINEAR); AlwaysAssert(c.shape() == imageShape, AipsError); AlwaysAssert(c.fftShape() == IPosition(1,15) , AipsError); AlwaysAssert(c.psfShape() == oddPsfShape , AipsError); AlwaysAssert(c.type() == ConvEnums::LINEAR , AipsError); { TempLattice result(imageShape); result.set(0.0); result.putAt(2.0, IPosition(1,0)); result.putAt(5.0, result.shape()-1); c.linear(result); // print(oddPsf1D, result, result); AlwaysAssert(near(result(IPosition(1,0)), 2.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,1)), 0.6f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,2)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,3)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,4)), 1.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,5)), 0.2f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,6)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(nearAbs(result(IPosition(1,7)), 0.0f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,8)), 2.5f, 10*NumericTraits::epsilon), AipsError); AlwaysAssert(near(result(IPosition(1,9)), 5.0f, 10*NumericTraits::epsilon), AipsError); } } // { // TempLattice psf1D(IPosition(1,4)); // psf1D.set(0.0f); // psf1D.putAt(0.1f, psf1D.shape()/2-2); // psf1D.putAt(0.5f, psf1D.shape()/2-1); // psf1D.putAt(1.0f, psf1D.shape()/2); // psf1D.putAt(0.3f, psf1D.shape()/2+1); // TempLattice model(IPosition(1,7)); // model.set(0.0); // model.putAt(2.0, IPosition(1,0)); // model.putAt(5.0, model.shape()-1); // const IPosition imageShape = model.shape(); // LatticeConvolver c(psf1D, imageShape); // { // TempLattice result(model.shape()); // c.linear(result, model); // AlwaysAssert(result.shape() == model.shape(), AipsError); // AlwaysAssert(near(result(IPosition(1,0)), 2.0f, 10*NumericTraits::epsilon), AipsError); // AlwaysAssert(near(result(IPosition(1,1)), 0.6f, 10*NumericTraits::epsilon), AipsError); // AlwaysAssert(nearAbs(result(IPosition(1,2)), 0.0f, 10*NumericTraits::epsilon), AipsError); // AlwaysAssert(nearAbs(result(IPosition(1,3)), 0.0f, 10*NumericTraits::epsilon), AipsError); // AlwaysAssert(nearAbs(result(IPosition(1,4)), 0.0f, 10*NumericTraits::epsilon), AipsError); // AlwaysAssert(near(result(IPosition(1,5)), 2.5f, 10*NumericTraits::epsilon), AipsError); // AlwaysAssert(near(result(IPosition(1,6)), 5.0f, 10*NumericTraits::epsilon), AipsError); // } // { // const IPosition resultShape(model.shape()*2); // TempLattice result(resultShape); // c.linear(result, model); // AlwaysAssert(result.shape() == resultShape, AipsError); // AlwaysAssert(near(result(IPosition(1,4)), 2.0f, 10*NumericTraits::epsilon), AipsError); // AlwaysAssert(near(result(IPosition(1,5)), 0.6f, 10*NumericTraits::epsilon), AipsError); // AlwaysAssert(nearAbs(result(IPosition(1,6)), 0.0f, 10*NumericTraits::epsilon), AipsError); // AlwaysAssert(nearAbs(result(IPosition(1,7)), 0.0f, 10*NumericTraits::epsilon), AipsError); // AlwaysAssert(nearAbs(result(IPosition(1,8)), 0.0f, 10*NumericTraits::epsilon), AipsError); // AlwaysAssert(near(result(IPosition(1,9)), 2.5f, 10*NumericTraits::epsilon), AipsError); // AlwaysAssert(near(result(IPosition(1,10)), 5.0f, 10*NumericTraits::epsilon), AipsError); // } // { // const IPosition resultShape(model.shape()*2-1); // TempLattice result(resultShape); // c.linear(result, model); // AlwaysAssert(result.shape() == resultShape, AipsError); // AlwaysAssert(near(result(IPosition(1,4)), 2.0f, 10*NumericTraits::epsilon), AipsError); // AlwaysAssert(near(result(IPosition(1,5)), 0.6f, 10*NumericTraits::epsilon), AipsError); // AlwaysAssert(nearAbs(result(IPosition(1,6)), 0.0f, 10*NumericTraits::epsilon), AipsError); // AlwaysAssert(nearAbs(result(IPosition(1,7)), 0.0f, 10*NumericTraits::epsilon), AipsError); // AlwaysAssert(nearAbs(result(IPosition(1,8)), 0.0f, 10*NumericTraits::epsilon), AipsError); // AlwaysAssert(near(result(IPosition(1,9)), 2.5f, 10*NumericTraits::epsilon), AipsError); // AlwaysAssert(near(result(IPosition(1,10)), 5.0f, 10*NumericTraits::epsilon), AipsError); // } // } // { // TempLattice psf2D(IPosition(2,3,3)); // psf2D.set(0.0f); // IPosition centre = psf2D.shape()/2; // psf2D.putAt(1.0f, centre); // centre(0) -= 1; // psf2D.putAt(0.5f, centre); // centre(0) += 2; // psf2D.putAt(0.4f, centre); // centre = psf2D.shape()/2; centre(1) -= 1; // psf2D.putAt(0.2f, centre); // centre(1) += 2; // psf2D.putAt(0.1f, centre); // Array psfArray; // psf2D.getSlice(psfArray, IPosition(2,0), psf2D.shape()); // cout << "psf = " << psfArray << endl; // TempLattice model(IPosition(2,7,6)); // model.set(0.0); // model.putAt(2.0, IPosition(2,0)); // model.putAt(5.0, model.shape()-1); // Array modelArray; // model.getSlice(modelArray, IPosition(2,0), model.shape()); // cout << "model = " << modelArray << endl; // const IPosition imageShape = model.shape(); // LatticeConvolver c(psf2D, imageShape); // TempLattice result(model.shape()); // c.linear(result, model); // Array resultArray; // result.getSlice(resultArray, IPosition(2,0), result.shape()); // cout << "result = " << resultArray << endl; // } } } catch (std::exception& x) { cout<< "FAIL"<< endl; cerr << x.what() << endl; return 1; } cout<< "OK"<< endl; return 0; } // Local Variables: // compile-command: "gmake OPTLIB=1 tLatticeConvolver" // End: casacore-3.7.1/lattices/LatticeMath/test/tLatticeConvolver.run000066400000000000000000000000421476623553700245030ustar00rootroot00000000000000#! /bin/sh echo "UNTESTED" exit 3 casacore-3.7.1/lattices/LatticeMath/test/tLatticeFFT.cc000066400000000000000000000135431476623553700227400ustar00rootroot00000000000000//# ClassFileName.cc: this defines ClassName, which ... //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include int main() { try { { const uInt nz = 3; const uInt ny = 8; const uInt nx = (ny+2)/2; const IPosition cShape(3,nx,ny,nz); const IPosition rShape(3,ny,ny,nz); PagedArray cArr(cShape); PagedArray rArr(rShape); IPosition centre=cShape/2; { // test the fft2d function cArr.set(Complex(1,0)); LatticeFFT::cfft2d(cArr); uInt i; for (i = 0; i < nz; i++) { centre(2) = i; AlwaysAssert(near(cArr.getAt(centre), Complex(nx*ny,0), 1E-5), AipsError); cArr.putAt(Complex(0,0), centre); } RO_LatticeIterator iter(cArr); for (iter.reset(); !iter.atEnd(); iter++) { AlwaysAssert(allNearAbs(iter.cursor(), Complex(0,0), 1E-5), AipsError); } for (i = 0; i < nz; i++) { centre(2) = i; cArr.putAt(Complex(nx*ny,0), centre); } LatticeFFT::cfft2d(cArr, False); for (iter.reset(); !iter.atEnd(); iter++) { AlwaysAssert(allNearAbs(iter.cursor(), Complex(1,0), 1E-5), AipsError); } } { // test the complex->complex fft function cArr.set(Complex(1,0)); LatticeFFT::cfft(cArr); centre(2) = nz/2; AlwaysAssert(near(cArr.getAt(centre), Complex(nx*ny*nz,0), 1E-5), AipsError); cArr.putAt(Complex(0,0), centre); const IPosition tileShape(cArr.niceCursorShape()); { RO_LatticeIterator iter(cArr, tileShape); for (iter.reset(); !iter.atEnd(); iter++) { AlwaysAssert(allNearAbs(iter.cursor(), Complex(0,0), 1E-5), AipsError); } } Vector whichAxes(3, True); whichAxes(2) = False; cArr.putAt(Complex(nx*ny,0), centre); LatticeFFT::cfft(cArr, whichAxes, False); IPosition planeShape = tileShape; planeShape(2) = 1; { RO_LatticeIterator planeIter(cArr,planeShape); Complex cValue; for (planeIter.reset(); !planeIter.atEnd(); planeIter++) { if (planeIter.position()(2) == centre(2)) cValue = Complex(1,0); else cValue = Complex(0,0); AlwaysAssert(allNearAbs(planeIter.cursor(), cValue, 1E-5), AipsError); } } } { // test the real->complex fft function rArr.set(1.0); LatticeFFT::rcfft(cArr, rArr); centre = cShape/2; centre(0) = 0; AlwaysAssert(near(cArr.getAt(centre), Complex(ny*ny*nz,0), 1E-5), AipsError); cArr.putAt(Complex(0,0), centre); { RO_LatticeIterator iter(cArr); for (iter.reset(); !iter.atEnd(); iter++) { AlwaysAssert(allNearAbs(iter.cursor(), Complex(0,0), 1E-5), AipsError); } } Vector whichAxes(3, True); whichAxes(2) = False; LatticeFFT::rcfft(cArr, rArr, whichAxes, False); centre = 0; for (uInt i = 0; i < nz; i++) { centre(2) = i; AlwaysAssert(near(cArr.getAt(centre), Complex(ny*ny,0), 1E-5), AipsError); cArr.putAt(Complex(0,0), centre); } { RO_LatticeIterator iter(cArr); for (iter.reset(); !iter.atEnd(); iter++) { AlwaysAssert(allNearAbs(iter.cursor(), Complex(0,0), 1E-5), AipsError); } } } { // test the complex->real fft function cArr.set(Complex(1,0)); LatticeFFT::crfft(rArr, cArr); centre = rShape/2; AlwaysAssert(near(rArr.getAt(centre), 1.0f, 1E-5), AipsError); rArr.putAt(0.0f, centre); { RO_LatticeIterator iter(rArr); for (iter.reset(); !iter.atEnd(); iter++) { AlwaysAssert(allNearAbs(iter.cursor(), 0.0f, 1E-5), AipsError); } } cArr.set(Complex(1,0)); Vector whichAxes(3, True); whichAxes(2) = False; LatticeFFT::crfft(rArr, cArr, whichAxes, False); centre = 0; for (uInt i = 0; i < nz; i++) { centre(2) = i; AlwaysAssert(near(rArr.getAt(centre), 1.0f, 1E-5), AipsError); rArr.putAt(0.0f, centre); } { RO_LatticeIterator iter(rArr); for (iter.reset(); !iter.atEnd(); iter++) { AlwaysAssert(allNearAbs(iter.cursor(), 0.0f, 1E-5), AipsError); } } } } cout<< "OK"<< endl; return 0; } catch (std::exception& x) { cerr << x.what() << endl; cout<< "FAIL"<< endl; } } // { // RO_LatticeIterator iter(rArr, rArr.shape()); // cout << iter.cursor() << endl; // } // Local Variables: // compile-command: "gmake OPTLIB=1 tLatticeFFT" // End: casacore-3.7.1/lattices/LatticeMath/test/tLatticeFit.cc000066400000000000000000000120121476623553700230310ustar00rootroot00000000000000//# tLatticeFit.cc: test the baselineFit function //# Copyright (C) 1995,1996,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include int main() { uInt nx = 10, ny = 20, nz = 30; Cube cube(10, 20, 30); Vector fittedParameters; // x^2 Polynomial > square(2); LinearFitSVD fitter; fitter.setFunction(square); // x axis { Vector x(nx); indgen((Array&)x); // 0, 1, 2, ... Vector mask(nx); mask = True; for (uInt k=0; k < nz; k++) { for (uInt j=0; j < ny; j++) { cube.xyPlane(k).column(j) = Float(j*k)*((Array&)x)*((Array&)x); } } ArrayLattice inLattice(cube); Cube outCube(nx,ny,nz); ArrayLattice outLattice(outCube); LatticeFit::fitProfiles (outLattice, fittedParameters, fitter, inLattice, 0, mask, True); AlwaysAssertExit(allNearAbs((Array&)outCube, 0.0f, 7.e-3)); AlwaysAssertExit(near(fittedParameters(2), Float((ny-1)*(nz-1)), 1.0e-3)); LatticeFit::fitProfiles (outLattice, fittedParameters, fitter, inLattice, 0, mask, False); AlwaysAssertExit(allNearAbs((Array&)outCube, (Array&)cube, 7.e-3)); AlwaysAssertExit(near(fittedParameters(2), Float((ny-1)*(nz-1)), 1.0e-3)); //crashes /* { SubLattice* pOutResid = new SubLattice(outLattice); SubLattice inSubLattice(inLattice); LatticeFit::fitProfiles (pOutFit, pOutResid, inSubLattice, pSigma, fitter, 0, False); delete pOutResid; } */ } // y axis { Vector x(ny); indgen((Array&)x); // 0, 1, 2, ... Vector mask(ny); mask = True; for (uInt k=0; k < nz; k++) { for (uInt i=0; i < nx; i++) { cube.xyPlane(k).row(i) = Float(i*k)*((Array&)x)*((Array&)x); } } ArrayLattice inLattice(cube); Cube outCube(nx,ny,nz); ArrayLattice outLattice(outCube); LatticeFit::fitProfiles (outLattice, fittedParameters, fitter, inLattice, 1, mask, True); AlwaysAssertExit(allNearAbs((Array&)outCube, 0.0f, 3.e-2)); AlwaysAssertExit(near(fittedParameters(2), Float((nx-1)*(nz-1)), 1.0e-3)); LatticeFit::fitProfiles (outLattice, fittedParameters, fitter, inLattice, 1, mask, False); AlwaysAssertExit(allNearAbs((Array&)outCube, (Array&)cube, 3.e-2)); AlwaysAssertExit(near(fittedParameters(2), Float((nx-1)*(nz-1)), 1.0e-3)); } // z axis { Vector x(nz); indgen((Array&)x); // 0, 1, 2, ... Vector mask(nz); mask = True; for (uInt k=0; k < nz; k++) { for (uInt j=0; j < ny; j++) { for (uInt i=0; i < nx; i++) { cube(i,j,k) = Float(i*j)*x(k)*x(k); } } } ArrayLattice inLattice(cube); Cube outCube(nx,ny,nz); ArrayLattice outLattice(outCube); LatticeFit::fitProfiles (outLattice, fittedParameters, fitter, inLattice, 2, mask, True); AlwaysAssertExit(allNearAbs((Array&)outCube, 0.0f, 2.0e-2)); AlwaysAssertExit(near(fittedParameters(2), Float((nx-1)*(ny-1)), 1.0e-3)); LatticeFit::fitProfiles (outLattice, fittedParameters, fitter, inLattice, 2, mask, False); AlwaysAssertExit(allNearAbs((Array&)outCube, (Array&)cube, 2.0e-2)); AlwaysAssertExit(near(fittedParameters(2), Float((nx-1)*(ny-1)), 1.0e-3)); } cout << "OK" << endl; return 0; } casacore-3.7.1/lattices/LatticeMath/test/tLatticeFractile.cc000066400000000000000000000206351476623553700240520ustar00rootroot00000000000000//# tLatticeFractile.cc: Tests the functions in LatticeFractile //# Copyright (C) 1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include int main (int argc, const char* argv[]) { try { cout << ">>>" << endl; Input inp(1); inp.version(" "); inp.create("nx", "16", "Number of pixels along the x-axis", "int"); inp.create("ny", "16", "Number of pixels along the y-axis", "int"); inp.readArguments(argc, argv); cout << "<<<" << endl; const uInt nx=inp.getInt("nx"); const uInt ny=inp.getInt("ny"); // // Test various sized arrays. // { // Test lattice with all equal values. IPosition shape(2, nx, ny); Array arr(shape); arr = 1.; ArrayLattice aF(arr); cout << "Fractiles (left 50%, right 50%) test" << endl; cout << LatticeFractile::unmaskedFractiles (aF, 0.5, 0.5) << endl; cout << LatticeFractile::unmaskedFractiles (aF, 0.5, 0.5, 16) << endl; cout << LatticeFractile::unmaskedFractiles (aF, 0.5, 0.5, 4) << endl; cout << LatticeFractile::unmaskedFractiles (aF, 0.5, 0.5, 2) << endl; LatticeExprNode afExpr(aF); LatticeExpr expr(afExpr[aF>4]); cout << LatticeFractile::maskedFractile(expr, 0.5) << endl; cout << "Fractiles (left 40%, right 45%) test" << endl; cout << LatticeFractile::unmaskedFractiles (aF, 0.40, 0.55, 2) << endl; cout << LatticeFractile::maskedFractiles(expr, 0.4, 0.45) << endl; cout << LatticeFractile::maskedFractiles(expr, 0.4, 0.45, 2) << endl; } { IPosition shape(2, nx, ny); Array arr(shape); indgen (arr); ArrayLattice aF(arr); cout << "Fractiles (left 50%, right 50%) test: "; cout << LatticeFractile::unmaskedFractiles (aF, 0.5, 0.5) << endl; cout << LatticeFractile::unmaskedFractiles (aF, 0.5, 0.5, 16) << endl; cout << LatticeFractile::unmaskedFractiles (aF, 0.5, 0.5, 4) << endl; cout << LatticeFractile::unmaskedFractiles (aF, 0.5, 0.5, 2) << endl; LatticeExprNode afExpr(aF); LatticeExpr expr(afExpr[aF>4]); cout << LatticeFractile::maskedFractile(expr, 0.5) << endl; cout << "Fractiles (left 40%, right 45%) test" << endl; cout << LatticeFractile::unmaskedFractiles (aF, 0.40, 0.55, 2) << endl; cout << LatticeFractile::maskedFractiles(expr, 0.40, 0.55) << endl; cout << LatticeFractile::maskedFractiles(expr, 0.40, 0.55, 2) << endl; } { IPosition shape(2, 10*nx, 10*ny); Array arr(shape); indgen (arr); ArrayLattice aF(arr); cout << "Fractiles (left 50%, right 50%) test: "; cout << LatticeFractile::unmaskedFractiles (aF, 0.5, 0.5) << endl; cout << LatticeFractile::unmaskedFractiles (aF, 0.5, 0.5, 16) << endl; cout << LatticeFractile::unmaskedFractiles (aF, 0.5, 0.5, 4) << endl; cout << LatticeFractile::unmaskedFractiles (aF, 0.5, 0.5, 2) << endl; LatticeExprNode afExpr(aF); LatticeExpr expr(afExpr[aF>4]); cout << LatticeFractile::maskedFractiles (expr, 0.5, 0.5) << endl; cout << LatticeFractile::maskedFractiles (expr, 0.5, 0.5, 2) << endl; } { IPosition shape(2, 32*nx, 32*ny); Array arr(shape); indgen (arr); ArrayLattice aF(arr); cout << "Fractiles (left 50%, right 50%) test: "; cout << LatticeFractile::unmaskedFractiles (aF, 0.5, 0.5) << endl; cout << LatticeFractile::unmaskedFractiles (aF, 0.5, 0.5, 16) << endl; cout << LatticeFractile::unmaskedFractiles (aF, 0.5, 0.5, 4) << endl; cout << LatticeFractile::unmaskedFractiles (aF, 0.5, 0.5, 2) << endl; LatticeExprNode afExpr(aF); LatticeExpr expr(afExpr[aF>4]); cout << LatticeFractile::maskedFractiles (expr, 0.5, 0.5) << endl; cout << LatticeFractile::maskedFractiles (expr, 0.5, 0.5, 2) << endl; } // Hereafter numbers can be different on different machines. // So 'outcomment' it for assay (and also outcomment the timings). cout << ">>>" << endl; { IPosition shape(2, 100*nx, 100*ny); Array arr(shape); indgen (arr, float(0), float(0.01)); ArrayLattice aF(arr); cout << "Last value = " << arr(shape-1) << endl; Timer timer; timer.mark(); cout << LatticeFractile::unmaskedFractiles (aF, 0.2, 0.8, 512*512) << endl; timer.show ("ArrayLattice 20% fractiles"); timer.mark(); cout << LatticeFractile::unmaskedFractiles (aF, 0.2, 0.8, 1280) << endl; timer.show ("ArrayLat 1280 20% fractiles"); timer.mark(); LatticeExprNode afExpr(aF); LatticeExpr expr(afExpr[aF>4]); cout << LatticeFractile::maskedFractiles (expr, 0.2, 0.8) << endl; timer.show ("MaskedLattice 20% fractiles"); } { IPosition shape(2, 100*nx, 100*ny); Array arr(shape); indgen (arr, float(0), float(0.01)); TempLattice aF(shape); aF.put (arr); Timer timer; cout << LatticeFractile::unmaskedFractiles(aF, 0.2, 0.8) << endl; timer.show ("PagedArray 20% fractiles"); timer.mark(); cout << LatticeFractile::unmaskedFractiles(aF, 0.2, 0.8, 4) << endl; timer.show ("PagedArr 4 20% fractiles"); timer.mark(); cout << LatticeFractile::unmaskedFractiles(aF, 0.2, 0.8, 1280) << endl; timer.show ("PagedArr 1280 20% fractiles"); timer.mark(); LatticeExprNode afExpr(aF); LatticeExpr expr(afExpr[aF>4]); cout << LatticeFractile::maskedFractiles (expr, 0.2, 0.8) << endl; timer.show ("MaskedLattice 20% fractiles"); } { IPosition fshape(2, 500*nx, 500*ny); TempLattice aF(fshape); IPosition shape = aF.niceCursorShape(); cout << "tileshape = " << shape << endl; Array arr(shape); indgen (arr, float(0), float(0.01)); Timer timer; LatticeIterator iter(aF, shape); while (! iter.atEnd()) { iter.woCursor() = arr; iter++; } timer.show ("Fill PagedArr"); timer.mark(); cout << LatticeFractile::unmaskedFractiles(aF, 0.5, 0.5) << endl; timer.show ("BigPagedArray"); timer.mark(); cout << LatticeFractile::unmaskedFractiles(aF, 0.5, 0.5, 1280*25) << endl; timer.show ("PagedArr 1280"); } cout << "<<<" << endl; } catch (const std::exception& x) { cout << "Unexpected exception: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/lattices/LatticeMath/test/tLatticeFractile.out000066400000000000000000000031021476623553700242620ustar00rootroot00000000000000>>> /export/home/gvd/aips++/sun4sol_egcs/bin/tLatticeFractile: Version <<< Fractiles (left 50%, right 50%) test [1, 1] [1, 1] [1, 1] [1, 1] [] Fractiles (left 40%, right 45%) test [1, 1] [] [] Fractiles (left 50%, right 50%) test: [127, 127] [127, 127] [127, 127] [127, 127] [130] Fractiles (left 40%, right 45%) test [102, 140] [105, 142] [105, 142] Fractiles (left 50%, right 50%) test: [12799, 12799] [12799, 12799] [12799, 12799] [12799, 12799] [12802, 12802] [12802, 12802] Fractiles (left 50%, right 50%) test: [131071, 131071] [131071, 131071] [131071, 131071] [131071, 131071] [131074, 131074] [131074, 131074] >>> Last value = 25099.6 [5099.59, 20099.6] ArrayLattice 20% fractiles 1.55 real 1.55 user 0 system [5099.59, 20099.6] ArrayLat 1280 20% fractiles 1.54 real 1.54 user 0 system [5102.72, 20100.4] MaskedLattice 20% fractiles 4.01 real 3.09 user 0.86 system [5099.59, 20099.6] PagedArray 20% fractiles 1.55 real 1.55 user 0 system [5099.59, 20099.6] PagedArr 4 20% fractiles 1.91 real 1.91 user 0 system [5099.59, 20099.6] PagedArr 1280 20% fractiles 1.54 real 1.54 user 0 system [5102.72, 20100.4] MaskedLattice 20% fractiles 4.12 real 3.17 user 0.78 system tileshape = [200, 160] Fill PagedArr 42.24 real 6.73 user 6.76 system [159.991, 159.991] BigPagedArray 92.33 real 21.87 user 10.32 system [159.991, 159.991] PagedArr 1280 171.81 real 30.89 user 20.09 system <<< casacore-3.7.1/lattices/LatticeMath/test/tLatticeHistograms.cc000066400000000000000000000247021476623553700244400ustar00rootroot00000000000000//# tLatticeHistograms.cc: test LatticeHistograms class //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void doitFloat(LogIO& os); void do1DFloat (const Array& inArr, LogIO& os); void do2DFloat (const Array& inArr, LogIO& os); void test1DFloat (LatticeHistograms& histo, const IPosition& shape, uInt nBin); void test2DFloat (LatticeHistograms& histo, const IPosition& shape, uInt nBin); int main() { try { LogOrigin lor("tLatticeHistograms", __func__, WHERE); LogIO os(lor); doitFloat(os); { Array arr(IPosition(2, 5, 10), 0.0f); for (uInt i=0; i<10; i++ ) { arr(IPosition(2, 4, i)) = 100*(i+1); arr(IPosition(2, 0, i)) = -arr(IPosition(2, 4, i)); } ArrayLattice latt(arr); SubLattice subLatt(latt); LatticeHistograms lh(subLatt); lh.setNBins(25); Array values, counts; Vector range(2); range[0] = -5; range[1] = 5; lh.setIncludeRange(range); lh.getHistograms(values, counts); AlwaysAssert(counts(IPosition(1, 12)) == 30, AipsError); } { Array arr(IPosition(3, 2, 4, 6), 0.0f); Float sum = 0; for (uInt i=0; i<2; ++i) { for (uInt j=0; j<4; ++j) { for (uInt k=0; k<6; ++k) { arr(IPosition(3, i, j, k)) = i + j + k; sum += arr(IPosition(3, i, j, k)); } } } ArrayLattice latt(arr); SubLattice subLatt(latt); LatticeHistograms lh(subLatt); lh.setNBins(25); Array values, counts; Array > stats; lh.getHistograms(values, counts, stats); AlwaysAssert(stats.shape() == IPosition(1, 1), AipsError); lh.getHistograms(values, counts, stats); AlwaysAssert( stats(IPosition(1, 0))[LatticeStatsBase::SUM] == sum, AipsError ); AlwaysAssert( stats(IPosition(1, 0))[LatticeStatsBase::NPTS] == 48, AipsError ); AlwaysAssert( stats(IPosition(1, 0))[LatticeStatsBase::MEAN] == sum/48, AipsError ); Vector axes(2, 0); axes[1] = 1; lh.setAxes(axes); lh.getHistograms(values, counts, stats); AlwaysAssert(stats.shape() == IPosition(1, 6), AipsError); for (uInt i=0; i<6; ++i) { Float sum = 16 + 8*i; AlwaysAssert( stats(IPosition(1, i))[LatticeStatsBase::SUM] == sum, AipsError ); AlwaysAssert( stats(IPosition(1, i))[LatticeStatsBase::NPTS] == 8, AipsError ); AlwaysAssert( stats(IPosition(1, i))[LatticeStatsBase::MEAN] = sum/8.0, AipsError ); } axes.resize(1); axes[0] = 1; lh.setAxes(axes); lh.getHistograms(values, counts, stats); AlwaysAssert(stats.shape() == IPosition(2, 2, 6), AipsError); for (uInt i=0; i<2; ++i) { for (uInt j=0; j<6; ++j) { Float sum = 4*(i+j) + 6; AlwaysAssert( stats(IPosition(2, i, j))[LatticeStatsBase::SUM] == sum, AipsError ); AlwaysAssert( stats(IPosition(2, i, j))[LatticeStatsBase::NPTS] == 4, AipsError ); AlwaysAssert( stats(IPosition(2, i, j))[LatticeStatsBase::MEAN] = sum/4.0, AipsError ); } } } } catch (const std::exception& x) { cerr << "aipserror: error " << x.what() << endl; return 1; } return 0; } void doitFloat (LogIO& os) { // Construct data array IPosition shape(1); shape = 40; Array inArr(shape); indgen(inArr); // Make 1D Lattice and test do1DFloat(inArr, os); // Make 2D lattice and test do2DFloat(inArr, os); } void do1DFloat (const Array& inArr, LogIO& os) { const IPosition shape = inArr.shape(); ArrayLattice inLat(inArr); SubLattice subLat(inLat); LatticeHistograms histo(subLat, os, False, False); // Make a flat histogram so we can test it easily const uInt nBin = shape(0)/4; AlwaysAssert(histo.setNBins(nBin), AipsError); // Test test1DFloat (histo, shape, nBin); // Test copy constructor - feeble test { LatticeHistograms histo2(histo); test1DFloat (histo2, shape, nBin); } // Test assignment operator - feeble test { LatticeHistograms histo2(histo); histo = histo2; test1DFloat (histo, shape, nBin); } // Test setNewLattice - feeble test { AlwaysAssert(histo.setNewLattice(subLat), AipsError); test1DFloat (histo, shape, nBin); } } void do2DFloat (const Array& arr, LogIO& os) { uInt nX = arr.shape()(0); uInt nY = 20; IPosition shape(2,nX,nY); // Fill Lattice with replicated rows ArrayLattice lat(shape); Slicer slice(IPosition(2,0,0),shape,Slicer::endIsLength); LatticeUtilities::replicate (lat, slice, arr); SubLattice subLat(lat); // Make LS object and set axes so that we work out histo // over first axis as a function of nY replicated rows LatticeHistograms histo(subLat, os, False, False); Vector axes(1); axes = 0; AlwaysAssert(histo.setAxes(axes), AipsError); // Make a flat histogram so we can test it easily const uInt nBin = shape(0)/4; AlwaysAssert(histo.setNBins(nBin), AipsError); // const uInt n = nX/4; AlwaysAssert(histo.setNBins(n), AipsError); // Test test2DFloat (histo, shape, nBin); // Test copy constructor - feeble test { LatticeHistograms histo2(histo); test2DFloat (histo2, shape, nBin); } // Test assignment operator - feeble test { LatticeHistograms histo2(histo); histo = histo2; test2DFloat (histo, shape, nBin); } // Test setNewLattice - feeble test { AlwaysAssert(histo.setNewLattice(subLat), AipsError); test2DFloat (histo, shape, nBin); } } void test1DFloat (LatticeHistograms& histo, const IPosition& shape, uInt nBin) { AlwaysAssert(histo.displayAxes().nelements()==0, AipsError); // Float val = Float(shape(0) / nBin); Double tol = 1.0e-6; { IPosition pos(1,0); Vector values, counts; AlwaysAssert(histo.getHistogram(values, counts, pos, True), AipsError); } // { Vector values, counts; AlwaysAssert(histo.getHistograms(values, counts), AipsError); AlwaysAssert(values.shape()==IPosition(1,nBin),AipsError); AlwaysAssert(counts.shape()==IPosition(1,nBin),AipsError); AlwaysAssert(allNear(counts, val, tol), AipsError); } } void test2DFloat (LatticeHistograms& histo, const IPosition& shape, uInt nBin) { AlwaysAssert(shape.nelements()==2,AipsError); const Vector dA = histo.displayAxes(); AlwaysAssert(dA.nelements()==1, AipsError); AlwaysAssert(dA(0)==1, AipsError); const uInt nY = shape(1); // Float val = Float(shape(0) / nBin); Double tol = 1.0e-6; // { IPosition pos(2,0,0); Vector values, counts; AlwaysAssert(histo.getHistogram(values, counts, pos, True), AipsError); AlwaysAssert(values.shape()==IPosition(1,nBin),AipsError); } // Check histo correct for each row { Array values, counts; AlwaysAssert(histo.getHistograms(values, counts), AipsError); AlwaysAssert(values.shape()==IPosition(2,nBin,nY),AipsError); AlwaysAssert(counts.shape()==IPosition(2,nBin,nY),AipsError); // IPosition beg(2,0,0); IPosition end(2,nBin-1,0); for (uInt j=0; j h = counts(beg, end); AlwaysAssert(allNear(h,val,tol), AipsError); } } } casacore-3.7.1/lattices/LatticeMath/test/tLatticeMathUtil.cc000066400000000000000000000121101476623553700240350ustar00rootroot00000000000000//# tLatticeMathUtil.cc: //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void doMinMax(); void doCollapse(); int main() { try { // minMax doMinMax(); // Collapse doCollapse(); } catch (const std::exception& x) { cout<< "FAIL"<< endl; cerr << x.what() << endl; return 1; } cout<< "OK"<< endl; return 0; } void doMinMax() { cerr << "minMax " << endl; TempLattice lat(IPosition(3,512,512,10), 1.0); lat.set(0.0); lat.putAt( 1.0, IPosition(3, 10, 10, 0) ); lat.putAt( -1.0, IPosition(3, 20, 20, 2) ); lat.putAt( 1.0, IPosition(3, 500, 400, 3) ); lat.putAt( -1.0, IPosition(3, 502, 490, 4) ); lat.putAt( 2.0, IPosition(3, 400, 500, 5) ); lat.putAt( -2.0, IPosition(3, 10, 400, 6) ); lat.putAt( 3.0, IPosition(3, 400, 100, 7) ); lat.putAt( -3.0, IPosition(3, 500, 100, 8) ); Float lmin, lmax; IPosition lminPos(3, 0); IPosition lmaxPos(3, 0); minMax(lmin, lmax, lminPos, lmaxPos, lat); IPosition trueMaxPos = IPosition(3, 400, 100, 7); IPosition trueMinPos = IPosition(3, 500, 100, 8); AlwaysAssert(trueMaxPos == lmaxPos && lmax == 3.0, AipsError); AlwaysAssert(trueMinPos == lminPos && lmin == -3.0, AipsError); } void doCollapse () { cerr << "Collapse" << endl; IPosition shape(3, 10, 20, 30); ArrayLattice latIn(shape); latIn.set(1.0); Array data; Array mask; IPosition axes(2); axes(0) = 1; axes(1) = 2; // Unmasked input { cerr << " Unmasked" << endl; SubLattice mLatIn(latIn); // LatticeMathUtil::collapse (data, axes, mLatIn, True); AlwaysAssert(data.ndim()==1, AipsError); AlwaysAssert(data.shape()(0)==shape(0), AipsError); AlwaysAssert(allNear(data, Float(1.0), 1.0e-6), AipsError); // LatticeMathUtil::collapse (data, mask, axes, mLatIn, True, True, True); AlwaysAssert(data.ndim()==1, AipsError); AlwaysAssert(mask.ndim()==1, AipsError); AlwaysAssert(data.shape()(0)==shape(0), AipsError); AlwaysAssert(mask.shape()(0)==shape(0), AipsError); AlwaysAssert(allNear(data, Float(1.0), 1.0e-6), AipsError); AlwaysAssert(allEQ(mask, True), AipsError); } // Masked Input { cerr << " Masked" << endl; SubLattice mLatIn(latIn); // ArrayLattice maskLat(shape); maskLat.set(True); mLatIn.setPixelMask(maskLat, True); // LatticeMathUtil::collapse (data, axes, mLatIn, True); AlwaysAssert(data.ndim()==1, AipsError); AlwaysAssert(data.shape()(0)==shape(0), AipsError); AlwaysAssert(allNear(data, Float(1.0), 1.0e-6), AipsError); // LatticeMathUtil::collapse (data, mask, axes, mLatIn, True, True, True); AlwaysAssert(data.ndim()==1, AipsError); AlwaysAssert(mask.ndim()==1, AipsError); AlwaysAssert(data.shape()(0)==shape(0), AipsError); AlwaysAssert(mask.shape()(0)==shape(0), AipsError); AlwaysAssert(allNear(data, Float(1.0), 1.0e-6), AipsError); AlwaysAssert(allEQ(mask, True), AipsError); } } casacore-3.7.1/lattices/LatticeMath/test/tLatticeSlice1D.cc000066400000000000000000000272501476623553700235450ustar00rootroot00000000000000//# tLatticeSlice1D.cc: test LatticeSlice1D //# Copyright (C) 2001,2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void doit1 (); void doit2 (); void doit3 (); int main (int argc, const char* argv[]) { try { Input inputs(1); inputs.version ("$Revision$"); // Get inputs inputs.create("shape", "-10", "shape"); inputs.readArguments(argc, argv); const Block shapeU(inputs.getIntArray("shape")); // Convert inputs IPosition shapeIn; if (shapeU.nelements()>0) { if (shapeU.nelements()==1 && shapeU[0]==-10) { shapeIn = IPosition(2, 10, 10); } else { shapeIn.resize(shapeU.nelements()); for (uInt i=0; i::stringToMethod("NEAREST")==LatticeSlice1D::NEAREST, AipsError); AlwaysAssert(LatticeSlice1D::stringToMethod("LINEAR")==LatticeSlice1D::LINEAR, AipsError); AlwaysAssert(LatticeSlice1D::stringToMethod("CUBIC")==LatticeSlice1D::CUBIC, AipsError); // uInt nDim = 3; uInt nPts = 100; IPosition shape(nDim, 5, 10, 15); TiledShape shape2(shape); TempLattice inLat(shape2); inLat.set(1.0); SubLattice inML(inLat, True); // LatticeSlice1D slicer(inML, LatticeSlice1D::LINEAR); AlwaysAssert(slicer.interpolationMethod()==LatticeSlice1D::LINEAR, AipsError); // Vector data, x, y, distance; Vector mask; IPosition blc(nDim), trc(nDim); uInt axis0, axis1; // { cerr << "Slice in X-Y plane" << endl; blc = 0; trc = 0; trc(0) = shape(0) - 1; trc(1) = shape(1) - 1; // slicer.getSlice (data, mask, blc, trc, nPts); AlwaysAssert(data.nelements()==nPts, AipsError); AlwaysAssert(allNear(data, Float(1.0), Double(1.0e-6)), AipsError); AlwaysAssert(allEQ(mask, True), AipsError); // slicer.getPosition (axis0, axis1, x, y, distance); AlwaysAssert(x.nelements()==nPts, AipsError); AlwaysAssert(y.nelements()==nPts, AipsError); AlwaysAssert(distance.nelements()==nPts, AipsError); AlwaysAssert((axis0==0&&axis1==1), AipsError); } // { cerr << "Slice in X-Z plane" << endl; blc = 0; trc = 0; trc(0) = shape(0) - 1; trc(2) = shape(2) - 1; slicer.getSlice (data, mask, blc, trc, nPts); AlwaysAssert(data.nelements()==nPts, AipsError); AlwaysAssert(allNear(data, Float(1.0), Double(1.0e-6)), AipsError); AlwaysAssert(allEQ(mask, True), AipsError); // slicer.getPosition (axis0, axis1, x, y, distance); AlwaysAssert(x.nelements()==nPts, AipsError); AlwaysAssert(y.nelements()==nPts, AipsError); AlwaysAssert(distance.nelements()==nPts, AipsError); AlwaysAssert((axis0==0&&axis1==2), AipsError); } { cerr << "Slice in Y-Z plane" << endl; blc = 0; trc = 0; trc(1) = shape(1) - 1; trc(2) = shape(2) - 1; slicer.getSlice (data, mask, blc, trc, nPts); AlwaysAssert(data.nelements()==nPts, AipsError); AlwaysAssert(allNear(data, Float(1.0), Double(1.0e-6)), AipsError); AlwaysAssert(allEQ(mask, True), AipsError); // slicer.getPosition (axis0, axis1, x, y, distance); AlwaysAssert(x.nelements()==nPts, AipsError); AlwaysAssert(y.nelements()==nPts, AipsError); AlwaysAssert(distance.nelements()==nPts, AipsError); AlwaysAssert((axis0==1&&axis1==2), AipsError); } { cerr << "Slice in XYZ plane" << endl; blc = 0; trc = shape - 1; try { slicer.getSlice (data, mask, blc, trc, nPts); } catch (std::exception& x) { cerr << "Caught expected exception " << x.what() << endl; } } } void doit2 () { uInt nDim = 3; uInt nPts = 100; IPosition shape(nDim, 5, 10, 15); TiledShape shape2(shape); TempLattice inLat(shape2); inLat.set(1.0); SubLattice inML(inLat, True); // LatticeSlice1D slicer(inML, LatticeSlice1D::CUBIC); AlwaysAssert(slicer.interpolationMethod()==LatticeSlice1D::CUBIC, AipsError); // Vector data, x, y, distance; Vector data2, x2, y2, distance2; Vector mask, mask2; IPosition blc(nDim), trc(nDim); uInt axis0, axis1; // { cerr << "Slice in X-Y plane" << endl; blc = 0; trc = 0; trc(0) = shape(0) - 1; trc(1) = shape(1) - 1; // slicer.getSlice (data, mask, blc, trc, nPts); AlwaysAssert(data.nelements()==nPts, AipsError); AlwaysAssert(allNear(data, Float(1.0), Double(1.0e-6)), AipsError); AlwaysAssert(allEQ(mask, True), AipsError); // slicer.getPosition (axis0, axis1, x, y, distance); AlwaysAssert(x.nelements()==nPts, AipsError); AlwaysAssert(y.nelements()==nPts, AipsError); AlwaysAssert(distance.nelements()==nPts, AipsError); AlwaysAssert((axis0==0&&axis1==1), AipsError); } // Copy constructor { cerr << "Copy constructor" << endl; LatticeSlice1D slicer2(slicer); AlwaysAssert(slicer2.interpolationMethod()==LatticeSlice1D::CUBIC, AipsError); // slicer2.getSlice (data2, mask2, blc, trc, nPts); AlwaysAssert(data2.nelements()==nPts, AipsError); AlwaysAssert(allNear(data2, Float(1.0), Double(1.0e-6)), AipsError); AlwaysAssert(allEQ(mask2, True), AipsError); AlwaysAssert(allNear(data, data2, Double(1.0e-6)), AipsError); // slicer2.getPosition (axis0, axis1, x2, y2, distance2); AlwaysAssert(x.nelements()==nPts, AipsError); AlwaysAssert(y.nelements()==nPts, AipsError); AlwaysAssert(distance.nelements()==nPts, AipsError); AlwaysAssert((axis0==0&&axis1==1), AipsError); AlwaysAssert(allNear(x, x2, Double(1.0e-6)), AipsError); AlwaysAssert(allNear(y, y2, Double(1.0e-6)), AipsError); AlwaysAssert(allNear(distance, distance2, Double(1.0e-6)), AipsError); } // Assignment { cerr << "Assignment" << endl; LatticeSlice1D slicer2; try { slicer2.getSlice (data, mask, blc, trc, nPts); } catch (std::exception& x) { cerr << "Caught expected exception " << x.what() << endl; } // LatticeSlice1D slicer3(inML, LatticeSlice1D::CUBIC); slicer2 = slicer3; AlwaysAssert(slicer2.interpolationMethod()==LatticeSlice1D::CUBIC, AipsError); // slicer2.getSlice (data2, mask2, blc, trc, nPts); AlwaysAssert(data2.nelements()==nPts, AipsError); AlwaysAssert(allNear(data2, Float(1.0), Double(1.0e-6)), AipsError); AlwaysAssert(allEQ(mask2, True), AipsError); AlwaysAssert(allNear(data, data2, Double(1.0e-6)), AipsError); // slicer2.getPosition (axis0, axis1, x2, y2, distance2); AlwaysAssert(x2.nelements()==nPts, AipsError); AlwaysAssert(y2.nelements()==nPts, AipsError); AlwaysAssert(distance2.nelements()==nPts, AipsError); AlwaysAssert((axis0==0&&axis1==1), AipsError); AlwaysAssert(allNear(x, x2, Double(1.0e-6)), AipsError); AlwaysAssert(allNear(y, y2, Double(1.0e-6)), AipsError); AlwaysAssert(allNear(distance, distance2, Double(1.0e-6)), AipsError); } } void doit3 () { uInt nDim = 3; IPosition shape(nDim,20,40,60); TiledShape shape2(shape); TempLattice inLat(shape2); inLat.set(1.0); SubLattice inML(inLat, True); // LatticeSlice1D slicer(inML, LatticeSlice1D::LINEAR); AlwaysAssert(slicer.interpolationMethod()==LatticeSlice1D::LINEAR, AipsError); // Vector xIn(3), yIn(3); uInt nPts = 100; Vector data, x, y, distance; Vector mask; IPosition coord(nDim,0); uInt axis0, axis1; // { cerr << "Polyline slice in X-Y plane" << endl; xIn(0) = 0.0; xIn(1) = 10.0; xIn(2) = 18.0; yIn(0) = 2.3; yIn(1) = 15.4; yIn(2) = 35.0; PixelCurve1D curve(xIn,yIn,nPts); slicer.getSlice (data, mask, curve, 0, 1, coord); AlwaysAssert(data.nelements()==nPts, AipsError); AlwaysAssert(allNear(data, Float(1.0), Double(1.0e-6)), AipsError); AlwaysAssert(allEQ(mask, True), AipsError); // slicer.getPosition (axis0, axis1, x, y, distance); AlwaysAssert(x.nelements()==nPts, AipsError); AlwaysAssert(y.nelements()==nPts, AipsError); AlwaysAssert(distance.nelements()==nPts, AipsError); AlwaysAssert((axis0==0&&axis1==1), AipsError); } { cerr << "Polyline slice in X-Z plane" << endl; xIn(0) = 0.0; xIn(1) = 10.0; xIn(2) = 18.0; yIn(0) = 2.3; yIn(1) = 15.4; yIn(2) = 35.0; PixelCurve1D curve(xIn,yIn,nPts); slicer.getSlice (data, mask, curve, 0, 2, coord); AlwaysAssert(data.nelements()==nPts, AipsError); AlwaysAssert(allNear(data, Float(1.0), Double(1.0e-6)), AipsError); AlwaysAssert(allEQ(mask, True), AipsError); // slicer.getPosition (axis0, axis1, x, y, distance); AlwaysAssert(x.nelements()==nPts, AipsError); AlwaysAssert(y.nelements()==nPts, AipsError); AlwaysAssert(distance.nelements()==nPts, AipsError); AlwaysAssert((axis0==0&&axis1==2), AipsError); } { cerr << "Polyline slice in Y-Z plane" << endl; xIn(0) = 0.0; xIn(1) = 10.0; xIn(2) = 18.0; yIn(0) = 2.3; yIn(1) = 15.4; yIn(2) = 35.0; PixelCurve1D curve(xIn,yIn,nPts); slicer.getSlice (data, mask, curve, 1, 2, coord); AlwaysAssert(data.nelements()==nPts, AipsError); AlwaysAssert(allNear(data, Float(1.0), Double(1.0e-6)), AipsError); AlwaysAssert(allEQ(mask, True), AipsError); // slicer.getPosition (axis0, axis1, x, y, distance); AlwaysAssert(x.nelements()==nPts, AipsError); AlwaysAssert(y.nelements()==nPts, AipsError); AlwaysAssert(distance.nelements()==nPts, AipsError); AlwaysAssert((axis0==1&&axis1==2), AipsError); } } casacore-3.7.1/lattices/LatticeMath/test/tLatticeStatistics.cc000066400000000000000000001516701476623553700244570ustar00rootroot00000000000000//# tLatticeStatistics.cc: test LatticeStatistics class //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void doitFloat(LogIO& os); void do1DFloat (const Vector& results, const Vector& hasResult, const Array& inArr, LogIO& os); void do2DFloat (const Vector& results, const Vector& hasResult, const Array& inArr, LogIO& os); void test1DFloat (LatticeStatistics& stats, const Vector& results, const Vector& hasResult, const IPosition& shape); void test2DFloat (LatticeStatistics& stats, const Vector& results, const Vector& hasResult, const IPosition& shape); int main() { try { LogOrigin lor("tLatticeStatistics", "main()", WHERE); LogIO os(lor); doitFloat(os); Vector data(1000); Vector::iterator iter = data.begin(); Vector::iterator end = data.end(); uInt count = 0; while(iter != end) { *iter = count % 2 == 0 ? (Float)count : -(Float)(count*count); ++iter; ++count; } { ArrayLattice latt(data); SubLattice subLatt(latt); LatticeStatistics stats(subLatt); Array median, iqr, medabsdevmed, npts, q1, q3; stats.getStatistic(median, LatticeStatsBase::MEDIAN, False); AlwaysAssert(*median.begin() == -0.5, AipsError); stats.getStatistic(q1, LatticeStatsBase::Q1, False); AlwaysAssert(*q1.begin() == -251001, AipsError); stats.getStatistic(q3, LatticeStatsBase::Q3, False); AlwaysAssert(*q3.begin() == 498, AipsError); Vector range(2, 0.1); range[1] = 1001; stats.setInExCludeRange(range, Vector(), False); stats.getStatistic(median, LatticeStatsBase::MEDIAN, False); AlwaysAssert(*median.begin() == 500, AipsError); stats.getStatistic(iqr, LatticeStatsBase::QUARTILE, False); AlwaysAssert(*iqr.begin() == 500, AipsError); stats.getStatistic(medabsdevmed, LatticeStatsBase::MEDABSDEVMED, False); AlwaysAssert(*medabsdevmed.begin() == 250, AipsError); stats.getStatistic(q1, LatticeStatsBase::Q1, False); AlwaysAssert(*q1.begin() == 250, AipsError); stats.getStatistic(q3, LatticeStatsBase::Q3, False); AlwaysAssert(*q3.begin() == 750, AipsError); // exclude range stats.setInExCludeRange(Vector(), range, False); stats.getStatistic(median, LatticeStatsBase::MEDIAN, False); AlwaysAssert(*median.begin() == -249001, AipsError); stats.getStatistic(iqr, LatticeStatsBase::QUARTILE, False); AlwaysAssert(*iqr.begin() == 499000, AipsError); stats.getStatistic(medabsdevmed, LatticeStatsBase::MEDABSDEVMED, False); AlwaysAssert(*medabsdevmed.begin() == 216240, AipsError); stats.getStatistic(q1, LatticeStatsBase::Q1, False); AlwaysAssert(*q1.begin() == -561001, AipsError); stats.getStatistic(q3, LatticeStatsBase::Q3, False); AlwaysAssert(*q3.begin() == -62001, AipsError); // mask Vector mask(1000); Vector::iterator miter = mask.begin(); Vector::iterator mend = mask.end(); count = 0; while (miter != mend) { *miter = count % 3 == 0; ++miter; ++count; } subLatt.setPixelMask(ArrayLattice(mask), True); stats = LatticeStatistics(subLatt); stats.getStatistic(npts, LatticeStatsBase::NPTS, False); AlwaysAssert(*npts.begin() == 334, AipsError); stats.getStatistic(median, LatticeStatsBase::MEDIAN, False); AlwaysAssert(*median.begin() == -4.5, AipsError); stats.getStatistic(q1, LatticeStatsBase::Q1, False); AlwaysAssert(*q1.begin() == -251001, AipsError); stats.getStatistic(q3, LatticeStatsBase::Q3, False); AlwaysAssert(*q3.begin() == 498, AipsError); // include range stats.setInExCludeRange(range, Vector(), False); stats.getStatistic(median, LatticeStatsBase::MEDIAN, False); AlwaysAssert(*median.begin() == 501, AipsError); stats.getStatistic(iqr, LatticeStatsBase::QUARTILE, False); AlwaysAssert(*iqr.begin() == 498, AipsError); stats.getStatistic(medabsdevmed, LatticeStatsBase::MEDABSDEVMED, False); AlwaysAssert(*medabsdevmed.begin() == 249, AipsError); stats.getStatistic(q1, LatticeStatsBase::Q1, False); AlwaysAssert(*q1.begin() == 252, AipsError); stats.getStatistic(q3, LatticeStatsBase::Q3, False); AlwaysAssert(*q3.begin() == 750, AipsError); // exclude range stats.setInExCludeRange(Vector(), range, False); stats.getStatistic(npts, LatticeStatsBase::NPTS, False); AlwaysAssert(*npts.begin() == 168, AipsError); stats.getStatistic(median, LatticeStatsBase::MEDIAN, False); AlwaysAssert(*median.begin() == -248013, AipsError); stats.getStatistic(iqr, LatticeStatsBase::QUARTILE, False); AlwaysAssert(*iqr.begin() == 505008, AipsError); stats.getStatistic(medabsdevmed, LatticeStatsBase::MEDABSDEVMED, False); AlwaysAssert(*medabsdevmed.begin() == 216216, AipsError); stats.getStatistic(q1, LatticeStatsBase::Q1, False); AlwaysAssert(*q1.begin() == -567009, AipsError); stats.getStatistic(q3, LatticeStatsBase::Q3, False); AlwaysAssert(*q3.begin() == -62001, AipsError); // corner case when lattice is completely masked mask.set(False); subLatt.setPixelMask(ArrayLattice(mask), True); stats = LatticeStatistics(subLatt); stats.getStatistic(npts, LatticeStatsBase::NPTS, False); AlwaysAssert(npts.size() == 0, AipsError); } { // using configure*() methods ArrayLattice latt(data); SubLattice subLatt(latt); LatticeStatistics stats(subLatt); stats.configureClassical(); Array mean; Float expec = casacore::mean(data); stats.getStatistic(mean, LatticeStatsBase::MEAN, False); AlwaysAssert(near(*mean.begin(), expec), AipsError); stats.getStatistic(mean, LatticeStatsBase::MEAN, False); AlwaysAssert(near(*mean.begin(), expec), AipsError); Array v; stats.getStatistic(v, LatticeStatsBase::MAX, False); AlwaysAssert(near(*v.begin(), 998.0), AipsError); //hinges-fences stats.configureHingesFences(0.0); stats.getStatistic(mean, LatticeStatsBase::MEAN, False); expec = -41960.081836; AlwaysAssert(near(*mean.begin(), expec), AipsError); stats.configureFitToHalf( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); stats.getStatistic(v, LatticeStatsBase::MEAN, False); Double m = *v.begin(); AlwaysAssert(near(m, casacore::mean(data)), AipsError); stats.getStatistic(v, LatticeStatsBase::MEDIAN, False); AlwaysAssert(near(*v.begin(), m), AipsError); stats.getStatistic(v, LatticeStatsBase::NPTS, False); Int npts = (Int)*v.begin(); AlwaysAssert(npts == 592, AipsError); stats.getStatistic(v, LatticeStatsBase::SUM, False); Double sum = *v.begin(); AlwaysAssert(near(sum, m*npts), AipsError); stats.getStatistic(v, LatticeStatsBase::SUMSQ, False); AlwaysAssert(near(*v.begin(), 127119111260752.0), AipsError); stats.getStatistic(v, LatticeStatsBase::MIN, False); AlwaysAssert(near(*v.begin(), casacore::min(data)), AipsError); stats.getStatistic(v, LatticeStatsBase::MAX, False); AlwaysAssert(near(*v.begin(), 2*m - casacore::min(data)), AipsError); IPosition minPos, maxPos; stats.getMinMaxPos(minPos, maxPos); AlwaysAssert(minPos.size() == 1 && minPos[0] == 999, AipsError); AlwaysAssert(maxPos.size() == 0, AipsError); stats.getStatistic(v, LatticeStatsBase::Q1, False); AlwaysAssert(near(*v.begin(), -497025.0), AipsError); stats.getStatistic(v, LatticeStatsBase::Q3, False); AlwaysAssert(near(*v.begin(), 161375.0), AipsError); stats.configureFitToHalf( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::GE_CENTER ); stats.getStatistic(v, LatticeStatsBase::MEAN, False); m = *v.begin(); AlwaysAssert(near(m, casacore::mean(data)), AipsError); stats.getStatistic(v, LatticeStatsBase::MEDIAN, False); AlwaysAssert(near(*v.begin(), m), AipsError); stats.getStatistic(v, LatticeStatsBase::NPTS, False); npts = (Int)*v.begin(); AlwaysAssert(npts == 1408, AipsError); stats.getStatistic(v, LatticeStatsBase::SUM, False); sum = *v.begin(); AlwaysAssert(near(sum, m*npts), AipsError); stats.getStatistic(v, LatticeStatsBase::SUMSQ, False); AlwaysAssert(near(*v.begin(), 72880554407048.0), AipsError); stats.getStatistic(v, LatticeStatsBase::MIN, False); AlwaysAssert(near(*v.begin(), 2*m - casacore::max(data)), AipsError); stats.getStatistic(v, LatticeStatsBase::MAX, False); AlwaysAssert(near(*v.begin(), casacore::max(data)), AipsError); stats.getMinMaxPos(minPos, maxPos); AlwaysAssert(minPos.size() == 0, AipsError); AlwaysAssert(maxPos.size() == 1 && maxPos == 998, AipsError); stats.configureFitToHalf( FitToHalfStatisticsData::CMEDIAN, FitToHalfStatisticsData::LE_CENTER ); stats.getStatistic(v, LatticeStatsBase::MEAN, False); m = *v.begin(); AlwaysAssert(near(m, -0.5), AipsError); stats.getStatistic(v, LatticeStatsBase::NPTS, False); npts = (Int)*v.begin(); AlwaysAssert(npts == 1000, AipsError); stats.getStatistic(v, LatticeStatsBase::SUM, False); sum = *v.begin(); AlwaysAssert(near(sum, m*npts), AipsError); stats.getStatistic(v, LatticeStatsBase::SUMSQ, False); AlwaysAssert(near(*v.begin(), 199999000001300.0), AipsError); stats.getStatistic(v, LatticeStatsBase::MIN, False); AlwaysAssert(near(*v.begin(), casacore::min(data)), AipsError); stats.getStatistic(v, LatticeStatsBase::MAX, False); AlwaysAssert(near(*v.begin(),2*m - casacore::min(data)), AipsError); stats.getMinMaxPos(minPos, maxPos); AlwaysAssert(minPos.size() == 1 && minPos[0] == 999, AipsError); AlwaysAssert(maxPos.size() == 0, AipsError); stats.configureFitToHalf( FitToHalfStatisticsData::CMEDIAN, FitToHalfStatisticsData::GE_CENTER ); stats.getStatistic(v, LatticeStatsBase::MEAN, False); m = *v.begin(); AlwaysAssert(near(m, -0.5), AipsError); stats.getStatistic(v, LatticeStatsBase::NPTS, False); npts = (Int)*v.begin(); AlwaysAssert(npts == 1000, AipsError); stats.getStatistic(v, LatticeStatsBase::SUM, False); sum = *v.begin(); AlwaysAssert(near(sum, m*npts), AipsError); stats.getStatistic(v, LatticeStatsBase::SUMSQ, False); AlwaysAssert(near(*v.begin(), 332833500.0), AipsError); stats.getStatistic(v, LatticeStatsBase::MIN, False); AlwaysAssert(near(*v.begin(), 2*m - casacore::max(data)), AipsError); stats.getStatistic(v, LatticeStatsBase::MAX, False); AlwaysAssert(near(*v.begin(), casacore::max(data)), AipsError); stats.getMinMaxPos(minPos, maxPos); AlwaysAssert(minPos.size() == 0, AipsError); AlwaysAssert(maxPos.size() == 1 && maxPos[0] == 998, AipsError); stats.configureFitToHalf( FitToHalfStatisticsData::CVALUE, FitToHalfStatisticsData::LE_CENTER, 65 ); stats.getStatistic(v, LatticeStatsBase::MEAN, False); m = *v.begin(); AlwaysAssert(m == 65, AipsError); stats.getStatistic(v, LatticeStatsBase::NPTS, False); npts = (Int)*v.begin(); AlwaysAssert(npts == 1066, AipsError); stats.getStatistic(v, LatticeStatsBase::SUM, False); sum = *v.begin(); AlwaysAssert(near(sum, m*npts), AipsError); stats.getStatistic(v, LatticeStatsBase::SUMSQ, False); AlwaysAssert(near(*v.begin(), 200042675448460.0), AipsError); stats.getStatistic(v, LatticeStatsBase::MIN, False); AlwaysAssert(near(*v.begin(), min(data)), AipsError); stats.getStatistic(v, LatticeStatsBase::MAX, False); AlwaysAssert(near(*v.begin(), 2*m - casacore::min(data)), AipsError); stats.getMinMaxPos(minPos, maxPos); AlwaysAssert(minPos.size() == 1 && minPos[0] == 999, AipsError); AlwaysAssert(maxPos.size() == 0, AipsError); stats.configureFitToHalf( FitToHalfStatisticsData::CVALUE, FitToHalfStatisticsData::GE_CENTER, 65 ); stats.getStatistic(v, LatticeStatsBase::MEAN, False); m = *v.begin(); AlwaysAssert(m == 65, AipsError); stats.getStatistic(v, LatticeStatsBase::NPTS, False); npts = (Int)*v.begin(); AlwaysAssert(npts == 934, AipsError); stats.getStatistic(v, LatticeStatsBase::SUM, False); sum = *v.begin(); AlwaysAssert(near(sum, m*npts), AipsError); stats.getStatistic(v, LatticeStatsBase::SUMSQ, False); AlwaysAssert(near(*v.begin(), 275539340.0), AipsError); stats.getStatistic(v, LatticeStatsBase::MIN, False); AlwaysAssert(near(*v.begin(), 2*m - max(data)), AipsError); stats.getStatistic(v, LatticeStatsBase::MAX, False); AlwaysAssert(near(*v.begin(), casacore::max(data)), AipsError); stats.getMinMaxPos(minPos, maxPos); AlwaysAssert(minPos.size() == 0, AipsError); AlwaysAssert(maxPos.size() == 1 && maxPos[0] == 998, AipsError); // mask Vector mask(1000); Vector::iterator miter = mask.begin(); Vector::iterator mend = mask.end(); count = 0; while (miter != mend) { *miter = count % 3 == 0; ++miter; ++count; } subLatt.setPixelMask(ArrayLattice(mask), True); stats = LatticeStatistics(subLatt); stats.configureFitToHalf( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); stats.getStatistic(v, LatticeStatsBase::MEAN, False); m = *v.begin(); AlwaysAssert(near(m, -167083.5), AipsError); stats.getStatistic(v, LatticeStatsBase::NPTS, False); npts = (Int)*v.begin(); AlwaysAssert(npts == 198, AipsError); stats.getStatistic(v, LatticeStatsBase::SUM, False); sum = *v.begin(); AlwaysAssert(near(sum, m*npts), AipsError); stats.getStatistic(v, LatticeStatsBase::SUMSQ, False); AlwaysAssert(near(*v.begin(), 42804555931071.0), AipsError); stats.getStatistic(v, LatticeStatsBase::MIN, False); AlwaysAssert(near(*v.begin(), -998001.0), AipsError); stats.getStatistic(v, LatticeStatsBase::MAX, False); AlwaysAssert(near(*v.begin(), 2*-167083.5 - -998001.0), AipsError); stats.getMinMaxPos(minPos, maxPos); AlwaysAssert(minPos.size() == 1 && minPos[0] == 999, AipsError); AlwaysAssert(maxPos.size() == 0, AipsError); // biweight // unset the mask mask.set(True); subLatt.setPixelMask(ArrayLattice(mask), True); stats.configureBiweight(20, 6); Bool thrown = False; try { stats.getStatistic(v, LatticeStatsBase::SUM, False); } catch (const AipsError&) { thrown = True; } AlwaysAssert(thrown, AipsError); thrown = False; try { stats.getStatistic(v, LatticeStatsBase::MEDABSDEVMED, False); } catch (const AipsError&) { thrown = True; } AlwaysAssert(thrown, AipsError); thrown = False; try { stats.getMinMaxPos(minPos, maxPos); } catch (const AipsError&) { thrown = True; } AlwaysAssert(thrown, AipsError); stats.getStatistic(v, LatticeStatsBase::MAX, False); AlwaysAssert(near(*v.begin(), 998.0), AipsError); stats.getStatistic(v, LatticeStatsBase::MIN, False); AlwaysAssert(near(*v.begin(), -998001.0), AipsError); stats.getStatistic(v, LatticeStatsBase::NPTS, False); AlwaysAssert(near(*v.begin(), 1000.0), AipsError); stats.getStatistic(v, LatticeStatsBase::MEAN, False); AlwaysAssert(near(*v.begin(), 471.024223013, 3e-5), AipsError); stats.getStatistic(v, LatticeStatsBase::SIGMA, False); AlwaysAssert(near(*v.begin(), 461.243958957, 2e-5), AipsError); } { cout << "test stats for complex value lattice using old and new methods" << endl; uInt size = 500000; Vector cdata(size); Vector::iterator iter = cdata.begin(); Vector::iterator end = cdata.end(); Float i = 0; DComplex expMean((size -1 )/2.0, (size - 1)/2.0); DComplex expNVar = 0; DComplex expSumSq(0, 0); DComplex diff(0, 0); for (; iter!=end; ++iter, ++i) { *iter = Complex(i, i); expSumSq += *iter * *iter; diff = *iter - expMean; expNVar += diff*diff; } DComplex expSum(124999750000, 124999750000); DComplex expNpts(size, 0); DComplex expVar = expNVar/DComplex(size - 1, 0); DComplex expSigma = sqrt(expVar); DComplex expRMS = sqrt(expSumSq/(Double)size); Array sum, npts, mean, sumsq, var, sigma, rms, mymax, mymin; IPosition pos(1, 0); ArrayLattice latt(cdata); SubLattice subLatt(latt); LatticeStatistics statsOld(subLatt); statsOld.configureClassical(0, 0, 1, 1); statsOld.getStatistic(sum, LatticeStatsBase::SUM); statsOld.getStatistic(npts, LatticeStatsBase::NPTS); statsOld.getStatistic(mean, LatticeStatsBase::MEAN); statsOld.getStatistic(sumsq, LatticeStatsBase::SUMSQ); statsOld.getStatistic(var, LatticeStatsBase::VARIANCE); statsOld.getStatistic(sigma, LatticeStatsBase::SIGMA); statsOld.getStatistic(rms, LatticeStatsBase::RMS); statsOld.getStatistic(mymax, LatticeStatsBase::MAX); statsOld.getStatistic(mymin, LatticeStatsBase::MIN); AlwaysAssert(sum(pos) == expSum, AipsError); AlwaysAssert(npts(pos) == expNpts, AipsError); AlwaysAssert(mean(pos) == expMean, AipsError); AlwaysAssert(near(sumsq(pos), expSumSq, 1e-9), AipsError); AlwaysAssert(near(var(pos), expVar, 1e-10), AipsError); AlwaysAssert(near(sigma(pos), expSigma, 1e-11), AipsError); AlwaysAssert(near(rms(pos), expRMS, 1e-10), AipsError); AlwaysAssert(mymin(pos) == DComplex(0, 0), AipsError); AlwaysAssert(mymax(pos) == DComplex(size-1, size-1), AipsError); LatticeStatistics statsNew(subLatt); statsNew.configureClassical(1, 1, 0, 0); statsNew.getStatistic(sum, LatticeStatsBase::SUM); statsNew.getStatistic(npts, LatticeStatsBase::NPTS); statsNew.getStatistic(mean, LatticeStatsBase::MEAN); statsNew.getStatistic(sumsq, LatticeStatsBase::SUMSQ); statsNew.getStatistic(var, LatticeStatsBase::VARIANCE); statsNew.getStatistic(sigma, LatticeStatsBase::SIGMA); statsNew.getStatistic(rms, LatticeStatsBase::RMS); statsNew.getStatistic(mymax, LatticeStatsBase::MAX); statsNew.getStatistic(mymin, LatticeStatsBase::MIN); AlwaysAssert(sum(pos) == expSum, AipsError); AlwaysAssert(npts(pos) == expNpts, AipsError); AlwaysAssert(mean(pos) == expMean, AipsError); AlwaysAssert(near(sumsq(pos), expSumSq, 1e-9), AipsError); AlwaysAssert(near(var(pos), expVar, 1e-10), AipsError); AlwaysAssert(near(sigma(pos), expSigma, 1e-11), AipsError); AlwaysAssert(near(rms(pos), expRMS, 1e-9), AipsError); AlwaysAssert(mymin(pos) == DComplex(0, 0), AipsError); AlwaysAssert(mymax(pos) == DComplex(size-1, size-1), AipsError); Vector include(2), exclude(2); include[0] = Complex(10000, 10000); include[1] = Complex(20000, 20000); exclude[0] = Complex(400000, 400000); exclude[1] = Complex(600000, 600000); // unmasked, include range expSum = DComplex(150015000, 150015000); expNpts = DComplex(10001, 0); expSumSq = DComplex(0, 4667166670000); expMean = expSum/expNpts; expNVar = DComplex(0, 166716670000); expVar = expNVar/(expNpts - 1); expSigma = sqrt(expVar); expRMS = sqrt(expSumSq/expNpts); statsOld.setInExCludeRange(include, Vector()); statsOld.getStatistic(sum, LatticeStatsBase::SUM); statsOld.getStatistic(npts, LatticeStatsBase::NPTS); statsOld.getStatistic(mean, LatticeStatsBase::MEAN); statsOld.getStatistic(sumsq, LatticeStatsBase::SUMSQ); statsOld.getStatistic(var, LatticeStatsBase::VARIANCE); statsOld.getStatistic(sigma, LatticeStatsBase::SIGMA); statsOld.getStatistic(rms, LatticeStatsBase::RMS); statsOld.getStatistic(mymax, LatticeStatsBase::MAX); statsOld.getStatistic(mymin, LatticeStatsBase::MIN); AlwaysAssert(sum(pos) == expSum, AipsError); AlwaysAssert(npts(pos) == expNpts, AipsError); AlwaysAssert(mean(pos) == expMean, AipsError); AlwaysAssert(near(sumsq(pos), expSumSq, 1e-8), AipsError); AlwaysAssert(near(var(pos), expVar, 1e-8), AipsError); AlwaysAssert(near(sigma(pos), expSigma, 1e-11), AipsError); AlwaysAssert(near(rms(pos), expRMS, 1e-8), AipsError); AlwaysAssert(mymin(pos) == (DComplex)include[0], AipsError); AlwaysAssert(mymax(pos) == (DComplex)include[1], AipsError); statsNew.setInExCludeRange(include, Vector()); statsNew.getStatistic(sum, LatticeStatsBase::SUM); statsNew.getStatistic(npts, LatticeStatsBase::NPTS); statsNew.getStatistic(mean, LatticeStatsBase::MEAN); statsNew.getStatistic(sumsq, LatticeStatsBase::SUMSQ); statsNew.getStatistic(var, LatticeStatsBase::VARIANCE); statsNew.getStatistic(sigma, LatticeStatsBase::SIGMA); statsNew.getStatistic(rms, LatticeStatsBase::RMS); statsNew.getStatistic(mymax, LatticeStatsBase::MAX); statsNew.getStatistic(mymin, LatticeStatsBase::MIN); AlwaysAssert(sum(pos) == expSum, AipsError); AlwaysAssert(npts(pos) == expNpts, AipsError); AlwaysAssert(mean(pos) == expMean, AipsError); AlwaysAssert(near(sumsq(pos), expSumSq, 1e-8), AipsError); AlwaysAssert(near(var(pos), expVar, 1e-8), AipsError); AlwaysAssert(near(sigma(pos), expSigma, 1e-11), AipsError); AlwaysAssert(near(rms(pos), expRMS, 1e-8), AipsError); AlwaysAssert(mymin(pos) == (DComplex)include[0], AipsError); AlwaysAssert(mymax(pos) == (DComplex)include[1], AipsError); // unmasked, exclude range expSum = DComplex(79999800000, 79999800000); expNpts = DComplex(400000, 0); expSumSq = DComplex(0, 42666506666700080); expMean = expSum/expNpts; expNVar = DComplex(0, 10666666666446218); expVar = expNVar/(expNpts - 1); expSigma = sqrt(expVar); expRMS = sqrt(expSumSq/expNpts); statsOld.setInExCludeRange(Vector(), exclude); statsOld.getStatistic(sum, LatticeStatsBase::SUM); statsOld.getStatistic(npts, LatticeStatsBase::NPTS); statsOld.getStatistic(mean, LatticeStatsBase::MEAN); statsOld.getStatistic(sumsq, LatticeStatsBase::SUMSQ); statsOld.getStatistic(var, LatticeStatsBase::VARIANCE); statsOld.getStatistic(sigma, LatticeStatsBase::SIGMA); statsOld.getStatistic(rms, LatticeStatsBase::RMS); statsOld.getStatistic(mymax, LatticeStatsBase::MAX); statsOld.getStatistic(mymin, LatticeStatsBase::MIN); AlwaysAssert(sum(pos) == expSum, AipsError); AlwaysAssert(npts(pos) == expNpts, AipsError); AlwaysAssert(mean(pos) == expMean, AipsError); AlwaysAssert(near(sumsq(pos), expSumSq, 1e-8), AipsError); AlwaysAssert(near(var(pos), expVar, 1e-8), AipsError); AlwaysAssert(near(sigma(pos), expSigma, 1e-11), AipsError); AlwaysAssert(near(rms(pos), expRMS, 1e-8), AipsError); AlwaysAssert(mymin(pos) == DComplex(0, 0), AipsError); AlwaysAssert(mymax(pos) == DComplex(399999, 399999), AipsError); statsNew.setInExCludeRange(Vector(), exclude); statsNew.getStatistic(sum, LatticeStatsBase::SUM); statsNew.getStatistic(npts, LatticeStatsBase::NPTS); statsNew.getStatistic(mean, LatticeStatsBase::MEAN); statsNew.getStatistic(sumsq, LatticeStatsBase::SUMSQ); statsNew.getStatistic(var, LatticeStatsBase::VARIANCE); statsNew.getStatistic(sigma, LatticeStatsBase::SIGMA); statsNew.getStatistic(rms, LatticeStatsBase::RMS); statsNew.getStatistic(mymax, LatticeStatsBase::MAX); statsNew.getStatistic(mymin, LatticeStatsBase::MIN); AlwaysAssert(sum(pos) == expSum, AipsError); AlwaysAssert(npts(pos) == expNpts, AipsError); AlwaysAssert(mean(pos) == expMean, AipsError); AlwaysAssert(near(sumsq(pos), expSumSq, 1e-8), AipsError); AlwaysAssert(near(var(pos), expVar, 1e-8), AipsError); AlwaysAssert(near(sigma(pos), expSigma, 1e-11), AipsError); AlwaysAssert(near(rms(pos), expRMS, 1e-8), AipsError); AlwaysAssert(mymin(pos) == DComplex(0, 0), AipsError); AlwaysAssert(mymax(pos) == DComplex(399999, 399999), AipsError); // masked lattice, no range Vector mask(size); Vector::iterator miter = mask.begin(); Vector::iterator mend = mask.end(); Bool mval = False; for (; miter!=mend; ++miter) { *miter = mval; mval = ! mval; } ArrayLattice mlatt(mask); expSum = DComplex(62500000000, 62500000000); expNpts = DComplex(250000, 0); expSumSq = DComplex(0, 41666666666378080); expMean = expSum/expNpts; expNVar = DComplex(0, 10416666666500000); expVar = expNVar/(expNpts - 1); expSigma = sqrt(expVar); expRMS = sqrt(expSumSq/expNpts); subLatt.setPixelMask(mlatt, True); statsOld.setNewLattice(subLatt); statsOld.setInExCludeRange(Vector(), Vector()); statsOld.getStatistic(sum, LatticeStatsBase::SUM); statsOld.getStatistic(npts, LatticeStatsBase::NPTS); statsOld.getStatistic(mean, LatticeStatsBase::MEAN); statsOld.getStatistic(sumsq, LatticeStatsBase::SUMSQ); statsOld.getStatistic(var, LatticeStatsBase::VARIANCE); statsOld.getStatistic(sigma, LatticeStatsBase::SIGMA); statsOld.getStatistic(rms, LatticeStatsBase::RMS); statsOld.getStatistic(mymax, LatticeStatsBase::MAX); statsOld.getStatistic(mymin, LatticeStatsBase::MIN); AlwaysAssert(sum(pos) == expSum, AipsError); AlwaysAssert(npts(pos) == expNpts, AipsError); AlwaysAssert(mean(pos) == expMean, AipsError); AlwaysAssert(near(sumsq(pos), expSumSq, 1e-8), AipsError); AlwaysAssert(near(var(pos), expVar, 1e-8), AipsError); AlwaysAssert(near(sigma(pos), expSigma, 1e-11), AipsError); AlwaysAssert(near(rms(pos), expRMS, 1e-8), AipsError); AlwaysAssert(mymin(pos) == DComplex(1, 1), AipsError); AlwaysAssert(mymax(pos) == DComplex(499999, 499999), AipsError); statsNew.setNewLattice(subLatt); statsNew.setInExCludeRange(Vector(), Vector()); statsNew.getStatistic(sum, LatticeStatsBase::SUM); statsNew.getStatistic(npts, LatticeStatsBase::NPTS); statsNew.getStatistic(mean, LatticeStatsBase::MEAN); statsNew.getStatistic(sumsq, LatticeStatsBase::SUMSQ); statsNew.getStatistic(var, LatticeStatsBase::VARIANCE); statsNew.getStatistic(sigma, LatticeStatsBase::SIGMA); statsNew.getStatistic(rms, LatticeStatsBase::RMS); statsNew.getStatistic(mymax, LatticeStatsBase::MAX); statsNew.getStatistic(mymin, LatticeStatsBase::MIN); AlwaysAssert(sum(pos) == expSum, AipsError); AlwaysAssert(npts(pos) == expNpts, AipsError); AlwaysAssert(mean(pos) == expMean, AipsError); AlwaysAssert(near(sumsq(pos), expSumSq, 1e-8), AipsError); AlwaysAssert(near(var(pos), expVar, 1e-8), AipsError); AlwaysAssert(near(sigma(pos), expSigma, 1e-11), AipsError); AlwaysAssert(near(rms(pos), expRMS, 1e-8), AipsError); AlwaysAssert(mymin(pos) == DComplex(1, 1), AipsError); AlwaysAssert(mymax(pos) == DComplex(499999, 499999), AipsError); // mask with include range expSum = DComplex(75000000, 75000000); expNpts = DComplex(5000, 0); expSumSq = DComplex(0, 2333333330000); expMean = expSum/expNpts; expNVar = DComplex(0, 83333330000); expVar = expNVar/(expNpts - 1); expSigma = sqrt(expVar); expRMS = sqrt(expSumSq/expNpts); statsOld.setInExCludeRange(include, Vector()); statsOld.getStatistic(sum, LatticeStatsBase::SUM); statsOld.getStatistic(npts, LatticeStatsBase::NPTS); statsOld.getStatistic(mean, LatticeStatsBase::MEAN); statsOld.getStatistic(sumsq, LatticeStatsBase::SUMSQ); statsOld.getStatistic(var, LatticeStatsBase::VARIANCE); statsOld.getStatistic(sigma, LatticeStatsBase::SIGMA); statsOld.getStatistic(rms, LatticeStatsBase::RMS); statsOld.getStatistic(mymax, LatticeStatsBase::MAX); statsOld.getStatistic(mymin, LatticeStatsBase::MIN); AlwaysAssert(sum(pos) == expSum, AipsError); AlwaysAssert(npts(pos) == expNpts, AipsError); AlwaysAssert(mean(pos) == expMean, AipsError); AlwaysAssert(near(sumsq(pos), expSumSq, 2e-8), AipsError); AlwaysAssert(near(var(pos), expVar, 1e-8), AipsError); AlwaysAssert(near(sigma(pos), expSigma, 1e-11), AipsError); AlwaysAssert(near(rms(pos), expRMS, 1e-8), AipsError); AlwaysAssert(mymin(pos) == DComplex(10001, 10001), AipsError); AlwaysAssert(mymax(pos) == DComplex(19999, 19999), AipsError); statsNew.setInExCludeRange(include, Vector()); statsNew.getStatistic(sum, LatticeStatsBase::SUM); statsNew.getStatistic(npts, LatticeStatsBase::NPTS); statsNew.getStatistic(mean, LatticeStatsBase::MEAN); statsNew.getStatistic(sumsq, LatticeStatsBase::SUMSQ); statsNew.getStatistic(var, LatticeStatsBase::VARIANCE); statsNew.getStatistic(sigma, LatticeStatsBase::SIGMA); statsNew.getStatistic(rms, LatticeStatsBase::RMS); statsNew.getStatistic(mymax, LatticeStatsBase::MAX); statsNew.getStatistic(mymin, LatticeStatsBase::MIN); AlwaysAssert(sum(pos) == expSum, AipsError); AlwaysAssert(npts(pos) == expNpts, AipsError); AlwaysAssert(mean(pos) == expMean, AipsError); AlwaysAssert(near(sumsq(pos), expSumSq, 2e-8), AipsError); AlwaysAssert(near(var(pos), expVar, 1e-8), AipsError); AlwaysAssert(near(sigma(pos), expSigma, 1e-11), AipsError); AlwaysAssert(near(rms(pos), expRMS, 1e-8), AipsError); AlwaysAssert(mymin(pos) == DComplex(10001, 10001), AipsError); AlwaysAssert(mymax(pos) == DComplex(19999, 19999), AipsError); // mask with exclude range expSum = DComplex(40000000000, 40000000000); expNpts = DComplex(200000, 0); expSumSq = DComplex(0, 21333333333178080); expMean = expSum/expNpts; expNVar = DComplex(0, 5333333333200000); expVar = expNVar/(expNpts - 1); expSigma = sqrt(expVar); expRMS = sqrt(expSumSq/expNpts); statsOld.setInExCludeRange(Vector(), exclude); statsOld.getStatistic(sum, LatticeStatsBase::SUM); statsOld.getStatistic(npts, LatticeStatsBase::NPTS); statsOld.getStatistic(mean, LatticeStatsBase::MEAN); statsOld.getStatistic(sumsq, LatticeStatsBase::SUMSQ); statsOld.getStatistic(var, LatticeStatsBase::VARIANCE); statsOld.getStatistic(sigma, LatticeStatsBase::SIGMA); statsOld.getStatistic(rms, LatticeStatsBase::RMS); statsOld.getStatistic(mymax, LatticeStatsBase::MAX); statsOld.getStatistic(mymin, LatticeStatsBase::MIN); AlwaysAssert(sum(pos) == expSum, AipsError); AlwaysAssert(npts(pos) == expNpts, AipsError); AlwaysAssert(mean(pos) == expMean, AipsError); AlwaysAssert(near(sumsq(pos), expSumSq, 2e-8), AipsError); AlwaysAssert(near(var(pos), expVar, 1e-8), AipsError); AlwaysAssert(near(sigma(pos), expSigma, 1e-11), AipsError); AlwaysAssert(near(rms(pos), expRMS, 1e-8), AipsError); AlwaysAssert(mymin(pos) == DComplex(1, 1), AipsError); AlwaysAssert(mymax(pos) == DComplex(399999, 399999), AipsError); statsNew.setInExCludeRange(Vector(), exclude); statsNew.getStatistic(sum, LatticeStatsBase::SUM); statsNew.getStatistic(npts, LatticeStatsBase::NPTS); statsNew.getStatistic(mean, LatticeStatsBase::MEAN); statsNew.getStatistic(sumsq, LatticeStatsBase::SUMSQ); statsNew.getStatistic(var, LatticeStatsBase::VARIANCE); statsNew.getStatistic(sigma, LatticeStatsBase::SIGMA); statsNew.getStatistic(rms, LatticeStatsBase::RMS); statsNew.getStatistic(mymax, LatticeStatsBase::MAX); statsNew.getStatistic(mymin, LatticeStatsBase::MIN); AlwaysAssert(sum(pos) == expSum, AipsError); AlwaysAssert(npts(pos) == expNpts, AipsError); AlwaysAssert(mean(pos) == expMean, AipsError); AlwaysAssert(near(sumsq(pos), expSumSq, 2e-8), AipsError); AlwaysAssert(near(var(pos), expVar, 1e-8), AipsError); AlwaysAssert(near(sigma(pos), expSigma, 1e-11), AipsError); AlwaysAssert(near(rms(pos), expRMS, 1e-8), AipsError); AlwaysAssert(mymin(pos) == DComplex(1, 1), AipsError); AlwaysAssert(mymax(pos) == DComplex(399999, 399999), AipsError); } { // CAS-10938 cout << "test CAS-10938" << endl; Array largeArray(IPosition(3, 4000, 4000, OMP::nMaxThreads() + 1)); ArrayLattice myLatt(largeArray); SubLattice mySubLatt(myLatt); LatticeStatistics lattStats(mySubLatt); Vector axes(2, 0); axes[1] = 1; lattStats.setAxes(axes); // ensure the stats framework code branch will be used lattStats.configureClassical(1, 1, 0, 0); Array npts; // The fact that this call completes successfully is // sufficient to verify the bug fix AlwaysAssert(lattStats.getStatistic(npts, LatticeStatsBase::NPTS), AipsError); } { Vector mydata(2); mydata[0] = 0; mydata[1] = 1; ArrayLattice latt(mydata); SubLattice subLatt(latt); LatticeStatistics stats(subLatt); stats.configureFitToHalf( FitToHalfStatisticsData::CVALUE, FitToHalfStatisticsData::LE_CENTER ); stats.setComputeQuantiles(True); Array v; stats.getStatistic(v, LatticeStatsBase::MEAN, False); AlwaysAssert(*v.begin() == 0, AipsError); // fix for issue found in as part of CAS-10948 implementation // successful completion of the call verifies the fix stats.getStatistic(v, LatticeStatsBase::Q3, False); AlwaysAssert(*v.begin() == 0, AipsError); stats.getStatistic(v, LatticeStatsBase::Q1, False); AlwaysAssert(*v.begin() == 0, AipsError); } { // tests using the various lattice stats algorithms IPosition shape(3, 100, 100, 100); Array adata(shape); indgen(adata); ArrayLattice latt(adata); SubLattice subLatt(latt); LogIO log; for (uInt i=0; i<3; ++i) { LatticeStatistics stats(subLatt, log); stats.setComputeQuantiles(True); switch(i) { case 0: stats.forceUseOldTiledApplyMethod(); break; case 1: stats.forceUseStatsFrameworkUsingArrays(); break; case 2: stats.forceUseStatsFrameworkUsingDataProviders(); break; } Array stat; stats.getStatistic(stat, LatticeStatsBase::SUM); AlwaysAssert(*stat.begin() == 499999500000, AipsError); stats.getStatistic(stat, LatticeStatsBase::SUMSQ); AlwaysAssert(near(*stat.begin(), (Double)333332833333500000, 1e-9), AipsError); stats.getStatistic(stat, LatticeStatsBase::MEAN); AlwaysAssert(*stat.begin() == 499999.5, AipsError); stats.getStatistic(stat, LatticeStatsBase::RMS); AlwaysAssert(near(*stat.begin(), 577349.8361764728, 1e-9), AipsError); stats.getStatistic(stat, LatticeStatsBase::VARIANCE); AlwaysAssert(near(*stat.begin(), 83333416666.666672, 1e-9), AipsError); stats.getStatistic(stat, LatticeStatsBase::SIGMA); AlwaysAssert(near(*stat.begin(), 288675.2789323441, 1e-9), AipsError); stats.getStatistic(stat, LatticeStatsBase::MAX); AlwaysAssert(*stat.begin() == 999999, AipsError); stats.getStatistic(stat, LatticeStatsBase::MIN); AlwaysAssert(*stat.begin() == 0, AipsError); stats.getStatistic(stat, LatticeStatsBase::MEDIAN); AlwaysAssert(*stat.begin() == 499999.5, AipsError); stats.getStatistic(stat, LatticeStatsBase::Q1); AlwaysAssert(*stat.begin() == 249999, AipsError); stats.getStatistic(stat, LatticeStatsBase::Q3); AlwaysAssert(*stat.begin() == 749999, AipsError); stats.getStatistic(stat, LatticeStatsBase::QUARTILE); AlwaysAssert(*stat.begin() == 500000, AipsError); stats.getStatistic(stat, LatticeStatsBase::MEDABSDEVMED); AlwaysAssert(*stat.begin() == 250000, AipsError); IPosition minPos, maxPos; stats.getMinMaxPos(minPos, maxPos); AlwaysAssert(minPos == IPosition(3, 0, 0 ,0), AipsError); AlwaysAssert(maxPos == shape-1, AipsError); stats.setAxes(Vector(1, 1)); IPosition loc(2, 50, 50); stats.getStatistic(stat, LatticeStatsBase::SUM); AlwaysAssert(stat(loc) == 50500000, AipsError); stats.getStatistic(stat, LatticeStatsBase::SUMSQ); AlwaysAssert(near(stat(loc), (Double)25503333250000, 1e-8), AipsError); stats.getStatistic(stat, LatticeStatsBase::MEAN); AlwaysAssert(stat(loc) == 505000.0, AipsError); stats.getStatistic(stat, LatticeStatsBase::RMS); AlwaysAssert(near(stat(loc), 505008.24993261247, 1e-8), AipsError); stats.getStatistic(stat, LatticeStatsBase::VARIANCE); AlwaysAssert(near(stat(loc), 8416666.666666666, 1e-9), AipsError); stats.getStatistic(stat, LatticeStatsBase::SIGMA); AlwaysAssert(near(stat(loc), 2901.149197588202, 1e-9), AipsError); stats.getStatistic(stat, LatticeStatsBase::MAX); AlwaysAssert(stat(loc) == 509950, AipsError); stats.getStatistic(stat, LatticeStatsBase::MIN); AlwaysAssert(stat(loc) == 500050, AipsError); stats.getStatistic(stat, LatticeStatsBase::MEDIAN); AlwaysAssert(stat(loc) == 505000.0, AipsError); stats.getStatistic(stat, LatticeStatsBase::Q1); AlwaysAssert(stat(loc) == 502450, AipsError); stats.getStatistic(stat, LatticeStatsBase::Q3); AlwaysAssert(stat(loc) == 507450, AipsError); stats.getStatistic(stat, LatticeStatsBase::QUARTILE); AlwaysAssert(stat(loc) == 5000, AipsError); stats.getStatistic(stat, LatticeStatsBase::MEDABSDEVMED); AlwaysAssert(stat(loc) == 2500, AipsError); stats.getMinMaxPos(minPos, maxPos); AlwaysAssert(minPos.empty(), AipsError); AlwaysAssert(maxPos.empty(), AipsError); } } } catch (const std::exception& x) { cerr << "aipserror: error " << x.what() << endl; return 1; } return 0; } void doitFloat (LogIO& os) { // Construct lattice IPosition shape(1); shape = 64; Array inArr(shape); indgen(inArr); // // Vector results(LatticeStatsBase::NSTATS); Vector hasResult(LatticeStatsBase::NSTATS); hasResult = True; // results(LatticeStatsBase::NPTS) = Float(shape(0)); results(LatticeStatsBase::SUM) = sum(inArr); results(LatticeStatsBase::SUMSQ) = sum(square(inArr)); Float med = median(inArr); results(LatticeStatsBase::MEDIAN) = med; results(LatticeStatsBase::MEDABSDEVMED) = median(abs(inArr-med)); Float t1 = fractile(inArr, 0.25); Float t2 = fractile(inArr, 0.75); results(LatticeStatsBase::QUARTILE) = (t2-t1); results(LatticeStatsBase::Q1) = t1; results(LatticeStatsBase::Q3) = t2; results(LatticeStatsBase::MIN) = min(inArr); results(LatticeStatsBase::MAX) = max(inArr); results(LatticeStatsBase::MEAN) = mean(inArr); results(LatticeStatsBase::VARIANCE) = variance(inArr); results(LatticeStatsBase::SIGMA ) = stddev(inArr); results(LatticeStatsBase::RMS ) = rms(inArr); // hasResult(LatticeStatsBase::FLUX) = False; // Make 1D Lattice and test do1DFloat(results, hasResult, inArr, os); // Make 2D lattice and test do2DFloat(results, hasResult, inArr, os); } void do1DFloat (const Vector& results, const Vector& hasResult, const Array& inArr, LogIO& os) { const IPosition shape = inArr.shape(); ArrayLattice inLat(inArr); SubLattice subLat(inLat); LatticeStatistics stats(subLat, os, False, False); test1DFloat (stats, results, hasResult, shape); // Test copy constructor - feeble test { LatticeStatistics stats2(stats); test1DFloat (stats2, results, hasResult, shape); } // Test assignment operator - feeble test { LatticeStatistics stats2(stats); stats = stats2; test1DFloat (stats, results, hasResult, shape); } // Test setNewLattice - feeble test { AlwaysAssert(stats.setNewLattice(subLat), AipsError); test1DFloat (stats, results, hasResult, shape); } } void do2DFloat (const Vector& results, const Vector& hasResult, const Array& arr, LogIO& os) { uInt nX = arr.shape()(0); uInt nY = 20; IPosition shape(2,nX,nY); // Fill Lattice with replicated rows ArrayLattice lat(shape); Slicer slice(IPosition(2,0,0),shape,Slicer::endIsLength); LatticeUtilities::replicate (lat, slice, arr); SubLattice subLat(lat); // Make LS object and set axes so that we work out stats // over first axis as a function of nY replicated rows LatticeStatistics stats(subLat, os, False, False); Vector axes(1); axes = 0; AlwaysAssert(stats.setAxes(axes), AipsError); // Test test2DFloat (stats, results, hasResult, shape); // Test copy constructor - feeble test { LatticeStatistics stats2(stats); test2DFloat (stats2, results, hasResult, shape); } // Test assignment operator - feeble test { LatticeStatistics stats2(stats); stats = stats2; test2DFloat (stats, results, hasResult, shape); } // Test setNewLattice - feeble test { AlwaysAssert(stats.setNewLattice(subLat), AipsError); test2DFloat (stats, results, hasResult, shape); } } void test1DFloat (LatticeStatistics& stats, const Vector& results, const Vector& hasResult, const IPosition& shape) { AlwaysAssert(stats.displayAxes().nelements()==0, AipsError); // typedef NumericTraits::PrecisionType AccumType; Double tol = 1.0e-6; { IPosition pos(1,0); Vector data; AlwaysAssert(stats.getStats(data, pos, True), AipsError); } { const Int nStats = LatticeStatsBase::NSTATS; for (Int i=0; i a; LatticeStatsBase::StatisticsTypes t = static_cast(i); IPosition pos(1,0); if (t == LatticeStatsBase::FLUX) { AlwaysAssert(!stats.getStatistic (a, t, True), AipsError); } else { AlwaysAssert(stats.getStatistic (a, t, True), AipsError); } if (hasResult(i)) { AlwaysAssert(a.shape() == IPosition(1,1),AipsError); AlwaysAssert(near(a(pos), results(i), tol), AipsError); } Array b; if (t==LatticeStatsBase::FLUX) { AlwaysAssert(!stats.getConvertedStatistic (b, t, True), AipsError); } else { AlwaysAssert(stats.getConvertedStatistic (b, t, True), AipsError); } if (hasResult(i)) { AlwaysAssert(b.shape()==IPosition(1,1),AipsError); AlwaysAssert(near(b(pos),results(i),tol), AipsError); } } } { IPosition minPos, maxPos; AlwaysAssert(stats.getMinMaxPos(minPos, maxPos), AipsError); AlwaysAssert(minPos.nelements()==1, AipsError); AlwaysAssert(minPos(0)==0, AipsError); AlwaysAssert(maxPos(0)=shape(0)-1, AipsError); } { Float dMin, dMax; AlwaysAssert(stats.getFullMinMax (dMin, dMax), AipsError); AlwaysAssert(near(results(LatticeStatsBase::MIN),dMin,tol), AipsError); AlwaysAssert(near(results(LatticeStatsBase::MAX),dMax,tol), AipsError); } } void test2DFloat (LatticeStatistics& stats, const Vector& results, const Vector& hasResult, const IPosition& shape) { AlwaysAssert(shape.nelements()==2,AipsError); const Vector dA = stats.displayAxes(); AlwaysAssert(dA.nelements()==1, AipsError); AlwaysAssert(dA(0)==1, AipsError); const uInt nY = shape(1); // typedef NumericTraits::PrecisionType AccumType; Double tol = 1.0e-6; // { IPosition pos(2,0,0); Vector data; AlwaysAssert(stats.getStats(data, pos, True), AipsError); AlwaysAssert(data.shape()==IPosition(1,LatticeStatsBase::NSTATS),AipsError); } // Check stats correct for each row { const Int nStats = LatticeStatsBase::NSTATS; for (Int i=0; i a; LatticeStatsBase::StatisticsTypes t = static_cast(i); IPosition pos(1,0); // if (t==LatticeStatsBase::FLUX) { AlwaysAssert(!stats.getStatistic (a, t, True), AipsError); } else { AlwaysAssert(stats.getStatistic (a, t, True), AipsError); } if (hasResult(i)) { AlwaysAssert(a.shape()==IPosition(1,nY),AipsError); for (uInt j=0; j b; if (t==LatticeStatsBase::FLUX) { AlwaysAssert(!stats.getConvertedStatistic (b, t, True), AipsError); } else { AlwaysAssert(stats.getConvertedStatistic (b, t, True), AipsError); } if (hasResult(i)) { AlwaysAssert(b.shape()==IPosition(1,nY),AipsError); for (uInt j=0; j #include #include #include int main() { try { // Array array1(IPosition(3,1024, 1024, 1024)); Array array1(IPosition(3,512, 512, 512)); ArrayLattice lat(array1); LatticeStatsDataProvider dataProvider(lat); dataProvider.reset(); while (! dataProvider.atEnd()) { ++dataProvider; cout << dataProvider.getCount() << endl; } } catch (const std::exception& x) { cerr << x.what() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/lattices/LatticeMath/test/tLatticeTwoPtCorr.cc000066400000000000000000000110201476623553700242100ustar00rootroot00000000000000//# tLatticeTwoPtCorr.cc: Test program for class LatticeTwoPtCorr //# Copyright (C) 1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main () { try { // Make Lattice uInt nx = 4; uInt ny = 8; uInt nz = 16; IPosition shape(3, nx, ny, nz); Array tArr(shape); indgen (tArr); // TiledShape tShapeIn(shape); TempLattice latIn(tShapeIn); latIn.put(tArr); // AxesSpecifier spec; SubLattice mLatIn(latIn, False, spec); // Make Structure Functions. No validation of output values // just make sure it runs. // x-y plane { cerr << "XY plane" << endl; IPosition axes(2, 0, 1); IPosition shapeOut = LatticeTwoPtCorr::setUpShape (shape, axes); cerr << "Shape in, out = " << shape << shapeOut << endl; TiledShape tShapeOut(shapeOut); TempLattice latOut(tShapeOut); SubLattice mLatOut(latOut, True, spec); // LatticeTwoPtCorr twoPt; twoPt.autoCorrelation (mLatOut, mLatIn, axes, LatticeTwoPtCorr::STRUCTUREFUNCTION, False); } // x-z plane { cerr << "XZ plane" << endl; IPosition axes(2, 0, 2); IPosition shapeOut = LatticeTwoPtCorr::setUpShape (shape, axes); cerr << "Shape in, out = " << shape << shapeOut << endl; TiledShape tShapeOut(shapeOut); TempLattice latOut(tShapeOut); SubLattice mLatOut(latOut, True, spec); // LatticeTwoPtCorr twoPt; twoPt.autoCorrelation (mLatOut, mLatIn, axes, LatticeTwoPtCorr::STRUCTUREFUNCTION, False); } // y-z plane { cerr << "YZ plane" << endl; IPosition axes(2, 1, 2); IPosition shapeOut = LatticeTwoPtCorr::setUpShape (shape, axes); cerr << "Shape in, out = " << shape << shapeOut << endl; TiledShape tShapeOut(shapeOut); TempLattice latOut(tShapeOut); SubLattice mLatOut(latOut, True, spec); // LatticeTwoPtCorr twoPt; twoPt.autoCorrelation (mLatOut, mLatIn, axes, LatticeTwoPtCorr::STRUCTUREFUNCTION, False); } // Copy Constructor { LatticeTwoPtCorr t; LatticeTwoPtCorr t2(t); } // Assignment { LatticeTwoPtCorr t; LatticeTwoPtCorr t2; t = t2; } } catch (std::exception& x) { cerr << "Caught exception: " << x.what() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/lattices/Lattices.h000066400000000000000000000466351476623553700170730ustar00rootroot00000000000000//# Lattices.h: Regular N-dimensional data structures. //# Copyright (C) 1996,1997,1998,1999,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICES_H #define LATTICES_LATTICES_H //#include //#include //#include //#include //#include //#include //#include //#include //#include //#include //#include //#include //#include //#include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // // Regular N-dimensional data structures. // // //
      • Programmers of new Lattice classes should understand Inheritance //
      • Users of the Lattice classes should understand Polymorphism. //
      • class IPosition //
      • class Array // // // // // Lattice: "A regular, periodic configuration of points, particles, or // objects, throughout an area of a space..." (American Heritage Directory) // This definition matches our own: an N-dimensional arrangement of data // on regular orthogonal axes. //

        // In Casacore, we have used the ability to call many things by one generic // name (Lattice) to create a number of classes which have different storage // techniques (e.g. core memory, disk, etc...). The name Lattice should // make the user think of a class interface (or member functions) which all // Lattice objects have in common. If functions require a Lattice // argument, the classes described here may be used interchangeably, even // though their actual internal workings are very different. // // // The Lattice module may be broken up into a few areas: //

          // //
        1. Lattices - the actual holders of lattice-like data which all share a // common interface. The following items // are all Lattices and may be used polymorphically wherever a Lattice is // called for. //
            //
          • The ArrayLattice class adds // the interface requirements of a Lattice to a Casacore // Array. The data inside an ArrayLattice // are not stored on disk. This n-dimensional array class is the simplest // of the Lattices. Users construct the ArrayLattice with an argument // which is either an IPosition which describes the array shape or a // previously instantiated Array object that may already contain data. In // the former case, some Lattice operation must be done to fill the data. // The ArrayLattice, like all Lattices, may be iterated through with a // LatticeIterator (see below). //
            Iteration can also be done using // LatticeApply and some helper // classes. It makes it possible to concentrate on the algorithm. // // // Make an Array of shape 3x4x5 // // Array simpleArray(IPosition(3,3,4,5)); // // // fill it with a gradient // // for (Int k=0; k<5; k++) // for (Int j=0; j<4; j++) // for (Int i=0; i<3; i++) // simpleArray(IPosition(3,i,j,k)) = i+j+k; // // // use the array to create an ArrayLattice. // // ArrayLattice lattice(simpleArray); // // //
          • The PagedArray class stores its // data on disk in the Table format // and pages it into random access memory for use. Paging is // used here to describe the process of getting pieces of data small // enough to fit into active memory even if the whole data set is much too // large. This class "feels" like an array but may hold very large amounts // of data. The paging has an added effect: all the data may be made // persistent, so it stays around after the application ends. // When you use PagedArrays - use // them because you need persistent data and/or paging into large data sets. //
            // The persistence is done using a Table, // and uses the tiled storage // manager. This means that accessing the data along any axis is // equally efficient (depending on the tile shape used). //
            // A PagedArray constructor allows previously created PagedArrays to be // recalled from disk. Much of the time, the PagedArray will be // constructed with a TiledShape // argument which describes the array and tile shape // and a Table argument for use as the place of storage. Then the // PagedArray may be filled using any of the access functions of Lattices // (like the LatticeIterator.) // // // // Create a PagedArray from a Table already existing on disk. // // PagedArray lattice(fileName); // // // Create a LatticeIterator to access the Lattice in optimal tile // // shaped chunks. // // LatticeIterator iter(lattice); // // // Iterate through and do something simple; here we just // // sum up all the values in the Lattice // // Float dSum = 0; // for(iter.reset(); !iter.atEnd(); iter++) { // dSum += sum(iter.cursor()); // } // // //
          • The HDF5Lattice class stores its // data on disk in HDF5 format. // It works in the same way as PagedArray. // //
          // //
        2. LatticeIterator - the // object which allows iteration through any Lattice's data. This comes in // two types: the RO_LatticeIterator which should be used if you // are not going to change the Lattice's data, and the // LatticeIterator if you need to change the data in the Lattice. //
          Note that iteration can also be done using // LatticeApply and some helper // classes. It makes it possible to concentrate on the algorithm. //
            //
          • The RO_LatticeIterator // class name reflects its role as a means of iterating a "Read-Only" array // (hereafter refered to as a "cursor") through a Lattice based object, // from beginning to end. Think of a window into the Lattice that moves to // a new location when requested. The Lattice doesn't change but you may // see all or part of its data as the cursor "window" moves around. This // class allows optimized read-only iteration through any instance of a // class derived from Lattice. The cursor's shape is defined by the user and // moved through the Lattice in an orderly fashion also defined by the user. // Since the cursor is "read-only" it can only be used to "get" the data // out of the Lattice. RO_LatticeIterators are constructed with the Lattice // to be iterated as the first argument. The optional second constructor // argument is either an IPosition which defines the shape of the cursor // or a LatticeNavigator argument. // The IPosition argument cause the iterator // to move the cursor in a simple pattern; the cursor starts at the Lattice's // origin and moves in the direction of the x-axis, then the y-axis, then // the z-axis, etc.. If a LatticeNavigator argument is given, more // control over the cursor shape and path are available. If no second // argument is given, the optimal // TileStepper navigator will be used. // // // simple route - define a cursor shape that is the xy plane of our // lattice. // // IPosition cursorShape(2, lattice.shape()(0), lattice.shape()(1)); // LatticeIterator iter(lattice, cursorShape); // for (iter.reset(); !iter.atEnd(); iter++) { // minMax(iter.cursor(), min, max); // } // // //
          • The LatticeIterator class // name reflects its role as a means of iterating a read and write cursor // through a Lattice based object. Not only does the cursor allow you to // inspect the Lattice data but you may also change the Lattice via // operations on the cursor. This class provides optimized read and write // iteration through any class derived from Lattice. The technique is // identical to the RO_LatticeIterator. But the cursor, in this case, is // a reference back to the data in the Lattice. This means that changes // made to the cursor propagate back to the Lattice. This is especially // useful for the PagedArray and PagedImage classes. These two classes // are constructed empty and need iteration to fill in the Lattice data. // // // make an empty PagedArray and fill it. The Table that stores the // // PagedArray is deleted when the PagedArray goes out of scope // // PagedArray lattice(IPosition(4,100,200,300,50)); // LatticeIterator iter(lattice, IPosition(2, 100, 200)); // // // fill each plane with the "distance" of the iterator from the origin // // for(iter.reset();!iter.atEnd(); iter++) { // iter.woCursor() = iter.nsteps(); // } // //
          // //
        3. LatticeNavigators - the objects which define the method and path used // by a LatticeIterator to move the cursor through a Lattice. Many // different paths are possible. We leave it you to choose the // LatticeNavigator // (method and path) when using a LatticeIterator. //
            //
          • The LatticeStepper class // is used to define the steps which the cursor takes during its path // through the Lattice. Every element of the Lattice will be covered, // starting at the origin and ending at the "top right corner." This // class provides the information needed by a LatticeIterator to do // non-standard movements of the cursor during iteration. The shape of // the cursor is specified by the second IPosition argument of the // LatticeStepper. The order of the axis is important. An IPosition(1,5) // is a five element vector along the x-axis. An IPosition(3,1,1,5) is a // five element vector along the z-axis. The degenerate axes (axes with // lengths of one) act as place holders. The third argument in the // LatticeStepper constructor is the "orientation" IPosition. This // describes the order of the axis for the cursor to follow. Again, we // treat the elements, in order, of the IPosition as the designators of // the appropriate axis. The zeroth element indicates which axis is the // fastest moving, the first element indicates which axis is the second // fastest moving etc. eg. The IPosition(3,2,0,1) says the LatticeIterator // should start with the z-axis, next follow the x-axis, and finish with // the y-axis. A single element cursor would thus move through a cube of // dimension(x,y,z) from (0,0,0) up the z-axis until reaching the maximum // (0,0,z-1) and then start on (1,0,0) and move to (1,0,z-1), etc. // // // The shape of our Lattice - a 4 dimensional image of shape (x,y,z,t) - // // and the shape of the cursor // // IPosition latticeShape(image.shape()); // IPosition cursorShape(3, lattticeShape(0), 1, latticeShape(2)); // // // Define the path the cursor should follow, we list x and z first, even though // // no iterations will be done along those axes since the cursor is an // // integral subshape of the Lattice. The cursor will move along the y-axis // // and then increment the t-axis. The construct the Navigator and Iterator // // IPosition order(4,0,2,1,3); // LatticeStepper nav(latticeShape, cursorShape, order); // LatticeIterator iter(image, nav); // // //
          • // The TiledLineStepper class // allows you to iterate through a Lattice with a Vector cursor. // However, it steps through the Lattice in an order which is // optimum with regard to the I/O of the tiles with which the Lattice is // constructed. // // // // // Set up a TiledLineStepper to return profiles along the specified // // axis from a PagedArray (not all Lattices have the tileShape member // // function). Then create the iterator as well. // // TiledLineStepper nav(lattice.shape(), lattice.tileShape(), axis); // LatticeIterator nav(lattice, nav); // // //
          • // The TileStepper class // allows you to iterate through a Lattice in the optimum way. // It steps through the lattice tile by tile minimizing I/O and memory usage. // It is very well suited for pixel based operations. // However, its iteration order is such that it cannot be used for // a certain subset of pixels (e.g. a vector) is needed. //
            This navigator is the default when no navigator is given when // constructing a (RO_)LatticeIterator. // //
          // //
        4. MaskedLattice - a // Lattice with a mask. It is an abstract base class for // various types of MaskedLattices. A MaskedLattice does not need // to contain a mask (see e.g. SubLattice below), although the user // can always ask for the mask. The function isMasked() // tells if there is really a mask. If not, users could take // advantage by shortcutting some code for better performance. // I.e. a function can test if a the MaskedLattice is really masked // and can take a special route if not. // Of course, doing that requires more coding, so it should only // be done where performance is a real issue. //
            //
          • A SubLattice represents // a rectangular subset of a Lattice. The SubLattice can be a simple // box, but it can also be a circle, polygon, etc. // In the latter case the SubLattice contains a mask // telling which pixels in the bounding box actually belong to the // circle or polygon. In the case of a box there is no mask, because // there is no need to (because a box is already rectangular). //
            A SubLattice can be constructed from any Lattice and a // LatticeRegion telling which // part to take from the Lattice. // If the SubLattice is constructed from a const Lattice, // the SubLattice is not writable. Otherwise it is writable if the // lattice is writable. //

            // There is a rich variety of region // classes which can be used to define a LatticeRegion in pixel coordinates. // They are described in module // LRegions. // //

          • Module LEL contains classes to // form a mathematical expression of lattices. All standard operators, regions, // and many, many functions // can be used in an expression. //
          // //
        5. LatticeLocker // can be used to acquire a (user) lock on a lattice. // The lock can be a read or write lock. // The destructor releases the lock when needed. //
          Lattices on disk can be used (read and write) by multiple processes. // The Table locking/synchronization mechanism takes care that sharing // such a lattice is done in an orderly way. // Usually the default locking mechanism is sufficient. // LatticeLocker is useful when finer locking control is needed for a // disk-based lattice. // // The following are listed for low-level programmers. // Lattice users need not understand them. The Lattice directory // contains several files relevant only to implementation. // //
            //
          • LatticeBase - a non-templated // abstract base class defining the type-independent interface to classes // which must act as Lattices do. //
          • Lattice - a templated // abstract base class (derived from LatticeBase) // defining the interface to classes which must act as Lattices do. // The user simply publicly inherits from Lattice and defines the member // functions declared as pure abstract in the Lattice header file. //
          • The LatticeNavigator // class name defines the interface used for navigating through a Lattice // by iteration. This class is an abstract base. Classes derived from // this (currently // LatticeStepper, // TiledLineStepper, and // TileStepper) must // define the path the iterator cursor follows, the size of the movement // of the cursor with each iteration, and the behaviour of that cursor // shape as it moves through a Lattice. //
          • LatticeIndexer - this // class contains the currently defined Lattice and sub-Lattice shape. It // is used only by navigator classes as it contains // member functions for moving a cursor through a defined sub-Lattice. //
          • The // LatticeIterInterface // class defines the interface for a specific Lattice's iterator. This // class is a base class with a default iterator implementation. // Lattice based classes may need to derive an iterator from // LatticeIterInterface to optimize for the LatticeIterator // internals which impact upon the new Lattice. //
          • PagedArrIter - this class is // the PagedArray's optimized method of iterating. This class is a // "letter" utilized within the LatticeIterator "envelope" and cannot // be instantiated by any user. //
          • LCRegion - this class is the // (abstract) base class for regions in pixel coordinates. //
          //
        // // // Lattices allow the various holders of data to assume a general method // of treatment; by making interfaces in terms of the Lattice class, // the programmer can polymorphically operate on objects derived from the // Lattice class. // // //
      • Make MaskedIterator class? // // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/Lattices/000077500000000000000000000000001476623553700167045ustar00rootroot00000000000000casacore-3.7.1/lattices/Lattices/ArrayLattice.h000066400000000000000000000210531476623553700214420ustar00rootroot00000000000000//# ArrayLattice: Object which converts an Array to a Lattice. //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_ARRAYLATTICE_H #define LATTICES_ARRAYLATTICE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A memory resident Lattice // // // // // //
      • Lattice //
      • Array // // // The ArrayLattice name reflects its role as a Lattice interface to an Array // object. // // // An ArrayLattice is a concrete Lattice class where the data is stored in // memory as opposed to the PagedArray class // where the data is stored on disk. As a result this class is much more // suitable to problems which require small Lattices that can fit into the // memory of a computer. // // ArrayLattice imposes another layer of function calls on top of a an // Array. As a result they should not be used for generic Array // manipulation. They are useful if you have an Array that needs to use // Lattice functions or needs to be used with PagedArrays or other Lattice // derivatives (like LatticeExpr or // SubLattice). // For example the LatticeIterator class can iterate through an Array in // more ways than any of the ArrayIterator classes can. The examples below // illustrate some uses for ArrayLattices. // // // All the examples in this section are available in // dArrayLattice.cc // //

        Example 1:

        // In this example an Array of data is converted into an ArrayLattice so that // the copyData function can be used to write the data to a PagedArray which // will be stored on disk. // // // make an Array and fill it with data. // Array myArray(IPosition(3, 64, 64, 2)); // indgen(myArray); // fills the Array with 0,1,2,....,64*64*2-1 // // construct the ArrayLattice // ArrayLattice myLattice(myArray); // // make a PagedArray to store the data on disk // PagedArray myPagedArray(myLattice.shape(), "myTestData.array"); // // now copy the data onto disk // myPagedArray.copyData (myLattice); // // Note that it could be done in a somewhat simpler way as: // // // make an Array and fill it with data. // Array myArray(IPosition(3, 64, 64, 2)); // indgen(myArray); // fills the Array with 0,1,2,....,64*64*2-1 // // make a PagedArray to store the data on disk // PagedArray myPagedArray(myLattice.shape(), "myTestData.array"); // // now put the data onto disk // myPagedArray.put (myArray); // // //

        Example 2:

        // The ArrayIterator class (or its // derivatives the VectorIterator and the // MatrixIterator classes) do not allow // the user to specify a cursor shape. In this example a Cube class will be // converted into an ArrayLattice so that an ArrLatticeIter can be used to // access the data spectrum by spectrum (assuming the z-axis is frequency). // // // Cube arr(64,64,128); // // assume that the data gets put into the cube somehow // // now construct an ArrayLattice from this cube. // ArrayLattice lat(arr); // // Construct an iterator that returns the 128-element spectra one at a time // ArrLatticeIter iter(lat, IPosition(3,1,1,128)); // // construct a Matrix to hold the results // Matrix channelSum(64,64); // // and do the summation one spectrum at a time // for (iter.reset(); !iter.atEnd(); iter++) // channelSum(iter.position().getFirst(2)) = sum(iter.cursor()); // // // There are more examples in the Lattice class // and many of the examples in the // PagedArray class will also be instructive. //
        // // We needed a way of creating Lattices but with Casacore Array characteristics. // //# //# // // ArrayLattice - a memory based Lattice. // template class ArrayLattice : public Lattice { //# Make members of parent class known. public: using Lattice::ndim; public: // The default constructor creates a ArrayLattice that is useless for just // about everything, except that it can be assigned to with the assignment // operator. ArrayLattice(); // Construct an ArrayLattice with the specified shape. // It results in a writable lattice. explicit ArrayLattice (const IPosition& shape); // Construct an ArrayLattice that references the given Array. // By default it results in a writable lattice. ArrayLattice (Array& array, Bool isWritable = True); // Construct an ArrayLattice that references the given Array. // It results in a non-writable lattice. ArrayLattice (const Array& array); // The copy constructor uses reference semantics. ArrayLattice (const ArrayLattice& other); virtual ~ArrayLattice(); // The assignment operator uses copy semantics. ArrayLattice& operator= (const ArrayLattice& other); // Make a copy of the object (reference semantics). virtual Lattice* clone() const; // The lattice data can be referenced as an array section. virtual Bool canReferenceArray() const; // Is the lattice writable? virtual Bool isWritable() const; // returns the shape of the ArrayLattice. virtual IPosition shape() const; // Set all of the elements in the Lattice to a value. virtual void set (const T& value); // Return the Array of the data within this Lattice. // Array& asArray(); const Array& asArray() const; // // Return the value of the single element located at the argument // IPosition. // Note that operator() (defined in the base class) can also be used. virtual T getAt (const IPosition& where) const; // Put the value of a single element. virtual void putAt (const T& value, const IPosition& where); // Check for internal consistency. Returns False if // something nasty has happened to the ArrayLattice. virtual Bool ok() const; // Returns the maximum recommended number of pixels for a cursor. // For this class this is equal to the number of pixels in the lattice. virtual uInt advisedMaxPixels() const; // Get a slice in an optimized way (specifically for ArrLatticeIter). // It returns in buffer a reference to the lattice array. void getIterSlice (Array& buffer, const IPosition& start, const IPosition& end, const IPosition& incr); protected: // Do the actual getting of an array of values. virtual Bool doGetSlice (Array& buffer, const Slicer& section); // Do the actual putting of an array of values. virtual void doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride); private: Array itsData; Bool itsWritable; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/Lattices/ArrayLattice.tcc000066400000000000000000000122141476623553700217630ustar00rootroot00000000000000//# ArrayLattice.cc: this defines the Lattice wrapper class for Arrays. //# Copyright (C) 1995,1997,1998,1999,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_ARRAYLATTICE_TCC #define LATTICES_ARRAYLATTICE_TCC #include //#include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template ArrayLattice::ArrayLattice() : itsWritable (False) { } template ArrayLattice::ArrayLattice (const IPosition& shape) : itsData (shape), itsWritable (True) { } template ArrayLattice::ArrayLattice (Array& array, Bool isWritable) : itsData (array), itsWritable (isWritable) { } template ArrayLattice::ArrayLattice (const Array& array) : itsData (array), itsWritable (False) { } template ArrayLattice::ArrayLattice (const ArrayLattice&other) : Lattice(), itsData (other.itsData), itsWritable (other.itsWritable) { } template ArrayLattice::~ArrayLattice() {} template ArrayLattice& ArrayLattice::operator= (const ArrayLattice& other) { if (this != &other) { itsData = other.itsData; itsWritable = other.itsWritable; } return *this; } template Lattice* ArrayLattice::clone() const { return new ArrayLattice (*this); } template Bool ArrayLattice::canReferenceArray() const { return True; } template Bool ArrayLattice::isWritable() const { return itsWritable; } template IPosition ArrayLattice::shape() const { return itsData.shape(); } template Bool ArrayLattice::doGetSlice (Array& buffer, const Slicer& section) { Array tmp = itsData(section.start(), section.end(), section.stride()); buffer.reference (tmp); return True; } template void ArrayLattice::doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride) { if (!itsWritable) { throw (AipsError ("ArrayLattice::putSlice - non-writable lattice")); } const uInt sdim = sourceBuffer.ndim(); const uInt ldim = ndim(); DebugAssert(ldim == where.nelements(), AipsError); DebugAssert(ldim == stride.nelements(), AipsError); if (sdim == ldim) { itsData(where, where + (sourceBuffer.shape()-1)*stride, stride) = sourceBuffer; } else { AlwaysAssert(ldim > sdim, AipsError); Array allAxes(sourceBuffer.addDegenerate(ldim-sdim)); itsData(where, where + (allAxes.shape()-1)*stride, stride) = allAxes; } } template void ArrayLattice::getIterSlice (Array& buffer, const IPosition& start, const IPosition& end, const IPosition& incr) { Array tmp (itsData(start, end, incr)); buffer.reference (tmp); } template void ArrayLattice::set (const T& value) { if (!itsWritable) { throw (AipsError ("ArrayLattice::set - non-writable lattice")); } itsData.set(value); } template T ArrayLattice::getAt (const IPosition& where) const { return itsData(where); } template void ArrayLattice::putAt (const T& value, const IPosition& where) { if (!itsWritable) { throw (AipsError ("ArrayLattice::putAt - non-writable lattice")); } itsData(where) = value; } template uInt ArrayLattice::advisedMaxPixels() const { return itsData.nelements(); } template Array& ArrayLattice::asArray() { if (!itsWritable) { throw (AipsError ("ArrayLattice::asArray - non-writable lattice")); } return itsData; } template const Array& ArrayLattice::asArray() const { return itsData; } // Check class invariants. template Bool ArrayLattice::ok() const { return itsData.ok(); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/Lattices/CurvedLattice2D.h000066400000000000000000000164551476623553700220140ustar00rootroot00000000000000//# CurvedLattice2D.h: A lattice crosscut based on a curve in a plane //# Copyright (C) 2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have receied a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_CURVEDLATTICE2D_H #define LATTICES_CURVEDLATTICE2D_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // A lattice crosscut based on a curve in a plane. // // // // // //# Classes you should understand before using this one. //
      • PixelCurve1D //
      • CLInterpolator2D // // // Class CurvedImage2D can be used to make a crosscut through an image // with a dimensionality >= 2. // The crosscut is based on a curve defined by a // PixelCurve1D object. The curve // can be any 1-dim function (e.g. straight line, spline) // supported by the Functionals module. The curve must be in one of the // main planes of the image as defined by the axes arguments in the // constructor. //
        See class CurvedImage2D for // a more detailed description. //
        // // See example in CurvedImage2D. // // // Users like to view arbitrary image crosscuts. // template class CurvedLattice2D: public MaskedLattice { public: // Default constructor CurvedLattice2D(); // Take a curved slice from the given MaskedLattice. For example, define // a spline in the RA-DEC plane and extend it in the FREQ direction. // The result is a 2D lattice with axes FREQ and 'spline'. //
        // The PixelCurve1D object defines // the curve in one of the planes of the lattice. The arguments axis1 // and axis2 define the plane the curve is in. // The CLInterpolator2D object // defines the interpolation scheme for pixels that are not on grid points. // An example is CLIPNearest2D which takes the nearest neighbour. // The dimensionality of the CurvedLattice2D is one less than the // dimensionality of the given lattice. Two axes (axis1 and axis2) are // replaced by the new axis representing the curve. The argument // curveAxis defines the axis number of the new axis. It defaults to the // last axis. // An exception is thrown if the dimensionality of the input lattice is < 2 // or if the given axes numbers are too high. CurvedLattice2D (const MaskedLattice&, const CLInterpolator2D&, const PixelCurve1D&, uInt axis1, uInt axis2, Int curveAxis=-1); // Copy constructor (reference semantics) CurvedLattice2D(const CurvedLattice2D& other); // Destructor, does nothing virtual ~CurvedLattice2D(); // Assignment (reference semantics) CurvedLattice2D& operator=(const CurvedLattice2D& other); // Make a copy of the object (reference semantics). virtual MaskedLattice* cloneML() const; // Is the lattice masked? // It is if its parent lattice is masked. virtual Bool isMasked() const; // Is the lattice paged to disk? virtual Bool isPaged() const; // The lattice is not writable. virtual Bool isWritable() const; // Handle ocking of the lattice which is delegated to its parent. //
        It is strongly recommended to use class // LatticeLocker to // handle lattice locking. It also contains a more detailed // explanation of the locking process. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; // // Resynchronize the Lattice object with the lattice file. // This function is only useful if no read-locking is used, ie. // if the table lock option is UserNoReadLocking or AutoNoReadLocking. // In that cases the table system does not acquire a read-lock, thus // does not synchronize itself automatically. virtual void resync(); // Flush the data. virtual void flush(); // Close the Lattice temporarily (if it is paged to disk). // It'll be reopened automatically when needed or when // reopen is called explicitly. virtual void tempClose(); // If needed, reopen a temporarily closed Lattice. virtual void reopen(); // Get a pointer the region/mask object. // It returns 0. virtual const LatticeRegion* getRegionPtr() const; // Returns the shape of the lattice. virtual IPosition shape() const; // Return the name of the parent lattice. virtual String name (Bool stripPath=False) const; // This function returns the recommended maximum number of pixels to // include in the cursor of an iterator. virtual uInt advisedMaxPixels() const; // Check class internals - used for debugging. Should always return True virtual Bool ok() const; // Do the actual getting of an array of values. virtual Bool doGetSlice (Array& buffer, const Slicer& section); // Do the actual getting of an array of values. virtual void doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride); // Get a section of the mask. virtual Bool doGetMaskSlice (Array& buffer, const Slicer& section); // Get the best cursor shape. virtual IPosition doNiceCursorShape (uInt maxPixels) const; private: // Make the AxesMapping object to map input to output axes. void makeMapping (uInt axis1, uInt axis2, Int curveAxis); MaskedLattice* itsLatticePtr; CLInterpolator2D* itsInterpolator; PixelCurve1D itsCurve; uInt itsAxis1; uInt itsAxis2; uInt itsCurveAxis; AxesMapping itsAxesMap; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/Lattices/CurvedLattice2D.tcc000066400000000000000000000165551476623553700223370ustar00rootroot00000000000000//# CurvedLattice2D.cc: A lattice crosscut based on a curve in a plane //# Copyright (C) 2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_CURVEDLATTICE2D_TCC #define LATTICES_CURVEDLATTICE2D_TCC #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template CurvedLattice2D::CurvedLattice2D() : itsLatticePtr (0), itsInterpolator (0), itsAxis1 (0), itsAxis2 (0), itsCurveAxis (0) {} template CurvedLattice2D::CurvedLattice2D (const MaskedLattice& lattice, const CLInterpolator2D& interp, const PixelCurve1D& curve, uInt axis1, uInt axis2, Int curveAxis) : itsLatticePtr (lattice.cloneML()), itsInterpolator (interp.clone()), itsCurve (curve) { if (lattice.ndim() < 2) { throw AipsError ("CurvedLattice2D: input lattice " + lattice.name() + " must have more than 1 dimension"); } makeMapping (axis1, axis2, curveAxis); itsInterpolator->set (itsLatticePtr, itsAxesMap, itsAxis1, itsAxis2, itsCurveAxis); } template CurvedLattice2D::CurvedLattice2D (const CurvedLattice2D& other) : MaskedLattice(), itsLatticePtr (0), itsInterpolator (0) { operator= (other); } template CurvedLattice2D::~CurvedLattice2D() { delete itsLatticePtr; delete itsInterpolator; } template CurvedLattice2D& CurvedLattice2D::operator= (const CurvedLattice2D& other) { if (this != &other) { delete itsLatticePtr; itsLatticePtr = other.itsLatticePtr->cloneML(); delete itsInterpolator; itsInterpolator = other.itsInterpolator->clone(); itsCurve = other.itsCurve; itsAxis1 = other.itsAxis1; itsAxis2 = other.itsAxis2; itsCurveAxis = other.itsCurveAxis; itsAxesMap = other.itsAxesMap; } return *this; } template MaskedLattice* CurvedLattice2D::cloneML() const { return new CurvedLattice2D (*this); } template void CurvedLattice2D::makeMapping (uInt axis1, uInt axis2, Int curveAxis) { uInt ndim = itsLatticePtr->ndim(); if (axis1 >= ndim || axis2 >= ndim || axis1 == axis2) { throw AipsError ("CurvedLattice2D - invalid axis1 or axis2 given"); } itsAxis1 = axis1; itsAxis2 = axis2; if (curveAxis < 0) { itsCurveAxis = ndim - 2; // last output axis } else { itsCurveAxis = curveAxis; } if (itsCurveAxis >= ndim-1) { throw AipsError ("CurvedLattice2D - invalid curveAxis given"); } IPosition old2new(ndim, -1); uInt nr=0; for (uInt i=0; i Bool CurvedLattice2D::isMasked() const { return itsLatticePtr->isMasked(); } template Bool CurvedLattice2D::isPaged() const { return itsLatticePtr->isPaged(); } template Bool CurvedLattice2D::isWritable() const { return False; } template Bool CurvedLattice2D::lock (FileLocker::LockType type, uInt nattempts) { return itsLatticePtr->lock (type, nattempts); } template void CurvedLattice2D::unlock() { itsLatticePtr->unlock(); } template Bool CurvedLattice2D::hasLock (FileLocker::LockType type) const { return itsLatticePtr->hasLock (type); } template void CurvedLattice2D::resync() { itsLatticePtr->resync(); } template void CurvedLattice2D::flush() { itsLatticePtr->flush(); } template void CurvedLattice2D::tempClose() { itsLatticePtr->tempClose(); } template void CurvedLattice2D::reopen() { itsLatticePtr->reopen(); } template const LatticeRegion* CurvedLattice2D::getRegionPtr() const { return 0; } template IPosition CurvedLattice2D::shape() const { IPosition shp (itsLatticePtr->shape()); shp(itsAxis1) = itsCurve.npoints(); shp[itsAxis2] = 1; return itsAxesMap.shapeToNew (shp); } template String CurvedLattice2D::name (Bool stripPath) const { return itsLatticePtr->name(stripPath); } template Bool CurvedLattice2D::doGetSlice (Array& buffer, const Slicer& section) { // Convert the curve pixel numbers to lattice pixel numbers. Vector x,y; itsCurve.getPixelCoord (x, y, section.start()[itsCurveAxis], section.end()[itsCurveAxis], section.stride()[itsCurveAxis]); // Let the interpolator get all pixels for the given section. buffer.resize (section.length()); itsInterpolator->getData (buffer, x, y, section); return False; } template void CurvedLattice2D::doPutSlice (const Array&, const IPosition&, const IPosition&) { throw (AipsError ("CurvedLattice2D::putSlice - non-writable lattice")); } template uInt CurvedLattice2D::advisedMaxPixels() const { return itsLatticePtr->advisedMaxPixels(); } template IPosition CurvedLattice2D::doNiceCursorShape (uInt maxPixels) const { IPosition cursorShape (itsLatticePtr->niceCursorShape (maxPixels)); cursorShape[itsAxis1] = 1; cursorShape[itsAxis2] = 1; return itsAxesMap.shapeToNew (cursorShape); } template Bool CurvedLattice2D::doGetMaskSlice (Array& buffer, const Slicer& section) { buffer.resize (section.length()); // Evaluate only if masked. if (itsLatticePtr->isMasked()) { // Convert the curve pixel numbers to lattice pixel numbers. Vector x,y; itsCurve.getPixelCoord (x, y, section.start()[itsCurveAxis], section.end()[itsCurveAxis], section.stride()[itsCurveAxis]); // Let the interpolator get all mask pixels for the given section. itsInterpolator->getMask (buffer, x, y, section); } else { // Not masked, so we can simply fill the buffer with True values. buffer = True; } return False; } template Bool CurvedLattice2D::ok() const { return itsLatticePtr->ok(); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/Lattices/ExtendLattice.h000066400000000000000000000163521476623553700216210ustar00rootroot00000000000000//# ExtendLattice.h: A subset of a Lattice or MaskedLattice //# Copyright (C) 2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_EXTENDLATTICE_H #define LATTICES_EXTENDLATTICE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // An extension of a Lattice or MaskedLattice // // // // // //
      • Lattice //
      • LatticeRegion // // // An ExtendLattice is a lattice virtually extending another lattice // by stretching axes with length 1 and/or by adding new axes. // It is useful for e.g. LEL to have the same shapes for lattices. // An ExtendLattice is not writable (since many pixels map to the same // underlying pixel). // // // // // // //
      • Any type that can be used by the Tables System can also be used by // this class. // //# //# template class ExtendLattice: public MaskedLattice { public: // The default constructor creates a ExtendLattice that is useless for just // about everything, except that it can be assigned to with the assignment // operator. ExtendLattice(); // Create a ExtendLattice from a Lattice. //
        newShape gives the new shape. //
        newAxes gives the new axes in newShape. //
        stretchAxes gives the stretched axes in newShape. //
        E.g. lattice has shape [32,1,5,1], newShape=[32,1,4,5,10], // newAxes=[2], and stretchAxes=[4]. It means that axes 2 in the newShape // is a new axes and that axes 4 in the new shape is stretched. The other // axes in the new shape have to match the other axes in the old shape. // Note that stretched axes have to have length 1 in the old shape. // ExtendLattice (const Lattice& lattice, const IPosition& newShape, const IPosition& extendAxes, const IPosition& stretchAxes); ExtendLattice (const MaskedLattice& lattice, const IPosition& newShape, const IPosition& newAxes, const IPosition& stretchAxes); // // Copy constructor (reference semantics). ExtendLattice (const ExtendLattice& other); virtual ~ExtendLattice(); // Assignment (reference semantics). ExtendLattice& operator= (const ExtendLattice& other); // Make a copy of the object (reference semantics). virtual MaskedLattice* cloneML() const; // Is the lattice masked? // It is if its parent lattice is masked. virtual Bool isMasked() const; // An ExtendLattice is not persistent. virtual Bool isPersistent() const; // Is the ExtendLattice paged to disk? virtual Bool isPaged() const; // An ExtendLattice is not writable. virtual Bool isWritable() const; // Handle locking of the ExtendLattice which is delegated to its parent. //
        It is strongly recommended to use class // LatticeLocker to // handle lattice locking. It also contains a more detailed // explanation of the locking process. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; // // Resynchronize the Lattice object with the lattice file. // This function is only useful if no read-locking is used, ie. // if the table lock option is UserNoReadLocking or AutoNoReadLocking. // In that cases the table system does not acquire a read-lock, thus // does not synchronize itself automatically. virtual void resync(); // Flush the data. virtual void flush(); // Close the Lattice temporarily (if it is paged to disk). // It'll be reopened automatically when needed or when // reopen is called explicitly. virtual void tempClose(); // If needed, reopen a temporarily closed Lattice. virtual void reopen(); // Does the ExtendLattice have a pixelmask? virtual Bool hasPixelMask() const; // Get access to the pixelmask. // An exception is thrown if the ExtendLattice does not have a pixelmask. // virtual const Lattice& pixelMask() const; virtual Lattice& pixelMask(); // // Get the region used (always returns 0). virtual const LatticeRegion* getRegionPtr() const; // Returns the shape of the ExtendLattice. virtual IPosition shape() const; // Return the name of the parent lattice. virtual String name (Bool stripPath=False) const; // This function returns the recommended maximum number of pixels to // include in the cursor of an iterator. virtual uInt advisedMaxPixels() const; // Check class internals - used for debugging. Should always return True virtual Bool ok() const; // Do the actual getting of an array of values. virtual Bool doGetSlice (Array& buffer, const Slicer& section); // Putting data is not possible. virtual void doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride); // Get a section of the mask. virtual Bool doGetMaskSlice (Array& buffer, const Slicer& section); // Get the best cursor shape. virtual IPosition doNiceCursorShape (uInt maxPixels) const; private: // Set the various pointer needed to construct the object. // One of the pointers should be zero. // It takes over the pointer and deletes the object in the destructor. void setPtr (Lattice* latticePtr, MaskedLattice* maskLatPtr); // Get mask data from mask. Bool getMaskDataSlice (Array& buffer, const Slicer& section); Lattice* itsLatticePtr; MaskedLattice* itsMaskLatPtr; Bool itsHasPixelMask; ExtendLattice* itsPixelMask; ExtendSpecifier itsExtendSpec; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/Lattices/ExtendLattice.tcc000066400000000000000000000222001476623553700221300ustar00rootroot00000000000000//# ExtendLattice.cc: A subset of a Lattice //# Copyright (C) 2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_EXTENDLATTICE_TCC #define LATTICES_EXTENDLATTICE_TCC #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template ExtendLattice::ExtendLattice() : itsLatticePtr (0), itsMaskLatPtr (0), itsHasPixelMask (False), itsPixelMask (0) {} template ExtendLattice::ExtendLattice (const Lattice& lattice, const IPosition& newShape, const IPosition& newAxes, const IPosition& stretchAxes) : itsExtendSpec (lattice.shape(), newShape, newAxes, stretchAxes) { setPtr (lattice.clone(), 0); } template ExtendLattice::ExtendLattice (const MaskedLattice& lattice, const IPosition& newShape, const IPosition& newAxes, const IPosition& stretchAxes) : itsExtendSpec (lattice.shape(), newShape, newAxes, stretchAxes) { setPtr (0, lattice.cloneML()); } template ExtendLattice::ExtendLattice (const ExtendLattice& other) : MaskedLattice(), itsLatticePtr (0), itsMaskLatPtr (0), itsPixelMask (0) { operator= (other); } template ExtendLattice::~ExtendLattice() { // Note that itsMaskLatPtr (if filled in) always points to the same // object as itsLatticePtr, so it does not need to be deleted. delete itsLatticePtr; delete itsPixelMask; } template ExtendLattice& ExtendLattice::operator= (const ExtendLattice& other) { if (this != &other) { delete itsLatticePtr; itsLatticePtr = other.itsLatticePtr; itsMaskLatPtr = other.itsMaskLatPtr; if (itsMaskLatPtr != 0) { itsMaskLatPtr = itsMaskLatPtr->cloneML(); itsLatticePtr = itsMaskLatPtr; } else if (itsLatticePtr != 0) { itsLatticePtr = itsLatticePtr->clone(); } delete itsPixelMask; itsHasPixelMask = other.itsHasPixelMask; itsExtendSpec = other.itsExtendSpec; } return *this; } template MaskedLattice* ExtendLattice::cloneML() const { return new ExtendLattice (*this); } template void ExtendLattice::setPtr (Lattice* latticePtr, MaskedLattice* maskLatPtr) { itsHasPixelMask = False; itsPixelMask = 0; if (maskLatPtr == 0) { itsLatticePtr = latticePtr; itsMaskLatPtr = 0; } else { itsLatticePtr = maskLatPtr; if (! maskLatPtr->isMasked()) { itsMaskLatPtr = 0; } else { itsMaskLatPtr = maskLatPtr; itsHasPixelMask = itsMaskLatPtr->hasPixelMask(); } } } template Bool ExtendLattice::isMasked() const { return (itsMaskLatPtr != 0); } template Bool ExtendLattice::isPersistent() const { return False; } template Bool ExtendLattice::isPaged() const { return itsLatticePtr->isPaged(); } template Bool ExtendLattice::isWritable() const { return False; } template Bool ExtendLattice::lock (FileLocker::LockType type, uInt nattempts) { return itsLatticePtr->lock (type, nattempts); } template void ExtendLattice::unlock() { itsLatticePtr->unlock(); } template Bool ExtendLattice::hasLock (FileLocker::LockType type) const { return itsLatticePtr->hasLock (type); } template void ExtendLattice::resync() { itsLatticePtr->resync(); } template void ExtendLattice::flush() { itsLatticePtr->flush(); } template void ExtendLattice::tempClose() { itsLatticePtr->tempClose(); } template void ExtendLattice::reopen() { itsLatticePtr->reopen(); } template Bool ExtendLattice::hasPixelMask() const { return itsHasPixelMask; } template const Lattice& ExtendLattice::pixelMask() const { return ((const ExtendLattice*)this)->pixelMask(); } template Lattice& ExtendLattice::pixelMask() { if (!itsHasPixelMask) { throw (AipsError ("ExtendLattice::pixelMask - no pixelmask available")); } // Construct the pixelmask (as an extension of the parent pixelmask) // if that is not done yet. if (itsPixelMask == 0) { Lattice& fullMask = itsMaskLatPtr->pixelMask(); itsPixelMask = new ExtendLattice (fullMask, itsExtendSpec.newShape(), itsExtendSpec.newAxes(), itsExtendSpec.stretchAxes()); } return *itsPixelMask; } template const LatticeRegion* ExtendLattice::getRegionPtr() const { return 0; } template IPosition ExtendLattice::shape() const { return itsExtendSpec.newShape(); } template String ExtendLattice::name (Bool stripPath) const { return itsLatticePtr->name(stripPath); } template Bool ExtendLattice::doGetSlice (Array& buffer, const Slicer& section) { IPosition shape; Slicer newSect = itsExtendSpec.convert (shape, section); Array tmpbuf(newSect.length()); itsLatticePtr->doGetSlice (tmpbuf, newSect); // Reform tmpbuf, so it has the same dimensionality as buffer. Array data = tmpbuf.reform (shape); // Now we have to extend tmpbuf along all extend axes. const IPosition& length = section.length(); buffer.resize (length); IPosition pos (buffer.ndim(), 0); IPosition end (buffer.shape() - 1); //# Iterate along the extendAxes through the buffer. const IPosition extendAxes = itsExtendSpec.extendAxes(); uInt nre = extendAxes.nelements(); for (;;) { uInt i; for (i=0; i void ExtendLattice::doPutSlice (const Array&, const IPosition&, const IPosition&) { throw (AipsError ("ExtendLattice::putSlice - non-writable lattice")); } template uInt ExtendLattice::advisedMaxPixels() const { return itsLatticePtr->advisedMaxPixels(); } template IPosition ExtendLattice::doNiceCursorShape (uInt maxPixels) const { IPosition cursorShape (itsLatticePtr->niceCursorShape (maxPixels)); return itsExtendSpec.convertNew (cursorShape); } template Bool ExtendLattice::doGetMaskSlice (Array& buffer, const Slicer& section) { // When lattice has no mask, set mask to True. if (itsMaskLatPtr == 0) { buffer = True; return False; } IPosition shape; Slicer newSect = itsExtendSpec.convert (shape, section); Array tmpbuf(newSect.length()); itsMaskLatPtr->doGetMaskSlice (tmpbuf, newSect); // Reform tmpbuf, so it has the same dimensionality as buffer. Array data = tmpbuf.reform (shape); // Now we have to extend tmpbuf along all extend axes. const IPosition& length = section.length(); buffer.resize (length); IPosition pos (buffer.ndim(), 0); IPosition end (buffer.shape() - 1); //# Iterate along the extendAxes through the buffer. const IPosition extendAxes = itsExtendSpec.extendAxes(); uInt nre = extendAxes.nelements(); for (;;) { uInt i; for (i=0; i Bool ExtendLattice::ok() const { return itsLatticePtr->ok(); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/Lattices/HDF5LattIter.h000066400000000000000000000113321476623553700212140ustar00rootroot00000000000000//# HDF5LattIter.h: a concrete iterator for use with HDF5Lattices. //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_HDF5LATTITER_H #define LATTICES_HDF5LATTITER_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A read/write Lattice iterator for PagedArrays. // // // // // //
      • PagedArray //
      • LatticeIterator //
      • LatticeIterInterface // //
      • letter/envelope schemes, eg. Coplien, "Advanced C++", ch 5.5 // // // The HDF5LattIter class name is a contraction of Paged Array Iterator // and reflects its role as the methods for iterating through Lattices which // are resident on disk. // // // This class is not meant for general use. Instead class // LatticeIterator should be used // to iterate through a PagedArray or any other // Lattice object // (like a ArrayLattice). //

        // HDF5LattIter is derived from LatticeIterInterface and implements // the iterator for a PagedArray // object. This iterator is somewhat special because it sets the // PagedArray cache size at the start of an iteration. // // // For for each derivation of Lattice to make as efficient an iterator as // possible. // The letter/envelope scheme allowed us to hide the special bits in // classes like the one you see here. // // //

      • Restricted to the type of the PagedArray argument in the // constructors // //# //#
      • //# template class HDF5LattIter : public LatticeIterInterface { friend class HDF5Lattice; //# Make members of parent class known. protected: using LatticeIterInterface::rewriteData; using LatticeIterInterface::itsNavPtr; protected: // Construct the Iterator with the supplied data, and iteration strategy HDF5LattIter (const HDF5Lattice& data, const LatticeNavigator& method, Bool useRef); // The copy constructor uses reference sematics for the PagedArray and // copy semantics for the cursor and Navigator. This way the newly // constructed HDF5LattIter can independently iterate through the same // data set. (with the same cursor shape etc.) HDF5LattIter (const HDF5LattIter& other); // Destructor (cleans up dangling references and releases cursor memory) virtual ~HDF5LattIter(); // The assignment operator uses reference sematics for the PagedArray and // copy semantics for the cursor and Navigator. This way the // HDF5LattIter objects share the same data set but independently iterate // with cursors of the same size. HDF5LattIter& operator= (const HDF5LattIter& other); // Clone the object. virtual LatticeIterInterface* clone() const; private: // Setup the cache in the tiled storage manager. void setupTileCache(); // reference to the PagedArray HDF5Lattice itsData; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/Lattices/HDF5LattIter.tcc000066400000000000000000000060151476623553700215400ustar00rootroot00000000000000//# HDF5LattIter.cc: a concrete iterator for use with HDF5Lattices. //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_HDF5LATTITER_TCC #define LATTICES_HDF5LATTITER_TCC #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template HDF5LattIter::HDF5LattIter (const HDF5Lattice& data, const LatticeNavigator& nav, Bool useRef) : LatticeIterInterface (data, nav, useRef), itsData (data) { setupTileCache(); } template HDF5LattIter::HDF5LattIter (const HDF5LattIter& other) : LatticeIterInterface (other), itsData (other.itsData) {} template HDF5LattIter::~HDF5LattIter() { itsData.clearCache(); } template HDF5LattIter& HDF5LattIter::operator= (const HDF5LattIter& other) { if (this != &other) { rewriteData(); itsData.clearCache(); LatticeIterInterface::operator= (other); itsData = other.itsData; } return *this; } template LatticeIterInterface* HDF5LattIter::clone() const { return new HDF5LattIter (*this); } template void HDF5LattIter::setupTileCache() { const IPosition& tileShape = itsData.niceCursorShape(); uInt cacheSize = itsNavPtr->calcCacheSize (itsData.shape(), tileShape, 0, tileShape.product()); itsData.setCacheSizeInTiles (cacheSize); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/Lattices/HDF5Lattice.h000066400000000000000000000253331476623553700210570ustar00rootroot00000000000000//# HDF5Lattice.h: Templated paged array in an HDF5 file //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_HDF5LATTICE_H #define LATTICES_HDF5LATTICE_H //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A Lattice that is read from or written to an HDF5 dataset. // // // // // //
      • PagedArray //
      • TiledShape //
      • HDF5File // // // Astronomical data arrays (like images) have to be persistent. // A Lattice is a templated abstract base class to hold any Casacore array. // The PagedArray class is a Lattice specialization which stores the data // in a Casacore table. //
        // HDF5Lattice ia another Lattice specialization making it possible to store // an array as a dataset in a group in an HDF5 file. //

        // When you construct an HDF5Lattice you do not read any data into // memory. Instead an HDF5 disk file is created, in a place you // specify, to hold the data. This means you need to have enough disk space // to hold the array. Constructing a new HDF5Lattice is equivalent to // creating a data set in an HDF5 file. //

        // To access the data in a HDF5Lattice you can (in order of preference): //

          //
        1. Use a LatticeIterator //
        2. Use the getSlice and putSlice member functions //
        3. Use the parenthesis operator or getAt and putAt functions //
        // Class PagedArray contains some more info and examples. //
        // // Create a HDF5Lattice of Floats of shape [1024,1024,4,256] in a file // called "myData_tmp.array" and initialize it to zero. // // const IPosition arrayShape(4,1024,1024,4,256); // const String filename("myData_tmp.array"); // HDF5Lattice diskArray(arrayShape, filename); // cout << "Created a HDF5Lattice of shape " << diskArray.shape() // << " (" << diskArray.shape().product()/1024/1024*sizeof(Float) // << " MBytes)" << endl // << "in the table called " << diskArray.tableName() << endl; // diskArray.set(0.0f); // // Using the set function is an efficient way to initialize the HDF5Lattice // // as it uses a LatticeIterator internally. Note that the set function is // // defined in the Lattice class that HDF5Lattice is derived from. // // // // There was a need to be able to use HDF5 files to hold image data. // // //
      • HDF5DataSet supports only a limited amount of types. // This restricts the template argument to // the types Bool, Int Float, Double, Complex, and DComplex. // template class HDF5Lattice : public Lattice { //# Make members of parent class known. public: using Lattice::ndim; public: // The default constructor creates an HDF5Lattice that is useless for just // about everything, except that it can be assigned to with the assignment // operator. HDF5Lattice(); // Construct a new HDF5Lattice with the specified shape. // A new HDF5 file with the specified filename is constructed to hold // the array. The file will remain on disk after the HDF5Lattice goes // out of scope or is deleted. // Optionally the name of an HDF5 group can be given to create the array in. // The group is created if not existing yet. HDF5Lattice (const TiledShape& shape, const String& filename, const String& arrayName = "array", const String& groupName = String()); // Construct a temporary HDF5Lattice with the specified shape. // A scratch file is created in the current working directory to hold // the array. This file will be deleted automatically when the HDF5Lattice // goes out of scope or is deleted. explicit HDF5Lattice (const TiledShape& shape); // Construct a new HDF5Lattice, with the specified shape, in the given // HDF5 file. The array gets the given name. // Optionally the name of an HDF5 group can be given to create the array in. // The group is created if not existing yet. HDF5Lattice (const TiledShape& shape, const std::shared_ptr& file, const String& arrayName, const String& groupName = String()); // Reconstruct from a pre-existing HDF5Lattice in the HDF5 file and group // with the given names. explicit HDF5Lattice (const String& fileName, const String& arrayName = "array", const String& groupName = String()); // Reconstruct from a pre-existing HDF5Lattice in the HDF5 file and group // with the given name. explicit HDF5Lattice (const std::shared_ptr& file, const String& arrayName, const String& groupName = String()); // The copy constructor which uses reference semantics. Copying by value // doesn't make sense, because it would require the creation of a // temporary (but possibly huge) file on disk. HDF5Lattice (const HDF5Lattice& other); // The destructor flushes the HDF5Lattice's contents to disk. ~HDF5Lattice(); // The assignment operator with reference semantics. As with the copy // constructor assigning by value does not make sense. HDF5Lattice& operator= (const HDF5Lattice& other); // Make a copy of the object (reference semantics). virtual Lattice* clone() const; // A HDF5Lattice is always persistent. virtual Bool isPersistent() const; // A HDF5Lattice is always paged to disk. virtual Bool isPaged() const; // Is the HDF5Lattice writable? virtual Bool isWritable() const; // Returns the shape of the HDF5Lattice. virtual IPosition shape() const; // Return the current HDF5 file name. // By default this includes the full path. // The path preceeding the file name can be stripped off on request. virtual String name (Bool stripPath=False) const; // Return the current HDF5File object. const std::shared_ptr& file() const { return itsFile; } // Return the current HDF5Group object. const std::shared_ptr& group() const { return itsGroup; } // Returns the current HDF5DataSet object const std::shared_ptr& array() const { return itsDataSet; } // Returns the name of this HDF5Lattice. const String& arrayName() const { return itsDataSet->getName(); } // Returns the current tile shape for this HDF5Lattice. IPosition tileShape() const; // Set the actual cache size for this Array to be big enough for the // indicated number of tiles. This cache is not shared with other // HDF5Lattices, // Tiles are cached using an LRU algorithm. virtual void setCacheSizeInTiles (uInt howManyTiles); // Set the cache size as to "fit" the indicated access pattern. virtual void setCacheSizeFromPath (const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath); // Return the value of the single element located at the argument // IPosition. // Note that Lattice::operator() can also be used. virtual T getAt (const IPosition& where) const; // Put the value of a single element. virtual void putAt (const T& value, const IPosition& where); // A function which checks for internal consistency. Returns False if // something nasty has happened to the HDF5Lattice. In that case // it also throws an exception. virtual Bool ok() const; // This function is used by the LatticeIterator class to generate an // iterator of the correct type for a specified Lattice. Not recommended // for general use. virtual LatticeIterInterface* makeIter (const LatticeNavigator& navigator, Bool useRef) const; // Do the actual getting of an array of values. virtual Bool doGetSlice (Array& buffer, const Slicer& section); // Do the actual getting of an array of values. virtual void doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride); // Returns the maximum recommended number of pixels for a cursor. This is // the number of pixels in a tile. virtual uInt advisedMaxPixels() const; // Get the best cursor shape. virtual IPosition doNiceCursorShape (uInt maxPixels) const; // Flush the data (but do not unlock). virtual void flush(); private: // Make the Array in the HDF5 file and group. void makeArray (const TiledShape& shape, const String& arrayName, const String& groupName); // Open the Array in the HDF5 file and group. void openArray (const String& arrayName, const String& groupName); // Check if the file is writable. void checkWritable() const; std::shared_ptr itsFile; std::shared_ptr itsGroup; std::shared_ptr itsDataSet; IPosition itsTileShape; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/Lattices/HDF5Lattice.tcc000066400000000000000000000240111476623553700213710ustar00rootroot00000000000000//# HDF5Lattice.tcc: this defines the HDF5Lattice class //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or(at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_HDF5LATTICE_TCC #define LATTICES_HDF5LATTICE_TCC #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template HDF5Lattice::HDF5Lattice() {} template HDF5Lattice::HDF5Lattice (const TiledShape& shape, const String& fileName, const String& arrayName, const String& groupName) { itsFile = std::make_shared(fileName, ByteIO::New); makeArray (shape, arrayName, groupName); DebugAssert (ok(), AipsError); } template HDF5Lattice::HDF5Lattice (const TiledShape& shape) { Path fileName = File::newUniqueName(String("./"), String("HDF5Lattice")); itsFile = std::make_shared(fileName.absoluteName(), ByteIO::Scratch); makeArray (shape, "array", String()); DebugAssert (ok(), AipsError); } template HDF5Lattice::HDF5Lattice (const TiledShape& shape, const std::shared_ptr& file, const String& arrayName, const String& groupName) : itsFile (file) { makeArray (shape, arrayName, groupName); DebugAssert (ok(), AipsError); } template HDF5Lattice::HDF5Lattice (const String& fileName, const String& arrayName, const String& groupName) { // Open for write if possible. if (File(fileName).isWritable()) { itsFile = std::make_shared(fileName, ByteIO::Update); } else { itsFile = std::make_shared(fileName); } openArray (arrayName, groupName); DebugAssert (ok(), AipsError); } template HDF5Lattice::HDF5Lattice (const std::shared_ptr& file, const String& arrayName, const String& groupName) : itsFile (file) { openArray (arrayName, groupName); DebugAssert (ok(), AipsError); } template HDF5Lattice::HDF5Lattice (const HDF5Lattice& other) : Lattice(), itsFile (other.itsFile), itsGroup (other.itsGroup), itsDataSet (other.itsDataSet), itsTileShape (other.itsTileShape) { DebugAssert (ok(), AipsError); } template HDF5Lattice::~HDF5Lattice() { flush(); } template HDF5Lattice& HDF5Lattice::operator= (const HDF5Lattice& other) { if (this != &other) { itsFile = other.itsFile; itsGroup = other.itsGroup; itsDataSet = other.itsDataSet; itsTileShape = other.itsTileShape; } DebugAssert (ok(), AipsError); return *this; } template Lattice* HDF5Lattice::clone() const { return new HDF5Lattice (*this); } template Bool HDF5Lattice::isPersistent() const { return True; } template Bool HDF5Lattice::isPaged() const { return True; } template Bool HDF5Lattice::isWritable() const { // HDF5Lattice is writable if underlying file is already open for write // or if the underlying file is in principle writable. return itsFile->isWritable(); } template String HDF5Lattice::name (Bool stripPath) const { Path path(itsFile->getName()); if (!stripPath) { return path.absoluteName(); } return path.baseName(); } template IPosition HDF5Lattice::shape() const { DebugAssert (ok(), AipsError); return itsDataSet->shape(); } template Bool HDF5Lattice::doGetSlice (Array& buffer, const Slicer& section) { buffer.resize (section.length()); Bool deleteIt; T* data = buffer.getStorage (deleteIt); itsDataSet->get (section, data); buffer.putStorage (data, deleteIt); return False; } template void HDF5Lattice::doPutSlice (const Array& sourceArray, const IPosition& where, const IPosition& stride) { checkWritable(); Bool deleteIt; const T* data = sourceArray.getStorage (deleteIt); const uInt arrDim = sourceArray.ndim(); const uInt latDim = ndim(); AlwaysAssert(arrDim <= latDim, AipsError); if (arrDim == latDim) { Slicer section(where, sourceArray.shape(), stride, Slicer::endIsLength); itsDataSet->put (section, data); } else { Array degenerateArr(sourceArray.addDegenerate(latDim-arrDim)); Slicer section(where, degenerateArr.shape(), stride, Slicer::endIsLength); itsDataSet->put (section, data); } sourceArray.freeStorage (data, deleteIt); } template IPosition HDF5Lattice::tileShape() const { return itsTileShape; } template uInt HDF5Lattice::advisedMaxPixels() const { return tileShape().product(); } template IPosition HDF5Lattice::doNiceCursorShape (uInt maxPixels) const { IPosition retval = tileShape(); if (retval.product() > Int(maxPixels)) { retval = Lattice::doNiceCursorShape(maxPixels); } return retval; } template void HDF5Lattice::setCacheSizeInTiles (uInt howManyTiles) { itsDataSet->setCacheSize (howManyTiles); } template void HDF5Lattice::setCacheSizeFromPath (const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath) { itsDataSet->setCacheSize (TSMCube::calcCacheSize (itsDataSet->shape(), tileShape(), False, sliceShape, windowStart, windowLength, axisPath, 0, 1)); } template T HDF5Lattice::getAt (const IPosition& where) const { T value; itsDataSet->get (Slicer(where), &value); return value; } template void HDF5Lattice::putAt (const T& value, const IPosition& where) { itsDataSet->put (Slicer(where), &value); } template Bool HDF5Lattice::ok() const { return True; } template void HDF5Lattice::checkWritable() const { if (!isWritable()) { throw HDF5Error ("file " + itsFile->getName() + " is not writable"); } } template LatticeIterInterface* HDF5Lattice::makeIter (const LatticeNavigator& nav, Bool useRef) const { return new HDF5LattIter(*this, nav, useRef); } template void HDF5Lattice::openArray (const String& arrayName, const String& groupName) { if (groupName.empty()) { // Use root group. itsGroup = std::make_shared(*itsFile, "/", true); } else { itsGroup = std::make_shared(*itsFile, groupName, true); } // Open the data set. itsDataSet = std::make_shared(*itsGroup, arrayName, (const T*)0); // Calculate tile shape if default tile shape is empty itsTileShape = itsDataSet->tileShape(); if (itsTileShape.empty()) { itsTileShape = TiledFileAccess::makeTileShape(itsDataSet->shape()); } } template void HDF5Lattice::makeArray (const TiledShape& shape, const String& arrayName, const String& groupName) { // Make sure the table is writable. checkWritable(); if (groupName.empty()) { // Use root group. itsGroup = std::make_shared(*itsFile, "/", true); } else { // Create group if not existing yet. itsGroup = std::make_shared(*itsFile, groupName); } // Create the data set. itsDataSet = std::make_shared(*itsGroup, arrayName, shape.shape(), shape.tileShape(), (const T*)0); // Calculate tile shape if default tile shape is empty itsTileShape = itsDataSet->tileShape(); if (itsTileShape.empty()) { itsTileShape = TiledFileAccess::makeTileShape(itsDataSet->shape()); } } template void HDF5Lattice::flush() { itsFile->flush(); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/Lattices/Lattice.h000066400000000000000000000455271476623553700204570ustar00rootroot00000000000000//# Lattice.h: Lattice is an abstract base class for array-like classes //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICE_H #define LATTICES_LATTICE_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class IPosition; class LatticeNavigator; template class COWPtr; template class Functional; template class LatticeIterInterface; // // A templated, abstract base class for array-like objects. // // // // // //
      • IPosition //
      • Array //
      • LatticeBase //
      • Abstract Base class Inheritance - try "Advanced C++" by James // O. Coplien, Ch. 5. // // // Lattice: "A regular, periodic configuration of points, particles, // or objects, throughout an area of a space..." (American Heritage Directory) // This definition matches our own: an n-dimensional arrangement of items, // on regular orthogonal axes. // // // This pure abstract base class defines the operations which may be performed // on any concrete class derived from it. It has only a few non-pure virtual // member functions. // The fundamental contribution of this class, therefore, is that it // defines the operations derived classes must provide: //
          //
        • how to extract a "slice" (or sub-array, or subsection) from // a Lattice. //
        • how to copy a slice in. //
        • how to get and put a single element //
        • how to apply a function to all elements //
        • various shape related functions. //
        // The base class LatticeBase contains // several functions not dependent on the template parameter. // Lattices always have a zero origin. //
        // // Because Lattice is an abstract base class, an actual instance of this // class cannot be constructed. However the interface it defines can be used // inside a function. This is always recommended as it allows functions // which have Lattices as arguments to work for any derived class. //

        // I will give a few examples here and then refer the reader to the // ArrayLattice class (a memory resident // Lattice) and the PagedArray class (a // disk based Lattice) which contain further examples with concrete // classes (rather than an abstract one). All the examples shown below are used // in the dLattice.cc demo program. // //

        Example 1:

        // This example calculates the mean of the Lattice. Because Lattices can be too // large to fit into physical memory it is not good enough to simply use // getSlice to read all the elements into an Array. Instead the // Lattice is accessed in chunks which can fit into memory (the size is // determined by the advisedMaxPixels and niceCursorShape // functions). The LatticeIterator::cursor() function then returns // each of these chunks as an Array and the standard Array based functions are // used to calculate the mean on each of these chunks. Functions like this one // are the recommended way to access Lattices as the // LatticeIterator will correctly // setup any required caches. // // // Complex latMean(const Lattice& lat) { // const uInt cursorSize = lat.advisedMaxPixels(); // const IPosition cursorShape = lat.niceCursorShape(cursorSize); // const IPosition latticeShape = lat.shape(); // Complex currentSum = 0.0f; // size_t nPixels = 0u; // RO_LatticeIterator iter(lat, // LatticeStepper(latticeShape, cursorShape)); // for (iter.reset(); !iter.atEnd(); iter++){ // currentSum += sum(iter.cursor()); // nPixels += iter.cursor().nelements(); // } // return currentSum/nPixels; // } // // //

        Example 2:

        // Sometimes it will be neccesary to access slices of a Lattice in a nearly // random way. Often this can be done using the subSection commands in the // LatticeStepper class. But it is also // possible to use the getSlice and putSlice functions. The following example // does a two-dimensional Real to Complex Fourier transform. This example is // restricted to four-dimensional Arrays (unlike the previous example) and does // not set up any caches (caching is currently only used with PagedArrays). So // only use getSlice and putSlice when things cannot be done using // LatticeIterators. // // // void FFT2DReal2Complex(Lattice& result, // const Lattice& input){ // AlwaysAssert(input.ndim() == 4, AipsError); // const IPosition shape = input.shape(); // const uInt nx = shape(0); // AlwaysAssert (nx > 1, AipsError); // const uInt ny = shape(1); // AlwaysAssert (ny > 1, AipsError); // const uInt npol = shape(2); // const uInt nchan = shape(3); // const IPosition resultShape = result.shape(); // AlwaysAssert(resultShape.nelements() == 4, AipsError); // AlwaysAssert(resultShape(3) == nchan, AipsError); // AlwaysAssert(resultShape(2) == npol, AipsError); // AlwaysAssert(resultShape(1) == ny, AipsError); // AlwaysAssert(resultShape(0) == nx/2 + 1, AipsError); // // const IPosition inputSliceShape(4,nx,ny,1,1); // const IPosition resultSliceShape(4,nx/2+1,ny,1,1); // COWPtr> // inputArrPtr(new Array(inputSliceShape.nonDegenerate())); // Array resultArray(resultSliceShape.nonDegenerate()); // FFTServer FFT2D(inputSliceShape.nonDegenerate()); // // IPosition start(4,0); // Bool isARef; // for (uInt c = 0; c < nchan; c++){ // for (uInt p = 0; p < npol; p++){ // isARef = input.getSlice(inputArrPtr, // Slicer(start,inputSliceShape), True); // FFT2D.fft(resultArray, *inputArrPtr); // result.putSlice(resultArray, start); // start(2) += 1; // } // start(2) = 0; // start(3) += 1; // } // } // // Note that the LatticeFFT class // offers a nice way to do lattice based FFTs. // //

        Example 3:

        // Occasionally you may want to access a few elements of a Lattice without // all the difficulty involved in setting up Iterators or calling getSlice // and putSlice. This is demonstrated in the example below. // Setting a single element can be done with the putAt function, // while getting a single element can be done with the parenthesis operator. // Using these functions to access many elements of a Lattice is not // recommended as this is the slowest access method. // // In this example an ideal point spread function will be inserted into an // empty Lattice. As with the previous examples all the action occurs // inside a function because Lattice is an interface (abstract) class. // // // void makePsf(Lattice& psf) { // const IPosition centrePos = psf.shape()/2; // psf.set(0.0f); // this sets all the elements to zero // // As it uses a LatticeIterator it is efficient // psf.putAt (1, centrePos); // This sets just the centre element to one // AlwaysAssert(near(psf(centrePos), 1.0f, 1E-6), AipsError); // AlwaysAssert(near(psf(centrePos*0), 0.0f, 1E-6), AipsError); // } // //
        // // Creating an abstract base class which provides a common interface between // memory and disk based arrays has a number of advantages. //
          //
        • It allows functions common to all arrays to be written independent // of the way the data is stored. This is illustrated in the three examples // above. //
        • It reduces the learning curve for new users who only have to become // familiar with one interface (ie. Lattice) rather than distinct interfaces // for different array types. //
        //
        // //
      • Make PagedArray cache functions virtual in this base class. // template class Lattice : public LatticeBase { public: // a virtual destructor is needed so that it will use the actual destructor // in the derived class virtual ~Lattice(); // Make a copy of the derived object (reference semantics). virtual Lattice* clone() const = 0; // Get the data type of the lattice. virtual DataType dataType() const; // Return the value of the single element located at the argument // IPosition. //
        The default implementation uses getSlice. // T operator() (const IPosition& where) const; virtual T getAt (const IPosition& where) const; // // Put the value of a single element. //
        The default implementation uses putSlice. virtual void putAt (const T& value, const IPosition& where); // Functions which extract an Array of values from a Lattice. All the // IPosition arguments must have the same number of axes as the underlying // Lattice, otherwise, an exception is thrown.
        // The parameters are: //
          //
        • buffer: a COWPtr> or an // Array. See example 2 above for an example. //
        • start: The starting position (or Bottom Left Corner), within // the Lattice, of the data to be extracted. //
        • shape: The shape of the data to be extracted. This is not a // position within the Lattice but the actual shape the buffer will // have after this function is called. This argument added // to the "start" argument should be the "Top Right Corner". //
        • stride: The increment for each axis. A stride of // one will return every data element, a stride of two will return // every other element. The IPosition elements may be different for // each respective axis. Thus, a stride of IPosition(3,1,2,3) says: // fill the buffer with every element whose position has a first // index between start(0) and start(0)+shape(0), a second index // which is every other element between start(1) and // (start(1)+shape(1))*2, and a third index of every third element // between start(2) and (start(2)+shape(2))*3. //
        • section: Another way of specifying the start, shape and stride //
        • removeDegenerateAxes: a Bool which dictates whether to remove // "empty" axis created in buffer. (e.g. extracting an n-dimensional // from an (n+1)-dimensional will fill 'buffer' with an array that // has a degenerate axis (i.e. one axis will have a length = 1.) // Setting removeDegenerateAxes = True will return a buffer with // a shape that doesn't reflect these superfluous axes.) //
        // // The derived implementations of these functions return // 'True' if "buffer" is a reference to Lattice data and 'False' if it // is a copy. // Bool get (COWPtr>& buffer, Bool removeDegenerateAxes=False) const; Bool getSlice (COWPtr>& buffer, const Slicer& section, Bool removeDegenerateAxes=False) const; Bool getSlice (COWPtr>& buffer, const IPosition& start, const IPosition& shape, Bool removeDegenerateAxes=False) const; Bool getSlice (COWPtr>& buffer, const IPosition& start, const IPosition& shape, const IPosition& stride, Bool removeDegenerateAxes=False) const; Bool get (Array& buffer, Bool removeDegenerateAxes=False); Bool getSlice (Array& buffer, const Slicer& section, Bool removeDegenerateAxes=False); Bool getSlice (Array& buffer, const IPosition& start, const IPosition& shape, Bool removeDegenerateAxes=False); Bool getSlice (Array& buffer, const IPosition& start, const IPosition& shape, const IPosition& stride, Bool removeDegenerateAxes=False); Array get (Bool removeDegenerateAxes=False) const; Array getSlice (const Slicer& section, Bool removeDegenerateAxes=False) const; Array getSlice (const IPosition& start, const IPosition& shape, Bool removeDegenerateAxes=False) const; Array getSlice (const IPosition& start, const IPosition& shape, const IPosition& stride, Bool removeDegenerateAxes=False) const; // // A function which places an Array of values within this instance of the // Lattice at the location specified by the IPosition "where", incrementing // by "stride". All of the IPosition arguments must be of the same // dimensionality as the Lattice. The sourceBuffer array may (and probably // will) have less axes than the Lattice. The stride defaults to one if // not specified. // void putSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride) { doPutSlice (sourceBuffer, where, stride); } void putSlice (const Array& sourceBuffer, const IPosition& where); void put (const Array& sourceBuffer); // // Set all elements in the Lattice to the given value. virtual void set (const T& value); // Replace every element, x, of the Lattice with the result of f(x). You // must pass in the address of the function -- so the function must be // declared and defined in the scope of your program. All versions of // apply require a function that accepts a single argument of type T (the // Lattice template type) and return a result of the same type. The first // apply expects a function with an argument passed by value; the second // expects the argument to be passed by const reference; the third // requires an instance of the class Functional. The // first form ought to run faster for the built-in types, which may be an // issue for large Lattices stored in memory, where disk access is not an // issue. // virtual void apply (T (*function)(T)); virtual void apply (T (*function)(const T&)); virtual void apply (const Functional& function); // // Add, subtract, multiple, or divide by another Lattice. // The other Lattice can be a scalar (e.g. the result of LatticeExpr). // Possible masks are not taken into account. // void operator+= (const Lattice& other) { handleMath (other, 0); } void operator-= (const Lattice& other) { handleMath (other, 1); } void operator*= (const Lattice& other) { handleMath (other, 2); } void operator/= (const Lattice& other) { handleMath (other, 3); } // // Copy the data from the given lattice to this one. // The default implementation uses function copyDataTo. virtual void copyData (const Lattice& from); // Copy the data from this lattice to the given lattice. // The default implementation only copies data (thus no mask, etc.). virtual void copyDataTo (Lattice& to) const; // This function returns the advised maximum number of pixels to // include in the cursor of an iterator. The default implementation // returns a number that is a power of two and includes enough pixels to // consume between 4 and 8 MBytes of memory. virtual uInt advisedMaxPixels() const; // These functions are used by the LatticeIterator class to generate an // iterator of the correct type for a specified Lattice. Not recommended // for general use. //
        The default implementation creates a LatticeIterInterface object. virtual LatticeIterInterface* makeIter (const LatticeNavigator& navigator, Bool useRef) const; // The functions (in the derived classes) doing the actual work. // These functions are public, so they can be used internally in the // various Lattice classes, which is especially useful for doGetSlice. //
        However, doGetSlice does not call Slicer::inferShapeFromSource // to fill in possible unspecified section values. Therefore one // should normally use one of the get(Slice) functions. doGetSlice // should be used with care and only when performance is an issue. // virtual Bool doGetSlice (Array& buffer, const Slicer& section) = 0; virtual void doPutSlice (const Array& buffer, const IPosition& where, const IPosition& stride) = 0; // protected: // Define default constructor to satisfy compiler. Lattice() {}; // Handle the Math operators (+=, -=, *=, /=). // They work similarly to copyData(To). // However, they are not defined for Bool types, thus specialized below. // virtual void handleMath (const Lattice& from, int oper); virtual void handleMathTo (Lattice& to, int oper) const; // // Copy constructor and assignment can only be used by derived classes. // Lattice (const Lattice&) : LatticeBase() {} Lattice& operator= (const Lattice&) { return *this; } // }; template<> inline void Lattice::handleMathTo (Lattice&, int) const { throwBoolMath(); } //# Declare extern templates for often used types. extern template class Lattice; extern template class Lattice; } //# NAMESPACE CASACORE - END //# There is a problem in including Lattice.tcc, because it needs //# LatticeIterator.h which in its turn includes Lattice.h again. //# So in a source file including LatticeIterator.h, Lattice::set fails //# to compile, because the LatticeIterator declarations are not seen yet. //# Therefore LatticeIterator.h is included here, while LatticeIterator.h //# includes Lattice.tcc. #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/Lattices/Lattice.tcc000066400000000000000000000247321476623553700207740ustar00rootroot00000000000000//# Lattice.cc: this defines Lattice.cc, a base for array-related classes //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICE_TCC #define LATTICES_LATTICE_TCC #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // destructor template Lattice::~Lattice() { // does nothing } template DataType Lattice::dataType() const { return whatType(); } // rvalue subscript operator for const objects template T Lattice::operator() (const IPosition& where) const { return getAt (where); } template Bool Lattice::get (COWPtr>& buffer, Bool removeDegenerateAxes) const { uInt nd = ndim(); return getSlice (buffer, Slicer(IPosition(nd,0), shape()), removeDegenerateAxes); } template Bool Lattice::get (Array& buffer, Bool removeDegenerateAxes) { uInt nd = ndim(); return getSlice (buffer, Slicer(IPosition(nd,0), shape()), removeDegenerateAxes); } template Array Lattice::get (Bool removeDegenerateAxes) const { uInt nd = ndim(); return getSlice (Slicer(IPosition(nd,0), shape()), removeDegenerateAxes); } template Bool Lattice::getSlice (COWPtr>& buffer, const IPosition& start, const IPosition& shape, Bool removeDegenerateAxes) const { return getSlice (buffer, Slicer(start, shape), removeDegenerateAxes); } template Bool Lattice::getSlice (COWPtr>& buffer, const IPosition& start, const IPosition& shape, const IPosition& stride, Bool removeDegenerateAxes) const { return getSlice (buffer, Slicer(start, shape, stride), removeDegenerateAxes); } template Bool Lattice::getSlice (COWPtr>& buffer, const Slicer& section, Bool removeDegenerateAxes) const { // Cast pointer to non-const. // This is safe, since the array is copied when needed by COWptr. Lattice* This = (Lattice*)this; // The COWPtr takes over the pointer to the array. std::unique_ptr> arr(new Array); Bool isARef = This->getSlice (*arr, section, removeDegenerateAxes); buffer = COWPtr> (arr.release(), isARef); return False; } template Bool Lattice::getSlice (Array& buffer, const IPosition& start, const IPosition& shape, Bool removeDegenerateAxes) { return getSlice (buffer, Slicer(start, shape), removeDegenerateAxes); } template Bool Lattice::getSlice (Array& buffer, const IPosition& start, const IPosition& shape, const IPosition& stride, Bool removeDegenerateAxes) { return getSlice (buffer, Slicer(start, shape, stride), removeDegenerateAxes); } template Bool Lattice::getSlice (Array& buffer, const Slicer& section, Bool removeDegenerateAxes) { Bool isARef; // When the slicer is fixed, it can be used immediately. // Otherwise unspecified values are to be filled in. if (section.isFixed()) { IPosition shp(shape()); if (section.ndim() != shp.nelements() || section.end() >= shp) { throw AipsError ("Lattice::getSlice - section outside lattice"); } isARef = doGetSlice (buffer, section); } else { IPosition blc,trc,inc; section.inferShapeFromSource (shape(), blc, trc, inc); isARef = doGetSlice (buffer, Slicer(blc,trc,inc,Slicer::endIsLast)); } if (removeDegenerateAxes) { Array tmp = buffer.nonDegenerate(); buffer.reference (tmp); } return isARef; } template Array Lattice::getSlice (const IPosition& start, const IPosition& shape, Bool removeDegenerateAxes) const { return getSlice (Slicer(start,shape), removeDegenerateAxes); } template Array Lattice::getSlice (const IPosition& start, const IPosition& shape, const IPosition& stride, Bool removeDegenerateAxes) const { return getSlice (Slicer(start,shape,stride), removeDegenerateAxes); } template Array Lattice::getSlice (const Slicer& section, Bool removeDegenerateAxes) const { // Cast pointer to non-const. // This is safe, since the array is copied when needed. Lattice* This = (Lattice*)this; // Note that getSlice is used to be sure that section gets filled // when needed. Array arr; Bool isARef = This->getSlice (arr, section, removeDegenerateAxes); // When not referenced, return it as such. // Otherwise make a copy. if (!isARef) { return arr; } Array tmp; tmp = arr; return tmp; } template void Lattice::putSlice (const Array& sourceBuffer, const IPosition& where) { doPutSlice (sourceBuffer, where, IPosition(where.nelements(),1)); } template void Lattice::put (const Array& sourceBuffer) { uInt nd = ndim(); doPutSlice (sourceBuffer, IPosition(nd,0), IPosition(nd,1)); } template void Lattice::set (const T& value) { LatticeIterator iter(*this, True); for (iter.reset(); !iter.atEnd(); iter++) { iter.woCursor() = value; } } template void Lattice::apply (T (*function) (T)) { LatticeIterator iter(*this, True); for (iter.reset(); !iter.atEnd(); iter++) { iter.rwCursor().apply (function); } } template void Lattice::apply (T (*function) (const T&)) { LatticeIterator iter(*this, True); for (iter.reset(); !iter.atEnd(); iter++) { iter.rwCursor().apply(function); } } template void Lattice::apply (const Functional& function) { LatticeIterator iter(*this, True); for (iter.reset(); !iter.atEnd(); iter++) { iter.rwCursor().apply([&function](T t){ return function(t); }); } } template T Lattice::getAt (const IPosition& where) const { // Casting the const away is harmless. Array tmp; ((Lattice*)this)->doGetSlice (tmp, Slicer(where)); // Since the array contains 1 element only, getStorage does not // create a copy. Bool deleteIt; return *(tmp.getStorage(deleteIt)); } template void Lattice::putAt (const T& value, const IPosition& where) { // Use a temporary 1-element array with the correct dimensionality. Array tmp (IPosition(where.nelements(), 1), &value); putSlice (tmp, where); } template void Lattice::copyData (const Lattice& from) { from.copyDataTo (*this); } template void Lattice::handleMath (const Lattice& from, int oper) { from.handleMathTo (*this, oper); } template void Lattice::copyDataTo (Lattice& to) const { // Check the lattice is writable. // Check the shape conformance. AlwaysAssert (to.isWritable(), AipsError); const IPosition shapeIn = shape(); const IPosition shapeOut = to.shape(); AlwaysAssert (shapeIn.isEqual (shapeOut), AipsError); IPosition cursorShape = to.niceCursorShape(); LatticeStepper stepper (shapeOut, cursorShape, LatticeStepper::RESIZE); // Create an iterator for the output to setup the cache. // It is not used, because using putSlice directly is faster and as easy. LatticeIterator dummyIter(to, stepper); RO_LatticeIterator iter(*this, stepper, True); for (iter.reset(); !iter.atEnd(); iter++) { to.putSlice (iter.cursor(), iter.position()); } } template void Lattice::handleMathTo (Lattice& to, int oper) const { // Check the lattice is writable. // Check the shape conformance. AlwaysAssert (to.isWritable(), AipsError); const IPosition shapeIn = shape(); const IPosition shapeOut = to.shape(); AlwaysAssert (shapeIn.isEqual (shapeOut), AipsError); IPosition cursorShape = to.niceCursorShape(); LatticeStepper stepper (shapeOut, cursorShape, LatticeStepper::RESIZE); // Create an iterator for the output. // If possible, use reference semantics in the iterators. LatticeIterator toIter(to, stepper, True); RO_LatticeIterator iter(*this, stepper, True); switch (oper) { case 0: for (iter.reset(); !iter.atEnd(); iter++, toIter++) { toIter.rwCursor() += iter.cursor(); } break; case 1: for (iter.reset(); !iter.atEnd(); iter++) { toIter.rwCursor() -= iter.cursor(); } break; case 2: for (iter.reset(); !iter.atEnd(); iter++) { toIter.rwCursor() *= iter.cursor(); } break; case 3: for (iter.reset(); !iter.atEnd(); iter++) { toIter.rwCursor() /= iter.cursor(); } break; default: throw AipsError ("Lattice::handleMathTo - Unknown operator"); } } template LatticeIterInterface* Lattice::makeIter (const LatticeNavigator& nav, Bool useRef) const { return new LatticeIterInterface(*this, nav, useRef); } template uInt Lattice::advisedMaxPixels() const { // The returned number of pixels is always a power of two for unknown // reasons, and occupies between 4 and 8 MBytes return (uInt) pow (2.0, ceil(log(4.0*1024.0*1024.0/sizeof(T))/log(2.0))); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/Lattices/LatticeBase.cc000066400000000000000000000066501476623553700214020ustar00rootroot00000000000000//# LatticeBase.cc: A non-templated, abstract base class for array-like classes //# Copyright (C) 1999,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LatticeBase::~LatticeBase() {} String LatticeBase::imageType() const { return "Lattice"; } Bool LatticeBase::isPersistent() const { return False; } Bool LatticeBase::isPaged() const { return False; } Bool LatticeBase::canReferenceArray() const { return False; } Bool LatticeBase::isWritable() const { return True; } void LatticeBase::save (const String&) const { throw AipsError(imageType() + "::save is not implemented"); } Bool LatticeBase::lock (FileLocker::LockType, uInt) { return True; } void LatticeBase::unlock() {} Bool LatticeBase::hasLock (FileLocker::LockType) const { return True; } void LatticeBase::resync() {} void LatticeBase::flush() {} void LatticeBase::tempClose() {} void LatticeBase::reopen() {} String LatticeBase::name (Bool) const { return ""; } uInt LatticeBase::ndim() const { return shape().nelements(); } size_t LatticeBase::nelements() const { return shape().product(); } LELCoordinates LatticeBase::lelCoordinates() const { return LELCoordinates(); } IPosition LatticeBase::doNiceCursorShape (uInt maxPixels) const { IPosition originalShape(shape()); uInt ndim = originalShape.nelements(); IPosition cursorShape(ndim); if (ndim > 0) { cursorShape = 1; cursorShape(0) = originalShape(0); for (uInt i=1; i < ndim && cursorShape.product()*originalShape(i) <= Int(maxPixels); i++) { cursorShape(i) = originalShape(i); } } return cursorShape; } Bool LatticeBase::ok() const { return True; } uInt LatticeBase::maximumCacheSize() const { return 0; } void LatticeBase::setMaximumCacheSize (uInt) {} void LatticeBase::setCacheSizeInTiles (uInt) {} void LatticeBase::setCacheSizeFromPath (const IPosition&, const IPosition&, const IPosition&, const IPosition&) {} void LatticeBase::clearCache() {} void LatticeBase::showCacheStatistics (ostream&) const {} void LatticeBase::throwBoolMath() const { throw AipsError ("Operator +=, etc. cannot be used for a Boolean lattice"); } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/Lattices/LatticeBase.h000066400000000000000000000241471476623553700212450ustar00rootroot00000000000000//# LatticeBase.h: A non-templated, abstract base class for array-like classes //# Copyright (C) 1999,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICEBASE_H #define LATTICES_LATTICEBASE_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class LogIO; // // A non-templated, abstract base class for array-like objects. // // // // // // This pure abstract base class defines the operations which may be // performed on a lattice of any type. //
        See class Lattice for a detailed // description of a lattice. //
        // // It is very useful to be able to keep a pointer to a // non-templated base class. Furthermore it gives the opportunity to // factor out some non-templated code. // // // The cache functions (maximumCacheSize, setMaximumCacheSize, // setCacheSizeInTiles, setCacheSizeFromPath, clearCache, and // showCacheStatistics) should all be over-ridden together as // in PagedArray. // // //# //#
      • //# class LatticeBase { public: // A virtual destructor is needed so that it will use the actual destructor // in the derived class. virtual ~LatticeBase(); // Make a copy of the derived object (reference semantics). virtual LatticeBase* clone() const = 0; // Get the image type (returns name of derived class). // The default implementation returns "Lattice". // Note it is made pure virtual in ImageInterface. virtual String imageType() const; // Get the data type of the lattice. virtual DataType dataType() const = 0; // Is the lattice persistent and can it be loaded by other processes as well? // That is the case for a PagedArray or PagedImage and for an ImageExpr // which does not use transient lattices or regions. //
        The default implementation returns False. virtual Bool isPersistent() const; // Is the lattice paged to disk? //
        The default implementation returns False. virtual Bool isPaged() const; // Can the lattice data be referenced as an array section? // That is the case for an ArrayLattice or a Temp/SubLattice using it. // It is used by LatticeIterInterface. //
        The default implementation returns False. virtual Bool canReferenceArray() const; // Is the lattice writable? //
        The default implementation returns True. virtual Bool isWritable() const; // Save the image in an AipsIO file with the given name. // Its purpose is to make ImageConcat and ImageExpr objects // persistent. //
        The default implementation throws an exception. virtual void save (const String& fileName) const; // It is strongly recommended to use class // LatticeLocker to // handle lattice locking. It also contains a more detailed // explanation of the locking process. //
        By default the functions do not do anything at all. // lock() and hasLock return True, which is suitable for all // non-paged lattices. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; // // Resynchronize the Lattice object with the lattice file. // This function is only useful if no read-locking is used, ie. // if the table lock option is UserNoReadLocking or AutoNoReadLocking. // In that cases the table system does not acquire a read-lock, thus // does not synchronize itself automatically. //
        By default the function does not do anything at all. virtual void resync(); // Flush the data (but do not unlock). //
        By default the function does not do anything at all. virtual void flush(); // Temporarily close the lattice. // It will be reopened automatically on the next access. //
        By default the function does not do anything at all. virtual void tempClose(); // Explicitly reopen the temporarily closed lattice. //
        By default the function does not do anything at all. virtual void reopen(); // Return the name of the current Lattice object. This will generally // be a file name for lattices that have a persistent form. Any path // before the actual file name can be optionally stripped off. //
        The default implementation returns an empty string. virtual String name (Bool stripPath=False) const; // Return the shape of the Lattice including all degenerate axes // (ie. axes with a length of one) virtual IPosition shape() const = 0; // Return the number of axes in this Lattice. This includes all // degenerate axes. //
        The default implementation returns shape().nelements(). virtual uInt ndim() const; // Return the total number of elements in this Lattice. //
        The default implementation returns shape().product(). // virtual size_t nelements() const; size_t size() const { return nelements(); } // // Return a value of "True" if this instance of Lattice and 'other' have // the same shape, otherwise returns a value of "False". Bool conform (const LatticeBase& other) const { return shape().isEqual (other.shape()); } // Return the coordinates of the lattice. //
        The default implementation returns an 'empty' LELLattCoord object. virtual LELCoordinates lelCoordinates() const; // This function returns the recommended maximum number of pixels to // include in the cursor of an iterator. The Lattice class has a default // implementation which returns a number that is a power of two and // includes enough pixels to consume between 4 and 8 MBytes of memory. virtual uInt advisedMaxPixels() const = 0; // Returns a recommended cursor shape for iterating through all the pixels // in the Lattice. The default implementation sets up a shape that // completely fills as many axes as possible, but always at least the // first axis. For example, given a 10x20x30 Lattice // // maxPixels = 1 --> niceCursorShape = [10,1,1] // 100 --> niceCursorShape = [10,1,1] // 300 --> niceCursorShape = [10,20,1] // 10000 --> niceCursorShape = [10,20,30] // // The default argument is the result of advisedMaxPixels(). // IPosition niceCursorShape (uInt maxPixels) const { return doNiceCursorShape (maxPixels); } IPosition niceCursorShape() const { return doNiceCursorShape (advisedMaxPixels()); } // // Check class internals - used for debugging. Should always return True virtual Bool ok() const; // The function (in the derived classes) doing the actual work. // This function is public, so it can be used internally in the // various Lattice classes. //
        The default implementation tries to fit as many axes // as possible given maxPixels. virtual IPosition doNiceCursorShape (uInt maxPixels) const; // Maximum cache size - not necessarily all used. In pixels. // Default returns 0, which means that there is no maximum. virtual uInt maximumCacheSize() const; // Set the maximum (allowed) cache size as indicated. //
        The default implementation does nothing. virtual void setMaximumCacheSize (uInt howManyPixels); // Set the actual cache size for this Array to be big enough for the // indicated number of tiles. This cache is not shared with PagedArrays // in other rows and is always clipped to be less than the maximum value // set using the setMaximumCacheSize member function. // Tiles are cached using a first in first out algorithm. //
        The default implementation does nothing. virtual void setCacheSizeInTiles (uInt howManyTiles); // Set the cache size as to "fit" the indicated path. //
        The default implementation does nothing. virtual void setCacheSizeFromPath (const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath); // Clears and frees up the caches, but the maximum allowed cache size is // unchanged from when setCacheSize was called. //
        The default implementation does nothing. virtual void clearCache(); // Report on cache success. //
        The default implementation does nothing. virtual void showCacheStatistics (ostream& os) const; protected: // Define default constructor to be used by derived classes. LatticeBase() {}; // Copy constructor and assignment can only be used by derived classes. // LatticeBase (const LatticeBase&) {}; LatticeBase& operator= (const LatticeBase&) { return *this; } // // Throw an exception for arithmetic on a Bool Lattice. void throwBoolMath() const; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/Lattices/LatticeCache.h000066400000000000000000000112771476623553700213760ustar00rootroot00000000000000//# LatticeCache: Cache for accessing a Lattice in Tiles //# Copyright (C) 1995,1996,1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICECACHE_H #define LATTICES_LATTICECACHE_H //# Includes #include #include #include #include //# Forward Declarations #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template class Block; template class Lattice; // a class for caching image access via tiles // // // // // // // //
      • Lattice // // // // This class divides an image into Tiles that are stored in // a Cache. // // // // An image is divided into tiles of a specified shape. Access to the // image pixels is via these tiles. A cache of active tiles is kept // in memory up to a specified limit in memory allocation. Tiles // are flushed to disk using a Least-Recently-Used Criterion. // // The tile size specified is the maximum dimension. Near the edge, // smaller tiles are returned if necessary. // // The offset needed to get back to true pixels is also available. // // The cache hit rate for a sufficient number of random accesses // goes as the ratio of cache size to image size. // // Tiles may be overlapped. If there is any overlap then the // caller is responsible for dealing with the overlap. Normally // one will only want overlapping windows for additive operations // in which case the additive flag to the constructor should be // used. // // // // // // // // // // To aid in gridding // // // // template class LatticeCache { public: // Constructor: cachesize in units of T. tileOverlap is the fractional // overlap between neighbouring tile. LatticeCache(Lattice &image, Int cacheSize, IPosition tileShape, Vector& tileOverlap, Bool additive); LatticeCache(const LatticeCache & other); LatticeCache &operator=(const LatticeCache & other); virtual ~LatticeCache(); // Return the tile for a given location // Array& tile(IPosition& cacheLoc, const IPosition& tileLoc, Bool discard=True); Array& tile(const IPosition& tileLoc, Bool discard=True); // // const version is needed const Array& tile(const IPosition& tileLoc); // Return the IPosition for the start of this tile IPosition& cacheLocation(IPosition& cacheLoc, const IPosition& tileLoc); // Show the statistics of cache access virtual void showCacheStatistics(ostream& os); // Clear the statistics of cache access virtual void clearCacheStatistics(); // Flush contents virtual void flush(); protected: LatticeCache() {}; Int numberTiles; IPosition tileShape; Vector tileShapeVec, tileOffsetVec; Vector tileOverlap; Bool additive; Int cacheSize; Int cacheAccesses; Int cacheHits; Int cacheMisses; Int cacheReads; Int cacheWrites; Int getFreeTile(Bool readonly); Block tileLocs; Block tileSequence; Block> tileContents; void writeTile(Int tile); void readTile(Int tile, Bool readonly); Lattice* image_p; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/Lattices/LatticeCache.tcc000066400000000000000000000240731476623553700217160ustar00rootroot00000000000000//# LatticeCache.cc: Cache for accessing a Lattice in Tiles //# Copyright (C) 1995,1996,1997,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICECACHE_TCC #define LATTICES_LATTICECACHE_TCC #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template LatticeCache::LatticeCache(const LatticeCache & other) { operator=(other); } template LatticeCache &LatticeCache::operator=(const LatticeCache & other) { tileLocs=other.tileLocs; tileSequence=other.tileSequence; tileContents=other.tileContents; numberTiles=other.numberTiles; tileShape=other.tileShape; tileShapeVec=other.tileShapeVec; tileOffsetVec=other.tileOffsetVec; tileOverlap=other.tileOverlap; cacheSize=other.cacheSize; cacheAccesses=other.cacheAccesses; cacheHits=other.cacheHits; cacheMisses=other.cacheMisses; cacheReads=other.cacheReads; cacheWrites=other.cacheWrites; image_p=other.image_p; additive=other.additive; return *this; } // Construct a cache for a given tile shape and a given size of the // cache. template LatticeCache::LatticeCache(Lattice &image, Int iCacheSize, IPosition iTileShape, Vector& iTileOverlap, Bool iadditive) : numberTiles(0), additive(iadditive), cacheAccesses(0), cacheHits(0), cacheMisses(0), cacheReads(0), cacheWrites(0), image_p(&image) { AlwaysAssert(iTileShape.conform(image.shape()), AipsError); AlwaysAssert(iTileShape.product(), AipsError); tileShape=iTileShape; tileShapeVec=tileShape.asVector(); tileOverlap=iTileOverlap; uInt i; for (i=0;i=0.0, AipsError); AlwaysAssert(tileOverlap(i)<1.0, AipsError); } tileOffsetVec.resize(tileShapeVec.nelements()); for (i=0;i LatticeCache::~LatticeCache() {} // Flush: write all extant tiles out template void LatticeCache::flush() { for(Int tile=0;tile-1) { writeTile(tile); } } } // Return a specified tile. If we cannot locate it in the // cache, then fill it into the cache. This is the prime // interface. If readonly is true then we discard the current // contents, otherwise we write them out, possibly adding to current // tile. template Array& LatticeCache::tile(IPosition& cacheLoc, const IPosition& tileLoc, Bool readonly) { cacheLoc=cacheLocation(cacheLoc, tileLoc); cacheAccesses++; Int foundTile=-1; for(Int tile=0;tile-1)&&(tileLocs[tile].isEqual(cacheLoc))) { foundTile=tile; break; } } // Keep track of hits and misses if(foundTile>-1) { cacheHits++; } else { cacheMisses++; foundTile=getFreeTile(readonly); AlwaysAssert(foundTile>-1, AipsError); tileLocs[foundTile]=cacheLoc; readTile(foundTile,readonly); } AlwaysAssert(foundTile>-1, AipsError); // Return the contents of this tile tileSequence[foundTile]=cacheAccesses; return tileContents[foundTile]; } // Return a specified tile. If we cannot locate it in the // cache, then fill it into the cache. This is the prime // interface. If readonly is true then we discard the current // contents, otherwise we write them out, possibly adding to current // tile. template Array& LatticeCache::tile(const IPosition& tileLoc, Bool readonly) { IPosition cacheLoc; return tile(cacheLoc, tileLoc, readonly); } // Const version template const Array& LatticeCache::tile(const IPosition& tileLoc) { return tile(tileLoc, True); } // Print the Cache Statistics template void LatticeCache::showCacheStatistics(ostream &os) { os<<"Cache Statistics"<shape()< void LatticeCache::clearCacheStatistics() { cacheAccesses=0; cacheHits=0; cacheMisses=0; cacheReads=0; cacheWrites=0; } // Find the cache location (i.e. only on a grid). template IPosition& LatticeCache::cacheLocation(IPosition& cacheLoc, const IPosition& tileLoc) { for (uInt i=0;i0) { Int loco=tileLoc(i); Int loc=loco; loc-=loc%tileOffsetVec(i); if((loco-loc)>=3*tileShapeVec(i)/4) loc+=tileOffsetVec(i); if((loco-loc)< tileShapeVec(i)/4) loc-=tileOffsetVec(i); if((loco-loc)<0) loc-=tileOffsetVec(i); if(loc<0) loc+=tileOffsetVec(i); cacheLoc(i)=loc; } else { cacheLoc(i)=0; } } return cacheLoc; } // ****************************************************************** // Start of private functions // ****************************************************************** // Write a specified tile template void LatticeCache::writeTile(Int tile) { tileSequence[tile]=cacheAccesses; if(additive) { Array tileOnDisk(tileContents[tile].shape()); tileOnDisk=0.0; image_p->getSlice(tileOnDisk, tileLocs[tile], tileContents[tile].shape(), IPosition(tileShape.nelements(), 1)); tileContents[tile]+=tileOnDisk; cacheReads++; } image_p->putSlice(tileContents[tile], tileLocs[tile], IPosition(tileShape.nelements(), 1)); cacheWrites++; } // Read a specified tile and validate it template void LatticeCache::readTile(Int tile, Bool readonly) { tileSequence[tile]=cacheAccesses; AlwaysAssert(tileLocs[tile].conform(tileShape), AipsError); Vector endLocVec=(tileLocs[tile]+tileShape).asVector(); Vector imageShapeVec=image_p->shape().asVector(); for (uInt i=0;igetSlice(tileContents[tile], tileLocs[tile], actualShape, IPosition(tileShape.nelements(), 1)); cacheReads++; } } // Get a free tile. The contents are undefined since // we will overwrite them immediately anyway. If readonly is // True then we discard the current contents iso possibly // writing them out. This is needed for a const version of tile. template Int LatticeCache::getFreeTile(Bool readonly) { Int foundTile=-1; // First search for unallocated tiles for(Int tile=0;tile0)&&(tileSequence[tile]-1, AipsError); if(!readonly) { writeTile(foundTile); } tileSequence[foundTile]=-1; } AlwaysAssert(foundTile>-1, AipsError); return foundTile; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/Lattices/LatticeConcat.h000066400000000000000000000233041476623553700215740ustar00rootroot00000000000000//# LatticeConcat.h: concatenate lattices along an axis //# Copyright (C) 1996,1997,1998,1999,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICECONCAT_H #define LATTICES_LATTICECONCAT_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class IPosition; class Slicer; // // Concatenates lattices along a specified axis // // // // // //
      • MaskedLattice (base class) // // // This is a class designed to concatenate lattices along a specified axis // // // This is a class designed to concatenate lattices along a specified // axis. This means you can join them together. E.g., // join lattices of shape [10,20,30] and [10,20,40] into a lattice // of shape [10,20,70]. // // In addition, you can increase the dimensionality // and join lattices [10,20] and [10,20] to [10,20,2]. This is // done by specifying the concatenation axis to be higher than // currently exists in the input lattices // // The LatticeConcat object does not copy the input lattices, it // just references them. You can use the Lattice::copyData(Lattice) // function to fill an output lattice with the concatenated input lattices. // // If you use the putSlice function, be aware that it will change the // underlying lattices if they are writable. // // // // // //// Make ArrayLattices // // ArrayLattice al1(a1); al1.set(1.0); // ArrayLattice al2(a2); al2.set(10.0); // //// Turn these into MaskedLattices // // SubLattice ml1(al1, True); // SubLattice ml2(al2, True); // //// Concatenate along axis 1 // // LatticeConcat lc (1); // lc.setLattice(ml1); // lc.setLattice(ml2); // //// Make output // // ArrayLattice al3(lc.shape()); // SubLattice ml3(al3, True); // //// Copy data to output (mask has to be copied separately) // // ml3.copyData(lc); // // // // In this example no masks are involved. See tLatticeConcat // for more examples. // // // // Image concatentation is a useful enduser requirement. An object of // this class is contained by an ImageConcat object. // // // template class LatticeConcat : public MaskedLattice { public: // Constructor. Argument axis specifies the concatenation // axis (0 relative). If this is one more than the number of axes // in the input lattices (set with function setLattice) // then the resultant concatenated lattice has dimension // one greater than that the input lattices. // Argument tempClose specifies whether you wish // all internal lattice copies to be // opened/closed on demand, rather than just being left open. // This prevents open file limits being reached LatticeConcat (uInt axis, Bool tempClose=True); // Default constructor. Sets the concatenation axis to 0 // and tempClose is True LatticeConcat (); // Copy constructor (reference semantics) LatticeConcat(const LatticeConcat &other); // Destructor virtual ~LatticeConcat (); // Assignment operator (reference semantics) LatticeConcat &operator=(const LatticeConcat &other); // Adds a clone of the lattice to the list to be concatenated. // Exception thrown if lattices are incompatible void setLattice (MaskedLattice& lattice); // Return the number of lattices set so far uInt nlattices() const {return lattices_p.nelements();} // Returns the current concatenation axis (0 relative) uInt axis () const {return axis_p;} // Set the tempClose state. void setTempClose (Bool tmpClose) { tempClose_p = tmpClose; } // Returns the tempClose constructor state Bool isTempClose () const {return tempClose_p;} // Returns the number of dimensions of the *input* lattices (may be different // by one from output lattice). Returns 0 if none yet set. uInt latticeDim() const; // Return pointer for specified lattice. Do not delete it. MaskedLattice* lattice(uInt i) const { return lattices_p[i]; } // Handle the (un)locking and syncing, etc. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; virtual void resync(); virtual void flush(); virtual void tempClose(); virtual void reopen(); // // Close/reopen a specific lattice. It is your responsibility to leave the // LatticeConcat object in a fully closed state. So always pair // a reopen with a tempClose. // void tempClose(uInt which); void reopen(uInt which); // // Name. Since many lattices may go into the concatenation, the name // is rather meaningless. Returns the string "Concatenation :" virtual String name (Bool stripPath=False) const; // Make a copy of the derived object (reference semantics). virtual LatticeConcat* cloneML() const; // Has the object really a mask? virtual Bool isMasked() const; // Get the region used (always returns 0). virtual const LatticeRegion* getRegionPtr() const; // If all of the underlying lattices are writable returns True virtual Bool isWritable() const; // Does the lattice have a pixelmask? virtual Bool hasPixelMask() const; // Get access to the pixelmask. // An exception is thrown if the lattice does not have a pixelmask // virtual const Lattice& pixelMask() const; virtual Lattice& pixelMask(); // // Find the shape that the concatenated lattice will be. // Returns a null IPosition if function setLattice has not yet // been called virtual IPosition shape () const; // Return the best cursor shape. This isn't very meaningful for a LatticeConcat // Lattice since it isn't on disk ! But if you do copy it out, this is // what you should use. The maxPixels aregument is ignored. virtual IPosition doNiceCursorShape (uInt maxPixels) const; // Do the actual get of the data. // The return value is always False, thus the buffer does not reference // another array. Generally the user should use function getSlice virtual Bool doGetSlice (Array& buffer, const Slicer& section); // Do the actual get of the mask data. // The return value is always False, thus the buffer does not reference // another array. Generally the user should use function getMaskSlice virtual Bool doGetMaskSlice (Array& buffer, const Slicer& section); // Do the actual put of the data into the Lattice. This will change the underlying // lattices (if they are writable) that were used to create the // LatticeConcat object. It throws an exception if not writable. // Generally the user should use function putSlice virtual void doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride); private: PtrBlock* > lattices_p; uInt axis_p; IPosition shape_p; Bool isMasked_p, dimUpOne_p, tempClose_p; LatticeConcat* pPixelMask_p; // void checkAxis(uInt axis, uInt ndim) const; // void setup1 (IPosition& blc, IPosition& trc, IPosition& stride, IPosition& blc2, IPosition& trc2, IPosition& blc3, IPosition& trc3, IPosition& stride3, const Slicer& section); Slicer setup2 (Bool& first, IPosition& blc2, IPosition& trc2, Int shape2, Int axis, const IPosition& blc, const IPosition& trc, const IPosition& stride, Int start); Bool getSlice1 (Array& buffer, const Slicer& section, uInt nLattices); Bool getSlice2 (Array& buffer, const Slicer& section, uInt nLattices); Bool putSlice1 (const Array& buffer, const IPosition& where, const IPosition& stride, uInt nLattices); Bool putSlice2 (const Array& buffer, const IPosition& where, const IPosition& stride, uInt nLattices); Bool getMaskSlice1 (Array& buffer, const Slicer& section, uInt nLattices); Bool getMaskSlice2 (Array& buffer, const Slicer& section, uInt nLattices); }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/Lattices/LatticeConcat.tcc000066400000000000000000000546341476623553700221300ustar00rootroot00000000000000//# LatticeConcat.cc: concatenate lattices //# Copyright (C) 1995,1997,1998,1999,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICECONCAT_TCC #define LATTICES_LATTICECONCAT_TCC #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template LatticeConcat::LatticeConcat() : axis_p(0), shape_p(IPosition(0)), isMasked_p(False), dimUpOne_p(False), tempClose_p(True), pPixelMask_p(0) { } template LatticeConcat::LatticeConcat(uInt axis, Bool tempClose) : axis_p(axis), shape_p(IPosition(0)), isMasked_p(False), dimUpOne_p(False), tempClose_p(tempClose), pPixelMask_p(0) { } template LatticeConcat::LatticeConcat (const LatticeConcat&other) : MaskedLattice(), lattices_p(other.lattices_p.nelements()), axis_p (other.axis_p), shape_p(other.shape_p), isMasked_p(other.isMasked_p), dimUpOne_p(other.dimUpOne_p), tempClose_p(other.tempClose_p), pPixelMask_p(0) { const uInt n = lattices_p.nelements(); for (uInt i=0; icloneML(); if (tempClose_p) lattices_p[i]->tempClose(); } if (other.pPixelMask_p!=0) { pPixelMask_p = other.pPixelMask_p->cloneML(); } } template LatticeConcat::~LatticeConcat() { const uInt n = lattices_p.nelements(); for (uInt i=0; i LatticeConcat& LatticeConcat::operator= (const LatticeConcat& other) { if (this != &other) { axis_p = other.axis_p; shape_p = other.shape_p; isMasked_p = other.isMasked_p; dimUpOne_p = other.dimUpOne_p; tempClose_p = other.tempClose_p; // uInt n = lattices_p.nelements(); for (uInt j=0; jcloneML(); if (tempClose_p) lattices_p[i]->tempClose(); } // delete pPixelMask_p; pPixelMask_p = 0; if (other.pPixelMask_p!=0) { pPixelMask_p = other.pPixelMask_p->cloneML(); } } // return *this; } template void LatticeConcat::setLattice(MaskedLattice& lattice) { const uInt n = lattices_p.nelements(); const uInt ndim = lattice.ndim(); dimUpOne_p = (axis_p==ndim); // // Check for consistency // if (n==0) { checkAxis(axis_p, ndim); // if (dimUpOne_p) { // Increasing dimensionality by one IPosition shape = lattice.shape(); shape_p = IPosition(ndim+1); shape_p.setFirst(shape); shape_p(ndim) = 1; } else { shape_p = lattice.shape(); } } else { if (dimUpOne_p) { // Increasing dimensionality by one IPosition shape = shape_p.getFirst(ndim); if (!shape.isEqual(lattice.shape())) { throw (AipsError("Lattice shapes inconsistent")); } shape_p(ndim) += 1; } else { // Dimensionality the same if (shape_p.nelements() != ndim) { throw(AipsError("Lattice dimensions are inconsistent")); } // const IPosition shape = lattice.shape(); for (uInt i=0; i(axis_p, tempClose_p); for (uInt i=0; i tmp = LCBox (lattices_p[i]->shape()); pPixelMask_p->setLattice (tmp); } } SubLattice tmp(lattice.pixelMask(), True); pPixelMask_p->setLattice (tmp); } else { if (pPixelMask_p != 0) { SubLattice tmp = LCBox (lattice.shape()); pPixelMask_p->setLattice (tmp); } } // Close this lattice if (tempClose_p) lattices_p[n]->tempClose(); } template uInt LatticeConcat::latticeDim() const { if (dimUpOne_p) { return shape_p.nelements(); } else { return shape_p.nelements() - 1; } } //Public virtual functions template String LatticeConcat::name (Bool) const { return "Concatenation :"; } template LatticeConcat* LatticeConcat::cloneML() const { return new LatticeConcat(*this); } template Bool LatticeConcat::isMasked() const { return isMasked_p; } template const LatticeRegion* LatticeConcat::getRegionPtr() const { return 0; } template Bool LatticeConcat::isWritable() const { const uInt n = lattices_p.nelements(); for (uInt i=0; iisWritable()) return False; } return True; } template Bool LatticeConcat::hasPixelMask() const { return pPixelMask_p != 0; } template const Lattice& LatticeConcat::pixelMask() const { if (pPixelMask_p == 0) { throw (AipsError ("LatticeConcat::pixelMask - no mask attached")); } return (*pPixelMask_p); } template Lattice& LatticeConcat::pixelMask() { if (pPixelMask_p == 0) { throw (AipsError ("LatticeConcat::pixelMask - no mask attached")); } return (*pPixelMask_p); } template IPosition LatticeConcat::shape() const { return shape_p; } template IPosition LatticeConcat::doNiceCursorShape (uInt) const // // This isn't very meaningful for a LatticeConcat // object since it isn't on disk ! But if you do // copy it out, this is what you should use.. // { TiledShape ts(shape()); return ts.tileShape(); } template Bool LatticeConcat::doGetSlice (Array& buffer, const Slicer& section) { const uInt nLattices = lattices_p.nelements(); if (nLattices==0) { throw (AipsError("No lattices set - use function setLattice")); } // Bool ok = False; if (dimUpOne_p) { // Increase dimensionality by one ok = getSlice1 (buffer, section, nLattices); } else { // No dimensionality increase ok = getSlice2(buffer, section, nLattices); } // return ok; } template Bool LatticeConcat::doGetMaskSlice (Array& buffer, const Slicer& section) { const uInt nLattices = lattices_p.nelements(); if (nLattices==0) { throw (AipsError("No lattices set - use function setLattice")); } // Bool ok = False; if (isMasked_p) { if (dimUpOne_p) { // Increase dimensionality by one ok = getMaskSlice1 (buffer, section, nLattices); } else { // No dimensionality increase ok = getMaskSlice2 (buffer, section, nLattices); } } else { buffer.resize (section.length()); buffer = True; ok = True; } // return ok; } template void LatticeConcat::doPutSlice (const Array& buffer, const IPosition& where, const IPosition& stride) { const uInt nLattices = lattices_p.nelements(); if (nLattices==0) { throw (AipsError("No lattices set - use function setLattice")); } // if (!isWritable()) { throw(AipsError("Some of the underlying lattices are not writable")); } // if (dimUpOne_p) { // Increase dimensionality by one putSlice1 (buffer, where, stride, nLattices); } else { // No dimensionality increase putSlice2 (buffer, where, stride, nLattices); } } template Bool LatticeConcat::lock (FileLocker::LockType type, uInt nattempts) { const uInt n = lattices_p.nelements(); Vector hadReadLock(n); Vector hadWriteLock(n); // for (uInt i=0; ihasLock(FileLocker::Read); hadWriteLock(i) = lattices_p[i]->hasLock(FileLocker::Write); if (!lattices_p[i]->lock(type, nattempts)) { // Try to put things back how they were if fails for (uInt j=0; jlock(FileLocker::Read, 1); } else if (hadWriteLock(j)) { lattices_p[j]->lock(FileLocker::Write, 1); } else { lattices_p[j]->unlock(); } if (tempClose_p) lattices_p[j]->tempClose(); } if (tempClose_p) lattices_p[i]->tempClose(); return False; } if (tempClose_p) lattices_p[i]->tempClose(); } return True; } template void LatticeConcat::unlock() { const uInt n = lattices_p.nelements(); for (uInt i=0; iunlock(); } } template Bool LatticeConcat::hasLock (FileLocker::LockType type) const { const uInt n = lattices_p.nelements(); for (uInt i=0; ihasLock(type)) { return True; } } return False; } template void LatticeConcat::resync() { const uInt n = lattices_p.nelements(); for (uInt i=0; iresync(); } } template void LatticeConcat::flush() { const uInt n = lattices_p.nelements(); for (uInt i=0; iflush(); } } template void LatticeConcat::tempClose() { const uInt n = lattices_p.nelements(); for (uInt i=0; itempClose(); } } template void LatticeConcat::reopen() { const uInt n = lattices_p.nelements(); for (uInt i=0; ireopen(); } } template void LatticeConcat::tempClose(uInt which) { AlwaysAssert (whichtempClose(); } template void LatticeConcat::reopen(uInt which) { AlwaysAssert (whichreopen(); } // Private functions template void LatticeConcat::checkAxis(uInt axis, uInt ndim) const { // Allow the possibility to add one higher dimension. if (axis > ndim) { throw(AipsError("Axis number and lattice dimension are inconsistent")); } } template void LatticeConcat::setup1 (IPosition& blc, IPosition& trc, IPosition& stride, IPosition& blc2, IPosition& trc2, IPosition& blc3, IPosition& trc3, IPosition& stride3, const Slicer& section) { // The Slicer section for the whole concatenated lattice blc = section.start(); trc = section.end(); stride = section.stride(); // The Slicer section for an individual lattice contribution // in coordinates of that lattice blc2 = blc; trc2 = trc; // The Slicer section for an individual lattice contribution // in the output Array coordinates blc3 = blc; blc3 = 0; trc3 = section.length() - 1; stride3 = stride; stride3 = 1; } template Slicer LatticeConcat::setup2 (Bool& first, IPosition& blc2, IPosition& trc2, Int shape2, Int axis, const IPosition& blc, const IPosition& trc, const IPosition& stride, Int start) { // This lattice contributes to the slice. Find section // in local lattice[i] coordinates blc2(axis) = max(0,blc(axis)-start); trc2(axis) = min(trc(axis)-start,shape2-1); // Adjust blc for stride if not first lattice if (!first) { blc2(axis) += (start-blc(axis))%stride(axis); } first = False; // return Slicer(blc2, trc2, stride, Slicer::endIsLast); } template Bool LatticeConcat::getSlice1 (Array& buffer, const Slicer& section, uInt nLattices) { const uInt dimIn = axis_p; // The concatenated lattice section if (section.end()(axis_p)+1 > Int(nLattices)) { throw(AipsError("Number of lattices and requested slice are inconsistent")); } IPosition blc3(dimIn+1,0); IPosition trc3(section.length()-1); IPosition stride3(dimIn+1,1); // The underlying lattice section - it never changes Slicer section2(section.start().getFirst(dimIn), section.end().getFirst(dimIn), section.stride().getFirst(dimIn), Slicer::endIsLast); // buffer.resize(section.length()); // We are looping over the last axis of the concatenated lattice // Each input lattice contributes just one pixel to that axis uInt k = 0; for (Int i=section.start()(axis_p); i<=section.end()(axis_p); i+=section.stride()(axis_p)) { Array buf = lattices_p[i]->getSlice(section2); // blc3(axis_p) = k; trc3(axis_p) = k; buffer(blc3, trc3, stride3) = buf.addDegenerate(1); if (tempClose_p) lattices_p[i]->tempClose(); k++; } // Result is a copy return False; } template Bool LatticeConcat::getSlice2 (Array& buffer, const Slicer& section, uInt nLattices) { //cout << "blc, trc, stride=" << section.start() << section.end() << section.stride() << endl; // Setup positions IPosition blc, trc, stride; IPosition blc2, trc2; IPosition blc3, trc3, stride3; setup1 (blc, trc, stride, blc2, trc2, blc3, trc3, stride3, section); // buffer.resize(section.length()); //cout << "Buffer shape = " << buffer.shape() << endl; // Int start = 0; Bool first = True; Slicer section2; // for (uInt i=0; i buf0(buffer); lattices_p[i]->putSlice(buf0(blc3, trc3, stride3).nonDegenerate(axis_p-1), section2.start(), section2.stride()); if (tempClose_p) lattices_p[i]->tempClose(); k++; } // return True; } template Bool LatticeConcat::putSlice2 (const Array& buffer, const IPosition& where, const IPosition& strider, uInt nLattices) { // Make a Slicer for the region to be put so we can reuse the functions // for setting locations used in the getSlice functions // Objects blc and stride will duplicate where and strider respectively //cout << "Buffer shape = " << buffer.shape() << endl; Slicer section(where, buffer.shape(), strider, Slicer::endIsLength); //cout << "Section = " << section << endl; // Setup positions IPosition blc, trc, stride; IPosition blc2, trc2; IPosition blc3, trc3, stride3; setup1 (blc, trc, stride, blc2, trc2, blc3, trc3, stride3, section); // Int start = 0; Bool first = True; Slicer section2; // for (uInt i=0; i buf = lattices_p[i]->getMaskSlice(section2); buffer(blc3, trc3, stride3) = buf.addDegenerate(1); if (tempClose_p) lattices_p[i]->tempClose(); k++; } // Result is a copy return False; } template Bool LatticeConcat::getMaskSlice2 (Array& buffer, const Slicer& section, uInt nLattices) { // Setup positions IPosition blc, trc, stride; IPosition blc2, trc2; IPosition blc3, trc3, stride3; setup1 (blc, trc, stride, blc2, trc2, blc3, trc3, stride3, section); // buffer.resize(section.length()); // Int start = 0; Bool first = True; Slicer section2; // for (uInt i=0; ishape()(axis_p); Int end = start + shape2 - 1; // if (! (blc(axis_p)>end || trc(axis_p)getMaskSlice(section2); if (tempClose_p) lattices_p[i]->tempClose(); // blc3(axis_p) += section2.length()(axis_p); } start += shape2; } // Result is a copy return False; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/Lattices/LatticeIndexer.cc000066400000000000000000000345161476623553700221300ustar00rootroot00000000000000//# LatticeIndexer.cc: A class for stepping through (sub-)Lattices //# Copyright (C) 1994,1996,1997,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA.,1995 //# //# 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., 675 Mass Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LatticeIndexer::LatticeIndexer() : itsFullShape (IPosition(1,1)), itsNdim (1), itsShape (IPosition(1,1)), itsAxisInc (IPosition(1,1)), itsOffset (IPosition(1,0)) { DebugAssert(ok() == True, AipsError); } // Specify the size of the Lattice. Assume a full size sub-Lattice. LatticeIndexer::LatticeIndexer (const IPosition& shape) : itsFullShape (shape), itsNdim (shape.nelements()), itsShape (shape), itsAxisInc (shape.nelements(), 1), itsOffset (shape.nelements(), 0) { DebugAssert(ok() == True, AipsError); } // Specify a Lattice and define a sub-Lattice within it. LatticeIndexer::LatticeIndexer (const IPosition& shape, const IPosition& blc, const IPosition& trc, const IPosition& inc) : itsFullShape (shape), itsNdim (shape.nelements()), itsShape (shape), itsAxisInc (shape.nelements(), 1), itsOffset (shape.nelements(), 0) { DebugAssert (ok() == True, AipsError); AlwaysAssert (blc.nelements() == itsNdim, AipsError); AlwaysAssert (trc.nelements() == itsNdim, AipsError); AlwaysAssert (inc.nelements() == itsNdim, AipsError); for (uInt i=0; i= 0 && blc(i) < itsFullShape(i), AipsError); AlwaysAssert (trc(i) < itsFullShape(i) && trc(i) >= blc(i), AipsError); AlwaysAssert (inc(i) > 0 && inc(i) <= itsFullShape(i), AipsError); } itsOffset = blc; itsAxisInc = inc; itsShape = (trc - blc + inc) / inc; DebugAssert (ok() == True, AipsError); } // Copy constructor. This uses copy semantics. LatticeIndexer::LatticeIndexer (const LatticeIndexer& other) : itsFullShape (other.itsFullShape), itsNdim (other.itsNdim), itsShape (other.itsShape), itsAxisInc (other.itsAxisInc), itsOffset (other.itsOffset) { DebugAssert(ok() == True, AipsError); } // the destructor does nothing LatticeIndexer::~LatticeIndexer() { // does nothing } // Assignment operator. Uses copy semantics. LatticeIndexer& LatticeIndexer::operator= (const LatticeIndexer& other) { if (this != &other) { if (itsNdim != other.itsNdim) { itsNdim = other.itsNdim; itsFullShape.resize (itsNdim); itsShape.resize (itsNdim); itsAxisInc.resize (itsNdim); itsOffset.resize (itsNdim); } itsFullShape = other.itsFullShape; itsShape = other.itsShape; itsAxisInc = other.itsAxisInc; itsOffset = other.itsOffset; } DebugAssert(ok() == True, AipsError); return *this; } // function to change the shape of the Lattice. Resets the sub-Lattice to // fullsize. void LatticeIndexer::resize (const IPosition& newShape) { if (newShape.nelements() != itsNdim) { itsNdim = newShape.nelements(); itsFullShape.resize (itsNdim); itsShape.resize (itsNdim); itsAxisInc.resize (itsNdim); itsOffset.resize (itsNdim); } itsFullShape = newShape; itsShape = itsFullShape; itsAxisInc = 1; itsOffset = 0; DebugAssert(ok() == True, AipsError); } // Returns the length of the requested axis in the parent Lattice uInt LatticeIndexer::fullShape (uInt axis) const { DebugAssert (ok() == True, AipsError); AlwaysAssert (axis < itsNdim, AipsError); return itsFullShape(axis); } // Returns the length of the requested axis in the sub-Lattice uInt LatticeIndexer::shape (uInt axis) const { DebugAssert (ok() == True, AipsError); AlwaysAssert (axis < itsNdim, AipsError); return itsShape(axis); } // function to return the increments along the requested axis of the // Lattice. uInt LatticeIndexer::increment (uInt axis) const { DebugAssert (ok() == True, AipsError); AlwaysAssert (axis < itsNdim, AipsError); return itsAxisInc(axis); } // function to return the offset on the specified axes between the // sub-Lattice and the parent one. uInt LatticeIndexer::offset (uInt axis) const { DebugAssert (ok() == True, AipsError); AlwaysAssert (axis < itsNdim, AipsError); return itsOffset(axis); } // Revert from a sub-Lattice description back to the main Lattice. This is // the only way to "increase" the the size of the sub-Lattice used by the // LatticeIndexer. void LatticeIndexer::fullSize() { itsShape = itsFullShape; itsAxisInc = 1; itsOffset = 0; DebugAssert (ok() == True, AipsError); } // function which increments (incr=True) or decrements (incr=False) the // cursor position (the first IPosition argument) by a cursor shape (the // second IPosition argument), tiling to the next/previous axis if // necessary. The path of movement is based upon the third IPosition // argument (a cursor heading) that is zero-based e.g. IPosition(3,0,2,1) // implies starting movement along the x-axis, then the z-axis, and then // the y-axis. Returns a value of False if the beginning/end of the // sub-Lattice is reached. The cursorPosition is relative to the origin of // the sub-Lattice. To get its location relative to the main Lattice use // the absolutePosition() function. Bool LatticeIndexer::tiledCursorMove (Bool incr, IPosition& cursorPos, const IPosition& cursorShape, const IPosition& cursorHeading) const { // this function performs IPosition addition/subtraction // ie. cursorPos += cursorShape but it makes sure that some pixels are // within the this Layout's shape (hereafter known as "boundary"). It // wraps (or "carries" to use the grade school arithmetic term) the result // onto the next axis when necessary, and returns true or false depending // on whether or not the addition worked. Failure occurs when the // addition/subtraction would move the cursor so that no pixels in it are // within the boundary, and axis-wrapping has been exhaused. // some key local variables: // activeAxis: // in incrementing [0,0] by [2,2] to produce [2,0], the zeroth // axis is "active". if the resulting cursor overflows the underlying // lattice, then 1st axis becomes the activeAxis. (this can go on // until the cursor fits, or until all the axes are exhausted.) // candidateCursorPos: // preliminary value for cursorPos += congruentCursorShape. // this is the "base" or "bottomLeftCorner" of the new cursor. DebugAssert (ok() == True, AipsError); AlwaysAssert (cursorPos.nelements() == itsNdim, AipsError); AlwaysAssert (cursorShape.nelements() == itsNdim, AipsError); AlwaysAssert (cursorHeading.nelements() == itsNdim,AipsError); for (uInt i=0; i 0, AipsError); } uInt activeAxis; uInt indexToActiveAxis = 0; IPosition candidateCursorPos(cursorPos); while (indexToActiveAxis < itsNdim) { activeAxis = cursorHeading(indexToActiveAxis); if (incr) { candidateCursorPos(activeAxis) += cursorShape(activeAxis); } else { candidateCursorPos(activeAxis) -= cursorShape(activeAxis); } if ((candidateCursorPos(activeAxis) < itsShape(activeAxis)) && (candidateCursorPos(activeAxis) + cursorShape(activeAxis) > 0)) { cursorPos = candidateCursorPos; return True; } if (incr) { candidateCursorPos(activeAxis) -= ((candidateCursorPos(activeAxis) + cursorShape(activeAxis) - 1) / cursorShape(activeAxis)) * cursorShape(activeAxis); } else { candidateCursorPos(activeAxis) += ((itsShape(activeAxis) - candidateCursorPos(activeAxis) - 1) / cursorShape(activeAxis)) * cursorShape(activeAxis); } indexToActiveAxis++; } // while return False; } // function which returns a value of True if the IPosition argument // is within the sub-Lattice. Returns False if the IPosition argument is // outside the sub-Lattice or if the argument doesn't conform to the // data members. Bool LatticeIndexer::isInside (const IPosition& index) const { DebugAssert (ok() == True, AipsError); AlwaysAssert (index.nelements () == itsNdim, AipsError); for (uInt i=0; i= itsShape(i))) { return False; } } return True; } // function which subsections a LatticeIndexer. The argument IPositions // specify "bottom left" and "upper right" corners and axis increments // (which default to one). The origins are cumulative. i.e. specifying a // blc of (2,2), and then (1,1) results in the sub-Lattice having an // origin at pixel (3,3) in the parent Lattice. Similarly the increment is // cumulative, i.e. an increment of 2 on top of an increment of 3 results // in a total increment of 6. This function can only decrease the size of // the sub-Lattice (i.e. blc >= 0, and trc <= shape(), and inc >= 1). The // fullSize() function should be used to revert back to the maximum // possible Lattice size. Also note that the trc might not be used if an // integral number of increments does not end on the trc (in which case // the last position below the trc will be used). void LatticeIndexer::subSection (const IPosition& blc, const IPosition& trc, const IPosition& inc) { DebugAssert (ok() == True, AipsError); AlwaysAssert (blc.nelements() == itsNdim, AipsError); AlwaysAssert (trc.nelements() == itsNdim, AipsError); AlwaysAssert (inc.nelements() == itsNdim, AipsError); for (uInt i=0; i= 0, AipsError); AlwaysAssert (trc(i) < itsShape(i), AipsError); AlwaysAssert (blc(i) <= trc(i), AipsError); AlwaysAssert (inc(i) > 0 && inc(i) <= itsShape(i), AipsError); } itsShape = (trc-blc+inc) / inc; itsOffset = itsOffset + blc*itsAxisInc; itsAxisInc = itsAxisInc*inc; DebugAssert (ok() == True, AipsError); } void LatticeIndexer::subSection (const IPosition& blc, const IPosition& trc) { DebugAssert (ok() == True, AipsError); subSection (blc, trc, IPosition(itsNdim, 1)); } // function which returns an IPosition in the parent Lattice given an // IPostion in the sub-Lattice. Accounting is taken of any offsets and // increments caused by subSectioning. No checks are made to ensure the // supplied IPosition or the returned one are within the bounds of the // Lattice(s). IPosition LatticeIndexer::absolutePosition (const IPosition& position) const { DebugAssert (ok() == True, AipsError); AlwaysAssert (position.nelements () == itsNdim, AipsError); return itsOffset + position*itsAxisInc; } // function which returns True if all the elements in this // sub-Lattice, are arranged contiguously, // i.e. without any gaps caused by increments or subSectioning. // THIS FUNCTION IS NOT FINISHED YET. // Bool LatticeIndexer::isContiguous() const // { // DebugAssert(ok() == True, AipsError); // Bool checkDegenerate = False; // for (uInt i=0; i < itsNdim; i++) { // if (itsAxisInc(i) > 1) // checkDegenerate = True; // if (itsOffset(i) != 0 && i != 0) // return False; // if (checkDegenerate && shape(i) > 1) // return False; // } // return True; // } // Is this LatticeIndexer consistent, i.e. are the class invariants valid? // return True if every thing is fine otherwise return False Bool LatticeIndexer::ok() const { ostringstream str; str << "LatticeIndexer::ok - "; if (itsNdim == 0) { str << "zero dimensions"; throw AipsError (str.str()); return False; } if (itsFullShape.nelements() != itsNdim) { str << "lattice has " << itsFullShape.nelements() << " instead of " << itsNdim << " dimensions"; throw AipsError (str.str()); return False; } for (uInt i=0; i < itsNdim; i++) { if (itsFullShape(i) < 0) { str << "lattice shape " << itsFullShape << " has a negative element"; throw AipsError (str.str()); return False; } } if (itsAxisInc.nelements() != itsNdim) { str << "increments " << itsAxisInc << " are the wrong dimension (ie. not " << itsNdim << ')'; throw AipsError (String(str.str())); return False; } for (uInt j=0; j < itsNdim; j++) { if (itsAxisInc(j) <= 0 || itsAxisInc(j) > itsFullShape(j)) { str << "axis increments " << itsAxisInc << " are negative OR larger than lattice shape " << itsFullShape; throw AipsError (String(str.str())); return False; } } if (itsOffset.nelements() != itsNdim) { str << "offset " << itsOffset << " is the wrong dimension (ie. not " << itsNdim << ')'; throw AipsError (String(str.str())); return False; } for (uInt k=0; k < itsNdim; k++) { if (itsOffset(k) < 0 || itsOffset(k) >= itsFullShape(k)) { str << "offset " << itsOffset << " is larger than lattice shape " << itsFullShape << " or negative"; throw AipsError (String(str.str())); return False; } } if (itsShape.nelements() != itsNdim) { str << "sub-lattice shape " << itsShape << " has wrong number of dimensions (ie. not " << itsNdim << ')'; throw AipsError (String(str.str())); return False; } for (uInt m=0; m < itsNdim; m++) { if (itsShape(m) <= 0 || itsShape > itsFullShape(m)) { str << "sub-lattice shape " << itsShape << " is less than or equal to zero or larger than lattice shape " << itsFullShape; throw AipsError (String(str.str())); return False; } } return True; } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/Lattices/LatticeIndexer.h000066400000000000000000000243661476623553700217740ustar00rootroot00000000000000//# LatticeIndexer.h: A helper class for stepping through Lattices //# Copyright (C) 1994,1995,1996,1997,1998,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICEINDEXER_H #define LATTICES_LATTICEINDEXER_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A helper class for stepping through Lattices. // // // // // //
      • Lattice //
      • IPosition // // // This class does various calculations involved with indexing in // Lattices. LatticeIndexer is not a good name, but it is // better than the previous name of LatticeLayout. // // // A LatticeIndexer contains all the information necessary to define the // shape of a Lattice or sub-Lattice. It is currently a repository of // functions that provide indexing calculations. //

        // A sub-Lattice is a section of a Lattice defined by a bottom left corner // (blc), a top right corner (trc), and a step size or increment on each // axis. The blc and trc pixels will always be included in the sub-Lattice // if the step increment is one. If the step increment is greater than one, // the pixel in top right corner may not be included in the sub-Lattice. //

        // This class knows the shape of the parent Lattice (including all // degenerate axes), and allows the user to specify a sub-Lattice that is // embedded in the parent Lattice. The default sub-Lattice, if none is // specified, is one identical in shape to the main Lattice. //

        // A sub-Lattice can be defined on the Lattice by specifying a trc, blc, // and step increment using the subSection function, or the // appropriate constructor. A sub-Lattice must be smaller than (or the same // size as) the Lattice that it is derived from. A sub-Lattice can be further // created from an already existing sub-Lattice eg. //
        // If we have a 128 by 128 Lattice, we can specify the centre quarter by // using blc=[32,32] and trc=[95,95]. Then specifying a sub-Lattice of // blc=[0,0] and trc = [31,31] results in a sub-Lattice that has a blc // of [32,32] and trc of [63,63] with respect to the parent Lattice. //

        // The only way to increase the size of a sub-Lattice is to first revert to // the parent Lattice (using the fullSize function) and then // generate the new, bigger sub-Lattice. //

        // Indexing calculations (eg. the tiledCursorMove or the // isInside function) are performed on the specified sub-Lattice. //

        // The role of this class is to centralise the information and functions // needed to operate on sub-Lattices. It will normally be used by other // Lattice classes, and is currently used by navigator classes like // LatticeStepper. // // // The shape, structure or geometry of a lattice is quite separable from // its actual contents, and the operations you can do on the contents. Also, // there are operations which apply only to the layout such as subsectioning. // //# //# class LatticeIndexer { public: // Default constructor (one dimensional, unit-length instance). LatticeIndexer(); // Specify the size of the Lattice. Assume a full size sub-Lattice. explicit LatticeIndexer (const IPosition& shape); // Specify a Lattice and define a sub-Lattice within it. LatticeIndexer (const IPosition& shape, const IPosition& blc, const IPosition& trc, const IPosition& inc); // The copy constructor uses copy semantics. LatticeIndexer (const LatticeIndexer& other); ~LatticeIndexer(); // The assignment operator uses copy semantics. LatticeIndexer& operator= (const LatticeIndexer& other); // Function to change the shape of the Lattice. Resets the sub-Lattice to // fullsize. void resize (const IPosition& newShape); // Returns the length of each axis (or the requested one) in the parent // Lattice. // const IPosition& fullShape() const; uInt fullShape (uInt axis) const; // // Returns the length of each axis (or the requested one) in the sub-Lattice. // const IPosition& shape() const; uInt shape (uInt axis) const; // // Function to return the increments along each axis (or the requested // one) of the Lattice. // const IPosition& increment() const; uInt increment (uInt axis) const; // // Function to return the offset (on a specified axis) between the // sub-Lattice and the parent one. // const IPosition& offset() const; uInt offset (uInt axis) const; // // Function which returns the number of dimensions in the Lattice (or // sub-Lattice). uInt ndim() const; // Revert from a sub-Lattice description back to the main Lattice. This is // the only way to "increase" the the size of the sub-Lattice used by the // LatticeIndexer. void fullSize(); // Function which returns the number of elements in the sub-Lattice; // this value is equal to the product of shape(). size_t nelements() const; // Function which increments (incr=True) or decrements (incr=False) the // cursor position (the first IPosition argument) by a cursor shape (the // second IPosition argument), tiling to the next/previous axis if // necessary. The path of movement is based upon the third IPosition // argument (a cursor heading) that is zero-based e.g. IPosition(3,0,2,1) // implies starting movement along the x-axis, then the z-axis, and then // the y-axis. Returns a value of False if the beginning/end of the // sub-Lattice is reached. The cursorPosition is relative to the origin of // the sub-Lattice. To get its location relative to the main Lattice use // the absolutePosition() function. Bool tiledCursorMove (Bool incr, IPosition& cursorPos, const IPosition& cursorShape, const IPosition& cursorHeading) const; // Function which returns a value of True if the IPosition argument // is within the sub-Lattice. Returns False if the IPosition argument is // outside the sub-Lattice or if the argument doesn't conform to the // data members. // Due to zero-origins, an index argument equal to the // shape of this sub-Lattice lies outside and returns False. // Bool isInside (const IPosition& index) const; // Function which subsections a LatticeIndexer. The argument IPositions // specify "bottom left" and "upper right" corners and axis increments // (which default to one). The origins are cumulative. i.e. specifying a // blc of (2,2), and then (1,1) results in the sub-Lattice having an // origin at pixel (3,3) in the parent Lattice. Similarly the increment is // cumulative, i.e. an increment of 2 on top of an increment of 3 results // in a total increment of 6. This function can only decrease the size of // the sub-Lattice (i.e. blc >= 0, and trc <= shape(), and inc >= 1). The // fullSize() function should be used to revert back to the maximum // possible Lattice size. Also note that the trc might not be used if an // integral number of increments does not end on the trc (in which case // the last position below the trc will be used). // void subSection (const IPosition& blc, const IPosition& trc, const IPosition& inc); void subSection (const IPosition& blc, const IPosition& trc); // // Function which returns an IPosition in the parent Lattice given an // IPostion in the sub-Lattice. Accounting is taken of any offsets and // increments caused by subSectioning. No checks are made to ensure the // supplied IPosition or the returned one are within the bounds of the // Lattice(s). IPosition absolutePosition (const IPosition& position) const; //# function which returns True if all the elements in this //# LatticeIndexer, or LatticeIndexer subsection, are arranged contiguously, //# i.e. without any gaps caused by increments or subSectioning. //# Bool isContiguous() const; // Is this LatticeIndexer consistent, i.e. are the class invariants valid? // Returns True if every thing is fine otherwise returns False Bool ok() const; private: IPosition itsFullShape; //# Size of the main-Lattice. uInt itsNdim; //# Number of dimensions in the main/sub-Lattice IPosition itsShape; //# Shape of the sub-Lattice IPosition itsAxisInc; //# Increment along each axis of main Lattice IPosition itsOffset; //# Offset between a sub-Lattice and the main one. }; inline const IPosition& LatticeIndexer::fullShape() const { return itsFullShape; } inline const IPosition& LatticeIndexer::shape() const { return itsShape; } inline const IPosition& LatticeIndexer::increment() const { return itsAxisInc; } inline const IPosition& LatticeIndexer::offset() const { return itsOffset; } inline uInt LatticeIndexer::ndim() const { return itsNdim; } inline size_t LatticeIndexer::nelements() const { return itsShape.product(); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/Lattices/LatticeIterInterface.h000066400000000000000000000270061476623553700231140ustar00rootroot00000000000000//# LatticeIterInterface.h: A base class for Lattice iterators //# Copyright (C) 1994,1995,1996,1997,1998,1999,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICEITERINTERFACE_H #define LATTICES_LATTICEITERINTERFACE_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class Lattice; template class LatticeIterator; template class RO_LatticeIterator; //

        // A base class for Lattice iterators // // // // // //
      • letter/envelope schemes - see Coplien, "Advanced C++", ch 5.5 //
      • Lattice //
      • LatticeIterator // // // The LatticeIterInterface class name reflects its role as the abstract // base class for concrete read-write LatticeIterators // // // This class is only for authors of Lattice letters for the LatticeIterator // envelope. General users should see LatticeIterator. // // The LatticeIterInterface class defines an abstract base for the standard // methods of iteration required by Lattices. Declaring an Iterator that is // derived from this class forces it to meet the virtual requirements. // // The author of a Lattice derived class should consider the following: //
          //
        • The LatticeStepper class has strong effects on how the cursor is // filled. A non-integral shape of the cursor may allow a step of // iteration to be only partially "touching" the Lattice. We have dubbed // this "hangover." //
        • If the cursor has "hangover" it should be filled with a value that // indicates the cursor is in undefined space. //
        • The cursor cannot be a reference to a part of the Lattice since // hangover would imply a reference to undefined memory. To enclose the // Lattice with a zero valued hangover buffer would be inefficient. The // method thus forced upon the programmer is to "update" the cursor with // Lattice values after each move or iteration and to "write" the possibly // changed cursor values back into the Lattice before each iteration. An // algorithm which does the cursor update/write actions (and is independent // of Lattice dimensionality) may be copied from ArrLatticeIter::cursorUpdate() // and ArrLatticeIter::cursorWrite(), respectively. //
        • The majority of the code in a new letter for LatticeIterator may be // cut and pasted from other implementations of letters. See ArrLatticeIter // or PagedArrIter. //
        //
        // // For an example see LatticeIterator. // // // The is class provides a tidy base for letter/envelope techniques of // iteration. // // //
      • IPositions are returned by value. This a reflection of the // LatticeNavigator base class' inability to predict the // availibility of data members for references. // template class LatticeIterInterface { friend class Lattice; friend class LatticeIterator; friend class RO_LatticeIterator; public: // Construct with the given navigator. LatticeIterInterface (const Lattice& lattice, const LatticeNavigator& navigator, Bool useRef); // A virtual destructor. A virtual is needed to ensure that derived // classes declared as pointers to a LatticeIterInterface will scope their // destructor to the derived class destructor. virtual ~LatticeIterInterface(); protected: // Default constructor (for derived classes). LatticeIterInterface(); // Copy constructor (copy semantics). LatticeIterInterface (const LatticeIterInterface& other); // Assignment (copy semantics). LatticeIterInterface& operator= (const LatticeIterInterface& other); // Clone the object. virtual LatticeIterInterface* clone() const; // Return the underlying lattice. Lattice& lattice() { return *itsLattPtr; } // Increment operator - increment the cursor to the next position. The // implementation of the prefix operator calls the postfix one. // Bool operator++(); Bool operator++(int); // // Decrement operator - decrement the cursor to the previous position. The // implementation of the prefix operator calls the postfix one. // Bool operator--(); Bool operator--(int); // // Function which resets the cursor to the beginning of the Lattice and // resets the number of steps taken to zero. void reset(); // Function which returns a value of "True" if the cursor is at the // beginning of the Lattice, otherwise, returns "False" Bool atStart() const; // Function which returns "True" if the cursor has been incremented to // the end of the lattice, otherwise, returns "False" Bool atEnd() const; // Function to return the number of steps (increments or decrements) taken // since construction (or since last reset). This is a running count of // all cursor movement since doing N increments followed by N decrements // does not necessarily put the cursor back at the origin of the Lattice. uInt nsteps() const; // Function which returns the current position of the beginning of the // cursor within the Lattice. The returned IPosition will have the same // number of axes as the underlying Lattice. IPosition position() const; // Function which returns the current position of the end of the // cursor. The returned IPosition will have the same number of axes as the // underlying Lattice. IPosition endPosition() const; // Function which returns the shape of the Lattice being iterated through. // The returned IPosition will always have the same number of axes as the // underlying Lattice. IPosition latticeShape() const; // Function which returns the shape of the cursor which is iterating // through the Lattice. The cursor will always have as many dimensions as // the Lattice. IPosition cursorShape() const; // Functions which returns a window to the data in the Lattice. These are // used to read the data within the Lattice. Use the function // that is appropriate to the current cursor dimension, AFTER REMOVING // DEGENERATE AXES, or use the cursor function which works with // any number of dimensions in the cursor. A call of the function whose // return value is inappropriate with respect to the current cursor // dimension will throw an exception (AipsError). //
        The doRead flag indicates if the data need to be read or // if only a cursor with the correct shape has to be returned. //
        The autoRewrite flag indicates if the data has to be // rewritten when the iterator state changes (e.g. moved, destructed). // virtual Vector& vectorCursor (Bool doRead, Bool autoRewrite); virtual Matrix& matrixCursor (Bool doRead, Bool autoRewrite); virtual Cube& cubeCursor (Bool doRead, Bool autoRewrite); virtual Array& cursor (Bool doRead, Bool autoRewrite); // // Function which checks the internals of the class for consistency. // Returns True if everything is fine otherwise returns False. The default // implementation of this function always returns True. Bool ok() const; protected: // Do the actual read of the data. virtual void readData (Bool doRead); // Rewrite the cursor data and clear the rewrite flag. virtual void rewriteData(); // Update the cursor for the next chunk of data (resize if needed). virtual void cursorUpdate(); // Allocate the internal buffer. void allocateBuffer(); // Allocate the nondegenerate array with the correct type. void allocateCurPtr(); // Synchronise the storage of itsCurPtr with itsCursor. void setCurPtr2Cursor(); // Copy the base data of the other object. void copyBase (const LatticeIterInterface& other); // Pointer to the method of Lattice transversal LatticeNavigator* itsNavPtr; // Pointer to the Lattice Lattice* itsLattPtr; // A buffer to hold the data. Usually itsCursor shares the data // with this buffer, but for an ArrayLattice itsCursor might reference // the lattice directly instead of making a copy in the buffer. Array itsBuffer; // Polymorphic pointer to the data in itsCursor. Array* itsCurPtr; // An Array which references the same data as the itsCurPtr, but has all // the degenerate axes. This is an optimization to avoid the overhead of // having to add the degenerate axes for each iteration. Array itsCursor; // Keep a reference to the data (if possible). Bool itsUseRef; // Is the cursor a reference to the lattice? Bool itsIsRef; // Have the data been read after a cursor update? (False=not read) Bool itsHaveRead; // Rewrite the cursor data before moving or destructing? Bool itsRewrite; // The axes forming the cursor. IPosition itsCursorAxes; }; template inline Bool LatticeIterInterface::operator++() { return operator++ (0); } template inline Bool LatticeIterInterface::operator--() { return operator-- (0); } template inline Bool LatticeIterInterface::atStart() const { return itsNavPtr->atStart(); } template inline Bool LatticeIterInterface::atEnd() const { return itsNavPtr->atEnd(); } template inline uInt LatticeIterInterface::nsteps() const { return itsNavPtr->nsteps(); } template inline IPosition LatticeIterInterface::position() const { return itsNavPtr->position(); } template inline IPosition LatticeIterInterface::endPosition() const { return itsNavPtr->endPosition(); } template inline IPosition LatticeIterInterface::latticeShape() const { return itsNavPtr->latticeShape(); } template inline IPosition LatticeIterInterface::cursorShape() const { return itsNavPtr->cursorShape(); } //# Declare extern templates for often used types. extern template class LatticeIterInterface; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/Lattices/LatticeIterInterface.tcc000066400000000000000000000304141476623553700234330ustar00rootroot00000000000000//# LatticeIterInterface.cc: A base class for concrete Lattice iterators //# Copyright (C) 1995,1997,1998,1999,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICEITERINTERFACE_TCC #define LATTICES_LATTICEITERINTERFACE_TCC #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template LatticeIterInterface::LatticeIterInterface() : itsNavPtr (0), itsLattPtr (0), itsCurPtr (0), itsUseRef (False), itsIsRef (False), itsHaveRead (False), itsRewrite (False) {} template LatticeIterInterface::LatticeIterInterface (const Lattice& lattice, const LatticeNavigator& nav, Bool useRef) : itsNavPtr (nav.clone()), itsLattPtr (lattice.clone()), itsUseRef (useRef && lattice.canReferenceArray()), itsIsRef (False), itsHaveRead (False), itsRewrite (False), itsCursorAxes (nav.cursorAxes()) { allocateCurPtr(); if (!itsUseRef) { allocateBuffer(); } DebugAssert(ok() == True, AipsError); } template LatticeIterInterface::LatticeIterInterface (const LatticeIterInterface& other) : itsCurPtr (0) { copyBase (other); DebugAssert(ok() == True, AipsError); } template LatticeIterInterface::~LatticeIterInterface() { rewriteData(); delete itsCurPtr; delete itsNavPtr; delete itsLattPtr; } template LatticeIterInterface& LatticeIterInterface::operator= (const LatticeIterInterface& other) { if (this != &other) { rewriteData(); copyBase (other); } DebugAssert(ok() == True, AipsError); return *this; } template void LatticeIterInterface::copyBase (const LatticeIterInterface& other) { delete itsCurPtr; itsCurPtr = 0; itsBuffer.resize(); itsCursorAxes.resize(0); itsNavPtr = other.itsNavPtr->clone(); itsLattPtr = other.itsLattPtr->clone(); itsUseRef = other.itsUseRef; itsIsRef = other.itsIsRef; itsHaveRead = other.itsHaveRead; itsRewrite = False; itsCursorAxes = other.itsCursorAxes; allocateCurPtr(); if (!itsIsRef) { allocateBuffer(); if (itsHaveRead) { itsBuffer = other.itsBuffer; } } else { Array tmp(other.itsCursor); itsCursor.reference (tmp); setCurPtr2Cursor(); } } template LatticeIterInterface* LatticeIterInterface::clone() const { return new LatticeIterInterface (*this); } template Bool LatticeIterInterface::operator++(int) { if (itsRewrite) { rewriteData(); } Bool moved = itsNavPtr->operator++(); if (moved) { cursorUpdate(); } DebugAssert(ok() == True, AipsError); return moved; } template Bool LatticeIterInterface::operator--(int) { if (itsRewrite) { rewriteData(); } Bool moved = itsNavPtr->operator--(); if (moved) { cursorUpdate(); } DebugAssert(ok() == True, AipsError); return moved; } template void LatticeIterInterface::reset() { rewriteData(); itsNavPtr->reset(); cursorUpdate(); DebugAssert(ok() == True, AipsError); } template Vector& LatticeIterInterface::vectorCursor (Bool doRead, Bool autoRewrite) { DebugAssert(ok() == True, AipsError); if (itsCurPtr->ndim() != 1) { throw(AipsError("LatticeIterInterface::vectorCursor" " - check the cursor has only one non-degenerate axis")); } if (!itsHaveRead) { readData (doRead); } if (autoRewrite) { itsRewrite = True; } return *(Vector*)itsCurPtr; } template Matrix& LatticeIterInterface::matrixCursor (Bool doRead, Bool autoRewrite) { DebugAssert(ok() == True, AipsError); if (itsCurPtr->ndim() != 2) { throw(AipsError("LatticeIterInterface::matrixCursor" " - check the cursor has only two non-degenerate axes")); } if (!itsHaveRead) { readData (doRead); } if (autoRewrite) { itsRewrite = True; } return *(Matrix*)itsCurPtr; } template Cube& LatticeIterInterface::cubeCursor (Bool doRead, Bool autoRewrite) { DebugAssert(ok() == True, AipsError); if (itsCurPtr->ndim() != 3) { throw(AipsError("LatticeIterInterface::cubeCursor" " - check the cursor has only three non-degenerate axes")); } if (!itsHaveRead) { readData (doRead); } if (autoRewrite) { itsRewrite = True; } return *(Cube*)itsCurPtr; } template Array& LatticeIterInterface::cursor (Bool doRead, Bool autoRewrite) { DebugAssert(ok() == True, AipsError); if (!itsHaveRead) { readData (doRead); } if (autoRewrite) { itsRewrite = True; } return itsCursor; } template void LatticeIterInterface::readData (Bool doRead) { if (doRead || itsUseRef) { const IPosition shape = itsNavPtr->cursorShape(); const IPosition start = itsNavPtr->position(); const IPosition incr = itsNavPtr->increment(); IPosition extractShape; Bool hangOver = itsNavPtr->hangOver(); if (hangOver) { extractShape = 1 + (itsNavPtr->endPosition() - start) / incr; if (extractShape == shape) { hangOver = False; } } if (!hangOver) { // No hangover, so get entire slice. if (itsUseRef) { // Set the cursor as a reference to the original array. itsIsRef = itsLattPtr->getSlice (itsCursor, start, shape, incr); DebugAssert (itsIsRef, AipsError); setCurPtr2Cursor(); } else { itsIsRef = False; if (doRead) { // Use a temporary array pointing to the same storage as itsCursor. // When getSlice returns a reference, tmp is pointing to that // referenced storage, so we have to copy the data. Array tmp (itsCursor); Bool isARef = itsLattPtr->getSlice (tmp, start, shape, incr); if (isARef) { itsCursor = tmp; } } } } else { itsIsRef = False; if (itsUseRef) { allocateBuffer(); } T overHangVal; defaultValue(overHangVal); itsBuffer = overHangVal; // Fill in the appropriate region with the bit that does not overhang. // Use the same method as above to deal with possible references. const uInt nrdim = extractShape.nelements(); Array subArr(itsCursor(IPosition(nrdim, 0), extractShape-1)); Bool isARef = itsLattPtr->getSlice (subArr, start, extractShape, incr); if (isARef) { itsCursor(IPosition(nrdim, 0), extractShape-1) = subArr; } } } itsHaveRead = True; } template void LatticeIterInterface::rewriteData() { if (itsRewrite) { DebugAssert (ok(), AipsError); // Check that both cursors point to the same data. if (itsCursor.data() != itsCurPtr->data()) { throw (AipsError ("LatticeIterInterface::rewriteData - " "the data pointer inside the cursor has been changed " "(probably by an Array::reference)")); } // Writing is only needed if the data was not referenced. if (!itsIsRef) { const IPosition start = itsNavPtr->position(); const IPosition incr = itsNavPtr->increment(); if (itsNavPtr->hangOver() == False) { itsLattPtr->putSlice (itsCursor, start, incr); } else { // Write the appropriate region. IPosition extractShape = 1 + (itsNavPtr->endPosition() - start) / incr; const uInt nrdim = extractShape.nelements(); Array subArr(itsCursor(IPosition(nrdim, 0), extractShape-1)); itsLattPtr->putSlice (subArr, start, incr); } } itsRewrite = False; } } template void LatticeIterInterface::cursorUpdate() { // Set to data not read. itsHaveRead = False; itsIsRef = False; // Reshape the cursor array if needed. if (!itsUseRef && itsCursor.shape() != itsNavPtr->cursorShape()) { allocateBuffer(); } } template void LatticeIterInterface::allocateCurPtr() { const IPosition cursorShape(itsNavPtr->cursorShape()); const IPosition realShape(cursorShape.nonDegenerate(itsCursorAxes)); const uInt ndim = realShape.nelements(); AlwaysAssert(ndim > 0, AipsError); switch (ndim) { case 1: itsCurPtr = new Vector(); break; case 2: itsCurPtr = new Matrix(); break; case 3: itsCurPtr = new Cube(); break; default: itsCurPtr = new Array(); break; } } template void LatticeIterInterface::setCurPtr2Cursor() { if (itsCursor.data() != 0) { if (itsCurPtr->ndim() == itsCursor.ndim()) { itsCurPtr->reference (itsCursor); } else { Array tmp (itsCursor.nonDegenerate (itsCursorAxes)); itsCurPtr->reference (tmp); } } else { itsCurPtr->resize(); } } template void LatticeIterInterface::allocateBuffer() { // Do not reallocate the buffer if not really needed. // If the cursor gets smaller, the existing buffer can still be used. if (itsBuffer.nelements() == 0) { itsBuffer.resize (itsNavPtr->cursorShape()); } Bool isACopy; T* data = itsBuffer.getStorage(isACopy); DebugAssert(isACopy == False, AipsError); itsCursor.takeStorage (itsNavPtr->cursorShape(), data, SHARE); DebugAssert (itsBuffer.nelements() >= itsCursor.nelements(), AipsError); setCurPtr2Cursor(); } template Bool LatticeIterInterface::ok() const { String message; Bool flag = True; // Check that we have a pointer to a cursor and not a NULL pointer. if (itsCurPtr == 0) { message += "Cursor pointer is uninitialized\n"; flag = False; } // Check the cursor is OK (by calling its "ok" function). if (itsCurPtr->ok() == False) { message += "Cursor internals are inconsistent\n"; flag = False; } // Do the same for the Array cursor if (itsCursor.ok() == False) { message += "Array Cursor internals are inconsistent\n"; flag = False; } // Check that both cursors have the same number of elements if (itsCursor.nelements() != itsCurPtr->nelements()) { message += "Cursors have inconsistent lengths\n"; flag = False; } // Check that both cursors point to the same data. if (itsCursor.data() != itsCurPtr->data()) { message += "Cursors contain different data\n"; flag = False; } // Check that we have a pointer to a navigator and not a NULL pointer. if (itsNavPtr == 0) { message += "Navigator pointer is uninitialized\n"; flag = False; } // Check the navigator is OK (by calling its "ok" function). if (itsNavPtr->ok() == False) { message += "Navigator internals are inconsistent\n"; flag = False; } // Check the Navigator and Lattice are the same shape if (!(itsNavPtr->latticeShape().isEqual(itsLattPtr->shape()))) { message += "Navigator Lattice and Data Lattice have different shapes\n"; flag = False; } // We do not check if the Navigator cursor and itsCursor are the same shape, // because ArrLatticeIter resizes the cursor only when it is being used. if (!flag) { throw AipsError ("LatticeIterInterface::ok - " + message); } return flag; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/Lattices/LatticeIterator.h000066400000000000000000000543411476623553700221630ustar00rootroot00000000000000//# LatticeIterator.h: Iterators for Lattices: readonly or read/write //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICEITERATOR_H #define LATTICES_LATTICEITERATOR_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class IPosition; class LatticeNavigator; // // A readonly iterator for Lattices // // // // // //
      • Lattice //
      • LatticeNavigator //
      • Array // // // The leading "RO" is shorthand for "readonly", which indicates that an // RO_LatticeIterator is used for traversing a Lattice, examining and // possibly extracting its contents, but not for modifying it. // // // This class provides a convenient way to traverse any class derived from // Lattice. You can iterate through the Lattice's data from "start" to "end" // by calling operator++, and reverse direction by calling // operator--. You can return immediately to the beginning by // calling the reset function. The RO_LatticeIterator gives the // user the opportunity to methodically walk through the data, in an // efficient way. //

        // The supplied LatticeNavigator // determines how to step through the Lattice. It can, for instance, // be line by line, but it can also be in a more complicated way. // When no navigator is supplied, a default navigator will be used // which steps in the optimum way. //

        // A cursor (which is an Array object) is // used to return the data for each step in the iteration process. // Depending on the navigator used the cursor can have a different shape // for each step of the iteration. This is especially true when the // end of an axis is reached for a non-integrally fitting cursor shape. //
        The cursor() function returns an Array which has the same // dimensionality as the Lattice. It is, however, also possible to get // an Array with a lower dimensionality by using the correct function // in the group vectorCursor(), matrixCursor(), and // cubeCursor(). Those functions remove (some) degenerated axes // resulting in a vector, matrix or cube. // When, for example, a LatticeStepper with shape [64,1,1] is used, the // vectorCursor() can be used. It will remove the degenerated // axes (length 1) and return the cursor as a Vector object. Note that // matrixCursor() cannot be used, because removing the degenerated // axes results in a 1D array. //

        // Generally iterators should not be long-lived objects - create new ones // when needed rather than keeping one around for a long time to be // reused. This is because the cache memory used by the cursor will be // released when the iterator is destroyed. //

        // The purpose of this class is to hide the possibly complicated // implementation and structure of the Lattice classes, and allow you to // iterate through a Lattice with the same ease as one iterates through a // Fortran or C vector. For example, and assuming that initialization has // been done properly, here's a typical 'for' loop: // // // code omitted which associates Lattice object and the iterator // for (iterator.reset(); !iterator.atEnd(); iterator++) { // meanValue = mean(iterator.cursor()); // } // // The iterator's cursor() member function returns a reference to // that part of the Lattice data which is presently "seen" by the // LatticeIterator. //

        // Before explaining the initialization of an iterator, the LatticeNavigator // class must be further introduced. This is an abstract base class, from which // concrete navigators are derived. After one of these is created, you // attach it to the LatticeIterator, and it provides a specific technique // for navigating through the Lattice. Different navigators deliver // different traversal schemes. The most basic is // LatticeStepper, which // moves a specified shape sequentially through the Lattice -- for example, // by moving one plane at a time, front to back, through a cube. Another // (future) navigator might be designed to move a small, 2-dimensional plane // through a cube, centering each iteration on the brightest pixel of the // cube's plane, and ignoring the darker regions of the cube. //

        // The performance and memory usage of an iteration through a lattice // (in particular through a PagedArray) // depends very heavily on the navigator used. Currently there are three // navigators available: //

          //
        1. LatticeStepper steps // sequentially through a lattice with the given cursor shape. // This can use a lot of memory for the PagedArray cache. //
        2. TiledLineStepper // steps line by line through a lattice. However, it is doing that // in such a way that as few tiles as possible need to kept in the // PagedArray cache. This reduces memory usage considerably. //
        3. TileStepper steps tile // by tile through a lattice. This navigator requires a PagedArray cache // of 1 tile only. However, it can only be used for application in which // the iteration order is not important (e.g. addition, determining max). //
        // The class LatticeApply is very useful // to iterate through a Lattice while applying an algorithm. It makes it // possible for the user to concentrate on the algorithm. //

        // Here's a typical iterator declaration: // // RO_LatticeIterator iterator(pagedArray, stepper); // // The template identifier Float defines the data type of // Array object that will be the iterator's cursor. //
        // The pagedArray constructor argument names a PagedArray object, // which is what the iterator will traverse. The stepper // argument is a LatticeStepper which defines the method of iteration. // // When passed the name of a previously created PagedArray stored on disk, // this function will traverse the whole array, and report the average value // of all of the elements. Imagine that the filename contains a PagedArray // with dimension 64 x 64 x 8. // // void demonstrateIterator (const String& filename) // { // PagedArray pagedArray(filename); // IPosition latticeShape = pagedArray.shape(); // cout << "paged array has shape: " << latticeShape << endl; // // // Construct the iterator. since we only want to read the PagedArray, // // use the read-only class, which disallows writing back to the cursor. // // No navigator is given, so the default TileStepper is used // // which ensures optimum performance. // RO_LatticeIterator iterator(pagedArray); // // // Add for each iteration step the sum of the cursor elements to the sum. // // Note that the cursor is an Array object and that the function sum // // is defined in ArrayMath.h. // Float runningSum = 0.0; // for (iterator.reset(); !iterator.atEnd(); iterator++) { // runningSum += sum(iterator.cursor()); // } // cout << "average value, from demonstrateIterator: " // << runningSum / latticeShape.product() << endl; // } // // // // Iterator classes are a standard feature in C++ libraries -- they // provide convenience and allow the implementation of the "iteratee" // to be kept hidden. // //# //#

      • //# template class RO_LatticeIterator { public: // The default constructor creates an empty object which is practically // unusable. // It can only be used as the source or target of an assignment. It can // also be used as the source for the copy constructor and the copy function. // Other functions do not check if the object is empty and will usually // give a segmentation fault. // The function isNull() can be used to test if the object is empty. RO_LatticeIterator(); // Construct the Iterator with the supplied data. // It uses a TileStepper as the default iteration strategy. // useRef=True means that if possible the cursor arrays returned // reference the data in the underlying lattice. This is only possible // for ArrayLattice objects (or e.g. a SubLattice using it). explicit RO_LatticeIterator (const Lattice& data, Bool useRef=True); // Construct the Iterator with the supplied data, and iteration strategy RO_LatticeIterator (const Lattice& data, const LatticeNavigator& method, Bool useRef=True); // Construct the Iterator with the supplied data. // It uses a LatticeStepper with the supplied cursor shape as the // iteration strategy. RO_LatticeIterator (const Lattice& data, const IPosition& cursorShape, Bool useRef=True); // The copy constructor uses reference semantics (ie. NO real copy is made). // The function copy can be used to make a true copy. RO_LatticeIterator (const RO_LatticeIterator& other); // Destructor (cleans up dangling references and releases memory) ~RO_LatticeIterator(); // Assignment uses reference semantics (ie. NO real copy is made). // The function copy can be used to make a true copy. RO_LatticeIterator& operator= (const RO_LatticeIterator& other); // Make a copy of the iterator object. // This means that an independent navigator object is created to // be able to iterate independently through the same Lattice. // The position in the copied navigator is the same as the original. // The reset function has to be used to start at the beginning. //
        Note that if the Lattice uses a cache (e.g. PagedArray), the // cache is shared by the iterators. RO_LatticeIterator copy() const; // Is the iterator object empty? Bool isNull() const { return !itsIterPtr; } // Return the underlying lattice. Lattice& lattice() const { return itsIterPtr->lattice(); } // Increment operator - increment the cursor to the next position. These // functions are forwarded to the current LatticeNavigator and both // postfix and prefix versions will do the same thing. //
        They return True if the cursor moved (which should always be the // case if the iterator is not at the end). // Bool operator++(); Bool operator++(int); // // Decrement operator - decrement the cursor to the previous // position. These functions are forwarded to the current LatticeNavigator // and both postfix and prefix versions will do the same thing. //
        They return True if the cursor moved (which should always be the // case if the iterator is not at the start). // Bool operator--(); Bool operator--(int); // // Function which resets the cursor to the beginning of the Lattice and // resets the number of steps taken to zero. void reset(); // Function which returns a value of "True" if the cursor is at the // beginning of the Lattice, otherwise, returns "False". Bool atStart() const; // Function which returns a value of "True" if an attempt has been made // to move the cursor beyond the end of the Lattice. Bool atEnd() const; // Function to return the number of steps (increments or decrements) taken // since construction (or since last reset). This is a running count of // all cursor movement, thus doing N increments followed by N decrements // results in 2N steps. uInt nsteps() const; // Function which returns the current position of the beginning of the // cursor within the Lattice. The returned IPosition will have the same // number of axes as the underlying Lattice. IPosition position() const; // Function which returns the current position of the end of the // cursor. The returned IPosition will have the same number of axes as the // underlying Lattice. IPosition endPosition() const; // Function which returns the shape of the Lattice being iterated through. // The returned IPosition will always have the same number of axes as the // underlying Lattice. IPosition latticeShape() const; // Function which returns the shape of the cursor which is iterating // through the Lattice. The returned IPosition will have the same number // of axes as the underlying Lattice. IPosition cursorShape() const; // Functions which returns a window to the data in the Lattice. These are // used to read the data within the Lattice. Use the function that is // appropriate to the current cursor dimension, AFTER REMOVING DEGENERATE // AXES, or use the cursor function which works with any number // of dimensions in the cursor. A call of the function whose return value // is inappropriate with respect to the current cursor dimension will // throw an exception (AipsError). // const Vector& vectorCursor() const; const Matrix& matrixCursor() const; const Cube& cubeCursor() const; const Array& cursor() const; // // Function which checks the internals of the class for consistency. // Returns True if everything is fine otherwise returns False. Bool ok() const; protected: // The pointer to the Iterator std::shared_ptr> itsIterPtr; }; // // A read/write lattice iterator // // // // // //
      • RO_LatticeIterator //
      • Lattice //
      • LatticeNavigator //
      • Array // // // LatticeIterator differs from the RO_LatticeIterator class in that // the window into the Lattice data which moves with each iterative step may // be used to alter the Lattice data itself. The moving "cursor" gives the // user the door to reach in and change the basic Lattice before moving to // another section of the Lattice. //

        // LatticeIterator can be used in 3 ways: //
        - For readonly purposes using the cursor() functions. Note that if // the entire iteration is readonly, it is better to use an // RO_LatticeIterator object. //
        - To update (part of)the contents of the lattice (e.g. clip the value // of some pixels). For this purpose the rwCursor functions // should be used. They read the data (if not read yet) and mark the // cursor for write. //
        - To fill the lattice. For this purpose the woCursor // functions should be used. They do not read the data, but only mark the // cursor for write. //

        // When needed, writing the cursor data is done automatically when the // cursor position changes or when the iterator is destructed. // // // Here's an iterator that runs through a cube, assigning every element // of each plane of the cube a value equal to the number of the plane. // See LatticeStepper for an // explanation of the navigator used here. // // PagedArray pa("someName"); // IPosition windowShape(2,pa.shape(0), pa.shape(1)); // LatticeStepper stepper(pa.shape(), windowShape); // LatticeIterator iterator(pa, stepper); // Int planeNumber = 0; // for (iterator.reset(); !iterator.atEnd(); iterator++) { // iterator.woCursor() = planeNumber++; // } // // // Here's an iterator that runs through a cube, subtracting the mean from // each line of the cube with a mean < 0. // See TiledLineStepper for an // explanation of the navigator used here. // // PagedArray pa("someName"); // TiledLineStepper stepper(pa.shape(), pa.niceCursorShape(), 0); // LatticeIterator iterator(pa, stepper); // Int planeNumber = 0; // for (iterator.reset(); !iterator.atEnd(); iterator++) { // Float meanLine = mean(iterator.cursor()); // if (meanLine < 0) { // iterator.rwCursor() -= meanLine; // } // } // // Note that in this last example no more vectors than required are written. // This is achieved by using the readonly function cursor in // the test and using rwCursor only when data needs to be changed. //
        Note that rwCursor does not read the data again. They are // still readily available. //
        template class LatticeIterator : public RO_LatticeIterator { public: // The default constructor creates an empty object which is practically // unusable. // It can only be used as the source or target of an assignment. It can // also be used as the source for the copy constructor and the copy function. // Other functions do not check if the object is empty and will usually // give a segmentation fault. // The function isNull() can be used to test if the object is empty. LatticeIterator(); // Construct the Iterator with the supplied data. // It uses a TileStepper as the default iteration strategy. // useRef=True means that if possible the cursor arrays returned // reference the data in the underlying lattice. This is only possible // for ArrayLattice objects (or e.g. a SubLattice using it). explicit LatticeIterator (Lattice& data, Bool useRef=True); // Construct the Iterator with the supplied data, and iteration strategy LatticeIterator (Lattice& data, const LatticeNavigator& method, Bool useRef=True); // Iterate through the data with a LatticeStepper that has uses the // supplied cursorShape. LatticeIterator (Lattice& data, const IPosition& cursorShape, Bool useRef=True); // The copy constructor uses reference semantics (ie. NO real copy is made). // The function copy can be used to make a true copy. LatticeIterator (const LatticeIterator& other); // destructor (cleans up dangling references and releases memory) ~LatticeIterator(); // Assignment uses reference semantics (ie. NO real copy is made). // The function copy can be used to make a true copy. LatticeIterator& operator= (const LatticeIterator& other); // Make a copy of the iterator object. // This means that an independent navigator object is created to // be able to iterate independently through the same Lattice. // The position in the copied navigator is the same as the original. // The reset function has to be used to start at the beginning. //
        Note that if the Lattice uses a cache (e.g. PagedArray), the // cache is shared by the iterators. LatticeIterator copy() const; // Functions to return a window to the data in the Lattice. Use the function // that is appropriate to the current cursor dimension, AFTER REMOVING // DEGENERATE AXES, or use the cursor function which works with // any number of dimensions in the cursor. A call of the function whose // return value is inappropriate with respect to the current cursor // dimension will throw an exception (AipsError) (e.g. VectorCursor // cannot be used when the cursor is 2D). //
        // When the iterator state changes (e.g. by moving, destruction) the // data are automatically rewritten before the iterator state is changed. //
        The rw (read/write) versions should be used to read the // data first. They are useful to update a lattice. // The wo (writeonly) versions do not read the data. // They only return a cursor of the correct shape and are useful to // fill a lattice. Note that it sets the state to 'data read'. I.e., // a subsequent call to, say, cursor() does not read the // data, which would destroy the contents of the cursor which may // just be filled by the user. // Vector& rwVectorCursor(); Matrix& rwMatrixCursor(); Cube& rwCubeCursor(); Array& rwCursor(); Vector& woVectorCursor(); Matrix& woMatrixCursor(); Cube& woCubeCursor(); Array& woCursor(); // // Function which checks the internals of the class for consistency. // Returns True if everything is fine. Otherwise returns False. Bool ok() const; //# Make members of parent class known. public: using RO_LatticeIterator::isNull; using RO_LatticeIterator::position; using RO_LatticeIterator::endPosition; using RO_LatticeIterator::cursorShape; protected: using RO_LatticeIterator::itsIterPtr; }; } //# NAMESPACE CASACORE - END //# See comments in Lattice.h why Lattice.tcc is included here. #ifndef CASACORE_NO_AUTO_TEMPLATES #include #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/Lattices/LatticeIterator.tcc000066400000000000000000000177331476623553700225110ustar00rootroot00000000000000//# LatticeIterator.cc: defines the RO_LatticeIterator and LatticeIterator classes //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICEITERATOR_TCC #define LATTICES_LATTICEITERATOR_TCC #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template RO_LatticeIterator::RO_LatticeIterator() { DebugAssert(ok(), AipsError); } template RO_LatticeIterator::RO_LatticeIterator (const Lattice& lattice, Bool useRef) : itsIterPtr (lattice.makeIter (TileStepper (lattice.shape(), lattice.niceCursorShape()), useRef)) { DebugAssert(ok(), AipsError); } template RO_LatticeIterator::RO_LatticeIterator (const Lattice& lattice, const LatticeNavigator& method, Bool useRef) : itsIterPtr (lattice.makeIter (method, useRef)) { DebugAssert(ok(), AipsError); } template RO_LatticeIterator::RO_LatticeIterator (const Lattice& lattice, const IPosition& cursorShape, Bool useRef) : itsIterPtr (lattice.makeIter (LatticeStepper(lattice.shape(), cursorShape), useRef)) { DebugAssert(ok(), AipsError); } template RO_LatticeIterator::RO_LatticeIterator (const RO_LatticeIterator& other) : itsIterPtr (other.itsIterPtr) { DebugAssert(ok(), AipsError); } template RO_LatticeIterator::~RO_LatticeIterator() { // std::shared_ptr destructor takes care of deletion of the LatticeIterInterface } template RO_LatticeIterator& RO_LatticeIterator::operator= (const RO_LatticeIterator& other) { DebugAssert(ok(), AipsError); if (this != &other) { itsIterPtr = other.itsIterPtr; } return *this; } template RO_LatticeIterator RO_LatticeIterator::copy() const { RO_LatticeIterator tmp; if (!isNull()) { tmp.itsIterPtr.reset (itsIterPtr->clone()); } return tmp; } template Bool RO_LatticeIterator::operator++(int) { return itsIterPtr->operator++(0); } template Bool RO_LatticeIterator::operator++() { return itsIterPtr->operator++(); } template Bool RO_LatticeIterator::operator--(int) { return itsIterPtr->operator--(0); } template Bool RO_LatticeIterator::operator--() { return itsIterPtr->operator--(); } template void RO_LatticeIterator::reset() { itsIterPtr->reset(); } template Bool RO_LatticeIterator::atStart() const { return itsIterPtr->atStart(); } template Bool RO_LatticeIterator::atEnd() const { return itsIterPtr->atEnd(); } template uInt RO_LatticeIterator::nsteps() const { return itsIterPtr->nsteps(); } template IPosition RO_LatticeIterator::position() const { return itsIterPtr->position(); } template IPosition RO_LatticeIterator::endPosition() const { return itsIterPtr->endPosition(); } template IPosition RO_LatticeIterator::latticeShape() const { return itsIterPtr->latticeShape(); } template IPosition RO_LatticeIterator::cursorShape() const { return itsIterPtr->cursorShape(); } template const Vector& RO_LatticeIterator::vectorCursor() const { return itsIterPtr->vectorCursor (True, False); } template const Matrix& RO_LatticeIterator::matrixCursor() const { return itsIterPtr->matrixCursor (True, False); } template const Cube& RO_LatticeIterator::cubeCursor() const { return itsIterPtr->cubeCursor (True, False); } template const Array& RO_LatticeIterator::cursor() const { return itsIterPtr->cursor (True, False); } template Bool RO_LatticeIterator::ok() const { if (!isNull()) { if (! itsIterPtr->ok()) { throw AipsError ("The actual Lattice Iterator class is inconsistent"); return False; } } return True; } template LatticeIterator::LatticeIterator() {} template LatticeIterator::LatticeIterator (Lattice& lattice, Bool useRef) : RO_LatticeIterator (lattice, useRef) { if (! lattice.isWritable()) { throw (AipsError ("LatticeIterator cannot be constructed; " "lattice is not writable")); } } template LatticeIterator::LatticeIterator (Lattice& lattice, const LatticeNavigator& method, Bool useRef) : RO_LatticeIterator (lattice, method, useRef) { if (! lattice.isWritable()) { throw (AipsError ("LatticeIterator cannot be constructed; " "lattice is not writable")); } } template LatticeIterator::LatticeIterator (Lattice& lattice, const IPosition& cursorShape, Bool useRef) : RO_LatticeIterator (lattice, cursorShape, useRef) { if (! lattice.isWritable()) { throw (AipsError ("LatticeIterator cannot be constructed; " "lattice is not writable")); } } template LatticeIterator::LatticeIterator (const LatticeIterator& other) : RO_LatticeIterator (other) {} template LatticeIterator::~LatticeIterator() {} template LatticeIterator& LatticeIterator::operator= (const LatticeIterator& other) { RO_LatticeIterator::operator= (other); return *this; } template LatticeIterator LatticeIterator::copy() const { LatticeIterator tmp; if (!isNull()) { tmp.itsIterPtr.reset (itsIterPtr->clone()); } return tmp; } template Vector& LatticeIterator::rwVectorCursor() { return itsIterPtr->vectorCursor (True, True); } template Matrix& LatticeIterator::rwMatrixCursor() { return itsIterPtr->matrixCursor (True, True); } template Cube& LatticeIterator::rwCubeCursor() { return itsIterPtr->cubeCursor (True, True); } template Array& LatticeIterator::rwCursor() { return itsIterPtr->cursor (True, True); } template Vector& LatticeIterator::woVectorCursor() { return itsIterPtr->vectorCursor (False, True); } template Matrix& LatticeIterator::woMatrixCursor() { return itsIterPtr->matrixCursor (False, True); } template Cube& LatticeIterator::woCubeCursor() { return itsIterPtr->cubeCursor (False, True); } template Array& LatticeIterator::woCursor() { return itsIterPtr->cursor (False, True); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/Lattices/LatticeLocker.cc000066400000000000000000000042101476623553700217350ustar00rootroot00000000000000//# LatticeLocker.cc: Class to hold a (user) lock on a lattice //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LatticeLocker::LatticeLocker (LatticeBase& lattice, FileLocker::LockType type, uInt nattempts) : itsLatticePtr (&lattice), itsOwnLock (False), itsHadReadLock(False) { if (itsLatticePtr->hasLock (type)) { return; } itsHadReadLock = itsLatticePtr->hasLock (FileLocker::Read); if (! itsLatticePtr->lock (type, nattempts)) { String str = "write"; if (type == FileLocker::Read) { str = "read"; } throw (AipsError ("LatticeLocker: no " + str + " lock could be acquired on lattice " + itsLatticePtr->name())); } itsOwnLock = True; } LatticeLocker::~LatticeLocker() { if (itsOwnLock) { itsLatticePtr->unlock(); if (itsHadReadLock) { itsLatticePtr->lock (FileLocker::Read, 1); } } } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/Lattices/LatticeLocker.h000066400000000000000000000135621476623553700216110ustar00rootroot00000000000000//# LatticeLocker.h: Class to hold a (user) lock on a lattice //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICELOCKER_H #define LATTICES_LATTICELOCKER_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //

        // Class to hold a (user) lock on a lattice. // // // // // //# Classes you should understand before using this one. //
      • Lattice //
      • TableLock // // // Class LatticeLocker can be used to acquire a (user) lock on a lattice. // The lock can be a read or write lock. // The destructor releases the lock when needed. //

        // LatticeLocker simply uses the lock and unlock // function of class Lattice. // The advantage of LatticeLocker over these functions is that the // destructor of LatticeLocker is called automatically by the system, // so unlocking the lattice does not need to be done explicitly and // cannot be forgotten. Especially in case of exception handling this // can be quite an adavantage. //

        // This class is meant to be used with the UserLocking option. // It can, however, also be used with the other locking options. // In case of PermanentLocking(Wait) it won't do anything at all. // In case of AutoLocking it will acquire and release the lock when // needed. However, it is possible that the system releases an // auto lock before the LatticeLocker destructor is called. //

        // The constructor of LatticeLocker will look if the lattice is // already appropriately locked. If so, it will set a flag to // prevent the destructor from unlocking the lattice. In this way // nested locks can be used. I.e. one can safely use LatticeLocker // in a function without having to be afraid that its destructor // would undo a lock set in a higher function. //
        Similarly LatticeLocker will remember if a lattice was // already read-locked, when a write-lock is acquired. In such a // case the destructor will try to ensure that the lattice remains // read-locked. // // // // // Open a lattice to be updated. // PagedArray myLattice (Table ("theLattice", // LatticeLock::UserLocking, // Lattice::Update); // // Start of some critical section requiring a lock. // { // LatticeLocker lock1 (myLattice, FileLocker::Write); // ... write the data // } // // The LatticeLocker destructor invoked by } unlocks the table. // // // // LatticeLocker makes it easier to unlock a lattice. // It also makes it easier to use locking in a nested way. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class LatticeLocker { public: // The constructor acquires a read or write lock on a lattice. // If the lattice was already locked, the destructor will // not unlock the lattice. This means that the class can be used in // a nested way. //
        // The number of attempts (default = forever) can be specified when // acquiring the lock does not succeed immediately. When nattempts>1, // the system waits 1 second between each attempt, so nattempts // is more or less equal to a wait period in seconds. // An exception is thrown when the lock cannot be acquired. explicit LatticeLocker (LatticeBase& lattice, FileLocker::LockType, uInt nattempts = 0); // If the constructor acquired the lock, the destructor releases // the lock and flushes the data if changed. ~LatticeLocker(); // Has this process the read or write lock, thus can the table // be read or written safely? Bool hasLock (FileLocker::LockType) const; private: // The copy constructor and assignment are not possible. // Note that only one lock can be held on a lattice, so copying a // TableLocker object imposes great difficulties which object should // release the lock. // It can be solved by turning LatticeLocker into a handle class // with a reference counted body class. // However, that will only be done when the need arises. // LatticeLocker (const LatticeLocker&); LatticeLocker& operator= (const LatticeLocker&); // //# Variables. LatticeBase* itsLatticePtr; Bool itsOwnLock; Bool itsHadReadLock; }; inline Bool LatticeLocker::hasLock (FileLocker::LockType type) const { return itsLatticePtr->hasLock (type); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/Lattices/LatticeNavigator.cc000066400000000000000000000061301476623553700224530ustar00rootroot00000000000000//# LatticeNavigator.cc: an abstract base class to steer lattice iterators //# Copyright (C) 1994,1995,1996,1997,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LatticeNavigator::~LatticeNavigator() { // Nothing } IPosition LatticeNavigator::relativePosition() const { return (position() - blc()) / increment(); } IPosition LatticeNavigator::relativeEndPosition() const { return (endPosition() - blc()) / increment(); } IPosition LatticeNavigator::subLatticeShape() const { return latticeShape(); } IPosition LatticeNavigator::hangOverBlc() const { IPosition blc(relativePosition()); const uInt ndim = blc.nelements(); for (uInt n = 0; n < ndim; n++) if (blc(n) < 0) blc(n) = 0; return blc; } IPosition LatticeNavigator::hangOverTrc() const { IPosition trc(relativeEndPosition()); const IPosition latticeShape(subLatticeShape()); const uInt ndim = trc.nelements(); DebugAssert(latticeShape.nelements() == ndim, AipsError); for (uInt n = 0; n < ndim; n++) if (trc(n) >= latticeShape(n)) trc(n) = latticeShape(n) - 1; return trc; } void LatticeNavigator::subSection(const IPosition& blc, const IPosition& trc) { subSection(blc, trc, IPosition(latticeShape().nelements(),1)); } void LatticeNavigator::subSection(const IPosition&, const IPosition&, const IPosition&) { throw(AipsError("LatticeNavigator::subSection(blc, trc, inc)" " - sub-Lattice's are not supported")); } IPosition LatticeNavigator::blc() const { return IPosition(latticeShape().nelements(), 0); } IPosition LatticeNavigator::trc() const { return latticeShape() - 1; } IPosition LatticeNavigator::increment() const { return IPosition(latticeShape().nelements(), 1); } Bool LatticeNavigator::ok() const { return True; } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/Lattices/LatticeNavigator.h000066400000000000000000000374431476623553700223300ustar00rootroot00000000000000//# LatticeNavigator.h: Abstract base class to steer lattice iterators //# Copyright (C) 1994,1995,1996,1997,1998,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICENAVIGATOR_H #define LATTICES_LATTICENAVIGATOR_H //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class IPosition; class ROTiledStManAccessor; //

        // Abstract base class to steer lattice iterators. // // // // // //
      • LatticeIterator //
      • Lattice // // // Lattice iteration can proceed with a number of different strategies - // all of which answer the question: where do I go from here? // You could travel through by making calculations on the lattice subscripts, // viewing ascending planes in an image cube, for example, or you could // travel through by making calculations on the data, viewing small // subimage planes in order of descending brightness over the whole cube. // Concrete classes derived from this base class implement different // navigation strategies - but they are all "navigators". // // // This abstract base class defines the interface for objects which generate // positions for LatticeIterators. This position is not just a single point // in the Lattice but a region or "cursor" that is moved through the // Lattice. The LatticeIterator classes actually retrieve the data in the // cursor from the Lattice. The class decribed here (and those derived from it) // are responsible for moving the cursor to the next position and determining // its shape. // // There may eventually be a large collection of tools for traversing // Lattices. At this writing (December 1999) there are three concrete // classes derived from LatticeNavigator: // LatticeStepper, // TiledLineStepper, and // TileStepper. // // The LatticeStepper class moves through a Lattice in fixed // steps defined by the user specified cursor, incrementing to the next // portion of the Lattice with each step, and wrapping around axes as // needed. Other position generators might follow the brightest pixel, // traverse a number of predefined subregions, or change size automatically // when near the edges. // // The TiledLineStepper class moves a Vector cursor through a // Lattice, until all the lines in the set of tiles along the specified // axis have been exhausted. It then moves to the next set of tiles. This is // a memory-efficient way to move a Vector cursor through a Lattice. // // The most important member functions of this class are those which move // the cursor to the next position. These are the operator++ and // operator-- member functions, (in postfix and prefix forms). // // The cursor shape need not be constant as it moves through the Lattice, // but may change depending on its current position. For the LatticeStepper // and TiledLineStepper classes , however, the cursor shape is constant // as it steps through the Lattice. // // It is not possible to randomly move the cursor to an arbitrary place in // the Lattice, although the cursor can be moved to the starting position at // any time using the reset member function. // // The position of the cursor can be queried at any time using the // position member function. This gives the position of the // bottom left hand corner of the cursor. The position of the top right hand // corner of the cursor is obtained using the endPosition member // function, and the current cursor shape is obtained using the // cursorShape member function. Note that the endPosition // does not take an overhang into account. // // It is possible that for some positions of the cursor, part of it will // "hang over" the edge of the Lattice. When this occurs the // hangOver member function will return True. This will occur // with a LatticeStepper if the Lattice shape is not a multiple of the // cursor shape. Hangover cannot occur with the TiledLineStepper as the length // of the Vector cursor is defined by the Lattice Shape. // // It may be possible (depending on the concrete LatticeNavigator actually // used) to specify that only a region of the Lattice (defined by a top // right hand corner, bottom left hand corner, and step increment) be // traversed by the LatticeNavigator. This is done using the // subSection member function. At any time the region can be // redefined by calling the subSection function again. This // replaces the previously defined region with the new one. // // Using the subSection function always sets the cursor position to the // origin of the currently defined sub-lattice. This is a backdoor way to // move the cursor to random locations in the Lattice. // // It is an error to define a sub-lattice that is bigger than the current // Lattice. If using a LatticeStepper it may also be necessary to resize the // cursor (using the setCursorShape member function) prior to // calling the subSection function as the cursor cannot be bigger than the // sub-Lattice on any axis. // // The arguments (trc, blc and inc) // to the subSection function are always // relative to the main Lattice. This is also true of the position // and endPosition functions. To get the position of the cursor // relative to the currently defined sub-Lattice use the // relativePosition and relativeEndPosition member // functions. // // Many of the LatticeIterator member functions are directly forwarded to // virtual functions of this class, and classes derived from it. For // instance, LatticeIterator::operator++() calls // LatticeIterInterface->operator++() which calls // LatticeNavigator->operator++() which might resolve to // LatticeStepper->operator++(). Other functions like this are documented in // the LatticeIterator class. // // // See the examples in the // LatticeStepper class, the // TiledLineStepper class, and the // TileStepper class. // // // // Iterator classes are quite common in C++. What's novel about the design // which includes this class is the separation of iterator mechanisms from // traversal strategy. The iterator provides a lot of functionality: it // provides a cursor, damage notification and tracking, and reading and // writing to the underlying data structure. Traversal strategies can and // should be isolated from these things. Because every LatticeIterator // uses a Navigator, it gets the benefits of a derived concrete navigator // without getting involved in its mechanism. // // // //
      • Think about how to implement Navigators which can traverse // arbitrary shaped regions. // class LatticeNavigator { public: // Default constructor. LatticeNavigator() {;} // Copy constructor. LatticeNavigator (const LatticeNavigator&) {;} // Assignment. LatticeNavigator& operator= (const LatticeNavigator&) { return *this; } // A virtual destructor. A virtual is needed to ensure that derived // classes accessed through pointers to a LatticeNavigator will scope // their destructor to the derived class destructor. virtual ~LatticeNavigator(); // Increment operator - increment the cursor to the next position. The // implementation of the prefix operator calls the postfix one. // virtual Bool operator++(int) = 0; Bool operator++(); // // Decrement operator - decrement the cursor to the previous position. The // implementation of the prefix operator calls the postfix one. // virtual Bool operator--(int) = 0; Bool operator--(); // // Function to reset the cursor to the beginning of the Lattice and // reset the number of steps taken to zero. virtual void reset() = 0; // Function which returns "True" if the cursor is at the beginning of the // Lattice, otherwise, returns "False" virtual Bool atStart() const = 0; // Function which returns "True" if an attempt has been made to increment // the cursor beyond the end of the Lattice. virtual Bool atEnd() const = 0; // Function to return the number of steps (increments or decrements) taken // since construction (or since last reset). This is a running count of // all cursor movement since doing N increments followed by N decrements // does not necessarily put the cursor back at the origin of the Lattice. virtual uInt nsteps() const = 0; // Functions which return the current position of the beginning of the // cursor. The position function is relative to the origin in // the main Lattice and the relativePosition function is // relative to the origin and increment used in the sub-Lattice (defined // using the subSection function). // The returned IPosition will have the same number of axes as // the underlying Lattice. //
        The default implementation of the relativePosition // function returns (position() - blc()) / increment(). // virtual IPosition position() const = 0; virtual IPosition relativePosition() const; // // Functions which return the current position of the end of the // cursor. The endPosition function is relative to the origin in // the main Lattice and the relativeEndPosition function is // relative to the origin and increment used in the sub-Lattice (defined // using the subSection function). // The returned IPosition will have the same number of axes as // the underlying Lattice. // It returns the end position in the lattice and // does not take overhang into account. //
        The default implementation of the relativeEndPosition // function returns (endPosition() - blc()) / increment(). // virtual IPosition endPosition() const = 0; virtual IPosition relativeEndPosition() const; // // Functions which return the shape of the Lattice being iterated // through. latticeShape always returns the shape of the main // Lattice while subLatticeShape returns the shape of any // sub-Lattice defined using the subSection function. In the // default implementation of this class it is not possible to use the // subsection function (it throws an exception) so the default // implementation of the subLatticeShape function calls the // latticeShape function. The returned IPosition will always // have the same number of axes as the underlying Lattice. // virtual IPosition latticeShape() const = 0; virtual IPosition subLatticeShape() const; // // Function which returns the current shape of the cursor which is // iterating through the Lattice. The returned IPosition will have the // same number of axes as the underlying Lattice. virtual IPosition cursorShape() const = 0; // Function which returns the axes of the cursor. // These are the axes which should not be removed by the // iterator functions vectorCursor(), etc.. virtual IPosition cursorAxes() const = 0; // Function which returns "True" if the increment/decrement operators have // moved the cursor position such that part of the cursor is hanging over // the edge of the Lattice. This function may always return a value of // "False" for some iteration methods that do not move the cursor past the // Lattice boundaries. virtual Bool hangOver() const = 0; // Functions which return the "bottom left corner" and the "top right corner" // of the cursor that does not hangover. Use these functions to extract the // valid part of the cursor when the hangover member function is true. If // there is no hangover then hangOverBLC returns an IPosition of zero and // hangOverTRC() returns the cursorShape - 1; // virtual IPosition hangOverBlc() const; virtual IPosition hangOverTrc() const; // // Function to specify a "section" of the Lattice to Navigate over. A // section is defined in terms of the Bottom Left Corner (blc), Top Right // Corner (trc), and step size (inc), on ALL of its axes, including // degenerate axes. The step size defaults to one if not specified. // In the default implementation of this class subsectioning is not // supported and using the subsection function will throw an // exception (AipsError). // virtual void subSection(const IPosition& blc, const IPosition& trc); virtual void subSection(const IPosition& blc, const IPosition& trc, const IPosition& inc); // // Return the bottom left hand corner (blc), top right corner (trc) or // step size (increment) used by the current sub-Lattice. In the default // implementation of this class sub-sectioning is not supported and these // functions will always return blc=0, trc=latticeShape-1, increment=1, // ie. the entire Lattice. // virtual IPosition blc() const; virtual IPosition trc() const; virtual IPosition increment() const; // // Return the axis path. // See LatticeStepper for a // description and examples. virtual const IPosition& axisPath() const = 0; // Calculate the cache size (in tiles) for this type of access to a lattice // in the given row of the tiled hypercube. // A zero bucket size indicates that the data are not tiled, but in memory. // Then a cache size of 0 is returned. virtual uInt calcCacheSize (const IPosition& cubeShape, const IPosition& tileShape, uInt maxCacheSize, uInt bucketSize) const = 0; // Function which returns a pointer to dynamic memory of an exact copy // of this LatticeNavigator. It is the responsibility of the caller to // release this memory. virtual LatticeNavigator* clone() const = 0; // Function which checks the internals of the class for consistency. // Returns True if everything is fine otherwise returns False. The default // implementation always returns True. virtual Bool ok() const; }; inline Bool LatticeNavigator::operator++() { return operator++(0); } inline Bool LatticeNavigator::operator--() { return operator--(0); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/Lattices/LatticeStepper.cc000066400000000000000000000404571476623553700221550ustar00rootroot00000000000000//# LatticeStepper.cc: defines LatticeStepper class //# Copyright (C) 1994,1995,1996,1997,1998,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LatticeStepper::LatticeStepper (const IPosition& latticeShape, const IPosition& cursorShape, const uInt hangOverPolicy) : itsIndexer (latticeShape), itsCursorShape (latticeShape.nelements()), itsCursorPos (latticeShape.nelements(),0), itsAxisPath (IPosition::makeAxisPath(latticeShape.nelements())), itsNsteps (0), itsEnd (False), itsStart (True), itsNiceFit (False), itsHangover (False), itsPolicy (hangOverPolicy) { setCursorShape (cursorShape); DebugAssert (ok() == True, AipsError); } LatticeStepper::LatticeStepper (const IPosition& latticeShape, const IPosition& cursorShape, const IPosition& axisPath, const uInt hangOverPolicy) : itsIndexer (latticeShape), itsCursorShape (latticeShape.nelements()), itsCursorPos (latticeShape.nelements(), 0), itsAxisPath (IPosition::makeAxisPath(latticeShape.nelements(), axisPath)), itsNsteps (0), itsEnd (False), itsStart (True), itsNiceFit (False), itsHangover (False), itsPolicy (hangOverPolicy) { setCursorShape (cursorShape); DebugAssert (ok() == True, AipsError); } LatticeStepper::LatticeStepper (const IPosition& latticeShape, const IPosition& cursorShape, const IPosition& cursorAxes, const IPosition& axisPath, const uInt hangOverPolicy) : itsIndexer (latticeShape), itsCursorShape (latticeShape.nelements()), itsCursorPos (latticeShape.nelements(), 0), itsAxisPath (IPosition::makeAxisPath(latticeShape.nelements(), axisPath)), itsNsteps (0), itsEnd (False), itsStart (True), itsNiceFit (False), itsHangover (False), itsPolicy (hangOverPolicy) { setCursorShape (cursorShape, cursorAxes); DebugAssert (ok() == True, AipsError); } LatticeStepper::LatticeStepper (const LatticeStepper& other) : LatticeNavigator(), itsIndexer (other.itsIndexer), itsCursorAxes (other.itsCursorAxes), itsCursorShape (other.itsCursorShape), itsCursorPos (other.itsCursorPos), itsAxisPath (other.itsAxisPath), itsNsteps (other.itsNsteps), itsEnd (other.itsEnd), itsStart (other.itsStart), itsNiceFit (other.itsNiceFit), itsHangover (other.itsHangover), itsPolicy (other.itsPolicy) { DebugAssert(ok() == True, AipsError); } LatticeStepper::~LatticeStepper() { // does nothing } LatticeStepper& LatticeStepper::operator=(const LatticeStepper& other) { if (this != &other) { itsIndexer = other.itsIndexer; itsCursorAxes = other.itsCursorAxes; itsCursorShape = other.itsCursorShape; itsCursorPos = other.itsCursorPos; itsAxisPath = other.itsAxisPath; itsNsteps = other.itsNsteps; itsEnd = other.itsEnd; itsStart = other.itsStart; itsNiceFit = other.itsNiceFit; itsHangover = other.itsHangover; itsPolicy = other.itsPolicy; } DebugAssert (ok() == True, AipsError); return *this; } Bool LatticeStepper::operator++(int) { DebugAssert (ok() == True, AipsError); if (itsEnd) { return False; } // Increment the counter. itsNsteps++; // itsStart = false by definition when incrementing itsStart = False; Bool successful = itsIndexer.tiledCursorMove (True, itsCursorPos, itsCursorShape, itsAxisPath); if (successful) { // test for hang over since cursor has moved. if (itsNiceFit == False) { const IPosition curPos(itsCursorPos); const IPosition curEndPos(itsCursorPos+itsCursorShape-1); const IPosition latShape(itsIndexer.shape()); const uInt ndim = itsIndexer.ndim(); uInt i = 0; while (i < ndim && curEndPos(i) < latShape(i) && curPos(i) >= 0) { i++; } itsHangover = (i != ndim); } } else { itsEnd = True; } DebugAssert (ok() == True, AipsError); return successful; } Bool LatticeStepper::operator--(int) { DebugAssert (ok() == True, AipsError); if (itsStart) { return False; } // Increment the counter. itsNsteps++; // itsEnd = false by definition when decrementing itsEnd = False; Bool successful = itsIndexer.tiledCursorMove (False, itsCursorPos, itsCursorShape, itsAxisPath); if (successful) { // test for hang over since cursor has moved const IPosition curPos(itsCursorPos); const uInt ndim = itsIndexer.ndim(); if (itsNiceFit == False) { const IPosition curEndPos(itsCursorPos+itsCursorShape); const IPosition latShape(itsIndexer.shape()); uInt i = 0; while (i < ndim && curPos(i) >= 0 && curEndPos(i) < latShape(i)) { i++; } itsHangover = (i != ndim); } } else { itsStart = True; } DebugAssert (ok() == True, AipsError); return successful; } void LatticeStepper::reset() { itsCursorPos = 0; itsNsteps = 0; itsEnd = False; itsStart = True; itsHangover = False; if (!itsNiceFit) { const uInt ndim = itsIndexer.ndim(); const IPosition latShape(itsIndexer.shape()); for (uInt i=0; i latShape(i)) { itsHangover = True; } } } DebugAssert (ok() == True, AipsError); } Bool LatticeStepper::atStart() const { DebugAssert (ok() == True, AipsError); return itsStart; } Bool LatticeStepper::atEnd() const { DebugAssert (ok() == True, AipsError); return itsEnd; } uInt LatticeStepper::nsteps() const { DebugAssert (ok() == True, AipsError); return itsNsteps; } IPosition LatticeStepper::position() const { DebugAssert (ok() == True, AipsError); return itsIndexer.absolutePosition (itsCursorPos); } IPosition LatticeStepper::relativePosition() const { DebugAssert (ok() == True, AipsError); return itsCursorPos; } // Function which returns the current position of the end of the cursor // relative to the main Lattice. IPosition LatticeStepper::endPosition() const { DebugAssert (ok() == True, AipsError); return itsIndexer.absolutePosition (relativeEndPosition()); } // Function which returns the current position of the end of the cursor // relative to the sub Lattice. IPosition LatticeStepper::relativeEndPosition() const { DebugAssert (ok() == True, AipsError); IPosition trc(itsCursorPos + itsCursorShape - 1); if (itsHangover) { const IPosition latticeShape(subLatticeShape()); const uInt nDim = trc.nelements(); for (uInt n = 0; n < nDim; n++) { if (trc(n) >= latticeShape(n)) { trc(n) = latticeShape(n) - 1; } } } return trc; } IPosition LatticeStepper::latticeShape() const { DebugAssert (ok() == True, AipsError); return itsIndexer.fullShape(); } IPosition LatticeStepper::subLatticeShape() const { DebugAssert(ok() == True, AipsError); return itsIndexer.shape(); } void LatticeStepper::setCursorShape (const IPosition& cursorShape) { setCursorShape (cursorShape, IPosition()); } void LatticeStepper::setCursorShape (const IPosition& cursorShape, const IPosition& cursorAxes) { const IPosition& latticeShape = itsIndexer.fullShape(); uInt latticeDim = itsIndexer.ndim(); uInt ndimCS = cursorShape.nelements(); uInt ndimCA = cursorAxes.nelements(); if (ndimCS == 0 || ndimCS > latticeDim) { throw (AipsError ("LatticeStepper::setCursorShape: cursorShape" " has no axes or more axes than lattice")); } if (ndimCA > latticeDim) { throw (AipsError ("LatticeStepper::setCursorShape: cursorAxes" " has more axes than lattice")); } if (!(ndimCA==0 || ndimCA==ndimCS || ndimCS==latticeDim)) { throw (AipsError ("LatticeStepper::setCursorShape: cursorAxes" " has invalid number of axes; it should be 0," " equal to cursorShape, or cursorShape should" " contain all axes")); } uInt i; // Check if the cursor axes are given correctly and in ascending order. for (i=0; i= Int(latticeDim)) { throw (AipsError ("LatticeStepper::setCursorShape: " "cursorAxes value <0 or >latticeDim")); } if (i > 0) { if (cursorAxes(i) <= cursorAxes(i-1)) { throw (AipsError ("LatticeStepper::setCursorShape: " "cursorAxes values not in ascending order")); } } } // Count the cursor shape axes with length > 1. uInt count = 0; for (i=0; i 1) { count++; } } // If cursorAxes is given and cursorShape is given for all axes, // check if the cursor shape for non-cursorAxes is 1. if (ndimCA > 0 && ndimCA != ndimCS) { for (i=0; i latticeShape(i)) { throw (AipsError ("LatticeStepper::setCursorShape: " "cursorShape <=0 or > latticeShape")); } } // When cursorAxes is not given, the axes with length>1 form the cursorAxes. if (ndimCA == 0) { itsCursorAxes.resize (count); count = 0; for (i=0; i 1) { itsCursorAxes(count++) = i; } } }else{ itsCursorAxes.resize (ndimCA); itsCursorAxes = cursorAxes; } itsNiceFit = niceFit(); reset(); AlwaysAssert (ok() == True, AipsError); } IPosition LatticeStepper::cursorAxes() const { DebugAssert (ok() == True, AipsError); return itsCursorAxes; } IPosition LatticeStepper::cursorShape() const { DebugAssert (ok() == True, AipsError); if (hangOver() && itsPolicy == RESIZE) { return relativeEndPosition() - relativePosition() + 1; } return itsCursorShape; } Bool LatticeStepper::hangOver() const { DebugAssert (ok() == True, AipsError); return itsHangover; } void LatticeStepper::subSection(const IPosition& blc, const IPosition& trc, const IPosition& inc) { itsIndexer.fullSize(); itsIndexer.subSection (blc, trc, inc); itsNiceFit = niceFit(); reset(); DebugAssert (ok() == True, AipsError); } void LatticeStepper::subSection(const IPosition& blc, const IPosition& trc) { subSection (blc, trc, IPosition(itsIndexer.ndim(), 1)); } IPosition LatticeStepper::blc() const { DebugAssert (ok() == True, AipsError); return itsIndexer.offset(); } IPosition LatticeStepper::trc() const { DebugAssert (ok() == True, AipsError); return itsIndexer.absolutePosition (itsIndexer.shape() - 1); } IPosition LatticeStepper::increment() const { DebugAssert (ok() == True, AipsError); return itsIndexer.increment(); } const IPosition& LatticeStepper::axisPath() const { DebugAssert (ok() == True, AipsError); return itsAxisPath; } // check if the cursor shape is an sub-multiple of the Lattice shape Bool LatticeStepper::niceFit() const { const uInt cursorDim = itsCursorShape.nelements(); // Determine if the Lattice shape is a multiple of the cursor shape. uInt i = 0; while (i < cursorDim && itsIndexer.shape(i)%itsCursorShape(i) == 0) { i++; } return (i == cursorDim); } LatticeNavigator* LatticeStepper::clone() const { return new LatticeStepper(*this); } uInt LatticeStepper::calcCacheSize (const IPosition& cubeShape, const IPosition& tileShape, uInt maxCacheSize, uInt bucketSize) const { return (bucketSize == 0 ? 0 : TSMCube::calcCacheSize (cubeShape, tileShape, False, itsCursorShape, blc(), trc() - blc() + 1, itsAxisPath, maxCacheSize, bucketSize)); } Bool LatticeStepper::ok() const { ostringstream str; str << "LatticeStepper::ok - "; const uInt latticeDim = itsIndexer.ndim(); // Check the cursor shape is OK if (itsCursorShape.nelements() != latticeDim) { str << "cursor shape " << itsCursorShape << " has wrong number of dimensions (ie. not " << latticeDim << ')'; throw AipsError (String(str.str())); return False; } for (uInt i=0; i < latticeDim; i++) { // the cursor shape must be <= the corresponding lattice axes AND // a cursor shape with an axis of length zero makes no sense if (itsCursorShape(i) > Int(itsIndexer.fullShape(i)) || itsCursorShape(i) <= 0) { str << "cursor shape " << itsCursorShape << " is too big or small for full lattice shape " << itsIndexer.fullShape(); throw AipsError (String(str.str())); return False; } } // Check the cursor position is OK if (itsCursorPos.nelements() != latticeDim) { str << "cursor position " << itsCursorPos << " has wrong number of dimensions (ie. not " << latticeDim << ')'; throw AipsError (String(str.str())); return False; } // cursor position or its "far corner" must be inside the (sub)-Lattice if (!(itsIndexer.isInside(itsCursorPos) || itsIndexer.isInside(itsCursorPos+itsCursorShape-1))){ str << "cursor beginning " << itsCursorPos << " or end " << itsCursorPos + itsCursorShape - 1 << " is entirely outside the lattice shape " << itsIndexer.shape(); throw AipsError (String(str.str())); return False; } // check the Axis Path is OK if(itsAxisPath.nelements() != latticeDim) { str << "axis path " << itsAxisPath << " has wrong number of dimensions (ie. not " << latticeDim << ')'; throw AipsError (String(str.str())); return False; } // each itsAxisPath value must be a lattice axis number, 0..n-1 for (uInt n=0; n < latticeDim; n++) { if (itsAxisPath(n) >= Int(latticeDim)){ str << "axis path " << itsAxisPath << " has elements >= the lattice dim " << latticeDim - 1; throw AipsError (String(str.str())); return False; } } // each itsAxisPath value must be unique for (uInt k=0; k < (latticeDim - 1); k++) { for (uInt j=k+1; j < latticeDim; j++) { if (itsAxisPath(k) == itsAxisPath(j)) { str << "axis path " << itsAxisPath << " does not have unique elements"; throw AipsError (String(str.str())); return False; } } } // Check the LatticeIndexer is OK if (itsIndexer.ok() == False) { str << "LatticeIndexer thinks things are bad"; throw AipsError (String(str.str())); return False; } // Check if itsNiceFit is correct. if (itsNiceFit != niceFit()) { str << "itsNiceFit " << itsNiceFit << " is inconsistent with niceFit()"; throw AipsError (String(str.str())); return False; } // Otherwise it has passed all the tests return True; } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/Lattices/LatticeStepper.h000066400000000000000000000533031476623553700220110ustar00rootroot00000000000000//# LatticeStepper.h: provides 'natural' traversal, by cursor shape //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICESTEPPER_H #define LATTICES_LATTICESTEPPER_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Traverse a Lattice by cursor shape // // // // // //
      • LatticeNavigator // // // LatticeStepper is so-called because it performs the calculations // necessary to step through a Lattice. The next position is always one // simple step forward from the current position. The step-size is // calculated directly from the size of the LatticeIterator's cursor or // window. // // // When you wish to traverse a Lattice (say, a PagedArray or an Image) you // will usually create a LatticeIterator. Once created, you must attach a // LatticeNavigator to the iterator. A LatticeStepper, is a concrete class // derived from the abstract LatticeNavigator that allows you to move // sequentially through the Lattice. //

        // In constructing a LatticeStepper, you specify the Lattice shape and the // shape of the "cursor" used to step through the data. The cursor position // can be incremented or decremented to retrieve the next portion of the // Lattice. // The specified cursor shape can (and often will) have fewer dimensions // that the Lattice itself. For example if we have a 4-dimensional Lattice // with latticeShape = IPosition(4,64,64,4,16), then specifying a // cursor of cursorShape = IPosition(1,64), will step through the // hypercube row by row. When the cursor shape has fewer dimensions than the // Lattice degenerate dimensions are added to the end of the cursor so that // in the above example the specified cursor is assumed to mean // cursorShape = IPosition(4,64,1,1,1). To access the data // spectrum by spectrum (assuming the last axis is the spectral axis), you // must use a 1-dimensional cursor of IPosition(4,1,1,1,16). The // cursorShape function always returns a shape with as many // dimensions as the underlying Lattice. //

        // It is an error (and an exception will be thrown) if the cursor has more // dimensions than the Lattice or if it is larger on any axis than the // Lattice shape. //
        // Also the cursor shape on all axes must be less than or equal to the Lattice // shape on that axis. Otherwise an exception will be thrown. //

        // In principle cursor axes with length 1 are degenerate axes. They // are removed from the lattice cursor if the // LatticeIterator cursor is accessed // using e.g. the matrixCursor function. // Using a special LatticeStepper constructor it is, however, possible // to specify which cursor axes with length 1 have to be treated as // normal axes. In that way one can be sure that a cursor is, for // example, always 2D, even if an axis happens to have length 1. // // IPosition latticeShape(4,20,16,1,4); // IPosition cursorAxes(2,1,2); // IPosition cursorShape(2,16,1); // IPosition axisPath; // LatticeStepper stepper(latticeShape, cursorShape, // cursorAxes, axisPath); // // This results in a cursor with shape [1,16,1,1]. The first and last // axis are degenerate, so the cursor can also be accessed using // matrixCursor (with shape [16,1]). // Note that the cursor shape could also be specified as [1,16,1,1]. //

        // The "path" of the cursor through the Lattice can be controlled by // specifying an axisPath during construction of the class. This is an // IPosition which has exactly as many elements as the Lattice // dimension. Each element must contain an integer between // 0 -- Lattice_Dimension-1, and must be unique. For example, // // axisPath = IPosition(4,0,1,2,3) or // axisPath = IPosition(4,3,1,2,0) // // are valid but // // axisPath = IPosition(4,1,2,3,4) or // axisPath = IPosition(4,0,1,1,3) // // are not, given the latticeShape specified above. An exception is thrown // if the AxisPath is bad. //
        // The "axis path" defines which axis will be iterated through fastest as // the cursor moves through the Lattice. With the above mentioned // 4-dimensional Lattice and a single element cursor // (cursorShape=IPosition(4,1,1,1,1)) setting an // axisPath=IPosition(4,0,1,2,3) will move the cursor through all // the columns, and then onto the next row, and again through all the // columns in the second row. Once all the rows in the first plane have // been exhausted the cursor will then iterate to the next plane, and // eventually to the next spectral channel. If, however, the axisPath was // axisPath=IPosition(4,3,0,1,2) then the cursor would iterate // through each spectral channel first, before moving onto the next column in // the first row. //

        // The cursor never changes dimensionality as it traverses the Lattice. But it // may change shape if the cursor shape is not a factor of the Lattice // shape. A cursor shape is not a factor of the Lattice shape if the Lattice // shape is not an integer multiple of the cursor shape on all axes. // The integer multiplier need not to be the same for each axes. // For example, for a Lattice of shape [10,10,10] a cursor of shape [8,5,2] // is not a factor but one with a shape of [10,5,1] is. //
        // When the cursor is not congruent with the Lattice moving the cursor through // the Lattice will sometimes result in part of the cursor hanging over the // edge of the Lattice. When this occurs the hangOver member function will // return True. What to do in these situtations is specified by the // hangOverPolicy enumerator. //

          //
        1. // If the LatticeStepper::PAD option (the default) is used at construction time // the cursor shape does not change. The parts of the cursor that hang over the // edge of the Lattice are filled with a default value, usually zero, that is // defined by the particular LatticeIterator used. //
        2. // If the LatticeStepper::RESIZE option is used at construction time the cursor // shape does change to a smaller value when near the edge of the Lattice so // that it is just big enough. For example with a Lattice shape of 10x10 and a // cursor of 8x8 the cursor shape will initally be 8x8, then resize to 2x8 on // the first step, then resize to 8x2 on the second step and finally resize to // 2x2. The hangover function will return True for the last three steps, even // though the cursor has resized. //
        // The portion of the Lattice that the cursor will traverse can be // restricted to a region defined by a top right corner, bottom left corner // and a step size. This is done using the subSection function, // which also resets the cursor position to the origin of the sub-Lattice. // The cursor shape will remain unchanged. It is no error when the cursor // shape exceeds the sub-Lattice shape (instead it is a hangover state). //
        // If a sub-Lattice is defined then cursor positions relative // to the sub-Lattice origins can be obtained using the // relativePosition function rather than the // position function, which always returns positions relative to // the origin of the main Lattice. //
        // To change the size of the sub-Lattice simply call the // subSection function again with a different trc, blc & // inc. This first clears the old sub-Lattice, then imposes the newly // specified one, and finally moves the cursor to the origin of the // new sub-Lattice. //
        // // This example is of a global function that will iterate through a // 4-dimensional Lattice. It is assumed that the axes are RA, Dec, Stokes & // Frequency, and it will calculate the average flux in the I polarization // on each frequency channel. Imagine it is passed a data set (ie. Lattice) // of size 256 x 256 x 4 x 1024. This corresponds to 1GByte of data. However // the iterator will page through this data using a cursor of size 256 x 256 // (or 256kByte) and will only read (because of subsectioning) the relevant // quarter of the data set. It is usually a good idea to set up the axis // path as this is gives hints to data cache about which data to retrieve in // advance. // // void averageFluxByChannel(const Lattice& data) // { // // for convenience, get the shape into a local variable // IPosition latticeShape = data.shape(); // cout << "Data has shape: " << latticeShape << endl; // // // check that the data has 4 axes. // DebugAssert(latticeShape.nelements() == 4, AipsError); // // // specify the cursor, or window shape. Here the cursor is a matrix // // that is the shape of the first plane of our Lattice. // // For convenience, get the first two axis lengths into local vars // uInt nCols = latticeShape(0); // uInt nRows = latticeShape(1); // IPosition cursorShape(2, nCols, nRows); // // // construct a stepper, which needs to know the shape of the lattice // // and the shape of the iterator's cursor. By using cursorShape, which // // is directly determined by the lattice's shape, we can be sure // // that the cursor is a factor of the lattice, and thus that // // all elements will be picked up efficiently during the traversal. // // Because we will not be iterating through the stokes axis this axis // // is made the slowest moving one. // IPosition axisPath(4, 0, 1, 3, 2) // LatticeStepper stepper(latticeShape, cursorShape, axisPath); // // // Subsection the stepper so that it only iterates through the I // // Stokes parameter (assumed to be when the third axis is zero) // uInt nFreqs = latticeShape(3); // IPosition blc(4, 0, 0, 0, 0), trc(4, nCols-1, nRows-1, 0, nFreqs-1); // stepper.subSection(blc, trc); // // // construct the iterator. Since we only want to read the Data, // // use the read-only class, which disallows writing back to the cursor // // (and hence is more efficient). // RO_LatticeIterator iterator(data, stepper); // // Vector spectrum(nFreqs); // spectrum = 0.0; // uInt channel = 0; // for (iterator.reset(); !iterator.atEnd(); iterator++) { // const Matrix& cursor = iterator.matrixCursor(); // for (uInt col = 0; col < nCols; col++) { // for (uInt row = 0; row < nRows; row++) { // spectrum(channel) += cursor(col, row); // } // } // channel++; // } // for iterator // cout << "Average spectrum is: " // << spectrum / cursorShape.product() << endl; // } // // // // Moving through a Lattice by equal sized chunks, and without regard // to the nature of the data, is a basic and common procedure. // //# //# class LatticeStepper: public LatticeNavigator { public: // The hangOverPolicy enumerator is used in the constructors to indicate // what this class should do when the cursor shape hangs over the edge // of the Lattice. enum hangOverPolicy { // PAD is the default and means that the cursor size supplied by the user is // kept fixed. But if the cursor overhangs the Lattice the part that // overhangs is filled with a default value that is specified by the // Iterator. Currently the default value is zero. PAD, // RESIZE means that the cursor shape is adjusted whenever it approaches the // edges of the Lattice so that it is always the right size to include only // the parts of the Lattice that are available. The user specified cursor // shape now becomes the default and largest possible cursor shape. RESIZE}; // The first argument is the shape of the Lattice to be iterated and the // second argument is the shape of the cursor. The cursor will increment // initially along first axis, then the second and then the third // (ie. axisPath = IPosition(ndim,0,1,2,...)) // The dimensionality of the cursorShape can be less than the // dimensionality of the lattice. It will be padded with 1s. //
        The cursorShape axes with length > 1 are seen as the true cursor axes. // The other axes are degenerated and are removed by the functions // vectorCursor(), etc., in class // (RO_)LatticeIterator. LatticeStepper (const IPosition& latticeShape, const IPosition& cursorShape, const uInt hangOverPolicy=PAD); // Same as the above constructor except that the axis path is explicitly // specified. The axis path is described in the synopsis above. LatticeStepper (const IPosition& latticeShape, const IPosition& cursorShape, const IPosition& axisPath, const uInt hangOverPolicy=PAD); // Same as the above constructor except that the cursor axes are // explicitly specified. This can be useful to avoid that cursor axes // with length=1 are treated as degenerated axes by the Iterator classes. // The following rules have to be obeyed: //
        - cursorAxes.nelements() <= latticeShape.nelements() //
        - cursorShape.nelements() == latticeShape.nelements() //
        or cursorShape.nelements() == cursorAxes.nelements() // The latter means that the cursorShape contains the axes mentioned in // cursorAxes. //
        See also the example in the synopsis. LatticeStepper (const IPosition& latticeShape, const IPosition& cursorShape, const IPosition& cursorAxes, const IPosition& axisPath, const uInt hangOverPolicy=PAD); // The copy constructor uses copy semantics. LatticeStepper (const LatticeStepper& other); ~LatticeStepper(); // The assignment operator uses copy semantics. LatticeStepper& operator= (const LatticeStepper& other); // Increment operator (postfix version) - move the cursor // forward one step. Returns True if the cursor was moved. virtual Bool operator++(int); // Decrement operator (postfix version) - move the cursor // backwards one step. Returns True if the cursor was moved. virtual Bool operator--(int); // Function to move the cursor to the beginning of the (sub)-Lattice. Also // resets the number of steps (nsteps function) to zero. virtual void reset(); // Function which returns "True" if the cursor is at the beginning of the // (sub)-Lattice, otherwise, returns "False" virtual Bool atStart() const; // Function which returns "True" if an attempt has been made to increment // the cursor beyond the end of the (sub)-Lattice. virtual Bool atEnd() const; // Function to return the number of steps (increments & decrements) taken // since construction (or since last reset). This is a running count of // all cursor movement (operator++ or operator--), even though // N-increments followed by N-decrements will ALWAYS leave the cursor in // the original position. virtual uInt nsteps() const; // Functions which return the current position of the beginning of the // cursor. The position function is relative to the origin // in the main Lattice and the relativePosition function is // relative to the origin and increment used in the sub-Lattice (defined // using the subSection function). If no sub-Lattice is defined // the two functions return identical positions. // virtual IPosition position() const; virtual IPosition relativePosition() const; // // Functions which return the current position of the end of the // cursor. The endPosition function is relative to the origin // in the main Lattice and the relativeEndPosition function // is relative to the origin and increment used in the sub-Lattice // (defined using the subSection function). If no sub-Lattice // is defined the two functions return identical positions. // It returns the end position in the lattice and // does not take overhang into account. // virtual IPosition endPosition() const; virtual IPosition relativeEndPosition() const; // // Functions which return the shape of the Lattice being iterated // through. latticeShape always returns the shape of the main // Lattice while subLatticeShape returns the shape of any // sub-Lattice defined using the subSection function. // virtual IPosition latticeShape() const; virtual IPosition subLatticeShape() const; // // Functions to change the cursor shape to a new one. They always reset // the cursor to the beginning of the Lattice (and reset the number of // steps to zero). // void setCursorShape (const IPosition& cursorShape); void setCursorShape (const IPosition& cursorShape, const IPosition& cursorAxes); // // Function which returns the shape of the cursor. This always includes // all axes (ie. it includes degenerates axes) virtual IPosition cursorShape() const; // Function which returns the axes of the cursor. virtual IPosition cursorAxes() const; // Function which returns "True" if the increment/decrement operators have // moved the cursor position such that part of the cursor beginning or end // is hanging over the edge of the (sub)-Lattice. virtual Bool hangOver() const; // Functions to specify a "section" of the Lattice to step over. A section // is defined in terms of the Bottom Left Corner (blc), Top Right Corner // (trc), and step size (inc), on ALL of its axes, including degenerate // axes. The step size defaults to one if not specified. // virtual void subSection (const IPosition& blc, const IPosition& trc); virtual void subSection (const IPosition& blc, const IPosition& trc, const IPosition& inc); // // Return the bottom left hand corner (blc), top right corner (trc) or // step size (increment) used by the current sub-Lattice. If no // sub-Lattice has been defined (with the subSection function) // these functions return blc=0, trc=latticeShape-1, increment=1, ie. the // entire Lattice. // virtual IPosition blc() const; virtual IPosition trc() const; virtual IPosition increment() const; // // Return the axis path. virtual const IPosition& axisPath() const; // Function which returns a pointer to dynamic memory of an exact copy // of this instance. The pointer returned by this function must // be deleted externally. virtual LatticeNavigator* clone() const; // Function which checks the internal data of this class for correct // dimensionality and consistant values. // Returns True if everything is fine otherwise returns False virtual Bool ok() const; // Calculate the cache size (in tiles) for this type of access to a lattice // in the given row of the tiled hypercube. virtual uInt calcCacheSize (const IPosition& cubeShape, const IPosition& tileShape, uInt maxCacheSize, uInt bucketSize) const; private: // Prevent the default constructor from being used. LatticeStepper(); // Pad the cursor to the right number of dimensions. void padCursor(); // Check if the cursor shape is a factor of the Lattice shape. Bool niceFit() const; LatticeIndexer itsIndexer;//# Knows about the (sub)-Lattice shape and how //# to traverse it. IPosition itsCursorAxes; //# the cursor axes IPosition itsCursorShape; //# The shape of the cursor IPosition itsCursorPos; //# The current position of the iterator. IPosition itsAxisPath; //# the heading to follow for the cursor uInt itsNsteps; //# the number of iterator steps taken thus far; //# set to 0 on reset () Bool itsEnd; //# is the cursor beyond the end? Bool itsStart; //# is the cursor at the beginning? Bool itsNiceFit; //# if the cursor shape is a sub-multiple of the //# Lattice shape then set this to True. Used to //# avoid needing to test for a cursor hanging //# over the edge of the lattice. Bool itsHangover; //# this data member is set by the increment and //# decrement operators if itsNiceFit == False. It //# is used to tell if the cursor "Hangs over" //# the edge of the lattice shape. uInt itsPolicy; //# what to do if the cursor does hang over }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/Lattices/LatticeUtilities.h000066400000000000000000000076301476623553700223440ustar00rootroot00000000000000//# LatticeUtilities.h: useful global functions for Lattices //# Copyright (C) 1995,1996,1997,1999,2000,2001,2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICEUTILITIES_H #define LATTICES_LATTICEUTILITIES_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template class Lattice; template class MaskedLattice; class IPosition; class LogIO; class Slicer; // Static functions for Lattices // // // // // //
      • Lattice // // // // Some static helper functions for Lattices // // // // Common functionality not appropriate for Lattice member functions // // // //
      • nothing I know of // // class LatticeUtilities { public: // Copy data and mask from input to output. If the input has no mask, // that means all True (good), and these values will be transferred // to the output. Mask transfer only occurs if the output has // a writeable mask. template static void copyDataAndMask (LogIO& os, MaskedLattice& out, const MaskedLattice& in, Bool zeroMasked=False); // Replicate array through lattice in the specified region. // The shape of pixels has to fit exactly into the shape of // the selected region for each axis of pixels. Otherwise // and exception will be thrown. For example, // if the shape of the region is [10,20], the shape of pixels could // be [10] and it will be replicated 20 times. Another example would // be that the shape of pixels could be [5,10] and it would be // replicated 4 times fitting into the Lattice template static void replicate (Lattice& lat, const Slicer& region, const Array& pixels); // Bin up one axis of MaskedArray (uses Lattices in implementation) template static void bin (MaskedArray& out, const MaskedArray& in, uInt axis, uInt bin); // Add degenerate axes to the lattice if needed (nDim is the desired number of dimensions // for the output lattice). If the shapes are the same, the returned // pointer holds a SubLattice. If a reshape was necessary, the pointer // holds an ExtendLattice. The pointer is the callers responsibility to delete. template static void addDegenerateAxes (Lattice*& pLatOut, const Lattice& latIn, uInt nDim); }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/Lattices/LatticeUtilities.tcc000066400000000000000000000146061476623553700226670ustar00rootroot00000000000000//# LatticeUtilities.cc: defines the Lattice Utilities global functions//# Copyright (C) 1995,1996,1997,1999,2000,2001,2002,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_LATTICEUTILITIES_TCC #define LATTICES_LATTICEUTILITIES_TCC #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# namespace casacore begin // LatticeUtilities template void LatticeUtilities::copyDataAndMask(LogIO& os, MaskedLattice& out, const MaskedLattice& in, Bool zeroMasked) // // This function coould be implemented with LEL // but requires two passes if zeroMask=True so // we leave it as it is { // Do we need to stuff about with masks ? Even if the input // does not have a mask, it has a 'virtual' mask of all True. // Therefore we need to transfer those mask values to the // output if an output mask exists. Bool doMask = out.isMasked() && out.hasPixelMask(); Lattice* pMaskOut = 0; if (doMask) { pMaskOut = &out.pixelMask(); if (!pMaskOut->isWritable()) { doMask = False; os << LogIO::WARN << "The output image has a mask but it is not writable" << endl; os << LogIO::WARN << "So the mask will not be transferred to the output" << LogIO::POST; } } if (!doMask) zeroMasked = False; // Use the same stepper for input and output. IPosition cursorShape = out.niceCursorShape(); LatticeStepper stepper (out.shape(), cursorShape, LatticeStepper::RESIZE); // Create input lattice iterator RO_MaskedLatticeIterator iter(in, stepper); for (iter.reset(); !iter.atEnd(); iter++) { // Put the pixels IPosition cursorShape = iter.cursorShape(); if (zeroMasked) { Array pixels = iter.cursor().copy(); const Array& mask = iter.getMask(); // typename Array::const_iterator mIt; typename Array::iterator dIt; typename Array::iterator dItend = pixels.end(); for (dIt=pixels.begin(),mIt=mask.begin(); dIt!=dItend; ++dIt,++mIt) { if (!(*mIt)) *dIt = 0.0; } out.putSlice(pixels, iter.position()); } else { out.putSlice(iter.cursor(), iter.position()); } // Put the mask if (doMask) { pMaskOut->putSlice(iter.getMask(), iter.position()); } } } template void LatticeUtilities::replicate (Lattice& lat, const Slicer& region, const Array& pixels) { SubLattice subLattice(lat, region, True); const IPosition shapePixels = pixels.shape(); const IPosition shapeLattice = subLattice.shape(); AlwaysAssert(shapePixels.nelements()<=shapeLattice.nelements(),AipsError); // LatticeStepper stepper(shapeLattice, shapePixels, LatticeStepper::RESIZE); LatticeIterator iter(subLattice, stepper); for (iter.reset(); !iter.atEnd(); iter++) { subLattice.putSlice(pixels, iter.position()); } } template void LatticeUtilities::addDegenerateAxes (Lattice*& pLatOut, const Lattice& latIn, uInt nDim) { delete pLatOut; pLatOut = 0; const uInt dimIn = latIn.ndim(); if (nDim < dimIn ) { throw (AipsError ("Input Lattice has more dimensions than desired output Lattice")); } else if (nDim == dimIn) { pLatOut = new SubLattice(latIn); } else { IPosition newShape(nDim,1); newShape.setFirst (latIn.shape()); IPosition tPath = IPosition::makeAxisPath(newShape.nelements()); IPosition newAxes = tPath.getLast(nDim-dimIn); IPosition stretchAxes; // pLatOut = new ExtendLattice(latIn, newShape, newAxes, stretchAxes); } } template void LatticeUtilities::bin (MaskedArray& out, const MaskedArray& in, uInt axis, uInt bin) { // Check const uInt nDim = in.ndim(); AlwaysAssert(axis data(in.getArray()); ArrayLattice mask(in.getMask()); // SubLattice mLat(data); mLat.setPixelMask(mask, False); // Create binner IPosition factors(nDim,1); factors(axis) = bin; RebinLattice binLat(mLat, factors); // Assign output MA MaskedArray tmp(binLat.get(), binLat.getMask()); out = tmp; } } //# End namespace casacore #endif casacore-3.7.1/lattices/Lattices/Lattices_1.fig000066400000000000000000000071151476623553700213670ustar00rootroot00000000000000#FIG 3.2 Landscape Center Inches Letter 100.00 Single -2 1200 2 1 4 0 1 0 7 0 0 -1 0.000 1 0.0000 6900 5025 75 75 6900 4950 6900 5100 1 4 0 1 0 7 0 0 -1 0.000 1 0.0000 10500 5025 75 75 10500 4950 10500 5100 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 4200 300 5700 300 5700 1200 4200 1200 4200 300 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 600 1800 2100 1800 2100 2700 600 2700 600 1800 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 2700 1800 4200 1800 4200 2700 2700 2700 2700 1800 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 6900 1800 8400 1800 8400 2700 6900 2700 6900 1800 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 4800 1800 6300 1800 6300 2700 4800 2700 4800 1800 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 9000 1800 10500 1800 10500 2700 9000 2700 9000 1800 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 3 4800 1500 4950 1350 5100 1500 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 4950 1200 4950 1350 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 4 1350 1800 1350 1500 9750 1500 9750 1800 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 7650 1500 7650 1800 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 5550 1500 5550 1800 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 3450 1500 3450 1800 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 5850 3300 7350 3300 7350 4200 5850 4200 5850 3300 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 7950 3300 9450 3300 9450 4200 7950 4200 7950 3300 2 3 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 8550 4350 8700 4200 8850 4350 8700 4500 8550 4350 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 8700 4500 8700 4650 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 3 7500 3000 7650 2850 7800 3000 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 7650 2700 7650 2850 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 4 6600 3300 6600 3000 8700 3000 8700 3300 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 600 3300 2100 3300 2100 4200 600 4200 600 3300 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 2700 3300 4200 3300 4200 4200 2700 4200 2700 3300 2 3 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 3300 2850 3450 2700 3600 2850 3450 3000 3300 2850 2 3 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 1200 2850 1350 2700 1500 2850 1350 3000 1200 2850 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 1350 3000 1350 3300 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 3450 3000 3450 3300 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 4200 375 5700 375 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 6900 1875 8400 1875 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 9000 1875 10500 1875 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 6150 5100 7650 5100 7650 6000 6150 6000 6150 5100 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 7950 5100 9450 5100 9450 6000 7950 6000 7950 5100 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 6150 5175 7650 5175 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 8700 4650 8700 5100 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 4 6900 4950 6900 4650 10500 4650 10500 4950 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 9750 5100 11250 5100 11250 6000 9750 6000 9750 5100 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 9750 5175 11250 5175 4 0 0 0 0 0 18 0.0000 4 255 1350 2775 2400 PagedArray\001 4 0 0 0 0 0 18 0.0000 4 255 1485 600 2400 ArrayLattice\001 4 0 0 0 0 0 18 0.0000 4 255 1455 4800 2400 TempLattice\001 4 0 0 0 0 0 18 0.0000 4 195 1650 6825 2400 MaskedLattice\001 4 0 0 0 0 0 18 0.0000 4 255 690 9375 2175 Image\001 4 0 0 0 0 0 18 0.0000 4 195 1020 9225 2475 Interface\001 4 0 0 0 0 0 18 0.0000 4 195 1215 8100 3900 SubLattice\001 4 0 0 0 0 0 18 0.0000 4 255 1335 5925 3825 LatticeExpr\001 4 0 0 0 0 0 18 0.0000 4 195 645 3150 3900 Table\001 4 0 0 0 0 0 18 0.0000 4 255 690 975 3900 Array\001 4 0 0 0 0 0 18 0.0000 4 195 795 4575 900 Lattice\001 4 0 0 0 0 0 18 0.0000 4 255 1575 7950 5700 LatticeRegion\001 4 0 0 0 0 0 18 0.0000 4 195 795 6525 5700 Lattice\001 4 0 0 0 0 0 18 0.0000 4 195 1650 9675 5700 MaskedLattice\001 casacore-3.7.1/lattices/Lattices/Lattices_1.ps000066400000000000000000000147321476623553700212470ustar00rootroot00000000000000%!PS-Adobe-2.0 %%Title: Lattices_1.ps %%Creator: fig2dev Version 3.2 Patchlevel 0-beta2 %%CreationDate: Fri Apr 17 11:23:26 1998 %%For: gvd@duw01 (Ger van Diepen) %%Orientation: Landscape %%BoundingBox: 134 72 478 720 %%Pages: 1 %%BeginSetup %%IncludeFeature: *PageSize Letter %%EndSetup %%Magnification: 1.00 %%EndComments /$F2psDict 200 dict def $F2psDict begin $F2psDict /mtrx matrix put /col-1 {0 setgray} bind def /col0 {0.000 0.000 0.000 srgb} bind def /col1 {0.000 0.000 1.000 srgb} bind def /col2 {0.000 1.000 0.000 srgb} bind def /col3 {0.000 1.000 1.000 srgb} bind def /col4 {1.000 0.000 0.000 srgb} bind def /col5 {1.000 0.000 1.000 srgb} bind def /col6 {1.000 1.000 0.000 srgb} bind def /col7 {1.000 1.000 1.000 srgb} bind def /col8 {0.000 0.000 0.560 srgb} bind def /col9 {0.000 0.000 0.690 srgb} bind def /col10 {0.000 0.000 0.820 srgb} bind def /col11 {0.530 0.810 1.000 srgb} bind def /col12 {0.000 0.560 0.000 srgb} bind def /col13 {0.000 0.690 0.000 srgb} bind def /col14 {0.000 0.820 0.000 srgb} bind def /col15 {0.000 0.560 0.560 srgb} bind def /col16 {0.000 0.690 0.690 srgb} bind def /col17 {0.000 0.820 0.820 srgb} bind def /col18 {0.560 0.000 0.000 srgb} bind def /col19 {0.690 0.000 0.000 srgb} bind def /col20 {0.820 0.000 0.000 srgb} bind def /col21 {0.560 0.000 0.560 srgb} bind def /col22 {0.690 0.000 0.690 srgb} bind def /col23 {0.820 0.000 0.820 srgb} bind def /col24 {0.500 0.190 0.000 srgb} bind def /col25 {0.630 0.250 0.000 srgb} bind def /col26 {0.750 0.380 0.000 srgb} bind def /col27 {1.000 0.500 0.500 srgb} bind def /col28 {1.000 0.630 0.630 srgb} bind def /col29 {1.000 0.750 0.750 srgb} bind def /col30 {1.000 0.880 0.880 srgb} bind def /col31 {1.000 0.840 0.000 srgb} bind def end save 117.0 37.0 translate 90 rotate 1 -1 scale /cp {closepath} bind def /ef {eofill} bind def /gr {grestore} bind def /gs {gsave} bind def /sa {save} bind def /rs {restore} bind def /l {lineto} bind def /m {moveto} bind def /rm {rmoveto} bind def /n {newpath} bind def /s {stroke} bind def /sh {show} bind def /slc {setlinecap} bind def /slj {setlinejoin} bind def /slw {setlinewidth} bind def /srgb {setrgbcolor} bind def /rot {rotate} bind def /sc {scale} bind def /sd {setdash} bind def /ff {findfont} bind def /sf {setfont} bind def /scf {scalefont} bind def /sw {stringwidth} bind def /tr {translate} bind def /tnt {dup dup currentrgbcolor 4 -2 roll dup 1 exch sub 3 -1 roll mul add 4 -2 roll dup 1 exch sub 3 -1 roll mul add 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb} bind def /shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul 4 -2 roll mul srgb} bind def /DrawEllipse { /endangle exch def /startangle exch def /yrad exch def /xrad exch def /y exch def /x exch def /savematrix mtrx currentmatrix def x y tr xrad yrad sc 0 0 1 startangle endangle arc closepath savematrix setmatrix } def /$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def /$F2psEnd {$F2psEnteredState restore end} def %%EndProlog $F2psBegin 10 setmiterlimit n 0 6052 m 0 0 l 11407 0 l 11407 6052 l cp clip 0.06000 0.06000 sc %%Page: 1 1 7.500 slw % Ellipse n 6900 5025 75 75 0 360 DrawEllipse gs col0 s gr % Ellipse n 10500 5025 75 75 0 360 DrawEllipse gs col0 s gr % Polyline n 4200 300 m 5700 300 l 5700 1200 l 4200 1200 l cp gs col0 s gr % Polyline n 600 1800 m 2100 1800 l 2100 2700 l 600 2700 l cp gs col0 s gr % Polyline n 2700 1800 m 4200 1800 l 4200 2700 l 2700 2700 l cp gs col0 s gr % Polyline n 6900 1800 m 8400 1800 l 8400 2700 l 6900 2700 l cp gs col0 s gr % Polyline n 4800 1800 m 6300 1800 l 6300 2700 l 4800 2700 l cp gs col0 s gr % Polyline n 9000 1800 m 10500 1800 l 10500 2700 l 9000 2700 l cp gs col0 s gr % Polyline n 4800 1500 m 4950 1350 l 5100 1500 l gs col0 s gr % Polyline n 4950 1200 m 4950 1350 l gs col0 s gr % Polyline n 1350 1800 m 1350 1500 l 9750 1500 l 9750 1800 l gs col0 s gr % Polyline n 7650 1500 m 7650 1800 l gs col0 s gr % Polyline n 5550 1500 m 5550 1800 l gs col0 s gr % Polyline n 3450 1500 m 3450 1800 l gs col0 s gr % Polyline n 5850 3300 m 7350 3300 l 7350 4200 l 5850 4200 l cp gs col0 s gr % Polyline n 7950 3300 m 9450 3300 l 9450 4200 l 7950 4200 l cp gs col0 s gr % Polyline n 8550 4350 m 8700 4200 l 8850 4350 l 8700 4500 l cp gs col0 s gr % Polyline n 8700 4500 m 8700 4650 l gs col0 s gr % Polyline n 7500 3000 m 7650 2850 l 7800 3000 l gs col0 s gr % Polyline n 7650 2700 m 7650 2850 l gs col0 s gr % Polyline n 6600 3300 m 6600 3000 l 8700 3000 l 8700 3300 l gs col0 s gr % Polyline n 600 3300 m 2100 3300 l 2100 4200 l 600 4200 l cp gs col0 s gr % Polyline n 2700 3300 m 4200 3300 l 4200 4200 l 2700 4200 l cp gs col0 s gr % Polyline n 3300 2850 m 3450 2700 l 3600 2850 l 3450 3000 l cp gs col0 s gr % Polyline n 1200 2850 m 1350 2700 l 1500 2850 l 1350 3000 l cp gs col0 s gr % Polyline n 1350 3000 m 1350 3300 l gs col0 s gr % Polyline n 3450 3000 m 3450 3300 l gs col0 s gr % Polyline n 4200 375 m 5700 375 l gs col0 s gr % Polyline n 6900 1875 m 8400 1875 l gs col0 s gr % Polyline n 9000 1875 m 10500 1875 l gs col0 s gr % Polyline n 6150 5100 m 7650 5100 l 7650 6000 l 6150 6000 l cp gs col0 s gr % Polyline n 7950 5100 m 9450 5100 l 9450 6000 l 7950 6000 l cp gs col0 s gr % Polyline n 6150 5175 m 7650 5175 l gs col0 s gr % Polyline n 8700 4650 m 8700 5100 l gs col0 s gr % Polyline n 6900 4950 m 6900 4650 l 10500 4650 l 10500 4950 l gs col0 s gr % Polyline n 9750 5100 m 11250 5100 l 11250 6000 l 9750 6000 l cp gs col0 s gr % Polyline n 9750 5175 m 11250 5175 l gs col0 s gr /Times-Roman ff 270.00 scf sf 2775 2400 m gs 1 -1 sc (PagedArray) col0 sh gr /Times-Roman ff 270.00 scf sf 600 2400 m gs 1 -1 sc (ArrayLattice) col0 sh gr /Times-Roman ff 270.00 scf sf 4800 2400 m gs 1 -1 sc (TempLattice) col0 sh gr /Times-Roman ff 270.00 scf sf 6825 2400 m gs 1 -1 sc (MaskedLattice) col0 sh gr /Times-Roman ff 270.00 scf sf 9375 2175 m gs 1 -1 sc (Image) col0 sh gr /Times-Roman ff 270.00 scf sf 9225 2475 m gs 1 -1 sc (Interface) col0 sh gr /Times-Roman ff 270.00 scf sf 8100 3900 m gs 1 -1 sc (SubLattice) col0 sh gr /Times-Roman ff 270.00 scf sf 5925 3825 m gs 1 -1 sc (LatticeExpr) col0 sh gr /Times-Roman ff 270.00 scf sf 3150 3900 m gs 1 -1 sc (Table) col0 sh gr /Times-Roman ff 270.00 scf sf 975 3900 m gs 1 -1 sc (Array) col0 sh gr /Times-Roman ff 270.00 scf sf 4575 900 m gs 1 -1 sc (Lattice) col0 sh gr /Times-Roman ff 270.00 scf sf 7950 5700 m gs 1 -1 sc (LatticeRegion) col0 sh gr /Times-Roman ff 270.00 scf sf 6525 5700 m gs 1 -1 sc (Lattice) col0 sh gr /Times-Roman ff 270.00 scf sf 9675 5700 m gs 1 -1 sc (MaskedLattice) col0 sh gr $F2psEnd rs showpage casacore-3.7.1/lattices/Lattices/Lattices_2.fig000066400000000000000000000147631476623553700213770ustar00rootroot00000000000000#FIG 3.2 Landscape Center Inches Letter 100.00 Single -2 1200 2 1 4 0 1 0 0 0 0 20 0.000 1 0.0000 7304 3444 75 75 7229 3444 7379 3444 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 5700 1800 7200 1800 7200 2700 5700 2700 5700 1800 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 5700 300 7200 300 7200 1200 5700 1200 5700 300 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 3 6300 1800 6450 1650 6600 1800 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 6450 1200 6450 1650 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 5700 3300 7200 3300 7200 4200 5700 4200 5700 3300 2 3 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 6300 2850 6450 2700 6600 2850 6450 3000 6300 2850 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 5700 4800 7200 4800 7200 5700 5700 5700 5700 4800 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 3 6300 6000 6450 5850 6600 6000 2 3 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 6300 4350 6450 4200 6600 4350 6450 4500 6300 4350 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 6450 3000 6450 3300 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 6450 4500 6450 4800 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 6450 5700 6450 5850 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 3900 6300 5400 6300 5400 7200 3900 7200 3900 6300 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 5700 6300 7200 6300 7200 7200 5700 7200 5700 6300 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 7500 6300 9000 6300 9000 7200 7500 7200 7500 6300 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 9300 6300 10800 6300 10800 7200 9300 7200 9300 6300 2 3 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 8100 7350 8250 7200 8400 7350 8250 7500 8100 7350 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 7500 7800 9000 7800 9000 8700 7500 8700 7500 7800 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 11100 6300 12600 6300 12600 7200 11100 7200 11100 6300 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 4800 7500 6300 7500 6300 8400 4800 8400 4800 7500 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 8250 7500 8250 7800 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 4 4650 6300 4650 6000 11850 6000 11850 6300 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 6450 6000 6450 6300 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 8250 6000 8250 6300 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 10050 6000 10050 6300 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 5550 6000 5550 7500 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 900 7500 2400 7500 2400 8400 900 8400 900 7500 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 1800 6300 3300 6300 3300 7200 1800 7200 1800 6300 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 0 6300 1500 6300 1500 7200 0 7200 0 6300 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 900 4800 2400 4800 2400 5700 900 5700 900 4800 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 3 1500 6000 1650 5850 1800 6000 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 1650 5700 1650 5850 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 4 750 6300 750 6000 2550 6000 2550 6300 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 1650 6000 1650 7500 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 0 3 0 0 1.00 60.00 120.00 3150 6300 3150 5100 5700 5100 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 0 3 0 0 1.00 60.00 120.00 4050 6300 4050 5400 5700 5400 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 0 3 0 0 1.00 60.00 120.00 12450 6300 12450 3450 7350 3450 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 0 3 0 0 1.00 60.00 120.00 150 6300 150 5400 900 5400 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 900 8700 2400 8700 2400 9600 900 9600 900 8700 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 1650 8400 1650 8700 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 4800 8700 6300 8700 6300 9600 4800 9600 4800 8700 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 5550 8400 5550 8700 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 9300 7500 10800 7500 10800 8400 9300 8400 9300 7500 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 0 2 0 0 1.00 60.00 120.00 10050 7200 10050 7500 2 3 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 7200 5250 7350 5100 7500 5250 7350 5400 7200 5250 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 7500 5250 7800 5250 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 7800 4500 9300 4500 9300 5400 7800 5400 7800 4500 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 1 0 3 0 0 1.00 60.00 120.00 7200 4050 8550 4050 8550 4500 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 5700 375 7200 375 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 5700 4875 7200 4875 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 7500 7875 9000 7875 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 9300 7575 10800 7575 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 900 8775 2400 8775 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 900 4875 2400 4875 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 4800 8775 6300 8775 4 0 0 0 0 0 18 0.0000 4 195 1650 5625 750 MaskedLattice\001 4 0 0 0 0 0 18 0.0000 4 255 1335 5775 2250 LatticeExpr\001 4 0 0 0 0 0 18 0.0000 4 195 510 6225 1050 \001 4 0 0 0 0 0 18 0.0000 4 195 1515 5700 5250 LELInterface\001 4 0 0 0 0 0 18 0.0000 4 195 510 6225 5550 \001 4 0 0 0 0 0 18 0.0000 4 195 510 6225 2550 \001 4 0 0 0 0 0 18 0.0000 4 255 1260 4050 6750 LELBinary\001 4 0 0 0 0 0 18 0.0000 4 195 510 8025 8550 \001 4 0 0 0 0 0 18 0.0000 4 195 1515 11100 6750 LELFunction\001 4 0 0 0 0 0 18 0.0000 4 195 510 11625 7050 \001 4 0 0 0 0 0 18 0.0000 4 195 1410 9375 6750 LELConvert\001 4 0 0 0 0 0 18 0.0000 4 240 735 9675 7050 \001 4 0 0 0 0 0 18 0.0000 4 195 1290 7575 6750 LELLattice\001 4 0 0 0 0 0 18 0.0000 4 255 1200 5850 6750 LELUnary\001 4 0 0 0 0 0 18 0.0000 4 195 510 4425 7050 \001 4 0 0 0 0 0 18 0.0000 4 255 1200 4950 7950 LELUnary\001 4 0 0 0 0 0 18 0.0000 4 195 1650 7425 8250 MaskedLattice\001 4 0 0 0 0 0 18 0.0000 4 195 525 525 7050 Bool\001 4 0 0 0 0 0 18 0.0000 4 255 1260 150 6750 LELBinary\001 4 0 0 0 0 0 18 0.0000 4 195 1515 900 5250 LELInterface\001 4 0 0 0 0 0 18 0.0000 4 255 1800 1725 6750 LELBinaryCmp\001 4 0 0 0 0 0 18 0.0000 4 195 510 2250 7050 \001 4 0 0 0 0 0 18 0.0000 4 195 525 1425 8250 Bool\001 4 0 0 0 0 0 18 0.0000 4 255 1200 1050 7950 LELUnary\001 4 0 0 0 0 0 14 0.0000 4 150 105 5475 5025 2\001 4 0 0 0 0 0 14 0.0000 4 150 105 5475 5625 2\001 4 0 0 0 0 0 14 0.0000 4 150 105 675 5625 2\001 4 0 0 0 0 0 18 0.0000 4 195 510 8025 7050 \001 4 0 0 0 0 0 18 0.0000 4 195 1155 5925 7050 Const\001 4 0 0 0 0 0 18 0.0000 4 195 510 5325 8250 \001 4 0 0 0 0 0 18 0.0000 4 195 1515 900 9150 LELInterface\001 4 0 0 0 0 0 18 0.0000 4 195 1515 4800 9150 LELInterface\001 4 0 0 0 0 0 18 0.0000 4 195 510 5325 9450 \001 4 0 0 0 0 0 18 0.0000 4 195 1515 9300 7950 LELInterface\001 4 0 0 0 0 0 18 0.0000 4 195 495 9825 8250 \001 4 0 0 0 0 0 18 0.0000 4 195 855 1275 9450 \001 4 0 0 0 0 0 18 0.0000 4 195 855 1275 5550 \001 4 0 0 0 0 0 18 0.0000 4 195 1560 7800 5025 LELAttribute\001 4 0 0 0 0 0 14 0.0000 4 105 390 8025 3975 uses\001 4 0 0 0 0 0 18 0.0000 4 255 1125 5850 3975 ExprNode\001 4 0 0 0 0 0 18 0.0000 4 195 795 6075 3675 Lattice\001 casacore-3.7.1/lattices/Lattices/Lattices_2.ps000066400000000000000000000252521476623553700212470ustar00rootroot00000000000000%!PS-Adobe-2.0 %%Title: Lattices_2.ps %%Creator: fig2dev Version 3.2 Patchlevel 0-beta2 %%CreationDate: Mon Apr 20 08:49:27 1998 %%For: gvd@duw01 (Ger van Diepen) %%Orientation: Landscape %%BoundingBox: 26 15 586 776 %%Pages: 1 %%BeginSetup %%IncludeFeature: *PageSize Letter %%EndSetup %%Magnification: 1.00 %%EndComments /$F2psDict 200 dict def $F2psDict begin $F2psDict /mtrx matrix put /col-1 {0 setgray} bind def /col0 {0.000 0.000 0.000 srgb} bind def /col1 {0.000 0.000 1.000 srgb} bind def /col2 {0.000 1.000 0.000 srgb} bind def /col3 {0.000 1.000 1.000 srgb} bind def /col4 {1.000 0.000 0.000 srgb} bind def /col5 {1.000 0.000 1.000 srgb} bind def /col6 {1.000 1.000 0.000 srgb} bind def /col7 {1.000 1.000 1.000 srgb} bind def /col8 {0.000 0.000 0.560 srgb} bind def /col9 {0.000 0.000 0.690 srgb} bind def /col10 {0.000 0.000 0.820 srgb} bind def /col11 {0.530 0.810 1.000 srgb} bind def /col12 {0.000 0.560 0.000 srgb} bind def /col13 {0.000 0.690 0.000 srgb} bind def /col14 {0.000 0.820 0.000 srgb} bind def /col15 {0.000 0.560 0.560 srgb} bind def /col16 {0.000 0.690 0.690 srgb} bind def /col17 {0.000 0.820 0.820 srgb} bind def /col18 {0.560 0.000 0.000 srgb} bind def /col19 {0.690 0.000 0.000 srgb} bind def /col20 {0.820 0.000 0.000 srgb} bind def /col21 {0.560 0.000 0.560 srgb} bind def /col22 {0.690 0.000 0.690 srgb} bind def /col23 {0.820 0.000 0.820 srgb} bind def /col24 {0.500 0.190 0.000 srgb} bind def /col25 {0.630 0.250 0.000 srgb} bind def /col26 {0.750 0.380 0.000 srgb} bind def /col27 {1.000 0.500 0.500 srgb} bind def /col28 {1.000 0.630 0.630 srgb} bind def /col29 {1.000 0.750 0.750 srgb} bind def /col30 {1.000 0.880 0.880 srgb} bind def /col31 {1.000 0.840 0.000 srgb} bind def end save 9.0 16.5 translate 90 rotate 1 -1 scale /cp {closepath} bind def /ef {eofill} bind def /gr {grestore} bind def /gs {gsave} bind def /sa {save} bind def /rs {restore} bind def /l {lineto} bind def /m {moveto} bind def /rm {rmoveto} bind def /n {newpath} bind def /s {stroke} bind def /sh {show} bind def /slc {setlinecap} bind def /slj {setlinejoin} bind def /slw {setlinewidth} bind def /srgb {setrgbcolor} bind def /rot {rotate} bind def /sc {scale} bind def /sd {setdash} bind def /ff {findfont} bind def /sf {setfont} bind def /scf {scalefont} bind def /sw {stringwidth} bind def /tr {translate} bind def /tnt {dup dup currentrgbcolor 4 -2 roll dup 1 exch sub 3 -1 roll mul add 4 -2 roll dup 1 exch sub 3 -1 roll mul add 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb} bind def /shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul 4 -2 roll mul srgb} bind def /DrawEllipse { /endangle exch def /startangle exch def /yrad exch def /xrad exch def /y exch def /x exch def /savematrix mtrx currentmatrix def x y tr xrad yrad sc 0 0 1 startangle endangle arc closepath savematrix setmatrix } def /$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def /$F2psEnd {$F2psEnteredState restore end} def %%EndProlog $F2psBegin 10 setmiterlimit n 0 9652 m 0 0 l 12693 0 l 12693 9652 l cp clip 0.06000 0.06000 sc %%Page: 1 1 7.500 slw % Ellipse n 7304 3444 75 75 0 360 DrawEllipse gs 0.00 setgray ef gr gs col0 s gr % Polyline n 5700 1800 m 7200 1800 l 7200 2700 l 5700 2700 l cp gs col0 s gr % Polyline n 5700 300 m 7200 300 l 7200 1200 l 5700 1200 l cp gs col0 s gr % Polyline n 6300 1800 m 6450 1650 l 6600 1800 l gs col0 s gr % Polyline n 6450 1200 m 6450 1650 l gs col0 s gr % Polyline n 5700 3300 m 7200 3300 l 7200 4200 l 5700 4200 l cp gs col0 s gr % Polyline n 6300 2850 m 6450 2700 l 6600 2850 l 6450 3000 l cp gs col0 s gr % Polyline n 5700 4800 m 7200 4800 l 7200 5700 l 5700 5700 l cp gs col0 s gr % Polyline n 6300 6000 m 6450 5850 l 6600 6000 l gs col0 s gr % Polyline n 6300 4350 m 6450 4200 l 6600 4350 l 6450 4500 l cp gs col0 s gr % Polyline n 6450 3000 m 6450 3300 l gs col0 s gr % Polyline n 6450 4500 m 6450 4800 l gs col0 s gr % Polyline n 6450 5700 m 6450 5850 l gs col0 s gr % Polyline n 3900 6300 m 5400 6300 l 5400 7200 l 3900 7200 l cp gs col0 s gr % Polyline n 5700 6300 m 7200 6300 l 7200 7200 l 5700 7200 l cp gs col0 s gr % Polyline n 7500 6300 m 9000 6300 l 9000 7200 l 7500 7200 l cp gs col0 s gr % Polyline n 9300 6300 m 10800 6300 l 10800 7200 l 9300 7200 l cp gs col0 s gr % Polyline n 8100 7350 m 8250 7200 l 8400 7350 l 8250 7500 l cp gs col0 s gr % Polyline n 7500 7800 m 9000 7800 l 9000 8700 l 7500 8700 l cp gs col0 s gr % Polyline n 11100 6300 m 12600 6300 l 12600 7200 l 11100 7200 l cp gs col0 s gr % Polyline n 4800 7500 m 6300 7500 l 6300 8400 l 4800 8400 l cp gs col0 s gr % Polyline n 8250 7500 m 8250 7800 l gs col0 s gr % Polyline n 4650 6300 m 4650 6000 l 11850 6000 l 11850 6300 l gs col0 s gr % Polyline n 6450 6000 m 6450 6300 l gs col0 s gr % Polyline n 8250 6000 m 8250 6300 l gs col0 s gr % Polyline n 10050 6000 m 10050 6300 l gs col0 s gr % Polyline n 5550 6000 m 5550 7500 l gs col0 s gr % Polyline n 900 7500 m 2400 7500 l 2400 8400 l 900 8400 l cp gs col0 s gr % Polyline n 1800 6300 m 3300 6300 l 3300 7200 l 1800 7200 l cp gs col0 s gr % Polyline n 0 6300 m 1500 6300 l 1500 7200 l 0 7200 l cp gs col0 s gr % Polyline n 900 4800 m 2400 4800 l 2400 5700 l 900 5700 l cp gs col0 s gr % Polyline n 1500 6000 m 1650 5850 l 1800 6000 l gs col0 s gr % Polyline n 1650 5700 m 1650 5850 l gs col0 s gr % Polyline n 750 6300 m 750 6000 l 2550 6000 l 2550 6300 l gs col0 s gr % Polyline n 1650 6000 m 1650 7500 l gs col0 s gr % Polyline gs clippath 5553 5070 m 5673 5100 l 5553 5130 l 5715 5130 l 5715 5070 l cp clip n 3150 6300 m 3150 5100 l 5700 5100 l gs col0 s gr gr % arrowhead n 5553 5070 m 5673 5100 l 5553 5130 l col0 s % Polyline gs clippath 5553 5370 m 5673 5400 l 5553 5430 l 5715 5430 l 5715 5370 l cp clip n 4050 6300 m 4050 5400 l 5700 5400 l gs col0 s gr gr % arrowhead n 5553 5370 m 5673 5400 l 5553 5430 l col0 s % Polyline gs clippath 7497 3480 m 7377 3450 l 7497 3420 l 7335 3420 l 7335 3480 l cp clip n 12450 6300 m 12450 3450 l 7350 3450 l gs col0 s gr gr % arrowhead n 7497 3480 m 7377 3450 l 7497 3420 l col0 s % Polyline gs clippath 753 5370 m 873 5400 l 753 5430 l 915 5430 l 915 5370 l cp clip n 150 6300 m 150 5400 l 900 5400 l gs col0 s gr gr % arrowhead n 753 5370 m 873 5400 l 753 5430 l col0 s % Polyline n 900 8700 m 2400 8700 l 2400 9600 l 900 9600 l cp gs col0 s gr % Polyline gs clippath 1680 8553 m 1650 8673 l 1620 8553 l 1620 8715 l 1680 8715 l cp clip n 1650 8400 m 1650 8700 l gs col0 s gr gr % arrowhead n 1680 8553 m 1650 8673 l 1620 8553 l col0 s % Polyline n 4800 8700 m 6300 8700 l 6300 9600 l 4800 9600 l cp gs col0 s gr % Polyline gs clippath 5580 8553 m 5550 8673 l 5520 8553 l 5520 8715 l 5580 8715 l cp clip n 5550 8400 m 5550 8700 l gs col0 s gr gr % arrowhead n 5580 8553 m 5550 8673 l 5520 8553 l col0 s % Polyline n 9300 7500 m 10800 7500 l 10800 8400 l 9300 8400 l cp gs col0 s gr % Polyline gs clippath 10080 7353 m 10050 7473 l 10020 7353 l 10020 7515 l 10080 7515 l cp clip n 10050 7200 m 10050 7500 l gs col0 s gr gr % arrowhead n 10080 7353 m 10050 7473 l 10020 7353 l col0 s % Polyline n 7200 5250 m 7350 5100 l 7500 5250 l 7350 5400 l cp gs col0 s gr % Polyline n 7500 5250 m 7800 5250 l gs col0 s gr % Polyline n 7800 4500 m 9300 4500 l 9300 5400 l 7800 5400 l cp gs col0 s gr % Polyline gs clippath 8580 4353 m 8550 4473 l 8520 4353 l 8520 4515 l 8580 4515 l cp clip n 7200 4050 m 8550 4050 l 8550 4500 l gs col0 s gr gr % arrowhead n 8580 4353 m 8550 4473 l 8520 4353 l col0 s % Polyline n 5700 375 m 7200 375 l gs col0 s gr % Polyline n 5700 4875 m 7200 4875 l gs col0 s gr % Polyline n 7500 7875 m 9000 7875 l gs col0 s gr % Polyline n 9300 7575 m 10800 7575 l gs col0 s gr % Polyline n 900 8775 m 2400 8775 l gs col0 s gr % Polyline n 900 4875 m 2400 4875 l gs col0 s gr % Polyline n 4800 8775 m 6300 8775 l gs col0 s gr /Times-Roman ff 270.00 scf sf 5625 750 m gs 1 -1 sc (MaskedLattice) col0 sh gr /Times-Roman ff 270.00 scf sf 5775 2250 m gs 1 -1 sc (LatticeExpr) col0 sh gr /Times-Roman ff 270.00 scf sf 6225 1050 m gs 1 -1 sc () col0 sh gr /Times-Roman ff 270.00 scf sf 5700 5250 m gs 1 -1 sc (LELInterface) col0 sh gr /Times-Roman ff 270.00 scf sf 6225 5550 m gs 1 -1 sc () col0 sh gr /Times-Roman ff 270.00 scf sf 6225 2550 m gs 1 -1 sc () col0 sh gr /Times-Roman ff 270.00 scf sf 4050 6750 m gs 1 -1 sc (LELBinary) col0 sh gr /Times-Roman ff 270.00 scf sf 8025 8550 m gs 1 -1 sc () col0 sh gr /Times-Roman ff 270.00 scf sf 11100 6750 m gs 1 -1 sc (LELFunction) col0 sh gr /Times-Roman ff 270.00 scf sf 11625 7050 m gs 1 -1 sc () col0 sh gr /Times-Roman ff 270.00 scf sf 9375 6750 m gs 1 -1 sc (LELConvert) col0 sh gr /Times-Roman ff 270.00 scf sf 9675 7050 m gs 1 -1 sc () col0 sh gr /Times-Roman ff 270.00 scf sf 7575 6750 m gs 1 -1 sc (LELLattice) col0 sh gr /Times-Roman ff 270.00 scf sf 5850 6750 m gs 1 -1 sc (LELUnary) col0 sh gr /Times-Roman ff 270.00 scf sf 4425 7050 m gs 1 -1 sc () col0 sh gr /Times-Roman ff 270.00 scf sf 4950 7950 m gs 1 -1 sc (LELUnary) col0 sh gr /Times-Roman ff 270.00 scf sf 7425 8250 m gs 1 -1 sc (MaskedLattice) col0 sh gr /Times-Roman ff 270.00 scf sf 525 7050 m gs 1 -1 sc (Bool) col0 sh gr /Times-Roman ff 270.00 scf sf 150 6750 m gs 1 -1 sc (LELBinary) col0 sh gr /Times-Roman ff 270.00 scf sf 900 5250 m gs 1 -1 sc (LELInterface) col0 sh gr /Times-Roman ff 270.00 scf sf 1725 6750 m gs 1 -1 sc (LELBinaryCmp) col0 sh gr /Times-Roman ff 270.00 scf sf 2250 7050 m gs 1 -1 sc () col0 sh gr /Times-Roman ff 270.00 scf sf 1425 8250 m gs 1 -1 sc (Bool) col0 sh gr /Times-Roman ff 270.00 scf sf 1050 7950 m gs 1 -1 sc (LELUnary) col0 sh gr /Times-Roman ff 210.00 scf sf 5475 5025 m gs 1 -1 sc (2) col0 sh gr /Times-Roman ff 210.00 scf sf 5475 5625 m gs 1 -1 sc (2) col0 sh gr /Times-Roman ff 210.00 scf sf 675 5625 m gs 1 -1 sc (2) col0 sh gr /Times-Roman ff 270.00 scf sf 8025 7050 m gs 1 -1 sc () col0 sh gr /Times-Roman ff 270.00 scf sf 5925 7050 m gs 1 -1 sc (Const) col0 sh gr /Times-Roman ff 270.00 scf sf 5325 8250 m gs 1 -1 sc () col0 sh gr /Times-Roman ff 270.00 scf sf 900 9150 m gs 1 -1 sc (LELInterface) col0 sh gr /Times-Roman ff 270.00 scf sf 4800 9150 m gs 1 -1 sc (LELInterface) col0 sh gr /Times-Roman ff 270.00 scf sf 5325 9450 m gs 1 -1 sc () col0 sh gr /Times-Roman ff 270.00 scf sf 9300 7950 m gs 1 -1 sc (LELInterface) col0 sh gr /Times-Roman ff 270.00 scf sf 9825 8250 m gs 1 -1 sc () col0 sh gr /Times-Roman ff 270.00 scf sf 1275 9450 m gs 1 -1 sc () col0 sh gr /Times-Roman ff 270.00 scf sf 1275 5550 m gs 1 -1 sc () col0 sh gr /Times-Roman ff 270.00 scf sf 7800 5025 m gs 1 -1 sc (LELAttribute) col0 sh gr /Times-Roman ff 210.00 scf sf 8025 3975 m gs 1 -1 sc (uses) col0 sh gr /Times-Roman ff 270.00 scf sf 5850 3975 m gs 1 -1 sc (ExprNode) col0 sh gr /Times-Roman ff 270.00 scf sf 6075 3675 m gs 1 -1 sc (Lattice) col0 sh gr $F2psEnd rs showpage casacore-3.7.1/lattices/Lattices/Lattices_3.fig000066400000000000000000000033511476623553700213670ustar00rootroot00000000000000#FIG 3.2 Landscape Center Inches Letter 100.00 Single -2 1200 2 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 5700 1800 7200 1800 7200 2700 5700 2700 5700 1800 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 5700 300 7200 300 7200 1200 5700 1200 5700 300 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 3 6300 1800 6450 1650 6600 1800 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 6450 1200 6450 1650 2 3 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 6300 2850 6450 2700 6600 2850 6450 3000 6300 2850 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 4800 3300 6300 3300 6300 4200 4800 4200 4800 3300 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 7200 3300 8700 3300 8700 4200 7200 4200 7200 3300 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 8700 1800 10200 1800 10200 2700 8700 2700 8700 1800 2 3 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 9300 2850 9450 2700 9600 2850 9450 3000 9300 2850 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 9600 3300 11100 3300 11100 4200 9600 4200 9600 3300 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 4 5550 3300 5550 3150 7500 3150 7500 3300 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 4 8400 3300 8400 3150 10350 3150 10350 3300 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 9450 3000 9450 3150 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 6450 3000 6450 3150 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 5700 375 7200 375 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 7200 3375 8700 3375 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 9600 3375 11100 3375 4 0 0 0 0 0 18 0.0000 4 195 795 6000 750 Lattice\001 4 0 0 0 0 0 18 0.0000 4 195 855 6000 1050 \001 4 0 0 0 0 0 18 0.0000 4 255 1575 5625 2400 LatticeRegion\001 4 0 0 0 0 0 18 0.0000 4 255 1470 8700 2400 ImageRegion\001 4 0 0 0 0 0 18 0.0000 4 255 1230 9750 3900 WCRegion\001 4 0 0 0 0 0 18 0.0000 4 255 1125 7425 3900 LCRegion\001 4 0 0 0 0 0 18 0.0000 4 195 645 5250 3900 Slicer\001 casacore-3.7.1/lattices/Lattices/Lattices_3.ps000066400000000000000000000107051476623553700212450ustar00rootroot00000000000000%!PS-Adobe-2.0 %%Title: Lattices_3.ps %%Creator: fig2dev Version 3.2 Patchlevel 0-beta2 %%CreationDate: Mon Apr 20 08:50:16 1998 %%For: gvd@duw01 (Ger van Diepen) %%Orientation: Landscape %%BoundingBox: 188 206 424 586 %%Pages: 1 %%BeginSetup %%IncludeFeature: *PageSize Letter %%EndSetup %%Magnification: 1.00 %%EndComments /$F2psDict 200 dict def $F2psDict begin $F2psDict /mtrx matrix put /col-1 {0 setgray} bind def /col0 {0.000 0.000 0.000 srgb} bind def /col1 {0.000 0.000 1.000 srgb} bind def /col2 {0.000 1.000 0.000 srgb} bind def /col3 {0.000 1.000 1.000 srgb} bind def /col4 {1.000 0.000 0.000 srgb} bind def /col5 {1.000 0.000 1.000 srgb} bind def /col6 {1.000 1.000 0.000 srgb} bind def /col7 {1.000 1.000 1.000 srgb} bind def /col8 {0.000 0.000 0.560 srgb} bind def /col9 {0.000 0.000 0.690 srgb} bind def /col10 {0.000 0.000 0.820 srgb} bind def /col11 {0.530 0.810 1.000 srgb} bind def /col12 {0.000 0.560 0.000 srgb} bind def /col13 {0.000 0.690 0.000 srgb} bind def /col14 {0.000 0.820 0.000 srgb} bind def /col15 {0.000 0.560 0.560 srgb} bind def /col16 {0.000 0.690 0.690 srgb} bind def /col17 {0.000 0.820 0.820 srgb} bind def /col18 {0.560 0.000 0.000 srgb} bind def /col19 {0.690 0.000 0.000 srgb} bind def /col20 {0.820 0.000 0.000 srgb} bind def /col21 {0.560 0.000 0.560 srgb} bind def /col22 {0.690 0.000 0.690 srgb} bind def /col23 {0.820 0.000 0.820 srgb} bind def /col24 {0.500 0.190 0.000 srgb} bind def /col25 {0.630 0.250 0.000 srgb} bind def /col26 {0.750 0.380 0.000 srgb} bind def /col27 {1.000 0.500 0.500 srgb} bind def /col28 {1.000 0.630 0.630 srgb} bind def /col29 {1.000 0.750 0.750 srgb} bind def /col30 {1.000 0.880 0.880 srgb} bind def /col31 {1.000 0.840 0.000 srgb} bind def end save 171.0 -81.0 translate 90 rotate 1 -1 scale /cp {closepath} bind def /ef {eofill} bind def /gr {grestore} bind def /gs {gsave} bind def /sa {save} bind def /rs {restore} bind def /l {lineto} bind def /m {moveto} bind def /rm {rmoveto} bind def /n {newpath} bind def /s {stroke} bind def /sh {show} bind def /slc {setlinecap} bind def /slj {setlinejoin} bind def /slw {setlinewidth} bind def /srgb {setrgbcolor} bind def /rot {rotate} bind def /sc {scale} bind def /sd {setdash} bind def /ff {findfont} bind def /sf {setfont} bind def /scf {scalefont} bind def /sw {stringwidth} bind def /tr {translate} bind def /tnt {dup dup currentrgbcolor 4 -2 roll dup 1 exch sub 3 -1 roll mul add 4 -2 roll dup 1 exch sub 3 -1 roll mul add 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb} bind def /shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul 4 -2 roll mul srgb} bind def /$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def /$F2psEnd {$F2psEnteredState restore end} def %%EndProlog $F2psBegin 10 setmiterlimit n 0 4252 m 0 0 l 11152 0 l 11152 4252 l cp clip 0.06000 0.06000 sc %%Page: 1 1 % Polyline 7.500 slw n 5700 1800 m 7200 1800 l 7200 2700 l 5700 2700 l cp gs col0 s gr % Polyline n 5700 300 m 7200 300 l 7200 1200 l 5700 1200 l cp gs col0 s gr % Polyline n 6300 1800 m 6450 1650 l 6600 1800 l gs col0 s gr % Polyline n 6450 1200 m 6450 1650 l gs col0 s gr % Polyline n 6300 2850 m 6450 2700 l 6600 2850 l 6450 3000 l cp gs col0 s gr % Polyline n 4800 3300 m 6300 3300 l 6300 4200 l 4800 4200 l cp gs col0 s gr % Polyline n 7200 3300 m 8700 3300 l 8700 4200 l 7200 4200 l cp gs col0 s gr % Polyline n 8700 1800 m 10200 1800 l 10200 2700 l 8700 2700 l cp gs col0 s gr % Polyline n 9300 2850 m 9450 2700 l 9600 2850 l 9450 3000 l cp gs col0 s gr % Polyline n 9600 3300 m 11100 3300 l 11100 4200 l 9600 4200 l cp gs col0 s gr % Polyline n 5550 3300 m 5550 3150 l 7500 3150 l 7500 3300 l gs col0 s gr % Polyline n 8400 3300 m 8400 3150 l 10350 3150 l 10350 3300 l gs col0 s gr % Polyline n 9450 3000 m 9450 3150 l gs col0 s gr % Polyline n 6450 3000 m 6450 3150 l gs col0 s gr % Polyline n 5700 375 m 7200 375 l gs col0 s gr % Polyline n 7200 3375 m 8700 3375 l gs col0 s gr % Polyline n 9600 3375 m 11100 3375 l gs col0 s gr /Times-Roman ff 270.00 scf sf 6000 750 m gs 1 -1 sc (Lattice) col0 sh gr /Times-Roman ff 270.00 scf sf 6000 1050 m gs 1 -1 sc () col0 sh gr /Times-Roman ff 270.00 scf sf 5625 2400 m gs 1 -1 sc (LatticeRegion) col0 sh gr /Times-Roman ff 270.00 scf sf 8700 2400 m gs 1 -1 sc (ImageRegion) col0 sh gr /Times-Roman ff 270.00 scf sf 9750 3900 m gs 1 -1 sc (WCRegion) col0 sh gr /Times-Roman ff 270.00 scf sf 7425 3900 m gs 1 -1 sc (LCRegion) col0 sh gr /Times-Roman ff 270.00 scf sf 5250 3900 m gs 1 -1 sc (Slicer) col0 sh gr $F2psEnd rs showpage casacore-3.7.1/lattices/Lattices/Lattices_4.fig000066400000000000000000000124541476623553700213740ustar00rootroot00000000000000#FIG 3.2 Landscape Center Inches Letter 100.00 Single -2 1200 2 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 6900 4800 8400 4800 8400 5700 6900 5700 6900 4800 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 2400 6300 3900 6300 3900 7200 2400 7200 2400 6300 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 4500 6300 6000 6300 6000 7200 4500 7200 4500 6300 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 11175 4800 12675 4800 12675 5700 11175 5700 11175 4800 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 3300 7800 4800 7800 4800 8700 3300 8700 3300 7800 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 3 3000 7500 3150 7350 3300 7500 2 3 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 3900 8850 4050 8700 4200 8850 4050 9000 3900 8850 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 1500 7800 3000 7800 3000 8700 1500 8700 1500 7800 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 300 6300 1800 6300 1800 7200 300 7200 300 6300 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 3300 9300 4800 9300 4800 10200 3300 10200 3300 9300 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 7950 6000 9450 6000 9450 6900 7950 6900 7950 6000 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 10050 6000 11550 6000 11550 6900 10050 6900 10050 6000 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 9000 4800 10500 4800 10500 5700 9000 5700 9000 4800 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 2400 4800 3900 4800 3900 5700 2400 5700 2400 4800 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 2400 3300 3900 3300 3900 4200 2400 4200 2400 3300 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 5700 1800 7200 1800 7200 2700 5700 2700 5700 1800 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 3 6300 3000 6450 2850 6600 3000 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 6450 2700 6450 2850 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 4 10800 3750 11400 3750 11400 2250 7200 2250 2 3 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 10500 3750 10650 3600 10800 3750 10650 3900 10500 3750 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 9000 3300 10500 3300 10500 4200 9000 4200 9000 3300 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 3 9600 4500 9750 4350 9900 4500 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 3 3000 4800 3150 4650 3300 4800 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 3 3000 6000 3150 5850 3300 6000 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 4 3150 3300 3150 3000 9750 3000 9750 3300 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 9750 4200 9750 4350 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 4 7650 4800 7650 4500 11850 4500 11850 4800 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 9750 4500 9750 4800 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 10800 4500 10800 6000 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 8700 4500 8700 6000 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 4 1050 6300 1050 6000 5250 6000 5250 6300 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 3150 6000 3150 6300 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 3150 5700 3150 5850 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 3150 4200 3150 4650 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 3150 7200 3150 7350 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 4 2250 7800 2250 7500 4050 7500 4050 7800 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 4050 9000 4050 9300 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 5700 300 7200 300 7200 1200 5700 1200 5700 300 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 3 6300 1800 6450 1650 6600 1800 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 6450 1200 6450 1650 2 3 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 3900 5250 4050 5100 4200 5250 4050 5400 3900 5250 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 4500 4800 6000 4800 6000 5700 4500 5700 4500 4800 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 4200 5250 4500 5250 2 3 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 5400 2250 5550 2100 5700 2250 5550 2400 5400 2250 2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 5 3600 1800 5100 1800 5100 2700 3600 2700 3600 1800 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 5400 2250 5100 2250 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 5700 375 7200 375 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 5700 1875 7200 1875 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 2400 3375 3900 3375 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 2400 4875 3900 4875 2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2 9000 3375 10500 3375 4 0 0 0 0 0 18 0.0000 4 255 1230 4650 6900 LCPolygon\001 4 0 0 0 0 0 18 0.0000 4 195 795 2775 6900 LCBox\001 4 0 0 0 0 0 18 0.0000 4 195 945 1800 8400 LCMask\001 4 0 0 0 0 0 18 0.0000 4 195 1680 8925 5400 LCIntersection\001 4 0 0 0 0 0 18 0.0000 4 195 1530 11175 5400 LCDifference\001 4 0 0 0 0 0 18 0.0000 4 195 1050 7125 5400 LCUnion\001 4 0 0 0 0 0 18 0.0000 4 255 1125 2625 5250 LCRegion\001 4 0 0 0 0 0 18 0.0000 4 195 630 2850 5550 Fixed\001 4 0 0 0 0 0 18 0.0000 4 255 1125 2625 3750 LCRegion\001 4 0 0 0 0 0 18 0.0000 4 255 690 2850 4050 Single\001 4 0 0 0 0 0 18 0.0000 4 255 1125 5925 2400 LCRegion\001 4 0 0 0 0 0 18 0.0000 4 195 615 9450 4050 Multi\001 4 0 0 0 0 0 18 0.0000 4 255 1125 9225 3750 LCRegion\001 4 0 0 0 0 0 18 0.0000 4 255 1350 3375 9750 PagedArray\001 4 0 0 0 0 0 18 0.0000 4 195 855 3600 10050 \001 4 0 0 0 0 0 18 0.0000 4 255 1800 7800 6600 LCComplement\001 4 0 0 0 0 0 18 0.0000 4 195 1455 10050 6600 LCExtension\001 4 0 0 0 0 0 18 0.0000 4 255 1605 3300 8400 LCPagedMask\001 4 0 0 0 0 0 18 0.0000 4 255 1305 375 6900 LCEllipsoid\001 4 0 0 0 0 0 18 0.0000 4 195 795 6000 750 Lattice\001 4 0 0 0 0 0 18 0.0000 4 195 855 6000 1050 \001 4 0 0 0 0 0 18 0.0000 4 255 1485 4500 5250 ArrayLattice\001 4 0 0 0 0 0 18 0.0000 4 195 855 4800 5550 \001 4 0 0 0 0 0 14 0.0000 4 150 225 7350 2175 +1\001 4 0 0 0 0 0 18 0.0000 4 195 645 4050 2250 Slicer\001 4 0 0 0 0 0 14 0.0000 4 195 1290 3750 2625 (bounding box)\001 casacore-3.7.1/lattices/Lattices/Lattices_4.ps000066400000000000000000000200571476623553700212470ustar00rootroot00000000000000%!PS-Adobe-2.0 %%Title: Lattices_4.ps %%Creator: fig2dev Version 3.2 Patchlevel 0-beta2 %%CreationDate: Mon Apr 20 08:50:57 1998 %%For: gvd@duw01 (Ger van Diepen) %%Orientation: Landscape %%BoundingBox: 8 22 604 770 %%Pages: 1 %%BeginSetup %%IncludeFeature: *PageSize Letter %%EndSetup %%Magnification: 1.00 %%EndComments /$F2psDict 200 dict def $F2psDict begin $F2psDict /mtrx matrix put /col-1 {0 setgray} bind def /col0 {0.000 0.000 0.000 srgb} bind def /col1 {0.000 0.000 1.000 srgb} bind def /col2 {0.000 1.000 0.000 srgb} bind def /col3 {0.000 1.000 1.000 srgb} bind def /col4 {1.000 0.000 0.000 srgb} bind def /col5 {1.000 0.000 1.000 srgb} bind def /col6 {1.000 1.000 0.000 srgb} bind def /col7 {1.000 1.000 1.000 srgb} bind def /col8 {0.000 0.000 0.560 srgb} bind def /col9 {0.000 0.000 0.690 srgb} bind def /col10 {0.000 0.000 0.820 srgb} bind def /col11 {0.530 0.810 1.000 srgb} bind def /col12 {0.000 0.560 0.000 srgb} bind def /col13 {0.000 0.690 0.000 srgb} bind def /col14 {0.000 0.820 0.000 srgb} bind def /col15 {0.000 0.560 0.560 srgb} bind def /col16 {0.000 0.690 0.690 srgb} bind def /col17 {0.000 0.820 0.820 srgb} bind def /col18 {0.560 0.000 0.000 srgb} bind def /col19 {0.690 0.000 0.000 srgb} bind def /col20 {0.820 0.000 0.000 srgb} bind def /col21 {0.560 0.000 0.560 srgb} bind def /col22 {0.690 0.000 0.690 srgb} bind def /col23 {0.820 0.000 0.820 srgb} bind def /col24 {0.500 0.190 0.000 srgb} bind def /col25 {0.630 0.250 0.000 srgb} bind def /col26 {0.750 0.380 0.000 srgb} bind def /col27 {1.000 0.500 0.500 srgb} bind def /col28 {1.000 0.630 0.630 srgb} bind def /col29 {1.000 0.750 0.750 srgb} bind def /col30 {1.000 0.880 0.880 srgb} bind def /col31 {1.000 0.840 0.000 srgb} bind def end save -9.0 5.0 translate 90 rotate 1 -1 scale /cp {closepath} bind def /ef {eofill} bind def /gr {grestore} bind def /gs {gsave} bind def /sa {save} bind def /rs {restore} bind def /l {lineto} bind def /m {moveto} bind def /rm {rmoveto} bind def /n {newpath} bind def /s {stroke} bind def /sh {show} bind def /slc {setlinecap} bind def /slj {setlinejoin} bind def /slw {setlinewidth} bind def /srgb {setrgbcolor} bind def /rot {rotate} bind def /sc {scale} bind def /sd {setdash} bind def /ff {findfont} bind def /sf {setfont} bind def /scf {scalefont} bind def /sw {stringwidth} bind def /tr {translate} bind def /tnt {dup dup currentrgbcolor 4 -2 roll dup 1 exch sub 3 -1 roll mul add 4 -2 roll dup 1 exch sub 3 -1 roll mul add 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb} bind def /shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul 4 -2 roll mul srgb} bind def /$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def /$F2psEnd {$F2psEnteredState restore end} def %%EndProlog $F2psBegin 10 setmiterlimit n 0 10252 m 0 0 l 12784 0 l 12784 10252 l cp clip 0.06000 0.06000 sc %%Page: 1 1 % Polyline 7.500 slw n 6900 4800 m 8400 4800 l 8400 5700 l 6900 5700 l cp gs col0 s gr % Polyline n 2400 6300 m 3900 6300 l 3900 7200 l 2400 7200 l cp gs col0 s gr % Polyline n 4500 6300 m 6000 6300 l 6000 7200 l 4500 7200 l cp gs col0 s gr % Polyline n 11175 4800 m 12675 4800 l 12675 5700 l 11175 5700 l cp gs col0 s gr % Polyline n 3300 7800 m 4800 7800 l 4800 8700 l 3300 8700 l cp gs col0 s gr % Polyline n 3000 7500 m 3150 7350 l 3300 7500 l gs col0 s gr % Polyline n 3900 8850 m 4050 8700 l 4200 8850 l 4050 9000 l cp gs col0 s gr % Polyline n 1500 7800 m 3000 7800 l 3000 8700 l 1500 8700 l cp gs col0 s gr % Polyline n 300 6300 m 1800 6300 l 1800 7200 l 300 7200 l cp gs col0 s gr % Polyline n 3300 9300 m 4800 9300 l 4800 10200 l 3300 10200 l cp gs col0 s gr % Polyline n 7950 6000 m 9450 6000 l 9450 6900 l 7950 6900 l cp gs col0 s gr % Polyline n 10050 6000 m 11550 6000 l 11550 6900 l 10050 6900 l cp gs col0 s gr % Polyline n 9000 4800 m 10500 4800 l 10500 5700 l 9000 5700 l cp gs col0 s gr % Polyline n 2400 4800 m 3900 4800 l 3900 5700 l 2400 5700 l cp gs col0 s gr % Polyline n 2400 3300 m 3900 3300 l 3900 4200 l 2400 4200 l cp gs col0 s gr % Polyline n 5700 1800 m 7200 1800 l 7200 2700 l 5700 2700 l cp gs col0 s gr % Polyline n 6300 3000 m 6450 2850 l 6600 3000 l gs col0 s gr % Polyline n 6450 2700 m 6450 2850 l gs col0 s gr % Polyline n 10800 3750 m 11400 3750 l 11400 2250 l 7200 2250 l gs col0 s gr % Polyline n 10500 3750 m 10650 3600 l 10800 3750 l 10650 3900 l cp gs col0 s gr % Polyline n 9000 3300 m 10500 3300 l 10500 4200 l 9000 4200 l cp gs col0 s gr % Polyline n 9600 4500 m 9750 4350 l 9900 4500 l gs col0 s gr % Polyline n 3000 4800 m 3150 4650 l 3300 4800 l gs col0 s gr % Polyline n 3000 6000 m 3150 5850 l 3300 6000 l gs col0 s gr % Polyline n 3150 3300 m 3150 3000 l 9750 3000 l 9750 3300 l gs col0 s gr % Polyline n 9750 4200 m 9750 4350 l gs col0 s gr % Polyline n 7650 4800 m 7650 4500 l 11850 4500 l 11850 4800 l gs col0 s gr % Polyline n 9750 4500 m 9750 4800 l gs col0 s gr % Polyline n 10800 4500 m 10800 6000 l gs col0 s gr % Polyline n 8700 4500 m 8700 6000 l gs col0 s gr % Polyline n 1050 6300 m 1050 6000 l 5250 6000 l 5250 6300 l gs col0 s gr % Polyline n 3150 6000 m 3150 6300 l gs col0 s gr % Polyline n 3150 5700 m 3150 5850 l gs col0 s gr % Polyline n 3150 4200 m 3150 4650 l gs col0 s gr % Polyline n 3150 7200 m 3150 7350 l gs col0 s gr % Polyline n 2250 7800 m 2250 7500 l 4050 7500 l 4050 7800 l gs col0 s gr % Polyline n 4050 9000 m 4050 9300 l gs col0 s gr % Polyline n 5700 300 m 7200 300 l 7200 1200 l 5700 1200 l cp gs col0 s gr % Polyline n 6300 1800 m 6450 1650 l 6600 1800 l gs col0 s gr % Polyline n 6450 1200 m 6450 1650 l gs col0 s gr % Polyline n 3900 5250 m 4050 5100 l 4200 5250 l 4050 5400 l cp gs col0 s gr % Polyline n 4500 4800 m 6000 4800 l 6000 5700 l 4500 5700 l cp gs col0 s gr % Polyline n 4200 5250 m 4500 5250 l gs col0 s gr % Polyline n 5400 2250 m 5550 2100 l 5700 2250 l 5550 2400 l cp gs col0 s gr % Polyline n 3600 1800 m 5100 1800 l 5100 2700 l 3600 2700 l cp gs col0 s gr % Polyline n 5400 2250 m 5100 2250 l gs col0 s gr % Polyline n 5700 375 m 7200 375 l gs col0 s gr % Polyline n 5700 1875 m 7200 1875 l gs col0 s gr % Polyline n 2400 3375 m 3900 3375 l gs col0 s gr % Polyline n 2400 4875 m 3900 4875 l gs col0 s gr % Polyline n 9000 3375 m 10500 3375 l gs col0 s gr /Times-Roman ff 270.00 scf sf 4650 6900 m gs 1 -1 sc (LCPolygon) col0 sh gr /Times-Roman ff 270.00 scf sf 2775 6900 m gs 1 -1 sc (LCBox) col0 sh gr /Times-Roman ff 270.00 scf sf 1800 8400 m gs 1 -1 sc (LCMask) col0 sh gr /Times-Roman ff 270.00 scf sf 8925 5400 m gs 1 -1 sc (LCIntersection) col0 sh gr /Times-Roman ff 270.00 scf sf 11175 5400 m gs 1 -1 sc (LCDifference) col0 sh gr /Times-Roman ff 270.00 scf sf 7125 5400 m gs 1 -1 sc (LCUnion) col0 sh gr /Times-Roman ff 270.00 scf sf 2625 5250 m gs 1 -1 sc (LCRegion) col0 sh gr /Times-Roman ff 270.00 scf sf 2850 5550 m gs 1 -1 sc (Fixed) col0 sh gr /Times-Roman ff 270.00 scf sf 2625 3750 m gs 1 -1 sc (LCRegion) col0 sh gr /Times-Roman ff 270.00 scf sf 2850 4050 m gs 1 -1 sc (Single) col0 sh gr /Times-Roman ff 270.00 scf sf 5925 2400 m gs 1 -1 sc (LCRegion) col0 sh gr /Times-Roman ff 270.00 scf sf 9450 4050 m gs 1 -1 sc (Multi) col0 sh gr /Times-Roman ff 270.00 scf sf 9225 3750 m gs 1 -1 sc (LCRegion) col0 sh gr /Times-Roman ff 270.00 scf sf 3375 9750 m gs 1 -1 sc (PagedArray) col0 sh gr /Times-Roman ff 270.00 scf sf 3600 10050 m gs 1 -1 sc () col0 sh gr /Times-Roman ff 270.00 scf sf 7800 6600 m gs 1 -1 sc (LCComplement) col0 sh gr /Times-Roman ff 270.00 scf sf 10050 6600 m gs 1 -1 sc (LCExtension) col0 sh gr /Times-Roman ff 270.00 scf sf 3300 8400 m gs 1 -1 sc (LCPagedMask) col0 sh gr /Times-Roman ff 270.00 scf sf 375 6900 m gs 1 -1 sc (LCEllipsoid) col0 sh gr /Times-Roman ff 270.00 scf sf 6000 750 m gs 1 -1 sc (Lattice) col0 sh gr /Times-Roman ff 270.00 scf sf 6000 1050 m gs 1 -1 sc () col0 sh gr /Times-Roman ff 270.00 scf sf 4500 5250 m gs 1 -1 sc (ArrayLattice) col0 sh gr /Times-Roman ff 270.00 scf sf 4800 5550 m gs 1 -1 sc () col0 sh gr /Times-Roman ff 210.00 scf sf 7350 2175 m gs 1 -1 sc (+1) col0 sh gr /Times-Roman ff 270.00 scf sf 4050 2250 m gs 1 -1 sc (Slicer) col0 sh gr /Times-Roman ff 210.00 scf sf 3750 2625 m gs 1 -1 sc (\(bounding box\)) col0 sh gr $F2psEnd rs showpage casacore-3.7.1/lattices/Lattices/Lattices_tmpl.cc000066400000000000000000000035511476623553700220230ustar00rootroot00000000000000//# Array_tmpl.cc: Explicit Array template instantiations //# Copyright (C) 2015 //# Associated Universities, Inc. Washington DC, USA, //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# Includes #include #include #include #include #include //# Instantiate extern templates for often used types. namespace casacore { template class LatticeIterInterface; template class LatticeStatistics; template class Lattice; template class Lattice; template class PagedArray; template class PagedArray; template class SubLattice; template class SubLattice; } casacore-3.7.1/lattices/Lattices/MaskedLattice.h000066400000000000000000000317151476623553700215760ustar00rootroot00000000000000//# MaskedLattice.h: Abstract base class for array-like classes with masks //# Copyright (C) 1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_MASKEDLATTICE_H #define LATTICES_MASKEDLATTICE_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class LatticeRegion; // // A templated, abstract base class for array-like objects with masks. // // // // // //
      • IPosition //
      • Abstract Base class Inheritance - try "Advanced C++" by James // O. Coplien, Ch. 5. // // // Lattice: "A regular, periodic configuration of points, particles, // or objects, throughout an area of a space..." (American Heritage Directory) // This definition matches our own: an n-dimensional arrangement of items, // on regular orthogonal axes. // // // This pure abstract base class defines the operations which may be performed // on any concrete class derived from it. It has only a few non-pure virtual // member functions. // The fundamental contribution of this class, therefore, is that it // defines the operations derived classes must provide: //
          //
        • how to extract a "slice" (or sub-array, or subsection) from // a Lattice. //
        • how to copy a slice in. //
        • how to get and put a single element //
        • how to apply a function to all elements //
        • various shape related functions. //
        // Lattices are always zero origined. //
        // // Because Lattice is an abstract base class, an actual instance of this // class cannot be constructed. However the interface it defines can be used // inside a function. This is always recommended as it allows Functions // which have Lattices as arguments to work for any derived class. // // I will give a few examples here and then refer the reader to the // ArrayLattice class (a memory resident // Lattice) and the PagedArray class (a // disk based Lattice) which contain further examples with concrete // classes (rather than an abstract one). All the examples shown below are used // in the dLattice.cc demo program. // //

        Example 1:

        // This example calculates the mean of the Lattice. Because Lattices can be too // large to fit into physical memory it is not good enough to simply use // getSlice to read all the elements into an Array. Instead the // Lattice is accessed in chunks which can fit into memory (the size is // determined by the maxPixels and niceCursorShape // functions). The LatticeIterator::cursor() function then returns // each of these chunks as an Array and the standard Array based functions are // used to calculate the mean on each of these chunks. Functions like this one // are the recommended way to access Lattices as the // LatticeIterator will correctly // setup any required caches. // // // Complex latMean(const Lattice& lat) { // const uInt cursorSize = lat.advisedMaxPixels(); // const IPosition cursorShape = lat.niceCursorShape(cursorSize); // const IPosition latticeShape = lat.shape(); // Complex currentSum = 0.0f; // size_t nPixels = 0; // RO_LatticeIterator iter(lat, // LatticeStepper(latticeShape, cursorShape)); // for (iter.reset(); !iter.atEnd(); iter++){ // currentSum += sum(iter.cursor()); // nPixels += iter.cursor().nelements(); // } // return currentSum/nPixels; // } // // //

        Example 2:

        // Sometimes it will be neccesary to access slices of a Lattice in a nearly // random way. Often this can be done using the subSection commands in the // LatticeStepper class. But it is also // possible to use the getSlice and putSlice functions. The following example // does a two-dimensional Real to Complex Fourier transform. This example is // restricted to four-dimensional Arrays (unlike the previous example) and does // not set up any caches (caching is currently only used with PagedArrays). So // only use getSlice and putSlice when things cannot be done using // LatticeIterators. // // // void FFT2DReal2Complex(Lattice& result, // const Lattice& input){ // AlwaysAssert(input.ndim() == 4, AipsError); // const IPosition shape = input.shape(); // const uInt nx = shape(0); // AlwaysAssert (nx > 1, AipsError); // const uInt ny = shape(1); // AlwaysAssert (ny > 1, AipsError); // const uInt npol = shape(2); // const uInt nchan = shape(3); // const IPosition resultShape = result.shape(); // AlwaysAssert(resultShape.nelements() == 4, AipsError); // AlwaysAssert(resultShape(3) == nchan, AipsError); // AlwaysAssert(resultShape(2) == npol, AipsError); // AlwaysAssert(resultShape(1) == ny, AipsError); // AlwaysAssert(resultShape(0) == nx/2 + 1, AipsError); // // const IPosition inputSliceShape(4,nx,ny,1,1); // const IPosition resultSliceShape(4,nx/2+1,ny,1,1); // COWPtr> // inputArrPtr(new Array(inputSliceShape.nonDegenerate())); // Array resultArray(resultSliceShape.nonDegenerate()); // FFTServer FFT2D(inputSliceShape.nonDegenerate()); // // IPosition start(4,0); // Bool isARef; // for (uInt c = 0; c < nchan; c++){ // for (uInt p = 0; p < npol; p++){ // isARef = input.getSlice(inputArrPtr, // Slicer(start,inputSliceShape), True); // FFT2D.fft(resultArray, *inputArrPtr); // result.putSlice(resultArray, start); // start(2) += 1; // } // start(2) = 0; // start(3) += 1; // } // } // // //

        Example 3:

        // Occasionally you may want to access a few elements of a Lattice without // all the difficulty involved in setting up Iterators or calling getSlice // and putSlice. This is demonstrated in the example below and uses the // parenthesis operator, along with the LatticeValueRef companion // class. Using these functions to access many elements of a Lattice is not // recommended as this is the slowest access method. // // In this example an ideal point spread function will be inserted into an // empty Lattice. As with the previous examples all the action occurs // inside a function because Lattice is an interface (abstract) class. // // // void makePsf(Lattice& psf) { // const IPosition centrePos = psf.shape()/2; // psf.set(0.0f); // this sets all the elements to zero // // As it uses a LatticeIterator it is efficient // psf(centrePos) = 1; // This sets just the centre element to one // AlwaysAssert(near(psf(centrePos), 1.0f, 1E-6), AipsError); // AlwaysAssert(near(psf(centrePos*0), 0.0f, 1E-6), AipsError); // } // //
        // // Creating an abstract base class which provides a common interface between // memory and disk based arrays has a number of advantages. //
          //
        • It allows functions common to all arrays to be written independent // of the way the data is stored. This is illustrated in the three examples // above. //
        • It reduces the learning curve for new users who only have to become // familiar with one interface (ie. Lattice) rather than distinct interfaces // for different array types. //
        //
        //# //#
      • //# template class MaskedLattice : public Lattice { //# Make members of parent class known. public: using Lattice::ndim; using Lattice::shape; public: // Default constructor. MaskedLattice() : itsDefRegPtr(0) {;} // Copy constructor. MaskedLattice (const MaskedLattice&); // a virtual destructor is needed so that it will use the actual destructor // in the derived class virtual ~MaskedLattice(); // Make a copy of the object (reference semantics). // virtual MaskedLattice* cloneML() const = 0; virtual Lattice* clone() const; // // Has the object really a mask? // The default implementation returns True if the MaskedLattice has // a region with a mask. virtual Bool isMasked() const; // Does the lattice have a pixelmask? // The default implementation returns False. virtual Bool hasPixelMask() const; // Get access to the pixelmask. // An exception is thrown if the lattice does not have a pixelmask. // virtual const Lattice& pixelMask() const; virtual Lattice& pixelMask(); // // Get the region used. // This is in principle the region pointed to by getRegionPtr. // However, if that pointer is 0, it returns a LatticeRegion for the // full image. const LatticeRegion& region() const; // Get the mask or a slice from the mask. // This is the mask formed by combination of the possible pixelmask of the // lattice and the possible mask of the region taken from the lattice. // If there is no mask, it still works fine. // In that case it sizes the buffer correctly and sets it to True. // Bool getMask (COWPtr>& buffer, Bool removeDegenerateAxes=False) const; Bool getMaskSlice (COWPtr>& buffer, const Slicer& section, Bool removeDegenerateAxes=False) const; Bool getMaskSlice (COWPtr>& buffer, const IPosition& start, const IPosition& shape, Bool removeDegenerateAxes=False) const; Bool getMaskSlice (COWPtr>& buffer, const IPosition& start, const IPosition& shape, const IPosition& stride, Bool removeDegenerateAxes=False) const; Bool getMask (Array& buffer, Bool removeDegenerateAxes=False); Bool getMaskSlice (Array& buffer, const Slicer& section, Bool removeDegenerateAxes=False); Bool getMaskSlice (Array& buffer, const IPosition& start, const IPosition& shape, Bool removeDegenerateAxes=False); Bool getMaskSlice (Array& buffer, const IPosition& start, const IPosition& shape, const IPosition& stride, Bool removeDegenerateAxes=False); Array getMask (Bool removeDegenerateAxes=False) const; Array getMaskSlice (const Slicer& section, Bool removeDegenerateAxes=False) const; Array getMaskSlice (const IPosition& start, const IPosition& shape, Bool removeDegenerateAxes=False) const; Array getMaskSlice (const IPosition& start, const IPosition& shape, const IPosition& stride, Bool removeDegenerateAxes=False) const; // // The function (in the derived classes) doing the actual work. // These functions are public, so they can be used internally in the // various Lattice classes. //
        However, doGetMaskSlice does not call Slicer::inferShapeFromSource // to fill in possible unspecified section values. Therefore one // should normally use one of the getMask(Slice) functions. doGetMaskSlice // should be used with care and only when performance is an issue. //
        The default implementation gets the mask from the region // and fills the buffer with True values if there is no region. virtual Bool doGetMaskSlice (Array& buffer, const Slicer& section); protected: // Assignment can only be used by derived classes. MaskedLattice& operator= (const MaskedLattice&); // Get a pointer to the region used. // It can return 0 meaning that the MaskedLattice is the full lattice. virtual const LatticeRegion* getRegionPtr() const = 0; private: mutable LatticeRegion* itsDefRegPtr; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/Lattices/MaskedLattice.tcc000066400000000000000000000202041476623553700221070ustar00rootroot00000000000000//# MaskedLattice.cc: Abstract base class for array-like classes with masks //# Copyright (C) 1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_MASKEDLATTICE_TCC #define LATTICES_MASKEDLATTICE_TCC #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template MaskedLattice::MaskedLattice (const MaskedLattice& that) : Lattice(), itsDefRegPtr (0) { if (that.itsDefRegPtr != 0) { itsDefRegPtr = new LatticeRegion (*that.itsDefRegPtr); } } template MaskedLattice::~MaskedLattice() { delete itsDefRegPtr; } template MaskedLattice& MaskedLattice::operator= (const MaskedLattice& that) { if (this != &that) { delete itsDefRegPtr; itsDefRegPtr = 0; if (that.itsDefRegPtr != 0) { itsDefRegPtr = new LatticeRegion (*that.itsDefRegPtr); } } return *this; } template Lattice* MaskedLattice::clone() const { return cloneML(); } template Bool MaskedLattice::isMasked() const { const LatticeRegion* ptr = getRegionPtr(); if (ptr == 0) { return False; } return ptr->hasMask(); } template Bool MaskedLattice::hasPixelMask() const { return False; } template const Lattice& MaskedLattice::pixelMask() const { throw (AipsError ("MaskedLattice::pixelMask - no pixelmask available")); //# Make the compiler happy. return *itsDefRegPtr; } template Lattice& MaskedLattice::pixelMask() { throw (AipsError ("MaskedLattice::pixelMask - no pixelmask available")); //# Make the compiler happy. return *itsDefRegPtr; } template const LatticeRegion& MaskedLattice::region() const { // If there is a region, return it. const LatticeRegion* ptr = getRegionPtr(); if (ptr != 0) { return *ptr; } // No region, so use the one in the MaskedLattice itself which // describes the entire lattice. Create it if it does not exist yet. // Check if its shape still matches. if (itsDefRegPtr != 0) { if (itsDefRegPtr->slicer().length().isEqual (shape())) { return *itsDefRegPtr; } delete itsDefRegPtr; itsDefRegPtr = 0; } itsDefRegPtr = new LatticeRegion (LCBox(shape())); return *itsDefRegPtr; } template Bool MaskedLattice::getMask (COWPtr>& buffer, Bool removeDegenerateAxes) const { uInt nd = ndim(); return getMaskSlice (buffer, Slicer(IPosition(nd,0), shape()), removeDegenerateAxes); } template Bool MaskedLattice::getMask (Array& buffer, Bool removeDegenerateAxes) { uInt nd = ndim(); return getMaskSlice (buffer, Slicer(IPosition(nd,0), shape()), removeDegenerateAxes); } template Array MaskedLattice::getMask (Bool removeDegenerateAxes) const { uInt nd = ndim(); return getMaskSlice (Slicer(IPosition(nd,0), shape()), removeDegenerateAxes); } template Bool MaskedLattice::getMaskSlice (COWPtr>& buffer, const IPosition& start, const IPosition& shape, Bool removeDegenerateAxes) const { return getMaskSlice (buffer, Slicer(start, shape), removeDegenerateAxes); } template Bool MaskedLattice::getMaskSlice (COWPtr>& buffer, const IPosition& start, const IPosition& shape, const IPosition& stride, Bool removeDegenerateAxes) const { return getMaskSlice (buffer, Slicer(start, shape, stride), removeDegenerateAxes); } template Bool MaskedLattice::getMaskSlice (COWPtr>& buffer, const Slicer& section, Bool removeDegenerateAxes) const { // Cast pointer to non-const. // This is safe, since the array is copied when needed by COWptr. MaskedLattice* This = (MaskedLattice*)this; // The COWPtr takes over the pointer to the array. std::unique_ptr> arr(new Array); Bool isARef = This->getMaskSlice (*arr, section, removeDegenerateAxes); buffer = COWPtr> (arr.release(), isARef); return False; } template Bool MaskedLattice::getMaskSlice (Array& buffer, const IPosition& start, const IPosition& shape, Bool removeDegenerateAxes) { return getMaskSlice (buffer, Slicer(start, shape), removeDegenerateAxes); } template Bool MaskedLattice::getMaskSlice (Array& buffer, const IPosition& start, const IPosition& shape, const IPosition& stride, Bool removeDegenerateAxes) { return getMaskSlice (buffer, Slicer(start, shape, stride), removeDegenerateAxes); } template Bool MaskedLattice::getMaskSlice (Array& buffer, const Slicer& section, Bool removeDegenerateAxes) { Bool isARef; // When the slicer is fixed, it can be used immediately. // Otherwise unspecified values are to be filled in. if (section.isFixed()) { isARef = doGetMaskSlice (buffer, section); } else { IPosition blc,trc,inc; section.inferShapeFromSource (shape(), blc, trc, inc); isARef = doGetMaskSlice (buffer, Slicer(blc,trc,inc,Slicer::endIsLast)); } if (removeDegenerateAxes) { Array tmp = buffer.nonDegenerate(); buffer.reference (tmp); } return isARef; } template Array MaskedLattice::getMaskSlice (const IPosition& start, const IPosition& shape, Bool removeDegenerateAxes) const { return getMaskSlice (Slicer(start,shape), removeDegenerateAxes); } template Array MaskedLattice::getMaskSlice (const IPosition& start, const IPosition& shape, const IPosition& stride, Bool removeDegenerateAxes) const { return getMaskSlice (Slicer(start,shape,stride), removeDegenerateAxes); } template Array MaskedLattice::getMaskSlice (const Slicer& section, Bool removeDegenerateAxes) const { // Cast pointer to non-const. // This is safe, since the array is copied when needed. MaskedLattice* This = (MaskedLattice*)this; // Note that getMaskSlice is used to be sure that section getMasks filled // when needed. Array arr; Bool isARef = This->getMaskSlice (arr, section, removeDegenerateAxes); // When not referenced, return it as such. // Otherwise make a copy. if (!isARef) { return arr; } Array tmp; tmp = arr; return tmp; } template Bool MaskedLattice::doGetMaskSlice (Array& buffer, const Slicer& section) { // Note that Slicer::inferShapeFromSource has already been called // by getMaskSlice. const LatticeRegion* ptr = getRegionPtr(); if (ptr == 0) { buffer.resize (section.length()); buffer = True; return False; } return const_cast(ptr)->doGetSlice (buffer, section); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/Lattices/MaskedLatticeIterator.h000066400000000000000000000205741476623553700233110ustar00rootroot00000000000000//# MaskedLatticeIterator.h: Iterators for Masked Lattices: readonly //# Copyright (C) 2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_MASKEDLATTICEITERATOR_H #define LATTICES_MASKEDLATTICEITERATOR_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A readonly iterator for masked Lattices. // // // // // //
      • MaskedLattice //
      • RO_LatticeIterator // // // The leading "RO" is shorthand for "readonly", which indicates that an // RO_MaskedLatticeIterator is used for traversing a masked lattice, // examining and possibly extracting its contents, but not for modifying it. // // // This class provides a convenient way to traverse any class derived from // MaskedLattice. It is derived from class // RO_LatticeIterator, so it // provides the same iterator capabilities. // On top of that it offers the function getMask to get the // contents of the mask at the current iterator position. // // In principle, iteration through a MaskedLattice can be done as: // // void someFunc (const MaskedLattice& lattice) // { // RO_LatticeIterator iter(lattice); // Array mask; // while (! iter.atEnd()) { // const Array& array = iter.cursor(); // lattice.getMaskSlice (mask, iter.position(), array.shape()); // iter++; // } // } // // Using a MaskedLatticeIterator makes getting the mask slightly more // convenient. // // void someFunc (const MaskedLattice& lattice) // { // RO_MaskedLatticeIterator iter(lattice); // Array mask; // while (! iter.atEnd()) { // const Array& array = iter.cursor(); // iter.getMask (mask); // iter++; // } // } // // However, the most important reason to use MaskedLatticeIterator is // performance. If the underlying lattice is a LatticeExpr object, // the expression will be evaluated twice if a LatticeIterator object // is used. The reason is that the lattice in the LatticeIterator is // a different object from the lattice object used to get the mask. // Hence, the optimization put in LatticeExpr is not used. // When using a MaskedLatticeIterator the same lattice object is used // to get data and mask. // // // The performance gain for LatticeExpr was the most important reason // to develop this class. // //# //#
      • //# template class RO_MaskedLatticeIterator: public RO_LatticeIterator { //# Make members of parent class known. public: using RO_LatticeIterator::isNull; using RO_LatticeIterator::position; using RO_LatticeIterator::endPosition; using RO_LatticeIterator::cursorShape; public: // The default constructor creates an empty object which is practically // unusable. // It can only be used as the source or target of an assignment. It can // also be used as the source for the copy constructor and the copy function. // Other functions do not check if the object is empty and will usually // give a segmentation fault. // The function isNull() can be used to test if the object is empty. RO_MaskedLatticeIterator(); // Construct the Iterator with the supplied data. // It uses a TileStepper as the default iteration strategy. // useRef=True means that if possible the cursor arrays returned // reference the data in the underlying lattice. This is only possible // for ArrayLattice objects (or e.g. a SubLattice using it). explicit RO_MaskedLatticeIterator (const MaskedLattice& data, Bool useRef=True); // Construct the Iterator with the supplied data, and iteration strategy RO_MaskedLatticeIterator (const MaskedLattice& data, const LatticeNavigator& method, Bool useRef=True); // Construct the Iterator with the supplied data. // It uses a LatticeStepper with the supplied cursor shape as the // iteration strategy. RO_MaskedLatticeIterator (const MaskedLattice& data, const IPosition& cursorShape, Bool useRef=True); // The copy constructor uses reference semantics (ie. NO real copy is made). // The function copy can be used to make a true copy. RO_MaskedLatticeIterator (const RO_MaskedLatticeIterator& other); // Destructor (cleans up dangling references and releases memory) ~RO_MaskedLatticeIterator(); // Assignment uses reference semantics (ie. NO real copy is made). // The function copy can be used to make a true copy. RO_MaskedLatticeIterator& operator= (const RO_MaskedLatticeIterator&); // Make a copy of the iterator object. // This means that an independent navigator object is created to // be able to iterate independently through the same MaskedLattice. // The position in the copied navigator is the same as the original. // The reset function has to be used to start at the beginning. //
        Note that if the MaskedLattice uses a cache (e.g. PagedArray), the // cache is shared by the iterators. RO_MaskedLatticeIterator copy() const; // Return the underlying MaskedLattice object. MaskedLattice& lattice() const { return const_cast&>(*itsMaskLattPtr); } // Is the underlying MaskedLattice really masked? Bool isMasked() const { return itsMaskLattPtr->isMasked(); } // Get the mask for the current position. // It returns the same flag as // MaskedLattice::getMaskSlice. // Bool getMask (COWPtr>&, Bool removeDegenerateAxes=False) const; Bool getMask (Array&, Bool removeDegenerateAxes=False) const; Array getMask (Bool removeDegenerateAxes=False) const; // private: // Construct from a LatticeIterator (for copy function). RO_MaskedLatticeIterator (const RO_LatticeIterator&, const RO_MaskedLatticeIterator&); // Fill the pointer with a pointer to the masked lattice. // This pointer is a casted copy of the lattice pointer in the base class. // In this way they share the same MaskedLattice object, which is needed // for optimal performance of e.g. LatticeExpr. // Otherwise getting data from the lattice and from the mask would // result in 2 evaluations of the expression. // However, the lattice can be a PagedArray (for example, for PagedImage). // In that case a clone of the original MaskedLattice is used. void fillPtr (const MaskedLattice& mlattice); // The shared pointer is used for automatic deletion. // If not null, it is the same as the normal pointer below. std::shared_ptr> itsMaskLattShrPtr; // Pointer to the MaskedLattice. // Deletion (if needed) is done by the shared pointer above. MaskedLattice* itsMaskLattPtr; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/Lattices/MaskedLatticeIterator.tcc000066400000000000000000000116711476623553700236310ustar00rootroot00000000000000//# MaskedLatticeIterator.cc: defines the RO_MaskedLatticeIterator class //# Copyright (C) 2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_MASKEDLATTICEITERATOR_TCC #define LATTICES_MASKEDLATTICEITERATOR_TCC #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template RO_MaskedLatticeIterator::RO_MaskedLatticeIterator() : itsMaskLattPtr (0) {} template RO_MaskedLatticeIterator::RO_MaskedLatticeIterator (const MaskedLattice& mlattice, Bool useRef) : RO_LatticeIterator (mlattice, useRef) { fillPtr (mlattice); } template RO_MaskedLatticeIterator::RO_MaskedLatticeIterator (const MaskedLattice& mlattice, const LatticeNavigator& method, Bool useRef) : RO_LatticeIterator (mlattice, method, useRef) { fillPtr (mlattice); } template RO_MaskedLatticeIterator::RO_MaskedLatticeIterator (const MaskedLattice& mlattice, const IPosition& cursorShape, Bool useRef) : RO_LatticeIterator (mlattice, cursorShape, useRef) { fillPtr (mlattice); } template RO_MaskedLatticeIterator::RO_MaskedLatticeIterator (const RO_MaskedLatticeIterator& other) : RO_LatticeIterator (other), itsMaskLattShrPtr (other.itsMaskLattShrPtr), itsMaskLattPtr (other.itsMaskLattPtr) {} template RO_MaskedLatticeIterator::RO_MaskedLatticeIterator (const RO_LatticeIterator& other, const RO_MaskedLatticeIterator& otherm) : RO_LatticeIterator (other) { if (!isNull()) { fillPtr (otherm.lattice()); } } template RO_MaskedLatticeIterator::~RO_MaskedLatticeIterator() {} template RO_MaskedLatticeIterator& RO_MaskedLatticeIterator::operator= (const RO_MaskedLatticeIterator& other) { if (this != &other) { RO_LatticeIterator::operator= (other); itsMaskLattShrPtr = other.itsMaskLattShrPtr; itsMaskLattPtr = other.itsMaskLattPtr; } return *this; } template RO_MaskedLatticeIterator RO_MaskedLatticeIterator::copy() const { if (isNull()) { return RO_MaskedLatticeIterator(); } return RO_MaskedLatticeIterator(RO_LatticeIterator::copy(), *this); } template void RO_MaskedLatticeIterator::fillPtr (const MaskedLattice& mlattice) { Lattice* lptr = &(RO_LatticeIterator::lattice()); MaskedLattice* mptr = dynamic_cast*>(lptr); if (mptr) { itsMaskLattShrPtr.reset(); // no deletion of the pointer itsMaskLattPtr = mptr; } else { itsMaskLattShrPtr.reset (mlattice.cloneML()); itsMaskLattPtr = itsMaskLattShrPtr.get(); } } template Array RO_MaskedLatticeIterator::getMask (Bool removeDegenerateAxes) const { return itsMaskLattPtr->getMaskSlice (Slicer(position(), endPosition(), Slicer::endIsLast), removeDegenerateAxes); } template Bool RO_MaskedLatticeIterator::getMask (COWPtr>& arr, Bool removeDegenerateAxes) const { return itsMaskLattPtr->getMaskSlice (arr, position(), cursorShape(), removeDegenerateAxes); } template Bool RO_MaskedLatticeIterator::getMask (Array& arr, Bool removeDegenerateAxes) const { return itsMaskLattPtr->getMaskSlice (arr, position(), cursorShape(), removeDegenerateAxes); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/Lattices/PagedArrIter.h000066400000000000000000000113711476623553700213710ustar00rootroot00000000000000//# PagedArrIter.h: A concrete iterator for use with PagedArray's. //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_PAGEDARRITER_H #define LATTICES_PAGEDARRITER_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A read/write Lattice iterator for PagedArrays. // // // // // //
      • PagedArray //
      • LatticeIterator //
      • LatticeIterInterface // //
      • letter/envelope schemes, eg. Coplien, "Advanced C++", ch 5.5 // // // The PagedArrIter class name is a contraction of Paged Array Iterator // and reflects its role as the methods for iterating through Lattices which // are resident on disk. // // // This class is not meant for general use. Instead class // LatticeIterator should be used // to iterate through a PagedArray or any other // Lattice object // (like a ArrayLattice). //

        // PagedArrIter is derived from LatticeIterInterface and implements // the iterator for a PagedArray // object. This iterator is somewhat special because it sets the // PagedArray cache size at the start of an iteration. // // // For for each derivation of Lattice to make as efficient an iterator as // possible. // The letter/envelope scheme allowed us to hide the special bits in // classes like the one you see here. // // //

      • Restricted to the type of the PagedArray argument in the // constructors // //# //#
      • //# template class PagedArrIter : public LatticeIterInterface { friend class PagedArray; //# Make members of parent class known. protected: using LatticeIterInterface::rewriteData; using LatticeIterInterface::itsNavPtr; protected: // Construct the Iterator with the supplied data, and iteration strategy PagedArrIter (const PagedArray& data, const LatticeNavigator& method, Bool useRef); // The copy constructor uses reference sematics for the PagedArray and // copy semantics for the cursor and Navigator. This way the newly // constructed PagedArrIter can independently iterate through the same // data set. (with the same cursor shape etc.) PagedArrIter (const PagedArrIter& other); // Destructor (cleans up dangling references and releases cursor memory) virtual ~PagedArrIter(); // The assignment operator uses reference sematics for the PagedArray and // copy semantics for the cursor and Navigator. This way the // PagedArrIter objects share the same data set but independently iterate // with cursors of the same size. PagedArrIter& operator= (const PagedArrIter& other); // Clone the object. virtual LatticeIterInterface* clone() const; private: // Setup the cache in the tiled storage manager. void setupTileCache(); // reference to the PagedArray PagedArray itsData; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/Lattices/PagedArrIter.tcc000066400000000000000000000061701476623553700217140ustar00rootroot00000000000000//# PagedArrIter.cc: a concrete iterator for use with PagedArray's. //# Copyright (C) 1994,1995,1996,1997,1999,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_PAGEDARRITER_TCC #define LATTICES_PAGEDARRITER_TCC #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template PagedArrIter::PagedArrIter (const PagedArray& data, const LatticeNavigator& nav, Bool useRef) : LatticeIterInterface (data, nav, useRef), itsData (data) { setupTileCache(); } template PagedArrIter::PagedArrIter (const PagedArrIter& other) : LatticeIterInterface (other), itsData (other.itsData) {} template PagedArrIter::~PagedArrIter() { itsData.clearCache(); } template PagedArrIter& PagedArrIter::operator= (const PagedArrIter& other) { if (this != &other) { rewriteData(); itsData.clearCache(); LatticeIterInterface::operator= (other); itsData = other.itsData; } return *this; } template LatticeIterInterface* PagedArrIter::clone() const { return new PagedArrIter (*this); } template void PagedArrIter::setupTileCache() { const ROTiledStManAccessor& acc = itsData.accessor(); uInt rownr = itsData.rowNumber(); uInt cacheSize = itsNavPtr->calcCacheSize (acc.hypercubeShape(rownr), acc.tileShape(rownr), acc.maximumCacheSize(), acc.bucketSize(rownr)); itsData.setCacheSizeInTiles (cacheSize); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/Lattices/PagedArray.h000066400000000000000000000650141476623553700211020ustar00rootroot00000000000000//# PagedArray.h: templated Lattice, paged from disk to memory on demand //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_PAGEDARRAY_H #define LATTICES_PAGEDARRAY_H //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A Lattice that is read from or written to disk. // // // // // //
      • Lattice //
      • TiledShape // // // "Demand paging" is a technique used to implement virtual memory in // computer operating systems. In this scheme, code or data are read from // disk to memory only as needed by a process, and are read in fixed-sized // chunks called "pages". PagedArrays are somewhat the same -- though // without the automatic features found in virtual memory demand paging. // However PagedArrays do allow the user to access chunks of the disk in a // flexible way, that can match the requirements of many algorithms. // // // At the time of writing, typical scientific computers provide sufficient // memory for storing and manipulating 2-dimensional astronomical images, // which have average size of around 8 MBytes. Astronomy is increasingly // using three or higher dimensional arrays, which can be larger by one or // two orders of magnitude. PagedArrays provide a convenient way of // accessing these large arrays without requiring all the data to be read // into real or virtual memory. //

        // When you construct a PagedArray you do not read any data into // memory. Instead a disk file (ie. a Table) is created, in a place you // specify, to hold the data. This means you need to have enough disk space // to hold the array. Constructing a PagedArray is equivalent to opening a // file. //

        // Because the data is stored on disk it can be saved after the program, // function, or task that created the PagedArray has finished. This saved // array can then be read again at a later stage. //

        // So there are two reasons for using a PagedArray: //

          //
        1. To provide for arrays that are too large for the computer's memory. //
        2. To provide a way of saving arrays to disk for later access. //
        // // To access the data in a PagedArray you can either: //
          //
        1. Use a LatticeIterator //
        2. Use the getSlice and putSlice member functions //
        3. Use the parenthesis operator or getAt and putAt functions //
        // These access methods are given in order of preference. Some examples of // these access methods are in the documentation for the // Lattice class as well as below. //

        // In nearly all cases you access the PagedArray by reading a "slice" of the // PagedArray into a Casacore Array. Because the // slice is stored in memory it is important that the slice you read is not // too big compared to the physical memory on your computer. Otherwise your // computer will page excessively and performance will be poor. //

        // To overcome this you may be tempted to access the PagedArray a pixel at a // time. This will use little memory but the overhead of accessing a large // data set by separately reading each pixel from disk will also lead to poor // performance. //

        // In general the best way to access the data in PagedArrays is to use a // LatticeIterator with a cursor size that "fits" nicely into memory. Not // only do the LaticeIterator classes provide a relatively simple way to // read/write all the data but they optimally set up the cache that is // associated with each PagedArray. //

        // If the LatticeIterator classes do not access the data the way you want // you can use the getSlice and putSlice member functions. These functions // do not set up the cache for you and improved performance may be obtained // by tweaking the cache using the setCacheSizeFromPath member frunction. // //

        More Details

        // In order to utilise PagedArrays fully and understand many of the member // functions and data access methods in this class, you need to be familiar // with some of the concepts involved in the implementation of PagedArrays. //

        // Each PagedArray is stored in one cell of a Table as an indirect Array // (see the documentation for the Tables // module for more information). This means that multiple PagedArrays can be // stored in one Table. To specify which PagedArray you are referring to in // a given Table you need to specify the cell using its column name and row // number during construction. If a cell is not specified the default column // name (as given by the defaultColumnName function) and row number (as // given by the defaultRowNumber function) are used. This ability to store // multiple PagedArrays's is used in the PagedImage class where the image is // stored in one cell and a mask is optionally stored in a another column in // the same row. //

        // There are currently a number of limitations when storing multiple // PagedArrays in the same Table. //

          //
        • All the PagedArrays in the same column MUST have the same number of // dimensions. The dimension used for any particular column is set when the // first PagedArray in that column is constructed. If you want to put a // say two-dimensional PagedArray into another row of a column that // already contains a four-dimensional PagedArray you need to add two // degenerate axes. In principle you could use the resize function, but see // below for why this is not recommended. It is better to just ensure that // all the PagedArrays have the same number of dimensions. //
        • All the cells in a column that contains PagedArrays must have their // shape defined. This becomes important if you are creating a PagedArray in // say row five of a Table that currently only has one row. The PagedArray // constructor will add another four rows to the Table, and put your // PagedArray (with the shape you specify) in row five. For the three // rows for which no shape was specified, the constructor will construct // PagedArrays with only one element (and of an appropriate // dimensionality). As you cannot resize these single element PagedArrays // without difficulty (see below), it is recommended that you add // PagedArrays to rows in your Table sequentially. It is necessary to have // the constructor define the shape of all cells in the Table as it is an // error to write a Table to disk with undefined cell shapes. //
        // // Each PagedArray is stored on disk using the tiled cell storage manager // (TiledCellStMan). This stores the // data in tiles which are regular subsections of the PagedArray. For // example a PagedArray of shape [1024,1024,4,128] may have a tile shape of // [32,16,4,16]. The data in each tile is stored as a unit on the disk. This // means that there is no preferred axis when accessing multi-dimensional // data. //
        // The tile shape can be specified when constructing a new PagedArray but // not when reading an old one as it is intrinsic to the way the data is // stored on disk. It is NOT recommended that you specify the tile shape // unless you can control the lifetime of the PagedArray (this includes the // time it spends on disk), or can guarantee the access pattern. For example // if you know that a PagedArray of shape [512,512,4,32] will always be // sliced plane by plane you may prefer to specify a tile shape of // [512,64,1,1] rather than the default of [32,16,4,16]. //
        // Tiles can be cached by the tile storage manager so that it does not need // to read the data from disk every time you are accessing the a pixel in a // different tile. In order to cache the correct tiles you should tell the // storage manager what section of the PagedArray you will be // accessing. This is done using the setCacheSizeFromPath member // function. Alternatively you can set the size of the cache using the // setCacheSizeInTiles member function. //
        // By default there is no limit on how much memory the tile cache can // consume. This can be changed using the setMaximumCacheSize member // function. The tiled storage manager always tries to cache enough tiles to // ensure that each tile is read from disk only once, so setting the maximum // cache size will trade off memory usage for disk I/O. Setting the cache // size is illustrated in example 5 below. //
        // The showCacheStatistics member function is provided to allow you to // evaluate the performance of the tile cache. //
        // // All the examples in this section are available in dPagedArray.cc // //

        Example 1:

        // Create a PagedArray of Floats of shape [1024,1024,4,256] in a file // called "myData_tmp.array" and initialize it to zero. This will create a // directory on disk called "myData_tmp.array" that contains files that // exceed 1024*1024*4*256*4 (= 4 GBytes) in size. // // const IPosition arrayShape(4,1024,1024,4,256); // const String filename("myData_tmp.array"); // PagedArray diskArray(arrayShape, filename); // cout << "Created a PagedArray of shape " << diskArray.shape() // << " (" << diskArray.shape().product()/1024/1024*sizeof(Float) // << " MBytes)" << endl // << "in the table called " << diskArray.tableName() << endl; // diskArray.set(0.0f); // // Using the set function is an efficient way to initialize the PagedArray // // as it uses a PagedArrIter internally. Note that the set function is // // defined in the Lattice class that PagedArray is derived from. // // //

        Example 2:

        // Read the PagedArray produced in Example 1 and put a Gaussian profile into // each spectral channel. // // PagedArray diskArray("myData_tmp.array"); // IPosition shape = diskArray.shape(); // // Construct a Gaussian Profile to be 10 channels wide and centred on // // channel 16. Its height is 1.0. // Gaussian1D g(1.0f, 16.0f, 10.0f); // // Create a vector to cache a sampled version of this profile. // Vector profile(shape(3)); // indgen(profile); // profile.apply(g); // // Now put this profile into every spectral channel in the paged array. This // // is best done using an iterator. // LatticeIterator iter(diskArray, // TiledLineStepper(shape, diskArray.tileShape(), 3)); // for (iter.reset(); !iter.atEnd(); iter++) { // iter.woCursor() = profile; // } // // //

        Example 3:

        // Now multiply the I-polarization data by 10.0 in this PagedArray. The // I-polarization data occupies 1 GByte of RAM which is too big to read // into the memory of most computers. So an iterator is used to get suitable // sized chunks. // // Table t("myData_tmp.array", Table::Update); // PagedArray da(t); // const IPosition latticeShape = da.shape(); // const nx = latticeShape(0); // const ny = latticeShape(1); // const npol = latticeShape(2); // const nchan = latticeShape(3); // IPosition cursorShape = da.niceCursorShape(); // cursorShape(2) = 1; // LatticeStepper step(latticeShape, cursorShape); // step.subSection(IPosition(4,0), IPosition(4,nx-1,ny-1,0,nchan-1)); // LatticeIterator iter(da, step); // for (iter.reset(); !iter.atEnd(); iter++) { // iter.rwCursor() *= 10.0f; // } // // //

        Example 4:

        // Use a direct call to getSlice to access a small central region of the // V-polarization in spectral channel 0 only. The region is small enough // to not warrant constructing iterators and setting up // LatticeNavigators. In this example the call to the getSlice function // is unnecessary but is done for illustration purposes anyway. // // SetupNewTable maskSetup("mask_tmp.array", TableDesc(), Table::New); // Table maskTable(maskSetup); // PagedArray maskArray(IPosition(4,1024,1024,4,256), maskTable); // maskArray.set(False); // COWPtr> maskPtr; // maskArray.getSlice(maskPtr, IPosition(4,240,240,3,0), // IPosition(4,32,32,1,1), IPosition(4,1)); // maskPtr.rwRef() = True; // maskArray.putSlice(*maskPtr, IPosition(4,240,240,3,1)); // // //

        Example 5:

        // In this example the data in the PagedArray will be accessed a row at // a time while setting the cache size to different values. The comments // illustrate the results when running on an Ultra 1/140 with 64MBytes // of memory. // // PagedArray pa(IPosition(4,128,128,4,32)); // const IPosition latticeShape = pa.shape(); // cout << "The tile shape is:" << pa.tileShape() << endl; // // The tile shape is:[32, 16, 4, 16] // // // Setup to access the PagedArray a row at a time // const IPosition sliceShape(4,latticeShape(0), 1, 1, 1); // const IPosition stride(4,1); // Array row(sliceShape); // IPosition start(4, 0); // // // Set the cache size to enough pixels for one tile only. This uses // // 128kBytes of cache memory and takes 125 secs. // pa.setCacheSizeInTiles (1); // Timer clock; // for (start(3) = 0; start(3) < latticeShape(3); start(3)++) { // for (start(2) = 0; start(2) < latticeShape(2); start(2)++) { // for (start(1) = 0; start(1) < latticeShape(1); start(1)++) { // pa.getSlice(row, start, sliceShape, stride); // } // } // } // clock.show(); // pa.showCacheStatistics(cout); // pa.clearCache(); // // // Set the cache size to enough pixels for one row of tiles (ie. 4). // // This uses 512 kBytes of cache memory and takes 10 secs. // pa.setCacheSizeInTiles (4); // clock.mark(); // for (start(3) = 0; start(3) < latticeShape(3); start(3)++) { // for (start(2) = 0; start(2) < latticeShape(2); start(2)++) { // for (start(1) = 0; start(1) < latticeShape(1); start(1)++) { // pa.getSlice(row, start, sliceShape, stride); // } // } // } // clock.show(); // pa.showCacheStatistics(cout); // pa.clearCache(); // // // Set the cache size to enough pixels for one plane of tiles // // (ie. 4*8). This uses 4 MBytes of cache memory and takes 2 secs. // pa.setCacheSizeInTiles (4*8); // clock.mark(); // for (start(3) = 0; start(3) < latticeShape(3); start(3)++) { // for (start(2) = 0; start(2) < latticeShape(2); start(2)++) { // for (start(1) = 0; start(1) < latticeShape(1); start(1)++) { // pa.getSlice(row, start, sliceShape, stride); // } // } // } // clock.show(); // pa.showCacheStatistics(cout); // pa.clearCache(); // //
        // // Arrays of data are sometimes much too large to hold in random access memory. // PagedArrays, especially in combination with LatticeIterator, // provide convenient access to such large data sets. // // //
      • Due to storage in Tables, the templated type must be able to be // stored in a Casacore Table. This restricts the template argument to all // the common types Bool, Float, Double, Complex, String etc.) More details // can be found in the RetypedArrayEngine class. // // //
      • A better way of resizing PagedArrays // // // PagedArray - a disk based Lattice. // template class PagedArray : public Lattice { //# Make members of parent class known. public: using Lattice::ndim; public: // The default constructor creates a PagedArray that is useless for just // about everything, except that it can be assigned to with the assignment // operator. PagedArray(); // Construct a new PagedArray with the specified shape. A new Table with // the specified filename is constructed to hold the array. The Table will // remain on disk after the PagedArray goes out of scope or is deleted. PagedArray (const TiledShape& shape, const String& filename); // Construct a new PagedArray with the specified shape. A scratch Table is // created in the current working directory to hold the array. This Table // will be deleted automatically when the PagedArray goes out of scope or // is deleted. explicit PagedArray (const TiledShape& shape); // Construct a new PagedArray, with the specified shape, in the default // row and column of the supplied Table. PagedArray (const TiledShape& shape, Table& file); // Construct a new PagedArray, with the specified shape, in the specified // row and column of the supplied Table. PagedArray (const TiledShape& shape, Table& file, const String& columnName, uInt rowNum); // Reconstruct from a pre-existing PagedArray in the default row and // column of the supplied Table with the supplied filename. explicit PagedArray (const String& filename); // Reconstruct from a pre-existing PagedArray in the default row and // column of the supplied Table. explicit PagedArray (Table& file); // Reconstruct from a pre-existing PagedArray in the specified row and // column of the supplied Table. PagedArray (Table& file, const String& columnName, uInt rowNum); // The copy constructor which uses reference semantics. Copying by value // doesn't make sense, because it would require the creation of a // temporary (but possibly huge) file on disk. PagedArray (const PagedArray& other); // The destructor flushes the PagedArrays contents to disk. ~PagedArray(); // The assignment operator with reference semantics. As with the copy // constructor assigning by value does not make sense. PagedArray& operator= (const PagedArray& other); // Make a copy of the object (reference semantics). virtual Lattice* clone() const; // A PagedArray is always persistent. virtual Bool isPersistent() const; // A PagedArray is always paged to disk. virtual Bool isPaged() const; // Is the PagedArray writable? virtual Bool isWritable() const; // Returns the shape of the PagedArray. virtual IPosition shape() const; // Return the current Table name. By default this includes the full path. // The path preceeding the file name can be stripped off on request. virtual String name (Bool stripPath=False) const; // Functions to resize the PagedArray. The old contents are lost. Usage of // this function is NOT currently recommended (see the More Details section above). void resize (const TiledShape& newShape); // Returns the current table name (ie. filename) of this PagedArray. const String& tableName() const; // Return the current table object. // Table& table(); const Table& table() const; // // Returns the current Table column name of this PagedArray. const String& columnName() const; // Returns the default TableColumn name for a PagedArray. static String defaultColumn(); // Returns an accessor to the tiled storage manager. const ROTiledStManAccessor& accessor() const; // Returns the current row number of this PagedArray. uInt rowNumber() const; // Returns the default row number for a PagedArray. static uInt defaultRow(); // Returns the current tile shape for this PagedArray. IPosition tileShape() const; // Returns the maximum recommended number of pixels for a cursor. This is // the number of pixels in a tile. virtual uInt advisedMaxPixels() const; // Set the maximum allowed cache size for all Arrays in this column of the // Table. The actual value used may be smaller. A value of zero means // that there is no maximum. virtual void setMaximumCacheSize (uInt howManyPixels); // Return the maximum allowed cache size (in pixels) for all Arrays in // this column of the Table. The actual cache size may be smaller. A // value of zero means that no maximum is currently defined. virtual uInt maximumCacheSize() const; // Set the actual cache size for this Array to be big enough for the // indicated number of tiles. This cache is not shared with PagedArrays // in other rows and is always clipped to be less than the maximum value // set using the setMaximumCacheSize member function. // Tiles are cached using a first in first out algorithm. virtual void setCacheSizeInTiles (uInt howManyTiles); // Set the actual cache size for this Array to "fit" the indicated // path. This cache is not shared with PagedArrays in other rows and is // always less than the maximum value. The sliceShape is the cursor or // slice that you will be requiring (with each call to // {get,put}Slice). The windowStart and windowLength delimit the range of // pixels that will ultimatly be accessed. The AxisPath is described in // the documentation for the LatticeStepper class. virtual void setCacheSizeFromPath (const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath); // Clears and frees up the tile cache. The maximum allowed cache size is // unchanged from when setMaximumCacheSize was last called. virtual void clearCache(); // Generate a report on how the cache is doing. This is reset every // time clearCache is called. virtual void showCacheStatistics (ostream& os) const; // Return the value of the single element located at the argument // IPosition. // Note that Lattice::operator() can also be used. virtual T getAt (const IPosition& where) const; // Put the value of a single element. virtual void putAt (const T& value, const IPosition& where); // A function which checks for internal consistency. Returns False if // something nasty has happened to the PagedArray. In that case // it also throws an exception. virtual Bool ok() const; // This function is used by the LatticeIterator class to generate an // iterator of the correct type for a specified Lattice. Not recommended // for general use. virtual LatticeIterInterface* makeIter (const LatticeNavigator& navigator, Bool useRef) const; // Do the actual getting of an array of values. virtual Bool doGetSlice (Array& buffer, const Slicer& section); // Do the actual getting of an array of values. virtual void doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride); // Get the best cursor shape. virtual IPosition doNiceCursorShape (uInt maxPixels) const; // Handle the (un)locking. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; // // Resynchronize the PagedArray object with the lattice file. // This function is only useful if no read-locking is used, ie. // if the table lock option is UserNoReadLocking or AutoNoReadLocking. // In that cases the table system does not acquire a read-lock, thus // does not synchronize itself automatically. virtual void resync(); // Flush the data (but do not unlock). virtual void flush(); // Temporarily close the lattice. // It will be reopened automatically on the next access. virtual void tempClose(); // Explicitly reopen the temporarily closed lattice. virtual void reopen(); private: // Set the data in the TableInfo file void setTableType(); // make the ArrayColumn void makeArray (const TiledShape& shape); // Make a Table to hold this PagedArray void makeTable (const String& filename, Table::TableOption option); // The default comment for PagedArray Colums static String defaultComment(); // Get the writable ArrayColumn object. // It reopens the table for write if needed. ArrayColumn& getRWArray(); // Do the reopen of the table (if not open already). // void doReopen() const; void tempReopen() const; // mutable Table itsTable; String itsColumnName; uInt itsRowNumber; mutable Bool itsIsClosed; mutable Bool itsMarkDelete; String itsTableName; Bool itsWritable; TableLock itsLockOpt; mutable ArrayColumn itsArray; mutable ROTiledStManAccessor itsAccessor; }; template inline ArrayColumn& PagedArray::getRWArray() { if (itsIsClosed) { doReopen(); } if (!itsWritable) { itsTable.reopenRW(); itsWritable = True; } return itsArray; } template inline Table& PagedArray::table() { doReopen(); return itsTable; } template inline const Table& PagedArray::table() const { doReopen(); return itsTable; } template inline const String& PagedArray::columnName() const { return itsColumnName; } template inline String PagedArray::defaultColumn() { return "PagedArray"; } template inline const ROTiledStManAccessor& PagedArray::accessor() const { return itsAccessor; } template inline uInt PagedArray::rowNumber() const { return itsRowNumber; } template inline uInt PagedArray::defaultRow() { return 0; } template void PagedArray::doReopen() const { if (itsIsClosed) { tempReopen(); } } //# Declare extern templates for often used types. extern template class PagedArray; extern template class PagedArray; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/Lattices/PagedArray.tcc000066400000000000000000000371601476623553700214250ustar00rootroot00000000000000//# PagedArray.cc: this defines the PagedArray class //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or(at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_PAGEDARRAY_TCC #define LATTICES_PAGEDARRAY_TCC #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template PagedArray::PagedArray() : itsIsClosed (True), itsMarkDelete (False), itsWritable (False) { // Initializes all private data using their default consructor } template PagedArray::PagedArray (const TiledShape& shape, const String& filename) : itsColumnName (defaultColumn()), itsRowNumber (defaultRow()), itsIsClosed (True) { makeTable(filename, Table::New); makeArray (shape); setTableType(); DebugAssert (ok(), AipsError); } template PagedArray::PagedArray (const TiledShape& shape) : itsColumnName (defaultColumn()), itsRowNumber (defaultRow()), itsIsClosed (True) { Path filename=File::newUniqueName(String("./"), String("pagedArray")); makeTable (filename.absoluteName(), Table::Scratch); makeArray (shape); setTableType(); DebugAssert (ok(), AipsError); } template PagedArray::PagedArray (const TiledShape& shape, Table& file) : itsTable (file), itsColumnName (defaultColumn()), itsRowNumber (defaultRow()), itsIsClosed (False), itsMarkDelete (False), itsWritable (file.isWritable()) { makeArray (shape); setTableType(); DebugAssert (ok(), AipsError); } template PagedArray::PagedArray (const TiledShape& shape, Table& file, const String& columnName, uInt rowNumber) : itsTable (file), itsColumnName (columnName), itsRowNumber (rowNumber), itsIsClosed (False), itsMarkDelete (False), itsWritable (file.isWritable()) { makeArray (shape); setTableType(); DebugAssert (ok(), AipsError); } template PagedArray::PagedArray (const String& filename) : itsTable (filename), itsColumnName (defaultColumn()), itsRowNumber (defaultRow()), itsIsClosed (False), itsMarkDelete (False), itsWritable (False), itsArray (itsTable, itsColumnName), itsAccessor (itsTable, itsColumnName) { DebugAssert (ok(), AipsError); } template PagedArray::PagedArray (Table& file) : itsTable (file), itsColumnName (defaultColumn()), itsRowNumber (defaultRow()), itsIsClosed (False), itsMarkDelete (False), itsWritable (False), itsArray (itsTable, itsColumnName), itsAccessor (itsTable, itsColumnName) { DebugAssert (ok(), AipsError); } template PagedArray::PagedArray (Table& file, const String& columnName, uInt rowNumber) : itsTable (file), itsColumnName (columnName), itsRowNumber (rowNumber), itsIsClosed (False), itsMarkDelete (False), itsWritable (False), itsArray (itsTable, itsColumnName), itsAccessor (itsTable, itsColumnName) { DebugAssert (ok(), AipsError); } template PagedArray::PagedArray (const PagedArray& other) : Lattice(), itsTable (other.itsTable), itsColumnName (other.itsColumnName), itsRowNumber (other.itsRowNumber), itsIsClosed (other.itsIsClosed), itsMarkDelete (other.itsMarkDelete), itsTableName (other.itsTableName), itsWritable (other.itsWritable), itsLockOpt (other.itsLockOpt), itsArray (other.itsArray), itsAccessor (other.itsAccessor) { DebugAssert (ok(), AipsError); } template PagedArray::~PagedArray() { // Reopen if marked for delete to force that the table files get removed. if (itsMarkDelete) { tempReopen(); } } template PagedArray& PagedArray::operator= (const PagedArray& other) { if (this != &other) { itsTable = other.itsTable; itsColumnName = other.itsColumnName; itsRowNumber = other.itsRowNumber; itsIsClosed = other.itsIsClosed; itsMarkDelete = other.itsMarkDelete; itsTableName = other.itsTableName; itsWritable = other.itsWritable; itsLockOpt = other.itsLockOpt; itsArray.reference(other.itsArray); itsAccessor = other.itsAccessor; } DebugAssert (ok(), AipsError); return *this; } template Lattice* PagedArray::clone() const { return new PagedArray (*this); } template Bool PagedArray::isPersistent() const { return True; } template Bool PagedArray::isPaged() const { return True; } template Bool PagedArray::isWritable() const { // PagedArray is writable if underlying table is already open for write // or if the underlying table is in principle writable. if (itsIsClosed) { return (itsWritable || Table::isWritable (itsTableName)); } return (itsTable.isWritable() || Table::isWritable (itsTable.tableName())); } template const String& PagedArray::tableName() const { // Make sure the table is open, so it knows about a possible rename // (e.g. if an LCPagedMask (which uses Lattice) gets renamed). return table().tableName(); } template String PagedArray::name (Bool stripPath) const { Path path(tableName()); if (!stripPath) { return path.absoluteName(); } return path.baseName(); } template IPosition PagedArray::shape() const { DebugAssert (ok(), AipsError); doReopen(); return itsArray.shape (itsRowNumber); } template void PagedArray::resize (const TiledShape& newShape) { IPosition tileShape = newShape.tileShape(); getRWArray().setShape (itsRowNumber, newShape.shape(), tileShape); } template Bool PagedArray::doGetSlice (Array& buffer, const Slicer& section) { doReopen(); itsArray.getSlice (itsRowNumber, section, buffer, True); return False; } template void PagedArray::doPutSlice (const Array& sourceArray, const IPosition& where, const IPosition& stride) { // Create a writable column object in case not existing yet. getRWArray(); const uInt arrDim = sourceArray.ndim(); const uInt latDim = ndim(); AlwaysAssert(arrDim <= latDim, AipsError); if (arrDim == latDim) { Slicer section(where, sourceArray.shape(), stride, Slicer::endIsLength); itsArray.putSlice (itsRowNumber, section, sourceArray); } else { Array degenerateArr(sourceArray.addDegenerate(latDim-arrDim)); Slicer section(where, degenerateArr.shape(), stride, Slicer::endIsLength); itsArray.putSlice (itsRowNumber, section, degenerateArr); } } template IPosition PagedArray::tileShape() const { doReopen(); return itsAccessor.tileShape (itsRowNumber); } template uInt PagedArray::advisedMaxPixels() const { return tileShape().product(); } template IPosition PagedArray::doNiceCursorShape (uInt maxPixels) const { IPosition retval = tileShape(); if (retval.product() > Int(maxPixels)) { retval = Lattice::doNiceCursorShape(maxPixels); } return retval; } template void PagedArray::setMaximumCacheSize (uInt howManyPixels) { doReopen(); const uInt sizeInBytes = howManyPixels * sizeof(T); itsAccessor.setMaximumCacheSize (sizeInBytes); } template uInt PagedArray::maximumCacheSize() const { doReopen(); return itsAccessor.maximumCacheSize() / sizeof(T); } template void PagedArray::setCacheSizeInTiles (uInt howManyTiles) { doReopen(); itsAccessor.setCacheSize (itsRowNumber, howManyTiles); } template void PagedArray::setCacheSizeFromPath (const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath) { doReopen(); itsAccessor.setCacheSize (itsRowNumber, sliceShape, windowStart, windowLength, axisPath, True); } template void PagedArray::clearCache() { doReopen(); itsAccessor.clearCaches(); } template void PagedArray::showCacheStatistics (ostream& os) const { doReopen(); itsAccessor.showCacheStatistics (os); } template T PagedArray::getAt(const IPosition& where) const { doReopen(); // Use a temporary 1-element array with the correct dimensionality. const IPosition shape(where.nelements(),1); T value; Array buffer (shape, &value, SHARE); itsArray.getSlice (itsRowNumber, Slicer(where,shape), buffer); return value; } template void PagedArray::putAt (const T& value, const IPosition& where) { // Use a temporary 1-element array with the correct dimensionality. const IPosition shape(where.nelements(),1); Array buffer (shape, &value); getRWArray().putSlice (itsRowNumber, Slicer(where,shape), buffer); } template Bool PagedArray::ok() const { if (itsIsClosed) { if (itsTable.isNull() == False) { throw AipsError ("PagedArray::ok - " "Table associated with closed PagedArray"); return False; } } else { if (itsTable.isNull() == True) { throw AipsError ("PagedArray::ok - " "No Table associated with the PagedArray"); return False; } if (itsArray.isNull() == True) { throw AipsError ("PagedArray::ok - " "No Array associated with the PagedArray"); return False; } if (itsRowNumber > itsTable.nrow()) { throw AipsError ("PagedArray::ok - " "Row number is too big for the current Table"); return False; } } if (itsColumnName.length() == 0) { throw AipsError ("PagedArray::ok - " "Column name cannot by empty"); return False; } return True; } template LatticeIterInterface* PagedArray::makeIter (const LatticeNavigator& nav, Bool useRef) const { return new PagedArrIter(*this, nav, useRef); } template void PagedArray::makeArray (const TiledShape& shape) { doReopen(); // Make sure the table is writable. itsTable.reopenRW(); // Get the lattice shape and tile shape. IPosition latShape = shape.shape(); IPosition tileShape = shape.tileShape(); // Create a new column if it does not already exist. const uInt ndim = latShape.nelements(); Bool newColumn = False; if (!itsTable.tableDesc().isColumn(itsColumnName)) { newColumn = True; // To build the column a table description must be created TableDesc description; description.addColumn(ArrayColumnDesc(itsColumnName, defaultComment(), ndim)); description.defineHypercolumn(itsColumnName, ndim, stringToVector(itsColumnName)); TiledCellStMan stman(itsColumnName, tileShape); itsTable.addColumn(description, stman); } // Attach the default constructed ArrayColumn to the Table itsArray.attach (itsTable, itsColumnName); // if table doesn't have enough rows to match our row number // then add rows and fill them with empty arrays const IPosition emptyShape(ndim, 1); const uInt rows = itsTable.nrow(); if (rows <= itsRowNumber) { itsTable.addRow (itsRowNumber-rows+1); for (uInt r = rows; r < itsRowNumber; r++) { itsArray.setShape(r, emptyShape); } } if (newColumn) { for (uInt r = 0; r < rows; r++) { if (r != itsRowNumber) { itsArray.setShape(r, emptyShape); } } } // set a shape of the PagedArray itsArray.setShape(itsRowNumber, latShape); // create the accessor object itsAccessor = ROTiledStManAccessor (itsTable, itsColumnName); } template void PagedArray::setTableType() { AlwaysAssert (!itsTable.isNull(), AipsError); TableInfo& info(itsTable.tableInfo()); { const String reqdType = info.type (TableInfo::PAGEDARRAY); if (info.type() != reqdType) { info.setType (reqdType); } } { const String reqdSubType = info.subType (TableInfo::PAGEDARRAY); if (info.subType() != reqdSubType) { info.setSubType (reqdSubType); } } } template void PagedArray::makeTable (const String& filename, Table::TableOption option) { SetupNewTable setupTable(filename, TableDesc(), option); itsTable = Table(setupTable); itsIsClosed = False; itsMarkDelete = False; itsWritable = True; } template String PagedArray::defaultComment() { return String("version 4.0"); } template Bool PagedArray::lock (FileLocker::LockType type, uInt nattempts) { doReopen(); return itsTable.lock (type, nattempts); } template void PagedArray::unlock() { if (!itsIsClosed) { itsTable.unlock(); } } template Bool PagedArray::hasLock (FileLocker::LockType type) const { return (itsIsClosed ? False : itsTable.hasLock (type)); } template void PagedArray::resync() { if (!itsIsClosed) { itsTable.resync(); } } template void PagedArray::flush() { if (!itsIsClosed) { itsTable.flush(); } } template void PagedArray::tempClose() { if (!itsIsClosed) { itsTable.flush(); itsTableName = itsTable.tableName(); itsWritable = itsTable.isWritable(); itsLockOpt = itsTable.lockOptions(); // Take care that table does not get deleted on temporary close. if (itsTable.isMarkedForDelete()) { itsMarkDelete = True; itsTable.unmarkForDelete(); } itsTable = Table(); itsArray.reference (ArrayColumn()); itsIsClosed = True; } } template void PagedArray::reopen() { doReopen(); } template void PagedArray::tempReopen() const { if (itsIsClosed) { if (itsWritable) { itsTable = Table (itsTableName, itsLockOpt, Table::Update); } else { itsTable = Table (itsTableName, itsLockOpt); } itsArray.attach (itsTable, itsColumnName); itsAccessor = ROTiledStManAccessor (itsTable, itsColumnName); itsIsClosed = False; // Mark the table for delete if needed. if (itsMarkDelete) { itsTable.markForDelete(); itsMarkDelete = False; } } } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/Lattices/PixelCurve1D.cc000066400000000000000000000123471476623553700214750ustar00rootroot00000000000000//# PixelCurve1D.cc: Arbitrary 1-dim curve in a lattice plane //# Copyright (C) 2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have receied a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN PixelCurve1D::PixelCurve1D (double x1, double y1, double x2, double y2, uInt npoints) { Vector x(2), y(2); x(0) = x1; x(1) = x2; y(0) = y1; y(1) = y2; init (x, y, npoints); } PixelCurve1D::PixelCurve1D (const Function1D& func, float x1, float x2, uInt npoints) { // Calculate the length of the curve numerically. // Analytically it is the integral of (sqrt(1 + sqr(df/dx)). // Use 1000 times the number of pixels in x or y for the numeric calculation. uInt np = uInt(1000 * max(abs(x2-x1), abs(func(x2) - func(x1)))); Vector x(np), y(np); double step = (double(x2)-x1) / (np-1); for (uInt i=0; i& x, const Vector& y, uInt npoints) { Vector xd(x.nelements()); convertArray (xd, x); Vector yd(y.nelements()); convertArray (yd, y); init (xd, yd, npoints); } PixelCurve1D::PixelCurve1D (const Vector& x, const Vector& y, uInt npoints) { Vector xd(x.nelements()); convertArray (xd, x); Vector yd(y.nelements()); convertArray (yd, y); init (xd, yd, npoints); } PixelCurve1D::PixelCurve1D (const Vector& x, const Vector& y, uInt npoints) { init (x, y, npoints); } PixelCurve1D::PixelCurve1D (const PixelCurve1D& that) { operator= (that); } PixelCurve1D& PixelCurve1D::operator= (const PixelCurve1D& that) { if (this != &that) { itsNpoints = that.itsNpoints; itsX.resize (0); itsY.resize (0); itsX = that.itsX; itsY = that.itsY; } return *this; } PixelCurve1D::~PixelCurve1D() {} void PixelCurve1D::init (const Vector& x, const Vector& y, uInt npoints) { AlwaysAssert (x.nelements() == y.nelements(), AipsError); AlwaysAssert (x.nelements() >= 2, AipsError); uInt nr = x.nelements() - 1; // Calculate the total length of the curve. // Also calculate the scaling in x and y for each line segment. Vector leng(nr); Vector scx(nr); Vector scy(nr); double totleng = 0; for (uInt i=0; i& x, Vector& y, uInt start, uInt end, uInt incr) const { AlwaysAssert (start<=end && end #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // Arbitrary 1-dim curve in a lattice plane. // // // // // //# Classes you should understand before using this one. //
      • Function1D //
      • CurvedLattice2D // // // PixelCurve1D represents a 1-dim curve in a lattice plane to be // used by CurvedLattice2D. // The curve can be any function supported in the // Functionals module. //
        A special constructor exists to define a straight line. //
        Another special constructor exists for a polyline. // // The domain for which the curve is valid is given by the interval // [x1,x2]. The granularity of the domain is given by the number of // points. The number of points also define the length of the new axis // in the CurvedLattice2D object. //
        // // // // Use function y=cos(2*pi*x) on the interval [0,2] with 5 points. // Sinusoid1D fn; // PixelCurve1D pcurve2(fn, 0., 2., 5); // AlwaysAssertExit (pcurve2.npoints() == 5); // pcurve2.getPixelCoord (x, y, 0, 4); // cout << x << y << endl; // // The result of x is [0, 0.5, 1, 1.5, 2]. // The result of y is [1, -1, 1, -1, 1] // // // The viewer must be able to show a crosscut through an image using // an arbitrary curve. // // //
      • Maybe it is better to make itsNpoints part of CurvedLattice //
      • If itsNpoint is still part of this class, it is possible to // precompute all possible Y values in the constructors. This may // speed things up for the polyline case. // class PixelCurve1D { public: // Define a straight line from (x1,y1) to (x2,y2). // The default number of points is the length of the line. explicit PixelCurve1D (double x1=0, double y1=0, double x2=1, double y2=1, uInt npoints=0); // Define a curve with an arbitrary function from x1 to x2. // The default number of points is the length of the curve. // The length of the curve is determined numerically by integration // of sqrt(1+sqr(df/dx)). PixelCurve1D (const Function1D&, float x1, float x2, uInt npoints=0); // Define a curve from a polyline with the given points. // Both vectors have to be equally long and at least 2 long. // The argument npoints defines the number of points // (with regular steps) in which the curve is divided. // The default is the length of the polyline. PixelCurve1D (const Vector& x, const Vector& y, uInt npoints=0); PixelCurve1D (const Vector& x, const Vector& y, uInt npoints=0); PixelCurve1D (const Vector& x, const Vector& y, uInt npoints=0); PixelCurve1D (const PixelCurve1D& that); ~PixelCurve1D(); PixelCurve1D& operator= (const PixelCurve1D& that); uInt npoints() const { return itsNpoints; } // Get the pixel coordinates in the original lattice for point start // till end with given step. void getPixelCoord (Vector& x, Vector& y, uInt start, uInt end, uInt incr=1) const; private: // Initialize the object. void init (const Vector& x, const Vector& y, uInt npoints); uInt itsNpoints; Vector itsX; Vector itsY; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/Lattices/RebinLattice.h000066400000000000000000000141171476623553700214260ustar00rootroot00000000000000//# RebinLattice.h: rebin a masked lattices //# Copyright (C) 2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have receied a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_REBINLATTICE_H #define LATTICES_REBINLATTICE_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class IPosition; // // Rebin a masked lattice. // // // // // //
      • MaskedLattice // // // This class enables you to rebin (data are averaged over bin) a MaskedLattice by // a given factor per axis // // // // IPosition shape(2, 10, 20); // TiledShape tShape(shape); // TempLattice latIn(tShape); // IPosition factors(2, 2, 5); // RebinLattice rl(latIn, factors); // cerr << "Binned data = " << rl.get() << endl; // // // // template class RebinLattice : public MaskedLattice { public: // Default constructor (Object is unuseable) RebinLattice(); // Constructor. The bins don't have to fit integrally. Whatever // is left over at the end is treated as a full bin. RebinLattice(const MaskedLattice& lattice, const IPosition& bin); // Copy constructor (reference semantics) RebinLattice(const RebinLattice& other); // Destructor. virtual ~RebinLattice(); // Assignment (reference semantics) RebinLattice& operator=(const RebinLattice& other); // Make a copy of the object (reference semantics). virtual MaskedLattice* cloneML() const; // Is the lattice masked? // It is if its parent lattice is masked. virtual Bool isMasked() const; // Is the lattice paged to disk? virtual Bool isPaged() const; // The lattice is not writable. virtual Bool isWritable() const; // Handle locking of the lattice which is delegated to its parent. //
        It is strongly recommended to use class // LatticeLocker to // handle lattice locking. It also contains a more detailed // explanation of the locking process. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; // // Resynchronize the Lattice object with the lattice file. // This function is only useful if no read-locking is used, ie. // if the table lock option is UserNoReadLocking or AutoNoReadLocking. // In that cases the table system does not acquire a read-lock, thus // does not synchronize itself automatically. virtual void resync(); // Flush the data. virtual void flush(); // Close the Lattice temporarily (if it is paged to disk). // It'll be reopened automatically when needed or when // reopen is called explicitly. virtual void tempClose(); // If needed, reopen a temporarily closed Lattice. virtual void reopen(); // Get a pointer the region/mask object. // It returns 0. virtual const LatticeRegion* getRegionPtr() const; // Returns the shape of the lattice. virtual IPosition shape() const; // Return the name of the parent lattice. virtual String name (Bool stripPath=False) const; // This function returns the recommended maximum number of pixels to // include in the cursor of an iterator. virtual uInt advisedMaxPixels() const; // Check class internals - used for debugging. Should always return True virtual Bool ok() const; // Do the actual getting of an array of values. // Slicers with non-unit stride are not yet supported virtual Bool doGetSlice (Array& buffer, const Slicer& section); // Do the actual putting of an array of values. // The lattice is not writable. virtual void doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride); // Get a section of the mask. // Slicers with non-unit stride are not yet supported virtual Bool doGetMaskSlice (Array& buffer, const Slicer& section); // Static function needed by LEL. Applies binning factors // to shape to give the shape of the output lattice. Will // give the same result as function 'shape' static IPosition rebinShape (const IPosition& shapeLatticeIn, const IPosition& bin); private: Slicer findOriginalSlicer (const Slicer& section) const; void getDataAndMask (const Slicer& section); void bin(const Array& dataIn); void bin(const Array& dataIn, const Array& maskIn); // MaskedLattice* itsLatticePtr; IPosition itsBin; Bool itsAllUnity; // Cache Array itsData; Array itsMask; Slicer itsSlicer; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/Lattices/RebinLattice.tcc000066400000000000000000000246771476623553700217640ustar00rootroot00000000000000//# RebinLattice.cc: rebin a lattice //# Copyright (C) 2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_REBINLATTICE_TCC #define LATTICES_REBINLATTICE_TCC #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template RebinLattice::RebinLattice () : itsLatticePtr (0), itsAllUnity (False) {} template RebinLattice::RebinLattice (const MaskedLattice& lattice, const IPosition& bin) : itsLatticePtr(lattice.cloneML()) { LogIO os(LogOrigin("RebinLattice", "RebinLattice(...)", WHERE)); const uInt nDim = lattice.ndim(); if (bin.nelements() != nDim) { os << "Binning vector and lattice must have same dimension" << LogIO::EXCEPTION; } // itsBin.resize(bin.nelements()); const IPosition shapeIn = lattice.shape(); itsAllUnity = True; for (uInt i=0; i shapeIn[i]) { os << LogIO::WARN << "Truncating bin to lattice shape for axis " << i+1 << LogIO::POST; itsBin[i] = shapeIn[i]; } if (bin[i] != 1) itsAllUnity = False; } } template RebinLattice::RebinLattice (const RebinLattice& other) : MaskedLattice(), itsLatticePtr(0) { operator= (other); } template RebinLattice::~RebinLattice() { delete itsLatticePtr; } template RebinLattice& RebinLattice::operator=(const RebinLattice& other) { if (this != &other) { delete itsLatticePtr; itsLatticePtr = 0; if (other.itsLatticePtr) { itsLatticePtr = other.itsLatticePtr->cloneML(); } // Clear the cache. itsData.resize(); itsMask.resize(); itsSlicer = Slicer(); // itsBin = other.itsBin; itsAllUnity = other.itsAllUnity; } return *this; } template MaskedLattice* RebinLattice::cloneML() const { return new RebinLattice (*this); } template Bool RebinLattice::isMasked() const { return itsLatticePtr->isMasked(); } template Bool RebinLattice::isPaged() const { return itsLatticePtr->isPaged(); } template Bool RebinLattice::isWritable() const { return False; } template Bool RebinLattice::lock (FileLocker::LockType type, uInt nattempts) { return itsLatticePtr->lock (type, nattempts); } template void RebinLattice::unlock() { itsLatticePtr->unlock(); } template Bool RebinLattice::hasLock (FileLocker::LockType type) const { return itsLatticePtr->hasLock (type); } template void RebinLattice::resync() { itsLatticePtr->resync(); } template void RebinLattice::flush() { itsLatticePtr->flush(); } template void RebinLattice::tempClose() { itsLatticePtr->tempClose(); } template void RebinLattice::reopen() { itsLatticePtr->reopen(); } template const LatticeRegion* RebinLattice::getRegionPtr() const { return 0; } template IPosition RebinLattice::shape() const { return rebinShape(itsLatticePtr->shape(), itsBin); } template String RebinLattice::name (Bool stripPath) const { return itsLatticePtr->name(stripPath); } template Bool RebinLattice::doGetSlice (Array& buffer, const Slicer& section) { // If all unity, access the lattice directly. if (itsAllUnity) { return itsLatticePtr->doGetSlice (buffer, section); } // Get the result for this section if not in cache if (!(section == itsSlicer)) { getDataAndMask (section); } buffer.reference(itsData); return True; } template void RebinLattice::doPutSlice (const Array&, const IPosition&, const IPosition&) { throw (AipsError ("RebinLattice::putSlice - non-writable lattice")); } template uInt RebinLattice::advisedMaxPixels() const { return itsLatticePtr->advisedMaxPixels(); } template Bool RebinLattice::doGetMaskSlice (Array& buffer, const Slicer& section) { // If not masked, simply fill the buffer if (!itsLatticePtr->isMasked()) { buffer.resize (section.length()); buffer = True; return False; } // If all unity, access the lattice directly. if (itsAllUnity) { return itsLatticePtr->doGetMaskSlice (buffer, section); } // Get the result for this section if not in cache if (!(section == itsSlicer)) { getDataAndMask (section); } buffer.reference(itsMask); return True; } template Bool RebinLattice::ok() const { return itsLatticePtr->ok(); } template void RebinLattice::getDataAndMask (const Slicer& section) { // Work out the slicer for the input Lattice given the slicer for // the binned Lattice Slicer sectionIn = findOriginalSlicer (section); // Fetch Array data; Array mask; itsData.resize (section.length()); itsLatticePtr->getSlice(data, sectionIn); if (itsLatticePtr->isMasked()) { itsLatticePtr->getMaskSlice(mask, sectionIn); itsMask.resize (section.length()); bin (data, mask); } else { bin (data); } // Remember what is in cache itsSlicer = section; } template void RebinLattice::bin (const Array& dataIn) { // Make Lattice from Array to get decent iterators const uInt nDim = dataIn.ndim(); LatticeStepper stepper (dataIn.shape(), itsBin, LatticeStepper::RESIZE); ArrayLattice latIn (dataIn); RO_LatticeIterator inIter(latIn, stepper); // Do it IPosition outPos(nDim); // for (inIter.reset(); !inIter.atEnd(); inIter++) { const Array& cursor(inIter.cursor()); const uInt nSum = cursor.nelements(); T sumData = sum(cursor); if (nSum>0) sumData /= nSum; // Write output const IPosition& inPos = inIter.position(); outPos = inPos / itsBin; itsData(outPos) = sumData; } } template void RebinLattice::bin (const Array& dataIn, const Array& maskIn) { // Make Lattice from Array to get decent iterators const uInt nDim = dataIn.ndim(); ArrayLattice latIn (dataIn); Array maskInRef(maskIn); // Make Lattice iterators LatticeStepper stepper (latIn.shape(), itsBin, LatticeStepper::RESIZE); RO_LatticeIterator inIter(latIn, stepper); // Do it IPosition outPos(nDim); Array cursorMask; // for (inIter.reset(); !inIter.atEnd(); inIter++) { const Array& cursor(inIter.cursor()); Array cursorMask (maskInRef(inIter.position(), inIter.endPosition())); // Iterate through cursor with STL iterators T sumData = 0; Int nSum = 0; typename Array::const_iterator dataIterEnd = cursor.end(); typename Array::const_iterator dataIter; typename Array::const_iterator maskIter; for (dataIter=cursor.begin(),maskIter=cursorMask.begin(); dataIter!=dataIterEnd; ++dataIter,++maskIter) { if (*maskIter) { sumData += *dataIter; nSum++; } } if (nSum>0) sumData /= nSum; // Write output (perhaps could redo this with an iterator) const IPosition& inPos = inIter.position(); outPos = inPos / itsBin; itsData(outPos) = sumData; itsMask(outPos) = nSum>0; } } template IPosition RebinLattice::rebinShape (const IPosition& inShape, const IPosition& bin) { AlwaysAssert(inShape.nelements()==bin.nelements(), AipsError); // const uInt nDim = inShape.nelements(); IPosition outShape(nDim); for (uInt i=0; i 0) n += 1; // Allow last bin to be non-integral outShape[i] = n; } return outShape; } template Slicer RebinLattice::findOriginalSlicer (const Slicer& section) const // // For a slicer for the RebinLattice, find the Slicer for the original // Lattice from which we must get data to then rebin // { const uInt nDim = itsLatticePtr->ndim(); const IPosition shapeOrig = itsLatticePtr->shape(); // const IPosition& blc = section.start(); const IPosition& trc = section.end(); const IPosition& stride = section.stride(); // IPosition blcOrig(blc); IPosition trcOrig(trc); for (uInt i=0; i #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // A subset of a Lattice or MaskedLattice // // // // // //
      • Lattice //
      • LatticeRegion // // // A SubLattice is a lattice referencing a subset of another lattice // by means of a Slicer object. //
        It is useful when only a subset of a lattice needs to be accessed. //

        // When the SubLattice is created from a const Lattice object, // it is not writable, thus it can only be used as an rvalue. //

        // Using an AxesSpecifier object // it is possible to remove some or all degenerate axes (i.e. axes // with length 1) to get a lattice with a lower dimensionality. // // // // // // //

      • Any type that can be used by the Tables System can also be used by // this class. // //# //# template class SubLattice: public MaskedLattice { public: // The default constructor creates a SubLattice that is useless for just // about everything, except that it can be assigned to with the assignment // operator. SubLattice(); // Create a SubLattice from a Lattice. // This results in a SubLattice without a real mask. //
        The "const Lattice" version yields a non-writable SubLattice, // while for the non-const version one has to specify if the SubLattice // should be writable (if the original lattice is non-writable, the // SubLattice is always set to non-writable). // In the 2nd case the lattice could have been declared const, // but is not to indicate it can be changed. // SubLattice (const Lattice& lattice, AxesSpecifier=AxesSpecifier()); SubLattice (Lattice& lattice, Bool writableIfPossible, AxesSpecifier=AxesSpecifier()); // // Create a SubLattice from a MaskedLattice. //
        The "const MaskedLattice" version yields a non-writable SubLattice, // while for the non-const version one has to specify if the SubLattice // should be writable (if the original lattice is non-writable, the // SubLattice is always set to non-writable). // In the 2nd case the lattice could have been declared const, // but is not to indicate it can be changed. // SubLattice (const MaskedLattice& lattice, AxesSpecifier=AxesSpecifier()); SubLattice (MaskedLattice& lattice, Bool writableIfPossible, AxesSpecifier=AxesSpecifier()); // // Create a SubLattice from the given MaskedLattice and region. // Note that the region can be constructed from an // LCRegion object or // Slicer object (with an optional stride). //
        An exception is thrown if the lattice shape used in the region // differs from the shape of the lattice. // In the 2nd and 4th case the lattice could have been declared const, // but is not to indicate it can be changed. // SubLattice (const Lattice& lattice, const LatticeRegion& region, AxesSpecifier=AxesSpecifier()); SubLattice (Lattice& lattice, const LatticeRegion& region, Bool writableIfPossible, AxesSpecifier=AxesSpecifier()); SubLattice (const MaskedLattice& lattice, const LatticeRegion& region, AxesSpecifier=AxesSpecifier()); SubLattice (MaskedLattice& lattice, const LatticeRegion& region, Bool writableIfPossible, AxesSpecifier=AxesSpecifier()); // // Create a SubLattice from the given (Masked)Lattice and slicer. // The slicer can be strided. //
        An exception is thrown if the slicer exceeds the lattice shape. // In the 2nd and 4th case the lattice could have been declared const, // but is not to indicate it can be changed. // SubLattice (const Lattice& lattice, const Slicer& slicer, AxesSpecifier=AxesSpecifier()); SubLattice (Lattice& lattice, const Slicer& slicer, Bool writableIfPossible, AxesSpecifier=AxesSpecifier()); SubLattice (const MaskedLattice& lattice, const Slicer& slicer, AxesSpecifier=AxesSpecifier()); SubLattice (MaskedLattice& lattice, const Slicer& slicer, Bool writableIfPossible, AxesSpecifier=AxesSpecifier()); // // Copy constructor (reference semantics). SubLattice (const SubLattice& other); virtual ~SubLattice(); // Assignment (reference semantics). SubLattice& operator= (const SubLattice& other); // Make a copy of the object (reference semantics). virtual MaskedLattice* cloneML() const; // Is the lattice masked? // It is if its parent lattice or its region is masked. virtual Bool isMasked() const; // A SubLattice is persistent if no region is applied to the parent lattice. // That is true if the region has the same shape as the parent lattice // and the region has no mask. virtual Bool isPersistent() const; // Is the SubLattice paged to disk? virtual Bool isPaged() const; // Can the lattice data be referenced as an array section? virtual Bool canReferenceArray() const; // Is the SubLattice writable? virtual Bool isWritable() const; // Handle locking of the SubLattice which is delegated to its parent. //
        It is strongly recommended to use class // LatticeLocker to // handle lattice locking. It also contains a more detailed // explanation of the locking process. // virtual Bool lock (FileLocker::LockType, uInt nattempts); virtual void unlock(); virtual Bool hasLock (FileLocker::LockType) const; // // Resynchronize the Lattice object with the lattice file. // This function is only useful if no read-locking is used, ie. // if the table lock option is UserNoReadLocking or AutoNoReadLocking. // In that cases the table system does not acquire a read-lock, thus // does not synchronize itself automatically. virtual void resync(); // Flush the data. virtual void flush(); // Close the Lattice temporarily (if it is paged to disk). // It'll be reopened automatically when needed or when // reopen is called explicitly. virtual void tempClose(); // If needed, reopen a temporarily closed Lattice. virtual void reopen(); // Does the SubLattice have a pixelmask? virtual Bool hasPixelMask() const; // Get access to the pixelmask. // An exception is thrown if the SubLattice does not have a pixelmask. // virtual const Lattice& pixelMask() const; virtual Lattice& pixelMask(); // // Use the given mask as the pixelmask. // If another mask was already used, the new one will be used instead. // It checks if its shape matches the shape of the sublattice. //
        If mayExist=False, setting the pixelmask is only // possible if the underlying lattice does not have a pixelmask. //
        If mayExist=True, the resulting pixelmask is the // AND of the given pixelmask and the pixelmask of the underlying lattice. void setPixelMask (const Lattice& pixelMask, Bool mayExist); // Get a pointer the region/mask object describing this sublattice. virtual const LatticeRegion* getRegionPtr() const; // Returns the shape of the SubLattice including all degenerate axes // (i.e. axes with a length of one). virtual IPosition shape() const; // Return the name of the parent lattice. virtual String name (Bool stripPath=False) const; // This function returns the recommended maximum number of pixels to // include in the cursor of an iterator. virtual uInt advisedMaxPixels() const; // Get or put a single element in the lattice. // virtual T getAt (const IPosition& where) const; virtual void putAt (const T& value, const IPosition& where); // // Check class internals - used for debugging. Should always return True virtual Bool ok() const; // This function is used by the LatticeIterator class to generate an // iterator of the correct type for this Lattice. Not recommended // for general use. virtual LatticeIterInterface* makeIter (const LatticeNavigator& navigator, Bool useRef) const; // Do the actual getting of an array of values. virtual Bool doGetSlice (Array& buffer, const Slicer& section); // Do the actual getting of an array of values. virtual void doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride); // Get a section of the mask. virtual Bool doGetMaskSlice (Array& buffer, const Slicer& section); // Get the best cursor shape. virtual IPosition doNiceCursorShape (uInt maxPixels) const; // Set the axes mapping from the specification. const AxesMapping& getAxesMap() const { return itsAxesMap; } // Convert the specified position in the sublattice to the corresponding // position in the parent lattice. IPosition positionInParent(const IPosition& subLatticePosition) const { if (itsAxesMap.isRemoved()) { return itsRegion.convert (itsAxesMap.posToOld(subLatticePosition)); } else { return itsRegion.convert (subLatticePosition); } } // Set the region object using a slicer. // Allows the region to be changed while keeping // the same lattice, so that new SubLattice objects do not have to be // created when one only wants to change the region of interest. Should // only be called when performance is an issue; otherwise, just create // a new SubLattice object. void setRegion (const Slicer& slicer); protected: // Set the region object. // It also fills in the parent pointer when the SubLattice is taken // from a MaskedLattice. // The default region is the entire lattice. // void setRegion (const LatticeRegion& region); void setRegion(); // // Set the various pointers needed to construct the object. // One of the pointers should be zero. // It takes over the pointer and deletes the object in the destructor. void setPtr (Lattice* latticePtr, MaskedLattice* maskLatPtr, Bool writableIfPossible); // Set the axes mapping from the specification. void setAxesMap (const AxesSpecifier&); private: // Get mask data from region and mask. // Bool getRegionDataSlice (Array& buffer, const Slicer& section); Bool getMaskDataSlice (Array& buffer, const Slicer& section); // // And tmpbuf into buffer. If buffer is a reference, first a copy is made. void andMask (Array& buffer, Bool ref, const Array& tmpbuf) const; Lattice* itsLatticePtr; MaskedLattice* itsMaskLatPtr; LatticeRegion itsRegion; Bool itsWritable; Bool itsHasLattPMask; //# has underlying lattice a pixelmask? Lattice* itsPixelMask; //# AND of lattice and own pixelmask Lattice* itsOwnPixelMask; //# own pixelmask AxesSpecifier itsAxesSpec; AxesMapping itsAxesMap; }; //# Declare extern templates for often used types. extern template class SubLattice; extern template class SubLattice; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/Lattices/SubLattice.tcc000066400000000000000000000421221476623553700214370ustar00rootroot00000000000000//# SubLattice.cc: A subset of a Lattice //# Copyright (C) 1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_SUBLATTICE_TCC #define LATTICES_SUBLATTICE_TCC #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template SubLattice::SubLattice() : itsLatticePtr (0), itsMaskLatPtr (0), itsWritable (False), itsHasLattPMask (False), itsPixelMask (0), itsOwnPixelMask (0) {} template SubLattice::SubLattice (const Lattice& lattice, AxesSpecifier axesSpec) { setPtr (lattice.clone(), 0, False); setRegion(); setAxesMap (axesSpec); } template SubLattice::SubLattice (Lattice& lattice, Bool writableIfPossible, AxesSpecifier axesSpec) { setPtr (lattice.clone(), 0, writableIfPossible); setRegion(); setAxesMap (axesSpec); } template SubLattice::SubLattice (const MaskedLattice& lattice, AxesSpecifier axesSpec) { setPtr (0, lattice.cloneML(), False); setRegion(); setAxesMap (axesSpec); } template SubLattice::SubLattice (MaskedLattice& lattice, Bool writableIfPossible, AxesSpecifier axesSpec) { setPtr (0, lattice.cloneML(), writableIfPossible); setRegion(); setAxesMap (axesSpec); } template SubLattice::SubLattice (const Lattice& lattice, const LatticeRegion& region, AxesSpecifier axesSpec) { setPtr (lattice.clone(), 0, False); setRegion (region); setAxesMap (axesSpec); } template SubLattice::SubLattice (Lattice& lattice, const LatticeRegion& region, Bool writableIfPossible, AxesSpecifier axesSpec) { setPtr (lattice.clone(), 0, writableIfPossible); setRegion (region); setAxesMap (axesSpec); } template SubLattice::SubLattice (const MaskedLattice& lattice, const LatticeRegion& region, AxesSpecifier axesSpec) { setPtr (0, lattice.cloneML(), False); setRegion (region); setAxesMap (axesSpec); } template SubLattice::SubLattice (MaskedLattice& lattice, const LatticeRegion& region, Bool writableIfPossible, AxesSpecifier axesSpec) { setPtr (0, lattice.cloneML(), writableIfPossible); setRegion (region); setAxesMap (axesSpec); } template SubLattice::SubLattice (const Lattice& lattice, const Slicer& slicer, AxesSpecifier axesSpec) { setPtr (lattice.clone(), 0, False); setRegion (slicer); setAxesMap (axesSpec); } template SubLattice::SubLattice (Lattice& lattice, const Slicer& slicer, Bool writableIfPossible, AxesSpecifier axesSpec) { setPtr (lattice.clone(), 0, writableIfPossible); setRegion (slicer); setAxesMap (axesSpec); } template SubLattice::SubLattice (const MaskedLattice& lattice, const Slicer& slicer, AxesSpecifier axesSpec) { setPtr (0, lattice.cloneML(), False); setRegion (slicer); setAxesMap (axesSpec); } template SubLattice::SubLattice (MaskedLattice& lattice, const Slicer& slicer, Bool writableIfPossible, AxesSpecifier axesSpec) { setPtr (0, lattice.cloneML(), writableIfPossible); setRegion (slicer); setAxesMap (axesSpec); } template SubLattice::SubLattice (const SubLattice& other) : MaskedLattice(), itsLatticePtr (0), itsMaskLatPtr (0), itsPixelMask (0), itsOwnPixelMask (0) { operator= (other); } template SubLattice::~SubLattice() { // Note that itsMaskLatPtr (if filled in) always points to the same // object as itsLatticePtr, so it does not need to be deleted. delete itsLatticePtr; delete itsPixelMask; delete itsOwnPixelMask; } template SubLattice& SubLattice::operator= (const SubLattice& other) { if (this != &other) { itsRegion = other.itsRegion; delete itsLatticePtr; itsLatticePtr = other.itsLatticePtr; itsMaskLatPtr = other.itsMaskLatPtr; if (itsMaskLatPtr != 0) { itsMaskLatPtr = itsMaskLatPtr->cloneML(); itsLatticePtr = itsMaskLatPtr; } else if (itsLatticePtr != 0) { itsLatticePtr = itsLatticePtr->clone(); } itsWritable = other.itsWritable; delete itsPixelMask; itsPixelMask = 0; delete itsOwnPixelMask; itsOwnPixelMask = 0; if (other.itsOwnPixelMask != 0) { itsOwnPixelMask = other.itsOwnPixelMask->clone(); } itsHasLattPMask = other.itsHasLattPMask; itsAxesMap = other.itsAxesMap; } return *this; } template MaskedLattice* SubLattice::cloneML() const { return new SubLattice (*this); } template void SubLattice::setPtr (Lattice* latticePtr, MaskedLattice* maskLatPtr, Bool writableIfPossible) { itsHasLattPMask = False; itsPixelMask = 0; itsOwnPixelMask = 0; if (maskLatPtr == 0) { itsLatticePtr = latticePtr; itsMaskLatPtr = 0; } else { itsLatticePtr = maskLatPtr; if (! maskLatPtr->isMasked()) { itsMaskLatPtr = 0; } else { itsMaskLatPtr = maskLatPtr; itsHasLattPMask = itsMaskLatPtr->hasPixelMask(); } } itsWritable = False; if (writableIfPossible && itsLatticePtr->isWritable()) { itsWritable = True; } } template void SubLattice::setRegion (const LatticeRegion& region) { ThrowIf( ! (itsLatticePtr->shape().isEqual(region.region().latticeShape())), "shape of lattice " + itsLatticePtr->shape().toString() + " mismatches lattice shape in region " + region.region().latticeShape().toString() ); itsRegion = region; } template void SubLattice::setRegion (const Slicer& slicer) { setRegion (LatticeRegion (slicer, itsLatticePtr->shape())); } template void SubLattice::setRegion() { IPosition shape = itsLatticePtr->shape(); setRegion (LatticeRegion (Slicer(IPosition(shape.nelements(),0), shape), shape)); } template void SubLattice::setAxesMap (const AxesSpecifier& axesSpec) { itsAxesMap = axesSpec.apply (itsRegion.slicer().length()); if (itsAxesMap.isReordered()) { throw AipsError ("SubLattice does not support axes reordering"); } itsAxesSpec = axesSpec; } template Bool SubLattice::isMasked() const { return (itsMaskLatPtr != 0 || itsRegion.hasMask() || itsOwnPixelMask != 0); } template Bool SubLattice::isPersistent() const { return itsLatticePtr->isPersistent() && !isMasked() && !itsAxesMap.isRemoved() && shape().isEqual (itsLatticePtr->shape()); } template Bool SubLattice::isPaged() const { return itsLatticePtr->isPaged(); } template Bool SubLattice::canReferenceArray() const { return itsLatticePtr->canReferenceArray(); } template Bool SubLattice::isWritable() const { return itsWritable; } template Bool SubLattice::lock (FileLocker::LockType type, uInt nattempts) { return itsLatticePtr->lock (type, nattempts); } template void SubLattice::unlock() { itsLatticePtr->unlock(); } template Bool SubLattice::hasLock (FileLocker::LockType type) const { return itsLatticePtr->hasLock (type); } template void SubLattice::resync() { itsLatticePtr->resync(); } template void SubLattice::flush() { itsLatticePtr->flush(); } template void SubLattice::tempClose() { itsLatticePtr->tempClose(); } template void SubLattice::reopen() { itsLatticePtr->reopen(); } template Bool SubLattice::hasPixelMask() const { return itsHasLattPMask || itsOwnPixelMask != 0; } template const Lattice& SubLattice::pixelMask() const { return ((const SubLattice*)this)->pixelMask(); } template Lattice& SubLattice::pixelMask() { if (itsPixelMask == 0) { if (!hasPixelMask()) { throw (AipsError ("SubLattice::pixelMask - no pixelmask available")); } if (itsHasLattPMask) { // Construct the pixelmask (as a subset of the parent pixelmask). Lattice& fullMask = itsMaskLatPtr->pixelMask(); itsPixelMask = new SubLattice (fullMask, itsRegion, itsWritable, itsAxesSpec); // If there is an own pixelmask, and them. if (itsOwnPixelMask != 0) { Lattice* pmask = itsPixelMask; itsPixelMask = new LatticeExpr (*pmask && *itsOwnPixelMask); delete pmask; } } else { itsPixelMask = itsOwnPixelMask->clone(); } } return *itsPixelMask; } template void SubLattice::setPixelMask (const Lattice& pixelMask, Bool mayExist) { if (!mayExist && itsHasLattPMask) { throw (AipsError ("SubLattice::setPixelMask - " "underlying lattice has a pixelmask already")); } if (!(shape().isEqual(pixelMask.shape()))) { throw (AipsError ("SubLattice::setPixelMask - " "shape of pixel mask mismatches sublattice")); } delete itsPixelMask; itsPixelMask = 0; delete itsOwnPixelMask; itsOwnPixelMask = 0; itsOwnPixelMask = pixelMask.clone(); } template const LatticeRegion* SubLattice::getRegionPtr() const { return &itsRegion; } template IPosition SubLattice::shape() const { return itsAxesMap.shapeToNew (itsRegion.slicer().length()); } template String SubLattice::name (Bool stripPath) const { return itsLatticePtr->name(stripPath); } template Bool SubLattice::doGetSlice (Array& buffer, const Slicer& section) { if (! itsAxesMap.isRemoved()) { return itsLatticePtr->getSlice (buffer, itsRegion.convert (section)); } // Axes have been removed, so we have to reform the array buffer // to be able to get data from the original lattice. // Get the section shape in the original lattice. Slicer latSect = itsRegion.convert (itsAxesMap.slicerToOld (section)); Array tmp; Bool reformed = False; if (buffer.shape().isEqual (section.length())) { // Use (in principle) the same buffer storage if its shape is correct. // This is needed for LatticeIterator to work correctly, because it // expects to get the data in the buffer it provides // (unless ref=True is returned). Array tmp2 = buffer.reform (latSect.length()); tmp.reference (tmp2); reformed = True; } Bool ref = itsLatticePtr->getSlice (tmp, latSect); // Reform (i.e. remove axes) if the buffer did not have the correct shape // or if the lattice data are referenced. if (!reformed || ref) { Array tmp2 = tmp.reform (section.length()); buffer.reference (tmp2); } return ref; } template void SubLattice::doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride) { if (!itsWritable) { throw (AipsError ("SubLattice::putSlice - non-writable lattice")); } if (! itsAxesMap.isRemoved()) { itsLatticePtr->putSlice (sourceBuffer, itsRegion.convert (where), stride * itsRegion.slicer().stride()); } else { Array tmp = sourceBuffer.reform (itsAxesMap.shapeToOld (sourceBuffer.shape())); itsLatticePtr->putSlice (tmp, itsRegion.convert (itsAxesMap.posToOld (where)), itsAxesMap.shapeToOld (stride) * itsRegion.slicer().stride()); } } template uInt SubLattice::advisedMaxPixels() const { return itsLatticePtr->advisedMaxPixels(); } template IPosition SubLattice::doNiceCursorShape (uInt maxPixels) const { IPosition cursorShape (itsLatticePtr->niceCursorShape (maxPixels)); const IPosition& shape = itsRegion.slicer().length(); for (uInt i=0; i shape(i)) { cursorShape(i) = shape(i); } } return itsAxesMap.shapeToNew (cursorShape); } template T SubLattice::getAt (const IPosition& where) const { return itsLatticePtr->getAt (positionInParent(where)); /* if (! itsAxesMap.isRemoved()) { return itsLatticePtr->getAt (itsRegion.convert (where)); } return itsLatticePtr->getAt (itsRegion.convert(itsAxesMap.posToOld (where))); */ } template void SubLattice::putAt (const T& value, const IPosition& where) { ThrowIf(! itsWritable, "SubLattice::putAt - non-writable lattice"); itsLatticePtr->putAt (value, positionInParent(where)); /* if (! itsAxesMap.isRemoved()) { itsLatticePtr->putAt (value, itsRegion.convert (where)); } else { itsLatticePtr->putAt (value, itsRegion.convert (itsAxesMap.posToOld (where))); } */ } template Bool SubLattice::doGetMaskSlice (Array& buffer, const Slicer& section) { // If the lattice has no mask, we can return the region and/or pixel mask. if (itsMaskLatPtr == 0) { if (itsOwnPixelMask == 0) { // Note that if the region has no mask, it will return all True. return getRegionDataSlice (buffer, section); } if (! itsRegion.hasMask()) { return itsOwnPixelMask->getSlice (buffer, section); } // Return AND of region and pixel mask. Bool ref = getRegionDataSlice (buffer, section); andMask (buffer, ref, itsOwnPixelMask->getSlice (section)); return False; } // The lattice has a mask. // If there are no other masks, we can return the lattice's mask. if (! itsRegion.hasMask()) { if (itsOwnPixelMask == 0) { return getMaskDataSlice (buffer, section); } // Return AND of lattice and pixel mask. Bool ref = getMaskDataSlice (buffer, section); andMask (buffer, ref, itsOwnPixelMask->getSlice (section)); return False; } // Lattice and region have a mask, so they have to be ANDed. Bool ref = getMaskDataSlice (buffer, section); Array tmpbuf; getRegionDataSlice (tmpbuf, section); andMask (buffer, ref, tmpbuf); if (itsOwnPixelMask != 0) { andMask (buffer, False, itsOwnPixelMask->getSlice (section)); } return False; } template void SubLattice::andMask (Array& buffer, Bool ref, const Array& tmpbuf) const { // Make a copy if the array is referenced. if (ref) { Array mask; mask = buffer; buffer.reference (mask); } // And the masks. Bool deleteBuf, deleteTmp; const Bool* tmpptr = tmpbuf.getStorage (deleteTmp); Bool* bufptr = buffer.getStorage (deleteBuf); uInt n = buffer.nelements(); for (uInt i=0; i Bool SubLattice::getRegionDataSlice (Array& buffer, const Slicer& section) { if (! itsAxesMap.isRemoved()) { return itsRegion.getSlice (buffer, section); } Bool ref = itsRegion.getSlice (buffer, itsAxesMap.slicerToOld (section)); Array tmp = buffer.reform (section.length()); buffer.reference (tmp); return ref; } template Bool SubLattice::getMaskDataSlice (Array& buffer, const Slicer& section) { if (! itsAxesMap.isRemoved()) { return itsMaskLatPtr->doGetMaskSlice (buffer, itsRegion.convert (section)); } Bool ref = itsMaskLatPtr->doGetMaskSlice (buffer, itsRegion.convert (itsAxesMap.slicerToOld (section))); Array tmp = buffer.reform (section.length()); buffer.reference (tmp); return ref; } template Bool SubLattice::ok() const { return itsLatticePtr->ok(); } template LatticeIterInterface* SubLattice::makeIter (const LatticeNavigator& nav, Bool useRef) const { return new LatticeIterInterface (*this, nav, useRef); // Make a clone of the navigator to be able to apply our region. /// LatticeNavigator* navPtr = navigator.clone(); /// const Slicer& section = itsRegionPtr->box(); /// navPtr->subSection (section.start(), section.end(), section.stride()); /// delete navPtr; /// return iterPtr; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/Lattices/TempLattice.h000066400000000000000000000257061476623553700213020ustar00rootroot00000000000000//# TempLattice.h: A Lattice that can be used for temporary storage //# Copyright (C) 1997,1998,1999,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_TEMPLATTICE_H #define LATTICES_TEMPLATTICE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A Lattice that can be used for temporary storage // // // // // //
      • Lattice //
      • ArrayLattice //
      • PagedArray // // // A TempLattice disappears from both memory and disk when it goes out of // scope. Hence it is only useful for temporary storage of data. // // // Lattice classes are designed to allow the memory-efficient handling of large // amounts of data. But they can also used with much smaller arrays. With // large amounts of data the PagedArray // class should be used, as this will store the data on disk and efficiently // access specified portions of the data on request. With small amounts of // data the ArrayLattice class should be // used as all the data is always in memory avoiding the I/O associated with // PagedArrays. //

        // Applications often cannot predict until run time whether they will // be dealing with a large or small amount of data. So the use of a // PagedArray or an ArrayLattice cannot be made until the size of the arrays // are known. TempLattice makes this decision given the size of the Array. To // help in making a good choice the TempLattice class also examines how much // memory the operating system has (using an aipsrc variable) and compares // it with the size of the requested Array. //

        // The algorithm currently used is: create an ArrayLattice if the size of the // array is less than a quarter of the total system memory; otherwise a // PagedArray is created. The PagedArray is stored in the current // working directory and given a unique name that contains the string // "pagedArray". This pagedArray will be deleted once the TempLattice goes out // of scope. So unlike PagedArrays which can be made to exist longer than the // time they are used by a process, the PagedArrays created by the // TempLattice class are always scratch arrays. //

        // It is possible to temporarily close a TempLattice, which only takes effect // when it is created as a PagedArray. In this way it is possible to reduce // the number of open files in case a lot of TempLattice objects are used. // A temporarily closed TempLattice will be reopened automatically when needed. // It can also be reopened explicitly. //

        // You can force the TempLattice to be disk based by setting the memory // argument in the constructors to 0 //

        // TempLattice is implemented using TempLatticeImpl for reasons explained // in that class. // // // // // Create a temporary lattice and initialize to 0. // TempLattice myLat (IPosition(2,1024,1024)); // myLat.set (0.); // // Temporarily close the lattice. // myLat.tempClose(); // // Do an operation, which will automatically reopen the lattice. // myLat.set (1.); // // Note that the destructor deletes the table (if the TempLattice // // was created on disk). // // // // I needed a temporary Lattice when converting the Convolver class to using // Lattices. This was to store the Transfer function. // // //

      • Any type that can be used by the Lattices can also be used by // this class. // //# //#
      • add this feature //#
      • fix this bug //#
      • start discussion of this possible extension //# template class TempLattice : public Lattice { public: // The default constructor creates a TempLattice containing a // default ArrayLattice object. TempLattice() : itsImpl (new TempLatticeImpl()) {} // Create a TempLattice of the specified shape. You can specify how much // memory the Lattice can consume before it becomes disk based by giving a // non-negative value to the maxMemoryInMB argument. Otherwise it will assume // it can use up to 25% of the memory on your machine as defined in aipsrc // (this algorithm may change). Setting maxMemoryInMB to zero will force // the lattice to disk. // explicit TempLattice (const TiledShape& shape, Int maxMemoryInMB=-1) : itsImpl (new TempLatticeImpl(shape, maxMemoryInMB)) {} TempLattice (const TiledShape& shape, Double maxMemoryInMB) : itsImpl (new TempLatticeImpl(shape, maxMemoryInMB)) {} // // The copy constructor uses reference semantics. ie modifying data in the // copied TempLattice also modifies the data in the original TempLattice. // Passing by value doesn't make sense, because it may require the creation // of a temporary (but possibly huge) file on disk. TempLattice (const TempLattice& other) : Lattice(other), itsImpl (other.itsImpl) {} // The destructor removes the Lattice from memory and if necessary disk. virtual ~TempLattice(); // The assignment operator with reference semantics. As with the copy // constructor assigning by value does not make sense. TempLattice& operator= (const TempLattice& other) { itsImpl = other.itsImpl; } // Make a copy of the object (reference semantics). virtual Lattice* clone() const; // Is the TempLattice paged to disk? virtual Bool isPaged() const; // Can the lattice data be referenced as an array section? virtual Bool canReferenceArray() const; // Is the TempLattice writable? It should be. virtual Bool isWritable() const; // Flush the data. virtual void flush(); // Close the Lattice temporarily (if it is paged to disk). // It'll be reopened automatically when needed or when // reopen is called explicitly. virtual void tempClose(); // If needed, reopen a temporarily closed TempLattice. virtual void reopen(); // Return the shape of the Lattice including all degenerate axes. // (ie. axes with a length of one) virtual IPosition shape() const; // Set all of the elements in the Lattice to the given value. virtual void set (const T& value); // Replace every element, x, of the Lattice with the result of f(x). You // must pass in the address of the function -- so the function must be // declared and defined in the scope of your program. All versions of // apply require a function that accepts a single argument of type T (the // Lattice template type) and return a result of the same type. The first // apply expects a function with an argument passed by value; the second // expects the argument to be passed by const reference; the third // requires an instance of the class Functional. The // first form ought to run faster for the built-in types, which may be an // issue for large Lattices stored in memory, where disk access is not an // issue. // virtual void apply (T (*function)(T)); virtual void apply (T (*function)(const T&)); virtual void apply (const Functional& function); // // This function returns the recommended maximum number of pixels to // include in the cursor of an iterator. virtual uInt advisedMaxPixels() const; // Get the best cursor shape. virtual IPosition doNiceCursorShape (uInt maxPixels) const; // Maximum size - not necessarily all used. In pixels. virtual uInt maximumCacheSize() const; // Set the maximum (allowed) cache size as indicated. virtual void setMaximumCacheSize (uInt howManyPixels); // Set the cache size as to "fit" the indicated path. virtual void setCacheSizeFromPath (const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath); // Set the actual cache size for this Array to be be big enough for the // indicated number of tiles. This cache is not shared with PagedArrays // in other rows and is always clipped to be less than the maximum value // set using the setMaximumCacheSize member function. // tiles. Tiles are cached using a first in first out algorithm. virtual void setCacheSizeInTiles (uInt howManyTiles); // Clears and frees up the caches, but the maximum allowed cache size is // unchanged from when setCacheSize was called virtual void clearCache(); // Report on cache success. virtual void showCacheStatistics (ostream& os) const; // Get or put a single element in the lattice. // Note that Lattice::operator() can also be used to get a single element. // virtual T getAt (const IPosition& where) const; virtual void putAt (const T& value, const IPosition& where); // // Check class internals - used for debugging. Should always return True virtual Bool ok() const; // This function is used by the LatticeIterator class to generate an // iterator of the correct type for this Lattice. Not recommended // for general use. virtual LatticeIterInterface* makeIter (const LatticeNavigator& navigator, Bool useRef) const; // Do the actual getting of an array of values. virtual Bool doGetSlice (Array& buffer, const Slicer& section); // Do the actual getting of an array of values. virtual void doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride); private: std::shared_ptr> itsImpl; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/Lattices/TempLattice.tcc000066400000000000000000000110201476623553700216040ustar00rootroot00000000000000//# TempLattice.cc: A Lattice that can be used for temporary storage //# Copyright (C) 1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_TEMPLATTICE_TCC #define LATTICES_TEMPLATTICE_TCC #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template TempLattice::~TempLattice() {} template Lattice* TempLattice::clone() const { return new TempLattice (*this); } template void TempLattice::flush() { itsImpl->flush(); } template void TempLattice::tempClose() { itsImpl->tempClose(); } template void TempLattice::reopen() { itsImpl->doReopen(); } template Bool TempLattice::isPaged() const { return itsImpl->isPaged(); } template Bool TempLattice::canReferenceArray() const { return itsImpl->canReferenceArray(); } template Bool TempLattice::isWritable() const { return itsImpl->isWritable(); } template IPosition TempLattice::shape() const { return itsImpl->shape(); } template Bool TempLattice::doGetSlice (Array& buffer, const Slicer& section) { return itsImpl->doGetSlice (buffer, section); } template void TempLattice::doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride) { itsImpl->doPutSlice (sourceBuffer, where, stride); } template void TempLattice::set (const T& value) { itsImpl->set (value); } template void TempLattice::apply (T (*function)(T)) { itsImpl->apply (function); } template void TempLattice::apply (T (*function)(const T&)) { itsImpl->apply (function); } template void TempLattice::apply (const Functional& function) { itsImpl->apply (function); } template uInt TempLattice::advisedMaxPixels() const { return itsImpl->advisedMaxPixels(); } template IPosition TempLattice::doNiceCursorShape (uInt maxPixels) const { return itsImpl->doNiceCursorShape (maxPixels); } template uInt TempLattice::maximumCacheSize() const { return itsImpl->maximumCacheSize(); } template void TempLattice::setMaximumCacheSize (uInt howManyPixels) { itsImpl->setMaximumCacheSize (howManyPixels); } template void TempLattice::setCacheSizeFromPath (const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath) { itsImpl->setCacheSizeFromPath (sliceShape, windowStart, windowLength, axisPath); } template void TempLattice::setCacheSizeInTiles (uInt howManyTiles) { itsImpl->setCacheSizeInTiles (howManyTiles); } template void TempLattice::clearCache() { itsImpl->clearCache(); } template void TempLattice::showCacheStatistics (ostream& os) const { itsImpl->showCacheStatistics (os); } template T TempLattice::getAt (const IPosition& where) const { return itsImpl->getAt (where); } template void TempLattice::putAt (const T& value, const IPosition& where) { itsImpl->putAt (value, where); } template Bool TempLattice::ok() const { return itsImpl->ok(); } template LatticeIterInterface* TempLattice::makeIter (const LatticeNavigator& nav, Bool useRef) const { return itsImpl->makeIter (nav, useRef); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/Lattices/TempLatticeImpl.h000066400000000000000000000223041476623553700221130ustar00rootroot00000000000000//# TempLatticeImpl.h: A Lattice that can be used for temporary storage //# Copyright (C) 1997,1998,1999,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_TEMPLATTICEIMPL_H #define LATTICES_TEMPLATTICEIMPL_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class Table; // // The class implementing TempLattice // // // // // //
      • Lattice // // // The class is used as std::shared_ptr in class // TempLattice. In that way the making a copy of a TempLattice uses the // same object underneath. // This was needed to have a correct implementation of tempClose. Otherwise // when deleting a copy of a TempLattice, that destructor would delete the // underlying table and the original TempLattice could not reopen it. // template class TempLatticeImpl { public: // The default constructor creates a TempLatticeImpl containing a // default ArrayLattice object. TempLatticeImpl(); // Create a TempLatticeImpl of the specified shape. You can specify how much // memory the Lattice can consume before it becomes disk based by giving a // non-negative value to the maxMemoryInMB argument. Otherwise it will assume // it can use up to 25% of the memory on your machine as defined in aipsrc // (this algorithm may change). Setting maxMemoryInMB to zero will force // the lattice to disk. // TempLatticeImpl (const TiledShape& shape, Int maxMemoryInMB); TempLatticeImpl (const TiledShape& shape, Double maxMemoryInMB); // // The destructor removes the Lattice from memory and if necessary disk. ~TempLatticeImpl(); // Is the TempLattice paged to disk? Bool isPaged() const { return (! itsTableName.empty()); } // Can the lattice data be referenced as an array section? Bool canReferenceArray() const { return (itsTableName.empty()); } // Is the TempLattice writable? It should be. Bool isWritable() const { return True; } // Flush the data. void flush() { if (!itsTable.isNull()) itsTable.flush(); } // Close the Lattice temporarily (if it is paged to disk). // It'll be reopened automatically when needed or when // reopen is called explicitly. void tempClose(); // If needed, reopen a temporarily closed TempLatticeImpl. void reopen(); // Return the shape of the Lattice including all degenerate axes. // (ie. axes with a length of one) IPosition shape() const { doReopen(); return itsLatticePtr->shape(); } // Set all of the elements in the Lattice to the given value. void set (const T& value) { doReopen(); itsLatticePtr->set (value); } // Replace every element, x, of the Lattice with the result of f(x). You // must pass in the address of the function -- so the function must be // declared and defined in the scope of your program. All versions of // apply require a function that accepts a single argument of type T (the // Lattice template type) and return a result of the same type. The first // apply expects a function with an argument passed by value; the second // expects the argument to be passed by const reference; the third // requires an instance of the class Functional. The // first form ought to run faster for the built-in types, which may be an // issue for large Lattices stored in memory, where disk access is not an // issue. // void apply (T (*function)(T)) { doReopen(); itsLatticePtr->apply (function); } void apply (T (*function)(const T&)) { doReopen(); itsLatticePtr->apply (function); } void apply (const Functional& function) { doReopen(); itsLatticePtr->apply (function); } // // This function returns the recommended maximum number of pixels to // include in the cursor of an iterator. uInt advisedMaxPixels() const { doReopen(); return itsLatticePtr->advisedMaxPixels(); } // Get the best cursor shape. IPosition doNiceCursorShape (uInt maxPixels) { doReopen(); return itsLatticePtr->niceCursorShape (maxPixels); } // Maximum size - not necessarily all used. In pixels. uInt maximumCacheSize() const { return itsLatticePtr->maximumCacheSize(); } // Set the maximum (allowed) cache size as indicated. void setMaximumCacheSize (uInt howManyPixels) { itsLatticePtr->setMaximumCacheSize (howManyPixels); } // Set the cache size as to "fit" the indicated path. void setCacheSizeFromPath (const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath) { itsLatticePtr->setCacheSizeFromPath (sliceShape, windowStart, windowLength, axisPath); } // Set the actual cache size for this Array to be be big enough for the // indicated number of tiles. This cache is not shared with PagedArrays // in other rows and is always clipped to be less than the maximum value // set using the setMaximumCacheSize member function. // tiles. Tiles are cached using a first in first out algorithm. void setCacheSizeInTiles (uInt howManyTiles) { itsLatticePtr->setCacheSizeInTiles (howManyTiles); } // Clears and frees up the caches, but the maximum allowed cache size is // unchanged from when setCacheSize was called void clearCache() { itsLatticePtr->clearCache(); } // Report on cache success. void showCacheStatistics (ostream& os) const { itsLatticePtr->showCacheStatistics (os); } // Get or put a single element in the lattice. // Note that Lattice::operator() can also be used to get a single element. // T getAt (const IPosition& where) const { doReopen(); return itsLatticePtr->getAt (where); } void putAt (const T& value, const IPosition& where) { doReopen(); itsLatticePtr->putAt (value, where); } // // Check class internals - used for debugging. Should always return True Bool ok() const { doReopen(); return itsLatticePtr->ok(); } // This function is used by the LatticeIterator class to generate an // iterator of the correct type for this Lattice. Not recommended // for general use. LatticeIterInterface* makeIter (const LatticeNavigator& navigator, Bool useRef) const { doReopen(); return itsLatticePtr->makeIter (navigator, useRef); } // Do the actual getting of an array of values. Bool doGetSlice (Array& buffer, const Slicer& section) { doReopen(); return itsLatticePtr->doGetSlice (buffer, section); } // Do the actual getting of an array of values. void doPutSlice (const Array& sourceBuffer, const IPosition& where, const IPosition& stride) { doReopen(); itsLatticePtr->putSlice (sourceBuffer, where, stride); } // Do the reopen of the table (if not open already). void doReopen() const { if (itsIsClosed) tempReopen(); } private: // The copy constructor cannot be used. TempLatticeImpl (const TempLatticeImpl& other) ; // The assignment operator cannot be used. TempLatticeImpl& operator= (const TempLatticeImpl& other); // Initialize the object. void init (const TiledShape& shape, Double maxMemoryInMB=-1); // Do the actual reopen of the temporarily closed table (if not open already). void tempReopen() const; // Make sure that the temporary table gets deleted. void deleteTable(); mutable Table itsTable; mutable std::shared_ptr> itsLatticePtr; String itsTableName; mutable Bool itsIsClosed; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/lattices/Lattices/TempLatticeImpl.tcc000066400000000000000000000077361476623553700224510ustar00rootroot00000000000000//# TempLatticeImpl.cc: A Lattice that can be used for temporary storage //# Copyright (C) 1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_TEMPLATTICEIMPL_TCC #define LATTICES_TEMPLATTICEIMPL_TCC #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template TempLatticeImpl::TempLatticeImpl() : itsLatticePtr (std::make_shared>()), itsIsClosed (False) {} template TempLatticeImpl::TempLatticeImpl (const TiledShape& shape, Int maxMemoryInMB) : itsIsClosed (False) { init (shape, Double(maxMemoryInMB)); } template TempLatticeImpl::TempLatticeImpl (const TiledShape& shape, Double maxMemoryInMB) : itsIsClosed (False) { init(shape, maxMemoryInMB); } template TempLatticeImpl::~TempLatticeImpl() { // Reopen to make sure that temporary table gets deleted. doReopen(); } template void TempLatticeImpl::init (const TiledShape& shape, Double maxMemoryInMB) { Double memoryReq = Double(shape.shape().product()*sizeof(T))/(1024.0*1024.0); Double memoryAvail; // maxMemoryInMb = 0.0 forces disk. if (maxMemoryInMB < 0.0) { memoryAvail = Double(HostInfo::memoryFree()/1024) / 2.0; } else { memoryAvail = maxMemoryInMB; } if (memoryReq > memoryAvail) { // Create a table with a unique name in a work directory. // We can use exclusive locking, since nobody else should use the table. itsTableName = AppInfo::workFileName (Int(memoryReq), "TempLattice"); SetupNewTable newtab (itsTableName, TableDesc(), Table::Scratch); itsTable = Table(newtab, TableLock::PermanentLockingWait); itsLatticePtr = std::make_shared>(shape, itsTable); } else { itsLatticePtr = std::make_shared>(shape.shape()); } } template void TempLatticeImpl::tempClose() { if (!itsTable.isNull() && isPaged()) { // Take care that table does not get deleted, otherwise we cannot reopen. itsTable.unmarkForDelete(); itsLatticePtr.reset(); itsTable = Table(); itsIsClosed = True; } } template void TempLatticeImpl::tempReopen() const { if (itsIsClosed && isPaged()) { itsTable = Table(itsTableName, TableLock(TableLock::PermanentLockingWait), Table::Update); itsLatticePtr = std::make_shared>(itsTable); itsIsClosed = False; } if (!itsTable.isNull()) { itsTable.markForDelete(); } } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/Lattices/TileStepper.cc000066400000000000000000000341231476623553700214560ustar00rootroot00000000000000//# TileStepper.cc: defines TileStepper class //# Copyright (C) 1997,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TileStepper::TileStepper(const IPosition& latticeShape, const IPosition& tileShape) : itsBlc(latticeShape.nelements(), 0), itsTrc(latticeShape - 1), itsInc(latticeShape.nelements(), 1), itsSubSection(latticeShape), itsTiler(latticeShape), itsTilerCursorPos(latticeShape.nelements(), 0), itsTileShape(tileShape), itsAxisPath(latticeShape.nelements(), 0), itsCurBlc(latticeShape.nelements()), itsCurTrc(latticeShape.nelements()), itsNsteps(0), itsEnd(False), itsStart(True) { const uInt nrdim = latticeShape.nelements(); AlwaysAssert(nrdim > 0, AipsError); AlwaysAssert(tileShape.nelements() == nrdim, AipsError); for (uInt i=0; i 0, AipsError); AlwaysAssert(tileShape.nelements() == nrdim, AipsError); reset(); DebugAssert(ok() == True, AipsError); } // the copy constructor which uses copy semantics. TileStepper::TileStepper(const TileStepper& other) : LatticeNavigator(), itsBlc(other.itsBlc), itsTrc(other.itsTrc), itsInc(other.itsInc), itsSubSection(other.itsSubSection), itsTiler(other.itsTiler), itsTilerCursorPos(other.itsTilerCursorPos), itsTileShape(other.itsTileShape), itsAxisPath(other.itsAxisPath), itsCurBlc(other.itsCurBlc), itsCurTrc(other.itsCurTrc), itsNsteps(other.itsNsteps), itsEnd(other.itsEnd), itsStart(other.itsStart) { DebugAssert(ok() == True, AipsError); } TileStepper::~TileStepper() { // does nothing } TileStepper& TileStepper::operator=(const TileStepper& other) { if (this != &other) { itsBlc = other.itsBlc; itsTrc = other.itsTrc; itsInc = other.itsInc; itsSubSection = other.itsSubSection; itsTiler = other.itsTiler; itsTilerCursorPos = other.itsTilerCursorPos; itsTileShape = other.itsTileShape; itsAxisPath = other.itsAxisPath; itsCurBlc = other.itsCurBlc; itsCurTrc = other.itsCurTrc; itsNsteps = other.itsNsteps; itsEnd = other.itsEnd; itsStart = other.itsStart; } DebugAssert(ok() == True, AipsError); return *this; } Bool TileStepper::operator++(int) { DebugAssert(ok() == True, AipsError); if (itsEnd) { return False; } itsStart = False; itsNsteps++; IPosition currentPos = itsTilerCursorPos; //# Move to the next tile. //# Set end-status if no more tiles. Bool empty = True; while (empty) { if (! itsTiler.tiledCursorMove (True, itsTilerCursorPos, itsTileShape, itsAxisPath)) { itsEnd = True; itsTilerCursorPos = currentPos; return False; } //# Calculate the boundaries of the tile. itsCurBlc = itsTiler.absolutePosition (itsTilerCursorPos); itsCurTrc = itsCurBlc + itsTileShape - 1; // cout << itsCurBlc << itsCurTrc << " "; empty = False; //# Calculate the first and last pixel in the tile taking the //# increment into account. Int nrdim = itsCurBlc.nelements(); for (int i=0; i itsTrc(i)) { itsCurTrc(i) = itsTrc(i); } if (itsCurBlc(i) <= itsBlc(i)) { itsCurBlc(i) = itsBlc(i); }else{ itsCurBlc(i) = (itsCurBlc(i) - itsBlc(i) + itsInc(i) - 1) / itsInc(i) * itsInc(i) + itsBlc(i); } itsCurTrc(i) = (itsCurTrc(i) - itsBlc(i)) / itsInc(i) * itsInc(i) + itsBlc(i); //# It is possible that the tile does not have any pixel at all //# (e.g. when increment > tileshape). // cout << itsCurBlc << itsCurTrc << endl; if (itsCurBlc(i) > itsCurTrc(i)) { empty = True; break; } } } DebugAssert(ok() == True, AipsError); return True; } Bool TileStepper::operator--(int) { DebugAssert(ok() == True, AipsError); if (itsStart) { return False; } itsEnd = False; itsNsteps++; IPosition currentPos = itsTilerCursorPos; //# Move to the previous tile. //# Set start-status if no more tiles. Bool empty = True; while (empty) { if (! itsTiler.tiledCursorMove (False, itsTilerCursorPos, itsTileShape, itsAxisPath)) { itsStart = True; itsTilerCursorPos = currentPos; return False; } //# Calculate the boundaries of the tile. itsCurBlc = itsTiler.absolutePosition (itsTilerCursorPos); itsCurTrc = itsCurBlc + itsTileShape - 1; // cout << itsCurBlc << itsCurTrc << " "; empty = False; //# Calculate the first and last pixel in the tile taking the //# increment into account. Int nrdim = itsCurBlc.nelements(); for (int i=0; i itsTrc(i)) { itsCurTrc(i) = itsTrc(i); } if (itsCurBlc(i) <= itsBlc(i)) { itsCurBlc(i) = itsBlc(i); }else{ itsCurBlc(i) = (itsCurBlc(i) - itsBlc(i) + itsInc(i) - 1) / itsInc(i) * itsInc(i) + itsBlc(i); } itsCurTrc(i) = (itsCurTrc(i) - itsBlc(i)) / itsInc(i) * itsInc(i) + itsBlc(i); //# It is possible that the tile does not have any pixel at all //# (e.g. when increment > tileshape). // cout << itsCurBlc << itsCurTrc << endl; if (itsCurBlc(i) > itsCurTrc(i)) { empty = True; break; } } } DebugAssert(ok() == True, AipsError); return True; } void TileStepper::reset() { //# Make sure the tiler starts on a tile boundary. //# Set itsTiler subsection (its increment is always one). IPosition tilerBlc = itsBlc / itsTileShape * itsTileShape; IPosition tilerTrc = itsTrc; itsTiler.fullSize(); itsTiler.subSection (tilerBlc, tilerTrc); itsTilerCursorPos = 0; //# Calculate the boundaries of the tile. itsCurBlc = itsTiler.absolutePosition (itsTilerCursorPos); itsCurTrc = itsCurBlc + itsTileShape - 1; // cout << itsCurBlc << itsCurTrc << " "; //# Calculate the first and last pixel in the tile taking the //# increment into account. Int nrdim = itsCurBlc.nelements(); for (int i=0; i itsTrc(i)) { itsCurTrc(i) = itsTrc(i); } if (itsCurBlc(i) <= itsBlc(i)) { itsCurBlc(i) = itsBlc(i); }else{ itsCurBlc(i) = (itsCurBlc(i) - itsBlc(i) + itsInc(i) - 1) / itsInc(i) * itsInc(i) + itsBlc(i); } itsCurTrc(i) = (itsCurTrc(i) - itsBlc(i)) / itsInc(i) * itsInc(i) + itsBlc(i); // cout << itsCurBlc << itsCurTrc << endl; } itsNsteps = 0; itsEnd = False; itsStart = True; DebugAssert(ok() == True, AipsError); } Bool TileStepper::atStart() const { DebugAssert(ok() == True, AipsError); return itsStart; } Bool TileStepper::atEnd() const { DebugAssert(ok() == True, AipsError); return itsEnd; } uInt TileStepper::nsteps() const { DebugAssert(ok() == True, AipsError); return itsNsteps; } IPosition TileStepper::position() const { DebugAssert(ok() == True, AipsError) // cout << "position = " << itsTiler.absolutePosition(itsTilerCursorPos) // << endl; return itsCurBlc; } IPosition TileStepper::endPosition() const { DebugAssert(ok() == True, AipsError); return itsCurTrc; } IPosition TileStepper::latticeShape() const { DebugAssert(ok() == True, AipsError); return itsSubSection.fullShape(); } IPosition TileStepper::subLatticeShape() const { DebugAssert(ok() == True, AipsError); return itsSubSection.shape(); } IPosition TileStepper::cursorShape() const { DebugAssert(ok() == True, AipsError); return (itsCurTrc - itsCurBlc) / itsInc + 1; } IPosition TileStepper::cursorAxes() const { DebugAssert(ok() == True, AipsError); return itsAxisPath; } IPosition TileStepper::tileShape() const { DebugAssert(ok() == True, AipsError); return itsTileShape; } Bool TileStepper::hangOver() const { return False; } // Function to specify a "section" of the Lattice to Navigate over. A // section is defined in terms of the Bottom Left Corner (blc), Top Right // Corner (trc), and step size (inc), on ALL of its axes, including // degenerate axes. void TileStepper::subSection (const IPosition& blc, const IPosition& trc, const IPosition& inc) { itsSubSection.subSection (blc, trc, inc); itsBlc = itsSubSection.offset(); itsInc = itsSubSection.increment(); itsTrc = itsBlc + (itsSubSection.shape() - 1) * itsInc; reset(); } // Function to specify a "section" of the Lattice to Navigate over. The step // increment is assumed to be one. void TileStepper::subSection(const IPosition& blc, const IPosition& trc) { subSection(blc, trc, IPosition(itsTiler.ndim(), 1)); } // Return the bottom left hand corner of the current sub-Lattice. If no // sub-Lattice has been defined return blc=0 IPosition TileStepper::blc() const { DebugAssert(ok() == True, AipsError); return itsBlc; } // Return the top right hand corner of the current sub-Lattice. If no // sub-Lattice has been defined return trc=latticeShape-1 IPosition TileStepper::trc() const { DebugAssert(ok() == True, AipsError); return itsTrc; } // Return the step increment between the current sub-Lattice and the main // Lattice. If no sub-Lattice has been defined return inc=1 IPosition TileStepper::increment() const { DebugAssert(ok() == True, AipsError); return itsInc; } const IPosition& TileStepper::axisPath() const { DebugAssert(ok() == True, AipsError); return itsAxisPath; } uInt TileStepper::calcCacheSize (const IPosition&, const IPosition&, uInt, uInt) const { // Cache needs to be 1 tile only. return 1; } LatticeNavigator* TileStepper::clone() const { DebugAssert(ok() == True, AipsError); return new TileStepper(*this); } Bool TileStepper::ok() const { ostringstream str; str << "TileStepper::ok - "; const uInt latticeDim = itsTiler.ndim(); // Check the cursor shape is OK if (itsTileShape.nelements() != latticeDim) { str << "cursor shape " << itsTileShape << " has wrong number of dimensions (ie. not " << latticeDim << ')'; throw AipsError (String(str.str())); return False; } for (uInt i=0; i < latticeDim; i++) { // the cursor shape must be <= the corresponding lattice axes AND // a cursor shape with an axis of length zero makes no sense if (itsTileShape(i) > Int(itsTiler.shape(i)) || itsTileShape(i) <= 0) { str << "cursor shape " << itsTileShape << " is too big or small for lattice shape " << itsTiler.shape(); throw AipsError (String(str.str())); return False; } } // Check the cursor position is OK if (itsTilerCursorPos.nelements() != latticeDim) { str << "cursor position " << itsTilerCursorPos << " has wrong number of dimensions (ie. not " << latticeDim << ')'; throw AipsError (String(str.str())); return False; } // cursor position or its "far corner" must be inside the (sub)-Lattice if (!(itsTiler.isInside(itsTilerCursorPos) || itsTiler.isInside(itsTilerCursorPos+itsTileShape-1))) { str << "cursor beginning " << itsTilerCursorPos << " or end " << itsTilerCursorPos + itsTileShape - 1 << " is entirely outside the lattice shape " << itsTiler.shape(); throw AipsError (String(str.str())); return False; } // check the Axis Path is OK if (itsAxisPath.nelements() != latticeDim) { str << "axis path " << itsAxisPath << " has wrong number of dimensions (ie. not " << latticeDim << ')'; throw AipsError (String(str.str())); return False; } // each itsAxisPath value must be a lattice axis number, 0..n-1 for (uInt n=0; n < latticeDim; n++) { if (itsAxisPath(n) >= Int(latticeDim)) { str << "axis path " << itsAxisPath << " has elements bigger than the lattice dim -1 (ie. " << latticeDim - 1 << ')'; throw AipsError (String(str.str())); return False; } } // each itsAxisPath value must be unique for (uInt k=0; k < (latticeDim - 1); k++) { for (uInt j=k+1; j < latticeDim; j++) { if (itsAxisPath(k) == itsAxisPath(j)) { str << "axis path " << itsAxisPath << " does not have unique elements"; throw AipsError (String(str.str())); return False; } } } // Check the LatticeIndexer is OK if (itsTiler.ok() == False) { str << "LatticeIndexer thinks things are bad"; throw AipsError (String(str.str())); return False; } // Otherwise it has passed all the tests return True; } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/Lattices/TileStepper.h000066400000000000000000000270001476623553700213140ustar00rootroot00000000000000//# TileStepper.h: Steps a cursor optimally through a tiled Lattice //# Copyright (C) 1997,1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_TILESTEPPER_H #define LATTICES_TILESTEPPER_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // traverse a tiled Lattice optimally with a tile cursor // // // // // //
      • LatticeNavigator // // // TileStepper is used to step optimally through a tiled Lattice. // // // When you wish to traverse a Lattice (say, a PagedArray or an Image) you // will usually create a LatticeIterator. Once created, you may attach a // LatticeNavigator to the iterator. A TileStepper is a concrete class // derived from the abstract LatticeNavigator that allows you to step // through the Lattice in a way that will minimize the amount of cache // memory consumed and maximize the speed. //

        // Some Lattices (in particular PagedArrays) are stored (on disk) in // tiles. For an N-dimensional Lattice a tile is an N-dimensional // subsection with fewer elements along each axis. For example a Lattice of // shape [512,512,4,32] may have a tile shape of [32,16,4,16], and there // will be 16*32*1*2 (=1024) tiles in the entire Lattice. To allow efficient // access of the data in a Lattice some tiles are cached in memory. As each // tile may consume a fair bit of memory (in this example 128kBytes, // assuming each element consumes 4 bytes), it is desirable to minimise the // number of tiles held in the cache. But it is also desirable to minimise // the number of times a tiles must be read into or written from the // cache as this may require a time consuming operation like disk I/O. //

        // TileStepper steps through a lattice in a tile-by-tile way. // This means that the cache contains 1 tile only and that a tile is // accessed only once. // It should be clear that traversing a lattice in this way cannot // be used if an entire vector or plane is needed. It is, however, very // well suited for purposes like initialising a lattice, where the // order in which the lattice pixels are accessed is not important. //

        // In constructing a TileStepper, you specify the Lattice shape, the // tile shape and optionally the axis path. The axis path defines the order // in which the tiles are fetched from the lattice. Default is the natural // order (thus x-axis in the inner loop). //
        It is possible to use the function subSection to // traverse only a subsection of the lattice. //

        // The cursor position can be incremented or decremented to retrieve the next // or previous tile in the Lattice. The position of the next tile in the // Lattice will depend on the tile shape, and is described above. //
        Note that the cursor shape does not need to be constant when iterating // through the lattice. If the lattice shape is not an integer multiple of // the tile shape, the cursor will be smaller on the edges of the lattice. // // // This example initializes a lattice with the given value. // // void init (Lattice& cArray, Complex value) // { // const IPosition latticeShape = cArray.shape(); // const IPosition tileShape = cArray.niceCursorShape(); // TileStepper tsx(latticeShape, tileShape); // LatticeIterator lix(cArray, tsx); // for (lix.reset();!lix.atEnd();lix++) // lix.woCursor() = value; // } // } // // Note that a TileStepper is the default navigator for an iterator. // So the code above could be made simpler like shown below. // Also note that this example is a bit artificial, because the Lattice::set() // function should be used to initialize a lattice. // // void init (Lattice& cArray, Complex value) // { // LatticeIterator lix(cArray); // for (lix.reset();!lix.atEnd();lix++) // lix.woCursor() = value; // } // } // // // // This class makes it possible to traverse a lattice in the optimal way. // // //# //#

      • //# class TileStepper: public LatticeNavigator { public: // Construct a TileStepper by specifying the Lattice shape, a tile shape, // and an optional axis path (default is natural order). // Is is nearly always advisable to make the tileShape identical // to the Lattice tileShape. This can be obtained by // lat.niceCursorShape() where lat is // a Lattice object. // TileStepper (const IPosition& latticeShape, const IPosition& tileShape); TileStepper (const IPosition& latticeShape, const IPosition& tileShape, const IPosition& axisPath); // // Copy constructor (copy semantics). TileStepper (const TileStepper& other); ~TileStepper(); // Assignment (copy semantics). TileStepper& operator= (const TileStepper& other); // Increment operator (postfix or prefix version) - move the cursor // forward one step. Returns True if the cursor was moved. virtual Bool operator++(int); // Decrement operator (postfix or prefix version) - move the cursor // backwards one step. Returns True if the cursor was moved. virtual Bool operator--(int); // Function to move the cursor to the beginning of the Lattice. Also // resets the number of steps (nsteps function) to zero. virtual void reset(); // Function which returns "True" if the cursor is at the beginning of the // Lattice, otherwise, returns "False" virtual Bool atStart() const; // Function which returns "True" if an attempt has been made to increment // the cursor beyond the end of the Lattice. virtual Bool atEnd() const; // Function to return the number of steps (increments & decrements) taken // since construction (or since last reset). This is a running count of // all cursor movement (operator++ or operator--), even though // N-increments followed by N-decrements will always leave the cursor in // the original position. virtual uInt nsteps() const; // Function which returns the current position of the beginning of the // cursor. The position function is relative to the origin // in the main Lattice. virtual IPosition position() const; // Function which returns the current position of the end of the // cursor. The endPosition function is relative the origin // in the main Lattice. virtual IPosition endPosition() const; // Functions which return the shape of the Lattice being iterated // through. latticeShape always returns the shape of the main // Lattice while subLatticeShape returns the shape of any // sub-Lattice defined using the subSection function. // virtual IPosition latticeShape() const; virtual IPosition subLatticeShape() const; // // Function which returns the shape of the cursor. This always includes // all axes (i.e. it includes degenerates axes) virtual IPosition cursorShape() const; // Function which returns the axes of the cursor. virtual IPosition cursorAxes() const; // Function which returns the shape of the "tile" the cursor will iterate // through before moving onto the next tile. IPosition tileShape() const; // Function which returns "True" if the increment/decrement operators have // moved the cursor position such that part of the cursor beginning or end // is hanging over the edge of the Lattice. This always returns False. virtual Bool hangOver() const; // Functions to specify a "section" of the Lattice to step over. A section // is defined in terms of the Bottom Left Corner (blc), Top Right Corner // (trc), and step size (inc), on ALL of its axes, including degenerate // axes. The step size defaults to one if not specified. // virtual void subSection (const IPosition& blc, const IPosition& trc); virtual void subSection (const IPosition& blc, const IPosition& trc, const IPosition& inc); // // Return the bottom left hand corner (blc), top right corner (trc) or // step size (increment) used by the current sub-Lattice. If no // sub-Lattice has been defined (with the subSection function) // these functions return blc=0, trc=latticeShape-1, increment=1, ie. the // entire Lattice. // virtual IPosition blc() const; virtual IPosition trc() const; virtual IPosition increment() const; // // Return the axis path. virtual const IPosition& axisPath() const; // Function which returns a pointer to dynamic memory of an exact copy // of this instance. The pointer returned by this function must // be deleted externally. virtual LatticeNavigator* clone() const; // Function which checks the internal data of this class for correct // dimensionality and consistant values. // Returns True if everything is fine otherwise returns False virtual Bool ok() const; // Calculate the cache size (in tiles) for this type of access to a lattice // in the given row of the tiled hypercube. virtual uInt calcCacheSize (const IPosition& cubeShape, const IPosition& tileShape, uInt maxCacheSize, uInt bucketSize) const; private: // Prevent the default constructor from being used. TileStepper(); IPosition itsBlc; //# Bottom Left Corner IPosition itsTrc; //# Top Right Corner IPosition itsInc; //# Increment LatticeIndexer itsSubSection; //# The current subsection LatticeIndexer itsTiler; //# For moving between tiles IPosition itsTilerCursorPos; //# The current position of the iterator IPosition itsTileShape; //# The tile shape (= itsTiler cursor shape) IPosition itsAxisPath; //# Path for traversing IPosition itsCurBlc; //# Blc of the current position. IPosition itsCurTrc; //# Trc of the current position. uInt itsNsteps; //# The number of iterator steps taken so far Bool itsEnd; //# Is the cursor beyond the end? Bool itsStart; //# Is the cursor at the beginning? }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/Lattices/TiledLineStepper.cc000066400000000000000000000426741476623553700224440ustar00rootroot00000000000000//# TiledLineStepper.cc: defines TiledLineStepper class //# Copyright (C) 1994,1995,1996,1997,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TiledLineStepper::TiledLineStepper (const IPosition& latticeShape, const IPosition& tileShape, const uInt axis) : itsBlc(latticeShape.nelements(), 0), itsTrc(latticeShape - 1), itsInc(latticeShape.nelements(), 1), itsSubSection(latticeShape), itsIndexer(latticeShape), itsTiler(latticeShape), itsIndexerCursorPos(latticeShape.nelements(), 0), itsTilerCursorPos(latticeShape.nelements(), 0), itsCursorShape(latticeShape.nelements(), 1), itsTileShape(tileShape), itsAxisPath(latticeShape.nelements(), 0), itsNsteps(0), itsAxis(axis), itsEnd(False), itsStart(True) { const uInt nrdim = latticeShape.nelements(); AlwaysAssert(nrdim > 0, AipsError); AlwaysAssert(tileShape.nelements() == nrdim, AipsError); AlwaysAssert(axis < nrdim, AipsError); uInt i; for (i=0; i itsTrc(i)) { tiletrc(i) = itsTrc(i); } if (tileblc(i) <= itsBlc(i)) { tileblc(i) = itsBlc(i); }else{ tileblc(i) = (tileblc(i) - itsBlc(i) + itsInc(i) - 1) / itsInc(i) * itsInc(i) + itsBlc(i); } tiletrc(i) = (tiletrc(i) - itsBlc(i)) / itsInc(i) * itsInc(i) + itsBlc(i); //# It is possible that the tile does not have any pixel at all //# (e.g. when increment > tileshape). if (tileblc(i) > tiletrc(i)) { empty = True; break; } } } //# When pixels in this tile, set to the first pixel. if (!empty) { itsIndexer.fullSize(); itsIndexer.subSection (tileblc, tiletrc, itsInc); itsIndexerCursorPos = 0; return True; } } DebugAssert(ok() == True, AipsError); return False; } Bool TiledLineStepper::operator--(int) { DebugAssert(ok() == True, AipsError); if (itsStart) { return False; } itsEnd = False; itsNsteps++; IPosition currentPos = itsIndexerCursorPos; //# Move to the previous position in the tile. //# If at the beginning of the tile, move to the previous tile. if (itsIndexer.tiledCursorMove (False, itsIndexerCursorPos, itsCursorShape, itsAxisPath)) { return True; } //# Move to the previous tile. //# Set start-status if no more tiles. IPosition tilerPos = itsTilerCursorPos; while (!itsStart) { if (! itsTiler.tiledCursorMove (False, itsTilerCursorPos, itsTileShape, itsAxisPath)) { itsStart = True; itsIndexerCursorPos = currentPos; itsTilerCursorPos = tilerPos; return False; } //# Calculate the boundaries of the tile. IPosition tileblc = itsTiler.absolutePosition (itsTilerCursorPos); IPosition tiletrc = tileblc + itsTileShape - 1; tileblc(itsAxis) = itsBlc(itsAxis); tiletrc(itsAxis) = itsTrc(itsAxis); Bool empty = False; //# Calculate the first and last pixel in the tile taking the //# increment into account. uInt nrdim = tileblc.nelements(); for (uInt i=0; i itsTrc(i)) { tiletrc(i) = itsTrc(i); } if (tileblc(i) <= itsBlc(i)) { tileblc(i) = itsBlc(i); }else{ tileblc(i) = (tileblc(i) - itsBlc(i) + itsInc(i) - 1) / itsInc(i) * itsInc(i) + itsBlc(i); } tiletrc(i) = (tiletrc(i) - itsBlc(i)) / itsInc(i) * itsInc(i) + itsBlc(i); //# It is possible that the tile does not have any pixel at all //# (e.g. when increment > tileshape). if (tileblc(i) > tiletrc(i)) { empty = True; break; } } } //# When pixels in this tile, set to the first pixel. if (!empty) { itsIndexer.fullSize(); itsIndexer.subSection (tileblc, tiletrc, itsInc); itsIndexerCursorPos = (tiletrc - tileblc) / itsInc; itsIndexerCursorPos(itsAxis) = 0; return True; } } DebugAssert(ok() == True, AipsError); return False; } void TiledLineStepper::reset() { //# Make sure the tiler starts on a tile boundary. //# Set itsTiler subsection (its increment is always one). //# For itsTiler we are not interested in the length of itsAxis axis, //# so make it the tile shape for convenience (but not exceeding lattice). IPosition tilerBlc = itsBlc / itsTileShape * itsTileShape; IPosition tilerTrc = itsTrc; tilerTrc(itsAxis) = std::min(latticeShape()(itsAxis) - 1, tilerBlc(itsAxis) + itsTileShape(itsAxis) - 1); itsTiler.fullSize(); itsTiler.subSection (tilerBlc, tilerTrc); itsTilerCursorPos = 0; itsCursorShape(itsAxis) = 1 + (itsTrc(itsAxis) - itsBlc(itsAxis)) / itsInc(itsAxis); //# Calculate the boundaries of the tile. IPosition tileblc = itsTiler.absolutePosition (itsTilerCursorPos); IPosition tiletrc = tileblc + itsTileShape - 1; tileblc(itsAxis) = itsBlc(itsAxis); tiletrc(itsAxis) = itsTrc(itsAxis); //# Calculate the first and last pixel in the tile taking the //# increment into account. uInt nrdim = tileblc.nelements(); for (uInt i=0; i itsTrc(i)) { tiletrc(i) = itsTrc(i); } if (tileblc(i) <= itsBlc(i)) { tileblc(i) = itsBlc(i); }else{ tileblc(i) = (tileblc(i) - itsBlc(i) + itsInc(i) - 1) / itsInc(i) * itsInc(i) + itsBlc(i); } tiletrc(i) = (tiletrc(i) - itsBlc(i)) / itsInc(i) * itsInc(i) + itsBlc(i); } } itsIndexer.fullSize(); itsIndexer.subSection (tileblc, tiletrc, itsInc); itsIndexerCursorPos = 0; itsNsteps = 0; itsEnd = False; itsStart = True; DebugAssert(ok() == True, AipsError); } Bool TiledLineStepper::atStart() const { DebugAssert(ok() == True, AipsError); return itsStart; } Bool TiledLineStepper::atEnd() const { DebugAssert(ok() == True, AipsError); return itsEnd; } uInt TiledLineStepper::nsteps() const { DebugAssert(ok() == True, AipsError); return itsNsteps; } IPosition TiledLineStepper::position() const { DebugAssert(ok() == True, AipsError); return itsIndexer.absolutePosition (itsIndexerCursorPos); } IPosition TiledLineStepper::endPosition() const { DebugAssert(ok() == True, AipsError); IPosition last = itsIndexerCursorPos; last(itsAxis) += (itsCursorShape(itsAxis) - 1) * itsInc(itsAxis); return itsIndexer.absolutePosition (last); } IPosition TiledLineStepper::latticeShape() const { DebugAssert(ok() == True, AipsError); return itsSubSection.fullShape(); } IPosition TiledLineStepper::subLatticeShape() const { DebugAssert(ok() == True, AipsError); return itsSubSection.shape(); } IPosition TiledLineStepper::cursorShape() const { DebugAssert(ok() == True, AipsError); return itsCursorShape; } IPosition TiledLineStepper::cursorAxes() const { DebugAssert(ok() == True, AipsError); return IPosition(1, itsAxis); } IPosition TiledLineStepper::tileShape() const { DebugAssert(ok() == True, AipsError); return itsTileShape; } Bool TiledLineStepper::hangOver() const { return False; } // Function to specify a "section" of the Lattice to Navigate over. A // section is defined in terms of the Bottom Left Corner (blc), Top Right // Corner (trc), and step size (inc), on ALL of its axes, including // degenerate axes. void TiledLineStepper::subSection (const IPosition& blc, const IPosition& trc, const IPosition& inc) { itsSubSection.subSection (blc, trc, inc); itsBlc = itsSubSection.offset(); itsInc = itsSubSection.increment(); itsTrc = itsBlc + (itsSubSection.shape() - 1) * itsInc; reset(); } // Function to specify a "section" of the Lattice to Navigate over. The step // increment is assumed to be one. void TiledLineStepper::subSection (const IPosition& blc, const IPosition& trc) { subSection(blc, trc, IPosition(itsIndexer.ndim(), 1)); } // Return the bottom left hand corner of the current sub-Lattice. If no // sub-Lattice has been defined return blc=0 IPosition TiledLineStepper::blc() const { DebugAssert(ok() == True, AipsError); return itsBlc; } // Return the top right hand corner of the current sub-Lattice. If no // sub-Lattice has been defined return trc=latticeShape-1 IPosition TiledLineStepper::trc() const { DebugAssert(ok() == True, AipsError); return itsTrc; } // Return the step increment between the current sub-Lattice and the main // Lattice. If no sub-Lattice has been defined return inc=1 IPosition TiledLineStepper::increment() const { DebugAssert(ok() == True, AipsError); return itsInc; } const IPosition& TiledLineStepper::axisPath() const { DebugAssert(ok() == True, AipsError); return itsAxisPath; } uInt TiledLineStepper::calcCacheSize (const IPosition&, const IPosition& tileShape, uInt, uInt bucketSize) const { if (bucketSize == 0) { return 0; } // Tile by tile is accessed, but the main axis needs the entire window. // So calculate the start and end tile for the window. Int tilesz = tileShape(itsAxis); Int stTile = itsBlc(itsAxis) / tilesz; Int endTile = itsTrc(itsAxis) / tilesz; return (endTile - stTile + 1); } LatticeNavigator* TiledLineStepper::clone() const { DebugAssert(ok() == True, AipsError); return new TiledLineStepper(*this); } Bool TiledLineStepper::ok() const { ostringstream str; str << "TiledLineStepper::ok - "; const uInt tilerDim = itsTiler.ndim(); for (uInt i=0; i < tilerDim; i++) { // the cursor shape must be <= the corresponding lattice axes AND // a cursor shape with an axis of length zero makes no sense if (itsTileShape(i) > Int(itsTiler.shape(i)) || itsTileShape(i) <= 0) { str << "tiler cursor shape " << itsTileShape << " is too big or small for lattice shape " << itsTiler.shape(); throw AipsError (String(str.str())); return False; } } // Check the cursor position is OK if (itsTilerCursorPos.nelements() != tilerDim) { str << "tiler cursor position " << itsTilerCursorPos << " has wrong number of dimensions (ie. not " << tilerDim << ')' ; throw AipsError (String(str.str())); return False; } // cursor position or its "far corner" must be inside the (sub)-Lattice if (!(itsTiler.isInside(itsTilerCursorPos) || itsTiler.isInside(itsTilerCursorPos+itsTileShape-1))) { str << "tiler cursor beginning " << itsTilerCursorPos << " or end " << itsTilerCursorPos + itsTileShape - 1 << " is entirely outside the lattice shape " << itsTiler.shape(); throw AipsError (String(str.str())); return False; } const uInt latticeDim = itsIndexer.ndim(); // Check the cursor shape is OK if (itsCursorShape.nelements() != latticeDim) { str << "cursor shape " << itsCursorShape << " has wrong number of dimensions (ie. not " << latticeDim << ')'; throw AipsError (String(str.str())); return False; } for (uInt i=0; i < latticeDim; i++) { // the cursor shape must be <= the corresponding lattice axes AND // a cursor shape with an axis of length zero makes no sense if (itsCursorShape(i) > Int(itsIndexer.shape(i)) || itsCursorShape(i) <= 0) { str << "cursor shape " << itsCursorShape << " is too big or small for lattice shape " << itsIndexer.shape(); throw AipsError (String(str.str())); return False; } } // Check the cursor position is OK if (itsIndexerCursorPos.nelements() != latticeDim) { str << "cursor position " << itsIndexerCursorPos << " has wrong number of dimensions (ie. not " << latticeDim << ')'; throw AipsError (String(str.str())); return False; } // cursor position or its "far corner" must be inside the (sub)-Lattice if (!(itsIndexer.isInside(itsIndexerCursorPos) || itsIndexer.isInside(itsIndexerCursorPos+itsCursorShape-1))) { str << "cursor beginning " << itsIndexerCursorPos << " or end " << itsIndexerCursorPos + itsCursorShape - 1 << " is entirely outside the lattice shape " << itsIndexer.shape(); throw AipsError (String(str.str())); return False; } // check the Axis Path is OK if (itsAxisPath.nelements() != latticeDim) { str << "axis path " << itsAxisPath << " has wrong number of dimensions (ie. not " << latticeDim << ')'; throw AipsError (String(str.str())); return False; } // each itsAxisPath value must be a lattice axis number, 0..n-1 for (uInt n=0; n < latticeDim; n++) { if (itsAxisPath(n) >= Int(latticeDim)) { str << "axis path " << itsAxisPath << " has elements bigger than the lattice dim -1 (ie. " << latticeDim - 1 << ')'; throw AipsError (String(str.str())); return False; } } // each itsAxisPath value must be unique for (uInt k=0; k < (latticeDim - 1); k++) { for (uInt j=k+1; j < latticeDim; j++) { if (itsAxisPath(k) == itsAxisPath(j)) { str << "axis path " << itsAxisPath << " does not have unique elements"; throw AipsError (String(str.str())); return False; } } } // Check the LatticeIndexers are OK if (itsIndexer.ok() == False) { str << "LatticeIndexer thinks things are bad"; throw AipsError (String(str.str())); return False; } if (itsTiler.ok() == False) { str<< "itsTiler thinks things are bad"; throw AipsError (String(str.str())); return False; } // Check the LatticeIndexer is OK if (itsIndexer.ok() == False) { str << "itsIndexer thinks things are bad"; throw AipsError (String(str.str())); return False; } // Otherwise it has passed all the tests return True; } } //# NAMESPACE CASACORE - END casacore-3.7.1/lattices/Lattices/TiledLineStepper.h000066400000000000000000000364011476623553700222750ustar00rootroot00000000000000//# TiledLineStepper.h: Step a Vector cursor optimally through a tiled Lattice //# Copyright (C) 1997,1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef LATTICES_TILEDLINESTEPPER_H #define LATTICES_TILEDLINESTEPPER_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Step a Vector cursor optimally through a tiled Lattice. // // // // // //
      • LatticeNavigator // // // TiledLineStepper is used to step a Vector cursor optimally through // a Lattice that is tiled. // // // When you wish to traverse a Lattice (say, a PagedArray or an Image) you // will usually create a LatticeIterator. Once created, you may attach a // LatticeNavigator to the iterator. A TiledLineStepper, is a concrete class // derived from the abstract LatticeNavigator that allows you to move // a Vector cursor through the Lattice in a way that will minimize the // amount of cache memory consumed. //

        // Some Lattices (in particular PagedArrays) are stored (on disk) in // tiles. For an N-dimensional Lattice a tile is an N-dimensional // subsection with fewer elements along each axis. For example a Lattice of // shape [512,512,4,32] may have a tile shape of [32,16,4,16], and there // will be 16*32*1*2 (=1024) tiles in the entire Lattice. To allow efficient // access of the data in a Lattice some tiles are cached in memory. As each // tile may consume a fair bit of memory (in this example 128kBytes, // assuming each element consumes 4 bytes), it is desirable to minimise the // number of tiles held in the cache. But it is also desirable to minimise // the number of times a tiles must be read into or written from the // cache as this may require a time consuming operation like disk I/O. //

        // Now suppose you wanted to traverse a Lattice with a Vector cursor of // length 512 pixel aligned along the x-axis. Using a // LatticeStepper, each Vector is // retrieved from the Lattice sequentially and without any consideration of // the underlying tile shape. What is the optimal cache size for the above // example? //

        // Suppose we have a cache size of 16 ie., the number of tiles along the // x-axis. Then Vectors beginning at positions [0,0,0,0] to [0,15,0,0] will // be stored in the cache. But the next Vector beginning at position // [0,16,0,0] will flush the cache and read in another 16 tiles. This I/O // takes time and will occur 16 times for each plane in the four dimensional // Lattice. Further when the cursor moves to position [0,0,1,0] the 16 tiles // that where initially in the cache will need to be read again. To avoid // all this cache I/O it is better to have a bigger cache. //

        // Suppose the cache size is 16*32 (=512) ie., enough tiles to contain an // (x,y)-plane. Then the cache size will not be flushed until the cursor is // moved to position [0,0,0,16]. Further the cache will never need to read // back into memory tiles that had previously been stored in there. The // cache is big enough to store tiles until they have been completely // used. But this cache is 64MBytes in size, and consumes too much memory // for many computers. //

        // This where a TiledLineStepper is useful. Because it knows the shape of the // tiles in the underlying Lattice it moves the cursor to return all the // Vectors in the smallest possible cache of tiles before moving on to the // next set of tiles. Using the above example again, the TiledLineStepper will // move the beginning of the Vector cursor in the following pattern. // // [0,0,0,0], [0,1,0,0], [0,2,0,0], ... [0,15,0,0] // [0,0,1,0], [0,1,1,0], ... [0,15,1,0], // ... [0,15,3,0], // [0,0,0,1], ... [0,15,3,15] // // Moving the Vector cursor through all 16*4*16 (=1024 positions) can be // done by caching only 16 tiles in memory (those along the x-axis). Hence // the cache size need only be 2MBytes in size. Further once all 1024 // vectors have been returned it is not necessary to read these 16 tiles // back into memory. All the data in those tiles has already been // accessed. Using a TiledLineStepper rather than a LatticeStepper has, // in this example, resulted in a drop in the required cache size from // 64MBytes down to 2MBytes. //

        // In constructing a TiledLineStepper, you specify the Lattice shape, the // tile shape and the axis the Vector cursor will be aligned with. Specifying // an axis=0 will align the cursor with the x-axis and axis=2 will produce a // cursor that is along the z-axis. The length of the cursor is always the // same as the number of elements in the Lattice along the axis the cursor // is aligned with. //
        It is possible to use the function subSection to // traverse only a subsection of the lattice. //

        // The cursor position can be incremented or decremented to retrieve the next // or previous Vector in the Lattice. The position of the next Vector in the // Lattice will depend on the tile shape, and is described above. Within a tile // the Vector cursor will move first through the x-axis and then the y-axis // (assuming we have a cursor oriented along the z-axis). In general the lower // dimensions will be exhausted (within a tile) before moving the cursor // through higher dimensions. This intra-tile behaviour for cursor movement // extends to the inter-tile movement of the cursor between tiles. // // // This example is of a global function that will do a 2-D inplace // complex Fourier transform of an arbitrary large Lattice (which // must have at least two dimensions). // // A two dimensional transform is done by successive one dimensional // transforms along all the rows and then all the columns in the // lattice. Scoping is used to destroy iterators once they have been // used. This frees up the cache memory associated with the cursor in each // iterator. // // // void FFT2DComplex (Lattice& cArray, // const Bool direction) // { // const uInt ndim = cArray.ndim(); // AlwaysAssert(ndim > 1, AipsError); // const IPosition latticeShape = cArray.shape(); // const uInt nx=latticeShape(0); // const uInt ny=latticeShape(1); // const IPosition tileShape = cArray.niceCursorShape(); // // { // TiledLineStepper tsx(latticeShape, tileShape, 0); // LatticeIterator lix(cArray, tsx); // FFTServer fftx(IPosition(1, nx)); // for (lix.reset();!lix.atEnd();lix++) { // fftx.fft(lix.rwVectorCursor(), direction); // } // } // { // TiledLineStepper tsy(latticeShape, tileShape, 1); // LatticeIterator liy(cArray, tsy); // FFTServer ffty(IPosition(1, ny)); // for (liy.reset();!liy.atEnd();liy++) { // ffty.fft(liy.rwVectorCursor(), direction); // } // } // } // // // // Moving through a Lattice by equal sized chunks, and without regard // to the nature of the data, is a basic and common procedure. // // //

      • Support for Matrix and higher dimensional cursors can be used. // class TiledLineStepper : public LatticeNavigator { public: // Construct a TiledLineStepper by specifying the Lattice shape, // a tile shape and the axis along which the Vector cursor will lie // (0 means the x-axis). Is is nearly always advisable to make the // tileShape identical to the Lattice tileShape. This can be obtained by // lat.niceCursorShape(lat.advisedMaxPixels()) // where lat is a Lattice object. TiledLineStepper (const IPosition& latticeShape, const IPosition& tileShape, const uInt axis); // The copy constructor uses copy semantics. TiledLineStepper (const TiledLineStepper& other); ~TiledLineStepper(); // The assignment operator uses copy semantics. TiledLineStepper& operator= (const TiledLineStepper& other); // Increment operator (postfix or prefix version) - move the cursor // forward one step. Returns True if the cursor was moved. virtual Bool operator++(int); // Decrement operator (postfix or prefix version) - move the cursor // backwards one step. Returns True if the cursor was moved. virtual Bool operator--(int); // Function to move the cursor to the beginning of the Lattice. Also // resets the number of steps (nsteps function) to zero. virtual void reset(); // Function which returns "True" if the cursor is at the beginning of the // Lattice, otherwise, returns "False" virtual Bool atStart() const; // Function which returns "True" if an attempt has been made to increment // the cursor beyond the end of the Lattice. virtual Bool atEnd() const; // Function to return the number of steps (increments & decrements) taken // since construction (or since last reset). This is a running count of // all cursor movement (operator++ or operator--), even though // N-increments followed by N-decrements will always leave the cursor in // the original position. virtual uInt nsteps() const; // Function which returns the current position of the beginning of the // cursor. The position function is relative to the origin // in the main Lattice. // virtual IPosition position() const; // // Function which returns the current position of the end of the // cursor. The endPosition function is relative to the origin // in the main Lattice. // virtual IPosition endPosition() const; // // Functions which returns the shape of the Lattice being iterated // through. latticeShape always returns the shape of the main // Lattice while subLatticeShape returns the shape of any // sub-Lattice defined using the subSection function. // virtual IPosition latticeShape() const; virtual IPosition subLatticeShape() const; // // Function which returns the shape of the cursor. This always includes // all axes (ie. it includes degenerates axes) virtual IPosition cursorShape() const; // Function which returns the axes of the cursor. virtual IPosition cursorAxes() const; // Function which returns the shape of the "tile" the cursor will iterate // through before moving onto the next tile. THIS IS NOT THE SAME AS THE // TILE SHAPE USED BY THE LATTICE. It is nearly the same except that the // axis the cursor is aligned with is replaced by the shape of the Lattice // on that axis. eg., If a Lattice has a shape of [512,512,4,32] and a // tile shape of [32,16,4,16] then tileShape() will return // [512,16,4,16] if the cursor is along the x-axis and [32,512,4,16] if the // cursor is along the y-axis. IPosition tileShape() const; // Function which returns "True" if the increment/decrement operators have // moved the cursor position such that part of the cursor beginning or end // is hanging over the edge of the Lattice. This always returns False. virtual Bool hangOver() const; // Functions to specify a "section" of the Lattice to step over. A section // is defined in terms of the Bottom Left Corner (blc), Top Right Corner // (trc), and step size (inc), on ALL of its axes, including degenerate // axes. The step size defaults to one if not specified. // virtual void subSection (const IPosition& blc, const IPosition& trc); virtual void subSection (const IPosition& blc, const IPosition& trc, const IPosition& inc); // // Return the bottom left hand corner (blc), top right corner (trc) or // step size (increment) used by the current sub-Lattice. If no // sub-Lattice has been defined (with the subSection function) // these functions return blc=0, trc=latticeShape-1, increment=1, ie. the // entire Lattice. // virtual IPosition blc() const; virtual IPosition trc() const; virtual IPosition increment() const; // // Return the axis path. // See LatticeStepper for a // description and examples. virtual const IPosition& axisPath() const; // Function which returns a pointer to dynamic memory of an exact copy // of this instance. The pointer returned by this function must // be deleted externally. virtual LatticeNavigator* clone() const; // Function which checks the internal data of this class for correct // dimensionality and consistant values. // Returns True if everything is fine otherwise returns False virtual Bool ok() const; // Calculate the cache size (in tiles) for this type of access to a lattice // in the given row of the tiled hypercube. virtual uInt calcCacheSize (const IPosition& cubeShape, const IPosition& tileShape, uInt maxCacheSize, uInt bucketSize) const; private: // Prevent the default constructor from being used. TiledLineStepper(); IPosition itsBlc; //# Bottom Left Corner IPosition itsTrc; //# Top Right Corner IPosition itsInc; //# Increment LatticeIndexer itsSubSection; //# The current subsection LatticeIndexer itsIndexer; //# For moving within a tile LatticeIndexer itsTiler; //# For moving between tiles IPosition itsIndexerCursorPos; //# The current position of the iterator. IPosition itsTilerCursorPos; //# The current position of the iterator. IPosition itsCursorShape; //# The shape of the cursor for itsIndexer IPosition itsTileShape; //# The tile shape (= itsTiler cursor shape) IPosition itsAxisPath; //# Path for traversing uInt itsNsteps; //# The number of iterator steps taken so far; uInt itsAxis; //# The axis containing the data vector Bool itsEnd; //# Is the cursor beyond the end? Bool itsStart; //# Is the cursor at the beginning? }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/Lattices/TiledShape.cc000066400000000000000000000201431476623553700212350ustar00rootroot00000000000000//# TiledShape.cc: Define the shape and tile shape //# Copyright (C) 1997,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TiledShape::TiledShape() : itsTileDefined (True) {} TiledShape::TiledShape (const IPosition& shape) : itsShape (shape), itsTileDefined (False) { uInt n = shape.nelements(); for (uInt i=0; i 0")); } } } TiledShape::TiledShape (const IPosition& shape, const IPosition& tileShape) : itsShape (shape), itsTileShape (tileShape), itsTileDefined (True) { uInt n = shape.nelements(); if (tileShape.nelements() != n) { throw (AipsError ("TiledShape: #elements in shape and tileShape differ")); } for (uInt i=0; i 0")); } if (shape(i) < tileShape(i)) { throw (AipsError ("TiledShape: shape has to be >= tileShape")); } } } TiledShape::TiledShape (const TiledShape& that) : itsShape (that.itsShape), itsTileShape (that.itsTileShape), itsTileDefined (that.itsTileDefined) {} TiledShape::~TiledShape() {} TiledShape& TiledShape::operator= (const TiledShape& that) { if (this != &that) { itsShape.resize (that.itsShape.nelements()); itsShape = that.itsShape; itsTileShape.resize (that.itsTileShape.nelements()); itsTileShape = that.itsTileShape; itsTileDefined = that.itsTileDefined; } return *this; } IPosition TiledShape::defaultTileShape (uInt nrPixelsPerTile, Double tolerance) const { uInt n = itsShape.nelements(); Vector tol(n); tol = tolerance; Vector weight(n); weight = double(1); return defaultTileShape (nrPixelsPerTile, tol, weight); } IPosition TiledShape::defaultTileShape (uInt nrPixelsPerTile, const Vector& tolerance, const Vector& weight) const { uInt nrdim = itsShape.nelements(); if (tolerance.nelements() != nrdim || weight.nelements() != nrdim) { throw (AipsError ("TiledShape::defaultTileShape: nelements mismatch")); } double nrLeft = nrPixelsPerTile; Vector tmpShape(nrdim); IPosition tileShape(nrdim, 0); uInt i; Int j; // Iterate until the tile shape is set nicely. // This is needed to prevent tile shape dimensions from underflow // or overflow. while (True) { double prod = 1; uInt n = 0; for (i=0; i 1) { diff = itsShape(i) / diff; } if (maxIndex < 0 || diff < maxDiff) { maxDiff = diff; maxIndex = i; } } } // If there is no underflow/overflow we can copy the dimensions // and exit. if (maxDiff >= 1) { for (i=0; i maxShape(i)) { Int sav = minShape(i); minShape(i) = maxShape(i); maxShape(i) = sav; } if (minShape(i) < 1) { minShape(i) = 1; } if (maxShape(i) > itsShape(i)) { maxShape(i) = itsShape(i); } cubeSpace *= itsShape(i); } // Find the shapes on each axis that will be tried. Block nval(nrdim, uInt(0)); PtrBlock*> values(nrdim); for (i=0; i (maxShape(i) - minShape(i) + 1); // First find exactly fitting shapes. for (j=minShape(i); j<=maxShape(i); j++) { if (itsShape(i) % j == 0) { (*values[i])[nval[i]] = j; nval[i]++; } } // If none available, use all possible shapes within half the range.. if (nval[i] == 0) { for (j=(tileShape(i)+minShape(i))/2; j<=(tileShape(i)+maxShape(i))/2; j++) { (*values[i])[nval[i]] = j; nval[i]++; } } } // Now calculate the cost for all the possibilities. // Take the one with the lowest cost. Block ndone (nrdim, uInt(0)); IPosition tshape (nrdim); for (i=0; i #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Define the shape and tile shape // // // // // //
      • IPosition // // // TiledShape defines the shape and tile shape of a tiled array. // // // TiledShape is a class defining the shape and optionally the tile // shape of a lattice. It is used in the constructors of // PagedArray and // PagedImage. //

        // In principle it serves as a place holder for the lattice shape and // tile shape. The functions shape and tileShape // can be used to retrieve the shapes. // However, when the tile shape is not given, the function // tileShape calculates a default tile shape using the // given maximum tile size in pixel elements. The default tile shape // is calculated in such a way that the sizes of its axes // are proportional to the sizes of the lattice axes. Per axis it is // tried as much as possible to fit an integral number of tiles // in the lattice. //
        In this way getting the tile shape is completely transparent. // // // // // Do not explicitly define a tile shape. // // This results in a default tile shape (of 32,32,32). // TiledShape shape(IPosition(3,128,128,128)); // cout << shape.shape() << ' ' << shape.tileShape() << endl; // // // Use with an explicitly given tile shape. // TiledShape shape(IPosition(3,128,128,128), IPosition(3,64,32,8)); // cout << shape.shape() << ' ' << shape.tileShape() << endl; // // // // Classes PagedArray and PagedImage contained // several duplicated constructors to be able to pass a tile shape. // This class makes it possible to have only one constructor // instead of two. Furthermore it contains the logic to check if the // shapes are conforming and the logic to calculate a default tile shape. // class TiledShape { public: // Default constructor has empty shape and tile shape. TiledShape(); // Use the given shape. // No tile shape is given, so function tileShape // will calculate it using the size of a tile. TiledShape (const IPosition& shape); // Use the given shape and tile shape. // Both shapes must be conforming (i.e. have same number of elements). TiledShape (const IPosition& shape, const IPosition& tileShape); // Copy constructor (copy semantics). TiledShape (const TiledShape& that); ~TiledShape(); // Assignment (copy semantics). TiledShape& operator= (const TiledShape& that); // Is the tile shape defined? Bool isTileShapeDefined() const; // Return the shape. const IPosition& shape() const; // Return the tile shape. // When the tile shape is undefined, the default tile shape will be // calculated using the given tile size and tolerance. //
        The tolerance is used to determine the boundaries where // it is tried to fit an integral number of tiles. IPosition tileShape (uInt nrPixelsPerTile = 32768, Double tolerance = 0.5) const; // Derive the default tile shape from the shape for the given // number of pixels per tile. It is tried to get the same number // of tiles for each dimension. // When a weight vector is given, the number of tiles for a dimension // is proportional to the weight. //
        After the initial guess it tries to optimize it by trying to // waste as little space as possible, while trying to keep as close as // possible to the initial guess. The given tolerance (possibly per axis) // gives the minimum and maximum possible length of a tile axis // (minimum = initial_guess*tolerance; maximum = initial_guess/tolerance). // The heuristic is such that a tile axis length dividing the cube length // exactly is always favoured. // The test program tTiledShape can be used to see how // the algorithm works out for a given shape and tile size. // IPosition defaultTileShape (uInt nrPixelsPerTile, Double tolerance) const; IPosition defaultTileShape (uInt nrPixelsPerTile, const Vector& tolerance, const Vector& weight) const; // private: IPosition itsShape; IPosition itsTileShape; Bool itsTileDefined; }; inline Bool TiledShape::isTileShapeDefined() const { return itsTileDefined; } inline const IPosition& TiledShape::shape() const { return itsShape; } inline IPosition TiledShape::tileShape (uInt nrPixelsPerTile, Double tolerance) const { return (itsTileDefined ? itsTileShape : defaultTileShape (nrPixelsPerTile, tolerance)); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/lattices/Lattices/test/000077500000000000000000000000001476623553700176635ustar00rootroot00000000000000casacore-3.7.1/lattices/Lattices/test/CMakeLists.txt000066400000000000000000000011401476623553700224170ustar00rootroot00000000000000set (tests dLattice dPagedArray tArrayLattice tCurvedLattice2D tExtendLattice tHDF5Iterator tHDF5Lattice tLatticeCache tLatticeConcat tLatticeIndexer tLatticeIterator tLatticeLocker tLatticePerf tLatticeStepper tLatticeUtilities tPagedArray tPixelCurve1D tRebinLattice tSubLattice tTempLattice tTiledLineStepper tTiledShape tTileStepper ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_lattices) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/lattices/Lattices/test/dLattice.cc000066400000000000000000000116241476623553700217270ustar00rootroot00000000000000//# dLattice.cc: illustrates the functions discused in the docs of Lattice.h //# Copyright (C) 1997,1998,1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Complex latMean(const Lattice & lat) { Complex currentSum = 0.0f; uInt nPixels = 0u; RO_LatticeIterator iter(lat); for (iter.reset(); !iter.atEnd(); iter++){ currentSum += sum(iter.cursor()); // nPixels += iter.cursor().nelements(); } return currentSum/Float(nPixels); } void FFT2DReal2Complex(Lattice & result, const Lattice & input){ AlwaysAssert(input.ndim() == 4, AipsError); const IPosition shape = input.shape(); const uInt nx = shape(0); AlwaysAssert (nx > 1, AipsError); const uInt ny = shape(1); AlwaysAssert (ny > 1, AipsError); const uInt npol = shape(2); const uInt nchan = shape(3); const IPosition resultShape = result.shape(); AlwaysAssert(resultShape.nelements() == 4, AipsError); AlwaysAssert(resultShape(3) == Int(nchan), AipsError); AlwaysAssert(resultShape(2) == Int(npol), AipsError); AlwaysAssert(resultShape(1) == Int(ny), AipsError); AlwaysAssert(resultShape(0) == Int(nx/2 + 1), AipsError); const IPosition inputSliceShape(4,nx,ny,1,1); const IPosition resultSliceShape(4,nx/2+1,ny,1,1); COWPtr > inputArrPtr(new Array(inputSliceShape.nonDegenerate())); Array resultArray(resultSliceShape.nonDegenerate()); FFTServer FFT2D(inputSliceShape.nonDegenerate()); IPosition start(4,0); for (uInt c = 0; c < nchan; c++){ for (uInt p = 0; p < npol; p++){ input.getSlice(inputArrPtr, Slicer(start,inputSliceShape), True); FFT2D.fft(resultArray, *inputArrPtr); result.putSlice(resultArray, start); start(2) += 1; } start(2) = 0; start(3) += 1; } } void makePsf(Lattice & psf) { const IPosition centrePos = psf.shape()/2; psf.set(0.0f); // this sets all the elements to zero // As it uses a LatticeIterator it is efficient psf.putAt(1, centrePos); // This sets just the centre element to one AlwaysAssert(near(psf(centrePos), 1.0f, 1E-6), AipsError); AlwaysAssert(near(psf(centrePos*0), 0.0f, 1E-6), AipsError); } int main() { try { const IPosition psfShape(4,4,4,2,3); ArrayLattice psf(psfShape) ; makePsf(psf); IPosition xfrShape(psfShape); xfrShape(0) = psfShape(0)/2 + 1; SetupNewTable xfrSetup("dLattice_tmp_xfr.array", TableDesc(), Table::Scratch); Table xfrTable(xfrSetup); PagedArray xfr(xfrShape, xfrTable); FFT2DReal2Complex(xfr, psf); AlwaysAssert(near(latMean(xfr), Complex(1.0)/Float(psfShape(2)*psfShape(3)), 1E-6), AipsError); } catch (std::exception& x) { cout << x.what() << endl << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } // Local Variables: // compile-command: "gmake OPTLIB=1 dLattice" // End: casacore-3.7.1/lattices/Lattices/test/dPagedArray.cc000066400000000000000000000232261476623553700223620ustar00rootroot00000000000000//# dPagedArray.cc: this contains the examples from the PagedArray.h file //# Copyright (C) 1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main(int argc, const char* argv[]) { try { cout << ">>>" << endl; Input inp(1); inp.version(" "); inp.create("n1", "512", "Number of pixels along the axis 1", "int"); inp.create("n2", "512", "Number of pixels along the axis 2", "int"); inp.create("n3", "4", "Number of pixels along the axis 3", "int"); inp.create("n4", "32", "Number of pixels along the axis 4", "int"); inp.readArguments(argc, argv); cout << "<<<" << endl; IPosition arrayShape(4); arrayShape(0) = inp.getInt("n1"); arrayShape(1) = inp.getInt("n2"); arrayShape(2) = inp.getInt("n3"); arrayShape(3) = inp.getInt("n4"); // Create a PagedArray of Floats of given shape in a file // and initialise it to zero. This will create a directory on disk // called "dPagedArray_tmp.data" that contains files that // exceed 512*512*4*32*4 (=128MBytes) in size. const String filename("dPagedArray_tmp.data"); { /// const IPosition arrayShape(4,512,512,4,32); PagedArray diskArray(arrayShape, filename); cout << "Created a PagedArray of shape " << diskArray.shape() << " (" << diskArray.shape().product()/1024/1024*sizeof(Float) << " MBytes)" << endl << "in the table called " << diskArray.tableName() << endl; Timer timer; diskArray.set (0.0f); timer.show ("set "); diskArray.showCacheStatistics (cout); // Using the set function is an efficient way to initialise the PagedArray // as it uses a PagedArrIter internally. Note that the set function is // defined in the Lattice class that PagedArray is derived from. } // Read the PagedArray produced in Example 1 and put a Gaussian profile into // each spectral channel. { PagedArray diskArray(filename); IPosition shape = diskArray.shape(); // Time how long it takes to iterate without doing IO. { RO_LatticeIterator iter(diskArray, TiledLineStepper(shape, diskArray.tileShape(), 3)); Timer timer; for (iter.reset(); !iter.atEnd(); iter++) { } timer.show ("iterate, no IO"); diskArray.showCacheStatistics (cout); } // Time how long it takes to iterate witt doing input only. { RO_LatticeIterator iter(diskArray, TiledLineStepper(shape, diskArray.tileShape(), 3)); Timer timer; for (iter.reset(); !iter.atEnd(); iter++) { iter.cursor(); } timer.show ("iterate, input"); diskArray.showCacheStatistics (cout); } // Construct a Gaussian Profile to be 10 channels wide and centred on // channel 16. Its height is 1.0. Gaussian1D g(1.0f, 16.0f, 10.0f); // Create a vector to cache a sampled version of this profile. Array profile(IPosition(4,1,1,1,shape(3))); indgen(profile); //// profile.apply(g); // Now put this profile into every spectral channel in the paged array. // This is best done using an iterator. LatticeIterator iter(diskArray, TiledLineStepper(shape, diskArray.tileShape(), 3)); Timer timer; for (iter.reset(); !iter.atEnd(); iter++) { iter.woCursor() = profile; } timer.show ("set vectors "); diskArray.showCacheStatistics (cout); } // Now multiply the I-polarization data by 10.0 in this PagedArray. The // I-polarization data occupies 32MBytes of RAM which is too big to read // into the memory of most computers. So an iterator is used to get suitable // sized chunks. { Table t(filename, Table::Update); PagedArray da(t); const IPosition latticeShape = da.shape(); const Int nx = latticeShape(0); const Int ny = latticeShape(1); /// const Int npol = latticeShape(2); const Int nchan = latticeShape(3); IPosition cursorShape = da.niceCursorShape(); cursorShape(2) = 1; LatticeStepper step(latticeShape, cursorShape); step.subSection (IPosition(4,0), IPosition(4,nx-1,ny-1,0,nchan-1)); LatticeIterator iter(da, step); Timer timer; for (iter.reset(); !iter.atEnd(); iter++) { iter.rwCursor() *= 10.0f; } timer.show ("set I-pol "); da.showCacheStatistics (cout); } // Use a direct call to getSlice to access a small region of the // in spectral channel 0 only. The region is small enough // to not warrent constructing iterators and setting up // LatticeNavigators. In this example the call to the getSlice function // is unnecessary but is done for illustration purposes anyway. if (arrayShape(0)>=100 && arrayShape(1)>=100) { SetupNewTable maskSetup(filename, TableDesc(), Table::New); Table maskTable(maskSetup); PagedArray maskArray(arrayShape, maskTable); Timer timer; maskArray.set(False); timer.show ("setmask"); COWPtr > maskPtr; timer.mark(); maskArray.getSlice (maskPtr, IPosition(4,64,64,0,0), IPosition(4,32,32,1,1), IPosition(4,1)); timer.show ("getmask "); maskPtr.rwRef() = True; timer.mark(); maskArray.putSlice (*maskPtr, IPosition(4,60,60,0,0)); timer.show ("putmask"); maskArray.showCacheStatistics (cout); } // In this example the data in the PagedArray will be accessed a row at // a time while setting the cache size to different values. The comments // illustrate the results when running on an Ultra 1/140 with 64MBytes // of memory. { PagedArray pa(arrayShape, filename); const IPosition latticeShape = pa.shape(); cout << "The tile shape is:" << pa.tileShape() << endl; // Setup to access the PagedArray a row at a time const IPosition sliceShape(4,latticeShape(0), 1, 1, 1); const IPosition stride(4,1); Array row(sliceShape); IPosition start(4, 0); // Set the cache size to enough pixels for one tile only. This uses // 128kBytes of cache memory and takes 125 secs pa.setCacheSizeInTiles (1); Timer timer; for (start(3) = 0; start(3) < latticeShape(3); start(3)++) { for (start(2) = 0; start(2) < latticeShape(2); start(2)++) { for (start(1) = 0; start(1) < latticeShape(1); start(1)++){ pa.getSlice (row, start, sliceShape, stride); } } } timer.show(); pa.showCacheStatistics (cout); pa.clearCache(); // Set the cache size to enough pixels for one row of tiles (ie. 4) // This uses 512 kBytes of cache memory and takes 10 secs pa.setCacheSizeInTiles (4); timer.mark(); for (start(3) = 0; start(3) < latticeShape(3); start(3)++) { for (start(2) = 0; start(2) < latticeShape(2); start(2)++) { for (start(1) = 0; start(1) < latticeShape(1); start(1)++) { pa.getSlice (row, start, sliceShape, stride); } } } timer.show(); pa.showCacheStatistics (cout); pa.clearCache(); // Set the cache size to enough pixels for one plane of tiles // (ie. 4*8) This uses 4MBytes of cache memory and takes 2 secs pa.setCacheSizeInTiles (4*8); timer.mark(); for (start(3) = 0; start(3) < latticeShape(3); start(3)++) { for (start(2) = 0; start(2) < latticeShape(2); start(2)++) { for (start(1) = 0; start(1) < latticeShape(1); start(1)++) { pa.getSlice (row, start, sliceShape, stride); } } } timer.show(); pa.showCacheStatistics (cout); pa.clearCache(); } } catch (std::exception& x) { cerr << x.what() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/lattices/Lattices/test/dPagedArray.run000066400000000000000000000000771476623553700226000ustar00rootroot00000000000000#!/bin/sh $casa_checktool ./dPagedArray n1=16 n2=16 n3=2 n4=2 casacore-3.7.1/lattices/Lattices/test/tArrayLattice.cc000066400000000000000000001002431476623553700227420ustar00rootroot00000000000000//# tArrayLattice.cc: test ArrayLattices and ArrayLatticeIterators. //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Int const_arg_func(const Int &val) { return 3*val; } Int func(Int val) { return 2*val*val; } int main() { try{ // make an array Array array1(IPosition(1,256)); Int i; for (i=0; i<256; i++) { array1(IPosition(1,i)) = i; } // make another array Array array2(IPosition(2,256,128)); for (i=0; i<256; i++) { for (Int j=0; j<128; j++) { array2(IPosition(2,i,j)) = i+j; } } // default ctor, useless (though legal) until assigned to ArrayLattice al0; // construct a new ArrayLattice, with 'array' as contents, in ArrayLattice al1(IPosition(1,256)); // construct a new ArrayLattice, with 'array' as contents, with a const ArrayLattice al2(IPosition(2,256,128)); // reconstruct from a pre-existing ArrayLattice in the Table, ArrayLattice al3(array1); // reconstruct from a pre-existing ArrayLattice in the Table, with // TableColumn name, and row number (defaults to row zero) const ArrayLattice al4(array2); // the copy constructor (reference semantics): passing by value // doesn't make sense, because it would require the creation of a // temporary (but possibly huge) file on disk ArrayLattice al5(al3); // test reference nature AlwaysAssert(near(al3(IPosition(1,0)), 0.0f, 1E-6), AipsError); al5.putAt (33.0, IPosition(1,0)); AlwaysAssert(near(al3(IPosition(1,0)), 33.0f, 1E-6), AipsError); // the assignment operator. typical use would be to create a new // ArrayLattice that (at least initially) is a copy of another one: al0 = al4; // test copy nature AlwaysAssert(near(al0(IPosition(2,0)), 0.0f, 1E-6), AipsError); al0.putAt (33.0, IPosition(2,0)); AlwaysAssert(near(al4(IPosition(2,0)), 33.0f, 1E-6) == False, AipsError); ArrayLattice al6(IPosition(4,5,6,7,8)); // returns the shape of the ArrayLattice. AlwaysAssert(al6.shape() == IPosition(4,5,6,7,8), AipsError); AlwaysAssert(al4.shape() == IPosition(2,256,128), AipsError); // function which extracts an Array of values from a Lattice - a read-only // operation. COWPtr > buffer1; IPosition start(2, 0, 0), shape(2, 128, 64), stride(2, 2, 2); AlwaysAssert(!al4.getSlice(buffer1, start, shape, stride), AipsError); AlwaysAssert(near(buffer1.ref()(IPosition(2,0,0)),0.0f, 1E-6),AipsError); AlwaysAssert(near(buffer1.ref()(IPosition(2,127,0)),254.f,1E-6),AipsError); AlwaysAssert(near(buffer1.ref()(IPosition(2,0,63)),126.0f,1E-6),AipsError); AlwaysAssert(near(buffer1.ref()(IPosition(2,127,63)),380.f,1E-6),AipsError); COWPtr > buffer2; Slicer theSlice(start, shape, stride); AlwaysAssert(!al4.getSlice(buffer2, theSlice), AipsError); AlwaysAssert(near(buffer2.ref()(IPosition(2,0,0)),0.0f, 1E-6), AipsError); AlwaysAssert(near(buffer2.ref()(IPosition(2,127,0)),254.f,1E-6),AipsError); AlwaysAssert(near(buffer2.ref()(IPosition(2,0,63)),126.f,1E-6),AipsError); AlwaysAssert(near(buffer2.ref()(IPosition(2,127,63)),380.f,1E-6),AipsError); Array buffer3; AlwaysAssert(al0.getSlice(buffer3, start, shape, stride), AipsError); AlwaysAssert(near(buffer3(IPosition(2,0,0)),33.0f,1E-6), AipsError); AlwaysAssert(near(buffer3(IPosition(2,127,0)),254.0f,1E-6), AipsError); AlwaysAssert(near(buffer3(IPosition(2,0,63)),126.0f,1E-6), AipsError); AlwaysAssert(near(buffer3(IPosition(2,127,63)),380.0f,1E-6), AipsError); Array buffer4; AlwaysAssert(al0.getSlice(buffer4, theSlice), AipsError); AlwaysAssert(near(buffer4(IPosition(2,0,0)),33.0f,1E-6), AipsError); AlwaysAssert(near(buffer4(IPosition(2,127,0)),254.0f,1E-6), AipsError); AlwaysAssert(near(buffer4(IPosition(2,0,63 )),126.0f,1E-6), AipsError); AlwaysAssert(near(buffer4(IPosition(2,127,63)),380.0f,1E-6), AipsError); // test reference nature of slicer buffer3.set(99.0); AlwaysAssert(near(al0(IPosition(2,0)), 99.0f), AipsError); // put 'value' at every element of the ArrayLattice al6.set(42); // pick a couple of locations at random AlwaysAssert(al6.getAt(IPosition(4,3)) == 42, AipsError); AlwaysAssert(al6.getAt(IPosition(4,1,2,3,4)) == 42, AipsError); AlwaysAssert(al6.getAt(IPosition(4,4,5,6,7)) == 42, AipsError); Array sourceBuffer(IPosition(4,4)); sourceBuffer = 6; // function which places an Array of values within the lattice al6.putSlice(sourceBuffer,IPosition(4,1,2,3,4), IPosition(4,1)); // check the same spots again AlwaysAssert(al6.getAt(IPosition(4,3)) == 42, AipsError); AlwaysAssert(al6.getAt(IPosition(4,1,2,3,4)) == 6, AipsError); AlwaysAssert(al6.getAt(IPosition(4,4,5,6,7)) == 6, AipsError); // function which returns an Array of the data within this Lattice. // AlwaysAssert(allEQ(al3.asArray(), array1), AipsError); array2(IPosition(2,0)) = 33.0; AlwaysAssert(allEQ(al4.asArray(), array2), AipsError); // // a handy place to check for internal consistency AlwaysAssert(al4.ok(), AipsError); // -------------------inherited from Lattice----------------------------- // returns the value of the single element located at the argument // IPosition. al6.putAt(99, IPosition(4,0)); AlwaysAssert(al6.getAt(IPosition(4,0)) == 99, AipsError); // returns the number of axes in this Lattice. AlwaysAssert(al6.ndim() == 4, AipsError); // returns the total number of elements in this Lattice. AlwaysAssert(al6.nelements() == 1680, AipsError); // returns a value of "True" if this instance of Lattice and 'other' have // the same shape, otherwise returns a value of "False". AlwaysAssert(al0.conform(al4), AipsError); // replace every element, x, of the lattice with the result of f(x). // You must pass in the address of the function -- so the function // must be declared and defined in the scope of your program. // All versions of apply require a function that accepts a single // argument of type T (the Lattice template actual type) and returns // a result of the same type. The first apply expects a function with // an argument passed by value; the second expects the argument to // be passed by const reference; the third requires an instance of the // class Functional. The first form ought to run faster // for the built-in types, which may be an issue for large Lattices // stored in memory, where disk access is not an issue. al6.set(2); // check a couple of random spots AlwaysAssert(al6.getAt(IPosition(4,4))==2, AipsError); AlwaysAssert(al6.getAt(IPosition(4,2,3,4,5))==2, AipsError); // func = arg*arg*2 al6.apply(&func); // check a couple of random spots AlwaysAssert(al6.getAt(IPosition(4,4))==8, AipsError); AlwaysAssert(al6.getAt(IPosition(4,2,3,4,5))==8, AipsError); // const_arg_func = arg*3 al6.apply(&const_arg_func); AlwaysAssert(al6.getAt(IPosition(4,4))==24, AipsError); AlwaysAssert(al6.getAt(IPosition(4,2,3,4,5))==24, AipsError); Polynomial poly(3); poly.setCoefficient(1, 0.5); poly.setCoefficient(2, 0.75); poly.setCoefficient(3, 1.0); al3.apply(poly); AlwaysAssert(near(al3(IPosition(1,0)), poly(33), 1E-6), AipsError); AlwaysAssert(near(al3(IPosition(1,127)), poly(127), 1E-6), AipsError); // ----------------------RO_LatticeIterator---------------------------------- IPosition zvector(4,1,1,7,1); LatticeStepper method(al6.shape(), zvector); // Lattice and LatticeNavigator constructor RO_LatticeIterator al6ROIter(al6, method); // LatticeNavigator default "BLC to TRC" constructor RO_LatticeIterator al3ROIter(al3,IPosition(1,8)); // copy ctor (uses reference sematics) RO_LatticeIterator al6ROItercopy(al6ROIter); // destructor (cleans up dangling references) //virtual ~RO_LatticeIterator(); // assignment operator (uses reference semantics) //RO_LatticeIterator &operator=(const RO_LatticeIterator &other); // Function which returns a value of "True" if the cursor is at the start. AlwaysAssert(al6ROIter.atStart(), AipsError); // Increment operator - increment the cursor to the next position. // al6ROIter++; ++al6ROIter; // AlwaysAssert(!al6ROIter.atStart(), AipsError); // Decrement operator - decrement the cursor to the next position. // al6ROIter--; --al6ROIter; --al6ROIter; // AlwaysAssert(al6ROIter.atStart(), AipsError); // Function which resets the cursor to the beginning of the Lattice // (also sets the number of steps taken to zero.) al6ROIter++; al6ROIter.reset(); AlwaysAssert(al6ROIter.atStart(), AipsError); // Function which returns a value of "True" if the cursor is at the end. Int I; for (I=0; I<240; I++) { al6ROIter++; } AlwaysAssert(al6ROIter.atEnd(), AipsError); // Function which returns the number of steps taken since construction // or since reset(). This is a running count // of all cursor movement (operator++ or operator--) since doing x iter++ // followed by x iter-- does not necessarily put the cursor back to the // origin of the Lattice AlwaysAssert(al6ROIter.nsteps() == 240, AipsError); // Function which returns the position of the beginning of the cursor // within the lattice. al6ROIter.reset(); AlwaysAssert(al6ROIter.position() == IPosition(4,0), AipsError); // Function which returns the end of the cursor (i.e. the cursor position // plus the cursor shape.) AlwaysAssert(al6ROIter.endPosition() == IPosition(4,0,0,6,0), AipsError); // Function which returns the shape of the Lattice being iterated through. AlwaysAssert(al6ROIter.latticeShape() == al6.shape(), AipsError); // Function which returns the shape of the cursor as set by the // LatticeNavigator method. AlwaysAssert(al6ROIter.cursorShape() == zvector, AipsError); // Function which returns a reference to the data in the Lattice. // The cursor array may have fewer dimensions than the // Lattice. A call of the function whose return value is // inappropriate with reference to the cursor shape as defined by // the LatticeNavigator will throw an exception. Vector zvectdata(al6ROIter.vectorCursor()); AlwaysAssert(allEQ(zvectdata, 24), AipsError); AlwaysAssert(zvectdata.ndim() == 1, AipsError); AlwaysAssert(zvectdata.shape() == IPosition(1,7), AipsError); Array zarray(al6ROIter.cursor()); AlwaysAssert(allEQ(zarray, 24), AipsError); AlwaysAssert(zarray.ndim() == 4, AipsError); AlwaysAssert(zarray.shape() == IPosition(4,1,1,7,1), AipsError); // test functions which should throw exceptions Bool caught = False; try { al6ROIter.matrixCursor(); } catch (std::exception& x) { caught = True; } AlwaysAssert(caught, AipsError); caught = False; try { al6ROIter.cubeCursor(); } catch (std::exception& x) { caught = True; } AlwaysAssert(caught, AipsError); // check internals for sensibility // AlwaysAssert(al6ROIter.ok(), AipsError); IPosition xymatrix(2,5,6); LatticeStepper newMethod(al6.shape(), xymatrix); // -------------------Read&Write LatticeIterator-------------------- // Lattice and LatticeNavigator ctor LatticeIterator al6Iter(al6, newMethod); // LatticeNavigator default "BLC to TRC" constructor LatticeIterator al3Iter(al3, IPosition(1,8)); // copy ctor (uses reference sematics) LatticeIterator copyal6Iter(al6Iter); // destructor (cleans up dangling references) //~LatticeIterator(); // assignment operator (uses reference semantics) //LatticeIterator &operator=(const LatticeIterator &other); // Function which returns a reference to the data in the Lattice. // The cursor array may have fewer dimensions than the Lattice. A call of // the function whose return value is inappropriate with reference to the // cursor shape as defined by the LatticeNavigator will throw an // exception Matrix xymatdata(al6Iter.matrixCursor()); AlwaysAssert(allEQ(xymatdata, 24), AipsError); AlwaysAssert(xymatdata.ndim() == 2, AipsError); AlwaysAssert(xymatdata.shape() == xymatrix, AipsError); Array xyarray(al6Iter.cursor()); AlwaysAssert(allEQ(xyarray, 24), AipsError); AlwaysAssert(xyarray.ndim() == 4, AipsError); AlwaysAssert(xyarray.shape() == IPosition(4,5,6,1,1), AipsError); // test functions which should throw exceptions caught = False; try { al6Iter.vectorCursor(); } catch (std::exception& x) { caught = True; } AlwaysAssert(caught, AipsError); caught = False; try { al6Iter.cubeCursor(); } catch (std::exception& x) { caught = True; } AlwaysAssert(caught, AipsError); // --------------------- inherited from RO_LatticeIterator ----------- // Function which returns a value of "True" if the cursor is at the start. AlwaysAssert(al6Iter.atStart(), AipsError); // Increment operator - increment the cursor to the next position. // al6Iter++; ++al6Iter; // AlwaysAssert(!al6Iter.atStart(), AipsError); // Decrement operator - decrement the cursor to the next position. // al6Iter--; --al6Iter; --al6Iter; // AlwaysAssert(al6Iter.atStart(), AipsError); // Function which resets the cursor to the beginning of the Lattice // (also sets the number of steps taken to zero.) al6Iter++; al6Iter.reset(); AlwaysAssert(al6Iter.atStart(), AipsError); // Function which returns a value of "True" if the cursor is at the end. for (I=0; I<56; I++) { al6Iter++; } AlwaysAssert(al6Iter.atEnd(), AipsError); // Function which returns the number of steps taken since construction // or since reset(). This is a running count // of all cursor movement (operator++ or operator--) since doing x iter++ // followed by x iter-- does not necessarily put the cursor back to the // origin of the Lattice AlwaysAssert(al6Iter.nsteps() == 56, AipsError); // Function which returns the position of the beginning of the cursor // within the lattice. al6Iter.reset(); AlwaysAssert(al6Iter.position() == IPosition(4,0), AipsError); // Function which returns the end of the cursor (i.e. the cursor position // plus the cursor shape.) AlwaysAssert(al6Iter.endPosition() == IPosition(4,4,5,0,0), AipsError); // Function which returns the shape of the Lattice being iterated through. AlwaysAssert(al6Iter.latticeShape() == al6.shape(), AipsError); // Function which returns the shape of the cursor as set by the // LatticeNavigator method. AlwaysAssert(al6Iter.cursorShape() == IPosition(4,5,6,1,1), AipsError); // -------------------- test Iterator very hard --------------------- IPosition orientation; Int j, k, l; for (i=0;i<4;i++) { for (j=0;j<4; j++) { for (k=0; k<4; k++) { for (l=0; l<4; l++) { if (l!=k && l!=j && l!=i) { if (k!=j && k!=i) { if (j!=i) { orientation = IPosition(4,i,j,k,l); // ------------------- integral shaped vectors ---------- IPosition xvector(1,5); LatticeStepper xvectorstepper(al6.shape(), xvector, orientation); LatticeIterator xiter(al6, xvectorstepper); for (;!xiter.atEnd();xiter++) {} AlwaysAssert(xiter.nsteps() == 336, AipsError); AlwaysAssert(allEQ(xiter.vectorCursor(), 24),AipsError); IPosition yvector(2,1,6); LatticeStepper yvectorstepper(al6.shape(), yvector, orientation); LatticeIterator yiter(al6, yvectorstepper); for (;!yiter.atEnd();yiter++) {} AlwaysAssert(yiter.nsteps() == 280, AipsError); AlwaysAssert(allEQ(yiter.vectorCursor(), 24),AipsError); LatticeStepper zvectorstepper(al6.shape(), zvector, orientation); LatticeIterator ziter(al6, zvectorstepper); for (;!ziter.atEnd();ziter++) {} AlwaysAssert(ziter.nsteps() == 240, AipsError); AlwaysAssert(allEQ(ziter.vectorCursor(), 24),AipsError); IPosition tvector(4,1,1,1,8); LatticeStepper tvectorstepper(al6.shape(), tvector, orientation); LatticeIterator titer(al6, tvectorstepper); for (;!titer.atEnd();titer++) {} AlwaysAssert(titer.nsteps() == 210, AipsError); AlwaysAssert(allEQ(titer.vectorCursor(),24), AipsError); // ----------------------non integral vectors------------------------ // use the algorithm: shape = ceiling(axis length / 2) IPosition xnonIntgrlvector(1,3); LatticeStepper xnonIntgrlvectorstepper(al6.shape(), xnonIntgrlvector, orientation); LatticeIterator nixiter(al6, xnonIntgrlvectorstepper); for (;!nixiter.atEnd();nixiter++) {} AlwaysAssert(nixiter.nsteps() == 672, AipsError); Vector tester(3); tester.set(24); tester(2) = 0; AlwaysAssert(allEQ(nixiter.vectorCursor(), tester), AipsError); IPosition ynonIntgrlvector(2,1,4); LatticeStepper ynonIntgrlvectorstepper(al6.shape(), ynonIntgrlvector, orientation); LatticeIterator niyiter(al6, ynonIntgrlvectorstepper); for (;!niyiter.atEnd();niyiter++) {} AlwaysAssert(niyiter.nsteps() == 560, AipsError); tester.resize(4); tester.set(24); tester(2) = 0; tester(3) = 0; AlwaysAssert(allEQ(niyiter.vectorCursor(), tester), AipsError); IPosition znonIntgrlvector(3,1,1,4); LatticeStepper znonIntgrlvectorstepper(al6.shape(), znonIntgrlvector, orientation); LatticeIterator niziter(al6, znonIntgrlvectorstepper); for (;!niziter.atEnd();niziter++) {} AlwaysAssert(niziter.nsteps() == 480, AipsError); tester(2) = 24; AlwaysAssert(allEQ(niziter.vectorCursor(), tester), AipsError); IPosition tnonIntgrlvector(4,1,1,1,5); LatticeStepper tnonIntgrlvectorstepper(al6.shape(), tnonIntgrlvector, orientation); LatticeIterator nititer(al6, tnonIntgrlvectorstepper); for (;!nititer.atEnd();nititer++) {} AlwaysAssert(nititer.nsteps() == 420, AipsError); tester.resize(5); tester.set(24); tester(3) = 0; tester(4) = 0; AlwaysAssert(allEQ(nititer.vectorCursor(), tester), AipsError); // -------------------------integral matrices---------------------------- LatticeStepper xymatrixstepper(al6.shape(), xymatrix, orientation); LatticeIterator xyiter(al6, xymatrixstepper); for (;!xyiter.atEnd();xyiter++) {} AlwaysAssert(xyiter.nsteps() == 56, AipsError); AlwaysAssert(allEQ(xyiter.matrixCursor(), 24), AipsError); IPosition xzmatrix(3,5,1,7); LatticeStepper xzmatrixstepper(al6.shape(), xzmatrix, orientation); LatticeIterator xziter(al6, xzmatrixstepper); for (;!xziter.atEnd();xziter++) {} AlwaysAssert(xziter.nsteps() == 48, AipsError); AlwaysAssert(allEQ(xziter.matrixCursor(), 24), AipsError); IPosition xtmatrix(4,5,1,1,8); LatticeStepper xtmatrixstepper(al6.shape(), xtmatrix, orientation); LatticeIterator xtiter(al6, xtmatrixstepper); for (;!xtiter.atEnd();xtiter++) {} AlwaysAssert(xtiter.nsteps() == 42, AipsError); AlwaysAssert(allEQ(xtiter.matrixCursor(), 24), AipsError); IPosition yzmatrix(3,1,6,7); LatticeStepper yzmatrixstepper(al6.shape(), yzmatrix, orientation); LatticeIterator yziter(al6, yzmatrixstepper); for (;!yziter.atEnd();yziter++) {} AlwaysAssert(yziter.nsteps() == 40, AipsError); AlwaysAssert(allEQ(yziter.matrixCursor(), 24), AipsError); IPosition ytmatrix(4,1,6,1,8); LatticeStepper ytmatrixstepper(al6.shape(), ytmatrix, orientation); LatticeIterator ytiter(al6, ytmatrixstepper); for (;!ytiter.atEnd();ytiter++) {} AlwaysAssert(ytiter.nsteps() == 35, AipsError); AlwaysAssert(allEQ(ytiter.matrixCursor(), 24), AipsError); IPosition ztmatrix(4,1,1,7,8); LatticeStepper ztmatrixstepper(al6.shape(), ztmatrix, orientation); LatticeIterator ztiter(al6, ztmatrixstepper); for (;!ztiter.atEnd();ztiter++) {} AlwaysAssert(ztiter.nsteps() == 30, AipsError); AlwaysAssert(allEQ(ztiter.matrixCursor(), 24), AipsError); // -----------------------non integral matrices---------------------------- IPosition xyNonItgrlmatrix1(2,3,6); LatticeStepper xyNonItgrlmatrix1stepper(al6.shape(), xyNonItgrlmatrix1); LatticeIterator nixyiter(al6, xyNonItgrlmatrix1stepper); for (;!nixyiter.atEnd();nixyiter++) {} AlwaysAssert(nixyiter.nsteps() == 112, AipsError); Matrix test(xyNonItgrlmatrix1); test.set(24); test.row(2) = 0; AlwaysAssert(allEQ(nixyiter.matrixCursor(), test), AipsError); IPosition xyNonItgrlmatrix2(2,5,4); LatticeStepper xyNonItgrlmatrix2stepper(al6.shape(), xyNonItgrlmatrix2, orientation); LatticeIterator ni2xyiter(al6,xyNonItgrlmatrix2stepper); for (;!ni2xyiter.atEnd();ni2xyiter++) {} AlwaysAssert(ni2xyiter.nsteps() == 112, AipsError); test.resize(xyNonItgrlmatrix2); test.set(24); test.column(2) = 0; test.column(3) = 0; AlwaysAssert(allEQ(ni2xyiter.matrixCursor(), test), AipsError); IPosition xyNonItgrlmatrix3(2,3,4); LatticeStepper xyNonItgrlmatrix3stepper(al6.shape(), xyNonItgrlmatrix3, orientation); LatticeIterator ni3xyiter(al6,xyNonItgrlmatrix3stepper); for (;!ni3xyiter.atEnd();ni3xyiter++) {} AlwaysAssert(ni3xyiter.nsteps() == 224, AipsError); test.resize(xyNonItgrlmatrix3); test.set(24); test.row(2) = 0; test.column(2) = 0; test.column(3) = 0; AlwaysAssert(allEQ(ni3xyiter.matrixCursor(), test), AipsError); IPosition xzNonItgrlmatrix1(3,3,1,7); LatticeStepper xzNonItgrlmatrix1stepper(al6.shape(), xzNonItgrlmatrix1, orientation); LatticeIterator nixziter(al6, xzNonItgrlmatrix1stepper); for (;!nixziter.atEnd();nixziter++) {} AlwaysAssert(nixziter.nsteps() == 96, AipsError); test.resize(IPosition(2,3,7)); test.set(24); test.row(2) = 0; AlwaysAssert(allEQ(nixziter.matrixCursor(), test), AipsError); IPosition xzNonItgrlmatrix2(3,5,1,4); LatticeStepper xzNonItgrlmatrix2stepper(al6.shape(), xzNonItgrlmatrix2, orientation); LatticeIterator ni2xziter(al6,xzNonItgrlmatrix2stepper); for (;!ni2xziter.atEnd();ni2xziter++) {} AlwaysAssert(ni2xziter.nsteps() == 96, AipsError); test.resize(IPosition(2,5,4)); test.set(24); test.column(3) = 0; AlwaysAssert(allEQ(ni2xziter.matrixCursor(), test), AipsError); IPosition xzNonItgrlmatrix3(3,3,1,4); LatticeStepper xzNonItgrlmatrix3stepper(al6.shape(), xzNonItgrlmatrix3, orientation); LatticeIterator ni3xziter(al6,xzNonItgrlmatrix3stepper); for (;!ni3xziter.atEnd();ni3xziter++) {} AlwaysAssert(ni3xziter.nsteps() == 192, AipsError); test.resize(IPosition(2,3,4)); test.set(24); test.row(2) = 0; test.column(3) = 0; AlwaysAssert(allEQ(ni3xziter.matrixCursor(), test), AipsError); IPosition xtNonItgrlmatrix1(4,3,1,1,8); LatticeStepper xtNonItgrlmatrix1stepper(al6.shape(), xtNonItgrlmatrix1, orientation); LatticeIterator nixtiter(al6, xtNonItgrlmatrix1stepper); for (;!nixtiter.atEnd();nixtiter++) {} AlwaysAssert(nixtiter.nsteps() == 84, AipsError); test.resize(IPosition(2,3,8)); test.set(24); test.row(2) = 0; AlwaysAssert(allEQ(nixtiter.matrixCursor(), test), AipsError); IPosition xtNonItgrlmatrix2(4,5,1,1,5); LatticeStepper xtNonItgrlmatrix2stepper(al6.shape(), xtNonItgrlmatrix2, orientation); LatticeIterator ni2xtiter(al6,xtNonItgrlmatrix2stepper); for (;!ni2xtiter.atEnd();ni2xtiter++) {} AlwaysAssert(ni2xtiter.nsteps() == 84, AipsError); test.resize(IPosition(2,5,5)); test.set(24); test.column(3) = 0; test.column(4) = 0; AlwaysAssert(allEQ(ni2xtiter.matrixCursor(), test), AipsError); IPosition xtNonItgrlmatrix3(4,3,1,1,5); LatticeStepper xtNonItgrlmatrix3stepper(al6.shape(), xtNonItgrlmatrix3, orientation); LatticeIterator ni3xtiter(al6,xtNonItgrlmatrix3stepper); for (;!ni3xtiter.atEnd();ni3xtiter++) {} AlwaysAssert(ni3xtiter.nsteps() == 168, AipsError); test.resize(IPosition(2,3,5)); test.set(24); test.row(2) = 0; test.column(3) = 0; test.column(4) = 0; AlwaysAssert(allEQ(ni3xtiter.matrixCursor(), test), AipsError); IPosition yzNonItgrlmatrix1(3,1,4,7); LatticeStepper yzNonItgrlmatrix1stepper(al6.shape(), yzNonItgrlmatrix1, orientation); LatticeIterator niyziter(al6, yzNonItgrlmatrix1stepper); for (;!niyziter.atEnd();niyziter++) {} AlwaysAssert(niyziter.nsteps() == 80, AipsError); test.resize(IPosition(2,4,7)); test.set(24); test.row(2) = 0; test.row(3) = 0; AlwaysAssert(allEQ(niyziter.matrixCursor(), test), AipsError); IPosition yzNonItgrlmatrix2(3,1,6,4); LatticeStepper yzNonItgrlmatrix2stepper(al6.shape(), yzNonItgrlmatrix2, orientation); LatticeIterator ni2yziter(al6,yzNonItgrlmatrix2stepper); for (;!ni2yziter.atEnd();ni2yziter++) {} AlwaysAssert(ni2yziter.nsteps() == 80, AipsError); test.resize(IPosition(2,6,4)); test.set(24); test.column(3) = 0; AlwaysAssert(allEQ(ni2yziter.matrixCursor(), test), AipsError); IPosition yzNonItgrlmatrix3(3,1,4,4); LatticeStepper yzNonItgrlmatrix3stepper(al6.shape(), yzNonItgrlmatrix3, orientation); LatticeIterator ni3yziter(al6,yzNonItgrlmatrix3stepper); for (;!ni3yziter.atEnd();ni3yziter++) {} AlwaysAssert(ni3yziter.nsteps() == 160, AipsError); test.resize(IPosition(2,4,4)); test.set(24); test.row(2) = 0; test.row(3) = 0; test.column(3) = 0; AlwaysAssert(allEQ(ni3yziter.matrixCursor(), test), AipsError); IPosition ytNonItgrlmatrix1(4,1,4,1,8); LatticeStepper ytNonItgrlmatrix1stepper(al6.shape(), ytNonItgrlmatrix1, orientation); LatticeIterator niytiter(al6, ytNonItgrlmatrix1stepper); for (;!niytiter.atEnd();niytiter++) {} AlwaysAssert(niytiter.nsteps() == 70, AipsError); test.resize(IPosition(2,4,8)); test.set(24); test.row(2) = 0; test.row(3) = 0; AlwaysAssert(allEQ(niytiter.matrixCursor(), test), AipsError); IPosition ytNonItgrlmatrix2(4,1,6,1,5); LatticeStepper ytNonItgrlmatrix2stepper(al6.shape(), ytNonItgrlmatrix2, orientation); LatticeIterator ni2ytiter(al6,ytNonItgrlmatrix2stepper); for (;!ni2ytiter.atEnd();ni2ytiter++) {} AlwaysAssert(ni2ytiter.nsteps() == 70, AipsError); test.resize(IPosition(2,6,5)); test.set(24); test.column(3) = 0; test.column(4) = 0; AlwaysAssert(allEQ(ni2ytiter.matrixCursor(), test), AipsError); IPosition ytNonItgrlmatrix3(4,1,4,1,5); LatticeStepper ytNonItgrlmatrix3stepper(al6.shape(), ytNonItgrlmatrix3, orientation); LatticeIterator ni3ytiter(al6,ytNonItgrlmatrix3stepper); for (;!ni3ytiter.atEnd();ni3ytiter++) {} AlwaysAssert(ni3ytiter.nsteps() == 140, AipsError); test.resize(IPosition(2,4,5)); test.set(24); test.row(2) = 0; test.row(3) = 0; test.column(3) = 0; test.column(4) = 0; AlwaysAssert(allEQ(ni3ytiter.matrixCursor(), test), AipsError); IPosition ztNonItgrlmatrix1(4,1,1,4,8); LatticeStepper ztNonItgrlmatrix1stepper(al6.shape(), ztNonItgrlmatrix1, orientation); LatticeIterator niztiter(al6, ztNonItgrlmatrix1stepper); for (;!niztiter.atEnd();niztiter++) {} AlwaysAssert(niztiter.nsteps() == 60, AipsError); test.resize(IPosition(2,4,8)); test.set(24); test.row(3) = 0; AlwaysAssert(allEQ(niztiter.matrixCursor(), test), AipsError); IPosition ztNonItgrlmatrix2(4,1,1,7,5); LatticeStepper ztNonItgrlmatrix2stepper(al6.shape(), ztNonItgrlmatrix2, orientation); LatticeIterator ni2ztiter(al6,ztNonItgrlmatrix2stepper); for (;!ni2ztiter.atEnd();ni2ztiter++) {} AlwaysAssert(ni2ztiter.nsteps() == 60, AipsError); test.resize(IPosition(2,7,5)); test.set(24); test.column(3) = 0; test.column(4) = 0; AlwaysAssert(allEQ(ni2ztiter.matrixCursor(), test), AipsError); IPosition ztNonItgrlmatrix3(4,1,1,4,5); LatticeStepper ztNonItgrlmatrix3stepper(al6.shape(), ztNonItgrlmatrix3, orientation); LatticeIterator ni3ztiter(al6,ztNonItgrlmatrix3stepper); for (;!ni3ztiter.atEnd();ni3ztiter++) {} AlwaysAssert(ni3ztiter.nsteps() == 120, AipsError); test.resize(IPosition(2,4,5)); test.set(24); test.row(3) = 0; test.column(3) = 0; test.column(4) = 0; AlwaysAssert(allEQ(ni3ztiter.matrixCursor(), test), AipsError); } } } } } } } // Test of operator+, etc. { const IPosition latticeShape(4, 4, 16, 15, 8); ArrayLattice pa(latticeShape); Array arr(latticeShape); indgen(arr); pa.put (arr); AlwaysAssertExit (allEQ(pa.get(), arr)); pa += pa; AlwaysAssertExit (allEQ(pa.get(), float(2)*arr)); pa -= ArrayLattice(arr); AlwaysAssertExit (allEQ(pa.get(), arr)); } // Test of copyData { const IPosition latticeShape(4, 4, 16, 15, 8); Array arr(latticeShape); indgen(arr); ArrayLattice from(arr.copy()); ArrayLattice to(latticeShape); to.copyData (from); AlwaysAssertExit (to.asArray()(IPosition(4,0,0,0,1)) == 960); AlwaysAssertExit (allEQ(arr, to.asArray())); } } catch (std::exception& x) { cerr << x.what() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/lattices/Lattices/test/tCurvedLattice2D.cc000066400000000000000000000156721476623553700233150ustar00rootroot00000000000000//# tCurvedLattice2D.cc: Test program for class CurvedLattice //# Copyright (C) 2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void doIt (MaskedLattice& lat, uInt axis1, uInt axis2, uInt curveAxis) { // Make a straight line from (1,0) to the trc. IPosition shp = lat.shape(); Int xtop = shp(axis1); Int ytop = shp(axis2); Int nr = xtop-1; if (nr > ytop) nr = ytop; PixelCurve1D pc(1, 0, nr, nr-1, nr); CurvedLattice2D clat(lat, CLIPNearest2D(), pc, axis1, axis2, curveAxis); // Compose expected output shape. IPosition outshp(shp.nelements() - 1); uInt axnr = 0; for (uInt i=0; i cdata = clat.get(); Array alldata = lat.get(); // Compare if they are equal. IPosition cblc(outshp.nelements(), 0); IPosition ctrc(outshp - 1); ctrc[curveAxis] = 0; IPosition ablc(alldata.ndim(), 0); IPosition atrc(alldata.shape() - 1); outshp[curveAxis] = 1; for (Int i=0; i achunk = alldata(ablc, atrc); AlwaysAssert(allEQ(achunk.reform(outshp), cdata(cblc, ctrc)), AipsError); } // Iterate through the curved lattice and check if the data match. RO_LatticeIterator iter(clat, outshp); Int i=0; for (iter.reset(); !iter.atEnd(); iter++){ cblc[curveAxis] = i; ctrc[curveAxis] = i; AlwaysAssert(allEQ(iter.cursor(), cdata(cblc, ctrc)), AipsError); i++; } } void doIt2 (const Lattice& lattice) { SubLattice mlat(lattice); doIt (mlat, 0, 1, 0); doIt (mlat, 1, 0, 0); } void doIt3 (const Lattice& lattice) { SubLattice mlat(lattice); doIt (mlat, 0, 1, 1); doIt (mlat, 0, 1, 0); doIt (mlat, 0, 2, 1); doIt (mlat, 0, 2, 0); doIt (mlat, 1, 0, 1); doIt (mlat, 1, 0, 0); doIt (mlat, 1, 2, 1); doIt (mlat, 1, 2, 0); doIt (mlat, 2, 0, 1); doIt (mlat, 2, 0, 0); doIt (mlat, 2, 1, 1); doIt (mlat, 2, 1, 0); } int main (int argc, const char* argv[]) { try { { const IPosition latticeShape(2, 16, 12); Array arr(latticeShape); indgen(arr); ArrayLattice lattice(arr); doIt2 (lattice); PagedArray pa(latticeShape, "tCurvedLattice2D_tmp.pa"); pa.put (arr); doIt2 (pa); } { const IPosition latticeShape(3, 16, 12, 4); Array arr(latticeShape); indgen(arr); ArrayLattice lattice(arr); doIt3 (lattice); PagedArray pa(latticeShape, "tCurvedLattice2D_tmp.pa"); pa.put (arr); doIt3 (pa); } { const IPosition latticeShape(4, 16, 12, 4, 32); Array arr(latticeShape); indgen(arr); ArrayLattice lattice(arr); doIt3 (lattice); PagedArray pa(latticeShape, "tCurvedLattice2D_tmp.pa"); pa.put (arr); doIt3 (pa); } { // Test performance. Input inp(1); inp.version(" "); inp.create("nx", "64", "Number of pixels along the x-axis", "int"); inp.create("ny", "64", "Number of pixels along the y-axis", "int"); inp.create("nz", "64", "Number of pixels along the z-axis", "int"); inp.readArguments(argc, argv); const uInt nx=inp.getInt("nx"); const uInt ny=inp.getInt("ny"); const uInt nz=inp.getInt("nz"); IPosition latticeShape(3,nx,ny,nz); { PagedArray pa(latticeShape, "tCurvedLattice2D_tmp.pa"); Array arr(IPosition(3,nx,ny,1)); indgen(arr); LatticeIterator iter(pa, IPosition(3,nx,ny,1)); for (iter.reset(); !iter.atEnd(); iter++) { iter.woCursor() = arr; arr += Int(arr.nelements()); } cout << "Filled PagedArray with shape " << latticeShape << endl; } PagedArray pa("tCurvedLattice2D_tmp.pa"); SubLattice mlat(pa); // Make a straight line from (0,0) to the trc. IPosition shp = pa.shape(); Int xtop = shp(0); Int ytop = shp(1); Int nr = xtop; if (nr > ytop) nr = ytop; PixelCurve1D pc(0, 0, shp(0)-1, shp(1)-1, nr); cout << "nr=" << nr << endl; { CurvedLattice2D clat(mlat, CLIPNearest2D(), pc, 0, 1, 0); Timer timer; clat.get(); timer.show("curved 0,1,0"); pa.showCacheStatistics(cout); } { CurvedLattice2D clat(mlat, CLIPNearest2D(), pc, 0, 1, 1); Timer timer; clat.get(); timer.show("curved 0,1,1"); pa.showCacheStatistics(cout); } } } catch (std::exception& x) { cerr << "Caught exception: " << x.what() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/lattices/Lattices/test/tExtendLattice.cc000066400000000000000000000212131476623553700231120ustar00rootroot00000000000000//# tExtendLattice.cc: Test program for class ExtendLattice //# Copyright (C) 2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void testVectorROIter (const Lattice& extendlat, const Lattice& lattice, Int nnew) { Int nstep; const IPosition latticeShape(extendlat.shape()); const IPosition cursorShape(1,latticeShape(0)); LatticeStepper step(latticeShape, cursorShape); RO_LatticeIterator iter(extendlat, step); LatticeStepper step2(lattice.shape(), cursorShape); RO_LatticeIterator iter2(lattice, step2); // static_cast's added for a workaround for an SGI compiler bug. for (iter2.reset(); !iter2.atEnd(); iter2++) { for (Int i=0; i >(iter.vectorCursor()), static_cast >(iter2.vectorCursor())), AipsError); iter++; } } AlwaysAssert(iter.atEnd(), AipsError); nstep = iter.nsteps(); AlwaysAssert(nstep == latticeShape.product()/latticeShape(0), AipsError); IPosition expectedPos(latticeShape-1); AlwaysAssert(iter.endPosition() == expectedPos, AipsError); expectedPos(0) = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); } void testRest() { PagedArray pa(IPosition(1,10), "tExtendLattice_tmp.pa"); AlwaysAssertExit (pa.isPaged()); AlwaysAssertExit (pa.isPersistent()); AlwaysAssertExit (pa.isWritable()); AlwaysAssertExit (pa.name(True) == "tExtendLattice_tmp.pa"); LCPagedMask mask(IPosition(1,10), "tExtendLattice_tmp.pa/mask"); { // Make an ExtendLattice. ExtendLattice sl(pa, IPosition(2,10,5), IPosition(1,1), IPosition()); AlwaysAssertExit (!sl.isMasked()); AlwaysAssertExit (!sl.hasPixelMask()); AlwaysAssertExit (sl.isPaged()); AlwaysAssertExit (!sl.isPersistent()); AlwaysAssertExit (!sl.isWritable()); AlwaysAssertExit (sl.name(True) == "tExtendLattice_tmp.pa"); AlwaysAssertExit (sl.ndim() == 2); AlwaysAssertExit (sl.shape() == IPosition(2,10,5)); AlwaysAssertExit (sl.niceCursorShape() == IPosition(2,10,1)); } { // A RO ExtendLattice as a masked Lattice. SubLattice sp(pa, mask); ExtendLattice sl(sp, IPosition(2,10,5), IPosition(1,1), IPosition()); AlwaysAssertExit (sl.isMasked()); AlwaysAssertExit (!sl.hasPixelMask()); AlwaysAssertExit (sl.isPaged()); AlwaysAssertExit (!sl.isPersistent()); AlwaysAssertExit (!sl.isWritable()); AlwaysAssertExit (sl.name(True) == "tExtendLattice_tmp.pa"); AlwaysAssertExit (sl.ndim() == 2); AlwaysAssertExit (sl.shape() == IPosition(2,10,5)); AlwaysAssertExit (sl.niceCursorShape() == IPosition(2,10,1)); } } void testMask() { IPosition latticeShape(3,10,11,12); PagedArray pa(latticeShape, "tExtendLattice_tmp.pa"); LCPagedMask mask(latticeShape, "tExtendLattice_tmp.pa/mask"); Array arr(pa.shape()); indgen(arr); pa.put (arr); Array arrm(pa.shape()); arrm = True; arrm(IPosition(3,0,0,0), IPosition(3,9,10,11), IPosition(3,2,1,1)) = False; mask.put (arrm); SubLattice lattice(pa, mask); ExtendLattice extendlat (lattice, IPosition(4,10,5,11,12), IPosition(1,1), IPosition()); Array arr1 = extendlat.get(); Array arrm1 = extendlat.getMask(); AlwaysAssertExit (arr1.shape() == extendlat.shape()); AlwaysAssertExit (arrm1.shape() == extendlat.shape()); for (Int i=0; i<5; i++) { Array parr = arr1(IPosition(4,0,i,0,0), IPosition(4,10-1,i,11-1,12-1)); AlwaysAssertExit (allEQ(parr.reform(latticeShape), arr)); Array parrm = arrm1(IPosition(4,0,i,0,0), IPosition(4,10-1,i,11-1,12-1)); AlwaysAssertExit (allEQ(parrm.reform(latticeShape), arrm)); } for (Int i=0; i<5; i++) { Array parr = extendlat.getSlice (IPosition(4,0,i,0,0), IPosition(4,10,1,11,12)); AlwaysAssertExit (allEQ(parr.reform(latticeShape), arr)); Array parrm = extendlat.getMaskSlice (IPosition(4,0,i,0,0), IPosition(4,10,1,11,12)); AlwaysAssertExit (allEQ(parrm.reform(latticeShape), arrm)); } } int main () { try { { const IPosition latticeShape(4, 12, 1, 4, 32); Array arr(latticeShape); indgen(arr); ArrayLattice lattice(arr); { ExtendLattice extendlat (lattice, IPosition(5,12,3,4,4,32), IPosition(1,1), IPosition(1,2)); Array arr1 = extendlat.get(); AlwaysAssertExit (arr1.shape() == extendlat.shape()); for (Int i=0; i<3; i++) { for (Int j=0; j<4; j++) { Array parr = arr1(IPosition(5,0,i,j,0,0), IPosition(5,12-1,i,j,4-1,32-1)); AlwaysAssertExit (allEQ(parr.reform(latticeShape), arr)); } } testVectorROIter (extendlat, lattice, 3*4); } { ExtendLattice extendlat (lattice, IPosition(5,12,3,4,4,32), IPosition(1,2), IPosition(1,1)); Array arr1 = extendlat.get(); AlwaysAssertExit (arr1.shape() == extendlat.shape()); for (Int i=0; i<3; i++) { for (Int j=0; j<4; j++) { Array parr = arr1(IPosition(5,0,i,j,0,0), IPosition(5,12-1,i,j,4-1,32-1)); AlwaysAssertExit (allEQ(parr.reform(latticeShape), arr)); } } testVectorROIter (extendlat, lattice, 3*4); } { ExtendLattice extendlat (lattice, IPosition(5,12,1,4,4,32), IPosition(1,2), IPosition()); Array arr1 = extendlat.get(); AlwaysAssertExit (arr1.shape() == extendlat.shape()); for (Int i=0; i<1; i++) { for (Int j=0; j<4; j++) { Array parr = arr1(IPosition(5,0,i,j,0,0), IPosition(5,12-1,i,j,4-1,32-1)); AlwaysAssertExit (allEQ(parr.reform(latticeShape), arr)); } } testVectorROIter (extendlat, lattice, 1*4); } { ExtendLattice extendlat (lattice, IPosition(4,12,6,4,32), IPosition(), IPosition(1,1)); Array arr1 = extendlat.get(); AlwaysAssertExit (arr1.shape() == extendlat.shape()); for (Int i=0; i<6; i++) { Array parr = arr1(IPosition(4,0,i,0,0), IPosition(4,12-1,i,4-1,32-1)); AlwaysAssertExit (allEQ(parr.reform(latticeShape), arr)); } testVectorROIter (extendlat, lattice, 6); } { ExtendLattice extendlat (lattice, IPosition(6,12,3,4,6,32,5), IPosition(2,3,5), IPosition(1,1)); Array arr1 = extendlat.get(); AlwaysAssertExit (arr1.shape() == extendlat.shape()); for (Int i=0; i<3; i++) { for (Int j=0; j<6; j++) { for (Int k=0; k<5; k++) { Array parr = arr1(IPosition(6,0,i,0,j,0,k), IPosition(6,12-1,i,4-1,j,32-1,k)); AlwaysAssertExit (allEQ(parr.reform(latticeShape), arr)); } } } } } // Test some other ExtendLattice functions. testRest(); // Test mask handling testMask(); } catch (std::exception& x) { cerr << "Caught exception: " << x.what() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/lattices/Lattices/test/tHDF5Iterator.cc000066400000000000000000001476461476623553700226000ustar00rootroot00000000000000//# tHDF5Iterator.cc: Test of HDF5 iterator performance //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace casacore; void testVectorROIter (const Lattice& lattice, Bool useRef) { cout << " Testing using a Vector cursor" << endl; Int nstep; const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(1,latticeShape(0)); LatticeStepper step(latticeShape, cursorShape); RO_LatticeIterator iter(lattice, step, useRef); Vector expectedResult(latticeShape(0)); indgen(expectedResult); AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()) == True, AipsError); try { Matrix temp(iter.matrixCursor()); throw(AipsError("tHDF5Iterator - " "matrixCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("two non-degenerate")) { throw (AipsError (x.what())); } } try { Cube temp(iter.cubeCursor()); throw(AipsError("tHDF5Iterator - " "cubeCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("three non-degenerate")) { throw (AipsError (x.what())); } } AlwaysAssert(latticeShape == iter.latticeShape(), AipsError); AlwaysAssert(cursorShape == iter.cursorShape().nonDegenerate(), AipsError); Timer clock; for (iter.reset(); !iter.atEnd(); iter++){ AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); expectedResult += Int(cursorShape.product()); } nstep = iter.nsteps(); AlwaysAssert(nstep == latticeShape.product()/latticeShape(0), AipsError); IPosition expectedPos(latticeShape-1); AlwaysAssert(iter.endPosition() == expectedPos, AipsError); expectedPos(0) = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedResult -= Int(cursorShape.product()); Int ns=0; for (; !iter.atStart(); iter--){ AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()) == True, AipsError); expectedResult -= Int(cursorShape.product()); ns++; } clock.show(); nstep = iter.nsteps(); AlwaysAssert(nstep == 2*(latticeShape.product()/latticeShape(0)), AipsError); expectedPos = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedPos(0) = latticeShape(0) - 1; AlwaysAssert(iter.endPosition() == expectedPos, AipsError); } void testMatrixROIter (const Lattice& lattice, Bool useRef) { cout << " Testing using a Matrix cursor" << endl; Int nstep; const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(2, latticeShape(0), latticeShape(1)); RO_LatticeIterator iter(lattice, cursorShape, useRef); Matrix expectedResult(latticeShape(0), latticeShape(1)); indgen(expectedResult); AlwaysAssert(allEQ(expectedResult, iter.matrixCursor()) == True, AipsError); AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()) == True, AipsError); try { Vector temp(iter.vectorCursor()); throw(AipsError("tHDF5Iterator - " "vectorCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("one non-degenerate")) { throw (AipsError (x.what())); } } try { Cube temp(iter.cubeCursor()); throw(AipsError("tHDF5Iterator - " "cubeCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("three non-degenerate")) { throw (AipsError (x.what())); } } Timer clock; for (iter.reset(); !iter.atEnd(); iter++){ AlwaysAssert(allEQ(expectedResult, iter.matrixCursor()) == True, AipsError); expectedResult += Int(cursorShape.product()); } nstep = iter.nsteps(); AlwaysAssert(nstep == latticeShape(2)*latticeShape(3), AipsError); IPosition expectedPos(latticeShape-1); AlwaysAssert(iter.endPosition() == expectedPos, AipsError); expectedPos(0) = 0; expectedPos(1) = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedResult -= Int(cursorShape.product()); for (; !iter.atStart(); --iter){ AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()) == True, AipsError); expectedResult -= Int(cursorShape.product()); } clock.show(); nstep = iter.nsteps(); AlwaysAssert(nstep == 2*(latticeShape(2)*latticeShape(3)), AipsError); expectedPos = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedPos(0) = latticeShape(0) - 1; expectedPos(1) = latticeShape(1) - 1; AlwaysAssert(iter.endPosition() == expectedPos, AipsError); } void testCubeROIter (const Lattice& lattice, Bool useRef) { cout << " Testing using a Cube cursor" << endl; Int nstep; const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(3, latticeShape(0), latticeShape(1), latticeShape(2)); LatticeStepper step(latticeShape, cursorShape); RO_LatticeIterator iter(lattice, step, useRef); Cube expectedResult(latticeShape(0), latticeShape(1), latticeShape(2)); indgen(expectedResult); AlwaysAssert(allEQ(expectedResult, iter.cubeCursor()) == True, AipsError); AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()) == True, AipsError); try { Vector temp(iter.vectorCursor()); throw(AipsError("tHDF5Iterator - " "vectorCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("one non-degenerate")) { throw (AipsError (x.what())); } } try { Matrix temp(iter.matrixCursor()); throw(AipsError("tHDF5Iterator - " "matrixCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("two non-degenerate")) { throw (AipsError (x.what())); } } Timer clock; for (iter.reset(); !iter.atEnd(); iter++){ AlwaysAssert(allEQ(expectedResult, iter.cubeCursor()) == True, AipsError); expectedResult += Int(cursorShape.product()); } nstep = iter.nsteps(); AlwaysAssert(nstep == latticeShape(3), AipsError); IPosition expectedPos(latticeShape-1); AlwaysAssert(iter.endPosition() == expectedPos, AipsError); expectedPos(0) = 0; expectedPos(1) = 0; expectedPos(2) = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedResult -= Int(cursorShape.product()); for (; !iter.atStart(); iter--){ AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()) == True, AipsError); expectedResult -= Int(cursorShape.product()); } clock.show(); nstep = iter.nsteps(); AlwaysAssert(nstep == 2 * (latticeShape(3)), AipsError); expectedPos = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedPos(0) = latticeShape(0) - 1; expectedPos(1) = latticeShape(1) - 1; expectedPos(2) = latticeShape(2) - 1; AlwaysAssert(iter.endPosition() == expectedPos, AipsError); } void testArrayROIter (const Lattice& lattice, Bool useRef) { cout << " Testing using an Array (4-D) cursor" << endl; Int nstep; const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(latticeShape); RO_LatticeIterator iter(lattice, cursorShape, useRef); Array expectedResult(latticeShape); indgen(expectedResult); AlwaysAssert(allEQ(expectedResult, iter.cursor()) == True, AipsError); try { Vector temp(iter.vectorCursor()); throw(AipsError("tHDF5Iterator - " "vectorCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("one non-degenerate")) { throw (AipsError (x.what())); } } try { Matrix temp(iter.matrixCursor()); throw(AipsError("tHDF5Iterator - " "matrixCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("two non-degenerate")) { throw (AipsError (x.what())); } } try { Cube temp(iter.cubeCursor()); throw(AipsError("tHDF5Iterator - " "cubeCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("three non-degenerate")) { throw (AipsError (x.what())); } } Timer clock; for (iter.reset(); !iter.atEnd(); ++iter){ AlwaysAssert(allEQ(expectedResult, iter.cursor()) == True, AipsError); expectedResult += Int(cursorShape.product()); } nstep = iter.nsteps(); AlwaysAssert(nstep == 1, AipsError); IPosition expectedPos(latticeShape-1); AlwaysAssert(iter.endPosition() == expectedPos, AipsError); expectedPos = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedResult -= Int(cursorShape.product()); for (; !iter.atStart(); --iter){ AlwaysAssert(allEQ(expectedResult, iter.cursor()) == True, AipsError); expectedResult -= Int(cursorShape.product()); } clock.show(); nstep = iter.nsteps(); AlwaysAssert(nstep == 2, AipsError); expectedPos = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedPos = latticeShape - 1; AlwaysAssert(iter.endPosition() == expectedPos, AipsError); } void test8ElemROIter (const Lattice& lattice, Bool useRef) { cout << " Testing using an 8 element cursor" << endl; Int nstep; const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(1,8); LatticeStepper step(latticeShape, cursorShape); RO_LatticeIterator iter(lattice, step, useRef); Array expectedResult(cursorShape); indgen(expectedResult); AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()) == True, AipsError); try { Matrix temp(iter.matrixCursor()); throw(AipsError("tHDF5Iterator - " "matrixCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("two non-degenerate")) { throw (AipsError (x.what())); } } try { Cube temp(iter.cubeCursor()); throw(AipsError("tHDF5Iterator - " "cubeCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("three non-degenerate")) { throw (AipsError (x.what())); } } Timer clock; for (iter.reset(); !iter.atEnd(); ++iter){ AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); expectedResult += Int(cursorShape.product()); } nstep = iter.nsteps(); AlwaysAssert(nstep == latticeShape.product()/8, AipsError); IPosition expectedPos(latticeShape-1); AlwaysAssert(iter.endPosition() == expectedPos, AipsError); expectedPos(0) = 8; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedResult -= Int(cursorShape.product()); for (; !iter.atStart(); --iter){ AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()) == True, AipsError); expectedResult -= Int(cursorShape.product()); } clock.show(); nstep = iter.nsteps(); AlwaysAssert(nstep == 2*(latticeShape.product()/8), AipsError); expectedPos = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedPos(0) = 8-1; AlwaysAssert(iter.endPosition() == expectedPos, AipsError); } void testTileROIter (const Lattice& lattice, Bool useRef) { cout << " Testing using a tile cursor" << endl; Int nstep; const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(lattice.niceCursorShape()); RO_LatticeIterator iter(lattice, useRef); Array expectedResult(cursorShape); indgen(expectedResult); AlwaysAssert(allEQ(expectedResult, iter.cursor()) == True, AipsError); Timer clock; for (iter.reset(); !iter.atEnd(); ++iter){ AlwaysAssert(allEQ(expectedResult, iter.cursor()) == True, AipsError); expectedResult += Int(cursorShape.product()); } nstep = iter.nsteps(); AlwaysAssert(nstep == latticeShape.product()/cursorShape.product(), AipsError); for (; !iter.atStart(); --iter){ expectedResult -= Int(cursorShape.product()); AlwaysAssert(allEQ(expectedResult, iter.cursor()) == True, AipsError); } clock.show(); nstep = iter.nsteps(); AlwaysAssert(nstep == 2*latticeShape.product()/cursorShape.product(), AipsError); } void testTiledLineROIter (const Lattice& lattice, Bool useRef) { cout << " Testing using a tiled line cursor" << endl; Int nstep; const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(lattice.niceCursorShape()); TiledLineStepper step(latticeShape, cursorShape, 0); RO_LatticeIterator iter(lattice, step, useRef); Vector expectedResult(latticeShape(0)); indgen(expectedResult); AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); Timer clock; for (iter.reset(); !iter.atEnd(); ++iter){ AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); expectedResult += Int(latticeShape(0)); } nstep = iter.nsteps(); AlwaysAssert(nstep == latticeShape.product()/latticeShape(0), AipsError); for (; !iter.atStart(); --iter){ expectedResult -= Int(latticeShape(0)); AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); } clock.show(); nstep = iter.nsteps(); AlwaysAssert(nstep == 2*latticeShape.product()/latticeShape(0), AipsError); } void testCopyAssignROIter (const Lattice& lattice, Bool useRef) { cout << " Testing the copy constructor and assignment operator" << endl; const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(1,latticeShape(0)); { // Test default ctor and its handling in copy and assignment. RO_LatticeIterator iter; AlwaysAssert(iter.isNull(), AipsError); RO_LatticeIterator iter1 = iter.copy(); AlwaysAssert(iter1.isNull(), AipsError); iter = RO_LatticeIterator (lattice, useRef); AlwaysAssert(!iter.isNull(), AipsError); iter = iter1; AlwaysAssert(iter.isNull(), AipsError); iter = RO_LatticeIterator (lattice, useRef); AlwaysAssert(!iter.isNull(), AipsError); iter1 = iter; AlwaysAssert(!iter1.isNull(), AipsError); iter = RO_LatticeIterator(); AlwaysAssert(iter.isNull(), AipsError); AlwaysAssert(!iter1.isNull(), AipsError); RO_LatticeIterator iterc(iter); AlwaysAssert(iterc.isNull(), AipsError); RO_LatticeIterator iterc1(iter1); AlwaysAssert(!iterc1.isNull(), AipsError); iterc1 = iterc; AlwaysAssert(iterc1.isNull(), AipsError); AlwaysAssert(!iter1.isNull(), AipsError); } RO_LatticeIterator iter(lattice, LatticeStepper(latticeShape, cursorShape), useRef); AlwaysAssert(!iter.isNull(), AipsError); iter++; Vector expectedResult(latticeShape(0)); indgen(expectedResult); expectedResult += Int(cursorShape.product()); AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); RO_LatticeIterator iterCopy(iter.copy()); Vector expectedCopy(expectedResult.copy()); AlwaysAssert(allEQ(expectedCopy, iterCopy.vectorCursor()) == True, AipsError); iter++; expectedResult += Int(cursorShape.product()); AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); AlwaysAssert(allEQ(expectedCopy, iterCopy.vectorCursor()) == True, AipsError); iterCopy--; expectedCopy -= Int(cursorShape.product()); AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); AlwaysAssert(allEQ(expectedCopy, iterCopy.vectorCursor()) == True, AipsError); AlwaysAssert(allEQ(iter.vectorCursor(),iterCopy.vectorCursor()) == False, AipsError); iterCopy = iter.copy(); expectedCopy = expectedResult; AlwaysAssert(allEQ(iter.vectorCursor(),iterCopy.vectorCursor()) == True, AipsError); AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); iterCopy++; expectedCopy += Int(cursorShape.product()); AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); AlwaysAssert(allEQ(expectedCopy, iterCopy.vectorCursor()) == True, AipsError); AlwaysAssert(allEQ(iter.vectorCursor(),iterCopy.vectorCursor()) == False, AipsError); } void testNonCongruentROIter (const Lattice& lattice, Bool useRef) { cout << " Testing using a non-congruent cursor" << endl; const IPosition latticeShape(lattice.shape()); IPosition cursorShape(2,9); LatticeStepper step(latticeShape, cursorShape); RO_LatticeIterator iter(lattice, step, useRef); Matrix expectedResult(cursorShape); Vector oneRow(cursorShape(0)); indgen(oneRow); uInt i; for (i = 0; i < uInt(cursorShape(1)); i++) { expectedResult.column(i) = oneRow; oneRow += Int(latticeShape(0)); } AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()), AipsError); iter++; indgen(oneRow, Int(cursorShape(0))); for (i = 0; i < uInt(cursorShape(1)); i++) { oneRow(7) = 0; oneRow(8) = 0; expectedResult.column(i) = oneRow; oneRow += Int(latticeShape(0)); } AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()), AipsError); iter++; expectedResult = 0; indgen(oneRow, Int(cursorShape(0)*latticeShape(0))); for (i = 0; i < 3; i++) { expectedResult.column(i) = oneRow; oneRow += Int(latticeShape(0)); } AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()), AipsError); iter++; expectedResult = 0; indgen(oneRow, Int(cursorShape(0)*(latticeShape(0)+1))); for (i = 0; i < 3; i++) { oneRow(7) = 0; oneRow(8) = 0; expectedResult.column(i) = oneRow; oneRow += Int(latticeShape(0)); } cursorShape = 5; step.setCursorShape(cursorShape); step.subSection(IPosition(4, 3,0,0,0), latticeShape-1, IPosition(4, 2,2,1,1)); RO_LatticeIterator subIter(lattice, step, useRef); oneRow.resize(5); Matrix expectedResult1(5,5); expectedResult1 = 0; indgen(oneRow, 3, 2); for (i = 0; i < 5; i++) { expectedResult1.column(i) = oneRow; oneRow += 32; } AlwaysAssert(allEQ(expectedResult1, subIter.cursor().nonDegenerate()), AipsError); subIter++; Matrix expectedResult2(5,5); expectedResult2 = 0; indgen(oneRow, 13, 2); for (i = 0; i < 5; i++) { oneRow(2) = 0; oneRow(3) = 0; oneRow(4) = 0; expectedResult2.column(i) = oneRow; oneRow += 32; } AlwaysAssert(allEQ(expectedResult2, subIter.cursor().nonDegenerate()), AipsError); subIter++; Matrix expectedResult3(5,5); expectedResult3 = 0; indgen(oneRow, 163, 2); for (i = 0; i < 1; i++) { expectedResult3.column(i) = oneRow; oneRow += 32; } AlwaysAssert(allEQ(expectedResult3, subIter.cursor().nonDegenerate()), AipsError); subIter++; Matrix expectedResult4(5,5); expectedResult4 = 0; indgen(oneRow, 173, 2); for (i = 0; i < 1; i++) { oneRow(2) = 0; oneRow(3) = 0; oneRow(4) = 0; expectedResult4.column(i) = oneRow; oneRow += 32; } AlwaysAssert(allEQ(expectedResult4, subIter.cursor().nonDegenerate()), AipsError); subIter--; AlwaysAssert(allEQ(expectedResult3, subIter.cursor().nonDegenerate()), AipsError); subIter--; AlwaysAssert(allEQ(expectedResult2, subIter.cursor().nonDegenerate()), AipsError); subIter--; AlwaysAssert(allEQ(expectedResult1, subIter.cursor().nonDegenerate()), AipsError); } void testVectorRWIter (Lattice& lattice, Bool useRef) { cout << " Testing using a Vector cursor" << endl; Int nstep; const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(1,latticeShape(0)); LatticeStepper step(latticeShape, cursorShape); LatticeIterator iter(lattice, step, useRef); Vector expectedResult(latticeShape(0)); indgen(expectedResult); AlwaysAssert(allEQ(expectedResult,iter.vectorCursor()) == True, AipsError); AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()) == True, AipsError); try { Matrix temp(iter.matrixCursor()); throw(AipsError("tHDF5Iterator - " "matrixCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("two non-degenerate")) { throw (AipsError (x.what())); } } try { Cube temp(iter.cubeCursor()); throw(AipsError("tHDF5Iterator - " "cubeCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("three non-degenerate")) { throw (AipsError (x.what())); } } AlwaysAssert(latticeShape == iter.latticeShape(), AipsError); AlwaysAssert(cursorShape == iter.cursorShape().nonDegenerate(), AipsError); Timer clock; for (iter.reset(); !iter.atEnd(); iter++){ AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); iter.rwVectorCursor()(0) -= expectedResult(0); expectedResult += Int(cursorShape.product()); } nstep = iter.nsteps(); AlwaysAssert(nstep == latticeShape.product()/latticeShape(0), AipsError); IPosition expectedPos(latticeShape-1); AlwaysAssert(iter.endPosition() == expectedPos, AipsError); expectedPos(0) = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedResult -= Int(cursorShape.product()); expectedResult(0) = 0; for (; !iter.atStart(); iter--){ AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()) == True, AipsError); iter.woCursor() = 1; expectedResult -= Int(cursorShape.product()); expectedResult(0) = 0; } clock.show(); nstep = iter.nsteps(); AlwaysAssert(nstep == 2*(latticeShape.product()/latticeShape(0)), AipsError); expectedPos = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedPos(0) = latticeShape(0) - 1; AlwaysAssert(iter.endPosition() == expectedPos, AipsError); } void testMatrixRWIter (Lattice& lattice, Bool useRef) { cout << " Testing using a Matrix cursor" << endl; Int nstep; const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(2, latticeShape(0), latticeShape(1)); LatticeIterator iter(lattice, cursorShape, useRef); Matrix expectedResult(latticeShape(0), latticeShape(1)); expectedResult = 1; AlwaysAssert(allEQ(expectedResult, iter.matrixCursor()) == True, AipsError); AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()) == True, AipsError); try { Vector temp(iter.vectorCursor()); throw(AipsError("tHDF5Iterator - " "vectorCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("one non-degenerate")) { throw (AipsError (x.what())); } } try { Cube temp(iter.cubeCursor()); throw(AipsError("tHDF5Iterator - " "cubeCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("three non-degenerate")) { throw (AipsError (x.what())); } } Timer clock; for (iter.reset(); !iter.atEnd(); iter++){ AlwaysAssert(allEQ(expectedResult, iter.matrixCursor()) == True, AipsError); iter.rwMatrixCursor()(0,0) = 2; } nstep = iter.nsteps(); AlwaysAssert(nstep == latticeShape(2)*latticeShape(3), AipsError); IPosition expectedPos(latticeShape-1); AlwaysAssert(iter.endPosition() == expectedPos, AipsError); expectedPos(0) = 0; expectedPos(1) = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedResult(0,0) = 2; for (; !iter.atStart(); --iter){ AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()) == True, AipsError); iter.woCursor() = 3; } clock.show(); nstep = iter.nsteps(); AlwaysAssert(nstep == 2*(latticeShape(2)*latticeShape(3)), AipsError); expectedPos = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedPos(0) = latticeShape(0) - 1; expectedPos(1) = latticeShape(1) - 1; AlwaysAssert(iter.endPosition() == expectedPos, AipsError); } void testCubeRWIter (Lattice& lattice, Bool useRef) { cout << " Testing using a Cube cursor" << endl; Int nstep; const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(3, latticeShape(0), latticeShape(1), latticeShape(2)); LatticeStepper step(latticeShape, cursorShape); LatticeIterator iter(lattice, step, useRef); Cube expectedResult(latticeShape(0), latticeShape(1), latticeShape(2)); expectedResult = 3; AlwaysAssert(allEQ(expectedResult, iter.cubeCursor()) == True, AipsError); AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()) == True, AipsError); try { Vector temp(iter.vectorCursor()); throw(AipsError("tHDF5Iterator - " "vectorCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("one non-degenerate")) { throw (AipsError (x.what())); } } try { Matrix temp(iter.matrixCursor()); throw(AipsError("tHDF5Iterator - " "matrixCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("two non-degenerate")) { throw (AipsError (x.what())); } } Timer clock; for (iter.reset(); !iter.atEnd(); iter++){ AlwaysAssert(allEQ(expectedResult, iter.cubeCursor()) == True, AipsError); iter.rwCubeCursor()(0,0,0) = 4; } nstep = iter.nsteps(); AlwaysAssert(nstep == latticeShape(3), AipsError); IPosition expectedPos(latticeShape-1); AlwaysAssert(iter.endPosition() == expectedPos, AipsError); expectedPos(0) = 0; expectedPos(1) = 0; expectedPos(2) = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedResult(0,0,0) = 4; for (; !iter.atStart(); iter--){ AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()) == True, AipsError); iter.woCursor() = 5; } clock.show(); nstep = iter.nsteps(); AlwaysAssert(nstep == 2*(latticeShape(3)), AipsError); expectedPos = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedPos(0) = latticeShape(0) - 1; expectedPos(1) = latticeShape(1) - 1; expectedPos(2) = latticeShape(2) - 1; AlwaysAssert(iter.endPosition() == expectedPos, AipsError); } void testArrayRWIter (Lattice& lattice, Bool useRef) { cout << " Testing using an Array (4-D) cursor" << endl; Int nstep; const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(latticeShape); LatticeIterator iter(lattice, cursorShape, useRef); Array expectedResult(latticeShape); expectedResult = 5; AlwaysAssert(allEQ(expectedResult, iter.cursor()) == True, AipsError); try { Vector temp(iter.vectorCursor()); throw(AipsError("tHDF5Iterator - " "vectorCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("one non-degenerate")) { throw (AipsError (x.what())); } } try { Matrix temp(iter.matrixCursor()); throw(AipsError("tHDF5Iterator - " "matrixCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("two non-degenerate")) { throw (AipsError (x.what())); } } try { Cube temp(iter.cubeCursor()); throw(AipsError("tHDF5Iterator - " "cubeCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("three non-degenerate")) { throw (AipsError (x.what())); } } Timer clock; for (iter.reset(); !iter.atEnd(); ++iter){ AlwaysAssert(allEQ(expectedResult, iter.cursor()) == True, AipsError); iter.rwCursor()(IPosition(4,0)) = 6; } nstep = iter.nsteps(); AlwaysAssert(nstep == 1, AipsError); IPosition expectedPos(latticeShape-1); AlwaysAssert(iter.endPosition() == expectedPos, AipsError); expectedPos = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedResult(IPosition(4,0)) = 6; for (; !iter.atStart(); --iter){ AlwaysAssert(allEQ(expectedResult, iter.cursor()) == True, AipsError); iter.woCursor() = 7; } clock.show(); nstep = iter.nsteps(); AlwaysAssert(nstep == 2, AipsError); expectedPos = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedPos = latticeShape - 1; AlwaysAssert(iter.endPosition() == expectedPos, AipsError); } void testCopyAssignRWIter (Lattice& lattice, Bool useRef) { cout << " Testing the copy constructor and assignment operator" << endl; const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(1,latticeShape(0)); LatticeIterator iter(lattice, LatticeStepper(latticeShape, cursorShape), useRef); iter++; Vector expectedResult(latticeShape(0)); expectedResult = 7; AlwaysAssert(allEQ(expectedResult,iter.vectorCursor()) == True, AipsError); LatticeIterator iterCopy(iter.copy()); AlwaysAssert(allEQ(expectedResult, iterCopy.vectorCursor()) == True, AipsError); iter++; iter.woCursor() = 2; expectedResult = 2; AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); expectedResult = 7; AlwaysAssert(allEQ(expectedResult, iterCopy.vectorCursor()) == True, AipsError); iterCopy--; iterCopy.woCursor() = 0; expectedResult = 2; AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); expectedResult = 0; AlwaysAssert(allEQ(expectedResult, iterCopy.vectorCursor()) == True, AipsError); iterCopy = iter.copy(); AlwaysAssert(allEQ(iter.vectorCursor(), iterCopy.vectorCursor()) == True, AipsError); expectedResult = 2; AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); iter++; expectedResult = 7; AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); expectedResult = 2; AlwaysAssert(allEQ(expectedResult, iterCopy.vectorCursor()) == True, AipsError); --iterCopy; iterCopy--; expectedResult = 0; AlwaysAssert(allEQ(expectedResult, iterCopy.vectorCursor()) == True, AipsError); } void testNonCongruentRWIter (Lattice& lattice, Bool useRef) { cout << " Testing using a non-congruent cursor" << endl; const IPosition latticeShape(lattice.shape()); { Array arr; lattice.getSlice(arr, IPosition(latticeShape.nelements(), 0), latticeShape, IPosition(latticeShape.nelements(), 1)); indgen(arr); lattice.putSlice(arr, IPosition(latticeShape.nelements(), 0)); } IPosition cursorShape(2,9); LatticeStepper step(latticeShape, cursorShape); LatticeIterator iter(lattice, step, useRef); Matrix expectedResult1(cursorShape); Vector oneRow(cursorShape(0)); indgen(oneRow); uInt i; for (i = 0; i < uInt(cursorShape(1)); i++) { expectedResult1.column(i) = oneRow; oneRow += Int(latticeShape(0)); } AlwaysAssert(allEQ(expectedResult1, iter.cursor().nonDegenerate()), AipsError); iter.woCursor() = (-1 * iter.cursor() - 1); iter++; Matrix expectedResult2(cursorShape); indgen(oneRow, Int(cursorShape(0))); for (i = 0; i < uInt(cursorShape(1)); i++) { oneRow(7) = 0; oneRow(8) = 0; expectedResult2.column(i) = oneRow; oneRow += Int(latticeShape(0)); } AlwaysAssert(allEQ(expectedResult2, iter.cursor().nonDegenerate()), AipsError); iter.woCursor() = (-1 * iter.cursor() - 1); iter++; Matrix expectedResult3(cursorShape); expectedResult3 = 0; indgen(oneRow, Int(cursorShape(0)*latticeShape(0))); for (i = 0; i < 3; i++) { expectedResult3.column(i) = oneRow; oneRow += Int(latticeShape(0)); } AlwaysAssert(allEQ(expectedResult3, iter.cursor().nonDegenerate()), AipsError); iter.woCursor() = (-1 * iter.cursor() - 1); iter++; Matrix expectedResult4(cursorShape); expectedResult4 = 0; indgen(oneRow, Int(cursorShape(0)*(latticeShape(0)+1))); for (i = 0; i < 3; i++) { oneRow(7) = 0; oneRow(8) = 0; expectedResult4.column(i) = oneRow; oneRow += Int(latticeShape(0)); } AlwaysAssert(allEQ(expectedResult4, iter.cursor().nonDegenerate()), AipsError); iter.woCursor() = (-1 * iter.cursor() - 1); iter--; iter++; iter.rwMatrixCursor() += expectedResult4; { Array m(iter.rwMatrixCursor()(IPosition(2,0),IPosition(2,6,2))); m += 1; AlwaysAssert(allEQ(iter.cursor(), 0), AipsError); } iter--; iter.rwMatrixCursor() += expectedResult3; { Array m(iter.rwMatrixCursor()(IPosition(2,0),IPosition(2,8,2))); m += 1; AlwaysAssert(allEQ(iter.cursor(), 0), AipsError); } iter--; iter.rwMatrixCursor() += expectedResult2; { Array m(iter.rwMatrixCursor()(IPosition(2,0),IPosition(2,6,8))); m += 1; AlwaysAssert(allEQ(iter.cursor(), 0), AipsError); } iter--; iter.rwMatrixCursor() += expectedResult1; iter.rwCursor() += 1; AlwaysAssert(allEQ(iter.cursor(), 0), AipsError); { Array arr; lattice.getSlice(arr, IPosition(latticeShape.nelements(), 0), latticeShape, IPosition(latticeShape.nelements(), 1)); indgen(arr); lattice.putSlice(arr, IPosition(latticeShape.nelements(), 0)); } cursorShape = 5; step.setCursorShape(cursorShape); step.subSection(IPosition(4, 3,0,0,0), latticeShape-1, IPosition(4, 2,2,1,1)); LatticeIterator subIter(lattice, step, useRef); oneRow.resize(5); Matrix expectedResulta(5,5); expectedResulta = 0; indgen(oneRow, 3, 2); for (i = 0; i < 5; i++) { expectedResulta.column(i) = oneRow; oneRow += 32; } AlwaysAssert(allEQ(expectedResulta, subIter.cursor().nonDegenerate()), AipsError); subIter.woCursor() = (-1 * subIter.cursor() - 1); subIter++; Matrix expectedResultb(5,5); expectedResultb = 0; indgen(oneRow, 13, 2); for (i = 0; i < 5; i++) { oneRow(2) = 0; oneRow(3) = 0; oneRow(4) = 0; expectedResultb.column(i) = oneRow; oneRow += 32; } AlwaysAssert(allEQ(expectedResultb, subIter.cursor().nonDegenerate()), AipsError); subIter.woCursor() = (-1 * subIter.cursor() - 1); subIter++; Matrix expectedResultc(5,5); expectedResultc = 0; indgen(oneRow, 163, 2); for (i = 0; i < 1; i++) { expectedResultc.column(i) = oneRow; oneRow += 32; } AlwaysAssert(allEQ(expectedResultc, subIter.cursor().nonDegenerate()), AipsError); subIter.woCursor() = (-1 * subIter.cursor() - 1); subIter++; Matrix expectedResultd(5,5); expectedResultd = 0; indgen(oneRow, 173, 2); for (i = 0; i < 1; i++) { oneRow(2) = 0; oneRow(3) = 0; oneRow(4) = 0; expectedResultd.column(i) = oneRow; oneRow += 32; } AlwaysAssert(allEQ(expectedResultd, subIter.cursor().nonDegenerate()), AipsError); subIter.woCursor() = (-1 * subIter.cursor() - 1); subIter--; subIter++; Array arr; lattice.getSlice(arr, IPosition(latticeShape.nelements(), 0), IPosition(4,16,12,1,1), IPosition(latticeShape.nelements(), 1)); AlwaysAssert(arr(IPosition(4,0)) == 0, AipsError); AlwaysAssert(arr(IPosition(4,1,0,0,0)) == 1, AipsError); AlwaysAssert(arr(IPosition(4,2,0,0,0)) == 2, AipsError); AlwaysAssert(arr(IPosition(4,3,0,0,0)) == -4, AipsError); AlwaysAssert(arr(IPosition(4,13,0,0,0)) == -14, AipsError); AlwaysAssert(arr(IPosition(4,14,0,0,0)) == 14, AipsError); AlwaysAssert(arr(IPosition(4,2,10,0,0)) == 162, AipsError); AlwaysAssert(arr(IPosition(4,3,10,0,0)) == -164, AipsError); AlwaysAssert(arr(IPosition(4,3,11,0,0)) == 179, AipsError); AlwaysAssert(arr(IPosition(4,15,10,0,0)) == -176, AipsError); AlwaysAssert(arr(IPosition(4,15,11,0,0)) == 191, AipsError); } void testAdd (Lattice& lat1, Lattice& lat2, Bool useRef) { { HDF5Lattice* pa1 = dynamic_cast*> (&lat1); if (pa1) pa1->clearCache(); HDF5Lattice* pa2 = dynamic_cast*> (&lat2); if (pa2) pa2->clearCache(); Timer timer; LatticeIterator lat1Iter (lat1, useRef); // Create dummy lat2Iter to setup cache correctly. // It may not be necessary, because the Table getSlice function // will setup the cache on its first access. RO_LatticeIterator lat2Iter (lat2, lat1.niceCursorShape(), useRef); Array lat2Buffer; while (! lat1Iter.atEnd()) { // Do separate getSlice to use reference semantics if // lat2 is an ArrayLattice. // Note that it requires lat2 to be non-const. lat2.getSlice (lat2Buffer, lat1Iter.position(), lat1Iter.cursorShape()); lat1Iter.rwCursor() += lat2Buffer; lat1Iter++; } timer.show (" iter-get "); ///if (pa1) pa1->showCacheStatistics (cout); ///if (pa2) pa2->showCacheStatistics (cout); } { Timer timer; // This iterator uses the TileStepper. LatticeIterator lat1Iter (lat1, useRef); // Use tile shape of lat1, because they have to be iterated // in the same way. The cursor has to be resized if needed. RO_LatticeIterator lat2Iter (lat2, LatticeStepper (lat1.shape(), lat1.niceCursorShape(), LatticeStepper::RESIZE), useRef); while (! lat1Iter.atEnd()) { lat1Iter.rwCursor() += lat2Iter.cursor(); lat1Iter++; lat2Iter++; } timer.show (" iter-iter"); } { Timer timer; LatticeIterator lat1Iter (lat1, useRef); Array lat2Buffer; while (! lat1Iter.atEnd()) { // Do separate getSlice to use reference semantics if // lat2 is an ArrayLattice. // Note that it requires lat2 to be non-const. lat2.getSlice (lat2Buffer, lat1Iter.position(), lat1Iter.cursorShape()); lat1Iter.rwCursor() += lat2Buffer; lat1Iter++; } timer.show (" iter-get "); } } int main (int argc, const char *argv[]) { // Exit with untested if no HDF5 support. if (! HDF5Object::hasHDF5Support()) { return 3; } try { { cout << "Creating a HDF5Lattice on disk" << endl; const TiledShape latticeShape(IPosition(4, 16, 12, 4, 32), IPosition(4, 16, 12, 2, 1)); HDF5Lattice pagedArr(latticeShape, "tHDF5Iterator_tmp.dat"); Array arr(latticeShape.shape()); indgen(arr); pagedArr.putSlice(arr, IPosition(latticeShape.shape().nelements(), 0)); } //++++++++++++++++++++ Test HDF5ArrIter ++++++++++++++++++++ cout << " Testing the RO iterator" << endl; // Check the Iterator with a Vector cursor. { const HDF5Lattice pagedArr("tHDF5Iterator_tmp.dat"); testVectorROIter (pagedArr, False); testVectorROIter (pagedArr, True); } // Check the Iterator with a Matrix cursor. { const HDF5Lattice pagedArr("tHDF5Iterator_tmp.dat"); testMatrixROIter (pagedArr, False); testMatrixROIter (pagedArr, True); } // Check the Iterator with a Cube cursor. { const HDF5Lattice pagedArr("tHDF5Iterator_tmp.dat"); testCubeROIter (pagedArr, False); testCubeROIter (pagedArr, True); } // Check the Iterator with an Array cursor. { const HDF5Lattice pagedArr("tHDF5Iterator_tmp.dat"); testArrayROIter (pagedArr, False); testArrayROIter (pagedArr, True); } // Check the Iterator with an 8 element element cursor. { const HDF5Lattice pagedArr("tHDF5Iterator_tmp.dat"); test8ElemROIter (pagedArr, False); test8ElemROIter (pagedArr, True); } // Check the Iterator with a tile cursor. { const HDF5Lattice pagedArr("tHDF5Iterator_tmp.dat"); testTileROIter (pagedArr, False); testTileROIter (pagedArr, True); } // Check the Iterator with a tiled line cursor. { const HDF5Lattice pagedArr("tHDF5Iterator_tmp.dat"); testTiledLineROIter (pagedArr, False); testTiledLineROIter (pagedArr, True); } // Check the copy constructor and assignment operator { const HDF5Lattice pagedArr("tHDF5Iterator_tmp.dat"); testCopyAssignROIter (pagedArr, False); testCopyAssignROIter (pagedArr, True); } // Test the non-congruent cursor handling { const HDF5Lattice pagedArr("tHDF5Iterator_tmp.dat"); testNonCongruentROIter (pagedArr, False); testNonCongruentROIter (pagedArr, True); } cout << " Testing the RW iterator" << endl; // Check the Iterator with a Vector cursor. { HDF5Lattice pagedArr("tHDF5Iterator_tmp.dat"); Array savarr = pagedArr.get(); testVectorRWIter (pagedArr, False); pagedArr.put (savarr); testVectorRWIter (pagedArr, True); } // Check the Iterator with a Matrix cursor. { HDF5Lattice pagedArr("tHDF5Iterator_tmp.dat"); Array savarr = pagedArr.get(); testMatrixRWIter (pagedArr, False); pagedArr.put (savarr); testMatrixRWIter (pagedArr, True); } // Check the Iterator with a Cube cursor. { HDF5Lattice pagedArr("tHDF5Iterator_tmp.dat"); Array savarr = pagedArr.get(); testCubeRWIter (pagedArr, False); pagedArr.put (savarr); testCubeRWIter (pagedArr, True); } // Check the Iterator with an Array cursor. { HDF5Lattice pagedArr("tHDF5Iterator_tmp.dat"); Array savarr = pagedArr.get(); testArrayRWIter (pagedArr, False); pagedArr.put (savarr); testArrayRWIter (pagedArr, True); } // Check the copy constructor and assignment operator { HDF5Lattice pagedArr("tHDF5Iterator_tmp.dat"); Array savarr = pagedArr.get(); testCopyAssignRWIter (pagedArr, False); pagedArr.put (savarr); testCopyAssignRWIter (pagedArr, True); } // Test the non-congruent cursor handling { HDF5Lattice pagedArr("tHDF5Iterator_tmp.dat"); Array savarr = pagedArr.get(); testNonCongruentRWIter (pagedArr, False); pagedArr.put (savarr); testNonCongruentRWIter (pagedArr, True); } } catch (std::exception& x) { cerr << "Caught exception: " << x.what() << endl; cout << "FAIL" << endl; return 1; } try { cout << "Creating an ArrayLattice" << endl; const IPosition latticeShape(4, 16, 12, 2, 32); ArrayLattice refLattice(latticeShape); { Array arr(latticeShape); indgen(arr); refLattice.putSlice(arr, IPosition(latticeShape.nelements(), 0)); } //++++++++++++++++++++ Test ArrLatticeIter ++++++++++++++++++++ // Check the Iterator with a Vector cursor. cout << " Testing the RO iterator" << endl; { const ArrayLattice arrLattice(refLattice); testVectorROIter (arrLattice, False); testVectorROIter (arrLattice, True); } // Check the Iterator with a Matrix cursor. { const ArrayLattice arrLattice(refLattice); testMatrixROIter (arrLattice, False); testMatrixROIter (arrLattice, True); } // Check the Iterator with a Cube cursor. { const ArrayLattice arrLattice(refLattice); testCubeROIter (arrLattice, False); testCubeROIter (arrLattice, True); } // Check the Iterator with an Array cursor. { const ArrayLattice arrLattice(refLattice); testArrayROIter (arrLattice, False); testArrayROIter (arrLattice, True); } // Check the Iterator with an 8 element element cursor. { const ArrayLattice arrLattice(refLattice); test8ElemROIter (arrLattice, False); test8ElemROIter (arrLattice, True); } // Check the Iterator with a tile cursor. { const ArrayLattice arrLattice(refLattice); testTileROIter (arrLattice, False); testTileROIter (arrLattice, True); } // Check the Iterator with a tiled line cursor. { const ArrayLattice arrLattice(refLattice); testTiledLineROIter (arrLattice, False); testTiledLineROIter (arrLattice, True); } // Check the copy constructor and assignment operator { const ArrayLattice arrLattice(refLattice); testCopyAssignROIter (arrLattice, False); testCopyAssignROIter (arrLattice, True); } // Test the non-congruent cursor handling { const ArrayLattice arrLattice(refLattice); testNonCongruentROIter (arrLattice, False); testNonCongruentROIter (arrLattice, True); } cout << " Testing the RW iterator" << endl; // Check the Iterator with a Vector cursor. { ArrayLattice arrLattice(refLattice); Array savarr = arrLattice.get(); testVectorRWIter (arrLattice, False); arrLattice.put (savarr); testVectorRWIter (arrLattice, True); } // Check the Iterator with a Matrix cursor. { ArrayLattice arrLattice(refLattice); Array savarr = arrLattice.get(); testMatrixRWIter (arrLattice, False); arrLattice.put (savarr); testMatrixRWIter (arrLattice, True); } // Check the Iterator with a Cube cursor. { ArrayLattice arrLattice(refLattice); Array savarr = arrLattice.get(); testCubeRWIter (arrLattice, False); arrLattice.put (savarr); testCubeRWIter (arrLattice, True); } // Check the Iterator with an Array cursor. { ArrayLattice arrLattice(refLattice); Array savarr = arrLattice.get(); testArrayRWIter (arrLattice, False); arrLattice.put (savarr); testArrayRWIter (arrLattice, True); } // Check the copy constructor and assignment operator { ArrayLattice arrLattice(refLattice); Array savarr = arrLattice.get(); testCopyAssignRWIter (arrLattice, False); arrLattice.put (savarr); testCopyAssignRWIter (arrLattice, True); } // Test the non-congruent cursor handling { ArrayLattice arrLattice(refLattice); Array savarr = arrLattice.get(); testNonCongruentRWIter (arrLattice, False); arrLattice.put (savarr); testNonCongruentRWIter (arrLattice, True); } // Test some performance aspects. { Input inp(1); inp.version(" "); inp.create("nx", "512", "Number of pixels along the x-axis", "int"); inp.create("ny", "512", "Number of pixels along the y-axis", "int"); inp.readArguments(argc, argv); const uInt nx=inp.getInt("nx"); const uInt ny=inp.getInt("ny"); IPosition shape(2,nx,ny); TiledShape tshape(shape, IPosition(2,nx,1)); HDF5Lattice pagedArr1(tshape, "tHDF5Iterator_tmp.dat1"); HDF5Lattice pagedArr2(tshape, "tHDF5Iterator_tmp.dat2"); ArrayLattice latArr1(shape); ArrayLattice latArr2(shape); Array arr(latArr1.shape()); indgen(arr); pagedArr1.put (arr); pagedArr2.put (arr); latArr1.put (arr); latArr2.put (arr); cout << "Shape " << shape << endl; cout << "paged+=paged useRef=False" << endl; testAdd (pagedArr1, pagedArr2, False); AlwaysAssert (allEQ(pagedArr1.get(), 4*arr), AipsError); cout << "paged+=paged useRef=True" << endl; testAdd (pagedArr1, pagedArr2, True); AlwaysAssert (allEQ(pagedArr1.get(), 7*arr), AipsError); cout << "array+=array useRef=False" << endl; testAdd (latArr1, latArr2, False); AlwaysAssert (allEQ(latArr1.get(), 4*arr), AipsError); cout << "array+=array useRef=True" << endl; testAdd (latArr1, latArr2, True); AlwaysAssert (allEQ(latArr1.get(), 7*arr), AipsError); cout << "paged+=array useRef=False" << endl; testAdd (pagedArr1, latArr2, False); AlwaysAssert (allEQ(pagedArr1.get(), 10*arr), AipsError); cout << "paged+=array useRef=True" << endl; testAdd (pagedArr1, latArr2, True); AlwaysAssert (allEQ(pagedArr1.get(), 13*arr), AipsError); cout << "lat+=paged useRef=False" << endl; testAdd (latArr1, pagedArr2, False); AlwaysAssert (allEQ(latArr1.get(), 10*arr), AipsError); cout << "lat+=paged useRef=True" << endl; testAdd (latArr1, pagedArr2, True); AlwaysAssert (allEQ(latArr1.get(), 13*arr), AipsError); } } catch (std::exception& x) { cerr << "Caught exception: " << x.what() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/lattices/Lattices/test/tHDF5Lattice.cc000066400000000000000000000172111476623553700223540ustar00rootroot00000000000000//# tHDF5Array.cc: tests the HDF5Array class //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace casacore; int main() { // Exit with untested if no HDF5 support. if (! HDF5Object::hasHDF5Support()) { return 3; } try { { HDF5Lattice pa(IPosition(2,12), "tHDF5Lattice_tmp.dat"); AlwaysAssert(pa.arrayName()=="array", AipsError); pa.set(10.0); Array arr; pa.getSlice(arr, IPosition(2,0), IPosition(2,12), IPosition(2,1)); AlwaysAssert(allNear(arr, 10.0f, 1E-5), AipsError); indgen(arr); Array arr1(arr(IPosition(2,0), IPosition(2,0,11), IPosition(2,1,2))); pa.putSlice(arr1, IPosition(2,0), IPosition(2,1,2)); Vector vec(10); indgen(vec); pa.putSlice(vec(IPosition(1,0), IPosition(1,9), IPosition(1,2)), IPosition(2,1,1), IPosition(2,2,1)); } { HDF5Lattice pa("tHDF5Lattice_tmp.dat"); AlwaysAssert(pa.shape().isEqual(IPosition(2,12)), AipsError); Array arr; Slicer sl(IPosition(2,0), IPosition(2,12)); pa.getSlice(arr, sl); AlwaysAssert(near(pa(IPosition(2,0)), 0.0f), AipsError); AlwaysAssert(near(pa(IPosition(2,0,1)), 10.0f), AipsError); AlwaysAssert(near(pa.getAt(IPosition(2,0,2)), 24.0f), AipsError); AlwaysAssert(near(pa.getAt(IPosition(2,1,1)), 0.0f), AipsError); AlwaysAssert(near(pa(IPosition(2,2,1)), 10.0f), AipsError); AlwaysAssert(near(pa(IPosition(2,3,1)), 2.0f), AipsError); pa.putAt (99.0, IPosition(2,11)); pa.putAt (98.0f, IPosition(2,11,10)); AlwaysAssert(pa.name(True) == "tHDF5Lattice_tmp.dat", AipsError); AlwaysAssert(pa.isPersistent(), AipsError); AlwaysAssert(pa.isPaged(), AipsError); AlwaysAssert(pa.isWritable(), AipsError); } { HDF5Lattice scratch(IPosition(3,9)); LatticeIterator li(scratch, IPosition(3,1,1,9)); Int i = 0; for (li.reset(); !li.atEnd(); li++, i++) { li.woCursor() = i; } COWPtr > ptrM; scratch.getSlice(ptrM, IPosition(3,0), IPosition(3,9,9,1), IPosition(3,1), True); AlwaysAssert(ptrM->shape().isEqual(IPosition(2,9)), AipsError); Array expectedResult(IPosition(2,9)); indgen(expectedResult); AlwaysAssert(allEQ(*ptrM, expectedResult), AipsError); ptrM.rwRef() = 0; AlwaysAssert(allEQ(*ptrM, 0), AipsError); Slicer sl(IPosition(3,0,0,5), IPosition(3,9,9,1), IPosition(3,1)); scratch.getSlice(ptrM, sl, True); AlwaysAssert(allEQ(*ptrM, expectedResult), AipsError); } { const IPosition latticeShape(4, 128, 128, 4, 32); HDF5Lattice pa(latticeShape, "tHDF5Lattice_tmp_1.dat"); AlwaysAssert(pa.tileShape().isEqual(pa.niceCursorShape()), AipsError); Array arr(IPosition(4,1,1,4,32)); Slicer sl(IPosition(4,0), IPosition(4,1,1,4,32)); pa.clearCache(); pa.setCacheSizeFromPath(arr.shape(), IPosition(4,0), pa.tileShape() - 1, IPosition(4,0,1,2,3)); pa.getSlice(arr, sl); pa.showCacheStatistics(cout); HDF5Lattice pa1(TiledShape(latticeShape,IPosition(4,16,16,4,32)), "tHDF5Lattice_tmp.dat"); AlwaysAssert(pa1.tileShape().isEqual(IPosition(4,16,16,4,32)), AipsError); pa1.clearCache(); pa1.setCacheSizeFromPath(arr.shape(), IPosition(4,0), IPosition(4,16,16,4,32) - 1, IPosition(4,0,1,2,3)); pa1.getSlice(arr, sl); pa1.showCacheStatistics(cout); pa = pa1; AlwaysAssert(pa.tileShape().isEqual(IPosition(4,16,16,4,32)), AipsError); arr = 9.0f; pa.putSlice(arr, IPosition(4,0)); arr = 0.0f; pa1.getSlice(arr, sl); AlwaysAssert(allNear(arr, 9.0f, 1E-5), AipsError); IPosition lat2Shape = IPosition(4,16); HDF5Lattice pa2(lat2Shape, pa1.file(), "array2"); arr.resize(lat2Shape); indgen(arr); pa2.putSlice(arr, IPosition(4,0)); IPosition lat3Shape = IPosition(2,16); HDF5Lattice pa3(TiledShape(lat3Shape,lat3Shape), pa1.file(), "IntHDF5Lattice"); Array iarr(lat3Shape); indgen(iarr); pa3.putSlice(iarr, IPosition(2,0)); } { HDF5Lattice pa1("tHDF5Lattice_tmp.dat"); AlwaysAssert(pa1.shape().isEqual(IPosition(4,128,128,4,32)), AipsError); HDF5Lattice pa2(pa1.file(), "array2"); AlwaysAssert(pa2.shape().isEqual(IPosition(4,16)), AipsError); HDF5Lattice pa3(pa1.file(), "IntHDF5Lattice"); AlwaysAssert(pa3.shape().isEqual(IPosition(2,16)), AipsError); Array iarr(pa3.shape()), expected(pa3.shape()); pa3.setMaximumCacheSize(256*256); indgen(expected); pa3.getSlice(iarr, IPosition(2,0), IPosition(2,16), IPosition(2,1)); AlwaysAssert(allEQ(iarr, expected), AipsError); { HDF5Lattice pa4(pa3); AlwaysAssert(pa4.shape().isEqual(IPosition(2,16)), AipsError); iarr = 0; pa4.getSlice(iarr, IPosition(2,0), IPosition(2,16), IPosition(2,1)); AlwaysAssert(allEQ(iarr, expected), AipsError); AlwaysAssert(pa4.ok() == True, AipsError); } } { const IPosition latticeShape(4, 4, 16, 15, 8); HDF5Lattice pa(TiledShape(latticeShape, IPosition(4,2,8,8,3)), "tHDF5Lattice_tmp_1.dat", "data", "group1"); AlwaysAssertExit(pa.arrayName()=="data"); Array arr(latticeShape); indgen(arr); pa.put (arr); AlwaysAssertExit (allEQ(pa.get(), arr)); pa += pa; AlwaysAssertExit (allEQ(pa.get(), float(2)*arr)); } { HDF5Lattice pa("tHDF5Lattice_tmp_1.dat", "data", "group1"); AlwaysAssertExit(pa.arrayName()=="data"); Array arr(pa.shape()); indgen(arr); AlwaysAssertExit (allEQ(pa.get(), float(2)*arr)); } } catch (std::exception& x) { cerr << x.what() << endl; return 1; } cout<< "OK"<< endl; return 0; } casacore-3.7.1/lattices/Lattices/test/tLatticeCache.cc000066400000000000000000000162131476623553700226720ustar00rootroot00000000000000//# tLattice.cc: test the Lattice class //# Copyright (C) 1994,1995,1997,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void a() { Int arraySize=2048; cout<<"Array Size? "; cin>>arraySize; IPosition map2shape(2, arraySize, arraySize); Int tileSize=16; Int cacheSize=351*tileSize*tileSize; Int trials=1000; Float tileOverlap=0.5; Int imageTileSize=16; cout<<"Image Tile Size? "; cin>>imageTileSize; // cout<<"Tile Overlap? "; cin>>tileOverlap; // cout<<"Cache Size? "; cin>>cacheSize; cout<<"Trials? "; cin>>trials; IPosition tileShape(2,tileSize,tileSize); IPosition imageTileShape(2,imageTileSize,imageTileSize); Vector tileOverlapVec(2); tileOverlapVec=tileOverlap; PagedArray pi2(TiledShape(map2shape, imageTileShape)); pi2.setCacheSizeInTiles(1); LatticeCache itc(pi2, cacheSize, tileShape, tileOverlapVec, (tileOverlap>0.)); MLCG rng(835, 05401); DiscreteUniform randomPos(&rng, tileSize, arraySize-tileSize-1); Uniform randomChoice(&rng, 0.0, 1.0); Timer timer; timer.mark(); pi2.set(0.0); cout<<"Time to initialize array = "<<1000.0*timer.real()<<" ms"<& myTile=itc.tile(tilePos,myPos,False); cout<<"Filling tile at "< "<>missFraction; for (Int trial=0;trial& myTile=itc.tile(tilePos,myPos,False); myTile(myPos-tilePos)+=1.0; } } itc.flush(); pi2.showCacheStatistics(cout); itc.showCacheStatistics(cout); cout<<"Time per tile = "<<1000.0*timer.real()/trials<<" ms"<>arraySize; Int nChannels=128; Int nChanTile=128; Int nPol=1; Int nPolTile=1; Int tileSize=16; Int cacheSize=351*tileSize*tileSize*nChanTile*nPolTile; Int trials=100; Int imageTileSize=16; cout<<"Image Tile Size? "; cin>>imageTileSize; Float tileOverlap=0.5; // cout<<"Tile Size? "; cin>>tileSize; // cout<<"Tile Overlap? "; cin>>tileOverlap; // cout<<"Cache Size? "; cin>>cacheSize; cout<<"Trials? "; cin>>trials; Vector tileOverlapVec(4); tileOverlapVec=0.0; tileOverlapVec(0)=tileOverlap; tileOverlapVec(1)=tileOverlap; IPosition tileShape(4,tileSize,tileSize,nPolTile,nChanTile); IPosition map4shape(4, arraySize, arraySize, nPol, nChannels); IPosition imageTileShape(4,imageTileSize,imageTileSize,1,imageTileSize); PagedArray pi4(TiledShape(map4shape, imageTileShape)); pi4.setCacheSizeInTiles(0); LatticeCache itc(pi4, cacheSize, tileShape, tileOverlapVec, (tileOverlap>0.0)); MLCG rng(835, 05401); DiscreteUniform randomPos(&rng, tileSize, arraySize-tileSize-1); DiscreteUniform randomChan(&rng, 0, 31); DiscreteUniform randomPol(&rng, 0, 3); Uniform randomChoice(&rng, 0.0, 1.0); Timer timer; timer.mark(); pi4.set(0.0); cout<<"Time to initialize array = "<<1000.0*timer.real()<<" ms"<>missFraction; for (Int trial=0;trial& myTile=itc.tile(tilePos,myPos,False); IPosition offPos=myPos-tilePos; myTile(offPos)+=1.0; } itc.flush(); cout<<"Time per tile = "<<1000.0*timer.real()/trials<<" ms"<>>"<>type; switch(type) { case 0: a(); break; case 1: b(); break; default: a(); b(); } cout<<"<<<"< #include #include #include #include #include #include #include #include #include #include #include #include #include #include void check (uInt axis, MaskedLattice& ml, MaskedLattice& ml1, MaskedLattice& ml2); void check2 (MaskedLattice& ml, MaskedLattice& ml1, MaskedLattice& ml2); void check3 (const Slicer& sl, MaskedLattice& ml1, MaskedLattice& ml2); void check4 (const Slicer& sl, MaskedLattice& ml1, Array& ml2); void check5 (const Slicer& sl, MaskedLattice& ml1, Array& ml2); void check6 (uInt axis, Lattice& ml, Lattice& ml1, Lattice& ml2); void check7 (const Slicer& sl, LatticeConcat& lc, Float val, Bool valMask); int main() { try { // Make some ArrayLattices IPosition shape(2,64,128); Array a1(shape); Array a2(shape); Int i, j; for (i=0; i l1(a1); ArrayLattice l2(a2); ArrayLattice l3(shape); l3.set(1.0); // Make MaskedLattices with no mask SubLattice ml1(l1, True); SubLattice ml2(l2, True); SubLattice ml3(l3, True); // Make some MaskedLattices and give them a mask SubLattice im1(l1,True); SubLattice im2(l2, True); SubLattice im3(l3, True); // ArrayLattice mask1(shape); mask1.set(True); ArrayLattice mask2(shape); mask2.set(False); ArrayLattice mask3(shape); mask3.set(True); im1.setPixelMask(mask1,False); im2.setPixelMask(mask2,False); im3.setPixelMask(mask3,False); // { cout << "tempClose/reopen/resync/flush" << endl; LatticeConcat lc(0, True); lc.setLattice(ml1); lc.setLattice(im1); lc.reopen(); lc.tempClose(); lc.tempClose(0); lc.tempClose(1); lc.reopen(0); lc.reopen(1); lc.tempClose(); // lc.resync(); lc.flush(); // IPosition outShape = lc.shape(); AlwaysAssert(outShape.nelements()==2, AipsError); AlwaysAssert(outShape(0)==shape(0)+shape(0), AipsError); AlwaysAssert(outShape(1)==shape(1), AipsError); AlwaysAssert(lc.isMasked()==True, AipsError); AlwaysAssert(lc.hasPixelMask()==True, AipsError); AlwaysAssert(lc.pixelMask().isWritable()==False, AipsError); AlwaysAssert(lc.pixelMask().shape()==outShape, AipsError); AlwaysAssert(lc.axis()==0, AipsError); AlwaysAssert(lc.nlattices()==2, AipsError); Lattice& pixelMask = lc.pixelMask(); check6(0, pixelMask, mask1, mask1); } // { cout << "partly pixelMask" << endl; LatticeConcat lc(0, True); lc.setLattice(im2); lc.setLattice(ml1); // IPosition outShape = lc.shape(); AlwaysAssert(outShape.nelements()==2, AipsError); AlwaysAssert(outShape(0)==shape(0)+shape(0), AipsError); AlwaysAssert(outShape(1)==shape(1), AipsError); AlwaysAssert(lc.isMasked()==True, AipsError); AlwaysAssert(lc.hasPixelMask()==True, AipsError); AlwaysAssert(lc.pixelMask().isWritable()==False, AipsError); AlwaysAssert(lc.pixelMask().shape()==outShape, AipsError); AlwaysAssert(lc.axis()==0, AipsError); AlwaysAssert(lc.nlattices()==2, AipsError); Lattice& pixelMask = lc.pixelMask(); check6(0, pixelMask, mask2, mask1); } // { cout << "Axis 0, ArrayLattices, no masks" << endl; // Concatenate along axis 0 LatticeConcat lc(0, False); lc.setLattice(ml1); lc.setLattice(ml2); // Find output shape IPosition outShape = lc.shape(); AlwaysAssert(outShape.nelements()==2, AipsError); AlwaysAssert(outShape(0)==shape(0)+shape(0), AipsError); AlwaysAssert(outShape(1)==shape(1), AipsError); AlwaysAssert(lc.isMasked()==False, AipsError); AlwaysAssert(lc.hasPixelMask()==False, AipsError); AlwaysAssert(lc.axis()==0, AipsError); AlwaysAssert(lc.nlattices()==2, AipsError); // check (0, lc, ml1, ml2); } { cout << "Axis 1, ArrayLattices, no masks" << endl; // Concatenate along axis 1 LatticeConcat lc (1, True); lc.setLattice(ml1); lc.setLattice(ml2); // Find output shape IPosition outShape = lc.shape(); AlwaysAssert(outShape.nelements()==2, AipsError); AlwaysAssert(outShape(0)==shape(0), AipsError); AlwaysAssert(outShape(1)==shape(1)+shape(1), AipsError); AlwaysAssert(lc.isMasked()==False, AipsError); AlwaysAssert(lc.hasPixelMask()==False, AipsError); AlwaysAssert(lc.axis()==1, AipsError); AlwaysAssert(lc.nlattices()==2, AipsError); // check (1, lc, ml1, ml2); } // { cout << "Increase dimensionality by 1, ArrayLattices, no masks" << endl; // Create axis 2 LatticeConcat lc (2); lc.setLattice(ml1); lc.setLattice(ml2); // Find output shape IPosition outShape = lc.shape(); AlwaysAssert(outShape.nelements()==3, AipsError); AlwaysAssert(outShape(0)==shape(0), AipsError); AlwaysAssert(outShape(1)==shape(1), AipsError); AlwaysAssert(outShape(2)=2, AipsError); AlwaysAssert(lc.isMasked()==False, AipsError); AlwaysAssert(lc.hasPixelMask()==False, AipsError); AlwaysAssert(lc.axis()==2, AipsError); AlwaysAssert(lc.nlattices()==2, AipsError); // check2 (lc, ml1, ml2); } { cout << "Increase dimensionality by 1, masks" << endl; // Create axis 2 LatticeConcat lc (2); lc.setLattice(im1); lc.setLattice(im2); // Find output shape IPosition outShape = lc.shape(); AlwaysAssert(outShape.nelements()==3, AipsError); AlwaysAssert(outShape(0)==shape(0), AipsError); AlwaysAssert(outShape(1)==shape(1), AipsError); AlwaysAssert(outShape(2)=2, AipsError); AlwaysAssert(lc.isMasked()==True, AipsError); AlwaysAssert(lc.hasPixelMask()==True, AipsError); AlwaysAssert(lc.pixelMask().isWritable()==True, AipsError); AlwaysAssert(lc.pixelMask().shape()==outShape, AipsError); AlwaysAssert(lc.axis()==2, AipsError); AlwaysAssert(lc.nlattices()==2, AipsError); // check2 (lc, im1, im2); } { cout << "Increase dimensionality by 1, masks, various getslices" << endl; // Create axis 2 LatticeConcat lc (2); lc.setLattice(im3); lc.setLattice(im3); lc.setLattice(im3); lc.setLattice(im3); lc.setLattice(im3); lc.setLattice(im3); lc.setLattice(im3); lc.setLattice(im3); // Find output shape IPosition outShape = lc.shape(); AlwaysAssert(outShape.nelements()==3, AipsError); AlwaysAssert(outShape(0)==shape(0), AipsError); AlwaysAssert(outShape(1)==shape(1), AipsError); AlwaysAssert(outShape(2)=8, AipsError); AlwaysAssert(lc.isMasked()==True, AipsError); AlwaysAssert(lc.axis()==2, AipsError); AlwaysAssert(lc.nlattices()==8, AipsError); // Now look at funny slices { cout << " All in lattice 1" << endl; IPosition blc(outShape.nelements(),0); blc(0) = 5; blc(1) = 10; blc(2) = 0; IPosition trc(outShape-10); trc(2) = 0; IPosition stride(outShape.nelements(),1); Slicer sl(blc, trc, stride, Slicer::endIsLast); check7(sl, lc, 1.0, True); } { cout << " All in lattice 1 + non-unit strides" << endl; IPosition blc(outShape.nelements()); blc(0) = 5; blc(1) = 10; blc(2) = 0; IPosition trc(outShape-10); trc(2) = 0; IPosition stride(outShape.nelements(),1); stride(0) = 2; stride(1) = 3; Slicer sl(blc, trc, stride, Slicer::endIsLast); check7(sl, lc, 1.0, True); } { cout << " Many lattices" << endl; IPosition blc(outShape.nelements(),0); blc(0) = 5; blc(1) = 10; blc(2) = 2; IPosition trc(outShape-10); trc(2) = 6; IPosition stride(outShape.nelements(),1); Slicer sl(blc, trc, stride, Slicer::endIsLast); check7(sl, lc, 1.0, True); } { cout << " Many lattices + non-unit strides" << endl; IPosition blc(outShape.nelements()); blc(0) = 5; blc(1) = 10; blc(2) = 1; IPosition trc(outShape-10); trc(2) = 7; IPosition stride(outShape.nelements(),1); stride(0) = 2; stride(1) = 3; stride(2) = 2; Slicer sl(blc, trc, stride, Slicer::endIsLast); check7(sl, lc, 1.0, True); } } { cout << "Increase dimensionality by 1, masks, various putslices" << endl; // Create axis 2 LatticeConcat lc (2); lc.setLattice(im3); lc.setLattice(im3); lc.setLattice(im3); lc.setLattice(im3); lc.setLattice(im3); lc.setLattice(im3); lc.setLattice(im3); lc.setLattice(im3); // Find output shape IPosition outShape = lc.shape(); AlwaysAssert(outShape.nelements()==3, AipsError); AlwaysAssert(outShape(0)==shape(0), AipsError); AlwaysAssert(outShape(1)==shape(1), AipsError); AlwaysAssert(outShape(2)=8, AipsError); AlwaysAssert(lc.isMasked()==True, AipsError); AlwaysAssert(lc.axis()==2, AipsError); AlwaysAssert(lc.nlattices()==8, AipsError); AlwaysAssert(lc.isWritable(), AipsError); // Now look at funny slices { cout << " All in lattice 1" << endl; IPosition blc(outShape.nelements(),0); blc(0) = 5; blc(1) = 10; blc(2) = 0; IPosition trc(outShape-10); trc(2) = 0; IPosition stride(outShape.nelements(),1); Slicer sl(blc, trc, stride, Slicer::endIsLast); // Array tmp0(sl.length()); tmp0.set(1.0); lc.putSlice(tmp0, sl.start(), sl.stride()); check4(sl, lc, tmp0); // Array btmp0(sl.length()); btmp0.set(False); lc.pixelMask().putSlice(btmp0, sl.start(), sl.stride()); check5(sl, lc, btmp0); } { cout << " All in lattice 1 + non-unit strides" << endl; IPosition blc(outShape.nelements()); blc(0) = 5; blc(1) = 10; blc(2) = 0; IPosition trc(outShape-10); trc(2) = 0; IPosition stride(outShape.nelements(),1); stride(0) = 2; stride(1) = 3; Slicer sl(blc, trc, stride, Slicer::endIsLast); // Array tmp0(sl.length()); tmp0.set(1.0); lc.putSlice(tmp0, sl.start(), sl.stride()); check4(sl, lc, tmp0); // Array btmp0(sl.length()); btmp0.set(False); lc.pixelMask().putSlice(btmp0, sl.start(), sl.stride()); check5(sl, lc, btmp0); } { cout << " Many lattices" << endl; IPosition blc(outShape.nelements(),0); blc(0) = 5; blc(1) = 10; blc(2) = 2; IPosition trc(outShape-10); trc(2) = 6; IPosition stride(outShape.nelements(),1); Slicer sl(blc, trc, stride, Slicer::endIsLast); // Array tmp0(sl.length()); tmp0.set(1.0); lc.putSlice(tmp0, sl.start(), sl.stride()); check4(sl, lc, tmp0); // Array btmp0(sl.length()); btmp0.set(False); lc.pixelMask().putSlice(btmp0, sl.start(), sl.stride()); check5(sl, lc, btmp0); } { cout << " Many lattices + non-unit strides" << endl; IPosition blc(outShape.nelements()); blc(0) = 5; blc(1) = 10; blc(2) = 1; IPosition trc(outShape-10); trc(2) = 7; IPosition stride(outShape.nelements(),1); stride(0) = 2; stride(1) = 3; stride(2) = 2; Slicer sl(blc, trc, stride, Slicer::endIsLast); // Array tmp0(sl.length()); tmp0.set(1.0); lc.putSlice(tmp0, sl.start(), sl.stride()); check4(sl, lc, tmp0); // Array btmp0(sl.length()); btmp0.set(False); lc.pixelMask().putSlice(btmp0, sl.start(), sl.stride()); check5(sl, lc, btmp0); } } // { cout << "Axis 0, masks" << endl; // Concatenate along axis 0 LatticeConcat lc (0); lc.setLattice(im1); lc.setLattice(im2); // Find output shape IPosition outShape = lc.shape(); AlwaysAssert(outShape.nelements()==2, AipsError); AlwaysAssert(outShape(0)==shape(0)+shape(0), AipsError); AlwaysAssert(outShape(1)==shape(1), AipsError); AlwaysAssert(lc.isMasked()==True, AipsError); AlwaysAssert(lc.axis()==0, AipsError); AlwaysAssert(lc.nlattices()==2, AipsError); // check (0, lc, im1, im2); } { // // Now, having convinced ourselves that the lattices are // concatenated properly, when we look at the whole thing, // make sure slices are correct when straddling lattice // boundaries etc. // cout << "Axis 0, masks, various getslices" << endl; // Concatenate along axis 0 LatticeConcat lc (0); im3.set(1.0); Lattice& pixelMask = im3.pixelMask(); pixelMask.set(True); lc.setLattice(im3); lc.setLattice(im3); // Find output shape IPosition outShape = lc.shape(); AlwaysAssert(outShape.nelements()==2, AipsError); AlwaysAssert(outShape(0)==shape(0)+shape(0), AipsError); AlwaysAssert(outShape(1)==shape(1), AipsError); AlwaysAssert(lc.isMasked()==True, AipsError); AlwaysAssert(lc.axis()==0, AipsError); AlwaysAssert(lc.nlattices()==2, AipsError); // Now look at funny slices { cout << " All in lattice 1" << endl; IPosition blc(outShape.nelements(),0); IPosition trc(shape-1); IPosition stride(outShape.nelements(),1); Slicer sl(blc, trc, stride, Slicer::endIsLast); check7(sl, lc, 1.0, True); } { cout << " All in lattice 1 + non-unit strides" << endl; IPosition blc(outShape.nelements(),0); IPosition trc(shape-1); IPosition stride(outShape.nelements(),1); stride(0) = 2; stride(1) = 3; Slicer sl(blc, trc, stride, Slicer::endIsLast); check7(sl, lc, 1.0, True); } { cout << " Straddle boundary" << endl; IPosition blc(outShape.nelements(),5); IPosition trc(shape-10); trc(0) = shape(0) + 30; IPosition stride(outShape.nelements(),1); Slicer sl(blc, trc, stride, Slicer::endIsLast); check7(sl, lc, 1.0, True); } { cout << " Straddle boundary and non-unit strides" << endl; IPosition blc(outShape.nelements(),5); IPosition trc(shape-10); trc(0) = shape(0) + 30; IPosition stride(outShape.nelements(),1); stride(0) = 2; stride(1) = 3; Slicer sl(blc, trc, stride, Slicer::endIsLast); check7(sl, lc, 1.0, True); } { cout << " All in lattice 2" << endl; IPosition blc(shape-1); blc(0) = shape(0) + 10; blc(1) = 10; IPosition trc(blc+20); IPosition stride(outShape.nelements(),1); Slicer sl(blc, trc, stride, Slicer::endIsLast); check7(sl, lc, 1.0, True); } { cout << " All in lattice 2 and non-unit strides" << endl; IPosition blc(shape-1); blc(0) = shape(0) + 10; blc(1) = 10; IPosition trc(blc+20); IPosition stride(outShape.nelements(),1); stride(0) = 2; stride(1) = 3; Slicer sl(blc, trc, stride, Slicer::endIsLast); check7(sl, lc, 1.0, True); } } // Putslices { cout << "Axis 0, ArrayLattices, various putslices" << endl; Array aa1 = ml1.get(); Array aa2 = ml2.get(); ArrayLattice x1(aa1); ArrayLattice x2(aa2); SubLattice m1(x1,True); SubLattice m2(x2,True); // LatticeConcat lc (0); lc.setLattice(m1); lc.setLattice(m2); IPosition outShape = lc.shape(); AlwaysAssert(lc.isWritable(),AipsError); // { cout << " All in lattice 1" << endl; IPosition blc(outShape.nelements(),0); IPosition trc(shape-1); IPosition stride(outShape.nelements(),1); Slicer sl(blc, trc, stride, Slicer::endIsLast); // Array tmp0(sl.length()); tmp0.set(1.0); lc.putSlice(tmp0, sl.start(), sl.stride()); check4(sl, lc, tmp0); } { cout << " All in lattice 1 + non-unit strides" << endl; IPosition blc(outShape.nelements(),0); IPosition trc(shape-1); IPosition stride(outShape.nelements(),1); stride(0) = 2; stride(1) = 3; Slicer sl(blc, trc, stride, Slicer::endIsLast); // Array tmp0(sl.length()); tmp0.set(1.0); lc.putSlice(tmp0, sl.start(), sl.stride()); check4(sl, lc, tmp0); } { cout << " Straddle boundary" << endl; IPosition blc(outShape.nelements(),5); IPosition trc(shape-10); trc(0) = shape(0) + 30; IPosition stride(outShape.nelements(),1); Slicer sl(blc, trc, stride, Slicer::endIsLast); // Array tmp0(sl.length()); tmp0.set(1.0); lc.putSlice(tmp0, sl.start(), sl.stride()); check4(sl, lc, tmp0); } { cout << " Straddle boundary and non-unit strides" << endl; IPosition blc(outShape.nelements(),5); IPosition trc(shape-10); trc(0) = shape(0) + 30; IPosition stride(outShape.nelements(),1); stride(0) = 2; stride(1) = 3; Slicer sl(blc, trc, stride, Slicer::endIsLast); // Array tmp0(sl.length()); tmp0.set(1.0); lc.putSlice(tmp0, sl.start(), sl.stride()); check4(sl, lc, tmp0); } { cout << " All in lattice 2" << endl; IPosition blc(shape-1); blc(0) = shape(0) + 10; blc(1) = 10; IPosition trc(blc+20); IPosition stride(outShape.nelements(),1); Slicer sl(blc, trc, stride, Slicer::endIsLast); // Array tmp0(sl.length()); tmp0.set(1.0); lc.putSlice(tmp0, sl.start(), sl.stride()); check4(sl, lc, tmp0); } { cout << " All in lattice 2 and non-unit strides" << endl; IPosition blc(shape-1); blc(0) = shape(0) + 10; blc(1) = 10; IPosition trc(blc+20); IPosition stride(outShape.nelements(),1); stride(0) = 2; stride(1) = 3; Slicer sl(blc, trc, stride, Slicer::endIsLast); // Array tmp0(sl.length()); tmp0.set(1.0); lc.putSlice(tmp0, sl.start(), sl.stride()); check4(sl, lc, tmp0); } } // pixelMask tests { cout << "Testing pixelMask" << endl; LatticeConcat lc (0); lc.setLattice(ml1); lc.setLattice(ml2); AlwaysAssert(lc.hasPixelMask()==False, AipsError); Bool ok; try { lc.pixelMask(); ok = False; } catch (std::exception& x) { ok = True; } if (!ok) { throw (AipsError("pixelMask forced failure did not work - this was unexpected")); } } { LatticeConcat lc (0); lc.setLattice(im1); lc.setLattice(im2); // AlwaysAssert(lc.hasPixelMask(), AipsError); Lattice& pixelMask = lc.pixelMask(); check6(0, pixelMask, mask1, mask2); } // Test lock etc { cout << "Testing locking" << endl; LatticeConcat lc (0); lc.setLattice(ml1); lc.setLattice(ml2); AlwaysAssert(lc.lock(FileLocker::Read, 1), AipsError); AlwaysAssert(lc.hasLock(FileLocker::Read), AipsError); AlwaysAssert(lc.lock(FileLocker::Write, 1), AipsError); AlwaysAssert(lc.hasLock(FileLocker::Write), AipsError); // ArrayLattices will always return True for hasLock lc.unlock(); AlwaysAssert(lc.hasLock(FileLocker::Read), AipsError); AlwaysAssert(lc.hasLock(FileLocker::Write), AipsError); } // Test copy constructor { cout << "Testing copy constructor" << endl; LatticeConcat lc (0); lc.setLattice(ml1); lc.setLattice(ml2); LatticeConcat lc2(lc); // Find output shape AlwaysAssert(lc.shape().isEqual(lc2.shape()), AipsError); AlwaysAssert(lc.isMasked()==lc2.isMasked(), AipsError); AlwaysAssert(lc2.axis()==0, AipsError); AlwaysAssert(lc.nlattices()==2, AipsError); // check (0, lc, ml1, ml2); } // Test assignment { cout << "Testing assignment " << endl; LatticeConcat lc (0); lc.setLattice(ml1); lc.setLattice(ml2); LatticeConcat lc2; lc2 = lc; // Find output shape AlwaysAssert(lc.shape().isEqual(lc2.shape()), AipsError); AlwaysAssert(lc.isMasked()==lc2.isMasked(), AipsError); AlwaysAssert(lc2.axis()==0, AipsError); AlwaysAssert(lc.nlattices()==2, AipsError); // check (0, lc, ml1, ml2); } // Some forced errors { cout << "Forced errors" << endl; // Concatenate along axis 0 LatticeConcat lc (10); Bool ok = True; try { lc.setLattice(ml1); ok = False; } catch (std::exception&x) { } if (!ok) { throw (AipsError("setLattice forced failure did not work - this was unexpected")); } // ok = True; try { ArrayLattice l4(IPosition(3,2,2,2)); SubLattice ml4(l4, True); lc.setLattice(ml4); ok = False; } catch (std::exception& x) {;} if (!ok) { throw (AipsError("setLattice forced failure did not work - this was unexpected")); } } } catch(std::exception& x) { cerr << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } void check (uInt axis, MaskedLattice& ml, MaskedLattice& ml1, MaskedLattice& ml2) { IPosition shape1 = ml1.shape(); IPosition shape2 = ml2.shape(); // IPosition blc(2,0,0); AlwaysAssert(allEQ(ml1.get(), ml.getSlice(blc,shape1)), AipsError); AlwaysAssert(allEQ(ml1.getMask(), ml.getMaskSlice(blc,shape1)), AipsError); // if (axis==0) { blc(0) += shape1(0); AlwaysAssert(allEQ(ml2.get(), ml.getSlice(blc,shape2)), AipsError); AlwaysAssert(allEQ(ml2.getMask(), ml.getMaskSlice(blc,shape2)), AipsError); } else if (axis==1) { blc(1) += shape1(1); AlwaysAssert(allEQ(ml2.get(), ml.getSlice(blc,shape2)), AipsError); AlwaysAssert(allEQ(ml2.getMask(), ml.getMaskSlice(blc,shape2)), AipsError); } else { AlwaysAssert(axis==0||axis==1, AipsError); } } void check2 (MaskedLattice& ml, MaskedLattice& ml1, MaskedLattice& ml2) { IPosition shape1 = ml1.shape(); IPosition shape2 = ml2.shape(); IPosition sliceShape(3,shape1(0), shape1(1), 1); // IPosition blc(3,0,0,0); AlwaysAssert(allEQ(ml1.get(), ml.getSlice(blc,sliceShape,True)), AipsError); AlwaysAssert(allEQ(ml1.getMask(), ml.getMaskSlice(blc,sliceShape,True)), AipsError); // blc(2) = 1; AlwaysAssert(allEQ(ml2.get(), ml.getSlice(blc,sliceShape,True)), AipsError); AlwaysAssert(allEQ(ml2.getMask(), ml.getMaskSlice(blc,sliceShape,True)), AipsError); } void check3 (const Slicer& sl, MaskedLattice& ml1, MaskedLattice& ml2) { AlwaysAssert(allEQ(ml1.getSlice(sl), ml2.getSlice(sl)), AipsError); AlwaysAssert(allEQ(ml1.getMaskSlice(sl), ml2.getMaskSlice(sl)), AipsError); } void check4 (const Slicer& sl, MaskedLattice& ml1, Array& ml2) { AlwaysAssert(allEQ(ml1.getSlice(sl), ml2), AipsError); } void check5 (const Slicer& sl, MaskedLattice& ml1, Array& ml2) { AlwaysAssert(allEQ(ml1.getMaskSlice(sl), ml2), AipsError); } void check6 (uInt axis, Lattice& ml, Lattice& ml1, Lattice& ml2) { IPosition shape1 = ml1.shape(); IPosition shape2 = ml2.shape(); // IPosition blc(2,0,0); AlwaysAssert(allEQ(ml1.get(), ml.getSlice(blc,shape1)), AipsError); // if (axis==0) { blc(0) += shape1(0); AlwaysAssert(allEQ(ml2.get(), ml.getSlice(blc,shape2)), AipsError); } else if (axis==1) { blc(1) += shape1(1); AlwaysAssert(allEQ(ml2.get(), ml.getSlice(blc,shape2)), AipsError); } else { AlwaysAssert(axis==0||axis==1, AipsError); } } void check7 (const Slicer& sl, LatticeConcat& lc, Float val, Bool valMask) { Double tol(1.0e-6); AlwaysAssert(allNear(lc.getSlice(sl),val,tol),AipsError); AlwaysAssert(allEQ(lc.getMaskSlice(sl),valMask),AipsError); } casacore-3.7.1/lattices/Lattices/test/tLatticeIndexer.cc000066400000000000000000000415421476623553700232700ustar00rootroot00000000000000// tLatticeIndexer.cc: mechanical test of LatticeIndexer class //# Copyright (C) 1995,1996,1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include int main () { try { { LatticeIndexer l; AlwaysAssert(l.nelements() == 1, AipsError); AlwaysAssert(l.ndim() == 1, AipsError); AlwaysAssert(l.increment().isEqual(IPosition(1,1)), AipsError); AlwaysAssert(l.offset().isEqual(IPosition(1,0)), AipsError); l.resize(IPosition(3,10,20,30)); AlwaysAssert(l.nelements() == 10*20*30, AipsError); AlwaysAssert(l.ndim() == 3, AipsError); AlwaysAssert(l.shape().isEqual(IPosition(3,10,20,30)), AipsError); AlwaysAssert(l.increment().isEqual(IPosition(3,1)), AipsError); AlwaysAssert(l.offset().isEqual(IPosition(3,0)), AipsError); l.subSection(IPosition(3,1,2,3), IPosition(3,9,19,29), IPosition(3,2,3,4)); AlwaysAssert(l.nelements() == 5*6*7, AipsError); AlwaysAssert(l.ndim() == 3, AipsError); AlwaysAssert(l.shape(0) == 5, AipsError); AlwaysAssert(l.shape(1) == 6, AipsError); AlwaysAssert(l.shape(2) == 7, AipsError); AlwaysAssert(l.increment(0) == 2, AipsError); AlwaysAssert(l.increment(1) == 3, AipsError); AlwaysAssert(l.increment(2) == 4, AipsError); AlwaysAssert(l.offset(0) == 1, AipsError); AlwaysAssert(l.offset(1) == 2, AipsError); AlwaysAssert(l.offset(2) == 3, AipsError); AlwaysAssert(l.fullShape(0) == 10, AipsError); AlwaysAssert(l.fullShape(1) == 20, AipsError); AlwaysAssert(l.fullShape(2) == 30, AipsError); l.subSection(IPosition(3,2,2,1), IPosition(3,4,5,6), IPosition(3,2,3,6)); AlwaysAssert(l.nelements() == 2*2*1, AipsError); AlwaysAssert(l.ndim() == 3, AipsError); AlwaysAssert(l.shape().isEqual(IPosition(3,2,2,1)), AipsError); AlwaysAssert(l.increment().isEqual(IPosition(3,4,9,24)), AipsError); AlwaysAssert(l.offset().isEqual(IPosition(3,5,8,7)), AipsError); AlwaysAssert(l.fullShape().isEqual(IPosition(3,10,20,30)), AipsError); AlwaysAssert(l.absolutePosition(IPosition(3,0)) .isEqual(IPosition(3,5,8,7)), AipsError); AlwaysAssert(l.absolutePosition(IPosition(3,1)) .isEqual(IPosition(3,9,17,31)), AipsError); l.fullSize(); AlwaysAssert(l.ndim() == 3, AipsError); AlwaysAssert(l.shape().isEqual(IPosition(3,10,20,30)), AipsError); AlwaysAssert(l.increment().isEqual(IPosition(3,1)), AipsError); AlwaysAssert(l.offset().isEqual(IPosition(3,0)), AipsError); } { LatticeIndexer l(IPosition(3,5,6,7)); LatticeIndexer lc(l), la; la = l; l.subSection(IPosition(3,2,2,1), IPosition(3,4,5,6)); AlwaysAssert(lc.shape().isEqual(IPosition(3,5,6,7)), AipsError); AlwaysAssert(lc.increment().isEqual(IPosition(3,1)), AipsError); AlwaysAssert(lc.offset().isEqual(IPosition(3,0)), AipsError); AlwaysAssert(la.shape().isEqual(IPosition(3,5,6,7)), AipsError); AlwaysAssert(la.increment().isEqual(IPosition(3,1)), AipsError); AlwaysAssert(la.offset().isEqual(IPosition(3,0)), AipsError); } { LatticeIndexer l(IPosition(3,5,6,7)); IPosition point(3,0); IPosition shape(3,3); IPosition heading(3,0,2,1); // move along the x-axis then z-axis // Move forward through all the locations AlwaysAssert(l.tiledCursorMove(True, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(3,3,0,0), AipsError); AlwaysAssert(l.tiledCursorMove(True, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(3,0,0,3), AipsError); AlwaysAssert(l.tiledCursorMove(True, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(3,3,0,3), AipsError); AlwaysAssert(l.tiledCursorMove(True, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(3,0,0,6), AipsError); AlwaysAssert(l.tiledCursorMove(True, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(3,3,0,6), AipsError); AlwaysAssert(l.tiledCursorMove(True, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(3,0,3,0), AipsError); AlwaysAssert(l.tiledCursorMove(True, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(3,3,3,0), AipsError); AlwaysAssert(l.tiledCursorMove(True, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(3,0,3,3), AipsError); AlwaysAssert(l.tiledCursorMove(True, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(3,3,3,3), AipsError); AlwaysAssert(l.tiledCursorMove(True, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(3,0,3,6), AipsError); AlwaysAssert(l.tiledCursorMove(True, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(3,3,3,6), AipsError); // Should not be able to move any further AlwaysAssert(l.tiledCursorMove(True, point, shape, heading)==False, AipsError); // Now move backwards one step and check we ended up where we were AlwaysAssert(l.tiledCursorMove(False, point, shape, heading),AipsError); AlwaysAssert(point == IPosition(3,0,3,6), AipsError); // set the position to the last element and move backwards from there point = l.shape() - 1; heading = IPosition(3,2,1,0); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading),AipsError); AlwaysAssert(point == IPosition(3,4,5,3), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading),AipsError); AlwaysAssert(point == IPosition(3,4,5,0), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading),AipsError); AlwaysAssert(point == IPosition(3,4,2,6), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading),AipsError); AlwaysAssert(point == IPosition(3,4,2,3), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading),AipsError); AlwaysAssert(point == IPosition(3,4,2,0), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading),AipsError); AlwaysAssert(point == IPosition(3,4,-1,6), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading),AipsError); AlwaysAssert(point == IPosition(3,4,-1,3), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading),AipsError); AlwaysAssert(point == IPosition(3,4,-1,0), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading),AipsError); AlwaysAssert(point == IPosition(3,1,5,6), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading),AipsError); AlwaysAssert(point == IPosition(3,1,5,3), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading),AipsError); AlwaysAssert(point == IPosition(3,1,5,0), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading),AipsError); AlwaysAssert(point == IPosition(3,1,2,6), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading),AipsError); AlwaysAssert(point == IPosition(3,1,2,3), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading),AipsError); AlwaysAssert(point == IPosition(3,1,2,0), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading),AipsError); AlwaysAssert(point == IPosition(3,1,-1,6), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading),AipsError); AlwaysAssert(point == IPosition(3,1,-1,3), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading),AipsError); AlwaysAssert(point == IPosition(3,1,-1,0), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading),AipsError); AlwaysAssert(point == IPosition(3,-2,5,6), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading),AipsError); AlwaysAssert(point == IPosition(3,-2,5,3), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading),AipsError); AlwaysAssert(point == IPosition(3,-2,5,0), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading),AipsError); AlwaysAssert(point == IPosition(3,-2,2,6), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading),AipsError); AlwaysAssert(point == IPosition(3,-2,2,3), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading),AipsError); AlwaysAssert(point == IPosition(3,-2,2,0), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading),AipsError); AlwaysAssert(point == IPosition(3,-2,-1,6), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading),AipsError); AlwaysAssert(point == IPosition(3,-2,-1,3), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading),AipsError); AlwaysAssert(point == IPosition(3,-2,-1,0), AipsError); // Should not be able to move back any further AlwaysAssert(l.tiledCursorMove(False, point, shape, heading)==False, AipsError); } { // test tiledCursorMove with degenerate axes. LatticeIndexer l(IPosition(3,7,9,1), IPosition(3,1,1,0), IPosition(3,6,8,0), IPosition(3,2,2,1)); IPosition point(3,0); IPosition shape(3,2,2,1); IPosition heading(3,0,1,2); // move along the x-axis then the y-axis // Move forward through all the locations AlwaysAssert(l.tiledCursorMove(True, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(3,2,0,0), AipsError); AlwaysAssert(l.tiledCursorMove(True, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(3,0,2,0), AipsError); AlwaysAssert(l.tiledCursorMove(True, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(3,2,2,0), AipsError); AlwaysAssert(l.tiledCursorMove(True, point, shape, heading)==False, AipsError); // Move backward through all the locations. point = l.shape() - 1; AlwaysAssert(l.tiledCursorMove(False, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(3,0,3,0), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(3,2,1,0), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(3,0,1,0), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(3,2,-1,0), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(3,0,-1,0), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading)==False, AipsError); } { LatticeIndexer l(IPosition(3,7,9,1), IPosition(3,1,1,0), IPosition(3,6,8,0), IPosition(3,2,2,1)); AlwaysAssert(l.isInside(IPosition(3,2,3,0))==True, AipsError); AlwaysAssert(l.isInside(IPosition(3,3,3,0))==False, AipsError); AlwaysAssert(l.isInside(IPosition(3,-1,0,0))==False, AipsError); } { LatticeIndexer l(IPosition(2,8,5)); IPosition shape(2,3); IPosition point(2,0); IPosition heading(2,0,1); AlwaysAssert(l.tiledCursorMove(True, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(2,3,0), AipsError); AlwaysAssert(l.tiledCursorMove(True, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(2,6,0), AipsError); AlwaysAssert(l.tiledCursorMove(True, point, shape, heading) , AipsError); AlwaysAssert(point == IPosition(2,0,3), AipsError); AlwaysAssert(l.tiledCursorMove(True, point, shape, heading) , AipsError); AlwaysAssert(point == IPosition(2,3,3), AipsError); AlwaysAssert(l.tiledCursorMove(True, point, shape, heading) , AipsError); AlwaysAssert(point == IPosition(2,6,3), AipsError); AlwaysAssert(l.tiledCursorMove(True, point, shape, heading) == False , AipsError); // AlwaysAssert(l.tiledCursorMove(False, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(2,3,3), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(2,0,3), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(2,6,0), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(2,3,0), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(2,0,0), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading) == False, AipsError); point = IPosition(2,1,1); AlwaysAssert(l.tiledCursorMove(True, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(2,4,1), AipsError); AlwaysAssert(l.tiledCursorMove(True, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(2,7,1), AipsError); AlwaysAssert(l.tiledCursorMove(True, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(2,-2,4), AipsError); AlwaysAssert(l.tiledCursorMove(True, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(2,1,4), AipsError); AlwaysAssert(l.tiledCursorMove(True, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(2,4,4), AipsError); AlwaysAssert(l.tiledCursorMove(True, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(2,7,4), AipsError); AlwaysAssert(l.tiledCursorMove(True, point, shape, heading) == False, AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(2,4,4), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(2,1,4), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(2,-2,4), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(2,7,1), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(2,4,1), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(2,1,1), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(2,-2,1), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(2,7,-2), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(2,4,-2), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(2,1,-2), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading), AipsError); AlwaysAssert(point == IPosition(2,-2,-2), AipsError); AlwaysAssert(l.tiledCursorMove(False, point, shape, heading) == False, AipsError); } cout << "OK" << endl; return 0; } catch (std::exception& x) { cerr << x.what() << endl; cout << "FAIL" << endl; return 1; } } casacore-3.7.1/lattices/Lattices/test/tLatticeIterator.cc000066400000000000000000001477651476623553700235010ustar00rootroot00000000000000//# tLatticeIterator.cc: mechanical test of the LatticeIterator class //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void testVectorROIter (const Lattice& lattice, Bool useRef) { cout << " Testing using a Vector cursor" << endl; Int nstep; const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(1,latticeShape(0)); LatticeStepper step(latticeShape, cursorShape); RO_LatticeIterator iter(lattice, step, useRef); Vector expectedResult(latticeShape(0)); indgen(expectedResult); AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()) == True, AipsError); try { Matrix temp(iter.matrixCursor()); throw(AipsError("tLatticeIterator - " "matrixCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("two non-degenerate")) { throw (AipsError (x.what())); } } try { Cube temp(iter.cubeCursor()); throw(AipsError("tLatticeIterator - " "cubeCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("three non-degenerate")) { throw (AipsError (x.what())); } } AlwaysAssert(latticeShape == iter.latticeShape(), AipsError); AlwaysAssert(cursorShape == iter.cursorShape().nonDegenerate(), AipsError); Timer clock; for (iter.reset(); !iter.atEnd(); iter++){ AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); expectedResult += Int(cursorShape.product()); } nstep = iter.nsteps(); AlwaysAssert(nstep == latticeShape.product()/latticeShape(0), AipsError); IPosition expectedPos(latticeShape-1); AlwaysAssert(iter.endPosition() == expectedPos, AipsError); expectedPos(0) = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedResult -= Int(cursorShape.product()); Int ns=0; for (; !iter.atStart(); iter--){ AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()) == True, AipsError); expectedResult -= Int(cursorShape.product()); ns++; } clock.show(); nstep = iter.nsteps(); AlwaysAssert(nstep == 2*(latticeShape.product()/latticeShape(0)), AipsError); expectedPos = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedPos(0) = latticeShape(0) - 1; AlwaysAssert(iter.endPosition() == expectedPos, AipsError); } void testMatrixROIter (const Lattice& lattice, Bool useRef) { cout << " Testing using a Matrix cursor" << endl; Int nstep; const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(2, latticeShape(0), latticeShape(1)); RO_LatticeIterator iter(lattice, cursorShape, useRef); Matrix expectedResult(latticeShape(0), latticeShape(1)); indgen(expectedResult); AlwaysAssert(allEQ(expectedResult, iter.matrixCursor()) == True, AipsError); AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()) == True, AipsError); try { Vector temp(iter.vectorCursor()); throw(AipsError("tLatticeIterator - " "vectorCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("one non-degenerate")) { throw (AipsError (x.what())); } } try { Cube temp(iter.cubeCursor()); throw(AipsError("tLatticeIterator - " "cubeCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("three non-degenerate")) { throw (AipsError (x.what())); } } Timer clock; for (iter.reset(); !iter.atEnd(); iter++){ AlwaysAssert(allEQ(expectedResult, iter.matrixCursor()) == True, AipsError); expectedResult += Int(cursorShape.product()); } nstep = iter.nsteps(); AlwaysAssert(nstep == latticeShape(2)*latticeShape(3), AipsError); IPosition expectedPos(latticeShape-1); AlwaysAssert(iter.endPosition() == expectedPos, AipsError); expectedPos(0) = 0; expectedPos(1) = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedResult -= Int(cursorShape.product()); for (; !iter.atStart(); --iter){ AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()) == True, AipsError); expectedResult -= Int(cursorShape.product()); } clock.show(); nstep = iter.nsteps(); AlwaysAssert(nstep == 2*(latticeShape(2)*latticeShape(3)), AipsError); expectedPos = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedPos(0) = latticeShape(0) - 1; expectedPos(1) = latticeShape(1) - 1; AlwaysAssert(iter.endPosition() == expectedPos, AipsError); } void testCubeROIter (const Lattice& lattice, Bool useRef) { cout << " Testing using a Cube cursor" << endl; Int nstep; const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(3, latticeShape(0), latticeShape(1), latticeShape(2)); LatticeStepper step(latticeShape, cursorShape); RO_LatticeIterator iter(lattice, step, useRef); Cube expectedResult(latticeShape(0), latticeShape(1), latticeShape(2)); indgen(expectedResult); AlwaysAssert(allEQ(expectedResult, iter.cubeCursor()) == True, AipsError); AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()) == True, AipsError); try { Vector temp(iter.vectorCursor()); throw(AipsError("tLatticeIterator - " "vectorCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("one non-degenerate")) { throw (AipsError (x.what())); } } try { Matrix temp(iter.matrixCursor()); throw(AipsError("tLatticeIterator - " "matrixCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("two non-degenerate")) { throw (AipsError (x.what())); } } Timer clock; for (iter.reset(); !iter.atEnd(); iter++){ AlwaysAssert(allEQ(expectedResult, iter.cubeCursor()) == True, AipsError); expectedResult += Int(cursorShape.product()); } nstep = iter.nsteps(); AlwaysAssert(nstep == latticeShape(3), AipsError); IPosition expectedPos(latticeShape-1); AlwaysAssert(iter.endPosition() == expectedPos, AipsError); expectedPos(0) = 0; expectedPos(1) = 0; expectedPos(2) = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedResult -= Int(cursorShape.product()); for (; !iter.atStart(); iter--){ AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()) == True, AipsError); expectedResult -= Int(cursorShape.product()); } clock.show(); nstep = iter.nsteps(); AlwaysAssert(nstep == 2 * (latticeShape(3)), AipsError); expectedPos = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedPos(0) = latticeShape(0) - 1; expectedPos(1) = latticeShape(1) - 1; expectedPos(2) = latticeShape(2) - 1; AlwaysAssert(iter.endPosition() == expectedPos, AipsError); } void testArrayROIter (const Lattice& lattice, Bool useRef) { cout << " Testing using an Array (4-D) cursor" << endl; Int nstep; const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(latticeShape); RO_LatticeIterator iter(lattice, cursorShape, useRef); Array expectedResult(latticeShape); indgen(expectedResult); AlwaysAssert(allEQ(expectedResult, iter.cursor()) == True, AipsError); try { Vector temp(iter.vectorCursor()); throw(AipsError("tLatticeIterator - " "vectorCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("one non-degenerate")) { throw (AipsError (x.what())); } } try { Matrix temp(iter.matrixCursor()); throw(AipsError("tLatticeIterator - " "matrixCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("two non-degenerate")) { throw (AipsError (x.what())); } } try { Cube temp(iter.cubeCursor()); throw(AipsError("tLatticeIterator - " "cubeCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("three non-degenerate")) { throw (AipsError (x.what())); } } Timer clock; for (iter.reset(); !iter.atEnd(); ++iter){ AlwaysAssert(allEQ(expectedResult, iter.cursor()) == True, AipsError); expectedResult += Int(cursorShape.product()); } nstep = iter.nsteps(); AlwaysAssert(nstep == 1, AipsError); IPosition expectedPos(latticeShape-1); AlwaysAssert(iter.endPosition() == expectedPos, AipsError); expectedPos = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedResult -= Int(cursorShape.product()); for (; !iter.atStart(); --iter){ AlwaysAssert(allEQ(expectedResult, iter.cursor()) == True, AipsError); expectedResult -= Int(cursorShape.product()); } clock.show(); nstep = iter.nsteps(); AlwaysAssert(nstep == 2, AipsError); expectedPos = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedPos = latticeShape - 1; AlwaysAssert(iter.endPosition() == expectedPos, AipsError); } void test8ElemROIter (const Lattice& lattice, Bool useRef) { cout << " Testing using an 8 element cursor" << endl; Int nstep; const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(1,8); LatticeStepper step(latticeShape, cursorShape); RO_LatticeIterator iter(lattice, step, useRef); Array expectedResult(cursorShape); indgen(expectedResult); AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()) == True, AipsError); try { Matrix temp(iter.matrixCursor()); throw(AipsError("tLatticeIterator - " "matrixCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("two non-degenerate")) { throw (AipsError (x.what())); } } try { Cube temp(iter.cubeCursor()); throw(AipsError("tLatticeIterator - " "cubeCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("three non-degenerate")) { throw (AipsError (x.what())); } } Timer clock; for (iter.reset(); !iter.atEnd(); ++iter){ AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); expectedResult += Int(cursorShape.product()); } nstep = iter.nsteps(); AlwaysAssert(nstep == latticeShape.product()/8, AipsError); IPosition expectedPos(latticeShape-1); AlwaysAssert(iter.endPosition() == expectedPos, AipsError); expectedPos(0) = 8; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedResult -= Int(cursorShape.product()); for (; !iter.atStart(); --iter){ AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()) == True, AipsError); expectedResult -= Int(cursorShape.product()); } clock.show(); nstep = iter.nsteps(); AlwaysAssert(nstep == 2*(latticeShape.product()/8), AipsError); expectedPos = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedPos(0) = 8-1; AlwaysAssert(iter.endPosition() == expectedPos, AipsError); } void testTileROIter (const Lattice& lattice, Bool useRef) { cout << " Testing using a tile cursor" << endl; Int nstep; const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(lattice.niceCursorShape()); RO_LatticeIterator iter(lattice, useRef); Array expectedResult(cursorShape); indgen(expectedResult); AlwaysAssert(allEQ(expectedResult, iter.cursor()) == True, AipsError); Timer clock; for (iter.reset(); !iter.atEnd(); ++iter){ AlwaysAssert(allEQ(expectedResult, iter.cursor()) == True, AipsError); expectedResult += Int(cursorShape.product()); } nstep = iter.nsteps(); AlwaysAssert(nstep == latticeShape.product()/cursorShape.product(), AipsError); for (; !iter.atStart(); --iter){ expectedResult -= Int(cursorShape.product()); AlwaysAssert(allEQ(expectedResult, iter.cursor()) == True, AipsError); } clock.show(); nstep = iter.nsteps(); AlwaysAssert(nstep == 2*latticeShape.product()/cursorShape.product(), AipsError); } void testTiledLineROIter (const Lattice& lattice, Bool useRef) { cout << " Testing using a tiled line cursor" << endl; Int nstep; const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(lattice.niceCursorShape()); TiledLineStepper step(latticeShape, cursorShape, 0); RO_LatticeIterator iter(lattice, step, useRef); Vector expectedResult(latticeShape(0)); indgen(expectedResult); AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); Timer clock; for (iter.reset(); !iter.atEnd(); ++iter){ AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); expectedResult += Int(latticeShape(0)); } nstep = iter.nsteps(); AlwaysAssert(nstep == latticeShape.product()/latticeShape(0), AipsError); for (; !iter.atStart(); --iter){ expectedResult -= Int(latticeShape(0)); AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); } clock.show(); nstep = iter.nsteps(); AlwaysAssert(nstep == 2*latticeShape.product()/latticeShape(0), AipsError); } void testCopyAssignROIter (const Lattice& lattice, Bool useRef) { cout << " Testing the copy constructor and assignment operator" << endl; const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(1,latticeShape(0)); { // Test default ctor and its handling in copy and assignment. RO_LatticeIterator iter; AlwaysAssert(iter.isNull(), AipsError); RO_LatticeIterator iter1 = iter.copy(); AlwaysAssert(iter1.isNull(), AipsError); iter = RO_LatticeIterator (lattice, useRef); AlwaysAssert(!iter.isNull(), AipsError); iter = iter1; AlwaysAssert(iter.isNull(), AipsError); iter = RO_LatticeIterator (lattice, useRef); AlwaysAssert(!iter.isNull(), AipsError); iter1 = iter; AlwaysAssert(!iter1.isNull(), AipsError); iter = RO_LatticeIterator(); AlwaysAssert(iter.isNull(), AipsError); AlwaysAssert(!iter1.isNull(), AipsError); RO_LatticeIterator iterc(iter); AlwaysAssert(iterc.isNull(), AipsError); RO_LatticeIterator iterc1(iter1); AlwaysAssert(!iterc1.isNull(), AipsError); iterc1 = iterc; AlwaysAssert(iterc1.isNull(), AipsError); AlwaysAssert(!iter1.isNull(), AipsError); } RO_LatticeIterator iter(lattice, LatticeStepper(latticeShape, cursorShape), useRef); AlwaysAssert(!iter.isNull(), AipsError); iter++; Vector expectedResult(latticeShape(0)); indgen(expectedResult); expectedResult += Int(cursorShape.product()); AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); RO_LatticeIterator iterCopy(iter.copy()); Vector expectedCopy(expectedResult.copy()); AlwaysAssert(allEQ(expectedCopy, iterCopy.vectorCursor()) == True, AipsError); iter++; expectedResult += Int(cursorShape.product()); AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); AlwaysAssert(allEQ(expectedCopy, iterCopy.vectorCursor()) == True, AipsError); iterCopy--; expectedCopy -= Int(cursorShape.product()); AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); AlwaysAssert(allEQ(expectedCopy, iterCopy.vectorCursor()) == True, AipsError); AlwaysAssert(allEQ(iter.vectorCursor(),iterCopy.vectorCursor()) == False, AipsError); iterCopy = iter.copy(); expectedCopy = expectedResult; AlwaysAssert(allEQ(iter.vectorCursor(),iterCopy.vectorCursor()) == True, AipsError); AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); iterCopy++; expectedCopy += Int(cursorShape.product()); AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); AlwaysAssert(allEQ(expectedCopy, iterCopy.vectorCursor()) == True, AipsError); AlwaysAssert(allEQ(iter.vectorCursor(),iterCopy.vectorCursor()) == False, AipsError); } void testNonCongruentROIter (const Lattice& lattice, Bool useRef) { cout << " Testing using a non-congruent cursor" << endl; const IPosition latticeShape(lattice.shape()); IPosition cursorShape(2,9); LatticeStepper step(latticeShape, cursorShape); RO_LatticeIterator iter(lattice, step, useRef); Matrix expectedResult(cursorShape); Vector oneRow(cursorShape(0)); indgen(oneRow); uInt i; for (i = 0; i < uInt(cursorShape(1)); i++) { expectedResult.column(i) = oneRow; oneRow += Int(latticeShape(0)); } AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()), AipsError); iter++; indgen(oneRow, Int(cursorShape(0))); for (i = 0; i < uInt(cursorShape(1)); i++) { oneRow(7) = 0; oneRow(8) = 0; expectedResult.column(i) = oneRow; oneRow += Int(latticeShape(0)); } AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()), AipsError); iter++; expectedResult = 0; indgen(oneRow, Int(cursorShape(0)*latticeShape(0))); for (i = 0; i < 3; i++) { expectedResult.column(i) = oneRow; oneRow += Int(latticeShape(0)); } AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()), AipsError); iter++; expectedResult = 0; indgen(oneRow, Int(cursorShape(0)*(latticeShape(0)+1))); for (i = 0; i < 3; i++) { oneRow(7) = 0; oneRow(8) = 0; expectedResult.column(i) = oneRow; oneRow += Int(latticeShape(0)); } cursorShape = 5; step.setCursorShape(cursorShape); step.subSection(IPosition(4, 3,0,0,0), latticeShape-1, IPosition(4, 2,2,1,1)); RO_LatticeIterator subIter(lattice, step, useRef); oneRow.resize(5); Matrix expectedResult1(5,5); expectedResult1 = 0; indgen(oneRow, 3, 2); for (i = 0; i < 5; i++) { expectedResult1.column(i) = oneRow; oneRow += 32; } AlwaysAssert(allEQ(expectedResult1, subIter.cursor().nonDegenerate()), AipsError); subIter++; Matrix expectedResult2(5,5); expectedResult2 = 0; indgen(oneRow, 13, 2); for (i = 0; i < 5; i++) { oneRow(2) = 0; oneRow(3) = 0; oneRow(4) = 0; expectedResult2.column(i) = oneRow; oneRow += 32; } AlwaysAssert(allEQ(expectedResult2, subIter.cursor().nonDegenerate()), AipsError); subIter++; Matrix expectedResult3(5,5); expectedResult3 = 0; indgen(oneRow, 163, 2); for (i = 0; i < 1; i++) { expectedResult3.column(i) = oneRow; oneRow += 32; } AlwaysAssert(allEQ(expectedResult3, subIter.cursor().nonDegenerate()), AipsError); subIter++; Matrix expectedResult4(5,5); expectedResult4 = 0; indgen(oneRow, 173, 2); for (i = 0; i < 1; i++) { oneRow(2) = 0; oneRow(3) = 0; oneRow(4) = 0; expectedResult4.column(i) = oneRow; oneRow += 32; } AlwaysAssert(allEQ(expectedResult4, subIter.cursor().nonDegenerate()), AipsError); subIter--; AlwaysAssert(allEQ(expectedResult3, subIter.cursor().nonDegenerate()), AipsError); subIter--; AlwaysAssert(allEQ(expectedResult2, subIter.cursor().nonDegenerate()), AipsError); subIter--; AlwaysAssert(allEQ(expectedResult1, subIter.cursor().nonDegenerate()), AipsError); } void testVectorRWIter (Lattice& lattice, Bool useRef) { cout << " Testing using a Vector cursor" << endl; Int nstep; const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(1,latticeShape(0)); LatticeStepper step(latticeShape, cursorShape); LatticeIterator iter(lattice, step, useRef); Vector expectedResult(latticeShape(0)); indgen(expectedResult); AlwaysAssert(allEQ(expectedResult,iter.vectorCursor()) == True, AipsError); AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()) == True, AipsError); try { Matrix temp(iter.matrixCursor()); throw(AipsError("tLatticeIterator - " "matrixCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("two non-degenerate")) { throw (AipsError (x.what())); } } try { Cube temp(iter.cubeCursor()); throw(AipsError("tLatticeIterator - " "cubeCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("three non-degenerate")) { throw (AipsError (x.what())); } } AlwaysAssert(latticeShape == iter.latticeShape(), AipsError); AlwaysAssert(cursorShape == iter.cursorShape().nonDegenerate(), AipsError); Timer clock; for (iter.reset(); !iter.atEnd(); iter++){ AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); iter.rwVectorCursor()(0) -= expectedResult(0); expectedResult += Int(cursorShape.product()); } nstep = iter.nsteps(); AlwaysAssert(nstep == latticeShape.product()/latticeShape(0), AipsError); IPosition expectedPos(latticeShape-1); AlwaysAssert(iter.endPosition() == expectedPos, AipsError); expectedPos(0) = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedResult -= Int(cursorShape.product()); expectedResult(0) = 0; for (; !iter.atStart(); iter--){ AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()) == True, AipsError); iter.woCursor() = 1; expectedResult -= Int(cursorShape.product()); expectedResult(0) = 0; } clock.show(); nstep = iter.nsteps(); AlwaysAssert(nstep == 2*(latticeShape.product()/latticeShape(0)), AipsError); expectedPos = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedPos(0) = latticeShape(0) - 1; AlwaysAssert(iter.endPosition() == expectedPos, AipsError); } void testMatrixRWIter (Lattice& lattice, Bool useRef) { cout << " Testing using a Matrix cursor" << endl; Int nstep; const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(2, latticeShape(0), latticeShape(1)); LatticeIterator iter(lattice, cursorShape, useRef); Matrix expectedResult(latticeShape(0), latticeShape(1)); expectedResult = 1; AlwaysAssert(allEQ(expectedResult, iter.matrixCursor()) == True, AipsError); AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()) == True, AipsError); try { Vector temp(iter.vectorCursor()); throw(AipsError("tLatticeIterator - " "vectorCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("one non-degenerate")) { throw (AipsError (x.what())); } } try { Cube temp(iter.cubeCursor()); throw(AipsError("tLatticeIterator - " "cubeCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("three non-degenerate")) { throw (AipsError (x.what())); } } Timer clock; for (iter.reset(); !iter.atEnd(); iter++){ AlwaysAssert(allEQ(expectedResult, iter.matrixCursor()) == True, AipsError); iter.rwMatrixCursor()(0,0) = 2; } nstep = iter.nsteps(); AlwaysAssert(nstep == latticeShape(2)*latticeShape(3), AipsError); IPosition expectedPos(latticeShape-1); AlwaysAssert(iter.endPosition() == expectedPos, AipsError); expectedPos(0) = 0; expectedPos(1) = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedResult(0,0) = 2; for (; !iter.atStart(); --iter){ AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()) == True, AipsError); iter.woCursor() = 3; } clock.show(); nstep = iter.nsteps(); AlwaysAssert(nstep == 2*(latticeShape(2)*latticeShape(3)), AipsError); expectedPos = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedPos(0) = latticeShape(0) - 1; expectedPos(1) = latticeShape(1) - 1; AlwaysAssert(iter.endPosition() == expectedPos, AipsError); } void testCubeRWIter (Lattice& lattice, Bool useRef) { cout << " Testing using a Cube cursor" << endl; Int nstep; const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(3, latticeShape(0), latticeShape(1), latticeShape(2)); LatticeStepper step(latticeShape, cursorShape); LatticeIterator iter(lattice, step, useRef); Cube expectedResult(latticeShape(0), latticeShape(1), latticeShape(2)); expectedResult = 3; AlwaysAssert(allEQ(expectedResult, iter.cubeCursor()) == True, AipsError); AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()) == True, AipsError); try { Vector temp(iter.vectorCursor()); throw(AipsError("tLatticeIterator - " "vectorCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("one non-degenerate")) { throw (AipsError (x.what())); } } try { Matrix temp(iter.matrixCursor()); throw(AipsError("tLatticeIterator - " "matrixCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("two non-degenerate")) { throw (AipsError (x.what())); } } Timer clock; for (iter.reset(); !iter.atEnd(); iter++){ AlwaysAssert(allEQ(expectedResult, iter.cubeCursor()) == True, AipsError); iter.rwCubeCursor()(0,0,0) = 4; } nstep = iter.nsteps(); AlwaysAssert(nstep == latticeShape(3), AipsError); IPosition expectedPos(latticeShape-1); AlwaysAssert(iter.endPosition() == expectedPos, AipsError); expectedPos(0) = 0; expectedPos(1) = 0; expectedPos(2) = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedResult(0,0,0) = 4; for (; !iter.atStart(); iter--){ AlwaysAssert(allEQ(expectedResult, iter.cursor().nonDegenerate()) == True, AipsError); iter.woCursor() = 5; } clock.show(); nstep = iter.nsteps(); AlwaysAssert(nstep == 2*(latticeShape(3)), AipsError); expectedPos = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedPos(0) = latticeShape(0) - 1; expectedPos(1) = latticeShape(1) - 1; expectedPos(2) = latticeShape(2) - 1; AlwaysAssert(iter.endPosition() == expectedPos, AipsError); } void testArrayRWIter (Lattice& lattice, Bool useRef) { cout << " Testing using an Array (4-D) cursor" << endl; Int nstep; const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(latticeShape); LatticeIterator iter(lattice, cursorShape, useRef); Array expectedResult(latticeShape); expectedResult = 5; AlwaysAssert(allEQ(expectedResult, iter.cursor()) == True, AipsError); try { Vector temp(iter.vectorCursor()); throw(AipsError("tLatticeIterator - " "vectorCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("one non-degenerate")) { throw (AipsError (x.what())); } } try { Matrix temp(iter.matrixCursor()); throw(AipsError("tLatticeIterator - " "matrixCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("two non-degenerate")) { throw (AipsError (x.what())); } } try { Cube temp(iter.cubeCursor()); throw(AipsError("tLatticeIterator - " "cubeCursor worked where it should not have")); } catch (std::exception& x) { if (!String(x.what()).contains("three non-degenerate")) { throw (AipsError (x.what())); } } Timer clock; for (iter.reset(); !iter.atEnd(); ++iter){ AlwaysAssert(allEQ(expectedResult, iter.cursor()) == True, AipsError); iter.rwCursor()(IPosition(4,0)) = 6; } nstep = iter.nsteps(); AlwaysAssert(nstep == 1, AipsError); IPosition expectedPos(latticeShape-1); AlwaysAssert(iter.endPosition() == expectedPos, AipsError); expectedPos = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedResult(IPosition(4,0)) = 6; for (; !iter.atStart(); --iter){ AlwaysAssert(allEQ(expectedResult, iter.cursor()) == True, AipsError); iter.woCursor() = 7; } clock.show(); nstep = iter.nsteps(); AlwaysAssert(nstep == 2, AipsError); expectedPos = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); expectedPos = latticeShape - 1; AlwaysAssert(iter.endPosition() == expectedPos, AipsError); } void testCopyAssignRWIter (Lattice& lattice, Bool useRef) { cout << " Testing the copy constructor and assignment operator" << endl; const IPosition latticeShape(lattice.shape()); const IPosition cursorShape(1,latticeShape(0)); LatticeIterator iter(lattice, LatticeStepper(latticeShape, cursorShape), useRef); iter++; Vector expectedResult(latticeShape(0)); expectedResult = 7; AlwaysAssert(allEQ(expectedResult,iter.vectorCursor()) == True, AipsError); LatticeIterator iterCopy(iter.copy()); AlwaysAssert(allEQ(expectedResult, iterCopy.vectorCursor()) == True, AipsError); iter++; iter.woCursor() = 2; expectedResult = 2; AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); expectedResult = 7; AlwaysAssert(allEQ(expectedResult, iterCopy.vectorCursor()) == True, AipsError); iterCopy--; iterCopy.woCursor() = 0; expectedResult = 2; AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); expectedResult = 0; AlwaysAssert(allEQ(expectedResult, iterCopy.vectorCursor()) == True, AipsError); iterCopy = iter.copy(); AlwaysAssert(allEQ(iter.vectorCursor(), iterCopy.vectorCursor()) == True, AipsError); expectedResult = 2; AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); iter++; expectedResult = 7; AlwaysAssert(allEQ(expectedResult, iter.vectorCursor()) == True, AipsError); expectedResult = 2; AlwaysAssert(allEQ(expectedResult, iterCopy.vectorCursor()) == True, AipsError); --iterCopy; iterCopy--; expectedResult = 0; AlwaysAssert(allEQ(expectedResult, iterCopy.vectorCursor()) == True, AipsError); } void testNonCongruentRWIter (Lattice& lattice, Bool useRef) { cout << " Testing using a non-congruent cursor" << endl; const IPosition latticeShape(lattice.shape()); { Array arr; lattice.getSlice(arr, IPosition(latticeShape.nelements(), 0), latticeShape, IPosition(latticeShape.nelements(), 1)); indgen(arr); lattice.putSlice(arr, IPosition(latticeShape.nelements(), 0)); } IPosition cursorShape(2,9); LatticeStepper step(latticeShape, cursorShape); LatticeIterator iter(lattice, step, useRef); Matrix expectedResult1(cursorShape); Vector oneRow(cursorShape(0)); indgen(oneRow); uInt i; for (i = 0; i < uInt(cursorShape(1)); i++) { expectedResult1.column(i) = oneRow; oneRow += Int(latticeShape(0)); } AlwaysAssert(allEQ(expectedResult1, iter.cursor().nonDegenerate()), AipsError); iter.woCursor() = (-1 * iter.cursor() - 1); iter++; Matrix expectedResult2(cursorShape); indgen(oneRow, Int(cursorShape(0))); for (i = 0; i < uInt(cursorShape(1)); i++) { oneRow(7) = 0; oneRow(8) = 0; expectedResult2.column(i) = oneRow; oneRow += Int(latticeShape(0)); } AlwaysAssert(allEQ(expectedResult2, iter.cursor().nonDegenerate()), AipsError); iter.woCursor() = (-1 * iter.cursor() - 1); iter++; Matrix expectedResult3(cursorShape); expectedResult3 = 0; indgen(oneRow, Int(cursorShape(0)*latticeShape(0))); for (i = 0; i < 3; i++) { expectedResult3.column(i) = oneRow; oneRow += Int(latticeShape(0)); } AlwaysAssert(allEQ(expectedResult3, iter.cursor().nonDegenerate()), AipsError); iter.woCursor() = (-1 * iter.cursor() - 1); iter++; Matrix expectedResult4(cursorShape); expectedResult4 = 0; indgen(oneRow, Int(cursorShape(0)*(latticeShape(0)+1))); for (i = 0; i < 3; i++) { oneRow(7) = 0; oneRow(8) = 0; expectedResult4.column(i) = oneRow; oneRow += Int(latticeShape(0)); } AlwaysAssert(allEQ(expectedResult4, iter.cursor().nonDegenerate()), AipsError); iter.woCursor() = (-1 * iter.cursor() - 1); iter--; iter++; iter.rwMatrixCursor() += expectedResult4; { Array m(iter.rwMatrixCursor()(IPosition(2,0),IPosition(2,6,2))); m += 1; AlwaysAssert(allEQ(iter.cursor(), 0), AipsError); } iter--; iter.rwMatrixCursor() += expectedResult3; { Array m(iter.rwMatrixCursor()(IPosition(2,0),IPosition(2,8,2))); m += 1; AlwaysAssert(allEQ(iter.cursor(), 0), AipsError); } iter--; iter.rwMatrixCursor() += expectedResult2; { Array m(iter.rwMatrixCursor()(IPosition(2,0),IPosition(2,6,8))); m += 1; AlwaysAssert(allEQ(iter.cursor(), 0), AipsError); } iter--; iter.rwMatrixCursor() += expectedResult1; iter.rwCursor() += 1; AlwaysAssert(allEQ(iter.cursor(), 0), AipsError); { Array arr; lattice.getSlice(arr, IPosition(latticeShape.nelements(), 0), latticeShape, IPosition(latticeShape.nelements(), 1)); indgen(arr); lattice.putSlice(arr, IPosition(latticeShape.nelements(), 0)); } cursorShape = 5; step.setCursorShape(cursorShape); step.subSection(IPosition(4, 3,0,0,0), latticeShape-1, IPosition(4, 2,2,1,1)); LatticeIterator subIter(lattice, step, useRef); oneRow.resize(5); Matrix expectedResulta(5,5); expectedResulta = 0; indgen(oneRow, 3, 2); for (i = 0; i < 5; i++) { expectedResulta.column(i) = oneRow; oneRow += 32; } AlwaysAssert(allEQ(expectedResulta, subIter.cursor().nonDegenerate()), AipsError); subIter.woCursor() = (-1 * subIter.cursor() - 1); subIter++; Matrix expectedResultb(5,5); expectedResultb = 0; indgen(oneRow, 13, 2); for (i = 0; i < 5; i++) { oneRow(2) = 0; oneRow(3) = 0; oneRow(4) = 0; expectedResultb.column(i) = oneRow; oneRow += 32; } AlwaysAssert(allEQ(expectedResultb, subIter.cursor().nonDegenerate()), AipsError); subIter.woCursor() = (-1 * subIter.cursor() - 1); subIter++; Matrix expectedResultc(5,5); expectedResultc = 0; indgen(oneRow, 163, 2); for (i = 0; i < 1; i++) { expectedResultc.column(i) = oneRow; oneRow += 32; } AlwaysAssert(allEQ(expectedResultc, subIter.cursor().nonDegenerate()), AipsError); subIter.woCursor() = (-1 * subIter.cursor() - 1); subIter++; Matrix expectedResultd(5,5); expectedResultd = 0; indgen(oneRow, 173, 2); for (i = 0; i < 1; i++) { oneRow(2) = 0; oneRow(3) = 0; oneRow(4) = 0; expectedResultd.column(i) = oneRow; oneRow += 32; } AlwaysAssert(allEQ(expectedResultd, subIter.cursor().nonDegenerate()), AipsError); subIter.woCursor() = (-1 * subIter.cursor() - 1); subIter--; subIter++; Array arr; lattice.getSlice(arr, IPosition(latticeShape.nelements(), 0), IPosition(4,16,12,1,1), IPosition(latticeShape.nelements(), 1)); AlwaysAssert(arr(IPosition(4,0)) == 0, AipsError); AlwaysAssert(arr(IPosition(4,1,0,0,0)) == 1, AipsError); AlwaysAssert(arr(IPosition(4,2,0,0,0)) == 2, AipsError); AlwaysAssert(arr(IPosition(4,3,0,0,0)) == -4, AipsError); AlwaysAssert(arr(IPosition(4,13,0,0,0)) == -14, AipsError); AlwaysAssert(arr(IPosition(4,14,0,0,0)) == 14, AipsError); AlwaysAssert(arr(IPosition(4,2,10,0,0)) == 162, AipsError); AlwaysAssert(arr(IPosition(4,3,10,0,0)) == -164, AipsError); AlwaysAssert(arr(IPosition(4,3,11,0,0)) == 179, AipsError); AlwaysAssert(arr(IPosition(4,15,10,0,0)) == -176, AipsError); AlwaysAssert(arr(IPosition(4,15,11,0,0)) == 191, AipsError); } void testAdd (Lattice& lat1, Lattice& lat2, Bool useRef) { { PagedArray* pa1 = dynamic_cast*> (&lat1); if (pa1) pa1->clearCache(); PagedArray* pa2 = dynamic_cast*> (&lat2); if (pa2) pa2->clearCache(); Timer timer; LatticeIterator lat1Iter (lat1, useRef); // Create dummy lat2Iter to setup cache correctly. // It may not be necessary, because the Table getSlice function // will setup the cache on its first access. RO_LatticeIterator lat2Iter (lat2, lat1.niceCursorShape(), useRef); Array lat2Buffer; while (! lat1Iter.atEnd()) { // Do separate getSlice to use reference semantics if // lat2 is an ArrayLattice. // Note that it requires lat2 to be non-const. lat2.getSlice (lat2Buffer, lat1Iter.position(), lat1Iter.cursorShape()); lat1Iter.rwCursor() += lat2Buffer; lat1Iter++; } timer.show (" iter-get "); ///if (pa1) pa1->showCacheStatistics (cout); ///if (pa2) pa2->showCacheStatistics (cout); } { Timer timer; // This iterator uses the TileStepper. LatticeIterator lat1Iter (lat1, useRef); // Use tile shape of lat1, because they have to be iterated // in the same way. The cursor has to be resized if needed. RO_LatticeIterator lat2Iter (lat2, LatticeStepper (lat1.shape(), lat1.niceCursorShape(), LatticeStepper::RESIZE), useRef); while (! lat1Iter.atEnd()) { lat1Iter.rwCursor() += lat2Iter.cursor(); lat1Iter++; lat2Iter++; } timer.show (" iter-iter"); } { Timer timer; LatticeIterator lat1Iter (lat1, useRef); Array lat2Buffer; while (! lat1Iter.atEnd()) { // Do separate getSlice to use reference semantics if // lat2 is an ArrayLattice. // Note that it requires lat2 to be non-const. lat2.getSlice (lat2Buffer, lat1Iter.position(), lat1Iter.cursorShape()); lat1Iter.rwCursor() += lat2Buffer; lat1Iter++; } timer.show (" iter-get "); } } int main (int argc, const char* argv[]) { try { { cout << "Creating a PagedArray on disk" << endl; const TiledShape latticeShape(IPosition(4, 16, 12, 4, 32), IPosition(4, 16, 12, 2, 1)); PagedArray pagedArr(latticeShape, "tLatticeIterator_tmp.table"); Array arr(latticeShape.shape()); indgen(arr); pagedArr.putSlice(arr, IPosition(latticeShape.shape().nelements(), 0)); } //++++++++++++++++++++ Test PagedArrIter ++++++++++++++++++++ cout << " Testing the RO iterator" << endl; // Check the Iterator with a Vector cursor. { const PagedArray pagedArr("tLatticeIterator_tmp.table"); testVectorROIter (pagedArr, False); testVectorROIter (pagedArr, True); } // Check the Iterator with a Matrix cursor. { const PagedArray pagedArr("tLatticeIterator_tmp.table"); testMatrixROIter (pagedArr, False); testMatrixROIter (pagedArr, True); } // Check the Iterator with a Cube cursor. { const PagedArray pagedArr("tLatticeIterator_tmp.table"); testCubeROIter (pagedArr, False); testCubeROIter (pagedArr, True); } // Check the Iterator with an Array cursor. { const PagedArray pagedArr("tLatticeIterator_tmp.table"); testArrayROIter (pagedArr, False); testArrayROIter (pagedArr, True); } // Check the Iterator with an 8 element element cursor. { const PagedArray pagedArr("tLatticeIterator_tmp.table"); test8ElemROIter (pagedArr, False); test8ElemROIter (pagedArr, True); } // Check the Iterator with a tile cursor. { const PagedArray pagedArr("tLatticeIterator_tmp.table"); testTileROIter (pagedArr, False); testTileROIter (pagedArr, True); } // Check the Iterator with a tiled line cursor. { const PagedArray pagedArr("tLatticeIterator_tmp.table"); testTiledLineROIter (pagedArr, False); testTiledLineROIter (pagedArr, True); } // Check the copy constructor and assignment operator { const PagedArray pagedArr("tLatticeIterator_tmp.table"); testCopyAssignROIter (pagedArr, False); testCopyAssignROIter (pagedArr, True); } // Test the non-congruent cursor handling { const PagedArray pagedArr("tLatticeIterator_tmp.table"); testNonCongruentROIter (pagedArr, False); testNonCongruentROIter (pagedArr, True); } cout << " Testing the RW iterator" << endl; // Check the Iterator with a Vector cursor. { PagedArray pagedArr("tLatticeIterator_tmp.table"); Array savarr = pagedArr.get(); testVectorRWIter (pagedArr, False); pagedArr.put (savarr); testVectorRWIter (pagedArr, True); } // Check the Iterator with a Matrix cursor. { PagedArray pagedArr("tLatticeIterator_tmp.table"); Array savarr = pagedArr.get(); testMatrixRWIter (pagedArr, False); pagedArr.put (savarr); testMatrixRWIter (pagedArr, True); } // Check the Iterator with a Cube cursor. { PagedArray pagedArr("tLatticeIterator_tmp.table"); Array savarr = pagedArr.get(); testCubeRWIter (pagedArr, False); pagedArr.put (savarr); testCubeRWIter (pagedArr, True); } // Check the Iterator with an Array cursor. { PagedArray pagedArr("tLatticeIterator_tmp.table"); Array savarr = pagedArr.get(); testArrayRWIter (pagedArr, False); pagedArr.put (savarr); testArrayRWIter (pagedArr, True); } // Check the copy constructor and assignment operator { PagedArray pagedArr("tLatticeIterator_tmp.table"); Array savarr = pagedArr.get(); testCopyAssignRWIter (pagedArr, False); pagedArr.put (savarr); testCopyAssignRWIter (pagedArr, True); } // Test the non-congruent cursor handling { PagedArray pagedArr("tLatticeIterator_tmp.table"); Array savarr = pagedArr.get(); testNonCongruentRWIter (pagedArr, False); pagedArr.put (savarr); testNonCongruentRWIter (pagedArr, True); } } catch (std::exception& x) { cerr << "Caught exception: " << x.what() << endl; cout << "FAIL" << endl; return 1; } try { cout << "Creating an ArrayLattice" << endl; const IPosition latticeShape(4, 16, 12, 2, 32); ArrayLattice refLattice(latticeShape); { Array arr(latticeShape); indgen(arr); refLattice.putSlice(arr, IPosition(latticeShape.nelements(), 0)); } //++++++++++++++++++++ Test ArrLatticeIter ++++++++++++++++++++ // Check the Iterator with a Vector cursor. cout << " Testing the RO iterator" << endl; { const ArrayLattice arrLattice(refLattice); testVectorROIter (arrLattice, False); testVectorROIter (arrLattice, True); } // Check the Iterator with a Matrix cursor. { const ArrayLattice arrLattice(refLattice); testMatrixROIter (arrLattice, False); testMatrixROIter (arrLattice, True); } // Check the Iterator with a Cube cursor. { const ArrayLattice arrLattice(refLattice); testCubeROIter (arrLattice, False); testCubeROIter (arrLattice, True); } // Check the Iterator with an Array cursor. { const ArrayLattice arrLattice(refLattice); testArrayROIter (arrLattice, False); testArrayROIter (arrLattice, True); } // Check the Iterator with an 8 element element cursor. { const ArrayLattice arrLattice(refLattice); test8ElemROIter (arrLattice, False); test8ElemROIter (arrLattice, True); } // Check the Iterator with a tile cursor. { const ArrayLattice arrLattice(refLattice); testTileROIter (arrLattice, False); testTileROIter (arrLattice, True); } // Check the Iterator with a tiled line cursor. { const ArrayLattice arrLattice(refLattice); testTiledLineROIter (arrLattice, False); testTiledLineROIter (arrLattice, True); } // Check the copy constructor and assignment operator { const ArrayLattice arrLattice(refLattice); testCopyAssignROIter (arrLattice, False); testCopyAssignROIter (arrLattice, True); } // Test the non-congruent cursor handling { const ArrayLattice arrLattice(refLattice); testNonCongruentROIter (arrLattice, False); testNonCongruentROIter (arrLattice, True); } cout << " Testing the RW iterator" << endl; // Check the Iterator with a Vector cursor. { ArrayLattice arrLattice(refLattice); Array savarr = arrLattice.get(); testVectorRWIter (arrLattice, False); arrLattice.put (savarr); testVectorRWIter (arrLattice, True); } // Check the Iterator with a Matrix cursor. { ArrayLattice arrLattice(refLattice); Array savarr = arrLattice.get(); testMatrixRWIter (arrLattice, False); arrLattice.put (savarr); testMatrixRWIter (arrLattice, True); } // Check the Iterator with a Cube cursor. { ArrayLattice arrLattice(refLattice); Array savarr = arrLattice.get(); testCubeRWIter (arrLattice, False); arrLattice.put (savarr); testCubeRWIter (arrLattice, True); } // Check the Iterator with an Array cursor. { ArrayLattice arrLattice(refLattice); Array savarr = arrLattice.get(); testArrayRWIter (arrLattice, False); arrLattice.put (savarr); testArrayRWIter (arrLattice, True); } // Check the copy constructor and assignment operator { ArrayLattice arrLattice(refLattice); Array savarr = arrLattice.get(); testCopyAssignRWIter (arrLattice, False); arrLattice.put (savarr); testCopyAssignRWIter (arrLattice, True); } // Test the non-congruent cursor handling { ArrayLattice arrLattice(refLattice); Array savarr = arrLattice.get(); testNonCongruentRWIter (arrLattice, False); arrLattice.put (savarr); testNonCongruentRWIter (arrLattice, True); } // Test some performance aspects. { Input inp(1); inp.version(" "); inp.create("nx", "512", "Number of pixels along the x-axis", "int"); inp.create("ny", "512", "Number of pixels along the y-axis", "int"); inp.readArguments(argc, argv); const uInt nx=inp.getInt("nx"); const uInt ny=inp.getInt("ny"); IPosition shape(2,nx,ny); TiledShape tshape(shape, IPosition(2,nx,1)); PagedArray pagedArr1(tshape, "tLatticeIterator_tmp.tab1"); PagedArray pagedArr2(tshape, "tLatticeIterator_tmp.tab2"); ArrayLattice latArr1(shape); ArrayLattice latArr2(shape); Array arr(latArr1.shape()); indgen(arr); pagedArr1.put (arr); pagedArr2.put (arr); latArr1.put (arr); latArr2.put (arr); cout << "Shape " << shape << endl; cout << "paged+=paged useRef=False" << endl; testAdd (pagedArr1, pagedArr2, False); AlwaysAssert (allEQ(pagedArr1.get(), 4*arr), AipsError); cout << "paged+=paged useRef=True" << endl; testAdd (pagedArr1, pagedArr2, True); AlwaysAssert (allEQ(pagedArr1.get(), 7*arr), AipsError); cout << "array+=array useRef=False" << endl; testAdd (latArr1, latArr2, False); AlwaysAssert (allEQ(latArr1.get(), 4*arr), AipsError); cout << "array+=array useRef=True" << endl; testAdd (latArr1, latArr2, True); AlwaysAssert (allEQ(latArr1.get(), 7*arr), AipsError); cout << "paged+=array useRef=False" << endl; testAdd (pagedArr1, latArr2, False); AlwaysAssert (allEQ(pagedArr1.get(), 10*arr), AipsError); cout << "paged+=array useRef=True" << endl; testAdd (pagedArr1, latArr2, True); AlwaysAssert (allEQ(pagedArr1.get(), 13*arr), AipsError); cout << "lat+=paged useRef=False" << endl; testAdd (latArr1, pagedArr2, False); AlwaysAssert (allEQ(latArr1.get(), 10*arr), AipsError); cout << "lat+=paged useRef=True" << endl; testAdd (latArr1, pagedArr2, True); AlwaysAssert (allEQ(latArr1.get(), 13*arr), AipsError); } } catch (std::exception& x) { cerr << "Caught exception: " << x.what() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/lattices/Lattices/test/tLatticeLocker.cc000066400000000000000000000075741476623553700231200ustar00rootroot00000000000000//# tLatticeLocker: Interactive test program for concurrent access to lattices //# Copyright (C) 1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include // This program tests concurrent access to lattices. void b() { // Open the table for update with UserLocking. Table tab ("tLatticeLocker_tmp.data", TableLock(TableLock::UserLocking), Table::Update); PagedArray pa(tab); LatticeLocker* latlock[10]; uInt nrll = 0; Array arr(IPosition(2,4,4)); arr = 0; Int val; Int opt; while (True) { cout << "0=quit, 1=rdlock, 2=wrlock, 3=get, 4=put, 5=unlock, 6=hasrl, " "7=haswl: "; cin >> opt; if (opt == 1) { if (nrll >= 10) { cout << "Cannot lock; already 10 lock objects in use" << endl; } else { try { latlock[nrll] = new LatticeLocker (pa, FileLocker::Read, 1); nrll++; } catch (std::exception& x) { cout << x.what() << endl; } } } else if (opt == 2) { if (nrll >= 10) { cout << "Cannot lock; already 10 lock objects in use" << endl; } else { try { latlock[nrll] = new LatticeLocker (pa, FileLocker::Write,1); nrll++; } catch (std::exception& x) { cout << x.what() << endl; } } } else if (opt == 3 || opt == 4) { if (! tab.hasLock ((opt==4))) { cout << "Cannot get/put; lattice is not (correctly) locked" << endl; } else { if (opt == 4) { cout << "value: "; cin >> val; arr = val; pa.put (arr); } else { pa.get (arr); cout << "lattice value = " << arr(IPosition(2,0,0)) << endl; } } } else if (opt == 5) { if (nrll > 0) { nrll--; delete latlock[nrll]; } else { cout << "no more lock objects" << endl; } } else if (opt == 6) { cout << "hasReadLock = " << pa.hasLock (FileLocker::Read) << endl; } else if (opt == 7) { cout << "hasWriteLock = " << pa.hasLock (FileLocker::Write) << endl; } else { break; } } for (uInt i=0; i pa(IPosition(2,4,4), "tLatticeLocker_tmp.data"); } b(); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } } return 0; // exit with success status } casacore-3.7.1/lattices/Lattices/test/tLatticePerf.cc000066400000000000000000000123071476623553700225630ustar00rootroot00000000000000//# tLatticePerf.cc: Test performance of lattices //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include //

        // Test program for performance of PagedArray and HDF5Lattice // // Create the lattice cube. void makeCube (bool useHDF, const IPosition& cubeShape, const IPosition& tileShape) { TiledShape tshape(cubeShape, tileShape); Lattice* lattice = 0; if (useHDF) { cout << "Creating tLatticePerf_tmp.hdf with shape " << cubeShape << " and tile shape " << tileShape << endl; cout << "HDF5 "; lattice = new HDF5Lattice(tshape, "tLatticePerf_tmp.hdf"); } else { cout << "Creating tLatticePerf_tmp.tab with shape " << cubeShape << " and tile shape " << tileShape << endl; cout << "CCTS "; lattice = new PagedArray (tshape, "tLatticePerf_tmp.tab"); } Timer timer; lattice->set (0); delete lattice; timer.show ("create "); } void getLine (const Lattice& lattice, uInt axis) { Timer timer; TiledLineStepper nav(lattice.shape(), lattice.niceCursorShape(), axis); RO_LatticeIterator iter(lattice, nav); for (iter.reset(); !iter.atEnd(); iter++) { iter.cursor(); } timer.show ("getLine "); } void getPlane (const Lattice& lattice, uInt nonAxis) { Timer timer; IPosition cursorShape = lattice.shape(); cursorShape[nonAxis] = 1; LatticeStepper nav(lattice.shape(), cursorShape); RO_LatticeIterator iter(lattice, nav); for (iter.reset(); !iter.atEnd(); iter++) { iter.cursor(); } timer.show ("getPlane"); } void getTiles (const Lattice& lattice) { Timer timer; TileStepper nav(lattice.shape(), lattice.niceCursorShape()); RO_LatticeIterator iter(lattice, nav); for (iter.reset(); !iter.atEnd(); iter++) { iter.cursor(); } timer.show ("getTiles"); } void getCube (const Lattice& lattice, const String& trav) { if (trav == "x") { cout << "x "; getLine (lattice, 0); } else if (trav == "y") { cout << "y "; getLine (lattice, 1); } else if (trav == "z") { cout << "z "; getLine (lattice, 2); } else if (trav == "xy") { cout << "xy "; getPlane (lattice, 2); } else if (trav == "xz") { cout << "xz "; getPlane (lattice, 1); } else if (trav == "yz") { cout << "yz "; getPlane (lattice, 0); } else { cout << " "; getTiles (lattice); } } int main (int argc, char* argv[]) { if (argc <= 1) { cerr << "Run as: tLatticePerf nx ny nz ntx nty ntz [hdf5] to create" << endl; cerr << "or tLatticePerf type [hdf5] to read back" << endl; cerr << " hdf5 1 use HDF5Lattice" < (is default)" << endl; cerr << " type x,y,z read vectors along this axis" << endl; cerr << " xy,xz,yz read planes along these axes" << endl; cerr << " else read tile by tile" << endl; exit(0); } try { if (argc > 6) { IPosition cubeShape(3, atoi(argv[1]), atoi(argv[2]), atoi(argv[3])); IPosition tileShape(3, atoi(argv[4]), atoi(argv[5]), atoi(argv[6])); Bool useHDF = (argc > 7 && argv[7][0] == '1'); makeCube (useHDF, cubeShape, tileShape); } else { Bool useHDF = (argc > 2 && argv[2][0] == '1'); if (useHDF) { cout << "HDF5 "; getCube (HDF5Lattice("tLatticePerf_tmp.hdf"), argv[1]); } else { cout << "CCTS "; getCube (PagedArray("tLatticePerf_tmp.tab"), argv[1]); } } } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } casacore-3.7.1/lattices/Lattices/test/tLatticeStepper.cc000066400000000000000000000317721476623553700233200ustar00rootroot00000000000000//# tLatticeStepper.cc: mechanical test of LatticeLayout class //# Copyright (C) 1995,1996,1997,1998,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include int main() { try { // typical lattice and stepper specifications IPosition latticeShape(4,10,12,4,7); IPosition stepperShape(1,4); IPosition stepperOrientation(4,0,1,2,3); // Check the exception handling LogIO logger(LogOrigin("tLatticeStepper")); logger << "Expect to see a number of SEVERE errors logged shortly" << " (if in Debug mode)" << LogIO::POST; { IPosition badCursor(4,1); IPosition smallLatticeShape(3, latticeShape(0), latticeShape(1), latticeShape(2)); try { // test the check for an bad cursor dimension LatticeStepper demented(smallLatticeShape, badCursor); cout << "'more axes than lattice' exception expected" << endl; return 1; } catch (std::exception& x) { if (!String(x.what()).contains("more axes than lattice")) { cout << x.what() << endl << "FAIL" << endl; return 1; } } IPosition bigCursor(4,12,1,1,1); try { // test the check for an bad cursor size (upper bound exceeded) LatticeStepper demented(latticeShape, bigCursor, stepperOrientation); cout << "'upper bound exceeded' exception expected" << endl; return 1; } catch (std::exception& x) { if (!String(x.what()).contains("> latticeShape")) { cout << x.what() << endl << "FAIL" << endl; return 1; } } IPosition zeroCursor(4,0); try { // test the check for an bad cursor size (lower bound exceeded) LatticeStepper demented(latticeShape, zeroCursor,stepperOrientation); cout << "'lower bound exceeded' exception expected" << endl; return 1; } catch (std::exception& x) { if (!String(x.what()).contains("cursorShape <=0")) { cout << x.what() << endl << "FAIL" << endl; return 1; } } IPosition badOrientation1(4,1,2,3,4); try { // test the check for an bad orientation bounds LatticeStepper demented(latticeShape, stepperShape, badOrientation1); cout << "'bad orientation' exception 1 expected" << endl; return 1; } catch (std::exception& x) { if (!String(x.what()).contains("makeAxisPath")){ cout << x.what() << endl << "FAIL" << endl; return 1; } } IPosition badOrientation2(4,0,2,2,3); try { // test the check for an bad orientation contents LatticeStepper demented(latticeShape, stepperShape, badOrientation2); cout << "'bad orientation' exception 2 expected" << endl; return 1; } catch (std::exception& x) { if (!String(x.what()).contains("makeAxisPath")){ cout << x.what() << endl << "FAIL" << endl; return 1; } } try { LatticeStepper step3(IPosition(4,2,3,4,5), IPosition(), IPosition(2,1,2), IPosition()); cout << "'no cursor shape' exception expected" << endl; return 1; } catch (std::exception& x) { } try { LatticeStepper step3(IPosition(4,2,3,4,5), IPosition(5,1), IPosition(2,1,2), IPosition()); cout << "'too long cursor shape' exception expected" << endl; return 1; } catch (std::exception& x) { } try { LatticeStepper step3(IPosition(4,2,3,4,5), IPosition(2,1), IPosition(5,1), IPosition()); cout << "'too long cursor axes' exception expected" << endl; return 1; } catch (std::exception& x) { } try { LatticeStepper step3(IPosition(4,2,3,4,5), IPosition(2,1), IPosition(3,1), IPosition()); cout << "'unequal cursor axes' exception expected" << endl; return 1; } catch (std::exception& x) { } try { LatticeStepper step3(IPosition(4,2,3,4,5), IPosition(2,1), IPosition(2,4), IPosition()); cout << "'too high cursor axes' exception expected" << endl; return 1; } catch (std::exception& x) { } try { LatticeStepper step3(IPosition(4,2,3,4,5), IPosition(4,2,3,4,1), IPosition(2,1,2), IPosition()); cout << "'> length 1' exception expected" << endl; return 1; } catch (std::exception& x) { } try { LatticeStepper step3(IPosition(4,2,3,4,5), IPosition(2,1), IPosition(2,2,1), IPosition()); cout << "'non ascending order' exception expected" << endl; return 1; } catch (std::exception& x) { } } logger << "End of section which checks error detection" << LogIO::POST; // Try LatticeStepper with cursorAxes given. { LatticeStepper step1(IPosition(4,2,3,4,5), IPosition(2,3,4), IPosition(2,1,2), IPosition()); AlwaysAssertExit (step1.cursorShape() == IPosition(4,1,3,4,1)); AlwaysAssertExit (step1.cursorAxes() == IPosition(2,1,2)); LatticeStepper step2(IPosition(4,2,3,4,5), IPosition(4,1,3,4,1), IPosition(2,1,2), IPosition()); AlwaysAssertExit (step2.cursorShape() == IPosition(4,1,3,4,1)); AlwaysAssertExit (step2.cursorAxes() == IPosition(2,1,2)); LatticeStepper step3(IPosition(4,2,3,4,5), IPosition(4,1,3,4,1), IPosition(), IPosition()); AlwaysAssertExit (step3.cursorShape() == IPosition(4,1,3,4,1)); AlwaysAssertExit (step3.cursorAxes() == IPosition(2,1,2)); } uInt count = 0; // Try the simplest thing moving forward with a one-dimensional congruent // cursor LatticeStepper huh(latticeShape, IPosition(1,10)); for (huh.reset(); !huh.atEnd(); huh++) count++; // Check the cursor is at the end AlwaysAssert(huh.endPosition().isEqual(IPosition(4,9,11,3,6)), AipsError); AlwaysAssert(count == 12*4*7, AipsError); AlwaysAssert(huh.nsteps() == 12*4*7, AipsError); // Now move back again with the same cursor for ( ; !huh.atStart(); huh--) count--; // Should end up where we started AlwaysAssert(huh.position().isEqual(IPosition(4,0)), AipsError); AlwaysAssert(count == 0, AipsError); AlwaysAssert(huh.nsteps() == 2*12*4*7, AipsError); // Test moving forward with a one-dimensional NON-congruent cursor // Check the first few steps manually; LatticeStepper s0(latticeShape, stepperShape, stepperOrientation); AlwaysAssert(s0.position().isEqual(IPosition(4,0,0,0,0)), AipsError); AlwaysAssert(s0.endPosition().isEqual(IPosition(4,3,0,0,0)), AipsError); s0++; AlwaysAssert(s0.position().isEqual(IPosition(4,4,0,0,0)), AipsError); AlwaysAssert(s0.endPosition().isEqual(IPosition(4,7,0,0,0)), AipsError); s0++; AlwaysAssert(s0.position().isEqual(IPosition(4,8,0,0,0)), AipsError); AlwaysAssert(s0.endPosition().isEqual(IPosition(4,9,0,0,0)), AipsError); s0++; AlwaysAssert(s0.position().isEqual(IPosition(4,0,1,0,0)), AipsError); AlwaysAssert(s0.endPosition().isEqual(IPosition(4,3,1,0,0)), AipsError); s0++; AlwaysAssert(s0.position().isEqual(IPosition(4,4,1,0,0)), AipsError); AlwaysAssert(s0.endPosition().isEqual(IPosition(4,7,1,0,0)), AipsError); count = 0; for (s0.reset(); !s0.atEnd(); s0++) count++; // Check the cursor is at the end (note the overhang) AlwaysAssert(s0.endPosition().isEqual(IPosition(4,9,11,3,6)), AipsError); AlwaysAssert(count == 12*4*7*3, AipsError); for (; !s0.atStart(); s0--) count--; // Should end up where we started AlwaysAssert(s0.position().isEqual(IPosition(4,0)), AipsError); AlwaysAssert(count == 0, AipsError); AlwaysAssert(s0.nsteps() == 12*4*7*3*2, AipsError); // Test the copy constructor and assignment operator use copy semantics LatticeStepper s1(s0); s1++; LatticeStepper s2(s0); // Cannot use the default constructor; s2 = s1; s2++; AlwaysAssert(s0.position().isEqual(IPosition(4,0)), AipsError); AlwaysAssert(s1.position().isEqual(IPosition(4,4,0,0,0)), AipsError); AlwaysAssert(s2.position().isEqual(IPosition(4,8,0,0,0)), AipsError); // Check the hangover function AlwaysAssert(s0.hangOver() == False, AipsError); AlwaysAssert(s1.hangOver() == False, AipsError); AlwaysAssert(s2.hangOver() == True, AipsError); // Check that things work with the RESIZE cursor LatticeStepper s3(latticeShape, stepperShape, LatticeStepper::RESIZE); AlwaysAssert(s3.hangOver() == False, AipsError); AlwaysAssert(s3.position() == IPosition(4,0), AipsError); AlwaysAssert(s3.endPosition() == IPosition(4,3,0,0,0), AipsError); AlwaysAssert(s3.cursorShape() == IPosition(4,4,1,1,1), AipsError); s3++; s3++; AlwaysAssert(s3.hangOver() == True, AipsError); AlwaysAssert(s3.position() == IPosition(4,8,0,0,0), AipsError); AlwaysAssert(s3.endPosition() == IPosition(4,9,0,0,0), AipsError); AlwaysAssert(s3.cursorShape() == IPosition(4,2,1,1,1), AipsError); LatticeStepper s4(latticeShape, stepperShape, stepperOrientation, LatticeStepper::RESIZE); s4.subSection(IPosition(4,1,0,0,0), IPosition(4,9,0,0,0), IPosition(4,2,1,1,1)); AlwaysAssert(s4.hangOver() == False, AipsError); AlwaysAssert(s4.relativePosition() == IPosition(4,0), AipsError); AlwaysAssert(s4.position() == IPosition(4,1,0,0,0), AipsError); AlwaysAssert(s4.relativeEndPosition() == IPosition(4,3,0,0,0), AipsError); AlwaysAssert(s4.endPosition() == IPosition(4,7,0,0,0), AipsError); AlwaysAssert(s4.cursorShape() == IPosition(4,4,1,1,1), AipsError); s4++; AlwaysAssert(s4.hangOver() == True, AipsError); AlwaysAssert(s4.relativePosition() == IPosition(4,4,0,0,0), AipsError); AlwaysAssert(s4.position() == IPosition(4,9,0,0,0), AipsError); AlwaysAssert(s4.relativeEndPosition() == IPosition(4,4,0,0,0), AipsError); AlwaysAssert(s4.endPosition() == IPosition(4,9,0,0,0), AipsError); AlwaysAssert(s4.cursorShape() == IPosition(4,1,1,1,1), AipsError); // Check the latticeshape, cursorShape & orientation functions AlwaysAssert(s1.latticeShape() == latticeShape, AipsError); AlwaysAssert(s1.cursorShape().nonDegenerate() == stepperShape, AipsError); AlwaysAssert(s2.axisPath() == stepperOrientation, AipsError); LatticeStepper method(IPosition(3,4,5,6),IPosition(2,4,5)); LatticeNavigator* clonePtr = method.clone(); AlwaysAssert(clonePtr != 0, AipsError); AlwaysAssert(clonePtr->ok() == True, AipsError); AlwaysAssert(clonePtr->latticeShape() == method.latticeShape(), AipsError); AlwaysAssert(clonePtr->cursorShape() == method.cursorShape(), AipsError); AlwaysAssert(clonePtr->position() == method.position(), AipsError); AlwaysAssert(clonePtr->nsteps() == method.nsteps(), AipsError); AlwaysAssert(clonePtr->atStart() == method.atStart(), AipsError); AlwaysAssert(clonePtr->atEnd() == method.atEnd(), AipsError); AlwaysAssert(clonePtr->hangOver() == method.hangOver(), AipsError); // LatticeStepper method(IPosition(3,4,5,6),IPosition(2,4,5)); method.setCursorShape(IPosition(1,4)); method.subSection(IPosition(3,0,1,0), IPosition(3,3,4,3), IPosition(3,1,2,3)); LatticeNavigator* stepPtr = method.clone(); AlwaysAssert(stepPtr->blc() == IPosition(3,0,1,0), AipsError); AlwaysAssert(stepPtr->trc() == IPosition(3,3,3,3), AipsError); AlwaysAssert(stepPtr->increment() == IPosition(3,1,2,3), AipsError); AlwaysAssert(stepPtr->subLatticeShape() == IPosition(3,4,2,2), AipsError); AlwaysAssert(stepPtr->relativePosition() == IPosition(3,0), AipsError); AlwaysAssert(stepPtr->position() == IPosition(3,0,1,0), AipsError); AlwaysAssert(stepPtr->relativeEndPosition() == IPosition(3,3,0,0), AipsError); AlwaysAssert(stepPtr->endPosition() == IPosition(3,3,1,0), AipsError); delete clonePtr; delete stepPtr; } catch (std::exception& x) { cout << x.what() << endl << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } // Local Variables: // compile-command: "gmake OPTLIB=1 tLatticeStepper" // End: casacore-3.7.1/lattices/Lattices/test/tLatticeUtilities.cc000066400000000000000000000140401476623553700236360ustar00rootroot00000000000000//# tLatticeUtilities.cc: //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void doCopy(); void doReplicate(); void doBin(); int main() { try { // Copy doCopy(); // Replicate doReplicate(); // Bin doBin(); } catch (const std::exception& x) { cout<< "FAIL"<< endl; cerr << x.what() << endl; return 1; } cout<< "OK"<< endl; return 0; } void doCopy () { cerr << "copyDataAndMask" << endl; LogIO os(LogOrigin("tLatticeUtilities", "doCopy", WHERE)); // IPosition shape(2, 5, 10); IPosition pos(2,0); ArrayLattice latIn(shape); latIn.set(1.0); SubLattice mLatIn(latIn, True); ArrayLattice maskIn(shape); maskIn.set(True); mLatIn.setPixelMask(maskIn, True); // Unmasked output { cerr << " Unmasked output" << endl; // ArrayLattice latOut(shape); SubLattice mLatOut(latOut, True); // LatticeUtilities::copyDataAndMask (os, mLatOut, mLatIn, False); AlwaysAssert(allNear(mLatOut.get(), Float(1.0), 1.0e-6), AipsError); AlwaysAssert(allEQ(mLatOut.getMask(), True), AipsError); } // { cerr << " Masked output" << endl; // ArrayLattice latOut(shape); SubLattice mLatOut(latOut, True); ArrayLattice latMaskOut(shape); latMaskOut.set(False); mLatOut.setPixelMask(latMaskOut, True); // LatticeUtilities::copyDataAndMask (os, mLatOut, mLatIn, False); AlwaysAssert(allNear(mLatOut.get(), Float(1.0), 1.0e-6), AipsError); AlwaysAssert(allEQ(mLatOut.getMask(), True), AipsError); // Now set one mask value to False so the output pixel should be zero Lattice& pixelMaskIn = mLatIn.pixelMask(); pixelMaskIn.set(True); pixelMaskIn.putAt(False,pos); LatticeUtilities::copyDataAndMask (os, mLatOut, mLatIn, True); // { Array dataOut = mLatOut.get(); Array maskOut = mLatOut.getMask(); AlwaysAssert(near(dataOut(pos), Float(0.0), 1.0e-6), AipsError); AlwaysAssert(maskOut(pos)==False, AipsError); } } } void doReplicate () { cerr << "Replicate" << endl; IPosition shapeLat(2,10,20); ArrayLattice lat(shapeLat); // Just use full lattice. Having the region in the function // call is pretty useless IPosition start(2,0,0); IPosition end(shapeLat-1); Slicer slice(start, end, Slicer::endIsLast); // Replcicate array 4 times { IPosition shapePixels(2,5,10); Array arr(shapePixels); indgen(arr); LatticeUtilities::replicate (lat, slice, arr); // Double tol = 1.0e-6; LatticeStepper stepper(shapeLat, shapePixels, LatticeStepper::RESIZE); LatticeIterator iter(lat, stepper); for (iter.reset(); !iter.atEnd(); iter++) { AlwaysAssert(allNear(iter.cursor(),arr,tol), AipsError); } } // Forced errro { IPosition shapePixels(2,7,13); Array arr(shapePixels); indgen(arr); try { LatticeUtilities::replicate (lat, slice, arr); throw(AipsError("replicate unexpectedly did not fail")); } catch (std::exception& x) { cerr << "Expected error = " << x.what() << endl; } } } void doBin () { cerr << "Bin" << endl; IPosition shape(2,16,20); Array data(shape); Array mask(shape); Float val = 1.0; data.set(val); mask.set(True); MaskedArray mArrIn(data,mask); // MaskedArray mArrOut; uInt axis = 0; Int bin = 4; LatticeUtilities::bin(mArrOut, mArrIn, axis, bin); IPosition shapeOut = mArrOut.shape(); for (uInt i=0; i #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Remove the dirname from the table name in an error message. String removeDir (const String& msg) { String s = msg; s.gsub (Regex("/.*/t"), "t"); return s; } void testTempClose() { PagedArray scratch(IPosition(3,64,64,257), "tPagedArray_tmp.scr"); scratch.tempClose(); AlwaysAssertExit (scratch.ok()); IPosition shape(3,1); shape(2) = scratch.shape()(2); AlwaysAssertExit (scratch.ok()); AlwaysAssertExit (scratch.isWritable()); scratch.tempClose(); LatticeIterator li(scratch, shape); scratch.tempClose(); Int i = 0; for (li.reset(); !li.atEnd(); li++, i++) { li.woCursor() = i; } shape = scratch.shape(); shape(2) = 1; COWPtr > ptrM; scratch.tempClose(); scratch.getSlice(ptrM, IPosition(3,0), shape, IPosition(3,1), False); scratch.reopen(); AlwaysAssert(ptrM->shape().isEqual(shape), AipsError); Array expectedResult(shape); indgen(expectedResult); AlwaysAssert(allEQ(*ptrM, expectedResult), AipsError); ptrM.rwRef() = 0; AlwaysAssert(allEQ(*ptrM, 0), AipsError); Slicer sl(IPosition(3,0,0,5), shape, IPosition(3,1)); scratch.getSlice(ptrM, sl, False); AlwaysAssert(allEQ(*ptrM, expectedResult), AipsError); scratch.set(0); scratch.putAt (7, IPosition(3,7)); AlwaysAssert(scratch.getAt(IPosition(3,0)) == 0, AipsError); AlwaysAssert(scratch.getAt(IPosition(3,7)) == 7, AipsError); } int main() { try { { PagedArray pa(IPosition(2,12), "tPagedArray_tmp.table"); pa.set(10.0); Array arr; pa.getSlice(arr, IPosition(2,0), IPosition(2,12), IPosition(2,1)); AlwaysAssert(allNear(arr, 10.0f, 1E-5), AipsError); indgen(arr); Array arr1(arr(IPosition(2,0), IPosition(2,0,11), IPosition(2,1,2))); pa.putSlice(arr1, IPosition(2,0), IPosition(2,1,2)); Vector vec(10); indgen(vec); pa.putSlice(vec(IPosition(1,0), IPosition(1,9), IPosition(1,2)), IPosition(2,1,1), IPosition(2,2,1)); } { PagedArray pa("tPagedArray_tmp.table"); AlwaysAssert(pa.shape().isEqual(IPosition(2,12)), AipsError); Array arr; Slicer sl(IPosition(2,0), IPosition(2,12)); pa.getSlice(arr, sl); AlwaysAssert(near(pa(IPosition(2,0)), 0.0f), AipsError); AlwaysAssert(near(pa(IPosition(2,0,1)), 10.0f), AipsError); AlwaysAssert(near(pa.getAt(IPosition(2,0,2)), 24.0f), AipsError); AlwaysAssert(near(pa.getAt(IPosition(2,1,1)), 0.0f), AipsError); AlwaysAssert(near(pa(IPosition(2,2,1)), 10.0f), AipsError); AlwaysAssert(near(pa(IPosition(2,3,1)), 2.0f), AipsError); pa.putAt (99.0, IPosition(2,11)); pa.putAt (98.0f, IPosition(2,11,10)); AlwaysAssert(removeDir(pa.tableName()) == "tPagedArray_tmp.table", AipsError); AlwaysAssert(pa.name(True) == "tPagedArray_tmp.table", AipsError); AlwaysAssert(pa.isPersistent(), AipsError); AlwaysAssert(pa.isPaged(), AipsError); AlwaysAssert(pa.isWritable(), AipsError); AlwaysAssert(pa.columnName() == PagedArray::defaultColumn(), AipsError); AlwaysAssert(pa.rowNumber() == PagedArray::defaultRow(), AipsError); } { Table pagedTable("tPagedArray_tmp.table"); PagedArray pa(pagedTable); AlwaysAssert(near(pa(IPosition(2,11)), 99.0f), AipsError); AlwaysAssert(near(pa(IPosition(2,11, 10)), 98.0f), AipsError); } { PagedArray scratch(IPosition(3,9)); LatticeIterator li(scratch, IPosition(3,1,1,9)); Int i = 0; for (li.reset(); !li.atEnd(); li++, i++) { li.woCursor() = i; } COWPtr > ptrM; scratch.getSlice(ptrM, IPosition(3,0), IPosition(3,9,9,1), IPosition(3,1), True); AlwaysAssert(ptrM->shape().isEqual(IPosition(2,9)), AipsError); Array expectedResult(IPosition(2,9)); indgen(expectedResult); AlwaysAssert(allEQ(*ptrM, expectedResult), AipsError); ptrM.rwRef() = 0; AlwaysAssert(allEQ(*ptrM, 0), AipsError); Slicer sl(IPosition(3,0,0,5), IPosition(3,9,9,1), IPosition(3,1)); scratch.getSlice(ptrM, sl, True); AlwaysAssert(allEQ(*ptrM, expectedResult), AipsError); scratch.resize(IPosition(3,8)); AlwaysAssert(scratch.shape().isEqual(IPosition(3,8)), AipsError); scratch.set(0); scratch.putAt (7, IPosition(3,7)); AlwaysAssert(scratch.getAt(IPosition(3,0)) == 0, AipsError); AlwaysAssert(scratch.getAt(IPosition(3,7)) == 7, AipsError); } { SetupNewTable arraySetup("tPagedArray_tmp_1.table", TableDesc(), Table::Scratch); Table arrayTable(arraySetup); const IPosition latticeShape(4, 128, 128, 4, 32); PagedArray pa(latticeShape, arrayTable); AlwaysAssert(pa.tileShape().isEqual(pa.niceCursorShape()), AipsError); Array arr(IPosition(4,1,1,4,32)); Slicer sl(IPosition(4,0), IPosition(4,1,1,4,32)); pa.clearCache(); pa.setCacheSizeFromPath(arr.shape(), IPosition(4,0), pa.tileShape() - 1, IPosition(4,0,1,2,3)); pa.getSlice(arr, sl); pa.showCacheStatistics(cout); SetupNewTable array1Setup("tPagedArray_tmp.table", TableDesc(), Table::New); Table array1Table(array1Setup); PagedArray pa1(TiledShape(latticeShape,IPosition(4,16,16,4,32)), array1Table); AlwaysAssert(pa1.tileShape().isEqual(IPosition(4,16,16,4,32)), AipsError); pa1.clearCache(); pa1.setCacheSizeFromPath(arr.shape(), IPosition(4,0), IPosition(4,16,16,4,32) - 1, IPosition(4,0,1,2,3)); pa1.getSlice(arr, sl); pa1.showCacheStatistics(cout); pa = pa1; AlwaysAssert(pa.tileShape().isEqual(IPosition(4,16,16,4,32)), AipsError); arr = 9.0f; pa.putSlice(arr, IPosition(4,0)); arr = 0.0f; pa1.getSlice(arr, sl); AlwaysAssert(allNear(arr, 9.0f, 1E-5), AipsError); IPosition lat2Shape = IPosition(4,16); PagedArray pa2(lat2Shape, array1Table, PagedArray::defaultColumn(), 2); arr.resize(lat2Shape); indgen(arr); pa2.putSlice(arr, IPosition(4,0)); IPosition lat3Shape = IPosition(2,16); PagedArray pa3(TiledShape(lat3Shape,lat3Shape), array1Table, "IntPagedArray", 1); Array iarr(lat3Shape); indgen(iarr); pa3.putSlice(iarr, IPosition(2,0)); } { Table file("tPagedArray_tmp.table"); PagedArray pa1(file); AlwaysAssert(pa1.shape().isEqual(IPosition(4,128,128,4,32)), AipsError); PagedArray pa2(file, PagedArray::defaultColumn(), 2); AlwaysAssert(pa2.shape().isEqual(IPosition(4,16)), AipsError); PagedArray pa3(file, "IntPagedArray", 1); AlwaysAssert(pa3.shape().isEqual(IPosition(2,16)), AipsError); Array iarr(pa3.shape()), expected(pa3.shape()); pa3.setMaximumCacheSize(256*256); indgen(expected); pa3.getSlice(iarr, IPosition(2,0), IPosition(2,16), IPosition(2,1)); AlwaysAssert(allEQ(iarr, expected), AipsError); { PagedArray pa4(pa3); AlwaysAssert(pa4.shape().isEqual(IPosition(2,16)), AipsError); iarr = 0; pa4.getSlice(iarr, IPosition(2,0), IPosition(2,16), IPosition(2,1)); AlwaysAssert(allEQ(iarr, expected), AipsError); AlwaysAssert(pa4.ok() == True, AipsError); } AlwaysAssert(pa3.maximumCacheSize() == 65536, AipsError); pa3.resize(IPosition(2,8)); pa3.set(0); pa3.putAt (7, IPosition(2,7)); AlwaysAssert(pa3.getAt(IPosition(2,7)) == 7, AipsError); AlwaysAssert(pa3.getAt(IPosition(2,0)) == 0, AipsError); AlwaysAssert(pa3.shape().isEqual(IPosition(2,8)), AipsError); } { SetupNewTable arraySetup("tPagedArray_tmp_1.table", TableDesc(), Table::New); Table arrayTable(arraySetup); const IPosition latticeShape(4, 4, 16, 15, 8); PagedArray pa(TiledShape(latticeShape, IPosition(4,2,8,8,3)), arrayTable); Array arr(latticeShape); indgen(arr); pa.put (arr); AlwaysAssertExit (allEQ(pa.get(), arr)); pa += pa; AlwaysAssertExit (allEQ(pa.get(), float(2)*arr)); } { PagedArray pa("tPagedArray_tmp_1.table"); Array arr(pa.shape()); indgen(arr); AlwaysAssertExit (allEQ(pa.get(), float(2)*arr)); } testTempClose(); } catch (std::exception& x) { cerr << x.what() << endl; return 1; } cout<< "OK"<< endl; return 0; } casacore-3.7.1/lattices/Lattices/test/tPixelCurve1D.cc000066400000000000000000000133321476623553700226330ustar00rootroot00000000000000//# tPixelCurve1D.cc: Test program for class PixelCurve1D //# Copyright (C) 2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have receied a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include int main() { Vector x,y; // Construct from straight line. PixelCurve1D pcurve(1.0, 2.0, 5.0, 6.0, 9); AlwaysAssertExit (pcurve.npoints() == 9); pcurve.getPixelCoord (x, y, 0, 8); cout << x << y << endl; pcurve.getPixelCoord (x, y, 0, 8, 2); cout << x << y << endl; pcurve.getPixelCoord (x, y, 1, 8, 3); cout << x << y << endl; // The same, but let class determine #points. { PixelCurve1D pcurve1(1.0, 2.0, 5.0, 6.0); AlwaysAssertExit (pcurve1.npoints() == 6); pcurve1.getPixelCoord (x, y, 0, 5); cout << x << y << endl; pcurve1.getPixelCoord (x, y, 0, 5, 2); cout << x << y << endl; pcurve1.getPixelCoord (x, y, 1, 5, 3); cout << x << y << endl; } // The same, but using a polynomial. { Polynomial func(1); func.setCoefficient (0, 1.); func.setCoefficient (1, 1.); PixelCurve1D pcurve1(func, 1., 5.); AlwaysAssertExit (pcurve1.npoints() == 6); pcurve1.getPixelCoord (x, y, 0, 5); cout << x << y << endl; float dx = 1; float dy = 2; for (uInt i=0; i<5; i++) { AlwaysAssertExit (near(x[i], dx, 0.00001)); AlwaysAssertExit (near(y[i], dy, 0.00001)); dx += 0.8; dy += 0.8; } } // Construct from a cosine function. Sinusoid1D fn; PixelCurve1D pcurve2(fn, 0., 2., 5); { AlwaysAssertExit (pcurve2.npoints() == 5); pcurve2.getPixelCoord (x, y, 0, 4); cout << x << y << endl; double dx = x[1] - x[0]; double dy = y[1] - y[0]; double lng = sqrt(dx*dx + dy*dy); for (uInt i=1; i<5; i++) { double dx = x[i] - x[i-1]; double dy = y[i] - y[i-1]; AlwaysAssertExit (near(lng, sqrt(dx*dx + dy*dy), 1e-5)); } } cout << setprecision(3); { PixelCurve1D pcurve2a(fn, 0., 2.); AlwaysAssertExit (pcurve2a.npoints() == 9); pcurve2a.getPixelCoord (x, y, 0, 8); cout << x << y << endl; double dx = x[1] - x[0]; double dy = y[1] - y[0]; double lng = sqrt(dx*dx + dy*dy); for (uInt i=1; i<9; i++) { double dx = x[i] - x[i-1]; double dy = y[i] - y[i-1]; AlwaysAssertExit (near(lng, sqrt(dx*dx + dy*dy), 1e-4)); } } { PixelCurve1D pcurve2b(fn, 0., 2., 81); AlwaysAssertExit (pcurve2b.npoints() == 81); pcurve2b.getPixelCoord (x, y, 0, 80, 10); cout << x << y << endl; double dx = x[1] - x[0]; double dy = y[1] - y[0]; double lng = sqrt(dx*dx + dy*dy); for (uInt i=1; i<9; i++) { double dx = x[i] - x[i-1]; double dy = y[i] - y[i-1]; AlwaysAssertExit (near(lng, sqrt(dx*dx + dy*dy), 1e-4)); } } cout << setprecision(6); // Copy constructor and self assignment. PixelCurve1D pcurve3(pcurve); AlwaysAssertExit (pcurve3.npoints() == 9); const PixelCurve1D& pcurve3ref(pcurve3); pcurve3 = pcurve3ref; AlwaysAssertExit (pcurve3.npoints() == 9); pcurve3.getPixelCoord (x, y, 0, 8); cout << x << y << endl; // Assignment. pcurve3 = pcurve2; AlwaysAssertExit (pcurve2.npoints() == 5); pcurve2.getPixelCoord (x, y, 0, 4); { // Construct from a very simple polyline. Vector xp(3); Vector yp(3); xp[0]=0; xp[1]=4; xp[2]=4; yp[0]=0; yp[1]=0; yp[2]=4; PixelCurve1D pcurve4(xp,yp); AlwaysAssertExit (pcurve4.npoints() == 9); pcurve4.getPixelCoord (x, y, 0, 8); cout << x << y << endl; } { // Construct from a square. Vector xp(5); Vector yp(5); xp[0]=2; xp[1]=4; xp[2]=2; xp[3]=0; xp[4]=2; yp[0]=0; yp[1]=2; yp[2]=4; yp[3]=2; yp[4]=0; PixelCurve1D pcurve4(xp,yp,9); AlwaysAssertExit (pcurve4.npoints() == 9); pcurve4.getPixelCoord (x, y, 0, 8); cout << x << y << endl; } { // Construct from another polyline. Vector xp(5); Vector yp(5); xp[0]=2; xp[1]=4; xp[2]=7; xp[3]=8; xp[4]=12; yp[0]=2; yp[1]=6; yp[2]=9; yp[3]=6; yp[4]=6; PixelCurve1D pcurve4(xp,yp,21); AlwaysAssertExit (pcurve4.npoints() == 21); const PixelCurve1D& pcurve4ref(pcurve4); pcurve4 = pcurve4ref; AlwaysAssertExit (pcurve4.npoints() == 21); pcurve4.getPixelCoord (x, y, 0, 20); cout << x << y << endl; pcurve3 = pcurve4; AlwaysAssertExit (pcurve3.npoints() == 21); pcurve3.getPixelCoord (x, y, 0, 20); cout << x << y << endl; } } casacore-3.7.1/lattices/Lattices/test/tPixelCurve1D.out000066400000000000000000000026331476623553700230570ustar00rootroot00000000000000[1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5][2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 6] [1, 2, 3, 4, 5][2, 3, 4, 5, 6] [1.5, 3, 4.5][2.5, 4, 5.5] [1, 1.8, 2.6, 3.4, 4.2, 5][2, 2.8, 3.6, 4.4, 5.2, 6] [1, 2.6, 4.2][2, 3.6, 5.2] [1.8, 4.2][2.8, 5.2] [1, 1.8, 2.60001, 3.40001, 4.20002, 5.00002][2, 2.8, 3.60001, 4.40001, 5.20002, 6.00002] [0, 0.500008, 1.00002, 1.50002, 2.00003][1, -0.999996, 0.999995, -0.999996, 1] [0, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2][1, -4.01e-06, -1, 1.2e-05, 1, -2e-05, -1, 2.81e-05, 1] [0, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2][1, -4.01e-06, -1, 1.2e-05, 1, -2e-05, -1, 2.81e-05, 1] [1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5][2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 6] [0, 1, 2, 3, 4, 4, 4, 4, 4][0, 0, 0, 0, 0, 1, 2, 3, 4] [2, 3, 4, 3, 2, 1, 3.14018e-16, 1, 2][0, 1, 2, 3, 4, 3, 2, 1, 0] [2, 2.35502, 2.71004, 3.06507, 3.42009, 3.77511, 4.20575, 4.76709, 5.32843, 5.88977, 6.45111, 7.00557, 7.2566, 7.50764, 7.75868, 8.03074, 8.82459, 9.61844, 10.4123, 11.2061, 12][2, 2.71004, 3.42009, 4.13013, 4.84017, 5.55022, 6.20575, 6.76709, 7.32843, 7.88977, 8.45111, 8.9833, 8.23019, 7.47707, 6.72396, 6, 6, 6, 6, 6, 6] [2, 2.35502, 2.71004, 3.06507, 3.42009, 3.77511, 4.20575, 4.76709, 5.32843, 5.88977, 6.45111, 7.00557, 7.2566, 7.50764, 7.75868, 8.03074, 8.82459, 9.61844, 10.4123, 11.2061, 12][2, 2.71004, 3.42009, 4.13013, 4.84017, 5.55022, 6.20575, 6.76709, 7.32843, 7.88977, 8.45111, 8.9833, 8.23019, 7.47707, 6.72396, 6, 6, 6, 6, 6, 6] casacore-3.7.1/lattices/Lattices/test/tRebinLattice.cc000066400000000000000000000210361476623553700227250ustar00rootroot00000000000000//# tRebinLattice.cc: test RebinLattice //# Copyright (C) 2001,2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void doit1 (const IPosition& shape, const IPosition& factors); void doit2 (); void doit3 (); void doit4 (RebinLattice& rb, const IPosition& shape, const IPosition& factors); int main (int argc, const char* argv[]) { try { Input inputs(1); inputs.version ("$Revision$"); // Get inputs inputs.create("shape", "-10", "shape"); inputs.create("factors", "-10", "factors"); inputs.readArguments(argc, argv); const Block factorsU(inputs.getIntArray("factors")); const Block shapeU(inputs.getIntArray("shape")); // Convert inputs IPosition shapeIn; if (shapeU.nelements()>0) { if (shapeU.nelements()==1 && shapeU[0]==-10) { shapeIn = IPosition(2, 10, 10); } else { shapeIn.resize(shapeU.nelements()); for (uInt i=0; i0) { if (factorsU.nelements()==1 && factorsU[0]==-10) { factors.resize(nDim); factors = 2; } else { AlwaysAssert(factorsU.nelements()==nDim, AipsError); factors.resize(nDim); for (uInt i=0; i inLat(shape2); inLat.set(1.0); SubLattice inML(inLat, True); // Unmasked input { // Make rebinner RebinLattice reBinLat(inML, factors); // const Array& data = reBinLat.get(); Float val(1.0); Bool ok = ::allNear(data, val, 1.0e-6); AlwaysAssert(ok, AipsError); // const Array& mask = reBinLat.getMask(); ok = ::allEQ(mask, True); AlwaysAssert(ok, AipsError); } // Masked input { TempLattice inMask(shape2); inMask.set(True); inML.setPixelMask(inMask, True); // Make rebinner RebinLattice reBinLat(inML, factors); // const Array& data = reBinLat.get(); Float val(1.0); Bool ok = ::allNear(data, val, 1.0e-6); AlwaysAssert(ok, AipsError); // const Array& mask = reBinLat.getMask(); ok = ::allEQ(mask, True); AlwaysAssert(ok, AipsError); } } void doit2 () { // Make data IPosition factors(1, 2); IPosition shapeIn(1, 6); Array dataIn(shapeIn); IPosition pos(1); for (Int j=0; j dataOut(shapeOut); for (Int j=0; j inLat(shape2); inLat.put(dataIn); TempLattice inMask(shape2); inMask.set(True); // SubLattice inML(inLat, True); inML.setPixelMask(inMask, True); // cerr << endl << endl; cerr << "factors = " << factors << endl; cerr << "shapeIn, shapeOut = " << shapeIn << shapeOut << endl; // Make rebinner RebinLattice reBinLat(inML, factors); // const Array& dataOut2 = reBinLat.get(); Bool ok = ::allNear(dataOut, dataOut2, 1.0e-6); AlwaysAssert(ok, AipsError); // const Array& maskOut2 = reBinLat.getMask(); ok = ::allEQ(maskOut2, True); AlwaysAssert(ok, AipsError); /* cerr << "Data = " << endl; cerr << "in = " << inML.get() << endl; cerr << "expected out = " << dataOut << endl; cerr << "out = " << dataOut2 << endl; */ /*/ cerr << "Masks = " << endl; cerr << "in = " << inML.getMask() << endl; cerr << "out = " << reBinLat.getMask() << endl; */ } void doit3 () { // Make data IPosition factors(1, 2); IPosition shapeIn(1, 6); Array dataIn(shapeIn); IPosition pos(1); for (Int j=0; j inLat(shape2); inLat.put(dataIn); TempLattice inMask(shape2); inMask.set(True); // SubLattice inML(inLat, True); inML.setPixelMask(inMask, True); // Make rebinner RebinLattice rb(inML, factors); // Test it doit4(rb, shapeIn, factors); // Copy constructor RebinLattice rb2(rb); doit4(rb2, shapeIn, factors); // Assignment RebinLattice rb3; rb3 = rb; doit4(rb3, shapeIn, factors); } void doit4 (RebinLattice& rb, const IPosition& shape, const IPosition& factors) { AlwaysAssert(rb.isMasked(), AipsError); AlwaysAssert(!rb.isPaged(), AipsError); AlwaysAssert(!rb.isWritable(), AipsError); // AlwaysAssert(rb.lock(FileLocker::Read,1), AipsError); AlwaysAssert(rb.hasLock(FileLocker::Read), AipsError); rb.unlock(); // rb.resync(); rb.flush(); rb.tempClose(); rb.reopen(); // AlwaysAssert(rb.getRegionPtr()==0, AipsError); AlwaysAssert(rb.shape()(0)==shape(0)/factors(0), AipsError); rb.name(); rb.advisedMaxPixels(); AlwaysAssert(rb.ok(), AipsError); } casacore-3.7.1/lattices/Lattices/test/tSubLattice.cc000066400000000000000000000426561476623553700224320ustar00rootroot00000000000000//# tSubLattice.cc: Test program for class SubLattice //# Copyright (C) 1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void testVectorROIter (const Lattice& sublat, const Lattice& lattice, const Slicer& slicer) { Int nstep; const IPosition latticeShape(sublat.shape()); const IPosition cursorShape(1,latticeShape(0)); LatticeStepper step(latticeShape, cursorShape); RO_LatticeIterator iter(sublat, step); LatticeStepper step2(lattice.shape(), cursorShape); step2.subSection (slicer.start(), slicer.end(), slicer.stride()); RO_LatticeIterator iter2(lattice, step2); // static_cast's added for a workaround for an SGI compiler bug. for (iter.reset(); !iter.atEnd(); iter++, iter2++){ AlwaysAssert(allEQ(static_cast >(iter.vectorCursor()), static_cast >(iter2.vectorCursor())), AipsError); } nstep = iter.nsteps(); AlwaysAssert(nstep == latticeShape.product()/latticeShape(0), AipsError); IPosition expectedPos(latticeShape-1); AlwaysAssert(iter.endPosition() == expectedPos, AipsError); expectedPos(0) = 0; AlwaysAssert(iter.position() == expectedPos, AipsError); } void testRest() { PagedArray pa(IPosition(1,10), "tSubLattice_tmp.pa"); AlwaysAssertExit (pa.isPaged()); AlwaysAssertExit (pa.isPersistent()); AlwaysAssertExit (pa.isWritable()); AlwaysAssertExit (pa.name(True) == "tSubLattice_tmp.pa"); LCPagedMask mask(IPosition(1,10), "tSubLattice_tmp.pa/mask"); Slicer slicer(IPosition(1,1), IPosition(1,3)); Slicer slfull(IPosition(1,0), IPosition(1,10)); { // A SubLattice as a Lattice copy (RO). SubLattice sl(pa); AlwaysAssertExit (!sl.isMasked()); AlwaysAssertExit (!sl.hasPixelMask()); AlwaysAssertExit (sl.isPaged()); AlwaysAssertExit (sl.isPersistent()); AlwaysAssertExit (!sl.isWritable()); // A copy of the SubLattice. SubLattice sl1(sl, True); AlwaysAssertExit (!sl1.isMasked()); AlwaysAssertExit (!sl1.hasPixelMask()); AlwaysAssertExit (sl1.isPaged()); AlwaysAssertExit (sl1.isPersistent()); AlwaysAssertExit (!sl1.isWritable()); AlwaysAssertExit (sl1.name(True) == "tSubLattice_tmp.pa"); } { // A SubLattice as a Lattice copy (RW). SubLattice sl(pa, True); AlwaysAssertExit (!sl.isMasked()); AlwaysAssertExit (!sl.hasPixelMask()); AlwaysAssertExit (sl.isPaged()); AlwaysAssertExit (sl.isPersistent()); AlwaysAssertExit (sl.isWritable()); // A RW copy of the SubLattice. SubLattice sl1(sl, True); AlwaysAssertExit (!sl1.isMasked()); AlwaysAssertExit (!sl1.hasPixelMask()); AlwaysAssertExit (sl1.isPaged()); AlwaysAssertExit (sl1.isPersistent()); AlwaysAssertExit (sl1.isWritable()); // A RO copy of the SubLattice. SubLattice sl2(sl, False); AlwaysAssertExit (!sl2.isMasked()); AlwaysAssertExit (!sl2.hasPixelMask()); AlwaysAssertExit (sl2.isPaged()); AlwaysAssertExit (sl2.isPersistent()); AlwaysAssertExit (!sl2.isWritable()); AlwaysAssertExit (sl2.name(True) == "tSubLattice_tmp.pa"); } { // A RO SubLattice as a masked Lattice. SubLattice sl(pa, mask); AlwaysAssertExit (sl.isMasked()); AlwaysAssertExit (!sl.hasPixelMask()); AlwaysAssertExit (sl.isPaged()); AlwaysAssertExit (!sl.isPersistent()); AlwaysAssertExit (!sl.isWritable()); // A copy of the SubLattice. SubLattice sl1(sl, True); AlwaysAssertExit (sl1.isMasked()); AlwaysAssertExit (!sl1.hasPixelMask()); AlwaysAssertExit (sl1.isPaged()); AlwaysAssertExit (!sl1.isPersistent()); AlwaysAssertExit (!sl1.isWritable()); AlwaysAssertExit (sl1.name(True) == "tSubLattice_tmp.pa"); } { // A RW SubLattice as a masked Lattice. SubLattice sl(pa, mask, True); AlwaysAssertExit (sl.isMasked()); AlwaysAssertExit (!sl.hasPixelMask()); AlwaysAssertExit (sl.isPaged()); AlwaysAssertExit (!sl.isPersistent()); AlwaysAssertExit (sl.isWritable()); // A RW copy of the SubLattice. SubLattice sl1(sl, True); AlwaysAssertExit (sl1.isMasked()); AlwaysAssertExit (!sl1.hasPixelMask()); AlwaysAssertExit (sl1.isPaged()); AlwaysAssertExit (!sl1.isPersistent()); AlwaysAssertExit (sl1.isWritable()); // A RO copy of the SubLattice. SubLattice sl2(sl, False); AlwaysAssertExit (sl2.isMasked()); AlwaysAssertExit (!sl2.hasPixelMask()); AlwaysAssertExit (sl2.isPaged()); AlwaysAssertExit (!sl2.isPersistent()); AlwaysAssertExit (!sl2.isWritable()); } { // A small region of a lattice. SubLattice sl(pa, slicer, True); AlwaysAssertExit (!sl.isMasked()); AlwaysAssertExit (!sl.hasPixelMask()); AlwaysAssertExit (sl.isPaged()); AlwaysAssertExit (!sl.isPersistent()); AlwaysAssertExit (sl.isWritable()); // A RW copy of the SubLattice. SubLattice sl1(sl, True); AlwaysAssertExit (!sl1.isMasked()); AlwaysAssertExit (!sl1.hasPixelMask()); AlwaysAssertExit (sl1.isPaged()); AlwaysAssertExit (!sl1.isPersistent()); AlwaysAssertExit (sl1.isWritable()); // A RO copy of the SubLattice. SubLattice sl2(sl, False); AlwaysAssertExit (!sl2.isMasked()); AlwaysAssertExit (!sl2.hasPixelMask()); AlwaysAssertExit (sl2.isPaged()); AlwaysAssertExit (!sl2.isPersistent()); AlwaysAssertExit (!sl2.isWritable()); } { // A full region of a lattice. SubLattice sl(pa, slfull, True); AlwaysAssertExit (!sl.isMasked()); AlwaysAssertExit (!sl.hasPixelMask()); AlwaysAssertExit (sl.isPaged()); AlwaysAssertExit (sl.isPersistent()); AlwaysAssertExit (sl.isWritable()); // A RW copy of the SubLattice. SubLattice sl1(sl, True); AlwaysAssertExit (!sl1.isMasked()); AlwaysAssertExit (!sl1.hasPixelMask()); AlwaysAssertExit (sl1.isPaged()); AlwaysAssertExit (sl1.isPersistent()); AlwaysAssertExit (sl1.isWritable()); // A RO copy of the SubLattice. SubLattice sl2(sl, False); AlwaysAssertExit (!sl2.isMasked()); AlwaysAssertExit (!sl2.hasPixelMask()); AlwaysAssertExit (sl2.isPaged()); AlwaysAssertExit (sl2.isPersistent()); AlwaysAssertExit (!sl2.isWritable()); } } void testAxes() { PagedArray pa(IPosition(3,10,11,12), "tSubLattice_tmp.pa"); LCPagedMask mask(IPosition(3,10,11,12), "tSubLattice_tmp.pa/mask"); Array arr(pa.shape()); indgen(arr); pa.put (arr); Array m(pa.shape()); m = True; m(IPosition(3,0,0,0), IPosition(3,9,10,11), IPosition(3,2,1,1)) = False; mask.put (m); { // Create a sublattice with the mask and assign a pixelmask to it. SubLattice ml(pa, mask); Array pm(pa.shape()); pm = True; pm(IPosition(3,0,0,0), IPosition(3,9,10,11), IPosition(3,3,2,1)) = False; ml.setPixelMask (ArrayLattice(pm), False); AlwaysAssertExit (ml.hasPixelMask()); // Test if the mask read is correct. AlwaysAssertExit (allEQ (ml.getMask(), m&&pm)); AlwaysAssertExit (allEQ (ml.pixelMask().get(), pm)); // Copy constructor. SubLattice ml2(ml); AlwaysAssertExit (allEQ (ml2.pixelMask().get(), pm)); AlwaysAssertExit (allEQ (ml2.getMask(), m&&pm)); // Assign another pixelmask. pm = True; pm(IPosition(3,0,0,0), IPosition(3,9,10,11), IPosition(3,5,1,2)) = False; ml2.setPixelMask (ArrayLattice(pm), False); AlwaysAssertExit (allEQ (ml2.getMask(), m&&pm)); AlwaysAssertExit (allEQ (ml2.pixelMask().get(), pm)); // Now make a sublattice from a MaskedLattice. ml2 = SubLattice (ml2, AxesSpecifier()); AlwaysAssertExit (allEQ (ml2.getMask(), m&&pm)); AlwaysAssertExit (allEQ (ml2.pixelMask().get(), pm)); // Assign another pixelmask. Array pm2(pa.shape()); pm2 = False; pm2(IPosition(3,0,0,0), IPosition(3,9,10,11), IPosition(3,7,1,1)) = True; // The first one should fail. Bool exc = False; try { ml2.setPixelMask (ArrayLattice(pm2), False); } catch (std::exception& x) { exc = True; } AlwaysAssertExit (exc); ml2.setPixelMask (ArrayLattice(pm2), True); AlwaysAssertExit (allEQ (ml2.pixelMask().get(), pm&&pm2)); AlwaysAssertExit (allEQ (ml2.getMask(), m&&pm&&pm2)); } Array arrs1 = arr(IPosition(3,3,1,2), IPosition(3,8,1,9)); Array arrsub = arrs1.reform(IPosition(2,6,8)); Array ms1 = m(IPosition(3,3,1,2), IPosition(3,8,1,9)); Array msub = ms1.reform(IPosition(2,6,8)); // Make sublattice with a removed axis 1. SubLattice ml(pa, mask, True); Array pixmask(IPosition(3,6,1,8)); pixmask = True; pixmask (IPosition(3,0,0,0)) = !msub(IPosition(2,0,0)); LCPixelSet pixset (pixmask, LCBox(IPosition(3,3,1,2), IPosition(3,8,1,9), m.shape())); SubLattice sl(ml, pixset, True, AxesSpecifier(False)); IPosition ncs (pa.niceCursorShape()); // Test if shape and niceCursorShape remove the axis. AlwaysAssertExit (sl.shape() == IPosition(2,6,8)); AlwaysAssertExit (sl.niceCursorShape() == IPosition(2, min(6,ncs(0)), min(8,ncs(2)))); // Test the getting functions. Array arrsl = sl.get(); AlwaysAssertExit (allEQ (arrsl, arrsub)); AlwaysAssertExit (sl.getAt(IPosition(2,1,3)) == arrsub(IPosition(2,1,3))); arrsub(IPosition(2,1,3)) += 10; // Test the put function and see if the result matches. sl.putAt(arrsub(IPosition(2,1,3)), IPosition(2,1,3)); AlwaysAssertExit (sl.getAt(IPosition(2,1,3)) == arrsub(IPosition(2,1,3))); AlwaysAssertExit (allEQ (sl.get(), arrsub)); arrsub(IPosition(2,1,3)) += 10; sl.put (arrsub); AlwaysAssertExit (allEQ (sl.get(), arrsub)); AlwaysAssertExit (sl.getAt(IPosition(2,1,3)) == arrsub(IPosition(2,1,3))); // Now get and put a slice. Array arrsubsub = arrsub(IPosition(2,1,2), IPosition(2,5,7), IPosition(2,3,2)); AlwaysAssertExit (allEQ (sl.getSlice(Slicer(IPosition(2,1,2), IPosition(2,5,7), IPosition(2,3,2), Slicer::endIsLast)), arrsubsub)); arrsubsub += 20; sl.putSlice (arrsubsub, IPosition(2,1,2), IPosition(2,3,2)); AlwaysAssertExit (allEQ (sl.get(), arrsub)); // Test the get mask functions. AlwaysAssertExit (allEQ (ml.getMask(), m)); msub(IPosition(2,0,0)) = !msub(IPosition(2,0,0)); AlwaysAssertExit (allEQ (sl.getMask(), msub)); Array msubsub = msub(IPosition(2,1,2), IPosition(2,5,7), IPosition(2,3,2)); AlwaysAssertExit (allEQ (sl.getMaskSlice(Slicer(IPosition(2,1,2), IPosition(2,5,7), IPosition(2,3,2), Slicer::endIsLast)), msubsub)); // Assign a pixelmask to this sublattice. Array slm = sl.getMask(); Array pm(sl.shape()); pm = True; pm(IPosition(2,0,0), IPosition(2,1,2), IPosition(2,2,4)) = False; sl.setPixelMask (ArrayLattice(pm), False); AlwaysAssertExit (allEQ (sl.getMask(), slm&&pm)); } void testAdd (Lattice& lat1, Lattice& lat2, Bool useRef) { { PagedArray* pa1 = dynamic_cast*> (&lat1); if (pa1) pa1->clearCache(); PagedArray* pa2 = dynamic_cast*> (&lat2); if (pa2) pa2->clearCache(); Timer timer; LatticeIterator lat1Iter (lat1, useRef); // Create dummy lat2Iter to setup cache correctly. // It may not be necessary, because the Table getSlice function // will setup the cache on its first access. RO_LatticeIterator lat2Iter (lat2, lat1.niceCursorShape(), useRef); Array lat2Buffer; while (! lat1Iter.atEnd()) { // Do separate getSlice to use reference semantics if // lat2 is an ArrayLattice. // Note that it requires lat2 to be non-const. lat2.getSlice (lat2Buffer, lat1Iter.position(), lat1Iter.cursorShape()); lat1Iter.rwCursor() += lat2Buffer; lat1Iter++; } timer.show (); ///if (pa1) pa1->showCacheStatistics (cout); ///if (pa2) pa2->showCacheStatistics (cout); } } int main (int argc, const char* argv[]) { try { { const IPosition latticeShape(4, 16, 12, 4, 32); Array arr(latticeShape); indgen(arr); ArrayLattice lattice(arr); Slicer slicer(IPosition(4,4,2,1,3), IPosition(4,14,10,3,23), IPosition(4,2,3,1,4), Slicer::endIsLast); SubLattice sublat (lattice, slicer, True); AlwaysAssertExit (!sublat.isPaged()); AlwaysAssertExit (!sublat.isPersistent()); AlwaysAssertExit (!sublat.isMasked()); AlwaysAssertExit (sublat.isWritable()); AlwaysAssertExit (sublat.shape() == slicer.length()); Array arr1, arr2; sublat.getSlice (arr1, IPosition(4,0), sublat.shape(), IPosition(4,1)); lattice.getSlice (arr2, slicer); AlwaysAssertExit (allEQ(arr1, arr2)); AlwaysAssertExit (allEQ(arr1, arr(slicer.start(), slicer.end(), slicer.stride()))); testVectorROIter (sublat, lattice, slicer); } // Test some other SubLattice functions. testRest(); // Test the axes removal. testAxes(); { // Test performance. Input inp(1); inp.version(" "); inp.create("nx", "512", "Number of pixels along the x-axis", "int"); inp.create("ny", "512", "Number of pixels along the y-axis", "int"); inp.readArguments(argc, argv); const uInt nx=inp.getInt("nx"); const uInt ny=inp.getInt("ny"); IPosition shape(2,nx,ny); ArrayLattice latArr1(shape); ArrayLattice latArr2(shape); Array arr(latArr1.shape()); indgen(arr); latArr1.put (arr); latArr2.put (arr); cout << "Shape " << shape << endl; { SubLattice slatArr1(latArr1, True); SubLattice slatArr2(latArr2); cout << "subarray+=subarray useRef=False" << endl; testAdd (slatArr1, slatArr2, False); AlwaysAssert (allEQ(latArr1.get(), 2*arr), AipsError); cout << "subarray+=subarray useRef=True" << endl; testAdd (slatArr1, slatArr2, True); AlwaysAssert (allEQ(latArr1.get(), 3*arr), AipsError); cout << "array+=subarray useRef=False" << endl; testAdd (latArr1, slatArr2, False); AlwaysAssert (allEQ(latArr1.get(), 4*arr), AipsError); cout << "array+=subarray useRef=True" << endl; testAdd (latArr1, slatArr2, True); AlwaysAssert (allEQ(latArr1.get(), 5*arr), AipsError); cout << "subarray+=array useRef=False" << endl; testAdd (slatArr1, latArr2, False); AlwaysAssert (allEQ(latArr1.get(), 6*arr), AipsError); cout << "subarray+=array useRef=True" << endl; testAdd (slatArr1, latArr2, True); AlwaysAssert (allEQ(latArr1.get(), 7*arr), AipsError); cout << "array+=array useRef=False" << endl; testAdd (latArr1, latArr2, False); AlwaysAssert (allEQ(latArr1.get(), 8*arr), AipsError); cout << "array+=array useRef=True" << endl; testAdd (latArr1, latArr2, True); AlwaysAssert (allEQ(latArr1.get(), 9*arr), AipsError); } } { // test position in parent ArrayLattice parent((IPosition(3, 20, 20, 20))); Slicer slice(IPosition(3, 1, 1, 1), IPosition(3, 16, 17, 18), IPosition(3, 1, 1, 2), Slicer::endIsLast); SubLattice sub(parent, slice); AlwaysAssert(sub.positionInParent(IPosition(3, 4, 5, 6)) == IPosition(3, 5, 6, 13), AipsError); Slicer slice2(IPosition(3, 1, 1, 1), IPosition(3, 16, 17, 18), IPosition(3, 16, 1, 2), Slicer::endIsLast); SubLattice sub2(parent, slice2, AxesSpecifier(False)); AlwaysAssert(sub2.positionInParent(IPosition(2, 4, 5)) == IPosition(3, 1, 5, 11), AipsError); } } catch (const std::exception& x) { cerr << "Caught exception: " << x.what() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/lattices/Lattices/test/tTempLattice.cc000066400000000000000000000063571476623553700226040ustar00rootroot00000000000000//# tTempLattice.cc //# Copyright (C) 1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include void doIt (TempLattice& scratch) { scratch.tempClose(); IPosition shape(3,1); shape(2) = scratch.shape()(2); AlwaysAssertExit (scratch.isWritable()); scratch.tempClose(); LatticeIterator li(scratch, shape); scratch.tempClose(); Int i = 0; for (li.reset(); !li.atEnd(); li++, i++) { li.woCursor() = i; } shape = scratch.shape(); shape(2) = 1; COWPtr > ptrM; scratch.tempClose(); scratch.getSlice(ptrM, IPosition(3,0), shape, IPosition(3,1), False); scratch.reopen(); AlwaysAssert(ptrM->shape().isEqual(shape), AipsError); Array expectedResult(shape); indgen(expectedResult); AlwaysAssert(allEQ(*ptrM, expectedResult), AipsError); ptrM.rwRef() = 0; AlwaysAssert(allEQ(*ptrM, 0), AipsError); Slicer sl(IPosition(3,0,0,5), shape, IPosition(3,1)); scratch.getSlice(ptrM, sl, False); AlwaysAssert(allEQ(*ptrM, expectedResult), AipsError); scratch.set(0); scratch.putAt (7, IPosition(3,7)); AlwaysAssert(scratch.getAt(IPosition(3,0)) == 0, AipsError); AlwaysAssert(scratch.getAt(IPosition(3,7)) == 7, AipsError); } int main() { try { { TempLattice scratch(IPosition(3,64,64,257), 1); AlwaysAssertExit (scratch.isPaged()); doIt (scratch); } { TempLattice small(IPosition(3,64,64,16), 1); AlwaysAssertExit (small.ok()); AlwaysAssertExit (! small.isPaged()); doIt (small); } } catch (std::exception& x) { cerr << x.what() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/lattices/Lattices/test/tTileStepper.cc000066400000000000000000000115211476623553700226160ustar00rootroot00000000000000//# tTileStepper.cc: Test program for class TileStepper //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include int main (int argc, const char* argv[]) { Input inp(1); inp.version(" "); inp.create("nx", "128", "Number of pixels along the x-axis", "int"); inp.create("ny", "128", "Number of pixels along the y-axis", "int"); inp.create("nz", "128", "Number of pixels along the z-axis", "int"); inp.create("tx", "32", "Tile size along the x-axis", "int"); inp.create("ty", "32", "Tile size along the y-axis", "int"); inp.create("tz", "32", "Tile size along the z-axis", "int"); inp.create("blcx", "0", "Blc along the x-axis", "int"); inp.create("blcy", "0", "Blc along the y-axis", "int"); inp.create("blcz", "0", "Blc along the z-axis", "int"); inp.create("trcx", "1000000", "Trc along the x-axis", "int"); inp.create("trcy", "1000000", "Trc along the y-axis", "int"); inp.create("trcz", "1000000", "Trc along the z-axis", "int"); inp.create("incx", "1", "Inc along the x-axis", "int"); inp.create("incy", "1", "Inc along the y-axis", "int"); inp.create("incz", "1", "Inc along the z-axis", "int"); inp.readArguments(argc, argv); const uInt nx=inp.getInt("nx"); const uInt ny=inp.getInt("ny"); const uInt nz=inp.getInt("nz"); const uInt tx=inp.getInt("tx"); const uInt ty=inp.getInt("ty"); const uInt tz=inp.getInt("tz"); const uInt blcx=inp.getInt("blcx"); const uInt blcy=inp.getInt("blcy"); const uInt blcz=inp.getInt("blcz"); const uInt trcx=inp.getInt("trcx"); const uInt trcy=inp.getInt("trcy"); const uInt trcz=inp.getInt("trcz"); const uInt incx=inp.getInt("incx"); const uInt incy=inp.getInt("incy"); const uInt incz=inp.getInt("incz"); // Check/adapt the values. IPosition shape(3, nx, ny, nz); IPosition tileShape (3, tx, ty, tz); IPosition blc (3, blcx, blcy, blcz); IPosition trc (3, trcx, trcy, trcz); IPosition inc (3, incx, incy, incz); for (uInt i=0; i<3; i++) { AlwaysAssertExit (shape(i) > 0); AlwaysAssertExit (tileShape(i) > 0); AlwaysAssertExit (blc(i) >= 0); AlwaysAssertExit (inc(i) > 0); if (tileShape(i) > shape(i)) tileShape(i) = shape(i); if (blc(i) > shape(i)-1) blc(i) = shape(i)-1; if (trc(i) > shape(i)-1) trc(i) = shape(i)-1; if (trc(i) < blc(i)) trc(i) = blc(i); } cout << "shape = " << shape << endl; cout << "tileshape = " << tileShape << endl; cout << "blc = " << blc << endl; cout << "trc = " << trc << endl; cout << "inc = " << inc << endl; { TileStepper stepper(shape, tileShape); stepper.subSection (blc, trc, inc); while (! stepper.atEnd()) { cout << stepper.position() << ' ' << stepper.endPosition() << ' ' << stepper.cursorShape() << endl; stepper++; } cout << "nsteps = " << stepper.nsteps() << endl; while (! stepper.atStart()) { cout << stepper.position() << ' ' << stepper.endPosition() << ' ' << stepper.cursorShape() << endl; stepper--; } cout << "nsteps = " << stepper.nsteps() << endl; } { TileStepper stepper(shape, tileShape, IPosition(3,2,1,0)); stepper.subSection (blc, trc, inc); while (! stepper.atEnd()) { cout << stepper.position() << ' ' << stepper.endPosition() << ' ' << stepper.cursorShape() << endl; stepper++; } cout << "nsteps = " << stepper.nsteps() << endl; while (! stepper.atStart()) { cout << stepper.position() << ' ' << stepper.endPosition() << ' ' << stepper.cursorShape() << endl; stepper--; } cout << "nsteps = " << stepper.nsteps() << endl; } } casacore-3.7.1/lattices/Lattices/test/tTileStepper.out000066400000000000000000000236631476623553700230520ustar00rootroot00000000000000./tTileStepper: Version shape = [128, 128, 128] tileshape = [32, 32, 32] blc = [0, 0, 0] trc = [127, 127, 127] inc = [1, 1, 1] [0, 0, 0] [31, 31, 31] [32, 32, 32] [32, 0, 0] [63, 31, 31] [32, 32, 32] [64, 0, 0] [95, 31, 31] [32, 32, 32] [96, 0, 0] [127, 31, 31] [32, 32, 32] [0, 32, 0] [31, 63, 31] [32, 32, 32] [32, 32, 0] [63, 63, 31] [32, 32, 32] [64, 32, 0] [95, 63, 31] [32, 32, 32] [96, 32, 0] [127, 63, 31] [32, 32, 32] [0, 64, 0] [31, 95, 31] [32, 32, 32] [32, 64, 0] [63, 95, 31] [32, 32, 32] [64, 64, 0] [95, 95, 31] [32, 32, 32] [96, 64, 0] [127, 95, 31] [32, 32, 32] [0, 96, 0] [31, 127, 31] [32, 32, 32] [32, 96, 0] [63, 127, 31] [32, 32, 32] [64, 96, 0] [95, 127, 31] [32, 32, 32] [96, 96, 0] [127, 127, 31] [32, 32, 32] [0, 0, 32] [31, 31, 63] [32, 32, 32] [32, 0, 32] [63, 31, 63] [32, 32, 32] [64, 0, 32] [95, 31, 63] [32, 32, 32] [96, 0, 32] [127, 31, 63] [32, 32, 32] [0, 32, 32] [31, 63, 63] [32, 32, 32] [32, 32, 32] [63, 63, 63] [32, 32, 32] [64, 32, 32] [95, 63, 63] [32, 32, 32] [96, 32, 32] [127, 63, 63] [32, 32, 32] [0, 64, 32] [31, 95, 63] [32, 32, 32] [32, 64, 32] [63, 95, 63] [32, 32, 32] [64, 64, 32] [95, 95, 63] [32, 32, 32] [96, 64, 32] [127, 95, 63] [32, 32, 32] [0, 96, 32] [31, 127, 63] [32, 32, 32] [32, 96, 32] [63, 127, 63] [32, 32, 32] [64, 96, 32] [95, 127, 63] [32, 32, 32] [96, 96, 32] [127, 127, 63] [32, 32, 32] [0, 0, 64] [31, 31, 95] [32, 32, 32] [32, 0, 64] [63, 31, 95] [32, 32, 32] [64, 0, 64] [95, 31, 95] [32, 32, 32] [96, 0, 64] [127, 31, 95] [32, 32, 32] [0, 32, 64] [31, 63, 95] [32, 32, 32] [32, 32, 64] [63, 63, 95] [32, 32, 32] [64, 32, 64] [95, 63, 95] [32, 32, 32] [96, 32, 64] [127, 63, 95] [32, 32, 32] [0, 64, 64] [31, 95, 95] [32, 32, 32] [32, 64, 64] [63, 95, 95] [32, 32, 32] [64, 64, 64] [95, 95, 95] [32, 32, 32] [96, 64, 64] [127, 95, 95] [32, 32, 32] [0, 96, 64] [31, 127, 95] [32, 32, 32] [32, 96, 64] [63, 127, 95] [32, 32, 32] [64, 96, 64] [95, 127, 95] [32, 32, 32] [96, 96, 64] [127, 127, 95] [32, 32, 32] [0, 0, 96] [31, 31, 127] [32, 32, 32] [32, 0, 96] [63, 31, 127] [32, 32, 32] [64, 0, 96] [95, 31, 127] [32, 32, 32] [96, 0, 96] [127, 31, 127] [32, 32, 32] [0, 32, 96] [31, 63, 127] [32, 32, 32] [32, 32, 96] [63, 63, 127] [32, 32, 32] [64, 32, 96] [95, 63, 127] [32, 32, 32] [96, 32, 96] [127, 63, 127] [32, 32, 32] [0, 64, 96] [31, 95, 127] [32, 32, 32] [32, 64, 96] [63, 95, 127] [32, 32, 32] [64, 64, 96] [95, 95, 127] [32, 32, 32] [96, 64, 96] [127, 95, 127] [32, 32, 32] [0, 96, 96] [31, 127, 127] [32, 32, 32] [32, 96, 96] [63, 127, 127] [32, 32, 32] [64, 96, 96] [95, 127, 127] [32, 32, 32] [96, 96, 96] [127, 127, 127] [32, 32, 32] nsteps = 64 [96, 96, 96] [127, 127, 127] [32, 32, 32] [64, 96, 96] [95, 127, 127] [32, 32, 32] [32, 96, 96] [63, 127, 127] [32, 32, 32] [0, 96, 96] [31, 127, 127] [32, 32, 32] [96, 64, 96] [127, 95, 127] [32, 32, 32] [64, 64, 96] [95, 95, 127] [32, 32, 32] [32, 64, 96] [63, 95, 127] [32, 32, 32] [0, 64, 96] [31, 95, 127] [32, 32, 32] [96, 32, 96] [127, 63, 127] [32, 32, 32] [64, 32, 96] [95, 63, 127] [32, 32, 32] [32, 32, 96] [63, 63, 127] [32, 32, 32] [0, 32, 96] [31, 63, 127] [32, 32, 32] [96, 0, 96] [127, 31, 127] [32, 32, 32] [64, 0, 96] [95, 31, 127] [32, 32, 32] [32, 0, 96] [63, 31, 127] [32, 32, 32] [0, 0, 96] [31, 31, 127] [32, 32, 32] [96, 96, 64] [127, 127, 95] [32, 32, 32] [64, 96, 64] [95, 127, 95] [32, 32, 32] [32, 96, 64] [63, 127, 95] [32, 32, 32] [0, 96, 64] [31, 127, 95] [32, 32, 32] [96, 64, 64] [127, 95, 95] [32, 32, 32] [64, 64, 64] [95, 95, 95] [32, 32, 32] [32, 64, 64] [63, 95, 95] [32, 32, 32] [0, 64, 64] [31, 95, 95] [32, 32, 32] [96, 32, 64] [127, 63, 95] [32, 32, 32] [64, 32, 64] [95, 63, 95] [32, 32, 32] [32, 32, 64] [63, 63, 95] [32, 32, 32] [0, 32, 64] [31, 63, 95] [32, 32, 32] [96, 0, 64] [127, 31, 95] [32, 32, 32] [64, 0, 64] [95, 31, 95] [32, 32, 32] [32, 0, 64] [63, 31, 95] [32, 32, 32] [0, 0, 64] [31, 31, 95] [32, 32, 32] [96, 96, 32] [127, 127, 63] [32, 32, 32] [64, 96, 32] [95, 127, 63] [32, 32, 32] [32, 96, 32] [63, 127, 63] [32, 32, 32] [0, 96, 32] [31, 127, 63] [32, 32, 32] [96, 64, 32] [127, 95, 63] [32, 32, 32] [64, 64, 32] [95, 95, 63] [32, 32, 32] [32, 64, 32] [63, 95, 63] [32, 32, 32] [0, 64, 32] [31, 95, 63] [32, 32, 32] [96, 32, 32] [127, 63, 63] [32, 32, 32] [64, 32, 32] [95, 63, 63] [32, 32, 32] [32, 32, 32] [63, 63, 63] [32, 32, 32] [0, 32, 32] [31, 63, 63] [32, 32, 32] [96, 0, 32] [127, 31, 63] [32, 32, 32] [64, 0, 32] [95, 31, 63] [32, 32, 32] [32, 0, 32] [63, 31, 63] [32, 32, 32] [0, 0, 32] [31, 31, 63] [32, 32, 32] [96, 96, 0] [127, 127, 31] [32, 32, 32] [64, 96, 0] [95, 127, 31] [32, 32, 32] [32, 96, 0] [63, 127, 31] [32, 32, 32] [0, 96, 0] [31, 127, 31] [32, 32, 32] [96, 64, 0] [127, 95, 31] [32, 32, 32] [64, 64, 0] [95, 95, 31] [32, 32, 32] [32, 64, 0] [63, 95, 31] [32, 32, 32] [0, 64, 0] [31, 95, 31] [32, 32, 32] [96, 32, 0] [127, 63, 31] [32, 32, 32] [64, 32, 0] [95, 63, 31] [32, 32, 32] [32, 32, 0] [63, 63, 31] [32, 32, 32] [0, 32, 0] [31, 63, 31] [32, 32, 32] [96, 0, 0] [127, 31, 31] [32, 32, 32] [64, 0, 0] [95, 31, 31] [32, 32, 32] [32, 0, 0] [63, 31, 31] [32, 32, 32] [0, 0, 0] [31, 31, 31] [32, 32, 32] nsteps = 128 [0, 0, 0] [31, 31, 31] [32, 32, 32] [0, 0, 32] [31, 31, 63] [32, 32, 32] [0, 0, 64] [31, 31, 95] [32, 32, 32] [0, 0, 96] [31, 31, 127] [32, 32, 32] [0, 32, 0] [31, 63, 31] [32, 32, 32] [0, 32, 32] [31, 63, 63] [32, 32, 32] [0, 32, 64] [31, 63, 95] [32, 32, 32] [0, 32, 96] [31, 63, 127] [32, 32, 32] [0, 64, 0] [31, 95, 31] [32, 32, 32] [0, 64, 32] [31, 95, 63] [32, 32, 32] [0, 64, 64] [31, 95, 95] [32, 32, 32] [0, 64, 96] [31, 95, 127] [32, 32, 32] [0, 96, 0] [31, 127, 31] [32, 32, 32] [0, 96, 32] [31, 127, 63] [32, 32, 32] [0, 96, 64] [31, 127, 95] [32, 32, 32] [0, 96, 96] [31, 127, 127] [32, 32, 32] [32, 0, 0] [63, 31, 31] [32, 32, 32] [32, 0, 32] [63, 31, 63] [32, 32, 32] [32, 0, 64] [63, 31, 95] [32, 32, 32] [32, 0, 96] [63, 31, 127] [32, 32, 32] [32, 32, 0] [63, 63, 31] [32, 32, 32] [32, 32, 32] [63, 63, 63] [32, 32, 32] [32, 32, 64] [63, 63, 95] [32, 32, 32] [32, 32, 96] [63, 63, 127] [32, 32, 32] [32, 64, 0] [63, 95, 31] [32, 32, 32] [32, 64, 32] [63, 95, 63] [32, 32, 32] [32, 64, 64] [63, 95, 95] [32, 32, 32] [32, 64, 96] [63, 95, 127] [32, 32, 32] [32, 96, 0] [63, 127, 31] [32, 32, 32] [32, 96, 32] [63, 127, 63] [32, 32, 32] [32, 96, 64] [63, 127, 95] [32, 32, 32] [32, 96, 96] [63, 127, 127] [32, 32, 32] [64, 0, 0] [95, 31, 31] [32, 32, 32] [64, 0, 32] [95, 31, 63] [32, 32, 32] [64, 0, 64] [95, 31, 95] [32, 32, 32] [64, 0, 96] [95, 31, 127] [32, 32, 32] [64, 32, 0] [95, 63, 31] [32, 32, 32] [64, 32, 32] [95, 63, 63] [32, 32, 32] [64, 32, 64] [95, 63, 95] [32, 32, 32] [64, 32, 96] [95, 63, 127] [32, 32, 32] [64, 64, 0] [95, 95, 31] [32, 32, 32] [64, 64, 32] [95, 95, 63] [32, 32, 32] [64, 64, 64] [95, 95, 95] [32, 32, 32] [64, 64, 96] [95, 95, 127] [32, 32, 32] [64, 96, 0] [95, 127, 31] [32, 32, 32] [64, 96, 32] [95, 127, 63] [32, 32, 32] [64, 96, 64] [95, 127, 95] [32, 32, 32] [64, 96, 96] [95, 127, 127] [32, 32, 32] [96, 0, 0] [127, 31, 31] [32, 32, 32] [96, 0, 32] [127, 31, 63] [32, 32, 32] [96, 0, 64] [127, 31, 95] [32, 32, 32] [96, 0, 96] [127, 31, 127] [32, 32, 32] [96, 32, 0] [127, 63, 31] [32, 32, 32] [96, 32, 32] [127, 63, 63] [32, 32, 32] [96, 32, 64] [127, 63, 95] [32, 32, 32] [96, 32, 96] [127, 63, 127] [32, 32, 32] [96, 64, 0] [127, 95, 31] [32, 32, 32] [96, 64, 32] [127, 95, 63] [32, 32, 32] [96, 64, 64] [127, 95, 95] [32, 32, 32] [96, 64, 96] [127, 95, 127] [32, 32, 32] [96, 96, 0] [127, 127, 31] [32, 32, 32] [96, 96, 32] [127, 127, 63] [32, 32, 32] [96, 96, 64] [127, 127, 95] [32, 32, 32] [96, 96, 96] [127, 127, 127] [32, 32, 32] nsteps = 64 [96, 96, 96] [127, 127, 127] [32, 32, 32] [96, 96, 64] [127, 127, 95] [32, 32, 32] [96, 96, 32] [127, 127, 63] [32, 32, 32] [96, 96, 0] [127, 127, 31] [32, 32, 32] [96, 64, 96] [127, 95, 127] [32, 32, 32] [96, 64, 64] [127, 95, 95] [32, 32, 32] [96, 64, 32] [127, 95, 63] [32, 32, 32] [96, 64, 0] [127, 95, 31] [32, 32, 32] [96, 32, 96] [127, 63, 127] [32, 32, 32] [96, 32, 64] [127, 63, 95] [32, 32, 32] [96, 32, 32] [127, 63, 63] [32, 32, 32] [96, 32, 0] [127, 63, 31] [32, 32, 32] [96, 0, 96] [127, 31, 127] [32, 32, 32] [96, 0, 64] [127, 31, 95] [32, 32, 32] [96, 0, 32] [127, 31, 63] [32, 32, 32] [96, 0, 0] [127, 31, 31] [32, 32, 32] [64, 96, 96] [95, 127, 127] [32, 32, 32] [64, 96, 64] [95, 127, 95] [32, 32, 32] [64, 96, 32] [95, 127, 63] [32, 32, 32] [64, 96, 0] [95, 127, 31] [32, 32, 32] [64, 64, 96] [95, 95, 127] [32, 32, 32] [64, 64, 64] [95, 95, 95] [32, 32, 32] [64, 64, 32] [95, 95, 63] [32, 32, 32] [64, 64, 0] [95, 95, 31] [32, 32, 32] [64, 32, 96] [95, 63, 127] [32, 32, 32] [64, 32, 64] [95, 63, 95] [32, 32, 32] [64, 32, 32] [95, 63, 63] [32, 32, 32] [64, 32, 0] [95, 63, 31] [32, 32, 32] [64, 0, 96] [95, 31, 127] [32, 32, 32] [64, 0, 64] [95, 31, 95] [32, 32, 32] [64, 0, 32] [95, 31, 63] [32, 32, 32] [64, 0, 0] [95, 31, 31] [32, 32, 32] [32, 96, 96] [63, 127, 127] [32, 32, 32] [32, 96, 64] [63, 127, 95] [32, 32, 32] [32, 96, 32] [63, 127, 63] [32, 32, 32] [32, 96, 0] [63, 127, 31] [32, 32, 32] [32, 64, 96] [63, 95, 127] [32, 32, 32] [32, 64, 64] [63, 95, 95] [32, 32, 32] [32, 64, 32] [63, 95, 63] [32, 32, 32] [32, 64, 0] [63, 95, 31] [32, 32, 32] [32, 32, 96] [63, 63, 127] [32, 32, 32] [32, 32, 64] [63, 63, 95] [32, 32, 32] [32, 32, 32] [63, 63, 63] [32, 32, 32] [32, 32, 0] [63, 63, 31] [32, 32, 32] [32, 0, 96] [63, 31, 127] [32, 32, 32] [32, 0, 64] [63, 31, 95] [32, 32, 32] [32, 0, 32] [63, 31, 63] [32, 32, 32] [32, 0, 0] [63, 31, 31] [32, 32, 32] [0, 96, 96] [31, 127, 127] [32, 32, 32] [0, 96, 64] [31, 127, 95] [32, 32, 32] [0, 96, 32] [31, 127, 63] [32, 32, 32] [0, 96, 0] [31, 127, 31] [32, 32, 32] [0, 64, 96] [31, 95, 127] [32, 32, 32] [0, 64, 64] [31, 95, 95] [32, 32, 32] [0, 64, 32] [31, 95, 63] [32, 32, 32] [0, 64, 0] [31, 95, 31] [32, 32, 32] [0, 32, 96] [31, 63, 127] [32, 32, 32] [0, 32, 64] [31, 63, 95] [32, 32, 32] [0, 32, 32] [31, 63, 63] [32, 32, 32] [0, 32, 0] [31, 63, 31] [32, 32, 32] [0, 0, 96] [31, 31, 127] [32, 32, 32] [0, 0, 64] [31, 31, 95] [32, 32, 32] [0, 0, 32] [31, 31, 63] [32, 32, 32] [0, 0, 0] [31, 31, 31] [32, 32, 32] nsteps = 128 casacore-3.7.1/lattices/Lattices/test/tTiledLineStepper.cc000066400000000000000000000115211476623553700235720ustar00rootroot00000000000000//# tTiledLineStepper.cc: Test program for class TiledLineStepper //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include int main (int argc, const char* argv[]) { Input inp(1); inp.version(" "); inp.create("nx", "32", "Number of pixels along the x-axis", "int"); inp.create("ny", "32", "Number of pixels along the y-axis", "int"); inp.create("nz", "32", "Number of pixels along the z-axis", "int"); inp.create("tx", "8", "Tile size along the x-axis", "int"); inp.create("ty", "8", "Tile size along the y-axis", "int"); inp.create("tz", "8", "Tile size along the z-axis", "int"); inp.create("blcx", "0", "Blc along the x-axis", "int"); inp.create("blcy", "0", "Blc along the y-axis", "int"); inp.create("blcz", "0", "Blc along the z-axis", "int"); inp.create("trcx", "1000000", "Trc along the x-axis", "int"); inp.create("trcy", "1000000", "Trc along the y-axis", "int"); inp.create("trcz", "1000000", "Trc along the z-axis", "int"); inp.create("incx", "1", "Inc along the x-axis", "int"); inp.create("incy", "1", "Inc along the y-axis", "int"); inp.create("incz", "1", "Inc along the z-axis", "int"); inp.readArguments(argc, argv); const uInt nx=inp.getInt("nx"); const uInt ny=inp.getInt("ny"); const uInt nz=inp.getInt("nz"); const uInt tx=inp.getInt("tx"); const uInt ty=inp.getInt("ty"); const uInt tz=inp.getInt("tz"); const uInt blcx=inp.getInt("blcx"); const uInt blcy=inp.getInt("blcy"); const uInt blcz=inp.getInt("blcz"); const uInt trcx=inp.getInt("trcx"); const uInt trcy=inp.getInt("trcy"); const uInt trcz=inp.getInt("trcz"); const uInt incx=inp.getInt("incx"); const uInt incy=inp.getInt("incy"); const uInt incz=inp.getInt("incz"); // Check/adapt the values. IPosition shape(3, nx, ny, nz); IPosition tileShape (3, tx, ty, tz); IPosition blc (3, blcx, blcy, blcz); IPosition trc (3, trcx, trcy, trcz); IPosition inc (3, incx, incy, incz); for (uInt i=0; i<3; i++) { AlwaysAssertExit (shape(i) > 0); AlwaysAssertExit (tileShape(i) > 0); AlwaysAssertExit (blc(i) >= 0); AlwaysAssertExit (inc(i) > 0); if (tileShape(i) > shape(i)) tileShape(i) = shape(i); if (blc(i) > shape(i)-1) blc(i) = shape(i)-1; if (trc(i) > shape(i)-1) trc(i) = shape(i)-1; if (trc(i) < blc(i)) trc(i) = blc(i); } cout << "shape = " << shape << endl; cout << "tileshape = " << tileShape << endl; cout << "blc = " << blc << endl; cout << "trc = " << trc << endl; cout << "inc = " << inc << endl; { TiledLineStepper stepper(shape, tileShape, 0); stepper.subSection (blc, trc, inc); while (! stepper.atEnd()) { cout << stepper.position() << ' ' << stepper.endPosition() << ' ' << stepper.cursorShape() << endl; stepper++; } cout << "nsteps = " << stepper.nsteps() << endl; while (! stepper.atStart()) { cout << stepper.position() << ' ' << stepper.endPosition() << ' ' << stepper.cursorShape() << endl; stepper--; } cout << "nsteps = " << stepper.nsteps() << endl; } { TiledLineStepper stepper(shape, tileShape, 1); stepper.subSection (blc, trc, inc); while (! stepper.atEnd()) { cout << stepper.position() << ' ' << stepper.endPosition() << ' ' << stepper.cursorShape() << endl; stepper++; } cout << "nsteps = " << stepper.nsteps() << endl; while (! stepper.atStart()) { cout << stepper.position() << ' ' << stepper.endPosition() << ' ' << stepper.cursorShape() << endl; stepper--; } cout << "nsteps = " << stepper.nsteps() << endl; } } casacore-3.7.1/lattices/Lattices/test/tTiledLineStepper.out000066400000000000000000004262651476623553700240330ustar00rootroot00000000000000./tTiledLineStepper: Version shape = [32, 32, 32] tileshape = [8, 8, 8] blc = [0, 0, 0] trc = [31, 31, 31] inc = [1, 1, 1] [0, 0, 0] [31, 0, 0] [32, 1, 1] [0, 1, 0] [31, 1, 0] [32, 1, 1] [0, 2, 0] [31, 2, 0] [32, 1, 1] [0, 3, 0] [31, 3, 0] [32, 1, 1] [0, 4, 0] [31, 4, 0] [32, 1, 1] [0, 5, 0] [31, 5, 0] [32, 1, 1] [0, 6, 0] [31, 6, 0] [32, 1, 1] [0, 7, 0] [31, 7, 0] [32, 1, 1] [0, 0, 1] [31, 0, 1] [32, 1, 1] [0, 1, 1] [31, 1, 1] [32, 1, 1] [0, 2, 1] [31, 2, 1] [32, 1, 1] [0, 3, 1] [31, 3, 1] [32, 1, 1] [0, 4, 1] [31, 4, 1] [32, 1, 1] [0, 5, 1] [31, 5, 1] [32, 1, 1] [0, 6, 1] [31, 6, 1] [32, 1, 1] [0, 7, 1] [31, 7, 1] [32, 1, 1] [0, 0, 2] [31, 0, 2] [32, 1, 1] [0, 1, 2] [31, 1, 2] [32, 1, 1] [0, 2, 2] [31, 2, 2] [32, 1, 1] [0, 3, 2] [31, 3, 2] [32, 1, 1] [0, 4, 2] [31, 4, 2] [32, 1, 1] [0, 5, 2] [31, 5, 2] [32, 1, 1] [0, 6, 2] [31, 6, 2] [32, 1, 1] [0, 7, 2] [31, 7, 2] [32, 1, 1] [0, 0, 3] [31, 0, 3] [32, 1, 1] [0, 1, 3] [31, 1, 3] [32, 1, 1] [0, 2, 3] [31, 2, 3] [32, 1, 1] [0, 3, 3] [31, 3, 3] [32, 1, 1] [0, 4, 3] [31, 4, 3] [32, 1, 1] [0, 5, 3] [31, 5, 3] [32, 1, 1] [0, 6, 3] [31, 6, 3] [32, 1, 1] [0, 7, 3] [31, 7, 3] [32, 1, 1] [0, 0, 4] [31, 0, 4] [32, 1, 1] [0, 1, 4] [31, 1, 4] [32, 1, 1] [0, 2, 4] [31, 2, 4] [32, 1, 1] [0, 3, 4] [31, 3, 4] [32, 1, 1] [0, 4, 4] [31, 4, 4] [32, 1, 1] [0, 5, 4] [31, 5, 4] [32, 1, 1] [0, 6, 4] [31, 6, 4] [32, 1, 1] [0, 7, 4] [31, 7, 4] [32, 1, 1] [0, 0, 5] [31, 0, 5] [32, 1, 1] [0, 1, 5] [31, 1, 5] [32, 1, 1] [0, 2, 5] [31, 2, 5] [32, 1, 1] [0, 3, 5] [31, 3, 5] [32, 1, 1] [0, 4, 5] [31, 4, 5] [32, 1, 1] [0, 5, 5] [31, 5, 5] [32, 1, 1] [0, 6, 5] [31, 6, 5] [32, 1, 1] [0, 7, 5] [31, 7, 5] [32, 1, 1] [0, 0, 6] [31, 0, 6] [32, 1, 1] [0, 1, 6] [31, 1, 6] [32, 1, 1] [0, 2, 6] [31, 2, 6] [32, 1, 1] [0, 3, 6] [31, 3, 6] [32, 1, 1] [0, 4, 6] [31, 4, 6] [32, 1, 1] [0, 5, 6] [31, 5, 6] [32, 1, 1] [0, 6, 6] [31, 6, 6] [32, 1, 1] [0, 7, 6] [31, 7, 6] [32, 1, 1] [0, 0, 7] [31, 0, 7] [32, 1, 1] [0, 1, 7] [31, 1, 7] [32, 1, 1] [0, 2, 7] [31, 2, 7] [32, 1, 1] [0, 3, 7] [31, 3, 7] [32, 1, 1] [0, 4, 7] [31, 4, 7] [32, 1, 1] [0, 5, 7] [31, 5, 7] [32, 1, 1] [0, 6, 7] [31, 6, 7] [32, 1, 1] [0, 7, 7] [31, 7, 7] [32, 1, 1] [0, 8, 0] [31, 8, 0] [32, 1, 1] [0, 9, 0] [31, 9, 0] [32, 1, 1] [0, 10, 0] [31, 10, 0] [32, 1, 1] [0, 11, 0] [31, 11, 0] [32, 1, 1] [0, 12, 0] [31, 12, 0] [32, 1, 1] [0, 13, 0] [31, 13, 0] [32, 1, 1] [0, 14, 0] [31, 14, 0] [32, 1, 1] [0, 15, 0] [31, 15, 0] [32, 1, 1] [0, 8, 1] [31, 8, 1] [32, 1, 1] [0, 9, 1] [31, 9, 1] [32, 1, 1] [0, 10, 1] [31, 10, 1] [32, 1, 1] [0, 11, 1] [31, 11, 1] [32, 1, 1] [0, 12, 1] [31, 12, 1] [32, 1, 1] [0, 13, 1] [31, 13, 1] [32, 1, 1] [0, 14, 1] [31, 14, 1] [32, 1, 1] [0, 15, 1] [31, 15, 1] [32, 1, 1] [0, 8, 2] [31, 8, 2] [32, 1, 1] [0, 9, 2] [31, 9, 2] [32, 1, 1] [0, 10, 2] [31, 10, 2] [32, 1, 1] [0, 11, 2] [31, 11, 2] [32, 1, 1] [0, 12, 2] [31, 12, 2] [32, 1, 1] [0, 13, 2] [31, 13, 2] [32, 1, 1] [0, 14, 2] [31, 14, 2] [32, 1, 1] [0, 15, 2] [31, 15, 2] [32, 1, 1] [0, 8, 3] [31, 8, 3] [32, 1, 1] [0, 9, 3] [31, 9, 3] [32, 1, 1] [0, 10, 3] [31, 10, 3] [32, 1, 1] [0, 11, 3] [31, 11, 3] [32, 1, 1] [0, 12, 3] [31, 12, 3] [32, 1, 1] [0, 13, 3] [31, 13, 3] [32, 1, 1] [0, 14, 3] [31, 14, 3] [32, 1, 1] [0, 15, 3] [31, 15, 3] [32, 1, 1] [0, 8, 4] [31, 8, 4] [32, 1, 1] [0, 9, 4] [31, 9, 4] [32, 1, 1] [0, 10, 4] [31, 10, 4] [32, 1, 1] [0, 11, 4] [31, 11, 4] [32, 1, 1] [0, 12, 4] [31, 12, 4] [32, 1, 1] [0, 13, 4] [31, 13, 4] [32, 1, 1] [0, 14, 4] [31, 14, 4] [32, 1, 1] [0, 15, 4] [31, 15, 4] [32, 1, 1] [0, 8, 5] [31, 8, 5] [32, 1, 1] [0, 9, 5] [31, 9, 5] [32, 1, 1] [0, 10, 5] [31, 10, 5] [32, 1, 1] [0, 11, 5] [31, 11, 5] [32, 1, 1] [0, 12, 5] [31, 12, 5] [32, 1, 1] [0, 13, 5] [31, 13, 5] [32, 1, 1] [0, 14, 5] [31, 14, 5] [32, 1, 1] [0, 15, 5] [31, 15, 5] [32, 1, 1] [0, 8, 6] [31, 8, 6] [32, 1, 1] [0, 9, 6] [31, 9, 6] [32, 1, 1] [0, 10, 6] [31, 10, 6] [32, 1, 1] [0, 11, 6] [31, 11, 6] [32, 1, 1] [0, 12, 6] [31, 12, 6] [32, 1, 1] [0, 13, 6] [31, 13, 6] [32, 1, 1] [0, 14, 6] [31, 14, 6] [32, 1, 1] [0, 15, 6] [31, 15, 6] [32, 1, 1] [0, 8, 7] [31, 8, 7] [32, 1, 1] [0, 9, 7] [31, 9, 7] [32, 1, 1] [0, 10, 7] [31, 10, 7] [32, 1, 1] [0, 11, 7] [31, 11, 7] [32, 1, 1] [0, 12, 7] [31, 12, 7] [32, 1, 1] [0, 13, 7] [31, 13, 7] [32, 1, 1] [0, 14, 7] [31, 14, 7] [32, 1, 1] [0, 15, 7] [31, 15, 7] [32, 1, 1] [0, 16, 0] [31, 16, 0] [32, 1, 1] [0, 17, 0] [31, 17, 0] [32, 1, 1] [0, 18, 0] [31, 18, 0] [32, 1, 1] [0, 19, 0] [31, 19, 0] [32, 1, 1] [0, 20, 0] [31, 20, 0] [32, 1, 1] [0, 21, 0] [31, 21, 0] [32, 1, 1] [0, 22, 0] [31, 22, 0] [32, 1, 1] [0, 23, 0] [31, 23, 0] [32, 1, 1] [0, 16, 1] [31, 16, 1] [32, 1, 1] [0, 17, 1] [31, 17, 1] [32, 1, 1] [0, 18, 1] [31, 18, 1] [32, 1, 1] [0, 19, 1] [31, 19, 1] [32, 1, 1] [0, 20, 1] [31, 20, 1] [32, 1, 1] [0, 21, 1] [31, 21, 1] [32, 1, 1] [0, 22, 1] [31, 22, 1] [32, 1, 1] [0, 23, 1] [31, 23, 1] [32, 1, 1] [0, 16, 2] [31, 16, 2] [32, 1, 1] [0, 17, 2] [31, 17, 2] [32, 1, 1] [0, 18, 2] [31, 18, 2] [32, 1, 1] [0, 19, 2] [31, 19, 2] [32, 1, 1] [0, 20, 2] [31, 20, 2] [32, 1, 1] [0, 21, 2] [31, 21, 2] [32, 1, 1] [0, 22, 2] [31, 22, 2] [32, 1, 1] [0, 23, 2] [31, 23, 2] [32, 1, 1] [0, 16, 3] [31, 16, 3] [32, 1, 1] [0, 17, 3] [31, 17, 3] [32, 1, 1] [0, 18, 3] [31, 18, 3] [32, 1, 1] [0, 19, 3] [31, 19, 3] [32, 1, 1] [0, 20, 3] [31, 20, 3] [32, 1, 1] [0, 21, 3] [31, 21, 3] [32, 1, 1] [0, 22, 3] [31, 22, 3] [32, 1, 1] [0, 23, 3] [31, 23, 3] [32, 1, 1] [0, 16, 4] [31, 16, 4] [32, 1, 1] [0, 17, 4] [31, 17, 4] [32, 1, 1] [0, 18, 4] [31, 18, 4] [32, 1, 1] [0, 19, 4] [31, 19, 4] [32, 1, 1] [0, 20, 4] [31, 20, 4] [32, 1, 1] [0, 21, 4] [31, 21, 4] [32, 1, 1] [0, 22, 4] [31, 22, 4] [32, 1, 1] [0, 23, 4] [31, 23, 4] [32, 1, 1] [0, 16, 5] [31, 16, 5] [32, 1, 1] [0, 17, 5] [31, 17, 5] [32, 1, 1] [0, 18, 5] [31, 18, 5] [32, 1, 1] [0, 19, 5] [31, 19, 5] [32, 1, 1] [0, 20, 5] [31, 20, 5] [32, 1, 1] [0, 21, 5] [31, 21, 5] [32, 1, 1] [0, 22, 5] [31, 22, 5] [32, 1, 1] [0, 23, 5] [31, 23, 5] [32, 1, 1] [0, 16, 6] [31, 16, 6] [32, 1, 1] [0, 17, 6] [31, 17, 6] [32, 1, 1] [0, 18, 6] [31, 18, 6] [32, 1, 1] [0, 19, 6] [31, 19, 6] [32, 1, 1] [0, 20, 6] [31, 20, 6] [32, 1, 1] [0, 21, 6] [31, 21, 6] [32, 1, 1] [0, 22, 6] [31, 22, 6] [32, 1, 1] [0, 23, 6] [31, 23, 6] [32, 1, 1] [0, 16, 7] [31, 16, 7] [32, 1, 1] [0, 17, 7] [31, 17, 7] [32, 1, 1] [0, 18, 7] [31, 18, 7] [32, 1, 1] [0, 19, 7] [31, 19, 7] [32, 1, 1] [0, 20, 7] [31, 20, 7] [32, 1, 1] [0, 21, 7] [31, 21, 7] [32, 1, 1] [0, 22, 7] [31, 22, 7] [32, 1, 1] [0, 23, 7] [31, 23, 7] [32, 1, 1] [0, 24, 0] [31, 24, 0] [32, 1, 1] [0, 25, 0] [31, 25, 0] [32, 1, 1] [0, 26, 0] [31, 26, 0] [32, 1, 1] [0, 27, 0] [31, 27, 0] [32, 1, 1] [0, 28, 0] [31, 28, 0] [32, 1, 1] [0, 29, 0] [31, 29, 0] [32, 1, 1] [0, 30, 0] [31, 30, 0] [32, 1, 1] [0, 31, 0] [31, 31, 0] [32, 1, 1] [0, 24, 1] [31, 24, 1] [32, 1, 1] [0, 25, 1] [31, 25, 1] [32, 1, 1] [0, 26, 1] [31, 26, 1] [32, 1, 1] [0, 27, 1] [31, 27, 1] [32, 1, 1] [0, 28, 1] [31, 28, 1] [32, 1, 1] [0, 29, 1] [31, 29, 1] [32, 1, 1] [0, 30, 1] [31, 30, 1] [32, 1, 1] [0, 31, 1] [31, 31, 1] [32, 1, 1] [0, 24, 2] [31, 24, 2] [32, 1, 1] [0, 25, 2] [31, 25, 2] [32, 1, 1] [0, 26, 2] [31, 26, 2] [32, 1, 1] [0, 27, 2] [31, 27, 2] [32, 1, 1] [0, 28, 2] [31, 28, 2] [32, 1, 1] [0, 29, 2] [31, 29, 2] [32, 1, 1] [0, 30, 2] [31, 30, 2] [32, 1, 1] [0, 31, 2] [31, 31, 2] [32, 1, 1] [0, 24, 3] [31, 24, 3] [32, 1, 1] [0, 25, 3] [31, 25, 3] [32, 1, 1] [0, 26, 3] [31, 26, 3] [32, 1, 1] [0, 27, 3] [31, 27, 3] [32, 1, 1] [0, 28, 3] [31, 28, 3] [32, 1, 1] [0, 29, 3] [31, 29, 3] [32, 1, 1] [0, 30, 3] [31, 30, 3] [32, 1, 1] [0, 31, 3] [31, 31, 3] [32, 1, 1] [0, 24, 4] [31, 24, 4] [32, 1, 1] [0, 25, 4] [31, 25, 4] [32, 1, 1] [0, 26, 4] [31, 26, 4] [32, 1, 1] [0, 27, 4] [31, 27, 4] [32, 1, 1] [0, 28, 4] [31, 28, 4] [32, 1, 1] [0, 29, 4] [31, 29, 4] [32, 1, 1] [0, 30, 4] [31, 30, 4] [32, 1, 1] [0, 31, 4] [31, 31, 4] [32, 1, 1] [0, 24, 5] [31, 24, 5] [32, 1, 1] [0, 25, 5] [31, 25, 5] [32, 1, 1] [0, 26, 5] [31, 26, 5] [32, 1, 1] [0, 27, 5] [31, 27, 5] [32, 1, 1] [0, 28, 5] [31, 28, 5] [32, 1, 1] [0, 29, 5] [31, 29, 5] [32, 1, 1] [0, 30, 5] [31, 30, 5] [32, 1, 1] [0, 31, 5] [31, 31, 5] [32, 1, 1] [0, 24, 6] [31, 24, 6] [32, 1, 1] [0, 25, 6] [31, 25, 6] [32, 1, 1] [0, 26, 6] [31, 26, 6] [32, 1, 1] [0, 27, 6] [31, 27, 6] [32, 1, 1] [0, 28, 6] [31, 28, 6] [32, 1, 1] [0, 29, 6] [31, 29, 6] [32, 1, 1] [0, 30, 6] [31, 30, 6] [32, 1, 1] [0, 31, 6] [31, 31, 6] [32, 1, 1] [0, 24, 7] [31, 24, 7] [32, 1, 1] [0, 25, 7] [31, 25, 7] [32, 1, 1] [0, 26, 7] [31, 26, 7] [32, 1, 1] [0, 27, 7] [31, 27, 7] [32, 1, 1] [0, 28, 7] [31, 28, 7] [32, 1, 1] [0, 29, 7] [31, 29, 7] [32, 1, 1] [0, 30, 7] [31, 30, 7] [32, 1, 1] [0, 31, 7] [31, 31, 7] [32, 1, 1] [0, 0, 8] [31, 0, 8] [32, 1, 1] [0, 1, 8] [31, 1, 8] [32, 1, 1] [0, 2, 8] [31, 2, 8] [32, 1, 1] [0, 3, 8] [31, 3, 8] [32, 1, 1] [0, 4, 8] [31, 4, 8] [32, 1, 1] [0, 5, 8] [31, 5, 8] [32, 1, 1] [0, 6, 8] [31, 6, 8] [32, 1, 1] [0, 7, 8] [31, 7, 8] [32, 1, 1] [0, 0, 9] [31, 0, 9] [32, 1, 1] [0, 1, 9] [31, 1, 9] [32, 1, 1] [0, 2, 9] [31, 2, 9] [32, 1, 1] [0, 3, 9] [31, 3, 9] [32, 1, 1] [0, 4, 9] [31, 4, 9] [32, 1, 1] [0, 5, 9] [31, 5, 9] [32, 1, 1] [0, 6, 9] [31, 6, 9] [32, 1, 1] [0, 7, 9] [31, 7, 9] [32, 1, 1] [0, 0, 10] [31, 0, 10] [32, 1, 1] [0, 1, 10] [31, 1, 10] [32, 1, 1] [0, 2, 10] [31, 2, 10] [32, 1, 1] [0, 3, 10] [31, 3, 10] [32, 1, 1] [0, 4, 10] [31, 4, 10] [32, 1, 1] [0, 5, 10] [31, 5, 10] [32, 1, 1] [0, 6, 10] [31, 6, 10] [32, 1, 1] [0, 7, 10] [31, 7, 10] [32, 1, 1] [0, 0, 11] [31, 0, 11] [32, 1, 1] [0, 1, 11] [31, 1, 11] [32, 1, 1] [0, 2, 11] [31, 2, 11] [32, 1, 1] [0, 3, 11] [31, 3, 11] [32, 1, 1] [0, 4, 11] [31, 4, 11] [32, 1, 1] [0, 5, 11] [31, 5, 11] [32, 1, 1] [0, 6, 11] [31, 6, 11] [32, 1, 1] [0, 7, 11] [31, 7, 11] [32, 1, 1] [0, 0, 12] [31, 0, 12] [32, 1, 1] [0, 1, 12] [31, 1, 12] [32, 1, 1] [0, 2, 12] [31, 2, 12] [32, 1, 1] [0, 3, 12] [31, 3, 12] [32, 1, 1] [0, 4, 12] [31, 4, 12] [32, 1, 1] [0, 5, 12] [31, 5, 12] [32, 1, 1] [0, 6, 12] [31, 6, 12] [32, 1, 1] [0, 7, 12] [31, 7, 12] [32, 1, 1] [0, 0, 13] [31, 0, 13] [32, 1, 1] [0, 1, 13] [31, 1, 13] [32, 1, 1] [0, 2, 13] [31, 2, 13] [32, 1, 1] [0, 3, 13] [31, 3, 13] [32, 1, 1] [0, 4, 13] [31, 4, 13] [32, 1, 1] [0, 5, 13] [31, 5, 13] [32, 1, 1] [0, 6, 13] [31, 6, 13] [32, 1, 1] [0, 7, 13] [31, 7, 13] [32, 1, 1] [0, 0, 14] [31, 0, 14] [32, 1, 1] [0, 1, 14] [31, 1, 14] [32, 1, 1] [0, 2, 14] [31, 2, 14] [32, 1, 1] [0, 3, 14] [31, 3, 14] [32, 1, 1] [0, 4, 14] [31, 4, 14] [32, 1, 1] [0, 5, 14] [31, 5, 14] [32, 1, 1] [0, 6, 14] [31, 6, 14] [32, 1, 1] [0, 7, 14] [31, 7, 14] [32, 1, 1] [0, 0, 15] [31, 0, 15] [32, 1, 1] [0, 1, 15] [31, 1, 15] [32, 1, 1] [0, 2, 15] [31, 2, 15] [32, 1, 1] [0, 3, 15] [31, 3, 15] [32, 1, 1] [0, 4, 15] [31, 4, 15] [32, 1, 1] [0, 5, 15] [31, 5, 15] [32, 1, 1] [0, 6, 15] [31, 6, 15] [32, 1, 1] [0, 7, 15] [31, 7, 15] [32, 1, 1] [0, 8, 8] [31, 8, 8] [32, 1, 1] [0, 9, 8] [31, 9, 8] [32, 1, 1] [0, 10, 8] [31, 10, 8] [32, 1, 1] [0, 11, 8] [31, 11, 8] [32, 1, 1] [0, 12, 8] [31, 12, 8] [32, 1, 1] [0, 13, 8] [31, 13, 8] [32, 1, 1] [0, 14, 8] [31, 14, 8] [32, 1, 1] [0, 15, 8] [31, 15, 8] [32, 1, 1] [0, 8, 9] [31, 8, 9] [32, 1, 1] [0, 9, 9] [31, 9, 9] [32, 1, 1] [0, 10, 9] [31, 10, 9] [32, 1, 1] [0, 11, 9] [31, 11, 9] [32, 1, 1] [0, 12, 9] [31, 12, 9] [32, 1, 1] [0, 13, 9] [31, 13, 9] [32, 1, 1] [0, 14, 9] [31, 14, 9] [32, 1, 1] [0, 15, 9] [31, 15, 9] [32, 1, 1] [0, 8, 10] [31, 8, 10] [32, 1, 1] [0, 9, 10] [31, 9, 10] [32, 1, 1] [0, 10, 10] [31, 10, 10] [32, 1, 1] [0, 11, 10] [31, 11, 10] [32, 1, 1] [0, 12, 10] [31, 12, 10] [32, 1, 1] [0, 13, 10] [31, 13, 10] [32, 1, 1] [0, 14, 10] [31, 14, 10] [32, 1, 1] [0, 15, 10] [31, 15, 10] [32, 1, 1] [0, 8, 11] [31, 8, 11] [32, 1, 1] [0, 9, 11] [31, 9, 11] [32, 1, 1] [0, 10, 11] [31, 10, 11] [32, 1, 1] [0, 11, 11] [31, 11, 11] [32, 1, 1] [0, 12, 11] [31, 12, 11] [32, 1, 1] [0, 13, 11] [31, 13, 11] [32, 1, 1] [0, 14, 11] [31, 14, 11] [32, 1, 1] [0, 15, 11] [31, 15, 11] [32, 1, 1] [0, 8, 12] [31, 8, 12] [32, 1, 1] [0, 9, 12] [31, 9, 12] [32, 1, 1] [0, 10, 12] [31, 10, 12] [32, 1, 1] [0, 11, 12] [31, 11, 12] [32, 1, 1] [0, 12, 12] [31, 12, 12] [32, 1, 1] [0, 13, 12] [31, 13, 12] [32, 1, 1] [0, 14, 12] [31, 14, 12] [32, 1, 1] [0, 15, 12] [31, 15, 12] [32, 1, 1] [0, 8, 13] [31, 8, 13] [32, 1, 1] [0, 9, 13] [31, 9, 13] [32, 1, 1] [0, 10, 13] [31, 10, 13] [32, 1, 1] [0, 11, 13] [31, 11, 13] [32, 1, 1] [0, 12, 13] [31, 12, 13] [32, 1, 1] [0, 13, 13] [31, 13, 13] [32, 1, 1] [0, 14, 13] [31, 14, 13] [32, 1, 1] [0, 15, 13] [31, 15, 13] [32, 1, 1] [0, 8, 14] [31, 8, 14] [32, 1, 1] [0, 9, 14] [31, 9, 14] [32, 1, 1] [0, 10, 14] [31, 10, 14] [32, 1, 1] [0, 11, 14] [31, 11, 14] [32, 1, 1] [0, 12, 14] [31, 12, 14] [32, 1, 1] [0, 13, 14] [31, 13, 14] [32, 1, 1] [0, 14, 14] [31, 14, 14] [32, 1, 1] [0, 15, 14] [31, 15, 14] [32, 1, 1] [0, 8, 15] [31, 8, 15] [32, 1, 1] [0, 9, 15] [31, 9, 15] [32, 1, 1] [0, 10, 15] [31, 10, 15] [32, 1, 1] [0, 11, 15] [31, 11, 15] [32, 1, 1] [0, 12, 15] [31, 12, 15] [32, 1, 1] [0, 13, 15] [31, 13, 15] [32, 1, 1] [0, 14, 15] [31, 14, 15] [32, 1, 1] [0, 15, 15] [31, 15, 15] [32, 1, 1] [0, 16, 8] [31, 16, 8] [32, 1, 1] [0, 17, 8] [31, 17, 8] [32, 1, 1] [0, 18, 8] [31, 18, 8] [32, 1, 1] [0, 19, 8] [31, 19, 8] [32, 1, 1] [0, 20, 8] [31, 20, 8] [32, 1, 1] [0, 21, 8] [31, 21, 8] [32, 1, 1] [0, 22, 8] [31, 22, 8] [32, 1, 1] [0, 23, 8] [31, 23, 8] [32, 1, 1] [0, 16, 9] [31, 16, 9] [32, 1, 1] [0, 17, 9] [31, 17, 9] [32, 1, 1] [0, 18, 9] [31, 18, 9] [32, 1, 1] [0, 19, 9] [31, 19, 9] [32, 1, 1] [0, 20, 9] [31, 20, 9] [32, 1, 1] [0, 21, 9] [31, 21, 9] [32, 1, 1] [0, 22, 9] [31, 22, 9] [32, 1, 1] [0, 23, 9] [31, 23, 9] [32, 1, 1] [0, 16, 10] [31, 16, 10] [32, 1, 1] [0, 17, 10] [31, 17, 10] [32, 1, 1] [0, 18, 10] [31, 18, 10] [32, 1, 1] [0, 19, 10] [31, 19, 10] [32, 1, 1] [0, 20, 10] [31, 20, 10] [32, 1, 1] [0, 21, 10] [31, 21, 10] [32, 1, 1] [0, 22, 10] [31, 22, 10] [32, 1, 1] [0, 23, 10] [31, 23, 10] [32, 1, 1] [0, 16, 11] [31, 16, 11] [32, 1, 1] [0, 17, 11] [31, 17, 11] [32, 1, 1] [0, 18, 11] [31, 18, 11] [32, 1, 1] [0, 19, 11] [31, 19, 11] [32, 1, 1] [0, 20, 11] [31, 20, 11] [32, 1, 1] [0, 21, 11] [31, 21, 11] [32, 1, 1] [0, 22, 11] [31, 22, 11] [32, 1, 1] [0, 23, 11] [31, 23, 11] [32, 1, 1] [0, 16, 12] [31, 16, 12] [32, 1, 1] [0, 17, 12] [31, 17, 12] [32, 1, 1] [0, 18, 12] [31, 18, 12] [32, 1, 1] [0, 19, 12] [31, 19, 12] [32, 1, 1] [0, 20, 12] [31, 20, 12] [32, 1, 1] [0, 21, 12] [31, 21, 12] [32, 1, 1] [0, 22, 12] [31, 22, 12] [32, 1, 1] [0, 23, 12] [31, 23, 12] [32, 1, 1] [0, 16, 13] [31, 16, 13] [32, 1, 1] [0, 17, 13] [31, 17, 13] [32, 1, 1] [0, 18, 13] [31, 18, 13] [32, 1, 1] [0, 19, 13] [31, 19, 13] [32, 1, 1] [0, 20, 13] [31, 20, 13] [32, 1, 1] [0, 21, 13] [31, 21, 13] [32, 1, 1] [0, 22, 13] [31, 22, 13] [32, 1, 1] [0, 23, 13] [31, 23, 13] [32, 1, 1] [0, 16, 14] [31, 16, 14] [32, 1, 1] [0, 17, 14] [31, 17, 14] [32, 1, 1] [0, 18, 14] [31, 18, 14] [32, 1, 1] [0, 19, 14] [31, 19, 14] [32, 1, 1] [0, 20, 14] [31, 20, 14] [32, 1, 1] [0, 21, 14] [31, 21, 14] [32, 1, 1] [0, 22, 14] [31, 22, 14] [32, 1, 1] [0, 23, 14] [31, 23, 14] [32, 1, 1] [0, 16, 15] [31, 16, 15] [32, 1, 1] [0, 17, 15] [31, 17, 15] [32, 1, 1] [0, 18, 15] [31, 18, 15] [32, 1, 1] [0, 19, 15] [31, 19, 15] [32, 1, 1] [0, 20, 15] [31, 20, 15] [32, 1, 1] [0, 21, 15] [31, 21, 15] [32, 1, 1] [0, 22, 15] [31, 22, 15] [32, 1, 1] [0, 23, 15] [31, 23, 15] [32, 1, 1] [0, 24, 8] [31, 24, 8] [32, 1, 1] [0, 25, 8] [31, 25, 8] [32, 1, 1] [0, 26, 8] [31, 26, 8] [32, 1, 1] [0, 27, 8] [31, 27, 8] [32, 1, 1] [0, 28, 8] [31, 28, 8] [32, 1, 1] [0, 29, 8] [31, 29, 8] [32, 1, 1] [0, 30, 8] [31, 30, 8] [32, 1, 1] [0, 31, 8] [31, 31, 8] [32, 1, 1] [0, 24, 9] [31, 24, 9] [32, 1, 1] [0, 25, 9] [31, 25, 9] [32, 1, 1] [0, 26, 9] [31, 26, 9] [32, 1, 1] [0, 27, 9] [31, 27, 9] [32, 1, 1] [0, 28, 9] [31, 28, 9] [32, 1, 1] [0, 29, 9] [31, 29, 9] [32, 1, 1] [0, 30, 9] [31, 30, 9] [32, 1, 1] [0, 31, 9] [31, 31, 9] [32, 1, 1] [0, 24, 10] [31, 24, 10] [32, 1, 1] [0, 25, 10] [31, 25, 10] [32, 1, 1] [0, 26, 10] [31, 26, 10] [32, 1, 1] [0, 27, 10] [31, 27, 10] [32, 1, 1] [0, 28, 10] [31, 28, 10] [32, 1, 1] [0, 29, 10] [31, 29, 10] [32, 1, 1] [0, 30, 10] [31, 30, 10] [32, 1, 1] [0, 31, 10] [31, 31, 10] [32, 1, 1] [0, 24, 11] [31, 24, 11] [32, 1, 1] [0, 25, 11] [31, 25, 11] [32, 1, 1] [0, 26, 11] [31, 26, 11] [32, 1, 1] [0, 27, 11] [31, 27, 11] [32, 1, 1] [0, 28, 11] [31, 28, 11] [32, 1, 1] [0, 29, 11] [31, 29, 11] [32, 1, 1] [0, 30, 11] [31, 30, 11] [32, 1, 1] [0, 31, 11] [31, 31, 11] [32, 1, 1] [0, 24, 12] [31, 24, 12] [32, 1, 1] [0, 25, 12] [31, 25, 12] [32, 1, 1] [0, 26, 12] [31, 26, 12] [32, 1, 1] [0, 27, 12] [31, 27, 12] [32, 1, 1] [0, 28, 12] [31, 28, 12] [32, 1, 1] [0, 29, 12] [31, 29, 12] [32, 1, 1] [0, 30, 12] [31, 30, 12] [32, 1, 1] [0, 31, 12] [31, 31, 12] [32, 1, 1] [0, 24, 13] [31, 24, 13] [32, 1, 1] [0, 25, 13] [31, 25, 13] [32, 1, 1] [0, 26, 13] [31, 26, 13] [32, 1, 1] [0, 27, 13] [31, 27, 13] [32, 1, 1] [0, 28, 13] [31, 28, 13] [32, 1, 1] [0, 29, 13] [31, 29, 13] [32, 1, 1] [0, 30, 13] [31, 30, 13] [32, 1, 1] [0, 31, 13] [31, 31, 13] [32, 1, 1] [0, 24, 14] [31, 24, 14] [32, 1, 1] [0, 25, 14] [31, 25, 14] [32, 1, 1] [0, 26, 14] [31, 26, 14] [32, 1, 1] [0, 27, 14] [31, 27, 14] [32, 1, 1] [0, 28, 14] [31, 28, 14] [32, 1, 1] [0, 29, 14] [31, 29, 14] [32, 1, 1] [0, 30, 14] [31, 30, 14] [32, 1, 1] [0, 31, 14] [31, 31, 14] [32, 1, 1] [0, 24, 15] [31, 24, 15] [32, 1, 1] [0, 25, 15] [31, 25, 15] [32, 1, 1] [0, 26, 15] [31, 26, 15] [32, 1, 1] [0, 27, 15] [31, 27, 15] [32, 1, 1] [0, 28, 15] [31, 28, 15] [32, 1, 1] [0, 29, 15] [31, 29, 15] [32, 1, 1] [0, 30, 15] [31, 30, 15] [32, 1, 1] [0, 31, 15] [31, 31, 15] [32, 1, 1] [0, 0, 16] [31, 0, 16] [32, 1, 1] [0, 1, 16] [31, 1, 16] [32, 1, 1] [0, 2, 16] [31, 2, 16] [32, 1, 1] [0, 3, 16] [31, 3, 16] [32, 1, 1] [0, 4, 16] [31, 4, 16] [32, 1, 1] [0, 5, 16] [31, 5, 16] [32, 1, 1] [0, 6, 16] [31, 6, 16] [32, 1, 1] [0, 7, 16] [31, 7, 16] [32, 1, 1] [0, 0, 17] [31, 0, 17] [32, 1, 1] [0, 1, 17] [31, 1, 17] [32, 1, 1] [0, 2, 17] [31, 2, 17] [32, 1, 1] [0, 3, 17] [31, 3, 17] [32, 1, 1] [0, 4, 17] [31, 4, 17] [32, 1, 1] [0, 5, 17] [31, 5, 17] [32, 1, 1] [0, 6, 17] [31, 6, 17] [32, 1, 1] [0, 7, 17] [31, 7, 17] [32, 1, 1] [0, 0, 18] [31, 0, 18] [32, 1, 1] [0, 1, 18] [31, 1, 18] [32, 1, 1] [0, 2, 18] [31, 2, 18] [32, 1, 1] [0, 3, 18] [31, 3, 18] [32, 1, 1] [0, 4, 18] [31, 4, 18] [32, 1, 1] [0, 5, 18] [31, 5, 18] [32, 1, 1] [0, 6, 18] [31, 6, 18] [32, 1, 1] [0, 7, 18] [31, 7, 18] [32, 1, 1] [0, 0, 19] [31, 0, 19] [32, 1, 1] [0, 1, 19] [31, 1, 19] [32, 1, 1] [0, 2, 19] [31, 2, 19] [32, 1, 1] [0, 3, 19] [31, 3, 19] [32, 1, 1] [0, 4, 19] [31, 4, 19] [32, 1, 1] [0, 5, 19] [31, 5, 19] [32, 1, 1] [0, 6, 19] [31, 6, 19] [32, 1, 1] [0, 7, 19] [31, 7, 19] [32, 1, 1] [0, 0, 20] [31, 0, 20] [32, 1, 1] [0, 1, 20] [31, 1, 20] [32, 1, 1] [0, 2, 20] [31, 2, 20] [32, 1, 1] [0, 3, 20] [31, 3, 20] [32, 1, 1] [0, 4, 20] [31, 4, 20] [32, 1, 1] [0, 5, 20] [31, 5, 20] [32, 1, 1] [0, 6, 20] [31, 6, 20] [32, 1, 1] [0, 7, 20] [31, 7, 20] [32, 1, 1] [0, 0, 21] [31, 0, 21] [32, 1, 1] [0, 1, 21] [31, 1, 21] [32, 1, 1] [0, 2, 21] [31, 2, 21] [32, 1, 1] [0, 3, 21] [31, 3, 21] [32, 1, 1] [0, 4, 21] [31, 4, 21] [32, 1, 1] [0, 5, 21] [31, 5, 21] [32, 1, 1] [0, 6, 21] [31, 6, 21] [32, 1, 1] [0, 7, 21] [31, 7, 21] [32, 1, 1] [0, 0, 22] [31, 0, 22] [32, 1, 1] [0, 1, 22] [31, 1, 22] [32, 1, 1] [0, 2, 22] [31, 2, 22] [32, 1, 1] [0, 3, 22] [31, 3, 22] [32, 1, 1] [0, 4, 22] [31, 4, 22] [32, 1, 1] [0, 5, 22] [31, 5, 22] [32, 1, 1] [0, 6, 22] [31, 6, 22] [32, 1, 1] [0, 7, 22] [31, 7, 22] [32, 1, 1] [0, 0, 23] [31, 0, 23] [32, 1, 1] [0, 1, 23] [31, 1, 23] [32, 1, 1] [0, 2, 23] [31, 2, 23] [32, 1, 1] [0, 3, 23] [31, 3, 23] [32, 1, 1] [0, 4, 23] [31, 4, 23] [32, 1, 1] [0, 5, 23] [31, 5, 23] [32, 1, 1] [0, 6, 23] [31, 6, 23] [32, 1, 1] [0, 7, 23] [31, 7, 23] [32, 1, 1] [0, 8, 16] [31, 8, 16] [32, 1, 1] [0, 9, 16] [31, 9, 16] [32, 1, 1] [0, 10, 16] [31, 10, 16] [32, 1, 1] [0, 11, 16] [31, 11, 16] [32, 1, 1] [0, 12, 16] [31, 12, 16] [32, 1, 1] [0, 13, 16] [31, 13, 16] [32, 1, 1] [0, 14, 16] [31, 14, 16] [32, 1, 1] [0, 15, 16] [31, 15, 16] [32, 1, 1] [0, 8, 17] [31, 8, 17] [32, 1, 1] [0, 9, 17] [31, 9, 17] [32, 1, 1] [0, 10, 17] [31, 10, 17] [32, 1, 1] [0, 11, 17] [31, 11, 17] [32, 1, 1] [0, 12, 17] [31, 12, 17] [32, 1, 1] [0, 13, 17] [31, 13, 17] [32, 1, 1] [0, 14, 17] [31, 14, 17] [32, 1, 1] [0, 15, 17] [31, 15, 17] [32, 1, 1] [0, 8, 18] [31, 8, 18] [32, 1, 1] [0, 9, 18] [31, 9, 18] [32, 1, 1] [0, 10, 18] [31, 10, 18] [32, 1, 1] [0, 11, 18] [31, 11, 18] [32, 1, 1] [0, 12, 18] [31, 12, 18] [32, 1, 1] [0, 13, 18] [31, 13, 18] [32, 1, 1] [0, 14, 18] [31, 14, 18] [32, 1, 1] [0, 15, 18] [31, 15, 18] [32, 1, 1] [0, 8, 19] [31, 8, 19] [32, 1, 1] [0, 9, 19] [31, 9, 19] [32, 1, 1] [0, 10, 19] [31, 10, 19] [32, 1, 1] [0, 11, 19] [31, 11, 19] [32, 1, 1] [0, 12, 19] [31, 12, 19] [32, 1, 1] [0, 13, 19] [31, 13, 19] [32, 1, 1] [0, 14, 19] [31, 14, 19] [32, 1, 1] [0, 15, 19] [31, 15, 19] [32, 1, 1] [0, 8, 20] [31, 8, 20] [32, 1, 1] [0, 9, 20] [31, 9, 20] [32, 1, 1] [0, 10, 20] [31, 10, 20] [32, 1, 1] [0, 11, 20] [31, 11, 20] [32, 1, 1] [0, 12, 20] [31, 12, 20] [32, 1, 1] [0, 13, 20] [31, 13, 20] [32, 1, 1] [0, 14, 20] [31, 14, 20] [32, 1, 1] [0, 15, 20] [31, 15, 20] [32, 1, 1] [0, 8, 21] [31, 8, 21] [32, 1, 1] [0, 9, 21] [31, 9, 21] [32, 1, 1] [0, 10, 21] [31, 10, 21] [32, 1, 1] [0, 11, 21] [31, 11, 21] [32, 1, 1] [0, 12, 21] [31, 12, 21] [32, 1, 1] [0, 13, 21] [31, 13, 21] [32, 1, 1] [0, 14, 21] [31, 14, 21] [32, 1, 1] [0, 15, 21] [31, 15, 21] [32, 1, 1] [0, 8, 22] [31, 8, 22] [32, 1, 1] [0, 9, 22] [31, 9, 22] [32, 1, 1] [0, 10, 22] [31, 10, 22] [32, 1, 1] [0, 11, 22] [31, 11, 22] [32, 1, 1] [0, 12, 22] [31, 12, 22] [32, 1, 1] [0, 13, 22] [31, 13, 22] [32, 1, 1] [0, 14, 22] [31, 14, 22] [32, 1, 1] [0, 15, 22] [31, 15, 22] [32, 1, 1] [0, 8, 23] [31, 8, 23] [32, 1, 1] [0, 9, 23] [31, 9, 23] [32, 1, 1] [0, 10, 23] [31, 10, 23] [32, 1, 1] [0, 11, 23] [31, 11, 23] [32, 1, 1] [0, 12, 23] [31, 12, 23] [32, 1, 1] [0, 13, 23] [31, 13, 23] [32, 1, 1] [0, 14, 23] [31, 14, 23] [32, 1, 1] [0, 15, 23] [31, 15, 23] [32, 1, 1] [0, 16, 16] [31, 16, 16] [32, 1, 1] [0, 17, 16] [31, 17, 16] [32, 1, 1] [0, 18, 16] [31, 18, 16] [32, 1, 1] [0, 19, 16] [31, 19, 16] [32, 1, 1] [0, 20, 16] [31, 20, 16] [32, 1, 1] [0, 21, 16] [31, 21, 16] [32, 1, 1] [0, 22, 16] [31, 22, 16] [32, 1, 1] [0, 23, 16] [31, 23, 16] [32, 1, 1] [0, 16, 17] [31, 16, 17] [32, 1, 1] [0, 17, 17] [31, 17, 17] [32, 1, 1] [0, 18, 17] [31, 18, 17] [32, 1, 1] [0, 19, 17] [31, 19, 17] [32, 1, 1] [0, 20, 17] [31, 20, 17] [32, 1, 1] [0, 21, 17] [31, 21, 17] [32, 1, 1] [0, 22, 17] [31, 22, 17] [32, 1, 1] [0, 23, 17] [31, 23, 17] [32, 1, 1] [0, 16, 18] [31, 16, 18] [32, 1, 1] [0, 17, 18] [31, 17, 18] [32, 1, 1] [0, 18, 18] [31, 18, 18] [32, 1, 1] [0, 19, 18] [31, 19, 18] [32, 1, 1] [0, 20, 18] [31, 20, 18] [32, 1, 1] [0, 21, 18] [31, 21, 18] [32, 1, 1] [0, 22, 18] [31, 22, 18] [32, 1, 1] [0, 23, 18] [31, 23, 18] [32, 1, 1] [0, 16, 19] [31, 16, 19] [32, 1, 1] [0, 17, 19] [31, 17, 19] [32, 1, 1] [0, 18, 19] [31, 18, 19] [32, 1, 1] [0, 19, 19] [31, 19, 19] [32, 1, 1] [0, 20, 19] [31, 20, 19] [32, 1, 1] [0, 21, 19] [31, 21, 19] [32, 1, 1] [0, 22, 19] [31, 22, 19] [32, 1, 1] [0, 23, 19] [31, 23, 19] [32, 1, 1] [0, 16, 20] [31, 16, 20] [32, 1, 1] [0, 17, 20] [31, 17, 20] [32, 1, 1] [0, 18, 20] [31, 18, 20] [32, 1, 1] [0, 19, 20] [31, 19, 20] [32, 1, 1] [0, 20, 20] [31, 20, 20] [32, 1, 1] [0, 21, 20] [31, 21, 20] [32, 1, 1] [0, 22, 20] [31, 22, 20] [32, 1, 1] [0, 23, 20] [31, 23, 20] [32, 1, 1] [0, 16, 21] [31, 16, 21] [32, 1, 1] [0, 17, 21] [31, 17, 21] [32, 1, 1] [0, 18, 21] [31, 18, 21] [32, 1, 1] [0, 19, 21] [31, 19, 21] [32, 1, 1] [0, 20, 21] [31, 20, 21] [32, 1, 1] [0, 21, 21] [31, 21, 21] [32, 1, 1] [0, 22, 21] [31, 22, 21] [32, 1, 1] [0, 23, 21] [31, 23, 21] [32, 1, 1] [0, 16, 22] [31, 16, 22] [32, 1, 1] [0, 17, 22] [31, 17, 22] [32, 1, 1] [0, 18, 22] [31, 18, 22] [32, 1, 1] [0, 19, 22] [31, 19, 22] [32, 1, 1] [0, 20, 22] [31, 20, 22] [32, 1, 1] [0, 21, 22] [31, 21, 22] [32, 1, 1] [0, 22, 22] [31, 22, 22] [32, 1, 1] [0, 23, 22] [31, 23, 22] [32, 1, 1] [0, 16, 23] [31, 16, 23] [32, 1, 1] [0, 17, 23] [31, 17, 23] [32, 1, 1] [0, 18, 23] [31, 18, 23] [32, 1, 1] [0, 19, 23] [31, 19, 23] [32, 1, 1] [0, 20, 23] [31, 20, 23] [32, 1, 1] [0, 21, 23] [31, 21, 23] [32, 1, 1] [0, 22, 23] [31, 22, 23] [32, 1, 1] [0, 23, 23] [31, 23, 23] [32, 1, 1] [0, 24, 16] [31, 24, 16] [32, 1, 1] [0, 25, 16] [31, 25, 16] [32, 1, 1] [0, 26, 16] [31, 26, 16] [32, 1, 1] [0, 27, 16] [31, 27, 16] [32, 1, 1] [0, 28, 16] [31, 28, 16] [32, 1, 1] [0, 29, 16] [31, 29, 16] [32, 1, 1] [0, 30, 16] [31, 30, 16] [32, 1, 1] [0, 31, 16] [31, 31, 16] [32, 1, 1] [0, 24, 17] [31, 24, 17] [32, 1, 1] [0, 25, 17] [31, 25, 17] [32, 1, 1] [0, 26, 17] [31, 26, 17] [32, 1, 1] [0, 27, 17] [31, 27, 17] [32, 1, 1] [0, 28, 17] [31, 28, 17] [32, 1, 1] [0, 29, 17] [31, 29, 17] [32, 1, 1] [0, 30, 17] [31, 30, 17] [32, 1, 1] [0, 31, 17] [31, 31, 17] [32, 1, 1] [0, 24, 18] [31, 24, 18] [32, 1, 1] [0, 25, 18] [31, 25, 18] [32, 1, 1] [0, 26, 18] [31, 26, 18] [32, 1, 1] [0, 27, 18] [31, 27, 18] [32, 1, 1] [0, 28, 18] [31, 28, 18] [32, 1, 1] [0, 29, 18] [31, 29, 18] [32, 1, 1] [0, 30, 18] [31, 30, 18] [32, 1, 1] [0, 31, 18] [31, 31, 18] [32, 1, 1] [0, 24, 19] [31, 24, 19] [32, 1, 1] [0, 25, 19] [31, 25, 19] [32, 1, 1] [0, 26, 19] [31, 26, 19] [32, 1, 1] [0, 27, 19] [31, 27, 19] [32, 1, 1] [0, 28, 19] [31, 28, 19] [32, 1, 1] [0, 29, 19] [31, 29, 19] [32, 1, 1] [0, 30, 19] [31, 30, 19] [32, 1, 1] [0, 31, 19] [31, 31, 19] [32, 1, 1] [0, 24, 20] [31, 24, 20] [32, 1, 1] [0, 25, 20] [31, 25, 20] [32, 1, 1] [0, 26, 20] [31, 26, 20] [32, 1, 1] [0, 27, 20] [31, 27, 20] [32, 1, 1] [0, 28, 20] [31, 28, 20] [32, 1, 1] [0, 29, 20] [31, 29, 20] [32, 1, 1] [0, 30, 20] [31, 30, 20] [32, 1, 1] [0, 31, 20] [31, 31, 20] [32, 1, 1] [0, 24, 21] [31, 24, 21] [32, 1, 1] [0, 25, 21] [31, 25, 21] [32, 1, 1] [0, 26, 21] [31, 26, 21] [32, 1, 1] [0, 27, 21] [31, 27, 21] [32, 1, 1] [0, 28, 21] [31, 28, 21] [32, 1, 1] [0, 29, 21] [31, 29, 21] [32, 1, 1] [0, 30, 21] [31, 30, 21] [32, 1, 1] [0, 31, 21] [31, 31, 21] [32, 1, 1] [0, 24, 22] [31, 24, 22] [32, 1, 1] [0, 25, 22] [31, 25, 22] [32, 1, 1] [0, 26, 22] [31, 26, 22] [32, 1, 1] [0, 27, 22] [31, 27, 22] [32, 1, 1] [0, 28, 22] [31, 28, 22] [32, 1, 1] [0, 29, 22] [31, 29, 22] [32, 1, 1] [0, 30, 22] [31, 30, 22] [32, 1, 1] [0, 31, 22] [31, 31, 22] [32, 1, 1] [0, 24, 23] [31, 24, 23] [32, 1, 1] [0, 25, 23] [31, 25, 23] [32, 1, 1] [0, 26, 23] [31, 26, 23] [32, 1, 1] [0, 27, 23] [31, 27, 23] [32, 1, 1] [0, 28, 23] [31, 28, 23] [32, 1, 1] [0, 29, 23] [31, 29, 23] [32, 1, 1] [0, 30, 23] [31, 30, 23] [32, 1, 1] [0, 31, 23] [31, 31, 23] [32, 1, 1] [0, 0, 24] [31, 0, 24] [32, 1, 1] [0, 1, 24] [31, 1, 24] [32, 1, 1] [0, 2, 24] [31, 2, 24] [32, 1, 1] [0, 3, 24] [31, 3, 24] [32, 1, 1] [0, 4, 24] [31, 4, 24] [32, 1, 1] [0, 5, 24] [31, 5, 24] [32, 1, 1] [0, 6, 24] [31, 6, 24] [32, 1, 1] [0, 7, 24] [31, 7, 24] [32, 1, 1] [0, 0, 25] [31, 0, 25] [32, 1, 1] [0, 1, 25] [31, 1, 25] [32, 1, 1] [0, 2, 25] [31, 2, 25] [32, 1, 1] [0, 3, 25] [31, 3, 25] [32, 1, 1] [0, 4, 25] [31, 4, 25] [32, 1, 1] [0, 5, 25] [31, 5, 25] [32, 1, 1] [0, 6, 25] [31, 6, 25] [32, 1, 1] [0, 7, 25] [31, 7, 25] [32, 1, 1] [0, 0, 26] [31, 0, 26] [32, 1, 1] [0, 1, 26] [31, 1, 26] [32, 1, 1] [0, 2, 26] [31, 2, 26] [32, 1, 1] [0, 3, 26] [31, 3, 26] [32, 1, 1] [0, 4, 26] [31, 4, 26] [32, 1, 1] [0, 5, 26] [31, 5, 26] [32, 1, 1] [0, 6, 26] [31, 6, 26] [32, 1, 1] [0, 7, 26] [31, 7, 26] [32, 1, 1] [0, 0, 27] [31, 0, 27] [32, 1, 1] [0, 1, 27] [31, 1, 27] [32, 1, 1] [0, 2, 27] [31, 2, 27] [32, 1, 1] [0, 3, 27] [31, 3, 27] [32, 1, 1] [0, 4, 27] [31, 4, 27] [32, 1, 1] [0, 5, 27] [31, 5, 27] [32, 1, 1] [0, 6, 27] [31, 6, 27] [32, 1, 1] [0, 7, 27] [31, 7, 27] [32, 1, 1] [0, 0, 28] [31, 0, 28] [32, 1, 1] [0, 1, 28] [31, 1, 28] [32, 1, 1] [0, 2, 28] [31, 2, 28] [32, 1, 1] [0, 3, 28] [31, 3, 28] [32, 1, 1] [0, 4, 28] [31, 4, 28] [32, 1, 1] [0, 5, 28] [31, 5, 28] [32, 1, 1] [0, 6, 28] [31, 6, 28] [32, 1, 1] [0, 7, 28] [31, 7, 28] [32, 1, 1] [0, 0, 29] [31, 0, 29] [32, 1, 1] [0, 1, 29] [31, 1, 29] [32, 1, 1] [0, 2, 29] [31, 2, 29] [32, 1, 1] [0, 3, 29] [31, 3, 29] [32, 1, 1] [0, 4, 29] [31, 4, 29] [32, 1, 1] [0, 5, 29] [31, 5, 29] [32, 1, 1] [0, 6, 29] [31, 6, 29] [32, 1, 1] [0, 7, 29] [31, 7, 29] [32, 1, 1] [0, 0, 30] [31, 0, 30] [32, 1, 1] [0, 1, 30] [31, 1, 30] [32, 1, 1] [0, 2, 30] [31, 2, 30] [32, 1, 1] [0, 3, 30] [31, 3, 30] [32, 1, 1] [0, 4, 30] [31, 4, 30] [32, 1, 1] [0, 5, 30] [31, 5, 30] [32, 1, 1] [0, 6, 30] [31, 6, 30] [32, 1, 1] [0, 7, 30] [31, 7, 30] [32, 1, 1] [0, 0, 31] [31, 0, 31] [32, 1, 1] [0, 1, 31] [31, 1, 31] [32, 1, 1] [0, 2, 31] [31, 2, 31] [32, 1, 1] [0, 3, 31] [31, 3, 31] [32, 1, 1] [0, 4, 31] [31, 4, 31] [32, 1, 1] [0, 5, 31] [31, 5, 31] [32, 1, 1] [0, 6, 31] [31, 6, 31] [32, 1, 1] [0, 7, 31] [31, 7, 31] [32, 1, 1] [0, 8, 24] [31, 8, 24] [32, 1, 1] [0, 9, 24] [31, 9, 24] [32, 1, 1] [0, 10, 24] [31, 10, 24] [32, 1, 1] [0, 11, 24] [31, 11, 24] [32, 1, 1] [0, 12, 24] [31, 12, 24] [32, 1, 1] [0, 13, 24] [31, 13, 24] [32, 1, 1] [0, 14, 24] [31, 14, 24] [32, 1, 1] [0, 15, 24] [31, 15, 24] [32, 1, 1] [0, 8, 25] [31, 8, 25] [32, 1, 1] [0, 9, 25] [31, 9, 25] [32, 1, 1] [0, 10, 25] [31, 10, 25] [32, 1, 1] [0, 11, 25] [31, 11, 25] [32, 1, 1] [0, 12, 25] [31, 12, 25] [32, 1, 1] [0, 13, 25] [31, 13, 25] [32, 1, 1] [0, 14, 25] [31, 14, 25] [32, 1, 1] [0, 15, 25] [31, 15, 25] [32, 1, 1] [0, 8, 26] [31, 8, 26] [32, 1, 1] [0, 9, 26] [31, 9, 26] [32, 1, 1] [0, 10, 26] [31, 10, 26] [32, 1, 1] [0, 11, 26] [31, 11, 26] [32, 1, 1] [0, 12, 26] [31, 12, 26] [32, 1, 1] [0, 13, 26] [31, 13, 26] [32, 1, 1] [0, 14, 26] [31, 14, 26] [32, 1, 1] [0, 15, 26] [31, 15, 26] [32, 1, 1] [0, 8, 27] [31, 8, 27] [32, 1, 1] [0, 9, 27] [31, 9, 27] [32, 1, 1] [0, 10, 27] [31, 10, 27] [32, 1, 1] [0, 11, 27] [31, 11, 27] [32, 1, 1] [0, 12, 27] [31, 12, 27] [32, 1, 1] [0, 13, 27] [31, 13, 27] [32, 1, 1] [0, 14, 27] [31, 14, 27] [32, 1, 1] [0, 15, 27] [31, 15, 27] [32, 1, 1] [0, 8, 28] [31, 8, 28] [32, 1, 1] [0, 9, 28] [31, 9, 28] [32, 1, 1] [0, 10, 28] [31, 10, 28] [32, 1, 1] [0, 11, 28] [31, 11, 28] [32, 1, 1] [0, 12, 28] [31, 12, 28] [32, 1, 1] [0, 13, 28] [31, 13, 28] [32, 1, 1] [0, 14, 28] [31, 14, 28] [32, 1, 1] [0, 15, 28] [31, 15, 28] [32, 1, 1] [0, 8, 29] [31, 8, 29] [32, 1, 1] [0, 9, 29] [31, 9, 29] [32, 1, 1] [0, 10, 29] [31, 10, 29] [32, 1, 1] [0, 11, 29] [31, 11, 29] [32, 1, 1] [0, 12, 29] [31, 12, 29] [32, 1, 1] [0, 13, 29] [31, 13, 29] [32, 1, 1] [0, 14, 29] [31, 14, 29] [32, 1, 1] [0, 15, 29] [31, 15, 29] [32, 1, 1] [0, 8, 30] [31, 8, 30] [32, 1, 1] [0, 9, 30] [31, 9, 30] [32, 1, 1] [0, 10, 30] [31, 10, 30] [32, 1, 1] [0, 11, 30] [31, 11, 30] [32, 1, 1] [0, 12, 30] [31, 12, 30] [32, 1, 1] [0, 13, 30] [31, 13, 30] [32, 1, 1] [0, 14, 30] [31, 14, 30] [32, 1, 1] [0, 15, 30] [31, 15, 30] [32, 1, 1] [0, 8, 31] [31, 8, 31] [32, 1, 1] [0, 9, 31] [31, 9, 31] [32, 1, 1] [0, 10, 31] [31, 10, 31] [32, 1, 1] [0, 11, 31] [31, 11, 31] [32, 1, 1] [0, 12, 31] [31, 12, 31] [32, 1, 1] [0, 13, 31] [31, 13, 31] [32, 1, 1] [0, 14, 31] [31, 14, 31] [32, 1, 1] [0, 15, 31] [31, 15, 31] [32, 1, 1] [0, 16, 24] [31, 16, 24] [32, 1, 1] [0, 17, 24] [31, 17, 24] [32, 1, 1] [0, 18, 24] [31, 18, 24] [32, 1, 1] [0, 19, 24] [31, 19, 24] [32, 1, 1] [0, 20, 24] [31, 20, 24] [32, 1, 1] [0, 21, 24] [31, 21, 24] [32, 1, 1] [0, 22, 24] [31, 22, 24] [32, 1, 1] [0, 23, 24] [31, 23, 24] [32, 1, 1] [0, 16, 25] [31, 16, 25] [32, 1, 1] [0, 17, 25] [31, 17, 25] [32, 1, 1] [0, 18, 25] [31, 18, 25] [32, 1, 1] [0, 19, 25] [31, 19, 25] [32, 1, 1] [0, 20, 25] [31, 20, 25] [32, 1, 1] [0, 21, 25] [31, 21, 25] [32, 1, 1] [0, 22, 25] [31, 22, 25] [32, 1, 1] [0, 23, 25] [31, 23, 25] [32, 1, 1] [0, 16, 26] [31, 16, 26] [32, 1, 1] [0, 17, 26] [31, 17, 26] [32, 1, 1] [0, 18, 26] [31, 18, 26] [32, 1, 1] [0, 19, 26] [31, 19, 26] [32, 1, 1] [0, 20, 26] [31, 20, 26] [32, 1, 1] [0, 21, 26] [31, 21, 26] [32, 1, 1] [0, 22, 26] [31, 22, 26] [32, 1, 1] [0, 23, 26] [31, 23, 26] [32, 1, 1] [0, 16, 27] [31, 16, 27] [32, 1, 1] [0, 17, 27] [31, 17, 27] [32, 1, 1] [0, 18, 27] [31, 18, 27] [32, 1, 1] [0, 19, 27] [31, 19, 27] [32, 1, 1] [0, 20, 27] [31, 20, 27] [32, 1, 1] [0, 21, 27] [31, 21, 27] [32, 1, 1] [0, 22, 27] [31, 22, 27] [32, 1, 1] [0, 23, 27] [31, 23, 27] [32, 1, 1] [0, 16, 28] [31, 16, 28] [32, 1, 1] [0, 17, 28] [31, 17, 28] [32, 1, 1] [0, 18, 28] [31, 18, 28] [32, 1, 1] [0, 19, 28] [31, 19, 28] [32, 1, 1] [0, 20, 28] [31, 20, 28] [32, 1, 1] [0, 21, 28] [31, 21, 28] [32, 1, 1] [0, 22, 28] [31, 22, 28] [32, 1, 1] [0, 23, 28] [31, 23, 28] [32, 1, 1] [0, 16, 29] [31, 16, 29] [32, 1, 1] [0, 17, 29] [31, 17, 29] [32, 1, 1] [0, 18, 29] [31, 18, 29] [32, 1, 1] [0, 19, 29] [31, 19, 29] [32, 1, 1] [0, 20, 29] [31, 20, 29] [32, 1, 1] [0, 21, 29] [31, 21, 29] [32, 1, 1] [0, 22, 29] [31, 22, 29] [32, 1, 1] [0, 23, 29] [31, 23, 29] [32, 1, 1] [0, 16, 30] [31, 16, 30] [32, 1, 1] [0, 17, 30] [31, 17, 30] [32, 1, 1] [0, 18, 30] [31, 18, 30] [32, 1, 1] [0, 19, 30] [31, 19, 30] [32, 1, 1] [0, 20, 30] [31, 20, 30] [32, 1, 1] [0, 21, 30] [31, 21, 30] [32, 1, 1] [0, 22, 30] [31, 22, 30] [32, 1, 1] [0, 23, 30] [31, 23, 30] [32, 1, 1] [0, 16, 31] [31, 16, 31] [32, 1, 1] [0, 17, 31] [31, 17, 31] [32, 1, 1] [0, 18, 31] [31, 18, 31] [32, 1, 1] [0, 19, 31] [31, 19, 31] [32, 1, 1] [0, 20, 31] [31, 20, 31] [32, 1, 1] [0, 21, 31] [31, 21, 31] [32, 1, 1] [0, 22, 31] [31, 22, 31] [32, 1, 1] [0, 23, 31] [31, 23, 31] [32, 1, 1] [0, 24, 24] [31, 24, 24] [32, 1, 1] [0, 25, 24] [31, 25, 24] [32, 1, 1] [0, 26, 24] [31, 26, 24] [32, 1, 1] [0, 27, 24] [31, 27, 24] [32, 1, 1] [0, 28, 24] [31, 28, 24] [32, 1, 1] [0, 29, 24] [31, 29, 24] [32, 1, 1] [0, 30, 24] [31, 30, 24] [32, 1, 1] [0, 31, 24] [31, 31, 24] [32, 1, 1] [0, 24, 25] [31, 24, 25] [32, 1, 1] [0, 25, 25] [31, 25, 25] [32, 1, 1] [0, 26, 25] [31, 26, 25] [32, 1, 1] [0, 27, 25] [31, 27, 25] [32, 1, 1] [0, 28, 25] [31, 28, 25] [32, 1, 1] [0, 29, 25] [31, 29, 25] [32, 1, 1] [0, 30, 25] [31, 30, 25] [32, 1, 1] [0, 31, 25] [31, 31, 25] [32, 1, 1] [0, 24, 26] [31, 24, 26] [32, 1, 1] [0, 25, 26] [31, 25, 26] [32, 1, 1] [0, 26, 26] [31, 26, 26] [32, 1, 1] [0, 27, 26] [31, 27, 26] [32, 1, 1] [0, 28, 26] [31, 28, 26] [32, 1, 1] [0, 29, 26] [31, 29, 26] [32, 1, 1] [0, 30, 26] [31, 30, 26] [32, 1, 1] [0, 31, 26] [31, 31, 26] [32, 1, 1] [0, 24, 27] [31, 24, 27] [32, 1, 1] [0, 25, 27] [31, 25, 27] [32, 1, 1] [0, 26, 27] [31, 26, 27] [32, 1, 1] [0, 27, 27] [31, 27, 27] [32, 1, 1] [0, 28, 27] [31, 28, 27] [32, 1, 1] [0, 29, 27] [31, 29, 27] [32, 1, 1] [0, 30, 27] [31, 30, 27] [32, 1, 1] [0, 31, 27] [31, 31, 27] [32, 1, 1] [0, 24, 28] [31, 24, 28] [32, 1, 1] [0, 25, 28] [31, 25, 28] [32, 1, 1] [0, 26, 28] [31, 26, 28] [32, 1, 1] [0, 27, 28] [31, 27, 28] [32, 1, 1] [0, 28, 28] [31, 28, 28] [32, 1, 1] [0, 29, 28] [31, 29, 28] [32, 1, 1] [0, 30, 28] [31, 30, 28] [32, 1, 1] [0, 31, 28] [31, 31, 28] [32, 1, 1] [0, 24, 29] [31, 24, 29] [32, 1, 1] [0, 25, 29] [31, 25, 29] [32, 1, 1] [0, 26, 29] [31, 26, 29] [32, 1, 1] [0, 27, 29] [31, 27, 29] [32, 1, 1] [0, 28, 29] [31, 28, 29] [32, 1, 1] [0, 29, 29] [31, 29, 29] [32, 1, 1] [0, 30, 29] [31, 30, 29] [32, 1, 1] [0, 31, 29] [31, 31, 29] [32, 1, 1] [0, 24, 30] [31, 24, 30] [32, 1, 1] [0, 25, 30] [31, 25, 30] [32, 1, 1] [0, 26, 30] [31, 26, 30] [32, 1, 1] [0, 27, 30] [31, 27, 30] [32, 1, 1] [0, 28, 30] [31, 28, 30] [32, 1, 1] [0, 29, 30] [31, 29, 30] [32, 1, 1] [0, 30, 30] [31, 30, 30] [32, 1, 1] [0, 31, 30] [31, 31, 30] [32, 1, 1] [0, 24, 31] [31, 24, 31] [32, 1, 1] [0, 25, 31] [31, 25, 31] [32, 1, 1] [0, 26, 31] [31, 26, 31] [32, 1, 1] [0, 27, 31] [31, 27, 31] [32, 1, 1] [0, 28, 31] [31, 28, 31] [32, 1, 1] [0, 29, 31] [31, 29, 31] [32, 1, 1] [0, 30, 31] [31, 30, 31] [32, 1, 1] [0, 31, 31] [31, 31, 31] [32, 1, 1] nsteps = 1024 [0, 31, 31] [31, 31, 31] [32, 1, 1] [0, 30, 31] [31, 30, 31] [32, 1, 1] [0, 29, 31] [31, 29, 31] [32, 1, 1] [0, 28, 31] [31, 28, 31] [32, 1, 1] [0, 27, 31] [31, 27, 31] [32, 1, 1] [0, 26, 31] [31, 26, 31] [32, 1, 1] [0, 25, 31] [31, 25, 31] [32, 1, 1] [0, 24, 31] [31, 24, 31] [32, 1, 1] [0, 31, 30] [31, 31, 30] [32, 1, 1] [0, 30, 30] [31, 30, 30] [32, 1, 1] [0, 29, 30] [31, 29, 30] [32, 1, 1] [0, 28, 30] [31, 28, 30] [32, 1, 1] [0, 27, 30] [31, 27, 30] [32, 1, 1] [0, 26, 30] [31, 26, 30] [32, 1, 1] [0, 25, 30] [31, 25, 30] [32, 1, 1] [0, 24, 30] [31, 24, 30] [32, 1, 1] [0, 31, 29] [31, 31, 29] [32, 1, 1] [0, 30, 29] [31, 30, 29] [32, 1, 1] [0, 29, 29] [31, 29, 29] [32, 1, 1] [0, 28, 29] [31, 28, 29] [32, 1, 1] [0, 27, 29] [31, 27, 29] [32, 1, 1] [0, 26, 29] [31, 26, 29] [32, 1, 1] [0, 25, 29] [31, 25, 29] [32, 1, 1] [0, 24, 29] [31, 24, 29] [32, 1, 1] [0, 31, 28] [31, 31, 28] [32, 1, 1] [0, 30, 28] [31, 30, 28] [32, 1, 1] [0, 29, 28] [31, 29, 28] [32, 1, 1] [0, 28, 28] [31, 28, 28] [32, 1, 1] [0, 27, 28] [31, 27, 28] [32, 1, 1] [0, 26, 28] [31, 26, 28] [32, 1, 1] [0, 25, 28] [31, 25, 28] [32, 1, 1] [0, 24, 28] [31, 24, 28] [32, 1, 1] [0, 31, 27] [31, 31, 27] [32, 1, 1] [0, 30, 27] [31, 30, 27] [32, 1, 1] [0, 29, 27] [31, 29, 27] [32, 1, 1] [0, 28, 27] [31, 28, 27] [32, 1, 1] [0, 27, 27] [31, 27, 27] [32, 1, 1] [0, 26, 27] [31, 26, 27] [32, 1, 1] [0, 25, 27] [31, 25, 27] [32, 1, 1] [0, 24, 27] [31, 24, 27] [32, 1, 1] [0, 31, 26] [31, 31, 26] [32, 1, 1] [0, 30, 26] [31, 30, 26] [32, 1, 1] [0, 29, 26] [31, 29, 26] [32, 1, 1] [0, 28, 26] [31, 28, 26] [32, 1, 1] [0, 27, 26] [31, 27, 26] [32, 1, 1] [0, 26, 26] [31, 26, 26] [32, 1, 1] [0, 25, 26] [31, 25, 26] [32, 1, 1] [0, 24, 26] [31, 24, 26] [32, 1, 1] [0, 31, 25] [31, 31, 25] [32, 1, 1] [0, 30, 25] [31, 30, 25] [32, 1, 1] [0, 29, 25] [31, 29, 25] [32, 1, 1] [0, 28, 25] [31, 28, 25] [32, 1, 1] [0, 27, 25] [31, 27, 25] [32, 1, 1] [0, 26, 25] [31, 26, 25] [32, 1, 1] [0, 25, 25] [31, 25, 25] [32, 1, 1] [0, 24, 25] [31, 24, 25] [32, 1, 1] [0, 31, 24] [31, 31, 24] [32, 1, 1] [0, 30, 24] [31, 30, 24] [32, 1, 1] [0, 29, 24] [31, 29, 24] [32, 1, 1] [0, 28, 24] [31, 28, 24] [32, 1, 1] [0, 27, 24] [31, 27, 24] [32, 1, 1] [0, 26, 24] [31, 26, 24] [32, 1, 1] [0, 25, 24] [31, 25, 24] [32, 1, 1] [0, 24, 24] [31, 24, 24] [32, 1, 1] [0, 23, 31] [31, 23, 31] [32, 1, 1] [0, 22, 31] [31, 22, 31] [32, 1, 1] [0, 21, 31] [31, 21, 31] [32, 1, 1] [0, 20, 31] [31, 20, 31] [32, 1, 1] [0, 19, 31] [31, 19, 31] [32, 1, 1] [0, 18, 31] [31, 18, 31] [32, 1, 1] [0, 17, 31] [31, 17, 31] [32, 1, 1] [0, 16, 31] [31, 16, 31] [32, 1, 1] [0, 23, 30] [31, 23, 30] [32, 1, 1] [0, 22, 30] [31, 22, 30] [32, 1, 1] [0, 21, 30] [31, 21, 30] [32, 1, 1] [0, 20, 30] [31, 20, 30] [32, 1, 1] [0, 19, 30] [31, 19, 30] [32, 1, 1] [0, 18, 30] [31, 18, 30] [32, 1, 1] [0, 17, 30] [31, 17, 30] [32, 1, 1] [0, 16, 30] [31, 16, 30] [32, 1, 1] [0, 23, 29] [31, 23, 29] [32, 1, 1] [0, 22, 29] [31, 22, 29] [32, 1, 1] [0, 21, 29] [31, 21, 29] [32, 1, 1] [0, 20, 29] [31, 20, 29] [32, 1, 1] [0, 19, 29] [31, 19, 29] [32, 1, 1] [0, 18, 29] [31, 18, 29] [32, 1, 1] [0, 17, 29] [31, 17, 29] [32, 1, 1] [0, 16, 29] [31, 16, 29] [32, 1, 1] [0, 23, 28] [31, 23, 28] [32, 1, 1] [0, 22, 28] [31, 22, 28] [32, 1, 1] [0, 21, 28] [31, 21, 28] [32, 1, 1] [0, 20, 28] [31, 20, 28] [32, 1, 1] [0, 19, 28] [31, 19, 28] [32, 1, 1] [0, 18, 28] [31, 18, 28] [32, 1, 1] [0, 17, 28] [31, 17, 28] [32, 1, 1] [0, 16, 28] [31, 16, 28] [32, 1, 1] [0, 23, 27] [31, 23, 27] [32, 1, 1] [0, 22, 27] [31, 22, 27] [32, 1, 1] [0, 21, 27] [31, 21, 27] [32, 1, 1] [0, 20, 27] [31, 20, 27] [32, 1, 1] [0, 19, 27] [31, 19, 27] [32, 1, 1] [0, 18, 27] [31, 18, 27] [32, 1, 1] [0, 17, 27] [31, 17, 27] [32, 1, 1] [0, 16, 27] [31, 16, 27] [32, 1, 1] [0, 23, 26] [31, 23, 26] [32, 1, 1] [0, 22, 26] [31, 22, 26] [32, 1, 1] [0, 21, 26] [31, 21, 26] [32, 1, 1] [0, 20, 26] [31, 20, 26] [32, 1, 1] [0, 19, 26] [31, 19, 26] [32, 1, 1] [0, 18, 26] [31, 18, 26] [32, 1, 1] [0, 17, 26] [31, 17, 26] [32, 1, 1] [0, 16, 26] [31, 16, 26] [32, 1, 1] [0, 23, 25] [31, 23, 25] [32, 1, 1] [0, 22, 25] [31, 22, 25] [32, 1, 1] [0, 21, 25] [31, 21, 25] [32, 1, 1] [0, 20, 25] [31, 20, 25] [32, 1, 1] [0, 19, 25] [31, 19, 25] [32, 1, 1] [0, 18, 25] [31, 18, 25] [32, 1, 1] [0, 17, 25] [31, 17, 25] [32, 1, 1] [0, 16, 25] [31, 16, 25] [32, 1, 1] [0, 23, 24] [31, 23, 24] [32, 1, 1] [0, 22, 24] [31, 22, 24] [32, 1, 1] [0, 21, 24] [31, 21, 24] [32, 1, 1] [0, 20, 24] [31, 20, 24] [32, 1, 1] [0, 19, 24] [31, 19, 24] [32, 1, 1] [0, 18, 24] [31, 18, 24] [32, 1, 1] [0, 17, 24] [31, 17, 24] [32, 1, 1] [0, 16, 24] [31, 16, 24] [32, 1, 1] [0, 15, 31] [31, 15, 31] [32, 1, 1] [0, 14, 31] [31, 14, 31] [32, 1, 1] [0, 13, 31] [31, 13, 31] [32, 1, 1] [0, 12, 31] [31, 12, 31] [32, 1, 1] [0, 11, 31] [31, 11, 31] [32, 1, 1] [0, 10, 31] [31, 10, 31] [32, 1, 1] [0, 9, 31] [31, 9, 31] [32, 1, 1] [0, 8, 31] [31, 8, 31] [32, 1, 1] [0, 15, 30] [31, 15, 30] [32, 1, 1] [0, 14, 30] [31, 14, 30] [32, 1, 1] [0, 13, 30] [31, 13, 30] [32, 1, 1] [0, 12, 30] [31, 12, 30] [32, 1, 1] [0, 11, 30] [31, 11, 30] [32, 1, 1] [0, 10, 30] [31, 10, 30] [32, 1, 1] [0, 9, 30] [31, 9, 30] [32, 1, 1] [0, 8, 30] [31, 8, 30] [32, 1, 1] [0, 15, 29] [31, 15, 29] [32, 1, 1] [0, 14, 29] [31, 14, 29] [32, 1, 1] [0, 13, 29] [31, 13, 29] [32, 1, 1] [0, 12, 29] [31, 12, 29] [32, 1, 1] [0, 11, 29] [31, 11, 29] [32, 1, 1] [0, 10, 29] [31, 10, 29] [32, 1, 1] [0, 9, 29] [31, 9, 29] [32, 1, 1] [0, 8, 29] [31, 8, 29] [32, 1, 1] [0, 15, 28] [31, 15, 28] [32, 1, 1] [0, 14, 28] [31, 14, 28] [32, 1, 1] [0, 13, 28] [31, 13, 28] [32, 1, 1] [0, 12, 28] [31, 12, 28] [32, 1, 1] [0, 11, 28] [31, 11, 28] [32, 1, 1] [0, 10, 28] [31, 10, 28] [32, 1, 1] [0, 9, 28] [31, 9, 28] [32, 1, 1] [0, 8, 28] [31, 8, 28] [32, 1, 1] [0, 15, 27] [31, 15, 27] [32, 1, 1] [0, 14, 27] [31, 14, 27] [32, 1, 1] [0, 13, 27] [31, 13, 27] [32, 1, 1] [0, 12, 27] [31, 12, 27] [32, 1, 1] [0, 11, 27] [31, 11, 27] [32, 1, 1] [0, 10, 27] [31, 10, 27] [32, 1, 1] [0, 9, 27] [31, 9, 27] [32, 1, 1] [0, 8, 27] [31, 8, 27] [32, 1, 1] [0, 15, 26] [31, 15, 26] [32, 1, 1] [0, 14, 26] [31, 14, 26] [32, 1, 1] [0, 13, 26] [31, 13, 26] [32, 1, 1] [0, 12, 26] [31, 12, 26] [32, 1, 1] [0, 11, 26] [31, 11, 26] [32, 1, 1] [0, 10, 26] [31, 10, 26] [32, 1, 1] [0, 9, 26] [31, 9, 26] [32, 1, 1] [0, 8, 26] [31, 8, 26] [32, 1, 1] [0, 15, 25] [31, 15, 25] [32, 1, 1] [0, 14, 25] [31, 14, 25] [32, 1, 1] [0, 13, 25] [31, 13, 25] [32, 1, 1] [0, 12, 25] [31, 12, 25] [32, 1, 1] [0, 11, 25] [31, 11, 25] [32, 1, 1] [0, 10, 25] [31, 10, 25] [32, 1, 1] [0, 9, 25] [31, 9, 25] [32, 1, 1] [0, 8, 25] [31, 8, 25] [32, 1, 1] [0, 15, 24] [31, 15, 24] [32, 1, 1] [0, 14, 24] [31, 14, 24] [32, 1, 1] [0, 13, 24] [31, 13, 24] [32, 1, 1] [0, 12, 24] [31, 12, 24] [32, 1, 1] [0, 11, 24] [31, 11, 24] [32, 1, 1] [0, 10, 24] [31, 10, 24] [32, 1, 1] [0, 9, 24] [31, 9, 24] [32, 1, 1] [0, 8, 24] [31, 8, 24] [32, 1, 1] [0, 7, 31] [31, 7, 31] [32, 1, 1] [0, 6, 31] [31, 6, 31] [32, 1, 1] [0, 5, 31] [31, 5, 31] [32, 1, 1] [0, 4, 31] [31, 4, 31] [32, 1, 1] [0, 3, 31] [31, 3, 31] [32, 1, 1] [0, 2, 31] [31, 2, 31] [32, 1, 1] [0, 1, 31] [31, 1, 31] [32, 1, 1] [0, 0, 31] [31, 0, 31] [32, 1, 1] [0, 7, 30] [31, 7, 30] [32, 1, 1] [0, 6, 30] [31, 6, 30] [32, 1, 1] [0, 5, 30] [31, 5, 30] [32, 1, 1] [0, 4, 30] [31, 4, 30] [32, 1, 1] [0, 3, 30] [31, 3, 30] [32, 1, 1] [0, 2, 30] [31, 2, 30] [32, 1, 1] [0, 1, 30] [31, 1, 30] [32, 1, 1] [0, 0, 30] [31, 0, 30] [32, 1, 1] [0, 7, 29] [31, 7, 29] [32, 1, 1] [0, 6, 29] [31, 6, 29] [32, 1, 1] [0, 5, 29] [31, 5, 29] [32, 1, 1] [0, 4, 29] [31, 4, 29] [32, 1, 1] [0, 3, 29] [31, 3, 29] [32, 1, 1] [0, 2, 29] [31, 2, 29] [32, 1, 1] [0, 1, 29] [31, 1, 29] [32, 1, 1] [0, 0, 29] [31, 0, 29] [32, 1, 1] [0, 7, 28] [31, 7, 28] [32, 1, 1] [0, 6, 28] [31, 6, 28] [32, 1, 1] [0, 5, 28] [31, 5, 28] [32, 1, 1] [0, 4, 28] [31, 4, 28] [32, 1, 1] [0, 3, 28] [31, 3, 28] [32, 1, 1] [0, 2, 28] [31, 2, 28] [32, 1, 1] [0, 1, 28] [31, 1, 28] [32, 1, 1] [0, 0, 28] [31, 0, 28] [32, 1, 1] [0, 7, 27] [31, 7, 27] [32, 1, 1] [0, 6, 27] [31, 6, 27] [32, 1, 1] [0, 5, 27] [31, 5, 27] [32, 1, 1] [0, 4, 27] [31, 4, 27] [32, 1, 1] [0, 3, 27] [31, 3, 27] [32, 1, 1] [0, 2, 27] [31, 2, 27] [32, 1, 1] [0, 1, 27] [31, 1, 27] [32, 1, 1] [0, 0, 27] [31, 0, 27] [32, 1, 1] [0, 7, 26] [31, 7, 26] [32, 1, 1] [0, 6, 26] [31, 6, 26] [32, 1, 1] [0, 5, 26] [31, 5, 26] [32, 1, 1] [0, 4, 26] [31, 4, 26] [32, 1, 1] [0, 3, 26] [31, 3, 26] [32, 1, 1] [0, 2, 26] [31, 2, 26] [32, 1, 1] [0, 1, 26] [31, 1, 26] [32, 1, 1] [0, 0, 26] [31, 0, 26] [32, 1, 1] [0, 7, 25] [31, 7, 25] [32, 1, 1] [0, 6, 25] [31, 6, 25] [32, 1, 1] [0, 5, 25] [31, 5, 25] [32, 1, 1] [0, 4, 25] [31, 4, 25] [32, 1, 1] [0, 3, 25] [31, 3, 25] [32, 1, 1] [0, 2, 25] [31, 2, 25] [32, 1, 1] [0, 1, 25] [31, 1, 25] [32, 1, 1] [0, 0, 25] [31, 0, 25] [32, 1, 1] [0, 7, 24] [31, 7, 24] [32, 1, 1] [0, 6, 24] [31, 6, 24] [32, 1, 1] [0, 5, 24] [31, 5, 24] [32, 1, 1] [0, 4, 24] [31, 4, 24] [32, 1, 1] [0, 3, 24] [31, 3, 24] [32, 1, 1] [0, 2, 24] [31, 2, 24] [32, 1, 1] [0, 1, 24] [31, 1, 24] [32, 1, 1] [0, 0, 24] [31, 0, 24] [32, 1, 1] [0, 31, 23] [31, 31, 23] [32, 1, 1] [0, 30, 23] [31, 30, 23] [32, 1, 1] [0, 29, 23] [31, 29, 23] [32, 1, 1] [0, 28, 23] [31, 28, 23] [32, 1, 1] [0, 27, 23] [31, 27, 23] [32, 1, 1] [0, 26, 23] [31, 26, 23] [32, 1, 1] [0, 25, 23] [31, 25, 23] [32, 1, 1] [0, 24, 23] [31, 24, 23] [32, 1, 1] [0, 31, 22] [31, 31, 22] [32, 1, 1] [0, 30, 22] [31, 30, 22] [32, 1, 1] [0, 29, 22] [31, 29, 22] [32, 1, 1] [0, 28, 22] [31, 28, 22] [32, 1, 1] [0, 27, 22] [31, 27, 22] [32, 1, 1] [0, 26, 22] [31, 26, 22] [32, 1, 1] [0, 25, 22] [31, 25, 22] [32, 1, 1] [0, 24, 22] [31, 24, 22] [32, 1, 1] [0, 31, 21] [31, 31, 21] [32, 1, 1] [0, 30, 21] [31, 30, 21] [32, 1, 1] [0, 29, 21] [31, 29, 21] [32, 1, 1] [0, 28, 21] [31, 28, 21] [32, 1, 1] [0, 27, 21] [31, 27, 21] [32, 1, 1] [0, 26, 21] [31, 26, 21] [32, 1, 1] [0, 25, 21] [31, 25, 21] [32, 1, 1] [0, 24, 21] [31, 24, 21] [32, 1, 1] [0, 31, 20] [31, 31, 20] [32, 1, 1] [0, 30, 20] [31, 30, 20] [32, 1, 1] [0, 29, 20] [31, 29, 20] [32, 1, 1] [0, 28, 20] [31, 28, 20] [32, 1, 1] [0, 27, 20] [31, 27, 20] [32, 1, 1] [0, 26, 20] [31, 26, 20] [32, 1, 1] [0, 25, 20] [31, 25, 20] [32, 1, 1] [0, 24, 20] [31, 24, 20] [32, 1, 1] [0, 31, 19] [31, 31, 19] [32, 1, 1] [0, 30, 19] [31, 30, 19] [32, 1, 1] [0, 29, 19] [31, 29, 19] [32, 1, 1] [0, 28, 19] [31, 28, 19] [32, 1, 1] [0, 27, 19] [31, 27, 19] [32, 1, 1] [0, 26, 19] [31, 26, 19] [32, 1, 1] [0, 25, 19] [31, 25, 19] [32, 1, 1] [0, 24, 19] [31, 24, 19] [32, 1, 1] [0, 31, 18] [31, 31, 18] [32, 1, 1] [0, 30, 18] [31, 30, 18] [32, 1, 1] [0, 29, 18] [31, 29, 18] [32, 1, 1] [0, 28, 18] [31, 28, 18] [32, 1, 1] [0, 27, 18] [31, 27, 18] [32, 1, 1] [0, 26, 18] [31, 26, 18] [32, 1, 1] [0, 25, 18] [31, 25, 18] [32, 1, 1] [0, 24, 18] [31, 24, 18] [32, 1, 1] [0, 31, 17] [31, 31, 17] [32, 1, 1] [0, 30, 17] [31, 30, 17] [32, 1, 1] [0, 29, 17] [31, 29, 17] [32, 1, 1] [0, 28, 17] [31, 28, 17] [32, 1, 1] [0, 27, 17] [31, 27, 17] [32, 1, 1] [0, 26, 17] [31, 26, 17] [32, 1, 1] [0, 25, 17] [31, 25, 17] [32, 1, 1] [0, 24, 17] [31, 24, 17] [32, 1, 1] [0, 31, 16] [31, 31, 16] [32, 1, 1] [0, 30, 16] [31, 30, 16] [32, 1, 1] [0, 29, 16] [31, 29, 16] [32, 1, 1] [0, 28, 16] [31, 28, 16] [32, 1, 1] [0, 27, 16] [31, 27, 16] [32, 1, 1] [0, 26, 16] [31, 26, 16] [32, 1, 1] [0, 25, 16] [31, 25, 16] [32, 1, 1] [0, 24, 16] [31, 24, 16] [32, 1, 1] [0, 23, 23] [31, 23, 23] [32, 1, 1] [0, 22, 23] [31, 22, 23] [32, 1, 1] [0, 21, 23] [31, 21, 23] [32, 1, 1] [0, 20, 23] [31, 20, 23] [32, 1, 1] [0, 19, 23] [31, 19, 23] [32, 1, 1] [0, 18, 23] [31, 18, 23] [32, 1, 1] [0, 17, 23] [31, 17, 23] [32, 1, 1] [0, 16, 23] [31, 16, 23] [32, 1, 1] [0, 23, 22] [31, 23, 22] [32, 1, 1] [0, 22, 22] [31, 22, 22] [32, 1, 1] [0, 21, 22] [31, 21, 22] [32, 1, 1] [0, 20, 22] [31, 20, 22] [32, 1, 1] [0, 19, 22] [31, 19, 22] [32, 1, 1] [0, 18, 22] [31, 18, 22] [32, 1, 1] [0, 17, 22] [31, 17, 22] [32, 1, 1] [0, 16, 22] [31, 16, 22] [32, 1, 1] [0, 23, 21] [31, 23, 21] [32, 1, 1] [0, 22, 21] [31, 22, 21] [32, 1, 1] [0, 21, 21] [31, 21, 21] [32, 1, 1] [0, 20, 21] [31, 20, 21] [32, 1, 1] [0, 19, 21] [31, 19, 21] [32, 1, 1] [0, 18, 21] [31, 18, 21] [32, 1, 1] [0, 17, 21] [31, 17, 21] [32, 1, 1] [0, 16, 21] [31, 16, 21] [32, 1, 1] [0, 23, 20] [31, 23, 20] [32, 1, 1] [0, 22, 20] [31, 22, 20] [32, 1, 1] [0, 21, 20] [31, 21, 20] [32, 1, 1] [0, 20, 20] [31, 20, 20] [32, 1, 1] [0, 19, 20] [31, 19, 20] [32, 1, 1] [0, 18, 20] [31, 18, 20] [32, 1, 1] [0, 17, 20] [31, 17, 20] [32, 1, 1] [0, 16, 20] [31, 16, 20] [32, 1, 1] [0, 23, 19] [31, 23, 19] [32, 1, 1] [0, 22, 19] [31, 22, 19] [32, 1, 1] [0, 21, 19] [31, 21, 19] [32, 1, 1] [0, 20, 19] [31, 20, 19] [32, 1, 1] [0, 19, 19] [31, 19, 19] [32, 1, 1] [0, 18, 19] [31, 18, 19] [32, 1, 1] [0, 17, 19] [31, 17, 19] [32, 1, 1] [0, 16, 19] [31, 16, 19] [32, 1, 1] [0, 23, 18] [31, 23, 18] [32, 1, 1] [0, 22, 18] [31, 22, 18] [32, 1, 1] [0, 21, 18] [31, 21, 18] [32, 1, 1] [0, 20, 18] [31, 20, 18] [32, 1, 1] [0, 19, 18] [31, 19, 18] [32, 1, 1] [0, 18, 18] [31, 18, 18] [32, 1, 1] [0, 17, 18] [31, 17, 18] [32, 1, 1] [0, 16, 18] [31, 16, 18] [32, 1, 1] [0, 23, 17] [31, 23, 17] [32, 1, 1] [0, 22, 17] [31, 22, 17] [32, 1, 1] [0, 21, 17] [31, 21, 17] [32, 1, 1] [0, 20, 17] [31, 20, 17] [32, 1, 1] [0, 19, 17] [31, 19, 17] [32, 1, 1] [0, 18, 17] [31, 18, 17] [32, 1, 1] [0, 17, 17] [31, 17, 17] [32, 1, 1] [0, 16, 17] [31, 16, 17] [32, 1, 1] [0, 23, 16] [31, 23, 16] [32, 1, 1] [0, 22, 16] [31, 22, 16] [32, 1, 1] [0, 21, 16] [31, 21, 16] [32, 1, 1] [0, 20, 16] [31, 20, 16] [32, 1, 1] [0, 19, 16] [31, 19, 16] [32, 1, 1] [0, 18, 16] [31, 18, 16] [32, 1, 1] [0, 17, 16] [31, 17, 16] [32, 1, 1] [0, 16, 16] [31, 16, 16] [32, 1, 1] [0, 15, 23] [31, 15, 23] [32, 1, 1] [0, 14, 23] [31, 14, 23] [32, 1, 1] [0, 13, 23] [31, 13, 23] [32, 1, 1] [0, 12, 23] [31, 12, 23] [32, 1, 1] [0, 11, 23] [31, 11, 23] [32, 1, 1] [0, 10, 23] [31, 10, 23] [32, 1, 1] [0, 9, 23] [31, 9, 23] [32, 1, 1] [0, 8, 23] [31, 8, 23] [32, 1, 1] [0, 15, 22] [31, 15, 22] [32, 1, 1] [0, 14, 22] [31, 14, 22] [32, 1, 1] [0, 13, 22] [31, 13, 22] [32, 1, 1] [0, 12, 22] [31, 12, 22] [32, 1, 1] [0, 11, 22] [31, 11, 22] [32, 1, 1] [0, 10, 22] [31, 10, 22] [32, 1, 1] [0, 9, 22] [31, 9, 22] [32, 1, 1] [0, 8, 22] [31, 8, 22] [32, 1, 1] [0, 15, 21] [31, 15, 21] [32, 1, 1] [0, 14, 21] [31, 14, 21] [32, 1, 1] [0, 13, 21] [31, 13, 21] [32, 1, 1] [0, 12, 21] [31, 12, 21] [32, 1, 1] [0, 11, 21] [31, 11, 21] [32, 1, 1] [0, 10, 21] [31, 10, 21] [32, 1, 1] [0, 9, 21] [31, 9, 21] [32, 1, 1] [0, 8, 21] [31, 8, 21] [32, 1, 1] [0, 15, 20] [31, 15, 20] [32, 1, 1] [0, 14, 20] [31, 14, 20] [32, 1, 1] [0, 13, 20] [31, 13, 20] [32, 1, 1] [0, 12, 20] [31, 12, 20] [32, 1, 1] [0, 11, 20] [31, 11, 20] [32, 1, 1] [0, 10, 20] [31, 10, 20] [32, 1, 1] [0, 9, 20] [31, 9, 20] [32, 1, 1] [0, 8, 20] [31, 8, 20] [32, 1, 1] [0, 15, 19] [31, 15, 19] [32, 1, 1] [0, 14, 19] [31, 14, 19] [32, 1, 1] [0, 13, 19] [31, 13, 19] [32, 1, 1] [0, 12, 19] [31, 12, 19] [32, 1, 1] [0, 11, 19] [31, 11, 19] [32, 1, 1] [0, 10, 19] [31, 10, 19] [32, 1, 1] [0, 9, 19] [31, 9, 19] [32, 1, 1] [0, 8, 19] [31, 8, 19] [32, 1, 1] [0, 15, 18] [31, 15, 18] [32, 1, 1] [0, 14, 18] [31, 14, 18] [32, 1, 1] [0, 13, 18] [31, 13, 18] [32, 1, 1] [0, 12, 18] [31, 12, 18] [32, 1, 1] [0, 11, 18] [31, 11, 18] [32, 1, 1] [0, 10, 18] [31, 10, 18] [32, 1, 1] [0, 9, 18] [31, 9, 18] [32, 1, 1] [0, 8, 18] [31, 8, 18] [32, 1, 1] [0, 15, 17] [31, 15, 17] [32, 1, 1] [0, 14, 17] [31, 14, 17] [32, 1, 1] [0, 13, 17] [31, 13, 17] [32, 1, 1] [0, 12, 17] [31, 12, 17] [32, 1, 1] [0, 11, 17] [31, 11, 17] [32, 1, 1] [0, 10, 17] [31, 10, 17] [32, 1, 1] [0, 9, 17] [31, 9, 17] [32, 1, 1] [0, 8, 17] [31, 8, 17] [32, 1, 1] [0, 15, 16] [31, 15, 16] [32, 1, 1] [0, 14, 16] [31, 14, 16] [32, 1, 1] [0, 13, 16] [31, 13, 16] [32, 1, 1] [0, 12, 16] [31, 12, 16] [32, 1, 1] [0, 11, 16] [31, 11, 16] [32, 1, 1] [0, 10, 16] [31, 10, 16] [32, 1, 1] [0, 9, 16] [31, 9, 16] [32, 1, 1] [0, 8, 16] [31, 8, 16] [32, 1, 1] [0, 7, 23] [31, 7, 23] [32, 1, 1] [0, 6, 23] [31, 6, 23] [32, 1, 1] [0, 5, 23] [31, 5, 23] [32, 1, 1] [0, 4, 23] [31, 4, 23] [32, 1, 1] [0, 3, 23] [31, 3, 23] [32, 1, 1] [0, 2, 23] [31, 2, 23] [32, 1, 1] [0, 1, 23] [31, 1, 23] [32, 1, 1] [0, 0, 23] [31, 0, 23] [32, 1, 1] [0, 7, 22] [31, 7, 22] [32, 1, 1] [0, 6, 22] [31, 6, 22] [32, 1, 1] [0, 5, 22] [31, 5, 22] [32, 1, 1] [0, 4, 22] [31, 4, 22] [32, 1, 1] [0, 3, 22] [31, 3, 22] [32, 1, 1] [0, 2, 22] [31, 2, 22] [32, 1, 1] [0, 1, 22] [31, 1, 22] [32, 1, 1] [0, 0, 22] [31, 0, 22] [32, 1, 1] [0, 7, 21] [31, 7, 21] [32, 1, 1] [0, 6, 21] [31, 6, 21] [32, 1, 1] [0, 5, 21] [31, 5, 21] [32, 1, 1] [0, 4, 21] [31, 4, 21] [32, 1, 1] [0, 3, 21] [31, 3, 21] [32, 1, 1] [0, 2, 21] [31, 2, 21] [32, 1, 1] [0, 1, 21] [31, 1, 21] [32, 1, 1] [0, 0, 21] [31, 0, 21] [32, 1, 1] [0, 7, 20] [31, 7, 20] [32, 1, 1] [0, 6, 20] [31, 6, 20] [32, 1, 1] [0, 5, 20] [31, 5, 20] [32, 1, 1] [0, 4, 20] [31, 4, 20] [32, 1, 1] [0, 3, 20] [31, 3, 20] [32, 1, 1] [0, 2, 20] [31, 2, 20] [32, 1, 1] [0, 1, 20] [31, 1, 20] [32, 1, 1] [0, 0, 20] [31, 0, 20] [32, 1, 1] [0, 7, 19] [31, 7, 19] [32, 1, 1] [0, 6, 19] [31, 6, 19] [32, 1, 1] [0, 5, 19] [31, 5, 19] [32, 1, 1] [0, 4, 19] [31, 4, 19] [32, 1, 1] [0, 3, 19] [31, 3, 19] [32, 1, 1] [0, 2, 19] [31, 2, 19] [32, 1, 1] [0, 1, 19] [31, 1, 19] [32, 1, 1] [0, 0, 19] [31, 0, 19] [32, 1, 1] [0, 7, 18] [31, 7, 18] [32, 1, 1] [0, 6, 18] [31, 6, 18] [32, 1, 1] [0, 5, 18] [31, 5, 18] [32, 1, 1] [0, 4, 18] [31, 4, 18] [32, 1, 1] [0, 3, 18] [31, 3, 18] [32, 1, 1] [0, 2, 18] [31, 2, 18] [32, 1, 1] [0, 1, 18] [31, 1, 18] [32, 1, 1] [0, 0, 18] [31, 0, 18] [32, 1, 1] [0, 7, 17] [31, 7, 17] [32, 1, 1] [0, 6, 17] [31, 6, 17] [32, 1, 1] [0, 5, 17] [31, 5, 17] [32, 1, 1] [0, 4, 17] [31, 4, 17] [32, 1, 1] [0, 3, 17] [31, 3, 17] [32, 1, 1] [0, 2, 17] [31, 2, 17] [32, 1, 1] [0, 1, 17] [31, 1, 17] [32, 1, 1] [0, 0, 17] [31, 0, 17] [32, 1, 1] [0, 7, 16] [31, 7, 16] [32, 1, 1] [0, 6, 16] [31, 6, 16] [32, 1, 1] [0, 5, 16] [31, 5, 16] [32, 1, 1] [0, 4, 16] [31, 4, 16] [32, 1, 1] [0, 3, 16] [31, 3, 16] [32, 1, 1] [0, 2, 16] [31, 2, 16] [32, 1, 1] [0, 1, 16] [31, 1, 16] [32, 1, 1] [0, 0, 16] [31, 0, 16] [32, 1, 1] [0, 31, 15] [31, 31, 15] [32, 1, 1] [0, 30, 15] [31, 30, 15] [32, 1, 1] [0, 29, 15] [31, 29, 15] [32, 1, 1] [0, 28, 15] [31, 28, 15] [32, 1, 1] [0, 27, 15] [31, 27, 15] [32, 1, 1] [0, 26, 15] [31, 26, 15] [32, 1, 1] [0, 25, 15] [31, 25, 15] [32, 1, 1] [0, 24, 15] [31, 24, 15] [32, 1, 1] [0, 31, 14] [31, 31, 14] [32, 1, 1] [0, 30, 14] [31, 30, 14] [32, 1, 1] [0, 29, 14] [31, 29, 14] [32, 1, 1] [0, 28, 14] [31, 28, 14] [32, 1, 1] [0, 27, 14] [31, 27, 14] [32, 1, 1] [0, 26, 14] [31, 26, 14] [32, 1, 1] [0, 25, 14] [31, 25, 14] [32, 1, 1] [0, 24, 14] [31, 24, 14] [32, 1, 1] [0, 31, 13] [31, 31, 13] [32, 1, 1] [0, 30, 13] [31, 30, 13] [32, 1, 1] [0, 29, 13] [31, 29, 13] [32, 1, 1] [0, 28, 13] [31, 28, 13] [32, 1, 1] [0, 27, 13] [31, 27, 13] [32, 1, 1] [0, 26, 13] [31, 26, 13] [32, 1, 1] [0, 25, 13] [31, 25, 13] [32, 1, 1] [0, 24, 13] [31, 24, 13] [32, 1, 1] [0, 31, 12] [31, 31, 12] [32, 1, 1] [0, 30, 12] [31, 30, 12] [32, 1, 1] [0, 29, 12] [31, 29, 12] [32, 1, 1] [0, 28, 12] [31, 28, 12] [32, 1, 1] [0, 27, 12] [31, 27, 12] [32, 1, 1] [0, 26, 12] [31, 26, 12] [32, 1, 1] [0, 25, 12] [31, 25, 12] [32, 1, 1] [0, 24, 12] [31, 24, 12] [32, 1, 1] [0, 31, 11] [31, 31, 11] [32, 1, 1] [0, 30, 11] [31, 30, 11] [32, 1, 1] [0, 29, 11] [31, 29, 11] [32, 1, 1] [0, 28, 11] [31, 28, 11] [32, 1, 1] [0, 27, 11] [31, 27, 11] [32, 1, 1] [0, 26, 11] [31, 26, 11] [32, 1, 1] [0, 25, 11] [31, 25, 11] [32, 1, 1] [0, 24, 11] [31, 24, 11] [32, 1, 1] [0, 31, 10] [31, 31, 10] [32, 1, 1] [0, 30, 10] [31, 30, 10] [32, 1, 1] [0, 29, 10] [31, 29, 10] [32, 1, 1] [0, 28, 10] [31, 28, 10] [32, 1, 1] [0, 27, 10] [31, 27, 10] [32, 1, 1] [0, 26, 10] [31, 26, 10] [32, 1, 1] [0, 25, 10] [31, 25, 10] [32, 1, 1] [0, 24, 10] [31, 24, 10] [32, 1, 1] [0, 31, 9] [31, 31, 9] [32, 1, 1] [0, 30, 9] [31, 30, 9] [32, 1, 1] [0, 29, 9] [31, 29, 9] [32, 1, 1] [0, 28, 9] [31, 28, 9] [32, 1, 1] [0, 27, 9] [31, 27, 9] [32, 1, 1] [0, 26, 9] [31, 26, 9] [32, 1, 1] [0, 25, 9] [31, 25, 9] [32, 1, 1] [0, 24, 9] [31, 24, 9] [32, 1, 1] [0, 31, 8] [31, 31, 8] [32, 1, 1] [0, 30, 8] [31, 30, 8] [32, 1, 1] [0, 29, 8] [31, 29, 8] [32, 1, 1] [0, 28, 8] [31, 28, 8] [32, 1, 1] [0, 27, 8] [31, 27, 8] [32, 1, 1] [0, 26, 8] [31, 26, 8] [32, 1, 1] [0, 25, 8] [31, 25, 8] [32, 1, 1] [0, 24, 8] [31, 24, 8] [32, 1, 1] [0, 23, 15] [31, 23, 15] [32, 1, 1] [0, 22, 15] [31, 22, 15] [32, 1, 1] [0, 21, 15] [31, 21, 15] [32, 1, 1] [0, 20, 15] [31, 20, 15] [32, 1, 1] [0, 19, 15] [31, 19, 15] [32, 1, 1] [0, 18, 15] [31, 18, 15] [32, 1, 1] [0, 17, 15] [31, 17, 15] [32, 1, 1] [0, 16, 15] [31, 16, 15] [32, 1, 1] [0, 23, 14] [31, 23, 14] [32, 1, 1] [0, 22, 14] [31, 22, 14] [32, 1, 1] [0, 21, 14] [31, 21, 14] [32, 1, 1] [0, 20, 14] [31, 20, 14] [32, 1, 1] [0, 19, 14] [31, 19, 14] [32, 1, 1] [0, 18, 14] [31, 18, 14] [32, 1, 1] [0, 17, 14] [31, 17, 14] [32, 1, 1] [0, 16, 14] [31, 16, 14] [32, 1, 1] [0, 23, 13] [31, 23, 13] [32, 1, 1] [0, 22, 13] [31, 22, 13] [32, 1, 1] [0, 21, 13] [31, 21, 13] [32, 1, 1] [0, 20, 13] [31, 20, 13] [32, 1, 1] [0, 19, 13] [31, 19, 13] [32, 1, 1] [0, 18, 13] [31, 18, 13] [32, 1, 1] [0, 17, 13] [31, 17, 13] [32, 1, 1] [0, 16, 13] [31, 16, 13] [32, 1, 1] [0, 23, 12] [31, 23, 12] [32, 1, 1] [0, 22, 12] [31, 22, 12] [32, 1, 1] [0, 21, 12] [31, 21, 12] [32, 1, 1] [0, 20, 12] [31, 20, 12] [32, 1, 1] [0, 19, 12] [31, 19, 12] [32, 1, 1] [0, 18, 12] [31, 18, 12] [32, 1, 1] [0, 17, 12] [31, 17, 12] [32, 1, 1] [0, 16, 12] [31, 16, 12] [32, 1, 1] [0, 23, 11] [31, 23, 11] [32, 1, 1] [0, 22, 11] [31, 22, 11] [32, 1, 1] [0, 21, 11] [31, 21, 11] [32, 1, 1] [0, 20, 11] [31, 20, 11] [32, 1, 1] [0, 19, 11] [31, 19, 11] [32, 1, 1] [0, 18, 11] [31, 18, 11] [32, 1, 1] [0, 17, 11] [31, 17, 11] [32, 1, 1] [0, 16, 11] [31, 16, 11] [32, 1, 1] [0, 23, 10] [31, 23, 10] [32, 1, 1] [0, 22, 10] [31, 22, 10] [32, 1, 1] [0, 21, 10] [31, 21, 10] [32, 1, 1] [0, 20, 10] [31, 20, 10] [32, 1, 1] [0, 19, 10] [31, 19, 10] [32, 1, 1] [0, 18, 10] [31, 18, 10] [32, 1, 1] [0, 17, 10] [31, 17, 10] [32, 1, 1] [0, 16, 10] [31, 16, 10] [32, 1, 1] [0, 23, 9] [31, 23, 9] [32, 1, 1] [0, 22, 9] [31, 22, 9] [32, 1, 1] [0, 21, 9] [31, 21, 9] [32, 1, 1] [0, 20, 9] [31, 20, 9] [32, 1, 1] [0, 19, 9] [31, 19, 9] [32, 1, 1] [0, 18, 9] [31, 18, 9] [32, 1, 1] [0, 17, 9] [31, 17, 9] [32, 1, 1] [0, 16, 9] [31, 16, 9] [32, 1, 1] [0, 23, 8] [31, 23, 8] [32, 1, 1] [0, 22, 8] [31, 22, 8] [32, 1, 1] [0, 21, 8] [31, 21, 8] [32, 1, 1] [0, 20, 8] [31, 20, 8] [32, 1, 1] [0, 19, 8] [31, 19, 8] [32, 1, 1] [0, 18, 8] [31, 18, 8] [32, 1, 1] [0, 17, 8] [31, 17, 8] [32, 1, 1] [0, 16, 8] [31, 16, 8] [32, 1, 1] [0, 15, 15] [31, 15, 15] [32, 1, 1] [0, 14, 15] [31, 14, 15] [32, 1, 1] [0, 13, 15] [31, 13, 15] [32, 1, 1] [0, 12, 15] [31, 12, 15] [32, 1, 1] [0, 11, 15] [31, 11, 15] [32, 1, 1] [0, 10, 15] [31, 10, 15] [32, 1, 1] [0, 9, 15] [31, 9, 15] [32, 1, 1] [0, 8, 15] [31, 8, 15] [32, 1, 1] [0, 15, 14] [31, 15, 14] [32, 1, 1] [0, 14, 14] [31, 14, 14] [32, 1, 1] [0, 13, 14] [31, 13, 14] [32, 1, 1] [0, 12, 14] [31, 12, 14] [32, 1, 1] [0, 11, 14] [31, 11, 14] [32, 1, 1] [0, 10, 14] [31, 10, 14] [32, 1, 1] [0, 9, 14] [31, 9, 14] [32, 1, 1] [0, 8, 14] [31, 8, 14] [32, 1, 1] [0, 15, 13] [31, 15, 13] [32, 1, 1] [0, 14, 13] [31, 14, 13] [32, 1, 1] [0, 13, 13] [31, 13, 13] [32, 1, 1] [0, 12, 13] [31, 12, 13] [32, 1, 1] [0, 11, 13] [31, 11, 13] [32, 1, 1] [0, 10, 13] [31, 10, 13] [32, 1, 1] [0, 9, 13] [31, 9, 13] [32, 1, 1] [0, 8, 13] [31, 8, 13] [32, 1, 1] [0, 15, 12] [31, 15, 12] [32, 1, 1] [0, 14, 12] [31, 14, 12] [32, 1, 1] [0, 13, 12] [31, 13, 12] [32, 1, 1] [0, 12, 12] [31, 12, 12] [32, 1, 1] [0, 11, 12] [31, 11, 12] [32, 1, 1] [0, 10, 12] [31, 10, 12] [32, 1, 1] [0, 9, 12] [31, 9, 12] [32, 1, 1] [0, 8, 12] [31, 8, 12] [32, 1, 1] [0, 15, 11] [31, 15, 11] [32, 1, 1] [0, 14, 11] [31, 14, 11] [32, 1, 1] [0, 13, 11] [31, 13, 11] [32, 1, 1] [0, 12, 11] [31, 12, 11] [32, 1, 1] [0, 11, 11] [31, 11, 11] [32, 1, 1] [0, 10, 11] [31, 10, 11] [32, 1, 1] [0, 9, 11] [31, 9, 11] [32, 1, 1] [0, 8, 11] [31, 8, 11] [32, 1, 1] [0, 15, 10] [31, 15, 10] [32, 1, 1] [0, 14, 10] [31, 14, 10] [32, 1, 1] [0, 13, 10] [31, 13, 10] [32, 1, 1] [0, 12, 10] [31, 12, 10] [32, 1, 1] [0, 11, 10] [31, 11, 10] [32, 1, 1] [0, 10, 10] [31, 10, 10] [32, 1, 1] [0, 9, 10] [31, 9, 10] [32, 1, 1] [0, 8, 10] [31, 8, 10] [32, 1, 1] [0, 15, 9] [31, 15, 9] [32, 1, 1] [0, 14, 9] [31, 14, 9] [32, 1, 1] [0, 13, 9] [31, 13, 9] [32, 1, 1] [0, 12, 9] [31, 12, 9] [32, 1, 1] [0, 11, 9] [31, 11, 9] [32, 1, 1] [0, 10, 9] [31, 10, 9] [32, 1, 1] [0, 9, 9] [31, 9, 9] [32, 1, 1] [0, 8, 9] [31, 8, 9] [32, 1, 1] [0, 15, 8] [31, 15, 8] [32, 1, 1] [0, 14, 8] [31, 14, 8] [32, 1, 1] [0, 13, 8] [31, 13, 8] [32, 1, 1] [0, 12, 8] [31, 12, 8] [32, 1, 1] [0, 11, 8] [31, 11, 8] [32, 1, 1] [0, 10, 8] [31, 10, 8] [32, 1, 1] [0, 9, 8] [31, 9, 8] [32, 1, 1] [0, 8, 8] [31, 8, 8] [32, 1, 1] [0, 7, 15] [31, 7, 15] [32, 1, 1] [0, 6, 15] [31, 6, 15] [32, 1, 1] [0, 5, 15] [31, 5, 15] [32, 1, 1] [0, 4, 15] [31, 4, 15] [32, 1, 1] [0, 3, 15] [31, 3, 15] [32, 1, 1] [0, 2, 15] [31, 2, 15] [32, 1, 1] [0, 1, 15] [31, 1, 15] [32, 1, 1] [0, 0, 15] [31, 0, 15] [32, 1, 1] [0, 7, 14] [31, 7, 14] [32, 1, 1] [0, 6, 14] [31, 6, 14] [32, 1, 1] [0, 5, 14] [31, 5, 14] [32, 1, 1] [0, 4, 14] [31, 4, 14] [32, 1, 1] [0, 3, 14] [31, 3, 14] [32, 1, 1] [0, 2, 14] [31, 2, 14] [32, 1, 1] [0, 1, 14] [31, 1, 14] [32, 1, 1] [0, 0, 14] [31, 0, 14] [32, 1, 1] [0, 7, 13] [31, 7, 13] [32, 1, 1] [0, 6, 13] [31, 6, 13] [32, 1, 1] [0, 5, 13] [31, 5, 13] [32, 1, 1] [0, 4, 13] [31, 4, 13] [32, 1, 1] [0, 3, 13] [31, 3, 13] [32, 1, 1] [0, 2, 13] [31, 2, 13] [32, 1, 1] [0, 1, 13] [31, 1, 13] [32, 1, 1] [0, 0, 13] [31, 0, 13] [32, 1, 1] [0, 7, 12] [31, 7, 12] [32, 1, 1] [0, 6, 12] [31, 6, 12] [32, 1, 1] [0, 5, 12] [31, 5, 12] [32, 1, 1] [0, 4, 12] [31, 4, 12] [32, 1, 1] [0, 3, 12] [31, 3, 12] [32, 1, 1] [0, 2, 12] [31, 2, 12] [32, 1, 1] [0, 1, 12] [31, 1, 12] [32, 1, 1] [0, 0, 12] [31, 0, 12] [32, 1, 1] [0, 7, 11] [31, 7, 11] [32, 1, 1] [0, 6, 11] [31, 6, 11] [32, 1, 1] [0, 5, 11] [31, 5, 11] [32, 1, 1] [0, 4, 11] [31, 4, 11] [32, 1, 1] [0, 3, 11] [31, 3, 11] [32, 1, 1] [0, 2, 11] [31, 2, 11] [32, 1, 1] [0, 1, 11] [31, 1, 11] [32, 1, 1] [0, 0, 11] [31, 0, 11] [32, 1, 1] [0, 7, 10] [31, 7, 10] [32, 1, 1] [0, 6, 10] [31, 6, 10] [32, 1, 1] [0, 5, 10] [31, 5, 10] [32, 1, 1] [0, 4, 10] [31, 4, 10] [32, 1, 1] [0, 3, 10] [31, 3, 10] [32, 1, 1] [0, 2, 10] [31, 2, 10] [32, 1, 1] [0, 1, 10] [31, 1, 10] [32, 1, 1] [0, 0, 10] [31, 0, 10] [32, 1, 1] [0, 7, 9] [31, 7, 9] [32, 1, 1] [0, 6, 9] [31, 6, 9] [32, 1, 1] [0, 5, 9] [31, 5, 9] [32, 1, 1] [0, 4, 9] [31, 4, 9] [32, 1, 1] [0, 3, 9] [31, 3, 9] [32, 1, 1] [0, 2, 9] [31, 2, 9] [32, 1, 1] [0, 1, 9] [31, 1, 9] [32, 1, 1] [0, 0, 9] [31, 0, 9] [32, 1, 1] [0, 7, 8] [31, 7, 8] [32, 1, 1] [0, 6, 8] [31, 6, 8] [32, 1, 1] [0, 5, 8] [31, 5, 8] [32, 1, 1] [0, 4, 8] [31, 4, 8] [32, 1, 1] [0, 3, 8] [31, 3, 8] [32, 1, 1] [0, 2, 8] [31, 2, 8] [32, 1, 1] [0, 1, 8] [31, 1, 8] [32, 1, 1] [0, 0, 8] [31, 0, 8] [32, 1, 1] [0, 31, 7] [31, 31, 7] [32, 1, 1] [0, 30, 7] [31, 30, 7] [32, 1, 1] [0, 29, 7] [31, 29, 7] [32, 1, 1] [0, 28, 7] [31, 28, 7] [32, 1, 1] [0, 27, 7] [31, 27, 7] [32, 1, 1] [0, 26, 7] [31, 26, 7] [32, 1, 1] [0, 25, 7] [31, 25, 7] [32, 1, 1] [0, 24, 7] [31, 24, 7] [32, 1, 1] [0, 31, 6] [31, 31, 6] [32, 1, 1] [0, 30, 6] [31, 30, 6] [32, 1, 1] [0, 29, 6] [31, 29, 6] [32, 1, 1] [0, 28, 6] [31, 28, 6] [32, 1, 1] [0, 27, 6] [31, 27, 6] [32, 1, 1] [0, 26, 6] [31, 26, 6] [32, 1, 1] [0, 25, 6] [31, 25, 6] [32, 1, 1] [0, 24, 6] [31, 24, 6] [32, 1, 1] [0, 31, 5] [31, 31, 5] [32, 1, 1] [0, 30, 5] [31, 30, 5] [32, 1, 1] [0, 29, 5] [31, 29, 5] [32, 1, 1] [0, 28, 5] [31, 28, 5] [32, 1, 1] [0, 27, 5] [31, 27, 5] [32, 1, 1] [0, 26, 5] [31, 26, 5] [32, 1, 1] [0, 25, 5] [31, 25, 5] [32, 1, 1] [0, 24, 5] [31, 24, 5] [32, 1, 1] [0, 31, 4] [31, 31, 4] [32, 1, 1] [0, 30, 4] [31, 30, 4] [32, 1, 1] [0, 29, 4] [31, 29, 4] [32, 1, 1] [0, 28, 4] [31, 28, 4] [32, 1, 1] [0, 27, 4] [31, 27, 4] [32, 1, 1] [0, 26, 4] [31, 26, 4] [32, 1, 1] [0, 25, 4] [31, 25, 4] [32, 1, 1] [0, 24, 4] [31, 24, 4] [32, 1, 1] [0, 31, 3] [31, 31, 3] [32, 1, 1] [0, 30, 3] [31, 30, 3] [32, 1, 1] [0, 29, 3] [31, 29, 3] [32, 1, 1] [0, 28, 3] [31, 28, 3] [32, 1, 1] [0, 27, 3] [31, 27, 3] [32, 1, 1] [0, 26, 3] [31, 26, 3] [32, 1, 1] [0, 25, 3] [31, 25, 3] [32, 1, 1] [0, 24, 3] [31, 24, 3] [32, 1, 1] [0, 31, 2] [31, 31, 2] [32, 1, 1] [0, 30, 2] [31, 30, 2] [32, 1, 1] [0, 29, 2] [31, 29, 2] [32, 1, 1] [0, 28, 2] [31, 28, 2] [32, 1, 1] [0, 27, 2] [31, 27, 2] [32, 1, 1] [0, 26, 2] [31, 26, 2] [32, 1, 1] [0, 25, 2] [31, 25, 2] [32, 1, 1] [0, 24, 2] [31, 24, 2] [32, 1, 1] [0, 31, 1] [31, 31, 1] [32, 1, 1] [0, 30, 1] [31, 30, 1] [32, 1, 1] [0, 29, 1] [31, 29, 1] [32, 1, 1] [0, 28, 1] [31, 28, 1] [32, 1, 1] [0, 27, 1] [31, 27, 1] [32, 1, 1] [0, 26, 1] [31, 26, 1] [32, 1, 1] [0, 25, 1] [31, 25, 1] [32, 1, 1] [0, 24, 1] [31, 24, 1] [32, 1, 1] [0, 31, 0] [31, 31, 0] [32, 1, 1] [0, 30, 0] [31, 30, 0] [32, 1, 1] [0, 29, 0] [31, 29, 0] [32, 1, 1] [0, 28, 0] [31, 28, 0] [32, 1, 1] [0, 27, 0] [31, 27, 0] [32, 1, 1] [0, 26, 0] [31, 26, 0] [32, 1, 1] [0, 25, 0] [31, 25, 0] [32, 1, 1] [0, 24, 0] [31, 24, 0] [32, 1, 1] [0, 23, 7] [31, 23, 7] [32, 1, 1] [0, 22, 7] [31, 22, 7] [32, 1, 1] [0, 21, 7] [31, 21, 7] [32, 1, 1] [0, 20, 7] [31, 20, 7] [32, 1, 1] [0, 19, 7] [31, 19, 7] [32, 1, 1] [0, 18, 7] [31, 18, 7] [32, 1, 1] [0, 17, 7] [31, 17, 7] [32, 1, 1] [0, 16, 7] [31, 16, 7] [32, 1, 1] [0, 23, 6] [31, 23, 6] [32, 1, 1] [0, 22, 6] [31, 22, 6] [32, 1, 1] [0, 21, 6] [31, 21, 6] [32, 1, 1] [0, 20, 6] [31, 20, 6] [32, 1, 1] [0, 19, 6] [31, 19, 6] [32, 1, 1] [0, 18, 6] [31, 18, 6] [32, 1, 1] [0, 17, 6] [31, 17, 6] [32, 1, 1] [0, 16, 6] [31, 16, 6] [32, 1, 1] [0, 23, 5] [31, 23, 5] [32, 1, 1] [0, 22, 5] [31, 22, 5] [32, 1, 1] [0, 21, 5] [31, 21, 5] [32, 1, 1] [0, 20, 5] [31, 20, 5] [32, 1, 1] [0, 19, 5] [31, 19, 5] [32, 1, 1] [0, 18, 5] [31, 18, 5] [32, 1, 1] [0, 17, 5] [31, 17, 5] [32, 1, 1] [0, 16, 5] [31, 16, 5] [32, 1, 1] [0, 23, 4] [31, 23, 4] [32, 1, 1] [0, 22, 4] [31, 22, 4] [32, 1, 1] [0, 21, 4] [31, 21, 4] [32, 1, 1] [0, 20, 4] [31, 20, 4] [32, 1, 1] [0, 19, 4] [31, 19, 4] [32, 1, 1] [0, 18, 4] [31, 18, 4] [32, 1, 1] [0, 17, 4] [31, 17, 4] [32, 1, 1] [0, 16, 4] [31, 16, 4] [32, 1, 1] [0, 23, 3] [31, 23, 3] [32, 1, 1] [0, 22, 3] [31, 22, 3] [32, 1, 1] [0, 21, 3] [31, 21, 3] [32, 1, 1] [0, 20, 3] [31, 20, 3] [32, 1, 1] [0, 19, 3] [31, 19, 3] [32, 1, 1] [0, 18, 3] [31, 18, 3] [32, 1, 1] [0, 17, 3] [31, 17, 3] [32, 1, 1] [0, 16, 3] [31, 16, 3] [32, 1, 1] [0, 23, 2] [31, 23, 2] [32, 1, 1] [0, 22, 2] [31, 22, 2] [32, 1, 1] [0, 21, 2] [31, 21, 2] [32, 1, 1] [0, 20, 2] [31, 20, 2] [32, 1, 1] [0, 19, 2] [31, 19, 2] [32, 1, 1] [0, 18, 2] [31, 18, 2] [32, 1, 1] [0, 17, 2] [31, 17, 2] [32, 1, 1] [0, 16, 2] [31, 16, 2] [32, 1, 1] [0, 23, 1] [31, 23, 1] [32, 1, 1] [0, 22, 1] [31, 22, 1] [32, 1, 1] [0, 21, 1] [31, 21, 1] [32, 1, 1] [0, 20, 1] [31, 20, 1] [32, 1, 1] [0, 19, 1] [31, 19, 1] [32, 1, 1] [0, 18, 1] [31, 18, 1] [32, 1, 1] [0, 17, 1] [31, 17, 1] [32, 1, 1] [0, 16, 1] [31, 16, 1] [32, 1, 1] [0, 23, 0] [31, 23, 0] [32, 1, 1] [0, 22, 0] [31, 22, 0] [32, 1, 1] [0, 21, 0] [31, 21, 0] [32, 1, 1] [0, 20, 0] [31, 20, 0] [32, 1, 1] [0, 19, 0] [31, 19, 0] [32, 1, 1] [0, 18, 0] [31, 18, 0] [32, 1, 1] [0, 17, 0] [31, 17, 0] [32, 1, 1] [0, 16, 0] [31, 16, 0] [32, 1, 1] [0, 15, 7] [31, 15, 7] [32, 1, 1] [0, 14, 7] [31, 14, 7] [32, 1, 1] [0, 13, 7] [31, 13, 7] [32, 1, 1] [0, 12, 7] [31, 12, 7] [32, 1, 1] [0, 11, 7] [31, 11, 7] [32, 1, 1] [0, 10, 7] [31, 10, 7] [32, 1, 1] [0, 9, 7] [31, 9, 7] [32, 1, 1] [0, 8, 7] [31, 8, 7] [32, 1, 1] [0, 15, 6] [31, 15, 6] [32, 1, 1] [0, 14, 6] [31, 14, 6] [32, 1, 1] [0, 13, 6] [31, 13, 6] [32, 1, 1] [0, 12, 6] [31, 12, 6] [32, 1, 1] [0, 11, 6] [31, 11, 6] [32, 1, 1] [0, 10, 6] [31, 10, 6] [32, 1, 1] [0, 9, 6] [31, 9, 6] [32, 1, 1] [0, 8, 6] [31, 8, 6] [32, 1, 1] [0, 15, 5] [31, 15, 5] [32, 1, 1] [0, 14, 5] [31, 14, 5] [32, 1, 1] [0, 13, 5] [31, 13, 5] [32, 1, 1] [0, 12, 5] [31, 12, 5] [32, 1, 1] [0, 11, 5] [31, 11, 5] [32, 1, 1] [0, 10, 5] [31, 10, 5] [32, 1, 1] [0, 9, 5] [31, 9, 5] [32, 1, 1] [0, 8, 5] [31, 8, 5] [32, 1, 1] [0, 15, 4] [31, 15, 4] [32, 1, 1] [0, 14, 4] [31, 14, 4] [32, 1, 1] [0, 13, 4] [31, 13, 4] [32, 1, 1] [0, 12, 4] [31, 12, 4] [32, 1, 1] [0, 11, 4] [31, 11, 4] [32, 1, 1] [0, 10, 4] [31, 10, 4] [32, 1, 1] [0, 9, 4] [31, 9, 4] [32, 1, 1] [0, 8, 4] [31, 8, 4] [32, 1, 1] [0, 15, 3] [31, 15, 3] [32, 1, 1] [0, 14, 3] [31, 14, 3] [32, 1, 1] [0, 13, 3] [31, 13, 3] [32, 1, 1] [0, 12, 3] [31, 12, 3] [32, 1, 1] [0, 11, 3] [31, 11, 3] [32, 1, 1] [0, 10, 3] [31, 10, 3] [32, 1, 1] [0, 9, 3] [31, 9, 3] [32, 1, 1] [0, 8, 3] [31, 8, 3] [32, 1, 1] [0, 15, 2] [31, 15, 2] [32, 1, 1] [0, 14, 2] [31, 14, 2] [32, 1, 1] [0, 13, 2] [31, 13, 2] [32, 1, 1] [0, 12, 2] [31, 12, 2] [32, 1, 1] [0, 11, 2] [31, 11, 2] [32, 1, 1] [0, 10, 2] [31, 10, 2] [32, 1, 1] [0, 9, 2] [31, 9, 2] [32, 1, 1] [0, 8, 2] [31, 8, 2] [32, 1, 1] [0, 15, 1] [31, 15, 1] [32, 1, 1] [0, 14, 1] [31, 14, 1] [32, 1, 1] [0, 13, 1] [31, 13, 1] [32, 1, 1] [0, 12, 1] [31, 12, 1] [32, 1, 1] [0, 11, 1] [31, 11, 1] [32, 1, 1] [0, 10, 1] [31, 10, 1] [32, 1, 1] [0, 9, 1] [31, 9, 1] [32, 1, 1] [0, 8, 1] [31, 8, 1] [32, 1, 1] [0, 15, 0] [31, 15, 0] [32, 1, 1] [0, 14, 0] [31, 14, 0] [32, 1, 1] [0, 13, 0] [31, 13, 0] [32, 1, 1] [0, 12, 0] [31, 12, 0] [32, 1, 1] [0, 11, 0] [31, 11, 0] [32, 1, 1] [0, 10, 0] [31, 10, 0] [32, 1, 1] [0, 9, 0] [31, 9, 0] [32, 1, 1] [0, 8, 0] [31, 8, 0] [32, 1, 1] [0, 7, 7] [31, 7, 7] [32, 1, 1] [0, 6, 7] [31, 6, 7] [32, 1, 1] [0, 5, 7] [31, 5, 7] [32, 1, 1] [0, 4, 7] [31, 4, 7] [32, 1, 1] [0, 3, 7] [31, 3, 7] [32, 1, 1] [0, 2, 7] [31, 2, 7] [32, 1, 1] [0, 1, 7] [31, 1, 7] [32, 1, 1] [0, 0, 7] [31, 0, 7] [32, 1, 1] [0, 7, 6] [31, 7, 6] [32, 1, 1] [0, 6, 6] [31, 6, 6] [32, 1, 1] [0, 5, 6] [31, 5, 6] [32, 1, 1] [0, 4, 6] [31, 4, 6] [32, 1, 1] [0, 3, 6] [31, 3, 6] [32, 1, 1] [0, 2, 6] [31, 2, 6] [32, 1, 1] [0, 1, 6] [31, 1, 6] [32, 1, 1] [0, 0, 6] [31, 0, 6] [32, 1, 1] [0, 7, 5] [31, 7, 5] [32, 1, 1] [0, 6, 5] [31, 6, 5] [32, 1, 1] [0, 5, 5] [31, 5, 5] [32, 1, 1] [0, 4, 5] [31, 4, 5] [32, 1, 1] [0, 3, 5] [31, 3, 5] [32, 1, 1] [0, 2, 5] [31, 2, 5] [32, 1, 1] [0, 1, 5] [31, 1, 5] [32, 1, 1] [0, 0, 5] [31, 0, 5] [32, 1, 1] [0, 7, 4] [31, 7, 4] [32, 1, 1] [0, 6, 4] [31, 6, 4] [32, 1, 1] [0, 5, 4] [31, 5, 4] [32, 1, 1] [0, 4, 4] [31, 4, 4] [32, 1, 1] [0, 3, 4] [31, 3, 4] [32, 1, 1] [0, 2, 4] [31, 2, 4] [32, 1, 1] [0, 1, 4] [31, 1, 4] [32, 1, 1] [0, 0, 4] [31, 0, 4] [32, 1, 1] [0, 7, 3] [31, 7, 3] [32, 1, 1] [0, 6, 3] [31, 6, 3] [32, 1, 1] [0, 5, 3] [31, 5, 3] [32, 1, 1] [0, 4, 3] [31, 4, 3] [32, 1, 1] [0, 3, 3] [31, 3, 3] [32, 1, 1] [0, 2, 3] [31, 2, 3] [32, 1, 1] [0, 1, 3] [31, 1, 3] [32, 1, 1] [0, 0, 3] [31, 0, 3] [32, 1, 1] [0, 7, 2] [31, 7, 2] [32, 1, 1] [0, 6, 2] [31, 6, 2] [32, 1, 1] [0, 5, 2] [31, 5, 2] [32, 1, 1] [0, 4, 2] [31, 4, 2] [32, 1, 1] [0, 3, 2] [31, 3, 2] [32, 1, 1] [0, 2, 2] [31, 2, 2] [32, 1, 1] [0, 1, 2] [31, 1, 2] [32, 1, 1] [0, 0, 2] [31, 0, 2] [32, 1, 1] [0, 7, 1] [31, 7, 1] [32, 1, 1] [0, 6, 1] [31, 6, 1] [32, 1, 1] [0, 5, 1] [31, 5, 1] [32, 1, 1] [0, 4, 1] [31, 4, 1] [32, 1, 1] [0, 3, 1] [31, 3, 1] [32, 1, 1] [0, 2, 1] [31, 2, 1] [32, 1, 1] [0, 1, 1] [31, 1, 1] [32, 1, 1] [0, 0, 1] [31, 0, 1] [32, 1, 1] [0, 7, 0] [31, 7, 0] [32, 1, 1] [0, 6, 0] [31, 6, 0] [32, 1, 1] [0, 5, 0] [31, 5, 0] [32, 1, 1] [0, 4, 0] [31, 4, 0] [32, 1, 1] [0, 3, 0] [31, 3, 0] [32, 1, 1] [0, 2, 0] [31, 2, 0] [32, 1, 1] [0, 1, 0] [31, 1, 0] [32, 1, 1] [0, 0, 0] [31, 0, 0] [32, 1, 1] nsteps = 2048 [0, 0, 0] [0, 31, 0] [1, 32, 1] [1, 0, 0] [1, 31, 0] [1, 32, 1] [2, 0, 0] [2, 31, 0] [1, 32, 1] [3, 0, 0] [3, 31, 0] [1, 32, 1] [4, 0, 0] [4, 31, 0] [1, 32, 1] [5, 0, 0] [5, 31, 0] [1, 32, 1] [6, 0, 0] [6, 31, 0] [1, 32, 1] [7, 0, 0] [7, 31, 0] [1, 32, 1] [0, 0, 1] [0, 31, 1] [1, 32, 1] [1, 0, 1] [1, 31, 1] [1, 32, 1] [2, 0, 1] [2, 31, 1] [1, 32, 1] [3, 0, 1] [3, 31, 1] [1, 32, 1] [4, 0, 1] [4, 31, 1] [1, 32, 1] [5, 0, 1] [5, 31, 1] [1, 32, 1] [6, 0, 1] [6, 31, 1] [1, 32, 1] [7, 0, 1] [7, 31, 1] [1, 32, 1] [0, 0, 2] [0, 31, 2] [1, 32, 1] [1, 0, 2] [1, 31, 2] [1, 32, 1] [2, 0, 2] [2, 31, 2] [1, 32, 1] [3, 0, 2] [3, 31, 2] [1, 32, 1] [4, 0, 2] [4, 31, 2] [1, 32, 1] [5, 0, 2] [5, 31, 2] [1, 32, 1] [6, 0, 2] [6, 31, 2] [1, 32, 1] [7, 0, 2] [7, 31, 2] [1, 32, 1] [0, 0, 3] [0, 31, 3] [1, 32, 1] [1, 0, 3] [1, 31, 3] [1, 32, 1] [2, 0, 3] [2, 31, 3] [1, 32, 1] [3, 0, 3] [3, 31, 3] [1, 32, 1] [4, 0, 3] [4, 31, 3] [1, 32, 1] [5, 0, 3] [5, 31, 3] [1, 32, 1] [6, 0, 3] [6, 31, 3] [1, 32, 1] [7, 0, 3] [7, 31, 3] [1, 32, 1] [0, 0, 4] [0, 31, 4] [1, 32, 1] [1, 0, 4] [1, 31, 4] [1, 32, 1] [2, 0, 4] [2, 31, 4] [1, 32, 1] [3, 0, 4] [3, 31, 4] [1, 32, 1] [4, 0, 4] [4, 31, 4] [1, 32, 1] [5, 0, 4] [5, 31, 4] [1, 32, 1] [6, 0, 4] [6, 31, 4] [1, 32, 1] [7, 0, 4] [7, 31, 4] [1, 32, 1] [0, 0, 5] [0, 31, 5] [1, 32, 1] [1, 0, 5] [1, 31, 5] [1, 32, 1] [2, 0, 5] [2, 31, 5] [1, 32, 1] [3, 0, 5] [3, 31, 5] [1, 32, 1] [4, 0, 5] [4, 31, 5] [1, 32, 1] [5, 0, 5] [5, 31, 5] [1, 32, 1] [6, 0, 5] [6, 31, 5] [1, 32, 1] [7, 0, 5] [7, 31, 5] [1, 32, 1] [0, 0, 6] [0, 31, 6] [1, 32, 1] [1, 0, 6] [1, 31, 6] [1, 32, 1] [2, 0, 6] [2, 31, 6] [1, 32, 1] [3, 0, 6] [3, 31, 6] [1, 32, 1] [4, 0, 6] [4, 31, 6] [1, 32, 1] [5, 0, 6] [5, 31, 6] [1, 32, 1] [6, 0, 6] [6, 31, 6] [1, 32, 1] [7, 0, 6] [7, 31, 6] [1, 32, 1] [0, 0, 7] [0, 31, 7] [1, 32, 1] [1, 0, 7] [1, 31, 7] [1, 32, 1] [2, 0, 7] [2, 31, 7] [1, 32, 1] [3, 0, 7] [3, 31, 7] [1, 32, 1] [4, 0, 7] [4, 31, 7] [1, 32, 1] [5, 0, 7] [5, 31, 7] [1, 32, 1] [6, 0, 7] [6, 31, 7] [1, 32, 1] [7, 0, 7] [7, 31, 7] [1, 32, 1] [8, 0, 0] [8, 31, 0] [1, 32, 1] [9, 0, 0] [9, 31, 0] [1, 32, 1] [10, 0, 0] [10, 31, 0] [1, 32, 1] [11, 0, 0] [11, 31, 0] [1, 32, 1] [12, 0, 0] [12, 31, 0] [1, 32, 1] [13, 0, 0] [13, 31, 0] [1, 32, 1] [14, 0, 0] [14, 31, 0] [1, 32, 1] [15, 0, 0] [15, 31, 0] [1, 32, 1] [8, 0, 1] [8, 31, 1] [1, 32, 1] [9, 0, 1] [9, 31, 1] [1, 32, 1] [10, 0, 1] [10, 31, 1] [1, 32, 1] [11, 0, 1] [11, 31, 1] [1, 32, 1] [12, 0, 1] [12, 31, 1] [1, 32, 1] [13, 0, 1] [13, 31, 1] [1, 32, 1] [14, 0, 1] [14, 31, 1] [1, 32, 1] [15, 0, 1] [15, 31, 1] [1, 32, 1] [8, 0, 2] [8, 31, 2] [1, 32, 1] [9, 0, 2] [9, 31, 2] [1, 32, 1] [10, 0, 2] [10, 31, 2] [1, 32, 1] [11, 0, 2] [11, 31, 2] [1, 32, 1] [12, 0, 2] [12, 31, 2] [1, 32, 1] [13, 0, 2] [13, 31, 2] [1, 32, 1] [14, 0, 2] [14, 31, 2] [1, 32, 1] [15, 0, 2] [15, 31, 2] [1, 32, 1] [8, 0, 3] [8, 31, 3] [1, 32, 1] [9, 0, 3] [9, 31, 3] [1, 32, 1] [10, 0, 3] [10, 31, 3] [1, 32, 1] [11, 0, 3] [11, 31, 3] [1, 32, 1] [12, 0, 3] [12, 31, 3] [1, 32, 1] [13, 0, 3] [13, 31, 3] [1, 32, 1] [14, 0, 3] [14, 31, 3] [1, 32, 1] [15, 0, 3] [15, 31, 3] [1, 32, 1] [8, 0, 4] [8, 31, 4] [1, 32, 1] [9, 0, 4] [9, 31, 4] [1, 32, 1] [10, 0, 4] [10, 31, 4] [1, 32, 1] [11, 0, 4] [11, 31, 4] [1, 32, 1] [12, 0, 4] [12, 31, 4] [1, 32, 1] [13, 0, 4] [13, 31, 4] [1, 32, 1] [14, 0, 4] [14, 31, 4] [1, 32, 1] [15, 0, 4] [15, 31, 4] [1, 32, 1] [8, 0, 5] [8, 31, 5] [1, 32, 1] [9, 0, 5] [9, 31, 5] [1, 32, 1] [10, 0, 5] [10, 31, 5] [1, 32, 1] [11, 0, 5] [11, 31, 5] [1, 32, 1] [12, 0, 5] [12, 31, 5] [1, 32, 1] [13, 0, 5] [13, 31, 5] [1, 32, 1] [14, 0, 5] [14, 31, 5] [1, 32, 1] [15, 0, 5] [15, 31, 5] [1, 32, 1] [8, 0, 6] [8, 31, 6] [1, 32, 1] [9, 0, 6] [9, 31, 6] [1, 32, 1] [10, 0, 6] [10, 31, 6] [1, 32, 1] [11, 0, 6] [11, 31, 6] [1, 32, 1] [12, 0, 6] [12, 31, 6] [1, 32, 1] [13, 0, 6] [13, 31, 6] [1, 32, 1] [14, 0, 6] [14, 31, 6] [1, 32, 1] [15, 0, 6] [15, 31, 6] [1, 32, 1] [8, 0, 7] [8, 31, 7] [1, 32, 1] [9, 0, 7] [9, 31, 7] [1, 32, 1] [10, 0, 7] [10, 31, 7] [1, 32, 1] [11, 0, 7] [11, 31, 7] [1, 32, 1] [12, 0, 7] [12, 31, 7] [1, 32, 1] [13, 0, 7] [13, 31, 7] [1, 32, 1] [14, 0, 7] [14, 31, 7] [1, 32, 1] [15, 0, 7] [15, 31, 7] [1, 32, 1] [16, 0, 0] [16, 31, 0] [1, 32, 1] [17, 0, 0] [17, 31, 0] [1, 32, 1] [18, 0, 0] [18, 31, 0] [1, 32, 1] [19, 0, 0] [19, 31, 0] [1, 32, 1] [20, 0, 0] [20, 31, 0] [1, 32, 1] [21, 0, 0] [21, 31, 0] [1, 32, 1] [22, 0, 0] [22, 31, 0] [1, 32, 1] [23, 0, 0] [23, 31, 0] [1, 32, 1] [16, 0, 1] [16, 31, 1] [1, 32, 1] [17, 0, 1] [17, 31, 1] [1, 32, 1] [18, 0, 1] [18, 31, 1] [1, 32, 1] [19, 0, 1] [19, 31, 1] [1, 32, 1] [20, 0, 1] [20, 31, 1] [1, 32, 1] [21, 0, 1] [21, 31, 1] [1, 32, 1] [22, 0, 1] [22, 31, 1] [1, 32, 1] [23, 0, 1] [23, 31, 1] [1, 32, 1] [16, 0, 2] [16, 31, 2] [1, 32, 1] [17, 0, 2] [17, 31, 2] [1, 32, 1] [18, 0, 2] [18, 31, 2] [1, 32, 1] [19, 0, 2] [19, 31, 2] [1, 32, 1] [20, 0, 2] [20, 31, 2] [1, 32, 1] [21, 0, 2] [21, 31, 2] [1, 32, 1] [22, 0, 2] [22, 31, 2] [1, 32, 1] [23, 0, 2] [23, 31, 2] [1, 32, 1] [16, 0, 3] [16, 31, 3] [1, 32, 1] [17, 0, 3] [17, 31, 3] [1, 32, 1] [18, 0, 3] [18, 31, 3] [1, 32, 1] [19, 0, 3] [19, 31, 3] [1, 32, 1] [20, 0, 3] [20, 31, 3] [1, 32, 1] [21, 0, 3] [21, 31, 3] [1, 32, 1] [22, 0, 3] [22, 31, 3] [1, 32, 1] [23, 0, 3] [23, 31, 3] [1, 32, 1] [16, 0, 4] [16, 31, 4] [1, 32, 1] [17, 0, 4] [17, 31, 4] [1, 32, 1] [18, 0, 4] [18, 31, 4] [1, 32, 1] [19, 0, 4] [19, 31, 4] [1, 32, 1] [20, 0, 4] [20, 31, 4] [1, 32, 1] [21, 0, 4] [21, 31, 4] [1, 32, 1] [22, 0, 4] [22, 31, 4] [1, 32, 1] [23, 0, 4] [23, 31, 4] [1, 32, 1] [16, 0, 5] [16, 31, 5] [1, 32, 1] [17, 0, 5] [17, 31, 5] [1, 32, 1] [18, 0, 5] [18, 31, 5] [1, 32, 1] [19, 0, 5] [19, 31, 5] [1, 32, 1] [20, 0, 5] [20, 31, 5] [1, 32, 1] [21, 0, 5] [21, 31, 5] [1, 32, 1] [22, 0, 5] [22, 31, 5] [1, 32, 1] [23, 0, 5] [23, 31, 5] [1, 32, 1] [16, 0, 6] [16, 31, 6] [1, 32, 1] [17, 0, 6] [17, 31, 6] [1, 32, 1] [18, 0, 6] [18, 31, 6] [1, 32, 1] [19, 0, 6] [19, 31, 6] [1, 32, 1] [20, 0, 6] [20, 31, 6] [1, 32, 1] [21, 0, 6] [21, 31, 6] [1, 32, 1] [22, 0, 6] [22, 31, 6] [1, 32, 1] [23, 0, 6] [23, 31, 6] [1, 32, 1] [16, 0, 7] [16, 31, 7] [1, 32, 1] [17, 0, 7] [17, 31, 7] [1, 32, 1] [18, 0, 7] [18, 31, 7] [1, 32, 1] [19, 0, 7] [19, 31, 7] [1, 32, 1] [20, 0, 7] [20, 31, 7] [1, 32, 1] [21, 0, 7] [21, 31, 7] [1, 32, 1] [22, 0, 7] [22, 31, 7] [1, 32, 1] [23, 0, 7] [23, 31, 7] [1, 32, 1] [24, 0, 0] [24, 31, 0] [1, 32, 1] [25, 0, 0] [25, 31, 0] [1, 32, 1] [26, 0, 0] [26, 31, 0] [1, 32, 1] [27, 0, 0] [27, 31, 0] [1, 32, 1] [28, 0, 0] [28, 31, 0] [1, 32, 1] [29, 0, 0] [29, 31, 0] [1, 32, 1] [30, 0, 0] [30, 31, 0] [1, 32, 1] [31, 0, 0] [31, 31, 0] [1, 32, 1] [24, 0, 1] [24, 31, 1] [1, 32, 1] [25, 0, 1] [25, 31, 1] [1, 32, 1] [26, 0, 1] [26, 31, 1] [1, 32, 1] [27, 0, 1] [27, 31, 1] [1, 32, 1] [28, 0, 1] [28, 31, 1] [1, 32, 1] [29, 0, 1] [29, 31, 1] [1, 32, 1] [30, 0, 1] [30, 31, 1] [1, 32, 1] [31, 0, 1] [31, 31, 1] [1, 32, 1] [24, 0, 2] [24, 31, 2] [1, 32, 1] [25, 0, 2] [25, 31, 2] [1, 32, 1] [26, 0, 2] [26, 31, 2] [1, 32, 1] [27, 0, 2] [27, 31, 2] [1, 32, 1] [28, 0, 2] [28, 31, 2] [1, 32, 1] [29, 0, 2] [29, 31, 2] [1, 32, 1] [30, 0, 2] [30, 31, 2] [1, 32, 1] [31, 0, 2] [31, 31, 2] [1, 32, 1] [24, 0, 3] [24, 31, 3] [1, 32, 1] [25, 0, 3] [25, 31, 3] [1, 32, 1] [26, 0, 3] [26, 31, 3] [1, 32, 1] [27, 0, 3] [27, 31, 3] [1, 32, 1] [28, 0, 3] [28, 31, 3] [1, 32, 1] [29, 0, 3] [29, 31, 3] [1, 32, 1] [30, 0, 3] [30, 31, 3] [1, 32, 1] [31, 0, 3] [31, 31, 3] [1, 32, 1] [24, 0, 4] [24, 31, 4] [1, 32, 1] [25, 0, 4] [25, 31, 4] [1, 32, 1] [26, 0, 4] [26, 31, 4] [1, 32, 1] [27, 0, 4] [27, 31, 4] [1, 32, 1] [28, 0, 4] [28, 31, 4] [1, 32, 1] [29, 0, 4] [29, 31, 4] [1, 32, 1] [30, 0, 4] [30, 31, 4] [1, 32, 1] [31, 0, 4] [31, 31, 4] [1, 32, 1] [24, 0, 5] [24, 31, 5] [1, 32, 1] [25, 0, 5] [25, 31, 5] [1, 32, 1] [26, 0, 5] [26, 31, 5] [1, 32, 1] [27, 0, 5] [27, 31, 5] [1, 32, 1] [28, 0, 5] [28, 31, 5] [1, 32, 1] [29, 0, 5] [29, 31, 5] [1, 32, 1] [30, 0, 5] [30, 31, 5] [1, 32, 1] [31, 0, 5] [31, 31, 5] [1, 32, 1] [24, 0, 6] [24, 31, 6] [1, 32, 1] [25, 0, 6] [25, 31, 6] [1, 32, 1] [26, 0, 6] [26, 31, 6] [1, 32, 1] [27, 0, 6] [27, 31, 6] [1, 32, 1] [28, 0, 6] [28, 31, 6] [1, 32, 1] [29, 0, 6] [29, 31, 6] [1, 32, 1] [30, 0, 6] [30, 31, 6] [1, 32, 1] [31, 0, 6] [31, 31, 6] [1, 32, 1] [24, 0, 7] [24, 31, 7] [1, 32, 1] [25, 0, 7] [25, 31, 7] [1, 32, 1] [26, 0, 7] [26, 31, 7] [1, 32, 1] [27, 0, 7] [27, 31, 7] [1, 32, 1] [28, 0, 7] [28, 31, 7] [1, 32, 1] [29, 0, 7] [29, 31, 7] [1, 32, 1] [30, 0, 7] [30, 31, 7] [1, 32, 1] [31, 0, 7] [31, 31, 7] [1, 32, 1] [0, 0, 8] [0, 31, 8] [1, 32, 1] [1, 0, 8] [1, 31, 8] [1, 32, 1] [2, 0, 8] [2, 31, 8] [1, 32, 1] [3, 0, 8] [3, 31, 8] [1, 32, 1] [4, 0, 8] [4, 31, 8] [1, 32, 1] [5, 0, 8] [5, 31, 8] [1, 32, 1] [6, 0, 8] [6, 31, 8] [1, 32, 1] [7, 0, 8] [7, 31, 8] [1, 32, 1] [0, 0, 9] [0, 31, 9] [1, 32, 1] [1, 0, 9] [1, 31, 9] [1, 32, 1] [2, 0, 9] [2, 31, 9] [1, 32, 1] [3, 0, 9] [3, 31, 9] [1, 32, 1] [4, 0, 9] [4, 31, 9] [1, 32, 1] [5, 0, 9] [5, 31, 9] [1, 32, 1] [6, 0, 9] [6, 31, 9] [1, 32, 1] [7, 0, 9] [7, 31, 9] [1, 32, 1] [0, 0, 10] [0, 31, 10] [1, 32, 1] [1, 0, 10] [1, 31, 10] [1, 32, 1] [2, 0, 10] [2, 31, 10] [1, 32, 1] [3, 0, 10] [3, 31, 10] [1, 32, 1] [4, 0, 10] [4, 31, 10] [1, 32, 1] [5, 0, 10] [5, 31, 10] [1, 32, 1] [6, 0, 10] [6, 31, 10] [1, 32, 1] [7, 0, 10] [7, 31, 10] [1, 32, 1] [0, 0, 11] [0, 31, 11] [1, 32, 1] [1, 0, 11] [1, 31, 11] [1, 32, 1] [2, 0, 11] [2, 31, 11] [1, 32, 1] [3, 0, 11] [3, 31, 11] [1, 32, 1] [4, 0, 11] [4, 31, 11] [1, 32, 1] [5, 0, 11] [5, 31, 11] [1, 32, 1] [6, 0, 11] [6, 31, 11] [1, 32, 1] [7, 0, 11] [7, 31, 11] [1, 32, 1] [0, 0, 12] [0, 31, 12] [1, 32, 1] [1, 0, 12] [1, 31, 12] [1, 32, 1] [2, 0, 12] [2, 31, 12] [1, 32, 1] [3, 0, 12] [3, 31, 12] [1, 32, 1] [4, 0, 12] [4, 31, 12] [1, 32, 1] [5, 0, 12] [5, 31, 12] [1, 32, 1] [6, 0, 12] [6, 31, 12] [1, 32, 1] [7, 0, 12] [7, 31, 12] [1, 32, 1] [0, 0, 13] [0, 31, 13] [1, 32, 1] [1, 0, 13] [1, 31, 13] [1, 32, 1] [2, 0, 13] [2, 31, 13] [1, 32, 1] [3, 0, 13] [3, 31, 13] [1, 32, 1] [4, 0, 13] [4, 31, 13] [1, 32, 1] [5, 0, 13] [5, 31, 13] [1, 32, 1] [6, 0, 13] [6, 31, 13] [1, 32, 1] [7, 0, 13] [7, 31, 13] [1, 32, 1] [0, 0, 14] [0, 31, 14] [1, 32, 1] [1, 0, 14] [1, 31, 14] [1, 32, 1] [2, 0, 14] [2, 31, 14] [1, 32, 1] [3, 0, 14] [3, 31, 14] [1, 32, 1] [4, 0, 14] [4, 31, 14] [1, 32, 1] [5, 0, 14] [5, 31, 14] [1, 32, 1] [6, 0, 14] [6, 31, 14] [1, 32, 1] [7, 0, 14] [7, 31, 14] [1, 32, 1] [0, 0, 15] [0, 31, 15] [1, 32, 1] [1, 0, 15] [1, 31, 15] [1, 32, 1] [2, 0, 15] [2, 31, 15] [1, 32, 1] [3, 0, 15] [3, 31, 15] [1, 32, 1] [4, 0, 15] [4, 31, 15] [1, 32, 1] [5, 0, 15] [5, 31, 15] [1, 32, 1] [6, 0, 15] [6, 31, 15] [1, 32, 1] [7, 0, 15] [7, 31, 15] [1, 32, 1] [8, 0, 8] [8, 31, 8] [1, 32, 1] [9, 0, 8] [9, 31, 8] [1, 32, 1] [10, 0, 8] [10, 31, 8] [1, 32, 1] [11, 0, 8] [11, 31, 8] [1, 32, 1] [12, 0, 8] [12, 31, 8] [1, 32, 1] [13, 0, 8] [13, 31, 8] [1, 32, 1] [14, 0, 8] [14, 31, 8] [1, 32, 1] [15, 0, 8] [15, 31, 8] [1, 32, 1] [8, 0, 9] [8, 31, 9] [1, 32, 1] [9, 0, 9] [9, 31, 9] [1, 32, 1] [10, 0, 9] [10, 31, 9] [1, 32, 1] [11, 0, 9] [11, 31, 9] [1, 32, 1] [12, 0, 9] [12, 31, 9] [1, 32, 1] [13, 0, 9] [13, 31, 9] [1, 32, 1] [14, 0, 9] [14, 31, 9] [1, 32, 1] [15, 0, 9] [15, 31, 9] [1, 32, 1] [8, 0, 10] [8, 31, 10] [1, 32, 1] [9, 0, 10] [9, 31, 10] [1, 32, 1] [10, 0, 10] [10, 31, 10] [1, 32, 1] [11, 0, 10] [11, 31, 10] [1, 32, 1] [12, 0, 10] [12, 31, 10] [1, 32, 1] [13, 0, 10] [13, 31, 10] [1, 32, 1] [14, 0, 10] [14, 31, 10] [1, 32, 1] [15, 0, 10] [15, 31, 10] [1, 32, 1] [8, 0, 11] [8, 31, 11] [1, 32, 1] [9, 0, 11] [9, 31, 11] [1, 32, 1] [10, 0, 11] [10, 31, 11] [1, 32, 1] [11, 0, 11] [11, 31, 11] [1, 32, 1] [12, 0, 11] [12, 31, 11] [1, 32, 1] [13, 0, 11] [13, 31, 11] [1, 32, 1] [14, 0, 11] [14, 31, 11] [1, 32, 1] [15, 0, 11] [15, 31, 11] [1, 32, 1] [8, 0, 12] [8, 31, 12] [1, 32, 1] [9, 0, 12] [9, 31, 12] [1, 32, 1] [10, 0, 12] [10, 31, 12] [1, 32, 1] [11, 0, 12] [11, 31, 12] [1, 32, 1] [12, 0, 12] [12, 31, 12] [1, 32, 1] [13, 0, 12] [13, 31, 12] [1, 32, 1] [14, 0, 12] [14, 31, 12] [1, 32, 1] [15, 0, 12] [15, 31, 12] [1, 32, 1] [8, 0, 13] [8, 31, 13] [1, 32, 1] [9, 0, 13] [9, 31, 13] [1, 32, 1] [10, 0, 13] [10, 31, 13] [1, 32, 1] [11, 0, 13] [11, 31, 13] [1, 32, 1] [12, 0, 13] [12, 31, 13] [1, 32, 1] [13, 0, 13] [13, 31, 13] [1, 32, 1] [14, 0, 13] [14, 31, 13] [1, 32, 1] [15, 0, 13] [15, 31, 13] [1, 32, 1] [8, 0, 14] [8, 31, 14] [1, 32, 1] [9, 0, 14] [9, 31, 14] [1, 32, 1] [10, 0, 14] [10, 31, 14] [1, 32, 1] [11, 0, 14] [11, 31, 14] [1, 32, 1] [12, 0, 14] [12, 31, 14] [1, 32, 1] [13, 0, 14] [13, 31, 14] [1, 32, 1] [14, 0, 14] [14, 31, 14] [1, 32, 1] [15, 0, 14] [15, 31, 14] [1, 32, 1] [8, 0, 15] [8, 31, 15] [1, 32, 1] [9, 0, 15] [9, 31, 15] [1, 32, 1] [10, 0, 15] [10, 31, 15] [1, 32, 1] [11, 0, 15] [11, 31, 15] [1, 32, 1] [12, 0, 15] [12, 31, 15] [1, 32, 1] [13, 0, 15] [13, 31, 15] [1, 32, 1] [14, 0, 15] [14, 31, 15] [1, 32, 1] [15, 0, 15] [15, 31, 15] [1, 32, 1] [16, 0, 8] [16, 31, 8] [1, 32, 1] [17, 0, 8] [17, 31, 8] [1, 32, 1] [18, 0, 8] [18, 31, 8] [1, 32, 1] [19, 0, 8] [19, 31, 8] [1, 32, 1] [20, 0, 8] [20, 31, 8] [1, 32, 1] [21, 0, 8] [21, 31, 8] [1, 32, 1] [22, 0, 8] [22, 31, 8] [1, 32, 1] [23, 0, 8] [23, 31, 8] [1, 32, 1] [16, 0, 9] [16, 31, 9] [1, 32, 1] [17, 0, 9] [17, 31, 9] [1, 32, 1] [18, 0, 9] [18, 31, 9] [1, 32, 1] [19, 0, 9] [19, 31, 9] [1, 32, 1] [20, 0, 9] [20, 31, 9] [1, 32, 1] [21, 0, 9] [21, 31, 9] [1, 32, 1] [22, 0, 9] [22, 31, 9] [1, 32, 1] [23, 0, 9] [23, 31, 9] [1, 32, 1] [16, 0, 10] [16, 31, 10] [1, 32, 1] [17, 0, 10] [17, 31, 10] [1, 32, 1] [18, 0, 10] [18, 31, 10] [1, 32, 1] [19, 0, 10] [19, 31, 10] [1, 32, 1] [20, 0, 10] [20, 31, 10] [1, 32, 1] [21, 0, 10] [21, 31, 10] [1, 32, 1] [22, 0, 10] [22, 31, 10] [1, 32, 1] [23, 0, 10] [23, 31, 10] [1, 32, 1] [16, 0, 11] [16, 31, 11] [1, 32, 1] [17, 0, 11] [17, 31, 11] [1, 32, 1] [18, 0, 11] [18, 31, 11] [1, 32, 1] [19, 0, 11] [19, 31, 11] [1, 32, 1] [20, 0, 11] [20, 31, 11] [1, 32, 1] [21, 0, 11] [21, 31, 11] [1, 32, 1] [22, 0, 11] [22, 31, 11] [1, 32, 1] [23, 0, 11] [23, 31, 11] [1, 32, 1] [16, 0, 12] [16, 31, 12] [1, 32, 1] [17, 0, 12] [17, 31, 12] [1, 32, 1] [18, 0, 12] [18, 31, 12] [1, 32, 1] [19, 0, 12] [19, 31, 12] [1, 32, 1] [20, 0, 12] [20, 31, 12] [1, 32, 1] [21, 0, 12] [21, 31, 12] [1, 32, 1] [22, 0, 12] [22, 31, 12] [1, 32, 1] [23, 0, 12] [23, 31, 12] [1, 32, 1] [16, 0, 13] [16, 31, 13] [1, 32, 1] [17, 0, 13] [17, 31, 13] [1, 32, 1] [18, 0, 13] [18, 31, 13] [1, 32, 1] [19, 0, 13] [19, 31, 13] [1, 32, 1] [20, 0, 13] [20, 31, 13] [1, 32, 1] [21, 0, 13] [21, 31, 13] [1, 32, 1] [22, 0, 13] [22, 31, 13] [1, 32, 1] [23, 0, 13] [23, 31, 13] [1, 32, 1] [16, 0, 14] [16, 31, 14] [1, 32, 1] [17, 0, 14] [17, 31, 14] [1, 32, 1] [18, 0, 14] [18, 31, 14] [1, 32, 1] [19, 0, 14] [19, 31, 14] [1, 32, 1] [20, 0, 14] [20, 31, 14] [1, 32, 1] [21, 0, 14] [21, 31, 14] [1, 32, 1] [22, 0, 14] [22, 31, 14] [1, 32, 1] [23, 0, 14] [23, 31, 14] [1, 32, 1] [16, 0, 15] [16, 31, 15] [1, 32, 1] [17, 0, 15] [17, 31, 15] [1, 32, 1] [18, 0, 15] [18, 31, 15] [1, 32, 1] [19, 0, 15] [19, 31, 15] [1, 32, 1] [20, 0, 15] [20, 31, 15] [1, 32, 1] [21, 0, 15] [21, 31, 15] [1, 32, 1] [22, 0, 15] [22, 31, 15] [1, 32, 1] [23, 0, 15] [23, 31, 15] [1, 32, 1] [24, 0, 8] [24, 31, 8] [1, 32, 1] [25, 0, 8] [25, 31, 8] [1, 32, 1] [26, 0, 8] [26, 31, 8] [1, 32, 1] [27, 0, 8] [27, 31, 8] [1, 32, 1] [28, 0, 8] [28, 31, 8] [1, 32, 1] [29, 0, 8] [29, 31, 8] [1, 32, 1] [30, 0, 8] [30, 31, 8] [1, 32, 1] [31, 0, 8] [31, 31, 8] [1, 32, 1] [24, 0, 9] [24, 31, 9] [1, 32, 1] [25, 0, 9] [25, 31, 9] [1, 32, 1] [26, 0, 9] [26, 31, 9] [1, 32, 1] [27, 0, 9] [27, 31, 9] [1, 32, 1] [28, 0, 9] [28, 31, 9] [1, 32, 1] [29, 0, 9] [29, 31, 9] [1, 32, 1] [30, 0, 9] [30, 31, 9] [1, 32, 1] [31, 0, 9] [31, 31, 9] [1, 32, 1] [24, 0, 10] [24, 31, 10] [1, 32, 1] [25, 0, 10] [25, 31, 10] [1, 32, 1] [26, 0, 10] [26, 31, 10] [1, 32, 1] [27, 0, 10] [27, 31, 10] [1, 32, 1] [28, 0, 10] [28, 31, 10] [1, 32, 1] [29, 0, 10] [29, 31, 10] [1, 32, 1] [30, 0, 10] [30, 31, 10] [1, 32, 1] [31, 0, 10] [31, 31, 10] [1, 32, 1] [24, 0, 11] [24, 31, 11] [1, 32, 1] [25, 0, 11] [25, 31, 11] [1, 32, 1] [26, 0, 11] [26, 31, 11] [1, 32, 1] [27, 0, 11] [27, 31, 11] [1, 32, 1] [28, 0, 11] [28, 31, 11] [1, 32, 1] [29, 0, 11] [29, 31, 11] [1, 32, 1] [30, 0, 11] [30, 31, 11] [1, 32, 1] [31, 0, 11] [31, 31, 11] [1, 32, 1] [24, 0, 12] [24, 31, 12] [1, 32, 1] [25, 0, 12] [25, 31, 12] [1, 32, 1] [26, 0, 12] [26, 31, 12] [1, 32, 1] [27, 0, 12] [27, 31, 12] [1, 32, 1] [28, 0, 12] [28, 31, 12] [1, 32, 1] [29, 0, 12] [29, 31, 12] [1, 32, 1] [30, 0, 12] [30, 31, 12] [1, 32, 1] [31, 0, 12] [31, 31, 12] [1, 32, 1] [24, 0, 13] [24, 31, 13] [1, 32, 1] [25, 0, 13] [25, 31, 13] [1, 32, 1] [26, 0, 13] [26, 31, 13] [1, 32, 1] [27, 0, 13] [27, 31, 13] [1, 32, 1] [28, 0, 13] [28, 31, 13] [1, 32, 1] [29, 0, 13] [29, 31, 13] [1, 32, 1] [30, 0, 13] [30, 31, 13] [1, 32, 1] [31, 0, 13] [31, 31, 13] [1, 32, 1] [24, 0, 14] [24, 31, 14] [1, 32, 1] [25, 0, 14] [25, 31, 14] [1, 32, 1] [26, 0, 14] [26, 31, 14] [1, 32, 1] [27, 0, 14] [27, 31, 14] [1, 32, 1] [28, 0, 14] [28, 31, 14] [1, 32, 1] [29, 0, 14] [29, 31, 14] [1, 32, 1] [30, 0, 14] [30, 31, 14] [1, 32, 1] [31, 0, 14] [31, 31, 14] [1, 32, 1] [24, 0, 15] [24, 31, 15] [1, 32, 1] [25, 0, 15] [25, 31, 15] [1, 32, 1] [26, 0, 15] [26, 31, 15] [1, 32, 1] [27, 0, 15] [27, 31, 15] [1, 32, 1] [28, 0, 15] [28, 31, 15] [1, 32, 1] [29, 0, 15] [29, 31, 15] [1, 32, 1] [30, 0, 15] [30, 31, 15] [1, 32, 1] [31, 0, 15] [31, 31, 15] [1, 32, 1] [0, 0, 16] [0, 31, 16] [1, 32, 1] [1, 0, 16] [1, 31, 16] [1, 32, 1] [2, 0, 16] [2, 31, 16] [1, 32, 1] [3, 0, 16] [3, 31, 16] [1, 32, 1] [4, 0, 16] [4, 31, 16] [1, 32, 1] [5, 0, 16] [5, 31, 16] [1, 32, 1] [6, 0, 16] [6, 31, 16] [1, 32, 1] [7, 0, 16] [7, 31, 16] [1, 32, 1] [0, 0, 17] [0, 31, 17] [1, 32, 1] [1, 0, 17] [1, 31, 17] [1, 32, 1] [2, 0, 17] [2, 31, 17] [1, 32, 1] [3, 0, 17] [3, 31, 17] [1, 32, 1] [4, 0, 17] [4, 31, 17] [1, 32, 1] [5, 0, 17] [5, 31, 17] [1, 32, 1] [6, 0, 17] [6, 31, 17] [1, 32, 1] [7, 0, 17] [7, 31, 17] [1, 32, 1] [0, 0, 18] [0, 31, 18] [1, 32, 1] [1, 0, 18] [1, 31, 18] [1, 32, 1] [2, 0, 18] [2, 31, 18] [1, 32, 1] [3, 0, 18] [3, 31, 18] [1, 32, 1] [4, 0, 18] [4, 31, 18] [1, 32, 1] [5, 0, 18] [5, 31, 18] [1, 32, 1] [6, 0, 18] [6, 31, 18] [1, 32, 1] [7, 0, 18] [7, 31, 18] [1, 32, 1] [0, 0, 19] [0, 31, 19] [1, 32, 1] [1, 0, 19] [1, 31, 19] [1, 32, 1] [2, 0, 19] [2, 31, 19] [1, 32, 1] [3, 0, 19] [3, 31, 19] [1, 32, 1] [4, 0, 19] [4, 31, 19] [1, 32, 1] [5, 0, 19] [5, 31, 19] [1, 32, 1] [6, 0, 19] [6, 31, 19] [1, 32, 1] [7, 0, 19] [7, 31, 19] [1, 32, 1] [0, 0, 20] [0, 31, 20] [1, 32, 1] [1, 0, 20] [1, 31, 20] [1, 32, 1] [2, 0, 20] [2, 31, 20] [1, 32, 1] [3, 0, 20] [3, 31, 20] [1, 32, 1] [4, 0, 20] [4, 31, 20] [1, 32, 1] [5, 0, 20] [5, 31, 20] [1, 32, 1] [6, 0, 20] [6, 31, 20] [1, 32, 1] [7, 0, 20] [7, 31, 20] [1, 32, 1] [0, 0, 21] [0, 31, 21] [1, 32, 1] [1, 0, 21] [1, 31, 21] [1, 32, 1] [2, 0, 21] [2, 31, 21] [1, 32, 1] [3, 0, 21] [3, 31, 21] [1, 32, 1] [4, 0, 21] [4, 31, 21] [1, 32, 1] [5, 0, 21] [5, 31, 21] [1, 32, 1] [6, 0, 21] [6, 31, 21] [1, 32, 1] [7, 0, 21] [7, 31, 21] [1, 32, 1] [0, 0, 22] [0, 31, 22] [1, 32, 1] [1, 0, 22] [1, 31, 22] [1, 32, 1] [2, 0, 22] [2, 31, 22] [1, 32, 1] [3, 0, 22] [3, 31, 22] [1, 32, 1] [4, 0, 22] [4, 31, 22] [1, 32, 1] [5, 0, 22] [5, 31, 22] [1, 32, 1] [6, 0, 22] [6, 31, 22] [1, 32, 1] [7, 0, 22] [7, 31, 22] [1, 32, 1] [0, 0, 23] [0, 31, 23] [1, 32, 1] [1, 0, 23] [1, 31, 23] [1, 32, 1] [2, 0, 23] [2, 31, 23] [1, 32, 1] [3, 0, 23] [3, 31, 23] [1, 32, 1] [4, 0, 23] [4, 31, 23] [1, 32, 1] [5, 0, 23] [5, 31, 23] [1, 32, 1] [6, 0, 23] [6, 31, 23] [1, 32, 1] [7, 0, 23] [7, 31, 23] [1, 32, 1] [8, 0, 16] [8, 31, 16] [1, 32, 1] [9, 0, 16] [9, 31, 16] [1, 32, 1] [10, 0, 16] [10, 31, 16] [1, 32, 1] [11, 0, 16] [11, 31, 16] [1, 32, 1] [12, 0, 16] [12, 31, 16] [1, 32, 1] [13, 0, 16] [13, 31, 16] [1, 32, 1] [14, 0, 16] [14, 31, 16] [1, 32, 1] [15, 0, 16] [15, 31, 16] [1, 32, 1] [8, 0, 17] [8, 31, 17] [1, 32, 1] [9, 0, 17] [9, 31, 17] [1, 32, 1] [10, 0, 17] [10, 31, 17] [1, 32, 1] [11, 0, 17] [11, 31, 17] [1, 32, 1] [12, 0, 17] [12, 31, 17] [1, 32, 1] [13, 0, 17] [13, 31, 17] [1, 32, 1] [14, 0, 17] [14, 31, 17] [1, 32, 1] [15, 0, 17] [15, 31, 17] [1, 32, 1] [8, 0, 18] [8, 31, 18] [1, 32, 1] [9, 0, 18] [9, 31, 18] [1, 32, 1] [10, 0, 18] [10, 31, 18] [1, 32, 1] [11, 0, 18] [11, 31, 18] [1, 32, 1] [12, 0, 18] [12, 31, 18] [1, 32, 1] [13, 0, 18] [13, 31, 18] [1, 32, 1] [14, 0, 18] [14, 31, 18] [1, 32, 1] [15, 0, 18] [15, 31, 18] [1, 32, 1] [8, 0, 19] [8, 31, 19] [1, 32, 1] [9, 0, 19] [9, 31, 19] [1, 32, 1] [10, 0, 19] [10, 31, 19] [1, 32, 1] [11, 0, 19] [11, 31, 19] [1, 32, 1] [12, 0, 19] [12, 31, 19] [1, 32, 1] [13, 0, 19] [13, 31, 19] [1, 32, 1] [14, 0, 19] [14, 31, 19] [1, 32, 1] [15, 0, 19] [15, 31, 19] [1, 32, 1] [8, 0, 20] [8, 31, 20] [1, 32, 1] [9, 0, 20] [9, 31, 20] [1, 32, 1] [10, 0, 20] [10, 31, 20] [1, 32, 1] [11, 0, 20] [11, 31, 20] [1, 32, 1] [12, 0, 20] [12, 31, 20] [1, 32, 1] [13, 0, 20] [13, 31, 20] [1, 32, 1] [14, 0, 20] [14, 31, 20] [1, 32, 1] [15, 0, 20] [15, 31, 20] [1, 32, 1] [8, 0, 21] [8, 31, 21] [1, 32, 1] [9, 0, 21] [9, 31, 21] [1, 32, 1] [10, 0, 21] [10, 31, 21] [1, 32, 1] [11, 0, 21] [11, 31, 21] [1, 32, 1] [12, 0, 21] [12, 31, 21] [1, 32, 1] [13, 0, 21] [13, 31, 21] [1, 32, 1] [14, 0, 21] [14, 31, 21] [1, 32, 1] [15, 0, 21] [15, 31, 21] [1, 32, 1] [8, 0, 22] [8, 31, 22] [1, 32, 1] [9, 0, 22] [9, 31, 22] [1, 32, 1] [10, 0, 22] [10, 31, 22] [1, 32, 1] [11, 0, 22] [11, 31, 22] [1, 32, 1] [12, 0, 22] [12, 31, 22] [1, 32, 1] [13, 0, 22] [13, 31, 22] [1, 32, 1] [14, 0, 22] [14, 31, 22] [1, 32, 1] [15, 0, 22] [15, 31, 22] [1, 32, 1] [8, 0, 23] [8, 31, 23] [1, 32, 1] [9, 0, 23] [9, 31, 23] [1, 32, 1] [10, 0, 23] [10, 31, 23] [1, 32, 1] [11, 0, 23] [11, 31, 23] [1, 32, 1] [12, 0, 23] [12, 31, 23] [1, 32, 1] [13, 0, 23] [13, 31, 23] [1, 32, 1] [14, 0, 23] [14, 31, 23] [1, 32, 1] [15, 0, 23] [15, 31, 23] [1, 32, 1] [16, 0, 16] [16, 31, 16] [1, 32, 1] [17, 0, 16] [17, 31, 16] [1, 32, 1] [18, 0, 16] [18, 31, 16] [1, 32, 1] [19, 0, 16] [19, 31, 16] [1, 32, 1] [20, 0, 16] [20, 31, 16] [1, 32, 1] [21, 0, 16] [21, 31, 16] [1, 32, 1] [22, 0, 16] [22, 31, 16] [1, 32, 1] [23, 0, 16] [23, 31, 16] [1, 32, 1] [16, 0, 17] [16, 31, 17] [1, 32, 1] [17, 0, 17] [17, 31, 17] [1, 32, 1] [18, 0, 17] [18, 31, 17] [1, 32, 1] [19, 0, 17] [19, 31, 17] [1, 32, 1] [20, 0, 17] [20, 31, 17] [1, 32, 1] [21, 0, 17] [21, 31, 17] [1, 32, 1] [22, 0, 17] [22, 31, 17] [1, 32, 1] [23, 0, 17] [23, 31, 17] [1, 32, 1] [16, 0, 18] [16, 31, 18] [1, 32, 1] [17, 0, 18] [17, 31, 18] [1, 32, 1] [18, 0, 18] [18, 31, 18] [1, 32, 1] [19, 0, 18] [19, 31, 18] [1, 32, 1] [20, 0, 18] [20, 31, 18] [1, 32, 1] [21, 0, 18] [21, 31, 18] [1, 32, 1] [22, 0, 18] [22, 31, 18] [1, 32, 1] [23, 0, 18] [23, 31, 18] [1, 32, 1] [16, 0, 19] [16, 31, 19] [1, 32, 1] [17, 0, 19] [17, 31, 19] [1, 32, 1] [18, 0, 19] [18, 31, 19] [1, 32, 1] [19, 0, 19] [19, 31, 19] [1, 32, 1] [20, 0, 19] [20, 31, 19] [1, 32, 1] [21, 0, 19] [21, 31, 19] [1, 32, 1] [22, 0, 19] [22, 31, 19] [1, 32, 1] [23, 0, 19] [23, 31, 19] [1, 32, 1] [16, 0, 20] [16, 31, 20] [1, 32, 1] [17, 0, 20] [17, 31, 20] [1, 32, 1] [18, 0, 20] [18, 31, 20] [1, 32, 1] [19, 0, 20] [19, 31, 20] [1, 32, 1] [20, 0, 20] [20, 31, 20] [1, 32, 1] [21, 0, 20] [21, 31, 20] [1, 32, 1] [22, 0, 20] [22, 31, 20] [1, 32, 1] [23, 0, 20] [23, 31, 20] [1, 32, 1] [16, 0, 21] [16, 31, 21] [1, 32, 1] [17, 0, 21] [17, 31, 21] [1, 32, 1] [18, 0, 21] [18, 31, 21] [1, 32, 1] [19, 0, 21] [19, 31, 21] [1, 32, 1] [20, 0, 21] [20, 31, 21] [1, 32, 1] [21, 0, 21] [21, 31, 21] [1, 32, 1] [22, 0, 21] [22, 31, 21] [1, 32, 1] [23, 0, 21] [23, 31, 21] [1, 32, 1] [16, 0, 22] [16, 31, 22] [1, 32, 1] [17, 0, 22] [17, 31, 22] [1, 32, 1] [18, 0, 22] [18, 31, 22] [1, 32, 1] [19, 0, 22] [19, 31, 22] [1, 32, 1] [20, 0, 22] [20, 31, 22] [1, 32, 1] [21, 0, 22] [21, 31, 22] [1, 32, 1] [22, 0, 22] [22, 31, 22] [1, 32, 1] [23, 0, 22] [23, 31, 22] [1, 32, 1] [16, 0, 23] [16, 31, 23] [1, 32, 1] [17, 0, 23] [17, 31, 23] [1, 32, 1] [18, 0, 23] [18, 31, 23] [1, 32, 1] [19, 0, 23] [19, 31, 23] [1, 32, 1] [20, 0, 23] [20, 31, 23] [1, 32, 1] [21, 0, 23] [21, 31, 23] [1, 32, 1] [22, 0, 23] [22, 31, 23] [1, 32, 1] [23, 0, 23] [23, 31, 23] [1, 32, 1] [24, 0, 16] [24, 31, 16] [1, 32, 1] [25, 0, 16] [25, 31, 16] [1, 32, 1] [26, 0, 16] [26, 31, 16] [1, 32, 1] [27, 0, 16] [27, 31, 16] [1, 32, 1] [28, 0, 16] [28, 31, 16] [1, 32, 1] [29, 0, 16] [29, 31, 16] [1, 32, 1] [30, 0, 16] [30, 31, 16] [1, 32, 1] [31, 0, 16] [31, 31, 16] [1, 32, 1] [24, 0, 17] [24, 31, 17] [1, 32, 1] [25, 0, 17] [25, 31, 17] [1, 32, 1] [26, 0, 17] [26, 31, 17] [1, 32, 1] [27, 0, 17] [27, 31, 17] [1, 32, 1] [28, 0, 17] [28, 31, 17] [1, 32, 1] [29, 0, 17] [29, 31, 17] [1, 32, 1] [30, 0, 17] [30, 31, 17] [1, 32, 1] [31, 0, 17] [31, 31, 17] [1, 32, 1] [24, 0, 18] [24, 31, 18] [1, 32, 1] [25, 0, 18] [25, 31, 18] [1, 32, 1] [26, 0, 18] [26, 31, 18] [1, 32, 1] [27, 0, 18] [27, 31, 18] [1, 32, 1] [28, 0, 18] [28, 31, 18] [1, 32, 1] [29, 0, 18] [29, 31, 18] [1, 32, 1] [30, 0, 18] [30, 31, 18] [1, 32, 1] [31, 0, 18] [31, 31, 18] [1, 32, 1] [24, 0, 19] [24, 31, 19] [1, 32, 1] [25, 0, 19] [25, 31, 19] [1, 32, 1] [26, 0, 19] [26, 31, 19] [1, 32, 1] [27, 0, 19] [27, 31, 19] [1, 32, 1] [28, 0, 19] [28, 31, 19] [1, 32, 1] [29, 0, 19] [29, 31, 19] [1, 32, 1] [30, 0, 19] [30, 31, 19] [1, 32, 1] [31, 0, 19] [31, 31, 19] [1, 32, 1] [24, 0, 20] [24, 31, 20] [1, 32, 1] [25, 0, 20] [25, 31, 20] [1, 32, 1] [26, 0, 20] [26, 31, 20] [1, 32, 1] [27, 0, 20] [27, 31, 20] [1, 32, 1] [28, 0, 20] [28, 31, 20] [1, 32, 1] [29, 0, 20] [29, 31, 20] [1, 32, 1] [30, 0, 20] [30, 31, 20] [1, 32, 1] [31, 0, 20] [31, 31, 20] [1, 32, 1] [24, 0, 21] [24, 31, 21] [1, 32, 1] [25, 0, 21] [25, 31, 21] [1, 32, 1] [26, 0, 21] [26, 31, 21] [1, 32, 1] [27, 0, 21] [27, 31, 21] [1, 32, 1] [28, 0, 21] [28, 31, 21] [1, 32, 1] [29, 0, 21] [29, 31, 21] [1, 32, 1] [30, 0, 21] [30, 31, 21] [1, 32, 1] [31, 0, 21] [31, 31, 21] [1, 32, 1] [24, 0, 22] [24, 31, 22] [1, 32, 1] [25, 0, 22] [25, 31, 22] [1, 32, 1] [26, 0, 22] [26, 31, 22] [1, 32, 1] [27, 0, 22] [27, 31, 22] [1, 32, 1] [28, 0, 22] [28, 31, 22] [1, 32, 1] [29, 0, 22] [29, 31, 22] [1, 32, 1] [30, 0, 22] [30, 31, 22] [1, 32, 1] [31, 0, 22] [31, 31, 22] [1, 32, 1] [24, 0, 23] [24, 31, 23] [1, 32, 1] [25, 0, 23] [25, 31, 23] [1, 32, 1] [26, 0, 23] [26, 31, 23] [1, 32, 1] [27, 0, 23] [27, 31, 23] [1, 32, 1] [28, 0, 23] [28, 31, 23] [1, 32, 1] [29, 0, 23] [29, 31, 23] [1, 32, 1] [30, 0, 23] [30, 31, 23] [1, 32, 1] [31, 0, 23] [31, 31, 23] [1, 32, 1] [0, 0, 24] [0, 31, 24] [1, 32, 1] [1, 0, 24] [1, 31, 24] [1, 32, 1] [2, 0, 24] [2, 31, 24] [1, 32, 1] [3, 0, 24] [3, 31, 24] [1, 32, 1] [4, 0, 24] [4, 31, 24] [1, 32, 1] [5, 0, 24] [5, 31, 24] [1, 32, 1] [6, 0, 24] [6, 31, 24] [1, 32, 1] [7, 0, 24] [7, 31, 24] [1, 32, 1] [0, 0, 25] [0, 31, 25] [1, 32, 1] [1, 0, 25] [1, 31, 25] [1, 32, 1] [2, 0, 25] [2, 31, 25] [1, 32, 1] [3, 0, 25] [3, 31, 25] [1, 32, 1] [4, 0, 25] [4, 31, 25] [1, 32, 1] [5, 0, 25] [5, 31, 25] [1, 32, 1] [6, 0, 25] [6, 31, 25] [1, 32, 1] [7, 0, 25] [7, 31, 25] [1, 32, 1] [0, 0, 26] [0, 31, 26] [1, 32, 1] [1, 0, 26] [1, 31, 26] [1, 32, 1] [2, 0, 26] [2, 31, 26] [1, 32, 1] [3, 0, 26] [3, 31, 26] [1, 32, 1] [4, 0, 26] [4, 31, 26] [1, 32, 1] [5, 0, 26] [5, 31, 26] [1, 32, 1] [6, 0, 26] [6, 31, 26] [1, 32, 1] [7, 0, 26] [7, 31, 26] [1, 32, 1] [0, 0, 27] [0, 31, 27] [1, 32, 1] [1, 0, 27] [1, 31, 27] [1, 32, 1] [2, 0, 27] [2, 31, 27] [1, 32, 1] [3, 0, 27] [3, 31, 27] [1, 32, 1] [4, 0, 27] [4, 31, 27] [1, 32, 1] [5, 0, 27] [5, 31, 27] [1, 32, 1] [6, 0, 27] [6, 31, 27] [1, 32, 1] [7, 0, 27] [7, 31, 27] [1, 32, 1] [0, 0, 28] [0, 31, 28] [1, 32, 1] [1, 0, 28] [1, 31, 28] [1, 32, 1] [2, 0, 28] [2, 31, 28] [1, 32, 1] [3, 0, 28] [3, 31, 28] [1, 32, 1] [4, 0, 28] [4, 31, 28] [1, 32, 1] [5, 0, 28] [5, 31, 28] [1, 32, 1] [6, 0, 28] [6, 31, 28] [1, 32, 1] [7, 0, 28] [7, 31, 28] [1, 32, 1] [0, 0, 29] [0, 31, 29] [1, 32, 1] [1, 0, 29] [1, 31, 29] [1, 32, 1] [2, 0, 29] [2, 31, 29] [1, 32, 1] [3, 0, 29] [3, 31, 29] [1, 32, 1] [4, 0, 29] [4, 31, 29] [1, 32, 1] [5, 0, 29] [5, 31, 29] [1, 32, 1] [6, 0, 29] [6, 31, 29] [1, 32, 1] [7, 0, 29] [7, 31, 29] [1, 32, 1] [0, 0, 30] [0, 31, 30] [1, 32, 1] [1, 0, 30] [1, 31, 30] [1, 32, 1] [2, 0, 30] [2, 31, 30] [1, 32, 1] [3, 0, 30] [3, 31, 30] [1, 32, 1] [4, 0, 30] [4, 31, 30] [1, 32, 1] [5, 0, 30] [5, 31, 30] [1, 32, 1] [6, 0, 30] [6, 31, 30] [1, 32, 1] [7, 0, 30] [7, 31, 30] [1, 32, 1] [0, 0, 31] [0, 31, 31] [1, 32, 1] [1, 0, 31] [1, 31, 31] [1, 32, 1] [2, 0, 31] [2, 31, 31] [1, 32, 1] [3, 0, 31] [3, 31, 31] [1, 32, 1] [4, 0, 31] [4, 31, 31] [1, 32, 1] [5, 0, 31] [5, 31, 31] [1, 32, 1] [6, 0, 31] [6, 31, 31] [1, 32, 1] [7, 0, 31] [7, 31, 31] [1, 32, 1] [8, 0, 24] [8, 31, 24] [1, 32, 1] [9, 0, 24] [9, 31, 24] [1, 32, 1] [10, 0, 24] [10, 31, 24] [1, 32, 1] [11, 0, 24] [11, 31, 24] [1, 32, 1] [12, 0, 24] [12, 31, 24] [1, 32, 1] [13, 0, 24] [13, 31, 24] [1, 32, 1] [14, 0, 24] [14, 31, 24] [1, 32, 1] [15, 0, 24] [15, 31, 24] [1, 32, 1] [8, 0, 25] [8, 31, 25] [1, 32, 1] [9, 0, 25] [9, 31, 25] [1, 32, 1] [10, 0, 25] [10, 31, 25] [1, 32, 1] [11, 0, 25] [11, 31, 25] [1, 32, 1] [12, 0, 25] [12, 31, 25] [1, 32, 1] [13, 0, 25] [13, 31, 25] [1, 32, 1] [14, 0, 25] [14, 31, 25] [1, 32, 1] [15, 0, 25] [15, 31, 25] [1, 32, 1] [8, 0, 26] [8, 31, 26] [1, 32, 1] [9, 0, 26] [9, 31, 26] [1, 32, 1] [10, 0, 26] [10, 31, 26] [1, 32, 1] [11, 0, 26] [11, 31, 26] [1, 32, 1] [12, 0, 26] [12, 31, 26] [1, 32, 1] [13, 0, 26] [13, 31, 26] [1, 32, 1] [14, 0, 26] [14, 31, 26] [1, 32, 1] [15, 0, 26] [15, 31, 26] [1, 32, 1] [8, 0, 27] [8, 31, 27] [1, 32, 1] [9, 0, 27] [9, 31, 27] [1, 32, 1] [10, 0, 27] [10, 31, 27] [1, 32, 1] [11, 0, 27] [11, 31, 27] [1, 32, 1] [12, 0, 27] [12, 31, 27] [1, 32, 1] [13, 0, 27] [13, 31, 27] [1, 32, 1] [14, 0, 27] [14, 31, 27] [1, 32, 1] [15, 0, 27] [15, 31, 27] [1, 32, 1] [8, 0, 28] [8, 31, 28] [1, 32, 1] [9, 0, 28] [9, 31, 28] [1, 32, 1] [10, 0, 28] [10, 31, 28] [1, 32, 1] [11, 0, 28] [11, 31, 28] [1, 32, 1] [12, 0, 28] [12, 31, 28] [1, 32, 1] [13, 0, 28] [13, 31, 28] [1, 32, 1] [14, 0, 28] [14, 31, 28] [1, 32, 1] [15, 0, 28] [15, 31, 28] [1, 32, 1] [8, 0, 29] [8, 31, 29] [1, 32, 1] [9, 0, 29] [9, 31, 29] [1, 32, 1] [10, 0, 29] [10, 31, 29] [1, 32, 1] [11, 0, 29] [11, 31, 29] [1, 32, 1] [12, 0, 29] [12, 31, 29] [1, 32, 1] [13, 0, 29] [13, 31, 29] [1, 32, 1] [14, 0, 29] [14, 31, 29] [1, 32, 1] [15, 0, 29] [15, 31, 29] [1, 32, 1] [8, 0, 30] [8, 31, 30] [1, 32, 1] [9, 0, 30] [9, 31, 30] [1, 32, 1] [10, 0, 30] [10, 31, 30] [1, 32, 1] [11, 0, 30] [11, 31, 30] [1, 32, 1] [12, 0, 30] [12, 31, 30] [1, 32, 1] [13, 0, 30] [13, 31, 30] [1, 32, 1] [14, 0, 30] [14, 31, 30] [1, 32, 1] [15, 0, 30] [15, 31, 30] [1, 32, 1] [8, 0, 31] [8, 31, 31] [1, 32, 1] [9, 0, 31] [9, 31, 31] [1, 32, 1] [10, 0, 31] [10, 31, 31] [1, 32, 1] [11, 0, 31] [11, 31, 31] [1, 32, 1] [12, 0, 31] [12, 31, 31] [1, 32, 1] [13, 0, 31] [13, 31, 31] [1, 32, 1] [14, 0, 31] [14, 31, 31] [1, 32, 1] [15, 0, 31] [15, 31, 31] [1, 32, 1] [16, 0, 24] [16, 31, 24] [1, 32, 1] [17, 0, 24] [17, 31, 24] [1, 32, 1] [18, 0, 24] [18, 31, 24] [1, 32, 1] [19, 0, 24] [19, 31, 24] [1, 32, 1] [20, 0, 24] [20, 31, 24] [1, 32, 1] [21, 0, 24] [21, 31, 24] [1, 32, 1] [22, 0, 24] [22, 31, 24] [1, 32, 1] [23, 0, 24] [23, 31, 24] [1, 32, 1] [16, 0, 25] [16, 31, 25] [1, 32, 1] [17, 0, 25] [17, 31, 25] [1, 32, 1] [18, 0, 25] [18, 31, 25] [1, 32, 1] [19, 0, 25] [19, 31, 25] [1, 32, 1] [20, 0, 25] [20, 31, 25] [1, 32, 1] [21, 0, 25] [21, 31, 25] [1, 32, 1] [22, 0, 25] [22, 31, 25] [1, 32, 1] [23, 0, 25] [23, 31, 25] [1, 32, 1] [16, 0, 26] [16, 31, 26] [1, 32, 1] [17, 0, 26] [17, 31, 26] [1, 32, 1] [18, 0, 26] [18, 31, 26] [1, 32, 1] [19, 0, 26] [19, 31, 26] [1, 32, 1] [20, 0, 26] [20, 31, 26] [1, 32, 1] [21, 0, 26] [21, 31, 26] [1, 32, 1] [22, 0, 26] [22, 31, 26] [1, 32, 1] [23, 0, 26] [23, 31, 26] [1, 32, 1] [16, 0, 27] [16, 31, 27] [1, 32, 1] [17, 0, 27] [17, 31, 27] [1, 32, 1] [18, 0, 27] [18, 31, 27] [1, 32, 1] [19, 0, 27] [19, 31, 27] [1, 32, 1] [20, 0, 27] [20, 31, 27] [1, 32, 1] [21, 0, 27] [21, 31, 27] [1, 32, 1] [22, 0, 27] [22, 31, 27] [1, 32, 1] [23, 0, 27] [23, 31, 27] [1, 32, 1] [16, 0, 28] [16, 31, 28] [1, 32, 1] [17, 0, 28] [17, 31, 28] [1, 32, 1] [18, 0, 28] [18, 31, 28] [1, 32, 1] [19, 0, 28] [19, 31, 28] [1, 32, 1] [20, 0, 28] [20, 31, 28] [1, 32, 1] [21, 0, 28] [21, 31, 28] [1, 32, 1] [22, 0, 28] [22, 31, 28] [1, 32, 1] [23, 0, 28] [23, 31, 28] [1, 32, 1] [16, 0, 29] [16, 31, 29] [1, 32, 1] [17, 0, 29] [17, 31, 29] [1, 32, 1] [18, 0, 29] [18, 31, 29] [1, 32, 1] [19, 0, 29] [19, 31, 29] [1, 32, 1] [20, 0, 29] [20, 31, 29] [1, 32, 1] [21, 0, 29] [21, 31, 29] [1, 32, 1] [22, 0, 29] [22, 31, 29] [1, 32, 1] [23, 0, 29] [23, 31, 29] [1, 32, 1] [16, 0, 30] [16, 31, 30] [1, 32, 1] [17, 0, 30] [17, 31, 30] [1, 32, 1] [18, 0, 30] [18, 31, 30] [1, 32, 1] [19, 0, 30] [19, 31, 30] [1, 32, 1] [20, 0, 30] [20, 31, 30] [1, 32, 1] [21, 0, 30] [21, 31, 30] [1, 32, 1] [22, 0, 30] [22, 31, 30] [1, 32, 1] [23, 0, 30] [23, 31, 30] [1, 32, 1] [16, 0, 31] [16, 31, 31] [1, 32, 1] [17, 0, 31] [17, 31, 31] [1, 32, 1] [18, 0, 31] [18, 31, 31] [1, 32, 1] [19, 0, 31] [19, 31, 31] [1, 32, 1] [20, 0, 31] [20, 31, 31] [1, 32, 1] [21, 0, 31] [21, 31, 31] [1, 32, 1] [22, 0, 31] [22, 31, 31] [1, 32, 1] [23, 0, 31] [23, 31, 31] [1, 32, 1] [24, 0, 24] [24, 31, 24] [1, 32, 1] [25, 0, 24] [25, 31, 24] [1, 32, 1] [26, 0, 24] [26, 31, 24] [1, 32, 1] [27, 0, 24] [27, 31, 24] [1, 32, 1] [28, 0, 24] [28, 31, 24] [1, 32, 1] [29, 0, 24] [29, 31, 24] [1, 32, 1] [30, 0, 24] [30, 31, 24] [1, 32, 1] [31, 0, 24] [31, 31, 24] [1, 32, 1] [24, 0, 25] [24, 31, 25] [1, 32, 1] [25, 0, 25] [25, 31, 25] [1, 32, 1] [26, 0, 25] [26, 31, 25] [1, 32, 1] [27, 0, 25] [27, 31, 25] [1, 32, 1] [28, 0, 25] [28, 31, 25] [1, 32, 1] [29, 0, 25] [29, 31, 25] [1, 32, 1] [30, 0, 25] [30, 31, 25] [1, 32, 1] [31, 0, 25] [31, 31, 25] [1, 32, 1] [24, 0, 26] [24, 31, 26] [1, 32, 1] [25, 0, 26] [25, 31, 26] [1, 32, 1] [26, 0, 26] [26, 31, 26] [1, 32, 1] [27, 0, 26] [27, 31, 26] [1, 32, 1] [28, 0, 26] [28, 31, 26] [1, 32, 1] [29, 0, 26] [29, 31, 26] [1, 32, 1] [30, 0, 26] [30, 31, 26] [1, 32, 1] [31, 0, 26] [31, 31, 26] [1, 32, 1] [24, 0, 27] [24, 31, 27] [1, 32, 1] [25, 0, 27] [25, 31, 27] [1, 32, 1] [26, 0, 27] [26, 31, 27] [1, 32, 1] [27, 0, 27] [27, 31, 27] [1, 32, 1] [28, 0, 27] [28, 31, 27] [1, 32, 1] [29, 0, 27] [29, 31, 27] [1, 32, 1] [30, 0, 27] [30, 31, 27] [1, 32, 1] [31, 0, 27] [31, 31, 27] [1, 32, 1] [24, 0, 28] [24, 31, 28] [1, 32, 1] [25, 0, 28] [25, 31, 28] [1, 32, 1] [26, 0, 28] [26, 31, 28] [1, 32, 1] [27, 0, 28] [27, 31, 28] [1, 32, 1] [28, 0, 28] [28, 31, 28] [1, 32, 1] [29, 0, 28] [29, 31, 28] [1, 32, 1] [30, 0, 28] [30, 31, 28] [1, 32, 1] [31, 0, 28] [31, 31, 28] [1, 32, 1] [24, 0, 29] [24, 31, 29] [1, 32, 1] [25, 0, 29] [25, 31, 29] [1, 32, 1] [26, 0, 29] [26, 31, 29] [1, 32, 1] [27, 0, 29] [27, 31, 29] [1, 32, 1] [28, 0, 29] [28, 31, 29] [1, 32, 1] [29, 0, 29] [29, 31, 29] [1, 32, 1] [30, 0, 29] [30, 31, 29] [1, 32, 1] [31, 0, 29] [31, 31, 29] [1, 32, 1] [24, 0, 30] [24, 31, 30] [1, 32, 1] [25, 0, 30] [25, 31, 30] [1, 32, 1] [26, 0, 30] [26, 31, 30] [1, 32, 1] [27, 0, 30] [27, 31, 30] [1, 32, 1] [28, 0, 30] [28, 31, 30] [1, 32, 1] [29, 0, 30] [29, 31, 30] [1, 32, 1] [30, 0, 30] [30, 31, 30] [1, 32, 1] [31, 0, 30] [31, 31, 30] [1, 32, 1] [24, 0, 31] [24, 31, 31] [1, 32, 1] [25, 0, 31] [25, 31, 31] [1, 32, 1] [26, 0, 31] [26, 31, 31] [1, 32, 1] [27, 0, 31] [27, 31, 31] [1, 32, 1] [28, 0, 31] [28, 31, 31] [1, 32, 1] [29, 0, 31] [29, 31, 31] [1, 32, 1] [30, 0, 31] [30, 31, 31] [1, 32, 1] [31, 0, 31] [31, 31, 31] [1, 32, 1] nsteps = 1024 [31, 0, 31] [31, 31, 31] [1, 32, 1] [30, 0, 31] [30, 31, 31] [1, 32, 1] [29, 0, 31] [29, 31, 31] [1, 32, 1] [28, 0, 31] [28, 31, 31] [1, 32, 1] [27, 0, 31] [27, 31, 31] [1, 32, 1] [26, 0, 31] [26, 31, 31] [1, 32, 1] [25, 0, 31] [25, 31, 31] [1, 32, 1] [24, 0, 31] [24, 31, 31] [1, 32, 1] [31, 0, 30] [31, 31, 30] [1, 32, 1] [30, 0, 30] [30, 31, 30] [1, 32, 1] [29, 0, 30] [29, 31, 30] [1, 32, 1] [28, 0, 30] [28, 31, 30] [1, 32, 1] [27, 0, 30] [27, 31, 30] [1, 32, 1] [26, 0, 30] [26, 31, 30] [1, 32, 1] [25, 0, 30] [25, 31, 30] [1, 32, 1] [24, 0, 30] [24, 31, 30] [1, 32, 1] [31, 0, 29] [31, 31, 29] [1, 32, 1] [30, 0, 29] [30, 31, 29] [1, 32, 1] [29, 0, 29] [29, 31, 29] [1, 32, 1] [28, 0, 29] [28, 31, 29] [1, 32, 1] [27, 0, 29] [27, 31, 29] [1, 32, 1] [26, 0, 29] [26, 31, 29] [1, 32, 1] [25, 0, 29] [25, 31, 29] [1, 32, 1] [24, 0, 29] [24, 31, 29] [1, 32, 1] [31, 0, 28] [31, 31, 28] [1, 32, 1] [30, 0, 28] [30, 31, 28] [1, 32, 1] [29, 0, 28] [29, 31, 28] [1, 32, 1] [28, 0, 28] [28, 31, 28] [1, 32, 1] [27, 0, 28] [27, 31, 28] [1, 32, 1] [26, 0, 28] [26, 31, 28] [1, 32, 1] [25, 0, 28] [25, 31, 28] [1, 32, 1] [24, 0, 28] [24, 31, 28] [1, 32, 1] [31, 0, 27] [31, 31, 27] [1, 32, 1] [30, 0, 27] [30, 31, 27] [1, 32, 1] [29, 0, 27] [29, 31, 27] [1, 32, 1] [28, 0, 27] [28, 31, 27] [1, 32, 1] [27, 0, 27] [27, 31, 27] [1, 32, 1] [26, 0, 27] [26, 31, 27] [1, 32, 1] [25, 0, 27] [25, 31, 27] [1, 32, 1] [24, 0, 27] [24, 31, 27] [1, 32, 1] [31, 0, 26] [31, 31, 26] [1, 32, 1] [30, 0, 26] [30, 31, 26] [1, 32, 1] [29, 0, 26] [29, 31, 26] [1, 32, 1] [28, 0, 26] [28, 31, 26] [1, 32, 1] [27, 0, 26] [27, 31, 26] [1, 32, 1] [26, 0, 26] [26, 31, 26] [1, 32, 1] [25, 0, 26] [25, 31, 26] [1, 32, 1] [24, 0, 26] [24, 31, 26] [1, 32, 1] [31, 0, 25] [31, 31, 25] [1, 32, 1] [30, 0, 25] [30, 31, 25] [1, 32, 1] [29, 0, 25] [29, 31, 25] [1, 32, 1] [28, 0, 25] [28, 31, 25] [1, 32, 1] [27, 0, 25] [27, 31, 25] [1, 32, 1] [26, 0, 25] [26, 31, 25] [1, 32, 1] [25, 0, 25] [25, 31, 25] [1, 32, 1] [24, 0, 25] [24, 31, 25] [1, 32, 1] [31, 0, 24] [31, 31, 24] [1, 32, 1] [30, 0, 24] [30, 31, 24] [1, 32, 1] [29, 0, 24] [29, 31, 24] [1, 32, 1] [28, 0, 24] [28, 31, 24] [1, 32, 1] [27, 0, 24] [27, 31, 24] [1, 32, 1] [26, 0, 24] [26, 31, 24] [1, 32, 1] [25, 0, 24] [25, 31, 24] [1, 32, 1] [24, 0, 24] [24, 31, 24] [1, 32, 1] [23, 0, 31] [23, 31, 31] [1, 32, 1] [22, 0, 31] [22, 31, 31] [1, 32, 1] [21, 0, 31] [21, 31, 31] [1, 32, 1] [20, 0, 31] [20, 31, 31] [1, 32, 1] [19, 0, 31] [19, 31, 31] [1, 32, 1] [18, 0, 31] [18, 31, 31] [1, 32, 1] [17, 0, 31] [17, 31, 31] [1, 32, 1] [16, 0, 31] [16, 31, 31] [1, 32, 1] [23, 0, 30] [23, 31, 30] [1, 32, 1] [22, 0, 30] [22, 31, 30] [1, 32, 1] [21, 0, 30] [21, 31, 30] [1, 32, 1] [20, 0, 30] [20, 31, 30] [1, 32, 1] [19, 0, 30] [19, 31, 30] [1, 32, 1] [18, 0, 30] [18, 31, 30] [1, 32, 1] [17, 0, 30] [17, 31, 30] [1, 32, 1] [16, 0, 30] [16, 31, 30] [1, 32, 1] [23, 0, 29] [23, 31, 29] [1, 32, 1] [22, 0, 29] [22, 31, 29] [1, 32, 1] [21, 0, 29] [21, 31, 29] [1, 32, 1] [20, 0, 29] [20, 31, 29] [1, 32, 1] [19, 0, 29] [19, 31, 29] [1, 32, 1] [18, 0, 29] [18, 31, 29] [1, 32, 1] [17, 0, 29] [17, 31, 29] [1, 32, 1] [16, 0, 29] [16, 31, 29] [1, 32, 1] [23, 0, 28] [23, 31, 28] [1, 32, 1] [22, 0, 28] [22, 31, 28] [1, 32, 1] [21, 0, 28] [21, 31, 28] [1, 32, 1] [20, 0, 28] [20, 31, 28] [1, 32, 1] [19, 0, 28] [19, 31, 28] [1, 32, 1] [18, 0, 28] [18, 31, 28] [1, 32, 1] [17, 0, 28] [17, 31, 28] [1, 32, 1] [16, 0, 28] [16, 31, 28] [1, 32, 1] [23, 0, 27] [23, 31, 27] [1, 32, 1] [22, 0, 27] [22, 31, 27] [1, 32, 1] [21, 0, 27] [21, 31, 27] [1, 32, 1] [20, 0, 27] [20, 31, 27] [1, 32, 1] [19, 0, 27] [19, 31, 27] [1, 32, 1] [18, 0, 27] [18, 31, 27] [1, 32, 1] [17, 0, 27] [17, 31, 27] [1, 32, 1] [16, 0, 27] [16, 31, 27] [1, 32, 1] [23, 0, 26] [23, 31, 26] [1, 32, 1] [22, 0, 26] [22, 31, 26] [1, 32, 1] [21, 0, 26] [21, 31, 26] [1, 32, 1] [20, 0, 26] [20, 31, 26] [1, 32, 1] [19, 0, 26] [19, 31, 26] [1, 32, 1] [18, 0, 26] [18, 31, 26] [1, 32, 1] [17, 0, 26] [17, 31, 26] [1, 32, 1] [16, 0, 26] [16, 31, 26] [1, 32, 1] [23, 0, 25] [23, 31, 25] [1, 32, 1] [22, 0, 25] [22, 31, 25] [1, 32, 1] [21, 0, 25] [21, 31, 25] [1, 32, 1] [20, 0, 25] [20, 31, 25] [1, 32, 1] [19, 0, 25] [19, 31, 25] [1, 32, 1] [18, 0, 25] [18, 31, 25] [1, 32, 1] [17, 0, 25] [17, 31, 25] [1, 32, 1] [16, 0, 25] [16, 31, 25] [1, 32, 1] [23, 0, 24] [23, 31, 24] [1, 32, 1] [22, 0, 24] [22, 31, 24] [1, 32, 1] [21, 0, 24] [21, 31, 24] [1, 32, 1] [20, 0, 24] [20, 31, 24] [1, 32, 1] [19, 0, 24] [19, 31, 24] [1, 32, 1] [18, 0, 24] [18, 31, 24] [1, 32, 1] [17, 0, 24] [17, 31, 24] [1, 32, 1] [16, 0, 24] [16, 31, 24] [1, 32, 1] [15, 0, 31] [15, 31, 31] [1, 32, 1] [14, 0, 31] [14, 31, 31] [1, 32, 1] [13, 0, 31] [13, 31, 31] [1, 32, 1] [12, 0, 31] [12, 31, 31] [1, 32, 1] [11, 0, 31] [11, 31, 31] [1, 32, 1] [10, 0, 31] [10, 31, 31] [1, 32, 1] [9, 0, 31] [9, 31, 31] [1, 32, 1] [8, 0, 31] [8, 31, 31] [1, 32, 1] [15, 0, 30] [15, 31, 30] [1, 32, 1] [14, 0, 30] [14, 31, 30] [1, 32, 1] [13, 0, 30] [13, 31, 30] [1, 32, 1] [12, 0, 30] [12, 31, 30] [1, 32, 1] [11, 0, 30] [11, 31, 30] [1, 32, 1] [10, 0, 30] [10, 31, 30] [1, 32, 1] [9, 0, 30] [9, 31, 30] [1, 32, 1] [8, 0, 30] [8, 31, 30] [1, 32, 1] [15, 0, 29] [15, 31, 29] [1, 32, 1] [14, 0, 29] [14, 31, 29] [1, 32, 1] [13, 0, 29] [13, 31, 29] [1, 32, 1] [12, 0, 29] [12, 31, 29] [1, 32, 1] [11, 0, 29] [11, 31, 29] [1, 32, 1] [10, 0, 29] [10, 31, 29] [1, 32, 1] [9, 0, 29] [9, 31, 29] [1, 32, 1] [8, 0, 29] [8, 31, 29] [1, 32, 1] [15, 0, 28] [15, 31, 28] [1, 32, 1] [14, 0, 28] [14, 31, 28] [1, 32, 1] [13, 0, 28] [13, 31, 28] [1, 32, 1] [12, 0, 28] [12, 31, 28] [1, 32, 1] [11, 0, 28] [11, 31, 28] [1, 32, 1] [10, 0, 28] [10, 31, 28] [1, 32, 1] [9, 0, 28] [9, 31, 28] [1, 32, 1] [8, 0, 28] [8, 31, 28] [1, 32, 1] [15, 0, 27] [15, 31, 27] [1, 32, 1] [14, 0, 27] [14, 31, 27] [1, 32, 1] [13, 0, 27] [13, 31, 27] [1, 32, 1] [12, 0, 27] [12, 31, 27] [1, 32, 1] [11, 0, 27] [11, 31, 27] [1, 32, 1] [10, 0, 27] [10, 31, 27] [1, 32, 1] [9, 0, 27] [9, 31, 27] [1, 32, 1] [8, 0, 27] [8, 31, 27] [1, 32, 1] [15, 0, 26] [15, 31, 26] [1, 32, 1] [14, 0, 26] [14, 31, 26] [1, 32, 1] [13, 0, 26] [13, 31, 26] [1, 32, 1] [12, 0, 26] [12, 31, 26] [1, 32, 1] [11, 0, 26] [11, 31, 26] [1, 32, 1] [10, 0, 26] [10, 31, 26] [1, 32, 1] [9, 0, 26] [9, 31, 26] [1, 32, 1] [8, 0, 26] [8, 31, 26] [1, 32, 1] [15, 0, 25] [15, 31, 25] [1, 32, 1] [14, 0, 25] [14, 31, 25] [1, 32, 1] [13, 0, 25] [13, 31, 25] [1, 32, 1] [12, 0, 25] [12, 31, 25] [1, 32, 1] [11, 0, 25] [11, 31, 25] [1, 32, 1] [10, 0, 25] [10, 31, 25] [1, 32, 1] [9, 0, 25] [9, 31, 25] [1, 32, 1] [8, 0, 25] [8, 31, 25] [1, 32, 1] [15, 0, 24] [15, 31, 24] [1, 32, 1] [14, 0, 24] [14, 31, 24] [1, 32, 1] [13, 0, 24] [13, 31, 24] [1, 32, 1] [12, 0, 24] [12, 31, 24] [1, 32, 1] [11, 0, 24] [11, 31, 24] [1, 32, 1] [10, 0, 24] [10, 31, 24] [1, 32, 1] [9, 0, 24] [9, 31, 24] [1, 32, 1] [8, 0, 24] [8, 31, 24] [1, 32, 1] [7, 0, 31] [7, 31, 31] [1, 32, 1] [6, 0, 31] [6, 31, 31] [1, 32, 1] [5, 0, 31] [5, 31, 31] [1, 32, 1] [4, 0, 31] [4, 31, 31] [1, 32, 1] [3, 0, 31] [3, 31, 31] [1, 32, 1] [2, 0, 31] [2, 31, 31] [1, 32, 1] [1, 0, 31] [1, 31, 31] [1, 32, 1] [0, 0, 31] [0, 31, 31] [1, 32, 1] [7, 0, 30] [7, 31, 30] [1, 32, 1] [6, 0, 30] [6, 31, 30] [1, 32, 1] [5, 0, 30] [5, 31, 30] [1, 32, 1] [4, 0, 30] [4, 31, 30] [1, 32, 1] [3, 0, 30] [3, 31, 30] [1, 32, 1] [2, 0, 30] [2, 31, 30] [1, 32, 1] [1, 0, 30] [1, 31, 30] [1, 32, 1] [0, 0, 30] [0, 31, 30] [1, 32, 1] [7, 0, 29] [7, 31, 29] [1, 32, 1] [6, 0, 29] [6, 31, 29] [1, 32, 1] [5, 0, 29] [5, 31, 29] [1, 32, 1] [4, 0, 29] [4, 31, 29] [1, 32, 1] [3, 0, 29] [3, 31, 29] [1, 32, 1] [2, 0, 29] [2, 31, 29] [1, 32, 1] [1, 0, 29] [1, 31, 29] [1, 32, 1] [0, 0, 29] [0, 31, 29] [1, 32, 1] [7, 0, 28] [7, 31, 28] [1, 32, 1] [6, 0, 28] [6, 31, 28] [1, 32, 1] [5, 0, 28] [5, 31, 28] [1, 32, 1] [4, 0, 28] [4, 31, 28] [1, 32, 1] [3, 0, 28] [3, 31, 28] [1, 32, 1] [2, 0, 28] [2, 31, 28] [1, 32, 1] [1, 0, 28] [1, 31, 28] [1, 32, 1] [0, 0, 28] [0, 31, 28] [1, 32, 1] [7, 0, 27] [7, 31, 27] [1, 32, 1] [6, 0, 27] [6, 31, 27] [1, 32, 1] [5, 0, 27] [5, 31, 27] [1, 32, 1] [4, 0, 27] [4, 31, 27] [1, 32, 1] [3, 0, 27] [3, 31, 27] [1, 32, 1] [2, 0, 27] [2, 31, 27] [1, 32, 1] [1, 0, 27] [1, 31, 27] [1, 32, 1] [0, 0, 27] [0, 31, 27] [1, 32, 1] [7, 0, 26] [7, 31, 26] [1, 32, 1] [6, 0, 26] [6, 31, 26] [1, 32, 1] [5, 0, 26] [5, 31, 26] [1, 32, 1] [4, 0, 26] [4, 31, 26] [1, 32, 1] [3, 0, 26] [3, 31, 26] [1, 32, 1] [2, 0, 26] [2, 31, 26] [1, 32, 1] [1, 0, 26] [1, 31, 26] [1, 32, 1] [0, 0, 26] [0, 31, 26] [1, 32, 1] [7, 0, 25] [7, 31, 25] [1, 32, 1] [6, 0, 25] [6, 31, 25] [1, 32, 1] [5, 0, 25] [5, 31, 25] [1, 32, 1] [4, 0, 25] [4, 31, 25] [1, 32, 1] [3, 0, 25] [3, 31, 25] [1, 32, 1] [2, 0, 25] [2, 31, 25] [1, 32, 1] [1, 0, 25] [1, 31, 25] [1, 32, 1] [0, 0, 25] [0, 31, 25] [1, 32, 1] [7, 0, 24] [7, 31, 24] [1, 32, 1] [6, 0, 24] [6, 31, 24] [1, 32, 1] [5, 0, 24] [5, 31, 24] [1, 32, 1] [4, 0, 24] [4, 31, 24] [1, 32, 1] [3, 0, 24] [3, 31, 24] [1, 32, 1] [2, 0, 24] [2, 31, 24] [1, 32, 1] [1, 0, 24] [1, 31, 24] [1, 32, 1] [0, 0, 24] [0, 31, 24] [1, 32, 1] [31, 0, 23] [31, 31, 23] [1, 32, 1] [30, 0, 23] [30, 31, 23] [1, 32, 1] [29, 0, 23] [29, 31, 23] [1, 32, 1] [28, 0, 23] [28, 31, 23] [1, 32, 1] [27, 0, 23] [27, 31, 23] [1, 32, 1] [26, 0, 23] [26, 31, 23] [1, 32, 1] [25, 0, 23] [25, 31, 23] [1, 32, 1] [24, 0, 23] [24, 31, 23] [1, 32, 1] [31, 0, 22] [31, 31, 22] [1, 32, 1] [30, 0, 22] [30, 31, 22] [1, 32, 1] [29, 0, 22] [29, 31, 22] [1, 32, 1] [28, 0, 22] [28, 31, 22] [1, 32, 1] [27, 0, 22] [27, 31, 22] [1, 32, 1] [26, 0, 22] [26, 31, 22] [1, 32, 1] [25, 0, 22] [25, 31, 22] [1, 32, 1] [24, 0, 22] [24, 31, 22] [1, 32, 1] [31, 0, 21] [31, 31, 21] [1, 32, 1] [30, 0, 21] [30, 31, 21] [1, 32, 1] [29, 0, 21] [29, 31, 21] [1, 32, 1] [28, 0, 21] [28, 31, 21] [1, 32, 1] [27, 0, 21] [27, 31, 21] [1, 32, 1] [26, 0, 21] [26, 31, 21] [1, 32, 1] [25, 0, 21] [25, 31, 21] [1, 32, 1] [24, 0, 21] [24, 31, 21] [1, 32, 1] [31, 0, 20] [31, 31, 20] [1, 32, 1] [30, 0, 20] [30, 31, 20] [1, 32, 1] [29, 0, 20] [29, 31, 20] [1, 32, 1] [28, 0, 20] [28, 31, 20] [1, 32, 1] [27, 0, 20] [27, 31, 20] [1, 32, 1] [26, 0, 20] [26, 31, 20] [1, 32, 1] [25, 0, 20] [25, 31, 20] [1, 32, 1] [24, 0, 20] [24, 31, 20] [1, 32, 1] [31, 0, 19] [31, 31, 19] [1, 32, 1] [30, 0, 19] [30, 31, 19] [1, 32, 1] [29, 0, 19] [29, 31, 19] [1, 32, 1] [28, 0, 19] [28, 31, 19] [1, 32, 1] [27, 0, 19] [27, 31, 19] [1, 32, 1] [26, 0, 19] [26, 31, 19] [1, 32, 1] [25, 0, 19] [25, 31, 19] [1, 32, 1] [24, 0, 19] [24, 31, 19] [1, 32, 1] [31, 0, 18] [31, 31, 18] [1, 32, 1] [30, 0, 18] [30, 31, 18] [1, 32, 1] [29, 0, 18] [29, 31, 18] [1, 32, 1] [28, 0, 18] [28, 31, 18] [1, 32, 1] [27, 0, 18] [27, 31, 18] [1, 32, 1] [26, 0, 18] [26, 31, 18] [1, 32, 1] [25, 0, 18] [25, 31, 18] [1, 32, 1] [24, 0, 18] [24, 31, 18] [1, 32, 1] [31, 0, 17] [31, 31, 17] [1, 32, 1] [30, 0, 17] [30, 31, 17] [1, 32, 1] [29, 0, 17] [29, 31, 17] [1, 32, 1] [28, 0, 17] [28, 31, 17] [1, 32, 1] [27, 0, 17] [27, 31, 17] [1, 32, 1] [26, 0, 17] [26, 31, 17] [1, 32, 1] [25, 0, 17] [25, 31, 17] [1, 32, 1] [24, 0, 17] [24, 31, 17] [1, 32, 1] [31, 0, 16] [31, 31, 16] [1, 32, 1] [30, 0, 16] [30, 31, 16] [1, 32, 1] [29, 0, 16] [29, 31, 16] [1, 32, 1] [28, 0, 16] [28, 31, 16] [1, 32, 1] [27, 0, 16] [27, 31, 16] [1, 32, 1] [26, 0, 16] [26, 31, 16] [1, 32, 1] [25, 0, 16] [25, 31, 16] [1, 32, 1] [24, 0, 16] [24, 31, 16] [1, 32, 1] [23, 0, 23] [23, 31, 23] [1, 32, 1] [22, 0, 23] [22, 31, 23] [1, 32, 1] [21, 0, 23] [21, 31, 23] [1, 32, 1] [20, 0, 23] [20, 31, 23] [1, 32, 1] [19, 0, 23] [19, 31, 23] [1, 32, 1] [18, 0, 23] [18, 31, 23] [1, 32, 1] [17, 0, 23] [17, 31, 23] [1, 32, 1] [16, 0, 23] [16, 31, 23] [1, 32, 1] [23, 0, 22] [23, 31, 22] [1, 32, 1] [22, 0, 22] [22, 31, 22] [1, 32, 1] [21, 0, 22] [21, 31, 22] [1, 32, 1] [20, 0, 22] [20, 31, 22] [1, 32, 1] [19, 0, 22] [19, 31, 22] [1, 32, 1] [18, 0, 22] [18, 31, 22] [1, 32, 1] [17, 0, 22] [17, 31, 22] [1, 32, 1] [16, 0, 22] [16, 31, 22] [1, 32, 1] [23, 0, 21] [23, 31, 21] [1, 32, 1] [22, 0, 21] [22, 31, 21] [1, 32, 1] [21, 0, 21] [21, 31, 21] [1, 32, 1] [20, 0, 21] [20, 31, 21] [1, 32, 1] [19, 0, 21] [19, 31, 21] [1, 32, 1] [18, 0, 21] [18, 31, 21] [1, 32, 1] [17, 0, 21] [17, 31, 21] [1, 32, 1] [16, 0, 21] [16, 31, 21] [1, 32, 1] [23, 0, 20] [23, 31, 20] [1, 32, 1] [22, 0, 20] [22, 31, 20] [1, 32, 1] [21, 0, 20] [21, 31, 20] [1, 32, 1] [20, 0, 20] [20, 31, 20] [1, 32, 1] [19, 0, 20] [19, 31, 20] [1, 32, 1] [18, 0, 20] [18, 31, 20] [1, 32, 1] [17, 0, 20] [17, 31, 20] [1, 32, 1] [16, 0, 20] [16, 31, 20] [1, 32, 1] [23, 0, 19] [23, 31, 19] [1, 32, 1] [22, 0, 19] [22, 31, 19] [1, 32, 1] [21, 0, 19] [21, 31, 19] [1, 32, 1] [20, 0, 19] [20, 31, 19] [1, 32, 1] [19, 0, 19] [19, 31, 19] [1, 32, 1] [18, 0, 19] [18, 31, 19] [1, 32, 1] [17, 0, 19] [17, 31, 19] [1, 32, 1] [16, 0, 19] [16, 31, 19] [1, 32, 1] [23, 0, 18] [23, 31, 18] [1, 32, 1] [22, 0, 18] [22, 31, 18] [1, 32, 1] [21, 0, 18] [21, 31, 18] [1, 32, 1] [20, 0, 18] [20, 31, 18] [1, 32, 1] [19, 0, 18] [19, 31, 18] [1, 32, 1] [18, 0, 18] [18, 31, 18] [1, 32, 1] [17, 0, 18] [17, 31, 18] [1, 32, 1] [16, 0, 18] [16, 31, 18] [1, 32, 1] [23, 0, 17] [23, 31, 17] [1, 32, 1] [22, 0, 17] [22, 31, 17] [1, 32, 1] [21, 0, 17] [21, 31, 17] [1, 32, 1] [20, 0, 17] [20, 31, 17] [1, 32, 1] [19, 0, 17] [19, 31, 17] [1, 32, 1] [18, 0, 17] [18, 31, 17] [1, 32, 1] [17, 0, 17] [17, 31, 17] [1, 32, 1] [16, 0, 17] [16, 31, 17] [1, 32, 1] [23, 0, 16] [23, 31, 16] [1, 32, 1] [22, 0, 16] [22, 31, 16] [1, 32, 1] [21, 0, 16] [21, 31, 16] [1, 32, 1] [20, 0, 16] [20, 31, 16] [1, 32, 1] [19, 0, 16] [19, 31, 16] [1, 32, 1] [18, 0, 16] [18, 31, 16] [1, 32, 1] [17, 0, 16] [17, 31, 16] [1, 32, 1] [16, 0, 16] [16, 31, 16] [1, 32, 1] [15, 0, 23] [15, 31, 23] [1, 32, 1] [14, 0, 23] [14, 31, 23] [1, 32, 1] [13, 0, 23] [13, 31, 23] [1, 32, 1] [12, 0, 23] [12, 31, 23] [1, 32, 1] [11, 0, 23] [11, 31, 23] [1, 32, 1] [10, 0, 23] [10, 31, 23] [1, 32, 1] [9, 0, 23] [9, 31, 23] [1, 32, 1] [8, 0, 23] [8, 31, 23] [1, 32, 1] [15, 0, 22] [15, 31, 22] [1, 32, 1] [14, 0, 22] [14, 31, 22] [1, 32, 1] [13, 0, 22] [13, 31, 22] [1, 32, 1] [12, 0, 22] [12, 31, 22] [1, 32, 1] [11, 0, 22] [11, 31, 22] [1, 32, 1] [10, 0, 22] [10, 31, 22] [1, 32, 1] [9, 0, 22] [9, 31, 22] [1, 32, 1] [8, 0, 22] [8, 31, 22] [1, 32, 1] [15, 0, 21] [15, 31, 21] [1, 32, 1] [14, 0, 21] [14, 31, 21] [1, 32, 1] [13, 0, 21] [13, 31, 21] [1, 32, 1] [12, 0, 21] [12, 31, 21] [1, 32, 1] [11, 0, 21] [11, 31, 21] [1, 32, 1] [10, 0, 21] [10, 31, 21] [1, 32, 1] [9, 0, 21] [9, 31, 21] [1, 32, 1] [8, 0, 21] [8, 31, 21] [1, 32, 1] [15, 0, 20] [15, 31, 20] [1, 32, 1] [14, 0, 20] [14, 31, 20] [1, 32, 1] [13, 0, 20] [13, 31, 20] [1, 32, 1] [12, 0, 20] [12, 31, 20] [1, 32, 1] [11, 0, 20] [11, 31, 20] [1, 32, 1] [10, 0, 20] [10, 31, 20] [1, 32, 1] [9, 0, 20] [9, 31, 20] [1, 32, 1] [8, 0, 20] [8, 31, 20] [1, 32, 1] [15, 0, 19] [15, 31, 19] [1, 32, 1] [14, 0, 19] [14, 31, 19] [1, 32, 1] [13, 0, 19] [13, 31, 19] [1, 32, 1] [12, 0, 19] [12, 31, 19] [1, 32, 1] [11, 0, 19] [11, 31, 19] [1, 32, 1] [10, 0, 19] [10, 31, 19] [1, 32, 1] [9, 0, 19] [9, 31, 19] [1, 32, 1] [8, 0, 19] [8, 31, 19] [1, 32, 1] [15, 0, 18] [15, 31, 18] [1, 32, 1] [14, 0, 18] [14, 31, 18] [1, 32, 1] [13, 0, 18] [13, 31, 18] [1, 32, 1] [12, 0, 18] [12, 31, 18] [1, 32, 1] [11, 0, 18] [11, 31, 18] [1, 32, 1] [10, 0, 18] [10, 31, 18] [1, 32, 1] [9, 0, 18] [9, 31, 18] [1, 32, 1] [8, 0, 18] [8, 31, 18] [1, 32, 1] [15, 0, 17] [15, 31, 17] [1, 32, 1] [14, 0, 17] [14, 31, 17] [1, 32, 1] [13, 0, 17] [13, 31, 17] [1, 32, 1] [12, 0, 17] [12, 31, 17] [1, 32, 1] [11, 0, 17] [11, 31, 17] [1, 32, 1] [10, 0, 17] [10, 31, 17] [1, 32, 1] [9, 0, 17] [9, 31, 17] [1, 32, 1] [8, 0, 17] [8, 31, 17] [1, 32, 1] [15, 0, 16] [15, 31, 16] [1, 32, 1] [14, 0, 16] [14, 31, 16] [1, 32, 1] [13, 0, 16] [13, 31, 16] [1, 32, 1] [12, 0, 16] [12, 31, 16] [1, 32, 1] [11, 0, 16] [11, 31, 16] [1, 32, 1] [10, 0, 16] [10, 31, 16] [1, 32, 1] [9, 0, 16] [9, 31, 16] [1, 32, 1] [8, 0, 16] [8, 31, 16] [1, 32, 1] [7, 0, 23] [7, 31, 23] [1, 32, 1] [6, 0, 23] [6, 31, 23] [1, 32, 1] [5, 0, 23] [5, 31, 23] [1, 32, 1] [4, 0, 23] [4, 31, 23] [1, 32, 1] [3, 0, 23] [3, 31, 23] [1, 32, 1] [2, 0, 23] [2, 31, 23] [1, 32, 1] [1, 0, 23] [1, 31, 23] [1, 32, 1] [0, 0, 23] [0, 31, 23] [1, 32, 1] [7, 0, 22] [7, 31, 22] [1, 32, 1] [6, 0, 22] [6, 31, 22] [1, 32, 1] [5, 0, 22] [5, 31, 22] [1, 32, 1] [4, 0, 22] [4, 31, 22] [1, 32, 1] [3, 0, 22] [3, 31, 22] [1, 32, 1] [2, 0, 22] [2, 31, 22] [1, 32, 1] [1, 0, 22] [1, 31, 22] [1, 32, 1] [0, 0, 22] [0, 31, 22] [1, 32, 1] [7, 0, 21] [7, 31, 21] [1, 32, 1] [6, 0, 21] [6, 31, 21] [1, 32, 1] [5, 0, 21] [5, 31, 21] [1, 32, 1] [4, 0, 21] [4, 31, 21] [1, 32, 1] [3, 0, 21] [3, 31, 21] [1, 32, 1] [2, 0, 21] [2, 31, 21] [1, 32, 1] [1, 0, 21] [1, 31, 21] [1, 32, 1] [0, 0, 21] [0, 31, 21] [1, 32, 1] [7, 0, 20] [7, 31, 20] [1, 32, 1] [6, 0, 20] [6, 31, 20] [1, 32, 1] [5, 0, 20] [5, 31, 20] [1, 32, 1] [4, 0, 20] [4, 31, 20] [1, 32, 1] [3, 0, 20] [3, 31, 20] [1, 32, 1] [2, 0, 20] [2, 31, 20] [1, 32, 1] [1, 0, 20] [1, 31, 20] [1, 32, 1] [0, 0, 20] [0, 31, 20] [1, 32, 1] [7, 0, 19] [7, 31, 19] [1, 32, 1] [6, 0, 19] [6, 31, 19] [1, 32, 1] [5, 0, 19] [5, 31, 19] [1, 32, 1] [4, 0, 19] [4, 31, 19] [1, 32, 1] [3, 0, 19] [3, 31, 19] [1, 32, 1] [2, 0, 19] [2, 31, 19] [1, 32, 1] [1, 0, 19] [1, 31, 19] [1, 32, 1] [0, 0, 19] [0, 31, 19] [1, 32, 1] [7, 0, 18] [7, 31, 18] [1, 32, 1] [6, 0, 18] [6, 31, 18] [1, 32, 1] [5, 0, 18] [5, 31, 18] [1, 32, 1] [4, 0, 18] [4, 31, 18] [1, 32, 1] [3, 0, 18] [3, 31, 18] [1, 32, 1] [2, 0, 18] [2, 31, 18] [1, 32, 1] [1, 0, 18] [1, 31, 18] [1, 32, 1] [0, 0, 18] [0, 31, 18] [1, 32, 1] [7, 0, 17] [7, 31, 17] [1, 32, 1] [6, 0, 17] [6, 31, 17] [1, 32, 1] [5, 0, 17] [5, 31, 17] [1, 32, 1] [4, 0, 17] [4, 31, 17] [1, 32, 1] [3, 0, 17] [3, 31, 17] [1, 32, 1] [2, 0, 17] [2, 31, 17] [1, 32, 1] [1, 0, 17] [1, 31, 17] [1, 32, 1] [0, 0, 17] [0, 31, 17] [1, 32, 1] [7, 0, 16] [7, 31, 16] [1, 32, 1] [6, 0, 16] [6, 31, 16] [1, 32, 1] [5, 0, 16] [5, 31, 16] [1, 32, 1] [4, 0, 16] [4, 31, 16] [1, 32, 1] [3, 0, 16] [3, 31, 16] [1, 32, 1] [2, 0, 16] [2, 31, 16] [1, 32, 1] [1, 0, 16] [1, 31, 16] [1, 32, 1] [0, 0, 16] [0, 31, 16] [1, 32, 1] [31, 0, 15] [31, 31, 15] [1, 32, 1] [30, 0, 15] [30, 31, 15] [1, 32, 1] [29, 0, 15] [29, 31, 15] [1, 32, 1] [28, 0, 15] [28, 31, 15] [1, 32, 1] [27, 0, 15] [27, 31, 15] [1, 32, 1] [26, 0, 15] [26, 31, 15] [1, 32, 1] [25, 0, 15] [25, 31, 15] [1, 32, 1] [24, 0, 15] [24, 31, 15] [1, 32, 1] [31, 0, 14] [31, 31, 14] [1, 32, 1] [30, 0, 14] [30, 31, 14] [1, 32, 1] [29, 0, 14] [29, 31, 14] [1, 32, 1] [28, 0, 14] [28, 31, 14] [1, 32, 1] [27, 0, 14] [27, 31, 14] [1, 32, 1] [26, 0, 14] [26, 31, 14] [1, 32, 1] [25, 0, 14] [25, 31, 14] [1, 32, 1] [24, 0, 14] [24, 31, 14] [1, 32, 1] [31, 0, 13] [31, 31, 13] [1, 32, 1] [30, 0, 13] [30, 31, 13] [1, 32, 1] [29, 0, 13] [29, 31, 13] [1, 32, 1] [28, 0, 13] [28, 31, 13] [1, 32, 1] [27, 0, 13] [27, 31, 13] [1, 32, 1] [26, 0, 13] [26, 31, 13] [1, 32, 1] [25, 0, 13] [25, 31, 13] [1, 32, 1] [24, 0, 13] [24, 31, 13] [1, 32, 1] [31, 0, 12] [31, 31, 12] [1, 32, 1] [30, 0, 12] [30, 31, 12] [1, 32, 1] [29, 0, 12] [29, 31, 12] [1, 32, 1] [28, 0, 12] [28, 31, 12] [1, 32, 1] [27, 0, 12] [27, 31, 12] [1, 32, 1] [26, 0, 12] [26, 31, 12] [1, 32, 1] [25, 0, 12] [25, 31, 12] [1, 32, 1] [24, 0, 12] [24, 31, 12] [1, 32, 1] [31, 0, 11] [31, 31, 11] [1, 32, 1] [30, 0, 11] [30, 31, 11] [1, 32, 1] [29, 0, 11] [29, 31, 11] [1, 32, 1] [28, 0, 11] [28, 31, 11] [1, 32, 1] [27, 0, 11] [27, 31, 11] [1, 32, 1] [26, 0, 11] [26, 31, 11] [1, 32, 1] [25, 0, 11] [25, 31, 11] [1, 32, 1] [24, 0, 11] [24, 31, 11] [1, 32, 1] [31, 0, 10] [31, 31, 10] [1, 32, 1] [30, 0, 10] [30, 31, 10] [1, 32, 1] [29, 0, 10] [29, 31, 10] [1, 32, 1] [28, 0, 10] [28, 31, 10] [1, 32, 1] [27, 0, 10] [27, 31, 10] [1, 32, 1] [26, 0, 10] [26, 31, 10] [1, 32, 1] [25, 0, 10] [25, 31, 10] [1, 32, 1] [24, 0, 10] [24, 31, 10] [1, 32, 1] [31, 0, 9] [31, 31, 9] [1, 32, 1] [30, 0, 9] [30, 31, 9] [1, 32, 1] [29, 0, 9] [29, 31, 9] [1, 32, 1] [28, 0, 9] [28, 31, 9] [1, 32, 1] [27, 0, 9] [27, 31, 9] [1, 32, 1] [26, 0, 9] [26, 31, 9] [1, 32, 1] [25, 0, 9] [25, 31, 9] [1, 32, 1] [24, 0, 9] [24, 31, 9] [1, 32, 1] [31, 0, 8] [31, 31, 8] [1, 32, 1] [30, 0, 8] [30, 31, 8] [1, 32, 1] [29, 0, 8] [29, 31, 8] [1, 32, 1] [28, 0, 8] [28, 31, 8] [1, 32, 1] [27, 0, 8] [27, 31, 8] [1, 32, 1] [26, 0, 8] [26, 31, 8] [1, 32, 1] [25, 0, 8] [25, 31, 8] [1, 32, 1] [24, 0, 8] [24, 31, 8] [1, 32, 1] [23, 0, 15] [23, 31, 15] [1, 32, 1] [22, 0, 15] [22, 31, 15] [1, 32, 1] [21, 0, 15] [21, 31, 15] [1, 32, 1] [20, 0, 15] [20, 31, 15] [1, 32, 1] [19, 0, 15] [19, 31, 15] [1, 32, 1] [18, 0, 15] [18, 31, 15] [1, 32, 1] [17, 0, 15] [17, 31, 15] [1, 32, 1] [16, 0, 15] [16, 31, 15] [1, 32, 1] [23, 0, 14] [23, 31, 14] [1, 32, 1] [22, 0, 14] [22, 31, 14] [1, 32, 1] [21, 0, 14] [21, 31, 14] [1, 32, 1] [20, 0, 14] [20, 31, 14] [1, 32, 1] [19, 0, 14] [19, 31, 14] [1, 32, 1] [18, 0, 14] [18, 31, 14] [1, 32, 1] [17, 0, 14] [17, 31, 14] [1, 32, 1] [16, 0, 14] [16, 31, 14] [1, 32, 1] [23, 0, 13] [23, 31, 13] [1, 32, 1] [22, 0, 13] [22, 31, 13] [1, 32, 1] [21, 0, 13] [21, 31, 13] [1, 32, 1] [20, 0, 13] [20, 31, 13] [1, 32, 1] [19, 0, 13] [19, 31, 13] [1, 32, 1] [18, 0, 13] [18, 31, 13] [1, 32, 1] [17, 0, 13] [17, 31, 13] [1, 32, 1] [16, 0, 13] [16, 31, 13] [1, 32, 1] [23, 0, 12] [23, 31, 12] [1, 32, 1] [22, 0, 12] [22, 31, 12] [1, 32, 1] [21, 0, 12] [21, 31, 12] [1, 32, 1] [20, 0, 12] [20, 31, 12] [1, 32, 1] [19, 0, 12] [19, 31, 12] [1, 32, 1] [18, 0, 12] [18, 31, 12] [1, 32, 1] [17, 0, 12] [17, 31, 12] [1, 32, 1] [16, 0, 12] [16, 31, 12] [1, 32, 1] [23, 0, 11] [23, 31, 11] [1, 32, 1] [22, 0, 11] [22, 31, 11] [1, 32, 1] [21, 0, 11] [21, 31, 11] [1, 32, 1] [20, 0, 11] [20, 31, 11] [1, 32, 1] [19, 0, 11] [19, 31, 11] [1, 32, 1] [18, 0, 11] [18, 31, 11] [1, 32, 1] [17, 0, 11] [17, 31, 11] [1, 32, 1] [16, 0, 11] [16, 31, 11] [1, 32, 1] [23, 0, 10] [23, 31, 10] [1, 32, 1] [22, 0, 10] [22, 31, 10] [1, 32, 1] [21, 0, 10] [21, 31, 10] [1, 32, 1] [20, 0, 10] [20, 31, 10] [1, 32, 1] [19, 0, 10] [19, 31, 10] [1, 32, 1] [18, 0, 10] [18, 31, 10] [1, 32, 1] [17, 0, 10] [17, 31, 10] [1, 32, 1] [16, 0, 10] [16, 31, 10] [1, 32, 1] [23, 0, 9] [23, 31, 9] [1, 32, 1] [22, 0, 9] [22, 31, 9] [1, 32, 1] [21, 0, 9] [21, 31, 9] [1, 32, 1] [20, 0, 9] [20, 31, 9] [1, 32, 1] [19, 0, 9] [19, 31, 9] [1, 32, 1] [18, 0, 9] [18, 31, 9] [1, 32, 1] [17, 0, 9] [17, 31, 9] [1, 32, 1] [16, 0, 9] [16, 31, 9] [1, 32, 1] [23, 0, 8] [23, 31, 8] [1, 32, 1] [22, 0, 8] [22, 31, 8] [1, 32, 1] [21, 0, 8] [21, 31, 8] [1, 32, 1] [20, 0, 8] [20, 31, 8] [1, 32, 1] [19, 0, 8] [19, 31, 8] [1, 32, 1] [18, 0, 8] [18, 31, 8] [1, 32, 1] [17, 0, 8] [17, 31, 8] [1, 32, 1] [16, 0, 8] [16, 31, 8] [1, 32, 1] [15, 0, 15] [15, 31, 15] [1, 32, 1] [14, 0, 15] [14, 31, 15] [1, 32, 1] [13, 0, 15] [13, 31, 15] [1, 32, 1] [12, 0, 15] [12, 31, 15] [1, 32, 1] [11, 0, 15] [11, 31, 15] [1, 32, 1] [10, 0, 15] [10, 31, 15] [1, 32, 1] [9, 0, 15] [9, 31, 15] [1, 32, 1] [8, 0, 15] [8, 31, 15] [1, 32, 1] [15, 0, 14] [15, 31, 14] [1, 32, 1] [14, 0, 14] [14, 31, 14] [1, 32, 1] [13, 0, 14] [13, 31, 14] [1, 32, 1] [12, 0, 14] [12, 31, 14] [1, 32, 1] [11, 0, 14] [11, 31, 14] [1, 32, 1] [10, 0, 14] [10, 31, 14] [1, 32, 1] [9, 0, 14] [9, 31, 14] [1, 32, 1] [8, 0, 14] [8, 31, 14] [1, 32, 1] [15, 0, 13] [15, 31, 13] [1, 32, 1] [14, 0, 13] [14, 31, 13] [1, 32, 1] [13, 0, 13] [13, 31, 13] [1, 32, 1] [12, 0, 13] [12, 31, 13] [1, 32, 1] [11, 0, 13] [11, 31, 13] [1, 32, 1] [10, 0, 13] [10, 31, 13] [1, 32, 1] [9, 0, 13] [9, 31, 13] [1, 32, 1] [8, 0, 13] [8, 31, 13] [1, 32, 1] [15, 0, 12] [15, 31, 12] [1, 32, 1] [14, 0, 12] [14, 31, 12] [1, 32, 1] [13, 0, 12] [13, 31, 12] [1, 32, 1] [12, 0, 12] [12, 31, 12] [1, 32, 1] [11, 0, 12] [11, 31, 12] [1, 32, 1] [10, 0, 12] [10, 31, 12] [1, 32, 1] [9, 0, 12] [9, 31, 12] [1, 32, 1] [8, 0, 12] [8, 31, 12] [1, 32, 1] [15, 0, 11] [15, 31, 11] [1, 32, 1] [14, 0, 11] [14, 31, 11] [1, 32, 1] [13, 0, 11] [13, 31, 11] [1, 32, 1] [12, 0, 11] [12, 31, 11] [1, 32, 1] [11, 0, 11] [11, 31, 11] [1, 32, 1] [10, 0, 11] [10, 31, 11] [1, 32, 1] [9, 0, 11] [9, 31, 11] [1, 32, 1] [8, 0, 11] [8, 31, 11] [1, 32, 1] [15, 0, 10] [15, 31, 10] [1, 32, 1] [14, 0, 10] [14, 31, 10] [1, 32, 1] [13, 0, 10] [13, 31, 10] [1, 32, 1] [12, 0, 10] [12, 31, 10] [1, 32, 1] [11, 0, 10] [11, 31, 10] [1, 32, 1] [10, 0, 10] [10, 31, 10] [1, 32, 1] [9, 0, 10] [9, 31, 10] [1, 32, 1] [8, 0, 10] [8, 31, 10] [1, 32, 1] [15, 0, 9] [15, 31, 9] [1, 32, 1] [14, 0, 9] [14, 31, 9] [1, 32, 1] [13, 0, 9] [13, 31, 9] [1, 32, 1] [12, 0, 9] [12, 31, 9] [1, 32, 1] [11, 0, 9] [11, 31, 9] [1, 32, 1] [10, 0, 9] [10, 31, 9] [1, 32, 1] [9, 0, 9] [9, 31, 9] [1, 32, 1] [8, 0, 9] [8, 31, 9] [1, 32, 1] [15, 0, 8] [15, 31, 8] [1, 32, 1] [14, 0, 8] [14, 31, 8] [1, 32, 1] [13, 0, 8] [13, 31, 8] [1, 32, 1] [12, 0, 8] [12, 31, 8] [1, 32, 1] [11, 0, 8] [11, 31, 8] [1, 32, 1] [10, 0, 8] [10, 31, 8] [1, 32, 1] [9, 0, 8] [9, 31, 8] [1, 32, 1] [8, 0, 8] [8, 31, 8] [1, 32, 1] [7, 0, 15] [7, 31, 15] [1, 32, 1] [6, 0, 15] [6, 31, 15] [1, 32, 1] [5, 0, 15] [5, 31, 15] [1, 32, 1] [4, 0, 15] [4, 31, 15] [1, 32, 1] [3, 0, 15] [3, 31, 15] [1, 32, 1] [2, 0, 15] [2, 31, 15] [1, 32, 1] [1, 0, 15] [1, 31, 15] [1, 32, 1] [0, 0, 15] [0, 31, 15] [1, 32, 1] [7, 0, 14] [7, 31, 14] [1, 32, 1] [6, 0, 14] [6, 31, 14] [1, 32, 1] [5, 0, 14] [5, 31, 14] [1, 32, 1] [4, 0, 14] [4, 31, 14] [1, 32, 1] [3, 0, 14] [3, 31, 14] [1, 32, 1] [2, 0, 14] [2, 31, 14] [1, 32, 1] [1, 0, 14] [1, 31, 14] [1, 32, 1] [0, 0, 14] [0, 31, 14] [1, 32, 1] [7, 0, 13] [7, 31, 13] [1, 32, 1] [6, 0, 13] [6, 31, 13] [1, 32, 1] [5, 0, 13] [5, 31, 13] [1, 32, 1] [4, 0, 13] [4, 31, 13] [1, 32, 1] [3, 0, 13] [3, 31, 13] [1, 32, 1] [2, 0, 13] [2, 31, 13] [1, 32, 1] [1, 0, 13] [1, 31, 13] [1, 32, 1] [0, 0, 13] [0, 31, 13] [1, 32, 1] [7, 0, 12] [7, 31, 12] [1, 32, 1] [6, 0, 12] [6, 31, 12] [1, 32, 1] [5, 0, 12] [5, 31, 12] [1, 32, 1] [4, 0, 12] [4, 31, 12] [1, 32, 1] [3, 0, 12] [3, 31, 12] [1, 32, 1] [2, 0, 12] [2, 31, 12] [1, 32, 1] [1, 0, 12] [1, 31, 12] [1, 32, 1] [0, 0, 12] [0, 31, 12] [1, 32, 1] [7, 0, 11] [7, 31, 11] [1, 32, 1] [6, 0, 11] [6, 31, 11] [1, 32, 1] [5, 0, 11] [5, 31, 11] [1, 32, 1] [4, 0, 11] [4, 31, 11] [1, 32, 1] [3, 0, 11] [3, 31, 11] [1, 32, 1] [2, 0, 11] [2, 31, 11] [1, 32, 1] [1, 0, 11] [1, 31, 11] [1, 32, 1] [0, 0, 11] [0, 31, 11] [1, 32, 1] [7, 0, 10] [7, 31, 10] [1, 32, 1] [6, 0, 10] [6, 31, 10] [1, 32, 1] [5, 0, 10] [5, 31, 10] [1, 32, 1] [4, 0, 10] [4, 31, 10] [1, 32, 1] [3, 0, 10] [3, 31, 10] [1, 32, 1] [2, 0, 10] [2, 31, 10] [1, 32, 1] [1, 0, 10] [1, 31, 10] [1, 32, 1] [0, 0, 10] [0, 31, 10] [1, 32, 1] [7, 0, 9] [7, 31, 9] [1, 32, 1] [6, 0, 9] [6, 31, 9] [1, 32, 1] [5, 0, 9] [5, 31, 9] [1, 32, 1] [4, 0, 9] [4, 31, 9] [1, 32, 1] [3, 0, 9] [3, 31, 9] [1, 32, 1] [2, 0, 9] [2, 31, 9] [1, 32, 1] [1, 0, 9] [1, 31, 9] [1, 32, 1] [0, 0, 9] [0, 31, 9] [1, 32, 1] [7, 0, 8] [7, 31, 8] [1, 32, 1] [6, 0, 8] [6, 31, 8] [1, 32, 1] [5, 0, 8] [5, 31, 8] [1, 32, 1] [4, 0, 8] [4, 31, 8] [1, 32, 1] [3, 0, 8] [3, 31, 8] [1, 32, 1] [2, 0, 8] [2, 31, 8] [1, 32, 1] [1, 0, 8] [1, 31, 8] [1, 32, 1] [0, 0, 8] [0, 31, 8] [1, 32, 1] [31, 0, 7] [31, 31, 7] [1, 32, 1] [30, 0, 7] [30, 31, 7] [1, 32, 1] [29, 0, 7] [29, 31, 7] [1, 32, 1] [28, 0, 7] [28, 31, 7] [1, 32, 1] [27, 0, 7] [27, 31, 7] [1, 32, 1] [26, 0, 7] [26, 31, 7] [1, 32, 1] [25, 0, 7] [25, 31, 7] [1, 32, 1] [24, 0, 7] [24, 31, 7] [1, 32, 1] [31, 0, 6] [31, 31, 6] [1, 32, 1] [30, 0, 6] [30, 31, 6] [1, 32, 1] [29, 0, 6] [29, 31, 6] [1, 32, 1] [28, 0, 6] [28, 31, 6] [1, 32, 1] [27, 0, 6] [27, 31, 6] [1, 32, 1] [26, 0, 6] [26, 31, 6] [1, 32, 1] [25, 0, 6] [25, 31, 6] [1, 32, 1] [24, 0, 6] [24, 31, 6] [1, 32, 1] [31, 0, 5] [31, 31, 5] [1, 32, 1] [30, 0, 5] [30, 31, 5] [1, 32, 1] [29, 0, 5] [29, 31, 5] [1, 32, 1] [28, 0, 5] [28, 31, 5] [1, 32, 1] [27, 0, 5] [27, 31, 5] [1, 32, 1] [26, 0, 5] [26, 31, 5] [1, 32, 1] [25, 0, 5] [25, 31, 5] [1, 32, 1] [24, 0, 5] [24, 31, 5] [1, 32, 1] [31, 0, 4] [31, 31, 4] [1, 32, 1] [30, 0, 4] [30, 31, 4] [1, 32, 1] [29, 0, 4] [29, 31, 4] [1, 32, 1] [28, 0, 4] [28, 31, 4] [1, 32, 1] [27, 0, 4] [27, 31, 4] [1, 32, 1] [26, 0, 4] [26, 31, 4] [1, 32, 1] [25, 0, 4] [25, 31, 4] [1, 32, 1] [24, 0, 4] [24, 31, 4] [1, 32, 1] [31, 0, 3] [31, 31, 3] [1, 32, 1] [30, 0, 3] [30, 31, 3] [1, 32, 1] [29, 0, 3] [29, 31, 3] [1, 32, 1] [28, 0, 3] [28, 31, 3] [1, 32, 1] [27, 0, 3] [27, 31, 3] [1, 32, 1] [26, 0, 3] [26, 31, 3] [1, 32, 1] [25, 0, 3] [25, 31, 3] [1, 32, 1] [24, 0, 3] [24, 31, 3] [1, 32, 1] [31, 0, 2] [31, 31, 2] [1, 32, 1] [30, 0, 2] [30, 31, 2] [1, 32, 1] [29, 0, 2] [29, 31, 2] [1, 32, 1] [28, 0, 2] [28, 31, 2] [1, 32, 1] [27, 0, 2] [27, 31, 2] [1, 32, 1] [26, 0, 2] [26, 31, 2] [1, 32, 1] [25, 0, 2] [25, 31, 2] [1, 32, 1] [24, 0, 2] [24, 31, 2] [1, 32, 1] [31, 0, 1] [31, 31, 1] [1, 32, 1] [30, 0, 1] [30, 31, 1] [1, 32, 1] [29, 0, 1] [29, 31, 1] [1, 32, 1] [28, 0, 1] [28, 31, 1] [1, 32, 1] [27, 0, 1] [27, 31, 1] [1, 32, 1] [26, 0, 1] [26, 31, 1] [1, 32, 1] [25, 0, 1] [25, 31, 1] [1, 32, 1] [24, 0, 1] [24, 31, 1] [1, 32, 1] [31, 0, 0] [31, 31, 0] [1, 32, 1] [30, 0, 0] [30, 31, 0] [1, 32, 1] [29, 0, 0] [29, 31, 0] [1, 32, 1] [28, 0, 0] [28, 31, 0] [1, 32, 1] [27, 0, 0] [27, 31, 0] [1, 32, 1] [26, 0, 0] [26, 31, 0] [1, 32, 1] [25, 0, 0] [25, 31, 0] [1, 32, 1] [24, 0, 0] [24, 31, 0] [1, 32, 1] [23, 0, 7] [23, 31, 7] [1, 32, 1] [22, 0, 7] [22, 31, 7] [1, 32, 1] [21, 0, 7] [21, 31, 7] [1, 32, 1] [20, 0, 7] [20, 31, 7] [1, 32, 1] [19, 0, 7] [19, 31, 7] [1, 32, 1] [18, 0, 7] [18, 31, 7] [1, 32, 1] [17, 0, 7] [17, 31, 7] [1, 32, 1] [16, 0, 7] [16, 31, 7] [1, 32, 1] [23, 0, 6] [23, 31, 6] [1, 32, 1] [22, 0, 6] [22, 31, 6] [1, 32, 1] [21, 0, 6] [21, 31, 6] [1, 32, 1] [20, 0, 6] [20, 31, 6] [1, 32, 1] [19, 0, 6] [19, 31, 6] [1, 32, 1] [18, 0, 6] [18, 31, 6] [1, 32, 1] [17, 0, 6] [17, 31, 6] [1, 32, 1] [16, 0, 6] [16, 31, 6] [1, 32, 1] [23, 0, 5] [23, 31, 5] [1, 32, 1] [22, 0, 5] [22, 31, 5] [1, 32, 1] [21, 0, 5] [21, 31, 5] [1, 32, 1] [20, 0, 5] [20, 31, 5] [1, 32, 1] [19, 0, 5] [19, 31, 5] [1, 32, 1] [18, 0, 5] [18, 31, 5] [1, 32, 1] [17, 0, 5] [17, 31, 5] [1, 32, 1] [16, 0, 5] [16, 31, 5] [1, 32, 1] [23, 0, 4] [23, 31, 4] [1, 32, 1] [22, 0, 4] [22, 31, 4] [1, 32, 1] [21, 0, 4] [21, 31, 4] [1, 32, 1] [20, 0, 4] [20, 31, 4] [1, 32, 1] [19, 0, 4] [19, 31, 4] [1, 32, 1] [18, 0, 4] [18, 31, 4] [1, 32, 1] [17, 0, 4] [17, 31, 4] [1, 32, 1] [16, 0, 4] [16, 31, 4] [1, 32, 1] [23, 0, 3] [23, 31, 3] [1, 32, 1] [22, 0, 3] [22, 31, 3] [1, 32, 1] [21, 0, 3] [21, 31, 3] [1, 32, 1] [20, 0, 3] [20, 31, 3] [1, 32, 1] [19, 0, 3] [19, 31, 3] [1, 32, 1] [18, 0, 3] [18, 31, 3] [1, 32, 1] [17, 0, 3] [17, 31, 3] [1, 32, 1] [16, 0, 3] [16, 31, 3] [1, 32, 1] [23, 0, 2] [23, 31, 2] [1, 32, 1] [22, 0, 2] [22, 31, 2] [1, 32, 1] [21, 0, 2] [21, 31, 2] [1, 32, 1] [20, 0, 2] [20, 31, 2] [1, 32, 1] [19, 0, 2] [19, 31, 2] [1, 32, 1] [18, 0, 2] [18, 31, 2] [1, 32, 1] [17, 0, 2] [17, 31, 2] [1, 32, 1] [16, 0, 2] [16, 31, 2] [1, 32, 1] [23, 0, 1] [23, 31, 1] [1, 32, 1] [22, 0, 1] [22, 31, 1] [1, 32, 1] [21, 0, 1] [21, 31, 1] [1, 32, 1] [20, 0, 1] [20, 31, 1] [1, 32, 1] [19, 0, 1] [19, 31, 1] [1, 32, 1] [18, 0, 1] [18, 31, 1] [1, 32, 1] [17, 0, 1] [17, 31, 1] [1, 32, 1] [16, 0, 1] [16, 31, 1] [1, 32, 1] [23, 0, 0] [23, 31, 0] [1, 32, 1] [22, 0, 0] [22, 31, 0] [1, 32, 1] [21, 0, 0] [21, 31, 0] [1, 32, 1] [20, 0, 0] [20, 31, 0] [1, 32, 1] [19, 0, 0] [19, 31, 0] [1, 32, 1] [18, 0, 0] [18, 31, 0] [1, 32, 1] [17, 0, 0] [17, 31, 0] [1, 32, 1] [16, 0, 0] [16, 31, 0] [1, 32, 1] [15, 0, 7] [15, 31, 7] [1, 32, 1] [14, 0, 7] [14, 31, 7] [1, 32, 1] [13, 0, 7] [13, 31, 7] [1, 32, 1] [12, 0, 7] [12, 31, 7] [1, 32, 1] [11, 0, 7] [11, 31, 7] [1, 32, 1] [10, 0, 7] [10, 31, 7] [1, 32, 1] [9, 0, 7] [9, 31, 7] [1, 32, 1] [8, 0, 7] [8, 31, 7] [1, 32, 1] [15, 0, 6] [15, 31, 6] [1, 32, 1] [14, 0, 6] [14, 31, 6] [1, 32, 1] [13, 0, 6] [13, 31, 6] [1, 32, 1] [12, 0, 6] [12, 31, 6] [1, 32, 1] [11, 0, 6] [11, 31, 6] [1, 32, 1] [10, 0, 6] [10, 31, 6] [1, 32, 1] [9, 0, 6] [9, 31, 6] [1, 32, 1] [8, 0, 6] [8, 31, 6] [1, 32, 1] [15, 0, 5] [15, 31, 5] [1, 32, 1] [14, 0, 5] [14, 31, 5] [1, 32, 1] [13, 0, 5] [13, 31, 5] [1, 32, 1] [12, 0, 5] [12, 31, 5] [1, 32, 1] [11, 0, 5] [11, 31, 5] [1, 32, 1] [10, 0, 5] [10, 31, 5] [1, 32, 1] [9, 0, 5] [9, 31, 5] [1, 32, 1] [8, 0, 5] [8, 31, 5] [1, 32, 1] [15, 0, 4] [15, 31, 4] [1, 32, 1] [14, 0, 4] [14, 31, 4] [1, 32, 1] [13, 0, 4] [13, 31, 4] [1, 32, 1] [12, 0, 4] [12, 31, 4] [1, 32, 1] [11, 0, 4] [11, 31, 4] [1, 32, 1] [10, 0, 4] [10, 31, 4] [1, 32, 1] [9, 0, 4] [9, 31, 4] [1, 32, 1] [8, 0, 4] [8, 31, 4] [1, 32, 1] [15, 0, 3] [15, 31, 3] [1, 32, 1] [14, 0, 3] [14, 31, 3] [1, 32, 1] [13, 0, 3] [13, 31, 3] [1, 32, 1] [12, 0, 3] [12, 31, 3] [1, 32, 1] [11, 0, 3] [11, 31, 3] [1, 32, 1] [10, 0, 3] [10, 31, 3] [1, 32, 1] [9, 0, 3] [9, 31, 3] [1, 32, 1] [8, 0, 3] [8, 31, 3] [1, 32, 1] [15, 0, 2] [15, 31, 2] [1, 32, 1] [14, 0, 2] [14, 31, 2] [1, 32, 1] [13, 0, 2] [13, 31, 2] [1, 32, 1] [12, 0, 2] [12, 31, 2] [1, 32, 1] [11, 0, 2] [11, 31, 2] [1, 32, 1] [10, 0, 2] [10, 31, 2] [1, 32, 1] [9, 0, 2] [9, 31, 2] [1, 32, 1] [8, 0, 2] [8, 31, 2] [1, 32, 1] [15, 0, 1] [15, 31, 1] [1, 32, 1] [14, 0, 1] [14, 31, 1] [1, 32, 1] [13, 0, 1] [13, 31, 1] [1, 32, 1] [12, 0, 1] [12, 31, 1] [1, 32, 1] [11, 0, 1] [11, 31, 1] [1, 32, 1] [10, 0, 1] [10, 31, 1] [1, 32, 1] [9, 0, 1] [9, 31, 1] [1, 32, 1] [8, 0, 1] [8, 31, 1] [1, 32, 1] [15, 0, 0] [15, 31, 0] [1, 32, 1] [14, 0, 0] [14, 31, 0] [1, 32, 1] [13, 0, 0] [13, 31, 0] [1, 32, 1] [12, 0, 0] [12, 31, 0] [1, 32, 1] [11, 0, 0] [11, 31, 0] [1, 32, 1] [10, 0, 0] [10, 31, 0] [1, 32, 1] [9, 0, 0] [9, 31, 0] [1, 32, 1] [8, 0, 0] [8, 31, 0] [1, 32, 1] [7, 0, 7] [7, 31, 7] [1, 32, 1] [6, 0, 7] [6, 31, 7] [1, 32, 1] [5, 0, 7] [5, 31, 7] [1, 32, 1] [4, 0, 7] [4, 31, 7] [1, 32, 1] [3, 0, 7] [3, 31, 7] [1, 32, 1] [2, 0, 7] [2, 31, 7] [1, 32, 1] [1, 0, 7] [1, 31, 7] [1, 32, 1] [0, 0, 7] [0, 31, 7] [1, 32, 1] [7, 0, 6] [7, 31, 6] [1, 32, 1] [6, 0, 6] [6, 31, 6] [1, 32, 1] [5, 0, 6] [5, 31, 6] [1, 32, 1] [4, 0, 6] [4, 31, 6] [1, 32, 1] [3, 0, 6] [3, 31, 6] [1, 32, 1] [2, 0, 6] [2, 31, 6] [1, 32, 1] [1, 0, 6] [1, 31, 6] [1, 32, 1] [0, 0, 6] [0, 31, 6] [1, 32, 1] [7, 0, 5] [7, 31, 5] [1, 32, 1] [6, 0, 5] [6, 31, 5] [1, 32, 1] [5, 0, 5] [5, 31, 5] [1, 32, 1] [4, 0, 5] [4, 31, 5] [1, 32, 1] [3, 0, 5] [3, 31, 5] [1, 32, 1] [2, 0, 5] [2, 31, 5] [1, 32, 1] [1, 0, 5] [1, 31, 5] [1, 32, 1] [0, 0, 5] [0, 31, 5] [1, 32, 1] [7, 0, 4] [7, 31, 4] [1, 32, 1] [6, 0, 4] [6, 31, 4] [1, 32, 1] [5, 0, 4] [5, 31, 4] [1, 32, 1] [4, 0, 4] [4, 31, 4] [1, 32, 1] [3, 0, 4] [3, 31, 4] [1, 32, 1] [2, 0, 4] [2, 31, 4] [1, 32, 1] [1, 0, 4] [1, 31, 4] [1, 32, 1] [0, 0, 4] [0, 31, 4] [1, 32, 1] [7, 0, 3] [7, 31, 3] [1, 32, 1] [6, 0, 3] [6, 31, 3] [1, 32, 1] [5, 0, 3] [5, 31, 3] [1, 32, 1] [4, 0, 3] [4, 31, 3] [1, 32, 1] [3, 0, 3] [3, 31, 3] [1, 32, 1] [2, 0, 3] [2, 31, 3] [1, 32, 1] [1, 0, 3] [1, 31, 3] [1, 32, 1] [0, 0, 3] [0, 31, 3] [1, 32, 1] [7, 0, 2] [7, 31, 2] [1, 32, 1] [6, 0, 2] [6, 31, 2] [1, 32, 1] [5, 0, 2] [5, 31, 2] [1, 32, 1] [4, 0, 2] [4, 31, 2] [1, 32, 1] [3, 0, 2] [3, 31, 2] [1, 32, 1] [2, 0, 2] [2, 31, 2] [1, 32, 1] [1, 0, 2] [1, 31, 2] [1, 32, 1] [0, 0, 2] [0, 31, 2] [1, 32, 1] [7, 0, 1] [7, 31, 1] [1, 32, 1] [6, 0, 1] [6, 31, 1] [1, 32, 1] [5, 0, 1] [5, 31, 1] [1, 32, 1] [4, 0, 1] [4, 31, 1] [1, 32, 1] [3, 0, 1] [3, 31, 1] [1, 32, 1] [2, 0, 1] [2, 31, 1] [1, 32, 1] [1, 0, 1] [1, 31, 1] [1, 32, 1] [0, 0, 1] [0, 31, 1] [1, 32, 1] [7, 0, 0] [7, 31, 0] [1, 32, 1] [6, 0, 0] [6, 31, 0] [1, 32, 1] [5, 0, 0] [5, 31, 0] [1, 32, 1] [4, 0, 0] [4, 31, 0] [1, 32, 1] [3, 0, 0] [3, 31, 0] [1, 32, 1] [2, 0, 0] [2, 31, 0] [1, 32, 1] [1, 0, 0] [1, 31, 0] [1, 32, 1] [0, 0, 0] [0, 31, 0] [1, 32, 1] nsteps = 2048 casacore-3.7.1/lattices/Lattices/test/tTiledShape.cc000066400000000000000000000107531476623553700224060ustar00rootroot00000000000000//# tTiledShape.cc: Test program of TiledShape class //# Copyright (C) 1997,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include // // Test program for TiledShape class. // // This program tests the class TiledShape. // The user has the option to enter an interactive session void testClass() { { TiledShape t (IPosition(3,32,16,5)); AlwaysAssertExit (t.shape() == IPosition(3,32,16,5)); AlwaysAssertExit (t.tileShape() == IPosition(3,32,16,5)); AlwaysAssertExit (!t.isTileShapeDefined()); } { TiledShape t (IPosition(3,32,16,5), IPosition(3,32,6,5)); AlwaysAssertExit (t.shape() == IPosition(3,32,16,5)); AlwaysAssertExit (t.tileShape() == IPosition(3,32,6,5)); AlwaysAssertExit (t.isTileShapeDefined()); } { TiledShape t (IPosition(3,32,16,5), IPosition(3,32,6,5)); TiledShape t1(t); AlwaysAssertExit (t1.shape() == IPosition(3,32,16,5)); AlwaysAssertExit (t1.tileShape() == IPosition(3,32,6,5)); AlwaysAssertExit (t1.isTileShapeDefined()); TiledShape t2 (IPosition(2,15,2)); AlwaysAssertExit (t2.shape() == IPosition(2,15,2)); AlwaysAssertExit (t2.tileShape() == IPosition(2,15,2)); AlwaysAssertExit (!t2.isTileShapeDefined()); t1 = t2; AlwaysAssertExit (t1.shape() == IPosition(2,15,2)); AlwaysAssertExit (t1.tileShape() == IPosition(2,15,2)); AlwaysAssertExit (!t1.isTileShapeDefined()); } } IPosition getVec (uInt nrdim, const String& prompt) { while (True) { cout << prompt; String str; cin >> str; if (str == "end") { return IPosition(); } Vector vec = stringToVector (str); if (vec.nelements() > nrdim) { cout << "value can contain max. " << nrdim << " values" << endl; }else{ Bool error = False; IPosition pos(vec.nelements()); for (uInt i=0; i> pos(i); if (pos(i) < 0) { cout << "Value " << pos(i) << " must be >= 0" << endl; error = True; break; } } if (!error) { return pos; } } } } void testTiling (uInt tileSize) { // Convert the command line argument to shape. while (True) { IPosition shape = getVec (10, "array shape (end means stop): "); if (shape.nelements() == 0) { break; } cout << "tileshape = " << TiledShape(shape).defaultTileShape (tileSize, 0.5) << endl; } } int main (int argc, const char* argv[]) { try { testClass(); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } cout << "OK" << endl; if (argc < 2) { cout << ">>>" << endl; cout << "The function defaultTileShape can be tested by" << endl; cout << "invoking as tTiledShape tileSize (in pixels)" << endl; cout << " Eg. tTiledShape 32768" << endl; cout << "<<<" << endl; return 0; } try { // Get the command line argument as tile size. uInt tileSize; istringstream istr1(argv[1]); istr1 >> tileSize; testTiling (tileSize); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } casacore-3.7.1/lattices/lattices.dox000066400000000000000000000046001476623553700174600ustar00rootroot00000000000000//# lattices.dox: doxygen description of lattices package //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ namespace casacore { // \defgroup lattices lattices package (libcasa_lattices) // // The lattices package is a generalization of n-dimensional // arrays. // It can hold arrays in memory, on disk (as a Table or in HDF5), // or as a virtual lattice using expressions, subsets, regions, masking, etc. //

        // The package is divided into a few modules. //

          //
        • Lattices contains // the basic lattice functionality like access, subsetting, and masking. //
        • LatticeMath contains // mathematical operations on lattice data like statistics, FFT, and fitting. //
        • LEL contains // classes to form expressions of lattices. //
        • LRegions contains // classes to define various regions in pixel coordinates (box, // sphere, polygon) and to define combinations of regions (union, // intersection, complement, etc.). //
        // The images module is built on top of // lattices. } casacore-3.7.1/mainpage.dox000066400000000000000000000217321476623553700156260ustar00rootroot00000000000000//# mainpage.dox: doxygen description of Casacore package //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: mainpage.dox 19988 2007-02-28 10:34:10Z Malte.Marquarding $ namespace casacore { /// /// \mainpage casacore /// /// The casacore package /// contains the core libraries of the old /// AIPS++/CASA package. This split /// was made to get /// a better separation of core libraries and applications. /// CASA is now built on top of Casacore. /// /// The system consists of a set of layered libraries (packages) which can /// be seen best on the Modules page. ///
        /// Included is a library (using Boost-Python) to convert the basic Casacore types /// (e.g., Array, Record) to and from python. Build of this library is optional. ///
        /// This library is used in the package /// python-casacore /// (previously pyrap) that offers a /// high level Python binding to /// the Casacore functionality. /// The documentation of the python-casacore packages can be found /// here. /// /// The main features of Casacore are: ///
          ///
        • \ref casa : Core functionality and data types like Array and Record. ///
        • \ref scimath : N-dim functions with auto-differentiation and linear or non-linear /// fitting. ///
        • \ref tables : Table data system supporting N-dim arrays with advanced querying. ///
        • \ref measures : Values in astronomical reference frames using /// physical units (Quanta). ///
        • \ref ms : MeasurementSets for storing data in the UV-domain. ///
        • \ref images : N-dim images in world coordinates with various analysis operations. ///
        /// Casacore notes describe /// some parts of the system in more detail. /// /// Casacore consists of the following subpackages: ///
      • /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// ///
        casa /// Core modules: /// N-dim arrays, /// quanta, /// OS, /// IO, /// HDF5, /// JSON, /// logging, /// and various other useful classes. ///
        scimath /// Mathematical modules /// N-dim functionals, /// linear/non-linear /// fitting, /// and miscellaneous. ///
        tables /// Database-like tables with advanced /// query language /// (TaQL). ///
        measures /// Quantities with references frames /// and their persistency. ///
        lattices /// Memory- or disk-based N-dim arrays /// (lattices) with masking, /// regions, expressions, and math. ///
        fits /// A C++ interface on top of /// cfitsio. ///
        ms /// The data format for visibility data as described in /// the /// MeasurementSet definition. ///
        derivedmscal /// Derived MeasurementSet quantities (like hourangle) that can be /// used as virtual table columns or as TaQL user defined functions. ///
        meas /// TaQL user defined functions to handle measures. It supports all /// conversions for epochs, positions, and directions. ///
        msfits /// Mapping of MeasurementSets to/from FITS. ///
        coordinates /// Coordinates for astronomical images. ///
        images /// N-dim images with masks, /// coordinates, and history. ///
        python /// N-dim python /// converters for basic Casacore data types. ///
        /// /// Detailed build instructions are given on the /// /// casacore pages. It makes it possible to build selective parts of casacore. ///
        When using a specific casacore package, it is useful to know /// the package dependencies when linking a program. They are as follows: /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// ///
        casa
        scimathcasa
        tablescasa
        measurestables scimath casa
        latticestables scimath casa
        fitsmeasures tables scimath casa cfitsio
        msmeasures tables scimath casa
        derivedmscalmeasures tables scimath casa
        measmeasures tables scimath casa
        msfitsms fits measures tables scimath casa cfitsio
        coordinatesfits measures tables scimath casa wcslib cfitsio
        imageslattices coordinates fits measures tables scimath casa /// mirlib wcslib cfitsio
        pythoncasa boost-python python numpy
        /// mirlib /// is distributed and built as part of Casacore. /// wcslib and /// cfitsio /// are external packages that have to be installed before building all of Casacore. /// Boost-Python /// is required when building libcasa_python. } casacore-3.7.1/meas/000077500000000000000000000000001476623553700142515ustar00rootroot00000000000000casacore-3.7.1/meas/CMakeLists.txt000066400000000000000000000026201476623553700170110ustar00rootroot00000000000000# # CASA meas # add_library (casa_meas MeasUDF/BaseEngine.cc MeasUDF/PositionEngine.cc MeasUDF/PositionUDF.cc MeasUDF/EpochEngine.cc MeasUDF/EpochUDF.cc MeasUDF/DirectionUDF.cc MeasUDF/DirectionEngine.cc MeasUDF/EarthMagneticUDF.cc MeasUDF/EarthMagneticEngine.cc MeasUDF/FrequencyUDF.cc MeasUDF/FrequencyEngine.cc MeasUDF/RadialVelocityUDF.cc MeasUDF/RadialVelocityEngine.cc MeasUDF/DopplerUDF.cc MeasUDF/DopplerEngine.cc MeasUDF/Register.cc ) set (top_level_headers MeasUDF.h ) init_pch_support(casa_meas ${top_level_headers}) target_link_libraries (casa_meas casa_measures) install (TARGETS casa_meas RUNTIME DESTINATION bin LIBRARY DESTINATION lib${LIB_SUFFIX} ARCHIVE DESTINATION lib${LIB_SUFFIX} LIBRARY PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) install (FILES MeasUDF/BaseEngine.h MeasUDF/MeasEngine.h MeasUDF/MeasEngine.tcc MeasUDF/PositionUDF.h MeasUDF/PositionEngine.h MeasUDF/EpochUDF.h MeasUDF/EpochEngine.h MeasUDF/DirectionUDF.h MeasUDF/DirectionEngine.h MeasUDF/EarthMagneticUDF.h MeasUDF/EarthMagneticEngine.h MeasUDF/FrequencyUDF.h MeasUDF/FrequencyEngine.h MeasUDF/RadialVelocityUDF.h MeasUDF/RadialVelocityEngine.h MeasUDF/DopplerUDF.h MeasUDF/DopplerEngine.h MeasUDF/Register.h DESTINATION include/casacore/meas/MeasUDF ) install (FILES ${top_level_headers} DESTINATION include/casacore/meas ) add_subdirectory (MeasUDF/test ${EXCL_ALL}) casacore-3.7.1/meas/MeasUDF.h000066400000000000000000000071561476623553700156570ustar00rootroot00000000000000//# MeasUDF.h: TaQL functions handling measures //# Copyright (C) 2011 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEAS_MEASUDF_H #define MEAS_MEASUDF_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // //

        // TaQL user defined functions handling measures // // //
      • UDFBase //
      • Measures module // // // // // // This module extends TaQL (the Table Query Language) with functions handling // measures. Currently it can handle MDirection, MEpoch, MPosition, MFrequency, // MRadialVelocity, MDoppler and MEarthMagnetic. // // These functions make it possible to convert one or more measures from // one reference type and frame to another. For example, to convert a // direction from J2000 to apparent one can specify the direction in J2000 // as well as a time and position to define the measure frame like: // // calc meas.app ([4h23m32.7, 34d11m54.8], "J2000", // datetime(), "UTC", POSITION) from my.ms/ANTENNA // // The above example converts the given J2000 direction to apparent coordinates // for the given time (current time is used) and for all positions in the // POSITION column in the given ANTENNA table. // // As shown in the example an argument of a meas function can be // a constant, a table column in a table. or any expression. // If a table column is given, it is recognized if the column has a reference // type attached to it (using the TableMeasures). In this example it // would be recognized that the positions in the POSITION column are given // as, say, WGS84. //
        For constants the reference type can be given in case it differs from // the default type. In the example UTC is specified for the time (was not // necessary because it is the default). // // The meas library will be loaded dynamically by TaQL when such a function // is used. Therefore it is important that the library and the other casacore // libraries are built shared. //
        It is also important that the library can be found in the // (DY)LD_LIBRARY_PATH. //
        //
        // // // It is very handy to be able to convert measures in tools that // deal with various table columns (e.g. TaQL, TablePlot, pyrap). // //# //# // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/meas/MeasUDF/000077500000000000000000000000001476623553700154755ustar00rootroot00000000000000casacore-3.7.1/meas/MeasUDF/BaseEngine.cc000066400000000000000000000066611476623553700200150ustar00rootroot00000000000000//# BaseEngine.cc: Base class for the TaQL UDF conversion engines //# Copyright (C) 2018 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include namespace casacore { BaseEngine::~BaseEngine() {} void BaseEngine::adaptForConstant (const IPosition& shapeConstants, uInt nvalues) { uInt size = shapeConstants.product(); // Set to shape if not empty. if (size > 0) { itsIsConst = True; itsShape.resize (0); itsShape = shapeConstants; // Prepend with extra axis if needed. if (nvalues > 0) { itsShape.prepend (IPosition(1, nvalues)); } } // If returning single values, one dimension less is used. // Note the shape is already set (to the column's shape) if a column is used. if (itsShape.size() > 0) { if (nvalues == 1) { IPosition outShape = itsShape.getLast (itsShape.size() - 1); itsShape.resize (outShape.size()); itsShape = outShape; } else if (nvalues > 0) { // Set correct nr of output values per element. itsShape[0] = nvalues; } } if (itsNDim < 0 && itsShape.size() > 0) { itsNDim = itsShape.size(); } } void BaseEngine::extendBase (const BaseEngine& engine, Bool skipFirstAxis) { // An empty shape means it is a scalar or it is unknown. // ndim<0 means the dimensionality and shape are unknown. // ndim=0 means a scalar. // ndim>0 is a known dimensionality (but shape might be unknown). IPosition shp = engine.shape(); Int ndim = engine.ndim(); IPosition shape; if (skipFirstAxis) { // Remove first axis (for e.g. position and direction). if (shp.size() > 0) { shape = shp.getLast (shp.size() - 1); } else { shape = shp; } // Treat it as a scalar if only 1 value. if (shape.product() == 1) { shape.resize (0); ndim = 0; } else if (ndim > 0) { // Remove first axis. ndim--; } } if (ndim > 0) { if (itsNDim >= 0) { itsNDim += ndim; } itsShape.append (shape); } if (! engine.isConstant()) { itsIsConst = False; } } void BaseEngine::deriveAttr (const Unit&, Int) {} void BaseEngine::setValueType (Int) {} String BaseEngine::stripMeasType (const String& type) { return type; } } casacore-3.7.1/meas/MeasUDF/BaseEngine.h000066400000000000000000000124371476623553700176550ustar00rootroot00000000000000//# BaseEngine.h: Base class for the TaQL UDF conversion engines //# Copyright (C) 2018 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEAS_BASEENGINE_H #define MEAS_BASEENGINE_H //# Includes #include #include #include #include #include namespace casacore { // // Abstract base class for the TaQL UDF conversion engines // // // // // //# Classes you should understand before using this one. //
      • EngineBase // // // DopplerEngine defines Engines (user defined functions) that can be used // in TaQL to convert Measures for dopplers. // In this way such derived values appear to be ordinary TaQL functions. // // Doppler conversions require a MeasFrame containing sky direction, // epoch and position on earth. // In TaQL these functions can be called like: // // meas.rv ('TOPO', 1 'm/s', 'LSRK', 'CasA', date(), // [1e6m,1e6m,1e6m], 'WGS84') // // which converts the dopplers from LSRK to TOPO. //
          //
        • // toref is a single constant string. //
        • // pos can have various value types. A single numeric array is // a series of RA,DEC in J2000. If given as a set, the last argument of the // set can be the reference types of the values in the set. The values can // be strings (indicating planetary objects) or value pairs giving lon,lat. // The default reference type is J2000. //
        // All such functions return data with type double and unit Hz. // // Dopplers can be given like: // [v1,v2,...], fromRef // where fromRef is the reference type. // // A doppler can also be a table column which usually knows its type. // It can also be an expression (e.g. DOPPLER[0,]) which also knows the type. //
        // // It makes it possible to handle measures in TaQL. // class BaseEngine { public: BaseEngine() : itsIsConst (False), itsNDim (-1) {} virtual ~BaseEngine(); // Adapt the output shape and dimensionality for possible constant values. // It also sets the itsIsConst flag. // If the given shape is not empty the shape is set to it and an extra axis // is added if nvalues>0 (for e.g. LONLAT). // If nvalues=1, the first axis is removed from the shape. // Note that the shape might have been set to the column's shape if a // measure column is used. void adaptForConstant (const IPosition& shapeConstant, uInt nvalues=0); // Extend the shape (if not empty) with the engine's shape. // If the engine is not const, itsIsConst is cleared. void extendBase (const BaseEngine&, Bool removeFirstAxis=False); // Get the output shape. const IPosition& shape() const { return itsShape; } // Get the output dimensionality. Int ndim() const { return itsNDim; } // Get the unit of the function's result. const Unit& unit() const { return itsOutUnit; } // Get the unit of the expression. const Unit& inUnit() const { return itsInUnit; } // Tell if the expression is constant. Bool isConstant() const { return itsIsConst; } protected: // Let a derived class derive its attributes. // The default implementation does nothing. virtual void deriveAttr (const Unit& unit, Int nval); // Let a derived class set its value type. // By default is does nothing. virtual void setValueType (Int valueType); // Let a derived class strip part of the reference type. // The default implementation returns the full type string. virtual String stripMeasType (const String& type); //# Data members. Bool itsIsConst; IPosition itsShape; Int itsNDim; // <0 unknown shape, 0 scalar, >0 known shape Unit itsInUnit; Unit itsOutUnit; TableExprNode itsExprNode; }; } //end namespace #endif casacore-3.7.1/meas/MeasUDF/DirectionEngine.cc000066400000000000000000000455631476623553700210670ustar00rootroot00000000000000//# DirectionEngine.cc: Engine for TaQL UDF Direction conversions //# Copyright (C) 2011 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { DirectionEngine::DirectionEngine() : itsEpochEngine (0), itsPositionEngine (0) {} DirectionEngine::~DirectionEngine() {} void DirectionEngine::handleDirection (const vector& args, uInt& argnr, Bool riseSet, Bool asDirCos) { // Initialize to unknown reference type. itsRefType = MDirection::N_Types; // Normally directions must be given in an array, but a single one // can be 2 or 3 scalars. uInt nargnr = argnr+1; Bool asScalar = False; TENShPtr scalar3; // A string means that object names (e.g. MOON) are given. if (args[argnr]->dataType() == TableExprNodeRep::NTString) { handleNames (args[argnr]); } else { if (! args[argnr]->isReal()) { throw AipsError("Invalid direction given in a MEAS function"); } if (args.size() > nargnr && args[argnr]->isReal() && args[argnr]->valueType() == TableExprNodeRep::VTScalar && args[nargnr]->isReal() && args[nargnr]->valueType() == TableExprNodeRep::VTScalar) { asScalar = True; nargnr++; // See if given as 3 scalars xyz (direction cosines). if (args.size() > nargnr && args[nargnr]->isReal() && args[nargnr]->valueType() == TableExprNodeRep::VTScalar) { scalar3 = args[nargnr]; nargnr++; } } // See if a reference type is given. if (args.size() > nargnr && args[nargnr]->dataType() == TableExprNodeRep::NTString) { if (handleMeasType (args[nargnr], False)) { nargnr++; } } // Process as scalars or as array. if (asScalar) { handleScalars (args[argnr], args[argnr+1], scalar3); } else { handleMeasArray (args[argnr]); if (itsMeasArrCol.isNull()) { // Set or convert the operand's unit to radian. TENShPtr operand(args[argnr]); TableExprNodeUnit::adaptUnit (operand, "rad"); itsExprNode = operand; } } } // Skip the arguments handled. argnr = nargnr; // Set shape, etc. for constants. adaptForConstant (itsConstants.shape(), asDirCos ? 3:2); // Determine the output unit, shape, and ndim. if (riseSet) { itsOutUnit = "d"; } else if (!asDirCos) { itsOutUnit = "rad"; } } void DirectionEngine::handleScalars (const TENShPtr& e1, const TENShPtr& e2, const TENShPtr& e3) { if (! (e1->isConstant() && e2->isConstant()) || (e3 && !e3->isConstant())) { throw AipsError ("Scalar values given as direction in a MEAS function " "must be constant values"); } double v1 = e1->getDouble(0); double v2 = e2->getDouble(0); double v3 = 0; Unit u1 = e1->unit(); Unit u2 = e2->unit(); if (e3) { v3 = e3->getDouble(0); if (! (u1.empty() && u2.empty() && e3->unit().empty())) { throw AipsError ("Directions given as x,y,z in a MEAS function " "cannot have units"); } } else { if (u1.empty()) u1 = "rad"; if (u2.empty()) u2 = "rad"; } if (itsRefType == MDirection::N_Types) { itsRefType = MDirection::J2000; // default reftype } itsConstants.resize (IPosition(1,1)); if (e3) { itsConstants.data()[0] = MDirection(MVDirection(v1, v2, v3), itsRefType); } else { itsConstants.data()[0] = MDirection(Quantity(v1, u1), Quantity(v2, u2), itsRefType); } } void DirectionEngine::handleNames (const TENShPtr& operand) { if (! operand->isConstant()) { throw AipsError ("Object names given as directions in a MEAS function " "must be constant values"); } Array names = operand->getStringAS(0).array(); itsConstants.resize (names.shape()); itsH.resize (names.size()); for (uInt i=0; i& directions) { Array values = operand.getArrayDouble(id); IPosition shape = values.shape(); int nrv = 0; Unit unit(operand.unit()); if (shape[0] % 2 == 0) { nrv = 2; if (unit.empty()) { unit = "rad"; } } else if (shape[0] % 3 == 0) { nrv = 3; if (! unit.empty()) { throw AipsError ("Directions given as x,y,z in a MEAS function " "cannot have units"); } } else { throw AipsError ("Number of values in a direction in a MEAS function " "should be a multiple of 2 or 3"); } IPosition dirShape; if (shape[0] == nrv && shape.size() > 1) { dirShape = shape.getLast (shape.size() - 1); } else { dirShape = shape; dirShape[0] /= nrv; } directions.resize (dirShape); Quantity q1(0, unit); Quantity q2(0, unit); Bool delIt; const Double* valVec = values.getStorage (delIt); MDirection* dirVec = directions.data(); for (uInt i=0; i DirectionEngine::getDirections (const TableExprId& id) { if (itsConstants.size() > 0) { return itsConstants; } if (!itsMeasArrCol.isNull()) { return itsMeasArrCol(id.rownr()); } Array directions; handleValues (itsExprNode, id, directions); return directions; } Array DirectionEngine::getArrayDouble (const TableExprId& id, Bool riseSet, Bool asDirCos) { DebugAssert (id.byRow(), AipsError); Array res (getDirections(id)); // Get epochs and positions if given. Array eps(IPosition(1,1)); if (itsEpochEngine) { Array arr = itsEpochEngine->getEpochs (id); eps.reference (itsEpochEngine->getEpochs (id)); } Array pos(IPosition(1,1)); if (itsPositionEngine) { pos.reference (itsPositionEngine->getPositions (id)); } // Convert the direction to the given type for all epochs and positions. Array out; if (res.size() > 0 && eps.size() > 0 && pos.size() > 0) { // 2 or 3 values per MDirection IPosition shape(1, asDirCos ? 3:2); // Only add the other axes if one of them has multiple values. if (res.size() > 1 || eps.size() > 1 || pos.size() > 1) { shape.append (res.shape()); shape.append (eps.shape()); shape.append (pos.shape()); } out.resize (shape); double* outPtr = out.data(); for (Array::const_contiter posIter = pos.cbegin(); posIter != pos.cend(); ++posIter) { // Convert to desired position. if (itsPositionEngine) { itsFrame.resetPosition (*posIter); } for (Array::const_contiter epsIter = eps.cbegin(); epsIter != eps.cend(); ++epsIter) { // Convert to desired epoch. if (itsEpochEngine) { itsFrame.resetEpoch (*epsIter); } uInt hIndex = 0; for (Array::const_contiter resIter = res.cbegin(); resIter != res.cend(); ++resIter, ++hIndex) { if (riseSet) { calcRiseSet (*resIter, *posIter, *epsIter, (hIndex md (mdir.getValue().getValue()); *outPtr++ = md[0]; *outPtr++ = md[1]; *outPtr++ = md[2]; } else { // Get angles as radians. Vector md (mdir.getValue().get()); *outPtr++ = md[0]; *outPtr++ = md[1]; } } } } } } return out; } void DirectionEngine::calcRiseSet (const MDirection& dir, const MPosition& pos, const MEpoch& epoch, double h, double& rise, double& set) { // See http://www.stjarnhimlen.se/comp/riset.html double lat = pos.getValue().get()[2]; // latitude double start = floor(epoch.getValue().get() + 0.000001); // Start of day is the offset for rise and set. MEpoch off = MEpoch(Quantity(start, "d"), MEpoch::Types(MEpoch::UTC | MEpoch::RAZE)); // Use noon in the MeasFrame. // Note that for Sun and Moon an iteration can be done using the // obtained rise and set time as the new time in the MeasFrame, // which makes the calculation more accurate. int ab = fillRiseSet (start+0.5, dir, lat, h, off, &rise, &set); if (ab > 0) { // Always below. set = start; rise = set + 1; } else if (ab < 0) { // Always above. rise = start; set = rise + 1; } else { // Note that sometimes Measures has to choose between 2 days // due to the 4 minutes difference between earth and sidereal day. // For the period between (about) 21-Mar and 21-Sep it chooses wrongly. // So adjust rise and set if needed (sidereal day is 236 sec shorter). if (rise < start) rise += 1 - 236./86400; if (set < start) set += 1 - 236./86400; // If set= 1) { return 1; } else if (ct <= -1) { return -1; } ct = acos(ct); // Get RA normalized between 0 and 2pi. MDirection::Ref ref1(MDirection::APP, itsFrame); MDirection app = MDirection::Convert(MDirection::APP, ref1) (dir); double normra = MVAngle(app.getValue().get()[0])(0).radian(); MEpoch::Ref ref(MEpoch::LAST, itsFrame, off); if (rise) { double t = normra - ct; Quantity tq = MVTime(Quantity(t, "rad")).get(); MEpoch tr = MEpoch::Convert (MEpoch(tq, ref), MEpoch::UTC)(); *rise = tr.getValue().get(); } if (set) { double t = normra + ct; Quantity tq = MVTime(Quantity(t, "rad")).get(); MEpoch tr = MEpoch::Convert (MEpoch(tq, ref), MEpoch::UTC)(); *set = tr.getValue().get(); } return 0; } /* # From old measures.g: # Rise/set sidereal time(coord, elev) # ct = (sin(el=5deg) - sin(dec)*sin(lat)) / (cos(dec) * cos(lat)) rise = ra - acos(ct) set = ra + acos(ct) const public.rise := function(crd, ev='5deg') { if (!is_measure(crd)) fail('No rise/set coordinates specified'); if (!is_measure(private.getwhere())) { dq.errorgui('Specify where you are in Frame'); fail('No rise/set Frame->Where specified'); }; private.fillnow(); hd := public.measure(crd, 'hadec'); c := public.measure(crd, 'app'); if (!is_measure(hd) || !is_measure(c)) fail('Cannot get HA for rise/set')\ ; ps := private.getwhere(); ct := dq.div(dq.sub(dq.sin(ev), dq.mul(dq.sin(hd.m1), dq.sin(ps.m1))), dq.mul(dq.cos(hd.m1), dq.cos(ps.m1))); if (ct.value >= 1) return "below below"; if (ct.value <= -1) return "above above"; a := dq.acos(ct); return [rise=dq.sub(dq.norm(c.m0, 0), a), set=dq.add(dq.norm(c.m0, 0), a)] } # # Rise/set times(coord, elev) # const public.riseset := function(crd, ev='5deg') { a := public.rise(crd, ev); if (is_fail(a)) fail; if (is_string(a)) { return [solved=F, rise=[last=a[1], utc=a[1]], }; x := a; ofe := public.measure(private.framestack['epoch'], 'utc'); if (!is_measure(ofe)) ofe := public.epoch('utc', 'today'); for (i in 1:2) { x[i] := public.measure(public.epoch('last', dq.totime(a[i]), off=public.epoch('r_utc', dq.add(ofe.m0, '0.5d'))), 'utc'); }; return [solved=T, rise=[last=public.epoch('last', dq.totime(a[1])), utc=x[1]], set=[last=public.epoch('last', dq.totime(a[2])), utc=x[2]]]; } */ } //end namespace casacore-3.7.1/meas/MeasUDF/DirectionEngine.h000066400000000000000000000206401476623553700207160ustar00rootroot00000000000000//# DirectionEngine.h: Engine for TaQL UDF Direction conversions //# Copyright (C) 2011 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEAS_DIRECTIONENGINE_H #define MEAS_DIRECTIONENGINE_H //# Includes #include #include #include #include #include namespace casacore { //# Forward declarations class EpochEngine; class PositionEngine; // // Engine for TaQL UDF Direction conversions // // // // // //# Classes you should understand before using this one. //
      • EngineBase // // // DirectionEngine defines Engines (user defined functions) that can be // used in TaQL to convert Measures for directions. // In this way such derived values appear to be ordinary TaQL functions. // // In TaQL these functions can be called like: // // meas.dir (toref, directions, epochs, positions) // meas.j2000 (directions, epochs, positions) // meas.dircos (toref, directions, epochs, positions) // meas.riset (directions, epochs, positions) // // The first two result in angles, the third in direction cosines, while // the fourth returns the rise/set time of sources as datetimes. // Note that the second form is a shorthand for // meas.dir('j2000', ...). There are more such functions. // The exact number of arguments depends on how they are specified. //
          //
        • toref is a single constant string defining the // reference frame to convert to. Note it should be omitted for // the functions (e.g., meas.j2000) with an implicit destination // reference frame. //
        • directions is one or more directions which can be // given in several ways. //
            //
          • An array of directions, each 2 angles or 3 directions cosines. // It can be given as a single list or a multi-dim array. The choice // between angles and direction cosines is based on the size of the // first dimension. If divisible by 2, it is angles, by 3 is // direction cosines, otherwise an error. Thus a list of 6 elements // defines 3 directions with 2 angles each (default in radians). //
            It can be followed by a string defining the source // reference frame. It defaults to 'J2000'. //
          • If a single constant direction is used, it can be given as // 2 (for angles) or 3 (for direction cosines) scalar values, // followed by the optional source reference frame. //
          • The name of a column in a table or a subset of it such as // DELAY_DIR[0,]. Often this is a TableMeasures column // which is recognized as such, also its source reference frame. // If such a column is given as part of an expression, it will not // be recognized as a TableMeasures column and its reference frame // should be given. //
          • As a list of (case-insensitive) names of known sources // such as 'CasA' or 'SUN'. //
          //
        • epochs can be given as shown in class EpochEngine. //
        • positions can be given as shown in class PositionEngine. //
        // Note that epochs and positions are only needed if required by the // conversion from source reference frame to destination reference frame. // For example, J2000 to/from APP needs them, but not J2000 to/from B1950. // // The result of the function is an array with shape [2|3,dir,epoch,pos] // where the last 3 elements are the shapes of these arguments. They are // omitted if all of them have length 1. // // Futhermore, it is possible to get the rise/set date/time of a source // given the source direction, date and position on earth. These functions // return data with type double and unit d (day). // If the source is visible all day, the rise time is 0 and set time 1. // If the source is not visible at all, the rise time is 1 and set time 0. // For the sun and the moon it is possible to add a suffix to the name // telling if and which edge and twilight should be used. For the sun and // moon the default is -UR (the upper edge with refraction correction). //
        // // // // Get rise/set time of the upper edge of the sun in the coming month // // at the WSRT site. // meas.riseset ('SUN', date()+[0:31], 'WSRT') // // Get the apparent coordinates of CasA for the given date and position. // meas.dircos ('APP', 'CasA', 12mar2015, [52deg,5deg]) // // Get the J2000 coordinates (in degrees) of CygA. // meas.j2000 ('cyga') deg // // // // It makes it possible to easily handle measures in TaQL. // class DirectionEngine: public MeasEngine { public: DirectionEngine(); virtual ~DirectionEngine(); // Get the values. // The first Bool tells if rise/set times have to be calculated. // The second Bool tells if direction cosines have to be calculated. Array getArrayDouble (const TableExprId& id, Bool riseSet, Bool asDirCos); // Get the directions. Array getDirections (const TableExprId& id); // Handle the argument(s) giving the input directions and reference type. // The direction can be a column in a table. void handleDirection (const std::vector& args, uInt& argnr, Bool riseSet, Bool asDirCos); // Set the MeasConvert object. void setConverter (MDirection::Types toType); // Set the possible epoch engine. // It can be done only once. void setEpochEngine (EpochEngine& engine); // Set the possible position engine. // It can be done only once. void setPositionEngine (PositionEngine& engine); private: void handleScalars (const TENShPtr& e1, const TENShPtr& e2, const TENShPtr& e3); void handleNames (const TENShPtr& operand); virtual void handleValues (TableExprNode& operand, const TableExprId& id, Array& directions); // Calucate the rise and set time of a source for a given position and // epoch. Argument h defines the possible edge of sun/moon. void calcRiseSet (const MDirection& dir, const MPosition& pos, const MEpoch& epoch, double h, double& rise, double& set); int fillRiseSet (double epoch, const MDirection& dir, double lat, double h, const MEpoch& off, double* rise, double* set); //# Data members. MeasFrame itsFrame; //# frame used by converter MDirection::Convert itsConverter; Vector itsH; //# diff for sun or moon EpochEngine* itsEpochEngine; PositionEngine* itsPositionEngine; }; } //end namespace #endif casacore-3.7.1/meas/MeasUDF/DirectionUDF.cc000066400000000000000000000130101476623553700202560ustar00rootroot00000000000000//# DirectionUDF.cc: TaQL UDF for Direction conversions //# Copyright (C) 2011 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include namespace casacore { DirectionUDF::DirectionUDF (FuncType type, Bool riseSet) : itsType (type), itsRiseSet (riseSet) {} UDFBase* DirectionUDF::makeDIR (const String&) { return new DirectionUDF (DIRECTION); } UDFBase* DirectionUDF::makeDIRCOS (const String&) { return new DirectionUDF (DIRCOS); } UDFBase* DirectionUDF::makeHADEC (const String&) { return new DirectionUDF (HADEC); } UDFBase* DirectionUDF::makeAZEL (const String&) { return new DirectionUDF (AZEL); } UDFBase* DirectionUDF::makeAPP (const String&) { return new DirectionUDF (APP); } UDFBase* DirectionUDF::makeJ2000 (const String&) { return new DirectionUDF (J2000); } UDFBase* DirectionUDF::makeB1950 (const String&) { return new DirectionUDF (B1950); } UDFBase* DirectionUDF::makeECL (const String&) { return new DirectionUDF (ECLIPTIC); } UDFBase* DirectionUDF::makeGAL (const String&) { return new DirectionUDF (GALACTIC); } UDFBase* DirectionUDF::makeSGAL (const String&) { return new DirectionUDF (SUPERGALACTIC); } UDFBase* DirectionUDF::makeITRF (const String&) { return new DirectionUDF (ITRF); } UDFBase* DirectionUDF::makeRISESET (const String&) { return new DirectionUDF (HADEC, True); } void DirectionUDF::setup (const Table&, const TaQLStyle&) { if (operands().size() < 1) { throw AipsError ("No arguments given in a MEAS function"); } // Get the 'to' reference type. // Determine the argnr of the epoch. uInt argnr = 0; if (itsType == HADEC) { itsRefType = MDirection::HADEC; } else if (itsType == AZEL) { itsRefType = MDirection::AZEL; } else if (itsType == APP) { itsRefType = MDirection::APP; } else if (itsType == J2000) { itsRefType = MDirection::J2000; } else if (itsType == B1950) { itsRefType = MDirection::B1950; } else if (itsType == ECLIPTIC) { itsRefType = MDirection::ECLIPTIC; } else if (itsType == GALACTIC) { itsRefType = MDirection::GALACTIC; } else if (itsType == SUPERGALACTIC) { itsRefType = MDirection::SUPERGAL; } else if (itsType == ITRF) { itsRefType = MDirection::ITRF; } else { itsEngine.handleMeasType (operands()[0], True); itsRefType = itsEngine.refType(); argnr = 1; } // Get the directions. if (operands().size() <= argnr) { throw AipsError ("No direction given in a MEAS function"); } itsEngine.handleDirection (operands(), argnr, itsRiseSet, itsType==DIRCOS); // Handle possible Epoch arguments. if (operands().size() > argnr) { itsEpochEngine.handleEpoch (operands(), argnr); itsEngine.setEpochEngine (itsEpochEngine); } // Handle possible Position arguments. if (operands().size() > argnr) { itsPositionEngine.handlePosition (0, operands(), argnr); itsEngine.setPositionEngine (itsPositionEngine); } if (operands().size() > argnr) { throw AipsError ("Too many arguments given in a MEAS function"); } itsEngine.setConverter (itsRefType); // Set datatype, shape, unit, etc. if (itsRiseSet) { setDataType (TableExprNodeRep::NTDate); } else { setDataType (TableExprNodeRep::NTDouble); } const IPosition& shape = itsEngine.shape(); if (shape.size() > 0) { if (shape.product() == 1) { setNDim (0); // scalar } else { setShape (shape); } } else { setNDim (itsEngine.ndim()); } setUnit (itsEngine.unit().getName()); setConstant (itsEngine.isConstant()); setAttributes (itsEngine.makeAttributes (itsRefType, itsType)); } Double DirectionUDF::getDouble (const TableExprId& id) { return getArrayDouble(id).array().data()[0]; } MArray DirectionUDF::getArrayDouble (const TableExprId& id) { return MArray(itsEngine.getArrayDouble (id, itsRiseSet, itsType==DIRCOS)); } MArray DirectionUDF::getArrayDate (const TableExprId& id) { Array res = itsEngine.getArrayDouble (id, itsRiseSet, False); Array dates(res.shape()); for (uInt i=0; i(dates); } } //end namespace casacore-3.7.1/meas/MeasUDF/DirectionUDF.h000066400000000000000000000111441476623553700201260ustar00rootroot00000000000000//# DirectionUDF.h: TaQL UDFs for Direction conversions //# Copyright (C) 2011 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEAS_DIRECTIONUDF_H #define MEAS_DIRECTIONUDF_H //# Includes #include #include #include #include #include namespace casacore { // // TaQL UDFs for Direction conversions. // // // // // //# Classes you should understand before using this one. //
      • UDFBase // // // DirectionUDF defines UDFs (user defined functions) that can be used in TaQL // to convert Measures for directions. // Special functions exist to convert to hourangle and azimuth/elevation. // In this way such derived values appear to be ordinary TaQL functions. // // A function is called like: // // meas.dir (toref, dir, time, pos) // meas.dir (toref, dir, time, pos) // For example, // meas.dir ('B1950', [2rad,1rad]) // meas.dir ('B1950', [[2rad,1rad], 'J2000']) // meas.dir ('APP', 'MOON', TIME, [[5d12m, 52deg, 11m], 'WGS84']) // meas.dir ('APP', 'MOON', TIME, POSITION) // Or // meas.dir (toref, fromref, dir, pos, time) // //
          //
        • // toref is a single constant string. //
        • // dir can have various value types. A single numeric array is // a series of RA,DEC in J2000. If given as a set, the last argument of the // set can be the reference types of the values in the set. The values can // be strings (indicating planetary objects) or value pairs giving lon,lat. // The default reference type is J2000. //
        // All functions have data type double and unit radian. //
        // // It makes it possible to handle measures in TaQL. // class DirectionUDF: public UDFBase { public: // Define the possible function types. enum FuncType {DIRECTION, DIRCOS, HADEC, AZEL, APP, J2000, B1950, ECLIPTIC, GALACTIC, SUPERGALACTIC, ITRF}; // Create for the given function type. // The Bools tell if rise/set times have to be calculated. explicit DirectionUDF (FuncType, Bool riseSet=False); // Function to create an object. static UDFBase* makeDIR (const String&); static UDFBase* makeDIRCOS (const String&); static UDFBase* makeHADEC (const String&); static UDFBase* makeAZEL (const String&); static UDFBase* makeAPP (const String&); static UDFBase* makeJ2000 (const String&); static UDFBase* makeB1950 (const String&); static UDFBase* makeECL (const String&); static UDFBase* makeGAL (const String&); static UDFBase* makeSGAL (const String&); static UDFBase* makeITRF (const String&); static UDFBase* makeRISESET (const String&); // Setup the object. virtual void setup (const Table&, const TaQLStyle&); // Get the value. virtual Double getDouble (const TableExprId& id); virtual MArray getArrayDouble (const TableExprId& id); virtual MArray getArrayDate (const TableExprId& id); private: //# Data members. DirectionEngine itsEngine; EpochEngine itsEpochEngine; PositionEngine itsPositionEngine; FuncType itsType; MDirection::Types itsRefType; Bool itsRiseSet; //# True = calculate rise/set time }; } //end namespace #endif casacore-3.7.1/meas/MeasUDF/DopplerEngine.cc000066400000000000000000000224721476623553700205460ustar00rootroot00000000000000//# DopplerEngine.cc: Engine for TaQL UDF Doppler conversions //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { DopplerEngine::~DopplerEngine() {} void DopplerEngine::handleDoppler (vector& args, uInt& argnr, Bool allowRadVel, Bool allowFreq) { // Default type is RADIO. itsRefType = MDoppler::RADIO; // See if the values are given as radial velocity, frequency or doppler. // If given as radial velocity or frequency, a measure type must be given, // thus 2 arguments with the 2nd as a string. // TODO: use TaQL attributes. so a single argument is possible!! BaseEngine* enginePtr = 0; Bool restConst = True; if (args.size() >= argnr && ! args[argnr]->unit().empty()) { if (allowRadVel) { try { itsRadVelEngine.reset (new RadialVelocityEngine()); uInt nargnr = argnr; itsRadVelEngine->handleRadialVelocity (args, nargnr); if (nargnr > argnr) { itsType = RADVEL; argnr = nargnr; // No conversion. itsRadVelEngine->setConverter (itsRadVelEngine->refType()); enginePtr = itsRadVelEngine.get(); } } catch (const AipsError&) { } } if (enginePtr == 0 && allowFreq) { // No radial velocity; try as frequency (if allowed). try { itsFreqEngine.reset (new FrequencyEngine()); uInt nargnr = argnr; itsFreqEngine->handleFrequency (args, nargnr); if (nargnr > argnr) { itsType = FREQ; argnr = nargnr; enginePtr = itsFreqEngine.get(); } } catch (const AipsError&) { } // The rest frequency must be given hereafter. if (enginePtr) { handleRestFreq (args, argnr); restConst = ! itsConstRestFreqs.empty(); } } } if (enginePtr) { itsShape = enginePtr->shape(); itsNDim = enginePtr->ndim(); if (enginePtr->isConstant() && restConst) { handleValues (itsExprNode, 0, itsConstants); } } else { // No match, so it must be a doppler value. itsType = DOPPLER; // Check if the value is a double without a unit. if (!args[argnr]->isReal()) { throw AipsError ("Invalid doppler value given in a MEAS function"); } if (! args[argnr]->unit().empty()) { throw AipsError ("A doppler value given in a MEAS function cannot have a unit"); } // Values can be given as [t1,t2,...],reftype uInt nargnr = argnr+1; // See if there is a reference type. if (args.size() > nargnr && args[nargnr]->dataType() == TableExprNodeRep::NTString) { if (handleMeasType (args[nargnr], True)) { nargnr++; } } handleMeasArray (args[argnr]); argnr = nargnr; } // Set the output shape, etc. adaptForConstant (itsConstants.shape()); } void DopplerEngine::setConverter (MDoppler::Types toType) { MDoppler::Ref ref(toType); itsConverter = MDoppler::Convert (toType, ref); } Array DopplerEngine::getDopplers (const TableExprId& id) { if (itsConstants.size() > 0) { return itsConstants; } if (!itsMeasScaCol.isNull()) { return Vector(1, itsMeasScaCol(id.rownr())); } else if (!itsMeasArrCol.isNull()) { return itsMeasArrCol(id.rownr()); } Array dopplers; handleValues (itsExprNode, id, dopplers); return dopplers; } void DopplerEngine::handleRestFreq (vector& args, uInt& argnr) { if (args.size() <= argnr) { throw AipsError("No rest frequency given after the frequency in MEAS.DOPPLER"); } itsRestFreqNode = args[argnr]; if (itsRestFreqNode->dataType() == TableExprNodeRep::NTString) { // It is the name of a line. handleLine (itsRestFreqNode); argnr++; } else { if (! itsRestFreqNode->isReal()) { throw AipsError("Rest frequency in MEAS.DOPPLER does not have a real value"); } if (! (itsRestFreqNode->unit().empty() || itsRestFreqNode->unit() == Unit("Hz"))) { throw AipsError("The rest frequency unit in MEAS.DOPPLER is invalid"); } // Handle the rest frequency argument. argnr++; if (itsRestFreqNode->isConstant()) { itsConstRestFreqs = getRestFreqs (0); } } } void DopplerEngine::handleLine (const TENShPtr& operand) { // For the time being the line names have to be constants. // In the future, it could be a table column. if (! operand->isConstant()) { throw AipsError ("A line name used as frequency in a MEAS function" " must be a constant string"); } Array names = operand->getStringAS(0).array(); itsConstRestFreqs.resize (names.shape()); for (uInt i=0; i DopplerEngine::getRestFreqs (const TableExprId& id) { // Use the constant values if present. if (! itsConstRestFreqs.empty()) { return itsConstRestFreqs; } // Get the values. Array values = itsRestFreqNode->getDoubleAS(id).array(); // Turn them into to MVFrequencies. Array freqs(values.shape()); Unit unit = itsRestFreqNode->unit(); if (unit.empty()) { unit = "Hz"; } Quantity q(0, unit); for (uInt i=0; i& dopplers) { if (itsType == DOPPLER) { Array values = operand.getDoubleAS(id).array(); dopplers.resize (values.shape()); Quantity q(0, itsInUnit); for (uInt i=0; i vels = itsRadVelEngine->getRadialVelocities (id); dopplers.resize (vels.shape()); for (uInt i=0; i freqs = itsFreqEngine->getFrequencies (id); Array rests = getRestFreqs (id); uInt incf = 1; uInt incr = 1; uInt size = freqs.size(); if (freqs.size() == 1) { incf = 0; size = rests.size(); dopplers.resize (rests.shape()); } else if (rests.size() == 1) { incr = 0; dopplers.resize (freqs.shape()); } else if (freqs.shape().isEqual(rests.shape())) { dopplers.resize (freqs.shape()); } else { throw AipsError("Frequencies and rest frequencies in MEAS.DOPPLER must have same shape"); } uInt inxf = 0; uInt inxr = 0; for (uInt i=0; i DopplerEngine::getArrayDouble (const TableExprId& id) { DebugAssert (id.byRow(), AipsError); Array res (getDopplers(id)); // Convert the doppler to the given type. Array out; if (res.size() > 0) { IPosition shape = res.shape(); out.resize (shape); double* outPtr = out.data(); for (Array::const_contiter resIter = res.cbegin(); resIter != res.cend(); ++resIter) { itsConverter.setModel (*resIter); MDoppler mf = itsConverter(); *outPtr++ = mf.getValue().getValue(); } } return out; } } //end namespace casacore-3.7.1/meas/MeasUDF/DopplerEngine.h000066400000000000000000000122541476623553700204050ustar00rootroot00000000000000//# DopplerEngine.h: Engine for TaQL UDF Doppler conversions //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEAS_DOPPLERENGINE_H #define MEAS_DOPPLERENGINE_H //# Includes #include #include #include #include #include #include #include namespace casacore { //# Forward Declarations class RadialVelocityEngine; class FrequencyEngine; // // Engine for TaQL UDF Doppler conversions // // // // // //# Classes you should understand before using this one. //
      • EngineBase // // // DopplerEngine defines Engines (user defined functions) that can be used // in TaQL to convert Measures for dopplers. // In this way such derived values appear to be ordinary TaQL functions. // // Doppler conversions require a MeasFrame containing sky direction, // epoch and position on earth. // In TaQL these functions can be called like: // // meas.rv ('TOPO', 1 'm/s', 'LSRK', 'CasA', date(), // [1e6m,1e6m,1e6m], 'WGS84') // // which converts the dopplers from LSRK to TOPO. //
          //
        • // toref is a single constant string. //
        • // pos can have various value types. A single numeric array is // a series of RA,DEC in J2000. If given as a set, the last argument of the // set can be the reference types of the values in the set. The values can // be strings (indicating planetary objects) or value pairs giving lon,lat. // The default reference type is J2000. //
        // All such functions return data with type double and unit Hz. // // Dopplers can be given like: // [v1,v2,...], fromRef // where fromRef is the reference type. // // A doppler can also be a table column which usually knows its type. // It can also be an expression (e.g. DOPPLER[0,]) which also knows the type. //
        // // It makes it possible to handle measures in TaQL. // class DopplerEngine: public MeasEngine { public: enum Type {DOPPLER, FREQ, RADVEL}; DopplerEngine() {} virtual ~DopplerEngine(); // Handle a possible rest frequency. // False is returned if it appears to be no rest frequency. Bool handleRestFreq (const TENShPtr&); // Get the values. Array getArrayDouble (const TableExprId& id); // Get the dopplers. Array getDopplers (const TableExprId& id); // Handle the argument(s) giving the input dopplers and reference type. // The doppler can be a column in a table. // If 'proper' is True, it is tested if a proper doppler is given // (with proper type). If not. False is returned. // The 'allow' arguments tell if the doppler can be specified by means of // a radial velocity or freq/restfreq. void handleDoppler (std::vector& args, uInt& argnr, Bool allowRadVel, Bool allowFreq); // Set the MeasConvert object. void setConverter (MDoppler::Types toType); private: void handleRestFreq (vector& args, uInt& argnr); void handleLine (const TENShPtr& operand); // Handle the values. virtual void handleValues (TableExprNode& operand, const TableExprId& id, Array& dopplers); Array getRestFreqs (const TableExprId& id); //# Data members. Type itsType; MDoppler::Convert itsConverter; std::shared_ptr itsRadVelEngine; std::shared_ptr itsFreqEngine; Array itsConstRestFreqs; TENShPtr itsRestFreqNode; }; } //end namespace #endif casacore-3.7.1/meas/MeasUDF/DopplerUDF.cc000066400000000000000000000053301476623553700177510ustar00rootroot00000000000000//# DopplerUDF.cc: TaQL UDF for Doppler conversions //# Copyright (C) 2018 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include namespace casacore { DopplerUDF::DopplerUDF() {} UDFBase* DopplerUDF::makeDOPPLER (const String&) { return new DopplerUDF(); } void DopplerUDF::setup (const Table&, const TaQLStyle&) { if (operands().size() < 1) { throw AipsError ("No arguments given in a MEAS.DOPPLER function"); } // Get the 'to' reference type. itsEngine.handleMeasType (operands()[0], True); itsRefType = itsEngine.refType(); uInt argnr = 1; if (operands().size() <= argnr) { throw AipsError ("No values given in a MEAS.DOPPLER function"); } itsEngine.handleDoppler (operands(), argnr, True, True); if (operands().size() > argnr) { throw AipsError ("Too many arguments given in a MEAS.DOPPLER function"); } itsEngine.setConverter (itsRefType); // Set datatype, shape, unit, etc. setDataType (TableExprNodeRep::NTDouble); IPosition shape (itsEngine.shape()); if (shape.size() > 0) { if (shape.product() == 1) { setNDim (0); // scalar } else { setShape (shape); } } else { setNDim (itsEngine.ndim()); } setConstant (itsEngine.isConstant()); setAttributes (itsEngine.makeAttributes (itsRefType)); } Double DopplerUDF::getDouble (const TableExprId& id) { return getArrayDouble(id).array().data()[0]; } MArray DopplerUDF::getArrayDouble (const TableExprId& id) { return MArray(itsEngine.getArrayDouble (id)); } } //end namespace casacore-3.7.1/meas/MeasUDF/DopplerUDF.h000066400000000000000000000074641476623553700176250ustar00rootroot00000000000000//# DopplerUDF.h: TaQL UDFs for Doppler conversions //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEAS_DOPPLERUDF_H #define MEAS_DOPPLERUDF_H //# Includes #include #include #include namespace casacore { // // TaQL UDFs for Doppler conversions. // // // // // //# Classes you should understand before using this one. //
      • UDFBase // // // DopplerUDF defines UDFs (user defined functions) that can be used in TaQL // to convert Measures for dopplers. // Special functions exist to convert to hourangle and azimuth/elevation. // In this way such derived values appear to be ordinary TaQL functions. // // A function is called like: // // meas.dir (toref, dir, time, pos) // meas.dir (toref, dir, time, pos) // For example, // meas.dir ('B1950', [2rad,1rad]) // meas.dir ('B1950', [[2rad,1rad], 'J2000']) // meas.dir ('APP', 'MOON', TIME, [[5d12m, 52deg, 11m], 'WGS84']) // meas.dir ('APP', 'MOON', TIME, POSITION) // Or // meas.dir (toref, fromref, dir, pos, time) // //
          //
        • // toref is a single constant string. //
        • // dir can have various value types. A single numeric array is // a series of RA,DEC in J2000. If given as a set, the last argument of the // set can be the reference types of the values in the set. The values can // be strings (indicating planetary objects) or value pairs giving lon,lat. // The default reference type is J2000. //
        // All functions have data type double and no unit. //
        // // It makes it possible to handle measures in TaQL. // class DopplerUDF: public UDFBase { public: // Create for the given function type. explicit DopplerUDF(); // Function to create an object. static UDFBase* makeDOPPLER (const String&); // Setup the object. virtual void setup (const Table&, const TaQLStyle&); // Handle the value arguments as doppler or radial velocity. // Optionally frequency is also allowed, in which case a rest frequency // must also be given. // It returns a pointer to the engine representing the given value. BaseEngine* handleValueArgs (vector& args, uInt& argnr, Bool allowFreq); // Get the value. virtual Double getDouble (const TableExprId& id); virtual MArray getArrayDouble (const TableExprId& id); private: //# Data members. DopplerEngine itsEngine; MDoppler::Types itsRefType; }; } //end namespace #endif casacore-3.7.1/meas/MeasUDF/EarthMagneticEngine.cc000066400000000000000000000354231476623553700216540ustar00rootroot00000000000000//# EarthMagneticEngine.cc: Engine for TaQL UDF EarthMagnetic conversions //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { EarthMagneticEngine::EarthMagneticEngine() : itsValueType (0), itsToValueType (0), itsAsLOS (False), itsAsLong (False), itsUseModel (False), itsConvertModel (False), itsEpochEngine (0), itsPositionEngine (0), itsDirectionEngine (0) { itsRefType = MEarthMagnetic::ITRF; } EarthMagneticEngine::~EarthMagneticEngine() {} void EarthMagneticEngine::handleEarthMagnetic (vector& args, uInt& argnr) { // Types are unknown. itsRefType = MEarthMagnetic::ITRF; itsValueType = 0; uInt nargnr = argnr+1; Bool asScalar = False; if (! args[argnr]->isReal()) { throw AipsError ("Non-real EarthMagnetic values given in a MEAS " "function"); } // Normally earthmagnetics must be given in an array, but a single one // can be 3 scalars. if (args.size() > nargnr+1 && args[argnr]->isReal() && args[argnr]->valueType() == TableExprNodeRep::VTScalar && args[nargnr]->isReal() && args[nargnr]->valueType() == TableExprNodeRep::VTScalar && args[nargnr+1]->isReal() && args[nargnr+1]->valueType() == TableExprNodeRep::VTScalar) { asScalar = True; nargnr += 2; } // See if there is a reference type. if (args.size() > nargnr && args[nargnr]->dataType() == TableExprNodeRep::NTString) { handleMeasType (args[nargnr], False); if (itsRefType == MEarthMagnetic::IGRF) { throw AipsError ("The 'from' EarthMagnetic reference type given in " "a MEAS function cannot be IGRF; " "use function MEAS.IGRF instead"); } nargnr++; } // Process as 3 scalars or as array. if (asScalar) { handleScalars (args[argnr], args[argnr+1], args[argnr+2]); } else { // Get the EarthMagnetic arguments. handleMeasArray (args[argnr]); } // Skip the arguments handled. argnr = nargnr; } void EarthMagneticEngine::handleHeight (TENShPtr& operand) { // Heights must be real values with an optional length unit. if (! operand->isReal()) { throw AipsError("Heights in a MEAS.IGRF function must be real values"); } // Adapt units to meters. TableExprNodeUnit::adaptUnit (operand, "m"); itsExprNode = operand; itsIsConst = operand->isConstant(); } String EarthMagneticEngine::stripMeasType (const String& typex) { itsValueType = 0; String type(typex); unsigned lens = type.size(); const char* suffices[] = {"XYZ", "AL", "ANG", "ANGLES", "LEN", "LENGTH"}; const char* units[] = {"nT", "", "rad", "rad", "nT", "nT"}; int vtypes[] = {3, -3, 2, 2, 1, 1}; for (unsigned i=0; i suf.size() && type.substr(lens-suf.size()) == suf) { itsValueType = vtypes[i]; itsInUnit = units[i]; type = type.substr(0, lens-suf.size()); break; } } return type; } void EarthMagneticEngine::deriveAttr (const Unit& unit, Int) { // Check if the unit is length or angle. if (unit.empty()) { itsInUnit = "rad"; itsValueType = -3; // llh as angle and flux } else { itsInUnit = unit; Quantity q(1., itsInUnit); if (q.isConform ("T")) { itsValueType = 3; // xyz in Tesla units } else if (q.isConform ("rad")) { itsValueType = -3; // llh as angle and flux } else { throw AipsError ("Invalid unit given for an EarthMagnetic value" " in a MEAS function (no fluxdensity or angle)"); } } } void EarthMagneticEngine::setValueType (Int valueType) { itsValueType = valueType; } void EarthMagneticEngine::handleScalars (const TENShPtr& e1, const TENShPtr& e2, const TENShPtr& e3) { if (! (e1->isConstant() && e2->isConstant() && e3->isConstant())) { throw AipsError ("Scalar values given as EarthMagnetic in a MEAS " "function must be constant values"); } deriveAttr (e1->unit(), 0); double v1 = e1->getDouble(0); double v2 = e2->getDouble(0); double vh = e3->getDouble(0); Unit u1 = e1->unit(); Unit u2 = e2->unit(); Unit uh = e3->unit(); if (u1.empty()) u1 = itsInUnit; if (u2.empty()) u2 = itsInUnit; if (uh.empty()) uh = "nT"; itsConstants.resize (IPosition(1,1)); itsConstants.data()[0] = makeEarthMagnetic(Quantity(vh, uh), Quantity(v1, u1), Quantity(v2, u2)); } MEarthMagnetic EarthMagneticEngine::makeEarthMagnetic (const Quantity& qh, const Quantity& q1, const Quantity& q2) const { if (itsValueType == 3) { Unit m("nT"); return MEarthMagnetic (MVEarthMagnetic(q1.getValue(m), q2.getValue(m), qh.getValue(m)), itsRefType); } return MEarthMagnetic (MVEarthMagnetic(qh, q1, q2), itsRefType); } void EarthMagneticEngine::handleValues (TableExprNode& operand, const TableExprId& id, Array& earthMagnetics) { Array values; values = operand.getArrayDouble(id); IPosition shape = values.shape(); if (shape[0] % 3 != 0) { throw AipsError ("Number of values in an EarthMagnetic in a MEAS " "function should be a multiple of 3"); } IPosition emShape; if (shape[0] == 3 && shape.size() > 1) { emShape = shape.getLast (shape.size() - 1); } else { emShape = shape; emShape[0] /= 3; } earthMagnetics.resize (emShape); Quantity qh(0, itsInUnit); Quantity q1(0, itsInUnit); Quantity q2(0, itsInUnit); if (itsValueType == -3) { qh = Quantity(0, "nT"); } Bool delIt; const Double* valVec = values.getStorage (delIt); MEarthMagnetic* emVec = earthMagnetics.data(); for (uInt i=0; i EarthMagneticEngine::getEarthMagnetics (const TableExprId& id) { if (itsConstants.size() > 0) { return itsConstants; } if (!itsMeasArrCol.isNull()) { return itsMeasArrCol(id.rownr()); } // Read from expression. Array earthMagnetics; handleValues (itsExprNode, id, earthMagnetics); return earthMagnetics; } Array EarthMagneticEngine::getHeights (const TableExprId& id) { return itsExprNode.getDoubleAS(id).array(); } Array EarthMagneticEngine::getArrayDouble (const TableExprId& id) { DebugAssert (id.byRow(), AipsError); // Get epochs and positions if given. Array eps(IPosition(1,1)); if (itsEpochEngine) { eps.reference (itsEpochEngine->getEpochs (id)); } Array pos(IPosition(1,1)); if (itsPositionEngine) { pos.reference (itsPositionEngine->getPositions (id)); } // Get the ems or heights/directions and determine the shape from it. IPosition vshape; Array ems; Array dirs; Array heights; const MEarthMagnetic* ePtr = 0; const MDirection* dPtr = 0; const Double* hPtr = 0; size_t nval1 = 1; size_t nval2 = 1; if (itsUseModel) { heights.reference (getHeights (id)); if (! heights.contiguousStorage()) heights = heights.copy(); hPtr = heights.data(); vshape = heights.shape(); nval1 = heights.size(); dirs.reference (itsDirectionEngine->getDirections (id)); if (! dirs.contiguousStorage()) dirs = dirs.copy(); dPtr = dirs.data(); nval2 = dirs.size(); if (nval2 > 1) vshape.append (dirs.shape()); } else { ems.reference (getEarthMagnetics(id)); if (! ems.contiguousStorage()) ems = ems.copy(); ePtr = ems.data(); vshape = ems.shape(); nval2 = ems.size(); cout<<"ems="< out; if (nval1==0 || nval2==0 || eps.empty() || pos.empty()) { return out; } // Determine the output shape. IPosition shape; // 1, 2 or 3 values per MEarthMagnetic. if (itsToValueType > 1) { shape = IPosition(1, itsToValueType); // multiple numbers per value } if (nval1 > 1 || nval2 > 1) { shape.append (vshape); } if (eps.size() > 1) { shape.append (eps.shape()); } if (pos.size() > 1) { shape.append (pos.shape()); } if (shape.empty()) { shape = IPosition(1,1); } out.resize (shape); double* outPtr = out.data(); // Iterate over all input values. for (Array::const_contiter posIter = pos.cbegin(); posIter != pos.cend(); ++posIter) { // Convert to desired position. if (itsPositionEngine) { itsFrame.resetPosition (*posIter); } for (Array::const_contiter epsIter = eps.cbegin(); epsIter != eps.cend(); ++epsIter) { // Convert to desired epoch. if (itsEpochEngine) { itsFrame.resetEpoch (*epsIter); } for (size_t i2=0; i2 vec = em.getValue(); *outPtr++ = vec[0]; *outPtr++ = vec[1]; *outPtr++ = vec[2]; } else { // Get as lon,lat. Vector vec = em.getAngle().getValue(); // Get angles as radians. *outPtr++ = vec[0]; *outPtr++ = vec[1]; } } } void EarthMagneticEngine::copyLLEM (EarthMagneticMachine& emm, double*& outPtr) { if (itsAsLOS == 1) { *outPtr++ = emm.getLOSField(); } else if (itsAsLong) { *outPtr++ = emm.getLong(); } else { // Get as EM field. copyEM (emm.getField(), outPtr); } } } //end namespace casacore-3.7.1/meas/MeasUDF/EarthMagneticEngine.h000066400000000000000000000224531476623553700215150ustar00rootroot00000000000000//# EarthMagneticEngine.h: Engine for TaQL UDF EarthMagnetic conversions //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEAS_EARTHMAGNETICENGINE_H #define MEAS_EARTHMAGNETICENGINE_H //# Includes #include #include #include #include #include #include namespace casacore { //# Forward declarations class DirectionEngine; class EpochEngine; class PositionEngine; // // Engine for TaQL UDF EarthMagnetic conversions // // // // // //# Classes you should understand before using this one. //
      • EngineBase // // // EarthMagneticEngine defines Engines (user defined functions) that can be // used in TaQL to convert EarthMagnetic values from one frame to another // or to calculate them from the IGRF model. // In this way such derived values appear to be ordinary TaQL functions. // // In TaQL these functions can be called like: // // meas.em (toref, emvalues, epochs, positions) // meas.igrf (toref, heights, directions, epochs, positions) // meas.igrflos (heights, directions, epochs, positions) // meas.igrflong (heights, directions, epochs, positions) // // The first one converts the given EarthMagnetic values to the 'toref' // frame for all epoch and positions (their Cartesian product). // The second one calculates the IGRF model values in the 'toref' frame // for all heights, directions, epochs and positions. // The 3rd and 4th return the model value along the line-of-sight cq. as // longitude for all heights, directions, epochs and positions. // The first two function names can be followed by XYZ, ANGLES or LENGTH // to return the values according to the suffix. //
          //
        • toref is a single constant string. // If not given, it defaults to ITRF. Note that 'toref' can also be // given for function IGRFLOS and IGRFLONG, but is neglected. //
        • emvalues gives the EarthMagnetic values to be converted. // They can be given in various forms. //
            //
          • An array of positions given as xyz or as lon-lat-flux // Note that specifying as lon-lat-flux precludes use of units // (angle and length units cannot be mixed in a TaQL value), // while xyz must have a flux unit (e.g., nT). It means that the // unit determines if xyz or lon-lat-flux is given. // It can be given as a single list or a multi-dim array. // It can be followed by a string defining the source // reference type, which defaults to ITRF. //
          • If a single constant position is used, it can be given as // 3 scalar values, optionally followed by the source // reference type. The unit defines iff x,y,z or lon-lat-flux is // given. //
          • The name of a column in a table or a subset of it such as // EMVAL[0,]. Often this is a TableMeasures column // which is recognized as such, also its source reference frame. // If such a column is used in a expression, it will not be // recognized as a TableMeasures column and its reference frame // should be given. //
          //
        • heights is one or more real values giving the heights // above the earth at which the model has to be calculated. // Default unit is m. //
        • directions defines the directions in which the model // has to be calculated. They can be given in all forms as described // in class DirectionEngine. //
        • epochs can be given as shown in class EpochEngine. //
        • positions can be given as shown in class PositionEngine. //
        // All functions return data with type double and unit nT. Unit rad is // returned for functions with the ANGLES suffix. // // The result of a conversion is an array with shape [1|2|3,em,epoch,pos]. // The model calculations result has shape [1|2|3,h,dir,epoch,pos] // The last 3 or 4 elements are the shapes of these arguments. They are // omitted if all of them have length 1. //
        // // // // Get IGRF model value for today at the WSRT at 200 km height // // in the direction of the SUN. // meas.igrf (200km, 'SUN', date(), 'WSRT') // // Similar, but the flux along the line of sight. // meas.igrflos (200km, 'SUN', date(), 'WSRT') // // Convert an earthmagnetic value from ITRF to J2000 angles. // meas.emang ('J2000', 677, 45441, 29517, date(), 'WSRT') // // // // It makes it possible to handle measures in TaQL. // class EarthMagneticEngine: public MeasEngine { public: EarthMagneticEngine(); virtual ~EarthMagneticEngine(); // Get the value type. It also gives the nr of output values per position. // 0=default, 1=length (in tesla), 2=angles (in radians) Int valueType() const { return itsValueType; } // Get the values. Array getArrayDouble (const TableExprId& id); // Handle the argument(s) giving the input earthMagnetics or direction // and reference type. The earthMagnetic can be a column in a table. // Note that direction (or height) can only be given for reftype IGRF. void handleEarthMagnetic (std::vector& args, uInt& argnr); // Handle the heights argument. void handleHeight (TENShPtr& operand); // Set the MeasConvert object. // Set the possible epoch engine. // It can be done only once. void setEpochEngine (EpochEngine& engine); // Set the possible position engine. // It can be done only once. void setPositionEngine (PositionEngine& engine); // Set the possible direction engine. // It can be done only once. void setDirectionEngine (DirectionEngine& engine); // Set the types of the result. void set (MEarthMagnetic::Types toRefType, Int toValueType, Bool asLOS, Bool asLong, Bool useModel); private: // Strip a possible suffix from the reference type. virtual String stripMeasType (const String& type); virtual void deriveAttr (const Unit& unit, Int nval); virtual void setValueType (Int valueType); // Make an MEarthMagnetic from xyz or length,angles. MEarthMagnetic makeEarthMagnetic (const Quantity& qh, const Quantity& q1, const Quantity& q2) const; void handleScalars (const TENShPtr& e1, const TENShPtr& e2, const TENShPtr& e3); virtual void handleValues (TableExprNode& operand, const TableExprId& id, Array& earthMagnetics); Array getEarthMagnetics (const TableExprId& id); Array getHeights (const TableExprId& id); void copyEM (const MVEarthMagnetic& em, double*& outPtr); void copyLLEM (EarthMagneticMachine& emm, double*& outPtr); //# Data members. MeasFrame itsFrame; //# frame used by converter EarthMagneticMachine itsMachine; //# model calculations MEarthMagnetic::Convert itsConverter; Int itsValueType; //# 3=xyz flux, -3=angle,flux Int itsToValueType; Bool itsAsLOS; //# get as line-of-sight? Bool itsAsLong; //# get as longitude? Bool itsUseModel; //# use model calculation? Bool itsConvertModel; //# model to non-ITRF? EpochEngine* itsEpochEngine; PositionEngine* itsPositionEngine; DirectionEngine* itsDirectionEngine; }; } //end namespace #endif casacore-3.7.1/meas/MeasUDF/EarthMagneticUDF.cc000066400000000000000000000141041476623553700210560ustar00rootroot00000000000000//# EarthMagneticUDF.cc: TaQL UDF for EarthMagnetic conversions //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include namespace casacore { EarthMagneticUDF::EarthMagneticUDF (FuncType type) : itsType (type), itsValueType (0), itsRefType (MEarthMagnetic::ITRF) {} UDFBase* EarthMagneticUDF::makeEMXYZ (const String&) { return new EarthMagneticUDF (EMXYZ); } UDFBase* EarthMagneticUDF::makeEMANG (const String&) { return new EarthMagneticUDF (EMANG); } UDFBase* EarthMagneticUDF::makeEMLEN (const String&) { return new EarthMagneticUDF (EMLEN); } UDFBase* EarthMagneticUDF::makeIGRFXYZ (const String&) { return new EarthMagneticUDF (IGRFXYZ); } UDFBase* EarthMagneticUDF::makeIGRFANG (const String&) { return new EarthMagneticUDF (IGRFANG); } UDFBase* EarthMagneticUDF::makeIGRFLEN (const String&) { return new EarthMagneticUDF (IGRFLEN); } UDFBase* EarthMagneticUDF::makeIGRFLOS (const String&) { return new EarthMagneticUDF (IGRFLOS); } UDFBase* EarthMagneticUDF::makeIGRFLONG (const String&) { return new EarthMagneticUDF (IGRFLONG); } void EarthMagneticUDF::setup (const Table&, const TaQLStyle&) { if (operands().size() < 1) { throw AipsError ("No arguments given in a MEAS.EM function"); } // Get the 'to' value type. // Determine the argnr of the earthmagnetic/direction values. uInt argnr = 0; Bool asLOS = False; Bool asLong = False; Bool useModel = False; if (itsType == EMXYZ) { itsValueType = 3; } else if (itsType == EMANG) { itsValueType = 2; } else if (itsType == EMLEN) { itsValueType = 1; } else if (itsType == IGRFXYZ) { itsValueType = 3; useModel = True; } else if (itsType == IGRFANG) { itsValueType = 2; useModel = True; } else if (itsType == IGRFLEN) { itsValueType = 1; useModel = True; } else if (itsType == IGRFLOS) { itsValueType = 1; asLOS = True; useModel = True; } else if (itsType == IGRFLONG) { itsValueType = 1; asLong = True; useModel = True; } // Get the to reference type. // IGRF means calculating the model and returning in ITRF coordinates. if (itsEngine.handleMeasType (operands()[0], False)) { itsRefType = itsEngine.refType(); if (itsRefType == MEarthMagnetic::IGRF) { throw AipsError("IGRF cannot be used as to type; " "use function IGRFxxx instead"); } argnr = 1; if (itsEngine.valueType() != 0) { if (itsValueType != 0) { throw AipsError("Valuetype should be given once; " "not in function name and reference type"); } itsValueType = itsEngine.valueType(); } } if (itsValueType == 0) { itsValueType = 3; // default is XYZ } // Get the value arguments. if (useModel) { // Heights and directions must be given. if (operands().size() <= argnr+1) { throw AipsError ("No heights and directions given in a MEAS IGRF " "function"); } itsEngine.handleHeight (operands()[argnr]); argnr++; itsDirectionEngine.handleDirection (operands(), argnr, False, False); itsEngine.setDirectionEngine (itsDirectionEngine); } else { // No model, thus conversions of earthmagnetic values. if (operands().size() <= argnr) { throw AipsError ("No values given in a MEAS EarthMagnetic function"); } itsEngine.handleEarthMagnetic (operands(), argnr); } // Handle possible Epoch arguments. if (operands().size() > argnr) { itsEpochEngine.handleEpoch (operands(), argnr); itsEngine.setEpochEngine (itsEpochEngine); } // Handle possible Position arguments. if (operands().size() > argnr) { itsPositionEngine.handlePosition (0, operands(), argnr); itsEngine.setPositionEngine (itsPositionEngine); } if (operands().size() > argnr) { throw AipsError ("Too many arguments given in a MEAS EarthMagnetic " "function"); } itsEngine.set (itsRefType, itsValueType, asLOS, asLong, useModel); // Set datatype, shape, unit, etc. setDataType (TableExprNodeRep::NTDouble); const IPosition& shape = itsEngine.shape(); if (shape.size() > 0) { if (shape.product() == 1) { setNDim (0); // scalar } else { setShape (shape); } } else { setNDim (itsEngine.ndim()); } setUnit (itsEngine.unit().getName()); setConstant (itsEngine.isConstant()); setAttributes (itsEngine.makeAttributes (itsRefType, itsValueType)); } Double EarthMagneticUDF::getDouble (const TableExprId& id) { return itsEngine.getArrayDouble (id).data()[0]; } MArray EarthMagneticUDF::getArrayDouble (const TableExprId& id) { return MArray(itsEngine.getArrayDouble (id)); } } //end namespace casacore-3.7.1/meas/MeasUDF/EarthMagneticUDF.h000066400000000000000000000101211476623553700207130ustar00rootroot00000000000000//# EarthMagneticUDF.h: TaQL UDFs for EarthMagnetic conversions //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEAS_EARTHMAGNETICUDF_H #define MEAS_EARTHMAGNETICUDF_H //# Includes #include #include #include #include #include #include namespace casacore { // // TaQL UDFs for EarthMagnetic conversions. // // // // // //# Classes you should understand before using this one. //
      • UDFBase // // // EarthMagneticUDF defines UDFs (user defined functions) that can be used // in TaQL to convert Measures for earthMagnetics. // Special functions exist to convert to hourangle and azimuth/elevation. // In this way such derived values appear to be ordinary TaQL functions. // // A function is called like: // // meas.em (toref, em, time, pos) // meas.em (toref, em, time, pos) // For example, // meas.em ('J2000', 'IGRF', TIME, [[5d12m, 52deg, 11m], 'WGS84']) // //
          //
        • // toref is a single constant string giving the // EarthMagnetic reference type. It is not possible to use IGRF for // the 'toref' type. //
        • // em is a value array followed by the reference type. // Type IGRF is the IGRF, for which no value array should be given. //
        // All functions have data type double and unit Tesla. //
        // // It makes it possible to handle measures in TaQL. // class EarthMagneticUDF: public UDFBase { public: // Define the possible function types. enum FuncType {EMXYZ, EMANG, EMLEN, IGRFXYZ, IGRFANG, IGRFLEN, IGRFLOS, IGRFLONG}; // Create for the given function type. explicit EarthMagneticUDF (FuncType); // Function to create an object. static UDFBase* makeEMXYZ (const String&); static UDFBase* makeEMANG (const String&); static UDFBase* makeEMLEN (const String&); static UDFBase* makeIGRFXYZ (const String&); static UDFBase* makeIGRFANG (const String&); static UDFBase* makeIGRFLEN (const String&); static UDFBase* makeIGRFLOS (const String&); static UDFBase* makeIGRFLONG (const String&); // Setup the object. virtual void setup (const Table&, const TaQLStyle&); // Get the value. virtual Double getDouble (const TableExprId& id); virtual MArray getArrayDouble (const TableExprId& id); private: //# Data members. EarthMagneticEngine itsEngine; DirectionEngine itsDirectionEngine; EpochEngine itsEpochEngine; PositionEngine itsPositionEngine; FuncType itsType; Int itsValueType; MEarthMagnetic::Types itsRefType; }; } //end namespace #endif casacore-3.7.1/meas/MeasUDF/EpochEngine.cc000066400000000000000000000152721476623553700201770ustar00rootroot00000000000000//# EpochEngine.cc: Engine for TaQL UDF Epoch conversions //# Copyright (C) 2011 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { EpochEngine::EpochEngine() : itsPositionEngine (0) {} EpochEngine::~EpochEngine() {} void EpochEngine::handleEpoch (vector& args, uInt& argnr) { // Initialize type to unknown. itsRefType = MEpoch::N_Types; // Convert a string epoch argument to a date. if (args[argnr]->dataType() == TableExprNodeRep::NTString) { TableExprNode dNode = datetime (args[argnr]); args[argnr] = dNode.getRep(); } // Check if the value is a date or double. // If double, its unit should match days. if (args[argnr]->isReal()) { if (! args[argnr]->unit().empty() && args[argnr]->unit() != Unit("d")) { // It tests if unit type conforms!! throw AipsError("Invalid unit used for an epoch in a MEAS function"); } } else if (args[argnr]->dataType() == TableExprNodeRep::NTDate) { TableExprNode dNode = mjd(args[argnr]); // convert date to double args[argnr] = dNode.getRep(); } else { throw AipsError ("Invalid epoch given in a MEAS function"); } // Values can be given as [t1,t2,...],reftype uInt nargnr = argnr+1; // See if there is a reference type. if (args.size() > nargnr && args[nargnr]->dataType() == TableExprNodeRep::NTString) { if (handleMeasType (args[nargnr], False)) { nargnr++; } } // Handle the epoch values. handleMeasArray (args[argnr]); // Skip the arguments handled. argnr = nargnr; // Determine the output unit, shape, and ndim. itsOutUnit = "s"; // Set shape, etc. for constants. adaptForConstant (itsConstants.shape()); } String EpochEngine::stripMeasType (const String& type) { String str(type); itsSidFrac = False; // F_ (or F-) indicates a full time, thus no sidereal fraction. if (str.size() >= 2 && str[0] == 'F' && (str[1] == '-' || str[1] == '_')) { str = str.substr(2); } else if (str.size() >= 4 && str[2] == 'S' && str[3] == 'T') { itsSidFrac = True; } return str; } void EpochEngine::setPositionEngine (PositionEngine& engine) { AlwaysAssert (itsPositionEngine == 0, AipsError); itsPositionEngine = &engine; extendBase (engine, True); // Define the frame part, so it can be reset later. itsFrame.set (MPosition()); } void EpochEngine::setConverter (MEpoch::Types toType, Bool sidFrac) { MEpoch::Ref ref(toType, itsFrame); itsConverter = MEpoch::Convert (toType, ref); itsSidFrac = sidFrac; } void EpochEngine::handleValues (TableExprNode& operand, const TableExprId& id, Array& epochs) { // Get unit (default seconds). Unit unit = operand.unit(); if (unit.empty()) { unit = "s"; } // Get values (as doubles or dates). Array values; if (operand.getNodeRep()->isReal()) { values.reference (operand.getDoubleAS(id).array()); } else { unit = "s"; Array dates = operand.getDateAS(id).array(); values.resize (dates.shape()); for (uInt i=0; i EpochEngine::getEpochs (const TableExprId& id) { if (itsConstants.size() > 0) { return itsConstants; } if (!itsMeasScaCol.isNull()) { return Vector(1, itsMeasScaCol(id.rownr())); } else if (!itsMeasArrCol.isNull()) { return itsMeasArrCol(id.rownr()); } Array epochs; handleValues (itsExprNode, id, epochs); return epochs; } Array EpochEngine::getArrayDouble (const TableExprId& id) { DebugAssert (id.byRow(), AipsError); Array res (getEpochs(id)); // Get positions if given. Array pos(IPosition(1,1)); if (itsPositionEngine) { pos.reference (itsPositionEngine->getPositions (id)); } // Convert the epoch to the given type for all positions. Array out; if (res.size() > 0 && pos.size() > 0) { IPosition shape = res.shape(); if (pos.size() > 1) { shape.append (pos.shape()); } out.resize (shape); double* outPtr = out.data(); for (Array::const_contiter posIter = pos.cbegin(); posIter != pos.cend(); ++posIter) { // Convert to desired reference type. if (itsPositionEngine) { itsFrame.resetPosition (*posIter); } for (Array::const_contiter resIter = res.cbegin(); resIter != res.cend(); ++resIter) { itsConverter.setModel (*resIter); MEpoch ep = itsConverter(); // Convert to seconds. // Possibly strip day from sidereal times. if (itsSidFrac) { *outPtr++ = fmod(ep.getValue().get(), 1.) * 24*3600; } else { *outPtr++ = ep.getValue().get() * 24*3600; } } } } return out; } } //end namespace casacore-3.7.1/meas/MeasUDF/EpochEngine.h000066400000000000000000000105161476623553700200350ustar00rootroot00000000000000//# EpochEngine.h: Engine for TaQL UDF Epoch conversions //# Copyright (C) 2011 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEAS_EPOCHENGINE_H #define MEAS_EPOCHENGINE_H //# Includes #include #include #include #include #include namespace casacore { //# Forward declarations class PositionEngine; // // Engine for TaQL UDF Epoch conversions // // // // // //# Classes you should understand before using this one. //
      • EngineBase // // // EpochEngine defines Engines (user defined functions) that can be used in TaQL // to convert Measures for epochs. // In this way such derived values appear to be ordinary TaQL functions. // // In TaQL these functions can be called like: // // meas.epoch (toref, time, fromref) // meas.last (time, fromref, pos, posref) // For example, // meas.epoch ('UTC', 1e9 s, 'WGS84') // //
          //
        • // toref is a single constant string. //
        • // pos can have various value types. A single numeric array is // a series of RA,DEC in J2000. If given as a set, the last argument of the // set can be the reference types of the values in the set. The values can // be strings (indicating planetary objects) or value pairs giving lon,lat. // The default reference type is J2000. //
        // All functions have data type double and unit radian. // A epoch can also be a table column which usually knows its type. // It can also be an expression (e.g. EPOCH[0,]) which also knows the type. //
        // // It makes it possible to handle measures in TaQL. // class EpochEngine: public MeasEngine { public: EpochEngine(); virtual ~EpochEngine(); // Tell if the fraction has to be used for sidereal times. Bool sidFrac() const { return itsSidFrac; } // Get the values. Array getArrayDouble (const TableExprId& id); // Get the epochs. Array getEpochs (const TableExprId& id); // Handle the argument(s) giving the input epochs and reference type. // The epoch can be a column in a table. void handleEpoch (std::vector& args, uInt& argnr); // Set the MeasConvert object. void setConverter (MEpoch::Types toType, Bool sidFrac); // Set the possible position engine. // It can be done only once. void setPositionEngine (PositionEngine& engine); private: // Strip a possible prefix from the epoch type. virtual String stripMeasType (const String& type); virtual void handleValues (TableExprNode& operand, const TableExprId& id, Array& epochs); //# Data members. Bool itsSidFrac; //# T = fraction for sidereal MeasFrame itsFrame; //# frame used by converter MEpoch::Convert itsConverter; PositionEngine* itsPositionEngine; }; } //end namespace #endif casacore-3.7.1/meas/MeasUDF/EpochUDF.cc000066400000000000000000000064241476623553700174070ustar00rootroot00000000000000//# EpochUDF.cc: TaQL UDF for Epoch conversions //# Copyright (C) 2011 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include namespace casacore { EpochUDF::EpochUDF (FuncType type) : itsType (type) {} UDFBase* EpochUDF::makeEPOCH (const String&) { return new EpochUDF (EPOCH); } UDFBase* EpochUDF::makeLAST (const String&) { return new EpochUDF (LAST); } void EpochUDF::setup (const Table&, const TaQLStyle&) { if (operands().size() < 1) { throw AipsError ("No arguments given in a MEAS function"); } // Get the 'to' reference type. // Determine the argnr of the epoch. uInt argnr = 0; if (itsType == LAST) { itsRefType = MEpoch::LAST; itsSidFrac = True; } else { itsEngine.handleMeasType (operands()[0], True); itsRefType = itsEngine.refType(); itsSidFrac = itsEngine.sidFrac(); argnr = 1; } // Get the epochs. if (operands().size() <= argnr) { throw AipsError ("No epoch given in a MEAS function"); } itsEngine.handleEpoch (operands(), argnr); // Handle possible Position arguments. if (operands().size() > argnr) { itsPositionEngine.handlePosition (1, operands(), argnr); itsEngine.setPositionEngine (itsPositionEngine); } if (operands().size() > argnr) { throw AipsError ("Too many arguments given in a MEAS function"); } itsEngine.setConverter (itsRefType, itsSidFrac); // Set datatype, shape, unit, etc. setDataType (TableExprNodeRep::NTDouble); const IPosition& shape = itsEngine.shape(); if (shape.size() > 0) { if (shape.product() == 1) { setNDim (0); // scalar } else { setShape (itsEngine.shape()); } } else { setNDim (itsEngine.ndim()); } setUnit (itsEngine.unit().getName()); setConstant (itsEngine.isConstant()); setAttributes (itsEngine.makeAttributes (itsRefType)); } Double EpochUDF::getDouble (const TableExprId& id) { return itsEngine.getArrayDouble (id).data()[0]; } MArray EpochUDF::getArrayDouble (const TableExprId& id) { return MArray(itsEngine.getArrayDouble (id)); } } //end namespace casacore-3.7.1/meas/MeasUDF/EpochUDF.h000066400000000000000000000067341476623553700172550ustar00rootroot00000000000000//# EpochUDF.h: TaQL UDFs for Epoch conversions //# Copyright (C) 2011 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEAS_EPOCHUDF_H #define MEAS_EPOCHUDF_H //# Includes #include #include #include #include namespace casacore { // // TaQL UDFs for Epoch conversions. // // // // // //# Classes you should understand before using this one. //
      • UDFBase // // // EpochUDF defines UDFs (user defined functions) that can be used in TaQL // to convert Measures for epochs. // Special functions exist to convert to local sidereal time. // In this way such derived values appear to be ordinary TaQL functions. // // A function is called like: // // meas.epoch (toref, epoch, fromref, pos, posref) // For example, // meas.dir ('APP', 'MOON', POSITION, TIME) // //
          //
        • // toref is a single constant string. //
        • // dir can have various value types. A single numeric array is // a series of RA,DEC in J2000. If given as a set, the last argument of the // set can be the reference types of the values in the set. The values can // be strings (indicating planetary objects) or value pairs giving lon,lat. // The default reference type is J2000. //
        // All functions have data type double and unit radian. //
        // // It makes it possible to handle measures in TaQL. // class EpochUDF: public UDFBase { public: // Define the possible function types. enum FuncType {EPOCH, LAST}; explicit EpochUDF (FuncType); // Function to create an object. static UDFBase* makeEPOCH (const String&); static UDFBase* makeLAST (const String&); // Setup the object. virtual void setup (const Table&, const TaQLStyle&); // Get the value. virtual Double getDouble (const TableExprId& id); virtual MArray getArrayDouble (const TableExprId& id); private: //# Data members. EpochEngine itsEngine; PositionEngine itsPositionEngine; FuncType itsType; MEpoch::Types itsRefType; Bool itsSidFrac; // T = use fraction for sidereal times }; } //end namespace #endif casacore-3.7.1/meas/MeasUDF/FrequencyEngine.cc000066400000000000000000000231741476623553700211020ustar00rootroot00000000000000//# FrequencyEngine.cc: Engine for TaQL UDF Frequency conversions //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include namespace casacore { FrequencyEngine::FrequencyEngine() : itsEpochEngine (0), itsPositionEngine (0), itsDirectionEngine(0), itsDopplerEngine (0), itsRadVelEngine (0) {} FrequencyEngine::~FrequencyEngine() {} void FrequencyEngine::handleFrequency (vector& args, uInt& argnr) { // Initialize type to unknown. itsRefType = MFrequency::N_Types; // Check if the value is a double. if (!args[argnr]->isReal()) { throw AipsError ("Invalid frequency given in a MEAS function"); } // Values can be given as [t1,t2,...],reftype uInt nargnr = argnr+1; // See if there is a reference type. if (args.size() > nargnr && args[nargnr]->dataType() == TableExprNodeRep::NTString) { if (handleMeasType (args[nargnr], False)) { nargnr++; } } handleMeasArray (args[argnr]); // Skip the arguments handled. argnr = nargnr; // Determine the output unit, shape, and ndim. itsOutUnit = "Hz"; adaptForConstant (itsConstants.shape()); } void FrequencyEngine::handleValues (TableExprNode& operand, const TableExprId& id, Array& frequencies) { Array values = operand.getDoubleAS(id).array(); const IPosition& shape = values.shape(); frequencies.resize (shape); Unit unit = operand.unit(); if (unit.empty()) { unit = "Hz"; } Quantity q(0, unit); Bool delIt; const Double* valVec = values.getStorage (delIt); MFrequency* freqVec = frequencies.data(); for (uInt i=0; i FrequencyEngine::getFrequencies (const TableExprId& id) { if (itsConstants.size() > 0) { return itsConstants; } if (!itsMeasScaCol.isNull()) { return Vector(1, itsMeasScaCol(id.rownr())); } else if (!itsMeasArrCol.isNull()) { return itsMeasArrCol(id.rownr()); } Array freqs; handleValues (itsExprNode, id, freqs); return freqs; } Array FrequencyEngine::getArrayDouble (const TableExprId& id, int type) { DebugAssert (id.byRow(), AipsError); Array res (getFrequencies(id)); // Get directions, epochs, positions, radvels and dopplers if given. Array dir(IPosition(1,1)); Array eps(IPosition(1,1)); Array pos(IPosition(1,1)); Array rv(IPosition(1,1)); Array dop(IPosition(1,1)); if (itsDirectionEngine) { dir.reference (itsDirectionEngine->getDirections (id)); } if (itsEpochEngine) { eps.reference (itsEpochEngine->getEpochs (id)); } if (itsPositionEngine) { pos.reference (itsPositionEngine->getPositions (id)); } if (itsRadVelEngine) { rv.reference (itsRadVelEngine->getRadialVelocities (id)); } if (itsDopplerEngine) { dop.reference (itsDopplerEngine->getDopplers (id)); } Array out; if (! (res.empty() || dir.empty() || eps.empty() || pos.empty() || rv.empty() || dop.empty())) { IPosition shape; // Only add the other axes if one of them has multiple values. if (res.size() > 1 || dir.size() > 1 || eps.size() > 1 || pos.size() > 1 || rv.size() > 1 || dop.size() > 1) { shape.append (res.shape()); if (itsDopplerEngine) { shape.append (dop.shape()); } else { if (itsRadVelEngine) { shape.append (rv.shape()); } shape.append (dir.shape()); shape.append (eps.shape()); shape.append (pos.shape()); } } else { shape = IPosition(1,1); } // Convert the frequency to the given type for all radvel/doppler,dir,epoch,pos. out.resize (shape); double* outPtr = out.data(); for (Array::const_contiter posIter = pos.cbegin(); posIter != pos.cend(); ++posIter) { if (itsPositionEngine) { itsFrame.resetPosition (*posIter); itsRVFrame.resetPosition (*posIter); } for (Array::const_contiter epsIter = eps.cbegin(); epsIter != eps.cend(); ++epsIter) { if (itsEpochEngine) { itsFrame.resetEpoch (*epsIter); itsRVFrame.resetEpoch (*epsIter); } for (Array::const_contiter dirIter = dir.cbegin(); dirIter != dir.cend(); ++dirIter) { if (itsDirectionEngine) { itsFrame.resetDirection (*dirIter); itsRVFrame.resetDirection (*dirIter); } for (Array::const_contiter rvIter = rv.cbegin(); rvIter != rv.cend(); ++rvIter) { if (itsRadVelEngine) { // The frame has to be set in the RadialVelocity. MeasRef mr(rvIter->getRef()); mr.set (itsRVFrame); MRadialVelocity radvel(rvIter->getValue(), mr); itsFrame.resetRadialVelocity (radvel); } for (Array::const_contiter dopIter = dop.cbegin(); dopIter != dop.cend(); ++dopIter) { for (Array::const_contiter resIter = res.cbegin(); resIter != res.cend(); ++resIter) { itsConverter.setModel (*resIter); if (itsDopplerEngine) { Vector freqs(1, resIter->getValue().getValue()); if (type == FrequencyUDF::SHIFT) { // Shift has to use a BETA Doppler, so convert. // Note that it does the same as MFrequency::fromDoppler. MDoppler tmp = MDoppler::Convert(*dopIter, MDoppler::BETA)(); *outPtr++ = tmp.shiftFrequency (freqs)[0]; } else { *outPtr++ = resIter->toRest (*dopIter).getValue().getValue(); } } else { // Convert frequency to desired reference type. MFrequency mf = itsConverter(); *outPtr++ = mf.getValue().getValue(); } } } } } } } } return out; } } //end namespace casacore-3.7.1/meas/MeasUDF/FrequencyEngine.h000066400000000000000000000124601476623553700207400ustar00rootroot00000000000000//# FrequencyEngine.h: Engine for TaQL UDF Frequency conversions //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEAS_FREQUENCYENGINE_H #define MEAS_FREQUENCYENGINE_H //# Includes #include #include #include #include #include namespace casacore { //# Forward declarations class DopplerEngine; class RadialVelocityEngine; class DirectionEngine; class EpochEngine; class PositionEngine; // // Engine for TaQL UDF Frequency conversions // // // // // //# Classes you should understand before using this one. //
      • EngineBase // // // FrequencyEngine defines Engines (user defined functions) that can be used // in TaQL to convert Measures for frequencies. // In this way such derived values appear to be ordinary TaQL functions. // // Frequency conversions require a MeasFrame containing sky direction, // epoch and position on earth. // In TaQL these functions can be called like: // // meas.freq ('TOPO', 1GHz, 'LSRK', 'CasA', date(), // [1e6m,1e6m,1e6m], 'WGS84') // // which converts the frequency from LSRK to TOPO. //
          //
        • // toref is a single constant string. //
        • // pos can have various value types. A single numeric array is // a series of RA,DEC in J2000. If given as a set, the last argument of the // set can be the reference types of the values in the set. The values can // be strings (indicating planetary objects) or value pairs giving lon,lat. // The default reference type is J2000. //
        // All such functions return data with type double and unit Hz. // // Frequencies can be given like: // [f1,f2,...], fromRef // where fromRef is the reference type. // // A frequency can also be a table column which usually knows its type. // It can also be an expression (e.g. FREQUENCY[0,]) which also knows the type. //
        // // It makes it possible to handle measures in TaQL. // class FrequencyEngine: public MeasEngine { public: FrequencyEngine(); virtual ~FrequencyEngine(); // Get the values. Array getArrayDouble (const TableExprId& id, int type); // Get the frequencies. Array getFrequencies (const TableExprId& id); // Handle the argument(s) giving the input frequencies and reference type. // The frequency can be a column in a table. void handleFrequency (std::vector& args, uInt& argnr); // Set the MeasConvert object. void setConverter (MFrequency::Types toType); // Set the possible doppler engine. // It can be done only once. void setDopplerEngine (DopplerEngine& engine); // Set the possible radial velocity engine. // It can be done only once. void setRadVelEngine (RadialVelocityEngine& engine); // Set the possible direction engine. // It can be done only once. void setDirectionEngine (DirectionEngine& engine); // Set the possible epoch engine. // It can be done only once. void setEpochEngine (EpochEngine& engine); // Set the possible position engine. // It can be done only once. void setPositionEngine (PositionEngine& engine); private: virtual void handleValues (TableExprNode& operand, const TableExprId& id, Array& frequencies); //# Data members. MeasFrame itsFrame; //# frame used by converter MeasFrame itsRVFrame; //# frame used for radial velocities MFrequency::Convert itsConverter; EpochEngine* itsEpochEngine; PositionEngine* itsPositionEngine; DirectionEngine* itsDirectionEngine; DopplerEngine* itsDopplerEngine; RadialVelocityEngine* itsRadVelEngine; }; } //end namespace #endif casacore-3.7.1/meas/MeasUDF/FrequencyUDF.cc000066400000000000000000000140171476623553700203070ustar00rootroot00000000000000//# FrequencyUDF.cc: TaQL UDF for Frequency conversions //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include namespace casacore { FrequencyUDF::FrequencyUDF (FuncType type) : itsType (type), itsRefType (MFrequency::DEFAULT) {} UDFBase* FrequencyUDF::makeFREQ (const String&) { return new FrequencyUDF (FREQUENCY); } UDFBase* FrequencyUDF::makeREST (const String&) { return new FrequencyUDF (REST); } UDFBase* FrequencyUDF::makeSHIFT (const String&) { return new FrequencyUDF (SHIFT); } void FrequencyUDF::setup (const Table&, const TaQLStyle&) { if (operands().size() < 1) { throw AipsError ("No arguments given in a MEAS function"); } // Get the 'to' reference type. // Determine the argnr of the epoch. Bool needRadVel = False; uInt argnr = 0; if (itsType == REST) { itsRefType = MFrequency::REST; } else if (itsType != SHIFT) { // A to-type has to be given if not shifting frequencies. if (itsEngine.handleMeasType (operands()[0], True)) { itsRefType = itsEngine.refType(); argnr = 1; if (itsRefType == MFrequency::REST) { itsType = REST; // Conversion to REST needRadVel = True; } } } // Get the frequencies (and type). if (operands().size() <= argnr) { throw AipsError ("No frequency given in a MEAS function"); } itsEngine.handleFrequency (operands(), argnr); if (itsEngine.refType() == MFrequency::REST) { needRadVel = True; } Bool useDoppler = False; if (itsType == SHIFT) { // For shift the output reftype is the same as the input. itsRefType = itsEngine.refType(); // Only get the doppler if shifting. if (operands().size() > argnr) { itsDopplerEngine.handleDoppler (operands(), argnr, False, False); itsEngine.setDopplerEngine (itsDopplerEngine); useDoppler = True; } else { throw AipsError("No doppler given in function MEAS.SHIFTFREQ"); } } else if (itsType == REST || needRadVel) { // Get the mandatory radial velocity or doppler for rest frequencies. useDoppler = handleRadVelDoppler (argnr, needRadVel); } // Get all possible other arguments if doppler is not used. if (! useDoppler) { // Handle possible Direction arguments. if (operands().size() > argnr) { itsDirectionEngine.handleDirection (operands(), argnr, False, False); itsEngine.setDirectionEngine (itsDirectionEngine); } // Handle possible Epoch arguments. if (operands().size() > argnr) { itsEpochEngine.handleEpoch (operands(), argnr); itsEngine.setEpochEngine (itsEpochEngine); } // Handle possible Position arguments. if (operands().size() > argnr) { itsPositionEngine.handlePosition (0, operands(), argnr); itsEngine.setPositionEngine (itsPositionEngine); } } if (operands().size() > argnr) { throw AipsError ("Too many arguments given in a MEAS function"); } if (itsType != SHIFT) { itsEngine.setConverter (itsRefType); } // Set datatype, shape, unit, etc. setDataType (TableExprNodeRep::NTDouble); const IPosition& shape = itsEngine.shape(); if (shape.size() > 0) { if (shape.product() == 1) { setNDim (0); // scalar } else { setShape (shape); } } else { setNDim (itsEngine.ndim()); } setUnit (itsEngine.unit().getName()); setConstant (itsEngine.isConstant()); setAttributes (itsEngine.makeAttributes (itsRefType)); } Bool FrequencyUDF::handleRadVelDoppler (uInt& argnr, Bool mustRadVel) { // In the REST function a radial velocity or doppler can be used. // They can be distinguished by unit or type, so the velocity unit // and/or a type is required for radial velocity. if (operands().size() > argnr) { uInt argnrOld = argnr; if (! operands()[argnr]->unit().empty()) { try { itsRadVelEngine.handleRadialVelocity (operands(), argnr); itsEngine.setRadVelEngine (itsRadVelEngine); return False; } catch (const AipsError&) { } } if (! mustRadVel) { argnr = argnrOld; try { itsDopplerEngine.handleDoppler (operands(), argnr, False, False); itsEngine.setDopplerEngine (itsDopplerEngine); return True; } catch (const AipsError&) { } } } if (mustRadVel) { throw AipsError("No radial velocity given in MEAS REST conversion"); } throw AipsError("No radial velocity nor doppler given in MEAS.REST function"); } Double FrequencyUDF::getDouble (const TableExprId& id) { return getArrayDouble(id).array().data()[0]; } MArray FrequencyUDF::getArrayDouble (const TableExprId& id) { return MArray(itsEngine.getArrayDouble (id, itsType)); } } //end namespace casacore-3.7.1/meas/MeasUDF/FrequencyUDF.h000066400000000000000000000106241476623553700201510ustar00rootroot00000000000000//# FrequencyUDF.h: TaQL UDFs for Frequency conversions //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEAS_FREQUENCYUDF_H #define MEAS_FREQUENCYUDF_H //# Includes #include #include #include #include #include #include #include #include namespace casacore { // // TaQL UDFs for Frequency conversions. // // // // // //# Classes you should understand before using this one. //
      • UDFBase // // // FrequencyUDF defines UDFs (user defined functions) that can be used in TaQL // to convert Measures for frequencys. // Special functions exist to convert to hourangle and azimuth/elevation. // In this way such derived values appear to be ordinary TaQL functions. // // A function is called like: // // meas.dir (toref, dir, time, pos) // meas.dir (toref, dir, time, pos) // For example, // meas.dir ('B1950', [2rad,1rad]) // meas.dir ('B1950', [[2rad,1rad], 'J2000']) // meas.dir ('APP', 'MOON', TIME, [[5d12m, 52deg, 11m], 'WGS84']) // meas.dir ('APP', 'MOON', TIME, POSITION) // Or // meas.dir (toref, fromref, dir, pos, time) // //
          //
        • // toref is a single constant string. //
        • // dir can have various value types. A single numeric array is // a series of RA,DEC in J2000. If given as a set, the last argument of the // set can be the reference types of the values in the set. The values can // be strings (indicating planetary objects) or value pairs giving lon,lat. // The default reference type is J2000. //
        // All functions have data type double and unit radian. //
        // // It makes it possible to handle measures in TaQL. // class FrequencyUDF: public UDFBase { public: // Define the possible function types. enum FuncType {FREQUENCY, REST, SHIFT}; // Create for the given function type. explicit FrequencyUDF (FuncType); // Function to create an object. static UDFBase* makeFREQ (const String&); static UDFBase* makeREST (const String&); static UDFBase* makeSHIFT (const String&); // Setup the object. virtual void setup (const Table&, const TaQLStyle&); // Get the value. virtual Double getDouble (const TableExprId& id); virtual MArray getArrayDouble (const TableExprId& id); private: // Handle a radial velocity or optionally doppler for REST conversion. // It returns True if Doppler is used. Bool handleRadVelDoppler (uInt& argnr, Bool mustRadVel); //# Data members. FrequencyEngine itsEngine; DopplerEngine itsDopplerEngine; //# for frequency shift RadialVelocityEngine itsRadVelEngine; //# for rest frequency DirectionEngine itsDirectionEngine; EpochEngine itsEpochEngine; PositionEngine itsPositionEngine; FuncType itsType; MFrequency::Types itsRefType; }; } //end namespace #endif casacore-3.7.1/meas/MeasUDF/MeasEngine.h000066400000000000000000000113101476623553700176550ustar00rootroot00000000000000//# MeasEngine.h: Templated base class for the TaQL UDF conversion engines //# Copyright (C) 2018 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEAS_MEASENGINE_H #define MEAS_MEASENGINE_H //# Includes #include #include #include #include #include namespace casacore { // // Templated base class for the TaQL UDF conversion engines // // // // // //# Classes you should understand before using this one. //
      • BaseEngine // // // DopplerEngine defines Engines (user defined functions) that can be used // in TaQL to convert Measures for dopplers. // In this way such derived values appear to be ordinary TaQL functions. // // Doppler conversions require a MeasFrame containing sky direction, // epoch and position on earth. // In TaQL these functions can be called like: // // meas.rv ('TOPO', 1 'm/s', 'LSRK', 'CasA', date(), // [1e6m,1e6m,1e6m], 'WGS84') // // which converts the dopplers from LSRK to TOPO. //
          //
        • // toref is a single constant string. //
        • // pos can have various value types. A single numeric array is // a series of RA,DEC in J2000. If given as a set, the last argument of the // set can be the reference types of the values in the set. The values can // be strings (indicating planetary objects) or value pairs giving lon,lat. // The default reference type is J2000. //
        // All such functions return data with type double and unit Hz. // // Dopplers can be given like: // [v1,v2,...], fromRef // where fromRef is the reference type. // // A doppler can also be a table column which usually knows its type. // It can also be an expression (e.g. DOPPLER[0,]) which also knows the type. //
        // // It makes it possible to handle measures in TaQL. // template class MeasEngine: public BaseEngine { public: MeasEngine() : itsRefType (M::N_Types) {} virtual ~MeasEngine(); // Get the reference type. typename M::Types refType() const { return itsRefType; } // Handle a doppler reference type. // If the reference type is invalid, an exception is only thrown // if doThrow=True. In this way a string argument can // be a source name for a direction. Bool handleMeasType (const TENShPtr& operand, Bool doThrow); // Make the expression result attributes. Record makeAttributes (typename M::Types refType, Int valueType = 1) const; protected: // Handle the operand representing an array of Meas values. void handleMeasArray (const TENShPtr& operand); // Handle a constant Meas value. void handleConstant (const TENShPtr& operand); // Let a derive class handle the values. virtual void handleValues (TableExprNode& operand, const TableExprId& id, Array& positions) = 0; //# Data members. Array itsConstants; typename M::Types itsRefType; ScalarMeasColumn itsMeasScaCol; ArrayMeasColumn itsMeasArrCol; }; } //end namespace #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/meas/MeasUDF/MeasEngine.tcc000066400000000000000000000177301476623553700202130ustar00rootroot00000000000000//# MeasEngine.tcc: Base class for the TaQL UDF conversion engines //# Copyright (C) 2018 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEAS_MEASENGINE_TCC #define MEAS_MEASENGINE_TCC //# Includes #include #include #include namespace casacore { template MeasEngine::~MeasEngine() {} template Bool MeasEngine::handleMeasType (const TENShPtr& operand, Bool doThrow) { if (operand->dataType() != TableExprNodeRep::NTString || operand->valueType() != TableExprNodeRep::VTScalar || !operand->isConstant()) { if (doThrow) { throw AipsError (M::showMe() + " type given in a MEAS function " "must be a constant scalar string"); } return False; } String str = operand->getString(0); str.upcase(); // Let a derived class strip part of the mesaure type (as needed). str = stripMeasType (str); typename M::Types refType; Bool fnd = M::getType (refType, str); if (fnd) { itsRefType = refType; } else if (doThrow) { throw AipsError ("Unknown " + M::showMe() + " reference type " + str + " given in a MEAS function"); } return fnd; } template void MeasEngine::handleMeasArray (const TENShPtr& operand) { itsInUnit = operand->unit(); itsNDim = operand->ndim(); itsShape = operand->shape(); if ((!operand->isReal()) || (operand->valueType() != TableExprNodeRep::VTScalar && operand->valueType() != TableExprNodeRep::VTArray)) { throw AipsError (M::showMe() + " value given in a MEAS function " "must be a numeric scalar or array"); } // See if the operand has MeasUDF attributes, which is the case if the // operand is a MEAS function itself. // If so, they define the reference and value type. // Note that the same field names are used as in TableMeasures. if (operand->attributes().isDefined("MEASINFO")) { const Record& measAttr = operand->attributes().subRecord("MEASINFO"); String type = measAttr.asString("type"); String ref = measAttr.asString("Ref"); Int valueType = measAttr.asInt ("ValueType"); // Check if type matches. if (type != M::showMe()) { throw AipsError (M::showMe() + " value expected in a MEAS function, found " + type); } AlwaysAssert (M::getType(itsRefType, ref), AipsError); setValueType (valueType); } // Let inherited classes derive attributes as needed. deriveAttr (operand->unit(), 0); if (operand->isConstant()) { handleConstant (operand); return; } // Try if the argument is a column. // If found, try to handle it as a TableMeasures column. const TableColumn* tabCol = 0; Bool directCol = True; const TableExprNodeColumn* scaNode = dynamic_cast(operand.get()); if (scaNode) { tabCol = &(scaNode->getColumn()); } else { const TableExprNodeArrayColumn* colNode = dynamic_cast(operand.get()); if (colNode) { tabCol = &(colNode->getColumn()); } else { // The node is an expression, not a column. directCol = False; // Try if the node is an array part of a column. TableExprNodeArrayPart* partNode = dynamic_cast(operand.get()); if (partNode) { colNode = partNode->getColumnNode(); if (colNode) { tabCol = &(colNode->getColumn()); } } } } if (tabCol) { // Try if the column contains measures. if (TableMeasDescBase::hasMeasures (*tabCol)) { TableMeasColumn measTmp(tabCol->table(), tabCol->columnDesc().name()); // Check the measure is the correct type. AlwaysAssert(measTmp.measDesc().type() == M::showMe(), AipsError); // Get and check the node's refType if it is fixed. typename M::Types nodeRefType = M::N_Types; if (! (measTmp.measDesc().isRefCodeVariable() || measTmp.measDesc().hasOffset())) { uInt refCode = measTmp.measDesc().getRefCode(); nodeRefType = static_cast(refCode); if (itsRefType != M::N_Types && nodeRefType != itsRefType) { throw AipsError ("MEAS " + M::showMe() + " reference type " + String::toString(itsRefType) + " mismatches type " + String::toString(nodeRefType) + " of column " + tabCol->columnDesc().name()); } itsRefType = nodeRefType; } // A direct column can directly be accessed using TableMeasures. if (directCol) { if (scaNode) { itsMeasScaCol.attach (tabCol->table(), tabCol->columnDesc().name()); } else { itsMeasArrCol.attach (tabCol->table(), tabCol->columnDesc().name()); } return; } // It is a part, so we cannot use TableMeasures. // If the reference type is variable, the user should index the result // of the meas.M function. if (nodeRefType == M::N_Types) { throw AipsError ("Column " + tabCol->columnDesc().name() + ", which has a variable reference frame, " "is used in a MEAS function with slicing. " "The slicing should be done after the function " "like 'meas.direction(arguments)[0:3]'"); } } } if (itsMeasScaCol.isNull() && itsMeasArrCol.isNull()) { if (itsRefType == M::N_Types) { throw AipsError("No reference type given for a non-constant MEAS " "function " + M::showMe() + " argument"); } itsExprNode = TableExprNode(operand); } } template void MeasEngine::handleConstant (const TENShPtr& operand) { AlwaysAssert (operand->valueType() != TableExprNodeRep::VTSet, AipsError); if (itsRefType == M::N_Types) { itsRefType = M::DEFAULT; } TableExprNode node(operand); handleValues (node, 0, itsConstants); } template Record MeasEngine::makeAttributes (typename M::Types refType, Int valueType) const { // This is the opposite of testing attributes in handleMeasArray above. Record srec; srec.define ("type", M::showMe()); srec.define ("Ref", M::showType(refType)); srec.define ("ValueType", valueType); Record rec; rec.defineRecord ("MEASINFO", srec); return rec; } } #endif casacore-3.7.1/meas/MeasUDF/PositionEngine.cc000066400000000000000000000453231476623553700207450ustar00rootroot00000000000000//# PositionEngine.cc: Engine for TaQL UDF Position conversions //# Copyright (C) 2011 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { PositionEngine::PositionEngine() : itsValueType (0) {} PositionEngine::~PositionEngine() {} void PositionEngine::handlePosition (Int toValueType, const std::vector& args, uInt& argnr) { // Handle the input position values and possibly type. // Set values to unknown, because they might have been set by // handleMeasType (called from PositionUDF). itsRefType = MPosition::N_Types; itsInUnit = ""; itsValueType = 0; uInt nargnr = argnr+1; Bool asScalar = False; if (args[argnr]->dataType() == TableExprNodeRep::NTString) { // Position is given by observatory name. handleObservatory (args[argnr]); } else { if (!args[argnr]->isReal()) { throw AipsError ("Invalid position given in a MEAS function"); } // Normally positions must be given in an array, but a single constant // one can be 1, 2 or 3 scalars. TENShPtr node2; TENShPtr node3; if (args.size() > argnr && args[argnr]->isReal() && args[argnr]->valueType() == TableExprNodeRep::VTScalar) { asScalar = True; if (args.size() > nargnr && args[nargnr]->isReal() && args[nargnr]->valueType() == TableExprNodeRep::VTScalar) { node2 = args[nargnr]; nargnr++; } } // See if there is a node giving height(s) (or z in case of scalars). if (args.size() > nargnr && args[nargnr]->isReal()) { node3 = args[nargnr]; nargnr++; } uInt nval = nargnr-argnr; // See if there is a reference type. if (args.size() > nargnr && args[nargnr]->dataType() == TableExprNodeRep::NTString) { handleMeasType (args[nargnr], True); nargnr++; } // Process as scalars or as array. if (asScalar) { handleScalars (args[argnr], node2, node3, nval); } else { // Get the position arguments. if (node3) { // For time being, heights can only be given as constants. handlePosArray (args[argnr], node3); } else { // There is a single argument; handle it. handleMeasArray (args[argnr]); } } } // Skip the arguments handled. argnr = nargnr; // Determine the output unit, shape, and ndim. if (toValueType == 2) { itsOutUnit = "rad"; // angles } else if (toValueType > 0) { itsOutUnit = "m"; // xyz or height } adaptForConstant (itsConstants.shape(), abs(toValueType)); } String PositionEngine::stripMeasType (const String& typex) { itsValueType = 0; String type(typex); unsigned lens = type.size(); const char* suffices[] = {"XYZ", "LLH", "LL", "LONLAT", "H", "HEIGHT"}; const char* units[] = {"m", "", "rad","rad", "m", "m"}; int vtypes[] = {3, -3, 2, 2, 1, 1}; for (unsigned i=0; i suf.size() && type.substr(lens-suf.size()) == suf) { itsValueType = vtypes[i]; itsInUnit = units[i]; type = type.substr(0, lens-suf.size()); break; } } return type; } void PositionEngine::deriveAttr (const Unit& unit, Int nval) { // This function checks and sets attributes. // There are two attributes (itsInUnit and itsValueType) which can be // defined or undefined. If defined, it is checked if an attribute // matches the other attribute. If undefined, it is set if possible. // First set itsValueType if needed. if (itsValueType == 0) { itsValueType = nval; } else if (itsValueType == 2 && nval == 3) { // If given reftype defines LL, accept an height as well. itsValueType = -3; itsInUnit = ""; } else if (nval != 0 && nval != abs(itsValueType)) { throw AipsError("The nr of position values in a MEAS function does not " "match the reference type suffix"); } // Set itsInUnit if a value unit is given. Check if it conforms. if (! unit.empty()) { if (! itsInUnit.empty()) { // Check if the unit given in a position value matches. if (itsInUnit != unit) { throw AipsError("Unit of a position in a MEAS function does not " "match the reference type suffix"); } } itsInUnit = unit; } // Derive itsValueType from unit if needed and possible. if (itsValueType == 0 && !itsInUnit.empty()) { if (itsInUnit == Unit("rad")) { itsValueType = 2; // lon,lat } else if (itsInUnit == Unit("m")) { itsValueType = 3; // xyz } } // Derive the unit from itsValueType if needed and possible. if (itsInUnit.empty()) { if (itsValueType == 2) { itsInUnit = "rad"; // must be lon,lat } else if (itsValueType > 0) { itsInUnit = "m"; // can be h or xyz } } // Check the nr of values against the unit. // Set nr of values if undefined. if (itsInUnit.empty()) { if (itsValueType < 0) { // Use rad for lon,lat; height is accessed differently. itsInUnit = "rad"; } } else { if (itsValueType < 0) { throw AipsError("A position in a MEAS function given as llh " "should not have a unit"); } if (itsInUnit == Unit("m")) { if (itsValueType == 2) { throw AipsError("For unit m 1 or 3 position values should be given " "in a MEAS function"); } } else if (itsInUnit == Unit("rad")) { if (itsValueType != 2) { throw AipsError("For unit deg 2 position values should be given " "in a MEAS function"); } } else { throw AipsError ("Invalid unit given for a position value" " in a MEAS function (no length or angle)"); } } // Check if itsValueType is defined. if (itsValueType == 0) { throw AipsError("The value type of a position in a MEAS function is " "unknown; use a proper unit or reference type suffix"); } // Use default reference type if not given. if (itsRefType == MPosition::N_Types) { if (itsValueType == 3) { itsRefType = MPosition::ITRF; } else { itsRefType = MPosition::WGS84; } } } void PositionEngine::setValueType (Int valueType) { itsValueType = valueType; } void PositionEngine::handleScalars (const TENShPtr& e1, const TENShPtr& e2, const TENShPtr& e3, Int nval) { if (! e1->isConstant() || (e2 && ! e2->isConstant()) || (e3 && ! (e3->isConstant() && e3->valueType() == TableExprNodeRep::VTScalar))) { throw AipsError ("Scalar values given as position in a MEAS function " "must be constant values"); } Unit unit = e1->unit(); if (unit.empty() && e2) { unit = e2->unit(); } // Make sure llh is handled correctly if a unit is used. if (nval == 3 && itsValueType != 2 && unit == Unit("rad")) { itsValueType = -3; unit = ""; } deriveAttr (unit, nval); double v1 = e1->getDouble(0); double v2 = 0; double v3 = 0; Unit u1 = e1->unit(); Unit u2; Unit u3; if (e2) { v2 = e2->getDouble(0); u2 = e2->unit(); } if (e3) { v3 = e3->getDouble(0); u3 = e3->unit(); } if (u1.empty()) u1 = itsInUnit; if (u2.empty()) u2 = itsInUnit; if (u3.empty()) u3 = "m"; itsConstants.resize (IPosition(1,1)); itsConstants.data()[0] = makePosition(Quantity(v1, u1), Quantity(v2, u2), Quantity(v3, u3)); } MPosition PositionEngine::makePosition (const Quantity& q1, const Quantity& q2, const Quantity& q3) const { if (itsValueType == 1) { // Height; towards pole. return MPosition (MVPosition(q1), itsRefType); } else if (itsValueType == 3) { // xyz. Unit m("m"); return MPosition (MVPosition(q1.getValue(m), q2.getValue(m), q3.getValue(m)), itsRefType); } // height,lon,lat return MPosition (q3, q1, q2, itsRefType); } void PositionEngine::handleObservatory (const TENShPtr& operand) { // For the time being the observatory names have to be constants. // In the future, it could be a table column. if (! operand->isConstant()) { throw AipsError ("An observatory name used as position in a MEAS function" " must be a constant string"); } Array names = operand->getStringAS(0).array(); itsConstants.resize (names.shape()); for (uInt i=0; iisReal() || operand->valueType() != TableExprNodeRep::VTArray) { throw AipsError ("A single double argument given as position in a " "MEAS function must be a double array of values " "defining x,y,z or lon,lat or height"); } // Use defaults for reference and value type if not given. deriveAttr (operand->unit(), 0); // Handle possibly given constants. if (operand->isConstant()) { handleConstant (operand); return; } // Try if the argument is a column. // If found, try to handle it as a TableMeasures column. const TableExprNodeArrayColumn* colNode = dynamic_cast(operand.get()); Bool directCol = True; if (!colNode) { // The node is an expression, not a column. directCol = False; // Try if the node is an array part of a column. TableExprNodeArrayPart* partNode = dynamic_cast(operand.get()); if (partNode) { colNode = partNode->getColumnNode(); } } if (colNode) { // Try if the column contains measures. const TableColumn& tabCol = colNode->getColumn(); itsShape = tabCol.shapeColumn(); itsNDim = tabCol.ndimColumn(); if (TableMeasDescBase::hasMeasures (tabCol)) { ArrayMeasColumn measTmp(tabCol.table(), tabCol.columnDesc().name()); // Get and check the node's refType if it is fixed. MPosition::Types nodeRefType = MPosition::N_Types; if (! (measTmp.measDesc().isRefCodeVariable() || measTmp.measDesc().hasOffset())) { uInt refCode = measTmp.measDesc().getRefCode(); itsRefType = static_cast(refCode); } // A direct column can directly be accessed using TableMeasures. if (directCol) { itsMeasCol.reference (measTmp); return; } // It is a part, so we cannot use TableMeasures. // If the reference type is variable, the user should index after // the meas.pos function. if (nodeRefType == MPosition::N_Types) { throw AipsError ("Column " + tabCol.columnDesc().name() + ", which has a variable reference frame, " "is used in a MEAS function with slicing. " "The slicing should be done after the function " "like 'meas.pos('ITRF',POSITION)[0:3]'"); } } } if (itsMeasCol.isNull()) { if (itsRefType == MPosition::N_Types) { throw AipsError("No reference type given for a non-constant MEAS " "function position argument"); } itsExprNode = operand; } } */ void PositionEngine::handlePosArray (const TENShPtr& anglesNode, const TENShPtr& heightNode) { if (!anglesNode->isReal() || anglesNode->valueType() != TableExprNodeRep::VTArray || !anglesNode->isConstant() || !heightNode->isReal() || heightNode->valueType() != TableExprNodeRep::VTArray || !heightNode->isConstant()) { throw AipsError ("Positions given as angles,heights in a MEAS " "function must be constant double arrays of values"); } if (itsValueType == 3) { throw AipsError ("Position reference type suffix in a MEAS function is " "given as xyz, while heights are used"); } Array angles = anglesNode->getArrayDouble(0).array(); if (angles.empty() || angles.shape()[0] %2 != 0) { throw AipsError ("Angles given as position in a MEAS function must " "be a constant double array of multiple of 2 values"); } Array height = heightNode->getArrayDouble(0).array(); if (angles.size() != 2*height.size()) { throw AipsError ("Angles and heights given as position in a MEAS " "function have mismatching sizes"); } // Set unit and reference type is undefined. Unit aUnit = anglesNode->unit(); Unit hUnit = heightNode->unit(); if (aUnit.empty()) aUnit = "rad"; if (hUnit.empty()) hUnit = "m"; Vector aVec(angles.reform(IPosition(1,angles.size()))); Vector hVec(height.reform(IPosition(1,height.size()))); if (itsRefType == MPosition::N_Types) { itsRefType = MPosition::WGS84; } itsConstants.resize (height.shape()); for (uInt i=0; i& positions) { Array values = operand.getArrayDouble(id); uInt nrv = abs(itsValueType); const IPosition& shape = values.shape(); if (shape[0] % nrv != 0) { throw AipsError ("Number of values in a position in a MEAS function " "should be a multiple of " + String::toString(nrv)); } IPosition posShape; if (shape[0] == nrv && shape.size() > 1) { posShape = shape.getLast (shape.size() - 1); } else { posShape = shape; posShape[0] /= nrv; } positions.resize (posShape); Quantity q1(0, itsInUnit); Quantity q2(0, itsInUnit); Quantity q3(0, itsInUnit); if (itsValueType != 1 && itsValueType != 3) { q3 = Quantity(0, "m"); } Bool delIt; const Double* valVec = values.getStorage (delIt); MPosition* posVec = positions.data(); for (uInt i=0; i 1) { q2.setValue (valVec[i*nrv+1]); if (nrv == 3) { q3.setValue (valVec[i*nrv+2]); } } posVec[i] = makePosition(q1, q2, q3); } values.freeStorage (valVec, delIt); } Array PositionEngine::getPositions (const TableExprId& id) { if (itsConstants.size() > 0) { return itsConstants; } if (!itsMeasArrCol.isNull()) { return itsMeasArrCol(id.rownr()); } // Read from expression. Array pos; handleValues (itsExprNode, id, pos); return pos; } Array PositionEngine::getArrayDouble (const TableExprId& id, MPosition::Types toRefType, Int toValueType) { DebugAssert (id.byRow(), AipsError); Array res (getPositions(id)); Array out; if (res.size() > 0) { if (toValueType == 1) { out.resize (res.shape()); } else { IPosition shape(1,3); if (toValueType == 2) { shape[0] = 2; } if (res.size() > 1) { shape.append (res.shape()); } out.resize (shape); } VectorIterator outIter(out); Array::const_contiter resIter = res.cbegin(); for (uInt i=0; i ang = pos.getValue().getAngle().getValue(); out.data()[i*3] = ang[0]; out.data()[i*3+1] = ang[1]; out.data()[i*3+2] = pos.getValue().getLength().getValue();; } else { if (toValueType == 3) { // Get as xyz. outIter.vector() = pos.getValue().getValue(); } else if (toValueType == 2) { // Get as lon,lat. outIter.vector() = pos.getValue().getAngle().getValue(); } outIter.next(); } } } return out; } } //end namespace casacore-3.7.1/meas/MeasUDF/PositionEngine.h000066400000000000000000000157151476623553700206110ustar00rootroot00000000000000//# PositionEngine.h: Engine for TaQL UDF Position conversions //# Copyright (C) 2011 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEAS_POSITIONENGINE_H #define MEAS_POSITIONENGINE_H //# Includes #include #include #include #include #include namespace casacore { // // Engine for TaQL UDF Position conversions // // // // // //# Classes you should understand before using this one. //
      • EngineBase // // // PositionEngine defines Engines (user defined functions) that can be used // in TaQL to convert Measures for positions. // In this way such derived values appear to be ordinary TaQL functions. // // In TaQL these functions can be called like: // // meas.pos (toref, pos) // meas.wgs (pos) // meas.itrfxyz (pos) // For example, // meas.pos ('ITRF', [1e6m,1e6m,1e6m], 'WGS84') // //
          //
        • toref is a single constant string defining the // reference frame to convert to. Note it should be omitted for // the functions (e.g., meas.wgs) with an implicit destination // reference frame. // The reference type WGS84 or ITRF can optionally have the suffix // XYZ, LLH, LL (or LONLAT) and H (or HEIGHT) telling how the result // should be returned. //
        • pos specifies the position(s) which can be done in // various ways. //
            //
          • An array of positions given as xyz, as lonlat, as lon-lat-height // or as height. The latter is taken towards the pole. // Note that specifying as lon-lat-height precludes use of units // (angle and length units cannot be mixed in a TaQL value). // It can be given as a single list or a multi-dim array. // If given as lonlat it can be followed by an array defining // the height for each lon,lat pair (their sizes should match). // Finally it can be followed by a string defining the source // reference type, which defaults to ITRF for x,y,z and WGS84 // for lon,lat. // The source reference type can contain the suffix XYZ, LLH, LL // or H to tell how the values are specified. If no suffix is given, // it is derived from the unit of the first value (angle means LL, // length means XYZ). //
          • If a single constant position is used, it can be given as // 1, 2 or 3 scalar values, optionally followed by the source // reference type. If x,y,z or lon,lat or lon,lat,h or h is given // is derived in the same way as above. //
          • The name of a column in a table or a subset of it such as // POSITION[0,]. Often this is a TableMeasures column // which is recognized as such, also its source reference frame. // If such a column is used in a expression, it will not be // recognized as a TableMeasures column and its reference frame // should be given. //
          • As a list containing (case-insensitive) names of known // observatories such as 'WSRT' or 'VLA'. //
          //
        // // The result of the function is an array with shape [2|3,pos] if // lon,lat(,h) or x,y,z is returned and shape [pos] if height is returned. // The last element is the shape of the position argument. It is omitted if // it has length 1. In such a case getting the height results in a scalar. //
        // // // // Get the WGS84 lon,lat of WSRT (in degrees). // meas.wgsll ('WSRT') deg // // Get the ITRF x,y,z of WSRT and VLA. // meas.itrfxyz (['WSRT', 'VLA']) // // // // It makes it possible to handle measures in TaQL. // class PositionEngine: public MeasEngine { public: PositionEngine(); virtual ~PositionEngine(); // Get the value type. It also gives the nr of output values per position. // 0=default, 1=height, 2=angles, 3=xyz Int valueType() const { return itsValueType; } // Get the values. Array getArrayDouble (const TableExprId& id, MPosition::Types toRefType, Int toValueType); // Get the positions. Array getPositions (const TableExprId& id); // Handle the argument(s) giving the input positions and reference type. // The position can be a column in a table. void handlePosition (Int toValueType, const std::vector& args, uInt& argnr); private: virtual String stripMeasType (const String& type); virtual void deriveAttr (const Unit& unit, Int nval); // Let a derived class set its value type. // By default is does nothing. virtual void setValueType (Int valueType); // Make an MPosition from xyz or height,angles. MPosition makePosition (const Quantity& qh, const Quantity& q1, const Quantity& q2) const; void handleScalars (const TENShPtr& e1, const TENShPtr& e2, const TENShPtr& e3, Int nval); void handleObservatory (const TENShPtr& operand); void handlePosArray (const TENShPtr& angles, const TENShPtr& height); virtual void handleValues (TableExprNode& operand, const TableExprId& id, Array& positions); //# Data members. //# 0=unknown, 1=height, 2=angles, 3=xyz, -3=angles,height Int itsValueType; }; } //end namespace #endif casacore-3.7.1/meas/MeasUDF/PositionUDF.cc000066400000000000000000000111501476623553700201450ustar00rootroot00000000000000//# PositionUDF.cc: TaQL UDF for Position conversions //# Copyright (C) 2011 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include namespace casacore { PositionUDF::PositionUDF (FuncType type) : itsType (type) {} UDFBase* PositionUDF::makePOS (const String&) { return new PositionUDF (POS); } UDFBase* PositionUDF::makeITRFXYZ (const String&) { return new PositionUDF (ITRFXYZ); } UDFBase* PositionUDF::makeITRFLLH (const String&) { return new PositionUDF (ITRFLLH); } UDFBase* PositionUDF::makeITRFLL (const String&) { return new PositionUDF (ITRFLL); } UDFBase* PositionUDF::makeITRFH (const String&) { return new PositionUDF (ITRFH); } UDFBase* PositionUDF::makeWGSXYZ (const String&) { return new PositionUDF (WGSXYZ); } UDFBase* PositionUDF::makeWGSLLH (const String&) { return new PositionUDF (WGSLLH); } UDFBase* PositionUDF::makeWGSLL (const String&) { return new PositionUDF (WGSLL); } UDFBase* PositionUDF::makeWGSH (const String&) { return new PositionUDF (WGSH); } void PositionUDF::setup (const Table&, const TaQLStyle&) { if (operands().size() < 1) { throw AipsError ("No arguments given in a MEAS.POS function"); } // Get the 'to' reference and value type. // Determine the argnr of the position. uInt argnr = 0; if (itsType == ITRFXYZ) { itsRefType = MPosition::ITRF; itsValueType = 3; } else if (itsType == ITRFLL) { itsRefType = MPosition::ITRF; itsValueType = 2; } else if (itsType == ITRFLLH) { itsRefType = MPosition::ITRF; itsValueType = -3; } else if (itsType == ITRFH) { itsRefType = MPosition::ITRF; itsValueType = 1; } else if (itsType == WGSXYZ) { itsRefType = MPosition::WGS84; itsValueType = 3; } else if (itsType == WGSLLH) { itsRefType = MPosition::WGS84; itsValueType = -3; } else if (itsType == WGSLL) { itsRefType = MPosition::WGS84; itsValueType = 2; } else if (itsType == WGSH) { itsRefType = MPosition::WGS84; itsValueType = 1; } else { itsEngine.handleMeasType (operands()[0], True); itsRefType = itsEngine.refType(); itsValueType = itsEngine.valueType(); argnr = 1; } // Default value type is xyz. if (itsValueType == 0) { itsValueType = 3; } // Get the positions. if (operands().size() <= argnr) { throw AipsError ("No position given in MEAS.POS function"); } itsEngine.handlePosition (itsValueType, operands(), argnr); if (operands().size() > argnr) { throw AipsError ("Too many arguments given in MEAS.POS function"); } // Set datatype, shape, unit, etc. setDataType (TableExprNodeRep::NTDouble); const IPosition& shape = itsEngine.shape(); if (shape.size() > 0) { if (shape.product() == 1) { setNDim (0); // scalar } else { setShape (shape); } } else { setNDim (itsEngine.ndim()); } setUnit (itsEngine.unit().getName()); setConstant (itsEngine.isConstant()); setAttributes (itsEngine.makeAttributes (itsRefType, itsValueType)); } Double PositionUDF::getDouble (const TableExprId& id) { return itsEngine.getArrayDouble (id, itsRefType, itsValueType).data()[0]; } MArray PositionUDF::getArrayDouble (const TableExprId& id) { return MArray(itsEngine.getArrayDouble (id, itsRefType, itsValueType)); } } //end namespace casacore-3.7.1/meas/MeasUDF/PositionUDF.h000066400000000000000000000074001476623553700200120ustar00rootroot00000000000000//# PositionUDF.h: TaQL UDFs for Position conversions //# Copyright (C) 2011 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEAS_POSITIONUDF_H #define MEAS_POSITIONUDF_H //# Includes #include #include #include #include namespace casacore { // // TaQL UDFs for Position conversions. // // // // // //# Classes you should understand before using this one. //
      • UDFBase // // // PositionUDF defines UDFs (user defined functions) that can be used in TaQL // to convert Measures for positions. // In this way such derived values appear to be ordinary TaQL functions. // // A function is called like: // // meas.pos (toref, pos) // For example, // meas.dir ('ITRF', [1rad,1rad], 'WGS84') // //
          //
        • // toref is a single constant string. //
        • // pos can have various value types. A single numeric array is // a series of RA,DEC in J2000. If given as a set, the last argument of the // set can be the reference types of the values in the set. The values can // be strings (indicating planetary objects) or value pairs giving lon,lat. // The default reference type is J2000. //
        // All functions have data type double and unit radian. //
        // // It makes it possible to handle measures in TaQL. // class PositionUDF: public UDFBase { public: // Define the possible function types. enum FuncType {POS, ITRFXYZ, ITRFLLH, ITRFLL, ITRFH, WGSXYZ, WGSLLH, WGSLL, WGSH}; explicit PositionUDF (FuncType); // Function to create an object. static UDFBase* makePOS (const String&); static UDFBase* makeITRFXYZ (const String&); static UDFBase* makeITRFLLH (const String&); static UDFBase* makeITRFLL (const String&); static UDFBase* makeITRFH (const String&); static UDFBase* makeWGSXYZ (const String&); static UDFBase* makeWGSLLH (const String&); static UDFBase* makeWGSLL (const String&); static UDFBase* makeWGSH (const String&); // Setup the object. virtual void setup (const Table&, const TaQLStyle&); // Get the value. virtual Double getDouble (const TableExprId& id); virtual MArray getArrayDouble (const TableExprId& id); private: //# Data members. PositionEngine itsEngine; FuncType itsType; MPosition::Types itsRefType; Int itsValueType; }; } //end namespace #endif casacore-3.7.1/meas/MeasUDF/RadialVelocityEngine.cc000066400000000000000000000204041476623553700220450ustar00rootroot00000000000000//# RadialVelocityEngine.cc: Engine for TaQL UDF RadialVelocity conversions //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include namespace casacore { RadialVelocityEngine::RadialVelocityEngine() : itsDopplerEngine (0), itsDirectionEngine(0), itsEpochEngine (0), itsPositionEngine (0) {} void RadialVelocityEngine::handleRadialVelocity (vector& args, uInt& argnr) { // Initialize type to unknown. itsFrame.set (MRadialVelocity()); itsRefType = MRadialVelocity::N_Types; // Check if the value is a double. if (!args[argnr]->isReal()) { throw AipsError ("Invalid radial velocity given in a MEAS function"); } // Values can be given as [t1,t2,...],reftype uInt nargnr = argnr+1; // See if there is a reference type. if (args.size() > nargnr && args[nargnr]->dataType() == TableExprNodeRep::NTString) { if (handleMeasType (args[nargnr], False)) { nargnr++; } } handleMeasArray (args[argnr]); // Skip the arguments handled. argnr = nargnr; // Determine the output unit, shape, and ndim. itsOutUnit = "km/s"; adaptForConstant (itsConstants.shape()); } void RadialVelocityEngine::handleValues (TableExprNode& operand, const TableExprId& id, Array& radVels) { // Use default reference type LSRK if not given. if (itsRefType == MRadialVelocity::N_Types) { itsRefType = MRadialVelocity::LSRK; } // Get values. if (itsDopplerEngine) { Array dopplers = itsDopplerEngine->getDopplers(id); radVels.resize (dopplers.shape()); Array::const_iterator dopIter = dopplers.begin(); MRadialVelocity* rvVec = radVels.data(); for (uInt i=0; i values (operand.getDoubleAS(id).array()); // Get unit (default km/s). Unit unit = operand.unit(); if (unit.empty()) { unit = "km/s"; } radVels.resize (values.shape()); Quantity q(0, unit); Bool delIt; const Double* valVec = values.getStorage (delIt); MRadialVelocity* rvVec = radVels.data(); for (uInt i=0; i RadialVelocityEngine::getRadialVelocities (const TableExprId& id) { if (itsConstants.size() > 0) { return itsConstants; } if (!itsMeasScaCol.isNull()) { return Vector(1, itsMeasScaCol(id.rownr())); } else if (!itsMeasArrCol.isNull()) { return itsMeasArrCol(id.rownr()); } Array radVels; handleValues (itsExprNode, id, radVels); return radVels; } Array RadialVelocityEngine::getArrayDouble (const TableExprId& id) { DebugAssert (id.byRow(), AipsError); Array res (getRadialVelocities(id)); // Get directions, epochs and positions if given. Array dir(IPosition(1,1)); Array eps(IPosition(1,1)); Array pos(IPosition(1,1)); if (itsDirectionEngine) { dir.reference (itsDirectionEngine->getDirections (id)); } if (itsEpochEngine) { eps.reference (itsEpochEngine->getEpochs (id)); } if (itsPositionEngine) { pos.reference (itsPositionEngine->getPositions (id)); } // Convert the radial velocity to the given type for all dir,epoch,pos. Array out; if (res.size() > 0 && dir.size() > 0 && eps.size() > 0 && pos.size() > 0) { IPosition shape = res.shape(); // Only add the other axes if one of them has multiple values. if (dir.size() > 1 || eps.size() > 1 || pos.size() > 1) { shape.append (dir.shape()); shape.append (eps.shape()); shape.append (pos.shape()); } out.resize (shape); double* outPtr = out.data(); for (Array::const_contiter resIter = res.cbegin(); resIter != res.cend(); ++resIter) { // The frame has to be set in the RadialVelocity. MeasRef mr(resIter->getRef()); mr.set (itsFrame); MRadialVelocity radvel(resIter->getValue(), mr); itsConverter.setModel (radvel); for (Array::const_contiter dirIter = dir.cbegin(); dirIter != dir.cend(); ++dirIter) { // Convert to desired position. if (itsDirectionEngine) { itsFrame.resetDirection (*dirIter); } for (Array::const_contiter epsIter = eps.cbegin(); epsIter != eps.cend(); ++epsIter) { // Convert to desired epoch. if (itsEpochEngine) { itsFrame.resetEpoch (*epsIter); } for (Array::const_contiter posIter = pos.cbegin(); posIter != pos.cend(); ++posIter) { // Convert to desired reference type. if (itsPositionEngine) { itsFrame.resetPosition (*posIter); } MRadialVelocity mf = itsConverter(); *outPtr++ = mf.getValue().get("km/s").getValue(); } } } } } return out; } } //end namespace casacore-3.7.1/meas/MeasUDF/RadialVelocityEngine.h000066400000000000000000000125711476623553700217150ustar00rootroot00000000000000//# RadialVelocityEngine.h: Engine for TaQL UDF RadialVelocity conversions //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEAS_RADIALVELOCITYENGINE_H #define MEAS_RADIALVELOCITYENGINE_H //# Includes #include #include #include #include #include namespace casacore { //# Forward declarations class DopplerEngine; class DirectionEngine; class EpochEngine; class PositionEngine; // // Engine for TaQL UDF RadialVelocity conversions // // // // // //# Classes you should understand before using this one. //
      • EngineBase // // // RadialVelocityEngine defines Engines (user defined functions) that can be used // in TaQL to convert Measures for radial velocities. // In this way such derived values appear to be ordinary TaQL functions. // // RadialVelocity conversions require a MeasFrame containing sky direction, // epoch and position on earth. // In TaQL these functions can be called like: // // meas.rv ('TOPO', 1 'm/s', 'LSRK', 'CasA', date(), // [1e6m,1e6m,1e6m], 'WGS84') // // which converts the radial velocity from LSRK to TOPO. //
          //
        • // toref is a single constant string. //
        • // pos can have various value types. A single numeric array is // a series of RA,DEC in J2000. If given as a set, the last argument of the // set can be the reference types of the values in the set. The values can // be strings (indicating planetary objects) or value pairs giving lon,lat. // The default reference type is J2000. //
        // All such functions return data with type double and unit Hz. // // Radial velocities can be given like: // [v1,v2,...], fromRef // where fromRef is the reference type. // // A radial velocity can also be a table column which usually knows its type. // It can also be an expression (e.g. RADIALVELOCITY[0,]) which also knows the type. //
        // // It makes it possible to handle measures in TaQL. // class RadialVelocityEngine: public MeasEngine { public: RadialVelocityEngine(); // Get the values. Array getArrayDouble (const TableExprId& id); // Get the radial velocities. Array getRadialVelocities (const TableExprId& id); // Handle the argument(s) giving the input radial velocities and reference type. // The radial velocity can be a column in a table. // If 'proper' is True, it is tested if a proper radial velocity is given // (with proper unit and/or type). If not. False is returned. // If 'proper' is False, the value is always considered as radial velocity. void handleRadialVelocity (std::vector& args, uInt& argnr); // Set the MeasConvert object. void setConverter (MRadialVelocity::Types toType); // Set the possible doppler engine. // It can be done only once. void setDopplerEngine (DopplerEngine& engine); // Set the possible direction engine. // It can be done only once. void setDirectionEngine (DirectionEngine& engine); // Set the possible epoch engine. // It can be done only once. void setEpochEngine (EpochEngine& engine); // Set the possible position engine. // It can be done only once. void setPositionEngine (PositionEngine& engine); private: virtual void handleValues (TableExprNode& operand, const TableExprId& id, Array& radialVelocities); //# Data members. MeasFrame itsFrame; //# frame used by converter MRadialVelocity::Convert itsConverter; DopplerEngine* itsDopplerEngine; DirectionEngine* itsDirectionEngine; EpochEngine* itsEpochEngine; PositionEngine* itsPositionEngine; }; } //end namespace #endif casacore-3.7.1/meas/MeasUDF/RadialVelocityUDF.cc000066400000000000000000000103631476623553700212610ustar00rootroot00000000000000//# RadialVelocityUDF.cc: TaQL UDF for RadialVelocity conversions //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include namespace casacore { RadialVelocityUDF::RadialVelocityUDF() {} UDFBase* RadialVelocityUDF::makeRADVEL (const String&) { return new RadialVelocityUDF(); } void RadialVelocityUDF::setup (const Table&, const TaQLStyle&) { if (operands().size() < 1) { throw AipsError ("No arguments given in a MEAS.RADVEL function"); } // Get the 'to' reference type. // Determine the argnr of the epoch. uInt argnr = 0; itsEngine.handleMeasType (operands()[0], True); itsRefType = itsEngine.refType(); argnr = 1; // Get the radialVelocities. if (operands().size() <= argnr) { throw AipsError ("No radial velocity given in a MEAS.RADVEL function"); } // First try if givben as doppler values. Bool asDoppler = tryDoppler (argnr); // If not, it must be radialvelocity plus possibly frame info. if (! asDoppler) { itsEngine.handleRadialVelocity (operands(), argnr); // Handle possible Direction arguments. if (operands().size() > argnr) { itsDirectionEngine.handleDirection (operands(), argnr, False, False); itsEngine.setDirectionEngine (itsDirectionEngine); } // Handle possible Epoch arguments. if (operands().size() > argnr) { itsEpochEngine.handleEpoch (operands(), argnr); itsEngine.setEpochEngine (itsEpochEngine); } // Handle possible Position arguments. if (operands().size() > argnr) { itsPositionEngine.handlePosition (0, operands(), argnr); itsEngine.setPositionEngine (itsPositionEngine); } } if (operands().size() > argnr) { throw AipsError ("Too many arguments given in a MEAS.RADVEL function"); } itsEngine.setConverter (itsRefType); // Set datatype, shape, unit, etc. setDataType (TableExprNodeRep::NTDouble); const IPosition& shape = itsEngine.shape(); if (shape.size() > 0) { if (shape.product() == 1) { setNDim (0); // scalar } else { setShape (shape); } } else { setNDim (itsEngine.ndim()); } setUnit (itsEngine.unit().getName()); setConstant (itsEngine.isConstant()); setAttributes (itsEngine.makeAttributes (itsRefType)); } Bool RadialVelocityUDF::tryDoppler (uInt& argnr) { // Try if a doppler value is given. // It is if no unit is given and a possible type is doppler. if (operands().size() > argnr && operands()[argnr]->unit().empty()) { uInt argnrOld = argnr; try { itsDopplerEngine.handleDoppler (operands(), argnr, False, False); itsEngine.setDopplerEngine (itsDopplerEngine); return True; } catch (const AipsError&) { } argnr = argnrOld; } return False; } Double RadialVelocityUDF::getDouble (const TableExprId& id) { return getArrayDouble(id).array().data()[0]; } MArray RadialVelocityUDF::getArrayDouble (const TableExprId& id) { return MArray(itsEngine.getArrayDouble (id)); } } //end namespace casacore-3.7.1/meas/MeasUDF/RadialVelocityUDF.h000066400000000000000000000100751476623553700211230ustar00rootroot00000000000000//# RadialVelocityUDF.h: TaQL UDFs for RadialVelocity conversions //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEAS_RADIALVELOCITYUDF_H #define MEAS_RADIALVELOCITYUDF_H //# Includes #include #include #include #include #include #include #include #include namespace casacore { // // TaQL UDFs for RadialVelocity conversions. // // // // // //# Classes you should understand before using this one. //
      • UDFBase // // // RadialVelocityUDF defines UDFs (user defined functions) that can be used in TaQL // to convert Measures for radialVelocitys. // Special functions exist to convert to hourangle and azimuth/elevation. // In this way such derived values appear to be ordinary TaQL functions. // // A function is called like: // // meas.dir (toref, dir, time, pos) // meas.dir (toref, dir, time, pos) // For example, // meas.dir ('B1950', [2rad,1rad]) // meas.dir ('B1950', [[2rad,1rad], 'J2000']) // meas.dir ('APP', 'MOON', TIME, [[5d12m, 52deg, 11m], 'WGS84']) // meas.dir ('APP', 'MOON', TIME, POSITION) // Or // meas.dir (toref, fromref, dir, pos, time) // //
          //
        • // toref is a single constant string. //
        • // dir can have various value types. A single numeric array is // a series of RA,DEC in J2000. If given as a set, the last argument of the // set can be the reference types of the values in the set. The values can // be strings (indicating planetary objects) or value pairs giving lon,lat. // The default reference type is J2000. //
        // All functions have data type double and unit radian. //
        // // It makes it possible to handle measures in TaQL. // class RadialVelocityUDF: public UDFBase { public: // Create for the given function type. explicit RadialVelocityUDF(); // Function to create an object. static UDFBase* makeRADVEL (const String&); // Setup the object. virtual void setup (const Table&, const TaQLStyle&); // Get the value. virtual Double getDouble (const TableExprId& id); virtual MArray getArrayDouble (const TableExprId& id); private: // Try if the value is given as Doppler. True is returned if so. Bool tryDoppler (uInt& argnr); //# Data members. RadialVelocityEngine itsEngine; DopplerEngine itsDopplerEngine; DirectionEngine itsDirectionEngine; EpochEngine itsEpochEngine; PositionEngine itsPositionEngine; MRadialVelocity::Types itsRefType; }; } //end namespace #endif casacore-3.7.1/meas/MeasUDF/Register.cc000066400000000000000000000432661476623553700176030ustar00rootroot00000000000000//# Register.cc: Register Measure UDFs //# Copyright (C) 2011 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace casacore; using namespace std; void register_meas() { // Register the TaQL Meas UDFs. // All of them should be shown in the showFuncs functions below. UDFBase::registerUDF ("meas.HELP", HelpMeasUDF::makeHELP); UDFBase::registerUDF ("meas.POS", PositionUDF::makePOS); UDFBase::registerUDF ("meas.POSITION", PositionUDF::makePOS); UDFBase::registerUDF ("meas.ITRFXYZ", PositionUDF::makeITRFXYZ); UDFBase::registerUDF ("meas.ITRFLLH", PositionUDF::makeITRFLLH); UDFBase::registerUDF ("meas.ITRFLL", PositionUDF::makeITRFLL); UDFBase::registerUDF ("meas.ITRFLONLAT", PositionUDF::makeITRFLL); UDFBase::registerUDF ("meas.ITRFH", PositionUDF::makeITRFH); UDFBase::registerUDF ("meas.ITRFHEIGHT", PositionUDF::makeITRFH); UDFBase::registerUDF ("meas.WGS", PositionUDF::makeWGSXYZ); UDFBase::registerUDF ("meas.WGSXYZ", PositionUDF::makeWGSXYZ); UDFBase::registerUDF ("meas.WGSLLH", PositionUDF::makeWGSLLH); UDFBase::registerUDF ("meas.WGSLL", PositionUDF::makeWGSLL); UDFBase::registerUDF ("meas.WGSLONLAT", PositionUDF::makeWGSLL); UDFBase::registerUDF ("meas.WGSH", PositionUDF::makeWGSH); UDFBase::registerUDF ("meas.WGSHEIGHT", PositionUDF::makeWGSH); UDFBase::registerUDF ("meas.EPOCH", EpochUDF::makeEPOCH); UDFBase::registerUDF ("meas.LAST", EpochUDF::makeLAST); UDFBase::registerUDF ("meas.LST", EpochUDF::makeLAST); UDFBase::registerUDF ("meas.DIR", DirectionUDF::makeDIR); UDFBase::registerUDF ("meas.DIRECTION", DirectionUDF::makeDIR); UDFBase::registerUDF ("meas.DIRCOS", DirectionUDF::makeDIRCOS); UDFBase::registerUDF ("meas.DIRECTIONCOSINE", DirectionUDF::makeDIRCOS); UDFBase::registerUDF ("meas.HADEC", DirectionUDF::makeHADEC); UDFBase::registerUDF ("meas.AZEL", DirectionUDF::makeAZEL); UDFBase::registerUDF ("meas.APP", DirectionUDF::makeAPP); UDFBase::registerUDF ("meas.APPARENT", DirectionUDF::makeAPP); UDFBase::registerUDF ("meas.J2000", DirectionUDF::makeJ2000); UDFBase::registerUDF ("meas.B1950", DirectionUDF::makeB1950); UDFBase::registerUDF ("meas.ECL", DirectionUDF::makeECL); UDFBase::registerUDF ("meas.ECLIPTIC", DirectionUDF::makeECL); UDFBase::registerUDF ("meas.GAL", DirectionUDF::makeGAL); UDFBase::registerUDF ("meas.GALACTIC", DirectionUDF::makeGAL); UDFBase::registerUDF ("meas.SGAL", DirectionUDF::makeSGAL); UDFBase::registerUDF ("meas.SUPERGAL", DirectionUDF::makeSGAL); UDFBase::registerUDF ("meas.SUPERGALACTIC", DirectionUDF::makeSGAL); UDFBase::registerUDF ("meas.ITRFD", DirectionUDF::makeITRF); UDFBase::registerUDF ("meas.ITRFDIR", DirectionUDF::makeITRF); UDFBase::registerUDF ("meas.ITRFDIRECTION", DirectionUDF::makeITRF); UDFBase::registerUDF ("meas.RISET", DirectionUDF::makeRISESET); UDFBase::registerUDF ("meas.RISESET", DirectionUDF::makeRISESET); UDFBase::registerUDF ("meas.EM", EarthMagneticUDF::makeEMXYZ); UDFBase::registerUDF ("meas.EARTHMAGNETIC", EarthMagneticUDF::makeEMXYZ); UDFBase::registerUDF ("meas.EMXYZ", EarthMagneticUDF::makeEMXYZ); UDFBase::registerUDF ("meas.EMANG", EarthMagneticUDF::makeEMANG); UDFBase::registerUDF ("meas.EMANGLES", EarthMagneticUDF::makeEMANG); UDFBase::registerUDF ("meas.EMLEN", EarthMagneticUDF::makeEMLEN); UDFBase::registerUDF ("meas.EMLENGTH", EarthMagneticUDF::makeEMLEN); UDFBase::registerUDF ("meas.IGRF", EarthMagneticUDF::makeIGRFXYZ); UDFBase::registerUDF ("meas.IGRFXYZ", EarthMagneticUDF::makeIGRFXYZ); UDFBase::registerUDF ("meas.IGRFANG", EarthMagneticUDF::makeIGRFANG); UDFBase::registerUDF ("meas.IGRFANGLES", EarthMagneticUDF::makeIGRFANG); UDFBase::registerUDF ("meas.IGRFLEN", EarthMagneticUDF::makeIGRFLEN); UDFBase::registerUDF ("meas.IGRFLENGTH", EarthMagneticUDF::makeIGRFLEN); UDFBase::registerUDF ("meas.IGRFLOS", EarthMagneticUDF::makeIGRFLOS); UDFBase::registerUDF ("meas.IGRFLONG", EarthMagneticUDF::makeIGRFLONG); UDFBase::registerUDF ("meas.FREQ", FrequencyUDF::makeFREQ); UDFBase::registerUDF ("meas.FREQUENCY", FrequencyUDF::makeFREQ); UDFBase::registerUDF ("meas.REST", FrequencyUDF::makeREST); UDFBase::registerUDF ("meas.RESTFREQ", FrequencyUDF::makeREST); UDFBase::registerUDF ("meas.RESTFREQUENCY", FrequencyUDF::makeREST); UDFBase::registerUDF ("meas.SHIFT", FrequencyUDF::makeSHIFT); UDFBase::registerUDF ("meas.SHIFTFREQ", FrequencyUDF::makeSHIFT); UDFBase::registerUDF ("meas.SHIFTFREQUENCY",FrequencyUDF::makeSHIFT); UDFBase::registerUDF ("meas.RV", RadialVelocityUDF::makeRADVEL); UDFBase::registerUDF ("meas.RADVEL", RadialVelocityUDF::makeRADVEL); UDFBase::registerUDF ("meas.RADIALVELOCITY",RadialVelocityUDF::makeRADVEL); UDFBase::registerUDF ("meas.DOPPLER", DopplerUDF::makeDOPPLER); UDFBase::registerUDF ("meas.REDSHIFT", DopplerUDF::makeDOPPLER); } namespace casacore { void HelpMeasUDF::showFuncsEpoch (ostream& os, Bool showTypes) { os << "Epoch conversion functions:" << endl; os << " MEAS.EPOCH (type, epoch [,position]) convert to given type" << endl; os << " MEAS.LAST (epoch, position) convert to local sidereal time" << endl; os << " LST is a synonym for LAST" << endl; if (showTypes) { os << endl; os << TaQLShow::showMeasTypes ("epoch"); } } void HelpMeasUDF::showFuncsPosition (ostream& os, Bool showTypes) { os << "Position conversion functions:" << endl; os << " MEAS.POS (type, position) convert to given type" << endl; os << " POSITION is a synonym for POS" << endl; os << " MEAS.ITRFXYZ (position) convert to ITRF XYZ coord" << endl; os << " MEAS.ITRFLL (position) convert to ITRF LonLat" << endl; os << " ITRFLONLAT is a synonym for ITRFLL" << endl; os << " MEAS.ITRFH (position) convert to ITRF height" << endl; os << " ITRFHEIGHT is a synonym for ITRFH" << endl; os << " MEAS.WGS (position) convert to WGS84 XYZ coord" << endl; os << " WGSXYZ is a synonym for WGS" << endl; os << " MEAS.WGSLL (position) convert to WGS84 LonLat" << endl; os << " WGSLONLAT is a synonym for WGSLL" << endl; os << " MEAS.WGSH (position) convert to WGS84 height" << endl; os << " WGSHEIGHT is a synonym for WGSH" << endl; if (showTypes) { os << endl << "Known observatory positions (names are case-insenstive):" << endl; Vector obs = MeasTable::Observatories().copy(); genSort (obs); uInt maxLen = 0; for (uInt i=0; i maxLen) maxLen = obs[i].size(); } uInt npl = 80 / (maxLen+1); uInt n = 0; for (uInt i=0; i 0) os << endl; os << endl; os << TaQLShow::showMeasTypes ("position"); } } void HelpMeasUDF::showFuncsDirection (ostream& os, Bool showTypes) { os << "Direction conversion functions:" << endl; os << " MEAS.DIR (type, direction [,epoch, position]) convert to given type" << endl; os << " DIRECTION is a synonym for DIR" << endl; os << " MEAS.HADEC (direction, epoch, position) convert to Hourangle/Decl" << endl; os << " MEAS.AZEL (direction, epoch, position) convert to Azimuth/Elevation" << endl; os << " MEAS.APP (direction, epoch, position) convert to apparent" << endl; os << " APPARENT is a synonym for APP" << endl; os << " MEAS.J2000 (direction [,epoch, position]) convert to J2000" << endl; os << " MEAS.B1950 (direction [,epoch, position]) convert to B1950" << endl; os << " MEAS.ECL (direction [,epoch, position])" << endl; os << " ECLIPTIC is a synonym for ECL" << endl; os << " MEAS.GAL (direction [,epoch, position])" << endl; os << " GALACTIC is a synonym for GAL" << endl; os << " MEAS.SGAL (direction [,epoch, position])" << endl; os << " SUPERGAL is a synonym for SGAL" << endl; os << " SUPERGALACTIC is a synonym for SGAL" << endl; os << " MEAS.ITRFD (direction [,epoch, position]) convert to ITRF" << endl; os << " ITRFDIR is a synonym for ITRFD" << endl; os << " ITRFDIRECTION is a synonym for ITRFD" << endl; os << " MEAS.RISET (direction, epoch, position) get rise/set time" << endl; os << " RISESET is a synonym for RISET" << endl; os << " MEAS.DIRCOS (type, direction [,epoch, position])" << endl; os << " as DIR returning 3 direction cosines instead of 2 angles" << endl; os << " DIRECTIONCOSINE is a synonym for DIRCOS" << endl; if (showTypes) { os << endl << "Known source directions (names are case-insenstive):" << endl; os << " All sources in the Measures Sources table" << endl; os << " SUN MOON MERCURY VENUS MARS JUPITER SATURN URANUS NEPTUNE PLUTO" << endl; os << " CasA CygA HerA HydA PerA TauA VirA" << endl; os << " In function RISET type SUN can have a suffix -XX where XX can be (default -UR):" << endl; os << " C center touches horizon CR center with refraction" << endl; os << " U upper edge touches horizon UR upper edge with refraction" << endl; os << " L lower edge touches horizon LR lower edge with refraction" << endl; os << " CT civil twilight darkness (-6 deg) NT nautical twilight darkness (-12)" << endl; os << " AT amateur astronomy twilight (-15) ST scientific astronomy twilight (-18)" << endl; os << " The first 6 suffices can also be used with MOON." << endl; os << endl; os << TaQLShow::showMeasTypes ("direction"); } } void HelpMeasUDF::showFuncsEarthMagnetic (ostream& os, Bool showTypes) { os << "EarthMagnetic conversion functions:" << endl; os << " MEAS.EM (type, em, epoch, position) convert em value to given type as xyz" << endl; os << " EARTHMAGNETIC and EMXYZ are synonyms for EM" << endl; os << " MEAS.EMANG (type, em, epoch, position) convert and return as angles" << endl; os << " EMANGLES is a synonym for EMANG" << endl; os << " MEAS.EMLEN (type, em, epoch, position) convert and return as flux density" << endl; os << " EMLENGTH is a synonym for EMLEN" << endl; os << " MEAS.IGRF (type, height, direction, epoch, position) IGRF model value" << endl; os << " IGRFXYZ is a synonym for IGRF" << endl; os << " MEAS.IGRFANG (t, h, d, e, p) IGRF model angles in ITRF" << endl; os << " IGRFANGLES is a synonym for IGRFANG" << endl; os << " MEAS.IGRFLEN (t, h, d, e, p) IGRF model flux density " << endl; os << " IGRFLENGTH is a synonym for IGRFLEN" << endl; os << " MEAS.IGRFLOS (h, d, e, p) IGRF value along line-of-sight" << endl; os << " MEAS.IGRFLONG (h, d, e, p) longitude of calculation point" << endl; if (showTypes) { os << endl; os << TaQLShow::showMeasTypes ("earthmagnetic"); } } void HelpMeasUDF::showFuncsFrequency (ostream& os, Bool showTypes) { os << "Frequency conversion functions:" << endl; os << " MEAS.FREQ (type, freq, radvel, direction, epoch, position) convert to given type" << endl; os << " Instead of freq, a period or wavelength can be given (requires a unit)" << endl; os << " radvel is only needed when converting to/from rest frequencies" << endl; os << " FREQUENCY is a synonym for FREQ" << endl; os << " MEAS.REST (freq, radvel, direction, epoch, position) convert to rest freq" << endl; os << " MEAS.REST (freq, doppler) convert to rest freq" << endl; os << " RESTFREQ and RESTFREQUENCY are synonyms for REST" << endl; os << " MEAS.SHIFTFREQ (freq, doppler) shift frequencies" << endl; os << " SHIFT and SHIFTFREQUENCY are synonyms for SHIFTFREQ" << endl; os << " It can also be used to shift rest frequencies" << endl; if (showTypes) { os << endl; os << TaQLShow::showMeasTypes ("frequency"); } } void HelpMeasUDF::showFuncsRadialVelocity (ostream& os, Bool showTypes) { os << "RadialVelocity conversion functions:" << endl; os << " MEAS.RADVEL (type, radvel, direction, epoch, position) convert to given type" << endl; os << " MEAS.RADVEL (type, doppler) calc from doppler" << endl; os << " RV and RADIALVELOCITY are synonyms for RADVEL" << endl; if (showTypes) { os << endl; os << TaQLShow::showMeasTypes ("radialvelocity"); } } void HelpMeasUDF::showFuncsDoppler (ostream& os, Bool showTypes) { os << "Doppler conversion functions:" << endl; os << " MEAS.DOPPLER (type, doppler) convert to given type" << endl; os << " MEAS.DOPPLER (type, radvel) calc from radial velocity" << endl; os << " MEAS.DOPPLER (type, freq, restfreq) calc from frequency" << endl; os << " REDSHIFT is a synonym for DOPPLER" << endl; if (showTypes) { os << endl; os << TaQLShow::showMeasTypes ("doppler"); } } UDFBase* HelpMeasUDF::makeHELP (const String&) { return new HelpMeasUDF(); } void HelpMeasUDF::setup (const Table&, const TaQLStyle&) { AlwaysAssert (operands().size() <= 1, AipsError); if (operands().size() == 1) { AlwaysAssert (operands()[0]->dataType() == TableExprNodeRep::NTString && operands()[0]->valueType() == TableExprNodeRep::VTScalar, AipsError); } // Set datatype, shape, unit, etc. setDataType (TableExprNodeRep::NTString); setNDim (0); // scalar setConstant (True); } String HelpMeasUDF::getString (const TableExprId& id) { ostringstream os; String type; if (operands().size() == 1) { type = operands()[0]->getString(id); type.downcase(); } if (type.empty()) { showFuncsPosition (os, False); os << endl; showFuncsEpoch (os, False); os << endl; showFuncsDirection (os, False); os << endl; showFuncsEarthMagnetic (os, False); os << endl; showFuncsFrequency (os, False); os << endl; showFuncsRadialVelocity (os, False); os << endl; showFuncsDoppler (os, False); } else if (type == "position" || type == "pos") { showFuncsPosition (os, True); } else if (type == "epoch") { showFuncsEpoch (os, True); } else if (type == "direction" || type == "dir") { showFuncsDirection (os, True); } else if (type == "earthmagnetic" || type == "em") { showFuncsEarthMagnetic (os, True); } else if (type == "frequency" || type == "freq") { showFuncsFrequency (os, True); } else if (type == "radialvelocity" || type == "radvel" || type == "rv") { showFuncsRadialVelocity (os, True); } else if (type == "doppler") { showFuncsDoppler (os, True); } if (os.str().empty()) { os << type << " is an unknown meas subtype; use pos(ition), epoch, dir(ection)," << " earthmagnetic (em), freq(uency) or radialvelocity (radvel)" << endl; } else { os << endl << "See also section 'Special Measures functions'" " at http://casacore.github.io/casacore-notes/199.html" << endl; } return os.str(); } } // end namespace casacore-3.7.1/meas/MeasUDF/Register.h000066400000000000000000000050021476623553700174270ustar00rootroot00000000000000//# Register.h: Register Measure UDFs //# Copyright (C) 2011 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEAS_REGISTER_H #define MEAS_REGISTER_H #include #include #include // // This function registers the TaQL user defined functions handling // Measure conversions. // It is called when the dynamic library casa_meas.so/dylib is loaded. extern "C" { void register_meas(); } // namespace casacore { // // General meas function to show the available functions. // class HelpMeasUDF: public UDFBase { public: // Function to create an object. static UDFBase* makeHELP (const String&); // Setup the object. virtual void setup (const Table&, const TaQLStyle&); // Get the value. virtual String getString (const TableExprId& id); // Show the possible functions. static void showFuncsPosition (std::ostream&, Bool showTypes); static void showFuncsEpoch (std::ostream&, Bool showTypes); static void showFuncsDirection (std::ostream&, Bool showTypes); static void showFuncsEarthMagnetic (std::ostream&, Bool showTypes); static void showFuncsFrequency (std::ostream&, Bool showTypes); static void showFuncsDoppler (std::ostream&, Bool showTypes); static void showFuncsRadialVelocity (std::ostream&, Bool showTypes); }; } #endif casacore-3.7.1/meas/MeasUDF/test/000077500000000000000000000000001476623553700164545ustar00rootroot00000000000000casacore-3.7.1/meas/MeasUDF/test/CMakeLists.txt000066400000000000000000000006351476623553700212200ustar00rootroot00000000000000set (tests tPositionEngine tEpochEngine tDirectionEngine tEarthMagneticEngine tFrequencyEngine tRadialVelocityEngine tDopplerEngine tmeas ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_meas) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/meas/MeasUDF/test/tDirectionEngine.cc000066400000000000000000000407701476623553700222250ustar00rootroot00000000000000//# tDirectionEngine.cc: Test program for DirectionEngine //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace casacore; using namespace std; void testScalar (Bool asDirCos) { cout << "test scalars as " << (asDirCos ? "dircos" : "angles") << " ..." << endl; // Convert a direction from B1950. MDirection coord(Quantity(185.425833,"deg"), Quantity(31.799167,"deg"), MDirection::B1950); MEpoch epo(Quantity(50217.625,"d")); // 14-may-1996/15:00 MPosition pos(Quantity(10,"m"), Quantity(6.60417,"deg"), Quantity(52.8,"deg"), MPosition::WGS84); // near WSRT MeasFrame frame(coord,epo,pos); { Vector dir; if (asDirCos) { dir = MDirection::Convert (coord, MDirection::Ref(MDirection::APP,frame))() .getValue().getValue(); } else { dir = MDirection::Convert (coord, MDirection::Ref(MDirection::APP,frame))() .getValue().getAngle("rad").getValue(); } ///cout << "meas=" << dir << endl; String funcStr = (asDirCos ? "dircos('APP'," : "app("); TableExprNode node(tableCommand ("calc meas." + funcStr + "185.425833deg, 31.799167deg,'B1950'," "mjdtodate(50217.625d),'UTC'," "6.60417deg, 52.8deg, 10m, 'WGS84')").node()); AlwaysAssertExit (node.getNodeRep()->isConstant()); Array arr1 = node.getArrayDouble(0); VectorIterator veciter(arr1); ///cout << "taql=" << arr1 << endl; if (asDirCos) { AlwaysAssertExit (node.unit().getName().empty()); } else { AlwaysAssertExit (node.unit().getName() == "rad"); } AlwaysAssertExit (allNear(dir, veciter.vector(), 1e-8)); } { Vector dir; if (asDirCos) { dir = MDirection::Convert (coord, MDirection::Ref(MDirection::J2000))() .getValue().getValue(); } else { dir = MDirection::Convert (coord, MDirection::Ref(MDirection::J2000))() .getValue().getAngle("deg").getValue(); } ///cout << "meas=" << dir << endl; String funcStr = (asDirCos ? "dircos('j2000'," : "j2000("); TableExprNode node(tableCommand ("calc meas." + funcStr + "[185.425833deg, 31.799167deg]," "'B1950')deg").node()); AlwaysAssertExit (node.getNodeRep()->isConstant()); Array arr1 = node.getArrayDouble(0); VectorIterator veciter(arr1); ///cout << "diff=" << dir-veciter.vector() << endl; AlwaysAssertExit (allNear(dir, veciter.vector(), 1e-8)); } { // Test a nested meas. function. Vector dir; if (asDirCos) { dir = coord.getValue().getValue(); } else { dir = coord.getValue().getAngle("deg").getValue(); } ///cout << "meas=" << dir << endl; String funcStr = (asDirCos ? "dircos('b1950'," : "b1950("); String funcStr2 = (!asDirCos ? "dircos('galactic'," : "galactic("); TableExprNode node(tableCommand ("calc meas." + funcStr + "meas." + funcStr2 + "[185.425833deg, 31.799167deg]," "'B1950'))deg").node()); AlwaysAssertExit (node.getNodeRep()->isConstant()); Array arr1 = node.getArrayDouble(0); VectorIterator veciter(arr1); ///cout << "diff=" << dir-veciter.vector() << endl; AlwaysAssertExit (allNear(dir, veciter.vector(), 1e-8)); } { // Test ZENITH. TableExprNode node1(tableCommand ("calc meas.azel([0,0,1], 'AZEL')deg").node()); TableExprNode node2(tableCommand ("calc meas.azel('ZENITH')deg").node()); Array arr1 = node1.getArrayDouble(0); AlwaysAssertExit (nearAbs(arr1.data()[0], 0.)); AlwaysAssertExit (nearAbs(arr1.data()[1], 90.)); Array arr2 = node2.getArrayDouble(0); AlwaysAssertExit (nearAbs(arr2.data()[0], 0.)); AlwaysAssertExit (nearAbs(arr2.data()[1], 90.)); } } void testArray (Bool asDirCos) { cout << "test arrays as " << (asDirCos ? "dircos" : "angles") << " ..." << endl; // Convert a few directions from J2000. String funcStr = (asDirCos ? "dircos('APP'," : "app("); TableExprNode node1(tableCommand ("calc meas." + funcStr + "[185.425833deg, 31.799167deg," "175.425833deg, 41.799167deg," "165.425833deg, 51.799167deg," "155.425833deg, 61.799167deg],'J2000'," "mjdtodate([50217.625d,50417.625d,50617.625]),'UTC'," "[6.60417deg, 52.8deg, -60.60417deg, -32.8deg]," "[10m,1000m], 'WGS84')deg").node()); // Do it as multi-dim TaQL arrays. TableExprNode node2(tableCommand ("using style python calc meas." + funcStr + "[[[185.425833deg, 31.799167deg]," "[175.425833deg, 41.799167deg]]," "[[165.425833deg, 51.799167deg]," "[155.425833deg, 61.799167deg]]],'J2000'," "mjdtodate([50217.625d,50417.625d,50617.625]),'UTC'," "[[6.60417deg, 52.8deg], [-60.60417deg, -32.8deg]]," "[10m,1000m], 'WGS84')deg").node()); AlwaysAssertExit (node1.getNodeRep()->isConstant()); AlwaysAssertExit (node2.getNodeRep()->isConstant()); ///cout << "taql=" << node1.getArrayDouble(0) << endl; ///cout << "taql=" << node2.getArrayDouble(0) << endl; Array arr1 = node1.getArrayDouble(0); Array arr2 = node2.getArrayDouble(0); AlwaysAssertExit (arr1.shape() == IPosition(4,asDirCos?3:2,4,3,2)); AlwaysAssertExit (arr2.shape() == IPosition(5,asDirCos?3:2,2,2,3,2)); VectorIterator arr1iter(arr1); VectorIterator arr2iter(arr2); // Check with Measures. Vector coord(4); Vector epo(3); Vector pos(2); coord[0] = MDirection(Quantity(185.425833,"deg"), Quantity(31.799167,"deg"), MDirection::J2000); coord[1] = MDirection(Quantity(175.425833,"deg"), Quantity(41.799167,"deg"), MDirection::J2000); coord[2] = MDirection(Quantity(165.425833,"deg"), Quantity(51.799167,"deg"), MDirection::J2000); coord[3] = MDirection(Quantity(155.425833,"deg"), Quantity(61.799167,"deg"), MDirection::J2000); epo[0] = MEpoch(Quantity(50217.625,"d")); // 14-may-1996/15:00 epo[1] = MEpoch(Quantity(50417.625,"d")); // 30-nov-1996/15:00 epo[2] = MEpoch(Quantity(50617.625,"d")); // 18-jun-1997/15:00 pos[0] = MPosition(Quantity(10,"m"), Quantity(6.60417,"deg"), Quantity(52.8,"deg"), MPosition::WGS84); // near WSRT pos[1] = MPosition(Quantity(1000,"m"), Quantity(-60.60417,"deg"), Quantity(-32.8,"deg"), MPosition::WGS84); for (uInt ip=0; ip dir; if (asDirCos) { dir = MDirection::Convert (coord[ic], MDirection::Ref(MDirection::APP, MeasFrame(coord[ic],epo[ie],pos[ip])))() .getValue().getValue(); } else { dir = MDirection::Convert (coord[ic], MDirection::Ref(MDirection::APP, MeasFrame(coord[ic],epo[ie],pos[ip])))() .getValue().getAngle("deg").getValue(); } ///cout << "meas=" << dir << endl; AlwaysAssertExit (allNear(dir, arr1iter.vector(), 1e-8)); AlwaysAssertExit (allNear(dir, arr2iter.vector(), 1e-8)); arr1iter.next(); arr2iter.next(); } } } } void testColumn (Bool asDirCos) { cout << "test columns as " << (asDirCos ? "dircos" : "angles") << " ..." << endl; // Check with Measures. Vector coord(3); Vector epo(3); Vector pos(3); coord[0] = MDirection(Quantity(185.425833,"deg"), Quantity(31.799167,"deg"), MDirection::J2000); coord[1] = MDirection(Quantity(175.425833,"deg"), Quantity(41.799167,"deg"), MDirection::J2000); coord[2] = MDirection(Quantity(165.425833,"deg"), Quantity(51.799167,"deg"), MDirection::J2000); epo[0] = MEpoch(Quantity(50217.625,"d")); // 14-may-1996/15:00 epo[1] = MEpoch(Quantity(50417.625,"d")); // 30-nov-1996/15:00 epo[2] = MEpoch(Quantity(50617.625,"d")); // 18-jun-1997/15:00 pos[0] = MPosition(Quantity(10,"m"), Quantity(6.60417,"deg"), Quantity(52.8,"deg"), MPosition::WGS84); // near WSRT pos[1] = MPosition(Quantity(1000,"m"), Quantity(-60.60417,"deg"), Quantity(-32.8,"deg"), MPosition::WGS84); pos[2] = MPosition(Quantity(2000,"m"), Quantity(-6.60417,"deg"), Quantity(32.8,"deg"), MPosition::WGS84); ///for (int i=0; i<3; ++i) { ///cout << MPosition::Convert(pos[i], MPosition::Ref(MPosition::ITRF))().getValue().getValue() << endl; ///} // Convert a few directions from J2000. String funcStr = (asDirCos ? "dircos('APP'," : "app("); TableExprNode node1(tableCommand ("using style python calc meas." + funcStr + "DIR[0,],TIME,POS1)deg " "from tDirectionEngine_tmp.tab").node()); TableExprNode node2(tableCommand ("using style python calc meas." + funcStr + "[DIR[0,0], DIR[0,1]]deg, 'J2000', TIME,POS2)deg " "from tDirectionEngine_tmp.tab").node()); AlwaysAssertExit (! node1.getNodeRep()->isConstant()); AlwaysAssertExit (! node2.getNodeRep()->isConstant()); AlwaysAssertExit (node1.nrow() == 3 && node2.nrow() == 3); for (uInt i=0; i<3; ++i) { ///cout << "taql=" << node1.getArrayDouble(i) << endl; ///cout << "taql=" << node2.getArrayDouble(i) << endl; Array arr1 = node1.getArrayDouble(i); Array arr2 = node2.getArrayDouble(i); AlwaysAssertExit (arr1.shape() == IPosition(1,asDirCos?3:2)); AlwaysAssertExit (arr2.shape() == IPosition(1,asDirCos?3:2)); VectorIterator veciter1(arr1); VectorIterator veciter2(arr2); Vector dir; if (asDirCos) { dir = MDirection::Convert (coord[i], MDirection::Ref(MDirection::APP, MeasFrame(coord[i],epo[i],pos[i])))() .getValue().getValue(); } else { dir = MDirection::Convert (coord[i], MDirection::Ref(MDirection::APP, MeasFrame(coord[i],epo[i],pos[i])))() .getValue().getAngle("deg").getValue(); } ///cout << "meas=" << dir << endl; AlwaysAssertExit (allNear(dir, veciter1.vector(), 1e-8)); AlwaysAssertExit (allNear(dir, veciter2.vector(), 1e-8)); } } void testName() { cout << "test names as angles ... " << endl; // Check with Measures. Vector coord(3); Vector epo(1); Vector pos(2); coord[0] = MDirection(MDirection::SUN); coord[1] = MDirection(MDirection::JUPITER); coord[2] = MDirection(MDirection::MOON); epo[0] = MEpoch(Quantity(50217.625,"d")); // 14-may-1996/15:00 AlwaysAssertExit (MeasTable::Observatory(pos[0], "WSRT")); AlwaysAssertExit (MeasTable::Observatory(pos[1], "VLA")); TableExprNode node1(tableCommand ("using style python calc meas.app (" "['SUN','JUPITER','MOON'], 50217.625d," "['WSRT','VLA'])deg").node()); AlwaysAssertExit (node1.getNodeRep()->isConstant()); ///cout << "taql=" << node1.getArrayDouble(0) << endl; Array arr1 = node1.getArrayDouble(0); AlwaysAssertExit (arr1.shape() == IPosition(4,2,3,1,2)); VectorIterator arr1iter(arr1); // Check with Measures. for (uInt ip=0; ip dir = MDirection::Convert (coord[ic], MDirection::Ref(MDirection::APP, MeasFrame(coord[ic],epo[ie],pos[ip])))() .getValue().getAngle("deg").getValue(); ///cout << "meas=" << dir << endl; AlwaysAssertExit (allNear(dir, arr1iter.vector(), 1e-8)); arr1iter.next(); } } } } void testRiset() { // Below is more or less the same test as in tmeas.run. // It is repeated here for easier debugging in case of problems. cout << "test rise/set ..." << endl; TableExprNode node(tableCommand( "calc str(meas.riseset(['SUN','MOON'], " "10Aug2010/13:12:11, 'WSRT'))").node()); AlwaysAssertExit (node.getNodeRep()->isConstant()); Array arr = node.getArrayString(0); AlwaysAssertExit (arr.shape() == IPosition(4,2,2,1,1)); AlwaysAssertExit (arr.data()[0] == "2010/08/10/04:06:04"); AlwaysAssertExit (arr.data()[1] == "2010/08/10/19:10:42"); AlwaysAssertExit (arr.data()[2] == "2010/08/10/04:29:35"); AlwaysAssertExit (arr.data()[3] == "2010/08/10/18:59:10"); } int checkErr (const String& command) { Bool fail = False; try { TableExprNode node(tableCommand(command).node()); if (node.isScalar()) { node.getDouble(0); } else { node.getArrayDouble(0); } } catch (const std::exception& x) { cout << "Expected exception: " << x.what() << endl; fail = True; } if (!fail) { cout << "Command '" + command + "' should have failed" << endl; return 1; } return 0; } void testErr() { cout << "test erroneous function calls ..." << endl; int nsucc = 0; nsucc += checkErr("using style python calc meas.j2000(" "[DIR[0,0], DIR[0,1]]deg, TIME,POS2)deg " "from tDirectionEngine_tmp.tab"); nsucc += checkErr("using style python calc meas.app(TIME) " "from tDirectionEngine_tmp.tab"); nsucc += checkErr("using style python calc meas.app(POS1) " "from tDirectionEngine_tmp.tab"); nsucc += checkErr("using style python calc meas.app(DIR) " "from tDirectionEngine_tmp.tab"); AlwaysAssertExit(nsucc == 0); } int main() { try { // Register the MEAS functions in TaQL. register_meas(); // Execute some tests. testErr(); testScalar(False); testScalar(True); testArray(False); testArray(True); testColumn(False); testColumn(True); testName(); testRiset(); } catch (const std::exception& x) { cerr << "Unexpected exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/meas/MeasUDF/test/tDirectionEngine.run000077500000000000000000000022061476623553700224370ustar00rootroot00000000000000#!/bin/sh # Create a table with 4 columns containing directions, epochs and positions. # The same positions are defined as WGS84 (llh) and ITRF (xyz). # Define the measure keywords for them. ../../../tables/apps/taql "create table tDirectionEngine_tmp.tab DIR R8 [shape=[2,2], unit=['deg','deg']], TIME R8 [unit='s'], POS1 R8 [shape=[3], unit=['deg','deg','m']], POS2 R8 [shape=[3], unit=['m','m','m']]" ../../../tables/apps/taql "alter table tDirectionEngine_tmp.tab set keyword DIR::MEASINFO=[type='direction', Ref='J2000'], TIME::MEASINFO=[type='epoch',Ref='UTC'], POS1::MEASINFO=[type='position',Ref='WGS84'], POS2::MEASINFO=[type='position',Ref='ITRF']" ../../../tables/apps/taql 'insert into tDirectionEngine_tmp.tab (DIR,TIME,POS1,POS2) values ([[185.425833deg, 31.799167deg], [0,0]], 50217.625d, [6.60417, 52.8, 10], [3.83879e+06, 444447, 5.05713e+06]), ([[175.425833deg, 41.799167deg], [0,0]], 50417.625d, [-60.60417, -32.8, 1000], [2.63452e+06, -4.67631e+06, -3.43588e+06]), ([[165.425833deg, 51.799167deg], [0,0]], 50617.625d, [-6.60417, 32.8, 2000], [5.33258e+06, -617394, 3.43642e+06])' $casa_checktool ./tDirectionEngine casacore-3.7.1/meas/MeasUDF/test/tDopplerEngine.cc000066400000000000000000000375471476623553700217220ustar00rootroot00000000000000//# tDopplerEngine.cc: Test program for DopplerEngine //# Copyright (C) 2018 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace casacore; using namespace std; void testDopplerScalar() { cout << "test doppler scalars ..." << endl; // Convert a doppler from BETA to Z. MDoppler dop(Quantity(0.5,""), MDoppler::BETA); MVDoppler ndop = MDoppler::Convert (dop, MDoppler::Ref(MDoppler::Z))().getValue(); ///cout << "meas=" << ndop << endl; { TableExprNode node1(tableCommand ("calc meas.doppler ('Z'," "0.5, 'BETA')").node()); AlwaysAssertExit (node1.getNodeRep()->isConstant()); Double val1 = node1.getDouble(0); ///cout << "taql=" << arr1 << endl; AlwaysAssertExit (node1.unit().getName() == ""); AlwaysAssertExit (near(ndop.getValue(), val1, 1e-8)); } } void testRadVelScalar() { cout << "test radvel scalars ..." << endl; // Make a (BETA) doppler from the radvel and convert to Z. MRadialVelocity vel(Quantity(200,"km/s"), MRadialVelocity::BARY); MVDoppler ndop = MDoppler::Convert (vel.toDoppler(), MDoppler::Ref(MDoppler::Z))().getValue(); ///cout << "meas=" << ndop << endl; { TableExprNode node1(tableCommand ("calc meas.doppler ('Z'," "200 'km/s', 'BARY')").node()); AlwaysAssertExit (node1.getNodeRep()->isConstant()); Double val1 = node1.getDouble(0); ///cout << "taql=" << arr1 << endl; AlwaysAssertExit (node1.unit().getName() == ""); AlwaysAssertExit (near(ndop.getValue(), val1, 1e-8)); } } void testFreqScalar() { cout << "test freq scalars ..." << endl; // Make a (BETA) doppler from the freq/restfreq and convert to Z. // Use the rest freq of the CII166A line (from the Measures Lines table). MFrequency freq(Quantity(200,"MHz"), MFrequency::LSRK); MVFrequency rest(Quantity(1.425445,"GHz")); MVDoppler ndop = MDoppler::Convert (freq.toDoppler(rest), MDoppler::Ref(MDoppler::Z))().getValue(); ///cout << "meas=" << ndop << endl; { TableExprNode node1(tableCommand ("calc meas.doppler ('Z'," "200 MHz, 'LSRK', 1.425445GHz)").node()); AlwaysAssertExit (node1.getNodeRep()->isConstant()); Double val1 = node1.getDouble(0); ///cout << "taql=" << arr1 << endl; AlwaysAssertExit (node1.unit().getName() == ""); AlwaysAssertExit (near(ndop.getValue(), val1, 1e-8)); } { TableExprNode node1(tableCommand ("calc meas.doppler ('Z'," "200 MHz, 'LSRK', 'CII166A')").node()); AlwaysAssertExit (node1.getNodeRep()->isConstant()); Double val1 = node1.getDouble(0); ///cout << "taql=" << arr1 << endl; AlwaysAssertExit (node1.unit().getName() == ""); AlwaysAssertExit (near(ndop.getValue(), val1, 1e-8)); } } void testDopplerArray() { cout << "test doppler arrays ..." << endl; // Convert a doppler from BETA to OPTICAL. MDoppler dop1(Quantity(0.5,""), MDoppler::RADIO); MVDoppler ndop1 = MDoppler::Convert (dop1, MDoppler::Ref(MDoppler::OPTICAL))().getValue(); MDoppler dop2(Quantity(0.6,""), MDoppler::RADIO); MVDoppler ndop2 = MDoppler::Convert (dop2, MDoppler::Ref(MDoppler::OPTICAL))().getValue(); ///cout << "meas=" << ndop << endl; { TableExprNode node1(tableCommand ("calc meas.doppler ('OPTICAL'," "[0.5, 0.6], 'RADIO')").node()); AlwaysAssertExit (node1.getNodeRep()->isConstant()); AlwaysAssertExit (node1.getNodeRep()->valueType() == TableExprNodeRep::VTArray); Array arr1 = node1.getArrayDouble(0); ///cout<isConstant()); AlwaysAssertExit (node1.getNodeRep()->valueType() == TableExprNodeRep::VTArray); Array arr1 = node1.getArrayDouble(0); ///cout<isConstant()); AlwaysAssertExit (node1.getNodeRep()->valueType() == TableExprNodeRep::VTArray); Array arr1 = node1.getArrayDouble(0); ///cout<isConstant()); AlwaysAssertExit (node1.getNodeRep()->valueType() == TableExprNodeRep::VTArray); Array arr1 = node1.getArrayDouble(0); ///cout<isConstant()); AlwaysAssertExit (node1.getNodeRep()->valueType() == TableExprNodeRep::VTArray); Array arr1 = node1.getArrayDouble(0); ///cout<isConstant()); AlwaysAssertExit (node1.getNodeRep()->valueType() == TableExprNodeRep::VTArray); Array arr1 = node1.getArrayDouble(0); ///cout<isConstant()); AlwaysAssertExit (node1.getNodeRep()->valueType() == TableExprNodeRep::VTScalar); AlwaysAssertExit (node1.unit().getName() == ""); Double val1 = node1.getDouble(0); ///cout << "taql=" << arr1 << endl; AlwaysAssertExit (near(ndop1.getValue(), val1, 1e-8)); Double val2 = node1.getDouble(1); AlwaysAssertExit (near(ndop2.getValue(), val2, 1e-8)); } void testRadVelColumn() { cout << "test radvel columns ..." << endl; // Check with Measures. // Make a (BETA) doppler from the radvel and convert to Z. MRadialVelocity vel1(Quantity(200,"km/s"), MRadialVelocity::BARY); MRadialVelocity vel2(Quantity(300,"km/s"), MRadialVelocity::BARY); MVDoppler ndop1 = MDoppler::Convert (vel1.toDoppler(), MDoppler::Ref(MDoppler::Z))().getValue(); MVDoppler ndop2 = MDoppler::Convert (vel2.toDoppler(), MDoppler::Ref(MDoppler::Z))().getValue(); ///cout << "meas=" << ndop << endl; TableExprNode node1(tableCommand ("calc meas.doppler ('Z'," "RVCOL) from tDopplerEngine_tmp.tab").node()); AlwaysAssertExit (! node1.getNodeRep()->isConstant()); AlwaysAssertExit (node1.getNodeRep()->valueType() == TableExprNodeRep::VTArray); AlwaysAssertExit (node1.unit().getName() == ""); Array arr1 = node1.getArrayDouble(0); ///cout<isConstant()); AlwaysAssertExit (node1.getNodeRep()->valueType() == TableExprNodeRep::VTScalar); AlwaysAssertExit (node1.unit().getName() == ""); Double val1 = node1.getDouble(0); ///cout << "taql=" << arr1 << endl; AlwaysAssertExit (near(ndop1.getValue(), val1, 1e-8)); Double val2 = node1.getDouble(1); AlwaysAssertExit (near(ndop2.getValue(), val2, 1e-8)); } int checkErr (const String& command) { Bool fail = False; try { TableExprNode node(tableCommand(command).node()); if (node.isScalar()) { node.getDouble(0); } else { node.getArrayDouble(0); } } catch (const std::exception& x) { cout << "Expected exception: " << x.what() << endl; fail = True; } if (!fail) { cout << "Command '" + command + "' should have failed" << endl; return 1; } return 0; } void testErr() { cout << "test erroneous function calls ..." << endl; int nsucc = 0; nsucc += checkErr("calc meas.redshift()"); nsucc += checkErr("calc meas.doppler('BETA')"); nsucc += checkErr("calc meas.doppler('BETX',20)"); nsucc += checkErr("calc meas.doppler('BETA',10,20)"); nsucc += checkErr("calc meas.doppler('BETA',10,'XZ')"); nsucc += checkErr("calc meas.doppler('BETA',10,'Z',20)"); nsucc += checkErr("calc meas.doppler('BETA','Z',20)"); nsucc += checkErr("calc meas.doppler('BETA',20+3i,'Z')"); nsucc += checkErr("calc meas.doppler('BETA',200MHz)"); nsucc += checkErr("calc meas.doppler('BETA',200MHz,'LSRK')"); nsucc += checkErr("calc meas.doppler('BETA',200MHz,'LSRK',1j)"); nsucc += checkErr("calc meas.doppler('BETA',200MHz,'LSRK','justaline')"); nsucc += checkErr("calc meas.doppler('BETA',[200,300]MHz,'LSRK',[100,110,120])"); nsucc += checkErr("calc meas.doppler('BETA',200 's/km')"); AlwaysAssertExit(nsucc == 0); } int main() { try { // Register the MEAS functions in TaQL. register_meas(); // Execute some tests. testErr(); testDopplerScalar(); testRadVelScalar(); testFreqScalar(); testDopplerArray(); testRadVelArray(); testFreqArray(); testDopplerColumn(); testRadVelColumn(); testFreqColumn(); } catch (const std::exception& x) { cerr << "Unexpected exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/meas/MeasUDF/test/tDopplerEngine.run000077500000000000000000000013061476623553700221240ustar00rootroot00000000000000#!/bin/sh # Create a table with 4 columns containing doppler, radvel, freq and restfreq. # Define the measure keywords for them. ../../../tables/apps/taql "create table tDopplerEngine_tmp.tab DOPPCOL R8, RVCOL R8 [shape=[2], unit='m/s'], FREQ R8 [unit='GHz'], RESTFREQ R8 [unit='MHz']" ../../../tables/apps/taql "alter table tDopplerEngine_tmp.tab set keyword DOPPCOL::MEASINFO=[type='doppler',Ref='RADIO'], RVCOL::MEASINFO=[type='radialvelocity',Ref='BARY'], FREQ::MEASINFO=[type='frequency',Ref='LSRK']" ../../../tables/apps/taql 'insert into tDopplerEngine_tmp.tab (DOPPCOL,RVCOL,FREQ,RESTFREQ) values (0.5, [2e5, 3e5], 0.2, 1425.445), (0.6, [3e5, 2e5], 0.3, 4874.157)' $casa_checktool ./tDopplerEngine casacore-3.7.1/meas/MeasUDF/test/tEarthMagneticEngine.cc000066400000000000000000000445601476623553700230210ustar00rootroot00000000000000//# tEarthMagneticEngine.cc: Test program for EarthMagneticEngine //# Copyright (C) 2018 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace casacore; using namespace std; void testScalar() { cout << "test scalars ..." << endl; // Convert a earthmagnetic from B1950. MEarthMagnetic coord(MVEarthMagnetic(Quantity(10, "nT"), Quantity(185.425833,"deg"), Quantity(31.799167,"deg")), MEarthMagnetic::B1950); MEpoch epo(Quantity(50217.625,"d")); // 14-may-1996/15:00 MPosition pos(Quantity(10,"m"), Quantity(6.60417,"deg"), Quantity(52.8,"deg"), MPosition::WGS84); // near WSRT MeasFrame frame(epo,pos); { Vector em = MEarthMagnetic::Convert (coord, MEarthMagnetic::Ref(MEarthMagnetic::APP,frame))() .getValue().getValue(); cout << "meas=" << em << endl; TableExprNode node(tableCommand ("calc meas.em('app'," "185.425833deg, 31.799167deg,10nT,'B1950'," "mjdtodate(50217.625d),'UTC'," "6.60417deg, 52.8deg, 10m, 'WGS84')").node()); AlwaysAssertExit (node.getNodeRep()->isConstant()); AlwaysAssertExit (node.unit().getName() == "nT"); Array arr1 = node.getArrayDouble(0); AlwaysAssertExit (arr1.shape() == IPosition(1,3)); VectorIterator veciter(arr1); cout << "taql=" << arr1 << endl; AlwaysAssertExit (node.unit().getName() == "nT"); AlwaysAssertExit (allNear(em, veciter.vector(), 1e-8)); } { Vector em = MEarthMagnetic::Convert (coord, MEarthMagnetic::Ref(MEarthMagnetic::J2000))() .getValue().getValue(); cout << "meas=" << em << endl; TableExprNode node(tableCommand ("calc meas.em('j2000'," "185.425833deg, 31.799167deg,10nT,'B1950')").node()); AlwaysAssertExit (node.getNodeRep()->isConstant()); AlwaysAssertExit (node.unit().getName() == "nT"); Array arr1 = node.getArrayDouble(0); AlwaysAssertExit (arr1.shape() == IPosition(1,3)); VectorIterator veciter(arr1); cout << "taql=" << arr1 << endl; AlwaysAssertExit (allNear(em, veciter.vector(), 1e-8)); } { // Convert from nT values instead of deg,deg,nT. // The result is the same. Vector em = MEarthMagnetic::Convert (coord, MEarthMagnetic::Ref(MEarthMagnetic::B1950))() .getValue().getAngle().getValue(); cout << "meas=" << em << endl; TableExprNode node(tableCommand ("calc meas.emang('b1950'," "-8.46092318369e-9nT, -8.03641753778e-10,5.26943439197e-9,'B1950')").node()); AlwaysAssertExit (node.getNodeRep()->isConstant()); AlwaysAssertExit (node.unit().getName() == "rad"); Array arr1 = node.getArrayDouble(0); AlwaysAssertExit (arr1.shape() == IPosition(1,2)); VectorIterator veciter(arr1); cout << "taql=" << arr1 << endl; AlwaysAssertExit (allNear(em, veciter.vector(), 1e-8)); } { Double em = MEarthMagnetic::Convert (coord, MEarthMagnetic::Ref(MEarthMagnetic::B1950))() .getValue().getLength().getValue(); cout << "meas=" << em << endl; TableExprNode node(tableCommand ("calc meas.emlen('b1950'," "-8.46092318369e-9nT, -8.03641753778e-10,5.26943439197e-9,'B1950')").node()); AlwaysAssertExit (node.getNodeRep()->isConstant()); AlwaysAssertExit (node.isScalar()); AlwaysAssertExit (node.unit().getName() == "nT"); Double arr1 = node.getDouble(0); cout << "taql=" << arr1 << endl; AlwaysAssertExit (near(em, arr1, 1e-8)); } } void testArray() { cout << "test arrays ..." << endl; // Convert a few earthMagnetics from J2000. TableExprNode node1(tableCommand ("calc meas.em('APP'," "[185.425833/180*pi(), 31.799167/180*pi(), 5," "175.425833/180*pi(), 41.799167/180*pi(), 10," "165.425833/180*pi(), 51.799167/180*pi(), 20," "155.425833/180*pi(), 61.799167/180*pi(), 30]," "'J2000'," "mjdtodate([50217.625d,50417.625d,50617.625]),'UTC'," "[6.60417deg, 52.8deg, -60.60417deg, -32.8deg]," "[10m,1000m], 'WGS84')").node()); // Do it as multi-dim TaQL arrays. TableExprNode node2(tableCommand ("using style python calc meas.em('APP'," "[[[185.425833/180*pi(), 31.799167/180*pi(), 5]," "[175.425833/180*pi(), 41.799167/180*pi(), 10]]," "[[165.425833/180*pi(), 51.799167/180*pi(), 20]," "[155.425833/180*pi(), 61.799167/180*pi(), 30]]]," "'J2000'," "mjdtodate([50217.625d,50417.625d,50617.625]),'UTC'," "[[6.60417deg, 52.8deg], [-60.60417deg, -32.8deg]]," "[10m,1000m], 'WGS84')").node()); AlwaysAssertExit (node1.getNodeRep()->isConstant()); AlwaysAssertExit (node2.getNodeRep()->isConstant()); ///cout << "taql=" << node1.getArrayDouble(0) << endl; ///cout << "taql=" << node2.getArrayDouble(0) << endl; Array arr1 = node1.getArrayDouble(0); Array arr2 = node2.getArrayDouble(0); AlwaysAssertExit (arr1.shape() == IPosition(4,3,4,3,2)); AlwaysAssertExit (arr2.shape() == IPosition(5,3,2,2,3,2)); VectorIterator arr1iter(arr1); VectorIterator arr2iter(arr2); // Check with Measures. Vector coord(4); Vector epo(3); Vector pos(2); coord[0] = MEarthMagnetic(MVEarthMagnetic(Quantity(5,"nT"), Quantity(185.425833,"deg"), Quantity(31.799167,"deg")), MEarthMagnetic::J2000); coord[1] = MEarthMagnetic(MVEarthMagnetic(Quantity(10,"nT"), Quantity(175.425833,"deg"), Quantity(41.799167,"deg")), MEarthMagnetic::J2000); coord[2] = MEarthMagnetic(MVEarthMagnetic(Quantity(20,"nT"), Quantity(165.425833,"deg"), Quantity(51.799167,"deg")), MEarthMagnetic::J2000); coord[3] = MEarthMagnetic(MVEarthMagnetic(Quantity(30,"nT"), Quantity(155.425833,"deg"), Quantity(61.799167,"deg")), MEarthMagnetic::J2000); epo[0] = MEpoch(Quantity(50217.625,"d")); // 14-may-1996/15:00 epo[1] = MEpoch(Quantity(50417.625,"d")); // 30-nov-1996/15:00 epo[2] = MEpoch(Quantity(50617.625,"d")); // 18-jun-1997/15:00 pos[0] = MPosition(Quantity(10,"m"), Quantity(6.60417,"deg"), Quantity(52.8,"deg"), MPosition::WGS84); // near WSRT pos[1] = MPosition(Quantity(1000,"m"), Quantity(-60.60417,"deg"), Quantity(-32.8,"deg"), MPosition::WGS84); for (uInt ip=0; ip dir = MEarthMagnetic::Convert (coord[ic], MEarthMagnetic::Ref(MEarthMagnetic::APP, MeasFrame(epo[ie],pos[ip])))() .getValue().getValue(); ///cout << "meas=" << dir << endl; AlwaysAssertExit (allNear(dir, arr1iter.vector(), 1e-8)); AlwaysAssertExit (allNear(dir, arr2iter.vector(), 1e-8)); arr1iter.next(); arr2iter.next(); } } } } void testColumn() { cout << "test columns ..." << endl; // Check with Measures. Vector coord(3); Vector epo(3); Vector pos(3); coord[0] = MEarthMagnetic(MVEarthMagnetic(-4.22979e-09, -3.98632e-10, 2.63628e-09), MEarthMagnetic::ITRF); coord[1] = MEarthMagnetic(MVEarthMagnetic(-7.42905e-09, 6.00219e-10, 6.66701e-09), MEarthMagnetic::ITRF); coord[2] = MEarthMagnetic(MVEarthMagnetic(-1.28774e-08, 5.90596e-09, 2.64441e-08), MEarthMagnetic::ITRF); epo[0] = MEpoch(Quantity(50217.625,"d")); // 14-may-1996/15:00 epo[1] = MEpoch(Quantity(50417.625,"d")); // 30-nov-1996/15:00 epo[2] = MEpoch(Quantity(50617.625,"d")); // 18-jun-1997/15:00 pos[0] = MPosition(Quantity(10,"m"), Quantity(6.60417,"deg"), Quantity(52.8,"deg"), MPosition::WGS84); // near WSRT pos[1] = MPosition(Quantity(1000,"m"), Quantity(-60.60417,"deg"), Quantity(-32.8,"deg"), MPosition::WGS84); pos[2] = MPosition(Quantity(2000,"m"), Quantity(-6.60417,"deg"), Quantity(32.8,"deg"), MPosition::WGS84); // Convert a few earthMagnetics from J2000. TableExprNode node1(tableCommand ("using style python calc meas.em('app'," "EM,TIME,POS1) " "from tEarthMagneticEngine_tmp.tab").node()); TableExprNode node2(tableCommand ("using style python calc meas.em('app'," "[EM[0], EM[1], EM[2]], 'ITRF', TIME,POS2) " "from tEarthMagneticEngine_tmp.tab").node()); AlwaysAssertExit (! node1.getNodeRep()->isConstant()); AlwaysAssertExit (! node2.getNodeRep()->isConstant()); AlwaysAssertExit (node1.nrow() == 3 && node2.nrow() == 3); for (uInt i=0; i<3; ++i) { cout << "taql=" << node1.getArrayDouble(i) << ' ' << node1.unit().getName() << endl; cout << "taql=" << node2.getArrayDouble(i) << ' ' << node1.unit().getName() << endl; Array arr1 = node1.getArrayDouble(i); Array arr2 = node2.getArrayDouble(i); AlwaysAssertExit (arr1.shape() == IPosition(1,3)); AlwaysAssertExit (arr2.shape() == IPosition(1,3)); VectorIterator veciter1(arr1); VectorIterator veciter2(arr2); Vector dir; dir = MEarthMagnetic::Convert (coord[i], MEarthMagnetic::Ref(MEarthMagnetic::APP, MeasFrame(epo[i],pos[i])))() .getValue().getValue(); cout << "meas=" << dir << endl; AlwaysAssertExit (allNear(dir, veciter1.vector(), 1e-8)); AlwaysAssertExit (allNear(dir, veciter2.vector(), 1e-8)); } } void testModel() { cout << "Test IGRF model ..." << endl; MVTime dat(1998,5,18); MVPosition mvobs(Quantity(3828488.86, "m").getBaseValue(), Quantity(443253.42, "m").getBaseValue(), Quantity(5064977.78, "m").getBaseValue()); MPosition obs(mvobs); MeasFrame frame((MEpoch(MVEpoch(dat.day()))), obs); MDirection::Ref mvref(MDirection::ITRF, frame); MVDirection mvd(obs.getValue()); EarthMagneticMachine fm(mvref, Quantum(0, "km"), frame); fm.calculate(mvd); cout << "LOS: " << fm.getLOSField() << endl; cout << "Long: " << fm.getLong() << endl; cout << "Field: " << fm.getField() << endl; { TableExprNode node(tableCommand ("calc meas.igrf(0,[3828488.86,443253.42,5064977.78],'ITRF',18may1998,[3828488.86m,443253.42m,5064977.78m])").node()); AlwaysAssertExit (node.getNodeRep()->isConstant()); Array arr1 = node.getArrayDouble(0); cout<isConstant()); Array arr1 = node.getArrayDouble(0); cout<isConstant()); Array arr1 = node.getArrayDouble(0); cout<(0, "km"), frame); fm.calculate(mvd); MEarthMagnetic coord(fm.getField(), MEarthMagnetic::ITRF); { Vector em = MEarthMagnetic::Convert (coord, MEarthMagnetic::Ref(MEarthMagnetic::APP,frame))() .getValue().getValue(); cout<<"meas="<isConstant()); Array arr1 = node.getArrayDouble(0); cout< #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace casacore; using namespace std; void testScalar() { cout << "test scalars ..." << endl; // Convert an epoch from UTC to LAST. MEpoch epo(Quantity(50217.625,"d")); // 14-may-1996/15:00 MPosition pos(Quantity(10,"m"), Quantity(6.60417,"deg"), Quantity(52.8,"deg"), MPosition::WGS84); // near WSRT MeasFrame frame(pos); { Double value = MEpoch::Convert (epo, MEpoch::Ref(MEpoch::LAST,frame))() .getValue().get(); TableExprNode node(tableCommand ("calc meas.epoch('f-last'," "mjdtodate(50217.625d),'UTC'," "6.60417deg, 52.8deg, 10m, 'WGS84')d").node()); AlwaysAssertExit (node.getNodeRep()->isConstant()); Double sca1 = node.getDouble(0); AlwaysAssertExit (node.unit().getName() == "d"); AlwaysAssertExit (near(value, sca1, 1e-8)); } { TableExprNode node(tableCommand ("calc " "meas.epoch('ut1', 50217.625d,'UTC')d").node()); AlwaysAssertExit (node.getNodeRep()->isConstant()); Double sca1 = node.getDouble(0); AlwaysAssertExit (node.unit().getName() == "d"); AlwaysAssertExit (sca1 - 50217.625 != 0); } { TableExprNode node(tableCommand ("calc meas.epoch('utc'," "meas.epoch('ut1', 50217.625d,'UTC'))d").node()); AlwaysAssertExit (node.getNodeRep()->isConstant()); Double sca1 = node.getDouble(0); AlwaysAssertExit (node.unit().getName() == "d"); AlwaysAssertExit (near(50217.625, sca1, 1e-10)); } } void testArray() { cout << "test arrays ..." << endl; // Convert a few epochs from UTC. TableExprNode node1(tableCommand ("calc meas.epoch('f-last'," "mjdtodate([50217.625d,50417.625d,50617.625]),'UTC'," "[6.60417deg, 52.8deg, -60.60417deg, -32.8deg]," "[10m,1000m], 'WGS84')d").node()); // Do it as multi-dim TaQL arrays. TableExprNode node2(tableCommand ("using style python calc meas.epoch('f-last'," "mjdtodate([[50217.625d],[50417.625d],[50617.625]])," "'UTC'," "[[6.60417deg, 52.8deg], [-60.60417deg, -32.8deg]]," "[[10m,1000m]], 'WGS84')d").node()); AlwaysAssertExit (node1.getNodeRep()->isConstant()); AlwaysAssertExit (node2.getNodeRep()->isConstant()); ///cout << "taql=" << node1.getArrayDouble(0) << endl; ///cout << "taql=" << node2.getArrayDouble(0) << endl; Array arr1 = node1.getArrayDouble(0); Array arr2 = node2.getArrayDouble(0); AlwaysAssertExit (arr1.shape() == IPosition(2,3,2)); cout< epo(3); Vector pos(2); epo[0] = MEpoch(Quantity(50217.625,"d")); // 14-may-1996/15:00 epo[1] = MEpoch(Quantity(50417.625,"d")); // 30-nov-1996/15:00 epo[2] = MEpoch(Quantity(50617.625,"d")); // 18-jun-1997/15:00 pos[0] = MPosition(Quantity(10,"m"), Quantity(6.60417,"deg"), Quantity(52.8,"deg"), MPosition::WGS84); // near WSRT pos[1] = MPosition(Quantity(1000,"m"), Quantity(-60.60417,"deg"), Quantity(-32.8,"deg"), MPosition::WGS84); int inx=0; for (uInt ip=0; ip epo(3); Vector pos(3); epo[0] = MEpoch(Quantity(50217.625,"d")); // 14-may-1996/15:00 epo[1] = MEpoch(Quantity(50417.625,"d")); // 30-nov-1996/15:00 epo[2] = MEpoch(Quantity(50617.625,"d")); // 18-jun-1997/15:00 pos[0] = MPosition(Quantity(10,"m"), Quantity(6.60417,"deg"), Quantity(52.8,"deg"), MPosition::WGS84); // near WSRT pos[1] = MPosition(Quantity(1000,"m"), Quantity(-60.60417,"deg"), Quantity(-32.8,"deg"), MPosition::WGS84); pos[2] = MPosition(Quantity(2000,"m"), Quantity(-6.60417,"deg"), Quantity(32.8,"deg"), MPosition::WGS84); // Convert a few epochs from UTC. // Note that the columns have the same values as the arrays above. // They are filled in tEpochEngine.run. // Use a column with POS in deg. TableExprNode node1(tableCommand ("using style python calc meas.epoch('F_LAST'," "TIME,POS1)d " "from tEpochEngine_tmp.tab").node()); // Use a column with POS in xyz. TableExprNode node2(tableCommand ("using style python calc meas.epoch('F_LAST'," "TIME,POS2)d " "from tEpochEngine_tmp.tab").node()); // Use a nested MEAS function. TableExprNode node3(tableCommand ("calc meas.epoch ('UTC', meas.epoch('F_LAST'," "TIME,POS2), POS2)d " "from tEpochEngine_tmp.tab").node()); // Now use a column expression, so reftype must be given. TableExprNode node4(tableCommand ("using style python calc meas.epoch('F_LAST'," "TIME-0,'UTC',POS1)d " "from tEpochEngine_tmp.tab").node()); AlwaysAssertExit (node1.nrow() == 3 && node2.nrow() == 3); AlwaysAssertExit (! node1.getNodeRep()->isConstant()); AlwaysAssertExit (! node2.getNodeRep()->isConstant()); AlwaysAssertExit (! node3.getNodeRep()->isConstant()); AlwaysAssertExit (! node4.getNodeRep()->isConstant()); AlwaysAssertExit (node1.isScalar()); AlwaysAssertExit (node2.isScalar()); AlwaysAssertExit (node3.isScalar()); AlwaysAssertExit (node4.isScalar()); for (uInt i=0; i<3; ++i) { ///cout << "taql=" << node1.getArrayDouble(i) << endl; ///cout << "taql=" << node2.getArrayDouble(i) << endl; Double sca1 = node1.getDouble(i); Double sca2 = node2.getDouble(i); Double sca3 = node3.getDouble(i); Double sca4 = node4.getDouble(i); double epn = MEpoch::Convert (epo[i], MEpoch::Ref(MEpoch::LAST, MeasFrame(pos[i])))() .getValue().get(); AlwaysAssertExit (near(epn, sca1, 1e-8)); AlwaysAssertExit (near(epn, sca2, 1e-8)); AlwaysAssertExit (near(epo[i].getValue().get(), sca3, 1e-8)); AlwaysAssertExit (near(epn, sca4, 1e-8)); } } int checkErr (const String& command) { Bool fail = False; try { TableExprNode node(tableCommand(command).node()); if (node.isScalar()) { node.getDouble(0); } else { node.getArrayDouble(0); } } catch (const std::exception& x) { cout << "Expected exception: " << x.what() << endl; fail = True; } if (!fail) { cout << "Command '" + command + "' should have failed" << endl; return 1; } return 0; } void testErr() { cout << "test erroneous function calls ..." << endl; int nsucc = 0; nsucc += checkErr("using style python calc meas.last(23m)"); nsucc += checkErr("using style python calc meas.last(date())"); nsucc += checkErr("using style python calc meas.last('23-may-2005')"); nsucc += checkErr("using style python calc meas.epoch('XYZ')"); nsucc += checkErr("using style python calc meas.epoch(POS1) " "from tEpochEngine_tmp.tab"); nsucc += checkErr("using style python calc meas.epoch('ut1',TIME+0) " "from tEpochEngine_tmp.tab"); AlwaysAssertExit(nsucc == 0); } int main() { try { // Register the MEAS functions in TaQL. register_meas(); // Execute some tests. testErr(); testScalar(); testArray(); testColumn(); } catch (const std::exception& x) { cerr << "Unexpected exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/meas/MeasUDF/test/tEpochEngine.run000077500000000000000000000016171476623553700215620ustar00rootroot00000000000000#!/bin/sh # Create a table with 3 columns containing epochs and positions. # The same positions are defined as WGS84 (llh) and ITRF (xyz). # Define the measure keywords for them. ../../../tables/apps/taql "create table tEpochEngine_tmp.tab TIME R8 [unit='s'], POS1 R8 [shape=[3], unit=['deg','deg','m']], POS2 R8 [shape=[3], unit=['m','m','m']]" ../../../tables/apps/taql "alter table tEpochEngine_tmp.tab set keyword TIME::MEASINFO=[type='epoch',Ref='UTC'], POS1::MEASINFO=[type='position',Ref='WGS84'], POS2::MEASINFO=[type='position',Ref='ITRF']" ../../../tables/apps/taql 'insert into tEpochEngine_tmp.tab (TIME,POS1,POS2) values (50217.625d, [6.60417, 52.8, 10], [3.83879e+06, 444447, 5.05713e+06]), (50417.625d, [-60.60417, -32.8, 1000], [2.63452e+06, -4.67631e+06, -3.43588e+06]), (50617.625d, [-6.60417, 32.8, 2000], [5.33258e+06, -617394, 3.43642e+06])' $casa_checktool ./tEpochEngine casacore-3.7.1/meas/MeasUDF/test/tFrequencyEngine.cc000066400000000000000000000522441476623553700222450ustar00rootroot00000000000000//# tFrequencyEngine.cc: Test program for FrequencyEngine //# Copyright (C) 2018 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace casacore; using namespace std; void testScalar() { cout << "test scalars ..." << endl; // Convert a frequency for a given frame. MDirection coord(Quantity(185.425833,"deg"), Quantity(31.799167,"deg"), MDirection::B1950); MEpoch epo(Quantity(50217.625,"d")); // 14-may-1996/15:00 MPosition pos(Quantity(10,"m"), Quantity(6.60417,"deg"), Quantity(52.8,"deg"), MPosition::WGS84); // near WSRT MeasFrame frame(coord,epo,pos); MeasFrame rvframe(coord,epo,pos); MFrequency freq(Quantity(1e9, "Hz"), MFrequency::LSRK); Double res = MFrequency::Convert (freq, MFrequency::Ref(MFrequency::BARY,frame))().getValue().getValue(); //cout << "meas=" << res << endl; MRadialVelocity radvel(Quantity(1000, "km/s"), MRadialVelocity::Ref(MRadialVelocity::BARY, rvframe)); frame.set (radvel); Double res1 = MFrequency::Convert (freq, MFrequency::Ref(MFrequency::REST, frame))().getValue().getValue(); //cout << "meas=" << res1 << endl; { TableExprNode node(tableCommand ("calc meas.freq('BARY',1GHz, 'LSRK'," "185.425833deg, 31.799167deg,'B1950'," "mjdtodate(50217.625d),'UTC'," "6.60417deg, 52.8deg, 10m, 'WGS84')").node()); AlwaysAssertExit (node.getNodeRep()->isConstant()); Double val1 = node.getDouble(0); //cout << "taql=" << val1 << endl; AlwaysAssertExit (node.unit().getName() == "Hz"); AlwaysAssertExit (near(res, val1, 1e-8)); } { // Specify frequency as a period. TableExprNode node(tableCommand ("calc meas.freq('BARY',1e-9 s, 'LSRK'," "185.425833deg, 31.799167deg,'B1950'," "mjdtodate(50217.625d),'UTC'," "6.60417deg, 52.8deg, 10m, 'WGS84')").node()); AlwaysAssertExit (node.getNodeRep()->isConstant()); Double val1 = node.getDouble(0); //cout << "taql=" << val1 << endl; AlwaysAssertExit (node.unit().getName() == "Hz"); AlwaysAssertExit (near(res, val1, 1e-8)); } { // Specify frequency as a wavelength. TableExprNode node(tableCommand ("calc meas.freq('BARY',c()/1GHz, 'LSRK'," "185.425833deg, 31.799167deg,'B1950'," "mjdtodate(50217.625d),'UTC'," "6.60417deg, 52.8deg, 10m, 'WGS84')").node()); AlwaysAssertExit (node.getNodeRep()->isConstant()); Double val1 = node.getDouble(0); //cout << "taql=" << val1 << endl; AlwaysAssertExit (node.unit().getName() == "Hz"); AlwaysAssertExit (near(res, val1, 1e-8)); } { // Convert to rest which requires radvel. TableExprNode node(tableCommand ("calc meas.rest(1GHz, 'LSRK'," "1000 'km/s', 'BARY'," "185.425833deg, 31.799167deg,'B1950'," "mjdtodate(50217.625d),'UTC'," "6.60417deg, 52.8deg, 10m, 'WGS84')").node()); AlwaysAssertExit (node.getNodeRep()->isConstant()); Double val1 = node.getDouble(0); //cout << "taql=" << val1 <isConstant()); Double val1 = node.getDouble(0); //cout << "taql=" << val1 <isConstant()); Double val1 = node.getDouble(0); //cout << "taql=" << val1 << ' '<isConstant()); Double val1 = node.getDouble(0); //cout << "taql=" << arr1 << endl; AlwaysAssertExit (node.unit().getName() == "Hz"); AlwaysAssertExit (near(rfreq.getValue().getValue(), val1, 1e-8)); } { TableExprNode node(tableCommand ("calc meas.shift(400MHz, 'LSRK', 3.5)").node()); AlwaysAssertExit (node.getNodeRep()->isConstant()); Double val1 = node.getDouble(0); //cout << "taql=" << arr1 << endl; AlwaysAssertExit (node.unit().getName() == "Hz"); AlwaysAssertExit (near(bfreq.getValue().getValue(), val1, 1e-8)); } } void testArray() { cout << "test arrays ..." << endl; // Convert a few frequencies from J2000. TableExprNode node1(tableCommand ("calc meas.freq ('LSRK', [[200],[220]]MHz, 'BARY'," "[185.425833deg, 31.799167deg," "175.425833deg, 41.799167deg," "165.425833deg, 51.799167deg," "155.425833deg, 61.799167deg],'J2000'," "mjdtodate([50217.625d,50417.625d,50617.625]),'UTC'," "[6.60417deg, 52.8deg, -60.60417deg, -32.8deg]," "[10m,1000m], 'WGS84')").node()); TableExprNode node2(tableCommand ("calc meas.rest ([[200],[220]]MHz, 'BARY'," "[500,600,700]'km/s','LSRK'," "[185.425833deg, 31.799167deg," "175.425833deg, 41.799167deg," "165.425833deg, 51.799167deg," "155.425833deg, 61.799167deg],'J2000'," "mjdtodate([50217.625d,50417.625d,50617.625]),'UTC'," "[6.60417deg, 52.8deg, -60.60417deg, -32.8deg]," "[10m,1000m], 'WGS84')").node()); AlwaysAssertExit (node1.getNodeRep()->isConstant()); AlwaysAssertExit (node2.getNodeRep()->isConstant()); Array arr1 = node1.getArrayDouble(0); Array arr2 = node2.getArrayDouble(0); //cout << "taql=" << arr1 << endl; //cout << "taql=" << arr2 << endl; AlwaysAssertExit (arr1.shape() == IPosition(5,1,2,4,3,2)); AlwaysAssertExit (arr2.shape() == IPosition(6,1,2,3,4,3,2)); VectorIterator arr1iter(arr1); VectorIterator arr2iter(arr2); // Check with Measures. Vector freq(2); Vector coord(4); Vector epo(3); Vector pos(2); Vector vel(3); freq[0] = MFrequency(Quantity(200, "MHz"), MFrequency::BARY); freq[1] = MFrequency(Quantity(220, "MHz"), MFrequency::BARY); coord[0] = MDirection(Quantity(185.425833,"deg"), Quantity(31.799167,"deg"), MDirection::J2000); coord[1] = MDirection(Quantity(175.425833,"deg"), Quantity(41.799167,"deg"), MDirection::J2000); coord[2] = MDirection(Quantity(165.425833,"deg"), Quantity(51.799167,"deg"), MDirection::J2000); coord[3] = MDirection(Quantity(155.425833,"deg"), Quantity(61.799167,"deg"), MDirection::J2000); epo[0] = MEpoch(Quantity(50217.625,"d")); // 14-may-1996/15:00 epo[1] = MEpoch(Quantity(50417.625,"d")); // 30-nov-1996/15:00 epo[2] = MEpoch(Quantity(50617.625,"d")); // 18-jun-1997/15:00 pos[0] = MPosition(Quantity(10,"m"), Quantity(6.60417,"deg"), Quantity(52.8,"deg"), MPosition::WGS84); // near WSRT pos[1] = MPosition(Quantity(1000,"m"), Quantity(-60.60417,"deg"), Quantity(-32.8,"deg"), MPosition::WGS84); vel[0] = 500; vel[1] = 600; vel[2] = 700; for (uInt ip=0; ip dop(2); dop[0] = MDoppler(MVDoppler(3.5), MDoppler::RADIO); dop[1] = MDoppler(MVDoppler(2.5), MDoppler::RADIO); Vector freq(3); freq[0] = MFrequency(Quantity(1.0e9, "Hz"), MFrequency::LSRK); freq[1] = MFrequency(Quantity(1.1e9, "Hz"), MFrequency::LSRK); freq[2] = MFrequency(Quantity(1.2e9, "Hz"), MFrequency::LSRK); TableExprNode node1(tableCommand ("calc meas.rest([1,1.1,1.2]GHz, 'LSRK', [3.5,2.5])").node()); TableExprNode node2(tableCommand ("calc meas.shift([400,440,480]MHz, 'LSRK', [3.5])").node()); TableExprNode node3(tableCommand ("calc meas.shift([400,440,480]MHz/0.6, 'LSRK', 2.5)").node()); AlwaysAssertExit (node1.getNodeRep()->isConstant()); AlwaysAssertExit (node2.getNodeRep()->isConstant()); AlwaysAssertExit (node3.getNodeRep()->isConstant()); Array arr1 = node1.getArrayDouble(0); Array arr2 = node2.getArrayDouble(0); Array arr3 = node3.getArrayDouble(0); //cout << "taql=" << arr1 << endl; //cout << "taql=" << arr2 << endl; //cout << "taql=" << arr3 << endl; AlwaysAssertExit (node1.unit().getName() == "Hz"); AlwaysAssertExit (node2.unit().getName() == "Hz"); AlwaysAssertExit (node3.unit().getName() == "Hz"); AlwaysAssertExit (arr1.shape() == IPosition(2,3,2)); AlwaysAssertExit (arr2.shape() == IPosition(2,3,1)); AlwaysAssertExit (arr3.shape() == IPosition(2,3,1)); for (uInt id=0; id freq(2); Vector dop(2); Vector vel(4); Vector coord(2); Vector epo(2); Vector pos(2); freq[0] = MFrequency(Quantity(0.2, "GHz"), MFrequency::LSRK); freq[1] = MFrequency(Quantity(0.3, "GHz"), MFrequency::LSRK); dop[0] = MDoppler(MVDoppler(0.5), MDoppler::RADIO); dop[1] = MDoppler(MVDoppler(0.6), MDoppler::RADIO); vel[0] = 2e5; vel[1] = 3e5; vel[2] = 3e5; vel[3] = 2e5; coord[0] = MDirection(Quantity(185.425833,"deg"), Quantity(31.799167,"deg"), MDirection::J2000); coord[1] = MDirection(Quantity(175.425833,"deg"), Quantity(41.799167,"deg"), MDirection::J2000); epo[0] = MEpoch(Quantity(50217.625,"d")); // 14-may-1996/15:00 epo[1] = MEpoch(Quantity(50417.625,"d")); // 30-nov-1996/15:00 pos[0] = MPosition(Quantity(10,"m"), Quantity(6.60417,"deg"), Quantity(52.8,"deg"), MPosition::WGS84); // near WSRT pos[1] = MPosition(Quantity(1000,"m"), Quantity(-60.60417,"deg"), Quantity(-32.8,"deg"), MPosition::WGS84); // Convert a few frequencies. TableExprNode node1(tableCommand ("using style python calc meas.freq(" "'BARY',FREQ," "DIR[0,],TIME,POS1) " "from tFrequencyEngine_tmp.tab").node()); TableExprNode node2(tableCommand ("using style python calc meas.rest(" "FREQ,RVCOL," "DIR[0,],TIME,POS1) " "from tFrequencyEngine_tmp.tab").node()); AlwaysAssertExit (! node1.getNodeRep()->isConstant()); AlwaysAssertExit (! node2.getNodeRep()->isConstant()); AlwaysAssertExit (node1.nrow() == 2 && node2.nrow() == 2); for (uInt i=0; i<2; ++i) { Double val1 = node1.getDouble(i); //cout << "taql=" << val1 << endl; Array arr2 = node2.getArrayDouble(i); //cout << "taql=" << arr2 << endl; AlwaysAssertExit (arr2.shape() == IPosition(5,1,2,1,1,1)); MeasFrame frame(coord[i],epo[i],pos[i]); MeasFrame rvframe(coord[i],epo[i],pos[i]); Double fr = MFrequency::Convert (freq[i], MFrequency::Ref(MFrequency::BARY, frame))() .getValue().getValue(); //cout << "meas=" << fr << ' '<< val1 << endl; AlwaysAssertExit (near(fr, val1, 1e-8)); for (int j=0; j<2; ++j) { MRadialVelocity rv(Quantity(vel[2*i+j], "m/s"), MRadialVelocity::Ref(MRadialVelocity::BARY, rvframe)); frame.set (rv); Double fr = MFrequency::Convert (freq[i], MFrequency::Ref(MFrequency::REST, frame))() .getValue().getValue(); AlwaysAssertExit (near(fr, arr2.data()[j], 1e-8)); } } } void testDopplerColumn() { cout << "test doppler columns ..." << endl; // Check with Measures. Vector freq(2); Vector dop(2); freq[0] = MFrequency(Quantity(0.2, "GHz"), MFrequency::LSRK); freq[1] = MFrequency(Quantity(0.3, "GHz"), MFrequency::LSRK); dop[0] = MDoppler(MVDoppler(0.5), MDoppler::RADIO); dop[1] = MDoppler(MVDoppler(0.6), MDoppler::RADIO); // Convert a few frequencies. TableExprNode node1(tableCommand ("calc meas.rest(FREQ, DOPPCOL) " "from tFrequencyEngine_tmp.tab").node()); TableExprNode node2(tableCommand ("calc meas.shift(FREQ, DOPPCOL) " "from tFrequencyEngine_tmp.tab").node()); AlwaysAssertExit (! node1.getNodeRep()->isConstant()); AlwaysAssertExit (! node2.getNodeRep()->isConstant()); AlwaysAssertExit (node1.nrow() == 2 && node2.nrow() == 2); for (uInt i=0; i<2; ++i) { Double val1 = node1.getDouble(i); Double val2 = node2.getDouble(i); //cout << "taql=" << val1 << endl; MFrequency rfreq = freq[i].toRest (dop[i]); //cout << "meas=" << fr << ' '<< val1 << endl; AlwaysAssertExit (near(rfreq.getValue().getValue(), val1, 1e-8)); MFrequency bfreq = MFrequency::fromDoppler (dop[i], freq[i].getValue()); AlwaysAssertExit (near(bfreq.getValue().getValue(), val2, 1e-8)); } } int checkErr (const String& command) { Bool fail = False; try { TableExprNode node(tableCommand(command).node()); if (node.isScalar()) { node.getDouble(0); } else { node.getArrayDouble(0); } } catch (const std::exception& x) { cout << "Expected exception: " << x.what() << endl; fail = True; } if (!fail) { cout << "Command '" + command + "' should have failed" << endl; return 1; } return 0; } void testErr() { cout << "test erroneous function calls ..." << endl; int nsucc = 0; nsucc += checkErr("using style python calc meas.freqx()"); nsucc += checkErr("using style python calc meas.freq()"); nsucc += checkErr("using style python calc meas.frequency()"); nsucc += checkErr("using style python calc meas.rest()"); nsucc += checkErr("using style python calc meas.restfreq()"); nsucc += checkErr("using style python calc meas.restfrequency()"); nsucc += checkErr("using style python calc meas.shift()"); nsucc += checkErr("using style python calc meas.shiftfreq()"); nsucc += checkErr("using style python calc meas.shiftfrequency()"); nsucc += checkErr("using style python calc meas.freq(10)"); nsucc += checkErr("using style python calc meas.freq('BARX',10)"); nsucc += checkErr("using style python calc meas.freq('BARY',10,'LSRX')"); nsucc += checkErr("using style python calc meas.freq('BARY',10,'REST')"); nsucc += checkErr("using style python calc meas.freq('BARY')"); nsucc += checkErr("using style python calc meas.freq('BARY','LSRK')"); nsucc += checkErr("using style python calc meas.shift('BARY',10,'LSRK')"); nsucc += checkErr("using style python calc meas.shift(10,'LSRK')"); nsucc += checkErr("using style python calc meas.shift(10,'LSRX')"); nsucc += checkErr("using style python calc meas.shift('BARY',10)"); nsucc += checkErr("using style python calc meas.shift(10,'LSRK',2,3)"); nsucc += checkErr("using style python calc meas.shift(10,'LSRK',2MHz)"); nsucc += checkErr("using style python calc meas.rest('LSRK',2MHz)"); nsucc += checkErr("using style python calc meas.rest(10,'LSRK',2MHz)"); nsucc += checkErr("using style python calc meas.freq('rest',10,'LSRK',2MHz)"); nsucc += checkErr("using style python calc meas.freq('lsrk',10,'rest',2MHz)"); AlwaysAssertExit(nsucc == 0); } int main() { try { // Register the MEAS functions in TaQL. register_meas(); // Execute some tests. testErr(); testScalar(); testDopplerScalar(); testArray(); testDopplerArray(); testColumn(); testDopplerColumn(); } catch (const std::exception& x) { cerr << "Unexpected exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/meas/MeasUDF/test/tFrequencyEngine.run000066400000000000000000000022211476623553700224520ustar00rootroot00000000000000#!/bin/sh # Create a table with 7 columns containing freq, radvel, doppler, # restfreq, directions, epochs and positions. # Define the measure keywords for them. ../../../tables/apps/taql "create table tFrequencyEngine_tmp.tab DOPPCOL R8, RVCOL R8 [shape=[2], unit='m/s'], FREQ R8 [unit='GHz'], RESTFREQ R8 [unit='MHz'], DIR R8 [shape=[2,2], unit=['deg','deg']], TIME R8 [unit='s'], POS1 R8 [shape=[3], unit=['deg','deg','m']]" ../../../tables/apps/taql "alter table tFrequencyEngine_tmp.tab set keyword DOPPCOL::MEASINFO=[type='doppler',Ref='RADIO'], RVCOL::MEASINFO=[type='radialvelocity',Ref='BARY'], FREQ::MEASINFO=[type='frequency',Ref='LSRK'], DIR::MEASINFO=[type='direction', Ref='J2000'], TIME::MEASINFO=[type='epoch',Ref='UTC'], POS1::MEASINFO=[type='position',Ref='WGS84']" ../../../tables/apps/taql 'insert into tFrequencyEngine_tmp.tab (DOPPCOL,RVCOL,FREQ,RESTFREQ,DIR,TIME,POS1) values (0.5, [2e5, 3e5], 0.2, 1425.445, [[185.425833deg, 31.799167deg], [0,0]], 50217.625d, [6.60417, 52.8, 10]), (0.6, [3e5, 2e5], 0.3, 4874.157, [[175.425833deg, 41.799167deg], [0,0]], 50417.625d, [-60.60417, -32.8, 1000])' $casa_checktool ./tFrequencyEngine casacore-3.7.1/meas/MeasUDF/test/tPositionEngine.cc000066400000000000000000000416421476623553700221100ustar00rootroot00000000000000//# tPositionEngine.cc: Test program for PositionEngine //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include using namespace casacore; using namespace std; void testScalar() { cout << "test scalars ..." << endl; // Convert a position from WGS84 to ITRF. MPosition pos(Quantity(10,"m"), Quantity(6.60417,"deg"), Quantity(52.8,"deg"), MPosition::WGS84); // near WSRT MVPosition npos = MPosition::Convert (pos, MPosition::Ref(MPosition::ITRF))().getValue(); ///cout << "meas=" << npos << endl; { TableExprNode node1(tableCommand ("calc meas.pos ('ITRF'," "6.60417deg, 52.8deg, 10m, 'WGS84')").node()); TableExprNode node2(tableCommand ("calc meas.itrfxyz (" "6.60417deg, 52.8deg, 10m, 'WGS84')").node()); AlwaysAssertExit (node1.getNodeRep()->isConstant()); AlwaysAssertExit (node2.getNodeRep()->isConstant()); Array arr1 = node1.getArrayDouble(0); Array arr2 = node2.getArrayDouble(0); AlwaysAssertExit (arr1.shape() == IPosition(1,3)); AlwaysAssertExit (arr2.shape() == IPosition(1,3)); VectorIterator veciter1(arr1); VectorIterator veciter2(arr2); ///cout << "taql=" << arr1 << endl; AlwaysAssertExit (node1.unit().getName() == "m"); AlwaysAssertExit (node2.unit().getName() == "m"); AlwaysAssertExit (allNear(npos.getValue(), veciter1.vector(), 1e-8)); AlwaysAssertExit (allNear(npos.getValue(), veciter2.vector(), 1e-8)); } { MPosition hpos1(MVPosition(Quantity(10,"m")), MPosition::WGS84); // near WSRT MVPosition nhpos1 = MPosition::Convert (hpos1, MPosition::Ref(MPosition::ITRF))().getValue(); MPosition hpos2(MVPosition(Quantity(1000,"m")), MPosition::WGS84); // near WSRT MVPosition nhpos2 = MPosition::Convert (hpos2, MPosition::Ref(MPosition::ITRF))().getValue(); TableExprNode node1(tableCommand ("calc meas.pos ('ITRF'," "10m)").node()); TableExprNode node2(tableCommand ("calc meas.itrfxyz (" "[10m, 1000m], 'WGS84H')").node()); AlwaysAssertExit (node1.getNodeRep()->isConstant()); AlwaysAssertExit (node2.getNodeRep()->isConstant()); Array arr1 = node1.getArrayDouble(0); Array arr2 = node2.getArrayDouble(0); AlwaysAssertExit (arr1.shape() == IPosition(1,3)); AlwaysAssertExit (arr2.shape() == IPosition(2,3,2)); VectorIterator veciter1(arr1); VectorIterator veciter2(arr2); ///cout << "taql=" << arr1 << endl; AlwaysAssertExit (node1.unit().getName() == "m"); AlwaysAssertExit (node2.unit().getName() == "m"); AlwaysAssertExit (allNear(nhpos1.getValue(), veciter1.vector(), 1e-8)); AlwaysAssertExit (allNear(nhpos1.getValue(), veciter2.vector(), 1e-8)); veciter2.next(); AlwaysAssertExit (allNear(nhpos2.getValue(), veciter2.vector(), 1e-8)); } { TableExprNode node1(tableCommand ("calc meas.itrfll (" "6.60417deg, 52.8deg, 10m, 'WGS84')").node()); TableExprNode node2(tableCommand ("calc meas.itrfh (" "6.60417*pi()/180., 52.8*pi()/180, 10," "'WGS84LL')").node()); TableExprNode node3(tableCommand ("calc meas.itrfllh (" "[6.60417*pi()/180., 52.8*pi()/180, 10]," "'WGS84LLH')").node()); AlwaysAssertExit (node1.getNodeRep()->isConstant()); AlwaysAssertExit (node2.getNodeRep()->isConstant()); AlwaysAssertExit (node3.getNodeRep()->isConstant()); Array arr1 = node1.getArrayDouble(0); Double arr2 = node2.getDouble(0); Array arr3 = node3.getArrayDouble(0); AlwaysAssertExit (arr1.shape() == IPosition(1,2)); AlwaysAssertExit (arr3.shape() == IPosition(1,3)); VectorIterator veciter1(arr1); VectorIterator veciter3(arr3); ///cout << "taql=" << arr1 << endl; AlwaysAssertExit (node1.unit().getName() == "rad"); AlwaysAssertExit (node2.unit().getName() == "m"); AlwaysAssertExit (node3.unit().getName().empty()); AlwaysAssertExit (allNear(npos.getAngle().getValue(), veciter1.vector(), 1e-8)); AlwaysAssertExit (near(npos.getLength().getValue(), arr2, 1e-8)); AlwaysAssertExit (near(npos.getAngle().getValue()[0], arr3.data()[0], 1e-8)); AlwaysAssertExit (near(npos.getAngle().getValue()[1], arr3.data()[1], 1e-8)); AlwaysAssertExit (near(npos.getLength().getValue(), arr3.data()[2], 1e-8)); } { TableExprNode node1(tableCommand ("calc meas.itrfll (" "6.60417deg, 52.8deg, 10m, 'WGS84')").node()); TableExprNode node2(tableCommand ("calc meas.itrfh (" "6.60417*pi()/180., 52.8*pi()/180, 10," "'WGS84LL')").node()); AlwaysAssertExit (node1.getNodeRep()->isConstant()); AlwaysAssertExit (node2.getNodeRep()->isConstant()); Array arr1 = node1.getArrayDouble(0); Double arr2 = node2.getDouble(0); AlwaysAssertExit (arr1.shape() == IPosition(1,2)); VectorIterator veciter1(arr1); ///cout << "taql=" << arr1 << endl; AlwaysAssertExit (node1.unit().getName() == "rad"); AlwaysAssertExit (node2.unit().getName() == "m"); AlwaysAssertExit (allNear(npos.getAngle().getValue(), veciter1.vector(), 1e-8)); AlwaysAssertExit (near(npos.getLength().getValue(), arr2, 1e-8)); } { // Test a nested meas.position function. TableExprNode node1(tableCommand ("calc meas.itrfllh (meas.itrfxyz (" "6.60417deg, 52.8deg, 10m, 'WGS84'))").node()); TableExprNode node2(tableCommand ("calc meas.itrfxyz (meas.itrfllh (" "6.60417deg, 52.8deg, 10m, 'WGS84'))").node()); AlwaysAssertExit (node1.getNodeRep()->isConstant()); AlwaysAssertExit (node2.getNodeRep()->isConstant()); Array arr1 = node1.getArrayDouble(0); Array arr2 = node2.getArrayDouble(0); AlwaysAssertExit (arr1.shape() == IPosition(1,3)); AlwaysAssertExit (arr2.shape() == IPosition(1,3)); VectorIterator veciter1(arr1); VectorIterator veciter2(arr2); ///cout << "taql=" << arr1 << endl; AlwaysAssertExit (node1.unit().getName().empty()); AlwaysAssertExit (node2.unit().getName() == "m"); AlwaysAssertExit (near(npos.getAngle().getValue()[0], arr1.data()[0], 1e-8)); AlwaysAssertExit (near(npos.getAngle().getValue()[1], arr1.data()[1], 1e-8)); AlwaysAssertExit (near(npos.getLength().getValue(), arr1.data()[2], 1e-8)); AlwaysAssertExit (allNear(npos.getValue(), veciter2.vector(), 1e-8)); } } void testArray() { cout << "test arrays ..." << endl; // Convert a few positions from ITRF. TableExprNode node1(tableCommand ("calc meas.wgs (" "[3.82849e+06, 443253, 5.06498e+06," " 3.83924e+06, 430428, 5.05801e+06m," " 3.83849e+06, 443453, 5.06598e+06," " 3.84924e+06, 430628, 5.05901e+06," " 3.81849e+06, 443153, 5.06398e+06," " 3.82924e+06, 430328, 5.05701e+06])").node()); // Do it as multi-dim TaQL arrays. TableExprNode node2(tableCommand ("using style python calc meas.wgsll (" "[[[3.82849e+06, 443253, 5.06498e+06," " 3.83924e+06, 430428, 5.05801e+06]]," " [[3.83849e+06, 443453, 5.06598e+06," " 3.84924e+06, 430628, 5.05901e+06]]," " [[3.81849e+06, 443153, 5.06398e+06," " 3.82924e+06, 430328, 5.05701e+06]]]," "'itrfxyz')").node()); AlwaysAssertExit (node1.getNodeRep()->isConstant()); AlwaysAssertExit (node2.getNodeRep()->isConstant()); ///cout << "taql=" << node1.getArrayDouble(0) << endl; ///cout << "taql=" << node2.getArrayDouble(0) << endl; Array arr1 = node1.getArrayDouble(0); Array arr2 = node2.getArrayDouble(0); AlwaysAssertExit (arr1.shape() == IPosition(2,3,6)); AlwaysAssertExit (arr2.shape() == IPosition(4,2,2,1,3)); VectorIterator arr1iter(arr1); VectorIterator arr2iter(arr2); // Check with Measures. Vector pos(6); pos[0] = MPosition(MVPosition(3.82849e+06, 443253, 5.06498e+06), MPosition::ITRF); pos[1] = MPosition(MVPosition(3.83924e+06, 430428, 5.05801e+06), MPosition::ITRF); pos[2] = MPosition(MVPosition(3.83849e+06, 443453, 5.06598e+06), MPosition::ITRF); pos[3] = MPosition(MVPosition(3.84924e+06, 430628, 5.05901e+06), MPosition::ITRF); pos[4] = MPosition(MVPosition(3.81849e+06, 443153, 5.06398e+06), MPosition::ITRF); pos[5] = MPosition(MVPosition(3.82924e+06, 430328, 5.05701e+06), MPosition::ITRF); for (uInt ip=0; ip pos(3); // Use same values as in tPositionEngine.run. pos[0] = MPosition(Quantity(10,"m"), Quantity(6.60417,"deg"), Quantity(52.8,"deg"), MPosition::WGS84); // near WSRT pos[1] = MPosition(Quantity(1000,"m"), Quantity(-60.60417,"deg"), Quantity(-32.8,"deg"), MPosition::WGS84); pos[2] = MPosition(Quantity(2000,"m"), Quantity(-6.60417,"deg"), Quantity(32.8,"deg"), MPosition::WGS84); // Convert a few positions. TableExprNode node1(tableCommand ("calc meas.itrfxyz (POS1) " "from tPositionEngine_tmp.tab").node()); TableExprNode node2(tableCommand // glish style starts at 1 !!! ("calc meas.itrfxyz ([POS2[1], POS2[2], POS2[3]]," "'WGS84') from tPositionEngine_tmp.tab").node()); AlwaysAssertExit (! node1.getNodeRep()->isConstant()); AlwaysAssertExit (! node2.getNodeRep()->isConstant()); AlwaysAssertExit (node1.nrow() == 3 && node2.nrow() == 3); for (uInt i=0; i<3; ++i) { ///cout << "taql=" << node1.getArrayDouble(i) << endl; ///cout << "taql=" << node2.getArrayDouble(i) << endl; Array arr1 = node1.getArrayDouble(i); Array arr2 = node2.getArrayDouble(i); AlwaysAssertExit (arr1.shape() == IPosition(1,3)); AlwaysAssertExit (arr2.shape() == IPosition(1,3)); VectorIterator veciter1(arr1); VectorIterator veciter2(arr2); MVPosition npos = MPosition::Convert (pos[i], MPosition::Ref(MPosition::ITRF))().getValue(); AlwaysAssertExit (allNear(npos.getValue(), veciter1.vector(), 1e-8)); AlwaysAssertExit (allNear(npos.getValue(), veciter2.vector(), 1e-6)); } } void testName() { cout << "test names ... " << endl; // Check with Measures. Vector pos(2); AlwaysAssertExit (MeasTable::Observatory(pos[0], "WSRT")); AlwaysAssertExit (MeasTable::Observatory(pos[1], "VLA")); TableExprNode node1(tableCommand ("using style python calc meas.itrfxyz (" "['WSRT','VLA'])").node()); TableExprNode node2(tableCommand ("using style python calc meas.wgsh (" "['WSRT','VLA'])").node()); AlwaysAssertExit (node1.getNodeRep()->isConstant()); AlwaysAssertExit (node2.getNodeRep()->isConstant()); ///cout << "taql=" << node1.getArrayDouble(0) << endl; Array arr1 = node1.getArrayDouble(0); Array arr2 = node2.getArrayDouble(0); AlwaysAssertExit (arr1.shape() == IPosition(2,3,2)); AlwaysAssertExit (arr2.shape() == IPosition(1,2)); VectorIterator arr1iter(arr1); // Check with Measures. for (uInt ip=0; ip #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace casacore; using namespace std; void testScalar() { cout << "test radialvelocity scalars ..." << endl; // Convert a radialvelocity for a given frame. MDirection coord(Quantity(185.425833,"deg"), Quantity(31.799167,"deg"), MDirection::B1950); MEpoch epo(Quantity(50217.625,"d")); // 14-may-1996/15:00 MPosition pos(Quantity(10,"m"), Quantity(6.60417,"deg"), Quantity(52.8,"deg"), MPosition::WGS84); // near WSRT MeasFrame frame(coord,epo,pos); MRadialVelocity rv(Quantity(100,"km/s"), MRadialVelocity::Ref(MRadialVelocity::BARY, frame)); MVRadialVelocity nrv = MRadialVelocity::Convert (rv, MRadialVelocity::Ref(MRadialVelocity::LSRK))().getValue(); //cout << "meas=" << nrv << " m/s" << endl; { TableExprNode node1(tableCommand ("calc meas.rv ('LSRK'," "0100'km/s', 'BARY'," "185.425833deg, 31.799167deg, 'B1950'," "50217.625d," "6.60417deg, 52.8deg, 'WGS84')'m/s'").node()); AlwaysAssertExit (node1.getNodeRep()->isConstant()); Double val1 = node1.getDouble(0); //cout << "taql=" << val1 << endl; AlwaysAssertExit (node1.unit().getName() == "m/s"); AlwaysAssertExit (near(nrv.getValue(), val1, 1e-8)); } } void testDopplerScalar() { cout << "test doppler scalars ..." << endl; // Make a radvel from a Z doppler. MDoppler dop(Quantity(2.5), MDoppler::Z); MRadialVelocity mvel(MRadialVelocity::fromDoppler(dop)); Double vel = mvel.getValue().getValue() / 1000.; // km/s //cout << "meas=" << vel << endl; { TableExprNode node1(tableCommand ("calc meas.radvel('LSRK'," "2.5, 'Z')").node()); AlwaysAssertExit (node1.getNodeRep()->isConstant()); Double val1 = node1.getDouble(0); //cout << "taql=" << val1 << endl; AlwaysAssertExit (node1.unit().getName() == "km/s"); AlwaysAssertExit (near(vel, val1, 1e-8)); } } void testArray() { cout << "test radialvelocity arrays ..." << endl; // Convert a radialvelocity for a given frame. MDirection coord(Quantity(185.425833,"deg"), Quantity(31.799167,"deg"), MDirection::B1950); MEpoch epo(Quantity(50217.625,"d")); // 14-may-1996/15:00 MPosition pos(Quantity(10,"m"), Quantity(6.60417,"deg"), Quantity(52.8,"deg"), MPosition::WGS84); // near WSRT MeasFrame frame(coord,epo,pos); MRadialVelocity rv1(Quantity(100,"km/s"), MRadialVelocity::Ref(MRadialVelocity::LSRK, frame)); MRadialVelocity rv2(Quantity(200,"km/s"), MRadialVelocity::Ref(MRadialVelocity::LSRK, frame)); MVRadialVelocity nrv1 = MRadialVelocity::Convert (rv1, MRadialVelocity::Ref(MRadialVelocity::TOPO))().getValue(); MVRadialVelocity nrv2 = MRadialVelocity::Convert (rv2, MRadialVelocity::Ref(MRadialVelocity::TOPO))().getValue(); //cout << "meas=" << nrv1 <<' '<isConstant()); Array arr1 = node1.getArrayDouble(0); //cout << "taql=" << arr1 << endl; AlwaysAssertExit (arr1.shape() == IPosition(1,2)); AlwaysAssertExit (node1.unit().getName() == "m/s"); AlwaysAssertExit (near(nrv1.getValue(), arr1.data()[0], 1e-8)); AlwaysAssertExit (near(nrv2.getValue(), arr1.data()[1], 1e-8)); } } void testDopplerArray() { cout << "test doppler arrays ..." << endl; // Make a radvel from a Z doppler. MDoppler dop1(Quantity(2.5), MDoppler::RADIO); MDoppler dop2(Quantity(1.5), MDoppler::RADIO); MRadialVelocity mvel1(MRadialVelocity::fromDoppler(dop1)); MRadialVelocity mvel2(MRadialVelocity::fromDoppler(dop2)); Double vel1 = mvel1.getValue().getValue() / 1000.; // km/s Double vel2 = mvel2.getValue().getValue() / 1000.; // km/s //cout << "meas=" << vel1 << endl; { TableExprNode node1(tableCommand ("calc meas.radvel('LSRK'," "[2.5,1.5], 'RADIO')").node()); AlwaysAssertExit (node1.getNodeRep()->isConstant()); Array arr1 = node1.getArrayDouble(0); //cout << "taql=" << arr1 << endl; AlwaysAssertExit (node1.unit().getName() == "km/s"); AlwaysAssertExit (near(vel1, arr1.data()[0], 1e-8)); AlwaysAssertExit (near(vel2, arr1.data()[1], 1e-8)); } } void testColumn() { cout << "test radvel columns ..." << endl; // Check with Measures. MDirection coord1(Quantity(185.425833,"deg"), Quantity(31.799167,"deg"), MDirection::J2000); MEpoch epo1(Quantity(50217.625,"d")); // 14-may-1996/15:00 MPosition pos1(Quantity(10,"m"), Quantity(6.60417,"deg"), Quantity(52.8,"deg"), MPosition::WGS84); // near WSRT MeasFrame frame1(coord1,epo1,pos1); MRadialVelocity rv1a(Quantity(200,"km/s"), MRadialVelocity::Ref(MRadialVelocity::BARY, frame1)); MRadialVelocity rv2a(Quantity(300000,"m/s"), MRadialVelocity::Ref(MRadialVelocity::BARY, frame1)); MRadialVelocity nrv1a = MRadialVelocity::Convert (rv1a, MRadialVelocity::Ref(MRadialVelocity::GEO))(); MRadialVelocity nrv2a = MRadialVelocity::Convert (rv2a, MRadialVelocity::Ref(MRadialVelocity::GEO))(); Double vel1a = nrv1a.getValue().getValue() / 1000.; // km/s Double vel2a = nrv2a.getValue().getValue() / 1000.; // km/s MDirection coord2(Quantity(175.425833,"deg"), Quantity(41.799167,"deg"), MDirection::J2000); MEpoch epo2(Quantity(50417.625,"d")); // 14-may-1996/15:00 MPosition pos2(Quantity(1000,"m"), Quantity(-60.60417,"deg"), Quantity(-32.8,"deg"), MPosition::WGS84); // near WSRT MeasFrame frame2(coord2,epo2,pos2); MRadialVelocity rv1b(Quantity(300,"km/s"), MRadialVelocity::Ref(MRadialVelocity::BARY, frame2)); MRadialVelocity rv2b(Quantity(200000,"m/s"), MRadialVelocity::Ref(MRadialVelocity::BARY, frame2)); MRadialVelocity nrv1b = MRadialVelocity::Convert (rv1b, MRadialVelocity::Ref(MRadialVelocity::GEO))().getValue(); MRadialVelocity nrv2b = MRadialVelocity::Convert (rv2b, MRadialVelocity::Ref(MRadialVelocity::GEO))().getValue(); Double vel1b = nrv1b.getValue().getValue() / 1000.; // km/s Double vel2b = nrv2b.getValue().getValue() / 1000.; // km/s //cout << "meas=" << vel1a<<' '<isConstant()); AlwaysAssertExit (node1.getNodeRep()->valueType() == TableExprNodeRep::VTArray); AlwaysAssertExit (node1.unit().getName() == "km/s"); Array arr1 = node1.getArrayDouble(0); //cout << "taql=" << arr1 << endl; AlwaysAssertExit (arr1.shape() == IPosition(1,2)); AlwaysAssertExit (near(vel1a, arr1.data()[0], 1e-8)); AlwaysAssertExit (near(vel2a, arr1.data()[1], 1e-8)); Array arr2 = node1.getArrayDouble(1); AlwaysAssertExit (arr2.shape() == IPosition(1,2)); AlwaysAssertExit (near(vel1b, arr2.data()[0], 1e-8)); AlwaysAssertExit (near(vel2b, arr2.data()[1], 1e-8)); } void testDopplerColumn() { cout << "test doppler columns ..." << endl; // Check with Measures. MDoppler dop1(Quantity(0.2,""), MDoppler::RADIO); MDoppler dop2(Quantity(0.3,""), MDoppler::RADIO); MRadialVelocity mvel1(MRadialVelocity::fromDoppler(dop1)); MRadialVelocity mvel2(MRadialVelocity::fromDoppler(dop2)); Double vel1 = mvel1.getValue().getValue() / 1000.; // km/s Double vel2 = mvel2.getValue().getValue() / 1000.; // km/s ///cout << "meas=" << ndop << endl; TableExprNode node1(tableCommand ("calc meas.radvel('LSRK',DOPPCOL)" " from tRadialVelocityEngine_tmp.tab").node()); AlwaysAssertExit (! node1.getNodeRep()->isConstant()); AlwaysAssertExit (node1.getNodeRep()->valueType() == TableExprNodeRep::VTScalar); AlwaysAssertExit (node1.unit().getName() == "km/s"); Double val1 = node1.getDouble(0); ///cout << "taql=" << val1 << endl; AlwaysAssertExit (near(vel1, val1, 1e-8)); Double val2 = node1.getDouble(1); ///cout << "taql=" << val2 << endl; AlwaysAssertExit (near(vel2, val2, 1e-8)); } int checkErr (const String& command) { Bool fail = False; try { TableExprNode node(tableCommand(command).node()); if (node.isScalar()) { node.getDouble(0); } else { node.getArrayDouble(0); } } catch (const std::exception& x) { cout << "Expected exception: " << x.what() << endl; fail = True; } if (!fail) { cout << "Command '" + command + "' should have failed" << endl; return 1; } return 0; } void testErr() { cout << "test erroneous function calls ..." << endl; int nsucc = 0; nsucc += checkErr("calc meas.radialvelocity()"); nsucc += checkErr("calc meas.radvel('BETA')"); nsucc += checkErr("calc meas.radvel('LSRK')"); nsucc += checkErr("calc meas.rv('BX',20)"); nsucc += checkErr("calc meas.rv('LSRK',10,20)"); nsucc += checkErr("calc meas.rv('BARY',10,'XZ')"); nsucc += checkErr("calc meas.rv('BARY',10,'Z',20)"); nsucc += checkErr("calc meas.rv('BARY',10 'm/s','Z')"); nsucc += checkErr("calc meas.rv('BARY',DOPPCOL)"); nsucc += checkErr("calc meas.rv(10)"); nsucc += checkErr("calc meas.rv('BARY',20+3i)"); nsucc += checkErr("calc meas.rv('BARY',200MHz)"); AlwaysAssertExit(nsucc == 0); } int main() { try { // Register the MEAS functions in TaQL. register_meas(); // Execute some tests. testErr(); testScalar(); testDopplerScalar(); testArray(); testDopplerArray(); testColumn(); testDopplerColumn(); } catch (const std::exception& x) { cerr << "Unexpected exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/meas/MeasUDF/test/tRadialVelocityEngine.run000066400000000000000000000020111476623553700234210ustar00rootroot00000000000000#!/bin/sh # Create a table with 5 columns containing radvel, doppler, directions, epochs and positions. # Define the measure keywords for them. ../../../tables/apps/taql "create table tRadialVelocityEngine_tmp.tab DOPPCOL R8, RVCOL R8 [shape=[2], unit='m/s'], DIR R8 [shape=[2,2], unit=['deg','deg']], TIME R8 [unit='s'], POS1 R8 [shape=[3], unit=['deg','deg','m']]" ../../../tables/apps/taql "alter table tRadialVelocityEngine_tmp.tab set keyword DOPPCOL::MEASINFO=[type='doppler',Ref='RADIO'], RVCOL::MEASINFO=[type='radialvelocity',Ref='BARY'], DIR::MEASINFO=[type='direction', Ref='J2000'], TIME::MEASINFO=[type='epoch',Ref='UTC'], POS1::MEASINFO=[type='position',Ref='WGS84']" ../../../tables/apps/taql 'insert into tRadialVelocityEngine_tmp.tab (DOPPCOL,RVCOL,DIR,TIME,POS1) values (0.2, [2e5, 3e5], [[185.425833deg, 31.799167deg], [0,0]], 50217.625d, [6.60417, 52.8, 10]), (0.3, [3e5, 2e5], [[175.425833deg, 41.799167deg], [0,0]], 50417.625d, [-60.60417, -32.8, 1000])' $casa_checktool ./tRadialVelocityEngine casacore-3.7.1/meas/MeasUDF/test/tmeas.cc000066400000000000000000000031341476623553700200750ustar00rootroot00000000000000//# tmeas.cc: Test program for the meas classes //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include using namespace casacore; int main() { HelpMeasUDF::showFuncsPosition (std::cout, False); HelpMeasUDF::showFuncsEpoch (std::cout, False); HelpMeasUDF::showFuncsDirection (std::cout, False); HelpMeasUDF::showFuncsEarthMagnetic (std::cout, False); HelpMeasUDF::showFuncsFrequency (std::cout, False); HelpMeasUDF::showFuncsRadialVelocity (std::cout, False); } casacore-3.7.1/meas/MeasUDF/test/tmeas.out000066400000000000000000000251701476623553700203230ustar00rootroot00000000000000Position conversion functions: MEAS.POS (type, position) convert to given type POSITION is a synonym for POS MEAS.ITRFXYZ (position) convert to ITRF XYZ coord MEAS.ITRFLL (position) convert to ITRF LonLat ITRFLONLAT is a synonym for ITRFLL MEAS.ITRFH (position) convert to ITRF height ITRFHEIGHT is a synonym for ITRFH MEAS.WGS (position) convert to WGS84 XYZ coord WGSXYZ is a synonym for WGS MEAS.WGSLL (position) convert to WGS84 LonLat WGSLONLAT is a synonym for WGSLL MEAS.WGSH (position) convert to WGS84 height WGSHEIGHT is a synonym for WGSH Epoch conversion functions: MEAS.EPOCH (type, epoch [,position]) convert to given type MEAS.LAST (epoch, position) convert to local sidereal time LST is a synonym for LAST Direction conversion functions: MEAS.DIR (type, direction [,epoch, position]) convert to given type DIRECTION is a synonym for DIR MEAS.HADEC (direction, epoch, position) convert to Hourangle/Decl MEAS.AZEL (direction, epoch, position) convert to Azimuth/Elevation MEAS.APP (direction, epoch, position) convert to apparent APPARENT is a synonym for APP MEAS.J2000 (direction [,epoch, position]) convert to J2000 MEAS.B1950 (direction [,epoch, position]) convert to B1950 MEAS.ECL (direction [,epoch, position]) ECLIPTIC is a synonym for ECL MEAS.GAL (direction [,epoch, position]) GALACTIC is a synonym for GAL MEAS.SGAL (direction [,epoch, position]) SUPERGAL is a synonym for SGAL SUPERGALACTIC is a synonym for SGAL MEAS.ITRFD (direction [,epoch, position]) convert to ITRF ITRFDIR is a synonym for ITRFD ITRFDIRECTION is a synonym for ITRFD MEAS.RISET (direction, epoch, position) get rise/set time RISESET is a synonym for RISET MEAS.DIRCOS (type, direction [,epoch, position]) as DIR returning 3 direction cosines instead of 2 angles DIRECTIONCOSINE is a synonym for DIRCOS EarthMagnetic conversion functions: MEAS.EM (type, em, epoch, position) convert em value to given type as xyz EARTHMAGNETIC and EMXYZ are synonyms for EM MEAS.EMANG (type, em, epoch, position) convert and return as angles EMANGLES is a synonym for EMANG MEAS.EMLEN (type, em, epoch, position) convert and return as flux density EMLENGTH is a synonym for EMLEN MEAS.IGRF (type, height, direction, epoch, position) IGRF model value IGRFXYZ is a synonym for IGRF MEAS.IGRFANG (t, h, d, e, p) IGRF model angles in ITRF IGRFANGLES is a synonym for IGRFANG MEAS.IGRFLEN (t, h, d, e, p) IGRF model flux density IGRFLENGTH is a synonym for IGRFLEN MEAS.IGRFLOS (h, d, e, p) IGRF value along line-of-sight MEAS.IGRFLONG (h, d, e, p) longitude of calculation point Frequency conversion functions: MEAS.FREQ (type, freq, radvel, direction, epoch, position) convert to given type Instead of freq, a period or wavelength can be given (requires a unit) radvel is only needed when converting to/from rest frequencies FREQUENCY is a synonym for FREQ MEAS.REST (freq, radvel, direction, epoch, position) convert to rest freq MEAS.REST (freq, doppler) convert to rest freq RESTFREQ and RESTFREQUENCY are synonyms for REST MEAS.SHIFTFREQ (freq, doppler) shift frequencies SHIFT and SHIFTFREQUENCY are synonyms for SHIFTFREQ It can also be used to shift rest frequencies RadialVelocity conversion functions: MEAS.RADVEL (type, radvel, direction, epoch, position) convert to given type MEAS.RADVEL (type, doppler) calc from doppler RV and RADIALVELOCITY are synonyms for RADVEL Unit: m [2.99486, 0.346737, 3.98881] [-0.291542, 0.270379] [0.923005, -0.276987, 0.267097] [2010/08/10/04:06:04, 2010/08/10/19:10:42] false Unit: rad Axis Lengths: [366, 2] (NB: Matrix in Row/Column order) [2.05311, 4.09106 2.05247, 4.09585 2.05157, 4.10085 2.05043, 4.10604 2.04903, 4.11142 2.04739, 4.11698 2.04551, 4.12273 2.04338, 4.12865 2.04101, 4.13474 2.0384, 4.14098 2.03556, 4.14738 2.03249, 4.15393 2.02919, 4.16061 2.02566, 4.16743 2.02192, 4.17437 2.01795, 4.18143 2.01377, 4.1886 2.00938, 4.19588 2.00479, 4.20326 1.99999, 4.21073 1.995, 4.21828 1.98981, 4.22592 1.98443, 4.23363 1.97886, 4.2414 1.97312, 4.24924 1.96719, 4.25714 1.9611, 4.26509 1.95484, 4.27309 1.94841, 4.28113 1.94182, 4.28921 1.93508, 4.29732 1.92819, 4.30547 1.92115, 4.31365 1.91397, 4.32185 1.90665, 4.33007 1.8992, 4.33832 1.89161, 4.34657 1.8839, 4.35484 1.87607, 4.36312 1.86812, 4.3714 1.86005, 4.37969 1.85187, 4.38797 1.84359, 4.39625 1.8352, 4.40453 1.82671, 4.4128 1.81812, 4.42105 1.80943, 4.4293 1.80066, 4.43753 1.7918, 4.44575 1.78285, 4.45395 1.77382, 4.46213 1.76471, 4.4703 1.75552, 4.47844 1.74627, 4.48656 1.73694, 4.49466 1.72755, 4.50274 1.71809, 4.5108 1.70857, 4.51883 1.69899, 4.52685 1.68936, 4.53484 1.67967, 4.54281 1.66993, 4.55077 1.66015, 4.5587 1.65032, 4.56661 1.64045, 4.57451 1.63053, 4.58239 1.62058, 4.59025 1.6106, 4.5981 1.60058, 4.60593 1.59054, 4.61374 1.58046, 4.62153 1.57036, 4.62931 1.56024, 4.63708 1.5501, 4.64483 1.53994, 4.65256 1.52976, 4.66028 1.51956, 4.66799 1.50936, 4.67568 1.49914, 4.68335 1.48891, 4.69102 1.47868, 4.69867 1.46844, 4.70631 1.45821, 4.71395 1.44797, 4.72157 1.43773, 4.72918 1.4275, 4.73678 1.41728, 4.74438 1.40707, 4.75197 1.39686, 4.75956 1.38668, 4.76714 1.3765, 4.77473 1.36635, 4.78231 1.35621, 4.78989 1.3461, 4.79747 1.33602, 4.80505 1.32596, 4.81263 1.31593, 4.82021 1.30593, 4.8278 1.29597, 4.83538 1.28605, 4.84297 1.27617, 4.85056 1.26633, 4.85815 1.25653, 4.86574 1.24678, 4.87332 1.23708, 4.88091 1.22743, 4.8885 1.21784, 4.89608 1.2083, 4.90366 1.19883, 4.91124 1.18941, 4.91881 1.18006, 4.92637 1.17077, 4.93393 1.16156, 4.94147 1.15241, 4.94901 1.14335, 4.95653 1.13436, 4.96405 1.12546, 4.97155 1.11664, 4.97903 1.1079, 4.9865 1.09926, 4.99396 1.09072, 5.00139 1.08227, 5.0088 1.07393, 5.01619 1.06569, 5.02356 1.05756, 5.03089 1.04954, 5.0382 1.04164, 5.04547 1.03386, 5.0527 1.0262, 5.05989 1.01868, 5.06703 1.01128, 5.07413 1.00401, 5.08117 0.99689, 5.08816 0.989907, 5.09508 0.983069, 5.10194 0.976381, 5.10873 0.969844, 5.11544 0.963464, 5.12207 0.957243, 5.12862 0.951186, 5.13507 0.945296, 5.14143 0.939578, 5.14769 0.934034, 5.15384 0.928669, 5.15988 0.923487, 5.16581 0.918491, 5.17161 0.913685, 5.1773 0.909073, 5.18285 0.904658, 5.18827 0.900445, 5.19354 0.896436, 5.19867 0.892635, 5.20366 0.889046, 5.20848 0.885672, 5.21315 0.882516, 5.21764 0.87958, 5.22197 0.876867, 5.22612 0.874381, 5.23009 0.872122, 5.23387 0.870092, 5.23746 0.868293, 5.24085 0.866726, 5.24405 0.865392, 5.24703 0.864291, 5.24981 0.863424, 5.25238 0.86279, 5.25472 0.862389, 5.25685 0.862221, 5.25875 0.862286, 5.26043 0.862582, 5.26187 0.863108, 5.26308 0.863864, 5.26406 0.864847, 5.26481 0.866056, 5.26532 0.867489, 5.26559 0.869143, 5.26562 0.871017, 5.26542 0.873107, 5.26498 0.875412, 5.26431 0.877927, 5.26339 0.88065, 5.26224 0.883578, 5.26086 0.886706, 5.25924 0.890031, 5.25739 0.893548, 5.2553 0.897254, 5.25299 0.901142, 5.25045 0.905209, 5.24769 0.909449, 5.2447 0.913857, 5.24149 0.918428, 5.23807 0.923154, 5.23443 0.928032, 5.23058 0.933054, 5.22651 0.938215, 5.22225 0.943508, 5.21778 0.94893, 5.2131 0.954473, 5.20824 0.960132, 5.20318 0.965903, 5.19793 0.971779, 5.1925 0.977756, 5.18689 0.983828, 5.1811 0.989991, 5.17513 0.99624, 5.169 1.00257, 5.16271 1.00898, 5.15625 1.01546, 5.14964 1.02201, 5.14287 1.02863, 5.13596 1.03531, 5.1289 1.04204, 5.1217 1.04883, 5.11436 1.05567, 5.10689 1.06256, 5.09929 1.06949, 5.09156 1.07646, 5.08371 1.08347, 5.07574 1.09051, 5.06766 1.09758, 5.05946 1.10468, 5.05116 1.1118, 5.04275 1.11895, 5.03424 1.12611, 5.02563 1.13328, 5.01693 1.14047, 5.00813 1.14767, 4.99924 1.15488, 4.99027 1.1621, 4.98122 1.16932, 4.97208 1.17654, 4.96286 1.18377, 4.95358 1.19099, 4.94422 1.19823, 4.93479 1.20546, 4.92529 1.21269, 4.91574 1.21992, 4.90612 1.22715, 4.89644 1.23438, 4.88671 1.24161, 4.87693 1.24884, 4.8671 1.25607, 4.85722 1.2633, 4.8473 1.27053, 4.83733 1.27776, 4.82733 1.28499, 4.81728 1.29222, 4.80721 1.29945, 4.7971 1.30668, 4.78696 1.31391, 4.77679 1.32114, 4.7666 1.32837, 4.75638 1.3356, 4.74614 1.34283, 4.73588 1.35006, 4.72561 1.35729, 4.71532 1.36452, 4.70501 1.37176, 4.69469 1.37899, 4.68436 1.38624, 4.67404 1.39348, 4.6637 1.40072, 4.65336 1.40798, 4.64301 1.41524, 4.63267 1.42251, 4.62233 1.42979, 4.612 1.43707, 4.60168 1.44438, 4.59136 1.45169, 4.58106 1.45902, 4.57077 1.46637, 4.5605 1.47373, 4.55025 1.48111, 4.54002 1.4885, 4.52981 1.49592, 4.51963 1.50336, 4.50947 1.51082, 4.49935 1.51829, 4.48926 1.52579, 4.47921 1.53331, 4.46919 1.54085, 4.45922 1.54842, 4.44928 1.556, 4.43939 1.56361, 4.42954 1.57123, 4.41975 1.57888, 4.41 1.58654, 4.40031 1.59423, 4.39067 1.60194, 4.38109 1.60967, 4.37158 1.61741, 4.36213 1.62518, 4.35274 1.63297, 4.34343 1.64078, 4.33419 1.64861, 4.32502 1.65646, 4.31594 1.66433, 4.30693 1.67221, 4.29801 1.68011, 4.28919 1.68803, 4.28045 1.69597, 4.27181 1.70391, 4.26326 1.71188, 4.25483 1.71985, 4.24649 1.72783, 4.23827 1.73581, 4.23016 1.7438, 4.22217 1.75179, 4.2143 1.75978, 4.20656 1.76776, 4.19894 1.77573, 4.19146 1.78369, 4.18411 1.79163, 4.1769 1.79955, 4.16983 1.80745, 4.16291 1.81531, 4.15614 1.82315, 4.14953 1.83094, 4.14307 1.83869, 4.13678 1.8464, 4.13066 1.85405, 4.12471 1.86165, 4.11893 1.86919, 4.11334 1.87666, 4.10793 1.88406, 4.10271 1.89138, 4.09769 1.89862, 4.09286 1.90577, 4.08824 1.91283, 4.08383 1.91979, 4.07963 1.92664, 4.07564 1.93339, 4.07187 1.94001, 4.06833 1.94651, 4.06502 1.95288, 4.06194 1.95911, 4.0591 1.96519, 4.05649 1.97112, 4.05413 1.9769, 4.05201 1.98251, 4.05013 1.98795, 4.04851 1.9932, 4.04713 1.99828, 4.04601 2.00316, 4.04514 2.00785, 4.04452 2.01233, 4.04416 2.0166, 4.04406 2.02067, 4.04422 2.02451, 4.04463 2.02813, 4.0453 2.03152, 4.04623 2.03468, 4.04742 2.03761, 4.04886 2.0403, 4.05056 2.04274, 4.05251 2.04495, 4.05471 2.0469, 4.05716 2.04861, 4.05986 2.05007, 4.0628 2.05128, 4.06599 2.05223, 4.06941 2.05293, 4.07307 2.05338, 4.07696 2.05357, 4.08107 2.05351, 4.08541 2.05319, 4.08996] casacore-3.7.1/meas/MeasUDF/test/tmeas.run000077500000000000000000000016151476623553700203210ustar00rootroot00000000000000#!/bin/sh export LD_LIBRARY_PATH=../..:${LD_LIBRARY_PATH} export DYLD_LIBRARY_PATH=../..:${DYLD_LIBRARY_PATH} $casa_checktool ./tmeas echo # Test various commands $casa_checktool ../../../tables/apps/taql 'meas.wgs("WSRT")' $casa_checktool ../../../tables/apps/taql 'str(meas.dir("ITRF", "SUN", 10Aug2010/13:12:11, "WSRT"))' $casa_checktool ../../../tables/apps/taql 'str(meas.dircos("ITRF", "SUN", 10Aug2010/13:12:11, "WSRT"))' $casa_checktool ../../../tables/apps/taql 'str(meas.riseset("SUN", 10Aug2010/13:12:11, "WSRT"))' # Check if all dates are correct. $casa_checktool ../../../tables/apps/taql "any(array(date(meas.riseset(['SUN'],1jan10+[0:366],[5d0m, 52d0m])),[366,2]) != transpose(array(date(1jan2010+[0:366]),2,366)))" # Show the rise/set times for the year. $casa_checktool ../../../tables/apps/taql "transpose(array(time(meas.riseset(['SUN'],1jan10+[0:366],[4d53m22, 52d22m26])), [366,2]))" casacore-3.7.1/meas/meas.dox000066400000000000000000000027631476623553700157220ustar00rootroot00000000000000//# meas.dox: doxygen description of meas package //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: meas.dox 20088 2007-06-05 03:57:23Z Malte.Marquarding $ // \defgroup meas meas package (libcasa_meas) // // The meas package contains TaQL functions for measures //
          //
        • MeasUDF: // special TaQL functions dealing with measures //
        casacore-3.7.1/measures/000077500000000000000000000000001476623553700151505ustar00rootroot00000000000000casacore-3.7.1/measures/CMakeLists.txt000066400000000000000000000104411476623553700177100ustar00rootroot00000000000000# # CASA measures # # Check whether DATA_DIR contains data (only if measures is build) get_filename_component(ABS_DATA_DIR "${DATA_DIR}" ABSOLUTE) if (NOT EXISTS "${ABS_DATA_DIR}/ephemerides/") message(WARNING "No ephemerides data found in the specified DATA_DIR (\"${DATA_DIR}\"), so the location must be specified at runtime. If you have preinstalled casacore data, specify the location with -DDATA_DIR.") endif() # If given, add the data directory as compile variable. # If it contains a single $, it needs to be doubled for make purposes. if (DATA_DIR) string(REGEX REPLACE [$] $$ DATA_DIRX ${DATA_DIR}) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DCASADATA='\"${DATA_DIRX}\"'") endif (DATA_DIR) add_library (casa_measures Measures/Aberration.cc Measures/EarthField.cc Measures/EarthMagneticMachine.cc Measures/MBaseline.cc Measures/MCBase.cc Measures/MCBaseline.cc Measures/MCDirection.cc Measures/MCDoppler.cc Measures/MCEarthMagnetic.cc Measures/MCEpoch.cc Measures/MCFrame.cc Measures/MCFrequency.cc Measures/MConvertBase.cc Measures/MCPosition.cc Measures/MCRadialVelocity.cc Measures/MCuvw.cc Measures/MDirection.cc Measures/MDoppler.cc Measures/MEarthMagnetic.cc Measures/MeasComet.cc Measures/MeasData.cc Measures/MeasFrame.cc Measures/MeasIERS.cc Measures/MeasJPL.cc Measures/MeasMath.cc Measures/MeasTable.cc Measures/MeasTableMul.cc Measures/Measure.cc Measures/MeasureHolder.cc Measures/MeasuresProxy.cc Measures/MEpoch.cc Measures/MFrequency.cc Measures/MPosition.cc Measures/MRadialVelocity.cc Measures/MRBase.cc Measures/Muvw.cc Measures/Nutation.cc Measures/ParAngleMachine.cc Measures/Precession.cc Measures/Quality.cc Measures/SolarPos.cc Measures/Stokes.cc Measures/UVWMachine.cc Measures/VelocityMachine.cc TableMeasures/TableMeasColumn.cc TableMeasures/TableMeasDescBase.cc TableMeasures/TableMeasOffsetDesc.cc TableMeasures/TableMeasRefDesc.cc TableMeasures/TableMeasType.cc TableMeasures/TableMeasValueDesc.cc TableMeasures/TableQuantumDesc.cc ) set (top_level_headers Measures.h TableMeasures.h ) init_pch_support(casa_measures ${top_level_headers}) target_link_libraries (casa_measures casa_tables casa_scimath ${CASACORE_ARCH_LIBS}) add_subdirectory (apps) install ( TARGETS casa_measures LIBRARY DESTINATION lib${LIB_SUFFIX} ARCHIVE DESTINATION lib${LIB_SUFFIX} LIBRARY PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) install (FILES Measures/Aberration.h Measures/EarthField.h Measures/EarthMagneticMachine.h Measures/MBaseline.h Measures/MCBase.h Measures/MCBaseline.h Measures/MCDirection.h Measures/MCDoppler.h Measures/MCEarthMagnetic.h Measures/MCEpoch.h Measures/MCFrame.h Measures/MCFrequency.h Measures/MConvertBase.h Measures/MCPosition.h Measures/MCRadialVelocity.h Measures/MCuvw.h Measures/MDirection.h Measures/MDoppler.h Measures/MEarthMagnetic.h Measures/MeasBase.h Measures/MeasBase.tcc Measures/MeasComet.h Measures/MeasConvert.h Measures/MeasConvert.tcc Measures/MeasData.h Measures/MeasFrame.h Measures/MeasIERS.h Measures/MeasJPL.h Measures/MeasMath.h Measures/MeasRef.h Measures/MeasRef.tcc Measures/MeasTable.h Measures/MeasTableMul.h Measures/Measure.h Measures/MeasureHolder.h Measures/MeasuresProxy.h Measures/MEpoch.h Measures/MFrequency.h Measures/MPosition.h Measures/MRadialVelocity.h Measures/MRBase.h Measures/Muvw.h Measures/Nutation.h Measures/ParAngleMachine.h Measures/Precession.h Measures/Quality.h Measures/SolarPos.h Measures/Stokes.h Measures/UVWMachine.h Measures/VelocityMachine.h DESTINATION include/casacore/measures/Measures ) install (FILES TableMeasures/ArrayMeasColumn.h TableMeasures/ArrayMeasColumn.tcc TableMeasures/ArrayQuantColumn.h TableMeasures/ArrayQuantColumn.tcc TableMeasures/ScalarMeasColumn.h TableMeasures/ScalarMeasColumn.tcc TableMeasures/ScalarQuantColumn.h TableMeasures/ScalarQuantColumn.tcc TableMeasures/TableMeasColumn.h TableMeasures/TableMeasDescBase.h TableMeasures/TableMeasDesc.h TableMeasures/TableMeasDesc.tcc TableMeasures/TableMeasOffsetDesc.h TableMeasures/TableMeasRefDesc.h TableMeasures/TableMeasType.h TableMeasures/TableMeasValueDesc.h TableMeasures/TableQuantumDesc.h DESTINATION include/casacore/measures/TableMeasures ) install (FILES ${top_level_headers} DESTINATION include/casacore/measures ) add_subdirectory (Measures/test ${EXCL_ALL}) add_subdirectory (TableMeasures/test ${EXCL_ALL}) casacore-3.7.1/measures/Measures.h000066400000000000000000001157461476623553700171230ustar00rootroot00000000000000//# Measures.h: a module for coordinates //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MEASURES_H #define MEASURES_MEASURES_H //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // // a module for coordinates // // // //
      • Quanta module for units and quantities. // // // The name Measure derives from physical measurements, i.e. values with // units and possibly a reference frame attached. // // // // The Measure model deals with measures (i.e. quantities with a // reference frame). // Measures are handled in the Measure section // (see Measure.h). // //

        Includes

        // Including the measures/Measures.h will take care of all // includes necessary for the handling of Units and Quantities, and the // general Measure interface. For the use of individual Measures, the // appropiate include files should be added. E.g. to be able to handle // Directions, the following includes could be given: // // #include // #include // // An inclusion of the appropiate measure file, will also take care of the // connected measure value (in this case MVDirection). However, // if only the value suffices, it can be included on its own (from the // Quanta directory).
        // When doing actual conversions (see MeasConvert later on), by using the // explicit Measure::Convert types, the description of the actual // conversions (called MCmeasure, e.g. MCEpoch.h) should be included as well; // in adition to general MeasConvert.h. // //

        Measures

        // // Measures are physical quantities within a certain reference frame. Examples // are the Hour-angle and Declination of a source at a certain time and // observatory; an Ra/Dec for a certain mean epoch; an apparent frequency at // a certain time given in eV; a local sidereal time at an observatory.
        // Measures can be converted from one reference frame to another (and this // possibility is its main reason for existence). A simple B1950-J2000 // coordinate conversion example: // // cout << // output // // the conversion of a B1950 direction // MDirection::Convert( MDirection( Quantity( 20, "deg"), // Quantity(-10, "deg"), // MDirection::Ref( MDirection::B1950)), // // to J2000 // MDirection::Ref( MDirection::J2000)) () // // where the constructor sets up a conversion // // engine, and the operator() converts // << endl; // // or converting an UTC to a local apparent sidereal time: // // // Set up the model for the input (default reference is UTC) // MEpoch model ( Quantity(0., "d")); // // Set up the frame with the observatory position // MPosition obs( MVPosition( Quantity( 10, "m"), // Quantity( -6, "deg"), // Quantity( 50, "deg")), // MPosition::Ref(MPosition::WGS84)); // Measframe frame( obs); // // Set up the output reference // MEpoch::Ref outref( MEpoch::LAST, // frame); // // Set up conversion // MEpoch::Convert toLST( model, // outref); // // Output a series of sidereal times (formatted in ddd::hh:mm:ss) // for (Double d = 12345; d<12346; d += 0.1) { // cout << "Converted from UTC to LAST: " << // d << " : " << // toLST(d).getValue() << endl; // }; // // // The examples show the use of the 5 major classes involved in Measures: // // Base Example Description // ------ --------- ------------- // Measure MEpoch has a value and a reference // MeasValue MVEpoch value // MeasRef MEpoch::Ref contains type, frame, offset // MeasFrame MeasFrame contains Measures describing frame // MeasConvert MEpoch::Convert contains conversion information and engine // // // Each type of Measure has its own distinct class. Each // is (weakly) derived from the Measure base // class, and its name starts with an M. Examples are: //
          //
        • MEpoch: an instance in time //
        • MDirection: a direction in space //
        • MPosition: a position on Earth //
        • MFrequency: the characteristics // of a wave //
        • MDoppler: a Doppler shift //
        • MRadialVelocity: a // radial velocity //
        • MBaseline: a baseline //
        • Muvw: a uvw value //
        • MEarthMagnetic: an // earth magnetic field value //
        // Others are being, or could be, considered. // The current set can be deduced from the class list at the end of // the html version of this module description. //

        // The main role of the Measure (and related) classes is to be able to convert // an observed (or to be calculated) physical entity from one reference frame // to another, e.g. a J2000 coordinate to galactic coordinates, or an TAI // time to a local sidereal time (LAST). // Simple unit conversions (e.g. an angle from mrad to deg), or calculations // with values with attached units, are sufficiently catered for by the // Quanta module classes. //

        // Each measure has a value (MeasValue) and // a reference (MeasRef). // The values are in general measure specific, weakly derived from MeasValue, // and named with an initial MV. Examples are: //

          //
        • MVEpoch (a high precision single value), //
        • MVDirection (direction cosines), //
        • MVPosition (3-vector positions), //
        • MVFrequency (single, unit depended // value). //
        • MVDoppler (single, unit depended value) //
        • MVRadialVelocity (single value) //
        // MeasValue and the MV classes can be found in the // Quanta module. // In addition some other value classes, not directly used in measures, are // available. Examples: //
          //
        • MVAngle (to normalise // and have specific I/O formatting for angle-like values) //
        • MVTime (same for time-like values) //
        // References are measure specific. Each specific reference class is // called Measure\::Ref (e.g. MEpoch::Ref). It specifies // the full reference frame of the specific measure, i.e. its type, an optional // frame of measures (a MeasFrame, consisting of say a time and position), and // an optional offset. // It has at least a reference code // (e.g. MDirection::B1950, MEpoch::LAST), with defaults for each measure // (i.e. MDirection::J2000, MEpoch::UTC) if none specified.
        // In addition the reference can contain a reference frame // (MeasFrame) to specify from when and/or // where the measure was obtained or calculated.
        // A third optional element of the reference is an offset measure, which // indicates the offset (e.g. a sidereal date) that has to be added to the // value referenced before it is used.
        // Examples of some measures are: // // // An instance of time expressed in days (MJD) in UTC // MEpoch date(MVEpoch(Quantity(50237.29, "d")), // MEpoch::Ref(MEpoch::UTC)); // // which could also be expressed as: // MEpoch date(Quantity(50237.29, "d"), // MEpoch::UTC); // // or using the default reference type: // MEpoch date(Quantity(50237.29, "d")); // // or as a time with an offset to a specific date: // MEpoch date(Quantity(12.3, "h"), // time // MEpoch::Ref(MEpoch::UTC, // reference with // MEpoch(Quantity(50237, "d")))); // offset // // A position of a telescope // MPosition pos(MVPosition(Quantity(25, "m"), // height // Quantity(20, "deg"), // East longitude // Quantity(53, "deg")), // lattitude // MPosition::WGS84); // reference type // // Use this position in a frame // MeasFrame frame(pos); // // Specify an LAST (in MGSD) observed at this position: // MEpoch last(Quantity(51000.234, "d"), // time and date // MEpoch::Ref(MEpoch::LAST, // indicate LAST // frame)); // and where observed // // Maybe we know the MJD of the observed sidereal time, // // but not its sidereal date. We could then specify it as an // // offset to the beginning of the sidereal day in progress at // // specified UTC // MEpoch last(Quantity(13.45, "h"), // time // MEpoch::Ref(MEpoch::LAST, // indicate LAST // frame, // where observed // MEpoch(51234, // MJD of today // MEpoch::Ref(MEpoch::TAI + MEpoch::RAZE))); // // where the RAZE indicates that the value will be truncated after // // conversion. In this case it will be converted to LAST to be able // // to add it as an offset to the specified LAST // // // // A direction (in RA/Dec) could be: // MDirection coord(MVDirection(Quantity(54, "deg"), // RA // Quantity(2034, "'")), // DEC arcmin // MDirection::Ref(MDirection::J2000)); // J2000 type // // If it were apparent coordinates, the time when observed should // // have been known. We could just add it to the frame defined above, // // and use it: // frame.set(date); // add time to frame // MDirection acoord(MVDirection(Quantity(54, "deg"), // RA // Quantity(2034, "'")), // DEC // MDirection::Ref(MDirection::APP, // apparent type // frame)); // and when // // If it was given in HA/Dec, the position should have been known // // as well, but it is already in the frame, hence we could say: // MDirection acoord(MVDirection(Quantity(54, "deg"), // HA // Quantity(2034, "'")), // DEC // MDirection::Ref(MDirection::HADEC, // type // frame)); // when/where // // In the above examples in general explicit MV // values have been used to specified the measure's value. In many // cases (depending on the actual measure) it can be omitted, and the data // can be given directly to the measure constructor. See the // constructors for the individual measures for details.
        // If the reference is simple (i.e. no frame and/or offset) the // Measure::Ref can be omitted, and only the code has to be // specified.
        // A MeasFrame is a container for specifying // Measures needed to describe the circumstances under which the measure was // observed (or for which it has to be calculated). // E.g. the position on Earth (an MPosition) is necessary for // sidereal time and coordinates like HA/Dec and Az/El; the time // (MEpoch) // is necessary for non-standard coordinates (apparent, mean, HA/Dec etc); // the coordinates (MDirection) for radial velocities; etc.
        // Although quite often the value has to be in a specific format (e.g. TBD for // precession calculations; astronomical longitude for the LAST), the // frame values can be given in any known reference format: conversion to the // appropiate type will be done automatically if and when necessary.
        // Frames (and references) are never copied, but act always as containers // with shallow copying only (i.e. copied frames will point to // identical instances, and changes made in one copy will be visible in all // others. This // means, e.g., that in the following: // // MeasFrame frame1(MEpoch(50236.12)); // MeasFrame frame2(frame1); // // the two frames will be identical, and a change to one means a change to // the other. Furthermore, only the information needed for a specific // calculation will be used (and calculated). This means that one frame can // be used specifying all of e.g. the position (which will probably stay the // same for a series of calculations) and time; with the time being set() // (if also the reference of the epoch changes) or resetEpoch() (if only // the value changes, but the reference and its frame stay the same). // A change in the frame will influence automatically any calculation (e.g. // conversion to LAST) of which it is part.
        // // The value of a measure (in MV format) can be obtained with the // getValue() member function. The value in a variety of formats // and units can be obtained with a (specific Measure dependent) series of // get() members of both the MV-value and the Measure.
        // // Measures in themselves are not really necessary for proper data reduction // and the like. Its real value is the ability to transform a Measure from // one reference type (and frame, offset) to another.
        // Conversion of a measure of a certain kind from one reference to another // is done with the aid of special, measure specific, // MeasConvert classes. Each conversion // class is called Measure\::Convert (e.g. MDirection::Convert). // A conversion generates from an input reference (or an input measure) and // an output reference a conversion functional, that can be used to convert // specific values.
        // Example: // // cout << // output // // the conversion of a B1950 direction // MDirection::Convert( MDirection( Quantity( 20, "deg"), // Quantity(-10, "deg"), // MDirection::Ref( MDirection::B1950)), // // to J2000 // MDirection::Ref( MDirection::J2000)) () // // where the constructor sets up a conversion // // engine, and the operator() converts // << endl; // // The same could have been done by only setting up the conversion engine, and // not specifing the default value to be converted in the Convert constructor // by: // // cout << // output // // the conversion of a B1950 direction // MDirection::Convert(MDirection::Ref( MDirection::B1950), // // to J2000 // MDirection::Ref( MDirection::J2000)) // // and use conversion on value // (MVDirection( Quantity( 20, "deg"), // Quantity(-10, "deg"))) // // where the operator() converts // << endl; // // Specifying the conversion engine separately, it can be re-used for other // values: // // MDirection::Convert conv(MDirection::Ref( MDirection::B1950), // MDirection::Ref( MDirection::J2000)); // // We have some coordinates from somewhere, say coord(0:N-1): // for (Int i=0; i // A larger example. Say you have the J2000 coordinates for a source (RA=11 // deg, DEC= -30 deg), and you want to observe it on May 17, 1996 (MJD=50220) // at 8:18 UTC in a place // with a Longitude of 150 deg (latitude of 20 deg) at 1000 m high, // you could get the // apparent RA,DEC, and the LAST at that time (you could also go straight to // HA/DEC or so) with (I write the example longer than necessary to indicate // the steps, and with explicit reference to MV values): // // // The observatory position. Note that the reference is geodetic position // MPosition myobs(MVPosition ( Quantity(1, "km") , // Quantity(150, "deg"), // Quantity(20, "deg")), // MPosition::WGS84); // // The time I want to observe (note that it could be specified in many // // other ways) // MEpoch obstime(MVEpoch(MVTime(1996, 5, 17, (8+18./60.)/24.)), // MEpoch::UTC); // // The frame specification for when and where to observe // MeasFrame frame(myobs, obstime); // // The reference for a sidereal time (note the frame could be empty and // // filled at the actual conversion time) // MEpoch::Ref sidref( MEpoch::LAST, frame); // // The reference for apparent coordinates: // MDirection::Ref appref( MDirection::APP, frame); // // The conversion engine for my time to LAST // MEpoch::Convert tosid(obstime, sidref); // // The conversion to sidereal time of obstime // MEpoch sidtime = tosid(); // // Conversion of UTC 10.8 h // sidtime = tosid(MVEpoch(MVTime(1996, 5, 17, 10.8/24.))); // // Show me some time // cout << "LAST for UTC = 11:00: " << // tosid(MVEpoch( MVTime( 1996, 5, 17, 11, 0))) << endl; // // An offset reference (note the RAZE will keep only the integer part of // // the day for the conversion result) // MEpoch::Ref offtime(obstime.getValue(), MEpoch::UTC+MEpoch::RAZE); // // The reference for a sidereal with respect to a specified offset (note // // that it is automatically calculated into correct units) // MEpoch::Ref sidoffref(MEpoch::LAST, frame, offtime); // // Show the offset result // cout << "LAST today: " << // MEpoch::Convert(11., sidoffref)() << endl; // // Coordinate conversion from J2000 // cout << "Apparent coordinates: " << // MDirection::Convert ( MDirection(Quantum(11,"deg"), // Quantum(-30, "deg")), // MDirection::Ref( MDirection::APP, // frame))() << endl; // // Handier to have the conversion engine available // MDirection::Convert cvt( MDirection(Quantum(11,"deg"), // Quantum(-30, "deg")), // MDirection::Ref( MDirection::APP, // frame)); // // Set another frame time (note it is now sidereal, not UTC. The // // frame will automatically convert it (using the frame again for // // position) to TDB for precession etc calculations). // frame.set(sidtime); // // And look what same position is at this new time // cout << "Next position: " << cvt() << endl; // //

        // Some conversions need maybe some fine tuning (e.g. what is the acceptable // interval for Nutation linear interpolation: could be different from the // default interval; some time calculations will want to use the predicted // IERS values rather than the actual determined; some Nutation will maybe // use the IERS updates, some maybe the JPL DE databases).
        // The AipsrcValue class can be used to // specify very specific parameters that are used to steer // the conversion process beyond what is possible with just a list // of measure reference types (that list is already long for some cases). // Values, switches can be set() (and removed) to change the // default behaviour of the conversions. In general the user will only need // to use the details in very specific cases. The details that can be used // are described in the classes that provide calculations (e.g. // Nutation), and in the aipsrc-data reference // manual entry.
        //

        // Some details about the different classes follows. In the examples often // a specific measure value (e.g. MVEpoch, the MeasValue for MEpoch), or a // specific measure (e.g. MDirection, a direction in space) is used. This // is only to visualise the use, any other measure could have been used. //

        //

        MeasValue

        // The MeasValue class derivatives are all named MVmeasure, e.g. // MVFrequency, and represent the internal representation of the // specific measure class. Details // can be found in the Quanta module. //

        //

        Measure

        // The Measure class derivatives are all called MMeasure. // MDirection (a celestial direction), // MPosition (a position on Earth), // MFrequency (characteristics of // electro-magnetic wave), // MEpoch (an instance in time), // MDoppler, // MRadialVelocity // MBaseline, // Muvw, // MEarthMagnetic, //.
        // A measure has a value (kept in internal units in MVmeasure // format) and a definition // of the reference frame (MeasRef) of the value. The reference is optional, and // will default to Measure::DEFAULT.
        // All measures have a set of standard constructors: // // M(); // some default, e.g. pole directoon, time ==0) // M(MV, MeasRef); // M(Quantity, MeasRef); // M(Quantum >, MeasRef); // M(Vector, MeasRef); // // But also some special ones (e.g. two Quantities for MDirection to specify // two angles) depending on type. The MeasRef can be omitted (will then be // defaulted to Measure::DEFAULT, e.g. MEpoch::DEFAULT); can be specified as // a full reference as a Measure::Ref (e.g. MDirection::Ref) // type; or as a simple reference as Measure::TYPE (e.g. // MDirection::J2000).
        // The individual elements of a Measure (i.e the MV value and the reference) // can be overwritten (or set) with the set() methods.
        // get() methods (in general get(unit) // to return the internal value in some // specified unit as a Quantum; and methods like getAngle() // for e.g. MDirection) // enable the user to obtain the value of the measure.
        // A String tellMe() will tell the type of Measure; a // void assured(String) and Bool areYou(String) will // check the type; while a String showType(Measure::TYPE) will // return the string value of a reference type code (e.g. J2000).
        //

        // Recall that a Measure is a value with a reference specified. The MeasConvert // engines enable you to convert it into another Measure, with a different // reference (e.g. from J2000 to AZEL). The different get() methods (either // directly, or indirectly using additional MV get() functions, or // Quantum conversion methods, can convert the internal value into a value // (or values) with user preferred units.
        // For reasons of speed (and safety) the allowed reference types for each // Measure are enumerated in each measure class. The different reference // types for MDirection are, for example: // // MDirection::J2000, // MDirection::JMEAN, // MDirection::JTRUE, // MDirection::APP, // MDirection::B1950, // MDirection::BMEAN, // MDirection::BTRUE, // MDirection::GALACTIC, // MDirection::HADEC, // MDirection::AZEL, // MDirection::DEFAULT = MDirection::J2000 // // The MEpoch has a special reference type (MEpoch::RAZE) that // can only be used // in conjuncion with another reference type // (e.g. MEpoch::UT1+MEpoch::RAZE). // The meaning is: if a measure with such a reference type is converted to // another reference type (say MEpoch::LAST) the // resultant (sidereal time) // instance will be razed to an integer number of days; hence providing // an easy way to specify sidereal times offset with the beginning of the // current sidereal day.
        // To aid with external data, a Bool giveMe(String, uInt) will // give the correct reference type to be used given the String type. // Note that the // uInt, rather than the corresponding enum is used, due to templating // restrictions in some compilers.
        // The correct reference (MeasRef) and conversion (MeasConvert) class for // each Measure (a frequency cannot be converted into an epoch) are templated, // and have specified (and to be used) typedefs: Measure::Ref and // Measure::Convert (e.g. MEpoch::Ref, MEpoch::Convert). In // addition, Measure::MVType and Measure::MCType are defined for all // measures. //

        //

        Measure errors

        // In the current implementation, no errors are attached to a Measure. In the // original design errors were foreseen, but up till now they have been left // out.
        // The addition of errors is in principle an easy process. They could be // attached to either a Measure (as an additial MV value), or the MV's could // be expanded to include errors (my preferred option at the moment). An // MV being converted will then automatically have its error converted as // well.
        // Before implementing, however, I think it would be worthwhile to look at // the whole area of error handling. The easiest way would be to introduce // for each of the defined Casacore standard values a corresponding E class // (EDouble, EInt, EComplex, EuInt etc), and have all mathematical and // logical operators that are defined for the standard classes be defined // for the E-classes as well. It would then be easy to introduce errors // everywhere. //

        //

        MeasFrame

        // A MeasFrame is a container with the instance of time // (an MEpoch) and/or the position (an MPosition) for a measure reference. // (Other Measures, like MDirection and MRadialVelocity are sometimes needed // as well). // MeasFrames are never actually copied, but only referred to (shallow copy) // , so they can be used for all different types // of measure reference. They are only necessary, but then essential, if the // reference type does not fully specify the frame (like e.g. MDirection::J2000, // or MEpoch::TAI do). Examples are the position necessary to go to // MEpoch::LAST, the epoch necessary to go to MDirection::APP, the epoch and // position necessary to reference an MDirection::AZEL.
        // A MeasFrame can be constructed empty (and used in references, as long as it // is filled properly at the time of an actual conversion), or with one or // Measures already defined with: MeasFrame frame(a_Measure, ...). // It can be filled, or re-filled, with set(a_measure,....).
        // The conversion routines use different values of the frame values given (e.g. // the precession and nutation will need the epoch in TDB time, the hour-angle // constructor local apparent sidereal time, which needs the astronomical // longitude etc.). For that reason the specification of an epoch or position // in either the constructor or the set() will create conversion engines for // conversion of the input measure to all appropiate values that can be asked // by the conversion routines. Note that the actual conversion is only done // when that value is requested (and is then saved for later use). It is, // therefore, safe and probably good practice to have one frame in a certain // conversion environment, filled with as much info as is needed at that stage.
        // To aid and speed up, resetEpoch() and resetPosition() // methods are available. As arguments they accept the corresponding // MV or a variety of Double and Quantum arguments to reset the value // of the corresponding frame measure only. In that case the conversion engine // won't be redesigned, leading to fast recalculation when necessary, since // e.g. nutation values could be re-used.
        // In an observing environment you could hence setup a proper frame with the // Observatory position, and an observing day offset (see MeasRef) time; and // do resetEpoch() to update the time if and when necessary.
        //

        //

        MeasRef

        // A MeasRef is a measure specific container (and its class reference is // Measure::Ref, e.g. MFrequency::Ref) with the // measure reference type (e.g. MEpoch::UTC), an optional (but in // some cases necessary) MeasFrame (e.g. to specify where the sidereal time // was determined), and, just for convenience, an optional offset (e.g. // the MJD for which the time specified in the MEpoch referenced is valid). // Note that if no frame or offset is necessary, the Measure::TYPE // can be used everywhere where a Measure::Ref is needed.
        // A MeasRef is never copied (all copying and so is done by referencing). This // means, for example, that if a specific MeasRef is part of the MEpoch // definition for an epoch that is part of a MeasFrame, and you chnage that // MeasRef, the change will automatically occur wherever that MeasRef is // used (as e.g. in the frame). In most cases that is the expected response, // but you should be aware of it, and not re-use a MeasRef for a completely // different purpose.
        // A simple example: // // MEpoch mytime(MVEpoch(50236.5), MEpoch::UTC); // // this will define a time in UTC on MJD 50236, 12 hours. The MVEpoch // // explicit conversion could be left out for most compilers, but some // // have trouble with automatic conversions. // // Another way of doing it would be to use Quantities, which have // // explicit constructors for all measures: // MEpoch mytime(Quantity(50236.5, "d")); // // A slighty more involved example, written out a bit: // // // Specify the location of the observatory (10m high, at given longitude // // and latitude as geodetic position) // MPosition obs( MVPosition( Quantity( 10, "m"), // Quantity( -6, "deg"), // Quantity( 52, "deg")), // MPosition::WGS84); // // If the current time is MJD50236, 12.3 h UTC, it could be specified as: // MEpoch tim( MVEpoch( Quantity( 50236, "d"), // Quantity( 12.3, "h"))); // // Note the default reference // // For this example we will also specify it as: // MEpoch offtim(tim); // offtim.set(MEpoch::DEFAULT+MEpoch::RAZE); // // These two could define a frame // MeasFrame frame(tim, obs); // // Or maybe as (since observatory will stay put) // MeasFrame frame1(obs); // // and later addition of some time and its reference frame // frame1.set(tim); // // with a change to another time value at a later stage with // frame1.resetEpoch( MVEpoch( Quantity( 50236, "d"), // Quantity( 13, "h"))); // // At this time we observe a sidereal time of 2.3 h. The actual instance // // of time needs a sidereal date to specify, but we are too lazy to // // look it up, hence we specify that this time has an offset, equal to // // the sidereal time at offtim (which with the RAZE addition will be // // converted to an integral number of days in whatever time it is // // converted to) // MEpoch mylast( MVEpoch( Quantity( 2.3, "h")), // MEpoch::Ref( MEpoch::LAST, // frame, // offtim)); // // Which specifies that we have a Local apparent sidereal time of 2.3 h // // at the position specified by obs in the frame, at an offset offtim. // // Note that the offset is given in UTC (and RAZE). Any conversion of // // this mylast value to any other reference type, will always auto start // // with a conversion of the offset to the current type (i.e LAST (with // // the RAZE taking the integer part only)), and adding it to the value // // given. Note that if an output reference has an offset, the resulting // // value will be corrected for the specified offset as well. // // The reference type can be set with a set() function, and set() functions // for the offset and frame will be present as well.
        // A Bool empty() checks if the reference is empty; get() // functions provide the information in the reference; and a // String showMe() will return the type of measure (e.g. "Epoch") the // MeasRef can be used for. //

        //

        MeasConvert

        // The MeasConvert class converts Measures from one reference type and frame // to another. // It gathers all relevant // information and analyses it to have fast multiple conversions. // The MeasConvert classes are Measure specific, and should be used with // the class names Measure::Convert (e.g. MFrequency::Convert // ). // The () operator will do the actual conversion; constructors and set() // methods will only fill the information necessary to do the conversion. // MeasConvert is a non-copying container.
        // To set up the conversion engine, the MeasConvert object has to know the // input data reference (remember the MeasRef contains information about the // type, the possible reference frame and a possible offset), and an output // reference. Using these references it will communicate with the appropiate // Measure class to set up a series of routines that have to be executed in // order to attain the goal. (Note that if the input and output reference // both define a frame, but different ones, e.g. because you want to convert // a sidereal time at one place to a sidereal time at another place, the // conversion machinery will always first go to the proper default (UTC in this // case), and then go to the goal).
        // The actual conversion need a value to be converted, and it also can use // a default Unit, so that if your frequencies are in nm, you can once // specify that they are nm, and then simply convert a Double.
        // This means that the optimal constructor for a MeasConvert is: // // // The first argument will give the input reference, and, if a Quantum is // // used to make the Measure, the default units for inputs to the conversion. // // It acts as a 'model' for subsequent input to be converted. // // () operator // Measure::Convert( Measure(Quantum), // // the second argument gives the output reference // Measure::Ref); // // The actual constructors present include ones with the first argument only // an input reference, rather than a full Measure. // However, in all cases an empty or partial one can be constructed, with set() // functions filling in the rest. The conversion engine is only // (re-)setup if at least an input and output reference can be found.
        // After setting up the conversion engine, the () operator can be used with // a variety of values to return a converted Measure. Possibilities are: // // () // convert the value as specified in the 'model' // (Double) // convert the value first to appropiate units (if they // // were implicit in 'model' or explicitly set), and // // then convert // (Vector)// as Double // (Quantity) // convert the full value, including its own units // (Quantum >) // as Quantity // (MeasValue) // convert the specified appropiate MV // (Measure) // set up a new conversion chain, using the value as // // 'model', and the old output reference, // // and then convert // (Measure, Measure::Ref) // set up a new conversion chain for the // // 'model' given and the output reference given // (Measure::Ref) // set up a new conversion chain using the old 'model' // // and the output reference given, and convert the // // existing model value // // A simple example to output the J2000 coordinates for a B1950 input (RA=20 deg, // DEC=-10 deg): // // cout << // MDirection::Convert( MDirection( Quantity( 20, "deg") // Quantity(-10, "deg"), // MDirection::Ref( MDirection::B1950)), // MDirection::Ref( MDirection::J2000)) () << endl; // // In this example everything is done in one go (the () at the end does the // conversion). Another example, to have a UTC to LAST converter: // // // Set up the model for the input (default reference is UTC) // MEpoch model ( Quantity(0., "d")); // // Set up the frame with the observatory position // MPosition obs( MVPosition( Quantity( 10, "m"), // Quantity( -6, "deg"), // Quantity( 50, "deg")), // MPosition::Ref(MPosition::WGS84)); // Measframe frame( obs); // // set up the output reference // MEpoch::Ref outref( MEpoch::LAST, // frame); // // Set up conversion // MEpoch::Convert toLST( model, // outref); // // Output a series of sidereal times (formatted in ddd::hh:mm:ss) // for (Double d = 12345; d<12346; d += 0.1) { // cout << "Converted from UTC to LAST: " << // d << // toLST(d).getValue() << endl; // }; // //

        // For specific purposes it would be very easy to set up a series of simple // classes, that would do standard conversions. //

        //

        MeasData, MeasTable, MeasBase, other help classes

        // A series of help classes are present to aid in the conversion, especially // caching information. They are of no direct use for the end user (except // maybe a few constants in MeasData).
        // The classes are: //
          //
        • MeasBase: // base class (derived from Measure) for all real Measures //
        • MeasData: // all constants, polynomial factors, interface to IERS // database etc. which are not stored in Tables. (MeasTable looks after // these). Mn short it provides all the actual data values necessary // for the conversions (and the other help classes) //
        • MeasTable: // interface for all data that comes from Tables rather than // the program //
        • MeasIERS: // (static) class to converse with the IERS database(s) //
        • MeasJPL: // (static) class to converse with the JPL DE database(s) //
        • Precession: // all precession related calculations //
        • Nutation //
        • Aberration //
        • SolarPos: // all solarposition related calculations //
        • Euler: // representation of Euler rotation angles //
        • RotMatrix: a 3-D rotation matrix //
        //

        // // // // The Measures module originated to be able to convert ccordinates between // different reference frames. // // // //

      • inlining // // // // See the individual measures for appropiate examples. // // //# Dummy class definition for extractor //# class Measures {}; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/000077500000000000000000000000001476623553700167345ustar00rootroot00000000000000casacore-3.7.1/measures/Measures/Aberration.cc000066400000000000000000000213331476623553700213330ustar00rootroot00000000000000//# Aberration.cc: Aberration class //# Copyright (C) 1995,1996,1997,1998,1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constants const Double Aberration::INTV = 0.04; //# Static data uInt Aberration::interval_reg = 0; uInt Aberration::usejpl_reg = 0; //# Constructors Aberration::Aberration() : method(Aberration::STANDARD), lres(0) { fill(); } Aberration::Aberration(const Aberration &other) { copy(other); } Aberration::Aberration(AberrationTypes type) : method(type), lres(0) { fill(); } Aberration &Aberration::operator=(const Aberration &other) { if (this != &other) { copy(other); } return *this; } void Aberration::init() { method = Aberration::STANDARD; fill(); } void Aberration::init(AberrationTypes type) { method = type; fill(); } void Aberration::copy(const Aberration &other) { method = other.method; checkEpoch = other.checkEpoch; for (Int i=0; i<3; i++) { aval[i] = other.aval[i]; dval[i] = other.dval[i]; } for (Int j=0; j<4; j++) { result[j] = other.result[j]; } } //# Destructor Aberration::~Aberration() {} //# Operators // Calculate Aberration const MVPosition &Aberration::operator()(Double epoch) { calcAber(epoch); Double dt = epoch - checkEpoch; Double fac = 1; if (AipsrcValue::get(Aberration::usejpl_reg) && method != B1950) { fac /= MeasTable::Planetary(MeasTable::CAU); } lres++; lres %= 4; for (Int i=0; i<3; i++) { result[lres](i) = fac * (aval[i] + dt*dval[i]); } return result[lres]; } //# Member functions const MVPosition &Aberration::derivative(Double epoch) { calcAber(epoch); lres++; lres %= 4; Double fac = 1; if (AipsrcValue::get(Aberration::usejpl_reg) && method != B1950) { fac /= MeasTable::Planetary(MeasTable::CAU); } for (Int i=0; i<3; i++) { result[lres](i) = fac * dval[i]; } return result[lres]; } void Aberration::fill() { // Get the interpolation interval if (!Aberration::interval_reg) { interval_reg = AipsrcValue::registerRC(String("measures.aberration.d_interval"), Unit("d"), Unit("d"), Aberration::INTV); } if (!Aberration::usejpl_reg) { usejpl_reg = AipsrcValue::registerRC(String("measures.aberration.b_usejpl"), False); } checkEpoch = 1e30; } void Aberration::refresh() { checkEpoch = 1e30; } void Aberration::calcAber(Double t) { if (!nearAbs(t, checkEpoch, AipsrcValue::get(Aberration::interval_reg)) || (AipsrcValue::get(Aberration::usejpl_reg) && method != B1950) ) { checkEpoch = t; switch (method) { case B1950: // Yes, this really should be the time in Julian centuries since January // 0.5, 1900, not 1950. And MJDB1900 should probably be named MJD1900 // since it is 15019.5, indicating a Julian instead of a Besselian (= // tropical) date. t = (t - MeasData::MJDB1900)/MeasData::JDCEN; break; default: t = (t - MeasData::MJD2000)/MeasData::JDCEN; break; } Int i,j; Vector fa(13), dfa(13); for (i=0; i<3; i++) { aval[i] = dval[i] = Double(0); } Double dtmp, ddtmp, sdtmp, cdtmp; switch (method) { case B1950: { for (i=0; i<12; i++) { fa(i) = MeasTable::aber1950Arg(i)(t); dfa(i) = MeasTable::aber1950ArgDeriv(i)(t); } std::shared_ptr> mul = MeasTable::mulAber1950(t, 1e-6); DebugAssert (mul->contiguousStorage(), AipsError); const Double* mulAberV = mul->data(); for (i=0; i<132; i++) { const Double* mulAberArgV = MeasTable::mulAber1950Arg(i); dtmp = ddtmp = 0; for (j=0; j<12; j++) { dtmp += mulAberArgV[j] * fa(j); ddtmp += mulAberArgV[j] * dfa(j); } sdtmp = sin(dtmp); cdtmp = cos(dtmp); aval[0] += mulAberV[0] * sdtmp + mulAberV[1] * cdtmp; aval[1] += mulAberV[2] * sdtmp + mulAberV[3] * cdtmp; aval[2] += mulAberV[4] * sdtmp + mulAberV[5] * cdtmp; dval[0] += mulAberV[6] * sdtmp + mulAberV[7] * cdtmp + (mulAberV[0] * cdtmp - mulAberV[1] * sdtmp) * ddtmp; dval[1] += mulAberV[8] * sdtmp + mulAberV[9] * cdtmp + (mulAberV[2] * cdtmp - mulAberV[3] * sdtmp) * ddtmp; dval[2] += mulAberV[10] * sdtmp + mulAberV[11] * cdtmp + (mulAberV[4] * cdtmp - mulAberV[5] * sdtmp) * ddtmp; mulAberV += 12; } for (i=0; i<3; i++) { aval[i] /= C::c; dval[i] /= (C::c * MeasData::JDCEN); } } break; default: if (AipsrcValue::get(Aberration::usejpl_reg)) { Vector mypl = MeasTable::Planetary(MeasTable::EARTH, checkEpoch); for (i=0; i<3; i++) { aval[i] = mypl[i + 3]; dval[i] = 0; } } else { for (i=0; i<13; i++) { fa(i) = MeasTable::aberArg(i)(t); dfa(i) = MeasTable::aberArgDeriv(i)(t); } std::shared_ptr> mul = MeasTable::mulAber(t, 1e-6); DebugAssert (mul->contiguousStorage(), AipsError); const Double* mulAberV = mul->data(); for (i=0; i<80; i++) { const Double* mulAberArgV = MeasTable::mulAberArg(i); dtmp = ddtmp = 0; for (j=0; j<6; j++) { dtmp += mulAberArgV[j] * fa[j]; ddtmp += mulAberArgV[j] * dfa[j]; } sdtmp = sin(dtmp); cdtmp = cos(dtmp); aval[0] += mulAberV[0] * sdtmp + mulAberV[1] * cdtmp; aval[1] += mulAberV[2] * sdtmp + mulAberV[3] * cdtmp; aval[2] += mulAberV[4] * sdtmp + mulAberV[5] * cdtmp; dval[0] += mulAberV[6] * sdtmp + mulAberV[7] * cdtmp + (mulAberV[0] * cdtmp - mulAberV[1] * sdtmp) * ddtmp; dval[1] += mulAberV[8] * sdtmp + mulAberV[9] * cdtmp + (mulAberV[2] * cdtmp - mulAberV[3] * sdtmp) * ddtmp; dval[2] += mulAberV[10] * sdtmp + mulAberV[11] * cdtmp + (mulAberV[4] * cdtmp - mulAberV[5] * sdtmp) * ddtmp; mulAberV += 12; } for (i=0; i<17; i++) { const Double* mulAberArgV = MeasTable::mulAberSunArg(i); dtmp = ddtmp = 0; for (j=0; j<7; j++) { dtmp += mulAberArgV[j] * fa[j + 1]; ddtmp += mulAberArgV[j] * dfa[j + 1]; } sdtmp = sin(dtmp); cdtmp = cos(dtmp); const Vector& mulAberV = MeasTable::mulSunAber(i); aval[0] += mulAberV[0] * sdtmp + mulAberV[1] * cdtmp; aval[1] += mulAberV[2] * sdtmp + mulAberV[3] * cdtmp; aval[2] += mulAberV[4] * sdtmp + mulAberV[5] * cdtmp; dval[0] += (mulAberV[0] * cdtmp - mulAberV[1] * sdtmp) * ddtmp; dval[1] += (mulAberV[2] * cdtmp - mulAberV[3] * sdtmp) * ddtmp; dval[2] += (mulAberV[4] * cdtmp - mulAberV[5] * sdtmp) * ddtmp; } for (i=0; i<17; i++) { const Double* mulAberArgV = MeasTable::mulAberEarthArg(i); dtmp = ddtmp = 0; for (j=0; j<5; j++) { dtmp += mulAberArgV[j] * fa[j + 8]; ddtmp += mulAberArgV[j] * dfa[j + 8]; } sdtmp = sin(dtmp); cdtmp = cos(dtmp); const Vector& mulAberV = MeasTable::mulEarthAber(i); aval[0] += mulAberV[0] * sdtmp; aval[1] += mulAberV[1] * cdtmp; aval[2] += mulAberV[2] * cdtmp; dval[0] += mulAberV[0] * cdtmp * ddtmp; dval[1] += -mulAberV[1] * sdtmp * ddtmp; dval[2] += -mulAberV[2] * sdtmp * ddtmp; } for (i=0; i<3; i++) { aval[i] /= C::c; dval[i] /= (C::c * MeasData::JDCEN); } } break; } } } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/Aberration.h000066400000000000000000000130601476623553700211730ustar00rootroot00000000000000//# Aberration.h: Aberration class //# Copyright (C) 1995,1996,1997,1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_ABERRATION_H #define MEASURES_ABERRATION_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Aberration class and calculations // // // // // //
      • Measure class, // especially MEpoch //
      • MeasData class for constants // // // // Aberration // // // // Aberration forms the class for Aberration calculations. It is a simple // container with the selected method, and the mean epoch.
        // The method is selected from one of the following: //
          //
        • Aberration::STANDARD (at 1995/09/04 the IAU1980 definition) //
        • Aberration::NONE //
        • Aberration::B1950 //
        // Epochs can be specified as the MJD (with defined constants MeasData::MJD2000 // and MeasData::MJDB1950 or the actual MJD), // leading to the following constructors: //
          //
        • Aberration() default; assuming JD2000, IAU1980 //
        • Aberration(method) assuming the correct default epoch of // JD2000 or B1950 //
        • Aberration(method,epoch) with epoch Double(MJD). //
        // Actual Aberration for a certain Epoch is calculated by the () operator // as Aberration(epoch), with epoch Double MJD, values returned as an // MVPosition.
        // The derivative (d-1) can be obtained as well by // derivative(epoch).
        // The following details can be set with the // Aipsrc mechanism: //
          //
        • measures.aberration.d_interval: approximation interval as time // (fraction of days is default unit) over which linear approximation // is used //
        • measures.aberration.b_usejpl: use the JPL database values for IAU1980. // Else analytical expression, relative error about 10-9 // Note that the JPL database to be used can be set with // measures.jpl.ephemeris (at the moment of writing DE200 (default), // or DE405). If using the JPL database, the d_interval (and the // output of derivative()) are irrelevant. //
        //
        // // // // // // To calculate the Aberration angles. An alternate route could have been // a global function, but having a simple container allows // caching of some calculations for speed.
        // Using MJD (JD-2400000.5) rather than JD is for precision reasons. //
        // // // class Aberration { public: //# Constants // Interval to be used for linear approximation (in days) static const Double INTV; //# Enumerations // Types of known Aberration calculations (at 1995/09/04 STANDARD == IAU1980) enum AberrationTypes {STANDARD,NONE,B1950}; //# Constructors // Default constructor, generates default J2000 Aberration identification Aberration(); // Copy constructor Aberration(const Aberration &other); // Constructor with type Aberration(AberrationTypes type); // Copy assignment Aberration &operator=(const Aberration &other); //# Destructor ~Aberration(); //# Operators // Operator () calculates the Aberration direction cosine vector const MVPosition &operator()(Double epoch); //# General Member Functions // Return derivative of Aberration (d-1) w.r.t. time const MVPosition &derivative (Double epoch); // Re-initialise Aberration object // void init(); void init(AberrationTypes type); // // Refresh calculations void refresh(); private: //# Data menbers // Method to be used AberrationTypes method; // Check epoch for linear approximation Double checkEpoch; // Cached calculated angles Double aval[3]; // Cached derivatives Double dval[3]; // To be able to use referenced results in simple calculations, a circular // result buffer is used. // Current buffer pointer. Int lres; // Last calculation MVPosition result[4]; // Interpolation interval static uInt interval_reg; // JPL use static uInt usejpl_reg; //# Member functions // Copy void copy(const Aberration &other); // Fill an empty copy void fill(); // Calculate Aberration angles for time t void calcAber(Double t); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/EarthField.cc000066400000000000000000000167201476623553700212600ustar00rootroot00000000000000//# EarthField.cc: EarthField class model calculations //# Copyright (C) 1998-2000,2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constants const Double EarthField::INTV = 50000; //# Static data uInt EarthField::interval_reg_p = 0; //# Constructors EarthField::EarthField() : method_p(EarthField::STANDARD), fixedEpoch_p(MeasData::MJD2000), agh_p(0), p_p(0), q_p(0), cl_p(0), sl_p(0), lres_p(0) { fillField(); } EarthField::EarthField(const EarthField &other) { copy(other); } EarthField::EarthField(EarthFieldTypes model, Double catepoch) : method_p(model), fixedEpoch_p(catepoch), p_p(0), q_p(0), cl_p(0), sl_p(0), lres_p(0) { fillField(); } EarthField &EarthField::operator=(const EarthField &other) { if ( this != &other) copy(other); return *this; } void EarthField::init() { method_p = EarthField::STANDARD; fixedEpoch_p = MeasData::MJD2000; fillField(); } void EarthField::init(EarthFieldTypes model, Double catepoch) { method_p = model; fixedEpoch_p = catepoch; fillField(); } //# Destructor EarthField::~EarthField() {} //# Operators // Calculate EarthField components const Vector &EarthField::operator()(const MVPosition &pos) { calcField(pos); Vector dx((pos-checkPos_p).getValue()); lres_p++; lres_p %= 4; for (Int i=0; i<3; i++) { result_p[lres_p](i) = pval_p[i] + dx(0)*dval_p[0][i] + dx(1)*dval_p[1][i] + dx(2)*dval_p[2][i]; } return result_p[lres_p]; } //# Member functions const Vector *EarthField::derivative(const MVPosition &pos) { calcField(pos); lres_p=0; // Make sure contiguous set for (Int j=0; j<3; j++) { lres_p++; lres_p %= 4; for (Int i=0; i<3; i++) { result_p[lres_p](i) = dval_p[j][i]; } } return &result_p[1]; } void EarthField::copy(const EarthField &other) { method_p = other.method_p; fixedEpoch_p = other.fixedEpoch_p; agh_p = other.agh_p; checkPos_p = other.checkPos_p; for (Int i=0; i<3; i++) { pval_p[i] = other.pval_p[i]; for (Int k=0; k<3; k++) dval_p[i][k] = other.dval_p[i][k]; } for (Int j=0; j<4; j++) { result_p[j] = other.result_p[j]; } } void EarthField::fillField() { // Get the interpolation interval if (!EarthField::interval_reg_p) { interval_reg_p = AipsrcValue::registerRC(String("measures.earthfield.d_interval"), Unit("km"), Unit("m"), EarthField::INTV); } checkPos_p = MVPosition(1e30, 1e30, 1e30); switch (method_p) { default: agh_p.resize(0); agh_p = MeasTable::IGRF(fixedEpoch_p); p_p.resize(PQ_LEN); q_p.resize(PQ_LEN); cl_p.resize(2*PQ_LEN); sl_p.resize(2*PQ_LEN); break; } for (Int j=0; j<4; j++) { result_p[j].resize(3); for (Int k=0; k<3; ++k) result_p[j][k] = 0; } for (Int j=0; j<3; ++j) { pval_p[j] = 0; for (Int k=0; k<3; ++k) dval_p[j][k] = 0; } } void EarthField::refresh() { fillField(); } void EarthField::calcField(const MVPosition &pos) { if (!pos.nearAbs(checkPos_p, AipsrcValue::get(EarthField::interval_reg_p))) { checkPos_p = pos; Vector posmv(3); posmv = pos.getValue(); Vector posv(3); posv = pos.get(); switch (method_p) { case NONE: { for (uInt j=0; j<3; j++) { pval_p[j] =0; for (uInt i=0; i<3; i++) dval_p[j][i] =0; } } break; default: { Double slat, clat, slong, clong, x, y, z, ratio, rr(0), one, two, three; Int l, m, n, fn(0), fm, j, i; for (Int lp=0; lp<4; lp++) { slat = cos(M_PI_2 - posv(2)); clat = sin(M_PI_2 - posv(2)); slong = sin(posv(1)); clong = cos(posv(1)); cl_p(0) = clong; sl_p(0) = slong; x = 0.0; y = 0.0; z = 0.0; l = 0; m = 0; n = 0; ratio = 6371200/posv(0); // // Compute Schmidt quasi-normal coefficients P and X (=Q) // p_p(0) = 2.0 * slat; p_p(1) = 2.0 * clat; p_p(2) = 4.5 * slat * slat - 1.5; p_p(3) = 5.1961524 * clat * slat; q_p(0) = -clat; q_p(1) = slat; q_p(2) = -3.0 * clat * slat; q_p(3) = 1.7320508 * (slat * slat - clat * clat); for (Int k=0; k=0) { if (m+1-n == 0) { one = sqrt(1.0 - 0.5/fm); j = k - n - 1; p_p(k) = (1.0 + 1.0/fm) * one * clat * p_p(j); q_p(k) = one * (clat * q_p(j) + slat/fm * p_p(j)); sl_p(m) = sl_p(m-1) * cl_p(0) + cl_p(m-1) * sl_p(0); cl_p(m) = cl_p(m-1) * cl_p(0)-sl_p(m-1) * sl_p(0); } else { one = sqrt(Double(fn * fn - fm * fm)); two = sqrt((fn-1.0) * (fn-1.0) - fm * fm)/one; three = (2.0 * fn - 1.0)/one; i = k-n; j = k - 2 * n + 1; p_p(k) = (fn+1.0) * (three * slat/fn * p_p(i) - two/(fn-1.0) * p_p(j)); q_p(k) = three * (slat * q_p(i) - clat/fn * p_p(i)) - two * q_p(j); } } // // Synthesise X,Y,Z in geocentric coordinates // one = (agh_p(l)) * rr; if (m == -1) { x = x + one * q_p(k); z = z - one * p_p(k); l++; } else { two = (agh_p(l+1)) * rr; three = one * cl_p(m) + two * sl_p(m); x = x + three * q_p(k); z = z - three * p_p(k); if (clat > 0) { y = y + (one * sl_p(m) - two * cl_p(m)) * fm * p_p(k)/((fn+1.0) * clat); } else { y = y + (one * sl_p(m) - two * cl_p(m)) * q_p(k) * slat; } l += 2; } m++; } // calculation loop // Rotate from local vertical/meridian to ITRF one if (lp == 0) { pval_p[0] = +x*slat*clong + z*clat*clong + y*slong; pval_p[1] = -x*slat*slong + z*clat*slong - y*clong; pval_p[2] = -x*clat + z*slat; } else { dval_p[lp-1][0] = (+x*slat*clong + z*clat*clong + y*slong - pval_p[0])/DER_INTV; dval_p[lp-1][1] = (-x*slat*slong + z*clat*slong - y*clong - pval_p[1])/DER_INTV; dval_p[lp-1][2] = (-x*clat + z*slat - pval_p[2])/DER_INTV; } if (lp < 3) { if (lp != 0) posmv(lp-1) -= DER_INTV; posmv(lp) += DER_INTV; posv = MVPosition(posmv).get(); } } // derivative loop } break; } } } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/EarthField.h000066400000000000000000000164621476623553700211250ustar00rootroot00000000000000//# EarthField.h: EarthField class model claculations //# Copyright (C) 1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_EARTHFIELD_H #define MEASURES_EARTHFIELD_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations //# Constants // Length of P and Q arrays, half length of CL/SL arrays in IGRF model const Int PQ_LEN = 104; // Interval (m) for derivatives in IGRF model const Double DER_INTV = 10000; // EarthField class model calculations // // // // //
      • Measure class for use //
      • MeasTable class for data // // // // Earth magnetic Field model // // // // EarthField forms the class for Earth magnetic field calculations. It is a // simple container with the selected model, and the mean epoch.
        // The method is selected from one of the following: //
          //
        • EarthField::STANDARD (at 1998/05/18 the IGRF definition) //
        • EarthField::IGRF (IGRF reference field model) //
        // Epochs can be specified as the MJD (with defined constants // MeasData::MJD2000 and MeasData::MJD1950 or the actual MJD), // leading to the following constructors: //
          //
        • EarthField() default; assuming IGRF and MJD2000 //
        • EarthField(method); assuming J2000 as epoch //
        • EarthField(method, epoch) with epoch Double(MJD) //
        // Actual EarthField for a certain position on Earth is calculated by the () // operator. Arguments can be: //
          //
        • MVPosition: a position on Earth (in the ITRF frame) //
        // The returned value is a 3D vector of the field (in nT) in ITRF coordinates. // The derivative (d-1) can be obtained as well by // derivative(MVPosition).
        // An EarthField can be re-initialised with a different method and/or other // epoch with the init() functions (same format as constructors). // // To bypass the full, lengthy calculation actual returned values are calculated // using the derivative if within about 50 km (error less than about // 10-2 G). A call to refresh() will re-initiate calculations // from scratch.
        // The following details can be set with the // Aipsrc mechanism: //
          //
        • measures.earthfield.d_interval: approximation radius // (km is default unit) over which a linear approximation // is used //
        // The field model is assumed to be constant over the time-span the class // is used. // // The calculations are based on a routine provided by the IGRF community. See // ftp.ngdc.noaa.gov/Solid_Earth/Mainfld_Mag/Models/IAGA, routine IGRFLIB.FOR. // The values are in nT (10uG). //
        // // // // EarthField mine(EarthField::STANDARD, // 45837.0); // define EarthField type // // for 84/05/17 // MPosition pos; // MeasTable::Observatory(pos, "WSRT"); // Obervatory position // // Make sure correct position frame used // MVPosition x(MPosition::Convert(pos, MPosition::ITRF)().getValue()); // MVEarthMagnetic now = mine(x); // get EarthField // // // // // To have a container (with history) for field calculations // // // //
      • nothing I know off // class EarthField { public: //# Constants // Default interval to be used for linear approximation (in m) static const Double INTV; //# Enumerations // Known EarthField calculation models enum EarthFieldTypes { // Standard IGRF model IGRF, // Make the field equal to zero NONE, // Standard default model if none specified STANDARD = IGRF }; //# Constructors // Default constructor, generates default J2000 EarthField identification EarthField(); // Copy constructor EarthField(const EarthField &other); // Constructor with epoch in MJulian days (default is J2000) explicit EarthField(EarthFieldTypes model, Double catepoch=51544.5); // Copy assignment EarthField &operator=(const EarthField &other); //# Destructor ~EarthField(); //# Operators // Return the EarthField components. Note that the value returned has only // a lifetime as long as the EarthField container exists, and no new // derivative is asked for. const Vector &operator()(const MVPosition &pos); //# General Member Functions // Return derivatives of field (to X, Y, Z). Note that the value returned // has only a lifetime as long as the EarthField container exists, and // no new components or derivative is calculated. The returned value should // not be deleted. const Vector *derivative(const MVPosition &pos); // Re-initialise EarthField object with specified model and epoch, or // defaults STANDARD and J2000. // void init(); void init(EarthFieldTypes model, Double catepoch=51544.5); // // Refresh calculations void refresh(); private: //# Data members // Method to be used EarthFieldTypes method_p; // Fixed epoch to be used (MJD) Double fixedEpoch_p; // List of spherical components Vector agh_p; // Work arrays for calculations // Vector p_p; Vector q_p; Vector cl_p; Vector sl_p; // // Check position MVPosition checkPos_p; // Cached calculated field components Double pval_p[3]; // Cached derivatives Double dval_p[3][3]; // To reference results, and use a few in interim calculations, results are // calculated in a circular buffer. // Current result pointer Int lres_p; // Last calculation Vector result_p[4]; // Interpolation interval static uInt interval_reg_p; //# Member functions // Make a copy void copy(const EarthField &other); // Create correct default fixedEpoch and catalogue field data void fillField(); // Calculate EarthField for longitude and latitude and altitude (m) void calcField(const MVPosition &pos); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/EarthMagneticMachine.cc000066400000000000000000000233501476623553700232460ustar00rootroot00000000000000//# EarthMagneticMachine.cc: Calculates magnetic field in a direction //# Copyright (C) 1998,2000,2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors EarthMagneticMachine::EarthMagneticMachine() : fex_p(False), pex_p(False), fil_p(0), cumf_p(0), clx_p(False) { init(); } EarthMagneticMachine::EarthMagneticMachine(const MDirection::Ref &in, const Quantum &hgt, MeasFrame &frame) : fex_p(False), pex_p(False), fil_p(0), cumf_p(0), clx_p(False) { inref_p = in; inref_p.set(frame); hgt_p = hgt.getValue("m"); if (!frame.getITRF(pos_p)) { throw(AipsError("No position in frame for EarthMagneticMachine")); } if (!frame.getTDB(epo_p)) { throw(AipsError("No epoch in frame for EarthMagneticMachine")); } fil_p = 15; init(); } EarthMagneticMachine::EarthMagneticMachine(const MDirection::Ref &in, const Quantum &hgt, const MPosition &pos, const MEpoch &tm) : fex_p(False), pex_p(False), fil_p(0), cumf_p(0), clx_p(False) { inref_p = in; hgt_p = hgt.getValue("m"); pos_p = MPosition::Convert(pos, MPosition::ITRF)().getValue(); epo_p = MEpoch::Convert(tm, MEpoch::TDB)().getValue().get(); fil_p = 15; init(); } EarthMagneticMachine::EarthMagneticMachine(const MDirection::Ref &in, const MVDirection &dir, MeasFrame &frame) : fex_p(False), pex_p(False), fil_p(0), cumf_p(0), clx_p(False) { inref_p = in; inref_p.set(frame); rin_p = dir; if (!frame.getITRF(pos_p)) { throw(AipsError("No position in frame for EarthMagneticMachine")); } if (!frame.getTDB(epo_p)) { throw(AipsError("No epoch in frame for EarthMagneticMachine")); } fil_p = 29; init(); } EarthMagneticMachine::EarthMagneticMachine(const MDirection::Ref &in, const MVDirection &dir, const MPosition &pos, const MEpoch &tm) : fex_p(False), pex_p(False), fil_p(0), cumf_p(0), clx_p(False) { inref_p = in; rin_p = dir; pos_p = MPosition::Convert(pos, MPosition::ITRF)().getValue(); epo_p = MEpoch::Convert(tm, MEpoch::TDB)().getValue().get(); fil_p = 29; init(); } EarthMagneticMachine::EarthMagneticMachine(const EarthMagneticMachine &other) : fex_p(False), pex_p(False), fil_p(0), cumf_p(0), clx_p(False) { copy(other); reCalculate(); } EarthMagneticMachine &EarthMagneticMachine::operator=(const EarthMagneticMachine &other) { if (this != &other) { copy(other); reCalculate(); } return *this; } //# Destructor EarthMagneticMachine::~EarthMagneticMachine() {} //# Operators Double EarthMagneticMachine::operator()() { return getLOSField(); } Quantum EarthMagneticMachine::operator()(const Unit &un) { return getLOSField(un); } Double EarthMagneticMachine::operator()(const MVDirection &in) { return getLOSField(in); } Quantum EarthMagneticMachine::operator()(const MVDirection &in, const Unit &un) { return getLOSField(in, un); } Double EarthMagneticMachine::operator()(const Quantum &in) { return getLOSField(in); } Quantum EarthMagneticMachine::operator()(const Quantum &in, const Unit &un) { return getLOSField(in, un); } Double EarthMagneticMachine::operator()(const Double in) { return getLOSField(in); } Quantum EarthMagneticMachine::operator()(const Double in, const Unit &un) { return getLOSField(in, un); } //# Member functions void EarthMagneticMachine::reCalculate() { fil_p = cumf_p; init(); } void EarthMagneticMachine::set(const MDirection::Ref &in) { inref_p = in; fil_p |= 1; init(); } void EarthMagneticMachine::set(const Quantum &hgt) { hgt_p = hgt.getValue("m"); fil_p |= 2; init(); } void EarthMagneticMachine::set(MeasFrame &frame) { if (fil_p & 1) inref_p.set(frame); if (frame.getITRF(pos_p)) fil_p |= 4; if (frame.getTDB(epo_p)) fil_p |= 8; init(); } void EarthMagneticMachine::set(const MPosition &pos) { pos_p = MPosition::Convert(pos, MPosition::ITRF)().getValue(); fil_p |= 4; init(); } void EarthMagneticMachine::set(const MEpoch &tm) { epo_p = MEpoch::Convert(tm, MEpoch::TDB)().getValue().get(); fil_p |= 8; init(); } void EarthMagneticMachine::set(const MVDirection &dir) { rin_p = dir; fil_p |= 16; init(); } Double EarthMagneticMachine::getLOSField() { if (!clx_p) { throw(AipsError("No value calculated for EarthMagneticMachine")); } if (!fex_p) { fex_p = True; los_p = fld_p * in_p; } return los_p; } Double EarthMagneticMachine::getLOSField(const MVDirection &in) { calculate(in); return getLOSField(); } Double EarthMagneticMachine::getLOSField(const Quantum &in) { calculate(in); return getLOSField(); } Double EarthMagneticMachine::getLOSField(const Double in) { calculate(in); return getLOSField(); } Quantum EarthMagneticMachine::getLOSField(const Unit &un) { return Quantum(getLOSField(), "nT").get(un); } Quantum EarthMagneticMachine::getLOSField(const MVDirection &in, const Unit &un) { calculate(in); return getLOSField(un); } Quantum EarthMagneticMachine::getLOSField(const Quantum &in, const Unit &un) { calculate(in); return getLOSField(un); } Quantum EarthMagneticMachine::getLOSField(const Double in, const Unit &un) { calculate(in); return getLOSField(un); } const MVEarthMagnetic &EarthMagneticMachine::getField() { if (!clx_p) { throw(AipsError("No value calculated for EarthMagneticMachine")); } return fld_p; } const MVEarthMagnetic &EarthMagneticMachine::getField(const MVDirection &in) { calculate(in); return getField(); } Double EarthMagneticMachine::getLong() { if (!clx_p) { throw(AipsError("No value calculated for EarthMagneticMachine")); } if (!pex_p) { pex_p = True; pl_p = sub_p.get(); } return pl_p(1); } Double EarthMagneticMachine::getLong(const MVDirection &in) { calculate(in); return getLong(); } Quantum EarthMagneticMachine::getLong(const Unit &un) { return Quantum(getLong(), "rad").get(un); } Quantum EarthMagneticMachine::getLong(const MVDirection &in, const Unit &un) { calculate(in); return getLong(un); } const MVPosition &EarthMagneticMachine::getPosition() { if (!clx_p) { throw(AipsError("No value calculated for EarthMagneticMachine")); } return sub_p; } const MVPosition &EarthMagneticMachine::getPosition(const MVDirection &in) { calculate(in); return getPosition(); } Bool EarthMagneticMachine::calculate(const MVDirection &in) { if ((cumf_p ^ 15) & 15) return False; rin_p = in; fil_p |= 16; calculate(); return clx_p; } Bool EarthMagneticMachine::calculate(const Quantum &hgt) { if ((cumf_p ^ 29) & 29) return False; hgt_p = hgt.getValue("m"); fil_p |= 2; calculate(); return clx_p; } Bool EarthMagneticMachine::calculate(const Double hgt) { if ((cumf_p ^ 29) & 29) return False; hgt_p = hgt; fil_p |= 2; calculate(); return clx_p; } //# Private member functions void EarthMagneticMachine::init() { cumf_p |= fil_p; if (fil_p) { // Initialise the direction conversion engine if (fil_p & 1) conv_p = MDirection::Convert(inref_p, MDirection::ITRF); // Distance of observer to Earth centre if (fil_p & 4) posl_p = pos_p.radius(); // Squared difference between posl_p and distance to sub-point if (((fil_p & 2) && (cumf_p & 4)) || ((fil_p & 4) && (cumf_p & 2))) subl_p = hgt_p*(hgt_p + 2*posl_p); // Field calculator if (fil_p & 8) fldc_p = EarthField(EarthField::STANDARD, epo_p); if (((fil_p & 16) && (cumf_p & 1)) || ((fil_p & 1) && (cumf_p & 16))) { in_p = rin_p; in_p.adjust(); in_p = conv_p(in_p).getValue(); } fil_p = 0; pex_p = False; fex_p = False; clx_p = False; } } void EarthMagneticMachine::copy(const EarthMagneticMachine &other) { inref_p = other.inref_p; hgt_p = other.hgt_p; pos_p = other.pos_p; epo_p = other.epo_p; conv_p = other.conv_p; fil_p = other.fil_p; cumf_p = other.cumf_p; pex_p = False; fex_p = False; clx_p = False; } void EarthMagneticMachine::calculate() { init(); // Angle between direction and Earth radius Double an = pos_p * in_p; Double x = sqrt(abs(an*an + subl_p)); x = min(abs(-an + x), abs(-an - x)); sub_p = pos_p + (x*in_p); fld_p = fldc_p(sub_p); pex_p = False; fex_p = False; clx_p = True; } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/EarthMagneticMachine.h000066400000000000000000000211161476623553700231060ustar00rootroot00000000000000//# EarthMagneticMachine.h: Calculates magnetic field in a direction //# Copyright (C) 1998,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_EARTHMAGNETICMACHINE_H #define MEASURES_EARTHMAGNETICMACHINE_H //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MeasFrame; class MPosition; class MEpoch; // Calculates magnetic field in a direction // // // // //
      • MEarthMagnetic class //
      • MDirection class // // // // From Earth' magnetic Field and machinery // // // // The construction of an EarthMagneticMachine class object creates a // machine that can // calculate the magnetic field in an arbitrary direction. // // The constructors need a reference code (and possibly frame) input // MDirection::Ref to specify how the // the input coordinates have to be interpreted (e.g. MDirection::HADEC). // It also needs an altitude above the Earth for which the field has to be // calculated. The position on Earth can be given as either a // position, or as a frame containing the // position. In the latter case the frame will also be used in the // coordinate transformations. // // Once the EarthMagneticMachine has been established, it can be used to // calculate // the field by the calculate(MVDirection) method. A variety of // get methods let you obtain e.g. the field along the line of sight, the // longitude of the point for which the field was calculated (e.g. the // sub-ionospheric point). // // // // // // Define a time/position frame // MEpoch epo(MVEpoch(MVTime(98,5,16,0.5).day())); // MPosition pos; // MeasTable::Observatory(pos, "ATCA"); // MeasFrame frame(epo, pos); // // Note that e.g. the time in the frame can be changed later // // Set up a machine // EarthMagneticMachine exec(MDirection::B1950, Quantity(200, "km"), frame); // // Given a current observational direction // MDirection indir(Quantity(3.25745692, "rad"), // Quantity(0.040643336,"rad"), // MDirection::Ref(MDirection::B1950)); // // The field in this direction is calculated // exec.calculate(indir.getValue()); // // Show some data // cout << "Parallel field: " << exec.getLOSField() << " nT" << endl; // cout << "Sub-ionosphere long: " << exec.getLong("deg") << endl; // // // // // To aid calculating fields in a simple way. // // // //
      • add more get() values if necessary // class EarthMagneticMachine { public: //# Constructors // Construct an empty machine (probably not usable unles set() used) EarthMagneticMachine(); // Construct a machine from the input values. Either a height or direction // is normally specified. The other can be set(), or can be iterated // over in the () operator or the getLOSfield(). // //
      • AipsError if frame does not contain position and time // // EarthMagneticMachine(const MDirection::Ref &in, const Quantum &hgt, MeasFrame &frame); EarthMagneticMachine(const MDirection::Ref &in, const Quantum &hgt, const MPosition &pos, const MEpoch &tm); EarthMagneticMachine(const MDirection::Ref &in, const MVDirection &dir, MeasFrame &frame); EarthMagneticMachine(const MDirection::Ref &in, const MVDirection &dir, const MPosition &pos, const MEpoch &tm); // // Copy constructor EarthMagneticMachine(const EarthMagneticMachine &other); // Copy assignments EarthMagneticMachine &operator=(const EarthMagneticMachine &other); //# Destructor ~EarthMagneticMachine(); //# Operators // Return line-of-sight field (nT or given units) (from previous calculate // if no direction or height given) // Double operator()(); Quantum operator()(const Unit &un); Double operator()(const MVDirection &in); Quantum operator()(const MVDirection &in, const Unit &un); Double operator()(const Quantum &in); Quantum operator()(const Quantum &in, const Unit &un); Double operator()(const Double in); Quantum operator()(const Double in, const Unit &un); // //# Member functions // Set or reset part of the machine // void set(const MDirection::Ref &in); void set(const Quantum &hgt); void set(MeasFrame &frame); void set(const MPosition &pos); void set(const MEpoch &tm); void set(const MVDirection &dir); // // Calculate a value from direction or height (in m if not Quantity) // Bool calculate(const MVDirection &in); Bool calculate(const Quantum &hgt); Bool calculate(const Double hgt); // // Return data // // Line-of-sight field in nT // Double getLOSField(); Double getLOSField(const MVDirection &in); Double getLOSField(const Quantum &in); Double getLOSField(const Double in); // // Line-of-sight field in specified units (e.g. G) // Quantum getLOSField(const Unit &un); Quantum getLOSField(const MVDirection &in, const Unit &un); Quantum getLOSField(const Quantum &in, const Unit &un); Quantum getLOSField(const Double in, const Unit &un); // // Field (in nT, in ITRF) // const MVEarthMagnetic &getField(); const MVEarthMagnetic &getField(const MVDirection &in); // // Longitude (rad) // Double getLong(); Double getLong(const MVDirection &in); // // Longitude in units (e.g. deg) // Quantum getLong(const Unit &un); Quantum getLong(const MVDirection &in, const Unit &un); // // Position point // const MVPosition &getPosition(); const MVPosition &getPosition(const MVDirection &in); // // // Recalculate the machinery void reCalculate(); private: //# Data // Input direction reference MDirection::Ref inref_p; // Height (m) Double hgt_p; // Observatory position MVPosition pos_p; // Distance to Earth centre Double posl_p; // Distance squared to sub-point Double subl_p; // Epoch Double epo_p; // Conversion engine MDirection::Convert conv_p; // Input position MVDirection in_p; // Re-typed input position MVDirection rin_p; // Extension calculated // Bool fex_p; Bool pex_p; // // Position sub-point MVPosition sub_p; // Earth field calculator EarthField fldc_p; // Magnetic field MVEarthMagnetic fld_p; // Line-of-sight field Double los_p; // Field position Vector pl_p; // Fields filled Int fil_p; // Cumulative filled fields Int cumf_p; // Calc done Bool clx_p; //# Private Member Functions // Initialise machinery void init(); // Copy data members void copy(const EarthMagneticMachine &other); // Calculate field void calculate(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MBaseline.cc000066400000000000000000000175751476623553700211210ustar00rootroot00000000000000//# MBaseline.cc: A Measure: Baseline on Earth //# Copyright (C) 1998-2002,2004,2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors MBaseline::MBaseline() : MeasBase() {} MBaseline::MBaseline(const MVBaseline &dt) : MeasBase(dt,MBaseline::DEFAULT) {} MBaseline::MBaseline(const MVBaseline &dt, const MBaseline::Ref &rf) : MeasBase(dt,rf) {} MBaseline::MBaseline(const MVBaseline &dt, MBaseline::Types rf) : MeasBase(dt,rf) {} MBaseline::MBaseline(const Measure *dt) : MeasBase(dt) {} MBaseline::MBaseline(const MeasValue *dt) : MeasBase(*(MVBaseline*)dt, MBaseline::DEFAULT) {} MBaseline::MBaseline(const MBaseline &other) : MeasBase (other) {} MBaseline &MBaseline::operator=(const MBaseline &other) { if (this != &other) { MeasBase &This = *this; const MeasBase &Other = other; This = Other; } return *this; } //# Destructor MBaseline::~MBaseline() {} //# Operators //# Member functions const String &MBaseline::tellMe() const { return MBaseline::showMe(); } const String &MBaseline::showMe() { static const String name("Baseline"); return name; } void MBaseline::assure(const Measure& in) { if (!dynamic_cast(&in)) { throw(AipsError("Illegal Measure type argument: " + MBaseline::showMe())); } } MBaseline::Types MBaseline::castType(uInt tp) { MBaseline::checkMyTypes(); AlwaysAssert(tp < MBaseline::N_Types, AipsError); return static_cast(tp); } const String &MBaseline::showType(MBaseline::Types tp) { static const String tname[MBaseline::N_Types] = { "J2000", "JMEAN", "JTRUE", "APP", "B1950", "B1950_VLA", "BMEAN", "BTRUE", "GALACTIC", "HADEC", "AZEL", "AZELSW", "AZELGEO", "AZELSWGEO", "JNAT", "ECLIPTIC", "MECLIPTIC", "TECLIPTIC", "SUPERGAL", "ITRF", "TOPO", "ICRS" }; MBaseline::checkMyTypes(); return tname[tp]; } const String &MBaseline::showType(uInt tp) { return MBaseline::showType(MBaseline::castType(tp)); } const String* MBaseline::allMyTypes(Int &nall, Int &nextra, const uInt *&typ) { static const Int N_name = 24; static const Int N_extra = 0; static const String tname[N_name] = { "J2000", "JMEAN", "JTRUE", "APP", "B1950", "B1950_VLA", "BMEAN", "BTRUE", "GALACTIC", "HADEC", "AZEL", "AZELSW", "AZELNE", "AZELGEO", "AZELSWGEO", "AZELNEGEO", "JNAT", "ECLIPTIC", "MECLIPTIC", "TECLIPTIC", "SUPERGAL", "ITRF", "TOPO", "ICRS" }; static const uInt oname[N_name] = { MBaseline::J2000, MBaseline::JMEAN, MBaseline::JTRUE, MBaseline::APP, MBaseline::B1950, MBaseline::B1950_VLA, MBaseline::BMEAN, MBaseline::BTRUE, MBaseline::GALACTIC, MBaseline::HADEC, MBaseline::AZEL, MBaseline::AZELSW, MBaseline::AZEL, MBaseline::AZELGEO, MBaseline::AZELSWGEO, MBaseline::AZELGEO, MBaseline::JNAT, MBaseline::ECLIPTIC, MBaseline::MECLIPTIC, MBaseline::TECLIPTIC, MBaseline::SUPERGAL, MBaseline::ITRF, MBaseline::TOPO, MBaseline::ICRS }; MBaseline::checkMyTypes(); nall = N_name; nextra = N_extra; typ = oname; return tname; } const String* MBaseline::allTypes(Int &nall, Int &nextra, const uInt *&typ) const { return MBaseline::allMyTypes(nall, nextra, typ); } void MBaseline::checkTypes() const { MBaseline::checkMyTypes(); } void MBaseline::checkMyTypes() { // Multiple threads could execute this, but that is harmless. static Bool first(True); if (first) { first = False; Int nall, nex; const uInt *typ; const String *const tps = MBaseline::allMyTypes(nall,nex, typ); MBaseline::Types tp; for (Int i=0; i(MBaseline::N_Types) == static_cast(MDirection::N_Types), AipsError); for (Int i=0; i(static_cast(in)); } MDirection::Types MBaseline::toDirType(const MBaseline::Types in) { MBaseline::checkMyTypes(); return static_cast(static_cast(in)); } Bool MBaseline::getType(MBaseline::Types &tp, const String &in) { const uInt *oname; Int nall, nex; const String *tname = MBaseline::allMyTypes(nall, nex, oname); Int i = Measure::giveMe(in, nall, tname); if (i>=nall) return False; else tp = static_cast(oname[i]); return True; } Bool MBaseline::giveMe(MBaseline::Ref &mr, const String &in) { MBaseline::Types tp; if (MBaseline::getType(tp, in)) mr = MBaseline::Ref(tp); else { mr = MBaseline::Ref(); return False; } return True; } Bool MBaseline::setOffset(const Measure &in) { if (!dynamic_cast(&in)) return False; ref.set(in); return True; } Bool MBaseline::setRefString(const String &in) { MBaseline::Types tp; if (MBaseline::getType(tp, in)) { ref.setType(tp); return True; } ref.setType(MBaseline::DEFAULT); return False; } const String &MBaseline::getDefaultType() const { return MBaseline::showType(MBaseline::DEFAULT); } String MBaseline::getRefString() const { return MBaseline::showType(ref.getType()); } Quantum > MBaseline::get(const Unit &inunit) const { return Quantum >(data.getValue(),"m").get(inunit); } Quantum > MBaseline::getAngle() const { return (data.getAngle()); } Quantum > MBaseline::getAngle(const Unit &inunit) const { return (data.getAngle(inunit)); } Measure *MBaseline::clone() const { return (new MBaseline(*this)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/MBaseline.h000066400000000000000000000166401476623553700207530ustar00rootroot00000000000000//# MBaseline.h: A Measure: Baseline on Earth //# Copyright (C) 1998-2000,2002,2004,2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MBASELINE_H #define MEASURES_MBASELINE_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MBaseline; class MCBaseline; template class MeasConvert; template class ArrayMeasColumn; template class ScalarMeasColumn; //# Typedefs // A Measure: Baseline on Earth // // // // //
      • Measure class // // // // From Measure and Baseline // // // // MBaseline forms derived Measure class for an interferometer baseline. // Baselines can be given in any of the direction types, or as ITRF, the // IERS base.
        // Note that at the moment no correction for Earth tides (error <~ 0.05 mm/km // EW baseline), plate motion (not relevant for telescopes on same plate) and // relativistic effects are incorporated. B1950 has the same caveat as in // MDirection. //
        // // // // // // // // //
      • add some Earth tide model // class MBaseline : public MeasBase > { public: //# Friends // Conversion of data friend class MeasConvert; //# Enumerations // Types of known MBaselines // // The order defines the order in the translation matrix FromTo // in the getConvert routine. Do not change the order without // changing the array. Additions should be made before N_types, and // an additional row and column should be coded in FromTo, and // in showType(). enum Types { J2000, JMEAN, JTRUE, APP, B1950, B1950_VLA, BMEAN, BTRUE, GALACTIC, HADEC, AZEL, AZELSW, AZELGEO, AZELSWGEO, JNAT, ECLIPTIC, MECLIPTIC, TECLIPTIC, SUPERGAL, ITRF, TOPO, ICRS, N_Types, // Defaults DEFAULT=ITRF, // Synonyms AZELNE=AZEL, AZELNEGEO=AZELGEO }; //# Typedefs // Measure value container for this class (i.e. MBaseline::MVType) typedef MVBaseline MVType; // Measure conversion routines for this class (i.e. MBaseline::MCType) typedef MCBaseline MCType; // Measure reference (i.e. MBaseline::Ref) typedef MeasRef Ref; // Measure Convert (i.e. MBaseline::Convert) typedef MeasConvert Convert; // Measure table Columns (e.g., MBaseline::ScalarColumn) typedef ScalarMeasColumn ScalarColumn; typedef ArrayMeasColumn ArrayColumn; //# Constructors // In the following constructors and other functions, all // MeasRef can be replaced with simple Measure::TYPE // where no offsets or frames are needed in the reference. // Default constructor; generates the ITRF centre MBaseline(); // Create from data and reference // MBaseline(const MVBaseline &dt); MBaseline(const MVBaseline &dt, const MBaseline::Ref &rf); MBaseline(const MVBaseline &dt, MBaseline::Types rf); MBaseline(const Measure *dt); MBaseline(const MeasValue *dt); // // MBaseline(const MBaseline &); MBaseline &operator=(const MBaseline &); // //# Destructor virtual ~MBaseline(); //# Operators //# General Member Functions // Tell me your type // virtual const String &tellMe() const; static const String &showMe(); static void assure(const Measure &in); // // Translate reference code. The uInt version has a check for valid codes // (i.e. it is a safe cast). // //
      • AipsError in the uInt interface if illegal code given // // static MBaseline::Types castType(uInt tp); static const String &showType(MBaseline::Types tp); static const String &showType(uInt tp); // // Translate string to reference code // static Bool getType(MBaseline::Types &tp, const String &in); Bool giveMe(MBaseline::Ref &mr, const String &in); // // Set the offset in the reference (False if non-matching Measure) virtual Bool setOffset(const Measure &in); // Set the reference type to the specified String. False if illegal // string, reference set to DEFAULT. virtual Bool setRefString(const String &in); // Get the default reference type virtual const String &getDefaultType() const; // Get a list of all known reference codes. nall returns the number in list, // nextra the number of specials (like planets) that should be at // end of list). typ returns the list of corresponding types. // virtual const String* allTypes(Int &nall, Int &nextra, const uInt *&typ) const; static const String* allMyTypes(Int &nall, Int &nextra, const uInt *&typ); // // Check if all internal tables of types (both enum and String) are // complete and correct. This function is called automatically if and when // necessary. // //
      • AipsError if a (programming) error in the types. // // virtual void checkTypes() const; static void checkMyTypes(); // // Get the correct MBaseline type from a given direction type (or v.v.) // static MBaseline::Types fromDirType(const MDirection::Types in); static MDirection::Types toDirType(const MBaseline::Types in); // // Get the reference type (for records, including codes like R_) virtual String getRefString() const; // Get Measure data // Quantum > get(const Unit &inunit) const; Quantum > getAngle() const; Quantum > getAngle(const Unit &inunit) const; // // Make copy // virtual Measure *clone() const; // private: //# Enumerations //# Data //# Member functions }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MCBase.cc000066400000000000000000000105101476623553700203320ustar00rootroot00000000000000//# MCBase.cc: Base for specific measure conversions //# Copyright (C) 1995,1996,1997,1998,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Destructor MCBase::~MCBase() {} //# Operators //# Member functions void MCBase::makeState(uInt *state, const uInt ntyp, const uInt nrout, const uInt list[][3]) { // Make trees uInt *tcnt = new uInt[ntyp]; uInt *tree = new uInt[ntyp*ntyp]; Bool *visit= new Bool[ntyp]; uInt *mcnt = new uInt[ntyp*ntyp]; for (uInt j=0; j namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MeasValue; class MCBase; class MRBase; class MConvertBase; class String; //# Typedefs // Base for specific measure conversions // // // // //
      • Measure class //
      • MConvertBase: conversion engine // // // // Measure, Conversion and Base // // // // MCBase forms the base for the individual state machines doing actual // conversions between frames. (see e.g. MCEpoch) // // It also has a static routine to calculate the state transition table based // on a list of transitions. The makeState() method find the shortest route // (weighted if necessary) for a given list of state transitions. // // The user of the Measure classes has no direct interaction with this class. // // // // Convert (with all steps explicit) a UTC to an IAT time. // // #include // #include // cout << "TAI for UTC = MJD(50237.29): " << // MEpoch::Convert(MEpoch(MVEpoch(Quantity(50237.29, "d")), // MEpoch::Ref(MEpoch::UTC)), // MEpoch::Ref(MEpoch::TAI))() << // endl; // // To get a static state transition matrix: // // static Bool made = False; // set not yet done // enum types { // states // A=0, B, C, D, E, ntyp }; // enum routes { // routes // A_B, B_C, B_D, C_D, C_E, // D_C, C_B, B_A, D_B, E_C, nrout }; // static uInt list [nrout][3] = { // description. The third number // {A, B, 0}, // is a penalty hop to weight // {B, C, 0}, // against using this route // {B, D, 0}, // {C, D, 0}, // {C, E, 0}, // {D, C, 0}, // {C, B, 0}, // {B, A, 0}, // {D, B, 0}, // {E, C, 0} }; // static uInt state[ntyp][ntyp]; // the resultant transition matrix // // diagonal == nrout // // Make the state machine // MCBase::makeState(state[0], ntyp, nrout, routes); // made = True; // // // // // To have specific conversion bases // // // //
      • Nothing I know // class MCBase { public: //# Typedefs //# Constructors //# Destructor virtual ~MCBase(); //# Operators //# Enumerations // Each derived class should have a list of routines to be called: enum Routes { N_Routes}; //# Member functions // All these functions are called by Measure::Convert classes only // // Create conversion state machine list virtual void getConvert(MConvertBase &mc, const MRBase &inref, const MRBase &outref) = 0; // Create help structures for Measure conversion routines virtual void initConvert(uInt which, MConvertBase &mc) = 0; // Delete the pointers used in the MeasConvert help structure cache virtual void clearConvert() = 0; // Routine to convert a Measure from one reference frame to another virtual void doConvert(MeasValue &in, MRBase &inref, MRBase &outref, const MConvertBase &mc) = 0; // protected: // The following routines create a state transition matrix from a list // of all defined transitions. It uses the following information: //
          //
        • nrout: the number of transitions; numbered 0, ... //
        • ntyp: the number of states //
        • list: a [nrout][3] list of input and output transition type of // transition and a penalty hop number (<100) //
        • state: a [ntyp][ntyp] transition matrix with diagonal elements set // to nrout. //
        // // Routine to make the transition table if necessary static void makeState(uInt *state, const uInt ntyp, const uInt nrout, const uInt list[][3]); // Return a fromatted String with matrix information (based on < 100 types) static String showState(uInt *state, const uInt ntyp, const uInt nrout, const uInt list[][3]); private: // Routine to find the shortest route between two points static Bool findState(uInt &len, uInt *state, uInt *mcnt, Bool &okall, Bool *visit, const uInt *tcnt, const uInt *tree, const uInt &in, const uInt &out, const uInt ntyp, const uInt nrout, const uInt list[][3]); // }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MCBaseline.cc000066400000000000000000000264621476623553700212170ustar00rootroot00000000000000//# MCBaseline.cc: MBaseline conversion routines //# Copyright (C) 1998-2000,2002,2004,2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Statics uInt MCBaseline::ToRef_p[N_Routes][3] = { {MBaseline::GALACTIC, MBaseline::J2000, 0}, {MBaseline::GALACTIC, MBaseline::B1950, 2}, {MBaseline::J2000, MBaseline::GALACTIC, 0}, {MBaseline::B1950, MBaseline::GALACTIC, 2}, {MBaseline::J2000, MBaseline::B1950, 2}, {MBaseline::J2000, MBaseline::B1950_VLA, 2}, {MBaseline::B1950, MBaseline::J2000, 2}, {MBaseline::B1950_VLA, MBaseline::J2000, 2}, {MBaseline::B1950, MBaseline::B1950_VLA, 0}, {MBaseline::B1950_VLA, MBaseline::B1950, 0}, {MBaseline::J2000, MBaseline::JMEAN, 0}, {MBaseline::B1950, MBaseline::BMEAN, 2}, {MBaseline::JMEAN, MBaseline::J2000, 0}, {MBaseline::JMEAN, MBaseline::JTRUE, 0}, {MBaseline::BMEAN, MBaseline::B1950, 2}, {MBaseline::BMEAN, MBaseline::BTRUE, 2}, {MBaseline::JTRUE, MBaseline::JMEAN, 0}, {MBaseline::BTRUE, MBaseline::BMEAN, 2}, {MBaseline::J2000, MBaseline::JNAT, 0}, {MBaseline::JNAT, MBaseline::J2000, 0}, {MBaseline::B1950, MBaseline::APP, 2}, {MBaseline::APP, MBaseline::B1950, 2}, {MBaseline::APP, MBaseline::TOPO, 0}, {MBaseline::HADEC, MBaseline::AZEL, 0}, {MBaseline::HADEC, MBaseline::AZELGEO, 0}, {MBaseline::AZEL, MBaseline::HADEC, 0}, {MBaseline::AZELGEO, MBaseline::HADEC, 0}, {MBaseline::HADEC, MBaseline::TOPO, 0}, {MBaseline::AZEL, MBaseline::AZELSW, 0}, {MBaseline::AZELGEO, MBaseline::AZELSWGEO, 0}, {MBaseline::AZELSW, MBaseline::AZEL, 0}, {MBaseline::AZELSWGEO, MBaseline::AZELGEO, 0}, {MBaseline::APP, MBaseline::JNAT, 0}, {MBaseline::JNAT, MBaseline::APP, 0}, {MBaseline::J2000, MBaseline::ECLIPTIC, 0}, {MBaseline::ECLIPTIC, MBaseline::J2000, 0}, {MBaseline::JMEAN, MBaseline::MECLIPTIC, 0}, {MBaseline::MECLIPTIC, MBaseline::JMEAN, 0}, {MBaseline::JTRUE, MBaseline::TECLIPTIC, 0}, {MBaseline::TECLIPTIC, MBaseline::JTRUE, 0}, {MBaseline::GALACTIC, MBaseline::SUPERGAL, 0}, {MBaseline::SUPERGAL, MBaseline::GALACTIC, 0}, {MBaseline::ITRF, MBaseline::HADEC, 0}, {MBaseline::HADEC, MBaseline::ITRF, 0}, {MBaseline::TOPO, MBaseline::HADEC, 0}, {MBaseline::TOPO, MBaseline::APP, 0}, {MBaseline::ICRS, MBaseline::J2000, 0}, {MBaseline::J2000, MBaseline::ICRS, 0} }; uInt MCBaseline::FromTo_p[MBaseline::N_Types][MBaseline::N_Types]; std::once_flag MCBaseline::theirInitOnceFlag; //# Constructors MCBaseline::MCBaseline() : measMath() { std::call_once(theirInitOnceFlag, doFillState); } //# Destructor MCBaseline::~MCBaseline() { clearConvert(); } //# Member functions void MCBaseline::getConvert(MConvertBase &mc, const MRBase &inref, const MRBase &outref) { Int iin = inref.getType(); Int iout = outref.getType(); if (iin != iout) { Int tmp; while (iin != iout) { tmp = FromTo_p[iin][iout]; iin = ToRef_p[tmp][1]; mc.addMethod(tmp); initConvert(tmp, mc); } } } void MCBaseline::clearConvert() { } //# Conversion routines void MCBaseline::initConvert(uInt which, MConvertBase &mc) { if (False) initConvert(which, mc); // Stop warning switch (which) { case J2000_JMEAN: measMath.createPrecession(); break; case B1950_BMEAN: measMath.createPrecessionB1950(); break; case JMEAN_J2000: measMath.createPrecession(); break; case JMEAN_JTRUE: measMath.createNutation(); break; case BMEAN_B1950: measMath.createPrecessionB1950(); break; case BMEAN_BTRUE: measMath.createNutationB1950(); break; case JTRUE_JMEAN: measMath.createNutation(); break; case BTRUE_BMEAN: measMath.createNutationB1950(); break; case J2000_JNAT: measMath.createSolarPos(); break; case JNAT_APP: measMath.createAberration(); measMath.createPrecNutat(); break; case JNAT_J2000: measMath.createSolarPos(); break; case APP_JNAT: measMath.createAberration(); measMath.createPrecNutat(); break; case B1950_APP: measMath.createAberrationB1950(); measMath.createPrecNutatB1950(); break; case APP_B1950: measMath.createAberrationB1950(); measMath.createPrecNutatB1950(); break; default: break; } } void MCBaseline::doConvert(MeasValue &in, MRBase &inref, MRBase &outref, const MConvertBase &mc) { doConvert((MVBaseline &) in, inref, outref, mc); } void MCBaseline::doConvert(MVBaseline &in, MRBase &inref, MRBase &outref, const MConvertBase &mc) { Double g2; // Planetary aberration factor Double lengthP = 0; measMath.initFrame(inref, outref); for (Int i=0; i #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MCBaseline; class String; //# Typedefs // MBaseline conversion routines // // // // //
      • Measure class //
      • MCBase base class //
      • overall conversion class // // // // Measure, Convert and Baseline // // // // Contains state machinery and caching for actual conversions // // // // See Measures module description for // conversion examples. // // // // // // //
      • nothing I know // class MCBaseline : public MCBase { public: //# Friends // Conversion of data friend class MeasConvert; //# Constructors // Default constructor MCBaseline(); //# Destructor ~MCBaseline(); //# Member functions // Show the state of the conversion engine (mainly for debugging purposes) static String showState(); private: //# Enumerations // The list of actual routines provided. // Each AA_BB in the list points to routine // that can be used in the FromTo list in the getConvert routine. // In addition the type to which each is converted should be in the // ToRef array, again in the proper order. enum Routes { GAL_J2000, GAL_B1950, J2000_GAL, B1950_GAL, J2000_B1950, J2000_B1950_VLA, B1950_J2000, B1950_VLA_J2000, B1950_B1950_VLA, B1950_VLA_B1950, J2000_JMEAN, B1950_BMEAN, JMEAN_J2000, JMEAN_JTRUE, BMEAN_B1950, BMEAN_BTRUE, JTRUE_JMEAN, BTRUE_BMEAN, J2000_JNAT, JNAT_J2000, B1950_APP, APP_B1950, APP_TOPO, HADEC_AZEL, HADEC_AZELGEO, AZEL_HADEC, AZELGEO_HADEC, HADEC_TOPO, AZEL_AZELSW, AZELGEO_AZELSWGEO, AZELSW_AZEL, AZELSWGEO_AZELGEO, APP_JNAT, JNAT_APP, J2000_ECLIP, ECLIP_J2000, JMEAN_MECLIP, MECLIP_JMEAN, JTRUE_TECLIP, TECLIP_JTRUE, GAL_SUPERGAL, SUPERGAL_GAL, ITRF_HADEC, HADEC_ITRF, TOPO_HADEC, TOPO_APP, ICRS_J2000, J2000_ICRS, N_Routes }; //# Typedefs //# Operators //# General Member Functions //# Enumerations //# Cached Data MeasMath measMath; //# State machine data // Transition list static uInt ToRef_p[N_Routes][3]; // Transition matrix static uInt FromTo_p[MBaseline::N_Types][MBaseline::N_Types]; // Object to ensure safe multi-threaded lazy single initialization static std::once_flag theirInitOnceFlag; //# Constructors // Copy constructor (not implemented) MCBaseline(const MCBaseline &other); // Assignment (not implemented) MCBaseline &operator=(const MCBaseline &other); //# Member functions // Create conversion function pointer virtual void getConvert(MConvertBase &mc, const MRBase &inref, const MRBase &outref); // Create help structures for Measure conversion routines virtual void initConvert(uInt which, MConvertBase &mc); // Delete the pointers used in the MeasConvert help structure cache virtual void clearConvert(); // Routines to convert Baselines from one reference frame to another virtual void doConvert(MeasValue &in, MRBase &inref, MRBase &outref, const MConvertBase &mc); // Conversion routine to cater for inheritance question void doConvert(MVBaseline &in, MRBase &inref, MRBase &outref, const MConvertBase &mc); private: // Fill the global state. Called using theirInitOnce. static void doFillState(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MCDirection.cc000066400000000000000000000411161476623553700214060ustar00rootroot00000000000000//# MCDirection.cc: MDirection conversion routines //# Copyright (C) 1995-1998,2000-2002,2004,2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Statics uInt MCDirection::ToRef_p[N_Routes][3] = { {MDirection::GALACTIC, MDirection::J2000, 0}, {MDirection::GALACTIC, MDirection::B1950, 2}, {MDirection::J2000, MDirection::GALACTIC, 0}, {MDirection::B1950, MDirection::GALACTIC, 2}, {MDirection::J2000, MDirection::B1950, 2}, {MDirection::J2000, MDirection::B1950_VLA, 2}, {MDirection::B1950, MDirection::J2000, 2}, {MDirection::B1950_VLA, MDirection::J2000, 2}, {MDirection::B1950, MDirection::B1950_VLA, 0}, {MDirection::B1950_VLA, MDirection::B1950, 0}, {MDirection::J2000, MDirection::JMEAN, 0}, {MDirection::B1950, MDirection::BMEAN, 2}, {MDirection::JMEAN, MDirection::J2000, 0}, {MDirection::JMEAN, MDirection::JTRUE, 0}, {MDirection::BMEAN, MDirection::B1950, 2}, {MDirection::BMEAN, MDirection::BTRUE, 2}, {MDirection::JTRUE, MDirection::JMEAN, 0}, {MDirection::BTRUE, MDirection::BMEAN, 2}, {MDirection::J2000, MDirection::JNAT, 0}, {MDirection::JNAT, MDirection::J2000, 0}, {MDirection::B1950, MDirection::APP, 2}, {MDirection::APP, MDirection::B1950, 2}, {MDirection::APP, MDirection::TOPO, 0}, {MDirection::HADEC, MDirection::AZEL, 0}, {MDirection::HADEC, MDirection::AZELGEO, 0}, {MDirection::AZEL, MDirection::HADEC, 0}, {MDirection::AZELGEO, MDirection::HADEC, 0}, {MDirection::HADEC, MDirection::TOPO, 0}, {MDirection::AZEL, MDirection::AZELSW, 0}, {MDirection::AZELGEO, MDirection::AZELSWGEO, 0}, {MDirection::AZELSW, MDirection::AZEL, 0}, {MDirection::AZELSWGEO, MDirection::AZELGEO, 0}, {MDirection::APP, MDirection::JNAT, 0}, {MDirection::JNAT, MDirection::APP, 0}, {MDirection::J2000, MDirection::ECLIPTIC, 0}, {MDirection::ECLIPTIC, MDirection::J2000, 0}, {MDirection::JMEAN, MDirection::MECLIPTIC, 0}, {MDirection::MECLIPTIC, MDirection::JMEAN, 0}, {MDirection::JTRUE, MDirection::TECLIPTIC, 0}, {MDirection::TECLIPTIC, MDirection::JTRUE, 0}, {MDirection::GALACTIC, MDirection::SUPERGAL, 0}, {MDirection::SUPERGAL, MDirection::GALACTIC, 0}, {MDirection::ITRF, MDirection::HADEC, 0}, {MDirection::HADEC, MDirection::ITRF, 0}, {MDirection::TOPO, MDirection::HADEC, 0}, {MDirection::TOPO, MDirection::APP, 0}, {MDirection::ICRS, MDirection::J2000, 0}, {MDirection::J2000, MDirection::ICRS, 0} }; uInt MCDirection::FromTo_p[MDirection::N_Types][MDirection::N_Types]; std::once_flag MCDirection::theirInitOnceFlag; //# Constructors MCDirection::MCDirection() : MVPOS1(0), MVPOS2(0), MVPOS3(0), VEC61(0), VEC62(0), VEC63(0), measMath() { std::call_once(theirInitOnceFlag, doFillState); } //# Destructor MCDirection::~MCDirection() { clearConvert(); } //# Operators //# Member functions void MCDirection::getConvert(MConvertBase &mc, const MRBase &inref, const MRBase &outref) { uInt iin = inref.getType(); uInt iout = outref.getType(); if (iin != iout) { Bool iplan = (iin & MDirection::EXTRA); Bool oplan = (iout & MDirection::EXTRA); if (iplan) { if (iin != MDirection::COMET) { mc.addMethod(MCDirection::R_PLANET0); mc.addFrameType(MeasFrame::EPOCH); mc.addMethod((iin & ~MDirection::EXTRA) + MCDirection::R_MERCURY); mc.addMethod(MCDirection::R_PLANET); initConvert(MCDirection::R_PLANET, mc); iin = MDirection::JNAT; } else { mc.addMethod(MCDirection::R_COMET0); mc.addFrameType(MeasFrame::EPOCH); mc.addFrameType(MeasFrame::COMET); mc.addFrameType(MeasFrame::POSITION); mc.addMethod(MCDirection::R_COMET); initConvert(MCDirection::R_COMET, mc); /////have to do this copy as inref and outref are const ////if they were not then could pass them directly to cometframe const MeasRef& inrefMR=dynamic_cast&>(inref); const MeasRef& outrefMR=dynamic_cast&>(outref); MeasRef incopy(inrefMR); MeasRef outcopy(outrefMR); MeasFrame cometframe=MDirection::Ref::frameComet(incopy, outcopy); //////////// if( cometframe.comet() && (cometframe.comet()->hasPosrefsys())){ iin=cometframe.comet()->getPosrefsysType(); } else{ iin = MDirection::APP; } } } if (oplan) iout = MDirection::J2000; Int tmp; while (iin != iout) { tmp = FromTo_p[iin][iout]; iin = ToRef_p[tmp][1]; mc.addMethod(tmp); initConvert(tmp, mc); } } } void MCDirection::clearConvert() { delete MVPOS1; MVPOS1 = 0; delete MVPOS2; MVPOS2 = 0; delete MVPOS3; MVPOS3 = 0; delete VEC61; VEC61 = 0; delete VEC62; VEC62 = 0; delete VEC63; VEC63 = 0; } //# Conversion routines void MCDirection::initConvert(uInt which, MConvertBase &mc) { if (False) initConvert(which, mc); // Stop warning if (!MVPOS1) MVPOS1 = new MVPosition(); if (!MVPOS2) MVPOS2 = new MVPosition(); if (!MVPOS3) MVPOS3 = new MVPosition(); if (!VEC61) VEC61 = new Vector(6); if (!VEC62) VEC62 = new Vector(6); if (!VEC63) VEC63 = new Vector(6); switch (which) { case J2000_JMEAN: measMath.createPrecession(); mc.addFrameType(MeasFrame::EPOCH); break; case B1950_BMEAN: measMath.createPrecessionB1950(); mc.addFrameType(MeasFrame::EPOCH); break; case JMEAN_J2000: mc.addFrameType(MeasFrame::EPOCH); measMath.createPrecession(); break; case JMEAN_JTRUE: mc.addFrameType(MeasFrame::EPOCH); measMath.createNutation(); break; case BMEAN_B1950: mc.addFrameType(MeasFrame::EPOCH); measMath.createPrecessionB1950(); break; case BMEAN_BTRUE: measMath.createNutationB1950(); mc.addFrameType(MeasFrame::EPOCH); break; case JTRUE_JMEAN: measMath.createNutation(); mc.addFrameType(MeasFrame::EPOCH); break; case BTRUE_BMEAN: measMath.createNutationB1950(); mc.addFrameType(MeasFrame::EPOCH); break; case J2000_JNAT: measMath.createSolarPos(); mc.addFrameType(MeasFrame::EPOCH); break; case JNAT_APP: measMath.createAberration(); measMath.createPrecNutat(); mc.addFrameType(MeasFrame::EPOCH); break; case JNAT_J2000: measMath.createSolarPos(); mc.addFrameType(MeasFrame::EPOCH); break; case APP_JNAT: measMath.createAberration(); measMath.createPrecNutat(); mc.addFrameType(MeasFrame::EPOCH); break; case B1950_APP: measMath.createAberrationB1950(); measMath.createPrecNutatB1950(); mc.addFrameType(MeasFrame::EPOCH); break; case APP_B1950: measMath.createAberrationB1950(); measMath.createPrecNutatB1950(); mc.addFrameType(MeasFrame::EPOCH); break; case HADEC_ITRF: case ITRF_HADEC: case HADEC_AZEL: case HADEC_AZELGEO: case AZEL_HADEC: case AZELGEO_HADEC: case MECLIP_JMEAN: case JMEAN_MECLIP: case TECLIP_JTRUE: case JTRUE_TECLIP: mc.addFrameType(MeasFrame::POSITION); break; case TOPO_HADEC: case HADEC_TOPO: case APP_TOPO: case TOPO_APP: mc.addFrameType(MeasFrame::EPOCH); mc.addFrameType(MeasFrame::POSITION); break; default: break; } } void MCDirection::doConvert(MeasValue &in, MRBase &inref, MRBase &outref, const MConvertBase &mc) { doConvert(*(MVDirection*)&in, inref, outref, mc); } void MCDirection::doConvert(MVDirection &in, MRBase &inref, MRBase &outref, const MConvertBase &mc) { Double g1, g2, g3, lengthE, tdbTime; // Planetary aberration factor Double lengthP = 0; MeasTable::Types planID = MeasTable::MERCURY; // to stop warning uInt comID = static_cast(MDirection::APP); measMath.initFrame(inref, outref); for (Int i=0; iadjust(g2); lengthE = 0; do { g3 = lengthE; *VEC61 = MeasTable::Planetary(planID, tdbTime - g3); *VEC63 = MeasTable::Planetary(MeasTable::SUN, tdbTime - g3); // Sb for (Int j=0; j<3; j++) { (*MVPOS1)(j) = (*VEC61)(j) - (*VEC62)(j); // P (*MVPOS2)(j) = (*VEC61)(j) - (*VEC63)(j); // Q } MVPOS1->adjust(lengthE); lengthP = Quantity(lengthE, "AU").getBaseValue(); MVPOS2->adjust(g1); if (planID != MeasTable::SUN) lengthE += 2*MeasTable::Planetary(MeasTable::GMS) * log((g2+lengthE+g1)/(g2-lengthE+g1)); lengthE /= MeasTable::Planetary(MeasTable::CAU); } while (abs(g3-lengthE) > 1e-9*lengthE); // MVDirections have default 0,0,1, we do not want to apply // a shift with 90 degrees in latitude (shifts should be // smaller than a few degrees). bool doShift = (in(2)!=1.); Quantity shift_long; Quantity shift_lat; if (doShift) { shift_long = in.getLong(); shift_lat = in.getLat(); } in = *MVPOS1; in.adjust(); if (doShift) { in.shift(shift_long, shift_lat); } // Correct for light deflection // Check if near sun if (planID != MeasTable::SUN && (g3 = in * *MVPOS3, !nearAbs(g3, 1.0, 1.0-cos(MeasTable::Planetary(MeasTable::RADS)/g2)))) { g1 = 2*MeasTable::Planetary(MeasTable::GMS) / g2; g1 /= (1.0 + (*MVPOS2)*(*MVPOS3)); in += g1 * ((in*(*MVPOS2))* *MVPOS3 - ((*MVPOS3)*in)* *MVPOS2); } in.adjust(); } break; case R_MERCURY: planID = MeasTable::MERCURY; break; case R_VENUS: planID = MeasTable::VENUS; break; case R_MARS: planID = MeasTable::MARS; break; case R_JUPITER: planID = MeasTable::JUPITER; break; case R_SATURN: planID = MeasTable::SATURN; break; case R_URANUS: planID = MeasTable::URANUS; break; case R_NEPTUNE: planID = MeasTable::NEPTUNE; break; case R_PLUTO: planID = MeasTable::PLUTO; break; case R_SUN: planID = MeasTable::SUN; break; case R_MOON: planID = MeasTable::MOON; break; case R_COMET0: { MDirection::Ref::frameComet(inref, outref). getCometType(comID); if (!MDirection::Ref::frameComet(inref, outref). getComet(*MVPOS1)) { throw(AipsError("No or outside range comet table specified")); } MVPOS1->adjust(lengthP); in = *MVPOS1; } break; default: break; } // switch } // for } String MCDirection::showState() { std::call_once(theirInitOnceFlag, doFillState); return MCBase::showState(MCDirection::FromTo_p[0], MDirection::N_Types, MCDirection::N_Routes, MCDirection::ToRef_p); } void MCDirection::doFillState() { MDirection::checkMyTypes(); MCBase::makeState(FromTo_p[0], MDirection::N_Types, N_Routes, ToRef_p); } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/MCDirection.h000066400000000000000000000136431476623553700212540ustar00rootroot00000000000000//# MCDirection.h: MDirection conversion routines //# Copyright (C) 1995-2000,2002,2004,2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MCDIRECTION_H #define MEASURES_MCDIRECTION_H //# Includes #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MCDirection; class MVPosition; class String; //# Typedefs // MDirection conversion routines // // // // //
      • Measure class //
      • MCBase base class //
      • overall conversion class // // // // Measure, Convert and Direction // // // // Contains state machinery and caching for actual conversions // // // // See Measures module description for // conversion examples. // // // // // // //
      • nothing I know // class MCDirection : public MCBase { public: //# Friends // Conversion of data friend class MeasConvert; //# Constructors // Default constructor MCDirection(); //# Destructor ~MCDirection(); //# Member functions // Show the state of the conversion engine (mainly for debugging purposes) static String showState(); private: //# Enumerations // The list of actual routines provided. // Each AA_BB in the list points to routine // that can be used in the FromTo list in the getConvert routine. // In addition the type to which each is converted should be in the // ToRef array, again in the proper order. enum Routes { GAL_J2000, GAL_B1950, J2000_GAL, B1950_GAL, J2000_B1950, J2000_B1950_VLA, B1950_J2000, B1950_VLA_J2000, B1950_B1950_VLA, B1950_VLA_B1950, J2000_JMEAN, B1950_BMEAN, JMEAN_J2000, JMEAN_JTRUE, BMEAN_B1950, BMEAN_BTRUE, JTRUE_JMEAN, BTRUE_BMEAN, J2000_JNAT, JNAT_J2000, B1950_APP, APP_B1950, APP_TOPO, HADEC_AZEL, HADEC_AZELGEO, AZEL_HADEC, AZELGEO_HADEC, HADEC_TOPO, AZEL_AZELSW, AZELGEO_AZELSWGEO, AZELSW_AZEL, AZELSWGEO_AZELGEO, APP_JNAT, JNAT_APP, J2000_ECLIP, ECLIP_J2000, JMEAN_MECLIP, MECLIP_JMEAN, JTRUE_TECLIP, TECLIP_JTRUE, GAL_SUPERGAL, SUPERGAL_GAL, ITRF_HADEC, HADEC_ITRF, TOPO_HADEC, TOPO_APP, ICRS_J2000, J2000_ICRS, N_Routes, // General for Planets R_PLANET0, R_PLANET, R_COMET0, R_COMET, // Individual planets. Order should be the same as in MDirection.h R_MERCURY, R_VENUS, R_MARS, R_JUPITER, R_SATURN, R_URANUS, R_NEPTUNE, R_PLUTO, R_SUN, R_MOON }; //# Typedefs //# Operators //# General Member Functions //# Enumerations //# Cached Data MVPosition *MVPOS1, *MVPOS2, *MVPOS3; Vector *VEC61, *VEC62, *VEC63; MeasMath measMath; //# State machine data // Transition list static uInt ToRef_p[N_Routes][3]; // Transition matrix static uInt FromTo_p[MDirection::N_Types][MDirection::N_Types]; // Object to ensure safe multi-threaded lazy single initialization static std::once_flag theirInitOnceFlag; //# Constructors // Copy constructor (not implemented) MCDirection(const MCDirection &other); // Assignment (not implemented) MCDirection &operator=(const MCDirection &other); //# Member functions // Create conversion function pointer virtual void getConvert(MConvertBase &mc, const MRBase &inref, const MRBase &outref); // Create help structures for Measure conversion routines virtual void initConvert(uInt which, MConvertBase &mc); // Delete the pointers used in the MeasConvert help structure cache virtual void clearConvert(); // Routines to convert directions from one reference frame to another virtual void doConvert(MeasValue &in, MRBase &inref, MRBase &outref, const MConvertBase &mc); // Conversion routine to cater for inheritance question void doConvert(MVDirection &in, MRBase &inref, MRBase &outref, const MConvertBase &mc); private: // Fill the global state. Called using theirInitOnce. static void doFillState(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MCDoppler.cc000066400000000000000000000076061476623553700211010ustar00rootroot00000000000000//# MCDoppler.cc: MDoppler conversion routines //# Copyright (C) 1995,1996,1997,1998,2000,2018 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Statics uInt MCDoppler::ToRef_p[N_Routes][3] = { {MDoppler::RADIO, MDoppler::RATIO, 0}, {MDoppler::Z, MDoppler::RATIO, 0}, {MDoppler::BETA, MDoppler::RATIO, 0}, {MDoppler::GAMMA, MDoppler::RATIO, 0}, {MDoppler::RATIO, MDoppler::RADIO, 0}, {MDoppler::RATIO, MDoppler::Z, 0}, {MDoppler::RATIO, MDoppler::BETA, 0}, {MDoppler::RATIO, MDoppler::GAMMA, 0} }; uInt MCDoppler::FromTo_p[MDoppler::N_Types][MDoppler::N_Types]; std::once_flag MCDoppler::theirInitOnceFlag; //# Constructors MCDoppler::MCDoppler() { std::call_once(theirInitOnceFlag, doFillState); } //# Destructor MCDoppler::~MCDoppler() { clearConvert(); } //# Operators //# Member functions void MCDoppler::getConvert(MConvertBase &mc, const MRBase &inref, const MRBase &outref) { Int iin = inref.getType(); Int iout = outref.getType(); Int tmp; while (iin != iout) { tmp = FromTo_p[iin][iout]; iin = ToRef_p[tmp][1]; mc.addMethod(tmp); initConvert(tmp, mc); } } void MCDoppler::clearConvert() { } //# Conversion routines void MCDoppler::initConvert(uInt which, MConvertBase &mc) { if (False) initConvert(which, mc); // Stop warning switch (which) { default: break; } } void MCDoppler::doConvert(MeasValue &in, MRBase &inref, MRBase &outref, const MConvertBase &mc) { doConvert(*(MVDoppler*)&in, inref, outref, mc); } void MCDoppler::doConvert(MVDoppler &in, MRBase &inref, MRBase &outref, const MConvertBase &mc) { if (False) {inref.getType(); outref.getType(); } // to stop warning Double t = (Double) in; for (Int i=0; i #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MCDoppler; class String; //# Typedefs // MDoppler conversion routines // // // // //
      • Measure class //
      • MCBase base class //
      • overall conversion class // // // // Measure, Convert and Doppler // // // // Contains state machinery and caching for actual conversions // // // // Conversion of a radio Doppler to an optical // // #include // #include // MDoppler radio(0.01); // A radio Doppler value // cout << "Doppler radio = " << radio << "; optical = " << // MDoppler::Convert(radio, MDoppler::OPTICAL)() << // Convert // endl; // // Setting up a conversion // // MDoppler::Convert to_opt(MDoppler::RADIO, MDoppler::OPTICAL); // for (Double d=0; d<0.1; d += 0.005) { // cout << "radio = " << d << " to optical = " << // to_opt(d) << endl; // // // // // // // // class MCDoppler : public MCBase { public: //# Friends // Conversion of data friend class MeasConvert; //# Constructors // Default constructor MCDoppler(); //# Destructor ~MCDoppler(); //# Member functions // Show the state of the conversion engine (mainly for debugging purposes) static String showState(); private: //# Enumerations // The list of actual routines provided. // Each AA_BB in the list points to routine // that can be used in the FromTo list in the getConvert routine. // In addition the type to which each is converted should be in the // ToRef array, again in the proper order. enum Routes { RADIO_RATIO, Z_RATIO, BETA_RATIO, GAMMA_RATIO, RATIO_RADIO, RATIO_Z, RATIO_BETA, RATIO_GAMMA, N_Routes }; //# Typedefs //# Operators //# General Member Functions //# Enumerations //# Cached Data //# State machine data // Transition list static uInt ToRef_p[N_Routes][3]; // Transition matrix static uInt FromTo_p[MDoppler::N_Types][MDoppler::N_Types]; // Object to ensure safe multi-threaded lazy single initialization static std::once_flag theirInitOnceFlag; //# Member functions // Create conversion function pointer virtual void getConvert(MConvertBase &mc, const MRBase &inref, const MRBase &outref); // Create help structures for Measure conversion routines virtual void initConvert(uInt which, MConvertBase &mc); // Delete the pointers used in the MeasConvert help structure cache virtual void clearConvert(); // Routine to convert Doppler from one reference frame to another virtual void doConvert(MeasValue &in, MRBase &inref, MRBase &outref, const MConvertBase &mc); // Conversion routine to cater for inheritance question void doConvert(MVDoppler &in, MRBase &inref, MRBase &outref, const MConvertBase &mc); private: // Fill the global state. Called using theirInitOnce. static void doFillState(); }; /* static class MCDoppler_initializer { public: MCDoppler_initializer( ) { if ( ! initialized ) { initialized = true; MutexedInit init(MCDoppler::doFillState); init.exec( ); } } private: static bool initialized; } _local_static_MCDoppler_init; */ } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MCEarthMagnetic.cc000066400000000000000000000316771476623553700222140ustar00rootroot00000000000000//# MCEarthMagnetic.cc: MEarthMagnetic conversion routines //# Copyright (C) 1998-2002,2004,2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Statics uInt MCEarthMagnetic::ToRef_p[N_Routes][3] = { {MEarthMagnetic::GALACTIC, MEarthMagnetic::J2000, 0}, {MEarthMagnetic::GALACTIC, MEarthMagnetic::B1950, 2}, {MEarthMagnetic::J2000, MEarthMagnetic::GALACTIC, 0}, {MEarthMagnetic::B1950, MEarthMagnetic::GALACTIC, 2}, {MEarthMagnetic::J2000, MEarthMagnetic::B1950, 2}, {MEarthMagnetic::B1950, MEarthMagnetic::J2000, 2}, {MEarthMagnetic::J2000, MEarthMagnetic::JMEAN, 0}, {MEarthMagnetic::B1950, MEarthMagnetic::BMEAN, 2}, {MEarthMagnetic::JMEAN, MEarthMagnetic::J2000, 0}, {MEarthMagnetic::JMEAN, MEarthMagnetic::JTRUE, 0}, {MEarthMagnetic::BMEAN, MEarthMagnetic::B1950, 2}, {MEarthMagnetic::BMEAN, MEarthMagnetic::BTRUE, 2}, {MEarthMagnetic::JTRUE, MEarthMagnetic::JMEAN, 0}, {MEarthMagnetic::BTRUE, MEarthMagnetic::BMEAN, 2}, {MEarthMagnetic::J2000, MEarthMagnetic::JNAT, 0}, {MEarthMagnetic::JNAT, MEarthMagnetic::J2000, 0}, {MEarthMagnetic::B1950, MEarthMagnetic::APP, 2}, {MEarthMagnetic::APP, MEarthMagnetic::B1950, 2}, {MEarthMagnetic::APP, MEarthMagnetic::TOPO, 0}, {MEarthMagnetic::HADEC, MEarthMagnetic::AZEL, 0}, {MEarthMagnetic::HADEC, MEarthMagnetic::AZELGEO, 0}, {MEarthMagnetic::AZEL, MEarthMagnetic::HADEC, 0}, {MEarthMagnetic::AZELGEO, MEarthMagnetic::HADEC, 0}, {MEarthMagnetic::HADEC, MEarthMagnetic::TOPO, 0}, {MEarthMagnetic::AZEL, MEarthMagnetic::AZELSW, 0}, {MEarthMagnetic::AZELGEO, MEarthMagnetic::AZELSWGEO, 0}, {MEarthMagnetic::AZELSW, MEarthMagnetic::AZEL, 0}, {MEarthMagnetic::AZELSWGEO, MEarthMagnetic::AZELGEO, 0}, {MEarthMagnetic::APP, MEarthMagnetic::JNAT, 0}, {MEarthMagnetic::JNAT, MEarthMagnetic::APP, 0}, {MEarthMagnetic::J2000, MEarthMagnetic::ECLIPTIC, 0}, {MEarthMagnetic::ECLIPTIC, MEarthMagnetic::J2000, 0}, {MEarthMagnetic::JMEAN, MEarthMagnetic::MECLIPTIC, 0}, {MEarthMagnetic::MECLIPTIC, MEarthMagnetic::JMEAN, 0}, {MEarthMagnetic::JTRUE, MEarthMagnetic::TECLIPTIC, 0}, {MEarthMagnetic::TECLIPTIC, MEarthMagnetic::JTRUE, 0}, {MEarthMagnetic::GALACTIC, MEarthMagnetic::SUPERGAL, 0}, {MEarthMagnetic::SUPERGAL, MEarthMagnetic::GALACTIC, 0}, {MEarthMagnetic::ITRF, MEarthMagnetic::HADEC, 0}, {MEarthMagnetic::HADEC, MEarthMagnetic::ITRF, 0}, {MEarthMagnetic::TOPO, MEarthMagnetic::HADEC, 0}, {MEarthMagnetic::TOPO, MEarthMagnetic::APP, 0}, {MEarthMagnetic::ICRS, MEarthMagnetic::J2000, 0}, {MEarthMagnetic::J2000, MEarthMagnetic::ICRS, 0} }; uInt MCEarthMagnetic:: FromTo_p[MEarthMagnetic::N_Types][MEarthMagnetic::N_Types]; std::once_flag MCEarthMagnetic::theirInitOnceFlag; //# Constructors MCEarthMagnetic::MCEarthMagnetic() : MVPOS1(0), EFIELD(0), measMath() { std::call_once(theirInitOnceFlag, doFillState); } //# Destructor MCEarthMagnetic::~MCEarthMagnetic() { clearConvert(); } //# Operators //# Member functions void MCEarthMagnetic::getConvert(MConvertBase &mc, const MRBase &inref, const MRBase &outref) { Int iin = inref.getType(); Int iout = outref.getType(); if (iin != iout) { Bool iplan = (iin & MEarthMagnetic::EXTRA); Bool oplan = (iout & MEarthMagnetic::EXTRA); if (iplan) { mc.addMethod(MCEarthMagnetic::R_MODEL0); mc.addMethod((iin & ~MEarthMagnetic::EXTRA) + MCEarthMagnetic::R_IGRF); mc.addMethod(MCEarthMagnetic::R_MODEL); initConvert(MCEarthMagnetic::R_MODEL, mc); iin = MEarthMagnetic::ITRF; } if (oplan) iout = MEarthMagnetic::ITRF; Int tmp; while (iin != iout) { tmp = FromTo_p[iin][iout]; iin = ToRef_p[tmp][1]; mc.addMethod(tmp); initConvert(tmp, mc); } } } void MCEarthMagnetic::clearConvert() { delete MVPOS1; MVPOS1 = 0; delete EFIELD; EFIELD=0; } //# Conversion routines void MCEarthMagnetic::initConvert(uInt which, MConvertBase &mc) { if (False) initConvert(which, mc); // Stop warning if (!MVPOS1) MVPOS1 = new MVPosition(); switch (which) { case J2000_JMEAN: measMath.createPrecession(); mc.addFrameType(MeasFrame::EPOCH); break; case B1950_BMEAN: measMath.createPrecessionB1950(); mc.addFrameType(MeasFrame::EPOCH); break; case JMEAN_J2000: measMath.createPrecession(); mc.addFrameType(MeasFrame::EPOCH); break; case JMEAN_JTRUE: measMath.createNutation(); mc.addFrameType(MeasFrame::EPOCH); break; case BMEAN_B1950: measMath.createPrecessionB1950(); mc.addFrameType(MeasFrame::EPOCH); break; case BMEAN_BTRUE: measMath.createNutationB1950(); mc.addFrameType(MeasFrame::EPOCH); break; case JTRUE_JMEAN: measMath.createNutation(); mc.addFrameType(MeasFrame::EPOCH); break; case BTRUE_BMEAN: measMath.createNutationB1950(); mc.addFrameType(MeasFrame::EPOCH); break; case J2000_JNAT: measMath.createSolarPos(); mc.addFrameType(MeasFrame::EPOCH); break; case JNAT_APP: measMath.createAberration(); measMath.createPrecNutat(); mc.addFrameType(MeasFrame::EPOCH); break; case JNAT_J2000: measMath.createSolarPos(); mc.addFrameType(MeasFrame::EPOCH); break; case APP_JNAT: measMath.createAberration(); measMath.createPrecNutat(); mc.addFrameType(MeasFrame::EPOCH); break; case B1950_APP: measMath.createAberrationB1950(); measMath.createPrecNutatB1950(); mc.addFrameType(MeasFrame::EPOCH); break; case APP_B1950: measMath.createAberrationB1950(); measMath.createPrecNutatB1950(); mc.addFrameType(MeasFrame::EPOCH); break; case HADEC_ITRF: case ITRF_HADEC: case HADEC_AZEL: case AZEL_HADEC: case HADEC_AZELGEO: case AZELGEO_HADEC: case MECLIP_JMEAN: case JMEAN_MECLIP: case TECLIP_JTRUE: case JTRUE_TECLIP: mc.addFrameType(MeasFrame::POSITION); break; case TOPO_HADEC: case HADEC_TOPO: case APP_TOPO: case TOPO_APP: mc.addFrameType(MeasFrame::EPOCH); mc.addFrameType(MeasFrame::POSITION); break; default: break; } } void MCEarthMagnetic::doConvert(MeasValue &in, MRBase &inref, MRBase &outref, const MConvertBase &mc) { doConvert((MVEarthMagnetic &) in, inref, outref, mc); } void MCEarthMagnetic::doConvert(MVEarthMagnetic &in, MRBase &inref, MRBase &outref, const MConvertBase &mc) { Double g2, tdbTime; // Planetary aberration factor Double lengthP = 0; EarthField::EarthFieldTypes modID(EarthField::IGRF); measMath.initFrame(inref, outref); for (Int i=0; ioperator()(*MVPOS1); break; case R_IGRF: modID = EarthField::IGRF; break; default: break; } // switch } // for } String MCEarthMagnetic::showState() { std::call_once(theirInitOnceFlag, doFillState); return MCBase::showState(MCEarthMagnetic::FromTo_p[0], MEarthMagnetic::N_Types, MCEarthMagnetic::N_Routes, MCEarthMagnetic::ToRef_p); } void MCEarthMagnetic::doFillState() { MEarthMagnetic::checkMyTypes(); MCBase::makeState(FromTo_p[0], MEarthMagnetic::N_Types, N_Routes, ToRef_p); } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/MCEarthMagnetic.h000066400000000000000000000134241476623553700220440ustar00rootroot00000000000000//# MCEarthMagnetic.h: MEarthMagnetic conversion routines //# Copyright (C) 1998,1999,2000,2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MCEARTHMAGNETIC_H #define MEASURES_MCEARTHMAGNETIC_H //# Includes #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MCEarthMagnetic; class MVPosition; class EarthField; class String; //# Typedefs // MEarthMagnetic conversion routines // // // // //
      • Measure class //
      • MCBase base class //
      • overall conversion class // // // // Measure, Convert and EarthMagnetic // // // // Contains state machinery and caching for actual conversions // // // // See Measures module description for // conversion examples. // // // // // // //
      • Use MCDirection routines directly // class MCEarthMagnetic : public MCBase { public: //# Friends // Conversion of data friend class MeasConvert; //# Constructors // Default constructor MCEarthMagnetic(); //# Destructor ~MCEarthMagnetic(); //# Member functions // Show the state of the conversion engine (mainly for debugging purposes) static String showState(); private: //# Enumerations // The list of actual routines provided. // Each AA_BB in the list points to routine // that can be used in the FromTo list in the getConvert routine. // In addition the type to which each is converted should be in the // ToRef array, again in the proper order. enum Routes { GAL_J2000, GAL_B1950, J2000_GAL, B1950_GAL, J2000_B1950, B1950_J2000, J2000_JMEAN, B1950_BMEAN, JMEAN_J2000, JMEAN_JTRUE, BMEAN_B1950, BMEAN_BTRUE, JTRUE_JMEAN, BTRUE_BMEAN, J2000_JNAT, JNAT_J2000, B1950_APP, APP_B1950, APP_TOPO, HADEC_AZEL, HADEC_AZELGEO, AZEL_HADEC, AZELGEO_HADEC, HADEC_TOPO, AZEL_AZELSW, AZELGEO_AZELSWGEO, AZELSW_AZEL, AZELSWGEO_AZELGEO, APP_JNAT, JNAT_APP, J2000_ECLIP, ECLIP_J2000, JMEAN_MECLIP, MECLIP_JMEAN, JTRUE_TECLIP, TECLIP_JTRUE, GAL_SUPERGAL, SUPERGAL_GAL, ITRF_HADEC, HADEC_ITRF, TOPO_HADEC, TOPO_APP, ICRS_J2000, J2000_ICRS, N_Routes, // General for Models R_MODEL0, R_MODEL, // Individual models. Order should be the same as in MEarthMagnetic.h R_IGRF }; //# Typedefs //# Operators //# General Member Functions //# Enumerations //# Cached Data MVPosition *MVPOS1; EarthField *EFIELD; MeasMath measMath; //# State machine data // Transition list static uInt ToRef_p[N_Routes][3]; // Transition matrix static uInt FromTo_p[MEarthMagnetic::N_Types][MEarthMagnetic::N_Types]; // Object to ensure safe multi-threaded lazy single initialization static std::once_flag theirInitOnceFlag; //# Constructors // Copy constructor (not implemented) MCEarthMagnetic(const MCEarthMagnetic &other); // Assignment (not implemented) MCEarthMagnetic &operator=(const MCEarthMagnetic &other); //# Member functions // Create conversion function pointer virtual void getConvert(MConvertBase &mc, const MRBase &inref, const MRBase &outref); // Create help structures for Measure conversion routines virtual void initConvert(uInt which, MConvertBase &mc); // Delete the pointers used in the MeasConvert help structure cache virtual void clearConvert(); // Routines to convert EarthMagnetics from one reference frame to another virtual void doConvert(MeasValue &in, MRBase &inref, MRBase &outref, const MConvertBase &mc); // Conversion routine to cater for inheritance question void doConvert(MVEarthMagnetic &in, MRBase &inref, MRBase &outref, const MConvertBase &mc); private: // Fill the global state. Called using theirInitOnce. static void doFillState(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MCEpoch.cc000066400000000000000000000176601476623553700205330ustar00rootroot00000000000000//# MCEpoch.cc: MEpoch conversion routines //# Copyright (C) 1995-2001,2004,2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Statics uInt MCEpoch::ToRef_p[N_Routes][3] = { {MEpoch::LAST, MEpoch::GAST, 2}, {MEpoch::GAST, MEpoch::LAST, 2}, {MEpoch::LMST, MEpoch::GMST1, 2}, {MEpoch::GMST1, MEpoch::LMST, 2}, {MEpoch::GMST1, MEpoch::UT1, 2}, {MEpoch::UT1, MEpoch::GMST1, 2}, {MEpoch::GAST, MEpoch::UT1, 2}, {MEpoch::UT1, MEpoch::GAST, 2}, {MEpoch::UT1, MEpoch::UTC, 0}, {MEpoch::UTC, MEpoch::UT1, 0}, {MEpoch::UT1, MEpoch::UT2, 0}, {MEpoch::UT2, MEpoch::UT1, 0}, {MEpoch::UTC, MEpoch::TAI, 0}, {MEpoch::TAI, MEpoch::UTC, 0}, {MEpoch::TAI, MEpoch::TDT, 0}, {MEpoch::TDT, MEpoch::TAI, 0}, {MEpoch::TDT, MEpoch::TDB, 0}, {MEpoch::TDB, MEpoch::TDT, 0}, {MEpoch::TDT, MEpoch::TCG, 0}, {MEpoch::TCG, MEpoch::TDT, 0}, {MEpoch::TDB, MEpoch::TCB, 0}, {MEpoch::TCB, MEpoch::TDB, 0} }; uInt MCEpoch::FromTo_p[MEpoch::N_Types][MEpoch::N_Types]; std::once_flag MCEpoch::theirInitOnceFlag; //# Constructors MCEpoch::MCEpoch() : NUTATFROM(0), NUTATTO(0) { std::call_once(theirInitOnceFlag, doFillState); } //# Destructor MCEpoch::~MCEpoch() { clearConvert(); } //# Operators //# Member functions void MCEpoch::getConvert(MConvertBase &mc, const MRBase &inref, const MRBase &outref) { Int iin = inref.getType(); Bool iraze = (iin & MEpoch::RAZE); iin &= ~MEpoch::EXTRA; Int iout = outref.getType(); iout &= ~MEpoch::EXTRA; Int tmp; while (iin != iout) { tmp = FromTo_p[iin][iout]; iin = ToRef_p[tmp][1]; mc.addMethod(tmp); initConvert(tmp, mc); } if (iraze) { mc.addMethod(MCEpoch::RAZING); } } void MCEpoch::clearConvert() { delete NUTATFROM; NUTATFROM = 0; delete NUTATTO; NUTATTO = 0; } //# Conversion routines void MCEpoch::initConvert(uInt which, MConvertBase &mc) { if (False) initConvert(which, mc); // Stop warning switch (which) { case GAST_UT1: if (NUTATTO) delete NUTATTO; NUTATTO = new Nutation(Nutation::STANDARD); break; case UT1_GAST: if (NUTATFROM) delete NUTATFROM; NUTATFROM = new Nutation(Nutation::STANDARD); break; case LAST_GAST: case GAST_LAST: case LMST_GMST1: case GMST1_LMST: mc.addFrameType(MeasFrame::POSITION); break; default: break; } } void MCEpoch::doConvert(MeasValue &in, MRBase &inref, MRBase &outref, const MConvertBase &mc) { doConvert(*(MVEpoch*)&in, inref, outref, mc); } void MCEpoch::doConvert(MVEpoch &in, MRBase &inref, MRBase &outref, const MConvertBase &mc) { static MVEpoch mve6713(6713.); Double locLong, eqox, ut, tt, xx; for (Int i=0; i 1e-7 && i<10); } } break; case UT1_GMST1: { ut = in.get(); if (MeasTable::useIAU2000()) { in -= MeasTable::dUT1(in.get())/MeasData::SECinDAY; in += MeasTable::dUTC(in.get())/MeasData::SECinDAY; in += MeasTable::dTAI(in.get())/MeasData::SECinDAY; in += MeasTable::GMST00(ut, in.get())/(2.0*M_PI); } else { in += MeasTable::GMST0(ut)/MeasData::SECinDAY; } in += mve6713; } break; case GAST_UT1: { // Guess UT1 without equation of equinoxes ut = in.get(); ut += MeasTable::GMUT0(ut)*MeasData::JDCEN/MeasData::SECinDAY; ut -= 6713.; // Equation of equinoxes eqox = NUTATTO->eqox(ut); in -= eqox/C::circle; // GMST1 to UT1 ut = in.get(); in += MeasTable::GMUT0(ut)*MeasData::JDCEN/MeasData::SECinDAY; in -= mve6713; } break; case UT1_GAST: { // Make GMST1 ut = in.get(); in += MeasTable::GMST0(ut)/MeasData::SECinDAY; in += mve6713; // Equation of equinoxes eqox = NUTATFROM->eqox(ut); in += eqox/C::circle; } break; case UT1_UTC: in -= MeasTable::dUT1(in.get())/MeasData::SECinDAY; break; case UTC_UT1: in += MeasTable::dUT1(in.get())/MeasData::SECinDAY; break; case UT1_UT2: break; case UT2_UT1: break; case UTC_TAI: in += MeasTable::dUTC(in.get())/MeasData::SECinDAY; break; case TAI_UTC: in -= MeasTable::dUTC(in.get())/MeasData::SECinDAY; break; case TAI_TDT: in += MeasTable::dTAI(in.get())/MeasData::SECinDAY; break; case TDT_TAI: in -= MeasTable::dTAI(in.get())/MeasData::SECinDAY; break; case TDT_TDB: in += MeasTable::dTDT(in.get())/MeasData::SECinDAY; break; case TDB_TDT: in -= MeasTable::dTDT(in.get())/MeasData::SECinDAY; break; case TDT_TCG: break; case TCG_TDT: break; case TDB_TCB: in += MeasTable::dTDB(in.get())/MeasData::SECinDAY; break; case TCB_TDB: in -= MeasTable::dTDB(in.get())/MeasData::SECinDAY; break; case RAZING: in = in.getDay(); break; default: break; } // switch } //for } String MCEpoch::showState() { std::call_once(theirInitOnceFlag, doFillState); return MCBase::showState(MCEpoch::FromTo_p[0], MEpoch::N_Types, MCEpoch::N_Routes, MCEpoch::ToRef_p); } void MCEpoch::doFillState() { MEpoch::checkMyTypes(); MCBase::makeState(FromTo_p[0], MEpoch::N_Types, N_Routes, ToRef_p); } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/MCEpoch.h000066400000000000000000000123231476623553700203640ustar00rootroot00000000000000//# MCEpoch.h: MEpoch conversion routines //# Copyright (C) 1995,1996,1997,1998,1999,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MCEPOCH_H #define MEASURES_MCEPOCH_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MCEpoch; class Nutation; class String; //# Typedefs // MEpoch conversion routines // // // // //
      • Measure class //
      • MCBase base class //
      • overall conversion class // // // // Measure, Convert and Epoch // // // // Contains state machinery and caching for actual conversions // // // // Convert (with all steps explicit) a UTC to an IAT time. // // #include // #include // #include // cout << "TAI for UTC = MJD(50237.29): " << // MEpoch::Convert(MEpoch(MVEpoch(Quantity(50237.29, "d")), // MEpoch::Ref(MEpoch::UTC)), // MEpoch::Ref(MEpoch::TAI))() << // endl; // // // // // // // //
      • Nothing I know // class MCEpoch : public MCBase { public: //# Friends friend class MeasConvert; //# Constructors // Default constructor MCEpoch(); //# Destructor ~MCEpoch(); //# Member functions // Show the state of the conversion engine (mainly for debugging purposes) static String showState(); private: //# Enumerations // The list of actual routines provided. // Each AA_BB in the list points to routine // that can be used in the FromTo list in the getConvert routine. // In addition the type to which each is converted should be in the // ToRef array, again in the proper order. enum Routes { LAST_GAST, GAST_LAST, LMST_GMST1, GMST1_LMST, GMST1_UT1, UT1_GMST1, GAST_UT1, UT1_GAST, UT1_UTC, UTC_UT1, UT1_UT2, UT2_UT1, UTC_TAI, TAI_UTC, TAI_TDT, TDT_TAI, TDT_TDB, TDB_TDT, TDT_TCG, TCG_TDT, TDB_TCB, TCB_TDB, N_Routes, RAZING }; //# Typedefs //# Operators //# General Member Functions //# Enumerations //# Cached Data Nutation *NUTATFROM; Nutation *NUTATTO; //# State machine data // Transition list static uInt ToRef_p[N_Routes][3]; // Transition matrix static uInt FromTo_p[MEpoch::N_Types][MEpoch::N_Types]; // Object to ensure safe multi-threaded lazy single initialization static std::once_flag theirInitOnceFlag; //# Constructors // Copy constructor (not implemented) MCEpoch(const MCEpoch &other); // Assignment (not implemented) MCEpoch &operator=(const MCEpoch &other); //# Member functions // Create conversion function pointer virtual void getConvert(MConvertBase &mc, const MRBase &inref, const MRBase &outref); // Create help structures for Measure conversion routines virtual void initConvert(uInt which, MConvertBase &mc); // Delete the pointers used in the MeasConvert help structure cache virtual void clearConvert(); // Routine to convert time from one reference frame to another virtual void doConvert(MeasValue &in, MRBase &inref, MRBase &outref, const MConvertBase &mc); // Conversion routine to cater for inheritance question void doConvert(MVEpoch &in, MRBase &inref, MRBase &outref, const MConvertBase &mc); private: // Fill the global state. Called using theirInitOnce. static void doFillState(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MCFrame.cc000066400000000000000000000405571476623553700205300ustar00rootroot00000000000000//# MCFrame.cc: Measure frame calculations proxy //# Copyright (C) 1996-2000,2002,2003,2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // MCFrame class //# Constructors MCFrame::MCFrame(MeasFrame &inf) : myf(inf), epConvTDB(0), epTDBp(0), epConvUT1(0), epUT1p(0), epConvTT(0), epTTp(0), epConvLAST(0), epLASTp(0), posConvLong(0), posLongp(0), posITRFp(0), posConvLongGeo(0), posLongGeop(0), posGeop(0), dirConvJ2000(0), j2000Longp(0), dirJ2000p(0), dirConvB1950(0), b1950Longp(0), dirB1950p(0), dirConvApp(0), appLongp(0), dirAppp(0), radConvLSR(0), radLSRp(0) {;} // Destructor MCFrame::~MCFrame() { delete static_cast(epConvTDB); delete epTDBp; delete static_cast(epConvUT1); delete epUT1p; delete static_cast(epConvTT); delete epTTp; delete static_cast(epConvLAST); delete epLASTp; delete static_cast(posConvLong); delete posLongp; delete posITRFp; delete static_cast(posConvLongGeo); delete posLongGeop; delete posGeop; delete static_cast(dirConvJ2000); delete j2000Longp; delete dirJ2000p; delete static_cast(dirConvB1950); delete b1950Longp; delete dirB1950p; delete static_cast(dirConvApp); delete appLongp; delete dirAppp; delete static_cast(radConvLSR); delete radLSRp; } // Operators // General member functions void MCFrame::resetEpoch() { delete epTDBp; epTDBp = 0; delete epUT1p; epUT1p = 0; delete epTTp; epTTp = 0; delete epLASTp; epLASTp = 0; delete appLongp; appLongp = 0; delete dirAppp; dirAppp = 0; delete radLSRp; radLSRp = 0; } void MCFrame::resetPosition() { if (posLongp) { delete posLongp; posLongp = 0; delete posITRFp; posITRFp = 0; delete posLongGeop; posLongGeop = 0; delete posGeop; posGeop = 0; } if (epLASTp) { delete epLASTp; epLASTp = 0; } } void MCFrame::resetDirection() { if (j2000Longp) { delete j2000Longp; j2000Longp = 0; delete dirJ2000p; dirJ2000p = 0; } if (b1950Longp) { delete b1950Longp; b1950Longp = 0; delete dirB1950p; dirB1950p = 0; } if (appLongp) { delete appLongp; appLongp = 0; delete dirAppp; dirAppp = 0; } if (radLSRp) { delete radLSRp; radLSRp = 0; } } void MCFrame::resetRadialVelocity() { if (radLSRp) { delete radLSRp; radLSRp = 0; } } void MCFrame::resetComet() { } Bool MCFrame::getTDB(Double &tdb) { if (myf.epoch()) { if (!epTDBp) { epTDBp = new Double; *epTDBp = static_cast(epConvTDB)->operator() (*dynamic_cast(myf.epoch()->getData())). getValue().get(); } tdb = *epTDBp; return True; } tdb = 0.0; return False; } Bool MCFrame::getUT1(Double &tdb) { if (myf.epoch()) { if (!epUT1p) { epUT1p = new Double; *epUT1p = static_cast(epConvUT1)->operator() (*dynamic_cast(myf.epoch()->getData())). getValue().get(); } tdb = *epUT1p; return True; } tdb = 0.0; return False; } Bool MCFrame::getTT(Double &tdb) { if (myf.epoch()) { if (!epTTp) { epTTp = new Double; *epTTp = static_cast(epConvTT)->operator() (*dynamic_cast(myf.epoch()->getData())). getValue().get(); } tdb = *epTTp; return True; } tdb = 0.0; return False; } Bool MCFrame::getLong(Double &tdb) { if (myf.position()) { if (!posLongp) { posLongp = new Vector(3); posITRFp = new MVPosition; *posITRFp = static_cast(posConvLong)->operator() (*dynamic_cast(myf.position()->getData())). getValue(); *posLongp = posITRFp->get(); } tdb = MVAngle(posLongp->operator()(1))(-0.5); return True; } tdb = 0.0; return False; } Bool MCFrame::getLat(Double &tdb) { if (myf.position()) { if (!posLongp) { posLongp = new Vector(3); posITRFp = new MVPosition; *posITRFp = static_cast(posConvLong)->operator() (*dynamic_cast(myf.position()->getData())). getValue(); *posLongp = posITRFp->get(); } tdb = posLongp->operator()(2); return True; } tdb = 0.0; return False; } Bool MCFrame::getLatGeo(Double &tdb) { if (myf.position()) { if (!posLongGeop) { posLongGeop = new Vector(3); posGeop = new MVPosition; *posGeop = static_cast(posConvLongGeo)->operator() (*dynamic_cast(myf.position()->getData())). getValue(); *posLongGeop = posGeop->get(); } tdb = posLongGeop->operator()(2); return True; } tdb = 0.0; return False; } Bool MCFrame::getITRF(MVPosition &tdb) { if (myf.position()) { if (!posLongp) { posLongp = new Vector(3); posITRFp = new MVPosition; *posITRFp = static_cast(posConvLong)->operator() (*dynamic_cast(myf.position()->getData())). getValue(); *posLongp = posITRFp->get(); } tdb = *posITRFp; return True; } tdb = MVPosition(0.0); return False; } Bool MCFrame::getRadius(Double &tdb) { if (myf.position()) { if (!posLongp) { posLongp = new Vector(3); posITRFp = new MVPosition; *posITRFp = static_cast(posConvLong)->operator() (*dynamic_cast(myf.position()->getData())). getValue(); *posLongp = posITRFp->get(); } tdb = posLongp->operator()(0); return True; } tdb = 0.0; return False; } Bool MCFrame::getLAST(Double &tdb) { if (myf.epoch()) { if (!epLASTp) { epLASTp = new Double; *epLASTp = static_cast(epConvLAST)->operator() (*dynamic_cast(myf.epoch()->getData())). getValue().get(); } tdb = fmod(*epLASTp, 1.0); return True; } tdb = 0.0; return False; } Bool MCFrame::getLASTr(Double &tdb) { Bool tmp = MCFrame::getLAST(tdb); tdb *= C::circle; return tmp; } Bool MCFrame::getJ2000Long(Double &tdb) { if (myf.direction()) { if (!j2000Longp) { j2000Longp = new Vector(2); dirJ2000p = new MVDirection; *dirJ2000p = static_cast(dirConvJ2000)->operator() (*dynamic_cast(myf.direction()->getData())). getValue(); *j2000Longp = dirJ2000p->get(); } tdb = j2000Longp->operator()(0); return True; } tdb = 0.0; return False; } Bool MCFrame::getJ2000Lat(Double &tdb) { if (myf.direction()) { if (!j2000Longp) { j2000Longp = new Vector(2); dirJ2000p = new MVDirection; *dirJ2000p = static_cast(dirConvJ2000)->operator() (*dynamic_cast(myf.direction()->getData())). getValue(); *j2000Longp = dirJ2000p->get(); } tdb = j2000Longp->operator()(1); return True; } tdb = 0.0; return False; } Bool MCFrame::getJ2000(MVDirection &tdb) { if (myf.direction()) { if (!j2000Longp) { j2000Longp = new Vector(2); dirJ2000p = new MVDirection; *dirJ2000p = static_cast(dirConvJ2000)->operator() (*dynamic_cast(myf.direction()->getData())). getValue(); *j2000Longp = dirJ2000p->get(); } tdb = *dirJ2000p; return True; } tdb = MVDirection(0.0); return False; } Bool MCFrame::getB1950Long(Double &tdb) { if (myf.direction()) { if (!b1950Longp) { b1950Longp = new Vector(2); dirB1950p = new MVDirection; *dirB1950p = static_cast(dirConvB1950)->operator() (*dynamic_cast(myf.direction()->getData())). getValue(); *b1950Longp = dirB1950p->get(); } tdb = b1950Longp->operator()(0); return True; } tdb = 0.0; return False; } Bool MCFrame::getB1950Lat(Double &tdb) { if (myf.direction()) { if (!b1950Longp) { b1950Longp = new Vector(2); dirB1950p = new MVDirection; *dirB1950p = static_cast(dirConvB1950)->operator() (*dynamic_cast(myf.direction()->getData())). getValue(); *b1950Longp = dirB1950p->get(); } tdb = b1950Longp->operator()(1); return True; } tdb = 0.0; return False; } Bool MCFrame::getB1950(MVDirection &tdb) { if (myf.direction()) { if (!b1950Longp) { b1950Longp = new Vector(2); dirB1950p = new MVDirection; *dirB1950p = static_cast(dirConvB1950)->operator() (*dynamic_cast(myf.direction()->getData())). getValue(); *b1950Longp = dirB1950p->get(); } tdb = *dirB1950p; return True; } tdb = MVDirection(0.0); return False; } Bool MCFrame::getAppLong(Double &tdb) { if (myf.direction()) { if (!appLongp) { appLongp = new Vector(2); dirAppp = new MVDirection; *dirAppp = static_cast(dirConvApp)->operator() (*dynamic_cast(myf.direction()->getData())). getValue(); *appLongp = dirAppp->get(); } tdb = appLongp->operator()(0); return True; } tdb = 0.0; return False; } Bool MCFrame::getAppLat(Double &tdb) { if (myf.direction()) { if (!appLongp) { appLongp = new Vector(2); dirAppp = new MVDirection; *dirAppp = static_cast(dirConvApp)->operator() (*dynamic_cast(myf.direction()->getData())). getValue(); *appLongp = dirAppp->get(); } tdb = appLongp->operator()(1); return True; } tdb = 0.0; return False; } Bool MCFrame::getApp(MVDirection &tdb) { if (myf.direction()) { if (!appLongp) { appLongp = new Vector(2); dirAppp = new MVDirection; *dirAppp = static_cast(dirConvApp)->operator() (*dynamic_cast(myf.direction()->getData())). getValue(); *appLongp = dirAppp->get(); } tdb = *dirAppp; return True; } tdb = MVDirection(0.0); return False; } Bool MCFrame::getLSR(Double &tdb) { if (myf.radialVelocity()) { if (!radLSRp) { radLSRp = new Double; *radLSRp = static_cast(radConvLSR)->operator() (*dynamic_cast(myf.radialVelocity()-> getData())). getValue(); } tdb = *radLSRp; return True; } tdb = 0.0; return False; } Bool MCFrame::getCometType(uInt &tdb) { if (myf.comet()) { tdb = static_cast(myf.comet()->getType()); return True; } tdb = 0; return False; } Bool MCFrame::getComet(MVPosition &tdb) { if (myf.comet()) { Double x(0); if (getTDB(x) && myf.comet()->get(tdb, x)) return True; } tdb = MVPosition(0.0); return False; } void MCFrame::makeEpoch() { static const MEpoch::Ref REFTDB = MEpoch::Ref(MEpoch::TDB); static const MEpoch::Ref REFUT1 = MEpoch::Ref(MEpoch::UT1); static const MEpoch::Ref REFTT = MEpoch::Ref(MEpoch::TT); delete static_cast(epConvTDB); delete static_cast(epConvUT1); delete static_cast(epConvTT); epConvTDB = new MEpoch::Convert(*(myf.epoch()), REFTDB); epConvUT1 = new MEpoch::Convert(*(myf.epoch()), REFUT1); epConvTT = new MEpoch::Convert(*(myf.epoch()), REFTT); uInt locker = 0; // locking assurance if (epTDBp) { delete epTDBp; epTDBp = 0; } if (epUT1p) { delete epUT1p; epUT1p = 0; } if (epTTp) { delete epTTp; epTTp = 0; } myf.lock(locker); if (epConvLAST) { delete static_cast(epConvLAST); epConvLAST = 0; } epConvLAST = new MEpoch::Convert(*(myf.epoch()), MEpoch::Ref(MEpoch::LAST, this->myf)); myf.unlock(locker); if (epLASTp) { delete epLASTp; epLASTp = 0; } if (appLongp) { delete appLongp; appLongp = 0; delete dirAppp; dirAppp = 0; } if (radLSRp) { delete radLSRp; radLSRp = 0; } } void MCFrame::makePosition() { static const MPosition::Ref REFLONG = MPosition::Ref(MPosition::ITRF); delete static_cast(posConvLong); posConvLong = new MPosition::Convert(*(myf.position()), REFLONG); if (posLongp) { delete posLongp; posLongp = 0; delete posITRFp; posITRFp = 0; } if (epLASTp) { delete epLASTp; epLASTp = 0; } if (radLSRp) { delete radLSRp; radLSRp = 0; } static const MPosition::Ref REFGEO = MPosition::Ref(MPosition::WGS84); delete static_cast(posConvLongGeo); posConvLongGeo = new MPosition::Convert(*(myf.position()), REFGEO); if (posLongGeop) { delete posLongGeop; posLongGeop = 0; delete posGeop; posGeop = 0; } } void MCFrame::makeDirection() { static const MDirection::Ref REFJ2000 = MDirection::Ref(MDirection::J2000); uInt locker =0; myf.lock(locker); if (dirConvJ2000) { delete static_cast(dirConvJ2000); dirConvJ2000 = 0; } dirConvJ2000 = new MDirection::Convert(*(myf.direction()), MDirection::Ref(MDirection::J2000, this->myf)); myf.unlock(locker); static const MDirection::Ref REFB1950 = MDirection::Ref(MDirection::B1950); myf.lock(locker); if (dirConvB1950) { delete static_cast(dirConvB1950); dirConvB1950 = 0; } dirConvB1950 = new MDirection::Convert(*(myf.direction()), MDirection::Ref(MDirection::B1950, this->myf)); myf.unlock(locker); myf.lock(locker); if (dirConvApp) { delete static_cast(dirConvApp); dirConvApp = 0; } dirConvApp = new MDirection::Convert(*(myf.direction()), MDirection::Ref(MDirection::APP, this->myf)); myf.unlock(locker); if (j2000Longp) { delete j2000Longp; j2000Longp = 0; delete dirJ2000p; dirJ2000p = 0; } if (b1950Longp) { delete b1950Longp; b1950Longp = 0; delete dirB1950p; dirB1950p = 0; } if (appLongp) { delete appLongp; appLongp = 0; delete dirAppp; dirAppp = 0; } if (radLSRp) { delete radLSRp; radLSRp = 0; } } void MCFrame::makeRadialVelocity() { static const MRadialVelocity::Ref REFLSR = MRadialVelocity::Ref(MRadialVelocity::LSRK); delete static_cast(radConvLSR); radConvLSR = new MRadialVelocity::Convert(*(myf.radialVelocity()), REFLSR); if (radLSRp) { delete radLSRp; radLSRp = 0; } } void MCFrame::makeComet() {;} } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/MCFrame.h000066400000000000000000000153141476623553700203630ustar00rootroot00000000000000//# MCFrame.h: Measure frame calculations proxy //# Copyright (C) 1996-2003,2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MCFRAME_H #define MEASURES_MCFRAME_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MVDirection; class MVPosition; // // Measure frame calculations proxy // // // // // //
      • Measure class //
      • MeasFrame class // // // // From Measure and Frame // // // // The MeasFrame class contains the 'when // and where' of an observed Measure. Calculations to get the appropiate // value (e.g. the Earth's longitude) from this frame for conversions are // done in this class, together with all the caching of (intermediate) results // that can speed-up calculations.
        // The MCFrame class is used by the individual measure conversion classes // (see MCBase class).
        //
        // // // // MEpoch my_epoch(Quantity(MeasData::MJDB1950,"d")); // an epoch // MCFrame frame(my_epoch); // used in a frame // frame.set(obser); // add observatory (an MPosition) // MEpoch::Convert conv(my_epoch, MEPoch::Ref(MEpoch::LAST, frame)); // // The conv conversion engine will (transpararently) use the MCFrame // class in calls from MCEpoch (the time conversions), which will be called // by the MEpoch::Convert () operator. // // // // To separate the frame calculations from the Measure containers, to enable // e.g. Tables to have Measures. // // // // class MCFrame { public: //# Friends //# Constructors // Construct using the MeasFrame parent MCFrame(MeasFrame &inf); // Destructor ~MCFrame(); //# Operators //# General member functions // Reset Epoch value void resetEpoch(); // Reset Position value void resetPosition(); // Reset Direction value void resetDirection(); // Reset RadialVelocity value void resetRadialVelocity(); // Reset Comet void resetComet(); // Make full Epoch void makeEpoch(); // Make full Position void makePosition(); // Make full Direction void makeDirection(); // Make full RadialVelocity void makeRadialVelocity(); // Make full Comet void makeComet(); // Get TDB in days Bool getTDB(Double &tdb); // Get UT1 in days Bool getUT1(Double &tdb); // Get TT in days Bool getTT(Double &tdb); // Get the longitude (in rad) Bool getLong(Double &tdb); // Get the latitude (ITRF) (in rad) Bool getLat(Double &tdb); // Get the position Bool getITRF(MVPosition &tdb); // Get the geocentric position (in m) Bool getRadius(Double &tdb); // Get the geodetic latitude Bool getLatGeo(Double &tdb); // Get the LAST (in days) Bool getLAST(Double &tdb); // Get the LAST (in rad) Bool getLASTr(Double &tdb); // Get J2000 coordinates (direction cosines) and long/lat (rad) // Bool getJ2000(MVDirection &tdb); Bool getJ2000Long(Double &tdb); Bool getJ2000Lat(Double &tdb); // // Get B1950 coordinates (direction cosines) and long/lat (rad) // Bool getB1950(MVDirection &tdb); Bool getB1950Long(Double &tdb); Bool getB1950Lat(Double &tdb); // // Get apparent coordinates (direction cosines) and long/lat (rad) // Bool getApp(MVDirection &tdb); Bool getAppLong(Double &tdb); Bool getAppLat(Double &tdb); // // Get LSR radial velocity (m/s) Bool getLSR(Double &tdb); // Get Comet type Bool getCometType(uInt &tdb); // Get Comet position Bool getComet(MVPosition &tdb); private: //# Data // The belonging frame pointer MeasFrame myf; // The actual measure conversion values // // Conversion to TDB time (due to some (for me) unsolvable dependency // errors) // not the proper MeasConvert* here) void *epConvTDB; // TDB time Double *epTDBp; // Conversion to UT1 time void *epConvUT1; // UT1 time Double *epUT1p; // Conversion to TT time void *epConvTT; // TT time Double *epTTp; // Conversion to LAST time void *epConvLAST; // LAST time Double *epLASTp; // Conversion to ITRF longitude/latitude void *posConvLong; // Longitude Vector *posLongp; // Position MVPosition *posITRFp; // Conversion to geodetic longitude/latitude void *posConvLongGeo; // Latitude Vector *posLongGeop; // Position MVPosition *posGeop; // Conversion to J2000 void *dirConvJ2000; // Longitude Vector *j2000Longp; // J2000 coordinates MVDirection *dirJ2000p; // Conversion to B1950 void *dirConvB1950; // Longitude Vector *b1950Longp; // B1950 coordinates MVDirection *dirB1950p; // Conversion to apparent coordinates void *dirConvApp; // Longitude Vector *appLongp; // Apparent coordinates MVDirection *dirAppp; // Conversion to LSR radial velocity void *radConvLSR; // Radial velocity Double *radLSRp; // //# Member functions // Default constructor (not implemented) MCFrame(); // Copy constructor (not implemented) MCFrame(const MCFrame &other); // Copy assignment (not implemented) MCFrame &operator=(const MCFrame &other); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MCFrequency.cc000066400000000000000000000300121476623553700214200ustar00rootroot00000000000000//# MCFrequency.cc: MFrequency conversion routines //# Copyright (C) 1995-1998,2000-2003,2007,2009 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include namespace { inline void updatePosition(casacore::Double const angle0, casacore::Double const angle1, casacore::MVPosition &pos) { if (angle1 == 0) { pos(0) = std::cos(angle0); pos(1) = std::sin(angle0); pos(2) = 0; } else { auto const loc = std::cos(angle1); pos(0) = std::cos(angle0) * loc; pos(1) = std::sin(angle0) * loc; pos(2) = std::sin(angle1); } } } namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Statics uInt MCFrequency::ToRef_p[N_Routes][3] = { {MFrequency::LSRD, MFrequency::BARY, 0}, {MFrequency::BARY, MFrequency::LSRD, 0}, {MFrequency::BARY, MFrequency::GEO, 0}, {MFrequency::GEO, MFrequency::TOPO, 0}, {MFrequency::GEO, MFrequency::BARY, 0}, {MFrequency::TOPO, MFrequency::GEO, 0}, {MFrequency::LSRD, MFrequency::GALACTO, 0}, {MFrequency::GALACTO, MFrequency::LSRD, 0}, {MFrequency::LSRK, MFrequency::BARY, 0}, {MFrequency::BARY, MFrequency::LSRK, 0}, {MFrequency::BARY, MFrequency::LGROUP, 0}, {MFrequency::LGROUP, MFrequency::BARY, 0}, {MFrequency::BARY, MFrequency::CMB, 0}, {MFrequency::CMB, MFrequency::BARY, 0}, {MFrequency::REST, MFrequency::LSRK, 3}, {MFrequency::LSRK, MFrequency::REST, 3} }; uInt MCFrequency::FromTo_p[MFrequency::N_Types][MFrequency::N_Types]; std::once_flag MCFrequency::theirInitOnceFlag; //# Constructors MCFrequency::MCFrequency() : MVPOS1(0), MVDIR1(0), ABERFROM(0), ABERTO(0) { std::call_once(theirInitOnceFlag, doFillState); } //# Destructor MCFrequency::~MCFrequency() { clearConvert(); } //# Operators //# Member functions void MCFrequency::getConvert(MConvertBase &mc, const MRBase &inref, const MRBase &outref) { Int iin = inref.getType(); Int iout = outref.getType(); Int tmp; while (iin != iout) { if(iin == MFrequency::Undefined || iout == MFrequency::Undefined) throw(AipsError("Transformations to/from frame \"Undefined\" are not possible.")); tmp = FromTo_p[iin][iout]; iin = ToRef_p[tmp][1]; mc.addMethod(tmp); initConvert(tmp, mc); } } void MCFrequency::clearConvert() { delete MVPOS1; MVPOS1 = 0; delete MVDIR1; MVDIR1 = 0; delete ABERFROM; ABERFROM = 0; delete ABERTO; ABERTO = 0; } //# Conversion routines void MCFrequency::initConvert(uInt which, MConvertBase &mc) { if (False) initConvert(which, mc); // Stop warning if (!MVPOS1) MVPOS1 = new MVPosition(); if (!MVDIR1) MVDIR1 = new MVDirection(); switch (which) { case BARY_GEO: if (ABERFROM) delete ABERFROM; ABERFROM = new Aberration(Aberration::STANDARD); mc.addFrameType(MeasFrame::EPOCH); mc.addFrameType(MeasFrame::DIRECTION); break; case GEO_BARY: if (ABERTO) delete ABERTO; ABERTO = new Aberration(Aberration::STANDARD); mc.addFrameType(MeasFrame::EPOCH); mc.addFrameType(MeasFrame::DIRECTION); break; case LSRD_BARY: case BARY_LSRD: case LSRD_GALACTO: case GALACTO_LSRD: case LSRK_BARY: case BARY_LSRK: case LGROUP_BARY: case BARY_LGROUP: case CMB_BARY: case BARY_CMB: mc.addFrameType(MeasFrame::DIRECTION); break; case GEO_TOPO: case TOPO_GEO: mc.addFrameType(MeasFrame::EPOCH); mc.addFrameType(MeasFrame::DIRECTION); mc.addFrameType(MeasFrame::POSITION); break; case REST_LSRK: case LSRK_REST: mc.addFrameType(MeasFrame::DIRECTION); mc.addFrameType(MeasFrame::VELOCITY); break; default: break; } } void MCFrequency::doConvert(MeasValue &in, MRBase &inref, MRBase &outref, const MConvertBase &mc) { doConvert(*(MVFrequency*)&in, inref, outref, mc); } void MCFrequency::doConvert(MVFrequency &in, MRBase &inref, MRBase &outref, const MConvertBase &mc) { Double g1, g2, g3, lengthE, tdbTime; for (Int i=0; iputVector(vel); } else { *MVPOS1 = MVPosition(vel); } MFrequency::Ref::frameDirection(outref, inref). getJ2000(*MVDIR1); g1 = (*MVPOS1 * *MVDIR1) / C::c; g2 = in.getValue(); in = g2*sqrt((1+g1)/(1-g1)); } break; case BARY_LSRD: { auto const vel = MeasTable::velocityLSR(0); if (vel.nelements() == 3) { MVPOS1->putVector(vel); } else { *MVPOS1 = MVPosition(vel); } MFrequency::Ref::frameDirection(inref, outref). getJ2000(*MVDIR1); g1 = (*MVPOS1 * *MVDIR1) / C::c; g2 = in.getValue(); in = g2*sqrt((1-g1)/(1+g1)); } break; case BARY_GEO: { MFrequency::Ref::frameEpoch(outref, inref). getTDB(tdbTime); *MVPOS1 = ABERFROM->operator()(tdbTime); MFrequency::Ref::frameDirection(outref, inref). getJ2000(*MVDIR1); g1 = *MVPOS1 * *MVDIR1; g2 = in.getValue(); in = g2*sqrt((1+g1)/(1-g1)); } break; case GEO_TOPO: { MFrequency::Ref::frameEpoch(outref, inref). getLASTr(g1); MFrequency::Ref::framePosition(outref, inref). getRadius(lengthE); MFrequency::Ref::frameEpoch(outref, inref). getTDB(tdbTime); MFrequency::Ref::framePosition(outref, inref). getLat(g3); g2 = MeasTable::diurnalAber(lengthE, tdbTime); // *MVPOS1 = MVDirection(C::pi_2 + g1, 0.0); updatePosition(M_PI_2 + g1, 0.0, *MVPOS1); MVPOS1->readjust(g2 * cos(g3)); MFrequency::Ref::frameDirection(outref, inref). getApp(*MVDIR1); g1 = *MVPOS1 * *MVDIR1; g2 = in.getValue(); in = g2*sqrt((1+g1)/(1-g1)); } break; case GEO_BARY: { MFrequency::Ref::frameEpoch(inref, outref). getTDB(tdbTime); *MVPOS1 = ABERTO->operator()(tdbTime); MFrequency::Ref::frameDirection(outref, inref). getJ2000(*MVDIR1); g1 = *MVPOS1 * *MVDIR1; g2 = in.getValue(); in = g2*sqrt((1-g1)/(1+g1)); } break; case TOPO_GEO: { MFrequency::Ref::frameEpoch(inref, outref). getLASTr(g1); MFrequency::Ref::framePosition(inref, outref). getRadius(lengthE); MFrequency::Ref::frameEpoch(inref, outref). getTDB(tdbTime); MFrequency::Ref::framePosition(inref, outref). getLat(g3); g2 = MeasTable::diurnalAber(lengthE, tdbTime); // *MVPOS1 = MVDirection(C::pi_2 + g1, 0.0); updatePosition(M_PI_2 + g1, 0.0, *MVPOS1); MVPOS1->readjust(g2*cos(g3)); MFrequency::Ref::frameDirection(outref, inref). getApp(*MVDIR1); g1 = *MVPOS1 * *MVDIR1; g2 = in.getValue(); in = g2*sqrt((1-g1)/(1+g1)); } break; case LSRD_GALACTO: { auto const &vel = MeasTable::velocityLSRGal(0); if (vel.nelements() == 3) { MVPOS1->putVector(vel); } else { *MVPOS1 = MVPosition(vel); } MFrequency::Ref::frameDirection(inref, outref). getJ2000(*MVDIR1); g1 = (*MVPOS1 * *MVDIR1) / C::c; g2 = in.getValue(); in = g2*sqrt((1-g1)/(1+g1)); } break; case GALACTO_LSRD: { auto const &vel = MeasTable::velocityLSRGal(0); if (vel.nelements() == 3) { MVPOS1->putVector(vel); } else { *MVPOS1 = MVPosition(vel); } MFrequency::Ref::frameDirection(outref, inref). getJ2000(*MVDIR1); g1 = (*MVPOS1 * *MVDIR1) / C::c; g2 = in.getValue(); in = g2*sqrt((1+g1)/(1-g1)); } break; case LSRK_BARY: { auto const &vel = MeasTable::velocityLSRK(0); if (vel.nelements() == 3) { MVPOS1->putVector(vel); } else { *MVPOS1 = MVPosition(vel); } MFrequency::Ref::frameDirection(outref, inref). getJ2000(*MVDIR1); g1 = (*MVPOS1 * *MVDIR1) / C::c; g2 = in.getValue(); in = g2*sqrt((1+g1)/(1-g1)); } break; case BARY_LSRK: { auto const &vel = MeasTable::velocityLSRK(0); if (vel.nelements() == 3) { MVPOS1->putVector(vel); } else { *MVPOS1 = MVPosition(vel); } MFrequency::Ref::frameDirection(inref, outref). getJ2000(*MVDIR1); g1 = (*MVPOS1 * *MVDIR1) / C::c; g2 = in.getValue(); in = g2*sqrt((1-g1)/(1+g1)); } break; case LGROUP_BARY: { auto const &vel = MeasTable::velocityLGROUP(0); if (vel.nelements() == 3) { MVPOS1->putVector(vel); } else { *MVPOS1 = MVPosition(vel); } MFrequency::Ref::frameDirection(outref, inref). getJ2000(*MVDIR1); g1 = (*MVPOS1 * *MVDIR1) / C::c; g2 = in.getValue(); in = g2*sqrt((1+g1)/(1-g1)); } break; case BARY_LGROUP: { auto const &vel = MeasTable::velocityLGROUP(0); if (vel.nelements() == 3) { MVPOS1->putVector(vel); } else { *MVPOS1 = MVPosition(vel); } MFrequency::Ref::frameDirection(inref, outref). getJ2000(*MVDIR1); g1 = (*MVPOS1 * *MVDIR1) / C::c; g2 = in.getValue(); in = g2*sqrt((1-g1)/(1+g1)); } break; case CMB_BARY: { auto const &vel = MeasTable::velocityCMB(0); if (vel.nelements() == 3) { MVPOS1->putVector(vel); } else { *MVPOS1 = MVPosition(vel); } MFrequency::Ref::frameDirection(outref, inref). getJ2000(*MVDIR1); g1 = (*MVPOS1 * *MVDIR1) / C::c; g2 = in.getValue(); in = g2*sqrt((1+g1)/(1-g1)); } break; case BARY_CMB: { auto const &vel = MeasTable::velocityCMB(0); if (vel.nelements() == 3) { MVPOS1->putVector(vel); } else { *MVPOS1 = MVPosition(vel); } MFrequency::Ref::frameDirection(inref, outref). getJ2000(*MVDIR1); g1 = (*MVPOS1 * *MVDIR1) / C::c; g2 = in.getValue(); in = g2*sqrt((1-g1)/(1+g1)); } break; case REST_LSRK: MFrequency::Ref::frameRadialVelocity(inref, outref). getLSR(g1); MFrequency::Ref::frameDirection(inref, outref). getJ2000(*MVDIR1); g1 /= C::c; g2 = in.getValue(); in = g2*sqrt((1-g1)/(1+g1)); break; case LSRK_REST: MFrequency::Ref::frameRadialVelocity(inref, outref). getLSR(g1); MFrequency::Ref::frameDirection(inref, outref). getJ2000(*MVDIR1); g1 /= C::c; g2 = in.getValue(); in = g2*sqrt((1+g1)/(1-g1)); break; default: break; } // switch } //for } String MCFrequency::showState() { std::call_once(theirInitOnceFlag, doFillState); return MCBase::showState(MCFrequency::FromTo_p[0], MFrequency::N_Types, MCFrequency::N_Routes, MCFrequency::ToRef_p); } void MCFrequency::doFillState() { MFrequency::checkMyTypes(); MCBase::makeState(FromTo_p[0], MFrequency::N_Types, N_Routes, ToRef_p); } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/MCFrequency.h000066400000000000000000000124741476623553700212760ustar00rootroot00000000000000//# MCFrequency.h: MFrequency conversion routines //# Copyright (C) 1995,1996,1997,1998,1999,2000,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MCFREQUENCY_H #define MEASURES_MCFREQUENCY_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MCFrequency; class MDoppler; class MVPosition; class MVDirection; class Aberration; class String; //# Typedefs // MFrequency conversion routines // // // // //
      • Measure class //
      • MCBase base class //
      • overall conversion class // // // // Measure, Convert and Frequency // // // // Contains state machinery and caching for actual conversions // // // // Get the Doppler shift for an oberved HI frequency of 1380 MHz // // #include // #include // #include // cout << "Redshift for 1380 MHz: " << // MDoppler::Convert( MFrequency( Quantity(1380., "MHz"), // MFrequency::TOPO).toDoppler(QC::HI), // MDoppler::Z)() << endl; // // // // // // // // class MCFrequency : public MCBase { public: //# Friends // Conversion of data friend class MeasConvert; //# Constructors // Default constructor MCFrequency(); //# Destructor ~MCFrequency(); //# Member functions // Show the state of the conversion engine (mainly for debugging purposes) static String showState(); private: //# Enumerations // The list of actual routines provided. // Each AA_BB in the list points to routine // that can be used in the FromTo list in the getConvert routine. // In addition the type to which each is converted should be in the // ToRef array, again in the proper order. enum Routes { LSRD_BARY, BARY_LSRD, BARY_GEO, GEO_TOPO, GEO_BARY, TOPO_GEO, LSRD_GALACTO, GALACTO_LSRD, LSRK_BARY, BARY_LSRK, BARY_LGROUP, LGROUP_BARY, BARY_CMB, CMB_BARY, REST_LSRK, LSRK_REST, N_Routes }; //# Typedefs //# Operators //# General Member Functions //# Enumerations //# Cached Data MVPosition *MVPOS1; MVDirection *MVDIR1; Aberration *ABERFROM; Aberration *ABERTO; //# State machine data // Transition list static uInt ToRef_p[N_Routes][3]; // Transition matrix static uInt FromTo_p[MFrequency::N_Types][MFrequency::N_Types]; // Object to ensure safe multi-threaded lazy single initialization static std::once_flag theirInitOnceFlag; //# Constructors // Copy constructor (not implemented) MCFrequency(const MCFrequency &other); // Assignment (not implemented) MCFrequency &operator=(const MCFrequency &other); //# Member functions // Create conversion function pointer virtual void getConvert(MConvertBase &mc, const MRBase &inref, const MRBase &outref); // Create help structures for Measure conversion routines virtual void initConvert(uInt which, MConvertBase &mc); // Delete the pointers used in the MeasConvert help structure cache virtual void clearConvert(); // Routine to convert frequency from one reference frame to another virtual void doConvert(MeasValue &in, MRBase &inref, MRBase &outref, const MConvertBase &mc); // Conversion routine to cater for inheritance question void doConvert(MVFrequency &in, MRBase &inref, MRBase &outref, const MConvertBase &mc); private: // Fill the global state. Called using theirInitOnce. static void doFillState(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MCPosition.cc000066400000000000000000000114551476623553700212750ustar00rootroot00000000000000//# MCPosition.cc: MPosition conversion routines //# Copyright (C) 1995,1996,1997,1998,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Statics uInt MCPosition::ToRef_p[N_Routes][3] = { {MPosition::ITRF, MPosition::WGS84, 0}, {MPosition::WGS84, MPosition::ITRF, 0} }; uInt MCPosition::FromTo_p[MPosition::N_Types][MPosition::N_Types]; std::once_flag MCPosition::theirInitOnceFlag; //# Constructors MCPosition::MCPosition() : DVEC1(0) { std::call_once(theirInitOnceFlag, doFillState); } //# Destructor MCPosition::~MCPosition() { clearConvert(); } //# Operators //# Member functions void MCPosition::getConvert(MConvertBase &mc, const MRBase &inref, const MRBase &outref) { Int iin = inref.getType(); Int iout = outref.getType(); Int tmp; while (iin != iout) { tmp = FromTo_p[iin][iout]; iin = ToRef_p[tmp][1]; mc.addMethod(tmp); initConvert(tmp, mc); } } void MCPosition::clearConvert() { delete DVEC1; DVEC1 = 0; } //# Conversion routines void MCPosition::initConvert(uInt which, MConvertBase &mc) { if (False) initConvert(which, mc); // Stop warning if (!DVEC1) DVEC1 = new Vector(3); switch (which) { default: break; } } void MCPosition::doConvert(MeasValue &in, MRBase &inref, MRBase &outref, const MConvertBase &mc) { doConvert(*(MVPosition*)&in, inref, outref, mc); } void MCPosition::doConvert(MVPosition &in, MRBase &inref, MRBase &outref, const MConvertBase &mc) { if (False) { inref.getType(); outref.getType(); } // to stop warnings Double g1, g2, g3, g4; for (Int i=0; i= 0) ? M_PI_2 : - M_PI_2; } } while ( !nearAbs((*DVEC1)(2), g2, 1e-10)); (*DVEC1)(0) = d2/cos((*DVEC1)(2)) - MeasTable::WGS84(0) * g3; in = MVPosition(Quantity((*DVEC1)(0),"m"), (*DVEC1)(1), (*DVEC1)(2)); } break; case WGS84_ITRF: { // Equatorial radius g1 = MeasTable::WGS84(0); // Flattening g2 = MeasTable::WGS84(1); g2 = 1.0 - 1.0/g2; g2 *= g2; // h g4 = in.radius(); if (g4 != 0.0) { // aC g3 = (in(0)*in(0) + in(1)*in(1) + g2 * in(2)*in(2)); g3 = g1 * sqrt(1.0/g3); // aS g2 *= g3; // Apply g4 = in.get()[0]/g4; in(0) *= (g4 + g3); in(1) *= (g4 + g3); in(2) *= (g4 + g2); } else { // C g3 = g2; g3 = g1 * sqrt(1.0/g3); // S g2 *= g3; // Apply in(2) = g2; } } break; default: break; } //switch } // for } String MCPosition::showState() { std::call_once(theirInitOnceFlag, doFillState); return MCBase::showState(MCPosition::FromTo_p[0], MPosition::N_Types, MCPosition::N_Routes, MCPosition::ToRef_p); } void MCPosition::doFillState() { MPosition::checkMyTypes(); MCBase::makeState(FromTo_p[0], MPosition::N_Types, N_Routes, ToRef_p); } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/MCPosition.h000066400000000000000000000111571476623553700211360ustar00rootroot00000000000000//# MCPosition.h: MPosition conversion routines //# Copyright (C) 1995,1996,1997,1998,1999,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MCPOSITION_H #define MEASURES_MCPOSITION_H //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MCPosition; class String; //# Typedefs // MPosition conversion routines // // // // //
      • Measure class //
      • MCBase base class //
      • overall conversion class // // // // Measure, Convert and Position // // // // Contains state machinery and caching for actual conversions // // // // See Measure for conversion example. // // // // // // //
      • // class MCPosition : public MCBase { public: //# Friends // Conversion of data friend class MeasConvert; //# Constructors // Default constructor MCPosition(); //# Destructor ~MCPosition(); //# Member functions // Show the state of the conversion engine (mainly for debugging purposes) static String showState(); private: //# Enumerations // The list of actual routines provided. // Each AA_BB in the list points to routine // that can be used in the FromTo list in the getConvert routine. // In addition the type to which each is converted should be in the // ToRef array, again in the proper order. enum Routes { ITRF_WGS84, WGS84_ITRF, N_Routes }; //# Typedefs //# Operators //# General Member Functions //# Enumerations //# Cached Data Vector *DVEC1; //# State machine data // Transition list static uInt ToRef_p[N_Routes][3]; // Transition matrix static uInt FromTo_p[MPosition::N_Types][MPosition::N_Types]; // Object to ensure safe multi-threaded lazy single initialization static std::once_flag theirInitOnceFlag; //# Constructors // Copy constructor (not implemented) MCPosition(const MCPosition &other); // Assignment (not implemented) MCPosition &operator=(const MCPosition &other); //# Member functions // Create conversion function pointer virtual void getConvert(MConvertBase &mc, const MRBase &inref, const MRBase &outref); // Create help structures for Measure conversion routines virtual void initConvert(uInt which, MConvertBase &mc); // Delete the pointers used in the MeasConvert help structure cache virtual void clearConvert(); // Routine to do actual conversion virtual void doConvert(MeasValue &in, MRBase &inref, MRBase &outref, const MConvertBase &mc); // Conversion routine to cater for inheritance question void doConvert(MVPosition &in, MRBase &inref, MRBase &outref, const MConvertBase &mc); private: // Fill the global state. Called using theirInitOnce. static void doFillState(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MCRadialVelocity.cc000066400000000000000000000237441476623553700224100ustar00rootroot00000000000000//# MCRadialVelocity.cc: MRadialVelocity conversion routines //# Copyright (C) 1995-1998,2000,2001,2003,2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Statics uInt MCRadialVelocity::ToRef_p[N_Routes][3] = { {MRadialVelocity::LSRD, MRadialVelocity::BARY, 0}, {MRadialVelocity::BARY, MRadialVelocity::LSRD, 0}, {MRadialVelocity::BARY, MRadialVelocity::GEO, 0}, {MRadialVelocity::GEO, MRadialVelocity::TOPO, 2}, {MRadialVelocity::GEO, MRadialVelocity::BARY, 0}, {MRadialVelocity::TOPO, MRadialVelocity::GEO, 2}, {MRadialVelocity::LSRD, MRadialVelocity::GALACTO, 0}, {MRadialVelocity::GALACTO, MRadialVelocity::LSRD, 0}, {MRadialVelocity::LSRK, MRadialVelocity::BARY, 0}, {MRadialVelocity::BARY, MRadialVelocity::LSRK, 0}, {MRadialVelocity::BARY, MRadialVelocity::LGROUP, 0}, {MRadialVelocity::LGROUP, MRadialVelocity::BARY, 0}, {MRadialVelocity::BARY, MRadialVelocity::CMB, 0}, {MRadialVelocity::CMB, MRadialVelocity::BARY, 0} }; uInt MCRadialVelocity:: FromTo_p[MRadialVelocity::N_Types][MRadialVelocity::N_Types]; std::once_flag MCRadialVelocity::theirInitOnceFlag; //# Constructors MCRadialVelocity::MCRadialVelocity() : MVPOS1(0), MVDIR1(0), ABERFROM(0), ABERTO(0) { std::call_once(theirInitOnceFlag, doFillState); } //# Destructor MCRadialVelocity::~MCRadialVelocity() { clearConvert(); } //# Operators //# Member functions void MCRadialVelocity::getConvert(MConvertBase &mc, const MRBase &inref, const MRBase &outref) { Int iin = inref.getType(); Int iout = outref.getType(); Int tmp; while (iin != iout) { tmp = FromTo_p[iin][iout]; iin = ToRef_p[tmp][1]; mc.addMethod(tmp); initConvert(tmp, mc); } } void MCRadialVelocity::clearConvert() { delete MVPOS1; MVPOS1 = 0; delete MVDIR1; MVDIR1 = 0; delete ABERFROM; ABERFROM = 0; delete ABERTO; ABERTO = 0; } //# Conversion routines void MCRadialVelocity::initConvert(uInt which, MConvertBase &mc) { if (False) initConvert(which, mc); // Stop warning if (!MVPOS1) MVPOS1 = new MVPosition(); if (!MVDIR1) MVDIR1 = new MVDirection(); switch (which) { case BARY_GEO: if (ABERFROM) delete ABERFROM; ABERFROM = new Aberration(Aberration::STANDARD); mc.addFrameType(MeasFrame::EPOCH); mc.addFrameType(MeasFrame::DIRECTION); break; case GEO_BARY: if (ABERTO) delete ABERTO; ABERTO = new Aberration(Aberration::STANDARD); mc.addFrameType(MeasFrame::EPOCH); mc.addFrameType(MeasFrame::DIRECTION); break; case LSRD_BARY: case BARY_LSRD: case LSRD_GALACTO: case GALACTO_LSRD: case LSRK_BARY: case BARY_LSRK: case LGROUP_BARY: case BARY_LGROUP: case CMB_BARY: case BARY_CMB: mc.addFrameType(MeasFrame::DIRECTION); break; case GEO_TOPO: case TOPO_GEO: mc.addFrameType(MeasFrame::EPOCH); mc.addFrameType(MeasFrame::DIRECTION); mc.addFrameType(MeasFrame::POSITION); break; default: break; } } void MCRadialVelocity::doConvert(MeasValue &in, MRBase &inref, MRBase &outref, const MConvertBase &mc) { doConvert(*(MVRadialVelocity*)&in, inref, outref, mc); } void MCRadialVelocity::doConvert(MVRadialVelocity &in, MRBase &inref, MRBase &outref, const MConvertBase &mc) { Double g1, g2, g3, lengthE, tdbTime; for (Int i=0; ioperator()(tdbTime); MRadialVelocity::Ref::frameDirection(outref, inref). getJ2000(*MVDIR1); g1 = *MVPOS1 * *MVDIR1; g2 = in.getValue()/C::c; in = (g2 - g1)/(1 - g2 * g1) * C::c; } break; case GEO_TOPO: { MRadialVelocity::Ref::frameEpoch(outref, inref). getLASTr(g1); MRadialVelocity::Ref::framePosition(outref, inref). getRadius(lengthE); MRadialVelocity::Ref::frameEpoch(outref, inref). getTDB(tdbTime); MRadialVelocity::Ref::framePosition(outref, inref). getLat(g3); g2 = MeasTable::diurnalAber(lengthE, tdbTime); *MVPOS1 = MVDirection(M_PI_2 + g1, 0.0); MVPOS1->readjust(g2 * cos(g3)); MRadialVelocity::Ref::frameDirection(outref, inref). getApp(*MVDIR1); g1 = *MVPOS1 * *MVDIR1; g2 = in.getValue()/C::c; in = (g2 - g1)/(1 - g2 * g1) * C::c; } break; case GEO_BARY: { MRadialVelocity::Ref::frameEpoch(inref, outref). getTDB(tdbTime); *MVPOS1 = ABERTO->operator()(tdbTime); MRadialVelocity::Ref::frameDirection(outref, inref). getJ2000(*MVDIR1); g1 = *MVPOS1 * *MVDIR1; g2 = in.getValue()/C::c; in = (g2 + g1)/(1 + g2 * g1) * C::c; } break; case TOPO_GEO: { MRadialVelocity::Ref::frameEpoch(inref, outref). getLASTr(g1); MRadialVelocity::Ref::framePosition(inref, outref). getRadius(lengthE); MRadialVelocity::Ref::frameEpoch(inref, outref). getTDB(tdbTime); MRadialVelocity::Ref::framePosition(inref, outref). getLat(g3); g2 = MeasTable::diurnalAber(lengthE, tdbTime); *MVPOS1 = MVDirection(M_PI_2 + g1, 0.0); MVPOS1->readjust(g2 * cos(g3)); MRadialVelocity::Ref::frameDirection(outref, inref). getApp(*MVDIR1); g1 = *MVPOS1 * *MVDIR1; g2 = in.getValue()/C::c; in = (g2 + g1)/(1 + g2 * g1) * C::c; } break; case LSRD_GALACTO: *MVPOS1 = MVPosition(MeasTable::velocityLSRGal(0)); MRadialVelocity::Ref::frameDirection(inref, outref). getJ2000(*MVDIR1); g1 = (*MVPOS1 * *MVDIR1) / C::c; g2 = in.getValue()/C::c; in = (g2 + g1)/(1 + g2 * g1) * C::c; break; case GALACTO_LSRD: *MVPOS1 = MVPosition(MeasTable::velocityLSRGal(0)); MRadialVelocity::Ref::frameDirection(outref, inref). getJ2000(*MVDIR1); g1 = (*MVPOS1 * *MVDIR1) / C::c; g2 = in.getValue()/C::c; in = (g2 - g1)/(1 - g2 * g1) * C::c; break; case LSRK_BARY: *MVPOS1 = MVPosition(MeasTable::velocityLSRK(0)); MRadialVelocity::Ref::frameDirection(outref, inref). getJ2000(*MVDIR1); g1 = (*MVPOS1 * *MVDIR1) / C::c; g2 = in.getValue()/C::c; in = (g2 - g1)/(1 - g2 * g1) * C::c; break; case BARY_LSRK: *MVPOS1 = MVPosition(MeasTable::velocityLSRK(0)); MRadialVelocity::Ref::frameDirection(inref, outref). getJ2000(*MVDIR1); g1 = (*MVPOS1 * *MVDIR1) / C::c; g2 = in.getValue()/C::c; in = (g2 + g1)/(1 + g2 * g1) * C::c; break; case LGROUP_BARY: *MVPOS1 = MVPosition(MeasTable::velocityLGROUP(0)); MRadialVelocity::Ref::frameDirection(outref, inref). getJ2000(*MVDIR1); g1 = (*MVPOS1 * *MVDIR1) / C::c; g2 = in.getValue()/C::c; in = (g2 - g1)/(1 - g2 * g1) * C::c; break; case BARY_LGROUP: *MVPOS1 = MVPosition(MeasTable::velocityLGROUP(0)); MRadialVelocity::Ref::frameDirection(inref, outref). getJ2000(*MVDIR1); g1 = (*MVPOS1 * *MVDIR1) / C::c; g2 = in.getValue()/C::c; in = (g2 + g1)/(1 + g2 * g1) * C::c; break; case CMB_BARY: *MVPOS1 = MVPosition(MeasTable::velocityCMB(0)); MRadialVelocity::Ref::frameDirection(outref, inref). getJ2000(*MVDIR1); g1 = (*MVPOS1 * *MVDIR1) / C::c; g2 = in.getValue()/C::c; in = (g2 - g1)/(1 - g2 * g1) * C::c; break; case BARY_CMB: *MVPOS1 = MVPosition(MeasTable::velocityCMB(0)); MRadialVelocity::Ref::frameDirection(inref, outref). getJ2000(*MVDIR1); g1 = (*MVPOS1 * *MVDIR1) / C::c; g2 = in.getValue()/C::c; in = (g2 + g1)/(1 + g2 * g1) * C::c; break; default: break; } // switch } // for } String MCRadialVelocity::showState() { std::call_once(theirInitOnceFlag, doFillState); return MCBase::showState(MCRadialVelocity::FromTo_p[0], MRadialVelocity::N_Types, MCRadialVelocity::N_Routes, MCRadialVelocity::ToRef_p); } void MCRadialVelocity::doFillState() { MRadialVelocity::checkMyTypes(); MCBase::makeState(FromTo_p[0], MRadialVelocity::N_Types, N_Routes, ToRef_p); } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/MCRadialVelocity.h000066400000000000000000000126311476623553700222430ustar00rootroot00000000000000//# MCRadialVelocity.h: MRadialVelocity conversion routines //# Copyright (C) 1995,1996,1997,1998,1999,2000,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MCRADIALVELOCITY_H #define MEASURES_MCRADIALVELOCITY_H //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MCRadialVelocity; class MDoppler; class MVPosition; class MVDirection; class Aberration; class String; //# Typedefs // MRadialVelocity conversion routines // // // // //
      • Measure class //
      • MCBase base class //
      • overall conversion class // // // // Measure, Convert and RadialVelocity // // // // Contains state machinery and cashing for actual conversions // // // Get the Doppler shift for an oberved HI RadialVelocity of 100 km/s // // #include // #include // #include // cout << "Redshift for 100 km/s: " << // MDoppler::Convert( MRadialVelocity( Quantity(100., "km/s"), // MRadialVelocity::TOPO).toDoppler(QC::HI), // MDoppler::Z)() << endl; // // // // // // // // class MCRadialVelocity : public MCBase { public: //# Friends // Conversion of data friend class MeasConvert; //# Constructors // Default constructor MCRadialVelocity(); //# Destructor ~MCRadialVelocity(); //# Member functions // Show the state of the conversion engine (mainly for debugging purposes) static String showState(); private: //# Enumerations // The list of actual routines provided. // Each AA_BB in the list points to routine // that can be used in the FromTo list in the getConvert routine. // In addition the type to which each is converted should be in the // ToRef array, again in the proper order. enum Routes { LSRD_BARY, BARY_LSRD, BARY_GEO, GEO_TOPO, GEO_BARY, TOPO_GEO, LSRD_GALACTO, GALACTO_LSRD, LSRK_BARY, BARY_LSRK, BARY_LGROUP, LGROUP_BARY, BARY_CMB, CMB_BARY, N_Routes }; //# Typedefs //# Operators //# General Member Functions //# Enumerations //# Cached Data MVPosition *MVPOS1; MVDirection *MVDIR1; Aberration *ABERFROM; Aberration *ABERTO; //# State machine data // Transition list static uInt ToRef_p[N_Routes][3]; // Transition matrix static uInt FromTo_p[MRadialVelocity::N_Types][MRadialVelocity::N_Types]; // Object to ensure safe multi-threaded lazy single initialization static std::once_flag theirInitOnceFlag; //# Constructors // Copy constructor (not implemented) MCRadialVelocity(const MCRadialVelocity &other); // Assignment (not implemented) MCRadialVelocity &operator=(const MCRadialVelocity &other); //# Member functions // Create conversion function pointer virtual void getConvert(MConvertBase &mc, const MRBase &inref, const MRBase &outref); // Create help structures for Measure conversion routines virtual void initConvert(uInt which, MConvertBase &mc); // Delete the pointers used in the MeasConvert help structure cache virtual void clearConvert(); // Routine to convert RadialVelocity from one reference frame to another virtual void doConvert(MeasValue &in, MRBase &inref, MRBase &outref, const MConvertBase &mc); // Conversion routine to cater for inheritance question void doConvert(MVRadialVelocity &in, MRBase &inref, MRBase &outref, const MConvertBase &mc); private: // Fill the global state. Called using theirInitOnce. static void doFillState(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MConvertBase.cc000066400000000000000000000031461476623553700215770ustar00rootroot00000000000000//# MConvertBase.cc: Conversion of Measures Base //# Copyright (C) 1995,1996,1997,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Destructor MConvertBase::~MConvertBase() {} //# Operators //# Member functions //# Global functions ostream &operator<<(ostream &os, const MConvertBase &mc) { mc.print(os); return os; } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/MConvertBase.h000066400000000000000000000163431476623553700214440ustar00rootroot00000000000000//# MConvertBase.h: Conversion of Measures Base //# Copyright (C) 1995,1996,1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MCONVERTBASE_H #define MEASURES_MCONVERTBASE_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class Unit; class MeasValue; class Measure; class MRBase; //# Typedefs //# Constants // Conversion of Measures Base // // // // //
      • Measure class //
      • MeasRef class //
      • Quantum class // // // // // // // MConvertBase can convert a Measure to the same type of Measure in a // different reference frame. The MConvertBase is a templated class, but // has typedefs for the allowed conversions, like MEpoch::Convert.
        // The basic operation is to create a MConvertBase with either of: //
          //
        • MEpoch::Convert(MEpoch, MEpoch::Ref), where the // MEpoch is a template for subsequent // conversions, i.e. it will remember the value and // the input reference frame. And the // MeasRef is the output reference class. //
        • MEpoch::Convert(MEpoch) with a subsequent setOut(MEpoch::Ref) //
        • MEpoch::Convert(MEpoch::Ref in, MEpoch::Ref out) is a template for // conversions from the input reference to the output reference. The // 'template' model used is the default value for the Measure, with // no units. //
        • MEpoch::Convert(Unit, MEpoch::Ref in, MEpoch::Ref out) is a // template for // conversions from the input reference to the output reference. The // 'template' model used is the default value for the Measure, with // the default units as specified. //
        • MEpoch::Convert() with a setModel(MEpoch) and setOut(). //
        // An empty MeasRef indicates no conversion
        . // The constructor, and set functions, analyse the 'template' Measure and the // output reference frame, and construct a pointer (in practice a list // of pointers to bypass the necessity of creating too many conversion // functions) to a conversion routine. Functionals will maybe used in // a later version in stead of a vector of function pointers. During the // implementation process, I have, for a variety of reasons, changed the // list of function pointers to a list of numbers, with the Measure having // just a doConvert() function containing an appropiate // switch statement.
        // Actual conversions are done with the () operator, which produces a new // MEpoch (or other Measure).
        // Possible arguments are (MVEpoch is used here generic, and indicates the // internal format of a Measure; possibly, to make sure distinction between // values with and without units is possible, even simple Measures will // have their own internal class format, e.g. MVDouble. This will also aid // in the possibility that I am still pursuing to have a fully dynamic // conversion possibility. However, to be able to use pointers to functions // in any reasonable way for all possible input and output types, a // multi-level approach is necessary, with all possible datatypes derived // from some MeasValue.): //
          //
        • (MEpoch, MEpoch::Ref): will create a new conversion method, and use // it to produce the result of converting the MEpoch to the specified // frame //
        • (MEpoch): will create a new conversion method from the // MEpoch to the MeasRef belonging to the MConvertBase //
        • (Quantity): will use the conversion chain deduced from the // MEpoch model in the definition of MConvertBase, and will convert the // Quantity //
        • (Quantum >) as previous //
        • (Double): will use the units (if present) as specified in the // MConvertBase object to construct the internal value // to be converted //
        • (Vector >): as previous //
        // Float versions will be produced if necessary.
        // The conversion analyser expects that all Measure classes have a set // of routines to do the actual analysing and conversion.
        // If the standard conversion is not sufficient, additional methods can be // added at the end of the list with the addMethod() member // function, or at the beginning of the list with insertMethod(). // The whole list can be cleared with clearMethod().
        // To ease the specification of the Method, each Measure has a typedef // (e.g. MEpoch_ConvType) specifying the Method type. //
        // // // See Measure for an example // // // // Conversion of Measures will in general be done on a series of values. // Separating the analysis of the calculations necessary for the conversion // from the actual conversion could speed up the process. // // // // class MConvertBase { public: //# Friends //# Constructors //# Destructor virtual ~MConvertBase(); //# Operators //# General Member Functions // Set a new model for the conversion virtual void setModel(const Measure &val) = 0; // Set a new model value only virtual void set(const MeasValue &val) = 0; // Set a new model unit only virtual void set(const Unit &inunit) = 0; // Add a method (Note: uInt should be an enum from the appropiate Measure) virtual void addMethod(uInt method) = 0; // Add a FrameTypes used (as specified in MeasFrame::FrameTypes) virtual void addFrameType(uInt tp) = 0; // Get number of methods virtual Int nMethod() const = 0; // Get method virtual uInt getMethod(uInt which) const = 0; // Print a conversion engine virtual void print(ostream &os) const = 0; private: //# Data //# Member functions }; //# Global functions // Global functions // // Output decalration ostream &operator<<( ostream &os, const MConvertBase &mc); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MCuvw.cc000066400000000000000000000416061476623553700203130ustar00rootroot00000000000000//# MCuvw.cc: Muvw conversion routines //# Copyright (C) 1998-2000,2002,2004,2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Statics uInt MCuvw::ToRef_p[N_Routes][3] = { {Muvw::GALACTIC, Muvw::J2000, 0}, {Muvw::GALACTIC, Muvw::B1950, 2}, {Muvw::J2000, Muvw::GALACTIC, 0}, {Muvw::B1950, Muvw::GALACTIC, 2}, {Muvw::J2000, Muvw::B1950, 2}, {Muvw::J2000, Muvw::B1950_VLA,2}, {Muvw::B1950, Muvw::J2000, 2}, {Muvw::B1950_VLA, Muvw::J2000, 2}, {Muvw::B1950, Muvw::B1950_VLA,0}, {Muvw::B1950_VLA, Muvw::B1950, 0}, {Muvw::J2000, Muvw::JMEAN, 0}, {Muvw::B1950, Muvw::BMEAN, 2}, {Muvw::JMEAN, Muvw::J2000, 0}, {Muvw::JMEAN, Muvw::JTRUE, 0}, {Muvw::BMEAN, Muvw::B1950, 2}, {Muvw::BMEAN, Muvw::BTRUE, 2}, {Muvw::JTRUE, Muvw::JMEAN, 0}, {Muvw::BTRUE, Muvw::BMEAN, 2}, {Muvw::J2000, Muvw::JNAT, 0}, {Muvw::JNAT, Muvw::J2000, 0}, {Muvw::B1950, Muvw::APP, 2}, {Muvw::APP, Muvw::B1950, 2}, {Muvw::APP, Muvw::TOPO, 0}, {Muvw::HADEC, Muvw::AZEL, 0}, {Muvw::HADEC, Muvw::AZELGEO, 0}, {Muvw::AZEL, Muvw::HADEC, 0}, {Muvw::AZELGEO, Muvw::HADEC, 0}, {Muvw::HADEC, Muvw::TOPO, 0}, {Muvw::AZEL, Muvw::AZELSW, 0}, {Muvw::AZELGEO, Muvw::AZELSWGEO,0}, {Muvw::AZELSW, Muvw::AZEL, 0}, {Muvw::AZELSWGEO, Muvw::AZELGEO, 0}, {Muvw::APP, Muvw::JNAT, 0}, {Muvw::JNAT, Muvw::APP, 0}, {Muvw::J2000, Muvw::ECLIPTIC, 0}, {Muvw::ECLIPTIC, Muvw::J2000, 0}, {Muvw::JMEAN, Muvw::MECLIPTIC,0}, {Muvw::MECLIPTIC, Muvw::JMEAN, 0}, {Muvw::JTRUE, Muvw::TECLIPTIC,0}, {Muvw::TECLIPTIC, Muvw::JTRUE, 0}, {Muvw::GALACTIC, Muvw::SUPERGAL, 0}, {Muvw::SUPERGAL, Muvw::GALACTIC, 0}, {Muvw::ITRF, Muvw::HADEC, 0}, {Muvw::HADEC, Muvw::ITRF, 0}, {Muvw::TOPO, Muvw::HADEC, 0}, {Muvw::TOPO, Muvw::APP, 0}, {Muvw::ICRS, Muvw::J2000, 0}, {Muvw::J2000, Muvw::ICRS, 0} }; uInt MCuvw::FromTo_p[Muvw::N_Types][Muvw::N_Types]; std::once_flag MCuvw::theirInitOnceFlag; //# Constructors MCuvw::MCuvw() : measMath() { std::call_once(theirInitOnceFlag, doFillState); } //# Destructor MCuvw::~MCuvw() { clearConvert(); } //# Operators //# Member functions void MCuvw::getConvert(MConvertBase &mc, const MRBase &inref, const MRBase &outref) { Int iin = inref.getType(); Int iout = outref.getType(); if (iin != iout) { Int tmp; while (iin != iout) { tmp = FromTo_p[iin][iout]; iin = ToRef_p[tmp][1]; mc.addMethod(tmp); initConvert(tmp, mc); } } } void MCuvw::clearConvert() { } //# Conversion routines void MCuvw::initConvert(uInt which, MConvertBase &mc) { if (False) initConvert(which, mc); // Stop warning switch (which) { case J2000_JMEAN: measMath.createPrecession(); break; case B1950_BMEAN: measMath.createPrecessionB1950(); break; case JMEAN_J2000: measMath.createPrecession(); break; case JMEAN_JTRUE: measMath.createPrecession(); measMath.createNutation(); break; case BMEAN_B1950: measMath.createPrecessionB1950(); break; case BMEAN_BTRUE: measMath.createPrecessionB1950(); measMath.createNutationB1950(); break; case JTRUE_JMEAN: measMath.createPrecession(); measMath.createNutation(); break; case BTRUE_BMEAN: measMath.createPrecessionB1950(); measMath.createNutationB1950(); break; case J2000_JNAT: measMath.createSolarPos(); break; case JNAT_APP: measMath.createAberration(); measMath.createPrecNutat(); measMath.createSolarPos(); break; case JNAT_J2000: measMath.createSolarPos(); break; case APP_JNAT: measMath.createAberration(); measMath.createPrecNutat(); break; case B1950_APP: measMath.createAberrationB1950(); measMath.createPrecNutatB1950(); break; case APP_B1950: measMath.createAberrationB1950(); measMath.createPrecNutatB1950(); break; case MECLIP_JMEAN: measMath.createPrecession(); break; case JMEAN_MECLIP: measMath.createPrecession(); break; case TECLIP_JTRUE: measMath.createPrecession(); break; case JTRUE_TECLIP: measMath.createPrecession(); break; default: break; } } void MCuvw::doConvert(MeasValue &in, MRBase &inref, MRBase &outref, const MConvertBase &mc) { doConvert((MVuvw &) in, inref, outref, mc); } void MCuvw::doConvert(MVuvw &in, MRBase &inref, MRBase &outref, const MConvertBase &mc) { Double g2; // Planetary aberration factor Double lengthP = 0; measMath.initFrame(inref, outref); for (Int i=0; i #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MCuvw; class String; //# Typedefs // Muvw conversion routines // // // // //
      • Measure class //
      • MCBase base class //
      • overall conversion class // // // // Measure, Convert and uvw // // // // Contains state machinery and caching for actual conversions // // // // See Measures module description for // conversion examples. // // // // // // //
      • Cater for EW baselines // class MCuvw : public MCBase { public: //# Friends // Conversion of data friend class MeasConvert; //# Constructors // Default constructor MCuvw(); //# Destructor ~MCuvw(); //# Member functions // Show the state of the conversion engine (mainly for debugging purposes) static String showState(); private: //# Enumerations // The list of actual routines provided. // Each AA_BB in the list points to routine // that can be used in the FromTo list in the getConvert routine. // In addition the type to which each is converted should be in the // ToRef array, again in the proper order. enum Routes { GAL_J2000, GAL_B1950, J2000_GAL, B1950_GAL, J2000_B1950, J2000_B1950_VLA, B1950_J2000, B1950_VLA_J2000, B1950_B1950_VLA, B1950_VLA_B1950, J2000_JMEAN, B1950_BMEAN, JMEAN_J2000, JMEAN_JTRUE, BMEAN_B1950, BMEAN_BTRUE, JTRUE_JMEAN, BTRUE_BMEAN, J2000_JNAT, JNAT_J2000, B1950_APP, APP_B1950, APP_TOPO, HADEC_AZEL, HADEC_AZELGEO, AZEL_HADEC, AZELGEO_HADEC, HADEC_TOPO, AZEL_AZELSW, AZELGEO_AZELSWGEO, AZELSW_AZEL, AZELSWGEO_AZELGEO, APP_JNAT, JNAT_APP, J2000_ECLIP, ECLIP_J2000, JMEAN_MECLIP, MECLIP_JMEAN, JTRUE_TECLIP, TECLIP_JTRUE, GAL_SUPERGAL, SUPERGAL_GAL, ITRF_HADEC, HADEC_ITRF, TOPO_HADEC, TOPO_APP, ICRS_J2000, J2000_ICRS, N_Routes }; //# Typedefs //# Operators //# General Member Functions //# Enumerations //# Cached Data // Calculation class MeasMath measMath; // Belonging direction MVDirection MVDIR1; //# State machine data // Transition list static uInt ToRef_p[N_Routes][3]; // Transition matrix static uInt FromTo_p[Muvw::N_Types][Muvw::N_Types]; // Object to ensure safe multi-threaded lazy single initialization static std::once_flag theirInitOnceFlag; //# Constructors // Copy constructor (not implemented) MCuvw(const MCuvw &other); // Assignment (not implemented) MCuvw &operator=(const MCuvw &other); //# Member functions // Create conversion function pointer virtual void getConvert(MConvertBase &mc, const MRBase &inref, const MRBase &outref); // Create help structures for Measure conversion routines virtual void initConvert(uInt which, MConvertBase &mc); // Delete the pointers used in the MeasConvert help structure cache virtual void clearConvert(); // Routines to convert uvws from one reference frame to another virtual void doConvert(MeasValue &in, MRBase &inref, MRBase &outref, const MConvertBase &mc); // Conversion routine to cater for inheritance question void doConvert(MVuvw &in, MRBase &inref, MRBase &outref, const MConvertBase &mc); // Get the correct belonging direction from the frame // void getAPP(); void getJ2000(); void getB1950(); // // Rotate from direction to pole void toPole(MVPosition &in); // Rotate from pole to direction void fromPole(MVPosition &in); private: // Fill the global state. Called using theirInitOnce. static void doFillState(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MDirection.cc000066400000000000000000000324561476623553700213120ustar00rootroot00000000000000//# MDirection.cc: A Measure: astronomical direction //# Copyright (C) 1995-2002,2004,2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors MDirection::MDirection() : MeasBase() {} MDirection::MDirection(const MVDirection &dt) : MeasBase(dt,MDirection::DEFAULT) {} MDirection::MDirection(const MVDirection &dt, const MDirection::Ref &rf) : MeasBase(dt,rf) {} MDirection::MDirection(const MVDirection &dt, MDirection::Types rf) : MeasBase(dt,rf) {} MDirection::MDirection(const Quantity &dt, const Quantity &dt1) : MeasBase(MVDirection(dt,dt1), MDirection::DEFAULT) {} MDirection::MDirection(const Quantity &dt, const Quantity &dt1, const MDirection::Ref &rf) : MeasBase(MVDirection(dt,dt1),rf) {} MDirection::MDirection(const Quantity &dt, const Quantity &dt1, MDirection::Types rf) : MeasBase(MVDirection(dt,dt1),rf) {} MDirection::MDirection(const Quantum > &dt) : MeasBase(MVDirection(dt), MDirection::DEFAULT) {} MDirection::MDirection(const Quantum > &dt, const MDirection::Ref &rf) : MeasBase(MVDirection(dt),rf) {} MDirection::MDirection(const Quantum > &dt, MDirection::Types rf) : MeasBase(MVDirection(dt),rf) {} MDirection::MDirection(const Measure *dt) : MeasBase(dt) {} MDirection::MDirection(const MeasValue *dt) : MeasBase(*(MVDirection*)dt, MDirection::DEFAULT) {} MDirection::MDirection(const MDirection::Ref &rf) : MeasBase(rf) {} MDirection::MDirection(MDirection::Types rf) : MeasBase(rf) {} //# Destructor MDirection::~MDirection() {} MDirection MDirection::makeMDirection (const String& sourceName) { // Look if it is a known moving source. MDirection::Types refType; if (MDirection::getType (refType, sourceName)) { // Only planetary objects are valid. if (refType > MDirection::N_Types && refType < MDirection::COMET) { return MDirection(refType); } } // Now see if it is a known standard source. MVDirection mvdir; Bool fnd = True; // Make it case-insensitive. String name(sourceName); name.upcase(); if (name == "ZENITH") { return MDirection (MVDirection(), MDirection::AZEL); } else if (name == "CASA") { mvdir = MVDirection (6.123487680622104, 1.0265153995604648); } else if (name == "CYGA") { mvdir = MVDirection (5.233686575770755, 0.7109409582180791); } else if (name == "TAUA") { mvdir = MVDirection (1.4596748493730913, 0.38422502335921294); } else if (name == "VIRA") { mvdir = MVDirection (3.276086511413598, 0.21626589533567378); } else if (name == "HERA") { mvdir = MVDirection (4.4119087330382163, 0.087135562905816893); } else if (name == "HYDA") { mvdir = MVDirection (2.4351466, -0.21110706); } else if (name == "PERA") { mvdir = MVDirection (0.87180363, 0.72451580); } else { fnd = False; } if (fnd) { return MDirection (mvdir, MDirection::J2000); } // Finally see if it exists in the Sources table. MDirection md; if (! MeasTable::Source (md, sourceName)) { throw AipsError ("MDirection: " + sourceName + " is an unknown source name"); } return md; } //# Operators //# Member functions const String &MDirection::tellMe() const { return MDirection::showMe(); } const String &MDirection::showMe() { static const String name("Direction"); return name; } void MDirection::assure(const Measure &in) { if (!dynamic_cast(&in)) { throw(AipsError("Illegal Measure type argument: " + MDirection::showMe())); } } MDirection::Types MDirection::castType(uInt tp) { MDirection::checkMyTypes(); if ((tp & MDirection::EXTRA) == 0) { AlwaysAssert(tp < MDirection::N_Types, AipsError); } else { AlwaysAssert((tp & ~MDirection::EXTRA) < (MDirection::N_Planets - MDirection::MERCURY), AipsError); } return static_cast(tp); } const String &MDirection::showType(MDirection::Types tp) { static const String tname[MDirection::N_Types] = { "J2000", "JMEAN", "JTRUE", "APP", "B1950", "B1950_VLA", "BMEAN", "BTRUE", "GALACTIC", "HADEC", "AZEL", "AZELSW", "AZELGEO", "AZELSWGEO", "JNAT", "ECLIPTIC", "MECLIPTIC", "TECLIPTIC", "SUPERGAL", "ITRF", "TOPO", "ICRS" }; static const String pname[MDirection::N_Planets - MDirection::MERCURY] = { "MERCURY", "VENUS", "MARS", "JUPITER", "SATURN", "URANUS", "NEPTUNE", "PLUTO", "SUN", "MOON", "COMET" }; MDirection::checkMyTypes(); if ((tp & MDirection::EXTRA) == 0) return tname[tp]; return pname[tp & ~MDirection::EXTRA]; } const String &MDirection::showType(uInt tp) { return MDirection::showType(MDirection::castType(tp)); } const String* MDirection::allMyTypes(Int &nall, Int &nextra, const uInt *&typ) { static const Int N_name = 35; static const Int N_extra = 11; static const String tname[N_name] = { "J2000", "JMEAN", "JTRUE", "APP", "B1950", "B1950_VLA", "BMEAN", "BTRUE", "GALACTIC", "HADEC", "AZEL", "AZELSW", "AZELNE", "AZELGEO", "AZELSWGEO", "AZELNEGEO", "JNAT", "ECLIPTIC", "MECLIPTIC", "TECLIPTIC", "SUPERGAL", "ITRF", "TOPO", "ICRS", "MERCURY", "VENUS", "MARS", "JUPITER", "SATURN", "URANUS", "NEPTUNE", "PLUTO", "SUN", "MOON", "COMET" }; static const uInt oname[N_name] = { MDirection::J2000, MDirection::JMEAN, MDirection::JTRUE, MDirection::APP, MDirection::B1950, MDirection::B1950_VLA, MDirection::BMEAN, MDirection::BTRUE, MDirection::GALACTIC, MDirection::HADEC, MDirection::AZEL, MDirection::AZELSW, MDirection::AZEL, MDirection::AZELGEO, MDirection::AZELSWGEO, MDirection::AZELGEO, MDirection::JNAT, MDirection::ECLIPTIC, MDirection::MECLIPTIC, MDirection::TECLIPTIC, MDirection::SUPERGAL, MDirection::ITRF, MDirection::TOPO, MDirection::ICRS, MDirection::MERCURY, MDirection::VENUS, MDirection::MARS, MDirection::JUPITER, MDirection::SATURN, MDirection::URANUS, MDirection::NEPTUNE, MDirection::PLUTO, MDirection::SUN, MDirection::MOON, MDirection::COMET }; MDirection::checkMyTypes(); nall = N_name; nextra = N_extra; typ = oname; return tname; } const String* MDirection::allTypes(Int &nall, Int &nextra, const uInt *&typ) const { return MDirection::allMyTypes(nall, nextra, typ); } void MDirection::checkTypes() const { MDirection::checkMyTypes(); } void MDirection::checkMyTypes() { // Multiple threads could execute this, but that is harmless. static Bool first(True); if (first) { first = False; Int nall, nex; const uInt *typ; const String *const tps = MDirection::allMyTypes(nall,nex, typ); MDirection::Types tp; for (Int i=0; i=nall) return False; else tp = static_cast(oname[i]); return True; } Bool MDirection::giveMe(MDirection::Ref &mr, const String &in) { MDirection::Types tp; if (MDirection::getType(tp, in)) mr = MDirection::Ref(tp); else { mr = MDirection::Ref(); return False; } return True; } MDirection::GlobalTypes MDirection::globalType(uInt tp) { static const MDirection::GlobalTypes oname[MDirection::N_Types] = { MDirection::GRADEC, MDirection::GRADEC, MDirection::GRADEC, MDirection::GRADEC, MDirection::GRADEC, MDirection::GRADEC, MDirection::GRADEC, MDirection::GRADEC, MDirection::GLONGLAT, MDirection::GHADEC, MDirection::GAZEL, MDirection::GAZEL, MDirection::GAZEL, MDirection::GAZEL, MDirection::GRADEC, MDirection::GLONGLAT, MDirection::GLONGLAT, MDirection::GLONGLAT, MDirection::GLONGLAT, MDirection::GRADEC, MDirection::GRADEC }; if ((tp & MDirection::EXTRA) != 0) tp = 0; AlwaysAssert(tp < MDirection::N_Types, AipsError); return oname[tp]; } Bool MDirection::setOffset(const Measure &in) { if (!dynamic_cast(&in)) return False; ref.set(in); return True; } Bool MDirection::setRefString(const String &in) { MDirection::Types tp; if (MDirection::getType(tp, in)) { ref.setType(tp); return True; } ref.setType(MDirection::DEFAULT); return False; } const String &MDirection::getDefaultType() const { return MDirection::showType(MDirection::DEFAULT); } String MDirection::getRefString() const { return MDirection::showType(ref.getType()); } Bool MDirection::isModel() const { return ((ref.getType() & MDirection::EXTRA) != 0); } Quantum > MDirection::getAngle() const { return (data.getAngle()); } Quantum > MDirection::getAngle(const Unit &inunit) const { return (data.getAngle(inunit)); } void MDirection::shift(const Quantum &lng, const Quantum &lat, Bool trueAngle) { data.shift(lng, lat, trueAngle); } void MDirection::shift(Double lng, Double lat, Bool trueAngle) { data.shift(lng, lat, trueAngle); } void MDirection::shiftLongitude(const Quantum &lng, Bool trueAngle) { data.shiftLongitude(lng, trueAngle); } void MDirection::shiftLongitude(Double lng, Bool trueAngle) { data.shiftLongitude(lng, trueAngle); } void MDirection::shiftLatitude(const Quantum &lat, Bool trueAngle) { data.shiftLatitude(lat, trueAngle); } void MDirection::shiftLatitude(Double lat, Bool trueAngle) { data.shiftLatitude(lat, trueAngle); } void MDirection::shift(const MVDirection &shft, Bool trueAngle) { data.shift(shft, trueAngle); } void MDirection::shiftAngle(const Quantum &off, const Quantum &pa) { data.shiftAngle(off, pa); } void MDirection::shiftAngle(Double off, Double pa) { data.shiftAngle(off, pa); } Measure *MDirection::clone() const { return (new MDirection(*this)); } String MDirection::toString() const { Quantity longitude = getValue().getLong("deg"); Quantity lat = getValue().getLat("deg"); Types type = castType(ref.getType()); String output; if ( type == J2000 || type == JMEAN || type == JTRUE || type == APP || type == B1950 || type == B1950_VLA || type == BMEAN || type == BTRUE ) { String ra = MVTime(longitude).string(MVTime::TIME, 12); String dec = MVAngle(abs(lat)).string(MVAngle::ANGLE_CLEAN, 11); dec.trim(); if (lat.getValue() < 0) { dec = "-" + dec; } output = ra + " " + dec; } else { String longStr = MVAngle(longitude).string(MVAngle::ANGLE, 11); String latStr = MVAngle(lat).string(MVAngle::ANGLE, 11); output = longStr + " " + latStr; } output += " " + showType(type); return output; } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/MDirection.h000066400000000000000000000344071476623553700211520ustar00rootroot00000000000000//# MDirection.h: A Measure: astronomical direction //# Copyright (C) 1995-2000,2002,2004,2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MDIRECTION_H #define MEASURES_MDIRECTION_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MDirection; class MCDirection; template class MeasConvert; template class ArrayMeasColumn; template class ScalarMeasColumn; //# Typedefs // // A Measure: astronomical direction // // // // // //
      • Measure class // // // // // // // MDirection forms a derived Measure class for a direction in space.
        // An MDirection can be generated from a // MVDirection or a pair of // Quantities specifying a longitudinal and a // latitudinal angle.
        // The different reference types that can be used for a Direction are: //
          //
        • MDirection::J2000 -- mean equator and equinox at J2000.0 (FK5) //
        • MDirection::JNAT --- geocentric natural frame //
        • MDirection::JMEAN -- mean equator and equinox at frame epoch //
        • MDirection::JTRUE -- true equator and equinox at frame epoch //
        • MDirection::APP ---- apparent geocentric position //
        • MDirection::B1950 -- mean epoch and ecliptic at B1950.0. The epoch // is taken from the frame epoch; or from the aipsrc variable // measures.b1950.d_epoch; or has default 2000.0 //
        • MDirection::B1950_VLA -- mean epoch(1979.9)) and ecliptic at B1950.0 //
        • MDirection::BMEAN -- mean equator and equinox at frame epoch //
        • MDirection::BTRUE -- true equator and equinox at frame epoch //
        • MDirection::GALACTIC -- galactic coordinates //
        • MDirection::HADEC -- topocentric HA and declination //
        • MDirection::AZEL --- topocentric Azimuth and Elevation (N through E) //
        • MDirection::AZELSW - topocentric Azimuth and Elevation (S through W) //
        • MDirection::AZELNE - topocentric Azimuth and Elevation (N through E) //
        • MDirection::AZELGEO --- geodetic Azimuth and Elevation (N through E) //
        • MDirection::AZELSWGEO - geodetic Azimuth and Elevation (S through W) //
        • MDirection::AZELNEGEO - geodetic Azimuth and Elevation (N through E) //
        • MDirection::ECLIPTC -- ecliptic for J2000 equator and equinox //
        • MDirection::MECLIPTIC -- ecliptic for mean equator of date //
        • MDirection::TECLIPTIC -- ecliptic for true equator of date //
        • MDirection::SUPERGAL -- supergalactic coordinates //
        • MDirection::ITRF -- coordinates wrt ITRF Earth frame //
        • MDirection::TOPO -- apparent topocentric position //
        • MDirection::ICRS -- International Celestial reference system //
        • MDirection::MERCURY -- the planet: has no data attached //
        • MDirection::VENUS //
        • MDirection::MARS //
        • MDirection::JUPITER //
        • MDirection::SATURN //
        • MDirection::URANUS //
        • MDirection::NEPTUNE //
        • MDirection::PLUTO //
        • MDirection::SUN //
        • MDirection::MOON //
        • MDirection::COMET -- solar system body: no coordinates attached, // only table //
        • MDirection::DEFAULT = J2000 //
        //

        // Conversion between the different types is done with the standard // MeasConvert class // (MDirection::Convert in this case).
        // For some conversion additional MeasFrame // information is essential. The following list specifies which information // is needed if the conversion goes to or from the different types: //

          //
        • Epoch: all but J2000, B1950, GALACTIC, SUPGAL, ECLIPTIC, ICRS //
        • Positiom: HADEC, AZEL, AZELGEO //
        // The conversion between B1950 and J2000 may have an Epoch. If none given // an epoch of 2000.0 is assumed for the conversion, unless an aipsrc // variable measures.b1950.d_epoch is given. // // Conversions are based on the IAU system of // precession and // nutation (with // IERS corrections if available); and on series expansions of the DE200 // planetary ephemeris (J system; for B sytem older expansions) for the // aberration and the // solar position.
        // The HADEC position has corrections for polar motion and the // equation of equinoxes; the AZEL will include Earth tides and // refraction at a later stage.
        // Note that conversion between B1950 and J2000 can only be approximate, and is // based on FK4 to FK5 conversion. The best conversion is to convert first // to an apparent position at the time of observation, and convert from there // to the other standard (the correct route will be followed).
        // Another problem can arise if the source has proper motion and/or radial // velocities. These should be taken into account. An // MCatalog class will maybe take care of that. // // The offset that can be specified in the MDirection::Ref is an MDirection // offset, and can not be used for specifying angular offsets. shift() // methods are available for these cases. // //

        // To aid in formatting of the angles without having to check all difference // referencetypes, the following global types are provided: //

          //
        • GRADEC for types that are probably expressed in HM,DM //
        • GHADEC for types that are probably expressed in +-HM,DM //
        • GAZEL for types that are probably expressed in +-deg,deg //
        • GLONGLAT for types that are probably expressed in deg,deg //
        // they can be obtained with the globalType() method. //
        // // // See Measures module description for // extensive examples. // // // // // // //
      • // class MDirection : public MeasBase > { public: //# Friends // Conversion of data friend class MeasConvert; //# Enumerations // Types of known MDirections // The order defines the order in the translation matrix FromTo // in the getConvert routine. Do not change the order without // changing the array. Additions should be made before N_types, and // an additional row and column should be coded in FromTo, and // in showType(). enum Types { J2000, JMEAN, JTRUE, APP, B1950, B1950_VLA, BMEAN, BTRUE, GALACTIC, HADEC, AZEL, AZELSW, AZELGEO, AZELSWGEO, JNAT, ECLIPTIC, MECLIPTIC, TECLIPTIC, SUPERGAL, ITRF, TOPO, ICRS, N_Types, // Planets. First one should be Mercury MERCURY = 32, VENUS, MARS, JUPITER, SATURN, URANUS, NEPTUNE, PLUTO, SUN, MOON, // Comet or other table-described solar system body COMET, N_Planets, // All extra bits EXTRA = 32, // Defaults DEFAULT=J2000, // Synonyms AZELNE=AZEL, AZELNEGEO=AZELGEO }; // Global types enum GlobalTypes { GRADEC, GHADEC, GAZEL, GLONGLAT, N_GTypes}; //# Typedefs // Measure value container for this class (i.e. MDirection::MVType) typedef MVDirection MVType; // Measure conversion routines for this class (i.e. MDirection::MCType) typedef MCDirection MCType; // Measure reference (i.e. MDirection::Ref) typedef MeasRef Ref; // Measure Convert (i.e. MDirection::Convert) typedef MeasConvert Convert; // Measure table Columns (e.g., MDirection::ScalarColumn) typedef ScalarMeasColumn ScalarColumn; typedef ArrayMeasColumn ArrayColumn; //# Constructors // In the following constructors and other functions, all // MeasRef can be replaced with simple Measure::TYPE // where no offsets or frames are needed in the reference. // Default constructor; generates the J2000 pole direction MDirection(); // Create from data and reference // MDirection(const MVDirection &dt); MDirection(const MVDirection &dt, const MDirection::Ref &rf); MDirection(const MVDirection &dt, MDirection::Types rf); MDirection(const Quantity &dt, const Quantity &dt1); MDirection(const Quantity &dt, const Quantity &dt1, const MDirection::Ref &rf); MDirection(const Quantity &dt, const Quantity &dt1, MDirection::Types rf); MDirection(const Quantum > &dt); MDirection(const Quantum > &dt, const MDirection::Ref &rf); MDirection(const Quantum > &dt, MDirection::Types rf); MDirection(const Measure *dt); MDirection(const MeasValue *dt); MDirection(const MDirection::Ref &rf); MDirection(MDirection::Types rf); // //# Destructor virtual ~MDirection(); // Make an MDirection object given the case-insensitive name of a // moving source (ZENITH, SUN, etc.), // or of a known standard source (CygA, etc.). static MDirection makeMDirection(const String& sourceName); //# Operators //# General Member Functions // Tell me your type ('Direction') // virtual const String &tellMe() const; static const String &showMe(); // // Assert you are a direction static void assure(const Measure &in); // Tell me the global type (like GRADEC) for tp (tp like MDirection::J2000) static MDirection::GlobalTypes globalType(uInt tp); // Translate reference code tp. The uInt version has a check for valid codes // (i.e. it is a safe cast). // //
      • AipsError in the uInt interface if illegal code given // // static MDirection::Types castType(uInt tp); static const String &showType(MDirection::Types tp); static const String &showType(uInt tp); // // Translate string to reference code // static Bool getType(MDirection::Types &tp, const String &in); Bool giveMe(MDirection::Ref &mr, const String &in); // // Set the offset in the reference (False if non-matching Measure) virtual Bool setOffset(const Measure &in); // Set the reference type to the specified String. False if illegal // string, reference set to DEFAULT. virtual Bool setRefString(const String &in); // Get the default reference type virtual const String &getDefaultType() const; // Get a list of all known reference codes. nall returns the number in list, // nextra the number of specials (like planets) that should be at // end of list). typ returns the list of corresponding types. // virtual const String* allTypes(Int &nall, Int &nextra, const uInt *&typ) const; static const String* allMyTypes(Int &nall, Int &nextra, const uInt *&typ); // // Check if all internal tables of types (both enum and String) are // complete and correct. This function is called automatically if and when // necessary. // //
      • AipsError if a (programming) error in the types. // // virtual void checkTypes() const; static void checkMyTypes(); // // Get the reference type (for records, including codes like R_) virtual String getRefString() const; // Tell me if you are a pure model (e.g. a planet) virtual Bool isModel() const; // Get Measure data // Quantum > getAngle() const; Quantum > getAngle(const Unit &inunit) const; // // Shift the direction in longitude (radians if Double) and/or latitude. // If the trueAngle switch is True, the longitude shift will be in // angular units perpendicular to the direction to pole, along a great // circle. See MVDirection // for more details. // void shift(const Quantum &lng, const Quantum &lat, Bool trueAngle=False); void shift(Double lng, Double lat, Bool trueAngle=False); void shiftLongitude(const Quantity &lng, Bool trueAngle=False); void shiftLongitude(Double lng, Bool trueAngle=False); void shiftLatitude(const Quantum &lat, Bool trueAngle=False); void shiftLatitude(Double lat, Bool trueAngle=False); void shift(const MVDirection &shft, Bool trueAngle=False); // // Shift over an angle off in the direction pa. pa is measured from North, // in the direction of increasing longitude. // See MVDirection // for implementation. // void shiftAngle(const Quantum &off, const Quantum &pa); void shiftAngle(Double off, Double pa); // // Make a copy // virtual Measure *clone() const; // // Convert to a String in astronomer-friendly format based on // reference frame String toString() const; private: //# Data //# Member functions }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MDoppler.cc000066400000000000000000000155421476623553700207740ustar00rootroot00000000000000//# MDoppler.cc: A Measure: Doppler shift //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors MDoppler::MDoppler() : MeasBase() {} MDoppler::MDoppler(const MVDoppler &dt) : MeasBase(dt,MDoppler::DEFAULT) {} MDoppler::MDoppler(const MVDoppler &dt, const MDoppler::Ref &rf) : MeasBase(dt,rf) {} MDoppler::MDoppler(const MVDoppler &dt, MDoppler::Types rf) : MeasBase(dt,rf) {} MDoppler::MDoppler(const Quantity &dt) : MeasBase(dt,MDoppler::DEFAULT) {} MDoppler::MDoppler(const Quantity &dt, const MDoppler::Ref &rf) : MeasBase(dt,rf) {} MDoppler::MDoppler(const Quantity &dt, MDoppler::Types rf) : MeasBase(dt,rf) {} MDoppler::MDoppler(const Measure *dt) : MeasBase(dt) {} MDoppler::MDoppler(const MeasValue *dt) : MeasBase(*(MVDoppler*)dt, MDoppler::DEFAULT) {} //# Destructor MDoppler::~MDoppler() {} //# Operators //# Member functions const String &MDoppler::tellMe() const { return MDoppler::showMe(); } const String &MDoppler::showMe() { static const String name("Doppler"); return name; } void MDoppler::assure(const Measure &in) { if (!dynamic_cast(&in)) { throw(AipsError("Illegal Measure type argument: " + MDoppler::showMe())); } } MDoppler::Types MDoppler::castType(uInt tp) { MDoppler::checkMyTypes(); AlwaysAssert(tp < MDoppler::N_Types, AipsError); return static_cast(tp); } const String &MDoppler::showType(MDoppler::Types tp) { static const String tname[MDoppler::N_Types] = { "RADIO", "OPTICAL", "RATIO", "TRUE", "GAMMA"}; MDoppler::checkMyTypes(); return tname[tp]; } const String &MDoppler::showType(uInt tp) { return MDoppler::showType(MDoppler::castType(tp)); } const String* MDoppler::allMyTypes(Int &nall, Int &nextra, const uInt *&typ) { static const Int N_name = 8; static const Int N_extra = 0; static const String tname[N_name] = { "RADIO", "Z", "RATIO", "BETA", "GAMMA", "OPTICAL", "TRUE", "RELATIVISTIC" }; static const uInt oname[N_name] = { MDoppler::RADIO, MDoppler::Z, MDoppler::RATIO, MDoppler::BETA, MDoppler::GAMMA, MDoppler::Z, MDoppler::BETA, MDoppler::BETA }; MDoppler::checkMyTypes(); nall = N_name; nextra = N_extra; typ = oname; return tname; } const String* MDoppler::allTypes(Int &nall, Int &nextra, const uInt *&typ) const { return MDoppler::allMyTypes(nall, nextra, typ); } void MDoppler::checkTypes() const { MDoppler::checkMyTypes(); } void MDoppler::checkMyTypes() { // Multiple threads could execute this, but that is harmless. static Bool first(True); if (first) { first = False; Int nall, nex; const uInt *typ; const String *const tps = MDoppler::allMyTypes(nall,nex, typ); MDoppler::Types tp; for (Int i=0; i=nall) return False; else tp = static_cast(oname[i]); return True; } Bool MDoppler::giveMe(MDoppler::Ref &mr, const String &in) { MDoppler::Types tp; if (MDoppler::getType(tp, in)) mr = MDoppler::Ref(tp); else { mr = MDoppler::Ref(); return False; } return True; } Bool MDoppler::setOffset(const Measure &in) { if (!dynamic_cast(&in)) return False; ref.set(in); return True; } Bool MDoppler::setRefString(const String &in) { MDoppler::Types tp; if (MDoppler::getType(tp, in)) { ref.setType(tp); return True; } ref.setType(MDoppler::DEFAULT); return False; } const String &MDoppler::getDefaultType() const { return MDoppler::showType(MDoppler::DEFAULT); } String MDoppler::getRefString() const { return MDoppler::showType(ref.getType()); } Quantity MDoppler::get(const Unit &un) const { return data.get(un); } Measure *MDoppler::clone() const { return (new MDoppler(*this)); } Vector MDoppler::shiftFrequency(const Vector &freq) const { Vector tmp(freq.nelements()); Double factor = sqrt((1-data.getValue())/(1+data.getValue())); for (uInt i=0; i > MDoppler::shiftFrequency(const Quantum > &freq) const { Vector tmp(freq.getValue().nelements()); tmp = freq.getValue(); Double factor = sqrt((1-data.getValue())/(1+data.getValue())); for (uInt i=0; i >(tmp, freq.getFullUnit()); } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/MDoppler.h000066400000000000000000000215061476623553700206330ustar00rootroot00000000000000//# MDoppler.h: A Measure: Doppler shift //# Copyright (C) 1995,1996,1997,1998,1999,2000,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MDOPPLER_H #define MEASURES_MDOPPLER_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MDoppler; class MCDoppler; template class MeasConvert; template class ArrayMeasColumn; template class ScalarMeasColumn; template class Quantum; //# Typedefs // // A Measure: Doppler shift // // // // // //
      • Measure class // // // // From Measure and Doppler // // // // MDoppler forms the derived Measure class for Doppler shifts.
        // An MDoppler can be generated from a simple value (or an // MVDoppler), which is then // interpreted as a Doppler ratio, and a reference, with a RADIO type // as default.
        // It can also be generated from a Quantity, where the interpretation // depends on the dimensionality of the Quantity: //
          //
        • None: a Doppler ratio //
        • Velocity: Doppler ratio calculated by dividing with c //
        // The different types of Doppler (with F = f/f0, the frequency ratio), // are: //
          //
        • MDoppler::Z (-1 + 1/F) //
        • MDoppler::RATIO (F) * //
        • MDoppler::RADIO (1 - F) //
        • MDoppler::OPTICAL == Z //
        • MDoppler::BETA ((1 - F2)/(1 + F2)) //
        • MDoppler::GAMMA ((1 + F2)/2F) * //
        • MDoppler::RELATIVISTIC == BETA (== v/c) //
        • MDoppler::DEFAULT == RADIO //
        // Note that the ones with an '*' have no real interpretation (although the // calculation will proceed) if given as // a velocity.
        //

        // Conversion between the different types is done with the standard // MeasConvert class // (MDoppler::Convert in this case).
        // // Dopplers can be created from an MFrequency // object, or from an MRadialVelocity // object.
        // // A shiftFrequency() method can shift frequencies. // // Dopplers do not need a reference frame. // // // // // Conversion of a radio Doppler to an optical // // MDoppler radio(0.01); // A radio Doppler value // cout << "Doppler radio = " << radio << "; optical = " << // MDoppler::Convert(radio, MDoppler::OPTICAL)() << // Convert // endl; // // Setting up a conversion // // MDoppler::Convert to_opt(MDoppler::RADIO, MDoppler::OPTICAL); // for (Double d=0; d<0.1; d += 0.005) { // cout << "radio = " << d << " to optical = " << // to_opt(d) << endl; // // // // // // // //

      • // class MDoppler : public MeasBase > { public: //# Friends // Conversion of data friend class MeasConvert; //# Enumerations // Types of known MDopplers // The order defines the order in the translation // matrix FromTo // in the getConvert routine. Do not change the order without // changing the array. Additions should be made before N_types, and // an additional row and column should be coded in FromTo, and // in showType(). enum Types { RADIO, Z, RATIO, BETA, GAMMA, N_Types, OPTICAL=Z, RELATIVISTIC=BETA, DEFAULT=RADIO }; //# Typedefs // Measure value container for this class (i.e. MDoppler::MVType) typedef MVDoppler MVType; // Measure conversion routines for this class (i.e. MDoppler::MCType) typedef MCDoppler MCType; // Measure reference (i.e. MDoppler::Ref) typedef MeasRef Ref; // Measure Convert (i.e. MDoppler::Convert) typedef MeasConvert Convert; // Measure table Columns (e.g., MDoppler::ScalarColumn) typedef ScalarMeasColumn ScalarColumn; typedef ArrayMeasColumn ArrayColumn; //# Constructors // In the following constructors and other functions, all // MeasRef can be replaced with simple Measure::TYPE // where no offsets or frames are needed in the reference. // Default constructor; generates a zero rest Doppler MDoppler(); // Create from data and reference // MDoppler(const MVDoppler &dt); MDoppler(const MVDoppler &dt, const MDoppler::Ref &rf); MDoppler(const MVDoppler &dt, MDoppler::Types rf); MDoppler(const Quantity &dt); MDoppler(const Quantity &dt, const MDoppler::Ref &rf); MDoppler(const Quantity &dt, MDoppler::Types rf); MDoppler(const Measure *dt); MDoppler(const MeasValue *dt); // //# Destructor virtual ~MDoppler(); //# Operators //# General Member Functions // Tell me your type // virtual const String &tellMe() const; static const String &showMe(); static void assure(const Measure &in); // // Translate reference code. The uInt version has a check for valid codes // (i.e. it is a safe cast). // //
      • AipsError in the uInt interface if illegal code given // // static MDoppler::Types castType(uInt tp); static const String &showType(MDoppler::Types tp); static const String &showType(uInt tp); // // Translate string to reference code // static Bool getType(MDoppler::Types &tp, const String &in); Bool giveMe(MDoppler::Ref &mr, const String &in); // // Set the offset in the reference (False if non-matching Measure) virtual Bool setOffset(const Measure &in); // Set the reference type to the specified String. False if illegal // string, reference set to DEFAULT. virtual Bool setRefString(const String &in); // Get the default reference type virtual const String &getDefaultType() const; // Get a list of all known reference codes. nall returns the number in list, // nextra the number of specials (like planets) that should be at // end of list). typ returns the list of corresponding types. // virtual const String* allTypes(Int &nall, Int &nextra, const uInt *&typ) const; static const String* allMyTypes(Int &nall, Int &nextra, const uInt *&typ); // // Check if all internal tables of types (both enum and String) are // complete and correct. This function is called automatically if and when // necessary. // //
      • AipsError if a (programming) error in the types. // // virtual void checkTypes() const; static void checkMyTypes(); // // Get the reference type (for records, including codes like R_) virtual String getRefString() const; // Get in specified units Quantity get(const Unit &un) const; // Shift the input frequencies to the output frequencies. In the case of // simple Double inputs, it is assumed that the values are linearly dependent // on frequency. I.e. frequencies given as wavelength or time cannot be used. // Vector shiftFrequency(const Vector &freq) const; Quantum > shiftFrequency(const Quantum > &freq) const; // // Make a copy // virtual Measure *clone() const; // private: //# Enumerations //# Data //# Member functions }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MEarthMagnetic.cc000066400000000000000000000215141476623553700220760ustar00rootroot00000000000000//# MEarthMagnetic.cc: A Measure: Magnetic field on Earth //# Copyright (C) 1995-1999,2000,2001,2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors MEarthMagnetic::MEarthMagnetic() : MeasBase() {} MEarthMagnetic::MEarthMagnetic(const MVEarthMagnetic &dt) : MeasBase(dt,MEarthMagnetic::DEFAULT) {} MEarthMagnetic::MEarthMagnetic(const MVEarthMagnetic &dt, const MEarthMagnetic::Ref &rf) : MeasBase(dt,rf) {} MEarthMagnetic::MEarthMagnetic(const MVEarthMagnetic &dt, MEarthMagnetic::Types rf) : MeasBase(dt,rf) {} MEarthMagnetic::MEarthMagnetic(const Measure *dt) : MeasBase(dt) {} MEarthMagnetic::MEarthMagnetic(const MeasValue *dt) : MeasBase(*(MVEarthMagnetic*)dt, MEarthMagnetic::DEFAULT) {} MEarthMagnetic::MEarthMagnetic(const MEarthMagnetic::Ref &rf) : MeasBase(rf) {} MEarthMagnetic::MEarthMagnetic(const MEarthMagnetic &other) : MeasBase (other) {} MEarthMagnetic &MEarthMagnetic::operator=(const MEarthMagnetic &other) { if (this != &other) { MeasBase &This = *this; const MeasBase &Other = other; This = Other; } return *this; } //# Destructor MEarthMagnetic::~MEarthMagnetic() {} //# Operators //# Member functions const String &MEarthMagnetic::tellMe() const { return MEarthMagnetic::showMe(); } const String &MEarthMagnetic::showMe() { static const String name("EarthMagnetic"); return name; } void MEarthMagnetic::assure(const Measure &in) { if (!dynamic_cast(&in)) { throw(AipsError("Illegal Measure type argument: " + MEarthMagnetic::showMe())); } } MEarthMagnetic::Types MEarthMagnetic::castType(uInt tp) { MEarthMagnetic::checkMyTypes(); if ((tp & MEarthMagnetic::EXTRA) == 0) { AlwaysAssert(tp < MEarthMagnetic::N_Types, AipsError); } else { AlwaysAssert((tp & ~MEarthMagnetic::EXTRA) < (MEarthMagnetic::N_Models - MEarthMagnetic::IGRF), AipsError); } return static_cast(tp); } const String &MEarthMagnetic::showType(MEarthMagnetic::Types tp) { static const String tname[MEarthMagnetic::N_Types] = { "J2000", "JMEAN", "JTRUE", "APP", "B1950", "BMEAN", "BTRUE", "GALACTIC", "HADEC", "AZEL", "AZELSW", "AZELGEO", "AZELSWGEO", "JNAT", "ECLIPTIC", "MECLIPTIC", "TECLIPTIC", "SUPERGAL", "ITRF", "TOPO", "ICRS" }; static const String pname[MEarthMagnetic::N_Models - MEarthMagnetic::IGRF] = { "IGRF" }; MEarthMagnetic::checkMyTypes(); if ((tp & MEarthMagnetic::EXTRA) == 0) return tname[tp]; return pname[tp & ~MEarthMagnetic::EXTRA]; } const String &MEarthMagnetic::showType(uInt tp) { return MEarthMagnetic::showType(MEarthMagnetic::castType(tp)); } const String* MEarthMagnetic::allMyTypes(Int &nall, Int &nextra, const uInt *&typ) { static const Int N_name = 24; static const Int N_extra = 0; static const String tname[N_name] = { "J2000", "JMEAN", "JTRUE", "APP", "B1950", "BMEAN", "BTRUE", "GALACTIC", "HADEC", "AZEL", "AZELSW", "AZELNE", "AZELGEO", "AZELSWGEO", "AZELNEGEO", "JNAT", "ECLIPTIC", "MECLIPTIC", "TECLIPTIC", "SUPERGAL", "ITRF", "TOPO", "ICRS", "IGRF" }; static const uInt oname[N_name] = { MEarthMagnetic::J2000, MEarthMagnetic::JMEAN, MEarthMagnetic::JTRUE, MEarthMagnetic::APP, MEarthMagnetic::B1950, MEarthMagnetic::BMEAN, MEarthMagnetic::BTRUE, MEarthMagnetic::GALACTIC, MEarthMagnetic::HADEC, MEarthMagnetic::AZEL, MEarthMagnetic::AZELSW, MEarthMagnetic::AZEL, MEarthMagnetic::AZELGEO, MEarthMagnetic::AZELSWGEO, MEarthMagnetic::AZELGEO, MEarthMagnetic::JNAT, MEarthMagnetic::ECLIPTIC, MEarthMagnetic::MECLIPTIC, MEarthMagnetic::TECLIPTIC, MEarthMagnetic::SUPERGAL, MEarthMagnetic::ITRF, MEarthMagnetic::TOPO, MEarthMagnetic::ICRS, MEarthMagnetic::IGRF}; MEarthMagnetic::checkMyTypes(); nall = N_name; nextra = N_extra; typ = oname; return tname; } const String* MEarthMagnetic::allTypes(Int &nall, Int &nextra, const uInt *&typ) const { return MEarthMagnetic::allMyTypes(nall, nextra, typ); } Bool MEarthMagnetic::getType(MEarthMagnetic::Types &tp, const String &in) { const uInt *oname; Int nall, nex; const String *tname = MEarthMagnetic::allMyTypes(nall, nex, oname); Int i = Measure::giveMe(in, nall, tname); if (i>=nall) return False; else tp = static_cast(oname[i]); return True; } void MEarthMagnetic::checkTypes() const { MEarthMagnetic::checkMyTypes(); } void MEarthMagnetic::checkMyTypes() { // Multiple threads could execute this, but that is harmless. static Bool first(True); if (first) { first = False; Int nall, nex; const uInt *typ; const String *const tps = MEarthMagnetic::allMyTypes(nall,nex, typ); MEarthMagnetic::Types tp; for (Int i=0; i(&in)) return False; ref.set(in); return True; } Bool MEarthMagnetic::setRefString(const String &in) { MEarthMagnetic::Types tp; if (MEarthMagnetic::getType(tp, in)) { ref.setType(tp); return True; } ref.setType(MEarthMagnetic::DEFAULT); return False; } const String &MEarthMagnetic::getDefaultType() const { return MEarthMagnetic::showType(MEarthMagnetic::DEFAULT); } String MEarthMagnetic::getRefString() const { return MEarthMagnetic::showType(ref.getType()); } Bool MEarthMagnetic::isModel() const { return ((ref.getType() & MEarthMagnetic::EXTRA) != 0); } Quantum > MEarthMagnetic::get(const Unit &inunit) const { return Quantum >(data.getValue(),"T").get(inunit); } Quantum > MEarthMagnetic::getAngle() const { return (data.getAngle()); } Quantum > MEarthMagnetic::getAngle(const Unit &inunit) const { return (data.getAngle(inunit)); } Measure *MEarthMagnetic::clone() const { return (new MEarthMagnetic(*this)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/MEarthMagnetic.h000066400000000000000000000220341476623553700217360ustar00rootroot00000000000000//# MEarthMagnetic.h: A Measure: Magnetic field on Earth //# Copyright (C) 1995-1999,2000,2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MEARTHMAGNETIC_H #define MEASURES_MEARTHMAGNETIC_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MEarthMagnetic; class MCEarthMagnetic; template class MeasConvert; template class ArrayMeasColumn; template class ScalarMeasColumn; //# Typedefs // A Measure: Magnetic field on Earth // // // // //
      • Measure class // // // // Earth and Magnetic field // // // // MEarthMagnetic forms derived Measure class for Earth' magnetic flux density. // The field can be specified as a model, or as a 3D vector (see // MVEarthMagnetic) with a specified // reference frame code. If a model is specified, a possibly specified // explicit field will be ignored, since the field will be calculated from // the model if a conversion is asked for.
        // The class contains the following magnetic field models: //
          //
        • IGRF international reference field //
        // The reference frame type can be any of the types specified in the // MDirection direction types (e.g. AZEL). // // The IGRF needs a Table of coefficients (at 5-year interval) // // Conversion between field models is not supported (but not relevant // anyway with only one model supported). Conversion to an explicit direction // is done by the standard MeasConvert // class and rules (see example) using MEarthMagnetic::Convert, // and the reference types (e.g. MEarthMagnetic::AZEL). // // An EarthMagneticMachine has // been provided to get e.g. the field in a certain direction at a // certain height. // //
        // // // // // Where on Earth // MPosition pos(MVPosition(Quantity(20,'m'), Quantity(5,'deg'), // Quantity(52,'deg')), MPosition::WGS84); // // Time we want it // MEpoch epo(MVEpoch(50000)); // // Put in frame // MeasFrame frame(pos, epo); // // Magnetic field model // MEarthMagnetic mf; // // Show field strength in Gauss in AzEl system // cout << // MEarthMagnetic::Convert(mf, MEarthMagnetic::AZEL)(). // getValue().getLength("G") << endl; // // // // // To have the Earth' magnetic field in the standard Measure environment. // // // //
      • maybe add other field models if necessary (e.g. dipole) // class MEarthMagnetic : public MeasBase > { public: //# Friends // Conversion of data friend class MeasConvert; //# Enumerations // Types of known MEarthMagnetics // The order defines the order in the translation matrix // FromTo // in the getConvert routine in MCEarthMagnetic. Do not change the order // without changing the array. Additions should be made before N_types, and // an additional row and column should be coded in FromTo, and // in showType(). enum Types { J2000, JMEAN, JTRUE, APP, B1950, BMEAN, BTRUE, GALACTIC, HADEC, AZEL, AZELSW, AZELGEO, AZELSWGEO, JNAT, ECLIPTIC, MECLIPTIC, TECLIPTIC, SUPERGAL, ITRF, TOPO, ICRS, N_Types, // Models. First one should be IGRF IGRF = 32, N_Models, // All extra bits (for internal use only) EXTRA = 32, // Defaults DEFAULT=IGRF, // Synonyms AZELNE=AZEL, AZELNEGEO=AZELGEO }; //# Typedefs // Measure value container for this class (i.e. MEarthMagnetic::MVType) typedef MVEarthMagnetic MVType; // Measure conversion routines for this class (i.e. MEarthMagnetic::MCType) typedef MCEarthMagnetic MCType; // Measure reference (i.e. MEarthMagnetic::Ref) typedef MeasRef Ref; // Measure Convert (i.e. MEarthMagnetic::Convert) typedef MeasConvert Convert; // Measure table Columns (e.g., MEarthMagnetic::ScalarColumn) typedef ScalarMeasColumn ScalarColumn; typedef ArrayMeasColumn ArrayColumn; //# Constructors // In the following constructors and other functions, all // MeasRef can be replaced with simple Measure::TYPE // where no offsets or frames are needed in the reference. // Default constructor; generates the default IGRF type MEarthMagnetic(); // Create from data and reference // MEarthMagnetic(const MVEarthMagnetic &dt); MEarthMagnetic(const MVEarthMagnetic &dt, const MEarthMagnetic::Ref &rf); MEarthMagnetic(const MVEarthMagnetic &dt, MEarthMagnetic::Types rf); MEarthMagnetic(const Measure *dt); MEarthMagnetic(const MeasValue *dt); MEarthMagnetic(const MEarthMagnetic::Ref &rf); // // MEarthMagnetic(const MEarthMagnetic &); MEarthMagnetic &operator=(const MEarthMagnetic &); // //# Destructor virtual ~MEarthMagnetic(); //# Operators //# General Member Functions // Tell me your type // virtual const String &tellMe() const; static const String &showMe(); static void assure(const Measure &in); // // Translate reference code. The uInt version has a check for valid codes // (i.e. it is a safe cast). // //
      • AipsError in the uInt interface if illegal code given // // static MEarthMagnetic::Types castType(uInt tp); static const String &showType(MEarthMagnetic::Types tp); static const String &showType(uInt tp); // // Translate string to reference code // static Bool getType(MEarthMagnetic::Types &tp, const String &in); Bool giveMe(MEarthMagnetic::Ref &mr, const String &in); // // Set the offset in the reference (False if non-matching Measure) virtual Bool setOffset(const Measure &in); // Set the reference type to the specified String. False if illegal // string, reference set to DEFAULT. virtual Bool setRefString(const String &in); // Get the default reference type virtual const String &getDefaultType() const; // Get a list of all known reference codes. nall returns the number in list, // nextra the number of specials (like planets) that should be at // end of list). typ returns the list of corresponding types. // virtual const String* allTypes(Int &nall, Int &nextra, const uInt *&typ) const; static const String* allMyTypes(Int &nall, Int &nextra, const uInt *&typ); // // Check if all internal tables of types (both enum and String) are // complete and correct. This function is called automatically if and when // necessary. // //
      • AipsError if a (programming) error in the types. // // virtual void checkTypes() const; static void checkMyTypes(); // // Get the reference type (for records, including codes like R_) virtual String getRefString() const; // Tell me if you are a pure model (e.g. a planet) virtual Bool isModel() const; // Get Measure data // Quantum > get(const Unit &inunit) const; Quantum > getAngle() const; Quantum > getAngle(const Unit &inunit) const; // // Make copy virtual Measure *clone() const; private: //# Enumerations //# Data //# Member functions }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MEpoch.cc000066400000000000000000000145041476623553700204220ustar00rootroot00000000000000//# MEpoch.cc: A Measure: instant in time //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors MEpoch::MEpoch() : MeasBase() {} MEpoch::MEpoch(const MVEpoch &dt) : MeasBase(dt,MEpoch::DEFAULT) {} MEpoch::MEpoch(const MVEpoch &dt, const MEpoch::Ref &rf) : MeasBase(dt,rf) {} MEpoch::MEpoch(const MVEpoch &dt, MEpoch::Types rf) : MeasBase(dt,rf) {} MEpoch::MEpoch(const Quantity &dt) : MeasBase(dt,MEpoch::DEFAULT) {} MEpoch::MEpoch(const Quantity &dt, const MEpoch::Ref &rf) : MeasBase(dt,rf) {} MEpoch::MEpoch(const Quantity &dt, MEpoch::Types rf) : MeasBase(dt,rf) {} MEpoch::MEpoch(const Measure *dt) : MeasBase(dt) {} MEpoch::MEpoch(const MeasValue *dt) : MeasBase(*(MVEpoch*)dt, MEpoch::DEFAULT) {} //# Destructor MEpoch::~MEpoch() {} //# Operators //# Member functions const String &MEpoch::tellMe() const { return MEpoch::showMe(); } const String &MEpoch::showMe() { static const String name("Epoch"); return name; } void MEpoch::assure(const Measure &in) { if (!dynamic_cast(&in)) { throw(AipsError("Illegal Measure type argument: " + MEpoch::showMe())); } } MEpoch::Types MEpoch::castType(uInt tp) { MEpoch::checkMyTypes(); AlwaysAssert((tp & ~MEpoch::EXTRA) < MEpoch::N_Types, AipsError); return static_cast(tp); } const String &MEpoch::showType(MEpoch::Types tp) { static const String tname[MEpoch::N_Types] = { "LAST", "LMST", "GMST1", "GAST", "UT1", "UT2", "UTC", "TAI", "TDT", "TCG", "TDB", "TCB"}; MEpoch::checkMyTypes(); return tname[tp & ~MEpoch::EXTRA]; } const String &MEpoch::showType(uInt tp) { return MEpoch::showType(MEpoch::castType(tp)); } const String* MEpoch::allMyTypes(Int &nall, Int &nextra, const uInt *&typ) { static const Int N_name = 17; static const Int N_extra = 0; static const String tname[N_name] = { "LAST", "LMST", "GMST1", "GAST", "UT1", "UT2", "UTC", "TAI", "TDT", "TCG", "TDB", "TCB", "IAT", "GMST", "TT", "ET", "UT" }; static const uInt oname[N_name] = { MEpoch::LAST, MEpoch::LMST, MEpoch::GMST1, MEpoch::GAST, MEpoch::UT1, MEpoch::UT2, MEpoch::UTC, MEpoch::TAI, MEpoch::TDT, MEpoch::TCG, MEpoch::TDB, MEpoch::TCB, MEpoch::TAI, MEpoch::GMST1, MEpoch::TDT, MEpoch::TDT, MEpoch::UT1 }; MEpoch::checkMyTypes(); nall = N_name; nextra = N_extra; typ = oname; return tname; } const String* MEpoch::allTypes(Int &nall, Int &nextra, const uInt *&typ) const { return MEpoch::allMyTypes(nall, nextra, typ); } void MEpoch::checkTypes() const { MEpoch::checkMyTypes(); } void MEpoch::checkMyTypes() { // Multiple threads could execute this, but that is harmless. static Bool first(True); if (first) { first = False; Int nall, nex; const uInt *typ; const String *const tps = MEpoch::allMyTypes(nall,nex, typ); MEpoch::Types tp; for (Int i=0; i=nall) return False; else tp = static_cast(oname[i]); return True; } Bool MEpoch::giveMe(MEpoch::Ref &mr, const String &in) { MEpoch::Types tp; if (MEpoch::getType(tp, in)) mr = MEpoch::Ref(tp); else { mr = MEpoch::Ref(); return False; } return True; } Bool MEpoch::setOffset(const Measure &in) { if (!dynamic_cast(&in)) return False; ref.set(in); return True; } Bool MEpoch::setRefString(const String &in) { MEpoch::Types tp; String x = in; Bool raze = False; if (x.before(2) == "r_" || x.before(2) == "R_") { raze = True; x = x.from(2); } if (MEpoch::getType(tp, x)) { if (raze) { ref.setType(tp | MEpoch::RAZE); } else { ref.setType(tp); } return True; } ref.setType(MEpoch::DEFAULT); return False; } const String &MEpoch::getDefaultType() const { return MEpoch::showType(MEpoch::DEFAULT); } String MEpoch::getRefString() const { String x; if ((ref.getType() & MEpoch::RAZE) != 0) x = String("R_"); x += MEpoch::showType(ref.getType()); return x; } Quantity MEpoch::get(const Unit &inunit) const { return (data.getTime().get(inunit)); } Measure *MEpoch::clone() const { return (new MEpoch(*this)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/MEpoch.h000066400000000000000000000171471476623553700202720ustar00rootroot00000000000000//# MEpoch.h: A Measure: instant in time //# Copyright (C) 1995,1996,1997,1998,1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MEPOCH_H #define MEASURES_MEPOCH_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MEpoch; class MCEpoch; template class MeasConvert; template class ArrayMeasColumn; template class ScalarMeasColumn; //# Typedefs // // A Measure: instant in time // // // // // //
      • Measure class // // // // Measure and Epoch // // // // MEpoch forms derived Measure class for an instant in time. // // // // Convert (with all steps explicit) a UTC to an IAT time. // // #include // #include // #include // #include // // cout << "TAI for UTC = MJD(50237.29): " << // MEpoch::Convert(MEpoch(MVEpoch(Quantity(50237.29, "d")), // MEpoch::Ref(MEpoch::UTC)), // MEpoch::Ref(MEpoch::TAI))() << // endl; // LogIO os(LogOrigin("FluxCalc_SS_JPL_Butler", "readEphem")); // os << LogIO::DEBUG1 << " at "; // os.output() << MEpoch::Convert(MEpoch(MVEpoch(Quantity(50237.29, "d")), // MEpoch::Ref(MEpoch::UTC)), // MEpoch::Ref(MEpoch::TAI))(); // os << LogIO::POST; // // Results in: // // TAI for UTC = MJD(50237.29): Epoch: 50237::06:58:06.0000 (on stdout) // at Epoch: 50237::06:58:06.0000 (in logger) // // // // // // // //
      • // class MEpoch : public MeasBase > { public: //# Friends friend class MeasConvert; //# Enumerations // Types of known MEpochs // The order defines the order in the translation matrix // in the MCEpoch class. Do not change the order without // changing the array. Additions should be made before N_types, and // an additional row and column should be coded in FromTo (MCEpoch), and // in showType(). enum Types { // Local Apparent Sidereal Time LAST, // Local Mean Sidereal Time LMST, // Greenwich Mean ST1 GMST1, // Greenwich Apparent ST GAST, UT1, UT2, UTC, TAI, TDT, TCG, TDB, TCB, // Number of types N_Types, // Reduce result to integer days RAZE = 32, // All extra bits EXTRA = RAZE, // Synonyms IAT=TAI, GMST=GMST1, TT=TDT, UT=UT1, ET=TT, // Default DEFAULT=UTC }; //# Typedefs // Measure value container for this class (i.e. MEpoch::MVType) typedef MVEpoch MVType; // Measure conversion routines for this class (i.e. MEpoch::MCType) typedef MCEpoch MCType; // Measure reference (i.e. MEpoch::Ref) typedef MeasRef Ref; // Measure Convert (i.e. MEpoch::Convert) typedef MeasConvert Convert; // Measure table Columns (e.g., MEpoch::ScalarColumn) typedef ScalarMeasColumn ScalarColumn; typedef ArrayMeasColumn ArrayColumn; //# Constructors // In the following constructors and other functions, all // MeasRef can be replaced with simple Measure::TYPE // where no offsets or frames are needed in the reference. // Default constructor; generates an instant at MJD 0 UTC MEpoch(); // Create from data and reference // MEpoch(const MVEpoch &dt); MEpoch(const MVEpoch &dt, const MEpoch::Ref &rf); MEpoch(const MVEpoch &dt, MEpoch::Types rf); MEpoch(const Quantity &dt); MEpoch(const Quantity &dt, const MEpoch::Ref &rf); MEpoch(const Quantity &dt, MEpoch::Types rf); MEpoch(const Measure *dt); MEpoch(const MeasValue *dt); // //# Destructor virtual ~MEpoch(); //# Operators //# General Member Functions // Tell me your type // virtual const String &tellMe() const; static const String &showMe(); static void assure(const Measure &in); // // Translate reference code. The uInt version has a check for valid codes // (i.e. it is a safe cast). // //
      • AipsError in the uInt interface if illegal code given // // static MEpoch::Types castType(uInt tp); static const String &showType(MEpoch::Types tp); static const String &showType(uInt tp); // // Translate string to reference code // static Bool getType(MEpoch::Types &tp, const String &in); Bool giveMe(MEpoch::Ref &mr, const String &in); // // Set the offset in the reference (False if non-matching Measure) virtual Bool setOffset(const Measure &in); // Set the reference type to the specified String. False if illegal // string, reference set to DEFAULT. virtual Bool setRefString(const String &in); // Get the default reference type virtual const String &getDefaultType() const; // Get a list of all known reference codes. nall returns the number in list, // nextra the number of specials (like planets) that should be at // end of list). typ returns the list of corresponding types. // virtual const String* allTypes(Int &nall, Int &nextra, const uInt *&typ) const; static const String* allMyTypes(Int &nall, Int &nextra, const uInt *&typ); // // Check if all internal tables of types (both enum and String) are // complete and correct. This function is called automatically if and when // necessary. // //
      • AipsError if a (programming) error in the types. // // virtual void checkTypes() const; static void checkMyTypes(); // // Get the reference type (for records, including codes like R_) virtual String getRefString() const; // Get time in specified units Quantity get(const Unit &inunit) const; // Create copy // virtual Measure *clone() const; // private: //# Enumerations //# Data //# Member functions }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MFrequency.cc000066400000000000000000000213331476623553700213230ustar00rootroot00000000000000//# MFrequency.cc: A Measure: wave characteristics //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors MFrequency::MFrequency() : MeasBase() {} MFrequency::MFrequency(const MVFrequency &dt) : MeasBase(dt,MFrequency::DEFAULT) {} MFrequency::MFrequency(const MVFrequency &dt, const MFrequency::Ref &rf) : MeasBase(dt,rf) {} MFrequency::MFrequency(const MVFrequency &dt, MFrequency::Types rf) : MeasBase(dt,rf) {} MFrequency::MFrequency(const Quantity &dt) : MeasBase(dt,MFrequency::DEFAULT) {} MFrequency::MFrequency(const Quantity &dt, const MFrequency::Ref &rf) : MeasBase(dt,rf) {} MFrequency::MFrequency(const Quantity &dt, MFrequency::Types rf) : MeasBase(dt,rf) {} MFrequency::MFrequency(const Measure *dt) : MeasBase(dt) {} MFrequency::MFrequency(const MeasValue *dt) : MeasBase(*(MVFrequency*)dt, MFrequency::DEFAULT) {} //# Destructor MFrequency::~MFrequency() {} //# Operators //# Member functions const String &MFrequency::tellMe() const { return MFrequency::showMe(); } const String &MFrequency::showMe() { static const String name("Frequency"); return name; } void MFrequency::assure(const Measure &in) { if (!dynamic_cast(&in)) { throw(AipsError("Illegal Measure type argument: " + MFrequency::showMe())); } } MFrequency::Types MFrequency::castType(uInt tp) { MFrequency::checkMyTypes(); if ((tp & MFrequency::EXTRA) == 0) { AlwaysAssert(tp < MFrequency::N_Types, AipsError); } else { AlwaysAssert((tp & ~MFrequency::EXTRA) < (MFrequency::N_Other - MFrequency::EXTRA), AipsError); } return static_cast(tp); } const String &MFrequency::showType(MFrequency::Types tp) { static const String tname[MFrequency::N_Types] = { "REST", "LSRK", "LSRD", "BARY", "GEO", "TOPO", "GALACTO", "LGROUP", "CMB" }; static const String ename[MFrequency::N_Other - MFrequency::EXTRA] = { "Undefined" }; MFrequency::checkMyTypes(); if ((tp & MFrequency::EXTRA) == 0) return tname[tp]; return ename[tp & ~MFrequency::EXTRA]; } const String &MFrequency::showType(uInt tp) { return MFrequency::showType(MFrequency::castType(tp)); } const String* MFrequency::allMyTypes(Int &nall, Int &nextra, const uInt *&typ) { static const Int N_name = 10; static const Int N_extra = 1; static const String tname[N_name] = { "REST", "LSRK", "LSRD", "BARY", "GEO", "TOPO", "GALACTO", "LGROUP", "CMB", "Undefined" }; static const uInt oname[N_name] = { MFrequency::REST, MFrequency::LSRK, MFrequency::LSRD, MFrequency::BARY, MFrequency::GEO, MFrequency::TOPO, MFrequency::GALACTO, MFrequency::LGROUP, MFrequency::CMB, MFrequency::Undefined }; MFrequency::checkMyTypes(); nall = N_name; nextra = N_extra; typ = oname; return tname; } const String* MFrequency::allTypes(Int &nall, Int &nextra, const uInt *&typ) const { return MFrequency::allMyTypes(nall, nextra, typ); } Bool MFrequency::getType(MFrequency::Types &tp, const String &in) { const uInt *oname; Int nall, nex; const String *tname = MFrequency::allMyTypes(nall, nex, oname); Int i = Measure::giveMe(in, nall, tname); if (i>=nall) return False; else tp = static_cast(oname[i]); return True; } MFrequency::Types MFrequency::typeFromString(const String& in) { MFrequency::Types tp; ThrowIf( ! getType(tp, in), in + " is not a recognized type identifier" ); return tp; } void MFrequency::checkTypes() const { MFrequency::checkMyTypes(); } void MFrequency::checkMyTypes() { static Bool first(True); if (first) { first = False; Int nall, nex; const uInt *typ; const String *const tps = MFrequency::allMyTypes(nall,nex, typ); MFrequency::Types tp; for (Int i=0; i(&in)) return False; ref.set(in); return True; } Bool MFrequency::setRefString(const String &in) { MFrequency::Types tp; if (MFrequency::getType(tp, in)) { ref.setType(tp); return True; } ref.setType(MFrequency::DEFAULT); return False; } const String &MFrequency::getDefaultType() const { return MFrequency::showType(MFrequency::DEFAULT); } String MFrequency::getRefString() const { return MFrequency::showType(ref.getType()); } Quantity MFrequency::get(const Unit &un) const { return data.get(un); } MDoppler MFrequency::toDoppler(const MVFrequency &rest) { Double t = data / rest; t *= t; return MDoppler( MVDoppler((1-t)/(1+t)), MDoppler::BETA); } MDoppler MFrequency::toDoppler(const MVFrequency &rest) const { Double t = data / rest; t *= t; return MDoppler( MVDoppler((1-t)/(1+t)), MDoppler::BETA); } MDoppler MFrequency::toDoppler(const Measure &in, const MVFrequency &rest) { MFrequency::assure(in); Double t = ((MVFrequency *)(in.getData()))->getValue() / rest.getValue(); t *= t; return MDoppler( MVDoppler((1-t)/(1+t)), MDoppler::BETA); } MFrequency MFrequency::fromDoppler(const MDoppler &dop, const MVFrequency &rest) { return MFrequency::fromDoppler(dop, rest, MFrequency::LSRK); } MFrequency MFrequency::fromDoppler(const MDoppler &dop, const MVFrequency &rest, MFrequency::Types type) { Double t = MDoppler::Convert(dop, MDoppler::BETA)().getValue(); t = (1-t)/(1+t); return MFrequency(MVFrequency(sqrt(t) * rest.getValue()), type); } MFrequency MFrequency::fromDoppler(const Measure &dop, const MVFrequency &rest, MFrequency::Types type) { MDoppler::assure(dop); Double t = MDoppler::Convert(dop, MDoppler::BETA)().getValue(); t = (1-t)/(1+t); return MFrequency(MVFrequency(sqrt(t) * rest.getValue()), type); } MFrequency MFrequency::toRest(const MDoppler &dop) const { Double t = MDoppler::Convert(dop, MDoppler::BETA)().getValue(); t = (1-t)/(1+t); return MFrequency(MVFrequency(data.getValue() / sqrt(t)), MFrequency::REST); } MFrequency MFrequency::toRest(const Measure &in, const Measure &dop) { MDoppler::assure(dop); MFrequency::assure(in); Double t = MDoppler::Convert(dop, MDoppler::BETA)().getValue(); t = (1-t)/(1+t); return MFrequency(MVFrequency(((MVFrequency *)(in.getData()))->getValue() / sqrt(t)), MFrequency::REST); } Measure *MFrequency::clone() const { return (new MFrequency(*this)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/MFrequency.h000066400000000000000000000267001476623553700211700ustar00rootroot00000000000000//# MFrequency.h: A Measure: wave characteristics //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MFREQUENCY_H #define MEASURES_MFREQUENCY_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MFrequency; class MCFrequency; class MDoppler; class MVDoppler; template class MeasConvert; template class ArrayMeasColumn; template class ScalarMeasColumn; //# Typedefs // // A Measure: wave characteristics // // // // // //
      • Measure class //
      • MRadialVelocity class // for some other background. // // // // // // // MFrequency is a derived Measure class for wave characteristics.
        // An MFrequency can be generated from a simple value (or an // MFrequency object), which is then // interpreted as a frequency in Hz, and a reference, with an LSRK type // as default.
        // It can also be generated from a Quantity, where the interpretation // depends on the dimensionality of the Quantity: //
          //
        • time (e.g. s): period //
        • frequency (e.g. Hz): frequency //
        • angular frequency (e.g. arcmin/s): angular frequency //
        • length (e.g. cm): wavelength //
        • inverse length (e.g. mm-1): wave number //
        • energy (e.g. J.s): energy (i.e. h.nu) //
        • momentum (e.g. kg.m): m.c/h //
        // The different reference types of a frequency are: //
          //
        • MFrequency::REST -- Rest frequency //
        • MFrequency::LSRD -- Local Standard of Rest (J2000) -- as the // dynamical definition (IAU, [9,12,7] km/s in galactic // coordinates) //
        • MFrequency::LSRK -- LSR as a kinematical (radio) definition -- // 20.0 km/s in direction ra,dec = [270,+30] deg (B1900.0) //
        • MFrequency::BARY -- Barycentric (J2000) //
        • MFrequency::GEO --- Geocentric //
        • MFrequency::TOPO -- Topocentric //
        • MFrequency::GALACTO -- Galacto centric (with rotation of 220 km/s // in direction l,b = [90,0] deg. //
        • MFrequency::LGROUP -- Local group velocity -- 308km/s towards // l,b = [105,-7] deg (F. Ghigo) //
        • MFrequency::CMB -- CMB velocity -- 369.5km/s towards // l,b = [264.4, 48.4] deg (F. Ghigo) //
        • MFrequency::DEFAULT = LSRK //
        //

        // Conversion between the different types is done with the standard // MeasConvert class // (MFrequency::Convert in this case). // Some of the conversions are only possible if frame information has been // filled in. The following frame information is necessary if a conversion // goes to or from the (different) specified types: //

          //
        • Radial Velocity: REST //
        • Epoch: TOPO, GEO //
        • Position: TOPO //
        • Direction all //
        //
        // To accommodate unknown or invalid frames, the additional reference type //
          //
        • MFrequency::Undefined //
        // is available. Conversions to/from Undefined are not possible. // If attempted, an exception will be thrown. // The name was chosen to be Undefined and not UNDEFINED in order to // not collide with the (ugly) WCSLIB macro of the upper case name // and in concordance with Stokes::Undefined. //
        // An MFrequency can be created from an // MDoppler (and a rest frequency, (the // QC class contains at least QC::HI)) // by the fromDoppler() member. It can be converted to an MDoppler // with the toDoppler(). Comparable methods will be available // for MFrequency as // toRadial() and fromRadial.
        // If the Doppler shift is known (e.g. from another spectral line), the // REST frequency can be determined with the toREST() member. // Conversion between the different frequencies can, // due to relativistic effects, only be done approximately for very high // (order c) radial velocities (shifted frequencies). A better approach // would be to start from radial velocities and a rest frequency. // //
        // // // Get the Doppler shift for an oberved HI frequency of 1380 MHz // // cout << "Redshift for 1380 MHz: " << // MDoppler::Convert( MFrequency( Quantity(1380., "MHz"), // MFrequency::TOPO).toDoppler(QC::HI), // MDoppler::Z)() << endl; // // // // // // // // class MFrequency : public MeasBase > { public: //# Friends // Conversion of data friend class MeasConvert; //# Enumerations // Types of known MFrequencies // The order defines the order in the translation // matrix FromTo // in the getConvert routine. Do not change the order without // changing the array. Additions should be made before N_types, and // an additional row and column should be coded in FromTo, and // in showType(). enum Types { REST, LSRK, LSRD, BARY, GEO, TOPO, GALACTO, LGROUP, CMB, N_Types, Undefined = 64, N_Other, // all extra bits EXTRA = 64, // Defaults DEFAULT=LSRK, // Synonyms LSR=LSRK }; //# Typedefs // Measure value container for this class (i.e. MFrequency::MVType) typedef MVFrequency MVType; // Measure conversion routines for this class (i.e. MFrequency::MCType) typedef MCFrequency MCType; // Measure reference (i.e. MFrequency::Ref) typedef MeasRef Ref; // Measure conversion use (i.e. MFrequency::Convert) typedef MeasConvert Convert; // Measure table Columns (e.g., MFrequency::ScalarColumn) typedef ScalarMeasColumn ScalarColumn; typedef ArrayMeasColumn ArrayColumn; //# Constructors // In the following constructors and other functions, all // MeasRef can be replaced with simple Measure::TYPE // where no offsets or frames are needed in the reference. // Default constructor; generates a zero rest frequency MFrequency(); // Create from data and reference // MFrequency(const MVFrequency &dt); MFrequency(const MVFrequency &dt, const MFrequency::Ref &rf); MFrequency(const MVFrequency &dt, MFrequency::Types rf); MFrequency(const Quantity &dt); MFrequency(const Quantity &dt, const MFrequency::Ref &rf); MFrequency(const Quantity &dt, MFrequency::Types rf); MFrequency(const Measure *dt); MFrequency(const MeasValue *dt); // //# Destructor virtual ~MFrequency(); //# Operators //# General Member Functions // Tell me your type // virtual const String &tellMe() const; static const String &showMe(); static void assure(const Measure &in); // // Translate reference code. The uInt version has a check for valid codes // (i.e. it is a safe cast). // //
      • AipsError in the uInt interface if illegal code given // // static MFrequency::Types castType(uInt tp); static const String &showType(MFrequency::Types tp); static const String &showType(uInt tp); // // Translate string to reference code // static Bool getType(MFrequency::Types &tp, const String &in); // Throws an exception if the type string is not recognized static MFrequency::Types typeFromString(const String& in); Bool giveMe(MFrequency::Ref &mr, const String &in); // // Set the offset in the reference (False if non-matching Measure) virtual Bool setOffset(const Measure &in); // Set the reference type to the specified String. False if illegal // string, reference set to DEFAULT. virtual Bool setRefString(const String &in); // Get the default reference type virtual const String &getDefaultType() const; // Get a list of all known reference codes. nall returns the number in list, // nextra the number of specials (like planets) that should be at // end of list). typ returns the list of corresponding types. // virtual const String* allTypes(Int &nall, Int &nextra, const uInt *&typ) const; static const String* allMyTypes(Int &nall, Int &nextra, const uInt *&typ); // // Check if all internal tables of types (both enum and String) are // complete and correct. This function is called automatically if and when // necessary. // //
      • AipsError if a (programming) error in the types. // // virtual void checkTypes() const; static void checkMyTypes(); // // Get the reference type (for records, including codes like R_) virtual String getRefString() const; // Get frequency in specified units Quantity get(const Unit &un) const; // Make a Doppler velocity from the frequency and the specified rest frequency // MDoppler toDoppler(const MVFrequency &rest); MDoppler toDoppler(const MVFrequency &rest) const; // // Local use only static MDoppler toDoppler(const Measure &in, const MVFrequency &rest); // Make a frequency from the Doppler velocity and the specified rest frequency // (default reference type LSRK) // static MFrequency fromDoppler(const MDoppler &dop, const MVFrequency &rest); static MFrequency fromDoppler(const MDoppler &dop, const MVFrequency &rest, MFrequency::Types type); // For internal use only static MFrequency fromDoppler(const Measure &dop, const MVFrequency &rest, MFrequency::Types type); // // Make a rest frequency using a Doppler velocity MFrequency toRest(const MDoppler &dop) const; // For local use only static MFrequency toRest(const Measure &in, const Measure &dop); // Make a copy // virtual Measure *clone() const; // private: //# Enumerations //# Data //# Member functions }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MPosition.cc000066400000000000000000000166021476623553700211710ustar00rootroot00000000000000//# MPosition.cc: A Measure: position on Earth //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors MPosition::MPosition() : MeasBase() {} MPosition::MPosition(const MVPosition &dt) : MeasBase(dt,MPosition::DEFAULT) {} MPosition::MPosition(const MVPosition &dt, const MPosition::Ref &rf) : MeasBase(dt,rf) {} MPosition::MPosition(const MVPosition &dt, MPosition::Types rf) : MeasBase(dt,rf) {} MPosition::MPosition(const Quantity &dt, const Quantity &dt1, const Quantity &dt2) : MeasBase(MVPosition(dt,dt1,dt2), MPosition::DEFAULT) {} MPosition::MPosition(const Quantity &dt, const Quantity &dt1, const Quantity &dt2, const MPosition::Ref &rf) : MeasBase(MVPosition(dt,dt1,dt2),rf) {} MPosition::MPosition(const Quantity &dt, const Quantity &dt1, const Quantity &dt2, MPosition::Types rf) : MeasBase(MVPosition(dt,dt1,dt2),rf) {} MPosition::MPosition(const Quantity &dt0, const Quantum > &dt) : MeasBase(MVPosition(dt0,dt), MPosition::DEFAULT) {} MPosition::MPosition(const Quantity &dt0, const Quantum > &dt, const MPosition::Ref &rf) : MeasBase(MVPosition(dt0,dt),rf) {} MPosition::MPosition(const Quantity &dt0, const Quantum > &dt, MPosition::Types rf) : MeasBase(MVPosition(dt0,dt),rf) {} MPosition::MPosition(const Measure *dt) : MeasBase(dt) {} MPosition::MPosition(const MeasValue *dt) : MeasBase(*(MVPosition*)dt, MPosition::DEFAULT) {} MPosition::MPosition(const MPosition &other) : MeasBase (other) {} MPosition &MPosition::operator=(const MPosition &other) { if (this != &other) { MeasBase &This = *this; const MeasBase &Other = other; This = Other; } return *this; } //# Destructor MPosition::~MPosition() {} //# Operators //# Member functions const String &MPosition::tellMe() const { return MPosition::showMe(); } const String &MPosition::showMe() { static const String name("Position"); return name; } void MPosition::assure(const Measure &in) { if (!dynamic_cast(&in)) { throw(AipsError("Illegal Measure type argument: " + MPosition::showMe())); } } const String* MPosition::allMyTypes(Int &nall, Int &nextra, const uInt *&typ) { static const Int N_name = 2; static const Int N_extra = 0; static const String tname[N_name] = { "ITRF", "WGS84" }; static const uInt oname[N_name] = { MPosition::ITRF, MPosition::WGS84 }; MPosition::checkMyTypes(); nall = N_name; nextra = N_extra; typ = oname; return tname; } const String* MPosition::allTypes(Int &nall, Int &nextra, const uInt *&typ) const { return MPosition::allMyTypes(nall, nextra, typ); } void MPosition::checkTypes() const { MPosition::checkMyTypes(); } void MPosition::checkMyTypes() { // Multiple threads could execute this, but that is harmless. static Bool first(True); if (first) { first = False; Int nall, nex; const uInt *typ; const String *const tps = MPosition::allMyTypes(nall,nex, typ); MPosition::Types tp; for (Int i=0; i(tp); } const String &MPosition::showType(MPosition::Types tp) { static const String tname[MPosition::N_Types] = { "ITRF", "WGS84"}; MPosition::checkMyTypes(); return tname[tp]; } const String &MPosition::showType(uInt tp) { return MPosition::showType(MPosition::castType(tp)); } Bool MPosition::getType(MPosition::Types &tp, const String &in) { const uInt *oname; Int nall, nex; const String *tname = MPosition::allMyTypes(nall, nex, oname); Int i = Measure::giveMe(in, nall, tname); if (i>=nall) return False; else tp = static_cast(oname[i]); return True; } MPosition::Types MPosition::getType(const String& in) { Types myType; if (! getType(myType, in)) { throw AipsError("MPosition::Types: Unrecognized type string " + in); } return myType; } Bool MPosition::giveMe(MPosition::Ref &mr, const String &in) { MPosition::Types tp; if (MPosition::getType(tp, in)) mr = MPosition::Ref(tp); else { mr = MPosition::Ref(); return False; } return True; } Bool MPosition::setOffset(const Measure &in) { if (!dynamic_cast(&in)) return False; ref.set(in); return True; } Bool MPosition::setRefString(const String &in) { MPosition::Types tp; if (MPosition::getType(tp, in)) { ref.setType(tp); return True; } ref.setType(MPosition::DEFAULT); return False; } const String &MPosition::getDefaultType() const { return MPosition::showType(MPosition::DEFAULT); } String MPosition::getRefString() const { return MPosition::showType(ref.getType()); } Quantum > MPosition::get(const Unit &inunit) const { return Quantum >(data.getValue(),"m").get(inunit); } Quantum > MPosition::getAngle() const { return (data.getAngle()); } Quantum > MPosition::getAngle(const Unit &inunit) const { return (data.getAngle(inunit)); } Measure *MPosition::clone() const { return (new MPosition(*this)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/MPosition.h000066400000000000000000000161471476623553700210370ustar00rootroot00000000000000//# MPosition.h: A Measure: position on Earth //# Copyright (C) 1995,1996,1997,1998,1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MPOSITION_H #define MEASURES_MPOSITION_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MPosition; class MCPosition; template class MeasConvert; template class ArrayMeasColumn; template class ScalarMeasColumn; //# Typedefs // // A Measure: position on Earth // // // // // //
      • Measure class // // // // // // // MPosition forms derived Measure class for an instant in time. // // // // // // // // // //
      • // class MPosition : public MeasBase > { public: //# Friends // Conversion of data friend class MeasConvert; //# Enumerations // Types of known MPositions // The order defines the order in the translation // matrix FromTo // in the getConvert routine. Do not change the order without // changing the array. Additions should be made before N_types, and // an additional row and column should be coded in FromTo, and // in showType(). enum Types { ITRF, WGS84, N_Types, DEFAULT=ITRF}; //# Typedefs // Measure value container for this class (i.e. MPosition::MVType) typedef MVPosition MVType; // Measure conversion routines for this class (i.e. MPosition::MCType) typedef MCPosition MCType; // Measure reference (i.e. MPosition::Ref) typedef MeasRef Ref; // Measure Convert (i.e. MPosition::Convert) typedef MeasConvert Convert; // Measure table Columns (e.g., MPosition::ScalarColumn) typedef ScalarMeasColumn ScalarColumn; typedef ArrayMeasColumn ArrayColumn; //# Constructors // In the following constructors and other functions, all // MeasRef can be replaced with simple Measure::TYPE // where no offsets or frames are needed in the reference. // Default constructor; generates the ITRF centre MPosition(); // Create from data and reference // MPosition(const MVPosition &dt); MPosition(const MVPosition &dt, const MPosition::Ref &rf); MPosition(const MVPosition &dt, MPosition::Types rf); MPosition(const Quantity &dt, const Quantity &dt1, const Quantity &dt2); MPosition(const Quantity &dt, const Quantity &dt1, const Quantity &dt2, const MPosition::Ref &rf); MPosition(const Quantity &dt, const Quantity &dt1, const Quantity &dt2, MPosition::Types rf); MPosition(const Quantity &dt0, const Quantum > &dt); MPosition(const Quantity &dt0, const Quantum > &dt, const MPosition::Ref &rf); MPosition(const Quantity &dt0, const Quantum > &dt, MPosition::Types rf); MPosition(const Measure *dt); MPosition(const MeasValue *dt); // // MPosition(const MPosition &); MPosition &operator=(const MPosition &); // //# Destructor virtual ~MPosition(); //# Operators //# General Member Functions // Tell me your type // virtual const String &tellMe() const; static const String &showMe(); static void assure(const Measure &in); // // Translate reference code. The uInt version has a check for valid codes // (i.e. it is a safe cast). // //
      • AipsError in the uInt interface if illegal code given // // static MPosition::Types castType(uInt tp); static const String &showType(MPosition::Types tp); static const String &showType(uInt tp); // // Translate string to reference code // static Bool getType(MPosition::Types &tp, const String &in); // this one throws an exception for an unrecognized String static MPosition::Types getType(const String& in); Bool giveMe(MPosition::Ref &mr, const String &in); // // Set the offset in the reference (False if non-matching Measure) virtual Bool setOffset(const Measure &in); // Set the reference type to the specified String. False if illegal // string, reference set to DEFAULT. virtual Bool setRefString(const String &in); // Get the default reference type virtual const String &getDefaultType() const; // Get a list of all known reference codes. nall returns the number in list, // nextra the number of specials (like planets) that should be at // end of list). typ returns the list of corresponding types. // virtual const String* allTypes(Int &nall, Int &nextra, const uInt *&typ) const; static const String* allMyTypes(Int &nall, Int &nextra, const uInt *&typ); // // Check if all internal tables of types (both enum and String) are // complete and correct. This function is called automatically if and when // necessary. // //
      • AipsError if a (programming) error in the types. // // virtual void checkTypes() const; static void checkMyTypes(); // // Get the reference type (for records, including codes like R_) virtual String getRefString() const; // Get Measure data // Quantum > get(const Unit &inunit) const; Quantum > getAngle() const; Quantum > getAngle(const Unit &inunit) const; // // Make copy // virtual Measure *clone() const; // private: //# Enumerations //# Data //# Member functions }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MRBase.cc000066400000000000000000000032731476623553700203610ustar00rootroot00000000000000//# MRBase.cc: Base for Reference frame for physical measures //# Copyright (C) 1995,1996,1997,1998,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Destructor MRBase::~MRBase() {} //# Operators //# Member functions //# Global functions ostream &operator<<(ostream &os, const MRBase &meas) { meas.print(os); return os; } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/MRBase.h000066400000000000000000000137311476623553700202230ustar00rootroot00000000000000//# MRBase.h: Base for Reference frame for physical measures //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MRBASE_H #define MEASURES_MRBASE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class String; // Base for Reference frame for physical measures // // // // //
      • Quantum class //
      • Measure class // // // // From Measure and Reference and Base // // // // MRBase is the abstract base class for reference frames. // Reference frames are specified (see Measure) // as Measure::Ref (e.g. MEpoch::Ref). // // A Measure::Ref is a container for type indicators, // (e.g. MDirection::J2000), // an optional offset (e.g. beginning of year), and, if necessary, a // MeasFrame.
        // A MeasFrame consists of // one or more Measures specifying the reference frame (e.g. an // MPosition for a sidereal time definition). // A time // (MEpoch) could e.g. have a type // MEpoch::TAI, and an MEpoch as offset: // // MEpoch off(Quantity(40745,"d"), MEpoch::Ref(MEpoch::UTC)); // MEpoch::Ref myref(MEpoch::TAI, off); // // // It is obvious that a circular reference between Measure and Measure::Ref // is possible. Therefore, each Measure has a default reference // (necessary anyway to be able to start a Measure chain). For MEpoch // the default is e.g. an MJD in UTC; and the default Measure for // an MEpoch reference is 0.
        // References are copied by reference; i.e. a reference can be used in many // places without overhead.
        // Some Measure::Ref could need additional conversion information // ( example: type of Nutation calculations). They are provided by // Aipsrc keywords.
        // All constructors are related to a specific Measure, to be able to check // relations at compile time. //
        // // // See Measure for an example // // // // To gather all reference frame information in the one class. // // // // class MRBase { public: //# Friends friend ostream &operator<<(ostream &os, const MRBase &meas); //# Constructors //# Destructor virtual ~MRBase(); //# Operators //# General Member Functions // Check if empty reference virtual Bool empty() const = 0; // Check the type of Measure the reference can be used for:
        // static const String &showMe() = 0; .
        // Return the type of the reference // the following should really be // (and should be interpreted as), but // compiler does not accept it: // Ms::Types getType(); virtual uInt getType() const = 0; // Return the frame of the reference virtual MeasFrame &getFrame() = 0; // Return the first frame which has specified information. Checking is done in // argument order. // //
      • AipsError if neither reference has a frame or the proper type // // // static const MeasFrame &framePosition(const MRBase &ref1, // const MRBase &ref2) = 0; // static const MeasFrame &frameEpoch(const MRBase &ref1, // const MRBase &ref2) = 0; // static const MeasFrame &frameDirection(const MRBase &ref1, // const MRBase &ref2) = 0; // static const MeasFrame &frameRadialVelocity(const MRBase &ref1, // const MRBase &ref2) = 0; // // Return the offset (or 0) virtual const Measure* offset() const = 0; // Set the type // //
      • AipsError if wrong Measure // // the following should really be (and should be called as), but // compiler does not accept it: // void set(Ms::Types tp); // virtual void setType(uInt tp) = 0; virtual void set(uInt tp) = 0; // // Set a new offset:
        // void set(const Measure &ep); // Set a new frame virtual void set(const MeasFrame &mf) = 0; // Print a Measure virtual void print(ostream &os) const = 0; protected: private: //# Data //# Member functions }; //# Global functions // Global functions // // Output declaration ostream &operator<<(ostream &os, const MRBase &meas); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MRadialVelocity.cc000066400000000000000000000200361476623553700222740ustar00rootroot00000000000000//# MRadialVelocity.cc: A Measure: radial velocity //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors MRadialVelocity::MRadialVelocity() : MeasBase() {} MRadialVelocity::MRadialVelocity(const MVRadialVelocity &dt) : MeasBase(dt, MRadialVelocity::DEFAULT) {} MRadialVelocity::MRadialVelocity(const MVRadialVelocity &dt, const MRadialVelocity::Ref &rf) : MeasBase(dt,rf) {} MRadialVelocity::MRadialVelocity(const MVRadialVelocity &dt, MRadialVelocity::Types rf) : MeasBase(dt,rf) {} MRadialVelocity::MRadialVelocity(const Quantity &dt) : MeasBase(dt, MRadialVelocity::DEFAULT) {} MRadialVelocity::MRadialVelocity(const Quantity &dt, const MRadialVelocity::Ref &rf) : MeasBase(dt,rf) {} MRadialVelocity::MRadialVelocity(const Quantity &dt, MRadialVelocity::Types rf) : MeasBase(dt,rf) {} MRadialVelocity::MRadialVelocity(const Measure *dt) : MeasBase(dt) {} MRadialVelocity::MRadialVelocity(const MeasValue *dt) : MeasBase(*(MVRadialVelocity*)dt, MRadialVelocity::DEFAULT) {} //# Destructor MRadialVelocity::~MRadialVelocity() {} //# Operators //# Member functions const String &MRadialVelocity::tellMe() const { return MRadialVelocity::showMe(); } const String &MRadialVelocity::showMe() { static const String name("Radialvelocity"); return name; } void MRadialVelocity::assure(const Measure &in) { if (!dynamic_cast(&in)) { throw(AipsError("Illegal Measure type argument: " + MRadialVelocity::showMe())); } } MRadialVelocity::Types MRadialVelocity::castType(uInt tp) { MRadialVelocity::checkMyTypes(); AlwaysAssert(tp < MRadialVelocity::N_Types, AipsError); return static_cast(tp); } const String &MRadialVelocity::showType(MRadialVelocity::Types tp) { static const String tname[MRadialVelocity::N_Types] = { "LSRK", "LSRD", "BARY", "GEO", "TOPO", "GALACTO", "LGROUP", "CMB" }; MRadialVelocity::checkMyTypes(); return tname[tp]; } const String &MRadialVelocity::showType(uInt tp) { return MRadialVelocity::showType(MRadialVelocity::castType(tp)); } const String* MRadialVelocity::allMyTypes(Int &nall, Int &nextra, const uInt *&typ) { static const Int N_name = 8; static const Int N_extra = 0; static const String tname[N_name] = { "LSRK", "LSRD", "BARY", "GEO", "TOPO", "GALACTO", "LGROUP", "CMB" }; static const uInt oname[N_name] = { MRadialVelocity::LSRK, MRadialVelocity::LSRD, MRadialVelocity::BARY, MRadialVelocity::GEO, MRadialVelocity::TOPO, MRadialVelocity::GALACTO, MRadialVelocity::LGROUP, MRadialVelocity::CMB }; MRadialVelocity::checkMyTypes(); nall = N_name; nextra = N_extra; typ = oname; return tname; } const String* MRadialVelocity::allTypes(Int &nall, Int &nextra, const uInt *&typ) const { return MRadialVelocity::allMyTypes(nall, nextra, typ); } void MRadialVelocity::checkTypes() const { MRadialVelocity::checkMyTypes(); } void MRadialVelocity::checkMyTypes() { // Multiple threads could execute this, but that is harmless. static Bool first(True); if (first) { first = False; Int nall, nex; const uInt *typ; const String *const tps = MRadialVelocity::allMyTypes(nall,nex, typ); MRadialVelocity::Types tp; for (Int i=0; i=nall) return False; else tp = static_cast(oname[i]); return True; } Bool MRadialVelocity::giveMe(MRadialVelocity::Ref &mr, const String &in) { MRadialVelocity::Types tp; if (MRadialVelocity::getType(tp, in)) mr = MRadialVelocity::Ref(tp); else { mr = MRadialVelocity::Ref(); return False; } return True; } Bool MRadialVelocity::setOffset(const Measure &in) { if (!dynamic_cast(&in)) return False; ref.set(in); return True; } Bool MRadialVelocity::setRefString(const String &in) { MRadialVelocity::Types tp; if (MRadialVelocity::getType(tp, in)) { ref.setType(tp); return True; } ref.setType(MRadialVelocity::DEFAULT); return False; } const String &MRadialVelocity::getDefaultType() const { return MRadialVelocity::showType(MRadialVelocity::DEFAULT); } String MRadialVelocity::getRefString() const { return MRadialVelocity::showType(ref.getType()); } Quantity MRadialVelocity::get(const Unit &un) const { return data.get(un); } MDoppler MRadialVelocity::toDoppler() { Double t = data.getValue() / C::c; return MDoppler( MVDoppler(t), MDoppler::BETA); } MDoppler MRadialVelocity::toDoppler(const Measure &in) { MRadialVelocity::assure(in); Double t = ((MVRadialVelocity *)(in.getData()))->getValue() / C::c; return MDoppler( MVDoppler(t), MDoppler::BETA); } MRadialVelocity MRadialVelocity::fromDoppler(const MDoppler &dop) { Double t = C::c * MDoppler::Convert(dop, MDoppler::BETA)() .getValue().getValue(); return MRadialVelocity(MVRadialVelocity(t), MRadialVelocity::LSRK); } MRadialVelocity MRadialVelocity::fromDoppler(const MDoppler &dop, MRadialVelocity::Types typ) { Double t = C::c * MDoppler::Convert(dop, MDoppler::BETA)() .getValue().getValue(); return MRadialVelocity(MVRadialVelocity(t), typ); } MRadialVelocity MRadialVelocity::fromDoppler(const Measure &dop, MRadialVelocity::Types typ) { MDoppler::assure(dop); Double t = C::c * MDoppler::Convert(dop, MDoppler::BETA)() .getValue().getValue(); return MRadialVelocity(MVRadialVelocity(t), typ); } Measure *MRadialVelocity::clone() const { return (new MRadialVelocity(*this)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/MRadialVelocity.h000066400000000000000000000240441476623553700221410ustar00rootroot00000000000000//# MRadialVelocity.h: A Measure: radial velocity //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MRADIALVELOCITY_H #define MEASURES_MRADIALVELOCITY_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MRadialVelocity; class MCRadialVelocity; template class MeasConvert; template class ArrayMeasColumn; template class ScalarMeasColumn; class MDoppler; class MVDoppler; //# Typedefs // // A Measure: radial velocity // // // // // //
      • Measure class // // // // // // // MRadialVelocity is a derived Measure class for radial velocity.
        // An MRadialVelocity can be generated from a simple value (or an // MVRadialVelocity object), which is then // interpreted as a RadialVelocity in m/s, and a reference, with an LSRK type // as default.
        // It can also be generated from a Quantity, where the interpretation // depends on the dimensionality of the Quantity: //
          //
        • velocity (e.g. AU/a) //
        // The different reference types of a RadialVelocity are: //
          //
        • MRadialVelocity::LSRD --- Local Standard of Rest (J2000) -- as the // dynamical definition (IAU, [9,12,7] km/s in galactic coordinates) //
        • MRadialVelocity::LSRK -- LSR as a kinematical (radio) definition -- // 20.0 km/s in direction ra,dec = [270,+30] deg (B1900.0) //
        • MRadialVelocity::BARY -- Barycentric (J2000) //
        • MRadialVelocity::GEO --- Geocentric //
        • MRadialVelocity::TOPO -- Topocentric //
        • MRadialVelocity::GALACTO -- Galacto centric (with rotation of 220 km/s // in direction l,b = [90,0] deg. //
        • MRadialVelocity::LGROUP -- Local group velocity -- 308km/s towards // l,b = [105,-7] deg (F. Ghigo) //
        • MRadialVelocity::CMB -- CMB velocity -- 369.5km/s towards // l,b = [264.4, 48.4] deg (F. Ghigo) //
        • MRadialVelocity::DEFAULT = LSRK //
        //

        // Conversion between the different types is done with the standard // MeasConvert class // (MRadialVelocity::Convert in this case).
        // Some of the conversions are only possible if frame information has been // filled in. The following frame information is necessary if a conversion // goes to or from the (different) specified types: //

          //
        • Epoch: TOPO, GEO //
        • Position: TOPO //
        • Direction all //
        //
        // For large radial velocities (of order c) the conversions are // not precise, and not completely reversable, due to unknown transverse // velocities, and the additive way in which corrections are done. They // are correct to first order wrt relativistic effects // // An MRadialVelocity can be created from an // MDoppler // by the fromDoppler() member. It can be converted to an MDoppler // with the toDoppler(). Comparable methods are available // for MFrequency as // toRadial() and fromRadial.
        //
        // // // Get the Doppler shift for an oberved HI RadialVelocity of 100 km/s // // cout << "Redshift for 100 km/s: " << // MDoppler::Convert( MRadialVelocity( Quantity(100., "km/s"), // MRadialVelocity::TOPO).toDoppler(), // MDoppler::Z)() << endl; // // // // // // // // class MRadialVelocity : public MeasBase > { public: //# Friends // Conversion of data friend class MeasConvert; //# Enumerations // Types of known MRadialVelocity // The order defines the order in the translation // matrix FromTo // in the getConvert routine. Do not change the order without // changing the array. Additions should be made before N_types, and // an additional row and column should be coded in FromTo, and // in showType(). enum Types { LSRK, LSRD, BARY, GEO, TOPO, GALACTO, LGROUP, CMB, N_Types, // Defaults DEFAULT=LSRK, // Synonyms LSR=LSRK }; //# Typedefs // Measure value container for this class (i.e. MRadialVelocity::MVType) typedef MVRadialVelocity MVType; // Measure conversion routines for this class (i.e. MRadialVelocity::MCType) typedef MCRadialVelocity MCType; // Measure reference (i.e. MRadialVelocity::Ref) typedef MeasRef Ref; // Measure conversion use (i.e. MRadialVelocity::Convert) typedef MeasConvert Convert; // Measure table Columns (e.g., MRadialVelocity::ScalarColumn) typedef ScalarMeasColumn ScalarColumn; typedef ArrayMeasColumn ArrayColumn; //# Constructors // In the following constructors and other functions, all // MeasRef can be replaced with simple Measure::TYPE // where no offsets or frames are needed in the reference. // Default constructor; generates a zero rest RadialVelocity MRadialVelocity(); // Create from data and reference // MRadialVelocity(const MVRadialVelocity &dt); MRadialVelocity(const MVRadialVelocity &dt, const MRadialVelocity::Ref &rf); MRadialVelocity(const MVRadialVelocity &dt, MRadialVelocity::Types rf); MRadialVelocity(const Quantity &dt); MRadialVelocity(const Quantity &dt, const MRadialVelocity::Ref &rf); MRadialVelocity(const Quantity &dt, MRadialVelocity::Types rf); MRadialVelocity(const Measure *dt); MRadialVelocity(const MeasValue *dt); // //# Destructor virtual ~MRadialVelocity(); //# Operators //# General Member Functions // Tell me your type // virtual const String &tellMe() const; static const String &showMe(); static void assure(const Measure &in); // // Translate reference code. The uInt version has a check for valid codes // (i.e. it is a safe cast). // //
      • AipsError in the uInt interface if illegal code given // // static MRadialVelocity::Types castType(uInt tp); static const String &showType(MRadialVelocity::Types tp); static const String &showType(uInt tp); // // Translate string to reference code // static Bool getType(MRadialVelocity::Types &tp, const String &in); Bool giveMe(MRadialVelocity::Ref &mr, const String &in); // // Set the offset in the reference (False if non-matching Measure) virtual Bool setOffset(const Measure &in); // Set the reference type to the specified String. False if illegal // string, reference set to DEFAULT. virtual Bool setRefString(const String &in); // Get the default reference type virtual const String &getDefaultType() const; // Get a list of all known reference codes. nall returns the number in list, // nextra the number of specials (like planets) that should be at // end of list). typ returns the list of corresponding types. // virtual const String* allTypes(Int &nall, Int &nextra, const uInt *&typ) const; static const String* allMyTypes(Int &nall, Int &nextra, const uInt *&typ); // // Check if all internal tables of types (both enum and String) are // complete and correct. This function is called automatically if and when // necessary. // //
      • AipsError if a (programming) error in the types. // // virtual void checkTypes() const; static void checkMyTypes(); // // Get the reference type (for records, including codes like R_) virtual String getRefString() const; // Get radial velocity in specified units Quantity get(const Unit &un) const; // Make a Doppler velocity (as an MDoppler::BETA default) from the RadialVelocity. // MDoppler toDoppler(); // Local use only static MDoppler toDoppler(const Measure &in); // // Make a RadialVelocity from the Doppler velocity (assuming LSRK default) // static MRadialVelocity fromDoppler(const MDoppler &dop); static MRadialVelocity fromDoppler(const MDoppler &dop, MRadialVelocity::Types typ); // For internal use only static MRadialVelocity fromDoppler(const Measure &dop, MRadialVelocity::Types typ); // // Make a copy // virtual Measure *clone() const; // private: //# Enumerations //# Data //# Member functions }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MeasBase.h000066400000000000000000000112201476623553700205610ustar00rootroot00000000000000//# MeasBase.h: Base class for all measures //# Copyright (C) 1995,1996,1997,1998,1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MEASBASE_H #define MEASURES_MEASBASE_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Typedefs // Base class for all measures // // // // //
      • Measure class // // // // Measure and Base // // // // MeasBase forms derived Measure class for all actual measures // // // // // // // To have most work in single routine // // // //
      • // //# Made non-virtual for MeasureHolder //#template class MeasBase : public virtual Measure { template class MeasBase : public Measure { public: //# Friends //# Enumerations //# Typedefs //# Constructors // In the following constructors and other functions, all // MeasRef can be replaced with simple Measure::TYPE // where no offsets or frames are needed in the reference. For reasons // of compiler limitations the formal arguments had to be specified as // uInt rather than the Measure enums that should be used as actual // arguments. // Default constructor MeasBase(); // Copy constructor MeasBase(const MeasBase &other); // Copy assignment MeasBase &operator=(const MeasBase &other); // Create from data and reference // MeasBase(const Mv &dt, const Mr &rf); MeasBase(const Mv &dt, uInt rf); MeasBase(const Quantity &dt, const Mr &rf); MeasBase(const Quantity &dt, uInt rf); MeasBase(const Measure *dt); MeasBase(const Mr &rf); MeasBase(const uInt rf); // //# Destructor virtual ~MeasBase(); //# Operators //# General Member Functions // Check the type of derived entity virtual Bool areYou(const String &tp) const; // Assert that we are the correct type // //
      • AipsError if wrong Measure // virtual void assured(const String &tp) const; // Refill the specified entities // void set(const Mv &dt); void set(const Mr &rf); void set(const Mv &dt, const Mr &rf); void set(const Unit &inunit); virtual void set(const MeasValue &dt); virtual Bool putValue(const Vector > &in); // // Get reference Mr getRef() const; // Get Measure data // const Mv &getValue() const; // // Get Unit const Unit &getUnit() const; // Get reference pointer virtual MRBase *getRefPtr() const; // Get pointer to data virtual const MeasValue* getData() const; // Print a Measure virtual void print(std::ostream &os) const; protected: //# Enumerations //# Data // The measure value (e.g. instant in time) Mv data; // Reference frame data Mr ref; // Possible input units Unit unit; // Error information // MeasErr error; private: //# Member functions // Clear the measure void clear(); }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/measures/Measures/MeasBase.tcc000066400000000000000000000112311476623553700211050ustar00rootroot00000000000000//# MeasBase.cc: Base class for all measures //# Copyright (C) 1995-2002,2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MEASBASE_TCC #define MEASURES_MEASBASE_TCC //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors template MeasBase::MeasBase() : data(), ref(), unit() {} template MeasBase::MeasBase(const MeasBase &other) : Measure(other), data(other.data), ref(other.ref), unit(other.unit) {} template MeasBase &MeasBase::operator=(const MeasBase &other) { if (this != &other) { data = other.data; ref = other.ref; unit = other.unit; } return *this; } template MeasBase::MeasBase(const Mv &dt, const Mr &rf) : data(dt), ref(rf), unit() {} template MeasBase::MeasBase(const Mv &dt, uInt rf) : data(dt), ref(Mr(rf)), unit() {} template MeasBase::MeasBase(const Quantity &dt, const Mr &rf) : data(dt), ref(rf), unit(dt.getUnit()) {} template MeasBase::MeasBase(const Quantity &dt, uInt rf) : data(dt), ref(Mr(rf)), unit(dt.getUnit()) {} template MeasBase::MeasBase(const Measure *dt) : data(*(Mv*)(dt->getData())), ref(*(Mr*)(dt->getRefPtr())), unit(dt->getUnit()) {} template MeasBase::MeasBase(const Mr &rf) : data(), ref(rf), unit() {} template MeasBase::MeasBase(const uInt rf) : data(), ref(Mr(rf)), unit() {} //# Destructor template MeasBase::~MeasBase() {} //# Operators //# Member functions template void MeasBase::clear() { data = Mv(); ref = Mr(); unit = Unit(); } template Bool MeasBase::areYou(const String &tp) const { return (capitalize(tp) == tellMe()); } template void MeasBase::assured(const String &tp) const { if (capitalize(tp) != tellMe()) { throw(AipsError("Illegal Measure type in context: " + tellMe())); } } template const MeasValue* MeasBase::getData() const { return &data; } template void MeasBase::set(const Mv &dt) { data = dt; } template void MeasBase::set(const Mr &rf) { ref = rf; } template void MeasBase::set(const Mv &dt, const Mr &rf) { data = dt; ref = rf; } template void MeasBase::set(const Unit &inunit) { unit = inunit; } template void MeasBase::set(const MeasValue &dt) { data = dynamic_cast(dt); } template Bool MeasBase::putValue(const Vector > &in) { return data.putValue(in); } template Mr MeasBase::getRef() const { return ref; } template const Mv &MeasBase::getValue() const { return data; } template const Unit &MeasBase::getUnit() const { return unit; } template MRBase *MeasBase::getRefPtr() const { // Throw away const return (MRBase *) &ref; } template void MeasBase::print(std::ostream &os) const { os << tellMe() << ": " << data; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MeasComet.cc000066400000000000000000000307061476623553700211260ustar00rootroot00000000000000//# MeasComet.cc: To define position for comets and other solar system bodies //# Copyright (C) 2000-2002,2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors MeasComet::MeasComet() : tab_p(), measFlag_p(True), measured_p(False), row_p(), mjd0_p(0), mjdl_p(0), dmjd_p(0), nrow_p(0), name_p(), topo_p(), mtype_p(MDirection::APP), // default, if the keyword obsloc is not defined, is apparent geocentric msgDone_p(False), tp_p(), haveDiskLongLat_p(false), ncols_p(5), hasPosrefsys_p(false), posrefsystype_p(MDirection::APP) { String path; if (Aipsrc::find(path, String("measures.comet.file"))) initMeas(path); for (uInt i=0; i<2; i++) lnr_p[i] = -1; } MeasComet::MeasComet(const String &path) : tab_p(), measFlag_p(True), measured_p(False), row_p(), mjd0_p(0), mjdl_p(0), dmjd_p(0), nrow_p(0), name_p(), topo_p(), mtype_p(MDirection::APP), msgDone_p(False), tp_p(path), haveDiskLongLat_p(false), ncols_p(5), hasPosrefsys_p(false), posrefsystype_p(MDirection::APP) { initMeas(path); for (uInt i=0; i<2; i++) lnr_p[i] = -1; } MeasComet::MeasComet(const Table &tabin, const String &path) : tab_p(), measFlag_p(True), measured_p(False), row_p(), mjd0_p(0), mjdl_p(0), dmjd_p(0), nrow_p(0), name_p(), topo_p(), mtype_p(MDirection::APP), msgDone_p(False), tp_p(path), haveDiskLongLat_p(false), ncols_p(5), hasPosrefsys_p(false), posrefsystype_p(MDirection::APP) { initMeas(path, &tabin); for (uInt i=0; i<2; i++) lnr_p[i] = -1; } MeasComet::MeasComet(const MeasComet &other) : tab_p(), measFlag_p(True), measured_p(False), row_p(), mjd0_p(0), mjdl_p(0), dmjd_p(0), nrow_p(0), name_p(), topo_p(), mtype_p(MDirection::APP), msgDone_p(False), tp_p(other.tp_p), haveDiskLongLat_p(other.haveDiskLongLat_p), ncols_p(other.ncols_p), hasPosrefsys_p(other.hasPosrefsys_p), posrefsystype_p(other.posrefsystype_p) { initMeas(other.tp_p); for (uInt i=0; i<2; i++) lnr_p[i] = -1; } MeasComet &MeasComet::operator=(const MeasComet &other) { if (this != &other) { initMeas(other.tp_p); for (uInt i=0; i<2; i++) lnr_p[i] = -1; } return *this; } MeasComet::~MeasComet() {} //# Member functions const String &MeasComet::getName() const { return name_p; } const MVPosition &MeasComet::getTopo() const { return topo_p; } MDirection::Types MeasComet::getType() const { return mtype_p; } Double MeasComet::getStart() const { return mjd0_p + dmjd_p; } Double MeasComet::getEnd() const { return mjdl_p; } Int MeasComet::nelements() const { return nrow_p; } Bool MeasComet::hasPosrefsys() const { return hasPosrefsys_p; } MDirection::Types MeasComet::getPosrefsysType() const { return posrefsystype_p; } Bool MeasComet::get(MVPosition &returnValue, Double date) const { if(!fillMeas(date)){ returnValue = MVPosition(); return False; } Double f = (date - ldat_p[0][0])/dmjd_p; returnValue = getRelPosition(0); const MVPosition deltaX(getRelPosition(1) - returnValue); returnValue += f * deltaX; return True; } MVPosition MeasComet::getRelPosition(const uInt index) const { return MVPosition(Quantity(ldat_p[index][MeasComet::RHO], "AU"), Quantity(ldat_p[index][MeasComet::RA], "deg"), Quantity(ldat_p[index][MeasComet::DEC], "deg")); } Bool MeasComet::getDisk(MVDirection &returnValue, Double date) const { if(!haveDiskLongLat_p || !fillMeas(date)){ returnValue = MVDirection(); return False; } Double f = (date - ldat_p[0][0])/dmjd_p; returnValue = getDiskLongLat(0); const MVDirection ll_on_second_date(getDiskLongLat(1)); Double sep = returnValue.separation(ll_on_second_date); Double pa = returnValue.positionAngle(ll_on_second_date); returnValue.shiftAngle(f * sep, pa); return True; } MVDirection MeasComet::getDiskLongLat(const uInt index) const { return MVDirection(Quantity(ldat_p[index][MeasComet::DISKLONG], "deg"), Quantity(ldat_p[index][MeasComet::DISKLAT], "deg")); } Bool MeasComet::getRadVel(MVRadialVelocity &returnValue, Double date) const { returnValue = 0.0; if (!fillMeas(date)) return False; Double f = (date - ldat_p[0][0])/dmjd_p; Double radvel = ldat_p[0][MeasComet::RADVEL]; Double deltarv = ldat_p[1][MeasComet::RADVEL] - radvel; radvel += f * deltarv; returnValue = MVRadialVelocity(Quantity(radvel, "AU/d")); return True; } MeasComet *MeasComet::clone() const { return (new MeasComet(*this)); } Bool MeasComet::initMeas(const String &which, const Table *tabin) { Vector reqcols(5); // Required columns. reqcols[0] = "MJD"; reqcols[1] = "RA"; reqcols[2] = "DEC"; reqcols[3] = "Rho"; // Distance from Earth in AU. reqcols[4] = "RadVel"; // AU/d Vector optcols(2); // Get these columns if the table has them. optcols[0] = "DiskLong"; // The positions of surface features may be optcols[1] = "DiskLat"; // neither known nor needed. static const String tplc = "measures.comet.directory"; if (!measured_p && measFlag_p) { LogIO os(LogOrigin("MeasComet", String("initMeas(String, Table *)"), WHERE)); closeMeas(); // seems to need this to ensure full initialization (TT) measFlag_p = False; tp_p = which; TableRecord kws; Double dt; String vs; Bool ok = True; if (!MeasIERS::getTable(tab_p, kws, row_p, rfp_p, vs, dt, reqcols, optcols, tp_p, tplc, String("ephemerides"), tabin)) { return False; } ncols_p = reqcols.nelements() + optcols.nelements(); ldat_p[0].resize(ncols_p); ldat_p[1].resize(ncols_p); // Make this more sophisticated if the number of optional columns grows. // That could also cause problems where enums like MeasComet::DiskLong are // used in ldat_p. haveDiskLongLat_p = (optcols.nelements() == 2); if (!kws.isDefined("MJD0") || kws.asDouble("MJD0") < 10000 || !kws.isDefined("dMJD") || kws.asDouble("dMJD") <= 0 || !kws.isDefined("NAME")){ ok = False; os << LogIO::SEVERE; if(!kws.isDefined("MJD0")) os << "MJD0 is not defined.\n"; else if(kws.asDouble("MJD0") < 10000) os << "MJD0, " << kws.asDouble("MJD0") << " is < 10000.\n"; if(!kws.isDefined("dMJD")) os << "dMJD is not defined.\n"; else if(kws.asDouble("dMJD") <= 0.0) os << "dMJD, " << kws.asDouble("dMJD") << " is < 0.\n"; if(!kws.isDefined("NAME")) os << "NAME is not defined."; os << LogIO::POST; } if (ok) { name_p = kws.asString("NAME"); topo_p = MVPosition(Quantity(kws.asDouble("GeoDist"), "km"), Quantity(kws.asDouble("GeoLong"), "deg"), Quantity(kws.asDouble("GeoLat"), "deg")); if (kws.isDefined("posrefsys")) { String prs = kws.asString("posrefsys"); prs.upcase(); if(prs.contains("J2000")){ mtype_p = MDirection::J2000; }else if(prs.contains("B1950")){ mtype_p = MDirection::B1950; }else if(prs.contains("APP")){ mtype_p = MDirection::APP; }else if(prs.contains("ICRS")){ mtype_p = MDirection::ICRS; }else if(prs.contains("TOPO")){ mtype_p = MDirection::TOPO; }else{ os << LogIO::SEVERE << "Unrecognized position reference frame (posrefsys): " << kws.asString("posrefsys") << " - possible are J2000, B1950, APP, ICRS, TOPO" << LogIO::POST; } posrefsystype_p=mtype_p; hasPosrefsys_p=true; } else if (kws.asDouble("GeoDist") != 0.0){ mtype_p = MDirection::TOPO; } mjd0_p = kws.asDouble("MJD0"); dmjd_p = kws.asDouble("dMJD"); nrow_p = tab_p.nrow(); row_p.get(nrow_p-1); if (!nearAbs(*(rfp_p[0]), mjd0_p + nrow_p*dmjd_p, 0.1*dmjd_p)) { os << LogIO::SEVERE << "MJD has a problem." << LogIO::POST; os << LogIO::DEBUG1 << "*(rfp_p[0]) = " << *(rfp_p[0]) << "\nmjd0_p = " << mjd0_p << "\nnrow_p = " << nrow_p << "\ndmjd_p = " << dmjd_p << LogIO::POST; ok = False; } else { mjdl_p = mjd0_p + nrow_p*dmjd_p; } } if (!ok) { os << String("Invalid comet table ") + tp_p << LogIO::EXCEPTION; } measured_p = True; } haveTriedExtras_p = false; // Defer reading them until asked to. return (measured_p); } Double MeasComet::getTemperature(const Bool squawk) { if(!haveTriedExtras_p) getExtras(); if(temperature_p < 0.0 && squawk){ LogIO os(LogOrigin("MeasComet", String("getTemperature(True)"), WHERE)); os << LogIO::SEVERE << "The comet table is missing the T_mean keyword, which holds the temperature." << LogIO::POST; } return temperature_p; } Double MeasComet::getMeanRad(const Bool squawk) { if(!haveTriedExtras_p) getExtras(); if(mean_rad_p < 0.0 && squawk){ LogIO os(LogOrigin("MeasComet", String("getMeanRad(True)"), WHERE)); os << LogIO::SEVERE // Remove/modify this when it starts supporting triaxiality. << "The table is missing the meanrad keyword, needed to calculate the apparent diameter." << LogIO::POST; } return mean_rad_p; } Double MeasComet::get_Quantity_keyword(const TableRecord& ks, const String& kw, const Unit& unit, Bool& success) { try{ const Record rec(ks.asRecord(kw)); const Quantity q(rec.asDouble("value"), rec.asString("unit")); success = true; return q.get(unit).getValue(); } catch(...){ success = false; return 0.0; } } String MeasComet::getTablePath() { return Path(tab_p.tableName()).absoluteName(); } Bool MeasComet::getExtras() { if(haveTriedExtras_p) // That was easy. return true; const TableRecord ks(tab_p.keywordSet()); Bool got_q = true; // Use impossible values to indicate failure to _successfully_ read any given // quantity. haveTriedExtras_p = true; temperature_p = get_Quantity_keyword(ks, "T_mean", "K", got_q); if(!got_q) temperature_p = -1; // Hopefully a model for the obj will supply a // temperature later. mean_rad_p = get_Quantity_keyword(ks, "meanrad", "AU", got_q); if(!got_q) mean_rad_p = -1.0; return true; } void MeasComet::closeMeas() { if (Table::isOpened(tp_p) || measured_p || !measFlag_p) { measFlag_p = True; measured_p = False; mjd0_p = 0; mjdl_p = 0; dmjd_p = 0; nrow_p = 0; tp_p = ""; msgDone_p = False; for (uInt i=0; i<2; ++i) lnr_p[i] = -1; row_p = ROTableRow(); tab_p = Table(); } } Bool MeasComet::fillMeas(Double utf) const { Int ut = ifloor((utf-mjd0_p)/dmjd_p)-1; if (ut<0 || ut >= nrow_p-1) return False; if (ut != lnr_p[0]) { if (ut == lnr_p[1]) { // Shift one for(uInt i = 0; i < ncols_p; ++i) ldat_p[0][i] = ldat_p[1][i]; lnr_p[0] = lnr_p[1]; } else { // Read first line row_p.get(ut); for(uInt i = 0; i < ncols_p; ++i) ldat_p[0][i] = *(rfp_p[i]); lnr_p[0] = ut; } // Read second line row_p.get(ut+1); for(uInt i = 0; i < ncols_p; ++i) ldat_p[1][i] = *(rfp_p[i]); lnr_p[1] = ut+1; } return True; } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/MeasComet.h000066400000000000000000000202561476623553700207670ustar00rootroot00000000000000//# MeasComet.h: To define position for comets and other solar system bodies //# Copyright (C) 1999,2000,2002,2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MEASCOMET_H #define MEASURES_MEASCOMET_H //# Includes #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MVRadialVelocity; class MVDirection; // Position for comets and other solar system bodies // // // // //
      • MeasTable // // // // From Measure and Comet // // // // MeasComet is the interface class between generated Comet position // tables and the Direction conversion machinery. // Tables are found using the aipsrc // (using measures..directory) // mechanism. If not provided they are assumed to reside in standard places // Tables are assumed to have the // VS_VERSION, VS_DATE, VS_CREATE, VS_TYPE, // MJD0 (first MJD in table - 1.0 * dMJD, >= 10000), // dMJD (increment between successive MJDs, in days, > 0), // and NAME // keywords, be gapless (constant dMJD), and be of type IERS, // or else an exception will be thrown.
        // They are also assumed to have the MJD, RA, DEC, Rho, and RadVel columns. // The DiskLong and DiskLat columns can be used if they are present, but they // are no longer expected. // The get() method will obtain data from the cometary // tables. The data obtained will be in the specified frame. // Note that the normal usage of these tables is through the Measures system. // // // A message is logged (once) if a date outside the range in // the Tables is asked for. // // //
      • AipsError if table opened has wrong format or otherwise corrupted. // // // // // See test/tMeasComet.cc. // // tbd // // // // // To use the JPL data for positions of solar system bodies // // // // class MeasComet { public: //# Constants //# Enumerations // Types of known data enum Types { // MJD (must be first in list) MJD, // Columns with data RA, DEC, RHO, RADVEL, DISKLONG, DISKLAT, // Number of columns N_Columns, N_Types }; //# Constructors // Construct using the aipsrc value (measures.comet.file) MeasComet(); // Construct a table from the named path. explicit MeasComet(const String &path); // Construct a table from the name and the input table MeasComet(const Table &tabin, const String &path); // Copy constructor MeasComet(const MeasComet &other); // Copy assign MeasComet &operator=(const MeasComet &other); //# Destructor ~MeasComet(); //# General Member Functions // Is it a valid comet class (i.e. can it be used) Bool ok() const {return measured_p;} ; // Get the name of the comet const String &getName() const; // Get the topo position const MVPosition &getTopo() const; // Get the direction type MDirection::Types getType() const; // Get the start of the table (in MJD) Double getStart() const; // Get the end of the table (in MJD) Double getEnd() const; // Get number of entries Int nelements() const; // Get a comet position Bool get(MVPosition &returnValue, Double date) const; // Get the local on-disk direction. Returns False if the time or sub-observer // longitude and latitude are unavailable, True on success. Bool getDisk(MVDirection &returnValue, Double date) const; // Get the velocity from a comet table, interpolated for date(in MJD(TDB)). Bool getRadVel(MVRadialVelocity &returnValue, Double date) const; // Return the temperature in K, or -1 if the table does not have it. // If squawk is true an error message will also be posted. Double getTemperature(const Bool squawk); // Return the mean radius in AU, or -1 if the table does not have it. // If squawk is true an error message will also be posted. Double getMeanRad(const Bool squawk); // Create a clone MeasComet *clone() const; // Close the Comet tabls only void closeMeas(); // Convenience function that returns ks[kw] in units of unit, setting // success. static Double get_Quantity_keyword(const TableRecord& ks, const String& kw, const Unit& unit, Bool& success); // Convenience function that returns the absolute path to the ephemeris table // connected to the MeasComet object String getTablePath(); ////Comet table has posrefsys defined Bool hasPosrefsys() const; ///Get the posrefsys dir type MDirection::Types getPosrefsysType() const; private: //# General member functions // Initialise table from the name given Bool initMeas(const String &which, const Table *tabin=0); // Fill Table lines Bool fillMeas(Double utf) const; // Helper functions for accessing ldat_p. index should be either 0 or 1, but // that isn't checked! MVPosition getRelPosition(const uInt index) const; MVDirection getDiskLongLat(const uInt index) const; // Must not be called if !haveDiskLongLat_p // Try to read mean_rad_p and temperature_p, returning whether or not it was // successful. (but the real mark of success is whether or not they are // positive.) // It sets haveTriedExtras_p to true and will return right away if it is // already true. Bool getExtras(); //# Data members // Initialized in the "initialization list" of the c'tors, so maintain order: // Actual table Table tab_p; // Measured data readable Bool measFlag_p; // Measured data present Bool measured_p; // Row descriptions ROTableRow row_p; // First MJD in list - 1.0 * dmjd_p Double mjd0_p; // Last MJD in list Double mjdl_p; // Increment in rows Double dmjd_p; // Number of rows Int nrow_p; // Name of comet String name_p; // Position on Earth MVPosition topo_p; // Type of coordinates MDirection::Types mtype_p; // Message given Bool msgDone_p; // File names String tp_p; // Whether or not the sub-observer longitude and latitude are available. Bool haveDiskLongLat_p; uInt ncols_p; // # of columns. // These may be initialized _inside_ the c'tors, but the order here is // unimportant: // Field pointers Vector > rfp_p; // Lines in memory mutable Int lnr_p[2]; // Why are these mutables here? // Last read data (measlow - meashigh) mutable Vector ldat_p[2]; // They allow declaring a const // which isn't. Bool haveTriedExtras_p; Double temperature_p; Double mean_rad_p; Bool hasPosrefsys_p; MDirection::Types posrefsystype_p; }; //# Inline Implementations } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MeasConvert.h000066400000000000000000000245231476623553700213410ustar00rootroot00000000000000//# MeasConvert.h: Conversion of Measures //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MEASCONVERT_H #define MEASURES_MEASCONVERT_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MCBase; class MeasVal; //# Typedefs //# Constants // Conversion of Measures // // // // //
      • Measure class //
      • MeasRef base class //
      • MConvertBase class //
      • Quantum class // // // // // // // MeasConvert can convert a Measure to the same type of Measure in a // different reference frame. The MeasConvert is a templated class, but // has typedefs, which are strongly recommended to be used, // for the allowed conversions, like MEpoch::Convert.
        // The basic operation is to create a MeasConvert with either of: //
          //
        • MEpoch::Convert(MEpoch, MEpoch::Ref), where the // MEpoch is a template for subsequent // conversions, i.e. it will remember the value (with its reference) and // the MeasRef output reference. //
        • MEpoch::Convert(MEpoch) with a subsequent setOut(MEpoch::Ref) //
        • MEpoch::Convert(MEpoch::Ref in, MEpoch::Ref out) is a template for // conversions from the input reference to the output reference. The // 'template' model used is the default value for the Measure, with // no units. //
        • MEpoch::Convert(Unit, MEpoch::Ref in, MEpoch::Ref out) is a // template for // conversions from the input reference to the output reference. The // 'template' model used is the default value for the Measure, with // the default units as specified. //
        • MEpoch::Convert() with a setModel(MEpoch) and setOut(). //
        // An empty MeasRef argument indicates no conversion will be attempted
        . // The constructor, and set functions, analyse the 'template' Measure and the // output reference frame, and construct a pointer (in practice a list // of pointers to bypass the necessity of creating too many conversion // functions) to a conversion routine. // // An isNOP() function is available to test if the created // conversion engine is empty. // // Actual conversions are done with the () operator, which produces a new // MEpoch (or other appropiate Measure).
        // Possible arguments are (MVEpoch is used here generic, and indicates the // internal format of a Measure; possibly, to make sure distinction between // values with and without units possible, even simple Measures will // have their own internal class format, e.g. MVDouble. // The possible arguments to the () conversion operator are (again Epoch // is used for the generic Measure): //
          //
        • (MEpoch, MEpoch::Ref): will create a new conversion method, and use // it to produce the result of converting the MEpoch to the specified // frame //
        • (MEpoch): will create a new conversion method from the // MEpoch to the MeasRef belonging to the MeasConvert //
        • (Quantity): will use the conversion chain deduced from the // MEpoch model in the definition of MeasConvert, and will convert the // Quantity //
        • (Quantum >) as previous //
        • (Double): will use the units (if present) as specified in the // MeasConvert object to construct the internal value // to be converted //
        • (Vector >): as previous //
        // Float versions will be produced if necessary.
        // The conversion analyser expects that all Measure classes have a set // of routines to do the actual analysing and conversion. // (see MCBase class for how this is done in // practice).
        // If the standard conversion is not sufficient, additional methods can be // added at the end of the list with the addMethod() member // function (for real pros).
        //
        // // // See Measure for an example // // // // Conversion of Measures will in general be done on a series of values. // Separating the analysis of the calculations necessary for the conversion // from the actual conversion could speed up the process. // // // // template class MeasConvert : public MConvertBase { public: //# Friends //# Constructors // In the following constructors and other functions, all // MeasRef can be replaced with simple Measure::TYPE // where no offsets or frames are needed in the reference. // Construct an empty MeasConvert. It is not usable, unless a setModel, and // probably a setOut has been done. MeasConvert(); // Copy constructor MeasConvert(const MeasConvert &other); // Copy assignment MeasConvert &operator=(const MeasConvert &other); // Construct a conversion for the specified Measure and reference // MeasConvert(const M &ep); MeasConvert(const M &ep, const typename M::Ref &mr); MeasConvert(const Measure &ep, const typename M::Ref &mr); MeasConvert(const M &ep, typename M::Types mr); MeasConvert(const Measure &ep, typename M::Types mr); MeasConvert(const typename M::Ref &mrin, const typename M::Ref &mr); MeasConvert(const typename M::Ref &mrin, typename M::Types mr); MeasConvert(typename M::Types mrin, const typename M::Ref &mr); MeasConvert(typename M::Types mrin, typename M::Types mr); MeasConvert(const Unit &inunit, const typename M::Ref &mrin, const typename M::Ref &mr); MeasConvert(const Unit &inunit, const typename M::Ref &mrin, typename M::Types mr); MeasConvert(const Unit &inunit, typename M::Types mrin, const typename M::Ref &mr); MeasConvert(const Unit &inunit, typename M::Types mrin, typename M::Types mr); // //# Destructor ~MeasConvert(); //# Operators // The actual conversion operations // // Convert model Measure to output frame const M &operator()(); const M &operator()(Double val); const M &operator()(const Vector &val); const M &operator()(const Quantum &val); const M &operator()(const Quantum > &val); const M &operator()(const typename M::MVType &val); const M &operator()(const MeasVal *val); const M &operator()(const M &val); const M &operator()(const M &val, const typename M::Ref &mr); const M &operator()(const M &val, typename M::Types mr); const M &operator()(const typename M::Ref &mr); const M &operator()(typename M::Types mr); // //# General Member Functions // Set a new model for the conversion virtual void setModel(const Measure &val); // Set a new output reference // void setOut(const typename M::Ref &mr); void setOut(typename M::Types mr); // // Set a new model and reference // void set(const M &val, const typename M::Ref &mr); void set(const M &val, typename M::Types mr); // // Set a new model value only virtual void set(const MeasValue &val); // Set a new model unit only virtual void set(const Unit &inunit); // Add a method (Note: uInt should be an enum from the appropiate Measure) virtual void addMethod(uInt method); // Add the frame type (Note: tp should be an MeasFrame::FrameType) virtual void addFrameType(uInt tp); // Get number of methods virtual Int nMethod() const; // Get method virtual uInt getMethod(uInt which) const; // Is the conversion engine empty? Bool isNOP() { return crout.nelements() == 0; } // Print conversion engine virtual void print(ostream &os) const; private: //# Data // The model template Measure Measure *model; // The model unit to be used in conversions Unit unit; // The output reference typename M::Ref outref; // The input offset typename M::MVType *offin; // The output offset typename M::MVType *offout; // Vector of conversion routines (length variable) Block crout; // Coded (with MeasFrame::FrameTypes) frames used in conversion uInt crtype; // Local conversion data MCBase *cvdat; // Cyclic buffer for return values // // Current pointer Int lres; M *result[4]; // // Local variables that can be used in conversion // typename M::MVType *locres; // //# Member functions // Initialise pointers void init(); // Copy a MeasConvert void copy(const MeasConvert &other); // Clear self void clear(); // Create the conversion routine chain void create(); // Convert a value // const typename M::MVType &convert(); const typename M::MVType &convert(const typename M::MVType &val); // }; //# Global functions } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/measures/Measures/MeasConvert.tcc000066400000000000000000000320261476623553700216600ustar00rootroot00000000000000//# MeasConvert.cc: Conversion of Measures //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MEASCONVERT_TCC #define MEASURES_MEASCONVERT_TCC //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors template MeasConvert::MeasConvert() : model(0), unit(), outref(), offin(0), offout(0), crout(0), crtype(0), cvdat(0), lres(0), locres(0) { init(); } template MeasConvert::MeasConvert(const MeasConvert &other) : MConvertBase(other), model(0), unit(), outref(), offin(0), offout(0), crout(0), crtype(0), cvdat(0), lres(0), locres(0) { init(); copy(other); } template MeasConvert &MeasConvert::operator=(const MeasConvert &other) { if (this != &other) { copy(other); } return *this; } template MeasConvert::MeasConvert(const M &ep) : model(0), unit(ep.unit), outref(), offin(0), offout(0), crout(0), crtype(0), cvdat(0), lres(0), locres(0) { init(); model = new M(ep); create(); } template MeasConvert::MeasConvert(const M &ep, const typename M::Ref &mr) : model(0), unit(ep.unit), outref(), offin(0), offout(0), crout(0), crtype(0), cvdat(0), lres(0), locres(0) { init(); model = new M(ep); outref = mr; create(); } template MeasConvert::MeasConvert(const Measure &ep, const typename M::Ref &mr) : model(0), unit(ep.getUnit()), outref(), offin(0), offout(0), crout(0), crtype(0), cvdat(0), lres(0), locres(0) { init(); model = ep.clone(); outref = mr; create(); } template MeasConvert::MeasConvert(const M &ep, typename M::Types mr) : model(0), unit(ep.unit), outref(), offin(0), offout(0), crout(0), crtype(0), cvdat(0), lres(0), locres(0) { init(); model = new M(ep); outref = typename M::Ref(mr); create(); } template MeasConvert::MeasConvert(const Measure &ep, typename M::Types mr) : model(0), unit(ep.getUnit()), outref(), offin(0), offout(0), crout(0), crtype(0), cvdat(0), lres(0), locres(0) { init(); model = ep.clone(); outref = typename M::Ref(mr); create(); } template MeasConvert::MeasConvert(const typename M::Ref &mrin, const typename M::Ref &mr) : model(0), unit(), outref(), offin(0), offout(0), crout(0), crtype(0), cvdat(0), lres(0), locres(0) { init(); model = new M(typename M::MVType(), mrin); outref = mr; create(); } template MeasConvert::MeasConvert(const typename M::Ref &mrin, typename M::Types mr) : model(0), unit(), outref(), offin(0), offout(0), crout(0), crtype(0), cvdat(0), lres(0), locres(0) { init(); model = new M(typename M::MVType(), mrin); outref = typename M::Ref(mr); create(); } template MeasConvert::MeasConvert(typename M::Types mrin, const typename M::Ref &mr) : model(0), unit(), outref(), offin(0), offout(0), crout(0), crtype(0), cvdat(0), lres(0), locres(0) { init(); model = new M(typename M::MVType(), typename M::Ref(mrin)); outref = mr; create(); } template MeasConvert::MeasConvert(typename M::Types mrin, typename M::Types mr) : model(0), unit(), outref(), offin(0), offout(0), crout(0), crtype(0), cvdat(0), lres(0), locres(0) { init(); model = new M(typename M::MVType(), typename M::Ref(mrin)); outref = typename M::Ref(mr); create(); } template MeasConvert::MeasConvert(const Unit &inunit, const typename M::Ref &mrin, const typename M::Ref &mr) : model(0), unit(inunit), outref(), offin(0), offout(0), crout(0), crtype(0), cvdat(0), lres(0), locres(0) { init(); model = new M( typename M::MVType(), mrin); outref = mr; create(); } template MeasConvert::MeasConvert(const Unit &inunit, const typename M::Ref &mrin, typename M::Types mr) : model(0), unit(inunit), outref(), offin(0), offout(0), crout(0), crtype(0), cvdat(0), lres(0), locres(0) { init(); model = new M( typename M::MVType(), mrin); outref = typename M::Ref(mr); create(); } template MeasConvert::MeasConvert(const Unit &inunit, typename M::Types mrin, const typename M::Ref &mr) : model(0), unit(inunit), outref(), offin(0), offout(0), crout(0), crtype(0), cvdat(0), lres(0), locres(0) { init(); model = new M( typename M::MVType(), typename M::Ref(mrin)); outref = mr; create(); } template MeasConvert::MeasConvert(const Unit &inunit, typename M::Types mrin, typename M::Types mr) : model(0), unit(inunit), outref(), offin(0), offout(0), crout(0), crtype(0), cvdat(0), lres(0), locres(0) { init(); model = new M( typename M::MVType(), typename M::Ref(mrin)); outref = typename M::Ref(mr); create(); } //# Destructor template MeasConvert::~MeasConvert() { clear(); } //# Operators template const M &MeasConvert::operator()() { return operator()(*(typename M::MVType*)(model->getData())); } template const M &MeasConvert::operator()(Double val) { if (unit.empty()) { *locres = typename M::MVType(val); } else { *locres = typename M::MVType(Quantity(val,unit)); } return operator()(*locres); } template const M &MeasConvert::operator()(const Vector &val) { if (unit.empty()) *locres = typename M::MVType(val); else *locres = typename M::MVType(Quantum >(val,unit)); return operator()(*locres); } template const M &MeasConvert::operator()(const Quantum &val) { unit = val.getUnit(); *locres = typename M::MVType(val); return operator()(*locres); } template const M &MeasConvert::operator()(const Quantum > &val) { unit = val.getUnit(); *locres = typename M::MVType(val); return operator()(*locres); } template const M &MeasConvert::operator()(const typename M::MVType &val) { *locres = convert(val); if (offout) *locres -= *offout; lres++; lres %= 4; *(result[lres]) = M(*locres,outref); return (*(result[lres])); } template const M &MeasConvert::operator()(const MeasVal *val) { return operator()(*(const typename M::MVType *)(val)); } template const M &MeasConvert::operator()(const M &val) { setModel(val); return operator()(*(typename M::MVType*)(model->getData())); } template const M &MeasConvert::operator()(const M &val, const typename M::Ref &mr) { set(val,mr); return operator()(*(typename M::MVType*)(model->getData())); } template const M &MeasConvert::operator()(const M &val, typename M::Types mr) { set(val,mr); return operator()(*(typename M::MVType*)(model->getData())); } template const M &MeasConvert::operator()(const typename M::Ref &mr) { setOut(mr); return operator()(*(typename M::MVType*)(model->getData())); } template const M &MeasConvert::operator()(typename M::Types mr) { setOut(mr); return operator()(*(typename M::MVType*)(model->getData())); } //# Member functions template void MeasConvert::init() { cvdat = new typename M::MCType(); for (Int i=0; i<4; i++) result[i] = new M(); locres = new typename M::MVType(); } template void MeasConvert::clear() { delete model; model = 0; unit = Unit(); outref = typename M::Ref(); crout.resize(0, True); crtype = 0; cvdat->clearConvert(); delete cvdat; cvdat = 0; delete offin; offin = 0; delete offout; offout = 0; delete locres; locres = 0; for (Int j=0; j < 4; j++) { delete result[j]; result[j] = 0; } } template void MeasConvert::copy(const MeasConvert &other) { clear(); init(); if (other.model) model = new M(other.model); unit = other.unit; outref = other.outref; create(); } template void MeasConvert::addMethod(uInt method) { crout.resize(crout.nelements() + 1); crout[crout.nelements() - 1] = method; } template void MeasConvert::addFrameType(uInt tp) { crtype |= tp; } template Int MeasConvert::nMethod() const { return crout.nelements(); } template uInt MeasConvert::getMethod(uInt which) const { return crout[which]; } template void MeasConvert::create() { delete offin; offin = 0; if (model && model->getRefPtr()->offset()) { typename M::MVType *ptmp = (typename M::MVType *)(model->getRefPtr()->offset()->getData()); // Next due to compiler error (gcc) MRBase *rptmp(model->getRefPtr()); typename M::Types tptmp = static_cast(rptmp->getType()); MeasFrame mftmp = rptmp->getFrame(); typename M::Ref rtmp(tptmp, mftmp); typename M::Ref mrtmp(*(typename M::Ref*)(model->getRefPtr()-> offset()->getRefPtr())); if (!mrtmp.empty()) { M mtmp(*ptmp, mrtmp); offin = new typename M::MVType(MeasConvert(mtmp, rtmp).convert()); } else { offin = new typename M::MVType(*ptmp); } } delete offout; offout = 0; if (outref.offset()) { typename M::MVType *ptmp = (typename M::MVType *)(outref.offset()->getData()); typename M::Ref rtmp(outref.getType(), outref.getFrame()); typename M::Ref mrtmp(*(typename M::Ref *)(outref.offset()->getRefPtr())); if (!mrtmp.empty()) { M mtmp(*ptmp, mrtmp); offout = new typename M::MVType(MeasConvert(mtmp, rtmp).convert()); } else { offout = new typename M::MVType(*ptmp); } } crout.resize(0, True); crtype = 0; // Make sure a reference given if (model && model->getRefPtr()->empty()) { ((MeasBase *)model) ->set(typename M::Ref(M::DEFAULT)); } if (outref.empty()) outref = typename M::Ref(M::DEFAULT); if (model && !(model->getRefPtr()->empty()) && !(outref.empty())) { // Next due to compiler error (gcc) MRBase *rptmp(model->getRefPtr()); MeasFrame mftmp = rptmp->getFrame(); if (!(mftmp.empty()) && !(outref.getFrame().empty()) && mftmp != outref.getFrame()) { MRBase *reftmp = new typename M::Ref(M::DEFAULT); cvdat->getConvert(*this, *model->getRefPtr(), *reftmp); cvdat->getConvert(*this, *reftmp, outref); delete reftmp; } else { cvdat->getConvert(*this, *model->getRefPtr(), outref); } } } template const typename M::MVType &MeasConvert::convert() { return convert(*(typename M::MVType*)(model->getData())); } template const typename M::MVType &MeasConvert:: convert(const typename M::MVType &val) { *locres = val; if (offin) *locres += *offin; cvdat->doConvert(*locres, *model->getRefPtr(), outref, *this); return *locres; } template void MeasConvert::setModel(const Measure &val) { delete model; model = 0; model = new M(&val); unit = val.getUnit(); create(); } template void MeasConvert::setOut(const typename M::Ref &mr) { outref = mr; create(); } template void MeasConvert::setOut(typename M::Types mr) { outref = typename M::Ref(mr); create(); } template void MeasConvert::set(const M &val, const typename M::Ref &mr) { delete model; model = 0; model = new M(val); unit = val.unit; outref = mr; create(); } template void MeasConvert::set(const M &val, typename M::Types mr) { delete model; model = 0; model = new M(val); unit = val.unit; outref = typename M::Ref(mr); create(); } template void MeasConvert::set(const MeasValue &val) { if (model) { model->set(val); } else { model = new M(&val); create(); } } template void MeasConvert::set(const Unit &inunit) { unit = inunit; } template void MeasConvert::print(ostream &os) const { os << "Converter with"; if (model) os << " Template Measure" << *model; if (!outref.empty()) os << " Output reference" << outref; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MeasData.cc000066400000000000000000000156431476623553700207330ustar00rootroot00000000000000//# MeasData.cc: MeasData provides Measure computing data //# Copyright (C) 1995,1996,1997,1998,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constants const Double MeasData::MJD2000 = 51544.5; const Double MeasData::MJDB1950 = 33281.92345905; const Double MeasData::MJDB1900 = 15019.5; const Double MeasData::MJDB1850 = -3242.29642; const Double MeasData::TROPCEN = 36524.2198782; const Double MeasData::JDCEN = 36525.0; const Double MeasData::SECinDAY = (3600.*24.); //# Member functions // Galactic coordinates const RotMatrix &MeasData::GALtoB1950() { static Bool needInit = True; static RotMatrix rot; static const Double data[3][3] = { { -0.0669887394, +0.4927284661, -0.8676008112}, { -0.8727557659, -0.4503469580, -0.1883746017}, { -0.4835389146, +0.7445846333, +0.4601997848} }; if (needInit) { // Multiple threads could execute this, but that is harmless. Int i,j; for (i=0; i<3; i++) { for (j=0; j<3; j++) { rot(i,j) = data[i][j]; } } needInit = False; } return rot; } const RotMatrix &MeasData::B1950toGAL() { static Bool needInit = True; static RotMatrix rot; static const Double data[3][3] = { { -0.0669887394, +0.4927284661, -0.8676008112}, { -0.8727557659, -0.4503469580, -0.1883746017}, { -0.4835389146, +0.7445846333, +0.4601997848} }; if (needInit) { // Multiple threads could execute this, but that is harmless. Int i,j; for (i=0; i<3; i++) { for (j=0; j<3; j++) { rot(i,j) = data[j][i]; } } needInit = False; } return rot; } const RotMatrix &MeasData::GALtoJ2000() { static Bool needInit = True; static RotMatrix rot; static const Double data[3][3] = { /// { -0.0548755397, +0.4941094533, -0.8676661359}, /// { -0.8734371080, -0.4448295894, -0.1980763861}, /// { -0.483834985, +0.7469822518, +0.4559837957} { -0.0548777621, +0.4941083214, -0.8676666398}, { -0.8734369591, -0.4448308610, -0.1980741871}, { -0.4838350026, +0.7469822433, +0.4559837919} }; if (needInit) { // Multiple threads could execute this, but that is harmless. Int i,j; for (i=0; i<3; i++) { for (j=0; j<3; j++) { rot(i,j) = data[i][j]; } } needInit = False; } return rot; } const RotMatrix &MeasData::J2000toGAL() { static Bool needInit = True; static RotMatrix rot; static const Double data[3][3] = { /// { -0.0548755397, +0.4941094533, -0.8676661359}, /// { -0.8734371080, -0.4448295894, -0.1980763861}, /// { -0.483834985, +0.7469822518, +0.4559837957} { -0.0548777621, +0.4941083214, -0.8676666398}, { -0.8734369591, -0.4448308610, -0.1980741871}, { -0.4838350026, +0.7469822433, +0.4559837919} }; if (needInit) { // Multiple threads could execute this, but that is harmless. Int i,j; for (i=0; i<3; i++) { for (j=0; j<3; j++) { rot(i,j) = data[j][i]; } } needInit = False; } return rot; } // B1950-J2000 conversions const RotMatrix &MeasData::MToB1950(uInt which) { static Bool needInit = True; static RotMatrix rot[5]; static const Double data[5][3][3] = { { {+0.9999256795, +0.0111814828, +0.0048590039}, {-0.0111814828, +0.9999374849, -0.0000271771}, {-0.0048590040, -0.0000271557, +0.9999881946}}, { {-0.00000242389840, -0.00000002710544, -0.00000001177742}, {+0.00000002710544, -0.00000242392702, +0.00000000006585}, {+0.00000001177742, +0.00000000006585, -0.00000242404995}}, { {-0.000551, +0.238509, -0.435614}, {-0.238560, -0.002667, +0.012254}, {+0.435730, -0.008541, +0.002117}}, { {+0.99990432, +0.01118145, +0.00485852}, {-0.01118145, +0.99991613, -0.00002717}, {-0.00485852, -0.00002716, +0.99996684}}, { {+0.9999256781, +0.0111820610, +0.0048579479}, {-0.0111820610, +0.9999374785, -0.0000271765}, {-0.0048579477, -0.0000271765, +0.9999881998}} }; if (needInit) { // Multiple threads could execute this, but that is harmless. Int i,j,k; for (i=0; i<5; i++) { for (j=0; j<3; j++) { for (k=0; k<3; k++) { rot[i](j,k) = data[i][k][j]; } } } needInit = False; } DebugAssert(which < 5, AipsError); return rot[which]; } const RotMatrix &MeasData::MToJ2000(uInt which) { static Bool needInit = True; static RotMatrix rot[4]; static const Double data[4][3][3] = { { {+0.9999256782, -0.0111820611, -0.0048579477}, {+0.0111820610, +0.9999374784, -0.0000271765}, {+0.0048579479, -0.0000271474, +0.9999881997}}, { {+0.00000242395018, -0.00000002710663, -0.00000001177656}, {+0.00000002710663, +0.00000242397878, -0.00000000006587}, {+0.00000001177656, -0.00000000006582, +0.00000242410173}}, { {-0.000551, -0.238565, +0.435739}, {+0.238514, -0.002667, -0.008541}, {-0.435623, +0.012254, +0.002117}}, { {+0.99994704, -0.01118251, -0.00485767}, {+0.01118251, +0.99995883, -0.00002718}, {+0.00485767, -0.00002714, +1.00000956}} }; if (needInit) { // Multiple threads could execute this, but that is harmless. Int i,j,k; for (i=0; i<4; i++) { for (j=0; j<3; j++) { for (k=0; k<3; k++) { rot[i](j,k) = data[i][k][j]; } } } needInit = False; } DebugAssert(which < 4, AipsError); return rot[which]; } // Solar semi diameter Double MeasData::SunSemiDiameter() { static const Double data = .004652472638; return data; } // J2000 obliquity Double MeasData::eps0J2000() { static const Double data = 84381.448*C::arcsec; return data; } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/MeasData.h000066400000000000000000000114521476623553700205670ustar00rootroot00000000000000//# MeasData.h: MeasData provides Measure computing data //# Copyright (C) 1995,1996,1997,1999,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MEASDATA_H #define MEASURES_MEASDATA_H //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class RotMatrix; // // MeasData provides Measure computing data // // // // // //
      • Measure class // // // // MeasData from Measure and Data // // // // MeasData contains the constant data // necessary for precession, nutation and other // Measure related calculations.
        // Database (Table) related data, or data that can be changed by the user, // is available in the MeasTable class.
        // All data. apart from a set of simple constants: // // MeasData::MJD2000 // MeasData::MJDB1950 // MeasData::MJDB1900 // MeasData::MJDB1850 // MeasData::TROPCEN // MeasData::JDCEN // MeasData::SECinDAY // // are obtained by calls to a method. // This class contains no constructors or destructors, only static // methods and (static) constants. //
        References:
        Explanatory supplements to the Astronomical Almanac //
        C. Ron and J. Vondrak, Bull. Astron. Inst. Czechosl. 37, p96, 1986 //
        M. Soma, Th. Hirayama and H. Kinoshita, Celest. Mech. 41, p389, 1988 //
        V.S. Gubanov, Astron. Zh. 49, p1112, 1972 (English translation: // Sov. Astronomy - AJ, Vol. 16, No. 5, p. 907) //
        // // // Usage examples can be found in Precession // // // // To create a clean interface between the actual calculations and the // methods to obtain the parameters for these calculations. Note that the // tables are in general in the format and units found in the literature. This // is to be able to easy check and change them. However, in the future // re-arrangement could produce faster and more compact code. // // // //
      • more precise data for VLBI and pulsar // class MeasData { public: //# Constants // General constants // // MJD of J2000.0 static const Double MJD2000; // MJD of B1950.0 static const Double MJDB1950; // MJD of B1900.0 static const Double MJDB1900; // MJD of B1850.0 static const Double MJDB1850; // Length Tropical century static const Double TROPCEN; // Length Julian century static const Double JDCEN; // Length of day in sec static const Double SECinDAY; // //# General Member Functions // Get the rotation matrices for galactic coordinates // static const RotMatrix &GALtoB1950(); static const RotMatrix &GALtoJ2000(); static const RotMatrix &J2000toGAL(); static const RotMatrix &B1950toGAL(); // // Get one of the 4 3x3 sub rotation matrices for B1950-J2000 conversions // static const RotMatrix &MToB1950(uInt which); static const RotMatrix &MToJ2000(uInt which); // // Get the solar semi diameter at 1 AU in rad static Double SunSemiDiameter(); // J2000 obliquity static Double eps0J2000(); private: //# Constructors // Default constructor, NOT defined MeasData(); // Copy assign, NOT defined MeasData &operator=(const MeasData &other); //# Destructor // Destructor (NOT defined) and not declared to stop warning // ~MeasData(); //# General member functions }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MeasFrame.cc000066400000000000000000000336141476623553700211120ustar00rootroot00000000000000//# MeasFrame.cc: Container for Measure frame //# Copyright (C) 1996-2003,2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Representation class class FrameRep { public: // Constructor FrameRep() : epval(0), posval(0), dirval(0), radval(0), comval(0), mymcf(0), cnt(1) {} // Destructor ~FrameRep() { delete epval; delete posval; delete dirval; delete radval; delete comval; delete mymcf; // delete conversion frame data } // The actual measures // // Epoch in time Measure *epval; // Position Measure *posval; // Direction Measure *dirval; // Radial velocity Measure *radval; // Comet MeasComet *comval; // Pointer to belonging conversion frame MCFrame *mymcf; // Usage count Int cnt; }; // MeasFrame class //# Constructors MeasFrame::MeasFrame() : rep(0) { create(); } MeasFrame::MeasFrame(const Measure &meas1) : rep(0) { create(); fill(&meas1); } MeasFrame::MeasFrame(const Measure &meas1, const Measure &meas2) : rep(0) { create(); fill(&meas1); fill(&meas2); } MeasFrame::MeasFrame(const Measure &meas1, const Measure &meas2, const Measure &meas3) : rep(0) { create(); fill(&meas1); fill(&meas2); fill(&meas3); } MeasFrame::MeasFrame(const MeasFrame &other) { rep = other.rep; if (rep) rep->cnt++; } // Destructor MeasFrame::~MeasFrame() { if (rep && rep->cnt && --rep->cnt == 0) delete rep; } // Operators MeasFrame &MeasFrame::operator=(const MeasFrame &other) { if (this != &other) { if (other.rep) other.rep->cnt++; if (rep && rep->cnt && --rep->cnt == 0) delete rep; rep = other.rep; } return *this; } Bool MeasFrame::operator==(const MeasFrame &other) const { return (rep == other.rep); } Bool MeasFrame::operator!=(const MeasFrame &other) const{ return (rep != other.rep); } // General member functions Bool MeasFrame::empty() const{ return ( !(rep && (rep->epval || rep->posval || rep->dirval || rep->radval)) ); } void MeasFrame::set(const Measure &meas1) { fill(&meas1); } void MeasFrame::set(const Measure &meas1, const Measure &meas2) { fill(&meas1); fill(&meas2); } void MeasFrame::set(const Measure &meas1, const Measure &meas2, const Measure &meas3) { fill(&meas1); fill(&meas2); fill(&meas3); } void MeasFrame::set(const MeasComet &meas) { fill(&meas); } void MeasFrame::resetEpoch(Double val) { resetEpoch(MVEpoch(val)); } void MeasFrame::resetEpoch(const Vector &val) { resetEpoch(MVEpoch(val)); } void MeasFrame::resetEpoch(const Quantum &val) { resetEpoch(MVEpoch(val)); } void MeasFrame::resetEpoch(const Quantum > &val) { resetEpoch(MVEpoch(val)); } void MeasFrame::resetEpoch(const MVEpoch &val) { if (rep && rep->epval) { rep->epval->set(val); rep->mymcf->resetEpoch(); } else { errorReset(String("Epoch")); } } void MeasFrame::resetEpoch(const Measure &val) { if (rep && rep->epval) { uInt locker = 0; lock(locker); delete rep->epval; rep->epval = val.clone(); unlock(locker); makeEpoch(); } else { errorReset(String("Epoch")); } } void MeasFrame::resetPosition(const Vector &val) { resetPosition(MVPosition(val)); } void MeasFrame::resetPosition(const Quantum > &val) { resetPosition(MVPosition(val)); } void MeasFrame::resetPosition(const MVPosition &val) { if (rep && rep->posval) { rep->posval->set(val); rep->mymcf->resetPosition(); } else { errorReset(String("Position")); } } void MeasFrame::resetPosition(const Measure &val) { if (rep && rep->posval) { uInt locker = 0; lock(locker); delete rep->posval; rep->posval = val.clone(); unlock(locker); makePosition(); } else { errorReset(String("Position")); } } void MeasFrame::resetDirection(const Vector &val) { resetDirection(MVDirection(val)); } void MeasFrame::resetDirection(const Quantum > &val) { resetDirection(MVDirection(val)); } void MeasFrame::resetDirection(const MVDirection &val) { if (rep && rep->dirval) { rep->dirval->set(val); rep->mymcf->resetDirection(); } else { errorReset(String("Direction")); } } void MeasFrame::resetDirection(const Measure &val) { if (rep && rep->dirval) { uInt locker = 0; lock(locker); delete rep->dirval; rep->dirval = val.clone(); unlock(locker); makeDirection(); } else { errorReset(String("Direction")); } } void MeasFrame::resetRadialVelocity(const Vector &val) { resetRadialVelocity(MVRadialVelocity(val)); } void MeasFrame::resetRadialVelocity(const Quantum > &val) { resetRadialVelocity(MVRadialVelocity(val)); } void MeasFrame::resetRadialVelocity(const MVRadialVelocity &val) { if (rep && rep->radval) { rep->radval->set(val); rep->mymcf->resetRadialVelocity(); } else { errorReset(String("RadialVelocity")); } } void MeasFrame::resetRadialVelocity(const Measure &val) { if (rep && rep->radval) { uInt locker = 0; lock(locker); delete rep->radval; rep->radval = val.clone(); unlock(locker); makeRadialVelocity(); } else { errorReset(String("RadialVelocity")); } } void MeasFrame::resetComet(const MeasComet &val) { if (rep && rep->comval) { fill(&val); } else { errorReset(String("Comet")); } } const Measure* MeasFrame::epoch() const{ if (rep) return rep->epval; return 0; } const Measure* MeasFrame::position() const{ if (rep) return rep->posval; return 0; } const Measure* MeasFrame::direction() const{ if (rep) return rep->dirval; return 0; } const Measure* MeasFrame::radialVelocity() const{ if (rep) return rep->radval; return 0; } const MeasComet* MeasFrame::comet() const{ if (rep) return rep->comval; return 0; } void MeasFrame::lock(uInt &locker) { locker = 1; if (rep) locker = rep->cnt++; } void MeasFrame::unlock(const uInt locker) { if (rep) rep->cnt = locker; } Bool MeasFrame::getTDB(Double &tdb) const { if (rep && rep->mymcf) return (rep->mymcf->getTDB(tdb)); tdb = 0; return False; } Bool MeasFrame::getUT1(Double &tdb) const { if (rep && rep->mymcf) return (rep->mymcf->getUT1(tdb)); tdb = 0; return False; } Bool MeasFrame::getTT(Double &tdb) const { if (rep && rep->mymcf) return (rep->mymcf->getTT(tdb)); tdb = 0; return False; } Bool MeasFrame::getLong(Double &tdb) const { if (rep && rep->mymcf) return (rep->mymcf->getLong(tdb)); tdb = 0; return False; } Bool MeasFrame::getLat(Double &tdb) const { if (rep && rep->mymcf) return (rep->mymcf->getLat(tdb)); tdb = 0; return False; } Bool MeasFrame::getITRF(MVPosition &tdb) const { if (rep && rep->mymcf) return (rep->mymcf->getITRF(tdb)); tdb = MVPosition(0.0); return False; } Bool MeasFrame::getRadius(Double &tdb) const { if (rep && rep->mymcf) return (rep->mymcf->getRadius(tdb)); tdb = 0; return False; } Bool MeasFrame::getLatGeo(Double &tdb) const { if (rep && rep->mymcf) return (rep->mymcf->getLatGeo(tdb)); tdb = 0; return False; } Bool MeasFrame::getLAST(Double &tdb) const { if (rep && rep->mymcf) return (rep->mymcf->getLAST(tdb)); tdb = 0; return False; } Bool MeasFrame::getLASTr(Double &tdb) const { if (rep && rep->mymcf) return (rep->mymcf->getLASTr(tdb)); tdb = 0; return False; } Bool MeasFrame::getJ2000(MVDirection &tdb) const { if (rep && rep->mymcf) return (rep->mymcf->getJ2000(tdb)); tdb = Double(0.0); return False; } Bool MeasFrame::getJ2000Long(Double &tdb) const { if (rep && rep->mymcf) return (rep->mymcf->getJ2000Long(tdb)); tdb = 0; return False; } Bool MeasFrame::getJ2000Lat(Double &tdb) const { if (rep && rep->mymcf) return (rep->mymcf->getJ2000Lat(tdb)); tdb = 0; return False; } Bool MeasFrame::getB1950(MVDirection &tdb) const { if (rep && rep->mymcf) return (rep->mymcf->getB1950(tdb)); tdb = 0; return False; } Bool MeasFrame::getB1950Long(Double &tdb) const { if (rep && rep->mymcf) return (rep->mymcf->getB1950Long(tdb)); tdb = 0; return False; } Bool MeasFrame::getB1950Lat(Double &tdb) const { if (rep && rep->mymcf) return (rep->mymcf->getB1950Lat(tdb)); tdb = 0; return False; } Bool MeasFrame::getApp(MVDirection &tdb) const { if (rep && rep->mymcf) return (rep->mymcf->getApp(tdb)); tdb = 0; return False; } Bool MeasFrame::getAppLong(Double &tdb) const { if (rep && rep->mymcf) return (rep->mymcf->getAppLong(tdb)); tdb = 0; return False; } Bool MeasFrame::getAppLat(Double &tdb) const { if (rep && rep->mymcf) return (rep->mymcf->getAppLat(tdb)); tdb = 0; return False; } Bool MeasFrame::getLSR(Double &tdb) const { if (rep && rep->mymcf) return (rep->mymcf->getLSR(tdb)); tdb = 0; return False; } Bool MeasFrame::getCometType(uInt &tdb) const { if (rep && rep->mymcf) return (rep->mymcf->getCometType(tdb)); tdb = 0; return False; } Bool MeasFrame::getComet(MVPosition &tdb) const { if (rep && rep->mymcf) return (rep->mymcf->getComet(tdb)); tdb = MVPosition(0.0); return False; } void MeasFrame::create() { if (!rep) { rep = new FrameRep(); uInt locker = 0; lock(locker); rep->mymcf = new MCFrame(*this); unlock(locker); } } void MeasFrame::fill(const Measure *in) { if (in) { uInt locker = 0; if (dynamic_cast(in)) { lock(locker); delete rep->epval; rep->epval = in->clone(); unlock(locker); makeEpoch(); } else if (dynamic_cast(in)) { lock(locker); delete rep->posval; rep->posval = in->clone(); unlock(locker); makePosition(); } else if (dynamic_cast(in)) { lock(locker); delete rep->dirval; rep->dirval = in->clone(); unlock(locker); makeDirection(); } else if (dynamic_cast(in)) { lock(locker); delete rep->radval; rep->radval = in->clone(); unlock(locker); makeRadialVelocity(); } else { throw(AipsError("Unknown MeasFrame Measure type " + in->tellMe())); } } } void MeasFrame::fill(const MeasComet *in) { if (in) { delete rep->comval; rep->comval = 0; if (in->ok()) { rep->comval = in->clone(); if (!rep->comval->ok()) { delete rep->comval; rep->comval = 0; } } if (rep->comval) { makeComet(); } else { throw(AipsError("Unknown or illegal MeasComet given for MeasFrame")); } } } void MeasFrame::makeEpoch() { rep->mymcf->makeEpoch(); } void MeasFrame::makePosition() { rep->mymcf->makePosition(); } void MeasFrame::makeDirection() { rep->mymcf->makeDirection(); } void MeasFrame::makeRadialVelocity() { rep->mymcf->makeRadialVelocity(); } void MeasFrame::makeComet() { rep->mymcf->makeComet(); } void MeasFrame::errorReset(const String &txt) { throw(AipsError("Attempt to reset non-existent frame member "+txt)); } ostream &operator<<(ostream &os, MeasFrame &mf) { os << "Frame: "; Double tmp, tmp1, tmp2; if (mf.rep && mf.rep->epval) { os << *(mf.rep->epval); if (mf.getTDB(tmp) && mf.getUT1(tmp1) && mf.getTT(tmp2)) os << " (TDB = " << tmp << ", UT1 = " << tmp1 << ", TT = " << tmp2 << ")"; } if (mf.rep && mf.rep->posval) { if (mf.rep && mf.rep->epval) os << endl << " "; os << *(mf.rep->posval); if (mf.getLong(tmp)) { os << endl << " (Longitude = " << tmp; mf.getLat(tmp); os << " Latitude = " << tmp << ")"; } } if (mf.rep && mf.rep->dirval) { if (mf.rep && (mf.rep->epval || mf.rep->posval)) os << endl << " "; os << *(mf.rep->dirval); MVDirection tmp; if (mf.getJ2000(tmp)) { os << endl << " (J2000 = " << tmp.getAngle("deg") << ")"; } } if (mf.rep && mf.rep->radval) { if (mf.rep && (mf.rep->epval || mf.rep->posval || mf.rep->dirval)) { os << endl << " "; } os << *(mf.rep->radval); if (mf.getLSR(tmp)) { tmp /= 1000.; os << endl << " (LSR velocity = " << Quantity(tmp,"km/s") << ")"; } } if (mf.rep && mf.rep->comval) { if (mf.rep && (mf.rep->epval || mf.rep->posval || mf.rep->dirval || mf.rep->radval)) { os << endl << " "; } os << mf.rep->comval->getName() << " comet between MJD " << mf.rep->comval->getStart() << " and " << mf.rep->comval->getEnd(); } return os; } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/MeasFrame.h000066400000000000000000000273151476623553700207550ustar00rootroot00000000000000//# MeasFrame.h: Container for Measure frame //# Copyright (C) 1996-2003,2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MEASFRAME_H #define MEASURES_MEASFRAME_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MVEpoch; class MVPosition; class MVDirection; class MVRadialVelocity; class MeasComet; class FrameRep; class MCFrame; template class Quantum; // Container for Measure frame // // // // //
      • Measure class //
      • MeasRef class // // // // From Measure and Frame // // // // Measurements are made in a reference frame (epoch, position, direction, // ...).
        // The class is a container for the reference frame Measures (MEpoch etc). // Since a frame will possibly be used by many different Measures, it behaves // as a smart pointer, with reference rather than copy characteristics. // Since it caches all its operations, it is advisable to have a 'global' // MeasFrame across an execution, resetting (or setting) its values // when appropriate. The frame can also contain other related information. At // the moment the orbit of a solar system body (MeasComet) can be set. // In future the planetary ephemeris used (e.g. DE205) and environmental // information like refraction data will be added. // // A MeasFrame is constructed by setting the appropriate Measures, either in // a constructor, or with a set(). The input to the constructors and set are // Measures.
        // // Inside the frames automatic conversion to the most appropriate usage of // its values is done (e.g. time to TBD time, position to astronomical // longitude). These conversions are done only if an explicit // Measure::Convert was used that needed information, e.g. the following // code: // // MeasFrame frame(obser); // obser is an MPosition // MEpoch::Convert conv(MEpoch(12345), MEpoch::Ref(MEpoch::LAST,obser)); // MEpoch last = conv(); // // will set-up a state machine to convert UTC(default) to LAST in conv; the // next call will do the actual conversion. During this conversion, the // astronomical longitude (among others) will be needed to convert to // local sidereal time. conv will ask (getLong()) this from the frame, which // will calculate it (including possible other conversions) from the // observatory's position specified in a frame. Any calculation done will be // cached (e.g. a Nutation calculation in this case for dpsi), and used in // subsequent conversions using the same frame.
        // Furthermore, a frame will often be regularly updated (e.g. coordinate // conversion for a series of times). To make use of cached information, and // to speed up as much as possible, reset...() functions are // available. These reset functions accept the same range of input parameter // types as the MeasConvert () operator, // and will keep any determined conversion machines and related information // intact, only recalculating whatever is necessary.
        // The actual frame calculations and interrogations are done in a separate // MCFrame hidden class, which attaches itself // to MeasFrame when and if necessary (see there if you are really curious).
        . // get...() functions can return frame measures. Only when the frame has been // attached to a calculating machine *MCFrame) are these values available. // This attachment is done if the frame has been actively used by a // Measure::Convert engine, or if explicitly done by the // MCFrame::make(MeasFrame &) static method. // An explicit (or implicit) call to MCFrame::make will // load the whole conversion machinery (including Tables) into your // linked module).
        // Aipsrc keywords can be used for additional // (highly specialised) additional internal conversion parameters. //
        // // // // MEpoch my_epoch(Quantity(MeasData::MJDB1950,"d")); // an epoch // MeasFrame frame(my_epoch); // used in a frame // // // // // To separate the frame definition from the measure type // // // // class MeasFrame { public: //# Friends // Output a frame friend ostream &operator<<(ostream &os, MeasFrame &mf); // Machinery // friend class MCFrame; friend Bool MCFrameGetdbl(void *dmf, uInt tp, Double &result); friend Bool MCFrameGetmvdir(void *dmf, uInt tp, MVDirection &result); friend Bool MCFrameGetmvpos(void *dmf, uInt tp, MVPosition &result); friend Bool MCFrameGetuint(void *dmf, uInt tp, uInt &result); // //# Enumerations // Enumeration for the different farme entries possible. This can be used // to find out if a certain conversion needs the frame. It will be // used in a registration/notify environment to enable bypassing of // some new conversion settings. enum FrameTypes { EPOCH = 1, POSITION = 2, DIRECTION = 4, VELOCITY = 8, COMET = 16 }; //# Constructors // Default constructor MeasFrame(); // Construct frame with specified measures // //
      • AipsError if a non-frame Measure // // MeasFrame(const Measure &meas1); MeasFrame(const Measure &meas1, const Measure &meas2); MeasFrame(const Measure &meas1, const Measure &meas2, const Measure &meas3); // // Copy constructor (reference semantics) MeasFrame(const MeasFrame &other); // Copy assignment (reference semantics) MeasFrame &operator=(const MeasFrame &other); // Destructor ~MeasFrame(); //# Operators // Comparisons // Bool operator==(const MeasFrame &other) const; Bool operator!=(const MeasFrame &other) const; // //# General member functions // Test if empty (i.e. no measure filled in) Bool empty() const; // Set frame elements // //
      • AipsError if a non-frame Measure //
      • AipsError if illegal or non-existant MeasComet given // // void set(const Measure &meas1); void set(const Measure &meas1, const Measure &meas2); void set(const Measure &meas1, const Measure &meas2, const Measure &meas3); void set(const MeasComet &meas); // // Reset a frame element and its cached derived values. // //
      • AipsError if the specific Measure not yet present in frame // // void resetEpoch(Double val); void resetEpoch(const Vector &val); void resetEpoch(const Quantum &val); void resetEpoch(const Quantum > &val); void resetEpoch(const MVEpoch &val); void resetEpoch(const Measure &val); void resetPosition(const Vector &val); void resetPosition(const Quantum > &val); void resetPosition(const MVPosition &val); void resetPosition(const Measure &val); void resetDirection(const Vector &val); void resetDirection(const Quantum > &val); void resetDirection(const MVDirection &val); void resetDirection(const Measure &val); void resetRadialVelocity(const Vector &val); void resetRadialVelocity(const Quantum > &val); void resetRadialVelocity(const MVRadialVelocity &val); void resetRadialVelocity(const Measure &val); void resetComet(const MeasComet &val); // // Get the epoch pointer (0 if not present) const Measure* epoch() const; // Get the position pointer (0 if not present) const Measure* position() const; // Get the direction pointer (0 if not present) const Measure* direction() const; // Get the radial velocity pointer (0 if not present) const Measure* radialVelocity() const; // Get the comet pointer (0 if not present) const MeasComet* comet() const; // Get data from frame. Only available if appropriate measures are set, // and the frame is in a calculating state. // // Get TDB in days Bool getTDB(Double &tdb) const; // Get UT1 in days Bool getUT1(Double &tdb) const; // Get TT in days Bool getTT(Double &tdb) const; // Get the ITRF longitude (in rad) Bool getLong(Double &tdb) const; // Get the ITRF latitude (in rad) Bool getLat(Double &tdb) const; // Get the position Bool getITRF(MVPosition &tdb) const; // Get the geocentric position (in m) Bool getRadius(Double &tdb) const; // Get the geodetic latitude Bool getLatGeo(Double &tdb) const; // Get the LAST (in days) Bool getLAST(Double &tdb) const; // Get the LAST (in rad) Bool getLASTr(Double &tdb) const; // Get J2000 coordinates (direction cosines) and its longitude/latitude (rad) // Bool getJ2000(MVDirection &tdb) const; Bool getJ2000Long(Double &tdb) const; Bool getJ2000Lat(Double &tdb) const; // // Get B1950 coordinates (direction cosines) // Bool getB1950(MVDirection &tdb) const; Bool getB1950Long(Double &tdb) const; Bool getB1950Lat(Double &tdb) const; // // Get apparent coordinates (direction cosines) // Bool getApp(MVDirection &tdb) const; Bool getAppLong(Double &tdb) const; Bool getAppLat(Double &tdb) const; // // Get LSR radial velocity (m/s) Bool getLSR(Double &tdb) const; // Get the comet table reference type Bool getCometType(uInt &tdb) const; // Get the comet coordinates Bool getComet(MVPosition &tdb) const; // private: //# Data // Representation of MeasFrame FrameRep *rep; //# Member functions // Create an instance of the MeasFrame class void create(); // Fill a MeasFrame element // void fill(const Measure *in); void fill(const MeasComet *in); // // Make full Epoch void makeEpoch(); // Make full Position void makePosition(); // Make full Direction void makeDirection(); // Make full RadialVelocity void makeRadialVelocity(); // Make full Comet void makeComet(); // Throw reset error void errorReset(const String &txt); // Lock the frame to make sure deletion occurs when needed void lock(uInt &locker); // Unlock the frame void unlock(const uInt locker); }; //# Global functions // Global functions // // Output a frame ostream &operator<<(ostream &os, MeasFrame &mf); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MeasIERS.cc000066400000000000000000000421141476623553700206150ustar00rootroot00000000000000//# MeasIERS.cc: Interface to IERS tables //# Copyright (C) 1996-2003,2007,2008,2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN #ifndef CASADATA #define CASADATA "/usr/local/share/data/casacore" #endif //# Constants const Double MeasIERS::INTV = 5; //# Static data std::once_flag MeasIERS::theirCallOnceFlag; uInt MeasIERS::predicttime_reg = 0; uInt MeasIERS::notable_reg = 0; uInt MeasIERS::forcepredict_reg = 0; Double MeasIERS::dateNow = 0.0; Vector MeasIERS::ldat[MeasIERS::N_Files][MeasIERS::N_Types]; const String MeasIERS::tp[MeasIERS::N_Files] = {"IERSeop97", "IERSpredict"}; uInt MeasIERS::sizeNote = 0; uInt MeasIERS::nNote = 0; MeasIERS::CLOSEFUN *MeasIERS::toclose = 0; //# Member functions Bool MeasIERS::get(Double &returnValue, MeasIERS::Files file, MeasIERS::Types type, Double date) { returnValue = 0.0; std::call_once(theirCallOnceFlag, initMeas); // Exit if no table has to be used. if (AipsrcValue::get(MeasIERS::notable_reg)) { return True; } // Test if PREDICTED has to be used. Int which = MEASURED; if (file == PREDICTED || ldat[MEASURED][0].empty() || AipsrcValue::get(MeasIERS::forcepredict_reg) || (dateNow-date) <= AipsrcValue::get(MeasIERS::predicttime_reg)) { which = PREDICTED; } Int ut = ifloor(date); if (which == MEASURED) { const Vector& mjds = ldat[which][0]; if (ut < mjds[0] || ut >= mjds[mjds.size()-1]) { which = PREDICTED; } } if (which == PREDICTED) { #if defined(USE_THREADS) static std::atomic msgDone; #else static Bool msgDone; #endif const Vector& mjds = ldat[which][0]; if (mjds.empty() || ut < mjds[0] || ut >= mjds[mjds.size()-1]) { // It is harmless if the message accidentally appears multiple times. if (!msgDone) { msgDone = True; LogIO os(LogOrigin("MeasIERS", "fillMeas(MeasIERS::Files, Double)", WHERE)); Time now; // current time if (date > now.modifiedJulianDay()){ // People using times from the future are almost certainly simulating // data, and, even if they would be upset by the IERS table not being // available, there is not much they can do about it. os << LogIO::NORMAL3 << "High precision Earth axis data is not yet available for requested JD " << date << LogIO::POST; } else { os << LogIO::NORMAL << "Requested JD " << date << " is outside the range of the IERS (Earth axis data) table." << "\nCalculations will proceed with less precision" << LogIO::POST; } } return False; } } // Interpolation fraction Int indx = Int(date - ldat[which][0][0]); // old version in use up to Jan 2016 // if (indx >= 0 && indx < Int(ldat[which][0].size())-1) { // Double f = date - ldat[which][0][indx]; // returnValue = ldat[which][type][indx+1]*f - ldat[which][type][indx]*(f-1.0); // return True; // } if (indx >= 0 && indx < Int(ldat[which][0].size())-1) { Double f = date - ldat[which][0][indx]; // Fraction Double vlo = ldat[which][type][indx]; // Get daily values Double vhi = ldat[which][type][indx+1]; if (abs(vhi-vlo) > 0.5) { // Jump vhi -= sign(vhi-vlo); // Remove jump } returnValue = vhi*f - vlo*(f-1.0); return True; } return False; } void MeasIERS::initMeas() { static const String names[MeasIERS::N_Types] = { "MJD", "x", "y", "dUT1", "LOD", "dPsi", "dEps", "Dx", "Dy", "DdUT1", "DLOD", "DdPsi", "DdEps"}; static const String tplc[N_Files] = {"measures.ierseop97.directory", "measures.ierspredict.directory"}; predicttime_reg = AipsrcValue::registerRC(String("measures.measiers.d_predicttime"), Unit("d"), Unit("d"), MeasIERS::INTV); notable_reg = AipsrcValue::registerRC(String("measures.measiers.b_notable"), False); forcepredict_reg = AipsrcValue::registerRC(String("measures.measiers.b_forcepredict"), False); dateNow = Time().modifiedJulianDay(); TableRecord kws; Table tab; TableRow row; RORecordFieldPtr rfp[N_Types]; Double dt; String vs; for (Int which=0; which(tab, names[i]).getColumn (ldat[which][i]); } // Check if MJD in first and last row match and have step 1. const Vector& mjds = ldat[which][0]; if (mjds[mjds.size()-1] != mjds[0] + mjds.size()-1) { LogIO os(LogOrigin("MeasIERS", "initMeas(MeasIERS::Files)", WHERE)); os << "IERS table " << tp[which] << " seems to be corrupted (time step not 1)" << LogIO::EXCEPTION; } } } } void MeasIERS::closeMeas() { // Cannot get this fast & thread-safe without rewriting initMeas/closeMeas. // But this is only used to check for memory leaks at the end and possibly // to compare tables in tests, so don't bother. Apply pray and HACK below... dateNow = 0.0; for (uInt i=0; i= sizeNote) { CLOSEFUN *tmp = new CLOSEFUN[sizeNote+10]; for (uInt i=0; i0; --i) { if (toclose[i-1] != 0) { toclose[i-1](); toclose[i-1] = 0; } } delete [] toclose; toclose = 0; sizeNote = 0; nNote = 0; } // Table handling Bool MeasIERS::getTable(Table &table, TableRecord &kws, ROTableRow &row, RORecordFieldPtr rfp[], String &vs, Double &dt, Int N, const String rfn[], const String &name, const String &rc, const String &dir, const Table *tabin) { Table tab; Bool ok = findTab(tab, tabin, rc, dir, name); if(!ok) return false; // findTab logs its own errors. LogIO os(LogOrigin("MeasIERS", String("getTable(Table &, TableRecord &, " "ROTableRow &, RORecordFieldPtr *, " "String &vs, Double &dt, " "Int N, const String *, const String &, " "const String &, const String &)"), WHERE)); TableRecord ks(tab.keywordSet()); ok = handle_keywords(dt, vs, ks, tab); ROTableRow rw(tab); if (ok) { // Check that the table is not missing any expected columns. for (Int i=0; i < N; i++) { if (!rw.record().isDefined(rfn[i])) { os << LogIO::SEVERE << "Column " << rfn[i] << " is missing." << LogIO::POST; ok = False;// break; } } } if (!ok) { os << name << " has an incompatible format." << "\nYou may want to notify the CASA system manager about it." << LogIO::EXCEPTION; return False; } table = tab; kws = ks; row = rw; for (Int i=0; i < N; i++) rfp[i] = RORecordFieldPtr(row.record(), rfn[i]); return True; } Bool MeasIERS::getTable(Table &table, TableRecord &kws, ROTableRow &row, Vector >& rfp, String &vs, Double &dt, const Vector& reqcols, Vector& optcols, const String &name, const String &rc, const String &dir, const Table *tabin) { Table tab; Bool ok = findTab(tab, tabin, rc, dir, name); if(!ok) return false; // findTab logs its own errors. LogIO os(LogOrigin("MeasIERS", "getTable(Vector& optcols)", WHERE)); TableRecord ks(tab.keywordSet()); ok = handle_keywords(dt, vs, ks, tab); ROTableRow rw(tab); if(ok){ // Check that the table is not missing any required columns. for(Int i = reqcols.nelements(); i--;){ if(!rw.record().isDefined(reqcols[i])){ os << LogIO::SEVERE << "Required column " << reqcols[i] << " is missing." << LogIO::POST; ok = False;// break; } } } if(!ok){ os << name + " has an incompatible format." << "\nYou may want to notify the CASA system manager about it." << LogIO::EXCEPTION; return False; } // Now look for optional columns. Vector foundoptcols; uInt noptcolsfound = 0; for(uInt i = 0; i < optcols.nelements(); ++i){ if(rw.record().isDefined(optcols[i])){ ++noptcolsfound; foundoptcols.resize(noptcolsfound, true); foundoptcols[noptcolsfound - 1] = optcols[i]; } } // Together these are equiv. to optcols.assign(foundoptcols), but I find this clearer. optcols.resize(noptcolsfound); optcols = foundoptcols; table = tab; kws = ks; row = rw; rfp.resize(reqcols.nelements() + noptcolsfound); for(uInt i = 0; i < reqcols.nelements(); ++i) rfp[i] = RORecordFieldPtr(row.record(), reqcols[i]); for(uInt i = 0; i < noptcolsfound; ++i) rfp[reqcols.nelements() + i] = RORecordFieldPtr(row.record(), optcols[i]); return True; } // Helper function for getTable(). Bool MeasIERS::findTab(Table& tab, const Table *tabin, const String &rc, const String &dir, const String &name) { Bool ok = true; LogIO os(LogOrigin("MeasIERS", "findTab", WHERE)); if(!tabin){ // No table object given: search name String ldir; Vector searched; if(name[0] == '/'){ // Absolute path given. ldir = ""; } else{ const String path[2] = { "/ephemerides/", "/geodetic/" }; Bool found = False; const std::string &measures_data = AppStateSource::fetch( ).measuresDir( ); if ( measures_data.size( ) > 0 ) { for (Int i=0; i<2; i++) { Path mpath = Path(measures_data + "/" + (std::string) path[i]); ldir = mpath.absoluteName()+"/"; searched.resize(searched.nelements()+1, True); searched[searched.nelements()-1] = ldir; if (Table::isReadable(ldir+name)) { found = True; break; } } if ( found == False ) { throw(AipsError(std::string("Measures directory specified which does not contain the IERS data: ") + measures_data)); } } if ( found == False ) { const std::list &state_path = AppStateSource::fetch( ).dataPath( ); if ( state_path.size( ) > 0 ) { String mdir; for ( std::list::const_iterator it=state_path.begin(); ! found && it != state_path.end(); ++it ) { for (Int i=0; i<2; i++) { Path mpath = Path(*it + "/" + (std::string) path[i]); ldir = mpath.absoluteName()+"/"; searched.resize(searched.nelements()+1, True); searched[searched.nelements()-1] = ldir; if (Table::isReadable(ldir+name)) { found = True; break; } } } } else if ( ! found ) { if (Aipsrc::find(ldir, rc)){ ldir += '/'; searched.resize(searched.nelements() + 1, True); searched[searched.nelements() - 1] = ldir; } else { String udir; if(!dir.empty()) { udir = dir + '/'; } String mdir; if (Aipsrc::find(mdir, "measures.directory")) { mdir.trim(); Path mpath = Path(mdir); mpath.append(udir); for (Int i=0; i<2; i++) { Path mpath = Path(mdir +"/" + path[i]); ldir = mpath.absoluteName()+"/"; searched.resize(searched.nelements()+1, True); searched[searched.nelements()-1] = ldir; if (Table::isReadable(ldir+name)) { found = True; break; } } } if (!found) { String casadata=String(CASADATA); casadata.gsub("%CASAROOT%", Aipsrc::aipsRoot()); casadata.gsub("%CASAHOME%", Aipsrc::aipsHome()); Path cdatapath(casadata); for (Int i=0; i<2; i++) { ldir = cdatapath.absoluteName() + path[i]; searched.resize(searched.nelements() + 1, True); searched[searched.nelements() - 1] = ldir; if (Table::isReadable(ldir + name)) { found = True; break; } } } } } } } if(!Table::isReadable(ldir + name)){ os << LogIO::WARN << "Requested data table " << name << " cannot be found in the searched directories:\n"; for(uInt i = 0; i < searched.nelements(); ++i) { os << searched[i] << "\n"; } os << LogIO::POST; return False; } tab = Table(ldir + name); } else { tab = *tabin; } return ok; } // Helper function for getTable(). Bool MeasIERS::handle_keywords(Double &dt, String &vs, const TableRecord& ks, const Table& tab) { LogIO os(LogOrigin("MeasIERS", "handle_keywords", WHERE)); Bool ok = true; if(!ks.isDefined("VS_DATE") || !ks.isDefined("VS_VERSION") || !ks.isDefined("VS_CREATE") || !ks.isDefined("VS_TYPE") || (tab.tableInfo().type() != "IERS")) { ok = False; os << LogIO::DEBUG1 << "ks.isDefined(VS_DATE) " << ks.isDefined("VS_DATE") << "\nks.isDefined(VS_VERSION) " << ks.isDefined("VS_VERSION") << "\nks.isDefined(VS_CREATE) " << ks.isDefined("VS_CREATE") << "\nks.isDefined(VS_TYPE) " << ks.isDefined("VS_TYPE") << "\ntab.tableInfo().type() " << tab.tableInfo().type() << LogIO::POST; } if (ok) { Quantity ldt; if (MVTime::read(ldt, ks.asString("VS_DATE"))) { dt = MVTime(ldt); vs = ks.asString("VS_VERSION"); } else { ok = False; } } return ok; } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/MeasIERS.h000066400000000000000000000235721476623553700204660ustar00rootroot00000000000000//# MeasIERS.h: Interface to IERS tables //# Copyright (C) 1996,1997,1999,2000,2002,2007,2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MEASIERS_H #define MEASURES_MEASIERS_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class String; // Interface to IERS tables // // // // //
      • MeasTable // // // // From Measure and IERS // // // // MeasIERS is the interface class to the global IERS data. // It has only static members.
        // It has a member (getTable()) to open and check IERS // (and other Measures related Tables) type tables. // Tables are found using the aipsrc // (using measures.
      • .directory, or measures.directory) // mechanism. If not provided they are assumed to reside in standard places // (i.e. they are looked for in (udir in following normally given by // program as ephemerides or geodetic) '.', './data', '~/aips++/data/udir', // '$AIPSROOT/data/udir', '~/aips++/code/trial/apps/measures', // '$AIPSROOT/data/udir' (last two only ad interim)). They are also looked // for in data/{ephemerides,geodetic} (root and user aips++). // // If an explicit Table object is given the lookup is bypassed, and the Table // provided is used. The table should still be named. // // Tables are assumed to have the // VS_VERSION, VS_DATE, VS_CREATE and VS_TYPE keywords, and be of type IERS, // else an exception will be thrown.
        // The get() method will obtain data from measured and predicted // Earth Orientation Parameters IERS tables (i.e. the IERSeop97 and // the IERSpredict tables. If not forced, the data is taken from // the measured table if possible. Only if forced (see below), or if data is // not (yet) available in measured the predicted values are used. A warning // message is (once) issued if values are not available at all. // // MeasIERS looks at some Aipsrc // values to determine actions: //
          //
        • measures.measiers.b_notable : Do not use IERS tables to convert measures //
        • measures.measiers.b_forcepredict : Use values from prediction tables // even if Measured table asked by program. //
        • measures.measiers.d_predicttime : Use values from prediction tables if // (now - time) less than value given (default 5) (days) //
        // These values can be set in aipsrc as well as using // AipsrcValue set() methods. // // A message is Logged (once) if an IERS table cannot be found. // A message is logged (once) if a date outside the range in // the Tables is asked for. // // //
      • AipsError if table opened has wrong format or otherwise corrupted. // // // // // See the dUTC() method in // MeasTable for an example of the // getTable method; and the polarMotion() method for // an example of get(). // // // // // To use the IERS data for time and nutation calculations // // // // class MeasIERS { public: //# Typedefs // Define the function pointer to be called to close files typedef void (*CLOSEFUN) (); //# Constants static const Double INTV; //# Enumerations // Types of known data enum Types { // MJD (must be first in list) MJD, // Polar motion x X, // Polar motion y Y, // UT1-UTC dUT1, // Length of Day LOD, // dPsi dPsi, // dEpsilon dEps, // Polar motion x error DX, // Polar motion y error DY, // UT1-UTC error DdUT1, // Length of Day error DLOD, // dPsi error DdPsi, // dEpsilon error DdEps, // Number of types N_Types}; // Types of files enum Files { // Measured EOP values MEASURED, // Predicted EOP values PREDICTED, // # of known types N_Files, // Default DEFAULT = MEASURED }; //# General Member Functions // Get the value from an IERS table, interpolated for date(in MJD). // The file can be PREDICTED or MEASURED, the type as given in enum. static Bool get(Double &returnValue, MeasIERS::Files file, MeasIERS::Types type, Double date); // Find and open table tab, using the rc variable, the dir and the name. // An rfn list gives the N row field names to be used // Returned are an open table, the table keywordset (kws), a row record, // pointers (rfp) to row data, the table version (vs), dt, and, directly, // whether or not it was successful. // Lookup for name is bypassed if the Table address tabin is provided. // //
      • AipsError if missing VS_ keywords, columns, or they type is not IERS. // static Bool getTable(Table &table, TableRecord &kws, ROTableRow &row, RORecordFieldPtr rfp[], String &vs, Double &dt, Int N, const String rfn[], const String &name, const String &rc, const String &dir, const Table *tabin = 0); // Find and open table tab, using the rc variable, the dir and the name. // reqcols gives the names (in order) of the columns which must be present. // optcols gives the names of columns which should be added, in order after // reqcols, if they are present. // Returned are an open table, the table keywordset (kws), a row record, // pointers (rfp) to row data, the table version (vs), dt, and, directly, // whether or not it was successful. optcols is set to the optional columns // that were found. // Lookup for name is bypassed if the Table address tabin is provided. // //
      • AipsError if missing VS_ keywords, required columns, or the type is not IERS. // static Bool getTable(Table &table, TableRecord &kws, ROTableRow &row, Vector >& rfp, String &vs, Double &dt, const Vector& reqcols, Vector& optcols, const String &name, const String &rc, const String &dir, const Table *tabin = 0); // A helper function for getTable() which is conceivably usable outside it, // for finding a table in the same way, but not requiring it to fit the IERS // mold. // Finds a Table for tab, by looking in tabin, rc, dir, and name. // Returns whether or not it was successful. static Bool findTab(Table& tab, const Table *tabin, const String &rc, const String &dir, const String &name); // Notify that a table has successfully been opened with getTable() static void openNote(CLOSEFUN fun); // Make sure all static tables are closed that were opened with getTable // (like JPL, IERS). This is the preferred way to close the // Measures related data tables. Only call it last at end of program. static void closeTables(); // Close the set of IERS tables only. Only call it last at end of program. static void closeMeas(); private: //# Constructors // Default constructor, NOT defined MeasIERS(); // Copy assign, NOT defined MeasIERS &operator=(const MeasIERS &other); //# Destructor // Destructor, NOT defined and not declared to stop warning // ~MeasIERS(); //# General member functions // Initialise tables static void initMeas(); // A helper function for getTable() which is not likely usable outside it. // Sets dt and vs (the table version), and checks that // ks has VS_DATE, VS_VERSION, VS_CREATE, and VS_TYPE, // and that tab's type is IERS in its info. // Returns whether or not it was successful. static Bool handle_keywords(Double &dt, String &vs, const TableRecord& ks, const Table& tab); //# Data members // Object to ensure safe multi-threaded lazy single initialization static std::once_flag theirCallOnceFlag; // Current date static Double dateNow; // Read data (meas - predict) static Vector ldat[N_Files][N_Types]; // File names static const String tp[N_Files]; // Check prediction interval static uInt predicttime_reg; // Use no table static uInt notable_reg; // Force prediction static uInt forcepredict_reg; // Size of close notification list static uInt sizeNote; // Tables notifying that they should be closed static CLOSEFUN *toclose; // Number of close notifications static uInt nNote; }; //# Inline Implementations } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MeasJPL.cc000066400000000000000000000252651476623553700205100ustar00rootroot00000000000000//# MeasJPL.cc: Interface to JPL DE tables //# Copyright (C) 1996,1997,1998,1999,2001,2002,2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constants //# Member functions Bool MeasJPL::get(Vector &returnValue, MeasJPL::Files file, MeasJPL::Types type, const MVEpoch &date) { returnValue = 0.0; // Open the file if needed. if (!initMeasOnce(file)) { return False; } // Get or read the correct data if needed. // Note that fillMeas uses locks to be thread-safe. The pointer returned // will never change, even if fillMeas has to extend the buffer. Double intv; const Double* dta = fillMeas(intv, file, date); if (!dta) { return False; } Double res[6]; Double res1[6]; for (uInt i=0; i<6; i++) res[i] = 0.0; // Interpolation fraction Bool mulfr = True; if (type == MeasJPL::BARYSOLAR) { res[0] = 0.0; } else if (type == MeasJPL::BARYEARTH) { interMeas(res, file, intv, dmjd[file], idx[file][1][MeasJPL::EARTH-1], 3, idx[file][2][MeasJPL::EARTH-1], dta + idx[file][0][MeasJPL::EARTH-1]); } else if (type == MeasJPL::EARTH || type == MeasJPL::MOON) { interMeas(res1, file, intv, dmjd[file], idx[file][1][MeasJPL::MOON-1], 3, idx[file][2][MeasJPL::MOON-1], dta + idx[file][0][MeasJPL::MOON-1]); interMeas(res, file, intv, dmjd[file], idx[file][1][MeasJPL::EARTH-1], 3, idx[file][2][MeasJPL::EARTH-1], dta + idx[file][0][MeasJPL::EARTH-1]); if (type == MeasJPL::EARTH) { for (uInt i=0; i<6; i++) res[i] -= res1[i]/emrat[file]; } else { for (uInt i=0; i<6; i++) res[i] += res1[i]; } } else if (type == MeasJPL::NUTATION) { if (idx[file][1][MeasJPL::BARYSOLAR-1] == 0) return False; interMeas(res, file, intv, dmjd[file], idx[file][1][MeasJPL::BARYSOLAR-1], 2, idx[file][2][MeasJPL::BARYSOLAR-1], dta + idx[file][0][MeasJPL::BARYSOLAR-1]); mulfr = False; } else if (type == MeasJPL::LIBRATION) { if (idx[file][1][MeasJPL::BARYEARTH-1] == 0) return False; interMeas(res, file, intv, dmjd[file], idx[file][1][MeasJPL::BARYEARTH-1], 3, idx[file][2][MeasJPL::BARYEARTH-1], dta + idx[file][0][MeasJPL::BARYEARTH-1]); mulfr = False; } else { interMeas(res, file, intv, dmjd[file], idx[file][1][type-1], 3, idx[file][2][type-1], dta + idx[file][0][type-1]); } if (mulfr) { for (uInt i=0; i<6; i++) returnValue(i) = res[i]*aufac[file]; } else { for (uInt i=0; i<6; i++) returnValue(i) = res[i]; } return True; } Bool MeasJPL::getConst(Double &res, MeasJPL::Files which, MeasJPL::Codes what) { if (initMeasOnce(which)) { res = cn[which][what]; return True; } return False; } Bool MeasJPL::getConst(Double &res, MeasJPL::Files which, const String &nam) { if (initMeasOnce(which)) { const TableRecord &tr = t[which].keywordSet(); if (tr.isDefined(nam)) { res = tr.asDouble(nam); return True; } } return False; } Bool MeasJPL::initMeasOnce(MeasJPL::Files which) { try { std::call_once(theirCallOnceFlags[which], doInitMeas, which); } catch (InitError& ) { return False; } return True; } void MeasJPL::doInitMeas(MeasJPL::Files which) { static const String names[MeasJPL::N_Columns] = { "MJD", "x" }; static const String tplc[N_Files] = {"measures.DE200.directory", "measures.DE405.directory"}; TableRecord kws; TableRow row; RORecordFieldPtr rfp[MeasJPL::N_Types]; Double dt; String vs; Bool ok = True; if (!MeasIERS::getTable(MeasJPL::t[which], kws, row, rfp, vs, dt, 1, names, tp[which], tplc[which], "ephemerides")) { ok = False; } if (ok) { MeasIERS::openNote(&MeasJPL::closeMeas); if (!kws.isDefined("MJD0") || kws.asDouble("MJD0") < 10000 || !kws.isDefined("dMJD") || kws.asDouble("dMJD") < 8 || !kws.isDefined("AU") || kws.asDouble("AU") < 1e8 || !kws.isDefined("CLIGHT") || kws.asDouble("CLIGHT") < 2e5 || !kws.isDefined("GMS") || kws.asDouble("GMS") < 2e-4 || !((kws.isDefined("RADS") && kws.asDouble("RADS") > 6e5) || (kws.isDefined("ASUN") && kws.asDouble("ASUN") > 6e5))|| !kws.isDefined("EMRAT") || kws.asDouble("EMRAT") < 10 ) { ok = False; } } if (ok) { mjd0[which] = Int(kws.asDouble("MJD0")); dmjd[which] = Int(kws.asDouble("dMJD")); cn[which][MeasJPL::AU] = kws.asDouble("AU"); aufac[which] = 1./cn[which][MeasJPL::AU]; emrat[which] = 1.+kws.asDouble("EMRAT"); cn[which][MeasJPL::CAU] = 86400 * kws.asDouble("CLIGHT")/ cn[which][MeasJPL::AU]; if (kws.isDefined("RADS")) { cn[which][MeasJPL::RADS] = kws.asDouble("RADS")/ cn[which][MeasJPL::AU]; } else { cn[which][MeasJPL::RADS] = kws.asDouble("ASUN")/ cn[which][MeasJPL::AU]; } cn[which][MeasJPL::GMS] = kws.asDouble("GMS")/ cn[which][MeasJPL::CAU]/cn[which][MeasJPL::CAU]; Int n = t[which].nrow(); row.get(n-1); if (*(rfp[0]) != mjd0[which] + n*dmjd[which]) { ok = False; } else { mjdl[which] = mjd0[which] + n*dmjd[which]; } } if (ok) { const TableRecord &tr = t[which].tableDesc().columnDesc("x"). keywordSet(); if (tr.asInt("Rows") != 3 || tr.asInt("Columns") != 13) { ok = False; } else { Array xx = tr.asArrayInt("Description"); uInt k = 0; for (uInt i=0; i<3; i++) { for (uInt j=0; j<13; j++) { idx[which][i][j] = xx(IPosition(1,k++)); if (i == 0) idx[which][i][j] -= 3; } } acc[Int(which)].attach(t[which], "x"); } } if (!ok) { // Close table if open. t[which] = Table(); LogIO os(LogOrigin("MeasJPL", "initMeas(MeasJPL::Files)", WHERE)); os << "Corrupted JPL table " + tp[which] << LogIO::EXCEPTION; } if (t[which].isNull()) { throw InitError(); } } void MeasJPL::closeMeas() { // Cannot get this fast & thread-safe without rewriting initMeas/closeMeas. // But this is only used to check for memory leaks at the end and possibly // to compare tables in tests, don't bother. Apply pray and HACK below... for (uInt i=0; i= mjdl[which] + dmjd[which]) { return 0; } // Turn day into interval (intervals are dmjd wide) plus fraction ut = (ut-mjd0[which])/dmjd[which]; intv = ((utf.getDay() - (ut*dmjd[which] + mjd0[which])) + utf.getDayFraction()) / dmjd[which]; // If needed, read the data of this interval. std::lock_guard locker(theirMutex); for (size_t i=0; i data (acc[Int(which)](ut-1)); dval[which].push_back (data); curDate[which].push_back (ut); return data.data(); } void MeasJPL::interMeas(Double res[], MeasJPL::Files, Double intv, Double ivf, Int ncf, Int ncm, Int na, const Double buf[]) { Double tc = 2.0*(fmod(Double(na)*intv, Double(1.0)) + Int(intv)) - 1.0; Int l = Int(Double(na)*intv - Int(intv)); // Chebyshev coefficients Double chc[18]; chc[0] = 1; chc[1] = tc; Double twot = 2*tc; for (Int i=2; i=0; j--) { res[i] += chc[j]*buf[(l*ncm+i)*ncf+j]; } } } { // Velocity for (Int i=0; i0; j--) { res[i+ncm] += chcv[j]*buf[(l*ncm+i)*ncf+j]; } res[i+ncm] *= vfac; } } } std::once_flag MeasJPL::theirCallOnceFlags[MeasJPL::N_Files]; std::mutex MeasJPL::theirMutex; Table MeasJPL::t[MeasJPL::N_Files]; ArrayColumn MeasJPL::acc[MeasJPL::N_Files]; Int MeasJPL::mjd0[MeasJPL::N_Files] = {0, 0}; Int MeasJPL::mjdl[MeasJPL::N_Files] = {0, 0}; Int MeasJPL::dmjd[MeasJPL::N_Files] = {0, 0}; const String MeasJPL::tp[MeasJPL::N_Files] = {"DE200", "DE405"}; Int MeasJPL::idx[MeasJPL::N_Files][3][13]; vector MeasJPL::curDate[MeasJPL::N_Files]; vector > MeasJPL::dval[MeasJPL::N_Files]; Double MeasJPL::aufac[MeasJPL::N_Files]; Double MeasJPL::emrat[MeasJPL::N_Files]; Double MeasJPL::cn[MeasJPL::N_Files][MeasJPL::N_Codes]; } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/MeasJPL.h000066400000000000000000000174771476623553700203600ustar00rootroot00000000000000//# MeasJPL.h: Interface to JPL DE tables //# Copyright (C) 1996,1997,1998,1999,2002,2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MEASJPL_H #define MEASURES_MEASJPL_H //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class String; class MVEpoch; // Interface to JPL DE tables // // // // //
      • MeasTable // // // // From Measure and JPL // // // // MeasJPL is the interface class to the JPL DE planetary data. // It has only static memebers.
        // Tables are found using the aipsrc // (using measures.
      • .directory) // mechanism. If not provided they are assumed to reside in standard places // (i.e. in $AIPSROOT/data/ephemerides) Tables are assumed to have the // VS_VERSION, VS_DATE, VS_CREATE and VS_TYPE keywords, and be of type IERS, // else an exception will be thrown.
        // The get() method will obtain data from the JPL planetary // tables (i.e. the DE200 and // the DE405 tables). The data obtained will be the barycentric // position (AU) and velocity (AU/d) of planets; the nutation (rad, rad/d) // or the libration (rad, rad/d; DE405 only). All in the J2000 system.
        // The JPL DE Tables have a large set of constants attach to it. Some // will be available by their own special code, the others their filed name. // (See the get functions.
        // The enumeration code gives the available data and planets. See // E.M. Standish et al., JPL IOM 314.10 - 127 for further details. //
        // Note that the normal usage of these tables is through the Measures system. // // // A message is Logged (once) if a table cannot be found. // A message is logged (once) if a date outside the range in // the Tables is asked for. // // //
      • AipsError if table opened has wrong format or otherwise corrupted. // // // // // // #include // #include // #include // #include // const MVEpoch dat = 51116; // a date (1998/10/30) in TDB // Vector val(6), valE(6); // results // // Get position and velocity of Venus (barycentric) // if (!MeasJPL::get(val, MeasJPL::DE200, MeasJPL::VENUS, dat)) { // cout << "Some error getting Venus position" << endl; // // Get Earth position and velocity (barycentric) // } else if (!MeasJPL::get(valE, MeasJPL::DE200, MeasJPL::VENUS, dat)) { // cout << "Some error getting Earth position" << endl; // } else { // cout << "Venus (geocentric): " << (val-valE) << endl; // }; // // // // // To use the JPL data for planetary positions and high precision nutation // // // // class MeasJPL { public: //# Constants //# Enumerations // Types of known data enum Types { // MJD (must be first in list) MJD, // Column with data X, // Number of columns N_Columns, // Planets MERCURY = 1, VENUS = 2, EARTH = 3, MARS = 4, JUPITER = 5, SATURN = 6, URANUS = 7, NEPTUNE = 8, PLUTO = 9, MOON = 10, SUN = 11, // Solar system barycentre BARYSOLAR = 12, // Earth-Moon system barycentre BARYEARTH = 13, // Nutations NUTATION = 14, // Librations LIBRATION = 15, // Number of types N_Types }; // Types of files enum Files { // DE200 DE200, // DE405 DE405, // # of known types N_Files, // Default DEFAULT = DE200 }; // Codes for special constants enum Codes { // Light velocity used in AU/d CAU, // Solar mass (GM0)/c2 in AU GMS, // AU in km AU, // Solar radius in AU RADS, // # of codes N_Codes }; //# General Member Functions // Get the values from a DE table, interpolated for date(in MJD(TDB)). // The file can be DE200 or DE405, the type as given in enum. static Bool get(Vector &returnValue, MeasJPL::Files file, MeasJPL::Types type, const MVEpoch &date); // Get indicated special constant static Bool getConst(Double &res, MeasJPL::Files which, MeasJPL::Codes what); // Get filed constant with name nam static Bool getConst(Double &res, MeasJPL::Files which, const String &nam); // Close the set of JPL tables only. Only call it last at end of program. static void closeMeas(); private: //# Constructors // Default constructor, NOT defined MeasJPL(); // Copy assign, NOT defined MeasJPL &operator=(const MeasJPL &other); //# Destructor // Destructor, NOT defined and not declared to stop warning // ~MeasJPL(); //# General member functions // Initialise tables static Bool initMeasOnce(MeasJPL::Files which); static void doInitMeas(MeasJPL::Files which); // Get a pointer to the data for the given date. It reads the data if needed. static const Double* fillMeas(Double &intv, MeasJPL::Files which, const MVEpoch &utf); // Interpolate Chebyshev polymomial to res static void interMeas(Double res[], MeasJPL::Files which, Double intv, Double ivf, Int ncf, Int ncm, Int na, const Double buf[]); //# Data members // Object to ensure safe multi-threaded lazy single initialization static std::once_flag theirCallOnceFlags[N_Files]; // Mutex for thread-safety (other than initialization). static std::mutex theirMutex; // Tables present static Table t[N_Files]; // Data column descriptor static ArrayColumn acc[N_Files]; // First (-1) MJD in list static Int mjd0[N_Files]; // Last MJD in list static Int mjdl[N_Files]; // Interval in days (i.e., date step between subsequent rows) static Int dmjd[N_Files]; // File names static const String tp[N_Files]; // Index in record static Int idx[N_Files][3][13]; // Dates of the data read in buffer. static vector curDate[N_Files]; // Data read in. static vector > dval[N_Files]; // Some helper data read from the table keywords // static Double aufac[N_Files]; static Double emrat[N_Files]; static Double cn[N_Files][N_Codes]; // }; //# Inline Implementations } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MeasMath.cc000066400000000000000000000523751476623553700207560ustar00rootroot00000000000000//# MeasMath.cc: Measure conversion aid routines //# Copyright (C) 1998-2000,2002-2004,2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include namespace { inline void updatePosition(casacore::Double const angle0, casacore::Double const angle1, casacore::MVPosition &pos) { if (angle1 == 0) { pos(0) = std::cos(angle0); pos(1) = std::sin(angle0); pos(2) = 0; } else { auto const loc = std::cos(angle1); pos(0) = std::cos(angle0) * loc; pos(1) = std::sin(angle0) * loc; pos(2) = std::sin(angle1); } } } namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Static data // Note: this static is not mutexed, because it does not harm if accidently // two threads fill it at the same time. uInt MeasMath::b1950_reg_p = 0; //# Constructors MeasMath::MeasMath() : inOK_p(False), outOK_p(False), inFrame_p(0), outFrame_p(0), SOLPOSIAU(0), ABERIAU(0), ABERB1950(0), NUTATIAU(0), NUTATB1950(0), PRECESIAU(0), PRECESB1950(0) { for (uInt i=0; i*frameInfo[i])()) { applyFrame_p[i] = inFrame_p; } else if (outOK_p && (outFrame_p->*frameInfo[i])()) { applyFrame_p[i] = outFrame_p; } else { frameOK_p[i] = False; } if (frameOK_p[i]) { if (outOK_p && (outFrame_p->*frameInfo[i])()) { deapplyFrame_p[i] = outFrame_p; } else { deapplyFrame_p[i] = inFrame_p; } } } } // Precession void MeasMath::createPrecession() { if (!PRECESIAU) { if (MeasTable::useIAU2000()) { PRECESIAU = new Precession(Precession::IAU2000); } else { PRECESIAU = new Precession(Precession::IAU1976); } } } void MeasMath::applyPrecession(MVPosition &in) { if (MeasTable::useIAU2000()) { getInfo(TT); in *= MeasTable::frameBias00(); in *= (*PRECESIAU)(info_p[TT]); } else { getInfo(TDB); in *= (*PRECESIAU)(info_p[TDB]); } } void MeasMath::deapplyPrecession(MVPosition &in) { if (MeasTable::useIAU2000()) { getInfo(TT); in = MeasTable::frameBias00() * in; in = (*PRECESIAU)(info_p[TT]) * in; } else { getInfo(TDB); in = (*PRECESIAU)(info_p[TDB]) * in; } } void MeasMath::createPrecessionB1950() { if (!PRECESB1950) PRECESB1950 = new Precession(Precession::B1950); } void MeasMath::applyPrecessionB1950(MVPosition &in) { getInfo(TDB); in *= (*PRECESB1950)(info_p[TDB]); } void MeasMath::deapplyPrecessionB1950(MVPosition &in) { getInfo(TDB); in = (*PRECESB1950)(info_p[TDB]) * in; } // Nutation void MeasMath::createNutation() { if (!NUTATIAU) { if (MeasTable::useIAU2000()) { if (MeasTable::useIAU2000A()) { NUTATIAU = new Nutation(Nutation::IAU2000A); } else { NUTATIAU = new Nutation(Nutation::IAU2000B); } } else { NUTATIAU = new Nutation(Nutation::IAU1980); } } } void MeasMath::applyNutation(MVPosition &in) { if (MeasTable::useIAU2000()) { getInfo(TT); in *= (*NUTATIAU)(info_p[TT]); } else { getInfo(TDB); in *= (*NUTATIAU)(info_p[TDB]); } } void MeasMath::deapplyNutation(MVPosition &in) { if (MeasTable::useIAU2000()) { getInfo(TT); in = (*NUTATIAU)(info_p[TT]) * in; } else { getInfo(TDB); in = (*NUTATIAU)(info_p[TDB]) * in; } } void MeasMath::createNutationB1950() { if (!NUTATB1950) NUTATB1950 = new Nutation(Nutation::B1950); } void MeasMath::applyNutationB1950(MVPosition &in) { getInfo(TDB); in *= (*NUTATB1950)(info_p[TDB]); } void MeasMath::deapplyNutationB1950(MVPosition &in) { getInfo(TDB); in = (*NUTATB1950)(info_p[TDB]) * in; } // Precession and Nutation void MeasMath::createPrecNutat() { createPrecession(); createNutation(); } void MeasMath::applyPrecNutat(MVPosition &in) { if (MeasTable::useIAU2000()) { getInfo(TT); in *= (RotMatrix((*PRECESIAU)(info_p[TT])) * RotMatrix((*NUTATIAU)(info_p[TT]))); } else { getInfo(TDB); in *= (RotMatrix((*PRECESIAU)(info_p[TDB])) * RotMatrix((*NUTATIAU)(info_p[TDB]))); } } void MeasMath::deapplyPrecNutat(MVPosition &in) { if (MeasTable::useIAU2000()) { getInfo(TT); in = (RotMatrix((*PRECESIAU)(info_p[TT])) * RotMatrix((*NUTATIAU)(info_p[TT]))) * in; } else { getInfo(TDB); in = (RotMatrix((*PRECESIAU)(info_p[TDB])) * RotMatrix((*NUTATIAU)(info_p[TDB]))) * in; } } void MeasMath::createPrecNutatB1950() { if (!PRECESB1950) PRECESB1950 = new Precession(Precession::B1950); if (!NUTATB1950) NUTATB1950 = new Nutation(Nutation::B1950); } void MeasMath::applyPrecNutatB1950(MVPosition &in, Bool doin) { getInfo(TDB); applyETerms(in, doin); in *= (RotMatrix((*PRECESB1950)(info_p[TDB])) * RotMatrix((*NUTATB1950)(info_p[TDB]))); } void MeasMath::deapplyPrecNutatB1950(MVPosition &in, Bool doin) { getInfo(TDB); in = (RotMatrix((*PRECESB1950)(info_p[TDB])) * RotMatrix((*NUTATB1950)(info_p[TDB]))) * in; deapplyETerms(in, doin); } // Aberration void MeasMath::createAberration() { if (!ABERIAU) ABERIAU = new Aberration(Aberration::STANDARD); } void MeasMath::applyAberration(MVPosition &in, Bool doin) { getInfo(TDB); // Aberration MVPOS1 = (*ABERIAU)(info_p[TDB]); // Get length lengthE = MVPOS1.radius(); // Beta^-1 (g1) g1 = sqrt(1 - lengthE * lengthE); if (doin) MVPOS4 = in; else { getInfo(J2000DIR); MVPOS4 = infomvd_p[J2000DIR-N_FrameDInfo]; } g2 = MVPOS4 * MVPOS1; // Shift MVPOS2 = ((g1-1.0-g2)*MVPOS4 + (1+g2/(1+g1)) * MVPOS1)*(1.0/(1.0+g2)); /// Really use JNAT rotateShift(in, MVPOS2, J2000LONG, J2000LAT, doin); } void MeasMath::deapplyAberration(MVPosition &in, Bool doin) { getInfo(TDB); // Aberration MVPOS1 = (*ABERIAU)(info_p[TDB]); // Get length lengthE = MVPOS1.radius(); // Beta^-1 (g1) g1 = sqrt(1 - lengthE * lengthE); if (doin) MVPOS4 = in; else { getInfo(J2000DIR); MVPOS4 = infomvd_p[J2000DIR-N_FrameDInfo]; } // First guess MVPOS2 = MVPOS4 - MVPOS1; // Solve for aberration solution do { g2 = MVPOS2 * MVPOS1; MVPOS3 = ((g1 * MVPOS2 + (1+g2/(1+g1)) * MVPOS1)*(1.0/(1.0+g2))); MVPOS3.adjust(); for (Int j=0; j<3; j++) { g3 = MVPOS1(j); MVPOS2(j) -= (MVPOS3(j) - MVPOS4(j))/ (((g1+g3*g3/(1+g1))- g3 * MVPOS3(j))/(1+g2)); } MVPOS3 -= MVPOS4; } while (MVPOS3.radius() > 1e-10); MVPOS2 -= MVPOS4; rotateShift(in, MVPOS2, J2000LONG, J2000LAT, doin); } void MeasMath::createAberrationB1950() { if (!ABERB1950) ABERB1950 = new Aberration(Aberration::B1950); } void MeasMath::applyAberrationB1950(MVPosition &in, Bool doin) { getInfo(TDB); // Aberration MVPOS1 = (*ABERB1950)(info_p[TDB]); /// Really should use precessed and nutated B1950 rotateShift(in, MVPOS1, APPLONG, APPLAT, doin); } void MeasMath::deapplyAberrationB1950(MVPosition &in, Bool doin) { getInfo(TDB); // Aberration MVPOS1 = (*ABERB1950)(info_p[TDB]); /// Really should use B1950 apparent rotateShift(in, -MVPOS1, APPLONG, APPLAT, doin); } // Solar bending void MeasMath::createSolarPos() { if (!SOLPOSIAU) SOLPOSIAU = new SolarPos(SolarPos::STANDARD); } void MeasMath::applySolarPos(MVPosition &in, Bool doin) { getInfo(TDB); // Solar position in rectangular coordinates MVPOS1 = (*SOLPOSIAU)(info_p[TDB]); // Get length and unit vector MVPOS1.adjust(lengthE); g1 = -1.974e-8 / lengthE; if (doin) MVPOS2 = in; else { getInfo(J2000DIR); MVPOS2 = infomvd_p[J2000DIR-N_FrameDInfo]; } g2 = MVPOS2 * MVPOS1; // Check if near sun if (!nearAbs(g2, 1.0, 1.0-cos(MeasData::SunSemiDiameter()/lengthE))) { MVPOS1 -= g2 * MVPOS2; MVPOS1 *= (g1 / (1.0 - g2)); rotateShift(in, MVPOS1, J2000LONG, J2000LAT, doin); } } void MeasMath::deapplySolarPos(MVPosition &in, Bool doin) { getInfo(TDB); // Solar position in rectangular coordinates MVPOS1 = (*SOLPOSIAU)(info_p[TDB]); // Get length and unit vector MVPOS1.adjust(lengthE); g1 = -1.974e-8 / lengthE; if (doin) MVPOS4 = in; else { getInfo(J2000DIR); MVPOS4 = infomvd_p[J2000DIR-N_FrameDInfo]; } g2 = MVPOS4 * MVPOS1; // Check if near sun if (!nearAbs(g2, 1.0, 1.0-cos(MeasData::SunSemiDiameter()/lengthE))) { // First guess MVPOS2 = MVPOS4; do { MVPOS3 = (MVPOS1 - g2 * MVPOS2) * (g1/(1.0 - g2)); MVPOS3.adjust(); for (Int j=0; j<3; j++) { g3 = MVPOS1(j); MVPOS2(j) -= (MVPOS3(j) + MVPOS2(j) - MVPOS4(j))/ (1 + (g3 * MVPOS3(j) - g1 * (g2 + g3 * MVPOS2(j)))/(1-g2)); } g2 = MVPOS2 * MVPOS1; MVPOS3 += MVPOS2; MVPOS3 -= MVPOS4; } while (MVPOS3.radius() > 1e-10); // Correction MVPOS2 -= MVPOS4; rotateShift(in, MVPOS2, J2000LONG, J2000LAT, doin); } } // Various conversions void MeasMath::applyHADECtoITRF(MVPosition &in) { getInfo(LONG); in *= RotMatrix(Euler(info_p[LONG], 3u)); in(1) = -in(1); } void MeasMath::deapplyHADECtoITRF(MVPosition &in) { getInfo(LONG); in(1) = -in(1); in = RotMatrix(Euler(info_p[LONG], 3u)) * in; } void MeasMath::applyHADECtoAZEL(MVPosition &in) { getInfo(LAT); in *= RotMatrix(Euler(M_PI_2 - info_p[LAT] , 2u, M_PI, 3u)); } void MeasMath::deapplyHADECtoAZEL(MVPosition &in) { getInfo(LAT); in = RotMatrix(Euler(M_PI_2 - info_p[LAT] , 2u, M_PI, 3u)) * in; } void MeasMath::applyHADECtoAZELGEO(MVPosition &in) { getInfo(LATGEO); in *= RotMatrix(Euler(M_PI_2 - info_p[LATGEO] , 2u, M_PI, 3u)); } void MeasMath::deapplyHADECtoAZELGEO(MVPosition &in) { getInfo(LATGEO); in = RotMatrix(Euler(M_PI_2 - info_p[LATGEO] , 2u, M_PI, 3u)) * in; } void MeasMath::applyJ2000toB1950(MVPosition &in, Bool doin) { if (!MeasMath::b1950_reg_p) { b1950_reg_p = AipsrcValue::registerRC(String("measures.b1950.d_epoch"), Unit("a"), Unit("a"), 2000.0); } Double epo; if (getInfo(UT1, True)) { epo = (info_p[UT1]-MeasData::MJD2000)/MeasData::JDCEN; } else epo = (AipsrcValue::get(MeasMath::b1950_reg_p)-2000.0)/100.0; applyJ2000toB1950(in, epo, doin); } void MeasMath::applyJ2000toB1950_VLA(MVPosition &in, Bool doin) { Double epo = 19.799-20.0; applyJ2000toB1950(in, epo, doin); } void MeasMath::applyJ2000toB1950(MVPosition &in, Double epo, Bool doin) { MVPosition VPOS3; VPOS3 = in; // Frame rotation in *= MeasData::MToB1950(4); in.adjust(); // E-terms deapplyETerms(in, doin, epo); MVPosition VPOS4; do { VPOS4 = in; deapplyJ2000toB1950(VPOS4, epo, doin); VPOS4 -= VPOS3; in -= VPOS4*MeasData::MToB1950(4); } while (VPOS4.radius() > 1e-12); } void MeasMath::deapplyJ2000toB1950(MVPosition &in, Bool doin) { if (!MeasMath::b1950_reg_p) { b1950_reg_p = AipsrcValue::registerRC(String("measures.b1950.d_epoch"), Unit("a"), Unit("a"), 2000.0); } Double epo; if (getInfo(UT1, True)) { epo = (info_p[UT1]-MeasData::MJD2000)/MeasData::JDCEN; } else epo = (AipsrcValue::get(MeasMath::b1950_reg_p)-2000.0)/100.0; deapplyJ2000toB1950(in, epo, doin); } void MeasMath::deapplyJ2000toB1950_VLA(MVPosition &in, Bool doin) { Double epo = 19.799-20.0; deapplyJ2000toB1950(in, epo, doin); } void MeasMath::deapplyJ2000toB1950(MVPosition &in, Double epo, Bool doin) { applyETerms(in, doin, epo); // Frame rotation MVPOS1 = in*MeasData::MToJ2000(2); in *= MeasData::MToJ2000(0); in += (epo*C::arcsec)*MVPOS1; in.adjust(); } void MeasMath::applyETerms(MVPosition &in, Bool doin, Double epo) { // E-terms MVPOS1 = MVPosition(MeasTable::AberETerm(0)); epo += 0.5; MVPOS1 += (epo*C::arcsec)*MVPosition(MeasTable::AberETerm(1)); if (doin) MVPOS2 = in; else { getInfo(B1950DIR); MVPOS2 = infomvd_p[B1950DIR-N_FrameDInfo]; } g1 = MVPOS2 * MVPOS1; MVPOS1 = g1 * MVPOS2 - MVPOS1; rotateShift(in, MVPOS1, B1950LONG, B1950LAT, doin); } void MeasMath::deapplyETerms(MVPosition &in, Bool doin, Double epo) { // E-terms // Iterate MVPOS1 = MVPosition(MeasTable::AberETerm(0)); epo += 0.5; MVPOS1 += (epo*C::arcsec)*MVPosition(MeasTable::AberETerm(1)); if (doin) MVPOS4 = in; else { getInfo(B1950DIR); MVPOS4 = infomvd_p[B1950DIR-N_FrameDInfo]; } MVPOS2 = MVPOS4; do { g1 = MVPOS2 * MVPOS1; MVPOS3 = MVPOS2 - MVPOS1 + (g1 * MVPOS2); MVPOS3.adjust(); MVPOS3 -= MVPOS4; MVPOS2 -= MVPOS3; } while (MVPOS3.radius() > 1e-5); MVPOS2 -= MVPOS4; rotateShift(in, MVPOS2, B1950LONG, B1950LAT, doin); } void MeasMath::applyGALtoJ2000(MVPosition &in) { in = MeasData::GALtoJ2000() * in; } void MeasMath::deapplyGALtoJ2000(MVPosition &in) { in = MeasData::J2000toGAL() * in; } void MeasMath::applyGALtoB1950(MVPosition &in) { in = MeasData::GALtoB1950() * in; } void MeasMath::deapplyGALtoB1950(MVPosition &in) { in = MeasData::B1950toGAL() * in; } void MeasMath::applyGALtoSUPERGAL(MVPosition &in) { in = MeasTable::galToSupergal() * in; } void MeasMath::deapplyGALtoSUPERGAL(MVPosition &in) { in *= MeasTable::galToSupergal(); } void MeasMath::applyICRStoJ2000(MVPosition &in) { in = MeasTable::ICRSToJ2000() * in; } void MeasMath::deapplyICRStoJ2000(MVPosition &in) { in *= MeasTable::ICRSToJ2000(); } void MeasMath::applyTOPOtoHADEC(MVPosition &in, Bool doin) { getInfo(LASTR); getInfo(TDB); getInfo(RADIUS); getInfo(LAT); g2 = MeasTable::diurnalAber(info_p[RADIUS], info_p[TDB]); // MVPOS1 = MVDirection(info_p[LASTR], info_p[LAT]); updatePosition(info_p[LASTR], info_p[LAT], MVPOS1); MVPOS1.readjust(g2); /// Really should use topo for planets rotateShift(in, MVPOS1, APPLONG, APPLAT, doin); deapplyPolarMotion(in); } void MeasMath::deapplyTOPOtoHADEC(MVPosition &in, Bool doin) { getInfo(LASTR); getInfo(TDB); getInfo(RADIUS); getInfo(LAT); g2 = MeasTable::diurnalAber(info_p[RADIUS], info_p[TDB]); // MVPOS1 = MVDirection(info_p[LASTR], info_p[LAT]); updatePosition(info_p[LASTR], info_p[LAT], MVPOS1); MVPOS1.readjust(g2); applyPolarMotion(in); /// Really use topo for planets rotateShift(in, -MVPOS1, APPLONG, APPLAT, doin); } void MeasMath::applyPolarMotion(MVPosition &in) { getInfo(TDB); getInfo(LASTR); in(1) = -in(1); Euler EULER1 = MeasTable::polarMotion(info_p[TDB]); EULER1(2) = info_p[LASTR]; in = RotMatrix(EULER1) * in; } void MeasMath::deapplyPolarMotion(MVPosition &in) { getInfo(TDB); getInfo(LASTR); Euler EULER1 = MeasTable::polarMotion(info_p[TDB]); EULER1(2) = info_p[LASTR]; in *= RotMatrix(EULER1); in(1) = -in(1); } void MeasMath::applyAZELtoAZELSW(MVPosition &in) { in(0) = -in(0); in(1) = -in(1); } void MeasMath::applyECLIPtoJ2000(MVPosition &in) { in = RotMatrix(Euler(MeasTable::fundArg(0)(0.0), 1, 0, 0)) * in; } void MeasMath::deapplyECLIPtoJ2000(MVPosition &in) { in *= RotMatrix(Euler(MeasTable::fundArg(0)(0.0), 1, 0, 0)); } void MeasMath::applyMECLIPtoJMEAN(MVPosition &in) { getInfo(TDB); in = RotMatrix(Euler(MeasTable::fundArg(0)((info_p[TDB] - MeasData::MJD2000)/ MeasData::JDCEN), 1, 0, 0)) * in; } void MeasMath::deapplyMECLIPtoJMEAN(MVPosition &in) { getInfo(TDB); in *= RotMatrix(Euler(MeasTable::fundArg(0)((info_p[TDB] - MeasData::MJD2000)/ MeasData::JDCEN), 1, 0, 0)); } void MeasMath::applyTECLIPtoJTRUE(MVPosition &in) { getInfo(TDB); in = RotMatrix(Euler(-Nutation(Nutation::STANDARD)(info_p[TDB])(2), 1, 0, 0)) * in; } void MeasMath::deapplyTECLIPtoJTRUE(MVPosition &in) { getInfo(TDB); in *= RotMatrix(Euler(-Nutation(Nutation::STANDARD)(info_p[TDB])(2), 1, 0, 0)); } void MeasMath::applyAPPtoTOPO(MVPosition &in, const Double len, Bool doin) { if (len != 0) { getInfo(LASTR); getInfo(LONG); getInfo(LAT); getInfo(RADIUS); ROTMAT1 = RotMatrix(Euler(info_p[LASTR] - info_p[LONG], 3u)); // Correction MVPOS1 = (ROTMAT1 * MVPosition(Quantity(info_p[RADIUS], "m"), info_p[LONG], info_p[LAT])) * (1.0/len); rotateShift(in, -MVPOS1, APPLONG, APPLAT, doin); } } void MeasMath::deapplyAPPtoTOPO(MVPosition &in, const Double len, Bool doin) { if (len != 0) { getInfo(LASTR); getInfo(LONG); getInfo(LAT); getInfo(RADIUS); ROTMAT1 = RotMatrix(Euler(info_p[LASTR] - info_p[LONG], 3u)); // Correction MVPOS1 = (ROTMAT1 * MVPosition(Quantity(info_p[RADIUS], "m"), info_p[LONG], info_p[LAT])) * (1.0/len); rotateShift(in, MVPOS1, APPLONG, APPLAT, doin); } } // General support Bool MeasMath::getInfo(FrameInfo i, Bool ret) { // Frame information groups static FrameType InfoType[N_FrameInfo] = { EPOCH, EPOCH, EPOCH, EPOCH, POSITION, POSITION, POSITION, POSITION, DIRECTION, DIRECTION, DIRECTION, DIRECTION, DIRECTION, DIRECTION, DIRECTION, DIRECTION, DIRECTION }; // Frame information methods static FRDINFO InfoDFrame[N_FrameDInfo] = { &MeasFrame::getTDB, &MeasFrame::getLASTr, &MeasFrame::getTT, &MeasFrame::getUT1, &MeasFrame::getLong, &MeasFrame::getLat, &MeasFrame::getRadius, &MeasFrame::getLatGeo, &MeasFrame::getJ2000Long, &MeasFrame::getJ2000Lat, &MeasFrame::getB1950Long, &MeasFrame::getB1950Lat, &MeasFrame::getAppLong, &MeasFrame::getAppLat }; static FRMVDINFO InfoMVDFrame[N_FrameMVDInfo] = { &MeasFrame::getJ2000, &MeasFrame::getB1950, &MeasFrame::getApp }; if (!infoOK_p[i]) { // Make sure there has not been an epoch added getFrame(InfoType[i]); if (frameOK_p[InfoType[i]]) { if (i < N_FrameDInfo) { (applyFrame_p[InfoType[i]]->*InfoDFrame[i])(info_p[i]); } else { (applyFrame_p[InfoType[i]]->*InfoMVDFrame[i-N_FrameDInfo]) (infomvd_p[i-N_FrameDInfo]); } } else { if (ret) return False; throw(AipsError(String("Missing information in Frame ") + "specified for conversion")); } infoOK_p[i] = True; } return True; } void MeasMath::rotateShift(MVPosition &in, const MVPosition &shft, const FrameInfo lng, const FrameInfo lat, Bool doin) { if (doin) { in += shft; in.adjust(); } else { getInfo(lat); getInfo(lng); // Rotation towards direction ROTMAT1 = RotMatrix(Euler(-M_PI_2 + info_p[lat], 2u, -info_p[lng], 3u)); // Rotation towards correction ROTMAT1 = RotMatrix(Euler(-(ROTMAT1*shft).getLong(), 3u)) * ROTMAT1; // Rotate over correction in = ((RotMatrix(Euler((ROTMAT1*shft).getValue()(0), 2u)) * ROTMAT1) * in) * ROTMAT1; } } void MeasMath::getAPP(MVPosition &out) { getInfo(APPDIR); out = infomvd_p[APPDIR-N_FrameDInfo]; } void MeasMath::getJ2000(MVPosition &out) { getInfo(J2000DIR); out = infomvd_p[J2000DIR-N_FrameDInfo]; } void MeasMath::getB1950(MVPosition &out) { getInfo(B1950DIR); out = infomvd_p[B1950DIR-N_FrameDInfo]; } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/MeasMath.h000066400000000000000000000233761476623553700206170ustar00rootroot00000000000000//# MeasMath.h: Measure conversion aid routines //# Copyright (C) 1998,2000,2002-2004,2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MEASMATH_H #define MEASURES_MEASMATH_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class Measure; class MRBase; class Precession; class Nutation; class SolarPos; class Aberration; //# Typedefs // Measure conversion aid routines // // // // //
      • MeasConvert class //
      • overall conversion class // // // // Measure and Mathematics // // // // The conversion of measures like MDirection, MPosition etc have many // conversion routines in common. This class combines all of these // conversions, including data caches for re-use. // // The class is always created by the default constructor. For each operation // (like e.g. Precession application), it has three function: //
          //
        • create(): create an instance of the data necessary to convert //
        • apply(): apply the conversion (in the sense of from standard to // perturbed (e.g. from J2000 to TOPO)) //
        • deapply(): in the reverse direction of apply //
        //
        // // // See MCDirection source for how to use // the class. // // // To re-use code for a specific measure conversion, and to ease // the caching administration for each individual conversion. // // // //
      • Nothing I know of // class MeasMath { public: //# Constructors // Default constructor MeasMath(); //# Destructor ~MeasMath(); //# Member functions // Initialise the frame to be used. The apply direction uses the // inref if present; the deapply the outref if present, otherwise the // other one. void initFrame(MRBase &outref, MRBase &inref); // Functions to create a particular conversion instance; to apply // or deapply the instance. // // Precession for J2000 (IAU definition) and in coordinates // void createPrecession(); void applyPrecession(MVPosition &in); void deapplyPrecession(MVPosition &in); // // Precession for B1950 and in coordinates // void createPrecessionB1950(); void applyPrecessionB1950(MVPosition &in); void deapplyPrecessionB1950(MVPosition &in); // // Nutation for J2000 (IAU standard) and in coordinates // void createNutation(); void applyNutation(MVPosition &in); void deapplyNutation(MVPosition &in); // // Nutation for B1950 and in coordinates // void createNutationB1950(); void applyNutationB1950(MVPosition &in); void deapplyNutationB1950(MVPosition &in); // // Precession and Nutation for J2000 or B1950 and in coordinates // void createPrecNutat(); void applyPrecNutat(MVPosition &in); void deapplyPrecNutat(MVPosition &in); void createPrecNutatB1950(); void applyPrecNutatB1950(MVPosition &in, Bool doin=True); void deapplyPrecNutatB1950(MVPosition &in, Bool doin=True); // // Aberration for J2000 (IAU definition) and B1950 and in coordinates // void createAberration(); void applyAberration(MVPosition &in, Bool doin=True); void deapplyAberration(MVPosition &in, Bool doin=True); void createAberrationB1950(); void applyAberrationB1950(MVPosition &in, Bool doin=True); void deapplyAberrationB1950(MVPosition &in, Bool doin=True); // // Solar bending for J2000 (IAU definition) and in coordinates. // False if dependent on frame direction rather than input one. // void createSolarPos(); void applySolarPos(MVPosition &in, Bool doin=True); void deapplySolarPos(MVPosition &in, Bool doin=True); // // Various conversions // void applyHADECtoITRF(MVPosition &in); void deapplyHADECtoITRF(MVPosition &in); void applyHADECtoAZEL(MVPosition &in); void deapplyHADECtoAZEL(MVPosition &in); void applyHADECtoAZELGEO(MVPosition &in); void deapplyHADECtoAZELGEO(MVPosition &in); void applyJ2000toB1950(MVPosition &in, Double epo, Bool doin); void deapplyJ2000toB1950(MVPosition &in, Double epo, Bool doin); void applyJ2000toB1950(MVPosition &in, Bool doin=True); void deapplyJ2000toB1950(MVPosition &in, Bool doin=True); void applyJ2000toB1950_VLA(MVPosition &in, Bool doin=True); void deapplyJ2000toB1950_VLA(MVPosition &in, Bool doin=True); void applyETerms(MVPosition &in, Bool doin=True, Double epo=2000.0); void deapplyETerms(MVPosition &in, Bool doin=True, Double epo=2000.0); void applyGALtoJ2000(MVPosition &in); void deapplyGALtoJ2000(MVPosition &in); void applyGALtoB1950(MVPosition &in); void deapplyGALtoB1950(MVPosition &in); void applyGALtoSUPERGAL(MVPosition &in); void deapplyGALtoSUPERGAL(MVPosition &in); void applyICRStoJ2000(MVPosition &in); void deapplyICRStoJ2000(MVPosition &in); void applyTOPOtoHADEC(MVPosition &in, Bool doin=True); void deapplyTOPOtoHADEC(MVPosition &in, Bool doin=True); void applyPolarMotion(MVPosition &in); void deapplyPolarMotion(MVPosition &in); void applyAZELtoAZELSW(MVPosition &in); void applyECLIPtoJ2000(MVPosition &in); void deapplyECLIPtoJ2000(MVPosition &in); void applyMECLIPtoJMEAN(MVPosition &in); void deapplyMECLIPtoJMEAN(MVPosition &in); void applyTECLIPtoJTRUE(MVPosition &in); void deapplyTECLIPtoJTRUE(MVPosition &in); void applyAPPtoTOPO(MVPosition &in, const Double len, Bool doin=True); void deapplyAPPtoTOPO(MVPosition &in, const Double len, Bool doin=True); // // // Transfer some information // void getAPP(MVPosition &out); void getJ2000(MVPosition &out); void getB1950(MVPosition &out); // private: //# Enum // Types of frame information groups enum FrameType { EPOCH = 0, POSITION, DIRECTION, VELOCITY, N_FrameType }; // Types of frame information enum FrameInfo { TDB = 0, LASTR, TT, UT1, LONG, LAT, RADIUS, LATGEO, J2000LONG, J2000LAT, B1950LONG, B1950LAT, APPLONG, APPLAT, N_FrameDInfo, J2000DIR = N_FrameDInfo, B1950DIR, APPDIR, N_FrameInfo, N_FrameMVDInfo = N_FrameInfo-J2000DIR }; //# Typedefs // To get frame group typedef const Measure* (MeasFrame::*FRFCT)() const; // To get frame info // typedef Bool (MeasFrame::*FRDINFO)(Double &) const; typedef Bool (MeasFrame::*FRMVDINFO)(MVDirection &) const; // //# Cached Data // Data cached for fast calculations and workspace // // Frame information // Bool inOK_p; Bool outOK_p; Bool frameOK_p[N_FrameType]; MeasFrame *inFrame_p; MeasFrame *outFrame_p; MeasFrame *applyFrame_p[N_FrameType]; MeasFrame *deapplyFrame_p[N_FrameType]; // // Conversion information // SolarPos *SOLPOSIAU; Aberration *ABERIAU, *ABERB1950; Nutation *NUTATIAU, *NUTATB1950; Precession *PRECESIAU, *PRECESB1950; // // Workspace // RotMatrix ROTMAT1; MVPosition MVPOS1, MVPOS2, MVPOS3, MVPOS4; Double g1, g2, g3, lengthE; Bool infoOK_p[N_FrameInfo]; Double info_p[N_FrameDInfo]; MVDirection infomvd_p[N_FrameMVDInfo]; // // Aipsrc definition for B1950 epoch (in years) static uInt b1950_reg_p; // //# Constructors // Copy constructor (not implemented) MeasMath(const MeasMath &other); // Assignment (not implemented) MeasMath &operator=(const MeasMath &other); //# Member functions // Get proper frame information void getFrame(FrameType i); // Get information from the frame // //
      • AipsError if information not available; or False return if // ret=True // // Bool getInfo(FrameInfo i, Bool ret=False); // // Make a shift of coordinate into a rotation and apply it when doin is // False. Else apply a shift. // Given are the longitude and latitude codes of the direction to be used, // and the shift to be applied in that system to the in coordinate. void rotateShift(MVPosition &in, const MVPosition &shft, const FrameInfo lng, const FrameInfo lat, Bool doin); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MeasRef.h000066400000000000000000000150621476623553700204330ustar00rootroot00000000000000//# MeasRef.h: Reference frame for physical measures //# Copyright (C) 1995,1996,1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MEASREF_H #define MEASURES_MEASREF_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class String; template class MeasRef; // Reference frame for physical measures // // // // //
      • MRBase: the MeasRef base class //
      • Quantum class //
      • Measure class // // // // From Measure and Reference frame // // // // MeasRef specifies the reference frame for a physical quantity // specified by one of the derived Measure // classes (e.g. MEpoch). It is derived from // MRBase, which describes the class. // // MeasRef containres are created using the Measure::Ref class // (e.g. MDirection::Ref). // // // // See Measure for an example // // // // To gather all reference frame information in the one class. // // // // template class MeasRef : public MRBase { public: //# Friends //# Constructors // Construct an empty MeasRef. I.e. it will have a standard, // default, type; no offsets and Frame. MeasRef(); // Copy constructor MeasRef(const MeasRef &other); // Copy assignment MeasRef &operator=(const MeasRef &other); // Construct a reference with specified type, offset and Frame // // The following should really be (and should // still be called as), but // compiler does not accept it, due to incomplete definition when // called in MeasBase: // MeasRef(Ms::Types tp); // Furthermore, default arguments are not supported with templated classes: explicit MeasRef(const uInt tp); MeasRef(const uInt tp, const Ms &ep); MeasRef(const uInt tp, const MeasFrame &mf); MeasRef(const uInt tp, const MeasFrame &mf, const Ms &ep); // //# Destructor ~MeasRef(); //# Operators // Check if same MeasRef Bool operator==(const MeasRef &other) const; // Check if unequal MeasRef Bool operator!=(const MeasRef &other) const; //# General Member Functions // Check if empty reference virtual Bool empty() const; // Check the type of Measure the reference can be used for // static const String &showMe(); // // Return the type of the reference // the following should really be // (and should be interpreted as), but // cannot create a virtual function: // Ms::Types getType(); virtual uInt getType() const; // Return the frame of reference virtual MeasFrame &getFrame(); // Return the first frame which has specified information. Checking is done in // argument order. // //
      • AipsError if neither reference has a frame or the proper type // // static const MeasFrame &framePosition(MRBase &ref1, MRBase &ref2); static const MeasFrame &frameEpoch(MRBase &ref1, MRBase &ref2); static const MeasFrame &frameDirection(MRBase &ref1, MRBase &ref2); static const MeasFrame &frameRadialVelocity(MRBase &ref1, MRBase &ref2); static const MeasFrame &frameComet(MRBase &ref1, MRBase &ref2); // // Return the offset (or 0) virtual const Measure* offset() const; // Set the type // //
      • AipsError if wrong Measure // // the following should really be // (and should be called as), but // compiler does not accept it, since a virtual function: // void set(Ms::Types tp); // virtual void setType(uInt tp); virtual void set(uInt tp); // // Set a new offset void set(const Ms &ep); // Set a new offset (for internal use only) void set(const Measure &ep); // Set a new frame virtual void set(const MeasFrame &mf); // Print a Measure virtual void print(ostream &os) const; private: // Representation class class RefRep { public: // Constructor // Next one must be in-line for (some?) compilers RefRep() : type(Ms::DEFAULT), offmp(0), frame() {} // Destructor // Next one must be in-line for (some?) compilers ~RefRep() {delete offmp;} // The actual data // // Type of reference typename Ms::Types type; // Pointer to main Measure, defining an offset Measure *offmp; // Reference frame MeasFrame frame; // }; //# Data std::shared_ptr rep_p; //# Member functions // Create an instance of MeasRef void create(); // Copy an instance MeasRef copy(); }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/measures/Measures/MeasRef.tcc000066400000000000000000000145611476623553700207600ustar00rootroot00000000000000//# MeasRef.cc: Reference frame for physical measures //# Copyright (C) 1995-2001,2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MEASREF_TCC #define MEASURES_MEASREF_TCC //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors template MeasRef::MeasRef() {} template MeasRef::MeasRef(const MeasRef &other) : MRBase(other), rep_p (other.rep_p) {} template MeasRef & MeasRef::operator=(const MeasRef &other) { if (this != &other) { rep_p = other.rep_p; } return *this; } template MeasRef::MeasRef(const uInt tp) { create(); rep_p->type = Ms::castType(tp); } template MeasRef::MeasRef(const uInt tp, const Ms &ep) { create(); rep_p->type = Ms::castType(tp); rep_p->offmp = new Ms(ep); } template MeasRef::MeasRef(const uInt tp, const MeasFrame &mf) { create(); rep_p->type = Ms::castType(tp); rep_p->frame = mf; } template MeasRef::MeasRef(const uInt tp, const MeasFrame &mf, const Ms &ep) { create(); rep_p->type = Ms::castType(tp); rep_p->offmp = new Ms(ep); rep_p->frame = mf; } template void MeasRef::create() { if (empty()) { rep_p.reset (new RefRep); } } //# Destructor template MeasRef::~MeasRef() {} //# Operators template Bool MeasRef::operator==(const MeasRef &other) const { return (rep_p == other.rep_p); } template Bool MeasRef::operator!=(const MeasRef &other) const { return (rep_p != other.rep_p); } //# Member functions template Bool MeasRef::empty() const { return !rep_p; } template const String &MeasRef::showMe() { return Ms::showMe(); } template uInt MeasRef::getType() const{ return (! empty() ? rep_p->type : 0); } template MeasFrame &MeasRef::getFrame() { create(); return (rep_p->frame); } template const MeasFrame &MeasRef::framePosition(MRBase &ref1, MRBase &ref2) { if (!ref1.empty() && ref1.getFrame().position()) { return ref1.getFrame(); } else if (!ref2.empty() && ref2.getFrame().position()) { } else { throw(AipsError("No MeasFrame position specified for conversion of " + Ms::showMe())); } return ref2.getFrame(); } template const MeasFrame &MeasRef::frameEpoch(MRBase &ref1, MRBase &ref2) { if (!ref1.empty() && ref1.getFrame().epoch()) { return ref1.getFrame(); } else if (!ref2.empty() && ref2.getFrame().epoch()) { } else { throw(AipsError("No MeasFrame epoch specified for conversion of " + Ms::showMe())); } return ref2.getFrame(); } template const MeasFrame &MeasRef::frameDirection(MRBase &ref1, MRBase &ref2) { if (!ref1.empty() && ref1.getFrame().direction()) { return ref1.getFrame(); } else if (!ref2.empty() && ref2.getFrame().direction()) { } else { throw(AipsError("No MeasFrame direction specified for conversion of " + Ms::showMe())); } return ref2.getFrame(); } template const MeasFrame &MeasRef::frameRadialVelocity(MRBase &ref1, MRBase &ref2) { if (!ref1.empty() && ref1.getFrame().radialVelocity()) { return ref1.getFrame(); } else if (!ref2.empty() && ref2.getFrame().radialVelocity()) { } else { throw(AipsError("No MeasFrame specified for conversion of " + Ms::showMe())); } return ref2.getFrame(); } template const MeasFrame &MeasRef::frameComet(MRBase &ref1, MRBase &ref2) { if (!ref1.empty() && ref1.getFrame().comet()) { return ref1.getFrame(); } else if (!ref2.empty() && ref2.getFrame().comet()) { } else { throw(AipsError("No MeasFrame comet specified for conversion of " + Ms::showMe())); } return ref2.getFrame(); } template const Measure* MeasRef::offset() const { return ( ! empty() ? rep_p->offmp : 0); } template void MeasRef::setType(uInt tp) { set(tp); } template void MeasRef::set(uInt tp) { create(); rep_p->type = Ms::castType(tp); } template void MeasRef::set(const Ms &ep) { create(); if (rep_p->offmp) { delete rep_p->offmp; rep_p->offmp = 0; } rep_p->offmp = new Ms(ep); } template void MeasRef::set(const Measure &ep) { create(); if (rep_p->offmp) { delete rep_p->offmp; rep_p->offmp = 0; } rep_p->offmp = ep.clone(); } template void MeasRef::set(const MeasFrame &mf) { create(); rep_p->frame = mf; } template MeasRef MeasRef::copy() { MeasRef tmp; tmp.create(); tmp.rep_p->type = rep_p->type; if (rep_p->offmp) tmp.rep_p->offmp = rep_p->offmp->clone(); tmp.rep_p->frame = rep_p->frame; return tmp; } template void MeasRef::print(ostream &os) const { os << "Reference for an " << showMe(); os << " with Type: " << Ms::showType(getType()); if (offset()) { os << ", Offset: " << *(offset()); } // Get rid of const if (!((MeasRef *)(this))->getFrame().empty()) { os << "," << endl << ((MeasRef *)(this))->getFrame(); } } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MeasTable.cc000066400000000000000000006230751476623553700211150ustar00rootroot00000000000000//# MeasTable.cc: MeasTable provides Measure computing database data //# Copyright (C) 1995-1999,2000-2004,2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN #ifndef CASADATA #define CASADATA "/usr/local/share/data/casacore" #endif //# Constants //# Static class Data MeasTableMulSC MeasTable::theirMulSC; MeasTableMulSC1950 MeasTable::theirMulSC1950; MeasTableMulSC2000A MeasTable::theirMulSC2000A; MeasTableMulSC2000B MeasTable::theirMulSC2000B; MeasTableMulAber MeasTable::theirMulAber; MeasTableMulAber1950 MeasTable::theirMulAber1950; MeasTableMulPosSunXY MeasTable::theirMulPosSunXY; MeasTableMulPosSunZ MeasTable::theirMulPosSunZ; MeasTableMulPosEarthXY MeasTable::theirMulPosEarthXY; MeasTableMulPosEarthZ MeasTable::theirMulPosEarthZ; std::once_flag MeasTable::theirPlanetaryInitOnceFlag; std::once_flag MeasTable::theirPlanetaryConstantsInitOnceFlag; std::once_flag MeasTable::theirObsInitOnceFlag; Vector MeasTable::obsNams; Vector MeasTable::obsPos; Vector MeasTable::antResponsesPath; std::once_flag MeasTable::theirLinesInitOnceFlag; Vector MeasTable::lineNams; Vector MeasTable::linePos; std::once_flag MeasTable::theirSrcInitOnceFlag; Vector MeasTable::srcNams; Vector MeasTable::srcPos; std::once_flag MeasTable::theirIGRFInitOnceFlag; Double MeasTable::dtimeIGRF = 0; Double MeasTable::firstIGRF = 0; std::vector> MeasTable::coefIGRF; std::vector> MeasTable::dIGRF; ///#if !defined(USE_THREADS) || defined(__APPLE__) ///std::mutex MeasTable::theirdUT1Mutex; ///#endif //# Member functions Bool MeasTable::useIAU2000() { // Aipsrc registration (for speed) of use of iau2000 and if so the 2000a version. static const uInt iau2000_reg = AipsrcValue::registerRC("measures.iau2000.b_use", False); return AipsrcValue::get(iau2000_reg); } Bool MeasTable::useIAU2000A() { // comment as above in useIAU2000() static const uInt iau2000a_reg = AipsrcValue::registerRC("measures.iau2000.b_use2000a", False); return AipsrcValue::get(iau2000a_reg); } Double MeasTable::precRate00(const uInt which) { static const Double preoblcor[3] = { -0.29965*C::arcsec, -0.02524*C::arcsec, 0*C::arcsec }; DebugAssert(which < 3, AipsError); return preoblcor[which]; } RotMatrix MeasTable::frameBias00() { static const Double bias[3] = { -0.041775*C::arcsec, -0.0068192*C::arcsec, -0.0146*C::arcsec}; static const RotMatrix rbias (Euler(bias[2], 3, bias[0]*sin(MeasData::eps0J2000()), 2, -bias[1], 1)); return rbias; } void MeasTable::precessionCoef(Double T, Polynomial result[3]) { static const Double PCOEF[3][6] = { {+2306.2181,+1.39656,-0.000139,+0.30188,-0.000344,+0.017998}, {+2004.3109,-0.85330,-0.000217,-0.42665,-0.000217,-0.041833}, {+2306.2181,+1.39656,-0.000139,+1.09468,-0.000066,+0.018203} }; calcPrecesCoef(T, result, &PCOEF[0]); } void MeasTable::precessionCoef2000(Polynomial result[3]) { static const Double PCOEF[3][6] = { { 2.5976176,2306.0809506, 0.3019015, 0.0179663,-0.0000327,-0.0000002}, { 0.0, 2004.1917476,-0.4269353,-0.0418251,-0.0000601,-0.0000001}, {-2.5976176,2306.0803226, 1.0947790, 0.0182273,-0.0000470,-0.0000003} }; calcPrecesCoef2000(result, &PCOEF[0]); } void MeasTable::precessionCoef1950(Double T, Polynomial result[3]) { static const Double PCOEF[3][6] = { {2303.5545,+1.39720,0.000060,+0.30240,-0.000270,+0.017995}, {2005.1120,-0.85290,-0.00037,-0.42650,-0.000370,-0.041800}, {2303.5545,+1.39720,0.000060,+1.09480,+0.000390,+0.018325} }; calcPrecesCoef(T, result, &PCOEF[0]); } void MeasTable::calcPrecesCoef(Double T, Polynomial result[3], const Double coef[3][6]) { Int l; Int m=1; for (uInt i=0; i<3; i++) { m = -m; l = 0; for (uInt j=0; j<3; j++) { Polynomial poly(2-j); for (uInt k=0; k<3-j; k++, l++) { poly.setCoefficient(k,coef[i][l]); } result[i].setCoefficient(j+1,m*poly(T) * C::arcsec); } } } void MeasTable::calcPrecesCoef2000(Polynomial result[3], const Double coef[3][6]) { Int m=1; for (uInt i=0; i<3; i++) { m = -m; for (uInt j=0; j<6; j++) { result[i].setCoefficient(j, m*coef[i][j] * C::arcsec); } } } const Polynomial &MeasTable::fundArg(uInt which) { static const Double FUND[6][4] = { { 84381.448, -46.8150,-0.0059, 0.001813}, { 485866.733, 1717915922.633, 31.310, 0.064}, {1287099.804, 129596581.224, -0.577, -0.012}, { 335778.877, 1739527263.137,-13.257, 0.011}, {1072261.307, 1602961601.328, -6.891, 0.019}, { 450160.280, -6962890.539, 7.455, 0.008} }; static const std::vector> polyArray(calcFundArg(&FUND[0])); DebugAssert(which < 6, AipsError); return polyArray[which]; } const Polynomial &MeasTable::fundArg1950(uInt which) { static const Double FUND[6][4] = { { 84428.26, -46.846,-0.0059, 0.00181}, {1065976.59, 1717915856.79, 33.09, 0.0518}, { 1290513.0, 129596579.1, -0.54, -0.0120}, { 40503.2, 1739527290.54,-11.56, -0.0012}, { 1262654.95,1602961611.18, -5.17, 0.0068}, { 933059.79, -6962911.23, 7.48, 0.0080} }; static const std::vector> polyArray(calcFundArg(&FUND[0])); DebugAssert(which < 6, AipsError); return polyArray[which]; } const Polynomial &MeasTable::fundArg2000(uInt which) { static const Double FUND[6][5] = { { 84381.448, -46.8150-0.02524, -0.0059, 0.001813, 0.0}, { 485868.249036, 1717915923.2178, 31.8792, 0.051635, -0.00024470}, {1287104.79305, 129596581.0481, -0.5532, 0.000136, -0.00001149}, { 335779.526232, 1739527262.8478, -12.7512, -0.001037, 0.00000417}, {1072260.70369, 1602961601.2090, - 6.3706, 0.006593, -0.00003169}, { 450160.398036, -6962890.5431, 7.4722, 0.007702, -0.00005939} }; static const std::vector> polyArray(calcFundArg00(&FUND[0])); DebugAssert(which < 6, AipsError); return polyArray[which]; } const Polynomial &MeasTable::planetaryArg2000(uInt which) { static const Double FUND[8][2] = { { 4.402608842, 2608.7903141574 }, { 3.176146697, 1021.3285546211 }, { 1.753470314, 628.3075849991 }, { 6.203480913, 334.0612426700 }, { 0.599546497, 52.9690962641 }, { 0.874016757, 21.3299104960 }, { 5.481293871, 7.4781598567 }, { 5.321159000, 3.8127774000 } }; static const std::vector> polyArray(calcPlanArg00(&FUND[0])); DebugAssert(which < 14, AipsError); return polyArray[which]; } std::vector> MeasTable::calcFundArg(const Double coeff[6][4]) { std::vector> result(6); Int i,j; for (i=0; i<6; i++) { result[i] = Polynomial(3); for (j=0; j<4; j++) { result[i].setCoefficient(j, coeff[i][j]*C::arcsec); } } return result; } std::vector> MeasTable::calcFundArg00(const Double coeff[6][5]) { std::vector> result(6); Int i,j; for (i=0; i<6; i++) { result[i] = Polynomial(4); for (j=0; j<5; j++) { result[i].setCoefficient(j, coeff[i][j]*C::arcsec); } } return result; } std::vector> MeasTable::calcPlanArg00(const Double coeff[8][2]) { static const Double APA[3] = { 0.0, 0.02438175, 0.00000538691 }; std::vector> result(14); for (uInt i=0; i<5; i++) { result[i] = fundArg2000(i+1); } for (uInt i=5; i<13; i++) { result[i] = Polynomial(1); for (uInt j=0; j<2; j++) { result[i].setCoefficient(j, coeff[i-5][j]); } } result[13] = Polynomial(2); for (uInt j=0; j<3; j++) { result[13].setCoefficient(j, APA[j]); } return result; } const Double* MeasTable::mulArg(uInt which) { static const Double ARG[106][5] = { {0 ,0 ,0 ,0 ,1 }, {0 ,0 ,0 ,0 ,2 }, {-2 ,0 ,2 ,0 ,1 }, {2 ,0 ,-2 ,0 ,0 }, {-2 ,0 ,2 ,0 ,2 }, {1 ,-1 ,0 ,-1 ,0 }, {0 ,-2 ,2 ,-2 ,1 }, {2 ,0 ,-2 ,0 ,1 }, {0 ,0 ,2 ,-2 ,2 }, {0 ,1 ,0 ,0 ,0 }, {0 ,1 ,2 ,-2 ,2 }, {0 ,-1 ,2 ,-2 ,2 }, {0 ,0 ,2 ,-2 ,1 }, {2 ,0 ,0 ,-2 ,0 }, {0 ,0 ,2 ,-2 ,0 }, {0 ,2 ,0 ,0 ,0 }, {0 ,1 ,0 ,0 ,1 }, {0 ,2 ,2 ,-2 ,2 }, {0 ,-1 ,0 ,0 ,1 }, {-2 ,0 ,0 ,2 ,1 }, {0 ,-1 ,2 ,-2 ,1 }, {2 ,0 ,0 ,-2 ,1 }, {0 ,1 ,2 ,-2 ,1 }, {1 ,0 ,0 ,-1 ,0 }, {2 ,1 ,0 ,-2 ,0 }, {0 ,0 ,-2 ,2 ,1 }, {0 ,1 ,-2 ,2 ,0 }, {0 ,1 ,0 ,0 ,2 }, {-1 ,0 ,0 ,1 ,1 }, {0 ,1 ,2 ,-2 ,0 }, {0 ,0 ,2 ,0 ,2 }, {1 ,0 ,0 ,0 ,0 }, {0 ,0 ,2 ,0 ,1 }, {1 ,0 ,2 ,0 ,2 }, {1 ,0 ,0 ,-2 ,0 }, {-1 ,0 ,2 ,0 ,2 }, {0 ,0 ,0 ,2 ,0 }, {1 ,0 ,0 ,0 ,1 }, {-1 ,0 ,0 ,0 ,1 }, {-1 ,0 ,2 ,2 ,2 }, {1 ,0 ,2 ,0 ,1 }, {0 ,0 ,2 ,2 ,2 }, {2 ,0 ,0 ,0 ,0 }, {1 ,0 ,2 ,-2 ,2 }, {2 ,0 ,2 ,0 ,2 }, {0 ,0 ,2 ,0 ,0 }, {-1 ,0 ,2 ,0 ,1 }, {-1 ,0 ,0 ,2 ,1 }, {1 ,0 ,0 ,-2 ,1 }, {-1 ,0 ,2 ,2 ,1 }, {1 ,1 ,0 ,-2 ,0 }, {0 ,1 ,2 ,0 ,2 }, {0 ,-1 ,2 ,0 ,2 }, {1 ,0 ,2 ,2 ,2 }, {1 ,0 ,0 ,2 ,0 }, {2 ,0 ,2 ,-2 ,2 }, {0 ,0 ,0 ,2 ,1 }, {0 ,0 ,2 ,2 ,1 }, {1 ,0 ,2 ,-2 ,1 }, {0 ,0 ,0 ,-2 ,1 }, {1 ,-1 ,0 ,0 ,0 }, {2 ,0 ,2 ,0 ,1 }, {0 ,1 ,0 ,-2 ,0 }, {1 ,0 ,-2 ,0 ,0 }, {0 ,0 ,0 ,1 ,0 }, {1 ,1 ,0 ,0 ,0 }, {1 ,0 ,2 ,0 ,0 }, {1 ,-1 ,2 ,0 ,2 }, {-1 ,-1 ,2 ,2 ,2 }, {-2 ,0 ,0 ,0 ,1 }, {3 ,0 ,2 ,0 ,2 }, {0 ,-1 ,2 ,2 ,2 }, {1 ,1 ,2 ,0 ,2 }, {-1 ,0 ,2 ,-2 ,1 }, {2 ,0 ,0 ,0 ,1 }, {1 ,0 ,0 ,0 ,2 }, {3 ,0 ,0 ,0 ,0 }, {0 ,0 ,2 ,1 ,2 }, {-1 ,0 ,0 ,0 ,2 }, {1 ,0 ,0 ,-4 ,0 }, {-2 ,0 ,2 ,2 ,2 }, {-1 ,0 ,2 ,4 ,2 }, {2 ,0 ,0 ,-4 ,0 }, {1 ,1 ,2 ,-2 ,2 }, {1 ,0 ,2 ,2 ,1 }, {-2 ,0 ,2 ,4 ,2 }, {-1 ,0 ,4 ,0 ,2 }, {1 ,-1 ,0 ,-2 ,0 }, {2 ,0 ,2 ,-2 ,1 }, {2 ,0 ,2 ,2 ,2 }, {1 ,0 ,0 ,2 ,1 }, {0 ,0 ,4 ,-2 ,2 }, {3 ,0 ,2 ,-2 ,2 }, {1 ,0 ,2 ,-2 ,0 }, {0 ,1 ,2 ,0 ,1 }, {-1 ,-1 ,0 ,2 ,1 }, {0 ,0 ,-2 ,0 ,1 }, {0 ,0 ,2 ,-1 ,2 }, {0 ,1 ,0 ,2 ,0 }, {1 ,0 ,-2 ,-2 ,0 }, {0 ,-1 ,2 ,0 ,1 }, {1 ,1 ,0 ,-2 ,1 }, {1 ,0 ,-2 ,2 ,0 }, {2 ,0 ,0 ,2 ,0 }, {0 ,0 ,2 ,4 ,2 }, {0 ,1 ,0 ,1 ,0 } }; DebugAssert(which < 106, AipsError); return &(ARG[which][0]); } const Double* MeasTable::mulArg2000A(uInt which) { static const Double ARG[678][5] = { // Multiple of // L L' F D Omega { 0, 0, 0, 0, 1}, // 1 { 0, 0, 2, -2, 2}, // 2 { 0, 0, 2, 0, 2}, // 3 { 0, 0, 0, 0, 2}, // 4 { 0, 1, 0, 0, 0}, // 5 { 0, 1, 2, -2, 2}, // 6 { 1, 0, 0, 0, 0}, // 7 { 0, 0, 2, 0, 1}, // 8 { 1, 0, 2, 0, 2}, // 9 { 0, -1, 2, -2, 2}, // 10 { 0, 0, 2, -2, 1}, // 11 { -1, 0, 2, 0, 2}, // 12 { -1, 0, 0, 2, 0}, // 13 { 1, 0, 0, 0, 1}, // 14 { -1, 0, 0, 0, 1}, // 15 { -1, 0, 2, 2, 2}, // 16 { 1, 0, 2, 0, 1}, // 17 { -2, 0, 2, 0, 1}, // 18 { 0, 0, 0, 2, 0}, // 19 { 0, 0, 2, 2, 2}, // 20 { 0, -2, 2, -2, 2}, // 21 { -2, 0, 0, 2, 0}, // 22 { 2, 0, 2, 0, 2}, // 23 { 1, 0, 2, -2, 2}, // 24 { -1, 0, 2, 0, 1}, // 25 { 2, 0, 0, 0, 0}, // 26 { 0, 0, 2, 0, 0}, // 27 { 0, 1, 0, 0, 1}, // 28 { -1, 0, 0, 2, 1}, // 29 { 0, 2, 2, -2, 2}, // 30 { 0, 0, -2, 2, 0}, // 31 { 1, 0, 0, -2, 1}, // 32 { 0, -1, 0, 0, 1}, // 33 { -1, 0, 2, 2, 1}, // 34 { 0, 2, 0, 0, 0}, // 35 { 1, 0, 2, 2, 2}, // 36 { -2, 0, 2, 0, 0}, // 37 { 0, 1, 2, 0, 2}, // 38 { 0, 0, 2, 2, 1}, // 39 { 0, -1, 2, 0, 2}, // 40 { 0, 0, 0, 2, 1}, // 41 { 1, 0, 2, -2, 1}, // 42 { 2, 0, 2, -2, 2}, // 43 { -2, 0, 0, 2, 1}, // 44 { 2, 0, 2, 0, 1}, // 45 { 0, -1, 2, -2, 1}, // 46 { 0, 0, 0, -2, 1}, // 47 { -1, -1, 0, 2, 0}, // 48 { 2, 0, 0, -2, 1}, // 49 { 1, 0, 0, 2, 0}, // 50 { 0, 1, 2, -2, 1}, // 51 { 1, -1, 0, 0, 0}, // 52 { -2, 0, 2, 0, 2}, // 53 { 3, 0, 2, 0, 2}, // 54 { 0, -1, 0, 2, 0}, // 55 { 1, -1, 2, 0, 2}, // 56 { 0, 0, 0, 1, 0}, // 57 { -1, -1, 2, 2, 2}, // 58 { -1, 0, 2, 0, 0}, // 59 { 0, -1, 2, 2, 2}, // 60 { -2, 0, 0, 0, 1}, // 61 { 1, 1, 2, 0, 2}, // 62 { 2, 0, 0, 0, 1}, // 63 { -1, 1, 0, 1, 0}, // 64 { 1, 1, 0, 0, 0}, // 65 { 1, 0, 2, 0, 0}, // 66 { -1, 0, 2, -2, 1}, // 67 { 1, 0, 0, 0, 2}, // 68 { -1, 0, 0, 1, 0}, // 69 { 0, 0, 2, 1, 2}, // 70 { -1, 0, 2, 4, 2}, // 71 { -1, 1, 0, 1, 1}, // 72 { 0, -2, 2, -2, 1}, // 73 { 1, 0, 2, 2, 1}, // 74 { -2, 0, 2, 2, 2}, // 75 { -1, 0, 0, 0, 2}, // 76 { 1, 1, 2, -2, 2}, // 77 { -2, 0, 2, 4, 2}, // 78 { -1, 0, 4, 0, 2}, // 79 { 2, 0, 2, -2, 1}, // 80 { 2, 0, 2, 2, 2}, // 81 { 1, 0, 0, 2, 1}, // 82 { 3, 0, 0, 0, 0}, // 83 { 3, 0, 2, -2, 2}, // 84 { 0, 0, 4, -2, 2}, // 85 { 0, 1, 2, 0, 1}, // 86 { 0, 0, -2, 2, 1}, // 87 { 0, 0, 2, -2, 3}, // 88 { -1, 0, 0, 4, 0}, // 89 { 2, 0, -2, 0, 1}, // 90 { -2, 0, 0, 4, 0}, // 91 { -1, -1, 0, 2, 1}, // 92 { -1, 0, 0, 1, 1}, // 93 { 0, 1, 0, 0, 2}, // 94 { 0, 0, -2, 0, 1}, // 95 { 0, -1, 2, 0, 1}, // 96 { 0, 0, 2, -1, 2}, // 97 { 0, 0, 2, 4, 2}, // 98 { -2, -1, 0, 2, 0}, // 99 { 1, 1, 0, -2, 1}, // 100 { -1, 1, 0, 2, 0}, // 101 { -1, 1, 0, 1, 2}, // 102 { 1, -1, 0, 0, 1}, // 103 { 1, -1, 2, 2, 2}, // 104 { -1, 1, 2, 2, 2}, // 105 { 3, 0, 2, 0, 1}, // 106 { 0, 1, -2, 2, 0}, // 107 { -1, 0, 0, -2, 1}, // 108 { 0, 1, 2, 2, 2}, // 109 { -1, -1, 2, 2, 1}, // 110 { 0, -1, 0, 0, 2}, // 111 { 1, 0, 2, -4, 1}, // 112 { -1, 0, -2, 2, 0}, // 113 { 0, -1, 2, 2, 1}, // 114 { 2, -1, 2, 0, 2}, // 115 { 0, 0, 0, 2, 2}, // 116 { 1, -1, 2, 0, 1}, // 117 { -1, 1, 2, 0, 2}, // 118 { 0, 1, 0, 2, 0}, // 119 { 0, -1, -2, 2, 0}, // 120 { 0, 3, 2, -2, 2}, // 121 { 0, 0, 0, 1, 1}, // 122 { -1, 0, 2, 2, 0}, // 123 { 2, 1, 2, 0, 2}, // 124 { 1, 1, 0, 0, 1}, // 125 { 1, 1, 2, 0, 1}, // 126 { 2, 0, 0, 2, 0}, // 127 { 1, 0, -2, 2, 0}, // 128 { -1, 0, 0, 2, 2}, // 129 { 0, 1, 0, 1, 0}, // 130 { 0, 1, 0, -2, 1}, // 131 { -1, 0, 2, -2, 2}, // 132 { 0, 0, 0, -1, 1}, // 133 { -1, 1, 0, 0, 1}, // 134 { 1, 0, 2, -1, 2}, // 135 { 1, -1, 0, 2, 0}, // 136 { 0, 0, 0, 4, 0}, // 137 { 1, 0, 2, 1, 2}, // 138 { 0, 0, 2, 1, 1}, // 139 { 1, 0, 0, -2, 2}, // 140 { -1, 0, 2, 4, 1}, // 141 { 1, 0, -2, 0, 1}, // 142 { 1, 1, 2, -2, 1}, // 143 { 0, 0, 2, 2, 0}, // 144 { -1, 0, 2, -1, 1}, // 145 { -2, 0, 2, 2, 1}, // 146 { 4, 0, 2, 0, 2}, // 147 { 2, -1, 0, 0, 0}, // 148 { 2, 1, 2, -2, 2}, // 149 { 0, 1, 2, 1, 2}, // 150 { 1, 0, 4, -2, 2}, // 151 { -1, -1, 0, 0, 1}, // 152 { 0, 1, 0, 2, 1}, // 153 { -2, 0, 2, 4, 1}, // 154 { 2, 0, 2, 0, 0}, // 155 { 1, 0, 0, 1, 0}, // 156 { -1, 0, 0, 4, 1}, // 157 { -1, 0, 4, 0, 1}, // 158 { 2, 0, 2, 2, 1}, // 159 { 0, 0, 2, -3, 2}, // 160 { -1, -2, 0, 2, 0}, // 161 { 2, 1, 0, 0, 0}, // 162 { 0, 0, 4, 0, 2}, // 163 { 0, 0, 0, 0, 3}, // 164 { 0, 3, 0, 0, 0}, // 165 { 0, 0, 2, -4, 1}, // 166 { 0, -1, 0, 2, 1}, // 167 { 0, 0, 0, 4, 1}, // 168 { -1, -1, 2, 4, 2}, // 169 { 1, 0, 2, 4, 2}, // 170 { -2, 2, 0, 2, 0}, // 171 { -2, -1, 2, 0, 1}, // 172 { -2, 0, 0, 2, 2}, // 173 { -1, -1, 2, 0, 2}, // 174 { 0, 0, 4, -2, 1}, // 175 { 3, 0, 2, -2, 1}, // 176 { -2, -1, 0, 2, 1}, // 177 { 1, 0, 0, -1, 1}, // 178 { 0, -2, 0, 2, 0}, // 179 { -2, 0, 0, 4, 1}, // 180 { -3, 0, 0, 0, 1}, // 181 { 1, 1, 2, 2, 2}, // 182 { 0, 0, 2, 4, 1}, // 183 { 3, 0, 2, 2, 2}, // 184 { -1, 1, 2, -2, 1}, // 185 { 2, 0, 0, -4, 1}, // 186 { 0, 0, 0, -2, 2}, // 187 { 2, 0, 2, -4, 1}, // 188 { -1, 1, 0, 2, 1}, // 189 { 0, 0, 2, -1, 1}, // 190 { 0, -2, 2, 2, 2}, // 191 { 2, 0, 0, 2, 1}, // 192 { 4, 0, 2, -2, 2}, // 193 { 2, 0, 0, -2, 2}, // 194 { 0, 2, 0, 0, 1}, // 195 { 1, 0, 0, -4, 1}, // 196 { 0, 2, 2, -2, 1}, // 197 { -3, 0, 0, 4, 0}, // 198 { -1, 1, 2, 0, 1}, // 199 { -1, -1, 0, 4, 0}, // 200 { -1, -2, 2, 2, 2}, // 201 { -2, -1, 2, 4, 2}, // 202 { 1, -1, 2, 2, 1}, // 203 { -2, 1, 0, 2, 0}, // 204 { -2, 1, 2, 0, 1}, // 205 { 2, 1, 0, -2, 1}, // 206 { -3, 0, 2, 0, 1}, // 207 { -2, 0, 2, -2, 1}, // 208 { -1, 1, 0, 2, 2}, // 209 { 0, -1, 2, -1, 2}, // 210 { -1, 0, 4, -2, 2}, // 211 { 0, -2, 2, 0, 2}, // 212 { -1, 0, 2, 1, 2}, // 213 { 2, 0, 0, 0, 2}, // 214 { 0, 0, 2, 0, 3}, // 215 { -2, 0, 4, 0, 2}, // 216 { -1, 0, -2, 0, 1}, // 217 { -1, 1, 2, 2, 1}, // 218 { 3, 0, 0, 0, 1}, // 219 { -1, 0, 2, 3, 2}, // 220 { 2, -1, 2, 0, 1}, // 221 { 0, 1, 2, 2, 1}, // 222 { 0, -1, 2, 4, 2}, // 223 { 2, -1, 2, 2, 2}, // 224 { 0, 2, -2, 2, 0}, // 225 { -1, -1, 2, -1, 1}, // 226 { 0, -2, 0, 0, 1}, // 227 { 1, 0, 2, -4, 2}, // 228 { 1, -1, 0, -2, 1}, // 229 { -1, -1, 2, 0, 1}, // 230 { 1, -1, 2, -2, 2}, // 231 { -2, -1, 0, 4, 0}, // 232 { -1, 0, 0, 3, 0}, // 233 { -2, -1, 2, 2, 2}, // 234 { 0, 2, 2, 0, 2}, // 235 { 1, 1, 0, 2, 0}, // 236 { 2, 0, 2, -1, 2}, // 237 { 1, 0, 2, 1, 1}, // 238 { 4, 0, 0, 0, 0}, // 239 { 2, 1, 2, 0, 1}, // 240 { 3, -1, 2, 0, 2}, // 241 { -2, 2, 0, 2, 1}, // 242 { 1, 0, 2, -3, 1}, // 243 { 1, 1, 2, -4, 1}, // 244 { -1, -1, 2, -2, 1}, // 245 { 0, -1, 0, -1, 1}, // 246 { 0, -1, 0, -2, 1}, // 247 { -2, 0, 0, 0, 2}, // 248 { -2, 0, -2, 2, 0}, // 249 { -1, 0, -2, 4, 0}, // 250 { 1, -2, 0, 0, 0}, // 251 { 0, 1, 0, 1, 1}, // 252 { -1, 2, 0, 2, 0}, // 253 { 1, -1, 2, -2, 1}, // 254 { 1, 2, 2, -2, 2}, // 255 { 2, -1, 2, -2, 2}, // 256 { 1, 0, 2, -1, 1}, // 257 { 2, 1, 2, -2, 1}, // 258 { -2, 0, 0, -2, 1}, // 259 { 1, -2, 2, 0, 2}, // 260 { 0, 1, 2, 1, 1}, // 261 { 1, 0, 4, -2, 1}, // 262 { -2, 0, 4, 2, 2}, // 263 { 1, 1, 2, 1, 2}, // 264 { 1, 0, 0, 4, 0}, // 265 { 1, 0, 2, 2, 0}, // 266 { 2, 0, 2, 1, 2}, // 267 { 3, 1, 2, 0, 2}, // 268 { 4, 0, 2, 0, 1}, // 269 { -2, -1, 2, 0, 0}, // 270 { 0, 1, -2, 2, 1}, // 271 { 1, 0, -2, 1, 0}, // 272 { 0, -1, -2, 2, 1}, // 273 { 2, -1, 0, -2, 1}, // 274 { -1, 0, 2, -1, 2}, // 275 { 1, 0, 2, -3, 2}, // 276 { 0, 1, 2, -2, 3}, // 277 { 0, 0, 2, -3, 1}, // 278 { -1, 0, -2, 2, 1}, // 279 { 0, 0, 2, -4, 2}, // 280 { -2, 1, 0, 0, 1}, // 281 { -1, 0, 0, -1, 1}, // 282 { 2, 0, 2, -4, 2}, // 283 { 0, 0, 4, -4, 4}, // 284 { 0, 0, 4, -4, 2}, // 285 { -1, -2, 0, 2, 1}, // 286 { -2, 0, 0, 3, 0}, // 287 { 1, 0, -2, 2, 1}, // 288 { -3, 0, 2, 2, 2}, // 289 { -3, 0, 2, 2, 1}, // 290 { -2, 0, 2, 2, 0}, // 291 { 2, -1, 0, 0, 1}, // 292 { -2, 1, 2, 2, 2}, // 293 { 1, 1, 0, 1, 0}, // 294 { 0, 1, 4, -2, 2}, // 295 { -1, 1, 0, -2, 1}, // 296 { 0, 0, 0, -4, 1}, // 297 { 1, -1, 0, 2, 1}, // 298 { 1, 1, 0, 2, 1}, // 299 { -1, 2, 2, 2, 2}, // 300 { 3, 1, 2, -2, 2}, // 301 { 0, -1, 0, 4, 0}, // 302 { 2, -1, 0, 2, 0}, // 303 { 0, 0, 4, 0, 1}, // 304 { 2, 0, 4, -2, 2}, // 305 { -1, -1, 2, 4, 1}, // 306 { 1, 0, 0, 4, 1}, // 307 { 1, -2, 2, 2, 2}, // 308 { 0, 0, 2, 3, 2}, // 309 { -1, 1, 2, 4, 2}, // 310 { 3, 0, 0, 2, 0}, // 311 { -1, 0, 4, 2, 2}, // 312 { 1, 1, 2, 2, 1}, // 313 { -2, 0, 2, 6, 2}, // 314 { 2, 1, 2, 2, 2}, // 315 { -1, 0, 2, 6, 2}, // 316 { 1, 0, 2, 4, 1}, // 317 { 2, 0, 2, 4, 2}, // 318 { 1, 1, -2, 1, 0}, // 319 { -3, 1, 2, 1, 2}, // 320 { 2, 0, -2, 0, 2}, // 321 { -1, 0, 0, 1, 2}, // 322 { -4, 0, 2, 2, 1}, // 323 { -1, -1, 0, 1, 0}, // 324 { 0, 0, -2, 2, 2}, // 325 { 1, 0, 0, -1, 2}, // 326 { 0, -1, 2, -2, 3}, // 327 { -2, 1, 2, 0, 0}, // 328 { 0, 0, 2, -2, 4}, // 329 { -2, -2, 0, 2, 0}, // 330 { -2, 0, -2, 4, 0}, // 331 { 0, -2, -2, 2, 0}, // 332 { 1, 2, 0, -2, 1}, // 333 { 3, 0, 0, -4, 1}, // 334 { -1, 1, 2, -2, 2}, // 335 { 1, -1, 2, -4, 1}, // 336 { 1, 1, 0, -2, 2}, // 337 { -3, 0, 2, 0, 0}, // 338 { -3, 0, 2, 0, 2}, // 339 { -2, 0, 0, 1, 0}, // 340 { 0, 0, -2, 1, 0}, // 341 { -3, 0, 0, 2, 1}, // 342 { -1, -1, -2, 2, 0}, // 343 { 0, 1, 2, -4, 1}, // 344 { 2, 1, 0, -4, 1}, // 345 { 0, 2, 0, -2, 1}, // 346 { 1, 0, 0, -3, 1}, // 347 { -2, 0, 2, -2, 2}, // 348 { -2, -1, 0, 0, 1}, // 349 { -4, 0, 0, 2, 0}, // 350 { 1, 1, 0, -4, 1}, // 351 { -1, 0, 2, -4, 1}, // 352 { 0, 0, 4, -4, 1}, // 353 { 0, 3, 2, -2, 2}, // 354 { -3, -1, 0, 4, 0}, // 355 { -3, 0, 0, 4, 1}, // 356 { 1, -1, -2, 2, 0}, // 357 { -1, -1, 0, 2, 2}, // 358 { 1, -2, 0, 0, 1}, // 359 { 1, -1, 0, 0, 2}, // 360 { 0, 0, 0, 1, 2}, // 361 { -1, -1, 2, 0, 0}, // 362 { 1, -2, 2, -2, 2}, // 363 { 0, -1, 2, -1, 1}, // 364 { -1, 0, 2, 0, 3}, // 365 { 1, 1, 0, 0, 2}, // 366 { -1, 1, 2, 0, 0}, // 367 { 1, 2, 0, 0, 0}, // 368 { -1, 2, 2, 0, 2}, // 369 { -1, 0, 4, -2, 1}, // 370 { 3, 0, 2, -4, 2}, // 371 { 1, 2, 2, -2, 1}, // 372 { 1, 0, 4, -4, 2}, // 373 { -2, -1, 0, 4, 1}, // 374 { 0, -1, 0, 2, 2}, // 375 { -2, 1, 0, 4, 0}, // 376 { -2, -1, 2, 2, 1}, // 377 { 2, 0, -2, 2, 0}, // 378 { 1, 0, 0, 1, 1}, // 379 { 0, 1, 0, 2, 2}, // 380 { 1, -1, 2, -1, 2}, // 381 { -2, 0, 4, 0, 1}, // 382 { 2, 1, 0, 0, 1}, // 383 { 0, 1, 2, 0, 0}, // 384 { 0, -1, 4, -2, 2}, // 385 { 0, 0, 4, -2, 4}, // 386 { 0, 2, 2, 0, 1}, // 387 { -3, 0, 0, 6, 0}, // 388 { -1, -1, 0, 4, 1}, // 389 { 1, -2, 0, 2, 0}, // 390 { -1, 0, 0, 4, 2}, // 391 { -1, -2, 2, 2, 1}, // 392 { -1, 0, 0, -2, 2}, // 393 { 1, 0, -2, -2, 1}, // 394 { 0, 0, -2, -2, 1}, // 395 { -2, 0, -2, 0, 1}, // 396 { 0, 0, 0, 3, 1}, // 397 { 0, 0, 0, 3, 0}, // 398 { -1, 1, 0, 4, 0}, // 399 { -1, -1, 2, 2, 0}, // 400 { -2, 0, 2, 3, 2}, // 401 { 1, 0, 0, 2, 2}, // 402 { 0, -1, 2, 1, 2}, // 403 { 3, -1, 0, 0, 0}, // 404 { 2, 0, 0, 1, 0}, // 405 { 1, -1, 2, 0, 0}, // 406 { 0, 0, 2, 1, 0}, // 407 { 1, 0, 2, 0, 3}, // 408 { 3, 1, 0, 0, 0}, // 409 { 3, -1, 2, -2, 2}, // 410 { 2, 0, 2, -1, 1}, // 411 { 1, 1, 2, 0, 0}, // 412 { 0, 0, 4, -1, 2}, // 413 { 1, 2, 2, 0, 2}, // 414 { -2, 0, 0, 6, 0}, // 415 { 0, -1, 0, 4, 1}, // 416 { -2, -1, 2, 4, 1}, // 417 { 0, -2, 2, 2, 1}, // 418 { 0, -1, 2, 2, 0}, // 419 { -1, 0, 2, 3, 1}, // 420 { -2, 1, 2, 4, 2}, // 421 { 2, 0, 0, 2, 2}, // 422 { 2, -2, 2, 0, 2}, // 423 { -1, 1, 2, 3, 2}, // 424 { 3, 0, 2, -1, 2}, // 425 { 4, 0, 2, -2, 1}, // 426 { -1, 0, 0, 6, 0}, // 427 { -1, -2, 2, 4, 2}, // 428 { -3, 0, 2, 6, 2}, // 429 { -1, 0, 2, 4, 0}, // 430 { 3, 0, 0, 2, 1}, // 431 { 3, -1, 2, 0, 1}, // 432 { 3, 0, 2, 0, 0}, // 433 { 1, 0, 4, 0, 2}, // 434 { 5, 0, 2, -2, 2}, // 435 { 0, -1, 2, 4, 1}, // 436 { 2, -1, 2, 2, 1}, // 437 { 0, 1, 2, 4, 2}, // 438 { 1, -1, 2, 4, 2}, // 439 { 3, -1, 2, 2, 2}, // 440 { 3, 0, 2, 2, 1}, // 441 { 5, 0, 2, 0, 2}, // 442 { 0, 0, 2, 6, 2}, // 443 { 4, 0, 2, 2, 2}, // 444 { 0, -1, 1, -1, 1}, // 445 { -1, 0, 1, 0, 3}, // 446 { 0, -2, 2, -2, 3}, // 447 { 1, 0, -1, 0, 1}, // 448 { 2, -2, 0, -2, 1}, // 449 { -1, 0, 1, 0, 2}, // 450 { -1, 0, 1, 0, 1}, // 451 { -1, -1, 2, -1, 2}, // 452 { -2, 2, 0, 2, 2}, // 453 { -1, 0, 1, 0, 0}, // 454 { -4, 1, 2, 2, 2}, // 455 { -3, 0, 2, 1, 1}, // 456 { -2, -1, 2, 0, 2}, // 457 { 1, 0, -2, 1, 1}, // 458 { 2, -1, -2, 0, 1}, // 459 { -4, 0, 2, 2, 0}, // 460 { -3, 1, 0, 3, 0}, // 461 { -1, 0, -1, 2, 0}, // 462 { 0, -2, 0, 0, 2}, // 463 { 0, -2, 0, 0, 2}, // 464 { -3, 0, 0, 3, 0}, // 465 { -2, -1, 0, 2, 2}, // 466 { -1, 0, -2, 3, 0}, // 467 { -4, 0, 0, 4, 0}, // 468 { 2, 1, -2, 0, 1}, // 469 { 2, -1, 0, -2, 2}, // 470 { 0, 0, 1, -1, 0}, // 471 { -1, 2, 0, 1, 0}, // 472 { -2, 1, 2, 0, 2}, // 473 { 1, 1, 0, -1, 1}, // 474 { 1, 0, 1, -2, 1}, // 475 { 0, 2, 0, 0, 2}, // 476 { 1, -1, 2, -3, 1}, // 477 { -1, 1, 2, -1, 1}, // 478 { -2, 0, 4, -2, 2}, // 479 { -2, 0, 4, -2, 1}, // 480 { -2, -2, 0, 2, 1}, // 481 { -2, 0, -2, 4, 0}, // 482 { 1, 2, 2, -4, 1}, // 483 { 1, 1, 2, -4, 2}, // 484 { -1, 2, 2, -2, 1}, // 485 { 2, 0, 0, -3, 1}, // 486 { -1, 2, 0, 0, 1}, // 487 { 0, 0, 0, -2, 0}, // 488 { -1, -1, 2, -2, 2}, // 489 { -1, 1, 0, 0, 2}, // 490 { 0, 0, 0, -1, 2}, // 491 { -2, 1, 0, 1, 0}, // 492 { 1, -2, 0, -2, 1}, // 493 { 1, 0, -2, 0, 2}, // 494 { -3, 1, 0, 2, 0}, // 495 { -1, 1, -2, 2, 0}, // 496 { -1, -1, 0, 0, 2}, // 497 { -3, 0, 0, 2, 0}, // 498 { -3, -1, 0, 2, 0}, // 499 { 2, 0, 2, -6, 1}, // 500 { 0, 1, 2, -4, 2}, // 501 { 2, 0, 0, -4, 2}, // 502 { -2, 1, 2, -2, 1}, // 503 { 0, -1, 2, -4, 1}, // 504 { 0, 1, 0, -2, 2}, // 505 { -1, 0, 0, -2, 0}, // 506 { 2, 0, -2, -2, 1}, // 507 { -4, 0, 2, 0, 1}, // 508 { -1, -1, 0, -1, 1}, // 509 { 0, 0, -2, 0, 2}, // 510 { -3, 0, 0, 1, 0}, // 511 { -1, 0, -2, 1, 0}, // 512 { -2, 0, -2, 2, 1}, // 513 { 0, 0, -4, 2, 0}, // 514 { -2, -1, -2, 2, 0}, // 515 { 1, 0, 2, -6, 1}, // 516 { -1, 0, 2, -4, 2}, // 517 { 1, 0, 0, -4, 2}, // 518 { 2, 1, 2, -4, 2}, // 519 { 2, 1, 2, -4, 1}, // 520 { 0, 1, 4, -4, 4}, // 521 { 0, 1, 4, -4, 2}, // 522 { -1, -1, -2, 4, 0}, // 523 { -1, -3, 0, 2, 0}, // 524 { -1, 0, -2, 4, 1}, // 525 { -2, -1, 0, 3, 0}, // 526 { 0, 0, -2, 3, 0}, // 527 { -2, 0, 0, 3, 1}, // 528 { 0, -1, 0, 1, 0}, // 529 { -3, 0, 2, 2, 0}, // 530 { 1, 1, -2, 2, 0}, // 531 { -1, 1, 0, 2, 2}, // 532 { 1, -2, 2, -2, 1}, // 533 { 0, 0, 1, 0, 2}, // 534 { 0, 0, 1, 0, 1}, // 535 { 0, 0, 1, 0, 0}, // 536 { -1, 2, 0, 2, 1}, // 537 { 0, 0, 2, 0, 2}, // 538 { -2, 0, 2, 0, 2}, // 539 { 2, 0, 0, -1, 1}, // 540 { 3, 0, 0, -2, 1}, // 541 { 1, 0, 2, -2, 3}, // 542 { 1, 2, 0, 0, 1}, // 543 { 2, 0, 2, -3, 2}, // 544 { -1, 1, 4, -2, 2}, // 545 { -2, -2, 0, 4, 0}, // 546 { 0, -3, 0, 2, 0}, // 547 { 0, 0, -2, 4, 0}, // 548 { -1, -1, 0, 3, 0}, // 549 { -2, 0, 0, 4, 2}, // 550 { -1, 0, 0, 3, 1}, // 551 { 2, -2, 0, 0, 0}, // 552 { 1, -1, 0, 1, 0}, // 553 { -1, 0, 0, 2, 0}, // 554 { 0, -2, 2, 0, 1}, // 555 { -1, 0, 1, 2, 1}, // 556 { -1, 1, 0, 3, 0}, // 557 { -1, -1, 2, 1, 2}, // 558 { 0, -1, 2, 0, 0}, // 559 { -2, 1, 2, 2, 1}, // 560 { 2, -2, 2, -2, 2}, // 561 { 1, 1, 0, 1, 1}, // 562 { 1, 0, 1, 0, 1}, // 563 { 1, 0, 1, 0, 0}, // 564 { 0, 2, 0, 2, 0}, // 565 { 2, -1, 2, -2, 1}, // 566 { 0, -1, 4, -2, 1}, // 567 { 0, 0, 4, -2, 3}, // 568 { 0, 1, 4, -2, 1}, // 569 { 4, 0, 2, -4, 2}, // 570 { 2, 2, 2, -2, 2}, // 571 { 2, 0, 4, -4, 2}, // 572 { -1, -2, 0, 4, 0}, // 573 { -1, -3, 2, 2, 2}, // 574 { -3, 0, 2, 4, 2}, // 575 { -3, 0, 2, -2, 1}, // 576 { -1, -1, 0, -2, 1}, // 577 { -3, 0, 0, 0, 2}, // 578 { -3, 0, -2, 2, 0}, // 579 { 0, 1, 0, -4, 1}, // 580 { -2, 1, 0, -2, 1}, // 581 { -4, 0, 0, 0, 1}, // 582 { -1, 0, 0, -4, 1}, // 583 { -3, 0, 0, -2, 1}, // 584 { 0, 0, 0, 3, 2}, // 585 { -1, 1, 0, 4, 1}, // 586 { 1, -2, 2, 0, 1}, // 587 { 0, 1, 0, 3, 0}, // 588 { -1, 0, 2, 2, 3}, // 589 { 0, 0, 2, 2, 2}, // 590 { -2, 0, 2, 2, 2}, // 591 { -1, 1, 2, 2, 0}, // 592 { 3, 0, 0, 0, 2}, // 593 { 2, 1, 0, 1, 0}, // 594 { 2, -1, 2, -1, 2}, // 595 { 0, 0, 2, 0, 1}, // 596 { 0, 0, 3, 0, 3}, // 597 { 0, 0, 3, 0, 2}, // 598 { -1, 2, 2, 2, 1}, // 599 { -1, 0, 4, 0, 0}, // 600 { 1, 2, 2, 0, 1}, // 601 { 3, 1, 2, -2, 1}, // 602 { 1, 1, 4, -2, 2}, // 603 { -2, -1, 0, 6, 0}, // 604 { 0, -2, 0, 4, 0}, // 605 { -2, 0, 0, 6, 1}, // 606 { -2, -2, 2, 4, 2}, // 607 { 0, -3, 2, 2, 2}, // 608 { 0, 0, 0, 4, 2}, // 609 { -1, -1, 2, 3, 2}, // 610 { -2, 0, 2, 4, 0}, // 611 { 2, -1, 0, 2, 1}, // 612 { 1, 0, 0, 3, 0}, // 613 { 0, 1, 0, 4, 1}, // 614 { 0, 1, 0, 4, 0}, // 615 { 1, -1, 2, 1, 2}, // 616 { 0, 0, 2, 2, 3}, // 617 { 1, 0, 2, 2, 2}, // 618 { -1, 0, 2, 2, 2}, // 619 { -2, 0, 4, 2, 1}, // 620 { 2, 1, 0, 2, 1}, // 621 { 2, 1, 0, 2, 0}, // 622 { 2, -1, 2, 0, 0}, // 623 { 1, 0, 2, 1, 0}, // 624 { 0, 1, 2, 2, 0}, // 625 { 2, 0, 2, 0, 3}, // 626 { 3, 0, 2, 0, 2}, // 627 { 1, 0, 2, 0, 2}, // 628 { 1, 0, 3, 0, 3}, // 629 { 1, 1, 2, 1, 1}, // 630 { 0, 2, 2, 2, 2}, // 631 { 2, 1, 2, 0, 0}, // 632 { 2, 0, 4, -2, 1}, // 633 { 4, 1, 2, -2, 2}, // 634 { -1, -1, 0, 6, 0}, // 635 { -3, -1, 2, 6, 2}, // 636 { -1, 0, 0, 6, 1}, // 637 { -3, 0, 2, 6, 1}, // 638 { 1, -1, 0, 4, 1}, // 639 { 1, -1, 0, 4, 0}, // 640 { -2, 0, 2, 5, 2}, // 641 { 1, -2, 2, 2, 1}, // 642 { 3, -1, 0, 2, 0}, // 643 { 1, -1, 2, 2, 0}, // 644 { 0, 0, 2, 3, 1}, // 645 { -1, 1, 2, 4, 1}, // 646 { 0, 1, 2, 3, 2}, // 647 { -1, 0, 4, 2, 1}, // 648 { 2, 0, 2, 1, 1}, // 649 { 5, 0, 0, 0, 0}, // 650 { 2, 1, 2, 1, 2}, // 651 { 1, 0, 4, 0, 1}, // 652 { 3, 1, 2, 0, 1}, // 653 { 3, 0, 4, -2, 2}, // 654 { -2, -1, 2, 6, 2}, // 655 { 0, 0, 0, 6, 0}, // 656 { 0, -2, 2, 4, 2}, // 657 { -2, 0, 2, 6, 1}, // 658 { 2, 0, 0, 4, 1}, // 659 { 2, 0, 0, 4, 0}, // 660 { 2, -2, 2, 2, 2}, // 661 { 0, 0, 2, 4, 0}, // 662 { 1, 0, 2, 3, 2}, // 663 { 4, 0, 0, 2, 0}, // 664 { 2, 0, 2, 2, 0}, // 665 { 0, 0, 4, 2, 2}, // 666 { 4, -1, 2, 0, 2}, // 667 { 3, 0, 2, 1, 2}, // 668 { 2, 1, 2, 2, 1}, // 669 { 4, 1, 2, 0, 2}, // 670 { -1, -1, 2, 6, 2}, // 671 { -1, 0, 2, 6, 1}, // 672 { 1, -1, 2, 4, 1}, // 673 { 1, 1, 2, 4, 2}, // 674 { 3, 1, 2, 2, 2}, // 675 { 5, 0, 2, 0, 1}, // 676 { 2, -1, 2, 4, 2}, // 677 { 2, 0, 2, 4, 1} // 678 }; DebugAssert(which < 678, AipsError); return &(ARG[which][0]); } const Double* MeasTable::mulPlanArg2000A(uInt which) { static const Double ARG[687][14] = { // L L' F D Om Me Ve E Ma Ju Sa Ur Ne pre { 0, 0, 0, 0, 0, 0, 0, 8,-16, 4, 5, 0, 0, 0}, // 1 { 0, 0, 0, 0, 0, 0, 0, -8, 16, -4, -5, 0, 0, 2}, // 2 { 0, 0, 0, 0, 0, 0, 0, 8,-16, 4, 5, 0, 0, 2}, // 3 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 2, 2}, // 4 { 0, 0, 0, 0, 0, 0, 0, -4, 8, -1, -5, 0, 0, 2}, // 5 { 0, 0, 0, 0, 0, 0, 0, 4, -8, 3, 0, 0, 0, 1}, // 6 { 0, 0, 1, -1, 1, 0, 0, 3, -8, 3, 0, 0, 0, 0}, // 7 { -1, 0, 0, 0, 0, 0, 10, -3, 0, 0, 0, 0, 0, 0}, // 8 { 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 6, -3, 0, 2}, // 9 { 0, 0, 0, 0, 0, 0, 0, 4, -8, 3, 0, 0, 0, 0}, // 10 { 0, 0, 1, -1, 1, 0, 0, -5, 8, -3, 0, 0, 0, 0}, // 11 { 0, 0, 0, 0, 0, 0, 0, -4, 8, -3, 0, 0, 0, 1}, // 12 { 0, 0, 0, 0, 0, 0, 0, 4, -8, 1, 5, 0, 0, 2}, // 13 { 0, 0, 0, 0, 0, 0, -5, 6, 4, 0, 0, 0, 0, 2}, // 14 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -5, 0, 0, 2}, // 15 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -5, 0, 0, 1}, // 16 { 0, 0, 1, -1, 1, 0, 0, -1, 0, 2, -5, 0, 0, 0}, // 17 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -5, 0, 0, 0}, // 18 { 0, 0, 1, -1, 1, 0, 0, -1, 0, -2, 5, 0, 0, 0}, // 19 { 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 5, 0, 0, 1}, // 20 { 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 5, 0, 0, 2}, // 21 { 2, 0, -1, -1, 0, 0, 0, 3, -7, 0, 0, 0, 0, 0}, // 22 { 1, 0, 0, -2, 0, 0, 19,-21, 3, 0, 0, 0, 0, 0}, // 23 { 0, 0, 1, -1, 1, 0, 2, -4, 0, -3, 0, 0, 0, 0}, // 24 { 1, 0, 0, -1, 1, 0, 0, -1, 0, 2, 0, 0, 0, 0}, // 25 { 0, 0, 1, -1, 1, 0, 0, -1, 0, -4, 10, 0, 0, 0}, // 26 { -2, 0, 0, 2, 1, 0, 0, 2, 0, 0, -5, 0, 0, 0}, // 27 { 0, 0, 0, 0, 0, 0, 3, -7, 4, 0, 0, 0, 0, 0}, // 28 { 0, 0, -1, 1, 0, 0, 0, 1, 0, 1, -1, 0, 0, 0}, // 29 { -2, 0, 0, 2, 1, 0, 0, 2, 0, -2, 0, 0, 0, 0}, // 30 { -1, 0, 0, 0, 0, 0, 18,-16, 0, 0, 0, 0, 0, 0}, // 31 { -2, 0, 1, 1, 2, 0, 0, 1, 0, -2, 0, 0, 0, 0}, // 32 { -1, 0, 1, -1, 1, 0, 18,-17, 0, 0, 0, 0, 0, 0}, // 33 { -1, 0, 0, 1, 1, 0, 0, 2, -2, 0, 0, 0, 0, 0}, // 34 { 0, 0, 0, 0, 0, 0, -8, 13, 0, 0, 0, 0, 0, 2}, // 35 { 0, 0, 2, -2, 2, 0, -8, 11, 0, 0, 0, 0, 0, 0}, // 36 { 0, 0, 0, 0, 0, 0, -8, 13, 0, 0, 0, 0, 0, 1}, // 37 { 0, 0, 1, -1, 1, 0, -8, 12, 0, 0, 0, 0, 0, 0}, // 38 { 0, 0, 0, 0, 0, 0, 8,-13, 0, 0, 0, 0, 0, 0}, // 39 { 0, 0, 1, -1, 1, 0, 8,-14, 0, 0, 0, 0, 0, 0}, // 40 { 0, 0, 0, 0, 0, 0, 8,-13, 0, 0, 0, 0, 0, 1}, // 41 { -2, 0, 0, 2, 1, 0, 0, 2, 0, -4, 5, 0, 0, 0}, // 42 { -2, 0, 0, 2, 2, 0, 3, -3, 0, 0, 0, 0, 0, 0}, // 43 { -2, 0, 0, 2, 0, 0, 0, 2, 0, -3, 1, 0, 0, 0}, // 44 { 0, 0, 0, 0, 1, 0, 3, -5, 0, 2, 0, 0, 0, 0}, // 45 { -2, 0, 0, 2, 0, 0, 0, 2, 0, -4, 3, 0, 0, 0}, // 46 { 0, 0, -1, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0}, // 47 { 0, 0, 0, 0, 1, 0, 0, -1, 2, 0, 0, 0, 0, 0}, // 48 { 0, 0, 1, -1, 2, 0, 0, -2, 2, 0, 0, 0, 0, 0}, // 49 { -1, 0, 1, 0, 1, 0, 3, -5, 0, 0, 0, 0, 0, 0}, // 50 { -1, 0, 0, 1, 0, 0, 3, -4, 0, 0, 0, 0, 0, 0}, // 51 { -2, 0, 0, 2, 0, 0, 0, 2, 0, -2, -2, 0, 0, 0}, // 52 { -2, 0, 2, 0, 2, 0, 0, -5, 9, 0, 0, 0, 0, 0}, // 53 { 0, 0, 1, -1, 1, 0, 0, -1, 0, 0, 0, -1, 0, 0}, // 54 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, // 55 { 0, 0, 1, -1, 1, 0, 0, -1, 0, 0, 0, 0, 2, 0}, // 56 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1}, // 57 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2}, // 58 { -1, 0, 0, 1, 0, 0, 0, 3, -4, 0, 0, 0, 0, 0}, // 59 { 0, 0, -1, 1, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0}, // 60 { 0, 0, 1, -1, 2, 0, 0, -1, 0, 0, 2, 0, 0, 0}, // 61 { 0, 0, 0, 0, 1, 0, 0, -9, 17, 0, 0, 0, 0, 0}, // 62 { 0, 0, 0, 0, 2, 0, -3, 5, 0, 0, 0, 0, 0, 0}, // 63 { 0, 0, 1, -1, 1, 0, 0, -1, 0, -1, 2, 0, 0, 0}, // 64 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -2, 0, 0, 0}, // 65 { 1, 0, 0, -2, 0, 0, 17,-16, 0, -2, 0, 0, 0, 0}, // 66 { 0, 0, 1, -1, 1, 0, 0, -1, 0, 1, -3, 0, 0, 0}, // 67 { -2, 0, 0, 2, 1, 0, 0, 5, -6, 0, 0, 0, 0, 0}, // 68 { 0, 0, -2, 2, 0, 0, 0, 9,-13, 0, 0, 0, 0, 0}, // 69 { 0, 0, 1, -1, 2, 0, 0, -1, 0, 0, 1, 0, 0, 0}, // 70 { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0}, // 71 { 0, 0, -1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0}, // 72 { 0, 0, -2, 2, 0, 0, 5, -6, 0, 0, 0, 0, 0, 0}, // 73 { 0, 0, -1, 1, 1, 0, 5, -7, 0, 0, 0, 0, 0, 0}, // 74 { -2, 0, 0, 2, 0, 0, 6, -8, 0, 0, 0, 0, 0, 0}, // 75 { 2, 0, 1, -3, 1, 0, -6, 7, 0, 0, 0, 0, 0, 0}, // 76 { 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0}, // 77 { 0, 0, -1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0}, // 78 { 0, 0, 1, -1, 1, 0, 0, -1, 0, 0, 0, 2, 0, 0}, // 79 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 1}, // 80 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2}, // 81 { 0, 0, 0, 0, 0, 0, 0, -8, 15, 0, 0, 0, 0, 2}, // 82 { 0, 0, 0, 0, 0, 0, 0, -8, 15, 0, 0, 0, 0, 1}, // 83 { 0, 0, 1, -1, 1, 0, 0, -9, 15, 0, 0, 0, 0, 0}, // 84 { 0, 0, 0, 0, 0, 0, 0, 8,-15, 0, 0, 0, 0, 0}, // 85 { 1, 0, -1, -1, 0, 0, 0, 8,-15, 0, 0, 0, 0, 0}, // 86 { 2, 0, 0, -2, 0, 0, 2, -5, 0, 0, 0, 0, 0, 0}, // 87 { -2, 0, 0, 2, 0, 0, 0, 2, 0, -5, 5, 0, 0, 0}, // 88 { 2, 0, 0, -2, 1, 0, 0, -6, 8, 0, 0, 0, 0, 0}, // 89 { 2, 0, 0, -2, 1, 0, 0, -2, 0, 3, 0, 0, 0, 0}, // 90 { -2, 0, 1, 1, 0, 0, 0, 1, 0, -3, 0, 0, 0, 0}, // 91 { -2, 0, 1, 1, 1, 0, 0, 1, 0, -3, 0, 0, 0, 0}, // 92 { -2, 0, 0, 2, 0, 0, 0, 2, 0, -3, 0, 0, 0, 0}, // 93 { -2, 0, 0, 2, 0, 0, 0, 6, -8, 0, 0, 0, 0, 0}, // 94 { -2, 0, 0, 2, 0, 0, 0, 2, 0, -1, -5, 0, 0, 0}, // 95 { -1, 0, 0, 1, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0}, // 96 { -1, 0, 1, 1, 1, 0,-20, 20, 0, 0, 0, 0, 0, 0}, // 97 { 1, 0, 0, -2, 0, 0, 20,-21, 0, 0, 0, 0, 0, 0}, // 98 { 0, 0, 0, 0, 1, 0, 0, 8,-15, 0, 0, 0, 0, 0}, // 99 { 0, 0, 2, -2, 1, 0, 0,-10, 15, 0, 0, 0, 0, 0}, // 100 { 0, 0, -1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0}, // 101 { 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0}, // 102 { 0, 0, 1, -1, 2, 0, 0, -1, 0, 1, 0, 0, 0, 0}, // 103 { 0, 0, 1, -1, 1, 0, 0, -1, 0, -2, 4, 0, 0, 0}, // 104 { 2, 0, 0, -2, 1, 0, -6, 8, 0, 0, 0, 0, 0, 0}, // 105 { 0, 0, -2, 2, 1, 0, 5, -6, 0, 0, 0, 0, 0, 0}, // 106 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 1}, // 107 { 0, 0, 1, -1, 1, 0, 0, -1, 0, 0, -1, 0, 0, 0}, // 108 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, // 109 { 0, 0, 1, -1, 1, 0, 0, -1, 0, 0, 1, 0, 0, 0}, // 110 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1}, // 111 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2}, // 112 { 0, 0, 2, -2, 1, 0, 0, -9, 13, 0, 0, 0, 0, 0}, // 113 { 0, 0, 0, 0, 1, 0, 0, 7,-13, 0, 0, 0, 0, 0}, // 114 { -2, 0, 0, 2, 0, 0, 0, 5, -6, 0, 0, 0, 0, 0}, // 115 { 0, 0, 0, 0, 0, 0, 0, 9,-17, 0, 0, 0, 0, 0}, // 116 { 0, 0, 0, 0, 0, 0, 0, -9, 17, 0, 0, 0, 0, 2}, // 117 { 1, 0, 0, -1, 1, 0, 0, -3, 4, 0, 0, 0, 0, 0}, // 118 { 1, 0, 0, -1, 1, 0, -3, 4, 0, 0, 0, 0, 0, 0}, // 119 { 0, 0, 0, 0, 2, 0, 0, -1, 2, 0, 0, 0, 0, 0}, // 120 { 0, 0, -1, 1, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0}, // 121 { 0, 0, -2, 2, 0, 1, 0, -2, 0, 0, 0, 0, 0, 0}, // 122 { 0, 0, 0, 0, 0, 0, 3, -5, 0, 2, 0, 0, 0, 0}, // 123 { -2, 0, 0, 2, 1, 0, 0, 2, 0, -3, 1, 0, 0, 0}, // 124 { -2, 0, 0, 2, 1, 0, 3, -3, 0, 0, 0, 0, 0, 0}, // 125 { 0, 0, 0, 0, 1, 0, 8,-13, 0, 0, 0, 0, 0, 0}, // 126 { 0, 0, -1, 1, 0, 0, 8,-12, 0, 0, 0, 0, 0, 0}, // 127 { 0, 0, 2, -2, 1, 0, -8, 11, 0, 0, 0, 0, 0, 0}, // 128 { -1, 0, 0, 1, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0}, // 129 { -1, 0, 0, 0, 1, 0, 18,-16, 0, 0, 0, 0, 0, 0}, // 130 { 0, 0, 1, -1, 1, 0, 0, -1, 0, -1, 1, 0, 0, 0}, // 131 { 0, 0, 0, 0, 1, 0, 3, -7, 4, 0, 0, 0, 0, 0}, // 132 { -2, 0, 1, 1, 1, 0, 0, -3, 7, 0, 0, 0, 0, 0}, // 133 { 0, 0, 1, -1, 2, 0, 0, -1, 0, -2, 5, 0, 0, 0}, // 134 { 0, 0, 0, 0, 1, 0, 0, 0, 0, -2, 5, 0, 0, 0}, // 135 { 0, 0, 0, 0, 1, 0, 0, -4, 8, -3, 0, 0, 0, 0}, // 136 { 1, 0, 0, 0, 1, 0,-10, 3, 0, 0, 0, 0, 0, 0}, // 137 { 0, 0, 2, -2, 1, 0, 0, -2, 0, 0, 0, 0, 0, 0}, // 138 { -1, 0, 0, 0, 1, 0, 10, -3, 0, 0, 0, 0, 0, 0}, // 139 { 0, 0, 0, 0, 1, 0, 0, 4, -8, 3, 0, 0, 0, 0}, // 140 { 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, -5, 0, 0, 0}, // 141 { 0, 0, -1, 1, 0, 0, 0, 1, 0, 2, -5, 0, 0, 0}, // 142 { 2, 0, -1, -1, 1, 0, 0, 3, -7, 0, 0, 0, 0, 0}, // 143 { -2, 0, 0, 2, 0, 0, 0, 2, 0, 0, -5, 0, 0, 0}, // 144 { 0, 0, 0, 0, 1, 0, -3, 7, -4, 0, 0, 0, 0, 0}, // 145 { -2, 0, 0, 2, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0}, // 146 { 1, 0, 0, 0, 1, 0,-18, 16, 0, 0, 0, 0, 0, 0}, // 147 { -2, 0, 1, 1, 1, 0, 0, 1, 0, -2, 0, 0, 0, 0}, // 148 { 0, 0, 1, -1, 2, 0, -8, 12, 0, 0, 0, 0, 0, 0}, // 149 { 0, 0, 0, 0, 1, 0, -8, 13, 0, 0, 0, 0, 0, 0}, // 150 { 0, 0, 0, 0, 0, 0, 0, 1, -2, 0, 0, 0, 0, 1}, // 151 { 0, 0, 1, -1, 1, 0, 0, 0, -2, 0, 0, 0, 0, 0}, // 152 { 0, 0, 0, 0, 0, 0, 0, 1, -2, 0, 0, 0, 0, 0}, // 153 { 0, 0, 1, -1, 1, 0, 0, -2, 2, 0, 0, 0, 0, 0}, // 154 { 0, 0, 0, 0, 0, 0, 0, -1, 2, 0, 0, 0, 0, 1}, // 155 { -1, 0, 0, 1, 1, 0, 3, -4, 0, 0, 0, 0, 0, 0}, // 156 { -1, 0, 0, 1, 1, 0, 0, 3, -4, 0, 0, 0, 0, 0}, // 157 { 0, 0, 1, -1, 1, 0, 0, -1, 0, 0, -2, 0, 0, 0}, // 158 { 0, 0, 1, -1, 1, 0, 0, -1, 0, 0, 2, 0, 0, 0}, // 159 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 1}, // 160 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2}, // 161 { 0, 0, 1, -1, 0, 0, 3, -6, 0, 0, 0, 0, 0, 0}, // 162 { 0, 0, 0, 0, 1, 0, -3, 5, 0, 0, 0, 0, 0, 0}, // 163 { 0, 0, 1, -1, 2, 0, -3, 4, 0, 0, 0, 0, 0, 0}, // 164 { 0, 0, 0, 0, 1, 0, 0, -2, 4, 0, 0, 0, 0, 0}, // 165 { 0, 0, 2, -2, 1, 0, -5, 6, 0, 0, 0, 0, 0, 0}, // 166 { 0, 0, -1, 1, 0, 0, 5, -7, 0, 0, 0, 0, 0, 0}, // 167 { 0, 0, 0, 0, 1, 0, 5, -8, 0, 0, 0, 0, 0, 0}, // 168 { -2, 0, 0, 2, 1, 0, 6, -8, 0, 0, 0, 0, 0, 0}, // 169 { 0, 0, 0, 0, 1, 0, 0, -8, 15, 0, 0, 0, 0, 0}, // 170 { -2, 0, 0, 2, 1, 0, 0, 2, 0, -3, 0, 0, 0, 0}, // 171 { -2, 0, 0, 2, 1, 0, 0, 6, -8, 0, 0, 0, 0, 0}, // 172 { 1, 0, 0, -1, 1, 0, 0, -1, 0, 1, 0, 0, 0, 0}, // 173 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, -5, 0, 0, 0}, // 174 { 0, 0, 1, -1, 1, 0, 0, -1, 0, -1, 0, 0, 0, 0}, // 175 { 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 1}, // 176 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, // 177 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1}, // 178 { 0, 0, 1, -1, 1, 0, 0, -1, 0, 1, 0, 0, 0, 0}, // 179 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1}, // 180 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2}, // 181 { 0, 0, 1, -1, 2, 0, 0, -1, 0, 0, -1, 0, 0, 0}, // 182 { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, 0, 0}, // 183 { 0, 0, -1, 1, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0}, // 184 { 0, 0, 0, 0, 0, 0, 0, -7, 13, 0, 0, 0, 0, 2}, // 185 { 0, 0, 0, 0, 0, 0, 0, 7,-13, 0, 0, 0, 0, 0}, // 186 { 2, 0, 0, -2, 1, 0, 0, -5, 6, 0, 0, 0, 0, 0}, // 187 { 0, 0, 2, -2, 1, 0, 0, -8, 11, 0, 0, 0, 0, 0}, // 188 { 0, 0, 2, -2, 1, -1, 0, 2, 0, 0, 0, 0, 0, 0}, // 189 { -2, 0, 0, 2, 0, 0, 0, 4, -4, 0, 0, 0, 0, 0}, // 190 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 0}, // 191 { 0, 0, 1, -1, 1, 0, 0, -1, 0, 0, 3, 0, 0, 0}, // 192 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 1}, // 193 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 2}, // 194 { -2, 0, 0, 2, 0, 0, 3, -3, 0, 0, 0, 0, 0, 0}, // 195 { 0, 0, 0, 0, 2, 0, 0, -4, 8, -3, 0, 0, 0, 0}, // 196 { 0, 0, 0, 0, 2, 0, 0, 4, -8, 3, 0, 0, 0, 0}, // 197 { 2, 0, 0, -2, 1, 0, 0, -2, 0, 2, 0, 0, 0, 0}, // 198 { 0, 0, 1, -1, 2, 0, 0, -1, 0, 2, 0, 0, 0, 0}, // 199 { 0, 0, 1, -1, 2, 0, 0, 0, -2, 0, 0, 0, 0, 0}, // 200 { 0, 0, 0, 0, 1, 0, 0, 1, -2, 0, 0, 0, 0, 0}, // 201 { 0, 0, -1, 1, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0}, // 202 { 0, 0, -1, 1, 0, 0, 0, 1, 0, 0, -2, 0, 0, 0}, // 203 { 0, 0, 2, -2, 1, 0, 0, -2, 0, 0, 2, 0, 0, 0}, // 204 { 0, 0, 1, -1, 1, 0, 3, -6, 0, 0, 0, 0, 0, 0}, // 205 { 0, 0, 0, 0, 0, 0, 3, -5, 0, 0, 0, 0, 0, 1}, // 206 { 0, 0, 0, 0, 0, 0, 3, -5, 0, 0, 0, 0, 0, 0}, // 207 { 0, 0, 1, -1, 1, 0, -3, 4, 0, 0, 0, 0, 0, 0}, // 208 { 0, 0, 0, 0, 0, 0, -3, 5, 0, 0, 0, 0, 0, 1}, // 209 { 0, 0, 0, 0, 0, 0, -3, 5, 0, 0, 0, 0, 0, 2}, // 210 { 0, 0, 2, -2, 2, 0, -3, 3, 0, 0, 0, 0, 0, 0}, // 211 { 0, 0, 0, 0, 0, 0, -3, 5, 0, 0, 0, 0, 0, 2}, // 212 { 0, 0, 0, 0, 0, 0, 0, 2, -4, 0, 0, 0, 0, 1}, // 213 { 0, 0, 1, -1, 1, 0, 0, 1, -4, 0, 0, 0, 0, 0}, // 214 { 0, 0, 0, 0, 0, 0, 0, 2, -4, 0, 0, 0, 0, 0}, // 215 { 0, 0, 0, 0, 0, 0, 0, -2, 4, 0, 0, 0, 0, 1}, // 216 { 0, 0, 1, -1, 1, 0, 0, -3, 4, 0, 0, 0, 0, 0}, // 217 { 0, 0, 0, 0, 0, 0, 0, -2, 4, 0, 0, 0, 0, 1}, // 218 { 0, 0, 0, 0, 0, 0, 0, -2, 4, 0, 0, 0, 0, 2}, // 219 { 0, 0, 0, 0, 0, 0, -5, 8, 0, 0, 0, 0, 0, 2}, // 220 { 0, 0, 2, -2, 2, 0, -5, 6, 0, 0, 0, 0, 0, 0}, // 221 { 0, 0, 0, 0, 0, 0, -5, 8, 0, 0, 0, 0, 0, 2}, // 222 { 0, 0, 0, 0, 0, 0, -5, 8, 0, 0, 0, 0, 0, 1}, // 223 { 0, 0, 1, -1, 1, 0, -5, 7, 0, 0, 0, 0, 0, 0}, // 224 { 0, 0, 0, 0, 0, 0, -5, 8, 0, 0, 0, 0, 0, 1}, // 225 { 0, 0, 0, 0, 0, 0, 5, -8, 0, 0, 0, 0, 0, 0}, // 226 { 0, 0, 1, -1, 2, 0, 0, -1, 0, -1, 0, 0, 0, 0}, // 227 { 0, 0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0}, // 228 { 0, 0, -1, 1, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0}, // 229 { 0, 0, 2, -2, 1, 0, 0, -2, 0, 1, 0, 0, 0, 0}, // 230 { 0, 0, 0, 0, 0, 0, 0, -6, 11, 0, 0, 0, 0, 2}, // 231 { 0, 0, 0, 0, 0, 0, 0, 6,-11, 0, 0, 0, 0, 0}, // 232 { 0, 0, 0, 0, 0, -1, 0, 4, 0, 0, 0, 0, 0, 2}, // 233 { 0, 0, 0, 0, 0, 1, 0, -4, 0, 0, 0, 0, 0, 0}, // 234 { 2, 0, 0, -2, 1, 0, -3, 3, 0, 0, 0, 0, 0, 0}, // 235 { -2, 0, 0, 2, 0, 0, 0, 2, 0, 0, -2, 0, 0, 0}, // 236 { 0, 0, 2, -2, 1, 0, 0, -7, 9, 0, 0, 0, 0, 0}, // 237 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, -5, 0, 0, 2}, // 238 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0}, // 239 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 1}, // 240 { 0, 0, 1, -1, 1, 0, 0, -1, 0, 2, 0, 0, 0, 0}, // 241 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 1}, // 242 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2}, // 243 { 0, 0, 2, -2, 2, 0, 0, -2, 0, 2, 0, 0, 0, 0}, // 244 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 2}, // 245 { 0, 0, 0, 0, 1, 0, 3, -5, 0, 0, 0, 0, 0, 0}, // 246 { 0, 0, -1, 1, 0, 0, 3, -4, 0, 0, 0, 0, 0, 0}, // 247 { 0, 0, 2, -2, 1, 0, -3, 3, 0, 0, 0, 0, 0, 0}, // 248 { 0, 0, 0, 0, 1, 0, 0, 2, -4, 0, 0, 0, 0, 0}, // 249 { 0, 0, 2, -2, 1, 0, 0, -4, 4, 0, 0, 0, 0, 0}, // 250 { 0, 0, 1, -1, 2, 0, -5, 7, 0, 0, 0, 0, 0, 0}, // 251 { 0, 0, 0, 0, 0, 0, 0, 3, -6, 0, 0, 0, 0, 0}, // 252 { 0, 0, 0, 0, 0, 0, 0, -3, 6, 0, 0, 0, 0, 1}, // 253 { 0, 0, 1, -1, 1, 0, 0, -4, 6, 0, 0, 0, 0, 0}, // 254 { 0, 0, 0, 0, 0, 0, 0, -3, 6, 0, 0, 0, 0, 1}, // 255 { 0, 0, 0, 0, 0, 0, 0, -3, 6, 0, 0, 0, 0, 2}, // 256 { 0, 0, -1, 1, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0}, // 257 { 0, 0, 0, 0, 1, 0, 2, -3, 0, 0, 0, 0, 0, 0}, // 258 { 0, 0, 0, 0, 0, 0, 0, -5, 9, 0, 0, 0, 0, 2}, // 259 { 0, 0, 0, 0, 0, 0, 0, -5, 9, 0, 0, 0, 0, 1}, // 260 { 0, 0, 0, 0, 0, 0, 0, 5, -9, 0, 0, 0, 0, 0}, // 261 { 0, 0, -1, 1, 0, 0, 0, 1, 0, -2, 0, 0, 0, 0}, // 262 { 0, 0, 2, -2, 1, 0, 0, -2, 0, 2, 0, 0, 0, 0}, // 263 { -2, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0}, // 264 { 0, 0, -2, 2, 0, 0, 3, -3, 0, 0, 0, 0, 0, 0}, // 265 { 0, 0, 0, 0, 0, 0, -6, 10, 0, 0, 0, 0, 0, 1}, // 266 { 0, 0, 0, 0, 0, 0, -6, 10, 0, 0, 0, 0, 0, 2}, // 267 { 0, 0, 0, 0, 0, 0, -2, 3, 0, 0, 0, 0, 0, 2}, // 268 { 0, 0, 0, 0, 0, 0, -2, 3, 0, 0, 0, 0, 0, 1}, // 269 { 0, 0, 1, -1, 1, 0, -2, 2, 0, 0, 0, 0, 0, 0}, // 270 { 0, 0, 0, 0, 0, 0, 2, -3, 0, 0, 0, 0, 0, 0}, // 271 { 0, 0, 0, 0, 0, 0, 2, -3, 0, 0, 0, 0, 0, 1}, // 272 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1}, // 273 { 0, 0, 1, -1, 1, 0, 0, -1, 0, 3, 0, 0, 0, 0}, // 274 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1}, // 275 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 2}, // 276 { 0, 0, 0, 0, 0, 0, 0, 4, -8, 0, 0, 0, 0, 0}, // 277 { 0, 0, 0, 0, 0, 0, 0, -4, 8, 0, 0, 0, 0, 2}, // 278 { 0, 0, -2, 2, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0}, // 279 { 0, 0, 0, 0, 0, 0, 0, -4, 7, 0, 0, 0, 0, 2}, // 280 { 0, 0, 0, 0, 0, 0, 0, -4, 7, 0, 0, 0, 0, 1}, // 281 { 0, 0, 0, 0, 0, 0, 0, 4, -7, 0, 0, 0, 0, 0}, // 282 { 0, 0, 0, 0, 1, 0, -2, 3, 0, 0, 0, 0, 0, 0}, // 283 { 0, 0, 2, -2, 1, 0, 0, -2, 0, 3, 0, 0, 0, 0}, // 284 { 0, 0, 0, 0, 0, 0, 0, -5, 10, 0, 0, 0, 0, 2}, // 285 { 0, 0, 0, 0, 1, 0, -1, 2, 0, 0, 0, 0, 0, 0}, // 286 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 2}, // 287 { 0, 0, 0, 0, 0, 0, 0, -3, 5, 0, 0, 0, 0, 2}, // 288 { 0, 0, 0, 0, 0, 0, 0, -3, 5, 0, 0, 0, 0, 1}, // 289 { 0, 0, 0, 0, 0, 0, 0, 3, -5, 0, 0, 0, 0, 0}, // 290 { 0, 0, 0, 0, 0, 0, 1, -2, 0, 0, 0, 0, 0, 1}, // 291 { 0, 0, 1, -1, 1, 0, 1, -3, 0, 0, 0, 0, 0, 0}, // 292 { 0, 0, 0, 0, 0, 0, 1, -2, 0, 0, 0, 0, 0, 0}, // 293 { 0, 0, 0, 0, 0, 0, -1, 2, 0, 0, 0, 0, 0, 1}, // 294 { 0, 0, 0, 0, 0, 0, -1, 2, 0, 0, 0, 0, 0, 2}, // 295 { 0, 0, 0, 0, 0, 0, -7, 11, 0, 0, 0, 0, 0, 2}, // 296 { 0, 0, 0, 0, 0, 0, -7, 11, 0, 0, 0, 0, 0, 1}, // 297 { 0, 0, -2, 2, 0, 0, 4, -4, 0, 0, 0, 0, 0, 0}, // 298 { 0, 0, 0, 0, 0, 0, 0, 2, -3, 0, 0, 0, 0, 0}, // 299 { 0, 0, 2, -2, 1, 0, -4, 4, 0, 0, 0, 0, 0, 0}, // 300 { 0, 0, -1, 1, 0, 0, 4, -5, 0, 0, 0, 0, 0, 0}, // 301 { 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0}, // 302 { 0, 0, 0, 0, 0, 0, -4, 7, 0, 0, 0, 0, 0, 1}, // 303 { 0, 0, 1, -1, 1, 0, -4, 6, 0, 0, 0, 0, 0, 0}, // 304 { 0, 0, 0, 0, 0, 0, -4, 7, 0, 0, 0, 0, 0, 2}, // 305 { 0, 0, 0, 0, 0, 0, -4, 6, 0, 0, 0, 0, 0, 2}, // 306 { 0, 0, 0, 0, 0, 0, -4, 6, 0, 0, 0, 0, 0, 1}, // 307 { 0, 0, 1, -1, 1, 0, -4, 5, 0, 0, 0, 0, 0, 0}, // 308 { 0, 0, 0, 0, 0, 0, -4, 6, 0, 0, 0, 0, 0, 1}, // 309 { 0, 0, 0, 0, 0, 0, 4, -6, 0, 0, 0, 0, 0, 0}, // 310 { -2, 0, 0, 2, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0}, // 311 { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0}, // 312 { 0, 0, -1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, // 313 { 0, 0, 0, 0, 1, 0, 1, -1, 0, 0, 0, 0, 0, 0}, // 314 { 0, 0, 0, 0, 0, 0, 0, -1, 0, 5, 0, 0, 0, 2}, // 315 { 0, 0, 0, 0, 0, 0, 0, 1, -3, 0, 0, 0, 0, 0}, // 316 { 0, 0, 0, 0, 0, 0, 0, -1, 3, 0, 0, 0, 0, 2}, // 317 { 0, 0, 0, 0, 0, 0, 0, -7, 12, 0, 0, 0, 0, 2}, // 318 { 0, 0, 0, 0, 0, 0, -1, 1, 0, 0, 0, 0, 0, 2}, // 319 { 0, 0, 0, 0, 0, 0, -1, 1, 0, 0, 0, 0, 0, 1}, // 320 { 0, 0, 1, -1, 1, 0, -1, 0, 0, 0, 0, 0, 0, 0}, // 321 { 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0}, // 322 { 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 1}, // 323 { 0, 0, 1, -1, 1, 0, 1, -2, 0, 0, 0, 0, 0, 0}, // 324 { 0, 0, 0, 0, 0, 0, 0, -2, 5, 0, 0, 0, 0, 2}, // 325 { 0, 0, 0, 0, 0, 0, 0, -1, 0, 4, 0, 0, 0, 2}, // 326 { 0, 0, 0, 0, 0, 0, 0, 1, 0, -4, 0, 0, 0, 0}, // 327 { 0, 0, 0, 0, 1, 0, -1, 1, 0, 0, 0, 0, 0, 0}, // 328 { 0, 0, 0, 0, 0, 0, 0, -6, 10, 0, 0, 0, 0, 2}, // 329 { 0, 0, 0, 0, 0, 0, 0, -6, 10, 0, 0, 0, 0, 0}, // 330 { 0, 0, 2, -2, 1, 0, 0, -3, 0, 3, 0, 0, 0, 0}, // 331 { 0, 0, 0, 0, 0, 0, 0, -3, 7, 0, 0, 0, 0, 2}, // 332 { -2, 0, 0, 2, 0, 0, 4, -4, 0, 0, 0, 0, 0, 0}, // 333 { 0, 0, 0, 0, 0, 0, 0, -5, 8, 0, 0, 0, 0, 2}, // 334 { 0, 0, 0, 0, 0, 0, 0, 5, -8, 0, 0, 0, 0, 0}, // 335 { 0, 0, 0, 0, 0, 0, 0, -1, 0, 3, 0, 0, 0, 2}, // 336 { 0, 0, 0, 0, 0, 0, 0, -1, 0, 3, 0, 0, 0, 1}, // 337 { 0, 0, 0, 0, 0, 0, 0, 1, 0, -3, 0, 0, 0, 0}, // 338 { 0, 0, 0, 0, 0, 0, 2, -4, 0, 0, 0, 0, 0, 0}, // 339 { 0, 0, 0, 0, 0, 0, -2, 4, 0, 0, 0, 0, 0, 1}, // 340 { 0, 0, 1, -1, 1, 0, -2, 3, 0, 0, 0, 0, 0, 0}, // 341 { 0, 0, 0, 0, 0, 0, -2, 4, 0, 0, 0, 0, 0, 2}, // 342 { 0, 0, 0, 0, 0, 0, -6, 9, 0, 0, 0, 0, 0, 2}, // 343 { 0, 0, 0, 0, 0, 0, -6, 9, 0, 0, 0, 0, 0, 1}, // 344 { 0, 0, 0, 0, 0, 0, 6, -9, 0, 0, 0, 0, 0, 0}, // 345 { 0, 0, 0, 0, 1, 0, 0, 1, 0, -2, 0, 0, 0, 0}, // 346 { 0, 0, 2, -2, 1, 0, -2, 2, 0, 0, 0, 0, 0, 0}, // 347 { 0, 0, 0, 0, 0, 0, 0, -4, 6, 0, 0, 0, 0, 2}, // 348 { 0, 0, 0, 0, 0, 0, 0, 4, -6, 0, 0, 0, 0, 0}, // 349 { 0, 0, 0, 0, 1, 0, 3, -4, 0, 0, 0, 0, 0, 0}, // 350 { 0, 0, 0, 0, 0, 0, 0, -1, 0, 2, 0, 0, 0, 2}, // 351 { 0, 0, 0, 0, 0, 0, 0, 1, 0, -2, 0, 0, 0, 0}, // 352 { 0, 0, 0, 0, 1, 0, 0, 1, 0, -1, 0, 0, 0, 0}, // 353 { 0, 0, 0, 0, 0, 0, -5, 9, 0, 0, 0, 0, 0, 2}, // 354 { 0, 0, 0, 0, 0, 0, 0, 3, -4, 0, 0, 0, 0, 0}, // 355 { 0, 0, 0, 0, 0, 0, -3, 4, 0, 0, 0, 0, 0, 2}, // 356 { 0, 0, 0, 0, 0, 0, -3, 4, 0, 0, 0, 0, 0, 1}, // 357 { 0, 0, 0, 0, 0, 0, 3, -4, 0, 0, 0, 0, 0, 0}, // 358 { 0, 0, 0, 0, 0, 0, 3, -4, 0, 0, 0, 0, 0, 1}, // 359 { 0, 0, 0, 0, 1, 0, 0, 2, -2, 0, 0, 0, 0, 0}, // 360 { 0, 0, 0, 0, 1, 0, 0, -1, 0, 2, 0, 0, 0, 0}, // 361 { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, -3, 0, 0, 0}, // 362 { 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, -5, 0, 0, 0}, // 363 { 0, 0, 0, 0, 0, 0, 0, -1, 0, 1, 0, 0, 0, 1}, // 364 { 0, 0, 0, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0}, // 365 { 0, 0, 0, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 1}, // 366 { 0, 0, 0, 0, 0, 0, 0, 1, 0, -3, 5, 0, 0, 0}, // 367 { 0, 0, 0, 0, 1, 0, -3, 4, 0, 0, 0, 0, 0, 0}, // 368 { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, -2, 0, 0, 0}, // 369 { 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0}, // 370 { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0}, // 371 { 0, 0, 0, 0, 1, 0, 0, -1, 0, 1, 0, 0, 0, 0}, // 372 { 0, 0, 0, 0, 1, 0, 0, -2, 2, 0, 0, 0, 0, 0}, // 373 { 0, 0, 0, 0, 0, 0, -8, 14, 0, 0, 0, 0, 0, 2}, // 374 { 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, -5, 0, 0, 0}, // 375 { 0, 0, 0, 0, 0, 0, 0, 5, -8, 3, 0, 0, 0, 0}, // 376 { 0, 0, 0, 0, 0, 0, 0, 5, -8, 3, 0, 0, 0, 2}, // 377 { 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 1}, // 378 { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, // 379 { 0, 0, 0, 0, 0, 0, 0, 3, -8, 3, 0, 0, 0, 0}, // 380 { 0, 0, 0, 0, 0, 0, 0, -3, 8, -3, 0, 0, 0, 2}, // 381 { 0, 0, 0, 0, 0, 0, 0, 1, 0, -2, 5, 0, 0, 2}, // 382 { 0, 0, 0, 0, 0, 0, -8, 12, 0, 0, 0, 0, 0, 2}, // 383 { 0, 0, 0, 0, 0, 0, -8, 12, 0, 0, 0, 0, 0, 0}, // 384 { 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, -2, 0, 0, 0}, // 385 { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 2}, // 386 { 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0}, // 387 { 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2}, // 388 { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 2}, // 389 { 0, 0, 2, -2, 1, 0, -5, 5, 0, 0, 0, 0, 0, 0}, // 390 { 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0}, // 391 { 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1}, // 392 { 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 2}, // 393 { 0, 0, 0, 0, 0, 0, 3, -6, 0, 0, 0, 0, 0, 0}, // 394 { 0, 0, 0, 0, 0, 0, -3, 6, 0, 0, 0, 0, 0, 1}, // 395 { 0, 0, 0, 0, 0, 0, -3, 6, 0, 0, 0, 0, 0, 2}, // 396 { 0, 0, 0, 0, 0, 0, 0, -1, 4, 0, 0, 0, 0, 2}, // 397 { 0, 0, 0, 0, 0, 0, -5, 7, 0, 0, 0, 0, 0, 2}, // 398 { 0, 0, 0, 0, 0, 0, -5, 7, 0, 0, 0, 0, 0, 1}, // 399 { 0, 0, 1, -1, 1, 0, -5, 6, 0, 0, 0, 0, 0, 0}, // 400 { 0, 0, 0, 0, 0, 0, 5, -7, 0, 0, 0, 0, 0, 0}, // 401 { 0, 0, 2, -2, 1, 0, 0, -1, 0, 1, 0, 0, 0, 0}, // 402 { 0, 0, 0, 0, 0, 0, 0, -1, 0, 1, 0, 0, 0, 0}, // 403 { 0, 0, 0, 0, 0, -1, 0, 3, 0, 0, 0, 0, 0, 2}, // 404 { 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 2}, // 405 { 0, 0, 0, 0, 0, 0, 0, -2, 6, 0, 0, 0, 0, 2}, // 406 { 0, 0, 0, 0, 1, 0, 2, -2, 0, 0, 0, 0, 0, 0}, // 407 { 0, 0, 0, 0, 0, 0, 0, -6, 9, 0, 0, 0, 0, 2}, // 408 { 0, 0, 0, 0, 0, 0, 0, 6, -9, 0, 0, 0, 0, 0}, // 409 { 0, 0, 0, 0, 0, 0, -2, 2, 0, 0, 0, 0, 0, 1}, // 410 { 0, 0, 1, -1, 1, 0, -2, 1, 0, 0, 0, 0, 0, 0}, // 411 { 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0}, // 412 { 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0, 1}, // 413 { 0, 0, 0, 0, 0, 0, 0, 1, 0, 3, 0, 0, 0, 2}, // 414 { 0, 0, 0, 0, 0, 0, 0, -5, 7, 0, 0, 0, 0, 2}, // 415 { 0, 0, 0, 0, 0, 0, 0, 5, -7, 0, 0, 0, 0, 0}, // 416 { 0, 0, 0, 0, 1, 0, -2, 2, 0, 0, 0, 0, 0, 0}, // 417 { 0, 0, 0, 0, 0, 0, 0, 4, -5, 0, 0, 0, 0, 0}, // 418 { 0, 0, 0, 0, 0, 0, 1, -3, 0, 0, 0, 0, 0, 0}, // 419 { 0, 0, 0, 0, 0, 0, -1, 3, 0, 0, 0, 0, 0, 1}, // 420 { 0, 0, 1, -1, 1, 0, -1, 2, 0, 0, 0, 0, 0, 0}, // 421 { 0, 0, 0, 0, 0, 0, -1, 3, 0, 0, 0, 0, 0, 2}, // 422 { 0, 0, 0, 0, 0, 0, -7, 10, 0, 0, 0, 0, 0, 2}, // 423 { 0, 0, 0, 0, 0, 0, -7, 10, 0, 0, 0, 0, 0, 1}, // 424 { 0, 0, 0, 0, 0, 0, 0, 3, -3, 0, 0, 0, 0, 0}, // 425 { 0, 0, 0, 0, 0, 0, -4, 8, 0, 0, 0, 0, 0, 2}, // 426 { 0, 0, 0, 0, 0, 0, -4, 5, 0, 0, 0, 0, 0, 2}, // 427 { 0, 0, 0, 0, 0, 0, -4, 5, 0, 0, 0, 0, 0, 1}, // 428 { 0, 0, 0, 0, 0, 0, 4, -5, 0, 0, 0, 0, 0, 0}, // 429 { 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 2}, // 430 { 0, 0, 0, 0, 0, 0, 0, -2, 0, 5, 0, 0, 0, 2}, // 431 { 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 2}, // 432 { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, // 433 { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2}, // 434 { 0, 0, 0, 0, 0, 0, -9, 13, 0, 0, 0, 0, 0, 2}, // 435 { 0, 0, 0, 0, 0, 0, 0, -1, 5, 0, 0, 0, 0, 2}, // 436 { 0, 0, 0, 0, 0, 0, 0, -2, 0, 4, 0, 0, 0, 2}, // 437 { 0, 0, 0, 0, 0, 0, 0, 2, 0, -4, 0, 0, 0, 0}, // 438 { 0, 0, 0, 0, 0, 0, 0, -2, 7, 0, 0, 0, 0, 2}, // 439 { 0, 0, 0, 0, 0, 0, 0, 2, 0, -3, 0, 0, 0, 0}, // 440 { 0, 0, 0, 0, 0, 0, -2, 5, 0, 0, 0, 0, 0, 1}, // 441 { 0, 0, 0, 0, 0, 0, -2, 5, 0, 0, 0, 0, 0, 2}, // 442 { 0, 0, 0, 0, 0, 0, -6, 8, 0, 0, 0, 0, 0, 2}, // 443 { 0, 0, 0, 0, 0, 0, -6, 8, 0, 0, 0, 0, 0, 1}, // 444 { 0, 0, 0, 0, 0, 0, 6, -8, 0, 0, 0, 0, 0, 0}, // 445 { 0, 0, 0, 0, 1, 0, 0, 2, 0, -2, 0, 0, 0, 0}, // 446 { 0, 0, 0, 0, 0, 0, 0, -3, 9, 0, 0, 0, 0, 2}, // 447 { 0, 0, 0, 0, 0, 0, 0, 5, -6, 0, 0, 0, 0, 0}, // 448 { 0, 0, 0, 0, 0, 0, 0, 5, -6, 0, 0, 0, 0, 2}, // 449 { 0, 0, 0, 0, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0}, // 450 { 0, 0, 0, 0, 0, 0, 0, 2, 0, -2, 0, 0, 0, 1}, // 451 { 0, 0, 0, 0, 0, 0, 0, 2, 0, -2, 0, 0, 0, 2}, // 452 { 0, 0, 0, 0, 0, 0, -5, 10, 0, 0, 0, 0, 0, 2}, // 453 { 0, 0, 0, 0, 0, 0, 0, 4, -4, 0, 0, 0, 0, 0}, // 454 { 0, 0, 0, 0, 0, 0, 0, 4, -4, 0, 0, 0, 0, 2}, // 455 { 0, 0, 0, 0, 0, 0, -3, 3, 0, 0, 0, 0, 0, 1}, // 456 { 0, 0, 0, 0, 0, 0, 3, -3, 0, 0, 0, 0, 0, 0}, // 457 { 0, 0, 0, 0, 0, 0, 3, -3, 0, 0, 0, 0, 0, 1}, // 458 { 0, 0, 0, 0, 0, 0, 3, -3, 0, 0, 0, 0, 0, 2}, // 459 { 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, -3, 0, 0, 0}, // 460 { 0, 0, 0, 0, 0, 0, 0, -5, 13, 0, 0, 0, 0, 2}, // 461 { 0, 0, 0, 0, 0, 0, 0, 2, 0, -1, 0, 0, 0, 0}, // 462 { 0, 0, 0, 0, 0, 0, 0, 2, 0, -1, 0, 0, 0, 2}, // 463 { 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, -2, 0, 0, 0}, // 464 { 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, -2, 0, 0, 1}, // 465 { 0, 0, 0, 0, 0, 0, 0, 3, -2, 0, 0, 0, 0, 0}, // 466 { 0, 0, 0, 0, 0, 0, 0, 3, -2, 0, 0, 0, 0, 2}, // 467 { 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, -1, 0, 0, 2}, // 468 { 0, 0, 0, 0, 0, 0, 0, -6, 15, 0, 0, 0, 0, 2}, // 469 { 0, 0, 0, 0, 0, 0, -8, 15, 0, 0, 0, 0, 0, 2}, // 470 { 0, 0, 0, 0, 0, 0, -3, 9, -4, 0, 0, 0, 0, 2}, // 471 { 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, -5, 0, 0, 2}, // 472 { 0, 0, 0, 0, 0, 0, 0, -2, 8, -1, -5, 0, 0, 2}, // 473 { 0, 0, 0, 0, 0, 0, 0, 6, -8, 3, 0, 0, 0, 2}, // 474 { 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0}, // 475 { 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0}, // 476 { 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 1}, // 477 { 0, 0, 1, -1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0}, // 478 { 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 1}, // 479 { 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2}, // 480 { 0, 0, 0, 0, 0, 0, 0, -6, 16, -4, -5, 0, 0, 2}, // 481 { 0, 0, 0, 0, 0, 0, 0, -2, 8, -3, 0, 0, 0, 2}, // 482 { 0, 0, 0, 0, 0, 0, 0, -2, 8, -3, 0, 0, 0, 2}, // 483 { 0, 0, 0, 0, 0, 0, 0, 6, -8, 1, 5, 0, 0, 2}, // 484 { 0, 0, 0, 0, 0, 0, 0, 2, 0, -2, 5, 0, 0, 2}, // 485 { 0, 0, 0, 0, 0, 0, 3, -5, 4, 0, 0, 0, 0, 2}, // 486 { 0, 0, 0, 0, 0, 0, -8, 11, 0, 0, 0, 0, 0, 2}, // 487 { 0, 0, 0, 0, 0, 0, -8, 11, 0, 0, 0, 0, 0, 1}, // 488 { 0, 0, 0, 0, 0, 0, -8, 11, 0, 0, 0, 0, 0, 2}, // 489 { 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 2}, // 490 { 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 1, 0, 0, 2}, // 491 { 0, 0, 0, 0, 0, 0, 3, -3, 0, 2, 0, 0, 0, 2}, // 492 { 0, 0, 2, -2, 1, 0, 0, 4, -8, 3, 0, 0, 0, 0}, // 493 { 0, 0, 1, -1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, // 494 { 0, 0, 2, -2, 1, 0, 0, -4, 8, -3, 0, 0, 0, 0}, // 495 { 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 2}, // 496 { 0, 0, 0, 0, 0, 0, 0, 2, 0, 1, 0, 0, 0, 2}, // 497 { 0, 0, 0, 0, 0, 0, -3, 7, 0, 0, 0, 0, 0, 2}, // 498 { 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 2}, // 499 { 0, 0, 0, 0, 0, 0, -5, 6, 0, 0, 0, 0, 0, 2}, // 500 { 0, 0, 0, 0, 0, 0, -5, 6, 0, 0, 0, 0, 0, 1}, // 501 { 0, 0, 0, 0, 0, 0, 5, -6, 0, 0, 0, 0, 0, 0}, // 502 { 0, 0, 0, 0, 0, 0, 5, -6, 0, 0, 0, 0, 0, 2}, // 503 { 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 2}, // 504 { 0, 0, 0, 0, 0, 0, 0, -1, 6, 0, 0, 0, 0, 2}, // 505 { 0, 0, 0, 0, 0, 0, 0, 7, -9, 0, 0, 0, 0, 2}, // 506 { 0, 0, 0, 0, 0, 0, 2, -1, 0, 0, 0, 0, 0, 0}, // 507 { 0, 0, 0, 0, 0, 0, 2, -1, 0, 0, 0, 0, 0, 2}, // 508 { 0, 0, 0, 0, 0, 0, 0, 6, -7, 0, 0, 0, 0, 2}, // 509 { 0, 0, 0, 0, 0, 0, 0, 5, -5, 0, 0, 0, 0, 2}, // 510 { 0, 0, 0, 0, 0, 0, -1, 4, 0, 0, 0, 0, 0, 1}, // 511 { 0, 0, 0, 0, 0, 0, -1, 4, 0, 0, 0, 0, 0, 2}, // 512 { 0, 0, 0, 0, 0, 0, -7, 9, 0, 0, 0, 0, 0, 2}, // 513 { 0, 0, 0, 0, 0, 0, -7, 9, 0, 0, 0, 0, 0, 1}, // 514 { 0, 0, 0, 0, 0, 0, 0, 4, -3, 0, 0, 0, 0, 2}, // 515 { 0, 0, 0, 0, 0, 0, 0, 3, -1, 0, 0, 0, 0, 2}, // 516 { 0, 0, 0, 0, 0, 0, -4, 4, 0, 0, 0, 0, 0, 1}, // 517 { 0, 0, 0, 0, 0, 0, 4, -4, 0, 0, 0, 0, 0, 0}, // 518 { 0, 0, 0, 0, 0, 0, 4, -4, 0, 0, 0, 0, 0, 1}, // 519 { 0, 0, 0, 0, 0, 0, 4, -4, 0, 0, 0, 0, 0, 2}, // 520 { 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 0, 2}, // 521 { 0, 0, 0, 0, 0, 0, 0, -3, 0, 5, 0, 0, 0, 2}, // 522 { 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0}, // 523 { 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1}, // 524 { 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 2}, // 525 { 0, 0, 0, 0, 0, 0, -9, 12, 0, 0, 0, 0, 0, 2}, // 526 { 0, 0, 0, 0, 0, 0, 0, 3, 0, -4, 0, 0, 0, 0}, // 527 { 0, 0, 2, -2, 1, 0, 1, -1, 0, 0, 0, 0, 0, 0}, // 528 { 0, 0, 0, 0, 0, 0, 0, 7, -8, 0, 0, 0, 0, 2}, // 529 { 0, 0, 0, 0, 0, 0, 0, 3, 0, -3, 0, 0, 0, 0}, // 530 { 0, 0, 0, 0, 0, 0, 0, 3, 0, -3, 0, 0, 0, 2}, // 531 { 0, 0, 0, 0, 0, 0, -2, 6, 0, 0, 0, 0, 0, 2}, // 532 { 0, 0, 0, 0, 0, 0, -6, 7, 0, 0, 0, 0, 0, 1}, // 533 { 0, 0, 0, 0, 0, 0, 6, -7, 0, 0, 0, 0, 0, 0}, // 534 { 0, 0, 0, 0, 0, 0, 0, 6, -6, 0, 0, 0, 0, 2}, // 535 { 0, 0, 0, 0, 0, 0, 0, 3, 0, -2, 0, 0, 0, 0}, // 536 { 0, 0, 0, 0, 0, 0, 0, 3, 0, -2, 0, 0, 0, 2}, // 537 { 0, 0, 0, 0, 0, 0, 0, 5, -4, 0, 0, 0, 0, 2}, // 538 { 0, 0, 0, 0, 0, 0, 3, -2, 0, 0, 0, 0, 0, 0}, // 539 { 0, 0, 0, 0, 0, 0, 3, -2, 0, 0, 0, 0, 0, 2}, // 540 { 0, 0, 0, 0, 0, 0, 0, 3, 0, -1, 0, 0, 0, 2}, // 541 { 0, 0, 0, 0, 0, 0, 0, 3, 0, -1, 0, 0, 0, 2}, // 542 { 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, -2, 0, 0, 2}, // 543 { 0, 0, 0, 0, 0, 0, 0, 4, -2, 0, 0, 0, 0, 2}, // 544 { 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, -1, 0, 0, 2}, // 545 { 0, 0, 2, -2, 1, 0, 0, 1, 0, -1, 0, 0, 0, 0}, // 546 { 0, 0, 0, 0, 0, 0, -8, 16, 0, 0, 0, 0, 0, 2}, // 547 { 0, 0, 0, 0, 0, 0, 0, 3, 0, 2, -5, 0, 0, 2}, // 548 { 0, 0, 0, 0, 0, 0, 0, 7, -8, 3, 0, 0, 0, 2}, // 549 { 0, 0, 0, 0, 0, 0, 0, -5, 16, -4, -5, 0, 0, 2}, // 550 { 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 2}, // 551 { 0, 0, 0, 0, 0, 0, 0, -1, 8, -3, 0, 0, 0, 2}, // 552 { 0, 0, 0, 0, 0, 0, -8, 10, 0, 0, 0, 0, 0, 2}, // 553 { 0, 0, 0, 0, 0, 0, -8, 10, 0, 0, 0, 0, 0, 1}, // 554 { 0, 0, 0, 0, 0, 0, -8, 10, 0, 0, 0, 0, 0, 2}, // 555 { 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 2}, // 556 { 0, 0, 0, 0, 0, 0, 0, 3, 0, 1, 0, 0, 0, 2}, // 557 { 0, 0, 0, 0, 0, 0, -3, 8, 0, 0, 0, 0, 0, 2}, // 558 { 0, 0, 0, 0, 0, 0, -5, 5, 0, 0, 0, 0, 0, 1}, // 559 { 0, 0, 0, 0, 0, 0, 5, -5, 0, 0, 0, 0, 0, 0}, // 560 { 0, 0, 0, 0, 0, 0, 5, -5, 0, 0, 0, 0, 0, 1}, // 561 { 0, 0, 0, 0, 0, 0, 5, -5, 0, 0, 0, 0, 0, 2}, // 562 { 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0}, // 563 { 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 1}, // 564 { 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2}, // 565 { 0, 0, 0, 0, 0, 0, 0, 7, -7, 0, 0, 0, 0, 2}, // 566 { 0, 0, 0, 0, 0, 0, 0, 7, -7, 0, 0, 0, 0, 2}, // 567 { 0, 0, 0, 0, 0, 0, 0, 6, -5, 0, 0, 0, 0, 2}, // 568 { 0, 0, 0, 0, 0, 0, 7, -8, 0, 0, 0, 0, 0, 0}, // 569 { 0, 0, 0, 0, 0, 0, 0, 5, -3, 0, 0, 0, 0, 2}, // 570 { 0, 0, 0, 0, 0, 0, 4, -3, 0, 0, 0, 0, 0, 2}, // 571 { 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 2}, // 572 { 0, 0, 0, 0, 0, 0, -9, 11, 0, 0, 0, 0, 0, 2}, // 573 { 0, 0, 0, 0, 0, 0, -9, 11, 0, 0, 0, 0, 0, 1}, // 574 { 0, 0, 0, 0, 0, 0, 0, 4, 0, -4, 0, 0, 0, 2}, // 575 { 0, 0, 0, 0, 0, 0, 0, 4, 0, -3, 0, 0, 0, 2}, // 576 { 0, 0, 0, 0, 0, 0, -6, 6, 0, 0, 0, 0, 0, 1}, // 577 { 0, 0, 0, 0, 0, 0, 6, -6, 0, 0, 0, 0, 0, 0}, // 578 { 0, 0, 0, 0, 0, 0, 6, -6, 0, 0, 0, 0, 0, 1}, // 579 { 0, 0, 0, 0, 0, 0, 0, 4, 0, -2, 0, 0, 0, 2}, // 580 { 0, 0, 0, 0, 0, 0, 0, 6, -4, 0, 0, 0, 0, 2}, // 581 { 0, 0, 0, 0, 0, 0, 3, -1, 0, 0, 0, 0, 0, 0}, // 582 { 0, 0, 0, 0, 0, 0, 3, -1, 0, 0, 0, 0, 0, 1}, // 583 { 0, 0, 0, 0, 0, 0, 3, -1, 0, 0, 0, 0, 0, 2}, // 584 { 0, 0, 0, 0, 0, 0, 0, 4, 0, -1, 0, 0, 0, 2}, // 585 { 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, -2, 0, 0, 2}, // 586 { 0, 0, 0, 0, 0, 0, 0, 5, -2, 0, 0, 0, 0, 2}, // 587 { 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0}, // 588 { 0, 0, 0, 0, 0, 0, 8, -9, 0, 0, 0, 0, 0, 0}, // 589 { 0, 0, 0, 0, 0, 0, 5, -4, 0, 0, 0, 0, 0, 2}, // 590 { 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 2}, // 591 { 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 1}, // 592 { 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 1}, // 593 { 0, 0, 0, 0, 0, 0, -7, 7, 0, 0, 0, 0, 0, 1}, // 594 { 0, 0, 0, 0, 0, 0, 7, -7, 0, 0, 0, 0, 0, 0}, // 595 { 0, 0, 0, 0, 0, 0, 4, -2, 0, 0, 0, 0, 0, 1}, // 596 { 0, 0, 0, 0, 0, 0, 4, -2, 0, 0, 0, 0, 0, 2}, // 597 { 0, 0, 0, 0, 0, 0, 4, -2, 0, 0, 0, 0, 0, 0}, // 598 { 0, 0, 0, 0, 0, 0, 4, -2, 0, 0, 0, 0, 0, 0}, // 599 { 0, 0, 0, 0, 0, 0, 0, 5, 0, -4, 0, 0, 0, 2}, // 600 { 0, 0, 0, 0, 0, 0, 0, 5, 0, -3, 0, 0, 0, 2}, // 601 { 0, 0, 0, 0, 0, 0, 0, 5, 0, -2, 0, 0, 0, 2}, // 602 { 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 2}, // 603 { 0, 0, 0, 0, 0, 0, -8, 8, 0, 0, 0, 0, 0, 1}, // 604 { 0, 0, 0, 0, 0, 0, 8, -8, 0, 0, 0, 0, 0, 0}, // 605 { 0, 0, 0, 0, 0, 0, 5, -3, 0, 0, 0, 0, 0, 1}, // 606 { 0, 0, 0, 0, 0, 0, 5, -3, 0, 0, 0, 0, 0, 2}, // 607 { 0, 0, 0, 0, 0, 0, -9, 9, 0, 0, 0, 0, 0, 1}, // 608 { 0, 0, 0, 0, 0, 0, -9, 9, 0, 0, 0, 0, 0, 1}, // 609 { 0, 0, 0, 0, 0, 0, -9, 9, 0, 0, 0, 0, 0, 1}, // 610 { 0, 0, 0, 0, 0, 0, 9, -9, 0, 0, 0, 0, 0, 0}, // 611 { 0, 0, 0, 0, 0, 0, 6, -4, 0, 0, 0, 0, 0, 1}, // 612 { 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 2}, // 613 { 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0}, // 614 { 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0}, // 615 { 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 1}, // 616 { 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 2}, // 617 { 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0}, // 618 { 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 1}, // 619 { 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 2}, // 620 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}, // 621 { 1, 0, 0, -2, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0}, // 622 { 1, 0, 0, -2, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0}, // 623 { 1, 0, 0, -2, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0}, // 624 { 1, 0, 0, -2, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0}, // 625 { -1, 0, 0, 0, 0, 0, 3, -3, 0, 0, 0, 0, 0, 0}, // 626 { -1, 0, 0, 0, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0}, // 627 { -1, 0, 0, 2, 0, 0, 0, 4, -8, 3, 0, 0, 0, 0}, // 628 { 1, 0, 0, -2, 0, 0, 0, 4, -8, 3, 0, 0, 0, 0}, // 629 { -2, 0, 0, 2, 0, 0, 0, 4, -8, 3, 0, 0, 0, 0}, // 630 { -1, 0, 0, 0, 0, 0, 0, 2, 0, -3, 0, 0, 0, 0}, // 631 { -1, 0, 0, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0}, // 632 { -1, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0}, // 633 { -1, 0, 0, 2, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0}, // 634 { 1, 0, -1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, // 635 { -1, 0, 0, 2, 0, 0, 0, 2, 0, -3, 0, 0, 0, 0}, // 636 { -2, 0, 0, 0, 0, 0, 0, 2, 0, -3, 0, 0, 0, 0}, // 637 { 1, 0, 0, 0, 0, 0, 0, 4, -8, 3, 0, 0, 0, 0}, // 638 { -1, 0, 1, -1, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0}, // 639 { 1, 0, 1, -1, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0}, // 640 { -1, 0, 0, 0, 0, 0, 0, 4, -8, 3, 0, 0, 0, 0}, // 641 { -1, 0, 0, 2, 1, 0, 0, 2, 0, -2, 0, 0, 0, 0}, // 642 { 0, 0, 0, 0, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0}, // 643 { -1, 0, 0, 2, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0}, // 644 { -1, 0, 0, 2, 0, 0, 3, -3, 0, 0, 0, 0, 0, 0}, // 645 { 1, 0, 0, -2, 1, 0, 0, -2, 0, 2, 0, 0, 0, 0}, // 646 { 1, 0, 2, -2, 2, 0, -3, 3, 0, 0, 0, 0, 0, 0}, // 647 { 1, 0, 2, -2, 2, 0, 0, -2, 0, 2, 0, 0, 0, 0}, // 648 { 1, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0}, // 649 { 1, 0, 0, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0}, // 650 { 0, 0, 0, -2, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0}, // 651 { 0, 0, 0, -2, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0}, // 652 { 0, 0, 2, 0, 2, 0, -2, 2, 0, 0, 0, 0, 0, 0}, // 653 { 0, 0, 2, 0, 2, 0, 0, -1, 0, 1, 0, 0, 0, 0}, // 654 { 0, 0, 2, 0, 2, 0, -1, 1, 0, 0, 0, 0, 0, 0}, // 655 { 0, 0, 2, 0, 2, 0, -2, 3, 0, 0, 0, 0, 0, 0}, // 656 { 0, 0, 0, 2, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0}, // 657 { 0, 0, 1, 1, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0}, // 658 { 1, 0, 2, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0}, // 659 { -1, 0, 2, 0, 2, 0, 10, -3, 0, 0, 0, 0, 0, 0}, // 660 { 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0}, // 661 { 1, 0, 2, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0}, // 662 { 0, 0, 2, 0, 2, 0, 0, 4, -8, 3, 0, 0, 0, 0}, // 663 { 0, 0, 2, 0, 2, 0, 0, -4, 8, -3, 0, 0, 0, 0}, // 664 { -1, 0, 2, 0, 2, 0, 0, -4, 8, -3, 0, 0, 0, 0}, // 665 { 2, 0, 2, -2, 2, 0, 0, -2, 0, 3, 0, 0, 0, 0}, // 666 { 1, 0, 2, 0, 1, 0, 0, -2, 0, 3, 0, 0, 0, 0}, // 667 { 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, // 668 { -1, 0, 2, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0}, // 669 { -2, 0, 2, 2, 2, 0, 0, 2, 0, -2, 0, 0, 0, 0}, // 670 { 0, 0, 2, 0, 2, 0, 2, -3, 0, 0, 0, 0, 0, 0}, // 671 { 0, 0, 2, 0, 2, 0, 1, -1, 0, 0, 0, 0, 0, 0}, // 672 { 0, 0, 2, 0, 2, 0, 0, 1, 0, -1, 0, 0, 0, 0}, // 673 { 0, 0, 2, 0, 2, 0, 2, -2, 0, 0, 0, 0, 0, 0}, // 674 { -1, 0, 2, 2, 2, 0, 0, -1, 0, 1, 0, 0, 0, 0}, // 675 { 1, 0, 2, 0, 2, 0, -1, 1, 0, 0, 0, 0, 0, 0}, // 676 { -1, 0, 2, 2, 2, 0, 0, 2, 0, -3, 0, 0, 0, 0}, // 677 { 2, 0, 2, 0, 2, 0, 0, 2, 0, -3, 0, 0, 0, 0}, // 678 { 1, 0, 2, 0, 2, 0, 0, -4, 8, -3, 0, 0, 0, 0}, // 679 { 1, 0, 2, 0, 2, 0, 0, 4, -8, 3, 0, 0, 0, 0}, // 680 { 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0}, // 681 { 0, 0, 2, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0}, // 682 { 2, 0, 2, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0}, // 683 { -1, 0, 2, 2, 2, 0, 0, 2, 0, -2, 0, 0, 0, 0}, // 684 { -1, 0, 2, 2, 2, 0, 3, -3, 0, 0, 0, 0, 0, 0}, // 685 { 1, 0, 2, 0, 2, 0, 1, -1, 0, 0, 0, 0, 0, 0}, // 686 { 0, 0, 2, 2, 2, 0, 0, 2, 0, -2, 0, 0, 0, 0} // 687 }; DebugAssert(which < 687, AipsError); return &(ARG[which][0]); } const Double* MeasTable::mulArg2000B(uInt which) { static const Double ARG[77][5] = { // Multiple of // L L' F D Omega { 0, 0, 0, 0, 1}, // 1 { 0, 0, 2, -2, 2}, // 2 { 0, 0, 2, 0, 2}, // 3 { 0, 0, 0, 0, 2}, // 4 { 0, 1, 0, 0, 0}, // 5 { 0, 1, 2, -2, 2}, // 6 { 1, 0, 0, 0, 0}, // 7 { 0, 0, 2, 0, 1}, // 8 { 1, 0, 2, 0, 2}, // 9 { 0, -1, 2, -2, 2}, // 10 { 0, 0, 2, -2, 1}, // 11 { -1, 0, 2, 0, 2}, // 12 { -1, 0, 0, 2, 0}, // 13 { 1, 0, 0, 0, 1}, // 14 { -1, 0, 0, 0, 1}, // 15 { -1, 0, 2, 2, 2}, // 16 { 1, 0, 2, 0, 1}, // 17 { -2, 0, 2, 0, 1}, // 18 { 0, 0, 0, 2, 0}, // 19 { 0, 0, 2, 2, 2}, // 20 { 0, -2, 2, -2, 2}, // 21 { -2, 0, 0, 2, 0}, // 22 { 2, 0, 2, 0, 2}, // 23 { 1, 0, 2, -2, 2}, // 24 { -1, 0, 2, 0, 1}, // 25 { 2, 0, 0, 0, 0}, // 26 { 0, 0, 2, 0, 0}, // 27 { 0, 1, 0, 0, 1}, // 28 { -1, 0, 0, 2, 1}, // 29 { 0, 2, 2, -2, 2}, // 30 { 0, 0, -2, 2, 0}, // 31 { 1, 0, 0, -2, 1}, // 32 { 0, -1, 0, 0, 1}, // 33 { -1, 0, 2, 2, 1}, // 34 { 0, 2, 0, 0, 0}, // 35 { 1, 0, 2, 2, 2}, // 36 { -2, 0, 2, 0, 0}, // 37 { 0, 1, 2, 0, 2}, // 38 { 0, 0, 2, 2, 1}, // 39 { 0, -1, 2, 0, 2}, // 40 { 0, 0, 0, 2, 1}, // 41 { 1, 0, 2, -2, 1}, // 42 { 2, 0, 2, -2, 2}, // 43 { -2, 0, 0, 2, 1}, // 44 { 2, 0, 2, 0, 1}, // 45 { 0, -1, 2, -2, 1}, // 46 { 0, 0, 0, -2, 1}, // 47 { -1, -1, 0, 2, 0}, // 48 { 2, 0, 0, -2, 1}, // 49 { 1, 0, 0, 2, 0}, // 50 { 0, 1, 2, -2, 1}, // 51 { 1, -1, 0, 0, 0}, // 52 { -2, 0, 2, 0, 2}, // 53 { 3, 0, 2, 0, 2}, // 54 { 0, -1, 0, 2, 0}, // 55 { 1, -1, 2, 0, 2}, // 56 { 0, 0, 0, 1, 0}, // 57 { -1, -1, 2, 2, 2}, // 58 { -1, 0, 2, 0, 0}, // 59 { 0, -1, 2, 2, 2}, // 60 { -2, 0, 0, 0, 1}, // 61 { 1, 1, 2, 0, 2}, // 62 { 2, 0, 0, 0, 1}, // 63 { -1, 1, 0, 1, 0}, // 64 { 1, 1, 0, 0, 0}, // 65 { 1, 0, 2, 0, 0}, // 66 { -1, 0, 2, -2, 1}, // 67 { 1, 0, 0, 0, 2}, // 68 { -1, 0, 0, 1, 0}, // 69 { 0, 0, 2, 1, 2}, // 70 { -1, 0, 2, 4, 2}, // 71 { -1, 1, 0, 1, 1}, // 72 { 0, -2, 2, -2, 1}, // 73 { 1, 0, 2, 2, 1}, // 74 { -2, 0, 2, 2, 2}, // 75 { -1, 0, 0, 0, 2}, // 76 { 1, 1, 2, -2, 2} // 77 }; DebugAssert(which < 77, AipsError); return &(ARG[which][0]); } const Double* MeasTable::mulArgEqEqCT2000(uInt which) { static const Double ARG[34][14] = { // L L' F D Om Me Ve E Ma Ju Sa Ur Ne pre { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 1 { 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 2 { 0, 0, 2, -2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 3 { 0, 0, 2, -2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 4 { 0, 0, 2, -2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 5 { 0, 0, 2, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 6 { 0, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 7 { 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 8 { 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 9 { 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 10 { 1, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 11 { 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 12 { 0, 1, 2, -2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 13 { 0, 1, 2, -2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 14 { 0, 0, 4, -4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 15 { 0, 0, 1, -1, 1, 0, -8, 12, 0, 0, 0, 0, 0, 0 }, // 16 { 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 17 { 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 18 { 1, 0, 2, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 19 { 1, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 20 { 0, 0, 2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 21 { 0, 1, -2, 2, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 22 { 0, 1, -2, 2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 23 { 0, 0, 0, 0, 0, 0, 8,-13, 0, 0, 0, 0, 0, -1 }, // 24 { 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 25 { 2, 0, -2, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 26 { 1, 0, 0, -2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 27 { 0, 1, 2, -2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 28 { 1, 0, 0, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 29 { 0, 0, 4, -2, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 30 { 0, 0, 2, -2, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 31 { 1, 0, -2, 0, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 32 { 1, 0, -2, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // 33 { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 } // T^1 term }; DebugAssert(which < 34, AipsError); return &(ARG[which][0]); } const Double* MeasTable::mulArg1950(uInt which) { static const Double ARG[69][5] = { {0 ,0 ,0 ,0 ,1 }, {0 ,0 ,0 ,0 ,2 }, {-2 ,0 ,2 ,0 ,1 }, {2 ,0 ,-2 ,0 ,0 }, {0 ,-2 ,2 ,-2 ,1 }, {-2 ,0 ,2 ,0 ,2 }, {1 ,-1 ,0 ,-1 ,0 }, {0 ,0 ,2 ,-2 ,2 }, {0 ,1 ,0 ,0 ,0 }, {0 ,1 ,2 ,-2 ,2 }, {0 ,-1 ,2 ,-2 ,2 }, {0 ,0 ,2 ,-2 ,1 }, {2 ,0 ,0 ,-2 ,0 }, {0 ,0 ,2 ,-2 ,0 }, {0 ,2 ,0 ,0 ,0 }, {0 ,1 ,0 ,0 ,1 }, {0 ,2 ,2 ,-2 ,2 }, {0 ,-1 ,0 ,0 ,1 }, {-2 ,0 ,0 ,2 ,1 }, {0 ,-1 ,2 ,-2 ,1 }, {2 ,0 ,0 ,-2 ,1 }, {0 ,1 ,2 ,-2 ,1 }, {1 ,0 ,0 ,-1 ,0 }, {0 ,0 ,2 ,0 ,2 }, {1 ,0 ,0 ,0 ,0 }, {0 ,0 ,2 ,0 ,1 }, {1 ,0 ,2 ,0 ,2 }, {1 ,0 ,0 ,-2 ,0 }, {-1 ,0 ,2 ,0 ,2 }, {0 ,0 ,0 ,2 ,0 }, {1 ,0 ,0 ,0 ,1 }, {-1 ,0 ,0 ,0 ,1 }, {-1 ,0 ,2 ,2 ,2 }, {1 ,0 ,2 ,0 ,1 }, {0 ,0 ,2 ,2 ,2 }, {2 ,0 ,0 ,0 ,0 }, {1 ,0 ,2 ,-2 ,2 }, {2 ,0 ,2 ,0 ,2 }, {0 ,0 ,2 ,0 ,0 }, {-1 ,0 ,2 ,0 ,1 }, {-1 ,0 ,0 ,2 ,1 }, {1 ,0 ,0 ,-2 ,1 }, {-1 ,0 ,2 ,2 ,1 }, {1 ,1 ,0 ,-2 ,0 }, {0 ,1 ,2 ,0 ,2 }, {1 ,0 ,0 ,2 ,0 }, {0 ,0 ,0 ,2 ,1 }, {0 ,-1 ,2 ,0 ,2 }, {1 ,0 ,2 ,2 ,2 }, {2 ,0 ,2 ,-2 ,2 }, {0 ,0 ,0 ,-2 ,1 }, {0 ,0 ,2 ,2 ,1 }, {1 ,0 ,2 ,-2 ,1 }, {0 ,0 ,0 ,1 ,0 }, {0 ,1 ,0 ,-2 ,0 }, {1 ,-1 ,0 ,0 ,0 }, {1 ,0 ,-2 ,0 ,0 }, {2 ,0 ,2 ,0 ,1 }, {1 ,0 ,2 ,0 ,0 }, {1 ,1 ,0 ,0 ,0 }, {1 ,-1 ,2 ,0 ,2 }, {-2 ,0 ,0 ,0 ,1 }, {-1 ,0 ,2 ,-2 ,1 }, {2 ,0 ,0 ,0 ,1 }, {-1 ,-1 ,2 ,2 ,2 }, {0 ,-1 ,2 ,2 ,2 }, {1 ,0 ,0 ,0 ,2 }, {1 ,1 ,2 ,0 ,2 }, {3 ,0 ,2 ,0 ,2 } }; DebugAssert(which < 69, AipsError); return &(ARG[which][0]); } std::shared_ptr> MeasTable::mulSC(Double time, Double epsilon) { return theirMulSC.getArray (time, epsilon); } std::shared_ptr> MeasTable::mulSC2000A(Double time, Double epsilon) { return theirMulSC2000A.getArray (time, epsilon); } std::shared_ptr> MeasTable::mulSC2000B(Double time, Double epsilon) { return theirMulSC2000B.getArray (time, epsilon); } const Double* MeasTable::mulPlanSC2000A(uInt which) { // Luni-Solar nutation coefficients, unit 1e-7 arcsec static const Double MULSC[687][4] = { // Longitude Obliquity // sin cos sin cos { 1440, 0, 0, 0}, // 1 { 56, -117, -42, -40}, // 2 { 125, -43, 0, -54}, // 3 { 0, 5, 0, 0}, // 4 { 3, -7, -3, 0}, // 5 { 3, 0, 0, -2}, // 6 { -114, 0, 0, 61}, // 7 { -219, 89, 0, 0}, // 8 { -3, 0, 0, 0}, // 9 { -462, 1604, 0, 0}, // 10 { 99, 0, 0, -53}, // 11 { -3, 0, 0, 2}, // 12 { 0, 6, 2, 0}, // 13 { 3, 0, 0, 0}, // 14 { -12, 0, 0, 0}, // 15 { 14, -218, 117, 8}, // 16 { 31, -481, -257, -17}, // 17 { -491, 128, 0, 0}, // 18 { -3084, 5123, 2735, 1647}, // 19 { -1444, 2409, -1286, -771}, // 20 { 11, -24, -11, -9}, // 21 { 26, -9, 0, 0}, // 22 { 103, -60, 0, 0}, // 23 { 0, -13, -7, 0}, // 24 { -26, -29, -16, 14}, // 25 { 9, -27, -14, -5}, // 26 { 12, 0, 0, -6}, // 27 { -7, 0, 0, 0}, // 28 { 0, 24, 0, 0}, // 29 { 284, 0, 0, -151}, // 30 { 226, 101, 0, 0}, // 31 { 0, -8, -2, 0}, // 32 { 0, -6, -3, 0}, // 33 { 5, 0, 0, -3}, // 34 { -41, 175, 76, 17}, // 35 { 0, 15, 6, 0}, // 36 { 425, 212, -133, 269}, // 37 { 1200, 598, 319, -641}, // 38 { 235, 334, 0, 0}, // 39 { 11, -12, -7, -6}, // 40 { 5, -6, 3, 3}, // 41 { -5, 0, 0, 3}, // 42 { 6, 0, 0, -3}, // 43 { 15, 0, 0, 0}, // 44 { 13, 0, 0, -7}, // 45 { -6, -9, 0, 0}, // 46 { 266, -78, 0, 0}, // 47 { -460, -435, -232, 246}, // 48 { 0, 15, 7, 0}, // 49 { -3, 0, 0, 2}, // 50 { 0, 131, 0, 0}, // 51 { 4, 0, 0, 0}, // 52 { 0, 3, 0, 0}, // 53 { 0, 4, 2, 0}, // 54 { 0, 3, 0, 0}, // 55 { -17, -19, -10, 9}, // 56 { -9, -11, 6, -5}, // 57 { -6, 0, 0, 3}, // 58 { -16, 8, 0, 0}, // 59 { 0, 3, 0, 0}, // 60 { 11, 24, 11, -5}, // 61 { -3, -4, -2, 1}, // 62 { 3, 0, 0, -1}, // 63 { 0, -8, -4, 0}, // 64 { 0, 3, 0, 0}, // 65 { 0, 5, 0, 0}, // 66 { 0, 3, 2, 0}, // 67 { -6, 4, 2, 3}, // 68 { -3, -5, 0, 0}, // 69 { -5, 0, 0, 2}, // 70 { 4, 24, 13, -2}, // 71 { -42, 20, 0, 0}, // 72 { -10, 233, 0, 0}, // 73 { -3, 0, 0, 1}, // 74 { 78, -18, 0, 0}, // 75 { 0, 3, 1, 0}, // 76 { 0, -3, -1, 0}, // 77 { 0, -4, -2, 1}, // 78 { 0, -8, -4, -1}, // 79 { 0, -5, 3, 0}, // 80 { -7, 0, 0, 3}, // 81 { -14, 8, 3, 6}, // 82 { 0, 8, -4, 0}, // 83 { 0, 19, 10, 0}, // 84 { 45, -22, 0, 0}, // 85 { -3, 0, 0, 0}, // 86 { 0, -3, 0, 0}, // 87 { 0, 3, 0, 0}, // 88 { 3, 5, 3, -2}, // 89 { 89, -16, -9, -48}, // 90 { 0, 3, 0, 0}, // 91 { -3, 7, 4, 2}, // 92 { -349, -62, 0, 0}, // 93 { -15, 22, 0, 0}, // 94 { -3, 0, 0, 0}, // 95 { -53, 0, 0, 0}, // 96 { 5, 0, 0, -3}, // 97 { 0, -8, 0, 0}, // 98 { 15, -7, -4, -8}, // 99 { -3, 0, 0, 1}, // 100 { -21, -78, 0, 0}, // 101 { 20, -70, -37, -11}, // 102 { 0, 6, 3, 0}, // 103 { 5, 3, 2, -2}, // 104 { -17, -4, -2, 9}, // 105 { 0, 6, 3, 0}, // 106 { 32, 15, -8, 17}, // 107 { 174, 84, 45, -93}, // 108 { 11, 56, 0, 0}, // 109 { -66, -12, -6, 35}, // 110 { 47, 8, 4, -25}, // 111 { 0, 8, 4, 0}, // 112 { 10, -22, -12, -5}, // 113 { -3, 0, 0, 2}, // 114 { -24, 12, 0, 0}, // 115 { 5, -6, 0, 0}, // 116 { 3, 0, 0, -2}, // 117 { 4, 3, 1, -2}, // 118 { 0, 29, 15, 0}, // 119 { -5, -4, -2, 2}, // 120 { 8, -3, -1, -5}, // 121 { 0, -3, 0, 0}, // 122 { 10, 0, 0, 0}, // 123 { 3, 0, 0, -2}, // 124 { -5, 0, 0, 3}, // 125 { 46, 66, 35, -25}, // 126 { -14, 7, 0, 0}, // 127 { 0, 3, 2, 0}, // 128 { -5, 0, 0, 0}, // 129 { -68, -34, -18, 36}, // 130 { 0, 14, 7, 0}, // 131 { 10, -6, -3, -5}, // 132 { -5, -4, -2, 3}, // 133 { -3, 5, 2, 1}, // 134 { 76, 17, 9, -41}, // 135 { 84, 298, 159, -45}, // 136 { 3, 0, 0, -1}, // 137 { -3, 0, 0, 2}, // 138 { -3, 0, 0, 1}, // 139 { -82, 292, 156, 44}, // 140 { -73, 17, 9, 39}, // 141 { -9, -16, 0, 0}, // 142 { 3, 0, -1, -2}, // 143 { -3, 0, 0, 0}, // 144 { -9, -5, -3, 5}, // 145 { -439, 0, 0, 0}, // 146 { 57, -28, -15, -30}, // 147 { 0, -6, -3, 0}, // 148 { -4, 0, 0, 2}, // 149 { -40, 57, 30, 21}, // 150 { 23, 7, 3, -13}, // 151 { 273, 80, 43, -146}, // 152 { -449, 430, 0, 0}, // 153 { -8, -47, -25, 4}, // 154 { 6, 47, 25, -3}, // 155 { 0, 23, 13, 0}, // 156 { -3, 0, 0, 2}, // 157 { 3, -4, -2, -2}, // 158 { -48, -110, -59, 26}, // 159 { 51, 114, 61, -27}, // 160 { -133, 0, 0, 57}, // 161 { 0, 4, 0, 0}, // 162 { -21, -6, -3, 11}, // 163 { 0, -3, -1, 0}, // 164 { -11, -21, -11, 6}, // 165 { -18, -436, -233, 9}, // 166 { 35, -7, 0, 0}, // 167 { 0, 5, 3, 0}, // 168 { 11, -3, -1, -6}, // 169 { -5, -3, -1, 3}, // 170 { -53, -9, -5, 28}, // 171 { 0, 3, 2, 1}, // 172 { 4, 0, 0, -2}, // 173 { 0, -4, 0, 0}, // 174 { -50, 194, 103, 27}, // 175 { -13, 52, 28, 7}, // 176 { -91, 248, 0, 0}, // 177 { 6, 49, 26, -3}, // 178 { -6, -47, -25, 3}, // 179 { 0, 5, 3, 0}, // 180 { 52, 23, 10, -23}, // 181 { -3, 0, 0, 1}, // 182 { 0, 5, 3, 0}, // 183 { -4, 0, 0, 0}, // 184 { -4, 8, 3, 2}, // 185 { 10, 0, 0, 0}, // 186 { 3, 0, 0, -2}, // 187 { 0, 8, 4, 0}, // 188 { 0, 8, 4, 1}, // 189 { -4, 0, 0, 0}, // 190 { -4, 0, 0, 0}, // 191 { -8, 4, 2, 4}, // 192 { 8, -4, -2, -4}, // 193 { 0, 15, 7, 0}, // 194 { -138, 0, 0, 0}, // 195 { 0, -7, -3, 0}, // 196 { 0, -7, -3, 0}, // 197 { 54, 0, 0, -29}, // 198 { 0, 10, 4, 0}, // 199 { -7, 0, 0, 3}, // 200 { -37, 35, 19, 20}, // 201 { 0, 4, 0, 0}, // 202 { -4, 9, 0, 0}, // 203 { 8, 0, 0, -4}, // 204 { -9, -14, -8, 5}, // 205 { -3, -9, -5, 3}, // 206 { -145, 47, 0, 0}, // 207 { -10, 40, 21, 5}, // 208 { 11, -49, -26, -7}, // 209 { -2150, 0, 0, 932}, // 210 { -12, 0, 0, 5}, // 211 { 85, 0, 0, -37}, // 212 { 4, 0, 0, -2}, // 213 { 3, 0, 0, -2}, // 214 { -86, 153, 0, 0}, // 215 { -6, 9, 5, 3}, // 216 { 9, -13, -7, -5}, // 217 { -8, 12, 6, 4}, // 218 { -51, 0, 0, 22}, // 219 { -11, -268, -116, 5}, // 220 { 0, 12, 5, 0}, // 221 { 0, 7, 3, 0}, // 222 { 31, 6, 3, -17}, // 223 { 140, 27, 14, -75}, // 224 { 57, 11, 6, -30}, // 225 { -14, -39, 0, 0}, // 226 { 0, -6, -2, 0}, // 227 { 4, 15, 8, -2}, // 228 { 0, 4, 0, 0}, // 229 { -3, 0, 0, 1}, // 230 { 0, 11, 5, 0}, // 231 { 9, 6, 0, 0}, // 232 { -4, 10, 4, 2}, // 233 { 5, 3, 0, 0}, // 234 { 16, 0, 0, -9}, // 235 { -3, 0, 0, 0}, // 236 { 0, 3, 2, -1}, // 237 { 7, 0, 0, -3}, // 238 { -25, 22, 0, 0}, // 239 { 42, 223, 119, -22}, // 240 { -27, -143, -77, 14}, // 241 { 9, 49, 26, -5}, // 242 { -1166, 0, 0, 505}, // 243 { -5, 0, 0, 2}, // 244 { -6, 0, 0, 3}, // 245 { -8, 0, 1, 4}, // 246 { 0, -4, 0, 0}, // 247 { 117, 0, 0, -63}, // 248 { -4, 8, 4, 2}, // 249 { 3, 0, 0, -2}, // 250 { -5, 0, 0, 2}, // 251 { 0, 31, 0, 0}, // 252 { -5, 0, 1, 3}, // 253 { 4, 0, 0, -2}, // 254 { -4, 0, 0, 2}, // 255 { -24, -13, -6, 10}, // 256 { 3, 0, 0, 0}, // 257 { 0, -32, -17, 0}, // 258 { 8, 12, 5, -3}, // 259 { 3, 0, 0, -1}, // 260 { 7, 13, 0, 0}, // 261 { -3, 16, 0, 0}, // 262 { 50, 0, 0, -27}, // 263 { 0, -5, -3, 0}, // 264 { 13, 0, 0, 0}, // 265 { 0, 5, 3, 1}, // 266 { 24, 5, 2, -11}, // 267 { 5, -11, -5, -2}, // 268 { 30, -3, -2, -16}, // 269 { 18, 0, 0, -9}, // 270 { 8, 614, 0, 0}, // 271 { 3, -3, -1, -2}, // 272 { 6, 17, 9, -3}, // 273 { -3, -9, -5, 2}, // 274 { 0, 6, 3, -1}, // 275 { -127, 21, 9, 55}, // 276 { 3, 5, 0, 0}, // 277 { -6, -10, -4, 3}, // 278 { 5, 0, 0, 0}, // 279 { 16, 9, 4, -7}, // 280 { 3, 0, 0, -2}, // 281 { 0, 22, 0, 0}, // 282 { 0, 19, 10, 0}, // 283 { 7, 0, 0, -4}, // 284 { 0, -5, -2, 0}, // 285 { 0, 3, 1, 0}, // 286 { -9, 3, 1, 4}, // 287 { 17, 0, 0, -7}, // 288 { 0, -3, -2, -1}, // 289 { -20, 34, 0, 0}, // 290 { -10, 0, 1, 5}, // 291 { -4, 0, 0, 2}, // 292 { 22, -87, 0, 0}, // 293 { -4, 0, 0, 2}, // 294 { -3, -6, -2, 1}, // 295 { -16, -3, -1, 7}, // 296 { 0, -3, -2, 0}, // 297 { 4, 0, 0, 0}, // 298 { -68, 39, 0, 0}, // 299 { 27, 0, 0, -14}, // 300 { 0, -4, 0, 0}, // 301 { -25, 0, 0, 0}, // 302 { -12, -3, -2, 6}, // 303 { 3, 0, 0, -1}, // 304 { 3, 66, 29, -1}, // 305 { 490, 0, 0, -213}, // 306 { -22, 93, 49, 12}, // 307 { -7, 28, 15, 4}, // 308 { -3, 13, 7, 2}, // 309 { -46, 14, 0, 0}, // 310 { -5, 0, 0, 0}, // 311 { 2, 1, 0, 0}, // 312 { 0, -3, 0, 0}, // 313 { -28, 0, 0, 15}, // 314 { 5, 0, 0, -2}, // 315 { 0, 3, 0, 0}, // 316 { -11, 0, 0, 5}, // 317 { 0, 3, 1, 0}, // 318 { -3, 0, 0, 1}, // 319 { 25, 106, 57, -13}, // 320 { 5, 21, 11, -3}, // 321 { 1485, 0, 0, 0}, // 322 { -7, -32, -17, 4}, // 323 { 0, 5, 3, 0}, // 324 { -6, -3, -2, 3}, // 325 { 30, -6, -2, -13}, // 326 { -4, 4, 0, 0}, // 327 { -19, 0, 0, 10}, // 328 { 0, 4, 2, -1}, // 329 { 0, 3, 0, 0}, // 330 { 4, 0, 0, -2}, // 331 { 0, -3, -1, 0}, // 332 { -3, 0, 0, 0}, // 333 { 5, 3, 1, -2}, // 334 { 0, 11, 0, 0}, // 335 { 118, 0, 0, -52}, // 336 { 0, -5, -3, 0}, // 337 { -28, 36, 0, 0}, // 338 { 5, -5, 0, 0}, // 339 { 14, -59, -31, -8}, // 340 { 0, 9, 5, 1}, // 341 { -458, 0, 0, 198}, // 342 { 0, -45, -20, 0}, // 343 { 9, 0, 0, -5}, // 344 { 0, -3, 0, 0}, // 345 { 0, -4, -2, -1}, // 346 { 11, 0, 0, -6}, // 347 { 6, 0, 0, -2}, // 348 { -16, 23, 0, 0}, // 349 { 0, -4, -2, 0}, // 350 { -5, 0, 0, 2}, // 351 { -166, 269, 0, 0}, // 352 { 15, 0, 0, -8}, // 353 { 10, 0, 0, -4}, // 354 { -78, 45, 0, 0}, // 355 { 0, -5, -2, 0}, // 356 { 7, 0, 0, -4}, // 357 { -5, 328, 0, 0}, // 358 { 3, 0, 0, -2}, // 359 { 5, 0, 0, -2}, // 360 { 0, 3, 1, 0}, // 361 { -3, 0, 0, 0}, // 362 { -3, 0, 0, 0}, // 363 { 0, -4, -2, 0}, // 364 { -1223, -26, 0, 0}, // 365 { 0, 7, 3, 0}, // 366 { 3, 0, 0, 0}, // 367 { 0, 3, 2, 0}, // 368 { -6, 20, 0, 0}, // 369 { -368, 0, 0, 0}, // 370 { -75, 0, 0, 0}, // 371 { 11, 0, 0, -6}, // 372 { 3, 0, 0, -2}, // 373 { -3, 0, 0, 1}, // 374 { -13, -30, 0, 0}, // 375 { 21, 3, 0, 0}, // 376 { -3, 0, 0, 1}, // 377 { -4, 0, 0, 2}, // 378 { 8, -27, 0, 0}, // 379 { -19, -11, 0, 0}, // 380 { -4, 0, 0, 2}, // 381 { 0, 5, 2, 0}, // 382 { -6, 0, 0, 2}, // 383 { -8, 0, 0, 0}, // 384 { -1, 0, 0, 0}, // 385 { -14, 0, 0, 6}, // 386 { 6, 0, 0, 0}, // 387 { -74, 0, 0, 32}, // 388 { 0, -3, -1, 0}, // 389 { 4, 0, 0, -2}, // 390 { 8, 11, 0, 0}, // 391 { 0, 3, 2, 0}, // 392 { -262, 0, 0, 114}, // 393 { 0, -4, 0, 0}, // 394 { -7, 0, 0, 4}, // 395 { 0, -27, -12, 0}, // 396 { -19, -8, -4, 8}, // 397 { 202, 0, 0, -87}, // 398 { -8, 35, 19, 5}, // 399 { 0, 4, 2, 0}, // 400 { 16, -5, 0, 0}, // 401 { 5, 0, 0, -3}, // 402 { 0, -3, 0, 0}, // 403 { 1, 0, 0, 0}, // 404 { -35, -48, -21, 15}, // 405 { -3, -5, -2, 1}, // 406 { 6, 0, 0, -3}, // 407 { 3, 0, 0, -1}, // 408 { 0, -5, 0, 0}, // 409 { 12, 55, 29, -6}, // 410 { 0, 5, 3, 0}, // 411 { -598, 0, 0, 0}, // 412 { -3, -13, -7, 1}, // 413 { -5, -7, -3, 2}, // 414 { 3, 0, 0, -1}, // 415 { 5, -7, 0, 0}, // 416 { 4, 0, 0, -2}, // 417 { 16, -6, 0, 0}, // 418 { 8, -3, 0, 0}, // 419 { 8, -31, -16, -4}, // 420 { 0, 3, 1, 0}, // 421 { 113, 0, 0, -49}, // 422 { 0, -24, -10, 0}, // 423 { 4, 0, 0, -2}, // 424 { 27, 0, 0, 0}, // 425 { -3, 0, 0, 1}, // 426 { 0, -4, -2, 0}, // 427 { 5, 0, 0, -2}, // 428 { 0, -3, 0, 0}, // 429 { -13, 0, 0, 6}, // 430 { 5, 0, 0, -2}, // 431 { -18, -10, -4, 8}, // 432 { -4, -28, 0, 0}, // 433 { -5, 6, 3, 2}, // 434 { -3, 0, 0, 1}, // 435 { -5, -9, -4, 2}, // 436 { 17, 0, 0, -7}, // 437 { 11, 4, 0, 0}, // 438 { 0, -6, -2, 0}, // 439 { 83, 15, 0, 0}, // 440 { -4, 0, 0, 2}, // 441 { 0, -114, -49, 0}, // 442 { 117, 0, 0, -51}, // 443 { -5, 19, 10, 2}, // 444 { -3, 0, 0, 0}, // 445 { -3, 0, 0, 2}, // 446 { 0, -3, -1, 0}, // 447 { 3, 0, 0, 0}, // 448 { 0, -6, -2, 0}, // 449 { 393, 3, 0, 0}, // 450 { -4, 21, 11, 2}, // 451 { -6, 0, -1, 3}, // 452 { -3, 8, 4, 1}, // 453 { 8, 0, 0, 0}, // 454 { 18, -29, -13, -8}, // 455 { 8, 34, 18, -4}, // 456 { 89, 0, 0, 0}, // 457 { 3, 12, 6, -1}, // 458 { 54, -15, -7, -24}, // 459 { 0, 3, 0, 0}, // 460 { 3, 0, 0, -1}, // 461 { 0, 35, 0, 0}, // 462 { -154, -30, -13, 67}, // 463 { 15, 0, 0, 0}, // 464 { 0, 4, 2, 0}, // 465 { 0, 9, 0, 0}, // 466 { 80, -71, -31, -35}, // 467 { 0, -20, -9, 0}, // 468 { 11, 5, 2, -5}, // 469 { 61, -96, -42, -27}, // 470 { 14, 9, 4, -6}, // 471 { -11, -6, -3, 5}, // 472 { 0, -3, -1, 0}, // 473 { 123, -415, -180, -53}, // 474 { 0, 0, 0, -35}, // 475 { -5, 0, 0, 0}, // 476 { 7, -32, -17, -4}, // 477 { 0, -9, -5, 0}, // 478 { 0, -4, 2, 0}, // 479 { -89, 0, 0, 38}, // 480 { 0, -86, -19, -6}, // 481 { 0, 0, -19, 6}, // 482 { -123, -416, -180, 53}, // 483 { 0, -3, -1, 0}, // 484 { 12, -6, -3, -5}, // 485 { -13, 9, 4, 6}, // 486 { 0, -15, -7, 0}, // 487 { 3, 0, 0, -1}, // 488 { -62, -97, -42, 27}, // 489 { -11, 5, 2, 5}, // 490 { 0, -19, -8, 0}, // 491 { -3, 0, 0, 1}, // 492 { 0, 4, 2, 0}, // 493 { 0, 3, 0, 0}, // 494 { 0, 4, 2, 0}, // 495 { -85, -70, -31, 37}, // 496 { 163, -12, -5, -72}, // 497 { -63, -16, -7, 28}, // 498 { -21, -32, -14, 9}, // 499 { 0, -3, -1, 0}, // 500 { 3, 0, 0, -2}, // 501 { 0, 8, 0, 0}, // 502 { 3, 10, 4, -1}, // 503 { 3, 0, 0, -1}, // 504 { 0, -7, -3, 0}, // 505 { 0, -4, -2, 0}, // 506 { 6, 19, 0, 0}, // 507 { 5, -173, -75, -2}, // 508 { 0, -7, -3, 0}, // 509 { 7, -12, -5, -3}, // 510 { -3, 0, 0, 2}, // 511 { 3, -4, -2, -1}, // 512 { 74, 0, 0, -32}, // 513 { -3, 12, 6, 2}, // 514 { 26, -14, -6, -11}, // 515 { 19, 0, 0, -8}, // 516 { 6, 24, 13, -3}, // 517 { 83, 0, 0, 0}, // 518 { 0, -10, -5, 0}, // 519 { 11, -3, -1, -5}, // 520 { 3, 0, 1, -1}, // 521 { 3, 0, 0, -1}, // 522 { -4, 0, 0, 0}, // 523 { 5, -23, -12, -3}, // 524 { -339, 0, 0, 147}, // 525 { 0, -10, -5, 0}, // 526 { 5, 0, 0, 0}, // 527 { 3, 0, 0, -1}, // 528 { 0, -4, -2, 0}, // 529 { 18, -3, 0, 0}, // 530 { 9, -11, -5, -4}, // 531 { -8, 0, 0, 4}, // 532 { 3, 0, 0, -1}, // 533 { 0, 9, 0, 0}, // 534 { 6, -9, -4, -2}, // 535 { -4, -12, 0, 0}, // 536 { 67, -91, -39, -29}, // 537 { 30, -18, -8, -13}, // 538 { 0, 0, 0, 0}, // 539 { 0, -114, -50, 0}, // 540 { 0, 0, 0, 23}, // 541 { 517, 16, 7, -224}, // 542 { 0, -7, -3, 0}, // 543 { 143, -3, -1, -62}, // 544 { 29, 0, 0, -13}, // 545 { -4, 0, 0, 2}, // 546 { -6, 0, 0, 3}, // 547 { 5, 12, 5, -2}, // 548 { -25, 0, 0, 11}, // 549 { -3, 0, 0, 1}, // 550 { 0, 4, 2, 0}, // 551 { -22, 12, 5, 10}, // 552 { 50, 0, 0, -22}, // 553 { 0, 7, 4, 0}, // 554 { 0, 3, 1, 0}, // 555 { -4, 4, 2, 2}, // 556 { -5, -11, -5, 2}, // 557 { 0, 4, 2, 0}, // 558 { 4, 17, 9, -2}, // 559 { 59, 0, 0, 0}, // 560 { 0, -4, -2, 0}, // 561 { -8, 0, 0, 4}, // 562 { -3, 0, 0, 0}, // 563 { 4, -15, -8, -2}, // 564 { 370, -8, 0, -160}, // 565 { 0, 0, -3, 0}, // 566 { 0, 3, 1, 0}, // 567 { -6, 3, 1, 3}, // 568 { 0, 6, 0, 0}, // 569 { -10, 0, 0, 4}, // 570 { 0, 9, 4, 0}, // 571 { 4, 17, 7, -2}, // 572 { 34, 0, 0, -15}, // 573 { 0, 5, 3, 0}, // 574 { -5, 0, 0, 2}, // 575 { -37, -7, -3, 16}, // 576 { 3, 13, 7, -2}, // 577 { 40, 0, 0, 0}, // 578 { 0, -3, -2, 0}, // 579 { -184, -3, -1, 80}, // 580 { -3, 0, 0, 1}, // 581 { -3, 0, 0, 0}, // 582 { 0, -10, -6, -1}, // 583 { 31, -6, 0, -13}, // 584 { -3, -32, -14, 1}, // 585 { -7, 0, 0, 3}, // 586 { 0, -8, -4, 0}, // 587 { 3, -4, 0, 0}, // 588 { 0, 4, 0, 0}, // 589 { 0, 3, 1, 0}, // 590 { 19, -23, -10, 2}, // 591 { 0, 0, 0, -10}, // 592 { 0, 3, 2, 0}, // 593 { 0, 9, 5, -1}, // 594 { 28, 0, 0, 0}, // 595 { 0, -7, -4, 0}, // 596 { 8, -4, 0, -4}, // 597 { 0, 0, -2, 0}, // 598 { 0, 3, 0, 0}, // 599 { -3, 0, 0, 1}, // 600 { -9, 0, 1, 4}, // 601 { 3, 12, 5, -1}, // 602 { 17, -3, -1, 0}, // 603 { 0, 7, 4, 0}, // 604 { 19, 0, 0, 0}, // 605 { 0, -5, -3, 0}, // 606 { 14, -3, 0, -1}, // 607 { 0, 0, -1, 0}, // 608 { 0, 0, 0, -5}, // 609 { 0, 5, 3, 0}, // 610 { 13, 0, 0, 0}, // 611 { 0, -3, -2, 0}, // 612 { 2, 9, 4, 3}, // 613 { 0, 0, 0, -4}, // 614 { 8, 0, 0, 0}, // 615 { 0, 4, 2, 0}, // 616 { 6, 0, 0, -3}, // 617 { 6, 0, 0, 0}, // 618 { 0, 3, 1, 0}, // 619 { 5, 0, 0, -2}, // 620 { 3, 0, 0, -1}, // 621 { -3, 0, 0, 0}, // 622 { 6, 0, 0, 0}, // 623 { 7, 0, 0, 0}, // 624 { -4, 0, 0, 0}, // 625 { 4, 0, 0, 0}, // 626 { 6, 0, 0, 0}, // 627 { 0, -4, 0, 0}, // 628 { 0, -4, 0, 0}, // 629 { 5, 0, 0, 0}, // 630 { -3, 0, 0, 0}, // 631 { 4, 0, 0, 0}, // 632 { -5, 0, 0, 0}, // 633 { 4, 0, 0, 0}, // 634 { 0, 3, 0, 0}, // 635 { 13, 0, 0, 0}, // 636 { 21, 11, 0, 0}, // 637 { 0, -5, 0, 0}, // 638 { 0, -5, -2, 0}, // 639 { 0, 5, 3, 0}, // 640 { 0, -5, 0, 0}, // 641 { -3, 0, 0, 2}, // 642 { 20, 10, 0, 0}, // 643 { -34, 0, 0, 0}, // 644 { -19, 0, 0, 0}, // 645 { 3, 0, 0, -2}, // 646 { -3, 0, 0, 1}, // 647 { -6, 0, 0, 3}, // 648 { -4, 0, 0, 0}, // 649 { 3, 0, 0, 0}, // 650 { 3, 0, 0, 0}, // 651 { 4, 0, 0, 0}, // 652 { 3, 0, 0, -1}, // 653 { 6, 0, 0, -3}, // 654 { -8, 0, 0, 3}, // 655 { 0, 3, 1, 0}, // 656 { -3, 0, 0, 0}, // 657 { 0, -3, -2, 0}, // 658 { 126, -63, -27, -55}, // 659 { -5, 0, 1, 2}, // 660 { -3, 28, 15, 2}, // 661 { 5, 0, 1, -2}, // 662 { 0, 9, 4, 1}, // 663 { 0, 9, 4, -1}, // 664 { -126, -63, -27, 55}, // 665 { 3, 0, 0, -1}, // 666 { 21, -11, -6, -11}, // 667 { 0, -4, 0, 0}, // 668 { -21, -11, -6, 11}, // 669 { -3, 0, 0, 1}, // 670 { 0, 3, 1, 0}, // 671 { 8, 0, 0, -4}, // 672 { -6, 0, 0, 3}, // 673 { -3, 0, 0, 1}, // 674 { 3, 0, 0, -1}, // 675 { -3, 0, 0, 1}, // 676 { -5, 0, 0, 2}, // 677 { 24, -12, -5, -11}, // 678 { 0, 3, 1, 0}, // 679 { 0, 3, 1, 0}, // 680 { 0, 3, 2, 0}, // 681 { -24, -12, -5, 10}, // 682 { 4, 0, -1, -2}, // 683 { 13, 0, 0, -6}, // 684 { 7, 0, 0, -3}, // 685 { 3, 0, 0, -1}, // 686 { 3, 0, 0, -1} // 687 }; DebugAssert(which < 687, AipsError); return &(MULSC[which][0]); } const Double* MeasTable::mulSCEqEqCT2000(uInt which) { // Equation of Equinox complementary terms static const Double MULSC[34][2] = { // sin cos { +2640.96e-6, -0.39e-6 }, // 1 { +63.52e-6, -0.02e-6 }, // 2 { +11.75e-6, +0.01e-6 }, // 3 { +11.21e-6, +0.01e-6 }, // 4 { -4.55e-6, +0.00e-6 }, // 5 { +2.02e-6, +0.00e-6 }, // 6 { +1.98e-6, +0.00e-6 }, // 7 { -1.72e-6, +0.00e-6 }, // 8 { -1.41e-6, -0.01e-6 }, // 9 { -1.26e-6, -0.01e-6 }, // 10 { -0.63e-6, +0.00e-6 }, // 11 { -0.63e-6, +0.00e-6 }, // 12 { +0.46e-6, +0.00e-6 }, // 13 { +0.45e-6, +0.00e-6 }, // 14 { +0.36e-6, +0.00e-6 }, // 15 { -0.24e-6, -0.12e-6 }, // 16 { +0.32e-6, +0.00e-6 }, // 17 { +0.28e-6, +0.00e-6 }, // 18 { +0.27e-6, +0.00e-6 }, // 19 { +0.26e-6, +0.00e-6 }, // 20 { -0.21e-6, +0.00e-6 }, // 21 { +0.19e-6, +0.00e-6 }, // 22 { +0.18e-6, +0.00e-6 }, // 23 { -0.10e-6, +0.05e-6 }, // 24 { +0.15e-6, +0.00e-6 }, // 25 { -0.14e-6, +0.00e-6 }, // 26 { +0.14e-6, +0.00e-6 }, // 27 { -0.14e-6, +0.00e-6 }, // 28 { +0.14e-6, +0.00e-6 }, // 29 { +0.13e-6, +0.00e-6 }, // 30 { -0.11e-6, +0.00e-6 }, // 31 { +0.11e-6, +0.00e-6 }, // 32 { +0.11e-6, +0.00e-6 }, // 33 { -0.87e-6, +0.00e-6 } // T^1 term }; DebugAssert(which < 34, AipsError); return &(MULSC[which][0]); } std::shared_ptr> MeasTable::mulSC1950(Double time, Double epsilon) { return theirMulSC1950.getArray (time, epsilon); } Double MeasTable::dPsiEps(uInt which, Double T) { #if defined(USE_THREADS) static std::atomic msgDone; #else static Bool msgDone; #endif DebugAssert(which < 2, AipsError); Double r = 0; MeasIERS::Types type = (which==1 ? MeasIERS::dEps : MeasIERS::dPsi); if (!MeasIERS::get(r, MeasIERS::MEASURED, type, T)) { // It is harmless if the message accidentally appears multiple times. if (!msgDone) { LogIO os(LogOrigin("MeasTable", "dPsiEps(uInt, Double)", WHERE)); os << LogIO::NORMAL3 << "High precision nutation information not available." << LogIO::POST; msgDone = True; } } /// cout << "psieps " << r << endl; return (r * C::arcsec); } // Planetary data Vector MeasTable::Planetary(MeasTable::Types which, Double T) { static MeasJPL::Files fil(MeasJPL::DE200); std::call_once(theirPlanetaryInitOnceFlag, calcPlanetary, &fil); Vector res(6); if (!MeasJPL::get(res, fil, (MeasJPL::Types)which, MVEpoch(T))) { const String tnam[2] = {"DE200", "DE405"}; LogIO os(LogOrigin("MeasTable", "Planetary(MeasTable::Types, Double)", WHERE)); os << "Cannot find the planetary data for MeasJPL object number " << (Int) which << " at UT day " << T << " in table " << tnam[fil] << LogIO::WARN; res = 0.; } return res; } void MeasTable::calcPlanetary(MeasJPL::Files *fil) { const String tnam[2] = {"DE200", "DE405"}; uInt t; Aipsrc::find(t, "measures.jpl.ephemeris", 2, tnam, "DE200"); *fil = (MeasJPL::Files)t; } // Planetary constants Double MeasTable::Planetary(MeasTable::JPLconst what) { static Double cn[MeasTable::N_JPLconst]; std::call_once(theirPlanetaryConstantsInitOnceFlag, calcPlanetaryConstants, cn); return cn[what]; } void MeasTable::calcPlanetaryConstants(Double cn[MeasTable::N_JPLconst]) { const String tnam[2] = {"DE200", "DE405"}; uInt t; Aipsrc::find(t, "measures.jpl.ephemeris", 2, tnam, "DE200"); MeasJPL::Files fil = (MeasJPL::Files)t; for (uInt i=0; i rfp[3]; Double dt; String vs; if (!MeasIERS::getTable(t, kws, row, rfp, vs, dt, 3, rfn, "Observatories", "measures.observatory.directory", "geodetic")) { LogIO os(LogOrigin("MeasTable", "doInitObservatories()", WHERE)); os << "Cannot read table of Observatories" << LogIO::EXCEPTION; } Int N = t.nrow(); if (N<1) { LogIO os(LogOrigin("MeasTable", "doInitObservatories()", WHERE)); os << "No entries in table of Observatories" << LogIO::EXCEPTION; } obsNams.resize(N); obsPos.resize(N); antResponsesPath.resize(N); Bool hasAntResp = False; if(row.record().isDefined("AntennaResponses")){ hasAntResp = True; } MPosition::Ref mr; MPosition tmp; for (Int i=0; i(row.record(), "Name"); if(hasAntResp){ antResponsesPath(i) = *RORecordFieldPtr(row.record(), "AntennaResponses"); } if (!tmp.giveMe(mr, *RORecordFieldPtr(row.record(), "Type"))) { LogIO os(LogOrigin("MeasTable", "doInitObservatories()", WHERE)); os << "Illegal position type in Observatories" << LogIO::EXCEPTION; } obsPos(i) = MPosition(MVPosition(Quantity(*(rfp[2]), "m"), Quantity(*(rfp[0]), "deg"), Quantity(*(rfp[1]), "deg")), mr); } } const Vector &MeasTable::Observatories() { std::call_once(theirObsInitOnceFlag, doInitObservatories); return MeasTable::obsNams; } Bool MeasTable::Observatory(MPosition &obs, const String &nam) { std::call_once(theirObsInitOnceFlag, doInitObservatories); uInt i=MUString::minimaxNC(nam, MeasTable::obsNams); if (i < MeasTable::obsNams.nelements()) { obs = MeasTable::obsPos[i]; return True; } return False; } Bool MeasTable::AntennaResponsesPath(String &antRespPath, const String &nam) { std::call_once(theirObsInitOnceFlag, doInitObservatories); uInt i=MUString::minimaxNC(nam, MeasTable::obsNams); if (i < MeasTable::obsNams.nelements()) { antRespPath = MeasTable::antResponsesPath(i); if(antRespPath.empty()){ // i.e. there is no table for this observatory return False; } else if(antRespPath[0] == '/'){ // path is absolute Path lPath(antRespPath); if(!Table::isReadable(lPath.absoluteName())){ return False; } } else{ // path is relative // find and prepend the path to the data repository String absPathName; Bool isValid = False; { String mdir; Aipsrc::find(mdir, "measures.directory"); mdir.trim(); Path lPath(mdir); lPath.append(antRespPath); absPathName = lPath.absoluteName(); isValid = Table::isReadable(absPathName); } if(!isValid){ String casadata=String(CASADATA); casadata.gsub("%CASAROOT%", Aipsrc::aipsRoot()); casadata.gsub("%CASAHOME%", Aipsrc::aipsHome()); Path lPath(casadata + "/" + antRespPath); isValid = Table::isReadable(absPathName); } if(!isValid){ return False; // table not found } antRespPath = absPathName; } return True; } return False; // observatory not found } // Line data void MeasTable::initLines() { std::call_once(theirLinesInitOnceFlag, doInitLines); } void MeasTable::doInitLines() { Table t; ROTableRow row; TableRecord kws; String rfn[1] = {"Freq"}; RORecordFieldPtr rfp[1]; Double dt; String vs; if (!MeasIERS::getTable(t, kws, row, rfp, vs, dt, 1, rfn, "Lines", "measures.line.directory", "ephemerides")) { LogIO os(LogOrigin("MeasTable", "doInitLines()", WHERE)); os << "Cannot read table of spectral Lines" << LogIO::EXCEPTION; } Int N = t.nrow(); if (N<1) { LogIO os(LogOrigin("MeasTable", "doInitLines()", WHERE)); os << "No entries in table of spectral Lines" << LogIO::EXCEPTION; } lineNams.resize(N); linePos.resize(N); MFrequency::Ref mr(MFrequency::REST); MFrequency tmp; for (Int i=0; i(row.record(), "Name"); linePos(i) = MFrequency(MVFrequency(Quantity(*(rfp[0]), "GHz")), mr); if (lineNams(i) == "HI") linePos(i) = MFrequency(QC::HI( ), mr); } } const Vector &MeasTable::Lines() { std::call_once(theirLinesInitOnceFlag, doInitLines); return MeasTable::lineNams; } Bool MeasTable::Line(MFrequency &obs, const String &nam) { std::call_once(theirLinesInitOnceFlag, doInitLines); uInt i=MUString::minimaxNC(nam, MeasTable::lineNams); if (i < MeasTable::lineNams.nelements()) { obs = MeasTable::linePos(i); return True; } return False; } // Source data void MeasTable::initSources() { std::call_once(theirSrcInitOnceFlag, doInitSources); } void MeasTable::doInitSources() { Table t; ROTableRow row; TableRecord kws; String rfn[2] = {"Long", "Lat"}; RORecordFieldPtr rfp[2]; Double dt; String vs; if (!MeasIERS::getTable(t, kws, row, rfp, vs, dt, 2, rfn, "Sources", "measures.sources.directory", "ephemerides")) { LogIO os(LogOrigin("MeasTable", "doInitSources()", WHERE)); os << "Cannot read table of Sources" << LogIO::EXCEPTION; } Int N = t.nrow(); if (N<1) { LogIO os(LogOrigin("MeasTable", "doInitSources()", WHERE)); os << "No entries in table of Sources" << LogIO::EXCEPTION; } srcNams.resize(N); srcPos.resize(N); MDirection::Ref mr; MDirection tmp; for (Int i=0; i(row.record(), "Name"); if (!tmp.giveMe(mr, *RORecordFieldPtr(row.record(), "Type"))) { LogIO os(LogOrigin("MeasTable", "doInitSources()", WHERE)); os << "Illegal direction type in Sources" << LogIO::EXCEPTION; } srcPos(i) = MDirection(MVDirection(Quantity(*(rfp[0]), "deg"), Quantity(*(rfp[1]), "deg")), mr); } } const Vector &MeasTable::Sources() { std::call_once(theirSrcInitOnceFlag, doInitSources); return MeasTable::srcNams; } Bool MeasTable::Source(MDirection &obs, const String &nam) { std::call_once(theirSrcInitOnceFlag, doInitSources); uInt i=MUString::minimaxNC(nam, MeasTable::srcNams); if (i < MeasTable::srcNams.nelements()) { obs = MeasTable::srcPos(i); return True; } return False; } // Magnetic field (IGRF) function Vector MeasTable::IGRF(Double tm) { std::call_once(theirIGRFInitOnceFlag, doInitIGRF); // Look up closest MJD interval. Note that each interval has same width. Int indx = Int((tm-firstIGRF) / dtimeIGRF) - 1; if (indx >= Int(coefIGRF.size())) { indx = coefIGRF.size() - 1; } else if (indx < 0) { indx = 0; } // Interpolate using the d value. /// What is factor 5 meaning? double mjd = tm - (firstIGRF + (indx+1)*dtimeIGRF); return coefIGRF[indx] + dIGRF[indx] * (5*mjd/dtimeIGRF); } void MeasTable::initIGRF() { std::call_once(theirIGRFInitOnceFlag, doInitIGRF); } void MeasTable::doInitIGRF() { Table t; TableRecord kws; ROTableRow row; const String rfn[1] = {"MJD"}; RORecordFieldPtr rfp[1]; Double dt; String vs; if (!MeasIERS::getTable(t, kws, row, rfp, vs, dt, 1, rfn, "IGRF", "measures.igrf.directory", "geodetic")) { LogIO os(LogOrigin("MeasTable", "doInitIGRF()", WHERE)); os << "Cannot read table of IGRF models" << LogIO::EXCEPTION; } Int N = t.nrow(); if (N<10 || !kws.isDefined("MJD0") || kws.asDouble("MJD0") < 10000 || !kws.isDefined("dMJD") || kws.asDouble("dMJD") < 300) { LogIO os(LogOrigin("MeasTable", "doInitIGRF()", WHERE)); os << "Incorrect entries in table of IGRF models" << LogIO::EXCEPTION; } firstIGRF = kws.asDouble("MJD0"); dtimeIGRF = kws.asDouble("dMJD"); coefIGRF.reserve (N); dIGRF.reserve (N); ScalarColumn accmjd(t, "MJD"); ArrayColumn acc(t, "COEF"); ArrayColumn accd(t, "dCOEF"); for (Int i=0; i &MeasTable::aberArg(uInt which) { static const std::vector> polyArray(calcAberArg()); DebugAssert(which < 13, AipsError); return polyArray[which]; } std::vector> MeasTable::calcAberArg() { static const Double ABERFUND[13][2] = { {4.4026088, 2608.7903142}, {3.1761467, 1021.3285546}, {1.7534703, 628.3075849}, {6.2034809, 334.0612431}, {0.5995465, 52.9690965}, {0.8740168, 21.3299095}, {5.4812939, 7.4781599}, {5.3118863, 3.8133036}, {3.8103444, 8399.6847337}, {5.1984667, 7771.3771486}, {2.3555559, 8328.6914289}, {6.2400601, 628.3019553}, {1.6279052, 8433.4661601} }; std::vector> polyArray(13); Int i,j; for (i=0; i<13; i++) { polyArray[i] = Polynomial(1); for (j=0; j<2; j++) { polyArray[i].setCoefficient(j, ABERFUND[i][j]); } } return polyArray; } // Derivative aber const Polynomial &MeasTable::aberArgDeriv(uInt which) { static const std::vector> polyArray(calcAberArgDeriv()); DebugAssert(which < 13, AipsError); return polyArray[which]; } std::vector> MeasTable::calcAberArgDeriv() { std::vector> polyArray(13); for (int i=0; i<13; i++) { const Polynomial *polyPtr = &aberArg(i); polyArray[i] = polyPtr->derivative(); } return polyArray; } const Polynomial &MeasTable::aber1950Arg(uInt which) { static const std::vector> polyArray(calcAber1950Arg()); DebugAssert(which < 12, AipsError); return polyArray[which]; } std::vector> MeasTable::calcAber1950Arg() { static const Double ABERFUND[12][4] = { {1065976.59, 1717915856.79, 33.09, 0.0518}, {1290513.0, 129596579.1, -0.54, -0.0120}, {40503.2, 1739527290.54, -11.56, -0.0012}, {1262654.95, 1602961611.18, -5.17, 0.0068}, {933059.79, -6962911.23, 7.48, 0.0080}, {764820.00, 210662974.800, 0, 0}, {1150495.2, 68903917.200, 0, 0}, {811011.60, 10924498.800, 0, 0}, {632145.60, 4398458.400, 0, 0}, {0, 8128.800, 0, 0}, {260701.20, 1542164.400, 0, 0}, {135831.60, 786459.600, 0, 0} }; std::vector> polyArray(12); Int i,j; for (i=0; i<12; i++) { polyArray[i] = Polynomial(3); for (j=0; j<4; j++) { polyArray[i].setCoefficient(j, ABERFUND[i][j]*C::arcsec); } } return polyArray; } // Derivative aber1950 const Polynomial &MeasTable::aber1950ArgDeriv(uInt which) { static const std::vector> polyArray(calcAber1950ArgDeriv()); DebugAssert(which < 12, AipsError); return polyArray[which]; } std::vector> MeasTable::calcAber1950ArgDeriv() { std::vector> polyArray(12); for (int i=0; i<12; i++) { const Polynomial *polyPtr = &aber1950Arg(i); polyArray[i] = polyPtr->derivative(); } return polyArray; } const Double* MeasTable::mulAberArg(uInt which) { static const Double ABERARG[80][6] = { { 0, 0, 1, 0, 0, 0}, { 0, 0, 2, 0, 0, 0}, { 0, 0, 3, 0, 0, 0}, { 0, 0, 2, 0, -1, 0}, { 0, 0, 3, -8, 3, 0}, { 0, 0, 5, -8, 3, 0}, { 0, 1, 0, 0, 0, 0}, { 0, 2, -1, 0, 0, 0}, { 0, 0, 1, 0, -2, 0}, { 0, 0, 1, 0, 1, 0}, { 0, 2, -2, 0, 0, 0}, { 0, 0, 1, 0, -1, 0}, { 0, 0, 4, 0, 0, 0}, { 0, 0, 3, 0, -2, 0}, { 0, 1, -2, 0, 0, 0}, { 0, 2, -3, 0, 0, 0}, { 0, 2, -4, 0, 0, 0}, { 0, 0, 3, -2, 0, 0}, { 0, 8, -12, 0, 0, 0}, { 0, 8, -14, 0, 0, 0}, { 0, 0, 0, 2, 0, 0}, { 0, 3, -4, 0, 0, 0}, { 0, 0, 2, 0, -2, 0}, { 0, 3, -3, 0, 0, 0}, { 0, 0, 2, -2, 0, 0}, { 0, 3, -6, 0, 0, 0}, { 0, 0, 0, 0, 1, 0}, { 0, 0, 9, -16, 4, 5}, { 0, 0, 7, -16, 4, 5}, { 0, 0, 1, 0, -3, 0}, { 0, 0, 2, 0, -3, 0}, { 0, 4, -5, 0, 0, 0}, { 0, 0, 1, -4, 0, 0}, { 0, 0, 3, 0, -3, 0}, { 0, 0, 3, -4, 0, 0}, { 0, 3, -2, 0, 0, 0}, { 0, 0, 4, -4, 0, 0}, { 0, 0, 2, 0, 0, -1}, { 0, 0, 3, -3, 0, 0}, { 0, 0, 3, 0, -1, 0}, //40 { 0, 0, 1, 0, 0, 1}, { 0, 0, 0, 0, 2, 0}, { 0, 0, 2, -1, 0, 0}, { 0, 0, 1, 0, 0, -1}, { 0, 5, -6, 0, 0, 0}, { 0, 0, 1, -3, 0, 0}, { 0, 3, -6, 4, 0, 0}, { 0, 3, -8, 4, 0, 0}, { 0, 0, 4, -5, 0, 0}, { 0, 1, 1, 0, 0, 0}, { 0, 3, -5, 0, 0, 0}, { 0, 6, -7, 0, 0, 0}, { 0, 10, -9, 0, 0, 0}, { 0, 0, 2, -8, 3, 0}, { 0, 0, 6, -8, 3, 0}, { 0, 0, 1, -2, 0, 0}, { 0, 0, 9, -15, 0, 0}, { 0, 0, 1, 0, -2, 5}, { 0, 0, 1, 0, 2, -5}, { 0, 0, 1, 0, 0, -2}, //60 { 0, 0, 0, 1, 0, 0}, { 0, 0, 7, -15, 0, 0}, { 0, 2, 0, 0, 0, 0}, { 0, 0, 2, 0, 2, -5}, { 2, 0, -2, 0, 0, 0}, { 0, 0, 9, -19, 0, 3}, { 0, 0, 11, -19, 0, 3}, { 0, 0, 2, -5, 0, 0}, { 0, 5, -9, 0, 0, 0}, { 0, 11, -10, 0, 0, 0}, { 0, 4, -4, 0, 0, 0}, { 0, 0, 2, 0, -4, 0}, { 0, 0, 5, -6, 0, 0}, { 0, 5, -5, 0, 0, 0}, { 0, 0, 4, 0, -3, 0}, { 0, 4, -6, 0, 0, 0}, { 0, 5, -7, 0, 0, 0}, { 0, 0, 4, 0, -2, 0}, { 0, 0, 3, 0, -4, 0}, { 0, 7, -8, 0, 0, 0} }; DebugAssert(which < 80, AipsError); return &(ABERARG[which][0]); } const Double* MeasTable::mulAber1950Arg(uInt which) { static const Double ABERARG[132][12] = { { 0, 0, 1,-1, 1, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0}, { 0, 2, 1,-1, 1, 0, 0, 0, 0, 0, 0, 0}, { 0, 1, 1,-1, 1, 0, 0, 0, 0, 0, 0, 0}, { 0,-2, 1,-1, 1, 0, 0, 0, 0, 0, 0, 0}, { 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0}, { 0,-1,-1, 1,-1, 0, 0, 1, 0, 0, 0, 0}, { 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { 1, 0,-1,-2,-1, 0, 0, 0, 0, 0, 0, 0}, { 0, 1,-1, 1,-1,-1, 0, 0, 0, 0, 0, 0}, { 0, 4, 1,-1, 1, 0,-8, 3, 0, 0, 0, 0}, // 10 { 0,-4, 1,-1, 1, 0, 8,-3, 0, 0, 0, 0}, { 0, 2,-1, 1,-1,-2, 0, 0, 0, 0, 0, 0}, { 0, 2,-1, 1,-1, 0, 0,-2, 0, 0, 0, 0}, { 0,-4, 1,-1, 1, 0, 8,-3, 0, 0, 0, 0}, { 0, 4, 1,-1, 1, 0,-8, 3, 0, 0, 0, 0}, { 0, 1,-1, 1,-1,-1, 0, 0, 0, 0, 0, 0}, { 0, 0, 1,-1, 1, 0, 0, 1, 0, 0, 0, 0}, { 0, 2,-1, 1,-1,-2, 0, 0, 0, 0, 0, 0}, { 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0}, { 0,-3,-1, 1,-1, 0, 0, 0, 0, 0, 0, 0}, // 20 { 0, 0,-1, 1,-1, 0, 0, 1, 0, 0, 0, 0}, { 0, 2, 1,-1, 1, 0, 0,-2, 0, 0, 0, 0}, { 0, 3,-1, 1,-1,-2, 0, 0, 0, 0, 0, 0}, { 0,-2,-1, 1,-1, 0, 2, 0, 0, 0, 0, 0}, { 0, 1, 1,-1, 1,-1, 0, 0, 0, 0, 0, 0}, { 0, 3,-1, 1,-1,-2, 0, 0, 0, 0, 0, 0}, { 0, 2, 1,-1, 1,-2, 0, 0, 0, 0, 0, 0}, { 0, 1, 1,-1, 1, 0,-2, 0, 0, 0, 0, 0}, { 0,-1, 1,-1, 1, 0, 2, 0, 0, 0, 0, 0}, { 0, 3, 1,-1, 1,-2, 0, 0, 0, 0, 0, 0}, // 30 { 0,-1,-1, 1,-1, 0, 0, 2, 0, 0, 0, 0}, { 0, 4,-1, 1,-1,-3, 0, 0, 0, 0, 0, 0}, { 0, 3, 1,-1, 1,-2, 0, 0, 0, 0, 0, 0}, { 0,13, 1,-1, 1,-8, 0, 0, 0, 2, 0, 0}, {0,-13, 1,-1, 1, 8, 0, 0, 0,-2, 0, 0}, { 0,13, 1,-1, 1,-8, 0, 0, 0, 2, 0, 0}, {0,-13, 1,-1, 1, 8, 0, 0, 0,-2, 0, 0}, { 0, 0,-1, 2,-1, 0, 0, 0, 0, 0, 0, 0}, { 0, 2, 1,-1, 1,-2, 0, 0, 0, 0, 0, 0}, { 0, 1, 1,-1, 1,-1, 0, 0, 0, 0, 0, 0}, // 40 { 0, 0,-1, 2, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 3, 1,-1, 1,-3, 0, 0, 0, 0, 0, 0}, { 0,-1, 1,-1, 1, 0, 0, 1, 0, 0, 0, 0}, { 0, 0, 1,-1, 1, 0, 0, 0, 0, 0, 0, 0}, { 0, 5, 1,-1, 1,-3, 0, 0, 0, 0, 0, 0}, { 0, 3,-1, 1,-1, 0, 0,-3, 0, 0, 0, 0}, { 0,-5, 1,-1, 1, 3, 0, 0, 0, 0, 0, 0}, { 0, 4, 1,-1, 1,-4, 0, 0, 0, 0, 0, 0}, { 0, 4, 0, 0, 0,-3, 0, 0, 0, 0, 0, 0}, { 0, 5, 1,-1, 1,-3, 0, 0, 0, 0, 0, 0}, // 50 { 0,-5, 1,-1, 1, 3, 0, 0, 0, 0, 0, 0}, { 0, 1, 1,-1, 1, 0, 0,-2, 0, 0, 0, 0}, { 0,-2, 1,-1, 1, 0, 0, 3, 0, 0, 0, 0}, { 0, 2, 1,-1, 1, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 1,-1, 1, 0, 0, 0, 0, 0, 0, 0}, { 0,-1, 1,-1, 1, 0, 2, 0, 0, 0, 0, 0}, { 0, 1, 1,-1, 1, 0,-2, 0, 0, 0, 0, 0}, { 0, 3, 1,-1, 1, 0,-4, 0, 0, 0, 0, 0}, { 0,-2,-1, 1,-1, 0, 2, 0, 0, 0, 0, 0}, { 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // 60 { 0,-1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0}, { 0,-2,-1, 1,-1, 0, 3, 0, 0, 0, 0, 0}, { 0,-2, 1,-1, 1, 0, 4, 0, 0, 0, 0, 0}, { 0, 2, 1,-1, 1, 0,-4, 0, 0, 0, 0, 0}, { 0, 1, 1,-1, 1, 0, 0, 0,-1, 0, 0, 0}, { 0,-2,-1, 1,-1, 0, 0, 3, 0, 0, 0, 0}, { 0, 3,-1, 1,-1,-3, 0, 0, 0, 0, 0, 0}, { 0,-2, 1,-1, 1, 0, 0, 1, 0, 0, 0, 0}, { 0, 4, 1,-1, 1,-4, 0, 0, 0, 0, 0, 0}, { 0, 0, 1,-1, 1, 0, 0, 1, 0, 0, 0, 0}, // 70 { 0,-2, 1,-1, 1, 0, 0, 2, 0, 0, 0, 0}, { 0,-2,-1, 1,-1, 0, 0, 1, 0, 0, 0, 0}, { 0, 5, 1,-1, 1,-5, 0, 0, 0, 0, 0, 0}, { 0,-1, 1,-1, 1, 0, 0, 2, 0, 0, 0, 0}, { 0, 4,-1, 1,-1,-3, 0, 0, 0, 0, 0, 0}, {-1, 0,-1, 2,-1, 0, 0, 0, 0, 0, 0, 0}, { 0, 5, 1,-1, 1,-4, 0, 0, 0, 0, 0, 0}, { 0,-2, 1,-1, 1, 0, 0, 0, 2, 0, 0, 0}, { 0, 0, 1,-1, 1, 0, 0, 0, 1, 0, 0, 0}, { 0, 0, 1,-1, 1, 0, 0, 0,-1, 0, 0, 0}, // 80 { 0, 5,-1,-1, 1,-5, 0, 0, 0, 0, 0, 0}, { 0, 6, 1,-1, 1,-6, 0, 0, 0, 0, 0, 0}, { 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0}, { 0,-7, 1,-1, 1, 3, 4, 0, 0, 0, 0, 0}, { 0, 7, 1,-1, 1,-3,-4, 0, 0, 0, 0, 0}, { 0,-2, 1,-1, 1, 0, 4, 0, 0, 0, 0, 0}, { 0, 2, 1,-1, 1, 0,-4, 0, 0, 0, 0, 0}, { 0, 4, 1,-1, 1,-3, 0, 0, 0, 0, 0, 0}, { 0,-2, 1,-1, 1, 0, 3, 0, 0, 0, 0, 0}, { 0, 1, 1,-1, 1,-2, 0, 0, 0, 0, 0, 0}, // 90 { 0, 1,-1, 1,-1,-2, 0, 0, 0, 0, 0, 0}, { 1, 0,-1, 2,-1, 0, 0, 0, 0, 0, 0, 0}, { 0,-1, 1,-1, 1, 0, 0, 0, 1, 0, 0, 0}, { 0,-3, 1,-1, 1, 0, 0, 2, 0, 0, 0, 0}, { 0,-3,-1, 1,-1, 0, 0, 2, 0, 0, 0, 0}, { 0,-2, 1,-1, 1, 0, 2, 0, 0, 0, 0, 0}, { 0,-3, 1,-1, 1, 0, 3, 0, 0, 0, 0, 0}, { 0,-3,-1, 1,-1, 0, 3, 0, 0, 0, 0, 0}, { 0,-1, 1,-1, 1, 0, 0, 3, 0, 0, 0, 0}, { 0,-1,-1, 1,-1, 0, 0, 3, 0, 0, 0, 0}, // 100 { 0, 1, 1,-1, 1,-2, 0, 0, 0, 0, 0, 0}, { 0, 1,-1, 1,-1,-2, 0, 0, 0, 0, 0, 0}, { 0, 7, 1,-1, 1,-5, 0, 0, 0, 0, 0, 0}, { 0, 7,-1, 1,-1,-5, 0, 0, 0, 0, 0, 0}, { 0,-2, 1,-1, 1, 0, 0, 4, 0, 0, 0, 0}, { 0,-2,-1, 1,-1, 0, 0, 4, 0, 0, 0, 0}, { 0,-2, 1,-1, 1, 0, 0, 3, 0, 0, 0, 0}, { 0,-2,-1, 1,-1, 0, 0, 3, 0, 0, 0, 0}, // g2,g3,g4,g5, v, g6,g7 { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, // 110 { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, { 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, // 120 { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, { 0, 0, 1,-1, 1, 0, 0, 0, 0, 0, 0, 0}, // 130 { 0, 1, 1,-1, 1, 0, 0, 0, 0, 0, 0, 0}, }; DebugAssert(which < 132, AipsError); return &(ABERARG[which][0]); } const Double* MeasTable::mulAberSunArg(uInt which) { static const Double ABERSUNARG[17][7] = { { 0, 0, 0, 1, 0, 0, 0}, { 0, 0, 0, 0, 1, 0, 0}, { 0, 0, 0, 2, 0, 0, 0}, { 0, 0, 0, 0, 0, 1, 0}, { 0, 0, 0, 0, 0, 0, 1}, { 0, 0, 0, 0, 2, 0, 0}, { 1, 0, 0, 0, 0, 0, 0}, { 0, 1, 0, 0, 0, 0, 0}, { 0, 0, 0, 3, 0, 0, 0}, { 0, 0, 0, 1, -5, 0, 0}, { 0, 0, 0, 3, -5, 0, 0}, { 1, 0, 0, 0, 0, 0, -2}, { 0, 0, 0, 0, 3, 0, 0}, { 0, 0, 0, 2, -6, 0, 0}, { 0, 0, 0, 2, -4, 0, 0}, { 0, 0, 0, 0, 0, 2, 0}, { 0, 0, 0, 1, 0, 0, -2} }; DebugAssert(which < 17, AipsError); return &(ABERSUNARG[which][0]); } const Double* MeasTable::mulAberEarthArg(uInt which) { static const Double ABEREARTHARG[17][5] = { { 1, 0, 0, 0, 0}, { 0, 0, 0, 0, 1}, { 1, 0, 1, 0, 0}, { 1, 2, -1, 0, 0}, { 1, -2, 0, 0, 0}, { 1, 2, 0, 0, 0}, { 0, 0, 1, 0, 1}, { 1, -2, 1, 0, 0}, { 1, 0, 2, 0, 0}, { 0, 2, 0, 0, -1}, { 1, 0, 0, 0, -2}, { 1, 0, 0, 1, 0}, { 1, 0, 0, -1, 0}, { 1, 4, -2, 0, 0}, { 1, -2, 2, 0, 0}, { 1, 2, 1, 0, 0}, { 0, 2, -1, 0, 1} }; DebugAssert(which < 17, AipsError); return &(ABEREARTHARG[which][0]); } std::shared_ptr> MeasTable::mulAber(Double time, Double epsilon) { return theirMulAber.getArray (time, epsilon); } std::shared_ptr> MeasTable::mulAber1950(Double time, Double epsilon) { return theirMulAber1950.getArray (time, epsilon); } const Vector &MeasTable::mulSunAber(uInt which) { static const std::vector> argArray(calcMulSunAber()); DebugAssert(which < 17, AipsError); return argArray[which]; } std::vector> MeasTable::calcMulSunAber() { static const Short MSUNABER[17][6] = { { 719, 0, 6, -660, -15, -283}, { 159, 0, 2, -147, -6, -61}, { 34, -9, -8, -31, -4, -13}, { 17, 0, 0, -16, 0, -7}, { 16, 0, 1, -15, -3, -6}, { 0, -9, -8, 0, -3, 1}, { 6, 0, 0, -6, 0, -2}, { 5, 0, 0, -5, 0, -2}, { 2, -1, -1, -2, 0, -1}, { -2, 0, 0, -2, 0, -1}, { -2, 0, 0, 2, 0, 1}, { -1, 0, 0, -1, 0, 0}, { -1, 0, 0, 1, 0, 0}, { 1, 0, 0, 1, 0, 0}, { 1, 0, 0, -1, 0, 0}, { -1, 0, 0, 1, 0, 0}, { 1, 0, 0, 0, 0, 0} }; std::vector> argArray(17); UnitVal AUperDay(1e-8,"AU/d"); Double factor = AUperDay.getFac(); Int i,j; for (i=0; i<17; i++) { argArray[i].resize(6); for (j=0; j<6; j++) { argArray[i](j) = MSUNABER[i][j] * factor; } } return argArray; } const Vector &MeasTable::mulEarthAber(uInt which) { static const std::vector> argArray(calcMulEarthAber()); DebugAssert(which < 17, AipsError); return argArray[which]; } std::vector> MeasTable::calcMulEarthAber() { static const Short MEARTHABER[17][3] = { { 715, -656, -285}, { 0, 26, -59}, { 39, -36, -16}, { 8, -7, -3}, { 5, -5, -2}, { 4, -4, -2}, { 0, 1, -3}, { -2, 2, 1}, { 2, -2, -1}, { 0, 1, -2}, { -1, 1, 1}, { -1, 1, 0}, { 1, -1, 0}, { 1, -1, 0}, { -1, 1, 0}, { 1, 0, 0}, { 0, 0, -1} }; std::vector> argArray(17); UnitVal AUperDay(1e-8,"AU/d"); Double factor = AUperDay.getFac(); Int i,j; for (i=0; i<17; i++) { argArray[i].resize(3); for (j=0; j<3; j++) { argArray[i](j) = MEARTHABER[i][j] * factor; } } return argArray; } const Vector &MeasTable::AberETerm(uInt which) { static const std::vector> termArray(calcAberETerm()); DebugAssert(which < 2, AipsError); return termArray[which]; } std::vector> MeasTable::calcAberETerm() { static const Double TERM[2][3] = { {-1.62557, -0.31919, -0.13843}, {+1.245, -1.580, -0.659} }; std::vector> termArray(2); Int i; for (i=0; i<2; i++) { termArray[i].resize(3); } for (i=0; i<3; i++) { termArray[0](i) = TERM[0][i] * 1e-6; termArray[1](i) = TERM[1][i] * 1e-3; } return termArray; } // Diurnal Aberration factor Double MeasTable::diurnalAber(Double radius, Double T) { /// static Double res; /// res = (2.0*M_PI) * radius / MeasData::SECinDAY * /// MeasTable::UTtoST(T)/C::c; /// return res; return (2.0*M_PI) * radius / MeasData::SECinDAY * MeasTable::UTtoST(T)/C::c; } // LSR velocity (kinematical) const Vector &MeasTable::velocityLSRK(uInt which) { static const std::vector> argArray(calcVelocityLSRK()); DebugAssert(which < 2, AipsError); return argArray[which]; } std::vector> MeasTable::calcVelocityLSRK() { static const Double LSR[2][3] = { {0.0145021, -0.865863, 0.500071}, {0.00724658, -0.865985, 0.500018} }; std::vector> argArray(2); Double v = 20.0*1000.; for (Int i=0; i<2; i++) { argArray[i].resize(3); for (Int j=0; j<3; j++) { argArray[i](j) = v * LSR[i][j]; } } return argArray; } // LSR velocity (dynamical) const Vector &MeasTable::velocityLSR(uInt which) { static const std::vector> argArray(calcVelocityLSR()); DebugAssert(which < 2, AipsError); return argArray[which]; } std::vector> MeasTable::calcVelocityLSR() { static const Double LSR[2][3] = { {-0.0385568, -0.881138, 0.471285}, {-0.0461164, -0.880664, 0.471491} }; std::vector> argArray(2); Double v = sqrt(81.+144.+49.)*1000.; for (Int i=0; i<2; i++) { argArray[i].resize(3); for (Int j=0; j<3; j++) { argArray[i](j) = v * LSR[i][j]; } } return argArray; } // LSR velocity wrt galactic centre const Vector &MeasTable::velocityLSRGal(uInt which) { static const std::vector> argArray(calcVelocityLSRGal()); DebugAssert(which < 2, AipsError); return argArray[which]; } std::vector> MeasTable::calcVelocityLSRGal() { static const Double LSR[2][3] = { {0.494109, -0.44483 , 0.746982}, {0.492728, -0.450347, 0.744585} }; std::vector> argArray(2); Double v = 220.*1000.; for (Int i=0; i<2; i++) { argArray[i].resize(3); for (Int j=0; j<3; j++) { argArray[i](j) = v * LSR[i][j]; } } return argArray; } // LGROUP velocity wrt bary center const Vector &MeasTable::velocityLGROUP(uInt which) { static const std::vector> argArray(calcVelocityLGROUP()); DebugAssert(which < 2, AipsError); return argArray[which]; } std::vector> MeasTable::calcVelocityLGROUP() { static const Double LGROUP[2][3] = { {0.593553979227, -0.177954636914, 0.784873124106}, {0.5953342407, -0.184600136022, 0.781984610866} }; std::vector> argArray(2); Double v = 308.*1000.; for (Int i=0; i<2; i++) { argArray[i].resize(3); for (Int j=0; j<3; j++) { argArray[i](j) = v * LGROUP[i][j]; } } return argArray; } // CMB velocity wrt bary center const Vector &MeasTable::velocityCMB(uInt which) { static const std::vector> argArray(calcVelocityCMB()); DebugAssert(which < 2, AipsError); return argArray[which]; } std::vector> MeasTable::calcVelocityCMB() { static const Double CMB[2][3] = { {-0.97176985257, 0.202393953108, -0.121243727187}, {-0.970024232022, 0.213247954272, -0.11652595972} }; std::vector> argArray(2); Double v = 369.5*1000.; for (Int i=0; i<2; i++) { argArray[i].resize(3); for (Int j=0; j<3; j++) { argArray[i](j) = v * CMB[i][j]; } } return argArray; } // Earth and Sun position const Polynomial &MeasTable::posArg(uInt which) { static const std::vector> polyArray(calcPosArg()); DebugAssert(which < 12, AipsError); return polyArray[which]; } std::vector> MeasTable::calcPosArg() { static const Double POSFUND[12][2] = { {252.25, 149472.67}, // Q {181.9798, 58517.8157}, // V {100.46644851, 35999.37285186}, // E {355.43327, 19140.29933}, // M { 34.351484, 3034.905675}, // J { 50.077471, 1222.113794}, // S {314.055005, 428.466998}, // U {304.348665, 218.486200}, // N {238.47, 145.28}, // P {297.850206, 445267.111519}, // D { 93.27210, 483202.01753}, // F {134.9634, 477198.8676} // l }; std::vector> polyArray(12); Int i,j; for (i=0; i<12; i++) { polyArray[i] = Polynomial(1); for (j=0; j<2; j++) { polyArray[i].setCoefficient(j, POSFUND[i][j]*C::degree); } } return polyArray; } // Derivative of Earth and Sun position polynomial const Polynomial &MeasTable::posArgDeriv(uInt which) { static const std::vector> polyArray(calcPosArgDeriv()); DebugAssert(which < 12, AipsError); return polyArray[which]; } std::vector> MeasTable::calcPosArgDeriv() { std::vector> polyArray(12); for (int i=0; i<12; i++) { const Polynomial *polyPtr = &posArg(i); polyArray[i] = polyPtr->derivative(); } return polyArray; } const Double* MeasTable::mulPosEarthXYArg(uInt which) { static const Double POSXYARG[189][12] = { //X,Y(ecliptic) factors {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 2, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 5, -8, 3, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 3, -8, 3, 0, 0, 0, 0, 0, 0, 0}, {0, 1, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, -2, 0, 0, 0, 0, 0, 0, 0}, // 11 {0, 0, 2, 0, -1, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, -2, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 2, -4, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, -1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0}, {0, 2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 3, -5, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // 21 {0, 8,-14, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 2, -2, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 8,-12, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 3, -4, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, -1}, {0, 0, 3, -2, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 3, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, -3, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 2, 0, -2, 0, 0, 0, 0, 0, 0, 0}, // 31 {0, 3, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 3, 0, -2, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 9,-16, 4, 5, 0, 0, 0, 0, 0, 0}, {0, 0, 7,-16, 4, 5, 0, 0, 0, 0, 0, 0}, {0, 0, 2, -4, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 3, -4, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, {0, 0, 1, -4, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, -3, 0, 0, 0, 0, 0, 0, 0, 0}, // 41 {0, 4, -5, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1}, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0}, {0, 0, 2, 0, -3, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 3, -3, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 4, -4, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 3, -8, 4, 0, 0, 0, 0, 0, 0, 0, 0}, // 51 {0, 3, -6, 4, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 2, -5, 0, 0, 0, 0, 0, 0}, {0, 0, 2, -5, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 4, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 3, -6, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 2, 0, 0, -1, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, -2, 5, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 0, 0, 0, -1, 0, 1}, {0, 0, 1, 0, 2, -5, 0, 0, 0, 0, 0, 0}, {0, 0, 7,-15, 0, 0, 0, 0, 0, 0, 0, 0}, // 61 {0, 0, 2, -1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 2, -3, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 5, -8, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 9,-15, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 4, -7, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, -2, 0, 0, 0, 0, 0, 0}, {0, 0, 3, 0, -3, 0, 0, 0, 0, 0, 0, 0}, {0, 5, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 5, -9, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 4, -5, 0, 0, 0, 0, 0, 0, 0, 0}, // 71 {0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0}, {0, 5, -7, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 3, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 11,-19, 0, 3, 0, 0, 0, 0, 0, 0}, {0, 0, 9,-19, 0, 3, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, -4, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 3, -5, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 3, -7, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 4, -6, 0, 0, 0, 0, 0, 0, 0, 0}, // 81 {0, 0, 2, -8, 3, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 6, -8, 3, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 0, 0, 0, -1, 0, 0}, {0, 8,-13, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 3, 0, -1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 2, 0, 2, -5, 0, 0, 0, 0, 0, 0}, {0, 0, 5, -7, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 5, -6, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 2, -6, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 2, 0, -4, 0, 0, 0, 0, 0, 0, 0}, // 91 {0, 0, 1, 0, 0, 0, 0, 0, 0, 3, 0, -1}, {0, 6, -7, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 4, -7, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 4, -8, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 4, -9, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // 101 {0, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 2, 0, 0, -2, 0, 0, 0, 0, 0, 0}, {0, 0, 6, -9, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 4, -3, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 3, -8, 1, 5, 0, 0, 0, 0, 0, 0}, {0, 0, 5, -8, 1, 5, 0, 0, 0, 0, 0, 0}, {0, 4, -4, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 5,-11, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 6,-13, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 2, -5, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // 111 {0, 3, -6, 0, 2, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 5, -9, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 8,-13, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 3, -4, 0, 2, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 3, 0, -4, 0, 0, 0, 0, 0, 0, 0}, {0, 7, -8, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 7,-11, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, -3, 0, 0, 0, 0, 0, 0}, {0, 0, 4, -6, 2, 0, 0, 0, 0, 0, 0, 0}, // 121 {0, 0, 2, -6, 2, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 5, -5, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0}, {0, 4, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 6, -8, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 8,-15, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 3, 0, 0, -2, 0, 0, 0, 0, 0, 0}, {0, 0, 5, -8, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 10,-17, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 4, 0, -3, 0, 0, 0, 0, 0, 0, 0}, // 131 {0, 0, 0, 0, 1, -5, 0, 0, 0, 0, 0, 0}, {0, 6,-11, 0, 3, 0, 0, 0, 0, 0, 0, 0}, {0, 6, -9, 0, 3, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 8,-17, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 3, -5, 0, 0, 0, 0, 0, 0}, {0, 0, 8,-15, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, -2, 0, 0, 0, 0, 0, 0}, {0, 6, -8, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {1, 0, -5, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 4, 0, -2, 0, 0, 0, 0, 0, 0, 0}, // 141 {0, 0, 5,-10, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, -5, 0, 0, 0, 0, 0, 0}, {0, 0, 6,-11, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, -2, 2, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 4, -2, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 5, -7, -4, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 5, -5, -4, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 0, 0, 0, 1, -2, 0}, {0, 0, 1, 0, -4, 5, 0, 0, 0, 0, 0, 0}, // 151 {0, 0, 1, 0, 0, 0, 0, 0, 0, 3, 0, 0}, {0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0}, {1, 0, -4, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {1, 0, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 2, -2, 0, 0, 0, 0, 0, 0}, {0, 6,-10, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, -1, 2, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 1, -2, 0, 0, 0, 0, 0, 0}, {0, 8, -9, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // 161 {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 6, -7, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 1, 0, -3, 5, 0, 0, 0, 0, 0, 0}, {0, 0, 7,-13, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 3, -5, 0, 0, 0, 0, 0, 0}, {0, 0, 3, -8, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 6, -9, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, -5, 0, 0, 0, 0, 0, 0, 0}, // 171 {0, 3, -7, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 2, 0, -2, 0, 0, 0, 0, 0, 0}, {0, 0, 2, -2, 0, 2, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 5, -8, 3, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 3, -8, 3, 0, 0, 0, 0, 0, 0, 0}, {0, 2, -4, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // 181 {0, 8,-14, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 8,-12, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 3, -5, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; DebugAssert(which < 189, AipsError); return &(POSXYARG[which][0]); } const Double* MeasTable::mulPosEarthZArg(uInt which) { static const Double POSZARG[32][12] = { //Z(ecliptic) factors {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, {0, 3, -4, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, -2, 0, 0, 0, 0, 0, 0, 0}, {0, 1, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 2, -3, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1}, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, -2, 0, 0, 0, 0, 0, 0}, {0, 4, -5, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // 11 {0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -1, 0}, {0, 0, 1, 0, -3, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0}, {0, 0, 1, 0, -1, 0, 0, 0, 0, 0, 0, 0}, {0, 5, -7, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1}, {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 2, -5, 0, 0, 0, 0, 0, 0}, {0, 2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // 21 {0, 3, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 2, -4, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 2, -2, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 8,-12, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 5, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // 31 {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; DebugAssert(which < 32, AipsError); return &(POSZARG[which][0]); } const Double* MeasTable::mulPosSunXYArg(uInt which) { static const Double POSXYARG[98][12] = { //X,Y(ecliptic) factors {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 2, -4, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 2, -6, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 3, -5, 0, 0, 0, 0, 0, 0}, // 11 {0, 0, 0, 0, 1, -5, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, -3, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 0, 0}, {0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 2, -5, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 2, -3, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // 21 {0, 0, 0, 0, 1, -3, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, -2, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0}, {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, -2, 0, 0, 0, 0}, {0, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 3, -2, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 2, -7, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 4, -5, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0}, // 31 {0, 0, 0, 0, 2, -1, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 3, -2, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 3, -3, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 2, -3, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, -3, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, -2, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0}, // 41 {0, 0, 0, 0, 0, 0, 2, -5, 0, 0, 0, 0}, {0, 0, 0, 0, 4,-11, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, -4, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 3, -4, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 2, -4, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, -4, 0, 0, 0, 0}, {0, 0, 0, 0, 2, -7, 3, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 2, -5, 3, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 2, -3, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0}, // 51 {0, 0, 0, 0, 1, -4, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 3, -4, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 5,-10, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, -6, 3, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 3, -6, 3, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 4, -9, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 3,-10, 0, 0, 0, 0, 0, 0}, // 61 {0, 0, 0, 0, 4, -2, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 4, -3, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 4,-10, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 5, -5, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 4, -4, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 4,-12, 3, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 3, -3, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 4,-10, 3, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 3, -8, 0, 0, 0, 0, 0, 0}, // 71 {0, 0, 0, 0, 3, -7, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 2, -8, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 2, -6, 2, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 2, -1, 0, 0, 0, 0}, {0, 0, 0, 0, 2, -6, 4, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 4, -2, 0, 0, 0, 0}, {0, 0, 0, 0, 5,-11, 3, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 3,-11, 3, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0}, // 81 {0, 0, 0, 0, 0, 0, 4, -4, 0, 0, 0, 0}, {0, 0, 0, 0, 3, -1, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 2, -4, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 2, -6, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 3, -5, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, -5, 0, 0, 0, 0, 0, 0}, // 91 {0, 0, 0, 0, 2, -5, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 2, -3, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 2, -7, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0} }; DebugAssert(which < 98, AipsError); return &(POSXYARG[which][0]); } const Double* MeasTable::mulPosSunZArg(uInt which) { static const Double POSZARG[29][12] = { //Z(ecliptic) factors {0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 2, -4, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 2, -6, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 3, -5, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, -5, 0, 0, 0, 0, 0, 0}, // 11 {0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, -3, 0, 0, 0, 0}, {0, 0, 0, 0, 2, -5, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0}, {0, 0, 0, 0, 1, -3, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 2, -3, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0}, {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // 12 {0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 0, 0}, {0, 0, 0, 0, 1, -2, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 2, -7, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; DebugAssert(which < 29, AipsError); return &(POSZARG[which][0]); } std::shared_ptr> MeasTable::mulPosEarthXY(Double time, Double epsilon) { return theirMulPosEarthXY.getArray (time, epsilon); } std::shared_ptr> MeasTable::mulPosEarthZ(Double time, Double epsilon) { return theirMulPosEarthZ.getArray (time, epsilon); } std::shared_ptr> MeasTable::mulPosSunXY(Double time, Double epsilon) { return theirMulPosSunXY.getArray (time, epsilon); } std::shared_ptr> MeasTable::mulPosSunZ(Double time, Double epsilon) { return theirMulPosSunZ.getArray (time, epsilon); } const RotMatrix &MeasTable::posToRect() { static const RotMatrix rot(Euler(+84381.4091 * C::arcsec, 1, -0.0930 * C::arcsec, 3)); return rot; } const RotMatrix &MeasTable::rectToPos() { static RotMatrix rot(calcRectToPos()); return rot; } RotMatrix MeasTable::calcRectToPos() { RotMatrix rot(MeasTable::posToRect()); rot.transpose(); return rot; } const RotMatrix &MeasTable::galToSupergal() { static RotMatrix rot(Euler( -90*C::degree, 3, -83.68*C::degree, 2, -47.37*C::degree, 3)); return rot; } const RotMatrix &MeasTable::ICRSToJ2000() { static RotMatrix rot(calcICRSToJ2000()); return rot; } RotMatrix MeasTable::calcICRSToJ2000() { RotMatrix rot(MeasTable::frameBias00()); rot.transpose(); return rot; } // Position related routines Double MeasTable::WGS84(uInt which) { static const Double data[2] = { 6378137, 298.257223563}; DebugAssert(which < 2, AipsError); return data[which]; } // Polar motion related routines Euler MeasTable::polarMotion(Double ut) { #if defined(USE_THREADS) static std::atomic msgDone; #else static Bool msgDone; #endif Euler res(0.0, 2, 0.0, 1, 0.0, 3); if (!MeasIERS::get(res(0), MeasIERS::MEASURED, MeasIERS::X, ut) || !MeasIERS::get(res(1), MeasIERS::MEASURED, MeasIERS::Y, ut)) { // It is harmless if the message accidentally appears multiple times. if (!msgDone) { LogIO os(LogOrigin("MeasTable", "PolarMotion(Double)", WHERE)); os << LogIO::NORMAL3 << "High precision polar motion information not available." << LogIO::POST; msgDone = True; } } /// cout << "polarmotion " << res(0) << ' ' << res(1) << endl; res(0) *= -C::arcsec; res(1) *= -C::arcsec; return res; } // Time functions Double MeasTable::dUTC(Double utc) { static const Statics_dUTC st(calc_dUTC()); Double (* const &LEAP)[4] = st.LEAP; // alias to avoid more clutter below const int &N = st.N; // idem Double val(0); if (utc < LEAP[0][0]) { val = LEAP[0][1] + (utc - LEAP[0][2])*LEAP[0][3]; } else { for (Int i = N-1; i >= 0; i--) { if (utc >= LEAP[i][0]) { val = LEAP[i][1]; if (LEAP[i][3] != 0) { val += (utc - LEAP[i][2])*LEAP[i][3]; } break; } } } return val; } MeasTable::Statics_dUTC MeasTable::calc_dUTC() { Statics_dUTC rv; Table t; ROTableRow row; TableRecord kws; const String rfn[4] = {"MJD", "dUTC", "Offset", "Multiplier"}; RORecordFieldPtr rfp[4]; Double dt; String vs; if (!MeasIERS::getTable(t, kws, row, rfp, vs, dt, 4, rfn, "TAI_UTC", "measures.tai_utc.directory", "geodetic")) { LogIO os(LogOrigin("MeasTable", "dUTC(Double)", WHERE)); os << "Cannot read leap second table TAI_UTC" << LogIO::EXCEPTION; } rv.N = t.nrow(); if (rv.N < 35) { LogIO os(LogOrigin("MeasTable", "dUTC(Double)", WHERE)); os << "Leap second table TAI_UTC corrupted" << LogIO::EXCEPTION; } if (Time().modifiedJulianDay() - dt > 180) { LogIO os(LogOrigin("MeasTable", "dUTC(Double)", WHERE)); os << LogIO::SEVERE << "Leap second table TAI_UTC seems out-of-date.\n" "Until the table is updated (see the CASA documentation or your system admin),\n" "times and coordinates derived from UTC could be wrong by 1s or more." << LogIO::POST; } rv.LEAP = (Double (*)[4])(new Double[4*rv.N]); for (Int i=0; i < rv.N; i++) { row.get(i); for (Int j=0; j < 4; j++) { rv.LEAP[i][j] = *(rfp[j]); } } return rv; } Double MeasTable::dTAI(Double) { return (32.184); } Double MeasTable::dTDT(Double ut1) { Double g = (357.53 + 0.9856003*(ut1-MeasData::MJD2000))*C::degree; return (0.001658*sin(g) + 0.000014*sin(2*g)); } Double MeasTable::dTDB(Double tai) { return(1.550505e-8*86400*(tai-43144.0)); } Double MeasTable::dTCG(Double tai) { return(6.969291e-10*86400*(tai-43144.0)); } Double MeasTable::GMST0(Double ut1) { static Polynomial stPoly(calcGMST0()); return (stPoly((ut1-MeasData::MJD2000)/MeasData::JDCEN)); } Polynomial MeasTable::calcGMST0() { Polynomial stPoly(3); stPoly.setCoefficient(0, 24110.54841); stPoly.setCoefficient(1, 8640184.812866); stPoly.setCoefficient(2, 0.093104); stPoly.setCoefficient(3, -6.2e-6); return stPoly; } Double MeasTable::GMST00(Double ut1, Double tt) { static Polynomial stPoly(calcGMST00()); return (stPoly((tt-MeasData::MJD2000)/MeasData::JDCEN) + MeasTable::ERA00(ut1)); } Polynomial MeasTable::calcGMST00() { Polynomial stPoly(4); stPoly.setCoefficient(0, 0.014506*C::arcsec); stPoly.setCoefficient(1, 4612.15739966*C::arcsec+630.73514045148926); stPoly.setCoefficient(2, + 1.39667721*C::arcsec); stPoly.setCoefficient(3, - 0.00009344*C::arcsec); stPoly.setCoefficient(4, + 0.00001882*C::arcsec); return stPoly; } Double MeasTable::ERA00(Double ut1) { static Polynomial stPoly(calcERA00()); ut1 -= MeasData::MJD2000; return MVAngle(stPoly(ut1)+ (2.0*M_PI)*fmod(ut1, 1.0))(0.0).radian(); } Polynomial MeasTable::calcERA00() { Polynomial stPoly(1); stPoly.setCoefficient(0, 0.7790572732640*(2.0*M_PI)); stPoly.setCoefficient(1, 0.00273781191135448*(2.0*M_PI)); return stPoly; } Double MeasTable::sprime00(Double tt) { return ((tt-MeasData::MJD2000)/MeasData::JDCEN * -47e-6 * C::arcsec); } Double MeasTable::GMUT0(Double gmst1) { static Polynomial stPoly(calcGMUT0()); return (stPoly((gmst1-MeasData::MJD2000-6713.)/MeasData::JDCEN)); } Polynomial MeasTable::calcGMUT0() { Polynomial stPoly(3); stPoly.setCoefficient(0, -0.65830845056254866847); stPoly.setCoefficient(1, -235.90946916710752); stPoly.setCoefficient(2, -0.00000252822553597972); stPoly.setCoefficient(3, 0.0000000001679); return stPoly; } Double MeasTable::UTtoST(Double ut1) { static Polynomial UTSTPoly(calcUTtoST()); return(UTSTPoly((ut1-MeasData::MJD2000)/MeasData::JDCEN)); } Polynomial MeasTable::calcUTtoST() { Polynomial UTSTPoly(2); UTSTPoly.setCoefficient(0, 1.002737909350795); UTSTPoly.setCoefficient(1, +5.9006e-11); UTSTPoly.setCoefficient(2, -5.9e-15); return UTSTPoly; } Double MeasTable::dUT1(Double utc) { #if defined(USE_THREADS) static std::atomic msgDone; #else static Bool msgDone; #endif ///#if defined(USE_THREADS) && !defined(__APPLE__) static thread_local Double res = 0.0; static thread_local Double checkT = -1e6; ///#else // !USE_THREADS (empty Mutex impl) or __APPLE__ /// std::lock_guard lock(theirdUT1Mutex); // Pity. Try to narrow blunt __APPLE__ cond. /// static Double res = 0.0; /// static Double checkT = -1e6; ///#endif if ( !nearAbs(utc, checkT, 0.04)) { checkT = utc; if (!MeasIERS::get(res, MeasIERS::MEASURED, MeasIERS::dUT1, utc)) { // It is harmless if the message accidentally appears multiple times. if (!msgDone) { LogIO os(LogOrigin("MeasTable", "dUT1(Double)", WHERE)); os << LogIO::NORMAL3 << "High precision dUT1 information not available." << LogIO::POST; msgDone = True; } } } /// cout << "dutc1 " << res << endl; return res; } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/MeasTable.h000066400000000000000000000507621476623553700207540ustar00rootroot00000000000000//# MeasTable.h: MeasTable provides Measure computing database data //# Copyright (C) 1995-1999,2000-2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MEASTABLE_H #define MEASURES_MEASTABLE_H //# Includes #include #include // calcPlanetary(MeasJPL::Files *) #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class RotMatrix; class Euler; // // MeasTable provides Measure computing database data // // // // // //
      • Measure class //
      • MeasData class for constant data //
      • Aipsrc class for data placement // // // // MeasTable from Measure and Table // // // // MeasTable contains the database interface for all // data necessary for precession, nutation and other // Measure related calculations.
        // All data are obtained by calls to a method. E.g. // fundArg(1) will provide the first fundamental argument for // nutation calculations, i.e. 'l'.
        // This class contains no constructors or destructors, only static // methods and (static) constants. //
        References:
        Explanatory supplements to the Astronomical Almanac //
        C. Ron and J. Vondrak, Bull. Astron. Inst. Czechosl. 37, p96, 1986 //
        M. Soma, Th. Hirayama and H. Kinoshita, Celest. Mech. 41, p389, 1988 //
        V.S. Gubanov, Astron. Zh. 49, p1112, 1972 // // Where strings are passed in as arguments (observatory names, sources), they // will be case insensitive, and minimum match. //
        // // // Usage examples can be found in Precession // // // // To create a clean interface between the actual calculations and the // methods to obtain the parameters for these calculations. Note that the // tables are in general in the format and units found in the literature. This // is to be able to easy check and change them. However, in the future // re-arrangement could produce faster and more compact code. // // // //
      • more database interfaces, rather than constants // class MeasTable { public: //# Enumerations // Types to be used in different calls enum Types { // Planetary information MERCURY = 1, VENUS = 2, EARTH = 3, MARS = 4, JUPITER = 5, SATURN = 6, URANUS = 7, NEPTUNE = 8, PLUTO = 9, MOON = 10, SUN = 11, // Solar system barycentre BARYSOLAR = 12, // Earth-Moon system barycentre BARYEARTH = 13, // Nutations NUTATION = 14, // Librations LIBRATION = 15, // Number of types N_Types }; // Codes for JPL constants: order should be same as in MeasJPL, length less // than or equal enum JPLconst { // Light velocity used in AU/d CAU, // Solar mass (GM0)/c2 in AU GMS, // AU in km AU, // Solar radius in AU RADS, // # of codes N_JPLconst }; //# General Member Functions // Selection related data // // Are the IAU2000 precession/nutation to be used or not (IAU1984) // Note that an Aipsrc::reRead() is not reflected in the return value here. static Bool useIAU2000(); // If IAU2000 model, do we use the high precision 2000A model? // Note that an Aipsrc::reRead() is not reflected in the return value here. static Bool useIAU2000A(); // // Precession related data // // Get the precession-rate part of the IAU2000 precession-nutation models // (which 0=dpsi (long) and 1=deps (obliquity) and 2 =0) static Double precRate00(const uInt which); // Get the frame bias matrix for IAU2000 model. static RotMatrix frameBias00(); // Generate the precession calculation polynomials for a fixed Epoch T // in the result area specified. // T is given in Julian centuries since J2000.0. static void precessionCoef(Double T, Polynomial result[3]); // Generate the precession polynomials for IAU2000 system. static void precessionCoef2000(Polynomial result[3]); // Generate the precession polynomials for 1950 system for a fixed Epoch T // in the area specified. T is given in Tropical centuries since B1850.0 static void precessionCoef1950(Double T, Polynomial result[3]); // // Nutation related data // // Generate the polynomial for the fundamental arguments (eps, l, l', // F, D, omega) as a function of Julian centuries // static const Polynomial &fundArg(uInt which); static const Polynomial &fundArg1950(uInt which); static const Polynomial &fundArg2000(uInt which); // // Get the planetary arguments (L, L', F, D, Om, Me, Ve, E, Ma, Ju Sa, // Ur, Ne, pre) static const Polynomial &planetaryArg2000(uInt which); // Generate the which' vector of the nutation series arguments // static const Double* mulArg(uInt which); static const Double* mulArg1950(uInt which); static const Double* mulArg2000A(uInt which); static const Double* mulArg2000B(uInt which); static const Double* mulPlanArg2000A(uInt which); // // Generate the which' vector of the equation of equinoxes (IAU2000) // complementary terms series arguments static const Double* mulArgEqEqCT2000(uInt which); // Generate the which' vector of the nutation series multipliers // at T, measured in Julian centuries since J2000.0, respectively B1900.0 // static std::shared_ptr> mulSC(Double time, Double epsilon); static std::shared_ptr> mulSC1950(Double time, Double epsilon); static std::shared_ptr> mulSC2000A(Double time, Double epsilon); static std::shared_ptr> mulSC2000B(Double time, Double epsilon); static const Double* mulPlanSC2000A(uInt which); // // Generate the which' vector of the equation of equinoxes (IAU2000) // complementary terms series multipliers // at T, measured in Julian centuries since J2000.0, respectively B1900.0 static const Double* mulSCEqEqCT2000(uInt which); // Get nutation angles corrections for UTC T in rad. // which = 0 : dPsi as given by IERS for IAU nutation theory; // = 1: dEps as same. static Double dPsiEps(uInt which, Double T); // // Planetary (JPL DE) related data // // Get the position (AU or rad) and velocity (AU/d or rad/d) for specified // code at TDB T. The ephemeris to use (now DE200 or DE405) can be selected // with the 'measures.jpl.ephemeris' aipsrc resource (default DE200). static Vector Planetary(MeasTable::Types which, Double T); // Get the JPL DE constant indicated static Double Planetary(MeasTable::JPLconst what); // // Observatory positions // // Initialise list of all observatories from Observatories table // Called using theirObsInitOnce. static void initObservatories(); // Get list of all observatories static const Vector &Observatories(); // Get position of observatory nam (False if not present) static Bool Observatory(MPosition &obs, const String &nam); // Get _absolute_ path to AntennaResponses table of observatory // nam. It returns False if no _valid_ path can be found or the // observatory is unknown. If the observatory is known, antRespPath will // be set to the entry in the AntennaResponses column of the // Observatories table even if it doesn't describe a valid path; if the // entry is not an absolute path, the data directory name will be // prepended and validity verified. static Bool AntennaResponsesPath(String &antRespPath, const String &nam); // // Source list positions // // Initialise list of all source from Sources table // Called using theirSrcInitOnce. static void initSources(); // Get list of all sources static const Vector &Sources(); // Get position of source nam (False if not present) static Bool Source(MDirection &obs, const String &nam); // // Rest frequencies // // Initialise list from internal Table for now // Called using theirLinesInitOnce. static void initLines(); // Get list of all frequencies static const Vector &Lines(); // Get frequency of line name (False if not present) static Bool Line(MFrequency &obs, const String &nam); // // Initialise list of IGRF data // Called using theirIGRFInitOnce. static void initIGRF(); // Earth magnetic field (IGRF) data // Get the harmonic terms for specified time (mjd) static Vector IGRF(Double t); // Aberration related data // // Generate the polynomial for the fundamental arguments (l1-l8, w, D, l, // l', F) for the Ron/Vondrak aberration calculations as a function of // Julian centuries(J2000), or the comparable ones for the Gubanov expansion // (B1950). // static const Polynomial &aberArg(uInt which); static const Polynomial &aberArgDeriv(uInt which); static const Polynomial &aber1950Arg(uInt which); static const Polynomial &aber1950ArgDeriv(uInt which); // // Generate the 'which' vector of the aberration series arguments // static const Double* mulAberArg(uInt which); static const Double* mulAber1950Arg(uInt which); static const Double* mulAberSunArg(uInt which); static const Double* mulAberEarthArg(uInt which); // // Generate the 'which' vector of the aberration series multipliers // at T, measured in Julian centuries since J2000.0 (or J1900.0, yes, // J1900.0, for B1950). // static std::shared_ptr> mulAber(Double time, Double epsilon); static std::shared_ptr> mulAber1950(Double time, Double epsilon); static const Vector &mulSunAber(uInt which); static const Vector &mulEarthAber(uInt which); // // Get the E-terms of Aberration correction (0 for position, 1 for velocity) // static const Vector &AberETerm(uInt which); // // // Diurnal aberration factor static Double diurnalAber(Double radius, Double T); // LSR (kinematical) velocity conversion: 0 gives J2000; 1 gives B1950. // In both cases a velocity of 20.0 km/s is assumed, and a B1900 RA/Dec // direction of (270,30) degrees. This value has been defined between // the groups doing HI radio work in the mid 1950s. static const Vector &velocityLSRK(uInt which); // LSR (dynamical, IAU definition). Velocity (9,12,7) km/s in galactic // coordinates. Or 16.552945 towards l,b = 53.13, +25.02 deg. // 0 gives J2000, 1 gives B1950 velocities. static const Vector &velocityLSR(uInt which); // Velocity of LSR with respect to galactic centre. 220 km/s in direction // l,b = 270, +0 deg. 0 returns J2000, 1 B1950 static const Vector &velocityLSRGal(uInt which); // Velocity of Local Group wrt bary center (F.Ghigo): 308km/s towards // l,b = 105,-7. 0 for J2000, 1 for B1950 static const Vector &velocityCMB(uInt which); // Velocity of CMB wrt bary center (F.Ghigo): 369.5km/s towards // l,b = 264.4,48.4. 0 for J2000, 1 for B1950 static const Vector &velocityLGROUP(uInt which); // Earth and Sun position related data // // Fundamental arguments for Soma et al. methods // static const Polynomial &posArg(uInt which); // Precomputed derivative of PosArg static const Polynomial &posArgDeriv(uInt which); // // Generate the which' vector of the position series arguments // static const Double* mulPosEarthXYArg(uInt which); static const Double* mulPosEarthZArg(uInt which); static const Double* mulPosSunXYArg(uInt which); static const Double* mulPosSunZArg(uInt which); // // Generate the which' vector of the position series multipliers // at T, measured in Julian centuries since J2000.0 // static std::shared_ptr> mulPosEarthXY(Double time, Double epsilon); static std::shared_ptr> mulPosEarthZ (Double time, Double epsilon); static std::shared_ptr> mulPosSunXY (Double time, Double epsilon); static std::shared_ptr> mulPosSunZ (Double time, Double epsilon); // // Get the rotation matrix to change position from ecliptic to rectangular // for Soma et al. analytical expression static const RotMatrix &posToRect(); // Get the rotation matrix to change position from rectangular to ecliptic // for Soma et al. analytical expression static const RotMatrix &rectToPos(); // Get the rotation matrix from galactic to supergalactic. // Based on De Vaucouleurs 1976: Pole at 47.37/6.32 deg; 137.37 l0 // Euler angles: 90, 83.68, 47.37 degrees static const RotMatrix &galToSupergal(); // Get the rotation matrix from ICRS to J2000/FK5. // Based on the IAU 2000 resolutions (the bias matrix) static const RotMatrix &ICRSToJ2000(); // // Position related routines // // Equatorial radius (0) and flattening(1) of geodetic reference spheroids static Double WGS84(uInt which); // // Polar motion related routines // // Get the polar motion (-x,-y,0)(2,1,3) angles at the given epoch static Euler polarMotion(Double ut); // // Time related routines // // WARNING given if correction not obtainable // // //
      • AipsError if table seems to be corrupted // // // Give TAI-UTC (in s) for MJD utc UTC static Double dUTC(Double utc); // UT1-UTC (in s) for MJD tai TAI static Double dUT1(Double utc); // TDT-TAI (in s) for MJD tai TAI. Note this is equal to TT2000-TAI static Double dTAI(Double tai=0.0); // TDB-TDT (in s) for MJD ut1 UT1 static Double dTDT(Double ut1); // TCB-TDB (in s) for MJD tai TAI static Double dTDB(Double tai); // TCG-TT (in s) for MJD tai TAI static Double dTCG(Double tai); // GMST1 at MJD ut1 UT1 static Double GMST0(Double ut1); // GMST (IAU2000) including the ERA (IAU2000 Earth Rotation Angle) in rad static Double GMST00(Double ut1, Double tt); // Earth Rotation Angle (IAU2000) in rad static Double ERA00(Double ut1); // s' (IAU2000) in rad (approximate value) static Double sprime00(Double tt); // UT1 at GMSD gmst1 GMST1 static Double GMUT0(Double gmst1); // Ratio UT1/MST at MJD ut1 UT1 static Double UTtoST(Double ut1); // private: // Copy assign, NOT defined MeasTable &operator=(const MeasTable &other); //# General member functions static void doInitObservatories(); static void doInitLines(); static void doInitSources(); static void doInitIGRF(); // The calcNNN() functions are helpers to initialize // function scope static variables in the NNN() callers. // Calculate precessionCoef // static void calcPrecesCoef(Double T, Polynomial result[3], const Double coeff[3][6]); static void calcPrecesCoef2000(Polynomial result[3], const Double coeff[3][6]); // // Calculate fundArg // static std::vector> calcFundArg(const Double coeff[6][4]); static std::vector> calcFundArg00(const Double coeff[6][5]); static std::vector> calcPlanArg00(const Double coeff[8][2]); // // Calculate planetary data // static void calcPlanetary(MeasJPL::Files* fil); static void calcPlanetaryConstants(Double cn[MeasTable::N_JPLconst]); // // Calculate aberration data // static std::vector> calcAberArg(); static std::vector> calcAberArgDeriv(); static std::vector> calcAber1950Arg(); static std::vector> calcAber1950ArgDeriv(); static std::vector> calcMulSunAber(); static std::vector> calcMulEarthAber(); static std::vector> calcAberETerm(); // // Calculate velocity data // static std::vector> calcVelocityLSRK(); static std::vector> calcVelocityLSR(); static std::vector> calcVelocityLSRGal(); static std::vector> calcVelocityLGROUP(); static std::vector> calcVelocityCMB(); // // Calculate Earth and Sun position data // static std::vector> calcPosArg(); static std::vector> calcPosArgDeriv(); // // Calculate some of the rotation matrices for coordinate conversion // static RotMatrix calcRectToPos(); static RotMatrix calcICRSToJ2000(); // // Calculate time related conversion data // For dUTC() pack vars for clean initialization of function scope statics. // Thread-safe (C++11). For pre-C++11 depends on compiler (GCC, Clang make it so). struct Statics_dUTC { Double (*LEAP)[4]; Int N; }; // static Statics_dUTC calc_dUTC(); static Polynomial calcGMST0(); static Polynomial calcGMST00(); static Polynomial calcERA00(); static Polynomial calcGMUT0(); static Polynomial calcUTtoST(); // //# Data // Planetary table data // static std::once_flag theirPlanetaryInitOnceFlag; static std::once_flag theirPlanetaryConstantsInitOnceFlag; // // Multipliers for nutation, etc. // static MeasTableMulSC theirMulSC; static MeasTableMulSC1950 theirMulSC1950; static MeasTableMulSC2000A theirMulSC2000A; static MeasTableMulSC2000B theirMulSC2000B; static MeasTableMulAber theirMulAber; static MeasTableMulAber1950 theirMulAber1950; static MeasTableMulPosSunXY theirMulPosSunXY; static MeasTableMulPosSunZ theirMulPosSunZ; static MeasTableMulPosEarthXY theirMulPosEarthXY; static MeasTableMulPosEarthZ theirMulPosEarthZ; // // Observatories table data // static std::once_flag theirObsInitOnceFlag; static Vector obsNams; static Vector obsPos; static Vector antResponsesPath; // // Spectral line table data // static std::once_flag theirLinesInitOnceFlag; static Vector lineNams; static Vector linePos; // // Sources table data // static std::once_flag theirSrcInitOnceFlag; static Vector srcNams; static Vector srcPos; // // IGRF data // static std::once_flag theirIGRFInitOnceFlag; static Double dtimeIGRF; static Double firstIGRF; static Double lastIGRF; static Double time0IGRF; static Double timeIGRF; static std::vector> coefIGRF; static std::vector> dIGRF; // ///#if !defined(USE_THREADS) || defined(__APPLE__) /// static std::mutex theirdUT1Mutex; ///#endif }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MeasTableMul.cc000066400000000000000000002603151476623553700215650ustar00rootroot00000000000000//# MeasTableMul.cc: Nutation multiplication coefficient for MeasTable //# Copyright (C) 1995-1999,2000-2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MeasTableMul::MeasTableMul() : itsLastUsed (0) {} void MeasTableMul::clear() { itsTimes.resize (0); itsUsed.resize (0); itsArrays.resize (0); } std::shared_ptr> MeasTableMul::getArray (Double time, Double epsilon) { { // cache lookup must be thread-safe std::lock_guard locker(itsMutex); // See if a time (within the required epsilon) has already been calculated. // If so, return it. // Note: locking needs to be done outside this class. for (size_t i=0; i>(itsDefArray.shape()); *arr = itsDefArray; calc (*arr, time); { // cache insertion must also be thread-safe std::lock_guard locker(itsMutex); // Determine where to insert in the vector. // If not full yet, put it at the end and first inser // Otherwise use the least recently used entry. if (itsArrays.size() < itsArrays.capacity()) { ///cerr << "new mulsc at " << itsUsed.size() << " for " << time<& result, Polynomial poly[], Int nrowTD, const Long coeffTD[][5], Int nrowSC, const Short coeffSC[][2]) { for (Int i=0; i(2); poly[2*i+j].setCoefficient(0, coeffTD[i][1+2*j] * C::arcsec*1e-4); poly[2*i+j].setCoefficient(1, coeffTD[i][2+2*j] * C::arcsec*1e-5); } } result.resize (4, nrowSC); result = 0.; for (Int i=0; i& result, Double time, const Polynomial poly[], Int nrowTD, const Long coeffTD[][5]) { for (Int i=0; i& result, Double time) { doCalc (result, time, itsPoly, 15, theirMULTD); } MeasTableMulSC1950::MeasTableMulSC1950() {} void MeasTableMulSC1950::init() { doInit (itsDefArray, itsPoly, 13, theirMULTD, 69, theirMULSC); } void MeasTableMulSC1950::calc(Matrix& result, Double time) { doCalc (result, time, itsPoly, 13, theirMULTD); } MeasTableMulSC2000Base::MeasTableMulSC2000Base() {} void MeasTableMulSC2000Base::doInit(Matrix& result, Polynomial poly[], Int nrowSC, const Long coeffSC[][6]) { result.resize (6, nrowSC); result = 0.; for (Int i=0; i(2); poly[2*i+j].setCoefficient(0, coeffSC[i][0+3*j]*C::arcsec*1e-7); poly[2*i+j].setCoefficient(1, coeffSC[i][1+3*j]*C::arcsec*1e-7); } result(2,i) = coeffSC[i][1]*C::arcsec*1e-7; result(3,i) = coeffSC[i][4]*C::arcsec*1e-7; result(4,i) = coeffSC[i][2]*C::arcsec*1e-7; result(5,i) = coeffSC[i][5]*C::arcsec*1e-7; } } void MeasTableMulSC2000Base::doCalc(Matrix& result, Double time, const Polynomial poly[], Int nrowSC) { for (Int i=0; i& result, Double time) { doCalc (result, time, itsPoly, 678); } MeasTableMulSC2000B::MeasTableMulSC2000B() {} void MeasTableMulSC2000B::init() { doInit (itsDefArray, itsPoly, 77, theirMULSC); } void MeasTableMulSC2000B::calc(Matrix& result, Double time) { doCalc (result, time, itsPoly, 77); } MeasTableMulAber::MeasTableMulAber() {} void MeasTableMulAber::init() { UnitVal AUperDay(1e-8,"AU/d"); double factor = AUperDay.getFac(); for (Int i=0; i<3; ++i) { for (Int j=0; j<6; ++j) { itsPoly[6*i+j] = Polynomial(2); for (Int k=0; k<3; ++k) { itsPoly[6*i+j].setCoefficient(k, theirMABERTD[i][k+3*j]*factor); } } } itsDefArray.resize (12, 80); itsDefArray = 0.; for (Int i=0; i<80; ++i) { for (Int j=0; j<6; ++j) { itsDefArray(j,i) = theirMABER[i][j] * factor; } } } void MeasTableMulAber::calc(Matrix& result, Double time) { for (Int i=0; i<3; ++i) { // get fundamental argument coefficients for (Int j=0; j<6; ++j) { result(j,i) = itsPoly[6*i+j](time); result(j+6,i) = (itsPoly[6*i+j].derivative())(time); } } } MeasTableMulAber1950::MeasTableMulAber1950() {} void MeasTableMulAber1950::init() { UnitVal AUperDay(1e-8,"AU/d"); itsFactor = AUperDay.getFac(); itsDefArray.resize (12,132); itsDefArray = 0.; for (Int i=0; i<130; ++i) { for (Int j=0; j<6; ++j) { itsDefArray(j,i) = theirMABER[i][j] * itsFactor; } } for (Int i=0; i<2; ++i) { for (Int j=0; j<6; ++j) { itsDefArray(j,130+i) = theirABERSPEC[i][j] * itsFactor; } } } void MeasTableMulAber1950::calc(Matrix& result, Double time) { for (Int i=0; i<10; ++i) { // get fundamental argument coefficients Int k = theirABERT1T[i]; for (Int j=0; j<6; ++j) { result(j,k) = theirMABER[k][j] * itsFactor * time; result(j+6,k) = theirMABER[k][j] * itsFactor; // d/dT } } for (Int i=0; i<2; ++i) { // get fundamental argument coefficients Int k = theirABERT2T[i]; for (Int j=0; j<6; ++j) { result(j,k) *= time; // Already multiplied by T in ABERT1T result(j+6,k) *= 2*time; // d/dT } } for (Int i=0; i<1; ++i) { // get fundamental argument coefficients Int k = theirABERT3T[i]; for (Int j=0; j<6; ++j) { result(j,k) *= time; // Already multiplied by T**2 in ABERT2T result(j+6,k) *= 1.5*time; // d/dT: 1.5 * T * 2 * T = 3 * T**2 } } } MeasTableMulPosSunXY::MeasTableMulPosSunXY() {} void MeasTableMulPosSunXY::init() { itsDefArray.resize(8,98); itsDefArray = 0.; for (Int i=0; i<98; ++i) { itsDefArray(0,i) = theirMPOSXY[i][0] * C::degree; itsDefArray(1,i) = theirMPOSXY[i][1] * 1e-10; itsDefArray(2,i) = theirMPOSXY[i][2] * C::degree; itsDefArray(3,i) = theirMPOSXY[i][3] * 1e-10; } } void MeasTableMulPosSunXY::calc(Matrix& result, Double time) { for (Int i=84; i<98; ++i) { // get fundamental argument coefficients result(1,i) = theirMPOSXY[i][1] * 1e-10 * time; result(3,i) = theirMPOSXY[i][3] * 1e-10 * time; result(5,i) = theirMPOSXY[i][1] * 1e-10; result(7,i) = theirMPOSXY[i][3] * 1e-10; } } MeasTableMulPosSunZ::MeasTableMulPosSunZ() {} void MeasTableMulPosSunZ::init() { itsDefArray.resize(4,29); itsDefArray = 0.; for (Int i=0; i<29; ++i) { itsDefArray(0,i) = theirMPOSZ[i][0] * C::degree; itsDefArray(1,i) = theirMPOSZ[i][1] * 1e-10; } } void MeasTableMulPosSunZ::calc(Matrix& result, Double time) { for (Int i=26; i<29; ++i) { // get fundamental argument coefficients result(1,i) = theirMPOSZ[i][1] * 1e-10 * time; result(3,i) = theirMPOSZ[i][1] * 1e-10; } } MeasTableMulPosEarthXY::MeasTableMulPosEarthXY() {} void MeasTableMulPosEarthXY::init() { itsDefArray.resize(8,189); itsDefArray = 0.; for (Int i=0; i<189; ++i) { itsDefArray(0,i) = theirMPOSXY[i][0] * C::degree; itsDefArray(1,i) = theirMPOSXY[i][1] * 1e-10; itsDefArray(2,i) = theirMPOSXY[i][2] * C::degree; itsDefArray(3,i) = theirMPOSXY[i][3] * 1e-10; } } void MeasTableMulPosEarthXY::calc(Matrix& result, Double time) { for (Int i=174; i<189; ++i) { // get fundamental argument coefficients result(1,i) = theirMPOSXY[i][1] * 1e-10 * time; result(3,i) = theirMPOSXY[i][3] * 1e-10 * time; result(5,i) = theirMPOSXY[i][1] * 1e-10; result(7,i) = theirMPOSXY[i][3] * 1e-10; } for (Int i=186; i<189; ++i) { // get fundamental argument coefficients result(1,i) *= time; result(3,i) *= time; result(5,i) *= 2*time; result(7,i) *= 2*time; } } MeasTableMulPosEarthZ::MeasTableMulPosEarthZ() {} void MeasTableMulPosEarthZ::init() { itsDefArray.resize(4,32); itsDefArray = 0.; for (Int i=0; i<32; ++i) { itsDefArray(0,i) = theirMPOSZ[i][0] * C::degree; itsDefArray(1,i) = theirMPOSZ[i][1] * 1e-10; } } void MeasTableMulPosEarthZ::calc(Matrix& result, Double time) { for (Int i=28; i<32; ++i) { // get fundamental argument coefficients result(1,i) = theirMPOSZ[i][1] * 1e-10 * time; result(3,i) = theirMPOSZ[i][1] * 1e-10; } for (Int i=31; i<32; ++i) { // get fundamental argument coefficients result(1,i) *= time; result(3,i) *= 2*time; } } // Define the various constants. const Long MeasTableMulSC::theirMULTD[15][5] = { {0 ,-171996 ,-1742 ,92025 ,89}, {1 ,2062 ,2 ,-895 ,5}, {8 ,-13187 ,-16 ,5736 ,-31}, {9 ,1426 ,-34 ,54 ,-1}, {10 ,-517 ,12 ,224 ,-6}, {11 ,217 ,-5 ,-95 ,3}, {12 ,129 ,1 ,-70 ,0}, {15 ,17 ,-1 ,0 ,0}, {17 ,-16 ,1 ,7 ,0}, {30 ,-2274 ,-2 ,977 ,-5}, {31 ,712 ,1 ,-7 ,0}, {32 ,-386 ,-4 ,200 ,0}, {33 ,-301 ,0 ,129 ,-1}, {37 ,63 ,1 ,-33 ,0}, {38 ,-58 ,-1 ,32 ,0} }; const Short MeasTableMulSC::theirMULSC[106][2] = { {0 ,0 }, {0 ,0 }, {46 ,-24 }, {11 ,0 }, {-3 ,1 }, {-3 ,0 }, {-2 ,1 }, {1 ,0 }, {0 ,0 }, {0 ,0 }, {0 ,0 }, {0 ,0 }, {0 ,0 }, {48 ,1 }, {-22 ,0 }, {0 ,0 }, {-15 ,9 }, {0 ,0 }, {-12 ,6 }, {-6 ,3 }, {-5 ,3 }, {4 ,-2 }, {4 ,-2 }, {-4 ,0 }, {1 ,0 }, {1 ,0 }, {-1 ,0 }, {1 ,0 }, {1 ,0 }, {-1 ,0 }, {0 ,0 }, {0 ,0 }, {0 ,0 }, {0 ,0 }, {-158 ,-1 }, {123 ,-53 }, {63 ,-2 }, {0 ,0 }, {0 ,0 }, {-59 ,26 }, {-51 ,27 }, {-38 ,16 }, {29 ,-1 }, {29 ,-12 }, {-31 ,13 }, {26 ,-1 }, {21 ,-10 }, {16 ,-8 }, {-13 ,7 }, {-10 ,5 }, {-7 ,0 }, {7 ,-3 }, {-7 ,3 }, {-8 ,3 }, {6 ,0 }, {6 ,-3 }, {-6 ,3 }, {-7 ,3 }, {6 ,-3 }, {-5 ,3 }, {5 ,0 }, {-5 ,3 }, {-4 ,0 }, {4 ,0 }, {-4 ,0 }, {-3 ,0 }, {3 ,0 }, {-3 ,1 }, {-3 ,1 }, {-2 ,1 }, {-3 ,1 }, {-3 ,1 }, {2 ,-1 }, {-2 ,1 }, {2 ,-1 }, {-2 ,1 }, {2 ,0 }, {2 ,-1 }, {1 ,-1 }, {-1 ,0 }, {1 ,-1 }, {-2 ,1 }, {-1 ,0 }, {1 ,-1 }, {-1 ,1 }, {-1 ,1 }, {1 ,0 }, {1 ,0 }, {1 ,-1 }, {-1 ,0 }, {-1 ,0 }, {1 ,0 }, {1 ,0 }, {-1 ,0 }, {1 ,0 }, {1 ,0 }, {-1 ,0 }, {-1 ,0 }, {-1 ,0 }, {-1 ,0 }, {-1 ,0 }, {-1 ,0 }, {-1 ,0 }, {1 ,0 }, {-1 ,0 }, {1 ,0} }; const Long MeasTableMulSC1950::theirMULTD[13][5] = { {0 ,-172327 ,-1737 ,92100 ,91}, {1 ,2088 ,2 ,-904 ,4}, {7 ,-12729 ,-13 ,5522 ,-29}, {8 ,1261 ,-31 ,0 ,0}, {9 ,-497 ,12 ,216 ,-6}, {10 ,214 ,-5 ,-93 ,3}, {11 ,124 ,1 ,-66 ,0}, {14 ,16 ,-1 ,0 ,0}, {16 ,-15 ,1 ,7 ,0}, {23 ,-2037 ,-2 ,884 ,-5}, {24 ,675 ,1 ,0 ,0}, {25 ,-342 ,-4 ,183 ,0}, {26 ,-261 ,0 ,113 ,-1} }; const Short MeasTableMulSC1950::theirMULSC[69][2] = { {0 ,0 }, {0 ,0 }, {45 ,-24 }, {10 ,0 }, {-4 ,2 }, {-3 ,2 }, {-2 ,0 }, {0 ,0 }, {0 ,0 }, {0 ,0 }, {0 ,0 }, {0 ,0 }, {45 ,0 }, {-21,0 }, {0 ,0 }, {-15,8 }, {0 ,0 }, {-10,5 }, {-5 ,3 }, {-5 ,3 }, {4 ,-2 }, {3 ,-2 }, {-3 ,0 }, {0 ,0 }, {0 ,0 }, {0 ,0 }, {0 ,0 }, {-149,0 }, {114 ,-50 }, {60 ,0 }, {58 ,-31 }, {-57,30 }, {-52,22 }, {-44,23 }, {-32,14 }, {28 ,0 }, {26 ,-11 }, {-26,11 }, {25 ,0 }, {19 ,-10 }, {14 ,-7 }, {-13,7 }, {-9 ,5 }, {-7 ,0 }, {7 ,-3 }, {6 ,0 }, {-6 ,3 }, {-6 ,3 }, {-6 ,3 }, {6 ,-2 }, {-5 ,3 }, {-5 ,3 }, {5 ,-3 }, {-4 ,0 }, {-4 ,0 }, {4 ,0 }, {4 ,0 }, {-4 ,2 }, {3 ,0 }, {-3 ,0 }, {-3 ,0 }, {-2 ,0 }, {-2 ,0 }, {2 ,0 }, {-2 ,0 }, {-2 ,0 }, {-2 ,0 }, {2 ,0 }, {-2 ,0 } }; // Luni-Solar nutation coefficients, unit 1e-7 arcsec const Long MeasTableMulSC2000A::theirMULSC[678][6] = { // Longitude Obliquity // sin t.sin cos cos t.cos sin {-172064161, -174666, 33386, 92052331, 9086, 15377}, // 1 { -13170906, -1675, -13696, 5730336, -3015, -4587}, // 2 { -2276413, -234, 2796, 978459, -485, 1374}, // 3 { 2074554, 207, -698, -897492, 470, -291}, // 4 { 1475877, -3633, 11817, 73871, -184, -1924}, // 5 { -516821, 1226, -524, 224386, -677, -174}, // 6 { 711159, 73, -872, -6750, 0, 358}, // 7 { -387298, -367, 380, 200728, 18, 318}, // 8 { -301461, -36, 816, 129025, -63, 367}, // 9 { 215829, -494, 111, -95929, 299, 132}, // 10 { 128227, 137, 181, -68982, -9, 39}, // 11 { 123457, 11, 19, -53311, 32, -4}, // 12 { 156994, 10, -168, -1235, 0, 82}, // 13 { 63110, 63, 27, -33228, 0, -9}, // 14 { -57976, -63, -189, 31429, 0, -75}, // 15 { -59641, -11, 149, 25543, -11, 66}, // 16 { -51613, -42, 129, 26366, 0, 78}, // 17 { 45893, 50, 31, -24236, -10, 20}, // 18 { 63384, 11, -150, -1220, 0, 29}, // 19 { -38571, -1, 158, 16452, -11, 68}, // 20 { 32481, 0, 0, -13870, 0, 0}, // 21 { -47722, 0, -18, 477, 0, -25}, // 22 { -31046, -1, 131, 13238, -11, 59}, // 23 { 28593, 0, -1, -12338, 10, -3}, // 24 { 20441, 21, 10, -10758, 0, -3}, // 25 { 29243, 0, -74, -609, 0, 13}, // 26 { 25887, 0, -66, -550, 0, 11}, // 27 { -14053, -25, 79, 8551, -2, -45}, // 28 { 15164, 10, 11, -8001, 0, -1}, // 29 { -15794, 72, -16, 6850, -42, -5}, // 30 { 21783, 0, 13, -167, 0, 13}, // 31 { -12873, -10, -37, 6953, 0, -14}, // 32 { -12654, 11, 63, 6415, 0, 26}, // 33 { -10204, 0, 25, 5222, 0, 15}, // 34 { 16707, -85, -10, 168, -1, 10}, // 35 { -7691, 0, 44, 3268, 0, 19}, // 36 { -11024, 0, -14, 104, 0, 2}, // 37 { 7566, -21, -11, -3250, 0, -5}, // 38 { -6637, -11, 25, 3353, 0, 14}, // 39 { -7141, 21, 8, 3070, 0, 4}, // 40 { -6302, -11, 2, 3272, 0, 4}, // 41 { 5800, 10, 2, -3045, 0, -1}, // 42 { 6443, 0, -7, -2768, 0, -4}, // 43 { -5774, -11, -15, 3041, 0, -5}, // 44 { -5350, 0, 21, 2695, 0, 12}, // 45 { -4752, -11, -3, 2719, 0, -3}, // 46 { -4940, -11, -21, 2720, 0, -9}, // 47 { 7350, 0, -8, -51, 0, 4}, // 48 { 4065, 0, 6, -2206, 0, 1}, // 49 { 6579, 0, -24, -199, 0, 2}, // 50 { 3579, 0, 5, -1900, 0, 1}, // 51 { 4725, 0, -6, -41, 0, 3}, // 52 { -3075, 0, -2, 1313, 0, -1}, // 53 { -2904, 0, 15, 1233, 0, 7}, // 54 { 4348, 0, -10, -81, 0, 2}, // 55 { -2878, 0, 8, 1232, 0, 4}, // 56 { -4230, 0, 5, -20, 0, -2}, // 57 { -2819, 0, 7, 1207, 0, 3}, // 58 { -4056, 0, 5, 40, 0, -2}, // 59 { -2647, 0, 11, 1129, 0, 5}, // 60 { -2294, 0, -10, 1266, 0, -4}, // 61 { 2481, 0, -7, -1062, 0, -3}, // 62 { 2179, 0, -2, -1129, 0, -2}, // 63 { 3276, 0, 1, -9, 0, 0}, // 64 { -3389, 0, 5, 35, 0, -2}, // 65 { 3339, 0, -13, -107, 0, 1}, // 66 { -1987, 0, -6, 1073, 0, -2}, // 67 { -1981, 0, 0, 854, 0, 0}, // 68 { 4026, 0, -353, -553, 0, -139}, // 69 { 1660, 0, -5, -710, 0, -2}, // 70 { -1521, 0, 9, 647, 0, 4}, // 71 { 1314, 0, 0, -700, 0, 0}, // 72 { -1283, 0, 0, 672, 0, 0}, // 73 { -1331, 0, 8, 663, 0, 4}, // 74 { 1383, 0, -2, -594, 0, -2}, // 75 { 1405, 0, 4, -610, 0, 2}, // 76 { 1290, 0, 0, -556, 0, 0}, // 77 { -1214, 0, 5, 518, 0, 2}, // 78 { 1146, 0, -3, -490, 0, -1}, // 79 { 1019, 0, -1, -527, 0, -1}, // 80 { -1100, 0, 9, 465, 0, 4}, // 81 { -970, 0, 2, 496, 0, 1}, // 82 { 1575, 0, -6, -50, 0, 0}, // 83 { 934, 0, -3, -399, 0, -1}, // 84 { 922, 0, -1, -395, 0, -1}, // 85 { 815, 0, -1, -422, 0, -1}, // 86 { 834, 0, 2, -440, 0, 1}, // 87 { 1248, 0, 0, -170, 0, 1}, // 88 { 1338, 0, -5, -39, 0, 0}, // 89 { 716, 0, -2, -389, 0, -1}, // 90 { 1282, 0, -3, -23, 0, 1}, // 91 { 742, 0, 1, -391, 0, 0}, // 92 { 1020, 0, -25, -495, 0, -10}, // 93 { 715, 0, -4, -326, 0, 2}, // 94 { -666, 0, -3, 369, 0, -1}, // 95 { -667, 0, 1, 346, 0, 1}, // 96 { -704, 0, 0, 304, 0, 0}, // 97 { -694, 0, 5, 294, 0, 2}, // 98 { -1014, 0, -1, 4, 0, -1}, // 99 { -585, 0, -2, 316, 0, -1}, // 100 { -949, 0, 1, 8, 0, -1}, // 101 { -595, 0, 0, 258, 0, 0}, // 102 { 528, 0, 0, -279, 0, 0}, // 103 { -590, 0, 4, 252, 0, 2}, // 104 { 570, 0, -2, -244, 0, -1}, // 105 { -502, 0, 3, 250, 0, 2}, // 106 { -875, 0, 1, 29, 0, 0}, // 107 { -492, 0, -3, 275, 0, -1}, // 108 { 535, 0, -2, -228, 0, -1}, // 109 { -467, 0, 1, 240, 0, 1}, // 110 { 591, 0, 0, -253, 0, 0}, // 111 { -453, 0, -1, 244, 0, -1}, // 112 { 766, 0, 1, 9, 0, 0}, // 113 { -446, 0, 2, 225, 0, 1}, // 114 { -488, 0, 2, 207, 0, 1}, // 115 { -468, 0, 0, 201, 0, 0}, // 116 { -421, 0, 1, 216, 0, 1}, // 117 { 463, 0, 0, -200, 0, 0}, // 118 { -673, 0, 2, 14, 0, 0}, // 119 { 658, 0, 0, -2, 0, 0}, // 120 { -438, 0, 0, 188, 0, 0}, // 121 { -390, 0, 0, 205, 0, 0}, // 122 { 639, -11, -2, -19, 0, 0}, // 123 { 412, 0, -2, -176, 0, -1}, // 124 { -361, 0, 0, 189, 0, 0}, // 125 { 360, 0, -1, -185, 0, -1}, // 126 { 588, 0, -3, -24, 0, 0}, // 127 { -578, 0, 1, 5, 0, 0}, // 128 { -396, 0, 0, 171, 0, 0}, // 129 { 565, 0, -1, -6, 0, 0}, // 130 { -335, 0, -1, 184, 0, -1}, // 131 { 357, 0, 1, -154, 0, 0}, // 132 { 321, 0, 1, -174, 0, 0}, // 133 { -301, 0, -1, 162, 0, 0}, // 134 { -334, 0, 0, 144, 0, 0}, // 135 { 493, 0, -2, -15, 0, 0}, // 136 { 494, 0, -2, -19, 0, 0}, // 137 { 337, 0, -1, -143, 0, -1}, // 138 { 280, 0, -1, -144, 0, 0}, // 139 { 309, 0, 1, -134, 0, 0}, // 140 { -263, 0, 2, 131, 0, 1}, // 141 { 253, 0, 1, -138, 0, 0}, // 142 { 245, 0, 0, -128, 0, 0}, // 143 { 416, 0, -2, -17, 0, 0}, // 144 { -229, 0, 0, 128, 0, 0}, // 145 { 231, 0, 0, -120, 0, 0}, // 146 { -259, 0, 2, 109, 0, 1}, // 147 { 375, 0, -1, -8, 0, 0}, // 148 { 252, 0, 0, -108, 0, 0}, // 149 { -245, 0, 1, 104, 0, 0}, // 150 { 243, 0, -1, -104, 0, 0}, // 151 { 208, 0, 1, -112, 0, 0}, // 152 { 199, 0, 0, -102, 0, 0}, // 153 { -208, 0, 1, 105, 0, 0}, // 154 { 335, 0, -2, -14, 0, 0}, // 155 { -325, 0, 1, 7, 0, 0}, // 156 { -187, 0, 0, 96, 0, 0}, // 157 { 197, 0, -1, -100, 0, 0}, // 158 { -192, 0, 2, 94, 0, 1}, // 159 { -188, 0, 0, 83, 0, 0}, // 160 { 276, 0, 0, -2, 0, 0}, // 161 { -286, 0, 1, 6, 0, 0}, // 162 { 186, 0, -1, -79, 0, 0}, // 163 { -219, 0, 0, 43, 0, 0}, // 164 { 276, 0, 0, 2, 0, 0}, // 165 { -153, 0, -1, 84, 0, 0}, // 166 { -156, 0, 0, 81, 0, 0}, // 167 { -154, 0, 1, 78, 0, 0}, // 168 { -174, 0, 1, 75, 0, 0}, // 169 { -163, 0, 2, 69, 0, 1}, // 170 { -228, 0, 0, 1, 0, 0}, // 171 { 91, 0, -4, -54, 0, -2}, // 172 { 175, 0, 0, -75, 0, 0}, // 173 { -159, 0, 0, 69, 0, 0}, // 174 { 141, 0, 0, -72, 0, 0}, // 175 { 147, 0, 0, -75, 0, 0}, // 176 { -132, 0, 0, 69, 0, 0}, // 177 { 159, 0, -28, -54, 0, 11}, // 178 { 213, 0, 0, -4, 0, 0}, // 179 { 123, 0, 0, -64, 0, 0}, // 180 { -118, 0, -1, 66, 0, 0}, // 181 { 144, 0, -1, -61, 0, 0}, // 182 { -121, 0, 1, 60, 0, 0}, // 183 { -134, 0, 1, 56, 0, 1}, // 184 { -105, 0, 0, 57, 0, 0}, // 185 { -102, 0, 0, 56, 0, 0}, // 186 { 120, 0, 0, -52, 0, 0}, // 187 { 101, 0, 0, -54, 0, 0}, // 188 { -113, 0, 0, 59, 0, 0}, // 189 { -106, 0, 0, 61, 0, 0}, // 190 { -129, 0, 1, 55, 0, 0}, // 191 { -114, 0, 0, 57, 0, 0}, // 192 { 113, 0, -1, -49, 0, 0}, // 193 { -102, 0, 0, 44, 0, 0}, // 194 { -94, 0, 0, 51, 0, 0}, // 195 { -100, 0, -1, 56, 0, 0}, // 196 { 87, 0, 0, -47, 0, 0}, // 197 { 161, 0, 0, -1, 0, 0}, // 198 { 96, 0, 0, -50, 0, 0}, // 199 { 151, 0, -1, -5, 0, 0}, // 200 { -104, 0, 0, 44, 0, 0}, // 201 { -110, 0, 0, 48, 0, 0}, // 202 { -100, 0, 1, 50, 0, 0}, // 203 { 92, 0, -5, 12, 0, -2}, // 204 { 82, 0, 0, -45, 0, 0}, // 205 { 82, 0, 0, -45, 0, 0}, // 206 { -78, 0, 0, 41, 0, 0}, // 207 { -77, 0, 0, 43, 0, 0}, // 208 { 2, 0, 0, 54, 0, 0}, // 209 { 94, 0, 0, -40, 0, 0}, // 210 { -93, 0, 0, 40, 0, 0}, // 211 { -83, 0, 10, 40, 0, -2}, // 212 { 83, 0, 0, -36, 0, 0}, // 213 { -91, 0, 0, 39, 0, 0}, // 214 { 128, 0, 0, -1, 0, 0}, // 215 { -79, 0, 0, 34, 0, 0}, // 216 { -83, 0, 0, 47, 0, 0}, // 217 { 84, 0, 0, -44, 0, 0}, // 218 { 83, 0, 0, -43, 0, 0}, // 219 { 91, 0, 0, -39, 0, 0}, // 220 { -77, 0, 0, 39, 0, 0}, // 221 { 84, 0, 0, -43, 0, 0}, // 222 { -92, 0, 1, 39, 0, 0}, // 223 { -92, 0, 1, 39, 0, 0}, // 224 { -94, 0, 0, 0, 0, 0}, // 225 { 68, 0, 0, -36, 0, 0}, // 226 { -61, 0, 0, 32, 0, 0}, // 227 { 71, 0, 0, -31, 0, 0}, // 228 { 62, 0, 0, -34, 0, 0}, // 229 { -63, 0, 0, 33, 0, 0}, // 230 { -73, 0, 0, 32, 0, 0}, // 231 { 115, 0, 0, -2, 0, 0}, // 232 { -103, 0, 0, 2, 0, 0}, // 233 { 63, 0, 0, -28, 0, 0}, // 234 { 74, 0, 0, -32, 0, 0}, // 235 { -103, 0, -3, 3, 0, -1}, // 236 { -69, 0, 0, 30, 0, 0}, // 237 { 57, 0, 0, -29, 0, 0}, // 238 { 94, 0, 0, -4, 0, 0}, // 239 { 64, 0, 0, -33, 0, 0}, // 240 { -63, 0, 0, 26, 0, 0}, // 241 { -38, 0, 0, 20, 0, 0}, // 242 { -43, 0, 0, 24, 0, 0}, // 243 { -45, 0, 0, 23, 0, 0}, // 244 { 47, 0, 0, -24, 0, 0}, // 245 { -48, 0, 0, 25, 0, 0}, // 246 { 45, 0, 0, -26, 0, 0}, // 247 { 56, 0, 0, -25, 0, 0}, // 248 { 88, 0, 0, 2, 0, 0}, // 249 { -75, 0, 0, 0, 0, 0}, // 250 { 85, 0, 0, 0, 0, 0}, // 251 { 49, 0, 0, -26, 0, 0}, // 252 { -74, 0, -3, -1, 0, -1}, // 253 { -39, 0, 0, 21, 0, 0}, // 254 { 45, 0, 0, -20, 0, 0}, // 255 { 51, 0, 0, -22, 0, 0}, // 256 { -40, 0, 0, 21, 0, 0}, // 257 { 41, 0, 0, -21, 0, 0}, // 258 { -42, 0, 0, 24, 0, 0}, // 259 { -51, 0, 0, 22, 0, 0}, // 260 { -42, 0, 0, 22, 0, 0}, // 261 { 39, 0, 0, -21, 0, 0}, // 262 { 46, 0, 0, -18, 0, 0}, // 263 { -53, 0, 0, 22, 0, 0}, // 264 { 82, 0, 0, -4, 0, 0}, // 265 { 81, 0, -1, -4, 0, 0}, // 266 { 47, 0, 0, -19, 0, 0}, // 267 { 53, 0, 0, -23, 0, 0}, // 268 { -45, 0, 0, 22, 0, 0}, // 269 { -44, 0, 0, -2, 0, 0}, // 270 { -33, 0, 0, 16, 0, 0}, // 271 { -61, 0, 0, 1, 0, 0}, // 272 { 28, 0, 0, -15, 0, 0}, // 273 { -38, 0, 0, 19, 0, 0}, // 274 { -33, 0, 0, 21, 0, 0}, // 275 { -60, 0, 0, 0, 0, 0}, // 276 { 48, 0, 0, -10, 0, 0}, // 277 { 27, 0, 0, -14, 0, 0}, // 278 { 38, 0, 0, -20, 0, 0}, // 279 { 31, 0, 0, -13, 0, 0}, // 280 { -29, 0, 0, 15, 0, 0}, // 281 { 28, 0, 0, -15, 0, 0}, // 282 { -32, 0, 0, 15, 0, 0}, // 283 { 45, 0, 0, -8, 0, 0}, // 284 { -44, 0, 0, 19, 0, 0}, // 285 { 28, 0, 0, -15, 0, 0}, // 286 { -51, 0, 0, 0, 0, 0}, // 287 { -36, 0, 0, 20, 0, 0}, // 288 { 44, 0, 0, -19, 0, 0}, // 289 { 26, 0, 0, -14, 0, 0}, // 290 { -60, 0, 0, 2, 0, 0}, // 291 { 35, 0, 0, -18, 0, 0}, // 292 { -27, 0, 0, 11, 0, 0}, // 293 { 47, 0, 0, -1, 0, 0}, // 294 { 36, 0, 0, -15, 0, 0}, // 295 { -36, 0, 0, 20, 0, 0}, // 296 { -35, 0, 0, 19, 0, 0}, // 297 { -37, 0, 0, 19, 0, 0}, // 298 { 32, 0, 0, -16, 0, 0}, // 299 { 35, 0, 0, -14, 0, 0}, // 300 { 32, 0, 0, -13, 0, 0}, // 301 { 65, 0, 0, -2, 0, 0}, // 302 { 47, 0, 0, -1, 0, 0}, // 303 { 32, 0, 0, -16, 0, 0}, // 304 { 37, 0, 0, -16, 0, 0}, // 305 { -30, 0, 0, 15, 0, 0}, // 306 { -32, 0, 0, 16, 0, 0}, // 307 { -31, 0, 0, 13, 0, 0}, // 308 { 37, 0, 0, -16, 0, 0}, // 309 { 31, 0, 0, -13, 0, 0}, // 310 { 49, 0, 0, -2, 0, 0}, // 311 { 32, 0, 0, -13, 0, 0}, // 312 { 23, 0, 0, -12, 0, 0}, // 313 { -43, 0, 0, 18, 0, 0}, // 314 { 26, 0, 0, -11, 0, 0}, // 315 { -32, 0, 0, 14, 0, 0}, // 316 { -29, 0, 0, 14, 0, 0}, // 317 { -27, 0, 0, 12, 0, 0}, // 318 { 30, 0, 0, 0, 0, 0}, // 319 { -11, 0, 0, 5, 0, 0}, // 320 { -21, 0, 0, 10, 0, 0}, // 321 { -34, 0, 0, 15, 0, 0}, // 322 { -10, 0, 0, 6, 0, 0}, // 323 { -36, 0, 0, 0, 0, 0}, // 324 { -9, 0, 0, 4, 0, 0}, // 325 { -12, 0, 0, 5, 0, 0}, // 326 { -21, 0, 0, 5, 0, 0}, // 327 { -29, 0, 0, -1, 0, 0}, // 328 { -15, 0, 0, 3, 0, 0}, // 329 { -20, 0, 0, 0, 0, 0}, // 330 { 28, 0, 0, 0, 0, -2}, // 331 { 17, 0, 0, 0, 0, 0}, // 332 { -22, 0, 0, 12, 0, 0}, // 333 { -14, 0, 0, 7, 0, 0}, // 334 { 24, 0, 0, -11, 0, 0}, // 335 { 11, 0, 0, -6, 0, 0}, // 336 { 14, 0, 0, -6, 0, 0}, // 337 { 24, 0, 0, 0, 0, 0}, // 338 { 18, 0, 0, -8, 0, 0}, // 339 { -38, 0, 0, 0, 0, 0}, // 340 { -31, 0, 0, 0, 0, 0}, // 341 { -16, 0, 0, 8, 0, 0}, // 342 { 29, 0, 0, 0, 0, 0}, // 343 { -18, 0, 0, 10, 0, 0}, // 344 { -10, 0, 0, 5, 0, 0}, // 345 { -17, 0, 0, 10, 0, 0}, // 346 { 9, 0, 0, -4, 0, 0}, // 347 { 16, 0, 0, -6, 0, 0}, // 348 { 22, 0, 0, -12, 0, 0}, // 349 { 20, 0, 0, 0, 0, 0}, // 350 { -13, 0, 0, 6, 0, 0}, // 351 { -17, 0, 0, 9, 0, 0}, // 352 { -14, 0, 0, 8, 0, 0}, // 353 { 0, 0, 0, -7, 0, 0}, // 354 { 14, 0, 0, 0, 0, 0}, // 355 { 19, 0, 0, -10, 0, 0}, // 356 { -34, 0, 0, 0, 0, 0}, // 357 { -20, 0, 0, 8, 0, 0}, // 358 { 9, 0, 0, -5, 0, 0}, // 359 { -18, 0, 0, 7, 0, 0}, // 360 { 13, 0, 0, -6, 0, 0}, // 361 { 17, 0, 0, 0, 0, 0}, // 362 { -12, 0, 0, 5, 0, 0}, // 363 { 15, 0, 0, -8, 0, 0}, // 364 { -11, 0, 0, 3, 0, 0}, // 365 { 13, 0, 0, -5, 0, 0}, // 366 { -18, 0, 0, 0, 0, 0}, // 367 { -35, 0, 0, 0, 0, 0}, // 368 { 9, 0, 0, -4, 0, 0}, // 369 { -19, 0, 0, 10, 0, 0}, // 370 { -26, 0, 0, 11, 0, 0}, // 371 { 8, 0, 0, -4, 0, 0}, // 372 { -10, 0, 0, 4, 0, 0}, // 373 { 10, 0, 0, -6, 0, 0}, // 374 { -21, 0, 0, 9, 0, 0}, // 375 { -15, 0, 0, 0, 0, 0}, // 376 { 9, 0, 0, -5, 0, 0}, // 377 { -29, 0, 0, 0, 0, 0}, // 378 { -19, 0, 0, 10, 0, 0}, // 379 { 12, 0, 0, -5, 0, 0}, // 380 { 22, 0, 0, -9, 0, 0}, // 381 { -10, 0, 0, 5, 0, 0}, // 382 { -20, 0, 0, 11, 0, 0}, // 383 { -20, 0, 0, 0, 0, 0}, // 384 { -17, 0, 0, 7, 0, 0}, // 385 { 15, 0, 0, -3, 0, 0}, // 386 { 8, 0, 0, -4, 0, 0}, // 387 { 14, 0, 0, 0, 0, 0}, // 388 { -12, 0, 0, 6, 0, 0}, // 389 { 25, 0, 0, 0, 0, 0}, // 390 { -13, 0, 0, 6, 0, 0}, // 391 { -14, 0, 0, 8, 0, 0}, // 392 { 13, 0, 0, -5, 0, 0}, // 393 { -17, 0, 0, 9, 0, 0}, // 394 { -12, 0, 0, 6, 0, 0}, // 395 { -10, 0, 0, 5, 0, 0}, // 396 { 10, 0, 0, -6, 0, 0}, // 397 { -15, 0, 0, 0, 0, 0}, // 398 { -22, 0, 0, 0, 0, 0}, // 399 { 28, 0, 0, -1, 0, 0}, // 400 { 15, 0, 0, -7, 0, 0}, // 401 { 23, 0, 0, -10, 0, 0}, // 402 { 12, 0, 0, -5, 0, 0}, // 403 { 29, 0, 0, -1, 0, 0}, // 404 { -25, 0, 0, 1, 0, 0}, // 405 { 22, 0, 0, 0, 0, 0}, // 406 { -18, 0, 0, 0, 0, 0}, // 407 { 15, 0, 0, 3, 0, 0}, // 408 { -23, 0, 0, 0, 0, 0}, // 409 { 12, 0, 0, -5, 0, 0}, // 410 { -8, 0, 0, 4, 0, 0}, // 411 { -19, 0, 0, 0, 0, 0}, // 412 { -10, 0, 0, 4, 0, 0}, // 413 { 21, 0, 0, -9, 0, 0}, // 414 { 23, 0, 0, -1, 0, 0}, // 415 { -16, 0, 0, 8, 0, 0}, // 416 { -19, 0, 0, 9, 0, 0}, // 417 { -22, 0, 0, 10, 0, 0}, // 418 { 27, 0, 0, -1, 0, 0}, // 419 { 16, 0, 0, -8, 0, 0}, // 420 { 19, 0, 0, -8, 0, 0}, // 421 { 9, 0, 0, -4, 0, 0}, // 422 { -9, 0, 0, 4, 0, 0}, // 423 { -9, 0, 0, 4, 0, 0}, // 424 { -8, 0, 0, 4, 0, 0}, // 425 { 18, 0, 0, -9, 0, 0}, // 426 { 16, 0, 0, -1, 0, 0}, // 427 { -10, 0, 0, 4, 0, 0}, // 428 { -23, 0, 0, 9, 0, 0}, // 429 { 16, 0, 0, -1, 0, 0}, // 430 { -12, 0, 0, 6, 0, 0}, // 431 { -8, 0, 0, 4, 0, 0}, // 432 { 30, 0, 0, -2, 0, 0}, // 433 { 24, 0, 0, -10, 0, 0}, // 434 { 10, 0, 0, -4, 0, 0}, // 435 { -16, 0, 0, 7, 0, 0}, // 436 { -16, 0, 0, 7, 0, 0}, // 437 { 17, 0, 0, -7, 0, 0}, // 438 { -24, 0, 0, 10, 0, 0}, // 439 { -12, 0, 0, 5, 0, 0}, // 440 { -24, 0, 0, 11, 0, 0}, // 441 { -23, 0, 0, 9, 0, 0}, // 442 { -13, 0, 0, 5, 0, 0}, // 443 { -15, 0, 0, 7, 0, 0}, // 444 { 0, 0, -1988, 0, 0, -1679}, // 445 { 0, 0, -63, 0, 0, -27}, // 446 { -4, 0, 0, 0, 0, 0}, // 447 { 0, 0, 5, 0, 0, 4}, // 448 { 5, 0, 0, -3, 0, 0}, // 449 { 0, 0, 364, 0, 0, 176}, // 450 { 0, 0, -1044, 0, 0, -891}, // 451 { -3, 0, 0, 1, 0, 0}, // 452 { 4, 0, 0, -2, 0, 0}, // 453 { 0, 0, 330, 0, 0, 0}, // 454 { 5, 0, 0, -2, 0, 0}, // 455 { 3, 0, 0, -2, 0, 0}, // 456 { -3, 0, 0, 1, 0, 0}, // 457 { -5, 0, 0, 2, 0, 0}, // 458 { 3, 0, 0, -1, 0, 0}, // 459 { 3, 0, 0, 0, 0, 0}, // 460 { 3, 0, 0, 0, 0, 0}, // 461 { 0, 0, 5, 0, 0, 0}, // 462 { 0, 0, 0, 1, 0, 0}, // 463 { 4, 0, 0, -2, 0, 0}, // 464 { 6, 0, 0, 0, 0, 0}, // 465 { 5, 0, 0, -2, 0, 0}, // 466 { -7, 0, 0, 0, 0, 0}, // 467 { -12, 0, 0, 0, 0, 0}, // 468 { 5, 0, 0, -3, 0, 0}, // 469 { 3, 0, 0, -1, 0, 0}, // 470 { -5, 0, 0, 0, 0, 0}, // 471 { 3, 0, 0, 0, 0, 0}, // 472 { -7, 0, 0, 3, 0, 0}, // 473 { 7, 0, 0, -4, 0, 0}, // 474 { 0, 0, -12, 0, 0, -10}, // 475 { 4, 0, 0, -2, 0, 0}, // 476 { 3, 0, 0, -2, 0, 0}, // 477 { -3, 0, 0, 2, 0, 0}, // 478 { -7, 0, 0, 3, 0, 0}, // 479 { -4, 0, 0, 2, 0, 0}, // 480 { -3, 0, 0, 1, 0, 0}, // 481 { 0, 0, 0, 0, 0, 0}, // 482 { -3, 0, 0, 1, 0, 0}, // 483 { 7, 0, 0, -3, 0, 0}, // 484 { -4, 0, 0, 2, 0, 0}, // 485 { 4, 0, 0, -2, 0, 0}, // 486 { -5, 0, 0, 3, 0, 0}, // 487 { 5, 0, 0, 0, 0, 0}, // 488 { -5, 0, 0, 2, 0, 0}, // 489 { 5, 0, 0, -2, 0, 0}, // 490 { -8, 0, 0, 3, 0, 0}, // 491 { 9, 0, 0, 0, 0, 0}, // 492 { 6, 0, 0, -3, 0, 0}, // 493 { -5, 0, 0, 2, 0, 0}, // 494 { 3, 0, 0, 0, 0, 0}, // 495 { -7, 0, 0, 0, 0, 0}, // 496 { -3, 0, 0, 1, 0, 0}, // 497 { 5, 0, 0, 0, 0, 0}, // 498 { 3, 0, 0, 0, 0, 0}, // 499 { -3, 0, 0, 2, 0, 0}, // 500 { 4, 0, 0, -2, 0, 0}, // 501 { 3, 0, 0, -1, 0, 0}, // 502 { -5, 0, 0, 2, 0, 0}, // 503 { 4, 0, 0, -2, 0, 0}, // 504 { 9, 0, 0, -3, 0, 0}, // 505 { 4, 0, 0, 0, 0, 0}, // 506 { 4, 0, 0, -2, 0, 0}, // 507 { -3, 0, 0, 2, 0, 0}, // 508 { -4, 0, 0, 2, 0, 0}, // 509 { 9, 0, 0, -3, 0, 0}, // 510 { -4, 0, 0, 0, 0, 0}, // 511 { -4, 0, 0, 0, 0, 0}, // 512 { 3, 0, 0, -2, 0, 0}, // 513 { 8, 0, 0, 0, 0, 0}, // 514 { 3, 0, 0, 0, 0, 0}, // 515 { -3, 0, 0, 2, 0, 0}, // 516 { 3, 0, 0, -1, 0, 0}, // 517 { 3, 0, 0, -1, 0, 0}, // 518 { -3, 0, 0, 1, 0, 0}, // 519 { 6, 0, 0, -3, 0, 0}, // 520 { 3, 0, 0, 0, 0, 0}, // 521 { -3, 0, 0, 1, 0, 0}, // 522 { -7, 0, 0, 0, 0, 0}, // 523 { 9, 0, 0, 0, 0, 0}, // 524 { -3, 0, 0, 2, 0, 0}, // 525 { -3, 0, 0, 0, 0, 0}, // 526 { -4, 0, 0, 0, 0, 0}, // 527 { -5, 0, 0, 3, 0, 0}, // 528 { -13, 0, 0, 0, 0, 0}, // 529 { -7, 0, 0, 0, 0, 0}, // 530 { 10, 0, 0, 0, 0, 0}, // 531 { 3, 0, 0, -1, 0, 0}, // 532 { 10, 0, 13, 6, 0, -5}, // 533 { 0, 0, 30, 0, 0, 14}, // 534 { 0, 0, -162, 0, 0, -138}, // 535 { 0, 0, 75, 0, 0, 0}, // 536 { -7, 0, 0, 4, 0, 0}, // 537 { -4, 0, 0, 2, 0, 0}, // 538 { 4, 0, 0, -2, 0, 0}, // 539 { 5, 0, 0, -2, 0, 0}, // 540 { 5, 0, 0, -3, 0, 0}, // 541 { -3, 0, 0, 0, 0, 0}, // 542 { -3, 0, 0, 2, 0, 0}, // 543 { -4, 0, 0, 2, 0, 0}, // 544 { -5, 0, 0, 2, 0, 0}, // 545 { 6, 0, 0, 0, 0, 0}, // 546 { 9, 0, 0, 0, 0, 0}, // 547 { 5, 0, 0, 0, 0, 0}, // 548 { -7, 0, 0, 0, 0, 0}, // 549 { -3, 0, 0, 1, 0, 0}, // 550 { -4, 0, 0, 2, 0, 0}, // 551 { 7, 0, 0, 0, 0, 0}, // 552 { -4, 0, 0, 0, 0, 0}, // 553 { 4, 0, 0, 0, 0, 0}, // 554 { -6, 0, -3, 3, 0, 1}, // 555 { 0, 0, -3, 0, 0, -2}, // 556 { 11, 0, 0, 0, 0, 0}, // 557 { 3, 0, 0, -1, 0, 0}, // 558 { 11, 0, 0, 0, 0, 0}, // 559 { -3, 0, 0, 2, 0, 0}, // 560 { -1, 0, 3, 3, 0, -1}, // 561 { 4, 0, 0, -2, 0, 0}, // 562 { 0, 0, -13, 0, 0, -11}, // 563 { 3, 0, 6, 0, 0, 0}, // 564 { -7, 0, 0, 0, 0, 0}, // 565 { 5, 0, 0, -3, 0, 0}, // 566 { -3, 0, 0, 1, 0, 0}, // 567 { 3, 0, 0, 0, 0, 0}, // 568 { 5, 0, 0, -3, 0, 0}, // 569 { -7, 0, 0, 3, 0, 0}, // 570 { 8, 0, 0, -3, 0, 0}, // 571 { -4, 0, 0, 2, 0, 0}, // 572 { 11, 0, 0, 0, 0, 0}, // 573 { -3, 0, 0, 1, 0, 0}, // 574 { 3, 0, 0, -1, 0, 0}, // 575 { -4, 0, 0, 2, 0, 0}, // 576 { 8, 0, 0, -4, 0, 0}, // 577 { 3, 0, 0, -1, 0, 0}, // 578 { 11, 0, 0, 0, 0, 0}, // 579 { -6, 0, 0, 3, 0, 0}, // 580 { -4, 0, 0, 2, 0, 0}, // 581 { -8, 0, 0, 4, 0, 0}, // 582 { -7, 0, 0, 3, 0, 0}, // 583 { -4, 0, 0, 2, 0, 0}, // 584 { 3, 0, 0, -1, 0, 0}, // 585 { 6, 0, 0, -3, 0, 0}, // 586 { -6, 0, 0, 3, 0, 0}, // 587 { 6, 0, 0, 0, 0, 0}, // 588 { 6, 0, 0, -1, 0, 0}, // 589 { 5, 0, 0, -2, 0, 0}, // 590 { -5, 0, 0, 2, 0, 0}, // 591 { -4, 0, 0, 0, 0, 0}, // 592 { -4, 0, 0, 2, 0, 0}, // 593 { 4, 0, 0, 0, 0, 0}, // 594 { 6, 0, 0, -3, 0, 0}, // 595 { -4, 0, 0, 2, 0, 0}, // 596 { 0, 0, -26, 0, 0, -11}, // 597 { 0, 0, -10, 0, 0, -5}, // 598 { 5, 0, 0, -3, 0, 0}, // 599 { -13, 0, 0, 0, 0, 0}, // 600 { 3, 0, 0, -2, 0, 0}, // 601 { 4, 0, 0, -2, 0, 0}, // 602 { 7, 0, 0, -3, 0, 0}, // 603 { 4, 0, 0, 0, 0, 0}, // 604 { 5, 0, 0, 0, 0, 0}, // 605 { -3, 0, 0, 2, 0, 0}, // 606 { -6, 0, 0, 2, 0, 0}, // 607 { -5, 0, 0, 2, 0, 0}, // 608 { -7, 0, 0, 3, 0, 0}, // 609 { 5, 0, 0, -2, 0, 0}, // 610 { 13, 0, 0, 0, 0, 0}, // 611 { -4, 0, 0, 2, 0, 0}, // 612 { -3, 0, 0, 0, 0, 0}, // 613 { 5, 0, 0, -2, 0, 0}, // 614 { -11, 0, 0, 0, 0, 0}, // 615 { 5, 0, 0, -2, 0, 0}, // 616 { 4, 0, 0, 0, 0, 0}, // 617 { 4, 0, 0, -2, 0, 0}, // 618 { -4, 0, 0, 2, 0, 0}, // 619 { 6, 0, 0, -3, 0, 0}, // 620 { 3, 0, 0, -2, 0, 0}, // 621 { -12, 0, 0, 0, 0, 0}, // 622 { 4, 0, 0, 0, 0, 0}, // 623 { -3, 0, 0, 0, 0, 0}, // 624 { -4, 0, 0, 0, 0, 0}, // 625 { 3, 0, 0, 0, 0, 0}, // 626 { 3, 0, 0, -1, 0, 0}, // 627 { -3, 0, 0, 1, 0, 0}, // 628 { 0, 0, -5, 0, 0, -2}, // 629 { -7, 0, 0, 4, 0, 0}, // 630 { 6, 0, 0, -3, 0, 0}, // 631 { -3, 0, 0, 0, 0, 0}, // 632 { 5, 0, 0, -3, 0, 0}, // 633 { 3, 0, 0, -1, 0, 0}, // 634 { 3, 0, 0, 0, 0, 0}, // 635 { -3, 0, 0, 1, 0, 0}, // 636 { -5, 0, 0, 3, 0, 0}, // 637 { -3, 0, 0, 2, 0, 0}, // 638 { -3, 0, 0, 2, 0, 0}, // 639 { 12, 0, 0, 0, 0, 0}, // 640 { 3, 0, 0, -1, 0, 0}, // 641 { -4, 0, 0, 2, 0, 0}, // 642 { 4, 0, 0, 0, 0, 0}, // 643 { 6, 0, 0, 0, 0, 0}, // 644 { 5, 0, 0, -3, 0, 0}, // 645 { 4, 0, 0, -2, 0, 0}, // 646 { -6, 0, 0, 3, 0, 0}, // 647 { 4, 0, 0, -2, 0, 0}, // 648 { 6, 0, 0, -3, 0, 0}, // 649 { 6, 0, 0, 0, 0, 0}, // 650 { -6, 0, 0, 3, 0, 0}, // 651 { 3, 0, 0, -2, 0, 0}, // 652 { 7, 0, 0, -4, 0, 0}, // 653 { 4, 0, 0, -2, 0, 0}, // 654 { -5, 0, 0, 2, 0, 0}, // 655 { 5, 0, 0, 0, 0, 0}, // 656 { -6, 0, 0, 3, 0, 0}, // 657 { -6, 0, 0, 3, 0, 0}, // 658 { -4, 0, 0, 2, 0, 0}, // 659 { 10, 0, 0, 0, 0, 0}, // 660 { -4, 0, 0, 2, 0, 0}, // 661 { 7, 0, 0, 0, 0, 0}, // 662 { 7, 0, 0, -3, 0, 0}, // 663 { 4, 0, 0, 0, 0, 0}, // 664 { 11, 0, 0, 0, 0, 0}, // 665 { 5, 0, 0, -2, 0, 0}, // 666 { -6, 0, 0, 2, 0, 0}, // 667 { 4, 0, 0, -2, 0, 0}, // 668 { 3, 0, 0, -2, 0, 0}, // 669 { 5, 0, 0, -2, 0, 0}, // 670 { -4, 0, 0, 2, 0, 0}, // 671 { -4, 0, 0, 2, 0, 0}, // 672 { -3, 0, 0, 2, 0, 0}, // 673 { 4, 0, 0, -2, 0, 0}, // 674 { 3, 0, 0, -1, 0, 0}, // 675 { -3, 0, 0, 1, 0, 0}, // 676 { -3, 0, 0, 1, 0, 0}, // 677 { -3, 0, 0, 2, 0, 0} // 678 }; const Long MeasTableMulSC2000B::theirMULSC[77][6] = { // Longitude Obliquity // sin t.sin cos cos t.cos sin {-172064161, -174666, 33386, 92052331, 9086, 15377}, // 1 { -13170906, -1675, -13696, 5730336, -3015, -4587}, // 2 { -2276413, -234, 2796, 978459, -485, 1374}, // 3 { 2074554, 207, -698, -897492, 470, -291}, // 4 { 1475877, -3633, 11817, 73871, -184, -1924}, // 5 { -516821, 1226, -524, 224386, -677, -174}, // 6 { 711159, 73, -872, -6750, 0, 358}, // 7 { -387298, -367, 380, 200728, 18, 318}, // 8 { -301461, -36, 816, 129025, -63, 367}, // 9 { 215829, -494, 111, -95929, 299, 132}, // 10 { 128227, 137, 181, -68982, -9, 39}, // 11 { 123457, 11, 19, -53311, 32, -4}, // 12 { 156994, 10, -168, -1235, 0, 82}, // 13 { 63110, 63, 27, -33228, 0, -9}, // 14 { -57976, -63, -189, 31429, 0, -75}, // 15 { -59641, -11, 149, 25543, -11, 66}, // 16 { -51613, -42, 129, 26366, 0, 78}, // 17 { 45893, 50, 31, -24236, -10, 20}, // 18 { 63384, 11, -150, -1220, 0, 29}, // 19 { -38571, -1, 158, 16452, -11, 68}, // 20 { 32481, 0, 0, -13870, 0, 0}, // 21 { -47722, 0, -18, 477, 0, -25}, // 22 { -31046, -1, 131, 13238, -11, 59}, // 23 { 28593, 0, -1, -12338, 10, -3}, // 24 { 20441, 21, 10, -10758, 0, -3}, // 25 { 29243, 0, -74, -609, 0, 13}, // 26 { 25887, 0, -66, -550, 0, 11}, // 27 { -14053, -25, 79, 8551, -2, -45}, // 28 { 15164, 10, 11, -8001, 0, -1}, // 29 { -15794, 72, -16, 6850, -42, -5}, // 30 { 21783, 0, 13, -167, 0, 13}, // 31 { -12873, -10, -37, 6953, 0, -14}, // 32 { -12654, 11, 63, 6415, 0, 26}, // 33 { -10204, 0, 25, 5222, 0, 15}, // 34 { 16707, -85, -10, 168, -1, 10}, // 35 { -7691, 0, 44, 3268, 0, 19}, // 36 { -11024, 0, -14, 104, 0, 2}, // 37 { 7566, -21, -11, -3250, 0, -5}, // 38 { -6637, -11, 25, 3353, 0, 14}, // 39 { -7141, 21, 8, 3070, 0, 4}, // 40 { -6302, -11, 2, 3272, 0, 4}, // 41 { 5800, 10, 2, -3045, 0, -1}, // 42 { 6443, 0, -7, -2768, 0, -4}, // 43 { -5774, -11, -15, 3041, 0, -5}, // 44 { -5350, 0, 21, 2695, 0, 12}, // 45 { -4752, -11, -3, 2719, 0, -3}, // 46 { -4940, -11, -21, 2720, 0, -9}, // 47 { 7350, 0, -8, -51, 0, 4}, // 48 { 4065, 0, 6, -2206, 0, 1}, // 49 { 6579, 0, -24, -199, 0, 2}, // 50 { 3579, 0, 5, -1900, 0, 1}, // 51 { 4725, 0, -6, -41, 0, 3}, // 52 { -3075, 0, -2, 1313, 0, -1}, // 53 { -2904, 0, 15, 1233, 0, 7}, // 54 { 4348, 0, -10, -81, 0, 2}, // 55 { -2878, 0, 8, 1232, 0, 4}, // 56 { -4230, 0, 5, -20, 0, -2}, // 57 { -2819, 0, 7, 1207, 0, 3}, // 58 { -4056, 0, 5, 40, 0, -2}, // 59 { -2647, 0, 11, 1129, 0, 5}, // 60 { -2294, 0, -10, 1266, 0, -4}, // 61 { 2481, 0, -7, -1062, 0, -3}, // 62 { 2179, 0, -2, -1129, 0, -2}, // 63 { 3276, 0, 1, -9, 0, 0}, // 64 { -3389, 0, 5, 35, 0, -2}, // 65 { 3339, 0, -13, -107, 0, 1}, // 66 { -1987, 0, -6, 1073, 0, -2}, // 67 { -1981, 0, 0, 854, 0, 0}, // 68 { 4026, 0, -353, -553, 0, -139}, // 69 { 1660, 0, -5, -710, 0, -2}, // 70 { -1521, 0, 9, 647, 0, 4}, // 71 { 1314, 0, 0, -700, 0, 0}, // 72 { -1283, 0, 0, 672, 0, 0}, // 73 { -1331, 0, 8, 663, 0, 4}, // 74 { 1383, 0, -2, -594, 0, -2}, // 75 { 1405, 0, 4, -610, 0, 2}, // 76 { 1290, 0, 0, -556, 0, 0} // 77 }; const Long MeasTableMulAber::theirMABERTD[3][18] = { { -1719919, -2, 0, -25, 0, 0, 25, -13, -1, 1578094, 156, 0, 10, 32, 1, 684187, -358, 0}, { 6434, 141, 0, 28007, -107, -1, 25697, -95, -1, -5904, -130, 0, 11141, -48, 0, -2559, -55, 0}, { 486, -5, 0, -236, -4, 0, -216, -4, 0, -446, 5, 0, -94, -2, 0, -193, 2, 0} }; const Short MeasTableMulAber::theirMABER[80][6] = { { 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0}, { 31, 1, 1, -28, 0, -12}, { 8, -28, 25, 8, 11, 3}, { 8, -28, -25, -8, -11, -3}, { -25, 0, 0, 23, 0, 10}, { 21, 0, 0, -19, 0, -8}, { 16, 0, 0, 15, 1, 7}, { 11, -1, -1, -10, -1, -5}, { 0, -11, -10, 0, -4, 0}, { -11, -2, -2, 9, -1, 4}, { -7, -8, -8, 6, -3, 3}, { -10, 0, 0, 9, 0, 4}, { -9, 0, 0, -9, 0, -4}, { -9, 0, 0, -8, 0, -4}, { 0, -9, 8, 0, 3, 0}, { 8, 0, 0, -8, 0, -3}, { -4, -7, -6, 4, -3, 2}, { -4, -7, 6, -4, 3, -2}, { -6, -5, -4, 5, -2, 2}, // 21 { -1, -1, -2, -7, 1, -4}, { 4, -6, -5, -4, -2, -2}, { 0, -7, -6, 0, -3, 0}, { 5, -5, -4, -5, -2, -2}, { 4, -1, 1, 4, 0, 2}, { -4, 0, 0, 3, 0, 1}, { -1, -3, -3, 1, -1, 0}, { -1, -3, 3, -1, 1, 0}, { 3, 1, 0, 3, 0, 1}, { 3, -1, -1, 1, 0, 1}, { -2, 0, 0, -3, 0, -1}, { 1, -2, 2, 1, 1, 1}, { -2, -1, 0, 2, 0, 1}, { 1, -2, -2, -1, -1, 0}, { 2, 0, 0, -2, 0, -1}, { 2, -1, -1, -2, 0, -1}, { 2, 0, 0, -2, 0, -1}, { 2, -1, -1, -1, 0, -1}, { 0, -2, -1, 0, -1, 0}, { 0, -1, -1, 0, -1, 0}, // 41 { -1, -1, -1, 1, -1, 0}, { 1, 0, 0, -1, 0, -1}, { 0, -1, -1, 0, -1, 0}, { -2, 0, 0, -1, 0, 0}, { 1, -1, 1, 1, 0, 0}, { -1, 1, 1, 1, 0, 0}, { -1, 1, -1, -1, 0, 0}, { 1, -1, -1, 0, 0, 0}, { 0, 1, 1, 0, 0, 0}, { 0, -1, 1, 0, 0, 0}, { -1, 0, 0, -1, 0, 0}, { 1, 0, 0, -1, 0, 0}, { 1, 0, 0, 1, 0, 0}, { -1, 0, 0, 1, 0, 0}, { 1, 0, 0, 1, 0, 0}, { -1, 0, 0, 1, 0, 0}, { 1, 0, 0, -1, 0, 0}, { -1, 0, 0, 1, 0, 0}, { 1, 0, 0, 1, 0, 0}, { -1, 0, 0, 1, 0, 0}, // 61 { -1, 0, 0, -1, 0, 0}, { 0, -1, -1, 0, 0, 0}, { 0, 1, 1, 0, 0, 0}, { 0, 1, -1, 0, 0, 0}, { 0, 1, -1, 0, 0, 0}, { 0, 1, 1, 0, 0, 0}, { 0, -1, 1, 0, 0, 0}, { 0, 1, -1, 0, 0, 0}, { 1, 0, 0, 0, 0, 0}, { 0, 1, 0, 0, 0, 0}, { 1, 0, 0, 0, 0, 0}, { 0, -1, 0, 0, 0, 0}, { 0, 1, 0, 0, 0, 0}, { -1, 0, 0, 0, 0, 0}, { 0, -1, 0, 0, 0, 0}, { 0, 0, 1, 0, 0, 0}, { 0, 0, 1, 0, 0, 0}, { 0, 0, 0, 1, 0, 0}, { 0, 0, 0, -1, 0, 0} }; // Wim Brouw's old table const Short MeasTableMulAber1950::theirMABER[130][6] = { // Order: sin(x), cos(x), sin(y), cos(y), sin(z), cos(z) { 1, 0, 0, -157, 0, 358}, { 715, 0, 0, -656, 0, -285}, { 543, 0, 0, -498, 0, -216}, { -72, 0, 0, 63, 0, 35}, { -60, 0, 0, 55, 0, 24}, { 38, 0, 0, -35, 0, -15}, { 0, -31, 28, 0, 12, 0}, { 0, 0, 0, 26, 0, -59}, { -26, 0, 0, -24, 0, -10}, { -22, 0, 0, -20, 0, -9}, { 22, 0, 0, -20, 0, -9}, // 10 { -22, 0, 0, 20, 0, 9}, { 0, -18, 17, 0, 7, 0}, { 16, 0, 0, 15, 0, 6}, { 0, 16, 14, 0, 6, 0}, { 0, 16, 14, 0, 6, 0}, { 0, 12, -1, 0, -5, 0}, { -12, 0, 0, 11, 0, 5}, { 11, 0, 0, 10, 0, 4}, { 11, 0, 0, -10, 0, -4}, { -11, 0, 0, -10, 0, -4}, // 20 { -10, 0, 0, -9, 0, -4}, { -10, 0, 0, 9, 0, 4}, { 0, 0, 8, -8, 0, -3}, { 0, 0, 8, -8, 0, -3}, { -8, 0, 0, 7, 0, 3}, { -8, 0, 0, -7, 0, -3}, { 0, 8, 7, 0, 3, 0}, { 0, -7, -6, 0, -3, 0}, { 0, -7, -6, 0, -3, 0}, { 0, 7, 6, 0, 3, 0}, // 30 { 7, 0, 0, 6, 0, 3}, { 0, 6, -6, 0, -3, 0}, { -6, 0, 6, 0, 3, 0}, { 6, 0, 0, -5, 0, -2}, { -6, 0, 0, 5, 0, 2}, { 0, 5, 5, 0, 2, 0}, { 0, 5, 5, 0, 2, 0}, { -5, 0, 0, -5, 0, -2}, { -5, 0, 0, 4, 0, 2}, { 0, 5, 4, 0, 2, 0}, // 40 { 0, 0, 0, 0, 0, -2}, { 0, 4, 4, 0, 2, 0}, { 0, -4, -3, 0, -1, 0}, { 0, -4, -3, 0, -1, 0}, { 0, 3, 3, 0, 1, 0}, { 0, 3, -3, 0, -1, 0}, { 0, 3, 3, 0, 1, 0}, { 0, 3, 3, 0, 1, 0}, { 0, 0, 0, 0, -1, 0}, { -3, 0, 0, 3, 0, 1}, // 50 { 3, 0, 0, -3, 0, -1}, { 0, -3, -3, 0, -1, 0}, { -3, 0, 0, 3, 0, 1}, { -3, 0, 0, 2, 0, 1}, { 0, -3, 2, 0, 1, 0}, { -3, 0, 0, 2, 0, 1}, { 3, 0, 0, -2, 0, -1}, { -3, 0, 0, 2, 0, 1}, { -2, 0, 0, -2, 0, 0}, { 0, 0, 0, 1, 0, -3}, // 60 { 0, 0, 0, 0, 0, 1}, { 0, 2, -2, 0, 0, 0}, { 0, 2, 2, 0, 0, 0}, { 0, 2, 2, 0, 0, 0}, { -2, 0, 0, 2, 0, 0}, { 2, 0, 0, 2, 0, 0}, { 0, -2, 2, 0, 0, 0}, { 0, 2, 2, 0, 0, 0}, { 2, 0, 0, -2, 0, 0}, { 0, -2, -2, 0, 0, 0}, // 70 { 0, -2, -2, 0, 0, 0}, { 0, -2, 2, 0, 0, 0}, { 2, 0, 0, -2, 0, 0}, { 2, 0, 0, -2, 0, 0}, { -2, 0, 0, -2, 0, 0}, { 2, 0, 0, 1, 0, 0}, { 0, 1, 1, 0, 0, 0}, { 1, 0, 0, -1, 0, 0}, { -1, 0, 0, 1, 0, 0}, { 1, 0, 0, -1, 0, 0}, // 80 { 0, 1, 1, 0, 0, 0}, { 1, 0, 0, -1, 0, 0}, { -1, 0, 0, 1, 0, 0}, { 0, -1, -1, 0, 0, 0}, { 0, -1, -1, 0, 0, 0}, { -1, 0, 0, 0, 0, 0}, { 1, 0, 0, 0, 0, 0}, { 0, 1, 0, 0, 0, 0}, { 0, 1, 0, 0, 0, 0}, { 0, 1, 0, 0, 0, 0}, // 90 { 0, -1, 0, 0, 0, 0}, { -1, 0, 0, 0, 0, 0}, { -1, 0, 0, 0, 0, 0}, { -1, 0, 0, 0, 0, 0}, { 1, 0, 0, 0, 0, 0}, { 0, 1, 0, 0, 0, 0}, { 1, 0, 0, 0, 0, 0}, { -1, 0, 0, 0, 0, 0}, { -1, 0, 0, 0, 0, 0}, { 1, 0, 0, 0, 0, 0}, // 100 { -1, 0, 0, 0, 0, 0}, { 1, 0, 0, 0, 0, 0}, { 0, 1, 0, 0, 0, 0}, { 0, -1, 0, 0, 0, 0}, { -1, 0, 0, 0, 0, 0}, { 1, 0, 0, 0, 0, 0}, { 0, -1, 0, 0, 0, 0}, { 0, 1, 0, 0, 0, 0}, // { 701, 0, 0, -642, 0, -280}, { 0, 158, 152, 0, 48, 0}, // 110 { 0, 159, 147, 0, 61, 0}, { 34, 0, 0, -31, 0, -14}, { 0, 20, 18, 0, 8, 0}, { -17, 0, 0, 16, 0, 7}, { 0, 12, 11, 0, 4, 0}, { 11, 0, 0, -10, 0, -4}, { 0, 9, 8, 0, 3, 0}, { 0, 8, 7, 2, 0, 0}, { -5, 0, 0, 5, 0, 2}, { -5, 0, 0, 4, 0, 2}, // 120 { 0, 4, 3, 2, 0, 0}, { -3, 0, 0, 3, 0, 1}, { 0, 3, 2, 0, 1, 0}, { -3, 0, 0, 5, 0, -5}, { 2, 0, 0, -2, 0, -1}, { -1, 0, 0, 0, 0, 1}, { 0, 1, 1, 0, 0, 0}, { 0, 1, 1, 0, 0, 0}, { 0, -1, 0, 0, 0, 0}, }; /* // new Rob Reid's table (some slight differences) const Short MeasTableMulAber::theirMABER[130][6] = { // Order: // Delta xdot Delta ydot Delta zdot // sin, cos, sin, cos, sin, cos { 1, 0, 0, -157, 0, 358}, // T { 715, 0, 0, -656, 0, -285}, { 543, 0, 0, -498, 0, -216}, { -72, 0, 0, 63, 0, 35}, // T { -60, 0, 0, 55, 0, 24}, { 38, 0, 0, -35, 0, -15}, { 0, -31, 28, 0, 12, 0}, { 0, 0, 0, 26, 0, -59}, { -26, 0, 0, -24, 0, -10}, { -22, 0, 0, -20, 0, -9}, { 22, 0, 0, -20, 0, -9}, // 10 { -22, 0, 0, 20, 0, 9}, { 0, -18, 17, 0, 7, 0}, { 16, 0, 0, 15, 0, 6}, { 0, 16, 14, 0, 6, 0}, { 0, 16, 14, 0, 6, 0}, { 0, 12, -11, 0, -5, 0}, { -12, 0, 0, 11, 0, 5}, { 11, 0, 0, 10, 0, 4}, { 11, 0, 0, -10, 0, -4}, { -11, 0, 0, -10, 0, -4}, // 20 { -10, 0, 0, -9, 0, -4}, { -10, 0, 0, 9, 0, 4}, { 0, 8, -8, 0, -3, 0}, { 0, 8, -8, 0, -3, 0}, { -8, 0, 0, 7, 0, 3}, { -8, 0, 0, -7, 0, -3}, { 0, 8, 7, 0, 3, 0}, { 0, -7, -6, 0, -3, 0}, { 0, -7, -6, 0, -3, 0}, { 0, 7, 6, 0, 3, 0}, // 30 { 7, 0, 0, 6, 0, 3}, { 0, 6, -6, 0, -3, 0}, { -6, 0, 0, 6, 0, 3}, { 6, 0, 0, -5, 0, -2}, { -6, 0, 0, 5, 0, 2}, { 0, 5, 5, 0, 2, 0}, { 0, 5, 5, 0, 2, 0}, { -5, 0, 0, -5, 0, -2}, { -5, 0, 0, 4, 0, 2}, { 0, 5, 4, 0, 2, 0}, // 40 { 0, 0, 0, 0, 0, -2}, { 0, 4, 4, 0, 2, 0}, { 0, -4, -3, 0, -1, 0}, { 0, -4, -3, 0, -1, 0}, // T**2 { 0, 3, 3, 0, 1, 0}, { 0, 3, -3, 0, -1, 0}, { 0, 3, 3, 0, 1, 0}, { 0, 3, 3, 0, 1, 0}, { 0, 0, 0, 0, -1, 0}, { -3, 0, 0, 3, 0, 1}, // 50 { 3, 0, 0, -3, 0, -1}, { 0, -3, -3, 0, -1, 0}, { -3, 0, 0, 3, 0, 1}, { -3, 0, 0, 2, 0, 1}, // T { 0, 3, 2, 0, 1, 0}, // T**3 { -3, 0, 0, 2, 0, 1}, { 3, 0, 0, -2, 0, -1}, { -3, 0, 0, 2, 0, 1}, { -2, 0, 0, -2, 0, 0}, { 0, 0, 0, 1, 0, -3}, // 60 { 0, 0, 0, 0, 0, 1}, { 0, 2, -2, 0, 0, 0}, { 0, 2, 2, 0, 0, 0}, { 0, 2, 2, 0, 0, 0}, { -2, 0, 0, 2, 0, 0}, { 2, 0, 0, 2, 0, 0}, { 0, -2, 2, 0, 0, 0}, { 0, 2, 2, 0, 0, 0}, { 2, 0, 0, -2, 0, 0}, { 0, -2, -2, 0, 0, 0}, // 70 { 0, -2, -2, 0, 0, 0}, { 0, -2, 2, 0, 0, 0}, { 2, 0, 0, -2, 0, 0}, { 2, 0, 0, -2, 0, 0}, { -2, 0, 0, -2, 0, 0}, { 2, 0, 0, 1, 0, 0}, { 0, 1, 1, 0, 0, 0}, { 1, 0, 0, -1, 0, 0}, { -1, 0, 0, 1, 0, 0}, { 1, 0, 0, -1, 0, 0}, // 80 { 0, 1, 1, 0, 0, 0}, { 1, 0, 0, -1, 0, 0}, { -1, 0, 0, 1, 0, 0}, { 0, -1, -1, 0, 0, 0}, { 0, -1, -1, 0, 0, 0}, { -1, 0, 0, 0, 0, 0}, { 1, 0, 0, 0, 0, 0}, { 0, 1, 0, 0, 0, 0}, { 0, 1, 0, 0, 0, 0}, { 0, 1, 0, 0, 0, 0}, // 90 { 0, -1, 0, 0, 0, 0}, { -1, 0, 0, 0, 0, 0}, { -1, 0, 0, 0, 0, 0}, { -1, 0, 0, 0, 0, 0}, { 1, 0, 0, 0, 0, 0}, { 0, 1, 0, 0, 0, 0}, { 1, 0, 0, 0, 0, 0}, { -1, 0, 0, 0, 0, 0}, { -1, 0, 0, 0, 0, 0}, { 1, 0, 0, 0, 0, 0}, // 100 { -1, 0, 0, 0, 0, 0}, { 1, 0, 0, 0, 0, 0}, { 0, 1, 0, 0, 0, 0}, { 0, -1, 0, 0, 0, 0}, { -1, 0, 0, 0, 0, 0}, { 1, 0, 0, 0, 0, 0}, { 0, -1, 0, 0, 0, 0}, { 0, 1, 0, 0, 0, 0}, // { 701, 0, 0, -642, 0, -280}, { 0, 158, 152, 0, 48, 0}, // 110 { 0, 159, 147, 0, 61, 0}, { 34, 0, 0, -31, 0, -14}, { 0, 20, 18, 0, 8, 0}, // T { -17, 0, 0, 16, 0, 7}, { 0, 12, 11, 0, 0, 4}, { 11, 0, 0, -10, 0, -4}, { 0, 9, 8, 0, 3, 0}, { 0, 8, 7, 0, 2, 0}, { -5, 0, 0, 5, 0, 2}, // T { -5, 0, 0, 4, 0, 2}, // 120, T { 0, 4, 3, 0, 2, 0}, { -3, 0, 0, 3, 0, 1}, { 0, 3, 2, 0, 1, 0}, { -3, 0, 0, 5, 0, -5}, { 2, 0, 0, -2, 0, -1}, { -1, 0, 0, 0, 0, 1}, { 0, 1, 1, 0, 0, 0}, { 0, 1, 1, 0, 0, 0}, // T { 0, -1, 0, 0, 0, 0}, // T }; */ const Short MeasTableMulAber1950::theirABERT1T[10] = { // Includes ABERT2T and ABERT3T, which will end up as T**2 and // T**3 respectively. 0,3,44,54,55,113,119,120,128,129 }; const Short MeasTableMulAber1950::theirABERT2T[2] = { 44, 55 }; const Short MeasTableMulAber1950::theirABERT3T[1] = { 55 }; const Double MeasTableMulAber1950::theirABERSPEC[2][6] = { {1719971.0, 0, 0, -1577888.0, 0, -684523.0}, {28809.0, 0, 0, -26429.0, 0, -11466.0} }; const Double MeasTableMulPosSunXY::theirMPOSXY[98][4] = { // XY, Sun(eclip) { 89.996243, 49567508, 0.007122, 49553856}, { 90.023036, 27184119, 359.978260, 27222470}, { 90.013001, 15543566, 359.986984, 15544429}, { 89.998760, 8346116, 359.994071, 8342410}, {270.00000, 2938943, 270.00000, 3386226}, { 75.67877, 1201314, 345.68424, 1201189}, {355.57060, 757834, 265.52664, 758691}, { 99.6017, 194166, 8.6359, 196403}, {276.7769, 193296, 186.7746, 193244}, {278.7363, 188909, 8.7801, 189177}, {279.0072, 143422, 189.2057, 143684}, // 11 { 98.7637, 140637, 188.7514, 140598}, { 65.8075, 118366, 335.7846, 118334}, { 86.7821, 81367, 356.7759, 81306}, {266.7770, 76708, 356.8019, 76713}, {266.5938, 62227, 176.6625, 62623}, { 61.365, 43663, 331.368, 43663}, {156.244, 37923, 247.016, 38293}, {344.302, 31543, 251.387, 31756}, {262.697, 30883, 172.654, 30923}, { 90.000, 30399, 0.000, 30401}, // 21 {103.163, 27884, 193.183, 28740}, {278.234, 22716, 352.523, 26991}, {258.215, 21619, 167.571, 21346}, { 89.978, 17674, 0.022, 17702}, { 89.962, 13472, 179.980, 13750}, { 80.376, 11698, 350.371, 11275}, { 88.031, 10909, 358.047, 10900}, { 12.883, 10542, 102.926, 10555}, {196.373, 9798, 106.615, 9800}, { 66.707, 6958, 336.709, 6959}, // 31 {267.217, 6612, 177.247, 6607}, {268.524, 6295, 178.506, 6287}, { 89.718, 6288, 359.697, 6285}, { 32.99, 5688, 302.71, 5664}, { 90.03, 4897, 359.97, 4891}, { 97.86, 3784, 8.61, 3476}, {200.32, 3055, 290.29, 3024}, {206.86, 3104, 79.86, 2759}, { 90.39, 2829, 359.63, 2926}, {280.03, 2862, 190.03, 2862}, // 41 {280.50, 2858, 10.52, 2858}, {107.89, 2378, 197.94, 2382}, { 9.32, 2317, 99.33, 2316}, {280.92, 2264, 190.71, 2336}, {273.69, 2236, 3.69, 2218}, { 86.85, 2188, 176.81, 2187}, { 98.81, 2003, 188.93, 2005}, {279.68, 1968, 189.63, 1969}, { 11.17, 1908, 279.02, 1855}, { 47.07, 1881, 317.07, 1881}, // 51 {260.37, 1832, 350.33, 1832}, {346.50, 1729, 257.13, 1742}, {174.63, 1637, 84.58, 1638}, {153.30, 1658, 68.45, 1555}, {127.97, 1574, 38.01, 1569}, {108.53, 1549, 18.95, 1555}, {278.95, 1159, 8.95, 1159}, { 98.80, 1144, 8.74, 1143}, {290.91, 1123, 197.58, 1160}, {288.14, 1083, 18.12, 1083}, // 61 { 74.74, 832, 344.74, 832}, { 62.37, 792, 332.33, 791}, {346.10, 749, 77.01, 757}, {176.20, 738, 86.29, 738}, {204.53, 723, 115.05, 724}, {309.70, 718, 39.74, 720}, {122.93, 712, 32.92, 715}, {130.84, 701, 40.48, 703}, {299.69, 695, 209.57, 696}, {315.60, 687, 45.65, 690}, // 71 {101.30, 640, 185.30, 704}, {105.72, 660, 195.76, 661}, {281.71, 622, 11.40, 630}, { 89.65, 622, 0.34, 629}, {271.69, 609, 181.66, 608}, {101.35, 585, 11.31, 586}, { 88.9, 516, 358.9, 516}, {310.2, 507, 220.2, 508}, {130.4, 498, 220.4, 497}, {351.2, 472, 261.2, 472}, // 81 {274.9, 458, 184.9, 455}, {251.9, 435, 161.9, 436}, {165.0, 428, 75.5, 442}, { 90.000, 12965, 270.000, 63}, // 85: (T terms) {234.527, 8975, 144.488, 8989}, {196.650, 7770, 106.330, 7815}, { 16.208, 7537, 106.256, 7550}, { 27.402, 6060, 297.410, 6056}, { 16.47, 5726, 286.565, 5733}, {196.27, 5615, 286.26, 5613}, // 91 {252.66, 1011, 344.13, 1029}, { 86.80, 875, 356.72, 873}, {140.69, 726, 50.67, 727}, {115.80, 537, 205.8, 538}, {103.44, 574, 343.3, 473}, { 13.1, 441, 283.1, 440}, {291.2, 321, 165.5, 446} }; const Double MeasTableMulPosSunZ::theirMPOSZ[29][2] = { // Z Sun(eclip) {246.32367, 1181234}, {259.53511, 1127775}, {228.2177, 480205}, { 90.0000, 114995}, {285.8981, 112657}, {152.407, 32986}, {245.217, 27333}, {254.184, 9425}, {122.335, 8186}, { 83.42, 4079}, {288.63, 3169}, // 11 {112.58, 2595}, {224.87, 2453}, {127.99, 2329}, { 3.20, 2180}, {202.72, 1973}, {295.15, 1452}, { 59.99, 1358}, {146.90, 1050}, { 55.63, 1050}, {283.32, 1047}, // 21 {230.88, 993}, {249.34, 872}, {106.62, 800}, {114.3, 544}, {216.2, 461}, {323.28, 5444}, // 27: (T terms) {143.14, 3882}, {270.00, 1334} }; const Double MeasTableMulPosEarthXY::theirMPOSXY[189][4] = { // X,Y Heliocentric Earth, ecliptic { 90.00087234, .9998292882e10, 359.99912749, .9998921102e10}, { 90.00000000, 56114420, 270.0000000, 244269903}, {347.0626587, 83525730, 257.0614968, 83529232}, {244.12566, 1046663, 154.12489, 1046697}, { 90.0000, 311084, 0.0000, 311084}, { 89.0578, 255249, 359.3759, 257033}, { 90.0271, 213728, 179.9916, 214746}, {196.7828, 168118, 106.7816, 168129}, { 16.7819, 167995, 106.7836, 168006}, {269.9806, 144524, 0.0522, 144026}, {269.6489, 109101, 0.3652, 113511}, // 11 {271.4276, 93443, 181.4222, 93454}, { 89.9756, 89914, 0.0162, 90056}, { 90.8784, 73446, 179.5547, 74492}, { 0.8110, 68144, 90.8196, 68133}, { 98.7707, 68441, 9.3357, 63930}, {263.7081, 61124, 173.6982, 61135}, {144.633, 56652, 54.610, 56711}, {270.242, 54701, 180.200, 54635}, { 1.314, 54095, 91.322, 54125}, {180.963, 52049, 91.427, 50708}, // 21 {327.392, 45226, 57.394, 45229}, {228.731, 45184, 139.361, 45042}, {147.393, 44981, 57.415, 45025}, {130.279, 40626, 40.291, 40632}, {151.268, 7729, 172.890, 55138}, {269.997, 25618, 180.003, 25613}, {269.273, 25582, 179.260, 25583}, { 75.848, 22789, 165.848, 22791}, {280.086, 22588, 10.214, 22779}, {215.722, 21496, 126.916, 21950}, // 31 {180.759, 20902, 90.897, 20623}, { 90.619, 19997, 0.625, 20002}, {162.681, 18226, 72.680, 18227}, {342.679, 18226, 72.681, 18227}, { 61.086, 17811, 150.582, 17925}, {214.749, 16119, 118.365, 14976}, {141.189, 15545, 51.188, 15545}, { 89.725, 15170, 359.704, 15278}, { 32.362, 12894, 122.345, 12898}, { 60.693, 12810, 150.613, 12823}, // 41 { 95.556, 6774, 176.881, 11874}, { 90.000, 8587, 0.000, 8587}, { 89.374, 8296, 359.942, 8349}, {182.488, 7920, 92.452, 8073}, {255.788, 9449, 23.725, 5809}, {240.578, 7780, 151.084, 7841}, {177.781, 7560, 87.769, 7562}, {141.375, 7348, 51.367, 7352}, {239.074, 6535, 149.568, 6569}, {237.645, 6324, 327.648, 6324}, // 51 { 57.641, 6298, 327.645, 6297}, {156.246, 6213, 243.587, 6363}, { 31.449, 6004, 121.422, 6008}, {182.297, 5734, 271.031, 6113}, { 31.69, 5371, 121.63, 5375}, {270.56, 5131, 180.56, 5130}, {243.06, 5109, 153.05, 5110}, {269.99, 5065, 180.01, 5063}, {117.64, 5049, 27.28, 5064}, {246.74, 4962, 336.74, 4962}, // 61 {270.52, 4896, 180.48, 4893}, {270.98, 4727, 358.98, 4934}, {257.23, 4811, 347.17, 4842}, { 67.00, 4808, 336.61, 4791}, { 75.42, 4735, 165.45, 4734}, {270.82, 4757, 359.00, 3972}, {100.50, 4339, 9.99, 4214}, { 86.90, 5232, 186.38, 2571}, {161.82, 3954, 251.82, 3955}, {215.93, 4114, 116.98, 3753}, // 71 {164.09, 3799, 74.07, 3795}, {327.42, 2048, 258.18, 4839}, {271.81, 3688, 181.44, 3638}, {341.29, 3612, 251.28, 3612}, {161.29, 3611, 251.28, 3612}, {290.01, 3255, 20.06, 3260}, {238.87, 3256, 332.92, 3114}, { 2.39, 3034, 92.35, 3035}, { 54.87, 3248, 350.61, 2674}, {193.66, 3255, 78.16, 2484}, // 81 {119.77, 2807, 209.76, 2807}, { 95.54, 2772, 5.54, 2773}, {269.94, 2611, 180.06, 2600}, { 65.10, 2493, 155.06, 2504}, {177.63, 2325, 87.61, 2323}, {336.33, 2164, 246.33, 2164}, {188.68, 2286, 85.72, 2001}, {213.94, 2187, 117.66, 2051}, { 3.91, 2106, 93.92, 2107}, {273.05, 2169, 19.52, 1751}, // 91 { 90.00, 1888, 0.00, 1888}, { 89.73, 1922, 180.34, 1800}, {210.03, 1857, 303.56, 1786}, { 2.52, 1791, 92.58, 1790}, {333.37, 1711, 63.34, 1711}, {346.35, 1505, 256.42, 1508}, {168.39, 1438, 78.35, 1436}, {266.20, 1433, 176.17, 1433}, { 59.17, 1428, 330.85, 1410}, {306.36, 1391, 36.18, 1383}, // 101 {152.50, 1361, 62.47, 1360}, {195.97, 1325, 105.86, 1337}, {159.31, 1384, 57.19, 1221}, { 95.48, 1242, 5.63, 1244}, { 9.14, 1192, 99.16, 1192}, {189.20, 1192, 99.18, 1192}, { 3.28, 1113, 272.48, 1256}, {304.43, 1160, 34.43, 1161}, {275.54, 1160, 5.55, 1160}, {102.72, 1099, 192.84, 1098}, // 111 {268.97, 1051, 358.97, 1050}, {181.30, 1053, 274.16, 1021}, { 97.55, 1050, 3.85, 1012}, { 88.97, 985, 358.97, 985}, {207.30, 259, 8.96, 1355}, { 89.87, 980, 180.15, 954}, {128.65, 994, 30.32, 912}, {305.27, 905, 215.06, 902}, { 4.49, 915, 91.47, 860}, {241.17, 886, 151.21, 887}, // 121 { 61.20, 861, 151.20, 861}, { 66.00, 853, 333.65, 830}, {133.29, 790, 43.29, 790}, {270.16, 780, 178.88, 747}, {189.53, 823, 83.47, 698}, { 70.27, 755, 160.31, 756}, { 90.04, 753, 0.18, 753}, {175.69, 906, 24.30, 534}, { 38.25, 746, 307.34, 738}, { 79.50, 743, 349.79, 741}, // 131 {100.84, 726, 190.47, 732}, { 23.06, 720, 113.06, 720}, {203.09, 715, 113.08, 714}, {217.94, 701, 307.96, 702}, {278.31, 693, 186.68, 692}, { 94.95, 696, 186.28, 686}, {269.39, 655, 0.69, 698}, {193.70, 403, 266.57, 855}, {300.05, 665, 30.03, 666}, {344.58, 641, 254.72, 639}, // 141 {333.46, 637, 63.56, 636}, {276.48, 623, 7.36, 649}, {152.71, 637, 144.59, 625}, { 87.41, 640, 4.96, 620}, { 89.57, 626, 0.36, 633}, {167.54, 622, 77.49, 623}, { 99.41, 621, 189.40, 621}, {279.42, 620, 189.41, 621}, { 90.00, 620, 0.00, 620}, { 77.23, 634, 174.53, 605}, // 151 { 90.00, 616, 0.00, 616}, { 52.74, 614, 322.76, 615}, { 73.20, 581, 343.28, 582}, {229.31, 575, 318.52, 584}, {101.4, 435, 41.26, 686}, {268.9, 571, 178.9, 571}, {162.3, 572, 252.7, 559}, { 95.8, 542, 5.8, 542}, {265.2, 540, 174.8, 540}, { 89.9, 535, 180.1, 528}, // 161 {194.0, 499, 101.9, 539}, { 29.8, 474, 310.0, 538}, {167.1, 486, 77.1, 486}, { 4.2, 480, 275.8, 484}, {123.8, 464, 215.4, 456}, {354.9, 437, 264.9, 437}, {335.8, 425, 65.8, 425}, {263.4, 307, 345.9, 501}, { 78.2, 412, 348.2, 412}, {299.7, 406, 29.8, 407}, // 171 {178.9, 402, 268.9, 402}, { 56.2, 402, 326.2, 402}, {303.9, 399, 213.8, 400}, { 90.00000, 1234019, 90.00000, 930472}, // 175: (T terms) {232.9938, 515000, 142.9903, 515065}, {130.051, 12907, 40.049, 12908}, {105.014, 10686, 323.41, 4646}, {271.93, 1999, 181.93, 1999}, { 91.93, 1997, 181.93, 1997}, {107.16, 620, 197.23, 620}, // 181 {182.69, 599, 272.68, 599}, { 2.66, 596, 272.70, 596}, {107.6, 486, 197.8, 488}, {288.1, 461, 197.2, 464}, { 46.3, 427, 316.4, 426}, {270.00, 4147, 90.00, 5032}, // 187: (T^2 terms) {140.87, 2164, 50.89, 2166}, { 1.41, 996, 255.23, 1021} }; const Double MeasTableMulPosEarthZ::theirMPOSZ[32][2] = { //Z factors(ecliptic, helio Earth) {180.000, 27962}, {256.611, 10164}, {280.555, 8046}, {256.72, 4386}, {256.62, 3187}, { 0.00, 2272}, {275.12, 1816}, {293.93, 1640}, { 76.55, 1447}, {103.42, 1431}, {103.26, 1121}, // 11 { 74.37, 1090}, {180.00, 1036}, {291.79, 972}, {180.19, 914}, {278.89, 880}, {169.36, 834}, {180.00, 770}, {263.31, 720}, { 59.06, 692}, {180.5, 526}, // 21 {103.2, 520}, {344.8, 503}, {280.6, 475}, {333.7, 453}, {162.3, 429}, {353.9, 406}, { 76.2, 402}, {185.12558, 2278227}, // 29: (T terms) { 90.000, 54293}, { 82.189, 19032}, // 31 {284.741, 9722} // 32: (T^2 terms) }; } //# end namespace casacore-3.7.1/measures/Measures/MeasTableMul.h000066400000000000000000000212511476623553700214210ustar00rootroot00000000000000//# MeasTableMul.h: Nutation multiplication coefficient for MeasTable //# Copyright (C) 1995-1999,2000-2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MEASTABLEMUL_H #define MEASURES_MEASTABLEMUL_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class RotMatrix; class Euler; // // MeasTableMul provides thread-safe access to time-dependent multiplier matrices // // // // // // MeasTableMul is a helper class for MeasTable to provide thread-safe // access to the various multiplier matrices for nutation, aberration, and // solar position. These matrices are dependent on the epoch. // // It is an abstract base class for specific derived classes dealing with // the various effects. This base class provides a cache to keep the matrices // for various epochs alive. The idea is that a program will process epochs // in order, where multiple threads can handle different epochs. //
        When the cache is full, the least recently used entry is replaced by // the new matrix. // // The cache does not hold Matrix objects themselves, but a // std::shared_ptr to avoid that in one thread a Matrix is // removed from the cache, while another thread is still using that Matrix. // This assumes that std::shared_ptr is compiled thread-safe. // // The class provides two virtual functions. //
          //
        • init is called on the first access and makes it possible // for the derived class to precompute some variables. In particular, // itsDefMatrix should be filled with default values. //
        • calc is called on each access and should return the // matrix valid for the given epoch. Prior to calling this function, // the class will copy itsDefMatrix to the result which // also defines the shape of the result. // Note that this function is only called if the matrix for the given // epoch is not in the cache. //
        //
        // // // Class MeasTable shows how it is used. // class MeasTableMul { public: MeasTableMul(); virtual ~MeasTableMul() {} void clear(); std::shared_ptr> getArray (Double time, Double epsilon); virtual void init() = 0; virtual void calc(Matrix&, Double time) = 0; protected: std::mutex itsMutex; Int64 itsLastUsed; std::vector itsUsed; std::vector itsTimes; std::vector>> itsArrays; Matrix itsDefArray; }; // // Base class for standard and B1950 nutation multipliers. // class MeasTableMulSCBase: public MeasTableMul { public: MeasTableMulSCBase(); protected: void doInit(Matrix& result, Polynomial poly[], Int nrowTD, const Long coeffTD[][5], Int nrowSC, const Short coeffSC[][2]); void doCalc(Matrix& result, Double time, const Polynomial poly[], Int nrowTD, const Long coeffTD[][5]); }; // // Class calculating the standard nutation multipliers. // class MeasTableMulSC: public MeasTableMulSCBase { public: MeasTableMulSC(); virtual void init(); virtual void calc(Matrix&, Double time); private: Polynomial itsPoly[2*15]; static const Long theirMULTD[15][5]; static const Short theirMULSC[106][2]; }; // // Class calculating the B1950 nutation multipliers. // class MeasTableMulSC1950: public MeasTableMulSCBase { public: MeasTableMulSC1950(); virtual void init(); virtual void calc(Matrix&, Double time); private: Polynomial itsPoly[2*13]; static const Long theirMULTD[13][5]; static const Short theirMULSC[69][2]; }; // // Base class for J2000 nutation multipliers. // class MeasTableMulSC2000Base: public MeasTableMul { public: MeasTableMulSC2000Base(); protected: void doInit(Matrix& result, Polynomial poly[], Int nrowSC, const Long coeffSC[][6]); void doCalc(Matrix& result, Double time, const Polynomial poly[], Int nrowSC); }; // // Class calculating the J2000A nutation multipliers. // class MeasTableMulSC2000A: public MeasTableMulSC2000Base { public: MeasTableMulSC2000A(); virtual void init(); virtual void calc(Matrix&, Double time); private: Polynomial itsPoly[2*678]; static const Long theirMULSC[678][6]; }; // // Class calculating the J2000B nutation multipliers. // class MeasTableMulSC2000B: public MeasTableMulSC2000Base { public: MeasTableMulSC2000B(); virtual void init(); virtual void calc(Matrix&, Double time); private: Polynomial itsPoly[2*77]; static const Long theirMULSC[77][6]; }; // // Class calculating the standard aberration multipliers. // class MeasTableMulAber: public MeasTableMul { public: MeasTableMulAber(); virtual void init(); virtual void calc(Matrix&, Double time); private: Polynomial itsPoly[18]; static const Long theirMABERTD[3][18]; static const Short theirMABER[80][6]; }; // // Class calculating the B1950 aberration multipliers. // class MeasTableMulAber1950: public MeasTableMul { public: MeasTableMulAber1950(); virtual void init(); virtual void calc(Matrix&, Double time); private: Polynomial itsPoly[18]; double itsFactor; //# AU/d static const Short theirMABER[130][6]; static const Short theirABERT1T[10]; static const Short theirABERT2T[2]; static const Short theirABERT3T[1]; static const Double theirABERSPEC[2][6]; }; // // Class calculating the XY solar position multipliers. // class MeasTableMulPosSunXY: public MeasTableMul { public: MeasTableMulPosSunXY(); virtual void init(); virtual void calc(Matrix&, Double time); private: static const Double theirMPOSXY[98][4]; }; // // Class calculating the Z solar position multipliers. // class MeasTableMulPosSunZ: public MeasTableMul { public: MeasTableMulPosSunZ(); virtual void init(); virtual void calc(Matrix&, Double time); private: static const Double theirMPOSZ[29][2]; }; // // Class calculating the XY earth position multipliers. // class MeasTableMulPosEarthXY: public MeasTableMul { public: MeasTableMulPosEarthXY(); virtual void init(); virtual void calc(Matrix&, Double time); private: static const Double theirMPOSXY[189][4]; }; // // Class calculating the Z earth position multipliers. // class MeasTableMulPosEarthZ: public MeasTableMul { public: MeasTableMulPosEarthZ(); virtual void init(); virtual void calc(Matrix&, Double time); private: static const Double theirMPOSZ[32][2]; }; } //# end namespace #endif casacore-3.7.1/measures/Measures/Measure.cc000066400000000000000000000042571476623553700206540ustar00rootroot00000000000000//# Measure.cc: Physical quantities within reference frame //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constants //# Constructors //# Destructor Measure::~Measure() {} //# Operators //# Member functions uInt Measure::giveMe(const String &in, Int N_name, const String tname[]) { return MUString::minimaxNC(in, N_name, tname); } const String* Measure::allTypes(Int &nall, Int &nextra, const uInt *&typ) const { static const Int N_name = 0; static const Int N_extra = 0; static const String *tname = 0; static const uInt *oname = 0; nall = N_name; nextra = N_extra; typ = oname; return tname; } Bool Measure::isModel() const { return False; } //# Global functions std::ostream &operator<<(std::ostream &os, const Measure &meas) { meas.print(os); return os; } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/Measure.h000066400000000000000000000423131476623553700205110ustar00rootroot00000000000000//# Measure.h: Physical quantities within reference frame //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MEASURE_H #define MEASURES_MEASURE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class String; class Unit; class MeasValue; class MRBase; template class Quantum; // // Physical quantities within reference frame // // // // // //
      • Measures module description //
      • Quantum dimensioned values //
      • MeasValue internal measure values //
      • MeasRef class to specify reference // frame //
      • MeasConvert class, doing actual conversions // of a measure from one reference frame to another //
      • Some classes if you really want to understand details: //
          //
        • MeasBase class, the immediate // parent of all specific Measures //
        • MCBase class, the base class // for all specific conversion routines (like // MCEpoch). //
        • MeasData class, containing a set // of generally usable constants, and all program data necessary for // conversions. //
        //
      • MeasTab;e class, containing // the interface for external Tables (like leap-seconds, IERS data, // JPL data). // // // // // // // Measure forms the abstract base class for physical quantities within // a reference frame. Examples of derived classes are: //
          //
        • MEpoch: a moment in time //
        • MDirection: a direction in space //
        • MPosition: a position on Earth //
        • MFrequency: wave characteristics //
        • MRadialVelocity: a space // radial velocity //
        • MDoppler: a Doppler velocity //
        // Measure is the generic name for the more specific instances like, e.g., // MEpoch, an instant in time.
        // A Measure has both a value (specified in some value internal to the specific // Measure, in general called MVMeasure (e.g. MVEpoch)), see // MeasValue for general details; and a // reference type and frame specifier (see // MeasRef class).
        // The MeasRef specifies the reference type // of the value, e.g. TAI, UTC, LAST. In addition the // MeasRef specifies a possible offset (e.g. // the beginning of the year, or today), and, if necessary, Measures necessary // for defining the absolute quantity (e.g. an // MPosition on Earth for LAST), using a // reference frame specifier (see // MeasFrame class).
        // The MeasRef class is templated, but typedefs exist // (and should be used) to // easily specify the correct one, e.g. MEpoch::Ref.
        // A Measure can be converted from one reference frame to another (e.g. // an MDirection can be converted from J2000 to apparent coordinates) by // setting up a measure specific conversion engine (see // MeasConvert class and below). // From an input // MeasRef frame and an output MeasRef frame it // constructs a conversion functional, that can be fed values (with // the () operator).
        // Some conversions can, in addition to the main type (like TAI), specify // details to completely describe any conversion process (e.g. the type // of nutation calculation) by specifying // Aipsrc keyword/value pairs.
        //

        // Measures can in general be constructed from a MeasRef and a // value. The value can be expressed in the internally used units (e.g. // MVEpoch for MEpoch, // MVDirection for MDirection), or // as a Quantum, i.e. a value with a dimension (e.g. (20,"km/s")) // (see Quantum class). The preferred way of // construction is by using the constructor: // // Measure(MVmeasure, Measure::Ref) // // where the reference can be omitted, // defaulting to Measure::DEFAULT), or in simple cases (not needing // additional frame information) be specified directly as a code (e.g. // MEpoch::IAT).
        //

        // The value of the Measure can be obtained by a variety of // get functions, returning in general internal or Quantum // values. The preferred way is a getValue(void), which returns // the specific MVmeasure value, which can then be further formatted // using the appropiate MVmeasure get() functions.
        // Special formatting (like hh:mm:ss.t, dd.mm.ss.t, yy/mm/dd etc) // are catered for in conversion-type classes like // MVAngle, // MVTime.
        //

        // Conversion (within a Measure type) from one reference frame to another // is done by the MeasConvert class. The // class is templated, but has typedefs Measure::Convert (e.g. // MEpoch::Convert) for easy, and recommended, reference.
        // The basic constructors for a // Measure::Convert are: // // // With a default Measure included // Measure::Convert(Measure val, Measure::Ref outref); // // With only input and output reference frames given // Mesaure::Convert( Measure::Ref inref, Measure::Ref outref); // // The val // is used as a model for subsequent input values into this // conversion engine, including possible units; the outref // specifies the output reference frame wanted. The constructor analyses the // conversion wanted, and sets up a vector of routine calls to be called // in sequence for the conversion. The actual conversion is done // by the () operator.
        // To aid in using the raw measures, each class has also a Measure::MVType and // Measure::MCType defined. They denote respectively the Measure Value class // of the internal value, and the class with conversion routines. //

        // In the member description a number of dummy routines are // present. They are the only way I have found to get cxx2html to // get the belonging text properly present. // // // // // // #include // #include // // Example is only to show what can be done, not the easiest way // // Set up a simple reference (no offset or secondary Measures). It // // indicates that times are given in MJD TAI. // MEpoch::Ref reftai(MEpoch::TAI); // // Same, but indicating MJD UTC // MEpoch::Ref refutc(MEpoch::UTC); // // Set up an MEpoch (note that no reference is given. In that case a // // default is assumed (for MEpoch UTC). MJD2000 is a provided constant // // of the MJD at 2000.0 // MEpoch UTCval(Quantity(MeasData::MJD2000, "d"), reftai); // // Set up, just for fun, an epoch, UTC for B1950.0: // MEpoch val1950(Quantity(MeasData::MJDB1950, "d")); // // and use it as an offset in a reference // MEpoch::Ref ref1950(MEpoch::TAI, val1950); // // An epoch for J2000 with an offset of B1950.0 will than be // MEpoch val20_50(Quantity(MeasData::MJD2000-MeasData::MJDB1950, "d"), // ref1950); // // Set up conversion from TAI(with values in days w.r.t. B1950.0) to UTC: // MEpoch::Convert tai_to_utc(val20_50, refutc); // // And convert a value (in this case the value in val20_50, the model) // // from TAI(relative to B1950.0) to 'absolute' UTC // MEpoch result = tai_to_utc(); // // Show result // cout << "Result 1: " << result << endl; // // To convert 10 years since B1950.0 // result = tai_to_utc(Quantity(10.,"a")); // cout << "Result 2: " << result << endl; // // To convert any value in years(the last used units of the model) since B1950.0 // result = tai_to_utc(12.3); // cout << "Result 3: " << result << endl; // // Which generates the output: // // Result 1: Epoch: 51544::11:59:25.2154 // Result 2: Epoch: 36934::10:09:42.1283 // Result 3: Epoch: 37774::11:57:41.1085 // // // // // To be able to specify a physical entity absolutely in any reference frame; // and to be able to convert from one frame to another. E.g. Local Sidereal // Time to Temps Atomic International. A templated version for the MeasRef // and MeasConvert was chosen to be able to check most arguments at // compile time. // // // //

      • more Measures, e.g. MPlanet //
      • operators on Measures (e.g. MEpoch - MEpoch == MDuration) // class Measure { public: //# Enumerations // Each derived class should have a Types enumeration, specifying // the recognised frame types. It is formatted as: // // enum Types { // CODE1, // CODE2, // ..., // N_Types, // Number of types // SPEC1 = n, // Possible special manipulator code // ....., // SYNONYM1 = CODEn, // Probable synonyms // ...., // DEFAULT = CODEm}; // // Dummy for cxx2html enum Types {N_Types, DEFAULT = 0}; //# Typedefs // Each Measure should have typedefs of the form: // // typedef MeasConvert Convert; // typedef MeasRef Ref; // // Dummy for cxx2html typedef void* Convert; //# Friends // Each derived class should have: // // friend class MeasConvert; // // Output a Measure friend std::ostream &operator<<(std::ostream &os, const Measure &meas); //# Constructors //# Destructor // Destructor virtual ~Measure(); //# Operators //# General Member Functions // Each Measure should have the following set functions (with appropiate // MVs and Ref): // // void set(const MVmeasure &dt); // void set(const Measure::Ref &rf); // void set(const MVmeasure &dt, const Measure::Ref &rf); // // virtual void set(const MeasValue &dt) = 0; virtual Bool putValue(const Vector > &in) = 0; // // Set the offset in the reference (False if non-matching Measure) virtual Bool setOffset(const Measure &in) = 0; // // Check the type of derived Measure entity (e.g. "Epoch") virtual Bool areYou(const String &tp) const = 0; // All should have: // Assert that we are the correct Measure type // //
      • AipsError if wrong Measure type // // Each Measure should have: // static void assure(const Measure &in); // virtual void assured(const String &tp) const = 0; // // Tell me your Measure type (e.g. "Epoch") virtual const String &tellMe() const = 0; // Each Measure should have the following static methods to give its // name (e.g. Epoch) or reference type (e.g. UTC):
        // // // Show the Measure type (e.g. "Direction") // static const String &showMe(); // // Cast an integer to the appropriate reference type. Avaialable to provide // // a safe cast in cases where Measure type is not explicitly known. // static Measure::Types castType(uInt tp); // // Show the reference type (e.g. MEpoch::showType(MEpoch::IAT) == "TAI") // static const String &showType(uInt tp); // static const String &showType(Measure::Types tp); // // virtual String getRefString() const = 0; // // Tell me if you are a pure model (e.g. a planet) virtual Bool isModel() const; // // Each derived class should have a string-to-code translation routine // for the reference type. The routine returns False if unknown String (and // a default mr), else an appropiate mr reference. // // Bool giveMe(Measure::Ref &mr, const String &in); // static Bool getType(Measure::Types &tp, const String &in); // // // Dummy for cxx2html void dummy_giveMe() const {} // // // Set the reference type to the specified String. False if illegal // string, reference set to DEFAULT. virtual Bool setRefString(const String &in) = 0; // Get the default reference type virtual const String &getDefaultType() const = 0; // Get a list of all known reference codes. nall returns the number in list, // nextra the number of specials (like planets) that should be at // end of list). typ returns the list of corresponding types. // All should have // // static const String* allMyTypes(Int &nall, Int &nextra, // const uInt *&typ); // // virtual const String* allTypes(Int &nall, Int &nextra, const uInt *&typ) const; // // // Check if all internal tables of types (both enum and String) are // complete and correct. This function is called automatically if and when // necessary. // //
      • AipsError if a (programming) error in the types. // // All should have // // static void checkMyTypes(); // // virtual void checkTypes() const = 0; // // // A general string checking routine to be used in derived measures. // Its arguments are the string to be converted (in), an array of // strings to check against (tname), and its length (N_name). The check // is case insensitive and mini-max. A return value less than N_name indicates // success. static uInt giveMe(const String &in, Int N_name, const String tname[]); // Each class should have a function to return its reference: // // Measure::Ref getRef() const; // // // Dummy for cxx2html void dummy_getRef() const {} // // // Each derived class should be able to get its internal value and have: // // const MVmeasure &getValue() const; // // To get dimensioned data, each derived class should contain the // appropiate one of: // // Quantity get(const Unit &unit) const; // Quantum > get(const Unit &unit) const; // // void dummy_getValue() const {} // // // // Get unit (only available if Measure generated from a Quantum, else "") virtual const Unit &getUnit() const = 0; // Get data pointer (used by MeasConvert) virtual const MeasValue* getData() const = 0; // Get general reference pointer virtual MRBase *getRefPtr() const = 0; // Print a Measure virtual void print(std::ostream &os) const = 0; // Create a copy // virtual Measure *clone() const = 0; // protected: private: //# Enumerations //# Data // Each class will have the following information: // Actual data // // MVmeasure data; // // Reference frame data // // MeasRef ref; // // Possible input units // // Unit unit; // // And maybe later (or somewhere else) // // MeasErr error; // // // Dummy for cxx2html void dummy_data() const {} // // //# Member functions // Clear the measure virtual void clear() = 0; }; //# Global functions // Global functions // // Output declaration std::ostream &operator<<(std::ostream &os, const Measure &meas); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MeasureHolder.cc000066400000000000000000000364051476623553700220120ustar00rootroot00000000000000//# MeasureHolder.cc: A holder for Measures to enable record conversions //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors MeasureHolder::MeasureHolder() : mvhold_p(0), convertmv_p(False) { createMV(0); } MeasureHolder::MeasureHolder(const Measure &in) : hold_p(in.clone()), mvhold_p(0), convertmv_p(False) {} MeasureHolder::MeasureHolder(const MeasureHolder &other) : RecordTransformable(), mvhold_p(0), convertmv_p(False) { if (other.hold_p) hold_p.reset(other.hold_p->clone()); createMV(other.mvhold_p.nelements()); for (uInt i=0; iclone(); } } //# Destructor MeasureHolder::~MeasureHolder() { createMV(0); } //# Operators MeasureHolder &MeasureHolder::operator=(const MeasureHolder &other) { if (this != &other) { if (other.hold_p) { hold_p.reset(other.hold_p->clone()); } else { hold_p.reset(); } createMV(other.mvhold_p.nelements()); for (uInt i=0; iclone(); } } return *this; } //# Member Functions Bool MeasureHolder::isEmpty() const { return (!hold_p); } Bool MeasureHolder::isMeasure() const { return static_cast(hold_p); } Bool MeasureHolder::isMDirection() const { return (hold_p && dynamic_cast(hold_p.get())); } Bool MeasureHolder::isMDoppler() const { return (hold_p && dynamic_cast(hold_p.get())); } Bool MeasureHolder::isMEpoch() const { return (hold_p && dynamic_cast(hold_p.get())); } Bool MeasureHolder::isMFrequency() const { return (hold_p && dynamic_cast(hold_p.get())); } Bool MeasureHolder::isMPosition() const { return (hold_p && dynamic_cast(hold_p.get())); } Bool MeasureHolder::isMRadialVelocity() const { return (hold_p && dynamic_cast(hold_p.get())); } Bool MeasureHolder::isMBaseline() const { return (hold_p && dynamic_cast(hold_p.get())); } Bool MeasureHolder::isMuvw() const { return (hold_p && dynamic_cast(hold_p.get())); } Bool MeasureHolder::isMEarthMagnetic() const { return (hold_p && dynamic_cast(hold_p.get())); } const Measure &MeasureHolder::asMeasure() const { if (!hold_p) { throw(AipsError("Empty MeasureHolder argument for asMeasure")); } return *hold_p; } const MDirection &MeasureHolder::asMDirection() const { if (!hold_p || !isMDirection()) { throw(AipsError("Empty or wrong MeasureHolder for asMDirection")); } return dynamic_cast(*hold_p.get()); } const MDoppler &MeasureHolder::asMDoppler() const { if (!hold_p || !isMDoppler()) { throw(AipsError("Empty or wrong MeasureHolder for asMDoppler")); } return dynamic_cast(*hold_p.get()); } const MEpoch &MeasureHolder::asMEpoch() const { if (!hold_p || !isMEpoch()) { throw(AipsError("Empty or wrong MeasureHolder for asMEpoch")); } return dynamic_cast(*hold_p.get()); } const MFrequency &MeasureHolder::asMFrequency() const { if (!hold_p || !isMFrequency()) { throw(AipsError("Empty or wrong MeasureHolder for asMFrequency")); } return dynamic_cast(*hold_p.get()); } const MPosition &MeasureHolder::asMPosition() const { if (!hold_p || !isMPosition()) { throw(AipsError("Empty or wrong MeasureHolder for asMPosition")); } return dynamic_cast(*hold_p.get()); } const MRadialVelocity &MeasureHolder::asMRadialVelocity() const { if (!hold_p || !isMRadialVelocity()) { throw(AipsError("Empty or wrong MeasureHolder for asMRadialVelocity")); } return dynamic_cast(*hold_p.get()); } const MEarthMagnetic &MeasureHolder::asMEarthMagnetic() const { if (!hold_p || !isMEarthMagnetic()) { throw(AipsError("Empty or wrong MeasureHolder for asMEarthMagnetic")); } return dynamic_cast(*hold_p.get()); } const MBaseline &MeasureHolder::asMBaseline() const { if (!hold_p || !isMBaseline()) { throw(AipsError("Empty or wrong MeasureHolder for asMBaseline")); } return dynamic_cast(*hold_p.get()); } const Muvw &MeasureHolder::asMuvw() const { if (!hold_p || !isMuvw()) { throw(AipsError("Empty or wrong MeasureHolder for asMuvw")); } return dynamic_cast(*hold_p.get()); } Bool MeasureHolder::fromRecord(String &error, const RecordInterface &in) { if (in.isDefined(String("type")) && in.isDefined(String("refer")) && in.type(in.idToNumber(RecordFieldId("type"))) == TpString && in.type(in.idToNumber(RecordFieldId("refer"))) == TpString) { if (!getType(error, in)) { error += String("Unknown Measure record in MeasureHolder::fromRecord\n"); return False; } String rf; in.get(RecordFieldId("refer"), rf); if (!hold_p->setRefString(rf)) { if (!rf.empty()) { LogIO os(LogOrigin("MeasureHolder", String("fromRecord(String, const RecordInterface"), WHERE)); os << LogIO::WARN << String("Illegal or unknown reference type '") + rf + "' for " + downcase(String(hold_p->tellMe())) + " definition. DEFAULT (" + hold_p->getDefaultType() + ") assumed." << LogIO::POST; } } if (in.isDefined(String("offset")) && in.type(in.idToNumber(RecordFieldId("offset"))) == TpRecord) { MeasureHolder x; if (!x.fromRecord(error, in.asRecord(RecordFieldId("offset")))) { return False; } if (!hold_p->setOffset(x.asMeasure())) { error += String("Unmatched offset type in MeasureHolder::fromRecord\n"); return False; } } QuantumHolder q0, q1, q2; uInt n(0); if (in.isDefined(String("m0")) && in.type(in.idToNumber(RecordFieldId("m0"))) == TpRecord) { if (!q0.fromRecord(error, in.asRecord(RecordFieldId("m0")))) { return False; } n = 1; if (in.isDefined(String("m1")) && in.type(in.idToNumber(RecordFieldId("m1"))) == TpRecord) { if (!q1.fromRecord(error, in.asRecord(RecordFieldId("m1")))) { return False; } n = 2; if (in.isDefined(String("m2")) && in.type(in.idToNumber(RecordFieldId("m2"))) == TpRecord) { if (!q2.fromRecord(error, in.asRecord(RecordFieldId("m2")))) { return False; } n = 3; } } } Vector vq(n); if (n > 0) vq(0) = Quantity(q0.asQuantumVectorDouble().getValue()(0), q0.asQuantumVectorDouble().getFullUnit()); if (n > 1) vq(1) = Quantity(q1.asQuantumVectorDouble().getValue()(0), q1.asQuantumVectorDouble().getFullUnit()); if (n > 2) vq(2) = Quantity(q2.asQuantumVectorDouble().getValue()(0), q2.asQuantumVectorDouble().getFullUnit()); if (!hold_p->putValue(vq)) { error += String("Illegal quantity in MeasureHolder::fromRecord\n"); return False; } uInt nel(0); if (n>0) nel = q0.asQuantumVectorDouble().getValue().nelements(); if (n>1 && nel != q1.asQuantumVectorDouble().getValue().nelements()) { error += String("Illegal number of values in MeasureHolder m1\n"); return False; } if (n>2 && nel != q2.asQuantumVectorDouble().getValue().nelements()) { error += String("Illegal number of values in MeasureHolder m2\n"); return False; } if (nel>1) { makeMV(nel); for (uInt i=nel-1; i 0) vq(0) = Quantity(q0.asQuantumVectorDouble().getValue()(i), q0.asQuantumVectorDouble().getFullUnit()); if (n > 1) vq(1) = Quantity(q1.asQuantumVectorDouble().getValue()(i), q1.asQuantumVectorDouble().getFullUnit()); if (n > 2) vq(2) = Quantity(q2.asQuantumVectorDouble().getValue()(i), q2.asQuantumVectorDouble().getFullUnit()); if (!hold_p->putValue(vq)) { error += String("Illegal quantity in MeasureHolder value\n"); return False; } if (!setMV(i, *hold_p->getData())) { error += String("Illegal MeasValue in MeasureHolder value\n"); return False; } } } convertmv_p = False; return True; } error += String("Illegal Measure record in MeasureHolder::fromRecord\n"); return False; } Bool MeasureHolder::fromString(String &error, const String &in) { if (!getType(error, in)) { error += String("Unknown Measure type in MeasureHolder::fromString\n"); return False; } return True; } Bool MeasureHolder::toRecord(String &error, RecordInterface &out) const { if (hold_p && putType(error, out)) { out.define(RecordFieldId("refer"), hold_p->getRefString()); const Measure *off = hold_p->getRefPtr()->offset(); if (off) { Record offs; if (!MeasureHolder(*off).toRecord(error, offs)) return False; out.defineRecord(RecordFieldId("offset"), offs); } // Make sure units available Vector > res = hold_p->getData()->getRecordValue(); uInt n(res.nelements()); uInt nel(nelements()); Record val; // Single value only if (!convertmv_p || nel==0) { if (n > 2) { if (!QuantumHolder(res(2)).toRecord(error, val)) return False; out.defineRecord(RecordFieldId("m2"), val); } if (n > 1) { if (!QuantumHolder(res(1)).toRecord(error, val)) return False; out.defineRecord(RecordFieldId("m1"), val); } if (n > 0) { if (!QuantumHolder(res(0)).toRecord(error, val)) return False; out.defineRecord(RecordFieldId("m0"), val); } } else { // multiple values Vector m2(nel); Vector m1(nel); Vector m0(nel); for (uInt i=0; igetRecordValue(); if (n>2) m2(i) = res(2).getValue(); if (n>1) m1(i) = res(1).getValue(); if (n>0) m0(i) = res(0).getValue(); } if (n > 2) { if (!QuantumHolder(Quantum >(m2, res(2).getFullUnit())). toRecord(error, val)) return False; out.defineRecord(RecordFieldId("m2"), val); } if (n > 1) { if (!QuantumHolder(Quantum >(m1, res(1).getFullUnit())). toRecord(error, val)) return False; out.defineRecord(RecordFieldId("m1"), val); } if (n > 0) { if (!QuantumHolder(Quantum >(m0, res(0).getFullUnit())). toRecord(error, val)) return False; out.defineRecord(RecordFieldId("m0"), val); } } return True; } error += String("No Measure specified in MeasureHolder::toRecord\n"); return False; } void MeasureHolder::toRecord(RecordInterface& out) const { String error; if (! toRecord(error, out)) { throw AipsError(error); } } Bool MeasureHolder::toType(String &error, RecordInterface &out) const { if (hold_p && putType(error, out)) return True; error += String("No Measure specified in MeasureHolder::toType\n"); return False; } Bool MeasureHolder::fromType(String &error, const RecordInterface &in) { if (in.isDefined(String("type")) && in.type(in.idToNumber(RecordFieldId("type"))) == TpString) { if (!getType(error, in)) { error += String("Unknown Measure record in MeasureHolder::fromType\n"); return False; } return True; } error += String("Illegal Measure record in MeasureHolder::fromType\n"); return False; } const String &MeasureHolder::ident() const { static String myid = "meas"; return myid; } Bool MeasureHolder::setMV(uInt pos, const MeasValue &in) { if (mvhold_p.nelements() > pos) mvhold_p[pos] = in.clone(); else return False; convertmv_p = True; return True; } MeasValue *MeasureHolder::getMV(uInt pos) const { if (mvhold_p.nelements() > pos) return mvhold_p[pos]; else return static_cast(0); } Bool MeasureHolder::putType(String &, RecordInterface &out) const { out.define(RecordFieldId("type"), downcase(String(hold_p->tellMe()))); return True; } Bool MeasureHolder::getType(String &error, const RecordInterface &in) { String tp; in.get(RecordFieldId("type"), tp); return getType(error, tp); } Bool MeasureHolder::getType(String &error, const String &in) { String tp(in); tp.downcase(); hold_p.reset(); if (tp == downcase(MDirection::showMe())) { hold_p.reset(new MDirection()); } else if (tp == downcase(MDoppler::showMe())) { hold_p.reset(new MDoppler()); } else if (tp == downcase(MEpoch::showMe())) { hold_p.reset(new MEpoch()); } else if (tp == downcase(MFrequency::showMe())) { hold_p.reset(new MFrequency()); } else if (tp == downcase(MPosition::showMe())) { hold_p.reset(new MPosition()); } else if (tp == downcase(MRadialVelocity::showMe())) { hold_p.reset(new MRadialVelocity()); } else if (tp == downcase(MBaseline::showMe())) { hold_p.reset(new MBaseline()); } else if (tp == downcase(Muvw::showMe())) { hold_p.reset(new Muvw()); } else if (tp == downcase(MEarthMagnetic::showMe())) { hold_p.reset(new MEarthMagnetic()); } else { error = in + " is an unknown measure type"; return False; } return True; } void MeasureHolder::createMV(uInt n) { for (uInt i=0; i #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class Measure; class MDirection; class MDoppler; class MEpoch; class MFrequency; class MPosition; class MRadialVelocity; class Muvw; class MBaseline; class MEarthMagnetic; class MeasValue; // A holder for Measures to enable record conversions // // // // //
      • RecordInterface class //
      • Measure class // // // // A Holder of general Measures // // // // This class can be used to handle heterogeneous collections of Measures, e.g. // as a Vector. With the aid of the // toRecord() and fromRecord() functions it can be used // to convert a Measure object into or from a record. // A MeasureHolder is created from a Measure, or can be empty. // // Checks on the contents can be made with functions like // isMDirection and the contents can be obtained with // functions like asMDirection. It is an error to try and // retrieve a measure of the wrong type and doing so will generate an // exception (AipsError). // // The MeasureHolder can, in addition to the Measure it is holding, also hold // a block of MeasValues. This is especially useful for intertask // communication (e.g. with Glish), for reasons of speed. In general the // additional values will be created when the record used to create // a Holder contains a Quantity rather than a quantity in // the m0, m1 and/or m2 fields. The getMV() method can be used to // access the nelements() additional information. They can be // (re-)set with the setMV() method (after a possible creation // of the extra block if not already there, or of the wrong length, // with makeMV(). If any value is set they will be used in // creating records, with the first value always overwriting the actual // Measure value. // // // // // // TableRecord rec; // MDirection dir(MVDirection(Quantity(12.5, 'deg'), Quantity(-2, 'deg')), // MDirection::J2000); // String error; // error message // if (!MeasureHolder(dir).toRecord(error, rec)) { // cout << error << endl; // } // Record grec; // a Record // if (!MeasureHolder(dir).toRecord(error, grec)) { // make record // cout << error << endl; // } // // Note that for GlishRecords use can be made of the // // GlishRecord::to/fromrecord() methods. // // // // // To make general conversions between Measures and records, without knowing // the actual Measure being converted. // class MeasureHolder : public RecordTransformable { public: //# Friends //# Enumerations //# Constructors // Creates an empty holder MeasureHolder(); // Create from a Measure (copy made) MeasureHolder(const Measure &in); // Copy a holder (copy semantics) MeasureHolder(const MeasureHolder &other); //# Destructor ~MeasureHolder(); //# Operators // Assignment (copy semantics) MeasureHolder &operator=(const MeasureHolder &other); //# Member Functions // Check the the MeasureHolder holds the specified Measure type. Return // True if if does and False otherwise. // Bool isEmpty() const; Bool isMeasure() const; Bool isMDirection() const; Bool isMDoppler() const; Bool isMEpoch() const; Bool isMFrequency() const; Bool isMPosition() const; Bool isMRadialVelocity() const; Bool isMBaseline() const; Bool isMuvw() const; Bool isMEarthMagnetic() const; // // Get a specific Measure from the holder (with lifetime as long // as holder exists). // //
      • AipsError if holder empty //
      • AipsError if holder contains wrong Measure // // const Measure &asMeasure() const; const MDirection &asMDirection() const; const MDoppler &asMDoppler() const; const MEpoch &asMEpoch() const; const MFrequency &asMFrequency() const; const MPosition &asMPosition() const; const MRadialVelocity &asMRadialVelocity() const; const MBaseline &asMBaseline() const; const Muvw &asMuvw() const; const MEarthMagnetic &asMEarthMagnetic() const; // // Create a Measure from a record. An error message is generated, and False // returned if an invalid record is given. A valid record will return True. // A valid record contains the following fields (any additional fields are // ignored): //
          //
        • type = TpString: type of Measure (direction, epoch, etc; case // insensitive) //
        • refer = TpString: reference type of Measure (case insensitive; // enough characters to be unique (e.g. J20, j200, utc, b1950, J2000); // unknown reference type will log an error message and translate into // the default type for the Measure. //
        • m0, m1, ... = TpRecord(Quantity): one or more Quantities giving // the value(s) for this Measure (e.g. longitude and latitude for a // direction). Each quantity can either be a scalar quantity or a // Quantum >. //
        • offset = TpRecord(Measure)--optional: an optional offset as a // Measure of the same type as the main Measure (e.g. an MEpoch for an // MEpoch) //
        // A Measure can be created from a string. In that case the string // will only indicate the type of measure (like direction), and will // create a default measure of that given type. In essence identical // to the fromType() method. // Error messages are postfixed to error. // virtual Bool fromRecord(String &error, const RecordInterface &in); virtual Bool fromString(String &error, const String &in); // // Create a record from a Measure. The return will be False and an error // message generated only if the MeasureHolder does not contain a Measure. // Error messages are postfixed to error. virtual Bool toRecord(String &error, RecordInterface &out) const; // This version throws an exception if the conversion cannot // occur. It is meant for more allow more compact calling code for callers // that are content with just letting the exception proceed up the call stack // so they do not have to check a return status. This is, among other things, what // exceptions are for after all. virtual void toRecord(RecordInterface& outRecord) const; // Create a default Measure or a record with only a type from a Measure // Bool toType(String &error, RecordInterface &out) const; Bool fromType(String &error, const RecordInterface &in); // // Get identification of record virtual const String &ident() const; // Do we write MeasValues to record? Bool writeMV() const { return convertmv_p; } // Make a block of n MeasValues void makeMV(uInt n) { createMV(n); } // Get number of MeasValue pointers in block uInt nelements() const { return mvhold_p.nelements(); } // Set a measvalue at position pos (False if illegal pos) Bool setMV(uInt pos, const MeasValue &in); // Get a pointer to a MeasValue (or 0) MeasValue *getMV(uInt pos) const; private: //# Data Members // Pointer to a Measure std::unique_ptr hold_p; // Block of pointers to measure values to make a faster interface Block mvhold_p; // Should the mvhold_p be converted into record? Bool convertmv_p; //# Member functions // Aid for to/from Record, String and Type // Bool putType(String &error, RecordInterface &out) const; Bool getType(String &error, const RecordInterface &in); Bool getType(String &error, const String &in); // // Make a MeasValue block of pointers of length n void createMV(uInt n); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/MeasuresProxy.cc000066400000000000000000000600641476623553700220770ustar00rootroot00000000000000//# MeasuresProxy.cc: Proxy class object, to be used in language bindings //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MeasuresProxy::MeasuresProxy() : pcomet_p(0) {;} MeasuresProxy::~MeasuresProxy() { delete pcomet_p; } String MeasuresProxy::getMeasureType(const Record &in) { //Bool b; String out; if (in.isDefined("type")) { out= "???";//b = GlishArray(in.get("type")).get(out); } else { out = "none"; } return out; } Bool MeasuresProxy::doFrame(const MeasureHolder &in) { if (in.isMPosition() || in.isMDirection() || in.isMEpoch() || in.isMRadialVelocity()) { frame_p.set(in.asMeasure()); return True; } return False; } Bool MeasuresProxy::doFrame(const String &in) { try { delete pcomet_p; pcomet_p = 0; if (in.empty()) { pcomet_p = new MeasComet; } else { pcomet_p = new MeasComet(in); } if (!pcomet_p->ok()) { delete pcomet_p; pcomet_p = 0; return False; } frame_p.set(*pcomet_p); } catch (std::exception& x) { return False; } return True; } String MeasuresProxy::dirshow(const Record& rec) { String out; MeasureHolder mh = rec2mh(rec); if (mh.isMeasure()) { ostringstream os; os << mh.asMeasure() << " " << (mh.asMeasure()).getRefString(); out = os.str(); } else { throw(AipsError("Non-measure input")); } return out; } // Convert measures Bool MeasuresProxy::makeMeasure(String &error, MeasureHolder &out, const MeasureHolder &in, const String &outref, const Record &off) { MeasureHolder mo; if (off.nfields() > 0) { if (!mo.fromRecord(error, off)) { error += String("Non-measure type offset in measure conversion\n"); return False; } mo.asMeasure().getRefPtr()->set(frame_p); } in.asMeasure().getRefPtr()->set(frame_p); try { if (in.isMEpoch()) { MEpoch::Ref outRef; MEpoch::Types tp; String x = outref; Bool raze = False; if (x.before(2) == "r_" || x.before(2) == "R_") { raze = True; x = x.from(2); } if (MEpoch::getType(tp, x)) { if (raze) outRef.setType(tp | MEpoch::RAZE); else outRef.setType(tp); } else outRef.setType(MEpoch::DEFAULT); outRef.set(frame_p); if (!mo.isEmpty()) { if (mo.isMEpoch()) outRef.set(mo.asMeasure()); else { error += "Non-conforming offset measure type\n"; return False; } } MEpoch::Convert mcvt(MEpoch::Convert(in.asMeasure(), outRef)); out = MeasureHolder(mcvt()); out.makeMV(in.nelements()); for (uInt i=0; i (*in.getMV(i))).getValue())) { error += "Cannot get extra measure value in DOmeasures::measures\n"; return False; } } } else if (in.isMPosition()) { MPosition::Ref outRef; MPosition::Types tp; if (MPosition::getType(tp, outref)) outRef.setType(tp); else outRef.setType(MPosition::DEFAULT); outRef.set(frame_p); if (!mo.isEmpty()) { if (mo.isMPosition()) outRef.set(mo.asMeasure()); else { error += "Non-conforming offset measure type\n"; return False; } } MPosition::Convert mcvt(MPosition::Convert(in.asMeasure(), outRef)); out = MeasureHolder(mcvt()); out.makeMV(in.nelements()); for (uInt i=0; i (*in.getMV(i))).getValue())) { error += "Cannot get extra measure value in DOmeasures::measures\n"; return False; } } } else if (in.isMDirection()) { MDirection::Ref outRef; MDirection::Types tp; if (MDirection::getType(tp, outref)) outRef.setType(tp); else outRef.setType(MDirection::DEFAULT); outRef.set(frame_p); if (!mo.isEmpty()) { if (mo.isMDirection()) outRef.set(mo.asMeasure()); else { error += "Non-conforming offset measure type\n"; return False; } } MDirection::Convert mcvt(MDirection::Convert(in.asMeasure(), outRef)); out = MeasureHolder(mcvt()); out.makeMV(in.nelements()); for (uInt i=0; i (*in.getMV(i))).getValue())) { error += "Cannot get extra measure value in DOmeasures::measures\n"; return False; } } } else if (in.isMFrequency()) { MFrequency::Ref outRef; MFrequency::Types tp; if (MFrequency::getType(tp, outref)) outRef.setType(tp); else outRef.setType(MFrequency::DEFAULT); outRef.set(frame_p); if (!mo.isEmpty()) { if (mo.isMFrequency()) outRef.set(mo.asMeasure()); else { error += "Non-conforming offset measure type\n"; return False; } } MFrequency::Convert mcvt(MFrequency::Convert(in.asMeasure(), outRef)); out = MeasureHolder(mcvt()); out.makeMV(in.nelements()); for (uInt i=0; i (*in.getMV(i))).getValue())) { error += "Cannot get extra measure value in DOmeasures::measures\n"; return False; } } } else if (in.isMDoppler()) { MDoppler::Ref outRef; MDoppler::Types tp; if (MDoppler::getType(tp, outref)) outRef.setType(tp); else outRef.setType(MDoppler::DEFAULT); outRef.set(frame_p); if (!mo.isEmpty()) { if (mo.isMDoppler()) outRef.set(mo.asMeasure()); else { error += "Non-conforming offset measure type\n"; return False; } } MDoppler::Convert mcvt(MDoppler::Convert(in.asMeasure(), outRef)); out = MeasureHolder(mcvt()); out.makeMV(in.nelements()); for (uInt i=0; i (*in.getMV(i))).getValue())) { error += "Cannot get extra measure value in DOmeasures::measures\n"; return False; } } } else if (in.isMRadialVelocity()) { MRadialVelocity::Ref outRef; MRadialVelocity::Types tp; if (MRadialVelocity::getType(tp, outref)) outRef.setType(tp); else outRef.setType(MRadialVelocity::DEFAULT); outRef.set(frame_p); if (!mo.isEmpty()) { if (mo.isMRadialVelocity()) outRef.set(mo.asMeasure()); else { error += "Non-conforming offset measure type\n"; return False; } } MRadialVelocity::Convert mcvt(MRadialVelocity::Convert(in.asMeasure(), outRef)); out = MeasureHolder(mcvt()); out.makeMV(in.nelements()); for (uInt i=0; i (*in.getMV(i))).getValue())) { error += "Cannot get extra measure value in DOmeasures::measures\n"; return False; } } } else if (in.isMBaseline()) { MBaseline::Ref outRef; MBaseline::Types tp; if (MBaseline::getType(tp, outref)) outRef.setType(tp); else outRef.setType(MBaseline::DEFAULT); outRef.set(frame_p); if (!mo.isEmpty()) { if (mo.isMBaseline()) outRef.set(mo.asMeasure()); else { error += "Non-conforming offset measure type\n"; return False; } } MBaseline::Convert mcvt(MBaseline::Convert(in.asMeasure(), outRef)); out = MeasureHolder(mcvt()); out.makeMV(in.nelements()); for (uInt i=0; i (*in.getMV(i))).getValue())) { error += "Cannot get extra measure value in DOmeasures::measures\n"; return False; } } } else if (in.isMuvw()) { Muvw::Ref outRef; Muvw::Types tp; if (Muvw::getType(tp, outref)) outRef.setType(tp); else outRef.setType(Muvw::DEFAULT); outRef.set(frame_p); if (!mo.isEmpty()) { if (mo.isMuvw()) outRef.set(mo.asMeasure()); else { error += "Non-conforming offset measure type\n"; return False; } } Muvw::Convert mcvt(Muvw::Convert(in.asMeasure(), outRef)); out = MeasureHolder(mcvt()); out.makeMV(in.nelements()); for (uInt i=0; i (*in.getMV(i))).getValue())) { error += "Cannot get extra measure value in DOmeasures::measures\n"; return False; } } } else if (in.isMEarthMagnetic()) { MEarthMagnetic::Ref outRef; MEarthMagnetic::Types tp; if (MEarthMagnetic::getType(tp, outref)) outRef.setType(tp); else outRef.setType(MEarthMagnetic::DEFAULT); outRef.set(frame_p); if (!mo.isEmpty()) { if (mo.isMEarthMagnetic()) outRef.set(mo.asMeasure()); else { error += "Non-conforming offset measure type\n"; return False; } } MEarthMagnetic::Convert mcvt(MEarthMagnetic::Convert(in.asMeasure(), outRef)); out = MeasureHolder(mcvt()); out.makeMV(in.nelements()); for (uInt i=0; i (*in.getMV(i))).getValue())) { error += "Cannot get extra measure value in DOmeasures::measures\n"; return False; } } } if (out.isEmpty()) { error += "No measure created; probably unknow measure type\n"; return False; } } catch (std::exception& x) { error += "Cannot convert due to missing frame information\n"; return False; } return True; } // Make uvw from baselines Bool MeasuresProxy::toUvw(String &error, MeasureHolder &out, Vector &xyz, Vector &dot, const MeasureHolder &in) { if (!in.isMBaseline()) { error += "Trying to convert non-baseline to uvw\n"; return False; } try { in.asMeasure().getRefPtr()->set(frame_p); // attach frame MBaseline::Convert mcvt(in.asMeasure(), MBaseline::J2000); const MVBaseline &bas2000 = mcvt().getValue(); MVDirection dir2000; Double dec2000; if (!frame_p.getJ2000(dir2000) || !frame_p.getJ2000Lat(dec2000)) { error += "No direction in frame for uvw calculation\n"; return False; } MVuvw uvw2000 = MVuvw(bas2000, dir2000); out = MeasureHolder(Muvw(uvw2000, Muvw::J2000)); uInt nel = in.nelements() == 0 ? 1 : in.nelements(); out.makeMV(in.nelements()); Double sd = sin(dec2000); Double cd = cos(dec2000); dot.resize(3*nel); xyz.resize(3*nel); if (in.nelements() == 0) { xyz = uvw2000.getValue(); dot[0] = -sd*xyz[1] + cd*xyz[2]; dot[1] = +sd*xyz[0]; dot[2] = -cd*xyz[0]; } for (uInt i=0; i<3*in.nelements(); i+=3) { const MVuvw &mv = MVuvw(mcvt(dynamic_cast (*in.getMV(i/3))).getValue(), dir2000); if (!out.setMV(i/3, mv)) { error += "Cannot get extra baseline value in DOmeasures::toUvw\n"; return False; } for (uInt j=0; j<3; ++j) xyz[i+j] = mv.getValue()[j]; dot[i+0] = -sd*xyz[i+1] + cd*xyz[i+2]; dot[i+1] = +sd*xyz[i+0]; dot[i+2] = -cd*xyz[i+0]; } for (uInt j=0; j<3*nel; ++j) { dot[j] *= M_PI/180/240./1.002737909350795; } } catch (std::exception& x) { error += "Cannot convert baseline to uvw: frame " "information missing"; return False; } return True; } // Expand positions to baselines Bool MeasuresProxy::expandIt(String &error, MeasureHolder &out, Vector &xyz, const MeasureHolder &in) { if (!in.isMuvw()) { error += "Trying to expand non-baseline type\n"; return False; } const MVuvw &uvw2000 = in.asMuvw().getValue(); if (in.nelements() < 2) { xyz.resize(3); xyz = uvw2000.getValue(); out = MeasureHolder(Muvw(uvw2000, Muvw::J2000)); } else { uInt nel = (in.nelements() * (in.nelements()-1))/2; xyz.resize(3*nel); uInt k=0; for (uInt i=0; i(*in.getMV(j))).getValue(); mv -= (dynamic_cast(*in.getMV(i))).getValue(); if (k == 0) { out = MeasureHolder(Muvw(mv, Muvw::J2000)); out.makeMV(nel); } if (!out.setMV(k, mv)) { error += "Cannot expand baseline value in DOmeasures::expand\n"; return False; } for (uInt j=0; j<3; ++j) xyz[3*k+j] = mv.getValue()[j]; ++k; } } } return True; } MeasureHolder MeasuresProxy::rec2mh(const Record& rec) { MeasureHolder mh; String err; if (!mh.fromRecord(err, rec)) { throw AipsError(err); } return mh; } Record MeasuresProxy::mh2rec(const MeasureHolder& mh) { Record rec; String err; if (!mh.toRecord(err, rec)) { throw AipsError(err); } return rec; } Record MeasuresProxy::measure(const Record& rec, const String& str, const Record& form) { MeasureHolder mhout; const MeasureHolder& mhin = rec2mh(rec); String err; if (!makeMeasure(err, mhout, mhin, str, form)) { throw AipsError(err); } return mh2rec(mhout); } Bool MeasuresProxy::doframe(const Record& rec) { /// @todo string method MeasureHolder mh = rec2mh(rec); return doFrame(mh); } Record MeasuresProxy::doptorv(const Record& rec, const String& str) { MeasureHolder mh = rec2mh(rec); MeasureHolder mhout; MRadialVelocity::Ref outRef; MRadialVelocity tout; tout.giveMe(outRef, str); mhout = MeasureHolder(MRadialVelocity:: fromDoppler(mh.asMDoppler(), static_cast (outRef.getType()))); uInt nel(mh.nelements()); if (nel>0) { mhout.makeMV(nel); MDoppler::Convert mfcv(mh.asMDoppler(), mh.asMDoppler().getRef()); for (uInt i=0; i (outRef.getType())).getValue()); } } return mh2rec(mhout); } Record MeasuresProxy::doptofreq(const Record& rec, const String& str, const Quantity& form) { MeasureHolder mh = rec2mh(rec); MeasureHolder mhout; MFrequency::Ref outRef; MFrequency tout; tout.giveMe(outRef, str); mhout = MeasureHolder(MFrequency:: fromDoppler(mh.asMDoppler(), MVFrequency(form), static_cast (outRef.getType()))); uInt nel(mh.nelements()); if (nel>0) { mhout.makeMV(nel); MDoppler::Convert mfcv(mh.asMDoppler(), mh.asMDoppler().getRef()); for (uInt i=0; i (outRef.getType())).getValue()); } } return mh2rec(mhout); } Record MeasuresProxy::todop(const Record& rec, const Quantity& form) { MeasureHolder mh = rec2mh(rec); MeasureHolder mhout; if (mh.isMRadialVelocity()) { mhout = MRadialVelocity::toDoppler(mh.asMeasure()); uInt nel(mh.nelements()); if (nel>0) { mhout.makeMV(nel); MRadialVelocity::Convert mfcv(mh.asMRadialVelocity(), mh.asMRadialVelocity().getRef()); for (uInt i=0; i0) { mhout.makeMV(nel); MFrequency::Convert mfcv(mh.asMFrequency(), mh.asMFrequency().getRef()); for (uInt i=0; i0) { mhout.makeMV(nel); MFrequency::Convert mfcv(val.asMFrequency(), val.asMFrequency().getRef()); MDoppler::Convert mdcv(arg.asMDoppler(), arg.asMDoppler().getRef()); for (uInt i=0; i& lst) { String out; if (lst.nelements() > 0) { // Note in next one the const throw away, since join does not accept // const String src[] Bool deleteIt; String *storage = const_cast(lst.getStorage(deleteIt)); const String *cstorage = storage; out = join(storage, lst.nelements(), String(" ")); lst.freeStorage(cstorage, deleteIt); } return out; } Vector MeasuresProxy::obslist() { return MeasTable::Observatories(); } Vector MeasuresProxy::srclist() { return MeasTable::Sources(); } Record MeasuresProxy::observatory(const String& str) { MPosition obs; if (!MeasTable::Observatory(obs, str)) { throw(AipsError("Unknown observatory asked for.")); } MeasureHolder mh(obs); return mh2rec(mh); } Record MeasuresProxy::source(const String& str) { MDirection src; if (!MeasTable::Source(src, str)) { throw(AipsError("Unknown source asked for.")); } MeasureHolder mh(src); return mh2rec(mh); } // Refmans says thsi is advanced use (Wim) only, so we ignore it /* // addev case 10: { Parameter val(parameters, valName, ParameterSet::In); Parameter > > returnval(parameters, returnvalName, ParameterSet::Out); if (runMethod) { Vector > res = val().asMeasure().getData()->getXRecordValue(); returnval().resize(IPosition()); returnval() = res; } } break; */ Record MeasuresProxy::alltyp(const Record& rec) { MeasureHolder mh = rec2mh(rec); Record outrec; Int nall, nex; const uInt *typ; const String *tall = mh.asMeasure().allTypes(nall, nex, typ); Vector tcod(nall-nex); Vector text(nex); for (Int i=0; i MeasuresProxy::linelist() { return MeasTable::Lines(); } Record MeasuresProxy::line(const String& str) { MFrequency line; if (!MeasTable::Line(line, str)) { throw(AipsError("Unknown line asked for.")); } MeasureHolder mh(line); return mh2rec(mh); } Quantum > MeasuresProxy::posangle(const Record& lrec, const Record& rrec) { MeasureHolder mhl = rec2mh(lrec); MeasureHolder mhr = rec2mh(rrec); MDirection x(mhl.asMDirection()); MDirection y(mhr.asMDirection()); x.getRefPtr()->set(frame_p); y.getRefPtr()->set(frame_p); if (x.isModel()) x = MDirection::Convert(x, MDirection::DEFAULT)(); if (y.isModel()) y = MDirection::Convert(y, MDirection::DEFAULT)(); if (x.getRef().getType() != y.getRef().getType()) { y = MDirection::Convert(y, MDirection::castType (x.getRef().getType()))(); } return \ Quantum >( Vector(1, x.getValue().positionAngle(y.getValue(), "deg").getValue()), "deg"); } Quantum > MeasuresProxy::separation(const Record& lrec, const Record& rrec) { MeasureHolder mhl = rec2mh(lrec); MeasureHolder mhr = rec2mh(rrec); MDirection x(mhl.asMDirection()); MDirection y(mhr.asMDirection()); x.getRefPtr()->set(frame_p); y.getRefPtr()->set(frame_p); if (x.isModel()) x = MDirection::Convert(x, MDirection::DEFAULT)(); if (y.isModel()) y = MDirection::Convert(y, MDirection::DEFAULT)(); if (x.getRef().getType() != y.getRef().getType()) { y = MDirection::Convert(y, MDirection::castType (x.getRef().getType()))(); } return \ Quantum >( Vector(1, x.getValue().separation(y.getValue(), "deg").getValue()), "deg"); } Record MeasuresProxy::uvw(const Record& mhrec) { Record outrec; MeasureHolder mhin = rec2mh(mhrec); MeasureHolder mhout; Vector res; Vector xres; String err; if (!toUvw(err, mhout, xres, res, mhin)) throw(AipsError(err)); Record r0; mhout.toRecord(err, r0); outrec.defineRecord("measure", r0); QuantumHolder qh0(Quantum >(res, "m/s")); QuantumHolder qh1(Quantum >(xres, "m")); Record r1, r2; qh0.toRecord(err, r1); qh1.toRecord(err, r2); outrec.defineRecord("dot", r1); outrec.defineRecord("xyz", r2); return outrec; } Record MeasuresProxy::expand(const Record& mhrec) { Record outrec; MeasureHolder mhin = rec2mh(mhrec); MeasureHolder mhout; Vector xres; String err; if (!expandIt(err, mhout, xres, mhin)) throw(AipsError(err)); QuantumHolder qh0(Quantum >(xres, "m")); Record r0, r1; mhout.toRecord(err, r0); qh0.toRecord(err, r1); outrec.defineRecord("measure", r0); outrec.defineRecord("xyz", r1); return outrec; } /* // framecomet case 14: { Parameter val(parameters, valName, ParameterSet::In); Parameter returnval(parameters, returnvalName, ParameterSet::Out); if (runMethod) returnval() = doframe(val()); } break; // cometname case 15: { Parameter returnval(parameters, returnvalName, ParameterSet::Out); if (runMethod) { if (pcomet_p) returnval() = pcomet_p->getName(); else return error("No Comet table present\n"); } } break; // comettopo case 16: { Parameter > returnval(parameters, returnvalName, ParameterSet::Out); if (runMethod) { if (pcomet_p && pcomet_p->getType() == MDirection::TOPO) { returnval() = pcomet_p->getTopo().getValue(); } else { return error("No Topocentric Comet table present\n"); } } } break; // comettype case 17: { Parameter returnval(parameters, returnvalName, ParameterSet::Out); if (runMethod) { if (pcomet_p) { if (pcomet_p->getType() == MDirection::TOPO) { returnval() = String("TOPO"); } else { returnval() = String("APP"); } } else { returnval() = String("none"); } } } break; */ casacore-3.7.1/measures/Measures/MeasuresProxy.h000066400000000000000000000066311476623553700217410ustar00rootroot00000000000000//# MeasuresProxy.h: This class gives a high-level interface to Measures //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MEASURESPROXY_H #define MEASURES_MEASURESPROXY_H //# Includes #include #include #include #include #include #include #include //# Forward declarations namespace casacore { //# NAMESPACE CASACORE - BEGIN class String; class MeasureHolder; class MeasComet; class MeasuresProxy { public: MeasuresProxy(); virtual ~MeasuresProxy(); Record measure(const Record& rec, const String& str, const Record& form); Bool doframe(const Record& rec); String dirshow(const Record& rec); Record doptorv(const Record& rec, const String& str); Record doptofreq(const Record& rec, const String& str, const Quantity& form); Record todop(const Record& rec, const Quantity& form); Record torest(const Record& rec, const Record& form); Vector obslist(); Vector srclist(); Vector linelist(); Record observatory(const String& str); Record source(const String& str); Record line(const String& str); Record alltyp(const Record& rec); Quantum > posangle(const Record& lrec, const Record& rrec); Quantum > separation(const Record& lrec, const Record& rrec); Record uvw(const Record& mhrec); Record expand(const Record& mhrec); private: String vec2str(const Vector& lst); Bool doFrame(const MeasureHolder &in); Bool doFrame(const String &in); Bool makeMeasure(String &error, MeasureHolder &out, const MeasureHolder &in, const String &outref, const Record &off); Bool toUvw(String &error, MeasureHolder &out, Vector &xyz, Vector &dot, const MeasureHolder &in); Bool expandIt(String &error, MeasureHolder &out, Vector &xyz, const MeasureHolder &in); MeasureHolder rec2mh(const Record& rec); Record mh2rec(const MeasureHolder& mh); static String getMeasureType(const Record &in); //# Data // The globally used MeasFrame for this DO MeasFrame frame_p; // The current comet class MeasComet *pcomet_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/Muvw.cc000066400000000000000000000162121476623553700202030ustar00rootroot00000000000000//# Muvw.cc: A Measure: uvw on Earth //# Copyright (C) 1998-2002,2004,2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors Muvw::Muvw() : MeasBase() {} Muvw::Muvw(const MVuvw &dt) : MeasBase(dt,Muvw::DEFAULT) {} Muvw::Muvw(const MVuvw &dt, const Muvw::Ref &rf) : MeasBase(dt,rf) {} Muvw::Muvw(const MVuvw &dt, Muvw::Types rf) : MeasBase(dt,rf) {} Muvw::Muvw(const Measure *dt) : MeasBase(dt) {} Muvw::Muvw(const MeasValue *dt) : MeasBase(*(MVuvw*)dt, Muvw::DEFAULT) {} Muvw::Muvw(const Muvw &other) : MeasBase (other) {} Muvw &Muvw::operator=(const Muvw &other) { if (this != &other) { MeasBase &This = *this; const MeasBase &Other = other; This = Other; } return *this; } //# Destructor Muvw::~Muvw() {} //# Operators //# Member functions const String &Muvw::tellMe() const { return Muvw::showMe(); } const String &Muvw::showMe() { static const String name("uvw"); return name; } void Muvw::assure(const Measure &in) { if (!dynamic_cast(&in)) { throw(AipsError("Illegal Measure type argument: " + Muvw::showMe())); } } Muvw::Types Muvw::castType(uInt tp) { Muvw::checkMyTypes(); AlwaysAssert(tp < Muvw::N_Types, AipsError); return static_cast(tp); } const String &Muvw::showType(Muvw::Types tp) { static const String tname[Muvw::N_Types] = { "J2000", "JMEAN", "JTRUE", "APP", "B1950", "B1950_VLA", "BMEAN", "BTRUE", "GALACTIC", "HADEC", "AZEL", "AZELSW", "AZELGEO", "AZELSWGEO", "JNAT", "ECLIPTIC", "MECLIPTIC", "TECLIPTIC", "SUPERGAL", "ITRF", "TOPO", "ICRS" }; Muvw::checkMyTypes(); return tname[tp]; } const String &Muvw::showType(uInt tp) { return Muvw::showType(Muvw::castType(tp)); } const String* Muvw::allMyTypes(Int &nall, Int &nextra, const uInt *&typ) { static const Int N_name = 24; static const Int N_extra = 0; static const String tname[N_name] = { "J2000", "JMEAN", "JTRUE", "APP", "B1950", "B1950_VLA", "BMEAN", "BTRUE", "GALACTIC", "HADEC", "AZEL", "AZELSW", "AZELNE", "AZELGEO", "AZELSWGEO", "AZELNEGEO", "JNAT", "ECLIPTIC", "MECLIPTIC", "TECLIPTIC", "SUPERGAL", "ITRF", "TOPO", "ICRS" }; static const uInt oname[N_name] = { Muvw::J2000, Muvw::JMEAN, Muvw::JTRUE, Muvw::APP, Muvw::B1950, Muvw::B1950_VLA, Muvw::BMEAN, Muvw::BTRUE, Muvw::GALACTIC, Muvw::HADEC, Muvw::AZEL, Muvw::AZELSW, Muvw::AZEL, Muvw::AZELGEO, Muvw::AZELSWGEO, Muvw::AZELGEO, Muvw::JNAT, Muvw::ECLIPTIC, Muvw::MECLIPTIC, Muvw::TECLIPTIC, Muvw::SUPERGAL, Muvw::ITRF, Muvw::TOPO, Muvw::ICRS }; Muvw::checkMyTypes(); nall = N_name; nextra = N_extra; typ = oname; return tname; } const String* Muvw::allTypes(Int &nall, Int &nextra, const uInt *&typ) const { return Muvw::allMyTypes(nall, nextra, typ); } void Muvw::checkTypes() const { Muvw::checkMyTypes(); } void Muvw::checkMyTypes() { // Multiple threads could execute this, but that is harmless. static Bool first(True); if (first) { first = False; Int nall, nex; const uInt *typ; const String *const tps = Muvw::allMyTypes(nall,nex, typ); Muvw::Types tp; for (Int i=0; i(Muvw::N_Types) == static_cast(MDirection::N_Types), AipsError); for (Int i=0; i(static_cast(in)); } MDirection::Types Muvw::toDirType(const Muvw::Types in) { Muvw::checkMyTypes(); return static_cast(static_cast(in)); } Bool Muvw::getType(Muvw::Types &tp, const String &in) { const uInt *oname; Int nall, nex; const String *tname = Muvw::allMyTypes(nall, nex, oname); Int i = Measure::giveMe(in, nall, tname); if (i>=nall) return False; else tp = static_cast(oname[i]); return True; } Bool Muvw::giveMe(Muvw::Ref &mr, const String &in) { Muvw::Types tp; if (Muvw::getType(tp, in)) mr = Muvw::Ref(tp); else { mr = Muvw::Ref(); return False; } return True; } Bool Muvw::setOffset(const Measure &in) { if (!dynamic_cast(&in)) return False; ref.set(in); return True; } Bool Muvw::setRefString(const String &in) { Muvw::Types tp; if (Muvw::getType(tp, in)) { ref.setType(tp); return True; } ref.setType(Muvw::DEFAULT); return False; } const String &Muvw::getDefaultType() const { return Muvw::showType(Muvw::DEFAULT); } String Muvw::getRefString() const { return Muvw::showType(ref.getType()); } Quantum > Muvw::get(const Unit &inunit) const { Vector x; x = data.getValue(); return Quantum >(x, "m").get(inunit); } Quantum > Muvw::getAngle() const { return (data.getAngle()); } Quantum > Muvw::getAngle(const Unit &inunit) const { return (data.getAngle(inunit)); } Measure *Muvw::clone() const { return (new Muvw(*this)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/Muvw.h000066400000000000000000000205351476623553700200500ustar00rootroot00000000000000//# Muvw.h: A Measure: uvw on Earth //# Copyright (C) 1998-2000,2002,2004,2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_MUVW_H #define MEASURES_MUVW_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class Muvw; class MCuvw; class MDirection; template class MeasConvert; template class ArrayMeasColumn; template class ScalarMeasColumn; //# Typedefs // A Measure: uvw on Earth // // // // //
      • Measure class // // // // From Measure and uvw // // // // Muvw is the derived Measure class for an interferometer uvw. // uvws can be given in any of the direction types, or as ITRF, the // IERS base.
        // Note that at the moment no correction for Earth tides (error <~ 0.05 mm/km // EW uvw), plate motion (not relevant for telescopes on same plate) and // relativistic effects are incorporated. B1950 has the same caveat as in // MDirection. //
        // // // // // Specify an Epoch and a telescope position // MEpoch tbm(Quantity(50927.92931, "d")); // MPosition pos(MVPosition(-4750915.84032, 2792906.17778, // -3200483.75028), // MPosition::ITRF); // // Use them in a frame // MeasFrame mf(tbm, pos); // // Specify an uvw (note that values here are in m) // MVuvw mvb0(100 ,10, 0); // cout << "uvw: " << mvb0 << endl; // // Specify a reference (type and where and when) for the following uvw // Muvw::Ref mbref0(Muvw::ITRF, mf); // Muvw mb0(mvb0, mbref0); // // Show the uvw // cout << "uvw: " << mb0 << endl; // cout << "uvw reference: " << mbref0 << endl; // // Another reference // Muvw::Ref mbref1(Muvw::J2000); // cout << "uvw reference: " << mbref1 << endl; // // Convert the uvw coordinates to the other reference and show it // cout << "Test uvw conversion ..." << endl; // Muvw::Convert bconv(mb0, mbref1); // cout << "Converted " << mb0 << endl << // " to " << mbref1 << endl << // " as " << bconv() << endl; // // // // // To be able to handle conversions between uvw coordinates with different // reference directions. // // // //
      • EW baselines // class Muvw : public MeasBase > { public: //# Friends // Conversion of data friend class MeasConvert; //# Enumerations // Types of known Muvws // // The order defines the order in the translation matrix FromTo // in the getConvert routine. Do not change the order without // changing the array. Additions should be made before N_types, and // an additional row and column should be coded in FromTo, and // in showType(). enum Types { J2000, JMEAN, JTRUE, APP, B1950, B1950_VLA, BMEAN, BTRUE, GALACTIC, HADEC, AZEL, AZELSW, AZELGEO, AZELSWGEO, JNAT, ECLIPTIC, MECLIPTIC, TECLIPTIC, SUPERGAL, ITRF, TOPO, ICRS, N_Types, // Defaults DEFAULT=ITRF, // Synonyms AZELNE=AZEL, AZELNEGEO=AZELGEO }; //# Typedefs // Measure value container for this class (i.e. Muvw::MVType) typedef MVuvw MVType; // Measure conversion routines for this class (i.e. Muvw::MCType) typedef MCuvw MCType; // Measure reference (i.e. Muvw::Ref) typedef MeasRef Ref; // MeasConvert use (i.e. Muvw::Convert) typedef MeasConvert Convert; // Measure table Columns (e.g., Muvw::ScalarColumn) typedef ScalarMeasColumn ScalarColumn; typedef ArrayMeasColumn ArrayColumn; //# Constructors // In the following constructors and other functions, all // MeasRef can be replaced with simple Measure::TYPE // where no offsets or frames are needed in the reference. // Default constructor; generates the ITRF centre Muvw(); // Create from data and reference // Muvw(const MVuvw &dt); Muvw(const MVuvw &dt, const Muvw::Ref &rf); Muvw(const MVuvw &dt, Muvw::Types rf); Muvw(const Measure *dt); Muvw(const MeasValue *dt); // // Copy constructor and assign // Muvw(const Muvw &); Muvw &operator=(const Muvw &); // //# Destructor virtual ~Muvw(); //# Operators //# General Member Functions // Tell me your type // virtual const String &tellMe() const; static const String &showMe(); static void assure(const Measure &in); // // Translate reference code. The uInt version has a check for valid codes // (i.e. it is a safe cast). // //
      • AipsError in the uInt interface if illegal code given // // static Muvw::Types castType(uInt tp); static const String &showType(Muvw::Types tp); static const String &showType(uInt tp); // // Translate string to reference code // static Bool getType(Muvw::Types &tp, const String &in); Bool giveMe(Muvw::Ref &mr, const String &in); // // Set the offset in the reference (False if non-matching Measure) virtual Bool setOffset(const Measure &in); // Set the reference type to the specified String. False if illegal // string, reference set to DEFAULT. virtual Bool setRefString(const String &in); // Get the default reference type virtual const String &getDefaultType() const; // Get a list of all known reference codes. nall returns the number in list, // nextra the number of specials (like planets) that should be at // end of list). typ returns the list of corresponding types. // virtual const String* allTypes(Int &nall, Int &nextra, const uInt *&typ) const; static const String* allMyTypes(Int &nall, Int &nextra, const uInt *&typ); // // Check if all internal tables of types (both enum and String) are // complete and correct. This function is called automatically if and when // necessary. // //
      • AipsError if a (programming) error in the types. // // virtual void checkTypes() const; static void checkMyTypes(); // // Get the correct Muvw type from a given direction type (or v.v.) // static Muvw::Types fromDirType(const MDirection::Types in); static MDirection::Types toDirType(const Muvw::Types in); // // Get the reference type (for records, including codes like R_) virtual String getRefString() const; // Get Measure data // Quantum > get(const Unit &inunit) const; Quantum > getAngle() const; Quantum > getAngle(const Unit &inunit) const; // // Make copy // virtual Measure *clone() const; // private: //# Enumerations //# Data //# Member functions }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/Nutation.cc000066400000000000000000000374501476623553700210550ustar00rootroot00000000000000//# Nutation.cc: Nutation class //# Copyright (C) 1995-1999,2002,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constants const Double Nutation::INTV = 0.04; //# Static data uInt Nutation::myInterval_reg = 0; uInt Nutation::myUseiers_reg = 0; uInt Nutation::myUsejpl_reg = 0; //# Constructors Nutation::Nutation() : method_p(Nutation::STANDARD), lres_p(0) { fill(); } Nutation::Nutation(const Nutation &other) { copy(other); } Nutation::Nutation(NutationTypes type) : method_p(type), lres_p(0) { fill(); } Nutation &Nutation::operator=(const Nutation &other) { if (this != &other) copy(other); return *this; } void Nutation::init() { method_p = Nutation::STANDARD; fill(); } void Nutation::init(NutationTypes type) { method_p = type; fill(); } void Nutation::copy(const Nutation &other) { method_p = other.method_p; checkEpoch_p = other.checkEpoch_p; checkDerEpoch_p = other.checkDerEpoch_p; eqeq_p = other.eqeq_p; deqeq_p = other.deqeq_p; neval_p = other.neval_p; deval_p = other.deval_p; for (uInt i=0; i<3; i++) { nval_p[i] = other.nval_p[i]; dval_p[i] = other.dval_p[i]; } for (Int j=0; j<4; j++) { result_p[j] = other.result_p[j]; } } //# Destructor Nutation::~Nutation() {} //# Operators // Calculate Nutation Euler angles const Euler &Nutation::operator()(Double epoch) { calcNut(epoch); lres_p++; lres_p %= 4; for (uInt i=0; i<3; ++i) result_p[lres_p](i) = nval_p[i]; Double dt = epoch - checkEpoch_p; if (dt != 0) { for (uInt i=0; i<3; ++i) result_p[lres_p](i) += dt*dval_p[i]; } return result_p[lres_p]; } //# Member functions const Euler &Nutation::derivative(Double epoch) { calcNut(epoch, True); lres_p++; lres_p %= 4; for (uInt i=0; i<3; i++) result_p[lres_p](i) = dval_p[i]; return result_p[lres_p]; } void Nutation::fill() { checkEpoch_p = 1e30; checkDerEpoch_p = 1e30; for (uInt i=0; i<4; i++) result_p[i].set(1,3,1); // Get interval and other switches if (!Nutation::myInterval_reg) { myInterval_reg = AipsrcValue::registerRC(String("measures.nutation.d_interval"), Unit("d"), Unit("d"), Nutation::INTV); } if (!Nutation::myUseiers_reg) { myUseiers_reg = AipsrcValue::registerRC(String("measures.nutation.b_useiers"), False); } if (!Nutation::myUsejpl_reg) { myUsejpl_reg = AipsrcValue::registerRC(String("measures.nutation.b_usejpl"), False); } } void Nutation::refresh() { checkEpoch_p = 1e30; checkDerEpoch_p = 1e30; } Double Nutation::eqox(Double epoch) { calcNut(epoch); Double dt = epoch - checkEpoch_p; if (dt == 0) return eqeq_p; return (eqeq_p + dt*deqeq_p); } Double Nutation::derivativeEqox(Double epoch) { calcNut(epoch, True); return deqeq_p; } Double Nutation::eqoxCT(Double epoch) { calcNut(epoch); Double dt = epoch - checkEpoch_p; if (dt == 0) return neval_p; return neval_p + dt*deval_p; } Double Nutation::derivativeEqoxCT(Double epoch) { calcNut(epoch, True); return deval_p; } Quantity Nutation::getEqoxAngle(Double epoch) { return Quantity(eqox(epoch),"rad"); } Quantity Nutation::getEqoxAngle(Double epoch, const Unit &unit) { return Quantity(eqox(epoch),"rad").get(unit); } void Nutation::calcNut(Double time, Bool calcDer) { // Calculate the nutation value at epoch Double t = time; Double epsilon = 1e-6; if (!calcDer) { epsilon = AipsrcValue::get(Nutation::myInterval_reg); } Bool renew = False; if (!nearAbs(time, checkEpoch_p, epsilon)) { checkEpoch_p = time; renew = True; Double dEps = 0; Double dPsi = 0; switch (method_p) { case B1950: t = (t - MeasData::MJDB1900)/MeasData::JDCEN; break; case IAU2000A: case IAU2000B: t = (t - MeasData::MJD2000)/MeasData::JDCEN; break; default: if (AipsrcValue::get(Nutation::myUseiers_reg)) { dPsi = MeasTable::dPsiEps(0, t); dEps = MeasTable::dPsiEps(1, t); } t = (t - MeasData::MJD2000)/MeasData::JDCEN; break; } Vector fa(5), dfa(5); Vector pfa(14), pdfa(14); Double dtmp; nval_p[1] = Double(0); nval_p[2] = Double(0); neval_p = 0; switch (method_p) { case B1950: { nval_p[0] = MeasTable::fundArg1950(0)(t); //eps0 for (uInt i=0; i<5; i++) { fa(i) = MeasTable::fundArg1950(i+1)(t); } std::shared_ptr> mul(MeasTable::mulSC1950(t, 1e-6)); for (uInt i=0; i<69; i++) { dtmp = 0; for (uInt j=0; j<5; j++) { dtmp += MeasTable::mulArg1950(i)[j] * fa[j]; } nval_p[1] += (*mul)(0,i) * sin(dtmp); nval_p[2] += (*mul)(1,i) * cos(dtmp); } } break; case IAU2000B: { nval_p[0] = MeasTable::fundArg2000(0)(t); //eps0 for (uInt i=0; i<5; i++) { fa(i) = MeasTable::fundArg2000(i+1)(t); } std::shared_ptr> mul(MeasTable::mulSC2000B(t, 1e-6)); for (Int i=76; i>=0; --i) { dtmp = 0; for (uInt j=0; j<5; j++) { dtmp += MeasTable::mulArg2000B(i)[j] * fa[j]; } nval_p[1] += (*mul)(0,i) * sin(dtmp); nval_p[2] += (*mul)(1,i) * cos(dtmp); nval_p[1] += (*mul)(4,i) * cos(dtmp); nval_p[2] += (*mul)(5,i) * sin(dtmp); } // Add an average for missing planetary precession terms nval_p[2] += 0.388e0 * C::arcsec*1e-3; nval_p[1] -= 0.135e0 * C::arcsec*1e-3; } break; case IAU2000A: { nval_p[0] = MeasTable::fundArg2000(0)(t); //eps0 for (uInt i=0; i<5; i++) { fa(i) = MeasTable::fundArg2000(i+1)(t); } std::shared_ptr> mul(MeasTable::mulSC2000A(t, 1e-6)); for (Int i=677; i>=0; --i) { dtmp = 0; for (uInt j=0; j<5; j++) { dtmp += MeasTable::mulArg2000A(i)[j] * fa[j]; } nval_p[1] += (*mul)(0,i) * sin(dtmp); nval_p[2] += (*mul)(1,i) * cos(dtmp); nval_p[1] += (*mul)(4,i) * cos(dtmp); nval_p[2] += (*mul)(5,i) * sin(dtmp); } for (uInt i=0; i<14; i++) { pfa(i) = MeasTable::planetaryArg2000(i)(t); } for (Int i=686; i>=0; --i) { dtmp = 0; for (uInt j=0; j<14; j++) { dtmp += MeasTable::mulPlanArg2000A(i)[j] * pfa[j]; } nval_p[1] += C::arcsec*1e-7 * (MeasTable::mulPlanSC2000A(i)[0] * sin(dtmp) + MeasTable::mulPlanSC2000A(i)[1] * cos(dtmp)); nval_p[2] += C::arcsec*1e-7 * (MeasTable::mulPlanSC2000A(i)[2] * sin(dtmp) + MeasTable::mulPlanSC2000A(i)[3] * cos(dtmp)); } } break; default: nval_p[0] = MeasTable::fundArg(0)(t); //eps0 if (AipsrcValue::get(Nutation::myUsejpl_reg)) { Vector mypl = MeasTable::Planetary(MeasTable::NUTATION, checkEpoch_p); nval_p[1] = mypl[0]; nval_p[2] = mypl[1]; } else { for (uInt i=0; i<5; i++) { fa(i) = MeasTable::fundArg(i+1)(t); } std::shared_ptr> mul(MeasTable::mulSC(t, 1e-6)); for (uInt i=0; i<106; i++) { dtmp = 0; for (uInt j=0; j<5; j++) { dtmp += MeasTable::mulArg(i)[j] * fa[j]; } nval_p[1] += (*mul)(0,i) * sin(dtmp); nval_p[2] += (*mul)(1,i) * cos(dtmp); } } /// cout <<"nval_p="<< nval_p[0]<<' '<=0; --i) { dtmp = 0; for (uInt j=0; j<14; j++) { dtmp += MeasTable::mulArgEqEqCT2000(i)[j] * pfa[j]; } neval_p += MeasTable::mulSCEqEqCT2000(i)[0] * sin(dtmp); neval_p += MeasTable::mulSCEqEqCT2000(i)[1] * cos(dtmp); } dtmp = 0; for (uInt j=0; j<14; j++) { dtmp += MeasTable::mulArgEqEqCT2000(33)[j] * pfa[j]; } neval_p += t*MeasTable::mulSCEqEqCT2000(33)[0] * sin(dtmp); neval_p += t*MeasTable::mulSCEqEqCT2000(33)[1] * cos(dtmp); neval_p *= C::arcsec; eqeq_p += neval_p; break; default: break; } } if ((renew && calcDer) || (!renew && calcDer && checkEpoch_p != checkDerEpoch_p) || (!renew && !calcDer && t != checkEpoch_p && checkEpoch_p != checkDerEpoch_p)) { t = checkEpoch_p; checkDerEpoch_p = t; switch (method_p) { case B1950: t = (t - MeasData::MJDB1900)/MeasData::JDCEN; break; case IAU2000A: case IAU2000B: t = (t - MeasData::MJD2000)/MeasData::JDCEN; break; default: t = (t - MeasData::MJD2000)/MeasData::JDCEN; break; } Vector fa(5), dfa(5); Vector pfa(14), pdfa(14); Double dtmp, ddtmp; dval_p[1] = Double(0); dval_p[2] = Double(0); deval_p = 0; switch (method_p) { case B1950: { dval_p[0] = (MeasTable::fundArg1950(0).derivative())(t); for (uInt i=0; i<5; i++) { fa(i) = MeasTable::fundArg1950(i+1)(t); dfa(i) = (MeasTable::fundArg1950(i+1).derivative())(t); } std::shared_ptr> mul(MeasTable::mulSC1950(t, 1e-6)); for (uInt i=0; i<69; i++) { dtmp = ddtmp = 0; for (uInt j=0; j<5; j++) { dtmp += MeasTable::mulArg1950(i)[j] * fa[j]; ddtmp += MeasTable::mulArg1950(i)[j] * dfa[j]; } dval_p[1] += (*mul)(2,i) * sin(dtmp) + (*mul)(0,i) * cos(dtmp) * ddtmp; dval_p[2] += (*mul)(3,i) * cos(dtmp) - (*mul)(1,i) * sin(dtmp) * ddtmp; } } break; case IAU2000B: { dval_p[0] = (MeasTable::fundArg2000(0).derivative())(t)/MeasData::JDCEN; for (uInt i=0; i<5; i++) { fa(i) = MeasTable::fundArg2000(i+1)(t); dfa(i) = (MeasTable::fundArg2000(i+1).derivative())(t); } std::shared_ptr> mul(MeasTable::mulSC2000B(t, 1e-6)); for (Int i=76; i>=0; --i) { dtmp = ddtmp = 0; for (uInt j=0; j<5; j++) { dtmp += MeasTable::mulArg2000B(i)[j] * fa[j]; ddtmp += MeasTable::mulArg2000B(i)[j] * dfa[j]; } dval_p[1] += (*mul)(2,i) * sin(dtmp) + (*mul)(0,i) * cos(dtmp) * ddtmp; dval_p[2] += (*mul)(3,i) * cos(dtmp) - (*mul)(1,i) * sin(dtmp) * ddtmp; } } // Add an average for missing planetary precession terms break; case IAU2000A: { dval_p[0] = (MeasTable::fundArg2000(0).derivative())(t)/MeasData::JDCEN; for (uInt i=0; i<5; i++) { fa(i) = MeasTable::fundArg2000(i+1)(t); dfa(i) = (MeasTable::fundArg2000(i+1).derivative())(t); } std::shared_ptr> mul(MeasTable::mulSC2000A(t, 1e-6)); for (Int i=677; i>=0; --i) { dtmp = ddtmp = 0; for (uInt j=0; j<5; j++) { dtmp += MeasTable::mulArg2000A(i)[j] * fa[j]; ddtmp += MeasTable::mulArg2000A(i)[j] * dfa[j]; } dval_p[1] += (*mul)(2,i) * sin(dtmp) + (*mul)(0,i) * cos(dtmp) * ddtmp; dval_p[2] += (*mul)(3,i) * cos(dtmp) - (*mul)(1,i) * sin(dtmp) * ddtmp; } for (uInt i=0; i<14; i++) { pfa(i) = MeasTable::planetaryArg2000(i)(t); pdfa(i) = (MeasTable::planetaryArg2000(i).derivative())(t); } for (Int i=686; i>=0; --i) { dtmp = ddtmp = 0; for (uInt j=0; j<14; j++) { dtmp += MeasTable::mulPlanArg2000A(i)[j] * pfa[j]; ddtmp += MeasTable::mulPlanArg2000A(i)[j] * pdfa[j]; } dval_p[1] += C::arcsec*1e-7 * (MeasTable::mulPlanSC2000A(i)[0] * cos(dtmp) - MeasTable::mulPlanSC2000A(i)[1] * sin(dtmp) * ddtmp); dval_p[2] += C::arcsec*1e-7 * (MeasTable::mulPlanSC2000A(i)[2] * cos(dtmp) - MeasTable::mulPlanSC2000A(i)[3] * sin(dtmp) * ddtmp); } } break; default: dval_p[0] = (MeasTable::fundArg(0).derivative())(t)/MeasData::JDCEN; if (AipsrcValue::get(Nutation::myUsejpl_reg)) { Vector mypl = MeasTable::Planetary(MeasTable::NUTATION, checkEpoch_p); dval_p[1] = mypl[2]*MeasData::JDCEN; dval_p[2] = mypl[3]*MeasData::JDCEN; } else { for (uInt i=0; i<5; i++) { fa(i) = MeasTable::fundArg(i+1)(t); dfa(i) = (MeasTable::fundArg(i+1).derivative())(t); } std::shared_ptr> mul(MeasTable::mulSC(t, 1e-6)); for (uInt i=0; i<106; i++) { dtmp = ddtmp = 0; for (uInt j=0; j<5; j++) { dtmp += MeasTable::mulArg(i)[j] * fa[j]; ddtmp += MeasTable::mulArg(i)[j] * dfa[j]; } dval_p[1] += (*mul)(2,i) * sin(dtmp) + (*mul)(0,i) * cos(dtmp) * ddtmp; dval_p[2] += (*mul)(3,i) * cos(dtmp) - (*mul)(1,i) * sin(dtmp) * ddtmp; } } /// cout <<"dval_p="<< dval_p[0]<<' '<=0; --i) { dtmp = ddtmp = 0; for (uInt j=0; j<14; j++) { dtmp += MeasTable::mulArgEqEqCT2000(i)[j] * pfa[j]; ddtmp += MeasTable::mulPlanArg2000A(i)[j] * pdfa[j]; } deval_p += MeasTable::mulSCEqEqCT2000(i)[0] * cos(dtmp) - MeasTable::mulSCEqEqCT2000(i)[1] * sin(dtmp) * ddtmp; } dtmp = ddtmp = 0; for (uInt j=0; j<14; j++) { dtmp += MeasTable::mulArgEqEqCT2000(33)[j] * pfa[j]; ddtmp += MeasTable::mulPlanArg2000A(33)[j] * pdfa[j]; } deval_p += MeasTable::mulSCEqEqCT2000(33)[0] * cos(dtmp) - MeasTable::mulSCEqEqCT2000(33)[1] * sin(dtmp) * ddtmp; eqeq_p += neval_p; /// deqeq_p += deval_p*...; break; default: break; } } } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/Nutation.h000066400000000000000000000201351476623553700207070ustar00rootroot00000000000000//# Nutation.h: Nutation class //# Copyright (C) 1995,1996,1997,1998,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_NUTATION_H #define MEASURES_NUTATION_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // Nutation class and calculations // // // // //
      • Measure class //
      • Euler //
      • MeasData class for constants // // // // Nutation // // // // Nutation forms the class for Nutation calculations. It is a simple // container with the selected method, and the mean epoch. // It acts as a cache // for values and their derivatives, to enable fast calculations for time // epochs close together (see the aipsrc variable // measures.nutation.d_interval). // // The calculation method is selected from one of the following: //
          //
        • Nutation::STANDARD (at 1995/09/04 the IAU1980 definition, // from 2004/01/01 IAU2000B) //
        • Nutation::NONE (nutation of zero returned) //
        • Nutation::IAU1980 //
        • Nutation::B1950 //
        • Nutation::IAU2000 //
        • Nutation::IAU2000A (equal to the full precision (uas) IAU2000) //
        • Nutation::IAU2000B (official lower precision (mas) of IAU2000) //
        // Epochs can be specified as the MJD (with defined constants MeasData::MJD2000 // and MeasData::MJDB1950 or the actual MJD), // leading to the following constructors: //
          //
        • Nutation() default; assuming STANDARD //
        • Nutation(method) //
        // Actual Nutation for a certain Epoch is calculated by the () operator // as Nutation(epoch), with epoch Double MJD. Values are returned as an // Euler. // The derivative (d-1) can be obtained as well by // derivative(epoch).
        // A Nutation can be re-initialed with a different method and/or zero // epoch with the init() functions (same format as constructors). // To bypass the full calculation actual returned values are calculated // using the derivative if within about 2 hours (error less than about // 10-5 mas). A call to refresh() will re-initiate calculations // from scratch.
        // The following details can be set with the // Aipsrc mechanism: //
          //
        • measures.nutation.d_interval: approximation interval as time // (fraction of days is default unit) over which linear approximation // is used (default 0.04d (about 1 hour)). //
        • measures.nutation.b_usejpl: use the JPL database nutations for // IAU1980. // Else analytical expression, relative error about 10-9 // Note that the JPL database to be used can be set with // measures.jpl.ephemeris (at the moment of writing DE200 // (default), or DE405) //
        • measures.nutation.b_useiers: use the IERS Database nutation // corrections for IAU1980 (default False) //
        //
        // // // // #include // MVDirection pos(Quantity(10,"degree"),Quantity(-10.5,"degree")); // // direction RA=10; DEC=-10.5 // Nutation mine(Nutation::IAU1980); // define nutation type // RotMatrix rotat(mine(45837.0)); // rotation matrix for 84/05/17 // MVDirection new = rotat*pos; // apply nutation // // The normal way to use Nutation is by using the // MeasConvert class. // // // // To calculate the Nutation angles. An alternate route could have been // a global function, but having a simple container allows caching of some // calculations for speed.
        // Using MJD (JD-2400000.5) rather than JD is for precision reasons. //
        // // //
      • Correct deval_p (derivative complimentary eqox terms) //
      • Improve speed by a bit more lazyness in derivative calculations // and separate eqox calculations // class Nutation { public: //# Constants // Interval to be used for linear approximation (in days) static const Double INTV; //# Enumerations // Types of known Nutation calculations (at 1995/09/04 STANDARD == IAU1980, // after 2004/01/01 it will be IAU2000B)) enum NutationTypes { NONE, IAU1980, B1950, IAU2000A, IAU2000B, IAU2000 = IAU2000A, STANDARD = IAU1980 }; //# Constructors // Default constructor, generates default J2000 Nutation identification Nutation(); // Copy constructor Nutation(const Nutation &other); // Constructor with type explicit Nutation(NutationTypes type); // Copy assignment Nutation &operator=(const Nutation &other); //# Destructor ~Nutation(); //# Operators // Return the Nutation angles const Euler &operator()(Double epoch); //# General Member Functions // Return derivative of Nutation (d-1) const Euler &derivative(Double epoch); // Re-initialise Nutation object // void init(); void init(NutationTypes type); // // Refresh calculations void refresh(); // Get the equation of equinox // Double eqox(Double epoch) ; Quantity getEqoxAngle(Double epoch); Quantity getEqoxAngle(Double epoch, const Unit &unit) ; // // Get the derivative of the equation of equinoxes in d-1 Double derivativeEqox(Double epoch); // Get the complimentary terms of the equation of equinoxes Double eqoxCT(Double epoch); // Get the derivative of the complimentary terms of the equation of equinoxes Double derivativeEqoxCT(Double epoch); private: //# Data members // Method to be used NutationTypes method_p; // Check epoch for linear approximation Double checkEpoch_p; // Check epoch for calculation of derivatives Double checkDerEpoch_p; // Cached calculated angles Double nval_p[3]; // Cached derivatives Double dval_p[3]; // Cached equation of equinoxes Double eqeq_p; // Cached derivative equation of equinoxes Double deqeq_p; // Cached complimentary terms equation of equinoxes Double neval_p; // Cached derivative of complimentary terms equation of equinoxes Double deval_p; // To be able to use references rather than copies, and also to use these // references in simple (up to 4 terms of Nutation results) expressions, // results are calculated in circulating buffer Int lres_p; // Last calculation Euler result_p[4]; // Interpolation interval static uInt myInterval_reg; // IERS use static uInt myUseiers_reg; // JPL use static uInt myUsejpl_reg; //# Member functions // Make a copy void copy(const Nutation &other); // Fill an empty copy void fill(); // Calculate Nutation angles for time t; also derivatives if True given void calcNut(Double t, Bool calcDer = False); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/ParAngleMachine.cc000066400000000000000000000166751476623553700222400ustar00rootroot00000000000000//# ParAngleMachine.cc: Converts a direction into parallactic angle //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors ParAngleMachine::ParAngleMachine() : indir_p(0), convdir_p(0), frame_p(0), zenith_p(), mvdir_p(), lastep_p(-1.1e20), defintvl_p(0.04), intvl_p(0) { init(); } ParAngleMachine::ParAngleMachine(const MDirection &in) : indir_p(new MDirection(in)), convdir_p(0), frame_p(0), zenith_p(), mvdir_p(), lastep_p(-1.1e20), defintvl_p(0.04), intvl_p(0) { init(); } ParAngleMachine::ParAngleMachine(const ParAngleMachine &other) : indir_p(0), convdir_p(0), frame_p(0), zenith_p(), mvdir_p(), lastep_p(-1.1e20), defintvl_p(0.04), intvl_p(0) { if (other.indir_p) indir_p = new MDirection(*other.indir_p); if (other.frame_p) frame_p = new MeasFrame(*other.frame_p); defintvl_p = other.defintvl_p; init(); } ParAngleMachine &ParAngleMachine::operator=(const ParAngleMachine &other) { if (this != &other) { delete indir_p; indir_p = 0; delete convdir_p; convdir_p = 0; delete frame_p; frame_p = 0; if (other.indir_p) indir_p = new MDirection(*other.indir_p); if (other.frame_p) frame_p = new MeasFrame(*other.frame_p); defintvl_p = other.defintvl_p; init(); } return *this; } ParAngleMachine::~ParAngleMachine() { delete indir_p; indir_p = 0; delete convdir_p; convdir_p = 0; delete frame_p; frame_p = 0; } //# Operators Double ParAngleMachine::posAngle(const Quantum &ep) const { if (!convdir_p) initConv(); frame_p->resetEpoch(ep); return calcAngle(ep.getValue()); } Vector ParAngleMachine::posAngle(const Quantum > &ep) const { uInt nel(ep.getValue().nelements()); Vector res(nel); for (uInt i=0; iresetEpoch(ep); return calcAngle(ep); } Vector ParAngleMachine::posAngle(const Vector &ep) const { uInt nel(ep.nelements()); Vector res(nel); for (uInt i=0; i ParAngleMachine::operator()(const Quantum &ep) const { static const Unit un("rad"); return Quantity(posAngle(ep), un); } Quantum ParAngleMachine::operator()(const MVEpoch &ep) const { static const Unit un("rad"); return Quantity(posAngle(ep.get()), un); } Quantum ParAngleMachine::operator()(const MEpoch &ep) const { static const Unit un("rad"); return Quantity(posAngle(ep.getValue().get()), un); } Quantum > ParAngleMachine::operator()(const Quantum > &ep) const { static const Unit un("rad"); return Quantum >(posAngle(ep), un); } Quantum > ParAngleMachine::operator()(const Vector &ep) const { static const Unit un("rad"); uInt nel(ep.nelements()); Vector res(nel); for (uInt i=0; i >(res, un); } Double ParAngleMachine::operator()(const Double &ep) const { return posAngle(ep); } Vector ParAngleMachine::operator()(const Vector &ep) const { uInt nel(ep.nelements()); Vector res(nel); for (uInt i=0; i > ParAngleMachine::operator()(const Vector &ep) const { static const Unit un("rad"); uInt nel(ep.nelements()); Vector res(nel); for (uInt i=0; i >(res, un); } //# Member functions void ParAngleMachine::set(const MDirection &in) { delete indir_p; indir_p = 0; delete convdir_p; convdir_p = 0; indir_p = new MDirection(in); if (!in.getRef().getFrame().empty()) { delete frame_p; frame_p = 0; } init(); } void ParAngleMachine::set(const MeasFrame &frame) { delete convdir_p; convdir_p = 0; delete frame_p; frame_p = 0; frame_p = new MeasFrame(frame); init(); } void ParAngleMachine::setInterval(const Double ttime) { defintvl_p = fabs(ttime); if (indir_p && indir_p->isModel()) defintvl_p = 0; intvl_p = defintvl_p; } void ParAngleMachine::init() { if (indir_p) { if (!frame_p) set(indir_p->getRef().getFrame()); if (indir_p->isModel()) defintvl_p = 0; } } void ParAngleMachine::initConv() const { if (!indir_p) throw(AipsError("A ParAngleMachine must have a Direction")); if (!frame_p->epoch() || !frame_p->position()) { throw(AipsError("A ParAngle Machine has no frame, or a frame without\n" "an Epoch(to get time type) or Position")); } lastep_p = -1.1e20; if (indir_p->isModel()) defintvl_p = 0; intvl_p = defintvl_p; MVDirection zenith; MDirection dzen(zenith, MDirection::Ref(MDirection::AZEL, *frame_p)); MDirection::Ref had(MDirection::HADEC, *frame_p); zenith_p = MDirection::Convert(dzen, had)().getValue(); convdir_p = new MDirection::Convert(indir_p, had); slat2_p = zenith_p.getValue()[2]; clat2_p = sqrt(fabs(1.0 - square(slat2_p))); } Double ParAngleMachine::calcAngle(const Double ep) const { if (fabs(ep-lastep_p) 0) { lastep_p = ep; UTfactor_p = MeasTable::UTtoST(ep) * C::circle; longoff_p = mvdir_p.getLong() - zenith_p.getLong(); slat1_p = mvdir_p.getValue()[2]; clat1_p = sqrt(fabs(1.0 - square(slat1_p))); } return -mvdir_p.positionAngle(zenith_p); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/ParAngleMachine.h000066400000000000000000000161401476623553700220650ustar00rootroot00000000000000//# ParAngleMachine.h: Converts a direction into parallactic angle //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_PARANGLEMACHINE_H #define MEASURES_PARANGLEMACHINE_H //# Includes #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MeasFrame; // Converts a direction into parallactic angle // // // // // //
      • MDirection class // // // // From Parallactic Angle and machinery // // // // The construction of a ParAngleMachine class object creates a machine that // can create parallactic angles from a series of time epochs given. // // The machinery needs an input // MDirection to specify the input // coordinates reference direction and coordinate system. // The parallactic (vertical) // angle will be calculated as the angle between the vertical in the // local coordinate system (Az, El) through the given direction and // the pole of the J2000 coordinate system. // To calculate the parallactic angle for another // coordinate system pole, add the positionAngle between the // J2000 system and the pole in the other coordinate system. // // The machinery also needs a MeasFrame, // with a position on Earth and // a reference epoch. The reference time is necessary to have an epoch type. // // The actual calculation of the parallactic angles is done by the // operator() accepting a time or a list of times in various // formats. // // The machine calculates the paralaactic angle for the first time given to // the machine. For subsequent times that are within a check interval, // the angle is calculated assuming that only the hour angle changes within // that interval. For moving objects the test interval is always forced // to zero. Tests show that the machine with a zero interval is about // 8 times faster than using brute force. Having an interval of an // hour improves that by another factor of 4. // If the parallactic angles for a series of directions have // to be calculated, it is best to have separate machines for each such // field. // // // // // // // // // To speed up parallactic angle calculations // // // //
      • // class ParAngleMachine { public: //# Constructors // Create an empty machine. It can only be used after appropriate 'set' // methods ParAngleMachine(); // Construct for the specified direction ParAngleMachine(const MDirection &in); // Copy constructor (deep copy) ParAngleMachine(const ParAngleMachine &other); // Copy assignments (deep copy) ParAngleMachine &operator=(const ParAngleMachine &other); //# Destructor ~ParAngleMachine(); //# Operators // Return parallactic angles (epoch in days if given as Double) // //
      • AipsError if no frame or a frame without an Epoch (for type) or // Position. // // Quantum > operator()(const Quantum > &ep) const; Quantum > operator()(const Vector &ep) const; Quantum > operator()(const Vector &ep) const; Quantum operator()(const Quantum &ep) const; Quantum operator()(const MVEpoch &ep) const; Quantum operator()(const MEpoch &ep) const; Double operator()(const Double &ep) const; Vector operator()(const Vector &ep) const; // //# Member functions // Will have a group of set methods (in direction; reference time; a frame; // a reference time valid period // void set(const MDirection &in); void set(const MeasFrame &frame); // // Set the test interval (in days) over which to use simple formula void setInterval(const Double ttime); private: //# Data // Input direction MDirection *indir_p; // Conversion engine mutable MDirection::Convert *convdir_p; // Measure frame MeasFrame *frame_p; // Converted zenith mutable MVDirection zenith_p; // Intermediate conversion result mutable MVDirection mvdir_p; // Time of last full solution (in days) mutable Double lastep_p; // Default time interval over which to do simple solution (days) mutable Double defintvl_p; // Time interval over which to do simple solution (days) mutable Double intvl_p; // Calculation cache // mutable Double UTfactor_p; mutable Double longoff_p; mutable Double longdiff_p; mutable Double slat1_p; mutable Double clat1_p; mutable Double slat2_p; mutable Double clat2_p; // //# Constructors //# Private Member Functions // Get position angle (Epoch is supposed to be in days if Double) // //
      • AipsError if no frame or a frame without an Epoch (for type) or // Position. // // Double posAngle(const Quantum &ep) const; Vector posAngle(const Quantum > &ep) const; Double posAngle(const Double &ep) const; Vector posAngle(const Vector &ep) const; // // Initialise machinery void init(); // Initialise conversion void initConv() const; // Calculate position angle Double calcAngle(const Double ep) const; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/Precession.cc000066400000000000000000000122351476623553700213600ustar00rootroot00000000000000//# Precession.cc: Precession class //# Copyright (C) 1995,1996,1997,1998,1999,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constants const Double Precession::INTV = 0.1; //# Static data uInt Precession::myInterval_reg = 0; //# Constructors Precession::Precession() : method_p(Precession::STANDARD), fixedEpoch_p(MeasData::MJD2000), lres_p(0) { fillEpoch(); } Precession::Precession(const Precession &other) { copy(other); } Precession::Precession(PrecessionTypes type, Double catepoch) : method_p(type), fixedEpoch_p(catepoch), lres_p(0) { fillEpoch(); } Precession &Precession::operator=(const Precession &other) { if ( this != &other) { copy(other); } return *this; } void Precession::init() { method_p = Precession::STANDARD; fixedEpoch_p = MeasData::MJD2000; fillEpoch(); } void Precession::init(PrecessionTypes type, Double catepoch) { method_p = type; fixedEpoch_p = catepoch; fillEpoch(); } //# Destructor Precession::~Precession() {} //# Operators // Calculate precession Euler angles const Euler &Precession::operator()(Double epoch) { calcPrec(epoch); Double dt = epoch - checkEpoch_p; lres_p++; lres_p %= 4; for (uInt i=0; i<3; ++i) { result_p[lres_p](i) = pval_p[i] + dt*dval_p[i]; } return result_p[lres_p]; } //# Member functions // Calculate derivative of precession Euler angles const Euler &Precession::derivative(Double epoch) { calcPrec(epoch); ++lres_p; lres_p %= 4; for (uInt i=0; i<3; ++i) result_p[lres_p](i) = dval_p[i]; return result_p[lres_p]; } void Precession::copy(const Precession &other) { method_p = other.method_p; fixedEpoch_p = other.fixedEpoch_p; T_p = other.T_p; cent_p = other.cent_p; refEpoch_p = other.refEpoch_p; checkEpoch_p = other.checkEpoch_p; for (uInt i=0; i<3; ++i) { zeta_p[i] = other.zeta_p[i]; pval_p[i] = other.pval_p[i]; dval_p[i] = other.dval_p[i]; } for (uInt i=0; i<4; ++i) result_p[i] = other.result_p[i]; } void Precession::fillEpoch() { // Get the interpolation interval if (!Precession::myInterval_reg) { myInterval_reg = AipsrcValue::registerRC(String("measures.precession.d_interval"), Unit("d"), Unit("d"), Precession::INTV); } checkEpoch_p = 1e30; switch (method_p) { case B1950: refEpoch_p = MeasData::MJDB1850; cent_p = MeasData::TROPCEN; break; default: refEpoch_p = MeasData::MJD2000; cent_p = MeasData::JDCEN; break; } if (fixedEpoch_p == 0) { switch (method_p) { case B1950: fixedEpoch_p = MeasData::MJDB1950; break; default: fixedEpoch_p = refEpoch_p; break; } } switch (method_p) { case IAU2000: for (uInt i=0; i<3; ++i) zeta_p[i] = Polynomial(5); break; default: for (uInt i=0; i<3; ++i) zeta_p[i] = Polynomial(3); break; } T_p = (fixedEpoch_p - refEpoch_p)/cent_p; switch (method_p) { case B1950: MeasTable::precessionCoef1950(T_p, zeta_p); case NONE: break; case IAU2000: MeasTable::precessionCoef2000(zeta_p); break; default: MeasTable::precessionCoef(T_p, zeta_p); break; } for (uInt i=0; i<4; ++i) result_p[i].set(3,2,3); } void Precession::refresh() { checkEpoch_p = 1e30; } void Precession::calcPrec(Double t) { if (!nearAbs(t, checkEpoch_p, AipsrcValue::get(Precession::myInterval_reg))) { checkEpoch_p = t; switch (method_p) { case B1950: t = (t - refEpoch_p)/cent_p - T_p; break; default: t = (t - fixedEpoch_p)/cent_p; break; } for (uInt i=0; i<3; ++i) { pval_p[i] = (zeta_p[i])(t); dval_p[i] = ((zeta_p[i]).derivative())(t); switch (method_p) { case B1950: dval_p[i] = dval_p[i]/MeasData::TROPCEN; break; default: dval_p[i] = dval_p[i]/MeasData::JDCEN; break; } } } } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/Precession.h000066400000000000000000000165151476623553700212270ustar00rootroot00000000000000//# Precession.h: Precession class //# Copyright (C) 1995,1996,1997,1998,1999,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_PRECESSION_H #define MEASURES_PRECESSION_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // Precession class and calculations // // // // //
      • Measure class for use //
      • Euler class for format //
      • MeasData class for constants //
      • MeasTable class for other data // // // // Precession // // // // Precession forms the class for precession calculations. It is a simple // container with the selected method, and the mean epoch. It acts as a cache // for values and their derivatives, to enable fast calculations for time // epochs close together (see the aipsrc variable // measures.precession.d_interval.
        // The calculation method is selected from one of the following: //
          //
        • Precession::STANDARD (at 1995/09/04 the IAU1976 definition, // at 2004/01/01 the IAU2000 defeinition)) //
        • Precession::NONE (precession of zero returned //
        • Precession::IAU1976 //
        • Precession::B1950 //
        • Precession::IAU2000 //
        // Epochs can be specified as the MJD (with defined constants // MeasData::MJD2000 and MeasData::MJD1950 or the actual MJD), // leading to the following constructors: //
          //
        • Precession() default; assuming JD2000, STANDARD //
        • Precession(method) assuming the correct default epoch of // JD2000 or JD1950 depending on method //
        • Precession(method,epoch) with epoch Double(MJD) (Note: not // valid for IAU2000: in that case always JD2000 assumed) //
        // Actual precession for a certain Epoch (TT for IAU2000) is calculated by // the () operator // as Precession(epoch), with epoch Double MJD. Values returned as an // Euler. // The derivative (d-1) can be obtained as well by // derivative(epoch).
        // A Precession can be re-initialed with a different method and/or zero // epoch with the init() functions (same format as constructors). // To bypass the full calculation actual returned values are calculated // using the derivative if within about 2 hours (error less than about // 10-5 mas). A call to refresh() will re-initiate calculations // from scratch.
        // The following details can be set with the // Aipsrc mechanism: //
          //
        • measures.precession.d_interval: approximation interval as time // (fraction of days is default unit) over which linear approximation // is used (default is 0.1 day). //
        //
        // // // // #include // MVDirection pos(Quantity(10,"degree"),Quantity(-10.5,"degree")); // // direction RA=10; DEC=-10.5 // Precession mine(Precession::IAU1976); // define precession type // RotMatrix rotat(mine(45837.0)); // rotation matrix for 84/05/17 // MVDirection new = rotat*pos; // apply precession // rotat = RotMatrix(mine(45839.0)); // interpolate new precession // // assuming d_interval set large // // // // // To calculate the precession angles. An alternate route could have been // a global function, but having a simple container allows caching of some // calculations for speed.
        // Using MJD (JD-2400000.5) rather than JD is for precision reasons. //
        // // //
      • Adjust on 2004/01/01 // class Precession { public: //# Constants // Default interval to be used for linear approximation (in days) static const Double INTV; //# Enumerations // Types of known precession calculations (at 1995/09/04 STANDARD == // IAU1976), from 2004/01/01 will be IAU2000) enum PrecessionTypes { NONE, IAU1976, B1950, IAU2000, IAU2000A = IAU2000, IAU2000B = IAU2000, STANDARD = IAU1976 }; //# Constructors // Default constructor, generates default J2000 precession identification Precession(); // Copy constructor (deep copy) Precession(const Precession &other); // Constructor with epoch in Julian days explicit Precession(PrecessionTypes type, Double catepoch=0); // Copy assignment (deep copy) Precession &operator=(const Precession &other); //# Destructor ~Precession(); //# Operators // Return the precession angles (for IAU2000 including // the IAU 2000 corrections) at the specified epoch (in MJD; TT for IAU2000). const Euler &operator()(Double epoch); //# General Member Functions // Return derivative of precession (d-1) const Euler &derivative(Double epoch); // Re-initialise Precession object // void init(); void init(PrecessionTypes type, Double catepoch=0); // // Refresh calculations void refresh(); private: //# Data members // Method to be used PrecessionTypes method_p; // Fixed epoch to be used (MJD) Double fixedEpoch_p; // Fixed epoch in centuries from base epoch Double T_p; // Length of century (depending on Bessel or Julian days) Double cent_p; // Reference epoch; Double refEpoch_p; // Check epoch Double checkEpoch_p; // Polynomial coefficients for zeta,z,theta Polynomial zeta_p[3]; // Cached calculated angles Double pval_p[3]; // Cached derivatives Double dval_p[3]; // To reference results, and use a few in interim calculations, results are // saced in a circular buffer. // Current result pointer Int lres_p; // Last calculation Euler result_p[4]; // Interpolation interval aipsrc registration static uInt myInterval_reg; //# Member functions // Make a copy void copy(const Precession &other); // Create correct default fixedEpoch and catalogue epoch data void fillEpoch(); // Calculate precession angles for time t void calcPrec(Double t); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/Quality.cc000066400000000000000000000047601476623553700207020ustar00rootroot00000000000000//# Quality.cc: Quality parameter definitions for interface to table data //# Copyright (C) 1994,1995,1997,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Quality::QualityTypes Quality::type(Int qualityNumber) { QualityTypes val = Undefined; if (qualityNumber > Undefined && qualityNumber < NumberOfTypes) { val = QualityTypes(qualityNumber); } return val; } Quality::QualityTypes Quality::type(const String &qualityName) { QualityTypes val = Undefined; String name = qualityName; name.upcase(); if (name == "DATA") val = DATA; else if (name == "ERROR") val = ERROR; return val; } String Quality::name(QualityTypes qualityType) { String qualityName; switch (qualityType) { case Undefined: qualityName="??"; break; case DATA: qualityName="DATA"; break; case ERROR: qualityName="ERROR"; break; } return qualityName; } Vector Quality::allNames(Bool includeUndefined) { uInt size = includeUndefined ? NumberOfTypes : NumberOfTypes - 1; Vector names(size); uInt idx = 0; for (uInt i=0; i #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Quality parameter definitions. // // // // // This enumerates the available Quality types. This class is a wrapper // for the ENUM and conversion functions. All methods are static. // class Quality { public: //# The enum comments below are placed in the position they are to make the //# extracted documentation look nice. enum QualityTypes { // undefined value = 0 Undefined=0, // the data type DATA, // the error type ERROR }; // The number of QualityTypes. // // Update NumberOfTypes when entries are added. // enum { // The number of QualityTypes. NumberOfTypes = 3 }; // Convert Int to QualityTypes, returns Quality::Undefined if // it is an invalid type static QualityTypes type(Int qualityNumber); // Convert String to QualityTypes, returns Quality::Undefined if // it is an unrecognized string. The valid strings are the // same as the characters used in the enum above (i.e. // "DATA" returns Quality::DATA, "ERROR" returns Quality::ERROR, etc). static QualityTypes type(const String & quality); // Convert QualityTypes to String, Quality::Undefined returns // "??". static String name(QualityTypes qualityType); // Get all recognized quality names in no guaranteed order. // The undefined type can be included. static Vector allNames(Bool includeUndefined = False); private: }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/SolarPos.cc000066400000000000000000000242321476623553700210100ustar00rootroot00000000000000//# SolarPos.cc: Solar position class //# Copyright (C) 1995,1996,1997,1998,1999,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constants const Double SolarPos::INTV = 0.04; //# Static data uInt SolarPos::interval_reg = 0; uInt SolarPos::usejpl_reg = 0; //# Constructors SolarPos::SolarPos() : method(SolarPos::STANDARD), lres(0) { fill(); } SolarPos::SolarPos(const SolarPos &other) { copy(other); } SolarPos::SolarPos(SolarPosTypes type) : method(type), lres(0) { fill(); } SolarPos &SolarPos::operator=(const SolarPos &other) { if (this != &other) { copy(other); } return *this; } void SolarPos::copy(const SolarPos &other) { method = other.method; checkEpoch = other.checkEpoch; checkSunEpoch = other.checkSunEpoch; for (Int i=0; i<3; i++) { sval[i] = other.sval[i]; eval[i] = other.eval[i]; dsval[i] = other.dsval[i]; deval[i] = other.deval[i]; } for (Int j=0; j<6; j++) { result[j] = other.result[j]; } } //# Destructor SolarPos::~SolarPos() {} //# Operators // Calculate Solar Position const MVPosition &SolarPos::operator()(Double epoch) { calcEarth(epoch); Double dt = epoch - checkEpoch; lres++; lres %= 6; for (Int i=0; i<3; i++) { result[lres](i) = (-eval[i] - dt*deval[i]); } // Convert to rectangular if (!AipsrcValue::get(SolarPos::usejpl_reg)) { result[lres] = MeasTable::posToRect() * result[lres]; } return result[lres]; } //# Member functions const MVPosition &SolarPos::baryEarth(Double epoch) { calcEarth(epoch); calcSun(epoch); Int i; Double dt = epoch - checkEpoch; lres++; lres %= 6; for (i=0; i<3; i++) { result[lres](i) = eval[i] + dt*deval[i]; } dt = epoch - checkSunEpoch; for (i=0; i<3; i++) { result[lres](i) -= (sval[i] + dt*dsval[i]); } // Convert to rectangular if (!AipsrcValue::get(SolarPos::usejpl_reg)) { result[lres] = MeasTable::posToRect() * result[lres]; } return result[lres]; } const MVPosition &SolarPos::barySun(Double epoch) { calcSun(epoch); Double dt = epoch - checkSunEpoch; lres++; lres %= 6; for (Int i=0; i<3; i++) { result[lres](i) = (-sval[i] - dt*dsval[i]); } // Convert to rectangular if (!AipsrcValue::get(SolarPos::usejpl_reg)) { result[lres] = MeasTable::posToRect() * result[lres]; } return result[lres]; } const MVPosition &SolarPos::derivative(Double epoch) { calcEarth(epoch); lres++; lres %= 6; for (Int i=0; i<3; i++) { result[lres](i) = (-deval[i]); } // Convert to rectangular if (!AipsrcValue::get(SolarPos::usejpl_reg)) { result[lres] = MeasTable::posToRect() * result[lres]; } return result[lres]; } const MVPosition &SolarPos::baryEarthDerivative(Double epoch) { calcEarth(epoch); calcSun(epoch); lres++; lres %= 6; for (Int i=0; i<3; i++) { result[lres](i) = (deval[i] - dsval[i]); } // Convert to rectangular if (!AipsrcValue::get(SolarPos::usejpl_reg)) { result[lres] = MeasTable::posToRect() * result[lres]; } return result[lres]; } const MVPosition &SolarPos::barySunDerivative(Double epoch) { calcSun(epoch); lres++; lres %= 6; for (Int i=0; i<3; i++) { result[lres](i) = (-dsval[i]); } // Convert to rectangular if (!AipsrcValue::get(SolarPos::usejpl_reg)) { result[lres] = MeasTable::posToRect() * result[lres]; } return result[lres]; } void SolarPos::fill() { // Get the interpolation interval if (!SolarPos::interval_reg) { interval_reg = AipsrcValue::registerRC(String("measures.solarpos.d_interval"), Unit("d"), Unit("d"), SolarPos::INTV); } if (!SolarPos::usejpl_reg) { usejpl_reg = AipsrcValue::registerRC(String("measures.solarpos.b_usejpl"), False); } checkEpoch = 1e30; checkSunEpoch = 1e30; } void SolarPos::refresh() { checkEpoch = 1e30; checkSunEpoch = 1e30; } void SolarPos::calcEarth(Double t) { if (!nearAbs(t, checkEpoch, AipsrcValue::get(SolarPos::interval_reg))) { checkEpoch = t; switch (method) { default: t = (t - MeasData::MJD2000)/MeasData::JDCEN; break; } Int i,j; Vector fa(12), dfa(12); for (i=0; i<3; i++) { eval[i] = deval[i] = Double(0); } Double dtmp, ddtmp; switch (method) { default: if (AipsrcValue::get(SolarPos::usejpl_reg)) { Vector mypl = MeasTable::Planetary(MeasTable::EARTH, checkEpoch); for (i=0; i<3; i++) { eval[i] = mypl(i); deval[i] = mypl(i+3); } Vector mypl1 = MeasTable::Planetary(MeasTable::SUN, checkEpoch); for (i=0; i<3; i++) { eval[i] -= mypl1(i); deval[i] -= mypl1(i+3); } } else { for (i=0; i<12; i++) { fa(i) = MeasTable::posArg(i)(t); dfa(i) = MeasTable::posArgDeriv(i)(t); } std::shared_ptr> mul = MeasTable::mulPosEarthXY(t, 1e-6); DebugAssert (mul->contiguousStorage(), AipsError); const Double* mulPosEarthXY = mul->data(); for (i=0; i<189; i++) { dtmp = ddtmp = 0; for (j=0; j<12; j++) { dtmp += MeasTable::mulPosEarthXYArg(i)[j] * fa[j]; ddtmp += MeasTable::mulPosEarthXYArg(i)[j] * dfa[j]; } const Double sinpos0 = sin(dtmp + mulPosEarthXY[0]); const Double cospos0 = cos(dtmp + mulPosEarthXY[0]); const Double sinpos2 = sin(dtmp + mulPosEarthXY[2]); const Double cospos2 = cos(dtmp + mulPosEarthXY[2]); eval[0] += mulPosEarthXY[1] * sinpos0; eval[1] += mulPosEarthXY[3] * sinpos2; deval[0] += mulPosEarthXY[5] * sinpos0 + mulPosEarthXY[1] * cospos0 * ddtmp; deval[1] += mulPosEarthXY[7] * sinpos2 + mulPosEarthXY[3] * cospos2 * ddtmp; mulPosEarthXY += 8; } mul = MeasTable::mulPosEarthZ(t, 1e-6); DebugAssert (mul->contiguousStorage(), AipsError); const Double* mulPosEarthZ = mul->data(); for (i=0; i<32; i++) { dtmp = ddtmp = 0; for (j=0; j<12; j++) { dtmp += MeasTable::mulPosEarthZArg(i)[j] * fa[j]; ddtmp += MeasTable::mulPosEarthZArg(i)[j] * dfa[j]; } const Double sinpos0 = sin(dtmp + mulPosEarthZ[0]); const Double cospos0 = cos(dtmp + mulPosEarthZ[0]); eval[2] += mulPosEarthZ[1] * sinpos0; deval[2] += mulPosEarthZ[3] * sinpos0 + mulPosEarthZ[1] * cospos0 * ddtmp; mulPosEarthZ += 4; } for (i=0; i<3; i++) { deval[i] /= MeasData::JDCEN; } } break; } } } void SolarPos::calcSun(Double t) { if (!nearAbs(t, checkSunEpoch, AipsrcValue::get(SolarPos::interval_reg))) { checkSunEpoch = t; switch (method) { default: t = (t - MeasData::MJD2000)/MeasData::JDCEN; break; } Int i,j; Vector fa(12), dfa(12); for (i=0; i<3; i++) { sval[i] = dsval[i] = Double(0); } Double dtmp, ddtmp; switch (method) { default: if (AipsrcValue::get(SolarPos::usejpl_reg)) { Vector mypl = MeasTable::Planetary(MeasTable::SUN, checkEpoch); for (i=0; i<3; i++) { sval[i] = -mypl(i); dsval[i] = -mypl(i+3); } } else { for (i=0; i<12; i++) { fa(i) = MeasTable::posArg(i)(t); dfa(i) = MeasTable::posArgDeriv(i)(t); } std::shared_ptr> mul = MeasTable::mulPosSunXY(t, 1e-6); DebugAssert (mul->contiguousStorage(), AipsError); const Double* mulPosSunXY = mul->data(); for (i=0; i<98; i++) { dtmp = ddtmp = 0; for (j=0; j<12; j++) { dtmp += MeasTable::mulPosSunXYArg(i)[j] * fa[j]; ddtmp += MeasTable::mulPosSunXYArg(i)[j] * dfa[j]; } sval[0]+= mulPosSunXY[1] * sin(dtmp + mulPosSunXY[0]); sval[1] += mulPosSunXY[3] * sin(dtmp + mulPosSunXY[2]); dsval[0]+= mulPosSunXY[5] * sin(dtmp + mulPosSunXY[0]) + mulPosSunXY[1] * cos(dtmp + mulPosSunXY[0]) * ddtmp; dsval[1]+= mulPosSunXY[7] * sin(dtmp + mulPosSunXY[2]) + mulPosSunXY[3] * cos(dtmp + mulPosSunXY[2]) * ddtmp; mulPosSunXY += 8; } mul = MeasTable::mulPosSunZ(t, 1e-6); DebugAssert (mul->contiguousStorage(), AipsError); const Double* mulPosSunZ = mul->data(); for (i=0; i<29; i++) { dtmp = ddtmp = 0; for (j=0; j<12; j++) { dtmp += MeasTable::mulPosSunZArg(i)[j] * fa[j]; ddtmp += MeasTable::mulPosSunZArg(i)[j] * dfa[j]; } sval[2] += mulPosSunZ[1] * sin(dtmp + mulPosSunZ[0]); dsval[2] += mulPosSunZ[3] * sin(dtmp + mulPosSunZ[0]) + mulPosSunZ[1] * cos(dtmp + mulPosSunZ[0]) * ddtmp; mulPosSunZ += 4; } for (i=0; i<3; i++) { dsval[i] /= MeasData::JDCEN; } } break; } } } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/SolarPos.h000066400000000000000000000144051476623553700206530ustar00rootroot00000000000000//# SolarPos.h: Solar position class //# Copyright (C) 1995,1996,1997,1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_SOLARPOS_H #define MEASURES_SOLARPOS_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Solar position class and calculations // // // // //
      • Measure class, // especially MEpoch //
      • MeasData class for constants // // // // SolarPos from Solar Position // // // // SolarPos forms the class for Solar Position calculations. It is a simple // container with the selected method, and the mean epoch.
        // The method is selected from one of the following: //
          //
        • SolarPos::STANDARD (at 1995/09/04 the IAU1980 definition) //
        • SolarPos::NONE //
        // Epochs can be specified as the MJD (with defined constants MeasData::MJD2000 // and MeasData::MJDB1950 or the actual MJD), // leading to the following constructors: //
          //
        • SolarPos() default; assuming JD2000, IAU1980 //
        • SolarPos(method) assuming the correct default epoch of // JD2000 //
        • SolarPos(method,epoch) with epoch Double(MJD) //
        // Actual SolarPos for a certain Epoch is calculated by the () operator // as SolarPos(epoch), with epoch Double MJD, as an MVPosition vector.
        // It returns the geocentric position of the heliocentre in rectangular // coordinates in AU.
        // The derivative (d-1) can be obtained as well by // derivative(epoch), baryEarthDerivative() and barySunDerivative().
        // The Earth's and solar barycentric position can be obtained by the // members baryEarth and barySun. // The following details can be set with the // Aipsrc mechanism: //
          //
        • measures.solarpos.d_interval: approximation interval as time // (fraction of days is default unit) over which linear approximation // is used //
        • measures.solarpos.b_usejpl: use the JPL database for solar position. // Else analytical expression, relative error about 10-9 // Note that the JPL database to be used can be set with // measures.jpl.ephemeris (at the moment of writing DE200 (default), // or DE405) //
        // Reference: M. Soma et al., Cel. Mech. 41 (1988), 389; // E.M. Standish, Astron. Astroph. 114 (1982), 297. //
        // // // // // // To calculate the solar/Earth positions for gravitational deflection. // An alternate route could have been // a global function, but having a simple container allows // caching of some calculations for speed.
        // Using MJD (JD-2400000.5) rather than JD is for precision reasons. //
        // // // class SolarPos { public: //# Constants // Interval to be used for linear approximation (in days) static const Double INTV; //# Enumerations // Types of known SolarPos calculations (at 1995/09/04 STANDARD == IAU1980) enum SolarPosTypes {STANDARD,NONE}; //# Constructors // Default constructor, generates default J2000 SolarPos identification SolarPos(); // Copy constructor SolarPos(const SolarPos &other); // Constructor with type SolarPos(SolarPosTypes type); // Copy assignment SolarPos &operator=(const SolarPos &other); //# Destructor ~SolarPos(); //# Operators // Operator () calculates the geocentric Solar Position in AU const MVPosition&operator()(Double epoch); //# General Member Functions // // Return derivatives of SolarPos (d-1) const MVPosition &derivative (Double epoch); const MVPosition &baryEarthDerivative (Double epoch); const MVPosition &barySunDerivative (Double epoch); // // Barycentric position of Earth const MVPosition &baryEarth(Double epoch); // Barycentric position of Sun const MVPosition &barySun(Double epoch); // Re-initialise SolarPos object // void init(); void init(SolarPosTypes type); // // Refresh calculations void refresh(); private: //# Data menbers // Method to be used SolarPosTypes method; // Check epoch for linear approximation Double checkEpoch; Double checkSunEpoch; // Cached calculated Earth positions Double eval[3]; // Cached derivatives Double deval[3]; // Cached calculated Sun positions Double sval[3]; // Cached derivatives Double dsval[3]; // To be able to use references in simple calculations, results are calculated // in a circular buffer. // Current buffer pointer Int lres; // Last calculation MVPosition result[6]; // Interpolation interval static uInt interval_reg; // JPL use static uInt usejpl_reg; //# Member functions // Copy void copy(const SolarPos &other); // Fill an empty copy void fill(); // Calculate heliocentric Earth position for time t void calcEarth(Double t); // Calculate heliocentric barycentre position void calcSun(Double t); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/Stokes.cc000066400000000000000000000146211476623553700205170ustar00rootroot00000000000000//# Stokes.cc: Stokes parameter definitions for interface to table data //# Copyright (C) 1994,1995,1997,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Stokes::StokesTypes Stokes::type(Int stokesNumber) { StokesTypes val = Undefined; if (stokesNumber > Undefined && stokesNumber < NumberOfTypes) { val = StokesTypes(stokesNumber); } return val; } Stokes::StokesTypes Stokes::type(const String &stokesName) { StokesTypes val = Undefined; String name = stokesName; name.upcase(); if (name == "I") val = I; else if (name == "Q") val = Q; else if (name == "U") val = U; else if (name == "V") val = V; else if (name == "RR") val = RR; else if (name == "RL") val = RL; else if (name == "LR") val = LR; else if (name == "LL") val = LL; else if (name == "XX") val = XX; else if (name == "XY") val = XY; else if (name == "YX") val = YX; else if (name == "YY") val = YY; else if (name == "RX") val = RX; else if (name == "RY") val = RY; else if (name == "LX") val = LX; else if (name == "LY") val = LY; else if (name == "XR") val = XR; else if (name == "XL") val = XL; else if (name == "YR") val = YR; else if (name == "YL") val = YL; else if (name == "PP") val = PP; else if (name == "PQ") val = PQ; else if (name == "QP") val = QP; else if (name == "QQ") val = QQ; else if (name == "RCIRCULAR") val = RCircular; else if (name == "LCIRCULAR") val = LCircular; else if (name == "LINEAR") val = Linear; else if (name == "PTOTAL") val = Ptotal; else if (name == "PLINEAR") val = Plinear; else if (name == "PFTOTAL") val = PFtotal; else if (name == "PFLINEAR") val = PFlinear; else if (name == "PANGLE") val = Pangle; return val; } String Stokes::name(StokesTypes stokesType) { String stokesName; switch (stokesType) { case Undefined: stokesName="??"; break; case I: stokesName="I"; break; case Q: stokesName="Q"; break; case U: stokesName="U"; break; case V: stokesName="V"; break; case RR: stokesName="RR"; break; case RL: stokesName="RL"; break; case LR: stokesName="LR"; break; case LL: stokesName="LL"; break; case XX: stokesName="XX"; break; case XY: stokesName="XY"; break; case YX: stokesName="YX"; break; case YY: stokesName="YY"; break; case RX: stokesName="RX"; break; case RY: stokesName="RY"; break; case LX: stokesName="LX"; break; case LY: stokesName="LY"; break; case XR: stokesName="XR"; break; case XL: stokesName="XL"; break; case YR: stokesName="YR"; break; case YL: stokesName="YL"; break; case PP: stokesName="PP"; break; case PQ: stokesName="PQ"; break; case QP: stokesName="QP"; break; case QQ: stokesName="QQ"; break; case RCircular: stokesName="RCircular"; break; case LCircular: stokesName="LCircular"; break; case Linear: stokesName="Linear"; break; case Ptotal: stokesName="Ptotal"; break; case Plinear: stokesName="Plinear"; break; case PFtotal: stokesName="PFtotal"; break; case PFlinear: stokesName="PFlinear"; break; case Pangle: stokesName="Pangle"; break; } return stokesName; } Vector Stokes::allNames(Bool includeUndefined) { uInt size = includeUndefined ? NumberOfTypes : NumberOfTypes - 1; Vector names(size); uInt idx = 0; for (uInt i=0; i Stokes::receptor1(StokesTypes stokesType) { Int rec1 = (stokesType-1)%4; if (rec1<2) rec1=0; else rec1=1; if (stokesType>Stokes::V && stokesType(rec1); else return Fallible(); } Fallible Stokes::receptor2(StokesTypes stokesType) { Int rec2 = (stokesType-1)%4; if (rec2==0 || rec2==2) rec2=0; else rec2=1; if (stokesType>Stokes::V && stokesType(rec2); else return Fallible(); } Int Stokes::FITSValue(StokesTypes which) { Int retval; switch (which) { case I: retval = 1; break; case Q: retval = 2; break; case U: retval = 3; break; case V: retval = 4; break; case RR: retval = -1; break; case LL: retval = -2; break; case RL: retval = -3; break; case LR: retval = -4; break; case XX: retval = -5; break; case YY: retval = -6; break; case XY: retval = -7; break; case YX: retval = -8; break; case PFlinear: retval = 6; break; case Pangle: retval = 7; break; default: retval = 100 + Int(which); } return retval; } Stokes::StokesTypes Stokes::fromFITSValue(Int which) { StokesTypes retval; switch (which) { case 1: retval = I; break; case 2: retval = Q; break; case 3: retval = U; break; case 4: retval = V; break; case -1: retval = RR; break; case -2: retval = LL; break; case -3: retval = RL; break; case -4: retval = LR; break; case -5: retval = XX; break; case -6: retval = YY; break; case -7: retval = XY; break; case -8: retval = YX; break; case 6: retval = PFlinear; break; case 7: retval = Pangle; break; default: retval = Stokes::type(which-100); } return retval; } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/Stokes.h000066400000000000000000000123631476623553700203620ustar00rootroot00000000000000//# Stokes.h: Stokes parameter definitions for interface to table data //# Copyright (C) 1994,1995,1996,1997,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_STOKES_H #define MEASURES_STOKES_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Stokes parameter definitions for interface to table data. // // // // // This enumerates the available Stokes types, but does not define // the operations for conversion between Stokes types. // This class is a wrapper for the ENUM and conversion functions. // class Stokes { public: // The Stokes types are defined by this enum. // // // DO NOT CHANGE THE ORDER OF THESE TYPES, as the integers corresponding // to the enum are required for storage in Tables. // One can add to these types, but the order must be preserved. // The correlation products are required to have the order indicated with // values of 1,2,3,4 plus n*4. // // //# The enum comments below are placed in the position they are to make the //# extracted documentation look nice. enum StokesTypes { // undefined value = 0 Undefined=0, I, Q, U, // standard stokes parameters V, // RR, RL, LR, // circular correlation products LL, // XX, XY, YX, // linear correlation products YY, // RX, RY, LX, LY, XR, XL, YR, // mixed correlation products YL, // PP, PQ, QP, // general quasi-orthogonal correlation products QQ, // RCircular, LCircular, // single dish polarization types Linear, // Polarized intensity ((Q^2+U^2+V^2)^(1/2)) Ptotal, // Linearly Polarized intensity ((Q^2+U^2)^(1/2)) Plinear, // Polarization Fraction (Ptotal/I) PFtotal, // Linear Polarization Fraction (Plinear/I) PFlinear, // Linear Polarization Angle (0.5 arctan(U/Q)) (in radians) Pangle }; // The number of StokesTypes. // // Update NumberOfTypes when entries are added. // enum { // The number of StokesTypes. NumberOfTypes = 33 }; // convert Int to StokesTypes, returns Stokes::Undefined if // it is an invalid type static StokesTypes type(Int stokesNumber); // convert String to StokesTypes, returns Stokes::Undefined if // it is an unrecognized string. The valid strings are the // same as the characters used in the enum above (i.e. // "I" returns Stokes::I, "Linear" returns Stokes::Linear, etc). static StokesTypes type(const String & stokesName); // convert StokesTypes to String, Stokes::Undefined returns // "??". static String name(StokesTypes stokesType); // get all recognized stokes names in no guaranteed order. static Vector allNames(Bool includeUndefined = False); // map StokesTypes to receptor number (0 or 1) for the // interferometric correlation products. // e.g. XY will give receptor1==0 receptor2==1 etc. // I,Q,U,V and the single dish types will produce invalid // Fallible. // static Fallible receptor1(StokesTypes stokesType); static Fallible receptor2(StokesTypes stokesType); // // These two functions map stokes type to FITS type and vice versa. If you add a // StokesType you should change these functions as well. //
          //
        • I,Q,U,V <-> 1,2,3,4 //
        • RR,LL,RL,LR <-> -1,-2,-3,-4 Note! Not the same as enum order! //
        • XX,YY,XY,YX <-> -5,-6,-7,-8 Note! Not the same as enum order! //
        • Otherwise, FITS type <-> 100 + Int(stokesType). This is not standard FITS. //
        // static Int FITSValue(StokesTypes which); static StokesTypes fromFITSValue(Int); // private: }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/UVWMachine.cc000066400000000000000000000223331476623553700212140ustar00rootroot00000000000000//# UVWMachine.cc: Converts UVW coordinates between coordinate systems //# Copyright (C) 1998,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors UVWMachine::UVWMachine(const MDirection::Ref &out, const MDirection &in, Bool EW, Bool project) : ew_p(EW), proj_p(project), zp_p(True), nop_p(False), in_p(in) { outref_p = out; out_p = MDirection(outref_p); planetinit(); conv_p = MDirection::Convert(in_p, outref_p); outin_p = conv_p(); out_p = outin_p; init(); } UVWMachine::UVWMachine(const MDirection &out, const MDirection &in, Bool EW, Bool project) : ew_p(EW), proj_p(project), zp_p(False), nop_p(False), in_p(in), out_p(out) { outref_p = out.getRef(); planetinit(); conv_p = MDirection::Convert(in_p, outref_p); outin_p = conv_p(); init(); } UVWMachine::UVWMachine(const MDirection::Ref &out, const MDirection &in, const MeasFrame &frame, Bool EW, Bool project) : ew_p(EW), proj_p(project), zp_p(True), nop_p(False), in_p(in) { outref_p = out; out_p = MDirection(outref_p); outref_p.set(frame); planetinit(); conv_p = MDirection::Convert(in_p, outref_p); outin_p = conv_p(); out_p = outin_p; init(); } UVWMachine::UVWMachine(const MDirection &out, const MDirection &in, const MeasFrame &frame, Bool EW, Bool project) : ew_p(EW), proj_p(project), zp_p(False), nop_p(False), in_p(in), out_p(out) { outref_p = out.getRef(); outref_p.set(frame); planetinit(); conv_p = MDirection::Convert(in_p, outref_p); outin_p = conv_p(); init(); } UVWMachine::UVWMachine(const UVWMachine &other) { copy(other); init(); } UVWMachine &UVWMachine::operator=(const UVWMachine &other) { if (this != &other) { copy(other); init(); } return *this; } //# Destructor UVWMachine::~UVWMachine() {} //# Operators MVPosition UVWMachine::operator()(const MVPosition &uv) const { if (nop_p) return uv; return uv * uvproj_p; } Vector UVWMachine::operator()(const Vector &uv) const { if (nop_p) return uv; Vector tmp(uv.nelements()); for (uInt i=0; i UVWMachine::operator()(const Vector &uv) const { if (nop_p) return uv; return (MVPosition(uv) * uvproj_p).getValue(); } Vector > UVWMachine::operator()(const Vector > &uv) const { if (nop_p) return uv; Vector > tmp(uv.nelements()); for (uInt i=0; i &uv) const { if (!nop_p) uv = (MVPosition(uv) * uvproj_p).getValue(); } void UVWMachine::convertUVW(Vector > &uv) const { if (!nop_p) { for (uInt i=0; i &uv) const { if (!nop_p) { for (uInt i=0; i &uv) const { phase = 0; if (!nop_p) { MVPosition tmp(uv); tmp *= uvrot_p; phase = phrot_p * tmp; if (proj_p) tmp *= rot4_p; uv = tmp.getValue(); } } void UVWMachine::convertUVW(Vector &phase, Vector > &uv) const { phase = 0; if (!nop_p) { MVPosition tmp; phase.resize(uv.nelements()); for (uInt i=0; i &phase, Vector &uv) const { phase.resize(uv.nelements()); phase = 0; if (!nop_p) { for (uInt i=0; i &uv) const { Double phase; convertUVW(phase, uv); return phase; } Vector UVWMachine::getPhase(Vector > &uv) const { Vector phase(uv.nelements()); convertUVW(phase, uv); return phase; } Double UVWMachine::getPhase(MVPosition &uv) const { Double phase; convertUVW(phase, uv); return phase; } Vector UVWMachine::getPhase(Vector &uv) const { Vector phase(uv.nelements()); convertUVW(phase, uv); return phase; } //# Private member functions void UVWMachine::init() { // Initialise the rotation matrices for uvw and phase conversion // Define axes static const MVDirection mVz(0.,0.,1.); static const MVDirection mVy(0.,1.,0.); static const MVDirection mVx(1.,0.,0.); if (!nop_p) { // Define rotation to a coordinate system with pole towards in-direction // and X-axis W; by rotating around z-axis over -(90-long); and around // x-axis (lat-90). rot1_p = RotMatrix(Euler(-(M_PI_2 - in_p.getValue().get()(0)), 3, in_p.getValue().get()(1) - M_PI_2, 1)); // Convert the input axes directions to the output reference frame, and // deduce a rotation matrix from these rot2_p.set(conv_p(mVx).getValue().getValue(), conv_p(mVy).getValue().getValue(), conv_p(mVz).getValue().getValue()); rot2_p.transpose(); // The rotation matrix from a system that has a pole towards output // direction, into the standard system. rot3_p = RotMatrix(Euler(M_PI_2 - out_p.getValue().get()(1), 1, -(out_p.getValue().get()(0) - M_PI_2), 3)); // Get the rotation matrix which re-projects an uv-plane onto another // reference direction: //
          //
        • around x-axis (out-lat - 90) //
        • around z-axis (out-long - in-long) //
        • around x-axis (90 - in-lat) //
        // and normalise rot4_p = RotMatrix(); if (proj_p) { RotMatrix x(Euler(-(M_PI_2 - out_p.getValue().get()(1)), 1, out_p.getValue().get()(0) - in_p.getValue().get()(0), 3, (M_PI_2 - in_p.getValue().get()(1)), 1)); rot4_p(0,0) = x(1,1)/x(2,2); rot4_p(1,1) = x(0,0)/x(2,2); rot4_p(0,1) = x(1,0)/x(2,2); rot4_p(1,0) = x(0,1)/x(2,2); } // Complete rotation matrix for uvw change uvrot_p = rot3_p * rot2_p * rot1_p; uvrot_p.transpose(); // Complete rotation matrix for uvw change including a re-projection uvproj_p = uvrot_p * rot4_p; // Phase change vector from input to output coordinates. phrot_p = rot3_p * (MVPosition(out_p.getValue()) - MVPosition(outin_p.getValue())); nop_p = conv_p.isNOP() && !proj_p && zp_p; } } void UVWMachine::planetinit() { if ((outref_p.getType() & MDirection::EXTRA)) { // out planet out_p.set(outref_p); // make sure frame set MDirection::Ref ref(MDirection::J2000, in_p.getRef().getFrame()); out_p = MDirection::Convert(out_p, ref)(); } if ((in_p.getRef().getType() & MDirection::EXTRA)) { // in planet MDirection::Ref ref(MDirection::J2000, outref_p.getFrame()); in_p = MDirection::Convert(in_p, ref)(); } } void UVWMachine::copy(const UVWMachine &other) { ew_p = other.ew_p; proj_p = other.proj_p; zp_p = other.zp_p; nop_p = other.nop_p; in_p = other.in_p; outref_p = other.outref_p; conv_p = other.conv_p; out_p = other.out_p; outin_p = other.outin_p; rot1_p = other.rot1_p; rot2_p = other.rot2_p; rot3_p = other.rot3_p; rot4_p = other.rot4_p; } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/Measures/UVWMachine.h000066400000000000000000000260741476623553700210640ustar00rootroot00000000000000//# UVWMachine.h: Converts UVW coordinates between coordinate systems //# Copyright (C) 1998,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_UVWMACHINE_H #define MEASURES_UVWMACHINE_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MeasFrame; // Converts UVW coordinates between coordinate systems // // // // //
      • MDirection class // // // // From UVW coordinates and machinery // // // // The construction of a UVWMachine class object creates a machine that can // convert UVW coordinates from one coordinate system to another. In addition // it can also supply the phase rotation necessary to move to a new position. // // The constructors need an input // MDirection to specify the input UVW // coordinates reference direction and coordinate system. // An EW flag can be specified to indicate the different type of UVW // coordinates. I.e. projection along polar axis rather than towards // observing direction (not implemented yet). // A project flag, if set, will re-project the resulting UV plane onto the // in-direction reference plane. // // The constructors also need an output coordinate system // (MDirection::Ref) or an output // MDirection (including the reference coordinate system). The first case // indicates that the real position on the sky does not change (only the // coordinate system used); the second indicates that the UVW should be // for a new position on the sky (in addition to maybe a different // coordinate system). In the first case only the UVW coordinates will change, // in the second the UVW positions will change, but also the phase of the // observed data will have to change. For some conversions a reference // frame is needed (to indicate e.g. the // position or time). This frame can be part of one of the constructor // references, but can also be specified separately in the constructor. A // change in the frame parameter can be made outside the UVWMachine // by e.g. frame.reset(anMEpoch). // // If the frame is changed by the user of the conversion machine, the // machine has to be reinitialised before using it for output by using // reCalculate(). // // Projection entails a rotation. For changes to a fixed frame (i.e. not // changing with e.g. time), the rotation matrix is calculated only once. In // other cases it has to be calculated per series of uvw conversions. The // latter case can hence be time consuming. // // // If either the input or output direction/reference specifies a planet, action // is special. Planets are assumed to be in J2000 positions, since that is // the only way to carry them from conversion to conversion (and also have a // variable phase-center; which can, btw, always be obtained by the // phaseCenter() member). // Note that a reCalculate() is necessary between calls of the engine, // since the planetary position will change from time to time (i.e. with // the Frame). // // If no explicit output coordinate is given (i.e. no phase shift necessary), // and the conversion from input to output is an essential NOP, and no // reprojection to the input plane is required, the machine will bypass all // calculations. This state can be inspected by the isNOP() method. // // If you want to convert to say an azimuth/elevation map of the Sun, this // can be done to have either two conversion engines (original to Sun, then // Sun to AzEl), or by conversion of the Sun to AzEl before entering the // engine. // // The output of the machine is either a set of rotation matrices that can // be used to convert UVW coordinates (and, if necessary, phases); or the // UVW conversion and actual phase can be calculated from a given // UVW coordinate (or set of coordinates). // // Since e.g. in an EW interferometer (or any set of baselines // on a line) the phase correction and UVW transform scales with the length // of the baseline, conversion of a nominal (say 1m) baseline suffices to // easily calculate others. The same is true for baselines in a plane, // where a conversion of two orthogonal baselines in that plane will suffice. // // // // // // // Given a current phase stopping Center // MDirection indir(Quantity(3.25745692, "rad"), // Quantity(0.040643336,"rad"), // MDirection::Ref(MDirection::B1950)); // // Conversion to J2000 is set by: // UVWMachine uvm(MDirection::Ref(MDirection::J2000), indir); // // The rotation matrix to go to new UVW is obtained by: // RotMatrix rm(uvm.rotationUVM()); // // If an UVW specified: // MVPosition uvw(-739.048461, -1939.10604, 1168.62562); // // This can be converted by e.g.: // uvw *= rm; // // Or, alternatively, by e.g.: // uvm.convertUVW(uvw); // // // // // To aid making maps in different coordinate systems // // // //
      • add EW UVW coordinates //
      • check if non right-handed coordinates systems (like AzEl) are // handled correctly //
      • provide a MVuvw and Muvw class to cater for second order effects // appropiately // class UVWMachine { public: //# Constructors // Constructors have an EW flag, which will give a projection parallel to // the polar axis rather than in the direction of the fieldcenter, and a // project flag. The last will correct the UV coordinates to re-project // them onto the plane specified by the in direction // // Construct a UVW conversion machine from the in coordinate and its // system to the out coordinate system (output absolute direction // remains the same) UVWMachine(const MDirection::Ref &out, const MDirection &in, Bool EW=False, Bool project=False); // Construct a UVW conversion machine from the in coordinate and its // system to the out coordinate and its system UVWMachine(const MDirection &out, const MDirection &in, Bool EW=False, Bool project=False); // Construct UVW conversion machine with an explicitly given frame // UVWMachine(const MDirection::Ref &out, const MDirection &in, const MeasFrame &frame, Bool EW=False, Bool project=False); UVWMachine(const MDirection &out, const MDirection &in, const MeasFrame &frame, Bool EW=False, Bool project=False); // // // Copy constructor UVWMachine(const UVWMachine &other); // Copy assignments UVWMachine &operator=(const UVWMachine &other); //# Destructor ~UVWMachine(); //# Operators // Return converted UVW coordinates // Vector operator()(const Vector &uv) const; Vector > operator()(const Vector > &uv) const; MVPosition operator()(const MVPosition &uv) const; Vector operator()(const Vector &uv) const; // //# Member functions // Return the new phase center coordinates const MDirection &phaseCenter() const; // Return if the engine is an effective NOP Bool isNOP() { return nop_p; } // Return a rotation matrix that can be used to convert UVW coordinates: // UVW(new) = UVW(old) * rotationUVW() const RotMatrix &rotationUVW() const; // Return a position vector that can produce the phase correction: // dPhase = rotationPhase * UVW(new) const MVPosition &rotationPhase() const; // replace UVW with converted values // void convertUVW(Vector &uv) const; void convertUVW(Vector > &uv) const; void convertUVW(MVPosition &uv) const; void convertUVW(Vector &uv) const; // // Get phase shift (in implied units of UVW), and change input uvw as well // Double getPhase(Vector &uv) const; Vector getPhase(Vector > &uv) const; Double getPhase(MVPosition &uv) const; Vector getPhase(Vector &uv) const; // // Replace UVW with converted, and return phase // void convertUVW(Double &phase, Vector &uv) const; void convertUVW(Vector &phase, Vector > &uv) const; void convertUVW(Double &phase, MVPosition &uv) const; void convertUVW(Vector &phase, Vector &uv) const; // // Recalculate the parameters for the machine after e.g. a frame change void reCalculate(); private: //# Data // EW flag Bool ew_p; // Projection flag Bool proj_p; // Zero phase flag (for speed) Bool zp_p; // No conversion necessary flag Bool nop_p; // Old phase center MDirection in_p; // New coordinate reference MDirection::Ref outref_p; // Old phase center in new coordinates MDirection outin_p; // New phase center MDirection out_p; // Rotation Matrix to go from input UVW to coordinate system RotMatrix rot1_p; // Rotation matrix to go from old system to new system RotMatrix rot2_p; // Rotation Matrix to go from new coordinate system to output UVW RotMatrix rot3_p; // Rotation Matrix to project UV-plane onto RotMatrix rot4_p; // UVW rotation RotMatrix uvrot_p; // UVW rotation including projection RotMatrix uvproj_p; // Phase rotation MVPosition phrot_p; // Conversion engine MDirection::Convert conv_p; //# Constructors // default constructor: not implemented UVWMachine(); //# Private Member Functions // Initialise machinery void init(); // Planet handling void planetinit(); // Copy data members void copy(const UVWMachine &other); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/VelocityMachine.cc000066400000000000000000000200011476623553700223170ustar00rootroot00000000000000//# VelocityMachine.cc: Converts between velocities and frequencies //# Copyright (C) 1998,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors VelocityMachine::VelocityMachine(const MFrequency::Ref &freqRef, const Unit &freqUnits, const MVFrequency &restFreq, const MDoppler::Ref &velRef, const Unit &velUnits) : fref_p(freqRef), fun_p(freqUnits), rest_p(restFreq), vref_p(velRef), vun_p(velUnits) { vfm_p = (MFrequency::Types) fref_p.getType(); init(); } VelocityMachine::VelocityMachine(const MFrequency::Ref &freqRef, const Unit &freqUnits, const MVFrequency &restFreq, const MDoppler::Ref &velRef, const Unit &velUnits, const MeasFrame &frame) : fref_p(freqRef), fun_p(freqUnits), rest_p(restFreq), vref_p(velRef), vun_p(velUnits) { fref_p.set(frame); vfm_p = (MFrequency::Types) fref_p.getType(); init(); } VelocityMachine::VelocityMachine(const MFrequency::Ref &freqRef, const Unit &freqUnits, const MVFrequency &restFreq, const MFrequency::Types &convertRef, const MDoppler::Ref &velRef, const Unit &velUnits) : fref_p(freqRef), fun_p(freqUnits), rest_p(restFreq), vfm_p(convertRef), vref_p(velRef), vun_p(velUnits) { init(); } VelocityMachine::VelocityMachine(const MFrequency::Ref &freqRef, const Unit &freqUnits, const MVFrequency &restFreq, const MFrequency::Types &convertRef, const MDoppler::Ref &velRef, const Unit &velUnits, const MeasFrame &frame) : fref_p(freqRef), fun_p(freqUnits), rest_p(restFreq), vfm_p(convertRef), vref_p(velRef), vun_p(velUnits) { fref_p.set(frame); init(); } VelocityMachine::VelocityMachine(const VelocityMachine &other) { copy(other); init(); } VelocityMachine &VelocityMachine::operator=(const VelocityMachine &other) { if (this != &other) { copy(other); init(); } return *this; } //# Destructor VelocityMachine::~VelocityMachine() {} //# Operators const Quantum &VelocityMachine::operator()(const MVFrequency &in) { resv_p.setValue(cvvo_p(cvfv_p(in). toDoppler(rest_p).getValue()). getValue().get().getValue() / vfac_p); return resv_p; } const Quantum &VelocityMachine::operator()(const MVDoppler &in) { resf_p.setValue(MVFrequency(cvvf_p(MFrequency::fromDoppler(cvov_p(in), rest_p, vfm_p). getValue()). getValue().getValue()).get(fun_p).getValue()); return resf_p; } const Quantum &VelocityMachine::operator()(const Quantum &in) { static UnitVal Velocity = UnitVal::LENGTH/UnitVal::TIME; if (in.getFullUnit().getValue() == Velocity || in.getFullUnit().getValue() == UnitVal::NODIM) { return this->operator()(MVDoppler(in)); } return this->operator()(MVFrequency(in)); } const Quantum &VelocityMachine::makeVelocity(Double in) { Double rfreqValue = rest_p.get().getValue(); ThrowIf( rfreqValue == 0, "Rest frequency is 0 so cannot convert to velocity" ); ThrowIf( rfreqValue < 0, "Rest frequency is " + String::toString(rest_p) + " which is invalid because it is less than 0 so cannot " " convert to velocity" ); resv_p.setValue(cvvo_p(cvfv_p(in). toDoppler(rest_p).getValue()). getValue().get().getValue() / vfac_p); return resv_p; } const Quantum &VelocityMachine::makeFrequency(Double in) { resf_p.setValue(MVFrequency(cvvf_p(MFrequency::fromDoppler(cvov_p(in), rest_p, vfm_p). getValue()). getValue().getValue()).get(fun_p).getValue()); return resf_p; } const Quantum > &VelocityMachine:: makeVelocity(const Vector &in) { uInt n = in.nelements(); vresv_p.getValue().resize(n); for (uInt i=0; i > &VelocityMachine:: makeFrequency(const Vector &in) { uInt n = in.nelements(); vresf_p.getValue().resize(n); for (uInt i=0; i #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MeasFrame; // Converts between velocities and frequencies // // // // //
      • Measures module //
      • MFrequency class //
      • MDoppler class // // // // From Velocity and machinery // // // // The construction of a VelocityMachine class object creates a machine that // can calculate the velocity from a frequency, or vice versa, a frequency // from a velocity. // // To be able to do the conversions, the machine (or rather its constructors) // needs to know the following information: //
          //
        • Reference for frequencies. It should contain at least the reference // code, to specify what type of frequency we are talking about // (e.g. MFrequency::LSRK). The reference could also contain an offset. // In that case all // input frequencies are considered to be relative to this offset; all // output frequencies will have this offset removed.
          // The reference can optionally contain a MeasFrame (which // specifies where, when and in which direction you are // observing). This frame is necessary if, in addition to // converting between velocity and frequency, you also want to // convert between different types (e.g. given an 'LSRK' velocity, // you want to know the 'TOPO' frequency), and if the offset is // in a different reference type. However, the MeasFrame // can also be given explicitly in the machine constructor as an // optional argument. //
        • Preferred 'frequency' units (e.g. GHz, or cm). These units are used // to output a frequency, or if an input frequency is given as a // simple double, these units will be implicitly assumed. //
        • Reference for velocity. It should contain at least the reference // code, to specify what type of velocity we are talking about // (e.g. MDoppler::OPTICAL, note // that MDoppler::BETA is the 'true' velocity). // The reference could also contain an offset. In that case all // input velocities are considered to be relative to this offset; all // output velocities will have this offset removed. //
        • Preferred velocity units (e.g. AU/a). These units are used // to output a velocity, or if an input velocity is given as a // simple double, these units will be implicitly assumed. //
        • The rest frequency to be used for converting between frequency and // velocity. It is given as an MVFrequency. //
        // To be able to convert between different types (say a velocity // referenced with respect to the 'LSRK', and a frequency referenced // with respect to 'TOPO', the following additional, optional // information can be included explicitly in the constructors: //
          //
        • A reference code for the velocity (given as a frequency reference // code (e.g. MFrequency::TOPO)). If given, all input frequencies // will be converted to the frequency belonging to this reference // code; all output frequencies will be converted from this // assumed reference to the specified Frequency reference. The // net effect is that all velocities will be assumed to refer to // this reference code. Note that in most cases the conversion // will have to know the 'when, where, which direction' // environment (the 'frame' -- a MeasFrame). This can be given // either implicitly in the 'reference for the frequency', or // explicitly (see next dot point). //
        • A frame (MeasFrame). This frame will be used in any conversion // between reference frames. If not given explicitly here, it will // tacitly be assumed that if a frame is necessary, it has been specified // in the frequency reference. //
        // Once the machine has been set up, operator() can be used to convert // between velocities and frequencies if the input argument type (e.g. an // MVFrequency) can be deduced. In other cases makeFrequency() or // makeVelocity() should be used (e.g. if the argument type is a // simple Double). //
        // // // // // Define a time/position frame // MEpoch epo(MVEpoch(MVTime(98,5,16,0.5).day())); // MPosition pos; // MeasTable::Observatory(pos, "ATCA"); // MeasFrame frame(epo, pos); // // // // Note that e.g. the time in the frame can be changed later // // Specify the frequency reference // MFrequency::Ref fr(MFrequency::LSRK); // // // // Specify the velocity reference // MDoppler::Ref vr(MDoppler::OPT); // // // // Specify the default units // Unit fu("eV"); // Unit vu("AU/a"); // // // // Get the rest frequency // MVFrequency rfrq(QC::HI); // // // // Set up a machine (no conversion of reference frame) // VelocityMachine exec(fr, fu, rfrq, vr, vu, frame); // // // // or as (with conversion of reference frame it could have been) // // VelocityMachine exec(fr, fu, rfrq, vr, vu, MFrequency::TOPO, frame); // // Given a current observational frequency of 5.87432837e-06 eV // // its velocity will be (in AU/yr) // cout << "Velocity: " << exec.makeVelocity(5.87432837e-06) << endl; // // // // Introducing an offset // MFrequency foff(MVFrequency(Quantity(5.87432837e-06, "eV")), // MFrequency::LSRK); // // // // and setting it in the reference, and regenerating machine: // fr.set(foff); // exec.set(fr); // // // // the following will give the same result: // cout << "Velocity: " << exec.makeVelocity(0.0) << endl; // // // See the test program for more examples // // // // To aid in converting series of frequencies and velocities // // // //
      • Nothing I know of // class VelocityMachine { public: //# Constructors // Construct a machine from the input values (no frame conversion, implicit // frame if necessary) VelocityMachine(const MFrequency::Ref &freqRef, const Unit &freqUnits, const MVFrequency &restFreq, const MDoppler::Ref &velRef, const Unit &velUnits); // Construct a machine from the input values (no frame conversion, explicit // frame will be added to freqRef) VelocityMachine(const MFrequency::Ref &freqRef, const Unit &freqUnits, const MVFrequency &restFreq, const MDoppler::Ref &velRef, const Unit &velUnits, const MeasFrame &frame); // Construct a machine from the input values (frame conversion, implicit // frame assumed if necessary) with explicit velocity reference frame // specified. VelocityMachine(const MFrequency::Ref &freqRef, const Unit &freqUnits, const MVFrequency &restFreq, const MFrequency::Types &convertRef, const MDoppler::Ref &velRef, const Unit &velUnits); // Construct a machine from the input values (frame conversion, explicit // frame) with explicit velocity reference frame // specified, and added to freqref. VelocityMachine(const MFrequency::Ref &freqref, const Unit &freqUnits, const MVFrequency &restFreq, const MFrequency::Types &convertRef, const MDoppler::Ref &velRef, const Unit &velUnits, const MeasFrame &frame); // Copy constructor (copy semantics) VelocityMachine(const VelocityMachine &other); // Copy assignment (copy semantics) VelocityMachine &operator=(const VelocityMachine &other); //# Destructor ~VelocityMachine(); //# Operators // Return velocity if frequency given, or a frequency if a velocity is given // const Quantum &operator()(const MVFrequency &in); const Quantum &operator()(const MVDoppler &in); const Quantum &operator()(const Quantum &in); const Quantum &makeVelocity(Double in); const Quantum &makeFrequency(Double in); const Quantum > &makeVelocity(const Vector &in); const Quantum > &makeFrequency(const Vector &in); // //# Member functions // Set or reset the specified part of the machine. The machinery will be // reset to reflect the changes made. // // Sets a new frequency reference. Note that if an explicit frame has been // used in earlier constructors, the frame should again be set explicitly // with set(MeasFrame). void set(const MFrequency::Ref &in); void set(const Unit &in); // Sets the rest frequency void set(const MVFrequency &in); void set(const MFrequency::Types &in); void set(const MDoppler::Ref &in); // Sets the MeasFrame to be used in conversions. void set(const MeasFrame &in); // // Get the general information used in the machine (shadows the sets above // and the constructor arguments. The MeasFrame should be explicitly // asked for from the frequency reference by the user // const MFrequency::Ref &getFrequencyReference() const; const Unit &getFrequencyUnits() const; const MDoppler::Ref &getDopplerReference() const; const Unit &getDopplerUnits() const; const MVFrequency &getRestFrequency() const; const MFrequency::Types &getConversionReference() const; // // Recalculate the machinery from the original inputs. Note that in all // normal circumstances this function does not have to be used (the set() // methods will do it automatically). At the moment I cannot think of // any circumstance it should be used explicitly. void reCalculate(); private: //# Constructors // Construct an empty machine (not implemented) VelocityMachine(); //# Data // Frequency reference MFrequency::Ref fref_p; // Frequency units // Unit fun_p; // // Rest frequency MVFrequency rest_p; // Velocity frame MFrequency::Types vfm_p; // Velocity reference MDoppler::Ref vref_p; // Velocity units // Unit vun_p; Double vfac_p; // // Frequency conversion forward MFrequency::Convert cvfv_p; // Frequency conversion backward MFrequency::Convert cvvf_p; // Velocity conversion forward MDoppler::Convert cvvo_p; // Velocity conversion backward MDoppler::Convert cvov_p; // Result // Quantum resv_p; Quantum resf_p; Quantum > vresv_p; Quantum > vresf_p; // //# Private Member Functions // Initialise machinery void init(); // Copy data members void copy(const VelocityMachine &other); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/test/000077500000000000000000000000001476623553700177135ustar00rootroot00000000000000casacore-3.7.1/measures/Measures/test/CMakeLists.txt000066400000000000000000000014511476623553700224540ustar00rootroot00000000000000set (tests dM1950_2000 dMeasure tEarthField tEarthMagneticMachine tMBaseline tMDirection tMEarthMagnetic tMFrequency tMeasComet tMeasIERS tMeasJPL tMeasMath tMeasure tMeasureHolder tMuvw tParAngleMachine tQuality tRecordTransformable tStokes tUVWMachine tVelocityMachine ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_measures) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) if (SOFA_FOUND) add_executable (tIAU2000 SofaTest.cc tIAU2000.cc) target_link_libraries (tIAU2000 casa_measures ${SOFA_LIBRARY}) add_test (tIAU2000 ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./tIAU2000) add_dependencies(check tIAU2000) endif (SOFA_FOUND) casacore-3.7.1/measures/Measures/test/SofaTest.cc000066400000000000000000000105111476623553700217500ustar00rootroot00000000000000//# SofaTest.cc: Wrapping of IAU SOFA Fortran routines and test class //# Copyright (C) 2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Include files #include "SofaTest.h" #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Constructors SofaTest::SofaTest() : n_p(0), sum_p(0), sq_p(0), max_p(-1e30), min_p(1e30), hstep_p(0), hsize_p(HISTO_WIDTH), histo_p(0) { hwidth_p = 2*hsize_p; histo_p = new Int[hwidth_p]; clear(); } SofaTest::SofaTest(const SofaTest &other) : n_p(0), sum_p(0), sq_p(0), max_p(-1e30), min_p(1e30), hstep_p(0), hsize_p(HISTO_WIDTH), histo_p(0) { copy(other); } SofaTest::~SofaTest() { delete [] histo_p; histo_p = 0; } // Operators SofaTest &SofaTest::operator=(const SofaTest &other) { if (this != &other) copy(other); return *this; } // Methods void SofaTest::clear() { n_p = 0; sum_p = 0; sq_p = 0; max_p = -1e30; min_p = 1e30; hstep_p = 0; hwidth_p = 2*hsize_p; for (uInt i=0; ihsize_p) { hstep_p *= 2.0; for (uInt i=0; i=0 && n=0 && jk) ? cnt[i] : k; n = Int(ceil(Double(k)/60.)); if (n==0) n=1; os << endl << n << " counts per step; " << step << " value." << endl; for (uInt i=0; i<41; i++) { if (i==19) os << " _"; else os << " |"; if (cnt[i] != 0) { for (Int j=0; j #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Wrapping of IAU SOFA Fortran routines and test class // // // // // // // The definition in this file enable the use of the IAU SOFA Fortran routines // in the C++ test routines. // By using the provided macro IAUR care is taken of SOFA prefixes // and probable extra underscores given by some compilers. // // For information on SOFA see the SOFA page at // IAU or at the currenthome of SOFA at // Rutherford // // The SofaTest class can be used to provide histogram of test data. // The resolution is defaulted to 500 steps, compressed to 40 in the output. // // // // // SofaTest dpsi; // Create an histogram class // // Loop over the following two statements to fill histogram // // Calculate a Double dpsival // dpsi.put(dpsival); // // Show the result // cout.precision(4); // cout << "Casacore dpsi(mas):" << endl; // dpsi.show(cout); // dpsi.showHisto(cout); // // The result will look like: // // Casacore dpsi (mas): // 5001 points were accumulated // with max = 1.529e-09, and min = -1.61e-09 // and an average of 5.066e-12 and a standard deviation of 2.42e-10 // // 23 counts per step; 0.0004 value. // |* // |* // |* // |* // |* // |* // |* // |* // |* // |* // |* // |* // |* // |* // |* // |* // |-* // |-* // |---* // |---* // |-----* // |---------* // |---------------------* // _--------------------------------------------------------* // |---------------------------------------------------------* // |----------------------* // |------------* // |------* // |----* // |--* // |-* // |-* // |* // |* // |* // |* // |* // |* // |* // |* // |* // |* // |* // |* // |* // // // // // To enable in-line testing of the Casacore Measures conversion routines. // // // // Nothing (I hope!) // // class SofaTest { public: // Constructors // Create an empty SofaTest class, ready for accumulation SofaTest(); // Copy SofaTest(const SofaTest &other); // Destructor ~SofaTest(); // Operators // Assign SofaTest &operator=(const SofaTest &other); // Methods // Clear the test class void clear(); // Accumulate statistics void put(const Double in); // Show statistics void show(ostream &os); void showHisto(ostream &os); private: // Histogram resolution static const uInt HISTO_WIDTH = 500; // Data // Count uInt n_p; // Sum values Double sum_p; // Sum squared Double sq_p; // Max found Double max_p; // Min found Double min_p; // Step in histogram Double hstep_p; // Histogram size (really (n-1)/2) uInt hsize_p; // Histogram width uInt hwidth_p; // Histogram Int *histo_p; // Methods // Copy object void copy(const SofaTest &other); }; //# Global function wraps // Global Fortran function wraps // #if !defined(NEED_FORTRAN_UNDERSCORES) #define NEED_FORTRAN_UNDERSCORES 1 #endif #if NEED_FORTRAN_UNDERSCORES #define IAUR(x) iau_##x##_ #else #define IAUR(x) iau_##x## #endif extern "C" void IAUR(cal2jd)(const Int &iy, const Int &im, const Int &id, Double &djm0, Double &djm, Int &j); extern "C" void IAUR(epj2jd)(const Double &epj, Double &djm0, Double &djm); extern "C" void IAUR(prec76)(const Double &ep01, const Double &ep02, const Double &ep11, const Double &ep12, Double &zeta, Double &z, Double &theta); extern "C" void IAUR(pmat76)(const Double &epoch1, const Double &epoch2, Double *rmatp); extern "C" void IAUR(nut80)(const Double &epoch1, const Double &epoch2, Double &dpsi, Double &deps); extern "C" void IAUR(nutm80)(const Double &epoch1, const Double &epoch2, Double *rmatn); extern "C" Double IAUR(obl80)(const Double &epoch1, const Double &epoch2); extern "C" void IAUR(pr00)(const Double &epoch1, const Double &epoch2, Double &dpsi, Double &deps); extern "C" void IAUR(bi00)(Double &dpsi, Double &deps, Double &dra); extern "C" void IAUR(bp00)(const Double &epoch1, const Double &epoch2, Double *rb, Double *rp, Double *rbp); extern "C" void IAUR(pnm80)(const Double &epoch1, const Double &epoch2, Double *rmatpn); extern "C" void IAUR(pn00a)(const Double &epoch1, const Double &epoch2, Double &dpsi, Double &deps, Double &epsa, Double *rb, Double *rp, Double *rbp, Double *rn, Double *rnpn); extern "C" void IAUR(pn00b)(const Double &epoch1, const Double &epoch2, Double &dpsi, Double &deps, Double &epsa, Double *rb, Double *rp, Double *rbp, Double *rn, Double *rnpn); extern "C" void IAUR(pr00)(const Double &ep01, const Double &ep02, Double &dpsipr, Double &depspr); extern "C" void IAUR(nut00b)(const Double &epoch1, const Double &epoch2, Double &dpsi, Double &deps); extern "C" void IAUR(nut00a)(const Double &epoch1, const Double &epoch2, Double &dpsi, Double &deps); extern "C" void IAUR(num00a)(const Double &epoch1, const Double &epoch2, Double *rn); extern "C" void IAUR(num00b)(const Double &epoch1, const Double &epoch2, Double *rn); extern "C" void IAUR(c2t00a)(const Double &tta, const Double &ttb, const Double &uta, const Double &utb, const Double &xp, const Double &yp, Double *rc2t); extern "C" Double IAUR(sp00)(const Double &date1, const Double &date2); extern "C" void IAUR(pom00)(const Double &xp, const Double &yp, const Double &sp, Double *rpom); extern "C" Double IAUR(gmst00)(const Double &uta, const Double &utb, const Double &tta, const Double &ttb); extern "C" Double IAUR(era00)(const Double &uta, const Double &utb); extern "C" Double IAUR(gmst82)(const Double &dj1, const Double &dj2); extern "C" Double IAUR(ee00a)(const Double &date1, const Double &date2); extern "C" Double IAUR(eect00)(const Double &date1, const Double &date2); extern "C" Double IAUR(eqeq94)(const Double &date1, const Double &date2); extern "C" void IAUR(pnm00a)(const Double &date1, const Double &date2, Double *rbpn); extern "C" void IAUR(c2teqx)(Double *rbpn, const Double &gst, Double *rpom, Double *rc2t); extern "C" void IAUR(rz)(const Double &psi, Double *r); extern "C" void IAUR(cr)(Double *r, Double *c); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/Measures/test/dM1950_2000.cc000066400000000000000000000227171476623553700215530ustar00rootroot00000000000000//# dM1950_2000.cc: This program demonstrates B1950<->J2000 conversion //# Copyright (C) 2007 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { void fk45(Double R1950, Double D1950, Double BEPOCH, Double &R2000, Double &D2000, Double X2000[], Vector V2000[]); void conB1950(const MVDirection &b1950, const MVDirection &j2000, Double epo=2000.0, MeasFrame *frame=0); void fk45(Double R1950, Double D1950, Double BEPOCH, Double &R2000, Double &D2000, Double X2000[3], Vector V2000[]) { Double W; // Position and position+velocity vectors Vector R0(3),A1(3),V1(3),V2(6),V3(3); // Radians per year to arcsec per century const Double PMF=100e0*60e0*60e0*360e0/(2.0*M_PI); // Functions // Double sla_EPJ,sla_EPB2D,sla_DRANRM // Vectors A and Adot, and matrix M (only half of which is needed here) Double A[3] = { -1.62557e-6, -0.31919e-6, -0.13843e-6}; Double AD[3] = { +1.245e-3, -1.580e-3, -0.659e-3}; Double EM[3][6] = { { +0.9999256782, +0.0111820610, +0.0048579479, -0.000551, +0.238514, -0.435623 }, { -0.0111820611, +0.9999374784, -0.0000271474, -0.238565, -0.002667, +0.012254 }, { -0.0048579477, -0.0000271765, +0.9999881997, +0.435739, -0.008541, +0.002117 } }; // Spherical to Cartesian R0 = MVPosition(Quantity(1.0,"m"),R1950,D1950).getValue(); V2000[0] = R0; //v // Adjust vector A to give zero proper motion in FK5 W=(BEPOCH-1950)/PMF; for (uInt I=0; I<3; ++I) A1[I]=A[I]+W*AD[I]; V2000[1] = A1; //v // Remove e-terms W=R0[1]*A1[1]+R0[2]*A1[2]+R0[0]*A1[0]; for (uInt I=0; I<3; ++I) V1[I]=R0[I]-A1[I]+W*R0[I]; V2000[2] = V1; //v // Convert position vector to Fricke system for (uInt I=0; I<6; ++I) { W=0; for (uInt J=0; J<3; ++J) W=W+EM[J][I]*V1[J]; V2[I]=W; }; for (uInt i=0; i<3; ++i) V2000[3][i] = V2[i]; //v for (uInt i=0; i<3; ++i) V2000[4][i] = V2[i+3]; //v // Allow for fictitious proper motion in FK4 W=(BEPOCH-2000)/PMF; /// needs b->D for (uInt I=0; I<3; ++I) V3[I]=V2[I]+W*V2[I+3]; V2000[5] = V3; // Revert to spherical coordinates for (uInt i=0; i<3;++i) X2000[i] = V3[i]; R2000 = MVPosition(V3).getLong(); D2000 = MVPosition(V3).getLat(); } void conB1950(const MVDirection &b1950, const MVDirection &j2000, Double epo, MeasFrame *frame) { // References MDirection::Ref j2000ref(MDirection::J2000); MDirection::Ref b1950ref(MDirection::B1950); if (frame) { MVEpoch time(((epo-2000.0)*MeasData::JDCEN/100.0+MeasData::MJD2000)); MEpoch tim(time); frame->set(tim); b1950ref.set(*frame); }; // Coordinates and conversion MDirection j2000m(j2000, j2000ref); MDirection b1950m(b1950, b1950ref); MDirection::Convert toj2000(b1950m, j2000ref); MDirection::Convert fromj2000(j2000m, b1950ref); MDirection jcoord(toj2000()); MDirection bcoord(fromj2000()); // Act cout << "----------------------------------------------------" << endl; if (frame) cout << "Conversion for epoch " << epo << endl; else cout << "Conversion for default epoch 2000.0" << endl; cout << "B1950: " << MVAngle::Format(MVAngle::TIME, 12u) << MVAngle(b1950.getLong()) << " " << MVAngle::Format(MVAngle::ANGLE, 11u) << MVAngle(b1950.getLat()) << endl; cout << "J2000: " << MVAngle::Format(MVAngle::TIME, 12u) << MVAngle(j2000.getLong()) << " " << MVAngle::Format(MVAngle::ANGLE, 11u) << MVAngle(j2000.getLat()) << endl; cout << "----------------------------------------------------" << endl; cout.precision(12); cout << "J2000in: " << j2000m.getAngle("deg") << endl; cout << " " << j2000m.getValue().getValue() << endl; cout << "B1950in: " << b1950m.getAngle("deg") << endl; cout << " " << b1950m.getValue().getValue() << endl; cout << "to J2000: " << toj2000().getAngle("deg") << endl; cout << " " << toj2000().getValue().getValue() << endl; cout << "J2000out-in: " << toj2000().getAngle("deg") -j2000m.getAngle("deg") << endl; cout << " " << toj2000().getValue().getValue() -j2000m.getValue().getValue() << endl; MDirection newcoord = fromj2000(toj2000()); cout << "back2 B1950: "<< newcoord.getAngle("deg") << endl; cout << " "<< newcoord.getValue().getValue() << endl; cout << "B1950out-in: " << newcoord.getAngle("deg") -b1950m.getAngle("deg") << endl; cout << " " << newcoord.getValue().getValue() -b1950m.getValue().getValue() << endl; // fk45 Vector xyz(3); Double R2000, D2000, X2000[3]; Vector V2000[20]; for (uInt i=0; i<20; ++i) V2000[i].resize(3, 0.0); fk45(b1950.getLong(), b1950.getLat(), epo , R2000, D2000, X2000, V2000); for (uInt i=0; i<3; ++i) xyz[i] = X2000[i]; cout << "fk45out: " << MVDirection(R2000, D2000).getAngle("deg") << endl; cout << " " << MVDirection(R2000, D2000).getValue() << endl; cout << "out-J2000in: " << MVDirection(R2000, D2000).getAngle("deg") - j2000m.getAngle("deg") << endl; cout << " " << MVDirection(R2000, D2000).getValue() - j2000m.getValue().getValue() << endl; cout << "out-J2000out:" << MVDirection(R2000, D2000).getValue() - toj2000().getValue().getValue() << endl; cout << " " << MVDirection(R2000, D2000).getAngle("deg") - toj2000().getAngle("deg") << endl; cout << "----------------------------------------------------" << endl; } } //# NAMESPACE CASACORE - END int main() { try { cout << "Demonstrate B1950<-> J2000" << endl; cout << "----------------------------------------------------" << endl; String epoch; // while (epoch != "B1950" && epoch != "J2000") { // cout << "Specify the base epoch (B1950, J2000) [B1950]: "; // The following and other flush() are necessary for cfront (although // theoretically cin should auto flush) // cout.flush(); // if (cin.peek() == '\n') { //cin.get(); //epoch = ""; // } else { //cin >> epoch; // }; // epoch.capitalize(); if (epoch.empty()) epoch = "B1950"; //}; Quantity ra; Quantity dec; MeasFrame frame; Quantity::read(ra, "13h28m49.657756"); Quantity::read(dec,"30d45m58.64060"); MVDirection b1950(ra, dec); Quantity::read(ra, "13h31m08.288048"); Quantity::read(dec,"30d30m32.95924"); MVDirection j2000(ra, dec); conB1950(b1950, j2000); conB1950(b1950, j2000, 2000.0, &frame); conB1950(b1950, j2000, 1979.9, &frame); Quantity::read(ra, "03h16m29.567289"); Quantity::read(dec,"41d19m51.91677"); b1950 = MVDirection(ra, dec); Quantity::read(ra, "03h19m48.160119"); Quantity::read(dec,"41d30m42.10389"); j2000 = MVDirection(ra, dec); conB1950(b1950, j2000); conB1950(b1950, j2000, 1979.9, &frame); conB1950(b1950, j2000, 2000.0, &frame); Quantity::read(ra, "13h28m49.657700"); Quantity::read(dec,"30d45m58.640000"); b1950 = MVDirection(ra, dec); Quantity::read(ra, "13h31m08.287984"); Quantity::read(dec,"30d30m32.958850"); j2000 = MVDirection(ra, dec); conB1950(b1950, j2000); conB1950(b1950, j2000, 1979.9, &frame); conB1950(b1950, j2000, 2000.0, &frame); Quantity::read(ra, "13h28m49.659"); Quantity::read(dec,"30d45m58.660"); b1950 = MVDirection(ra, dec); Quantity::read(ra, "13h31m08.288"); Quantity::read(dec,"30d30m32.959"); j2000 = MVDirection(ra, dec); conB1950(b1950, j2000); conB1950(b1950, j2000, 1979.9, &frame); conB1950(b1950, j2000, 2000.0, &frame); } catch (std::exception& x) { cout << x.what() << endl; } return(0); } casacore-3.7.1/measures/Measures/test/dMeasure.cc000066400000000000000000000077211476623553700217760ustar00rootroot00000000000000//# dMeasure.cc: This program demonstrates Measures to calculate filed rotation //# Copyright (C) 1995,1996,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include int main() { try { cout << "Demonstrate measure class to provide field rotations" << endl; cout << "----------------------------------------------------" << endl; String epoch; while (epoch != "B1950" && epoch != "J2000") { cout << "Specify the base epoch (B1950, J2000) [B1950]: "; // The following and other flush() are necessary for cfront (although // theoretically cin should auto flush) cout.flush(); if (cin.peek() == '\n') { cin.get(); epoch = ""; } else { cin >> epoch; }; epoch.capitalize(); if (epoch.empty()) epoch = "B1950"; }; MVDirection coord; MVDirection pole; MEpoch tim; MeasFrame frame(tim); MDirection::Ref appref(MDirection::APP, frame); MDirection mycoord(coord, appref); MDirection newcoord; MDirection::Convert toward; MDirection::Convert from; if (epoch == "J2000") { toward = MDirection::Convert(mycoord, MDirection::J2000); newcoord = MDirection(coord, MDirection::J2000); from = MDirection::Convert(newcoord, appref); } else { toward = MDirection::Convert(mycoord, MDirection::B1950); newcoord = MDirection(coord, MDirection::J2000); from = MDirection::Convert(newcoord, appref); }; Double ra, dec; while (True) { cout << "Specify RA in degrees: "; cout.flush(); if (cin.peek() == '\n') { break; }; cin >> ra; cout << "Specify DEC in degrees: "; cout.flush(); cin >> dec; cin.get(); coord = MVDirection(Quantity(ra,"deg"), Quantity(dec,"deg")); Double mytim; while (True) { cout << "Specify time in MJD: "; cout.flush(); if (cin.peek() == '\n') { cin.get(); break; }; cin >> mytim; cin.get(); tim = MEpoch(Quantity(mytim,"d")); frame.set(tim); MDirection ncoord(toward(coord)); cout << epoch << " coordinates: " << ncoord.getAngle("deg") << endl; MDirection npole(toward(pole)); cout << "Rotation angle: " << -ncoord.getValue().positionAngle(npole.getValue(), "deg") << endl; cout << "Apparent" << " coordinates: " << from(ncoord).getAngle("deg") << endl; MDirection apole(MVDirection(0.,0.,1.),appref); MDirection mpole(from(pole)); cout << "Rotation angle: " << from(ncoord).getValue().positionAngle(mpole.getValue(), "deg") << endl; }; }; } catch (std::exception& x) { cout << x.what() << endl; } return(0); } casacore-3.7.1/measures/Measures/test/dMeasure.in000066400000000000000000000000651476623553700220110ustar00rootroot00000000000000B1950 50 -60 12345 # Above 2 lines should be empty casacore-3.7.1/measures/Measures/test/dMeasure.out000066400000000000000000000006261476623553700222150ustar00rootroot00000000000000Demonstrate measure class to provide field rotations ---------------------------------------------------- Specify the base epoch (B1950, J2000) [B1950]: Specify RA in degrees: Specify DEC in degrees: Specify time in MJD: B1950 coordinates: [50.3041, -59.8021] deg Rotation angle: -0.487177 deg Apparent coordinates: [50, -60] deg Rotation angle: -0.487075 deg Specify time in MJD: Specify RA in degrees: casacore-3.7.1/measures/Measures/test/tEarthField.cc000066400000000000000000000115201476623553700224140ustar00rootroot00000000000000//# tMEarthMagnetic.cc: This program test Measure functions //# Copyright (C) 1998,1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include int main() { try { cout << "Test Earth Magnetic field values" << endl; cout << "--------------------------------------" << endl; MVTime dat(1998,5,18); MVPosition mvobs(Quantity(3828488.86, "m").getBaseValue(), Quantity(443253.42, "m").getBaseValue(), Quantity(5064977.78, "m").getBaseValue()); MPosition obs(mvobs); MeasFrame frame((MEpoch(MVEpoch(dat.day()))), obs); cout << "Date: " << dat.string(MVTime::YMD + MVTime::NO_TIME, 6) << endl; cout << "Position: " << obs.getValue().get() << endl; cout << " " << obs.getAngle("deg") << endl; cout << "----- IGRF coefficients" << endl; cout << "Field " << MeasTable::IGRF(dat.day()) << endl; cout << " " << MeasTable::IGRF(dat.day()).nelements() << endl; EarthField ef(EarthField::STANDARD, dat.day()); cout << "Result: " << ef(obs.getValue()) << endl; cout << "Derivatives: " << endl; for (Int i0=0; i0<3; i0++) { cout << " " << ef.derivative(obs.getValue())[i0] << endl; }; cout << "--------- From derivatives ----------" << endl; cout << "+10km X: " << ef((obs.getValue()+MVPosition(10000,0,0))) << endl; cout << "-10km X: " << ef((obs.getValue()+MVPosition(-10000,0,0))) << endl; cout << "+10km Y: " << ef((obs.getValue()+MVPosition(0,10000,0))) << endl; cout << "-10km Y: " << ef((obs.getValue()+MVPosition(0,-10000,0))) << endl; cout << "+10km Z: " << ef((obs.getValue()+MVPosition(0,0,10000))) << endl; cout << "-10km Z: " << ef((obs.getValue()+MVPosition(0,0,-10000))) << endl; cout << "--------- From scratch ----------" << endl; ef.init(EarthField::STANDARD, dat.day()); cout << "+10km X: " << ef((obs.getValue()+MVPosition(10000,0,0))) << endl; ef.init(EarthField::STANDARD, dat.day()); cout << "-10km X: " << ef((obs.getValue()+MVPosition(-10000,0,0))) << endl; ef.init(EarthField::STANDARD, dat.day()); cout << "+10km Y: " << ef((obs.getValue()+MVPosition(0,10000,0))) << endl; ef.init(EarthField::STANDARD, dat.day()); cout << "-10km Y: " << ef((obs.getValue()+MVPosition(0,-10000,0))) << endl; ef.init(EarthField::STANDARD, dat.day()); cout << "+10km Z: " << ef((obs.getValue()+MVPosition(0,0,10000))) << endl; ef.init(EarthField::STANDARD, dat.day()); cout << "-10km Z: " << ef((obs.getValue()+MVPosition(0,0,-10000))) << endl; cout << "--------- From refresh ----------" << endl; ef.refresh(); cout << "+10km X: " << ef((obs.getValue()+MVPosition(10000,0,0))) << endl; ef.refresh(); cout << "-10km X: " << ef((obs.getValue()+MVPosition(-10000,0,0))) << endl; ef.refresh(); cout << "+10km Y: " << ef((obs.getValue()+MVPosition(0,10000,0))) << endl; ef.refresh(); cout << "-10km Y: " << ef((obs.getValue()+MVPosition(0,-10000,0))) << endl; ef.refresh(); cout << "+10km Z: " << ef((obs.getValue()+MVPosition(0,0,10000))) << endl; ef.refresh(); cout << "-10km Z: " << ef((obs.getValue()+MVPosition(0,0,-10000))) << endl; cout << "------------------------------------------" << endl; } catch (std::exception& x) { cout << x.what() << endl; } return 0; } casacore-3.7.1/measures/Measures/test/tEarthField.out000066400000000000000000000060271476623553700226440ustar00rootroot00000000000000Test Earth Magnetic field values -------------------------------------- Date: 1998/05/18 Position: [6.36457e+06, 0.115264, 0.92034] [6.60417, 52.7316] deg ----- IGRF coefficients Field [-29643, -1746.35, 5225.1, -2245.68, 3068.92, -2444, 1674.19, -443.363, 1338.1, -2281.17, -238.789, 1251.09, 296.197, 728.974, -470.251, 934.804, 784.588, 269.152, 263.01, -233.234, -407.879, 112.384, 114.78, -304.516, -217.239, 351.595, 44.5156, 226.431, 169.656, -126.367, -136.32, -167.754, -44.4065, -14.2336, 106.528, 70.9014, 67.8097, -17.2699, 72.1834, 66.3996, -163.86, 65.718, -4.30624, -60.1592, 17.583, 0.797577, -91.2457, 41.263, 78.3495, -73.3495, -66.0311, 0.325257, -24.4602, 31.5761, 5.48444, 7.76645, 24, 5.95676, 15.5156, 7.52768, -24.9446, -1.46021, -5.86505, 24.5952, 6.40485, 11.6073, -8.15918, -21.3374, -8.25778, 8.33737, -15.7543, -21.9879, 9.06747, 15.3374, 6.67474, 9.58304, -6.95676, -15.2578, -7, -2.71799, 4.67474, 9.2699, -19.7976, 3, 13.9204, -8.92041, 12.3374, 6.85294, -6.13495, -8.60727, -8.2699, -1.33737, 8.2699, 9.52768, 4.19031, -3.55191, -8.13495, -8.13495, 4.21454, -2.7301, -6, 1.47232, 1.79758, 0, -3.39273, 4, -0.662628, 4.93253, 3.79758, -5.60727, 1.32526, -1.13495, 2, -2.60727, 4.46021, 0.460205, 0.52768, -2.13495, -0.742218, -7.2699, 1.82181, -1.14706, 0.0674743, -1.28201, 0.877166, 1.01211, -0.607269, -0.0674743, -1.75433, 0.0674743, 0.607269, -0.47232, -0.47232, 0.47232, -1.88928, 1.14706, -0.607269, 0.0674743, -0.809692, 0.809692, -1.28201, 2.69897, -0.607269, -1.48444, -0.202423, -0.269897, 0.134949, 0.202423, 0.607269, 1.68686, -0.134949, -1.75433, 0.607269, 0.47232, -0.337372, 0.202423, 0.202423, 0, -0.202423, 0, -0.269897, 0.202423, -0.0674743, -0.607269, -0.134949, -0.269897, -0.269897, 0.539795, -0.134949, -0.607269, -0.607269, 0.202423, 0.134949, 0.0674743, 1.21454, -0.269897, -0.269897, 0.877166, -0.674743, -0.269897, -0.0674743, 0.47232, 0.47232, -0.269897, 0.202423, 0.202423, 0.404846, -0.0674743, 0.202423, 0.269897, -0.134949, 0, -0.337372, 0.0674743, -0.607269] 195 Result: [41652.3, 1805.43, 24913.3] Derivatives: [-0.00694446, -0.000380301, -0.0174335] [-0.00170254, 0.00154398, -0.00145143] [-0.0174261, -0.000135646, -0.00201752] --------- From derivatives ---------- +10km X: [41582.8, 1801.62, 24738.9] -10km X: [41721.7, 1809.23, 25087.6] +10km Y: [41635.2, 1820.87, 24898.8] -10km Y: [41669.3, 1789.99, 24927.8] +10km Z: [41478, 1804.07, 24893.1] -10km Z: [41826.5, 1806.78, 24933.5] --------- From scratch ---------- +10km X: [41582.8, 1801.62, 24738.9] -10km X: [41721.2, 1809.24, 25088.3] +10km Y: [41635.2, 1820.87, 24898.8] -10km Y: [41668.9, 1790.05, 24927.4] +10km Z: [41478, 1804.07, 24893.1] -10km Z: [41827.3, 1806.75, 24933.2] --------- From refresh ---------- +10km X: [41582.8, 1801.62, 24738.9] -10km X: [41721.2, 1809.24, 25088.3] +10km Y: [41635.2, 1820.87, 24898.8] -10km Y: [41668.9, 1790.05, 24927.4] +10km Z: [41478, 1804.07, 24893.1] -10km Z: [41827.3, 1806.75, 24933.2] ------------------------------------------ casacore-3.7.1/measures/Measures/test/tEarthMagneticMachine.cc000066400000000000000000000244451476623553700244170ustar00rootroot00000000000000//# tEarthMagneticMachine.cc: This program tests the EarthMagneticMachine class //# Copyright (C) 1998,1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include int main() { try { cout << "Test Earth Magnetic field machine" << endl; cout << "--------------------------------------" << endl; MVTime dat(1998,5,18); MVPosition mvobs(Quantity(3828488.86, "m").getBaseValue(), Quantity(443253.42, "m").getBaseValue(), Quantity(5064977.78, "m").getBaseValue()); MPosition obs(mvobs); MeasFrame frame((MEpoch(MVEpoch(dat.day()))), obs); cout << "Date: " << dat.string(MVTime::YMD + MVTime::NO_TIME, 6) << endl; cout << "Position: " << obs.getValue().get() << endl; cout << " " << obs.getAngle("deg") << endl; cout << " " << obs.getValue().getLength("km") << endl; EarthField ef(EarthField::STANDARD, dat.day()); cout << "Result: " << ef(obs.getValue()) << endl; cout << "----------- H=0 -- El=90 ------" << endl; { MDirection::Ref mvref(MDirection::ITRF, frame); MVDirection mvd(obs.getValue()); EarthMagneticMachine fm(mvref, Quantum(0, "km"), frame); fm.calculate(mvd); cout << "LOS: " << fm.getLOSField() << endl; cout << "LOS: " << fm.getLOSField("G") << endl; cout << "Long: " << fm.getLong() << endl; cout << "Long: " << fm.getLong("deg") << endl; cout << "LOS: " << fm() << endl; cout << "LOS: " << fm("G") << endl; } cout << "----------- H=0 -- El=90 ------" << endl; { MDirection::Ref mvref(MDirection::AZEL, frame); MVDirection mvd(Quantity(0, "deg"), Quantity(90, "deg")); EarthMagneticMachine fm(mvref, Quantum(0, "km"), obs, MEpoch(MVEpoch(dat.day()))); fm.calculate(mvd); cout << "LOS: " << fm.getLOSField() << endl; cout << "LOS: " << fm.getLOSField("G") << endl; cout << "Long: " << fm.getLong() << endl; cout << "Long: " << fm.getLong("deg") << endl; cout << "LOS: " << fm() << endl; cout << "LOS: " << fm("G") << endl; } cout << "----------- Along field ------" << endl; { MDirection::Ref mvref(MDirection::AZEL, frame); Vector xvd(3); xvd(0) = 18312; xvd(1) = -381; xvd(2) = 45184; MVDirection mvd(xvd); EarthMagneticMachine fm(mvref, Quantum(0, "km"), obs, MEpoch(MVEpoch(dat.day()))); fm.calculate(mvd); cout << "LOS: " << fm.getLOSField() << endl; cout << "LOS: " << fm.getLOSField("G") << endl; cout << "Long: " << fm.getLong() << endl; cout << "Long: " << fm.getLong("deg") << endl; cout << "LOS: " << fm() << endl; cout << "LOS: " << fm("G") << endl; } cout << "----------- Ha=0 -- Dec= 52.7316 ------" << endl; { MDirection::Ref mvref(MDirection::HADEC, frame); MVDirection mvd(Quantity(0, "deg"), Quantity( 52.7316, "deg")); EarthMagneticMachine fm(mvref, Quantum(0, "km"), obs, MEpoch(MVEpoch(dat.day()))); fm.calculate(mvd); cout << "LOS: " << fm.getLOSField() << endl; cout << "LOS: " << fm.getLOSField("G") << endl; cout << "Long: " << fm.getLong() << endl; cout << "Long: " << fm.getLong("deg") << endl; cout << "LOS: " << fm() << endl; cout << "LOS: " << fm("G") << endl; } cout << "----------- H=0 -- El=45 ------" << endl; { MDirection::Ref mvref(MDirection::AZEL, frame); MVDirection mvd(Quantity(0, "deg"), Quantity(45, "deg")); EarthMagneticMachine fm(mvref, Quantum(0, "km"), frame); fm.calculate(mvd); cout << "LOS: " << fm.getLOSField() << endl; cout << "LOS: " << fm.getLOSField("G") << endl; cout << "Long: " << fm.getLong() << endl; cout << "Long: " << fm.getLong("deg") << endl; cout << "LOS: " << fm() << endl; cout << "LOS: " << fm("G") << endl; } cout << "----------- H=200 -- El=45 ------" << endl; { MDirection::Ref mvref(MDirection::AZEL, frame); MVDirection mvd(Quantity(0, "deg"), Quantity(45, "deg")); EarthMagneticMachine fm(mvref, Quantum(200, "km"), frame); fm.calculate(mvd); cout << "LOS: " << fm.getLOSField() << endl; cout << "LOS: " << fm.getLOSField("G") << endl; cout << "Long: " << fm.getLong() << endl; cout << "Long: " << fm.getLong("deg") << endl; cout << "LOS: " << fm() << endl; cout << "LOS: " << fm("G") << endl; } cout << "----------- H=200 -- El=0 -- N ----" << endl; { MDirection::Ref mvref(MDirection::AZEL, frame); MVDirection mvd(Quantity(0, "deg"), Quantity(0, "deg")); EarthMagneticMachine fm(mvref, Quantum(200, "km"), frame); fm.calculate(mvd); cout << "LOS: " << fm.getLOSField() << endl; cout << "LOS: " << fm.getLOSField("G") << endl; cout << "Long: " << fm.getLong() << endl; cout << "Long: " << fm.getLong("deg") << endl; cout << "LOS: " << fm() << endl; cout << "LOS: " << fm("G") << endl; } cout << "----------- H=200 -- El=5 -- E ----" << endl; { MDirection::Ref mvref(MDirection::AZEL, frame); MVDirection mvd(Quantity(90, "deg"), Quantity(5, "deg")); EarthMagneticMachine fm(mvref, Quantum(200, "km"), frame); fm.calculate(mvd); cout << "LOS: " << fm.getLOSField() << endl; cout << "LOS: " << fm.getLOSField("G") << endl; cout << "Long: " << fm.getLong() << endl; cout << "Long: " << fm.getLong("deg") << endl; cout << "LOS: " << fm() << endl; cout << "LOS: " << fm("G") << endl; } cout << "----------- H=200 -- El=5 -- W ----" << endl; { MDirection::Ref mvref(MDirection::AZEL, frame); MVDirection mvd(Quantity(-90, "deg"), Quantity(5, "deg")); EarthMagneticMachine fm(mvref, Quantum(200, "km"), frame); fm.calculate(mvd); cout << "LOS: " << fm.getLOSField() << endl; cout << "LOS: " << fm.getLOSField("G") << endl; cout << "Long: " << fm.getLong() << endl; cout << "Long: " << fm.getLong("deg") << endl; cout << "LOS: " << fm() << endl; cout << "LOS: " << fm("G") << endl; cout << "------------- recalculate ----------" << endl; fm.reCalculate(); cout << "LOS: " << fm(mvd, "G") << endl; cout << "------------- use set() ------------" << endl; EarthMagneticMachine fm0; fm0.set(mvref); fm0.set(Quantity(200, "km")); fm0.set(frame); fm0.calculate(mvd); cout << "LOS: " << fm0("G") << endl; EarthMagneticMachine fm1; fm1 = fm0; fm1.calculate(mvd); cout << "LOS: " << fm1("G") << endl; cout << "LOS: " << fm1(mvd) << endl; cout << "LOS: " << fm1(mvd, "G") << endl; cout << "Long: " << fm1.getLong(mvd) << endl; cout << "Long: " << fm1.getLong(mvd, "deg") << endl; cout << "Field: " << fm1.getField() << endl; cout << "Field: " << fm1.getField(mvd) << endl; cout << "Pos: " << fm1.getPosition() << endl; cout << "Pos: " << fm1.getPosition(mvd) << endl; EarthMagneticMachine fm2(fm1); fm2.calculate(mvd); cout << "LOS: " << fm2("G") << endl; cout << "------------- iterate height ------------" << endl; Quantum qhgt(200, "km"); cout << "LOS: " << fm1("G") << endl; cout << "LOS: " << fm1(qhgt) << endl; cout << "LOS: " << fm1(qhgt, "G") << endl; cout << "Long: " << fm1.getLong() << endl; cout << "Long: " << fm1.getLong("deg") << endl; cout << "Field: " << fm1.getField() << endl; cout << "Pos: " << fm1.getPosition() << endl; qhgt = Quantum(190, "km"); cout << "LOS 190: " << fm1(qhgt, "G") << endl; qhgt = Quantum(200, "km"); cout << "LOS 200: " << fm1(qhgt, "G") << endl; qhgt = Quantum(210, "km"); cout << "LOS 210: " << fm1(qhgt, "G") << endl; } } catch (std::exception& x) { cout << x.what() << endl; } return 0; } casacore-3.7.1/measures/Measures/test/tEarthMagneticMachine.out000066400000000000000000000052011476623553700246260ustar00rootroot00000000000000Test Earth Magnetic field machine -------------------------------------- Date: 1998/05/18 Position: [6.36457e+06, 0.115264, 0.92034] [6.60417, 52.7316] deg 6364.57 km Result: [41652.3, 1805.43, 24913.3] ----------- H=0 -- El=90 ------ LOS: 45007 LOS: 0.45007 G Long: 0.115264 Long: 6.60417 deg LOS: 45007 LOS: 0.45007 G ----------- H=0 -- El=90 ------ LOS: 45007 LOS: 0.45007 G Long: 0.115264 Long: 6.60417 deg LOS: 45007 LOS: 0.45007 G ----------- Along field ------ LOS: 34970.8 LOS: 0.349708 G Long: 0.115264 Long: 6.60417 deg LOS: 34970.8 LOS: 0.349708 G ----------- Ha=0 -- Dec= 52.7316 ------ LOS: 45007 LOS: 0.45007 G Long: 0.115264 Long: 6.60417 deg LOS: 45007 LOS: 0.45007 G ----------- H=0 -- El=45 ------ LOS: 19092.4 LOS: 0.190924 G Long: 0.115264 Long: 6.60417 deg LOS: 19092.4 LOS: 0.190924 G ----------- H=200 -- El=45 ------ LOS: 19606.3 LOS: 0.196063 G Long: 0.115264 Long: 6.60417 deg LOS: 19606.3 LOS: 0.196063 G ----------- H=200 -- El=0 -- N ---- LOS: 862.719 LOS: 0.00862719 G Long: 0.115264 Long: 6.60417 deg LOS: 862.719 LOS: 0.00862719 G ----------- H=200 -- El=5 -- E ---- LOS: 3048.57 LOS: 0.0304857 G Long: 0.39911 Long: 22.8673 deg LOS: 3048.57 LOS: 0.0304857 G ----------- H=200 -- El=5 -- W ---- LOS: 7558.25 LOS: 0.0755825 G Long: -0.168581 Long: -9.659 deg LOS: 7558.25 LOS: 0.0755825 G ------------- recalculate ---------- LOS: 0.0755825 G ------------- use set() ------------ LOS: 0.0755825 G LOS: 0.0755825 G LOS: 7558.25 LOS: 0.0755825 G Long: -0.168581 Long: -9.659 deg Field: [38708.2, 375.482, 21097.9] Field: [38708.2, 375.482, 21097.9] Pos: [4.01992e+06, -684178, 5.14449e+06] Pos: [4.01992e+06, -684178, 5.14449e+06] LOS: 0.0755825 G ------------- iterate height ------------ LOS: 0.0755825 G LOS: 7558.25 LOS: 0.0755825 G Long: -0.168581 Long: -9.659 deg Field: [38708.2, 375.482, 21097.9] Pos: [4.01992e+06, -684178, 5.14449e+06] LOS 190: 0.0755537 G LOS 200: 0.0755825 G LOS 210: 0.0756108 G casacore-3.7.1/measures/Measures/test/tIAU2000.cc000066400000000000000000000274651476623553700213440ustar00rootroot00000000000000//# tIAU2000.cc: Test the IAU2000 conversions against SOFA library //# Copyright (C) 2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "SofaTest.h" #include // Print separation line of given (default 75) length void SEPAR(const uInt l=75) { cout << String(l, '-') << endl; } // Fill RotMat with Fortran Matrix RotMatrix fillRM(const Double rm[3][3]) { RotMatrix trm; for (uInt i=0; i<3; ++i) { for (uInt j=0; j<3; ++j) trm(i,j) = rm[i][j]; }; return trm; } // Check rotation angle around arbitrary axis between two matrices (mas) Double checkRot(const RotMatrix &rm1, RotMatrix rm2) { rm2.transpose(); RotMatrix r = rm1 * rm2; Double x, y, z, s, c, phi; x = r(1,2) - r(2,1); y = r(2,0) - r(0,2); z = r(0,1) - r(1,0); s = sqrt(x*x + y*y + z*z); if (s != 0.) { c = r(0,0) + r(1,1) + r(2,2) - 1.; phi = atan2(s, c); return phi/C::arcsec; }; return 0.; } int main() { try { // Init cout.precision(12); cout << "Test IAU2000 coordinate conversions ..." << endl; SEPAR(); // Default date Time t0(2003, 3, 1); Double MJD0 = 2400000.5; Double tJD = t0.julianDay(); Double tMJD = t0.modifiedJulianDay(); cout << "A default date of 2003/03/01 is used (JD " << tJD << ", MJD " << tMJD << ")" << endl; SEPAR(); // Precession and nutation cout << "Precession" << endl; SEPAR(); Double padpsi, padeps, paepsa, pbdpsi, pbdeps, pbepsa; Double parb[3][3], parp[3][3], parpb[3][3], parn[3][3], parpn[3][3]; Double pbrb[3][3], pbrp[3][3], pbrpb[3][3], pbrn[3][3], pbrpn[3][3]; Precession p00a(Precession::IAU2000A); Precession p00b(Precession::IAU2000B); IAUR(pn00a)(MJD0, tMJD, padpsi, padeps, paepsa, &parb[0][0], &parp[0][0], &parpb[0][0], &parn[0][0], &parpn[0][0]); IAUR(pn00b)(MJD0, tMJD, pbdpsi, pbdeps, pbepsa, &pbrb[0][0], &pbrp[0][0], &pbrpb[0][0], &pbrn[0][0], &pbrpn[0][0]); RotMatrix carm(p00a(tMJD)); RotMatrix cbrm(p00b(tMJD)); RotMatrix parm = fillRM(parp); RotMatrix pbrm = fillRM(pbrp); RotMatrix nbparm = fillRM(parpb); cout << "SOFA precession 2000A and bias:\n" << nbparm << endl; cout << "Casacore precession and bias:\n" << MeasTable::frameBias00()*carm << endl; cout << "SOFA precession (no bias):\n" << parm << endl; cout << "Casacore precession (no bias):\n" << carm << endl; Double depspr, dpsipr; IAUR(pr00)(MJD0, tMJD, dpsipr, depspr); cout << "SOFA precession corrections: " << dpsipr << ", " << depspr << endl; cout << "Casacore precession corrections: " << MeasTable::precRate00(0)*(tMJD-MeasData::MJD2000)/MeasData::JDCEN << ", " << MeasTable::precRate00(1)*(tMJD-MeasData::MJD2000)/MeasData::JDCEN << endl; RotMatrix pparb = fillRM(parb); cout << "\nSOFA 2000A bias matrix:\n" << pparb << endl; cout << "Casacore 2000A bias matrix:\n" << MeasTable::frameBias00() << endl; cout.precision(9); cout.setf(ios::fixed); cout << "Difference (arcsec) SOFA 2000A - Casacore nobias: " << checkRot(parm, carm) << endl; cout << "Difference (arcsec) SOFA 2000A - Casacore bias: " << checkRot(nbparm, MeasTable::frameBias00()*carm) << endl; Double fbrm[3][3], prrm[3][3], fbprrm[3][3]; IAUR(bp00)(MJD0, tMJD, &fbrm[0][0], &prrm[0][0], &fbprrm[0][0]); RotMatrix prm = fillRM(prrm); cout << "Difference (arcsec) SOFA 2000A - Casacore nobias: " << checkRot(prm, carm) << endl; cout << "(The above must all 3 be .002 uas)" << endl; SEPAR(); Nutation n00a(Nutation::IAU2000A); Nutation n00b(Nutation::IAU2000B); RotMatrix narm = fillRM(parn); RotMatrix nbrm = fillRM(pbrn); RotMatrix cnarm(n00a(tMJD)); RotMatrix cnbrm(n00b(tMJD)); cout << "Nutation" << endl; SEPAR(); cout.precision(9); cout.setf(ios::fixed); cout << "Difference (arcsec) SOFA 2000A - SOFA 2000B: " << checkRot(narm, nbrm) << endl; cout << "Difference (arcsec) Casacore 2000A - Casacore 2000B: " << checkRot(cnarm, cnbrm) << endl; cout << "(The above two differences are 0.512mas (note next note))" << endl; cout << "Difference (arcsec) SOFA 2000A - Casacore 2000A: " << checkRot(narm, cnarm) << endl; cout << "Difference (arcsec) SOFA 2000B - Casacore 2000B: " << checkRot(nbrm, cnbrm) << endl; cout << "(This should be 0.422 uas, due to different definition of\n" "\tthe fundamental arguments)" << endl; cout << "Equation of equinoxes (IAU2000A)(SOFA, Casacore, diff): " << endl; cout << " " << IAUR(ee00a)(MJD0, tMJD) << ", " << n00a.eqox(tMJD) << ", " << IAUR(ee00a)(MJD0, tMJD) - n00a.eqox(tMJD) << endl; SEPAR(); Double y,tta,ttb,uta,utb; { cout << "SOFA method comparisons ..." << endl; SEPAR(); cout.precision(9); cout.setf(ios::fixed); Double dat = 32; Double dut1 = -0.3; Double dtt = 32.184 + dat -dut1; Double rmceo[3][3],rmequ[3][3],rmold[3][3]; y=1935.; IAUR(epj2jd)(y,tta,ttb); uta=tta; utb=ttb-dtt/86400.; Double xp=0; Double yp=0; IAUR(c2t00a)(tta,ttb,uta,utb,xp,yp,&rmceo[0][0]); Double sp = IAUR(sp00)(tta,ttb); Double rpom[3][3], gst, rbpn[3][3]; IAUR(pom00)(xp,yp,sp,&rpom[0][0]); gst = IAUR(gmst00)(uta,utb,tta,ttb) + IAUR(ee00a)(tta,ttb); IAUR(pnm00a)(tta,ttb,&rbpn[0][0]); IAUR(c2teqx)(&rbpn[0][0],gst,&rpom[0][0],&rmequ[0][0]); Double rm[3][3]; IAUR(pnm80)(tta,ttb,&rm[0][0]); gst = IAUR(gmst82)(uta,utb)+ IAUR(eqeq94)(tta,ttb); IAUR(rz)(gst, &rm[0][0]); IAUR(cr)(&rm[0][0],&rmold[0][0]); RotMatrix rm1 = fillRM(rmceo); RotMatrix rm2 = fillRM(rmequ); RotMatrix rm3 = fillRM(rmold); cout << "Difference (arcsec) SOFA CEO and Equinox based in 1935: " << checkRot(rm1, rm2) << endl; cout << "Difference (arcsec) SOFA CEO based and old J2000 in 1935: " << checkRot(rm1, rm3) << endl; cout << "(The above should be 0.119 uas and 65.034 mas respectively)" << endl; SEPAR(); } { cout << "IAU2000A/B comparisons ..." << endl; SEPAR(); uInt iau2000_reg = AipsrcValue::registerRC(String("measures.iau2000.b_use"), False); uInt iau2000a_reg = AipsrcValue::registerRC(String("measures.iau2000.b_use2000a"), False); cout << "Registrations old: " << iau2000_reg << ", " << iau2000a_reg << endl; MDirection md(Quantity(30., "deg"), Quantity(50., "deg"), MDirection::J2000); MEpoch ep(Quantity(50083.,"d")); MeasFrame frame(ep); MDirection::Convert mcv(md, MDirection::Ref(MDirection::APP, frame)); AipsrcBool::set(iau2000_reg, False); AipsrcBool::set(iau2000a_reg, False); cout << "New J2000 " << AipsrcBool::get(iau2000_reg) << ", " << "J2000A " << AipsrcBool::get(iau2000a_reg) << endl; cout << mcv() << endl; MDirection mdcv = mcv(); AipsrcBool::set(iau2000_reg, True); MDirection::Convert mcv1(md, MDirection::Ref(MDirection::APP, frame)); cout << "New J2000 " << AipsrcBool::get(iau2000_reg) << ", " << "J2000A " << AipsrcBool::get(iau2000a_reg) << endl; cout << mcv1() << endl; cout << "Difference: " << (mcv1().getValue().getValue() - mdcv.getValue().getValue())*200000. << endl; AipsrcBool::set(iau2000a_reg, True); MDirection::Convert mcv2(md, MDirection::Ref(MDirection::APP, frame)); cout << "New J2000 " << AipsrcBool::get(iau2000_reg) << ", " << "J2000A " << AipsrcBool::get(iau2000a_reg) << endl; cout << mcv2() << endl; cout << "Difference: " << (mcv2().getValue().getValue() - mdcv.getValue().getValue())*200000. << endl; SEPAR(); } { cout << "Test of some details ..." << endl; SEPAR(); Double era = IAUR(era00)(uta,utb); Double gmst = IAUR(gmst00)(uta,utb,tta,ttb); Double sp = IAUR(sp00)(tta,ttb); Double eect = IAUR(eect00)(tta,ttb); Nutation nuta(Nutation::IAU2000A); cout << "UT: " << (uta-2451545.0)+utb << ", " << utb-MeasData::MJD2000 << endl; cout << "TT: " << (tta-2451545.0)+ttb << ", " << ttb-MeasData::MJD2000 << endl; cout << "s' (Sofa, Casacore, diff): " << sp << ", " << MeasTable::sprime00(ttb) << ", " << sp - MeasTable::sprime00(ttb) << endl; cout << "ERA (Sofa, Casacore, diff): " << era << ", " << MeasTable::ERA00(utb) << ", " << era - MeasTable::ERA00(utb) << endl; cout << "GMST (Sofa, Casacore, diff): " << gmst << ", " << fmod((ttb+6713.)*C::_2pi + MeasTable::GMST00(utb, ttb), C::_2pi) << ", " << gmst - fmod((ttb+6713.)*C::_2pi + MeasTable::GMST00(utb, ttb), C::_2pi) << endl; cout << "GMST82 (GMST82, GMST00, diff): " << utb+MeasTable::GMST0(utb)/MeasData::SECinDAY + 6713. << ", " << ttb+MeasTable::GMST00(utb, ttb)/C::_2pi + 6713. <<", " << utb+MeasTable::GMST0(utb)/MeasData::SECinDAY - ttb-MeasTable::GMST00(utb, ttb)/C::_2pi << endl; cout << "EqEqCT00 (Sofa, Casacore, diff): " << eect << ", " << nuta.eqoxCT(ttb) << ", " << eect-nuta.eqoxCT(ttb) << endl; SEPAR(); } { cout << "Test Aipsrc value cross talk ..." << endl; SEPAR(); uInt iau2000_r = AipsrcValue::registerRC(String("measures.iau2000.b_use"), False); uInt iau2000a_r = AipsrcValue::registerRC(String("measures.iau2000.b_use2000a"), False); cout << "Registrations now: " << iau2000_r << ", " << iau2000a_r << endl; cout << "New J2000 " << AipsrcBool::get(iau2000_r) << ", " << "J2000A " << AipsrcBool::get(iau2000a_r) << endl; SEPAR(); } } catch (std::exception x) { cerr << x.what() << endl; cout << "FAIL" << endl; return 1; } catch (...) { cerr << "Exception not derived from AipsError" << endl; cout << "FAIL" << endl; return 2; }; cout << "OK" << endl; return 0; } casacore-3.7.1/measures/Measures/test/tMBaseline.cc000066400000000000000000000200621476623553700222450ustar00rootroot00000000000000//# tMBaseline.cc: This program tests MBaseline class //# Copyright (C) 1998,1999,2000,2002,2007 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main() { try { cout << "Test measure class MBaseline" << endl; cout << "--------------------------------------" << endl; cout << endl << "MBaseline state transition matrix:\n" << endl; cout << MCBaseline::showState() << endl; MEpoch tbm(Quantity(50927.92931, "d")); MPosition pos(MVPosition(-4750915.84032, 2792906.17778, -3200483.75028), MPosition::ITRF); MeasFrame mf(tbm, pos); mf.set(MDirection(Quantity(10, "deg"), Quantity(80, "deg"), MDirection::HADEC)); MVBaseline mvb0(100 ,10, 1e-8); // to stop Intel problems cout << "Baseline: " << mvb0 << endl; MBaseline::Ref mbref0(MBaseline::ITRF, mf); MBaseline mb0(mvb0, mbref0); cout << "Baseline: " << mb0 << endl; cout << "Baseline reference: " << mbref0 << endl; MBaseline::Ref mbref1(MBaseline::J2000); cout << "Baseline reference: " << mbref1 << endl; cout << "Test Baseline conversion ..." << endl; cout << "--------------------------------------" << endl; MBaseline::Convert bconv(mb0, mbref1); cout << "Converted " << mb0 << endl << " to " << mbref1 << endl << " as " << bconv() << endl; MBaseline::Convert bconvb(mbref1, mbref0); if (allNearAbs(mb0.getValue().getValue(), bconvb(bconv()).getValue().getValue(), 1e-7)) { cout << "Back " << mb0 << " : ok" << endl; } else { cout << "Back " << mb0 << " : not ok" << endl << " as " << bconvb(bconv()) << endl; }; cout << "--------------------------------------" << endl; cout << "Testing all conversions forward/backward" << endl; Bool isok = True; Vector tvec(3); tvec = 0.0; for (uInt i=MBaseline::J2000; i > vq(3); vq = Quantity(23, "m"); x.putValue(vq); cout << "putValue: " << vq << ", " << x << endl; cout << "BaselineAngle: " << x.BaselineAngle(mvb0) << endl; cout << "BaselineAngle: " << x.BaselineAngle(mvb0, "deg") << endl; cout << "get: " << x.get() << endl; cout << "getRecordValue: " << x.getRecordValue() << endl; cout << "separation: " << x.separation(mvb0) << endl; cout << "separation: " << x.separation(mvb0, "deg") << endl; cout << "crossProduct: " << x.crossProduct(mvb0) << endl; cout << "getAngle: " << x.getAngle() << endl; cout << "getAngle: " << x.getAngle("deg") << endl; cout << "getlength: " << x.getLength("cm") << endl; cout << "radius: " << x.radius() << endl; cout << "getXRecordValue:" << x.getXRecordValue() << endl; Vector x1(3); x1(0) = 30; x1(1) = 40; x1(2) = 0; x.putVector(x1); cout << "putVector: " << x1 << ", " << x << endl; MVBaseline x2(vq); cout << "VQ constructor: " << x2 << endl; cout << "Pos constructor:" << MVBaseline(x, x2) << endl; cout << "Q constructor: " << MVBaseline(Quantity(50, "m")) << endl; cout << "QV constructor: " << MVBaseline(x2.getAngle()) << endl; cout << "QV constructor: " << MVBaseline(Quantity(34,"m"), x2.getAngle()) << endl; cout << "V constructor: " << MVBaseline(x1) << endl; cout << "D constructor: " << MVBaseline(Double(78)) << endl; cout << "operator+: " << x+x2 << endl; cout << "operator-: " << x-x2 << endl; cout << "operator-pre-: " << -x2 << endl; RotMatrix rm(Euler(25, 1, 0, 0)); cout << "operator*: " << x2*rm << endl; cout << "operator*: " << x2*2 << endl; MVBaseline::assure(x); cout << "assure: " << "ok" << endl; cout << "getLength: " << x.getLength() << endl; cout << "operator*: " << x*x1 << endl; cout << "operator* " << x*x2 << endl; cout << "operator*: " << x1*x << endl; MeasValue *xc = x.clone(); cout << "clone: " << *xc << endl; cout << "getVector: " << x.getVector() << endl; cout << "near: " << x.near(x2) << endl; cout << "near: " << x.near(x2, Quantity(1, "deg")) << endl; cout << "nearAbs: " << x.nearAbs(x2) << endl; cout << "!=: " << (x != x2) << endl; cout << "==: " << (x == x2) << endl; cout << "All MVBaseline functions: ok" << endl; cout << "----------------------------" << endl; delete xc; } cout << "Exercise all MBaseline function" << endl; { MBaseline mb(mvb0, MBaseline::B1950); String s0("azel"); MBaseline::Types tp; MBaseline::Ref mr; cout << "getType: " << MBaseline::getType(tp, s0) << ", "; // next () to stop egcs warning cout << (uInt)tp << endl; cout << "giveMe: " << mb.giveMe(mr, s0) << ", "; cout << mr << endl; cout << "setRefString: " << mb.setRefString("hadec") << ", "; cout << mb << endl; MBaseline::assure(mb); cout << "assure: " << "ok" << endl; Measure *mbc = mb.clone(); cout << "clone: " << *mbc << endl; cout << "get: " << mb.get("cm") << endl; cout << "getAngle: " << mb.getAngle("deg") << endl; cout << "getDefaultType: " << mb.getDefaultType() << endl; cout << "getRefString: " << mb.getRefString() << endl; cout << "All MBaseline functions: ok" << endl; cout << "---------------------------" << endl; delete mbc; } } catch (std::exception& x) { cout << x.what() << endl; } return 0; } casacore-3.7.1/measures/Measures/test/tMBaseline.out000066400000000000000000000133511476623553700224720ustar00rootroot00000000000000Test measure class MBaseline -------------------------------------- MBaseline state transition matrix: | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ---------------------------------------------------------------------- 0| -- 10 10 18 4 5 4 4 2 18 18 18 18 18 18 34 10 10 2 18 18 47 | 1 1 14 4 5 4 4 8 14 14 14 14 14 14 15 1 1 8 14 14 21 1| 12 -- 13 12 12 12 12 12 12 12 12 12 12 12 12 12 36 13 12 12 12 12 | 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 16 2 0 0 0 0 2| 16 16 -- 16 16 16 16 16 16 16 16 16 16 16 16 16 16 38 16 16 16 16 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 17 1 1 1 1 3| 32 32 32 -- 21 21 21 21 32 22 22 22 22 22 32 32 32 32 32 22 22 32 | 14 14 14 4 4 4 4 14 20 20 20 20 20 14 14 14 14 14 20 20 14 4| 6 6 6 20 -- 8 11 11 3 20 20 20 20 20 6 6 6 6 3 20 20 6 | 0 0 0 3 5 6 6 8 3 3 3 3 3 0 0 0 0 8 3 3 0 5| 7 7 7 9 9 -- 9 9 7 9 9 9 9 9 7 7 7 7 7 9 9 7 | 0 0 0 4 4 4 4 0 4 4 4 4 4 0 0 0 0 0 4 4 0 6| 14 14 14 14 14 14 -- 15 14 14 14 14 14 14 14 14 14 14 14 14 14 14 | 4 4 4 4 4 4 7 4 4 4 4 4 4 4 4 4 4 4 4 4 4 7| 17 17 17 17 17 17 17 -- 17 17 17 17 17 17 17 17 17 17 17 17 17 17 | 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 8| 0 0 0 0 1 0 1 1 -- 0 0 0 0 0 0 0 0 0 40 0 0 0 | 0 0 0 0 4 0 4 4 0 0 0 0 0 0 0 0 0 18 0 0 0 9| 27 27 27 27 27 27 27 27 27 -- 23 23 24 24 27 27 27 27 27 43 27 27 | 20 20 20 20 20 20 20 20 20 10 10 12 12 20 20 20 20 20 19 20 20 10| 25 25 25 25 25 25 25 25 25 25 -- 28 25 25 25 25 25 25 25 25 25 25 | 9 9 9 9 9 9 9 9 9 9 11 9 9 9 9 9 9 9 9 9 9 11| 30 30 30 30 30 30 30 30 30 30 30 -- 30 30 30 30 30 30 30 30 30 30 | 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 12| 26 26 26 26 26 26 26 26 26 26 26 26 -- 29 26 26 26 26 26 26 26 26 | 9 9 9 9 9 9 9 9 9 9 9 9 13 9 9 9 9 9 9 9 9 13| 31 31 31 31 31 31 31 31 31 31 31 31 31 -- 31 31 31 31 31 31 31 31 | 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 14| 19 19 19 33 19 19 19 19 19 33 33 33 33 33 -- 19 19 19 19 33 33 19 | 0 0 0 3 0 0 0 0 0 3 3 3 3 3 0 0 0 0 3 3 0 15| 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 -- 35 35 35 35 35 35 | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 16| 37 37 37 37 37 37 37 37 37 37 37 37 37 37 37 37 -- 37 37 37 37 37 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 17| 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 -- 39 39 39 39 | 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 18| 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 -- 41 41 41 | 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 19| 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 -- 42 42 | 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 20| 45 45 45 45 45 45 45 45 45 44 44 44 44 44 45 45 45 45 45 44 -- 45 | 3 3 3 3 3 3 3 3 3 9 9 9 9 9 3 3 3 3 3 9 3 21| 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 -- | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Baseline: [100, 10, 1e-08] Baseline: Baseline: [100, 10, 1e-08] Baseline reference: Reference for an Baseline with Type: ITRF, Frame: Epoch: 50927::22:18:12.3840 (TDB = 50927.9, UT1 = 50927.9, TT = 50927.9) Position: [-4.75092e+06, 2.79291e+06, -3.20048e+06] (Longitude = 2.61014 Latitude = -0.526138) Direction: [0.17101, 0.0301537, 0.984808] (J2000 = [-33.1687, 80.0127] deg) Baseline reference: Reference for an Baseline with Type: J2000 Test Baseline conversion ... -------------------------------------- Converted Baseline: [100, 10, 1e-08] to Reference for an Baseline with Type: J2000 as Baseline: [-97.9137, -22.6474, -0.0224705] Back Baseline: [100, 10, 1e-08] : ok -------------------------------------- Testing all conversions forward/backward All forward/backward Baseline conversions: ok ------------------------------------ Exercise all MVBaseline function putValue: [23 m, 23 m, 23 m], [23, 23, 23] BaselineAngle: -2.1853 BaselineAngle: -125.209 deg get: [39.8372, 0.785398, 0.61548] getRecordValue: [0.785398 rad, 0.61548 rad, 39.8372 m] separation: 3.14159 separation: 180 deg crossProduct: [-230, 2300, -2070] getAngle: [0.785398, 0.61548] rad getAngle: [45, 35.2644] deg getlength: 3983.72 cm radius: 39.8372 getXRecordValue:[23 m, 23 m, 23 m] putVector: [30, 40, 0], [30, 40, 0] VQ constructor: [23, 23, 23] Pos constructor:[7, 17, -23] Q constructor: [0, 0, 50] QV constructor: [0.57735, 0.57735, 0.57735] QV constructor: [19.6299, 19.6299, 19.6299] V constructor: [30, 40, 0] D constructor: [0, 0, 78] operator+: [53, 63, 23] operator-: [7, 17, -23] operator-pre-: [-23, -23, -23] operator*: [23, 19.7536, 25.8418] operator*: [46, 46, 46] assure: ok getLength: 50 m operator*: 2500 operator* 1610 operator*: 2500 clone: [30, 40, 0] getVector: [30, 40, 0] near: 0 near: 0 nearAbs: 0 !=: 1 ==: 0 All MVBaseline functions: ok ---------------------------- Exercise all MBaseline function getType: 1, 10 giveMe: 1, Reference for an Baseline with Type: AZEL setRefString: 1, Baseline: [100, 10, 1e-08] assure: ok clone: Baseline: [100, 10, 1e-08] get: [10000, 1000, 1e-06] cm getAngle: [5.71059, 5.70114e-09] deg getDefaultType: ITRF getRefString: HADEC All MBaseline functions: ok --------------------------- casacore-3.7.1/measures/Measures/test/tMDirection.cc000066400000000000000000000063251476623553700224510ustar00rootroot00000000000000//# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include Bool testShiftAngle() { Double rav = 30; Double decv = 40; Quantity ra(rav, "deg"); Quantity dec(decv, "deg"); MDirection x(ra, dec); Quantity offset(4, "arcmin"); Quantity pa(0, "deg"); x.shiftAngle(offset, pa); Quantum > angle = x.getAngle(); AlwaysAssert( abs((angle.getValue("deg")[0] - rav)/rav) < 1e-6, AipsError ); Double exp = (dec + offset).getValue("deg"); AlwaysAssert( abs((angle.getValue("deg")[1] - exp)/exp) < 1e-6, AipsError ); x = MDirection(ra, dec); pa = Quantity(90, "deg"); x.shiftAngle(offset, pa); exp = rav + offset.getValue("deg")/cos(x.getAngle().getValue("rad")[1]); angle = x.getAngle(); AlwaysAssert( abs((angle.getValue("deg")[0] - exp)/exp) < 1e-6, AipsError ); exp = (decv); cout << (angle.getValue("deg")[1] - exp) << endl; AlwaysAssert( abs((angle.getValue("deg")[1] - exp)/exp) < 1e-6, AipsError ); x = MDirection(ra, dec); pa = Quantity(-90, "deg"); x.shiftAngle(offset, pa); exp = rav - offset.getValue("deg")/cos(x.getAngle().getValue("rad")[1]); angle = x.getAngle(); AlwaysAssert( abs((angle.getValue("deg")[0] - exp)/exp) < 1e-6, AipsError ); exp = (decv); AlwaysAssert( abs((angle.getValue("deg")[1] - exp)/exp) < 1e-6, AipsError ); x = MDirection(ra, dec); pa = Quantity(180, "deg"); x.shiftAngle(offset, pa); exp = rav; angle = x.getAngle(); AlwaysAssert( abs((angle.getValue("deg")[0] - exp)/exp) < 1e-6, AipsError ); exp = decv - offset.getValue("deg"); AlwaysAssert( abs((angle.getValue("deg")[1] - exp)/exp) < 1e-6, AipsError ); return True; } int main() { try { Bool success = True; success = success && testShiftAngle(); if (success) { cout << "tMDirection succeeded" << endl; return 0; } else { cout << "tMDirection failed" << endl; return 1; } } catch (std::exception& x) { cout << "tMDirection failed: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/measures/Measures/test/tMEarthMagnetic.cc000066400000000000000000000240231476623553700232370ustar00rootroot00000000000000//# tMEarthMagnetic.cc: This program test Measure functions //# Copyright (C) 1995,1996,1997,1998,1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main() { try { cout << "Test Earth Magnetic field" << endl; cout << "--------------------------------------" << endl; cout << endl << "MEarthMagnetic state transition matrix:\n" << endl; cout << MCEarthMagnetic::showState() << endl; MEarthMagnetic vl; MVTime dat(1998,5,18); MVPosition mvobs(Quantity(3828488.86, "m").getBaseValue(), Quantity(443253.42, "m").getBaseValue(), Quantity(5064977.78, "m").getBaseValue()); MPosition obs(mvobs); MeasFrame frame((MEpoch(MVEpoch(dat.day()))), obs); cout << "Date: " << dat.string(MVTime::YMD + MVTime::NO_TIME, 6) << endl; cout << "Position: " << obs.getValue().get() << endl; cout << " " << obs.getAngle("deg") << endl; EarthField ef(EarthField::STANDARD, dat.day()); cout << "Result: " << ef(obs.getValue()) << endl; cout << "------------------------------------------" << endl; MEarthMagnetic::Convert cv(MEarthMagnetic::Ref(MEarthMagnetic::ITRF, frame), MEarthMagnetic::Ref(MEarthMagnetic::AZEL)); MEarthMagnetic::Convert cv1(MEarthMagnetic::Ref(MEarthMagnetic::ITRF, frame), MEarthMagnetic::Ref(MEarthMagnetic::HADEC)); MVEarthMagnetic res(ef(obs.getValue())); cout << "In ITRF: " << MEarthMagnetic(res) << endl; cout << " " << MEarthMagnetic(res).getAngle("deg") << endl; cout << "In AZEL: " << cv(res) << endl; cout << " " << cv(res).getAngle("deg") << endl; cout << "In HADEC: " << cv1(res) << endl; cout << " " << cv1(res).getAngle("deg") << endl; cout << "------------------------------------------" << endl; MEarthMagnetic::Convert cv2(MEarthMagnetic::Ref(MEarthMagnetic::AZEL, frame), MEarthMagnetic::Ref(MEarthMagnetic::HADEC)); MEarthMagnetic::Convert cv3(MEarthMagnetic::Ref(MEarthMagnetic::AZEL, frame), MEarthMagnetic::Ref(MEarthMagnetic::ITRF)); MVEarthMagnetic res1(18312.1, -382.004, 45184.5); cout << "In AZEL: " << MEarthMagnetic(res1) << endl; cout << " " << MEarthMagnetic(res1).getAngle("deg") << endl; cout << "In HADEC: " << cv2(res1) << endl; cout << " " << cv2(res1).getAngle("deg") << endl; cout << "In ITRF: " << cv3(res1) << endl; cout << " " << cv3(res1).getAngle("deg") << endl; cout << "------------------------------------------" << endl; MEarthMagnetic::Convert cv4(MEarthMagnetic::Ref(MEarthMagnetic::IGRF, frame), MEarthMagnetic::Ref(MEarthMagnetic::ITRF)); MEarthMagnetic::Convert cv5(MEarthMagnetic::Ref(MEarthMagnetic::IGRF, frame), MEarthMagnetic::Ref(MEarthMagnetic::AZEL)); MEarthMagnetic::Convert cv6(MEarthMagnetic::Ref(MEarthMagnetic::IGRF, frame), MEarthMagnetic::Ref(MEarthMagnetic::HADEC)); cout << "Model ITRF: " << cv4(res) << endl; cout << " " << cv4(res).getAngle("deg") << ", " << cv4(res).getValue().getLength("G") << endl; cout << "Model AZEL: " << cv5(res) << endl; cout << " " << cv5(res).getAngle("deg") << ", " << cv5(res).getValue().getLength("G") << endl; cout << "Model HADEC: " << cv6(res) << endl; cout << " " << cv6(res).getAngle("deg") << ", " << cv6(res).getValue().getLength("G") << endl; cout << "--------------------------------------" << endl; cout << "Testing all conversions forward/backward" << endl; Bool isok = True; Vector tvec(3); tvec = 0.0; for (uInt i=MEarthMagnetic::ITRF; i > vq(3); vq = Quantity(23, "G"); x.putValue(vq); cout << "putValue: " << vq << ", " << x << endl; cout << "earthMagneticAngle: " << x.earthMagneticAngle(mvb0) << endl; cout << "earthMagneticAngle: " << x.earthMagneticAngle(mvb0, "deg") << endl; cout << "get: " << x.get() << endl; cout << "getRecordValue: " << x.getRecordValue() << endl; cout << "separation: " << x.separation(mvb0) << endl; cout << "separation: " << x.separation(mvb0, "deg") << endl; cout << "crossProduct: " << x.crossProduct(mvb0) << endl; cout << "getAngle: " << x.getAngle() << endl; cout << "getAngle: " << x.getAngle("deg") << endl; cout << "getlength: " << x.getLength("G") << endl; cout << "radius: " << x.radius() << endl; Vector x1(3); x1(0) = 30; x1(1) = 40; x1(2) = 0; x.putVector(x1); cout << "putVector: " << x1 << ", " << x << endl; MVEarthMagnetic x2(vq); cout << "VQ constructor: " << x2 << endl; cout << "Q constructor: " << MVEarthMagnetic(Quantity(50, "G")) << endl; cout << "QV constructor: " << MVEarthMagnetic(x2.getAngle()) << endl; cout << "QV constructor: " << MVEarthMagnetic(Quantity(34,"G"), x2.getAngle()) << endl; cout << "V constructor: " << MVEarthMagnetic(x1) << endl; cout << "D constructor: " << MVEarthMagnetic(Double(78)) << endl; cout << "operator+: " << x+x2 << endl; cout << "operator-: " << x-x2 << endl; cout << "operator-pre-: " << -x2 << endl; RotMatrix rm(Euler(25, 1, 0, 0)); cout << "operator*: " << x2*rm << endl; cout << "operator*: " << x2*2 << endl; MVEarthMagnetic::assure(x); cout << "assure: " << "ok" << endl; cout << "getLength: " << x.getLength() << endl; cout << "operator*: " << x*x1 << endl; cout << "operator* " << x*x2 << endl; cout << "operator*: " << x1*x << endl; MeasValue *y = x.clone(); cout << "clone: " << *y << endl; delete y; cout << "getVector: " << x.getVector() << endl; cout << "near: " << x.near(x2) << endl; cout << "near: " << x.near(x2, Quantity(1, "deg")) << endl; cout << "nearAbs: " << x.nearAbs(x2) << endl; cout << "!=: " << (x != x2) << endl; cout << "==: " << (x == x2) << endl; cout << "All MVEarthMagnetic functions: ok" << endl; cout << "----------------------------" << endl; } cout << "Exercise all MEarthMagnetic function" << endl; { MEarthMagnetic mb(mvb0, MEarthMagnetic::B1950); String s0("azel"); MEarthMagnetic::Types tp; MEarthMagnetic::Ref mr; cout << "getType: " << MEarthMagnetic::getType(tp, s0) << ", "; cout << Int(tp) << endl; cout << "giveMe: " << mb.giveMe(mr, s0) << ", "; cout << mr << endl; cout << "setRefString: " << mb.setRefString("hadec") << ", "; cout << mb << endl; MEarthMagnetic::assure(mb); cout << "assure: " << "ok" << endl; Measure *y = mb.clone(); cout << "clone: " << *y << endl; delete y; cout << "get: " << mb.get("cm") << endl; cout << "getAngle: " << mb.getAngle("deg") << endl; cout << "getDefaultType: " << mb.getDefaultType() << endl; cout << "getRefString: " << mb.getRefString() << endl; cout << "All MEarthMagnetic functions: ok" << endl; cout << "---------------------------" << endl; } cout << "------------------------------------------" << endl; } catch (std::exception& x) { cout << x.what() << endl; } return 0; } casacore-3.7.1/measures/Measures/test/tMEarthMagnetic.out000066400000000000000000000141241476623553700234620ustar00rootroot00000000000000Test Earth Magnetic field -------------------------------------- MEarthMagnetic state transition matrix: | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ------------------------------------------------------------------- 0| -- 6 6 14 4 4 4 2 14 14 14 14 14 14 30 6 6 2 14 14 43 | 1 1 13 4 4 4 7 13 13 13 13 13 13 14 1 1 7 13 13 20 1| 8 -- 9 8 8 8 8 8 8 8 8 8 8 8 8 32 9 8 8 8 8 | 0 2 0 0 0 0 0 0 0 0 0 0 0 0 15 2 0 0 0 0 2| 12 12 -- 12 12 12 12 12 12 12 12 12 12 12 12 12 34 12 12 12 12 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 16 1 1 1 1 3| 28 28 28 -- 17 17 17 28 18 18 18 18 18 28 28 28 28 28 18 18 28 | 13 13 13 4 4 4 13 19 19 19 19 19 13 13 13 13 13 19 19 13 4| 5 5 5 16 -- 7 7 3 16 16 16 16 16 5 5 5 5 3 16 16 5 | 0 0 0 3 5 5 7 3 3 3 3 3 0 0 0 0 7 3 3 0 5| 10 10 10 10 10 -- 11 10 10 10 10 10 10 10 10 10 10 10 10 10 10 | 4 4 4 4 4 6 4 4 4 4 4 4 4 4 4 4 4 4 4 4 6| 13 13 13 13 13 13 -- 13 13 13 13 13 13 13 13 13 13 13 13 13 13 | 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 7| 0 0 0 0 1 1 1 -- 0 0 0 0 0 0 0 0 0 36 0 0 0 | 0 0 0 0 4 4 4 0 0 0 0 0 0 0 0 0 17 0 0 0 8| 23 23 23 23 23 23 23 23 -- 19 19 20 20 23 23 23 23 23 39 23 23 | 19 19 19 19 19 19 19 19 9 9 11 11 19 19 19 19 19 18 19 19 9| 21 21 21 21 21 21 21 21 21 -- 24 21 21 21 21 21 21 21 21 21 21 | 8 8 8 8 8 8 8 8 8 10 8 8 8 8 8 8 8 8 8 8 10| 26 26 26 26 26 26 26 26 26 26 -- 26 26 26 26 26 26 26 26 26 26 | 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 11| 22 22 22 22 22 22 22 22 22 22 22 -- 25 22 22 22 22 22 22 22 22 | 8 8 8 8 8 8 8 8 8 8 8 12 8 8 8 8 8 8 8 8 12| 27 27 27 27 27 27 27 27 27 27 27 27 -- 27 27 27 27 27 27 27 27 | 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 13| 15 15 15 29 15 15 15 15 29 29 29 29 29 -- 15 15 15 15 29 29 15 | 0 0 0 3 0 0 0 0 3 3 3 3 3 0 0 0 0 3 3 0 14| 31 31 31 31 31 31 31 31 31 31 31 31 31 31 -- 31 31 31 31 31 31 | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 15| 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 -- 33 33 33 33 33 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 16| 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 -- 35 35 35 35 | 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 17| 37 37 37 37 37 37 37 37 37 37 37 37 37 37 37 37 37 -- 37 37 37 | 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 18| 38 38 38 38 38 38 38 38 38 38 38 38 38 38 38 38 38 38 -- 38 38 | 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 19| 41 41 41 41 41 41 41 41 40 40 40 40 40 41 41 41 41 41 40 -- 41 | 3 3 3 3 3 3 3 3 8 8 8 8 8 3 3 3 3 3 8 3 20| 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 -- | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Date: 1998/05/18 Position: [6.36457e+06, 0.115264, 0.92034] [6.60417, 52.7316] deg Result: [41652.3, 1805.43, 24913.3] ------------------------------------------ In ITRF: EarthMagnetic: [41652.3, 1805.43, 24913.3] [2.48195, 30.861] deg In AZEL: EarthMagnetic: [-18006.3, -2996.95, 45007] [-170.55, 67.9235] deg In HADEC: EarthMagnetic: [41583.5, 2996.95, 24913.3] [4.12222, 30.861] deg ------------------------------------------ In AZEL: EarthMagnetic: [18312.1, -382.004, 45184.5] [-1.19506, 67.9342] deg In HADEC: EarthMagnetic: [12788.5, 382.004, 47047.1] [1.71096, 74.7866] deg In ITRF: EarthMagnetic: [12747.6, 1091.33, 47047.1] [4.8932, 74.7866] deg ------------------------------------------ Model ITRF: EarthMagnetic: [41652.3, 1805.43, 24913.3] [2.48195, 30.861] deg, 0.485679 G Model AZEL: EarthMagnetic: [-18006.3, -2996.95, 45007] [-170.55, 67.9235] deg, 0.485679 G Model HADEC: EarthMagnetic: [41583.5, 2996.95, 24913.3] [4.12222, 30.861] deg, 0.485679 G -------------------------------------- Testing all conversions forward/backward All forward/backward Magnetic conversions: ok -------------------------------------- Exercise all MVEarthMagnetic function putValue: [23 G, 23 G, 23 G], [2.3e+06, 2.3e+06, 2.3e+06] earthMagneticAngle: -1.47878 earthMagneticAngle: -84.7277 deg get: [3.98372e+06, 0.785398, 0.61548] getRecordValue: [2.3e+06 nT, 2.3e+06 nT, 2.3e+06 nT] separation: 3.14159 separation: 180 deg crossProduct: [5.3148e+10, 3.84996e+10, -9.16477e+10] getAngle: [0.785398, 0.61548] rad getAngle: [45, 35.2644] deg getlength: 39.8372 G radius: 3.98372e+06 putVector: [30, 40, 0], [30, 40, 0] VQ constructor: [2.3e+06, 2.3e+06, 2.3e+06] Q constructor: [0, 0, 0.005] QV constructor: [0.57735, 0.57735, 0.57735] QV constructor: [0.00196299, 0.00196299, 0.00196299] V constructor: [30, 40, 0] D constructor: [0, 0, 78] operator+: [2.30003e+06, 2.30004e+06, 2.3e+06] operator-: [-2.29997e+06, -2.29996e+06, -2.3e+06] operator-pre-: [-2.3e+06, -2.3e+06, -2.3e+06] operator*: [2.3e+06, 1.97536e+06, 2.58418e+06] operator*: [4.6e+06, 4.6e+06, 4.6e+06] assure: ok getLength: 50 nT operator*: 2500 operator* 1.61e+08 operator*: 2500 clone: [30, 40, 0] getVector: [30, 40, 0] near: 0 near: 0 nearAbs: 0 !=: 1 ==: 0 All MVEarthMagnetic functions: ok ---------------------------- Exercise all MEarthMagnetic function getType: 1, 9 giveMe: 1, Reference for an EarthMagnetic with Type: AZEL setRefString: 1, EarthMagnetic: [41652.3, 1805.43, 24913.3] assure: ok clone: EarthMagnetic: [41652.3, 1805.43, 24913.3] get: [4.16523e+06, 180543, 2.49133e+06] cm.m-1.kg.s-2.A-1 getAngle: [2.48195, 30.861] deg getDefaultType: IGRF getRefString: HADEC All MEarthMagnetic functions: ok --------------------------- ------------------------------------------ casacore-3.7.1/measures/Measures/test/tMFrequency.cc000066400000000000000000000033031476623553700224630ustar00rootroot00000000000000//# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include int main() { try { AlwaysAssert( MFrequency::typeFromString("LSRK") == MFrequency::LSRK, AipsError ); Bool except = False; try { MFrequency::typeFromString("J2000"); } catch (const std::exception& x) { except = True; } AlwaysAssert(except, AipsError); cout << "ok" << endl; } catch (const std::exception& x) { cout << "tMFrequency failed: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/measures/Measures/test/tMeasComet.cc000066400000000000000000000230401476623553700222620ustar00rootroot00000000000000//# tMeasComet.cc: MeasComet test //# Copyright (C) 2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main() { try { cout << "Test MeasComet..." << endl; cout << "--------------------------------------" << endl; { MeasComet comet("VGEO"); cout << "Opened VGEO" << endl; cout << "--------------------------------------" << endl; cout << "Name: " << comet.getName() << endl; cout << "Type: " << MDirection::showType(comet.getType()) << endl; cout << "Topography: " << comet.getTopo() << endl; cout << "Start: " << MVTime(comet.getStart()).string(MVTime::YMD) << endl; cout << "End: " << MVTime(comet.getEnd()).string(MVTime::YMD) << endl; cout << "Entries: " << comet.nelements() << endl; cout << "--------------------------------------" << endl; cout << "Radial velocity:" << endl; for (Double x=50802.75; x<50803.0625001; x += 10.0/60./24.) { MVRadialVelocity y; cout << MVTime(x).string(MVTime::YMD) << " " << comet.getRadVel(y, x) << ": " << y << endl; }; cout << "--------------------------------------" << endl; cout << "Position:" << endl; for (Double x=50802.75; x<50803.0625001; x += 10.0/60./24.) { MVPosition y; cout << MVTime(x).string(MVTime::YMD) << " " << comet.get(y, x) << ": " << y << endl; }; cout << "--------------------------------------" << endl; cout << "Disk longitude and latitude:" << endl; for (Double x=50802.75; x<50803.0625001; x += 10.0/60./24.) { MVDirection y; cout << MVTime(x).string(MVTime::YMD) << " " << comet.getDisk(y, x) << ": " << y << endl; }; cout << "Frame and conversion:" << endl; MeasFrame frame; MEpoch epo(MVEpoch(50802.729167), MEpoch::ET); frame.set(epo); frame.set(comet); MPosition pos; MeasTable::Observatory(pos, "vla"); frame.set(pos); cout << "Frame: " << frame << endl; MDirection::Convert cvt(MDirection::Ref(MDirection::COMET, frame), MDirection::APP); cout << "Apparent: "; cout << cvt() << endl; cvt = MDirection::Convert(MDirection::Ref(MDirection::COMET, frame), MDirection::TOPO); cout << "Topo: "; cout << cvt() << endl; cvt = MDirection::Convert(MDirection::Ref(MDirection::COMET, frame), MDirection::HADEC); cout << "HADEC: "; cout << cvt() << endl; } MeasComet *cl; { cout << "--------------------------------------" << endl; MeasComet comet("VTOP"); cout << "Opened VTOP" << endl; cout << "--------------------------------------" << endl; cout << "Name: " << comet.getName() << endl; cout << "Type: " << MDirection::showType(comet.getType()) << endl; cout << "Topography: " << comet.getTopo() << endl; cout << "Start: " << MVTime(comet.getStart()).string(MVTime::YMD) << endl; cout << "End: " << MVTime(comet.getEnd()).string(MVTime::YMD) << endl; cout << "Entries: " << comet.nelements() << endl; cout << "--------------------------------------" << endl; cout << "Radial velocity:" << endl; for (Double x=50802.75; x<50803.0625001; x += 10.0/60./24.) { MVRadialVelocity y; cout << MVTime(x).string(MVTime::YMD) << " " << comet.getRadVel(y, x) << ": " << y << endl; }; cout << "--------------------------------------" << endl; cl = comet.clone(); cout << "--------------------------------------" << endl; cout << "Frame and conversion:" << endl; MeasFrame frame; MEpoch epo(MVEpoch(50802.729167), MEpoch::ET); frame.set(epo); frame.set(comet); MPosition pos; MeasTable::Observatory(pos, "vla"); frame.set(pos); cout << "Frame: " << frame << endl; MDirection::Convert cvt(MDirection::Ref(MDirection::COMET, frame), MDirection::APP); cout << "Apparent: "; cout << cvt() << endl; cvt = MDirection::Convert(MDirection::Ref(MDirection::COMET, frame), MDirection::TOPO); cout << "Topo: "; cout << cvt() << endl; cvt = MDirection::Convert(MDirection::Ref(MDirection::COMET, frame), MDirection::HADEC); cout << "HADEC: "; cout << cvt() << endl; cout << "--------------------------------------" << endl; } { cout << "--------------------------------------" << endl; cout << "Cloned VTOP" << endl; cout << "--------------------------------------" << endl; cout << "OK: " << cl->ok() << endl; cout << "Name: " << cl->getName() << endl; cout << "Type: " << MDirection::showType(cl->getType()) << endl; cout << "Topography: " << cl->getTopo() << endl; cout << "Start: " << MVTime(cl->getStart()).string(MVTime::YMD) << endl; cout << "End: " << MVTime(cl->getEnd()).string(MVTime::YMD) << endl; cout << "Entries: " << cl->nelements() << endl; cout << "--------------------------------------" << endl; cout << "Radial velocity:" << endl; for (Double x=50802.75; x<50802.8; x += 10.0/60./24.) { MVRadialVelocity y; cout << MVTime(x).string(MVTime::YMD) << " " << cl->getRadVel(y, x) << ": " << y << endl; }; cout << "--------------------------------------" << endl; delete cl; } } catch (std::exception& x) { cout << x.what() << endl; } try { cout << "--------------------------------------" << endl; cout << "Default VTOP" << endl; cout << "--------------------------------------" << endl; MeasComet comet; cout << "OK: " << comet.ok() << endl; cout << "Name: " << comet.getName() << endl; } catch (std::exception& x) { cout << x.what() << endl; } /* try { cout << "-----------------------------------------" << endl; cout << "Read a table without DiskLong or DiskLat." << endl; cout << "-----------------------------------------" << endl; MeasComet comet("JPL-Horizons/Ariel_55438-56292dUTC.tab"); cout << "Opened JPL-Horizons/Ariel_55438-56292dUTC.tab" << endl; cout << "--------------------------------------" << endl; cout << "Name: " << comet.getName() << endl; cout << "Type: " << MDirection::showType(comet.getType()) << endl; cout << "Topography: " << comet.getTopo() << endl; cout << "Start: " << MVTime(comet.getStart()).string(MVTime::YMD) << endl; cout << "End: " << MVTime(comet.getEnd()).string(MVTime::YMD) << endl; cout << "Entries: " << comet.nelements() << endl; cout << "--------------------------------------" << endl; cout << "Some radial velocities:" << endl; for(Double x= 55555.75; x < 56000.0625; x += 40.0){ MVRadialVelocity y; cout << MVTime(x).string(MVTime::YMD) << " " << comet.getRadVel(y, x) << ": " << y << endl; }; cout << "--------------------------------------" << endl; cout << "Some positions:" << endl; for(Double x=55444.75; x < 56030.0625; x += 40.0){ MVPosition y; cout << MVTime(x).string(MVTime::YMD) << " " << comet.get(y, x) << ": " << y << endl; }; cout << "--------------------------------------" << endl; cout << "A disk longitude and latitude\n" << "(should fail gracefully by showing 0:):" << endl; Double x = 55444.75; MVDirection y; cout << MVTime(x).string(MVTime::YMD) << " " << comet.getDisk(y, x) << ": " << y << endl; } catch (std::exception x) { cout << x.what() << endl; } */ return 0; } casacore-3.7.1/measures/Measures/test/tMeasComet.out000066400000000000000000000253351476623553700225150ustar00rootroot00000000000000Test MeasComet... -------------------------------------- Opened VGEO -------------------------------------- Name: VENUS Type: APP Topography: [1e-06, 0, 0] Start: 1997/12/20/17:00:00 End: 1997/12/21/01:30:00 Entries: 35 -------------------------------------- Radial velocity: 1997/12/20/18:00:00 1: -9911.56 1997/12/20/18:10:00 1: -9910.09 1997/12/20/18:20:00 1: -9908.62 1997/12/20/18:30:00 1: -9907.15 1997/12/20/18:40:00 1: -9905.68 1997/12/20/18:50:00 1: -9904.21 1997/12/20/19:00:00 1: -9902.74 1997/12/20/19:10:00 1: -9901.27 1997/12/20/19:20:00 1: -9899.8 1997/12/20/19:30:00 1: -9898.32 1997/12/20/19:40:00 1: -9896.85 1997/12/20/19:50:00 1: -9895.37 1997/12/20/20:00:00 1: -9893.9 1997/12/20/20:10:00 1: -9892.43 1997/12/20/20:20:00 1: -9890.95 1997/12/20/20:30:00 1: -9889.47 1997/12/20/20:40:00 1: -9888 1997/12/20/20:50:00 1: -9886.52 1997/12/20/21:00:00 1: -9885.04 1997/12/20/21:10:00 1: -9883.56 1997/12/20/21:20:00 1: -9882.08 1997/12/20/21:30:00 1: -9880.61 1997/12/20/21:40:00 1: -9879.13 1997/12/20/21:50:00 1: -9877.65 1997/12/20/22:00:00 1: -9876.16 1997/12/20/22:10:00 1: -9874.68 1997/12/20/22:20:00 1: -9873.2 1997/12/20/22:30:00 1: -9871.72 1997/12/20/22:40:00 1: -9870.24 1997/12/20/22:50:00 1: -9868.75 1997/12/20/23:00:00 1: -9867.27 1997/12/20/23:10:00 1: -9865.78 1997/12/20/23:20:00 1: -9864.3 1997/12/20/23:30:00 1: -9862.81 1997/12/20/23:40:00 1: -9861.33 1997/12/20/23:50:00 1: -9859.84 1997/12/21/00:00:00 1: -9858.35 1997/12/21/00:10:00 1: -9856.86 1997/12/21/00:20:00 1: -9855.37 1997/12/21/00:30:00 1: -9853.89 1997/12/21/00:40:00 1: -9852.4 1997/12/21/00:50:00 1: -9850.91 1997/12/21/01:00:00 1: -9849.42 1997/12/21/01:10:00 1: -9847.92 1997/12/21/01:20:00 1: -9846.43 1997/12/21/01:30:00 0: 0 -------------------------------------- Position: 1997/12/20/18:00:00 1: [2.88451e+10, -4.02763e+10, -1.79162e+10] 1997/12/20/18:10:00 1: [2.88431e+10, -4.02715e+10, -1.79128e+10] 1997/12/20/18:20:00 1: [2.88411e+10, -4.02667e+10, -1.79093e+10] 1997/12/20/18:30:00 1: [2.88391e+10, -4.02619e+10, -1.79059e+10] 1997/12/20/18:40:00 1: [2.88371e+10, -4.0257e+10, -1.79024e+10] 1997/12/20/18:50:00 1: [2.88351e+10, -4.02522e+10, -1.7899e+10] 1997/12/20/19:00:00 1: [2.88331e+10, -4.02474e+10, -1.78955e+10] 1997/12/20/19:10:00 1: [2.88311e+10, -4.02426e+10, -1.78921e+10] 1997/12/20/19:20:00 1: [2.88291e+10, -4.02378e+10, -1.78887e+10] 1997/12/20/19:30:00 1: [2.88271e+10, -4.0233e+10, -1.78852e+10] 1997/12/20/19:40:00 1: [2.88251e+10, -4.02282e+10, -1.78818e+10] 1997/12/20/19:50:00 1: [2.88231e+10, -4.02234e+10, -1.78783e+10] 1997/12/20/20:00:00 1: [2.88211e+10, -4.02186e+10, -1.78749e+10] 1997/12/20/20:10:00 1: [2.88191e+10, -4.02138e+10, -1.78715e+10] 1997/12/20/20:20:00 1: [2.8817e+10, -4.0209e+10, -1.7868e+10] 1997/12/20/20:30:00 1: [2.8815e+10, -4.02042e+10, -1.78646e+10] 1997/12/20/20:40:00 1: [2.8813e+10, -4.01994e+10, -1.78611e+10] 1997/12/20/20:50:00 1: [2.8811e+10, -4.01946e+10, -1.78577e+10] 1997/12/20/21:00:00 1: [2.8809e+10, -4.01899e+10, -1.78543e+10] 1997/12/20/21:10:00 1: [2.8807e+10, -4.01851e+10, -1.78508e+10] 1997/12/20/21:20:00 1: [2.8805e+10, -4.01803e+10, -1.78474e+10] 1997/12/20/21:30:00 1: [2.88029e+10, -4.01755e+10, -1.7844e+10] 1997/12/20/21:40:00 1: [2.88009e+10, -4.01707e+10, -1.78405e+10] 1997/12/20/21:50:00 1: [2.87989e+10, -4.01659e+10, -1.78371e+10] 1997/12/20/22:00:00 1: [2.87969e+10, -4.01612e+10, -1.78337e+10] 1997/12/20/22:10:00 1: [2.87948e+10, -4.01564e+10, -1.78302e+10] 1997/12/20/22:20:00 1: [2.87928e+10, -4.01516e+10, -1.78268e+10] 1997/12/20/22:30:00 1: [2.87908e+10, -4.01468e+10, -1.78234e+10] 1997/12/20/22:40:00 1: [2.87888e+10, -4.01421e+10, -1.782e+10] 1997/12/20/22:50:00 1: [2.87867e+10, -4.01373e+10, -1.78165e+10] 1997/12/20/23:00:00 1: [2.87847e+10, -4.01325e+10, -1.78131e+10] 1997/12/20/23:10:00 1: [2.87827e+10, -4.01278e+10, -1.78097e+10] 1997/12/20/23:20:00 1: [2.87806e+10, -4.0123e+10, -1.78063e+10] 1997/12/20/23:30:00 1: [2.87786e+10, -4.01182e+10, -1.78028e+10] 1997/12/20/23:40:00 1: [2.87766e+10, -4.01135e+10, -1.77994e+10] 1997/12/20/23:50:00 1: [2.87745e+10, -4.01087e+10, -1.7796e+10] 1997/12/21/00:00:00 1: [2.87725e+10, -4.01039e+10, -1.77926e+10] 1997/12/21/00:10:00 1: [2.87705e+10, -4.00992e+10, -1.77891e+10] 1997/12/21/00:20:00 1: [2.87684e+10, -4.00944e+10, -1.77857e+10] 1997/12/21/00:30:00 1: [2.87664e+10, -4.00897e+10, -1.77823e+10] 1997/12/21/00:40:00 1: [2.87643e+10, -4.00849e+10, -1.77789e+10] 1997/12/21/00:50:00 1: [2.87623e+10, -4.00802e+10, -1.77755e+10] 1997/12/21/01:00:00 1: [2.87603e+10, -4.00754e+10, -1.7772e+10] 1997/12/21/01:10:00 1: [2.87582e+10, -4.00707e+10, -1.77686e+10] 1997/12/21/01:20:00 1: [2.87562e+10, -4.00659e+10, -1.77652e+10] 1997/12/21/01:30:00 0: [0, 0, 0] -------------------------------------- Disk longitude and latitude: 1997/12/20/18:00:00 1: [0.523993, -0.851692, 0.00719069] 1997/12/20/18:10:00 1: [0.52417, -0.851584, 0.00716742] 1997/12/20/18:20:00 1: [0.524346, -0.851475, 0.00714415] 1997/12/20/18:30:00 1: [0.524523, -0.851367, 0.00712088] 1997/12/20/18:40:00 1: [0.524699, -0.851258, 0.00709761] 1997/12/20/18:50:00 1: [0.524876, -0.851149, 0.00707434] 1997/12/20/19:00:00 1: [0.525053, -0.85104, 0.00705107] 1997/12/20/19:10:00 1: [0.525229, -0.850932, 0.00702664] 1997/12/20/19:20:00 1: [0.525406, -0.850823, 0.00700278] 1997/12/20/19:30:00 1: [0.525582, -0.850714, 0.00697952] 1997/12/20/19:40:00 1: [0.525758, -0.850606, 0.00695624] 1997/12/20/19:50:00 1: [0.525935, -0.850497, 0.00693298] 1997/12/20/20:00:00 1: [0.526111, -0.850388, 0.0069097] 1997/12/20/20:10:00 1: [0.526287, -0.850279, 0.00688643] 1997/12/20/20:20:00 1: [0.526463, -0.85017, 0.00686316] 1997/12/20/20:30:00 1: [0.526639, -0.850061, 0.00683989] 1997/12/20/20:40:00 1: [0.526815, -0.849952, 0.00681662] 1997/12/20/20:50:00 1: [0.526992, -0.849843, 0.00679277] 1997/12/20/21:00:00 1: [0.527168, -0.849734, 0.00676834] 1997/12/20/21:10:00 1: [0.527344, -0.849625, 0.00674506] 1997/12/20/21:20:00 1: [0.52752, -0.849516, 0.0067218] 1997/12/20/21:30:00 1: [0.527696, -0.849407, 0.00669852] 1997/12/20/21:40:00 1: [0.527872, -0.849298, 0.00667525] 1997/12/20/21:50:00 1: [0.528047, -0.849189, 0.00665198] 1997/12/20/22:00:00 1: [0.528222, -0.84908, 0.00662871] 1997/12/20/22:10:00 1: [0.528398, -0.848971, 0.00660428] 1997/12/20/22:20:00 1: [0.528574, -0.848862, 0.00658042] 1997/12/20/22:30:00 1: [0.52875, -0.848752, 0.00655716] 1997/12/20/22:40:00 1: [0.528925, -0.848644, 0.00653388] 1997/12/20/22:50:00 1: [0.5291, -0.848534, 0.00651062] 1997/12/20/23:00:00 1: [0.529276, -0.848425, 0.00648734] 1997/12/20/23:10:00 1: [0.529452, -0.848315, 0.00646407] 1997/12/20/23:20:00 1: [0.529627, -0.848206, 0.00644022] 1997/12/20/23:30:00 1: [0.529802, -0.848097, 0.00641579] 1997/12/20/23:40:00 1: [0.529977, -0.847988, 0.00639252] 1997/12/20/23:50:00 1: [0.530153, -0.847878, 0.00636924] 1997/12/21/00:00:00 1: [0.530327, -0.847769, 0.00634597] 1997/12/21/00:10:00 1: [0.530503, -0.847659, 0.0063227] 1997/12/21/00:20:00 1: [0.530678, -0.84755, 0.00629885] 1997/12/21/00:30:00 1: [0.530853, -0.847441, 0.00627442] 1997/12/21/00:40:00 1: [0.531027, -0.847332, 0.00625115] 1997/12/21/00:50:00 1: [0.531202, -0.847222, 0.00622788] 1997/12/21/01:00:00 1: [0.531378, -0.847112, 0.00620461] 1997/12/21/01:10:00 1: [0.531553, -0.847003, 0.00618134] 1997/12/21/01:20:00 1: [0.531727, -0.846893, 0.00615748] 1997/12/21/01:30:00 0: [0, 0, 1] Frame and conversion: Frame: Frame: Epoch: 50802::17:30:00.0288 (TDB = 50802.7, UT1 = 50802.7, TT = 50802.7) Position: [-1.60119e+06, -5.04198e+06, 3.55488e+06] (Longitude = -1.87829 Latitude = 0.591675) VENUS comet between MJD 50802.7 and 50803.1 Apparent: Direction: [0.547479, -0.764558, -0.340175] Topo: Direction: [0.547535, -0.764484, -0.340249] HADEC: Direction: [0.446423, -0.827609, -0.34025] -------------------------------------- Opened VTOP -------------------------------------- Name: VENUS Type: TOPO Topography: [-1.59947e+06, -5.03669e+06, 3.57509e+06] Start: 1997/12/20/17:00:00 End: 1997/12/21/01:30:00 Entries: 35 -------------------------------------- Radial velocity: 1997/12/20/18:00:00 1: -10205.6 1997/12/20/18:10:00 1: -10194.5 1997/12/20/18:20:00 1: -10182.9 1997/12/20/18:30:00 1: -10170.9 1997/12/20/18:40:00 1: -10158.2 1997/12/20/18:50:00 1: -10145.1 1997/12/20/19:00:00 1: -10131.7 1997/12/20/19:10:00 1: -10117.6 1997/12/20/19:20:00 1: -10103.2 1997/12/20/19:30:00 1: -10088.5 1997/12/20/19:40:00 1: -10073.3 1997/12/20/19:50:00 1: -10057.8 1997/12/20/20:00:00 1: -10042.1 1997/12/20/20:10:00 1: -10025.9 1997/12/20/20:20:00 1: -10009.6 1997/12/20/20:30:00 1: -9993.08 1997/12/20/20:40:00 1: -9976.27 1997/12/20/20:50:00 1: -9959.33 1997/12/20/21:00:00 1: -9942.29 1997/12/20/21:10:00 1: -9925.08 1997/12/20/21:20:00 1: -9907.82 1997/12/20/21:30:00 1: -9890.51 1997/12/20/21:40:00 1: -9873.17 1997/12/20/21:50:00 1: -9855.86 1997/12/20/22:00:00 1: -9838.56 1997/12/20/22:10:00 1: -9821.37 1997/12/20/22:20:00 1: -9804.26 1997/12/20/22:30:00 1: -9787.25 1997/12/20/22:40:00 1: -9770.47 1997/12/20/22:50:00 1: -9753.85 1997/12/20/23:00:00 1: -9737.38 1997/12/20/23:10:00 1: -9721.29 1997/12/20/23:20:00 1: -9705.41 1997/12/20/23:30:00 1: -9689.75 1997/12/20/23:40:00 1: -9674.58 1997/12/20/23:50:00 1: -9659.69 1997/12/21/00:00:00 1: -9645.08 1997/12/21/00:10:00 1: -9631.08 1997/12/21/00:20:00 1: -9617.41 1997/12/21/00:30:00 1: -9604.07 1997/12/21/00:40:00 1: -9591.45 1997/12/21/00:50:00 1: -9579.21 1997/12/21/01:00:00 1: -9567.36 1997/12/21/01:10:00 1: -9556.31 1997/12/21/01:20:00 1: -9545.68 1997/12/21/01:30:00 0: 0 -------------------------------------- -------------------------------------- Frame and conversion: Frame: Frame: Epoch: 50802::17:30:00.0288 (TDB = 50802.7, UT1 = 50802.7, TT = 50802.7) Position: [-1.60119e+06, -5.04198e+06, 3.55488e+06] (Longitude = -1.87829 Latitude = 0.591675) VENUS comet between MJD 50802.7 and 50803.1 Apparent: Direction: [0.547479, -0.764557, -0.340174] Topo: Direction: [0.547535, -0.764484, -0.340249] HADEC: Direction: [0.446423, -0.827609, -0.340249] -------------------------------------- -------------------------------------- Cloned VTOP -------------------------------------- OK: 1 Name: VENUS Type: TOPO Topography: [-1.59947e+06, -5.03669e+06, 3.57509e+06] Start: 1997/12/20/17:00:00 End: 1997/12/21/01:30:00 Entries: 35 -------------------------------------- Radial velocity: 1997/12/20/18:00:00 1: -10205.6 1997/12/20/18:10:00 1: -10194.5 1997/12/20/18:20:00 1: -10182.9 1997/12/20/18:30:00 1: -10170.9 1997/12/20/18:40:00 1: -10158.2 1997/12/20/18:50:00 1: -10145.1 1997/12/20/19:00:00 1: -10131.7 1997/12/20/19:10:00 1: -10117.6 -------------------------------------- -------------------------------------- Default VTOP -------------------------------------- OK: 0 Name: casacore-3.7.1/measures/Measures/test/tMeasIERS.cc000066400000000000000000000056001476623553700217570ustar00rootroot00000000000000//# tMeasIERS.cc: This program tests the MeasIERS functions //# Copyright (C) 2014 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include void getX (double date) { double result; bool sts = MeasIERS::get(result, MeasIERS::MEASURED, MeasIERS::X, date); cout <<"MEASURED "< val(6); double date= 51116; for (int i=0; i<3; ++i) { getX (date); date += 0.5; } getX (37660); getX (37665); getX (55000); getX (55809); getX (600000); // Test for handling of leap seconds (CAS-7984) Double startMJD = 57202; // 2015-06-29T00:00:00 Double oneHour = 1.0/24.; for (Int i = 0; i < 72; i++) { const Double mjd = startMJD + i * oneHour; const MVTime now(mjd); Double dUT; Bool rval = MeasIERS::get(dUT, MeasIERS::PREDICTED, MeasIERS::dUT1, mjd); if(!rval){ cout << "MeasIERS::get returned False for PREDICTED, dUT1, mjd " << mjd << endl; return 2; } cout << now.string(casacore::MVTime::YMD) << " " << setprecision(3) << fixed << mjd << " " << dUT << endl; } } catch (const std::exception& x) { cout << x.what() << endl; return 1; } return 0; } casacore-3.7.1/measures/Measures/test/tMeasIERS.in_tgz000066400000000000000000173440211476623553700226770ustar00rootroot00000000000000‹ØÒ§Vì½\WǶèÏ‘.E@AšTE:ØØØ±cÇŽ;MÁ’SM×TO’“h*v¢QIb²Ñ$ j[Žé¦Ç4M7ý}gÏüöæzÍy÷¾wÿçæÿ®~>ãÙ3«Ìšµ¦ÏšŠ¡³¦•ì7rÔÔŠ…%gN«˜ÖÅé¿ú_ ÿ²33ùMMÍLK1ã™©Ùæ¯ú甚–ŧÌÔ´´t§”ÔôŒ” §ˆÌÿrN.óoIyÅ´²ˆ§™%³*Ê–ýy¾i NûWðó/þWñïëάÅ3gUÌñ_¦ÿ™úOOõŸ™š•u¥þÿÿþYý‹Ï%e³fÎQñ¥ ÿùúÏ?Wêÿ_ðï?ZÿÓ¦/˜•<;åÿ„†¨à¬ŒŒËÖfzVvjjÚ¿­ÿ4²§8EüûÏþû^ÿ/òŸ ‚ßÀE3Êf-œµ¨bÚ‚QC§-â[ '§o/òÛ† 9`þàß»Wþý—ÿë˜w.»Zü¥?ã<8ý‹ïêžYïÞ¾Ogý5÷ÔØsU§õ6÷éúÇk¾úqßk޽t1 ÷õÄòR¿G_ŠÉØèŸÿÛ#'ë]ï Ë=÷‘±|~Aí7+vêYùyï•>R õ’NÏþ;æ_¬ÔÆ?ü¿œÐOÿvËÏ~?A?î|ßÎÌ!¹§.îZýAñ×FÍïç§4xWË—tìïù$ðõþδì[jõ@™O?ýSÙ€~9ç­xÓ÷Á÷ýÓVŸFÍ1ãÀhïcÙ™ëžËès -«ê]sõKü†Ž ›«¿ÖòöºçúëíT½7­½#mò )¹Ÿøß³òçØGšEû®~îkg=ç“qwÝáý°±üÞÞí[ô¾|Rovvÿ1®õ<ýµõã#]nÔ›–ï^]ÝÕæç®Ž©þ^2–ßòÊ>½© 8©»¼v9§Ò]?ýG‹qWÝ;Nošá¹vÔ²_rÏ9êí±.}×&Ül,—|'ëW@ß'£rCôHÏÖ×ä¼ÒG?ýƒ¶låKëõ¦aUÃd5‡V­ûúä—zôÆÄ”1iÓŒeÏ¶ÚøñW{€—õ¾«}ÛîÝŠwè§?{{¼·g“ÞÔݽõ¡º£æÝåÖïk,“ú›w®«¬ç]áu#>¿¡ƒ~ú¹[{¿tȲ‡¦ÄŸ-hGù„Ö_ÝÓÖ¯®ª¾SŸÝ½øî,KΧ?þØø,˾šBŸY»àñE¹ß)}ßh²él×›£Þ»ªzÜö|ÐÖ{¡eóô&ß;?ZÒÍÒÛ-<úæ´çõ8ɧ±ü\Ì7qM zûÓ?=”S;|JÀráú麯ƒ¿F$Øû©ÞÔ"óü“As?tØá¹6ê ^ÆŠ‘NWê• ¼ª÷½ç¶[òªUžÓ{Ú¾ßv–…ïØ·×]xîÓGìúÜüPôž{O|0êÇ£¯ƒGéÁ÷[ï{r}€ÞÂQ®§·w Îýúäœvû†Ì¶í´èÔØd?€“ú±ÇÅí› ïXíÊéê·ÒËÂF[òû¾£~M5™a¬lÑûúw¦‚GéÇhïƒw<2_? 3n;Gé¯~?½Í!gš·ÞÌn|àœ±ÂQÝ¥>ìžrsÔ(ÿ¶\¤ü,>^=5?Ö¸ižm¯/}{6Ûûmà¥^ì6Á½íú Iü[Ê‹­,¾_Ý~Ã= m ,½Zqì”áqïfà•>¼°&ä“qw7ú–Ö_½éî·ïÿ!ÉÖãÇúîÂc¥hæŸ~8UÿŸ& |bVµ~ºml¯ñ{l~'ÿ0fÒÈOt'‡þ\sø®n •¦oVR/öxÝö˯³zÙzÝfËËçW?k•çÕŒå}¿öÍýuø¾wÓÞ1jÚpëkÓ«ô¾çßzhèFcÅ+…3ûEÿàhŸóÎõú±'iãqº2ý´G½ê3ðž‡Wçþ^¼þäÀ×5GKºýrõZcEØ]{~wߜԃ=úÏûNüý ~ÚìÖ;ëGÎ=á­¥wXõmÙeYß{fnýãÚ$òÃS?_ûéUVZò?Ò8‘ùj‡]轤|ý°£_Ÿjl<¹ëýú© )×D¹C?²¥nÂp§ó¹?¸vtþê`£±1úч*˜o÷ÿ=Tûðæýeîú©O6/™™’dÉïÈ=ÅZiÛ|»_4É­4V ¸>Ú÷Ø*à¥Ôù>·:vÓ¹(¾Oâz—_Ž¥—GªÛˆLîÅp—]oXa··¾Zqײ@»^{Hý¨ìßþêå!¿äÛrYtãÂܬþl£P£Ýg¬~veNí Ý-ëÀ'õ¤®ëæosJ§é§nðÅïêG¦ÄTÝ0äMË.åZyÛݯß31ï\O©u½S~½èó’]®gnïöñ oêG†.Ý\èö‡±1þlãë'û+íAO©u³{Tü2z½~êÑÉ·/kzR?Òý–¶§uŒO¬ò¯Ü"öË€“úP÷À¦nÇoÕOÝó#Žy–]‰kŠßðî³–ÞYöÔSÖ{ݧO…}üÀ)ýÔêå·ÏüÇ×¶œü7¤¿›ôïûýÒ%wDÞ7 xYÿÏŽ<‘ñùmôS+'¼\úL•UÞÃû|ó‹ŽVý×|Þ?ãþ=½ìñGOYÿÏ6D.˜62Ñâ÷TÉÌ/Î=zÀª¿Ão¾9ùæüÖ¶¼ûC½Yïõôë´Ô·:ú=Jêx¥<—þP«ß}\?5kÞ üò‚U®Ã/ÜúIÂâYVÿ¿ñZíÕ©{ÙX±âëO?vw^Öûsk\¾zÔc³¥Ï§&|5£Ã-Vüð#ÑýÇv|*÷¼Äkl|À Ëß©¹Æ”мs¹Ržûè C ÖÛv:<ê*ç[ÂõÃ×n¤'רxó»þeç­þÒXnšëà¥><ßÓ­aðä#:2½ä±vúáyá«÷Þê'7îï¾lä‰Ìft¥><¿ö®™¡i·ÚüËqŽ~xl·Grto»üßyÆW~5ÌnŸs¥^<f|CcšÝžŸŠ(©c f•ãpÿ¡ûwL´Æ¿¿³iå ¶~öéôÄòª¶}æJ}Ù–ðVÙ¤\›/¿Ûô\—R«>œã½ûàS‡¬yËF1­[ùº±âmºMµë9WêÏÞYŸ23 °ùTó_ÕÏê‡ãWFxû:[ãà÷L\]d8ÆYà‘ú²WÌFæ^§ŸTóÇxÿpP°ÿs=ž²õï&ÑP3úvÃ/zÑæJ}Ù{¨óîÐÚ"ý¤Ð¢Ì«õC¿.ïŠjåøÑXáþ€˜çÓ¥^ìmj-šýä™üÝsÎtýл»Z¾=÷¤]/õŸùsÆ¥ñ¾û—ÏNêÃÞ#âß«–üN¾\ûðº·Þ´Ê{h߬5W­íhó‹ÐŸˆ^êÅÞ7ß½s@Jž~rký:¯˜kyèñ!+œOÿ=÷G‡Ü.{r——¯£Ÿ²ëQ—ú±÷§ÉÃî¬õ¶æo'ï7V–~ºeîò/ßúÔÑnÅ(®hœÞMê©C~öxH—úñ‚϶ÜCÆë'¯=yýÒož´ÊyhIñ´s 1¹?9Úm9_w¬O/õá5=¹Ø©øÍ[·è‡f¯{~hþjÝ)OLÌ#My¢ÃÙl,{éÖ›‹æ'ëÿ…àôü¤O÷Zõ~râé€/æÓ©q’6´ËäçúÙØô€9·ô\Í3rŸÒÿìÈÓV½œ·6wU?[>“^ŽÍ ;¯»Ìn5âÄ„ÍÆ¦Í·Lúè™{½!OéIÝŠ·è’-û8™½¤®µ6Ë®·©÷®}µýÕz‹²«¿ýìõƦǶ†œ˜ò¶­oyJoæ]ýÓ›QÇ-;9ópߟ]-¼‡Ìfw®£Ý56ÉõG»hמң0s"lµÛ'½ßºiô·•ú¡ùÙ7^ãù¨5nÚ´vÔ²ŸïÏšYó‚<ÕΈÒE¿jµ÷'¾±ïîOé‡JÖÝ~Ë:k\¾éÉ!]®[1ÈX!†)÷ý¼Ô—畟xmôœžˆe¦!ÖøøåWîf,›°½ê÷óSíúë¥ú•ƒ?.ÿ!o¦~¢ðÕˆí^ÐIøÝið5Öxosî¸Q}ábÍÏ—m»‹žËÅ7öRýŒ‡9ôàDçg|g?¡êÿé7ÏûÊêÿ7ß}ó®··íuÀ^ª1»Ÿ8KOtn5¶EÈݶ~çÜÑûÞ§,»ßü²û™/׃z©~%%)ï‡yc,¸!AëWd&é‡:­YµÿÝØ,¦y÷ÞgÛíò˜›‡ô]iëc/ÕŸÝꞯôΑ½ÒV?s¤8êc‹œoÙëJ½T¿‘µöý)‹ÿãŸíœ³þ'Û®"ÞÝÕ{­¥[:ºEÜðô özX/Õ^¨yÊñS÷2“JµËñÞÿèil 5;@Ǻ]Ÿ½U;!Äb\°×y÷Íed0Êæ#BÔdWk~µù“·ö¹w•­—½•^¼Ÿ<ù½IÍÊó´[EñMíþ%tÉSC<^¶ú©Í[J½wÜû™]žÞyÿÆ.?Zvc—ñ·è‡üû~ïóÞV¿´ù6 ß8úU3úª=Pí£>~Ïöá>׵⇘U 8ö¨±ùÀSçÿ¾£³Ý÷VíÁô釟J{W?~Ó'õ‹Ú>kñÝøåõ's~\glñYf·×½U»ÐULLlþKÅJ‡%ÏÆ—æ.ÝÛÁÉ’ß–ao½¿jœ±ì'š€›Á£Ú'³E°ú×ãs¢c²ìv¿ñéÏ=·b­ÍO¯·ÛE~–e,ï4{͸ãUyçú¨ñçîmW]ð~|rժа»­vªñ±uhÄ϶^Ýè”ûY¤=¿ë#õá¹™û/f?ó¨^šÑâ³A·éÇG7Þpañ\+Þø  NÑÓÕ:í–êƒÝöÞSb¯/÷‘úð\pøð¥S_×+>Ø{r «õãG0ö¶âëÌo=oúÐ_oüÕØò¬9á4–‹iMǯÀ#õãY9žÑ+Å*eÒ)ý¸¾ò˵ÅåVÜÁOiưßt÷;v¿ÑGêɳ·]ãU¥/›•tò¥i­õãª<Žx#¡ëU!ú`9î4¶Èñ½îÙGêͳb•¢o“®ú[ýxümŸ¸l¯³âæ²è½à’rYãí>Rožõ ]uǸùxX ’™hÅëž;èw8A)ç³Æ–]™NwùQO•úaÏ«úHý©;õÛ·»û=ª_u•‘Íá»ôãþ‰(+Þ¸ÿÎIñß¿¤‰zòÛï»ì7ÌÕÊìzë+õ§Î,†—~µX>¯Ó·Œy»K»õV¼ñPÈ ³ãVêã„5ûÒN1ˆ™:áucy/sY®÷[ý¾o׸ý}»î+õhϾž@´ú*¥?M(#{+îóÔ†o£«6û[ãÌC»’ý3ø¤>í¹}ø»ÝgNÐW™Õ¥7©ñ¾#ÞøìôÛ^è§ Œý½ÌØZtnØw½ïX/vôkà“zµ©vøæ#ýZ×½Úc‹ºèM¦9gXñÆÚÌF$Ô§½yè¾µOM6¶n~¨÷¶ãv{ÔWêÓž$gïÏMѯ]t¿àPoÚS±ð•׬xcMᲞ¯_£Og²R¾à]cëÇœêÔãc¹ÙÌ3nê'õi7ZP¾êŒ~íc®£bÛì×›¶n~d̃=­¸Ãîf¨öd›Ï—XT »é'õi÷þØê¿©_'–ÃWÕ›û‡gÏ’Ç­xãúc!NOÐgª}¿mQb"¾ÐÖï~Rvßn–~]Þ°ÇköÑ›”¾;â¢~~íð(}Ö­×^³aý1c[LæÜE…´û¿‡=U»ó.ðH}Ú=õ %«_'ùÖ›ÌeøR+Þ(Ô(óW}vYÚëfp”Ë¡à‘úäØï¸N¬†ÎûTo’ë VÜÁÏœ>çf¼•?ß!g«þ­ö»ŸÔ§]4Ö}çäè×#õ¼ù½Þ´òú]oЭxãƒæŠ^,íÉÝv©m¿ý¤íê’¼C¿~ù{¶®Ð›*FÝñíÐyV¼qmà¯ÿ–­Ó8_p3¶î¹¶Íä-õh×ÏŸ3‚ûÒÞ§„ZI]“5þmüûêîõíiÍ×·6¼{С?ô·ÚñþRŸvüÛâšäÕzƒÆŠ½ó¬yDãš4ÿÃ.Ûäú‚Ýß÷—ú³Kéë±ïÌ ±Þxݼë’þæe­³oÃ_çoíqf©/»V˜gk¼{ìl63Ÿ1ÖºW㲉™µÏo­ÿo[ÚðL×B?ÛÞCÅÂq ø¤Þì2›¹eö~Û1sƒÅÂßX¾¤÷£=œ¬}ÁmÅ“÷Ìï^§wQý¾Õõ—ú³K¶Sú±ýbáëW½±lxÍ3ƫƲë×è·;5ï\¾¬ÿ§ MJúm—]ÿtŠí·¸ë…õ·.ù¬Ø÷n»ûá­§×ì»t^©µæ´È^w9–qí‹·î›§7ö6>a޽.¸m³P˜û“õ^;Ù÷‰;K£ôcɾªW…ÅGc§ñ¿„‡\°êmÛëÁ7Ñ5ÚrÌ—õ¾]ô¢]ûØrŒŸþýº¿S!W#¹‡¬úØî_vÞó£,»>óeýoûM @ZØr 'MöøÝmå—e©ÆvóØ€n,—û˜ÀK}ØÖí*zòfô%½áâ…^ýCÛû®MX}çv{/ëëôœ7zD†¿ƒwƾV¹>Ÿ³ÿÆ‹[í‰úÿ½¼=ïÊ—õ¾¥|s〖Ãm9·:)V-~ÞªyÿýºƒÖzàv­nö‡Ýjì}­²Þ7ß*&”÷ÙçÄ®uþ)K ïëùõ[>¸ë½óåûô!Éé翾×ÅXfnoôrŒÁ+õ@­ÃèGÿØôzÃŽŸïÞ^ñªmo̸nM£JlGž2€“z°)º«X‰µ÷cÍá~ e_ ÷½ôkŸoÛÞ}\ÿ¨1 8Yÿÿ&þ= ÇŸÊ·è •Ïÿ¤cŸÇÙ&ׇ-;¬’ûÿö¼~ ÔƒAÙl¼ºµ½¿¬ÎXò(Ü;¾Î¹›±=üŽ¢êòB£Jô‚Wÿ ¼ÔƒškçÕ|Þ?Óæÿû/˜¡½ 7ˆ^¶ÿKÿ·Y$Z@£ò½´]S¾þx©O½# l­~ô‡¼Ÿ2È.wôíí÷_mµÛ¦{ôjˆÔ³.˜‘ Ç|Ì:Ç0PꉹÛÔ©§]súkïÿ4økµ.Åö¾óöù\u®ëçÖ<§R¨s«{íõ¸ARož»Kƒ†êGIÜ–P~¿ÞÐ"a‚ÉÖüvûÈ_cÇn~ÇX&ÏÕ'õâ 9ÿ±Ï3ˆÑU‡U΃¿ÖÖÇ ³ôm»l‡Œe?^<´jÝ7à‘zò“BZäÛ÷ùvßn°Êsð§£·ßßéŒÃÎõ5´Ö I}yt_â™vïl´ìõèglêvª·ƒ?§gÏü:ÔØ.Ïq8Ö〗z²Aœ®X¤•z ßC‚Ç»½n·“?½ÕôQ‘¡æ·ÀIýX?êý×¢oÜ®†¢±«¾8­”çùŒmJ_¬ö•ÑÖM'ìz$õäauàè³–¾½å¼…ïà¯üÔ%iŽÝO~÷Ð÷‹¶Ü¨§ªý;ÕîÛû:ƒ¤ž<4}ޢ­h›­úÁ‹lñžþ‘=®’çIìñÔ`©“ã5»>ÕùƒtÚëÛÙ|´[^1ªä¸x©÷=îôK͵óízØüŽþü?vèű¾#Û^Y8éç-oØú¨Î“¨ñ)x¤>Ü­ÖÍ,>¶w'ÖôƒM3&ôž¿Ëw¼:hC+çHcéG·¤}7z,õaí°î¯fÖÚõòtû…áŒe>»bOÊucm9¨v«2Æ/õáNÙ¾Úôw·Ü³©ãëúÁ 7=þø÷Ûíž8U5n´µÎ[i.TÙíÐ`©'·ªó}?b··wª~ðÖ ²BïT»Uí±C_*?ßî=cx¤žÜ¬ÎE5íåê«Ú—?Za·‹²}³Û¡ÁRnØUþÙÎ9ìòü¶äý –ZõtpnÔëùoü»~µR´ IòÎ ‘ú±JpÕÖßî_T¿zpRÅ’öïßi·;²=u¬¿Ùå"õDЩyñA±Œ¹ãc»ý•/4ªäz+ù¥>,m}2ôÁÙ?‘_žq”ã`ŸŸOž‹4¶½ßgì}ÝbTÊu औŠiUÖE{÷|—/ƾfÛWFé÷D}ÚØI¶ž\óýuOt¿Ã¨ósà¥~ô²N¬Ùãv1œ ;añyà£qïøÌþÚÖß5ÜS1Ú¨çrÀcê‹ÑW•ÃâC¬"kÝõõ¯ÑB‡Úåz8T¬Ôæ2³–wn¨©'ÆŒ¤1ç;¼ý…~ŒÁroìù)SŸƒÅgmúæô ÑZÿ\â’úÁSo{¸x ø¤žÜ}Oêò#Ã~²×Wäù*‹¿WÌíÆ(«>¶~Ý&ù¶QzšL7–Èu.{_~˜Ô—{½|EËcɧI,­µç篜<ïùž«Í翼°""­Yy¥žÜ7ÁÜ@×›R/Š“ªú+ϼõùïûz[ýòÉB£¢ýµÁ‚¢É/õâ~uÀ*hÝ®©Ò_Y3É%+h¶eÇ[å=SÖQ&×Á#õdÝOO§Î:ù7›q: ÔGeÎÓõš÷b›ïׯ™¾{ÛUF©<7 ¼Ô—ä~ªÞ´jÿL±myöؼ꧹Zv»õ½IOryEÏPçKÏý¥ïìùÈp©7}sìÞSŸþbëÍ}sv}ýâ÷V»÷Šÿu»oøÂÚ¯SòÔ3Ÿe{~wßöK’½9\êÓ#jÞe•ó)Ñ`¬²ôéåOGu÷«`—W¬R.¹ÃaÏF¹Ò«ý.õj}uù¸K>ЛԾÃËû_yÿÕúcëÛ‹Ä£üEs•üR_6Tuΰ8QoÚgné/oÈçs—Õn*ºFyæ‰_œÔGÅ-ŒèMG¿§¿|íô~‹—¼alýìæ¥™ŸôÑ»©}ý²ëÖô>³ÿqञ<&vÇ‚ßÔ›ÞîÒî‘+õ—gŽù쫈v9¥~eÒÞ€“za÷æmÔ›¾h#zxýå¾×ß3ìµß­¦zÏÔsÕø¬tï˜ûŽÔz¼Ô‹Ç__6Ø÷ Ìe ‰V=¾±¬ýà5‘Ö8|«:W앚á«íö´@êÇ«ÄFK¨~<À\`·ð¿ôc—¥ûÍ·Ëen„%›ŸVðÊçÀK=x2þðñ˜AÓõãæöQ”þRÃñ{ož»Áj϶ŠcÆ=k’VæÂp²žŸÜt‡èAìýßÛ—¿—oÅ_º¯!Y{ÐËn?fþã—=o4ïüyáùƒÔG¬ÿ§ÆŠƒsì}PaUcê/Í-Üîi›¹e”ˆãv›¼Ôƒµ.lñqÍm5mï ³åç›sm¼±•N»Û]%P)>p7ðRjž­¼¡ìÁxýøºò?¶„%ZíüKNEU¾oÛü›ÃÈÁF‰´kà¥^lÌØ²ý‡“©úñZ¯Ã‡9Yõ¹ÿtΖ‹l=Vë%Žþ±dCö··Î¶ÏIH=Ù(×iíýesºèlÙÿþmßLßÖš¯ªzÕ•¾—ˆeÝ÷Þ°ç½#¤žl2Ÿ¶×Ës|V{¼ÿöÄwtÛgë9=_`”ÊsÀÀK=ÙtÚ\ÐÖO´17ôý‹KŸúÀ½ÊØêòúÆÑ‘õF©9Œ. ¿ÔͲ½±ÏY¨ó¶ûåùcKÃ;‰/E%%æ²q,pR¶ôxø ãé‰ú óO´¾¿»ËûÞǾ1¶˜ŸÇ%ævÉRòËúßrÎÜÒO{œº7$Ißß¡÷ó³Ë~µ÷Íåý1c±X¥vb\;BÖûÖµÅeµ¯Xr=!–ûæî·Ö}÷kzîxo®Õol¹ÙœÞC—•Ûö8BêöBqÝÖ£â¸Ñ¬ßõ}ïݸ.3è6k^¹åšÊí3[v³îÏ-~ûó×µ;–€GêÁvós¤~â@Xy°Úå}/ÎÖö®{ÔÒË-rÝXœÛé¹òä¼s#e}o]|£\Ÿ‹Çô}÷?_îž9ÞØRe^ð1šèÇ‘_Öoíˆ>FYi¶~Ò{ǽçÞ{Zß·ðƒ´¹ï®³ìO•ߘçX)ë¹v“ØøßeÕóI¹®­ïëíÕsЇ‰vyïv3æ8æG#e}×šÇ çè'{9?ü´%·}ÁW¼±ûy«¿Ü²ö83Ý,ë\à1™å ©µªß>©æÃ~꿊pÙ|c’}~Aœ>›¸JÏQíj±Xf}p-x¤^<í..0øØçûæ NÏïdñUxû}#ê,»Þb.ÏYõ8ï‘ÅNø¤><­ÚÅ“«gݳ}x+›¯ídm™YiëMbË™Æ|¹ ¼Ôƒ§Õ:ÅÉ'}úM?»^¯ÿ[ùë_”8ÙôÅ6ZË;lº£dýï ±¹z¥›}îÒ¼Þ8Ñj'ëo˜íúÕØ[lúÙEÆäªUFñÜëFÖŃGêÅy®Ì1/ÔOšÃñtËnêK¯ž8ìM»žÙ–ï³å:x¤žìT÷MN~U‚è­ö§~¢ï×­/±ú©-Þ˜,Ó³Õ¼d¶ÇXfbýÀ#õeçdó@¢U®Sâ4ˆçZ«Ý­Ïsç=¶À*—8•_tï«=¹ë}?_û©Ý?’ú³sñÕ=ÛïÓôS©æ Œeoõ¦úÄ›Ííâc¶´GाìœkHÑO©óqÆæ¸6Üo•g³yŒ¶›1§­XüpR/v_¸~ÚOôSWUO™šò©nlúãÛyØ|wÓ¯ÏFc/rÿ8©;¯:×õ³·Ç[õx £AÒV»eÈó;Æs˜2Ó˜#×WóÎÉ+¼ÆÎuß¿6¯Îê_N‰SýŽèFPžKÇa‹Œ-f³ÛͶ¯ÑRvîMºçõ¼eú©7LÅÒ_|muvécŒ-j=}¶\Ï%¿ªoynÌ>o-ޤ?dÉõE9î¶õ8HLè´ö§ç´ø£O¯‡Së à•õÿŒy½ ³~:¤faé·Ó-»|qÙ¦~cµ‹›ÅéÞ!•FHw’wü…K,B6A\b\Å¥EqAM\BPÄåqÐ\Ô™80* ŠaâP8ˆ!6ÏÅF«Øl/b], ŠE2±"&·b¢"£b !:Ñ eCKG($Œ'L L$L"L&L!L%ˆº.¦ffffæŠ s óó  ÂoÁbB ¡”PF('T––* Uáða9aa%á*ÂÕ„kÕ„U„k ×®'Ü@¸‘pa5áfÂ-„[ ·n'ÜA¸“°†°–pánÂ=„{ ÷î'¬#üðáAÂC„¿&Â~ÂK„— ¯ĪÂAB¡‘pˆp˜p„ð*á(ᡉpœp‚p’pŠpšðá„3„× oÞ$¼Ex›ðá]ÂYÂ{„÷ >$|Dø˜ð‰“üW¬äpSEßµ"q ûuF¿ÑgâÎÄ]°7tÒ¹º¿ù[ -š¤Y»¢Ï®Ô™+|µ$oKò¶¤ŽZ‡¶ìn7dêvÓÇöÜ¡ínwÒ=ˆ{ /ÈÔZžÐö„¶g­l¼Àå…¾x!S/ཡíMº7é>´#>Ðö¶¼´"ÞŠx+ølŸ¾àò…O_hùBË/‚m¿zÙ¼ø“în`à;€´ðÛ¾Zo lkø $Ý@Òá#(@<¾ƒˆWp½l¢Ú€» åjC]‡ ƒÊ‚LBà£-¸ÚÛØvðÜŽ´v¤µƒN(i¡¤…‚§=pí±õö”§=ia”=Œò„‘NZ8iᤅÃoà: çµ²ù.`"ˆG€7"¡ Hd)âðÏQðE<|ÑÈ.š2D! é1Ћ^GäӸޤwD^±ä% ýXpÅA/޼q”)Žx<ñxâñ ²©MW° ÄÁï‰À&¢3I0œ„¬“à5 Ü(K'øêÝÎäí _IëLÞdÒ’Á› dÊÝ…xäÕ¥V6Û)àMo xRá9•xjlÊÓø; ÒÀ›é¤›OÐÉ€Ç Ò3ˆgÏ„n&t3¡“EÞ,ä™E<«AvÙà~9„OŒä‘Cþàs ÝÚ])SWh‹;òÝà³´ºÁwwòv'¯¸ÓÛ]Äá¥øÅТm¹Ò‡^éC«®ô¡å>Ô]Õ±('ö«¡Ï:¡×h/œóÔ±3qgâ.tι¹¿ù[PÇ-àÃ\®è«+uâ Ý–ämIÞ–ÔAKÚ7lÕ Ünu’¬;¶åmwp»“îAÜ}ð@fÐò„¶'´=É¼Ð¯ÙLxCÛ›toÒ}h'| ímxiE¼ñVðÙ >}Áå Ÿ¾Ðò…–vïm?pùƒÛŸtpûߤ€7ØÖðÕšxk`[ÃW ñ@èÖɦ("T/›¥`âÁà wp·wÊÕ†º A!”#™„ÀG[pµ¶-°íà¹iíHkPÒBI O{àÚ(O{ÒÂ({å #-œ´pÒÂI ‡ßÀu@Îà/úä Þxˆ„N$t"‘Ud“l£à9 £ˆGƒ/ÙES†hâ1ä!=z1Ðëˆ|:ß‘ôŽÈ+–ü±Äckd“½8òÆÕËæ5žx<ñxp%ÀO¸€M žîDxO6I Y'Ák¸;Q–NðÕ ºÉÛ¾:“Ö™¼É¤%ƒ7:É”» ñ.È« åNN xSÀ›žTxN%žJ¦‘–ÆßiðÖ ›õtxL6ž2 “¤gÏ$ž ÝLèfB'‹¼YÈ3‹xéÙàË_6´²©Óä‘Cþàs ÝÚ])SWhw%½|vƒ–ðIÒ¼ÝÉÛ2 ¢ûèþà7}ˆöQüsô¡¢ïý¦è//×OŠ>R(¥£_lÞ':úCÑŠþÏÑï‰>OôqŽþíÒ¾Môg¢/û³>¬y_%ú)G%ú§æ}Óÿ—ý’£?ýÐ¥}Ð¥}Oó~ÇÑçˆþÆÑ׈~Fô1¢}ËŸõ)ŽþärýˆèCD›z¹>ÃÑ_8ú Ñ/4ïD? ÚG»/ÚzÑÆ;ÚvÑ®;ÚrÑŽ‹ºý0z¯! uæodìR+‡\-ò”Ù•¼®ðß’´–ðìAÆ­ž6˜úv¿àðä›ù½h'¼ùÛ÷¶ºÕŠ¿[ÕJÕó¥=òΛñ'¯ÿYÚMì8€oÀVø ÂN‚ø;˜ß`t%ÛiLäÞ; §x _[xhKÞ¶È£ôÚ‘ÖŽ´vðJZ(i¡À·‡ÿöèJ{xiOZå £œa¤…“NZ8i(cxˆà7Z‘àŒg$剂—(xˆâ{40Ñ”!š¿cÈ®pu$oGÊKz,øâ oñü& £p$—\"p‰ÐMÄÖ“°õ$à:Ás'htWgÒ;S–dpwá{ÊÒÞRÀ“\ ùS‘_*u˜Æ·tÒ3ÀŸþ ðg›E9²IË&uÑ•ü]‘[7ðuƒ¯î„ȹ‡°“+ãð+ãðj§+ãð¿ò8?x÷GVþÐ m'´Z“7ø dÄo0ºL½S–6üÝi³ž€­† Ÿ!ðR'Õ¹-ôÛ×¼íHkGZ;ÒBá5”´PÒBÁÓžrµÇnÚC+Œ´0ø £üa¤…“NZ8iHëOà#|Q0íhø‰†~ ¼Ä\Ghu¤±”7Ø8pÆA/^âùMDnIØ}y:ÁK'`:ƒ§3üuæ[r:] Ù…ô.Àt!ž‚Sà'ü©Ô]ñtÒÒÏ>>2ù–Éß™àÊ>›x6 Æ9¤wE¶Â®0Ïnàî†ì»ÁWwÊÔߤõhr²÷¬®ŒÃÿzãð+cð+cðxUÐѰC ]ÔˆkðíŒÝ9£{ÎĤ麠ë.Ô¹ é-Hoîµ@Æ-h\ÁåZD@f®´M-±›–ämIÞ–ÄÝ€u—<»ënwê×½^š¿qêÓ^¿´_¼\x¹>ðÏú¿ÿ꾯ùÚºèïþ•cùæýÜ¥ýÚ?ëÓš÷c¢»\ÿ%ú­æ}Ö?ë¯þ£ý”èŸ.·æÞ¼_}’è.×]Ú]Ú÷ügúGŸãèg„þ¬‘p:ªñ·F›áŒÎ;Óf8w©&ð­ßZP—-(³+p®Ô›kTû–èZKò¸å€q;‹ïÎ7ìÁƒ¿=¡'¿žÈØ“t/pxQ·Þ|óæ›7ß|øæƒÝú`£­ø»·¢¼¾hû‚Ï>ýÐO?`ühüIó'Í¿NEH €‡ÊÔš¼­‰·&Þ<ðH<Ø@èÁS0v Á¢/ Lm¨³6´!”)„²‡@³-xÚÂg[ò·…f;ÒÚ‘ÖŽ´PÒBI %-¾Û#ƒö´í¡†ÌÂà- Üa¤…“NZ8ià£|u€Ò"ÁIy#)GeŠ‚—(x‰"-š´hxŒ&®¾uä[GþŽå7¸XpÆA/zñüÆÃO<¼&P¾à€ID¦‰ðXO‚,’òå°ºüwW'òt&OghunCídÚÆdpu!O—³â-eHAF©”'|©È:ïiàH‡v:ùÓÁ•í pe@?“ôLþÎoy²à-›o⽟l¾åðwé9äÍwWxëŠ|º’Ö wƒ~7ê­pÝ‘ð¿ßƒtÓ¿¹ã=¨œfç)=wrúëÏsþ§Íqþ{ ÕNWæ8å9N¾’!ù4lHC×4ât#EâÎÄ]°Q—<uæB¼é-ÐȨíŒ+vàŠ®¸Ræ–ämIÞ–ÔË&iânÐr—m‡;´ÜÁíNÜtìÛƒ¸õáA™@´ýŽ6ßѾ_zfÆÑ~‹¶[´Ù¢]í±£í¯£Ýí-å2ÛÙæcrÑ–:ÚQG›)ÚÊæí$õdµŽ6ÐÑÞ5oßíÙDzòÒHsF‡ùucd~[ {WêÀƒòy’ÇYz¡§^üz#wèµ¢ÎüàÏŸr¢KAàhK}·>´V êäÐ!=é@Ù:H1%oTнBt$o,8cÉ'ôzŽc=ð'B;¸$1¶CÏ;ÁWgl&™¿“…Þ‚7…<©èu*ùRá3x¿éÀ¤‹1ù3á-›ïÙäoÞu%W1ƒ^7ø1ß¿9«tæ¿kœ…¼ÿåkÉÿ/Œ±þ£ã+ÇØêŸ­ÿïÆUÿ]cªÿ)ã©B%ìWCß5âq 8gôÛ=p&îLÜ[sA/]· ñäo.´ Œ-°i×<uìJ™Z’·%y[R¯-áÑ ;vCgÝÀåFâAàowÒ<°Qðx kðz‚×S´‡Ô‹'y½Àã^/díEÜ›toÒ½I÷†®t} ãÓ ›…VÄ[¡3­ÀåK|Áí -_hùQ>?èøçOš?ºê\ü@¾dÑþZoM¼5xá1°ˆÍ@x6‚ÀDþ`l=ƒá1ÜmHoî6È&„²‡P†x ‡¶àn lÛZÙ,µ#­ií JZ(i¡ài\{dÖy·'-Œr‡Q–p¾‡ó=œïáÐîLxío A¾âõ²y‹D6‘âoøŽ‚Ï(øŠ" žhxŽ®—M_ i1àvGÒ:ÂWGÒbù;–¿cá1zqà#_üÇ3žx<<Çýð$@#¡^6‰À&Âk"ºœ„“c’èc{'âÀÕ©N6­©»Îð›Ì÷dp&C#š](câ](ch¤€3œ)àH¥îR‘G*ñTøI#-r¥3x:ü¥C3½N6Ñð—AzF½l®3¡™ ÍLdœEÞ,d—Y¤gA+|ÙàΦîr…xÃV¼›í®Ðo‘Šw@»’.Þ½ìŸÝje“/Þ'ìŽ,º‹8¼ôð÷ =ˆ›ý†ø×|Œä8ÿì诚Ÿ¹ý‘èsý‹£?ýˆ£ÿý†£¿ý„è.]:ºÜèrãŸæíXóñc¬ƒ,\¨¯ÈÆ™ºRç-‘¥ev¿òñ@žØ‡çEÕ•#ÛVÀøëGù#Ûþ kasÀ [£¼AȬ#2ïþXàb…þ¡?ñÔUp‰”·ù;‰ñ7t“ù;™ô”¹Ä‘JYS¡›&ÆÑàL®'ušIÞLxÈæ{wâ=D»eÝäÊþðÿK}½˜§#|FøœðáKÂW„óNæd§¯ ß¾%|GøžðáG'ó~•ÓO„Ÿ ¿~%üFøð‡:÷Écy±¼ÆX^c,¯1–×ÜÔž4cy±¼ÆX^c,¯Ñh´}š¯ÚK  ÔËkŒå5ÚB¶Pc,¯µQë=Œå5:M¶Q£mÔËkŒåµjŽE‡§Ñ^j´—šÆx^£ÝÔâÔø†öS£SÒÏkŒç5:ñ¼ÖEÝ¿¦mÕÏk´Ëö¯aÿö¯e«»eØ¿†ýkØ¿†ýkØ¿†ýkº:óŠýkØ¿†ýkØ¿†ýkØ¿6@íÇcÿö¯aÿö¯aÿö¯ WkYØ¿†ýkØ¿†ýkØ¿†ýkãÔØ û×° û×° û×° û7ïŽcÿö¯aÿö¯aÿö¯aÿæ½8ì_Ãþ5ì_Ãþ5ì_Ãþ5ìß¼{ýkØ¿†ýkØ¿†ýkØ¿†ý›çy± û×° û×° û×°qnLÃþ5ì_Ãþ5ì_Ãþ5ì_ÃþͳØ¿†ýkØ¿†ýkØ¿†ýkØ¿¹„ýkØ¿†ýkØ¿†ýkØ¿†ý›ë|Ø¿†ýkØ¿†ýkØ¿†ýkØ¿9nÅþ5ì_Ãþ5ì_Ãþ5ì_ÃþÍ{õØ¿†ýkØ¿†ýkØ¿†ýkØ¿ygû×° û×° û×° ûg™5ì_Ãþ5ì_Ãþ5ì_Ãþ5ì_œ³Ð° û×° û×° û×°±G¦aÿö¯aÿö¯aÿö¯aÿb SÃþ5ì_Ãþ5ì_Ãþ5ì_ÃþÅú†ýkØ¿†ýkØ¿†ýkÂþÅ?Ñ¿”¨v°AÚ»is9ªîר2 :Ô~]:—]«ÆøîêlvÚëUc}w5Þ/T÷%k›ÕÎQ絫äÝÑç›çýÔ_¡ZW­•k«æù‘µïW¤ÎÖ¨s„NÊ7Aº[¹Fž)1}¸«;–j¾°Fù+8«ÎyG¨³ÞÅê¬I<(Ææ¹ïuö{Z›=£îa¦¨ó'%ê JÚWtWgQòÕþâúfw3óÕ>c­»˜{ùêŽfºS¯Æ4ÕrýÆ:Ÿ’§æµÒçyN%EÍ?ªå¸Ç܇tRó5©Rk»uÊ‚»ºËY¨îs®“÷”Ì=J'¹NdÞí,Vç%kå™I1†2ÏM橳“«å>¦9w9£Ö‚Ýåys¡Î­¨»JÕÒ‚¹¦Û¤ö(ÝåùqóÌx¡šƒä«ûJkÔ™•ynÅ\¿=£Öp#”¿ƒbéóÀÛ¥¨û™Uòl¸y&e¼·$ö+ŘÏ<Ÿ’£Î¨T©s*uò<¥é÷ DÝÛ,TþÖ©ùÉ5G Qó”B5WY/ç+â,¹¹Þ¢Ö| Ô¹ò5êly“:_î§îFªsæëÔY—3ê¼Kˆº3U(Ç¢æÝ©ZyÊÜ/õS{¦jßt:Þ¤Î{ú) ùj´FÍ…šÔ|È]ù\ÈSçeŠÔÝÑ5jm¹Nž5טÔ9ö96ïhU+ÿ uò~©¹/¢ÎÀä+ ÅÊBœë˜gEÝÕ=-?åK!_ÍwêÕ9˜õj®Ó¤Ö‰ÔZqŠ<#æDð _ПóÊ;†‚k(¿Ã€Îaà~e‡Qîað*| ?žÃákø9 (à{qá×o„ø=+§# üHÊ2 Z£H>´FCCø´ oc 3†rŒ¡Æ ï±È`,¼…æXhƒî8ðv°ãà­ØÂ|9½ñ01ž|ã¡?ZÈ?‘ô‰”u"t'"߉àŸþIàŸLžÉä™L¹'ÃÃx˜Þ©à-"½úEà+_2,nZˆœªL‡ÞtpOÇtpLÇtpÌ$ÌÏ,ôdu=«ZMc€+_1¼ÃK1å(¦s‘Á\êd¼Í;#§8ó¡?Ÿ¼  ± ‘ÁBð,„…ÐYEÐXÅÄ/!^B¼„¼%èD ø* _\r­ ÜK UÅ·ªêfýï_ý¼ÀÿÄsÑWæ¾Wæ¾Wæ¾Wæ¾bþseîûß?÷óÞÿ¢9¯&ì^ô«¢ï¨Rm\“´eÓžòT½®Sü \Õ9¡Bu¢NùÖóS÷Ý Õy¡:µŽí§üìIÿb¿Þ¼¡ö»ŠÔžWô»gÞ•Qg‰ŠÔþWÚsRw—ó•¡ÕÊßu9E­R猔_!?åÓ¢P®›÷*ꕟ>wé'G왉ó7Ÿ}æ= 'y÷ΊÖK?Eqg•¯¢éÓÃ<ÓQ­ÎóÖ«;NêÞFžº»Q­îo4¨}º¹‡%| _yæ}ŽuòN‡ðhÞQ Qw«‹ÔýŽõò ”é#ÄIí½€¿w¾:µNîí‰{×æÙ¨u>ªP‘Z§î8žQwAB”_¤¹ocÞ ©“wCL?ƒÈ;y6|ý‘mÿ3rX9ˆº„ "ÿ ò ßuòŒUïy7Ã<ƒ\¯|”3£Vž'6÷ô ¤Ï#ñ¦ŸyžÜ}׫»ðÖ·Fù>"_ßZ駤o:[Ìoßz鳤oƒôÔø>àë ¹”¥/x{B§'rÉ…·\xÉg_àsÉåW^_'^x¦|È0=Í#_ùòÈ“/½ï ¾ÞàîÍ÷Þ|ïÍ÷>ëå»ò)¦<ôCÎý Õœýà¯?ù ›”cø€xPž|Ò’6´”q 4!çAäDY‘>˜ú ÞÁàLú`è ‡)àSÈ'Þ ê'‡öÃÈ?œ|ÃÀ9 œÃ 'ÞBï› v8°â-ŠáÀPvñ~xƒ@ø<æÉ÷‘À~$eEY„¿éQ¤†Îhx oc 5†rŒ¡®Æ"ƒyÈi,ôÆBg4ÇSLeˆœNWÎB`ÇCc<4Æ“0ž¼ãák´&@"y&R†‰ÐH½L¤>'Bc4&“g2y&SöÉà™¢¦#¤!·"p•S¶"ð!›"঑6zÓ¡7ÜÓÁ1ÓÁ13 ³À3 þgÁ{¥˜çS ¾Jø(^Š)O1噋 æR'óàm´ƒk1ô#‹J~+Å/i Á¿Ü‹À½=\L|1ñâ%ÄK S‚.”·ÚЮ¶‚ò.FߪVË=R1^4ÿ]ñëuůןùõr̳šÏ±þwó+ÇÜŠy•9—ó¨ËÍ¡ÄüéÒ¹“˜79æKbŽÔ|~t¹¹Qóy‘˜9æAŽ9Ð¥s1çsÿ›9Žcn#æ4޹Œcsé¼Å1gY¤Æ±—››Ðv\v^réœä¯6¹Ü<½´æ Íç´GæÜÃ1ïpÌ9Ä|Ã1×ó 1Çpì­‰9†˜_ˆ¹…˜Wˆ9…˜Oˆ¹„˜G¼-ÏZ‰öÉôõœ£üää7ó»½^ÝÑ*~0\ÖË{Âw³yOºI6{¦Ÿè*yKøt¾ÝÌ;Z«å97q×¼ÏU,Ïh‰sXâ<»8ïfÞÙM‘¾Ýbªåy7Ó—e¡¼7 îêŠ;½æ™özÙ´ ¿â\°yw EŸi’gÆLEÅÍλùIÿ›Âצyÿ­AúM²hvÅ8áßGø½¾·…/ q÷XÜUîpVú.g‰£ª¥ q¯@øÙ4Ï™¹K?ÜÂ7¸Olú ['ÏÞ‹{æòc¼òKƒ ûÀkd܇¿{—Kùt1æo/ø×«¤/Jq^ªtzA·×§Ò§s¯‹òÞmoxì \.¸•ÈsV½¡‘KõãÁxé»?tú’Þ‡ ½þàì#}Üõ§ò‘UÊ4ð¢<Ë?ˆ¿‡ˆ_1žßü&éK§Yâw¿âͽ¡bl ñ~ì ð ai ;¸Ið>Šü£ø.ÞÎß“(‡xÛi4p“ 9™2ÍÏhpÌvFìŠ ‰‹·;&SÆ1ð2QäçdÊ5¸b|€Œfò½Ü¥à.…ß™¤—W ÌLÊ^ žià/¥þJŸIÙJJYÊ€ L4æñ½ ¹”3YNƒ¿E䛃¼ÊÀ_F€³ Ü3¡UÎ2p—Áoå,£îfS&ÆðWNý”#‡rh̯—]âRÒ§·\åà(ßR䱨¥À•ƒ¯œ¼åðºô¢ì. I›Ó€› ­Éü=šrL%ÏLøœÍ·9‚_ðÌçLàç&B{*0ó_ä] ò7‡ø<Ê4_ȘbìCÞ™¤Í&ÿ‘¾Zvç‹‘E¹ø.ÆŸä_ŒŒð[‰Œç­“ÝýBÊW.Æ’Èn1rZLÙ*Åx¯H.WÂïÂ9\XÅȯ˜…ä©à{%¼TˆïÔG¥ã·º ÈSI9*¡_ O•äÒF•WLù*ø.Îèšÿ®ì;\Ùw¸²ïpeßáʾÕ}‡+ûÿ²3wæþƒèEQ­Ú²3ÒfM»ÉWõ·^ñyVùBW÷”×(¨ÔÛ>9Êçõé“É|ã'D½QP¬ü£Ö«»Ëñ꽟buξV½Yà¤Þ-ÈW>S×(ŸMgÕœu§DùܨS~7ü”ÿì"åKu½¼çlúS Q>U‹ÔAë¥oUó?u–¿@½u°N¾w`úêpW¾žò¥ÏÓçj½ºÛè§ü®)ß«ë¥ÿUóÎcˆòéQ¨îõÔ*?PÊo÷jé3Ð|!¥™?¨†fþK”O¨ºf÷¦ Ô;Cg¥O%óþtžzohz/áSåÿ#Eù)Vþ¢j¤Ï(ÓokˆòU¬ü·ÖHŸ„â-ÓkŠò#U%}…ˆwÌûÖNÊoHô"|: ?Ù¤õZ/ß[0ï(çËû7â]áËC¼)$|ˆwĽjñîé‹0DúŒ2}‚«w å{6ÂO”:E¬—þÂÅ]jñ¾éÊ]ù†*T÷§×©7ò”?ÖÕÊGHµzW¨^ÝuR÷¦óÕÝéÕòþ¨ð#nÞ¡vWwòÕ} Õê>uƒò)â®üŠä«»Õ«•‘&åCÊ]ù‘ÊSo*T«·‰êÕÛ Nê¢<õNÑjåßµAùxuWoåË{«ª•Ï©zåOÑIù~ÍS¾«Õ[ õê='é ¶ïug»Jú0I®Wï3„È7„Ïó.Rµ¼$ÞDw©…_Eá]ø­2ïɺ+ÿŒyê=¤u×»FÞ[2ýɺ˷̻مÒºéÏd¼—-Þ_0ýšø)ÿŒ!ÊGz¡zǨIÝÏ®‘ï/æ=#?åÿõ¬¼ÿ;¸ž|ë‰|z"Ÿžðßþsá'—:ÈO.p:ytʬƒ+ý˃FyòÈ“Gž¦;~g“wÈÜÁ?ež/Æ\À,ôÉ;“´Ùä_ Ò)Óh/|‹ïä›CþÅ”a¿•Ènü.$ÏBäYNz%e^ŒüS¦J1vãÒ+áw¡‰¼ÐYŒL+€YHž ¾WÂK…øNVB·¼büEžJÊQ ýJxª„n8ªÄ8 ¸*`ªÈWqQµSâß•ý„¿Þ~•½„+{ Øï•½§+{ ÿßKpŒã¯ì!È=ѧQ÷fûZ'ÛÓ…=¨z©Q||*ßæ±|s­“ï9˜oªE(§ÅÊGWƒzã8B½ŸY"ßñ¾yL])ê½c¾»®‘~¹Í÷ÖÜÕ›kê­ŸuÒO·ù²Ÿzû¡P½Á¶N¾½iùH…Ž{±z ¨Fú÷2ߊï ?‡æÉ5òmóNàM¿`9ê-·5Ê×êõ–D‘òÖ ÞÿÌQ~Â×)Ÿ«îêM â~õÍ|…ª7–ëÕûn~Êox‰òÁÊ÷€ ò=Ëk‰ò#^«|‰;)FÀ–È·'ˆš>¼L¿b!ÊÏQ‘|kÔôÕÚ |Œ¹+Ÿ­…Êoë:廵I½Oá$}¨ŠwÌ÷–S”/Ö"õžÑ:ùΨxsÙ|oÂI½1š§ü°–¨7ÞŠ”ÖéC,⬒5)¿â~Ê·x‘ò¶^½å–¯Þ9Z£|®®–o*‹÷$L¿aîÊwXò¶FùkR~ÄÀŸ¢Þ™(‘ï" ¿”âÝeÓ'«ŸòËZ }Ušo0×É7KM?å~ÊWy¾z¿tµz—¹A½eê®ÞgÎWo4¯Qo.5©w—üÔ{ÍÊ'Ùjåß¼A½[á®Þ®ÈWïW¬–ï  Ÿçæ;rîÊßTžô•™\­ü¾6(߯NÊYŽzÉzìR¢Þš£nºÔÊ7纜U>ÎüÔûùê-è*åóŒ|)Mê= ?ùÖªék³H½W¥ÞvªWïÆA·kˆz³"B½W¤ÞpFv£ë¥ONñæªx»¢ûEõ~E¼zÃâS9DîÉ·žÈ§'òé ß=á;~r©ƒ\ðä§“G§Ì:¸ò(_ž˜§’'yçSæ”k0 øX…gäTg4‘gñ9å)!^B¼„ú,AJÀWAB´+gõ¹ZU|«âÛT?92ÿ5Ÿë^nžûgw®¼Çrå=–¿Ê{,è·9/sÂæóAÇüOÌý.™ïYó<Ç}·æu—›ÓýÙ|®ù\®ù<îrs¸æ{"bΆÞÿ›¹Ú¥ó31'k>s1túßÌÁþlþu¹yן͹.7×ó¬ÿÈžÈ_mnuåœÖ¿÷–¢Ú'd*þ¼¤¨º_-ßÕ1ß§¦k¤vñ>Ah‘z¯FùjwRo¡wõ“~z#‘«ë:ù›é÷8E½“W#ßu ‘o#›~ä{onå{©â}Pñþðé+Þ®öˆ—o(›oÉw%¢°¥ðyæÉ÷%„ÿ^ñ®j88¢ Ô›ÖÐô*”oz­–ïZ{]”~’Å{@ŸoL“zs<>üÝÊ]ùI†v«å+^}ùæ =_ÊãK»â¡|&W©7(Ψw®s¤?zó}ÖõFk¼z¢JúQo \”¾s»’/›oñòMŠ®µòÍ=á7ð¢z›"O¾-Þ&5ß¿ÎQ>•×˷ղ᧗[×GŒKÉׇィÏ^ ò}Ÿü/ßH{•~{»ƒ[‡ÏÿÅÞy€uql Ÿµ‚;6DQÁެÅB¤‰ü£)¤kš¦,IŒ1‰¢Fl ¦-é$1 1Q1ÖtM1¤›þýfgv÷ýò~ï{Ÿç¾÷˽WŸgå¿;gN›sfÎÌÎÎ CÎÑÞrØìî/óHŒ¡îx_™û`ÇÃÏXø:÷ùÀæÓùà[ýeÔ[†^—UÉÒÍ37Ï&‹˜OÄòâ_˜ÇïH9–šÿþêïµþÓÞiý3¾‘)ñºüNë?åÖ?ó}þþ/û.ëÒ9Ô?ú=–ç;¬¿êû«ÿf~eæBCÇmÑUÛ•÷9_åô¶m©ÊÿµÅ‡ÚâmÏ©¼iØ?íï¤ò‚Ãùgª|Ñkecúÿr™?Zäñõ/Ê b(1󇂰xÚ§KåO;ê·[¥r‹‚£|´O»j™ÚÌÑŽöàh òQcí‘¥=²´Ï—ùÛºŠ2dižÎàmLí+dŽR‘_ª=<µ¿(s&t羃·Ê]ê/s’t¬•Ü™Ë\ Ó :Ý*g)¸;ƒ³s¥ÌÕÅ­r–"KWx隬rX»ež7‘§Zä+é!æAþ*g5ð="e·žÈ׺=kåÐ90Y怇ÆHxHÝ¥2§¨™ÿ£Jæ†ù©^T9¢ás|ŠTù¢cd~p`ÁÇ p Z«òHo–9­DîA¢¼Fæ–6ó¯Áã`•¯4Tåß`ðMnÌ9•Ÿ |ƒKUêr™wd0¸ゟÁçTþ6ðEÁ“ž!*_µø¯òV»Užp × ™Uä{ù¶Dþ’!µ2,0ó[ƒc(| G ¯Êw .1Ð˳à™ ž(xž_Á=t›ÊW%óbODÃòenl3¿jŒÊy³á%*'6úÁïB—ʉ]*sÇ ¹±'‚o*4GåËœØá5*w´Â¡ßáè`ϧò;Þ"Э>z‰È  ÞуŽ(èGñ;–úI”Eñl2üFÁCô'"KzIÇ<äcÑçX`lj8•úóÄžÆAo´]àp!£ .xtÁŸ ²¸&ŠxSà¥îDø,þRwx&‹ûZ6EÓ†ÑÐŒó 䎆v!²Oá~ ´§@s rL…Þ4`§;²i”M£,¹“Dûð,†úÓáÛYÈ™ ­,ìgugRw&ug7“º3©;¼³¨; }ÍvòÌæ~6÷±ð ϱð<;Y†mqЊtâ‘;ƒúñÈž<¹ÔMâÞ ­®9Л½9Лƒ<ó(›'tÜ<êÌC7óÐÓ<ÊÒá'|I”'¢ÇDô˜D[%WžÊSà7EàL6>‹à%•:©àLåY*uÓ…Nà?Øtžg!G0ÅÔË툓z3Á9 ¸YÔŸO³ÐËlîgs ï±ð o±ð‡ìqàg4â‘=ž:ñè/¹ãÑa<:O²ÖÊPm4ç@súH¥Î<às¡1O\Ô›GyèkºJmG§Â["øÁ•ij$`“€Máy <§Às xR(K…×TžåÃ_*<§ò<Y‚8Š ™|ºˆóÁ“!æÈ•%ÚNÄ÷ð–\ÙÈ•\ÙÀgƒsu¸g|æÞKe¨˜ ϹØi1<å¢ß\d.@Öð ßžÀW1W‘¸ÀW¾"ð¯˜ûbWŠ8Wüóœû[ç[ñ½ç™"~1º[ñ·ˆ»­x[ÄÙV|-âjÑø—Îó/ãÿÙÜ^Ä„ž1 gÌç9§¿tþþ÷ÎÝÿžù¹ç¼ä«û]<°êÃ̯õgãÒ²?|h{ÄGòþRLÆŽc{ëvüì=èÓ¸;oor¿‘yײ§³¯úY]0åð×Û…ÎfIg†žñ݉³ú‘ØÏª’c¯ÕŸöLýÝ9ÙŸ¶¸ûŠŸ·;Ê“{:òvà+%|ÊŒ>yúLýÈÒÑ_>•pZ¶õ‰A÷žÒŒ«›Î™»®·‘qí;×}óðg%|I!Þ2áþc/ÿrX?²vtÍó[gëÏ|õÊC¥,þEûžºî­~Fú Wð¹§ÛFžŸ-åØ¿åDÊSõÑÛÞ{høG›ëGv^8ûíëú3/|õXqÝÆŽ%/µ[ÑcµÞ}{ŸÐÙSŒ…M‹È‰NÐ{H¼à x ñºóÐú‘W;ù}øô;z=¥ÇgÖ4Y~ꦈÏÇÌXØkK}cGaZhpäz7ɯ±0º4©ÁwëC]‘ïs¾¹ûÔGOó¥~`Á »=òí÷!Ï[í >e?µ5cA©÷úÁçÇôÓv eëñé'ŸøÎªíÆŽÜ£Ëó‹?1Òž»³ç€–Ô—vqÀoä½ZeëG‡.ls¢iþôŸ:¾]eì¸egA“½ëôàsÇ”?m¤Îž0gÀÖgõ@[?ÒNLuÿð¬Å—~töcgè™z{ÅÏÓÍÞzdãÙMÆŽû4ÿ¨øN#ep½Ï&ÝÚšúÒnÄ=}õòÝi¶>ŽNVXÑJêÉÒ/·„d;6=~ý-ŸÏ5{^ÿÝËŸ¼y>VÚ˼Y“÷ìaµ¿~tmÜÛî~a¶ý=µø`äïo¼ñ•ô+cÇÆôŸÍïiù…1¿÷É%óÂ/胴´ë›µm^i? ?¨—S4Ùj?ýè#¯Õ}±üÛ?Ÿ ^~eó.ñ§æ HˆHzÂØñàw{.ô¢‡(:ÉͲ}³c¼>`Ïñà«ÏÞ^iGRW-¼{÷4½‹ÂsôÙa§ïºÿ1ýÉï²7ßôZš±c]Ðëo÷˜¤÷=òÓ¦°òÉFʉ×ÖÝñè|} ”<Ò~ô_ò^Ë]zG §=ž­õÜ ?ùrka!g_þqó/o³ìÐXPÿií¡Üê+{9~.bôœ vûý9¤þ£ÞôFêþÉAÍ[>¿ÂØÑéÎ'~÷Þmû‡%—ݯĪ~å†ù[–ÜbósÌt»¹ú“‹MÇ7ʾ®3tdÏ|cÞµmàðQê©þeä´”Œî=õc‘?dÏöηõûäÈÌ[Þ»{¨Qvæb·§¾Ø¡‡éw|2þëXc®´CK?àQýŽ×ÌŽv¿Ý¶÷cé+“:|5I²n¿=ç¿q[~©‡©þQñáôqÒžö}Q盟nŠ´ÛýØÍåU7OøÒÖËÁ—G|0«ÓÛ?Ë>¹áDѾ§mþæ_i¸®~ýN«¯ê¼~ÙvmövýØv¯ˆÏ6êoZ2mÛþJ»ß-ûjoÙ÷7æÿr×êÞÔSýN×ʱAS~³íåXU׺dýàÄs×$/pú±:M&>™8ÄH|(aȯ#§Q_õ3Ý›¿Urr~싾¥­o¨l±rÆî0vtþ`LìºOôR.#é›n¿¹à´‡>”45L¯1;º,»}*ÎþÚÿÉ^ôÇ£Ç}¿kÝ£–]POÚþoÖîØ?uº^#õb믢â¾ì3ŸG|iùͬîÆt,ºF’¯äŸ´“}5 U¯lf·oMÖ±ÁýO<®W”6î0âGküšÔ}xåçt—j¤ªo»»w4uúó8i/ûžY0àMäº;?í‹ó9r]õÖù'Œ.ÆÓüSí~/ùl7¿Àß—DžWvòÐXFìÛÞkž~6å­ßËõŠE®)E;â#Ž÷YZàûÑ,c‡—(øÃ—„ìÊ90D´‹}k6¶j3„^ó~§§·gµ×+æüðòÆ9iv¿Rv*¡I£êcA骋)4¥ž´‹}+… _Øvq\»½°8è}½b²Ù1eŒî×|qÄHyùí÷½_øŽzÒö©þÛ²ïã]ã27=x«Ý_V Ï<–paµQöêî÷ß[ÙØCniûú4H}eÚ‡®^]q«—^Ñ-ÓçÝ{üí~Çâ;ùõÞ‰÷w𡾲 /Ól½ŸwEMéÞÏý×ÍÌŽÞúQöÙ²+K“Œ×o¼“wõ¥ì­þ89jõ0ÛŽ3h4~TâÄw7ýz°GDµEgZìãoSFŠÕÇËöß{ÏèŽõFß ¿kß#·´Ûª?±sÐþÏG¼ªÚ­¬¦ýém÷¦ZýoäùÙî{g%—¥=Ñî7XúÙ¾Œõ'ŠŽL\ê»Þ(ûð“_ºwª±Àê¯d;ïÝë—ëÇßùrÆU3ZÛþðĨüW×=‘áðý{§GË÷Ýi,ØuÍ®ÏVQ_¶÷^¸½þÌ›v;ÿòç1¿Ï»ÓÖÛšïƒÃ¾½5â]+nhZr,1ÀH¹éÓÊÜvÁ#Û/£Øý¥§lý½WÿNß°y™úã¯^yë·¥çñÌÖÍß:bÛíü··t~(a(x¤ìùñâk×”~cëá½ÞêöÔ__sÝ“ƒÇ:ñ‹þÈé_WŒ°ã—„…¬´‡=?Õv¸1w¬ÃO¨6í–øÛõÇ—yr¯'žºúìÍ_íÝaÅ7Æ\“ÜxðH»ØóÍ“÷uIö×ßsµ©mpÛAýq“Z;¤Ÿ:ýS‚lÿ=ï—¯ÞÜk†¿½—T³kDZúãÃûwþqT‡wÝñ'ç§9v0GÚÁžç?Œø£]ŒþÞ Þ߸ösýñN¯Fl9ü»Í¯ï¬~ˆzÒöÜ|ã±.Cõ÷î{¥ç §Ïé~«·åׯÊ¿Y[÷ŸFòã¡Û[ÛL=Ùþ{&<æ·â}—ÝÞï{æÆEì°ãÚo'Õyñh¢3.%ѯøò«_lÿ=uoxùÊßÙõÞÑuÜ,ýÀ†G|Zm½h'úÍ+‡ô¹>ÌîU?ÙþåO¾âûzï$ý½ß¿J¬j²Ì¶Ëóûi•¯%;ò?µ«²´±>8æ«“›¦l·ìȲðI;(O×ý‡«§ê'üû¬}¶™~ KÇ_N\`”q×ôËÞÖ¸¼lïò¦/ ëz$\?1p˜¡m½ìÿä›s•®9Žöú·FcŽðæÄeÔ—í¿{ÅÔOÄŽ±ýðÄ„SßÜÐWßÿäÝO}4!ÜiGÙÚú˜÷Çç '-ïy~®´ƒÇ>¼óý¯–>§ŸHzaÿm–Øö´ÃÉ5y.GŸ¿è]óÅ:§=æJ{x,¶ž?S5›ÿE§˜ñ|§ï¿1aÒwµ5ÆŽ&«¶^õÖgN?»ŸYðÅ¢7o´Ç™ïÌ•ö³ýÔsŸ®»b·Í×If “Î\ôÐÓ¢£çv_ãȧâà”Ú¢ÛßüräùyÒ~¶wß²iù†E6'»~6䮤ù²>ý%ðŠÇž¯)ýš!Ä×çI;Ú&ºëw ÛžOen(éð#:ÆÛ9Õøg·ÛuæDÝ’¼ÒÎSgó ýä¼V¿5mPlÛûCBJ—þ±Óñ“ % ¹í~þÿš'ΓöõHºùÏæë¤ÐZ·§íxb¿Ï–È®å¶ß§BÕ]YŸúÒŽ¾õp£ŸCØíwòÖgZŽ}¤›¾ïÃîj¸»«QÆ(€î¬uêI;ÚZ%&¶Íº›¾ÏÝyc}}ßó÷[_îlûEúžŽ‹;ûÞjÍw¨/íhk£ å¦öÒOn×{7zjœ­‡}Þ»ºs×™ŽŸ?ðlQÕþáÔ“vb?‹.ê'÷›ž¾ïîã‘îu~FYÙˆ·oëyÁás¾´‡- ³Ÿùð—glü'Ÿ›õê µy¶¼û‡®ñvÚñ}s`°ã]Kïv¼;_Úǃ9æBŽ~òíácÚî|ɶ¯}«^‹¸²ÕôKÇkKïÎuüøµÚIŒ¬f`ì´s¢´Ÿu/5þþØ*ýÔ¢…Û·Õ[gó·wEÔÇWÞ÷”ã¯sƒìø8ÃÒS¢´—{¿þíô3kõS+¿>÷‰wC}oÿÚµÛYl”­.òéè ÆÂ[®½úÁÍo/íâ®Êç~]30ÌnÏS¢µ&/µåØóI\Å”ƒ¯ØvŸöáÓï̹aõ¥}Ü)ÌêæM¶]žzÈœˆÛzÙóˆ¾aâóÏeÆ ›ÚøÄé¶ÜÒ>ÖŽ[~|vA´íß§†mœáu•ÍÏž¼_[Ìÿa¶ã§r½ÄÈ ŒÚôx x¤}ÜZzu÷C·;r¼9 æ½?uøˆª9—VÇÛiµn¢ÊEr>>i7«GŽÈܳ!J?%–“&µåÛÓîæÏK¾ø9¢ÖZ§‘ówÛsÌ^(±ßDi/7ˆnûú³ú©ïëWÿÿ£^þí¸¦|çØÝ‘•¯ü¸ÂYÏÌùì›éÏݵ?ò|’´’GŠ.ÜÖ–ç´÷c¿ôy¬·-oùë»×Í ¨°Ûy±\£¾´‹"©wýtç7&=جŽ-Oùƒ½úÞ½ï£l¿o\ù‘³Ó{@àùbêIûH žýU—S_è§ÍpÍ¥—/½ydÜ‹eí›|]Òè ÛM¸¾{ó·®qÖ!’LûЧ¨¸ÑâóôÌÛ¿’ÝF/×;^së|§=Ú.~ïÍ Íþ#É´=ß\œ ŸÎ¿ýô¬ý·éå^c:œ¾¶Æ(ë±:zì;H2í@_ùÑËî,öÓO«~dwNýÆ5+ãìuÄ2ŇÕ^Šž³n‘d¶¿~ÝœˆÌJ}?xFX¦m§=Ÿµiå¡ÞŽ]Jý9úóÇÏ“L;ÐWe]7=¾"Èö¯ÓN¾¬ûµÔóy*{ëÁUŽŸ›an]Ëž"Ï›êôÒoá_×?ìþáô+i»þ¸¶¯Ýž»&}ÔûìÖöFÙôvH–o,«…oPß´}møAØë®§Å²òÇØí³sm§v­W<í¬c˜ÓùúøMÝŸ¸§_w#OŒÒ#¼,½€×´ýŽÝ#üÞ|±³½|šèºò¹ßôì¿çþ±FYž>ã×À8#ÏŠ£“¥}Üpüî¹9ÃìõïÓŸoÀÞ²õ¾cÂÃåø~i”©ö·Þäíû™Èl«£ßdi/w2÷³Æ~o¯gŸþ!2õ¾ÄI¶|eÏfZQRí´›9­sì!ϲƒdiO똕V<ùŠëg´â§EçÚíW%^pôtüXÎ[ôrÞbä•ùò]à“vµ¾ÍMÁušL²ÛïL“FÕ4‰mWÛ_yúù»nûÔ±WœbÔ¾‚+ÅÀ@<•,íhƒš§Z|œQãìöÑ?t>òX';._b­?¥Hû¹ol+"¶~ψ꙯Øqʶ‡w¾1³ÞÍFÙòŽ÷6_y¿±ä‡s½'>¼úÒ~x®õ™…³_²éYóŸG?Û²y{üL£,¸m½?ÆŒ6–úáÙa§ï¦ž´›Oß¹á÷ÎÔëõTÈqú£íGúm¯W`÷£Ûù4ç–1ëŒ%OüNÏL=i[ ƒ8aÏ{Ï4ÿiÉ„qÃm}=5õªó~Á¶¾¶ÿ`zv{:|HûxH­¿Yñö™"°¿E8¯ðøæÚGœþÈlž»—·:çS i[¿{ù“ãÏNÑÏ´ñ‰­<ú½¾uï=çß/¿Ù‰ïÄpùeµQð´ùB…z²ýÍnÜ÷{GžŽcú>¼Â­oá“q™Ÿ©u‚™Å?ßû>óªÙîÒHK<¥Ÿ hxЦ´ýþ¡õïNÿ5ºQ¶ ûû7–1 ~1ÂÈó d»o«½>å§ÚŽú™ËDlëï¡‘¾ËǯZiù¹±Tơԓí]ÖÑô3}Ì…5ÛN¶|5Uó;üƒ#¯ìoe+D`»’ú²Ýw,¾ñ‹¶ŸïÖÏ Šî`ë}KyêÙAOE|ýNFûç¢Ó²Ue·ïOµÛmÙ›/vz{KðH;ØÉè|þP…-ï™°‚ä{ršê[Ü·•ÇMŽtøýž½®¿lCl¡š3/v`FïË>ÑÏD™ óº9{Élc¯ooîsÅ Æ2óõZ$õd»ï¾u[»5t»?9#×9íöÜâ5oüôoÏ;öH0—qàciÕ‘[nº<ÒÊK–Æ}’ÿ¡~fÊ3¿[ZOpßcîYVàпIÿù¹Ã÷ë#U\¾4rêÖmÏÍvâÐÒö„8øxÞ]Ž^f¾q*·^¥þ`ü/cj6}ìø×moì»býncIZ‡·,¾)ò|ª´‹=¿™/¬õ3ó‚¾~Í<}³´‡ÿoOùÆmÔ“v±×|mÛÙԓ®ìçìöÝ\ôþé¯vµÛw»¹œÔÃŽ· Dë´káôß©Ò^öÝöÐó-n:g÷+g²žY<ã°­ßN}.–Ìm?Ýþ–x!n¯38ý]ª´›ý^hš¬ŸY<ȇ¦Öï7žøõób{»Xöó®w-½בµ•»œydª´—ýâ­gt©~&·ÿž¾¦ß·µ~¿ôìR{œ\¶æ8šüxi'„×g|oÓÝtß}ƒãßèjl¯6{;N(TïÁÕx@}i‹åä;îÔÏdnzð–£ l½nÜGÎÖ\c»Þ¹BWROÚÃã»{vl7r„~F¬¶O~ÅÖã†OïœTk´wäþÌ\è·×K ãü¾ž9ѱ¯4iO„uºÒp•8ýUìºoøÍφЖm®ýi¬­‡íê}ÕP9n…÷üþ^zËë¸0MÚÏÏô Ÿ»£›~FLÏ^ÿĶßõ%yA«Ûß'V ìõ»¢ÎÓŠ’jsì%MÚK…Xý˜uÚéÏ]ÚÓë±ý³ô¥1‹Ölle÷Û^xõãëo0Šž*}åƒk£À#í¤â[öäÇèg"âf>ôE]ýÞO¯œ1rè0;Ûž·­ÿM‡«Œ¢Ÿ¯=weß+¨'íâàÂàw^Hie÷§gÄòGÐmú:±ÌóíY‡®X[mÉu êK;y²á¾ÛÆ\ãØ{ࢭwU= ßsãM¹>{ÃŽ+¶ËuN+ާ¾´“'ˆ ß³ú™®ý–Xã¬ÜÝÒ|ql÷‹Û“¾|$%h¸³N”&íå©I¯ý‰zŒGbâq·-Ï·”oymà Npýæ|`÷ÓEl¦ þ^(íå©çN¹»áj§=üLFl¾îèðÉ'3o\ëøï–—³ê_éo¿G,’v>i'OywQ q“~¦AS!í7Û|­‘qˆ£:Á!‡'9ú](íãiå·–|§/š Ð6?·½~êàõíw:üT~.ÞÐE󇯣©Á#íã™mñÅáǯ¶å:ýe«»¬8¤ß:ãÓ~vûÅiçݪ?Nm¿)’ïõÁ#íåÙø‡BÆÞÑG?C··¶å¹eô¡~Ù›b/õ#c¹µOg¡´—gÍíEÍôÓò=—~sÉ[÷‡¥½nlÿýÆëF%×5ÜCZ3Ãß¼´CDïøÃšoˆí÷¯·ímõÙO‡Ö»ÿu;î¸4~r‹]5ã—OÚKå Î#®,‹±ûýÓOý¸Iâ>}õ€±ãÊúrú©G}Š×Ý¢çy>]ÚI¥¹]g¼~z§¹@iëuÕ˜ðœ%íºYþfíŸ0–‹æjvõ¥]<§Þ›žf2Ûu´³ÿàFéŸÆöÜkïÞk,·ÆýtiÏå?t¬Qx~ZÑ¿!ùú‡[¿y¿Ó~rÿƒ±|ÌyF´EÔ“íÿÜñ«<þØ•úé5æ@©_·ºÿ¹IÇîqìF,[\ù®cé²½Ÿ/¾¶ÛùôõÍÚ¶ûñ'[ÞkV¯Èx¸Ï+ÆvÕ¨þÍ(je.¨ƒG¶ûóŠ®mÇË_œ‡¯éW}ß½_ðÞ!ö{ôík:Eéõ±>@­¾–³yÔäFà‘öðü‰÷ì\ìðc†Aóìö\ñõªÅƒ»8~õÈÆ„ìözX‘¹Íe3ÿK—vñ‚ZŸ³çËâ5ö»eúòAÝrFÞy¥Óíý«Ëey>CÚà j½ÚžÇtîyýw¯èK_«è²îþpc{ÙC»ü'në½\†´ƒ6?xÏ=è§G<ºñÉ“l;ÈßYùÌkoOrÚç…[êíH^d÷3ÅrÝÀï2¤}¼pa‹¤9z4oõEï6¸â뽨ñ·8ÆtêK;!6ˆ{ôÆçõÓb5¯Å^[¯^oÜÀÑQýÀIF±/¨/íåÅfH»y¡WlItÖç޽ǿÏú6_ +Ì…GO*léIÚ%ø¤ý¼ø‰wÃ!Weè§L3ÊÔ',\>³ÓþñŽ^”ýY~_,ñS_ÚÉKÓòZ§Ü¡ŸZ?îñŠšXrC[þóš+é÷ÄãÆ?ÅòoäùLi/©ýW§®ýMìð2ævøvåmŒËÁâó>ÛoÜæk°!ÖøO}i'/•›5õSy}Ä ª‘ùù꘢í¹ÕøêžÔ,ªqjcêI{xI¼®¿ãm§=Z]¿m6–[êÿkŒóžpû­Wa*†»µ°P_ÚÃË"zü…m—§†®h¾vÝÓ–ÿÅ=‚Þ¼¹ƒÃÇ‘º®W†÷(~œø0SÚÉËôöuhk·ã©VÏvûøt‹ŽqÕ§×\÷Sgg?Ê%vb¸Õ>SGOÒ^^VãŠÅïIÇ]s¡Ç¬ÜgÝÄÜN2‰zÒ.^ ýôø³ô“‡ÜçBŒë~|óx››‚ír~a¸åºðÒ^¹yžx¡ŸÜ¶¸àÛ©Æ ÷6žx¶Óκ“X=kôˆá6ÃùqŽ]gI{x僣ݙ*è'o4aÜxþ©Ú“E·e¾æ WÃ}òİW7œ^¶U˜÷ >Æ&ç=[¶¹Pa·Ãª6;?Û=äCgý­ËäÎŒ‰3Šå:0x¤=T©ýgV?u2æ¥ÏŒüÀX=¶GLjŸÏ9ë@Ä‚ò>£XŽcÔ—öPen7*ÔOzÙ«÷׋Œ›ïðMìØÚlj3U;õ·öKfÉö®úÚÜ€¢Ÿ«áóf·¶^³?|õ›¶½¨ýŠF±ò?»Î’íúªŠKOnû'·héó¥q«c¿{,|g}¾Q,ûIêÉv}UíÓ÷”ý¸æ+ãîÜÃãöÞQaïë)»b΋ûÝö|~ùsÏ<ÜbÓYÇÿ²¥}¼¦â©÷Äã¾÷˜¯WÚz*»^r¥Åõ¤}¼vá‰:É}ïÐß“ïYì~`Ý­OÍÞiñ¡—öaù‡c'ÙÒN^Wë±ïÉýyÆ:¹¾ìÐWë<Å›mÿäÂüK{y=Ƨï[‹>´Ûë=!îS÷ö|áô§×\ïèC½ï]Ñ7]ìü¥¾´›×W˜ ¶Î~z¿FwÌ´ÛåÞdc¾ûšξ+ù^ÊÞ¼Â|-1×é/I;zÝ\®œlÇÁï™áw˜qï×Ü×ýŽ#Žß?rÝÊIÎø%íÚÑÓ"i?¯¿kÐÒm>¿?ï‘×ê¾dëýÞÃQ%ï¼õƒÓŸÝ–v¬öË˯­qÕƒOiG‡Ìpc¾~\ìJ8Å–»tеMνËYŸLoyÝÚÑÇíqBùóc‘´§Cbñ»ïêÇå~X£´âë)î)vä 7b'Y$íéµCßÑK}ëã×mhTó QÖû•bBB£¸ÃKñiãzxð/íçI®Ž³ßMìjMÉ06Ôp¿W?£lTWÿ«®hh¸ŸIïuŸCÔ“vsH­ûÛû¥ÙúÜððK?¾\¸Ñ([Ùxx»ÀÑμd‘´›7Ôþ—š÷Ì|ÆÆ ý7ÿ6Ò^ÿ*;4õâ-÷zŒÏ9Ò.Þ˜4%8ø·–?ë5b—à°ÆÆ•½Ñð†gÏùÛÞØw¥ýbùÁÙŸÉŸ´‹7Ô>âñú¦Î~cãýžœ’ë²öcË?77Æ/Ûû ÞåîÒkd¼klÌšÝ?äÙÁÎ>p¹ÿÇñ÷Ù®oÖÏoòìïjÔû°MÆ­Ú<¥¥QöËUkÿ¸·‡½Îä–ûL¨/Û÷Msùm¢~ìççß÷J ±aí„F%·^ëØ«Øu×s®ÓN9²}ß,Îxí³eWÙí{LùÇmhÞ(ñ÷»üõþüEïãö>\÷¯b#KCðÈö~óþÜÏóëÚãñ1±½Öë.ÛÞ×ß?}MÄ¡ýb™zÅmö{Ûsdû¿iv“_rÞlÝ룵ä1í¨ÖÝ­}4Å ‰/Xœ~}±´7ß3_¤èÇfLÞôø ¶]¬ïòc›è%8û'Ô¾c{~ac‹¥]¼yqØþ-ƒ6éÇþvýÝXï¿Ùç‡î ­qxiou½fNÃ}3õ£_ >ú`ÊÇ]s›c€±cà-‹oüÂß^·,ž³ÛýûWIξÅÒNÞRóª£r½ËX?]¿mÿjcGôó1K¼ç:qÒbioÝØ>ùÝØþúÑ[Ú‰•_cýU'¯úzù£ÆQ=²ÂÖ—²'g=i±´·ÊÞ¸ÿºív=:/ã̇}8í°ï¶äGÕw¾[úéå>æ]i¯ «y¾³^¿XÚË[/ÏjòÊíÎwGû®½æùu»Xÿ±ÿ·_š`ûÍÎQ÷aì™ë¬Èù¼,–vó–¹ z–óÝÓ¯»oÙä66ÈøÖØ9ýÞ-®»_´Çƒ"±ú9‘ùP®´·^½ðú/­èGÌe÷cCs#¹m;c ’œùnnÀŸÊsä¾U#n¬ÿöºÚ'Ï=`ÛýÎ…‘DÔ?Úû0-ýØýn®²I_?²xÞÏ;Ÿ3Ö¿zç Û»:ë‡;å{dÃ-šñ QOهخÛz³~dÔЃë}f¬§&=ÞО§Êå™b£Xî[¡ž²“3k&„FްÛùˆÏ¢ƒLÿ½²ºùò—½n¸s¸ØÚãŸ[ÆýàSv£¾;°æ¹ïšá}KÇnntË„WÐg`ìŽÓúÓŽZëJ¹ÒNª®N ð¤þîVñáÆ"Ço^^fl8ýù¾¨÷£vQûó¬ï†ìñ)OÚIu¹ñÀáCòel¼â÷øäVéŽd™ ^Îø’'í¢Z­G¼söôáUoæÿ˜°tžˆ±ózsã½N´|ç‹_­:H–'í¢Z|e”`ÇÇïÈ ¦v{lŠvï—¯|åøÛAsC¤ßšÛ9ÂÁ'í¥ZÅqï¸ßýíÛÇÇbóDØé\c§x={×öû‚åýk›¹°õ¤]T‡›fõw̰ØÏØ´ä„C?×»vo©Sœj÷±t‹ð϶ýøñ:Ãe›DÓà!Ûwõü²zÆ]“uÁ<ÕÞã'®Íy­¿~øSóËBcãï#ë~ФÚÖï.3\aËg§ùª½Ímaúá§Ì…0c£ùº+ÀØÕLl.tÆ¿|Õ®Ê,û8,vEdí26Šåšù·Ù~³«QÆ‚fçÞvö£ç«vËà ÞЋÞÚç»ÜØãü‹³â'ØqÏ®…·w]çÌ¿óUûŠpãåOõÃýïPž´ÛöÛš¹À¶ï]bTêçø‰´/§ÍWíÛI¼x¬o÷go›¯M} j=ÑÖcÆò†÷'|«S~©üΙGä«vïb~¤¿-Ÿ¦>llø*oM=¿N;‹i_9þŸ¯ì@ìÊξZnï»o°=OÜðóÅ„Ñw»f;ÝYa÷ç¶¿ä+;½dÝýí ^tàccë‹k^ªÛqÃ.ßËS—õ¾oUäùÕþâs‚Ïî×ß–ûÚ­y†±1ìÎ ×ï*6vÉ}8Æòæ+Å—”ÔSãÀW÷íí×,V¯þý%znÚV»yUqßÚ~õ˜XUª;Ae*~°ûÿÕÿW_aztuU“¥;^à´çœÏ¤íYš ðËñ*?'24 à¶a¹Ê1ªrq¢ïs2¿ŒO¾Ê½IýFÀ7¢¼ð€o îÆ”7æ¾1ðMTnÍrÙ]4_Ó|•KøfÜ7[å‘;&^å¹è‘?<- Û"™ y[ Ë–Èß’û–ðÑ’º­ Ó :­(ouQåÆD~¿JÙ YyaZS· ¸ÚÛ†ºm¨Û6@ͽVžÁ-ÎÓ2ÏÏ*WçcEª3°Îª3°Ýêü*ôÞ!HæøùB;\Tù2©Ûq›<ÛÚ<ÏZ·ðÃÔ™U•2ÿŽ™3ž» cò ÀZ•/3LžgݵJv‹Ýà£÷Ý*UþÌd™7Sœ—%ίêÝÜ÷€NOêötËšâ«@ø„Àr•Cýô*‘y3Íœ™ð´YåÈWo`{#CoøèCÝ>àêS­ò_‚;˜ûàm*ç%ð}3UžKàû!G?xëW¡òYÆÈü1ý©­îC¶©ü”ü…nhÊCé’ç°ˆsNÅù_#Õ¹[ê|­0u¶öYufv¼<Kœyežs•)ϯ2ϯSçPUʳJ†{ºV#ó÷˜y.©V#Ïn1Ï£~x•Ês¦ò[VȳÁFBk$õGBk÷£"å9Ô"?Ðå1ÔëòZâuy ý+¡Þª7KZ晛ؔ™·î¢Êß¶JæÂ®ë­ò`£óºô;õ°ýzn™÷Z¸t}—Êwß7àJæoxl¤òZWH’ÞÔõ†®7úõ—~æC¹÷>Ôo|#ÊUÈn¡±Kå©®P9ª)oBy1fz«œÔ”7­”]G³0•‹\ÍTê ÙøºTεZ•:Så[;«rOsß>ZR·UÊ5½Må˜Æýß™[C·5¸ZC§õY•C:_åŽ>§r]dªœgUÎ ·=‘¯C…ìÒ:†©\ÓåòìGó¼GhuBÞNe®i3/D•ìöºÐ†]à¹K­Ì]m枆VÀ9y¤ÈѵZæÅëݸïV¥ò¶eʳÀDÎ3Gt{pß:=©Û³Džwi技@øä¾—¯ÊA ®^´y¸‚à#ˆºAÔí¦òK#CoøèCÝ>àêl0uƒÁ\¢rGß7Lå‹.Wy¢T~hdî|p÷G÷ý©â¯ò>—Ë.;”ߡРEïüÕYâkå9áæùd.yn¥yVåE•³b­ÌAaæœHVy%jÔ9‘ù2'„yæc¤:DZJÃbd.¾aÐ ƒÇ0x Û¦rD‡ÊsE‘o„¯Ê í•2·ÆHh¤þHhòU¹¡%|_ô¥âŸ5vzŽ›ÿݘi—b¬ã£0¤?Řxéx(ÆBk ¼4wîŸwžc㬱Í×.ÏÄ8&Ư¿gܲÆ+1V‰±IŒKž9o­³¦Ñ­9æ\zÆ´5ÆüÙ˜"Æk,¹tì°Æ ÏñáÒñÀDÿo嫵ú|Ïþ]ôíVŸîÙŸ‹¾\ôá¢ï>«Ú°FóW.ç:è®uëRVÝÖ…¿ºØn=ô^™ëQV?FæÍl ú[l¯a%¦B¹·[æÂôÞ¿òáw#Êñ¼0±§Æàh³&¢-ñÈe ¾æàkŽî›ëËs_èø^tòU¶„Ï–ô­¨ß ~ý€ñƒŸÖðÚØÖàhãVçúžSçò–È=f.ž•s§VåÓY%ÏÂíîüíȳNni¾âlÛ.àï‚ß•ûn¾2og7hvgÿšçÙ–«pºÔW/èõ¢è…žƒÀT®ò܇ªÜö<®P¡ r÷Ë—¹èÍóÀ‡P ý2?8QäÇ1ϹuÉü6fž·Ì!#òà ~X¼ÌµÎ0p†“纎pÉ3h/ÇË^—ãå¯Ëñò_9^öWm¸MÂjê¼u䪃oÖÁ¾ê¬•¹Žë[7Fæ96sc»õh¿z”Õ¶>°õÑwýZ•ß8Sæ7nÞ†øIÃ™ÛØÛ[æ5öæÞ»Rº¿O$÷>âžú€oT¢òÓ4ŽQ9‹+UÎbÊ›PÞ„û¦¾*_1åMoÆ}3ð7ƒ—fàj/Í±ãæ”ùúª<ÅÛd·Ò‚z-¿íÜ‚þª%ò·ý…ÌIÜ :­(oŽuý¨ë·Yæ$n>ZsßšûÖç<òÛ¦Vå…Ë—g¼›g¹»ÔYíUò>.ªœj¥2ßt7dèÝĘÃ}w`»çË3sͳÓÑO™º'tzR·ç*y¾»yn:|R½©¶Ó \½ÐG÷AÐ ª]lopõF¦Þ➺}¨ÛØ>ÀÃG0¸ƒÁ î¾Ðê |_àûб ™û!s?îû!càûC«?rôǾBà;Z!Ô EþPÊBEì îA*ßN©Ì©cžå#óæˆ¼o"gŽ™Ûm³:›=LæÈ1s°UùÕJdî43_ZŒ:ó¼Z怖,sV£n<†ÁcuÃje®3ÿåé?¾GÄÈóÎGT©¼gÐIùHhò—ù{Ìœg·å +gÌ´âdkœã£ý¼œ±Pûĸgub<c™ÇÄöÿŠ}=Ï”¶Æ+1V]:NýÙeOb<²Æ¢KÇ!k ãÏ¥cŽ×_þlLù³qDŒ!bü°Æ1fˆñBŒžc„¬1AŒ—öù¢¯}¼Õ¿[ñïÕ[ý¶g-úd«ï}®èoE_ òTTÒ—¢‹:´u]ü®.4ë­•yâ닾’ûØlCdòæò¡½}.ʼîMÄ…Í4u©Üì12'{ó0•‹g¾Ø€/ø}EFÝ*ߺ[†O­J¸àÁ8?àüΪœêün ®ÖØlè¶©”yªDJqn¶™+¢\劌‘9Ìàë¸Jæ±2ó7ÀKgÑÿ€¿ v ®mÒÍ<Žè¾íÑ ºÝ±Çîn™oBäcèÁßžÀô„f ²B£°Aðž Ê{ׇ²><F}¡Ùšýà£þÒü!À†B?4Sž.òg™ùãe,3'c¤Ì± ò%ˆ\‰"g»È5LÌs/ªÜ„ÐÉý¨™sàr<ëu9ž-ñºÏþ•ãÙ ÕFBNüÕÌ‹D›kðUß«ƒ}ÔAÇu(«À•Ì…ÌõðÛzØf=ÊêA£>~XŸ²úà©OпhžÀ6 lÉE[6DoðzS×›{oèøpïƒøˆþ»Jv¸oÄ}#àƒ»1vÞøÆU²‹hByÊ›PÞ”ò¦àkJySôÑŒûfÀ7ƒ—fðÒÚÍ‘±9e¾Èà‹L¾ðÕº-ÀÓ‚²´S øl‰¿·ä¾%÷­±u[A§tý¨ëG]?lÆ}´FW­¹o ÖÐilxhl›‹*‘[æ3Ý”™‡¨Tæ$6sgÊüÂfž x•/¸Få¦nèv@¾ŽÜw¤nǵ2?ƒ™˜òN”w¢¼³¿Ì{hæû¡~—™¸K…ìþ)€û€r™ÓHäÿé*ÆÚ­Ýh·nÔí†LÝi·î%2·ƒ™+¼= ÓƒûžðÑ“º=KeÎ`Ñ…¢¿@Ê¡Û ™z!S/põB—AÐ ‚n°½ÁÕ\½ÁÝ»Rv·}ÄØl`ƒá#˜ò`d F¦¾ÐêKy_àûVÊ.¹2÷ƒV?øî|hõGŽþð­h…@+”²P1¦!h­Ê)™¯rXœ“y!E.H3ŸqµÊM”,ó›y†\*¿p…Ì!læ ^+ó›ù~âUþd røRwml<‡UÈaAä±0ó÷Àçpê~D¼Êã[£òöP$õGBkTÌ[$ò÷޾+ú3ñOŒ¡büô;­±RŒ“—ŽÖ˜h‡Â¬1ðÏÆ¿ÿjìó÷<Ǽ?ï<ã_1¾‰±Ís\»t,ã—çØ%Æ-ÚÁ¯þlŒú{ǦÿIÂKÇ#k,²Æ Ïñç¿{þlÌãçX#Æ™¿7¿ 5¦ˆñ¤ÒëoÇkÜø³1ãÒñÂZ_cÄŸblã‚B•jU¾7ðÕÁîëÒuá¡öYŸßõ+¤)6ˆäÂöÒÞ ±io`½©ãC{ûà7ðýFün$~SÞ8€ Ý5¦ eM(kBYÊš‚»)eM᫶ЬBšwsøiÎ3_ìÕ—v÷…øzKl±%e-ùÝ ú­ÀéŒ0­ÅU!Ý¡Kåœ/W9ââeÎ7‘ÛÝÌå­”w€ðÑ:Æ«üj<ïÄóN<ï sߘ¹×©ß%HæPëž`*d^Ú®¢¯ÅŸ»QÞ ]u¾ú쎮º¯’9ṡQX…ì€ C¾áÀ‡Öpt0"Yæ,qVæ, ­‘µ*W™˜#ˆ¾Mü óø 3fñòúÇÍþ™s‡ļáßuÎðï<_øGÏþ*ó—Ò­¾ÆÌ‘J[ià­ÀE›×AwuðëºøE]îëÂs=ü¯^2eõðõúÀÖGÎømp6 /hP)]¹!vÛöi/ÞÀyc§ÞÜ{CÃ_õáÞ‡{dmÞFÜ7â¾ð¡Ù:¡Óø&”7¡¼ eMÁÕ”²¦”5…‡fÜ7Æ›A·9|4‡æÈÖœ2_äò…'_xoÍðÔz-¹e$p-¹oþV”µâ¾ôü¨çG=?ú¾F…ª|žà¼—ü0ïÿÅè9ö‰qÏsÍLÝ¥c×<Ç11~Yq¿³¬±JŒQÖØdíïcw<Ç1¾XãÊ¥ëØÖ¸abl¸tLð<ûϽ¢¿÷ìãEßîÙ§‹þÜêÇ=s4Z}´ˆ·/í›ÿlφÕ÷^Úïzîß}­èg=ûX+¾¶úTÑŸZý(vjæŽÆ&5Êê ÷:ðW{© ÎzøB=îëó¼>¶WÚ °…†ØQCl¾!25ÄF|Ði#~7âwcÚª1¸š ƒ&Ü7¥~3ê6£nsžùRßWô_"Fñ1°~Fq?JŒwbŒÿþUÖ°ÿ7Ö¯ÿãPlæ‹þoÄ¡ÿ] úgñç{оíþo­S{ÆžÿÓ¸3^ò%¦€Z¤ÊOT»®CûÕAæ:øn]îëÒ^u¡S/€ Û¨GY=ü¹>eõ­ ðÅØeÚ¯25ä¾a2ºm¬7¾äͽ7mà o>àò—÷>àn„¯6¢¼ð€oŒÿ4¦¼1÷M(kBY`›PÖ\M)k M¹oÆ}3øhÍ¡Û>šÛœ2_ðøÂ“/4Z@³r·X+»Œ–Àµ¤/oIY+h´¢¬÷­Ðƒõü¨çGÛú¡‡ÖÜ·F­¡ÑØ6ðÓm¨ÛšmE|[d÷‡Ž?üùû?uÛ…©Üö5*g=þÓž²öÐélêvà¾üv¤¼#u;n–ùÍôÐêDy'à;‡Ê¼õfîyà»Ðv]๠°ð@ßÕ2‡pW`»ÒÎ]á£|tCOÝéW»C³{©Ì-ܸÐìÁ}hô„FOêõ„fϳ2ç{ <rž^Ðìž^à W4ƒ¨×<½ë žÞðÓ‡z}ÀÛ¸`pó;¹‚í l_àú×¾ûÁO?ð÷ð÷G_ý+eWþ`C¨JY(íZ-»ß‘2§òô1Ð[æDï±À •ùîE~ûÁ1*}•ÊU¯rÐCsh¨Ê'ž¡èux‡¡ÇaÔ ƒ¿0`Ãà!Œûá*|¥ìÞGPwxF€g$¼Ž¤ÞÈ*Ùå S9ãE¹ÙÇ‹ÖX÷÷Æ˜ÖØóÿŠ!ÅX"ÆküãÄŸÅžñâŸÅŠžq gü'úÏ?‹ÿDyiÿ(úF+ö»4îýœÕW‰¾ ÛÑà[ƒ^ôV‡ºuÑe]Êëa/õ¸¯Ïóú´w}ì¦mÙkˆ]5ýúmFY3ÊšsïK¹/u[§½ZÂ{+h´¢}ýhß6´c[ê·¥Mýƒ¤ŠÛA¯²t¦ÍºÀKê`7àìŠÞ»ºehÐ 9‚„ƒ³öÖ‡û`þöå¾_¹ úS¿?ÏC°Ëž‡@7”g¡ÔÀ³Að:¼ƒ¹Âýx"ì¯D† #¡;Šß—ß¹{ýµâ–Õ5´TóW}çNïužë3®Ï¹¾àú’ë×W^æ{/‘Ký®o¹¾ãúžë®½Ìõ7¯Ÿ¸~æú…ëW®ß¸~çúCÅJü§1_ј¯hôá}¸Æ|Ek¨¾“¦/ט¯hÌW4út>]£Oך«oÀ˜¯hôí}»Fß®1_ј¯hmÕ7 tBý¼Æ|Ec¾¢1_Ñð­‹Ú‹ÿkø¿†ÿkø¿†ÿkø¿ÖKí1Àÿ5ü_Ãÿ5ü_Ãÿ5ü_ Qk‹ø¿†ÿkø¿†ÿkø¿†ÿkÃÔÿ×ð ÿ×ð ÿ×ðMWq"þ¯áÿþ¯áÿþ¯áÿÚÕwãÿþ¯áÿþ¯áÿþ¯MSßãÿþ¯áÿþ¯áÿþ¯Å©ïãð ÿ×ð ÿ×ð ÿ×’Ô~`ü_Ãÿ5ü_Ãÿ5ü_Ãÿ5üßÜ׆ÿkø¿†ÿkø¿†ÿkø¿†ÿ›û/ð ÿ×ð ÿ×ð ÿ×ðóþ¯áÿþ¯áÿþ¯áÿþo®Ëâÿþ¯áÿþ¯áÿþ¯áÿæúþ¯áÿþ¯áÿþ¯áÿþoÆØø¿†ÿkø¿†ÿkø¿†ÿkø¿÷4ü_Ãÿ5ü_Ãÿ5ü_Ãÿ5üßüþÿ×ð ÿ×ð ÿ×ð ÿ7¿-Äÿ5ü_Ãÿ5ü_Ãÿ5ü_ÃÿÅ÷.þ¯áÿþ¯áÿþ¯áÿþ/öZkø¿†ÿkø¿†ÿkø¿†ÿkø¿Ø3¨áÿþ¯áÿþ¯áÿþ¯áÿbo‹†ÿkø¿†ÿkø¿†ÿkø¿†ÿ‹÷þ¯áÿþ¯áÿþ¯áÿþ/Ö¼5ü_Ãÿ5ü_Ãÿ5ü_Ãÿ5ü_¬íhø¿†ÿkø¿†ÿkø¿†ÿkø¿˜¿˜ÿ2½þ}Ï.¨ñ˜ã”z¼»-Qïn½Ô·CÉ2î1ßå¨ó <ç:1s µŸ¼Tí'Pûo*Ô~ò 5ßYå±'ÌYg7÷”‡ªµöRµöLõf•Ú[ ß›kî5j?ŽK}‹ä9ÿIVóŸjõž8ÒÙcn~“¤Þ¯UgÔªõ÷|õmfµÚoàqÆA•Ús¤Öãת}ç¾ê¬ƒ|¹ÆfîÕñWó£Urÿ¹yæ¿Úƒž©Î=¨Vßm©½;%j¾T­ö£û«=éñj_ú6µ—ÇKíç‰Qs¨RKšûÔ½ÕS—Ú¯¾VîY7×ò¼ÕY 1ê[ϵêÌ„Jyn‚¹Ý_íeWg(l–s/ó»+/µ(R­û»ÕÚ…|·m~꯾ MVûƒJÕž÷µïÝ[}+ªÞ'«}ðkÕÜ­Jî!2×½Õ»q—ÚO´Jî‰ëâ»Ró=‚¿z_¯ö­Ug5TÉw æ™ þj]2^½K/UûjÔü/H~ƒjî£ÏW{éËåþ#sÏQ€úÞ´Jí9òRßEÊxÛüÞ´\î¡7ç}¾ê]ƒK¾{ó?óÛ2où ªyvC¤:¿¡Dí§¯T{¼ä>$s/}¤úþ´Dí©¯”ûêÃyγpž…ó,9¡]Ü:tuô¥£/¾#á-Þ#©‰>¢€‹. üQè$ ™£! Ø(ࢀ‹n4z&Cô±ð3ø±èh,¸Çñ|¿Çó|<ÏÇÃÓxt;ÝŽGæñÕ2”wQî¢ÜE¹‹rå.Ê'ÂûDxŸxV†ú“€› _“Ñ[4£¡ ÿÑð OÑèo ú› ž©È>•úSy>•úÓàc2ÄP7†º1Ô¡n uc ƒ~¦ó{:þçó|>ðóÁ?\óÁ•D~'#鬜Â$ƒ?üÉèTä‹KA)À.€‡ð™ ®TÚVäù^Ò(O£|!å"?ÉBt´p­œòd€+\àg÷‹óØ3Ï> xqþxðYÀ/¢¡›~çÀ8u1¼/†Þb艳«¾cÉ—{ÕÌïCÕY€k=ö«Eª÷†•ê;Ç0õîp³Ú3¦ö¬mV{G¼Ô;ÄLõñ¬ÇÞ5÷%gfª³kÔþ5—Ç·-^ê]b²ú^»J¦¾q)Wï½ÔwÛÉꜣjõ e¨Ü—b~¿}V}¿í’ïÅ™æžìunàZùm¥yîQ€úÆ2_}T#Ã.óc²ú¦»\íwóRg Fª÷«ä™‚ænoµ;^-¸Yž/h~㫾‘‰Qßk–Êo6Íw”¾ê¼¤xõ x•ÜÃbž›ä¥ÎŒT{fJäûsø9uaü>\œ hž©´Yžm$¾ûèÂÃd¨h~3¾J¾ßÙˆïCÅûÅðHyFR¸¸bäY:ºÓä·ÛæûÉRyΠyS¨<ÓP¼2÷™¯UßæTªïͽÔù)‘ê •yVÓ˜³jOO€<Å|oJù˜sj¿Þf¹/Ý<ÇÉ[}˯¾ßq«ox*ä^u±GÝ<ÿ°\}Èý¤‹ê{t—<Ñü½B~»cžè¯öÅÈ={âDó»t_yN‹y~“Ká´J}ÇS¥öµ{Ëý{æ7<.õ]ú*ù ¬x¯*¾ç çY8ÏÂyγp¡{èF]‚[‡®Ž¾tô¥Ãw$¼EÂ{ä9vG\ø£ÐK2G!C°QÀE“¡ùhô7Ý…§±ð3ø±èh,¸Çñ|¿Çó|<ÏÇÃÓxt;ÝŽGæñÈ좞‹rå.Ê]”»(wQ>Þ'ÂûDhM:n2|MFoÑð hø†§hxŠFSÐßTðLEö©ÔŸÊó©ÔŸÓ!†º1Ô¡n uc¨ƒn¦ów:ü̇à˜Ì ñ\3¡;“ß3ÅoðÎï|ðÍâšÏ³YàŸ ³› ÜlžÍ÷lt Þ®XpÇ¢ÿ8ôÇï8~' ßp$ÀWºM@æpÍ…ö\žÍE‰\sÑA>¸ÄYÜË?…¿Ë‘+:‰àN_Ï’á!œÙàHo2ºL†×`S ·Ú à/¤Ò¦©È’Fyåi”/¤|!å ÑÍBøÉ_ø2À•® `3Å|&ðYÀgŸ|ð‹àMäÛÉá·ÈG’‹Ñåb艼‹¡—‹\"7A.2åÒ^yÔÉão> »€òÊ (_BÝeàYBýeÐ\ÍBhB³š…Ð)¤ÜMûS bžÍc±XÅ¿Ëç—ýg_&æcb&æ]bÎ%æ[b®¥æYæ¼JÌ©°sþdÍ•¬y‘5²æ;b~#æ6—Îeļ…vû›9Êÿt^bÍEÄ<ÄšwXs k>á9—óϹƒ˜7ˆyíeÎ .ˆùÀ_ñÚŸÍ<çžs+þ·bú3î1¿[ÄùVŒ/â{Û‹¸^Äô"ž±¼ˆãE /æíÂÇÅÀßë¯q¾]€:#t›Ç~¼joÄkÔ¹ÚçÔÞœm—| ª¾/Uß®U{ì¼ÔYwùûì"Õw.õ=q€Ükg~K*Ïh¶¾7gMŠ3ŠÄ7uâ»bq–޹¿k³ÜŸ,¾EU"Ï‹4Ï"Ê”ûšÅùâ=±LìaŽ„Nd€œIð7_ü…çñàÉw6x–·¾ò!°\\ð»œzË‘/]Ä#Ȗɳ4ôɳlê/åy>õ–Â[|çƒ'WŒ“ð½>–ÂÇRxK°ð»},§ÎrAyçƒ? ùÒ`—S'…ût®LpdsŸþøJ/—ÃD:ø²i¿$A~r‘?92Á™_iÔ˃^6ôòK§=Óѹȓ”'ê€/Ÿ²eè&Ø4êåÃW.2çAoø–SžÙ\˨—î–S€Œ©Ô+@Æå<9”–QÏÍóeüv‹Ø=¹ƒäR«[ÄÐ[ñZËÍ ßË'þò å†o7véF7nÚà ÿnxuØbðS¦‰1Cü»¼Þ~y½ýòzûåõöËëí—×Û/ï]ûk®»‹qÔíõï{ê93¯Ë=rï¬Ußèø«oÃó=r„ª³PËÕYØA*ÿN¹Ê¿&Ï%±¿S߈W©3JÂTþ€Íê{oõx©Úûï­ÎDu«<<^*‡€[‹Z£Î’ õÈÇsÎãÛ’Kò ä«|gÕ÷;1ß{«¼<™çJy©ïxJäÙR]Ϊ3OB=ÎK­QgL…©oz6«sPÔùÜ%2Ï€ùMy¨:;µTž7ež×¤Îœr˳»ÍïG½UîžLyÖ¡™{à¬:+%HA•©ÎM©PßœûªïΓU>‚m2'y–Š¿:O%^OµMžQeæ(ðWç®&«³ªJU¾‚juF¸·Ê[àRß ­’ߪ‹ï*Ìs¬|Õ7D1ê;¢U*'P•úžÈ[}ãò­U9‚ªTž _yÞ¢yîx˜ú¶=S×X*¿Ù繊oÝÍoüÕ÷±ñê»÷RuÆK•:Ë[õêRç½®Rß(Uȳ_Ìo•¼Ô0.y¤ø¶ÖÌP¥ÎƒõUßË'«saJÔwò•ò[yóûøPu^VúFÞW#s'˜çeUªób¼Ô¹Y‘ò[܉¥êì,d‰•gŠor£ÃÔY°kÕY2Õê›y_ù-“y–LŒ:?k­:C«Zž-γpž…ó,Úcjeˆ]¼:úÒÑ—m¾g!ÿt~ÇÁc<%@# =ÌBž(äŽ(àæR7–ûÑèuôGSo ¼~l¨Z*„§ñÈ2]E#ãxžÏài<8Ç£ÛñèaúvQÏE¹‹rå.Ê]”»„^à}"òL„÷IÀMøá28£á?þ£á9¾¢á3>¦@w*x¦"ûTêO¥î®ið2 ¾c¨C½4èÅP7†º1Њ¡§ó{:|MÏ ðÌ€Þ àf€o&¿gò{¦ø î™àž³À; ü³áo60³™ ÌlpÏF—±ÀÆ‚7ö¬œ~Ä¡¯¸³j‚.¨Ÿ_ è7Aè\sÀ5€¹<Ÿ‹.梃¹<ŸOó¨3þçƒ>xç#÷|øMäw"x“À‘„,ÉàH6üÉè4~Sjä´f<,€ÏTøOÅÞSá1ò´9ÕYHùBÊBg!|e€+\àÊW°™âª‘S¡,à³€Ï> øE´Ñ"Ú6‡ß9ð“ï‹á}1ôCo1ôr‘-Ùr‘-þó¨“Çß|þ {å”P^@ý%Ô_Býe5r:UÍBhB³:…Ðq#w10ÅÐ(æÙhx0ãnñÏšCc·—ÏCöú×;Ùsžué¼êÒ¹”˜Gy®­‹¹’˜‰y‘ç|ˆöþ›y5×sÚÚžÃ`ûæœEÌSļDÌ;Ä|CÌ3¬ù…˜Sˆ¹„5ðœ?ˆy‚˜ˆy5ÀnÍ9€ÿ‹Ø_Äû—Æ÷ÕØþ¯°†àõ¿wFv¨ÊPá‘3«T}§êVgÿ‡ÉsÿÍœ/ùêÌ/uR…ú=R~nž©¾?¯Qg•¨\XÞòŒ9q~G»³2–È!)rŠo¡Íü/n3Hòå·Ò"¯Š™Æ%ÏŸ7Ï3Ê—çg›gËÅËsNͳå·É39Å9ñâ,q¾fyN¿øæ92Hž/r²ŒÆàÆ^”ÝÙ8_yö^¸¯<«Dty‘à„V$Ï'ƒ2x&ÃÿdøŠDÇ‘kežÆ1àGÿ=†òqà›Dù$èL@Î â/ô¦Š±û©øãDêM~°ñàMB†$tOýxpÅC#IÈ›Sá'žºñà™EÙœZ¹¬—¾â1žûXøŒå>‘:‰ð;—:óE|#~ÓvIÐÌG‡ùÐJ6zéeËh×`Ó¨—.rá%zËÀ·œòtøËæZæ’CÇr` -•zrŠ› ï˨çæù2~»y^€ÞÜ\‹àÕ ßË\ñZËÍ ßË'þò å†o7úq£O7ºtÿ^Ýðã¦üÅ”]^÷º¼^âuyüò:øåuðËëà—×ÁÿÊëàb,ñú÷ÍŸPë‘/§Ò#÷ãf•ƒ>@UU¢r‚ÕªsRÝ2‡Âßä«Ty!#å„~•*½K塯Vgºd®H;½¯ÊE¿YåÑõW¹V©ü‘¾*d‰Ì¹cæÓõUy$KTNÝZ•S7YåU¨RyÂ"=ò„S¹é“=αòU¹uÝg~ûªõ«ä¹ßfžz_u¢[æY8«Î”gñ˜çתóÀãU¾ÝjuVb˜Êë³Y î¥r×Ç«Ü •ê¼+_•7_å`¨Tyì}U~Ë•‹·Tæ¹4ÏYôWç`eªúµ!»ñÇL)`¾°8ÐÂ6™ßÕ}²U7™Î¤ÊŸ¾÷tï9ÿó?çÞ÷Þ¹OÉ=Ýrÿ Q‡Lì[Þ3+V«—™¯í+´EîݸtPÛ‹»ËRµ=É[µº{dm‡e#Z}‡%r/WQCl´Îï Vë7F¦…—ÅkulZÎnm_ÈGµúEȵzzYãlƒMÛ—kPÖ' ïw«ÕˆÈ—5wžb¯£5ݲZxÏ£IÚ>±ùZýˆ-ZM´A­Ž:ÉfY#)¼Wl·ÜRÔD{x…÷žLÕö‰Öö Œ×êGØd=Ððžéûµ½b±—gÖöO§#G«^«#£íß•ªíáÕ)k…†÷Òö«;-UÛ3Ö¦í«¾SÛ;vHî»¶ ´m m~lÀ® v2ñU!^ ±Rˆ…o½ø «Çßtø¥Ã!ütb’ŽÏéøœŽl:réÈ¥#g ž° ÈdÀ)ù póà”ýL|ÌD7‹ö,Ú²ˆk.ØYÄ6‹XdÁÉŒž™~3ýfú+ág¦ß çl¸gc+rËA.—Xå‚»;á¿Ná´qX.,ðÎÃv¾ç¡Ÿ‡~úVxXñ!ü|tóÑÍG7;ùøâÆv8ÇåÒ¡œB쫼"΋8/ç`] bp‹Á/_ 2%È” Sv >–"[ n)¸eø^F¼Ê8·q^—rôËáUÎØ”Ã¥,;Xp¬ ½‚XTàCí89Щ„{%ø•àVË¥‹“s'¸U`Tá ².ð]øæ‚¯Yv=pðÀ³þÕÌ÷j8ÖÐ_C-ýµô×Ò_‹ZxÕƒUV=Xõ`Õ#ëäo@¾ùäodŒ™›8ߟMpo‚{öš°×„½f|kÆ·æ!¹ljY"—N­Ûð½M¬©éo£¿ }?ú~ôÛ±ÙÍlv`³›ØéÀN'~w!ÓPm:‘+‹õø‰µXOG¯¥#kçsuTÎÕQ9›uTÄÚ0².kÂ3­™Óã×£k¾è5^ôºN¬éÄz.z-'Öpã×mã×lÑë5±Vc^þÞM¬Ï"k²ÈZL3¶k°èõ—X{ýwë®3­¹"k­èµ•XWiMõÇÖPÌÉÑõSôÚ‰yùk¦ÈzéL¿)ˆ5Ò¹µÑŸgm´"æKUG(\ nóêwkõ̲ö›xLÛ¬Õ|C.n’VÃì8°gìÑöãôjµÝ’´Åñro×pÍóX ûÙ§´ºBƒZ-7úVÆku@‰M"ÏŽD¯¶Ÿ§È!m²^C¸&(ç«\r¿uQs½YÖ5oE2Q@Ô'{·†k7œ’µ‰×wkµ†ze·•I²fƒ¨# ê6ˆh©ø²~¿¬m ꬖõxÄþé©Ã²¯ =¶Œ½²Îx$e‚— ·L03—ÈúŸ¢–ÃZ‘gÒ¾!IÖ:Ð{e S¯¬?º™\â±[&xèiSÄ:€ï9ÊÚÅÙèᔃ\.\²i³rÌ¡ÍÄç²'å#1›&âY4I>`[Gd-Ó,Ú6‚›#ô»e}ãú6« _«ÌZ ñX”Á#|¸ç-‘Ï<žÍyø^JÌìÇe}·|!ϵ_l˜60JáÛH#ñ*¢­ 6Ê‘iÄf1prâG%mN‘Ëa£F|ð»ùJp+ùîy/m­Ø­L•¯”+8÷ò ñqƒž|þÖ SÇy˜­´·ÂÕËÇÍw|<ðòÒ^ƒ¿>Æ }/\ZÑiEÆ‚ÍMÂgbÚJ 6ñ½…öMph†“Ÿ ˆy ¼šE^FHèÒÀ÷vü ¡"&E`„èo'%È”p^J{)ú!ÑDZŒ¾r¸”£_>$Ó;v*àX æ@Ű|ïÀžÙJøT‚U‰~%þ8E 9wÛ*0ªàáÃ…¬ ß\ðqaϬ{bä!&ÕÌájü¯†S ý5ô×Ò_K-ýµØ©…W=Xõ`ÕƒUV=²^ñA¾ùäo@¾ùFbÕÈ5º‰óMC2­j‚{öš°×„½f?|kÆ·ø·ˆ1æØÊ± ßÛèo£¿mX¦`~ôýè·c³›ØìÀf6;°Ó.Æ­ ™.ÚºöÈý¥ÿX-ñþ/üw¶~OúKþ–$ž!ÿ×ß‘¾ª¿!}•?:Û¿ýµýntî7£?üÍèÜïEÿÿ¿‰¹lŽùjÖ‚‹ÑjE«ÉàÆb'ÙXž¯±ø2e‰¬É1åѨºÝZíÑIQµ¢ÑŽît³V›ƒþ8±Ž€CㆵÚèÏ+Má2.3‰•._«¡—µ‚SxÆÅƒ9‹þY6¹÷¿¨= N³óeý°Ùðš½_>~ç¤Êúp:oT-Ó|­~4~&0f‰à'‚¡cÜ Øœ ×¹œg £×jH»´š#²6RR¼V·c³¬sšt\«ö¼Í²~‡xćkÇ¡;ÝùCZ³¬§Û)k§…kŸ¦ÊZ†Ý²¾ôÅ#ZTdvjõäh7ÿLärñݲB«!E»®W«ó×ÅfYcNÔ×H¦/yHÖtÎqÉúná:¦`¦»|L/¥WÖ10À{5:«‡dgQ/JÔÚ(ÏŠ­µ`¬õÊ:ÏÅ`”ŒÈšáZÏf­æýi[´ºÎউqÝ/k<§ ËÚn¢¶”ݬÕÛÀÿõI²Þ³-_Öß(ÆV v3ᣱBO‡Žó”Le”™Îè'É$œb`Ê=²†™æÁ6‚m$VÕôQ2Š5~ñÁ(Æ~è ›­ øeò=“yf‚£ y~™ðË„Ï&tLøœ f6\|wa3Ùld³‘µÂ»ùúrˆEþärÌåha -Ø´`Â= ö,ðµœK·¬p±ÂÛŠŒ +\ ˆO6 ð¡;øP€ô‚Û€L!Ø…Ø.××B°‹À(¢¯ŽE#2u+&õœÁ*ÅV)¶JÁ.ÅŸRѾýpʰS†>ÛS;çvlÚ±gßÁÇŽ¬;Ž~!çŸbÜÀöŽ|w''øNâTE{6ÝppÃÁÍw7öÝô»±ëÆ®®âåÁ/vªGdúXƒNã„C×Jñ©«ü:°ëOÉ´Ò‹œl/Ø>ä|ÈùóaLJ²>ÅFŒ>4a³‰ó&ì5‰µ ql¯Y¬YÐmA/H [àØJ›Ÿþýl° -—øAp‚àƒàˆLYCð Á+¯N‘ψÜOü_ü)µf"kqó‰^OˆuDäý»X;DÖ gªE#Ö"ÿÎ÷ÅE6¾ ¾Ÿ«g#óY‘ÇFç¯Ñï·#ùjt®Jžú{9ªÈM£sR‘FòÐñy§È7#¹æøÜ2’KŠÜ1’7¦Få‹úq¹a¶–÷åGåxgz'íÓò1‘‡íÒrŸü˜¿h GQ£4–y–Œ½•Äs*8)ñ²>i~Å‘Æõ¦Ã^²xæs®ˆg#}ñŒŸ§ÙØU†d½X¸¾ëÀ5€kà˜¿ÕÌÁ °2zeïü4pÌâº^–x.‰Ü¹äÝÚòYÌ…su+c¾Üu+¹¦¿ÿ>8ú=Ïÿôx¿3þÝΗíߟ{¯söÞëˆyk‹ùjÖYå:˜Ì™Œ?“ÁÅN,cË=%¦Ày%:Sð}j¬VÙ©èNãû4t§ñ}ºÓÑŽOÓù>þ8xÄÁ#®WÖhu¨E=íÄgx3ÀIÛL¸Ì„ÛÌã²~c<2ñ`Äã÷,¾Ïcßg¡³šx¥ñ=»³›ÙØ™ƒs“9Þ±zÜ ØK'™„ýò±›HŒñ-œDlÏEf.|çvkuXÑ»{s8‰<±[«)Žè%hµ¼ÁžGÛ<ìÏŸ¤ÕdEo>¾Î‡÷‚XYã{¾.Ø#ë³&Ó—‰Ž;™øcBo!X ‘K#:l.?ßrÀ·€¹ˆöEƒ²¾i²Èÿ53.ÉpI¦=\Ü“ÁLÑÒV­&%xúRÐÓ!¿Š­1xÈgÇÊt¡Nkಆï%ô•0–k;e-ðb>kᚪ×j‚ûTðSéOÛ­ÕK3 =Èi·Ã{=xvä×32‘±ãÏzæÒzpl"¿FÞN¼làéà¢Ã'…£¹Ø5€—¾Yœ Èè7"g¤=›Ø/#öª±a$nF°àáaÄ/£ÈÛá—F±Ê§Zä´`[Å‘vLø” –I䪜çˆ|•O6|kÑÉF¦{UØÎ7‡y•Ãxær,äã Í‚ xì”Á¥ƒ£y+\­`wÀÝ –•ö8 Sßø`«ŒB0«/„[! Á+D¾±-D¿ý"âPÄy1ØÅb€ïÅÈ—ò½ÝRlùñ!HüK±QŠ2ÎËÄ[ebM€Ÿ~ì9àlǦ}»8ソ*|md|8wˆÈÁOÉ´Í)Ö Ä¥JØAÆ]Ÿ*ÚÜØwÓîFÆm76ÝØôÀ±øØ«¯¼ôjâeêW‡/uØ­cžÔUç:â^‡¾9/r^°½`ûó!çÆ\r~0}øÝˆ/Ä®‘X5qÞ„í&ì6!×4"ÓÈflµ`£ý8¶`£…¸øGdJ ?€6ÈàÀF¹ ¸Alá‚W^¡%2å ç¥â/²–øSßÕDrûÿê]ŒÈÕEŽÉÏE~¦w1Ñï]ÎôÎ%ú}Jô{‘ŸŠœtü{‘ƒŽÏ?Eîy‡2þý‰È##¹ x†žáÿèÿ¯ßcDçJÑù‘>*ŠÎ"¹Îø÷N-wÿÃsVj'·ÊºÖ±Ì­ä-r©6¹æÍjÚSÄ3Ì8qfÜu`ÎGì똗 }ÎÑ7`?C|àdË€~&32™ôçà» ¼ü´pn¿ÜLøX‘·¢—¯8fO’K¼ð2Œ\x–€c÷qÄÇbäíp±Ðï@Ç$0é³ÀÁ*žEÌ‹÷JŽqíqîD·yàwÉ%™v×K#8AðÁpÀ© ²A|®÷bêÇŽ ŽÕÄÔ¼Ù ¸plàÚ ÒÖ ®=qÿB>(0àáÃo?:ó÷Èzæ]KbÎú߆Kõïˆ}G•§Ÿ¸:Ô_3Q™Ük)IlP/ý’U*¬9v"Áx™zû„÷¯Êþâ;º7N=Òýº÷äx$µïö+Ok’²Jö«Á—¾5tù NeYÿZÛ7cg·´óó+“’ú•Áe¾·Ÿ¯{ZÝuWAâé½fµoëûë–ÏýXI¾`ÚÕ©{jè&ë«WÔØÑ{Rê=üiÓûîQ¦–ßùÂSŸ=£<=’z±¤MÝ5¯ú‹£öéºc¯Ýzø_µïf׿@Ù[jçmíOøþîSô‡¥þÝ#öÖ›_Qž¾Ññæ¿^\¥ö„aÚվ歋¾v@íšxøî—ÜëßñÇHùï¹7$ÿì'ÊÓ õÊÛ×\§LÐüêYWó£;&Vè>ŒØSá¡ó•+®o6þs×jgoSÛGžje¾ä ^’Ä«ÿÝK¯ÞbRöźÄyçh\{þfò/~öÆmºw–Úbß(Vû.úà5žPRΫ¹îÛ3g«q»¯\½ôº±8úõ/kÕŽŸ¦ç(Ó%/e å¶ÕÏd¿?µç› nصMí3¿/ñ­Û•Õa˜T5ôÚÊGœ'ÿCIxþ“;R÷ä‚ç’xËGúŠj¿¦ $ØVœïP{ÒÚâfLyL÷ž#¹\WõSµïÚ­»JË>S;cîÛû€izÚ¼™ýÑ]ß5(G~½ò`Ù}7¨=Á'æžþa·Ú÷äå—ïlQCGbßµ9y9þ_t›°)¨©}¶§¥÷rµçG½·oKp¨}"Z£cãî—ã>0ø¦+ý{kGçå‘ ×´Äx«ö<ø÷¯ö¶Ý£;a,¬]|÷×•å ¯ûÝSoýF ]ûìuÁïE_ŽûÆë®¬RïÞrÙð·æŒÅýêãï7~w¢î-^}bº?°'çoeQ$î9/< ^»~çêŽr˜ &(³#¸‰Ó_âÙŠÈu¢öú‡eK•´WzwT;ÛÕNåÖ·²N–Žg@Ε»ëÖ·¦èqߌºóæ5·LÊJÒ ?õñîGÞ»IíŸwY(ù–½jgàô‰—ü}9.Ì›òÒÁ‘yªÚ²ýñû^Wæi×ÓŽcÊÅC_TŽòê_uô.÷«?¯[|Uæapä<8òró„'Ëç)‡6ξqï/õêŽe_ûföcNµñã—¿[zTY¿<僓ÛÎW»®HóþxW:zrÙÿvÙ?Þ¬;­Ó¡ ×ÍZ”þ°ºýª%-7>S:z}öOü÷­YkG¯—®ñ· 9?ެÍ}ê¾îT2K¿xåê¶ãÖoÞ2k,žY|3æZ£ìËyrXLó]ÏúÐfÝØÜtGä~¡nûNç¦ùÞ Ææ³6. î_º¢d¥[íL_žœ7‡ïk;Ðúñc£ã|ð¼…=ï]Õ:z}m[s´äØ÷ŒÅ3vAõCe1Ê å™“÷®†&œ6¦ß¹BÿN»œ7‡«àÌI]©xhóÉKß;Æ+až<46¾—~R™wËžÑù¼w7¢CàÈyr8!<ÊŸO0ã³hèÇ/WêFÇ5øTÜü{?ú7ôäü8tÀ41o`—r`ÞéåÜÔm%{—û3Uí_jÏúAÀ8‡ ³æØµÄµ]·CM׿;óD¿²ÿ¨åêo?;4ÆûŸ*ë[ð·j¢îùÅoÎSƒ•ë¶zíqôä|8tá÷?û¼6}t>ìçn6ëãOÔíç?Ú¾î6µ_Dç¼üÈõ¥#×C»œû. l¼UÙ¿lë‹úÎíêöË ~ëýjÿž»Ù70z}hÏôä<8XyS×à½ÁÑûžç©7øFã´ý‘¼eu“t/kó ŸÉœüz¯²xÇáu§NVCw›·þÚô+ðä<88õ…@üçùʾkVÌ€¢ºýÄ­xA»îEí~½çÏm‹_Ú¹¯êßéã}àñò—ùöve_ò²÷PU]Û bA@E¯Xè½])¢œ+6Ô¨XAш,;ƆšcŠ$šˆ5("ر1—¦¨Q‰±`I$1EM¢Æ˜Ä“ÿ™3ûœs_¾¼ï÷~kýkýùÞ߬u"çÎÌž={v›93{G}4ò3¶¡nÌ(WVP»Ç­‘c3ÙÞ]ã·P_Ìë™ä.e›äSw}{íŸÁ64ºµrðsXþs_?1d…F'½1¯g×¼¼xËù¨r~Õ¶¡åôbÛù÷YþémY.­¯²t¿wŒE¶ß¡¾˜ÏŠâœ=#š>“O%üÜÄéà¶¡÷³ÁƒýY¾°º~Ÿ%æ±bƈÎÒ Ú<ž²1ñöMn8üGÐK9ƒuþ]:¿·{d±+ô›#ì à‰y­àÃ[|D“«ò³®´ùÉ?àÔÕy}ó3nµŸÑf° Î÷‚½ô¬²<1ß§oÅNºtkŸ\¾t@B¡÷¯º~Íú©nn”>Ï[²/¬rTÿíÅüž^™òmÀãåòNa¡uî³w»þ1âõú±üª¼ÜÍC6²Ù•¯/œ<~·éÞl1¯§¦ÈÜ"ä²_9±áV=ûnšÌ ê~ùCãÅ?ér«ÊÑl1¿§>ý}^ä˵ä2¡oÙÆ?Ïç¶ló)+öÍî\¶"/ðê‹y=õö댨<.—-ºxïsf›^+œÖópºFç‚fqomê‡úb^O ˜ZðÉËÏ´y*‹úÙ»é$“úÎ6}z§ÿ)ûvxsÁ¯^[Á·'ßY$û“Öæi¶˜÷S+¿6¿ä¤éÓÒŸçýdûÛôíºýž ×ñ¨ý`kýÝÛµùNÅ|àˆù.çf>(S.=rÏî—=5?jÓ­n‘Íž,cfæB5±ôï?þnà¢h'æµ|õ£?;(—’ÜmÚ]¸ãBbŒfGTº¥_âŽj´óY.û68ÞíK¹“¸ÄÈ6Íî}û©Û V`Ïón–žùtôNÛî¦{sÄ|–}];ÿÈ={¹´½òŸÆ?›ú¾ïªóõ³¤{ýoŽ˜×²e£yÔj…\ bÛËÔô㦨+ïxø\fù·8$Yî2ò§!#~­ùÎb^GÌw™“â hóWrïÃ#—[t“[^ÚÔ×­bÉÓ5%ÿB»¸w¾–{ ;¨ò›ªÇWðEékZno®ùÍ%…¿ìk7þ2Û´âÂñcC.iün}GßÓóä.û®û/®^Ãfqµ’}ÑOÁ¥}O>}2³Ž\òòÝ…óWi~Ï&îU¿®ÛùSƒ^»Yî¼Å.>·ž¥)î}'Ýïœ#ø£ävÜH»¯É%ÝvºE&±÷FO[‘õ:Ë?–éµ{íAÔüP¢˜Íp¹Î#fT£Ë{þèÿû†·YþŽ×¦»æö‘;‘ž˜Å­_p Ú ¾(ñöócÖûrñ…z£¦]rQýföþŒ·à÷–>Ï<Ò­ýAõ›t?-]ðKñ è¹xë©”º šÞzÿŸp_–¿St6+|ó®EMýÑNðIñkŠ¡Ôæ·xÑÄÆMö²Ío}¶(ô(Ë¿ùBIÜ Í˜Å”ŽN¯tÁ'Å&ó×jö­xx~úG±Í½^‰jÔ}•*OšŸ6{¥âÀ©ãÁæ¯ò?ü¬üe¹¸ý¾ziÉ™lsßyŸ8> Ðå«W óäpÕ. Fç‡tÁæyŸõÜÛl¾\ÜððwçØ°ÍQ®ÑѾFÍ/*hª8¨/æÛlP…lrÇ6{n{j2èý û#Gpo²ÑûªcAÁ,¡ã Â/ eséfHf¶Ù5õ û&ïéþXÅîé¹¶lvÓ[mœð Ú >(š¼ýŠÍ]dó–ÛG#6d›ûÛ.ë+E±üÍRá„/¢4¿4 ^üðå™úüÏó’{.£Ÿ^4/Íû©¦g7‹~Õu ËW–eqš~œ©¨ oÀ|q"KQ(Ú|š'F´Èhz2»G—€G¿|¬ó'çª!e¢ÏŒ¥ëbªJ¶ëó;WðÉñc¡]/Oö–Í}F~8öû.,Û¼fÖý‰w£/«ô9³÷£È®šß3ãߊÂ?}S… 8‚OŽ•M?˜ÓN6‡¸½þ›üÛbz×£v³•º}¾ãg=öt›)ô Ú ¾8úÅÏ㟭¾)›ùhÈ„{Ìò±võôOYZ“º'¤m/¡¾à‹£b½*›m[MŸµÖmmÎ'þ‰Ž/é_½Á…!³Ó>ÛAÓoì§o¶~™m}ñ{½÷]Ñùáw®ÙÌ7ÛÍû°øa®à‡#´.gä/m}õ»¼ÇßéóGþ—é¹´å#·ÎX¬¾›îÍ|qÊjl¯ùª^’Y¦4i†f¶æ¸>~ÇΨò¥ìAxͺ÷YÁªloÕo<Á‡n’›5±–YçÄ\×%ÃÙÖ»O\{¥«¶~É?é7 ¯ÙŠ»0MŸ¿y‚ñåèðr׆ã'³­Ç¥37oªúU—yb¾vHy)a[°\”ïÉW&lëßÝmf`ù9A+/>ªëÕybžô>ûçÔöõå¢yvXq¾£éÕ­w¢ÿl×^§ÿËþEgsW³4¡/Ð^ÌûþÑ ZÝà¤Í_ÑÐ:†7ÒN°m;]ßá6Nçû ;âVJš5óñºÜƒýÈÁª>š'øaŸ­n5ù,Š:ß!'a®f×·õysÍñuºÝSÌÏt6sTéÁŸšÏP×A€'ø£À\7:pL¤¦·‹|<¦×î©sˆ×~¤§ŽgtíÔó³tùœÛôn³¸GúüÎü’ÿ¦ÂHš\ÔÊæÝZ³<Ù¶g¶ÖéæºÚz[å“éÂ.ë|7_ðIÞ¶î©ÉEÞ{/4á<Û>åè„¢y¿êvØqôëÍ^»Á¦ ÿí_ì©»ž|²\T7¹íùÞ[4>Ý^}´ÉÍí×X~£P¾R`Ó×k²r:_ðÇî-k¹Æ¤ÊE ¼–é°_£ÃŽ!IËv8^Ôé`ÓÃ*•MTú‚ÓêSh/øeW“,îÉENI—‡5ÐúÝQ:¯óvXÇ»î6ß}ÙG[÷ÌÀb%`@–ÿ|°º buÏïä"·/?u<;UÓ›;]TïY¸UÛWËÇâuXÅMÏàÚºR·góÿì¬?Ô|õÇîš>.òø$ØisÛ9múwlÒu¾q[Xku›ßÕ}>6#oIÞýU³uÿk¾àŸíãï®÷l×DÅíÜ·uûÍæX¾ÿ®~’æêãûìáLsñ3Ÿ>ÙúÒ¾#&aõüôìËl'wÓ¥ÏY¾A1”l†²X¡…&Ñå´û–³æ¥MØ®‘’ùl’¶Üû6_hiûÌ)GïúêÁÝÎ/|°šüEÈoPý½Ý~Óä¿{iüNûÚzjÒæ¢Ù#Õu+à ~XÊW9Žst}GûÕ»9›x½Ûn¶JrÄ—5=7•ô·&÷ _ÌçjÇã}¹h_ë©mí_a¹Ç†|Tºç }¼pÀ7ËÛªôF;Á)±Ï6ã¦Aã9jó›*ðÓçw¡Â/¬æü2òÛóØ…#™î³½Ý~­Û,êg ŸTX›¸òoÐ^á¶NÙ–~AÇçkßùg{¥?fìÈÓùÍërt Këd´Wø€½Iû‹šß×|“û‘·5yÝÛ;‚{6šŸµWÈ3›úÑÖ¶Û†…™î-Rø€ey&ÖN¼ÒTÃÃܹÇþÉOç°½Ù7ó:&õcy_½{9 Óf6eƒÝü÷­íÑNá¶‘{õÈf8‡.‰f–ßðûg}—ió‘W9ð ˜vÍ¿˜ò2wÈ>@{Á››sÆsÐñ_™sµïb;Mÿæ¯~¡í‰¥»4½‘W1«ø¬BMÿNIP<Á'[€„SØÙœÛ(sû¢JVÐàûs«®îÕè˜gÎjèá2]ÛÏžÄ?;tùDç·E‚O¶¸”mÜqG§G¥²aƒ=ײŸ¯ñIÞáV£öžÐø7™¾Ûhzi‘à“íCƒêÿ±b©l†4^/ê«ùùïåý7õËÛØxh–l‚ºßºHðÇNÛîcª7ß•‹[µYäú¬àÎùwzE³¼#“£ ;ýÀ&¨û¥‹?|p[q¤õu\Y9ñ+¶ÏõÁÄ=¿gyb?‘Mœzã»Φ{‹ÅüïâË‘¯ÊÅc"ntr5°}ƒÏ,4-…娕â4ë¼Æ¿É)|ƒÌG·/‹än:¼lÍ7‰ê:^.~Yù@£Íã¾EC{†NªóùªÝJûºß´XðG^ëb«¯òÏËÅ;GûDÖ bû¶ é2o–>ŸkÏ$þY'^!û©ê%MN ¾ ïyr1·– Ê4ý»Ï|Ó»Ö¶X}>LƒGó毲>‚M,>¹ÃaS5Ú >((U>¤È%¯ðù±ä5ÍßÙ/üR¶çAõç¢ãÙ±NG;Áûã£sÃæiüP"ìµ6þýËÞ-›gÐðÞ#ÖÁr(ß컋UÜúö:=3¿ìS–½VšßYÎ=(ÍÛ{´G½Ý5=¿çl $%InOßFoªžõÓ37Àü³¿ÏÍì^oË¥d—ÕñèúÖkOtüžõyôÎð|m~’„ÜŽà—ý§ ×]ÙqT.½.­5×WÛß9Àw3ú]dya?ßùèC{M^G«ëÝ Á'úÌ(o9ÏS.ÎÚ=Û^7pËS>'¶Qùžùvò…åí÷¡à‡ßÖzüËJ“\–h¼zc%;8á¥3Ÿ‡³¼.iÑow.gã^»þ,ìç/P_ðÁÁwö®ïï¶P.üÅîÞÐâvË«:¸ðôélÜ…²6muF;Á‡¼•…œ¶ŸZv!µão‹^×è~°Ú¡oðÈÚz7¯^Vîã4}7Îëé«W\<Á‡¶§\ º¥ù;åô½÷PCã¡ÝÓtº?¾øÖ廿iëü±w•cÓ½%‚‹ïðr¹2=s5þ:dœ÷¸ñŽC>{®ßŽþ@Óß>K.Ÿ:â×=7´ñ•‹ý.vh°UÈà¯>g{Ž\nÑõÞ86V¬oÐNÌûe[Yß÷,W¶Ù¡ô­»]öÐèºçõúEccJæ9å6Ú‹y/ô¿{¢ù&ù”ëýoŒ*c‡¶HS¾^õ5Û“öÛàÍo.g£U?u‰˜ïÂG|öµ†ç©9­¹ed‡.¦§ÙRÂöLäŠxKªvkæõÇ ´ó~´ø;×8É§Î ŠƒkËש÷áÂV:}ô~Ä|[¹è‹Ï§ÆjëÐÓJ7Öš}?Ý»hRûùÚz~Ï4eA¯Ë×1ÏǹUèHÃ÷ôKüC|‚Æ7‡ù2ä·7õùžóî丂Ç, ÔL>õ†éÞËbžskgÒ÷ OóÕøˆ¯Øá“‡–ߘ¤ÓYØyM$*ËÒ€#æùD„ÍòúLû®*W4<÷Uë?¿a‡•ÏH t<Þp¹þfâ6äeƒ íÅ|Ÿà_óš~,W o½Ç¦7;~æÝ{Ùží~ß5}³(ö³P_ÌïÉeŠáÕüÇŠ¼x~R…™\ìPû½l xÝ›Ë |ÑNÌsÑàƒ¯vüj…|¦öõ]ƒ]ÍìÈ›1]J¢>í·©ßùÔs"/‹yfÜ-|ï{ù 'ßúŸ5}vd_ä™þ)çTúÊ¦ÃÆ]ŽáÙl„ØgE{1ïŒö‹T:Ÿyéž{¿ÒäêHy¸îî?érõ°´¯ïË=I>GüùM½ÞsÚèûy/ >0÷˜¹äÚÍå3œÍKéx]uNüòúÑ•d÷8+L,«ºßL÷–Šù7¯çä³´Ï~äÞ¯7ž9D°\å3‘=&ì/ê‹y6s6týSÿþ oßœW‡Z¿¿xÿÄÓþ¹/³úoå²øo”/àh/æ¹8âaèÕ-côóއfÞ?Ì ¯Ý/ zGã·\Á—,^õk–Šy/&?C¥ã9~JǦ5+L>[v°2^ã³Ü éñ¾û—hßW†Ò~1Ÿ<Á%´_¡Žç\Ùˆã%Gû±Â¼OžtZ8*úÑ/7çåI9ßôM'ý~¡urvJv§> t;ºTðIIJ^L陇Ú~߇m|oÍ­Éeá¯&^[¼W³£¹×ö×n}ÿ±vdˆØG³ÀSðOIYù†~ã™ÆïÒþº:ßGc¥ŸÔ¹¢ùÕ{”mÍAºœ-üRtóÐ{·tü.ﶇJcG_}ØeòM'ݾw?wÂÿÍëÚy£¡bœr;±ßdº·LðOéÊ“k×{,Ÿ÷«â_ª4~>ZñÁö¶ñ7Øž^Ç^ì /dñ]ï½;í•Òy–ó  /›¡ã§&¦´qCÙž€/ƽc}žÅ«~÷2Á?eFÓO“†ht8µú…ºëíµ~9å'‡ØëÏ6®»ÿ¾v€ú×ýøe‚ŸÊ°¸“Î'ivæBо²³ š?,À=mn¿lMçÞo¸åÉ>ÝÞÅ ¿Nß_]&øªì„²p“/¬räàÜHÓïÇLÊO¯Ž™S6â„¶/˜@þ¸¶±LðUÙoV'ŠFWÊîW$ ]ò;–½|ö‡_iþwnqJIÔÜAš•@ç‰4ÿ{™à£òN«ž4ðI×ìÏÅ!;Ã¥`ÇfÿVߥcËÝ3½Ñþ·î³¡¬_-8eà¤ýål“ü[ÔÕÇñCux£[+õùÛ—ß±Ù…26Hõƒ–‹y?%ö»4ýôQÿC½~‰ªñÓ‰.MO­½7úŒŠÇG½Ç¸®ñS_±xbþO…‡<á˜üÿ¬;÷'ÖXÍݺ´/Ë}ØëâÐû¬‡6bþOñã@1ä.q‡\?§t⢲áÈrŸüгž«ºŸgº·BÌû©ÔÏ‹ýªZÉ—šçM9ÔAû.|²áÌ2Ÿu¿@ì;³~ç{ód´|pjÅ5ç0¯@ùßÝLèÏNš>L¿÷Ó.–ûûxüu_Õ—¨/æûÔ¦s »¤Éò¥7”ÚzáäŒó ~|ò…¶.̽҄{ü,A•Û4ß9]32ã6É—*•|:¾;¾92ss¨¶ß˜»¶×|ÿ%¨ëÂ4ïœm®½§Ù©ÅwPMüôB¼}fcM_çvû²Ñ‹šiò;X]'¬ >P>_µ?–-¾ôÞi ¯"Xm¨ ŹK6@ÕÓ+hÞØu:ðå,Í?ûX9ÎWG£OQjÁ”®?þÂr[(tYœðcОæÿWþa>áj|PtÚªN»“wõñ)Áû«¯·W ¾8Ýà˰¸¶^:>ÜŠt:£Ñ‡9÷øÎ#ÑW—~ŒñíõÚ>ê@õíJÁ'§]–ï[»[¾\«OûOo36síTÇ<{–›Ölû¥ýfÝ\)øät÷W.5ø5X¾Ì&uÔèÀNl©ûþŽº}r­íçëý ~9=z¢»GE˜f.+ŸÜ´ùaOºUßÈ>§óéiuw "’Zpÿœ~|ùUeC„™m÷§ú…®d¹Êö@´>¿+œæ_—ú‡kô¼Ì”LÌìgûâ«à‹ÂNXñ]a”mø‘h'øâ4}ßRçõ2÷­ÒöyÌ®õŽ,› Óƒè?Høº¾])øäôžÃÛm/Òü‹+^]øÖÏšÝþ´YµH§‡òÚE›×!³Wñ“_ºŸ›IüBç¯ ;P¹`ÙaÍŸ2»•Åy­’t>‰ýxoòÈ#ºß™IüúrÑšâIÚ<]!ùUçÝìöÓÛÉîú8Å>…n—Åù,Ý.g9òÍui­|Ŭ̠ŽœûvO·èãì¹ÌÝîâí<%ág1Nâ'ë­ûRû?‘¯àŸ± —ä*e[ÜA²]å¬ÛOßßoeó®Ü‘úZç~ïWu¿f5ù§7Ý*¿LŠÑæ§ê†âà°b7sÛäìÙn¾<›÷É— ù£|µ0ëgm\×ó‹{µq‹stª\¡àeíú]M¿_ï:ùµM›³â…U3~è´M_§*Çž]´sRÃźRÿ²ZðIy߯ïZvNÃÿ:ÿªVg‰j§YñÞ?GLqØ¢ëÕhîˆ×ÖöG•ÏÁ£ôß”·sxTúB+ùºâæcÅ·Ö¬Ø7jËMšï‚J—^ øedÏ-n¿^=7ˆ•н“Ü–Û±´Õ¿3¼"ø¥TYžèß}ož?°à½ëYIeÆá™eOt>ð±kR2o,í¬¤A{Á?¥aü Ô Í^ÞäZ öFuÎJvïZúw¥\—Ô‹fM^Õñh|ðŠà¯R~|±cŽ|ËcÕ Ý´Óö5Jê~V—¥ËÍ›¦7_|T¢ãŠÑèqKØÍÏ)åÞö¼~l÷ƒ±Î«ëë롱¦~ÛsЇŽà›’·¿\ò¤—|k±û‡¯~¾‡•ÙSûÏI£ËîÇŠß8ðþýàÁ7%tÎE¥ë-¬J6ïZÄJg *¿+cÊgá‹÷5z¼jG_üQ¨|•oѽCÕ-çÁ˜Ø~Ä&¨öïÁÅ?î¾ål±ü‰²f¥¹öÇÆLxÆrûš<D³ñâÞœéÞ«bþ‹Žyåd“îò'1ßöI +-~ùÛ½÷Çië:կ*æ½X¹öè©Ñ÷º¤ÍÓµn?~O5ôaòàÕ/^f€'æ»Ø7óµN£5z}²7zÊ Ãf±ÒGî÷î7««ó!ÙÉ)]v K´ómæ·[Õ×ñQ¶iÂ5>,³Û2ò@ä"–ÛYù¥Ùƒ—¶ ëð{TÀómNu€„Djx|* fçÊÂ÷D9wP×nóÒ&˜jÍš*¾Sžà³Ø¿–?íëï—ƒ•MÜtºÏÒWØî M°²»Î&«ß#^óÎÞS>ÀËŸ.Z`ýÙÆ×YÙ¦¡Øug»G-;{e~KëÔó]ô-ÿ´*kþæ§üÔfš#+û¸EÊ.kMì¦ó;*Ÿ'«÷ Ö >(Šÿ{Èò§üëY—Ǭ¼Þ”º «æk~âne›t¬þ\Ý__+øá$¼:Xþ· J¬<ôá¹+Cm4ÿm7sP×)ê½·µ‚NÒ÷Mu<·{»Gš¿y ñUù°}øãi¶[ùÌÖ[û˜rÇ ¢½ p?œØ¸ãΠŸ/jëƒÛüöDsýr9ù ª9Yõ3Ö >8®¨‡ùòímNü‹0+­ÙÍañ«õý*¾­yñO6Uõ“ÖŠù>.î Ê·ùnTR<+ßæßäã.‰,—îNýíU‡\lP_Ì÷1N…­åÛ¿~SfSõ+?ø÷ôÙî‡ûw¾!R×ókÅ|åæ{a†æ'W‹ó®–ŸÚà³ÓaŽNï£?bå¹P?¯&ô•¸ëÏÿ{ž£ù?7¾>¿äñŸc_yñÿKŽfþ_2Ñ m•XhIVJ>&.ÂJŒdÊ×ZE±Ë3(Wd¹Ž æ´ÞëÚS¾Vи.`Y(ÞÕ:Šwe æh_¯PÄüTò¾ ÞmžB5øˆ8 <·Q}³Es¼7ŒO)îUÅ1GýFé÷ªšâ^%‰x¡JÜ«ŠeŽñ4Fv>”·µ’b™û ‹ØW&Šg^%âŠ6‰ ¼­è»)ê7…|4Eݦ€Ý °šv³Bʇ”@±ÌQ·9Þ››ô¸WJ¾Öd‹˜Wöó x«%ôNËT‹æFÊÓ X­Ðo+àÙ ï­PÞê)Å.ϲÈÏ XmÖQ~Ö·¼-úmû”â[&ÎèÛù©ENÖBŠWîBñÊϵšò±&S¬òJ‹XåÙ"¦•§€ã8>èÛÇL9U‹/põE¹Êýðî‡w?àê÷ˆò¥¢½?è`#r£`n0?è+°ñˆ¾‚€Gê¡n°‹È{Œ²`”LjºF3å9Šqdy|- =ý†¤‹¼¥J,Ù8Š‹Ž~B"ßThå/}:$‰8é<~V`…6a(o)hn¦B‰…ŽñD˜EN¨H”G¢m$ú|$r•òدJltÌEúŠJùJ•˜è&‘ǪúîÄeÿ¹}nc3¬žÛØÿM6–Ëc*Ñå.ż𜆵PVËd‘¤šâIfZä?×ÁœÕ© œèÉ”°¬]ð€_­1gÖW=Š©öõÌW¿Ù¾Y¨…úFŠ+™E¹Ñ W2‹r££NCàÔ0“bp£~£ Šq…>m±%yÌ+Åæ.¤ÜèFÊ)XE±%=`Ù›…ÚqÀxЗC5Å—4‰ƒJ|I=/zSÀnXÍR)/º=Å–Ì®‘°›WPþAÔm‰rÝyVCѾCªÈ ¼Â „»!bÇFØ‹<¬‘¨iîÏÃÐc‹=¢*(Ç·ÏýØç~l†Õs?ö“k 9åã‚î\(–;ð¬Y¬kmk›ð¬£¸è‘Ûºæ¤N•E.Àª XÖx·¿YcN¬W=ŠŽöõ¿tˆ äÍ:ÆÙx´xJ¹´1NhÕý¶Œ¥|w•Bµ)yµÑ¯úm…¶­’D®»V”[ï­s,rÜ¡ï69B *9îж-`9¶3ðtNqÐ]ðî‚1º Ü¥B¨JW´wE{×Ê« ¹¥‹¬J tŒÅ=‰âŸƒÆÀÇõ=ПÇSÊcšy‚f^€å…2¯ÊY‡2oàí2ŒÙ}û`œ>ÀÍ—Û6ÐÈå¾èÇï~x÷n~f¡žýñîr” }hš^ Ƙ)re¢¯ ô„ºA¨ <‚Aï`Ô F™pŒ€kD¿í@¿v "Ž/Ï¡Çs™‡ <ï!U”“0YÄ]oÿHÄ[ç9ÈCÑOÔí€~;¤‹øëaÀ) ´ C?á( 7‰xëá(‹0PÎ<àQ)r„G¢m$ÚF¢ßŽ6"'¢’K¯B˜“(´Â˜¢*EÌužÿœç8ïø€‹¢³ù–vµ¦­i;¹Ý´ôiÕüajî0ËÄ–vOµm܆q»¥Ú¬š¶ŠÛ'n¸½áv†ÛnSÔ¸Õ5í·Ün¨6ÂÒ¨ºÿ¯ô>×÷ª®ÇÜü=ÿ¯t¼ªßÿ½Îõ¹ež5¶¥_k©“¹.¶ÌféÂæJÞ̹TI¹BAËÚYƒºÚ"ŸøË[ƒ·mÀ'6˜w´©: À÷ Á· ãD¾ÏFà“F…">µ­‰r?¦Cå댦\?Çš¡¼Yår¨q©yžS³•‘òfPNüÛÖ†ò)ž3Ú¹ _W”¹¢?wŒÇ²á^(òxr9G?^(÷*låß}0fôáyò… øg_àä‡ö~x÷=ýÑ?þöG™?ÊÐ&tM‚%ƒ+åA(F?Á I°Y°ª‘Ë0p~!À5¤šr&<1¹yžË°d‘Ã2G ïü‰²Žö"çhTå§D›çþ«Õsÿ5Ãê¹ÿú¿Éõ¡9ãxC†ÿ!G3d­ø±V†ˆû¯äõÁ{mиö]Êë“d‘§9‚ò`ŒuQ×ïÖà'kÐÜ:©žQäPò5W U`c³Žò5Cöëã½>x¬>ÚÔÌhÓ òû`l SÑߧ!úhyj„öÌBØâÝ8؇Æè qœÈ?Às$*y›ñn|íÛÞ(r&Ú{ôï€þðîØÀ·‰ åùAy“G”·yåm¶·ÈÛŒ¶Ž £cªÈ_àxWäùQrü ¯æÀ»hÑu[T¦ä÷Á¸ À»%`µ^-ÑOKÀr²¹•¼>w)Os2åô©¤œ>xo]h‘Ï']äin XmѶ-ê¶,g¼;ogôí\I¹™1g.€í‚wWôåŠö®éy™1.7ÐÌ ðÜп;puÇØÜQîš{€nx÷(ªÕýyˆœ=^&ÊÕƒ¶Þ€í2oàâ2´óAß>èÛ4ñÅ8|ñî zû¢ÜýøáÝå~À͸ûãÝåþ(@ûà<€G ð Ä|‚¦Aè+}¡nê`”£Ìˆ2#àך´C¿í’DLž;(„Û@´ ž!wEm%tŽPÿ¡è'ý†VàÔÁ$ò/ñÐaÀ9 ´Ná( y‚ÂQáCy‚7ï‘(DÛH´D¿ "‡“’?ïQÀ3 í£Ð>ªZä|î¼:ὓY˜+qLØWËÜ*eOÿÊ–ªv4Ð줥mäv‘ÛDK{Èí`MÛWÓîYÚ<5/‹¥ã6®¦}S횥MãöÌÒ–©v¬¦ û+›ei¯jÚ(nŸþÊ.©vè_Ù K»SÓÎü•ßüïøÌ܆¨{˜Ûÿb3T[¡æÞý+û`i¸]Pí@µÕ?ê~®ó¿¡¹¬°ÒòÒÖrÁcnZmÌAmà_ûåjË¡\m&<¨k y«Ç}jÌ«MåbƒÌ×Gø½þnx  6Â<Ûrý l Ø3)Ïþ¶l;´³G;{ÐѰ hÛsÞ¿5yJ9Õª,r©¡ž#Æàˆ¶ÍÁ«Í§9ÆÖ8¶lÊ x0Æ–è§%à8¡'Àtâ£]k´k*òÛ·~mL"Z[ôßõœÑ§3úrF¿. Ú¹š(o}å8>îÐî”›{pež€áÉÿþÞèÛïÞÀÓxú¢o_ÐÖãôÿ~èÃýúãoüX™Â ä~;Ú¡}Ê‚/¸ñ›8µó¹ëC€cê„äˆüt<ÇY(ð=:ÄŠ|½<ÇYh<Às8`‡cÃïŠ|ô‘¨ XñwàG¡< å·37ÒÜŸçÿEXü š(þ„•Õ¿çï?÷õÿç¾þßÝÏîãÿ£ÿÿÚ¿¯éÛÇÒ\TÖÈ×YMùÈ¡#k¡¼6d´vÐ¥öSÊë•Jù:ñ^pꮣ|€c 8Öàë!âõð^/•r–À³‰£œÀ©>ä¸>`ÔÏÔmëf´i€÷…”Û}6 ñÞí%PžsÐÓz–Ûàj ^cÔm œWQîN¼Û;À¶7QîNð=`;¸àÁ»`; ¼ újùh’#ÔNSÀnš%ò5ìfè»Þ›¡­#t€#Æêˆ¶ŽhÛ:£yåìĘZ‡ÀÁ ¨kŽàØÒ@ù:ÑGKÀu»út»ÓSÊÓ œZá½Ú·ÜÖ锣ãmØm@Ÿ6”ŸmÛ¢n[Àr,gàì ñî‚ñ¸ Üå.€åjo‘“»a¯Þ}0Ï>€ë ¸¾“/hå‹r?¼ûáÝå~ÀÓãðÇ»?ÊýQ€~@¿Œ1ô žS ð B_A€T(Tu0ÆŒ²`”QfD™ã5n;ôÓ.Yä›çy@C@¼‡ÏôÓý´œPŒ-pBA‹PЩàtˆyì;ÿ0Œ' c Cá( >á€޲#åûDŸà‹H”G‚6‘h‰>;blQÞå; åQh…öQ¨ß øtâ6cíÌížÈ*ÓÒfZúñª´´Ü6ZÚEnÑ·fù„ª¶¯¦­S휥ãö­¦m³´eÜ~©¶«¦½ªi£jÚ¦šö¨¦ ú+»cio¸]±´%–¶Ãò\†j/¸àvÛÕXúáê™ Kÿ[ÕíàïسþïÎdXžÅPu´åYŒšû.ªNŽcPs**¹“ÑOmÌsmîwcžê¤ v¨‹ºuQDza<\ÏFäF¶oÙðÁ›õãÛ4ˆ¥ÜÇK#{Ê} ^¶¿Ø¢O[ðecðJcÀ´C¹êÚ’~qH§üÆ›¦€ß4,Çôáhš£MsÈT ôÙp &ÊQ Ø-Û‰?§á²µ=ZC®Z£mkàÒO[ôÕuÚP®aôáŒ]0v7àêøîh㎹÷m<ñ›'ê{aì^x÷FÞx÷î> /pöC}ÀõǸÁ 1þ Ô F»`ŒÇˆvFôiD_í"D®àôR(Ä¢=à„¢¯PŒ'xw^€KðÃøÂ¹ì£]`Dbü‘(ïè#rGvú‹B¨G”ãcíl ^àÿýöÅ-ýä×Gæz£¦oüwÛÿ;ùÅ—ýï¿‹_üwÛûæz8h ˜’ åÄí¤G”Ë> æ¢pª Ùª ¾¨](Ķt@ Ê}‹¶u«n6å¿…œZÇR>{3å³Ç{=ðM=î/G{Êi_@ùìѾ>ê7¬\—ó¿Í”ϸ4Dß +(Ÿ=ø·ú±E[ào[MyìÑcüm‡:v¨o‡öö€g+rîÚ£/ûG"½pw¨ª¤ Þ› ~Œ¥)ê7^M¹_|—òØã½Þ›A:–#ps4 µÓthŽ¶Í „ jœZÀÀº†,‹ÜõÀ»%êµ\'¼;'§BÊYº­ðÞª€rÖ›ð ŸÖgŒ§ hÒïmðÞø¶M§<õ3ptF¿ÎÀÑï.ÀÉ8¹– úvE{WÀwÅ\º¢½ê»aLnxw«êÐý¹ãÝåÀÇõ=¸í©¤¼ôèÏu½0o^ÀÅ+Sä¢÷Æ8¼ÓEž]®F}Ç}û oÐ˸ø¢Ü·B¨W?üí<ý0FŒÃóào*7}€v€œA¯@¼§ à„¶A¨ ‚0Ê‚QfDÇFôi=Œ;åZF?íðBðb¹–Û£Ÿöè·=p ÜPÐ.ó œ:à½Þ;ä5Xa Kè޲pôޱ…cl(‹Nè7ýD¢<m#Ñ6°:b|ÑOGÔïˆöQÀ3 cŠÂ{Úw^€W'Œ£3Ú*¶ÿ÷Wç2jÚ£æÃþ3ÿÕÒÎü•[Óžp[‚±þK¿õßõG¹žµÜ¶ô?ÿ]?¸HÀQm%Ю6èU´oÈðK#¼7-mñ¯-èi ú7£¾~³|{ðhîÞu@›&à‡¦ø­)Æ×sÒ¬@¸Ž #ÆÔ°›cn[€gZ ÿ€e@™Ë/Ú¶BÛV˜÷Öà§Ö¨ßsÜ;Æî¾ôÄ|{bþ½ÀË^x÷>Þx÷.>€XÀ9õ‚ð{0ê£QäoCQŠñ„¢ŸÀ/ ¸„~p/´zþÍýïèsüöãþn~‡ºäçd¾ÃóÏC+åܨÏþÏxžàùÏOx~¶R¾Ë[ý‚çW<¿áùÏ3<àù“ööð? k k :T‚• C¥zäß`m!am!A§JЩÖÖ’ÝoÆÚB‚C am!am!AH0òR º›…µ…„µ…„µ…„µ…„µ…„µ…äLg^±¶ÜðÀ JX_HX_H^x¼éüÖŒ „5†ƒ'A§KÐéR09h‡ë º]ÂZC‚~— ß¥pú^/a½!AÏKXoHÐõä_’i_ò/Aþ%È¿ù— ÿä_êIkmÈ¿ù— ÿä_‚üK©?ù~ ò/Aþ%È¿ù— ÿR<é^È¿ù— ÿä_‚üK ò¯Ü ‡üK ò/Aþ%È¿ù— ÿʽ6È¿ù— ÿä_‚üK ò¯œ†üK ò/Aþ%È¿ù— ÿÊÙ7È¿ù— ÿä_‚üK ò¯œ×€üK ò/Aþ%È¿ù— ÿü[Ÿù— ÿä_‚üK ò/Aþ•=aÈ¿ù— ÿä_‚üK ò¯ìS@þ%È¿ù— ÿä_‚üKÅo†üK ò/Aþ%È¿ù— ÿŠÝ‚üK ò/Aþ%È¿ù— ÿü^½ù— ÿä_‚üK ò/Aþù@ ò/Aþ%È¿ù— ÿä_‚üóû-ä_‚üK ò/Aþ%È¿ùçg±%È¿ù— ÿä_‚üK òÏÏJ ò/Aþ%È¿ù— ÿ䟟…‘ ÿä_‚üK ò/Aþ%È?ßO— ÿä_âòÏÿK¶úψPIûï±{Höt¿1Ý"†€IœŸTî7ÚÓ|ºˆ! Üo4ÑþRÅ0зÞÚƒ 8ÙçÃãè|M±1’.öã•xItF¼‚öä}ÄÝ#%¦ÀSÚ“O§ûŽUbJY‹äXœµ‰ =«lº÷hcqæ&ËâöèshmbCß׉}-~‡IYŸ$P¬lÚ¯÷¡5Ê:qÒ¹Zœ ÕîCPÌ#Í¡˜öt–<™Ö+…´ïB±R…¿Å¿C+çr\hí’.¾G+w$íéžd2ݪߦ•û’ ´·_@>š ùi ä«åˆ;TÊ^¿öûh“Iûþf:ËcEß·céw&ë© ³=öô= ޾ ¬£x•ÓÀžÖDqô`ø&Îc(g[íéüOaÏ¢sì•tÞÕžîs‘_©œ *ß”û]öäkÆÒ7õt:ë^(î})ß× tî=Nø¢ÊYÙq†H¹ËiCë­Xúþ!¾Å+ñª)†‚ö«(†‚Ö\Ét¯3G¬½”ûcVt>KÄMàçá•ߘ%ü\~öVYshÿ1ÎÀ¯£õ–Y|¿Wb'ØÐ}N+Ú“Œ ïÕ´îZGw:«Äyxþ-¢3ÆÜ8Fƒ.Ñ€~£Ño4êÈh+ã7ÆiB?&À1NàÒíº ] 0¾Ð#mcÐ6mcP¯+žn¨Û xtün¨ÓôíŽ9ê\{€ž=Àã=ð[Àèš÷À˜z¢Ïž€Õ°zVOÀ‰î±À½Êz¡¬Êz¡¬Êz£Þè£ÆÓ‡ÿ ¼û ^Ôëƒz}Pïþ n_Ôí‹:}Q§/hÞt臧?ðé|úc<ýKàÒ¸Äa¾â0_P>å€ïÀ8ïŠ%à ü>c„vƒ0ÆAèw0ðŒöƒÑ~0è6m†¿!¨;s3p†‚&CQ(èÏÐ8ø& þ0ô7 4<‡¡Î0”ŒD´Mމ a"úID›Dà1pG‡‘À{$è4²J,Q^Äï£Ðvð…q&‡$Œ? }'¡î”A»1 ×ÔƒzcPo,Æ7ýŒC?ãÐÏ8ô3x^0Î ßà:¿MÄoëDà:¸¦F `LŒIç$ÔŸ„ú“P êOEý©¨?õ§¢þTÐij¥X ¥ŸTà– ÜRÑg*Æ68ϬK£4àž†ò4à”†ò4”ó|Ùéèƒç‹N¯Ë¥¹€ÃóK_’Mþ;ì¿ÿ§žUÁ<>_#[=_#?_#?_#?_#?_#ÿ7kd‰Ë?·¹©Vÿ1€ªèœEœÅw9ݟΰˆK÷Oª)Ö^,ÅÚ3ÓýéXú>W@±€\è¬Z!}Ÿ3Q, º‡bCçÕÖ‰˜{ÊiŠ»—AßêìéÜF¶ˆÏ¡œß0Ò}êBºb¢³ÓßìRé›]5£6Ñw»ŠßaoqŽ-›îVÛÐ7 q'Rù.`OçÙ²èžÅãK¢xA9tÄH1ù²DåLˆÁ"H!Å Š û*7È@wVR)>Ÿ™ÎøPü tŠÓWIçÝ|(V_†8ó¦Ä1P ‘Tº‹]IçߌW(SÜiQâ÷ÙS ¿$ŠãW@÷³­Äm~DùŽ–$Ξñ3*<îr܆ÎÉʼn˜$ü,8󧜗3ˆs,ü~'¿—¢|/Ì¢˜DU—È@ñÿÄYånw¡ˆS¤Ü‘1Ð9òº šM÷eªèÎŒî}'PœÀ,ŠREwÀ"†‰rö.CÄ-Qîšé^¸•¸GÊcžðX‚1>tϦ@œÍSâ§Ø‹ï8ÊñLqN/ƨÇ@êKwpLt^ÏJœcWâ ¦Š3ì½âÄ7 åþ¸ ÝGͱø½Tå\Ž™¾Mf‹Ø‚áÕ_Ð…¾O&‰»ªÊ½qgœŸÕSâÙ‹û6=èÛ¤IœÓë•D1³èÎxµ¸ÛÊÏñtðÎÀ+´‰Ühô~£QGF;¿™0Vú1ÝîvàÒíº ] 8Í@“´AÛ˜jÝ݀G7Àï†:Ý[wÌQàÚôëºõÀo=øÚ4ï1õDŸ=«'`õ¬n|,pî½PÖ e½PÖ«Z¸ö½ÑGoôÑãéÃÿÞ}P¯êõA½>¨÷Bµpýû¢n_Ôé‹:}Aó¾ C?<ýOàÓãé\ú—þÀ%ó‡ù€ò(+– gh= pá÷xŒ-mâçpÔ…ñŽœÑh? ã‚>g¿™h7 esð>”¯™Ñn(èÏÐ8ø&€ÎÃÐß0ÐxàCa9b ’ˆ6‰À101¾D´ID#ÃHÐq$p :Äx_~/â÷Qh;Šãq&‡$ô’Pw ÊÇ ÝÐk êA½1¨7xŽC?ãÐÏ8ô38MA_ã׌kÆ9¸NÀïñÛDà:¸N®)€‘“cè5 õ'¡þ$ÔŸ‚úSQ*êOEý©¨?tžŠ¹H¾©À'¸¥·Tô—бMçtÃxÒPžÜÓPžœÒPž†òtÐ$}¤ÿtô=õæÎ\Ô„zÜ×Uþ{çëOœ/¾Vâk¤¶>ú«µQÍu_ ©ë ¾þQ×>|Íc¹Þák¿Zßü³µåšÆr-£®_j®[ÔõŠºVùŸ®Søú„¯Mj®G,×|ÝQsÍQs­¡®3øúB][¨ëuÍðw[+üÕ:Ár`¹>P×àQe] ® øz@] ðu_¨þ?÷ý¹ßÏ}~îïs_ŸûùÜÇorßþÿJ̹qŽE‰7'âdò»ÙʽTýì ?›¦œG« xA©÷¬Ít'#VÜÉPbI§‹¸@< Äãñx3Êù²l:ïGç’‰óN<~%ã¦Ü|*îÊÕâ> üHlÅ™ âl?O¦ÄS,q*•˜É>â U,úí‚1›ŒâœTwîŽl÷øý¯˜¦™Ç¦ŒCy~ïj1ãPÖ/]¨Ó~€ÛÛdàÞ<b0¦Þ g,êõFûXÀé¾b?ôŒß»â÷x{K28Ƨ^èg Æ€¾@ƒ´Kàå g_Œg0ƲxÔë‡÷€O?<1æxÔòñܯ‰1AF§˜ÿ€9ý'V2úÇ¿#Ð.u“2¯‡òxŽ[¦Ø2LþÉ€;mG¿QÀu8ÚŒžÃs2h4™ÃÄ3å½s$æg<ÚG»Ñ ÅhnÇïXÔÍ}!ÀŸ¹÷À10Ç‚v)aV^Â\LýRPgÆ24ž€:3€Óþ/`NAÝiüoà5 OAŸÓx}ô9 0g ÿŒgðš x³y_O…™âæ+8CûÑ O p‡öÐ~4êBŸ£@ÌÏ”M¼ ø7c‡qOà¿ã™Æý$À\§ ,xŽáx¡ýLÀJá>pXSP6õgÖÌé>à7p§áßà3s7ôÃëò~@¯™6ÿ0æö4Œm‡Éÿå0k&ïãœÉáÆÀ˜‰6sxü>ç‘Ø–†SÑg*êÍä~Ƙé E:à¤cüs¹^¾Oÿ|Ÿ>Ãêù>ýó}úçûôÏ÷éŸïÓÿ]öé¹ÍL·úψqZMçä“(Î]ºG1JÌ'*â”Ü¥xýqç´‚b•ÄÑYúBŠuj¬‘'VÄÅægë•&ö”'KÄíWî–ûPìþu»ß…ÎÝçPÌS+Šm²ŽâFÙÓüu"î)?‹¯ÄïÏ 9wéžyåÉ) øQ‹;69CÊ@ùr2E)%gŽræäˆ³üJCÝ=_GwnžR<ÔTº·YA÷n|,⢚).j,ÝC/¤øÝ."vŠ5›òèXÑ}ôº“žcqÇH1¾3Å=P%Vª ÅKM±•Øÿ6C1™rì˜)€ò$S.€BŠCeCñM*b-ˆ¸ªÊ]v{º¿“@wÚ³è^{5Ýåq¡¼ šâ¿¸ˆŽü«’k ›â´VS\Gåˆù}Lë(öUÅz´¡|?&q÷UÉIEñ\«(¦«r%Тut?¶’îÈÚPœ™Xº+k#îÖ+9 Ò)z¡ÈÄcFòûõJ\¬ŠeEñfðÏO £<6CÜâ±±xütîFó»D_4Æ \z‚~Ñè?ýÊàƒ8Ô•9½0NúìXýÐ6>]¸Ó]Ð6†?x=b8îhƒ:]Q·€w7àÑ }tCîè£;pézö={à=ž¿FŒ¹hÔ“ãX=«'`ÅN,p>½PÖ e½PÖ e½QÖ}ôF}0ž>ü_àÞõú ^Ôëƒz/à鋺}Q÷ÿ°wî±mž×W‡¡†ÙV_;Ž™ÄN´,keE•e;âG™ºP2%ñ.’¢(REJ”(R) Íí‚TÅPÌ+ T-ÐVY³Íqÿˆ&‹ÿÈ@ïOÀkƒNmÑMK¶À]—Bi‹Õòu¿ó½/F³›¬—Ì $€à÷½ïyŸóœóñòžOÒyú°éæoó­R`>𠞸 Àe..òì"ÇnæÝ̻ɥ8òí%×^ƽ¬ó£¿^üúXï#o>òæ»¦Ê ?~®K@pÀ!@NØ ÊCjbøÁ aâZ†° ab~¿C¬Â×9ÂÏk†à7 ‡ar9Œßaò4ŒÏ(Ü¢Œ°vdC•+1rÃw ®1lG™ebÞ£Øn¨R&A|cøÃÏ~Æà5¯$¼ÆÉÏ8×cœ±qÆRŒ¥àš‚kJêò UòL‚1I¾&±ŸÄ~û)ì§±ŸÆ~ûiì§Éó4×"ß|rpËÁ-Gl9bËÃyŽx晟‡û<óópšg~žù9)á£ÿ¾±[g1§úÆÉ~Øü¹ÑÿËíô1¾õúo¯‘¤>ª®¤&’z¨º’úg{í#õŽÔ:RçH]#uŒÔ0?î><¯Ù7k©W¤>‘º¤º‘ä'½ç^©9*õF¥Ö:£R[Tê Þÿ«~¸Yí õ‚Ô Rl¯ vîÁßü¼¥æçÛ3ûšÒ”¬ô]ºÍ¢û¥.)½*S3e©ªŸÒ–ê×gj;n)­)³¦E÷‚oT}ãEIô¥LM©5¥C(·ä¤'§ô¶“^>P¨1®«ž™60mõÿ¢ÒwG4O.) Ƈ,ª¦üªô£—Öæÿ7ª^ñò¤ÒsÓAlV@íøê¬Sº1¢/Ø~MõÞ´o§Eõ“ÿc¶aoãØêPÚ„}ðèË%ãäÓÅœ‹¼%î^ât1æc®—µ.¸¹àag÷Á ¸.Ö±nlûàá’ïZYŽŒAÖ;áÜÏcNAƃÌGÄ/ü’Ä›ä8 vœc71„yNã/Íx¼þ#ä-ÉqŸiÎîM늭k z‹ÖfXVý«¤§øaK•ýšîçjÑcçU¯qS/×¢uÆ.iMú:­5–SZAw]Özc6­7vAi6˜úôUº ëZ·Á¥ûZ]Öºc º×ë’ÒÉ|SC·Uë-k ²M­WߪuÈVt¯«­[ïR½{LM‡MÝ÷ʦ5ŠVTFS›Ì¢õÉrZ£ì²ÒÚÍ-SëÁ¡ûÃ.i͇KJÏÈìU¯uîcºGÖªêãhjÕj-‡î“¾¬ûf­ëÞYµº×£C÷N_Ö—µÎYÖBr(Oé©nj÷²ÖÔ½f´~DZk ]P:¦žo­î¹eÓš9­ï»¦4~M}‰:­õëÐý¸–tO.8|xSë+Y´þ/ó%­?qIiP˜½ºêtïZ—îÕU§úQ>Ó=ºVUOʇ6U_Êž î羦ôF;_­ÙiM香ýjëtÏZ—îñ¾¬úUšú¿Wu.ø­EZ¯ú¾‹VÚ™˜îa)×ò‚îY‹Ÿ~x´s1õÉvVøXñkÓ ¦A® Ælø±§ œvpÚY×κvÖœ©ív'¾Ï²ö,kÏbgÇ®Cðî€Ë9|v·Nøw« ¾žƒàw3ÖEŽºˆ©ŸÝ2V7XÝ`9ÀqÀÝÏæz˜ëa®‡¹^æzñÑ+>àuNžÁ8‡Ý9ìÎaw;'>lû°íæ›>b rûá6Ÿb ž¸ Àe€ëåÚT%Âï/76n®›8rí%×^ÖyYç%>/~½øõ±ÞGÞ|ä͇o?>ü¬÷ƒÀÿù À!€ÿvƒòÀ6ß ¸!lCØŒÂ.Äü~‡X?„¯!r7„Ÿ¡ ]–`7L‡ñ;Lž†ñ…[”ñÖNág„Xbä(†ÿ)ÖÆ¤>f~”u£ðÅn›\Ä7†Ÿ1üŒa;¯1x%á5N~ƹãŒ3–b,×÷¯* O³w-xëT/sS;à‚Ö^ךõ¥ó)š]¢`öüfì#`6­ëÞæ ZëÝ¡õ?/+-¡&æîZWúk'ÉÍÉœÖh¸ªõàh\SšB'd¯Ä˜­V韸¬ú¦Þ{MéÛ‹Æ®hŽIrkƒÒm+~­ä©ýªÒ1hÁÖÆØÃÌuÂÁ&kˆ©Á¥z­þ±Ùd/ÈÃ&ç¼·:eß~vvpÚÉo?ãýørageíƒð²‰-¾{וfƒù³àuâßgc>®…‹¼ñ믇¹Nøxˆ/H¼.bê—sÝØôÈ~†¸+Ì5 ãà VŸiìðIƒéá9FZlÀÀ!"s`Ed/C žÓä--vª÷úüœ‡±s{˜X‚ø&?b â{Ž#ðI²n–5qp;ðŸd¾ˆ¿8¸#œO07·YŽgáw–‰§Èš"vEüƒ=Ží,×c–¹säaBø™ÀO?1Ïb;Ãñ9bnÜpgbê6®—õsØ-ÈCö‹äjÛlÀñ±> ûBÖÈK€óAâ n¨¯ø¶!0CÌ…ˆ!Äü8Cø‚ó×kHö‡¬‰“að‡%/ð&WÃÄ…C”ñ‰Ÿ×b »þcøŒa#¾QæGy-âc»QìpNÀm ?cøÃÏØ†ÚZ$á5NÎÇÉ×ø†Úf¤KÁ5ט`L‚1 Æ$ù˜Ä~ûÉ µ™Æ~ûiì§±Ÿæ:LóÈÁ7·ÜrøÌ[Îy8ÏÏ<ÏóÌÏÃižùyæ Ì—À*Á¿ÿv‹à,b·¸¤t®o¦%ß=æÏ»ù}ÊÎïRvô¯Þï¿Cùÿþý‰ì¿ÿ/¿;y¿ÿÞägõ;“ß—üô¿/‘×°£æý¡Ç†]¼fvác×%õu¶›óÝi­CqU÷/iŠš*­å Zï³Qk-¯h- |ìuh­eüßÞ µ(ˆ³¥NioíkTZÍŒïÃÇþZ¥KÔTÒšË .k]Ðú*MŠ ­¹ŒÏúU­ÑV[¥»,ç×µ6ø‡Î«>÷¦Vc‡Á9¼¬ô)DóÀÔkëÎ¥Ùv'vG´3çG¶´nøwݺ®uÛˆñè’úênÝPZdojV¬jý6ìî†ÃÝ+Zà Û{•†[;ø÷¬i]æz­]{áÔÅÚ{·”vYyêïØÝ¿Vé4·ãûø²Ö(½¦ûó;´fóªÖµ¨ÑÚ.­o±ªúö‹ÆšèŒzðÙß t“¬R/À­™ãæËJw´yS÷ì§ìü¶¤•þ¨èHøƒj[qªVë¨1vJêr~ìÓ¬9Tz¤¢­t¾§W´.òŠÖTÃ_?y;¯‡ÁhÃwÇmøoÃÛ–Ú¦Xyíø1°1àÜ~ìì£ä¸¬vbj—Z æ6}«ŒüØñ€O'öý|ûX<û±õ“?ýØùñáßR[µçÎóp °6(ñâ'Ìú0ÇaÖ‡á&ß9Ç.Aî p@.çc9‡KnQb‹Ê5Â_ìè–Úöʼn!Fü8×)Îú8¸qðâä!F‚<$ˆ'NÞ bLHý}û$þ’¬I²&%51§®«-cš<¥ñ‘?}†Á ˜xeÀÌ€™3f–¹,sYæ²Ìe™Ë2—…wžã<ÇyìòøÈã#YòZ¿~ü"|Šð)‚U«V‰qs%?òEQ]STß®ÔÕõƒÔÕuƒÔ òaS©¤F¨ÔÛkJp#m•}À÷Fpû½cÙ£Vö¦•¿ß¾ݾÿÜ~¹ºw‹ì3·ßG®ÞSnïÙRùû›Ê²úþqõž±²O¬¾ÜWµÜþ·áÛ÷{£z_—Öû¹Ê^îFýX>©÷S®š÷TK±Éƃñ]`îb¼É¥4”L ZÞO{ÀmnTš³Í|æ5Û”Þ¨h‘¶€};}­Z7 Û–z­ef+q4gsIéíˆVÒA^+wÈÞÞ­«¼ý;$ßص•”þŽÁøŽÛdoÀü‘F¥Û ¿l;—óNŽ5¥mØEŽ|®ž¬SÚO];®)­s×Õ[Y4œä§ŸøŒ¹áäiUow|'YÔ-€l<Äè'V?ãmòÝÅy‡|_ÀÇoùõ°~íø;;ã!|Gà1ˆNù&'Nù¾‘ïp¼ÄÁWˆ\ĸF=Ø&‰1‰M û~<ø‰Ég<ö1ðû‰3Æu™d]올Çÿ$Ç~8Lóì]W5^ÖdÁõËç7þóÄRsð œ-êBžø êã,Ã| ß1p ðÉbS€SÞYmQ|àU þ8¹Jsë—{r“LÖ¤„v° ¬Ë€•'ÃXVÖpmòØðY 'yl ð/c¸ ð(‚7¸÷ƒU ªúÄü¹þÞ·úþÔ»½7%ßGÛïIÝjÛ{+ݺUþ®÷V¹u«ýMïþlçoyß»¿åýIîKý4÷¤nõûQÁš_X TѵÜÅõ߇]ŒïfÝîœÒ<Ü þÖìYÖÚ‡ÌÕa_—«ÒAåü6Îo[ÕzˆðÙ‹ß½œï•½Z£ÖF$?·ÃãvÞ‹ûð·oI} 53·¿Nk¢‚³¿û±9ÆrÖÂkÿ qL+íă|ÔƒYm¼Z8¾ƒXî î;VÕ×§hËbüìóàÝt^kh3v˜\–sò}øºÖÔëÎU­«Ý‘V¥“Úg{½ÖÙ&ç¸wq]Z¯j½T8Åî(¹8ʸ…üXˆÍ‚­å‚ÖM…ƒAÜw¯jÝTlîiUÚ©¢íiÛÁxœ:YãØÔª¬9†ïVâ=æ18ÃÏ1æí¬;ÎúãØÇî8y¾Ï¢5UÁhßIN­Œwm*ýQ;cn0NÄ´®)›àÔ´¦4M›Y×,ûZòÕNLÍJË´™µ€Û*Ϭmam‹\;xŸ„‡“‡—\žÂßi|ØñuйSŸf]s§%ÖœÖ1…³]ö¹KJãÛžÖZ¦p8C^&޾í²7—¼À¡ ßVòf%~ l üZå?ë l;ñÁw;ÏíàµËÞ˜8íø²‹_¹žÄlÇokzáÕA~:ðÑ!ûf|t2ÞÉXã(ÜBä0 —nüvã¯¿ÝøtÀÅ!×Nމ¹‡óÎ{±íÅg/9ìÅg/þzñ× ''q;ñéÄÆÉ|¿œÃÉɼ“ù>xöÉ~çcýØöáû8ÀÑ/¾\ørÉ5ßMþÜÄ놻›{ä½7ñ¹Yã§C>Sä3S>'ä3”ùïƒâŠc„÷{”ñIÞË1|äËÀ+%ïù\à8#*çò¬Oá/F γ¬Ip\€_?yð2Øg˜+À« Xð)à³@|…-UÆy}Ü çûù,)ÅÔýäÊ{QîAËß8ÉßÉ=¹_mö!=¯î ËßZ‹ž–¼Gåïcä}jþôŠú?kù{ù?iùÝü†h_ý¬Ú´}ûÌ£ò¥-OþÑVØ8Z¨ëó™068ydã3Ư´¼ü;í¿Q~þ¿ñø¯Y¯^ùáê3¯}b;Rùâ#á¿Î?]2Ž]fù•ôëåìñßûÁ•W¿a´8lÿ”ÿÂ~,ÊOì¿»?søyc³í»¥zã“Æ/kûç¿´ëµcCÖ—ôùÅà÷zúYÃaüá«]¯ÊÓû^yî«áGÁ±™8ÏþÕÓÿ~`¶ÉØt=ÿg^6ö†>ÿ÷Wþó+å+uŸûàG¿ßúõæòuÿì+_ w}nîìw_4k¿¸ø\yòû9ÿ­ý[àÄΑW?ûÒƒmÆ=j½±™XN~êÉcŸæq¥ñ•?빯|qÆð¼qß`yâ6Ç}á«3¬_2×ÿÙüß%š§ll¾xèÜ73~ú…S?úÚžòwq_È,_ü­ÿøæ=Wÿ¥œþóñûé|u«æºgžûÀ³zª’_cóc¨{ìäÊWrM+¿tù­/ÿã×>õ—ŒòÅß¼8ð·gŸ4Gû¾òúo”'^©ÚÇÁ¹lâ˜Ypþ®±§rÝ>½ç£Ÿÿ`Ý[ü?ñhróCOX¿9²Ž<[¾˜q­}ï£_®Ä[Îp•WžúWð6Þ}/ýƒñ\%ŸÆæŸœlyñ{©ò•'~õ¥{¿~oùâàŸ>ú—÷ývyJù±}û¼ºþ_N<øÑ_x‹Ç3óâÇÿÃÿÃÞw€Uu|û²- ‚¥ƒô*" È>v4бaÒ5Fb¢!Ñ(ƨر*5*Öر¢ù[bÔDÑø~³göÂMî{ßýî÷^îûë÷9söÌš5kV›Ù3k)eéô±iO¡ÎKB'2°nhOùb÷Ò>eo¦ò~ 7u àózñõÓ}=6rþ˜Ðò“¬®WE_ÊgR¢OMó¶Ié€Gùc×}Ó—çøœïðÃªÖ Ÿ;ãV–oRøY¡‹”øK›ÒÂÚk€CùCw}Áޤf{ÄšÚ^­fÕJ§ê°ÀÉè+)ø÷ß¸Ä “’Z-¾úºã Ô§üPx¿d·h4Ÿ×GMò7í”N¿^1ýéÎï¤üAƉæM,ÄÀ82Hœ£ð§Xkk»ý1|ɹ¨ˆ7ŒÎñ p(?¬&³3¢Xûn7)9)X*úƳµ§Äõ§ºØ6â’Û÷R2”±÷»Ùœ¯–PþX1÷§é?okõ°ù¨ªwJÂz¾þa×ÃgÞߺ·©Ø=O±ºüå' ?kïEùci…盼ôkGÞlìÑa§T2ûKëØå«¤|÷¢\/‰Aà>ÏOC¤Ô°çn“µœ¿¾¢ü±¨èÝæÙ©r_;ùÈíß|ÀéóyØœO³lù|…Îôq«¤Ôgã#îÊ7Ÿínhóàç±–puU¿J%ËãbÒŸˆb?¿ìT)uØÔàQÒ´£ü2×2ýó2ÍÎo#ãüï¾áó»µ[¹Qþc)¿õ´kL9=Ž:}˲ p(Ÿ¼h©ÐG¬ýìü™¢¬Ë=ü0äü{q™Æse€Êw)W¿j€GùfšÇäት‹¾Hº5 t­jÚ£ —7б¸»»BG)™¶G;Ê'Ãtq‡µ‹ÄZ*RɃyÝg¼±ärº³ë‰ÒGåœß#>îÑâ5‡Ìbï°Nñ»VuáôX1vdªÇ:©Ôèçð’[^ªÜ%† ŸøímOíý¥2ˆ ïO,{ú¡X»fß§ ~¦Î_©[~ü³}RÞ£³¿[îM•¦ÿ|‡Œíäùg[Ëòùß´Ss~äM©´g¿ãž»‡q}oÒà–Á¹ý ?K‰ûüó,ƒsGžñÓwvvjU©Ú£Ú<ѣɡª?T:úÇ« k¸¼Ž=ÓoÑ[‘·ëeË—l_,•ùBü‚ÙÝÚ‚9ƒ§>³—JßOåâc(å½\QÛ~o×»Kåù¿p:ô0ÿ€X»gÉWãÚªüSºà‹Û¶Iž*ÿæýòÞžªÙóT{’Bÿr{²Tžqi×½wó8×ãû§„…?•JWLÌjo~VGÞëŽÏoŸ+ýŸ¥F?¾±¦_—³¥2_ˆË=jõØÝåRi΂WN.¤|£=1¹Ý>–R…úÚ=G}Ê«šý”Ñdö0®÷>[óKÂN©4÷ùõ„h­”/´[ùãìD)­«M£®ŸÖhïMù`í²!­ š\_3øWaùwR©.¡ü“”÷ýz¯¼s¿ŠÎlüi£—‹Œö”r¨ßÄùï¸kÆÚÞ1|©ŸÀé)­³ao‰jOÓ–ýqmBËOòņ+ן}ö órìÕâöÁ ¤ÒÒK>Û¼EÊ[ußåg·*)Tÿ$ õé¼oŠ6ñ®œr›[Áÿvц5k‡ª~^Þ©­6Ÿß£úyé²Ê[Œnv·$ÖJ²Ê ç ŠøuÇ;¹WÇAê¼¥}lèrÓ¯-ÚÓùß mï²óu4øÖÒuR1·G2¥¼¬©e>ûl¤ÔUƒ‡ü>)õéüæÖ”^½ÐãK±öpÞ¤6izUï”ÜØ÷ô\)¨=Œvtžóoÿëä©QÜ^³ðHËRÙÏ)Í—µâ~­ãƒ ¥q'Åæß§&Ž{xãiíýetÞwœ[µl|X]Ô~Sk T”²T¶kâÙc¥ˆ;Š?5kÿàn U=–ºùünéz?À£|PK§"ïbí®€ñV&Je.‡6x”òc&½÷ãV)užì0 o]×êã›ÞRø ò ³Üz§ª7Êj'…7<Ü/â‡n1ãÝ66–ò1*¸ª<¦÷ðùÀ£ü°«õå}CWe¨ëÚ—²Šw6âãz¸uþÝùòq1¹Qø"­ovœá¯_å‹]÷¿ÔJ¬ÝaK4¼Tö¬ÅР˳9_=QöqöÏR:Y%Lxí(?ì–?³2Âé›—ÛhùÄ}ªYö2ÙqÞ‚Óœ¿Þ\nñõ£PiF£7ݺ¬óÊ'{dõÚ˜Ï7œ«ÎW?RõÕÙë6h‡p?À4kù¡ìÑ•ÍSúçýË|œòÏ7[fÍœêÏåeÛ©½ÚFªòz¶E¿œ5'ÚÔÇK壴oÇ:团ø¡ÚûË)?íÍ3ˆxà°šë¿­ ¬7[>ãx:h6‡}ÓT¥{Þ•¾×÷®;Pý§Ð]t£x.å«};æ7Ž·Ó×±7 ¾»Nr|ƒ‡¹>k Ç—ù©©Wµ3—[\ÝïùjQ³u€Gùmÿ”Ûsî‡þÀíÎúÉKÎÚ!‘0нÿ¥ºþ‘ôÅü+ =‡òY‘™ÉP±6ûÌwsµEÒÙÄ#œ‚¹ªžM‘—[&¨Où¨è%YpïkamÚ/à뤳Yi>yógHùíŸõn™ý¹”|%Eó*:í(¨Õ}žãÃç?«!V<Ûùø ‹û.(Õs¹ïþù³&NÑFmÎ=6HÑÏ€KùêàÊêOvèÎé°Ð“¬D8^ú¸•'4æ~Ü€¢;EÜTæç?ڭ唿 \ÿ5Dœó×ü6£.ö•ÎÞ‹è:ôÑØˆ»æ_Ï~éºQÊïçåõú›ru¬êÓ”ŸÛ̵:mé,Ö’Y˜’ÅùˆÉI~—¸5ßÔJéJÿ+(¿Þùf®×ѳbmZÿà´" ©¼å½Nfk ¹¼S¿ŸËÇÖ í)×<÷ÞÎÇñdÚ\*wi:³aÏR¾¬ÆûHé§-¶>ýí(?©l«”ÓsØvLJKådG’Óq²Ì RúÃ/‹žA{ÊGglëUmÈ×ï[ºg멼ÇOœƒsùÆjföKÔ} ¡ÓL ò‡ò‹Ô©UÅI[ôÿŽìØ©ë¨òᯛ¾Ï×÷>ûÚêâ U>ŸAÜÆµ™€Cù£XFÏ\¬%ËŽ#¥ò÷ÿ(]ßæ¶”÷¤-±Üª_=Ã÷IÁ€ñ ÐŽÎñôIÝ'†Øb7eñj +©<ÇÙaùÌ9¥^ªëžôϼ4ë}P{?›Î{qé¼'Áý(»1û T¾,/ÿ\÷¨÷/Ü?¦|% ã>mѺ”¶`îGr*òÃ1k¹CNW&7å¿ÏöÁÄ”u5_ŸdS>8Æö©jý²SÞì°•ôvÅ{'JùkíG·¹˦ó¬ðËç›ïnk‰µ6=$飭Ÿži¶’ÏûƒÔ9Xª¨ëúTÌÒ¹ö\.³)£þjÄ3eüvòFޤOpŠ3]õ#·õ™a³ÂTJêÄÿh ´§óüÝiº›s_súï.RRý{ýÜ&þ×'Ûs>xUaÛn÷ÇšDLíûàQ>8¾w÷Å‹ Þáô“Å`„ªô ¯%¤½-åß0ÉØÝÕH  ëq)•ÂÊ',ÈÌŠë1ƒßsçNÎSçU¿$>YçpJÊ?@î9ªŸ‘6µùôïìæzf%å“ÓÏÛÿ|g±XóËÃŒ3ŽHú¯ˆƒ\Êß¶úà “¥ô˜#áÍ; >åƒò¶D„Xóàóô »Ý8=–þz|ÒšÙRþŽ«à¤aÜþ®¤|pÒ!±¨R:Í÷¹@Ü´ïIúe‡Ž~¶x‡”_üIË„_¢>僓)®’:ðý),BÃ÷|/éWvx|öÒ`®§õë>¹Ó®PµªZIùà$qžk˜?*W/øç' WõƒTÿc%åƒSNÌ훩|PC¶-¿åôÞ2ívûI5\¯\ô_÷°XÕïŒ~|³’òḛ́MÝ;÷âûˆ›ÅïKm-%ýŽUØnÛ%åÓý_Åîªþ‚º¯±’òÃ)²{…ù[^µdΔ ’^wè—Ò¥íž)ËÏñõÍ*:ß§£\/fاñýºÏGw8°OÝÑïê“Õ«ÉÇÒv(ç]šJ©F¤ôÊ*:ÿ§CÝ…ãÍÖ*= zV–xTqù"Û’»—óýƒ’õÚŸ}¤ØUÀ¥üqƾ£«÷ÁŽ£C\>¶îmjêvH•·í첪¿™:þÞWíZr=°ŠòÑ(yH„º®ªµŒHÇw•ý¹Áë{«ü°ý›(²SÏíè*ÊGg.M¨ìò³Ç/úV§U?TŸy~ËÛ;Tû¨à§ÚÇ%Û†WšÎåoå¯ßÀÇ?-k´¡ÛŸ†$©zMŸ2g«]³aÒömí¿aÆí”"W«(•$ûðàO Ä+@NV÷õõè-áò i;ݑ҉5öžv”oJö_lÝíþ8Õ¿¨qô™°øï¿çÁÊÔž/¥íÅò/R:a¯ñùÚû«)•¶|8¥b^Îædã>ÕDç«?*|p䇀ŠBuß!ýû/à©Yå«RÙn"ÖœÛß:ö;NWïÉÇ-lUýí›—‚ƒ6q»¿šòOiíð­e O©úâÛë}G'óyöÚ”tý£š/ïÞ®îÿ¦ï²™fg¶p(¿”…µ›þÊ|¤*ïß~ã7ôêd·›N‹U×QÛ§ÍØú‡BuŸ­«¹¼¯¦üSöå¸+O~Œ¿ýBWòE¯9^a]Ë}&¯‰¸ÏôÇv²m¬-W÷™]â|½šòMÙ÷£íÚÍeµ| óÕ kUœ‡´ýÆÿœõ|¿o5哳d5ø§O¤ðüÝF—`_†t½ñLåÛô}—”ŠE­°)í)¿œa´î½Ÿqº¸.#;’~ÈKy?8¨ó¼ƒ¸›NÛÄFÿ°ÿ¹†òÏY²šŠ:¨òß­7?ÀÛr{2Ô̪bæ:U.w¬]Û!VïÈ÷Ug‘埳?¾ìöÇð¯Ä[—é~¨Í3IÛu‡½ÒŽÅ탿ЕJ©…ý›ÚO@}Ê/åÌ/¾%¿PØËçcÀóŠ«VŸI;¦­¾£OSßë©vx å“òîUPaâ­âÚ>î])飞½w5ËSÚ!oËÇ«ú?MÞf øçhOù¡|ÙÐ¥ê“[½å Ißç˜û ÇI;¢^,X1sˆ”î~öœKŸ1hGç½ü€óÆ53V‰·ÀtfÎã8_ö¶÷9®{ êÝ#dÇ‹¯[×P>(¸LzÒc¦:7ïv%+u] §ë-Nwx‡¿NTù›ù뜿×PþÐ3} ð×̓º]œâÆñ“—‚º¯¹ÒzR·^µo OÎçk)ŸèÈÆWGñ&•ð›­m¯ÍÒŽ’[ž'œ<þ^¿®¥|¡ï¹{Ê‹ôõ|¼Cþ•øÝ1/I?¦²úÉ™UzÇmKⱩû%ét8”_ô#z}ëH–xÓl8õäü’D ¥/eÇGJ{6²i”ù ´£|¢O o{•.7^|;+jÖ~IÿÑ»÷ÝÈ‘vº¬ëþ²q+Î_k)èeuÙN¼qúð½af’~A£í£¦| íŒ?ýÕ¯÷<¤”Ÿ³`ªà§¬¥|¡_à»æìœ.ªyƒ<Ž> îAÌã%¶ª¿´ó`“˜IïRõkÊç{ÉŽàPþГ×Ã9[T½~NmÃa—¸>^®itõ“¯U}¼ó—ÆUX’óu2ÅKñƒ—ñÇ‘-ækjÒÅd¿ßkIÿõþÝŸÌ• Lþ˜ÿIø()­aÀím×¶hï¯có~yá$¿„OÄë?\¥½ÏíÒâþK71’ ˜PÞ¥w>9§ÏY¾ï±ŽÍ?ÃOñ;¯ÌtÍ_ô¤Ÿ?böî«|=»óG÷…wêÄpÙ7”fX“ ©•ÜOYGù ‚í^§ô–ô³W|ÕßIÚyÃŽ¼ àëÈutþ+˜¿¥ÌÿõaC"¶wœ%é©^vê÷ܾ®£ó_1klÊÔo]Õù¼î)oÜ«ëPýŒÂq†¡—¥l½ÜyŽùÑÙ¯TSÝgYGù£‚®·T:\{¾øÑ/?æòIí¢*÷;Ïm´Ûô^G1Œ­gØ>/—¯u”_*è>­xíÔŠ¨ñG7HúEç¤Yws~#¯»ÓÕiGù¡â»nƒ—ÿ…x-;ÈrÁÜ I¿!ËØ!îg>d˜6Nõ'T¹ZOù£âÙ“êîU­T>½6Å•xøÜ¥~œ:žͤOÞ-âûïÔŽð÷zë)¿T¶–,U/_£ûª’þÄŽ¯Vm¸"S!±ã¤4¢ýŽ£å‹J¶_pÍž¼èI–ôú†’Vبú¸à£_>Ù¶ˆ¯×Sþ¨ìò¸Ãå *\ýUxö‰´¿Ô$óñ* èúQÕwÌžq}¹žòM岑?U¥ËÕÓ›Y´žûe§}<Ý`’º^)8ŸY‘ØéwÕbøñùZOù§’í÷]]&;Æê~“þ›™]ý_½ ô}6´hàÈ×Ñë)TÊÛ•ﮎ¾‹íß—ã³Þ¹çàvÛ¤¶¿¬¾Ø›ò`ÏÄ €Cù¥V.WðÓ…;€ß(–L}(,Ö5=û/[¾TÖq9”_*Ér6c·xÕ¨äåãT=¯OZT´ÌhœT0/p—Qê$)ýzój¿+#ÑŽñà —Ô‹žªüP}Áÿk›¹­¹>qñÌ4󤂬7¨9û“9Œ/˜£Œ¿zåЙ<æ~N¿–á]·ªþ_A¡|Ð@ìHý[)Ý®z\uà1~ÉÙÏ뺞©¶œxœï{¤l[ñ¹TpÉí{Ç!“Ä`ÅQösrŸì’‘XmûûR˜IO^“uÿ^*;Ý’_Dð÷T9Œˆ7âüJ+l_UOhQf²Ç}Õ,4Y–ÿ|ñcuŸJ}/Ãøã7»qÓ-LjW ìþ^]ø8¿?ÏÍFµ3…hÔ3e.÷+É1«y™ÜÉ¡|"{eƒÚ‹Wfmñåûç8^^M¦Ÿúâ€ThItMÙÖÞß@ù£ÊùËMÇÍ3Uô ´YÎA{Io¿çå´Ç%ª$/\-¥”=dcÞ í)ŸTù{i_»Àí ¼¡Êñz=ê~Ôí¥bW6ÞÔ“½N4÷ŸÌýÎ ”ªØ9´Ë=‰Ç©®ËÝ6»¡â§#»Sc3=‰ö”oªä×߆âå5¿ÏF¯òó'æŸÿq¦ª¿ k¯}Ð4ÔZìFÏ[Hé÷þtôÙZ®o7P~ª"Ò"Fˆ—'“hçÔñ•r&žß·Îö;‰ÝØþä r¬ËôNzQ~ªbþêeºîÊ·½|^O’ ©K3ä?ýPŸñËÒÔÓ?|©î^–æ•T¾fÂËcfJDXG–4ãû«¿ÈÇ¥¬ÅKw—ÃSŒäûª«÷å¿“=Ï3œé.%Ž¢Vy/FßÓrÿh#ãŸüNç¾l÷HÕ—ØzYáßòÍÑ‹Ç-” ?ù sè1ÕOavˆ¿ŸÙÈøfû-ñЕÝâ¥E¥ÃÞ4"•ïÉ?|ea7Õî¿oú[r/e}‚vŒ?t'Wo¹-^¢~Šê”Ëng©ÜÎU×aŠ_ ú]ŸìZÕ%®G­ª¯/yËD]g”W‡O]Q]©Ú!]ù`&×{t^ù~×FÆ/ºuÙ7®‡¨úç9nVùF*¿“÷Ç´÷$…Ì üýöFÆ;Ú;-ù]+^¼¼aôç¤òûñ- H:Êp\?nd|±-©4ñùAUž/Ò!þà®Í5¯¡Ž’Îíe›f?eð}ÖàõyZxqû¹‰ñ‹ü:'E]‡\”Í¢ÀéqëÚÃ>Úåêþ–NÞNâûñX-Ø0ð¿,#扩¿ùûåúÊU’®Ã´S—Cæs½½‰ñÂn°ÜíÄ‹.{:~ùB*?õ¾Øü‚Š¿úÞ–î3¢ãê·ˆžŽsv[ äR(õ¹~t¦zVÙïVåc›ÿ4}Ó}?N/ÝÉö!RùÞÎãZ¬‘t¿Úâ£_ù>í&6¯lë}†úd»ÏŸë¡û²¡A}6Ÿì\å…Ad6ÏKјݳ¥®Çäã½Ô}'õ}Ã&6ÏCWeôžðxÁV^Xq~—Â,¹º›Ï‡¹Ìˆêz5%N>˜§½¿™Í/1Ó;wˆç鹩¼dû¥Z#ÎßV„±I)t>ÑŽÍ#}ϨòÙyʧ˜Ï•Ë&^ߥÒYñ·R¼=¶¿ùíÙ¼ö‘VŠçÓׯèÙ‡ë¡a—Vºxpü‚>œØÂ[Õ¯)Aç{ßéŸ8lžIé]þ¾è|÷qâ³+3râˆ÷ß´æ\F´cóŒÕܫۖºoÄõÖ]yãTÒ9Nã9]J¡öíÙ¼‡^º³é7ñœ¬®rüï~~7õ?ç§ëß7aÚêïù~ õó‡ñƒO‹Á4KÅsò W>ß=ýèèkNÊg\ïmf|`M,ïSÇŽíÛ¨öízdÖWŸ_çó9áN§m«qyÙÂø Á;×^|«úóçºN¿«ÉÆí¬¼MÜŒ«[Ffôê—ÜïeöAÕ{[˜zsů^“ï‰çè>‹T^.oq|dµ=ïC2» ÚÇ-Ì%îïØŸÄ*znF*?»®rlF‰xÏ6o~àçQ¶0“ùeŠ]ª*,¯=5wp»?Óh檞’Nè¿ vQ°rž홟ù©³ieÆ ±J~íÒžÛwÊWœ Ö[Áu–’ËÅŠþáæoößÛû·ÈÁêùªᇎè÷5ß1h¶ .§ê'*ò¢Ð3™˜öà1“¼eêÿ­XEõ$_ÿ:n9y|¥¤ó%—¨ë,•ß·P>© Úhü2±ÊD>È(é»~?äV󉪿«cû‚=¨Ÿ«ž{PíÁVÊ'Ä ÿC¬”Èm1ÏHrÀÕ‹‡HWó±‹ò>PY?n¥|Q¡n¹s®º¨œ*+zIŸ8ÉnÛÏø>ðV:ÿ>cB®…;ª~n%yÍz0“ÓaÖÊ—æyé’®oã¯ÌB†ó}¦­”ô/å·ª¼W°sFêþáG­ƒzºðyE­øÓKÅʾ-}ŸÄýÀ­”OôòÂG•›Šþeä„à}Ú}íÑãœÏé9`±«¬fü”ñÕ¡/åö¾H¬ §^úlôv¶9fpWÒÅÈøÕ÷)ª=ØJùC?c^òjwSUèKäÜŸÓûÊÁ +%]Ÿ}†ûó]Õu¤º½•ò‰^~ÂÏ饽ï^SÆçùÃ|Ï&ËæIºào6®1åþÛ6Êåt[Ôò}õœÏÏܬøìé’ÎY¾È ¥öLùøÊõ_ÐŽòCyêïä䈨· aûóþ>¯èóAÔ ®­d‚I)òôîn£üQn%oĪó[~vN—Ôˆå’þëu™–·W¨û¥ºf)ÛK{ªûÜê9Òm”OÎÒsªܖ<+–…’~£6 rF¼¤£ò£î;$~|üvÄk´§üp6ѵA©±Ê×åõåu—é•;±Ôýd9Γ¿YJÔB6Êg½µX'ž%·,ò÷c’Ö¦ÿ±oÕñ>é1ãê $õ½{"% +8Ÿn£üQöSƒŽaíųK‰BžÂ÷Î’f½¤Âóò”d߬ñÜ|få‹2öþT¡ËÙžVìïÒGÒ_˜ØæXß êx ÏlÊv°¹,%Í™2>/·±ö~.å‹2vλì•üAÒ_t¸ìy©ðûRÛV·HÉT.PŸòC™ ¾ŸÊ‡eL^Õý—ê½nÁŸ3#`>?ŸËν«r•Kù£ô'r vºXÆü&uåÒG¿íë¢Øyõ< [×¢=å‹ÒjžžPù«¬#,¤5_×VmMwxô¥ºîÓ™“ ©U횪r)Ÿ”’ã×s2ÄÒ¼ßïN]Я÷aäº8 çë°‹’ɲü*¿&3¸ p(Ÿ”zd.žßf´*÷òªiÎ2ÎoåU|ñÍKUÎõ:Z¶ý Õ/Rü’Kù¥äAŸ…–}³ÅÒf#÷XuÆ)\ÜË_ºC•‡©l]¯îÏäQþ9CÏŠgàÍܽ4Qªè.õÝõù,©°à°öËE¥ü¼-eÌé™Gùç õ»U½p†íc)ó^1ñ·ækg¤ñu.}.%²ó±*?çQþ9cB¦ÎQåñ }Ÿªú7ŸžÞtäÛ2>ïò1&O)‘®‡òÏéJùB•xÆnѨŒ”X©"{÷ýòý>áz)òÅiòºÛä’xšîƒK›ÉAž)œž³äƒÂÒtùX„/ÚQþ8Íޜ{l`é|>î¼S£Î?ãûetßBåÏÄ“É %íý|Ê/§j ‡©çjO“Ûq=U½P±SïÓý¿WÈê)v3ñÁº„]ùüäS¾9•U¼³áÕ<•oNÓó˜*_Tåçó-œ“ÉM#ÕßJbr¨òM>å›Sá7/×ô5TýÐS?O8Ø;t;týö±Æ¾ïÄæOY§%.—.“Oùè¤|,ÄQ¶Sþ9~ÂÉÃ~ÏKñä«ÍO;©z¡R¾FÇß/ëQ;tªÞ§ò 8”_ŽÓóì꼞¼8Ø×äÏT¾®Œ±Îíµ»§T@߃ªëøøWÑ‘VOŒ¸}ÝNùäØýÐ7ßk&ž¤ç³¥Êx«IU{T¹RÞ£(~ÇdrªÖä × Û)›åòyßî³Å“Î6¬]½Dªœµdù²«mUû¬¼¯PÆ5…ÞSC{ÊÇȰ Ÿ‹'#’¥Jùd ÕßPöù¾ŸJÏM =åâ¯Ú˜?9!ždûY•KÞ$;ãÉû/‘0•ϧÑû4öù÷Oˆ½öÿk~€úq"þ‰9þ)1ÙþIyþ_Çe#ÿâÙø^Ð8c$¦ÆÓ2Ù@Ëò­V±¸WàÓ†˜‹†€×HS'×úoŒrc”£Üø Ë·Šy5,©÷*‹Å¼Ò°X£(—°\«£ðAÙ¤†åYE¹ æµI5‹ÁŽúM1MkXÒH–kåæ–gµˆåYEß-2Yü«ÿªŠÅ_GÙ,‹Æ&Uc¯gsÔmé€Ê-uub¯ƒ·-P¶¨a9”ð¼UË£¹´},‹XÜuÀ³ý¬JX>%Яu‹¹Žgš™,޺˩¼¬ñ¬†ÅZœ¶ {[àØuÛŽ žÙ ®M&¯.Ç·\Ûl[ݟƶ²Žv÷XluÀ²,û{,_*ppÀs”CX®ÔË uË x9cüÎ3YnÔ,/j"[%çDdùP«X¼ªX–x¸'· sÓÝæZrϤyN=ÜY~Ó\ªb=CX>Ó"–Ãe¯D–»´šæYòF{oÀö<ÐË}ùo”}5,)øÁxú¡o?´÷ž~OX®Qàâ_Âr‹¢€r`·w§1]Hüöö G Ë3Šrà=·¶CáNT|ÊA(Óøûr wôÓ±†Æ——sZS çͦ1îå¸î‘,×è ËôèTÂòŒFÒø.J̬ðx–c8¼µ™omf†Á[›ùO´™Æln‹ xîÌ%ÉYØ2× ’æ.iPÍâNf×ÉQžÈò—~c”£ÜXÇr”–!æÍ¸A>¢Y~r´5v`q"Q6®b¹KÀƒ&Ù,79ä© ÊMrXnr–·óÔô‹Ír— ÜÜå-)fyµ,/9ž™jY ¬j3RËr’?©/8›£nKè–(·,ª/2£N>r–¼˜å)ÜY‚>–Å,æ7àY~VU,O è×:“ÆüÖà™°4Õ,Fd4ÍCh]ÃòÇÓÜ$mA÷¶Z–w¼Šåf9ÇQ¶E¿¶€k ÚØb¬v€k‡ºvÀÑî ‹ Xö€eÿ„å!x£¶Nô儺N9,¯8ÆïœAóŽ)ÇzÄx\ŠX®‘h–gu]Q×uË/ò‚å Ϥq°ÜñÌ´qÎîŸèæ~™,Nn6ËÏÜÚF²|L¨oƒ-ÚØ_;Ô·+ £ž}"‹k‹²à;¢èä”Hó¬:û³xµ £ úvÁ÷vf4>­ks‡'žyoOàëö>¨ãƒqúâ¯/ñÁ3˜X€†Ñ4ÿlÀ=+‘äJ Äß@ò÷Í“DrºUÑÜHr\ZÀA!è3ã ÌNÀ-Œ|Ðß[ÿØà­œaðÖ?þ'úÇ6wÅ,l$‹Å^Årfƒ¯€¶  º³ü~¹,¿Ÿ?¥.çÏF¹1Ê‹Y~?À2D[CômXF£hÞ#Ô5,ãD–ÛÏMP6AÙ$§N^¿™4×¶K=„ÅRG¹éðšå²ÜÛþõbªG³œ~xfŠï¦ønŠ~Ì0F³hšÏ¨sôcŽ~̳9ê¶D?-QnYLcÇZhi.?9–úK]ÎãÚYK<·¬¢ªHÍåWÃ⨃~­³i\Y è¡A] žY;Ð8³$†ºõ=?}&ŸÞpÛF³¸é54W·M,ËÙ‡²-Æ`‹²­Žª7;ÀµË¤y»í€³=`Ù–=Êö(;à¹`9”Pè]'>:ðpB]§\–›ãw,gÔuA?.h낲 Êí44_;àÑx¸¢W̉«ŽåÞ\7ôã†gîxæ⎺î»àz ®èè 8ž€ë :z‚n^({¡ì…²xÉ xxã7oÐÇxy£ì<}ÐÞeÀ÷M|Qö%þ0æÛ}ûO?À÷GÙeàâ1¸Ó|Èrnõ'4?ɽHâ°·}k ÊÅT­Ëù²hLö 3š³<å j7XŽÉ^Ls#‘˜ìÁÀ;ãÎ!ñ4>É‘Hb´‡b ¡À±“Ë :áYÊa±,/ Æ8áÀ!uÓ¬7É¿ú6“V][Il$±um"±‡õm!ðVó¨;Gì±iÿ™\׆&²½"vª®]"6‰Ø¡ÿŠý!6‡Ø›ºv†ØbSˆNªëïÖµõmE];ñW6B± Š]Pì¢û‰Îÿ+}__ϯè÷úz½®>ÿ+=®ÄâVtw]Mt5ÑÓ5TG;°1Cæä\&àe¡šåµÞ žÐ«$—Ecò=£®!ê‚—ŒÀïFñ4gÉf‚y6!>1øªž5Ó±<¨h×|Ø"–æŸ ldŠïfèÓ,–æœ0Ç|·.øÛŠøÀÀÃøYfÓ|$ç—œ§°5è_ƒß5ÀÁýXkÀµ¾mÐ_ÀiSEs‘Ú -ú±%AW»,ž_ÔüíˆþÜYžP—ssB ÁG†¹TœBX^Îjêµø€ŒÛ:˰LP6ì&fÎÍÙå¦xÞ4“åæDûf€× ðš•°SÊA(¡ô‚æ툺«h~¹`À FŸÁè3$„æ¨ )¢ùìCA§PàZMóÒɹ=A—N aÊa(‡Q³8း‰]#:ü«ï{¨ØAeÿ¸¾Í«oëêûÉŠ]û;_¹®ýª¦‚Ø«¿²SÄ>ÕµMõí‘b‡šþÉî(öF±5õmŒbW›òW¶¤þ¹Š¿²нøßí1+6A±u÷NˆPô}ÿWú^ÑóDÇÿ•^¯«ÏÿN×Ýg®¯·£Î÷x.ÆÅ4cCÈQ£h–/í ðM ¿hf„¾ÁÇÆ‘”µLP×mš€šàoSümšÅò9¢~3ümXÍÑO Ào:¦€iб›¡l†çæ(›g³\ŽÀ«%Æc¡¡ù[·[¡m+ȇ%øË u­À VÀÇ x·ÞÔÕQ×ÍðÛ§6€ÓmÚ¢ž ðµA_6øn œlßmìâY®aÔu@ÙøØ€ïˆºŽ€ëøN§3Ú;£OgÐ˲à¸í€K»Dš'ØÏ]Ó ÏÜ€‡;ÊîøÝ¸{€hç‰><Ï p¼0Vo->™Ô=ôA>ÀËí|As?¢‹ _þ€ï¯£.cർöÑ4r{Ô ÄßÀjšë9ˆ|tT;^G< FÝ`Ð.øÍŠþBkh~äNc'àæOs‡£ïpÔG»pÀì¬1ø?ó·‰Q~ësÿï}îÿIþöÿ4_û¿{ºØàŸéoDzq?a9{1ÿ‚ŽŠfðqÌgàÖ2×P‹æ°!pi„r#´mš6B¹1ÊGáƒrcèCÈ!Ú‚&Fg#´5ÂüOcÔ5£l º˜à¹ `™ lXMðA¹ æ» ÊMQnŠúM1¿MѾê7¼f¨ß 忀×}7ÞÍÑw Œ£ðl¶¦ÀÃx˜¢SèJ3<3Ã33´3Ç3s´3Îæ(·„Þh9Šåþ½,ЯøÝe -ðVuôÿ©ÏªèG¢ ýW÷\C}?“豺zKÑUŠž¯4ÄÜ7DFønˆù6^†Dgà·&˜§økŠßMñ»)èj†úf g+ÐÓ¼d…º­1‡­Ñ¯õ5Ä—ÓQ· -žÙàc‹ù²Å|Ùá¹=ž9à¯ñ××ðœ0OΠ¿3h势®¨ëú»–;Êî€íþ<0—àOÀðÅ|û~ü?àóŸñy„ÏcÙ¾ü„ÏÏø<Åç>¿àó+>Ï ä;¹¿áóŸßñy…Ïk|þÀç »„ÿf>³ŸY€Ï,ÀgŒØ¹xøÌ|f>³"@‡ð™Sv>>³ŸY€Ï,Àgà3 Ð/BkvV>³ŸY€pð™øÌ|fÁž½·†Ï,Àxð™è"ºH€Ï,¸±½:øÌ|fÊ]€2à3 ð™?æ[Àgà3 ð™øÌ|f>³Ìî#CŸ Ðg|f>³½&@¯ "»{ŸY€Ï,@O ð™øÌ|f¡;{ ŸY€Ï,@ ð™øÌ|f¡?;ŸYˆÁ~³¿Y€ž '…!ì<ô¥})Ào 3øÍüf!ŽíCÂoà7 Ð¥t©¿Y€ß,Ld~üf~³ý*Àoà7 гô¬|×~³¿Y€ß,Àoà7 нüfù^ô¯¿Y€ß,@ ÐÇøÀw–ï<@þÈ¿ù ÿä_€ü ùÜ-ä_€ü ò/@þÈ¿ù—Ï@þÈ¿ù ÿä_€ü ù=ä_€ü ò/@þÈ¿ù—÷`!ÿä_€ü ò/@þÈ¿¼æ‡ü ò/@þÈ¿ù ÿħ ÿä_€ü ò/@þÈ?¹‡.@þÈ¿ù ÿä_€ü rçN€ü ò/@þÈ¿ù ÿä>ˆù ÿä_€ü ò/@þÉ™dò/@þÈ¿ù ÿä_€ü“3rä_€ü ò/@þ"ÿä_¼Á¿ÇÝù’:~r&ó“5ì G&=ëü§3EuÎ:Ïd¾ò=v~f»€!l:‡ÝŸ×°=—lv‡Þ˜ÝÉfwèØYŽLºó§}j»hÆöª3èÙgÙaï?³©}'ï@ez&󡟰³ÑÌî—°ó!ìn½âK;°»õÙô,´|Æ#„úän|ÎPËöuŠØ;T ó«3é¶ü.Õícg²óÆì®}<;XÌö~ØýÁ öÞµ†ùÚZv/'“ž•–}ncæŒbg¦uô.¾|&ĘÝ/ŒfgC²é¾·|7߀ù)ZvG&óÍ‹è;\ÙwѰ;û±ìÞO6½»OÎ>Ê{Mv‡?–ÝIÌf箫ؙ3ö8š-ÉbçKJØYlcvvRËî,fÐûDd?œÍ–ßkØ™ÊhvN;‹Þ5"ïe¿ÊŒùþÑ,@‹PÅb˜±½®h¶ß•ÅöàKØYcv®;’žY‘÷âãÙùÌlv÷±„î‡Ék-{'Èî>æ0?î»óèÏöÇâé»jÙ·‹¥w›È;kÙÇ«f~ž‹ËÖ 5,~€ÝO“÷è#é»lyíK÷Ôä{‘:C€œ÷”×þlÏu;ã·Œ+í#€oú@ÿèK‹²´Öž¸kѯ¶†oAuź¢ï® WWÀêŠv]Ñ®ÆÒ ýw-ºnÝ¿;àw¼¨ß}ô@ݨÛ8öÀ{`Œ½ð¼úë…þz¡¿^è¯W ÛÖÂo½Q¿7ÆÒp{ã÷ÞhÓãéØ}жàö­û`Þú ^_|ï‹ï}K_Àê[C·ÃúfýP? ¸D¡Mp‰B½(´‰ì(à:ѨqEc<ÑÀ'0¢#ã‰AŸ1@,Ïcð<}Ä íà8c€1 ~A£ ÷@Ðhú„>áÙ <Œ>£ÁóÀ‚~c1žXà‹ßb7õßCÝ¡¨;ý ؆¢¡¨?õ‡ãûp|Žï#ÐvÚŽÀ˜F€^#1ç#Ñ6mãÐ6mãP?õGãïhÀ]M·ôÆ`ÿ‘ÈîkäиäÞ‰™'ÇÈ‹dçÑf²»Í¹4fž|.ÄÑŽfw›³hÌ<ùÝF;—VÌΦ°{Î94nž|†Û˜žãŽò§ïÎäs$ÑìœZ&}wmÌî=Ñx@ò] »ÏMcE`LÑøD }ú€¾#È}i¿´Öž¸kÑ·}wÜ®À¿+úí ZuEýAøôÜ®h× c醱t-ºnÝ¿;àw¼hÓýô@ý¨Ûxö¨a.9ž÷B½Ð_/ô× ýõB‘èo~ëϲfC½±ø>ýŒÆ8ÞC> q<¾÷ɦîz_À‰G?}ñ{_À‰G¹/êõ¬~ [?Àˆ.Q¨3¸E¡]ðZEçhÔ‰FhÐhžEŸhÀ‰ŒŒ'mbÐ_ žÇàƒö0¦Ó€º\š †£Î Àøƒjèb0úŒ>·!€=ýÆb~bW,~‹E›Ø"º¼ŠºCÑßPŒg(úŠúCQ8¾Ç÷áø>mG íŒiè3²š.CâÐ6mãÐ6õGã3 ã Ø£ËÐd Æ3xŽÁ¼ŒŸŒÃüެqÀcÆ6cKœ ÏÀŸøðû„jº”™ˆgñh7 í&aL“@—Iï$Ðbúš‚öSð|*žOÅó©€;0¦‚® À-õ€WB ]úL¼éÑt铌ïÉè3õ“/ðR0i'ÀLîéÀ=ôJǘÒA‹>ÄæƒDþ½wõïï üñ§õ”²–"k(²~"k§zk¦?­•ÀòºèïÖAdý£¬}ȺGYó8ð5Îß­o”µ²®©»¦QÖ*Ê:EY£õ Y“(keýAÖÊšƒ¬7ê®3”5†²¶ k Ș¼žPÖÊú¬È:៶6ø«uøG]Ô](ke ¬ˆÿ¯øþÄï‡~Qý}âë?ŸøøÄ¿'¾=ñë‰OOüy²–'sOø!ÃàŸ;MËbNÞ£gv•ûò=¸hv'8„Ũbq˜uìÌU"‹Ÿ¦añ$riüe«…œõ ±—å3­Å4¹#'Ÿ¯ª¢jœã%±€åXÕ,N‹éÏâ©E²‘U,þM&=7KbLxÊäl‰oFâ ‹À­s$½£Ö9–Þu%÷WEТèÒ ýtÎ$QöIæX|AcvΡ[}$®‹í:HŒ§3êöDß‘ä\ûgSUƒvýñlÆÐ¿÷Ä<öÞý1ŽîTõ.ƒ€soÀ‚¾‡¡þb§1®ah?œø$(¿Gl:~{tBÛ!˜¯!CÀ܆Æxb‹àHŒg$àv!v0GÏ‘x>í‡Î´‰ßÇc<ãÿxÝ>zc‰ŸüÛ}øÿ¸ÿß½÷NlâLƒ›Õ,}$‹¥ü¿Øûð*ªmÿ (["¡‡±Ä@(9sÒOzïí¤Ò’ ‚R‚rE1 b …PìPip 6"JE±  ¼ßš½g&—«ÿïÿÿ¿wßõ=É÷å;sfVßkö¬µÏìµ®þÁ _±/ºQì‹å5–©½\3C/굋:›Q‡~»¨¹l-êЗwªµ™ÂëjÈõ—mÅþ袇‹©¨G_Â÷HË5é,ž¢ž©¨Ï\Ä÷JË5šÍDmú’Ný\¬E]¡2±‡ÃBÔ*á5œåœ¶¢g¹Ø7m!ê •vÚ7ªõv‘÷Z‹þ.e¢¿‹©¨Ç™"j ]µ† ¢f}™¨…g"öRy hÚ{(ײû<ŠDß—:ÑûÅBÔ²ÏuˆêxÍN¹ˆ…¨Id{@Êù>¹–´™¨sï+jÝ—ð1T[OÞËh*öbD¿˜¾O„êNSm¹wŒ•¨õ™"ê•‹ZFbØiûµËx-ª‘/ïÛ¶ûI²y=¹ÇLè3cÊk•ØuðÚù´[®}TÆëúÉ{(MÄo½Øç],öz׉ýÞ&¢¶‰^ÔF*æ{Q¨Ž(íÿ¦:Dr½}[QS¶u¼åj@vÃy7àºAG7_¾_…ê!9ÇpÐ;ÒŠ×Eñôå{Sô¶¢OMߟIµ÷å>5%bŸf#ß«I5­xÝ$gk^ƒ_ÞnÆ{ר_ûUBE~À¹Zˆ:JÐÏÕ’×ã–÷‰[ò½+® gqøWÂS“È0:'@ŽÐK€nIï$è’I8—“À#<’­yú’ ˜Tè” »¤BßTðJ…ͦ¶‹¥_Àåà8xÓ@#òdB‡,Ðȵ,È™‹ã\ç‚O.`rA#4ò`÷ií<å)€¼·6*€.°A:xS|*ÿÝ™ëÞ­Ÿû¿¿~nç<êr¨?ÊŸþ wú‡œ‰ò%Ê•:çH×)yåBJdiòyŽ’ãPnC9Lç<…røÉ?¬½+9Iç\„òÊ=”¼CY{§ü‚r‹Îyå”;t^k§|¡sž@¹|DÎ þl¹ÀŸaÝÝÒäß[Ù¬SÍzk^ƒê`È56Wˆ›¾¢7ÓQO3›× ’ëÎëÅÞå2Ñ›ÉLô%)å}ô¨&õd¢ºŽT»†öÓÊ}˜.ð:7r­L½ØÜÁkaPªáH{r©×Õ˜°©ãûmå>Ke|ï£-_¢“Šx­dÇP^ÝΗ÷²Káuá$È"¦à$Š ¯ðñÝZvtt%Èd~ö+ÎŽâÈàZ.ào‡OÎpl¼òêaû€Ž?=·/ð¥;zÎCogÈå »à8´€5RLaÐ!öŠÝxð3Ò³ö‰hØ*žb§V tŽ­Xè :)8ÎÏ||ϦïøLDÐÏ€LS¡gÎgÐ5zæƒN6ôˇù¸]Æ—óÁ' ô£¡K>Îåc,"!w>l™øh|Æ'…䂬±øL„Üñ€ÆõÀN%ùOߟÛÄÒyàe`ì¡Wä™ üàÅ?ræÌÀK^l2´“ gèåSlA4@/‡è`RÚùÒhèe‚Þ4Œ[üj*hæ@ð;Ó`—LŠ)@*øL½Ò²dÂŽ9t4§Ažð¾ÓÀk`¦ 옉ï™Drä?<¦¯t _Yäµú»»þ~wýýîúûÝõ÷»ëïw×ßÿLëïoÿ»×Þÿ•ïÀÓ³­Ää¯Qÿãû¸È= m§ºQÔÖkµ’ŒZ¿+¹>©o§ú¤–¢l)ïK!ÇczÑÿ°¦SÒ"^Oî c}ÔËEß+ Ñ÷j¯É'×ô·ýÔËEík Ñÿª˜×éëß!úª‡Š±í¢G¬­¨‹].z«[òúØÔK†za©µK‹yM¹¿º%¯›-×ù¿ ê)E?¬fQWÉVôŒ-ýÍDšìNõ•,E?šbQc©]ôµ5MWðZKrím+Ñ{½Xô’mýd-EϬ"Q»Qô­1=Ùõ¢‡V ¯ÓM5jä^Z¢ŸV¨è©UÊûÎRϹF“™¨Óä+zЖòz6ÔkK®j&ê6ùŠ>/m£è½eÊû ص‹ZN%¢Wèi"úqÙŠ>ïE¢oívÞ»Vî;`&ztDŸÉÞË–úÈuŸLy=C¹¯mŠèÝUÎûîÈu LEíUƒ¨¿Z"ê׉~<&¢_¥^ôæ)}â‹DMÖrÑß«÷À•{ÞúòšˆÏó~=r­¨FÑ'ÞJÔd5оe¼Ç—Üû¶TÔŠjõ¢LEèëÊ{}Éu£,D­V_^ÿg’‘×St }¿EÍÖFÑóËTôÅÕóú@Ôÿ‹Âd7ë ›ttà¯øëAG=héÁ[¾zðµ_ÊI¡›'p ƒð€ç6MÈ@'º'@·$ÐNí$œK‚\I Ÿ úÉ Ÿ ¼TøA*”H…MR¡k*ì {Mn:®§ãz:覃F:x¥CöLÀg‚O&C,Ð˽,È›‹ã\ç>@¹å”PüÿW~ïæ_û[›üûûÅE?-ð1Ž9d艹½gЍmmÅûgɵ­Cyq¥W–\‹üÆvðÞÜrEKQÿ²FôMáu©wÕ`É{ÆÊµýkx]¹®5ž;ƒ!Çà«jù_ýÅAwއ^Âw 4cÝí;x¿Ógð/†‘÷z‘ÀÃŽ`kxíPªeO½R­JyÿE c'uðÞNhÛÁ×ôÐKoÉ{Rßä1@_]¼O9ïÑèšÞ€õƒ\ÐÒƒ·'ìä ùÞ‡`A;4\®ó)<²À^ްWìù#AÏhЭHàÁ.aà ØHà„^$d7B#äJ¤Ïr¾äh„F\7‚F<ààÊ{8†¶‘hB^#èGÂÑø…Üñ€ l‰…Üñ8¸4ÀgÃ~¸9cÁ? ògãz6äÊÆ¹|k¾™ ÞÙ ‘ó°{®M…¾SA/ðù 72eƒwxf@¦|’ôs`óÀæÃ?ó!á@Þ|è‘„ïÁ€ Ýœ Pð…l¡8 Z¡À L8ä ¿pŒm8løÀG’q…ã(àFAß(ÈY¢!W pc€ÜàÆ>|cA;–>¡{䌃ýã SÅ9£ÐJ€ #´ Gl—úI Ÿ„sI- <’Á#%¢æ¿‰èi^Ì{ÑRíxµ¯y™¨ýo"z›CŽ~ÛE¿-ÈÝ¿„×§r¿s+Þó|ð@Ö°Ó@½è;˜×yírê=£ö¨…_O,ã½Ã>ƒ¯‹ºæ)¢ÀuQßÁøt½8ü'‚¯¼ $3Ù 2 ³þb ›ãœläY<`?ØÁ´=@Û´=@Û´=@ÓrxB/ày¶äõ‚¬yÐ+°^Ðß úû@>Ðõ/ðòmðó¡ñÁ§ðý€ï|?àE@6?ð Ĺ@œ „ ñ=øàÞAà;Æ>:åQ¾¾Az¢…àZ·à\ÎE@§Рù0œ ù0ú¹Âp- 2E€FäIƒ¼…ø€Þࢮóð®2dÁ&Ѱ½¶O #` c~2ìd8Ø!úÇQþùãMyˆ{%/ü›z‰ÀKNx%¯>› ž)ÀKNÉ;¥Á&i+ 4 a‹4ÀgmèU^iÀË€là‘¹²`Ÿ,Ø. °Y — ØlÐËL6`òÀ+´òhÜ€›w•‡›bÁ†E [{AÞ"È[y‹(>¥˜ˆþî\w¥¼¢s ÊîÌîÌî\Uòƒ?ZƒíœÜíÿöçïÿFñ¯ûþ^̫ĺw¬ßþÓûÑ×*±ì½}g¼Jq*Ũw¾'­ÄžwÆœãK½ˆ')†TÞ¹3nü½xQygÚhòǵJ”XP‰•øÆÝ×äßÞ/ï™ë¢ÿhHÁÛyÏuê×Þp=/ðþìÔ‹ùÑÞ÷ˆúQRœÞí<]·ÁØL¤ç©‘÷¤¦^ì}­y‰à= °6zÞwz‰ROæþ 10@ÓÞ’÷µ·â}âìq¿ë rè ÷\s€OOmœŸœ ›èÀÃà ‹t\@öp wà:Áîßvr§g{9Ÿ"&Ò3 òú€¦ä÷‚>–|9@8?œ÷ÃyÐÓQLc?\÷À¿x{AG|út †Nö çGÏ@èåAç!G(dˆnä …Œ~°Shã|0=™BÁ#8Áô<„Ì éz1øîƒcŠ[“[&à3:Å@ÎD >¥ÅŒà— ¸dàyAžd’zùAV?§‘ô¬À÷4à¥Ã–qô lì‡ïiÐ/2¥AÆBÀ.ª‘O“y/r§C¦tð/\xæábÅ48gŽ8…€-„ÜY ‡qJƸ’|е6HßBÈ–ÿO§çÎ¥á{!dË#Ø x…8.¤càç ‡¬yÀ+„¼…­ž¥i¾*æ¹üw÷=Ù»ýÿÌkbÿÕïÇÖ™ü5ÖÅ:¿{÷½ØÿÜ{±ÿ“Þ‰ýïX 5ùkôÂÜÞ©ÿ|‘è?öOS.@yt·-{Ðr€üWyHãþް¿#h9‚¿ds¢ÜãíD±?`®Š°ô\`ÐL¦Øt]kßDÈÙ Û¹ €5βx p=@ß8 ïú ïq•‡NžÅ²xÏ ´½ ³༠·äö‚ ¼`ØÖ|ÂXÐMƒü1¸æ¾~8ç|?àûß|üÀ'çq.ôq.ÎwT³xµ ¶ ¢è~A°it ^!8‚ó!8‚sQ%ŒtÆ÷0úǹ0èjĹ0è}#`‡ÈA¹dŠ€îàŸŒÏtàGƒn4tˆíhè Œôq0BG#уžFÐ1R~™â€w‡„ñ”[àZS ‡=t ÄxN¼`ý «=ø{àØòÓ<@÷'®‡BÖ`àÓ<ƒs¡°y(|5 òDÑ\úÁ8 ¸Dè“hÆÓ°dð Ã÷dÐM^h&CŸBÐŒÿBð4âz|<ßó—G÷5®çA¶,à¤á{p²€›žiÐ9 ú‚×0È9ªDŒ_ç˜Õ¡SLjcwgL'bÇ'*ñ¡ˆXí3>ÎòØCZ;ëjÉ×Îä½/Í¢þT¶øÍ°„ïw¡z:ÔÏMîuX*z=\çïþS{Ú/ÿx÷{£ç$í“§ÚøT¿ŸjÝËugMx¯8Šoä½òWù¾êÝFï²Qúí‰ö ÓûmÎe|_:õz£ß•è¹E=Îh;Õ•ß[[Á÷ŠÓš Õ…¥ß\ìÊø{jôν·F{Äi-‡žT«•žwô» ý6Dï£9ùžoz¯My/j«Òûbôní¦½Ú´þBuF©. íÕ¦=Ï”²S-yέãû•ã¼>(½sFõBiï5Õ¥½ÖS¼~(ía–ë6ó÷¸å>^&¼v(í¡ž ô{í­–û{ÙòùœÞù¢Ú,´öhçó:-ôþ7½»Nõ^è}(ª¹NõЩ_˜\§¥Œ¯eÑ;Tô%ÕŸšºÿÕvê/VÔÑ‘T×ïóò7⣥G¦™y L•¾˜»Â¦÷©gت“n´²¦¿=¹À·Û ]óŸË¶^Yx'%V½wÖÎïî/zãÈÄÛ„eNO>|)–4Þ ÿ"çbðéù0¯Ý×Èë&5lþ5ãÛƒk¤ãÏ\àøkZÕ_|O_Ý¡Ñy9f_²ê•3¾»pΔeš-t]sô+e>ÒžÔ{gö™s\Á“–Ͼoøgc¤n×·–|™òkZslv—kƒYuä’ïÉG¥'æf:®Ÿ¾‹¥;>ùþsEéR?Žz–2½Ýó?ÞñcØ©á9ïçjƒu·¹þ¬©ú§ëWí]Xõ Ÿ§<8ø4¶G¯l÷;²ÔŸ¯ž½â{M¯J½Lg×ÂIç^þ¤PjˆÛV=38ƒ5íõ}£ÑÔUý¼æ|Å´–|yƒÙ°øM€7ÊðÎ>ùɯü¦È-5¸xã‘ç5=>3ù¹`ßVõíÁÕ+,²Ä¢ð6ÿøÅ2þÎ9f>9cžf‡gNûF=²Vµg³É–ð³·×èŽ zÕLÙ|vÚ$iìÆSO¼Ðñ*‹ÿí¡ûŠêî½2™ÞŽÅû»ÍÎ2‘+ã?èŒcðëçÔñoîm~úfàs¬Zúð䦮U{Ä?5ñõ__îGÛ+¶xûî¿,5<Ô>æd´¥ô¨àßüø`ŒøUVÛt«&ÿiŒ°sÂÈ[9óçžì4.ÜOj=¢ŽÄ' á|¤ú[?¨Ù¼TcÍ“v-ËN¸¢ëàþȪCV}Ï5i$–4ȧ ¦½Zpâ—·lk<ô«¸ÿlÛúÀ#£>´VÎKõï\Hw@ê#ü¶ÙàÜkÌ+UºÏ"džébjYõ“=«fW]Vý:i「AðIuü«¸mÝ2<&üTšTÿÑ4¬Ùo±îȾTVÝ­.lH×¹,1vÉ£‹Oýxî/[ʦxÜq‘T¿ùÛžüEÓ+$!íËÕët§Ä}Põ}ÒN·)óYÂäŸGõJÓŸûÏæ²÷G._~[õŸú·>šiŸ/iãaÒºŒdUç/żtø„z$]{íÇ1õ ÃýhÓ{Q §7¯•ê_Ý›Z?yºf‡ÈÙ=‚WöaUÇím§­–žt“ûÒ ð7às¿ÙXÛ'ôLßDÕë‹Ý’fÄ ÐÆ=zbΤŒU5LrbÊç,yïî÷Íßê>÷—š3{G·÷;-Õç~ÜsLU†Æ?ÖíÀµÌëê8TÕN¼}Ê5i‚2÷×ýýGgÐáþRÓŸ|N¿êÓºŒøÛ•ç•qfÍIËæ-6Fhvݾó ÙGk÷ò±ÏñþEóÃjî/Õ[âÝfä÷–ê÷”í :™•sïM̪֙è.Y®dq÷îê²:s à¹TžtµD7[ª7>Y:»þKå~bÍEÙ©À{ûÙУ–5,ÖðÛ;mY³€Çý¡b${%õë§4;Æç¥Ÿ™®ùÅœ{&=kÎŒ¶‘ɧ¿­Ð>÷‡u…/ç®´zD±ŸT/î3Uÿ…«ëÍ_¹ ÎU+¿:š¿tºôôˆ—~8pîc)îÕ¯«¹|°~DûÉ·{(rHõþ½ æî«éõÆßÎZ\»Âª–¤äÖXîgá~WÜãùàsÿxŸÏ[š\Õ--4ÿ(#=¨Ë¼WŸ†+w’ƒûÉêŸzì•'¤zë gbŸ ùùZóBËÆ£Ò¡gd¥éØ‘§+óð¹¼§³H}Ñ/LªüýW‹æç²æõ}ßþ ó+Ÿÿ™ñᴛDŽê/nàã^¶pÑ¡ˆÛÝ4½ûîxxݹoYs•OX× gÑìþ—ng±Ïv»ä¾ 7ðøø¯²‘ž¿²$E»/‡‘áKÔy½¹¦kJšçûš_ûÏ‚EçKöb>}«cÚO¿ ÕôßÀýãM<] 6ïRô–êGgIþ7Gjóý¦U^QÅgYÕ(»ˆŠ!³¥Éâ~}å|]fß Ãýä ÷My˜U®}¥×²±,¦ñÚ°¢Š‡€ÇÇ}áåS]M›.Õ]rCZ{š5Ÿô ó¹Ñ]8訋ÂSaÉÒÀóqžûኃgæØKõn_îî^¨éwiâþgXeñAzr°ðùû÷JÍÍú‹5|¼_xŒ&vsåù/Õ[ìNõ¼ k1qõzkê½ês¦rÓò‹_ÔÌ—F ùƒßV»ü©a ÃÇ?çñ’ÅsûÅj÷qOÙЬ¥ÏÊß~¾ÐM½ÿ*¯Z¿0ìÈ"uþ äÏAÐáãí£ÈaþÉá×—| ú{ËSΔ›$²ªÇ?Í´û†®iÝÄ>ñž<¾ÊxIõ–ß,4û±¿:Ÿ¶èß{eÖWg4¿£p¥`‹d+-9çò]0 QæõyœÙ4‡Ý^êꟽüfŸŒOÔy¨ÅÇ|[‹)Úý:âJ³ÿk˜7®Þ·S* åô@Gw6{¡ Õ˜~ðÙzÖºo÷¸Öå¬êžùo¼6¿_y¼Ù<÷®¶;5kÏgþ\`-1–Cº×¾Ä*ÏwÝP{ÑŒNÇ´ðäqgK†Txäîï«Ý¯ï¤½ö}i%kIˆ+Õ¾@?zLê^×ønäñëòë'ô?¥ióÔÁ„ªÛsžTæÖ’V~ÙåÙªý*ËûÒ"[7šf$®<76Ê~ÀVà¡øD[ƒv¿^j4Ͼ¢Î›-y7} }4¹JÖÌjº¸]²)›a3ú%[ñÌ«s¿¶=ÙØÊU÷=òÕô¥Z|n8eÓ m|gÅ}š9ºX—Êó x]&äSÇe#÷“U'õ|±ÔQ•OÄŸ¬eÁk–§–E°Ê 1SLbxÜ/Êp1}B¬Ô0ËnÀ^“ó¬å­¦êۑ鬒žvºÛÌ·cè£#oåžÿ{5–ûV¾ÿ¥Ô°þ調ÉZªR^,´HTïw_eÞÝÈÇÍ«—#žòÒkzâ! Nê}ܲç¤~_ïLÍn[mL–ùY.î+¯™ÌðÂGKAûÅÚ·ó®|.5üðlß/Mz³–&ïW<ßÔì´»§ÓÚ¡:5žuu¼ÿ©aªþâ&îåïžÞa»Ò_µÓ¾‡(±c-ŸíÞÚ¾l¶æÖïðZfªÆµzE¯MÜÖS6£QÕkŸ•×Í/{?¬Îƒ-cÖÏŸó¾*r_Šyt¸TÈæ!í3ìÙºéøqÍ.ßÎÖ¦×ôâ~®ú§c—„—îÓtøø‹ùC§ö|ÿþšÔ“¬åúiÊ„4½Z{üŠ©žy*qô&îÕöÄ÷X íÛ"'>¬åækñ?æ~®Ío\æèùÒ¼àqØ€(çɯÕçã¾Ûo ÏXÌZnÏ_ÔñZÀÓ3t?Ý;âžob”½v·‘ b})Í>zÜ6ßÞÛût¢tàñ¼×›—Ìd­}Ë]7¹LÔ}×–Üo¯g«øØ¯ï±î1Úý´™ÿ¦[û3"­’¤™f›÷`­ÏøÕ3¶]wUÜMÇÙýË+:ñãã½YÄ·VWY´F¦æ¹­SºîòzHµ{Å'”X¼Î¼ZÞ´:l<ðùxo±ú±~qäãê}pàìxßA#^Öô÷É}ºßþYº¯ÄøW\ùÕñVäRõ~vÐâÖÍܶìÃ;qÏ»J<(|ÚpáƒçU¿nýôµKOYk÷û¨µÆý^½ßƒ\ÃǮ٣åy›¹lèq¢ç×ÒÁiÛ{ÁõYkÎÜ ¾îg•Ñ›{ízŽÙGýéðÜ/¶’»!ÄPô;XŸ˜¶ûË›š~3\[q1ûŸæ×¡â¹ìÀý\‹Û¶p¿Ù¶îÜ7µ÷UýÞsztNÓoîäoÞx“Uæß|gÙËêóƉÃ÷—ÚŒ}óˆ*_cxö¢Ï·²Ö½=WÄtg•áûr¶90þ÷—ícm1 ¯ê·å{½|•µ.éÚx&r6«œ²êk¶1‚·,]ôà*àqÙþËÆ±‰mSï¯Æ&»öÄ6köxý½ÌÀœí~$/ø¨þ®Î[¸ÿìØ?'¨Æf·*Ç!§^¹gXë[W°ÞºžUÜLÔ)ºÄ|·Y¯ë=¡ xÜ?v¾ðnY“EéМߦé–ÎZß÷ r´Êg§»Zë'1?>?žû‡â¹¨È}¨nÅÃ-sXk͛Ƀ®õ`+ܹK²âú*ÏeA‡ûÇÇ^·±{¾jïÃ<Ïc­{m±|êmV!§O2wyÙ,_q+ï]Y½‘é—J‡å央¬õÈÌþ[–d°Êû¾XYzisUÖ¶òqÝ%âmÅ.‡ÅºWë'A#Ž8ÄkþÆýž¹òç=ðùøîævQõ=|9{ä=‡LµûûÒ’SkW±JÛ÷m÷mWã'7*v=>î{(ÌX²KúˆÒõÏN²Ö_2{¿â[¯³Ý7EÒR¦ãq¾²Þ|>Î{ ³§Ÿ}çIUŸ(Ê­am÷ÅôÙê®ê#æu%>>ï=æUrUŸ!kEÃÚÌ»Pæ¤ÍöŸõri<ù†²¾:ܘUÜ+ÞþNµË‘.z'mkë˜ðéÐ Í®žaùç".±I|•uÐá~ÀŠn}Ýø t$¢Ž ¬mÐŒÃÝb•ãéÑî›m|üY³"†‰Ò‘Ê ?µYƒßÐ/*CÎHšý<ðèfæ/ò/u¾ØÆý¡nè‡_¯ß¾V:òÛµmÎï~ÄÚFÆÛ÷¹î¤=Gt·|yãË꼄»ÿ¾Ÿ–ŸûC]ÈÌå!º½ŸøÂ¶7Õ¼³í‰YûçïPõVâe¾QóˆmÜêÖ²ÁÒÑ¥µ·L«Õy‘µ=à±Îc«¨ÌypÓòKêz›Ÿœ¦DŸûAÝ»¯Ò&óª’ï·M:±çË·tgNÿ}Ù‘ ‰U ™ iO˜÷Q÷w¾gèp¨£è1àºtLÿÈÎ’‘¬ÍÅ«~ô¦í9SÔÕåÓ¨éYßo?}ËkòÜÒžÛ¸?Ô}-;ŒtLÌÇJÜÜpyâ;Q·YÅ"Wv7ue>Êóiÿ½}GÚo¶ª•šDœÐçÕúݺ›¬bÛ•äÂî«´øµ–ÿ^>RSÐJ»îšÞÙ羿Pg`gžŽú"òu=Í[ø¯ºîTËý`¯<]ú©óOÓ;2Auþm›™<î2;©Ý=šíÞ\î ÚÑç·Úó¶–ûÇ^zJÙ¨ÏW©éëàMëi~"òju¾QâáZî{‡¤Çú–š‡Ê *¬ ³íqÖCóÏ1/Îp6I]/uöÐôãþ±÷ï RÇd¾¨>›¶µ}Ægš~¯^Û³nàJí>H«NÍg\”¼»–ûÉÞS/ÐÊ»j¯æ¹'ItÖïúUÂÞRjõ¬ ÙõB‘6´ÊŠ°ŠŽÏ[KŽe«yW@[ÿ•I¿ŒÓ_ÜÁǹAÄá­EéCSî?ÁÚ>¾:ãÖ,K-Ïy"èÛÁŸ} x>ž {†žý¼÷aõyغÆäFùœ©¬­ãüøn«>Òæs!·ŽÏŸŠ•8ôøx7œ™£¯-~VÓ_äwmg¤ÈX‰«$ñžñÀľ긴Ñσ7w°ã½õ]Gxg²Š¡ÍgöóÔ¸Uù@¿v i•¸U=›É€êýqœ¬Ý¿¯ò;.«èE?àÝPïϨ¯ÍÛji zÂ_šŠ?—;P:îÿšûáÛì¸È*¤µŸß|n‹æ¿ó^øEÇüo7­ÿR:^J‰} ;î{~ºåLoå9¯ïNáçä…Zé8ÝfƒV«ãq<Ž™ÿ2²Q³#OÀY¬’·í”ýC:þÝÀϰãÙ+óeü{g^Uu=úœ@È Cæ2ÝÌ7!$! 7BÀADBˆ#Q ¢uHÚ8´¦¶Õh¢V ÁÚ8£'ŒÆ±hÉàœhÿ-¶qF;øÿí³×9ç¾üñ}ïûÞû¿÷éƒï»Üì»÷Z{í½×Úkk¯knÙ÷ðGw?^àèçüú‘ÿ7kvÕ®ó÷êsKóÀå+g?úÅWÎ<ãÒ'ãÜsoÕý?¹Ú—^¤þÒßãèËM/^xù÷˜[¬åa²s.ìœc?+ãûâ,uCéïýÉ/Þ¾ý‹<óÀõ!?}çrö1íר“EgŸ¼RÞºw\}ù¬Œë‹VGù{ŸR{¾ÛOÍm'Ž3n4·XÇ=³ÌúÏŸÿÝÐÀÉøuïüç-%ÎüÑû'TÿñÑóÀÍ—}q^ú ¦ujzy¦yÖ®ËÕp2Žº|Ö–2ç<¼oì8UÂmwË—SÚøÊ2=o˜¶¤,¾+õÇæ–œ3îI »Ó¹7=«ß:°Nøâþ».½sµ»>ëÛû·âéïš¶_ÿáÌ-æ}~ë¬KÏÜ÷êPØîϪ>Ø&ü`m—;|Õ§V¥ù?wæ·;«KkÊÿâöÇ4µÍ1Ï´×ÙÛþGýЯïáÍ/©/›[*»söJw\· ?h>÷÷ϙƮ5ô¯8Ôün¹ÛO…ú"ÙÖ_r/ ¼ðÅò¿:}Q‹¿}ÊÒ—ß^cܹqyc‰K§ÌSöyê9¶ÞÞ&| Ž§:Žøûe=rààPß‚—¸ó‘æ#óÜ›®Q;àdœn‹½+½ËßÿÇ¿«3wœ•´GbÚ÷§¶Ü¯½å춉°ŽGbœõÚ@èÛê$ÂåÃÏþ4gtÜMö¼hž¯–ÿ[\yܦÇO¼šˆ#ý•ÿ¸Š©Î<ðuÐ{WE}mvümOØÀ‡·™¤ýuê/Vî©ú`»ï=yªÎún`UTþ¸ß^cö%Ðó1f‡¬VÛçþÛõøîI²{_ÜóÛÞ–foØêwkòN2/R·tiË]¹Ø®ÇyO¬:zÖ? Çß™ÿ{'X`v¨Y⇣s´‹m}´]óžhµàëð|lu¸Ù›9óáÜöš·•üþŒOœuôÅÚŽ8_¥µÒpäâuueöV¾3yåC+ûžŽåÿxWÖœõðEúÜÞ¯í2îý3×>Ðï̯/KiØwë—Îxõ.½*ÙóõfÇBë Ì9ºàÓûjØòô‹ÈûãïÝ>í"ÿë¿X¹ûÉ/bÍÞ ÷þãÈ”kÍŽë*®ï6/8A€ÞFy‘ó;ã'ù_mÁŸ·-qûñÚ]·›¿Ù玛ZåžYõÁ‘ï«•"ÜâÌ»oŒzª#s`ÀáÛÞÛÖ&\tK­Ã÷ß\4%|vy‘>—È÷¹Æ{u‹kœyâ ëØá<—ŽßÄìjKOsä¯ÃbÇ ó"ûüv‡Èý¼ÒÛQ~ëÚç¸×ÍÞG>÷ØšïÿyÏÞÛQNä[±åõ¿q۪߼ãs³÷ñÕG6Þ³ÛÙßu¼ª.ô–ºvY;ô¸ï²ÔÖÎ8½am?œý@ïÖ›?Z•×lvXÇ”ÅæšVLýçôÅÀëqÞe©í(ÿ¢Ï{Ÿ­XüÚCyf‡uÇ<ÿ•Е¿æNóA÷ìÊOØ×îë’+›ÎXYìÈK_Ú×ý0ãW^ô}š¹Z¯ÿ×|a~0ö«õówøâ-}Ïèèվ¯bþ0õt³ƒÝñ¿ž|Éœð§#/¦æ “]д-Ÿ:ýùÖëU›nÿG—±?sæ³YÝEE­—øgêyÌžOÝsånÍ';¶”]>¶åvÿÛQÿz~Ï-fßÜ‹}Ç¢Dg\×jû9Êk>Ø¡Äu`·ÿm™çíñì;ÁZèš fÜðY„×Ñ·ëôúÀG»5lß¾¤pß¶ƒþ·/,¯7O¿Ìì[’òÍÞùw›%Ÿ-×zƒ?_ö;ëåÃÑwÝš¶×«yÎÿ¶º®ûÙ§fß)*I57ÿã/Þ4ç6s½uL˜Gy=þÛ­cÊþ·ÍI?«oÚ`öv(®­h«¹ù¥§ÄÍùÀ\ÿ³1wß]º‚òzÜ·ýädeYâŒÛÛúœÜì;÷w\Ü^äÌo›Õ1ί;ëÌ S­‰Ùç»5lSÛä 6;zþëºÞëŽßú÷·\ÙãŒßæýmôú©sÎq‰}NÑ­ùáÙFË@ÀáçwæXÍ’Ãå«ëÞÿæ£iaææî¿©›/g¿×Ÿ{‡oGTÕ;õø?óÉ’¿xâa—.±Ssèúõ‚»ƒGüÙáóÍ÷ï;?äÊxgßw‰m'²SóÇ3÷Z†+þwtš}O<áYzÚ=ææžçK"ï˜çœ¯×ûNà4?<ã½ôТ¬^ÿ;[êW_~ÿfß ŸôUû´Ùa±Ñ…æ:Û>m§ÿ§­ë µþw^RÐŒËÛï7^¿#É嵌þáæ:ÛÞh§æƒ§S¶m>?q£#GïˆÝ‹ÃÇ[ýÙÜÁ7Ýy¶EM„[œó­õzÿ >Í']z欇ÞU×-·¼áœÃô}º ¢®l²s.ÚÑøòè­_å¬3¾Û©ù䩾­?¸³icoò®ÏÛ¶9vs}_¼òï¾ÒeÎyu‡ZDÌtÎ6x,ƒ[þÀ«ùå)u]}˯]:O:¹¾ãœÝ}G,C—Îhë`Ñ9wÙ ísí}aÕ»4ÿlU·yÓ+ýïnT†£ôÚy×+ó½²ÙÚVMsìq.ÑçWö¾<šo¶j»Tg=úî»o¹¥ÞìûúòëÓÒÌ‹-ך—ØçG»4ßüÞwA^ÜÈoüïªÓµä¿9ú°OŸÇ¹rúµZ(®tåh—æ£'µ]‰#çï¾³½%þ/uÎyPHò½ë¿\cn¬ÿ÷ü;’Ýþ}N¡­÷viþzâ½>eaç4.»b÷½ÿ2û£F- ª›èÊ5“,åÊÁsbOç=­oû‹´µ šýÅ/\¹jʇfÇèû>{ìP†»îÙ­Çÿ1ºˆzß?hmK.6ûÕ-Ãé/™)ÖÁ¹Ö¾Ø­Ç¹Sµöê§üƒJ<ÿÆ‘»þãͬßZ2«Ûæ¿DËpÓ\wë9I%7­^÷£j¶I-óža]Ì9óTÿ ç½»ó.w=2Aíøý„Èxô¸?ÂàÔ°Ø™×(éÒsâgWß1s©ËÇ_löçD<ëì3.I¾&vßÄtðéqß2Ç2üwÖ׃W© Õ£.}'Z¨.?j»}ó’§_l¨Þ±¿Ãì_¾¥«)êQWÞ¤?m;¶'mŸQSêÊÛÍ÷ë{}W^ä>½ÿœg~ùìŸæ;|¹Y]cÞq½s±Q™íÜ3Í÷ÝfUà¶KGoxÄÑËý½—lßåÒ·í_'¾ãTg~oüý†¿>±ê>ði~hÛùwïÍñ~­6 W¸| í’ÍÍ_U/£éÎýà®W÷jþ¸U-K›8ë­!ZyeG­+ßbÐam;н*råÚïÕ|r‹~ßãRÏ.~úŠÙ¿÷ÉŽÛãÞuôÜ¥Ú4 =šnPR™ýsÿÐñjÁþ¦Ùÿ¶%'ÿø³CY=¯æ“½zü¯µŽ§.uÛ_r+K—õ9‚»Ñë0s£~¯ÍWì úóï^zÏ?¤®-Ên1ûß¼¶úóuîxT”l¼äÕ³ÍKĎѯ}šOÖöZR·ÿ–Ü~õ/÷¸|òç½—ÞVéÒ!üdëó ÃßCìÓü²DŸ¿»íÓvî|§¬ëŒ÷Üu¡Øû;ë9½O ×⿜Sú‡–¿º© b¾CGÿ×—ÎÙð©K§2û ÿ«Ã?ëÔiOBŒ»¾ÙgñÓÉÖÅ´è4ËpÍ‘÷àçÃ’ K]úîN­g äÌëë–^ÆÊeŠkײÏâÿ5ZnÝþ<çÃXš;t„ÝPyc•ٱ󵻟ËÜã®öY|ä¿AÝ"^äö›ºþ;ð+s`Ì[jçcv¼´èÈM·o27èù8‹Ÿü·¼Ÿ‹æ©õ œ½Îˆ|Pݺú[½f9ãw½¶Ïâ#ÿ­oXèþ¡+.Þž=ó4gœFç_ûá yn?˜S~¾ÜÕKÏY|äÿ•yøøK__æÎ7?jU'‚ Œ*^ßðG¯ÙÑ}í¸5·ÿÔ¹ÿhü·uÒäŽósÿøï°ÔS°èú}Wþ»¤Í•çožlzð‘—ÍŽW©—jîþéÀAeyæî£ŸÓür×Y÷ýþ¹—oô)«ÐO\¾>’—Úðà ö¹–«o¥ÎùÆsšOÚd½7Ô<ý¹3çÿ[·¾6Ó>Ç¥œÿûeL=Í êó½óµ{ÜyR™Ç¿ñ³ã­ð¦Ç 5•ýüïÀéqàÚìU'ÆNqôÓ¾ÿqéýkrÇ'ýÆåou¬ðá_û_Yg¸üýœæ‹sJ•…‘;.Í1½ž×dö¸ÓI_~nvüÎ2\ò—Jÿ6–ß»ùªñyî}ðsš?úSåÿ~æ-à/ÿé9ý»íú´åúÏîËvùCÎíœõSæö—=¿»ÿðÜöñÿáëÎ2ûÿ¹®Ö0·Œ´6˜Î{” O¦«á±;Íõú pš/,keþ!½þ4Bn›ýÅÜ_¹v+b7»ÀŸm® diVÜ.÷>¹GóIçÀÜý°¢Hë)ú裂Ëî¿Öµï“Úóiúeþãµý…¹Aß÷¸ãÙ£ùÂ2#¸ñ°;Ï4..oìbòñê˜}¯Ù÷óþãe}¶Aßo¯ùáñZyüCʼôÉŸºüðçmîzñgæ±C²÷e#Šßø]}û¼æ‹'­énœèâó箪HqõÃÛm+ž>ÇÜ"ç)3 §|ôñ¯F˜gîùño ^tûåyÍ¿Wך3÷ú‡d¿àìûǵ}xI‰s¯¼q}I͵ç-NóÁÖ=ON«‹ö­¶6Èný ^ûióds‹eHý³QÛ'i?]êß1ŸzÇâMüÿè[ï»wBýk¾9">TÖø4ƒgƒ—àƒâÏŒ¼ä ìHäaä&³6$^Ƕ BºÅçĬ…®Pd;”üPÒ¡à CÃêù0¦a=⣖üðñS->ÍšÅïø4~ôa¦àÇÔø4‹Ÿf´#ò°ø4kÐqk£j_¶–ïÿv‰]ë¿fû%^¸bÀ®qÑ·ÚÆAËxÒã¡m<åÇwIÜZÒ¨{ð©{"é‰ÍS+ZüþK]qñ~Í %Úã¡%žv%€+~Lh ˆW[!þÌ:Åß?å“hKR«øú÷‰¯ÿVñõï?ÿÀO¢®IÔ5 øIÐ:é ö5“JùTÚ—Jû<Ñ—«+ÀÇ?ô¤‘N£üd¯Ä§mÓñºÒÉO§|z«öÝ’|ud´ø.«•˜´ƒâ·¬JâÑvJ,ZÒÙ-Ú‡—òÞZ‰; Í9àË¡¾às€ÏõHlÙ&S6tùyÐ—ÇØæ“ί“˜±à. lA»Ä…õJ<Ø.=¥QoÑ&‰ýJÛ}Ôí£->èò‘ø®MâCºK¼Ú—KI—öqlÅ~ ùSŽh0¥-Úç±åÿ§Zü§í—¯M:'PN=å´©œz+âÅ_Z§ÄrõˆOäNñ—ƘW6é®JµXñšt,W+Žk­ö}¬b 8r{L×~?uí1=ûýÓ³J×ê¶[¾h«Åo)m &/~ ¦ßƒ™ÃFT‹÷®€ØðM[Ç#±uãñû^¯c댢ÞPä6”üPÒ¡‡%6<¼Ƙ…íߡ䇷ŠïPÊGÔ‹ïЉ; ïnˆ OþòÇÐîHê‹lß¡äGUH|h:à?´SbìxÅwÚ€Ž³S>^bS¶I|xÒã¡m|“ć–øðÔ=a@âÃ×J|xÊÇÆ‹ÿPàc©+Žü8òãZµ¿­xŸÄ†Wz\ ôcBs@\Ò‰MWç°ø ¥-Imâ¾BûŸ·bê”xðÀ§ì—XðÔ5 øI”M%JÙTÚ–JÛ<ñG§;ÀW(´¤©4å'û$f»ø %?òéÔ•N[3â%þ;é ò3ÁŸY'qs %‹ödÁ'YГÕ%ñrHg·ê8ïV|÷:‰‘¹_bºS_ð9Àç2.¹äçŸK}y^‰Õ}yGt|ö|Æ*¿Sâ±S¶@ýM^!´R¶°[â¬SoýPD~m÷y$¦z—ÄQ¯Öq ­ø=u|O+Žz·öïjù(mÒqF•°â¨·ê˜Ê¿žò‹gù'…Î2`Ëšu¬$å“´œzÊiS9õVx´?R7]©Ži^s@ùJUj¤’ñ®¶r¿Ž`ù$%=ô è˜Q§}ô)Ÿ¤Ö­þÆ4 Œ—hûpT:t¸î Ô™¶~ü6}¨ô ­•þ ŒgËähzÎÖq¶~ Ôm¶Î²õ•­«”žRºÉÖIÃõÐð˜%¶žQ:ÅÖ'¶±õ‡ÒJgØ1£•ž -1\ ×jþWs¿šóçx;‰šÓÕ<ƒÄž·íùZÍÓñ¡Õü­qZþŸ»FÒÁÐ|Db–U‹fä#d¿ø_¦_B=|àƒPè Ö~–#Zů2éÑõ|:%î¿G¹V/«¬¸ÝÚod´ú4‹Ïä‰)]ã<S Ïßã›%&/õLßÚqG$.õ&@kB‹ø:æ÷$ú3‰ïäÃ㋼ð¥BK*ux<ÚOqyià˜LÙtÒéí“‹ïÌzíg8 ^É‚æl¾³©ËËï^på€#¼¹Ð“G[ò6éx¹ùôitÔë¥\¡Z[ë¿<ÅäÖ¾Kh×”.í'±´Müƒ¯¬BÇù-W²êÕþ3U¬­JÚ9]ÉÞ±³¤ï÷úöØYÒ÷s/ã íV<…Zñ¯­ÁäÃsÁíZœGÔŠoü‰Iz$eGBWˆWâµJüHŸÛhsK(ù¡õ|èÃÐ#?ü0p‡Q>œºÂÉo¾>ðk鈉}Dzt—øÆ~ é1ä¡]‘ÔI;"Õš–ü¨*ñ­QGÄ7lS€|ÒÑð[4}/±×»$†¤Wâ$µK IÒãiÛø–a1$©{ÂA‰!Y'1$¡5ÚcÉ>|qäÇ‘×.ñ#©;ž~‹§l?$Ð ­âŸ90‘y"±YûÆWÓ\å“ '©SOyÉUÚ7¾Š¯”|D|âŸ2(ñ"©k𓀟D~*}“JùÔN‰ïNø<ôM}•|åÓTšò“+bER_z•ĉTiêÈðˆ/|Ò”Ï_&c‘I:üY´) ²è¯,pf‡i¿÷ÙmÚo¬—ò^Ê{¡Ù ¾Ò9àÏ¡=9ÀçBo.øsÉÏ¥ó '±ËëÑSv>ùù´'ŸöÐ_à*`ì ©§²…”-¤lyEÔ[DÛŠÈ/—º|Ôå#¿˜üâZ¯Rùâ/¦-%À—P¾d¿ŽueÅ©$=…ºJÁ_ ]¥í:^–Šq`ùä§]SÁ]F?•µhüåJ_RO9eËJœJÒà™ži>SKùá·üîCG%°•:6§åwŸôtpÍ€Žõ:f–ŠKiͳê_ ®ý6=ûm:öÛôk ^U:UéÓ@=¨;•¾´õäÑbù)=¨ôŸÒsºLé±@¦ô—­·õ•ÒUºIé$¥‹”Rú'P÷(cë¥W”> Ô%öz×Öñø”ÎPúâ¶æµuA /õás¿š÷í¹Þžã‡¯çô£Íç>×çðáó¶'HóücŹW ê æ÷àz>ó·yX,Ê ˆcÂߣ*☨õt·Äøå·0òÃé—ðAícÝŠSÂXŒ¦Ì~„†Hþ[Åù‰æ;š~Žñ踼*¯Š)bÅ>¶AbêR\§Ž-M u:~n"r¨ÖÍ´+ \É›´/vÅŽ)ÈÑ$òR¡7µKb{P¿‡ö¥µèøéàK?¨ãtdú$nm—ŽUkŨ¥²ù;z¼Ôç¥lår¨#™Î£Î<ú,_­¡¡™(G!¿ªy„tå|Àø¨ÛÎb~+®ÓqnKtÌ’)À–RWi§Ä´¦ ™.kÕ±:ÊÁWN^ùa‰Ñ® `¦UHÌ`+)[9¨}ËÏçŒ'OÅÞ8¶Þ:¶Þn :¶Þþ®­·½2.ÔmÅƒÇ úÙ ®`ò‚$^;²9‚¼-ó"^ǽ°âµƒ7Ù ¡lH»Ä¢ªrãaŒb~ %?t­Äk ˆ×NŸ‡Q>ÜÃøpÒá¤#(AùðEЮÑ^‰¹ü˜h‰EEþÊaΊ¬ø j}M~²Õ,ñ¨hÞXÒc›%éhÒÑðK4uÅx%U÷QâQ”˜íõ³}PâQ5Äl÷IÌvðM¤|,éXh‹í”xT¤ã(×¥ãQÅC{<¸âÁ•àÑ1¬XT¤é×Dú9‘üDhO¢¯’ 5©IÇiO&L:™t2éÒ)Ì)ÔrXâOÕK|ö=-¦R_ꦀøì´ÏC_xÇ4èI>òiûõÔ9¹JâOQ>=Zâ²7I\ö×ödtëé5³BbN©4ø³hSùY­kŠ6e“ΦÙô½ô—´|9Àç€?ü9Àç’Î¥|.mÊ%Gýyð]ôåÓÞ|òó)ŸOº€º ¨»€º ¡µ²…”-$¯ˆ¼"ú¢ˆtùEàòë£í>è*&¿¸^â_õèi¿„¶—P¾dPâÃ×ëxWSÀWJ;J¡«´KǦU1Z¬øW´c*é2ú±¬MLJ/‡Žrh,§lù‰yE½à™ÓªtÜZ¾¼•Œy%°•u\~:tM'=:f¬Õ1\T xk>Wÿ”®µãdÛkk[‡*½©˜OéI[Úºp¸ Ô{ÃuÞ·é:¥ç”n [é°@ýeë.¥¯l]¥ô’Ò?Jרkèákç@Ý¡ôƒ­c ®›ωÕüþmóºšÓÕ|~´¸–óµ=GÛœlÏÅöÚØžcU_ªµï ÄYflG´JÌ>Æ.„Ï(ø ž g\Ã×p~‹€/#ëÑÀn’Ø2Œ÷x'²JÇ•‰Wýþ±µ[¾F&¢IGÓöàÇUéØy¼7¯SâæÁ»)3‘úb©?üqЯÆS>¾K"/¦Ñ1ì<àòP‡ö¤‘ž&ñéhC†ZËBk&4dQWe²Á‘ÍïÙÔá¥ÞàsÛ5«åÑù¤óÕz–þ+¦øà ¡¿p@wŸ2>êô¯šK¨¯Øúg eJ©c*ôOíÔlZ½eí:ŽNy·,Ó¨£Ò#±â Û’õ¯"àï*·  ÿ³ëÖÀ5ë÷q½úß±V=Ú:õÿõõû¶>ý®­M«¥OÁmx‚t XúРl0¼¼V⤒7‚¼ä ž‘¤GÂC#éÏ‘Ì !ÈAeCø{8Gµh±­ˆs­ãÜ[ñQéÇ0æ¯pêWó4ép`#ªøŽ`"È üèf‰ÊÜ<ø1äéÖÓC$éHêŠ$? 𢉍V‰ Çœ2–ôØV‰FýѤ£Á îpÇ€;¦GO+ãÀ5Žô¸®€©´q(¥®2ʔў².KÒŠËܪcHª¸×Óø®$¿²Sb.«opÍ׌&áõï¿ëÜWÍßÇ5ô±3ߣ¯©¿Këéî ÿ»ëéÿµtôy†Ob߶똶ÁU|Ç`êAÞòF7‚yq$é‘ðÈHú"9¡l<Bß"= þE[Bù;”¼Pµ¶†¾0ê #F?…QO8ùáðC8éÊGA½àŽ ´š÷óÑ%®.ùcÈþHæˆHðEÒ†Hò£ë¨z>ÐE}c™'ÆB÷XòÇÒŽhÒѤ£»uìÝpÇ€;\ãÂ$¯ÒÝzÊmãiçxÚ5žòû ´sù)?‘ü‰¤'’Ž% ¾XàcÁmqäÇAkuÇC{<´ÅÓ®xp%Ї À&›@Ÿ&’N¤®DhO„ö$Ê'Q> |IÀ'“N†¾dÒÉàO!BùðM¢®IÔ5‰ô$¥ƒ€Oå·TʧR>•òèóö€ßsXÇÇM£/Ò(ŸFùÉà›L}“éÉÔ—N:òéÀ§CcðÔ‘ÁXeŸ þLÚ›I~&é,ú&‹öd? üÙ:s¶ZK륬—¾ð‚+‡t¸s€Í¶\på’Ÿ |.ùyŒS´ä©u6ãšO:ŸqÏgóÁ]ÐYÝ…ÐYHÙBê-$¯ˆz‹è‡"ÊÛnuùÈ÷‘_L~1õCsI¼Ž¿\Ò©§ù)àì`§P¶”²¥Ð\º_âr“?•6M…Æ2`Ë([ÆßåÔQNå´§œ²¤+HW€§ú§Aû4þžÆß•Œs%p•ôK%|0ôtÒÓ»´™A3¨cFV)–¾Pÿl=¨7m„í5tàÚ×^óí,vøºWéo[תy^ÍíöÜh««æßáëÔ£­Gíùpø¼w´ùîeÍiÏeŒŸŠñn¨u&´Sv‡0!ôé(¾G1Ö¡ðK4mЦ_cèƒñaºë&Ðßã ü>‘²ù;–òqàŠW|ƒ^j$ñw22“L^ ß)ðP cŸ ^íL~:¿§ƒ'>È í_ôå“Ëøä’WÀ˜À ðB!p…ð@ <\B=¥ÐRJ½¥”Ÿ ìÔ÷»ŒºÊø»à«€—+hó4¾§ñ]I~%´Lç{:ß3ªuœøc럠ïÇúç»v¦ØôÝ;WT¶*‡ø|dÍ'Aóù„ϧ|>ãó9Ÿ/ø|d½gúŠÏ×|þÁçŸ|þÅçß|¾‘wXüg0gÌÙ{ƒ½ÁÞÇVæpƒ9Ü`ïc°÷1Øûì}Œ±b+ÅÜn°÷1XˆLTs¼ÁÞLj“;y&%ƒýÁœo°ÿ1ØÿìŒT9eÿc ÿòo ÿòo ÿF¶ìÍ‘ù7ù7ù7Šd-ˆüÈ¿üÈ¿üÈ¿Q®çZù7ù7ù7Ã/o½‘ù7ù7ù7æË5äß@þ äß@þ äß@þÅbß‹üÈ¿üÈ¿üÈ¿±\ìù7ù7ù7VÊ™0òo ÿòo ÿòo ÿÆ*Yç"ÿòo ÿòo ÿòo ÿÖ;uäß@þ äß@þ äß@þ äßz_‡üÈ¿üÈ¿üÈ¿ü[oCù7ù7ù7Ëvù7ù7ù7ù·ìäù7ù7ù7Ëù7ù7ù7ù·ÎÑù7ù7ù7küÈ¿üÈ¿üÈ¿ü«7úòo ÿòo ÿòo ÿò¯ÞÈ¿üÈ¿üÈ¿üÈ¿zc ÿòo ÿòo ÿòo ÿÊnÛ@þ äß@þ äß@þ äß@þ• üÈ¿üÈ¿üÈ¿ü+;ù7ù7ù7ùW÷¬òo ÿòo ÿòo ÿ†’õ¯!èûë› pÿS?ì>¡^ß [ï&½ò†¨MüxdÔ,þ ‚ÄÎÅÞ–ûã±s5Sµø(è–{d¯Ø–Û{¡x±-o‘½P´Ø»4É~hPüTÉ=CkÀ[J¯>{³l_zÄÖ¼JöE=¶/›ÄÖ|Pìͽr÷Ь×i–L…ì‘ÚäŒ.HÞ6ÕÊD»ì•‚Än³Aî¹»î#jµ§óÞÒ+w›äÍ倨wVÈ»ËV½&´î'¼òNªEÛÉX>¢åž¢AöR]òÓ#o¨šõ;Lë¾"LöUuroѪ÷WÊ^ݺ¿ˆ—;Œ:¹Çh•»ŒAÙsyåN£Aî5Úõ݆å!Hü#T‰]{“ØÚtË[­ ñ—P%ç›ôݽõ®sPlÞ=r—_/¶8mú]—º×·ömÑr/R«íU­û‘6m¯îI¬5qìé*äþ­Øîtj[µ^¶ì[+Än~“ìõºåh¼­ Mr¯Ò¥í{|öz»Jö~Mr·Ò¥íb­7¢kÅC§ÞûYvôÑòF´Vö}Íb_°_l{¢åíY~fݳ´iÛë¥Ile»´ý¼µ÷ ;žj±›mûùýÚ¦g¦Úêeõ,hžE›gøýÐáÆß¬¼TñÆP–Þà›MÞlòfƒo6xfÊrœs gý6‡ü9Ð8ø9Ð5—ßæÒî¹ÀÌf.øŽ‡Æã©{m›Gó¨{eæ32óÀ;ŸvÍ'=æCw5}QMùjÊ/¤} ¡g!ô,¤¾…à^ÜBp×@G ýR 5Ô_| ð'¿ˆò‹(¿ˆò‹(¿ˆò‹¡e1øjù®Ußà­¥\-åj)WK¹%|/Qßõöá$h?‰6ŸD¿-¥ÜRêZJþRè^ÊœL]'ƒkíZFþ2Ú± :–AÇ)ðÙ)ôùrò—Ó–å”YN[—Cëò½ ©£Ü p¬ ½+(³‚2+èŸÐñòN%ïTpœJ[Oÿ©à?ØÓô–åtpŸÜéôýéÐv:ürøÏ ÿ è[ +_Ie+é«zÊÔC=¸Ï$ïLÚv&}p&¸Ï¦ÜÙÔs¶ú›zÎÐ[Ÿs¨ã\`Îæ<`Îæ<`ΣÜyÐÙ\p ¤€kÐۣ󻀾¸:VÓ¾ÕаV³šß/çEà\Î5à\Î5´} ô¯cŒÖ³˜uÀ¬f0ëŽè­T#¸iw#ín¤Mຠ:.k ÐÑÇìÆ¿Ÿ{hÆüØþ9èØþùØþùØþùØþù»·VºwmÐ÷×çPg€o¿†£ÜvÊ›lŸ¼Én¿C^ñï×"~‡ÂÄ&o“öïgÝ!úì¯ÅÇ_­øêÛ<Ÿ¼Al•»¼El•;‰x±ÏnÖ¾þ,½hñç±V¿Q´Þi‡Ü+¶ {;Ò*6,Ñb³×ð†Ñ'ö,-Ú÷Ÿe÷]%¾ÿÚå~1LÞl×i;ëÝö€Ü3Vh?!–_·Ü7zÄ–¯5à ·Olašô;në%ZÞr¯ŸEûÅNÆ'ï¹[µ}Ÿå»(^î ׊ÀnyÛí xÛÝ-÷‘Ñâ+°^ÛØXï¼»åeø5ª?%Mòî²[Þ^FËûËZñuÔ¢mrÔ}åó(LüUË•fmß®î{¬·áaâ©Zî5›Än°Gî7ÃÄ/RµøBi„ÝÚ–ÐòE/6>uò¶¥E|vk›ËGa˜ø)¬{ÃMbsØ%v‡Aòþ¥JÞÀ4‰ÿÂyw&oÏ«ÅÏ4΋×þW,»û ñcX­ï«¬7èíÚß’zcùcÙ$>–ºô=–õ&&^ûf±lò[´=âÜzíkɲϗ·èõò½EÞ¤÷ˆQ›¾ßší•·¦jX/6ˆµò¦EÞh{Ä™´g!åfѦYÀ͢ͳ€™~?tøñã‡N?0~ú¯Š²³Á7›¼ÙäÍßløo6¸Ž#oí^ |=e6@S´,§?SßRúr4ÎUmf.øŽ§þyÔ=¶Í£ÎyÀΣoæQç4ÌO5}QMùjòÒ¾…ÔµzBÏBp/÷Bp×Ð/5ô ýTl °gñû‰À/¢ü"Ê/¢ü"Ê/â÷Åв|µ|תoðÖR®–rµ”«¥Ü¾—¨oÊŸD{O‚ö“è³¥”¹˜¥Ôµt¿ÞJœLzí¹€~­Sm¡Ì2Ú± Z–AË)ôË)ä-§ÜrÚ²œ²Ëiërúh9ýVþ:Ê­€Þ´weVPfý³:þ“½3ŽªJþPÑ £AŒ¬AÁ•ˆh“×ٲウïIgï(hTÔ¸ÇeQ—QHDp"\0 ƒ ‚‚DQÄmønßûºûÇaΙÿÿ?sÆŸçäôëw«¾Uuo½÷ª.ýªÒKg,ŒtlM?ütx3øË„?ìLø2™ûLtËÄ_²ÀÏê•)L6:dß ÙÌ• ú[ÀÎa,‡õÌarÀ΃.9yâ9y`ä##ðÀSO!<…ðBWˆžÅðÃWÌ÷bøŠá+¯¾Ò25*þ2t(C‡2xÊ8_f˜•`V‚Y f%¶W¢5kT O5<ÕðTÃS½S¦Rõ`׃]ÝõØ]Mõ`5¡Güöø[ü;U_ìg}1#ù“È—\ó"×\?°ç@"ï9ÈmDN#ò#_9ŠÈODnbä%"¹þbÏ-Dìfä"0ò‘+ˆñΫøÍšøÝ–¨éf7ÂMÖ¦ïËŠwD 8ñûfû{·n²ž…½²»¬U¡ñg‚Æ$ž÷ž²þ±¨8ßñ/œïá̱ Þh#!ž·ðFºË[Y8v‡£_ð1y;‹CÏ8xâ°+ûÂųœñ$ñ,/š±hÆ Kà{ ˜fæÈ¼HnÅ%aWóžÄX4flJc,M<±5 ¼4tLƒ¯–ù¯eNËù^Ž~ðÖBWËú•3ž+Î }À¯BïFÖ²j+E+6Z}äv\6Õq®Ùð7"ËŠ®`4"³±OnÕeoƒÖ†ýØaÁß, àSEèQ Ocy` ùÈ´`)>X¬Ž л¾"0óй«MÞÒ‹Á®«T`b·Ú:xKÇVÎU€YŒž6¡;Lè[Á:ÔÁcåØ^)tuÌ ^+sXÁqó`ƒ¾BØmø®ûê8ge^lBkiƒßV)óP†-60ê8®ÃÆ:ìªóŸMð@ß„ÍMÐ5aO2DŽlÿwjßúϹo}ê·_§ö®¹îOí]»Ú»þß¾wýß´_-ž™6·?oýÐuªFþäzÑLT½h:Ôûã&UÓ¨CÕõq©“ßëÒ¦EÕÉwwù}|‡ª#êåRG´S½Gé£j-Tõò'«ÚF‹T½|/õ^åw¬ê‘Ó¢Þ9íT½r¬½nª›êÃcR}šU?žeòýûû=-êÝùª~“›ª‘è£úØTßY/U„оðúb“6ù¡³6ûï¾&æ  =5x4|Ä­?xþŒù3æž?þçNc؈Í3‘ˆ>è„~!èn3Áž öL°g‚‰ÜHt‰dž"áì•¡üÑÐGC }4ôÑÐÇ K x±|ÆŠOpc¡‹….ºXèâøŒã3[KÑ;žy‹G©xæ-ºd%0žÀ$‚“ n4Iódz„I½2½H†?Þd¥`K ¶§`k s–¿ÙC¦©Øj?šThR™ŸÔ™Ž¤cK:éØšŽŒtðÓáÍà/þL°3á«À+>’)rQ椎ÉÍÓ-;e“ƒN9Œåp¾Ú<°óÀÎúƒn>¸ùàì”iN!<…ðBSŒ­…èV _1|Åâ|Åð•ÀW_)ö—q”aW:”¡O«á©†§žjxªá©«ìz°ë±µ½ë±©¬&ôh‚_İö®¹ò©úÀÿ]õ¼ËȹŒ\ËȳŒKåWŽÜÊÈ©\ó(cÜÈŸDî$ò%#O2ò#‘ùkþs²|ÿuä9"ÇqÍiðã“æ1'æ/"o1ò‘§XÜd~bä""ÿ0ró‘sˆªµEÖå=/j–õIì5™zU}‹zOuª¹¿P½+û6Šú>¾-²¶¥ýýÑY+HÔÏóîU}”Üå¶ž¨é!úŠz&>Må;ÌâÀg@¯¬?$úC‰š!öš•|×Àò'ßuö](ûDE3 i¿¬o±v¡W8óŽÞáÅòï@·ˆxœ@ðÑ'¼UöH 3’¹ˆä3ìpä„ÂŽ áËd¯ÄPæ%ûc˜Ãt Gïh°Â9N7¬8!]â„Ü>¹Í|'€›F$ÇÉŒû‚›Œ®ÉÐıž è`FŽ™ófdøƒ‡<óIðær>ý3 )à3yiœKC‡ >3àÉ帀ódXà«ÂŽ*ì¨b=,èUf9ºWA[m­8ž¹ÌctµœÏôÐÔr®š"hŠÀ)BF#óÑ})óVŠV>­È«×ŠmµØR‹MVä×1Öo#Yð7bs#cYœ³Ácƒ¾Š·`¿[ À/Bv)k—~¸EðpX˜ÛRdW€]Àq˜¥ð™ÇüTÀc£¹ÅB'°J&óe…¶Nè-08¶r®Ìbt° ]Á)Àö ü·+Ç6ðJ¡«c>lðZ™ Ž+„þÐW06hëÄwp­ø@ç¬Ìm¡ÜêµÁo«˰ÅFÇuØX‡]uènƒÏÖ'ÛMØÜ]ö4!ãÔïÕÝNíû7»Ú÷?µïjßß5F>µçÿÇÚóÿoý½ºxöµ¸ýyûlTýÃL.=rûTÿ°YïJ„¢î½~ì:U?Ö¤zF.R= ÕKפúéÎS½ö«žº±ªOÏ"Õ«ÇMõÖ-v©?ë®ú «´ëT]/U‡¶Ù¥ÿ˜‡ª—e•}1í5³ÜT2³ª‘¾HÕ¦uWõ³ŠUmÚNÕw×KõÏ´ª÷Ëd/M{uÕ3!VõMh•µÕEM{íZ/YgÝ^¨Yõçíµˆìýi;UïOÕ¿Á¬z¦1>y™ìyd¯÷å¡ú¥™Uoày²Þ—è&êéÚ{7´¨~iUo`wÙ7ÍÞ ‰ñk›U‡uª/’»ì,ú¡ùr.¨XõvèP=lªOÚ2ÙØ^?—õðŸ¬ê{YdÌëªú¹`ú‚é‹M¾Øä‡M~`û£wŸ Øí‡Ž<<zúƒ‰L°ÌÈðgÌŸ13þ`úƒN ú¢_ ãØ>=bÐ'¼ 胄þ¬Ed¬ ׃Ñ1ÜxB  a^B  7¬pC‘ŠÎaèÆ<„AýLtš‰>3‘5ì™ðÍìUá>sR]$:D üQðGC }4ôѽ2ˆz‚Ëg¬ø7ºXèb¡‹….ŽÏ8ñ‰NñØÏ|ÅcG´ñÌY)ßX“ìMDV"XIè™N8Iè‘„ÉŒ%cw2¼)ÈJ&ÛSX¿æ6~3ø©Ð¥‚‘ŠVðS¡K…®¼TtIÇ–t0Ò±5ütðÓáÍà/þL°3áÉÄ72ÁÎ; š,tÈ;þl°³ÁΆƂîìËa,ýr˜ÿ°sÀÎCNÇyâŒ[Ø‚ŒdÖ'Úh£Ñ%œ™ð¤aW4Øq\Ç™Âù„XYë2̳ìYÇ:Æac˜ \»E'Ë©I¬O.saA¾Oò² Ï‚&}ª˜»*æ5C`¢W:äbWôU|/g^ÊÑ)w¿Ü,ô`Tñ½–ÏZh‹À¯@· ì©‚¶ÂSn6²>VÎY±­™¥â¹èÙˆMØÝ^ŸuÈmDž{¬Ø•f:X±§Ž9¯ÃŽ$ô·‘,æ šÎ¥€gƒ7¹©à¤Š˜]Ò9—M::¤Ã—v:4üe‚‘ o&Jf¢K&ögbG–ˆ7°7ülø³Ñ%›yφÆ"æ›sËA¯ôÊ;ì<ääqœ'ŽÁÈCF>2ò‘QO!<…ðÂS]!¶‚W _1ß‹á+†¯¾øJà+E2ì)C‡2t(ƒ§ ÛÊ8_f%˜•`V‚YÉÜV2•ØP O5<ÕðTÃS O5<5"λìzì®Ç¦zt¨« Ú&ñÜ1†øçãrÌÜ‹}û¿åþ½ëÞýŸqßþß±g²ýúÿô^ýŸmŸþÔýÿßýŸiþßõ»|#ÞþoÛ›~æöçìOÖ©zæÆž¤gîeC=eš¡±ªWY‡êU¦ú*l”ÿ-î‰ÍžVٷLJã‹|Tÿ\ô¸h£ìs5âzè‚1 =†s½ GÎpx†ó}ó4‚ù1OÕx÷”}¶ì}/›eoŸ‘¬Ó¨‰ª—n«ì¥;JÄÖÈ „g4:øÂÝh0½¼dÏ¿yª¯.ºAΫKßOÕ;Óªz/€yñ1UKÞ¬z›u¨~ñÐŽ Sý‚DNÀ˜¯—ê!oQ½Îvʾ˜û &—ž œ÷S=à‹U„Õç–ñ)-ª§˜8î½áE_MÑÈÞ^䨬øfÙßÖg™ì/ú‡EYe¯tŸæ®ú٢۴ɪ¯-k8 §ËÞñ|šÌªŸòf€3_tLå3•¹Ió /|¾Ðû!ÏÚ`øý ÷ƒÎ™~È4Ac˜Œ™°Å„-&°MÐyœ à\çÄ9øðé ô b®³ñçÎç #ˆõ B`ì †'˜ù æ|04Áð#7¾PÖ#ÞPèC¡E×Pôƒ6 Ú0hà Ö°>Uò=â‡?‚Ïè¢øKDF:EÁ%æ»¢Ž©­(ô/]b ‹Á†t>š8æ;¼8ô‰CŸ|‘+1ïfæ<ÚxðâÁ‹‡6º|p‘(ä"/QÌ5ú%“_2üÉÐ$#+ºdd%C› ­û̬‹0CSÃqö—³Æi|¦1iÈMƒ.±4èÒÀËà|ó“Áe`Oç3°)Œ,x³Ïb< ¾,hj]ƒî |¯A ºY°Ï‚}¹Ðç"#—±\dä‚•‹Œ\xò¡ÍGß|æ&»ó±;»‹°«;‹à-BV²ŠSo<%ð—À_"ޱµ[K /a^Ê¡+GN9z—£s9:—£[9rª°¹ Ì*ô©BŸ*èªÀ¬‚®ÿ¯En-㵌×2^Ëx­‰NØÔˆMØÔN#z5¢WöJü3ò‘“å!FþaäÆ>©¸)ž˜kœ¸oúöLBä§úéýçûé,þ=YÜ{²x÷dqîÉâ[׿™q­ÏŠXv”ŠcŒØÕu¿VÄ«"N1ªˆOO—ºþ~Úˆ?ØóĘóĽ\×}ÜãI#ŽžÐùxÉ>üÍ@þf˜dï>{¿ëXÙ“È]†ƒ3¼Yõ’ÏLøMðûpí™3Á ¾‹çædÙW&€sÈ7 VöIšF€Ynøpá.û‰†Ye¿Àl2¹öfüQ`Æ07Q`F#t„/JР¿/ñâÏõ…~¾ÐGÀÆ9_>á C^"|aÌ¿ÜD袄`¦Šg(ç¡ÏQñüA·`Æ3™Ç0¾gr>•9ÌÀölô‹Gßx>³‘™(žK`f³ÞÙ"&`žsÄóyQÐEA%žÁØ™Œ(ñÌ»PÈ…¾Hæ{z"'–õÊb.²•³_Þ+Ñ£*ËAdav•CWƒýiŒÕˆç)߫ıƒ¿ò‘Ÿåàg#³Œæ®œæ!×Cn…4,ôÈÅ–ñ|ƒ¦ý‹Ài€?_<_Á+b¼uª‚¾út©ÁŽì.ç|ƒx¦ dU¡[ãUÈn³{Ч| ð5@Û€cl²ÏnS³Ì£ìÿþ]¿“Ͻ?ã^Û©ßÈžê ù»ïöGÙsû#ÿ&ö_±ßöïú-ìÉöØNýVÆkf·?gßR¾Ÿ·Sõ°f| òroðß~Æ=à„ß²¸ô.õTý­›eïR{Ÿk0cû`lŒ CÐaªw6ò†ó}k1šè4•O_Ù×TôÃ4M”½MG2?£X·QØ2 {G¡‡ |Žæs4ºŒ†4úŽÓ >/°¼Zd_F/lîhÇ`ë0Çì—ýNƒÑùâ…²·Æ"g¬ÅÙót,>3ºq±ªïi‡ìë8žyï£ߌGÎxÎO`& gr&pnç¼÷¶ÈÞÛÞœ÷Û™ÞŒOiU½FÅ1ò¦ Ç”^Ù{t*ôS›U¿Qìž ]<Ç>èà­O§ìÇmï‰ÓÅ\LÃþiè6 =¦±–ÓÀ Fþ4°f€3c¡ê× Æ 0f€áË<ø¢g0sO&CC ò-Øf´øO®#ò ÁŒ\dä¢k.ó’ϼäco>zç÷É0²{‹Q„Ì0‹-B^üEð—À_ sR‚%ØY‚%ÇdÈY‹}åèU.ìeÍÊ‘SŽœrh«Ð§Jä(èS^4 È­ej‘YËx-㵌×2^Û"CÒFljĦFljdîÑ­I+bMñÏÈC\ó×Ú¸"§¹„똈ÓO›‹¸üÄý/wÿ£ý-'‹ØØˆ}]kÔŠøõÄý*×Ôˆ=xòĸñdñâ?³÷ôÏì;°çdíŒ÷ñ]÷“Œ¸ÍˆÏXG¬A0ç"ùDþÌ`à‡fΛ‘~²¸÷ß~0cÉâþŽÁÈIEv6fò f&rø>#×aÆD™b†¡C<9â¾ÀX>˜ùÈ-÷4t.dþJÏÜ‚UN<öæôÉô³RÜ3À¯™,/•4ìªAÿ4æ´œ¿4ä” YŒ×€[Â_6sÚ xÀo€¾ºtn´ØÓmv6p\‚>åÈ/b¬JÐò½ÝÊѵAðc{ÀÃÖš>™âÖ2 Ð4ˆó¬é°/³É¸ü ÿùŽ0Ø¿Né«_Ñ»"ߌÕÎO]ðɆ_·j›z-}l„ñ]ß½ôýïÜšì÷Þ¥µÕ_$žˆ¤¿˜tGP¼V¬]fÚ[ýT³ž74sÚ£¿Üf|GN¯îõÑw8mO?m`½GTÒÈmóUKÊ—Oí§}ígߌ¼Bß½"m~sx¡_熟.?tŸþbÄ€÷mzUó?ç‡ ûvë¹Ë6ß>åeÓRîëë† Nmsݤ'[f<«ïîè½ãÞÒ_<É-K¾¾KóÑÜò]²žwYÒáÑ=áó²ó­ _5U›mÈÕ6/?gà%¯O†ìÆŒqoëíìqü›³ôüö†¾4äFøL’oy÷mÈùÚæßÇ¿2ì€a‡¾{ù¶Ü·Wêí<>xhætmÊâK'']£>1vå_¯ ¿ÅÎÿ†÷ ¾·"‡;ìÿ( øÇ%¾ ï~õÙ6-ÕÛ+î8xá7K5ï—w]vsï=zñ…—Ì þP±ýç'|–E€Ó,qZ×-9}×bí£»—m¼;ôüyU3ÆøéíÅak—¿²]/Ù|VvÕV/èÚé×^´ê¼Åû;Ö÷£]IÕO¬Ô†[Þòyñwè Ýöá|c}õvë„ÓÞs¡—üJ»æ}öà›}Þ Ô »ÎÝyÕ'YÚùéú쿦 YõÖùÜoÏ]zhˆ¾ûû3¦Yêôö%ëæ3ÎK»\®Ÿ^tëÇs¾Þ¹ž_K¿Ð—óÁ¾‘nÎyÝPÒö…·é»ßttžÍÛq=´ú@èdÓt½øÒÇ&¯=÷üÒô®îümÕ8§ûÜÏšzS‘ÞÕÿ¾=7ÔM×Û+0}hûZ/¾iÖ™{>éëìË4Èql±_n³õ®³šçŒÚãדáê—½RÑ-á³cïצ=0Òoû%_j£¥ÿ‚#ýbݘòœøK«´-WnX³?a†ã¾Ñuö¬´·«_óÛeøÕpMmúžEsó²êþí¸O|-ýcß×^óÛfh[â6·Ä¡Ô«ýé^þvÜT½$vʨé³ÛL¾‘~°®°5e‹í Çuµ¥¾cp¿ü9ÚÊ¿»¬ôÙ·%ÝaºN´€þkú=[y•^*íOúź–c9ÏŸ¬m™kK™ôÊ-z×9-ïûͬ·_{ÃÀÖG×h~›f>}Þicô²Ð9c~t |ÒÖýmsz@ðQmËû Ñ︺?tyü8ì½¥Ÿùõëè™x÷ãm×ËϘóéfoø¥¬ÛT÷PÓàýÚ–/šBï¶iïSo.ÖÛín祗ÿ(ùëÐËu_×ewǺo=çÇOZ½â´{Ò™wÝñÛ§~_(¿2îÏe#Zùw÷¥N?üFùÃÞ‘k— Ó¶N;ãŠ{/ vÜ—»¦7Ùüì§þƒÅ þWm “|Þ…Ãô’ÏÝpÝ»œëøò‹/Žô‹õ§Ý=òׇïÖ>ö\TQ}$WïºeõÍo]¹Vo·”ÝðLæ}zyàå»ÁV½ô‡õR íã˜åá?‡%;õ`ŒõaÁ~ÛÔ:µ—Ì›yžÿ9zÅ¤îš ßoá—þ°>m©í×çÇ-ƒ¸£5ë]=ýû%?:õ6ßíî!Žõ­(ÞðÐÑý“´a†_”þ²þNí—õ[8üåc±Úõ›ô®§ï|î¹æ:ï3j>çX¥Ç½šßéÕÎõ=(ýeýb7®Ôǵ÷×Ó½¿Ð»^سcõ½f½íÀ¸ï/霤Wm<2ÖÖ~.ôÒÖ¯]¸ztÄhmÛØ¼W’¯wú×K~å‘©õŽçO›¸º¬Ú¥jÜ*.Ÿ½»Á‘þ±¾ëâÎ/-Ú¶ä+üýŽÛô®‡?zÞVüò|ÿì‡To‰ž±iê"§Ÿ’þ°þÈŠà§ß_¯m»=ó™š;®Ò»ì·éj½ÍüÓùÃ^[©×Ï™CrÝß”÷ ‡o{Åó‚…7NÕ»äsLo«›Ýœ•í­×ØÃŽøäú¿i¿ Ÿçðãm=;z#û?ä¼>{c¯‹9V¨·Ùoç“´ëÕs¢&øÕ÷TÜi½BÛiWߪw=ï¼³&êmM¯‹ˆX¯YÙEƒ Wë|Æö¸÷–8õ¾ÁëÛû9×ûØÐ ¯|?Yo»ñœiMpY?/­Çìðßï]}îcÁ+ëwt‰uß÷œþ[Úoüc‡f9ò”Úsz÷lmÙl:ÐgÄ 3<Cú|uÅÓÿò‰Þ}¤1ý–ÉCó™þèĶŒsÌGí#Þ7|ý38*Ž\1çžoÒ¯Ðv‘‚Gq_;üî³ó¼FêmþÙÁ½¯íÕëŒü²OÅ•±©^Û5nAÐ/ý‡8ž݇‚xf|«·}¾>o£6]]Çõ‘ó²Ï<úüÊ|2Š>ýüRG^µ+bÔgÉsωîCyÁá+.׋(Ò’¢Må!vé½^~:óš>_ž>_ŸrçPm—mÛïGV„8õùÎ~Aê‹_z(æâÙ§ëõÒÏá“믷×çO¾ÌßáG»ÚÇÜ’vÖ«¬ã'C·¶eè‹7\:òªÛnœéÌx¥ó9yDúê˜{DÆ¥u/9wNT?½Gæ?Ný}rÍÜ•Îëåˆ\ÿUã^œÿ÷Qޏ³ûíwæFèzÏÑ…]K¦[Œø]¯“¸ðÉuïhËóØu˵n™?è=?Û ½Múƒ^ÿiAÒ;WÖB/׽Ò|ËÁmmÎùi¶s¾yxiÝ&oc_@o[PùuÂé'^7θÿˆô‹Ž‹_?ØÖñ¼Ã/zÜCF?Õ¯Ã9?‹ûlçõl¿-¤ëèŒH¿X¹çáK¶?èгG¸éáN=Ý3×–r©#ð­‡ÝüÁCÆõià‚'ýbå¬C×,Óz¦U ¸pÇ §¿3yŽ:ý'½Ml—<îÈo^Ÿ÷îg·úq,8ÒOV†N6Mk?â´“ÙåNçÔëç¡nG‚ÏÒÛn¿óáž¹Gõ†;/;í/áÜßþ±’à±8L×zª#^^:]ïùõóé›Ò‹õ¶¬©*êõbWuP7ôÒ/V°?`µž»®‹ØðBŸ¾§ß®>ž5ØéÏE¹çíß2P¯ß¶ÿW·5ká“~±â‰fû×iêy¦/Ízÿ}Ï€9Ÿ?<͹ê¾á/¯½þÀÞew-¼Ä¸O'ýfEì("ñwë±Êçñx·›ë»çüš÷"|ÎÕÛ>›Ë {jרù5âÇ<þ ýeù¡‰÷~uú2çëÝÙ9ç;§ï™¤77;GÊ’žù³ªŸ¢wøÕ}Õ$:7øÎ…„ª]¡Ê8IZò¬L IßîÍã­›‚ĺ:[Äý݊˃ª,ÊHÐVêÀ{{<{8ITÜ¿¶œ>ú¾ë¼G¼ƒ÷wš|ëÀ‹Bc£H¥ŒC¼$Î÷hpaƒ_¿~¼ôû|™èÛOÙê3æ}õÄë ݹ<õlmæ¹VöÌ{ÄObSyï§Q­Y×€/Y›ìºK¢C¨ÂLæþ{hMõØ„<|¸I äë],ééô‹ï½mÖÈõÇH4æ7¸Íö‰ðÿß#NPïJúý£iDIàM°ÌùÝ5±o=ásóâB·÷ˆ‹Ó ôÊv’^ Û 'ú¶«r†^¸A¢}~º¿;ÁÀ纋.d­ÀÕ{ÄÃiš®p¨ô·þ¡¢g~TôÖ‘ë/ß0åùˆàÌCGÖ‡Á8ÄÁéÑ#ƒÒm%½A1ˆÞ±þòÔôË"ž†yÕnóxVŠü×ÞÞ]¶iÅùX`üÐkì(.·ô½/ïëÐPÄ Ó½fù²TøŸëŸ&ûÛžü(E|hšÐ‰Žá|-°]Öqã¨Á\~éÝVimÄüÓÀsNßÀaÕ‡n/\!öK)â%îíèùÍÚŽ• œüWGìLôî…¯´Wlx¾MÍð¸c)â#–jiïMbƒ©AÞKðYŠ÷\2"Bì—¾šg,Ή8‹ã‰ù nbÚ)!©€zeÊý×ßͳäe Ãò|ª¿ì1æèÉ”I0ñ¢ú˜/ z­slLb†ÍÏÕžJBíCîG|œL6wï>ËU*ØIÆbþޱºwýær,æ›V߬ý²¡ê—ÀxÄÉ æß$þ´\'¢·»Ó¼m—³\N«ùN5îºh@ÚºØî9ÏÊ/ÇmÌnqy÷« n˜Z¾éŸ%Ömë“9å­…ÈÛÌŠü#ã¯M<žŠãáyˆ“¨+(‚¸Ÿ[PêæžìDô ó‹'»ß"1+:w¬·Ä”„<ðXò[ãÛ0q ÆXËMWÅ<*ZZ—¬¬+ägÝÉûškEb¶<Œée·CÈ󽓧TÎÛ-ü´2ÄÇaoê¬dËÄñ'­RýW¢§i´`½ ƒv'ø> ¼ê^þ{aW”!.¸”=ºu­¡$7R Ñ›%ÃÖ‘˜y pI°º_Ê{rhB(Q’[RGÐW¬Ãbè‚¶>ÖœžÜ®-C<ü¦SÈaÙ~•,‚u.Î?z~ƒ˜?ÞX4Tó’ªÏC|ì<¾™zNœòWŠƒ æcóåß%Ó?ÂÙSÓø³Ïr.óÜB.–#n¶ŸjJ=.d·&×ÓÚÀü,´º6Ň¯/÷¥g„ñˆ“M×_5èŸX(ÉÛOKnÙG¬¯Áèeº¦Å|}ê>Wõn Óþw…|*Gü„÷§†¬…$øÒ5ùù+Áïz%Dz»¬%±Š™ÒCµG…œ.G¼„Y;§œŒñ‘äñUö“£ÁÞ1Û˜û“k×ܶ¬<{5 Ú­$ðà~Ä‹OJ²¢ž†€ýk³ølÆj[õˆj@‡rÄà ö^Î× sÛ½)ä|н\p2}Ýz÷—*Ý.zëRžþÆí¡àj‹%Éæ‚¿å ^Tù*É~ÔÂ'ºöm7\èþ°ànĵh‰Ä‚Ñï¶#Jàý2a‡—+8‘üÐÞ8Yàw \"¢{ðŤm ¸\ey 5Îü ¡?(øÔW÷³pî¹:QØõO‘ñ$‰Çmc¢îÕÈ÷u¨éWNäöÅ7Ò %Û‹Ç£äШ–#ó#9~t¹cšÙl#±,n«Ök¨ûšë‘ n¤5_Sÿ]’—Ô¼žži-æyËâUÏì(KÅlÿC$ˆºÍA~Pp#mPÒvßK²"~íˆî†¥«¦c)‰ëüÛÍ_–ú0‚õW¿:[è› ޤmýfOmc%ÖRÕ¶ìƒð rF¼ØâÂãÍqü¨FçvG@í;†LØÏSp&íдOÛwì‘$o¦…/ˆ.½ëÀÖ]M¸Œ›뙚õšÓ…ç#> žv1È;™è.¶Ë\õ˜Ä­[Óß×äϪù$žû€8ÚMÕìÙß…\Ø3äLê"ká7i†®vCÈÍ0/|ËãnA®áÇüß+O{iXsÙ?’|:ÚÙ‚?‘m³Z<²'qç#éb^“`pžºÍ/óª@Üì/Lê8cêIŽ<`hˆnûÈþî!N®—+_ÓÓ¦H’Ìä'çGX»½7*ÂHxŽÛW‹xJâáðŸ6ÔCáþ“|jb‡äÁÅ|ý×]õr'q—··x:e§ªgÔ¸¥ÀgâãÈ É¿nê-èùû9Z@t3®µ|òß—qimniÅó'!ÿÎÏÃóQ9î4ÏétÿÞºD7ár³E³ë’¸œÊ¦?çü V–ÕÆ!.Žy\S<ù¤-µÈ=”²O—»áz€[%ÏCPƒ0qq|•bH ùB³ÚvsžÅ<¹ÀéíûÞ™Y½¸ŸÀí¬JÄÃI«ð:d?ì;Œ£gFeLRÇê(‚Õú¬Jäÿ©ÛW’LÃA϶ ¾ j“öz£J‡0ñÝiaU£ïJ%œ ú“Í`¾ ÷íÓ’ë©8p¦¿ÙÇíB–OvI%â#&axÊ›$y?5H-Åú‡L9ÇIær;¾Û©[¥ÆKE|ýâ\{´-ëo-èJÉ1î4ÌÏ1ìBÎ ¿6ûÎò ð—6.j2®Æ!âCÐcäæ»³–zH«Úw˜ÇsâïÖxåÚƒë.+šq—<¶lƒýžºÙ,ÚWÅ1ѹT5Ý–ä.ÖU£ 9˜Åå¢j_pýQ‰xÑ(îÂIf~ÛÏD×mÃ`‹äú$¾xõØIp‘â˜x<«B\œþ™*.sIVÌID×ÎD«‰WÂ^$ľ&pÓº{p?â"Á´µáÍ·W„^Þ”ž"ݼ)æÏâ»*ŸãwRƒmÏ«3=/äMâ%šÉ7 9€qEn—èlæßøg"—ƒŒObÿ }-öoâ'‘F«¿qOye‚§e½-ÂŽ¨g‘&Íø•Û_ñÎ/Ú§î#xâ& ãb~,?Éå´õcÃÍÔ\±îeÁ!—æ¯øÏC<%Që©qµÀêC§†EáÛʼI<-[™”§æga<âçEÔ•d ²R¢kjlk–˜Fâ߯9Öoë(·*ÄÇ´Ë$™VÉ,½EtÍ5¥±O ª¿‰"Q¸8sÅ~åáãÅ>aù'¾ÿZlnä±·£ûóAãýŽ˜ Ë{©v0‹ œV#~ÎÒr®•%ÂÎ ™°\Z¢kö®ïÈÃEîOEüTa„hè°°-œ¯<ÎXx¡U»þ~&ÉŠ˜™$øùEÄ·¥Kù>Ôxö¬*·I%Á‰T15ƒñˆ‹s%à*ÖÇâÝœþõÖî^ñäÓùp}Á佨©Fœœ‡ÍÒÎÅ^’±î’èh´eú×DCÅç–ëbÿU#.°<’<Ï{ÆËã3‰¶ì¼ì[3Œhz_5êR²@äoª‘ï8\tz1ìÍ£;3 $Ú—Ž}®ãòBÓÍ£tþ$+ª„•¼aòÿ"ΗçOdÿ”JLˆö‘ƽýµZ÷b†[ «#Æì–Ïö âãÒ·'áù‹ÁN¥ž+Ñj'YïrxhÝ£÷ë’]j~UØ'5ˆƒË˜7…yxE9 þ•hïw;v¸¯­ w38bÿ°x×5ˆƒË,&ãïD{¯øå€ðD£0¶Šç‚®,£™!wjFÎ|\æW#ɳéÀuD›¥wÓ{&<~ÃõɵÒ?ûè#>Zò›ô¡ ?ðOîÇFšL´5ßË˺¸?crJÓB!¤ÐÇ5Èw®ÇþIžú`þ|c{¢}2%ù´ ÐM™ ¦¨Z°îG¾';9í®‘ä‰Ô€×Š|²²m<¹¤q?Ñ÷w?ož‘šTÚ,Hä•kÉJ·¥$T•D[ñÈw‹Õ¢°w—gk3n_²¼¼«µÈÿd´ãyOÆõ‰ütM‡þûˆ ŠOüƒ©õ㵈ƒäWŠB–äÞßÑŒ;ÑVçmÎíyAàñÅëx8k‘ÿ)°ˆ¹$ÙA €ðüš¶"¨Cøæ«âýJySÕöZ-â eÞ•~Kǃh«D@W¹Ñh§é^K<ûžÓ…ç“k)‡ÂKÎf4)ÉÍhçzM´¯ïFÎ,rÉ5úmßÀ™Âï­E\¤ÜS ‰¥G—nO ‡u)âCR¯'/+Õ|G4ÔÚ ;Ëíaæo |×"n®°z…Å^mýºý-ÉÆQþ=N§©× ?SäFÑ¥fû;мÛÜ P û?â;âæ Ë¯.ÅúN©€NÇg‡zM´4h½‘h\üh íšØ×y`¹8ŒÇ|´Üj¯Ih§¡RÝ6“ö©×D[u½µW§i /Q¾s°É¶Õ.åðÄM*(½ïLç~`ÁmPíÂÎ6ºôç̵Ü^ÐPóf¢žûj^Ò`„8J½Ù9²à¼+÷k ö4»Ù›è>©[Ö³~j•§ÒÆ#~ÒhYhØ?b>+´N1zJt Jêç„è‰bÅ”ô^ÄM,bDß^R÷FZiúëÝ“¶q;B³“Ü®ĺf. Fˆ›4?(øêü™…;]ˆ®Ñ±A%^bÞ‘}Þþ2±°·™ÞSå™Áñ’FÉ^T©âH*¨³àü/+@Õ›N3¼ÜßÐ쥙k{Q/Éð§úc#ÄKºïŸ8]ô…¾7ö%ì&ãîÕóFå>­?ywtX^Ïói…ÛøJé¡Íþ³h¤¤§_ƒ˜_‚ýzáÛÃæi\^pܰ:'ƒ1â&fÝÆOäö‡þÄ>@Î*¢}ºñiÈ+¢îsžï6#>Ò+¥ãúªe’ã '"¼ÐÕ'%ýúçZ‡g0F\\D+®RÄúŒÓ¼YžH´¹gJö8«vð#Y|Ó`Œø¸Êâ›Ú7dÁ`ÏѸ)©¼.! ‡J×áúÍ`‚øÈ Òxvª¤S– öÉS¥ ŠÛƒL.Ãýˆ‹Œå%EO¬,9.tðßâµ³ˆöÍ©»dh†ÏY>£Õkx•&¨ÞÁxÄGFül ×;…"Ï:K>T4×™h+¯{6Ï te¾£a_Q·Éê9}M7Ôk4’]ÜMþÊ ö·QÔÎWÓ_bN;r½¤ÖC z Ž2­3¶š!éšSÁïûN dˆ}ç«ð‰¸ñ~9´´ÚNàÆq“9Ê;äÉ4ƒ¨K¡i_G°ßj’Žûy— ¹Åü[Uß~‡6˜ Ž21_)i¯u²lvȇwôËgF¯Oꥸ_o0AeRé¾â¸¨·ÀÀѾ¯ï¼¨Ëx¢aßëL™%·_Ž_1^¬#‚<µÀïž½õ#/QqÆò|0ñ’¥¤Cg‹:êŽÕq¦E<àÙYÒvëLžWbu›ð<ÄO~Ÿ"æJÐtÚQ?Y>xxþ‹N".ðTù‚Ç•‚šó¸Ÿ ¯)â(«½¥npãIbž.ýhdøß$÷}þî/ú»ÑÂTÀ‹)â%ËM)àóÁzuðÃ?X¬‹Å'Ôuýˆõ!7¦ˆ›,¥Œx§wþ«%Ô2&º–';¼ÙÓ@¬Kqç|>ZâD)ßýy—¨ÛÊ蔪ºZèûNÏFD·!Z–TBÈø]ŒG|d)ŸS˜ðº1îç1Ô+ÛKàýâåýuU¿±3ÄK«wÈgÿçú˹jfƒú"~ ¡ÙäÀE<ï¿–%ê^Í~”ôÃh) ÄÆŠuô3-Q|£êUž¬ºòȽÖVÐÇŒáë@}í‘è\£¶]Š´àr˜}Oã>”²šžy5é?úTÄŠõ¹¾õø[;üå;C¾ŸØ÷#\/›1ÜÜyµÆá)ï®ò¡¢X—kåÑq1)$þ¹åˆÅ­ÿËëàÒr£Œ£0žá„–kUm“ò0HtýVcþ ‰§l›ôš`œîG\d³xTÞZåÃP¢sßamqp%añ:€~>Ü8È®«˜Iy¬>‹¯s`áÏ.|’hb+†Ëiõûƒ9â!»ÎïQ±-r¥¼C”/ë®®ˆ8•â¶µ!øÝ•°ÌÛ³ùS±>VÔ¡6£êE¢›tâòpïqdýŠÇ¹ò?÷íóð¶çÝÍ‘ÿÙL¨||ðïúA¢›U÷lc‡ŽDQSr|þ‡ås¹=iޏÈV—íD0«{àú'xBRB…¨ˆ_³|Ä—®)d>«sSóŸsÄE6ó‡¨ñ¼ð¢z/C¬0N¨~ïe„½ èŸÏg,î÷ùŒåÿ¿ó蟹l Ŭ·í·kVÎhêÌzßÃï&å°}=à/ð“êsS¸¦zË øCå¸Ò_°l¾õÙ€}má…=ð-àY–ð,K/ì»aYÛöÕ\vÎj1;g~¯s’ùÔ“õæÒ°øÙ9«É("”Þ\»YV#ÖŸÞe¿×·bgAÁøúÉõæÚÀzsµ`½¹àº!ЯíÀúßÃØ/`n_ÌýèŒÕöõåºÏÎXõ…¿0·&EìÜ(ø½)`­)ð«Ygv¶jæGý¸VâÙªJ?./ì{¯œ© ï²…{m5¬д%ÜÛƶ‚{[y°ž÷ðÜÖÙ9ªðœÖpo¸n3—ŸZÎzn­Ä~[Tôµƒg·ƒ5¶“Y_û€OúkÍÅžöT4ÚÁœìà9vE¬ýÖþœõÒÚvZv„ëNÀçN»Ù9¨ð{˜“ývv*ÐÖžëp˜y óï Ïê|Ål/v®)üÖæÛÆv…±]á½Ýz²sK3Ùy¥0GøÝæè÷w‡õt‡gu‡yu‡ë°¦pø½Üï¿;Áú`ž=­Øù¢ðÞžEØßEé啌gqõ‚yõÚôº=zé¹J_/¸v†58Ãsá¹ÎåØóEéëc]`½.0'x® üÖžÓîí£a=b¼°Ÿ.íóåÚžõÏ?ÌÎøò`çŒÂúúYaÏ_ÚCFéíUÎÎ…±ýéžý¬;?ëΕFŸuçÿ«î´bü¥s,g=-áZ9£±ˆ³„s·˜VM_¦™¸Í†±3ÊáÚö¡9`ÕøežÌzTù²~åEìŒr¸¶~X³3ÊØù1åì¬D_vFy9;/qÃG}-{²sá]Ö0okÀýÿØ{°:Ï2ÿŸ·ÍBö“²ž¶°„¶ç !„$$°…ä–@H[¢£âÔgj—¶¸MQGNÛ Ö‘v¬­¶t§:&±+i]RǶtÜâ8êÿs¿÷ó‘?ÎoüY¯‹\9ç}Ÿ{Ÿõ=Ïsçö™¼–Á&¯eoFyÙ8Fùl[à3y-¡w¹5o–à)ºˆÏBd/ì49-]øäý;Ûs½¸ßä´DÞÚÍ’ƒMžj0©KKyË n ò–C»¼Ìä²$.!ÈÁ–í¶BÑ mè€vac8äð®„ve¾æL_yÅ`£wÕ vq«¹^Ý¢1ÒÝ­I5¸ãЮ 6˜ã=kÜøèF[òI…<˜ƒ/ŽMaýW<Á`ŠjwŽýáÄ6|È`‰óœ7ô)æ‹{›6ö*Ö‹ŽÜbÁs‹ÄþÈ.Å 6¸à 8öF7 ðQƒù1ÃÚ oÂŽM”oÂÆMÐÇz ž÷ ÁðƧ8èã´«Ž§<ÿã±3Ø%ø &÷¨æD³qg†´+OÄ®Db“8¢X?’WÞÆ á: ’›4 ]½äG³óaKlJî×î߯ßîT n;'¾OsÐ &Ú4ä¦ao:´é]ŠÍ½Í¥yò%ç¨àÐȰ‘AyF¯ârÛý­ü“±SŸŒ™ã¥Œ•㤌¹È1Ðóœ1NÆ5 €3†Ž_2n9c–3^ŽMΘ$ãPà$ãÏÄq'p¼qÆ–SA8ŽŽ2VL'dŒ±Ád1Àéû'ëï~^úxé×¥ßþc}5õÉvúf§_ì“¥?þc}±Óÿöµ.õÕÆ“ÏQƒõ@]¸‘çy#õðFêÄ4¾Oãs:ºgPofôñÈ©;3¯|-îÍ4Ó$—Éç;lpc{ V–›?ìpùk!zv™Ü¼ÈXÜirò¶¼«ªMªæÞ]î6ØVÈ AWºB¹ÊçŠ`“CwDóçJŽÜ5Ü[ã5ùp{{j2Ö *fl2Âdþüð`“¿–¶´šøþÚQ$4QÐGAÕ¯Õ4šòhlŽÁöM2?Ff<þ%`[t ð$\×Àvn[ʱ!ñšæ)ü¨$Ú_|[ƒCjë°bÐ&(¾k ²S5'¢àQ~•×–8¥SžŽ¿ðfð=ƒ²©ùlÐÔ|¶+hj>ûfφ˜çƒ vŽÿT“Wu0 G{·ÁA¤ÞX¤ØÝ7¢s×ÓŠ Ž7×ò^|:uq:åÓ±y²gPwfߣ‘º6³_›¼‡Ø©yÚ¥ùÛx<-Їhcò {6Ï~6ºçà!Å¡+Žëxèã)Çÿxøˆs²µû¶1yþ›)K„6»ñ)qTóãÛùzû5§»`±'q„Ü­.Íyoçv‡7§’‘›ŒÜä¤PçS°)eÈà;µhŽ__1Uóº .¼ éЦc_:r¶…h~Ál’ï‚óžAy¼Ø`÷ÃòOÆN3e¬”±PÆA'bV˸‡Ïc㛌mÔÀqLÆ0g¼ §&ŽO2ŽA2þL6æ8ã ñ±ÇSç®2†Èø!c‡3nŽÎx!ãÄdóØ@\ggpðdûþ7š×:}½ôáÒKßýFý¶ôÑóXéƒþvbëô¯N¿êô©ê‚ì=’éÎ4â$¿_ÊosÓ‘1ƒòØ2ƒº1“¸w ØÁq 2ƒ‚¾i.tóÜk ø\@àâ9º¨ ó ~+e‹á[̽%”-é2ø¬\/ƒf1ZÍrÊB""”z*ý e+{ ®*|«hß«‘µš¬)R‡µÔÓµø±–zéNU¼†u ŠËÖ©8¨ëó æ)v‡ãç¾oÀÖ Š¯ y˜#ð1’ö‰¬Hx¢à‚?ŠûÑØ ] rbð)=qØ ñØž€Ý Ð&À·9X±ƒ- º$è¶¢/)ʯfã˜BŸÆ3N‡>?ÒGMðK3xR'¦æ§SóÓ® ©ùé›u~iâ;l° ò >6Ý@»¼ëz þ#íìÆ²œn®e¯ŒÓM_4YÓ©KÓ)ŸNù ®gP7f ¼n/\Ïä:8X1"m¼ntÍ 68BÔ•YC8B<ÛÙèžã28B=¯Ùs©—s‘5]󸞇îyKÒÆâz>×ó±mmxô ¸^@_ì¢Íº¸vqí¢Ÿ[Xd0„ ]äþC ¡ÅزÝ‹±u±\C¿þ%Ý«›ò¥”/íVü ¥× N7qX†¼åÐ.Ç–åÈ_~Å`t#;¿B‰a(1 ¥,”ë!›Þø±[Vr½²Ïà£UÝ—›˜¬Î7˜Üð®w ´k ]ƒÞµð®åù¬í×.Ð ¯›ø¹±Ù¬u©Þ0âÖ€ÿSd°† î± ¿`0¸ÞÐ`°~ˆåÆTƒ­ïÑÁu6F '#±!Y‘ÈŠBOz¢(‹ÆÞhì†7ÞâƒÞbƒžMðnÂæMÐo‚>–òXÊc‘‹¬8®ã(CWñˆ‡>ÿãáÇŽâœo´›]ï›ØlFv"׉\'^08£ù÷;¶È¸‡Ü$®“»Õmp¿áÝ o2þ%cs26¥ 'ÿRzÓB0G+TðÀm,¢|ÅJ£Þ¤C›m:rÒ±o›[±H»\ð‡2ˆGz2°!þà}«4 ñ𿛣:cŸßéï'öóûö‰}ºôåÎubîôÙûk§|çØ';}±ÓöÁNëô³ j÷ .Å`“½ŽÓxÖÓh?Ó‰Ótêà hf Ì5b2“ïÁ.ÅZ›M]MýšSd°ÔFè÷ ŸK<ç^3ØiÈZ ýZ¿N—³°ªBýZD]]Âó]Òc0ÏŠ´êÆÙrh–#/ºPýŒü]Ъ´ úÕЮ¦l5ú×p-u|-¶¯¥žº±ÕMYçU#°!’ú }ôQðFq/Ÿcð9þìÚ„ìM”m¢,–û±ÐÇq/{ãáÇÖxèâG ¶ö%r?‘ï‰ýŠœ}~lÅ–­”o%VÉÄ7™)!Š'œš xbiЧ¡/x¥‡(ï6tf ê”.HYèœÏùŽ?ö˜ê”ÿoÏs§æ¸Ù9îÿæüvjnûÿnn›o⃠µÈ`k¢ó®oàú†^ƒéæÏÇ_Æy‹âœOc,žž`ð1‰ç ÚÂŒ.ƒyN›™o°Ï‡ ö9u5¹Áè™Eù¬|ƒÉõl®g£g6vÍÆö9ÐÏ~NŸÁÇL5ؘÄu.}Ú<ôÎÃŽyØ1ëù\Ïçz>×ó±k—?ìZÀõ®]\»¸vñÜlLl^ï"|Z„®EÄ`¶,Æ–ÅÄ`1׋‡´»Y‚­K°} 1YJùRè—R¾{–!oò–!oò–£k9¶,çzù5ƒ‡Ù£x˜¡Ä3”6Š_¡W00¡]íJlYIùJì^…ÜUè]…ÞUÄd51YÞÕ2öp½Þ5ð®v z×¢s-ö¸±ÏýnøÜÃÚýÙØ–ð­ƒ/ {ˆ{ú×W@·ºЬLP¬àUð¯*RœàU´ÏÕ½Ú5˜öæF·šuЮë58¿è ÃßõÈXýzü\®p|Ú€ü Ô¯ ƒÚ„6i3ŠOh"±'QȈB^÷¢¹M šxп٭À‰”%Ê':·„(ÖãlO’9{™âDn%FÉÄ:¾dt¦À“‚ü”QÅYN…>X§s?é××7ƒë iC2_ÿÓyùÍÉÿ_½{ž8'ÿKÏÇÿsñ?å]sà<ü/ýŽYúξ ¿þù÷dsïÿ›yw™ñëšÁ¹õñGœ,dÊ™r9Ç}q¹ášÁ¥ç™ß8`pé»&`Òw)&ýtÚõ êìŒnƒ3‹œ™E—žë`ÚQ0rƒ‘Œm³h—³(ŸÅs›Åõlèg£g6å³Ñ;‡ë9Òß#{²ç"{.²æò}zç¡wvÌçû|ú‘ù|_€M ¹€²ƒ‹>×®!ƒEÌ…Ú],·EØ´ßaÃbÊs½˜ëÅÄ} üKˆÕlZBùRlZJùRl^мe2^ h7³=˱c9ö.ÇÆ®C‚/¡è Å·Pä„r½‚ë\¯ÀšoQ<ÝUÈ\ݪQÅ—_¾ÕÈYÍõb¸º5Эo-rÖb§;ÄàÈçuؼ›×Á³ž0ìÃÞ0ì_Ýzø×cãzì'vá]+~Tqâ7ðü6àÛFälDÎF®7rÁu×ÈŠ€7ݑȊDO$¼Qè‰â~44ÑðDcK öÇïäÇ@³‰ëM”o‚göÄâO,å±ø oöÅáGåqÐÇ£#ž˜Æèët$ ?aDñ‹7S¶™²ÍÐ&BÈu"¶nAÏäléUœú$ƈ$®“›„ž­ø½Ú­Ú]'Sg’¡M¦,9)Ø“B|Só»Xðëm|zhÒÐM:¾§h×.¸ÈÛ¿ ¾mÈÈà:ƒ¸fÀ—)ã“ô©òïÍ¥1ËŸþ»yóĹòÄùñÿÉx²¾âÜ÷OïöãNÿíôÕsÛ‰{n'ö¿N¿ëô±oôî×é?'ë7ßýÎ]'ö‰<;Kú;êâ è½aT±³oÄçi|NëÖ©È éÏxþÁ<ß`žó,êÓ,>gSgC;‡:1[çQoæuëôd¾ô?<[×°âm/Äþ¥Ð/ãû2è—q½Y¬µ,:3kÙƒÀzËbe±Þ²XoY¬·,Æ.k¹yËzËb½eÑiY¬·,Æ3‹ñÌZk曌kë-‹õ–Åøf1¾YŒoV„9‹ÍzËb½e1ÖY´‹öoÑþ­xsÞŒöoÑþ-Ú¿Eû·hÿíßJ1ûwiÿíߢý[´‹öoÑþ-îK³hÿíߢý[´‹öoÑþ­fíߢý[´‹öoÑþ-Ú¿µÏ¼£¦ý[´‹öoÑþ-Ú¿Eû·JͻڿEû·hÿíߢý[´먙kÓþ-Ú¿Eû·hÿíߢý[´û:íߢý[´‹öoÑþ-Ú¿Eû·ÏÚÑþ-Ú¿Eû·hÿíߢý[´û¬íߢý[´‹öoÑþ-Ú¿Eû·÷6Óþ-Ú¿Eû·hÿíߢý[´{o íߢý[´‹öoÑþ-Ú¿Eû·ßÏÓþ-Ú¿Eû·hÿíߢý[´yïcÑþ-Ú¿Eû·hÿíߢý[´{Aû·hÿíߢý[´‹öoÑþe¶hÿíߢý[´‹öoÑþ-Ú¿}FŸöoÑþ-Ú¿Eû·hÿíߢýÛçiÿíߢý[´‹öoÑþ-Ú¿½¯›öoÑþ-Ú¿Eû·hÿíߢý˾‹öoÑþ-Ú¿Eû·hÿíߢýËoíߢý[´‹öoÑþ-Ú¿Eû—5”Eû·hÿíߢý[´‹öoÑþ%¿€%í_þÉ»ê£=ftš¾sÀô!´c»-I}.3Ïµ×ø':®›ßHRÍ{¶.ó®mмo 2û¼æw“.³/hP÷®Ûy BÌû¸"ó[JÙ+4¨ûÙíßU\fß×ìáì2û‡†ÌoâÁf‘לÝì2û݇ô §½ÏÓeò ä›ýïÝæŒÒɉlÖ‡ù&7B·yÿ7 ïí³ž©&OB§Y3öëï5v΄n³n6¿Ù™ßmR5o‚ý[|°Ù“”oö–v›=ó©&‡B‹É£Ðo΃6˜³ ô<¨½ÿ4Èœ•ºfÞ+ºÍï9>³Æì5¿éwêïú²Î”ó¡öž¥`ÝK/ïíó¡.s¶ªHs,Øû•ÊÌžú^sNôŠ9+ÚeÖœýºî”óWö&—îkµßS†˜}LEæ7Ÿn“o¡Ó¼»Ð÷—vî…3¿t™÷˜ùæ]f·þþ#ï1%ŸÉmö4•é¾{;ç°ɻbΖiþ{dÎzÍš´Åì—-2gG{õ¬—ýûO°Ù‹Ð`öÚ_Ðýöö<5ļ÷ì5¿ûŒ˜÷ž‘úîSöÛË{-lÖ£e&çBŸY‹›\ EfS¯î±µÏˆé1y/jçY¸¢çEãÍÞ… ón4ßüÆÓcöÏ7˜3¡ýz~Ì^{ºÌÞ¤>³?iØœ% Òs¡v…>Í£ û“ìw¦!ºOÉ~Ob~Ç)3{“zu¯ƒ3¡×¼76¿á¸ôw{ÿn·Ù+?¬ûåe­™‰o™ø–‰o™Øœ o¦¬C)Ï¢<‹ò,ʳ…ì,ôdá{6y°ÕÃsó ÛÓ%’å–¿í¦¼”{±Ó‹^ø½ð{á÷"ß‹¿ÙÄ;Û²‘Ÿþlôg£?›òlôç ?ý9”çPžƒý9èÈAG6ä@“ M.2r‰u.±ÎÅŽ\ôä"'—º‘‹ž\lÝN̶cËvdmGÖvtmGÖvdmÑ)ýìÝá;°·Û ‘[ˆÜBü+ÄþBä"·¹{±k/´{¡Ý‹¼½ÈÛKl‹°§ˆ²"ôq¿ˆûEðÁStM—Ðs¹°÷>€ïå±åàˆ.Š¡)F~1º‹ñ©„¸•Pv»a×!øÁþRt”¢£”˜”âk¼eð–Á[O<ØT­Ȫ@_1®¤ì0e‡);LÙaÊSv˜²#\áú×Gä¹G‘{½>lõûj|­&¦Õè©ÆŽjl¨ÁïbUClk «®–Ø×bOþÕƒzøê᫇¯õèh V øÑ€Ÿ ØÕˆ]ØÕˆ'à;_|Mð5Áׄ¾&ô‚ï|§ä;vžB_3tÍ𞾢˟xZàiáùµ «YíзSÞmåvPÞAy'>v"³û;‘wžxŸÇ¯óÝ:Ûÿ¦ö¿O­‰§ÖÄSkâ©5ñÔšxjMüÿ_ËZøÏ°¶¤ÍËø%c õ@úby÷n÷yƒÚ7ØíSÚˆÔSŸy^}Æî¶íß-½æ·Ënóûåù Ó­¹îì\D &ÑÍIdŸ“ 2û½SÍyÅNý}Ò΃áÕÜ’ÏηlÎÐtk¾ yg/g½í},.³G<ßäâè6g‡ÍÞ–ÍÉ!¿‹.Ñóöï£ýºïÅÎqä2¿/™\G4§œ·7 2çp|æ,NŸž—=2v¤“;ðŠù5Øü–êÕ¯¬gÊå7VÙ?hŸ'Ï7gÊ»u_£ä”<"öþÆA=[nïsté¹ùÍÕÎ1bΗ—iÎ${¼OÏqʾûŒùˆ9gÞmò hÎAû¬yù-öšù-Ömö>–™ßd{Lþ¤.ó›ì ù]6È샼nÎõ$˜³ç-æüy¿æP’ó+ö^ûH³Ò§gE%‡’½ç>Èìôš}Ý&÷`°9sžoò(õè¾=û÷ÙNóíÙûãÒýöï³=æ7Úa“‡Ð­¿ÕnèÓý?ö¹Ò`s¶´LÏöÈÞG;ï Ëü–ã39”.˜œƒ.“;©ÌììÓ3=öùò2sÆ|@ÏÑØ¿Íé™r{Ÿc°ù-¶ÈìêÕó§²GÈΣ2 gÊ%¿ œŸ‘|~²Ç?eDóÉDɇ"û†ìœH4/’äZ±uë^Fû7W·Ù7ä3ûûto£©Os•³ãö>¢ýíÕ>“Ó£çYå7#9›“IY&¾eâ[&¾ebs&¼™”gRžEyåY”g!; ÙYèÉÑ)±Û<ØîiäGA¡“Ìëüa§?¼”{±Ó‹^ø½ð{á÷"ßK¼³‰w6¶e#?ýÙèÏF6åÙèÏAús(Ï¡<ûsБƒŽlÈ&þƒèÎ%v)¯Dþ~¹O½8Ø«ÓôíÄk;vlGÎvälGÆvdlÇÎع;wÈ9;°µ» E.t…øVˆí‰[!öb_!²÷B¿Ú½ÈÛ‹ž½”aïAÊ‹°¹]E”QV$÷à)Âçè;€¾Ø{€ºq›À{@ìŦƒØT M14Åè(F1v•àW e‡°í¶‚ÿü‡à/EG):JGtQo¼eð–ÃSO6U`o²*ÐWAŒ+);LÙaÊSv˜²Ã”¦ì×G¸>Âõ‘+º9ŠÜ£èõa«Wão5q­FO5vTcC Ô¯bW] tµÄ¿ßêð¿žzxN@[_=:êÑÑ@¬ð£?°«»±«;NÀ{Þ&x›àm‚¯ }MÈ8 ý9ü>‡žNxNó½…øµ@×] ϬþøÛ¡o§¼9”w ³ƒòÊ;ñ«ýØÜ‰¬óÄø<¾œGOsjÐØ1™©¼aAýyÃd}$ë!Y Éh²õOàÚGÖ;²Ö‘5ެk×0²~ùSÖ,ÎZEÖ)“­O×&²‘µ‡¬;dÍ!k Y[ÈšBÖ²†õõtlÍ kYL¶&\ 8s~™ëOœçËÜþÍ6ŸŸl.8œÃ;ówgîîÌÛeÎîÌ×å÷«ÀùºÌÕež.st™ŸSwìy¹ÌÉe>>¢ùe/䪔ü>²WNrºIN Ù -9{ºMI—æÑ˜r¦Dò1ËiÉ‘!ùxd™ìK^6¨{¶%çœäy–½1ö¹Ÿæ ”q’»GòIž8û É·!ç¡e‹t+v®æÍ'¹%í³ÁšŸÎΩѥç:ìü8©šRöm˾gÉ,û”%¤ì“¼prîBò!ËÙ É…cç?ÒÜ7v¾Æ2ÍÇ(ùŒíóÄ šÿXònH>:9k!ùz$ç²äó³’²¿ZΔìëÑ×s[|š»CöÎHÞe;ó€ž}–œÏ²oz³[÷¶InÉ=Ø·Ú=ÄdO—æ‹”ý4’—2Ý«ùæ$ì¥ÞAÏŒìÑýg%Ø»²}ô-ûå{öáÛîkº÷mo°v±åØTí~ÊöáÛAdÁž—±»BÛJ)+ç^9÷ʹ>†=ÇðÁƒÇÐQ.×È9†®ã|¯„¦»ŽaÓì­“?lß‹5è?ŽmÇáóá[ rZ±í8¼mÈkç6èOrÿ$²Úˆ×Y)CúO"³²Óøz–ïgá=ì³øpþsøp5òÉýsÈòaO¾œàºšÊø¬C¿'ˆíIøëÐQí âÒ 2Ru89Œ¾7@w ¹'°©Nl(Òaæ„È&n§‰é)|l€¿]ôóy ;OÁßÎýÓÄñ>œƒÿ4ºO£ï¼íB#² ;AÜÚù~NäaË9èO!÷úÚùlGÆiùÿ9bxŽØ´‹œk:ÔuâW'¶Ÿ–ù:[ kGN;u¸{:ñ£þóØw^úLCäßÔ{ð©÷àSïÁ§ÞƒO½Ÿz>õü/²7Ì~'.cU‘æÅ·ûýnÓ¿ i?`·Eiù¦^t›øˆ×Ln¨H“ªÁäˆêׯХùì3 ]æÜ°9»¢gbmLž^ƒËsÅà DêY;ëÉså2¹®|š bÄà „<ž“‡µß` „˜ü«>sf邿·±sZùL^«>ͽjŸÖœŒöÙ$—ÁÝ)3g”úLþªN“cuÐä± Ò³ÿöYÿ zÞ_°vì«.ͳjçï×\ãvî›`“cÕk°u"ÍY¤s邞«•\ãrfb÷¨Éí¤Ø:r¦÷uƒ 0 ¸:2ÍÍÄŽLdfâ[&¾ebs&ü™#:΢<‹ò,ʳ…ž,ôda“›<ØêáÙyïÁf¬ý°Ùƒ~/¾x)÷b§;½ð{á÷ÂïÅ–¾gïlìËF~6ú³ÑŸþìrç ?ý9”ç {|åWt*ž¢Óë\øs‰s.6ìGo.zr‘“‹Ÿ¹ÐnÇÎJtžåÞvâ¶Y'»}Û‘¹{·Ã·{w`ï„îÀÞBä"»ëBd"÷$rê(;~]»àd×QæÃ÷½È+âºÙEØ\„ž"¹‡? È.–"øðýz`óêÆžÉl=@ùAl9ˆ-ÅÐCSŒîbä#¿Ý%”®CØuþCð‚¿ßJÑSÊs)Æ2xËà-ƒ·ž x*°©£+U¾ b\IÙaÊSv˜²Ã”¦ì0eG¸>Âõ®p}¹G‘{½>l­ÆÏjžm5:ª±¡F¾cC ~׫b[sE—,µÄ½–ò:ü«#õðÖs]üzä7 «ðñõ¢»±«»);ß øšàk‚¯ MèlBß)bpŠï§s }§ k†·ÞÓÄ£…X¶ÀÓO ²[ÕM;¼í”w ³ƒòžUå”wâc'öt^ÑåÑyâ}¿Î£g¯Ìd-,ÿd‚,kâ©<Äoþ<Äë%Y+®‘d}$k£ÀuÑdk!gý#kŸ‰ëYó8ëg#ëYÇL\»Ðþ`Íâ¬WœµJàÅYŸÈšDÖ ²þ\s8ïÇ'®1œõ…³®\S8ë YG8ë‡Àõ‚¬œõAàZঠ7ßœÿMð®\ð©$·ˆäYëÕ<ôv¿“[¤OqXì|M.=÷-çÁJ΋Ûç¨}/°Eó÷ÉùoÁsÌÈ.Å9‘|Ó’»^°œ$¯žäT’sÒ’‹h[ó×Ûgº]š [ðPrý­÷¼B¯â§Øù>{4o½äò“Ü}’IÎxÚ¸ƒšsÞÎoäÓü‚rÎS°$¿¼ä–<òr¾S°äl§œ¥NñjnwÁK‘œ|’GSpR$¿àê>“{:HstJ¾¾õ>Å>’ÜøƒšCTΜ Þ’ä•ì<Âw|DZá8e>tWò½ˇõ•b~ù:õà¾gó׊½­Øp–8œÅÇVâuœ{mø\î#ã,òN ·NäcëYd‡® Ymè? íY™“6bqVø¡i“ktŸÅ×J>}è8­yçÐ] ù'ˆk ÷kð¡Ï:ž›ÞøuÞ:øêÄ|j€¦úSÄæ42øÞÝ)êÀ âZ‡ÞÓØÙNù l;O§‰å)ìo€¿ºs|žâ‚¿û§ñãvŸƒÿ4ºO‹ð¶ È‚îv¶óýœÈû¡?…Üsèkç³§å;üçdþoí"‡8ubK'~ubûi™7 ³ºö :dv`O'~tÂûÎK9õÞ|ê½yWÐÔ{ó©÷æSïͧޛO½7ÿK¿7VvÉÛ-ýºärðl¬wikRß‹Ìsï1þ‹ £šÿÛÆ°-2ð½þŠÁ‚w™œXešF°À[ÝÉ%X±6.X¾ÉÞmðÁ§ÑÆa6Xa©ÿ¶Ó`†õ\† ƒ““j°ä; žü æÈš1br» fƒÏä#ïÓœä3G ¾˜Û`æúgÇÉy;»Ë`Ï»L®“gqH1¸ìœ6ÁšweŸâGÚxº.ÅÔ]â6yί¬¯Á"ëR|]ÉyhçÅmÐ<8‚ù Ø ó 6}ŸâôÌ49rC4/º7Ä`?”™<¹ë@pê%o¢àýHÎ+Áɵsx¹5W®W¬¸»Q}¢Ëävl1yÕû5·º]dòæ^Ðܹ‚Ákã›Ü<£Ã>Áäöj0Xö}»¬Ç`Ùk¾ÉÍ(˜B’›Jp/WxM>Ý.ƒ1¨ù{l 3—Á1+R\L;‡û Éé2xöE&×W¯bdÚ9¾R5g“;R°,m,ûnÍÅ(˜övηæýZßeòéš<’ÁšWWp!$?­àØÛùtC4÷»÷ª_ócÉ”ÑÎ¥›oò|õ(æ¦L!í<ºùŠÃ»Õgpì‡L.ø“þ‚bBØøõ.۱êC æ®Ï`Ö_0yá» öÃÉ—¬ù'í^ýšwrˈÁ~QÜ#l@1Éì<¹.Å@’|”6®n‚É3Ôb°éû5——?vAñèmœ‡`ƒõàÕœ”}šO^0u%/n&vd"3ß2ñ-ÞLx3)Ï¢<‹ò,ʳ(ÏBvz²Ð“…Mlò`«‡gæA¶›=ØìÁf¾xñÅ‹åÈób§þô{‘áÑiu6¶ec[6ò³ÑŸþlè³ÑŸƒþôç ?‡ò☃Žtä`C4¹Ðä"#¹Ä:;rÑ“‹œ\üÌEO.¶nÇÖíÄl;²¶#k;º¶#k;²¶cïvìݽ±o>íÀÞBäâÛ^l(Dfò ‘[ˆÜBxöB·ÿö"o/òö»—²"è‹ÐSÄý"îq¿;N$˜¥zÀ{yðéåà;ÍAäÄ–bhŠ¡)F1>ck >—Pv»áï!øÁþRì*%n¥øZ†¯eð–Á[o9<ðT`S~U «}į’²Ã”¦ì0e‡);LÙaÊŽp}„ë#\áú(r"÷(z}#º,©Æ×jbPžjì¨Æ†ü®!^5ĶºZžQ-¾×BWwE—.õðÕs]ŽztÔ££_ð£?°«»±«šWtyÓ_|MèkB_úNÁwŠï§qjD—>Íð6Ã{šx´ËxZài¡îµ\Ñ%Q;¼í”w ³ƒòžUå”wâc'¶t"ï<öŸ'Þçñëqm#kž»½ž™l_yàzEÖ(ÎúDÖ&²&‘5ˆ³þµFà:CÖk g]á¬)|A¸Ž\C8ëY'¼ÑAÖ“­ d= kÚŽ½˜šûÿyæþ.Í·(˜’ÿU0Æç)ž—`‚ N—`H^WÉ­jãø#vu‚â>®õìœÅî²óuw)®ïZžßj)ëQìòuÈZçRü®Õ]Š·cãÔö ×.ÅîMè2˜hAŠ ¹´/RòJ¾mÉiçÖ<Ýq‘3+ßäZíSÜ3ɳ(y½ãZ{Kð Ò.(v–äS”¼¥‚‘%x‚- ùÿ%ï¶ä+µó+º÷ArxÛ8; ˼Û`_†(F–äéìKÉqjçœíT¼2?×mòKB·nÀ` )6™OáèÞpÅàµcÏÆÍ!kç'/2¸ ƒš›|_¿bßÚx Ø»‡²ýŠ«°›ëønÍK¾ç‚âí¡l¼{°oÏÅÛ3ªXìå šOr/~”#³Dþ:5ëîAæ~b±;Ë‘{ ŸŽ!3 ºƒÐ”£çrË‘_)ã91õ!³ThÑq >´ÇˆC+4­Ð¶bã|: Mëu&Ú¨[mØSÇý6d´Ç6âÐ&׾úÕ†œ“Èhƒ¯N‹“ÈjÞ“øp]mÈ/âºëМ Öá§ Ú¤›Î‰l™áKÉÒΡçÌsW†ße]úÚ´”U“ è+_1ªÃßaÊSv˜²Ã”¦ì0eG¸>Âõ®p}yG±å(qñaC56UcC5~U£§½Õè­Á¿bXƒ5ÐÕZbQ ]ö×ÃW_=×õè¨GG=:ˆGÏ¡áŠÅØÕˆ]М€¯ ¾&øšàkB_úšÐw ¾S|?%óô5C× o3¼§‰O <-ð´PÏZ¨C-Èj…¦Þ>;(ïÀ§Ê;(ï ¼Øß‰¼óÔó<Ãóè<ždàš øîÖ¾Ýþ÷çø=`ê·€)œ©©ß¦~˜ú àþÀ›ñýÿ3w,Äà ð,f{ ¾®ÏàÉg§byÍé28»´ù¹'}.ío.mlîÅ›´q®)¹‡\æÆnÅ'³±q¹ Á`“aß‚^ÅÞœÉU}Mñf]× ÖzÁ'£,Ê78eð/êT^{‹‘±x@qx7*6ŽàÒ ¶Žä=_šj°xËKÁÆá-3˜;ÈZÖ­xíˇ4g¸àñ v™©¤x4¡^ÅeüöPŸb+ŽB*ºB‰K<<ÉÜ[I›[é2½ø½¾•E›‡˜­T¼÷U#Š·°fÄà—¡g-|;º ö{¤Áä…Ö {ܣзeãÞ`oØ€âÀ –Y˜Ä{v )~™­€Ì .ƒ ïV^k“ûÜKDND¤bóF¹ oªÁU yØ¿­Só²çq/_òÊWði“°5¶Û`•õLlŠR,Þ¯ækßFY| Ä-¡E185w{"¾%Ž*v¼ ý‚S–ŒŒ½C&Ÿ;ß“ M’g3¨øº‚I&¸ Éø›¬x6¦.:Ò }·ÁN@÷6ômP zÁí¼„mðfÀ»ûv³_vB{ºò‡m;±mç¨NòÑ‘O<ò±3ž|tå×_>qÌÇÖbøòå=ùèÉÞë:õØÿ.⸋øîBà.b±‹Xì‚vî#þG±¹ZæÔèÞ…¯»äv!gr °·ÙUØQªÓ2ô–á_¶ìC~¾ §› ð§@ÊU€¬dŒê¨˜ëÝØt}»áÛ¼ÝØµ¿vcÛnäíÆ¾ÝÈ*lÌàÙìg¶îþqÜÏⱞ=b÷÷£}ÜÛǽ}ÈØ‡û¡Ûìý\—ýrÏàÓIdVAÛˆ½á?íAh«dÄýbôÃSm ´%Ø[]I¾NÏJ±«›J‰K)ºKÑ] _|eð•ÃW_94åДCSÉg%2*¡­„¶’¸Uák%UØU…-UĮШU«*bWEì|èjäþqlð!χò|ØrLž‰|bû1bq ™ÇwŒØÕÃZdÔbSí¨NëU‡ŒãðÇŽãØqŸÏ ó86´"¯žV¯N[¡9ɽðäÞI¹†ç$<'‰çIxNbw3üÍØÜ o3ºNã+6&­Ð·b[+<­ð´ŠxZñé,tg¡9‹ogÑs_ÎÂwú³²6‘yìĽGÎ:c²õEà{WYSL|ïøÎUÖ× “­&b8ëgM@\¦ðú‚Þœx}“͇÷GÎ{ÿ»}Ñ2¯•ù¬3—¸'z²9«›¿ÉÞï:óÓ‰sÒÉæ¡NþïÌ7'›g:óKgnéÌ)+ß`é̽#ÍA“ï™îÑ9ܼÅ{Ì•e؆ü<—ô7˼ŠÍîSì×½ŠK¸Qîµ(ζàFŽ(¾SôˆÁ?¥Ž.ËW\©ŠIºœÏPäG\d/‡'6_±[ I0›¹Ÿ˜¯øÚ‰eŠ1?¤xG‚ß'x¤Iسòd¾oV|ÐÔÅ! ëã~§âƒ‡#?|X1–’»GPºÁŒÙˆí;h/;zõÕ‚`tÇ##JæP÷e2vÈü¡E±À·5(fÌè»õµÃî'àßd&¡g´yÈÈ Vœ˜mÐ'¢#û‘™‡ iÔ¥¼Å4ÍÛЗ's ⓌÎ<® ÑQˆì<™ËàC1ØË½Á3b+þ–É< ÚÎ.]‡Ùÿþ§ïád¬üKîÉøî/ýþíÏñîíOÙƒøÞí/½÷VÆø¾ ¿þ÷m“½k£Mý¯¿gûkxÇòs•©÷kož=¶ÿï×–ñ|V ¬>b=›xÍÆç¥Ô™°ƒ5Šså¦mÎéUœé¹´C7si/s©ûsGç> {çÌiḑþÌ4øÓÄzñZÐb°HécÐ_,9\¿âeºhg.ä¹®+†ßÂ`ƒGÊs^TdpIáEÞâƒIÚ¯xô‹‡Ÿt©Ë`’·¾¥ð¹é¢‘¹–ù Æ 6,CÆ2d,çåð.Qlëdhâ¹AòB‰K(:BáÅÇPxC‰Ih¯s;¸·_WFÜRèWõÌRâ´ ÛV]QìëUâÛ5ƒU¬öÛзÖm°KS³T†3÷u.¿4 ¢Ð6¬¸¥a£:„®§l÷Â¡Ý sLùŒTlì× F)>D ?‚{Ä$û¢ .6ñ‰B¾E—˜ƒQJy ¶Ä@ƒï1ÐÄâwl⦡#ÿb±+º¼Å"Mƒ/Y Ä$q@±Gñ'Qæà2‡Åß-Ø”}R¯ÁE^>ì….ß’øLVìl?Sð!»Ò°7 [Ò°5 {Ò³yûCt¨Þ†=ÛdÎ>bp¹—!óUxv³ý;Ñ»ÛwB»];±m§ÌM‘³Úýøv»óáɇ'ž|xò±3ž|äç#¿ú*ìÚÅç.lۅﻈé.lÜE vƒ]ðîÂÇ]èÛÿ.b² ÙÕèÜÅßÑ^v §½È(@F±,@N±/ÀŽì(Àßè"§yÈ*@V>ȼvcËn™'s76íFÞnäíFVôG±½ ™»±m7òv¿ÝØ·Sî!s6쑹9<{¸WIø÷qo?r÷cß~îíçÞ~¹‡û‘y0R§8‘wy¡m–ù?±;ƒmÅ£:*A~ ×%Е@W])~”¢»¥Ô¯Rt—¢»=e£:å*‡¯œòZâQM94åðVòY)¶C[IÜ*ñ³¹UØU…UġиT·*âVI |èjæó86ødN¼cð4Bw\¾#÷~CÞ1䃶y­2ÇÇ×Zb’ïuØ]‡¬:ñ;ŽcÇqì8ŽßÇá;N|ŽÃ×ÈshD#úñ§þðž€÷$|'á; ßIøNÂsRÖ2÷Çžføšák¾®ÓÄÓÄáÌu¶Bß }«Ø}+¾ž¥ü,ñ=‹}gñé,¾œÅ—³ør›:ež)ó8ù÷ÇÞ•9s}g^ÿß½›ø.lâû¯ÿ“w\“Í™'¾ÛúSßgÎy¯3Çí zsâñNœCî3œ8Oœl~èÌ ç~ê{©ÀùܽšìýSqÐ8^_™{Àæ£ê”)øŠª—Ì!¨‹¡ÙØÇœ€øD)nv´|'á=ŠÕÎsŠu+^öÿú;¶È`ïòÏsHDÇøSá ë6¸ß´‹°~ƒñüpÊÃGw;†ºŸJ,7r½]1Ôù¨`ÅïÝ!:³cPqwÀ‹O;¸—M´ Ø”©KËüÊã:O®eŒ$.y´ïmÈ̓?{ò¨Ÿy}Š“]ˆœBtça[:òð+{¹ŸÇ³Þ;ªËÕÐìDîNäË8æÕeéNäî„w'>•ñ½LÆ-tç£;?_—§ùè:,c2«dÜ’±ŽbÏ®bÏAl< ÿ~øvI‰Œ£è©%–µÐÕË=tÖÊ€ õ2ŽÉØ‚ÍM”5»ýb66¡«™g\̽féÝÚl¥?C÷!|jE_ŸeCÚŒËq º£RÆg3:«°«›Î ¯Z±é :Îðœ#§YúUì?Ãçh àÛMÝšU΃ÈüGð­þ·ÿeÍûòÃ>ùæùêßͺáù ïx•ÿãåóÏÕ¦øØâeUž%׿ÒýRÃëþËï:.Æß~»à¾îÏ:mÈÓzò`ÔÚ/ÿç©QçyzFîúç;J>–9þ|g_ù䲺OøïúÙ¿ìøä#ß‹£?£õäÁ·ÿú™°áŽÛõÉù7¬Ëi«Ç—~;ðÛ_Ûë¿ëÙ5ç?òOêK?xËÜtÚ‘'Lå"OëɃO½Ã+Ã=#~wÕ§ÚÞã¿ô³o>ýÌM»üw=þå¿ùÈÝôd¾ÍŸÿŽG?è?öÙŸý"þ›ði}x(¦äµµÏýÔ3òÞûŸ¼qæø/½ÒõÉ·.hòßõô¯.ȸÇ&åŽÏ¿}q ôúüz{Ïïoï¯?7æ=[Uë¿ô“™-ÉÃ÷¬ß½ç¦ Ÿ¿ã-WwÞ½ä­ði=x芨qåõ¯ÿücq»ôãÅ>øâÿÝ©‰g;žòd›~¯ý}_qÏ|nûx;{\ëÅPúç>vß³³=#¥Q_êz}ÓxÜ~uk¸ÿ»ãõãÝ[¾8³£Ñ㵇ݲñúñ¸Ö¡Êü‚gdÇ/îºí³}ãö¼tÏéç¿ë¿û®ïõ Þ5mÌž¶Ÿ6=Høµ> ½Ô0ú®êÿôŒ$¦ü]ÿÐßù/½ðšù,¯×È“¯/·‡üWQ>üZÞ–ÞðÅf×§µ2q±ÆÚÉ¥§S^«¸mßXû¾û§þ/V–zÆÆ×¶›zr®|óŸÆçk}xø¶÷g$/ôŒÌÛqläŽkþKßyà=ßy¥Óqþw¯”=ìÌ ×zðð«á[›ËÇžÏ ¿¸é3Ûn)Çß.ìûæMþ‹1Ÿê—Öyûœ‘ç¿ÓýüZ/Iì«Ïèøç…ï´ï¾/÷O—¾þÒ÷~ûø¯²ž;¼¹<ëèWý5NžäÑï¸ï!§žŒ·k}yÄîy^øçu﬘ùå±~æÒ=3oŒêÚ46/;ó•öŸ|ùø'=‘Ú>¼/?¡õãÿ{ºéžÞþƒOåßà¿ô…uŸ ½ç¹±~÷âš[|]íeþ³‘>^pl|~ò„Ö‹G]ëk¾TäyaÏ2F´—Çýù使þûÖ{ÇžÇE™•.õ¤?áöš#cöŒÕ×'´žþ Ïó:Žù/õfáÛÛ‚ýwÿâ§]¥ß?>®>¡Ïý1×±=ßyý cíóùáÌÞš³Êé#™w~æ‹ãþÓØ3†žWŸÐzð˜Î¯<Ï÷üËŒ¯ŽÏ›.}üÊ¢·nzhü9j=ò¬uÆÕ—¾öÝŠww#GëÃcïñÎİzž×ù±ÿÒ§ìÕ‰ûØx7î¯>÷Ǹ㷾—÷ÕÃçk>q„.Á駯÷nú’ÿ"½(Ô™§y_~RŸ÷ãA?ºøØÕ=Ï3KûôMãýÁ]µûïMHuæùþ‹¿{íÈÐ\g>æo×þ~|>ð¤>ÿÇ“¦ý¤à楞çç/ýÕõGÇãpׯÖ\|µÅñ¾î_¸å»é?Çž÷“ú¼ïøàùÅ×–xž“YÚúÇ©<µÅú/þûÌ‚s«n÷·/«J»í?o‚^Ÿóã÷Ùêù›W=ÏÝÙ:÷K·þÄéÓ?¯o~øÅñçõë¶;R_ó·—ì¬ØüO_‡OŸ÷㿘>üŸe¿ò<ÇloëýûÇÛÉG?ñ™Ûÿíãõô‡¯^òÒýN?1>=©Ïý‰ÍEO¿ô£oõCÏ™úºÂ‰cϫÛ¾ûþ‹¬fºåÇÌ÷Ugn¾~}ÞOœìËØ=ë'žç¶Ý²›¡Ä©ûÞsïnû˜ÿâ]'¿²5èCcóagÞ46®>©Ïý‰/¤3‚áÿŸºü-g~é¿tÓÍ W<}n¬?½x‡5Pÿƒmž-Úû;òÚßyù™_Œ‡ÃZžxñö´S³B<Ͼ–té“Õ?ô_z爧¥¬ÌQ¦×i·Í:¶ý*bñ ¯Sá×çÿä*™ø~ØóìòÐy;v4ßóò;Sý?5ZÑrËóþöï®üXý¯·@¯ÏûÉž/Ô>0¶Þ|öˆÛý—Þ¿å‘Í×ÆÆ£‹_?;tOãûû®<™¿>ÿ'e”Þ6ÿgÏåu¿åýuãÏSÂpöûãqxU:€mcýF[o÷õêÏÎCžÖ‹'/Ú˜çÙ¼]='‰ŽpYµtÖX½è_(æêØ<ÅOÆêó°Ö'_zÛËi?yÎóìb»Áø/Ýzãæ—>÷Œ¿?|ŽÌümß|K KDèµ> ˲ç‹=ÏȨÇòéÒm›ã þ™±úÜý‘„¯Ï{Åé·Çëðևáð/g^|ÿõ±x>s×`ïœp·ÿÒ‡Ò§Í|ìmþþM²ÐôŒÏsÝëµ={_~JëÁpf˧/ÏÎô<óÎáÏůô_z¿'$÷ò÷ÛËÂ"›´Ò¢Qèõ¹ðÿù3#cÏá™Ò%÷>Ú°kl=é]OÎLëß0¿ŒÿxòÖ{y¼=?¥õa¸ú@ôiÏAÏ3›ïý—ÓJŽo‘…þ‹µÅσ«þvÙƒK×{6:qJëÅp[peHñßÇAÂþôgÆí9}iÿc_߃¢^o›œ ‘ÏoOi½~ÏåµÉc=öôîË´‹úíWÎ…ÿf¬ì_)á[þ?ö¾<ªÊªm_™Q@AF‘QŽˆÂsJMÍRLÍ!MÍ›ÐÒè­”ÒÌ´ÔÒÌœÍL‹Ã#f¹QfEEe8‡Ôpì5Ks(ÓßµŸ{ïç¡S}ë÷Ï·¾o}¯®ÅÂýÜ{¸‡½ï{OìK›oÎÿ¡BÊS?¨T—íÏhþ¹Ž3ŸZVóôÍø5›÷iñ!“6Ïa}e\ÐûÓ1Ñ?6yêá>75¹ê&”ÚÙÿ«uáþ¦=9,¯ÙO§Òßýc—ùÅý³÷*u“ú¾3ŠÕŒ¾°£ýاX^óA‹F,Nd/w=Þÿü  Ɔ㢔Ž)l­X~¿ôü¢^+t{<;¬ñjˆËCçJϯÔíx\ô‡º=¿ŽÜ^©XŽV1ç³ô8ð’Ë=ïÞ]Yž¿ø5ôòrîàî ¼è4_T,Ìmý’>x½Ïϯ›÷éã¡c=Vf³¤ý5¿¦‹ã¢?¨Ë†qŠ%ýÖžúq÷t~æóKtÊby¼øœô¸~\Øýò¢º¥všÝ,˜\%ÕõÔãäbïæ~v;ô~¶úNIL[m=$ý¤6¾ŽS8ÖLÕ¤bqz$®þåÛaó{?¡ù©¼Ñ“8ãÐD/Âþ¿«EÅ\õdœó½wu9DœÉ§.õyæqa÷;ÎÏíy¶bþtÞ†óî»ôñüôØ„K÷GÈý4cà a÷Û‰;>ë¼F1Ã;¸¤Ë»`Œ¡óìíš_Î{,{ø·s2”>}úò.Ê ûß\K˜sÔ¯–ŒŽÖùœ;7{}‹–×cSï”G¶±7/G÷߯Z†éý&³m¯'&F±< Ú~„¶®9!ì-ö+j½=gãÄã¹Í·¬ÿqËóU†î÷NˆqUÌkJíÐòw®½ù¨ý[m€MŸñS‹ ÝÒ_ØOMiý©6ÈÏôЮö)dy!œÁî,ýùƒwµÍC9a¿Ë¿7ûvß3J ýf5Ë/To¸Fïwñµ|æÁÒ}¹B<«v¼|¬Õ`Ój¥fgËV‘{ôñûéÞÈßÛ³¼Ô‡G]¢í—iíV ;6dTýq}—¶R3÷›.½ªžÓõº¶OpÑþIúxìמy›ìÒeÿV“¾Weü³ƒ±&œž¨÷‹õñß/9¯÷Ì¢²ï¿Í¦¿{©àE¿=ú:­JØ›»­Q¥&ìÌü•]Û@ŽÆU_a]V%ìxaa§ý=•êëns°$Ÿ“²wØcÓÕa¦ Ÿ°ŸØï«Þ÷ß…^ºüt¨úIÄ™„»X:èë½*a¿ê£_^7O['T/àL}|,Ouwp|‹åuϺž4}¼¦WáW›È!ìJ^¥zàž³Neëz]rxòÊSú8#©í¯hóîjag,Òß²Uª1ë>øÖ§º?XÌgb,÷Ç'Áù9ÞPNØWì£ËþX…EÑTû7õyÏ;¯ñ»ªôù“˜ÇΈçÔæ¨GØ•æ12¾*UûíŸz ‡ô{ÐKVà 5!ú:èàÃ9Ǻc|Œ¹9lô—P°ë¶†ŠÝU>JÕ‚ÏgmÈ—q˜Õ¬[ëöið-– §8¡4¥oL¼þþPŒÿjaï/f…\ýÀCò­TñÙ´­«1u:èÜ ,«£ç–±tŠ('ì¿zó¹!·Ž*U¾Þˆ˜XÍÞ.A^ßý‘z^Ž«–‡.Ü¿¢Åm|Tü)®Ÿh˜p²ß´2î`ÝÏÝAo=® ÿ1}Èk¿}róßjaÿ)Eó³ã4½øîö—íì´õaMõ3kç9_`y|Û¶zªö^ÙÕ{ÑÛ™Ú<àÄ'ã·½¥'¾«<í²î„ßë Ô#ì¿°x¿RY©œ˜™ï W¨÷¿3[ô© ÑÇ£ôgÒ^5Âîä•Þ¸fî]ÉjÎvZ²¾w•nŸa×)ïn­y|Žr¢mßya­ïÌ%÷Þ?¦÷o?¾1ÅÒiþŽr®"žJ¿~‹úMóu9Ïø¦õÎ ºœ./g•÷u`éY;¦_ÑýO°wòüóùo(ÇËöýñÞjVs¾ü᪥õþý3Ÿ oeéë^¼’rÂÎ|›g„¾zü“V¯¯spg5“/?²¹–å˜8í»sˆ5®î»¾ôNRŽ?Û'ìæœÚù@ ßNh£ƒ5ÏlÜYzDãWóµdç£×>•›q_9®nïMÐå¾ïÇOhXîû[¡9E÷ûµdWõø¬t‰r\ìcÕªË1ì…o¿œê?ó0ò‘ý޾ßlÈÙšöʱÓÞ±eµm¾w5Çײç?Ú}Ï)ùÈ~Gû}²±lœrŒú¾:¶Âwú%6uÛ—¯îF~²Û‘ßÕ OåØÜ;AϾäÍjU7ð›¶cwìàO>C>²Ç‘¼" AåØ[G,mÞeµ 7EœñÖíñÁ­Ï/eÍÔüÀót®‚òd—#/wä+y嘺­ªï'w¹²Êç…ïõõãR”\Ç¿¸id×»=¡²×uZŠxÇÝOo]¾nå[°Ü:;?TÊ^t¶×“ŸÌdŸÃW3øÈV*wn«ª²€üó??8¨7Ë¥s/öRÙõ°Œ,ØÅLv9œ5`F±ß¿”ÊÌÒäo—§³Ú®N7û•ú±Ü§ËxÏÕý‹™ìs˜÷ºø>X_ñmÉ.¬¶óži×’Îiûï¹ýÒºöe?–^½™ïÐëó(3Ùíplä‰ÝŽ(•Ñ/¯¨üðMèeVôNgXÎíEð1,]ÎÛÌd· 1ÿ©lþÚ…ëÿ`µ\+¾³YŽi¾ý” ÃìE¾ 5ü"ò“ý*¾^õÐØ>gd¼QŽnéôCÚwVûPÕ®/ütýç|àþ«¹>ïzžÆê!;VL4Vd4ÜRŽ6­º¸•ÕZüNm}?ø3ç¯6eû±ç¦Ï\ºbÊ‘Ý*Äúí¨XGÔò]¸Áþ,§äÛËCz¸³i¾ß°9b…±ÁBöªh£*H9²ûÉ^6;Z±ÚYü^³y9ËáÛÍo±)ßMŠ|£Oò“½ý>uå£n)GÒ“Ÿýa3V»èn»öÿ¾©­ûrh}À&_Q7PŽìv¨a7 Z<^ÓË‘8uƒ_ïWëíÛWLÑ÷©øöLòVm=¤óAvh}d†ËI<½l]v Ï·' o ?Ù»Lœ÷ËyôÁaNé-~^ï×WûÿÚæ“_ô{üvÆÈÓlÒ»l\úcl8Iv, 4k¬9G)ûºµ7ºøkæ­ŸÇ«ÛS}Ù³r?æ$Ù¯øè½¼W¶ÌÐÛgÆê¨Vè7œæ7Xv_Hfä~ÔI²[1¢ZHÑ¥”æ1ÌÜâŒÝG]o³ìxÿõûf•³gä>ðI²SqË ->ò/¥Ô·Ù–bS_föŸ¾úñ#,{p÷8l¼zì=ùÉ.E{ý¯o|ìG¥¤¡ý/‘•ÑÌ #N±eÏ ?Ùcÿf>ÐÞQŠïÇõ»¼EÛ?A9CçÙÛëýÏ’ìÙØ_Î/^8ý4Ê“}ö‹ýäâš÷§Æ¿8üU¤–ÝÒü`v´>vˆo§È>j ¸¨1>ª»]3óîðÚïl¼¼×qŠìQ÷îñÒü•â×C GrÙyêrß´§Ùh¾-Ýn ò‘þßjöRÒ÷wêôõy¥ˆOG]Þcæüþ–ÝíÖ¹cîlP¨Ú ò“¾¿¥ù©RDóAfþ`þÄú¸/YvÊ;kÞ_Ê—ýå4éwo‘“ùÇ ”¢úv^÷f0óR¯Ÿ~5žeÃPMfiržyšôûêFžRŠ—Í”8‹™—/œ‚es÷xù*×m§IÏßðYèŽJÑ·ûÆÃ0óû=\ú,Ø Ý“9±Ã‰ÂñžÈOúÞó)ÿ·J)¢xÁÌ">›h_ÑØðé;ŸîÇ(E«ŸhÉÌ™±-µG¿ùô»ûük|gV)z‹_¨yŸ™_»9)ìÑ²ß éw×­ž.Áeו¢ç"ø :3Ï|äÄKù “>w«@)zzöæÛ33w[­€NúÛ9ú7“årªRôØÈW.>ÕúÅ-S'þ :ékÇç1ÿö\nPŠÄ>µyÖýäÓí´ûKÈGzÚ^7ØïˆãX¥ˆöØyN?Rô²Ý…o¬4*E Œ7§ cæ…ûÇ߀_®'}l{lî¤Á/|­Ñ~32ê·Û#1/ª'}|½á(½YàhÐIy걺RøkÊ]…~q†äÙ$æµ…jxÃÌ—|·¾0ý:è$ׯçÕ/¥ðkÇW¦¼û%3ÿ´×íéÉËA'yÖÓ¾•N¿eL9õ5üî’Gî+˜ïçŸÖÌØp–äYÍ·Y޾¥¨3f¾ÿæoÍÎÎäZYÜ‹ßìU sO ú~ï&b>~–äS¯MÎ] ;¨çßÌ|ovpÁ’­ “—ñéM›5Já½ÑØ“™ÿèTÞ°t’{ásiy¿¼¾])¢õèøNè$÷¼¯Üá)"•¢v{ü*ÿ ´vÓ‘/0Î’ÜsÔíãÑJÝÛe÷`áç³$÷L²‹RÄO†^—óc=Ÿ¢ˆõ“RÄ·_ßÇ,|Ú6þCÐIþ'è>‘R4²¬¼“ë§Ì’0« í» «ò³´$õF)ÊXáy¢íjféãÿn¶èdg±?¢m ¼¸ª*–YFž¹¾y6æïçTù™Æ_åä÷› 9Ç,ÏßNûÁ{3èªülù[¥ˆDÝd–7Jííïº*?›G÷;”bº÷¢Å;Ë‚v•ˆ£çT=°E¾C®>Z­‹ý7Ë’)ûâ_÷56œWõÀ–òí£{?aþSòÑÍËÑ̲Ì2)©W èªØ2:'RŠé*³|[{tì@ÐI«Û ­•b~‹¦çifYõë7êG‚NzýˆY6üpúv¿7ðä_u…„JñÝ%~Í,_¾væ…`ßó$ÿõØþM¥„GÝ^3ËŽœ²êÒ¾ “ükù5dÛb¥dÞ"~£œYö_)K^Q :É­®n^®QJvZ•Ù³UÜÆ† $÷ü˜åäìŸ_Ý‚xyäݽ`É|ÿñJ‰za³\|˜­LÀüòÉûßl¯”ªÛJ#™åÇÉkJÌÓýì’{Ó/K³v ¬”bÒ÷Ð)f¹:/ûµG_äß,îõ–îºuÄk©Ì‚Yð©ý—A'ù·¸Š~z][¥Tì7Z¯†ØfÍäßúâþÙßülþªß_üS¾“Ü_‰8XæýØÊ±·˜åô ÷}Ëà Iî¬}³޾¨”uj÷áï æIîlu“ª”MQ7L˜åTçÇÆ|ñ9è$wNà–¼í~¤”-ç£61ËicZm^:É›ë¡d)etÌ,,ç]æ½ :É›‡EµÛœ›Jyóä6ÙÁw’3Oô¿òî½|LEטåZfBOGÌ.’œ_óF)çÛ6~GXk”Cî#GIÞmêBH)çÛ#[Ö°ºÕ!.‘¼Û.UO:úÐ/ò«‹~衆\Ä›K$÷ö­oOÛzåå`lëì·²¯°ºNçÞö¿ut’{Ǩ““!R98Ù 9ÞfuIù9†ZÌ#.‘Ü;ÎòSåàÆ¥‡ß[Äêz,ô+¯Æ¼úɽ“Ö)ÊÁúë‡R‡eu=/&:É¿ó#wx’zåPÂr~SÕ¥fuûW+ø¡K$ÿ.±/t(ódç(ÿð™ˆðî “ü»U÷öªr¨L½@Ïê½ÓnEì=cÃe’7fŸK«¿P*°zòº½‹Õ Ï,tÂ<ö2ÉŸ?çÐGÉÑ¥J…zœîÈê& [ÿbqè$ÿ>½ÿé”R±S²ºWßl<|èªüJ…ˆ/u î.›ð+ì}™äÞ³¨æ™W‹Æ(‡Å|½neCúô·Ã@'¹¿á×A~X¢&ùYÝæŸ ï>Ãr©ÍYîÒ{W–ž“çòž¹Ü¯—û˜ò~v?Ãô}bùª†pfâÛIy ™iΫ¹Ï:$3“˜7¤}hf¢sg6Â[ÝÀb¦@>aMeOŠ}6Œ£›Ëµ}¹“û'r)Ó2ŸHËyà_ʪ×2ú)…ö<¾Üñ=¥ìñ/›¥6†jò‰ýl%QÔ/õ ÏÝføóÜJ-Ÿü-ö¯µ´8ÏÑô“-ΓÅß§°Át^ÃLüšYÃbÖŸw“ï1÷Z¡¬/í[²lO~Ñ`{œÖiLý«’йÞ`¦£Ï{³!™‰¸ÉLk×vq8” ¡{uÒ?Êu¨¦©©GÇäzM)¶Œ{RÍþâ^’v"OÜÓÊãÇÊù{åy¢ÒEè±Ý?eêõ¿†³lÝÓf&HûÁáíl8‡2S¿˜™/×ZyL܃2e4ÜüR‰f©âï>Lˆ>Eyë™äÃD÷+™´—GÊmÖ¾KùÅoëþ¢éKÌW•µ øÅˆ¾ÊRuÛtº¼ÿ¬õq¿Oë/ÖãGœ?Ê}vm‰{ºZ?/îiÊ~$îohv–÷ÞMêqÄ8f¤õ$3ñë¯æ²^dwùwKìQÚ÷d&Z÷²ÁâÞ¢é_Ýßn~x3‰{]ÖýBö©/9¿·û³^µrò·<絪ç/z•ãµPu¯0‹ýýÀ·~v`{~~Žß˜”ûíÚ9µô O Z4¾7‹eýÁÿ°#” &}±,u›-šñCÆGVÉu6ËR‰Y–hg0í;²´Oò—qa%§6 ¿:¼nîùMRâž·f_ë´<×’ã_®ë³øŸ'ÞÂLgV/m\Çäýa“:ü»1:ÖÀú‹{á¦íê‹ çØ&qÎiš´tø±ŒŽlhĽé ç×j|ùÿÙ~rŸ]“WÚGŽ[kÿ'û³¸ÏȲ}ø†öu­_Ë¿/3%œpøìët6lÖ{}¿›ÄLüØ2l1{Rôç¬Æ–o|}• ýüø6öýã=MöëƒÆOCÜàÛi ‡™Iì ”ã]ìÇ×6nŸ¼áŽó_ú©¿•½î[õ_‘–çBšýJÔ?›z”YîùÍüåг,w¿’¦ù9q¯@Ó‡•ÿÓì*û“é«|~P‹{OÐýMf¨è±a´/ÿÞ‘’úãÓF‡³lŒ:Í{–™ÄüñiÚÔøµŽƒÖãÑú{!Ÿ†WïýKôú‡údZÞ‹ÉQ¯ÿǰ§ÔíöGäýv­¿È{Ywùa#-üu?¶ùx…&ßѲJ¶ü´v[G6Rôç¬êÖü&!%þÎ ‹ö«å¾ª¦‡Qt¾ÅL´ÿ­ñ)÷å-t.¡Ï‰óP-Ë{9=Ô ¿fOé_d>éåx±ì•o§Íþ §}{fÚ-ÿÎ×6\ÄeS¿Pµ˜’þUÝÞÀƈóS›5a»?îÈÆ‰~ž¥^7ÄxíË/Z¸³g(~[ÏO4{YÅeÍŠql·¬ã¼6¬üØ_òËö¼þì'4=Ëþ%õ%Ïû¿³ø1IØM–U»Í6 ñ–õm‡åãk,KÜûçD,+Ÿ_,®ÖýžŸú‡Blùioéo­çâ\Pr~e5ïûKÚzþ×êÏz±ÎÏžâ»îÆÅZ|–ó>±o¯ñ#ücê «ö|¬æ!VþVóÃUVåÄy®V¿ô³R^ÉôÓb^ð—y¹•ßÓä“~RÎÛÅ8×üšœÏH>¤ÿ‹°šïËø&í‘øók¿bm«8¥¥ÿÁnì)1O÷¸ÿâ÷¤Þåý{éÇÆÓ}Ç¿ôwëqwߊ?k¾¥äßÝþó<õûó½9¯cc鞉Ö$Ÿ2¾Z«q¿ú—çž©Óì`ÝŸ¥?ýKŽg)—”ÇZ^+½jãLÊ3TØ_êMÌ3´~&í¯Í»w©@Zÿ‘ó\9.å¼VÎÆÊþ,üôÃ4ÓâÈxZiýXÞ³ëíï ‡ˆ¿O ³R_Ò¿K{I{XÏs‚­æ?²ÿ[ç—ñC¾+`=¿—¿%®ÍèM=þïVýÿM|šXõpjš¾ÄÿM!™Õ·‘ÐwU¼ÀJþ*Æ5Ò6àÉãÎ}Ëi[¤í0ÎìЗì {_¿³Gß°GÚÁIàR#¯øqDYG¤— ìi¤Ð®ÓVr%Î cº€ÜJ #~–6žŠ+$0M`»–מt¦À5¹,°¤QÞ5O`™€î†´[¹$õ½ÅLÂüswøÑ™„eâÞ=À›xõo­Ý~ di [y"í9E¼¯h&l@/н@÷Ê8Ñ „Uâ¾í ݵqo+B¾6e+:?éô67WÑ<ø‚ß|‡æï>qœ?”ñCÚŸ¿¾ü—Ò{Pꛊý~`™ŽÉÐO`DçÑ{Š*N!ÚD¾ÀJr«*^t½KÉ1L8v Ç>ä…Á)öŽB2èmKþÆbh½ µCº]½ƒÉßüæo.†Až°q?í„¡íö!„'ÃߟâïHñ7ÃQ.ßÂ7Ü?|‹H¸+émpõ]Fð y#¡“Hð‰ïQø…¼QS.`½Éß W±O?:ƒp£Ágt}üiôðƒúb@) 7ÓùÛ—ü+Ž“Â1R8>Jè¯ÃRÂWìPF؉±h;6ê0«AïèDïmñ7Õù[Y*¶Ja©tqh#.pã@=åãP>®žBU<Êǃ?E`n wº8> »’¿uÎß“ä¸,‹EÅ‚Ì#LÇ´a@´aݺtèÐ  wr'ŒlþÆûƒØÞìAlÏlö ¶ÿ§Äv>–ÓI&õÝkþæ ôËñrm¶AÚi´i‹qe‹¾c‹´-ÒvGv™ø¼ö!ø‡ØÞiø¤×í9¢¬#ÒŽ°½#Ê:!í„v`+g´ãŒºœQ·3åNâ]eämQI.¦¥‘ÞTn Û´D}. », w•ù[Ê®(ïŠò®ùâeÐÝvCº•“À)CþV¨ß¼¹£>÷„QæÞ=À›xõo­}ÅÛÉ¥5lቴgºÀ$CÚ å½@÷Ý«€Þ~VñÈ0.½Ñw½¡»6¾‹ òµá±<ûÀoùdƆŠC|Áƒ/Ò¾· ‹C}+eüöGÚ|ù¯$<,pۢζÈ€oøAï&s7˜DøÂÈ^‚À_¾e6KYÅ#C¹`ð ™B`è($“Þ©æxÄ¡#F/tÖévFzkR} u„Až0´†t˜™Ütû(Â!áXmü}H« åÂñ-ßÂñ-ß"ð-"Þ¬äïFªï-ƒÇHÈ D‚‡H|Â÷(äBÞ¨ â}ËÛô®³ú3òGƒçhТÁg4øŠq"\àôðƒúb@)#<:2Ô7›Ä;ÍÐ]èª+òu@ýПbÑn,è]!,ô›.ð…óc8y:º Ü”4zÛR}ç|w„.:Þ&\æ8È—FxËq Ç¡|ÊÇxðòñ ÇƒzüVÂV‰¯§0¦¾iXk„ñœ€6ÐFÚ0  Ú0€nݺtè†Û„áÂ߃æo{ªq„ÿã±ÇtÛÆê¦ñYÆe´¯Æaoåû´2†ò¸Éc&øÓb%‹¼-÷x¼ãñMƵñÍôX&ã—ŒOMãÑßÅ!x쑸§MãŒ32ÆÈØ»k1„Ç;xÜూÇšÆù-÷ñÒ¿sßÞÔŸK_ÞÔÃnªïæ>[újî—¥O–¾˜ûá;Bÿô.¾ ú†ä³Cöè¿°ƒú¯¾;â·#dq‚m¡ï–è;-ÑO\ñ7èÉ­¾ ýʽ½ ïü(ç ûy!Ÿ7úWüß'Ìë‹|¾eâ]vÔé¿ÞbWqÐ{ãAFÂ÷æø7Áè_Á[ ¯/ø2½¯â–£Ív¾ô޽ú¾:ò‡¡­®ÈßÿOJ"ÜŸpÐ;óñZ²/ራ8ã¾»¯ž°Â9nxb:áøÉwÈcÐnêK½'xJF¹dô±¼èMá?œ^R!S äIE}=3·[A})ü;òÑn"ƈ]QŸz t•z›¦~æ¿ÍÌ3›=˜ÿþ§Ì}…½Á³Šë‚¾Õúkl¶‡èËuÚbÜØ"¯-è¶HÛñ[Û!¯=Æœ=ÊÚööÏ!?è‡ùä2QÖiGØÎevB?t‚®ÑŽ3Æ”3êv®¾C-ÐZ ®–H·ì'°z+Éݸ€î²TàõBWÐ]QÞµ@`ô‚î†~ê[µr8"Èß õ¹CfwÐÝ—vˆü‰xó¯à­uˆÀå…,­¡kOÐ=37Äi/”÷Ý t¯J]x#7äó®'œ3u¶1“ÛóÌ>™„ɫ℀_ðà[Iîãñª!äý‘ö‡Ný·þ¯Š½‹vÛB/mÍä6ÐN¾do ê D¹@´ˆ|à%üA?A OÅæ…ŒÁÐI0mH‡€·¤CFo(è¡H‡BÆP´Õévý˘¿ƒÎqyÃP& 2‡!†vÚ£íöIo/Ÿ0{Uœ^” G¹pñ-ß"ð-"ƒpû8†/ÇîUqF o$t >#ñ= ߣ7 y£¶¾wýÑ!Óù£Ás4hÑÐc4d‰¼1ÐU ô=Ä ¾˜üuN#œ®à©óÂ)é€ÿ'òù/tÖmt€|±h;ùc‘'íÇB±+ôØÛ„!Èqe8– Ó]Å ßë)ÅÇ8Ðâ2Ç7ô8”ñî„Íòñ Åóÿç†Lçqôf¼ú>|a¤¨8Ãà1ü% „z q´a€Þ  @7€nÝPOᯓ¯xÛžÇî‹ù?Ûe\—ñ\ÆrkÜ6¿eÜn«ÑöŸb4Ï<&ËX,c0¿2æ6±Sy<•±”ÇѦqóŸbå#›ÆF%–éÿ{ÿ„cjã¬ãk2¦5a Éã5ù¢.ß•· ¶õC;þN„Î1=ýÁkÛq„ãÙ6 H Œ¦@wÂ-çØœ|ªÉq3» ï;å‚o¾7Ç<¡øZO˜à} ï< }.mu¯íQ6‰¯Æ.zç(Âíl$Ì«äÂUñFÝ c‚c‡§ _Ja€wÇïÔ‘8…0—8¾RbayvÈ'\òðÛéTð›Šo©¨;6IA:õ§pZa ÷D=)¼m¾N@[©œv™ÿÅ~ C ¯¯ i#äH¿)à/rAO!6Q2ø+-øáu ¿ÞŒÈkì'úÿ÷`Ýñß·îx°æøŸ_sü§¯7¢„­Ì'ý¢9tÓmØ mÃãH [ôw[ØÝ¶€\Òv°“Ú³ÇX²GY{ØÅü; ^‡Lü@WŽðŽFü€î˜Onà e@w‚®œáœáãœÁ‡3ʶ@ºÅ8ü ®H·Dºå]>]àW\@wAÚõ»¢BW“À›k¥À,DÚ õ¹!+ÈÑ ã¥ò·B}îH»£¼;xwïðàÍryÜx…Èßí·FÚ²y‚WOÐ=‘ß ²yî…ò^¨ÏíyCoäñF¿ó¾F8Ù*>!è>àÏtðã~|‘ßíû¢¼/Ò~ û¥¡ø÷‡|þÈïÝûC_þ·&!ÚlËãê @™| @=;åQo ÊB–@ðá[êZJxŒ*f!øFÝÁùäbC`›Ô²”p C!(è¡à/u´‹"ìo{©@`¢LØÂÄC=íÁsû~„ë­bjCá¨<å‘/yÂñ-ß"ð-"“p¿#® \C”D›‘ÐA$øŒÄ÷(|BÞ(äÊ# qŽã%0‘?Ÿÿ“ë »ÿ.V7Ñ<Ë8Ì.c¯Œ¹\2ÆòØÚ4¦òxÊ㨌Ÿ<^ÊXÉc$…2òØ'㞌w2Ö5s<Æ5m<®5e2ŽYǰŠ]Mc–uœ²Þ³ç±¨iÌáqF®dLáñ„ÇGx ù»ØÑt_ƈ¦käž½ôóÒ¿K_~[è%áºÙðèÂmØÁ¾v#vàÙùìÁ«=ú†ò;æ8¡?:£l Ø·ôÜ¿[ŽƒOõ%\XWô WäqC7ðÓ*Šð^Ý‘×vö€~[£^O#~PŸêöFnƒt¤}PÎuú"¿Ÿ»À_E}þ¨ß|úд¯-ŸÓ£®.èÏ]P6`)a­¢î ”銶‚V lU” F¹È‚o!õ4} EžPÈŠñÓòwíG¸ª]Ó‡´}aÎu/á(ßù# _52' k´KºÀU…I¨£+Úë Þ“P><ö„<=ù´ž|^Ф“QGOþ=0N{¢Þžà¿'ôÞõÆ£l ÚLásnð˜¼>´käsoðdDF”QÇ*ÿ—ÔäÿFaófÍþwÍËÿ/Íɹîÿ»çåæäÎþ«9y?¡ëzÍ™‰ÈßuØ mƒ´ äµÅ8·E¶…]m!ŸƵú¯]¹{#~׺µGYŒ-ôäuD^G´ãº#êvBÚ u9¡.'´ë‚ŸqøÙ °ÅÑo[ Þ³e”À­%òº ¯ Ò.H»@®ð®(늺\AwÝ i7ÐÝP¾U‚ÀGi…´;êsGÚ}+á†{€oðéQF.ªua†·Î'wåÙO`‡ƒî‰ü^ {î…ò^—v8dñFÞÍyÚô#¬pŽWÝß| ¯òøl Ìp_ðà‹:|Q‡/Ò~Á<û?êôÏ$¬kŽ·Þºo >Ûb̶_ '”ï>Eà†£L Ê¢@” Dž@Ø ß‚PoÐÂ<åî”ã…#O0ê _ɨ3ôÐSä E;¡ø ý‡B–v°qÈÓn)a‰‡¡LwH‡A¶0”iïK¸¬*Öj™À‡ŒáøŽoáøŽoøoÐUD¹ðDüNBº3OƒHä‹B¾D´…¼Q Eå–+Çï”'pÂÑv'äéÙ;AÖÎøgü¿ó5ró]ÀgÈßru) ¼ð. w¹F! +è]AïÝvÿÝÐf7è¦dë6‚0ÝAK-‘óÃã/t“z"ú]"êJD]‰à7 ùŒ°Cò&¡Î$È‘>“ÐnÚMª'<Ù¤knºCŽî w½;èÝAïZ2Ê%ÿ?öÞºŠ#yôfˆD˜ 2Î"Z$]QBñ*ç,„„£ÖiYGÖfqÆF`œq9a‚±eÛr±Á‹q¯ÓûÕtÏ‹VÞ·çûvßÛ·sޏ÷ÎTUWuWMWõtWq}1}±T¾ÓÎbðÓÆÚXB_„ð·dƒªÿº„~X"°|_ ÞR~/•ßÐ\Ú©êš[ÏWù×}ïiîîiÎîi®îiŽö~ kÏÉ2Ëì=ÿzûí2çzϵ2Ïö4¿z¿9µû\jÏŸ¿äÛ{ûõ2GÚ~½÷ü(óâ?»§'ßþ·ÇžãìyÍû½€½/G汞֔ºÏ[ö|eÏMö¼ÔÓ|ÔÓ<ÔýÝ@W/5çØó=ÏxÇÞóJOóI¢–<ƒ16èŸÞ|ö–Ot±zÕGæ ô¨o>2W ƒýç~Ðèô§ÿú£»€€<>|ú€ãƒÎû@w ôò9ˆgÔ ðcãÚ`ltˆ?À áþP~…ßa´?Œ¾vDÕ‹µN{Ãág4æñ ÑγÞçñ{<Ìg¼GÉsdRýÐ\óám,¼œ¾‘kÐ:vÇÑæðÇÑæé\›ìéÛµ{ í@ø DÖ àLf¿ó7—6&q‘ð Ï5ښ̟?ò.wy¶@w‘Ì#bÛÜ›Ë÷¹bûnå¦.£ï–ÉsŒ±[íüÍ£íyà-þù„\AÀÁÃdXvT¹¿AåÊÍ ‚—eÐ ‚EЂ¯ hºxÞ¹h3k.è/æ{04‚øt,xð ¼Kb ú3X®s?H®#o0rÃ{}‚L!ð$0—\c\ð ¿!’­ŽOx ¦½ Ú ¡ã:*™ùƒ¯MZÿäß¿*.ùO~Wð¯ˆIþ]ïzŠCþ“ß ü·Å ÿ ñ‡[÷8F0Œ—l0½ùݛ߽á·6Ù'Ÿ?Ƭ¿ûúó‡Þõ¥ŸúaËýÐí~íÊüûcýë€À½èÉúŇß>Ðñal}hs ¶::áa ¿a›ƒZùƒÖ ~ÇïãÐÑã¸ræ÷`tz°Ì9Ð"óøC 7ø¡ÜÊï¡ÜŠ ÃaøÃøîË=_¾û2öÃy> ‡¯áÈ7yFð{°#h{„ÄüIߌDŽ‘ê5Jæ'ø­Ñ´;šû£Á üàÇ@oŒÄð9–þ‹œcác,¿ýhÛ|?ðýø=üqð9üñ|îxúh÷çs>÷p÷H|A?.¤ÒÞBúe!¼Ó§§sýt®Ÿ.ó(¼ž(Cù_:KùÜ©¦…@`g«) é·@Ú _ø–k´»}\Ľàq÷‰ÿÁ½ÅÀ,æÞbd]L,FQîøÚ_‚ÜKè‹%ÐX‚¾,ÎÅý`ð]ð½T~ó,±¸Käy"ó†üû¥8£{Œ!s­Ì³ö>ûîó¨=_zÏ=Å özO±‚÷:›÷œ&ó™Ìaö!™£ºÇôÁ?åó{Ï3¿ä»÷ä‹Ë³Xž¿¶Ï-ÏÔîkïÞÏQï½óèª50–rèGo>{˳‘¾ì#ÏEt¤/rõG—úC{cäü@ðò9e„,ƒù̘A'†p}¨øËж]¹H¾Œóp¾ ‡ö<è@_FÒþ<úgø£øCÿŽAWð7–¶È3‡>XèÈxÚ>Øù\ŸÌ\Ú äo¢è%ø  ¹hr©E÷àažèã»þ—Â×\ð¦ÑÎh-f™Ð Ï—ò·LüV±W¾/Uô—ÀK0¸Áôc´Bзl/Ü膭ðB-¼ñA…Þlý»E_Û¤üXëºà´k튮¬©‹¿²Sí‘<Ù“oánWkðÖZÈv½Ò_¿›\§ßMÒg|õßý~r»:çk­‹øé³¾n½'rƒ^éòzWÕm_dû?8ó»Ñ9ók ò×ç}Ýι ô®ÒzO¹]ï‰<ÒÙ uj‘u.¨C¿³ì¥÷Föp6¨Óëe ×ÞÈúÌoW·sAùz_ä†cÏy¿”8KtÇZ3êTû©ä½‹ÄrWYëBGÔzõ\NÔû¾Ö©ý\ò>GÎYÏèýL®SûE%”MÎaÈ\!ºg=W;5yc½§)W1œµVÓ¡ÞÃXç2ºtæ¯÷V¹ULf½ç)W1˜ÄEÖÞ©CÚýÒº¶Në%tBºtH@ü„Ñoa܃Ç0ðÃàk9×–#÷rp–ƒ³záðNÛÈA›´L8ÀD@7¹"ù ‘ðE_D| òÅÀO üÄÐ^ ´cÀ‹v,|ÄÒ/±ðKû±àÇ‚~<ðñÀÇ|<ð ð’½D>庉À%—\"p+ù\)Ÿ‡T“ïIÈœD¿%—L[ÉÜO†ïdÆ …¶R •Š\©ÜOEŽTøH…4ô,>Oç~:²¤“ެéðšÞ©¦ 7pÐÈ@Þ `2€É 2à#“{YÜË‚F²fA? úYàfwª©&Ú9àåÐ÷9𖃾äB?—û¹ð—yàçÑX}•L>üçC»€{ÈV@@»¸"Ú)’ï´Sԩ°bÚ(§œRpJÁ)§¸Rø,¯¼r~—ƒWÞ©¦¾ ð*é‹Jø¨B¾*x¨‚‡*pª¸¾ š« YÍhÖ@³Ùk࿞1ª§œzpêÁ©§þ¨ ïš Ý„ÜMÈÝ„LMÐZ kÁ7T2áÿ¬÷ŠÿmûýDyÿoíùcÌÿǼ[üõŒÑ?ŽïåLáüýÈßOüý¬óèðŸíؾíؾíôY|lßÀö lßÀö lßÀöaú¬¶o`û¶o`û¶o`ûÆX½'Û7°}Û7°}Û7°}c²Þ{ˆíؾíؾíؾ1Kío1°}Û7°}Û7°}Û7NÓïI±}Û7°}Û7°}Û7N×kߨ¾íؾíؾí.½Îíؾíؾíؾ©}zlßÀö lßÀö lßÀöƒÛ7°}Û7°}Û7°}Û·ò`û¶o`û¶o`û¶o`ûrÞÖÀö lßÀö lßÀö lßÀö­s`ؾíؾíؾíؾì÷7°}Û7°}Û7°}Û7ˆ¬}Ø¿ýØ¿ýØ¿ýØ¿õŽû7°û7°û7°û—÷öo`ÿöo`ÿöo`ÿöo­aÿöo`ÿöo`ÿöo`ÿØ¿ýØ¿ýØ¿ýØ¿•¿ û7°û7°û7°û·r@`ÿöo`ÿöo`ÿöo`ÿÖ9ìßÀþ ìßÀþ ìßÀþ ìßÚÓŠýØ¿ýØ¿ýØ¿ý[ï×±û7°û7°û7°ký û7°û7°û7°û—ÜSöo`ÿöo`ÿöo`ÿö/ù+ ìßÀþ ìßÀþ ìßÀþ ì_Î÷Ø¿ýØ¿ýØ¿ý[s³Ì½uê9gÙjT/Ï^]+/F¢Š5­sQ:/Æv½O7XçÅØ©ób$zå¼ò÷Ê‹qH¿ãIô: s^mÔ{u}ô¹ úl ŸÞ«»^彲λÕû+/F€Þ«»^¯Íùëµ¹mNlkç¼²öøë½º½Î–«÷DÖÙÀ}6p“Î{5[Ÿ3Y¯ã`½_·E7±öèýÛ¼öë&:ëtÖþ€cƒ>è¯×ê6èý~úŒú:ãÞÓ¥ó_mÔ{Û|t¬:u†ÝZ»óÑ9°6¨<ÖÚ]¢>#¸MçÈÐ92Ö«ÜZ» Öù¯6éüW>zœ[½—²Î vê½pz oƒ: /益øëœTÎ kï@€Ú?à97Ø¥÷«sòv^,+wF€×ºÞN½§×OŸ¬Ók{íz_ïl½`½ÞWwDçÐÖ94Zõ^‚vG£—Þ߬×ùZu>vµ¯ÀÊ•ÐmŸïNµ?ðsjlòÊ©á£seE9ç ­³„;ÿ7y²ÚÕ>ßÏ®×{}ÛuN#ÝöûºÕÙBϹÂ./Ë¿Û~ßM:§Æ¡ng ËÕy¿cÏÚ¹°¬=&ëôž‹vµÏÂ:{Ô¢öµÈ»Ekó¨Zß´öN¸uÆzy¾:›$ïM­=jE’}ŒòîTö~ȹ"kßC»zG(k‘r¶)>_íy”ý²ŸQÖy¬=j=HÞwÆøªuY“±Îå«÷˜’AòÈ{Jë½a§^¿ôÑk6uzÍz!è_´B¹†ÜÉàçÓOnxI§?h/™¾ ƒÇå"8Ë¡Nû´l´n}A›ÐLåZ$t"ù ‘Љ¢/¢€â^ òÅÐV üÄÀO ´c íXú%–þ¥ŸbÁ·ëqàÇ|<ðñÀÇs=^ —Èg¢|B7¸DàKn%Ÿ+åø$äM‚÷$ú,˜Jp’i+¹C…)üNEžJúÕ-²“Š©ð’ /iôK÷ÒKG–t`Ó‘5>J§ßÜÐw—¿È›L0ôO|dr/‹{YÐÈBÖ,ègA? ÜlþrÀÏvx9ô}¼å /¹ÐÏíR!L<䟇 yôU>0ùðŸíî0žôA´‹€+¢"ùN;EÐ(¦bÚ(§œRpJÁ)§¸Rø,¯¼r~—ƒW^xàUvªÐ¨ ùªà¡ ªÀ©âú*h®‚f 4k YÍd¯ÿzƨœzpêÁ©§¾S…RMÐn‚vr7!w25Ak-|¬ßò¿åŸÄÍÞñ²+KŒ,ñq÷ýõ{ÇÂvÜ+ÇžâÜ€^Nl+q­ÓJ·øÛ]ÚÏþ³ò¯­u+‰ZU[VžúÜt»>çÖá•Kn¶Êƒd½Çv«sÓVÎ ·WŽ8_u–BÞQ[ù16è÷Òu:×k‹Î7[ŸG«S禭s-*W’•û-@ç½èÔç,Zõ{ç@õîÙ:_6[ïc=ää°°ö¯nÒ9ÛZtž¶Nµ$&ùW%'›¼'•¥7Ù—)¹Uå}œ“w§²—Jò ÉiÉÝ%û"%?§<~¬w—êý¥ìs’|²I–Ùä}®õ>ÓGåT’ýA²wHÞ•ÊÙdÉ£$9V€Íïh䋿wt”z'´Ø´»Bæ[pc}t/䎆¿ð£êq¶>W‚³¹"‘/Zærî§Ê\ ½xîÅs/¸d~'@ÓM¹7©¥¸TäJ¥ßS¹— Œ™2¹—)ó ²fB/3Ák ÿèÓj~WÃ_ ¸ À50~ÕÜ/”kÂôká»™±l†§îUÂO2Öªå¸FdjäZ#m7ƒßL[uðÚ fÚl>¢–êr¡ßl ò— G>ú–Ïtª >*Á)â^´Ê¤}ÚÌGþJtpm•ð½¾+Á+ƒf<¯§®E=ÒË¡½ Z•B¹ë€m·Rhð½Žk« YŸ-Â;H%ð»Šqh§Žï-Ы®‘¾i·Ž>\Å÷UôC ð«D`å7t믑kuôK‹´ÁX¶€ß­Jú¡ YZ ÑÈ÷FdlD®FéðZøµÈ¼¸µÈ³–6 Uì°×¯ëÖ½þ;×­ÿ§‹ùuíú×µë_×®ÿ;×®ÿ'­W˜٢žM–ý%êqØ©ó¹Ug+¯D¢Îc·ÓëÜò:'ï¨U£aƒÎc óØmóªÑ¯òJX¹‹uçMú|³ŸsŽÍÊcä¯Î:{rÙùé³lÛun£@¯³l‡t†¯<ÎÁN~Së,C€×Y¶.}–­Eí'µrK«sÒž\v:·ÄFËÙWŸg[§òKXu‚Õ.©Óà9ÏæöªÓÐKç—¨S{¼¬½¥jŸ—µ·ôˆ>wíV9”$¯ªU«Á_ç™hÑ9»t®‰`u¶ÍÊkש÷šºu^».ÓÙ­óLlWg&ŽÉk·A×lðÑyíZu͆C:¯s Îm·^團ܥιYûN[ÔY7Éqgí= Ðyî6ê\®½t ‡|b»Îõì§Ï¼µxå{öÑùîÊu-‡}îÍ_ç¡hÑõvê³oz?êuöM΂[yïÊÕ¾Q+ïó&â®ë0[ç¿+×çà6yÕwð롾ö.ž•ŸÂWç€Nì–›¢ãØÐ—ÿy§“—bq°“›BòÒÊ™q 圇ì;•s!V‡|µoÔÎO!ù¤Nm÷Ê=»[‡m*Þ1ù)u}‡uŸŸBò¯Ó¡ÎåÈùÉç+y°¬s8­:o‹Ú÷+{k]]êÜ‹äì•31Ö>Ä j/aúPî…!w2ÇÐvü„ÁÏrø‹€ÿåÐ]Îrp–ƒ³záÀ…ÓßÈA›ÀDÐ7Ê5€n$t"á!²K¹óQЊ> ø䋟ø‰v ´c íXÚ…—Xú)üØ.åúÇ|<ðñÀÇ|¼$@/‘ÏDù„n"p‰À%—ÜJ>Wò™„¬•ðD¿%ÁTý– \2m%s?™1HN!tW“ºA…©ð‘ڥ‹4ðÓÀM§­tdIGötdM§ÏÒÁwûªÐ#Yë ŸL0ôOÆvŽd!K4²5‹6² Ÿn69àç@;¼UÈR‡ŽäH,JŸ4Ò¹yÐÌ'žó;USOÜ+àz#°EÐ.‚v‘ðí"èC·º%*Ì)§œR`Ê‘µÞÊÁ+¯\®W^xàU"vR…\UðP?UàTq}U§ j YÍú¿ykè“zú³œzpêÁ©§œÕÐj‚v´›µ ¾›© Zkác-øâÃZÿ¼cå_Š“)Fþ¥øØ;.–˜Xâaï8Ø;ö¥’ØÈއ¼ãŸžâô×çHŒãÓ Ç=Æ1Ýã‰[ìxEâ”ü^*>±c‰?ìØ£{Ü!1‡Ä_´öRq…ÄGØ1DOñïëéÎzº/µ&#ô|uÿtè|rùª®€•»b]·¼ëuîê|•—ÔÊ#¨ó“n×5Úuþ¸§b“Î7[ç/ªS5Yä,˜Ú:o|HŸõjÕ9&¢T®7É#1:ß«NJ°®vDç‡Þæäj“³Ev}9[ì©o–¯ó>·ëükuŽçDUßDÎ Ë™%ÉÏe娮ÎaH.É lçì’e=Éj½èÒçwwª3Bò å3´Kk’|irvɪ]0[Õ3R³`ÙFU—$ž{ñ¾êŒÔ-sáȵ¾¢éïhøŽ.WõÃÂÄo:aÐ ƒŸèõ*'S4cé‹X>£¡M;‘àE#Cô6•§!’~‰Fþú0£á;ZÑ|O†n$´VJ›ð²RÚ=¢–#ãéïdè&C#–ïi‡T½„4xMf%ã™ nÚqsÝÝ®Î|¬lU9—RÁ-äz:ügSÂg:íer-²ù̧ï%\ϧ|ðj‘£9j|ø*f5¼×[ lƒ\‡ÏBú± ¸®ç <0 \k¦ ˜2è”ÑF3ýÑ |%ýV u|ÖÑÞ*èÖ![²4 Sí7r¯Üfhä‚ߌÌÍÜËåZ 8-À—ÀS÷ó‘?YK _FÛ•Œ]ô‹ [^ vOßVÒö*h—𽚕à•A³ˆþYN4Êi·\x‚V¥Ð¤¿ê€m¾…ß븶 šåðÐ"¼B§ÙW¡¿àÔñ½z•À5Ò-àÖÑ«ø¾Jø~ýÑl£ü†n:Ðȵ:ú e£Zêm¿Z•ÈX…,-Ðhä{#26"W#¼·€×rDMÛk‘y-pk‘g-müº_½×¯ëþ­½~]÷ÿuÝÿ×uoù×5ÿÿ¬5ÿÿ©ûÕeî[§ž1–¹uŸvê<¥åªŽÈÊa×镇hƒ®Q ëØlÒùKƒu^év¯:Žu:/‘®]Ъê8Z¹‰ü½rIÒ¹$ê¼rKÏÖ¹$vê¼ÒÁ:—Ä6¯:Žët_]ÇqªcsL.‰m:ŸQ ®ãØ®ó%ê\ví:—]°Îe·IײñSyެœ;u-Ç(]§¹]å<²òJ”{Õ²ñÑùìZt=Ç£º~B®Ó|Tç³+×ùì:t=ÇÙ:ë:UÓÆª×ì§sM¯S5¢jáü]Èí=Ôwvëëÿ¾¾³]ãFò H~,+G`‡Ê (52$—ä°rsÒ99fë\å*°ÔðXØ®òo[yÿf«:{’Cwy¹Îû·]ÕÇœ‰V®m:Æ•S[r , Ö¹x[U?É•!9y­\]*ÿ–äÇ’¼\’ó*•Ïä:W«Uå.\’¯ 6Jå(pwèœÜswêœÐ …Nü„Á_÷Ã=>àg9ô–¿\øg,b•»Ð'˜ú%˜èÆB+º‘´ ÏQðE?D| <ÅÀO mÅ@;¼˜.íîÓ'%ÀÅÂC,ø±àÇ~<ðñÀÇߥÂáz‰|&Ê'tK.¸DàVò¹R>á) Y“è¯$äH6‰>«äw2c’Œ¼)´•­TøL…N*tRá#>Ò¸—†Üià¦ÓV:0éÈžÎø¥Ó·é໡Ÿ\42£úÀeW½ xÉB–,hd!kô³ Ÿn69àç@;œt#Ú9ÐÎ&ò ~´ó L>¼ç#_÷ ீþ/€v´‹h§ˆïEòE´QLÅ´QN)8¥à”‚S UðW |9xåò ^9xåàU€W^%|Ta/UðPUŒUxU\_ÍhÖ@³š5È\ƒnÔ C=}RN=8õàÔƒSÎjh5A» ÚMÈÝ„LMðÒ­µð±|ñ­â@K¬ì½æïÛKM\K ,qo÷<ÏÝcZø;¦¶”w ûK±«Ä­¢üÞõ¥ì˜Ô;õÎ%iÇžgJ½Øùz×»]çÛàUæˆÊ/-u^¬nu:[ Î þq­ºfäv]'2Ø«fK¢ªÙbå:jUùެmÁª6¤äg“œlV¶]÷1Q×=nt€ª+lÕóÕ5[T}óq*ŸšÔ°ê¦t¨\ÌVmêv•gSòI#+O×6U@êŒÏ9¢jWKNQÉdå;Z¯ê*J~O+Op/U?\rK~ãPñލú`V.cUG@RÓ+šßÑôQ8øÑȨj ¬€ör·Zª[Xp£¡M[±ÀÇAÑÐCŽH¹×¡jxK‘ÚHc|âM6^¢¡N&rÅC{%ö—Í÷•ÐLçzr¢ª‰– Íd·Êu¶’q\‰Œ+¡™Œí–ñ=¹\å2Ne| é‹ü@•Û,“örÏ& ~jé»Zú5[hÂW <"W ðµü®¦_ªá©ðZ,xhÔò»Ï`Ë ¿ ÞV!O-°«üÔra3ãSǵ:dk¦ÍJù¤ÝfølF¦UÈÝ ½F>i·™öê§¹’¡ÙuÈÓHŸ7"G*ü·@#Mú ˜t®¥C¯ÜtÚÍ€N†øð’ŵ,`²à! ¼,hg“Í_4rÀÍÉxÉAþäÈyó Ÿ~¼äÑïyÀäK"s÷ ૾  ]í"Ú)â{‘|‡FmÓF1m”€S N)8¥à”WŠì¥Ð+¯œßåà•ƒW^xàUÂGòTÁCÛu¾bÚØâ•Ç3XçÂߦóƒÜ:]»¥ò{n×ut£t­”N]+Å­knu¨inz0 üaí:ÿg”®³»SçuëœÅüÁïQ*¾Ôæ²jïú«ü #‘{d§W­ôjʨk¥?ÆOÕÆ²ò·ªzéžú…­ºVz/»¸Nå.–ú¸ãu½”õªV¯ÔÎZÂïñn]Ã>ŽŸ­ë¥€sü¯üÅt½/?•ûßÊaܪêMìÒ9Œó­aJ{aªŽ¡ä¼ëÒuÓ‘Õß­rÕ[µ }tý”:]?¸©~ºvzÎi¼S×OŸ­ò™J]C©iè©£¥ënSõ>–ù{å7æ÷ŒNU_d&}?3Ø«Žz§®ÑçÖuúZt­¾ ª^Ÿäj•ÚRkÀª°ÓÉc,uʤöØd^A[I­*‡«UÃd§Ê-W§k˜Uu¥>ñ"U_xQ°ªA¶(_Õ¤‘š¡åª¶˜wÉ,µ‚3øÌ oR¤?ݺ^p¹ªC,õˆÃÁÚ©ë ÓfÐQU{@r%K~d©1 õV¬úÀ‡J{\ åZ(×Båø¡èôrø\N_ç¡Ï\/ åŒçrøGÞppÂéÏp®‡N8íF‚ÉxD‚ |$ð‘ð QÀFl°QÈuD§<€$îGƒ¿‚ÏÀÅñ—BqðNœô'rÅÕKQðŸ^¼$—€ ðš|"0+éï•Ð[ ?+á§Xb%úÝMŸ§›½$è%› \1tRh;EÚ¥½ékøK…N&xià§“F[iÀ¥ÑV°iÀº‘Ï͸¸Ñ70«ùž‹üÕŒq&Ÿ™ôC&íf—ɽLà2¡—Íõlú'›1ÊFžl®g#S64rÁÍå~.÷sÁËf5m¯†÷5ü^ ùð–|ùÈW|!mr¯6 ¡UH…à[ ¿ÅôM1r#w1r—!×jä,·Œ¶Êh«ŒvÊÀ]NøàWÈwd­@Ö à+è—jàªi§¾«á¹ž«á­švj‘¹šµðS ?µÀÕB³¸ô¿v¸ßÀýî7p¿AÚ„§fdjF¦fdj†N3|5Ã×Zþ,JþÙñHOqˆر‡½N*Åî±F÷uÓ_Z3µc‰측§ýÐ2ÏI,ГßoûûÞ¾¾wñómÿÞ{?NO¾½wÍÛ‡§ÿŽ©¥)>{w?Ý{MUüoÆÖ³GZ|îž|íž|loßÚö¥íý.=ùÐÝ}çî¾²·oìûCüážüàžüßžüÞžüÝžüÜžü[ñkí=Óâ×Úþ¬ø²“´cû®Þëµâ¯ŠŸ*>ªø§=ù¥Þû§mÿÓö=»ûœÝ×r½×q»û“¶Ù} ×öiýÖöÑ!kÝUú*çzTª ´ÂݪFO(m­€føqÐL oâ ­„DUO N`àI2ÇcŸqð· øàEqmŸ)àFÑ^ xQô¿º)ÀÅ ÐÌ9”ë)À'Ê<*ó¼…s?‡~Œâw×3èÃldσ¿$øMâ36Sd^‚fã'>ý\ ó íÅ\œÌÁÈYLq2ç@»TÚ¾}Hãw6|—ÒN"ã•K_äÒVÁ!õx¬jxªá^|¤‰ÈU ÜjäÏäÞj™Où]+ßåø«á¡˜ö³‘³úy´ÙÕô]tVÓ…¾j)dЂBdY-ó0«á¿ :kÀ/–ùzeÜ_Ã8Õ_|¼¬FŽÕÈ]Íõ52§ Úª…·5ܯ¥í5Ð\èqßUóÆSWØ~-ª}] [À÷ÜOºtM0·®GÜ®ëäp 2 ä÷ à%êº`ÐÄýãÄ÷Û k‚?˜ûƒ[½jÙ»uM°Cºe¾®I ßø?ŒûÃhoÏ_èû‚ïË}_ð‡£÷ÃóUÍ«N¥ø‰‰º6üŒ ý‘Љì#‘}$2Œ‚‡QàŒ‚Æ(pFCs42Œg4<áþ‚`]«¹ÆìôªKÌï±àøã¨ë„Áç¼`5ÝÎ Ö5ë‘yãŽêÅ´7ÆÃÓx®¨êüHâãù=±˜—¯j_.ÈWµ*'ú«:UR/ǪSLÿL Pµ0'mPuŠƒi#ŒÏÉ|N†—ÉU½âÉÐôÏZþ´ãϸú#ûßcëˆMæTÚ ‡ç©uÝbä˜F;Óò½j£3Ó›NL‡Þtp§Coý0#POßÜ›A;3¸>“q˜I;3ig&×frm÷ç@s2ÍáúhÏ¡Í9ÛTmµyU-£yíÇÖ(^üà¬SõE—´N×ój÷ª|D× ¦í8úbò/‚·Eð±È­ê‰†ç«:JV]àNÍ.©ÿ»Ëè‡eðN¯' ¾2©:gAíÊZEAÀQ®ŒÔ?“ŸÁ­º®çFUË3øP®…r-”k¡\ •kèË è/‡Ïåôé pä7}¾œöWÀ{0n¾‡£³áÈ.>·ðO»‘àDÉýHx]A)È\p¹ðÏÀF¡Ñü^+$Ð\ì `K*צ˜þ¬‘øœ8äŠ'—v n¼'Àûjå2%t(÷k%¿ó “M?¯ä¯¼$~'q? ZIG”{–ÌõÚÌeLR¸Ÿ‚|)´—rT¹m©è¥›þNƒ·<ðÊø\šô}ã¦OÜÀKµ´Cße"&8…#“ L&0™È™M¿d3ÖÙôYÙàår­˜Bú®¹\Ïåw.2æÁK¼æU®a>íç#[¾À¢?…ÒŽÄ‚O…´Q¯…ôK1ýRŒ¼Åð]|D¹‘%È[Fe´¹še-£½2ðËÀ¯¿ü ú¤9+³*Ž*—³ùªá«Zäe̪i§švª­…ŸZ‰Qà§zµÀ¬¡Ýú¡6¸ßÀýî7p¿arI›‘©™š‘©™¾k†·µ"¬øšòÏŽC¼ãïܸSH,á½&~zO¾¹øåÝ׿Äïþ¥õ-ñ“Å7¶}_ïµâ¿v_¯òöAmßÓö'»û=ù‹ÿÌÚÓ?³îÔmÍÉòíìóøÞëI¶ßfûgŒƒÇ뾆dç¨í¾ïoI¯cßWöä?y¯Ùçí½ÏÚÛ¾Py¯ž×‡~i](_É.¾žU£šþê¨ë‰¢Ãý}u-jôÚÝ›ƒ<Ñqº6½žì<®-à÷‚@U~A”S§qB£t]=ô;~ƒp®­ðSµãøn‚üÁûryþ ó)ÐI‘ç ×RøsC3=tsÝMû+ Ÿ&Ï~ðS ν4y~Àc8ídÐv2æð ÍÚÂ>W=ì0{¶ 1£à!‰¿y.p¯šÅ´[*Ï4x.¥ÿ*h+‰¾+…Vt’·àˆ ?kä™ýÕÊT2‘k5ügÒ§ÕüeÒNµ´ÅýÕЭà/>]#8Ð_üàVÃóEž5À®AÎ5|¯€ŸjÚ/ã^­Àò{5¼UÃëÁGöÕBYWQ!n}°˜5r1íYÐ\k??þ…ÿ–þ|¸ßFùæÚ=«cö­s kòKXáz»üÈßq:úðº?•n¾]Qnã 7½}bC½ïŸSºS2ïûý¾Cß÷zÊ5gÐÈs_3«.ª »wí“®ÙM¿êòÁ7ÓN»j'2<ð³ùo¹Þ«ºâ†‘c\c¯{iÑÏo3ß¿ë³çêß z?{NFPÞ£æ}WD/js¦ø1«Û|æÌüp­ë$Ez]Š^Zú÷b\oûÏyù¾~·=ñõÐùk‚öéß÷]ºiÜ]6¾¹ª|ÇUß:Á53*ø`ý-­Á‡û÷Rôr.[Ûq×WçOõ_ôÖdóý‡gWeŒª1ïk}oÞê‰)®YšßUAoÌúË”tû7øþ ?û/MÏËrõUíº:÷ŸøÜÔü]ƒþñ£ a§˜ï?²,(þÈl»ÍûJR_8uõ;Žœo|wcà¶®…¾‚ÝÌO‹&_<ÐÕùä5uņ¯®oø!Ø‘N§G=ý_}Þ˜£§Þ ~¾–ë`ö]»ûº:oønljÇgÃÇ÷Jêác뻞g}_à[µW¾¿d¯«óÌ÷Žœ;ÝÖ óýÇïyèþæÖöÓNÛÐØË¬;ùÎ3ZªÁÓz”uíì=¯Nwuæ.põÉ•v?˜ï?;pzÕ‘‘A];¾Ýøð'—™[×ÿ|ÝôÆ×Â#ý/Ûþø‹fÕ ¥ßÍË™á¯ú zZ_bߊžu¥Ëî?Wgð°Ç×ÍÜìèÍKÞ–‘õ¤§¶žÕÿà ë?rÍ׿+/~áWG‡k²âºZo4ÎI3.øjÇ_Í÷ßqÚ–ê‡Í­g§U5yŠY±dqùýׇ õ"&pîšÆW=öñÖÑØ-éOžkók¾èù3Æ~5ÒÜú»©ÉÃ'öŒgYÛóŸ­{¬:Z?2n~kÇ÷¯¹ÞzãñüFo4ßÿvÍóçYÔa÷ËÛŽÛóÁD³tõÜØóKW:z0@ëAÓŒˆAwź޲ÀzyúaßOÏŠþñ »_Í’¯r‹þxzüÏÿóŒ­Ìu°Û¿¤ïæüª3Íýö|ññýÍ­Wß¼åõϘ¥ïé<í­\ð´\pàå9‰ïºÞ*zúáÞ°ñÍýc2¼="Õé÷Û3ü°$Áu²Ò_³Ü¶ûZ/Î{êèW ¶}ºÞ /ìºåP¹§ÿöû½ó¬û Ì­ñ¸%‹=vZyËÓkv>´È5Uµ =­Oüþêß=ìÈ3eóŠÕ/Œsúc\ý3CÎ<ìô«æïTîÞ1«æ^²ê¢ý\Óî91 untµ^ܾÅïµÜ÷m:®7¸ãÄOF^½Šœ+7_áÈÛâçºòƒH³ò¡GO^yÝmÎsÄGëË]±§fŒGÞ7ß}ø¦÷&­ENé˜!æÖÅgß›°;̬lÚ{Ü#Ÿ”9|øh=¹µàÏß–üä‘ï͇ßÛá5–ËÜ"Òûû˜U¯zô'ŸûÀÓúqýÉ¥ëÓ_u½¹îŽs^ùp»Ó¿,CõôǾ}ͪwVŽ{y@øZOþðôONðèù›9Ãî¼¢~ª3îSwÝso^`n9wèë»\…q¯}~ÏfUäÓ†½òG_}´þÜÔúÉ™û}sîWÑ#6üΧS¦Å?vk³¹E?m»©(9éõç Fº¦¨çô´Ý:´÷”Ð:‡¿¡×|ºèÔÉžçþþ¥3b–Ï7·\ôñØîóõŒwÙ¸oîþÍ=òÑztó³/GD¯w½ñiüÌ}­“9ãx*5ÿBsË o^”±Â£%§ßrÏ9#Oò/­7· ¼÷ö-ã\o¼þ‡ÚM§®3÷O»vÀ²?š[¤›ûÜ`)xGž½Žmÿ}ûz¯˜oî_ÛúuÎ_.7·ú^ê êSo.¿kj_%ðZ/DÜIßzôâ‹ßyì댇Ìý—~»ûn÷-æÖ‘'¥~6y¿Y8^sxZ/6–.mü>ÕõFɹüá‚[=Ïóý7Õ?íõ“½øÓEç/ÍwM׿‹*åA9Ëy.Ôzrã´GÊ4Ï8¼ô›ð¡?îvômÓ·ïÖ$›[¾(}?ûpÎo ~ô×áלõ·™·™[N:éLJ^šaž¿>´óÙ; £õ!Èr\û††W4èJó@ï ?^¶9ÛÜrÚxžà»œã±ç£¶,¯a˜¾saɘwÍ-ñÖkf»gdõÉzÃu¢­×ƒ”ìÚ² ¥év×ë·®y¿íÓƒæ9k®¿Ò77è3Íÿ–è•çÝsõ`3sÿ&ñˆÁSã¿«íÓ®/÷¹^_û·ëÎ}Ø<°ø¡€¯®¹ÊÜ2ç±Gj¯^hfŸ:ï³Ïß'øðqjÜw]“U}z¾éñ_ONxò+Õ»<àz{ÞŸ²³Ì-Ç×7]>åZÞås·|ÇÕ®¹ösì8¥»Ö]qÑøü}žq|]ã³/óø_Bî}a‰Ï¨ Â’JfÝÖÏÜâûõø]÷ýÑ£o…ÏžQÔP}Àö¡«ôc×ÙU%÷lê籋×}›#Öqô:÷öqúePYáÐC¯šÅ#ú=iÜ^¾Ò‹]eÍnÎøÒõšõØZêñƒ„\V1pëfÛ×5mõ+÷Ø‹ÖOÇÏ?NéÇ®åeÏ6=Ͻ×vùL8uÞ§¿‚/<)î’;h¤y þ«Œ·×Ÿèð93mó~דžç£~n;qÓ`¥/; ’N¬u%{ü‰×æ_÷hHLœy ãܪS>u,»þ÷¡öóÕÌÛùå´–ÍCÿd°Ò—âÕÌ9Óãç¼6j&ÏoÍe_½{àŠÈ õs³mÖ]§/Üû…+o'âó43[Ça{¬ôäÅŸ$`Xj?ÿ]¯~^5|Ç«;n¹ížÁû¦›mU/_8ïþ®eg›Qçî¹ÊÌÚ–·õÉà˽øSzó¢Ö=_º^}½öœež1\|ÅâúÿÆlÓ|k»ÈÒþœÓoJo^Üà/*në—ëU¬qÁk1æîüSò·¯xžkmo ÍüÔtWœ¿Ò½}6øJ?^ ¿ôµA;Ͷ׫Wü¬¡ýYÏóöÀ=£Å#0Û~ºáÌÏ}`¦)¿|¥7/Ž“³èÚï=ýüjý—……{î6<˜´)¬uóœ3/ê¤COº‚öƈ‡cº¯n|²òœ¿9v0XéÓŽ7÷ð¤8ÅégQ‹¤ÌÕNùf÷^g¾8öÍG2opªþ0sôüçégu ÚµcÃ{뇿9Ã<{¼yàÉ/8zôN³M¤ïw·ýÜOéÏ„˜{ Ñó\xuH&‘ßKÎó¦½fÜcCïõøm× ;óæþ¾ž¸,O‡Ç¢ôhÇIcûþêyŽun<»57Ïynì8û§¹K=r¶ù;iÙ5Ï(¾`èØñf¶'è)=zAùÉ®Ž]Y?÷M?Çy^tä;àüufÛYøp¯­‡à)½yAù®Ž{·ßùrV¨Óþû½;ì®(³­õÅÅ„âf–=>C”¾¼°ùËÀúüß{ú¥C¼šÄæOg—ÍJ»Èÿ¶YáõBfhw‚‡¥?/èy¶û޵#J<âèßϵÞÙæŒ»~n¹æ+?ÃLÑxÿmˆÒ£¬ÇîdW‡ˆ}Ùf—Ï9æÔöIfÛ£ûƆ}XìYGX9¿ïá˜KG;þîP¥7/,{ûÜÂG¶zžÇ!¿;}ÅŽM{ïòí? õÇgœñ×öc‚_=CWéÕ CÓúú]Ýäê˜*¤KÜyQÐ.{œ[>üæ× .—Ögí8óÛP¥?Ï÷ºû…¶Èã=öýеì–C¿<6ôž>sô¦ö©Y˲6{ø´Ÿθ+½yî¥ËþØ6wšÓ_Lxø±Ëï_ztŸg¯­ìÀŸNì_ìZ¬í'ûçÄ4Otâ°aJŸž»4eáöïîw½Ò7ýìß§öÄ]~sh»¼Âl‹»§WÐá)f–Ïõ½q™¾†)½y.eÅý÷-íé¿—­ipøgÄD}Wc¶‰vôÿ£mgà)½ynžõÏ#ÏËj½þž{ÿ)óǘ›?Ê:%.x†é½ñÌ'^žÒ›çF?ÜpøÁrÏsêeÁrû:ã5ñÅÏí?äéßÍ]zÎìéÇÙ|›©¹@çù7LéÕs¾XºsÿI®—W®Úöþy?:ú4õ‡Ïî,37ßqõÎ[‚o÷ÄgI¶>l£ÆižpÝ0×Ë<´}>ù‹çy×uÂäÿ¶êˆg\6?yÒ5o¯õ¬×¬üí_å‰ ¥7Ï=ý¡ÛæÝèzyPREFÞ§fW€‘p‰ûrGžÏ-GÝöŸÍäÔÈÌ9w< ¾Ò“g?ï½pÉŒ:×ÞýO9¹~Yøfê/¾Ãl›Z>pßïý”ÿàñƒÿn>ðUúñÌŸ¾êLÿ¹®—þ8N œñ ß##j¶½žyáo¯Þoæ(ÿ <¥Ïüþ§wJG\à™—^ÒqmW”ï÷7™m8-«¶í÷ÈaûyýôUzñLìÇ!ïŸêzéJ+00»b¯U9žç«jßáß^?õUúð̸3¦ÿ.v¹§?_ª´&z³+ù¬‘kÞˆ6ÛdµlÚb3=ìâ÷¢ª‚Wãß.ËïüÙõRÔ˜#ý/{ÌìÊ®~ëˆùμ2ÿ™Mm٣̴£§Þõå×à©qo¿ãµÌwãy»Ï'¥Sælú¬ÅäÙyÁ4_W”öoÿn¾®ô¡½)áô¦í#]{¾[-o0Ì®ÚÛÛåYŸhkü>å–k.4»¤¡ÕN¿ WãßS8íàE\{^k•_gÜÖö™2aÛbGž­wGvöûÀŒµ×qogu¨kÏi??3ú€ÙÕúHÃó_>n¶á½]ÿá O|`?ÿ‡«q7¶á'\{nî?ìÏk¯2».Ú°`ô%ç™m‡¿XùÌÕ¹¨ç™l?O‡«q6'~pý¾“—yžo{~ûÔ+} ·¯ûì§—™m]Mßü8åg¯~Wãü´¬õ¾Åµ§,ödßÔuÙ‰“/_üŒÙöL¯î{é¶|x„ç§¾î×ñ7÷·®=²Š{Æ §.¿óဗ‰­ÇC„ÇÐóøj¼ŸÊ´ Ö£_{®™@:í_‘ºßýÁ ÇÞ6ïŠìŸàñ¿rÖäuniƒžï'Íû³Òƒ6»öŒ¨¹î²úû µN®ã<×¢+&Ê 3Z‘ŒxjœŸ<á¦uKn_îÚýå­b)f×zÒ{9ã›=òÇ!ý×zäÈývéàÉ;¿_ó—´Õ~àZÞîî¬yæœÇ?wúãÊ1›¶ïèÝŠ^¶÷A×<=¯de‹C{zjü—Ù|F–k÷ŽŠ~g=ßÑÿ«cÿ´¿ Æákö×Ï^‘}‚éVv¾ÒƒÇõû°Ýx‰—ïÊ6»®›.žÙÖ7TÞ ˜iêý ðjü3o¤‡vzôf·z_åø7N¬>šxÀÜü±åX{âÊ4Û^F*}Ø~´à®!áE®ÝöÎ?yýyŽ?pëcß]Rÿ˜GþÍŸü-ì§ì«œyÄ^©ôbû¢¾§\zb¸k÷ýVMò½ÔáãîG~:µf“ÙÖï*ßÀìrGGªñôâ½÷÷zÍãOìÞæÿˆ»Ör­™oÇ/£Ôøo+Ÿö͹ñŽ«ôáâ±VǸöȪþI';üõH¹÷+?³ýG³>õ›L;xöÚ]G?kZiʇڿþ(¨GʃñÎmÎKßs §Ï¯‘}‹=Ø5ï/×þWárþÅBÞ_™ú¯ãÅ$ºÑ7ûëôoµwvºÖ ÷цt«ñžh”G}R>„ãË59—ïÖý`?@Sœ‘§Ÿ¡?3¿;@ío*Œ}F¸” ±L_;äò¬Î ¹{Jæÿ|ßm¢¢ÿµ…_UŒG~9ï¢n‡Ãréù@ÍdžÙï¼)œÁR ÄòîH˜]Üϰnâ)yL›¤ûùáËÿüÛw_Gþ;þøØ¦>mì ºø¯Ÿ=~º¥˜¾M,6îû"xž_0Úå{6áLøîîz?òùyš¨ w”ŠOŸçsOÕ¼U»NÚøzê“SC„3}KèÕÍGôý º/Xtàï}sd;Êñ¼¾¨ü?Í{Zµß4Ö™3¿lùë÷–ŠF:L0õæ">÷Gò|¿ÔúEëãŸè{ÛII‹®Î0í¼³8v^ý }¾m<ø5½D›ö¥"ãŒÔbêãù_/ÕÄݯÅùÛ‡ý°ÛÔ[Î;Æî›{±V4ݰnçCÝæ}Qû3 –ƒ—鸺üû'õTÓá,}úBvS¼æ ¹S&3ýM9Šd9x…ï)íŸù}m¯ϽçÞ~8êI=žsÑt“.ÿg÷Yß.£<Ïÿf:e-=§ùrvóÚþ× gÙÕž ìï%*ï÷„ÇšX”ãùßÒüüß¶Ÿo´wúÝÔ¿`––»åFX÷ÿµ{èQyQ>ä¢<ËÃÖe_¯›WÙbÊo§4þÂù›Ø­Éµcµü9¼µ¢3Ã^Èïb‰q>‹dù¨ç{E{§t×›¥×ÑCW¨×#Y9ßgL>VÑmOHBqËÇëòXàeïd~gù‘¶Ëÿâ8„]\`Þ—TÉçµé(Çrðúº±ØaØ;—ɇ{-—s£O¥=o¾×6|uØ·ïâóÞ¡JÝûš~¦Q,¯Ö¯…¨Ù;ŸãýÀU„s‘õ¦¹»§›üh gøë0×óXUŸÓ·¢<ËÃ6yLXhï$·¨£Zjç_ Y+ø~GT²ÿʱlS÷æ]òÚ¾@8WL[ürt h“üVÖÅûE%y»æÝ€ü<ÿo”u{½®ÍŸ¯Çýü’•~¯œ6Çݘ)œ ý¥ßq¢X¶r8¦{[œ~TzÍœÇõƒ/|”o×òTrbï¼é‡Ìs¥ê—~߉b¹ø½t“¬±wÝ·übOC –ÏWoŸñÈÃ;Dãüó½s:o6Ï…•Ÿ. ¿ZZ¢ç#šåbÇsd×ë÷åÇèA¾C8ëÉao²hT~ëÆ»ÈÅws¿ÍrB·´_þ÷h{×s!¿Ú8ÓG8·mœòùн>×Jƒf¾£Vñ9Qï×¢Y>vnþí¼5¿vŸ»tÈ£\8·ã”}^4®èLÿ~ú=¢ŠùŠr,;Éë}ôMö.öƒÎïÕ—œ¹Uï«èÙ*ñ€¨úãÇóJÞ=ˆr,üL™§ß§Õ;9ß»?ñí–¹z~â—?ès`šé?Vuøöümóõ¾/šågwɽ§’­˜|펠ƒÝ!­?›æ¿zߨe&ú"I˜÷ÖÕŽg†¿3õ±5H÷^‹É—îÉO8U¤ûIîëñ›E>ý6ÇÔwŠòsQžå¥¡½’Òkï¦íÊ–Õ“/òòÇ-Sâ¿gޝá™U/~ùй¯v°_cq ËK#)û—Û»Ÿýs`å7#'ÿh´ÿ~Øñ§_Ýo꣹}ñ4÷±Ž`éÀ‚zX^öÈjï±wKw Oáì gé,ÑÐC6QžúŠíÝÑÈÏraœ×»OHáü´pií:§žÒÊ7Ðz/†å¢)2¾?ï¥róüÜM§›‡Íý¼ÓÙýmç[ëµ>NÝ›²dƒ¶gKf~ð‡+¡•ZNcXnšH»[>´WçQs^/mø¸yƒ–û[Ö/÷N¹ß²©a’p~ÿÈ+éÏÁþÏŒúæŽÎûµ>Œa¹ØrtA}Ñ]æøŽ«ï" ½èòXí½u‰^Ëm¡hÚ<7*}¯ï­bX^öÆo ²wC®é·-\òX<Ê|woÜxÄùTq«9ÏÕ÷Å »Cë™X–›&y É´_OoÁÂ5ºz©õÌQÑøÜW[Ü!|_ˆü,MÛ*Wf=°Æ~ügcÞÞý¨pÉÏòÌuì0üòcY>šh{ýâß y´gÿSϺBgN­¾´Ió!œ.jzM>Ô¨sŠ©gc•üÐnαÆ~Bù™|zãhµg«¹nù­á{(­c•¼(ÿÙtQú?æºu;~ÍžW¯õ@síµof´ Gþ7>²œìt,oWÙR7?Éñaï½ó{Sž]éòR¯ÃmòÁ”gÇõßQÄ*9â÷ í×È÷¦\»òÏ“Á×ý”b¿1Ç]­Îgæ:‰e9j&·øöì'^,L9²t§pÝìÈ~óéý†>Õìï^ÜÇrÒLÛ°˜«öôÚ_í®»W¯k[eì/DÕmK–ú_E~–“æ¸Ó_}vÿk?Ï®©/á„#\÷ÏYñþËmZ?(ÿÓŽÑçmMz¾ãŠÚ߯w̲N®çi¦+Õû‘#Z~¬c‚°ÅÐû¶;~±=TûÛűü4ËãM¼½Ç‡Ü– לÙw¯ª,Õë'{’3súYâ\ÝOúÑ#ŸY~-\ó&8·nÑã‘Ï)3íÅm»C êEµzÏN7øÇòÒ¼¤í½«/èï\zè–fæ&áZèŸÐóí Ñxë—£fk/R|q,£WoíÇòÑ|ôæ½Ýo±÷”ʦ´\T¤æ®¸ÓØ·r‹r<ÿ-Ø<“K^t‡Å:dù“ébÜS8Þ"rSq¿…ç¿eåw^èÓþ×t=ss³ig\UEOÌèªÐû¯DKEkg›i7¼ÿÕþÔ–“–“½õ­ÈËöž dˆæêy­™õqÇwω†k1X¡úݬæ©O;¾Û¡÷s–}¤6ÕÙ{ö]JÌ{t¡æÃãÒDïc»ŸOúºkš¨IìBy–‡}·ˆJ¥ÌõÛÃï"µ¼³¶öÐ6ÑÐÒ¾æö‹?šç¼š³9oÎøöš^¯–}OÒ‡ {Ïu~®Õ¯åÞè_¥í9ßǘ뿆ÜÇ|¡×©…åd_ëœ/ w¾ªýýå5aªp­eÿæFö£ò¹óa”c¹Ø‡ÝgÀ™qöÞ1ä0Qóc£ü~IŸOø.ªø/úõ]ûQžåcˆÜè˜üèM^7yù”1z¾ëßžô½–ûÝÝWnXú„©wÔþ«¸?žågÆÁ¨–™Íú{y|›ªù³³çÁ™GÎëu¬î? û¥öšßñ,?û•?/=sŒì®=òþXÛyú À'^Tù<;럲ÿ„r,/ûo‘ŽÀæù³·¦y¶÷Ä“ÂÕ|[ƒï²úމ߳Ì÷—*öG=,7ûù»=.ºµ¨\©íQstë–ùÖ÷¬|f¾«;Ìñ°üì§ëƒîCö^ê¶e¥p5H¼Í6Ç|÷®1î¥ã•|4Ї`3í½ŸÉ ájxñ\|ön=/Ê/¬Æ(ðÕ3(§äCºm9L¹ê•×ÿ^ZNvßýiÂÜ/ôþ‹ÏÓö"~_5´Ý¬Ã:ŽgyÙ—ãÿÊ”ƒØOÆÉ ]ôcÔÞÙi×r¶I^›÷Jæ>aœÒ/òsйú;´›å†IóqÏÇ…IëßÖòÁ~"¦ß°â£¶ïã”~y–^HfÙO*ÿQ×>êŽEËGSÛF¨ó^±Z L©ùì^­_Ʊ¼´DPÇM¿.ûIö³®C —¢º/‰Æwä‡3æ9JÙO·þ(»³v4«Ë~R~qŸp½ê˜=bûÝvgÇ CÊCñá†0”yø ôoAÆm£8à—Ä_ºóíþz ¨ØöóTÜ­3n8ª-*îäÔ såÕ®ânA.½w) ÕbÆlòÙ¥boÙ†j+/}w«Va¨ªøöH±ôUñV!‡ôŒoºéÛ‰ïdq‹»…~’¯:a<ɘ[á*æÖ.…ódSØ©}*®}‰Š·…rAH Zík‹â}Ô©xöÐä "ñRò†´3”ŒezBûT|-ð% mÒûšÄI¾‘qRé-DƱG#?2@á£ÖqL­(ÐQ%*¦Ò£1†è…Ú§pQç©8Zƒ*Žx ÞÅ^€ÊMûý¸V; e-(ki¿.fV«D§~5ǨO@zø—P¡âÓ£|¢q¨‘?q@Å¥Gù$äOT±±0¶ñí ß´Dáš‚¶¢ïÖr…cÚÆjVâ—¢|J;«ÜTÔ—Šñ¤¶°úMCù4ô'­Uq:ò§¯f R‰?Z¦0G»XEKŒQð2ó‚ÂE_³PWh[±ÂíR1yËU¬.ÔƒyÈÁ8sv©ø¼¥* Êæ¢\йh'w€ã»ÈXùÈ›7¨0DëÓª @á„"½ KÅí-ãxý2Ž—UÅÇ? ‘·°DáˆB6&p|5à s[„þÕ2~èí6d;ë† ÙÎ_ªíôUóÛ¦bc•ªx•Ð;XW =À?Aoy=[TÜq‹Â…iUq§ ‡^˜ ¯.ª?- ¦Daò¾6 ÚõCüJ869aÃñÉG }Æ62@Ŭ¬Uøãh—¾ýµKá _ô Ÿ?у {ý ïè›ú‡pÇIHÜqÈ}k cUb|äÿˆrAH BZP;«™`ÐÁ«UŒJ›ÂowÂAÞ.Æ‚‘±)Qù*…_ÂÐfØ…šü$(6¥ÄþÒ[·Ä/UØâ¨? tè(ÔOoZôŽ]ªð_Î0þK æî¹IÅÅ¢/t—Ûª°ÄAÇ­f qeüð ;eãK9VT|»Â /U˜áHO@zø—PËq&IU&NP¸.ÈŸ8Èñ%%žK«Ðñ¥nØà¾ |#ã[Ñ+ÆjmEù¤§ þ”OAzª¯Âú®`Œï4Ði¥ Óéé Ó‘?t:x—IÌÀ|f ½ ЙHÏ,U¸Ü{êʪS8Ü m(kCÛ¶>Že/ñe@g£î«ÂÝnQqíËÖ Êæ¢\йh'wã¡É˜—-¬öóAç#o>ê)g,È‚:Ž£Fñîeüú] sÆÆ±.'‚Ÿ…áŒÍ]ˆ>B6&…sü{ûs[4Aáo“N ]LÿÈ^’}$Û莩ánÝm!Ù? ²ydçÜmÙ³ÿ +ƶ‹ìÖÏÙ+w[E6‰lÙ!²AdsÈÖ!ÛB6…ì ÙÆöãz»án/ÈF}0ìÀÿe ýOzß]דŽ'ýN:œô7énw½mà“Ž&½lècÒä{Iç’®%=kèXC¿’^%ú%Ï ayìRñ}TL_Ôí…yñ‚Lxaž½ñ·7äÐíûà‡âñøp¬^÷²9 ²æßÂS.1_Qv4df ÒÆÌcÜW·˜q®$¾ÚŒÿ§ï‚×r¼¿±~[—|Ã6ÕéQ¤cÊ8Öm ø@oH„#EoÇÖâ«ð¢|ñ¤7Ê>þ?tòŽG¾ñeŒí”Œ±%ãïdô?´ý·âo+ÚKAùTÌ*x”†|iHO§õŒü™h7 k, ¿mø?Êd#=<Êi帴¹»XŒó@ç#­`5ãFȸ³¨wb»Â½¯WXàEí7†ö¥CûÒºaCûÒ_ê¾4\Íú cé—cÜpè, ~H϶𲥸§žÈë‰ñz*<ÐÃQÖ y½ G^à¥úC±½!7Þm*–z©ÂÉ!z@ab^}QÖíú•ºÅRP±Ô‘N±|F"}d©f!Öã¨yŒÙ-±t@ûƒö=ÚWᢣÑΤ©PX… «òpã´Êê-¬Z$N!d)üFÞ`ÐÁ ƒQ}c$q½Ñ~ÒB‚´>Ž›Nß„¢ÍPÔ¾„¡Í0ô'lÐ-^:ÒÃ/0öwÒ#ÐnèH«Â%Dý‘¨üÞ¢@“XÒ£‘N>Fä× :t ê‹A?c1ÞXô%¼‰mcu'c¢£®8¤[ÞØdt”¥÷œxô=¾‹ñÄé¾^Æ@GzÚN¯êðƒôD_…7ˆú»Xm&IkgPb –+|A”OÆ\%ƒN ÚŠ±Z1V+h+ò§  êOAû) SA§¢¾T´— YHCù4ô7 éi(ŸŽôtÐé ÓAg =sú2@g"=íe¶°ªÎB]Yè[V«mÊÚ×v†cóÊìk6èdÈÁ¸rÚÖR¹ŠÇŽ~ä¢\й¨7Ï—ãòÊ8ìmŒ©ž:yóQOxVº`-c4þá(6¢ÄAœÀ±×'v1þz!ê-DŸ !W“,ŒK)1A¡ÿE¨§ˆÖ<éQúgØN²›îö’l%ÙIwùsvѰ…hël Ù?ÃæöŽlÙ´ë혱?5ì—a» »u}l²UdŸÈ‘-r·?†Ýù9›cز5î{W²1îö„l Ùwûán; »aØ ÃV¶ÁÝ- ;@6€ô?é~w½ÿszþz<åŸÓé¤ÇIw»ëmÒÙîúÚ}kQãëâXÕ%rüÎ-ÆO›Â°@žáÈC1w½‘Ç»Va‘A|‡Ìé~K?ðÁi# #6ºa‘U0æ+Å]3AáK€Ã@Ôت0%êñƒµ\ʘ­cK¯5thúN,} ¿è›“p”¨UØÈG~ÄQ»Æ*òEãïhücc\ÕXô9¶”±Tãð;mYÂÃ!¾Oᣢܸ Œ…š ðÖ*ô'i£Â^@¾dð0™~#¿ãJRÐÏT”MOÒž:ùÓñ;túž :å21n굡}þßvãygãwêËig\6ÂjÈEÙ\”ÉE™<ô+?à]ÁFÆi#,ZÂkø§h"ÆSØÂ±¾'až†öÇÆöÇuÆöÇ¿Ôý±Uñ¿Oa`®ohU8C{ÒÑè¿'Ö¨'äÔséÙÎKx8ä~8ÊGY/«Âò¯¼ ¼Q–âzK¬!¬OŸ2…5ÔÎË]b aÞ|QÖíRŒW?Ð~ÈOñFG âjŽ@]ord™ÖÐ…çÝ®p†@û£mŠ_6ôhôy4ú1c ˜KcjÆP8c -ièc òRü‘ t*iAŠ+Œ¼Áe [yÇ–*\!¤… -i!H AB1öÐ Æî…ÞC½aµnØÝè}+GØÝრ»tòG $òG¢ô]Lä…Û]®p»‘?ÚÆ8BÑ £AÇ | øÞÅ¢Ø2…Õ ÞÅaq ãÀ«¸3 Ÿu[ÐW êŽG]ñ ÉçHbs[.7ÒÇ ¸ar£¾„>…Ç :õ%‚N“À‹¤z…„òã+ÐÆAL :õY1v+xamEþÔ—‚±¥ ½Щ S!W©h/õ§¡|ÒÓž†úÒQ:ètÐé 3žú3À» ЙHω9ÌÂØ³PWúšÞÛÐWÆj#{7ÀXueƒÎAßrÐnNãºÄG?rÑN.êý_öÞ¼ª*]øÏ–bÐC=ôÐB€$û¤ŸôÞ{/F:êÇA‚Å2GQ‚E žè8 zlc,£€ ËUËük¯µÏ9“aæÎÜûÿ¾Ï{'yž³÷^o]k½«íµÞw%tW¹ËØM"6ø*èzóìͳ7°ÞȰš<]Íóê6·TÄ%qEE ##F82­w ú®…îZø¬E¦µçe6ûM³Ï}¥è#ÍþQô‹¢ï3û=çq°èãœ×oÿQ<ä ­Û:÷S¢o2û¥¾}’s\dÑ×8÷3¢qî[Ì~ŹO}ˆÙì#œÇ¾¢/0Ç»f{/Úy3–ŒÙ¶;·ëf›n¶å¢ wn¿Í¶Û¹Ýv÷:·Ó}ÛgÑ&_hÜëÜSöÓ‹öWŒ-¶Èt#þ\—,ꋨÁo€h_)ƒ¤võË•²¾É…ð!Ô“¡Ô¡¡è:Œßa¤ ÿ¨F 6~‡Cs8tF¸Ëj"|¾$MøsCïQðEºð%4¦C­ÆŠ¶çqÐï%cðŽ§®N ^‹3ßî¢Ý‚öDd›Ìä2ïL »¦ð+ΓLE'qÎAìñ1ͦcb¿¶Ø3íÎ ÑÞˆ¶†´YÀÍæýdœï¹È”6tÏÃx~ŇAû«Š¿ íáØþpž…oááоnGP_GtÊ8ì#I‰#~+Ýà#ü-Šæd2ŽjVq7‘i4i£á9™„³1ð±®*Þ&òŽ%Mø1ì8`Ç!ÏxèŒoVq6¡+ü˜üUœMdpçYøkÍÔDž'’o⼿_üIÈò͇ß|ž@oí×ôY½…à/D¾…À/$}Ï‹x^Äó"ž#ÿbè-&/ó¼„ô%ÙU¬ãy]«Œáé‹ü¾Àúë‹ F[)þ.Ôwš}¦è/ûÈ´f,ú@Ñ÷™ý^ßõâ õooœlöe}û¯ õYf_eöS¢2û¥ õGf?döAfÓ·Ÿ1û³_ý‰Ù\híØì+úö}û… õ}Ûÿ¾ãé¿·†Üw,m¶çf;n¶áÚ'Ñ·½yÚæ"ãz©8ÌÝN1’ÉßÀ DÇÀˆøfƒÑ_Ä庘:ëJp¥žº’6ü!Ô±¡e2Îñ°írØu 4…?üáüïQñŒÉ÷]*†1inÔ}7ôþSGõ¨˜Å5*F1pc©‹cEûX£âCWøò~µ&PW&@ÇwdH=ž¸OÝ&uÈá›[ž“y?œ)üNv*º‹³šÓE{†üÀxtÊj=f‚7³SÅÿí2̆æœ2×w.8sÑkpžðõ„¦'ö:Ÿ÷ó¡9ÜðX­…À,„þ"q¡Ó"à£Ãb𗈋|_ŠÞK©Ã^ä‘r.s•1‚—󻜲XNþ®ð‘1€Wò»²GÅ$½ùõF&oè®îTq$¡ëÌd_ÃûµÐ\ Ÿu¼_GøÂÏ~¾ÛU½jþ'Œ©ûÇÓ?þñtÿ8úŸG§)ýÏ˳y¬õ¨ö›U¼Ylj@ù;€´Øà@Ñf“¯Ï«ö”­ˆç)¦Æƒ©ÇƒL;r1íÅÅÔ+óïâÇžzäÚ¡bØÃsÏCaíÍPž‡ò,b/1ìyþ0à/qU1ì‘ñhç~8÷"~Åøˆ8 #ö©Øõ¤D†‘À¹aÓnÍ*n=4„ÿîQè&üS _Ñ£IMŽ>§bÖ‹>:cÑ{,÷cá=–´q¤VøµñV§º\UœzxN@OwpÝÉ#wÑg@k"2MýéŸü$䘽IðžÌ³ð$|õL&} ðÂÌèOáy*éS¡7•|šŠ,Óe²MãYø˜î©âÐ#ëtÒ=È7qNZœQž®8‹;£YÅž'_gò<“ô™¤Ï"}ð³ÈçY*æ<ôfרxóäážçð<‡ç9¤Ï~.ºÍ.ôæAožè£xöDvOdõäy>póá;Ÿ÷ó[Ô¥Ƚ€ç…à-DŽ…¤/äyð‹x^´O6»‹ÉãÅðY,žI_Búž—ÇK)»¥È¼ZKÉ/ê€<½€]† ËaÏË ½œçåð]N=\Žü+,2†ñ pW@w%Ï+]Ù+ã$¯ª‘ñîW¡»7ÏÞÀzwÊf~5°«I_ ]ò·rõA&p× Óø¬éÝÀZ`×Bw-ò®vÏë¶È¸÷¾Àú"¿/°¾Èd´åâ¯oßhö‰¢/ìÛÿ]¨ß3û;ç>í_3#ç?/›û-úŽ“Í>Çì7Ì>Áy\+Ú× µ©¢-ýgö4ügcRÑ:·sy’ŸÈV ½4lð"丈²@Ü.‡ƒÐoe5˜ò±kS&Sö‹öJŒCÑe¨¸öÉ¡É0êÃ%”Ç%лØáü‡æpF@g²‰#I±FÁs飡%|»Ö`„ê±Ü…žðÝ;¸ ä¥ðÇ)|QN¤l' ÛG©è!ü0x@8s¨ƒsy^ŒÌKø]‚ K¨‡Ká±¹½e9øË©gË©ç+¬²ÈWŠ_ø¬DžUà­BFo~½¡·šú¶zŸŒî=`ÖòΚ¾Ðôíté_»ìkõ¯]þWÇ\âÛ6år†ë,ׇ\q}Ìõ ×§.Æ\Ùå3®Ï¹¾àú’ë?¸¾âúÚů¹|Ãõg®o¹¾ãúžë®¿¨¶ŽýˆF?¢Ñh4r4íbu~š>Ecž¥Ñ˜iô-ó,y–6R£ŸÑèg4úy–Æû×° û×° û×°-Užÿа û×° û×° û×rÕ>fì_Ãþ5ì_Ãþ5ì_Ãþ5ì_ì­Ó° û×° û×° û×°cŸö¯aÿö¯aÿö¯aÿöo|‹Äþ5ì_Ãþ5ì_Ãþ5ì_Ãþõpì_Ãþ5ì_Ãþ5ì_Ãþ5ìßXƒÁþ5ì_Ãþ5ì_Ãþ5ì_Ãþyö¯aÿö¯aÿö¯aÿö/úP û×° û×° û×° û7Îåcÿö¯aÿö¯aÿö¯aÿÆÙCì_Ãþ5ì_Ãþ5ì_Ãþ5ìߨŽýkØ¿†ýkØ¿†ýkØ¿†ý{`° û×° û×° û×°ñVÃþ5ì_Ãþ5ì_Ãþ5ì_ÃþÅ· û×° û×° û×° ûëSö¯aÿö¯aÿö¯aÿö/æHö¯aÿö¯aÿö¯aÿö/|hØ¿†ýkØ¿&ì_ü•¹ü{û/pžw•9íítëóÝ"Míwß§Îa¦©ýî½j¿{šòa°Oͽ,jOO‡òcà.×éþjþe•ûBíûÞ=œ¾_ôªïeòl”áËÀMù2ئ|¸«õ¼mjﻋÚû¾Eî}7Îdzªý=[Ô9*µæ—§öÀ÷¨ù˜Úç³Mú60Ö½Ô>Óíj/¼›:£Ùªüôªïij?|—Úï¥Ö ÛäYMÃχòuЪöþôªóYµÿ§MíwS{€jœÎj¹*¿yÊ÷A›š¿Vç8=Õž 2µ§u»Ü×j¬?º«5È4¹iì£ï”罌ù»šã¥©5Êmr€8óiìrU{ˆ¬jŸýf¹7VøO0ö¹ª=Eµï¾Yy;Õ¸×EÍ -jn¸Y}Wé”û Œ³¢®ê‹:oV&×Aó£ò ©±ÉMíÙ·ªó¤›ÕÞýNy®ÔØ£ä¦öñ[Õ»UÎ1¹å6yÆTìW2ÖRÝä÷ã{L‡Ü«dÌ/]Õ·«:_Z£öòwH Æ>^Wu¾4N1ݬ¾Ít©}K[ä¾^1Ï4ö-¹©=qjOž:oÚ&ý5ˆýK¾ÀùC×ÞþðöG~èúãß)"sA[ÎB¹YÈ/ ô,Àˆ zÀ@/ Wµ B¾ ä ‚F|‚Ð=>Ár‚l!À… _ôB ­PdEÎPò.9B¡ Ph„–Cv+4¬ÈaÏ žØpò(ü´ÊG¾¤G ÝHèDB':‘§åP? þÑÈÍ»hä‹A¦ä‰/ùca ~,rÄR¿cÉ‹Ø^5-7ñÈÿxÊ3úñÐJ.¸DôMD–DdM¤®'—tZN!’yŸ ~2øÉÐO†~ :¤ G*´SIK%-ÙS‘= úiÈš~:tÓ)¯tIG¾ Ò3à™n&¸™àfò>“üÏ¢>dõÈiI68Ùàd#G689”i.rä¢g.úæÂ#¹ðȇN>tò¡“|èäÃ#…Ð)§œBp Á)§ˆü+§œbpŠÉbxç>¥èX Rh”A¿ Ø2`Ëпø xT§åth=8ë©ä]%8UЯ¢|ªÐ¡ ÞUàÔ@³š5ð¨…f-rÕB³šõä]=zÖƒ_~r7€Û€þ-À¶¿-ÛdÿmüõïóéŸ+÷Ï•ûçÊýsåþ¹rÿ\ùŸŸ+cûÆỸÅøÍE?Zãòïíƒh‹Ó7€š>çYÊœö¡äÉ3Žv_yê¬ãiuž%Où!êV>ü¬rï·qæû´ün`|ܦüø¹«ï­NçZ<Õ÷Çu®ÅGíßkSþˆÜ•?¢6åÈC}ƒlSg[\Õ¹Émòì¤á?ÄKí#ߦλªo‘eêŒK¯òëgQûùÚ¤"㛤:ëÒ¡ÎZº«³à›¥"c_Ÿ§Ú×·Eú)2Î_ú¨o“ÛÕ™pµ¿/Nî›1öøVgíjïúvu>Ó]íõkîsFܪöûm–¾ÿ„¿=cߟ›ò§|m‘¾ÅÙã{¦‡ú¦™§¾k¶©o›½Ê? ‡ò˜'¿sŠ¡˜Ø#(|ûÝÔ^ù8uŽf‹:KÓ£ö º©=ôVuV´UùìR>]Õ·«ˆ<)¿Åu‹¨  ñxç ¿óÒÏŸ±/ÇC~×>…ß±çPœU7ö㻫s§qêÜúvéóO|OþYŒ}úîêLjœòO¸Yù(lU{»äþ}ã+°±žrOQt›ÜÇo|wrSûŠâÔ9öfu~µSúY2Î츩sìirŸQØ>éó% OíõߦÎìôªý‰îjbš:³S&ýÁˆ=HÂç’Øûo ¡ëoxû#¿?éÁðôGn½[-äQ0ðhÃ+:‘ð 6@ÈÀ»`d pè„üAÈ ð‚x Ÿ`t/‚fïC€ A¾h†À7š¡ÈŠœ¡ä](å ýP`C¡ý<ä²R¾V„ªC¿&ð¬À†ó>‚÷iЈ€GúF@7‚ôH!+t"¡ ,ò= Øh䎆n/z1È^ òÇ"K,ø±ÈK݉EîXôŒ7Ùãá¬ñð‡f“üÏ¢>d!S6ô²ÁÉ'þÙÛå´#9rÑ3}sá‘ \xäC':ùÐɇN>tòá‘Bè‚SN!8…à‚SDþƒS N18ÅäGq¯œÂ”À§üpJ¡Q‰¾õÈTŸ2pêù­„G=ù_ ^½xfè4ã¹›Io†F-ùÕ z®diH“S›h´Ÿ-Àc]ñ×ïOì†?11Çó1·1ç3ÎsQžbÞœå‚sçyŠ˜£ˆy‰˜‹˜sçù†‡‹œc˜ó 1§pžKü½yÄ6góç9‚û‹q¿9Ö7Çùæßß‹1½9–ÇvŒq¼¿;ÛÅ8vÌ{ÿØÆÛk;³ÇØæøÚ[S_ŒqµS÷O›ß›Ä·&ñI|cß—ÄØZŒ«ßuùqø¡Û¦|QŸVgOjœöε9ü¾¦7+_tyêl¢—:Ÿ¸]ú¢3| å)i꬟»<Ç |rˆ3ÀÆ>d«òoÙ¬|B7«=k^ò¼C`/Wô$öqоù¼lŠ„cqöYøk ‚n‡Ü³eœ7¼L6{¿±_§ô‰ެaÔ9]ôÉ"Ò1M¸a¢G–(ò/€v0™ãÐ=½# EžHà¢ùð號p~£ÉßhpS Í»htˆæ>’w±ÀgŸÄ»,®„V¹T Á™â?ødKBŽ`øæÀ7‡û àÊѽåèSNžnà]†x¯ ¼ßŸ ¤m -=Ë¡“Rò~46@{ù´Y7ˆt®<ô¬¾ (« âzMà6!c9y¶¼¯Cþ:ä¯óQM7ôª¡U ýjèÕÁ¿IüB§‰wuèX~)ï‹Ð­‚:V úZ~KÓäR`>÷”y¥èg‘±˜ü­ý*:Š÷žrjY*úXä©„wW1²Ô_‡>è\½p+¹/E®:ò¬Yàó¾uèYNt›Mò¬FôÅЪCÖJèÔ¡g…™K{•/ý8yÆRøð3ü–zöñ§ï!÷ÁÿÍôVy†Æ8ƒnQg0;”ïRå»t»òùç©âÑlWþOÜ•”6åŸÉEùÕ/“¾P¦÷*¦§óè§•_l«ŠM³]ù2uu:WÓ©ü5y8ùkÚ§bÔø¨3žÛ”OS7åC°Yž÷4ü6¹ªsŸiNçlÎ)ß‚q޳ê†'u&´µ§8u>t‹òçÔ­Îà¸+_üiÊ'÷6é“ßðÝâª|åïÉUùu±*ß.›¥CûYSwéÇÔˆ{Ó¬bßtIßMÆù/éÇ_œ‰~¡ÄÙuã\êvyÞðíï¦bãÄ©ø8[ÔŸ.y>Þðî¦Î¯Z”Õuög»<ÿcøLtQ±|ÔY fO§CÆÔ1|°º¨Ø:uöµYùœêP1¶K¬ÆÙzåoÑ¢ÎuÊsõFŒwuN(MùšjUþ¦ºÔÙzÒC-ÊcžòY¾Ežçë:¤ïrá«Æˆàª|³Z¤VÃ_MòÍÚ!ý³Š³ö~ÀùCÛÞþðö‡®?0þÀèȦcÆB^E’nž:Ð à7Ø` €À„üAÈ„îàe Ce˜N¤ø…O¸ÀeQÆäU÷¥È¼:¤%O9…òF^‡‘Vž­Ð·B3‡wVð„n²F¯Hèç‘ HðK©E<ç‹yà”C/ “zåP?Þ1¤Å€ƒ,±ðˆ…N,òÅRV±àxdˆ'âáOÇ YIËeA^$ð›\"õ<}S(—$Þ%“?ÉÈ’|2øÉÈ ýdè§ôÊéD*´SIK/ú©äo<Ò ›†Ìé”W:å•ÁûŒ9åÈ'œLäÍ$ß³à—…<ÙÈ l6:eC+œ\dÈE†\ôËEÏ\hçB;ÚùÐɇN>tò¡“ß#§-•ÀŠ9*4 Å=8…à‚SD¾ƒS N18%Ð/~ <+€/…giœæ”A¿ Ø2ô.£l*нÝ+H¯ Öƒ»˜õä[%ï*¡_…¾UèPÿ*xTSÍšn9-ª…-4k¡YKZ=<둯¾GN“Àm@–änv p-bì!æÃâÏÙÿZ¿âÿÝþ‰Íµr1Ç2çRæ¹óú¸˜'9ÏĜȜ™s!1ó1ï1ç5b#æ/æÜEÌQÄÜÄœ“ˆùÈß›‡ˆ9ˆ˜˜s1ç0çÎëßÎóç¹åjŸˆyó|Àœ ˆq¿9Îï_ w¬…{¸üxüVo—±YÄOq®ÜŒÍhÄe¬Q~˜j¤&ÃS<ïiÆh±û±ÈxE†o‘Nå¿ÚSù°îPþ«kœ|)õ(ÿ¢écÄ8×é#ã@‰¸‰"¶‘ˆ(â¤ò~”½ß6ÙÔ~ö¼¤ßÀÓ2~à\ô r•gFý¶«X+®ŸÂ|¸¸ ¶YúòC®p~Ñ+¼UÆa~¦u‘F¾†“‡z«<ßo]ÈLxQè€ Áâžô(ô BæPd‹:'—ô¢)§hx§Á7Y£Ñ1^ÑÈMDC;ZŒG€Iâ>wIÈ–p’Ę9¼Ï!=´tK|KµÜ*›ßRtMá]9²—ƒWެåÐΧø”U2& wòæ ßFt)ê‘Ms²ÖA£œ´:䩆_9éåàm€O)òWC«Úi\ÕЩCŸjèÔ!k¼›¨guÈÚÍ,x6ÛD^4WŸIù­p“ÝF!¿¥àÃ3Ÿû xVrS‹Éà ô.%¯ Å{Ñ¿WŠL¥ð®„oW12Õ_'úuò¬z5àVr_ŠþuÈÐ,ðy_I¾Ô!_ 8Ðm4ɻʢZuèQ :d®€Or7C§FÀ |A«F.‡ÖóFôûÐh¶™êÑ»™§N;ùœêT>[-Êgk‡Šõå¡â·osŠgàÕ'Þ—§Šá¾ÍÉ÷”UÅ¡íR¾§¬Ê÷TWŸ˜N1 š¥ÿëgÿSn*îW“WwϽUú;4üŠ{ªx’›•/×,NŦíRþ\=•?×mNqÀ,Ê'U›Šuà®b´*¿T§U<0‡ÇŒónø§òT±)7KU†ïrO‡Ÿª¿òóš¦|VmSþ^{Tìw+,OÅ k“ñ, ÿ¯nÊlœò»Eú@±m °nÊ'lœò »EÅLèVqÅ\T ÷¶Užݧ⌹¨ø@>*fæéwUÄ>šî*.nšŠ»MÅ–ß§üc¹(ë^*^nžò3»EÆ ˆS±ç]¥/ö 2ƒ¾UÆÒ ï1Œ8DÀèb¾&æ~*Æî6¯s‹ŒK/bÜ1Š\U¼«ŠIߥ|Iº(?î¯ÁðE»Yúsþ‡V÷*ÿ’*FC™Šc¶MƧñÌ “Û¥_Z#ž|¢;dœúh`ºUŒúmÒo‘ð³¬yç]xûÃÛºÁÀú/tCFX pòË= phˆ ¸¡?u# .ùƒ'ZA§åÐ;˜<&-º!ÈN|C ­Pò0Z¡Ô‹Pèn@¾Pt…F(4¬àç€c…†9¬àYµ’ÏáäQ8ü#€É7˜äN7’çHèDŠ{à"‹BÎ(ôŽæ]4´c)™b CþÆ ,ôcÉXê_,ù ïXpãàýxä‡<üã¡­xx&—\"p‰À%RŸK„fï“áL&ƒŸŒÞÉÐO†~ r¤"G*8©¤¥‚“ íTò$ úi=rº‘ŽÜéä[:ò¥ƒŸAzze‚› n&¸™ÈIþg‘‡YÈ”,Ùàd£[64³ÁÉEŽ\äÈ…N.²åÂ#·GM[ S l>´êxŸ½|xÀ£œB€ Á)$­°GNmŠÈ¿bà‹-†w1¼‹Ñ©>%à”³eÐ(ƒ~°eÈ_FùTäA=2Ö ßzpÖ  QÙ+§DUè]Ez¼«àQN 4kà_ ÍZø×B³šµ¤ÕC§ùêÁo ½Ütj@îh·×"Æb^)þœ×ÏÍy²è¥æQÆÊy9õÁ>W2çHæüÈœaköyÑ5λÐÎKùu‘þÄE“(ü1ŠX½F|Þ·rx“ ?ûš§bö4;ÅPw*ô§yÉxÓšU,H7åëµWÅ«Ü'c"¬Ê“1„¿Ó9辪UÆ‘ >È]ÅÊÝ'ã`é5Ò￈—%|ЇÇÉó{¥ÏSá³TÄÈ]×+}|‘¬|ýÄ˜Š¼Ó7Ëø:~À`ÿQÛ•ÿq~cyâ> ø`èGõJ?®Qô•QäE€Eú%?ˆßPè‡Q.Ñb\ LðqÀƒIZ$¼ÃÈ·`hû#Áæ>¬KúzF–p¡‡€G—ò*:ÑÈ’‚må›Ç}ïsÐ=w m2Ffe—Ô&ý˜'uÊn( :)m2ÖÁÊ-Ü ÈPÎsޏ‡°9ÐÍ /ÛÀû4è–“WiðÈâwÏ9ÐÙ îyìäÜÝRô,繜ôt 4¸ßHޔ¯›À)G¦j~#ȃ ÈÚ„ÜMÈ[NýÙ€~ø¾yJ¡Q íjèT’¯u<7ñn5¸•”{“ÐuÈÖ$hûú´ìv›Ä/´ë1ù’[åòk2å n*tRy‘Š ©ðHc6ðÓá“lï2 ‘ l&°™”K&´2Ñ3 ^Yðͦ^dŸ ÿlèdƒ“ ¯\äÏEç\øçŠqtóÐ!:ùÐɇN>ùž|>< àQN!8…à‚SN8E”S18ÅàûÞÅäS |JD>A£eÐ(ƒ~°eÈ_F^UÀ£}+ U¼ëÁYÌzêT%8UЯ"¿«Ð¡ ÞU𨧚5ð¨…f-ôiäÝ´Ó*¶Z\ŸøäƒGœô#ï±OÆX †N ô /ºÜ-*þ0üg’3»UŒ"+¢MÅ FæÙè>»YÆ-šÝ«â­7g‹ôQoÄŒp—¾êxÄàÎí–Ã#öôæ;¯[Å_ã'åíÉ;Ï.ïhy—Œ?k‡ŒÇ°’ú³²CÆñAE¬"áÓ~%ðÞ<{óìÝ«âAw5¸«=Tœ3hû ‡O‡Œ,➉Â×ýZd]ë!ã[¶«øgV»œuÛe,ÑTpü€ñ‡ŸŸ˜ 眺û¡“Ÿ(³Ír¸¢‹94uàtòA'ÿut´Àß ´,𷜓ÚàÁ $ï“Å8™4+W zƒW€ŒÁèƒ,Ð F®è„@'¸sj¹Œ<…V(ðaÈ&æUȆŒaȆŒVòÌ ptGÆà᎜àGßLèF’HèEA# QЈÚ,‡PQäQ2Ç€…L1YbÎËaW,øqÈG=ˆCþ`Ê Ço\‡f5·eðhà}ù”¯$d/‚_ï’Ð! ’xNá} ²¦ k °)ÐNv ´ÓÐ' Ü4`ÒHOóðÒ€K'¯Ò‘±øtd,AîtdÌ'K\È™E^dA; Ü,p²òäð/‡ôè6’žCzéyÈ’Gz¼óà½<ò!½ ÈŸFdÞ\xFÏø ciEÀ_DZiE¤•p_Â} ÷eäßzqOþ— k9üÉïrd,GŽrô+sôk$Ö‹ ÝÖ»6¿Á];«Á©Fîjpª‘½¼jäªF¿È»‘úÔŸÐÚïÈØxNI›Àm· ¼&pšÀiBç1Vc"ñw¡9‰9óç¹Ç?Ú‹-æbNaÎ'þÕ&ÎëËæ¡ï¼àBss`ŽÿE…4Çûç›ã{sloŽãûŽßÍ1»9^ãts|~¡=Ùæ¼ïØ»ïxûBcì¾ãê¾ëÐoo¶9fþÏb¬\ÈŸIßq°óø×ÜçÓwýÙóöYþ«qî…|œˆqmßýÜÎkÑb{¡µhs¼êìÿDìÁéëÿÄ—šcÒ¾ëÎ}ÇŸs^h9ÎÌrùÛ5èB§1¥9ž¼osx³/ÞªÆq¢·¹üxâ,ŠþXŒ§(³•è8»ƒ|c ¿˜qðqÈ8žöÀÇSÆ1÷±ÈxßF\%ð'RëcRžŒm: ù}xžì!c-®«‘q²¼I³py‹{d±ð;çéÈïÁs0t¡ŒþÈ ýh…"O¨è¿¡²]6+yoÝ&㋧ɘ©È†.­2¢EŒàm%-„çÑçñ.k§Œ͘m²Y‰C¿ð-¤'‚“€Î‰àÅðœ úRä³¢O r¥Š_ôM%á‘Ès"÷éÐL¥\2¡•-Þ£K6yhåJ‡¦èK¡…žÜ[ÁËCŽÒ3¹ò£€ôÑÏ8ø•½äË£ìc(Ïtä."¿ŠHˆg™€¶ ¸è­'_É·õÈYEZ¸)èQEzºÕò® :e\ ÛdsÚ€ÞéèÛ ès߀<µ¢ŸG‡äl€O-WeR&ž¡_ËU‚Þy¢‚Gü V@XOYl„×zÒË ±QôÉ<—À¯‘ûÈY%ú/tnD¯*hV 8ÑW ô«‚_#:®G®Fx¬ý<6£‘¼ßŸFø4’·½;e\4ƒ³%NεŒ¿ õ}ÿÖâú×á~üëpýëoÿû÷ÿßÚcücZ‡ûÿkñu-÷Öàþ•õ·4—ϬÀŽây4°£[ Ñ»UÅi=§â´‚7‡´9bìþ\ò}.8s¡3ÏUÆŸ'ò›´y=*¦8ï<Ñß“÷žûäPa9¿ËI÷y¯Œ ¾²Sƈ1TWvËx©+Ï©áÏ« å}ZÅç~5r®öT1Ÿ9| á³OÆ 7bKŠq0ï’Á_K^øŸSqSÑm8QÜÇñ~õo]—Œ)žÈo2p~¤ùç‡Î~äyáG>êðÖá­Ã[G7²ÒÉw-ð²@à ü-È|ðà’/È¿@`áˆlÁàÃ#˜üFŸ`1§@¾`1þ‡^ôB r^}B¡Y€|%Ȇüa܇‰{d ƒN²ZÏÉ¡Q8yÿpä GÞp1v'o#H8/‡M‘ÐË„~ž³C# QÐ(á7…ú– l 8™ÀgsŸ¿Zä/Úqȇìqb^‚ìqȇìqÀÄ¡c‚˜Ÿ gôÀM@†ò, ÚI¼¯EÇ$Þ'¡Cü“ —l p)À¥ˆgè§@? }ÒÀK/MŒµËäð* ¸t褋¹ ²®Göôsrh—Nrf‰ Y²ÀË‚v8Yàg›LtsHÏ!}£˜Ëð¾Lä 0yðσŽÏ£ãFøG¼+๠й c#pE¤‰¹ï‹x_ÄûîK¸/÷ÈZBÙ–ˆ9Éy9”¬â¾9Ê¡¿úU¼_O®v½Ðëœfn@Þ ÀUSÕbn"æ"ÐÚÏjxo2Rß6Š9:l2‘Þ½&pšÀiBÇ&tm¾‰¼hBÏÊÁsŠ¿¾ss®!æ}çšO˜óç¹Â¿ºÖ&äùGël¦ÿƒ¾ëkæXÞ›cmçõ01n½ÐXUŒQÿÿÙZ–[þ½u«>kVµ&Õw¬7Ôi,f®)ý£õ¤ íYügÖ~š]þzÝ'ÏåGßx%tGQwGS·F#ÓÊwL‡œÊŽ%Í›z;Ž_lÆ>Èã o`'’ë ±þþÐõG†Y܇´É˜äè^|­ØY ¸äñGžD`¹@Ï+úäˆ. Y’±Ç@dI6RÅ3´áÏTäϰØiªh;ÁÍCï8ò2üDð H+ âà›| ôÊDì%¢]E¯ÒâxŸÇorÔB³ZéâžÙÐ.o°µ‚¼‹À]ß,§šäAùÑÀ4Š6ú¤oäýF‡žðm„öìó2¾z‹°£ÿæŸß%–³q§·ú祃ðÿKƒ[tòÔr½çΦÛ×ÇíÒ‡z¿óá” ÅúqÏ÷¾½uoÿñóo~·ì³¾”lím¾‘CÎüJ_tâ›;}:"må÷û2gXŒ>IâÃ'Ïà³3yjÙ‹7~­÷ÜúËØ·Öéǧ,¸hù2[û­#/½k°›­tɹ‡‹/¾UÂïMó¹nÑ{nüêôüðßéLJÿ¢ãò¡ŸØÚ¯~üõ±úÌô»N½øíQ[Ѧ·Ã{©îmµ¼]{w+ømþÃËÛJ|ë¿ÕJ¹õž_/_}]Ç~ýØ÷Ìv&ÐÖ^UyÇ{‡ôÙ2ÝV¸0ùÓéo~~—Ä­dôO·—åùÏÿZ?öñä¯wFγµç<6æÙ™ïÛ “Ã2–Ý÷,ð½ü#ßþÃUÉzÏ-7<Õ½W?öú•ù^wÚÚ³ 7ßüˆ­hô §µßVYÎù¦w”ÿ4>­ÓSï¹ëùáá[ô‘*ÿíp¶&òy[{ÝŽýaƒcmÅ3ïÿâ?–>ž‡Ä;ùÁ{'}i‡ïùí©¡~5÷éÇž|骡^¯ÛÚùNûò™7Úó§´òµÃŸŒô_–÷®‰‡/έ>ª÷ÜûzþÞAßêǶZxOÕ&[û} >só2[ÅþGÞ~íÒaÀËrÛEînzÛj¯=m£¡ü’ýùØ/5Ôíßýâ×müK[»¢;cè˜+|þd«| ã±n,Ò§Ky¡+ËwWp©ÏÔËmzÏm^Ïÿx¾~ìÆ+› ßk÷ß¿ ®Öí½$³^ØË§jíåÅÒ§ÈzYλb¯Oû•÷¯©'-cN×ýÂ%ñ“³M9l•—Û¬W¼|#ð²\w•\š;ùÓ½ç xäÉ3ú±Ÿíú™õÁglí/_Pï‹­òȽÓ~›î ¼,×]÷¾X>èrw‡}üìõÆÇö^m>vùô÷c“Ž:ô7Ⱥéž¤Þø«Kî²UïûbVóŽáýƒeùïúr×'³WA¯õ¥µOß\c¯§Çª?Œ(I¾ÛÖN%§ÊØjîYýÅ/’V;ì)XÖƒG-#÷lž»üÏÍ»µ£<2ÿ|ô¶­e¦½ÚÚ7­¹J;”g«QñÝá)i; –õáÑõqŸ_ú˜Þsuþ7ç&_ã#à./-öz‡^oNšqÖ[Ÿõà¯äåùŽ| –õäÑë+¯ùhÂGÐñqýÙ.ýØÌW­oŒzÏÖ®û¨9´Ï¶þµxQó€—åÿh÷Ÿò®]­÷\óSß¼‹îvÈQ`w®çÏlí¥o½»`p‘mÃ’Ÿ~¶ux²¼[ððüº[Žè=›ãîøæÅúÑ÷&Nlo©1Û![åØwŸ>–q ð²¼fó |–¦_ûÖ(ýèÈÑÑCîrÔ³Þ†¯¾ŸñG¹Ërß=ëÉ­‹gÅÂçpÍÚo¯°ÛÛÑÇïþFþVÛ΋÷Ef,²×ój﹋öÔ-±œ ‘å»»>ñW_DWè=ט¹8Ú¢½?í…gV³íZZ0âô[­Ä^–çîû x¡ãn½çº°~¸ã2ýè·ÅLÒCmígŒ ¨/•ümw^¹óìµàÉòÛýÒ¯î®¸é ½çÊj—²×¡½9æä÷ÛWÛÚŸ|ÿL}UÛ¥«\íc«»yÙ¦ƒ1߀'Ëk·mÈÖ‡¾Þ¢÷\:lÍĹúÑ_¹®+j¡£¼oòxõæÌ ¶º»vžØÒµz屇¼ï~Å¿×äÕ_zL÷“õÊV·êhø{±mБå¸[µ+övHtSŸëG[îŠÿµÿ![ûÒŸ^1k­¾æ×SýOÌû“mão%Î@»¢ÊSÚ·Þ³áÀâ'&wèG«ôæ½ÇZmí²œmµÙ_%g%|`9*Ëïñ1"Ã(ÿ¼E[®|þ]ý(•tü®)þï¼uòæƒ;t[ûtðûÚ9ø²</òZhùªÂÑN ê‹ïqÔ£ØÇÿ±¦8ôŸtÝžÓè ¯© z¨åi[•Ê_{»*Ëûñw¿>üê½'òçϼ2ÀU?ê_xúÍû®·µ/‰þàlž­:rÚ;A)·šõ<ù䙑ßl Ó{V•W¥ýv©>ZöwúÑ$UêwÙÚ½Ž ¾wW½>Ô÷éwêón=¸æ/'GBGÖ‹NZ+Û…þ_šz-=•ãú’½œŽ|s óÕà‡íéhÃíõ´þð SÜ;Ý,7ËÙ0Y:i$/›üš£ßX’ývÖýô#Ÿíyþ­á¶Ÿ~ßõâUº¿Ò¯Aöóf}‚ެGm[÷Ÿ¬÷,øýÌùÓwëGÎüé°Íɶï‚ØÏŠyêž&àeýèüà/ikŸÒ{æ\ìAצé ºw¼þ¦ÿ›YËÒýsŸ´í0š£9¶†­?0¹àÊÛÌ~z²~y|BнgÈ‚ô#¥“ž‹*êЧ½ö’G·žµ÷‹uÏÎüÓǰœµÊzðÔWZËe¿oÓ{Œæ£R?’Y²´×¡Çç¼®˜uÈÑnZe¹ïQùxêÛq×}L?2÷³7âcŸ“ŽÔVgæ—U–ûëÍ/„ý~¸~êó-;vÇÄ;ÊOöûûL›“9 ó„}\U?÷‡Úë®9YöT྽r£~Ê0¯{~u7|‘OåR[{cÄ[çg>mï÷ìõÏ*ëÁžŸÿä½w*­v{:õöòÇs>ûAïþä²ðï7uØQÁ=¿tè:[ãëÃ…É/Ë}Ï=#.šX£ŸzóÃWµ_5ØëQ÷{e^ØÝíÐCÕo«~Ãû¡Ÿ¥Øš7ŸÏ¿xtd¹ïÙó›éy¶é§N4½õ´½}è~ý©Wnü€#ÿ?Øp}ÐÖ+m-!Oü¾qØWŽvÓ*ËÏ3¾Þ£VÒO½²è›ì˜-z÷Ñœ7Í_å(ÿ.cdfk~ãõÕûo?c9.Ë}O‡Ç wüî]ýÔ¡Ç.»íáõî—Zî|ØH{{¿ãwFCnkgtÐà©rÿíNwXØÛS/…Þ½wå´±Ï|oÛq‹çËGfG˜í˜­é^£àÍú Ë_ó¶qßî5iŽ|ܱdüM]¾ùnÑßÞï˜öhïwÂU½xòׇ¿]¨ŸzÜãâ7ƒÇ8òSɱ£%áèÊowØã˜c“ï(ùÆI.U?ŒiY²~êþ|Ï5—8äúiÁôGöuØv\;<#ó–ùº¯šOší«CUO®ÜöÙ±ôSÆÏOôîªÃc¾|ðzG¹Þ5˜žäFGýWõ¢aNèÐû£ôSLJ&/¿^ïΨïºÓ÷ÿ.³\v=²vìálõ÷}Ôöz4xª” 9¾Õýå~øˆ›Þ-¦Ïì±íxÁµç£­]¶†3ow\Û6Ïr6B•¿ï¾?Òdë§.__üàöAz·÷öåOmé°;wœýC&=@õ“Mk*‡L8ù„£ŒPõa¼è`_ÕOÑ”¶}£wÏŸõ§Î”©öqÚ*CÁ¬wô•JoUŸó¤UÆŠŒø¹~ªjÉ®–°·ÝÓ6½aÙG¹ž¹ÔcÙáGôÕª\•]9ÚÕˆ¼¿nŸ*žy÷Ûg.Õ»Çm›ðTÆ|ÛŽßû…>ºÁ„·5›ýY„*ÿÙ×F_¶L?•w&æÝßq´/ÃãÏOJ«µíõP_¡]=bÂ$[˺µe»n_•{bç{—¾©ŸJõßὉrø— ¦*Žü(ß1"`˜­åácg^μ!¢Ë¹]ÒOŽöîû/¼¨¿òå·ù%Zõ%6¿tÖì}¶M‘¯·ýnî-à©r§õúþÏê§"Œ†I¥g^Ý;±9ø «Ÿý¹¾Lµ‡›þ¸ýÖœzËÙHUÞþ´®ë¹ïízž “–±Ü^ï_é0 ÒÑ?Ï4&FöþäR×Û/¢ÊBOÖ‡½G4M¹u¤~jíûÌDwÛíð•˳gËusv=õF,ö»]ÛõŠ”õaï‚Á…4-v9N­øùø!)Ï鯈ÜÔâíÔŠ×οµ)Æ>ε·s‘²ìÝå}ßë§UÌ~Ì_åâÀ1m/>îЇÚ?ð–2[SsêüG[ÿEÊz°7ðã½éo>¯Ÿš÷§©e¿ÑzòáÔ[ºl;rëÖ¿8ú…HYî{›„¾‘]¤ŸšSýݨì¯ôÃÛª}²ò;Û9¯´5‰å‚‘ï/Ë{¯´K{=?å‘IKý®]ßÕù‹*žo3jqó ûüªITû[ö)ëÁÓbYaÖ¯ôSî?¹lðÛwÚÛçÃÑ ~¿û ­]´ ³Šìýª½ÞGÉzð´ûMõOW\á¨#Žõ¤ïÛ¯^Ù6é™IÛå¦ú{s<²I޳¡#Ëÿi5ï°ë5¨ûÏi_¶÷·‡§Þ½ú¹ÕŽüWö¬ô±]ZòÍŠìÙw9ì9JÖ‡§§\5þÅq³ô“ß~Wl9ØâÐoÀØŸ7^YjŸ÷ìø©ècÍõÛ¥²ƒŽ¬OËu.;þÉEAÜ¢ú âû¤¤q¶’¿mÓ÷D}ð&xª>|rï‡~»S?ùê³Q§Â=õC/NL*zc–mGöÁÂÏäüE_ ê_Kõö%??ºÏÔzª~ˆeŸÃ·Ûç•'KG,½ó*»ý\Sv*ý“km;ÎÝ–ñHó_ìù¡ìÍ1n‰îr®×úÉL÷¤ëîèÖNºª ¤ºÑQ?Î5϶ILã»»ÁSíFÊ1sp”wByzîÇ÷ë/ÿ`,¼šãs|ã°ËÕnTNs£(õ“¡¿©£ÉÔ_~Ê_>¼8ÂQ?ˆç#öñø¦­Sþ |Õ^Ô›t÷Í?ÓOú¼æ;Ãý'úˇ®œ÷èÁZG9ªñÎ&sÝ7Fµj]òäcVùá7_Ö]Ýt­:ú¥e÷¿~õ{ï¯ß³—ÛÉi_û^2}¿ýùåk?¨ÿÄVbošågobd=xZ®C;ìZÚ½Ý>^Îþ¡³gV £>Õ6üjÆ-‰öu‚M»Ÿ\ë½Ð“õàégó_ù¡£A?é&4|ô—ýô£k<ého¯è½îÓGwØ6]dT<ðTyŸý<þ¹›vë'E7b;g—çÀùA¹{ã79òOٳٴ >(Vª #Ëÿ™±Æ‡~âÏÏýÍKsíõàÀ‹S¯™ùBœCUÌï(Í.ü¡=l²ål¬¬Ï„Ç_õàM—è'ά9ûfú%úÛ GùJ„­}Ì÷Ã7w9ú×XYî²ÙŸ£Ÿ½Ìòùë—k‡ýà˜G©õÙ¦ö>ÝüT3ø²üŸéxóªïÞ±—߉/ÿay[‰~ k€÷¸âß;Ö­ä÷ ûx­YtwÜY/žùnYé'ë¾ìêÇßÔÄL«;É®·úžb_´<5âÁ÷?yÒ±Þ+ë…èæÝ ÛëÁ ¹.¯Úú‹«2óJkÅÑ7v…šõÙaO±²><{ýsåϯkqèuÓc÷_?ñ>ý@Àñ'îŸøúßÌ/ÍváR?cÁ:²~<ûäCszNýÆž¯'~~Ó›·~µÐÞ¾X;£ðû“Nr íö»}k }Þf'ÆÊzòì"#·ê'Œi¢§~Àk[Œþ×¶vr“¦ÝQ?ãd}°‰Ï #¿ÑOTµ_3¨lº~À#÷³žénöú`7ÇÉú`›uP|ÒOd7_9yêMŽzxIøžœUºcÝW®/Øí³ù»8ëøsCGÖ ¥àeˆ£¬ßß}¬ú'úþ/~‘äÝùgG¹Î4&LöuÀf9o†Ž¬6µþgÊqB?ÍüÛÿê–ùÇ®päŸZÏ3ÇÙʹ¯·9æ…q²žØr«NÿüOŽrQã`“Ïþ®ïvöÞn[{ʉW:t·¾\³š®:vuãç÷;æÿq²¾ØÊŇªŸè'\kF‘dï_÷?8jÐÛÏÅÛÚiBš^uŒUêKÖÛÕN\úÒ×úñsâCË}ÿ­ÃÂ{§¾ckïÙö虃¶&óûgœ¬¶­ÇOëòŒ~üÍ“½QƒnÔ÷_w4¥åÚt{ý4¿#4‰agᣖ³ñª^Èú¥Wßöon‹­]gÏÇÜ{.£¶ïxÞ¡w¼ª/j]áøÃ—§lørº£\~ùõ}ìhpÈ1ÿY~ŸØÛ-5>rØ]¼ª7j½öøs–:÷{}?³¯°ßí²í\t)3þ,Ý« úèg~gkQßò¨úòÇ›îÚyâýxáaO|\j¯û•äYoÛ9çãî„›"õjÞÔ"×}ã¤xY?ºù/.ðq¬ËÏ~rýºN?}ÿÃ;Z¶~2ƶs‚¨_˜ëAàÉzÐ5ÞPH?•^ÿ~æY}ÿ£?ü¡2ë[ÛNŒ ’ŽyQ¼,ï.1b[£÷ß 0 ¿øwI6Û΋®›úíM×9Ö«âeywÍyøýeçï±·KÇÅòûÝÚŸ÷ßu󎩱wÚíkçd£ãsôÊìëÛ ²>t‰¯É®ŽïùÓÅ‚T}ÿ5 ç½üÕ{ŽrôþúÝ#Ýv¯Öìíp‚¬]òsˆ~|˜1Ñ÷—÷Ńbm;E-™±ØÖtò‰ŒÛ[÷(þ§ŸçlÇwE±ªèó©¾?ú¾ÃWj_ØvÖˆÌþËQod¹w©ùŽýûàé²/º,Ð÷¯þг§$ÇQ¯o¹¿mtúÝözý7v˜ ÊßÍXÕ½f|øÒ÷Ïý³~ð®‡l;Ÿ9ÿeÝÀ¶FAæ®àU¹Ë~X?¦Ö÷Oùö©ð‚G¾‰Õå¶Æî.__üxÊÞ¿|ý­_‡y9¾ï]úQʉnG½”¤Oi¸ÊÞoƒc]Ï^Žª~ úÓ#®øJ?öÀ5ÏÏmý£=›ÚòËën½Ë!ÏwïŽþ°¿cj,%8泉ª^cä2º­õú~³™‰—|lÛi,»¶ØZÄ2à¶Ïù—¨ÊßXVL²×ËcWz ËÐ'¨vg¿Ç†Ÿ:u›mçÓÄ,û:ƦgWÝ”û‚£|Uýß­ôcÕI‘ EíãõýSÖîŸÿck§úî¶Ï‹íý[¢ª'«wß»âΑú1Õ¿íwÛSPò}±£~œ½¶qÕAŽö2QÕe7Ç‚o˜¿ù×רõØ÷ýk×õÚcÏׇىé_Øÿ|ÕéË]¾ªjɱ¥UWÝ|{Š]þ}ï7Œ½bÌ`{ù>¼H|8úÐ>ž°×—DÕ^\öÿ±w-nU[ßR䦸-7÷ä"*¢ä`šÙç%SQÒ¼k⤼vñ–æ­2--*-2“½½b/аw¦Ÿ,/i¥=™Ý¨ÔH;ó›wÍ;³¿Îù¾ó<ö<<5Í;3kf­YkÍÌÚëÇ Á)v&Œ?`ç0§óõ6Wß|LÛÓÿŽ\H­¨®!Â÷B–~ÂOrìŒwEÿk>yR®œº['õÄž1¿ð›py–ï6#…¿&÷gÃo!p¡™sÕ7¯í™¹^Ñ¿dgÓà³%?‹Zª>{ úk¤‹ÅËÆç%_g ü–í¾ß˜sÚ¹n‰ªõÓÃ\f©ùëרÐ^ÈþLÝŠ5p+ùômæ:­qüÛC´2“¾ÕºüæV§ØÄœù£žùþ3)Î^ëæ<<ýw­lø±£>øR[p;~ú7ŸMA;Áoþìãûkï6N­”k•çò¾¿ž›×Aéõ¼–·_¿3«ÙÔ¯ 9ü »Wú9ΘwŸ=hQçþ2á§|”ç”,!Üá»ÂÄyÕvã䦶ӵ²¤-œrmAö×&ÅŠï…_ÀÃ<&:XCšÏ¥•#eÎVCÇåDßÐÊ9=ê†3_í“,álçŽÏ·¬AØ!Ç÷»øÍ„<‡—­:¼`ùÜ×¥û&Kø‡Ó¾O¸úÄVµþa#N}ñ¤‡œ¿Ã¹xï¾AZ™ˆÛ‘÷.Æýb–ð …¾hh=4àâ‰W™cgäÄóÁªujЬø~×amÁÐIU%E ° —ïwúý™»±ž9–_oýÃ|É7›g÷%SÚ$|Wz8Kø‡žoð­ÞYi˜cöŠ’-s¥üØ|kÒ:}ö€<Ÿï;Ò®"9¨‚vÝw­ÕY¹ÁôM6s<>¤É>:V³™tÅ¡5¶|±ü#¾'>Wé6}Y=œ†6ÓÿÁ¹žSÎ i”òoë<|dþ®IÆxhGü®ñ|†|Õ·½±åÒEæÈËkmž¤æ+oýy†|g~…²£Hª¦ø¹ê¦Û:pœZ½&‰9&_Ÿ9é¹ë’¶Ôð^‹w c}Néùê~vÉEUw¶è‡SÕ»éÎÞë­¬’eÇìIï?Ú¨Ö•æÇ˜qî×ÃêÌè䣪õ­s«öJýR¿±íú ÿPqZŽyew[ü¸HÑ×Qtb™ä£’—ÊŸÛó“¢kéÁ­Ï—\•óu–•-Ùªè2ñ‰ù²ÞâÞ¿˜_;/¹ƒþHn*Ïö\1æn±úy›ùÎbpkDƒz—´…ñ?©Ù;ò›§Ì£I>*+K¬;rÕϨ«¿ÒŠ9¦N¼ãf_5.é-­Në’ x´#9©Ü7öÂt%ï³ô4æ÷EÜõ+gûü`´ç§ãŽ-ä=Ö|þš<ýÜT>e¹üñ…3ŠÏ3ÓòµqO1ǰ‘»­Z°füáè#K‹Õ;>œ xÞhOrR)â+ê'ĉf–âCf…ý—¬_5[Öç-nk…ƒ·p ‰v$GÎé^E¿xwr$?—õB‘ŸFcݸ7?áŠz7â+F“<ñYõC¿UôŇÌ=¼öMû×}¾ë7|Rç·=4ÛB~­-ÚóÞÃç[¢ñÿˆ~}ÃêûisçôØÁa/Œ|¶'öÅ*ý;šø[ñý'c<;)yéê»Gæi–;à±›òÕ¯<êÐ+ÚSÃô‘yƒÂŠYÝCI{Zßé™±žû”ü9(nWíS;\áêžÂé0ýÿÅ=;«ç§¶êâèT|{Í×MJî>ðYµc‰K®Ÿ¼/È&þWèÓ¿¹øi¦ @ÑCïGJïlÖ“VLví‰ÿuïý´}oæúÛ¦+?Í;Êq}ùL4ÛšWqòاünŠW@;â…~]4Bîc×…ë}û£šGLÅÂv«ô3m+KÏYê'ýïz¸óXôGrP±­MÕW;2™ë;òH æh§ ªf[÷åÈý/öVz6›ø_‘ÊO3ב— žøþ†œwÝï§»ç^Ðlü¹ìÏϵ"=ìx6Ú 9ˆ6”Ÿ®f®=wãöX糺sÁ »róÔ:é×»“ä~ãªsÂ’‡~Ðá˜K¼oÔí}qŤËIšM߬-8Z¹3`Ûe|Oü6â.\Ü µ¾!שî%Ï21Ô:ýÞjÆáµK´bqN–z} ñû£…ü€=N½SÓý2«[xmÄîG—jvÅü¤nØEi¤C|ÿ(êÊŠ-݃™KwÛ¼YݬʹGü©Ùy˜å¤²ïâü)íââÿáSy[kÖx0×2½gV7e›cÐsk5{@cÍà5êœ6†ø{˜ß´~›¹DügÝ´àþ~Q«5{@ûB+nÍãÂ÷ÄßÃÂßwMãú”Õ=ÙðÐÞåš]¿I׊)Nß_Á#ÿO¬yÌ5îŧ\ﱺùå=ì‘ç$»çü©Aé§WNî¼è¡“Нc‰¯å9zàŠ’k8ˬ⼒¯Âæo?½Z³‰óÀ_Ï“ò~f,ñ½\ÜH~‰wÉ~ëÛa¥f»ª_Ä«{<²Onôÿ¸1G æâæðî7Rï×MynÛùÀ…šoÄ·Šû3Åÿ±ÄÿCâ>ÕõЂ Ys³ºQï<]óÝb%‡göLwh†ŒŸ˜/è—ï?cIéáe 'ƒ?¨¼ÂêúÕÏ¥ÙÎ÷w½§Í˺£ôh¾'98xsÛ­'w¯PëÛç†ùµ]½Y]Ül†£‰Ú‡_;òëAûO›Kñè‡äãàûÌê]ñZWìºAu¥¬.PWÀj?¾è+䄼ÿñanëAòspÈÒ6gÎUtA™MŒxÕÞ]›þ臚,·•ýÍ!y9p—?üx1Wfú¿öœ#é©ýû)kPøQ5Ÿßí)“Îl•çƒBþš=å-µ¿sH^ˆø5¯Ö½7Ž`µg¯—=3a†œ—½E—µq •q 2. ‡ää€VÅjO–¼>oÚôNoÜ‹á{’‡&ݰ¨¸“=[¯½û‡,×:;Í||xœŒ‡±ƒJP ìZÉÃ~Îõä,æê—°óé♬֑mªýøÍÞéƒAsk;È÷(ñ>‰v$ûgòº(æê?œM¹y í^˜µûƒZE·þP ç+Ï{9$ûÅ;‚\7º`µÇ2¦NÕl¿~Ògôˆê}×xÊ!þï£wQÅ]|“åþª=´}Ûí”\‰ýjèIÃNÈsm.ÉÇ>}e0¿ÝëvNÞƒÔ¾y±áŽo±fãÑPV³¼ç-~{²áïæ’\ì£÷8æì±É¿g.«]T‘ñ̆ušMÞŽÕ wý¶þÇ[°ë¹Äÿ}½ý XµßÓ½líÐ%ß¶OÖìÍ—×-þ3Uê™Bþ,úüeã=íIö^ë1,Ëhö¾úRëþÕ¤¬ÚÄy™7Æ‘œØôðßÞÌE÷ì8½OköÜŸ¨ã#y°mÔè˜KÜSŸ6=öÚÃí_2Vßô¶k ú};ñÒÀhGraë1)KË\¿0ÖáxÞÞ¯]VúÊÏnŸz¢øôCòQÆwßÜ\ôÃl¨Z¿¬i^»>ÖìíuÇCʵ¼ßGò°ç¢¨¬øåwzÙ¥TvœâïÿMÏtÈsÀ8’q¯+ýr¹~ÉýgL»8Yé_]¬šI¿HžCÆ‘\ìïÚ’—nìÿãA'Ê#6¿¡ö¿þ ÜBÞ§ÌÏÑ^èäAÄ?2W ¬«e57‡ô5'v•?× S¿WOüßÅ­z^ ¾ý¶øÓ?YÍé¡+¯ÿC³¹¦¬m6âoÚzOÄ÷Äÿ÷át6[ü©’7ŸoÐ_Sú´g—ï¤7ø0‡ß΄<Ž~HJÃõƒ¿Š7M˜¼at}±ì·æ¹¤ο+ûa»¸f÷Ÿ}Ê.þÀx’‹÷‚¦ó_`ú=&«³‹&í† R¼tm©6â±ÑŽäáÝÞ<€m¼šÿ¹Å»_©ù°+¯æ%4èçøBãw€ãIvŽ,|'dÐ%¹/j¢ò®)ý7¾~ŒOOr°#×ÇšzJ¹®ñ¹zÃUÓ Ùô0‘EZÑ6ý‡1øžøüN´“ï(æÒÍãlv¬ĩ ½4Û9~!ç§â€òˆÏoý:aÂ'ï¥*;­ÿPƒ»:èÉ—r6©{Á7ùnCú ýÿK-ë6«ö¬’WhOEر“^ñs„©ùòg‰MòžTž‡òˆÿo†¯Ë_6/›¹Ä{Ô1[Êš—lUü¦ø/­¿>ÀÞæŸ·ûê|—«OlmyŠ[é¹ñøa5îv=0C¾ÿ­>aªœ¬üÇ<âûë¶¼=G2×1—¸g2ø,Çïç6Ÿj6=|?Yí3ò¿”ŸG|Mã°£á‡R|ñ±ðÏoxôIÍ&ÎgQñÿÄ÷­üWµ‹ÊàÇñ…­cGÏWÍÝ|û1ÍVÞùNˆÏ/êw6yÄ÷-?u;ûÖãטë]ÑÊýstýèßÒ>Ÿ ø(ΓÆ=À<²O*.+Ÿäb‹xWtˆðévŒ}ø÷—>ëö´Ò_GÿXß5m ß ‡6 -´¢¿ ´š*rvxQÎ=ïV¹È»…ïÛˆ|÷&‘w õÁç ÇJÇ¡ÅØí1“IäÜ­&”;˜m©Èso¢|®!(‡€öО”ã>c…^ø³Å"×è]aX—0”ÃÑ6|ªÀžEÛŒ‘-òla,3ښѷ¹D`Κæ,¾ï„yD¢>r™ÀÈj&òj¡mT9©Y=—ý*·œZø>¦€òØsõ‹yÇ¢},Ö)¶QäÓB}çr·\Z”·Þê%ðdA‹߯Y~l©Àmñ˜K| aÅê8±ùöºÀ„-X°X·D”1~"Ö"sMB9i˜À{=IMÛõɨOÁx)?íSNk±È¥r*hM- ¼ÖÔFÂhåùÿy>}n.º£ïîè«;Æî‘LùZxN\žWŸçÑOËx­”·—çÃÕsêƒötô^ °Z›(—~/ÐÒ õ½PßÛLy}õ¼^hŸrFá´Þ³ÁÍîÙàeÍîÙàÿfÌ÷gXGôuŸYäþ_%ö»Eä'Ç\îG¹¹E`¿W ||ßååc}xäã¼õpŠ\—ØG-!‹-m"§•Y`Í 'Ê^؇^èà }¶òð¥ë:Â{˜¼EäºD¹5ÖÏ:Â'[äÀÂ|Qï›/ra]9/‹)ÿ¹Ž²êýJE^¬L‘÷´ø£`Xðåo{8´‚ö@ô„öAX³ nsA[[ŒÝvå¾äªKÏ}Y,r¦cžÁÙ"÷åe7¼ŒÝs1¡Þ„¹™¶x³Àš±‰œ—f‘óåК)ò]b¬ÐF7œ¬Sè]aX—0”ÃÍÿmÃÑ6cG`Þö;ښѷ¹”°etÜ÷|Â|çê4õ‘«¦ x…yE¡mTµÀzÝÑ(GWSž«|SL9-cQ‹yÇ¢}l¹Àwí‹ ÛÝ‚u³ l)¹,Q¶bl+h±âÛ8ÐÚâ@Kæo˜í¨ÇÜÀóÔ' >õ]Pߥ˜0,u,v‹À`ÇZ$b®I¹z’ÊÎ:èOF}2êS’Ž:Ú§`ü®(w]&òk66g*úOEÛÔ&Ê£ÆóÏs<žO³;úî^"0Ó{ÏGÏqmxnM7´¦5QN5ž¯Sϳ ÚÓÑw:ÊéÕdfx~Í^ù3½‘ðÒyþ|ŽeÓí3,/ën?ø?Üör{Ëm-·¯ÜNrûhØFÃ&r;Çí·OÜ6q{dØ"wgÃþ8ΆáöÄÝ–¸Ûn# Û€µÓí·Üp}oèw®Û¹>7t9×=Õá†îæzÛÐ×\Gs}lè^w}Ëu­{ACºëMC_r]ièG®¹Ðqh±öÍùy¡\äãÅz{Ÿ­ü ûÊ|ñ-! YŽ™ˆvAàEÚ¶E][ÈC;/99 ¾NynMÚ…€w!åäÊ„ùj!“/»"¶ì'´1C^:aÌÈLÂoŠÁ¿cÐo¬¿ÀdZF¸°üÛ‚=jÅž°â{+ß3h‡qâ1—xô`ƾÀ'bM“A_2÷7ñmŠD¢ë*rR«… º{`¼4ô“VNnŽÁ„õHÇx½Ð¶ÊÎfê,wϼçÞóÿ³øÿÙ4 þò5‚îÑóÒg‹|éNÍÝSä9wÑönÞ“pº›;Ýòœ£Ü¢špk<ЇdÎ|ó@›–^ËrÖß´l"<ž×Ô³šÔƒê½Ð‡úleùÎmkeïlÂü–Xƒ„yÓëãcÆ×Ï%"ç9ê}Qö-uˇºLä=ÇX~(û¡Þýûÿ”•cú£ì†9èOXá -û„?íƒð}úk ÚÚ¢¯¶˜k[̵?åPÕqs@{0朿-smÛ£ïö»=Ê&Ô›Po*%|ñ‘÷cuí! %åÐêE˜ã¡[W'´v(ržãÛ0|†¾ÃÐWÆ·þ¸žïm#0vh(¹Î±æfÔ›ËÝòœPŽóH|‰¾#7ˆüæ˜WÆŽÂØQNÊo žD£í$u30Ìc\KóŠEûØj‘×õW AÔ[P¶ lAÿVðÐZ@¸æ\MÇÖ8Ї¶q 5åx”ã±nñ˜kæ–€ïð}ê» ¾ xÒõ]PŸÈíÆOD9ëš”LØåI ' }&c>É('s›ˆú´OA)è¯+híŠrW|›ŠÿN©è;óìæE˜I<7;ϯÎ1Þ9†{wôÓýôH8GˆçYçXíi ; cöô˜IË(Ç:7)éø>k·èè…ú^˜WïdʹËóªs³“rÚgp½Aadg ßÏݦr{Êí(·™†?èn ›Èí¡a ûçnó 'ºm3ì™a¿ ÛeøŒ†}2l‘a‡¸ â6‡Ûw;Ãm ·+Ü–6ð†­àv° îþ£»®çºÝ]—s=þ×üÓX?]s]lè_CïrkèÙ¿úŠ\or}i&܇û±Í3Ý0VM„¯êÁýFðÉ^à£þŸê[¡·Y`¥‚Ÿ­Ñ¯d×§TälÆ_›LÊ×ÌYæ¿Jà.@›¾)úi‹>Ú™W!¸„p¸LØË&›À(Ÿ ‹”»G±/;¢>†£>óÀ˜ð]'üwä0 BŸQh…1¢ñãE˜ ±¯3²‚V+ÚX«…넾PNø{o_Uuîýg£@˜s˜3@„!„s2'd: ™IÈIÈ2Oµ­C¬Sœiµ™DŒ^­i¯öÄÖ«hµÅV%BµÑ´W´Ú¦U+Z«ï÷Ùkœ”ÚÞþï½ÿ÷mï ŸÏádíõ¬gZëÙk­}öz~”× ÛÆizÃ'X¾eMÉõÚnðR¸b©ÛHÝÆsçB½¦çäméR¹¥ÃeÌ é1=ºž]OŽ®'ÿ9Ÿ+úé>?ó&Ö¡]å½7ñ‰ß1 :>º\@ù‚„Ø9¾|ìñ„ÆÏ!fÆ2®ÆÒ7c¡ç¥1t3-˜8:”ÇŸÐøÕÔ{Âó_ãW3î&ôjÜD?Ø£séÃ’CaèL¢~2¼&#o2åÉÈŸBýÊS(O¡<ÞS»tŽ}ôŸfåCý4ø{Qö"½º4΢‡Æ±îÔx:>#p¬û4Ž5üfR?]½ÑÍ^ÞØêMyþ˜ý,l™…n³Ñm6ºÏ>¢ñt=»ç {ŽÌ Ôû4ŒÀ°Fö\êç"k.åy”çaë˜úuð_‡Ÿ×Ñ~=¶¬‡ßú} #;ÝCÐ5þ!”7ø(|%/[6Âk#¼6ö¨ic“MãföjìxoF×ÍÈ õQ8œ&†å-ÈÚýdm9©ð{ C—°>5ål Uø—‚ÕŽ/©§.÷¹×Ê?×+s«k^•¹TæN™7]ó¥ë£Ä½kNt͇2~Õ<(s kî9ïý­ùnäü6ò9åÈùLæ2׌uüf Ÿ û§WãV…jìÎn}IlŽaüéÖø—^ ¿\ð¬.€÷…ÄÎ…•ÿ;Æzj LÆÌXü>–6ã|5Öý<šñÐŒ‡f<åñ'5Þ9<=áá9 q0;øà× Üï&k<,Ê“h; 'u*\¬IÔO†~2ô“©ŸBýê§PžBýTÊS‘=•14ÞÓ5úiÔO£_¼({!Û‹z/ìŽ}Ó©ŸNy:ô3¸w̨T¸˜3NŒÀĤ<û½‘ínÞØê ý,™Ÿì|(ÏB·ÙØ9›úÙ²f§íÚÎA÷9ðò¡ìC{ê}N(쭹Ȟ‹_æRžGyåy ?Î'VçÃ{>åè¾Y zÔíÐÄÁ„vaŸÆÁLÐ8˜øx1´‹)/v( L_ìôEßNIyI‚ƾ„÷R{ ï¥Ì‰Ë‚ݘ—˰{9åå”—S^ŽÝ+|5Þ%åԯĮ•Ô¯ìÖx—Ô¯¢~åUÔûáW?Ê~”ýàïŸü‘ím²Ð-€rº¢[ å@Ê}êö½]WS^ ýü´†>\CyÍ u[‚_}Ô§nñk)¯EŸµÈ[K›`äã‹`ê×Ñ~üÖá‹uÈ[üõð[Dám†@B9„>¡íÊൡGánnÄáµQÖìèº)WáÞo:¡±7á½™òfÚ†ú)œµPÊ¡ÈÚ‚_¶PÞŸ0ä†A†aÔm…×VÊ[¡ÝJ9œr8~Gp‰k¹‡Ê?×¼ëz7Àµ–—yUæQ×êš;G®çÏŸ/¿jžtÍ27ž?~ÕxþÜ÷UóÌs®ùÍ5¯¹æ´óŸ+»æ¯¯ZÇ»æ¬ó窑kúóŸ9Ÿ¿–ÿªuüÿ—w\óˆk‘¹Ã5g|ÕsÑsßóº4~0cb²Èz_ÖøØ½Ð¦Â`‘|C·£«/qâËßKà±ß,¥ÍÒ^…¸ =–cërþ^!ë}tYÁµ•è¶ÚUÈZ…oVqÍo?îÁþøÍ^þÔðï@ìZÜÕ”×`Ã|„NAÈ_Ë÷ZhƒñA06sm4ëÐqm×C»¹!|‡à›h7@»6àŸè·‘vñÏ&lÙÝfÚoFÞfämиðÚ‚¾[Ðs íÂàƵ­6…õ»•¿ÃÑ3áØc®äßè:ÿÿî:ÿâÿ?ZßÿµµýÿÖuýöù|®ö;z^“–q`à7ƒ¶cˆ·1Œ›1=£–ò”/¹€˜¿ú ní…è:cmßß…f17.WãØ¢ïxhÆÛÎø¯Ô÷ÐO F& Ó„ê61eÁW“? ^“¨ŸÔ§1Lýdê§xiœ{ÊSh?ú©v>è7ý§ùj¬{ÚOë×X÷Èö¢Þ ݦC?þÓ)OG—Äû ì›Ñ­ñi?ï[½ÑÝúYÐÏ‚ß,lŸn³©ŸMýlt™CÛ9¹#pî­çžò\Ês©›‹œ¹È™Gyåy2O¡×|îKóá39ó)/@ïèµ OãÛçºñí!wåE´]4 ðícóbÚ/†·¯¯Æµ‡Þ—ú%资ú%ÝÏžú¥ð^ ï¥Ø¸ ›–u(lße”—[5Ž=ååg5Ž=ö® ne {êV¡÷*ü±Š:¿`[ߣnÁþÈô‡&yè€üô DŸ@|ˆþس{VË|Hýjê×P¿>kdnÄž ìBn¾ ¢¼–òZÊkÑc-z¬EF06SLý:Ú¯Cö:Ú¯ƒßz䯇ßzlXmz†P¡må ´Ý€½¨ß@oÄþðÚˆn›|Vò&xm‚÷fxoF×Íèºù¬ÆN¦>”úPdm÷Ê[ðÁx…Ñ> ]¨£~+í·Ò~+í·R§Ž/ÂÑ%z(€*5¿Ž\Ç»æO×(óßȹÏ5ßÉ|ö·Öç2É<ôUën×Üñ÷¬©ñ—9Œ¼ÿ\+»ž[ÿ­5°kýû·Ö»mýjWôcÇcqßàó ðáx|9ßzrÍ“òü?ÿO$&‹SðùÚNÅSé«©´›Fý4ÚxɺÓW/KÐs6cm6´s¸6GbÛæÑï  á³=A³H¾©[DÝbdø3¾ÐûR^Â8XJy)>\Õ£–(~èDû t ‚~-¯….˜> æZ0×Ö!sí×cÇ®o`LnÀžŒýÐlD¯M0ÛŠì­Ø¼Úpì ïQK–ÿUk¤Ñg¡£ÏBÿ»ÖL²×,õßóùÏG|>æó>Ÿx˜ï9x|Êç3>äó9Ÿ?ñù‚Ï—ú*ÿÜà öH{$ƒ=’Áɯ÷ÜÜÛ îí{$ƒ=’Á=ÞàoLÓë4öH{$ƒ=’ÁÍÈ`d°G2æèóÓì‘ öH{$ƒ=’ÁÉ`d,Ög»Ø#Ä¿AüÄ¿AüÄ¿±J¿÷KüÄ¿AüÄ¿AüÄ¿±V¿ÃAüÄ¿AüÄ¿AüÄ¿±Y?&þ âß þ âß þ âß°è5&ñoÿñoÿñoÿF¼>ûMüÄ¿AüÄ¿AüÄ¿‘¦Ï¥ÿñoÿñoÿñoäèw–‰ƒø7ˆƒø7ˆƒø7Šôû'Ä¿AüÄ¿AüÄ¿AüúÙ6ñoÿñoÿñoÿF½^ÿñoÿñoÿñoÿæ¹uâß þ âß þ âß þ âßaƺģĔÄE‚ö_Ÿ¶Ct9§ßƒòÑïÖw«5‹™ë _ï»õ¾”¹'óÔïM…ê3™ýþT¯úýÅ<›é¡ÏgZõÍý>UŸú=f8_‚M½Ëjþ.sD½_eæNðÔïí'èóQz]uBçQHÐë«.½¯;¡ŸvéwùOê÷¼ôo76}¦ÓSŸëLÐù:ÔùNó½~«:w5¹CŸñìÕ¹ºÕóC9ãi¾å£ßóÏÕïauê3ž}j g¾çï¥Þ%0s-xè}ŸŸ~¯ W=_4Ï{æêý_§þ}§GŸóêÔ{À>õž–¹ôQg?Íßx<ô»Z¡ú}­}þ³AŸíQ¹äÝ]ÙšûA?}´AçbèÑÏ+»õûZú-ýÞV®þ½ÇªßÛêÐçzõo?'Õy2óÝ-õþ¯¹Ot¨w¶Ìµ¨§ÎÉ Ï„vè}aúÍÇÌÉà©Ï„Zõ»Â ú,@:jþöã¥Ï‚ëó •z¸Oíå,¨ù›‡>ªÏ…:T.9`>õÔïn…ª÷·Ìý¡‡þ½'X¿ÿ«ÎµÉùOó]-Ou®Í|g˦Θïk5èß{zÔo>æYO½7´ëýá>uT~ï1ó1 ég©~ú÷»~¢[ÿÖÓ§÷ˆžú7ŸµW4Ï4è| =ê<€¹WôRgAÍ÷}ô™Ð\}.tŸ: °y@Ÿ«óÕyìê\¨ùÜõ„~öê©s2XÕ™P3C‡Þ'öªg±æ™Pdl“ ·!g|¶!g4Û ‰€_ü,ð³ÀÏ? ü,ð²ÂËŠ>ÖµzÁ G¯xxÇŸUË~ÁÏN@÷D ‰Ø˜ÿDèÑ;ñ¬Úlç; ý“Ð_ðP“°3 ÞIØ™ ]2õ)Ô§PŸÁ6L¡^0åR‘—Ú¯¶i虆¬4ÚÛeC– Y6xØà‘Ž­éèŸ]tÐeÀ'2à“ŸôõùÆ÷™Ð F&t™ø3 É÷Ÿ.ÙðÉæºä«—ó’Ÿ<Y9ÈÊÁ¯¹ÈÊ…&š<øçÑ6ò¹žOɇ›~;åƒÌÈ܉LÉ»)¹4%f!|vA¿ ]wÁGrÖI¾¹]Ȱã;ýeÇGv)Ó¶˜¶Å\/¡ÝnþÞÍß»¡ÙM›Ý'õ¶ˆ~.Ãæ2®•T[¤rüZÿ øW`C×+àW JxTB_?«±£š~®ÆŽ|Sƒž5\¯¥}-íki_‹-µØR‡ê°¿] m€O´ èÛx4£C3´-ØØ¯ê[¨oÁmðsÀÏí^tÙ‹î{ñ—1úþÑèž»ÃctÏ=ºçÝsî¹G÷ÜÿD{nƒø7Ç–Œ# º¯;T>"é7¹wI¾É«gžË Uý1ƪΉïÍwÕsÕ{,âkó7ÍS°C?’÷ûäÉodþö9¤ïôÔçÜmú·Ï}>©W{—wßåwóü»ŸþM4WçBêÖgáûõ{~ê÷Póý™JŸðˆ;?’¼7(9’äìŽüf*goÌ\I'u¾$/}¶É¦ÏÌwéü…'uî$›Îa¸Oå1”s§æoªûôy§~ý¾¤~''WŸ£÷Ògém:§R§:SožOMÐgê;õ¹ú>_éˆúÕ*'ŒùÎQ®~ß²SŸ›êUçïÍ÷<Õ9^ó=$»Ê‰(ù—䬔ùÛ¬—~ßҪΘù=õo³¡ú]K»:‡/ydÌs^úì½Uÿ>ëÐçú·Ù^õû¬y^ÊKç>¬Ôù»ÕÙûµ:Ó9ý»l°þm¶R¿kyD½Ëdæ_Rg‰Íßgm*¢œ)6ßkÚ§Ÿí׿ùèÜ5ú7Z«zÏÉ<ß­ÎJ™¿Ïzê÷-ôï´ú·Ù“ú÷Y/‡)Aåº1ó/uª3øYú7Z•q›|³ 9Ûà³ 9Û ÛMü"àgŸ~øYàg‡­ècEŸHô‰DŸHê#©Âž(ì<œhlÊãZ4¾Ž&bh³Þ1´‹£M"ý”ˆüddÆãtÈ ­<ôΡm¾Ú ïB|%9åwâÇd&Ð>¶‰”¡M„"~HDïíÔoG÷$x%¡ûNø%ÉKà‘„ÉÐ%C“M 4)ðI¡^ð¢SÅWðJEWÁ7ß½‚S.ã6dÙe£Þ<Ò±3[³6:Á’ü× l|VÁTÝþ‚¥)ÛÁ°ÌÄ&Á–ÌÄŸ‚Õ—…O²Ñ%>‚5—ÌlÏ¡>Y9ÈÊEN!þÍ…Fp¦òà/¸>yèO]><óñY>úí”2‡C¶6ø«€úê éŸ]ÐK®zÉ/¿ »· vü`G®ÙOª-P1m‹¹^B»Ýü½›¿wC³›6»Ñ§—ÑweØ\Ƶ2®•s­¿–ÿþØPÁõJÚ7ó©„G5¾¬Æ†jÆE56Ôà—t¬áz-mÛ¡¯¥}-ík±¥[êðQö×£Kô ði€¾}àÓŸfth†¶[à×B} õ-Øã ­~ø9 Ý‹.{Ñ}/þŠ…—¹£yÉþóyÉð«¹÷‘=ìmØÓ˜{×þEö,²_qíM\ûÙwÈ~ƒ>ù³ýcÁÜ?ÈÞÁµG=ÁÈõ?}g®óe}ïZ×»Öñ¥z-&ktYŸËš\Öã®õ·k=r]-kiYGÿ£¬›ÿ£53÷¿X/Ÿ¿V¹N¹Fv­G®]ëbך˜~6×îµ°kìZ¿¯Þ'‘“rFDrInJÉ7d¾ÛeS9%åÝ0y¿Ú<ÓÝ©ßùêVùuÌ<Š ú\D‚:ç ¹=äby‡Jò Ï´«\:’QÎNHÎ8É…¸ÅWµ–\ò°ä˜“÷PÌ<ÁÁú̳§:ßô‹€&¡ޱ2s&è³Ç¹êœ¡¼G%çä]É5lžw8¢rFt«3Ê ØE]c>á¬zçi;:m‡.‚ï(h¢ &yÉÈÁîx¾c¡w¨[‡àÍÆqoKGç,êR)'#/štx§Ò>ä _×SùæªÇ[Yð(äS…¾‚SUM)zUQ_JÛRxTQ_„>Uè°¢_<WºLîßÈ.Ãæ*üWOý|±öÀ£ ÿì¶Þõ”ë¹^Žeø¹ví´k§Mõ´,»*äV G²Kè¿=ø±"WÝêÊ ­=U"¼©/Áî<þ®—¹ÞÕÂúvxÔ£w5mš oGv5vî~4{°³ß4ÑvßíØY/²ø»ú=èQB¿´‹¼sê];2ªÑ³½K§ò‘99µø¡´ã›Z|Ý.rEøÕÿ]ä£k-öÖ#¿Yxˆ|ø7 ²Ú‘ÑŒìæõˆ¯™6ø6sÝOmØà€ß^®ïmP±cþ}Þ;ú¼wôyï?_B×zgôYïè³ÞÿÉÏzGŸñ?ãü9ß!9àå ¶ä[ž`×Ót¥Ê£'y@%—“`­Hþ&¹‡Êp9.1-y=dº–¼yò·œ+1ó¼{è¼L^úl·¯Æ_ Vç åì‡ä5±WzUþ&Éÿ)gÀ%g“äz’3#’—DÎŒ n‹œ/‘|ó’{D0^ÌUGTÎSÉ7?|¦Aç¿ïQùOÍó’ž:ª]ç¬Ú§òLËJ3ªÎ’«rÊ™J3?~¿ÊAmžƒïÐgáûõ;ìÝú,ü€Â§1ÏÍøê³3voÐGç¶jÐçgztn+›Êo%ùM$¡œ—3™fþÔõ.¼™›ÐSãØ$¨÷âÍóñûtîÔ“ÇÆCŸ—ï×9ô=5–M°>3o×gjl*g‹‰iӭα˜yT÷)Ly^rªšçç=u¬“úŒ—>CŸ ÏÑ;t¬±OçÛöTX7fnýP[µCåß–Ü/&ÖM:ocæWõÔy¹T®}óÌMŸ§ïÔgêOhÌ›!}FÔO©—s*&îM§:'jæÙ÷ÑyVm*/–o·Ê…%gnä ¨œ»‘÷ýͼX6}–¾CçÄêÓy±«}DN¬«Så’‘%žä;|9_*¸5’«*fŸÊ]/yM圀¹ðµÒÎʵ(ÙkIyŸ:²ÍKaݘgè;ÆäÏœÉ7#gç异™WÿˆÂ ‘ðO„>?$B“Ívh¶£ú'Á; ú$x'agü’¡K¦>…úêSà“B} õ©Ô§"7?¤¡g~J£}õ6dÙeC– 6x¤cG:ã%º è2è“zèö@—Ÿ øì@ÿLxí¡.ºLxeâË,ê³Ð7=²á‘ÍõltÈÆöê÷ Krr³‡k¹ÐäB“Ÿ<®UÃ3ùù²ÇåZ>2wòÙƒŸ«áYM]=|ê¹VO}üšàÓ„þõüÝ/;ýgÇ~;~·sÍŽ¼bøs½8Wmwvó÷nùšÝ´ßM?—âã2ú· }˸VƵr®•ãÏrøV ·9j{T JxT£ÿT£Oµè‰5ø¤†þ¯áz-íkiPKûZüV‹ÿ갥Ϊ¶Q Ð6À§Úôm€G<šÑ¡ÚlnW õ-Ô·`ƒ¶ø9àç€v/ºìE÷½øeí̵¸ü“ýôh¾áÿó Ëþǵï‘ýŽÞë ïoÎ,{úà/ö0²_q=–} ãÂÜwàÿáçÁ²¿=ÅÈçÂ#÷#÷ ®ýì \û¯ÚÈÚßµî¹æ—µ¾¬ñ]ëzYÏË:žñþÏÏ_§ÿ£ êÖ X}«¬*w§`™ùrUþ É{!9†í*‡žäþ¬<É{'ù2ä¬ïz«ÊÏ.9…$»ä0•\y’#HòïñÕ¸w•ú\nƒ:KiE¾•¶”#:UÎŒøGìS¹2$F<N*¼Á:‰¢}”MåÕ‘|w’ÿ.™ÛñMŸâ3™údèRáE•{j |“‘+ó,ã ÆÃ/™O"ºdQ—ŒéðMDßd™o¯ÉÈM†o!×òˆÕ¡‰:àßÿ®·`‡ûðp`Û^êö"g/í÷Êýmô=èÑçâ£ÏÅG߃}6>úlüûÙø?Ë:üÿâ3rsn–ùQæ8™gd®û}§¾ïöèû[¿¾p/0ãUbNâFƾMûè„ÖuHãzyiŒõ}:×ÓI•“Uò¹šX¯^ïÕªs»6hܯnWå¤Îõê¡qp‚5&{¥ÆëÑø ÃÁGáä˜íÖ«ðÌœQ^Ó¡Rå7sGõéüQ7Öª±c;t.©>×Ūsvê<„}:g¬‡Æ’=¡q<5öC‚Â0±|unò\…!ùãÝÌ›«s•wi,ˆ“·§WáÈš8ï^gÖ¦òšØbÝ BòM™8³ž ^òM Ƙàn™9eCUŽCÉ'+˨VW¶CãCQù`{ÌÌyØ«ð€Ì܇^*ï¹`Dþû¢>dSù¨d™5«CcÀQù¨L 2/•»IðhÍ|T•:'Õ•oVr’›x´ý SÈÌK¬s'6h,ø\•?QrS­èSØk¬ —VrS­ïÐxd6KÛ¥óRyèÜTV ïP˜´&í •Gr,Érq‰¯Â…7s-vêë'tžuO…±ÜWçBµi\ø.{ö„ʵnæ§òÒ9×m:÷l—ÂÎ4óÍzh,²`Gf×´^ ^ð¶yj<ÚN=æ¡1hƒu¾ÙJ…!ùM¼ˆ#3b@çœõÕø³ ¬Gåi7sûxê<³¾/"WcFìÓXd½:w»‡ÎßnÕ9;4^D§ÆŒ8¡ó+úXÑ'}"©¤>’ú(ì‰Æžhì‰ÆîhìÆîhâ †v1´‹¡],:ÄB M,¾‰ã;qè‡â ƒ>}âÑ+_Å£SÙ)Ô¥P—E¿¤RŸŠÜTü†žiÈJƒG46dÙeC–m@m1Ò±#1•]tÈÊÀ† xeÀ'>;оɄ_&t™ÐeÂ/fA“…ÎÙè’ Ÿl®g£K6öçPŸ#ßj*É…&š\hòàŸwBmaòÑ!žùôA>úí”:îDfýQ€Ìê Ôg|vÁgºîBÖ.dì¢oìøÓŽîv|`Gf1úÓ¿Å\ß ŸÝü½[þ†f7mvÿ_—Á§ >e\+ãZ9×Êñk9ò*°¡]*¸^ JxT£Õø³;ªûÕ6ª=k0´†ëµ´¯¥}-ík±³?Ö¡wz7À§Úø4@Û€¾ ýjËÕŒÍжà“xµPßB} ö8h뀟ã„Ú~íE—½è¾·[aˆËºÔü72g›k?=Š£ò¿Gŵ“½›k¿&Ïöùú‹}šìÑFîÍF>çwíÅdÿ%{/×~KöZ®ýcÉÜW¹öT®ý“kï${&Ù+Ü¿rí\ûŸ‘ûž¯Úç¸ö7²—‘=Œkï"ûמŵ_qíU:<þ|2º?ù§ØŸÌéVyP½ˆûéçtþS•#Q°sMðÎßL° æ kÎY…‹ëÝ«°3Wð+M¬Ú…o&xo‚á3oHa æØ¼s CX° $¨äç^A›UÐùçªüÞ‚[+8è’[Vr.:¢pÇ_lE‡Æ²ìPø:Ô6( 03ÿ©ŸÂ¯õǦ¥èàDáÑv«¼¤’+[°1Wt+L1[»¡ P8³AضÆO=¶ì.ÉK*Øë‚+Ø‚)Øk}UoÁ­•\’‚ÿå“ r’š8À½*ߣ` H®G3¨]ãô ïzøm€~~—Âì’Ü£‚+ù½ÛËÄ”¥.Š6Q´]bWùº£ £H°‰¢ ‰‚WtVl±BgEV2´1Ð¥ZùÀ+žr2å˜n…¡M”¬§ðu*íRÑ#¾SågÝŽM‰ØœŠ­Û±k;º¥( ´Dd&ïSØ ‰èš }ªÈA‡TÚçá“BhòYˆœ<>…Ÿ6…Ðæñw!²sàE›Bô®Bvm²ø»^…Сg–|Ó/þ.Bvº” #úêJºÔTVM4…ôK 6Uñ]F_T ßôhÞzôª[äÝ«°izTÑ×m”Ûø®=ŸièЄnM|·QnBf:~jç“A›&dg cüÚ‘³ƒ>iÇÎLdeÒ&StïWSr6veÃ7›ëÙ´ËÆŽês䙹ðÌ…&š\hòà›‡>ù\χ_>~ÊG—|lÜ)dîD^r ¨/ ¾€v» ß…½»à³KÊÈÚ…Œ]ð²ãw;¾²S¶Ó¶˜¶Åè[Œ-»ù{7ï†f7mvã«ÝØ\ŠŸÊ¨/ãZ×ʸVεrø—ÿ*¸^¿JxT£úJäTË:Ž~¨Fv ã¨=k5\¯¥}-ík±¥ö¤z<^‡ýu\o€¶ÁªòR7À§ ðhF‡fh[°±^-ðj¡¾{Zð¡Ýðs@»]ö2>öâ³½ø²ÑS&dõÏkÄß>j.1ÿþ®2ú»Êèï*ÿ|øVÜ{þîßTFOý=å?û{Êu¯ÂXžà©ñ‹CN‚ài.©ÔØ:]*Wÿ’…e<±OãŸ{“ú–±‰…æ¡pL4Æ×ÆÀœP½ILÁçSì ‡ÞÄDëQ˜¿~…·%˜<2ý,ƒÖ‹v^ ­RãÕwiœÚLïS8ÇÓ6š`(Ì¿‡4Ö±—Χï\…u,˜ÞðñîR¸Ç‚s¿ð¤ÆóìQy×gIy@ãÛvðòs ãxŽCá§Étiâ*Àn°Æù„v®]cwhÜ4lœÇýq^¯ÂA6±°kA‚Æý´k\ t[8 °Mì4ü·Hö#~ yñÆ=öÒ Èôµ* µ%ð_‚îKzþ±à§ ÖÂ’s3 ]–íS;˺5†´+<5ö1üVÀo…Uã Ûžš°n5ÅGÂßÒ§pÔüN*ÜcÁ5±Ó¨÷‡¿¯ÂB61½4Ö×Ñß‚^«ûæ±`‰ ÖÂjÚ¯ÒKäÙ5îq‡Æ[@ß äõh<"ô †.Ø®ñ¡‹†Wl§ÂZX7¤–&2rCh‚ !'nšà-„à“ èM9þíÿ^ñQ$ü6A·iHã•Âk3¼6+,äÐ>…},Øi‚¹ ¿Lü¹ÅSá™ ~©à…Á#ìœÆ2åz8c"¼Cc§u«<ùáðÚŽ=™´‰ Ÿ#‘‹-ø&@›  |,Cj dE+ºX¡‹RË¡(/µÊ„.ŠëQÆ 3™1ðŒÁ÷1ŒµÆZ<׋Ñ5ٱȎß!+–¶ñ‡6ñð§]<¼âá~ ø)vÅø2O6ô ´¯äÚvÙáÃíøp;:§`K<“†Ô2L–OÉðN†o2<“Ñ'¾)ÈN.eH-ÓRÑ-Köyèÿ4|g£l£lCíl´KÇé²×Á‡Å´OÇ–ttJGŸÔïÀÖ較˜ª”kÐï€> ²à•…YèQCû,è³ ÍFV²r É&y9ÈË¡>{ré‡"®Õ`o.´yÐæy©¥âNù†çNx–âƒFøÖÉ7zЮ[ Ωåd!í ¡/‚¶Þ¥èV„ü"üWD›"üWB} ü*iÛˆìèJ )¦”úRú¢‘ºRt)ÅîRhJñe9¶–Ë7uåÈ,Gfò*°§†ºVÊ•|ª¸Vŵ*ì©?о?6RW3¤–®{Ða:Ô‰-ØQGû:x×Á»nH-c› i‚¦ MèØ„ŽMèØ†ÿÛmø¿ ûÚð}c¾ }ÛÐ×!kqYwÉ?×~Æ•»ÃõìÞ£¸£¸çã®0ö†s•¸žkËúyä³m×ZÙõ<Ûµ&¹>íëZïÊ:÷¯=Ïvå6ùkϵÏŸýüu©Íã/Ÿk\kŽ|žíZ[ºÖ”®õdÓˆõã%þ ûnµ†[L¬-ä³øˆÂL]ÈXÆ_N¿,cL¯¤nFŸÂEìX3©Aa²ú1Æ–÷+ro«ÂLZاðÂWö*ü'ÁèQا\ ÂO+¹¾º `…a¸°_ãšû*lpÁP]>¤0 ßTp¢Ï\°j»4¤Sa™!sS¯Â€L#Á@Ü# ñÕGŽkX‡Â0œ§p«ÂßvN…³_ŸÂOŠðTXO‚0¤ð›"i‰œHxFx)|òPxDË\X©pm“5_D wt¯Â³‰„g(Ç:þj<ìÌuÐ&P¿nGf4IÐnBn t)ø. Þ)è†ìþN“õþHg,„#'>èM›èœÀµ$ìM‚f;úÄBoÁÖlÊ™ÒþÑØÇý=’6i2×RŸF›\dDÃ+ºl®å‹Î2_ÉÜÝù´£¾€ò.ÚgÒ>_æ`ère®BV16Ãgþ)¦œ$ó v&‰M´/§®œöåØ–ÍµJ¾s¹^€œ®g£Ûèwà“š}êK#¼ñg±Ìgôu#u-ЕâƒR»z Ò Mz´“ðkį­´i…W1´E\o„_+í[i_|NÝf[áÑ JüЊï*±§Z¡m„Ov·¢cºµŠŽ"[¡kDÿ:áIÛFüÑJ¹•ï6ú¡ 9mÈ™ sýàhP{'óßès¸QüÉÿê3¸ÿèùÛ_{öö¿õ¹Û?ÊûÌÿLÏÞþ;ž»ýwÓ¡p¶çÂ.üçÂ.öÍMи¦ÐÍmPxÛ‹ÏjœDtŸ‡}ó°mv-À®èJ](r ÓBôßÔ¯q¸ñÝ"|·ÚEÐ.Æ_OÇ<_äù&hlSt[Ò§p—œT8Ý&΢‡Æé–õl—ÂÝ ·klSè—aç i ¿ð[‘«°¼Ït%ëá³ù둹^ÖïÈ 9©°ÀC†Ô²búoÄßáµQÖ×k ¯ôÁFdn‚×fOk ¯ÍÈߌühä$8Fx(‡©eÉx†!+ì¤Â3 RË”­\ßJßm…G8üÃ;5¦©¬áñAøIµTÉä;ø'9è~¹ø YÐÇŠ>Vô±ÂË m$×¢¸ŵ(dDq=ŠëQ´An ×bðA ~( Mm  /ÇO%ÐBWM²‹Ät*¢}ôEr Ù%ДÀ«ù%Ж@WM 4¥Ô—¢)þ,E~)ã¬ßU"§yåÈ.çïò!µÜ¬ÀgØR‰Þ•CjéYŵ*®5Rn㻆v5ð¨¦[÷ çt؃uø´Ýëà[ß6ÆOtuÐ5A×]tMèÙ„žMèÙ„žmø¾ ¿·á÷6llÃïmè܆Îmèì5´¬]åŸìSF>WsíC\{ ¹aÜC¸ö Àëy™¬ëe=ÿUÏÁ\kð¿çúÿ?Åñ=ïÒ˜ó૞|n$k²óŸ¹Ö_›=þò9‘k5ò¹k}´K¯kF®aÎqäsü5ÿÎÇ–ÅŒ……Œ¯Å2_3Nü  `l/cl,g¬ù1f¨÷£ÍÆÚjꂱ/ Oá`ÑïŒË%´[€­Á|Ó.˜1³ š•øxÓ …é½Úp>~”Ãä¾Ëø´À;€±€.tYÍ'™«Ñ'’6ÑÔGʇr帅‡M<¸žÆõ4Ú¥£sué•j˘Ž=i‡¸ÊFL¹¿B›‰^qÄ]$r à‘>»(çÒ6Ú\hй^ŒŒbù[bœ±\.qþåø ÚJ¾‹%ö‰¹JhŠðO>|êä~Ç}£_4ÒÏ•Èm¤} 6–RWŠ~­Ð6òw#ñÙˆžu\kćÈ,Âw­ðl¥M |åž Vä·Rß ]+r[ѵU®©°i£_ÚÙ†Ì ³ø×áP±.¿IJÞɉý_ýù‘õ=ÉEp:¾£ÌxÛâ}îñÎÁÊßY^3‹¹–i­^)Y «œƒO¼èžÀ“¯¨úó99úèšÏ¿»ì€eÝÄ™—…þ[´³ùûKõæ¬,KT9ÝJÎwïk{ãèo-¯ýxAîŠ8Ÿ™sÍØ¨"çÑeOܾfYšeÝ#¯^6p³Ù÷õÛvîÙlYšwÏ©gÿøSÚ÷©ö—}üõC[otéeyí×qî|"2âK—žÏô·/ÿ캈ך½~™éÀS.~÷ªjK”²ÇÙúî[=×v¯¢¯j÷òþEò6YN­ùxìÉÏò,j¹ƒOmüuKÎÓΣ)xD¼·dX~û–Ú s^ûŽeÁ«ŸÞÚ“«âsù±÷®mƒåð›Ÿ_æì½þÀ;/ot-˜ù§)ãö:Ûï|qË—¯MƒÞ®è'V”L=û²åTvßkÇü«sð1Ëþc·¸ýW}é§g–¾ìl_Þ²ãÆSh§ÆËÑš_òÈx—ß-§kBã뜃\õà½G¼Q°./¢è çѦ‚m¿qøX•žÎ¶KŽßß?Î=.–©qñЙîC+o¿Ãe·åÔ·œ!×Ìžà<þƒëj¦F <ûI÷ãÜ`Y¥ôw¶n¹ã³+ÎZ+ùðQãã¡Ôég^¸ãË©§Òƒž}ò¬sðXÝg~ô ó¨}̽³Yàl I<ûäèU¿?øÎ«å?‰ü½åÔÀ”ù­7B¿çñ·F¼3ý¶K>[¹xü$èñÓÚví >O•»û}¹ê÷ {ù†¿±œ" ößípÇã/¿jíe.ýGÑ~êm¿qõ›³…AnwÂGƒ³sþXµoû°_ûÍ€¬r‡G²&ß÷ãß»ýÛ¶ýç–>élþÙü»Ê?  Ø¿ˆÿæ‚aö¯¹> 6ŽôXS²ÅÒ_|xJlÉ[ŒÃËo»ö¦áx:öÓßý8ï1KpIÊO÷ÀçÎæ³Þ¶ßþünÚ«ñpÄNù–þKú÷=úîK´/ýÑιÊyì_.ùæñ[ÒœÍ÷{µ«ïôªßïïþtBÍw¯¾/ôßñ«o¬ÿ(Ñe‡sðñw¢¾Ùå°ßŽÌ¸uû _ZBÎZ_ìlYùEã7®>?5.î—¨˜–aéïÙþqO<ˆß?ÿiêì©Ãqqì'Œÿ}øóÃ÷«Ö¨F]9`}o…÷Ϭ¶Nû^§ÛÏZ¼gŒ}’ñõÆGá_+r;\ì·å g›ß^^¾½„vªÿÿh‡mÂêŸXúûç½yäÎÝÎÁݹîÒ/œÇªg/Jk³;Ûfš ½êçÃÕYž Ó'ºÇËûf`ºïÛÔ—ÍxÀñö/^»íŇ,Îc‰éWÓ¼Ñ:~ë‹…÷?â?7Õ=®º> 9rp8~¾ý•³ôxʦë[þôQçÖ_~jÜö§¿{ß,§¿ÞÕÿƒƒÎÁÚë¾ú³7†ï÷{èÞ¬»†×½­KøñÚÐ^ƒÛ6õ~úȺrËéëž®úÁÖ½ÎÁʯ·û”e8~?ûÕŸ<øcg‹+.ýT¿ßªúux=qúfÿΛ®žç^:4ÿ꺈_ê~xx0`Üîçl–ð ßþîö à£ÆÃ-¯¼2&)äÌpœ–Y¸6Ä9˜ÿѱ'ËSÜúËô“õoÃq8ÚàL[ýiaj×ðý÷a„5v¶üø™…/ï_L;5®¿ïº×JÚŸ±œ~øÅ·þí æÇ”Ù¬(ßuë½ëÄ俇ž·,;]á§ÆÁ7¾Õ‘X~q‘åôS]>ïäÜêG)Ÿ½°i®;þ¾.÷ÆM7yþåúÊ_õçK¾ïÊ·œ~þá·N_<É9˜¾þÜE'/w>¬ìr¶|÷G• ßÿôª¿¯™¸­áÀ©‰îþcµúÁÉÎÁ¬°g>yãð¸ë1>I¿ðÕŒáuYË3ñ?œLø«qp•GîqlnÓŠƒ¹?8˜ùæeΞf‡»æcÚ©~ÿº¹<-tËXê½ò‹&ú}èâ/.õuö¬=µË7î9÷ºÎ_õs‡¾ÏžŒør®q²sÆ„œ×;{¶<ôahc±{Ýè¯úõ¢$ÙHÜ1¼:ýÎ?ñ.ósÞ^Ÿ¹?Ýñë茲UûÇ:{¬&×|8Â_ªŸ›ŒÝ±rÎ,§?¨hO>~:ûjä´Û‡ÇwOÂ÷ô•WÜë¨â›½YÒÐ^õsÅÓOš~÷€åôG‹—¬i_OÿXn`é=m‡»!éwÏSf¿[=¸Å_k9ýÇK»¾¼sùð}q0î9Y ß7{¸+Ž»á»–-7-ŒxuÕ¯œí¹oÏ- |ŽO0ûßR™ù»~òŠÓrÚµ>Œüáoï]qÒe—¾ß´É…1´3ûßÒüƬ>k93fÓÖõŸ;·Ý¶ý¥ÏÙ³õ@̶¸G­/ìéOš½Ùï–‹×_W{õû>ÃýxFv»ùw9·~oßûÝz‡, ûÚƒ¶áySïƒÜóf€9.,—¿hN˜Ã~=ãµl÷£9ÎÁ°CWœyçÃáõl“ZÐΖ«zÓò¯§µœñ¹ô’qoÝü?5öÄ]éìYßÖòòîîù%ÀìwËõòTcç«Ãñqfåí·qºÜç2lÆ Ç¹÷îyϵ~ 4ǃ¥ëp›ïon˜n9³é¢i]w°ˆÊ<ðltœ³'ø2fœÁá}¤ÖÛ½¯Tý‹¬~6Œµœ‰?î}Ñ[ îûdìó’»8Ü_ÍF)+Ôy´Sý|Û×:vŸuû˼Îp&„ÝñÊ·ºÇ±=ûò÷_yÐ}Ÿ Tý}Çüž¢ãOFºûÏΊ9Ì#â#×z#ÑÿÁ…­Ã÷Ûžk޼–r÷ù뮸ì¾îŸ¸îŸðSãáNY%$Dºõ‘Y+ã§îýABĉ[ªrûµ»k [úþPã`_ó—²¢´œiŠ ýíFüP[ðÙÑÓn=ö_sé/ß®u¶™ËÆ ´Sãà›íW5Ýå7ÍmëyGäù-}w»ýÁ¤h%{øþ¯÷k#ôPãä[;¾øí®n{ö~&3ã#oÏóÜv4Úogês¶Êj±â>÷óžÕj|Ü%»¦e/ý¥^[¿ve×¹î8É:½Ê¯äΖ׭Ž;fþŒöj||{±Ù1n=Z¼Yiô÷†Z&=ììIK®«½ëWÎæk÷ÿF íÔø¸G­×Ür›7ô ®Ã¨š×ú’[=>šÔƒ)w\®Vã¤{â%ùÏ4>æ–ßõÁ¿æ½é޵îsÛqÑ ¥§†>píG†Ÿß÷V«ñÒýôç7­ß|z-ÜñÒuÎÁø\Ÿ{þ€û>%³kœ¯ûùËj5>î3oã›-göübeÍÁÛ¸ßÞâ]=Ïæ–,°_pÏ®uÒj5Nö˨Üð¾åLÍôg_~k‚û¾öEOËýMnùß ~`ÖængËU…û›®^;¢_ÕøØÿ»þøÎô¯lêñ}†ûNº-ó¨¿Ó­ÇËOÌáížÿÖ¨ñpàŽ“7­¦ÜrF=¿pÚ¶^þTæ»ç_ÿYì Ζ­Ÿ¬šYm…^õÿÁõ/äÜõÜý˜q$º£3Ý}ŸHþÚüÇn®u÷ç›?\êïële×øÝûÚá£ÆÃÁ›ÊfŸ™:¼Î?í”á¶?δÛmÿ«‡¾[rýSÃ~¾ÿ¯QãâPXDßÚµûÜ÷Ñ­7ÊZç uöØc~ëìyéDkÚæ6g냟Üô›? ê÷CjçÖƒÉ3üSîñzßÝßÎ÷qûQ–-—vÏßkÔ88Ô* »å¯/¾dþétçàºýÏÌwûC=ríc­ÉûŠÆýýú5ÉÓê¤NË™ ¾·ýúYÎÁÀ÷^ù¸+Ì}¿¹÷leȳÎÖ)Ç#ö$3ï¬QýÈÜÖ‡[΄þvÃk÷•¸ûÃoÓ¯S÷y;{n3fîçwAªÿŸå2×}–yã¥ß—/Ñ}Ÿ\5eñ6×øwÏ;¥ó™q¯‡~½nëÛA»,gôóèaù«>>›6aÇð¼ÓqyìÔ?½@;5gN~îÆ{kÜ~[¶ÿîöoÕ¸ž—8ý?üúÐ÷ÞuÊ¢'#³Ø´âÊžý÷ÓÎFåw©qpø­»ºÞ»§ÞrFžÎ.¹cXîàê÷OxŸòs¯Ç®»Ó‘ãÿ/ÎÆ7XºŸ©qq?£äýŽç,gf› Üë à›Þ{&è›îþ$zÊ8âº_Ñ^‡#A²A¿ÙrfÚO:~ºuCÅÅ[ ¢œ=û}÷Å'^qï÷‚T¿éÿvçP¬û¾6õOÏ?sºËí‡Íw¾{Ç:gϵ17û_ëŽã Õÿ$Ç\ü³ñn?ÊíÄ“ýê–øÄêc ]ãÎuÿ°¾·Võÿ‡Ÿ4mÕ¿ºãÙÛ xwÿoÉ÷Œ|ïÿ°÷%ÐQUY»$©$•yN*ó@F’ynãЊ Š@ "CPDphãØ€J£ØŠ3­ô/ ©¤Ô8žØ8DAM#ÑJœÐ4â„¢-íøûîsn•%¼÷þ÷ú­õ{ÔZµªÎ=çì³÷>Ã>ÓÝŸ«ö¾)^ž¢ì“j‡®qºˆÛCÇ;åcß}ü!íÝH}a þ6wÿßøªÒ›®†85»öC‹¸]t¼9õpËùڻ扩{õºÖ•«Ÿë¡°ßç}EÖÄ £=ëÕ"nÝwžq­æÃ´¡<×% ;!a¿â?¥ÿ2ÖM\ïÛJæ½uo›6Ìã)úAþ;ߦºê«ˆë·3²ˆvþµá£¿£VeÇ„ýÂ>j*bÙ³úÆÒsýv®Û9÷´«—ƒŸª”=à’ç”3¾ÙmÙ(ìmÐhhûsÕÑÛ&W!×kçûÓƒµáŸô1rVÍΩ;ÇóŸ6­‡‹¹>;ÿaûàÌÓ1ß{ùàÓÛ±Îzñ¹²½2öQ—ý ô =×Sç7G§ DkÃ|ž'FætŸáuK¤¸ì£//ï{ã[1×ÇVž_c]C5.FnøzýÖ®/KfˆËžÔëéYÿ[Û²½_5'iÃoÓ5úÍÒá—ìtoGXŽíÚ7)fýoòZwŪmd¡Á·ÏPÇä ×x{Õ Þ¹®[Ù_c¿l ï›"?×kw±×Ù·N[§ oõÖ.#í_äüG•°O¸rhʲ3ÅbÞ¯Gz®×îÞs>½9¥WæñØ5ÎþÁ'¯½ Ð5¾C›­/oKƒË_ØÒ5Ãz¸”ë¹§H_hÃwêbä¶I¦"°Î›¢~ѶèµÂ'íHÏõÛ30°à¶Q“µaž‹‘õÕ3|8â²ë¬±,U/ù¸^í%oôd%ÅkÃ÷?òyžÙtîøûÔ<é¸þì¼ÿbÈcØß?¥­‹ñŠv:›ø Ú§A>®7ûO—ßw©6¬/–¹Úï' {=u°7IJóhÁQ†|\_Ò©ýD·òö$ÿ‡°S÷þª],{`?FÚ äãz²óÁ²6ŒÙؽQbDï „Ý¿â÷ B ŒóÚezsÂ<»”ëËÎýU–çË#÷´?ý—Þ3æe=ú±C©«¼2®'û[Ý­= ùf®É8/"UŒÜ÷ñð”3n=··ùï[¯m}zƒCz®'û?ô2mø¬ì}í©W¸úï¦^¿Ecöˆž?\òYsžXòñø©wX‹|\OÛ²îýüš¥Ï¹ú_Í ^¯ÏºÏ5ÎwœVž½Rô胧Ä"ÚîÊxÌu¾RÆõ¸m^þ[»fG¹ö¿y,F¶S7®—êÒsým{R¤¬ki׆“~ùÔÿô+Åȳ%®HzNèÛS쪤çzÛ®·;m8^/HŒ¼¸9`Væy.=î¾³6ï•«ÄâÇì;î¸ób­Loe\ÛGtËÆŠX×~lŒ¾‘"Fv¯ {bê%¢ç­¥¿oHzA,9{Å,‡ í¸Œëo{aèT“eƒëb€6Ö¹öOe<óYçS®~]Îõ·}¬~°¤ ü|Ëõ-bä­‚93ïÅ|P?~ÏK¾«Ní?Šô\Æø3êÚ}M“]õ·ïcûÜýW¹æo^:BåpýmÏKÝñýâ#Ú~]äL12øeêûŸ‰žýk¿ÜÞ9‚t\?Û³J"Žì:S¢ÑãœÏÄÈ;9÷þù§Ñs—>‘pã§]Ò¥íž mèã.:É#C±È÷=Í×AÓcNÖ‹œO ÑiÍX1òîÆ¦Î—êD;®ý€r®‡m_G¾s¹åGmè•××Ü:öA1ò¡v·Å4,º–Û´~,Ò±Þ·}p‰mÜ®×´¡gn|³àŸ‰çƒÕko p|0yçíµ‹^u°gÏÇÖì÷m´½ùømˆÛ›«ºà¯·³©rÄB2#÷<‚|¬ÿm4íÜõ6D×v¾øÈ5.}úÊYÛ¿=t Þÿhåvƒ|²ÿ|;}ÇÀ57kCwRÝ/F~|2p×–Ù®v™q­÷ÚäÅ|ÒÞiO#Ÿ¬òOï‹[ü®6tssdz?iŒ_NßÙûŠ—\$æc²WôÎL—þ*d½Èqkèê¯4ûã¿3ø»×›ö°hÕSs‘^ÖOÇÌ$ÞÒ†äþ€3*aÝw×办zšhb!»Àk.DzÙOv,ØôÏ€EÚÐ%K¦=Z4A8ãΚ÷ü¦§EOiÕZ{?Æ Ù/ŸùÕO<¯ ÑÏsë…3¾åû˜wË]ç" ?¿¢§õp¥ìßS³8@š:Þ{gØáLxé÷£¿>ËØO’÷²žëc»Ò±^íúrz¥ëž Ÿ# ç­&ÞÛt³°Ñµ¥Ý‘^ê7óëœÜG„sæ—wÞôžK}N~õæ#K]÷©œ”γ aY-õGêÌù^s æH?ãÎæ5X‰— ^êëjHæ~×û|ñH pn})¦yþŸ­‡kd{ã Í!Ï«œ|_ ñ¬Çá´û:k÷ çãñÏY¼‰ç²}m~îéÀs!—>Í­Î',÷ âe»âvî’Ÿí’pö¶ÜûàîiHÇr;~ºâcÓ¢®qó©)ßÝ8ýYijüvyþ=äÝR°þú]ÂùtËY)ËLˆgùíßê ñ|Nã|v}ãc&ÊÏòÛ?×DÂÙ÷ãίœÇju¹…ó•óþîÿÑ „eýóùžæøáÇyÖ=«„óM½c!^ÖûŽ%/üþÍñõ9/lØñ˜p¾<õì{¾@¼¬ïå-{µí˜æ8°“n¼çý–¯Ãú¢–å”óXÍ!ÏÎkw]Õ@üH9ó=û@¦æØø}‘/èRsKoÄK9kõ1Í!÷Ÿ%˜Ö™j/åœ_¿ü‡É›4f×~‚ñîóÓÞžd=\'ûÜ?s¼sZÎ×#~û?fu;â¥ü¾ØÐ¶Ys¨þòù3ÞŸÔx)‡ÞA5Çß™V{ÞSÂùÅæŒ5÷¼ˆx)?ïCi¾ß*œGÊíí|ñR~¹>pЮٙ…óhôkwÎúñRþpJЩ9ä|ÀùÝ“¯Üˆv\ÇòËyæx«ø®¤â„SßžB;¨cù{Ø0A?ÝèÌêLoU·ñ\ëáz–¿ç¢•×'&oÐýkOýìçûÅ:õ>Õñ,Wï|gíq ::å¾oaßëYînšý¦VjŽ—i¡uº88&ç«'î@<Ëm{éíê[¦Ÿ¡9^Š¿*sÍ™â@zô™Ï§ ^ëYn[NÝ@ÓÏ,yûʶ«Ä¬&Ç¡‹½Ïrwm¢†±Nst_;uÑ7©â@ή ­9»žåî }þÃÍÚAÍñçÇi¦%ä~dº³ñ,·Ú?qÜE|ß³n`y·Æ“…Ó»¿~¥tz<ž³œ«ž±õm Òtvù%ÚàÖä³H…Ýh`y;y¡9xýª >:ioûÂoÏòªý©Á{ž¹Ü\1ÏYÎŽÙ %O=Ѧ9®¹éñ÷^Ö´AÌ Ø}-âYÎŽÓuF5]w÷¢6¸ê£Å‹_ØŒx–³c’~0ª9–jçþ˜}¾6Èçˆg9Õþ¢cñýÎ×—ß© êÇ,)ÖÃ,ïàùN15Â>6²œ[xüÕ¼^Ó'×~òÞYˆg9úu.ȯ__ Ås–Ï1‹.¢LE˜åÚò·°sºîûYsð¹‹68ã|˧k&!^ʵæÃˆË¾ B˜åpPó=x‹6ØR“õÌÕ‹ðœùwœMÈmpö½+ƒ:1þiÌw‡>=Ót[ya,äZYqÿ.+âYŽŽ÷ô É(ß2yíýÚà§¿Y’{¨±´êËiP|kƒçì½`ë©Mˆÿu}9èúì#ïjƒÍ9Oüǂǿzþ›µÁ†óWͽë@mÓ¯ë¹4aìuKÏÄs)Ÿiƒ9×LØýlžËv8÷òEf/ÒSNtšQVY/aÎUw< }ZuyðË|z·]ùšUêï;#,ù’õ¶ïŸ±£ŽNðÇsæË±êÀÃù{µ}Ÿê|xÎ|ísÜu}¾óZ«Ô÷ƒúÅQm_ß7笈F;mb~ö=¶øža·©}@ŒMÌ義;›ž¹­L­ðœùÜ÷»ŒþëË–üYšÒ÷ããïg=Œ“üÔž×ëì½aÉGéËYŸ<ô'cŸª‡šyòsâb¹·ÑõŽéGÅl,Ö¾,lÓ‚7YÌ¢YÉÔ{„íý%¦¾ééâ"Z%˜úD·ÿ¬¥{SŠ™rŸZÍ[ä}™n?½‚ÄÞS÷jÔ¾›Ú·ó «õ·’[éÓ¸gªî{Êõ—ÚÏRëc^à™~(¤xa~œ¯æÐÅMÕ­úF³±ßÑ19½ï”\­£ïÓÏ¿ÿ³ÖÑù¨Í²w–Ö!×{9¹s¾ÝøÖIj½ú}­S?–[nÄ«s/u_î÷¨{KƯԿÚQ÷f<Ó Û—¯<º1-YÌåûf¶gîWMi3\õE»ûÓ׊¹´ú»t½°Ñµâ´/ÅÞw¶ .¼;w÷ßÄì˾ ª¹Ÿñ½«ýKµ?±MÞGëØu«ikË¥êÞšÚo1Î9ùµš³…­Y¿h%lÖÌ =ëD‹¼eÓ_[8OÌúÚ¹ní²„f¦»ÅEyúÀ%.hÙ7ulÀ/Æ|zzDóOh²Âö;"dý°aÍãifc?'ÚC.9ÿ†œ2!§jWÛnÖ/†kC¾ßú¢ë ß¼ò®žï4Ç+ë^¸á¨6ûÇu×¾÷­ã‹Ý?Ä<¾\ëôßqî–ñ×k3+´k>¿CëÊ¿‹nûjýâ“¡—"9Oµ¯£‰Jº¯gÜû[¦_3œm´uîѳOü©ó˜X*ï»õpUt…­„~•b¶²:¿3êGé]õWu/p±lgF»–vZÝëš¾üà…‡ƒö©ÞS™B»'÷Åÿäb1™×ÏÂÆíIœÿݱ׮¿WØô×?N¬}ém`@Ìç¾3^ñcÝÙÆ¹¨j¿JJïŽ}tAöO½)ýé¢ßxŸÃ~AZäãê|Ä_µ?;5ÓënW÷|Œv«îëý´iyãÝ /‰>tµKÞW3äVãU½î²§WÌ|]ÁFtí¡ÏëâzdærÑ¥oWŠé|¯Oؼ×&ÿ°a­°%цßëžz1ÂöC%¼oŸ¡¯}íb+]¯²~¤9®¼ïÒIö¯1¯Ú½çëÛ®ÒÚ·ó6äTõoËÏÿiçž,q!Ÿ#ª÷JÄtYÿ¶ sö?|¨ÕÅ'Ÿ»‰ú¶vòoÆ!UOêý£A^§‹Ë¸åË}?õ>™¡ï Íú²èÚ¯‹y¿¦K¾w#í±Ñ¯gËzîÒ/º³ä8ÔuÇßΪ{½ÒEfƒnÓž›ÚÑÅ÷ßÅù‹‡ßø"¬á7íKõC5Èþþ›þÝÒ×~¹óØoÊãG0O™¨hrÊû êþ¶a?eûólw¢ëL½Ã¹ú×ÌQÏgSø^躋^¸xÔ%‡\Ï”û]|ÏTÌ‘çú)JzŸ˜K†ýFa‹¦}nsþzÍLzSroqê/ðhÌÖ5šCòïhÓv­Ã6n׫_ìÑòœ_ÙG5Þ¨ñGÞïP÷ZqHõ»ÅÏêçòFSó ¥yž¨î‡ó>[Y@Sú"1—σÅ<¾_ ºä9ü<¾_gŒKsù^¡·Ù|%ºþrÕÊE­®ö%õ(ãñ×Óîªváio=ìƒï1¾vØsâñ‹õLJ#cü.ÖÍ]=ﮬSh{/FÝ«0~Õ{Ÿÿ†ÿ,ÑÅýRØhð‚Âv.mÜa\iÕêâb¾7&lsª‡ëÓ-bžlg6Ú%n º^ürõS+…M7«…¢Už§wmÓ¶X¨¿»À£cmPï1zÎcñ¹C¿veÌ+7ÿaÃû÷üCsÐmæå—ï+hy/Ïñà+Y»>8„vF/dfÏÕ½’¥ú6ðÆx.ç¯F{i”ú'ÇÕ5É·j¯Mé/úŠ9òý‹.}›7Zíƒrœ¨ž:ÞüîŠÏj[¡í ïÎ3Ú¯:'_(Çû…ÒþIýí¹{’~!ÂàOåWý'Ãc> Ú™Ç¼ö7ëÕþ<懞ù=íôNÒ]üËö¬ô«ÚºoЭo ÇýÜè§lWŒðùÞšé×ómÏþ`ô7Ï~gúuø7ë.õüë€ß̯<é|ók;k¬ïT>¥5Ï•ãŠî–çÒÕ²ýyÊ}yÕ¹ºí”¨)§^P*lt]ûï¿㊇ý÷¬_ãWÙS5nªö-ß3êSΣ{Ͷôf#¿ÏU»Sõ®Ö{êÞžM–Î6ž{ŽÏÕúR®— > >x}§ôiÌ×T=©õÞÿLJ^uMŽs†<¿žovXÙõ«è¨úTõ<ÏiŒ~ªúƒ²còýŒWK>mËöùÍü^éSÙI5)=Ì‘÷«•½TôŠ~=nb?wô9éÓò$¶ÌIl™ÿ¿|\Ò§•õ«û¹l–>… ¯òx£_y#ìÝ/1º­“òš0&˜&1¦ž úóÅà‹°ïjÆÙö3K|mä÷ƒüþ¹Keùƒ–¹Xâg;¤ïÉ}HbfO“~IÌlÄÙÙßa0ÂÁ­#ûˆôuˆpÂ!Ðe(¡-[¼‡·0ð¶š1²u?‡-ŒHÙŸ"š¥Cc F"}ä&ökHø!Qmû˜ÄÁFŸŠ†b gL3cˆè>  =Å®g l¿Ö}®gLBK8cïYP–e¿ÄAYñˆG| -Ò¡ƒ‡ËD”—ù‘'±Ÿq¯É¯¶Ž²šqÀ“Àg²Eb`¯g<ÝŸá$Æ.Lé“xØ •ÚÊ8Øä§dدa›ÄÅF™éጅMþ¹u<‘cŒQ¨ccƒ‡ ÐÎ8ÆØ~„Ñ¢c‹ /ùPÉ™x–‰¼ä‹ƒ|~ëØ"ÍŒŸ˜…tY+;Mâd·³ŸIòíMÃ=½›žƒô9mŒ®ãg#}.ÊËÄ81ôÞ®ŽArHúBD\^3ã1ꘋ›71|P™_›ÞYƒücÚg†|˜“_rò®c•@ÆüI§ñùˆÏG|>â Â%÷$‰Á½šý‘ëX&‡Ø¯"½CC‹…Ð]!x,BÐ(BÐÎøÜôn„áƒqµô¿ˆtcûÓ±´ŠÀoä¡ûîE+%–7Ê+B}ÐsÓú.:Æ~»è¾·î»q’6 hÓ=j…I„óhôý“6ÿ¤Íÿ¯Øü“öþßÛÞSŸncý龫'IÆ< x£Ïx#ì=ÀC‚ú€êÚò˜ÐßMhÓ&Ô­ úñEÿöEØñ¾Ð§âý@Ïùý Ÿ?ú§?Ú°¿‹5ƒ¶iÍû%¶âY‘6°Eú8F8}?¨Ub‹A–`„ƒÁwp/û>&<±„C®Bm•~Á{x oa(/l¿ôiŒøpðŽôiøN’bû38é#·HÜ0”…þÕÇÃadD·ã =Ä@ΘIÒwñ€Ä†žb72¶nœEú-F8a ¤· ,Ë!é·xãÇ#>á„V‰^¡¬D”—ù‘'q€ý9ê˜a ›™’ÀWøL†ÉÓëƒð6t¬0„SNA¥‚V*h¥¶1&anèþŒ­øB¾4è? e¦[³—07ȧ1 ÉÈ—|ž G„|Gê~‘o4d!e™x–‰|™ýì‹P÷k<‰ñ³.ËÁCz6žg#möjÆ!?8ºc<ÏAúðC>YrxèÏEy¹ÓØo%a†ÿcg,œñÅ“™p• ³¸¬Yâ'#~ â«Èæ#Íä³’ý^N a†?J“ òå#>%ã-ç#>ñùˆ/@|â _ÐÆ8'ä¿RÇ,CÕdëQ½ëJï§Ò»¦… QØË_… S:ÕÈW³’ñQÓ¬|ÖN’˜fŒáL˜ÍäO¹¨˜MYQ«ô­Îþ•uœ°-ŒeF¾–‹6²¿årЩ¥´à³ ÿ˧±¯fºÇÞ€_º7TN6„ì}¨ƒ›F¹ìº»-'N óñl¶»½¦Æ@ö™l²²Åîv—l.ÙZèF·¯d[Ýmê‰ìéñl§Â~p·‘Êö)[§ìœ§m#›¦lÙ1µ^%Û县@ö‰lÙ!eƒÈö(›ãnoÈÖ(û¢l Ù²d/”­ð´dÈ.([à>öÓ¸¯Æ{ëil§q]åá\6ùÐ÷†~½ilF{ðÙªE[1¡<ß|ÑýçºþhþЗ?ò™‘ÇŒôf<À³@ci\E{ †CPv(Ò‡âY(Úp—›ðÅóÑ"A?úˆBž(è9 eE›+ˆü»ë˜ìh±È qȇ_Ë4ÆEŒÇóx„ËÌ.Wb† >a x± , â-G$N/âãŽG| m+¼$‚^"äI„<‰H“èà¡SÇA¾$è7 m3 |&£¬äVö³N¸½:N/ÊJAÙ)à=´RA+u%ûf'¬;»ò¤AŸi7 e¦ç2¦2aÝvoÒd _òe€6ù¤mfìåÑ-Œ+Bؽ£!O&ò’¯ÞÌŒïGC¸Žã dA‡YHG¾J³‘?ÏÉ_gözÆê#¼cÏsž|+æ ¼‡ÄÒE|®™1 ç¤ÂÁX):îxÉCž<ðzyˆÏCùyÐK9ÍO?éÈßùð"¿[äC‹üb‘9Ñ1!c>âɧùc"ßJä‰L ù#"ßB4§µ²zòOX&:Ž hBöBè²uPˆüÕ4' ò™Bfа ˜°ÿt\ᕌ%LïÆÖÉXÐKsoÐ*¿Eèou|EÔB¶:ÄáKï@Aô. ™=··—±{ɧ=á‹n aŒÔ#}mc‹èï¸Ï¿É¾+;®æÝîsnjhî¶™*SÙbe‡I0b‚l®²©d?Év*»Iö‘좲‡d OdÝíÙ>e÷”=#¦ì×ñl—§ÝrŸk+›¤ìÑñì²?d{Èθϵ=mв'ʆ½PvâxöAÙO Æ{÷y·ÚcQã»ÛÕü'Y/ýŒáäú ¯˜P7&Ô³/êÝo¾hOþÐmÉŒxó1žÂéxNh3AhgAˆ¶â Z!ø†ta4Çn•xJmŒÏIXI1¨£X´ÏXú¿qH‡_ ʵ ½ÅΘä„}TJ¿‡$ö8ÊJD[HF[IF\ Ò¦P¿F~òÉ@Ø ©ûÛ¨²17Óh®],1{P^h—à;t3ñœ|q—YTÇ‚Žë¨ŸP¿iaìlÂô©€\ôþkâ+Ž0Žv]+O óhn¸FÐmD™H_ ~k@§é¶ã ½×DïvÔÑšKC.+þÓû uvžB^¶~4ú䵂+Í·AÇz„ñ´é] zï‚p(ßçä™÷¨“óïöQ'çßÿNóïÕ8õ«væ_ÇðA;óB]xoôio´ko„½öAا_èÚå™ÐçLHo¢qüù‚–/Ú¡/Òû"½_¾Hï·…‡ô!´#èÒ Zfô ³‡•Ä€ÔC Æš@+¾öñp„pò!o0⃑>á`ð‚pâCéç!)´Yâû~(x oaà5 á°cÓ}"ÜÎÃVD1ãEPñ‘G">²Ÿ‡³(¤BB=F[$–äŒíÈÓ*±û w,±Ç‚V,ôW,1ûŽCØ‚°aK/‰:nÊŠG|<âNh—X}'¢¼D”—ˆg‰H“¸Ÿ1Š’. t’ÀGÒ§Éx– ZÉH—|Hb÷‘½o)È— Z©ÐMj;c"6`øMý4è/ e¦AþôjÆI$<#ÂóË@¾ ä#<œ ð|£ÃG›pªGodÒsÀSøÌ\¤ÏÅsò•MxMäsšüF“Yȃ 䃙ü)çï<Ð#_Åäo˜LùûCspÈ0åYÍxO„éD¸LùfÆ ÌG|>âóŸø|Äç#¾ñˆ/@<ùæ,hg\(òkI>)ÉÌ9*„¾ ¡ËBè¼4 A£4 Ac,hŒµ0–ayE:ò¿GXƒÕô´È¿™3ò+G¾áŠÐGŠ@«´È[9Ê/‚>È@èA_ÅfÆ£*FÙä¿ÌÀ*lgœÂbЯAþbÔ[1êR÷õEc6}”½w·ñîö캲éÊ–S£uß '»­ìµ²ÓÊF+ûìn›É.ŸÈƒÿßì‘{ÚÝÙ\÷ù½²µÇ³³Ê¶*{ªl)ÙÑÿ‘ÍTøld=í¡û~º'¦1Ù?O›w<;§lœû¼ŸìÙ.e›”M"{äi‡ÈyÚ²;'²7dkÜ팚û+»â>ï'{¢l Ùb¦í•†&„¶ç {ƒü÷AÛóÏ&´[hø"­/Ú¿Ú†äò‡ŽüÁ«u€6Y÷5á õŒçŒú ^/1\ÑBð?ñ¡ ša ÖËM4ñὌù‰þ‰zŠø«ˆ‹B‹‚ÌÑÓo54bÀO hÄÐ8ÿ±ø‹±/ùã@§<“¿¸R³û$Ö*âÐFK¶r’?¸’•Œ¥šŒÿɽŒ¡š‚g%(³<§€N*­!ÀGÚzÆÿL‡îÊ@+Ï »«‚èõ2*ùìÉlclPò÷FX¨Y E>»+´|”3fh9ôM>x*· |W0]Å!7,QüVàK¾?ôi0dªÅŸº•ÜåŽ1ŽjʨAY¦5 èf*½ã\ú5à‰ü#ÔƒN]?cŠÒ{úôî¸zd©ÐÐ cCãŠ~*½§Ü„¸bšÒ:ƒ>Õnÿ‘NŸŸŒõëuˆçäÿ•õ‡çÚãßiÝqrÍqrÍñ²æh–u»_âƒBÏ^(Ë; ß|ñßÿ}о| #Ð1¡ÿ˜ÐžLй a_„}öE^_ðá‡þ燶êûcœðG¿ðGýûCfô}3â̽[q(?ú D\`³ÄG8ñA!oò#Œ>Œp0Â!H‚ü!‡ }(âC]-ñÃÁ[x £µ…Y↷ã 9"ð<¢1Ãi˜Š´â»šñ£@' i£Ð&£ tšÄ ‡¼Ñ ùbVJlpÄÇ"‹pl/oqV‰ Na¤· ®œÖhCñ øxÄÇ÷ò˜PvâÓo:r&âY9¾IÈ“Ô̘àIÐqxL†<É “ŒødÈ“|Œñ©Sè >SP© • :©«k5õˆÄ dz4è,­Ÿ‡ÙtÐIÝò^Æ Ï@¾ ¤É@¾ ¤É@¾Ñài4x 9q4ô Ã/|eÇr¤-FZÂ{ËBº,¤Ë³,<ËF~ÂñÊÆs»"ñFäk@™9ø’O’JÐ/C|tš‹ô„ç“ÛÊØ­e+yH/A9%à¯:'ÿ³%¨§RŒ=ä´r“¯ÏRðF¾;ÉO%ù.)[Ï8®Z?cf—‘}Æÿrè±e•“M†,d¿s3¶òW¢ìJ”[ =TB_Vèˆ|Žÿ´*ðIþѪ@›üŸU¡lòkVºÕÈ_Mvô¬HW ZÕ U ¾È7—;^x d©,5ÈW~jÀO ø©?µÈ_ ~jñ¬q½–8ò¦z¤«Ç³z¤%FõxNþ‹ç¶¡™ñÝõq–>ÇÛSô\g(ûí¹Îp·ÙÇ»wC6Úó<À}ÍA6ùxkµÞÏÆ²»do•UöUÙÖÝÇñ´¥Ç³¡d?·g÷_µ'²›îg d+•$Ûx¼5ÈñÎÔ=²udç”mSvÍÝž‘-ëuü5ɉÎ<íÓñlÒñlÑñl²7dz34ZÏù¼Pÿ^è ^Å}Á¿>h+>dGP÷¦vn޾øï ~èC~HïÙüÑý÷£©"lF8m1tÐ_ñˆß ”´ _¤ FÝã0þ‡ }„ਅ›vè‡F8h†CGhÿèßx‰ôQH_ ZÑÐo)þÇ€òs þJÁ+ù..qÍÜ%,ˆ·àY9 UŽÿ øMPùm¬¤14« 3ùÇ,G\5þ×Ð<ŸÆ/ðP Uó48|–#.ôÊÀùÄ«E©à³ùÊ1¥µ0¦u~k®Ezò)W}×C‡ à£zn@ò%W yÉ×ùÝl¤µäÐð[FkZ ¬¤×Àg9­] ƒ4¬´Í*ÈL¾˜¬à±ñUgÕ4~ÿ(£‰Ò_¨“:âò7!}d¨Ã¸×}‘_%òµ¤clƒòfEùVð×€² _C5ão7áxj"[šä{© å6ÑZˆž£\ëãmëþ•¨ÒçßñüääÙÉÿþ:æ_¹†ù_]¿ÐؽiÔo×-ÿ·Ö,ÿÝë•ÿîµÊ4YÈï…~à…¶ày!ì>å¶ã°7Â>èG>h>ýÜýMh÷&Ô¿ ñ¾èÿ¾û"Þ×Ο_ÐóCzôo”åºõ‡,æ4|gîgŒûÄ€è!á@ô™@„BÞ Ä¡îƒ7á`üÆÿ¤ iÁáè7áP¤ E» E}†¯0ð¾ÂPV8ò†#>|‡£¾"0.E€Ï¤@|$ÒG"}$ÂQH[ŠÿQ úQÐA4tþÝÇÃW òÆ€v … Æ‚v,Âq 9âŽCØ‚°é-[Pv<èǃ~<ÂñˆO/ à-r' >u–ˆòQ~"øIDšDÈœ„|I ›„r“ ë$è(y“A+™æÊÐq ÊJA8´SP?) • Z© E>ÊSNC}¦A–4„ÓP^:ò¤#O:覃n:ÊÊ@ž äÉ@ù䫼žÖ-Ðq%ä¬EšÑÐy&ÊÎD¾LäËijLäË"›9@»25@ÙH“:Ϧÿ(O£u ô‘r@Ÿ°Vs@Ÿ0Js‘6ñ¹ Q ^KÀ[ h–€Ÿ”]‚øšW ¾ôJinÙKÁiÊȆ#® qeà‰üÝW!}Ê%_êåGØT€wòu^]ïrò‹]‰rÉïu%x"Ö• QIkðYþ«@» ´«ð¼ åV÷³É ŸÆÕdGi]:Õ´N?ÕÐG5d¨ 5¡†æ(¡¼_Ýš#lzjÁ ùÏ­¥¹=Cž:ä©£¹òÔ!Oä&ߢM4ïoõx^çõ´.!=£üz²d—èã¹×è¾6q_{xÚY²±îëµ~ [w¢5„ç„çüÝÝVxž'xÎãÉxŽã4~»ÏÉÝÇd5Ÿh~Mã%“4þý$y„þh\ô‚|^hÞ¨[oüúÐ<uã]ø£ÌбíÁŒgø€|aøŽøpÔo­A#m" ºB[‰FÝGƒ·È\ŠgÈiAÚJêã´½ZßFy—o¿Ç9b«ë QgvÀíì0Íí]ÙVyn¸Qž:xEßÉ•ãJ+¿›@÷¦èÌ‚î=ѽBýb¶ T§úºeï£ècM/¿Ë@ï0Ð{ t/R¿#e—w¤ñz†Ö´/Cs}}Ïc½Üëàw™è}#}ÿbŸIÐÙ&­U¨^i¼ vCc/­ô1"\î]L’û-|†¡¿KÔÇwiíBï4Ð*º«¨¯UVs½Ò½,ÚÇÑöó})Z›Ðžµj;T¿úa·ZÐT˜ês<ø~ƃÆx”3²Ÿ‚rNIã)òð6é&€¿  7´&€ÖDð0|N„î&‚‰ =4&‚Æ©‡x˜ifðÑŒ|ÍÈ׌´§AG§â!ètÄŸyÿ“½3«*ûn[J(ÐVlK@–°6ìe5PèY'ûdŸì“}²Oö‰²D-p!jD„ˆ¼mX A['€b—4Eˆˆ(bD(‚ñáý>÷œ;¹Šþ?ÿ¿¯ÿôóIçÎ=çÙÎ9ÏÜóœ{ÎïI£<òtø¦Ã'>éðIŸUSñ äg¢w&÷2Ñ/ ²Ð' º,ôÏFX6ôÙè‘ÏeÓÙ3zÚm2rÐ5ù9ôgüsà•K½\êåaoºä¡k~—G½üY5Å/à~ôÐÀ¿þ…ØPˆEð.¢¬ˆ²"t/Bwü=èê¾¾ÅôW1Š£_ å%È,¶ÚRhK¹_Jû—1ʦUØPM94åèQM}Z‰•ØY‰½•ȨDF%2ªàSŸ*øTÁ§ >UȨBF |j ©¦šhj ©¥ýê ©ƒ¦š:Ú£ÙuÈ©GN66À£>øû¨ë£®û›¨ß„Œ&Ú iV…+ÍÐ4S§•{­Ð´Á¿þiÆ6d·A㇧ž~dtÀ³½:àÙÏnÚ®;»¡ï†¾½{ íÁþ~êöÓ¾ýèoȼ[þýoŒc—ö.½“[z'÷Ïǹ²ø{Ãþoàÿþoàÿþoì¯ÏÉãÿþoàÿþoàÿþo¬Ñçxðÿ7ðÿ7ðÿ7Õ{ ñÿ7ðÿ7ðÿ7ŽVûI üßÀÿ üßÀÿ üßÀÿú}!þoàÿþoàÿþoàÿÆzÍÿ7ðÿ7ðÿ7ðã£:†Çÿ üßÀÿ üßÀÿ üß0õ\ÿ7ðÿ7ðÿ7ð#Ecáÿþoàÿþoàÿþodk üßÀÿ üßÀÿ üßÀÿ"uVÕÀÿ üßÀÿ üßÀÿ üߨÔg ðÿ7ðÿ7ðÿ7ðk&þoàÿþoàÿþoàÿF»^ïÆÿ üßÀÿ üßÀÿ üßÀÿ­õ üßÀÿ üßÀÿ üßÀÿ üßÂ;Âÿ üßÀÿ üßÀÿ üßÀÿ-|üßÀÿ üßÀÿ üßÀÿ üß:¿…ÿø¿ÿø¿ÿø¿ÿËYÿ7ðÿ7ðÿ7ðÿ·öžâÿþoàÿþoàÿþoàÿÖ»büßÀÿ üßÀÿ üßÀÿ ü_Öú üßÀÿ üßÀÿ üßÀÿ üßZ»Áÿ üßÀÿ üßÀÿ üßÀÿ%Î1ðÿ7ðÿ7ðÿ7ð ÿ7ðÿ7ðÿ7ðÁ0ð{yŽøµOø´ì¥5ðiŸ6ðiC|Zž£þˆ…³o÷àQŽ}·óú…_ï»Õgß|z]i­ÆÒçÞÖê=·£Ž=· wÂZWŠÕ8Sãzï•Kï»Óç2\ú]ø´ã]x@ï»Öï9Fô¹·ÇÚÒ¼Þs;væmTïÏŠ ×rë5¥Y}Þͯñ&fÔº’'Žëón^uö#„/åÕçÝfõ¾-¯>_=©ñ¥\Žwá³úÌ›GcLMêu%÷ÂûpkÏmŒ>'2ªÏ½Å:0'fkJÃz=)ZcM k¬‰H-5¤°&¬õ¤Mj=i¬‰X½¦4¢±&Öêwá~½×vBãKÅj¼‰Q½×6JcKmUûl-¼‰}ÖmPáM„öÙú5®Ô´kÂíÀš˜Õ{l]gbÄñþÛ¥ñ¤FÆ„…/áÒ8R[†”à+ØØ®„; CjZãJDÿƒgÚ¼j?­LÅäL›¼{\ Sb“c?í Â”°ð$æ4~TŒ>ÏæÓ{i‡õ>ÚÙ}q£ä¼¹ì£uOªs/×ËÙÙû&ëM¡wÙ1ê“…5®öÊZëkQúý¿[ïýQï½ežuög^áSþ„¼Ë“÷uò~MÖÞdO€ì56ë<ÿ¼Ú·—£Îf;έUkòLÖã¬õ´!µŽ&gûål¾œë³ÖÇ<ê\Ê„z—/ï¸ä]¾õNnL­iXïï£ôÚ˜G¯ƒùÔÞ=Áѳzr.Ïš¯Uçƒd EÞÓÉY¤Äi…`íŒPë‰3jý$qV½ K÷ê÷jj$%¿H"v& ú$À#ºî%"'Ûkáéå~u“Ð/ žIÈM‚g2:$£g2m—L¿'Ã?™ºÉðH¿½\ô¯ ¥º°¯:uS¹ŸÆ}<Ò‘†½iðM£<]t…O:|ÒáQF»gP7½3á›ËýLôË‚_údA—…þÙè’ }6zd3v²Ñ;;ÝÐæ {2rÐ5ù9ðÌOŽð›UÓô<ôðÂ7]óh«vd·ËwêPV}ôð/€!6R^ï2´WuŠÐÝúzàQÌýbú«=‹Ñ¯„òt+¶ÚRhJ¹_Jû—1ÊЩ~åДCSŽüòvT¢G%vVbo%2*‘Q‰Œ*øTÁ§ >Uð©‚O2ªQŸhj ©¦šhji¿:hê ©ƒ¦Žö¨›Q!L=r ÷CÓVìíF'r|ÐtóÙŠŒnÚ¿ºn¹‡Œ|ÈèFïåxtÐ^xtó׃.=ÚôãŸöì§~hO©Ä±öº®ÄªòP S1ªÄ¥“ÚçNÃß™~ØRg1ì;ŽtÆ+bGhïºÄ‚Jì'ƒÉÞ§.1žÛÙq]x<çŒãìý!´Ùû¾o´ã²ð=ê{IÜeï ‘˜ÊOIüDY±Sø9TúÊŠ…ìýèvìãÜ÷ac¿HŒcïC·ãg #ý)q 1Ë¢±Š3N‘Eâ‰EìÄoDG¨ÃŽ+$¦pÆïGü½˜AâgŒ s™÷Ûs}{žoÏñíù½Ìéí¹<¾cÍãeþË<ß1kîýï6ß^l®íœg;çØöüÚž[3^¬yµÌ©ÃçÓÄÐ1´Aül;Œƒ±cÍ­e^½Wù³…4¢ßÃÎéóã›Í«Î¥Y¸>½OtLaÉžP9s&ç­s1 KUðS­³dn¥6ë8G6¦÷wÆ)5Á¼TëÜØ¬#uBŸ^ÀK³p€¶ê³`^õ~ÓÂ]˜S Žé¬Â²0K=ú\”Ú+(¸ ÖY€MêL…WÐ8eƒ°IáÅÏð7­öGʃxy6Ï«Ÿ"Ù(g«äÝ[|¢ÕÞ@9¯/XZ©>õ³'çþeϤì¥IE×”A…á“­ð|ä]HŠ<ãÑ%cRí·I@g7¶§bw:õ’Ñ'z™|fJ}ìÌGN*Ÿ™´o&´…ðÌä^&6drνlê—P?Ÿ{eü娥ºldTˆ tr£.õ+Ð-=‘[Ü ®K¨×ˆíÈhÄžFÚ´…{%rY-ÜoAN e-”U`g#|*àÑ@Û·À£Þ-´S º¶H9^ìl§~;:TÓW-ò~}Ðö¡c#mÖBÛw¡úwÅêŸnøµÃ«þíðëB~Ÿ|§{]ØØ}÷k±­‰1V-ÏZ>­Ènâ¯]üÔïž&ln‚ŸÚV®Ы‹6 =÷[aÐ…~hšàž´™_žÅðêB×Vøtag“èLàã—ºB/¼¸î’ç32ýØÔ uèÔMÛ°;€à€Þ~Ú¬™ÝÈïF~´ýØÛ? ý\þ-­O/­O/­O/­O/­O/­Oÿ+ëÓÿ¿¬Kã÷Ö\¿·æÓø}hNß[ójüÞš[Ëڵ̯eýZæØï·†-ÏÊ€úͱüÆ«íœÓg¹üH}–k«Ân³ö¹4vò„Þ—ïÖç¹Æõ9b·Þ£?£÷Fº5vÄ´Æöô)ìd 7b“Ε0¦Ï{4nÛœãŒñ¸Þ»ïÒøŸ³3Ù«1“çô™ã€ÂÝ3yRŸåòésÇ3³Í§ñ"æôY.Ÿ³-Jï‹Tû"­ó\1ßhXí“:,Zã&ª½‘Ö¹äX1¦÷FÆiܶQ½/2Zc&h¼ä˜0¼¶¨0¼¶—ìÓxm3¯-NçGÕxmÑŽ}‘#/"Rc¶ h¼ˆ9µ/ÒÂLTçœ-¥X1¤ñÚÖ:r#Œ†aµyXms:/‚[ã´9öFº5Nò¨Æ‡ˆÒøÈ~Í6²€Ífa²Å.‚<ó1!|:°Ƅ˜W˜lg{ù†›œ}6§öëÅS7ýÐ'ÛK +A‡ú°štùDN´%Ô+£Kh+× èÜ ŸÊò¡Í¡Ÿ’ùL¡­Shß]ðwÁ³‚{.èR©Ÿß tM£< Yéð÷RžŸtèµ|÷R'¼Ð4Â/ógÔT? ÙY”eAŸ….ÙÈȆO6úeÓWÙÐÔBïFF:äÐ9ÈÏ¡sDWÊ*¤/h‹\>ó¨—Ç8ÏÃÞBú%Ÿ{´OºR¿úô.€ü gT8Qï"ÊŠ +‚íëA†¾t.¦¿Šé¯î—L«£šRhJÑ·”v/C^ú”£C9u˱©^åÐT¢C%:Tb_%vV»Þ•ð®‚O|ªàSŸªi¶´R¯FbTxÔÈ545ÐÔ@SK»ÕASM4õ𯇿™MÔo@fô s|ð÷QׇÝ>ú¦ Û›°½‰ò&Ú§Úfê4Ón­Ük…ö¶aCòÛÑžþIu ¿žðì ¬™Ýè×=­Â¤h{Ð¥½ûáÝO½~™{H<,ÿìØXââpLg‘v´bàÅâ^;ÖEwŸ6ιóŒ¥Ä®èô®8Õy®Ò‰Å¾7[C±¨ ˜Å0Îí¸r±˜Ò¡NbIçZ¸ÄÎxÑÞgM{†âCglHÿ½kŸ5í¿(^‹ÕbŸ•”ØŽ> Åu‹ÅqN|tç™IgÌ&qšÄfθLb2g<æ\/·×Ê%Ærb´h|ÆP¬$q’3>’˜ÈއìXHb ‰$î±ã‰a$~±c‰Q$6±c‰GÞ+‘Dâ;ö˜ÃŽ'œëßθÁ+Я¡Ø@âg<`Ç2ï·çùKká káÑ ˜n>}NÉ«Î)-S˜ vÛ€ÆÈŸÑØ<}i­ÎIæÖ{ú=úìë„ÆKÒ=3:ɸÊ/&¸ÞÁˆÆÈQ˜Æ‡Äi<ž±œ|ëœ_c§ù5O¬Âà‘ü`>ڌΡ0w#ßÊÿ5¦s~Åh âQ®gô¶§•ï#Nåö³+rÆ^ÎŽ™`z Ö€àÚ ÏEÃê<ŽüÔ n¾àëžNü¬ÂÕœà„H•[CÎêXgé#u¾­h……›*ðJÙªÎÍÊ9„T>SÑ+u@g•ó©rîUΤ* É™•:¤09c?ºŒ1u†5Q®)ÏÀžtNF·Œ9µ¤—I?e"ÛƒÜLtÍÄÆ\de¢C&m ïL™P'Ÿk÷òÑ­>ÐäËœܯ <—²\lË—?äÖ£k£Kýü6`k!÷ѽºFtm„w4Ô勞ªÑ1½½è[}ØR;­~š»Ðµ ”u¡O;ò)o„®9 è߯vx{øk‡Oö´Ã§ ]ûÝÇ8ëB×>x–!³Ú>Ú¢o^¿&å³i­zlÔðÙ]2«¸nBf+uŒß:Ú° »h«¹/Ïwê5 S²[‘ÛÄ_:ù©ß%ÏuÚ¬ ~~h[¹nÀþ.t=÷[i—.ôóCÓ߀ð¤íüôE^]ØÑ Ÿ.tnBNzàã—ºB/¼üj9´™~yîÃ#@Ý:ucw€6Ð~x ÷£ƒŸ6ì@f7ò»‘ßm?ööCoȺœü[Z/_Z/_Z/_Z/_Z/_Z/_ÚÏýÿ~Ý\žy[#Bø™V‹ó:ǰOŸ³Ô9˜±S`Ú·<ªñ57éü“•·ÅÂrÓX óê|¼`. >‚`"dŽ*L„Lê¤Lª|Œ‚Ñjåk¡Nê„ÂK’n‚Õ*XM‚É”8©Î¼çD*<;+ÄZ…Ã$øK‚·$øI‚‘$¸®nÒ´:O½xê% ú$À+aVM½ióDÊ’à›„~)Ð$!7 ~IðJ¦ á•̸H†o ú%cC2<’áႾ<\èá‚ÎE]íœJ¥"?:%ЦQ§½ó¡Mç{:|ÒåšzéÔË@Ï ìÎä^&¼³Ð) ²à›Eûf¡6ü³ilÆ_6íìlhÝÈÈúæ ?ù9ðÏW2s©—K½<êåQ/ñ”G½²c#|-¡çy‹ÅA…ï;g\îó¼W¼ã\‡gìXq޽ÿÜŽoœq³H¼ò^±ŠÄ)£ØñÉb{ÒÃc|è=cÆU(öX,îX,æXc)Îø×ã ™ä *°úh\ç5§—¡Ïò­sFcôÏé܈Ã:/92WŨ<äVò9§kRç[ è|+Qçr@ãþx5¿_c»u;ÜÂ2×¹ÂÔO¢äê’\…¶å¨ÊMháYNè„nw0 ò}[¹NÞ¾ä8œ*ÁÜ—|\’ÿ6z\ã G)¥`å^4£ð„2FtŽl>³ùžÀuõáŸ1£òïeð¬Ì˜WX•g O&ô |&Ã?…~ÉŒR¹÷ÜÔwS?útÊÒ‘B»åR7—ßþtè3¹NWX¢™è’*vH}lÉ¥­ÜðÉD—B|Ë ­—ë îW`{.÷r‡>p}—?¬ð7óÇÔc(>…à S«…~+„¶ù^!×È÷R·¾%<ËZ¸ïo#måAFŸ-|¯€O‹\s/º-èÙßìlä{#åÕØš$<¸î¤m׎}Ð4¢S;Ÿi´A ºö¡wú62~Z°¯…Ï ê7 O<ÚáÝŸVÚµ‹ï}|϶ÚVú½OìCFºõ od·ÏªÇnŸ|»  Я`@-¿ÐßEÐÁ§ˆEèP„ ÌÙ /FN1uK¸WRê–R·”~)…W)v–!« ¹åŒ‹rê—#¿>åÐT"«ý+±¹ù•2oƒ¯ªàSŸ*øTÑîUÔ¯BF52j ©¦šhj ©…¦–~ªƒ¦š:d×!»ŽvªGN½´<àჇþ>êúÐßG[5!£ {›àÕ„¾ÍÐ4S§™1Õ MüÛhï6lhCv2Ú ñÃÓŒxv £žðì ¬;»áÙ }ö÷@Ûƒý=èÝÏu¿|ʼAÄò/Öq§žÖ¿¥÷Kï9–Þsüû¿çÇñŸü~Ã~·ñïþ^ãƒ¼ÓøO}Ÿ!c×¥}ñj½›y:y¬ÆÀœÔ¹iÃåð[Î÷‘ïwPçBŒÕùIÆØòè¶rZãfzu~’I Ñ«óãÎj MŸÂÑ´ð~+«¼:ùœÎGîÕØóó:â€#?‰Kåѵò“ kµ_å& aý¢ÇšiÏéÓ¹I"t~’€Æ¥çû!…ó»OÄ!Ÿ„ëu ³ÞÊ¥«q~©»9\çwÌ‘ѧóO:°~TD+gï&‹œ:‡M;ð~Þ¯•Ñ£r”HŽ Ám·r ¨äïÊO2§s“ èü$³:ÿ¡[ç ÕùI¢ù'TþÃDøÄÇ*Ì|yä& :r”ÐÇNªÇ¼•ƒÜ§ó“L;°~ë—þ;!ZçÔ¹çÉM2©¦ VîqŸÆûTÓ†˜Ma9çrޤ Ï5ªò >¿`¥ ±äEq摜`’«ü¼•X¦vž€XÚ'vPá" v¿`êÛyCB9A¢U^ÉG"¹Ã.p©<Ä›¡Ù<¢òƒM*}Éfá¨Æª| yTN°‹*/€LWÕÊF=3Nå3½ _?n\áëÇͨœÅ2­œÖxhãiû™'Sæâ/»¡«FÇDìÈB—jx&¢W|’à“D½¤9½\F$Ã+™ú)èš"q:¦ c :¦ £‹6s!#ÛSѱú©ðLEÏ4èÓ(ÏBn)|Ó)ËBF:ü2à‘ xdlUS¨ Ú(³ Ë@§,¡E—¬y5íʆÞþnÆý{¨ãƒ·›O÷¨šfõж>dôp?—vÊGV>º×"¯{ùØ ù|/ä~!º¢k!u á]ïBx{°Ç­‡:Ê‹%>ÎC½bÚª«©_ŒŽõè]ŒŽ%ДÉz–Ñeð.ƒ¶ š2¯šþUP^ß^Ê+(¯ Ü‹.^ʽÈö"Û ?/íàÅ®jÚ§;©W-ß±³ùÕØXKY-õk©_KY-eµ”Õs]Ïu=×>Ú¯Y®iÿztmD~/í݈Žèш}“`_/íÓ,ØÖLÝflj¡~K”šv¶CÓŽÞíд£{;tíèÕŽ}èÛÉxêEN'¼:‘Ý‰Ž½sjJÚm´}ÐõAÓM6÷Ë\]æDòo±˜ÄŽE$qÆï·[b ‰)ìx"|öbqÃ{­/Û1Bx\°X,`Çöü_äû庱ç÷öÜޞLJÏßí9»=_—yúûa_Ûsðð¹wø|{±9öb8Ù2Ÿþ{{³í9³=WÏecÏÃ3 Ÿ;ç¿ö>Ÿðõg{ζþ¼Ïãÿ[{ŒÿÖáþOí/þg×âþÙu¸÷[ƒû ëoí«óºí¥>cí¼ ¾ËftŽyì^ÎoÁrÊV0vVÈ|nXçŽÓy¶Æuž-ú`%uWâûóŒÚß§ólMë<[´C$þ íÈ<€ï ÃøÄ*¾¯âû*x­¢ü@¾ýÔ?(r!G½•kË­rÔŒŽ«‘³Vã÷kÐo ekÐa õÖ2×t~­H_k@åÉ9d­Ê“sŽÏ‘¿Þ­ók!¶¬ÛªòkYyù¾™ë±eí²ºu~­Ëý…þPêDÁ?Ê¥sl«ÇèaÈ?Œ:‡Qç°•çp™cÒF‡O‡åØB§#"U¾û#ø~Ĥ#·Ö Ê¯%yŠ[È­ut´Ê)¼eZç×ùµby'u^`Úíd¯cÆÂrl¡Ï±ð8ùÇÅé¼Àè||”Ê•{¼[çØ’ù}wt'@wÂÊ ìväׂÏFøl¤}6©œ_¹ý˜XxHå–©‚;+6 òüž5«rŸ‡-gͩܕçÌ,äÍ:oRå÷=zçaëG#UŽeÉÅKÄ©üE’ÇRòcÙy°$ÇU÷ &Už+Ét¶^€m›¡ÉàÚÍýÍŒ¿ÍÈÞ,s^> æTÞ+Éou6_DÛ\4 ò"™È6‘m"ÛÄ6“¾2=*§äC’˜qÈC~ܼʧ)y„ã¡§]âÑ3yñÔGV<º%B›ˆŒDÚ#{%¦@¿D™ÿÃ/ ~IðKšWSŸdxV£_=ú¥  ×)r®)ðIAWלš¥Ò©ÈOEßTôM•¹;m›FyÚ¼š6¥Ã¯þ^™³Ã#ð¨ç³ñVJÝ,hJ©_εyèŸ ]6¼ÝèïFw·Ä%èîFw7º»©ãÆÆ\‰OÐ3þ¹Ðæ¢C.m–ï|îw`c>÷ó±!ùùð+¤n!õ ©W(ßá_öx ó@瑹¶OM¯<Ô+†O±Ä*èÚŒîÅsjjWM z–ɺ”AWï2hÊ /£nu*à[AyåËpß'íB/ò½È¯ù<6v"¿š6ªæ^5ß«±¹›«±¹=:©WKY­Ä:ܯå~-÷ë¹®çº^®Ñµž¾­—˜d^M%Û¸nDFøw¿ûÍ´a3u›Å®95ÍlAßêµÓí›H,¯Nd¶#»Std¼uJL ¢å½ð냦š>lìÃÖ>ê÷Ñ}ØÙO?XsNùsر†ÄáqÅbñ„G8c…ºÖ&ú¼ß:›¾¾fÏåíù¸=×v®‡É¼u±¹ªÌQÿŒ¿·–%sË÷Z· [³ÚgM*|®·Ê1³×”Þo=i±=‹ÿÈÚO bßu¯jK7úAö .£Ý–aÇrÆÖŠó™þØq·ÿ]Éý•Ø´?úìï×ùìà»Jþ&Tˆz ãÿ ÆÏAð;ˆºóy0«iË5ðYCÙZ|àø~ˆ±{cë¯Êñl彃×:·Êó¾Þ­ò/Û¹Û%¯¢äE<ŒöÛ Í+nBå¸K‚ÿqðHÞ èÒëÂÏâ]:çúäQ'ë4ìL“ºò,A4ꥠKþ.ÔG‡"ùï<äÄ#³ýK¥.~Z$¿Ðz±ÛM[z¡Ïƒ®š²jÚÈÜ<ê×ÃÏ'¿è^/¿«ØUM™›û^>{Уžð*–kd–û¹ÕÔíÈ®…¶9 BÍ^Ú –öè…G'uzå7þ½”wr¿Sêag/r{á}<üN¢ûÅþÅþ>îey^ð„ü›:Ïʸ|Ë;=k3 Žl4';î Äoú›¹FªøÕ­Ï}tËó;¶îõý!œSpôî›>V™ô¼yêÏÿrKìhz°ëÞÛ?{ØmÌó\qÏw|m9^KÎ ÷™ñ°Û\¡ø˜“—÷ãk_¸ËßþÉêæ3Ž ŽŽï·å´êóÍÓO¸âO¾ôt°kCùù_úïË¡°è?´ê×ÅþÀœ¼ü;ç$<ÑlŽ_{ã©Oý¬28úè oΞ”ìºjGôþ¿J¤þ°UÿêßÿÇ“'™“—Æ``¹9Þ¶÷ ;Ïj ŽÞw¾gmuV°û¶)4ʤþ¸Uÿ3ë|Üç .´ÃÅ›ö¬¼õnsÕy/üÓÌñüì½­ÇÆoùÅÉ]k_ÌŽÞp™š{ÄÜT9õ‡o½ì~õüÓúó…ð›±ø]qê…_ý}ðnsràháhŽtEÿW|ÙnÇàhß«Y'>1`Æü¦è†ÏôÕ`×ß^ßÏX}Í®¸—«UçÜý‹S.›ùlhîÞï’C?µçŠÐ8ÜUúõ³úé–_•Y¼¥òà¨kÃÜÊk\èÇ_>ö¥ëï(‡Ÿ5‚ñûí4¾Ñv†¹;j¤µãõjsב¯¾ýÖú¿}Ó›ƒ‡ß÷À©9CÔ·ú;X‘Ý[9ý_w™»-÷<ËÜùbOåÛ­Å[fl»O™Ý¹á–ãƒ5oö­Cgõo°öÄ·;®þôSæîS¿ùñ@˱æÎmg>uÈðïô5ǾëÐÏêç`Ó÷—Ü4jî>÷Ò†Õ§~8d÷ÎËêg[ê®Øò¢í/‘W¼¥ÆîÏ`Ç£»Ÿ|äO ã¬ÚÁƽD;® ƒÝæä$®ú¾3ý€ß^û“{CzmŸ³:"¼ýü°ÚÁæ›ëÿrvù æîìÏzpEsçªçWÜpî|è÷`ûcñÛvŸ/zXýlËùÝ•G-ôcuìÓòÇï>û¡ÄÇ_nÿúðÏ¢"F‚w”M~·íÉ…qX£~_m?ÙÓ5þÐ[××ßÍ?¬l¢èõÐïÊö«XölÄ”­Ð?sìºßî4VzÀW [Þ.:óÁûÍïürÇWž9ª?¸ý‹OÌþ5bW°ý s¹«Œúj\tm¯ºý`s÷—&¯¿¤¹ÞüNá;­®.¸ýẦ]{ß²ýˆúj<ô,?sïOÓÜ=òdæe«Ÿ0üùMŸ8r¡Ý÷ž¼²æ‡n[¯`ó<µIǽ ½}z\ØÏ¡Ý÷ÆÜ?ÝöPè÷çAïÇÿ”Ó»åGvûÏ\ýÚ=wî ¶®Û»sOɧÆE×σݼ©é”CÍWT>ÿ¡þ?o>šÐ¯ ¶}Î;ÐUôtªß/Ùù­ÆÃ{~ò¿Ýϸš¦žYx.Œíº¸fã«Áíߺôç¼fÿ~Úýïhw5.>uÎïn:´õ—æîù?u­X~sȾ±­'ývÿ/-ôã¶q×~îG¡öi»à’;³KX—µj|\9ü‰sO¾"Öœ:¨ëÛ?JYiŽ•žô‘O]øéàö;òÕË_pús¿)ý~ÆÂ¸ˆðýò³wu˜gè~ðíªßxqÒá§ÆÇ ½ztâj—9¥æ!}îæîäŠ«Ž Ïm¾ð £'^éÕp×÷_Ûú`ÀÑŸj||~õeo\þÍ Í©öÏŸ;•ú¢yÿ5¯5œ²51¸í{v×àaÁú[Ž{à‹§÷rêÿ/œ7ö—»Ï¬7§ô|êþÍý¹Sçü>¸íõ̦ Geëz¯z,jW=õUÿñáëÊbÞxd¡UÚ;^¹¸}ׯ‹‚Ûþzò¶“ºn ÍçêÞ¶¥%£xa^P§ÆÅKË<å”·CöNÝüͽyþ™¹ãö'‡ÞnܸeÜn¿Èä£R¡ß×z™mÖç,øEC}rè{^1§n{ë¶“_YgîhøVÙ•®n?ükßë¸Ï ãªNƒ[~z„ç„Òåæ”üÜí™6ï{úîëµ·7¸íßüð‚]ÁÆÛ¬‰õU?µêgovßaN½¶ê×ç¹2ï{ ½µ÷„èඇšœ:Øô}¾{gÓ¥ÿ÷r½êçáå¯ÞzÀ·™{öïö}fäIó¾ëÆÚSî„Æé¶ëF|ü¥#Í3Õü!è[oý0@¯ú}XÜ;ï§¡~ß#³å„úï¾’ïËÌa¼6½%™gªßÑ Ïš>Ô˜Ç*ÿ‡¯_¿ë¦·¼ãnsFœpÅæ}‡Ì}åšÖªà¶´Ø³z»w?~üU‰S_õó­ùÞ;kž7÷ <¥ä,ó^ÍYßY°ã4ëAl¼îÈ-?ßøkèTÿÞú£È#N?{.¤ïžÜKs×÷^zÜ›Q~ûm‰QÁmÑÉ?¬Ý±0^/ÏñŒÅ,Œ—zÕ¿ß`:ùßÅæžêû·]RØšŸÝ#Öu=Üv¦ûé½/ý X”Lˆ¶A§úùŸ]ñmoó'Ì=´Fõãw˜÷Tmó?tö…ÁmåÏ—ÝþØŠ`ÍžÈLúªŸ¿ÁÓèÊÏü{ß”ÕÑý­Ò±¡Ò‹<ôÞE¤èESDM"ÑÄ5¢HÄAE{‰5X¢¢ σˆ ë"jbb¢&Pc%v51ÆÿoïîÝû¼¾yg¾™ÿ|3ß÷ŽÎ8¸ìîÙ³çœ=çìîu«¤Ÿ?=ãÿǰ¾ÒÎÇ·Ö-Ô^þ®tqjÃðëddÕŒÄ)ãU{Íô_p¼lcÒÏ_}L%+í\Ûéþ©šªÜÖù²bà"?J¤Z·ý™þ ätÎMú™¦ƒåÒÎø_·7$e‘ÒE®ýÛ9·"I›hC{¦×‚Û‰ˆt?«ö‚$©Ï†}¢¼Óº¨âá»{Å:/…“˜zc¨Ø|²ÓQŽbfúßâì0=d…ôóÓgXñ¤²ë}g|±f )…6}gGFºj—äWöLï[6¾ù§‘eŒTmc‡s¥² ›0×ñ1ª†KYw¿HVòpÕ>G3=öp4ìñÙe©:" KŸ#•ÁùL(k ¥¦²§#Ÿ kÙúøw´gú-ün|^Lón¿q»ªŽ§ ör‰çëBÞºÚ ókzJQ\ÞIKæÎF*§ìç@ék´Ç¤¿Ú},üQub¯Ì©–…¢¼ãÉÎü-­Å|t×½¹FÑ?IdòTýo2³‡­ù;SúþÖI¬‡êÉÚé&H;ÞÜ׳´’è*|cqÊGìOFéÆï o®Æ›df[å´ÝYª–Ýo?©ôˇh_Ý7ÇnÎ%#6\žöä…+Ú3»ØÖ¿àN‹GŠø]½þL51[#øÐ]0Z7ew€Ogšöøý™þ·ýòã÷mÆIÕ»—EݘwQÒ¹ø•‡O&º¦Eiá7ß #œåÄí™þ‹rËo¿¯ZªþÎ+`­¤yÝÔÿw¢{9×ïÈwZ’.’¿ÎA{¦ïâÖ Yãý½T}¥g©åŒ+RIÃÜó{;]T×IÛ˜]×§EI<'L{áŒeIêzKfvPüIæp‡û½Uùþa6nÿÒYBo%ú[Ræ$òÿR§me»¾X)ìóÃjšÌì¡x{Îö§áRM;Y°Òö¦~÷¿p|LJù¹Gâ×ë VßÅ6az.nl7!B¬»ŸK“‡v½'ìsûò@¿¥ÓNˆ¼Á›fè"Þ&½“;ÜøÉ*Õ~Æ0½o’%©¦›ìpýí]å ª:/Æ—2’´gJӮћÕyav±]‹‘R ¯øYÍ,H)ÍŠÈ(Æ/Ú3;ØþËã'¾|Oªùàñô!s‚¥âòì‡þ^[‰ŽºåAOU?:†ÙÁö‡-:G{¤H5ü<¤øÃÀ #a:u=Öͦž\ä-‰tØú;ª?Ãìc{Éâ¡×w»K5“~r~tMÝWÕµqÛ²“èX¾OÙºA?fÛ{Å,úÍÜ[ªYDÒR*âç:í½ËOu#‰ccÛXè…ö\Ï/:?…© ú5rZÚKÈy[zýˆIó7‹8 “Ó¬i"?äv ÚåXn5× >õ-ì²æxèw·-ëqkÁ†÷/ñ"ºÊÜ–îšTÉoþÄ7¶gRø“Ü=ŒevP¼¿Mñ{û¤šÆÏ{ÇFI…§ù­ª‹%:d›¶7‘‘ŠüÆ2=³mŽFªy!7 ›Ûî:v“”¶pé1馡ØÅX¦çâaï!3ù^:gMi´¥§CÏÏÜ,HiGšKxÞÌç~LßÅH‚6‘ÎyõCÆ$ìì½¢§ùR*çõD;¾ÞiXÚ;OìKÎÑc2çM¢\ SD‘R[9"Y^AF,¸Y9Ñn?èðõžÓòrÃO‹¤s,oòͧÞÇæM±tm”ö—bÛÈ ID²åˆžž˜þ±Y£Ò¹‘nWæç† ýäŒùëñ%#ÕnÓ ¡Íõ4ŠCÙÛ4Žé»Äþñæwîôyι´G…[ÆœWù:Ç|Àz¢ƒ1¾{³‰Œ¢Ûßy ÑŸé·³Zx:E:·LR~ë÷?[p@µ»þ?ÔO4¬”b÷[Eä‘Dv‰þLßZdãV…Hç¶¾ôifois¡kb­µŽè‹et­“ºó|“Ÿw¡Ó»®ÍCÛœi‚ÏsÔZæH›ÝGXuH•§Çݳýrúˆñº Ýø‹/è0;ÐÅ5ú¾aß¹s¿¿yÐþ7±®ò–Î|åq¡*Oï¥7 v¨z®ä㘽èämÎPéÜït#ôD¬ÇMìüF•K×y–済øÎít˜½è˜÷38AÌï¼Ýi“á“~|nœ0è佇ÕyýS[w«›Ô…ûeçÆ1{) »ÿpµA¨t>âñÒ:ï—¾2­>pþü1¢›C#cg2¢“aSï¥V±Mã™]”Ò´(ú©t¾ß_žJ¤õ ‡9F}Ktù“ç ^¬Æ!e=Žgö Ä¿óç®Z7`´núõ¦CuÞÅͺ5iÖKâï_Úðn1¡œ×gö CéúÅs!·ótûÛó®´öc›_ú¬ê¯Îw÷÷&šù‚D%þŒçöQ ±“Îo9L%)åÒS•·‡ ?(â ËÇÐÛ ï§~”ÎÍÙµu‰½àãËU‰gv…Ø«óàrPâàh×otß :ÜNY&ÝZ©êÎ#‰òšƒíWxd}¡Ø•G”üTÍkÇ3{Ð>Æ|¹tþùáLMÈiõmÛ¡÷lòˆ®dӇ뻚¢Ó³v{Eáé!=¤Zæÿ¤U_ät ÿš|¢äI˜~µ äÀ(ÕÊÇMoK9¥%«ïuPå;ÿŽÍ¯;,Äþìz]2÷–²ï¦omúí'Å’T;&ˆj\ØçJ»¢žåoGªy縤â"Ã5$‰fÃAUj¾0ûº»R#Õ~¶çÒ×’¥´¢ëÀŒÄµÈó’å 2ŠÙ Ú3ýjåcоbÖæQüðcKß±lRמè- Üpj&ex„´Wýè¦o­M(ž ¾kù~\9ÿ\pË`ѳèJÜû>~n:Lß%/¨AºHµ—¨`‡ŠýÞ§;—i=– "º_ëš/Ÿ–¡äñèÇôZÂãbí³ˆÝùaĸ³oO-ˆm¹ˆ”šý=ÿÓ˜%/B?¦ç™Œ­Tg%3"eŒKKÙTøŽºï£§j©ê~a"÷÷vòAˆ_uô–éÁïÔ6—'äÌé§Œ+â»X'y~—óhEÉî÷¤º¾{zý7@ÈoÒ›Õonÿ1ÑÝK·ÅÒ÷Oâ\i"Ïç¨wñê ÖCË—Äzí¹,ôSó–âüOñ‡âœŒÝŸ€Ï½èó`í©nYAU»·„] {ž³hÆTµëŒ³[Ó4÷…ßÃòÐáyâ^JuôšÑê7)®£ÁS£y™D뾫ëŽådlBõ€@³—hÏóïËßÖýü¶T·ïvÛ?&÷$ÝÙzQý„sD¥žSLdz/šxtÖ‡-Ä|ë¾Éغ§%ö ,î«üÔàR)½Áâ&ýÝ8fÛ’7l^rn¤˜oÝ…g3ÞËTôAÆ¿üÕ¤÷t'±µ¬,ueçŸ$‰‡Ö½ˆmšÄìc+v¯õs_Hu¿-øk¿[™öÁó çß_BF>îôc¾3Ú1ý:ÚEG%—KØ9bO$kHïâÔ™ˆNf·›Ôý_ý¬ºî'1;Øòö”9ç/þ.ìൊ±±dNžÑñ$gIÄÛn|ÿÂý¬ê'1ýð|öÂ(‡Ð%Ïî–?ñvÓëý¹Å9­”{ ´gzfÇÖ#¤ ã×ÿñµ/YpUr¯}1Œèèñß©C$yV–ñ•õ_ =Ósþ/Ý^ÚŇI¶ÐötˆSø%‹gºÛ¶Žy©ê›ù+é ¾/Ûçb^¡ç5/žÄôŸŸÚyK³çÅbÝ\¨¢ Yúõ‰>gZŽ#:zºš– ô4íËÔ}Ñ$¦ÿÍ÷ÜÃÇL,]à÷ËN|[CtÍž¾c”ÎÎïÉ$Ù ›©þ/…é{ód2X‘.¼pyw[±#²"hÏÙ£¡ªvŸÚmM×"ÞMztmùâÔÐaöGO?:Jåô5‰¬xø{fwr„èÞ.÷,m“äÉÏ&íúsÂýo¶ ÓÏ¿/Òëœ-/”õ@¾ØîÙo††èFO7Ù8ø1Iaëý˜¾7m¹yÏ7§…Ý\ìýÛƒÚ7Ï’•Ëkv9tYìWuˆ–§wx”5s¿¹:ý™þ7ÉÛÞÎbÝ\dû>’“›ØewÖTÕïÈÇaÓľ/eüw{ÊT¿šÂìc#Ï—.N±ý+>Ά¬º°Âà^¾9Ñùg.jýÑP2ñêÖ\_ŒöLÿ_½'¨ŠõQÞ6'kFïyxä·¯Tþ6»±ã±oÏ㤰§f¨uÀå^¤×}Û¾&¹-ïNmÕ} ¿¢·qC³jsË›TþS™¬_ÞgÁá3ÒÅŠÔr…}¯]'_l©ö½üÛ!/ *ã“ñüþFä©ÌÖEÎmþÃðuÒÅS7árȺ;½cW?zKÍ£¿= ÝWîÐÙC.÷'éÿôt YwŸ·sÝ¢»±®Ú?Fµ£~^-äÊìB¾^º§Þ§ó{z&3;XôÖÞci-Ÿ¨zÜÞlå÷OÉæöiÏÍ4Qj<¬ÞnqÙÕRñj|šÌìa’`·Är‘/]âç„Êúؼcf°îí'DK?‡é¶FØ÷WêùÐdfóÙy°t©Þȧ“¶LØUþûrÀ#Ú“ ¾Ç\}DÞ•ÂÎÑŸÙǼûÎõ¿Öµ.Ý“7Z‚ü{·ölSN´?™ÿd´¤&O.Ó߀~Ì>cqUª—Ãr)XzQé/ÕŠê_q­‚~fv}hÛSD+ÎãBÒ”óÿ)LÿÓxT?æŽÿµQkIá™»·g"Zú5×â\>ÅÕ~¦0ýOîë:³ÅâŽR}æÃ[7LMÈVŸàÚl%Úc›^$ÜîKÒ¶ÒP-Ú3½+ûÉzæßÉÖ‚ˆ¢V\#Úús—ß1ZI¦çÇ­:ÞóXlÓT¦ï÷øyl=M£oVŠx¼->zÎÞ÷ŒT}Qn»•H]x~?ý‰ßØ›5£UùL•í@êÏïê÷ï¥7Τ(ìëf>Ç©ëyá–YgnWˆýx¿×~hª¬wi¬l~-¥zvîOŠ»¼|~º‡­ªç´ÞÏ\‘©ë |>&ýdýJSÙy•TÏÎÉvz;$MÈ]Ö»4ǰæýsKÇH Òõo,¯‰ü\7,ûÚš»÷Ôxò¾üÁIo&'¶j<&Ûƒôégô@p¬ð£ ½Úç.|6RÌ_W¹m‡õj\±wi ÏIþ9ƒŸÿ ÿ<ÙÅ<§úɆ”:Íw=ï¥ú•Zz1SAfüM/>Vª÷«Ó˜=,4ºþ¸Íì'ª^Øý¨ð¥Yô¢ÞXµü‹#='™ØÌ¿c«ú•iÌN–ÐSwõ>¦a¬Ÿ ¶¿–Þ¹"õÎ-!Z~¿7c5½ø›‡þÌNVXË¥n‡;Òò[8hY^¦Èí™Pcñ´©’æ £'I¤Ìܦì‰.•hv× ©$éô¶lV?´gú_uQ>(P監©[5d<)K0çNõvu=±{vqO;Å/Ðavñe§@ú%–Ô#ÃRò*;dTãoݨÊÿ½gK¾L(ôÈý˜º.Ó˜}¬ýù­¥9Rƒ|avŠìt®Y8,$Œh»×Ã:“4Y¬#Ñžé}ÃV@7‰¸ÐÀ÷ Š=íœ×X”Z€õÉîS…ßö“Æô¿)éÓ/¨ò`ù¶˜ÏÎۦω­'Ú.e½÷Wtå¼ é;ïw9 I ’á R÷ÇD«ï­üYí’¡|Æô›ßëZß¼÷ß”^ÐÄÏ”oqíiÒß‚þŒì^ŸdwD{¦ßv?.5²s"RþøãÖÄHÕõþÆ?ˆ{ùÌ®&'û û^Í?Ò˜Þ©wÜGœ¥F§•ûþ6#»ÞHôwîqhééœÛ(’ÅâÚ3ý"È:\x_Üß5z~×:,Nøñ]+£YnzVέ=g±ï^Õõ1é™ß[K!ŸŸ0œ3‰ìºþ\Õ¢¥_y)ÎÃÒÙwAJ¾Û”ÎôÏón‘w5Ž2ެ¹¶Eèu³[ÕÈŸé&‹|.ß#ö‰éÌN”{ÄÆÄò/Z“ ˯ò›÷$Z¦W’Á¾C{f¥ìûI±îáÕÿüõ8©øäãMkZŠ}I¦"‡tf;øw8bœ‚›?†·ûHß,ß46籈ß3éêw¨®ƒtf/e†t‡®~Çß(‡ëN¤¢öâé“}W¨óOmU¾úöU±®f[ÓĦè0û)û`1ý²AÑ Ùß6özÁ°d¢µ–JI¶›Ì(Ú3»(Û}ÅéPñÕ.èç3?Õ }îï3xÝV3KÕÞègÔnd6;§Qýg:³‹È~ZÊQác¹Å™!Žóû"³?:ð•s‚šd0{Ø)§S¥ÆOÎtÔþ<ÙqÁŒ]îóÔùw,;G?¦ïrš ?.5N`‰‹²^„ܬË]æK´®ÉfÕ«mUýe0½—ËŸ£Ø‹øÜ˜RpÞ¼kª˜ÿzÿµ8¯Êì@ÿƒÇsôgúßÅýk#%c÷ 9ððÔ¢sº§DÛŽ.°þªÿÎ`zÞU)GP©qRQà‚ŸN’ƒñ¿ô¹Ú…hͲË{˜´RÖÚ3}î–O_VJÏMOɸAn÷¨=ÿ•¹¸×QòaOL¯»wuݱìi¡Ô8~D?ßI’XOÿü|æÞë†j¼`|Šï︿TãqÓë,6Óyæª}&ÍÞ»ö³ÍäPÕ[Oüd¢Ú;‚„ÝiÂóÊØ¦L¯{èê‰iPíâ£zƒ&âó¡ìU×kÊû­=ìFf¸žJý™~÷ò<¬ñ½_V^¹?UÌçÐÞo¶}Û`D´mŽ4n‘nˆ{¥Ùcå‹ôgzÞ˶A*ÿðÚ·Ÿ“Côtì£:5n1.öuœ5OœÁô¾Oþœñ]uÝß|9(êr¸íÞÉU#Túë€wcÕïgóóR±nf0»ØG­§™•Ôè®I©8S)þÿÀá®ý{–½$%W!mb_1›ÏCè}³—ŠWÎÿmå\ØóáŒe‹¿Üh£Î—#¯^£ø'½y2;ªx”Yþ÷ ø·–m½Ý!‡¿é¼øÜÂR‚ ëØôˆÌfV =³“ýéÕ/ï}[j¤ŸcG‘#>«ÝÉO·IÉOSj”Û’lv¾Û”Éìb?ÿ£ów$â©©êùp‰lNÃ…½Ï®—/¼TûÌdöq€Ý }4Ü¥¼ ?@‚ÒnXŒÝ©Î›Åyñú,vp¯Ê3“ÙÍÁw®G>;Ë"„­_¢åû¬,v>ƒöÌ.µèváY£Cµü©H¥ÛŸ;[nõûR%Î,”$ÑŸÙÁ¡ÃíéI‰¸olàñC±÷ÊaSû ORÎUD‘y¿×¤  Ãìà0»çK û§Ï›¼Þ›T.´ëIL¨º¯’‡³%3Ù}ú3½¾}¥lQž—ÔÀî%HeiŸæóÛ½ : šà^%³FM²ñ…Ú3½™ÑvÅšƒk¥z[à³QȽ’^§Ï+W÷÷¿È‹êzPÖA³‡#ì{qì_v΋+>L*oí»Ö½XõWKå ŠXŸÙ]×Ñú3; ü\¢aâ—Ëú6F“£&CºYFœyödÓ!¶,A{¦_B¯—/Võ5pæê–Äz9êÑu›of°G­==ØùQ¬Ãl~'ì1‹ÙAeøaì€K qGö”WW“£o9?š´B¿Wò‚Yìœý™T²ý ÔÀò(rTþüþqUʨ;· H“Ú3}Wþôq½‘O¸Ônµdîì|rt•É¢]CÕ¸YOgN2ég«KÐéù(ßW7xn_¾;±9zäç¶ÆÝU}Ñk‚ï¯*óSýVÓûÑ•ôË©ÁæèáÂv_‘£7—Û…ž»«ÊkïgKàI&[DZM3™ž.“? Q׫AÊ'§õvSÕJvì*ÿüÿß)yO&Û¨ëu&ÓÿQþ]Y}Ó®Ñy˜ =VXïŸ}ëžržJ²Øýú1;¨b÷Æêùßiú!õM•Ÿž/Nä8 ûUüÿ6 Ãô^ÅóšzúUå(R5LN€ÔøH¯“?ÚAfûÿø¤9ö%3™¾«È Jõ9Ãí~bMªÆY{&òs±ÞfŸ|ì–^Ò홾ñûùú ù¢˜TM;ô&y§\|¿¤ìÿf³ÿׂ~LßÇ^ÙßÖw€ç¨Ê)Ó=qé—ý‰Žç ÿ2Kùÿ83™Þñÿ/[ÏÏ•ªæø][óÇüŽƒ;£ÓçqúßwZ—Šü¬žç—JÞP•7Ý`ü†–ê9Ým¹”ˆýŸÊÓóqúùBÊi©ž~5á}…Tí/ïZón¡ê×”ýÉ,¦Ïã'~ ‰¾(]ºµmýK¤ªÎz_‹VŠœÑŽéï‹ê}µæˆ4q>Võ× ’••#Õ{O[yÁ {æóVâB3öÖýó;áõ›m¯±þÿ»þIæ2«åoqÀþd|,ðÙBÃñes9”-{ U~Çþ2K ön˜¿g9Æ,êòø;ö(Ò{OKÃÞÌ0 Mð÷´’ù[ö(›Åâ/h›Uê½§µ¿«±[b¬–éìMUgí[^+”[a¼ÖXÛ­𷈿·aËß³?m.ó·µPß¶‚¹!‹`þæj_Ë–ãPåéáÍb¼ö ×ütð~åmô·=Ë\öÆ–äg…z+ô·ÂxÖ˜Ÿ5ÊÖyü]{ÌÇó±Áø6ôðfËø›öqüm­\þ¶–7Óýíoñw[ñ·µ /Gðëù9¢¿#Ú;¡ì”À±fQßí;¢¾#èu<Éß³G½sÇ™Õ轩…² ê]ÐÞóu?®àÏü¸–ñ÷ëƒÙÛ"n˜«øw×ð·ëQö°à˜² ù»õèïÏß«o^¨÷BÙ ¶âUÆßªÇ|½³Ù;õ>¨÷‰ãø±(û‚_ðæ[ÄqbQö‹ãø°—9.,êýÁ»?ææÿŒãe¥pì×góåÀ<†—Ìñ]Q ÞƒÁOðBŽÝ ú!q ¯5mCA?åÐ… ›•¾á†>a) £5 óïÌÞÕ¥ïy…£8dŽþá ßÙ‚½ÛBߣoéÓ·ó# ûðr[öŽ‹üŽþe†#&cµb¼ÈJþŽ>ø‹ÊeXZ4Ewáo½`üè[ '6cÄPŸú:6¿ŽÍÙÍ^Çæÿ–ØL×j — èÊož&4cؔࣅ7ÇfÌã3½w.1Ž¡-³|!ÇÇÚ2B½Qçeã½÷¬@Ï$á¿›<ãïZ¥è½uÇñßQo†²y,þÂ^ÌÏrüwŒÕ2›½u)c?Æq ø2æbd xØkZ~À±fPßü´¿mÁo[Ô·…-0w‹.üÝKÔ[€ÿvŽ7SÄñf0^{Œ×¾ŒcÁc>0Ÿ°KS†i z–˜Ÿ%x¶‚ü¬Po…þVÏó³FÙºˆ½nƒùØ`>6ßæÇ–Ìfx3ò»—Ï.ãÍ ¿}0{WÝýíè½{ 9@_Ž=x´wBÙ)™aP:]ÖýŽgÙ»—ÎÉþïô4yÿõ.) ûºWWðç ~\+ø—(»_7ÌÕ ü»{ó÷-Or¼÷ASæ$sÇžƒøÛ–àÍËV㽂¿kÏñÝÑÞõ>(û€žÏIŽçÞ|Á‹/xóÓpüvÌÝôüÑÞõþy §]Æh‡=€ß€³“òD9<^æøë/õÁà=xÇW‡îC@?ôC@/mCA?åÐÛ<„¡Oè…ÿ0Ì¿SöŽ(ŵ GÿpÈ.ýÃA¿³-{?¾QO1nè;›2†z.ÇN×ð7ô‹Ø;›Û3åHŒy’ãÛ€¿(ôýhÈ+:–½[O±m¢ÁO Ê1) ?]Ž)ôÍ4&ÓØKc.±J|¥1•ÆS%v*ñRyCÆ>ýxúr|£±M‰a4fÑX¥Ä(Ÿ 9)qg"÷+úX4Ž(qƒÆ %FèÇÅÿSß®áL}¸â»©¯Vü2õÉÐì‹?Lý¯âo_Kýì«øÌÐì3©¿TÞ¤>Ñ‚ñKßx–qò8-tg„ñ‹Ø›½¦jšcÇq¯*æB[è®-æß¼X@†íð»vèÛ4:€_Kðe…²5ÊÖøiǰ§è;{ò;·øÆuD?'ú7…aÊ:ÓõŠz ÍñoW¬WÐsG;ìÂt=AÃúðÆ¿}ð×í}Á—ÍcÁ—?ÊèˆyaÌ Ì1íƒ1Ç´ íPü C›0ÐîË0§Â1^g ÃZ oÈRZŠÿ‰1¢Ð?sˆ† bÀ÷ë<±Ùë<1»Ùë<ñ¿%O´åú¬`ýškø[îÅùnÌq‹ø{èX[¨7@Ùt 5ü=ôzX„) ‹ÐØ”c¢½1õ¯Ïø»§Ù‹Cš³÷OM!S3üÂ,žc¢Þes”Íaæµcµ\ÈßHmÆßD½VèßÚ”c…cüÖ¨oýLïmtŒßü¶¥>A(a¼ è:s Áà7² ½ÐAÛ”C5 Ï<4—¿ÉÂÐ' ôÃN²°Ñ)ŽaÑ÷ØÃQÙ…ƒŸpŒßÙ›aGRÌ"²Š¿à?âÃ]¢oðÊ8ˆ¨Äx‘4îæ2,tŠþ¢@/ ô£!¯èxŽ…XÉÂR ÆÁ1ÔçÑØ@ÿÐx¬Äa•Ø«ÄÛWsF[1ö¿ÅT?õß ¦qQÁvSrGïhŒÓÏ!õã™~ SâQ¯Æ§WcG4Ñø3‹û(_”¼R?v(1CÁhSb¨ï×Ï1©§~]ñéÔ+~›újÅO+þYÁ]£>zø?«Ÿ_B†2þÇ-ŽßŠŸ˜A™Šý%ã´BwÆX_&£ lÉmM1?SŒo›ÁÌ/sÌü®µ{û¹-ÚXÄsìÔJ†ß c6€f¬7Kô±VÓª’ã2 ÞôlAÇÇ6löö•,µr-š“>PñM²9žéeŽá…ß»T2R÷XŽ3Šyz$slQŒçúÞø½tミ¾Ã¿ó£këŸîÝPžÁCäD×h„‚V(~†aœ0ŒÑÉ”a‰†£Ü9˜c1ì±ÈË « cFc^1hCõú:/}—f7{—þ·ä¥Þ\_•ÍT\EØGsÈ ùŽÑ}·€ÌZ l€z؇ê à/ Ñß0ápz c ŽÁzÆ•Ì5˜`m˜¬à8=ð¦¨7¥¾21ú7Kà8=¨7Gsê£AÛü–F6ú·¬äo÷â˜gõp²Á_ëJænÚÄò·üé¹øk¬‡•öèo²ê-nqÜž… ÛQyç_ÆË½/óë€þ–¶»ãYB>–èoyX¥3Ìl+Œg _b²5Ê6èo“ð ^6|-èÛ¢l§Á_ÔÛa|»2æþì1ž=ƳG´w@Ùòt¿Ï8fÏB†•툲SŽ•}R³'[+» ÇÉ®dnTƒz Êš ŽÕCcÚ» ¿+øuÅü\WpŒÌ× e·Û”ãbç2¬@ÈÖ¼y`~¨÷ÔplÐö/^Þ;—ã`Ûr l”½Ñßõ>(û ìƒ²/èù‚ß³Ìû¡ì‡þ~˜‹èùƒWÔû£ì5~ Ïô@9ô!ß@È#°‚¹þ ”ƒÀ_l9ýƒÁo0ÊÁ(‡€~Ê!h‚r(Ê¡(‡æ1\n* ¯0Ì? óëdÁ0!(ÎŒ„ºpð~Â1~ç. ³“âxËØÜ¨€<"Pñ€ãD¤p¼ Ì'óÄü#A/òÇäFû(´B9ó‰N`˜A#(ãÇ`üŒC}õëô>.£ébø§¸Kc®~¬ÕÏQ•ØJ}¥úXŠJ¼¤±RX‰‡J,¤1Æ>%ÆÑøöjlSbšÏ”86¡™«ôã”’«êŸ…*±G‰5JŒy5®èÇ;^J¬ qÆ*Wý³RÅïSŸ¯à +þ]ñëÔ§ëc¯(¾6!ûíW}6õÕJnÌ1T ÷æ°‰øk€²A%ÔŠ:ÃäWðN0¦1Úƒ¬GÌÑ¿7ÅüÍ G3´7‡lÍao-Ѧ•-Ã6k ›ir›8ŽmÛ…áÛZ ]»8Ž[†þíѦÖƶÊfæbM¢Þ´l’9þÍ}±&ì`+v™=õUàË1ŽaŠQ³rŠexb1†3èj@Gƒ±]ð{Ðs¥þöì–Ëq¿ #ÌÕró]ÏlŽç¥aX^›Ôm|@Ïüû‚O?؋ƭԂ¯ ðDB–Á ŒŸ!à-?Cñ3 <†å2,ßNtO‰r8Ö[gðGÐõûŒá„u¿‘7’æÈh‰±£PƒñbP'çÅÍøºPþ :rümÖìÿ•ø7¶å–hoŒùš`}›Äs Kðoв)ʦԟ£½™7ǰ-3Ô›Ûrìò¢W0,Ѿ%èµÒàoǶBÿÖŽe‰ñ[£¾ øiƒr›Ïü¶åx– oöèoAcæj1Ú¡}» ü´Of¸–2¦¥-Ç?G}ô·Ô0L,KÌϲ±„L­ºèá¡c|kŒg²5èÙh^Á´=[Œo ú¶ g‡±ìÀ‡=äbqìÑÏtPv@Ùã8T2·çˆqs9Ž¥)DZÌe®°cœŠaÙsrŽåêh«A[ ê5¨×@&.(» ì‚² xr® í Z®àÉ e7ø7èÌ ewð⎲{ǪÄ|=à#< 3Øœ'|¨'hyV2—ë…²æì…þ^èï ™z£ì²÷†}åCãèù€W_ðê ùú‚?ðæ‡z?Ð÷«dîÚõþ ï¹ùc¼ð Ê(¢è~ƒP*bî=ýƒA/¸Œ¹úÐlB*™ÛE9´ˆaf†Aaà3 |w‚®:N'ôë>ÃQ>ÂÁG8æÑ9ŽcÖWrÌLÔG€nD% 2VZ6Ã̤!$åHÈ!ô"Ÿq¬LÔG¡}èEcÑK4ƆÞbPŽÜc0F ]§Ô÷Ò?Ê=¾>F¹~>¬Hc§3õc䫱Q/ð?åÀʽÿÊ…_=¿ýOñë?÷ÐØEãÕ?Å©WãÓ?Å%%gVb‘~ÒÏŸ•¸£khœQâ‹[h/úh,8&o<ÇâÅ®ƒ8/è»%pì݆½ç=aÛžhëy’ãê¢7øðF;ïg<]ÌÆ_Ô€÷ÿaïLà#­ªDŸË!½ 醦Ó4K³u‡ô–îNR•½²WöÊ^Ù+{eOg ŽK3y:¶#KP”V§Ûžç€‘ ŒBƒËËh7_Gû缑fDiÄ÷?ß=•”y :Š>Ó¿_uê»÷l÷ÜïÔwÎýî=ç:tµ™¿›¹ÞÂïÂäßB[<2Å£óëù»U>âkÇÙšÈRsWLi;mÛ¡¹ùv€¿ywò71Zëë"û®1kj»¹ÞŸ=ÌÏÆ–Ä÷$¹ä™.ÿÞÌõé·¢Ÿ}2ûÍð¯ßŒõèß¶½¼ýÖ[‡þC}ìÿª-¿Ó>Õá¬Övõjmxä><ŧu^ç©Øì©ò¾WëÃs}š›÷ÈiôŸŽ]žìéð>#NkÀr}°gÒw¦Ÿú>“þh®£¡ ïècZž{é,ô}×goÒ:±\ŸŸsàs÷ê9ÀŸÃ¸ÏÅFÏå¾>wJkÄÿŸÖ¥ÿc;ÐãŽI[—Þ©CÏõNhïä::‰ÐNœ´uèw»‹ë]ȵ›ï»‘k7}»‘c7ÏÜ=ñ¶6ñøîAŽ$®“#I~ä·_þ-õ£Û>‡“ùÏ‘¾óÉüæ7ò™åù$ϦÈ5by¶œÌ×=™»Ô¿Ü3ûz¾íÒßóð‡°Ÿ{2ÿ6ü;þÍ}=ÿ=/ø²ò{è×ÚÖÌå)|?•9<ù9]žNûéÜog€s&÷Û™Ìc40g1®³™«³¹‡Îæ¾8œsµžõ¿AÀžÇžÏ½q>÷ØùÌõ `Vˆ ÌJÜg«Û*úVOÚÖkh_ÞZx¯…÷%Èt üÖƒ»Zëi‹C– ´m€Ïh] ÍK¡±‘ûu#ò]ÆßËÄN¹_®à~»zWò÷J`6A}WÑw¯†îÕð½Z×w-߯c×1¦-Ü?[˜ß-|GŽxtr=r^^·r½U|Hú·Ñ¾:Û‘itwð79w¡›Ý´'‰¾—úvËë¨Ë{þ\}¾ÿßk«²—èE>¿àóK>/E9k¬Q/óùO>¯ðy•ϯø¼Æç×ê'òŸ!¶3Äv†ØÎð\2ÄvæL=Mlgˆí Ï(Clgˆí ±9_Ïgaÿû7Ø¿Áþ öo°s¡=³`°ƒýìß`ÿû7Ø¿Y¯ûv±ƒýìß`ÿû7Ø¿¹R÷¥aÿû7Ø¿Áþ öo°³E÷W`ÿû7Ø¿Áþ öo°³Sßýaÿû7Ø¿Áþ öo°ãÒõgìß`ÿû7Ø¿Áþ öo²uÍû7Ø¿Áþ öo°ƒý›"õ±±ƒýìß`ÿû7Ø¿ÁþsåØ¿Áþ öo°ƒýìß`ÿÎÙ6ìß`ÿû7Ø¿Áþ öo°gO3öo°ƒýìß`ÿû7Ø¿³·û7Ø¿Áþ öo°ƒýìßyï‰ýìß`ÿû7Ø¿Áþ ö﬽cÿû7Ø¿Áþ öo°ƒýËzÁþ öo°ƒýìß`ÿûwâ ìß`ÿû7Ø¿Áþ öo°yþìß`ÿû7Ø¿Áþ öo°çÌ=öo°ƒýìß`ÿû7Ø¿sîû7Ø¿Áþ öo°ƒýìßÙïýìß`ÿû7Ø¿Áþ öïì»Áþ öo°ƒýìß`ÿû—÷û7Ø¿Áþ öo°ƒýì_b'ƒýìß`ÿû7Ø¿Áþ ö/ù öo°ƒýìß`ÿû7Ø¿œY4Ø¿Áþ öo°ƒýìß`ÿ²WÝ`ÿû7Ø¿Áþ öo°ƒýËžLƒýìß`ÿû7áwŸ¨åS9 üú>e^ßµ{õ¬ä´æ1ˆÓ=ð‡ôJŒž—ôë>øú^>¨¹ f5ôëyÉyÝ—š¨ïV¦#bA¿î‡?¤k‹qzvRcBgÿjœ¾k ÚóTξøDÝ¿OsÄè~Ö±ˆü1úÎ%h×#ö"m²ë’¿ñþeSD®ƒÃºW>qñŒ–ãcÆè¾¤1Íy0¯{æ5ïÁ>»gÖ9_¯{ÆìZæÂ>¥D#÷Ù³–Îú榈ó–3OÆi>„ æD˜Ñ¼Qº¯Þ­{ë'ô½ÍŒ¾»‰Ò} ‰z&3 9öÙØSÎg:ûv£ôœf¢žÕ è>üý6Ÿ‚³§7JÏnºõüæ„õ‹ýùóº*NcU¿Æ«ûí¬s®3Z÷ì{ìY5çÐ~{ÆÓ‰ac4ŽõêYÏ)ëg;{¦¢ô=‘[ãÚ ÍË0£ç?£4ÆuëÚî^ûþHr5l™×=1ºçßk×{ýþ»Ù‰}÷kü;mß%9çB£õl¨Ûƿκð^»Cò68û«bìÞ'WôÍ×àìYŽÓ=ÿ>=Ô¸w¿]3–¼ ÎþªãûnÒø7`בå|¨ûNjΆ9=‡ks7$Ã3ºÉÐMFÉÀ'#2ºN6X°.`]À¹‚’ W9ÝÈéÆ L*ò§B+˜4ù—Ï4x¦ÍYw>±dÀ#Ü p3hÏ7ÜLèg1–,®³á—Íu6cÎ>™<|÷0¸æË3oChæ0ö\häÂ?Þ¹ðÎ…w.<òäŸ<èæC7ºùÐʇV´ ¯=@£…è¯ý1~/úôríåÚ‹>½ÈâE–bè_B üK Yßø–2/¥ÈWŠ|¥ð,£¿Œy/cÞËáWŽ<åó6$© ¯‚¾ ð*‘§Ú•àVB¿\òú请 W[…\ÕÈYM{52Õ  ð5À×"k-rÔB«ZuÀÖ[l=<êá_ÏÜÖ·¡Ž¹ýÐðÓŸöFx6ÑÞ|4šÐM´›¡ÝŒÞZÀoáZêÏ·¢—Vxµ2æVdhGÆvdl‡o:úôK-ßNøt§üNð» ×½.pºÀ‘º‰ÝÈ*u »íVjñõ¡ã>䑺S}Ì©ÔsêCN©ƒ4àUê} @{€¾Aè !Ï0¸’¿~zÃô3–axKŽÛñIû wþ-ï÷_޹—cîå˜{9æ^޹—cî?}Ìý»ÆÙò,F-ç#Ú‘(ñŽuSÄYóÍI´IÏvNëþœX=o°çzœw­‰z®g¿¾o[Ì=â¼oÑÜDcš›è¸æ èùži}ïºIÏžïÓ÷+Qš?ЫçE§õ¼¨[ÏùLiž¢X=ë3‘«(V÷üŒE¼²yN~ãl”žGhÞ¢Ùˆ³?A=“~Ló ºõüÏ!}?£gÓÇtŸÐœžOOÔ½ú®v^÷?ºõ½Ï”ͥ⼳8¯~XsnÒÜFcšßè°æ8ŠÖsB=+´WßåÖ½GѺOÒ­gÚƒšÿhʾO’œ-ÎY¢h=çîÖ³îA=3{ÈæFrÎEëÙwæxÙ«y k.Ãh= ïÑsG“zöhVÏÅÇè™[¯žŸÔ³Hsšç0Vßaùl¾gæaÝ‹­û¤<ö|’{Ls,Ö¼2ÑúÞË£ï“'í>*É»$çyÅ¥uÞ…%ÚLλåxÍ¿Ô÷c‡ì;2'÷Ò¼}·ìÞ«gë=úÞ,hsÓ8{7çtÿf¬ž ž²{6ÜKQšÉmó#JÞç<x)ðÈ’ø1@?zÅŒ#<÷¤Ý«éœ#j^Ä}ši^ÏÖÇÙ™òøx¸ö0¸ÆéN4sÐOúË…F.üsá ï\xçÂ#O>ó6<ȇv>´ó¡•/zƒVò0ÞhB£y*d>‹¿—¹óríåÚ‹½ÈãE–bè_B üK Yßø–27¥ÈWz̆eô—1ïeÌG9üÊ‘§œ¾ d¯ ¯‚¾ ð*‘§ڕЖšužø×G¸Uè¼ Ü*äªFÎjÚ«‘©ùkæmøR‹¬µ ÖB«ZuÀÖ[l=<êá_ÏüÖ#»þ~äöCÃO»™ý´7³‰ö&à› Ñ4gßfh7#Wˬ ƒZ‘½½´Â«•1·"C;2¶#c;|РР?Nøt§sÖ†K]Ðë‚^8]àt¡“ndíFÖn`»íF¦>t,5¢ûÐAsÚ'mÈ„¿Ô~Y‡h—šwÃÀÓ6 aøÏÙkœ¶q‰yÄï—ËùÆþ°|cÓH<Çü?1 ÷ØÆ&“,G–Æ w„c‰3$¦x"2–A≖Æâ÷‹_µèÓ‹?ö幯|xñ×þºøè⛋_Î=¼à‹ÿ.~¸øà,ßš¹=©_éSGúÓ‘¾tØûÐaÿ9ì;‹ßö™Å_ûÊâ'‡}dñ¹'œwQâ‡}cñ‹Å'8&ê“‹nJsYNؼD²'ÅÉ%½Éæ!’½w’¯Ø9Ï=¥g¸gì9cÉ)$g¶%÷¡äƒ“}5NÞç9{EòÕÈ™“”C6/‡ä¾qÎPøíycÉ—ksÔd‚“yÌš{cÍ‚^8YÈ™E8YŒ5 þEô—¯"`ŠàS­<~§‹¹2€-B_E{íÞ²d,¾  çá“#ÏyÁaŒÅà€[Ì÷Rú À+¯”¶R¯Í›\¯r®Ë-†~9°åðÈAîRÚJWžUàUB£|ÌþLU1–Júª¹ Y«£Šþ:èµ€×B_#ºldžF‘i”ö&pZ ÓF{;zlçºv0ôw×Áu ¯^ÆÖ¯^榘~¹æ¯Ô€‚ŸÔxíg¾†Ãc£Mê½q=Æx¤†Û2´LÙŸÏÆÖ |'|[ Ó ï^xöóþíà1î^¹®—ñÁ³“ù‚N;<:™~Ú‡k 1g½È+õ³¥F›Ùz‘½]4!C»¼¼>¼¼>¼¼>¼¼>üö]^^~k® ÿ>û±ä™:µœôPD>Ò Í[ïì‹Èýt8"'é^›Ú9‘ÿiZëʸmžÉMíä¯×üOÓZ[&ÖæªvêËŽÈaÔ³î3Zc&^ϼï×3ÑšËÞ§gßk>(ͳâä<¦ùì=6çÊB®Ò8=³3Qs&Zs–´î̬žÝIŒÈ[:¯gF=‹9¢rÜ{ô é´îýÕ\Qz–g^óE¹õ\é^{&`áLGkÒìל¦Ñz^> ù£f5ÿ}¼ÍÅ-¹_œü¦³šã4FÏÏ{õ ý¤­W#y¥œs?16ÏKú1›×Eò0¥Ÿ°çÐ%·¡œ¯ÏJ´y–$ïTò¤¹µ® úȈ³¹¿Ü21š‹Ê«yR'5§þ¬æÕÑÜT^Í?³Ïæ ‘s NžªXÍUå³ùZ8Óš³*JÏé»õ¬þ^{†Vòï;ç‹bôŒ‘WkäLjŽÕYͳ£õr¼zFbŸ={$9ÉWÑZCÇms”;õs5Où˜ÖÑ™ÖZ:‡mý'ÿj¬æ‰ôÚZ:ÎùŠ}ö¬®“ƒ5Jóõ»5ŸùaÍimóš;ysšwužÃpò\Ó3þ'l.+9—áä™ôj®«I›‡UÎögŒÙü«N h=ã+±/<“ÁM¦?ødô‘ŒüÉÐLÖ¬ X0à]ðwÁß ]7rºI&ùS¡• \š|€Kƒ_4Ó€IG¶tèdpn¸´g‚WÙ<|÷ç×Ã|y˜hæ0Þ\h仹sá ï\Æœ <ù@7ºùÐ͇n>´ò¡U€|uÐ*˜·áC!sT(± ôŠ¿—yóÒæ¥Í‹ðì ½:½è¿…¶tÑ\05\—B¯ ^å|/C¶2梌9/Gîrd)‡FrWÐWA_²V"K%c¬¤¿Y}àûàQ^•ÐGž*úªà]Í÷jæªü䯶kåZèÔW\=ôë¡_ïvä«G?~x·£#?zðCÇOŸÖ†<ðl¢½ œ&è4¡—fè7C»µ€ßÊu+¼[¡× ¿VôЊÌíÈÙŽŒíð@'@€þt:áÓ ŸNð»Àï‚^ôºÀé§ t#o7òvÛ l72õ¡×>äéC}Ìg߬ ¯‚ð€ÇðÈ:ü}ðgy†ù;Lÿ0ô†‘a˜±ŒÀ{œ¶ñ)õ·åŸÄבçð–óÿiò‡c¢p,Žƒ$Zÿ„ãžpÌÃ=ìÄ6ÏH,Ž["c‰UÂqJdŒŽM$.‘8DbpÜ!1ÇÒØ‚ûʼn'$ŽÇáØ!2nx!2Vàþvâ‰"ãƒÈµhîÍߨ'¼9~Øß__|{æÐñëŇÿsñßWßý·ùíÜwK}v±ß?YÎëCZWqVóNj§­ÏäѼ¦6÷5?[¶öJ”­‹(µ€âç5‡©_Ï•Ïj®¦ã6?“Ôd‘º(RãGò‘Ê9q'/ ôÜÐwÓçöÛš’›Ä9ëìÕZ}òܱyƒ’ÑK&¼2¡›‰ÌYŒ+ ZYâÓ“\2e“!mè$ÚYòL‡O0EÐÍ¿ÀA¿ˆq•C»˜‚x›C°ˆû4GžË»TXNã+âþ,¦-œä-‡f±|ZŒ¥¸ÁgüÅÌK)}¥“ö¼v)óV |9m•àV!{#º«„^òÖÑ^‡¼ÉÐÉ¢½FÚàÙ½è¶×ÈØÑOô›Àï@žd-¢½ü6þ¶ÛLÛ ûÓÜ ~trùƒ×Ž,À ¡«^äî…ç´Æä{¡9L?ßûù>„ÎûÑÅ:‚ÖÇdœôW0Ž1ð}´U[ƒ,UЮFÕÀÖñ½™jèkb>j¡ÛŽjK2ÖÑ× ýzàë¡UΘÛd÷ßœ:~xt㾉¿MÀ6¡×väoB_ÍÐídž›¡Ý"ÏztÑ n+2¶"{'òvÒ×.~2o™;Å®zÂþàwA¯ ø®1ûëÚokÝò\GOCÀu‹Þ§9ûàׇ~û¸ÒD–h ã8¼€ oý1¶aè K¸ÃÈ?Ž,ãŒey}=jy}}"jy}}y}ýÏ{}ýí¾¶¾¼÷úíµ¾.ÏĽQËu¦#ê*Œi½¯(ÍuÐü±sZ_Ç£9k.šMšCv¯Ö†µ¹iœ²ÓZ÷Ë­9dk}Ø8­ý5QcÁ£õÛ§´>O´æ¿³¹Ê:±1Z,`óÝ89c5ÖDD½…Mšoq_DÍ…x­ç¾7¢nlŒÖ^jíØ9ÍãÖ|³ÓµÝ½‹¹ÐÚ`›"rdÖïqš{v¯­óîäÊŠÓz ­É0QïÝ«uei¡›Ïѩϰ_ë3Dk~ǀ楶9zœZ ±šïѧù´öÙš³’ÝÉß«y =ZÃaLë‹í·uâ¥Î˜ä‡têÅÇkÍx¿Ö¦²9¤ö˜“×6Vó·û´žÑ>[oSòÜ:õÈb5×­OóÝNÙœ·×ÌkÞÛ8Í}ë×zSš¯ë˜Ö*Û¤um¶6’“»kNkÏÇjýy¿Ö¹ÝgkEHz§~R¬Ö¼õiMú)›«Ë=aóIÞ[§®ÇÖ8»>Ns|ù5w.ðY‡m]߉Å:A­Sï³õp·Mh‰­Émë…J.z§®xy{µ¾YŒæÐ ÚZMNnÌ)››^r}95΢5Of¬Ö‘ðiÝ}¶ž„Sw¿­#áÔÁ…fÞ>›÷+žyÐK†v2mÉè#yÞºã)Àº€uç®8p.þºE/Èé¦YRéO¥/M>ðK& ~UÈžŒ‡¿éÈ—^xÃ#w&ãΤ/‹ëlxeÃ+›ëlÆ›<ÙÈãệ¹ô€ëaÌt›­œDäB#þ¹ðÎ…w.cÍʺùÐ͇n>tó¡•­` ¯ý"G!óSÈ\úmáåÚ˵W®™3/²x%†>fC‹ø—@³¾%À” ïRd-E¾Rx–Á³ ùʘ‹2ðËÁ-Gžrd¯·‚¾ ú*·y*Á­¤¿\¸>hû¤ Ü*tY5kC–jÆZM{5üj¿yj¯EÖZ䨅Ví´ gê­‡G=<êá_?¯!üýÐðCÃO»™ÑA#<›ho¾ MÐn†v3´›Ñ[ ø­\·"{+¼ZáÕÊÜ·"s;2¶ÏÚð(ýúÐé„O'|:Áï¿ z]Ðë§ œ.tÒ¬ÝÈÚ l7°ÝÈÔÇô!O:èc¾úh "gþð~Y€˜³áÖ 8CÈ3Ìßaú‡¡7Œ ÃŒeÞã´‹C¿ó/_˃u¹~ʯú)KöK<µ?Iì´4n’x)'EÆEáèdëô¯÷œl½~i|ózqÍÒ˜Fb™pÃý¾°ž/±Kd¼òzqJ8F‰ŒM$. Ç!ƒ„÷”‡c ‰1±ÅÅËqÃ/nà7ì-QÿgFëòNÙ´N½uè]àÓºfÍY>kë^J­]§Wk™¹µvÙ¼­ùzQÜb}©Q¶#ÆÖmujæÎ/æ)_¨½à·5ÜÐv‹ß;mëñHý7cJ:n.“%9wm­§¦péà¦3GéÐN†O:ð)Óö§5x7ð±¶¶e¼3ÀKA¾ d+J´9Í‹ “!~cËB×"W÷e–ÐA¾ë‚¶ÖXxðsfl=øyü-¿úyÈU$~0¥ÈRJc(¦½ÔcóV£ËbhçX| h•´&ºÚgë Ôøm­øJèV"[¯ÇÖ@(GoUŒ§™jÀ«ƒ^¼ê YL íuЮC½àÕ!O cL&•¾ø¤Ñ—­4t:Èœ ÎÛGJ&r¢¿äÍæ3Jû¨ø\Óö‘2ü:f¼\¾w ÿ(í½~»¼Ù‹L½ðë墟¿ýÈÛì°åðªàºB|"ÆãcìU´W¡§*d©†_5:­†W5tkC ²Ô¢³ZÆ^ËøjÁ«E¦z`ë­‡^=ôê¥ ™h÷3N?4ü´û‘ÇO{#ºo¾ ø&h4¡§&h7C»=6#c ×­ÈÝÊü´Â«•qµ"C+ãoGÆvd @'@€þtðí„O§ü¿ z]Ðëb\]àt1ændê†w72uÛLÝÐêCÎ>tÐÇXûhëCÎ üƒŒu@ü d€Ç´d¡3¸É>ú‡é†Þ0ýÃŒeÞ#àŽ3ŽqyVËÃRþ%F|wÛßzçßïúbùÄrýª°ùv}ï~çðçø¾áíô®!ìÿ9ìá+¿gûص\om&¢ÞÚ„­?/õhWºµ>Ä~­Aû*ŸÖ‡˜µÔÕñZ#:«ç´±ßÖº`FëwzµNÄœ}ô:5‡üZ—xNkyNh=Ox¬Aok&lýµ5Çm½©a+µgׂ³Vü{h\ä³u‰¤¦@J´Ö˜Ò:lðI Úú¶Rt´ÖÓ¼ò­Q¼_kÇ.ÖHå“Θâ çÓ:³Z«ýïš)ऴž´·Š ›4=S ±k¯­-º‘¾´_¶IëµíµyêƱ5&iíÑ[g©L{ü¤­œOr&ðÉN‚_ë“°×ÖNØgk¤mEoÛÐÉ®}Z/-ÎÖvØ>¥õ‚‘yû¬­ìÔLc|;çlà2è%ÆÚºÀéA­;zÈÖrØÞ.‰À-„v‰èŒï{b´Nã.¯¸$ú’øžtÈÖ*•zI’?Y½üMá7‹Rà—l °n‰•àÝÀÇ -}ð«F†TpROE—iÀ¥“&±0…ðH—¹¿¸fù.mðM?SôÇßæ,Z™Œ%“ëlÆ[È÷j¾{ ça hæ0îäËAÖh借^:oæoãÍ>Ø<‰¿˜Ã<æ%Ÿ1æÃ?ÿ¸uÇ ³Øè£×"!Ÿ ÄiôÑ_Ìßbþö„g1t‹¡[ ÝpJw 8m´53®Rx—[F[9¸å\—s]'¾70⇻­«W)úd<•Ðô1òû³y|È냾øjä®>nÝÁd©^ sP:®ë S:è×!Wºèáºq4 £`Ð<±Ý7ÒÞH[#<{ÐOöFi‡o³ÌÓqë^¶@¿~-Ðh_ò¶¡‡ 2¶ß~2·qÝA´:ÖÕ춃{ Cdà{ß{ä;rôÀ¯~=ðë_Ïqëªö¯~½ðë…^?zëg|ýŒ§Ÿñõ3¾~l¬ƒÐ„æ 4¡9ÍAhBs9FÑÃ(0£ÀŒ3 Ì(0£ÀŒÓçøfò/œw#¼Ö+ñKäzo8V ¯ñ†c’ÈdiìŽ7Â5NN¶ÆÎÓñzk½K÷Y¿^|ðzû“$6xàdqÀRÿÿd~xM8ìëGúù‘ëÃa¿>Ò—?Y ñ×—® ‹ÿ}2¿;ìo‡÷ñœÌŸûÑazéú°øËá å9.²!c ýe\—Á¿œTèºÐO64+ SBŸ¾©ò¬6=UË3¹«å™-Ï"øÔ2®ÆÚ€œÙðh`ÎòÐac+‘çãh@Æfd)nƒ×þüäï/¿ïÍ´{á€f~e³ZÊ ž ÈZÝîãöçªÚAy.I;mAd¬>(Ïxá3‚,>dõÁ„y‘v®GÐQ5ôGÜvéd½Œ@g Œq„O3r@k„q6¢ƒfdj–gŸàÉs œÆÛ&ã\ÆÐÁ=1^|F€ïºòü’~Ác=ðaA‘•ë ²0æA`éEÎAø&Úš›RSs|ÂÆIο7sï[q]ídkjoÆzÚ›±Ÿ÷·íå]ÞÇûÖÛÇû‡®©ýW×Ó~µ´ßkßîçu´ä¨ß=Æò:ÚgMî]_Ôr]UðV0¦è`%r­äž[ ´Þ!m«üZ÷±¬Ž¶uëWÃ{5ý GBŒÖW=¬uº¡ùNúbm}N§Æj@k¬ÎÛzÝ;§´Æ*×k€[ƒ »âmÝîíA­mϸ׳¹/¢ï"ä¸hRë­"ÇÅ´]Œ¨¹JÛ:pÖÁkò¬o+:¼Døñw+ót 2\Bûú¸ÅúŒëi_O[ôâà·Ok°Bom‚Z»qÎ>þ/×Z¬Èréaë$‰;«5Ý8k]„ËâµN8°—Áç2ø\ïËÑáå{µ>+z¼"ÖÖ~Œ‡N<íñÈϸâÙzàñà& o‚è{ÌÖ.u— >ü”­ºõ„u+¶1IÜO¹ðÚÿíÐÙí¢[ưý˜Öýïäzç ë‚85ÄÁODFø™ÐñŠÍÊà³kNë »Ø=±Z_ä=è> Ù“= žIðL:lk£&³î‰ÙËèK.¸”ý6ÄwÃÓ O7´ÜÐjàoS[×'þ4úÓOšÄàW£›Zh§#W:tÒá× ÍL>eâ—“ L&ºËD_Ù'¬»äAnôJ€oCþ:Èaœ9â×Â#€¬9àåH?rÀË£=O|z„­E¦Fæ!ÞùÀæÓ_ ýFæ¾zàr]ÝB‰7Ž[W¬¾´• [1pÅÐ,†f14KÄ×çž(‘þãÖ]+¾T|lúã­ëVÎuPüy`ËáSN{3rV€S•àTS íJú*Ñ•±øÐÁˆÄÐ÷[\ÕÀ×_ƒ,5Ъ¶Õ@»ü:ðëà]'×Ü{uŒ¹=5 ×6#ôÞÿ kd.á×(º¾Qâxu@»~-ðk_ ¸-´µÀ¯{¢ ºmÈ×~ømÈÛ†N:$΀w‡ð®šðï@=rÍ÷ùÿxöHÜÁ¼ôÀ³~½ðë…_/üF¡Õ Ï~ôÓÏøú_?ãëGî~è÷3ƒÐ„æ 4eLЄæ 4G‘cTb`Ff˜Q`ÆÅßSþ-]'ûmùN¶>¹6&†¹t]ìÖÄœ 1êí[ÿw©ùFkSâKŠÿ¸tMjéz”ø‰K×¢"דÂkI‘ëG¾¨ßÜ[öŸ–®-õ…Â>ÐÒu"ÔŸ¬®ñõØÇŠ9 G¡± ¸­ðØÜjpwÊ3ƒ¶ÝðIà¾L€fRÀÖáÞ¯x¦òw'£Ðgݺmi©é>.6ûþKú‘û§·;ßB3g¿«úëýÿ”òëᘂòu®'>³âÖùá—\gïøá¿]œ~]èHGÒ©÷lLyôêÁþ˜•-¥:øåSü×N¾Çåq}äÇY/T„‚ó—®¾âµ×ûÙþ;&àgùÄþ¤ò–½ÃõÄÃùZÂT[èÈ@ËžÁÐÁ›c~±öÑC¡þ‡ÿåÙè~x·…ßòD}\Ö#®ÓNܳ÷¹À ®'~öÍW.¸g(tä]©Ÿx-½=t°&-ó>îZoå |èÜÛnÛV¾ßâ»þõÑu«äš[WúígzO/t䦲»O˜Í¡ƒ—¿÷Ňü”kã箎/Oh <1ûª×þ„ÅϹ¯~»ë/ÃzpÍå6m|öýûBGþ~"§í/Bž­ýìc§~õI†uîí¡–‹>Øóð§,~Ñ‹ÛÖ<åš.Ú9<½*täàÆ?ö³U¡þäïÞuðîPÿ-‚Ð üŒ…/¼kÿƒe¹æ>}zé>:òõO~æ¹Ò¡_îÚ3ôóPð׿l¨ôÃÀÏ[xï'_~øê‹\sß}ú³êo yêÖOlª¾%t äy÷7oÙíÚlÇê_î¶÷ÿƒû§wDýƸž<í þ®>:òüDù+÷ĦÌ<üÒÔ=ÿ~sèÀ3½§ÍTmp]|ôå[å…ú_q}ö¯Þ¾†ŽÎgòÏÆb]·,ÌÏ“ÛÞóÀì =e殩º'Ã÷KèàY¯½ÿ¯“ü®µz? š–÷žwáZ×¥U·?ñð+ß…žÎ÷u{ufÁ±…ûðIêM;óIyÑÒ]uÖ#¿þÁ@èàŽGg­¿cA¾Á^÷ _Ï#ôtþÏë|õ;û\OÞôŽêš¿»jaþ®ŸùðÝû½ ã=XrÁÈM­Y¸oº'îÌg2"ä³÷Cè[»~ýøys=ù¥Ùw½÷žgBG7=rG[Óö…qvºÏ¿oï•ÀÛù ôï¸+êUדϬÝðÓíÿ#ttóçü÷¾ü«ÐAnþ¤¡WBƒÇV{ŸúVàíü‡V9Âãu=ý¾³B·6‡Ž&ÜðÙç5¥Q=øeymÉO6‡†ßq0¥;Ÿûý{?<ðзå”,èï©]_øybcèèßw–{ä<ðÇß_•·`¯#[=×»ÿB׆³W½;ñéîŸNÙûä¿ú̉ü>×SåÑÁg‡ŽîyÅ÷/%½¡_¼ù¥»~ò…ÐÈÃ\úÙŸÿx{?<àn¾­>7qQþ©•hð‘ÐÑôwUÜò¥o…ïËÐÈìGþ²«õó‹ó5eçÿk/ÖŸS¸âéEùgW „ëüð¼u!tà w”29:tƒó¸¹x;ïÓŸoyèÄÎ{žKÏl¸±úÌ/•†ßðdï'Cç-èóÀ{ Ìî[>½ð¸áÑ/>ûÔ_œ³h矲ó?ýÎãgÜ<ýU×3…ÏÝòìóC¡ÇåiQ»:t yoëG¿Xºáo¾ñ kvx;¯_îþ`úÇn<ÃõÌ»ÏûÞ\Õ£¡Çsï<ï” i¡ò˜=m&4þ3ú¾¼¯{úNVÎdë™C ïw(ôxã\Óµ™§,êÅ™¦u¡±¾÷ï%Už¯{ÏOúÒ¿ïv=óÜÝOÝõê] ÷ãã7Tü`ë·§<Æ/_xø–—\ ÖþÃó¸øûòÙû°,«­mAÅg@De|°ÑPQÑpÆg2‡×F›-ËÈÎ14SRÐ5E­6Í8%Ž Ì N•‘•™•ý÷Þk=ÏÃñï»þïû®ÿ|ÿ9ÿ%×õê»ß½÷Úkヲ=­µ™æóÔ)ƒ¼o¬Ò«:~ðÞ[­ÿ.νä?éDÙÞ­ãÿxãô<”§ùÜ›ÿç#ï5ù¢jÈG-Âfšô{.weÔ7ŽnV?ÆíÀL8ëO̽wûCïó¬w§ú€Kó½·ã½W&WÄëU ÿÌëì_!ν{ä—&ûûŠüÌ7K›ÆÎËD¯';4ò4ß{žÉGUoÌ\w}mž¡wŹÏ\äXY.òWç_}jI¤Êø-#¹gÍ÷›4ß»o…6Üæ|M¯íÝ!ÎUTÏxòÑŽ"ù¼÷|cÓÄðbžøÖåi¾w¯üµ¼[É×zÕåm/n8#Î]¾Ú÷“Îy"?íVÞùËýÅÃÒ I[ò4ß»Û}þqç“›õê&³<»ÒOœûqÒ¤£Ûz‰üÀÀ?öëaŽÏŠˆÔã^»Þ´äÆ›4ïÙŠL»¢ºÇw%É놊s·›ô÷ëøá/Ø›‹•aÚýϤ>oɉ7i¾ ¤Ú÷;¦W÷ß¹¬Õ‹¢´éG¹éE^é}¾/ö_-Vߣ<Í·ý±‰¿ÖuzB¯7%ç4ç¹Ôýù_VùÞyg¶»À´5åøŠ χ¦¹jÑÙ›4¿»0ŠãvtÓ«—îšÒ¨ï9QªÄtŒ)?ó®ŸxªÄ”Ç+v·åäfé¯7iÞwöúþ‡W{êÕÒZR&JU<êÝû‘Ү닟õø¨êÒ#qW·Ð¼æ‰‚q£ûïЫ•ùÒV”¦WxûÂKtPŠ^÷¡q+îñÈÛëš[ô±…æ{Ç¢v[Ní.2é­úÄmû¢m Déªý7nгø®QVQÃþÁz뇉¿­ñØBô°½ÕÚõ‡²‹õêov¹xOÞ-J7®Ú=ýÓ–~ì’ÞñÕcÑ&Ý|Ãt8D¹Ï^rܵÿŠ^ãä¸,"J üºù„="_©g'=ˆíÁ‡^“Pè`››øšÉÇ5XÌúISž”žšÜò£Ö“L»È#Ë®t¿î[⯇œ\Uó àlÕZþјãSãs%ëÌ?™ë¥Ò/ÏþqüF¶ÜØûÒ½æø¬h'ÿFðˆ^Þ";B¯éóã³)‘L¾.­“é!òïYþÚΗÄ*eƧ¡ÑÇ›×67Ùþf¾^3¤¬á×?¶¥·¾XÙò–ÈúŸÇì6¿MÕ¢^»úÑÇæ—M^8»ÚeÖµe >>7ap#k^G„|úîåL=j›4ìÔoÿnè{À#:Ùô诞Sæµ·Æablø}™¢¬¹ôÞÐ×’‹Ä¿æ8Ø8sMyÆâ×·ˆNr"æÈÐkÈŽe4o"_.w}!lä _‡òD'ú·ú3¿jFþ=›óZøðÎã·Ö5M§Ojyù¤1ŸÂFëÀ#:y½Ç¼ß[¿aõ‡ø×œ×²8û?¨†s<ÿvJ×"±êåÇÇo^ð„%ÇÞ":ÉŽ‘Œzµ­‘+5Q6Ê &GäýöûÔ¸cYóùÑÁËlÖ<ÿã°™ £ÑÁ‹SO¿6/7Ô‡ßJI Êi9éµäDs^ój?yd¤=R”êþàgbe£V_=ôâ»ÿo%:y–×u5ဌù\/ ±‘Wrhî¹¥™båýK,ËÇzr+ÑÁK3úú S^×|”è“A”mû$oH§x‘÷ÙóÐX?šã¾ VMãâ¡Öxm¥ù_MxXãzgÆøý³¬ùÜw&Öqýsý¿¨>÷ò±ºÈ;&ÿŽø [¼4dXtµ•èdiÖhÿݶL{©¦¶ç;~¸mÊ÷²VΛ¿íKkœXÎv¨më¯kϵ<¢“/tîÖ÷k½æJ«_ dá÷ùì&±ë§˜ãd+™þ¬Ã_¢ž¢‡÷Í~}ߥò›ó DYÙˆ©Ã&î±ôÂ{mníÖßÔSt¡g¼7Íwù czÍÏ¡Nù¾…wm®O÷Q¿ˆ¼œ˜¡M®ô‡íûhI³Öü0ý•}s=H»ø´¥O{§âSÝê÷ê¿Õ,¾ñG·¸«Û]è>¶äú[[fêµN³—$Tß4åTÙ¯î= ‚#DÞ´µ£Of‹Õ ì^c]»Mчþtø–÷6µÖ¥µ.Gý'lìdöã<É?‘7ò‰Éz†5Ÿ’ík/Ž¢}mòº„#ÎÑk;]áöycqÞóÏ7ž¬ë.òҼѡÀCØ>ù&¼ß!(¯æ]_7¾ûF¨x½Ö79%}ûTq>êùw?ý$Ñâ§•~Ý›e9 áÃÞv@=5¿úzh„Ÿ~ÐkCÇšß{Ú¾ßíë*½6¦«ëÊå—¼Õ§·Gæˆ|I¥]G‹•~ÕcçÅžqWsi7ùõu ~6ÀšþÁ“¢.ĘrîüG‡¯ý­ƒÈ§u¬Þ‹í”Uoh…Ó¾êgÉ‹\šßÍå5¼õ­^;@ntDŠógîvmöaL¯Œí,lŠ–¡<Íã–”€Ókãµ_F8ç¿Ju<ÞÎÝÒ?‹¿… «|ßôëÈ$Ô£ù|«°­Ü9ÒkIˆó×'Œ:>;T`Ønêå—Äêò#ë_Ü6åi·^““/êµ~Uøp¥¸ IoæØ¿2-|Lÿ÷ã*Vóþ–i§çÒüæönÿÌ#«6ëµJ|‰ íâü¾ú-Ûâ—#Áû:ÙÓM»cõ7{Žø¶ÞøÐ<¿ýò¸Ù}Ò…E'Ò,=zÒ¤ÿ "ß œ'¤1}ÏØkSÿ-y–Kó½£Y_7Ÿ{ôZiþ6;&. ó›§›aY|ð6Íw^Yöî+Ç Mþ¯ŸµºSçuâBúsÎ/nüUä=üT‹±iæ~ËQÔ§ùÝyjÍç™Ñ¿¡Þ±É? ð¾áþf]o‘m?o]¤eg¼MókŸ7càô¨Î(­:V\Xï•1òïE~¨ûï/9lÙÁ5-:Í_‚z4¿¿úeêÏ žÜðŒòÓ»üßÔ¢ ÅÎíÌ}[TÚôê/PŸæ{÷à¿/¼ç»C¦œ¯•ÛIåGÅ…¢s?<ÔIäûI²Ð´?Lýó6Í÷žãû®¿õ(ê)FNغn…—I/ù‘¿|yò˜‹%iž4àÐ|難 [å}¼ÞxËb'“¿/TõIW›ÛÿRë——ßòÙ,òå4½hŽÿjÞG0éñmš÷½ïôvxéØ/Æ~˜^+­ýÈeæþØÚ‡¶ìj©6޾kŽ÷#d×ZýÞNôñÎ+£Û8š:Wû:âÂwŸ}ßqY¦ÈŸ-<.1öµ·=ìË®(ïsøu½–䵸p}lV5ùSJ뾋}B<*kj(Oô°ŸÖKÖ¼ðzûÂ/ _v{‹È4©æË¦œyDJÕÙ_ÑG¡$ãýgôÚäKÁ›ºý&.ü! ݱ"Ÿ÷i*¶MPžèá@vBËÍ&£ü{1‘­#Dy£+6?z'¹cÚù«éÜÁØÇG}¢‡ƒkaaï°øFß@¿áæ<–·}áÏw¿êjÑGÔyc‡˜û-«Ç«Zc]¸D‡¤˜øæq‹>RšÃBšeʃrï¾Eß|wË WÓnZMûøz/Ú?<¢‹w—n˜•d¿®×ŽÈÛp»s®(ï=5fá†Ý-V÷½·cÞÇuqWwÐ|¿7´îµ±»‚ë¾'Ö.Êï›™ßÙ©³Åg½sSŠ=oÎÇêÔBß[î-ôpC®í :x¿Ñþí=ÊÊ ÇåÆÀë¢||qÏ^©?ö¯1ìm^G0¾bu»ÎÿyÓÂÑÇû{=z"èW½vpÓ­‰¡ŠòÌôC_³Ö­rÛóÒY=n_ØÛíûäÛ“šË¦>?Yë²DbÒ¾+FÁ>PÜuöÄdQ¾ê£÷ÇÌùW”‚ÕcW¨ hcaèSÔ'z)¢}7k¾û—#aÍ÷Ó9ÚKÛú˜çF1†¼Ü$n},9½ƒè§èу÷ž™¥×Ò~¿5¿/=90Èfœè1!j¡/V»©3kµƒè¥èöëÿpù¢^Kz[”¿Ú6ܵX±Snï¼½§¶­V¢<Ñä?õZÏ/îµþ¢(ÿÛÑéÖzLž¾Å,«ïU'qWóˆ.>¸(&ø³½:Xå[ƸÄ<ïcÉ)®?¯GúéÉKEsÝX|šGtñaÊÞç¢/>aÙ’¼g¾lÊ©òëm5Sìù=»¶(ßñ‚¹žZýÈÛëšÕT|¸D'ò>b­ãÇöÙ•¢üЇ«µÏˆü›?-tr|]ïKv¹9ŽæþAÑÇG½WNoÔÖ´Ë\/ý5逨9äµNŸ¤NÑ£øÏXïšëô<¢ò>þ~Í,Øï ‹>øýQ^¾ìë‡fýas½|­oˆç/bÕûKŠ÷öź2èàc÷ªÜW'OÐk¾T+Êi_@ì´/Ú6ÿð|”£yþ£öxùR½æœ÷so~ØF”ß8rÜ5ÑWìÌ9Øe¨ç–¾Ë£yþøWµÁf­ûŽ=Úfnvÿ¯ zýó›Æ K;‹kåÁaŠÞÿ¸”¶tðšeÏMµä]>Íÿ'´?jÔ×kÄ“3¾ úʤۊ†ãžZ÷’›ØIôd­Ô1dšE·ùDŸÐ9™^³çÖœï?Û"*œ=@é×­s·÷Öº^½NØn¥Â’~õhž?mát5áY½æíß.Í~f ¨ÀªjòcÖ8WI»$V?§êÑü~*¥àÃ{¬ñÇ!Iæ:¥Â¹¸ÇÆ.Ѱg yðHÃwµ7ç†Zz<ŸæûS²ôšzöyÚ~XT4Þ8æúÁ¿‹’­¾~Z¬>9\J”§yþ¬C“Q¨¢×<òò†Q£ödM¯Û.vÊmõ…ˆÕª[íQžæû³U5O¿{‡^³¤ÙÏ¥kÚXãܨäÌk÷]±Î[j~<ÚtŠe7ÑzÔ’7ùDŸñ~¹Î•³7å5«ÿMjF|ÙMì¼´ZîÜXô —©_œ‹»º“è xLWÇÇ?]©×°}YárúÙ s—ˆ¿/ëûˆvÜ”W¶ñ­Þza>Ö‡;i¾‹?¹wÖ ¯ƒ¿’VœÐÛê­×L=¹«Üˆš£ ý(lrÕ5phþó~‘açÔ Šú>âÜ&S~T4šüÙýï˜òc—Ϭ-ëŠßÐëFC®ï$º8üx´HõšèÛï?r¯¨Ç¸/_3Çw×Àý×¼ð„°=«÷wÌüõhþ—®i £×„Í ìèô§¨H(}yë7/‰]¼¯k#}„ò4ÿG°kd޵Ñõžy—\sÍuU…¤ž.馞ݥ ¬uºnð‘ZÃ~ÙIôq„öy¬u{kµ ÓJßšóeO«ÿêšA{k^˜>Lùµ“èãÈþÛÎ; Ô«å;EEæáõû§7öv²´îÙ}ëÊ]DG›ÆÊ “žª¥º}ºÎš×%•y×j~2ÇÑÎr®ïóºÚÒÓ»ˆNŽŽT zõÞîŽ=?STÑÁ‰Œ¿mzæÜ$½ò e ˆ ºOaÒ§}Gó5[V–èÑŠ òÉÒËvšÿjÒoyM¯TÇ ÷cÞ¶;¸,4×£venÜkÚyÌW–~, :(Qên^I÷eDÅî瞘ZÚÿ Ó·ö!¬ýˆš÷’°À¸3Gê•´Þ;ïÙØtÀæþßý>–uåÆÛº°ÝSöá–Ì{Í{ ßÓ©ìsxÕî×EEÞ/ç½þZÿNOwÿ qš°>zг§L»|Ëas]@tQæéúgˆ^IòMT캿ÅcÃ4a?îµksÝXsüx¾^¿‰JÔ6ÃX½B³ì)ûZuêq#¢ÿ÷Ìö²o_uƒÌñ[%GsÔ«Öº»€è¡¤8}ÔêoÏêòÖÇ ˜ÏKµ¿;ßvoCŽ®’ÛhXôT@ôPrC]Ì1æW¯PÛ²Þ¢âèžb7ç‹zcä­•‰%澿Í{òîQ~8D'½‡Ø´¤J¯àsÿŠÒÒ´{VoñÅŒ[ue<™ }»á•{<œâ®î¦ù?Éç‡÷Ò:Q¯ 9¬Gúî‹WûÎiâjîk´“7ܬsœÝD'稃d}Ùcz9ˆÚÿ³eú`Æ·ЂÞZfòWA×¾[>ï=Ë^7ìÝD''éüKOf»®\^Lj+4à µ=sìys}[Ð;eñ›î‰ú=TþßÚMts’ïEŽLÌ–7Hôò)c=òœÃvDÅÊ`5û_{~–¤Æúpˆ~N¾'-ÑS×-zwæÊ[zy¨T; ¸ÀOûéq¼ß[Àç‚<ÎÂÆ÷+MºÜMôtòÌ+®¯…ë㔹õ ~AÛ§툊k“»<ÝÄÑ„SP&5¶µ®&;Ü¢³ÝDg'Õ¶äd}<ÏÏÚÖÇóüÝ¡1•\6ç« Û­sGH¶"uR8Lgrtî]oÝïúÄmY÷§-ûˆÖ¥¦=QÐíÖ]š÷¶ìÃ=D_§\ÔBȺgFëSU6ì}øbÆ*S¾ø Øã·¯Ì’‹r[g;Ö½{ˆþNÑù€…Wêý‰sçüMT¶¶¬²}©§W]Îèõ©Öa{ˆÞN xpPÍÞZý‚º°HTzô(YÔ5ZôRbÕéW† zßCttj¼ºXcâþb攉Jÿ¦òDÛ´ è‚y.nêÁ=DG§øÜͰ Ϋë=}E%­3DÁ Ÿ8­žç ß;rÈØð-ï×ÃèåÔsò"ÝKúyZ‡ˆÊ˜Ù¯–ˆ6õOÁ~áù|ºÍ’{ˆNý]ØëçG¨!Q9 ä±ËáùÞPÊɧz[|¾‡æÿÉ9ý|׳1•çjL}_9°»Gÿ[—Ís½‚}óÖõ>• 'ö¬¡×÷òüËÛ /êeDw¢òÞÛi/žnµ¿õf­|é¾öu,~ÞËó¾On`×Ëh_ý¨Z}j²(@©„}Ūâ½³v´@yžçCßÊz漕­T ITö_´ëì æúÔ¸¿bÈ+ž7‹_÷2ÐúZ/‹Ÿ)oðŠÊèÃþÛ;/FûêÜÍÚWßËó]š½ÈA/ë /^þ$*£Ô‚KlK“'ŽÂÖË+œ$”çù•§“úê¥7œ ŠyCTöú°ß+ ¢À8‡5èi/Ï+-”¬{àòؤ²÷+c¦D¤Ý®6×ïr»q[yfÕªgsÝ^èlɽ<ï§ËÆî©—>?ôÉ÷N8bœa}.t«"}‚.„]ðÏ«´žÓK¥Yºã°¨Ú²ÓÐwj­qåóèUq›Ÿ\ùÕ¨ÇóX)–ꥴ.•£‡—|ÐÂS/¾SlÙ£„'êó¼^<ÿ~bé}漞“˃è÷DåÔIc3A$W­}wxþ$y4J?ÇçÝ• Š.½“& ®šÏÓ­NØ*z-ÐϬhbÛ}ÕÏgVû²p³¹Þ+81êjqúhC÷÷ÛYŸ?¡ŸµÉ Œõ”¨|.¯Çs©÷Š‚{½íIËî¦õœµÎØÇý©ž:ò“ýì}oM]µoƒ¨|iÜþ}‡ÚZz¦pÚWÑÛþfžãÙÖ« œÖºiÍ÷iÚß1éä¬[8n¥¨Üì»z–%7´|ûâµýÖ=>·6÷I÷1=¸miÿÓ„fú™ê›Ý} ù»õGy¹Û1õ ·©–üÙÇóïáÖ/:c·~fs\ÑŠÊ}Ÿm;\ÕÐ’›{³î ûý¦XùKLó.Å?¢Ï¿¼yúWF]Óî)*‹#§v(oiƒ|°üšy~Á÷L,ûyÓ?ëgäkˆÞ,ùp~ÉŸÑUÝDºÆÙÞ\O­¤}K.ìgú`;îô%×Ü9ó4÷©*/ÝøéýG'ˆ‚?ԛѾXùæ˜Þ¿÷»õ™>Øn=½ýÍ|×SŠÊ» þ®û8±›ð²ÎÉ÷3¨íᮦ¾<=WnÔ¥ŠÊß^ÿ.¬n²Øñ͆ŽsÊÅ*uÍõŒy§sÁÓ='.ïta„¨rx=lÁóëÄn¿¥×†ûœ5ûÉv>êñüÊÛn£Ì}¬S¿Jƒ`¨r|êf¿‹ébwÓäc¼fÞ\Iòõy¾•9ð ‰ï)iž·ýCT5ÌúúãoF[úîòEçÆ½WšòÍ'žÿ†Ûœ¿ûz¨¹tê9yP×ßœ·*²ï-:8)ÐÇ&_¯’Çþѹ–þÚÏtà 6Ž,üÆ,º8îjsÓž«jöbðÁas->?â*o²˜÷?õ™¹>-d=@ûÚú)ÿÏ‚*UmbîÙ:ç=Kî°¼à{W¨Çzà»éKo«Ÿ¤ûƒ¢ÊcŽ§Ë³à eßcÍO!Ëý²ò…É'åðŸ=$ª7Í]ÞÅÂûKÙѧÌû+iÿ×:ç/d}@ûÙæ¾ÏÉuÍjªN=eî[Vé{EöüÐêÛÕ†]º’ÎUõ=ïãäsôªÑ»®ÆÞ¨ò4«i‰ÞÇ‘ïcYöM!ëÿ Û£O>磟 <ýÑĶq&U-pëÖêçVÿê¤ûÔ\ÿ¬P×§°æ»íºG‚uçû}ªÖ½aÍó³¯&¿ºü)«_ê¹£ÞŸåÈ uL5¥Þx±]0;“¦—Ð~™¨Ú’÷c›‚=&^»]ÔB'îꦋ¡»¢ÛÐKr]å«7D.GýÅn§{«þܺ{€é!ôч¼£Íù-I”u‹î‹ŸèÚa®I÷òÒÊýM;ŸÇð˜^ø>ºÁG%|ÏÙ×3rÃñW±›ïGv»qÏÎ;HôRÂë~ƒnaõêÿƒuß zù™i?koˆ‚Òˆòw6šû"ŒW½ñ!:)¡s ýhå7çµç‹êçz>Ú´™‡(àsÑårùÔ¨å‰J6ɾÛúQ–WÕ¯½2½¼`¶%/h_@,¿z}Äëö¢ÑC‰<=™³@?*ÉùÜQ«ÿ›ç¦ÌŸŸfÑE]' ÙÜo[Á÷|Myxˆè¤„ï¡};«ÿ¹_|Qø™õ^ª@=Sj¨G0üåLJCD'%]×Ë›%ú‘—äƒMWQ½sõš€Ša^›=¥&6ÇsyÚx¬‡ÑɉŸžüý€÷›æ|áû*Õ{œœõ꟢àùpêcsžWн>Ô'z9±¹nlæóUú~—T}¨üËºŠ‚Eý×Ç~ü¤I§+xÿÆä×CD'R~\:nu˜~˜ß TæòVȯ¢€îŸ‰þ Òb¯¡<Íÿ ÇqgÛ|;[?§ý½ø™W³FûïÕ·ßÈßu²(Ø(~Œ1åÚÃßß'-㞊ù§’wcDÿkÄg¸#úßÛ·Üÿ‹˜ ò/ƒÇ¹Œýv¤²¿0ô]ÅŒbß÷Èw?9¦²ß.ôÍ å@ãNH;•±ÿû(ö_Hb Ê7Â87ºÌ¾»P¾±üwIñàF¾7”üË7tÙsÛ¤Žý´‚Oš‚V𖱝$ö_ÌqcQ¿y.ûn ã8Sh¿ൌ²bÆ*ÿ]©3s為.q3u[£nkŒGk´Ýé6èo›tö}_ƾï¯-ø¢-ê·~íP¿ðm—˾ï]Ù÷=ú×0:x±ïû5/¶£3ÇŠÍ%ñæŠ6]ÓÙWûïʬç÷í¹£?î¨ÛÉ™ýÝçÔóÛ•Ä>»Ð×Î~ÿÖùž(ó,f÷ÿ.À¿ Ò^èW<û·®]‘ß5žcÀ^7¤»—n€ß ¸yžwûäªc\Yïù=Ð~ ŽõZÇq^Qßi¤}Ã8®k!‰f?äû¡o~è‹?~ðGYà€1 œ;‰í@Œq òQ/ð&Ä8à!?ùA79Ö*àçRŒÕgŽ­Šq)"Q¯â©¢N(Ê„¢¯aè{X*ûC:0Â#<—|ûJŸ,Ò7XOé³år½¸©Ùä[?ð"ÖR­ˆ›3UúÏ•>ÁdŒ±HÔ¬H¤ûø‘`¿@ùKâø©EgKúø•¾YúntÅÚR1S/³/0ŒQ¿"R?2Vªôÿ+}êßÕÁwu°Íá®þwÖÁ’?3ykØgV:ûÎDßT<Æ8öYÃ1ۑ︖}£¼hØ i§ö{Çþ3Q¿à7BùFRC^4öâ4kØïòÁcÎìC³Žc8fqìö›ìC|дд†üb5Keš%ßõ›ÛÙ‡fÇ Aû-ê8nûZŽùˆº­Ò)îc«bŽÛÏ1hP·5ê¶Æx´FÛ­‘nÆ1ÛíåÛ^[[½˜í¨ßø¶³“ïu32ƒãµF?ö£¹–cGºpüH;ÇjG›®(ïjgš^§ðÝnrLIôÇu;¹°ïLà× ´áá±ÙQ×£Žã²gXñfTLv”÷ÄØy–°¯Ì$ŽÇŽ´—+ÇbG¾píŠü®Iì#³Žc°gqüuàæ xÞhÏ;—ãÉDqÌõB޹ÆñÖQ¾ÇMŠ!ãƒú>vޱŽò¾(ï ÜüÐw?äû­¡Øé*f:Êú¯ŒY@&ÅDD¹ÀxŽƒ^ÄñÏ'ùA…$΃Q7pƒÑN0ðž!‡ŒCÆ10BÑV(ê„¢L(ú†¾‡¥S\¯3<Œã—Ûɧšô“¦âÚ Íž‹^À©W:ùÑ—>:#/ãQB*CúRíCñl"+ÒÆqÌ‘îÆþø Ù''Æ0j-Ǻt¥A2NNßË»¸Gç¯4é‹SÅ.GÙô#&Šü·I¿œJ7È?©¥î5t­¡_ Ý*uj}}*u§Ô“RGJ½(u ¡û gè»;ý–K½&ušÔcRI]%õTýøRzHê©cêû,4bnÔ÷Uh詤2cðŸ’ï†l—r\Ên)¯ Y-岔ǷλwÊØúòTÊNjKúVñ¸0WŽÙì?}wÊæØ´˜£F(ÛóÓ¸ˆcjšæp¼,Ìg Ìg‹›‡å[aNZ†\’ØŸ/Ê´®£¸WmãÙ$Â÷öu¿ ´Òtä*ít?ŠSÕÉ•bOu.æøR(×¥Œü÷u~]s)N”7ÊxÛ9î“´…QÞmøý@§þ˜{ÀóGÝÐlÊ .6åƒQ&m„â·P䇗ð(Š×Õýê•A¾‚Uü'i—:“/ÂH´Ù'ŠbQ(_ƒß©¯¤qiܵ'ïÚ“6‡»ö俳=éÊsl§²Ê§~ûp/æx† Ñç—9Î7ò‘vDÛNà/§t|vºÌ¾Ø“Øgj Ç45Â85‚¬iìÇñu0ï‹9¦a>(㌹r¾É1wlä“]Š åc5ã¢f€Ù éf¹õâð ~s”oŽú-â9&O1‰Ó0›cóxá“Á1 Ë8¦!ðqÉáx†À¥5Æ£5Æ£5Òm¢Ø'{!ûd÷¢Xãm×p5`wAºK:ÇØ©ãØÔÀÍ ð¼nÖ‹­SÈqu¯ÛŠKÝ å½½8žúÖ¸unÝ«»L£~”ï!uÊû ßù>¨ïƒ´/Ò¾è«/ò}Ñ7?ôݸø7?ÔõGYÀòG;(€r(ˆ61ÆH"„ü À B»ÁH#Œü`´Œú!èGÆ!ã‚:¡€ ø¡(Š:¡h/,Œcû ¯aH‡F8òËHÈØØ2vOÌS/àÞ óØ+‹âÉxØÀ=¸G`œ{»RŒ$°"+°"‹HôI¢x2.vÆ< cÜ¢j(^§Œ× cbGn4êFgQœÏuû¡l?ÔA;1ñÛHÆòQ2[þz¶~ `©_ïÜ/”D"õ¦¡/¥®”zQêÃúºÐÐ{RçºÎÐqR¿IÝ&õ™¡Ë¤þ2t—ÔSõu”ÔGõõÐ1} ý"uJ}]rg _CwH½aì-þ•ž:⿪¤n¸S/{†0ä½!ë 9§<7dy}9.e¸”Ýõeö2úÍŠs–^/-pq”²5cцqûyáƒþúÙÉœóG´á<P&õP.pñ=ô„߃'sŒï!èOú‚raøf#rG^x1ÅÝ•ñÈd¬®´!yÙ‹bãJs0å"%;SŒ®(ÀŠºL1Ò¢msWÅ®O¥X_Ê.–Qõ¾Ç9vpø¯ÙÍwmæ»6ó6ó?Ë^þïØÊÿ¿ÛÉñ9œ2)ž¸Ò Á_ Q¶a.ǦŒâ¸”èccÔmŒtc”oŒ¹j ¼½ðÍ;£}gŒS”i‚tÔi‚:M!šÆqrà×m46ͤ>€ŒnŽüæ¨ßõ[ |‹t|r9>¹+ǤBº%ʶBû­²86Ò.a¯Ü°[»r|ʵ·¼ `µ.mnSLâ¨mÅ®Rñ);àß.cš“¨j|Ú£NûŠkÞéÀ¿ÚìPÇñ)×X±)]ц+Ò®¨ïŠþ¹!í†ñqC¾;prO縔ÏNÀ¿“ àÛé&ǤD]bŽG‰º‘ßå;£¼'Ê{"퉱òDù.Hwþ]ßå&Ç ÄxxsüIäwEù®HwCºàu˦¸“ÝPÞå½mo¸uÇxu®=P¶訇ü^Âñ%Ó)^”Ò¾K_”õEÚxùNü0&~ÀËuýQÖ?—ÄrÊNÊâ÷@|ÄXa¬‚$u æ6m#Œt°´•_Æ:ズ¡h#ù¡È-!ÑÇñ.1~áh7ùáÈ/¡¸¢*æ%Úë ø½îDZë?íEçi#~o?ŠM&c]ÊØ–‘¨‰º‘(Ûuû¤RÜQï2 G¡np‹ªãx£¨ÛxD£l4Æ9xEsŒKô»Æ£úƒ~Ç$q¬K)k¤•õõë_éU©Oÿê\]êN©+ëï“Ê7ôà¤ëÛÍuænè¸úû¨†î’zËÐYõõ•ÔSõuÔ¤ŸîÔKµÿŠñü‡=Xþþ+cèC·ü•N1t‰¡GêÛÓRÜiKKaìÛÖ×õõÀ_Éÿ¿’ûwîéJY_ã@rþνÝú²ý{÷bŽCžÔP¯>™[Ø…HFÅÎä8qødsü`Àt?4Áœ5)£ØÀÍ0÷Í0¾ÍÀcͳ9ư9Ê´Mµòð[VKä·Š£¸.™÷׫^¼_´ÑcÖ¼Ó>žâøª¾h£ø¨#ÚéØƒëZ"W7àá&åร-w;ÇèE;¤ìC”õ@](¯g‘x;™„^(ãü½P¦+êtÅÿÝ׿{#í~v÷¢Ø¹=\8^.~óÁo>øß¸û"Ï7À÷ÃØù£œ?à`œð[ ø&x÷ ” ¸£Nú‚üPü†ïa“pÔ O§˜Å=åÿ¨×+žb#Êx…h7cÒ;ŒböF{‘¨‰²}P§ü0£€ÊEݤøÅ};ùÑ€Ñ0úa¼úÇ~—)6®¢ùww_û?o£ÿ«Ûçÿ/÷³ÿU÷²ÿ§ìó¶mžÊã4ŽyÔ0ê6¯4À¼7@]9f<ò‹îˆ_L,Þ¼Ó醨ß2£h½QÇyüÆ(ßù1!_œý8v¨ëƒ´/ÆÐi_”õ^~3?Œ·úàzþÈóNh3mHå1/È D1VA€„ü äal‚7ùÁh'¸„DwÆ>cx!€жBQ'} CŸÃ†>„¡|8ÚGýpÔG~O/ŽEözï^Èï…ò½P¾ò#Ð~`G`<#€Ooà×{-Å DÙHÔÜ>€Ó'ƒâ «ô·(Ô‹*$•ÑóÒãÞe£Q6ã 8ÑewsÔeû¡¤cƒü)#¤,—e‡×¿ãjèÇ¿²Á¥¾û+;üNüNû[ê¥úºèN½óËÎþ+›ú?cO×·£ëÛÌe'×·‹ëËÚ;ï8üG¶°”£†ü}5À86À7À9bN¤ý‹´Ò ñ#Œ]#)ï@΀ÛcÓ°š¢ŸMQ¦h¢™´w1¯Í1ßÍA“-0÷-¤Üì–€Ñ 0\ï‚ï. •v˜§öh§=Ê·Ï%³¨#àtDÝŽhÇÿ»!ß m¹Iù8î€ç^D&S'Àë$eêx ¬ðêìE&”'òº n”íZCq™½QÎå½wwÌa”í¾÷@Ú°}ð¿/pöEž/Æßùþ¨Z -ã÷àÜÑžÉq¸ñêGH4ŒG$ò#ñ{ŸL"Ç(É3è{_àÕõ£ý(®v?Ôë‡>õCc€[ŒÔ•ÿj¶ÝÝý×»wþ•öbÁ‹?àsŸñù ŸŸñ¹Ï/êƒÃ¯øÜÂç7|~Çç|nãó'ïßâ k; k; k; †Ÿ†µÖ˜÷ ›4è& ºIƒ¦am§am§µbºJƒ®Ò°¶Ó°¶Ó ³4¬í´ŽüþºKÃÚNƒ Ò`èhXÛiLZ~›…µ†µݦam§A¿iXÛi¾ô6Aÿkà ü¯ÿ5ð¿þ×Bù..ø_ÿkà ü¯ÿ5ð¿Ö‡ïp€ÿ5ð¿þ×Àÿø_ÿk:ï]ƒÿ5ð¿þ×Àÿø_ÿkCx?ü¯ÿ5ð¿þ×Àÿø_»Ÿíkð¿þ×Àÿø_ÿkàm4¿ýÿkà ü¯ÿ5ð¿þ×Àÿê]ø_ÿkà ü¯ÿ5ð¿þW÷ŒÁÿø_ÿkà ü¯ÿ5𿺟þ×Àÿø_ÿkà ü¯ÿÕ¾<ø_ÿkà ü¯ÿ5ð¿þWkð¿þ×Àÿø_ÿkà ü¯Þ­ƒÿ5ð¿þ×Àÿø_ÿkàõ¦ü¯ÿ5ð¿þ×Àÿø_ÿ«;Òà ü¯ÿ5ð¿þ×Àÿø_Ý­ÿkà ü¯ÿ5ð¿þ×ÀÿêLü¯ÿ5ð¿þ×Àÿø_ÿ«u ø_ÿkà ü¯ÿ5ð¿þWoîÁÿø_ÿkà ü¯ÿ5ð¿zþ×Àÿø_ÿkà ü¯ÿåýn ü¯ÿ5ð¿þ×Àÿø_ÿË{Aø_ÿkà ü¯ÿ5ð¿þ—ç!ø_ÿkà ü¯ÿ5ð¿þ—{mø_ÿkà ü¯ÿ5ð¿þ—k6 ü¯ÿ5ð¿þ×Àÿšäù'õŸÔAR¬ey&aJ¾–¼%é;ÝÁZ–Lu®Æ÷Ö3ù-¤lyßSîù©w‘I|~“Íg8e|ŽãÊkÅT^/fÓþ |/©Îu\ÙoAï®å5d ïºòYO*ß‰Êæ÷”e|7ʋז|ÞNw¤”ÍåÂ>âù库*÷ •¿g~£϶™ß]³æÂ÷哸œ¨Žß_†ñÌ,ö…PÈ÷æãØŽËä}Ì\>;ŠãûUY|ǪÏø.}!߇uà;õñt¯^ݳŠçýεtßJ®WÕ[ÍBZ«ª{õ®|·>ßMÅñ½Ÿ'ó™’¿Ñtæ{õIü¶j-ùLP÷êm|窘Þ[©µ«+½×”w¯Ôþ© ¯a“讽¼“+mMu®äÌë×xÚSUk×Lö›PÌw¼ÈþTw¯üxýjã³¥ÞgµÓ;.u¶äž2莯Úo]Kw|徫ºkïJkYuÇ>žlWu×>›ïa]æ7›.ün3•ßnÚÉ‚ºs•Éw(ì¼7ë@¶­z§™Îþrh «î^yñ±\º[¯öj]­5¬ò“°†}%”°=ìBï5•ß­(æs%g:[RëÕL^³ÚiݪîÔ»’Ý,ï«õj¿9s¥{Vò>²ÜÛUï2 ùŽ•Ÿ'Åó]«l:WR¾ØæŽ£ý_õf3‡î_©w›¼®ã}á5¼¾-æ³'g¾—Ïwðm|?«ˆïh9ð}ü$¾“¿†ß¼‘=¯Þ{:ð¾rßáÊdÿ …´×¬ü08ó9ž÷×òV1ï?;óš9žß†®¥{^r]ƒñ‰üXŒO,Æ'8Æ¢n,êÆ¢î@´7åb<ÖI?8BŸ#o0ò#o0p XC7$žÌýx|G¹xô=pãÑ·xÀMÀ' ½´—€ùH@ÐçÀODÙD”M‰(›†ÿaÀ8ðŽzÃQo8Ê ¼áhw8Ê$!/ yIË$à•¼’€WòG ?0’#ˆ%c ’1ÖÉh#õRP/í¦f `¦ ÝÐÓH¤G"=é‘HŒQ˜‡Qhc4êŽΣs*~OÅï©Àk ú6ðÆÞà0uÇ îX´3mCÇ¡î8Œ}ꥡlðIä^p²ã1nãÑñh| -s&ÿ hcpŸ€ù›€²¢½‰¨;íLÄøOB™IÀoÚ™„v&£Ì”™ü¦ îÔ‚ºSðû4à8 8NCzÒÓÑîtà8u3Ðn`f÷ø}~Ÿßgæ,´1 °fÖ,Ô…º³ÑÆlôeÒsž‹ô\ôa.ú0°æ¢™À+02#éùèÿ|Ô™þ/Ä÷…ø¾8-N 1 Ñö"ÐÐÀY‚ß—à÷%ø} ú¹8,•ú¸Žõ÷Ýs•»kï»kï»kï»kï»kï»k·fø’:Nê™,–—Å$7ïJþ‘4œáðþÿä›!uG2Šße‘ï y_RúTo‹\ùy*ŸÏæÐ­zOîÀ>Šâù]¹ß–“ù ÏaÕÛ£8~”ÅoÍ é’:Çõâ;<ét^éñ¹®3¿Aç»—Ùä×HݯwàwJQ|3“Ïz é¼Wù:rá÷éIìwp ù ‘÷3•ÿAW~Ç”Jw…ÔMW¾3”Î÷†rø<8Œï¥ó»¦lö‰TÂoØÓÙ7a.ûFªáwN9äI½sr¦7©òÌE¾wRw8“øœ8›îÿKÿ$Òçˆ|ë¤ü:ð›Õ zÛ®|Æó]Î5t'I¾mWçÆÎü¦Ý…ß;¥òÛölò—¤Þ;­¡÷òm»¼“©Îw¼Èç‰<;–otÔû§0>C¶ñ»§">;v¡·Oòí’ •¤µä'IÞëTþ ]Ù‡Kû(´Ó›vuÊ…ÎŽ•oÂ,öOXHgWê­“û%Ìfß„5ü–Ý‹îsª·M6~Ç^Äw9ù®U½‰èCw®”Ÿ$göWÏçÈkØoa1û‹qfÿ…ñ|¶¼–üÊ÷Åꎖ ßÿLâ·Qkø}E1¿±pæwR©üVj-¿‹/&¿3#Jøí”û]Jâ7T9ô.CžM+L.|æ–ÄçÔÙ|÷«„Ï«]Ø7b¿¥Ï¦û¤15dÆÇ~,Æ'ã cQ7ucQw Úˆò1žó à38BŸ#o0ò#op™úC7íÄ£\<¾Ç£\<ú¸ñè[<à&`ŽÐ^ÚKÀ|$ ? èsà'¢l"Ê&‡D”MÀÿ0à?xG½á¨7e†Þp´;e’—„¼$ŒeðJ^IÀ+ ù#Ÿ É€‘ Ü’1Éëd´‘‚zó0)¨›˜)€™‚vS@O#‘ž¼fâ3ýÈ@½É¨3ù£ñÿhü>8§v*æ'xA߯Öôc pƒ²ck,Ú‡¶Ç¡¿ãPwÆ> õÒP6 pÓ0i€—\Ç£ìx´7ý|Æ£0ž€ÿ´1¸OÎPöA´7u'–ÐògÊLÂLB;“ÐÎd”™‚2S€ßÔ‚ºSPw ~Ÿ§ÇiHOCz:Ú§£nÚÍÌ à>¿ÏÀï3ðû Àœ…6fÖ,Àš…º³Pw6Ú˜¾ÌAzÒs‘ž‹>ÌEæV&pÊDýLÔ_€üLôi>ú?uæ£ÿ ñ}!¾/N ÓBŒÅB´½4´p–à÷%ø}I1-É–‡¥ømê©u‚ü»ëgìŸëg ó¦ÖH˜µ2ÖAÆÈXûÈu±Ö‘kc=#×0õ×,r­rçÅX—ë¹îk¹Þk‹úëŒû?¬Œõ€\Lu°ì~iïK;_ÚôÒž7ìxÆ—¶»a·K]ÚåÒÿŸ²ÁÁÿ`×·½ëÛÝ†Í Sö¶akv¶acKûÚ°­¥]-mjÞß);ZÚІý,mgi7K›YÚËWØNþŽüUÊ;Ýê.]6ßÍs¡7–ê>^ù£”>žÕ{Kgò)ï×Éw•òM‹¼û"ïÏÉ·ò µ¼_-ߊÈ;0ò>¶|ç­ÞsgÑ]né¿GÞƒ“w\¼sÈŸ£ÜÊ’Û[ò-¥Ü¦R÷Ù¢hkKùc #ÿ Òw¢|ó(ßBKÿ ò~™¼C&ß´(¿ÅIôF[ú~”oYä;KùîZúQ÷ë¼èM¥|g)ߣÈw'òm‰¼'ßVË;ÝÒŽ|O"ï½ F½Á¨3} xCÑ·û0ŸCû}À{ÒCëè®÷ýR‡J½ øƒk0` ÆÜ'ëßD©çP/ðî“i”†þ CýDÔŽ> G™ûQþ~”ñð¨ã-3´1¸E±øm4Ò©(7Zê->ß ©ÇÐî8À'uÚ˜Š²Ï<ÀË@ý‰øLÓ?¿M†œŸ‰¾ÌAz~Ÿ‰ùšƒòI€1°ö”YŒz‹1^ P'øC™ÅÀa2æp2êOCzÚ› ü'â÷iø>ßg¢c0fsP&ð‡ôDà¼@¶ x3ÑŸyø=Câ/ÛBÙyøm`,F»RO£ø¾c<m/ €çÀXŒögÞ¤­¿ÅH/À\-–z ø/@ûsd[h3éLŒo¦ì'ÚZ€6F&æx±¬ƒ² Ðîb”],óð=}XŒ¶ߤíÂ%ÒKg à,Áx-‘: }_˜KPo)æ@ûï¼—½»o|wßøî¾ñÝ}ã»ûÆw÷ïîÿs÷ÿêž–Ô· Y¥7l,ÿJH(^”üÄt±–û'q¸Éï0ãÈŠº“žCo2Ô°O/öC•Î1dr)ŽŒòGå̾G3ȳŠ)“K÷Ø•Rg~ŸN~GåÝö†9äã^ùVq`ŸUñäûYÞy—¾«dÜ™&9üþ'Œ}ßgYoô¥?yùN_úUo²8Mû.u%ßQʇ`*År‘ï÷å[yéÏT¾!U>ò½ø-:™1Ò©ô7¨Þ eð›¡\ŠY£â“¸OJé ZÆ`‘ïhšÛØ7aûÏ·“ÏSÇÆ…ßù³?ö‰•Jïˆäzå÷ÔÆ¾O‹è®¾òZ̾ôù]Q<½R~O3ÉÏ¡|W$ýÐ+ßX.ä¿PùÄr%2*¦ßñ›ÿµüî¿„ýc9³Ï™"ò9£Þx±ýtò ÞþslW~JïTlûB-ãwFaä__½}ªç[¿ýd9Ð;#å{Ñ™ýd¥rŒ›ò¿-ß½+?Y5ìÓÆâÜH¿ŒÊ@&ûÊÊ¥7GÒ_–ò‘åÅ>3Èoc÷"òÛ­ÞþÛøQ¿?p!ßúÊÿi&ûó¶³_}gò­¯ü:²o,öyO~õ•ïÓl~Wë@oŒä{ZéUù=]Ëo‹Êø}‘+Å´QþómìC¿ˆcÙ8P<å;?‡ýç_f_X~ôWùÉYÃ~°Šù]‘ ¿-Jå·ÿ¹ôÆHù?uaŸúIô>BùȲӻ#åÕ…}ì'ÑÛ å3«üf©·HÀ÷þ:~ӛɾ÷íä/Uù p!<Òÿý(?y#PpR€{ ÊŒ(£wKCêØwO.ùT•fòÀ ö/E¾|ÔûŒBò¿5¢†ßj¸²ÿþTöÅ•Co‡cÐÇX´‹ñ‰ÅøÄV,pŒEýXÔˆ¶¢lÊ NÑþ à38Bパ?ùƒ‘7m A¹!È‚vâQ.ßãQ.}Üxô/p0Ï h/í%`>ПŒIà'¢l"Ê&‡D”MþÀÿ0à?xG½á¨7e†Þp´;\Žò’0ŽIÀi2æ! x%¯$ä@~2`$F2pKÆ$£L2ÚHA½ÀLÌÀLÌ”2ZnŒDz$p)Ó€1 0FÆ(À |G×Ñ2$¿§‚fSc ú6ðÆ c€Ã˜2Z¦ŒE;ãPoú;íŽÃا¡^ʦŸ4´“˜ihg<ʎǸG_ÆŸñèÃŒç´=mLî0PöA´7u'¢IÿI(3 ã0 íLB;“oÒrf ê-Fz êNAÝ)ø}2¦ÇiHOCz:Úý_ì}XT×Ú.)*–**Q¢l¨ˆF…=ô&Ò;ÈÐ{oƒ bÇ[Ô45ÅcO6*TìXb°D &±$&–¨ÿûíµfoŽÇsþsï=÷žsîï<Ï<³÷Úë««}kÍÌ÷ÆCÇxÐ&@nB3Û&%¢<å‰(OdÈH¿dðJm2hS G lIÅ}*îÓpŸÒ`Cx¥WôÊ ðÈ€M™°?4™°?×ٸΆNÙÐ)»‰ÿ×y¸Î‡mù›OÆPÌO/Z`Ÿç þÇò£¯Ê{ÚÇ þê¬W»WÑîE´{øUÚo´=ßÕî´{íÞ@{®K{Úh÷Úx¿mŒO±=ú•·kãsí¹®6ׯÞwkÏvÛžëRáðK8|½Ãq ~áðÍdÔ¦µ õ’è6G‚6’äÀîTȈ†}QðidEãy2ì‹¢5 ´1 ‹c¨uâÀ7×ñÛâAºTZ‡à»TÓ ê¦Aÿ ØŸÚ Ø »3aw&ddá>÷Ù“ ÿf£ýr odæ¡,ey°+ºæÓœHó4½žŸ???~~~üüüøùùñóóãççÇÿ>çÇÿÈïŽi}¥5ŠÖ‰>ß5³q/=êÿA¼¬áö´pœª¡ë/c—²\Pn•.Ç#wáøUKy¾í&–s[ʃbÁ±Q38>jÃ)—ðôy¾(žÓµ„çu­å¹]uy¾n/Žwµ”ã˜7qUCžï5ˆã™—2¼A)¿ .Ï-¥âØæÅ[µ–ç`Ñá«*žoJÃsNUð¼S:sÕ…ã'sÜó¡<_KËÙ"á(4ñ|á^ƒµ„åp!ì,¨‘òRe0Ïç£â˜ë% #‰òÔJ9~txžIÏY[Ìñ*xîZž¿VÅ1Ù‹&,åËš­Áßþ±†¬ákè` ZkÐZC¾ä9 ¾ä9àÞ:;BgGØä„gNxæ„gNhgðrÆ3gÈqA=\» ž lw_Øî¾n°Ç òÜ Ï íá{Üà7🈺Qw"t˜ØÂ¶ ÐßúO‚ž“@7 t“PgÒ.¾}€\/<óB¿ñ‚/½ —ôò‚^^×Ù¶Â<|ÀúùÀ>ð‘døÎ<ýÀÓ<ýÀÓrýÑŸüqïÝü[ØV$<À#<Q/:Bç ”Avxö`ð †ÁÐ!´!à9¡  …½¡ ߇. uàOä„5³mM8ê†Ãoá°#rÂaÃdøs2dO†ŒÉÐ}rÛúD@^$h#!' ~ˆB(ø r¢*ØV(rbPÚÐÆ4ñítŒƒŽqMl›¹ñÐ1´ ›ž‰Ð=å‰(ODy"x&ƒW2x%ƒWrÛN¥@Ø’ÚĶUi¸Oƒ i°! ¼ÒÀ+ze€GxdÀ¦Lè• šLØŸël\gC§lè” _ä@vdç¡<òòPž‡ò|Ø™òQ—öFK/ÔŽS¢óŸS‚±!íÇhF{°§÷[ÏÚci÷UÚ}”vEñ›vïôüÞ¹íþ§í^§íþ†ö2Ú= úæ_ì](Nü{{í>E»GÑž£/üÅÞ£Xç/ÏÍi?A{‰§÷Ï÷ÿÒýCWø¾[óQ·b†ãJ¸f„a@˜^ðM}†ßõª!Ãâ–0vq,® Ž0”å=%<Èj¼KV-åý¤¼‚„S+á ¼ßR–O›°´% 3ŠñpÝÙ‹ábNj— –;›0Ä)¯¨6/8a-PÎAý†7ÛÝ‚åpí>Ðu š9†‚ ËÝ+ˆå!´0dØ¥½À×P—ãŠepÌØ2Ž'º>*ž<åO”p®sÌXÇ‹5æ8 Ž%F±,ÚÐ×ý y.Öމ Ï0$,X´“ ìjX~U Ûü†¢Ž+d¹îâx•à3,ã/q ¤Ü¨Ç1ì.'|ÂŽ@ÈsE¹k3Ë îÚÂp¹è¸’pV'Zp¬ž†¿%[¢¾'äECF4ÊÇ¢m|Qæ :_ÐûB¦?ø{¯g9MƒW êúC÷@¼ßÂu ì D{†P½Ç—pœZÓA ݬQ×| K4žÅÁÿ‘¡¾3ìtFܧ¢Ü:e¡¾tN…- °Û r'âyxNŸ$ÜOBIÐ7÷éðK:ʲÀÛõüð,våÂ&?²<ýñ™‹çè ëÙ‘l®ƒ(ÖÜ`È Fa0ê£~0ê‡@~(t ¯PЄ6±£Û0Ô ƒ^a°; rÃà¯pÔ G†Ã'áè7áÐ->™ ¿O†ŒÉÐe2ôŸŒºd?h#!'>ˆB(ø* r¢ 'ubP'úÅ€6´1 AytŒ#â>÷ñãA›¹ à™ÝQžˆòD”'‚g2d$ƒW2x%ƒ6´)‘B~Æ}*îÓpŸÒ`Cx¥Á† è•à‘QË–ûLÐdªÙQs6®³¡S6tʆ/²!;mš>y(ÏCyÊó`g>tÈ'ǃ—ŒùdÜæz(›ß¥×ÿÊ÷Ï¿‹xþ]ÄÓßE<ÇzþýÃÿî÷ÿÌïþo|ïÐö;‡ÿ´ïŠuþ­¾krtƒ¦S)Ç$ ‚N/›N¤„Wƒþ×ÅáºtÁXér‹aé®—„fÈòywE?ê:ŽáÑvuáØº Ÿ@o)ÇÕÅ<¡‡ñ®×Ä0v 3Œ°¡/˜Â7ÝПº©8Ö®šáÝt‡nÝw1 ±áàѽ™áß6å 7pá8»e ax»=Á»'ôìycˆ3¬]ÂÇRÌq ¼æ:aXõF™ t1,ãX»Ðϰ…cîê°\ã”[¼tèÓ̱ÄPnÞ}ñÌ(cíÂNÓžs|Ãô’pÛ-8–ÝC^?Ôïþf% `€šå 'œZc}†uK¸îÞn1Ç.Ïe +Óú›@ð2I`x™ÏR޽~ƒ@;¨˜cî‚vhC†Q;„Þ°eHÃÛÒ°x«€òš›â™i3Ë'MÂÖ%œÂÈŽûº GS€ ²-KNê˜]gXbx9ã¹9ÚϼÍ+ŽáHXºÐɲŒaê~á8‚#žy¸p¼0cŽMÿ©ð©‚U°OûTà­‚}*Ш@ã…¶RA– rUÐ_ž*ðt¦:ÐIl¡·-øúÃÇ>¨c ~¶e ?Ú¶¨k ^¶ÐͺÙB7[ð±…lo±°Ä´vÐË:Ù¡ì ô±½t²;ð°;ð°ƒ;ð°{È÷öÐÁöØC¾=äÛC¾=èíáK{ÐÛÃ&{ô{Øc{ìÁ#åAx‡€·=ìñ%÷Xˆä¾¸w A'ðv#èɯ¨WIà‘è›»RP/6¥À¦ðHðH‡¼tÈNG½tø84éðs:dg¡/e¡/eìÏÿ,ðÏÿðʯؗC{ðÊEý\Ô× \š[,Ä-€žàQ OŠ¿èÕv_ó¬ý ícž•gâ9îÞÿ߸{mãhmüL±óßËAñ±6.~úwÔÏ:C¦ø·mÌû¬³dŠuµ1®6¾¥¸VÓ¶eµ±«Ó߈UµqjÛóem\úô™²6ÕÆŸ)<Ö|:¾ü[97æðøñS†EØ}„‡$mãïq,mŒµ>%ßÄ‚ah›ªðöâ¸Ù7œb’¥ [Â…>f賆¥ ÃÏrÃé#ì!ÂCé³e. WÐ eV¨o•Á¶ôý0ôƒ,}ŽÍ¾6t_Û@¾ ÚÆ”Ê ƒÍ8†jVÌpmÀÇr‡”0ÜS|Ú,”ÙTཋá|Û Ú`>ŽûátMñô³¡öš£¾`Ìð»ÍÁ[€ Kú„L6Z¡ZF ÝA㹞;Ó'ä à%€VÀüæ‚zÎà炾*ÜcG*Èp‡}*ØâA± dyÀo*øÄÏróQ/ºäCNþ.6•FÀ¦ü v,“™ùx d‚W>žEë²é4 Ÿñ(‹…]±´žÂ¯ñ´¡-’hm‚nIÐ;²RЦº†¼t”kà³”å@ÇLðÌ„îðÑÐz=4°OCk*h5D ýì(î²`û-éõü÷ÃÏñ ÿ¿þwýÍðÿ«ó:ŒñéYÝÒ9Ý¿âwÂÿèYÝÓ¿þŸðÛà¶çtÿÌ3:ÌPÖ c¶SÃÔë„qЙ޷8F(úVŸ¥ «² úIŒƒ.÷Ø2Cø›¯ lÚè ŽJŸhÏ®h›®jŽ= [õPW¯”á‡êaëa<ê—ätÓçXÔ ï†¾Ò üº¡ýº%p Qè×ú3<ÑîЯ-Ç…,È2€,È2€,èÛó:[æú7qkèß ü{¦—ŠcþAFoÐõ†œÞh¯Þð¹!ô3oCèg†÷¾¨9êô¯>- ãZÂEy_ð5‚\#È5*Ḣ°Ó:›S¬êÂð¤û©>`?\[­gšý!£?dô¿þ÷86xƒ§1nh8&6xÏÐm@-Ç$>a&Å ÿZÂßA D±8ènÐ.Ž#ŠúCàG~°Ÿ!Ï♟›£¾iø6…¦·ø²ÞÂp³%,QÜ€/G3\Q3ð2GÈ3C=³[|é_Ã0µÍ‰7ÊÍQÇŸž[â™%t±Ä3K”[¶°°À2FÃöÑàïBñ/d¸C†;|1ú©Ðn*øPEñ/lTA¦;ÊGº†^žxæ ~*ðWÝba„-xÛ‚¯-hlñÜülÑ&¶,Œ°…lAo ýl¡Ÿ-ô³EÙB7[èd~vÐÇôvÐÉí`G±2t±]t°­#>(–­#}‚Þ:ØC¾=äÛC¶=dÛC¶=ÚÑò#ð€þöè»ö ·§k²2ìÑ&¾D ~ö°Ç¼|Á' å( ™¨AöƒÆ厨繎·X¸äÝ¡£3ê:Ã6wÈõ€,gÔu_Wèä \¡“+ž¹’OЯ\QÇöyã™;ÅøàáýÜ!˲ü‚²è«Mô l5|«]&]ƒ¿üCàO5ôSÃ/jøE ¿¨Á3:¦ƒV ¾jðUS=è =’ C$žEÒþŸÑô ¢Á/¼Óé÷ú{ç\Ï:¯¢8m¢Î>ÕöÖccÚ~îM]86ú©)Úf8æ 35ÃÎ5wa˜¾ýÑ·Í¡“¥Šãj£^Z‡Pfİ­PfVÅX§a³ø˜ ¯›ÀVÈ´A=ðµ ­í¨7mm>6¨kCŸhK”™áÞ”î¡çpȶAû ǵÍ-†½M]Uo3Ô £ÞŽàm…çôs6 à%P}ª‡±(К¹Âu¶=v§5|<é =Ýáo|ú ždÚ‚§-ì  7ø…àyt LgºÇs;èrw¶UŽ€}(ðKhC 3õP×¾ñ 5 zEк½"P/ º{ ^,žûb¬ÆBoðM½?®}Á?ÏSˆô €~°#ý0Ÿæ`Ø¢†™ðS,êæÐü„²LÔ‹…>94Çáyx$Ÿ¼Ó!'×9¥lˆjPGƒçøM³žŒcßýi7Gÿ§ßóÓoå(? ývŽÎ·"ÕÊw5„ýE¿§£Û“*ØïýøFÆÇMáM×#­µŠÝŬŸÔuéÔdýÞ;ò¸9§w?ËÙñMÁ@ÛžÏÆuŸµTéoë’VÜYºYÌÆ¯X4®ÌmGû]ʸ)fý¤.¤ÃW>¥²žgoŽ>õQäBoÞ¿*¥a nÝ<·]B¿Cò8,’ºùKÊüPÌúGk?YϳǛƒkëFÉý®²ïÃï;êeÛœà÷[«÷]÷¯/ ä÷\Oðcý¦þ¥Ê²µk¾ÎîS-^R&·w¥IÑùúŒcâÖk3fÝï£íÿbÑÉûŒ+s׎sðaý¦ÞnëÑ7í …³ŸM4ŸUè&V±'Ûì®ì¯mºNý×½°KœúyÙW˖ǨZg°~RŸ{¨óŽŸã…³Kº|øáè`Ðyí—Ç^²þÛ½:þ­Dq›_@ÇúKýÒ(œÍ~²ù5Óór©dÙíÖÁ‰6—¾?µ²ñ AÜ6>&Äh³®0f}á˜a³Ç‰Sk¼pm&ø°~T¿]/nƒ×`µ2Þøü,÷‹¬_Ôÿqùh£¾¬·pæg¨ ò½öE®XyfÖÊœU_ˆe^îùÊVqŠúDÀÈŽhÇY¬ý ]tí¥m»„ÓÒôh)VþpµÀiÂC±,oíÃNnâ”ǯÁóËQŸµû!¡èçe ÙÂiІ"ãÅÊ›%»óçd+í¶"g_Ò´Â(>VÅ8šüzÖî‡|}—ör…ÜoN·›Ú{æñÙbå]³á_Ž‘ÇcÙ‡·{¼µU?ò¼2›µû¡¸!EŽ ûd?:¹·ÝƒŸÄÊß¶ì‹ó˜ –}ôýîqïûhçIбö=”½ªiÙÔdíü*œÚýQÞw›o*íûKû3ãzöQìù¼j³s_9žÕŽCƒ/k÷Cù–oW½´Ú—«„ÑÚñ‰Á—PþžÀº½;øª9ß3þ™î §²FMœç-Vqó_òùa±lO~ƒi„XôÑÂSQù•¨ÏÛ?ëf§ÞðNMl·\\˜bמëk÷$Õ+ãr¿_Ýœ[éÚuô¼?Ñ@¹#œ°Êç› o(ëÐ牱w‡D(úWݰi¤0Í+bÑj½Âµíõ•ñ4›÷_jÛû$‹+½ÞMÎËX·A§ugŽ;.:&ŒöºyþÿÌæýÈ2öø»é¥ÂɃ¯X$ ï-V®ðYh½D,k8:Ð-j Òïæðþ2àÉH—ëŸËãò$‹ƒÅÊU/u1yP®Øu8ã­‡Ó–Éqafî’«|Á‡÷=ïÍï=î'·óIiöRâ¥n[LÐUü|Êcz×ãÍʺ‚ÅÃ=¼üx¿Ñ¹º­ñbµp‹LòÛïc]š½ðF¨™¢µó¸/ÏáóŽWhG!œ¤ÒìTežhJ¿Y4ÐFÖ£œÅµb¡ól½#3@ÏúKãŸØ?N\s.ï«}Š?šù²~¶<ʇ,³™á¨§Ý§ˆ……·¯_Õí>¬ß4žxôëÇåv>±£9mÿ´½båŸÛo_Ùî&ÛS>ö×E~oìÖŽ#Ðóõ„‡4-öþI¶£ªÃƒõ[çŠåo¶~Ü¥³¿³ýèùz2Ïï×üЉèþ :¾xAwU;Š+;…¹-ê)ÛÁǪu._Oü.LÛpÏC8ñ–iMÁg_ËñdÕ‹ËgäGÿ øÓŒ|;e~ÒÆÓsù:b4³WuOá„~ ­€˜ß溟W –kîs¡tµX”ÿÍܨÏã :0Z-¿n@=_¬¼º®]ëÀF±»8³EÃÄ¢†©¶96«PŸÇ‡¥WîÏÇ÷n©XÓy ÒþgÓrŸ¨×*~ïßeôþÒÍòzZt7vàÇãZ¶:ÌŽ/]µwMÍå6qÀ•:»Ë»õ?¢Tö;‹VŸY ><žØS2xÓ’íòüzœ¢hÛÎbåÉEKnöÝ,–¿|ÒûÔ¢De\ÍåñåuÎí'hðwz`®Ì;g¾?[½`…byÃq’Mér»¸SÉçñ&uÓ®¿ ¼3¤ièǘ7^‰å=T“>-ݯ§Ä‚n.ÖOWµÎãñgÂ4£*úëþüƒ»Ë ±²õÇ_‹åß¾òóÐÅbAûƱ䀎Ǚo¾:Øö+SYî±ëŸ¿võ=Ð݉*Yè¦øOšv!Avú1cÐóö§c%Q8öíÂý‰Æ‹•÷ç ö×ö[yüO©Ûvñlagб~Pÿçà€/.¨„cKÔÅÙW•v{tçîh†"·0ÇàÓc_)óÇ<Öîõ¿ì|Q=b™p,|qAÓgybåca̺ßÉóq9i‡Î‹QŸµoýõ©# ç¿"³ø“f¥¿ÝŸ8o^€XÎöùÊ|ç£3õ„íгv®—ÂÍÞ±.ýk5)@ü0"{вÊxžÇÚ±~£`Úi¯£pô*)þ·5›æjíQµ²c=¡>ãÒþaÍ}…£ÒŠŒyïc‡èöjEêf>ß >k§úÑö'’WG×Ü>þ³Ï4e:tröp9Ž‘ç™¿Š›KX{Õ=ºðÍRÃkÂÑ̺Œ?v·(~ß÷í”Y²ßåø£„µW݉w ‡½k)Ôí\ýªåb9µuå<±pÃeß?ŸA=Ö.u_ך¿Ð\^gŽZôˆËRô=vqf\¾2.¦ œ?Ñ¡H>×) +j^óe«²n–°ö«{_ÚˆG{ÍþbìÏ›ÃÌë3^é'3¤ƒ qʉÖþT:Önus\6î{4QîßMRó¿ŽxkGÿK¯­Ë—Çôµ0M‰GKX;Ö¥û¹—o{KhjùµÁ&ÐO¬2Xc]ÐÁWiŸ/~—©~GœRìWa¤jÏÚµÎwpïýßlšÜ»ýãÑb•Õå°P·ŠžÒ¶ÁIœò# pSбö­ãór“4M«&–ÏŸ¿A™ÿ©Õ}z*ëð|Þž/¼µü“v¾BÓô»´ã«Âv.}¯è‰ÁaüËba›w@ÇÚ³VšÎÌ…&õˆ¥3\–ç­ª˜lçþv"–~>O9ÿœÏÚ·–v ¯'ÈíÛ$ì=ýåK¯)ë^\ÖÃõýnŠåÛ5vj cíW·4ð¨ÆLhêGù5e}ŠÿIýíOŠ6ÄNßñîežÏÚ±v`gÍKNçåýë‘{õ3ÖÜ>!¯ûUÉ7.ÆvrËW=˜‰¡®ì[ç³ö¬ù)àä‘M‡åuþÈÎé„L¬Ê]5§»‰¡â¯y×èäR,Œ»o>p­ªukךŠý¾=jœ"Ú•K©./ŠUs»= šs\éÏ|\LÁb=9ô¬}k˜ù.Ó–¿5L¬úðK³®/‹åtóê=±àà—¿P'ƒŽµoMì —‚<' G^i6?=ÙXGU»–è&¯ôÇ–íû‰š1=Μþ1èY;ט}ôúƒS Âᆇ=¿Î¾¡Ä§zÝKê8_ñ»–š‹yRøßC‰ç°v¯¾µÕª‡çÌ[ñÝêßÅ*©»M˱ ù¥å71†¥ëÔgí]]n”ÚO‘pغrî³±ºGÇš'²Äò%¹C[êÅ|8ÇôÃÔgí[ÕõúQ=¹_z°ú⨯#d{«G|ä²gÖ]­Ÿåó0Mijæ¯QÑÊ<·€µw5‹_„CômD°Z¬¶•¤´³Ôýr´ûr±à]z!žZÈÚ»j§ØK“pˆ¯?ÕÞ¯[Î2oËWì²²ÒEÞ?Ly1wS§Ï@ÇÚ¹Šv½Á¿j÷›ò¸ªï`×c}X>w µ ¢ã²±ë•ùc!kï*“ß§{|s_8ôҜꩭûcUšÿËi™Rÿ¨Äà Y;W~?eÒ”B#ã·Û'V§øí»µc§Ò¾³qÎéôçd cíZ9ÿ·NCóÜürÓ¥Õrÿ¨Î3õÑ½è¥ø+]'áÜÂÍbáû40žµs¥4}òüÚè±QǦu€¢÷œ ³*ã#jÑ7Ý>lÃ÷ó¹&Né *P™ï²þpðИҠ¡±4Äê÷îîÞ‚8&|ÒÛeÅ)½SÏþóÒBÖÞSÚ¿3£½ÐpUZX;öt=Äv±V®XpYÇàÞ×óU­‹X;d߃ÈqSm‹z~¯´Û¥]5anmöÉ»M:Pé/‹X»ØOòF¡ašµÑ~kò|XÓ{áXçI5?hŽMþ®)â‹E¬Ý¤KвìŒ>ï’,Öx^[Û2L£ÈŸ¶çö‹oŒk÷}éÀÏV7 í_é”=S¬áëž,—ÝƒŽµûþý×Vmý\¨oúAm;¬<Ö|ÕÜ¢3wœ"oæ ‡"Þ­”ãf>ÞÀ‡µÿþänÕG/v”çµú÷FÐL/Ö›±ßüU§Ñ~_´ˆµóþžc×mœÖC¨Où­ÿ³|y]¨ùmøŽxó=òü¢aç* cí]Áö›ò|\ïün_le¿×v3-¾ë¥ÿæ¹f÷î9þÖD£0?/fý Âçfʇ²ëé´?îŠì—ÚÞ7¶é›Ä(ö¤–}7óQžâŬˆ-4. uÒ1VŒXku¤ùÆ=ûE, -¢>kw‘¢íÖr»×ýý…‚¢J¹ÿÖZg|rº“µX.45Å/Ò5z¹ôº…uq1kÿoÖ4þP·é“-†Ç”ù²–íÅò‘ýþ˜ÐÅXÔèHBÊ8[ÌúÁ7Ž:d\&÷ÿ:ú¶$½§X›9R5gø$ù{¨r¦·˜w«ýâ]{j@ÏÚ;_Uô—Ž­3”vX£º3nµÒ_ ]5´QÌ=qý¡Î¾oÁ‡õƒ}}TíŽ uÔMÝa÷W›ö^d/ï»r>=ö¥xÎõYûïu«’jÕQ¨ƒ´ôo(òNwM¹¨ÒN/^Ð9º³·˜½ÒrJã¤ûªÖ%¬½÷d^HÜbW)ÔŒÿcH$±öaGã·F ËÙþHÌfüQŸµëî~*† µÄ>ZpvX×—¾ˆ»ñ_ì x•Õµ°É<““„‰I1óÈG""MµHŒ ‘¡¢Wm¬U©WÁ-bI‚ ¢;u óIB«µ©#¢UœiU¼ïúöþNŽG¼ÿÿ÷ï}žÞŸçö^k¯½ÖÚÃZ{øöRëô9›š—{ø²›nx|Ý®¿ŠOÝðTÕõǽ}CnW[+Oëy$ïA5_<þòWÏO·ßFZ3ô–§½úïz¹zÓ’–éÞößzê5ïÊ/ò®K.Où ÏçnÊëö{Ê>œbu­g~õ˜Nµõ'­w'ݱ¼¿ßG]yÛ™WzÏ_.óuâó”×í÷ä éÝ}È9ŸÀ¿ÛuÞqÇéíG[g-:íŽM‘ýûBŸÏ Z®ÔåXçiŸîïO·êv|âocÓ%½ý¡Kﯩ­W~}~KuÈìœú'µÀñGoÕíùDÍ­'߸y·Õuëgm¸¥Fm]¸mÌÕ §÷ï'íûðNT§\sUèŸWÜV`‘nÇ óμõ³Sš­.YE¶¼¥¶.ÿdá¬Cùªcw²ôxµ ÑvÀ×í¸þ{âðŽó.)öæïÔÖÇg¼´ñÉ;U‡Ö¿º\ØŽ}›rº=×û×V×…'1“=¬¶zþ\xÝ‘êçÚ¿é÷—év}\v¥sßW[¿üùãÜw«ùúüµ__‹tû­»e¸œtyχ¶ ûÛº¨Grûõmø™¿æ«E}ñÊévëøniμ3Y]ÓÖ'lÉ~×Û^Û쎧:´}Qóôù åtût\ôvõoV<Ûßߦý¤è½ï¼ýmÛâÆÒϾ½£_SŽù—¿¯æÉ±ý1øw‹t{µÿâæÕ)‹Ò-gžÙöÜo7N9-@uü¡6+ùš«Ô<½T`±n§6½/ku‰¾ó~µíýõ·4>R¢:žŸ(#_Í£SÆ5÷¯7ëöú­íîœfuM±$ÕöàMÙ4°Ÿ?™[VyÏçÇ´™9ñÜ~p±n¿Ç̽o»Ë1Ù-‹¼óÆö¤÷®>£¦\u,±ñ®GçµüìÃ÷| :º]ýHéyV× 9 ù™Úîm?aM¸êXao¨¹8ÇùÍûÁ×í»úâëNoØÈ¼Ñ‡Uj{eð™'¿2»¿}Í~¿¾tû>¢çYú§}>¬¶O÷rǨ)ªCV#ïSóvÉÀÍ_·ëÿ‘ Ÿ“¬®ŸOøÓ¡ìMj{ã“ý¦£Suèû3ýóçbÝ~«ôý«ë—öÆÚ~Õšù¿þxŸw¾7ç(Ö˜˜¥æÊnÙÌŠúúú§õÐâãö=·êlÆ·ægû½‘xðï÷ËÅâxfÌlu™^_SN·çƒÓ›7¿õõfg©¶o¹tzeÓ·ª£9?)ø»±ªE£ß¥€¯Ûíã_tÙ˵½oUjnÚgªCÛo5[ï˃¯Ûgåõâé@ßø×;‚ʧ?×\·Ç}2—¯©î±º6mùéîÃjÇpϼ¬êhÕqfëòXܦõϯž1V×ïd£ûMµc\ÛõOïAµÈ*¶òDð´Þï~çþüG»¿´ºž[ÅHxDí8ÿÎ?Nüy·ê8çÚ÷¬‰S—é~¾ÖûÒ“ì i«ë¥w_Ý2ñµ£eþú?½ªÿÜæÜ,<ÿkÔœWæ&cêëÜ®õ½D÷C«ëÕýcŽ›ò‘×?Ýqõ_.Yýâªãô±jÎejnTß= wQNëû¶sRâÆ»ÒêúðØ?¦fP;^üÒ¬ŠšþyÕÜcšóPÈ™¹IÏQNëýf}huÙnY±Úqǧ‹Ûpjû欯k¿…õýíZï7<ö†ìX[͹؎‡ê£˜ÂUGœ=0ÕœqdIn_·C«>´¶&Ë„~ÚñÌÍ M?S]öÚ¶»nûÍùàév˜£/ŽY[sî÷·AjÇk!ó6„yÏ1/Óûâàëö8GŸ7Y[eµ3åDµ3xýóïMøVµÛf÷2ÕÒ¹6èÕGÏßnuîôüÞçš`m=ï»àÉWß©vV6Þ}ïöÕr³5&¨=Þaë_]¦×+ÖÖÆaÍøBíœwíãCNœ¦Úõþ·j©œ*3ø¶ÞÕU<ðÄË;o²¶>¾gOàÉ¥jç{ö¥àûÏþâKu)^YF×gàÛúV×ýäù _žcm}çäñ½mMjçW!×_yx•ê0ëŸYúÜ|[ßjáu¯þâ‚§¬m ½i,ÉÔ®’†Oû¦î¤]ì}u©K¸ |[ßêÖëÞÞön±µM´V›¥v]´îΧïîïwööý©ªe`ó7»†4PÎÖ¿º]Z}ÒKÖ¶ÊÎðù'j×âô”Ä+7é4ý ýÕoË¿z«GµÚ‹PÎnu§¹o¸ml ÜPQ»Ö-gþgÕn»Õ5ªE·?øº–Úîr‘µ ïp{wŽÚÅâ®vý;õ–hýßmwû ­mWÞ”þõµë£“¾¼ôSu©s¯j‰Öû {Û¨ÌÚöð¤\¨ÝñëÖ_ÒóTÿ}%Zß÷šsØm»dÁÒ v×]]åzŽy~‰Öï}ƯÞ.»1W>¨v_úË%ËÏA/K´>ïÇ9<¶+ËÚ.Ö1çSµûÁŠ;~\ëm¥žÏ¬íÍïýþ¢ÝÇyíën¹=rÁ%ài==ÐrÚç¥)oõ¯?X‰ç²ZyôyxZ?šuÚv½.VžÔij¾þþæZ?«Î)Œ8|ÃuÖö¿Íúøå‡–)Oþ빯¿µ¸ÖËÃzhí0çïž*Yر~½Sëã9]ž¾ÆY¿+ÏØØ“NÊ(®õ¡¯1¤Y;¼ÿå£ÖHåÿá¾×vu×úX}éïdejí°Õ5My&žÿȪqÉÀµ>e¼éãõÖã{NýÓOsÂN®õðè XôуÖNYÊsÆ¢”˜¤zàZþ5-¶chí”SÙ¦{•§a~tÚRÖÏwiùÓû¢ÖΓdq¹ò4¾þô£8¸–ÿ·]Z¸îk§œd~¬<3÷>÷ñž*àZ~ÇÏØùÛå‡Ó³Ny®}öµ·èŸwiù×^5{󈺩ÖξÆÃ'Þ=Dy¿úÊåð‡îÒò·™û¥»°âÙ|®ô¶kp-›­žrkW½\¼}IyžýpÍÆ‡™_îÒò·3Ûììšoíš5>à«ÓC”dzæ7oì}¸–¿ý‰“þ:þì?X»ô<­QÝo¯Èxì®OkùŸ–Þrõ9V÷ Sñ(NUÝŸeœð—œó€kùŸ1÷7»õ}?ÕüuÜuO9ë!ðl=X=úâŠêIùàÂÅ[]äkùŸ¹"aÿ I­9­½0JõÝxãøicê,×ò?kΓzä”~ÖùªçÄÒ˾Z’\Ëÿ,JÿŰVÏÊÅ£úbÒTÏTϳ—–Ó„ †ÑE­žÎëâ/]z‹ê±Ÿñ»—kù7Ùnb¼Õ#Ü<¬L=Àµü›åÔqÊT«WVÍ%7ªž_½õÝÇUáÀµü›µ^­ÞÜc×»ŸÜ§zþ󽑳§'×í¿Ùv“#­ÞSåÂÏZÕsûG¿Z{Åà¶üVï¼üÕ•_Ö«ž{Þ¼dÂÃõô“°j³¹ÓûPêɯ­„¯5·UŸv£³n/S——ÛQ±ªgÓ²³ŸD¾–{ó­¶GdõÊuÑäoTOÏÜß?ó8óÉ -÷–8Ûñ²öè}XÕóAÔUeÁ/×roÉ{>{dÆkÏqaQ7ýê ®ÎºðÛ½ÀµÜ[*޸㾵{­=³ßK^=kŽê2ÿÚ›§2.Vh¹·4<4zÜmyÖže¿ÏT¢z‹W-ýÛù#ëvß"æxò×Ö›P›ê=~öüWWÒ_îÑòoyà§o5ý°µçhY!«Þ³×üì9üÕ{´Ü¿w}‘ºµýMÕÛ8eýšMûúÇõ=õ~ü“Ïÿ<êKÕÛüZý¨ÁO“ߨóímö±ªwÞÉù—_Í8»§Uç›û¡½WÏ^:cq1ù+u¾ÜršºIõ.¼1û¾ÅW¯ûó^Ûm=Eõʵ…7I¾nϽ%sÒÏÜùGÕ»Ú^¸Õ¸w€Î?¿JN¶Tï–€Éíù ùZ޽7ÚQªwï£/6¾Œ½ºWó¿÷É)Ë[OºHõÜ“Ÿùšÿ½²üæuµ'ê‹WÆ=N¾n¯Î„ocBtZ¯DØÜj}Œ\·WgÎO¦¼ÚÜl½’÷«Êðë#ÕžÚÉ÷;ûs¯n/}Œ=Öz匞Ò–'©=§åõ]3yïÕíÕ“vÙü[³­W®µ„ÕžóoÜ7ì8ÕþËü-Û»Iµÿgíå‹ÂÕL}Yµ¯²ÞÙšž¨šõ½?Õ¾î¼Éc+Wí+í…ŽºX¦»«×¨vùœã导ëzgڻΟ<òñÖO ÿÊÙ'ñž#<§ý½xfœx×;›×ÞõÈÊø)jË=r¡ÅröÓ½ûjæ|Å{¯fÝlé]Î=ï:Ú9ßvί֭Jü|ZÔ©Î}:羞³ŸäœO9ûF^¸søRs/¦}®}@©šeÕãQíúBÍÔ÷<½ð™úÞjwÛÈ…‹nPÍúþºj—S£€‡üõèÝWÚb¯Vë®´7¼÷æÌ½TgÒ¹_èèÅËgÛç÷ÈÍլィ‹õýÕó«SªïP3̽5ã©‹ôy¢wÑÙ§Ùbü›-ŸÈÂö°UkôT©Ïý;çÝÎþŸjÿ&NVp^~~UsÖ²;~ò‚ºHßÇWí¶¹š¥šÌþYûˆgGxÎ^Õ&£,à+ռᩂӗ>¨Úž°v^¼¶»d#°PͰ§Ã:ç{o2þ‘W¿þý5öûýÒ»¿¥\8ì“xùvú‰£gçü²Cna_PeUësgï9ÇÅú¼KµÍœxî¼w§ª¶k>4qmƒšQ(ºªM¶ßyCµ½öĽ¯½B]dîkÎø­Ü] šô¹jÊ»[n9r8üùË¡ºgÞ÷ʲW•²ì•œsRg?ÍÛ¯ ι’÷~—³No¿S6«‹Íý§9ýÛáïb»;«šï·/Ö«f-¿ºøé¾ûÑSªí±ò+c/UmÚûÁüûýþå]o–Û¾çfyÇŸ£G¯³³ì ªƒÒM/Ý¡.6ãØ¹Ÿ×véòE€jk:ÿ©Kj6ªr|táãªMN½kRMgÝ´b÷¥Ú~}f\F´£_Õö`Äš‡Ö¦ô÷#Ó>9ãH/£¦?1÷Àúxç1Gg>ûüGäsÚÍéoþó¢“~ßà™}Tï½³u)ö0¯ÞܪÍÜpæ)ç;¿6[ á^þþÔÖ=÷±­'z烶ÛOËfi¯š?{`â‡ÇþQµiû¯šÅ{ɽK™ýcÕ¤ÏÛÔZs¾6£áÍ”éù{üùÿÁøä'§3¾žyëÝ^\zª¿œÞ~jö•0ÿ:÷ª/z;˜aÕ]­äF‡ÓþÞþíôÛµrÌW{¿šüÝØcï+Rk?Z _"xûq›éÿ?°Í¾wY¿>ìëuW¨KŒý>²=óöGng|9ýع¶N®ý¹Ÿò¶S‡ÞóÎ'm{/o¹âûUÛ ×Õ6òwú–•Ïdœ¬féï¼ý¿MNm‚ÂUÛyÇÿÅZÆ™½}4\ÍÖßýxË]ªïù©6¡>w¯³ÿãµ»þó‰ÓoÍy°·¿ú·³wú÷žïç+eü5õim×yÏ{ÛÅ™oœïÃLÚ;O9½ök’}qÜÙWSmX·g×þNÍÒí®ÚÎÚpKõ»7¨6í—ª6}?_Íjzéö/÷Tm²¯½nÛß{úoW8züA{:vWÉéxþ9^¡}vOƧo/òÚÛcLÿ4rùÛ™ô‹Xú¿KüÓô³_ôö¯ÝÖçjŽœbž¾Ì;.œs–ÙöµÜ©ÞñáÌŽœmÆß1óžÓï~t|šï.¼ãÎß0û¥ª]–ýzœùÝ*1ãÑÏó×çü<Ç¿û1øçGN{ñœû–~óª×>úû—¯~ߟôÎ#æ;¼þqgo{ÎW—è{6^ÆÑK½ÞŸuôâ|Ÿë?Ï{ÛÙÏõ—Û+—¿}0zUíú~š×¿rüŽ sŸÍi?»®ÚäëÙÉ–Óî?˜?>y´]'ªYö¶L¹·ŸÍÐçÝjÖ¦G/N¿ÓÛ/:NýYßÞ~‘áç?¶}"`S¼øÎýäæ¾Mûù ®MK¿Ã9‡ôÒqÚç"ÙÞûì ïüàðiüjgœöÏ/Oÿàú¡½ó‹W?úJÍÔß©ûså˜~ÿ¡sôèeóx×ÆÎ©6s¿¦m¼\Ô^á|oâø»^½8ãÙñs[‚>³X¥f zkSãÍÁs¾q¾ öó“½íçÌ7N}fžð·ûô›xòßÑ·.ƧùG¾yùŽQó|÷òß9Vüפuj¿94Þ¼—ˆŽ]&föJ+›qÔhbd÷™y u,ìL~ôë`!ûLÌkÒ¡” ¥¾°"3¯COáÔn8ºŠ #‚tÄb·Z‘æ=Džb¢êùQWT§ŽSÝbâS÷é÷c€ÅlÔSýÞáb“ÚŽGÝ`Þ<×ÅÜájбõ\ûÌ[‡¤ã <>YÇÚ“¸'ò¾¡L_ Ô€ë8ÔƒÂÍ[‡M:f ºÈÄ¢^©cØï"ÓàVO¦=; é$øL:dâO·è7‘ì$ÐHi2±¨á%^Rô›ÛòÖ¡¼o˜†^ÒÀO£Ž!Ô1¤Á¼sè11ûšLê}úÝ$;ÉJ»ÏŽCMÔ™±L¿yh¿oH™Lp2÷™8Ôõ&& 8Y”Ë&/f·ê2%ÛïÒO†ÁÛ0xËq™˜Ôè5œœýúryÜ~ ‘üáèa8rç’—‹¹è3ÜÜýýqªå­qy?Qb–È4/1Ýä¹ÉsSÞ½ßÄ­.2ï™·è8*v<“>mòÐC´ò¨#¯ÕÄì4ñ­çϯ×ñ%Ƶě‘w¡ä}Eû]E*.@¾hÀc2ÀO¼%ïD™7Óè·¥äÝF;& ²"Ga¥Ž×ý½øØÀ >ºRǵ‘7Ëí÷7êx)£šŽÐ/B¶"ê/¢þ"ê/¢þ¢ƒ:ö¡ÄЖ·Øå-Ky'ÝŽ³²ÚÄXÙ§cN£ûbä,FÎbðŠ[}ÆûQÛ~Ô¶µíÿ;lûßk×e·h}ÙoVO2ï# c4X º„fc&ˆ~DÛQp=?Ú9^C!ü€…7”tèj=]„‘£lz§žpê 7=D0WDŽ ÝHhE6™8dÀ£Àb¼EQWT—yÓ˜þ½Z¿mí`1ð<0ÜăÖ@ÊÆ‚Ûhâëb^p‘vïê3ƒVðxðãM¬1Ê'@/ºÐCy Ô?Èe⌑7¨ËÄØ…‡DÒ‰«u¼1ûíbdÌ8 NR¸y¿˜tR§žþ츻 t, ûÍbh¤´˜¼ð’ /©úC;ÆX¦‰Å ~ZŸ‰ÃÛhâ‹Ák:²¤·˜8¼}:Ö†¼]<ü¡ûL<^êÌ ÎŒ•:昼YœI™Lp2ûLŒÞñæcp²(—M^6:͆÷ìNçÌŽ7֪ヷh瀓ƒ^sÀÉ9¨cÄÉ{vü1ò‡wéé;—¼\ó^.¸¹uÜ_y“ZÞx´ß=¦^7õ¸écnòÜä¹»úã¬41Jè¸vŒ2øÈ£LzȃVuäÁseóºLì`àùÀóç7™ø&«u¬y3ÙŽeF)@¾hÀc2ÀOm4ŠºGUš·*[õ»ö›Ê}Úô"G!</lÕ±‰ Uej³4ZþÖëx)Ä~s™ò£¡]Ýøªào²QõQõS_¥à”zt<ã2Ê–/Ð&¬†×B¿&\Çy.¯‹e®["ÿ‰=÷µãþ6Û÷m×#Ùf±ÉþöØß‹Ýul®ØY±©Èü=[*6Rì£ØB;(¼Š}»æØ4™£[æØ1±]b¯Ð£m«Ä>‰m{$¶HìŽcoÄÖˆqlŠØ±b+;!öÁ×68vÀwÞwæxß9Ý™Ë9\æo™³ežvæb™¿2:ÞhÞI¥ÏÒ^Aô ð‚i¯`™?釡Ìa´cz Cö0ø§l8ý"‚ö‰ Le"é«‘-:^xe£èÑ´} åb ?2±²–Yfâ)B7Žrñn11\ÇL¹$—‰ƒ¸XÇ>L•¹¼´÷›ôúr:tÒW›X†äe¬Ö1Ï3e΀·RxÍNÖï°–@¿’~[%c=•é˜t¥ðVqPw²•úÝÕZꨀNí!íŠÕIYôSǯ*‹Lè.àÕè°Jð P ïuè®n’~Óµ:uЮf xµÈSK~-õÔI_9êßõo[ü¸{Ô·ýçòmÿö­d.Ð2Û1Wè—vÜ\ddŒ DŸ” ªçG¿ BÿA¤ƒéãÁ´U0ü„0ÞBè“!¤C¨?ÜPÒ¡è: ºaŒ—0h…¡»pê oän8²Fdò“yºÐdlF.01<€GQ> xÔJk7šº¢[ùu˜øºÐŽ¡OÅÀóÀd£Ü”7¶ÉÄé×mip×~›ƒt|ÆQw<øñM&Få¨;9à'¼ê”lâsÀà ҉à$"o¢Ø‹§™ÃÇ`t9ž“’MløLBÉÌŸÉÐHnÕñ9츹ÐHFФá5ý§¶˜Xº¤Óà-tð4ÒC2MœdBéÈ’NùtxHßoâèJz(²d€ŸA­&Þ8™àd’Î$I: œ¬IzÊÌ¢L6éläÈFŽì.ƒÎŽ£Ë0 ¾†ÁG89àä [N—‰Ÿ—¬ãÛ±;ÈŽžr‘9—¼\xÊE—¹âï‚;­cëI¬a;žõºÑ©›vu“ç&ÏMy7¸#3M ¾FÙŽÃë1±=(“‡ð|àùÀó[tÜ>‰',o˜Û±@€ Ãä+€v< cAŸ6-£¨{T½‰ñ·PÇÍ“X!÷Ù ‘£ž /^¼øhà£Ýúív‰»"±IìX¿]:Žˆ˜«"èA¿ÙЍ¿ˆú‹¨¿¨O›²c’u;öHGÜ‘cÐQ1rC°¼bä,–ùXl‚üçØsÇŽ;6\ì·¿ívl¶c¯Å>ãm‹Å;6××ÞŠul¬¯}uìªØT±¡b?›)öÒß.:¶Pl cû»çkóD._;÷c6îH~°Ø1Çvýw6˱O¾6ÉñÅæ8¶Æ±+bK;âëûÆ:àÌý2ïûÎ÷ÎüîÌÛ‡ŒÞ»LÜÊ¢ïÀº)ƒh÷,ÝÃW0}9„¿!” ¥ýCá#œòáûuSG¶êxHÑô¥è&öÑ$“œrÁ‹m1ñǹÈsA3®EÇO€‡„}:v‘Ä·cÑ.ƒé‹ƒ¡›ÄxJvéØà)Ô•ÂßTøM]`b QoÿâÖ1…†@;½ž¼¤óï2Ê ^,sØ~—3“2ÅÀŠ™¸Aäg»uƒ ø© ¾2Ú°„¿•â7/6|ŸŽ‡-ñv*È+ƒÇ hT2ËVëøãà•7’r䨄n¿*pª W ÿÕè¾ZÕÈ7ŠüjhU‹-?ÊÖ G-¸C³6ÙÄíLÖ±;úÓþ~ú_Ý—>ºWü¯åO»M{{L<™FÃ#ÀÑGã=ˆ>D:ý“7Xþ ½pCèw!zŠ7”~Š®ÂëaŒ‰0ʆQO8u†Ó‡ÃiÇpx ­ñŸ¡YÏ>I;E"k壀GE:x4ðèN=åÄ0žbè31ûLü;h„Ö@hÇÂWìóN¦$e]ðéî‚ï8ÒqÀã:õT~|‹‰wG] è$ú ðž ¶½ rëøvƒ¨/Þ‘5x"u$4ñí 1žC#)Óĸ#D™dÊ$C3y¡Žs'±íR ‘B:EÒð• ©ÐLíÒÓc|¦‘NƒÏ4àC¨cˆØ$êB:òéÀÓ§“Jù¡¤‡ OðœA u‰w— ÍLÒ™b¯Hg¹ulŸ,Êe!yÙE:ÆI6}"Û£cð C–aÈ?LükêÊ!/œäËñ˜XÔÈ<ž†£‡áäGo¹4@.y¹ð‹.s=z*Q¤cÙ± 7ê˜xntä¦Ï¸És“çÞ§§û‘nŸXCð0ØHÁGeò;ZyÔ‘ÏyÀó€çÏž<xþ£ÈŽ+ <xðtR€|Ô[ÈX?£¨·YF×1F-ÖqƒFyLÌ=d+DŽBx.^¼x!ðBà£.Òñ$&áhà£>úEô"ê(‚~²QõQðc€“©c'ÿÇ@ãpŽYiâøytü>1wÅÈQ,þxÅâ—È|«¾ÿá~±ØqÇnËñ·×b§ûìkÛ{$ŸúÇìí‘|k»úcöÔß–Š u짯ï-6Ó×Nþ˜}tl£ØDÇúÚ>gßȱqbÓ{æì?É~9v˱SbŸ|m“¯-ò·;þ1ÇÄÎøÚÿ8ºb;Ä^81ÇÄ.øÚÇÈüï;ïiv,RÊÖóCÖ úQ0} ˜¿!ä… —þJ ¥…F¹ÊG Û`Q™üšL¬Rún4<Ç€ÓȯÏÄ"EßÁHý±Ðí¤«ãÚ¨]Ä8tO~<õÅ“­êJ`|$ì71Féà—H_H–Dè FÇIn?x2ü$ÃkŠK»–©ÐM? ÞÒäo—îÊC*u¼Pq9Ó'™Ø À‡2q@ÁÏìÔñ>³Á-–ìÃà#~J9-:¶g•øîðK^.´*ࣄ2%ÐtÛ¾Z.?`eÈTÍd¬¿>+ WM[ÔQ¾Nü÷Õ&¾'2Õ«<è¢|U²qU¡]:êª^ýZ™C(__uRvjÇÀÛ—Ž«V‡uôå1ÒÄ¿—ÿ*}þ ]ÛO0àÆÿÿWõýÿöÑÿ¾ÿ¿ƒß/>ÿ?«¿?Þ´!uÚ1ÅéOè5Ài¿@ä büÑ—ƒÄ. —`Æl0¸Áz7¤•éPpCùw(r†eò£‡A'Œ:‹áôÉpÚ&>"{¤#(I¹Èñ&¶¤á! ž¢€G~4*À­ ¯‚¼ ø®€~ô+©ž*?¡R~ðWI¹Jd¨DUðWE~•ø äWué¸Úåªá«¾ªÑ‡Ä­†·jáz5ä×Ào y5äÕP ´j U-s®~`ñû6ÛÏüHöÙß.Éÿ˜Ï$ÿ¿ÛG{êØQ_êïë‹Ýôµ™?f/d'ûè»ßþcþ¾³Ïîk÷ÄÞùÚ4ÇŽ‰ýrl—c·h÷ïÙ+ÇF9öɱK¾÷Qüm‘ïÞ»¿ÿ/vÆ×ÆiOIl‹cSœµ€cGûáØÿ5s‡åÑ»ô#ÆWí@^ yôË úEý48“º æß!Œþ† ¥†öÑÕè‹a”gÌ„£ãpdŒàoxQÐŒZ ÝÄhúq åb ?ëù¡ÿØpg8–:]À]”‹£ŸÆÁWyñà”427‹ÿMÙRù7åKióA2@¿>C§:¥ðR ¼LæêzK¸¹Ê •ŒÞR'^RÈ/‡vªü]¨c WÐ?‡ð«¤\:<”tè.]ý*ñ×É˹TæôT ­ò²©sò×Ë¡l94kÁ¯•yºåô™™«¨¯LæòêGæ>pꀑ¹é Žu\¿c¤ üV´èXÆvÜcÒc6êXÆô,ê·(_)sõZàT¡—*IËþ%–q ¼Œ9$/(°ãKüd ™$–q køk¯g¬‡f-eê¡[/ëàõÈZGÙzx«ƒ—zÚÀö9ä¿ÿ—õÂßsVð¿m­p¤9îÿ´NøŸ\#ü³¯ÿ냶sÿ5Bƒi ðèÿô™€=´ „× L~¤ƒ+˜qLß æß!Œçúw}!„t(x¡¤CiûPx 7Œ¶ ƒ~x=¿V~zzˆ A:‚r‘ŒûHÊER_$º„Ç(ø‰E[E3§D7ò ?1Ô1ðî@Æß@xHÙXxŠ…V,<Ä‚ë×EÚ-uÅQWé8hÅ?~’ŽyOùêM ®RäM€ö ÒƒH¢®AÈ‘ˆL‰¤)ŸèÑñëK¨k0|? ü$xK? üdædÆi2øÉ´k åS(Ÿ‚^R¨?•úS©?•t*é4ÒiÔ¶LOOC€ þxK‡÷tʦ“N§î¡èe(ð¡àžA:ƒú3¨/œ p2¡— ½Lp2=z Í' :YȜե§Ôlt˜ ßÙŒ­løFû £? #= ¾r Nòæ [²T@w8õ'o8ò§\.òåB·–ò¹àå‚7FPçø#¨sºr£7ynòÜà•ð«…f­¬([L_©$]Lýåü»Ü’ƒzê#~?x¥â÷ÓÆ•ÈS ^)r—eêi½Tüñà­ ¾Êà¡ ~ËÑCøeb_¡[.vUòá§ŠürðÊá¿zµÈV_åôçrê.‡ïrô`¹%ú ^¯©¾w.Þb¾£:d¾nôÙ/K6g1+Í]S—ù–j£9/2gã+}lb£Yõ™o©&éu˜÷œ&Ó|KÕaÎÆ]>¶qµÏ7Ò-æiù–ªQŸÝØ÷¯Â}l1ßHï÷Ù7[¨¿]>ž’ùý½3ûœ<Ó쟵˜ï¥™ûZMÚVz¿—®4çåæ›—93o1ßWu™ss·± ÍÙù!svÞh¾±ê2çC•æûéÕ>ßYUšï¬V›»¨áúìÈÞS[fÎÏÃ;Ú$󵯭4çèË|ÍwÔ‹ÍwÔÍþÚ$Ÿ{©Æž0ß]Õë½6û~êJ³×Ög¾«Î4ßVO26¶ÕØÙó=VŸ9gO6ßdM2gí õ7÷÷Ö ð]–Û|oÝäsÞîñûÞz’ùÞzå|—Õäó]V§þ6ë{ße5ýðîªým–«ÿ{kûlÝm¾­n2çê«õU{ÿqŸ¾Ë'ß¾È9Ÿœë•4ê}Ùo´} Ùkpé}FY÷Ë~¦ø âÈ=ùL|#Ù“•9ÑöV÷Ûû»šƒzÞ–}Y{/°IÛx9´÷üë»{² ßÍÈÝ=Ù[sBù^Æ>ûkÕû2gÊügï2û•Ú§‘ýÙ¨‡Ïzø¬—yœcáÿXh Îqòï8ê<Ž:Û§—¸c‘euŒ£ì8ÊŽ#e§ìñÐ?YN }"õHúDd>üái<ÿã);žöß§—È'Aó$dŸ Ô?º'P÷êž@'ËzN†îDèN„îDhM„Ö)Ð:þNA§@ãThœŠþNE§!ÿ$ô9‰ô$Ò“Ðç$x™/§CïtðÏ~õŸÍ3¨÷ ê=“v9þ΄¿3©ó,àgÑîgÑîgSßÙðsvŸÞ¢<Ø9ÀΡÜdø™ íÉ” ýÉ”m€ßàçRö\t~.eÏ…¯)ð9…ü)ðtüŸþyàO…שð1ZS¡u>¸ÓÀî4ê˜FýÓhÛiµËÑßÐh$¿‘zÉÿ)u^@þà_ ÐÍоÚ¢·ŸQþg¤§Ãûtô2º¦#ótx˜3àqõ6A§ xð&è4SO3õ4S¾™ò—@ïè]B™K(s :™ ¯3áu&¸3Á O³Ññlø™ óiÓÙ’Ÿ-Ô?‡:æ€?^çPÇhÏ6:óàg>e矽ùÀç#Ë|꾂¼+knÿwô¾êÑᅫÞYý×Ú›:Ò¶¼Uö ¿oùæ÷yÈÿ0þÿŒÿÆã? ÌøòÿÅÞ™ÀÇYUý¿‹„¶´¥ø‡°6@Å i¡4èLöÉ>Ù'ûdŸì“}R@‚"F)›oE,A ‚&¬)KŸ¤A""„×IõżU¡€@}qy¿ç¹÷É óÊßåóñ¯Íç“ÏÌsŸsÏ9w9ÏsÎ{û7°û7°û7°c©>K†ýØ¿ýØ¿ýØ¿qœ>Ç€ýØ¿ýØ¿ýØ¿±BïÅþ ìßÀþ ìßÀþ ìßø¤þ­û7°û7°û7°ã\ý› öo`ÿöo`ÿöo`ÿÆ:½‡ýØ¿ýØ¿ýØ¿áPñ¦ýØ¿ýØ¿ýØ¿ý[øNØ¿ýØ¿ýØ¿ýØ¿…ýØ¿ýØ¿ýØ¿Q¬ã ìßÀþ ìßÀþ ìßÀþ ìßÚ7ŒýØ¿ýØ¿ýØ¿ý[û °û7°û7°û7°kû7°û7°û7°û·°©°û7°û7°û7° ãû7°û7°û7°û·Îgcÿöo`ÿöo`ÿöo`ÿÖBìßÀþ ìßÀþ ìßÀþ ìßÚýØ¿ýØ¿ýØ¿ý[1"öo`ÿöo`ÿöo`ÿöoí±Àþ ìßÀþ ìßÀþ ìßÀþe}ÕÀþ ìßÀþ ìßÀþ ìßÀþ—ËÀþ ìßÀþ ìßÀþ ìßÀþßÃÀþ ìßÀþ ìßÀþ ìßÀþålºýØ¿ýØ¿ýØ¿ýK|k`ÿöo`ÿöo`ÿöo`ÿr†ÇÀþ ìßÀþ ìßÀþ ìßÀþe¿øG޳å]ì×Ï<©'¶çÕ}4­Ïqú5þØ”^gö†á”D볜Cƒ,Vc•lÕ{Ïc5Vɰ^[Y:Ëi­±¬ŠØ“âÒX%“ú<§SïI™ÒûÝzÍy$lŸc@ç´öŸÇé})#ú÷̽/e«>›­ÏtŽë}qz_ÊÖ0,2Ÿ^{žUgù-,²!µ?ÅÞ›béÓ{"£Ã°ÈFÂ0K³dZŸéôé½)³À©×¡Ç#Ö¡7©sÙªÐï¢ÖZô}¶Ó­ÏD…íI÷k\² Æ/qª}•Ö¾ôY½6íÖç<·êõéú¬§gž}+>…?`­Q/Ð{Ôýaç=£5NÙ Þ£>«ñLœzzXïÓ\¦~‡µÎ~Fà•9ÕÞÍìWÕ¸&›®‰…W¶Jïc èýꓳ,JŸuéµë!q2©÷®Gé}-Nµ÷ÓÂ1ÒXfcúlè¬Úçò©}>Ô£1Í6é3¢“%þɦyΈÆjü®Ùtþ‰GãŸlý3¢+þÌQÿ<{Úh\³8…2‡aæÖû×7é³ SjMßÚßsHáJvе˯Öïe`¤X¿áG«u{ù=ý‚qµoíÍY¥ÖBeO’üF ¿‡Ë:»ünž"ñ!mÌ‚üriGõœ›Ô¾Ùkcí­TûjkB~W[pXä· Á§½¶Ö>Ú­j2gZáSȺ§¸âÖoñNµÎiýö'z:ÑÓTnz<úÇÃ+šù‡.™ ÈL€&‘¾MBFý˜DÝ$ê&•KŸLÝd§rëS‘•*ßio*ô©è”Á¿‹kípQ×E;]ðIƒgý“Fÿ¥Ã#ùéÈNGv:²Ó‘‘!ÿAdÂ;Þ™ðÊ”~ƒWúeÑÞ,xdÃ#›qÊfûyÈσgró›ÏØä£_þ¬ 9 ¸_À¸0…È+DŸBî¡{÷ЏWD½bô)†w1¼{¼*<ñ ¯‡û%Ô-¡ÏK¨[‚^¥èYJy):•¡YP…/åèZNÅrx•ëÚJh+¡­DF%ò+ßJt÷"ß‹Þ^xx)÷¢³—òjdÖP^} ¶b);Ž¢¿æâ%ÆdnŸ¯ÄFáq}jÅ<ŒãHlcÇ4öÙhÇü_1 sìÏÆ&“DÆ#‘1ˆÄvÌ!q†ÄO„Ç#H| ±@d ~¿ø÷• B>½øó¶/ϼšóáÅ_·}uñÑÅ7¿œ9<ç‹?\|ð”oÍØÎëW‡ûÔáþt¸/mûѶmû϶ï,~³í3‹¿lûÊâ'Û>²øÇÌ ƒ¸ØòmßXübñ‰Å^¦æ²Œ©µ¯bXcðùôyÊ­ G×ÚO·@Ÿ‰wéýÑcêÌ£…‹ç×{ŸoXí•“³‹‚m'gÉ,,»Uê|¢`Ò ¾¨½ïÀ:o8®ðF“NöË9tÙkáÎMjìØ(uæ\ö Èïíòû¸`˜Éïßr.]öOË~U9Ï"¿M'S'yV™{ mM_ uRÐ3…û)ÔI¡­)ÈÏá^N´ú ?šääÀ+ƒçtŽÐ¡W´9ôWÎú]3 s°ï,ø¹øO“÷¼Ô¡¹ÔÉ¢n.ßó¹—E½\êåS–ïV¿•æ «ëBhsá_m!2ÒÐ;Ÿ²|©+ï*êã0 S%´¥˜{%è\‚®%èQÂý øÕQ¯Ž{Õôe5ãÔNý”×P§> ”7Ò\7чMŒc÷› kbŒšè¯vÚÖ‰¬vƦšN¹æ3€¼äõ «“ñê¡ =´%@Y€²×Ú@‡:Ô «Çgmk‡¾¹uðiFv;2;‘Y‡üFêöÐîv¹†®öÙÌxôÀ§ÍŒG'å=è Ï{³vôíä?€/ºµ£{3}Qƒò®¡½=𠿇:i 4dvÒduÒGíûÕò_|èÕNYuèÓC.ôˆNRÚê¨eÁÚ2€žÈïEÇ^dÐ# B¿í\>¸>|p}øàúðÁõáÝõáƒkÃÿœkÃØ¾åcûyXÞ©ƒúÙ&4bC>ÝA)ÐxÖA½ÇΧñþöëó*i=†Q2¦Ï´ÇiÌ¿«". £d>»Ð{“£4¦õ°Þ›¶7yZŸ[ôè\cúœ‹[ãþë3ñN}æeLïOŽUç^æ°JVi¬’©°ó‹ƒ«$¨ñ^ýÛz¿Æþó꜓:gEœ>;?®ÎÆX˜W.… +ûÖÂþ Ãþ‹ÕgÇôÆè0lëI³"Nç¬Ø¬p²,|ëØˆ¼Q7Ë£ÎÜ[Ø%Qúì} w6F­±ö/k<­}®qPãNë}Ìq “ÖÚË<¶—Ù¯1ƒz/³KáÕÎa™ÄhÜëMê ¿…e­÷3êýÌAèÔ˜&Cø×®ˆ³ŽQÿÚ§1M¦4þu¬Îk1¨÷6Oi ìeÛÄ­ó[lÒxSúìã2…8«ö•ˤÄê ’àë 6vJœÂÜ“ôIpjŒlú#)æoÀÜ<öIœÆ „ád#°½Kpä¯Ä> Ì“û"êƒX‚s¸çb³Æ8™ÖgAg®š`0X8 n}, ÷v©s`âËžo9ï)ç¬doªì×¶Îoƪ3^²w\öˆË™Më¼æ˜ÂbWZðˆ/ôª³^²·ÕÚPç-£AöX'N·œ›üÙ?+{fŸApÖƒAðä<ªìIü5Ù+¸kÖ^qh\.}¾ùNø:ÑÓ M<4ñè¯xèäºäeÁ3šDtK„O×IÔM¢nåÉÔ+cœ’áÂu*¼R¡Kå:•6§BŸŠN.¾» sQ×Åx¹4x¦ÑÞtx¤#» ½Ó‘ŽìtÚœŽŒ ù‡o&|3á› ßLxeÂ+ ý*à•TáC6c”-± ürh¿›qsSæ¦ÌMÈl¢¼>íôeuôE t%Дq¿dò½Ý ‹Ƽ½ Ñ¥Eè]Ľ"î¡k1ºÓÆb‡úd”P¯Dø£O ÷J]Ê÷Rƪ‘úeè_m#:–Ë?|ÊáS]%t•ð¯„%²ѯ’þñ"»‘>òÒ^øx¹çR!O52k(¯¡N |jè—Zø×»–>«£~=×õÈ®‡_=òêé‡ztnDÏFtlD¶>>îû¸ïƒO3rš‘ÓLýê·À¯~-Ôi¡N }ÒŠ¾­èÛ m+´­èÔA¿v OýÐÁxvL©ðÊü.dtAß…®]Ðwq¯ ÙÝÔéAŸ^>{¹ß ¿^tè¥-}È l`XûÛò'ñµW‡Ÿ÷³ãçȵc«32F–xØ^G–¸—6Zq-órn YâW‰YÃ×’ÃãÓð˜ÔŽ?%挌7#cM‰/%¶”xÒÆã”xÑ^_íPâ=‰ñìØŽ>´â:‰åÂך%f“8ÍŽÑ$ Çü±ã.;Þ’Kb+‰©èóÄMáëÌvLdÇBv$1PdücÇ=vÌöb‰g$–±ã–ð˜Eb;N QìØDâ‰C$±ã‰9"c æ‹OHaÇvì7H¼+0¿­8Ab„ðø |-š¹ù}"Œ›å÷Ûþ¾øúâÛ3†–_/>ü¿‹ÿþQ}÷¿ä·3ï"}v±_+ŽÕس{DåA°p^3dLçBÒçÅõ¹—e [[p´­ó}“ ÷C°>ÛCÎìYØ#ÿzJçiÛ¤ñ­Ç5¾µKcs ª÷ò7)œÚ|Æ­úBÊŠ©[‚îÕô]1ü*з‚òŠ!u&<…ò2)Cfüêà[]5m¯¦ªà_Cý&ôiB×Êë¨ßÀgu i8 ÍíÔÏ€O:ÿ¹ÔkD—&hzè«vônGf^¹FÇvx éä{'ß{èóNú¢‡>êW€Ï€´“ûE´#@}e%Ж¡K ¼KéÇRh+ø^…NeÜ«a<ÊáÛH”Ó–*t¬à^3ü+¡¯„W%}VÅØV¡»úFè¼ðñ"£/ô5|Ö@[C¿6¢ ýU ßfƹÞuò®§/ê©[ŽõèÞŒ¾ÍÜk?|´×‡ÎÍâ@× ¿f‘üfê·À¯ú–€zµlU¯µVy¯ÓO=еJ¿¡Ozv ¯ƒþíàÚÏ}?ºtÁ³ Õ}÷zèÿÚÖ Ÿ^¹GÝ^ô@—Úrp}}ÁÁõõÁ××®¯ÿ{¯¯ÿ«¯­Ü{ý¯µ¾.ïÄ!ýìÂþ-ðëöî׸°ƒ:ŸÎ~: pb­|:«4ø°ÆwjLÀI è åsÀÊ©³IaÈZ9#ú¼û”ÎæÕ9u¦56 Gç‚žÕøQ^3r\Ÿ‰÷hð)'åÒøã:´SãŒiìÙX/5­ñ¥<:ô˜Î­ãÔXàcaXà¾yrGnVy#-U:·ÎΆQ;¦ñÀ¡¼‘HL(/›•{"*,wäp&x\X~Y §ò¸ V•… ­ñuž:O…Gç‡žÔøà±#dHç­*\ ÷Ö¯sEO‡åŠTy,¬œ;1?pPaâZø«4Vøf…báÆè|ÑC WÀ ‰Ñ9£}3|<,ûƒ¸!V~I§Æ ߪraX9xât~É!7zZçá‰Öø!gr³ÆŸÖ8"ÑKÄ¥1 gÐÊ=9®0Å­=Ët^iWXžžá¿[|8"·tL¶ø`X®žY•ŸCðÅ-lqŸÆ›'·´7,·ô؇ä–T9('EpÎW8iáØâ«c4ޏWçä>eRå‘öPX‹â'ùUNÁÇ,Ìó5–â¸ÂV\G ?Å¥p?%cHá¡H~ÁEÌ2Ác´pO†5ÞÉ´Êy"ùJÄœJÁYlÁ:ÜÉhánUXj^2<36+<5ɘ1¬qŽ7+|Á‚wÜÂD[¦ðw"8„¥â”~AO'4èÏýxî%È?ò I@^ º'@ãâ3ý’¨—D½$‰áÑ;™v's/…ëTd¥"+•ëTÚ›Š>©èã⻋±tQ×E›]ôm¼ÒâTtä§#;Ùé´5]úPþá› ßLøfÂ7^™ðÊ‚& ý²èÿlôÈf|²Ël¯ !Ü\»¹vË5cæF·Äг*´ÈC~<ó›Mý®ùè—Ìd _cQ@ýBê¢O!ºQ·ˆ{EÜ+Bßbô)¦n1÷‹©ë¡®Þ)¡n }Y2¥B–RÚZJy)òÊп }Ê¡/G×rô(‡Wù˜ g*¡­DF%2*‘_Ô!ò½ððÂÃK¹«éƒjdÖP^} øø¸ïã¾>ÍÈiFN3õ[¨ß¿øµP§…:-ôI+º¶¢k+´­Ð¶¢ScÐ>ôAãÕA™=ýÈïBFô]èÚ}×´ ·º©Óƒ>½|ör¿~½èÐK[ú=@Ù€ø1Ü·þìøZ^¬v2¾ù°¸&2¦‘XÆŽc˜ïsëù»„Ç+§Ø1Jxl"q‰‡H bï)·c ‰1ìØâÏÅã†\ÜÀ3Ìš/<ß,œØ:—ü¸Æ·ò©¼ns8áSa¹€¢NlåQ›t.Mèz5þ74‹£æ·`]Y9]ã;Ná÷Y¹Ý\:gO”Ê})9/­Ü–1£Žué|–S¡\–Vž7·ÎáÔù!‚*Çʉ1*¿Žµ´§ñ W©Ün§u®õ!3gRcåM),TÁE=mLáà nµ`VI~y\Zù£TþÁÉ–¼Ç‚+›HÝDÆ(1¨ò·%îW˜®òhMŠRxVIÑ ÷9ÙI± —. ÝrÄפrà“$~mKVùjR˜—)ÂÇ«sÕÈ=깨Ÿ6®r•»¨ŸÁg!õsáŸ^9â@“.ùÜÏ¢ ¹”ç»T~ç\ú2^¹ÈÉŸ^ù>…mmå‹QÖe^…a] ßbtkw)ÌîBú­„öT Sõ*àW‚¬ x–@SFy¼+è¿vêU OmŒ‡&ž{UÈIà^¼èÓnƬ;¨^)ÉèÝMÿÕ¡o*ÿý”÷‹Ï5¦^)ÝÐ÷ЧýÐôS/ïMèßOy»W-o¶£S;òÚ¹èä³};ѧ{С^yÐpà:À÷´h ‘UÄu‘øD´ÇCÛK(/¡ŸJÐ¥y¥ôi)²Já[FÊÐ¥œ>+§íå´¯œzåèT m%´•ð«„_¥”¡så^Úé…‡—r/úx)¯¦ï«¡¯¾5ôS ¼ká]K?Ö¢c×õè]ÏøÔ#«žvÕ£C=íoDÇFtôÁÇÇ}÷}ðñ!·9ÍòIýøµÀ¯…vµP§…6·¢S+²[Ñ©ÚVtj…Wzvдµƒ²ôô#ßO[»ÄB×.dtÁ»KÆ>ݫԫ¿—û½ðëå~/méEvuhÇ€¼«åe)qaßêYoý}Ôß!þñû B~ø[{8ø»Ãßþ»ƒý›Ã¿ãï ÿJ¿5Øþø¿Ãþæßd»ôscrÁÖ«¥Û¸ÆÈv©|jV¾c§Î™9©sÇé #:‡‚[ãdOë\ .KgJç¡wk¬lì-Šë(®£ÄžÖy3ý*oæ‘A—-¾ôÖ0¬l®A¿~‹cæÁˆßQC:=ú,ázÉ&™½@çkƒ~iPcfSÙ&•SÇÊŸéÖùÛÐmõ~Çͯò./ê|ôž°\ô t>7OXÍU;{“ÊëfåÕqëüÌÓêÕkåxó†åÐôè\ôÓ*×›•?sPån¶rÑG«œ,‚]kah‹¿Jch©üÈ’í$xžÄý“¦Ô«\r¾Ö³…¥=®óÒGë;ð?e«Îñ­sÓCÏ"mŠY¥1µ7…ajûTæD¯Ê¯œèSy OWx»’oY°w%¯¦äd“ü0’cî¹5á¹’ò•̧•³:W½Kãk„åÖ¤ïcý:û&•=“ûin•[= >k¼:Ç:4k†Ư䇌m C;Jå§±òéĨœ:k‡uÞºq•;Cr`H~ Ë-¡þ:¾ ª|Ô‚_è×ùmFtŽ›q•çFp¬³á'}6¥òeHžÉI‘M½"è.âÞEà Xð†%ÇäHFWwPåôqϪ¼‚,¹1 [òSTE«\:î!¯â©}<}™]4 kA“ŒD;ñ»¡«•ïR†ÜDê'Kÿñ™Æ˜%Ã+™¶$sJ{³ù^Êwü\´ÁÏ4Ú†~i蚯4ê§Q¯Š>¯å3ƒöf@Ÿm†Ä_Œaã’I3‘Ÿ¹_¹cYè™m|³‘‘M—£6ÿ~‰Ó¸ŸÃý\>sùlƒÎÌ\øæÂ7¾yÔÉ£ÝyÔi ¬–vå#;ÚÊ ©[Èu!×â{CS$~¸S¹zÅÒŸ´§žÚáAmö }=ð÷@_ŠÞ¥û•;X†.eð+c ÊàSÁu|*àQÿ ôª /Ú¸®¢UôQ´Uô2ªÐ±Š¾¯¦¼š²jd¶Ñ?~Ê«¥¹µ2Nû•{Yÿ:äÕÁ£y èÛ@?øÑ±ú Ôo@ç®›¸ß¯&Ÿr5› mb4‰|oã{›|G6äµ!¯ ymÈkÛ¯\Õväµ#¯yíðë¤ß:i_'íé¤}´¯ëDF7<»áÙ ÏnxvóžÝðìG~ú¡š~hú¡é‡¦š~h¸gùfògãnØk½¿„¯÷Ú±Š½ÆkÇ$á1HdìaÇg|دÓñak½‘û¬?,>ø°ýIH<0_éÿÏç÷Ûk¶¯î燯Û~}¸//~¼í¿Û¾»øë‘ëÂâÏçwÛþ¶½g>Úö£m:r}Xüe'ÄömßX|âp8b/÷¼¾o¤Ï;Ÿ¯ûaëÆó­ÿ¹ýÝâ«Ú>ªíŸF®‹Ojû£á¾ç|>§ík†ïùŽ\¶×†#ýGÛ_ ÷Å7¼]ûjn5î–.C“ß­–¬|æS:ß-ýtxPå²²òòìˆ ê\·ÌÁ…ôÓBæçÂe~‹xþ-¦ŸCsÔ&{œzK¦tÂaþÛe>µ¤°:¨ò®>Ê ²&*”×VòN¬Tyå$•ä\V~¹Í*ïëš•WPò‡&Faû¯W¹©ƒ_ðø-Ü}Ês+9_Oò)l~ɵ~Jåº~Áɨ¼Pkå}Œ¼xä$2®‰N•ë.Ñ­òDm@V²Wå°üþTÊRù¾v³ÊS—}šKå¯ÊDçLʳ‘‘ÌlèÝc*§Õzú#[Þã“*·•ä(àºùEÔ‰W¹§RáYŸ<îy/ïhSé§RyG£w©¼³å]„œrÚUE[«Ð3UŒY}X@Ûòä=B;ªÐ±]òà[åVŸLäåAãæù^K¹Ù>xúW0¥–B Y…®ðmݯWmðöË{IÊ)ó£cô~yÿ Ûœ>tñ «ù}ŒSŸ”sÝG•¿ϩ–Núè—>øôÁ£Š6öñ_‹^}ðê£ÕôA-:ÕÊ»OêÉ{:M´·AÚ!uiCs¢zMÈ郾MøÊûKîK=ÚцŒ>Úá]¹ö£{m›{ýèÙü~t<²3Ðw`PÅIÖßßsï?ãºÚ|kjõ´¿Ç~Þ¿´—÷à>Þ¾}¼ëšÚÿëzÚ_±–öWíÛýÿyíâãà:Ú?fMæ®GÛ:öbÍA·æ‹}²Lç™ãúìñШ°\ÔØÐa\æÔùæÆU.¥Ã¡=|6"×´Gpïúûˆ›”ë(xG!;jV磦?Žä¹p$× ™s Åßãzr9u>jèaŸ‹cU.¦Å\/žÕ9™àwm>ŠûKÐ} ò–lÖùLÑe)Ϭ¥Øç2h—mÖyM¹w´G磦–Gë|ÔÈ^Ž®Ëá}L´Ê9w õ¡-RyÊ>>¬óQ£Çše:ç<åû±ð<–ûk¢US+µOçC¿è•¯8z«Î;ÝñC*§éñ“*÷™JrI€Þ'rïDoDNjÊN‚ö$hNšÖy©©s2²NFŸ“÷«T§Ä¨ügç1N§ Ã)”¯ lz®€nå+(‹‰Uù©¬|tð;5Jå¨>§¢ï©Ó:e§Awºœ6©Ü+?ó”ÎK7–«ڕЮ„v%rV"ç1:7ÝÎMG?žAœAßÇÂ'–òXtˆ¥]±è M,u× ïéï€Êñ,ùR×lRyª%'µä£·BòÎ]Ä|JGÖùÈ_ Ÿµc*­ä璜ےÓUòM¯£ÿ×q½î€rAâ(‹£~\ŒÊ…• ·ø×´¡9ŸáûúÊM¹0Jå‘ Ï£%¹Z/Ú¬òÌJ>YÉ—'¹ï.šUî‰Ý ¶ª[6ëœY TÞ8gŒÎù ¯*>«øŒß¯\Ÿî'p?ö$H|@ýRú¦Þ‰è•ŸDäÕÂ3™ÿñË¡I†&™¾K¦¿R(wÉ…Þ.øåA߀þ>ú v¦‰_‹ º¦Q/Mz”gˆO²åèTÍ8d";ÚLî—¿š±Ï‚_u²¹Î†o¶Äû•+Ö„Ü"ÊòÐ-º\xæÂ3žyâë3'òäþ~å®åCŸ/>6÷»c•ëVȵ_üyh ‘SHy-zQ§=Š©S M1¼‹¹WL_yh‹‡>è“XþhKÑ«ú2èËÐ¥ ^eЖÑAeð® ~õ+]!×̽ Ú\E?Uѯ ̇&#ú½ ù~>»¡«f,ª‘W-}}µÄ Èj‚w-òêW‡¼:êÖQV‡¼æD|Яú Ôo@ßú¤Iâ d7‰<èÚàÙ„ü&ú M®ùÞ&ߑ߆Ì6‰;—6d¶!¯yíÈkG^?¼Ú‘ÙIÿtÒ¾NÚ×Iû:Ñ»þŒ_7<»áÙ Ïni<»áÙ Ï~ôè—Øš~hú¡é‡¦šñ‡Å÷”¿Èu²¿„70ßúXøÚ˜fäºØŸ[³‚ÆÜã(¾ú|kYó­aE®_…cØ~ØÚU¤lc ØëXó­_Ù~­íÆû­áëWÌÙ¹µªùüË?·6%¾¤ø‘kR‘ëQâ'F®E…¯'ÙkIáëGžÜ[hûO‘kD‘¾íE®y•ßrH´ÊÙxè2ç§üðý:Ç9síæ\”Wåh\È<^ˆý,ÄfQg1óe1´GaKK ]ÂÜ]Š®K±«¥T>óÕ³*ǬŽÂãŸÊM¾Ö£rÉJÎе”­GÎæåš8•Kq ý±YkÏçºq•ß{-<â‘—HÅÈ'|’=*i<¼ÖPžLýd¿Êí˜LÔ)•÷;=6À7ïëùÏ„>]ž›•Tò=®G'7²ÜS*ïcÙü—¢O2÷ªÑ¡9ð)à~5úeÊ;Ý«ä|K¹.å~õ˜7>yŽsÝ@Ÿ g+zùÐ¥yy”·Q^>«Ì«qóÓÆ"èªé?r«iCüû¸îãºOž)ÈêCNãÚ†>mô­^~Êúàí§¾ž~dôG)³íGN?üû©ÓOß÷Sï4dAÛÄfÿÆ¿‹;÷=n}sü`ñ7¾q¾çÙ ê]–Uxr“cò‚±ß®npvà¡¡ŸûÞ4ƒ‹žýãû¿=dà ê:’“9úí']˜×ä8ýî3c ×T›—ìöÔ%Ÿþ®cÕ/‹oøÊâ-ȉ±ä<;ᘚj¼Æñ侪+Ÿþñ¥¶<3ýòϯÜðÒ™ÝË~Q`Ž~«ýœÑ]ç9κª=ñžÇÍKR¯<}ésW8.p9g:o„ŸSñk<}åäõ¶žŽ'·žóÅçŸÜÒûçüîèçÖš£·]~ÍÖã¯sœyÁÞ_”x¶¹Ñ<ï‹ÇY ¯â³äî×^äÇ“å—No¾ŸŒ½nß®s¾fŽ~}Ó¾-í¿27Þ¶½oòÁÏ@?hÑ?óh÷ÙïþCÇ“Çö§Äø-fpý¯u¿ßîsôæCWÿü®ŸšŸúÞÌË—,¢Þ°ªWþØŽmY79öì|êõ↓LxñîÝÞIsôÆî¼úøo;ÎùÄ•¿ÝóÚËæÆùtî|“zãªÞa Wþì«{¤;û®c¡nG0=nM_Ï7Líyoø¡ß\kŽ^µyíÿ¹úsŽÕ ¹4^O¨ñÿþ‘7ßóÞuoÌ×îÍéKâÕ†ÚS¿0ù…w™£Õ¯Þö°ßÜ_•|p&¤ÿjÜŸþΗËÿëÁ•ŽÝåÿsïK³Ì ÿË÷~õ¯9ê~ùç¯íÚc·z5¾Ooˆnú|n‰c÷I ^ûÞ³fð²ë?QôÃ-æh|LÙø ëÌ‹ß\xiôj|Ÿ2÷'÷¿T8×Þ]ÏöÜ0pÌlh^]½âõû§¾¹áÕòÕ%ª1G×eì¹ë-ŽÕ£/uyðjs ùá}‹ÞuœªÆ¾jüŸrÑXv{Ü\ûw \ÿxáUI¡öo¾±çñæËCóhÍC•oþágvš«»˜OO¨y1ùûoŸù›cnrìZç5+W˜Á;¶¬X_ü«Ð|X[Ðû­2Íþìúíî žÿÉíÝÿ ‡„Ú·¼dËOöü>Ô¾Ñ=wgléqÑ—~»pU`Îû¦®¿¬¥þÇ Uß¹o»š'“HËøéív{;÷ÿæâ/Üz­|¼çý‚Ûn6G“ãN¾Ìtœ¡æ…Ùÿ™¶#{ñáÐ<Ù®æÉä)b¿tìÜ%“mwÇçÜù{s4=묳þðp\ÿZÊ›EЫùð¤~Þì¼ùŠ­|õ\3øL×m%ϼoŽf&]û£#Ì÷y§¬¿ìèÕ|x’Î:ñäçÆagíÂëóÞßð[»ýÏù—9ç.oâq÷îzãKv{ÍmÖ~j¾ìyïº×ßùõ¡vŸùÝOuõ‡!û°Ìeièù¶ò­ON­úæ\? \9ü‹e¯†Ÿš'{nº…ÜìØyèo]4ùŸfð•Ñ”Ê/67#LýÚ‰»=æÀ…ë}£·ÄSO͇=çÞ×úÐZc®];þ[}Ê î=Ú9Ô7'©<çÆsàŒøV=< 5?vo{¸ãƵÏ;vðÐó,«1ƒ¿œM¹ãE3$ÿg³7>Rä8O?ïê9²SS͇Ý9?»°®ôdÇŽ;¬‰o_ò©âgSÌ‘WÎë:9ÿf`ÑÊÿØôjÜw½·-èý£Ë±ãªŸ¬¸à ìá=×í%W\jË3ûþçs³—}úRèÕ¸ïúêc›ŸÜû¹¹y¼£Å=òÖ%˜3ÆçwJ]‡9òßïÞíøÔ"ǹzú®¿«|ê±û=5vsÞoÞ|h¨ßä±xß½æÌQ ûŸ›)˜³§‘÷<¾½zʱ^Û«ÖÇqÒwkÜHüÔ|Øùð×Ñ£;ÎÿÕ-ǵýÔœ9áþ-YW„ìéˆß·¾höŠ?uÿà›ÔSã¾sÃw.ظôzÇŽ£¿øËñöææã̪–’·gš£Q;ß®;ʯŸïýž½ÇןõBÈ~L5vì>~ãÊ/eÎÍljwîûêÃËçæãÌÚ{:=«÷›#¸íGŸ½Øqáí>jì¸x×U÷ý}ÇÄ‹qW•¤¿cÎÄŸtæ!kŽ4Gfï:ùµ[^0û`Z²…y3®Æ{b¬áëïºÕ1qÏ·î‹~¾ÊœIwÞüVò§Í‘§¯Øüæ~â?®Æ{bíVÏÀÅ/9&¾pë57ƼjonúÂ¥Y—„æëÃÓíŸ}ln¾õu­Éü|C.|Ô<7åŲÉ1Ñ~ïU‡ûVÌ=OgŠj¿öékæìxä»ïc™=Ž5ò¸{ÿy³wø’µg^5Æ7ì‹¹åž úý3SÙxSís˜#{ëéÜlö(ý Wãl~å¶æߺ~nþM?~yÿ÷êÌ™Fž%nØû³ozæ;s䙀ÌĹ÷rïiw¾ýι;à£Æ}û+µCõ7ÏÍ¿‰Ì_Ç¿zÂiæL×+g-¹ýó!ýŸìÍY×çˆSöfö>Ý:|QÆÂÐsx\ÿö‚¯]ÿÚUŽ‰ì Ÿ¥‹Í™ÁË^ˆ_K?N<õ…ýfß‘Eã/¾“ ½ç'Þ^rù;Ÿ¿Ã1QòÀÔ¥W>lÎ\wùs;,7G,óÏ6{û¾ôtô Î}jœŸ¸*)ÏÑøŽc·ç†wg?šW[f¯}»ûÌиmmë|»¦Ö‘ôíçï7šeö\÷Ò.xïŽÓTÿÃO̓'Œ½žœÌöÐ|íúúª¥Ëw̽fî ubg_Èïì‹yýÚ£çëûݯþÇ»g5φìpBÍ‹ÇknèÉg¿ì˜àí^º«Ëœyì©C÷ù3Cϱ;~P–ü®m?fwÕÎß=¶+L?5/»þ†º×\호¹øãÛ¾ï3g&+/þæí!=Üo¼rkÖÝfwÌK7•µ® ½÷'ÔèÕx?øÕƇÞÜþŽc©m×YΕ\<27÷•vÓ–¡cCó*ßžçž+Ýg¾±âÕ_ÃOÿ]Ö»VþÈÞcÞY°`Ù…Sùq÷~èÔ¸ß_}é‰/瞀Ýþü†™7z̽§þé×ì]æHn¢ÙÕ¹Îìo1Ͻ÷Ñ–m×|vÕÊиœ:ž´*ÛÜ{Ζ_”¯½:d‡¥þ¯ügÁC޵j¼ÌNå§ÂG̓‘¡Õw•zŽ©öš{/:âõE ß ¿<Þ¿zÏé¿Sû÷.Ùò±¥¿Ø÷‹.8úüÃö™{Ó^ó]ž\ꯞ»:ŸòplÐñNwajéêoo‡çïZ­P»Nc¾÷­¹ù³·xùð¯{Ö„æÏå§?síÞ{ ÷]qß¾/õ™ÝÍÎ¥}~jüï}ë¹›_˜}ß1¡ý8ûý°·&ãÑåý—9òùGÏO|¡Åq‘z~˜]<ÔÒ¾šÿËÞw@gYlk‡4 ÒH%½“R‰€%ˆb@Á(¢ ‚) ‘š¥H‘€ HÉ)ZÞ„šPÒË„@PDC¿Ï¼3ï;Ÿ\ÏýÏ¿Öþ{Ϲf­¬d¾™Ù³göž½÷”ož­íÜŸ]þÄ¿zÆÇw¬Ô:qæþ>™_JÓ¾Ópû¦”zê·¢ÞMKQžÉû»Ò²žf_=OJ¢iƒ·¥ÖÔu©[ou” F[lü"ÅCJ³½ì~6Ê3¹o¦«¸±½Ä¸E& ¾+µ¦wÞ’$âð‚Øo<óüòÌ,)~ÎÉÅRZçæ¦êœrÐaúðíµôÚGw~ã>ºÇ×îK­³Zo½ÔÁV胷~™‰s°º^Iñ;þñ·»A‡éÃÆóã^=œFJ¢nEÔ¯{[j7Ü»[ ì]ÀUxÂRJê­N—"âÚŽ0¹o˜Þ20ßf)°ßñκÅ_­½9µI*xÞum‡Â÷Ô¸+µåVZqÉ#Ôgò]%{a-)yý5AI­ŸÕ^}G_*è§=´á•&) «ªÙ÷΢<“çZ‰‰_¬"%Å~ürµÔú}È_7ŽãÜ?#'aÕ}v˜\W»ÛÏžiÜ »˜ûdy1N¶²CByYÎÒrÙHÛt¥#­Ê¯ñ²ó“ë_èÛ¬ŒG7Ù¨þ>™ûß0ů“õ@ZIWÇo>¯êÝá!ŽŸ-‰Sç_cåÜ«aÛÝżïN ë)Ò›Û©ä:9pr;Æô`M]˜—‘à ô†·Ö÷ û÷½Zux»TàõºÁëuÖRJ¼¼°By&çu64ðm'‡O,à ´Š4nžÞ1ÈñºT1é(–VR ËGy&Çoú$鯵ûž¡ä=‘Æ[¸jÌ2©`ðèSc~V3’Ùúõ˜7Žù™j:9/½ïÇN¤qAÙ¦Ùc¶ª~'™ÅoqmÇ™7eP¢É‘äwn\[Œ4ÂJ8ü~O*ÀdG$B»áÚ%¢dó6ÔcrýîÝo:ÝzA•Ï‘õŸÎ¾Ø:IÈkú¥'>Ü,ì"Ý•œ³žÄ³}grßÚsþ¤OnØ‹}±ºaõ &„¨ñW㇭FÿöªT`I'À5îOÁ‡o¼|…ôPÖeǙܯÈùhgºÁ34&½qj’¤¡»”z¶jÜÎÇEì grΟú±osÙrôùÛ?ZK‡ô±ß­¿MÕCÍã±Ç”õ˜ˆƒ3¹kî—T¯9îMŽÊjíO#ÊŸ¹l³ZvûÖµ¯®vžùUe_t˜>ÀèNKžNŽ~÷àÊó®Lþ¤ÑÙÖtfÆ\)•íû <Óƒï?êu¥ßWähÅc„Z©bÙ>²¤ù­&t©s–”j)O츶R¦;º?XòÙn7±Ïû ]¸¹hoMûeÎŃÂ.YÉPÝÇJYÝ<õ·GbýSÊôeG‰¿Ö±é;rÌgæs'm“¼±"ôÃýƒ·^öÿP臼ܚ,Öµ¥LOvŽÝ1âI‰Êϱw>´}ëK[¢­²¶måce+çŠÿó·”éÇ.³_ü½Fíç±-­Ë£'™ªiméÚ¸õŸÎü9ËDªýM•ͤ è1=ÙutùKãŠÖ ¾NÎêpšht¯ZïòjW”ýýTÚ½IŸ¢>Ó“Ý+ÿÖÏÙ°?9N£º¢Åd{Ãq)õÐô±iœGy¦?Lܰ¤tí³äøòS†/ºC´Å¿ûÈf$ø”–XO–2}Øc^£YZFŽ_>NßEù‘îóŽIÖòWýTÚRº°¼×VÆôaÏ´Ž_üe?)åñ›¢GÚ¢Ñ_Ç•öØdž©´z,‰û!ô;ÛÞyJ\*æeÓƒ=g‡JH5!¥_Ò¤?¾E´{ç\ýi¢ØßàóEJ¹M-õ1Ôcr/”§Ï(RzgÚ뙡vD»ÃÀ¹í——û^±Z:ó¾÷zI>e‰v ÏÓ]š O)Å/ç‹OßRö7A‡É½p鑇ÍBIŸÏZyfœ˜Ït7̹„<Ëãbn÷Ä|,còÞëD7œ{2.7íWcZ"¯tW÷{ø¼ñR“ó^Æ()»íD9#ÚÏå 'Ú¿1¢®bK¹2/uÆÉ}ß«r\¦ÚÙŒ/¡·ß_ÞÒs·ØwÒcqš2?S¹ÝPçCÓýt[³â‰Jç;ï"Ú‰ Ë2%!g£KwÌçü¦úG×ĵ`ú‚ERà½ÑCɉßâÆ®óÑŽžx½e\'©€n¿¤ìóæÓ‡ýE—šlO“¯]Nn- Ú—?Ùýåó—ÿv2£b]Æâ/Ôgz±ÿ†þ/÷>SçÝÉc¯t9¾rtìÎäv#±neë±þ=KñhÐazq ðâ;_Ÿ&§â¢·Þ‰J!Ú~[ÖÎÕ†õCèÃv³Ãb¾ž`ò?§¡×·œÚë¹~õ´•Dûúoyg· {Áãwu_‰õõ™dëzu¼O§žé²]•«¶gÞ»}¦<q ŸŠÕ}ÈL/jþ¾áÊVrúëÐCçúàHO*Uû¥î¯ Yñ¦ñoK…}=Áô ˆ¯»Ë;È=ÑÚŽ~sÄ¦í’ænöÆØEC¤)š¡fs_„Ÿ9Éä]ÄöõU¾Ëßxæ§tû8¢ÕÖrBy&ïâg¨›F*¿xÃ÷î¡/Ä8­H{ßè5¡‡ŽM›—#öÍ­kœV½ û~’É»xÚó9ÓŽSù¯<6ÁhV·lÑŸÅt!¶ ëÚÀ5ÞþH^¶FĵbzPÌ÷ç+ÛåiX4üzôÚ1Byܦž3(û÷§˜ü‹oÏ}ëÞmgRåu¸éJæ\Ò0ÿ`”áð!Ri`ÅÄ ³¥Øzå™|K¬ä›Tqù5|–uÒ퇤ùåâ¢ÏSš¤i{ž}áÅ€@”gr.¡ÞnÖcREOEÌ$ ÔÌ\,‘4ô$ŠÄÈÃ4Eœ+žbò. ôù¶wd9©ÚdróÒàAªž7d¥uyÏ.Wø­}9Þ[íRÏeÒݨFFƒNÞÚWügVWÉß4¹±}$î¼?éÖñ $ŠÇCüœô˜üKºŒÙi]äIªÍ—þìJ&ïu"n›?ßË!j<ÅϽPŸÉ½ÄQ^ ªqdõ ù€WÕë†7¿L²ˆHüð{Jüóݾʺ×všÉ¿¤[ðíüáãH5߇kx1dôþÕ÷?lÞŠó¢ÓLî%.rGª÷ž_ôiˆZõ²Þ¬ZÑnþ¸…‹Ê¤i¿·ŸÈü õ¸üÙy%©¾+o8‘ÏÚŸÇÌ\¨ÊA±sÓ T¿59¨Çõ —ÞâS¿©ã^ZdvÓw¡ƒÅÉ¡.ƒŸ“4ô–ŠÞ+êù¨zNwšëE\ú2ëgR3~õºùõo“úGÁKÇwHQâiÚBùàå¹üX¿:pTOu^ÕÐí–ãåjºþÆ×žØÖHš›ß¾åm¨ž3«z|šËíß“šjWªé*ßõ,žVì´r~¡è³°ó§¹üy\SÛùnCŽÕR_•‘»«Û u]Àï/ĵ•sùÒmÞ[çHíÀç¢nE4ú£&;¯.“4t×NÏ_ܧ)wÓÕOu¾Ô²ó\5]Ïì­ˆ{Jïx¦o5Sâi¦ƒ|°­ì‚.—;õ}ƒHíñw¶?É $õâ ¸™,iV¤=ÙÖÝ_š©ør.oYÜQê8×Ñ]‘i&bܮ;vŸÐÓ÷~={þ‹ªžÏœäb¹€ôë™r&ÿâßÞÕ$ÙJíOÛ¯'õ©›ÆŸ,i|I#Pu:ƒjõ³½„Ý/çvŸ^X°DðG¯drlúø“l1è0(Q×ÿê=—rîäc£÷H…==I}ô}¡]»I(£ó6iz·IgÊ¢ãÉí¿|\õ©g÷}H½«ì(%ÍÉ“V]’4ŸN¸xqœº™)‡ñCQŸÉ½x‰¼ #õ«OÎzv !uWZšÜŸ!iröN›—ºJš¹` =yCy&÷bzÝdé“§”'¸Dëê4-,—Ô-p˜=b`„”_tžZziöäëÉÞúebÝTÉ㲘K“ß¹I´¯÷{îîöå‚ÿ)‘=K~ ”ò±hLŽy Œ³4ëÀwàª=&ߢvz`ˆuòŠÏö.[%ä;òÔý[wKùgÞíšÛ_š5†^›ªÃ“sÑ®±ƒfL±%ÚÚ jI]ßM†|ÆJùõÓ’?¾´Všå÷c*BY”gò-šŽž@-å"Rç™iž“øDÊo´û4@¿‹o%“oÑkÿ¹¡Gì‡ñû‹êøšîìè{-HµùôX m›j·g¹&u[~*F§ßLЍÚ^. 3^ðŒ.¾¡ö»öNšßáׄp žf«÷1gåЃ£BØ3RÑ˶}vÂ4Ê×0æÚo7¯è©ª½˜=ྑMìï(Ïä^Do ºHëåÀCíO-ÂÞ”'í’†ž§9¨û/sâäAŸW1=(¢ÖeÊbµ~ッ3ÜÂ*DŽ$ÝÌX‡ùí?êù5iýU»Áï…‰~p}ð=YÕã…·ÉŒÊõ›÷IíycSÒðûSÊ>i“ÿÁ³ò9ó’7ÕÑ>^\ÔuK½šìúLNOÿÀc|'rFÞæIRõ¸vEÆÒKu;„\÷ý¬ë¥Úë̱YEóK„?©búrù)r†nÛš =©çÑfQ³S•kþ×N&+õ§ªñ|¦²ZÅôã½F²TCÎpþk'%ŒKsO’ò™þJ™Ê~D5“ÿþc›n­Ù!üòY¦ÇBÃ.¸¯¿ò»°éö°,Õu@&?wSíh5Ó“ýò±£9ËïAÖFÌ^pº±”?]>02VÝ;æïôÊ3}ØwïSr¿¤†œ•¯E½Hjíª£»'ªûŒùŸy ·rí"e°ó<Ôcrß÷åpºãAή“/ÌšÇI‡¿ˆœ å/£—‚¥ 圣šÉ}_Ï_u]ñ9[)_Üqà•Òª¿}ö¾º¨¬³3š©áMqH5“ÿ^z:ÕÙ‚œ½G7ršÚ㓬f —ò¿(è|òrw)Sf+\ìW39ï}¶”îtªúò£‡|1U“Þ¸¿íŒ÷BË»Žej<’ÉÎ…þW3¹²{:j~dëc5¾¯)¼X8ãÇÛR>G¶÷Wýz&Û?k«aúPèvdÕÆ ¯e7mAj¶ý>àÃU_Iù³7¶Ùž¨ŽK&»W„zLÞ{Þ¥VäGI^xš<—Ÿ´¹C¥ü7C¯.íw_Ì[ßÓ *ñµØW¬erßIÃâŽ_“&9|4'5úöM¿Ú =Œ™µeè ÌwvŽzLî;Ó>8ï=q#i¢ÛN‰‹Hõ¥×°A{òõÔw¤Lvÿå™Üw†Ÿi??ý%ÕÞ4ÉÛuÕtõI¯Àæžg…ýLùœ®{)eòs4ÕŸ×2ýØÁÎéI»¢Žoõ.ýª‰ËŸ¨û‰ù›j.ÝQâ7)“Ÿ£ªþ©–éÏŽÉ˽_SåÒô•|A—TçE9 é¤Ä)Ræ)Kó÷–£ÓïÏ•\Y6S£¶ß$_;ðTõ°zÑé…nÌý«¢tT]gf¥ÕAsí„ÞÔ2½ù~XÖwK:·¦sõÍCŒ“êÙc¶ô®ôGÊRî)Ô2}à÷Éù‹¦~ìÛBª©˜6,Uγ¤låÞH“¿²î<ïq¿þäðRMWC®¤üÄoB|é/e[Ñãw(Ï䞟}Ñ+ÿrOržÞJnEªûMÜ=ª8NèËHùàH½'–ÅîÓˆuhÓ‡íæ# í—LUÇç<ÝyæRMoÿM«ÚcåžJ¶ñzú1/ë˜ü·æÑ‹qOT=:?÷Å1‹Åùq5?ÇfË¡ÿyÔ1¹oyê¼ø<¿ß ðWusÍ£mãgþô`…ì»÷#üyÓƒï,h€MÎÓ°¦\Cªª¤éµ;¦Hù,Þ²&Œ|CŽòL¾›Øþ9/OªþT®[½f”½W7KYòò?õ™¼7Òmù»sÉy¬Ê^º ö'«¾yXÖ¶ò)¬ëç¦úMR–rNXÏä_ Ëþ¢:NÍVôâÆÑþÒ^Û>ïí­®ó“2Ò^»œ"ô•Ý·þ¨žéÇzöýu›=›óâÁ{¤êÓ‡tÇ\Ø%*qj6.‡æÓuò×…ôò±ô05]5ëç5ïnß%ƇÇsÊú'›Jeb.è1=YÛî±ÿÆ–½¤ùÙ)}—=s”TMun]T÷³¢R¶ro¬žéÚÈß/T²ýà~UŸ‚õ_è"ú“pôzXl«zÏ);ÖrÚó93@é…rAíÏ[_R‹FªèíÄßJùt›ßõs”gz±|×úðÕæiN–/Æ«|TxRb{~œð]!‰ë:ü3½XÊî³æÙ×öH®¤ ÑONûÛb¼¬éE—&5ÈVîI50ýø’Þrqœ/úÿùQú .RíàýìN?)_߽߇WT}ÈöîVrpcWÔgzð9?Gj^A'à^Rå½ÿÕmX7éUíé–Øª¶›%o'¡“{6?ÇRÇé[9Pr·šAOФ|wù•º¯œµç±Iþƒa˜ÜÓgÏ4nY%îi7Ë_TrûÞ÷ª¥Wm”ùE¢¿èÞKiÅî ÿÐÀôb¿·ÓÌ¿VÙ2]ÿÁh)ŸÞn™òŠ” o\£¼,wå^"i>šEOðH剕‡Ç x,å»>Ï¥PØ×YîdÒb¹#¢ÿòµÜ4R¹G?)07Køç+çcYkŽ{nºªÓYÈLºmÒwinȱ‚)%•ß¾²øÉàâ¼’/«ïéÖ™ë»KYʽN­¬dÞ-×s×õI3ÿÞˆGU2¹Šý3où`ZW³Òì&Ä û¨•õ‚, ÇDd±rÿŠ4cÑÖÿñhR9ƒYnk¬º~Uöqùx {­•õ„äÊÓQOȵ½71jÜ[9g‹á Wßòåí5ŽÊžûÑ/7¼¯Ã—¬'d ;À$-|?¬2…˜|)ÿPùóƒr'‰ù¥•õ€,{‘‚£H ¢~ç'×IeÒsûî†ÿ*åÓÛf¯-öLËôà+:m|÷*…9-¤rpö+GxÿäÛÝi˜Çì~¨£–éÅêi+éNi¡ÛAŸªÚ¡Êð‡Ÿ”ïß'âœ;/¾oç2TÌ/©vZËô"9d•N‹µÜARétgÆBb/äjé9vÇkzêþ,·ûqmL?Ö{g¬4LÌÏzK>^ó‹ÛŠý §[ƒ*F\Wít–NÍ=¦èõ›{ªkqtoëµ$‰T\L48mã(i\Á‰õˆª¯L/¾{œž ‘,ò,ï:‘ ¬*^Ûô©¢ŸJœ…òLî›órƒšÍœHKÀ£]§¼:’Š­Ãëã®ÕIvOTÊZD¿ ;å™Ü·Ð]~ï~¤¥×ÃØ¡o½G*V¾}³—‡»¤JOÕñVíF#“ÿ6zýÛøÒÒ7èí¨3}HÅÂz»ö÷;©v\“ _ÔR÷;²e·4Dèi#ÓƒíO²ŠNl#-t.r©`ß“4#?;o•zKÊ–ùÂQžÉ™OUåÑÂî§©r¯˜Ùã™Sm~ªÓÈÛOzê>Nöiy$ôç “{Á´Ÿ¨Ç%-üÞnEʢ¿u|GÒ¼%1WÌ—3L®ß¿ðëmí€J”—7HÅ;æk†íz,ièö×Ö2)›Ý7Cy&Çtõþ¦†´ð{µ %{Ó†ï–4ôzrêž <“ãN—Öþ#–]&-ìž?©ˆ:pþ–'ôxrÖÒ•#…Þ±{5zìmúó–ôÿ ‡¿°¤ÿµß ûŸ€í@Ƴ~Ëò7Î0Î2ά~ÇsœÙföö†ÁxŽ3‹|Cè»a.{ëËð6Ç™ÍÕÁ™Mdï¶rœYèG¤;Vò÷¾Pß:g‚ú¦&:o~U2¢¾áqéìöGœÙÎí0/ ×e3ëí™%ñ7ó 9Îl<3ùnüÍ/г–(o ~-3ø›ùàß öà ý³B­Ú9ÎlÃ±ê ž¬‘oÎ0fÿð^~/?Tç½üRŽu…Ïì28Ö•=Ç»ÊÓÁ—Mâo~Ýf¸Wò[ùhß¡™½•/cÊæñwòAË i§ÍÌ4:£Îàǹ™IK6™½ïÚ.Qüm|´å^\}9~,è»"íDZc‘vC{îö; iwðã|tŽ‹ö=Aßùž›ŸÂŠEºúïÊp´¼Ðw/Ð÷FÚå½Á¿7xðO> é}ð™ÏmŽ‹>ù¢ _¤ý@Ó4ý6s|Wðìòþè“?òB¾VÚ@~`Çm-䘭¡«åƒÑ^0Ú Fý`´ B@/d<Ã_¥î 4Ša¯Ò·ÄÂè0ñ w5¬’¹ ú¦ }[¬'òÃA/<¿\ÌßC:bÇ]µgïÃÐ÷wé›b‘HGBޑЇHŒooÐêÄßnfï‰E·¨B†¹*¿ï6ž¿ÑßÎ0»brÙûÀòÛb¨‹üXŒU,ê÷áïÏÈo‹QÛó—þËGgèýå£ÿ|4¯É¬_ò›]QüQŒ£Œñˆù¤ŸÌ0môAÛóÓ ™a>ÊXðÐgÃìÍ, >ž¿›UÉqm’ø›œÅ2Q ÞãÚdàõMaLãØûZ2<Êw?î„~wöÕÁµ)e¦EÆ‚/àor¢=³ñü]öb\”7G¾…/“ ô,*9|<Ç‚/å¸6°VÉ:Xðq ~3Ç‚G¾uÇ´}ðg“Ä1mÀ£m{ŸS~›³’cÚDqx-Ç€Obï¹Ëx6è³=ÊÛ£öíÏ&Gûå“uÞä-'¤ ø{œèŸ3øq.æoq"¿;æp÷Rœ÷Žñ^\C–%Åxi=·xŽkIÓhÏÝa׸rlwä{d0ŒK×=Šcº<…SSÀ±Ü£8>M%3É*†ûf†ßîž|â9nûfŽ×Ž2¾è“o!ÇeM?Ðô+`fÜ?”c¯—rÌu”Ï•ces8®:x Šâø—(Œö‚ã9~úf†z!ÈA~(Æ,4Žãç ؆¡l˜–½ÕOßa“qsnÉqÑsŽüög"ÇGGÛ½ÜØ[ª½ò^N$Ò‘w$ô!ãÛ´zgoûÓ7@£Ðרt†N±4éû«ô~Š¡#c¤Çs\t-ûõc‘‹±ŠEý>q »HÆÎ¡¶…úú£¯'|±âƒÿ«‹­øYê__Jý§®¿¤~òiÿ¨øDÅ÷QŸGýõuÔ¯éú0ôÿþJñEÔQߣøêk¨¡þ„úê?¨ï þB×W(~€Ú|jç©MWì·‚å¬à8SÛ¬k‹uí¯b{©ÍUì-µ³Š}Õµ§·øØAæú}ðlÙ¤s|¯DŽ{›À1½J9ŽtÀ24…}è”Ëñ¸L—ŒÃUÊßF}ó<†skúVéç6™ãiå°·íâ8¶m3ð/dá’ÚuÂÿÎ å šÝ38vʺBfn&ìM]ù-Ývöf®üF.hzűwq} xø„ÏýQ?u‚ÀGxF{!éì­Ü0ð–Èp¤z"/ÜžáGE ^æM/_†7A1t#ѧÈÛìíᨠ†ƒ²1à7ŸÅRôW\ùW\™¡÷W\ùïWÒù–Îø’ñâø›¬+óA?ƒ½ó®º¾:x‰˜ë†Iø…L +9^"µ¯y«Ç ¿ÐGã/Ñž¿õ¾‚¿õîËßz‡Î˜ ßù¦˜¦+8^"øé~:!Ý å;‡ê¼õŽqêbÉßz/äoº‚žY2ëôÌíù[ïÐ sä[ ¾EÒSx‰ /±’™%«(†—H±€dìòø§Þ|G¾u.ïíÛ€?›ñ'mØ"m lÁ£­–aÉ8çÈ·»ÊqÇÿñ½w{”·Çø: ¬ÆÃ!W'åѾ#Ê;Â8¾S:ÇAGÿœQÞ9‡ã#Zò7Þ‘î^ÉqÑ?—ô]ã&"}×Ý åÝ8"M£=w´çúîH{ ¼ò=r"5·ž¨ï‰|ÏB7ÜÇsìÃÛ÷ô¼Ðwo†yèþyƒoðàž|@ÓcìCãH”ñE_Ðô-f¦Û4ý@ÓùþÈ÷ǘûƒÿJfÒP>m @ô!2 ÌeØê£ ä~òƒÑ^0Ú F:<C! ‚tè…¢~h<ÇdD: ý½°† EßïïÇß’½pÈ+<‰áCÑ·zé{òà5å#Ðv¯Pö°ü®<ÚŠD:ù‘HG"¿7hõNfoS\Fk}‹ªdR2FT:ÃeŒíÈ7f3{ûWÆcDýXäÇb¬bA¯O<{__~SžÚê+è⣩_þ³øRñ½ŠoU|ªâ?ucJÅ¢mÙ÷)¾N‰%©£þMñiŠÿÒõYÔWQßDýâo”XRñ-Š/Ñ)©ßPâJê'”7¶_ øÅþ+¶_×îS›Oí¼nì©ØtÅŽë¾µ­k«•ø“ÚgÅ&C²ýUðá¨]…^Ⱦ2èÙéÇ1ŒYƒP†‹ahÉ1Ì’8^,do’Ëpb;A†ðYÈ¿ µ_¨cžÍ!_ËD†I!cP ~W´Ñu­Aߺ™cM´ss³ç é²žÐOüï ÝêAãNÐõBÛÞÐ/oÐôÁç¾(ç‹2¾¨ï‡2~øÌ¼øÓùˆú €:ö,ì B߃Àg0xA¹Žá^ =‘îËÞצX§2®)úÐ+ŠašFð° é(-Ã*IbaÙ_ñ¨Þ_ñh†Þ_ñè¿S<êËeÖÌqâõv· Çî†ÌôéúöÉóà —ãD¢¼!ôÍcn¨å¸CHmæØC m }3ÎãØCn/ùAË$”áÈøÝhßù¦ÐÓ<Ž=z8~÷mŽß ýì¼™c¡|¤»€×. g†|³t†5 ãOºqÌBŽÝ|‹ñ:X”¾¿iK-Ç ŠãDZŽA„ö»"Ý4º¢ kŒ‡5øµF}äÛ mƒ>ÚlffÌi[ð`KÓÍ:DHÛÝæøC(ß  é> á‹6|QÆi_ðäš~ éš~È÷ äûƒGô1é´€6ÐF èB¦Ô¢~ê¡|xBŸƒ‘Œö‚A?<£!ÈA~è…¢ýÐŽzaGäVÈp)(nÅ‹ ˜à=‘G~øx†EJ1ÂeLp”(fn¤Wß1ŽÐßHô%ù‘H÷o½ã¦)Åb—qŽP> mG5s Ó†)JñÁcÐv ÚŠ)`X¡2¾êÇ"?ù}Lž{Ÿ ŽNmµûôGÁI¦~ùiüg~WñµŠoUüª®?ýGö=u}¢®ÿÓõyÔ¿)~ úûöò_ŠïRü–Ïb þ®/Òg!7ÕWèÆ«Ô(1«²WªØ{ݘU±íÔžëÚr%f¥6\×V?m›©-¦vøË\Žõy@ VpL77޵Ý1*àx+(ßò蘬ƒ×†ñ3¥ö1‘añvjç8¼³8ŽÁ†´9ê˜ ì5Š»b]²Êeê` ¬AÓºi:¶Ð-;Ðîùv£ö íAÓ}v_Ž íˆñuM'”uB}'¤À»3ÚsF?œÑvw|Þ=ã›i¦™;꺣]wÐòpãøe(ßô½@Ë <úâs?¤ýP&uðY è‚VxZÁÔ1„þE;¡n§ c~{&p,2Ô‹@½|1î…>÷*f˜c‡ªo>‹ïÑh+tb0V±H÷±dXaò\Ò㺮üÇe©§÷ïÿ¿Œ…ÿq°n üÏŒÿ7ľÿŒ¸÷_!æçc}•cl%rŒÏJ޵ ÝÔG¾~¥.:ÆÞ|b>B‡ 1–†H!m1ÂøÁ¦cÎ#m¼™ãcQ›Îð6©)0A¾ tÔù&àËõM‘6E¾émŽ«žÄñ6õ8Þf~‘ßù*ÇÛ¯]J™1of e† é|@Ã4|‘öE_¤}Ѧhø¦êø!íþÈ÷þà9éÔ@h#ý„ ©ÿCù ” BÙ È$´ƒQ6´ƒÑ~d‚ü䇠n(òCÁo(Ò¡(Ý ƒ<ÂP6 éžnÇÿ‡#/í„0üNŠÕ²èC/†³Ú+—ávR÷‰>DB×"A«7úÔù½3›ŒÛ‰tÆ9 ´¢ÁGt"ÃO“1;‘ŽIgxnK-|Ä¢|,òcÑ^ŒAŸDŽß‰2}èü¥6•þ(¾Vñ±tRýWçøÔ‡*¾óé½×?‹q©Ôõ‹ëR¨øAŒƒìŸö}º>OñqÔ·ý=¿9þ§}ÅŸ)¾ìïÅÀÔw)~KwòøS?¥ø§§÷{¤‹­H}õ=ÔçüßìùR¢GÿY M}ÅÓ1ôŸùƒ?óOÛ~ݸšÆyz²Ý—1”µ ?YÆLÆ<2 eXÉFøk”ÁÔÈr4­ŽQÏzeBm6æ´i2ÃCî4žã"¯s<Ç6ý.èƒh›¡=s|fšæøÌºhQÈTÒ4, 8Æ1x³B[]¡wÖ–Óy6 m Ý·E;7ü¢Ž]!Sánh¯æƒ=þÚ£M{ôÉü; Œ#øqDûŽhÓ õ@Û9Šcƒ§î ß½€a» . éŠ¿®W9Îp2ÇNàØÂÍWmx‚OÌcO-à ö}¯$†ì<ï\fú ¼Êø¢¬>ó ôÕ< \Ú @@ðAø<cŒz!ƒ¤Cnsœa|†¾öD;=‘î‰têEà³-Ã…¤xÁ‘‹HŒu$lRoÐì]Êñ‚‘…~D^4øˆA1(ƒñ}ÀOДãúó_ÅïÿJ±ûÿï½ì4vÿߺ­»ÿwíYëýãûÕÿHÜþß³'òq¼í€yÑ!‰áÓvhæµyøO° ÔÖcÌ Ð†!æ€!dnˆ12DÚi#ÈØãbŒ²ÆÐqc䣼1Úì‡_¤;"ßóÛièœI!3¦˜s¦Ð1S䛂—Nà¥ò;•2¼Ûΰ¡A¿3ßA¯ êwŸ]À›Ú3of o†öÌPßùæ9ý°? oú–(o ~-‘¶¤~ü[¹á}·ÂXX~WðÓ4»¢®ÿÁÞ™À×UUû?ÛÃð×HiÚÒÞΡ mIiÓ†äÜÌ7óÍ|3ßÌ7óÍ|ãD)¿UA#ʳQ¢8ÄçSRˆ8Ü€á=[óG†"7ˆQ|‹(¼ï>{Ýäþ#ðŸÏ_ÑÉçssïÙkíµ~kŸ³ÎÙ{Ÿ½×ÊX“?wü+øÎ¡|%õV·]+‘½’º« ­Bö*ŽW£k5×öêsÛŠÅŽXdÆÒ¾ç‚á\øÎEç¹à\ƒkµúŽ×"{-˜Ör¼¹ë°im²×á<ôœ¦óÀtÇëÁ¼úzô®‡õ7p¼ù8v Ï>ÇäoDßFômäx#m° ú&ø7q¼ |›‘¿Ù#¹î9Þ‚[hÇ-àÙ2+9îÑ¿}[‘¿ þm“¯vön‡¶ßÛ©Gyrâø}>rÎGÎùú™†ÞÈÙ;°kÇ çp\€Ü {!v_î ¡_Hý‹à¿yMš[öÅà¾]#ïbôîDþNè;¡ï¢Mv!{פ¹¥ïFÖnÎÇnøvŸ’¼ÇÔ½D÷ϡţ'žöŒGïŽ÷À»—ÒV—"çRìÞË|ǽœó½ïÏ>ôíãxÇ èO€7vL@Ç~0ìçx?øöcßð€÷@OC"õiŸDÎËe_ÿeè¾lÚo¸y^Ú×Õ÷ãp7|ï]:GòJ}ÜðúÚÈþ,çjáþ¾/†ïÿ)6¡OÍ›üÛoâ½™ï·p®ßÂ99ózçäm\ŸoãüœÎç l<=gr^Ïä<¥û¨œ«œ÷\§+8Gç@_©ýþÕœ‹ÕÈY­ýþXxcÑ·=kÀ·¾µ”­=eº,ë‘¿žzøÞ ßh[áÙŠìí\+Û)‹£nXãø}>ôó¹>wpNvp­^ }LQç"ø.¢l'Øvò{§þž]|ïæšÙ ¦K{É”éÚÄSoîïqÝí£þ>¾›0iº8ûѵ_%‘•ˆþËôµó?¥¥Ç+ËkþqæEßèkô:ŸßóyŽÏ¢ì>WÔó|þÈç>âóg>/òyIö0óO1†RŒ¡c(ÅJqÿWo“ýS<ã(Å8Jñ,PŒ£ã(õYË8J1ŽRŒ£ã(Å8J1ŽR«e]ã(Å3CñÌPŒ£Ï ÅMIm9\ÆQŠq”b¥G)ž) ÿWÛ¥¿ˆÿ+ü_áÿ ÿWø¿ÂÿÕNÙÿ+ü_áÿ ÿWø¿ÂÿÕ>Ùû…ÿ+ü_áÿ ÿWø¿Âÿ•%kxñ…ÿ+ü_áÿ ÿWø¿Ê’5ø¿Âÿþ¯ð…ÿ+ü_Êû?ü_áÿ ÿWø¿Âÿþ¯*d~ÿWø¿Âÿþ¯ð…ÿ«zé ãÿ ÿWø¿Âÿþ¯ð…ÿÛ{Ëñ…ÿ+ü_áÿ ÿWø¿Âÿí}mø¿Âÿþ¯ð…ÿ+ü_áÿöúdü_áÿ ÿWø¿Âÿþ¯ð{½þ¯ð…ÿ+ü_áÿ ÿWø¿=·Žÿ+ü_áÿ ÿWø¿ÂÿþoÏÑàÿ ÿWø¿Âÿþ¯ð…ÿÛý|ü_áÿ ÿWø¿Âÿþ¯ð{ß<þ¯ð…ÿ+ü_áÿ ÿWø¿½gÿWø¿Âÿþ¯ð…ÿ+ü_¯½Vø¿Âÿþ¯ð…ÿ+ü_áÿz}¡Âÿþ¯ð…ÿ+ü_áÿ ÿ×keþ¯ð…ÿ+ü_áÿ ÿWø¿~·«ð…ÿ+ü_áÿ ÿWø¿Âÿõ{…ÿ+ü_áÿ ÿWø¿Âÿþ¯ç¯þ¯ð…ÿ+ü_áÿ ÿWø¿)ü_áÿ ÿWø¿Âÿþ¯ð݇Pø¿Âÿþ¯ð…ÿ+ü_áÿ:æ€Âÿþ¯–ã¼r|‚ð˜Ë!ëÔ›uAöžÇ¼?·×©GɸË-ëEOÊøË±6脌¿üf©½2ZÞ·û_f}ФŒÅb"âLÉ¡8鳑½qò>~LÖ EÉ|àa³ÒžÜ%1 ŽÈžÈXÙyPÆe§$nGbL/Y¿>#ïSvɾÈ1³fÈŽ_+û#›ý‘ö:W—¬š’¾£CÆh>§MËzv‡¬i˜uöºö(™ktÊÚöq3n[xßâ‘õDS2†‹–½“>3i—µEq2¦óÉ;˜ ³ÎÈßEKÜ·Ä>“±Þ¬Ä@ˆ‘8n÷É{šYÿŘùM{ßåA³VÁÞ{’µ¹Y›ä“uº&^‚½^7JÖ*¹dŒxXöeÎÈúh‰¡à’=šcfܨ×ÔÛó¦12†tËžÍÃòÞgÚ¬k²çTcd½½SÖÜL?Û^{?+ï„¢e¬é‘ø ã&ƒ½nxLÆœ'dg¬ÌËzdÌyPöwÎÈÏ(³ÏÓ^ƒï•÷CG̺ Ý·×?͘uÆöø3Vb1¸M<½îØž·="ïŠNÈû¢Ó¿·×>$ÔY¯ß%cC26$£7ÉÈHW2vXðXèr¢Ë Ý Ý Ý‰Œä¥Ðv)´] mŸ ü)ð§òŠì4t§1ziÔKCn:m—-Z¸28GàÏ@N¦þÀ“ ¾Lø2çÍ ‹vq!Ç…r\ð¸Ðç ÉPü9ÈÉž=zôèyèɃž==yèÈom™Žèà(Àžì)À¶ô»)wSî¦ÜM¹›óéæÜ»ÁU„좂ƒ­˜¶)Fw1ºK_‚üxJBfxR O)úËhÿ2ä—!¿ Z9m_­œúÔ¯€^ö °U€ÍvWr\Éq¼Uà©BO:ªÀRMÕȬF_5íUùª§Yuè¬ÓßȬƒ^½õØå¥M¼\‹^ðzÁã…Ö€¾ô5êx±¹Z#X› 5QÞLy3ú›‘Ù ®fêµ@kÖ –Vê´¢«•:m”·a[íç›5êvô¶ƒ¯í´g;|`îk'X;±«sÖ »ºàíFO÷¬~õpÜæ0÷P×Ïo?¿ý`ôƒ£9}ÈéCß²5€üAp R>HÙ ò´cžôôQè£ÐGußM÷åõßëiÒò}yŒ¾òHü£#æý’ÞdÇAŠ•XHóÎIïÕkôþu;va¬¼«õJ|¤ ÙË>/û‡âd¥_öMš˜Iö~¢hYwé7ë.uLB{Ÿû¬ì¹‘8JnÆÞŸ0eöÙï·b%ö¡ÇÄŒ±÷M˜KöÞ…(‰µ´Kb"údÍæ¸ìI:!ïƒc$F¢Wb0M˜8Lö¥#'1$ñgòŽØ+qË~ùYÙ3mbÒØ{“|ò~xܬåÜ2{“ô»^½7Þ~׿0±˜’NÊþ$x]”%2k¬ô»bÝíÖû•ìX‰Ù£4nÞëµWùØ‘Œ ÉØŒÞdä$£7\ÉØa…L—݉\'t't'ôd¥Ðn)´[*|)úo&òS‘›ƒÎ Ú>ï4¤Q/ziÈMGg:Ýœ{7ø‹]„ìbtƒ­˜¶)Fw1ºKh“t”ÀSO)<¥ð”¢¿Œö)C~òË •SÞ€}åÔ¯€V­¿Á^¾ ðyÀàÁîJŽ+9®‚¿ = š=ÎÐËqÊþúqÊ/-Œh[{¼£Ç8áñ×èñŒÇèqKxƒèqGxÌkè1ן=¶ÐcŠÈ1„7„Ç zœ„ûÿ᾿îóGö÷u?²OûÚ}ûp?>²¯ûïºÏ®ûè˾÷+õ»#ûÜ‘ýíȾv¸ŸoÚ}ìpÿZ÷­9Ÿv¿:ܧÖýiΧݗÖýh݇÷ŸußY÷›uŸ™ój÷•u?9ÜG~Æô_óxtS²oƬ½³÷OÎK|˃f n¾°FǬ´÷r›XÒv i§Ä “½Ù^Ù'9.{°gdsœÙomïÕ˜ýÆz-–^«¥cÆ¥";uÜÄ0ÖûÓÝ: î>«³IèÌü†ßs¶~†aw6÷÷lꦃ=¹)`Js®~fR7ûª‹¶ÈÅ®\ꎛø‹…"·l…Ȫ_e‚Y_]Îjê”éç e•È«S.º*‘W‰ìj¥ZóQ¯–ònðvS6ŒíõàªGfúøíCN+r}´E+üýÔïGߨRÉg˜ßÔ• «¹õÈÛð4pöóéó0ò†‘; ½ŸÏòšÑ[Îftô"«¬ðtSÖKýV¾;ÀÓMy3²{‘ÛA›uƒ«;{‘ݬøFÀØK]m7#KÑ8½èí†6‚}½ÈòÑ#´y/rzµýüÇçµF¸VüÈÑÏ]¿|Ý”wSæ£ÞØG°Û¯¿±mì#`טFÀ1‚.?Çèîƒo@ëgßäàx”ãÑ#2¶ÔËóÅËóÅËóÅËóÅËóÅËóÅoÔùâ×Ë\±~Þ¢–ãŽÎ.‰;:-ñìwIΙ#’sÆ!kæ'%ÖSLDLû)‰õ´KòÎ6{6í=5N‰i?)ñGc÷n.äžqÉÎiÉ?ã0qªí8¤3²Ÿ3AâÛOJ|•Ùc3!ûâc$Æý‰E-±ŸÊÞøìwK<ÒY‰Gš {?Ç$‹#bü Ùkã’˜¤²O>JöÊ{Mœ–…½òÙ:#1ïwIÌûƒ’›fVbBí’}ó‡Í¾Q{ï|Œä©qK|¨I‰ƒ-{ |²'gFòÕÄJœ¨€ì/˜‘˜ø±/Ê#1£Ž˜8¦zo½Ç&Vâ™zMìn{Ÿý´ìµ’½©NÙsPâJMKl©(‰/å’Sc²·gVâéÇHÞÄ?bb‚ë|3öþü³¿Õއ}?S&Ö¾‡*Vb£z$>ê¸É³3$9rƒß+ñR˜ýA<ö§ó;yé§N›|6vŒþi7ߎ§köËÚ±ú}›|ÒÄ'·÷ý›xýöžÿ‰•ã21Víxýc&.¤Î«cdzŠ11Víýþ~É©3aöÞÚqû£LlU{ß´ìýwÊþÿ€‰­cï)š}E!ɱã˜ý^Ù÷?arëè˜Vv·;’žŒÞdd$£7\Éè²ÐåD—]NèNèNèNlKA^ m—BÛ¥ÀŸoáIÓ­O ™®}ºÓ°9ziÔKÓí2Ýý hèÍø3 gêø2iãLÚ%yYèÏ¢]\Èq!Ç 9.ð¹ gÓn9ÈÉANôè9Ðs ç@ÏCOô<èyèɃžÇùÌ;a†Ð hŸì)ÀžôÐ>nÊÝ”»)wSîÆf7¸Üà/Bv²‹Ñ] ¶bÚ¦ÝÅÈ.¡=J ×Q^O)<¥ü.Eí_†ü2ä—A+‡V­œú\Ð+À^q c<`ð`wå 3œ©‚· ¸üMc_ÏH,~ŸävŠ3ûÉí¸ûÓ’_Ñ%9›Üf߸Þ/®céë8¥vŒüq‰ý7/1HÇ%îhHâ¸M¾C½/ÙŽÁtÂä8Ôû;užcdoÈÄÒùtœw/ÅŽéŽéé»LZòÒú$‡Ø´ÄÇŠ•ü´‰m;%¹Äb–䑟•<µ‰q{PòÉÏJ^1‡ÄºõJ,÷q“¿ÈŽ %ñ´œè°‰iç²—ÜFq’ßÈ'ym'$ÿü¼ÉÁiǃwK|Ü#’‹þ„ä$‹•|·^ÉM6nâ éevÜÉX‰Çå‘\eã&†îÅ!‰+ïüI^“CÉŽO9#qu£%¶.Ø’æ%ý”É}«sšÙ±v£%h‚É jç”8,ùsg$·D”ÄàuJ¬£1ïHç=Ó1/uìzóÌŽñÅ9vÅJÞ{¿É#ªóÝë|¡v®‰S’÷>ÁÄøJŸ‰È31krŸY³›÷¤äÔÝ%ùϼ&ç„“wLòMÌJÎ ø3ý&ö—ó~LrMÌšØøIšN=ôdlIæw2¸’Á”Œºœèr¢Ë .'t'våÓÎEÈIAf mŸ 2SàOEf*²ÓøNCn6¦Q/ ¹ið¥CË€–-ƒ¶Ëø3 gêr3±?™´Kú³hr\+<.ä¸À炞M»å ''`† 9Ðs ç@Ïžç4Æ<ôä¡'z×Jò ÀQŽÚ±[ 8Çè/À7ånÊÝ”»)wc·\nð!»ÙÅè.[1mSŒþbd—Ð%è/§žRxJá)Eí_†ü2ä—A+‡V­œúœ§ èÔ­›l0x_ÉqÇUðV§ =UðU¥›ª‘Y} wj8W5ðÔ «uúz:ëÐYÏy®Ç./íâE§¼ÞY3,j@_ú¡7bO#67bs#õš¨×Dy³§#³™Íèl†Ö­…z­`i¥N+òZ)oC^¶µÑ~>°¶co;zÛÁ׎ íÈn‡¯Ì`íDn'ç¦Þ.x»àíÆÎnŽ{8îá¸ý=`î¡®Ÿß~~ûgͰ¬}}ÈéCß²5€üAÊ)¤lyÚ1Ozú(ôQè£c&÷„îCۺîÇèz|¾t,þrãïå<,¯}–ðø+<æz1ê/ÇYz|…ÿØc«ð¼~äX*r^éxI•ô8I‘ôØ(r]¹EÎïsíñ3ÊŒsÂãšð˜FeÂã=† ]ð{̲t¾é8åÿµÞ< }Ï—Îý‡Ç‘cˆåñÃk«IßôypGý]s-äê=(9Ñ&Ö¤wZòAŒK^ü$Ö‘¬kb%ÄAÉÁñZ¿änw˜vìXŸäÖX±Øã ŽÜŽy“¿gÓ¼Éã®sdmóJnK·ä)ˆ5qPçMI œÖA3ͨóŒéœZé|§{Mëˆä`€/“öI¡NRÈä¼Má“NùÅN“‡!]÷Á—é3Ó…ÐÒ'LîÍ\Ú wÆÄtÍ¥~!¼…È.3¹Årb$÷6bs!ê7¹ï^)ÿ•¾¿Û¯§÷Íw‹÷Ëù¯–ó_-¿søû¾sX~ßðÚ¿oÐ׳+j9o[hIÞ6îñÔ9›úgODäªG׊Éá0yÛtŽ€sNHî6xV¢c%2VÎDä)>bbÃ' s5ö¬ž0¹1uløx‡äÿŒZÌ=dç„Ó‡ä :,yˆ’«\k§%/D\D^ˆÉW¬óÜê¼v|y¿ÉÝ£s¹ÙùŠ¡op™xóN™¼{Áä€ßqÒ<‚7¢g£×ä,ÞK¥Æ™ðvŽˆ€äˆ8)9"8Þ'ÇÛÁ¸9Û‘½}Br EIÞ7Ékœè39òÁZ0oò¦Å{M>RÏhø’?8ÚäpØ{Pò6€¡l¹1&‡°Îu”­™ Гœ’g ݉`NË~Ú*y¥|ò]&p"u,¾Ëá)=hò é8ù‰ÈL¶$ð$éúÐ’°/‰óÄy°Àla‹EXȲ°ÉB·¯“ör‚=kÌtƒê‘QŠÌ\ôdQ?þTøË‘“ªÏ xRÁœzÄt•²à-®tý¡N:º2hŸ df`C&m‘Iy&ú3ÑŸ…œ,Ú<‹ë+ ÝùèkG6˜³‘› Žldg#7»²i«lÚ7—k5—vò豘r±7—º¹ÔÍW¾n'êçS7ŸºùÔͧnÇ]ðR¿ú…ØR–BêQVDY²ŠU„Œ&mrJÀ\‚-ð–ÀWöxKà-{)˜JÁ_Š­¥§L·¯ [ËÑY­ûk©?Èw¶yÀãÁnu=ÔóPÞ2nº‡µà©E~-vÕ‚¯lµ`©G-økÁT ŽZdÔ"£µÈ©EO=<õðÔÃS½Z=´Êûô7íÕ½zôÝ&Èjâw“þ­ÇAó¦ Ú ÞÊ[¨× ¦p¶`Ûø[±­ò6ÊÚNš.ª2í䃷ƒ6ê }:hŸl耡:Àц.äv¡¯ }]èë:iº´½Øt¸ï¼tM¸î+sÍüEÿ8Ü/^:w¬û¾ë¥£û¼Kû¹Kc’¼ÚÚðÈØ$ºßúJ±I"ç#׌GöA_®ï¹´¿ùrkÈÃ}Ê¥±Ktÿñ³Ò¯Ócáñ¨È|Úíœ\ ñÎFþŠ1“OüœÃ’Õeòêü·:§Ò*ÚkòöRo52Vƒ3Ö!9jô3;Ñ{.å‰.“Ck?zׂc?x“ѵùÉÐ÷P'œÎI“Ÿ|=¶$ƒ!}&Íô‡ ©üH£n÷É$ä§Á›AÝMðnæ8C?;áËÂî=ú·×äÏÉG‚ßÜ>öêoäç¡7魯•äs\¾ ä:ùèò83…‘ÜlêcSñ´™Îp¢7™Nê䣯œåè*ÇíQ ž4ä—ck9íWJ[¦4·«ž5å|Jô³ì5ଡ]j]®ŸAÔ÷ ¯ yä5ðÝ€ -úCy e…´¿Ù5úyƒì&ý\ÖÏ1¾»hëðÔèçukhç.]W?ïà)×ÏøúÀàáÓŸAì®cŸ~–‚­O?O‘Ó§Ÿ©èDN=õ±«9-Ô‚ož!è}àâü´Pˆ¶ëCÞm×Fý!ÚhˆöÒÏ2ÎM~fzÍm·ƒ:½ÈéCu{µ,t ak/õ‡4nti¬àÒòÁ6¤åj÷¼É;ðšq˜ý÷jóuÿ“æê^ëµÃÿݹº7êzáȹº¿×áé¨×G¾ËÿîÝßj~›ûG[ îE¾O_:÷÷˜‹ûG™‡óD-çbÈÅ:.ù¼©s62ÎFæÙ“æ¹+йbÖä¯>‡ópNHò|CÛm%õWÎJÎo®¡‡äüƾÕú™û©‹}±ÇNHþïèˆÜ¬èK@ÞþIÉÍ:fr8&¸MÎεè^;cr9®Cî:0®ÃŽuà>Ï!9©s¶®§íÖ'H¾Ç©ˆ­ÈÚÀq*Øèv Ï\uÈÝgò‰ZÐ6NK®Vdmÿ&ômš4ýÍÈÞÌñæIÉ× Ïx²À¹eÞäÏFßVÚe+í²ÙNôfa{Ú ÉÛŠümàÚßvpmGÞväí¡<‹k n—ä¢Ä¦xpÅsŸ`òHÆÃ®xäí™5yS÷è¾6zÜ|.Eÿ¥ÈÝKݽ`ØK{ïEö^d§Á›Aì€É-ž0frë|©Åó&¥î~  `W>ºŠ)+GW"m•8&ùS‘—ˆ¼$p$AKB^ô$lKB_ö&A·ÀmÑ6r,ì©V„ì ÚÁ‰>'x=Ô¯ç“By :SáOÁ†Tê¤R'•öH¥}Ru_þt]ì¹ØØD½tÚ§I÷Ÿ½¦+”‰ŒLh™èÎÔçE·=×N:³Ðég6e!+—ºEú|aS66eƒ9›ö©×c Ú»9¥”çbg.ºò)/E^>xòÁ“O½|èùÔ˧^¡[P¯‰òBÎU!6‚«zEZeEÈ*BV‘.CNrJ¨WŠŒÒÓý*Ñ}}øÊ§M×jÙ¥`î‚;Êô¸„²rÚ¡\GN™®\mS­B#å×½GÛ O%ôZÚ¥–v©[-ØjÁQ‹ÜZpÔ‚»O=Ç]´S¶ÕS^¯ËôoÙC`k ¬G÷Û±¥Z´=nAo¿›øÝ¾&xšÐÝŒîÊ[ ·€«E7Nš®d+˜Û(k£¬ ^Ÿ{ ßGûûÀÛAÛuÐ&àío‡Æ7iº•]ÈìBWººôøƒßÝèêÅÆ^lì¥N¯sP§—ë³Z?6ôÓæýÈíGn?öôcG?×ñ“ g¾aø†á†o¾aøºŸ­û±úOw^–Æ.5–Î¥½ÚÜÙËͽڜ—îw‡ûÜáþöÒy­¥sUṩȾíÒ9*ÝÕý×È>ëÒwɯ47Žo9Å9yÕ<ÁÒü‹y¦—›_ ÷É"çŽÂk #ç€Â}žÈ¾M¸O³t~'rÇõ×ËYŒ-+àßÃ5w×Ü9ó&—u<| ú9Ãõ´zÊ䪎åÚLDǹøB"P Þ,lª¡N)Ÿ,hõ`®Go´&¾›8Î绾Bl¨An›¦ÁÛ¤ýZ<=èêA—›zÐSsÊ ùnOåàëÐ÷íãèïÀ¶>êõRÖ…Ì!í¯”÷Ò&}§Lîô8d|&ÿ”ŽYbïÿŽ’õY±f}–Ž™ª÷ë½ûúy¬ã’üÿþ%ã|ê'õ/ë«usÌææä—†bòËÎk·æJ¿qÍ'ÿ·õ–Sß<ü3ß3ÖÎ+¼aÃU&?R³»2¹þÖ¥’‚7^üϺ™³,—õÑ'3Ÿ)úðYŸúÔžJk¯Ë9×wãAôŒÛz¾íç+.OþÈ+jÆ[~hAïñ—Îuǯ?ü„¡o~_ÒºÛ£~¿9Ïßvì>~óÖuÖ\Ê¥¥C7­±ÎØûø¯Ö¥]d/{üƒŸ‹/ N&Þ”ž”ù5kû'~¸ÿ¥Þ<ôÝËö¾sÏ[©oÎï·Ÿ™2úm—Zs‰|G¯OYÄ_øþm³ çyòüìÏ·\~«µÅØC}sž¿sד}/ïAknßÞc϶ÞfϬªhîx"8¹±«¡xGoðÐÇrþý¥îxçS?5çõ»WÝpõÇ?±æö<ð™†'þ`ß›ûë¶îNÝõ‡ño>}Mp2î3}+á“ÁC‡û¼p9õÌùýÞÞDgn¸1ÜžÖÜîÄÇ/®[´×qmfP]²ˆWì^cè8ÌùÿÞ¯mG\ôà Ÿ¯-øÈ¤uvå§¼ë…{­ãÑ_øÄW9žüÐŽ¾˜'Jƒ“ÕÓZ”µ«1ÿÞg¾ô§à!Ǿv˧3¬cOï/yôsöüø´Ïõ»­½'O»fê¶»ƒ‡ž›×Wrøº@ž¹¾¿êôòé~o…žýüçÚܳ ÷Ø oùÍ{²ÿœ|ô^®ÈDëÒñw_ºãŸ‚Wf\}ïܹ还ëàûï*x×­åiVèW©.^ŸoûÍ™gü9p_~õ„/÷½+cF2¿›úæzøþî ž~Ý—­Ðãl¾æ¦;ýñìçUtÒw¯§ÇlEÖî£]pyèCÁC_¹â+O}`9æºøãŸüüÏJÚ9ôð8g蟭ãçÖÌTüö´àäOÞwÏõ¿ ^qòûyk>xõÌõñƒÁ/öÝã?e…î[ö˦ŸZÇ×›»ñŽ/'¿}lj5¯¸fëӳŃß\?¸¿è«Ûw.ž‡6=“â¨Yô£Ø† ÷®]<¿¿ÝøòYßG‚W|â³®ÿ â¾ú¨¹nî<<½sç‘…ö ÝõÞ/oµŽ¿óìáNw,´ÃѨ'oþáÜ]Ö…÷?CÂdnøz·Ö™cä™ëåÎÏ ?òo¿™³BÁ“#•-ž—Óžm<ü¡Ò|GOÛòÈÎ5ã‹~ú¨¹>îÔÚ/²B·®ö<~nËâõñgÏU?^‘°p½=½ó[W¿/ÎÚmü4xhëîwÒÄ‹×É£æ:¹+еÉwúý ç7ôÕ’õ¸gѯ~{åÉÛæo ]ýÀ-U×\¼=j®“»Ìõl…>÷ÝÛÎ(†ÿ×__·ñÓ·îù·›Ÿûñîà¡g¾ôÂ/º>¿¹î®sdÞÝüU+tCãg¾y÷qëØS/>ùBÞeÁ£¥÷\u²W·»9ÿwÿǵοû]‹í~ÝX8>ötÕܱŸß»Øþjë¿<½p=‡û# ÷©GÍu2s󹎲h+ôa}#wXÇž­+?Öµ3x´wââ÷ß;³ØïxÌœÿ™\²ïƒ“÷X¡+‹»Ø?1Ïkä™ëáîJ·÷óVÈ?úó/ø×ÅöyîŽöÞýÃàѤ‡.o¼å«v™ó~ÏW¯¥äÍV¨y—nÚsð¬”òǃGS½•Ä.·—ß|6QÓJ¬ÇXÒムê3¾gw¸Óus÷6¤$tÄÙâÄv$ïÔû—ºjRÞ=Ù>Ø-P‰ýõGúê19È^[4ûh³6r]¨‰äí©¶µ¢°³‰ÓÓ7} {(éß+l÷ÏiïÐã{ö‹æ“÷i¤Äu;õHÞªúúØ>–ýïêå˜ÒÆM‰¡ZdÌìÒÆÿœN·¾ë½†”Ô}åÌÓê$/öh¿J1«$ßϯ²õXÛD®ûÑúk±¡ÿJ¿sÖDõqOŠ!%Fa^ñ1éR~¢öGO¼¢$Þ®PûÝÁ߈Oã9¯2ï]Qb–SÃ5õsÔe×ܸ¾bãÞ ËH^dׇÌJù¸|íbIg%Îãð·Qñ7·Þ‚ŒTRHn²èDMØ£©kßר©$žòËî³c¾ô“Ks©—Dz’L¯J9š})~’÷n9ßô…Æ…Tb§ÔzP»[ê3þæîúµÞ¤< rƒv×f»œçÒ?"—{¯Ró©ã߇xÎóÝî)ÇËõÏøœûpkæ¯&3È ÿ³TRHÞÚZƒ‹W”Jþ\ºújþ‡#ÄCó£¸ß*ìg ãsžn„gãÏä†%ýÇDÊéF]Å® T©8}w^Ê)“¹No2¾ç­~uárýÆ™&ݮܑôØ|²»Y+%‘ɉf/”˜Â.ÔÃ@}Æ÷¼¤èç^®ÛÉõäiÅeºY$oÃÑø.ƒþƒo1·4º ?å&ãwÞû57ý"×ãÿ:Ð¥[&É[QÇÅ ï+ÓªÖuQ¢sÜn´3CyÆç3ªyš(üÑ룲½Ùé¥]˜óÃχ†|’t|¸8oÿôubßc¬N´ÇäàLlÈáèfc„~ºú`‡ý½õrþãF{ü™ñNÎãÓ¼ÙÁa•_bbw­¨Zrí1¹8s*cM—a ¹îØzì}Û$¯ÿÖ“&¿íU’ª=(¬Þ}¯´o7™ä›ì0}r·“˜Ïµ÷£Ÿþ#䵫evúóu!‡IÖ‘¿eÛž AšŽrþÎ{.yù½ÿ÷žžä’V\ýÇú¸Åä¤À§é³ç«*‘kÖ'”î'y.C¿­ó®’Ô2âKæ-¥Ÿp‹ÉAvæËz >?3¿Û¼›ƒœ_#oÏ$ÿñ’¿ŒŽrŸ“¤\íœÖ í19)èþ2~\ !—Å|ÿ'ä”ùÑJRªÀûˆý‹Ð;·˜| Ézø–oL;Ó®C<É«àÓÈíŸWJ’ûij™•XºûñïRz›ñ¿`ÎàÊìæ‘âHÃÀ¾V$—vçõ§¤_ÀÊQiGn3þlx^ǧlŸ˜oqOÕáôÈMݹg@­’¿Í¿i<öC ÒŽï„|›ÉIAâSgÿ_Ç>C Lø¼G´Ÿ»õÕ"ŸÉJRÛïï—šKî÷Y¾Äb£ó¦â”éé›Ñ—4—è zb3Ցܸ‡‹:)It>¨³UÝ¢<ç;Õš.’«w;…žÛ.Ç?±ÿ‹±öJÒÊu½ûü;BØáʦæÛPŸËºí‰|ºÊýàÜðÎeÖ£ßë›Åˆó‰˜Ð›Î‰úœïÿzìsŸ°†\]k—Ñ÷ç¶°[êù†’´âÕ˜B^ø>õŸ -’àA&’«ã:ý~hÎ"’;pÙÜ37§*Iã®ÿº·õ)©‡î0>6ØÍczèjû~Màâ ½˜; ]÷—Ö·zô]a®[M”¤~O‡6X`&ÖàÿÆÿÂf>Wn(ôºJ¥fj”äW¿v&´~*åèõàLÒQãÃÆÿ¶Ïš]ÜB®<-y™ÓªÉíóøüîJÝÆO»&õðÆçÂο½Vω\I¬úúRÉíêQ2³ß”¯ÑÀ¢Yš´šÿr‡ñ»°wÝ¡¥ß}eñÏ—Êž´éÜÖû ¿³•ýúdÐëëãw!÷÷¯ô;5î@Tk’«‹Lév"NI‚”üšÐQ‰.¾¨BOø-w G.]_Ëæ'rÅsÄý ¿ä“\Ûq÷òÜkI½Ð÷mM¨j%ššÇŒG¨ÇùK«FþI®TØ‘±§½ sο×7òëj@˧Q¶!JôݰokÑYêË»œïì<\¦ÛßÝïHNñTåÈÉsJóK•˜žS`¡¡×îr¾Ft}Õ¬îmÁÇËÛ¬_ ¬ÚE¤sNÌž½w“¹ÿTõ`¸ië‘ñUê×»œ¿£Í°ãê/æqyœ#=á“óÚ¢2´Õy®×’æùþøÛÒev íqþ¬©™àÛeªöÞì"9 Fïù+ÃZIš?óέѡÂ?ûë»\è®°kgr¹ÖˆêGãHΔ¹®vyÇ•¤9ÔAºªÄÐÝQ=K”ç|B |Rtwß«µHÎèì*oÆÌoU ñùr}ßåü{ˆÄ-R´¿åß‹ßþIr†¯;V1KISœ îJ?C“Ó»œÿÿ·iê%è^Ýá—iƒê“œþëúýv¤‡¤ÿ¤¼ª‡ž —~[®Uµ΄”Þãügû@Rôý‡ÛÖ–ûHÎ÷WEmÝ%×å°'nÿ{B³·¨Çå`zۜ㞫„~(¢ÇOó“œ@÷'=o(IÌŽ*±¯™¸7Û‹zœÏ ÷Œ³HZ-øz©tÿðÍÿ˜‘œú“b X'å¶ÛxÓþ¶?È} ¶?»Çù»>¾tã˜Gä’ªn+’ì772ò.TU’ú¯vË)tVbæßOS÷Ês~r9½´²ÉÔÜ.ÿ윆ÿ:uÜ'õ¾æÞãüܲ9ß¶ÂNri„C×È0’½ñܪ–¥íĹrz­ðï.±á~ªÜßãüåç,Ÿ.…tÙ¶3õ’=©q;óíå|'Έ8¨‰G¡'îq~/03º^á,¹T‹N +ÉîžõÖ7òž¶BJïs~ªÇû½ÈÅÛÇÏõ›'ø“ÝbEí6ýþr±åàé¼ßŠuñˆžìJÿï>çó•Aäb"]øõÄ<²]œ{¿ýi’¤7,hÿø³°_ÿA‡ûœïáy·T#§«Š‘d×ͺ°eþŸrþ[Z<¥`»]âTÛåÓxÔã|î§2”\lÿøÓúi/Hvõœ®Úb}ô 1Êqþªæy «‹ÖNÛ_¾Ö‰tvÿ™Ã«yIþaqý»8/Š>¤ÛeÝ~Â}Îl>¾Ýï.ôÇ…Û3–Jÿ4ëãÛ•~{'Çl¸î‘îDüø>+z~E«--^I=wŸËƒç®B¸$äBÒ§ŒÑþ%YÏO÷µÍÈV’¼~‰ïS%î=¢cÒn·ú\õ8ÿížuÈïýˆ\ TŠtóʺ>ubÐÏÑr^‹.m0.Î…ø8BJpù¨îzÌ÷qrï[Å|Ò*¤”5H–ëÞ:eÿ9žÜ¿û¼žôRrA½þèL²6äô‰9 ½Êé%Ö]ƽ —p?îMÈÐ ;’ ÆÞ‹<Úv!Y“Ü'–L•rÉöJ4Ûס÷Û¨õ|WÌû|јԙG1n¾ßógþ¸ð£‘V•" ïpîuDÛ‹yŸ§^¨eeÁç¬ÞiúR[ÉWªý]þ’ôdçþhûw7¶‘»Yö¢þùº‘^D ¯·Tu¤'Û¿hv\ž“>à~Ÿ“#1_Cá†ÇwÇ™ ÿEðeúœƒÅ™Dó›Ð“—‚옄ççžÈqÑcyi¢}ƒzl(éþƒÞÿDw_É7õxÅCŽó!“Ÿ‚ô¬§9ÿÚóÕV> ôi@ ëÌ‹¶Øü¢$užýK÷щÊ,z}1î:Ês99Õ>ÝR7‚œ»¿zú¾¡Ä07qnè®JR`lżAë”Y0†-ë§¢<—“VÞCżÏ)K¢'H:ŒOž2ª¢¤+ß =fKÿ±”ëî!—:í²9äœj:ÃÀ¨þý#~U’,:èOÄ=»‡\^þìFW Û¹±½:%þ$éØîŸ#+on“çEð1ÜÍI½Âô*Úãò_ééV³¿äü:N®¿¦út9?7¯1/Ïm•çŒnJô{êD;\^&ºoˆûæ9®Æé×ïÇÌ!†Š]ö]ÒBžWP/fÌÉ×+·ïÊXƒv¸œt¯{¦Ê röu½¬¿o^!§ÏwH(hûII¼÷ô°Q˜·MÍÐ-ì+J9ÿƒ ©§DΦ©šœ¦Ç]+—Èù?™>ö„kKe³W¨ÇåÀ«æÞ˜½ÈÙë…±ÑäôÇA¹¶ã•Ä窣¦ÌT¯áº¢<—‡)óK' ºœU—‡‘˜ïéö‡&¿IüCÎóÅÑ Âl•™cE¸e¡ÎwvîBζ§ˆKät£uÿf[)‰o»Ãâ_|šÉöy¨ÇøžÿiÀ²Â.Áb?tÖ¹jT¥vr?sº²z¡.ôŽv.ÔŠZ9#Ge/'üíRÆÿüKÛëB‚„]>kœò£c%ù!óÙÚè±.îR¾Ù~\Ü£Í2ЋKi§K™<äÓkHëlRˆE[yèi’yõþ‹ô ë•D(ÉO»(³6ULþåö)ì —ä«ÇÂÃIáÁ†U®µ©E2³z®›ßHI¼D7n•Y6‰õG;À¯xÄøžÏÞ)Â8zÁÛ^Ð!3¹Zþöõ7%ÿ]ᶣЗ3k=x™µ<§zÄä!¿×¦•sÚ*æ]¨n7ÍÅþ>“­;%‘ ùiEú8¹¾1yÉçöº°Yß¼†ÿA2—9…Ouþ­s›éÊLVå™\ä×>’ªBzšÔ§Ég?eñ‚%q<á‡ÍlóÞ¤ö7oQÉÅ™—¯}Ó†$ê1ÈTqß–©^?VR£Ô‡BŸÈ~ÿÏä¾9ÙâúJRpú罟c½H¦ëÞÖݲªì¬Ì¬\ýΔeÇQžñõ̶Û<žÔ&Ì?"Ÿþ}UñÔ%qÙñæµéQ®}Æ×3lß&ä«`Æî®Ù­ÿtθ0pd¦c²’˜ >lû±n3~ŸaçA¢^Á€ciG¾_)ìIÆž>·ˆóe±Kä~®°CŸÏpù.˜÷cÇWè~èÀ–[ÿQWöÕ"LQfÜ[wÞ+xÊ3>æñ{—‚ú}÷¯4@ÈYÆpϸ6•S0ïÚK/l!öE3jSGwê3¾æ=?ùê÷×cI¬ËÚô…rÞ› ôë.öc‰‹fºaI ¿uúh*Yh‡ñ9ïjäþc³óI>_oôöÙt®œ/5_kO)Ó[Ó!ê1þæA‰N«HòãŽLž;~=Éhq}é¨2Ù/Û?+ÓØ}3ê1>çýâJwT‚oùMÕ’a¶}Ü3ÝŸr}ý@tæŠùO»“7qù”Úh‡ñ?ïÎ\ÛCOJ¿N=7jþMãiWÚ °Ÿ¡9Ÿ^í$<™{!¥OÿsÕëIyuFuÈ©sE?²ü”ÄÀïêì9U&éö„ñ;w~¯—“ûLjyœTÀíœJ|3?10K¼÷IÔ´|â¶XœcMO¥Ë·Ð“‡Ü z@f.ÇQÁã´œ=ÿÅUêaÏY% ží×ôÚar‘óð0<‰Á$ï¸zqCN ¬om6=FIt[:¯Þ`e:;ßEyÆÿõ¹…É‹¢H’É)úLéí9yÿæE/ÖÎ(ÓèëÓ¿Qñ?§Õ_ͧV_FòèsãR!§,ƒ?ÿœ¿XÒŸ>›hó^¼#˜jñ<Ú|zùÞâ “‹ìóç:5½*Ïy©ƒY,ÒéO{/èÖMúωü=‘&Sø¾WèË'LN²©9;¾õe’{–nÐýIú…×#¦ì¥=å÷¨SÔjHéS&YÜÎåî=îå¡b~ééÝ:‡î}¦$òûJíÁTöžõ™|díþc¯íÙA¤Ó$7R}ø!ô~ú—SÊùø8¼ ¶h(Χ›´ÄquO´Çä#ëG ó‚–ëI.¿gMß1é_Ÿ÷•Dvž©LSŸóø£<“ƒ,õÙ‹%Éå~b:»—WÙy©2•£ <“ß×ÖηøyEú’]Ž="ß=ÑSÉžÛÄzš²–þ¬C;L. ±6™ÖN;H?¯HŸ¢^|É÷2t×6¤º°›SÔã^gÔgr` §Æã:“œeªA$é?=y^»HÑg.óÀ½£x·3¥rîÖ«ƒ¡ã³Á¢4ØyŽ6ø˜ÉûG§$ßÔmI æçhQØ4ô˜Ù#¤ôã÷éÓo§¾ ù™ä´nûzïê$½ÎÔÓí ŠžÉÅäåOÏŒ§;C’Ã߉¥=³wJDZŠþVÝažçÒ•¨ Ÿ÷Ø»£<ãßiWzÀvŒä8Ñ ç’–›ù¬eгœ®JÔé[ô õ3™ýz&ÇøówßnÔ‰tÚjçuµ‚í¥™ÖÇ Azs9šÂüÞh—ñ;“wÉsƒûÞ[œÞ_ôJã÷ÂðsvÁwv_,×Û3ÆÿLSõ‚d,›1rØn’º÷é±fJ¢ÉŒ:±çæ(Sླྀ*ByÆï nŸ³O.Lý5í’æ9dΫ'Jyy{¤$ìS¨e­>ŒB=Æï v¿J²WÒ‡ŒÅ$­¶IDl;ù~‰í“Ĺédz]ý<¤´ŒñûÔAÿ Ësß’ìáÕà±Ç’ÔÏ]/^z°@ÑŸÿøòPÛ­(Çø|ŠºY[ËH6×C©¥õÃ÷Ø&ùF¯o2R•Éôöéç~¨Çø}Š¿ËfôüJ-yâd°•|¯tüäàüOzùñ==ýÀ›ñ~r¿ÎÞ±‰tjâêê[~Ú/éDÍË¢û¨Ïø›Îߟd¥·p¼|¤.6t³†«¢§Ï¶z¾zs2»gD=Æ¿t.§YÌQ#©CŠ‚G­‘þþtÐñ•ÄžŠú•> qC}ÆÏ´¢´m½®Ï"Yô:zíß$µÙº·ÕrEO?ž¹]ð#*îÝàí–mQñ3m©¾jÎ={’5†:RÆ$•Þ–¶òVô'¨Ã+ùÿœñ/ŸWdñû”k Þ' RôÊO°dÊä@õ åS©u¨Q,øÕä<øæBþSŽmñØãwEŸª>Xç[‘ãýèŽí0¾¦î/˜>çà5y.eO m¤X7)›ƒ;™=\¢èÙ1¼ s¤C×ÈAEûÐãoêÈa»v¯&YØ%ÎÚ²…¤ü6síК¯=;U"{¨iQžñ3U½>nE²Ø_9îIO×n÷9§è³ü,×¶=¬D²syÔcüL¡ËV¿@îãÿý0,$wаC)CLŸðLÊQr¾’ÙËR™ %‹7Úa|MéoÛkÁúb ÇãWòIJ×a 7-Þ(z¦•É]†Ü|´Ê3~¦Pöì^J Ô¬_ù‡¤fØÿukœ¢OHz˜{ø¼‰Ý~û¢Ê!¥/?•×Ö¼ñI ¹«4œ›$ä<Åѽí„ r½ðóFÍŸ4Xu”¥?ñ‚ñ[¡Ç½/Šyø9‘˜w¥{­_é çI/Š; ¹ž¸íl’rõ{©ß^0þŸ¯“›o.ù èW×:g·~¸2DlþöÊ3~ßK7(9ݦú´•­ÈIú\üUœ¤×VõbI™DŸ}ÓPñýxÈâ%Yý?Ësxaç{““nF3âR¯J>&µ®RuÑ%²i¨çƒãuBJ_298vfnÓÄ*¥¼.º7÷êd‘>qç@Ǧ•ä8Ø9šÐ+‘Üîˆsƒ—LŽõ|{æ²Í|ñîO£û‰=×ë~ðˆzMóÏ#[žš·×;Gžï¿dü?Êßékó:1{Hƒ¿ zI‡—Œ¿G±ÝWH «‡|~ò591üh£éÅÙB?qùByÆÇ#™v¦_&þž@“Óêñ@€¢WŸ-;)“¼u9²e2ê1¾ùÖ@_ƒ~cBñÕ9¯¦©gF™µRôËÕƒC¹^²GAC™£>ãsò±1'GLzÑp¬ÞË-Kz› í×z”üž?‡ÕÞLbzU®¿—L’ùûxm<Çó.·+òj•¥ñí ¹æã’ë퓇Ã'\[öÿËIÊ?;ÎüL±ßÔÏ®9fÍânJ$ç_þA}Æ÷Ã.#·­0l’ó;«>8ó;îžÍiî/Š~Á³¤Ý·Ö(‘Ë胱¨Ïø}hTç'Þ åñrH– ätÕû;=½*îÆJäöÍ5Ü¿æãÿÁƒ¨'vÏCŽMM4ýXKÒ¼bü??òöŒ‡AÄpÇŠÞÐctWÖ;UÒký?TB”I1ô±ê1þï§»¹&wˆ>SΈó<úþþòŽeŠ´Û;ìé „¦/QŸñ?éã¤[Æ£"¥¢·£­F‰ù=?¶ïŒ±k$÷чžg}&4| Úc|ç￈¿¯;z´ä›Vë"°ÞUý¤DªÇÒCBJ$#I„sª;WEÒ÷îçAý.“£;']ëwRÑÓk˶7Å»øÈß^åÝõµï1ø}ÇQºÛøBÒm—u‹M»fŠóÏÉÌF}Æ_•j?è‰îæu6„Z­%†jßP&³wT(Ïøº½w"†ÏªtœlOŽNèûºf9ù¡Ç‰V‹PžñuÏ2ú€PúYØý€D’O½þÈü®m¤/_|Hú¾füÞ] ~qˆdYуì[ä¨û*gåìCEÏü0e"“O”güÝ©~k"ɲ¹jYä{IŒãÈ‹Ç/*·TôÃVþÝÕrŽvÿ¡L¤Ç…'á7½füÜQeã/Žn~“==ˆêOŽ(~ô)5XÊu†äJýô†ñwÛÛa\9*ýX7úŨväÈ„yfk‹–›÷ÝMž»ÎŠïÓLbüF;ŒÏ[ù÷*²øy‘&óÇ%Öß{™D¿&vü$Ê3¾ni¼–žÐ“,u{öI~ºoãÍ==ž++Òô;Ê3¾nêwyĈŠ.$‹>ü˜M’÷þµé‡õX¯+èÑ÷‚>¿zÃøû;;Çç£YÔÌúÎú;yü’åúaýÑç+¡â=Îdúí‡Ð‘òÞé ãï:~?šÕ× –èIVÝg ø¥`¹jö å×LU/Iתíß“mL=5Hþ\‹¥+]¼TŸÑµ•ûŽ7Œß«¸Ÿ¥%æuø9ý"N´äW}0W_øK|]K»û–ñÅ÷êÃA,8½Ã¾ ‡³>/qh¤ßãUJYÛ(e26‰‘/¶£>ã;ý¶¶2’¾êqÍ rxEnÁ§}žaRבr?ÿ–ñ?žÝsË{|ö}#1ŽÃVö4=/ýµ?éÒ7rÛöPzdÕ7’?o™œ,R¿ÖÒX´“ÍΡɡ§Eñ]Góa÷±ÂÿãçÒ¾½eò3ŸíI6ý¶Ú°Ob¼‡fêæÏ(ývŸ(øå±–ž(¢&7Ú{£ììJ§ô×’CF—ók)…ØZ!wQôZjö·o™üð}˜ ovY¿ˆ%×å÷WÂi«Ø~>ówPÉÉHÓuôÄŒä°sAr°î‚GÑß+ú ºÇîZ!÷•ï˜tRŸÍØŠsÃÇY<¬¿TÒ‹ÝO+“Ù»xÔSù¯ô09^ñ1¾b<9Ý÷¬ûd¿SÐé@DÖêÃÃÍ¥_ɾ)÷§Ü/æ÷¦hW•%œ½'óÎùU=ˆ|=ÐtpïÞn­R´vù}¶8o|û;Š’ŸïTùP&¨îGS’3ÿD~¥*/È#Ûë¯lN–³GÇ?v*[«L}èüµÀõT9ÐÎEÿ97;L 'û/G~ºî$ÇÁæ'ÏûÞ©üWbX4KݹGÒéva®Uµ‘b~ûÕã„ï=¿Öì÷´iê4ÑŽ*Ê<æ—ˆqäÚR8‹ìgr*çÁüeíOQ_• eaöjxP…ò!Z Ï\Aöÿ´ûbÅÏå<¸?3­ýœFÕócBJÿQåD‰ÿ©úŸKÇI/7ZÕ(b<ûU jßè/©‡ø~P“ï©èÑhŒ­¯˜Ï_1Ÿ£+üïÂ]øŠùÌ~Â9mð8Z}+Èxö y<ûørñì±N*Åql'¬iãnåâi¡¬ðÒã­Œú•Ã9îkÇ|Š*+eM£8Ô;Ï>ŠÇä@_æ:Kk'Ç}E~Õðr¸¯èߢ§ŠñX¢?Ë0ü¢K´QÍ–ã¾"]íÇ}E{Õ8îk±šÂ1_‘_éH×4嘯OÍdÓí׊(‡ùŠñÔç±A d<-kÌÇù6:‡5Åb­ƒü:oЯNSy¶(c‹>lѦíK ô¬›ÌcÚëx<{äÛaLvÝx-¤ë#]üªþê£?{ôm¶ìwòXöhËe Êűf1[ÕXö:; ý8‚–Ž ‡c<ÇtEÚ åï”Ìñ¬tåâ×#팴3æï¼™ÇÌÒq×d¦–]t=ÆîÀñZ17ŒÇiw”w½Ü1fÐ×ùzŽÇŠ´g7ŽÃjàø«³W4Ç]}À1WAsï gÕÇŠã«¢ŽÊøšrÆÐ&s“Í_`´ïäíº/â^¢¬i4Þq/‘6­ÌÑ—y£¥gØU‘_5¢ö ú·èËc_b<–èÏ2œc´pŒv”¯†tµ2Ž?Å1Ú‘¶ åq/ óù5®aàøì!<æe îÆñÙ FDZÙ7süôg9Yc>ÖÈ· `q/moƒü: 9.{<ÇeÇmQÆ6šã²cLuÑg]гn ‹£^/€Ç¼4pLvÔ·‹g1/ë[•ÃcGöèÛ>ša±ÓxêhË!žã°7äq.ãX¬K$UÜ™†ñ _RÅ`=x|K¤PÞ ùN)k& \|Ë2޽Žù;£ç2޹ÁðÖ]L9Öz‹i©âÉ =W´ç ^¹aìn! GÆÍÀqÕ‘vàx곇Žã§c¼hßiOÌßmx‚¿^hà cöB/ôá…2Þ˜˜7hî1z—pÔ3õ£Æ\fãTYa.Vèߪˆã"¿FÃ=Tã­#]ã©iàñÖ8öaS]µC8ùNwúÇã±NæØäÈ·ÁxloƒñÕqc1×V9a‹2¶ñ ûÐ<¨‹>ë"]}Ö}ÇqË8î!Æd‡öí¶+âØå,Öº=ú³=íãÊÅYÇü6s¼CôÝù ÐvC¹ë›Þ¡#ÚrDyGŒÕi'¤PÞ }9xlu”o„ö¡¿FïX\uç(Žoˆtcä7ŽfqÕ]п ò]ïRÀc©£=WôçŠñ¸a.nÝX u7代¼;µu(ïŽ1¸£Œê{ ßóñ@ûžÔþaþž“'èç…6¼Ð†ÊxaŒ^(ã ž{£ oŒÉmø`Þ> ¹êø Œ/Æá šù¦0s C¾ãÒ%0,õ&H7A~¤›˜© 8Õ~x¬w´ÕyM Ì|4 eØ—oQÅXĘýQßsð›7dØ‘jœ÷#uZ€Ž- ˜É¡qô)ÆbòQ>0Ša5©qÞQ6é ä=`±ˆÕ8ï¨ šcìÁO0ÕTïÓj£©}.¹£Ùá/ñä¨('§ù¤åm+µ«ÔŽjöS³šÍ¤¶R³‘_ú«Ô&–÷W©ÝÓlžf¿4»¥Ù,j§þ›ŸZÞGÕlµ9š­Ñì µ-š]Ñì‰fKÊû¬š]Ðl€¦ÿ¿Ôûšž/¯ßéùÕïšN§ô§ºr)t4ÕÍT/—÷a©¦º·!»Š¯‚2Fá<¦u_ŽKþš€¿&úr²§*à}䙂'¦3ȃøo†~ÍC.¬ò,ОÊ[pØŽÿªçxñï5žáÕìÆ±]‘®Êbb[£ ëŽÉÚ¹e*kÃ`U1W‘oqÚƒ6»ÆÞãkˆr ©.ÁgŽèÓ) :ý6ÂçR¤x5 £=ŒÛ]ÃÖ©;äÒã÷ÀX<7ããñƼQΟû@}Ð/Æà ùðEÿ:Ì£ >oBý`ô凾ü>C3¤›!í±4G˜g€)Ä tcX±AÈ*c¸iÁè?˜òòë¹ëW¿7ºÂW¿÷Û¹«ç‹c„p %к"ÊaM!mÚ¡ýJ¶ o@Å*ã˜ß;cðÎã7A¾I8Ãü’‹ÊÉåp P¶ Êšb=š¢-SôoŠþÍ Ìo†¾ÍЗ9Ú2Çš1ãxC¦óéªàEÕwóã³ï-¶D¾eÇ*ãxCÑøMæØ“h¯zÇ DÚ kÎ*¡ÖP7ŽùtM¤k"¿&Ò5 8^B(ÇÂøk#]»Ãý®þj£?kÇü6p¼o”·ÁxloSÆñ†"æ·ŠY‰9Ú¢ŒmB9¼!ôYéºè³ž)ÇṴ́†ì@o;Ð×i»Ž3„öëÖ·ÀB]¤Â8Ö7ún€² n€¶ ¯†_C´ÝP/±…TœoÐÆi'7†=à„¾œÐ^#”o„òÐ~#Ã÷vÆØ1gð£1ÒAÆÈoŒ±¹ 什Þ.H»¢=×p†!äŠñ¸5äøÝ ?ÑiwÔwG{î(ãŽ1x í|Séžà§'Úðý<цêx¡ /”ñ*`êÞe¼Ñ§7Æä6| Ã> ¹æáƒ~|A_ôá‹ò:ŒY‡´óÖ!_ž4Aù& i¤› ¼æí‡>üö+ãGh¯)ÒMÑF3ä7ëËp¦(Ö‘Š/Žyû£ ÐÒch®cØŸ*ÆÚhq·ÀÜZ N‹"†mJq€ü@”Œfx¤ã(í!„ü w ƒCÅGÝ`´ŒºÁO0ÕTgÓÍVkö™Úå/m2µÅšÖ|àòv·¼ü¥m¥v•ÚTjO©-nKmgy_X³•_âÇhöÚ=ÍæQ{§Ù9jã¨}ûÒ7þÒ¦ý·³\ 7t¾2µIÿÍivèK¤ñR»SÞÎh6¦¼}¡¶åK¿¹¼­øƒF³ÔPݯéüòú½¼nÿoú¼¼.×ññ£íŠcEȉä¯RCü‚~•èÿøÌó1Ág& ŽŠFÈS|^¥ºõÍ6Cæñ;ÆŠáûV5pŒ_=ÇuC{ÕðyµˆS_Žã9²ÚÌ1}73Œ™Zø[ý×Fyë(ŽÅ†ú6H× `¾¶(g‹ruÑW]ü­‡¿õÂÎŒæf‡ñØáÿú [MÅTÝÂ9†Új€ÿ` Q¯!ÕcH7 ãøghßzÁshŒtcЪ1ÚsÁç.˜§ þwE?®É× cpOf"ï¹x`ÌžXÛž ›'Æë‰þ¼PÎ'¿èÓãòEû:ô©ÃXuàòýð¿Ê6ÅX›6Íð™?ú÷­šƒÍ“¦1Å Àÿh'0Žáà%3W”âË©˜fè'å‚Ñf0å?]ãô' Üÿ!\&*Tøê¯õ×ÿgøëÿS|õÿ_ýôPNÏ"Ž9ÊñÁôly…ài#ÌÛõ+amU‚üTJaË^ÅwGÚt7ÆøLoU²^ü®Œt¤«@¶« lŒÕë̲j ~›¢3¬{3䛡o3ôeÞ¿H›#ß¼ˆãŒ¡½ªh¯jS+*.(ú³ø?ì peiußõÍÖšžéiº[­ÞÕ»¦WukÔR/Ò{ÚŸö§ýiߥ§ýirbZÆÈ6!m‡Ô¨À€PØÝÄØšŠªB4ÃÖ“GÆ îÔ`Ò*t 1ùûÛý=.lŒTõt—ï|ÿ³Ü{îýν÷;gÑ^b¶ ÿο-‹öró„_ë‚®h]ÐV~ôßÊv ø1lÇ O üžLÔš l?Év,Û±lÇbŸXøoÃ>ÛgíÛÐu;ÛÛ¶–üöe{ ÛN;føÝ´5åãàG{Ü¢ÖEÞa[ÿlçm[[>> uAÁÜEŸ]ðÜÅö.0v³½íf{7ô{À߃½÷`£=lïo/ø{Wm½4©•¶{ík?Øû¡Ý¿l/›è{€íèsû'€´5@årzcy0dëÊ¥õÛ‡Ø>ïCÈrúÃrßÿðª­7vÙ`Ï#«¶öçÑ€ÖþD¶cð?КŸl‡ÿqðŽƒœûV"ò&‚—¸`ë–=íSèòí'8WN`‡`€÷Il}’c{Û\´—ôSô?EÿSМ‚æ4}NCsyN¯ØËý¶ÏÐç <ΠóYìu–ö³Ð'™„LIÈ”„=“éíç =‡<çÑç<øç¯ÛÛD2mÉ`%ÓžLûÓè÷4íOÃÿé›Z{”ãÂñOY±·“ à_˜±õG/€ŸŠÌ©è™JŸÔ5[/6 þi´§Ñ~ÑkëÅJ}ÒKà]—°É%Ú/£ße¶/Ãÿ2º\ë }¯ ßú:×fù“{òúšðîýWî½÷»ßº÷Ùõcs¹—Ê}Ô½‡®¿o®ÿ¦âï{NíÞÝû¡Ü #ï{îýný8=òöú{š{?‹|žíÞÃ~-êGïWî}ʽG­®½~̾þÙ6ÇìGž¹÷¹ÏÈý%òÞòzõ#ÝûEä}ÂýVïËõ_®ýî5?òywäu~ý3o¿ÖŠÄ?à\y½à<|[<¸bk8?ŒO?Ìþ‡Ù~„sá¹³ÜÔʆߣ¬? Æf–›×lmÇÇðáÇé÷ø²Ö_–ë*Ë'h{ßßšÀe çK vˆá||’å“,c9v±Ø)¬m`ncÿväÜÁ1ر¦µ“ÁÝ ÞN–ñÐÄC³‹ý»äÚÇr7Çx7ûvƒ·ÇË/¬õ9¯öb³}èµ»ìO´µ€•@¿ú% ëAödybý0m‡i;Lß#¬ay”åÑ[³øÇúËãЧ_"´‰ðO„ÇSð8ÍN ÛIô8‰Ì§à šSô?mNc¯3Øó,òeýìšuslŸ‡ÏyhÏcã§C¶†±Ô,NÁ~ÐíüR±Aê‚ÖËD†‹>[/Sj_‚ö2ü/Ëx=ÉM£lÒПl\þFÉžãñ7ãXý!¶!Ç!ðCììaý:áXaû(ÛGÙ> í1Ö±~lÁ^Zƒsœã«ö2›ˆ\‰ðMD‡DäxŠí§  =žBÖØ÷zœ ÿ °Obß“ô?…|§èw §ØNc×ÓÈtì3È|{œö 4g°ëY¶ÏÒ~–ã„IôO‚ï9hÏÑvÚsО£ý<§ý<çK2íÉØ:™öd[Óþ4íOÓþ4r=Ív Û)ðOáœKá<º@Ÿ ô¹Àö…U{[HE·TŽk*©ôIc; Ì´{»¸ˆ ‘ï"v¸Þ%lp‰¶ËØà26¸L¿Ë´]A÷+ð¾‚ìér”{‘ü¹÷Íõcæû•Ý{×úq°û,ZîîýAî ë¿F×`÷šê^ÝëŸ{½ãXýØøÐ½qÜ ú>€¾a»‡9Fcƒ‡±ç&Žû¦DcƒGY»nö 0·bË­,c°Íôß1gkuïbÿ.ñäÝ˾½Ø}¸û8Ø—öAŽÑA–ǰïqœ€×‰°½ÕŸå˜žïËóП—±Î¢­ žF¿‹ì¿ˆ¾—és9µñlïWùÙÞÆ»øŸÍ3>ùÆF’áHByN ‰þ–ß÷ùý€ßÿã÷wü~¨ßKñÏCbC aˆ! ×B³IßåC˜Íüˆ# q„!Ž0Äf«>CäFo¸¹âÃuÓpÝ4\7ÍNO‰# q„á:j¸ŽâõÔÐ1ÑA~\W ±„Áÿ þoðs\çZãÿÿ7ø¿Áÿ þoðsVçwáÿÿ7ø¿Áÿ þoð“ªßàâÿÿ7ø¿Áÿ þoðãÑïðƒÿüßàÿÿ7ø¿É×ç£ø¿Áÿ þoðƒÿüß”éxÿ7ø¿Áÿ þoðƒÿüß™'Žÿüßàÿÿ7ø¿Áÿ þïÌMÃÿ þoðƒÿüßàÿÿw¾Æÿ þoðƒÿüßàÿÿw¾±Àÿ þoðƒÿüßàÿÿwžíâÿÿ7ø¿Áÿ þoðƒÿ;cQüßàÿÿ7ø¿Áÿ þoðgŽ;þoðƒÿüßàÿÿ7ø¿3¯ÿ7ø¿Áÿ þoðƒÿüßùöÿ7ø¿Áÿ þoðƒÿüßù>ÿ7ø¿Áÿ þoðƒÿüßyÿˆÿüßàÿÿ7ø¿Áÿ þ/Ï­ þoðƒÿüßàÿÿ7ø¿<1ø¿Áÿ þoðƒÿüßàÿ27ø¿Áÿ þoðƒÿüßàÿr?5ø¿Áÿ þoðƒÿüßàÿ2ÿßàÿÿ7ø¿Áÿ þoðƒÿËœCƒÿüßàÿÿ7ø¿Áÿ þ/ß…üßàÿÿ7ø¿Áÿ þoðù–Ñàÿÿ7ø¿Áÿ þoðƒÿË·5ÿ7ø¿Áÿ þoðƒÿü_ÞÝü߈ÿË_0j#Áòç1pž[%곫yýN'Jß9Ï­›Ùª¹ nj|Òªó#×Ö}«³¨ßê$êwês6FqæHz5FYÐ÷1šÓ`VßDë·ê³6^qÞ]'®ûf'QóÌê3±ýnçºÆ, :W*lß 8ïµ}úÝΊæ6ðjnƒE3™ ß­ÏØïL‘ñË¢æ8HÐoxfí÷§ÎÜÉ}7>¯ï¢4×A«~˳ñžÀ«qͬÍ{à¼/HÔoÚgí»óãkšÿ Iß£ÏØwéN„5_™ s,[õ{Ÿyû>Á™ß¥cQ¯¾[˜Ñ÷ Ë Ek<äÕïàgôÛØeû}¬óÎ!^Ÿõùì;z'fšÓ¸iEs)Di>…4¡BúN⺥œïkcô[Ÿ~??«ï(Vô=E´~{ëÓ¹œ³š{aEó/Äè÷E~}qMßa¬è·FÑú­½OçzÎê{Ç¢õä5û~Cæ}:ßÄÛž ö»{'‹Öü >‹-Øç•ΜÏhýæ7Qçzí˜Þùö~EŸ_.Úoœ˜,Fã2Ÿ~wÔ9Ÿ š£á¦Ï$&K縤#W:öLGîtŽM:xÐf€Œè<àyÀó€åeé•%:zÑÑ _/x™Ðe²Ì‚˜Y`fq³ÀË‚o6´ÙÐfC“Mzç`çøæÒ7YrÁÎ;9r9Gr9Gò9òÙÎ'_¶oÙpÁ}r°Q¯x@S¯t,”¶/ÂöEð*‚W¼Š°E1r#G |KÀ)AÞø”À§”¶RÚʱŒ~eô+£_úúÁ÷#ƒÊé[ÿrø—#s4ÐT g¶«¸eC•JøWAW]tUð¯B·*誠«¾eØèj£]jÐ¥¬ZøÕBWËy@žíÚÈYÇz2×É:4uÈ]‡ÜõèSfò5 4 ðlà˜6@Óf#6jÄ>ا ¾Mœ'MÈÖŒ®ÍðnF¾æU:µ€ÑF+üZe‰\­Ø mÈÔFßvúµ£;mí´µ#G¸´u‚Û ÿNøw®Ú°« Ì.°º‘¥ún°z ïÁF=ðï/üzéÛ M/vêEÖ>ìØ¯Už ‚3Î |ÑiœaÖ‡±Ç06 aƒ8!pBÈ0Š<£È9Š,£è?Fûíc`L‚?ööœk ÂØ2Œ½Ãô #g˜}ÓÈ0;éY{ßwþ6¾ÛÙˆí7bûØ~#¶ßˆí7bû7>¶—¸þçÏËý;µ‘ëhå>¹Ž¢"Þ5.è÷|Ñú=ß|Äœó4ÍM8¯sÎctžæu}߯ßôÍØoVœïÌ“tîͼæ'ŒÑ¹7!û‹“Ÿ0^ó]Óü„1:×óšÍQè|Û—¤ßö-è÷èIšûèš¾ƒŒ×ïûíûç[Ä{È(Í4¯sD£tú5ûŸ3=QçŒÎê|œ[ër.k¤Dû}»;'Ç™ž¨ßû-è÷2Ñš)¨ß¼¯è·3‰:?'¨ß¿/ë·4I:ÿôš~xKs$¥éw€³ú-àŠ~gm¿µqæ­õÛÀûÓ™¿­¹}ö['Ò‚Óãä=ŒÑ܇>Û3«ó{VtŽO´ÎóIÓyîA͉8¯yW5ßR´æ\òjŽÄ°ý¾Ç™SçÅë| ¿Î º¦ïIWõ; ä×ùñ×4?ÓªæhŠ×oúuξK•œ3ò½¿óÎ,Iß«†4wÓu;×ɵ£ïWçìwENŽš(ÍSãÕ9ô×í<"'×bŒæpòkŽÅæY¼®óèiÏ^ÐùCI:‡>¤¹¯k¾Åy;— på^¾ÅÒ;ÏWæø:ïX¡/„Ö]+ØWA›ŸãPŒŒU蜎lUÈX Mx`gÈ:2zàíÓCX^–^Y¢£—þ^x7CÓ‚®™ð¯/K~£,øe—Åy– m6´ÙÐäОƒÞ9Ø9{åÒ?—s$ì\°s‘-rÑ)Ÿó!Ÿí|pòe™|œ >è «>ði„¦šx Káš ŠàS̾"xa‡b0Š‘£9Jà[N ú—À§ŒRÚJi+CÆ2ú–ѯ ËÐ×¾Ÿþ~d(§o9üË‘¹™+ ©€¦»UÐ^‡=+‘³’etUÐUAW… UèV%¶‡¶Úèj «AŽô©AŸ°jáW ]-çBy´h gëuÈ\'ëÐÔ!wr×£O=˜ È×€ü Ð4À³cÚM#˜Ø£q͆EMðmâœhB¶ftm†w3ò5CÛ‚\-`´€Ñ ¿VY"W+6h…G2µÑ·~íèßN[;míÈÑnmÝì¦_'ü;Áì³ Ì.öu#K7ôÝ`õ@߃zàßÿ^øõ"S/4½«6ëÃŽ}ðgœApÁDŸIúLb‡IÖ‡e î(¶E†Qd…ÿ(:a—1°Ç‚6\›o NÑo ¹Ãô cã06 ƒfß4|§Ù7Í>'†¿¼go|Þ3‰©dlåÆJ#I\$1ÇñnüÃyèÄ<ï¸qŽÄ6×DÆ4n#q‹¯¸±‰H,"ñ‡Äo¸qç‰W¬#$v¸Aâ‰$.xà~1€ŒûÝ1?çÜÝ1¾;¶×»czË»cx¿Ë˜]Æë2V—1º;>çü~ÃÆåøÁ}Çä‘ãñȱxä8ýìïضúYöÓ¿Ûôƒ×N?ǦÑ>ŽkÆžôk‘{ r÷³ìGÖ6~è=þz Èõ°ƒðs€ßx3öÒ8Œ` C;Á¾ øµÊ>è'u[M`Ï xL€ß,v`_;4Ax·"K;<ƒØ­¹‡Ñ¥]ø‚3 M;ü‡á? ÍvÆÖA°Fä~DßV¡Å~“,Gà9M/´“ôAß^öOb«dþ#ðŸs=&Áë]°Û$í‚‹Ž“´ ³ä~Gû#È9‚“Øvòš½%Œ"ç¤È ÷@d Á3î¨ðC÷QèÆgJ~È7ž0z†á5¬ÓÈ2½uï}õÆóëç×ϯÿiäGÜxv½ñìzãÙõ›÷Ùõ/ê»4¹ÏDmäM½yŸ¼©ÑZ_gFç̬EÌ™YÐüQ1š;5¤¹SoiÞþ°Í7èÔØIX7o&:bnú‚æîOÐÜ)a;wÆÉÝŸ`sJ'w¼æ‘šÓüýQ:G=¬sÔïh^• ÖÛ¹¥óÔÚÓ0JçÒ4ÿªÎ§iÕ¹475—jÀæô–¹œNN©$Í)5gçu:9Uã#òù¯hNÕ¤ˆ9ë7íÍ©¿Ójs‚;ókb47xH箯FÔáñk~ÿ9Í¥ó؃6¸Ì urNÅk®ÿæ\½nó®:spbtŽ_sÿ_Ӹ̉ÏÛ›fsšK½g~üŒVžž×\Vk:W>AsÎtÎüœ­ý#¹­œù>ñš‹& 9!ç쥴ûèS€\ð)EÞÚ  +€WçS!¿"ì_¯"x_„ÜEØ¢x͆%èVN 6(e_s’ 3êáUF¿2ú•ѯ ËÐ×¾Ÿ6?ú–Ó·þåÈ\n4ÐT ˲W`¿Jd­ÄÆUÐ ß0Ë*è'À "g¬*è‚üº:šáÛJßf‰û±[-çAylзÌ:Öë°M¼ê ©[³aO=ý둯ùÀi€¦«šFx7»û4aŸ&°š8OšÐ¡]›‘³û6CÛ‚ -`´€Ñ ¿VY¢c+r·Â£ ™ÚèÛN¿vôo§­}Õ†VàvÐÖIŸNdì„'˜]`vÙÅþnd醾¬è{°Aü{àß ¿^dê…¦ûô!köëƒ×8ƒà ‚3Î ú ¢Ó vf}{ £_š4!hBÈ0Š<£È9Š,£´a£1øŒÑ>þØSÐM­ØP.Œ-ÃØ; fxÕ†vÓÈ0;iö9c~ù“ÁýF>å7w>e‰Õ$NӸ̉ÅÜçâ¹ÏÅ%v’xIb%‰“"c#7rã7ö‘˜'2Þ‘ÇgÜXFb˜ÈøEâÎ÷y..ñÉúçá_ÜïYxd!±ƒ7H¬ 1¿Öñ¦Än,0õ£cüìWfLÿÏ»cù„¨7Wnï½z”’ƒDò«:uf´FÕ²æI]м#­šodQç„/h®‘[sƩԜ§­öñ \jÎßËñïÔ”š×¹ÝA›Ïß©}èµ9û$‡½Ì}N]³9è¥NŒäô‘|?2'9;äq.ä±?ï–¿,¹³3è“…\§2‘»¬ ä*„G&Ç>‡_>º•"C)mùð,†¶˜õjŽI>ü}wô‘!|*àWvºÓ^ –úzd)EWÀ>2¬@ŸZúÑ·_Öå> ]-|k‘§ünÚëÀëg»œ~d­G‡~p¼`7²ÝÏv3º96ýèØ¼ýèÕŽmò»m/¯Ýà W'´èä ÀoyÀk~ÃÈ=ï ä˜`ÿ|&ä~† &Ðo~köŸÈÏ:ŽM;øõôo·y;áÙŒM›‘¡¾-ì¢o+XíÈÐ*4ìkÅ6ml·c‡vdè§þíìo‡×<‡aÖɯ™zéß ßNp»ÀícRÚä¾½zÀA¦0ÀèA†^Ú{iïE‡^ìÒIøˆ}ø ‚3Î û±Í(zL¢Ó 8ÃØu¹Cð….Ö$ûF‘yÛŒ"ß(í“b°§Ø?%÷sp¦Ø+Œ-ÂÈFž0û¦á?Íö´\ÿäYüm<—ßx.¿ñ\~ã»ògóÏæ7žÍ¿yÇñ?í³ùĨºs¯S×!FkšA»¨ù¬µžîuÍ› õš·ü¶Öv˜µµœšºô‰CÏ8pãV×å´º®µÍ5oì¬ÖÕ½£¹­Zmn+§ff¢æŽ×úfÑ9®°ÇÞhͲõ4÷ÞÖ¢Æ.ô®k>ôxÍ!;¿.×UPk­FÔ|hÕzg«Z«È«9¡;|Kë>¤i½]ä;rKsÊú´nç‚Ö2ŠÖº»­6¯5Т5VHë ­jžÙ[çÓÉ‹5§¹±ÀI¼£uÒ4÷,íOÍÙÜìR›×©oë‚:yÚÃZ3bÑæ¤uj¦ÅkÝ´€æ¦³ùi¥Ž¨“Ç=FóÔzµžDHkù.h=ß5­«£µÕüZÛšÓ 6­Sg-Zk­aÃŒYÍÃ5oëý:9⣴ö|ζjŠy[»ôìšÖ£ ’Oóȃ‘´lëò:u¢´.0ýϵjî®y­¼¦uÚh??«yçW´f[Œ­ÿ”ì×<¸ËšÏ2JsZ‚—ÒªµQgl}Ô Ð\¸©ùp4nšÖ©˜ÑÚ©ËZ?õº­å佌¶¹//ym^\'wýŒæ¯¿nëª:yqáÎqIG®täJGßt®›tdÏ;cÍÝ=àyÀó€çËËÒ+Kè¼àyoÙ¡}&xYðφYØ0‹ã˜f|³8ϲ¡Í†6šhr ÉoöÊ¥o.úå‚Ë9’‹¹—\lÏv>8ùàäË66ñA¹ ¿^Ð@S¯ü´_¶/‚W¼ŠàU„ÜEØ¢ý*Øn¦­™JÀ(£KÙ.CÆ2ÚÊèW]2–¡¯|ÿMŠ”c—røWË6rW@SMP°ù5"O²Vbã*誠©B—nxU¡_4Uظšä¬_ üjàWV rÖ‚W‹LµÐhз½ÈZÇzxu`ÕAS‡ÜõÈ]ŸfC äk¯Þ Ð5À¯šFthÄFا ¾MÈ×ÄyÒ„mšÅ&È݌훡mÁÎ-`´Ð¿UbyY"SëMNµ%Ú°©¾m`´ÓÖN[Ûàv°¯ù&‘¡þ`vÙf—ØYºoÚ¬úlÔÿø÷¯™z¡éÅN}ÈÚN2€3Î 8ƒà b«AtÄNìcaô A‚&M9F‘gÛŽ"Ë(ícØh >c´O?ötS`…Ñ!Œ-ÃØ: f9§Ù7 Óì›fŸŒ—?7¶wãy‰ã×Çð»oÔ{ù§QïEb?εÈ8ωñÜøî~1ùñÛúØÍ×$N‹ŒÍ$“Ì}àÆXn|åº[¹qç¼O¹±“û>`}¬#ILäÆB‘q¹±Ä<û‹w^/Îqã‰mîÏDÆ2n #ñ‹·¸1‹¯¸±ÉF\ò‹K’¢Þœ5Š’´ÆýŒÖˆH³¹kãØG{6ÜéךmI¶V±<ꔼç»yw’æ>‡nrìvçÛÞ­KìµuÙ’ïhÍ¢­E¶ùÏ%çÝZk7m­5©ÕyøºÖ‹·5†Ï·jVŸ­-,uœz¡¬gÂ'óº­ë™·`kŽIO©–‡NWøåA{ûäݶ—uyìy{b‡RŸ­&u„ Ñ¥™‹±C>¼ ±K!ú”ÊøDÆK2¶®tÆÖ–ܲÅì/䨢C!ü}Ðäûm-¤ ä«@Ïbt(„¾˜¾çWm=µRp«Ù.†®9Úæ”õ£gõœ½ÅTƒW!û蛆LÕÐU£k´^­µVùcÙ· ™·ÍÚzR»×©Yáךp+Z—Ôkóç;µI‘!Ž>qèn6Ú îNôÚ9Q.Më Ñ/~Uë7'ií xïwwšÖ¯XÐÎàìgòí÷Þx­Í^úì•ÁQ.ÆÖ8õ\ÓZÎkZË™sòÀ¢ÖˆKÔJ šÛŸ>½¶N\ížy['NnëwkÅ­i­8x†÷áE['5yxµ®3:fGkÍ%ìutÆÖ·8ºfë; kÊu[çâû޳ï¸Oë1±ïøM­w‘hkK¡ßyÚKÐ%Ógk´•ÌÛúÉrlVlT©5‘|[ë'¯ØÚ)ìK¹­õ”ÁLETäI«2MëK€1ÑÖgËŽ×ÚŸ2pRWµ†2Çï|3i󬨫‘÷ ÛW¶V[¥¬C{åš­«œ 2ÀË;^ÈžmØp=èî¹m‡F•ì÷bG/ô™¬ûà— f:d‚™)ºƒ™ÍñÈ9á• N64Ù´gӞï ÜèrÐ+sµ >yôË£_ýòè—G¿<úåÑǽOb/è}ÐûÀðÁ×G¿Bt(D®BŽe™Œ×‘«ˆýõàèW„}ûDNÚŠ¡-Fþbô-¦ä¶¾•Ò¿”þ¥ÈÐÏ6èýìó³ÏÏ>?¶ñ#Û}Ë‘»œãÑn9r”ËìzdCÆÐ¢ÚAWÉz%òV"k%tÕ`µ_ V5òUÓ§^5ÐÔ€UƒL5·íp±þµðÀ+@{€ö€ÄIàîèP’¾õã1ѽ»àWÕ#G#º6ÆØa`#8ðn„or Á» ÚxMà6Ñ¿ ÌúöñkÁn-`´@ÓÿhÚÀo£­ýmìocÿ8vl£­úöw°¿ ¬äí»Y»ävˆåºw#S7òt#OP~Ø6ˆ<ãèÄAYB×V8}àôÉ1£ú9CÈ2„,Cð}e9†Öìx#ðA¾ðG8¯G8gÇ¡‡~\d‡~úñÛv˜†pã‡ÈïÈݘAâ…_¥:†‘ãæõcewŒìŽÝqñ–¨ûƒÝñ¯;öu¿M_ŸŸå~cZ˺ߧ˸UÆ«÷§ºãS÷;uÔý¿S¯¿Ï˜Óc¾Þóhw,éŽ!ÝñãLÔ?‡þ°ŽáüQoº:É`ÄB“Œ®)ü¶yY†l=ÕØ/%lë9]¢-ó"í¶­Ýu%ÚÖn—Z§Rw(ÞɳZ³ útäKG–+^[?5Þ{Wìc}ôó@»ÿ–­}뙳5¹²87Óùe¡“‡ó> ^™`eݱr¡¿ß\öûÀõ!ßElãC×0SèsûúÐ'•}El!G&´E𾈠Jàu…~eè^Â/Ìt󎌠­bŸy|èTF6ý*éSÎv6ú¸ïÖC˜³—žzÚòè_†¾Eè™M˜erþ•Ø®R°Á¬£ÝK +C×6ÚKä~À² ¾M´•Ñ·º6ú•˽Kä{+üzSîE‚…Î]È2„M†àb;€¼5rÿ‚~Œ¾cô °Þÿ1¹ɽо=ðBÎ1h›ìå/N¶ÂærïDŸ1äìdëkŒ~cr?”û zÓ§º í’{ëAì5ÎqèCžqÖûÀ☠¡ã8Ç{ìqì7.ý9N!0Çå$íÒö8²a™Hû”ׯQÎßOòîz÷ó|þöf|öùÜíõî—¿¨gn?Éó¶7â›eyÖy_þÇ?ûºàÛÃq –`bÿrô.Çåì/¿m‡c5È5vH䃶Y+e½*¡«¦­¼jxTƒSŽÕèXŽÕœ;5àÕ S 2Õ¿þµÂ¬ü` Ñ7 cv¶»$& O=}ë%†Aμ†?ˆžôo¤#}á׈!áÙ„LMôo¢oüšnÛáa}ƒôiwœþ-ЄàÙ"q ømà·ÑÖÅvÜ6ÚÛ$Öz‰1Àí£ƒ>`vݶCËôéFŸn°»é×ô™Jˆ²ãmùsã‰1"ãŠû=órc€õϳÜo%e¬í޳e|½>‡/Çîg]ø¾Ïj"ŸÑDæÐ½ß³w<ã>™‰¸×G>ÿÀ®?‹ºÇ©œ;6d»Â¿+ȼ Ú=øü°Rà¿öL0€Ÿfk '³ž"×EÚR‘%¼tŽ©s©„sÈÃñö°¬‚‚Tݱ‡$O®ôõÑ·I~¬wЧœe¹ø:¼›d ï®9ÕãþcÐÀ£½ž>-´·È9Éú:޳G¶ñk¶®x"ý¦|Q?õ_úãÞפž}>ïsŸøá[=»/|ýµ½Ù§=¯|ùÿþ~Úõ"OlÝû¿úÂ÷_YZ<3õØêÎÿ’±úÂ÷æ?õ7ÿb=ÒÒ'·û»‘ß~‡ç˜Ïû—#˜YšIÞé×?æ÷\°ÛÞ×vGY>žßýFÞ·k<¯üÑ˳w~biñ}kþÔ²;KŸÜÿẔ\^ººòÃáo>ÁÒ'}ñ‘Ýy^yÇž;ü¡ß_zî¡Çþîòƒ__údÜ ;}ä»K3Û>z"©ú<ô^KþÅÚ?|ûg=;ŸýüÅ~e«ç•Îám¿»Ã³u"¦¤z_ïÒso»²ý7s—nüðŒïÖî÷vº·-ÍüÆgÿ*ㇻÀiµ8;ŠçZùžg³kœ OÏwÿë]{<÷Ÿîî›ËxõÄØH̯ZºñÍ/},fíÇgõ[šùôÐW¦B¿ÞŒƒ÷Ò×^{Õ¼kÂóÊþÿXøÁ'XzîÕ¦/}nðÉ¥·2²ê¿Õ±4óNOƃ¡/@?oé?vôæWß·ÙóÐOÍþUðÛž—¿÷ýñ‰Î»z<¿ioåwÞ·'ãkçê2ZþléÆÿøÀɾü–f?øßžK{/8Ë癿üÏ>wO—¿:ûäWOÞÕãùÓ·BÏf¾|OWO^]ûíÛwãÕ¾eÛÉêÿž=O^jú|Ç·3û—ÉKŸÎøVEà÷‚K7>äœÀKWíq†Þ÷ÿ¬&û?Ùzï8&½{ï[ã^¸{?ý;;FŸ£ÿ ½ÿz¼ç€ÝO{¼øþÏ~!¯àÚ½ãýž&Žæ-}úæºø®g7-ÝøhÕÁåœÄ»çñÝãµÇÿßúî÷ÔÔ~ß³ú'þý¡§–>s°ð‹íÿóÁ¥×kþxEÕÒÕ«ï\Øõ/÷Aoï‹}ÕÑ¡'7{Vÿäb ¦½ô®½>#G½ay鯧6}-gÛÒÕV<ìR”÷µ½ö8¿èÿó×Î]þºgõ·¾»9qê[®¼KŸyÏK+Ë_ø½{ç×óÍ)žþ­»ç×[¾¹t£¡ÖŽ=î/z·>?{ìãžÕ®wÿqÙ–gîñá¯þÛâ¯Þ;owõ{Î}æ»/|ã?/½%É”ýNà]àØãþbê_ü«÷â+®=«¹|qù7c=Ûµÿg¾ù‘·ÿõ7Î,ÝøÜôG>õØVÏÿgï;À²:ºuÙŠH±#X@Aº"¢¨û³+¶ˆ;vŒ&bÇ ‰1‰£1Ø1Mˆ‰ÁX°€h j ‰IÄÒÔ˜ÄÜwͬ½7ÇóŸÿž{ŸûÜ{þsåy>ýæ›¶ffµ)k­6<K–ˆ˜eç^Ò}^¡=^÷Fö]W}qÑ\‡âæ;XÿÀ„ï˜ËC§¿aóø¯Û~›õŽÁý- øô™Q~Ð)¾Šv~P²÷!½Øå7Pæ{âXPý/ôÍ";Ó~ó”~K_øl–}.Ê+|(ÛÆïø _/º&^‹=<ôþÊ"{íÕñóóF‹%g[œúüÖ ”Wë_°ùáŠò%AzÑöw>NË}G[út¯ˆ9¬y|öEŸÍƒŽ‰¥ÝV«}õÌÒ'û@o‹æPÁÁæºûèò^û/oYüg¦Sƒ«‡FêÑkfu{sÁQ±4ýÓí ¶ê­ zl¬ð¤@_|wcÂ\s]Šb.¿›0úC“Ž;UùÜîÒ‡| ÏdyçmÓýo {éE×íbé‹ñÉs‡}kÑec…75Þí<£ïp½¨Þs¿;˜ ŽÈ\uGdO=½`P‰ÞRɱTá ê)<Éÿíä†Qþ÷ô‹ŸŽž:i“8~ôÌé>E."{¶]µo[óßXáAþ‰!«+fx¤_Ìš·oNÁ!êϲ{qk{‹ŸÌ_ýÌVÿšz(kq¿ýv¿ó±è»±Zÿ|H™{¯ô4çãâÊ´®¥'wwþ›ë‰)N‡œ>-èÝ%O‹E‹~*ÿÖ±:ê+|ÈŸûVA/‡úÅÁµšºFœ"sÅ´Ì;=Eö íÂØ-ybѨťéï}‡ò òcüt[¿ýFûúÅfß|^¯pfç_y¾ÅµÂ¯jiYÖü/lþ|ßî‹õNŠ.Ä¢ßÏ´òµD߯ä§ý¢]…/ùž/œ>¡_2ñåÂ/‡@q'¬qüù\õ¼† ú˜.ˆÉZµöÎÈ`k]›(|9ûiEØ2ß ú…÷"×%µ°ó’:"{ĤG?Ž5ùÞâ²gïýåó7ê)|8ûæ…í+¿ö3ÇyAŠ«$sœ¹ '^>5Nd÷z·îÂ/cL¹½8¤Í?mªŠv~œM™ú}Ðדô cïîçßÞÄÿ\ÿOÓ½î€N¡´ 7Åàb‘âϨ¯ðåìÌ6N]ò}ô ®Ñ ŠÜ¿xÕ í(²ýºä }¾kûòØ<”Wøp¶ûÆ€Ô kô Í·EŠÜØ[ ¼—ôÙ^_úð‘“Xpwñìc-;¡¼Zÿ³Õ{ÏoôjMý‚CRnµÎ€+a²ç-×:Ök÷D‚)ׯ_y:ĉæIáÃ0ÅY¯®7×íü­¿ã: >,r“zoû´»ÈUx-Ù\§E†ãÔWë~†°-"]?_$Î qÅú¼:âáƒáÝD¶Ï[}ž9ÝP,RüÀv§©ZÏÓ}~,Íã–~þ­÷ûCd˜ø‘{ QÎëÓ¾°ð.tI.½óWõ=½¡qç[~cÒÍù¤®a>p³Ö'O»1¾¦5I0%‰E½VùÖ,ZŽvÔúž&©æÙN?ß½˜4I‘{uèW/ìn#²{¸ýUwC,"mfôy”Wëyº¡$ý¼wÞÖ=7‡ˆÜo_ìüóªú";æ¯7.Ï^Z©}µž§«DF·˜ý§)7Îk÷ÚôÛ£Öøz9ô¿øZ)¯ö¹‘±Ço³íŽ·Â‹<I€ú¹Ä ùÏh'N8 ÚаFÐ×gwyö‹H}ÜüÊ«õ?uÙkëäßÛèçúÕox[œ¨»ß>1ÓId7®{óèå‘bÑž›ƒï_¼†òjOm™’rY?¼£ÙëçL¹}ÂïCß¼J¬ys>Òý×±bµ>ºê«u?5a̼€×S þ¡Ÿ«sœ³¾5^ý@פޯXò_­ŸÞQñ±È¿fí“ 'Xüß[áÇ) G­Ñ÷Lú+ü­ûÏ_vXðí´¤½û)k™ ½báÇŸË¿½¥÷z+|9ù©Sò{]«ë……ƒuµÖuå¶ÒÚ‹‚­ñ¶*õü<óUCN‹…¼ž&žx+<9¹<ý§Ëw™ë]øZÐä´a—¬ñgºþ~sM? Ψ­ƒì–\1åö‚›v™Ï£=…?'IÝ~£Ü¤ßÂÙú ?ý†YãΟ˜ª\EdëÅÅSÖÙ‰JÙîø(|9!Ѳµ^Ø«GÔŸ˜z݉2=1nX–Èî9æùfƒk‰µ$a¢žÂ›þënU=£*¾+NüüÑëMãÁÇûß|éË牤?ccêW€Ïø(¼É=ù Ýû;#¬qWñé:ûVC ο®Ž¨³ÎÒƒ¤:Þ_,Pò í(üÉ»à}`g…9ÞÞ¯¬Öÿ×á×Ò¬y‹½~óÛ¼3¦Õª8Õ×sm¥õRë}ÌGn¸õ‚M®O®½Jœôñþòþ'D6q Ï ‘4uåÀ¸Ûfj}ò¾ßг VþÞ[yq244}îßo‹ll²ø)æß œvëã)¨§Ö÷H‡„ì׺Œµà…ò6ÜÇÞÜ?ž´…ÇÌO¨oÌ·¤ôq‘ÔÛ­‹Ë´£Öý£iZ‹-wëÌÿNjg¿Àeµ5N— Î/4ôy oš©õ>üõ€ŒÝ„^|®î¤ò—ÄÉɾ½Snn°ôæSæ<5Së{øÕ1¯äŘü @ÉSNž\2¹{©GÈ™£åƒ£M|Y°…þ^C{j½üÛ~Ø’MÞ±>`®ßºÈë)s-ükß­ÁÛyÖþÁîz¿Žqéö>ä¨qêK;|¹MœÜ>¿ÿ{cDvç嘅–œh¦Ö=Gé‰zó-Oß]}¸ê+<ÉžðiÌ´’Ïô|(ym—N§ÞKþ)Èß 3Kÿk®ð#kÓŒ³‡ôµÆ["‚8Õ¿ÖÂÍ/‰lh5.‡îšz9ë?¨¯ðáÀ¦+åØÃþoÖÛkª%ˆSóvÙ°µÙaÇkÜõ_oéåÍ>¼óÇËÏàíhÑ¥ã©_ÊÚÕ0ùÊ©ôéÛÂúYëÈz¥Á>=ì©é½ÑžÂ‹·Ç j5[lÑe³½¿üzÒœ÷S‡Þ쓾ꬹ?1ö9 <_݉v¼™OŠØÇzí|Òí{ûÅ© þøU;máéS{&-;ôš¥·7Wë½àŠý/»|©ð>ÊÀ›SŸ¯n]-t}¡mfœ¥G5Wëœ9 ï¬™[¿±è’ù« ÷w¾´»óm£¦OC_Ü-hϤhO­û¾-^§ã&ö°æ÷­§ä1RkƒJ"î¼ÖÀÐs¬ý^ …û<2gÎùe<äKÕð›ûnXëòèÕÞv‡¡w_v¹ºXò Üõì%îé>ߤςw¾{þ¹¶·L~›çüzùÁƒÞ"[íßL>kž ´Pø±wäw.;~}ÏÂ:mš•nÎKž§ý‹ö|L:2öcÖ8^ì¹ÕuKéÊô‚¯â0Ó³D^ÈþøC¿ÿeáUóÞã}¿L‹Ýޱ[ÿõì!ö³ªL/¨ð"Î!òz9'¯[1Bd7u8‘ù¶XDÔÝ|;Ê«õß½yÊ?ÿM/x‘\VCä{ fç2ƒ_4‘çæù*Ÿ#Xû’ výývc°K?Rç7"oQ#;÷3,½¨ùÏ-‹ýwçbÑõ /VkñË v½¾ð|ÿßõB”ÞñÅa‘÷²dt";|Òe¨ðbñº—½¯½ùâ§Ö}WàЛ~ö½^¨èË\÷¼7/w9?ØÂŸöß}6ÜÕE,þûvÌ-~ê§ð`gêÅÄ,Ó S®ŸøÉO&=äß}ç³6}­ùo÷˺!‘‡M¾¿$áÌK÷ÊЎƒ)·rg5<¬œ 6Cäß=ul8øÍ~÷‡bi‡%o(Äüù©õΠã£Äz¡’?"¯¬Õü/ú³X6MNÊ«u~cʯ7¾ØÐËÄ×Bb³÷‰¼ïoZ|eŠ5ßJ®šx–ì6íÏ‹âÐŽZÿíõ gdtìcÒ±Ÿ1ôÚ¼û‡ËâÅXó§ä¤©/%;M¿¹ÔÂ?…Ûf6!R?²íÜ’.óÄéª9^ËPŸŽá>°Î§ýÔz¿¦ä¾µŒ ,?Z›I7§ëì~ðÌÌj^Nñmž9ÉÄKæ¶;-^láó7s|óêî.y/×\×Ó­Š>_ÚÑçýÃúZ¿B{ ?Òíö~»——~ŽN+§w§ûŒ{äsm¶ÁÄòº®ýùålæóæs_…ŒþrÔ>qúÙk{*¬õi|;ïçD2ínßD=…¯¼´ NyÝÖyŒÃ—[Ó¾ÛnÍÇÞ©kß¿ò|篾¸úÊù·t‘í&7$&_L®úÃN§7w£=…//ý´ÿ[3ÖZçþýþ¼YÏMœ¾‘²ð`óÕÖúÖO ¬âÚÛ:—§ÑϽjÑIK…7£i@£M¹{ž²—­gÜ¿Òü›—-:‘˱ؔ[KsÞYÝ$í(<Ù î{L}áü௮ú®ßmÒïu`é/^ÚUm°ŸXfìŸ[*üY§ö×<ÓýDÊÔïÍu>3yL•ï6»Xz7Ëe;½Fš›íŽ¿Â›çÕ~Ѽ?jlêoËQxñ© ë<Á_áÏXÕE|¢žZ÷µJ·îÍ~‘À"äöu½v,³ÖÝ¿¬àÚåžz—Ñ÷†ŽtK¬r=ûâÓ^±ô¬VjÝ7Øv¦,ýú+½X’wWó^$îÖ*&mÙ-7kê=+Öç|tÖ’ó­l j¹·]äE½ØK*r"ÿå‘ï׸m­»Të››÷«'~{çÔWëþ6µsg|¡·n¶ñ}ŸÈWï%Ìû ã>–ù)ê©õ~¥vµ£Ú®Y¦Ü(~Jì‹üïFv½kí²äñÁR±J>OˆB}µþ›Z~ã3,áuë^˜õѵ?2ùFÖ÷›DE$±òÅ_úM«ßÄv'PáÃæ§GkVv4é¹xiÑíESS> |à—xÝ\Ï,zÎâú¼‰+þô{ú­ÏmÖym Â‹W¶íX{u¼^ îâtûEQ°jrà»UßÙêþ@¬t)û¼$|(PáCºâ+&ÝŸ(yýl‹“ æ:žk·ù5çk"‹ß³¬ú‹.šÏ¡¾ÂŸ§¥ ×/ý¶rOô‹}Ź޵Ÿ)èeêkYòX9ÖÔÿVÑ+“~ê+üÙ¹&tøó_ÔÒKø=Æ9ìF¬}ÕXsß·êó¥{ô}ÇÂ÷ …/»ÚÑÃ¥Oô¿+ɘqnßMǤyk,¾tµß2·Ë¥æùÞjymæeÌ¿íN°Â“Ý­îÖy%|‘Iß%t|á°Ë\ßsEwo/q8jáßšˆbMÔšá½ýɘ/´§ðe|†å¬—Œvëï8qîçùËmŸÙDV):šüaÍ[ŸëG>yÏW°Â“=‡Ç¯;V»‡^BÛ㊻â|Í“wv.üNdå´|èéú³XóDÝEyµþ{«86 ‰ÐK¶ÓCBqÞ?y`ÁýP‹¿fý¼è½G§EJÃû —oA=µþ{åôÏÕKκŸ ³Ý”Kçm&GÌQï‹IOëWÔûUqÑ×ÿ÷_ûuY×^9£I(¯ðbf;kýÊõ³Íl".FWýʵèg‘…Ý §Oˆ”õ¾ˆž8å^ì£Óæ˜Mú•#ég¿ZÑE\´Ð{Ø‹£,~–ÕûÇošçì)„½½f[úE¨Â}IÃèDßßúzÍ÷”¿)Ï/Îx®÷|-~ö^×ê.ëþ´èûËÖŒùé‘uΪðd]—õ:¥_ù-ýõŸ„šxwñùV‡Öý8ÅZW:Þ[ºÑì›±¶Û¦å&¿ø¸Íõ_,ì/.8dÍ4‘¥î'L¹™¢àF}µîûÚËtúÇ#ª”Ù]\¼{¯¶mÞD‘%¯Gˆ”}kNú%¿n»¦Ö}ŸÂýc©¦ÍEuZ6o—eÌ›Á_Pžù¡Ðýã7y'7]µk½-béz ?_°øJSy@cÉõ0æ|>ú±|žÛ×Ä÷¢átP°ÕâïÄ®O_5÷ŸÌç-þÆ|Ãû‡õî¿yéWí%˜ëW´@ø‹,`X¿H‘Ï–±o Së¾ç†T,õ«átqýH¥oZ¿uÌfs?mÈyxßeÀ‹vÔzïyïÊ•*}"L:¾:Œæk\ïŸoQÝ{©÷²œ5åeê\ynññ0…{Ș~uÑX¯Ÿ*EÅ·7þ=g’)7RW~ÑíÊt”Wë¿'6Esߥ_•èUU}ßÓ­ÔÙÚOž‡Ôtàø¡íN¸Â>÷7ñîêñµ'¦žŒ6籸êê3Kµ±èŠå}ã!ÃañÍpÖ†,^¼ê~õ³·(ûåœ9ŵŠÚŽº9 ŸmN7ùxê®Ðîž·ð%œõ†‰a¶{Óô«¿ËQÜ,©ø|Û‹ÿ g¢¯Ý©'wù|é^ÔSx!ŸÜûËäŸÐ3Ÿò£Ö¸"78ölú†5?×;ú`Kb­KɘϪ´µÎqÃÞìRBE2çë“ö¹Ð8­ûõbÒn‡ZtqîRóÞã›Wš…7ôú=蟚¾àQ<µcÕ£ý|E–´Ü«‰H7‚ˆDyÖ+|ÿfΞ‹ú'i’ˆâÕ—ë´»`ÈC‘JÇë?½‰ò v¨s'ýbïCO[ãÞ•L~=’ ý*RÆÈ†<³Ýi­ðb‡ÒûM¼þ¤bˈI‹â“Ò–ll!”ú5Ó<Ÿ]Ãû|[+<Èx§êµýCšé¥ò@mQL×´Ÿf[ûûàu­zô)5mw¨ù5ê©uÏîÌ[jÎsi¸¼hÅüîÍÜG:œßycÜ‹Ï+:·äGk…Õ:ºnާ4v+½D5×í’C€ÛÝ*,<—Ïñ:™ãKQçVÖ:¶Vøð†º_ÑKåqÜ|sž/Õ­?ËnƒE÷|Àò õlç÷È¥D¾í~—|WÛzv Ÿx:ÄéQŠ¥gµVxðú¹Q³æ%ƒ4€³âRØ@»]/ÿ`ñ¯£ÇÇ=ʲ䕲ƒ@; ?¶©ûsk>¿µæ£Óö³vö­MùúïôeƒŸ¶QøbìÓKé8£É‹âRŸÀùKj‚Îù]§)Ú(¼ØòWäý›—Îë¥åØ=>^\žS¥ÃŒ›ÖþÚ“w¸ûNxõ^¤«w–ú5ûª¯‰6)âR‚Ó•MÖ<ÿDKíL}"µÃàœ¯s[r£Â‡Í¬7^Sï¹Ä%2Oº^n÷k÷Caûë‰Ô‰=|ï-ëzj½_¹4!bÅñuúµXi $.¥¼üÙ«÷*õOϯN¿*RÛÖ[»bÙNÔSëü2íúëǘëvMí,|yu^‹é…ƒ­þYÞû¼”¿¦üy¸9ÚSxðÒäèäJ¿–½«{§^⽦wïaΟqÞiê mÔºo<Сîż&æº_#õî÷kÝö¬ã”og“ð=Œ±oM9qŒX«¥wF¨õOSú´5>e§aïAÍ{}>W7ù¿º³äk„“ ÊND¿v1<öúMë^ýR~îÅùw,>À÷û|Ÿ RÕyE¯ Ö7• ê×ø^íÒ¥MÏOÍxWdc—³íÐj‘ÚbdÕ‘×Ay…'/Éê; Ó¯Ñ-{ÈSâëÅüÎÎâ» ?RèÚëã#úu‰~3Ä¥/¿ú<Åi^¥w­è¥¢yŸÀãF}…'Æyëuî·§Å¥ï·ûΨwٺNJ—W,º¦[k·.•æMáDz7´œÉ_Gë×Õyµ¸ô°ÖþgdY÷Xê~S¤TÜí´zÛzÔSøñÜ¥¡ŸŒÑ¯«u1ׯÄÙáöß®GDöótSañ…—–^ÝVáÃ4zÎ_\¬_—Çq溕4zñ­¿/¼%²Sô‡'J¶ò^¤¼÷cêá$KµUëßOn·"õë[äE«( ýþé‹P†|cÁaè«må:ë£Ù^æú;o½1tk'QÒuñâAñ÷DöØeïÙD¤ØÿÝ­Ëö0”—ë¬Ï“æZ¡úõSoü{€5îÁ/œØ°ü¶µ~Dþk­ó«5릆ÎZ±ÉÚ¶•x '?ã8ÒcÈZýº:%ãçç:rÊ´—ás”—뮯&)2W¿®Î{­ùb}Ã<_áàâ-Ì÷0kÈ:lò‹ÛÊõ×_øaBÓœªê7ì—϶KøT”<ûÝ”òž{­óAº–ŒÓ¼÷]Ã÷bÖüK|Ð7øMßýrþú 29±_”,¢‹ðƒè—LŸ´?³š~˸‹’¥¢Yn“©"[½kÔ»e”—뮿LϦ¤ß³ÝËñ5QÂïz²7˃±f/ ¾R”Wë½iÔ [Á;|õüN£dõ™svUÒ3ÖÊb<>Ãþ4R­û«êüE¿ÁïœJè”ØîY‘6£0øP#K¿ás{sž#Õ:oQï%õÒ¬ë•öb\£û©ÖüÐíÖ¬«Öz)=ÁÒO"Õúo•Ç«M}õ†²±ððÕæó¿ø‹5e¿&Ö|óBë_Ÿ¿ŠTëþº¢“ߘ¾,'-|zýИó[[ðѱÿ©/¬}–ÒÑžZÿ7äµñýFmð–‰’]»?xúÅ"ûru%Î-= Zç Ö;n€k>û‰(ÉŒnìšÜEd_<ТQÃhsÌs…vj½w¨ûyýÆKîQ£¦”‰õþFdFÀ¬óOÚ©uß‘ïCœ@¿!Õýf¢ä­­¾Ÿ ¹nÍ©Ç/d›÷'k¤7Ù’ íúõ 2³òÞŠvzu©n½GO½ç3Ï};±Svîô÷ćÿ ?bÿ¿ûð ù?çËŸþxnóÙ7E<ûƒBU@3U€wU®RÊ>Ï‘®ŠòUK+ùkÂÚÚs|!àIµLåsBú×LP>ÏeLSÔ¯Žüê9ì÷ÜÆ>Ï‹+ùéJ—s|ÏDå•ÄC$ÚŽÌT±=Û9²Oª4å* }G!?*MÅö$ªäK½=ÒíK9®g,ûQÏQ±¥¢‘±G—*?°äãUú…"óDÖ>‘µÉvOdí¿š¬%šLä¹+f¿O ì[íUMT‰Çé*eìéª(_i{wåÓÉ>ã‡û³kÌeµ2ö˜È~ÑEuÔ¯Žüê¹ì×u38¾ˆ7û¸NSþ‰M8ÛðA}çÅ2\ÂØ?b&ljb߈ÈwÅøjøs,@´ç†ïnI*v¸[ûÂÆØj¶š_$ã‡cìµ<8N ú¯Øk‡Uòˆ¶ë ¿N¼Š-RבㆧrÜpðÇzäŸ ýÕËå¸á¨_°ÔÏWìKÆ GÛ ò•/m›Š/è‘ËñDbÙ/bljU~e¬AÿJ±D[£0+ŽHcwö‡˜ÊqÂg“8C„Ø¢Œ!¯b„cÈÊ"Ú%;`õ·Š"ãƒ?ø·~›!ßå}S9nà${'鱘ã…$*߇Äfe ð4ŽÿíÎþ1-ËUlòså|z«+c}#? MÅ÷n…¶[ÅpLï2Žã‰l†Z¡|+Ì Ù­´Bù@¬m Ê¢í@À„òAè+é`” FÝ`ÀEQ>ã Áœ„  Xè=d(òC1Gô6/,AÅÂ&Oo¦Â1'ôÆ(¼\ÅÆ£XÞ­1m0Gmb8†N>ÇÿFùôMwÆ€¥m”òkKwˆ‘€%u#I&¢­vìÓ6CùøŽBßQÈB:ªXŽ‘qñnò0/â•Ïoòïþ£Q?ùÑåìS7–ý| ~N뙆\%™úd©!?×9i‘ ¹XYþ3HòÏÐ?ûUY®rÌa•å—!«×=+Ë"CörÆðµLr¤r,¯ÊrÃÐ1 Ù`È‚Êz&ñ}ƒÇú&ñuƒ§c]Mþý¸¾IüÙàÅă‰ÿ<–æ´œ}1ƒGjÀ•*™øc|{¬­=ÖÖ¾Bù\v ½”tQàº#ÖÞ1ž}.ãwgà˜3úu<®XSWà+Ú¬z5P¾úr³qœÀY3Šctâ÷rŽÉ‰òµ‰o¡ÿ:h«®ÇÚLã8X³ú¥_3žãhâ»'úóDž€Å í4BùF(רŸ}úÿ@]o”ñ¬ÍU,KòûA¾/ÈWY¬bURŒJò¯Ov«þh“ìôZa.Z¾@Œ+eƒP–ÞɇÝ¢N(ú Ca¨ŽöÃѽïjã®|ðÓŠOIwá‘€©úŒBù¨RåO?ã‰&ÙöD?}¢Ÿ&Û=ÑOÿÕôS^7;x†Œçƒu–1ûì8Î2ð¢JLJ±q|äWEy{Ô·ç8ËȯÃ>î1ÕïÅq–K9®ò«cªc¬Žîgm;¢®S”òƒï„|'ä;ƒÞAΩgå]п àsÁ\º"ß4ášÎ1bì8Î2ê×@ßnøî–Îñ‘Wuk&s\@¤Ý‘vOâø€g9AÅ $Tùµ‘_;‹cÄ ¿ê×,u½ñÁ¸ë¦s|G?ÐŒ¯Œêöúé[å í€­ê{ ¼GœŠ)Hña"¿!Úk˜ÆñaH> M>Ô=Ñ·æÆ õ½‹×ާŒ¶ÉÇqcð­Æ OkÜ„äàn8šºsL¤›’/p“¾ŠöBwÒ!H“P(ú E:°„¡nà,ác8ʆcNÂIîUp<ä·FÝ6˜û6±*¾#ÅjŽ@:å#0GùJ$´Qñs(&M$àŽD[‘˜¿H´Eïˆè-ŒEƒqE¡í(´…tTÇoF~{¤Û£|‡(oRÆ¢A{Ñ€/éhÀ]¡bɸÍÀŽÄˆ·Ò_eÝÔˆhÈO’•e&ÉJCF’<4ä`åsÐ$ó 9gè¡Ë5’g•e™×ÃW$£ ùdȦÊç¡ÿL‘ ª,*Ÿ‡V–3•cÐV>}\®2åqyR9íã2ãq9aÈC.2¡²ÞŠ©Z8ZãŠW_8_sìÃñTÑo­DŽ©…òµñ0ŽC_Æñ²Ð~}àA}Ìm´åáÎñä‘ßÐ_Å1¡ø;Þ myå(T•q¯P§è§q”Š¡Úm“oé&¥u½ã8<úôÁ7C½føÝýø"Ïe›£^ó,ë|œµÀxýз_"ǧÂ÷–˜+òçãßüQ6éVôÁ÷@Œ-0Ž Ô%ûè`E6!è/ý…"Mvw¡h/ óO¶Sáã éÖ(Cö“…Þ¿G ^`Ž í·U±¶(|$à¥7÷…âÆSÜwŠE+cWá{û*fU4Ú¼Ñ9ŸŠp„ôoú‹ªôc–2ßÎî‰~Núù¿ªnþ_Q/¢“ÿŸ×ÉcxMhl u#3™cI¹s¼F¤«š­ŠtU”¯Š±VEy{Љ=ðÅ>G‘w5àfµtޱ´Ê;`]Ê8f#ò«cªc,ŽÞøŸs+p²áƒ´òPÞùÎ(ïœÎqQßí¹¤rÜFä»—]38ž•;ÇmÌRlDÆçÎà˜€½&É•4ŽÛˆ´;ÒîÉ»ô\ }׸j£Úè§6òjç*öS'Šc6–s¼Fô[m×E[õÜU̬zH×Üõgýx|29V#Ê7À5\ ï|Ô÷ …ü†˜Ã†H7Ä8¢=O”÷Là8è›bòx6ŠMÓp7B_P·dLãDŽÑˆïMcÌ ÅhŠ>šÆp|F´é4ùd÷FYo¤}96#Æäƒ¹k†üfh·ÒÍï‹´/Ê“Ÿ[ò5Ûm7 Ís8&#ê¶¼äÒðû!Ïy~€£%`j™¯X,ùióGžŽb·h'ù$÷P¶ê¶Bºò1w éä€!0’¿• ô„y FÙ`ä¦`À‚üÀL> BP>ù¡h+õCÑvú"»ç0À\ Çz“=k8éå8$Ò­Q·¿ŠÙIq%Û /²ÃŠ@:óü¶€­mœŠqF6"‘h;s‰¶#¨8îí9$úŽB~Æ•¥âºË˜ÈoŸ£DF›Š×Ù°v@ûÑè/壱ÆÑTìwcëHtKü•þ Yúø1!ütFL2²²N^Y.òçñ7 †¬#Ùfèãÿ‘.nÈ®ö>ÁOëå¿S ùódÏã:ùãò¥²>þ¸.^Y'ùArƒdÅ?ÓÁI&<®ƒW>[þGoˆ¯?~¾lððÊüûq}ÛàÕħçѱJ•¢Xº߯J.ÐøX㫚Ï1sñ[5ÐCµxðZ”s^9oª#í\rLÆeœPÆ ´ëTÎqq3€Bèßs^ek?ÝU¼ÛšéÓÖ†ú©í¡bØÖAºN:Ç®¾ÔEݺh£êÔ#ž— âÔ6@^Àæœó@™†  Š›ç‰ß=ÑúñB{0ŠÕxßõ?àX³_“rg°)êy£]ŒÍóH~ëÉg|3Àá‹ß}QŽ|N“Š×eZ`\~èÏmµD-‰¡?ò‡€¾[!M¾Ñ1¦@À„9!_HAS0Ò!€‰|´‘P´ŠvÈ—CX±R [ƒöɦXÆ‘Eâ (ßpmVdšŠÉ+cÆ¢n~‹ÂDá{{üÖ}w<ÐF4Ö!óÐ1ŒãÀb;V0ÐßÿªýßM‡þïtÆý¿«Gÿw?ÛþWÖ¡ãxλz”ñâ±F`©âO,Ç<ìUAsUc9æ9ªØƒì±ÖöÈ«z­œª–Á1ÏÑ–ÚrÀ|:Tp׎y8A_Žh×uP× í:¡-'ä;¡¼3òãñÁz9WpÜs”q¬.‡+è×xèŠ|×R¶FB¥˜çøî–É1b‘® Xjfp¬X¤Ý‘vGÿî(_ °Ö=ÖBÿµŠ9î9úªüÚH×qä¸ç¨_<°.Æ]}×EûuK=ô]ýÕìõ®ïÍñÎ+õ{”o€¶ Ýù€Ýƒd Ê{ ¿¡ ’1h¿!ò=QÞiŠçéX½Ð7Å–”±ÎÑW#ôÕ(K±;Ši×m5ÎQ¬¯I:Ç7 M1†¦H7E›CÉ;QÅ8—ñÍÑ®æÀù¯¥Ú¤ø!Ãâgø¢¬/Æç[Á1ÍÑ?ÅháÈñÌ3U,sò.c˜ç*¶Ú2Šã—Vôáºä_Øë€ºäÓ6ð’/×Ìe+”'ÿ¢­O~1‘Écêa Aè+eƒQ6ùÁHcÍC0!€;éÀBþ«B‘š¥ØwúÆ¡l8`ÇwòÍÒp´F^kàRkÔkƒÂm€kmÐNôõ!_dßO6ömÑO[ôÓ鶘HÔļE+Q@v­í0†v¹J,D!? }Eå(!c«#¿=ÆØcê£bÜËXêè?ýG#?ºX‰‘Ž(ßóßù”ôgÈÉô~×y† «,¯*뻕Ϙ+ë°Äû×[ þ^Y_ýGzªÁ«çÏÆYñÿLWýgzªñ¢²¾J¼³2¯¬¬ŸþGº©Áï°®UP·*æªæ¯æÙ¿9äpœi̽#ñŒÃk]ƒtK¬›éè£ð±ðÄkæ5óÀúxbý=QÆ‹híRÜÅ&À±&XgŠÙGñÜš¢-Š åƒõnüòÅúû¢nsÒ÷€ ¢Êø¡ù“o üi‰þüѦ?à%_Ìè£~o…t Ú «AÀë ,^vôBúÚ %Ü ahŸüM…£|8ê·F_­1¶Ö¤÷amC$ÊM{;ÂYÔ‰Bº=þot{ôÕ0t@ùhÂË¥æ<Ñ‹ìþë1þWÕþÕÏéÍÅ©ÿÑNÞýÛý„ÏÏøü‚ϯøü†Ï=|îÛÉsH»ßñyˆÏøü‰Ï_ø<ÂçoÞ ã {" {" Š”†=‘†=‘Vu0ì‰4ðz ¼^ÞH«öEZM¶uƾHþHƒÐ 4ì‹4ì‹´lŸ™ a_¤a_¤A6hP4(ZS¶IÀ¾HþHþHþHþHþHkÉïn±/Ò°/Ò°/Ò°/ÒÀ\4È-”ß“a_¤þ5пú×@ÿè_kÇï"@ÿè_ýk  ô¯þ5ïß@ÿè_ýk  ô¯þµ^|† ú×@ÿè_ýk  ô¯ Pçè_ýk  ô¯þ5п6ŒõWпú×@ÿè_ýk  ô/íÄAÿè_ýk  ô¯þ5п´mýk  ô¯þ5пú×@ÿòM2è_ýk  ô¯þ5пú—oB@ÿè_ýk  ô¯þ5п<Ÿýk  ô¯þ5пú×@ÿR÷ýk  ô¯þ5пú×@ÿÒÆô¯þ5пú×@ÿè_ýK»<пú×@ÿè_ýk  ô/íQ@ÿè_ýk  ô¯þ5п|o ú×@ÿè_ýk  ô¯þå[Bпú×@ÿè_ýk  ôOïa4пú×@ÿè_ýk  ôOw¯è_ýk  ô¯þ5пú§³{ ô¯þ5пú×@ÿè_ýÓ¾Dýk  ô¯þ5пú×@ÿdÿ¯þ5пú×@ÿè_ýk ²YÔ@ÿè_ýk  ô¯þ5Ð?Ùêh  ô¯þ5пú×@ÿèŸÞ¢kDÿôG²ŽäMóÝæ?à’‰VŸx]3Õøä{!o~3ÏwêlLÚPÚ)ýD¾!Jà;‹Lu^&m*íøý¦ßp&ñ»÷ec)ßyðøXömÎoáKù½‘#Û Å°þ“¤î¼åÙ[>Ÿ¿Ùñ¾ÍÆ{·4Þ¿óûÐ0¶YJbÍö‰àÈ~Âø¼.žïFÒÙ?‚7ûHˆã7õélï”ÈzWŽº#‘¾<؆3S½]’6œîlÇ£ö|Ò&*Žý&¤±=g)¿µÏRï˜ä;{o¾“OTöRÒ–3™ßÙç³Ý”»zo/m9mlÏ™ªt>z³*Ï sÕ™¡´éôæ7¬‰ê«|»¥Þ¯Ê=_:ûN(gNÿJ>ÒÕ{ÚóÉóÄ0ö—ÈvœYjÏ'ßÙ»³ÿ„Þÿ¥ò=J1ßýÛ±}VÛh%²½g¦ò¯ ïX<øž%Žß=¥+.yçbÇ{FÛtº³]g,ïÓÙ¾³”ïP<øMS<¿kÊä÷1üF YÙxÑ[–e|o’¦Î/É–Sîý¼ÙwB<ï3Ô¹¦´í´ãóÍ(~ß”ÌoœrÕÞP¾Ã÷æ;–x~³›¡l?å}‹£z{ m@ãÙçB¿Ñ/ã7 ÞüÆ7žíB3Ô›}ynjÇo£bø^&•ïfòùí‚#¿å·±Ýh2ûiÈTohÏ)íHÝùM°ý-$òÝK†z§Ooèm¾è·ÚíS®¶ }Ño_ôÛ0õE»}Ñn_´ÛóÓíõÇ÷þè£?¾@ÿÐî´;pÄÆXŒ/m DÞ@àÎ@ä D[ƒ0®A˜ßÁ€e0æk0àŒõŒù‚þ‡”©-ÈPäEþPÐÌPÀø4ê ÃoÃðÛ0Ô†:ÃPgÖ,°Ä•ª­ÊpÀ=pÃ1®¨?eG¦¥j3°ŽD£0¦Q€sà 8G£Þhô5íŽA›cËXü>}ŽEù±˜‡±h'íŒÃ|ŽÃïãðû8ü>¿C[ã1?ãëÀ:ù0æ ÈŸ€ü‰hw"ÚˆúQf`„y™ø&£Þdü6¿MÌSPg Æ6e§Þ©hg*Æ3õ§®ièÚŸŽ9›Ž5˜z3ÐÖ ´5}Í@½YHÏBzÒ³ž:³ÑÖlÀœX¬¶^s°6sžƒ¶ç ¿9h{.à˜‡ßçŽy¨?õç¡þ³ŽJ<'á÷$”O,Iè{ú^@º$éðô÷ä½Ï“=ù“=ù“=ù“=ù“=ù“=ù¿äž\#ú'Y |–2%™yk®â1’Ήb•O ‰7i<åü&ÜŸß &ð½g¦ºû”6çŽìK0†mÏÓ*ÙŸ;*²Ç‘v>Éʶ’Þ’Óûé÷ÃŽ} Úø­a2ß—æ*_Hòí¹;۪DzÂd~ƒ˜¥l×å]ª#û$ŒQwd¯)}–²­PÛ³'³M{®å?‰ÞJû¡X¶qOVvDÒ—’?ûSŠg{Ï ¶wObß…¹üÞÆŽß/ÚØ‡a1Û¼{°Ÿ’Xuo"íÞãÙÇR:Û¿—±Q¿e´cÿ%±lc”ɶï©l3ZÌ6ðÊÎHھǰo“4å×ìŒä½l>ßÇÚ±¯¥ö·”ÎvE6¶-Jeû¢|åÓPÚ¼‡Uò¯”¡l‹ÈŸ¡¼bßJIì¥TÙÑ[Cò³Dw¦ò~¢²M%{ºŸ%?äo|‘/$²1—÷µiüV¨˜ß yó›¡x~·Ÿ¡læåû!Gå ‘|JŸ*ìW%Žï2ØÇJ¿òæ·û êý>ù8”oˆbùmc*ÛÁç+ŸKò.6ïcËØŸ¡?ûYJàû¦Lu?+}´8ò=­M½õ—¾3Ù6¾œíüùMRÛ!e*[yù>ÉßBÆò{ÈT¾ÏÍgMŽì1†ïvSù~7ŸïxÙ† –ïzÓÔ}/ÙÖËw“îl§Ãvö©ìÓ)K½¥¤»0éOƃí‘bØ7S’z÷$ý!æò[È2¾ÏõVþ™ú§©wPÒ/S®R§m©ì Ѧlçå;Ètek ý ¢ŸØ u§Û ýuBW'Ô넾:¦N€»3þïŒñ韎r:ÚÓÑžŽöl(gC¾ ù]ßù]ÐN´ÓãꊱwE^7äuC^7äuC^wÔëŽzÝ‘×y=×y=ÐnOŒ¥'êöDþ”Üè‰ñö¼cÐg Òc0Ö1€3ðAùQ¨7 ec0öxŒ1í?6†Ð¾eú#Ýuûàÿ‰ÀÏ>h»/úí‹~û¢ß¾¨ÛåÐO_´Ñ—öâ¨ßßûã·þX—èæjÖw`ˆE~,Æ‹9ê‹üÀÈ„ºƒÊÕvc0æ0æk0à ˜¶!è{ò‡"o(ò†ž¡€->CmI†á÷aø}ê Ca¨3Œöä€%° ÜÃ÷pÀ=p ÇøG (;0@™‘(3°Ž¤yA»£ç(À9cz£ÑæDš[À1mŒÅïcQ6mŒEñhcðuàHDÙqȇù‡¼q´'/UÛ¡ €uÊL@[?ùçÄ2µMš8§¢I˜—Ih{r©Ú6MÁïSóÔ™‚±MAù©€w*Ú™ŠñLESÛ4Ô†ö§cΦcŽg Þ ´5mÍ@_3PoÒ³ž…ô,¤g£Îl´5›Æƒþæ`.æ`mæ =mÏAsÊÕVl~Ÿ8æ¡þ<ÔŸ‡úÏ¢~ê$á÷$”O,Iè{ú^€ßF¹ÿ ¿'~Ìþßø13öX¿³îDûŸÇ÷=Æ^‡ö8´·1ö4´1ö,´?1ö´W0ö´ }éþ¤ïc¾¤noèñ¤³ú9éÛ¤[“^ýSŸ~\—®¬G:4p×ÔŸ ÝÙЛ +éËE•ôä+•ôãk¬:qëÂ_³\κï÷¬óV¨·cÒ[Œ²Õ&?“d—-ýI¦+?ô^…|þO ²É ;é›9UÙvЛc²C¦7(ä_ƒÞב}!ùB¦7Îô†šHŒ|W“ïdi—â¨ÞÂÐ[:ò£A6ÙÒv"WùO&¿äÛ‘üòÐ{e²'¤÷+ÒŸ£zÓL¾¬¥eoå'šÞ×I.É&ù{#?DäLjìȇ$½Á!{@²m _?dרuº¢Í®Þ| F²seóf[ŒÇæ¯ÞeuFº3æ¥3¾Û0']1]1æ®ñø 㱡® eúã÷þhcÊBºÊöA¹Ak,êŽBß£Ï ’Y$/ÑW<ÊŽA™!HÁœÅ¡ß§ñ‰§òhk2ò¦£Þd”›Hiüž@|s1íÏÄÎʧ#/éDÀˆ¾ŸÃ<‡ñŽ¢öÑÿLŒ?c…ú3ñûtÀ<ð'`nâ1ßÓQg<ÊMGùD|f¢þt”‹r3Qo<Ê̼3Q~.Ê&¦*Ö•¸ç¢Ü|ÐðdúŽß'£ù6ÅÎÆ¶YX«¹”G¿ιh+c…ö1ó f”›±Î¥4àšOðaœóÑ×|À3k6ŸúA;³0öùÔÚ˜¶æb<ó©>æb>Õ¡²ôÆ0°'¡$ŒMшü{ržûä<÷Éyî“óÜ'ç¹OÎsÿÕÎsI÷4Îmÿ8³%yK2‰äB*óÇ|Å'$­=Å)ÿšríÓyn*ØÎ;ŒíT9æK–zã-}ݹ³OúXå÷NÆ€ÉawåwYÚǰTöÑ”¯lɯ»ôY¯ÞŠKŸ¢™J• _œÒg^œòB¾è9ÅM!pòåDþóÈŸùm’¾íc9–Lû¸/c#6åolWÈ.ÆÍ[Ùu“ Ù0ÒûròÏI>ñH!ÑÒþ$ƒ}“&°ÿ§Lö¬|à“ÿ'i/ãÈ631ì ¿Tùï#Ÿ~ÒWiû÷±íL2ÛŸçsGöWZÆöçalC“¬üBIPiÊ'Å¡‘¾þ¼•-ºôË>KÓ•|ò{Bj•é*Lù–¾P’•=º´=aûóL寔lPÈ7>ù”6™qì«4SùH!¿øô_ÚdzÝL†ò—"ýâÛ± MÛ¢'±¨åSPÚ¥;²/ÓögšÄ>£rØ÷µûŽŠaÿ¦©·&ŸmoÜÙ§~ û-õfߥñ“&“}˜–³=?Û¤'²]zÛÑÄqš,õŸljÈ©´ÈPöÒ”#ۢDz=zšòÇBvÒ)ÚïœÎ~LØg~–ò%ýæ;²Ï¨Xå[únÉR>¥ÝÛÞÄ)ûRéë4KÙ±K§îì[?–íÒ8žM1Û"¸³}{ǶIWñmȧ¢´MõPv 1€/ÿ?…qýöÎ<ªêÍÿ£„±PcJr'¤÷Þ2é½RQˆ…& ¡C(°¢;ŠJï¨(V°DEŠìçÜsg&Ëêoÿ»ûì…<Ï<3÷œ·¿çž{ÎÉÌû „7þx‚±Ù¼žÄÎÙÈôÜ&ë“jõM×Éßßhu¤lõÚ‹z½ÓYzÓ]z=}[½¦~ž^—±N¯qºS¯§o£×’"gØíMøå_B7¶y`·'ïžø§`‹‚>%O Ib+tú ô{ÑïE¿r¼3»ÇÐîM{4ÞôyÓçMŸ|>´ùÐïK¿/}¾È Ä.?bCv„ñÙ?âÄ;¶àc1ˆÃü €?þ@쌣=š r‡î |bLÇáo0²‚‘ŒÎ`xBЂÎì Ážä†À‚þ¤}›°KnRèGnœ¸&áÈŒ@~y‹@o$y:‘ míÑØM죑_qÈÆæhbÐGþb¡‰…&;ãh‹ç:žëB®ã዇/žìŽÇ‡ô'À—ˆí‰Øžˆí‰Ø’Hl’àI‡$ìJ‚&šdòšŒŒüJÁÎT|‡­©ð¥Â—Šì±È‹î4úÒ Oƒ> 9iÈ1"'úqØT ý8|,An!±,Dgr³xÏÆîlìÉ&69ðåàk.m¹´åbW.ô¹èÉE^>:ò‰Q>|ùè­¤½ø[¸Wn‘Šà+BVv!¯¾®K¸.ẄëRxJ‘Uнeè+Ç®rì+çºÙåÈ-G~vTÑ^…UðWÁ_5üµðÔÒ^ }í.¹Õîñ´eCk©‘tý¹ìº¿ÿ÷uÍû(±j½w2ï“Ìû#óÞȼ'ûŸÖ{óž§õ™®ycÞ»˜÷-äʲW1ï?ľBì)ÄÞAìÌûózß¼¶o}¾û¿½®f¾ø?ÿžÄÿËzûo­µÿjýgkìs²ö»¨ñ©Õ–‹µ}D!ñ'jÆ QŸNÔçÔjþ4J<"q¯Šß ÷7JÌ­Öe„ÄýµvžžV7§LÖaõ~D½K­®E™ÄøøM'¨o¬ã.jyhõ2d-RQ·TüæXüöZÃí3Iì¾²nfG£ÄÜuyDf­Ô‹­Äu÷´º˜‰…¨áºËšø¢º¨Å,jzˆß\‹Ú“¢Ž¤¨S$jÉ:Ò¢V¨¨w!~Ï+êxŒwŒAþRüW`öhõ/êä4ãÇ\åç qFû"‰+jµj8í²¶â˜Fß«QâÈ>D,ýêôúS.¿Ý³QÇTh”¸ ¢£†çn£cv¹è5xòd=s­¶”IÖgXìZMV;!BÇHhÐqöêØ]v:~W€¬Íªá°¯“µ5ì.[½.+ýØí]øå.lò8*—ážâÿì2 #ßt 3@g ß@¿ý^ÈñBŽ—ˆ!¾¡o }ÞôyÓçMŸ7}>ðùÐçCŸ/}¾ôù"Ó;ýˆ‰ý~øá‡~Ðøcg Ÿà 6‹úˆmývr $Þ¡ "AÈ "~AÄ/X¼°7¹Áè †'½!è Á¦ä† 7¹!Ä']¡Ä' a|çs8rÑŽÿáÈ‹À¿ˆSr{ÉØ‰¤/Y‘ÈŠ"?ÑØMü£±#¿¢‰´Ø§£?ý±ôÇÒKüc±3™ñ´ÅÓO<<ñðÄÃM Ø’€ÜDl®DO¢xÁ“H¼’àOÂþ$lJ‚&ß’‘›ŒŒ|J9*·5©ø _*|©È‹Ü±Ø’F{ôiЧ!' 9Fä¤ãW:íéÈO§=ø¥#+¿3ÅÞ¾Lú3áˤ?“þ,ìÉBnqËÆŸlìÍ&fÙôçЖK[.m¹ÈÌ%'¹Ðæ¢+9ùÄ.þ|Úò±«ýÄ¢Eè+‚¯YEÈ*ÂÆ"øJ¸.Ẅë®Ká)EV)6•‰ý7±('7å\—#»ÙåȮŽ*Ú«°£ þ*ø«à¯†¿žZÚk¡¯%îãÑ=ÝãiSÄšKìgÅ_ë³n±{ð8"ÿz8"æ=_ë}ÞõûºëÏÙÅ>NìáZïßÌ{7Ʀ¶g3ï×Ì{µÖgïŽ6ÿqOö·ÎážKì³ÄÞJì©Ä>Jì¡Ì{§ë÷Mb¯ôg{$ó~Èüýl±ÿ¹~ïÃøùwûó~Gìs&ëë[ó^ƼW1ïSÄåÆÞäÿÛÞD`ÔìÙnuz­ë2‰U#êœjõJ‰S|µG_;£ÄäX =ë$¬ÀŠõ@EWèúeýÏž¿LÔµöEínkÎTªáçÌoÛE“ULûJÔ¿¾¥A¯÷ºMÖvx­¢VhÿS²^¿ÀgØ3K`ët†oB»Àcç c“¡ÓY]#Zá @cÏý0Ô$±ÈV”¨Ã)jµ <Q¯u¼#$ª†‰{Jb5Šé[«ÇºMÖüÖp œåt<¹c5cïuQÖÀÖðÑ]dDQ+_ó“õ²G$Ï(t ÇG?è‡co÷´Ÿ‹Ä´õs—uU ÐøAï!1+ý$ÆŽŸxSC‚Ä0ðyL­ÄªõCßh}x™%k¨"ÿAú‚Å ;¢ ‚7a¼G!/ÝÜ7ÁØG[à6YC5 9Ä0Ì(±ÁÂòt<ä†ÑÕ 10ÃÐÕ(ñb°! þÚ†36âŒG@Ãá!W##$VWœxAW+1»åFø<ˆµ‘ëlHâ•Oñ‹¬B|ÈÃÖrT†5ÐÔ¿b^…Ä¡’¶2äGâcö‹~øk°·æ¢|4ŽC÷8ú¸NÄþDè“°#‰x$AŸD{2:’ñ!Eø„M)èO…'ù©ÈMÅç±Ø=Ùi´§aWôiÄ& 9ƽòžN{:íé´§ÓžŽ¬ lÍÀžLìȤ?»3éϤ? ¹YÈÍ‚? šlìË&žÙØ—_m¹´åÛ\xr‰[.´ùØ›œ|rœþQyÔ]€üBOâU_²ŠU„®"øJ¸.Ẅ뒽rIQЬRl.C_±('îå\—#»}åȮĎ*Ú«°£ þ*ø«öÊ%È8xj‰S-ôµØR‹îñè çR f“s«Ï.òÙ¡ýÝø?Ê?îÿPþÿb^»ÞøßÉ¿ÆÿNnüßä?ÿ¿É¿òÿLĞ䬎W°SâBj86:þ.c¢£‹ÄáÕ°wïË>OâNu³“ø°¿SƒŽÅk’ø¸;AÓÅEbñöÊ“ø/†AžÄ,°Û+±iìNI|u gš®:oƒÄú²o”¸¼ÝwI<0 óFÈd>rà½[‚ŽV+ñx¥À0è~QÇdG^äô€Ï!BǤ,ÓkŸ7H\^ ƒ×YÇ-µÎ{BÛ ޶:öüŽSרc ñAâ¶kØ^;%.¬ÀÛuÄ/Çw×VÇ+p”øîNK9NecÀµQÇïÈrÚ&q.ÞŽVsÝ]bü,ñ8á¼úÛJÜËþÈî ã]ºË:ìÎèp6êX—ØêÜ cÆÏÁÎ:f%tƒd½þÁu:¦4ƒM^àY ¬ÁÄbˆ­Ž™ ïx‡è¸]ÄHŽy ¯ÂkÈN‰+/ð¼|u\øÝìtœ]d¸¹ëx»Èp«Õq2¡‰7ô»í”øó;Sà¸á¿§ŽË+Þñy(r†ºHœ^é?KǪձ{Iì÷åߨãm¢[!–þ´ O8¾{Ó ÝÊ.+{G8H_ ë`§Äí¸AZ=yìMPƒŽf’õåGï’ø½;,žhÞ=±Õ;=ɽ'6z¢OA–?ô¾Ð+Ð(è4ˆ59:£ùìEŸ}^Ðy!Ï ½^Ðùà»ò|çƒ<äù χúBë ­/´¾"öÈ {/äú#+{üi§ßŸ¾Ú‰]8mb…œ@èÅŒØå’*[‚àBv´Aåò+þPèB‰q(± ….ºPèB‰Qd‹\š…as6‡as6GpÁu×â%èŠDn2£è"QÄ>Š\Eac m1ðÄÀO <™ø_,6ÅÂo|qðÅÁ]/#m ø—Lžð/ÿE~°/¾$ø’Ÿ$Öýô%¡#™|&¯l^Åð'³äëåXè³ñ3“ö±ÐEßXì4"ÃÈg£øŒÎbl6¢3ƒ¶ Ú2hËû2q® gì*åÊÄ–,lÉB_r² ÉF~v‹\‚æÐ^Ï9ôåЗ‡¼<äå!/üWC—‡œ<òQ@> ÈGù({!ú*å²µ¾ba›°б¡žbxJÅ ¾tTˆÏð– ^âQA{ñ¨À† ìªh‘KÝJbQC jˆu y©!'5ØSƒ-5È­%ÚúNü™÷4×çZì_þê;×b¯ÒúLºõþļ/{’ëkd˜÷ba>þ«³hóâoÕǸ£÷ßÃÑký½î¿:wn}æ,Ö¿¶ök^óZ·õw„ZŸ9·^ÛŠõ¬XËŠ5jëõ©X›ŠuéH}ýh^7š×ŒI­Ö‰×Ÿ!›×ׯýÌk¾ñ²Æë»F¹ærâï·KbV÷#ÆöŒ¯~G%fv/>wçëæ ‡·«£Ä)rÅþ^FÞÝ%þR/îÙž¶:>ö,‰Ó'ðŠ^’ÀÚX,½Ó =ýÐçì(±H{‘'W£ÄpqÅ×2‰Oªa 5HL aÐ ;%±G‡¡ÛÝî‹ämæÊ»³»Äÿø@®Ì£Ñç¾Kbfºš$FÑp[‰§7û\‘çú­ÄýTIü"/Úc“·x‡n0vx£ß¹¾´y+‰óâÏ\å‹}þb­Aÿhú}-²ôûòrÏ=t‡â‹·xFÓçŽý¡øéO8üá‹$žv ³ÄÑŽDŽ?ïÑøˆ®hñÆöXhc‘/ž»ôû#7Ñß"$áIFn22’¡O„7Õ$§ŠdxS‘“F[t‹œF \#/ 9áô…BÎuö„Ó+ž›Ä/8e£3¾\â)xøœL[,tÙëäÔ#¦œ<ò]M6zóð)œÏyò#ÚT1gë èÈYµ˜Ï‰y5¾VcO5q­&wFhªñ%{«ñ§û«±+š A‡mÅðˆvl¯†®ŸK±©eä®þjtVÐ^ôUcO ²kÈI zÝĽM­AîK´¿ÿê9×?Û×?Ów…ÿ»ç\ÿìßþW:ãºñÝàÿÚ×?úùÖÿæÙÖÿä»Àâl‹{ñ–]¿þVÆfG[Çž\wt—xö]¶ƒYLù·as>t¶Nèè´Nâ2 ½NÜ_ÕÞnðu Ðqí‰qøí é%Þ¡³ã^±/Ó1œ»]uL{äÚ‹5¼öȶß&ñ8{ÛI|çn´wCf7£ÄvØðû±;÷n÷ùxëa'1:$³C‚Žiϼë€|‡Eß¾'t=uÌgƒÄÑtÆ·ž òñçH¿#׎èwD–c‚ÄyvDŽ#rzbƒc£Ž¹‰Ž»$~½#r/Jìuwú°Ñ 9Nî:®=rœã„'|vš¥ãtšä£Ñi§Ä½wÂ''|êmÒž ¹è‹¼¾Ø™ý±±?×JƒŽumâãŒç<ßùÎb]ºNbÝvÑñå¡lÔñ¢Åº“þÁ¢—Ä×°3±g:†8ê8šð ‰Ðñ4‰ÿY:.=¼C°üCàÿS:þ´Ä¬ m ïnÈqCŽ[„ŽÃY§cÙ/’˜…nÈpÛ%±íC=v¸á{(ñj+ñ:Cá÷‡o¨hãóPüÝ ãxb×PÑÇu¤hCæpä wѱ<ÉÁptÂ)ÖÆØ-tèxžø;úÐÄ–‘Ø2ò¨Äò‰ÑøJbaGÒOûhr6šœ>%ñ±®§'vz’#Oôx"Û=±OA–?ÈóA^¬XWÓž ­o‹\"ù!Ï{B‰…?úü…¾¹l  /YèHÇÎXè¡ §?¸³ daGXƒÃD_0±Ï#.¡Œ©PèB¡ e|‡Š}…°Yèƒ6”x„"7 ú0ô„a[6GpÁu×âc"ð!"O.ÓbèÏÀ¶(tDû(…1ðÄÀO <©ÈŽ/FøÞ"—tqðÅÁgïðÆAoï âq–€ÉÄ1¡N.ûò—„ì$ø2_-úž…8&ãK2qKÆ—dô¥`ÛXñ‚~,ôc¡‹}è+ö¼¹6ŠÏèª&&FäeÚ2hË×è®À¦LäÓ—Ù"—–YØ…Ü,ädÑžM6zsЙC_}9ôåЗ‡¼<ä呇<î©<òG ÐY@ °½€< £…È(†§žbl(ƆbxŠá)†§T¼à+ûxKá-…·Þ Ú*ľEìY¸®DV¥Ø¯»ü¨!5ä¡[j°¥¹5È­ ±þæýÉŸÕs5ï5Ì{‡Öû„ÖçO­¿óØúLI¬¹¯?G2¯«[ŸýÙ¹‘y|ýºØüÝÅÿììèo™ãßúüH¬Y[¯Q[ŸýÕY‘yÙz]ùWgBæßû·^+¶þ⟠ýÕyX÷™¿{ØzMçncýÞ¡A_³]¿V3ÿ0ÂæßŸýÙ÷ [Ÿµ>'2¯“Z uqÜöŒc‰yÛ÷Æ_WƒŽÙë®ãñB×k§Ä×íEÌ]Ÿ®ŒûÞи‹ç ôîиCã ÿ艟ë*h‰¿k‹ÜºjxºÈðfl{‰ç$r¼+ÞÈðG—?rüñs4íþØåO‚ÙþøäÍ|⎠xGò Ež7z¼É»7z1wbS8ã1»½¹Ï‘íͽ ½¿x†¡/ºHú£‘‘â9Áµ´±ô'r탑ôÇÃë‹^_>§òž€Îú¡‰…'û-‚àMEV&téØK[6ýi|NÃŽTlLvð^@|"iÏCN¬xw·^$t™ð%àC"²RýÅÐàtÙØH[Ÿ ˆS5q+Ʀjd“ü4bS22x¯&Žb>&–´eŠùžläWC[¿ÕØ^À{1ýÄ¿Y¥Äªû+r«ÑYÑ §„ô¹‰1F[m€|^‰ÿo‰ó,Q—FüÿGû¿¬ ý.a‘¬ñ-°·Dmn±–9ߥJm”8Xâ,JlùEípñœuRö—øÝºø¿ø ¼ø¾öûò2ýûéuò;êZ ÝYSõú7f©áÌïŽâ“údxïIm½×óZµ]hì=ùʇ¯Tü¥ý>åÖa_œ½Û{ ²õä¨C‹ûÞçyð⋳Nç»^’jªyöÍý‚”副ýÎÅ©ã#†Ü;rR“2,Àðyùsuè1htOwÉYñH¥‹YÏ¥öÝFý6@ÙúèSE ?6«¦Ò|Ÿ\÷{”»ý¾ÔݬŽïïõ¼óKGá7jüó·<Ø6ØMùÈûŽoÿ4Û"gëC-³žzù¨jª°Mvˆ™£<0³Ä»iüuüi›n_œ Æ¿pØGçs^ TìŸ=òîåýÊG ;7µ;¶^Ù²ç“Þ3r:xî}÷·Æx\5÷86%ãeÅù›ø'çÞö¬:áÓu 3ÇV!§Q“³¨Ó[Ãy(úråô¹Ê–"åíQ¨¦ giùA}¸¼z®Ó‚hèwJú7WÅ|:yòñÝm]û§léØeÀ«}¯šª½êtmú°)móÃ\èOiô‹ßšsÓc¡ò±ñ`Ü [®)&Óãû¾9M5U­-¿ì¢úðO{âÎì2œ¹$㿤]ò!ûï‹”Ÿ-9[Ö¿%o¦±Ïßþzï¯=ß¿¿²ÜîËÕ4yb‡Ï—<¡ Xøáˆk‡»¨ÝMA/u؆9–ºè°bK™òñáqeã¿zÎó‚Ì·bêÞÓ»¶ï!Ïæø,¸ýÀ]KrÍy‚_æwYO§3CŸ2*{:}èÈXG¥yMÂÛ¯ 9`Õ/ÄµÔ›Ç |2¯Ë~¬u IÙZÐãÞð¥ÙyM×qŽï«¦=ê»1·í²Œ‡‰¿”lœÙ>>™Ïç\¾¹–02FÙC––-sS6/q?|k¦‹5ûÇ~Òþ¾¡êäñ¹œ©š ŸÌ_£ãÛKVŸ†ïpƸ·SvXÆý¦¯t J5m7£b‰³:eýäÛˆý >™ÇÆ>¿gÇú| ýÞ®©¿Æ¦D+›çvosg{ÏS×ÅÇç)±K_)kú—Ž4}dCuøðêm·«u^‹xç¬áÌ™÷µ´ß4u³%þûgÖµâ'Ëø\ýZgûoGvUMSú|øøÕ:Óù‡·þñür¬]ðôÛþo)ûÄÝþ@“²ÚïÖ5!MWM³DŤ+CÚdÕw¾£§Z·ä÷wï¿+Eé)çøåxX×·cm;¿“ʾÙs-Ý=IYõA¸Ï­ç>TMSß<íyíNèd¾×É<*ûÖ̹sU÷ Ê*ß>¿N }]5匮ºÓ¨ÖM¬ñä» ô2¿ë~ßâš}`±Å}ïîûÜö­ –ùhåóõWïÏh²Þo!›âwL©UÜõùºÎA|è¤ô–ã ¹2ÿëË~Û~Êø‡²ï‡KÞ¤<©¬ì3tÞ ö­âû Öõs[¤,—Ï]«?C»Ï™6e…å>¬{¤Ýòá¬y¼*ÇÁïùS;ôýDÙÿ¨ÿ÷,™¨46}›ûϹªiøm'‰ùY­ûéäÒÐõ6Ð˼oXúRýœ³ÉÊþ©ß»Fiìe—3¥á¸uÜ»¿sØ}¦:õÖÛ§¸¿ã Ÿ>U^=²µ²›HðAå¹-³'ä®¶ú=¸¦j_æPå!é—Zwmaߪèyæ8 GŽ g“†úYžsûlÎK}¹Pyö‘y—çô·ÎÿÒ¥¯nWÝ/=ßoþü¸u\\•ãb×U=9¾›eþÝÿóòï½>Q–}ãÔñ»1aÖç—A êÔnóŽ]v~96êÏ6kßÙè¯,‹Ÿ¹øÁÇTS„vãXæí©Å¦O¦]­Oއ†Ç羟|Íâǯ*ÏïXòºtÇc·}w÷<Õ$¦Ý˜-óò´î5³?pxÍ:/ý!ÇÅÆ­ó¿ûÜô¨r@›.{)K~piÉ8ØQ5E²ç7KvnýåoŠæ@/ó¾é¾“<2”‹˜XúÚå™·zß×ëùKÖçüèó{æüN^_s~õªødþ7é÷»9^^¾çÁßS•Å^»œŠÒ£¬ñ’óƒ:‡h¯O¾WœÌãá96iËËóäÀg£²’îÞh‘»›¯8 ­jÒï3s§eÝ5xNñ#Öûò9.6»´ Ÿ“0W9ØîmÓ³‹>QæÏî_¿ç摪IJúx¿öF=½ÌûæWε6ªŸrÐù—7ç¥Üg±ã©©GN\xäªu<t÷/½?Él¿:u„X€¶ Gæ¿ë|å¤ èÑÒáñíÊ+Vè1j£jyyrõ…êÔZMô2ïÍQWúÇq3˜ý™gSYW"7Ââï¡'Ä Ñr=ëù²›ªŽ½g7Ú‚àŠ2DŸÿ¦û>¶ÿÖK!OæÙtüô×o¿³Ð¯CnÓ^ŸóF%3¬üzêúí{GÌÓd‡È‘ùßâÒƒTaá;!×,ÏÓú Ó?¸<ªZ5=ØÀMQ\õüOïǯ­¶GŽÌÿ–4ßS/|~¯r¨D,œ¿V¦½5mú4ÕÔ¿iî ™êôˆ[ÜSxz™ÿ-•ÚH9´`âæµþG•:i§jê\påã»Ôé†.¯Ì`8k#ó½å‘ov–ܹ]9ôÚžv7Ÿ÷P&¬¹åöU+Ôæù¾î½QÊ ÁþõqKœÎÚȼo©»ubÒÛ–uÊ¡o=Ç$ý˜©T=_·»hÂ$Ëønf6ûíô~‹Ó¾/üxÆ-–çÄY™ÿ-b[õš%>‡{hP)Ú=IÌ\j³|NYž[Óôqf¾OÏÚÈñ°%ã±×ì}Ö(‡}wïxàéc–<;Ü\wõtËxl>{sи»*®[Ž=0åÔuº|.›ŸgÈ“ãa‹¶ì騮8nÿЦbËzrÄÏïݶ¸°Fm¾ØûÕï›¶[ž+z|-û—³6úxËc“rxñÑ鯸ù¨Q·ü ÎYÒ¤šÚ>'V‚êôm_n{ød ôzÞ×Ç8íôqV«·Ìoú­AÍñi@¯höi7œÛùAu:»€œß‡@¯ç}³Øi[ŸÛ‡?_ÒpæÙRóøSKœ¢‡}º}¬ji+Á¬Ï¿6úxØ–óåȵK•#<´êÙhöW­|ìxIõµtëóTv“­ãc—XOAŽ>>6>óÇ=k·(G˜¼Ãæ™Ôq»l½~ø¼§Zß6xÈñ‹ŸA§çý¹)­»sžeþ8Ÿ·tùœÃæûWØewø½Á¾æñdÙ×gûd?W{äèy_ó¢ØùZü?2I ˜²ûIsþÕºÜö»|2@5Ý´ ÷Åsªr¿Þ^ÿíe›¯g GÏ·¾?>²ê› ÕY.êôa S—ÇF¨Í翜ûhù§êŒœ=^çí@¯çõë_nk¨ùðåƒwx§ÎœÐ¥aÁ«‹Õæý½‡:£é·y?þòô2¯[ÛÝ}æ|ä›Ê„T:\±øùèìçü¬ZâÛüfvÁk§¯XÖW3fúD)¹¿ Gæ{ë½+‡^¥Û¯5ÔÇ(\÷ÎÌG¬÷Ý[/üÚ£b°ZzǤ³ gÛÊün²´ó럭RŽŽ OÏícYç¨ _¼qÿÑžŸ©Íò9©Ö÷ÿ£üÑ™øÙVæs«æN…%OG3~Y´ìÜæx«O¾6zXW·öÖùZŽk¥¿_êô·Òo7„­FžÌûÖÞO°³üÌ’¯£sÄBÑjÏÓùN==Èúœìbÿæ„ÌJë}öÛÇÇz<òòdþ·™žY¢ËõqÍêüÎù=‚yš»±Ó<µ~Fꊊ™Ì_mež·ºíä0Y9ª¥³R]ð…Ó®µ«îµ®ïÚüyÓ¡(u†­ö€Oæ{kŸÏg.ÚC9*ÒþàêÂíwtÊPí­q—ÏCÅM¿ž1°OØöåã,ë³mõqЦ+£2Ësê˜~®a¾ße>¼7éWës«yKr¼ç†a–y´þêÏ/ù.ßm½Ûêó€¶Me™÷Ž øví=ߨ‹.®zãì®QjóÆ{ÄÊÌòü«_µ«z"Ôp¶~ÿÏX¿ãjˆr ï#?¦.žþtÕ‚¦ý–¸4¿sÓÔR›|Ë>¦¾,ëûïV¼ ¿~ßÛ/šu1}å¹w¬lå‘[=ÊÕgÄéKÚ1«?Ú²_±ä³^,{û9úz`åC>OÜ7[9¶0,ûõÆWÔg^móþÀ'ÌñµÞ·íôu€|)Çxzv]zJ]2Al ö©ÍÏ_*þé½Uæ<@/óßÜá×§ý*§)Ç~zoå"Ç{,ãn©q÷ù­#VYÆqóÒŒå/¾÷±ù9¿¾¾s`¥ðŽ%Îǵc“*Ë|»¬bØd¯°)Öq±z^¹Ó†`óºZ­ÿy\òT—È“ãaÓm£ ÌPŸ]Õ{öÂ·Ž¨Í»Ÿyß{Ôúc†Ú·€^æy“~f?ÇÜlÑ-~<÷Õ˜¯â?í¤6ÿzÀåé»§YõÝ$ó¼±ü6Vô_(Çx¼=sÓ ‹Ýsµƒ.Õ¤ŸCšçÉöâÁí¿Ìó†ï›¶­þ8Y9.÷[êòž]ûxœo²¬ÓftÚìY’½Ìç†˜çžæTŽËõ•º|îñÞ¿®îdYÇÔK? —ùlz¦.0çá4ËüpüÒáÝÑ·Xü[~ö»]3{ Ûÿ¡eüš÷a3Š:•ÆoEŽÌ7k¹ö_ýÜÙ2ÞN¤¿ÚþÒÙ·-ö¬|¢ìðç­ó×íÚ n~>ζ—y_ØPôÁÀ—”S2^Ú<)N]5ÚáŶØOôÐ&èd~W‡~s&­~·rb™[ÂGŽ&uµÍ¡oyÙYó[ÌNý1èe~WH°Ë= œÐn¿KžVoÿLå ­ñ–7€ânö“I&ÿ¡Räȼ¯t/7Î/ꤜЎÿ°÷e߉oO´>WôóÕу†ütn~;ë8k/ó½"ðËðÆHËüs²ÍGi‹ßšc^§ªk§•_?(ÃjÏ€K=o;W§ cwµí•÷Ìó¡u^m/ÇÁò²ð nwž¶ŒÇ“}ºì©;9ļ^U× òy黡ۭvË;ËyM}rÑp£šŠ<9.ôó^ådàèÙnuV×½4?ê‹Ë§­vùŽ;[¢ÖöWÉÿ>9åÂR9)–iK^U×÷ùj[Ü=Þª)¨³WÇÌŽjýA±ÀúÝp¶ƒÌ·ùûäÔï6ÙR×OùöÇÂéT“v[8ª3ü“\W½ö ô2ï˶ìwÓÊIñ_‰ûžU׿×íÊ7~ZÇ•¼ÏÔéOˆ“ ødþ—Þ®œZÆùÉfq¢kY×©ë¯Ø^š0£%>–ù¤ƒÌû3Ú¿7<-óÙI6+ý’-ëYµiàKw™Òš-ñ©Ÿï î\øeÞ½òžÝîû¬óÅÉtf;Αë©]µÓ®/Ú šôij¬Xy[¬ý¬Ï¦£Û«–ŠýpȰw¯ÛöjÚR¹{þñ‡¬ã³Üg0ÆYûAÿ|÷ âAg¶ß´kg¢GÍк֊¨yc«E³ÝéÍ[nÝn틊?Ÿ›dÇœÙ~ÄfþeЋŸj]»i¢Ô'šÓwQ¤)Vòz´˜Éó'”g;æy“ÖÅ~_4_ýɆñó­íÎ å…¯­ýÎY·—v;Ï;´n׆·›Cþ!šoz=zÖ>ë¼Äˆ‡TûrÑí§M‘û ª}uÇ3ßòóc|l¾Ååú‘w|c]ŸûC3ßrëÅnOíéÿܾª¨JëF¯ÿà°›hwõuk2þ"V"Z»¡÷5k|à¢ÛQ“þ^ëFÔµ¿¹I4Oÿî/îUrZçQŸ\»É3g"êévÕž£åÀUsñ³û΢…jNñAsSâܪʵ¿²‹1sÑí©ÍçõL­û!]Ñ¢ùtî¥{¼½ÅJs¥³û —¬ó@ÝŽÚ‚ãŽunÕºy_C´TÜ8ýd@ºXùùÚàª/C­óKÝŽÚÂõ{Ï_õa¸á»_zêÁñõu†^i³h|Ž“uýéLîÞ·î9¦Ö¬ós¶ïkO$<;üª'µî7OLòâ»4Üq÷“÷[û‚ZÏ«ÇYý‚+Û÷uÞçÔºi÷Ùç-Ñr&¼)³w·XIáé=¢V_>.Ey¶ã›¼_g¬#t×ÑÂÚë¢åÔ©–O–»ŠVZ5|ä’¨}û[»ï~xõØžo=vÆíXþ­û?ÇœžëkÌZ:"Þ^¾úV£ŸµúŸq¦ËºžàÊv}{dU\ÖÍû<¢å¥J‹Ý÷é¢5á»äΔe¢–Û)ʳýVÄ8w]åÿG­ÛrÿÒ\'‰–kZŠ‹ç=j裳ÅîºWÅ̱ ÷mì+D=¶ã;Ë\¬«yÐXÇé^ïXžyo‘hþ·Ÿéñûn1öÃZy_KÌ|îï;w 7Ôg»ÖEm¿þ-/;c]©»ãßt¿bm /T¯ìjõÛªý¹²=ë×<² ï×[õ¼ÿá Ó?ø—¡·fÌ®/®›fðcÈïÆvm¤å¼žÖ~ðŵ-O|aø…æ²ÊêŸëŒöeèñ•AOtÜÿëÆönâJ+×VyNЂùµÖþŸðäZ·Mu¢5ûášWßb¬‡÷¸±Ýß“û„ݧü ³hŽ S³håý<ë¼Úí¬ö‡»ÏÆOmôxV4‡V-\ìvÀÚ>FÖÔ/pÿBÌ$o2~ßíÝz×’p—ÅvZ·’/já¦KC-V{ßõÚ#uÙÆ~ãL?Ǿý°šßÛßœíø®Ë©cVûs?°ê->3Ýœ÷˜µ=2`¡盬ã·‹®¶{P¿®fb]ÕþOsÉ8oŒ?­3Æ8[FÕYçÅ´!oíßnÜ,'Ç6q—á»õer“ÕË.i¸ÖÊ×òÀsw¹ß¨ü¼µß»sûXõ¾Þ€öq„ÄÜù³Ág¢™²Yo~¥UCÅL}4x¸}¬~èŽ MNhÝ?¹«ÝãqÑÄqhÅìîîM«­q£;·‡5zXt‡ÖÍó!ÑtÍß¼7GŽ­gï½wÛ»CD-uÿï£<·‡5wðÃŽ-ö–“ïô‰Öîš+7m3iµòˆݹ¬Ý»f ¦pZ7M³gìÓœo/­[CÖý)ùI«ug{·ÝG·7ZÇsŒîagß2âÇÆ{gÞ¶ðñ¬í˜vÛGibæ ­ïì¶óû‘ˆhn·¶¿ïýÂV­Í0â’Æ‰÷¼Q¿ehÕIäYåö`»¾O»óy_ƒï1ŠÆgô…qÑ*ç#µwë k(Ïöû ·gqðC‡´îí:5jú(ÑøÞÚ“Þ?<&Ì2N¨ "Z‹òl¿ôðn¼u\ùÚ5|Dã?-›TÞ+Û‹™¢r·Q¨Çvü°òêóÍ ßµÚcÏΆíKfí­ñ»]›_þ­›× EÙ—"ÎþqGùW²½˜ù¼ŠuÓ“í%hu¬ø#ní~Êî¹È‹VÿÒ0ïû§“^þµï+O^HZ&ÌwÏܺgZ»˜™Qºä[Ñ ¢n¢ë³ëо½Ø›&~½çO;+µ®ÇÍ_>Àº¿P—÷DÞˆ¡Ç”?2õx³6Éuå®»õ“†ë§ŸºúdʱÞ7] \=å‹ÖucÉö¼º*QôüâÜ_gý~Âë\ZWñËo;ŽNuåí/7NØ©úʱ^?Y²èð¡‚­ZW҃˴¿)êæ>T2iæ9Þ¬ÇOh63æ=­Ë_ßHug^«~$)pÖ×§·Ý}ê{R´ÃÒ_ÖÓ®|þ‹€³~>}ƒ°mÓ#XŽJœ%ê—ö®8sr0ଟO{õ,íðæyoN\pVÔïuôØ´ÖÇg7{ò v¸¹‘BuQ²óäK?? 8ëá³õú4í0ï‹zZ^ðùଇÍ>˜àÝ:@;üÂjñ¢þ}=œå߬/·¸i‡§Væ\êóõ³zéd*à,ÿæ7{òƯ´Ãw|¸ñý?¼"xðFÀYþÍò<Éa&3·‰ú4¯ÍCc|gùÛåºÓaÕ=Ç`êœåo—ó¤C—þèÙR.ê];Ç©œdêñeùÛi×hx­vè+ý@›¨?~|ôlÌŸ}Yþ-Zgçý/ ÐíüiåãïbÜ*¬Ê½ð£PŽõ°¥vEɼæh‡>Ìþö–=¢þ¾k¿êÆ<È—õ°åãs¯Ÿ¸Y;¤OFŠúÅ3§%ŒC;ñe=lùJµC4ööõŸ~úè¼¶¿YÛ“/ëc+f“/×MÒ¨?ñÉÁçüg}lMN¹÷ü¢%Ú¡[{nð9ÑÀ뀳>¶VÑFCšvHŸ>GˆosBÃÁ\SëcëÔIq\÷h‡2ÿ`J°”[åôc}l]ð Ú!yn§Çåߺö–¡v«¼µƒ4}Y𦨿pó§7~¦üÇ/(ÇzØúå«E¹†hy~"êÏ|çS;ଇm¾úÄO;HÑÀ·‰Ǽ-Ç'`ìÇòo+ZßsêÂUÚÁåÓwž\+DƒÚW÷cù·ÑôíÁ´ƒÏÜL+¢aþöʲ—ÑüXþmúv I;£"BÞN™¦†S?Ë¿mËⓠߥh‡;¾ìSx‡h|}¿ß´ŒÍ€³üNúF³v¼Üõ÷ЦĖãƒú0^ù³:²i!©X;è4nóM«¯MÏÏn½{âD–¿ƒ¶W¶?§ø²Æ´¶:W4uNõý¹×gù;j?ȺûA퀾,4E4}7ñ[çèÏŸåïЇåíÀ«SoM5ÏMçVT}ó䀳ü›Î-ðèÒLÔ %š¾ºï5§Ž·gù·x3ÌBíÀUeטìMÿ8èý‹M=,ÿv¹w€ãÑôIÓ¤ƒçZgù·Ñ¼vÀ…yЦ#è¤8à,ÿöšÿ×—“´ý§‹²à‚€?cNÔµhŸ,ÿöeúF®¶ŸŽcÿôŒh:ûã‚ò¼Ïgù·7]~íÌxm?·'ÌŸ,ã)‰œåß.×Û÷ÿ6 †‹æ[oÝSß8Ë¿}Oi×ޣõý餢yÀ.忤okûo^5·øølѼ·pömן7õ²ü;\š][:VÛOîíT§hñ]ÔÒæÕ 8Ë¿#äÚ‘UmÚþÈÑ𠎢eØŠçµq›gù÷ý¤Ÿ,-SèàÓ¼g¹÷ñþŸh™{qG`õ/ðí#osÿ[¢åW:_‚>YÞò|ä>};mŠhiø¼³¢c4àRÞïõ{mŸ~ܺhÙüî–nG{ÀYÞtê:º[Û7”&„Š–sAëZßÙ§âFSO˽/ŒêUŠ÷èAqššÎrï•óë÷v›=0ÿ¼gy÷n{wð‘wŠ•¦Î†w»á—ƒXÞ½´ú{ëGb%Þîýà¼gy÷ò9iÑÖSöy8âï )§Ü·Ø{ÛãÇoÿÆC´ÎšXÐTÞ¦æm(ÇòîÍÎ^4åç&ÑúÅôÜ÷¯„÷G¡‡½.úaÎ~}źW0–íºwÓð°ç>Óö|Ù48ÂL^꺯—íºgPÉ—Ywi{hû|Þ*a^±`Ù‘\¶ë3SÖc*¡í¡c”óÎ óþ;/<V¸l׺¸O ‹ÛK£ÿzþ=X¶g:ôy‡¶‡×Û„¥¢åß}½€Ëö|Î-å/§o–i_Äž¾$ÞK»ÒjMÌ`mÏ¿„声í_¼‰v,åݯÚ>·,VÙŸ^æÚ°ÜÔÂòî¦]êU܈÷RÎÎQ ®ßú³¶›÷ĪtÐnàRNé§v7é;©bÕw…¯ÆT.\ÊIÃcr§¶›z‰Ý›b5…Óß.å•ûé»ù{ ±zú-׿> ?"åå}4mwåÌ#;«W¿£-¾r…H¹i–3&RÛ¢ˆ«ÿùªóí§—r=“nçq½¶[_®»V¬ñpZò´y‚©'”åîPç(ÿùÔÉ¢oºÄšìç»?=‹x%”åïØU§}š¶k]2füqbͨ¿·\³þ'”åïß—ìâódb~|hªš‡£롃ã%m…ÝÿhkÌsg?š 8ë¡ãUïiKœ|´]7úÚºðe±æ‡›{ü`>Êzèx‚ئi»x¿U¬5­lÞÀY·‡ÜüÜß;µ]ŽO×ì²Î{×~Ôڱ𚵿bœk’çµÕù\µ¤ åu>5¯Pç÷Ôy µÎ£öa5MÎï~ fýøe‚ZoæžO\ö»P­¿óŽÓÞ¥–cj]D˜×8­mH< fÒ0?»V˜åwF}ÚM¯¼F­û óοí>qq ¨Eã|hˆ«0óøSÕwÝ­Î ÏïÕ¹5aIˆ™Ü¶s½°P/¾i©°Pص³QÔªçKÔ²Õ:º˜¬€êšw K€¾ªô#,íÞtD­cˆýs˜Û„EŽ>÷(jÖþäÒr1]XòéÔGßµKš÷Ì_ß,,¼®æYÂB§|+WëÂÂç ÕzްÐòÀòzµN¤Ú›ŠÿT¦ÆCcBöo.ëóEµ_ÛààÜñTøj1K?îü'µßnìï­äuhC>˃/þÝ?èNa¹;üÌu;o/XètEÐgÂÂçDÕ:²°ÜðØ§¡O$ŠÚñ¿¬ºi8äÑ÷ë…e°W›÷ç–åüÌàS͇ռ]¶sC>·ý»½éôѳÛÊÕùunÃ8o¢ÎßÊ}µn´çZ%§<7#ç÷꼯վ,¢fþ–ÊSÞÖµQÍ·•ÞÿʯvQëw >A;¦.ù‹¶GžkëÈVýËsê\ôJþNDXÈ-Ýù…¨3ë™]‹D ¢1ÌàÓ’Ö’:e!øçsÂMšŠ}ù%íö†CKW$ KÄÐŒOL3>×'´å'É·¡Wµ¾£Þ«¿Jµÿ§Ö÷lä•ë]j=ISçŒs#­t ìÏÂü\äÅÏ®VëÙ†üòܳ°ðü_ÔÐçg¹Í¢æ rÄ?rÊuañöÈÝP¯ÖE­zæ~fð)ÏáíðœM»òþe¿Rõ »n—ëvòÜqÞϬÃ0;}ç7¢öÙÕ´£$Ì}«– y]­ÿ 3ÏŒqSŽß¼Kß1Æ;³~œ~²¨©O_: ¯·‹)™hð»Ãã_ÕnOÞn´¹_aìÏÖðº¿0&…l4àj_ÐàŸŽûNZ-Ìt|óÛŸŠ¾Y?€ƒqoÞ„ùø¬C¶|$jÒÑF]J2Æký³Ò!‡Ä þŽN˜É¹àC»ÿÜ¡#/þjÜ’íåWz6¾+¥c‰#÷i{2¦=ë9æãümk´þA:§¦Ú•ÑÞÔ9.Ëàçšýmˆ0ËsÈ5>ú‚b†¾Í:^˜çSàÔ(Ìý°yý"w1ƒ¶‹Žf¹+¿7°>ów'Fû°mG¶ïÕ¾jWêïösú³1îзáÄ*ù ²Ÿ™Ï˨v+,ô¹îœ÷qÏðϺú 3¯ïrH~Å ï‘M‹Šæ'ÆM™t$ÉX׬Ö?Û½Y­‡‹j ‚÷¾°NTÓªÇèj;ôîcµÇw¼=6ÄvÜPv5ÆÃÁ¿´ƒ±f–ç•TÿQç¿äþ—°ðzž˜Ñ3+ªmä·Æ÷FrÝVÔð¹\užUÌHóâì°{„Y¯Yí¥ìü*}˜7L˜åx8ƒ×K >Mü€Ñ/äy ã¼@ í¦Œ\dð©â š~b™VÌß3q”l¬O#ÆáïŽÉÅŸ6üƒœWúRÕx¬â6õW‡–çéãB#NS~NÊgëŒñ7à—ãµWØø_ƒÕ^sù\‡ÑÎ?êÙ¦ý~Öæ¯:/kàSßXæÝ@;Mª? yy%Ìò{¼2>ß.ÌÏè2þÇ&n0Æw%¯Ôë¯ô`Ó?>¥Õ>~+¾²ñ‹ÚŽpúð¼ÂÀoãOÔ:´¿©g›ñð·âã‹ÒOÍF½#ñƒ…ÏS«sF;Qö±µ›?WýËПA§8uó_ßYmŒ3*žW~n†~ÌÂÓ:ñ÷bÊ~F»É_jÜVýGõ;ƒï«ñC¯†^”½$¿†~Ô³Íxd«w£}Z†èÿŒq*o©~pëWñ§²·’Gõs9žˆj¹.§Æm—qòO¶ýEêÙð[¶ýMéMé§DÖSø$=£¼’Cþå;iôWröüϺÏô÷’»ç÷z¯é•ü=ÿñ~Sýß©ð ß#:vçmÎì0.Ø¡oÐ7DzŽtða¾j?YæDdzCŠÌƒ¾Ræ?:›îˆòŽ}2ÏùXÎ_è¸3àΠé¼Ræ/Ì‘yÌWò¤ç-ŸÊwcºöÉ|åxvk—÷dî>Yæ*‡^=Rdþ<{ôÉÜä“eÎØÅ p/<Ó~²· ßIy^¼QÞåiŸSÏq¸’ó½è÷a¢?û‚__Ðô á¼2t/&åqôdz?ðù£¼/ß©çxYĹÉSdNòEœïEÏó‚2´þMkØ4\“ùÉ¡£àòî,à ½PàÅxCëˆzŽò2ÿËΆçp” ‡œáÐK8Ê„ƒNDŠÌO>_æKHЉD™H”‰„¬Q(Þ¢`ï(È Y£‡É;ÚÛ8·ŒžKf*ß©©ç‡1q=ïK/çz‰Cù8ÐŒ‡îâÁkÁw‡é9bæð½™”«2ôRñ.øS¡‹TÈ™zBæ3ßi€¥–¼iÀ“{¥õ^&Ÿ9à逧žÞǹõ|æÐ_dÉî Ø*ügôõËgx&à™€gžy”]TàY€gžxàY€ge÷•ùõ\æÕ¿ÌgN®-'Dæ3!s™Ï—¹Ì¡›œ^Î9(†ï×§ûñõûÔq¾ýþú>yWZŽÌ™3AÞ+ºTæÉ9*ï‘wˆŽ•9q–Ê\8'dþ›Îys%fp%f¨p%f¸3ü:f ~?Yê·WÞI9AÞy Ø¡ßÛ¡íÛ¡}з¿v c¾höfßÎùŽðì€g‡6ÎyìŽ(OߊÑðBßX9§p:î ¸3h:£¼ è»rÞc<»âÙ}‘Îñ»vòpä8óvë”9ãw=w”w‡Þxöiãüp”ŽÎùø‚__Ф³2t¿¶žv¦³#þÀçòþ}œ#ŽîϦ3 t.!ôèœíùÓ¾½~‡6ÊA'Aà!2C§ÁÀOûÃÁ½|צ~§6èÑÞf(Æ“Ð92¯r¯¼W4 SžÃQ&r†C/á(:(=DoðD‚N$èD¢LdÅQ(Þ¢`ï(È Y£GpNºãSÏCW(óÍ­—ùä†ÉûµWr>™8ȇòq ÝŃ×xð¿^æ‹Ëá¼Ö”“:4A#4Q'q=çˆÑï×˹®)u2äIdÐMF½ä^Î#£ß½z)¨—‚w© —Šw©ÀŸ ]¤BÎÔ^Î휾ÓK, xÓ€' öJ½tÀÓO¯âœÏ逧ž¾Ÿ]Mhe@® è/²dwl•q‚ÝP&èf‚¿LÀ3Ï<ðLÀ³ £,À³Ï< ð,À³Ï<ðlÀ³‡qŽèlÀ³Ï<ûçoiÎ]Ź£Ks8§[~%Uœ'<—šø~Vº•òøQþºcU¿_5DæÙÁ9nô;ÈA£`²¼ÜEæÓ&óèÍ‘w·Ë»Å}ä}âäëÈ÷Ð?8(^èŸ3‹ü>ù|òóÊ¿+ŸNþ\ùq•«¿ÏV~šü²òÇÊ÷*KôÉ¿’o%ªü¨ò›¶~’Æ/ò‹*çù=èç²>Ž|›òiäÇ”S¾‹üôjÜW¬|“òKÊ‘RþGùò7Ê×(£ü ù•þ>¥¿/!?¢üù å/”¯P>Bùò ÊÐ8ÿÓ#•> Q,@ã4tM÷8ÐxŒ6ã;Ów£Î€Ó÷„ô-š+Ê»B/n€»á/}?áÞË9 èL¼§ ç!ðoÔó=}ð×üú¡-ù§pÎx Np®ø Ø3؇óÄë¹/Q6´Oæ¼DÝ0šã€V8ÊG¢\$èD¡^tŒÌ`✔€rYRȇ¶‡òñx—< À›Ãyà)¿UäJp%>¯p%>¿Ÿÿ:>§~[ÍúÓ˘äýî°×@Èl¸Ú¶ìo×ÎCÝAcöbßÉÃÝaât§‡øpD_rDyGÀ0®8áÙ 8`c'Àé[zú®Ýø\0®Ð7Ù.h¿.xv=Wô5WÔw…NÝPß õÝðì¶_æ½Ü| 蹟G!çoö@}Oàó!s9£ÍxBoô }‡âÕÉ÷Òë¹nðLßDøŸêÓ÷z.ç^Îoã‹þHgèé¼_ŽÌã þé\¸?žýAŸÎP¸È<7€€úÀyë8³ž·<A† ðŒg:Jg:ƒ)^ïãüÏz¾àÅXHçCQžÎ÷Ñxx ƒÂP>eÂ3eÂQ&t#@7t#€7ºŠHØ.x#A' ¼F¡LÊDQ¼Ž:Ñ)œ§‘r-R~E=Gô0™ ºSæÃ!sß´ñýýq€ÇFêÇCñà5åã;eÎðšš à+eA#eqŽHòõ¼ÎÐqt”„2É(“ :ÉÀ“ŒzÉ}œ«QÏ‹ƒz)à-ïRñ.¸RÁO*ì› 9Sñ> 6KÝ4ÀÒKiÀ“v”ÝI:à逧žx:à逧žž3@?re@7à!¸3ÀkxË<t3Á_&à™€gž x&àYhsY€gžxàY€gžx6àÙ€gž x6àÙ€gž xð瀿ð—þrÀ_øË9à/ò òá ”kIÏu9‡óUêyNpþIÊ}]ˆ2%c9÷žÿz¥Ì{Ý+såÈœ@“eŽë•2·uŸÌe]È9¬uCÿ(^PñÊY¨â£“AÉo“Ï&ÝßG+¬ü°òÁä{ûû]òµÊ¯*ŸJ¾´¿%ÿi£“Ÿ´ÍÉÏÙú6[Ö?6ïŸG„|“òGýãqå{úûås”¯ùïÆæ¶>„ü‡ò°÷/ârò Êô÷4öÓ¸¯ÆúŸ/3¶Ç0?”§’‡ íasèÜ:qÄ{'ØßýË ?w´÷ùœ7ÌmÂíÁ å¼PνÑÖè;.üßíÌ:òEÛóC›ñÇ/¸Ðާrî ´£ Ô¥3óÁØÕèyêOp¾WÊOþÂQ'e"A3j*ç†ÕsÓ/â]±1œS,ïâ@3®óÍ& ~ð%NàÜñI ™\Å9])O<…“©à5vÊŸÊù¤òqN) )Wz>êLàü¯”÷•r¯R®Õ"à*Â; xŠÀcÅßÀ¡åï*žbÀ(g+åC§p“r­SÞóR™[*…CP=ß9ð–·qÞt•?ŠòxQ¾)ø2'ý¿Šs£S®­+qû€+q{õ€+qû•¸ý×c{Š´}»Ì!…~0mi`çͱœîþ´ƒ}í {Œö(cg{Èì€:èäÚyHqij#àt—šÊ;áÙ 8Úx¨qF}gÐt>Œ.x¦ûŒ\ðìƒúÝsCw̸¡¾êÓ½'tLjž«pwò3íó9W¥Êûb¬ñEóE›ó…ÞýL2O%žý`'úæÒåýQ>ÀGæ¦< ‡Dú&¾³£œåz~JФïÓ‚À3}7 0tÜɹ6)o¥ž£åCA?ãA(žCÛeÎJЃLaí<¼†£L8ä G™ðvn#@7zŽÞЉHЯ‘ÐMðD¡Lx‹Bˆ‚¬ÑÀ=AæÏê”ù/«dŽË£2‡e•ÌW¹žsoÆá9n%ãñwãQ?~çFO@ý„œç(|%€F"h$BÞÄ¥œÓ]Ï_ >’ð. :JB™d”I¯É¨—ÜÎÍ’òué¹,Q/¼¥BO© — z©)ü¤e—‘† iÐa`ià5 :Hž4Ø+õÒO<ðtÀÓO<ð ð“úà;ô2ÀC!l–Aþ´3ÏÝLÈ x&à™€eB–LðždžxàY€gžxàÙ€gž x6àÙ€gž x6à9ÀŸþrÀ_øË9à/üåœ`·7(„óÝS{ÊOOyéõ¼ò2odœ"s|Bƒ«9¯gé0™×t†Çð1¤ŠsÆë9<Û8o§Êשçè$_D~€þQ¬Ð?> Ø@Å—‹”ïW~ßÖç+?Oþ½¿O'ÿM¾[ÅöÊ7+¿Üß'+¬ü°ò¹*¶·õ±ý}+ùRåCûûOå;•ÏìŸG°¿Ÿü-ÿH~‘üaÿõùËù?ò{Êç)_G~îrsƒþ9•ÏRó€Ëù¨þ¾©¿ORþˆüòAý}­Ï!£æýý‰ò%6~„ì¨çD[µÃÿí o;ж‡è_´3´SG´!G´=G¼s‚LÎxvF;vFY”uA»wÁÿ]ÑFÜÐVÝhœÁ¸Žrhó¹ÙyŸ7Êù Ž/Ú/Ú²/Ú·ß0Îì‡þàÿûƒfðÐ7ÚÀ¾è»ß`”¿!¨O߇† n(ð„¢|Xç‹fŒ:À~Ù"![$hGEó¯QíœÛ8¼Ä ^ êÄÔq.àXðÛιˆãð>|Å_<è%"è%¢lâQÎåKy “ÀWÒz‡óÁ[2øÉ¯â¶ùÀ‘OãèçWãúà; 8 éé;å iLOExŸ¹mK@¯uÊÁ—E «—’aœ¯–ò╃ 45Ô×/EÝrð•C?¼¯@½Rà)sáüËexo‚|&Ô7Çrþ¶q~B=Oa5ç.‡Là_~Ï}]ýßÄmIÿ÷ÿóüäÚ¼äÊœä÷?'ù=ÎG†I»B¶!2Ÿl5~Ðõ@дC?²C›£<t¿=ÊØ£ÛÃVtg»Æ<Ó½âßÏtçµ#àŽÉ å§ûŒ _gÀAÃmÅõéXÀ]ÀÝYêŠ~Mw{º¢¾+êÓ}“t¤žÝðìŽqÁÏtŸ;xö>àó>ºsÍpº‹Ìøèî//Œc^€Ó½T^¨ï ¸7èy“OCyüõéä¡Œîø¡{z|ÛxXóC=?ô ºsÆúõdz?ÊûCNº'%x€\à#º œÃùrQ>´‚ » ðÚÁnƒ÷s~Ýà©ã¡’î&Eyº; ð|à)Ä»0ÈÃ!K8Ƈ<‡£L8ÞE€nøŠÞð {E¢L$èâ}hD¯(Ô‰êä¡7e¢1VDƒn4tÃyØK@;6Åÿc¡ÃXÈ‹:q#8ãÀO<èѧx”GýxÐI”OO <Œ'¢L"h$BÖ²Îý›>’Àgô• Y’³u’éyS𮨇ìàNÁ»TÐK®\ð™ÛËC|Þåç¼™—ræ•9é§ZóÒ—VAe).­<Ð- Á/¼çÁNù(“X>pæ“ïG½|ŠÈ(—rù½ìV P¶ò–A–àÕȃvÊÎ!g ôVX!x)D{(Äs!d,¬õ‹ÀgxÐÀ· xËð+Îbè©´‹¡çbÀŠ·˜x/Åࣸ—]Y d(Aùà,¡8‚â Ô)¡X‚è£l)ð—‚¶¹ËQ¶ú,…-JQ® ôË/ƒŽ*€«l©è_ÿ˜À6Pþ_ù~åóÉ×_ÎÇ+ß®üºÚk ßÝßo_Î_Ûî9(¿|9¬ü°Ú{°õ»ÿiŽ¢ülÿ¹ÊoÍS ·ËîSô÷—j~ò[çˆÔžÅåÎõ÷s¶¾Ív¿¢ÞsÛ½ å›.7'9:Àê‡úû þçˆl÷,úûÛù‰:cD¶XÄ4Æà‡²v&üÐìÈ_ŒÅ²9 ½P> Güu„èŽ~'´]g<;£ýÒèÎèK.Àå‚¿®(ãŠznø¹£íx Ýçé˜'ž=ÑG¼Ð&½P× t¼À·7`>x¦ûå|iž‚vìWÅ¡ª?ýÅ{º++}.´3x‚ÀkèG0Úd0lKwêн8!x <h… [xõoà£ø8"(þGùÔ/Â/tŠ ‡(ÈRŒzE)œ£¼2ÅÐø‡~Wš¹€ÅGúf)ôXyÊ€+—Æ1àËEý2´\ÈWÚ¹°øÎÏåø[YÊ!s ð l9h€v1Ê—ðZÂݪ6©Î ü-¥±u5ȥѼïM(_Ló È«¡^%Á¨hjµx*@·å+Q¾ ´*ÁS%ÊTÒüfç{¯ 8t]A?'A«zÔð«D™  Ù+¡ÊÉÖ)Êÿv^ò2ù½ÎE ·ÿk{%¿5ù=í‘\™‹ü¿Ÿ‹ðûœTIÛ€¿èwñ<ma` vлx£¼\öhŸöxogÊßä€gØr Q¾G´OG”w¯NÀå‡Aú÷/½rŸ.Çæ{Ù•ýöé\þÃÙ:ù½Ë|yžnÿ/÷åHOS M}[ï×9ÜŸ©ÿÑú?­ûÓx©¯åþ§¯ÿ§pܯ÷³ÿbïLà몪ýßÝ‚HAl˜J ”Fd(-Ð@Knh¦›ùf¾™oæ›ùfN1 -UÑOTЂ‚A¦Oˆ Cdº)J‰b%O@/¨`Qž¼ï:{ŸÜClÿú|ÛÏ'½÷ž½öZk¯½×9{í³÷oíÔþ%ûÄdÿ‘œ ù¿åSQÚ—ä|€Ìûņ–ÏŒ™×kü2Ï—yºØj!FߥßÈš¾ÌïæénÛ[ûüzÎ.ç¤/dMAâz˾áÚÖ2·æÚðÛ†Ü8hã ƒ&šxÚã‘›@ÝtI€w¼Ð#>L`œ$1’øŸ$ù=§§§nè“Ñ+™ÉÈJFV24ÉÈJ¦)ò‡íS±}*²R‘•ЬTl‘†i葎Ütø¤£o:rÒ‘“AYe™è˜I½LêeR/“özàïA:dQ7 ùYÈÏBçlh²¡É¦ÙØ.{N/ä ?º\èr¡ËE~.mË….º¼9=u·.=òiK>mɇWò  +`xÑÇK¹—r/zò½ å;4…è]ˆÞE´§žÅèWŒþÅÐ#³˜>-†¦ž%بû”`ŸRä–2NJÑ­Œ¶–!» ýÊ&õ”½åðð!Ï'ŸèåÃ>dT Su+©WIû+)«¤¬=ªà[EY5|«‘_üêI=ݯg ¼jÑ¥úZxÕA_‡ê_‡üzäÕS·šzìT® رYM“:,h†O3|š‘ÛL›šáÓÊ÷VìÑŠÍØ Ÿ|èÐŽ>íèÙŽ.í´¿ƒòÊ;àÑ ÿx÷`ÏxõІ^lÙ‹½{©Û‹ž½\ëC‡>®õíÔë Ö¿ç÷ZØôÀ¾»%âÊqåâJÙ“«6{îð…ÿ+ü_áÿ ÿWø¿:Üì«Àÿþ¯ð…ÿ+ü_áÿjµyG†ÿ+ü_áÿ ÿWø¿ÂÿÕ fÝÿWø¿Âÿþ¯ð…ÿ«õ&¦Åÿþ¯ð…ÿ+ÁïÂÿþoaóàÿ ÿWø¿Âÿþ¯ð…ÿ[çðñ…ÿ+ü_áÿ ÿWø¿Âÿ­s@ø¿Âÿþ¯ð…ÿ+ü_%™ý†ø¿Âÿþ¯ð…ÿ+ü_eš÷ø¿Âÿþ¯ð…ÿ+ü_˜xÿWø¿Âÿþ¯ð…ÿ+üßÂÂÿþ¯ð…ÿ+ü_áÿ ÿ·0ð…ÿ+ü_áÿ ÿWø¿Âÿ­3Lø¿Âÿþ¯ð…ÿ+ü_áÿÖ^Iü_áÿ ÿWø¿Âÿþ¯ðëÝ%þ¯ð…ÿ+ü_áÿ ÿWø¿µ–€ÿ+ü_áÿ ÿWø¿Âÿþoa"áÿ ÿWø¿Âÿþ¯ð…ÿ[øø¿Âÿþ¯ð…ÿ+ü_áÿÖù+ü_áÿ ÿWø¿Âÿþ¯ðkŸ'þ¯ð…ÿ+ü_áÿ ÿWø¿õÞÿWø¿Âÿþ¯ð…ÿ+ü_ÖAþ¯ð…ÿ+ü_áÿ ÿWø¿à9)ü_áÿ ÿWø¿Âÿþ¯ðÁnPø¿Âÿþ¯ð…ÿ+ü_áÿrvLáÿ ÿWø¿Âÿþ¯ð…ÿË™…ÿ+ü_áÿ ÿWø¾Âç>¯ðy…Ï+|^áó Ÿ—}J ŸWø¼Âç>¯ðy…Ï+|^Þ3+|^áó ŸWø¼Âç>¯ðyy®+|^‰ÏËó;`îƒROüÅgúmÚìŸõšý³ÓæìD¬>?a½¯Ž0ï«w:νyô)ë}u”ãÌÛ´Á±ê7ç¢Ã;©~‡Y8V˜a“¢W¿«°p¬68p¬æÌ» ï¢÷Õ~ƒc5ib¹€y_=o0)zkEn}ÎZöÎZV±f?Ö„yg½Á`R LŠ0ÇûŽIs;Ú`Y \Šp}>[Î~|àÜ[¿Æ²²Ö6˜=´ƒfm¸ÙCÐïEälˆ…ikοÍ:öÑèó">ņÐÚ‘œó¶°)|ÏjÖœ}ók\Š,+ÙC;aÞ_{ûg—8ν›÷×QŽ=´c&Ö 7çßvê÷ØÖ¸(Ǹ ®•wÑ:R´>«bƒ›08Qæ}¶ßà[/ÚW;`ÎÃÍ™5¥hs&n§y·=a°®Â v…Ûì³Ýið+& æUØŸÙkþ'öÚΛ3r‘÷Êcp,ú öÕØ¢³r‘ü«Þ?±ïÖó7œ—Ûkð¯¢CX‚[aá_Mü«ð?³ç6ܜۥ÷Ú.Æ»’we²Ž!kÖ^p³>çÑûû伿œ’uOy§/ïòã†ÌYÿ z¿„µf6 ÷Ê™"9ÿ/gˆR&ô{zäÓž|Ú“¯ä@WÀXð¢—r/å^ô,ä{!:Êwh Ñ»½‹hO<‹Ñ¯ý‹¡)Ff1}Z M làCF:UP·’z•´¿’²JÊ*Ñ£ ¾U”Õr½•zÕȯ†g áÛŽ-ÚѡڑßN›;°K¼;ü:\ë_6ì¡^z÷R§÷b³^xôr­¹}\ëãšCÈ?™ÈÛq¹ƒKܽx-Xbk‰¥íØxûG$þuîÁˆk%–µãX‰]ÑߊS÷“J,J›âÐÅûBì¸RbHgü¸¿óª÷Ù1ž/fëÉ›I\F?}àüéâXË^[–ØŠ1ðGq•ÄT2·²c%‰‘$.’˜ˆ~\ˆ‡VÌ#ñŽçHl#q3¦±ã‰[ìxÅŽMìxDb‰?$öxÃŽ3'V\±8ŽØAâ‰$>¸@â}Å2ï·çüŒ¹…9¾=·wÎëí9½Ìåí9¼ÌßeÎ.óu™«ËÝžŸ3¾ÿaórü`Ÿsrç|Ü9wÎÃsp|ga.sp{þmϽeÞ-snÆ‚5ß–¹¶Ì³í9¶Ì¯ÃõضϜYç˼æ<Ù¬Æh³Î | 1þêy¿¹×à2Lšý”“‡Áz'áÓØ Ö~›€>³%øg^j´Þi½ƒÜ¥ñdŸœõ^,Ü`%ì2gŽh§yŸèÕg­sãú¬±µO>RãXgm¢5†•œ“—37òîRÞk%ë[Fâ˜~%¸[ò~+q¯ÞgŸÃg²“Âô¾9œÒ«Ïû&Á7žñ|ÏàZ<Ï’4ôO“g/õ’™Äï> à•†Ìä%‡éÛ•¼ëK£MiƒúÌ@úgð݃~h‹Ð)›zyÃú¼qmk¤]rMž×Èiäz£ÈÀ¶èÐÈg#õ±M#üJàÓHß4Žéå¸2ì™M½ryÆ w#ŸèZÁ_5íî‚íj’?¾7ÁÛü6x6ñ׿¦~}kl¥]ðj…¶‹k]ÈóÉ5è»Ðµ [uaÏ.dtÁ¿LìÀµJhüÈö¡K%2ýØ­½[iK¥È…O74•ÈoE~74mØ¥[ûáÕ&Ï#êú„ûuóÙ†Ì&hê¡í¦^í­çz7¶jC—Vøw#¿ ùÝðl¥Ýð«•¶`·nÊ›„/m즬•ëmèØ$Ï;Ê›àцžm´£ÛvèGB;zv‹ò D§2ðmy´½ºôé‘?ôëáZ/íìEVºö¡Kßñ?ùw`ýúÀúõõë?¿~}`íúÀÚõµëk×ÏÚ5þ¿0wÆÿÿiëØòî7÷;¹.¾á7}4kÎõúç®ÂCç{ö;ú8aŽsWófßP¿Æ ²ò1Ä ˆ Ç™«låhv[¸Áýì×û‹,låh¶ò¼Ùóè[´?°låa³'Ém°Û† ¶²GãYû#ô}k¿ã´Ù«íÀ€˜5ùüfÏã´Áˆ5øÊCc4Ò`;p ¼¿mÂà+ÇêóÈÄ´ÙÛï1Ëc"Òäf4y"ôekä„ÁƒˆväeØk0ÜüÃmÎ`Aôü¶%_Ùë8‡®ñ’¬3XÓ¿Ík°“&ÌÈ ŽsX&/C„ÁS0{!—,7¿Á„˜v`-ûågˆuàBL<· fOdÀ`.Oèižµ/Ò‡°p—# FDÀ`/kœ 9Ü`¼yLÞ†ƒó6ip˜ÃÿLî† :oÃÙýÌÝ û %ƒœ­’½Š‚Ý*x­‚¡*ø©rî:)\c0 ³ì´ð˜ûõ´òôÁýäpðþØÓz¤Lm÷‰÷6­Ïy[xo&ƒw?ù" VÄ ÎÙ ØËrÞÌÆ_–s²OöÝY{M#ôÞÒä}6]ð° KöõYgÔæõy49.gÏ҆͹³]kZc×ZXX³zÚm5‹Ö{;«VΟș59C&Sm9?&x¸Ö”(}ÖMö~.ì×4˜XÓúœ‰ìO•s"röL°¶ ËÚ8höÃΚý ázZ/gF¶Åê3 ÖÙôÛÆ‹ƒ6nVOù㡉‡&¹ñØ+ãgó=Þ Ø:vzä$1^’à‘Ä÷ø¹‘“A¹›:É蕌œ ôM¦,ºdd%3žRøKÅþ©ÈJEV*üSÑ;[¤Íê°"¶¥Ã'dp­lƒ3Š•I½LêeR/3i¯þÊ<´7‹ºYÈÏBç,øfC“ M6º´¡{6öËA×lœ ]úµò™ }|üèé‡W.t~þj½:,©F‡2äú¨[&q?v+`xÑÇËo/í-„g!ß ±M!² ¡)œÕaOõ‹Ð¯ýŠáS M1m.F¯bhJ]‚ììSŠ}JáUÊ8)¥ e´µ =˰o´åèPrxøç“OÚèCo2*Щ‚º•Ô«¤ý•”UNêЪ ¾U”US§«‘_ ÏxÖÀ³†ëµèR }-¼ê ¯ÃuȯC~=òêÑ©šzìÓ€® دYMði†O3|šáÓLûšiS3vjå{+öh¥}hР  C;ú´£g;º´SÞ:ÓAyü{àÝ]Ï„åz±e/öî…gï¤íúСk}\³æüòO&÷ö™ÊÅç)í8Ý£ËCfñYJg,.q¸ÄÝrS’x{ñYÊýa7Ú/Îup‰“íyñùÈÅXê‹×¿kßv¬*1ª›þ9Lu‰=káv¹?¬F;N´ãBg<(û™$´c@Æ„ë1>€Í(qœsm\b6;V“8ÍÄeV,f¯‹Küe¯‹Kì$ñ’ÄJ'9c#;²ã;ö‘˜ÇïHŒcÇ3v,#1Œ3~‘¸…ñþuq‰O¯‡K|±¯µpg!±ƒ7H¬ 1Â5f¾)ñ€ .ùàÜ?û·™Óÿµóy{.©Ç¼¿Ï<ž¡N‚…ÅnÎ"í5¹Òæ Ϥ94nr¢í28ln¹oå=ó;ÎÍÌý0ÛÌÂA09Í îq¬ÉE0x<Ã&_Y¿ÁÝ7yD†Bç{¬µŸâ×øœñü%Ѷ tÈ , ™iЦñ=>IB¾{¯Y2DN6ò²áL[Ò(Oƒ—ú"tÉ ­¯^2̦=Ôm¡n£|—ç,tÈ-@Ÿø×R^¿F~7§]‹hCã.}^§„ßü.£í~ú¦‘66¢o#íj¤ò7¯o¯µðoB¯jh›hƒŸ>hB^ú4Á¿ ^]ÈkEï.dw¡G×»Ó%Ï3lÐEûº×5«oÑ^ùYHßT¿ˆúÅð-Fßjd–aÓ2t(Gn9×ý´×¯Jtð ×|ئ‚ߕءêáS‹üJ®W"« ™­«æ¯ê©_ÜjøÖÀ·ÝR&ÏmìUŸ6tªƒG<êСžòzÊëiC=vi@F7òÛÄ>ü5ç>Í\kÆ6í´£›65ç»¶¢w¹íÐàÕ͵vtnÇ6íè×Ny·ØÞ=\ï‘ç9|z¸Ö ¯^lыνèÓ˵>ä÷ñ»Oî²V'ÿ¬ËX—?°.`_ùµùkóÖæÿuçñïÚ|”¹_K›Ä¯eìLÌ;0ц ADÛf!W²ßàpN‡pn$W²LŠ5Xc&¯ŠÛà¡M›<É>ƒ‡6aòp¹ >óœÁéôèœ\’+ÙÊ«»(¯Ê“§kÈ‘+¹wyUÆLÞCÉ0fð™½mÎäTñ˜œ‡s‹ÀmpÑÆ >s”É{8l0u"¹UÆLn• mÂæ3Í“&/˜Û6k° ¼&¿Ê„Á 29V†MþÃ(GÎäIƒ‘æ6ùÇ ¦h¬É¯2¬qy,Œ´&oA˜É­â –|cÎè°ÁG‹ÐX=Fó¤É}mð †4Fš•ÿ0Ê`îÒçª-üÑØEXia&OYÀäY™3yÝ/mÈ`6‡™\+^ƒS:h0 ÂLå€ÁM›49W" nA¯É»2¦ñÓ¬Ü+ÇÙkò#îÒ¹r°Düí9­¼Êá&K¬ÆCµr±ì4¹Çù•ÃùXü"_¢Oc§žÑÿæX39–Ãöë<®q[%‹…ëiò$úö“+1ÒäTÒ9­üˆá¡ü+‚#!ØK‚GgaÑE :ŸÁŒí×ÖçŽü¸½¯Að­/ÊÂfè×xp’_n!gÅ„Îé(øtC¬Æ“‚éda½ Œ…93=ÐXP‚ë X ‚ç&ù-K!f\cBÉÔ]0¿Ip—\ýV0ðä<¾u†NOíJ°ž,\'·Æd’3ùÛ»qm´ñÐÄCMIò›¸¡wCŸŒ^É蟌¬dh’¡IFV2~šÂ_*¶OEV*²R‘•ŠÞ©Ø"ƒöe󻌲4tJ‡G:<2Ð1ƒß™è˜IY&õ2¡ËDÇLÚë¿gZ‡"YØ% ùyò½³¡É†Æ/¼ù+AŸltÍÁƹÐåB“K[j‘•Kûr¡ÉÅÆyÐä£g>òò‘—¼|xå£gü Щ/å^êVÓn/ºò½~…ð*„¦½‹Ð»(Z‡@ÅèW ¿jdCWŒ¼bhJhC 6*Á>¥È-E¿RÆI)¶)› w¶/ƒ¶;—ãœú>‰åå|Ó:œªˆÒaS=r+àQIY%eUü®‚oתѯª‘_ ÏxÖÀ³Fl.µÓ:«ƒ¾Õ!¿ùõÈ«G§zhê±Sº6À§›àÓ Ÿfø4ç[5Ó¦fìÔÊ÷VìÑJûР @@vôiǶíèÒNy6ê@Nå=ðïwt=ðê¥ ½Ø²[÷³=û¸Ö‡}\ëãšÌ—­vlïÄ[ÃKìþ§0kô‹cq‰Ã%—›ŒÜ8œûÕÅ™kõvlmÇÐvÌlÇËN':é²¥c‹°Ðöj,fÿLr8ÊíÌÂ\4˜Ë“&WãÉÏ8irˆ¹MŽîK+‡M.ƽ&ŸË¸ÁM7xeðZåÕ¹­¼Šý&'˸ɣ8`pÇM.1ä­Æt.1 ïØ¯ó˜/ä0Ÿ6øbØÂ63Xb:וWeÈä?ìÕØÅn¦ßä6œÖÅg‡il0+wu„Î;.y:ΦÞÙne#uN™Ö8`‚×+x—‚¿•8¤q€oW°®Ç5nnâ´Æ­Jœ×·uYöü8öLÁn—[r¦Ð–4tNÃIÈNÁ.)´'Cæ'2_’¹ t\?s\çúNãz }›BRï†&É£ó’d£_6íL£ )Ч „rfÀ7ßiЕ…iL2íÌÛ¥1yðË–k½K:º<Úš mv¬Î9(¸Á¹È( Èlä³Ý ¹V4®ñšJüãSÊáÙˆ­)+®^ ð*—ùöJÓ˵IØ&½›àÓª±G|ªÑÉŸ&ê5a§tdvÉ'z6QÙüÁ£‰kmÈéBvvn£Í­ði…O—”Á7º\¡gßó)ËztÊÇîب€þ󢇗²BùCf!ö(‚WuŠÑ©˜kÅð(¦ŸKѧ]ʨ_†Œ2®•ѾrèÊ¡óÁÃ'Ÿèã“6ãBæQÔ«DÏJlVIY%e•èQ…ª(«†gµØ 9Õð¬†g Mði†O3|š‘ÛL5çUìE;[§õ Ÿ|èÐŽ>íèÙŽ.í“zjÐAy<ºáßCyÌ­àÕCz±U/ví¥n/zör­ú¸Öǵ¾1yˆ.Ñÿ¢ßåw£ù~à=Ê÷(ÿ ïQþUÞ¡xràý‰=ßÿg½?ùw}w"ãÙmî2·_ÊÁ8l0†c f=þº”q¿Œ±± ,7yÓ Ë¬á(Õ¹bÜäM§¯päÜò¬a|,Œqæ7ØöÓ&oºWãÛ[XÑ:—äõµò¦G˜¼é&ßÉ“y+Çô£ñPw(ÿ¢•¯ËcòÿN˜¼é>“‘ßᑬáy“Øgróûˆ“÷dÀä=™×9‚­¼éc:ÿ´…9ì å>±r£ÓQ»˜ÃÑ&ÿ×ÉÁßÕ^§¿>«ámpõ©1©±õ­\Œ?ýhøÍ1´õ˜!ƒ?aò0˜<Œ&gX¯{ØíÈFÿ®ñëÜ¿k°ñšY“ƒ‘1yÂØ"ìá!“·˜:kiÏZ®m›Óy»Xë‚Ïoå2ž]„C<¦s1 ¦ýÉÔ=¹_çP£Më°ù:Æ#–|(ëfu²ä½ØÊ¥Îµõ\[¿õ½xÚäeŒÒ¹SißFÊÓ4Þê&hÒud+¯à„Î-¸ 7Íë¼'’óDð†­\'ó&ß <7tÉ%“–ü’ .Bc ^pæ˜Æ–ü’»@r 6±äM–<‚%,9 $Ç‹äÈñ˜ü#óWrHÉ{&y“_V°a%oŠ•ÿŒ¶»æõÔ(Çʳ,yÜÈ»ž©Ó:¿Ù…:×Yý'z"+>qÐÄQGy<™ð‡.žvÅ£c<ºf"'‘z‰ÔK¤^"õ©—H½D긡wKì½z7<ÜÈuS/…6¤ W }™)óuôJåz|¼ÔKž ¢'eiЦ¡íM‡&}^Oß2¨ŸAý th@fô®y¸æášÛxЭ…ºYèEÔÁ7 =²äÞEèÜŽ1=µë€.‡ï9蛃®9ÐåÁ«þyðÊC¿<êä!+š|xå£Sþ¼ž. ¿ù^dy)÷Rî•8 >Þ½f*IÝ"ú¸CÚK»kW„¡G m- ×ÓÀø” »¹¥èÕ‚ìRhð+…o)õKáYEÝþʱ[9<Ê¡)G~94卉‚ë\¯àz'v¬ ¬ ú*®Wq½^Uè[ï*t­‘?xøl¡íµèT‹>µèã—?lëGŸNÚâÇ~ù„®^ ði€Oƒô!<iS#}Ђ.-èÒ‚Üiº´ Gˬž·!£ mè×ÿ6Æuc¶úNè;Ewè;¡ïœ×Óä.êôHŒ"s:ù'1ûì+Ïξâ;®Y¼æ,7 ;ÇŽÄ,‹ã”Å)j¸ƒØñ‡ÄÎ8ÃŽ/¯?;÷Ž/Ž!ìøaqމ$^pÆv\`Ç‹÷“ÿ%ùuœû¥ìù½åíœËÛsøýí1wÎËmì{ZæÛûÊ«³x^½x¯¹sÞ¼x®lÏ‘íù±=/>tɾçÁöüמûÚ{Óã³ìkN+sY{ºÌ[e¾º¯yª=?µ÷©{–ì{ŸzÑ>æœösëÑö\ÒžCÚóÇþ%¼}“™ÃyL›ñߥøëRh—âÇËKËø\Î8_AûVp}ńɋí5ùç|&ïìÉ57frÌ͆r]¯¤ÞÊq\Þ‡zL9Ê›49­ù Ç®áÃ&‡5Ÿ›|:ÇÐ&¿Îpd¬ÎI&¹%7œä&“Ü¢‚§/9C£Ña5u·†é¼¨[£tî…MÈÞ´SçþܺAçýŒö­ð‹Þ«óþ7¡—5ާž Ú5s:¯›ä„Ƕic`mr1î·Íë<Ûöêe‡„9¯4ënøºÝ:o©ÛÊY ùŒÜ´g3×Rù:¦s¥NèGé»t¾ãLÚž>¬ó H.³8á#óhs¹æF7mʇGõr¨“Åï8Úëå¹[w—¾õQ–HýLÚ›J;¡)†g¦<ß°o¶ËÞðÌGmO‡.“¶VPž.Ï>3‘[JY&uË¡« ^–<»Dy¶"¯NxʳHxÑætiÁ&-ÈðÛ‹¾ùòü‚¾ƒºÔõò½ ùòü’guëÑ‚žЖéÛŸ>uغ›Wɳ“öt gƒðA·*xuP¯Cž‡òœ¡ÝÔ©B‡hkä™Äw?öê¤Ч“ï ðo¡OZhc'ýÝïNì×)õé§<;å$åRïNt=™Ï(Ê{bueýûKÖáþQkpÿÌõ·ŵ7çºÛþž—ÿ[knÉzÛ?bϲ¬µ9ŸËËÛÿ…=Êÿ¿Ö×þžuµ×5µý­§ýoíEþ¿¾–æ5~ÌØ·ÆƒÇè8Σ…ñµ”>XŠNK¿Ëèïe”-ÃÏ–3.—ÓËiïr|~9¾±BælЮ˜4yƒ±óA»LÞ`~Œ]6yp¨0ù!#MŽHd²×‘¯kÐä Žtä žvä·GÖJxŠž‡z9ƒÃM¾.~6mrÃïp¾‡Ó†p¾‡™œÁü^ÅïM^“78ÒäêÚ¥suYyƒ‘u$¼Ž„÷‘ó&ç¤Wçë²rO.19»Ðï(d|ý>kr›ÜÁð] ßÕÔY ŸÍØê|Ê# ßJÙùØñhl{ô„~ìCýcúuÎøcø;6Rç÷9ÖgòG:òóûxdï1ùºæLÞ`x¬AÆÚµfÞäìÂf' ÿ~GnÐùº$Ïc$ý¿–:kiëÚ^ÿJñ'F›¼]йøK€î$äœÔ¯sôZ¹ƒ©s²|G¿“±×:ø­Ã>ëà±nLçïÚ„=7C·9Öä†×)\_O;ÖÃo=×Ös-•¿èµ¹¡ÝHÙF™“cÛM2GG÷M´wz¤ÓV·Ï‘—K®!/a¯ÎÇ%y“$¿§äÓÜ<¨sIÎ_+ßÖ´ÎU,9$w²äöµòjtNO™rHî±óu.¤óÇC¹|%—’ä#–\H’£×Ê5¤óøÆÐžøÅÄêiˆä^’|š.ä¹h¯ ÝbÑ-ºtøåñýBø]Í…ð»~ÂïB™k£_úÅ¡[åq”ÇQGyzÅó]<ºÅË\úD™CŸ}"ô‰Ð'ÊÜz7ípC›CûòÐ5OâtJá3…>KAïx¤ˆî2ÇÒS¦Ôy=•J£Nz§A—N½tÚ’þÔÏ ~u3¨ëᚇk®Õ ·Ž~ /á‰ý³hwöÈâzÖ¼žŽå£W ¼¢´9èš#ßiWty”åÁ/yðÉ£y´16æ1vòá—NùèT€üäˆ|xç…G u½2gçwÄÔ)¢n‘Ä0è@V úûig õK¨_Bä•`ˆd–¢S)õK©[мÒy==¬¡®Ÿ:åðí¤~94d–K\ÿ øWPVÃo?|+(¯XGè%Æ€o<ª¨SÏšy=µ¬¢=µ´§ÞµÔ«E?:6PÏ^~tòc?vðÓ× ði8> óz:Úˆ>دEâ#±‡Ä5ôC :´ C íhCF2Z¨Ó†>mÈh£ÍmŒÍNêtBß }'ôÐw£‹:]Ôé‰\¢çÛòÏŽ?$ÆpÆûZó²c€ÅëYö^I™kÛól™_/Æð¥ï>°ÖdÏeí¹©=·´ç‘ö¼ÛüÑ:™Ëís­Æ¹FãÄÐÝ×ZŒ=Ÿ±×FúÏzçúv•½mKṜ~\½WÐ?+¦M~Ë€á¬|–|_IŸ ía³&ÿ$ŸáŒ…ÍŒ‰£¼:dÛÊ[ÑùhhÅçÖ¹Ô÷ê|Á'À?’:‘\Û4 ón¦l³Oç–<ÀnÆR:cÈE»øÌE~"ŠäîÕ]’(÷ 꺩[*|¯¢NŸYâëÈ.•Od×ìÒ!P2Щ߾”Q§œòr“|o¡|v¢[§êå˜W÷Þ»óÿ[®—îûE]ÏÁ7ÄüFÿNÝ~ϵ“‘11/œÚÑþjîbNÁ‘¸?”|á®ïh‰»£ï¡àŽûŠ®ëO®uQxÃó{~?…œAKÎÍ_®»÷­GÞ¶ùº^úÎæµÏnýqÌû]áéyÇ×§nýñ{wyÌÜ‘¢_Vð™C\5ŸàŽ;ìzðùø[ünùì埼qði×K“¿óþöˆcƒSߨ•0ó©“‚#¾Ó.{ô•àŽ[QaúY‹þÖŠ¨ó–Ÿ~•륗_9õ ªÇƒS×ÏŽFæGrV°òìGm½cmµËu[TåÛ»®GÏ_D]õú²á=®[Úµ.ñÃÁ‘ØûûÏnÞãÚü•'Ï{ÿ¹Ãƒ;öuâ­¿~Ûu¸n|"5ŸW~ðdøa®—Þu罸>*8µkõ·‹>v¡mÏàȹ[Öµ¾·Ê{߆ÛÚ<Üñµ“î¿æô“\«5_øÄZ|†®]I“®™¯ýú°O¾²›h}qyÌäžßÞû‹ÏG.,O˜½ç?]'Ý~ꆼÁ|wÇ7Oµ\ë?ŸÅïö'üîGîuͬÞýð-«®µoðéˆ%·í‰ùž­Ÿï‡ùgò¾k“á·ý±3Ú_öVãª=S!½n_ófäWƒ#Ÿî>çõm×··¥~í¾+®¢žÿq—¿™êš9ÇuÉ/¾àN}+ãÑ忺+8rçgJ^»çäàö¬ç®ª?³zÝßß¼ü¿»~²¼É5sÁ/{#\_ N­ÿÝ1+y0þµ•e£Áí%Ù¯Ÿ~ãIÐëþ¾ë¶¤iL嚉¿õĘˆ†àÔCÏ_׺;8òÈgw×?º%¸½éÐÖÜTÆÓyº¿ïþaðkîØkëïšI}ið–S¾œz¤âé? w…úk÷©ÓÇüøv×zÝ?®cÎýÉÏŽ‹;>º¿‡{w^ô¹×LáÚeÛ÷\ŠÜžlúÒ¡qýÔ ŸzuÝÝ®“Íïí—tòÎ<6Ôßçéþ~áêâ¦Í®™ºèã?t‡úG·;æG%gÆ”ßyyï‰þüN[»\Ç=ûî×¢‡Sá§ûû[gÕqÚÅŸ>4¸£ô¼/ÿîò9èuÿŽqæ7›î=Ç5sÉô®‘7žb<¬}ò…ʃƒ#Xoè+Uö¸„^÷ïÈŽ3 ?ýò*×Ìïλ#ÔÞ[Þ»´ÑõfÈþß ;îŒMó®ŒþÛqªÊ·¯u}h¡tÿ:}æóe‘ ÷›™;ïÉðì µû–SÏy=,dÇÇÇž½åÛ®µ:ò“ÑÅ·_üøo/z'ÖõÛoÎ×ãd#žqå3®™Ž ãVÀ}ëæøÕW®ŽíÝàèÙ—ã™ ®sݱÿÙöõþ½MÀG“û3½"]mqÍL¬ì¸ó{I¡ö濨ü÷Åüäåç®~òNWp4æý£=›Ö¸Î2ý¹CºgÕNøèñq?ƒ#ñqüôñϽáK¿æþSõÀ!8šûõ«·—~#tÿÔí·Ç[ì϶èq0–rßA÷ßqŠkæ±£/:ùÓiØÿ…G/½æ¬˜7Ì8 <5Ñ•¹ya<íhúÍ kOïqh?·èñ1vÓóº p³}Æ®Ky‚]œzïÐÓ¢›CíÙYsõÝ™‡Ùõƒ;Ò?yØ3ÓE¶ßÃOožõ×ýakfðÕp)Á©›jž8ç×Ó ãlô³Ëïô5^ܱÉýñ¹‡VSO‡o±úØŸmÁOxzƼœºa ¯¼®58ú‰7Î{óG…¡q°Eƒ–÷毲 Ý/wî­¸õЄP¿~õâ·æ~þyñl×)vÿ^öóÞ>ë:Õß[ô8y ®ò°¹²Ç•_º2ùàÔ×NØþ|ü; ÷ÑÂsÞÛ’Y±`×…q²E“^hhPë® égî+ ãåÆO^Ü-~öˆŸ7>ÜþrMÞcgtP_++Ÿ¸mãË¡ûÇÅçœzEô!¡öÝÑ|JgXx¨}½ËgJ«C÷õõ¯­-ð_oË‹ýÙV=nüýÃGžõ´k¦íÜ›—üþö>÷»ÞŽÞöâÍïÝüñ…ûÀw[>tتÇÍC×_¶÷©µ<®Z’ó“ç‚Sã»>|rd[h¼ݼræÊ¼Ðs^†í;s!?¸@÷óîKë;툻Bñ˯Žx¾#â½…ûÝÔÓÁ=¹+'b~a?ŸŒÝæ³aUÜ:Bó¥ ôxØý`áv¿þåP¼òzÓg㮹Ì1.o|æ¤?¼ó_æ÷=ËßL¹ê¨t×™¶¾ÛŽ[¾íŠYû9_=>v¿£ú.ùΠë¥Å™—Tœz~ö”„W__Ðïžc>óØn×ÿ°÷%`UV[ÿ¼ŽDDATDdqxŠŠŠŠ#*8‚(„’HƒŠSQ]‹Ê i¶É©hÜ(4dÔ,­(³È¬,þ¿õîõ¾ïɧ{¿ïû?ßpŸ{ñyއ}öÚk¯½×^{­=­U­Û…Öóãåx8p­:÷W_µ¹zØôeÞàoÓÇ/w9•)ʼ{}TÐ!v>÷Óßüpðr<žŸö܊×±Îê–è&N´ø¶œ}p²(Ã,Ÿuó×b'¤–(àåx8¼áƒîXúã¶¹4Øia§þâDó‰_zn,7ÆmYŸßzvɯ0íƒþ½ã~{´Æ”¿ñr~äð‡S¦©Í„)³oO½C|ªçc<• ®úÞ;ÿyq³œßQNŽ‡Ã´šX)Ôæ¿LžùÅWæxÔÌ÷d“LþéÞ>¦<Ò4ôÈs¾/ÇÇa¬ †þR¯6ç'ú—|gò¯öÆ ¯úyú¤l¦s÷ÅÍïÍKp úÈ\/—ã¡£qþ÷ÕfÍŒÿ;ä|_8T”MÉÝZßüƒ¸³[ö¶{/ù\óæîœÔfJÙ†ú&§»,®e!4ÅÍ7Ñ ¥‹õ¼*ù[¹nÈó3Ö5Ç›6»õöˆç÷ü—<2ÀX¯”M~ïÍÀÝb‡ÖÍýP^òýÈCÝ~rY¾ÚôM¯±Ð¢Æë‰ž¯<Ö*Ê–=öòñn;No~êrü‹€—ü=òáÀ$Ÿ”ŽjÓi2p½DMHöÞùS DYÞt¬°ÞÔõ!à%üòBcëø‰¿šÞÝZü]Í£Ÿk¢£öŽxí.“_rdÎg°:>܉ùÞ*ù”¶^*W›J'víþ·_EÍxÿ°.öežð˜Ñü˜Ø9béMšæ^òùhBVÞÇÔ&¹aöÏ„†Ö,fÿÈöêó¦¾7Ç·UòùhÎìK‘ýϪM0~×ßjò-öo?½ûL²É·øäë>OùÊœ?å¼cêA«Gwk LmZwzõ‹ˆš©AN7VecùZH1ì }cè«äÿÑã×?ýr÷^jí¤¦ˆšoÇæ~h¶‹´cþV±óÞß›V¹ì@9É÷c–÷ù·=¤6Mýú÷o¼(jæ4½=Üí!Qyhï ‹\Mûlé]®w6þ†r’ÿǤݡ6E¬4ïƒS¢&y^ÒÚ½%¢,¬¦ËãrtýxÉÿc»VÔ·]¯6Q±c_ˆš¥gŽîí»òÝüòÃ-žjز™'¾{öW±ƒ¶~*2õËÉïc¯O{ë¥nj“cí½î{D͚͵6<#ʸþRÿ^òûØéùe»b¾P\°hî!¢&/üåž7åFËšË ¾ìðý}Ým7×›ûQ$Ÿ+Îúþoj#fßnk5[[z–æ,ŽžkW¯äãqÏO'-¼ïsµ±nì©“gâEÍ®—Üû*ê»fà –ðábGm‡M?^òí8YRÕÆJÍ`5÷¿‘k™,ÊHºRêÄÎD××Þ˘xɯã4Û®9«6î?x– ¢¦dY·Wû 7ù{8ð„>NÌyb‚äÛñ…ÚÄ¥6¢Ó¯OÏ”¿gîILíkηúúo®wq¯§î\§6Þz{ÈCïm2Çû‹¿¾Øe¢( Òª>/šz|¢äßñøì¬Ï} 6f~Q·ê£‰¢¦¬ô—{^2÷‰Êä>•ÎwqóWUi ·^Ðíà‘|=>ï§ûÝ¢6Ní2ëÂDÍ›+¬M}$õà™´,xøaµÑ÷ËüÚß.‰šã3¶ÿ­÷£Ÿn¦mÖKžù—øyΧ‡ÕFGñвÇ^5uaq?"Êüµ s=鸰âä“ÍþÈü\¢-´Õ†o»}•à)j43p…É—·¾øÕ­F;wÒ×ôWÌù`"óyñ°Gbé¬6Ôi+¢æ÷ms6o¬e4ýxIì¼D ’À3_ãɲv]CùÏÂWÖŠZZ-Ü4ÅØŸ,s;ÿ’³÷ s>Ú?ò­9!ÇÍq?‘ùM«ËNc¾nx๘ê]¾¢väÔ3«—VŠ2Ÿ®^;ì;ã‹S»ü¸Û¤ó»ÿO—ßݺGm¸±ûèþ¾EíÜAM¸N”I93ç‡IÌW ™’»Mmж•oµkŸÌ¶\t_¡ËëÍ ;i×8ô°Iï$æ3-Óox\m;ÃñË]ölí6mÓl¿«¶À;{î¿6>ÙÔß“˜ÿ– ŸÍˆ«UU?îùD²¨½÷ÖÕ%/Þ&Ê8½³Êç;›Ïü¦æM8¨Öÿ‘3¯ü3C.jŸúbç˯v5ç–«R?™údóÛYÛ5øWÿi§µyÓψڲÊC·'šrtnÅž.˜ëÔIÌï÷w}úµþð‹ü~¨µ•M/W>ø¾(svÊüõæþœÄü¥ÚV4ü­òžªG­OˆÚÚe•“^í%~ÝáâÏ·XÍ}Rm:ëbò9–ù¼æì¦/GŸWëµeõ0QûÅî_Nœe®§¿âùÓXÀ3ŸosìpÚá„ZŸGYÁ¢ö×þ‡Öû`ñQÐÏ‹g‰9+¾þòñc€g¾jf·¯Z¯msÌ5øZçâí>â×Í}´÷>øÇù®êx^oíö–¹eþ¾óÍ{Wú¾¬Öc5ÿî}wüªóïøŽës¿äm‚1Îi¶7$W½3ÂÉ´Ûc™ÿÊ®÷WëéxæT½¨S#Î.J±£k|â¼'¾Ûw‡ßðþ,Ìû±ÌtÁƒjýà¤iEÝ‚êÊî~Ð\ïjËåCN·§V–ýØo½©÷c™ï5•KûXg«õ]/Ž­:(ê2é`à{³þ–¶°-ÞöÌöë¹ÿ“/£<IŸz²mß¼•ʧ¢nSî;>ùiâà§Ú­Ø.¿­ç'3¿õ¥õäImCMÔíÚÜaÅðûÔý?f¶šúp2óû@Jâøç£Ô“åsÎïô,u̼´eÏ8qð˜Vê.Æ|ºÆEÔOçLyœÌü,þë §<Ô“Dõ’‹¢îÉQÛfÆ¿ņÙ~×çS–O³Ÿ&3ß"ÃîCõäõŸ=ø¬yîR÷Âm£|»\ïó{KEcÿqGë‡7ß^†òÌg)êÉÔ}oZwÝiŽ›²ÚÂmmŸˆƒwÇø¿þc~2ÆÉdæ÷ë…¾ÏÝQ¦žŒk}fÐ犺·]!N¼v󸇱c¯¸eõ×ÖóS˜r=oÌ3'!$!Žˆº/g)æø[r4ÞXs_-ŠÊ*SOaþ~7Á+¥ÂØGê¾™å[[0XÔý~¢Ã}Gšãpô¦¼£m$ï6ûi óûçcmRëx½Ò­ç2ÑûgqpÍöé·XlÿàTv§ŠdÀK¾Vñ>B¶<î)N†÷º5í—ã\l¿ãû™™ý</ùXÕå“‹¾zD­{iE—ÑuŸ‰“Ó‡“%iÊ}îÚ}×<%¶ó¹…1L‘|¬"óíòÇj]Ñ}o?+N¦í²Üõè“v‰Ø.åð’ŸU4-zß¡Ö]ëÑ|û âd=gÓ÷ïÅvßYO^ò³ªã"â–~ꦿûGV„£8¹=»ìË­Ñâ`„öÏïÛ|;øÔ׿>ÏTÆ3¥eñòBµn˜WNùGâä]ÎKöu4ù!í±í­”c~ÒhŠþV­“v¯8ùˆµ KqPÚ#bÛö¢‰ ‡Ÿ<óëëçÊŸú0E­=Û—4ª1?Ÿ|¡üšÙߊƒrž5ä|Û¯tÀcEy–Ë/_žw,5ÆO­<÷0äëä[ÞÝ>|I Ò„ôyåY.Û.ŒÛùÐ.µö5: }EœüèVõ¥‰ù&?û¾œûÕÁ ±} )ØQŽåñÒ-¿¾æý„Zû¼fÉ‹“ç,sË^¥ÛÞÿñíQ§wžåò|×éÞ¯ÖÒ¶ïïߊ“4=,µ‰ƒN[~ØþÔX±-û~Ú<Ë%Stóý©³4âÍö Ô ƒoõsŽ]wÇk&'T Y»ÔØïÛú¾f˜˜ã6ŽÇïç×vx´ßY¢~é…k>ùàsq°Gá“›?úJl%-2óYóŸÏ•kä~£¨ÏYtkÖ‰5â`÷3§O~h×ÌwmztSkÞs]Ùz·¨¿id§#·}lênÿè;¯þ™Ø¦™¡¦½Çã!V[@ª5/Þ÷t‰Ë5¢þ–ìA_½·(½8çÐ=Ÿ1Îö~b©üþÌ(”ãq1`êï^6µæn"쌨¿;üØŒgãEióÒ7:ÿ‚vM“|?öÓXÚi2æš›öì»{öPQÿðëE·ù¦篺^ßÖí—ÐÎÏ8¢¼äû±ZZU¨5ë÷{yHÔ?×ñÖCûDéëNs+6ôܶÈó¸eÁž&ùzìÕ…t©Ö$}Úe`­¨=̥߶ŸEé¾êÑ“Ü^Û´cjð’_Ç45µ@­™ivpÜ~Qÿ~Ũ{\cÍóg™/¶^QéÆ ÊI¾“çjÍxí€DÔúèC­¹¢t÷ ¯dv×ÖW݈”“ü;–CrŒñ\C«áM_Šú+žîß¾ádžËßs‘N°ygk]è <’ÇÈÚIšmŒß¹1ì‚WÚÐxV”>¾x×õ¶§ ~|õHöù_à‘ü<æ?øà/Ym&=Ýîšwý/Å¢!¨þ£ÔY±¢”Ž=} : Hm|ºÍz~ºäóÑŸætª›S¯ž€µ­þrX4ÄúÍüõl_QJÛ¡_% žÎóúfWoÀK¾=ºm–lÝ'ø~@CÒ¦kïO/Ÿüsï³Ýeðw«ü6íªé’ÏGï¤ Ô\õÄëƒgx~:I4¬ŽM´Y”ù÷"¶j_Ð Ó%Ÿ¦î©¼½Óóê ¹OhÈyÃÆñÃ~«›ÉïmtÑ'Û8÷ØJ»FQ—Mù™.ùT»Þ³Üè·|nÙ°ý€­ª“yNXª™MËù¨à¦/·SGއ£|OãÄ £·)¤‰†"ÿ}³#Ðï´êLÞËéÆà%ßvÐ.©'ÖA7A4<¼§Ãç¾¥›çöÍ»õ]Q0“üî¼äïmûÔQ=¡m‡™ãã…2¿»Ü›í^æýÉÍÅ#M~ÉëŒê‘G[3"Ž9¨'äý'ÑðÖîéO©¢”¦‰§oâe{ ð’¿Gn{ð£ZÑM=ÁëÅXž5î]¼‘}rcäh†äã‘-ÚÂO=1øQ¥|Õ9“/çÆ7&?ÑÍ”íÚÚJc\üM…ä}hî_Ì|>’ûÇ ƒü[ÔΰN4üpéܤîîæ}©yåçÊo*ÉlûèµZžƒ‹ÆoÊ÷zÂI”™xíî{Eßc¿ý àå88,÷ÕjZÞ~Q'ï¼-ÞóÍ¢T÷~¢ ã7;>{x¦‡ù]õní"‚h|:¢³s·Ñ¢Ôo#VFuêX–ë-iXYÄ8˜ë‹™r¾ASÀjõ=·¾ìÕõ”häubi ‚‹ÕHî·Í¤…:¼cÚO3%¿ÓqÈ•_Õê;6\ïwæ]Ñx²Åcê³gÍñ7âÒ4—â[{¢›4µçò’χhÛ®ø¢Z½IÄmyïnc¾k¼pmÓöì—Íñ7¾’vÑÏ7½` ÷ýòzà‘|?ô¼vð®Vgx~0ý1'Ѥœö½k…Ý=N¬Úâ‹SÅM‡>éýOQNòùÐríâŽ9Îh·3¦¯hr9pp͉WÍù,~ûª9YÄ&yîŠò’¿‡ü[Ö/÷Z=¶û‹&¯!]_ݱʜ¿dÿˆÍÒ¾B9Éç ºvÍKjuøYX ¿‰¦ ~«÷¹£¿¨¢lã^†>> {x–ä{…œ§Õjm¹,šFû¼qãš'£+Î_øÅœ·¤|˜÷šfI¾WÜG$ÒÕêA®g߬I1ä¾)®¥÷¹ãܼ´÷-_Td{ãh³Ô³æ<>KŽƒŠ¤¼^z¼M­Æêòɽ‡DSÊ7Ý+H;Ll’ü¼ä{¬·—v™÷9«»/‡%sƒqNÖ´ÞÿË®÷þhÜï8ðÅ}7í{&N¿/fγ$ÿE3-°†©ÕÄÞOމ¦Ûè€é&ã^ÊLŠs& ±é;·¶}óPNò_dj†›j£]”•kDÓž›öÖˆt­£ú±³òÒ»ú^òûm¹¿®Ú¾Ð6NDÓë?D\òž'J;ÙÿHñ)±¹¸¥yÔ;çY’Ïog,~u͘rÕFNj¦ê¹Y·:iŽKî__›·ŸóÙ÷ùs>š-ùý6ݾ¬ÚÞÛ4áºñ÷rÒôÙ?ŸÿòQêÑ㻂n7ú–ùdÚA³%ßß’ûïÆx·½Ý“nP‹¦ËÛÎ͸V”jæY¢¡¯6iÛÐQ&¿gK~¿5OÛ1øgÓ.ô~.š-Oß¿ïÔ‡ºÜ˜úa¶äû›¿}ÿÊäÇÞSmòþ·hîwvÝ|‡ ¿ìÕ 9Å:Ý€—|}óæÎèªí‰Îó|Ý‹foqyï÷Ï™ý‡UCMåRc]»ùŽ´‚ÜÄÏíÚ-ùü†¶<ËQm÷©<Ý*šCo?}ôû{Íy|ÈW#ïI="6ÓìÑaÊI~¿¡ çªí®gÙÞ¸V4¹’T=7ÛœüµƒP£ß·j `S¿Ï–ãàõF ½ëŠUµÑ)‡c…hžööß·k‡Oxï¶Êx±¥FÛH³žO|¶¡ç¼hòKÞÍr}aÒÕ8* ]“ù§Gòý5ím‚jÓ®Í%ˆæLM¡í8 m_¥Šy_Í”¯Éï×ä=Õ&ïA‰f¢Òá CN ´eoà%ŸËéÒwÕ6­hí»Á¯ˆf>×8@·ü²cÅ–g7÷ \ð-à%ŸËù>€MÞ3æ£æ}[ Zfšý£}ÉHUl¦Ñ™{Ò”“ÉçòøßÜb¶w,]ëªßÛî~õw˾+æ~óÉßWRþø#)FµÅí>2µ²§1?´ô>иå³=“ ±9ù"­ÌQ^ò÷eyßNµMÞØ¸`]¼hDWžIß·gûå$Ÿ_–ëpã>‹Ô§ëG¢ÅÏÖáÓXJzDÁ\mãÏWs$ßËöö§Õ–°Þ’â¾@´Œ<ý±§ÅßìG¶×ŒqÍãÓ°CæH¾óy™j£íÏ]OŠíÙ쟆ú‡»¥‹-Ú²ãsŽäûA,Z7­Yeö¿G4LçßnŽã¹’ÿ/ð>¿¶–ý(ZN¼òÝžÈ6³~æCA“ö Çz~žä÷ór¿óÈÐMnó-MŽï‡?t(uËjúð›^b+]'üäÀK>?730KVc~kùde·Éµæ=9Oúúfëø­“~{לæIþ?«=\1ǿԢåËœuÛ¼SLºÙžÔ÷{·.×Ìywžäû^õ®Ï1u«¶ ý^i¸V´|¼Çž5y¢ZO}úc±UÛ~ð¼äï3é3ûEÄ™ã$ÖF#AœêP½æþßï4Û£ sx›Ô“À#ùþtéšËyTbþ–öý)×’’Kõût}fÜcâ}DsÞœ'ùþô€ý©ûÞœ`èµS»ÿpð©0õbëç–®#7›û‚ó$¿Ÿ*~–$è–ó®8%í|ƒnm7 ;Yl•÷ý­çåÓ<õ)>¯³e­û~Ù²÷Å)ºÅ|M…¹ÎáûQú~dÁþ‹7–þ~Ôìïùr<ùxÄCNoŸ1çÝhbØ#N­K oçjÈ™±.•û8fûçËqð„C¯mƒQse àå¸(!óúØ[渖ûmâIѤßE)vEÖ›çStM"â€9ÿ-ãäÑŽëZ»W~éâÔ{ŸœŸØ¡I”ÆTïò¹`Ûµc¿Å€—ãázwÝ3fÿÿðuÁñcßVœjx¯«m_“ÙoWmt,|è7Ón^ ÇÇÃý#_xé'µZ»î4Oœúbv¨ËØIã¿«I÷8¿J”Òm¸ìÎê}_î7™ü\ ÇǃtxXÖyÚýqêGëu+ë3Þ1îåéç Èq±gò+•yÝT«5q §;у†±¢Ô·#d»y.±@òýþåÚÁ²Z-çQqÚ%WŒ¾¿!¶Ñ+«„6ÀK~ß×ý‡úÂÞ¥ju ԛⴧ¶Ô×åb;Ý6œ} ð’ß÷zu¥“cµš®mÞ~ÞП§ý×DÌ?4Ô”ëm¡`ðïE˜ò¸Pò];ŽX]¬Vk×n§Išï4Ç©”G±½êQëã·`Ü-”üß=,õšÆÌLµZ;Ö›*N~æÐ ·FˆÒèy×U/7ÏÕiW«¯ÝûÕ…’Ïrû'V­¾ÞïÌ;5Ƹ9­î*»ÕÑßœ´mÖ‰ú} ó]ÏBÉç{èÙR¿‡Tm”νGœ–ç Æ½r}µSÛ&܈r’¯÷ðý³j¾t:îÆ=µs~5ìö´Úv!ŸH?;Èí>ýÿ9üŠý»ûôoü?ãÛŸþep_´²/‹43‘•}¦#Ýôv„ÌwĸîÞul嘨Ç 9&*d®3ÊwFßwFù.Hwþ.û9&*d®k>è‹…ý¦ÙùLÏ‘~;5Ÿé(Û­€ý¥[Ø_z±Œe¤ÅCM°ó牲=Q¶'èìÙ`ç׳ˆ}cøqœ£Böñé…OÇC|oÀ÷-½ Ùß“EúüÔü=¾…ý¥£/úìeO^ì/½@ÆGÒü¥¦/Ò}ÑÞ¾¨·ŸûK/fO~ì/½˜c¡:s,Ôb µ?ùé°š¾Ò=öÀ<ãQÄqPÁ¦è—È: Î¨s ú|`‘ô•>e¥ÉØJƒ*ätêƱP÷²¯tÀ N¾?£ÞÁ—íü¥ïçX¨€‚ü!À1é¡VŽÇ´—ý¤;³Ÿt´×»JúI–À>Ò«Øçò}€Ã8|Ðg¾ Ãx}‹Ø7:êžÄ1OÑ?~îÒ¿‡ß~ŽqƱMQÞåП(ø”Dù@¤KdÜÒ wŽU ƒF~0òƒQ_pÇ"!èïÇ㘣URehqF1Âпa )<ŒãŠ¢LxƒôÙN1¦F Î !ÂOú #¿íäŸ]‹E…t$ÚyYÆn"¿±ä{•|QE¡Ÿ¢µWÆ¥8¢äkEóGš¢Ý9žè^éVóEUÀñD‘ŽñcŸí w pÑ›ç19K´Mú›ÕüPí—±«Úu¹C»./ph×åÿ꺜d8‡ÛÚÆ~1®Œø'qìO›Ò §#dº#ÆmÇŽo딆OÇ?‰¾*¾9Ò]rd|s-ö ŸBŽoî̾‹Ùÿ6êvÌ·óÝͱÍÏpLÂ$öÛhãØ'Iv~½ì⚟±óÛÜN­ìç*cŸ4°¿î$»Ðð½ß´ôF~ï Žmx仾³ôáM± µØæ—í|7ÊØ'šïF+Ç6G{û¶rlóŽ}ˆþr cß%Ñãž”pܤûÇIßßšÿF¤=0xsLDðkúe@>Ç4wà˜æèóÅì eepÜ“*»¸æ÷õÌ`Ð>0ƒmrªÕ|8¯W9Ç=ü䎡Hã˜'À1´•cž€6o´×ÛÆþ“¤éa69Uk±N€Ã8|.ssàõ-fŸ¨sxÇ8iãØåi2f9MíZ¬rô¹Ç'G |àP>å‘Ü+ã/¡|Ê헱ƵãÈ.–±ÅCÐŽ$Ž%Žþ Eß„‚ÆPÐjã¸áH‡Éxáa )4„gp|ð3ìS2‡ã‚·r<ð4ö) #‘ŽD:²œã‚‡Ißïß…b¯D¹s|p¤£ÎH¿k·Eó+ÙÀñÁ‘ŽÞ/Õ“{ô.—ª*&Œc¯€Þ1À5|¾=c.K?ñ{e,àÇÒ ðH]N‚Cº˜ô,1Ý^¿ê:•t)éP]w’¾Ôu#éA]_‘n"½¤ëÒ!¤7HWèz‚tÂÕñ‚iÞ¿z.§yœæjš—õ9˜æ[šSi.¥9TŸ7i~D¿is¡>ÒüGsÙ苎îÒlWðÑ‚þ±”°X´¯›UÆ:ížÆþ_Ñ=ñ»Êô*–þðzÇq¼"ô§{†ô­ªùFõ’~P§±ÿ» éë”üšz§7à†yI¦ä·”âõø¡¼àüQ—?øˆòA­2¶}êÍáHØ/ãDfȸòQøŽÂïÑÀ].}ô“ϧ²[Úí³vû¬À¡Ý>ûW·ÏÜ™ÏU2­ùQǸ1b$°OmJ·rüç4|örügÈP'ŒÃNàe§6ö šcŸé.ùvñé 3]‹964ê¶¿e/û…¼:‚Ç*9¥t‹ãØt­›õtßË~µ1·ô=0Æz Ý3ŒcHƒÎž€wN€wÂrî^ÑìWé^­ì[;cÓ!Ýø{ƒ–Þ¨¯wǦ¼ ò]@kwö­](cÓÑTæ ®iv¾µ-2ö Å£î‹öömã¸ÔùÒ·6Myn Ã xÝör\:ÐéŽþrGÚõôGºû×N¤=0Ox”pL:´{út@Ǥ³pL:à˜è‹Aàá ¯eãÒ¡Œg¾mà :f0Êx—Uú[õÞ!^2&Ý䎡¤ƒ@×P䎡Àá oÐæ>ómÃPï°4éupú§Ò>Àác“Ó¶/êð^_üæ ÃÑÃ3¤?íáO~è/?äû‘=ºýïþô·Éi>ùÈ|Ê¢| Úˆt ø‚€/4!?ùÁ€F}ÁÀ‚v„ ?ð! 9u„¢ ¡¨#4‡!? é0´! 4„¦pÐáàQxûûF#Êe\ìˆh«â⑊‰D:éH”‰>-ãñh1ò…~‹B~ÒQm2N#øG¦h¤£‘&ÿš'Gó÷ úF_ ðÅX¥ßïÐK¾êÆ€/äûmÌ~©Â´X>ù2ŽøX’qé¨HêrÒáöú[×ÕºŽÖõ³®—ÿOúW×»öö›®Guý©ëN-øƒÔ—º$݆1aè0]Oé:G×/¤3HG^ }`¯HÐüNs9Íã4oÓüEs/Í­4Ò|÷#Ó_È>+8Nú³ã~ޝ‰qÑé2ÇÔŽ.2€0ü9¢|·éã¿ÆGð¨'~w¿œÓ ß½ÚØWÿ~ŽUo“þù]Á£¾¨Ó ¿»£ßúWÿö‹ï̱åÑ~/¤‡àooŒaüîk‘±‡#ox9Çf¨/ˆì;|‡x1 Ðבȋ!;½\ÆCl·ßÚí·‡vûí_Ý~óc>Ù¤¨kqlòMíÒð)º*¾0Æ]Çr»¸(92¾0M ã8ŽãØ!Ý㤋ãÙA&º–p|a/|€ß²_N#ŽÀ刺Ûͱ…·[ÇEA=ÝÛýÒé £ÆPàêiå¸Â ³'àh½ x§ýr*ê…ü^H÷¢ôeéOÞ9GÆFÑb {qlaÐÖ|qAyä»Þøû ¿O‚Åô¹‡+hp ® ©¯;ÇFAº/ú¯ïeŽ-Œþè‡zÜPÆ t¸åp\aZ»cŽrG»ÓüŽzúÓv÷G=ýÓi4Ž)ÜÊ1…ѧ ¥Ï{-¦pÇÌ@”œƒò9>JÇGAOÐá Ú=/s\aÀ Ì`”ño¼ãUÈq…Ãdl”!È‚ôP´(èŠü¡À18¼I߀6oô™7úpÊ C Czpú ^¤}€Ã8|I/¡ß|މíC8ýÐ?~è/?Ðé‡1àòþ€÷Gú£|ò@z¬\Nù(ø@Jƒ† Єü  © ‚‘Œt0hŽÐ‚þ|ú3u„¢ ¡¨#4‡¡aH‡¡ a !u†ƒ†pàÍá—e\ã¨sêt„UÆM¤x-‘€D:ù‘(?mi•± ´Ø-€ Q€B:게£ÅoþQ )éh¤£«d\Còó=ô¾à‹Iàx- w p_Æ€/ä—â3’?X-VK•TgÚœNÿHÀíõ÷ÕúújŽô3éf{½ŒþÑt2écÒźÖu°½]G:÷j]ûl<Ò§¤?Ig’®$ýHºQ׉èMþ•þ#§ë7Òk¤ËÐçÒY¤«H/‘NÒõn’¾Ñõ‹®WþJè6#éÒ¤+Ðï©'H?ü•n ½`¯H\­ t=ÿiþÿ{±Zi¾×çx}~“mÔâr Î^×*‡ãÃ;ËxV-˪ãÁ7ØÅoãðh¯#øãØÀñGiß»£¯{à»GÇvǼå„:œð»Êôî^åÒtÆoÎ2Uo´Ëp.Èëƒ~r-‘±=´XR€qË—±£´¸ë ¿?ÚÑmö@ Ž…ºâ·AÑ?xãw¯"Žñ„߇àÛe¼÷rlóÏÜ·œ‡.êóCÛýÑnЀ:As ڈߑBýÁø-¿_~ ®PЊ2¡ ! }†t8øðr)ZL§B) Z¬TÐ ˜Hü6tŒŒ“ñ§(öyèŒ*â¢Å;ŽQèWŠÇm“qL)nSLÇjB½cªd-~9+’qúm÷·•ǃƒÃ?¯=ßnË·Ûòÿ¶ü¿‹Ç} Z´8šVޝˆtÈVŒ¯Å2î´ß0Ú.þ´»ŒË¤Å7D?v†œwNàMg8¾!Æw¤»œáø†ivñ ý8¾!úÙu;¢¬#êrl7àêœÝ×2ܽ¤»£Í=îr=@wË2vuO”ë šz"íï¼½€§hì{!íŒzœÑFgä;Wq\kÐÑøzc,õ~”w¼ à]Ї}ßíèƒ:ú€§®Àé ®(ãŠ2®(Ó× Ÿ4Žoh“S[?ÔÛ}Ùõ¸¡ŒÒn¨×­JNyîÀáŽ|w´ËõôG»úGàè´èð@È÷h“ñ $pܪ3ß0‰ã¢OfpBzÐ~ŽqO”ñD=ž69R íÁ€ ¼ƒãb¼ã¼^HŽ!ÈCŠö]CÁÇ¡Ur öoŒ oÀx£‡!= ´C kåø†èCäû€V_Ðá›Àq ѯÃÁ‡áqÓ8ýÐ?~Hû¡?Œ=Àû£mþÀçòÈ]€| ÒÀø@Jƒ† Єv!Œü`ä>4ƒ¦ôoh A?…‚¾PÐ ü¡H‡!/ é0¤ÃP8ú$åÃ/ôްÈ‹#Pßà@:"NÆ0§X‹‘€D:ù‘(?ôLñ)î"©›(Ðø(”e‘qµØ‹À? ôS\§h¤£Q~´ŸŒ³H±sF#ü§%ôÅ€Þ1 w Òc€ŒãšÅqœEÀ#Ls>ýÓ÷_ÿž oo¿“>þ+]ìå`ê^û}4{ûÝ^×RƒI·ÚÛé¤/IW’ž$IºPפÿH¿ézM×iöºÌþ<žôÓÕúH×CººZ÷ØÇtÖõ‹®[t¢ë“«õˆ½î ½a¯+À?MGèçúövº>×Ӡô ‡¬íÃë‡2~èÐæ¸à @^òÝ¥yA¨;é`Ô‚6‡W(p†¢LÒ#€3ÂYÆŒ@#7xG‚Ö(”Âe¼VæÑôß(Ö˜+ß1íc‰‡¤‹éßÅžnßÿç²§ÿ7léÿI;ú¿Ó†þ¿´Ÿÿ¯mç$î›35c‹wÈAŒ•è÷6)æÁߎ˜:yáØNБî \Ûe»øq\p¤»€Ž®~2Öi× Žw ´ ¬eQ¯#`ÁCG”í\ÝÛ­JNÝ­ø`ìtGÙîÀÕé§=@wO 뉲=QOO›œbœ N Ó é^˜KzÎ^¨«ÙʨËùÎÈw¶ÙÅG~oàë>qA¾K¡Œ NÓSä÷LõA}ÐO®Àé WÐàŠ2® ©/Ú×4÷Ý˱ÁQO?ôG?ÐÝi7”qnÀëFút»‡;òÝ+ät×ýÝõôßÿçØà Ãt ðã¸à¨cêˆô@àX.§G-.8Òƒ(|Oä{‚OðÓ4 Fƒã®Š ޾öN/àôBC€cò‡ÇËíŠòÞ(ï š¼Ñno”fá˜àH#Ý|>ÑŽî |Aƒ/~óE»‡»sp´{8púáo?à÷¬?`ýÑ~”õG:°Èl`Ak p¢=À„úƒPÒAH#/õ£|0h­!H‡àïPÐ :C‘ŠtòÂ#] ž†w8ʇ£®pô÷Ô=ý2¸GwÒ 2¾9Å$|$Ò‘ =´ŽL“qvG6ȘäQ + é(”…²£;ªHÆ$F~4ÒÑÈnåøä e4x0iбH1ãcÊ¥úƒü1è—1€§8n+m,h¥˜`ŸkéSšß韽þ{ö±~GájÛXßÛþ+ûøïÙÆ¤'õ=m{¨ÛǤçtgo'“^³ßÛ¾ÚnÖõé*û½mÒIöwXI×è6ñ?²‡u}ðl`}?›æz}[ŸÓõ¹üj›˜æn}Îþ¯ÚÃ?ʶ+è¼T ?1n:bœt_;¡¾ÎèÿΠ¿3òºà»+xÐýдY0¶-€qÄØpÄ8îØnÈë†ßºOú` õÄøè 'Œ9'ŒÏ^àµ3Ƥs‘.øÝ÷A_÷½®(Óõô®~€ëû7”sœp¸£LŒãþ€õÀ·h€:€Ž(;È"ãQ‚¬y¢m^rx Iâ°àèg+†p ÞáÛÃQ‡Ú臶úƒ–Àlƒþ Ô<Á^r(† m! %ý <¡ÀV€á‰>GûGnpGà;å"ЮHàŽî‘HG_T‰Œ‹ üÑôt4ò)Víh|Ç ýŸs Ú2å)VàØ½ÿöaû~kû݉d7Ò©_ð¹‚ϯøü†ÏïøüÁw)🂵¡£QNR “è$¥+ïÑbm¨`m¨ÀÀS §è)zJéÅv( 3““¥`}¨@o)Ð[Š¿i†þR`$)Ða t˜¦`}¨ æwR˜lL4 ô™}¦`}¨@§)Ãùž.Ö‡ ôš‚õ¡‚õ¡‚õ¡‚‰E åûnÐs ôœ‚õ¡‚õ¡‚õ¡§Œâ{"ò¯@þÈ¿ùW ÿŠÊûÏò¯@þÈ¿ùW ÿÊTÞÿ€ü+ò¯@þÈ¿ùWf³ ùW ÿ ä_ü+ò¯$²¾€ü+ò¯@þÈ¿ùW ÿÚ{oÈ¿ùW ÿ ä_ü+ò¯½!ƒü+ò¯@þÈ¿ùW ÿÚfÈ¿ùW ÿ ä_ü+ò¯Ý‘ü+ò¯@þÈ¿ùW ÿÚ™+ä_ü+ò¯@þÈ¿ù×öæ!ÿ ä_ü+ò¯@þÈ¿¶7ùW ÿ ä_ü+ò¯@þµõ ä_ü+ò¯@þÈ¿ù×t)ä_ü+ò¯@þÈ¿ù×ÞÂCþÈ¿ùW ÿ ä_ü+z_§@þÈ¿ùW ÿ ä_ü+ºß­@þÈ¿ùW ÿ ä_ü+º+©@þÈ¿ùW ÿ ä_ü+º_¤@þÈ¿ùW ÿ ä_ü+:¯V ÿ ä_ü+ò¯@þÈ?k(ò¯@þÈ¿ùW ÿ äŸöÕÈ¿ùW ÿ ä_ü+òOk;ò¯@þÈ¿ùW ÿ ä_ü“¢@þÈ¿ùW ÿ ä_ü+ò# èw$3Ú} Xþ“¾rx ÙúkÈV^C¦ÙÝ¿Ìwà“Øß€Íî|¼«Ýƒ÷cŸ¼–ÔÎaüø.Uô; Åxñ{Åy‡@»Seáueß«*絥߭*às™3ì‡ÀÊ÷ã÷òÙŒ;ßÏa_ ü~1}ìçóg~“•ÏkMÛƒaÒ&Ôýhû¦^WÝ—oå÷Œq¼ZÂo¹øÞU¿kÜowwÞ*ï;ø”Øù*pç;Xò„æ¯Àß9&ñþk¹ß‚hö]Ï{²¼NµðZÕÊç;ù|Ï~/¿…<Ãï!ùM¤•}äóZv¯<ÿÑü8ðºÖÊ~ Ø÷A¹\ãj÷º,|7?Žï/²?„*ùVM»ŸáÇw42x ¼W¾©Ôö†Ýy-œ&÷ˆµ÷•åÒ_‚vŽäÌûÆI¼F.‘ö´v·ÃÂ÷;âøl©ˆÏ—lüþÒ™ý)Äñ;ÌV¾ûáÌ6x4Ÿ9åÈ3&Z;kï0Ù¯‚UúV ó%í~¿…ïø[Ù§BŸ-á=j/Þ§ÎçLÚ›K/¾×ŸÆ~JØn/’÷úißZ{{éÎ÷û“¤¹>éqH£4Ú=<‡~6¨ C}*êRQ—ZHÞú`Ú#ß |Và›€òP~ÊOéƒ:'n"ú~"ðLžIÀ‹ïXú¾Xà‹\,h‹E»'~2à)VÔdà¼S€w ðRü©¨{*`¦fj«\>Pܘ8ô×4à†¿§~dfpOC[¦ÿtà§x ÓAïô6¹Ô˜ÜäË>ðñÀøxôùo'ßë3[å2„ü]“êYÈŸ\³€kö¹4!߸äß–|Ö’Zò!;xç'ù³$_”ä_’|DÎEÝó@÷¼V¹Œ™¸ù€›ºçƒÎø&e о€[¸…hùçJÄw"ÆV"*Ml"p&&mIÂ'c(t$£ß“‘—Œ¼dô_ Ò)àe êOÜ"À-Ü"ü¾xv±M.—–ÿа4¤‚†Ô¹|J¯ÒÐÑihú)}ÐKä²jò—!ò—£ìr”]n“K­È[šW ®•È[‰¼•6¹üZ…úV¡‚UèÓtä¥#/83ÐÖ äe€– ü¾íÈDd"‰º31î2Ñÿk—…ü,Ð’…¾ÊF۲џÙÀ™ œ9¨38sänhYë äR.uä68s38®Oòð½¸6¢Mc#ðoD}ùîvºþŸõþTû™Oûšc¾}Mïо¦o_Ó·¯éÿ»Öô´ž‡Üÿ;¬å’{Òá9íþ†œÿ“þ†òÿŽïÀr>“÷Šô7QÚ}Êh>O)ása ¿‹*¼ê]TšéCP»‹Æ÷* ¥ï!í^’¿o/´»_éÌþÓäòC¤ùôã{–…ò¼X{;à.ï0iï¥ö󙱿—ÊgDgìÞ»ó}Ë6¾oiå7ïûùÝ”…ߤ™¾‰´3d¿«ÞOµñû÷>KÞËoà-|3CúhÑΔÏÈ7½šßÁ|~OUÅþмø>f¡¼©ù,²ð»ø4>c®°ó]deÿE|æ\ÅgMÎì—0Žï\ð»«ýòí¼víÀçÑa|+ƒïqó{zû<²°Ã8ö}TÈþ*ØŸ¡3¿ÓJï!´÷ZEìÉÆïîÙ7R¿¿/âûŸ6>×öâó¯ >ßÞ+ßeÏ$í¬ÛÏ»ÓØâ^é Q»#êÌ÷Døü»˜ÏÀø½¾;ûTJïö'£üDà™Œz&ÓÚuNF™©hãTà™ Ø©èW+ê Ø™š¯#”™ž!ßuÐ;zózã{:pNÏ·;÷ã³ðy÷L{£ï'ßckwÏ ù¾hŸ—󸟃[å¹Ý‡ô8¤ÇQ4ŒÇ¡_Æ£Ý*êWA¯ŠºTÔ¥QÔÐ|ëiÚO@ù (?å'€Æ‰ôA7}?x&Ï$à‰Åw,}_,ðÅ.´Å¢'~r«\LÎÙ } ðNE{fÏTÔ=0SéoàŒíqhsúðNÃßçsdfpO£þþéÀ?¸¦ƒÞé€r0žúð.ðñ诹Ô߀˜YÈŸ…þœ…¼Ù opͮ٠1ù ÈO@~èK@'Pyà œsñÛ\ü6—~C½©ø{螺W£ kñM>ñÉŸý|ÐI~Ëfòn!p_‹rKÑ–DДˆñ’H68“ˆ¶$ᓌñ‘ :’ÑïÉÈK¶ñréð2íIÜ"À-Ü"ü¾xv1ÚµmYüK@ÃÐ ÒÁŸüžƒºsЦ<”]öç®<ðoò–f9Ú¹e–£ÌràZßW h]:Vâ÷õ¨g%òV!oêY…2é(“…ö§£Y )õd þŒB¾ú‹ò™¨+¿e‚ï™o™è÷5€Ë¢²àCú0teO6pfƒ¾Ô™ƒ:s€wê[ZÖÇ:”ÉE:õç68s38®£öá{#pmD›6ÇFà߈úò‘· ü×Ö<ôïßÁߘ¾žùM6×X·èë}m¢¯KôõÈ@¶}ôµ…¾¦°_Oèk ZGèk}m ¯ ôõ€nÿëö>Ù캭Nö9ø¢Ùæd‡ëö·nkë66ø®Ù×dSƒçšMö3ÙÍÿÛøê³.Œ;Ã6¶·‹!§šM¬Ûú-¬ÛÁº Lö¯nû’ݫۼdï’­Kv®nã’}K¶-Ùµãš={‰íXZ»BžþÛ|ËeHÿrtDóÍ~¤/Aº_§ùiô“¾kèí8½¡ÐüÐdÈ·ŽÚ›‰By—ŒÞ‚,‘÷àè­„þ¾{Lš¼¿E>}ǧµXú#žLzxf ì$äMG=“Ðæ¤/H‡¢¾ÙÀ?›tÚ403гm<ðÍí³ÁÓ9€Ÿ ¼kÉJià˜‡¼y47£M H£M €#ã=xSñ÷rôe åÞµøNG:•þFÞj|¯mkAûZ|¯mkÑÞµ¨g-éмtLG;×þZ¤—¢Ž”[ϵ ih߀ü 4§Çzðmýú–£¾$´g5áC›–ƒÎk)±–&·´£îtúðyàc>é ýZÀgÖÕ¨s}\ʬÞ,ôY~_:W£sH€†<´y=h]úò€/xó@sÚ´´f¡¾|ò€;ùy¤KŸGmíyús¨<*K'ACê΢ùžp!?æyбž>àÅz´'õæy„åò@sò7¢¾År;N[“ésuû¾kû¾kû¾kû¾kû¾ë?ï¾kûžkû=ªÿß½WÒÃí¾DÝÿ“¾D þޝ÷ »¸-9voÙ-ì_%GúÕî³;ó{ö"ö·âÀ>W2ì|¾;ؽË)b¿¢ì‹%ã¸TðûwöÿžÁïÛ«Ø|ØŸã¹hoܽø½NŒé¢Ý“÷ãwîv¾FäCÍ×hÇuñâ·î…Ò¼æïř߼gØùuà7¨iæ»wÃwUÇyÙ/ý&j±^¬üþ½XÞÏ×ÞÀ{±Ÿø~oc_¤~vq_ªØ©3û´ÊàûüUì—ÔßÅgH3Úÿ_…bÂh¾ÙËÙ{¹|'Cþ¤´ø)%ÒwÝé§wAäŠÞ’ÓÛTímP¹ôeJïf´÷ôaü¦>G¾«§{üã«äÛzò¯Å“Æ7p\™böwÚ ýBjïi£ù-Q>û¦/gÿYìC+ýhÙùBEžµ„}ãXÙ?N¡|‹K¾ëµ7Gîò]®æ#µ€ýl•óû#ó Ò¤Ëìs«MƦ™ê%ýTR¬íMR>¿gpà7û´V‹ã·ûiò ’ækË™ýêıOû|Fó³å̾R“ØßV1û× c[9òÝo\+û²/‘>¶´wŽI'ãÒŒÃßsñÛ8¤ÇQíÞŽC¿*hTÑnu©¨K-¦ˆÓè/ä[oÊNŽ (?å' o&Ò0“QïD´g"ðLžIÀ‹ïXú¾Xà‹L,h‹E?N¦2 w p¦f ðNÞ©hÏTÔ;ùIÈOANÞ$ú ÜZ£.ß© !ð©À½p‹ñûRàZŠ¿—âïåàÇràŽzg¢üJüO´;}6øf‚w³ÐŸ³ÐŸ³€spÍü,Ð9y ÈK@^òPWÆRÊÎ¥¾®¹øm.~›K¿¡Ÿæ¢Ÿæ¡®yè‡ùhÛ|Ð0póAë|Ô»ß ¿í[¸¨c!êXˆ:Ñ_‰ =c °‰À™Ø`.}’+t$£Ï“‘—Œ¼k¨ÿNSlrY´p‹·¿/žÅ€]Œ¶.A=K€ hXRAC*~OŽ4ÔŸ†2ihOò–ÒùK‘¿ å–!ú&e—#o9ò–#oòV ÏW€æ¨k%}·y«· õ­ÎUài:Ê¥ƒ–tôQÚšrÀ—ß3AC&êÈD:°™èËLôÍÀe!? üÈBŸf£mÙèÏlàÌŸrP_êËÎu¨op®Žu(“‹t.êÏl.pæg.p\Þåá{#pmDçnŽÀ¿õå#/ý¨­wèŸîÏÂ~ÿ/îOX[ËØ¯chBëZkкâê}VZ7ÐzÖúZ@·õuý¬ÙêºmNv9ÙẬï™^m“ >·Û·ÿ±mûìÚ¿°iµqбÿæ7ºŠÝÀ$H?EFó5î'ßjo¤«Øh´ôÕ®ù¨)–qÝÈW½óÕâ³í•þ))&Åx!?â›b·ŒÜdäMEûâð÷tÔ7cl:h™Ñ&ý¢_ƒï9¨Îe)vä熶°RЦ”KMI(»é• m5äq5ð¯ÞÕè—•èõ w-è[ VÓ4m@zþÞšÖ~èÚ~o@]P×<üžŒ²ÉÀ—‚O2Í»ôüå£t|£­)è×E€[„¼¥¤ó·ô,ÝË1.MKAK*êJE©ø}%Íç4"/m]ŽÏRгùK©p­Ä' ¿¥gÚ¸ßË‘—…þ^Fs9ê^ºWâ÷•À½y«€/u®þUh{:µô®=éÀ¼Ô•z²“‰t&þÎDdÏzÀä Þ,Ô›Gs;ú#õ¬Çw6ðå_>9€ËoסüzÔ›‹zóÐ_¹À™ \¹€¿óq¾7‚ÞÀ±87‚wùø]¹b7‡·ï˶ï˶ï˶ï˶ïËþóî˶߇ý×µ]ÿ7öfýÚã´ýâþqž¬Ò·ÈŸ|%Åq¬§rŽ«ínWÛÆþ’¬vñžØÿˆ•ckËØ0š/’hö“],}'‘ù¤ùµrœíŽ!cáXÛf|NͯŸû.)âPo;‰}V°R«]*ŽÓéǾ‹düAÍ©;ûÌ·‹)`aßJv>Ø_wÇí,çØîv~–J8wû'M²ó·ÔÀ±oÂ8Žg‰ô¹¤Åpg_¥9›ÛÆqqÂØSû-ÝϾØi4ûÏñÉO¸…ŒI Åï¶p ïhöגþ K¤?qòÛDqwÈo¡o*‰cNsÜ©*Ž êÀñA­#´@ú{¡XAg8žÇ4HcŸ¨%2¸æ—ܾQ“Ø?j±ô Cq¡´Xö—Ç>S‹ÙTÇ÷bŸPi+¼BÆ$Ðâ…[دŒ•ã†p, û‰ ã¸26ùZÔâ†[ØOT4ûŠÊàxág4šýÐd°ïóöyîÎqÒØÿy ûNfÿçùêœãX핾еXáÎc4AÆ%ßPã‡ô8Jÿ?öκé*ÏãŒ2Šè U-¥@)XPt¤µ…V ÿ´¡!mš6é3 }¤¥´) iSšú¬B¡ŠŠ;³ìÜSñ±€³»QtVÏ¿³¾˜YEhQǶ¢.*>Êê*« ³Ÿ›ûO[;0ç03;‹»í99ÿÿÿþ¾¿ïïqï?¹÷ŸôþÐ[N\ËÈi¼‚ q)ØR°¥`K!v=r=|)è§ Ÿ‚~ yÉ“Ê1\*¸Ô9ý7Àc€gÇâß °+ðm¾­ iàÓð7 ^#¼FxðÁ®ëh8W‚Y çJòkcZ$—éÄŸÎy¹¸&×éè¤ã‡?3°‘!ŽØ0£S§™þÊ[O&×™b ‹?™ÄŸIÞ,´gÁ™EN³Èi²,x²ð5™™™™]+ãÅŠ^œ9ðåЖC[ŽhÃv1•€ÍÅ÷<|ÈÃ~¸6 °Q€BrVHL…ŒƒB°…pÖ,K‘B°EœÁ]D¼EÈŠhs`c׫èËrôV‘×bpÅàŠi/§l 9)ÅN)ü¥øPŠeøPF»'öè8‰Ç‰¬\¼—#¯@¯yò ò³Ùjd«‘U"«¤Ÿ+±]‰­*ñBV…¬Y5öªá¬¦Okð£AÄDŽ\ÄêBÏ…/.øêð¡u\×­#—uäÆ n ò5ûä’ÍClòéÓC?­ÅÞZì­…Ó‡=œ>8|è4r݈ýF°p6Ë;ÿ"¹´k†«™ñÓ G3üÍØ ,’{¿‰9qðO|Пi¯á±z!ÿ÷ê…„Öz§[çi}ZÛüý0ãà;k5Æ@pm6ò·Äb-&Öa#×\ôù¬³èó¡õÕÈߟn%ÖO¡uÓèµRhZ…ÖEb=$ÖB§[ÿ„Ö=b­#Ö9bsºß"^ËŒ\¿Œ^»Œ­[þz{ó,÷ý¨Ó-kˆ‰Ú¯Á½[µ}Ú>ƒa²FX°ÖÔ8mÏú0¹§ØõZ½¶‡½]îå'êà,qÊ}NE½nQ›JìÍ)öÀÖyÈzT:^óȽƒûvÂ¥#:p:®õèëõ²M2>ê­²î½Nà:åþ„i´éíroN=6tÉ:4z—Ü¿^-=8}«¬%ª‡_ß!k­êÀÈCúiNYwÕŒ 6lÄ`ã<™¥[Û÷?Lîñm†w%9È ×`?ƒ¶ìvY¿Ë´OÖá1Áíg¦Í®c@ÖcÍÀމö\üÏÝ*ÉŠ=™ðY„Md‰à³»‡÷òwˆyʇò¬¨»%jm•`s:ô{>y²ãs=þÕÓf‡§ž¾©§ëµð—ñªG§†öÑŽN½h#¾ðÕ“£zrU¾ W’/ØZdUèÕ’³zñ"÷«7þÕc9h@^…Ÿ™øVÿ ð6ÀŸO=qgÁÑ_=1ä`¯ö&x¼"ð4ñʃ§ l>>äw?‚.ÄŸB|+„»\!EÄX„Í"ò\D{íEäa•˜ƒáã*r± \1¸bpÅ´—ÀQ¶„Ü”ÏRxKñ³{eØ+#–2ÚäÆI,N|tâs¹xák9òräÈ+W _îjtWùY%²Jâ®ÄV²*dUȪUc¯šØ«ÉE ²d5ä”À…Ì…/.Úk‰£ŽüÖq]‡í:ÆsÝaùø| ò5ø²†Ý¤Õ(£-2L«µ€Ÿ‘´Eõ©Ä^àÁzÃYo8鄬;6 lبZíò6“üÏܪÕ&ŽY\/?¬Õâ:™×l«¬[¶f£M[ô"­ºÑûd ¡üJÙ1¢†×1ØŽÁvÌÚQ5È×ÜYk`1móÈÍ<«œ.ÌkÕê™á»±C«9„­Ë;dM3Q» ýX8q¾CîÉž²OۗݪÕ(Þ!ë5Äác¶ãvhµÄñ1Y8 }K„¬—€½Ä ZOú%1I«÷i×j|Ë’hYÿW&k÷]·j5ñ'™¾MÁf2¾.³4ZÖ ³j5Àà[Ö*k€š­rz³L¬ÃÏËðI‡m|:a:Îpèàφ3——^¼à¶#/%6=ØüO¥Ý.Y:ùHE/ ä#n6 à Ø3`Ï@ÜÙr*–†nx>Yð1 =#8 ö`ð‰ÍHÎŒØ23!3aÏD<&¸Mp›Ð)ë€#}PNéò8fãSzfÚÍbíˆOnü6“33zfa‹v í¸<Ø,€×BŸYè³lò’ >›˜³áÈëæÜC 6Ž6Úl´ÙзÁiCφ^.z¹èå‚ÉãÂN.¸\p®§ä42?LNäÈÇ˃¿pÀQoÂb* þ‚9%´ã›]¬‘Ä;øè8À»DüØqÀåàܬ„øKȯk·XÛ S _)|¥ƒršZ†eàÊÀ9‘9‘9É·¹×*§°åôWþTÀ]‰ /}P®bP›Ú"«ÄF%ú•èVÒ^…~ðÕà«‘W#¯F^¼zP›sî¢jÁÖb£–¼Õ2š¸v—›XÝøìFß-bë,tÜèx‰Ï Æ Æ Æ Æ‡OMÄããÓO¼ Ä×wçMœ7k“°C|MÄÒÄxjÛ‚¼\ }×Bßµ kóp1W¡ß#Ÿé™÷ÈçÝb}4V_ïÜ«¯7êöw~o=r^|¦}/FÎż7ôüzäo±Å\UÌQCsÓÓíyQ6î»Ï›Gþoäȹ _›ÿ|ÎÚó¢C›«‰¼ZÇ›uÈZ˜¢žµ¨¥—. ;Ó#d}zQ·ZÔÁuEM=Që'ò°¬{ªÀ«ÐÊçpÀùr䳑+‡e½”åØœƒÿ‹á\ŽÍ\i_Ü-ëo.Þ äň1ØIóxÒwÈ~)`ôÌøž‚ ž,Ž™Ø4n•·X&mVpVÚ³ÀZ¢åc0yð¤ÀW€ +¸\Æ‘W:x;÷ö Ú²¹¶;#yup´ñ>eâhÂf©x_ŸEâœ8òð;;Å´çq´ˆÏG¸óˆÙ_6Káv¢cïÛèØà²wñ‹÷Ip.lãW.¾ú8÷ƒ)Æw¼Îpúàð¡ïdž^¿ø¼‚;@ÎäË…}?ç^°.!‡ÇG›OØ ‚W|&0fýK»_à°5°Lë‚àßÙ<û-ð¹õ<ê¯ñ,êò9Ô_ò÷ÌÿÛzˆß—gOÿ[ÏÎÕßú~ŸŸ=|îzæô'~G>VÏó,êyn•µŸ§À1¦àã|žBî¦bg*ñOEo*ï‡S‰cÚYC9XÓ®ip‡ÃŽ^8zá`"¢em¼ô"Ћ m:mÓi›µ=§‹y ü‘&­¶4º‘ØL 3ÀÍh•uô‚u¦ÁE‹.ê°V[¾œI 3ÉÓLtgÃ,Úf¹´zŸäb6m³íZ½O0)´'ýVÖwîÔj~.U÷»1àb°ƒÝ|‰An˜VÿsášKæ¢?ýyvY'/å)9õÖwùVYTÔœE?ýX8cá4`ÓŒùèÏo•u0ç?&ëfñŠã<ãáÌ”5ësŒ{J«ã‡Ä0­N=ý¨—õêŸÐjÓø—,ÒêÔ#KÆ^2¾&»´š›ØÕ³€YÊq)ø°KÅ ìR°ËÚµšô²þ_3>˜éC3zfôÌèYh·ÐnÏBnll¡Ï²‰3›ólâÈF?\6v³ÁÚh³Ñf£Í&®á´ ]ôrÑËEÏw.¸\p¹àrÁåb#ßòñ+¿œä"_ôúèˆ~£@ÄOìnÑGbÞNÌvlÛÑ·ƒµƒµƒsÀéàÚA<tØt WÊ«„øK°SŽJäNÚ+Ð+%O^ü,%WeøS® \}àåå‡Ó‰¬Y9<åøZ¯n|¨ô_`Vƒ©ÄßJÚ+éƒJÚ¼.9u­B·™?«‘°] ¦L z®A9¥­W‹Z|ªÅ~-ãÀnât£ã"èºåÔ× §ŒŒW¬oÀùå48€†09ím"¶&x›àmb 5á_±´9v[°ÛBŸµïä-È[Äœ9´ÏèÈuЙž'…þÇ}ô³¤Ðo'O÷<éLÏ’‚ êqcõhÅœùlŸ‰yð{†4rïT1Ç=›gH£ÿ§?ôiôœsô\34ǹªí4óÇ’qøÜiô¾\kGÍoÐæy#Ÿ;ü}ã#æp¡gQÎqçL½ÞøvY<¸¦‚IhåˆÝ„­.5ö“ļIØKb,%ÀŸ´OÖ¦µ{¯c<\wBÖÂ]¼C.ï§tȺ½Éø’Âч;‹±——ÂQÇ8HÆn:\éKs}µ =9>Üo٦￰l8/{OMø§oªý¶Ç^~懯ÛmžuÄP¨†úoJhœÙåxY¿yÍÆO"†Æ{Ï+ûš²—¬SûkŸxDùyÆðýyÉñÖ‰7†îSuãåï]ujD‚ãDÕÆÒsdzÕÂÞçÔþÛª›¯.½PÝ}Iú/Kežæ×ÆM Ï£ ½àxQí÷ùŸ©»ù¥çøUóY²Díß6ÿ£Ü©6ÝÑKÿæÆob·«]§6®_æ<è~Óò8Ÿýα7ÏÓ+’ïsžënÚ•ýƒÒ;í¢‚îCÿ©öïÿlÒ²Ç?P»¾ìúl^b­ÚöÂOÞºlø`« aIÅ5ýï)½Y>Y¯öpde{Þ~µkpÆFa—Úöþƒëç×XÀûYm”ï;Cã­×þ‹ç?¿}è¾ìÿöéôŠôÔ®îzåñ®Äu½¹ð–ÍêÆœg—-¾4ž`¿«þ‡zv«o[†ú«·d{ÃÆkêÀ„”Oß¾Yí:bÏÎôx†óôÁñ_¦Oî÷¢`¿«ÍXo: ôúÿ}ÕÇoW.Ýt´Û©vÉûMÝäÚöÀæCàe¿Þ°rÃÜI¯Þ:ìÛ‹7ŠïT"ƒ7ÖðûÒs›Ç?êt½Ïnz7þ‰Òã§”Ù¡ûºHö÷ÍŸ¨]« •á8î»x ¯çö¡ûz æ¦óî˜yr¸ßd>”Dî¢+7$©íµëmö§ ¿Éþoå]ÇúÂ1¥÷&|ú9]¸²yûþ—oU»ý"Éçü©Ú~¼÷Óœ›sôDzßo õãs?ZT·0\ˆö_×ì¿Níúçwߺáâdõö9ç‹HÁË~_÷–ºi³†î»^ÞÜ×e ¨K\±Ü¸{è¾ëúÇo_—xTI)ù*¿8ç¨ÚþÒ‘Ûô{[Cã>9.Úfº^ÜòÕ‡Cy}m|„¸Ô´SKÊ Õ®Îù9yÎ]ÊâÁ îzê—/©í×¼Qm|i8Ÿ9.6%¯¹(üГÊk±»îÞ³š¸­'}‡.ˆS»¶TΈßìz?ÜôÕº£¯× ß·9nŸößìXWǶèÙVPA,vlô*DecÇ –(vìØ±†h )JIÃDsHLÁ$FÓc’5œTQþ“¦h4‰é&'åýfÏìÿþoÎû¾wßwï»÷>ý>„Ù³f­5³fÍšº–PÈ1z娱ßô©2jgïoj|Qo·¿P§Ú7m>†W´¥ ²Ÿä.O.üns‘^¹ðøµþÅô«åŸŒ øšqà™UNÆèýêÖ<•id+}wÊo†ì÷Î;Úø×Ë'õÊÌuÓ>O¿`Ôn:²ºÛOoö̪8ºêœ‘=·ÉOÛG®^Êû¾6¿{6É(Ó+ŸÌ?ÿaÿ7ÚÜþM~=üâ°œ]Œ,‡ug&\ž)å}ïY¿éñŒS~•¯ß{léñFmþ¹¼Ç¾šf÷cãñù»JÊõQÊ>)}ÐC…vßò*ødÈ•ã„^ õ¿?vɨݽ$Çý;ãæžx£×‰OŒ,©o”“rÏéûÛÀ¤yKôÊžŸµ…‹~™Þé{ÜûxéËCO¼iµ³‘v¯Üù™mÏf*ù 6½vègý·Ø’ø‚Q{¤4{mð%ãÀ'­Ö~Óì3#[ŽÇÀKy«ñÓÙïÎŽr4þì{/£öx§FCï©uÚ½UŠsöŸœ1?\u ¯°ô|Rî÷Œ\wgÕ‡?êgŬ¯[#£öÍû|ÆåÏ5¼7õËòÔiF΃ãÏ~¼”ó]!«õI¿ègªûf]Ùq£öÌÁÇ:>õ¨qà­´Ä×KŠŒœ#^Ïþõaà•^/¹Ïmrý9ýlñÛ½šúo5j?ÚñѸï[í›py–”ïm{ ‹¶=´P?[¹Ã/äÑh{<ùâõ›âN^³õ³ú̈û>hnääˆjNy)Ï[w¿ÒgX¥~ö{¯;~¼{·QûÝ òCNæð“{ÕÈY$ü˳¤7¬ð\=eìAg:׿¦Q3¢)÷Û#ßzd…ÝŸÞ>\é;ì’Õ~”—r[3ù–_ÿVã”Ã9”&æñ–F]ÓOf}7zŽ­¯ÿðø«^6r†rP^ÊqEDÌ7ßîh¨Ÿ›37ê‹?SŒºÖ# ­w¤q@XÛ[¿6²¿˜ÆŒóIड़–Üuæžß=§ŸÛÚ8­Ë;cºN«û}“9ÕêïFös>jÜx)§¢:Ͼn×ïïÚîmýµ®×ÑÍËÛýåôûuî'~°ÇÍYR~)>æÀm×óï½=qØ)ŸºˆÈ×c^:gxÓ\ 9ÒŽ$\ž-åªúŸ~îâû-'¾´Ó¨ëûšÞÔ–çñ ñ¶O6r·¾;oh5åä¼l#þSÅ•û‰ïkû{Úüº¹mÌÃ/ZöÅÈ=¹¦8c¨³ß€GÎÓ¦vIÚ8×±ÏÙUbÖÝò¨ÍÿÐ}ïæg·ÃÓ]ž™Þ÷7ç|)÷Ú'›&l:lëÍl9›wõĸ¹§íR5äÉ(-é^£.ñÓ sÿXiصñ£—¾®3îí^ñYêÊÉyÛriõªéæ@bÔ%-}óÉ{XvǸ7||BÏ¢xàM¹ëk>|ó‘m{fÛü¯Þ‘õA¹ÝSçß|rÖ«ÆÂaqœë½ÜʰAO^)³çå³Í~¡ovØæ[°“yÞ¨›]2iÖÉãÀ³>?Ìi>ÁÈùĺ¡_1þÎ6û~ËW·®~-p°^õhÇS) Gu‹µWóŸ†ï¼o†êdä\Œ:ãë —ç˜r×ïØ¾EÌpœó„ª—Fu¤êv»¯>t¾àŽ%¶žåô{zÏUç<3ç÷~?_xÿm½›\o‚Wö‹»ªÖùý–œ¨W½zÓÖá“t£nÓ1F’»?=bN¼¬rFî n~·ßÚTï`»sd¿Ø²ãšÅ­ïѫޙûè‰{u[Oês·¥Úý@Í#råz‡rRîÙc¼†4_Ð\¯ºÒdÁÉFÝCWŸ¸oå<úÙÝ­Wå9E·ÞSòðRÞ–]¬’ëN£îÉéß½òDW›ß:³c[öÔÈ‘ó:ÛÎÏ‘ýàúaSù,WwH}4êö~5·Í½Ì[Å,wÕt#GÊÍž/ΑòÎê³BrÊÛõ¼Oÿ§ž7ê|P{¡Ñ(ãÀw­E‹¹æ2a¦=/Ÿ#å¾meáGwý¾ÑÙÿr`÷¿#³†ú÷s³í¦äÇi÷rÕúÊ9_˜+ûǶŸ´[n=Q ;Äp÷Õ6£®¬yö›ñ·ÅÿÝ7/¸Q•§ýÊùHÐ[ï÷´Ûe®ìužüÎG«ëŽ5-î¸ToÔ½ÑgÍÏÛ»Ùr|ïÂõ{Ö¹³}ÀPÊIù?Ôçòc¾+?Ô·OòÙ˜ó¦Q÷nEQó{lú¯«¸ÍÈÝŸñÇ7sÊ)'åÿÐ{;*/þCÓ9Ý'·êÚŸŒºs‡f<–9Ú8 úe®“ÕÎcšÝïæÊþ°]¨¥q@wlïtW»Ó>v;~âûÚ½_¸ŒÇÏmôÿú~g¿ÈÍ=uL¯¨p©¿ì´9Óqçâ_tÇS¯o,/`Ô}ñ~ßVÓO>{*ôù÷2rS+§Fxüi÷‹¹²_äúíšç çøêØ7y‘Vººu“ãÓÞ¶ý˜7H ·õIÉÁÙOæÊ~BÝožÛÉîg†ÇŽ~Î3ê~ÉÒ=vÆîïr<3r§NûÇÒü1 —SexüæÇÄ Tw¼3f—WƒnF½v߬Çú¼i8¿vÖà¯3Œ\¹Ÿ¼”ûßúF\Ý7YwÔÃ1̨wÿíá?®cgŽËFÎGǾxäÖýÀKy?Õ>9¦KÜív?6ÍNŒQß²í‡Ó§å…y øCPóÜ¡¥©~ö¸Ÿ*å_`6äR»žŸoùpcÑ«F}sãÄÞwè¾ ÿÄ–ŸÕÿRe?(ÈNÞùËéÝñ¥ÿc/Ä}`Ôû–-ŸÞÖÚ_1îõtˆðRÎþ­çúÉÚüX´«ÖÙÿê;6HZ”_gïÈý6§þÜ«ìŽsþ™*ûÁ®-‰Ï¿úû8»>¿hÄ®·œý²¾Kçn?ÝgïkˆeÓ'×,;åÒ>²<-Øúäˆ^í!&&‡ŒúnOÐb›qP3Zã^÷Çlè52áò<)÷§=÷Öäd½ºíƒÕ¿÷»fÔ÷*ˆm?NCæúθ·á̳­¯¬^ÊýÙccj®}¢Ww)xyKW£>ä%ïíO¿â´#Es¾¿­wó¤üw»§·¢ ëÕæ6Ï<£>êè^}6:Ç-e/—r~Î-øÛå­ÞpÚ¯ê¾n½ýÓëF}ÿšyµ§öÚôZæ=r4¿ÜÈÍ…/Úí;OÊù¹ó³d/Úálßê¡#~|ù‘=F}Bx»#w\²ÇÍ?#/îéòoÆ;§žÍ“ý`Oª1;ãÎŽzõ„ ÌÀ×õ£Ó>R;Ï–OÃN_~7ñ¸‘4ÿÇü']ø‘òÞcLÒ«§½zÇÍûõ“¤ŸÝÝ8Øùç”ø9rÜ^Êso£G–”|kèÕ©— XoÔÏî¾eq㯃#ÆñåÜ-FNBCP§„Ëó¥<÷®-ô?¹ó9½šÕêâ÷†õ‹s=ûì¼d·×âWFÊaž)÷y('åú|ãÛ|™â:Ç£êµbAÛ×îß«}.âÌ$ã ³£Ç9ãÒ<6ÄžïÍ—r~þ@ô¢3¦;õ¤šÁ™©ÝŸ7$õßPÚÆ8˜±¢{šÇY#çQñï1»Ï—òAl+ ݯWoÓc@Ù£~“(ÐÌÉÏÁuá?6®øÕ²÷ö¬WËy»sÞSGX”GQS{ÎÜfg¯³æK9¿Ôúå;™êÕ9ž3f>lÔßsçj·´óÆÁ©óùd‘‘sOÁ§Þ‡¢—r}I˜ï'õêmiHà”QŸûnÏ£»Bƒr~kÏKæK¹¾üÁ·Ïÿã‹•zµ¬¿QÿPäË+Jú‡ÌQ[\gäHû›py”ë¾¢6¯wÿìc½zWÁ{~n{úGs/g^ùÕn¹÷¼yvs¹‘#×a”“rÝ/–'o¼«W6ëóÎnF½Ü‰¯ÿäÜö·_ÔƒY·Z¿2Ñæo”cáPÇñg§@O,Ú&ì¶ågšÅVñ—¬þ¾3¨eëã›ì~¾@ʯð©‹i1§ÝôjÉQÿRÒí'¦ÜnÓ}4êuϯ‚íy—\·Úú¶@ʱðÓÒÍç¯FéÕ_øí]¹æ{£¾pÿÜÊÍôiðŒ|=ðRnº,\í3o›^ýõ„€ÊLƧ‌ïÜi<žôCŸFŽœ¿/åv ¹æÂç'OëÕß ÅÈ5êzåùâÇìþ!Ç%#gSÏœqÃoµûÙ)ÇrÿM¯fTy® µQø­¢òö¶\¾^Ð5×£¡s=£äj ¥|({W-÷ÉúR½ù¾œ+v{]3¸Ö¸D9)_Õõê?_ê|~-ô/9ωÿÊÒ÷Ÿ<1gŒÞÃ×ä<Áž.”ò>øIòì–»ÔkÜWvWm}9lDÙíñÍÁêÿ6Àâß9¿tÎoJùM‹±ß&oç¸[Óºñ«Ú3«hß}·M]ñƒqð«6Û£7½mäü|íÍ;󿣜”w‘<ÿÒk:šõÀ>“½÷€qðç¿þñÊFŽØ~hö+ðRÞÅýZõiôåXçxScn×'YtœãrQËÀ£‘W¦Ùû1åb!=Òî· e(.¼ÿþŸw;íNM¯èVLidž‡&Ohø{mwkg{ªõ¶Kýe¿(Ùì¹q‘wõG[ŒÈ.oE^™zö½íþºHʽä\í¸ÆyCgßçÑ×vÙzöúÈû^;Þ9n){o7åˆéÔm/Øýh‘ì‡æ™n½FÎŒúcuc?ù`s^]¶[Œ¤Nù©s/»_/’ýáÐÅàÑ»eÚò {…·‰Q/¶ãnÞb ô¾ydöfC­Ç('å~x¾×Å÷[NÒk˜,fTþaÔŸ~cáËÞeMÂÛŒ,s[Œþ»HÊû°Mo:e˯÷£[ççõoÌßáÿ€O|™êwEb»§w‰½û~Fxì¨Õ¶ÝX$ûCi;s©×ˆÝ€”ôšµig‚¢vÙõ¿oJ¿Ò_X|Ûò_$å_ºéþ…UW¿ÒkB/¾Úîñžz͆'/Þÿý:£è±Y¯½×Ð9^©}AÛþ-’r/•ûF´¿´ÿ5·dl«÷‰±å˜½hûþ$/ç>ŽØû¦È÷äQ²~dÒb…d·7sa­×Ü:ú÷MmŽ;ç™E¢øÁõ Iײë¶<Ë~qÄ\{ W…s÷½:D¯¹íÛ'¿\l¥›WFvià¯Z ‡‹¥ü_‰÷[z÷Äé¶\亗r7ÿz!y³ðìóF¶•ç/ œ”ÿ+Œ¦§þ6A¯i»©nÔ¾¶zͦëV|`-–³ó|Ź_¶Xöƒ£Å¿q ý¶Ø0ªÖk6Ƶw»«‰Qt÷{a¿ÌN²íD“·ŸþpÞQ[î‹¥Ü_Uëº?ó€C¯Y#*¸ÏÖi¸­}@[î‹¥Ü_•çCÎùOŸhPO½fe«Í“¶²ågîb&9××jäÒÞ²¼&ç».ígN„ôš¥gĄϖßùÄeœ?`Kr>gó·Dö‡×–/zž¹ ^ã}(êyŸXðÈyKÑGû)tÙE#G˜Ó û—ò~­Fl°ë5j_¡Æ\6ô0оl¾ë‡_9-šU ~lðRÞ¯67’ô¡Ͷé5é{·~ü¨Ý~ʾdÿ–œØîjS»¾K¤Ü_Wó“šVâàf2zsKPíßkœë±b¹¿k·›¹ÕÓ¥ž²b{ð•l»ž›Û<¾ý§'Œbó:Ãz«½—r7Òoa¥ý‚-7ÏŽk6<Ð]¯¹ã÷/º5¿älçbf›•;ü¬õ%å¥Ü qŠì¦Ûrò07Àõš»6¯®ÿü²E÷z¾m½_"å]ø\ÿ~ï|§×4pöÓgîÖkînÖ¼ã«?:Û¯X¶‹Õ~ —Ó¤\Ëfˆ…u ^ãv[å¾SôšL“’Q,VÛgí}è4)ײÜQWþØy«^-ïWØõÞ”ÓÁ½ÌÙOÅ!Er–æìWÖý†O-½I“r/{íqÃC¯¦3 hd·ÃÚ¿mqØæ?DìÍÖ8aÛ4)ÿ2kþ{á¯möïMÿ4ÏÁâñZÜCÏ46²è÷ ûHÖÓiRÎÇzŠôÓzõ‡bƒv¨^“:¡ËšFIÎyFñâ¼i ñFö”ÜïU¶ÜÒ¤ÜÍ4'zµ<'±ÇÉiÓÎ>Ùßîwë^üû¨&I¶½§5wüä¿ìÇ ÞZæ»æy¦^“T2ú—Ä©Fñ­Qgš<}ÀÈ’ë6ॼÉó½ú°o ¦@¯“øË*Ÿ,£X“=ý¸‘uÒ'ÎH¸¼TÊù¸y<>B¯~¹,¿yO½f䱃e÷Ï¿2f|¨³Ÿe1Ëhõx­­gK¥ü«ýÔêz9©^3|Ä ƒ&Ûõ}î͆' Ÿ²å>ÝœèP^Êû¸I¶«^-°oøY¯QçY–},ÞÿÓ™¨í­~N9)ßãw]¼-l3óÜ'ú¤¼ã_h÷!–;î·éË H#gûÌýS ÊKy7§cгäéOÙòtò¥5?Úô÷íåhü¹µKy)ïãš2zµï½?õê-WW/¾D¯ ÿ¥ÿõ[â‚Ac=.=` /åz¢Ã÷»Æ]j¯ƒ7¯oûìEÌÏ:µ·Ô–×sÝ……4²¥]L¸¼LÊùDïþ¹…å¹öúw­¹AŠÝMÝ÷Hf­­Ob:Õø}#;nré§¥·Úãà2)磷ÅßIËÉ“3.(Åæqi/#ûö[›Ôíܼ”ë‰sÖ?‘mó-ÏÏm95ŸºaÚcß9×Å?/ú=·†ö:xIœ°GÊù«åÝÏ.³ù¿É¼ð¡×4PØó§NþKF_ØS³Û¹®É~ÇÿÓWgØýu™”û‰}æD@¯VçŸ5jŸÁjÇs['x)çu[óûúܧW+»ZÓøéªÈß»ØíöËì ¶°çˤœO¶ìGíúGtùyP ô¬ÉÑ/klËíû›gÞÕιϘýskqCÉ–É~prÌ¡&‡_°ÛAÊû0éÁöž¾FñOíÄÍ"Ûn«y…s^²\ö‡“w/ž¸²ut§abæ=>¸ãRÝ£ø›¿½H?qèÄÆæ?/å~RîÛZûqzMËØ»ï÷tŽë%r^aÍl;´\öƒ“ÿAŸ¯W·8Ÿ5åû›)ÿû³ÃÛíÝb–º‡mG—K¹ŸòX~ä¾ÛƒôjͼÃ<`ôªk®%ƒû¼´ÿg{Ÿi¹”ë©!åÝVØû–??ŠêÐ>ŽÓçZ¿aÿÚ¸íÀŸ­ózÊIùžZšð“Ý¿Ÿ}ìóæŠ½¦Á—4_]`ëó5Ïý»aÿÄjtD奜Omoš3opoÝQ)6"a½¼ñ–†Ý:%âÇŒFöªc·¿òmà¥Í]û%÷鎲ÈÈüõnzµ¸†·ís»=Bî‰ußÒÌÈ»á^´Ç )·Sò~¡îx±Eö³·W`×&ÿünµ¯ÝÿÅrlp¡uO–û )ÇÓbY:æ>»}Ôø^]½j§áù†MÞ6!i#Kž_P^Êñtôµ‚¼ðZÝqϽ—g†×«ßÏþ!òb/£d~íSÓúÙn{N½4ª#ðR~§Å®^³ Ýaíÿ½ÖýO÷£DÜv¼Óºÿ¼”ßé™b#ôo¶æM Y­OÖ«ÿ¾p~f`µ]OÓŒuv®w­ûCÎñj…”ëé•;?}gýCº#ù”¸Ijï·¾±fØ€¸b»¾#N¿zq²óœ1[œZxÛ7VH9Ÿ–÷ íöœþLU³ÁzõÉ))#/~O½Ä†Õ!û¼@Í'ë¨Rþ§³Å‚î[»žâšêç5¶^¿ž?õ™M'íúŠ]òF/Ú÷VÊþpz»¸¨q^w´èZþ}[ôê’£±I<ç´[%ò>™=ÌWsívZ©úÅSËþ.ï%½Jœ¶:OÝ—”åì÷%LÚ¼wÅÚvl¥ê/æÎú¬¸§^%–{/\À¯ny&̦?lùƒ;Û´³îQNõ Ñv_ЫÞÝs éz½úÁ¸Mîìi” Þòø}wû·ë蕪ìù扃á^öùÿQsâb¯wîÎòÜý¡Q"Ô`ÿF–¹Ý„òª?˜Jìssy¿Än÷õ·eΙm×›ÙÖM5FÖÕ¯àŒuÌJÕ~òå³yÇô*y˦/í¢-7¹Ÿî<'sö÷•ª<*nFéU›Þžpí^»ßWÏm4)b—¥Wö}!9o²å·Jõs;;P¯²ÎRLÃg”ˆ]ƒÁ[ì[Wß1ÿ0ðJÞ[²f‰¦W‰]ÝίéÕ“%ökõ_#[¤¯ä|÷ ÔOùêU¿uÛä•ôìÝÚžgûdËûRÀ+ùÞ1þµk?¬Ó«Ú›”zõ¸—i‰ [ßÌay¼Ý/­yÝ*%g¹>׫ä}`½:qu£Ç]o”Œ½úèŒý·Ø÷QV)¹šÓ¸nú9q,¶M¯Ú¦àtñ[ŽS6<Óaìy뾠ݯV)ynœžóI+û>Ëõç3±—úécó?ÙÜp3²ÅnôíϹÈãŸåzîÍ?WÆx Õ«Õ>U‰yŒ9Ôš—$\^­ä÷”yAY?'ޝº”b—ë8ûí£d¨yË^o­Vò+™óíï»Üã‘ó>½Z݇±öy•ØûCêÞ¢ó¼xµ’ï±G‡Ÿ×Ïí·èŽoß;ºý‘^¶}Çc¿µçï«‘ßNµÇÇÕJîÇß9:o¥~N½GwÔl7{l}@Ê=~ºÃÈË>¯Kv»­Vr7²–bâôsJããɈüGé6?Ë^+:#KÍGò\­úÃas!bËsÞ­k&vÐ…i­c½/Ùülˆ‹/‹ŒrÚ‘¬ ‹^/x¥+xT¿xñcýhÕAýÜèÖùÙ×æëŽ§&.½£ëN»¬û²hIÁ/FVÆ´àƒ™‚¾’ÿ£â€a¸~.äD÷à®ÅºcÛ“¿L¹ÜÖ(Ië"N²,«ýÓ•üŵmß+ú9¯e¿½Û)EwlýâóðÓþÍ8bí³fíÙz< ó {ž•®úÅÝyâ„D?ûó³_¼¸a¡î¸5lWâ+.óZ%G«?¨ñÑjgð¨þ°I`>ªŸËÒ‘£ÜÏ0J}t®v\k¿Ô…¾’¿yÌἿ Ÿ•çúºcþ¾±ÚÖÖF‰œŸÙû¥Ÿ§×Óõlù¥«~`.+–ég+͉£î˜ä^—Ü*ÏÖç`ó€sžãìGéJþA7=!à¬~ö­Ï;ýy™ùR¼¹om”t3TíûÐéJÎM›ß÷ßégËÌ·îšs¢¦ä¤Qâßô£ámn2¶Ê{²À«ù£é¡}·égå=dÝÑzsØîiv;žùb·;­{ñ¶ý_£æq±©ÚëgÍkØÏßï:ò«-oÕN–œ¶Îðȯw]´÷ó×HyŸzòîO{íû\?Ë"2¶óízÕ—#gL[¸ÜiGï ÖH¹žRçBgÇ›V½ª¦ûO»=—Ûz%Ïœï8¶ÍgÄbk¤œOåÖùqz±~6vç$·Û*õªw&|ðÖ—IvýÇŠƒÎ9¶^µÜüd“–Ÿº´ƒ”ó)qM½ýýlðœ';º?¦WŸòÌéa#-û`dM~æJƒï~^ÊõÔ-âÌ7úYñZbõzÕ‘&#—w™e÷‹°Í9ž3fYò]å¤|O©}™³+~Mù¹µ^U(Ïùó¤á÷ðçºQÍ{íñm’»ªo¥²#U{ÿÆ 7Ä(þÇ”§¶o™cd7ïéŸ^Z‘py­’³Ÿ¹ Ô+k¿+~Ú{ÞòÔ÷ímÿ ËºB~WzAy)ß“r½á,W)®K>ö‡^µÝ¿¨rß0ç;,µn¶Û[êµÝ_ÖJùŸ<3Ëÿ±}_ZÎsíùЖv†í°×¥,|ÆåÛú*åaëëZÙNÊý½Ò¼®¾Y¯ZŸë“ï{Ä(® XLj[9‹Xÿ¬•r?yß ÷¾¤WÊyó–¾÷ÇÅUæ€ndÉ}à¥ÜO s¼}¿^ùyð¥W¾úiÇÛ“ñÎȺm‡x¼”÷ɰ¼;_Ðí{ø÷”œ?­·Õ«¢óƒ2'¾i·û—ßM<öp±‘Õ'âÃ’'ÎS^ÊùÄ/æE½R»TµoTt⋱ö~Ð¥§O/m|›õnÆ×I¹Ÿ8jØíl.èUò¾‹M_î›8÷·>¹õåpê±NÊÿ„x%wËûz¥¼'¯ŸûjÁˆÑ‡ìùuñ0sÿÞ)Ÿ­=îæx«l[>ë¤ÜOˆë —¿Ö+ÅkŠèAÌoº¿Ø«ÈÏ(>jn,[å}Xà¥^dvXûŒÝ|n¶ë‘ô`î¸.¯ÙûtÙÏÞþÞ¥Rû½ŸÚrŽ7ëU¿ëCþF‹q›¿Á·4T“o·³|oç\ïÜ#¶›~v±ëU?1‡™õúy¯O?vhHz ‡QüpùS Og÷ˆéþ‡»€WýCûØõ3Ïôs:ü‡;ò½½ûCN=²è:ûçzÕOä{»>W*Å zý\ËçEzTÛã¦Ü_s¾ƒtêãÕ_¦‹Ç”¡t÷»ô·†#ŊÖSúÂ+ 1ÆÏ}ñ+ÆM§œêƒDGÚa×C¾e¾·j^“}Œb¡®Ö[z›^Ê©~¢îÞ¹ØV¼PÔÏ^ :ø\aµQÜ[\HÜãœg8û×Õ/ä}7»¾b64j†~¶¶Ñáw3^›ê:ݹ¾Ý"÷ìvÛ ûÁ±ZóL?óntrÍ…/ô³ è<íOúu{qñä±eñ{C¾k-ü;\s“ÿþ«Æ¾¸áoðFü áßäFìñ¿Bñ/Mµ¸Lß}™n2¶eпdÈxà ýT¬á?né2Öp£«ÊIŠŠñU¡â$*ÿ¸W•Üd—xÃA*n2r¯uñå–¯|¹ù)¿¹ùÒŸ›wþšÓ¼@ÅöV>Ý Ù¢\ùÓMT>ÝàÑ‹ñÉ ž¼òTp¶¤|Ëü¿ðéV«bß*ß%îpªŠ;|Õ%îp¾ô‡búsƒÇ¶´WÛ‹Ê—[²ŠP®âŒ¥¨Ø¥*vcŽ/ù¾erøó£N~ÐðƒG¿r9𱆳¥ßÓ‡[Š@[w¸ªâ gȸd¯©8ðÑ©TňU>ÛDúšò×–©|µ‘ß•tWðu¥^]ÁçŸàG˜t·(—ÂÐïå?ø¢Kìà7Ø_ƾ6zÒn½h—^ÉÊ÷?uêuMùþÏ”þØÌ8Á‰Êß¡ŠL:(CÆ> ºªbÓvÁà .—C||†€#„2!´(|„fÊØ¾aÐ > šaå*Ž/ðá´c8õŽ ±#UÌÞ½*V/ß")¹WÅ祿D‘Ž¢^Qµ*oºŒAM~o{—üÞ” ’ñàb Í>ðÐ'Aúi~–M¿q±2\_xîK½ú%HŸ8¯²é?ýÓdü`3&/ð±¤có¥/gáGÏô#Gzé8o›·PúÁ3ãdʸ¼Â| Š•ñxE\‚6Üí† Ït»aÃÿ'Ûp¡¿éªžW•¯3djÆEÿÐdÊØ? ýU\νÊXŠûC7¶1ºÖ8UÅþq(ÿöÉÊë5å‹5Å%fg”ôu/âvºS?ås x«.þX ¤ï13ü5¦ù^ÈOù#ƒf‹ å—šžä{£ãXië×T¼ŸLéU ™¦ïUøèT¦ü®B£3éÎer(íBº 4»8dŒŸ®¤»‚¯+õê >ÿD—¸>¤»ÅºÄô~÷X—x>WU,xî±÷Ÿ}«ö$¿'íÖ‹vé•¢üªVÈáÚô© ¥rè6cö@#°Tú§"D:žƒÀ Î`Ú.œÁà&>CÀB™Ú?>BI‡’Í0ààF:øpàÃiÇpê," êA="ùI™ÈB›4ŠþE:ŠzE?švÎ>^£Éï O½ÓU9]òMT¾—©['¡çà육bwyËé’²ò,æÍÀP&¼üX&}éÃ0ôC€ ¡‡R>áä…ó-’¼(xŒŽ•q³„¨û%Ë8XýÁ¡8`âÊ”ÿaÊò–1®œkÃóÆÿþóÆsÆsÆëçŒ~JÆ¢Œ1ÎxG¤ × è› q‡ŠåNiH›6o£X廿TI7Nû ÿý…r˜0ýҦɸHf¼ÈX忟6vŸðéʇ?ùÍ¢”¯ZÒÍ.~ü3TìHÊ´ðç'EÅŽÆÓOùò'ß¼È÷§Wòk –¤[‚³%ioò½÷&ß|­bÿEìÈòëüøS§6À·_[êЖü¶ðìCÚ|>ù*f$évÀ·Ë—þû}iS_Æ_ÚØ·BÅš§N~Ðôƒ?‡Œiúî§L{Ê´oøè¥rˆìH;"—Že*>$Bá#”z…R¯0Ï> šaÂö3øpòÃÉ@¤# AFP>’o‘Ј$ ßQþ2–}õŠB¶ÑàΔ¾ÿ£ÉïM={gÈØ½)1ÀÇ@#š}(ß'YÆk±4Í8´u_xî Ïýhë~)Ò籈™iÆ €Fpö/“f(øØ éCYÄþ͸¤Ž#Ïqå*vôBo ø‘”¨b}6OüÊçjŸ…íöVØÐP·;Œ;(ìeã,fÙ&a—„ vFØרvÖØ.Æq1þºÎᬱVŒ©bücžï\Dzë}…Ó® hËànP+cÖ ×MJ¥Oëæ´c‹TGÍ_ù±¦¼€õÞ«br]E~mi_pù"_‡Œ›Ö‘ò2eÌ 1½èÞ^A2–D:J›†Â[8¸Ã¡N '‰œ¢è}ië~©Êßõ5mÇñ;˜8a®)YܘGý÷ŸGÝØ»1—ú«¹T’a…Ša¬â ‘n€n6HU±·/ºÄ‘¤Í264"Ý([Å”dhLº1ý­q¡òëî5¡6!¿)ùMc¯‹…” |ýÓ†îàóÞƒþèQ*‡“fÀ7ƒ^3ð5£š{Kÿÿfnp4§L øo‘&c"µÆ“´'4<É÷„/pzÓ‹~âEZF©8ÜÀ·ÞxoÊ{ª8Ü *Že©Š‰” bYÒm`º <µòÛ\S1¸éãm+ä°æ~ðù¨XH¤Ûߎt;`|iS_ÚÔ7ÿºØÛÐôÛ«boûó“¢ânWÈ¡±|t ­:P¯Ž´CGpt„nÇrû¹uÊVq¶)ß™tgÒ+TÌ£d§  »hÙÃëmà_Ù>aó,[gÙ8˾ÑnN{fÙ±l·Ž;iÙ­e¯®ß tµS–}vÉÕ&Y¶èz;dÙ+†µ_øW6Æ5¦eO,[bÙWr½Ýö²®6² Q²ÞfÜš»¼ üù¹ªb¸ÐV Ëd7j”¨bàч›ÐæMÊe w¾{ÐG=hk`›%ÐÅüdìàæÀ´¶ºá)Æeð¶ŒUqêøæ]ªbÓ%ÊØtfʵ†6e*þ2ð¡Œx|öÊX.íH·Æ·@Å—+T1åÐÇö©Œ›î*ŽõëÅýè”§bÅñ»3¿»Pï.àìJž¿ŸŒ/ÜÍ[Å}ã[w~w/S1Þ2el·žðѾ{^SñܨW/èÐnШEUø;\ð ~ñ;ÁðB~ù¡ð ¡à Caà W84Âù½HhDÒ&QäG‰ù<üDS.Þ¢©G4e{ÓN1|‹¡.1àï“*ãܘ1àÀßÚý¨_¿tï­?øúS¿Xoÿf@‚ŒûÇï¸l§y åŠù†KÄ¿X—¿»±ïêö³^¸±V¸±Vø¯°VHT2p¨˜©©*Æ*t W ÒTÌTÆŸ†É*Þunä®b¦æ©Ø_èpcp5¦?5¦Mß½jBº ùMÁÝ4ŸLŒòî”wÏSqÁ`Áƒ|ú“ùÍÈo¾f¤›‰µõlîÏO²ŠŸZ!‡™Q*~*0-¨«'iOhx‚üàÁ‹´ýÀ‹tKh´gKà[ï ¼7:âœ[A³ø[‘ߊtkw?z­i6Ô¡ åÛ@¯M¹ÆÚ’ß–öiKûùïC¾´| ÕŽ¿ÛÛŽölGy_ø÷MSñR¯ºÄKÍTñRùÖ~ÚÓ˜öàìÍÀt@F¨sGÊt$¿#éŽäw"݉1¤éNÐèŒÌ:“b£"—.È´ 9‡A3 ø0h„ÓŽáðN:YDêAùHd Ÿ‘´C¤°¿ÀDŽ(ÒÑà¦ÞÑÀG—KÓÑ;QƱXcÈ!Ú.û@³4úª­ðÜûÂs_êÕ¶î|?xêwQÅh…FxèO[Ç’Ž%›-ãÔ pW1[ái@™4Oq¤ãòdì:¯u øBo ô‘”¢âµŠqEÂøçu‚묫mþWçõ®6XØ^kàº?+ì¥ëzÀZ¸žÕ[1'…ݶÊu^ïz&(l’«=²ìeƒ¬¹½°9ÂÖ¸Îé]Ïð]Ç~1î[c¼×­1\ŒÝÖ˜-Æk×¹÷õónkìµÆY×ó}k<õ,P1 ŸIh°W5=ri„\!—ÆerêÔ¹6!Ý”ú4¥Oº# wàÜóóm¾5§'ù^ R\-ùÞ’¾ê ï­€o-~€o®6À·Þ'Q…¥m;€¯4:Ò7:Ò;ñ» œáà §>áÈ.‚üú^ðÔ'ž#‘O$e"é?‘rø"Í(ÒÑðMý£&ÝøÞÔ¹7ù½¡C~ <Æ@#¦Tš‹>Ðì}à¡<÷i_hö…ç~üÝþú‘×þúÃÚ´?ôûSçXäK:ü±2Öñðàï8øãï¸y ¸‚w t’Dz°ÅÐ$Æ1¦‹ÿjžéz/TØÛÿ“yæõw¬¹&íûOöïz»'leã\íš«-»~¿ù7'½Þ.]o‹\c ÿÕž³°'®óSa?„¸~ž*ì°Èí/÷ÿjl¿~ßXŒãÖ.ÆlkŸø¯æ®b\cqªäC£_hÔµíÛy7¤_4$¯òj„ÌÑwšð­ õjÂßMéOMkC‘›;ùîô ÚÙƒoÍ(ç Ï|Ï»%åZ’çMÿòæ»7íçMßnEÙVô»ÖünM~kp´[Ê·…'Ú¢eÛÃ7JÆôö…·öô‰öàè@¹Ðî?ùÖ‘þÞ‰ß(Û‰þÚ™ßùÖ…²](Ûí ®Àûçžt£ÞÝ{w~w‡÷üîß=€é /” „ç@ðñ;œ!” .”¼Px ƒ¿0ò#„®ówdºÍï(ðDQÏhøìM;ö¦^½ùÖ:1|‹¡lwÃ\Äï ¾~àîÝþàˆ…ïXÊ =€ßq{¥* n e‰þsã®ÃýËÿ×óÆÿû—è›Û/üüÊÏ?øùŸßùùƒŸ?ÕýþÓX#jØ& Û¤±FÔX#jMÕ¹vJÃNiØ)5¢ÆQÃ^i-Õž(6Kc¨±FÔX#j¬5l˜æ«ÖúØ15¢ÆQÞiØ3 {¦uUsZÖˆkD »¦±FÔ°mkD-P·¬µ~X'j¬5Ö‰ëD-R½f¨±NÔX'jØC {¨±NÔú«·U¬5ô_Cÿ5ô_Cÿ5ô_Óå; ý×Ð ý×Ð ý×Ðm”ºg‹þk迆þk迆þkè¿–¤îŽ ÿú¯¡ÿú¯¡ÿú¯MSgŠè¿†þk迆þk迆þksÕ~2ú¯¡ÿú¯¡ÿú¯¡ÿÚ¹O¢¡ÿú¯¡ÿú¯¡ÿú¯­Vëô_Cÿ5ô_Cÿ5ô_Cÿµ›¥­ÒÐ ý×Ð ý×Ð ý×Ðóí8ú¯¡ÿú¯¡ÿú¯¡ÿúo¾KCÿ5ô_Cÿ5ô_Cÿ5ô_CÿÍ;È迆þk迆þk迆þkè¿y¯ý×Ð ý×Ð ý×Ð ý7Ï[Ñ ý×Ð ý×Ð ý×Ðs¯ý×Ð ý×Ð ý×Ð ý7×B迆þk迆þk迆þkè¿ù.ý×Ð ý×Ð ý×Ð ý7ßÜ¡ÿú¯¡ÿú¯¡ÿú¯¡ÿâ~µ†þk迆þk迆þk迆þ‹;Cú¯¡ÿú¯¡ÿú¯¡ÿú/Î’5ô_Cÿ5ô_Cÿ5ô_Cÿ5ô_œ#h迆þk迆þk迆þkè¿Xçi迆þk迆þk迆þkè¿xó¯¡ÿú¯¡ÿú¯ ýÿÒÜnø Èþ~üÕ\*O³»«{NêžS­º3ž®Î/Ê]î§Ëu¤y†¤üä©{ãÞêÞx¶œƒùÖª÷† .wÇ+”Ï€(u–QpÝYF¦zsxQg$ª{O¥jmé§Î4²åÛCs¥Î5²•‹jï2E½A,U÷ üÔ½òt5Ç«Uw¡乿ó>”·º•¡| 8®»U¦î˜©7‰ùò^À?½KÌTw£jÕ4VÝ7ÏVw¤ê­T¬º3©Ö£õn*JÝ—Ê–>kå;*s~™ªÞ-¨·‹µê>z:IU÷¨òÔÝôryŸÊ\»ú©õk²:3ÉSþ *Ô»,oå§ Y­ióäÞ­xïhÞQðSç))ê®B¾:WqÈù­y§=VÝkÏPwÛËÔýv7õ&2Q·ä©w‘êm¤Ÿ:wIUw´ Ô=-‡:ƒñSw ’Õ­<õn²\½t—óhóN|ªº_ }!˜w¹¼åýxáûÀ¼#á®îÃ'¨û™êþ»¿ò{¢Îdòä}xq_Â\»©ó˜(u>MÝÙJV÷à3•ÿƒBµG]«ÖÊ~ÊÿA²Z3çË÷kbÏZ¼ç2ƒ€ ŸƒÁñàg<ý âÁ:õI.¸ð%€/> 9y ×p …æPh¥~C©Ë0à†7 ¸aÔg¸†Sçáþrê?Bü¦î#¨ûàGÀã`G;š£ 9 š£ 9ŠüQàJ$?z‰ÐMþhúáhø ­1ð=†ú§1ÀŒÇXè…ÎXh¥Ì8xÎqàÎqÀçxhŽ~8'P‡ ð4œIâºIÀ$“L28’™í‰ÐžH}'Ò¶Á5‘¶ˆ &ÁÃ$x˜ÍÉП ÜdÚv2VÌ;þòþ•é¯(S½A/Sï¦ÜÔ»áõv8S½.—oˆÍ3ë åË(]Ý“,•w¸Ì7êþêü:M¾S0ý–+ÿFnêîdŠ|cl¾_ÈWï×+Ôvoå»0Qsg+†µêmƒŸ|‡%ü™w)ƒätÙô{”&ýšï¬‚ägqOLø,Y.ß]‰û“¦¯BwywÌôY˜,}™ïRÔ{«låó¨Tu_TgiþÊçQŠ:W+Pï×÷Ê3nóɰƒás0¸ãá#üñàŒ§ÄÃC<øâáA§> À%—¾ð%Àk4‡ !µrj?šC¡9”ú ¥>ÀÜ0à†QŸaàáàA½Fð÷ê>ØDøÜHàáq$0£ 9 𣠙H^"tfÀãh¾Ïn4‚+¾‘?‹ºÍçÑäÏv>ßçÀÛ|ê8º©üNJ“K‹qàG™qЭñÀßxhN÷ê0Þ'RŸ$~'Q‡$`’€I&™ïÉÀL„þDèMïDèO×Dä7Z“ =©V.W&SÏÉÀM¦m'Ó¶Sm`§€w x§€w |E̓¯ªù¯¯Ð tæ?Íç[š¼7è¼/xU¾i7ßWfªwëuo¦L¾wþÅÝpóÍr†º+“¦üó”Ê÷áæ{è|õÞa¯|ï!Þ? ¿#¦¯`ò Y«îlç©wËÔ'žò:pCÀ7 ¾†7œ²ÃY(}Ž,“>Ká9‘ü±àCÙ±|+lõ Ž$`Æó}Š·Tó›„¡Ž“(7 ü“òÔ³cê7…:M>EÀ’N¶ô ~fÁÇ,ê>‹úŠXÃKiŸTð§Ò6s ½^üP§ùПÏ÷ùð»„rKÀ±”z-„÷Uð´JØ4ä°˜å”]ë]Oþê-â3l÷ògÀ÷àçÃórx^ í…ü½JŒóàZ ÿK€Y.¾A{ õ^š-·ØVÁÏð‰¸E+¨Ï*Úm¸ñ÷úÈ xX­5ÐYý5ð²JðÞ|_½ À­÷† ¹=·œkÁ¹ ĸï'·ðQ~m²Ú«É1‡6{"lm°Ø ¢¬°%"Oð¯àmƒ°ð•.ààaýa´7ÂÇFòoö—ºaþ»±Ïûߟ÷Æï=ÞÿÛ=Þû»7öwÿ'ïïZóÐÿ‰{ºÂÎfºÝðÝYð/|wF)?Ì{]Þò$Ë;îâ¹és*Aùï,¼î-y¶ŠÛâ®ü¡¤]÷žàÓÔûòr—X.éÊ·g¹zï¤Þ™gKÿžfLéGZ¼-ý§w?)ÊÏg©Šíâ§Þœç©w§ÞÊ7|†ò ï¸Î?Už|“jú¨òSoÒ•ŸøRõÝOù¬NWþ?KÕ[t?u¯?]ùq)So„ü•ïøt忪Pú°2ãÀx+¿ ÉÒßµéï%_½U¯P¾_Ü•ÿ—åß*S½]/SqbÜÔ›¢õŽ=Sù¼*S~DÝ•ï«DåO4[ùÀ*WïÛÝ•/¬Då+[ùĪP~±¼•o¬_¦@úÇ2ß"¹)Ÿ£ÉÊïhžòUÞáWUÌõv7U½‰/•o~Ä;ákÒôaŸ¬bÑäI_ö"Æ‹ùNÞ_ùªq¨w¾Þêm|²zó›'ß' ¡ÍÆVHßõCàoÈ^éGk¸tøíD`†ñm,¸Æó{<åÇSŸñð4ž:×x`’¨ç˜ f¢·ËÛøLéãÆôU(ý‘¢žƒá1…ŸxøL<åã)®xê½økrŠž\$€/ØxM -‡€k¸†Bs(ô†R¡ð4…ßÀÜ0à†g*ýe8| ‡Þ~¿AFPf°#€Y+§þ£ 9 š£ 9 \£à+‘üÄ‹rI0|Iä†ïÑð=†:N¢~c€Ž1Ð ±ð5þƃ8ÇsåÆAsðã_+—ÈŸOÈŸÍ$ñC¹$`’jår#|ÉÀL„þDd1‘vž‰È1Üé?“h—Ið9º“©çdxœ “©ÃpOáï)àÞ)à¯7¡‡S›JÞTà¦"ƒ©äMÇ4p¤P.~R •Â÷é|ŸÎ÷é”™A]fð}ßgRç™|ŸÉ÷™|ŸYð2›ï³Á1~—Ðæ³ù¾”¿WAg.¸V‘^߫ĚGÈýØ#p¡?ŒÏ0>Gi¥}tF-Ë¢`GÁŽâsŒq0Æå9þÇñ'/qÚœOsý"Ÿô:½¬1̵Eæ^ª¬d= k̹¾ÌóeN/söôizžžž“Ëü[æÖ2§–¹´Ì¡Óóe™#¯¶/*óàôœ7=×åüžž×Þ™õóóX™Ãž sÕó^„÷;gå½wÖù*çýçæª«ÍSÓ¿Èÿ5ÕmÎRõ~Nÿ¦:¨~?mj®å«š:ò›fù-¡ùæIõ{e©ËfÖ[›R¿ÝѸZ-²TÆû¹Œ÷K#cy–c_θJlkðWC­\Ûå³»&®MÙµ=Ôµûd×öQ×öQ×î“Mï©ÊgçᬵþÓg©áoÑzHÓ:Øn­ƒ=£ë#9”Ί©…½ k$ÙµöÝŒ®)bÓµük-ìíºNÒ”ÒE2k%åëZIGtÎE¥×bêiNhMì%­§̨չN×Mòi]ì­«iѺšSºNu¶ÖJ «š|f}ÿ,­¯™Ð5þg´NöŽ3uú¤Î¿©³¹]k'^¡—íÓõþSZos»®«4¥ë*åi ¥ ¥¡dÖ:ÉÖõ<ÃZ;{F×ÀÎ×úÙ ­§”Òu–òµ¦RBë¤t½¥ü =í)¥¯dÖNÙ®5–º^öqU3ÛÔæÌÓún­¹tXët¦”ö’Y4[×µ«ÚÚ¦nçQ¥`jw®Ó5Yìº&áaU#Tô¸E;ÇÔäÞ¡kµ„µ6÷´Òö4ë8eëÚ¡]ŸûˆªÑ-ZŸfÑ<]KÔ§5?ªúݦv÷:­=àVšKñYJ,¥ÄR ¯2ìʰ+îlAMûˉ¹_«Àª‚—~;þªé¯æµ[ ÷jxWûŒâ¬Á¦fQ-%jñS ¯ZÆÔÁ±Ì:0ëÀ¬Ã¾Ž|Ôc_}˜ ô7À©þ|6Ê?¼±iÄÆ<6ûð½s²ó°Üîkç`ç­‰¼4᳟ÍÄÙ Çf86Ã~°÷ó|?¸ûO¨¥Œ“XúàÕ‚] }-صpZð鯛qnø¸ñã¦ÝC»‡vcZ‰¥•öVÚÛˆ¹ö6ÚÛho‡Ç ¼;hï€s8ðíÀw'ÄÓIÄщßNigLö]ØwÓE{ý]ôuᣮݴwƒßMλ9¿°Ø÷€ß~¶=䢗œö‚ßK\}ÄÜ7¯–\AüÁSîý` Àsî´€=@„?¯‘A8 ‚9æùâ 9—¸€=BÞGèã3ŒÏQÚGiÅgì(ØQ°£øcŒqyŽÿqüĉ9îPµ„eNkþeÖKË\·Ÿm½ž^«ƒµ¦ß‘õ›¡ßÁkîôð]=\¹Ö“µ¬éxŸœu —^»Éº-sͶÚ:måÚ,½ËÜÏ\sñ>1×Yéõ•¬­2ï)–µTæ*sÍ”^/É:iµõQ暈׌¹J¯~ѾzzM³ÚZfmóÑÔèÙ‘unjÏÈ#í9ðYŸ­ë¨b› f®Ck¦Î«Úýézƒf½Tÿ“ª§©‹ŠÿËRJÕÔl·¨Úüf]xú7ÛTMþÜgôOoð)uS¯3¬êÇçQu=óç°Òèí–0 ‚ZïtAéÁ›úØa­çȘ2|–íP:‘RÔ¬%?©õ7ño£Á±qTÕ ·âÃJŽöL«šŸ×äk-ø<¥å)—~Cæ:ð­¡ß³†¾l xKÛŒÒæªÁg ¾kà`¯8—cS~BÕݯÄw央…*s6·ªƒZy\éÝÔ£ÚÁqÁ͵¨>jj‚ªª ®eõq³>Œi´)M-í~bõƒ»~ûÖ©z¦-<¶à£iJiiµÐç=¢´Ä÷3¾é¨Ò±oÁç~°Šé/æ±…8Üô·ò¼‡~ìBð,åy;yö—OæäÕ·ÿpü´ 3ÆOΪ8/ÃôÛé«&†bI.91>†m6ýðæ±quØôc; V?m 2„ŒCä#B¸4Ã3FŽšmj‹xŒãýÄ>&s;ú[°o‘|Ñî¦Í ®›67mÚ<Œk¿•öVÚ[É_ç´ö6ÚÛÁh‡g;í䦌8uÐNl;áÞ‰ßNâêœWÛß]ØwaßE]ôuÑ×V7±tsŽ»Áï&ŽnrÐ V¿=Ø÷€ß~8½œ·^òß ~}äªxƒä"ˆß ØAÉýäe>´€=öþñ5ȹsp^M;†à0æG8—#`Ào„þ0>Ãø•ùí£ø; vìhPmŃ1.Ïñ?ŽŸqrß¡ôãΦ›$ŸæßÚ÷3kºIkßͬ}7³ò»™µïe>yßË|ïdÞÏ÷1Ów1kš_¿Dó˦õ)å˜ñ9ä$ǧ´ ÉÍú<]›Në±Yf®[ë“ÃKó2ô–•¶ì8m˜PÚ_oÄ.yA¥¿°sAëåk`°/;¡u‚»1¬µæµV°Eé1XÝZ Þ›ñ¹9¡5ÈÝ–<­œÐº óºÁJó÷i»œ±—[2ôfÔÔÁÔh Î|^+ù'ÔTb«Eë4L*á­ÄW@[AXéÖ,*a©onj„‘ó+–24ಠÛæµV˜¬]ÜZ/ ¼B0®\Öša´[°½ >WÑWGž®¢ïj‡ÒÎ*L)aÑã’©ŒÔS/\RS•RüVÌkm!|Vz½ ü×I;ýÕr þnï¶hÍ^|îfŒå¨Òê.©½nYÒ?=_Òz½älöl×Ú½p²Nh}úJáeð:ªXÖú½Äi…ƒŸV°­`[Á¶‚í¯‡ü<ø7ðopl߀»Á¸0JÀ¨Ä_ ¸%Ø•`WBé’š^•á·Œó[&ówpk±+§¿^弞ʉµßåø®„s%þ*Á©§•ØÙi·Ë†ö^Æ8o.š°i¯‰X›Ûç&87K3öÍbÏØf¸63¦ŽÍœ 'ñ;‰Ã?/\ÚèwÒ .|¸çÃ8.l<Äí!fvž%5íl…cc{±i[RSÐvÚÚáÕv;çË ¯8c£àyÉ—<ø÷âßDZOÖ5`øhóIþ}pô3ÖO~ü5ÕìÃm?~Á€k»ð²š¦Á1„m/½ðì…g/}}ÄП 㢴õïŸã9ë'wC؆à"§!ÎKBžÿœ†°cŒaÆ cÂg„ç1ž'à'BŒr_ Þ1xÇà+n ܶ bK0>AþÄ“Àw |Ü,k!™·Ê_æ¾xæ½È™k¢³Õ‘XÓ«{ÿzué9ì;Y«ï+§ç§éùçjsËÌû¨WÛ?ά?!sÕóB™ÊGàâ#þZ¸tCüƒë„‡Üx4Èu ß½ØÁï…‹o‡Z®ÆO˜xr-Ä_”6ö.òÓ‹öqúãØÅi‹ƒ‡‡>8Ç—ë­\£ñáÇoœñQþŒ’ñàDùËußq8Fá'ß!Á Ïa¹†w‚x†8wCrm“k,q'“À_tY].bØ$à”€ObBi©Š†âoe^ûÎõý¢OÂ^ѯzï¹²OôaíMeýæì}”z†éÏÔêžÝ²/ôËö„Î¥ý s}/èüG÷ãØrg­épά¢Ã™­4à áxÉŒúXÍáýÃë&ç¨Öâ$æõ¯ŸTZŒ¹çbŸK.ù»4_i×] ‡KSê£xƒMiqîsüúhγ(íï¼Z“X/ç2p/Ãn#Ü7Âe#\7‚måù&nkñošÖÏpÞŒ¿ÍZkÖ8lá¸(_ëá-(mxÑ3·Ík}xÚ.gìåàÛµ>ü:¥‘w#ùœ£|xl¥m«Méåm¥m댚2ÐV·ò\°´B·“¸·ql»mpÛÿBÆVÑ^M1Ûˆ¹ö+gÔ”C4< + õ«°½jFMCLýxp¯žÐšžóJSZt2 Éw!8ÛáxÃ:¥ÓnjbJìG´;cŠˆ· ;kBkcâk7üw;´N&c,ØXRJÓBn,`[–•.»Lcö€½‡±{»Ç¦µÙÝjj³—>'X{ó´.;9´âߊ+¸Vp­2׆s¹ÌÙ±-ŇïJÆTJ®àl0¦„ñ%Œ/¿»lJàW ŸRø4NeŒ+c\9}0Ë9·•2ÿçõSŽOí.øUµœJñEüuœ«J±Å—>;}vúì`T3Îjø:x¬ÃŸÛ^0«ñ_Íq-˜µØÖ‚åbœOæù“jzd\=¹¨§­.õÄQO øtÁ§ñ!x‡àÅ¿ƒqYopÞ¼&äÈAŽøj§ »&pšàÙÏ&ü¹àåÁ¾YþÓŒ}3öCä: ¦“œðpÒçÏI¿ÿQúÛxî¡Ï%ëÆ»°qIv·~^8·’ßVx¶â¿ ì6ÚÛióÂ#~;ñµcç…“—|ˆÍKN¼ò?m!ú}ûeý!mpóÁÁÇx?}~ò Ý­Ÿ¸übËq€|ÈKÛ¶üà€w@ÖEð ©wIMYûÈS<ƒËjêÚ}?Ç!pûe=ç˜Ã/Ä9 ‚K!ð‡–Ôôvœaì†3 ¿Ï#pp#B¬xFà£?ÿ>bàÆÀ3ï¸ ø&Èk¾‰%5-¾Y.À2w”¿³íûdÖ•õÍÙ÷YùõôÞü×ôc³~}ú±éyïj÷(®ÜGÊœ§Êuµ}¥Ì=%™k®¼ÿpµ½¤ôœRæ“gÛSJ߸ÚÞ’?ë½ûKƒYïý=ú·üéy\æÜíά÷î%ù²>~=Ý<¥!-:â9Ä‘#ó 8ä‚›ËóÜ%¥7»ñÈÙNÆî\T:Ï;—”¶¬¼u6âÛ Æ&â-Âö&ÆÉ»-ÄW„ƒv> ù¼¥Í€ƒqåv¥am×f)ñT`WÅcÕqµ5°›±EðÜÍØ:°«¤Ÿ±UÂ…Ç’uê­ZÅølWŸŠõv­ ÝŽM)|ø/ËÉ5Ê 'yqbTNî*kçßCüΉ“Ƕ)þáÒO;cÚ¦Õ©«¦ÍC¼]`{äs ûžwÈu–vñu-«-ƒÛðÛÁc_A°†ãß)×V¸…Ä/¾BpòrÜFœa0½´‡9oaìœ×!üzɧ.qiã8 §0cýØúˆÛï09 ~Ü·0>ø‹sçyTÉO¾(Çaž‡àåxˆ±°¢´E‰)Ÿ>ô'ÀO¿¯—þ¯ð)½tÙë’šÓf””Úã—Z¦r¿˜ÔL‘Ïùm¿Üc&RëT>¤þ¨àÉoíåûù~Aî­“ßÔK]QÙ “ßÊK Qù®@öÅä^-ùnA¸ËgˆÔ;‘ïä³Dj²Êwr/—|~É÷ r_Ü/'õV廩C Ÿ5² c~Ï0sæ»Ñ=Ù§4ëÎd©ù–Ô›•ZCæïލfm›)U¿¥cJý¾@~ ŸRC]æ2g‘ßMÈ\Bâ7ëÑõÂú³pB}ÿ!¿§’ß>I½•_õoï;¶ScSòÌøÆ³7ÝÜôí¢âŸe×;·ô³Ïì»î¹X<}<÷ì_þù_[*þîµÑHöëûW"%ŸÚpêñì+ÆuWzó¹¾’¼}Kð¹»ßZ4 <}ç¹·¿Ÿ”òów÷ýíÔ%­ÆìãÿöÊ‹n4æžhxöü?VüZûõžâΧ“O]”8¯òÕcë…9·Z¾Y–<ôð=·w<5~OñgAásÆ^زþõäSÙ_ÝñÅ\KòÐÅÏßõðÀ=Œ›Pã>ýÀÂØ[ï³wßuñƒî4æ®ÝrrÓxòØ/ì¾øÕ;’_zmøü”{õ:H-í¸õŠï2fïz¡íg绌¹ì#á·O=Tü‚æyŒè.¸÷Kéóš<8»éó½?¹Á8_ñGçÔK±»oÎY4fÿìË‘‹Ÿ¼ïtü'ßþ}ËÝÉc_»ó–íÛ.J|í¯ÞúÌÀ"ãÔyMMÿøÀ“?ý¦1{è[sžÿãäR_»?ÇŸ<–úÆ;QhITy¶Š«ó™zô{]_ÿÔÛgÎÏ-}¿ûÙK;}|r¦äÅæk«‹O¦_¯,ÿë60>;÷“,ӵɃò* IóW÷Ô}_Ÿ|þû·¥ÛÏð?þúñ¯ž~$½qÑ#o>ñ#csï±`ÇÓ¡½iÿà©×AJ¢MœgÌþÑ?Ê+À8ùÐß|ëæ¢3¼¾7õ…«î½ït^õyÌà¥^)÷Kù?ºt&Þñ×¾ü£…7ÏÄ{Ûä‹ëmלá'oƒ~ܸ\á$mM•o¯<õúH]óàÿ±÷`Uß¾l¥¨TQŠô^é2ر‚£FbEE%FÀ†€5‰ÆÄÞ°##Ö%ŠÁ&*‰ÑØ’˜bMŒï7{fï9×ëÿÞïÞwË»÷é÷sff­5³Ö𵦮ܨõíIõ„¿¬?éK.tü±¡M¸¬ãò<[·)¿é;”gò!ÿñÍÍEB„<¿úè§ÖóÈ¥ÎuNß¶“gÛ Žþìôg*“yÛäá!þmHuJ™÷ ÇFäRÐPÇŸÊ:ª–çË9%ò7ÝŽ&£<ã¿<.Ì´ME ѾdÓ€ co‘K¶ã»5z\ ë ãýIêÕdÊ‚ÓBž3™<¾ÝÎ~;ä2~[Ät‹Å¤únúˆÙwû˺Ãn·o49-ç oÖ2Ü|”gü>|8òÆÒU;I5m¥S©¾>} ø,O|?bƒ$ç¼{ÝØ7|Ê3¾.Z6ìÝüHuœîöäBª-ÿ\ ôŽëK”Ê6ùê1þž_u²¬ðŠè¯Ö·µÈÆøZZ³?¡¶±¬ÛQ¾ÜÜÃUí'Ôc|<<¾ÝÖivŠþ‰¥ñ©Þd5Õµâ\|¹Ú?T»{Ê9Êðq"“ñõ°ÒMMÞˆs¿Üß™T/;ýꊅ¬›ogÚ¯üªÚŸ¨ÇøËºkœÀü°ßå ÛHu®ß·Ý¾\,ë2·5ÌÝ0KÐ;ñój¥¯‰#Õ ›fˆúS[Éy¸KÖ}ppÌGÏ¿‘sèènŒzŒo‡6½ç]/HÐé=ª»]X"©aÖá’Ëf!“Òozݨꉰ7Ó?Mó©;U[-ðú͉2™kFª{ö™7Æê'Á§1?^y¡­§Ñ†ü´é=!ŸÓ¿…SE^¯¶|7:ØÝí” gH³_:_è/gÿI6Ýøk:ê1~„4]\ç‚v@Ï]!Õž;ï„>[+ðšQ³¼ä¾œ½}ÅßN›w£ã÷ÁÁÃrG|²Kôƒ{}k§Ü Õ…é*ÞÑË»4nc.ggFušà'ÆŸiŒïÛ7}úìŒè‡¢qïÿ>”T=ÐüâÉ‚ŽŸï\’ä&g‡×»ß¥ ‰§198p!àùà…¤ºÑÎøôn4ºªn%°|tOÖÍ£zPÎúåÛ/»o1tLgòp€Û¤Ú¨vKŸå¤êBôÇË8Ⱥ™÷¢ï_ g=v<器8=3ÝT½ìúèówviôW|Ü`òûBÞGl)ª÷YšœÍÇ+m˜Îä`åíÔ6 "IÕ“»¾7Ž U;ò¬F¸(Æñîc윓¦ÈÙ{íS¾o:õ¿÷¿7£ÙµžŽ¤êA†—á)Rµê°,æ²®Ów‹ ÙröîZÿYuù(Ïø¼ï澨/zª~ÇÜ|]IÕ¢ûVÄóÊ'g·YGÙw}ñæúÆùEl5zß÷ñvTUŽ*0è}‹TÍšºkxýY×Ë`æ¥6­ˆÏýá¬"ö ê1>—jÙèó{Iuwìg“ªqûþ>wQØ•6«B¤¤|Í®dŸ¸øÉ±?ôøÄø\ßuÅÖXÁßý+]Rí—“ªñ¶‘_Öü>ÿËÒ³ž ˆ#ÓUî…Ü|Èø¾—w—7©|AÿS_Ö|s¾BðÁ¯þ°“I{4»œ}°ï¼ö½ˆàç‡L.ö¦ç·û4»>©Úð׿ŸlIUT‹]3Cv鼅 *º>+g·Sl”güßkúéÖ§‹~!UëæôÃ÷ãD»|Ïl¾2ZÖ%½7ÊÝ£Bήw¹ç•ôÇŒÿ_í¸\X¾£©ZûÞOGü-äÏis±nñY×Sa”6ndÛ.ª}ñ õ™<|EÝš9¤ê‹½{ÇDàµzt¬›c¾èG.Z?2ýãÙ‡L.¾jÝüˆÁbRõ©ƒßç!‡IUý'Ÿtœ”#øË쮜MÍÞqØ›™\|å˜z©_™ é*´ìˆ–¾ø²xÅ(çßãï©p¸œ…2^¯ÿ™|ìy4atûQQ‚ŸŸÞû®xÁrññžnãĸÔÃê›ÓŸ-íJ|¹ºzÂGBÞf0ùØSÜ 3—“¤jÙÞ6]ºû“‹?,¿_*ä"ÑÿîA»/…\¨üÁäaO̹ßL)%UŸÝÎkùGgrñÎÁ¡Í>2—uñý{¯h$gûæ.šçÿs“‡Ò]Iæt—HÕ’³OGÞ$oú­úáYo`ÞCÁÇLJ£Š»|U¿ úlÒÑeµ!¹xyÖó=ÃÞuÜâÉÅzÌïV_§Ö· †¥¿Ê/UoPŸñ¿£‘‹î…àÿBÚaSÉÅí¶'R×I‚ŽF!cüíµyœff0¾—t £ž8©Êûú©¬$W„Ÿnè ì£CÎî{®UQJƒ™Œ¿»k…¡†TåNG Ðþy·J'ÜØ#ëìë½j×f•œuíÖã_/CyÆÏÝ;î/Àå½¶~\:Œ\œøØ¸ò…¬ó²?rh£µœE‡Åﯠ<ãçîIŸU.ž9tuzø÷3ÈÅ!%?ý=ù ¬cþ‘œeôó:Ó­„Ïd|ÝݪÏäõŽÝŸ iÁmäb·uG#…}éUÔ.+·§ÆŸY«¥²‘?€ß3¿‹Ÿî«Ký;‘T-B5’`İÞt®èסCÏlnySβQku~=“ñ·øÀø+S3¦ þl¨‚‡Ý]Ó¯‹!|²ø·oeÝ0³Å½§ý©éSÖ×Bc¿Æçâ[ÁÃ1%UºÖ»>ÝH.º½2Yû‘¬Hg´röÄ_ÌnGÀoŸÉøYŒI†g󦤪\ÉE›žM¾ìK*¯„÷ùbèIÑÿÌNÉYk#/è)üº˜ì¨ ¾ú®k'RÅýµ}•‡,i»°_–ìýÛd—œÅí²&O19ØÁýê9wg·r£qƒM‡&È:Šˆvßþ½Hyjí¸G&Ûßëå7ô&Õ&?Ýîšx‰T~Ü‚®€`<£Ít–³™~¢<ãû¶ K+V'À/u¨¢3R™yvÅÔÛd]e!FÎöè2Ôý»ùzô2þokº¡Éïš“jßðí»žT“Êá}ïÿìZOØ¥­þŠMzOó«4{4‹ÉÃV8gi¦—1¿RÆYRÙsÞ7É4ãRß_\®?¨%î*ÿ—Ð ×xáïÌbüßrrÚ¦=æðË•”T’òš,wýÞ·Ó;¡ 9b~šè·YL.¶tùãQMûJá/¿·˜Žä‚ŸÏ#?þ~ž¬ëM eºœ ïç÷ºHÑ/³˜<qÿªšNƒž¨xH%f§–ðã:øí†jmÞŸcö"Øx³©^ÿ0y(šufIŒïIR=Ÿ¼8RµŠTšÍ9œÐó,ªžîÊ9ž¡Ô@yÆ÷¢æAa¿üú)©þ¤Á‚÷Z·$¹ß»ok,ô ³ØÂÏÊ9Ž^yéÑÍä`óAÅQ$Õl]\8“äܵý:Ñá9‡óŒ‘sÈ(º"€zL6̱KÄ|ýr׿—_ ë¦ßÛæ{[óÇT•ÏcE¿g19Øô²Å« Ä{b~Qt¥û¬Æ—È…É]÷^¸#ë_Ûía[9‡šÛŸ ãû&XmÝ9‚_»öÞ³x>QK_èÐù«€©ýEû­\†Ù¢ÉÁìúëvg$éù¯YL65+²ó`A›‡“ ¶aK·µ³ã‚­²p'ÖŤásÛ;Š~Íbò°ñûzéSºÔ‘êÓmw^ŒnGÎ×UµNjrWÐåª ¤rus7îC=Æÿl=–TÓÕ—„r~×Ùï¾Îé'낾<3³Íb] ‹ñãÉs òƒVŠþx²…øš çsfW¿»·ƒjÏ´u°œàé{ZIzü`r°ñسg?ë_–QƒFݼEο3ääÝ+!\ŽÕù@Ž2ì÷ÑëO&Ê,,ÍŒ\rÿnÞòVvä|Ä ÝÖ31>\?òãg3å?áA6“‡ Ôª‡Hb}+l¢Sïs×Éù&#ŽVe6ãËØE_ØØ ~}p˜|l€uîúÍZÑŽD»Gõî#çž&®=C´cÚ‹eßµÜ#ÏîPPUD}&ëoOމ/&—ø<î\MÁ¼ÈC².+7ù‹ç'ä9TÍB÷£<ãózÅÍ@.Mùàâ°ðÙÞs_míº|ÎIY÷Qá«eˆõélÆçuÔm´H.ÍŽK5\mOÎ-Z?eIà§B>”åñáòìš§Z6F=Æïuœ—>¾Ñ§ta,97ì‡mwÖGÉ:º¬±áU¾QžñuñAiýø`m<º´r%,N ÍîkµÏ%ãÆ"uBóãT½Õä:›ñuí÷)IÝÆ×ê_Ú¹¹Sñr®Ï½À¾Ÿú)޵X¿Sù”Ãø½ö‹Âû«Æ?t<üÞ…¿‹ÉÙoÆ8gŸé$úÙ!oyõÛv#AøC9Œïkßi—«=¹¤¸™iÎîÕwÚ®¦‚ï#{Ž+¾>[ž=gÊo7ŒA}Æ÷µêºåù# SÈYê=ùcÜ}2,ioyöO½Ð‚©(Ïø¾v𓾃zýH.}Kõ<9;ÕãE~×ç²£Óì òœˆ²ç»CG¢<ã÷Úøì_žýö`…ý¸k4y?;¸Í‹)½Û©|û c½äù£ }Íar°Ö¢']™ÙëéŸ »Ùüþo=|"Ïeóö„sÿWÑe£ðÝàj¢õÛée½–ÍȽ+ëØzž<÷ ºPó.ê1¾¯VÌE²àç¡gLªWOKŸN}÷vÚFcÁÏÈ—ã®_Õü’¹tpÔZÀcò±ššÛèí‚WþٳJÐÔÝqD}[1ްu#!—Êr†·àã&«R¦Éå_NÂSr"§þîzºvǽ-o09NÖíˆ_¢Í;æ«ó¼¹L>Ö…üõ¬Ñ±Hѱžþ²z—TÐ]ádSY×íhòD“æ÷qxÍ~aæ2yYwtú°Ié7É•^—²\&“ŠCËg óþYð¡}ëŽ%cŸÉóغêq€ÍGEñ}ÛŠYÍF[[÷ýÄæYš¾hú9—û Š{å,Ú17lwƒFkéŠ6‹Wœ\f$øÉü}ÁÏzK?88F̃æ29ÙÀé¹²¥Iäê-Ú:©09f¹ü?Ë©s?ücë]¨ÏäaCù€FsO+'¿ÏIØ›ENž9û[Á´'BΙÞ>Îe|߀Ö9?#WnNï1}o?rrYëU' êiëa¹›¿ÞÞ©Ê3¾oøãÄÚÃÝɕߕ…srr”ã‘n#Íd²<ÔCÎ%‹ïtüµ_ƒyŒïáUa¦K®òyþÉÏOÎýÝÔ«ÎWëïÜ>ʆ¼X'˜Çø¾±û÷‡Ì9C®66lÑvÂä¤åþ¡#_ŽÐÖyr§µ®5t/Ê3~o¤îèrU9&¦ñçĽůÞ~Riv-—º Sûp¿7)ÇÂ5~\uIibýáÄq¯VN?ÿ-䇭 jrœ‹Y~éÞÁïyŒß›0È{ì'W•ƒäÄÊË.­œ!ë,.dÁ$˹¿VSOå7»Ó{r•Ëç‰É¯RW^X äžÍ_äÜC#½gt8‹zŒ¿›•í³rµWŸÔ­Ã‰~èòIfEÚ~î’?WÎ]^rïìÞKb¼™Çø¾ùòÈ m~³!W_³‹'ž9çÓÖKžÓ oµ¶Î‘{tCŸmJx0ŸÉC¼Îq_Ü&W§?I¶ò]òõ­A»ly_.áû3ª=]ÐЬ²õŠOQñ¿HÛLrµ°mÍÑ }É×t©½\k´T·Y^ÐŒ.¼zˆñb>“ƒ¢»›î¬¸D®n\ô~‹m]É×cNtšºðŠ\¢,‡9˹Êöü;(Ïø½åÝUÍLV’«ÔÝ*!_û5>ÙÎÕR.ùÕ0"Ö3CÎôj;\$”g|ÜR­L<ÈÕÊsö|{‚¿ÿ™ù„Õgä’g‘¥ë¾”s?šQÿ»/‹þœÏø¹Õ³æêJ³Q?®ÞºxÖ²ñX-}|ǹÀößnre@ëWj¿èµ“ñy+ÕÞvFäêiÚŒck4èødÏŽf›ºÅ?h×k„÷:c¹äåd:£•Ðݾ™FªüãóÖèéÜk”e»î‚®ø?.éò«&w%—¯üÕ>XÎkq¿ÕÒ!Ç…_‘Ëø­¸­^“+e7ovð»“ÑŸ[þü1=_ÎëJR|†òŒÏ[þše6ƒÔ4Oñh4H£ãØÕoþ˜ÿ×>!gÊr­&gy?ó9sÑpß·¡R*H·ÏÐÇËW‘cëûÖ‹ïí&ëŒ{{Ù9ŒrŒß[T­<éyœÔ´\32îƒ?ɱt¯ÏO<&ëèrë‹å(Çø¼Í\¨H :¿mózäXHä•·ë²r¼áÅ9¯Íu¥ß¡<ãï6¶^Cj:5ë4ÇÝ’ý-j™kÛÏ´uåP —HÈûO3&ònúŒŸÛؾ®æ‡Õô!aÊFŽ–Ê‹Îz)—ÜLl±q‘fW|20=2UF}ÆÇmüœD͈„³™÷ž’£™Ã?Œž.úïæ3·· üp6»µŸ!üÊŒÛ[8@‚¿'5|y”ó_kÝ~ÑtTÐÈŽÂZÀøºÎ"–“¾NÔlìÒ³•¯=Ê6@9¯gÓó †d ããö±ç©…%5ôTDÆ>räÅQøéq¢å¼ãï—f¶ E=Æ×í# û_Ì r ï¬Ï©yšÕN›/ô«¤ÆñFѲaÚv(ÇÆlIÍùŽ ÓO“ò'Q-þ"—ñCn‘óÙ<å™ìä󔚺nÆK,£Hù¹Ú¸Ž5‚ì|‘œï¦lÜ£“å˜Vl©ù£³õòÜg¤|é÷é‚^jf6>Óü£¼ÊÔgò°óÈGû1„“Z¦ÿ¤|Üô~7¼å’õô‡£Ú8’o4ð²õÃt1Žå1¾ïŠþNóí&¤¶ ]X¾¦ñ¿\in=úš¬ù°•œÏÖPŸñ}×á)¥Ñ)¤–Ûq™+¼µTÐÏÖ5úóé2fÂÇBóÿ‹Ym\¨¥Ç2kiyîÕö«ûiþU‰â^jþU>Ïzãt“‡âTÐ*Im+º3JäÆÕtÅJ.©[}7-서w) nÕÃò„ùŒïÅ\kc• :9ü¥õ£•ã4“orÞÖ˜‹ =F=ÆÿⳊhò^Kwó=®“Ã-ýOµ4Ôö¡K÷ ‹Ð#¶ÿ"ä/ŸÉE1Û·ýÑÕùûvý–‘CtX?vSÛàþà3?©õC>““ÝÔ‹ž)öûk“çK–k£È¡Ì'ùß?Ý »®8ÊrAëŸ3ÈÔcò±{˜âPjrQÛï~Ej?í<"9ärüȈÂDÿüSp˜œì¦³Ï^}~zªsæ§äàñˆ¿š,û⪿–¯d÷×ë&'%ÜîÖŽx™wmßc®ƒÃÆšëg%ëêmK;}œ¿_hPÉAÉLÃ<§??!µ;uˆú¥9ðb·ù&¯ Ygzoá9ÝL9ŸMxPÀä€óY´›.+WéÈWYúÛbYgXj1rcOâÏÏåS+5xš:®“ ~G´›n{g}K¸ä™Þä:Úµ¹®Žg¨Çø¯[÷åÔc{’Ú¿˜"ý%3äñdš\rÏã7ïJ_mý+ßÇÂSGá‡0~ëø:qíbº°÷7ÙŸxµÎ«Ãm¹äçaÀl¤Ví£ÚÏŒï¥? ÿ¼þ9!Ÿ…ÞÚü Ùwk™ÍŽ?«þ eç…ä<õ¿KçÐZÏIí #Ⱦ‰Ö‡7ɺFÊÆ¤œoCü‰òŒ¯¥lþ%ú‰Ÿ‹ÞkbsºÌUÛ×ðSåœôX%ìXãó:{´þœÔn¾¶á¯ þ¤ì÷¾çÿ¼UÖÙo?þKî>õœ‘œ_®´XØ…Œï_±u(RËí¨JOY‰E‰ÇÖk²ŽÑ-ç??á×lÐL!Ÿ ¿¿úôÓ¿¯´!µ‡.`fØš”MqÈ·JX!븟¥ò­ ©2Àèág|ÿêg zòRÈÝ9å›–.‹}êm3&þF;“Zm\-àûšÞ/dr°·%u¨6‰~åçŽ÷¾ò¼~å˜ðÃÍënTåž— †®Ýsò\ž§…LööWn“ZæÏ“½h1Ó§‹¬s¤Ž®·æoäØhâ¶Yðe!“‡½èÍ¢+ÝIíÊA'²w’ï±Nwωy߇. îñ@ø ™\ìer úC9V×ìmѼ8ú Y×xÖãÙãä‚zmçÜ<*ôo!“‡½ôXú­C¤–®Òº—‘¯¾.ά .QõWÎçûXÚüøc&e‰‡÷”\º$úíî“­ÄúÅW윭è?~^Eã;xL.Êøy­|Ýí+ƒ˜²¡%3Åþ2?‡¬Âñy|tÑ _±nð1““}N·ŒvÔW¨Û»uÙ³j’SPã±Á×§Ãø<úŸÉýÇLNö}pgà}óuäšqšó¹.kÉž¶­—„žè&ÖYø¾·W¨Çäbß±|:â’k>ïðÕ±)¤ôa…íUoÈû{6 =6l‘óN),PžÉÁ¾—O!Bäz&ˆ”®¬ßjƒ×í¼£æ~Ìø¿?hðwƒ6Öúí?'ª¦KûÑ…D+q12Nœ£Îß³ò[çiiBO?fr±¿_ÿ?1Ñöw®Yü0mñÁ~ÚxWj·ýþ®V·dݨ5ÏMÇî×Öe l^6ªŸY.úo“—ý=ÿ®àÐáx’Æݵ=Ǿ8³^ì_°ýmüår"ôe“—ý›~|t¹ÖøðÍ äŽF—nÍÕgd¶6þiöçôg‹7º¨G““ýßîîøî7ðgBŸ®»wÝð•Qyù¦²Žnï=Ø%ìÞ"&ؾ¹fæ3õçÞDçUÙ㽂[â?O«Ù«ELðýh•ÎkÊ6{Ròhlä¼YæÚ<]Çð‰ñ“‹ƒl?JðÙèxñªå×µ~,açØE?.V^hûÆ|>"Æ¿EL~²{ä]>XýR£¯dñä÷j»‹{:þ…)¥œ¯+ŠG}&'ù9]uÝ¡–ÝÏÑè,ím^ÿ8tô¡-Ñø›ßŸÔ·òWÈäåPôgôD~=üÇ—'ˆövãÚÃ.b\X¥Üüfç«ÉË¡þ¶ûΤu&µ·[ôOûr) užû²Þzõ^€¶Èýdá2994©íOÜ µl_”8?kèrJハ2•óéõO_¡“—CʲA;RK‡Ùƒâ>P‰â–§‹ñãz@ü¹Ð#.÷¿ ™:uüZ¡Ñ/¤³+LHI£‘.|‡úß&Ž©úv·æ7kv¢ÉÏa“úíô%µüÜeIý'ê Ü%èW¦ÛÈô0Ôý¨Çäã°2½ "µ[“èI²ûOg‡_4ãçɧӟ$ ׿‹LõúÉÉáùþ† ;ïãñúñA»‡‘Ý÷v5Ú6OôCÕ¤ËûKÄøÄïíhz»˜ÉÇae9~8©ý\Y@!»/gõaoYÇöyå|ºjä¾åÿåÞëþöþ=Iv—ö+ms @Ö1?ZÎÿ¥ó¸î(Ïø-¿vî­v¾â°ÝùÊ ;~MG5¿`1ã9úÍíΦ#´ôî¶nguëD> ÷<ŘOÒ¯'È®×ÚÏù$Ik'§D?-aü>¢4ߟÔÒmÍm¿“]ã–é, FhrÂÇaa—0¾Y¡L€H-ßßÜeîÓ+hÍK•NmœËcóYÔcü=¢,˶'µ-÷¼ûëË›dçúÈ¢†ÏÐÚÅù‰òŒGŽ·<Ýó|RËÏîŒ ½9¥ù!¹tfÖ»CBîŠùÎRÎ?zü=冀£A;®WíüØi„¶®WÊ×¹Ôy€ÊM¯–2þ}m¸–zÆñdÇ’Þ}bî— þ*fØA“ë¼YG~8Dô÷RÆç£m˜ÃÄ‘ZÃ÷ÚkvpÇ»•~™á!ø¬l¿¦ŠuÇ¥ŒÏGߨ`ðçRÃÎktíèЦWñ¦¿„ž)Ú>Èæ¥ßTq)ãûÑõ;è‰NRí¸~ä.ÙÑ®‹´íâ6¹ôëe=F^+ô|)ã÷ÑŠ†“¶êDjè­§ ÉŽ~¿ûëu].½¥Pí+Ê3>U–²H ¿÷¹ƒó’÷4p»{ngužŽòŒÏǬ”ÄbÝs¯aj@aÙ¡\‡®É㞦ÊB²°£l 9 ÖÛèš¼Ç.Œzärþ”@ú¦áìßÿ„·ÿ'ÅÖxûüm|ÿ®øô_ï—»ü˜3 úl™5Ìý§ï¨ñ˜Ç>üÞ\›ÀUﺲ×â¯áïöºò¸ÇEüÍÀ0AÚ4˜ mŠ´iêÞ¢Cy³gü-:䛞ù#órÛ}ßÈ„¿C—ÆÞki„65F~ã 󸆿E—Àc Ž¥%Ç%ƒ½¬Ä=NäoÑÕèÅ&ÈboÛXò˜ÇkxÌc¤myÌãrÏ,Ç%¨äq ¶=vhŸ=Úo>²Gy{”·Gßi‡½Ç™z±v,dñA_³dÛ¸FïÝ9ä7¯àñ2Xì%žq2;€þu~Æcgòx¨ë Ø®H»ñÆ®<Ö@¹^üâ ½÷å|øûr¹ü}9ÀðHaoË)ñPß0=3xÌâ:¯0½@ƒ×]§8½Ëx|b´ß'—Ç%ß‹å}‘ï‡6ø¡ ~e<Ö0ðù£½þÀXHä²xÂÈþ@¤+xüà7¸œ™àd'¸†ÇNæq‘õá1€;´·´dñ'è›xÊÛvÉ,nûØáH‡²7|è{ÏJ܃bfR”xÀ<° wX‘Å<°‹¬¼w‡t4ÒÑ(]ÌÞ VÞ¹CÛb@[ `ÄF,Ò±ËÙ=ô½;o..ƒÅ~k£ ÞÚè,ƒ·6ú“¦úšÁÛýˆ¿Ù–j bA_ !“†à›!Úný4‚Œóøž!ü­ØBþŽ>ôÏåsÙ›úZü à¨_Äß‹õa1@FÐk&H›€¤M‘6Óåü4öNšY1RÌ‘oŽ|sÀ3ÆceჾmdÉßS¾F•FhScä7Î䱃êøk‰ü½XÔ±|ËD½÷b xü à·ªïÅ*ñƒoãÀbioÅ"m›ÌcUð·b3ø[±5ü­X¤í@ÚgöÛ£ìQÞåíÑÀw@Ú¡ŽÇ ¼¦YzoÄ"í¸œÇ }ÍRôÞ†þæH7G~óJþ.l&{V‰QŠÂ‘_ÎÞ×Sb1d±XDÀtD&‹ÿ@ßÂSÞ£¬È2fj¢BXì(“ˆšh¤£Q>ºŒ½Ï«Ä"BÛb@[ `ÄF,Ò±kX,å-ÚDÿ4ŽŽMԖЪ]Vm0µ¿Ôöªv÷u[«ÚYÈŽb_Uû©ÚJj)Ô¢ Ší£vÚ6};¦Ú/ÕvéÛ*Õ>©öHµ=ªÝ¡öFµ5ÔžP[Aíµúã?ßß4®«ã9Ç鮎Ût¼Ö«ÕñYÕ±˜Ž¹êX 9PÆNuœ´äï>‚~CðÊýdÕ‹â1DêøÛØümìLþö#øÙ|4A9³4MyÛòÓ¨ˆÅ5SÞ®®ao=ÒØe6®ìÝi[ÐÖćÇ÷ÊÛ=âïJ'³¸ÅJ¼b:& ^3ÈEóö~£S VÎÞ~vA¹™ì­<ÐáIu´yãwoÐí‡ú~øöpäû£ŸýAp ¾ƒLX¬ß`à !ø-eBц–®,¾I`„›ðØ'ÏX¼“Hü :¢P.–Ê+ÚWÁû–þû·ø“o}É·¾ä[_ò?Ηüò#8¿i› ÷J|‰ ½xÈ7LÅ<1ÝFZæò÷z-yÌà·ºËÞí¥oöZ‡u ?™ÆÞﵩãñPÞåm‘ß}Ð$“¿é‹úv!,v½]1‹?©Ä°GÙ¿=úÈmr@}Ôw¸Ë㠤𘓀ïzSx¼É:oø›¡=ÍP¾9ð7¼ækX|{§[À©œÅpFž3`9WðØ’è¤]PÖu]Ûmw-ã1%Q¿E–^ô·Ònl¨uGÚøÝA¿{ ‹#à~x ¾ÇÝ×bHóø‘øÍ }àUÎcF†w!‹Icø }ϧ‚ Û¾à™/êû¢¼Úë‡6ø¡¼ÊûŸ?ðù±¸÷€zÐþàD›?é@ЄúA€„úA¨Œü`”Fý`”A‡~ʇ^(p‡æ²xâ¡H·ôa1Ci\Ë0Ô CÙ0À ¬pÀOe±H©ù ±¼”Ø–¨¼¤#²XÌ0#S‰i‰t$`E¡ÝQ ,ža>‰F^t9i‰vÅ€ÎôK ÊǢݱHDZ¸—JK´+øâèxBíýGí/µ»úþ#µ¡”™ÔNêÛAj©½š_¨Ú0Õ^QÛDíµ7ÔÎPû¢Ú’×í‡j;T;¡ÿŽ:µúã¾sý®ŒÙt¼¦c3—é˜KÇ[:¶¾>޾>vÒ1SõûèüZ騧Ž{tœCIe<ž.äÂȇÅÑ­ßLЇ&èssôƒ9è7§¾Gðm±,ä±HÀk«"s2h œö€ç`ÉãÅ.ç1@×^‹¥áKçNÔ·ƒ. NêN`· a1Ch¼0üŽrái,v}¼õá•Èb™Df4¾cð[,hŒE¹8jkÞ®¾õé² ÞútÿÝ>Ýäú çiÅbHh§„²†®<Žúݸ ?F™zq¤ ïõ_ý^åQß2h >×ð8 É<–`ÖÆã‹#Ý œ &‰<¾x…^|qà0LSÀ4fH›!ßðÌQÞ4›—óXR€ßø‚¾F ·Q2-^þZ,)š< ”±HÅg -ø–Ð;KÀ°¬ã±Ð+ÈŠò­‘¶F¾5ò­‘oƒ6ڀȊ úÛևǽ¶H7A~àoRÆâ5ØEñ¸âe<ŽÒöè3û5<ŽÆ<Ôw(â1¤Bx )ä7Ô÷ýžhŸ'`z‚Ï2 ¿ye±˜àJ<(ÀôMÞuìmúƾàù‡/pø"ß8}QÞíõCüPÞ4ùƒf´ß°ý/ô€žôOÚˆòÀˆt à¡ÿ‚?ðƒP?õƒQ>ýŒò! ?ðC¼PàîPàEºe‹‡AcU…¡nʆ–0¤Ã+<ƒÅè ñ5h|V%fÚ¼HGä²8¯JÌ*¤#‘ŽD: 壒Y|X¯<éhàŽF:º‚Ç«îÀí1( ü±™,Þ‡·é8´-uâê¸.Óª¦¶™*Ù붘Ú^5&Ï?²±ªm¥ Ó·©o²£¯ÛNÕ^êû‹ú6QµƒúöO]‘Ú:}§oÓôýÊ7Ù/}›EûƒÚ)ý˜Îoò1©Ýy“ÍÑÝC팾ѷ/ªm¡vzðÏlµ Ôü#; Úýñ_­RóÕñ^ë_ãã¼Ée8h,CCȈ!Ê?FHG=ÈŽñŒÇÌmÓâûAîL /&É<®~7E»ÌÓ uÍÓeBVBîFCÀn„ß›°7Ç:b~²D¾ð[A†­SñAÚåm w¶€i‹ï& ø€^»kpìéXH}æbîºâƒ´#ÒÍ|Xì¼æàus´Ã 4:§Úå ˜.h“ hs W¤]A[ ð× 4»>7:'^ô‡G.qOàõ~¯\³4x£¬`ù€nêc£¾/êú"ß4ø¡-~à?pø—3W34ƒ¦2¦¡€Š:-»%ʆ¡N¾ÃQ?<“Ť±xZ¡L+àˆÝ…,Ft$¾#ñ•ÂâFÓxvÑ€Mçz&<~~‹þØ,û'ŽÊ1Õ}ú/Jïo”Ul¿ÁÿÿþÖwÿÏóÝßúíÿúíÿ=‘óø”¸Ã <¦õß1îBG ! †èOCÔ5B¾òêxü2äÕ+f*oŒ)<®à5üÉ<¾Úo‚ñÏù&H› mjÉã¾B¶LÑÿ¦Ào4>3Ðg|æÈ7GÚå"Ý0àkÚAß^£\÷ð#ÝðS{x(c-@³Ò–hŸ%`XRÛ;b…´ÚdüVÈ·FykØ kä[‡ ÚhúlÀäÛFñ¸¯€g‹tôg´· ò› m—Èc¾¿½%ùšÉâ¾Ú£ÐG Çøš¢š^SÀkJ}sàvDž#ò»Y‹¿Ö °švóßµŒ ‹Nè§å<®+à8£®3ôÄ}ç<.h§ ðº%)Ý õ"ðwÊD L`Eñ8ÎHÇo,ÊÇQù¡v”þû¯\¯þ·ø»ÿš¯û¿ÕÏýGãÊÿ«~î¶û¿Ù·ý÷¬I§ðþ\É’Ç_¤J¦Æ† ø€Ï†ÅL¥oþN=W|®‡üzècÔ7†¼CnŒ©ß Zêcü¨ŸŠêÔÌø»ð5@;M Ï&H› m‚´©å ¹1E?š–Ꙏ9p™#ÏisÐÚé†eløhým8@w#ä5F^cÈ}cÀm\Ɇ ”±e<0ôÚe,im±BÚ ùVlø±Fykè‡uŠl@‹ àÛT°aÉýb‹vÚ"¿ à5mM@[äÛ!m‡´ÊÛ¡]ö,~°=àÛ£¼=ÚãøH; |Sô[SÀk |M©¯ ;äz‘ïHýYä5ìf•ÌMiŽtsôQsô¹ê:¡íNh«Úá <ΠË:à <.èÀuAYWÐ劺®H»¢¼+úªhkZ[€_-PÞ }çxnH»!íŽ|wðØ´¸³aÕ#‘Åjõ=žÈ÷Äßžh§þö,/Àölo¤½ÑÇÞ¨ç Ú|ÐfÐæƒ´`û‚6_àò­¾Hû6?äû!ß}à<þhgÚ@C@9ºÑ?(HÓ€ØAhWê! XÁ¨Œþ A»CAùÐ ºBAW(ð†‚î–&,tK¤[oʆ¡l`…V8Òá¹,þs+ài…º­P¶ò"Ž@:éÔD:mŠDDR›‰þŒ]Q€…t4ÒÑÈFùhÀ‹AÛbж´-eb#펅ìÄ"?´Å¡­qÀ‡ü8ª¿t<§ÿ¨­}Óšóë>-pj¶‘2ðM>®¾KíÛëëÉÔ–ý#;FmØ›ü\j—TÿVµAªýQmê×RûB튾/«ÆzÝVèû³ªx“KÇz}?ö_×Õ1ý_ógU_VÝX=ã ŽÁ긛Êh’ †ÔŸŒÐ7FzKcÈaúI{-ž9þ6>3”7Cßš¡¼øniˆ~k™·€LXàÛ+ȧU.iŽß­!»ÖøÝõmPßømñ·-d¬ ä­ è²Ã·¾PÞõš¢¼#à8N3ÐÚ 8›£Nsüíºœ —ΠÁt»‚ލÛc•òÜA¯;þö@yàó@]/ä{¡-Þ,>¶pû¢¬/`ø%2‘ó§:öf p¢LÊþ ê¿g0ʃ÷Á 1‡ L(ð…nKàhYÌܶ0ä…ÓOôy­P7¢˜ÇOQ€ üÑÈq`®\,•Û·{ÿo×ÿ+ü,zžç1>Oðyj œë4xŽÏ |þÄç/|^âó7>¯øùü'Á “àxI˜KIë%Ì¥¤|=s) s) c¿„±_ÂØ/a.%Yp_s) ¶@‚-à´H˜KI˜KIöüþ0æRæRæRl…[!a>%¹ð»K°l†„ù”»!a>%a>%yóóû°ì‡e–0§’`˜%Ì©¤`~föD‚=‘  æUæUæUR$?û€y•„y•e”0¯’`w$Ì«$Â×Z1¯’0¯’`‡$Ì«$Ø" ¶HêÄÖ$Ø# öH‚=’`$Ø# öHJâ~,æUì’„y•Û$Á6I˜WI˜W)w§a£$è¿ý— ÿô_‚þKÐåÞô_‚þKÐ ú/Aÿ%è¿ý§w$è¿ý— ÿô_‚þKÐ ú¯œ×…þKÐ ú/Aÿ%è¿ý— ÿʹè¿ý— ÿô_‚þKÐ úO÷%è¿ý— ÿô_‚þKÐ ú¯¬CCÿ%è¿ý— ÿô_‚þKÐ҇þKÐ ú/Aÿ%è¿ý— ÿÔ&IÐ ú/Aÿ%è¿ý— ÿô_¹Wý— ÿô_‚þKÐ ú/Aÿ•;mÐ ú/Aÿ%è¿ý— ÿô_9« ý— ÿô_‚þKÐ ú/Aÿé¹? ú/Aÿ%è¿ý— ÿô_‚þ+gb ÿô_‚þKÐ ú/Aÿ%迲ý— ÿô_‚þKÐ ú/AÿéüE‚þKÐ ú/Aÿ%è¿ý— ÿôμý— ÿô_‚þKÐ ú/Aÿé}= ú/Aÿ%è¿ý— ÿô_‚þÓsØô_‚þKÐ ú/Aÿ%è¿ý§çy$迤ÞËH3x{w?áßqw9Ÿs™è͹Šùœ+™íeÓ³?Ú:]ßO0Ñ;ûSÄÏþ¸ò}î"~®ÛßßÏÒ[Ïóág»³Ø~åìh"?T¤w¾;ƒí-(g€Bø=þ5üßcHåó°~(ÏÅŠø}AK~_0“ßé¯ãó±Dv¿ˆ® jg¾Ø™Tzæ[¹ÛïÊ÷2øÙ î%²}víì·ƒÞÞö÷ œÿöákŒ™ÌOSö Lø9ð ¾QÉïüûð3C¹|?¢†ß+tàw SøýÂåìŽ!ÝŸPÞpàûÉ|¯¢Ïí*øº¥ ?;Å|Aå y.¿/UÆÖ4•óä&ü^b¿›˜ÅÏ–—³ûTÊþ†Ÿ¦ðya![¥çÍ•w,ù1™Ï ù\±’Ï-ùH2?¯”ËÏ£W°·”s–üüR2ß)dg~險rwË’ßwLfþ©r®©˜ŸmºËïu%°¹&=L×\•ûYü¬B9?¯PÎæšôΗr^!Šï¡dòû…Ì·¥gØéº¬rÒ•ßLæóÍB>ç¬dû(Ê3ƒ¿OPÄî?Ò3NÊ:î]¾âÀçš©ü^Ù¶ÌØñ ;¿Çã÷xú;êÔMîàM€Ü%@Æ?ðÛwànƒ2mQ¦-à¶EÚ‚þv€Ñ0ÚF»:¶dÚ8ÚF”ë€rP®ÚÐ<ìPǦ ‘#hî¹è¹è„z‰¨—ˆz‰¨—9LD½D´/ø;ãÓ4t]Ð]@gÐÐ庢LWäwC~7ÀêXÝÐÎnÈﻃÆî(Óez_ÐÙ¸’P6 }’œIè×$´; å“€7u’ëØ²nO”ë‰r=³'hëE?ÀÛ íè…vôÜÞÀÝ´õÁï}P¿~ïÞ÷®¾ §/èíý€£êõ«aKÃý»?òSÀ·Д‚r)€‘)蟀ûÚû꽃ßÞÁoQv ê Aï@Èæ@à\ƒPvÚ=üƒÑÞÁ€;p£ïÆ`´+õSñ[*p¥¢­©€ûp½‡²C‘Š6 ¼a(7 tC¹aÀ3xFÿ”~\#@ÃÔ\£Ð£@Ã(Ð6 uFß(ÀßF£ìhÐ5tAÙ1hûüžŽ~Mm逑¼ãú{ðNþ €5õ& ^êeGpO¤ôËDÔ›ˆz“oàMž)øž‚¼)À?ySÑ–©(?5‹ÙvåßÛsùÿsö<ÞÎÍßÎÍÿ»çæoçåoçåÿ?ÍËÿœ‡Koßèù¿{£gÍkïèeñýOK¾ÿYÈÎöiûŸ¹ìÜÌÏöùð³}Åüž¦+§'Wo4„ßÅÉåoõ¸ò÷ô²ø{=ø}œL¶ªœOâïõñ3ê&üüMW¯ŽŸYOäoëó{×üÞu»ç._/™ß½.~óå ¾GšÉÏÿÕñ·ö’ùÀrvôŸ¼·WÄÏúð»Ø)üÎN1ß;µä÷M3ÙùzuÊj*ß§˜Ÿ 4àç‚ø~j?'XÎÏ ð{=QüíŸL~¿§ŒÝßVöZ-ù]Ÿþf_&·¯ˆ·§ï÷)÷,ùýîD~Ç;—ߪàw½ ø ~/(‹½w¢ìÓÖñ÷ƒø>O ßëYÎ÷{jøž?¯”ÂÏøòûC•ü!þÎPk(“¿ŸRÌöw•;ãì-åÜb&»@Ï.Òwü´;ä‰lI¹g”Àï‘çòsŒü,c;ÃHßTÞ#Jeo)çËØ]#ºïÛ¹’ß'÷ao³(÷2ù]‚2~ ‘½ØåÛ1W8 ò”„>MBÝ®ÀÕ¹†ß#Oà{¾¹ì|U<ÊÅv<`Äã÷xüOGx ¨ŸyK]AKú,ýѸÛf”i‹2m·-ÚÐmhí£`´C[ÛG{àh8P®ÊuýQ®ø×e:¢LGúàvÜŽèËN ¿˜Ò õQ/õ?2˜ˆz‰àY"í#|º€†.èƒ. 3¸»v”ëŠ2]‘ß ùÝ«`uC;»!¿;ÊuÝQ¦;Êô¾ ³p%¡?ú¡\p&¡¿“Ðî$Ú·À›Œ:É ¹'hî ˜=Q¶'èê…ò½è7Ú0åz¡½·7p÷ïú€ž>ø½O ›–ô®¾ §/hîý€£êö=ý»?`÷G~ ø–šRP.0R#ý3pßA›ßA½wðÛ;øm ÊD c èXɦ9ƒ€kÊB»ƒ€c0Ú;pî`ôý`ÀŒv¥" <£+mMÜ÷€ë=”ŠôP´ià ža ;yÃP>¿MÝž€º“ÑÎ÷ñ™ \ïã3 ðGÖhÐ1tŒ£ñûÐ1mƒßÓчé %xÒgàŒCÿŽž À7횀zP/õ2ЯàÑDúA?LD½‰¨7 ð&Þ$à™‚ï)È›üS7tNEù©ÈAçtnBÿ½}ë?ö=.ôµ2ßÑ›ë(óô—2Ç su.@}~Õ¿§~=x­øîÔ7§>9õ½©ÏM}lê[«~´ê3S™úÉÔG.à~ñ¦¿û|Ý2=?WßÇ-×óoUßVõkÕ½&uŸ©Rϯ½¬çÏ^ã~¬êÃ~Ç}×ÛÜgUýÕ‡ÜO}ÄüÓÿòwÑrõÞA®àgB ù™µ»üŒZ¦xûFy׸NÜ­ ïîÑ÷q•÷õŠÙ ú~r÷ô¶.fw”é™ úÖ}÷‚¾³Ò´´Id÷ï"ëØ9)z'€P[VÎîÖÑsÅ­ñÝßQ®#êtDÛZ£|÷ö&owüÞ¿wAOg|wÅw"pu¤i´¯3èèNÿF?t¥ÐÔxúVŸ ¦¾ihWʾƒ~éc‘7ô ®á¨ŸL£ß ' ô¤¡ÆÎûh÷dàš z&£'ƒ'“AÓdÀ œIè‹4´m$›; 4EÝá¨7¾’-QŽTjPg<òÆ¢ßß§c7èOñãï±€14Œ§y¨7pÆã·¡ i4àŽFýÉElik$ò'Óñr4ðÒ‘?y“3ÒŠ6¤ƒ®É(7ðÒA÷ûøž€ò“)|Š‹~ž±å1º$6õ¦ÇD|O¤ß¨?åÿ{ßU±öA%"UJ"54 MÒ“ìlz›¾é½·M_!Ô4À BéH³„z ¢Óá "*‚€ŒbùÿæÌœ=ÈŸ{Ÿ{ï÷}÷z¿/<ϲ93oç̼3{Îû¦ÓtOtø3|2(,ì }²Á?ç”È¿ƒvä§ë83í83ýÏÎO×ñ,SÇ™iÇ™éÿÉg™žêÈOùÏç§Üù7rœ‡ñ÷Gêx’°Gê?öþHn;—½-æ12åyŒJù³Üü’8žëü:Ï塿ïIWñ÷¤gðwIjù{ÒJž«²‚¿+Ýë‘wJªø;%&<¯Q.{Ÿ‘æÎÓç6Êgy+Åú$¦üÝ’|ö¬¸þÝiÕ#ïNðèjù1Ï‘1¯URÊß¡6x,zçÄç; ãùDªxÝcž÷(—¿r…絜Áß«.å5L®óH¦<RËï'¾“RËßK1dï¦Ð\“bÞKд¬e!ÍKBóFÒ\I4ÇÍ;Ißaßë0fuAhî?š{\̯nÌßeÑðë,Ÿ‘TŲ¿«íÈß×.cy'Åú(×ù{ÛJþîv.ϳTÍßá6`yÅw_ÂxÝж*æy5 Y®iËvþ>L{ž^̳YÅjªˆ9™LxÎÍ0ž»½Œçooå9šÌX;öܽ)ÏÓTÌß™©ãïÍÔñçñ ø;¥ŽüÝ™|žƒ³Œåró¯°wgh­1gÏå^Æk®´²÷gÄüíZžÃ½‚åáó²´±weÈuž§É‘×Z)fïÍ(àè+`{ÚhWÐö:š+¡‹UbÌ)A_ Û)©íÀÛê Û­c ºÖÐÁ:Ø€† hØ€† øÛ‚‡-xØ‚†µ µt°ƒvÓ0ötoŠk{е‡-0À×8Þ€už#ðAßcɱ–m ¢v²­3dˆLôˆNìøèä :¡àÚζè2pE{Ý_/v €|n9°jØK yÜAÏü¼¡Ïlô«Ð¯‚î*è¥=ÀxP~°‹xyÖ“~ ‡'l“X˶$^àáczCVo´û€~"hø€¦dõ¾/ìšx_èï _ù¶dñC¿ò©!‹pjÀDA5tñ§²C™à =m€ N ä\ ÆGøW`ƒ Kt`è ºÁ  ™ƒA#z…A§0´…AÞ0ȺáàNí[ǶI‘  >‘ðM$à"ëØ¶)pÑÐ5¼¢!C4ú¢Ñ }b¡O,pb ~±à Úñ€‡\ñ+r%.2' = ~JlRÛv%C¿dÈ— © • ©ÀKžúhÀ[_¤Óì’^Ƕe ™zà“ïlôeײ-ZtÉ|õ3ÚÄýýבŸöÉùiéþæÑ3Q|‰ûiÏ"íO¤½Ýw`\ˆû øOÜ'ÀN83•âw»Ã¯b¬Nãt)6§q9ÅiNcpKq7줩ÿg©–8øï‰ÿVüû×bß'ŽtÏkbðߟ§¸Ž×–kçù$âx>ñ6–kRÿžo1Ï%áÈkÃUó\’f,‡$­1#¾³«fy$hÎHZûŒÖPkš)YþCË*–ÚæJ¡¹Öø[‡ À>VÉ ð6ÓíVàe6ðw޶Àµ‡~vÐEÚŽ°úœ ³h;A7À¹Qx\»àÚ´ÝawÈNçV|«ï þ¾àá ^Õ}˜7 G"xF€vèD62E@‡è–ˆë8ŒÁ8ÐOÄut‰Nx$Ò6ðLÁwì‘9³`³,ðÈ‚m³ {äs¬š®›ÐOë0ðUCÎðLèà:r‚whEn ]c!S ðƒƒ kd‚üÁTfØ4ô‚A? þIA{¥‘ϦèVv¬Aõ‚=ž®/À‰ÁßQøDBþ(ðÈÝ$ô¥¢/4¢Á+ö‹Í$ð…\±)0ñ¸NC{,¥ãüx´¥C$Ø; ú&gxd74“a›TJþL|*à³A/ôÒa‹tÀ§Sxº¦Àž ›ölØ(í9-ürè¼Ñ‘¯ºãŒ7× ãŒ÷ß}ÆûŸö\lÇ9oÇ9oÇ9ïŸóœ—®—¹ùêÕÿD¾úŠÇj•òZD¦Ôœnåõ,ã©giÂëY泚Ó4ä1VòšÓµ,üsÖkxÎúZ^ÓRÉóaîäu§ÍX ;šŽæÄ”ryèëZöâ91óY};1O1¯AÏj‰ù>Ly~ç|–Cèù1‹y¾g^£(Œç²¯æ¹„LÉ%t…çÉœÁë]泜!bnh^ó²Œç2|¬fQ¯MÝ‹çŽãy3«yýKž;3Ÿ×©¾ÎóÜ+YÎ<Ó2^ ³ç•6ã¹45<ï}Ë3-æ&éÅóM;ò<øù<Çf5Ï?mÀëú™ñüDa<ïf)«Cóoеþ x½ë¼æµ–×ÿ«bu5ÅÜù½x¾>ÏÙ—Ïóè×ñœF†<‡Ÿ#Ïã—ÏóuÖòœ~†<¿¾#¯Ÿ”ËëpV³\ûV¥,ÿXOÉ‘åݧ9îÅúÙÕ¼>§¯£íÈs"åó¼HÕÔÔžÁsjx®OÏ÷YÊjkÓüG6Zž£ÿ:¯s8;3–£Ÿæäwªå9’jyÞOCVcÛªŒ×‰cõph~AZ¿“æß¦u¶ÅzK¹¼æR«“CsŠy’Úy]m3^wIËrrÓúÚ øHú Ø^vÚ´2*Á[ •JøO úJÈ¡„ÎVàmúÖ€±Œ5èZCWkŒа аo[ð°[аœàì ƒt°ƒœv€±Œ=®íA×þ Û8À'àíÀõ¼> ïK?°­/ú}1&ý@Ûúø¡_ ™ÔE 5h«ëØöÆt _ðêØV'°À „Ð/¶ ¯ ð lô‚lAà ›ƒn0èƒn0hÓ½8Ý—£- ò†µ²­R8x…6בÐ)ô"Á'~Œ\$Ú£Ñ ¸hè ^Ñ!}Ñè‹…>±Ð'8±À‰¿Ø:¾ÕÂñ+rÅC®À&6íIðU`“@#ô“¡_2äKTÐJTà¥O}4à­/ÒévI^ð2@/ô2À'ßÙèËÿôå@—À甲--yÅÒýI¹Ãè¢ØQ¯âo½ º—{ÂNÜ·=ºO“öhÒþŒîËߋѽ—´ï¢û-iŸE÷Wî­¤=•´Ÿ’öNÒ^‰îÝÑ=Ñãû!ºúk{ iÿC÷>tß#ŸKûº·‘ö34~¥ûiÏB÷*Òcà‰{’Ç÷#{MÞ3ƒ?m•ë¼&Õ^gTóÇúÒ´.½¾}¯Ažy}ž×SÅë…B§ÁÆ,w4­M(Ö›îvV#tz5ÏáYÇêG‰õä!ãð*ž«³šÕ,¤5ĺ•u,Ï$­5ô ä"ôð¤‚ÕôR²ÚI3¯³ì´V:Í=)ÖD2dùå-qmÆêY[áÛ ß–×Ù‘¦þs +Ü[n´¼lÐïFãB Ë9ï†oÌ£.àmÛÆ¦};жƒ-ñíÙU4‚ÎŽÐÉ›Ò=§ –ÞòyÒXCÍjÈûâo÷:–§Ó¶w+å5ã{±üž€÷‡ŽþÀ÷Ž?lá ÙükY}'Oú77€ëßÊê:%Bè”Úþ •¨bG®‰øÏÚŒ»F€¾-àÒ k"xÛ~"Æ“hD@®ÈØPJvH¯È–Háñø8øÀ<²hüZ13íièO£ðèO£´¥@nÐÌ‚O=©^? r¥Q} £xfvp}@Ëx¾ÐËãÅxjè©F¿´ÔTWð ïÈ€¶´B§@à‚~ è‚v hv`ƒ gä ªcÇÍÁÐ/tƒ¡_0hC÷0à‡¡- ¼Â O膃W8`#p{D‚^$à"1." >Qà þÑ€‹†^Ñà ¢«Ø‘u,ôŽ… ±-8±à Úñh‹l<䊯cËôM@{ü•Ù’@# |“A?sF2ø¦‚*h¥/µŽykÀCÞéô»¤/x —zà“ïlôeƒ6úr Kàs´,ì_«D×ñßåw‘ŽßD:êuüòï«ô¤ßAþ§ù{ÿø¿ôÛÇÿÔïÿ)¿yüƒ{ŽúUÿdýªªGj&¿oÙ#µfßðý`ƒþèïOã}ðëzF gx#Èd™ŒacèdŒkãV¹†•9d±•ךUñú1à9Ð×)eµGf`©yÝrðlÂóýÃyÎSVïhjËKO—ý¡f¼¦U)ÏÙ6Gžûmíly¦5R‡Å±šÃ7 ô†kX]rºŒ+ëX Z%d¸à;¢–׸‘h ž#[YýkÈ ¼Âë\A¾Q×Y]KcV/ŠÖ´¡ui_þhS^ß°£«yÀ™:²:bmЛ\Íê©O¾ÂjÕˆujã9§¨y}u-ÏÑ_ÌêÁN½Âê׈yÀ y}Xð›®e5aUhó€Ü^V{ŠÖ^óòÓoŒðµ-fõií)±,ðÌq­]ó2V«Ö†µ„,–°£%ð-o |KàZ† @FUàOЮlc!‘d°‚\N°£ý@/kж†Ì6è³AŸ plÐn:¶àm ™mÁÓ6´…ž¶ÐÓÎu§{%À…àc9@V{j#ðs€ÕvtŽp ƒàœãÙÔ,4r‚œ.Å}~Ð#×þ é‚~üí ü8ÈåÚÆÂ47øÜ ×îàç^îàåš Àu‡œ~ÔÖÀUÁ6*Ø&×!€SA^Ð)e¡]ýOºÏ¿(è¤Égáž7xxC¦ Œ±ØÅ<|ÚYè‹>_à„ƒ¶/`|ëùQÞÐ×vòƒÌ~ÐÙüü¡«?dñÿ(Àûƒ‡?xû'0!T&ÐG[(x%á+ ö¥à‡?üB¡c(t …<¡à—^¡ô~¡5t4 Û‡£?}áh‚}2©½A' tR@?8Q%í1°s Úc c mƒ,1à=Ó YØ=ã@'ðq€O€œqZÆ&Â&‰–~.§ÐoÀ§>ð)€Mí4ðN£°èOCúÓП†þLØD‹öLØ1òeB—LØG ;h«¬°ZÀjAK 9µ4¦¤1!ýGƒ!º×¡û›Ç÷5Ò~¦£îÚŸ§îs¥óaÛŠ±¬Ç>)~•âVé¹ÇãU«JÏé_>)®|R<)KgÆRÎ í#ñžç-à1ô¬5õÊàßV§î•vVû\_ÓüúÏ`uï{±z‹t;>“Æ j¶mŸ8‹jVSÑ~°À|4<Ã<m¬®ùcVÞ֚±¦ëà¬Ao*hÏÄ=êY èZ²ºÎüp¬«XíGkÀ;CNgº6‚s«ñhM¿éB×±\VÃüœ!»;úgã{6xz ß²ðò¢04VxxÁ^˜k¼ »èûàÛ²{A>|ü@ß:º@&/ºæÑu2¸£Ý®? B× ðsE¿ ú"!ƒdޤr@.È©‚® ¶ÀFCoA×üüÑï8È™ ¼¨ €M† !TÐ ïdÐH@¿úü WÆMøæà“ù3ПÞ ‚¶z Ü [d/¼“[ÙtB¿a÷葉O8] C&tJ~ ¯ñ‰\9 •‰ïLºÞÀŽ à“ÜLø<t5 ›™ÐYKÛéü šZÐ [™Ö²}†øï_ù\oG]¿Žº~ÿèyÆþÿÚ³ªŽgwÿx~õg?»êxf÷¿í™ÝŽú”ÿ@}Êü'Ô^Çœ9µ”×_~?èÝôúÃnýa‡þÕl 6=#ÀA&£:¶4C_c\_y¤F¥–Õ_m ÇC^û¸¯SÇjÇ Dÿ àÂõ ð 6åµä ÏàZ^SÎŒÕa§µ¼‡€ïPÐ:ƒ×JFûÐ:V·’ÖDœiÂëV¶òšì€¦áuÙAk&®‡Ód^ÍêWNÏà7¢Ž×®„Œ#Ñ?2„Œ£?~ÆëXÂ6Sû’ ¯Ï®áõ,ëX¸1ڌ״ìèZ^§]ÅêÛ‰õ#áÉhŸÜÊj£‹õ$ x IÀMŸ)VݼÍAkj+«%ILXx2­¯“^Ó;°Ó‹Yt¢äµ%wòzvÐe&ü>ý*VWÒ׿¹¬†¤5tôB›9>.°£äóMKв¾%ð-oIcnÀÈH # A‚~B÷ÐÅ ²YìjE÷è÷Më6&Ù ßý6ݲڀž-äuÅÇ6·…mm¡¯ pma{|Û6rº‚Ž=l»G‚žx:@&´;À¦ÀsŽÅ状8èäyUàåDã{üí‚>À&àÚ´\ ƒ mǵ+Ú])/Ðq7à»A^`Ü ‹;xºïdaVôv‡ÌîY[e@ôU¡=¼£0¾=@Ã4<¡·'èxBOÈ^ÐÁ ýÞè÷¬7ú½ÁÃòyÆí¾h÷E{Ú5àí‹v?ð ‡íc@Óòj@Ëî@Ë~ó‡,þÐŲøÃÇþ+|ChL_ÅÂÇPà†‚n(膢/tâ kpâ€ØØ+0‰°K"ì’œDÀÇV ¾SŸøÀ§>…¶ƒ~àÒП†þ4ô§¡? ýièOƒŽ™Ð-vÉ„Ï2éþ òg¶³X x-ീ×^ zZè­¥ñ$/é?=éYÉÇϰ GGUƒ}UëÒØVz–ñoWÑUŠI?›zRÜ)Åš4Æü{ΤþÞó(ƒÿÿJÊ¡ú¤<i|nqÁ#qÚ£gRaŽš³Å¼N¶†mõûA–~¸çúãÛ2šn&)@ßܘչžBcÀ[€¦íÓ«Ž9lo~“Ác 䜜 \´ÙBöahŽk[àM†<ÖÐÅt¬!ËLàO]gú©fu™qÿÛ‚Ž-]3!§9]qZ‚Þì+ø ßp³Á×6pnc·A¿`½Ðç¾*ðð_wz [û”²ÛÕ‡®¿¸üÀ#:†Îú9áÛ28áïè¦ïLÑüîbí1öîŠø^J)Ï;^̞ݣïƒÐ{ƒ¾ç\ÆòÐgöèrCóœÓzb4ÿ ­CFc&ª¿góßž4,FÈÌecƒæø¢õi.ÿê?‹{Ê›o™Ð¿È©Ø{ùü ÅïY½Ü|Ç“Öeý\KC»(îµWæw‡ìûõ%³[“ú+.MòW„|œ’PùœQ³.`=ÖµÏü§m„¢Óê(»È þ›ZÎ<¬%㳺ñúCƒc¤5;óÓÈ)‹È;ýÛž+è/¼ÿûÍ.Î9ƒ…¢ð#Ïü|³ða ¾v³²iºu£üL<é:íêÍA6È;Cgù}[¾EqåÌe•·— •ÏŒ™²wÿÄô¿7WtÛ$ ~óào†ûA'—ÑyÐ`öÖ …¤5d*™{{%y§ï1ª‘PiŸìûD¸ÕçÐÅMÏèÑåÏWv5ó™.-0ëï¨ü‚ôeð s…Ñ Šýü˱ϒV»ßOi!û68åXûSÜfö*G¸Ú’}Y(ò4x½Ñjªòæj†§½ñ`#ûqj|ŠzëDÒ“_¯[»x‹âêçÍo}´‡•&].Ùöñ!ÙŸ„¢ÎM/ÜJ=6.jJŸi¢9$ë3bùÖ“½óõ~Ø7®áTx¥ì‡)êMöoÕëWøþÁñkÊåq°šƒ]kʉyGd9ûüÚýYmµ^νw—Íy3BoBepyúÒ‰Ba¢ªâîkÈÆôØ8©×ò¶ÒÚåùe‰&{k^š:ø»ß„Ê¥¶ž$ö¾Ph¿1ÃúöÀ³ñPsiá¯YWŸ&-l†­·ÉÞõC÷”Ü*Tn¤ T…¹N1¯…<íÝOMÖDZn×y®r>§×/%BeÙkSÇ.ž!uoØb<6N÷øõlÍÅbÒrez·Ïò|ÈÞP£cEߌQ\›‘Öë+o‰Ÿ|ßíMëö^ ðÙ88mvÞ";¡°q<îèjy~\Ãü}f¨Ë«6~¤y¯á¤—nÌ!»®^Î{.s°~ž.Ü|<»öý™Ê›¥Ì¯g&©.~yí4iÞdûó3}-È®CºÔUSëÝ ‹Š­[O …»g}º|Ôw€gþ;c1 3KÒ¼ÚzÐÓÖKÈ®EºNKn)ݘŒ’º•¯Ë÷Y)óßkqb%ÍùíáÛ»Ëãy—õøs³F•èçiÕS]kªÞ…¿ ÞQqàMÐa~=c»}¸Â84¿ú…Ã;}_ÓûcçO®y¥=}õ÷«ÎùóöáGnëýRØþù«î¯”Ça)÷¯"ÉÕ?óiŽ_ä¡®2%;ߟ©îá.ËÃî+Ùnü¾×Ïw¥Üϳ^ÇÌhCš½/Ï{»ÝìÔzþx¯´F–ÇþŽï‹½ôótaá餮øÜïæOÅÔFš­Bí®¼ÿÙi{gKiâA§2jñ½¤u ðÜ¿–ƒN\« ÍãÞº Ô–/~½à¼VÐEäÔ9%ËËý£¿×rÏ|™Ž\ÒÜÿL¿áÛï‘m·-—l€œé_˜¸/Y(2ö.\¾ ðÜß“ÚËŠ'\!Íîþ”Gêý·£fžÓ»?tZc²òšƒ~^/BøÉÉþ ÃÇÁÝÀŠÐý¤éÎñ{î§’…®CŽ.Ú¥ø¦÷[s~©\?.£·u`“쯵Üÿã¾iŠ9Oš¾˜\rç7²C1ò·cÏ}!è^÷Mº7t˜°Ì1¡þ³wíÏý;òÒÄ›“¦O–¼òn—L²ýŠáËÃ_I÷Áp¯ÞC»É÷ïža °”Èþ]ËýËîýxkªêóTÔâúëíšyFÁ}Zd¿9{øë å¸Èp]§¬QT.>¯·»îó;:Ÿ4mÞ5¯Ï8_òö/óɇ‚®ø÷5#3½åõm-Ÿ¿[þr/ï—C¤)ïØùÎ]î’·— Ù>_нah?ts'¡ðBBÂS£Ö)o®ãó´?§÷SSê/½ƒø·YÜ'o1¬ £Ùø Å0µ³¼~¬ãóõ.Å·&ëI“ÿÔ_Ìg‡“mµë{|µÙCÐv²(¸×UHó‘~¼­ãë9Sš¬OýîÜC²íÕMãίÄüùɺñ1Åjy¼òõK?Ÿ­ãóùªç¯\®/ Mc[`j‘üG¶YÚ¿—Øž#û“cNÃ$ù¥yNGëøº¿|NÝöìa¤©ßtê²­—8(ê$ÿUOœXši ¯ÏÂ+yýŸó“ü :|½Ï#?Ÿ¨ßDš:us:2Uoï­·>²_ÝóAWù^cc'ÌÓ7FÞ]7xÜÿârЗ4~ßõëiª!dëÇ¿=¼÷ÔiA·ÿ£/N/ô•ãèuÜÿ^_Jyºš4ŠêG’­ ,ö,øIqVZÄév¬PÔ§ðÌ’‡Ê›ëù8˜umÉ_²ß'Ç»ß6]ö Ùú†ßÓ/FºÍ «îï÷ï¤è†µ©;I#oÿM­dk¸oЄѹ²=ÖQÇÞ–æc)~>÷/ˆwÞ_Eù¸ß:2rÙÏ5ò8Ûpîu«LB^ÆlvæÚEÉò8[ÏýÌãÕÆ…ʃ¹SRHysÌ®…¶7äõÉM^¡wŸÑyYÏýú|dו^¯’FDûþŸ“òl/›ŒWl3x¸sa¢4>Ïý‡MÃ3Š ¤1´uߞ;¤¼ó oïzœ”ü Ç{lý“ÇõzæÇPMØy“4ÎÎ…½dËÒWŠ> r|q*ûùû-ùúx¨P³â²÷ûËÏüZs{e\zÅ0ÒˆÙKÙÒeõƒìo­e»7´úמ,ߟ“¶;ÜÕS¾?60?cÐù»ò®¤ñåW¾¿³º3)[i|bÍ×Q‚®iô×ÃüÀ—†Ç6 Ïü]s<üüoYúù«q]>Õ_—öYë+ÛýlàïOûÍ#/ñ8³àºÂ[Z)¾]6jxüÐH£ž~ndóî3>Þ!ëƒÙçÒÞïõëDÁ¥ß”ÌÝ/ƒ lÔhKú4 \Oh”˜âO65xþÔ"Aw Ïñá__ ~lÿpAé]À3¿×xØéiÓIÝ=Ï|™lz9e§·C®|ÿ±uðÌï5“?ôÛ±ô$i8ú«KÛÚ@²q¾Ç÷ºÏÊݑҮ.´’ÇŸøü|î÷‘Ïk;ÛFÞ¦â%dÃ{=ß¹û"ææû¶GÊ÷ûîçTáWIÃêΓ¾Üñ²þØq‹^y‚Ž.¿?Ü'ÓèqÁáôñÅhiœmäþ¥aœ¶I?¿4,ÿqÛ7{²È:¶oÐï+tçö«Ì¸I”:³]ý¦— …g¯¶’“!bÀ¢ßW6°s$ýõʈĉ#:Ïtço†¬ÐÇ5‹Ë¾ê¥›$ÏÛ›Ø88I‡ÉfØu蟓¿¯%ÅovÞ:|¿Ÿ ;Ø)l|ñB¡`—÷°j[SÀ3?ŸdóièNW.òƆˆ-•|"èàÅ¥û^ ¾4€êØÏlb~>qñ“ïzZÀ41¼@VL¸®Ycõ© ›ûá·™ó„Ânwr»ÎÅ~kóó QÝLR¿t㣉dYEõK Ú&ïøg^7ZØ(ï­&Ub¿µ‰ùý„¯x’zh ÕH‘Á´¥Û>œ-èbÄr\ ͇›˜ŸO°ý¥þ~¨¯¹ôÌy=/è÷ê‘|ƒ›Šï¹=u4Úœ#ÇLù<`ó{õ™k0$H½¬]0pˆžþÒ»oo‹o™"Ž‚.sÌÆ|ó­²^FÉÔrò}Æ %ÕoÕ8œên¦÷w}…IÍú·¿$‹Ž(ß ·kÁ¯Ó~üòSì‡73?WgzÑ;ŽÔ—ÅXd>ô! Ÿµž–qYÐÍ¿¿èmóBÁ[IÓÃÀ3?W‡Ð‰öšl•;‚ꎤ’yµÝ2öœuã->®¥ø/ÿNÃmÏyžrÜ¿™ƒj‡H}¾jýOgÆ’9¯Ž,pµ+ûµäëÂÉ÷œôû¾|zÊqP¾7³ñ!ðsØú\‘Étywÿ¬¾ç帛Å)úõ2?ôÙoÙgÊ÷ãf6N„üÞï^—«÷o½¦ê¼pÆ›¤NøpÎÔlÅ éþæzç~ÉgûWù¾ÜÌÆ #¥’z>wºL¢'Ð@;Gް+tݧ&cùøËÇ,‡ølœo:í¥ñ‡I=]í»¯%ê~â Kð¡#SÈ¿ú"•7ËØ88Î÷yõØÔúÚtÖë1ƒŸ_ëÄ…AÈg×ÀcãàØ×?ô˜1©gûq½z;þº¹!UžT§oN2¿*ëÍü :l|›#’úY^U_UÍ MBï´t®½æ•ƃ~\•±ñptÍA+g·q¤ÞôþÉ7‚ÆH÷—0gÉ>¯ÂÅòøâçÒ¼š×,ZtØ88:­ê§w'Åú—éæŸÜ¹tCLƒ—aü‹ÊÑpÌßG`µ×>k#õÃW>$Û?Þþ-먭 èÈ‘–÷:òè±Ø¸FyÜna~?"nO½Hý­t'&,9ð…Í\ßUò}4c©¿ó½;úõ(ïõÕ~}>óÿáïG:½†ù•@o÷üjÍÅàý²ž/¿‹I-ämìÑã†l÷-Ìÿ‡Åí‘硾âŒP8bîgjê×?oaÃâì»ÛÇü¨Äó˜Å´Þ²ÿÙù˜4~…¢›Æ½S޹Éú d{cqU,°“ÇÕ6‰ÛÒXYqúŸ!,_²aÙ*“‹²^/ê±ëÚ÷úy6oÌ{¹wÆ›Êëâ6ª>^ªï?Ûà.¼Ñ癫v‚îéjÿa—Jržù½Š—4 ¤¾ÿ†WOŠã‡ì(üá+ùœžÝwBîÆ§ƒƒ·|›ÎïþD¶³£þ¾\?èºó? •çŽÒ“ý8ÎûT;a†Cª¼-gããàÎf·ù=Iý -tÄéýûæëç.Z

        §Úìr.øÈv¥ÇÕc[e»–³ñrÇsõƒæ¶–VU Ý7ßqþ¹7gÉ㿜‹ƒ~¢‚ÿ»éH“ì*¬`dïqO¨¤aÖ;;„< ¦Œ¦#ò}XÎÆÇÁÑG&ÞòmÆýŒðtû‡BIIyŠwZštþ.é xæwÝõ1NoGç’z³§f©W¥£ÍΤÅý$T~yÇÊ$HÈ£ä{¬–×Ñræ_ÝŠ°Ü ¿kÀãamå@íGë„ÊOËqgMòâ6l)jŽ<ó¯nšxÐ"¯¯¤Æ–\?O¬7m étSö÷'Öv÷ßѯgK÷\¦w¶¼.–3ÿWJóñŒ gݳU†åA™Yƒ¾*1 ™Þ?%,eçáÊ›[™+×Ñ=RoQ{yì©ázÿn¤aUÝý¸aÛž)úuy ?ÿ×ûw+óoåÿcïÌë,®ÇŸ$H€@ÈBö=7„$—¸A@¢²DD*˜R”¢Q‹ÐÖV$€qO4j-€@€ ‹¼ÁãR›²Æ­¦u‹¢Å\¿ŸygÞ÷½Rý=öþž.ð<—ܹsæÌ™™sæÌræœ2ñÅÿ#ÿ¼Ñu(pµ±¢ð±™ó¼îܬ5/"ìùxi”©0À£ÆÝ«ÎÙ=Ïw¼á—¶ûeåн‰yÓªœ~Y‘¹áÏŸ c©„¾b‚#w«Õø{m?»xØ&ÏAy y‘±ò˜œ€Bí{µ¥7pxcX;ðjü½WÏ}è­?Îó¼ø«+ëÎïa¬ºjQ{TÃ&‡ÏQj¿cËûR¤ãPÛTGnV+~ØqÒ}mÅò«Àc2˜Ý¯<÷NåMö}ÙRs{G9Å;ªÿ”uzÚx‡/ÊËúž¼Ëx´þø¹ƒîÜh|H^LL·çã%oÈ‹¦ë}ójÅ;Rgí›_æŒÃyµW=Ÿ½ÓîÇÕêÜÍh”×Ò[¾²åvIãõïÊéÇ5ŠO¶¿Q>-tíÝ]Ão-ºà€}a¬éóéôàq½œqÑçaÖü²äæÀ¤×sû9ü²FñËö³Vžîz•C§Þ_Zt®yÙ<pú_oÄ?c¯»—Œž*O0À§øf{í&ï¸ò?kënüEê‡Kìþ^SyàžÏ;€Wü±}šI¨ç Ö“ëÆ>™±ýgyFãy?ÏXb;3¿®Qü±íë»ݶÍspÔØwß¿l‰Ýþu<’xUŸCö}¦n·O?*¾Ø¶p;+¥;=å1ÐWïÚzjýIC_|?Ý>×h¼'Ì}é¬vg\º›Gñɶ°UE'ï˜ä98ÕÜàë¿=Y»äÓUö>²±jõ±nC¯µùd±)®Ì?k4l޴⢇†:ã9ë¿ ÿvÔ¨ÿÕÂϦ½}—Ñx‘ì(Ç~añ®*Ëöítøl­â‹†qkê÷OzÞ¿k ×ø}µÁ¨ÿðmy³è´gÔŒö•öyÝâ'K sÖk?lýpÝÏÓû;z÷×wÎÙ¢Ïõ>ÇØønHïü]sœþÞùá¬ùAöyàâoNîµêG­Uü²Ež6O ódSvá26©ùÁhÔçŒÖzzñKOÇüùQ摵Š?¶¨û#g|ë£ä €ñXÚ5·œükÃ+©¦nµås±¹ÜèæðëZÅ'›?¹b÷y%·Ùçãw ÝzçkÇ´\ZüæÕö–~X<'ŸåÊy_§øe³4›¸ä6ÏÁ½Ëªf~ð¾±yùž}·Üý˜ÑØùey2`ñ›Ãë_l¾öäŒ/¬÷|æþq—ï[el¹péÝ~`Ñ!„'ÅjßæèåuŠ6ßxÍ©C/õTë/c«y<žâÜ›Ç6M8¾$Î3Hoõë÷ž9»Ã9/[§ød³š÷þTç¨6_nݽÁû@k¼3ÿõüCû¼Ï¿±Ï7«/{Jª4g?¿NñÉfs9]iÛo|#eΚ{Ÿ³×9 ù¹bÞ¶Þôžž6îúüç¿››ß<ŠO6-MÚ1´Ásðý–ŠÉ7h4,J½3ä©i†÷í¹ó¶õlµyðµÖ§Ÿ¿lVç>Žœ*ò>šÿ{nxþ‹®»ßµÛç5¯ÁæÛó“Õÿ6ÿ®Óü3T.˜8çŒ]>afÎ4¶õ‘DÛråýÛ\¹r°õêây«£/xu•Ãë5ÿèû‡CÊžÊØ6óôï^ûååqÉÔ§m½°xÈÒ¢€rñßkŸÅLJÜ :ºj†-çÛv¿uÕ׋6ýîyñó}n»]Õ¦Px4?ÉfßðœÓž1æ‚ÁØÞwÑé‘IýùÖög–|U›×-—8ò°^ó•ž1K\}t¾±ýú#»·Gö5Íj‡Õl>RÛ¼xÍ/,V3¦:vq‡®“YÆö—6žœž×ãøÏêêËÅߦŒÍ/¦yҵιŸº´ùyGâ‚M!5kŒF™{íq©.•þEàÑ|ily);cÇu«o¿íÍʽ*->]¯øá±÷?™°ÿ^Æoã¯öln®3vìîÕøvúk^5ªg—†î®I-=^¯Æû±¥Ò@áÏ!m?°ãË®£N¿q‘c‡¥úÉžwìvÖ«qßôÑyšü~‹çPó~4·Ûægo^QCÕ<¯Ó_¦¹‡s>PmšÉýÆ™ëÕøoRv!Nÿï[¹;î‚8Ã[QsôÐÎ~þnÄðG\Âé7uþï¬ëÕøo”ÇÀ¿yÄáËïßÞÛ>Ú¾ño÷ÞOåÁßX{<tüÁˆ«Á£øbƒ:osøRª—.8íÝöíñìnIÎ|-­¡zo°ùs‘ÜE ði¯â“zó}0ýoÚ ÞW÷‰‹ÿ~ØðþÅ<€2m7^ód9ðŠêû›'ô÷ÕìŒ7¦z½Ìð>//ê+ŒEÏ6ÕY» xÅë—›[žC-óÆÍk²å±ñœ¡÷äcxÕ½’±èë”É›þ‚…ŸÍó^«}À«ñ]¥ìB<‡5¿îÙôJ÷o>ZÂ8½”WþÊ[Æ¢×¶;ýÖàÕ¸®l¹môß>ä9üåÑ.,ïjìy}Ò+c:Â} ïÓ­ýXuÁÁóÞ¿Ò™'6©ñ^™›[+yŽH+Þ4[Ï{ý¿êY½³ó™ú‰rj\Wt]¾ñ‹»?òQû[coƯË|Á–›ê~]q:ðj܉4 Ô{unió×Þq×4}v“­g½‡Í ʨ|t[ÕøOyPÏì<›×Caözâ“Mqèÿe“{Ï-N¨ñvøKÍGμ÷˜÷?ÄšPž#ç.»è•Tcc.‘fxõ|e»ÜM_u;åÔx?$wûAKx@ªÉ‘ë».m—ðÄ…þ·¼q°ÓþuÏw~ºa…Q}«iñNy5þuW¼U¼þ!ÇÎÿÈý .Nß~³ñÄŠ’ªçî{Ü¡cœØÆ:ë5ŸGñC\&žû°C‡š¯Œ'>:}ê¦Ngÿ°<`ïË«Õ=xŸÈ[Óœ[Ÿ¬^|Ã'k×Ìväx³â“º–'F-ðQë1c_ݬÙ×Í\exï5`Tÿrïìß~x5þôØðÎßïä9¢Î?}ïîhk÷[jxM³ó‹þ۬ƽÎ|nì9¢ìÛ ÃuÝ]÷™jx·Ê‹¾g]¿,ï×/Ž;m3åÔí~6ì…ô˹h¯øvôý cÞLWféç†W×/9àèew@y5þuÚîÿ“Áo>~Ï0¼ÁƒÞ­œêŒûK¦±#Ç[*§=>§Ä‘§ÍšNo“Tž£ÒʯÛ:›—Ÿó‰3ï«u˜Ýï‹åíúØoœye³â‡¤ºýèu›Ž*;g{<›•½ëB×M‰/Þåœ+,7¤8óéfÅD¥ ß‘ö¸ç¨¹=ˆ0š=Ÿ×†7>h﫽ºÿ¬ûÉÅw¿üMáo9tmñû^…ê¯ßìc4Ï~suý»á†>ÿ5w¹õçCóv¯ø¡ŽÝzlÓDÏQSíDÍuš×¬œnxÕ¾Ââ;à5?˜æmÓ=G§ý¢æòe[íõpó3_Ê_ô–3¿É×F®Rg¿®öN»·hþHý2:äã…ž£³?Þ÷éÃFóÉC«%~gÍö=qµœåçÔú´Wó‡\ug¸?±øzí}{êž}“õš©Às}.f·{«æm`›=þá—#¾æôçê][B".wúSéA›Î%ÒŒ­òag|¶j~aÔî~¨·ç˜ÒsÆþI½_½‰õ™:?µål‰º§œæ }o½:–²|ù·¯\aì¾)î¾G†:|öî&9“8ç÷%¯mèÓ_šO"ÍFÏ1iŽzà cû¢; _©qÚsdÂÑ;®ÌuèS'o¢}Ú£øãþ þÒslFŽ?‡N4öùfÅA÷Xû"à?Üg^3&zŽývÎåêŒ'£æÞ=þÞ½†w½Tˆ»%fuׯøa¹y=šhÏ'Ç´žzrÈê‘CÏÝîìî“‚ò™½.]¢Þß”oP|±\®‚/pÞoó&]6õåÙÆ“3¶ßh\í£§Xô¥Íÿ»-_KäuCÏל~kP|±LíkºZÓV½±k°ñ¤:FvÆaqãk<á¶_j^#rί?,“×µc7x޽cnhl~}r÷„•O_nÛx«¯˜0·á/¶ÜReÏÅË&çÈ—žcªßŒ'ßýnJñ…»zÔúÞnßRuÿäÓ>ÅË´]A[¯€½bõÕÆSáæA—áUó«±TÍ_À«ñ_öbr—ø%Û=mÉmòæËxjØ­õ·½”lxõy²n7ðŠ–«ý£}Ñ6xÓI÷µ?7žº¼ö·¿O¾Øµ~qúOÍ‡Žœ6h>9Ã>£MÝÃÙrúÔmK¥¥ŠÓ™ŠÚXz£\¨ùÈÅ6Å/ËÔzÜÓÆ¦Á/ýc㩆¤Ï/¹«§ámx¤îµWÝÆRe¨¼æm7ÚöÛ…Ó/suOµM¸|ìÏw8zjçb9s·DIÐùÙ¦ÆÿÞs>ÜsÉëO:ï.ïð ë\õ’ñ´ÿÖ{Òg´Þý~ïl}ñoÎýî65Þ÷ªs2O›âãéƒIIÎgÿmއq˸ËåÉðj\ïÝcì{ÚÖ?´ûµ®7O_¶oYÒ;Ö|h,Uv Àëq' jJœ~ÝÛ!_vÚüù´¼fºãk{¿¯ï)}úS÷2ïòDÕiŸÚ/ÛçÏO+ý挚7ìu·%7ö¸oSã¾LÏ—mæµåpãõÓ'eÇYz|»Wub·çåNÞÐ+ÖN0ž¹væ³éŸ=ìðºpìQF¼÷‹×Êæ8û†íñßã“—õù¥n·ñ̆žÝ?ÁYrãTkëý[îÿÆ™SÁ£å_wx^N‰ØÿÄÚ^Æ3¯tD.ÚüŒ£_õ>÷–½7Íß:3rZÞŸ[râš+Gz^.üuhí}{]žŽZñV!ý·»ÛÄ+/1jüÖ?óØhé/ñ¤Ÿúw6öúŽ¿Æ³±Fþwbü³þå¿JÝçíÚï]™ö{G;:Eê¸ËÍ:nC¼öñ|ç{¹\û½kR>ˆ¦¨8i¦Ï;—öy¾À6í¦LÇMg—>¾aš|â.W©j]ÛuÜåJíÿŽt08ƒ+´?ãvíÿ®Âǧ±KûÀ«ûØËM:~ƒ[ûÀkÐ>ðhCØBåûØôΞ*ö²ít/êìE;z“ß»RÇo á´+œ:©#œ:ûDúÄ^nõ‰½Ü }àÑÆÚ¯FP_$mŒDî"›uìꋪÔþïÈ&?ú¢[u7èïW£ã6éxË ´ÿ;ú$Æ­ýß5©i3–üXòc›u|·Rã-®Ã'Æröw¯ã+×+_w¦Ÿ»Jåç.1HÅh0c*›Äø';IúÖèб”iWr‹ŠÅR®ã0—©c0€7•²iA:þ‚ôÉÑ¡c%ƒ7šÓIgÄë¸ÈägÐîLêÊ„–Lðg“ mYôK8³VªXÇÙàÌž¢c“ΡL4äÐw9ôMer©'—2¹¤]À»h œ.pºÈÏ+Õ±‰ÛT¼-3&ñåoOª 3&1õåw(ÿÕ2>Ä h|ôT(Ù26„é[²… JµH?C¦Ÿ½•*þÛ­c·*_ÙÒégþ+&]Lº˜übú¾ø„ò¥-ýø !=„t ´–Ô(_Û¶<ÿ«uóY½|V/ŸÕËÿZ½,å´J÷)åLŸÆåÚÇ-tvŠ×ñ”Zt,d«s…ò½Ö¹UÇT‚_ýk|üÑVè˜JÔèÖ~h¡¹K¼O<%ðu_ø‚¨?¨Ù'¦éO©CÇ6­Ò¾ÛHƒ/¸Rû¤%’¦ã 4i¿´äw'Ý}å?Æ=íѬýÒ–ò©Ñ~i¡7 úÃj´_ÚS:&j¥Š©Ô+Hû‚#Ý‹:{ÑŽÞä÷®Ò~iic8í §Žpê§Î>ñ*fªS©MÇN­Rq "ÀA#hc„ÔÑÔI#òiÑ>i©/ ø(ò£È&?ú¢Ût<%èïW«ýцé8«”ïߤ}Ò’S¥b)ÅK~,ù±-:ŽR™Ž¡tBÇ]­Ò>hÁ7 MÇNjPÓkB©Š”@ÙDêJ,Óñ’€Mbì“Ê•¯«¤Êïl2íJnÕ¾f§h?³ä¥Æk³àMmW~¹L³ä§‘ŸNÙtð¦Cs:é èÈ Þ ò3hw&ueBK&ø3›Õ”žE¿d3‹~Îg68³+TlÖlÒ9”Ɇú.§IMý¹Ô“K™\©ƒwÑ8]ÍJ%ä‘΃Ž<Ê$`©òÍ'ã5åÓ—ùà˧¾üÊoŸéÿ_=•Ê7±ô+c3R¶ºŠ‚T\*3Vå‹(ï·›ºÜmʰôÝ'ã4Éø¶Å¤‹I“_,uô)?KÆtBzéh-©U1/L=$ÿYzØWK½*;LvuÚ:Jê"©w¤Î‘úFê©S|u‡Ô¦s5¿Ô¾ó½œë­9Þš×­9ݚϭ¹Üwî¶æa9ÿZó«ïœ*çP9wZó¢œ}çAߨ‡Ö<'ç8ÆNÄk_”m:Î24Tj+•ßGûXÆ éMûÂ[u,´våC:Ò­– Ñò¯”7¹ö=¡ü<›>˵?çSÚg3㜤|5'–+ÍIä'‘ŸLÝÉБ¦Ôë8a”Iã{ùéŒKé,É»’?ùž ®+8¯\ù‚̯PqÀ¤åBò‹äzòî2í3™:Šë´Éjérö,Åïìšm¡ßÙ5ÛËš-Rw½úÝŒ#Q¡ã´ë˜Œg§V?€tgø«s½Ž/ÏxÿZÇ®“>`. ,å| ø¡«KšŠõdÆhUSE8ƒ !¨åŒ8˜ôs×:&ùݨ³8ƒÁLÁ¤ƒÉq9>x»ƒ°;ùÝIw¯ÿáX˜¡À„"£¡ÐJ:,RÇÁ¬Õ¾{ý|ba’ß‹ò½Ju<•:®½KÇÂlÑñhW8u„SG8ýÒ'Í'ž@‡Ž… ¾¾ÀG€/‚6FѤ† ü‘ôs$ôFEúÄ ?z£IGƒ;º]Ç(Wq0ûuè8ÔÝŸòý¡¿?ôÄоÊÇ€/øXòckt Òq¤ãê}â^/Óà­HW° eʇpe©+‘²‰ôC"°I Hš¢b\Ê)8:“©':SèƒÆ>¥Adzo*xSiSj‡ŽeIÙ´5]§SO:mJ§ÒÏ>ø pg€/“º2É„–L`²è—,x# ˜,pf3œÙŒS6élÒ9”ÉalsÛÚ“K™\hÌ¥L.8\À»èK8]´/öæAStäQ~ ùËT · Ÿü|ڔߠbŒÉø fŒMh|ôT©øb2¾f!éBú£²Ea*Η¯ I©7u»©ËÝ®b‡É2VÁ`ÒŤ‹Iƒ«¸U©"ÂŒ]@º„¶”P¾¤Nù„6uü'õ²¯–úWê]K×ZzÕÒ¡R_J)õ¡Ô…ô©³¬5ÔK–Þ‘:Gê©c¤~±tНu©;¤ÎðÕ–ž°Öw¾ë:9ßË9žvÚsº5—[󶜣åI¡ÏRštÌ'ð¦ÖøÄ(Wt¤‘ŸNÙtú$6¦ÓG´!ƒqÏ€Ž ú<|™Ô• -™Ð’ -Y”É& ˜,pf3˜lêÌ&M:‡tuä@SíÉ¥í¹Ð˜ ¹àpAƒ‹z]àt‘Î#?öäѾ<Ê$`¹Š¹.c§ç“Î~“R2N«7ÑwÐS°@Å7c¦SW!¸ )[©bgȸéEÐç¦nêvëîP±8ÌØéÔ5˜º‹I“.&¿¸MÅúñ»d<ª!¤Kh ù%+UÜYs.—ÿ¤^þ!}ìkG2­¥o-]+uì™zUêQK‡JÝiéL©+}õ¤¥i¿©-=hé=©ç,÷CºvغÌÒa¾19¥Þò!-õ“ÔM–>²tÔ?–Þ‘úÆWÏX:Æ:ôÕ%RHÝ!u†o<K7H=໦ðç¯?¸€ .qä÷À&—Ø Zs/^ÇÙ#/¾ æo|_v_øýxz¡ði(|Êoai:>M›Š—Ü»TÇÃ;¡ã%ŸÒ1ð€é LD¼Š{ GÒ¦(~" ¾h9_‘ß¶Ä@C ´ÅPGlšŠ“Gâ# Êx4(—Àï À$R&‰z“€I&y¥Š1—B:Ø4p¤ÑOi¤ÓᥠښA~&õf‚#‹t6ý”M[sÀ›Kݹàu‘çâotå?¯AÅœË_~³Š1'—†RŽ™ É+¶ˆ¹ÄMÜÔ妌›ºCÓ`ú£‚ò‘Ô9EÇS¥/£¨+ Ø(ò¢io4°Ñðg4yýÒtUèèG=ý]:Ž*¸úC{ ð1StÕCµRÅQ%G[â 3Žq‰ç{<°ñôU¥âÒš±Zë•Z)&] -Åà*¦üúbø‡@ËÒ%ôs ù%ä—HÙ—s«üg­-ý*õ©¥KH‡ZºÓ:ƒ:SgJ})uå™kå3u¤¥}u!²cêA©ÿ|uß™úîL÷ckè3ušÔg¾÷êg®£¥~:S7ýNú1]dé¡ÓA–þù!½#Ï`,ý!õ†uÖbé ßø–RGü˜nðÕ ?¦ ~líí»æ–k øNÆúî_t‚7;#'á=x͘Æ+€ß€ „?áó.Àv¡Lø,hŠŽs¬teü»ÒÝÂt,Èßš¿!” aœCȦÀöG¾‡ÂK¡r~Bǯ¦-=í lÏŽ3âCRW8åÂt|H~ëK^_ùø¾:&u»ŽC D"?‘´)Šühê‰nRñ"û7úbÀCݱÀÅAKÜ?šïñ-:f4ø §À›\ð‰Ô“žÄf#šrI”K¦O’”˜¤Ð)5:t¹Šÿ,ãÿɸÏ2Ža:õ§ƒ#KÎOÀfSGù9Ô“K^.õ¸¨ÇEyüÍ£Ž<êX¡ãFÓÞ|àó)ŸßªâC"¿š hg!}Sl!m,¶Eô¯Ünêu7+1Ü b=—Ðæ’~?¾Nÿo9÷þOX¯ÿ;œwÿ3kõÿŸëôÿô3îÕ:}Ї6ø”ã)h_'ä±Súª8;#áÓÎÀwnVâí¼?üáŒ?sf|@~øãù€?¤½]À×ø.àïBÿvg8ƒ*Tlà èꊜu…_»2¶]ÁÑünàèÎnàF†ƒIƒ3˜t8C )øðu'¿;ùÝï¾àëA}= ¯ù¡Èx(üJ}¡Ð=aÐF:Œ~íI}=Á×|=)ß |½À׋úz1¦½©¯7éÞÀ÷>œt88©#œ6õ¡ûßÞí|_òû’î ¾¾à‹€žÚA:z"ùÙ ¦µ(êŠ6м(pGÓÑÐM_GCk?hï­ýZÔÔ×\ýÉï|ú*†ò1ôe ´ÅK~,ù±à¥î8ÊÇAg°ñ|6^ê(Ú<€z@óhJ /¼Ê%€'<‰Ô›H½InS7 ÉÔ— \2íN¡Ý)Œ} øRÈK¥Ý©àL…¶ÔV5õ¦ÑOiôcùéüÎtêH§­Ð|å3šÕôœIßdÒ–LhÊ¢/²ÈÏ"?«UMÛÙÔ— ÍÙ­j Ï8s(“C™\êÈ¥L.4äÒ´»Àé¢Ný‘G:¶äµ¨) íHþ@ò’Ÿ¾|êËoS1£‘ýƒ€/€Þp.€ÞBò )[HÝ…”-w´Ñ67t¸¡ËM¹ÁôÓ`ð f\ÓoŤ‹I“_,Ó*NµŒ=DêXÒ%ÐU%ä—Hٶα­XñR§þ¿ÖçRGZ:ñ̵øé=K×I÷ϬÁlíí»æ–:Iê£3×Ü×êvùÚ>œ¹ö–ëî3×Û?vîã«K¤¾wŽméK'ø®µ­ùß÷LÛZ_ÿÔµµ5o[óõ™gÙ¾v±Ì·ðQ'ú¥õú3¦þejx˜‹à£p2n]€ ’ëhø¹+¼Ö-žå»Qg0Ÿx'Þ !/>ì\ƯßC( w4ô®'p=é… õ¦lopõ–keûð½/¿õ•ë \$õE»‘ÐU¡âªG/PqÕû-äžþÔ×_ÎAðUŒœ{HÇ_ÅAm‰nc2ºšk&.Tñ¡“he’€M6™~In†u¡)…tãœÆßtà3 !ƒº3)“I]™àË’sý– þ~Ï¡½9üž[¯Y:Ò†´'Ÿ¼|àóÁ7¼ƒÈ+ˆWñÜ i{!p…ÀA›Âî…*žü`è)æoñBÇ}ˆ”M)7gmþ}Ïdÿ“Ö{ÿʳYi£ô)ŸÏø|Îç ?Ó¶Áï4Ÿ/ù|Åçk>ßðù–Ïwú —ÿ{;ÁÞN°øLP‚½è¢×Žè!ÁÞN°·ìí{;ÁÞN„ê·Åìí{;Žè(Žè(¡ß8±ìí{;ÁG ·{;§ß °·è1Ád!ØÛ öv}&Rµ½-:M°·è5ÁÞN°H, D®¶Û@Ç tœ@Ç öv‚½`o'Šôù4:O°¿ìïºO°¿è?áÑëbt `'Øß t¡`'Їb´~7ÍþN ÿùÈ¿@þò/Æë÷[È¿@þò/ü ä_\¬ßD ÿùÈ¿@þò/q™¶%Fþò/ü ä_ ÿb–¶IAþò/ü ä_ ÿù7ïH‘ü ä_ ÿùÈ¿˜¯Îtò/ü ä_ ÿùÈ¿¹'@þò/ü ä_ ÿù—úK ÿùÈ¿@þò/ü›oÊ‘ü ä_ ÿùÈ¿@þÍ·mÈ¿@þò/ü ä_ ÿ¦5ò/ü ä_ ÿùÈ¿iƒü ä_ ÿùÈ¿@þòoÞ#ÿùÈ¿@þò/üË» ü ä_ ÿùÈ¿@þò/÷CùÈ¿@þò/ü ä_¾—È¿@þò/ü ä_ ÿòÝž@þò/ü ä_ ÿù—ïaò/ü ä_ ÿùÈ¿´!È¿@þò/ü ä_ ÿÒS ÿùÈ¿@þò/üK[%ü ä_X6Š•~gßþ—û¼ýïÐû?ßu‘ïþÏZ#Yû¿¶¹¯)Õo–êϸÿoÕ6ë¥úýƒ>wLÓûÀze·nž=ºô{ÃzíÀ­Ï"Wj§4m»¾PÙ ˜¾ü|Þ6kûu·öPïcÃ^¦mØ›õ=N¤ö °@ÙÒš~â}ìžšµÝSš~‡X§lÚ͵›K½­2Ï8Û´·¾ÛiÐoƒô¾±Bû hÒûG?í3 BÛº7i»¨H}çS©ý4ûø(So´ä¹¨i'Õ¡íßKµ}C­²ƒ—g¥æÆ0í[ \›šï›ÔÝi©l€Í½çBý¶±YÛÉûù¼q¬Ðöò+Õ[GóÞÈOïKÝúþ¨JËšþš”OÓæ*LÛV”kÛ«:mWߦm°ÂôS¹¾gªÕþ Zõº5Hû-(×oÐj•ÿi£lîk㵃 m¿R½4í6ü´íF©¶ÛZ¨lšå™°ùžÒOÛp•©3âü m¿U§mó[õ»Ê0µ66íŸÃô»Êrm£_£ï¨š”ý‡¹^ŽÔ{à)Ú~«EÛp…é{ª)Ê×ùž²J¿©lPv\æ»ÊH}¶\¦mDj”ˆôu m¹†‘? †‘? š‡k8†ãéP[ÖR`J)¦˜R`†SÇphNÛ‡SÏ9ÐsõœÌ`FÐ?#èŸÔ3¢]myGÒ?#©s$ð£€ÎQÐ= |£h÷¹Ô7š1 žÑà ½£¡e4´”[lm,ã·2ê/w¸ÏkWÛ…óÁ>ŸOýçSÿùÔ}yu^@chÇúl xÆ€g,}7–ºÇR÷XàÆ7¸qÀ‡†ñÐ0ÆÓgãÁ5˜ñÔUN»Ë¡µØ ÀN€¶ àš®‰|Ÿþ‰àŸ)3‘ñžÈx_H™I´o0“€Ÿ“À; |µ©-ÌdêLùÉàšLÞdò.nSÛš)äMáËè™Bû§ðû%à¾ÜS¡c*m›JÛ§‚w*eÖ®¶?—RîRp^ ÎKië¥ôÛ´6µ%š-ÓÁ9¼éôót~¿ š/ƒÖ ƨ‚þ¬ -à«€–Ÿƒs¿Ïà÷ü>|3À7fRÇLðÌÏÌVµ­ºœº/ÿà›E¹Y”›E¹Yà›Ì,úp6ã6›¾žMÙÙü>»Um½æ@ÛUÔ7—>›KÛæ2s©o.tÌ¥¾kèßk õpWÑþ*pTÑö*꿎¼ë ë:இ†ë¡áz~»ø¨ïÒ7¾˜ a>0óË•n7ÿõÿñïc/uv~v~v~vþ¿¸?—ûò³{òjO.¤ÌK]åwÖ÷Oöýsâ'úã«P÷ß»“]àc÷Sæ¼#ÿž e›~oT¦ýÿ4é{Y—¾çhÐoQý´ >>€Jõýl½¶§té·ª5Ú¦²]û盢ߔ·è÷«¥ÚPƒ~©ýÕ*@æ]m¼ö ´P½C2ý¥ùØX¶èw.íçd¥zdûì[¨ïoÛµ Rm{Ԥߛ‡é;–Jí+¨YßµiŸA•êݬ¼w1ßÄkÛ¤*í?¨ÅLJP¹~¾RÛdžÐo—Ê´]fzÃ$ï}M¿*‘Ú·Ð}\§} µë7¹ñúmS…¶Û¬Ww<ÒžÑ|ç¯ß¯Wê·»õê»yO¤üJ?}ò]wq©ò£7¼F½¤ÞíšïÛÝÚ_àBuWäiÓï¢Ni?Ení«h~%÷ZaÚ4Lû-š¢ßÀ7©÷Rc#ÕRÖ´ -Ó÷Í5ÊKú1mDƒ´h™~SQ£ßSµè·òAÚn´\ÝGK{QùVB¾©’wÑò½¼´Ý4ý¶éwU‘úÍüý¾ªVÛp5kÛQ?ý.£T½±2ßd´êw‘úNºBù:2ßÊ/P>kÌ;évýf>^ßE—k»ÑZm;Ú¢ìG‡‘? †‘? º‡k8†㡞RÚU L)0¥À”S Ìpê-Ã;Ôòýè9‡zÎf0#èŸôÏêÌHðŒ¤FRç(`ǃs8GA÷(ð¢ÝçRßhÆh4xFƒg4ôކ–ÑÐRöì xTEÖ°ãFg3 %€@D &·³ïû¾wöÎÞÙÛÁ%*h€ (¢ˆ¨Ñ0*h€Âz:6 ¸7†ÅuTýøßêªîÎÏ8Î?Ï|Ÿ¿ßLòuàÕA·’þ:Ú3 •~™Ð(F–,èdÁ? þYÀf¡O}ÙÐÌF>#xFlhD~#üðÏAï\ÚsiÏ¥=z¹Ø?~ùÐχN>tò¡_ýä,€~!ôŠÀ+¯¼"èu«G ðU ö-·„öÚKÁ-E¶2ø•£O9¾*Çþåð+GŽrøUbÓJd­„¶ýÍÐ0£¯þÕôU#W5p5ÈPƒ 5´Õ_¿z®ë¹®ï”Û³`?…vÛþEüõçý’{±Ÿ{±aÏòíSÄ~ľÇû ûþ@ì D¼ml1zßøÜ“‹ø[ÄÞöX»ol-âjS‹xÚKc7Gìl“EL|vAð /;„C3áð s*xÉØ †ÏTxĈy¼bè#C*9ò˜“òà_ˆ\…ð(§ þÅð)xèZŒ­ŠÁ)§ ¸bèWÀ¿Œþ ð+ SËgía9%ÔaË:xUÑV‡íêÀ©ƒn4ë —ÏüS½2d/F¿BèæƒSŒ¬•ô•q]Ü!ŸÊàU‰žÅè\†N…üWâójàòù¬„Oð…Ð)FÿJô,„NxÅ𯲀SFÿ•ÈSß*…bÞEçJä*8ð¬ƒO!ºÕA«Œïuâ;z•:‚žà ÏjøÔ_­ª¡U j£<"«G¶jlc†n5“n{OÝ]åW·È÷6m9ÜUݳÊ%ݩꟸ©¼f•ã©S½¿î©Þ±È<ÓâÝN[OOõŽL£ª‹Ò¥Þg÷TyØU.öõ¼»‹zÿÓ s¸ˆçßmù :UÍW•£=DÕNY rCu©\/®}rD™Už¨6™ûÐöܼ›Ì%ò1ÙÞµ±¨÷mZUþ¨Ãêýxõî©Q½'ߪޕ?¬ÞÃñPïâUí•™3T¼£j«Áâ®r‡U®ÅÅ2¯öÄê&Ë$•KÔ¬òNµÉœ¢¶wvÜÔ{­1ê=û2'x¦ß–ÊM½ÇCŸŒÊ1Ú¤j¶t¨çü]TžøÃ*w‡ÊKe”yâÅ;<2רíXWõî}ˆ|ÿÞö¾}·zçÞCÕm1É\ß¶|T*×h‡zŸÇU¾àÛ"sDŠ÷eE®Q_øø ¯†‘ôûÓ€A\§Ò‰LáÀDB?›%‚ŸÈu"úÆ—ˆ‰ðHDÎD®#Ñ7œ>?hùAË_ØûøÓæO¿?ö €_ö 8*Cþ@ä„O r" ôƒÐ+~ÁÐ †N0òÃ+^ì 6C'„öh‡@;”ÿ0è‡A? YÂàÿ0ø‡#c8<#°E¶Œ€F4b?ÛEÂ;Rè \$pQÀE ÑÈ ÑЊ¦?>1ÈRýXàb‹E®BèÅB+Zq\§Â7ãàwXnUâÑ­þôK&øè&›ÍDx'¡C|“1 Ø$ú’èK¦/…¾úRÀK/ýS„¿èO…vöLƒnýiডS:xÈ™]2 ™œ´g K&}YÐÌ' øJñ Í,Ú³éφ¦šFìiψ,9èŸK[.muÀæÒž ½Ü.µ‚G>´òéËG¶è s2B¯Ü"p‹À+‚^0&ä+A¾l]n í%´—‚[ŠLeð,ÇfåŒÅr|Q¿ònµ%ÃÆ•ÈZ m3¾0CÃŒmÌ䕡ºKnÓj¡jh«¾~õ\×s]L24Ó€ü>b#ö*âOìÓûóðþm^±÷{ö9¶=ØÃˆýŠ}"ö'bo"ö%b"öbŸ!â}Û‹¸^Äï)*Vq¸ˆ¹í±µýÌTÄÌ"N>;>11~þÅÎIéX·ëŸŒsÿQŒû÷âÛŸŠm…o¹Oÿ[ò,·¨Zò½>[nÍ&•/½QåÒì”ïÛêË™d®e‘È–‡®Qå 3ªše“dÞsQßBÔKù/}=ä;pâ¾›ZTïvù®¬xÇÎön¬E¾?'꫈üŒAèÔ*k™y–ÇS¾À…ÒJŸo£<ö €NP»Ìo$ÖAžH®CÅzÂ3zÙ|æ _üóÄZ¿ hÖò_ l1¼ËD2UÐ_E{²Uq]¿:x×aƒ:h×±þ¤"W2})ðLA¿h§cÇB>3Ñ!ÞiÈ•ÎgíiØ*œtèf@+[¬Èš™ôçˆ5ÿB¾Ã7 žYø!žY‚xÙÈn¾k#>)†¿qœÊriÏ¥­’ÿB`sËi-eЬ†^¾ N rU¢C²WNtŠ€+×$tpØ£„¶¾—‚_ |¥XðA9v(Çnåb®®W"ŸÚfd®Î jÚ«‘¥š5à×p]¯êù^/¾c«zÆyý bÎé?§í?§mté?§í?§í?§í?§í?§ýw9§kf£K>}SŸ|ú.?S'©õ¬:IgÕ·lR5òÜe¾[žÐÎ>y‡ZUŽ;O•'tAŸœúU+©Cå¢vë“/´«O^ýF•ßÃUÕ¹4«šI}òë›T¢Ã*ohŒÊ¯ß¡råMR¹C[UÝ•ȨjQw©JUºEÕ£vë“G´MåuU¹DȺԶÜzª¾_“ÌSdË)ê¡jaZd]%‘kÏVÓ]åá·¨<Ù]*÷Þ$U³ºQÕZêVyù'©ÚÕMªVf·Êg4IÕ]jR5;U-kW•ß(Då"mRùH»TM7U“)FÕÓ\¬r“v«ü¤n*GiŠª­¹XÕ¼îVu¯ÝUÍUÿºQæ<u m9LªZØž*G’Iæ ´ÕàìRõœ\Uîÿ•ÿ¿IåMêT¹“\Tþ¤U›³IåQêTyOÝT>¥U«s±ª¡Ý­êh»«üJ)*áb•µ[åEuWµµSd}mQ_Ç–k©UæFµÕ×vUu¡ŽªÜ¨ž2ϸ­žg£¬ %jk‹Ú¶œKn*_aŒ¬³-rzVù =U-O³¬ %rÛjk·ªZž'U~TOUWÛ¨r.µ¨¼K]2÷’ý>ôû ·´| áŒ¼4ô2@ÃŒ0` ÀøÒï‹î¾ðñC?Ʀ0~ðñGìã`ü¡€}°OðÀB3Ý‘;ùÛä :ÁÐ †N0ò#K0¼B€ 6Þ!èRù ~(ôàý0d ƒüÃàŽžáðŒ@lèD@']"á ïHà"‹. ¸hdˆF†hdˆ†V40ÑÀDëú1ðÏ¡-øXhÆB+Zq|/¼8ä‹ÃqØ9ÞUnWÐ/˜à ›€Œ ÐJ„w:$Á7 ù’ •N_6ú'ñ™B_ })ी—‚MR ›zXnwÒ°gtÓèO7 ÙÒ¡™|Ø%šèœA{zdÒ—Í,p²èË' šY´gwÉ­’šFìiψ Flƒþ¹´çÒžK{.ôriÏC¿|èäC':ùÈVýd.0ËíUxEàW½"`LÈW‚|%0,·„öÚKÁ-E¶2ø•c³rÆz96-‡_9:WÀ¯ûVvÉm™Ÿ™¡aÆ6føWÓW\5ÀÕ C @5´Õ_¿z®ë¹®¦€i0Éú§".¶ý‰}úOíÏÅž¼¿®Æÿžºb¯fß“õÝÙ÷bb†o{¯´ïê»ß껿áâÜS‰ýTß=Tßý¾ý›}>þ›=ÎÏíoľ_Ûö1b#ö+bŸbߟˆ½‰Ø—àcÇ^ľ±ï?úî=~ê¹áþ½Æ¯f¯ñ«© Ó¨jd”5±lµM»U=ú®>ùõÚT~ÐŪþáU¿4¥Oîþ&Y³Ô–§¿UÖ ´å£^ óRŠ\Ÿ¶ºU‹U=Äò&‹ÈCm«? _dòõ’yåE¾;Q7Ó~¾™K^ä~¹‡wø´È\}¶\ÖèÿxÑŽ¼áàG¢C"tOn“õàm?íA7ß$‚ŸŠ‰àÅaŸlÚ³6^éЈƒFªIåãÄžÉÐOF§T‹¬šÿLø¥b×DdÌä?›¾lhf‚ŸM{p©|ÏýÀ#G ¼ Ñ«>Åâ3E.-9ôƒSŒÌ𩯊¶*dªÅ¿ebí¶Œþ*t¨Bž*àÊàM-¶¨ŸØ»™c¡‡üqЬC—8èÔÁ?ï ô%€—Dà“°Aô“- Y“„®ÈžL_ })À§ W ò¤„ðec& Û¤!WôÒÀMG†tð2ÀË€f43°S2gŠ8‰¾,dÈ‚f}Yè™E{6òe3ÈnÄ7Fd7Bψ,9ÐÌ¥=—ö\Ús¡— ½\dȇG>tò¡“\ùÐ/€wô ¡W^xEàA¯˜"lU¸)Á%à–Ð^B{ ¸¥ÈVÖ-—ört+ÇÞåð+GŽrøUbÏJd­„¶ýÍÐ0£»þÕôU#W5p5ÈPƒ 5´Õ_¿z®ë¹®¦€i@þÉØöïÕ7s¿íïìß9þ§ãè¯oÔ_ßè_ù]ã?½¾Ñ/ñ{Æúoÿ“¿cü'ý†ñ3{Šþ:]ªN—«ªiÛ¤j9ºŸUÓö”¬í(ê•_†~—q=ÈSÕ´] j˜s=ؤrÿCc<‡p=¤]æÎ¶Õ™1©Üÿ]ªVWŒ¬É$j¯\á¡rj/î“ÿŸ±=úC»dNê[N©-*×62OkQusØQU¼i­ªv×)U À¢j@{¸«ÊÇ-ö -ª&òx ³màz•µ4wUϦ]Õ³áz¤IÕžìT5à9 ž£uJ·ÊãM[`·¬0­CÕcG§)‡U}¯FUã ˜)ð~X§ªÓ.s}Ûê}!çX“¬ù%ræ{ºÉš8"¿¨gcËãíªê´Ãó&hÇpeR5ÛM²Æ–¨ÛxJæõa†7pSñI«¬§%êÜLƒÞ´Sªö,pÓi‹h•uÞmµ+]dÞï0àã½dÝwQO+Ù½ùòyû½áD¿7¼½áí œ7öò†–7:h´i诡“¾F[8úài€_ô" ŠŒ¡ÐòE&?úüÐ#¢C†Mþ´ù#§?zÀ+ße"_2ðÐ/€§ ™‚ н‚‘; Y‚øÏ/ þaè ¿`hg[€Œ!ÐÅ>¡š¡Œ‹PpBËp,þpx‡‹Oè„‹˜:yðHæ?þ&hGàÃhGœ”á[ Ÿ¥Ð‹B¯(tˆòLŒðü2Á.ÅÀ3:1Ø(ØXdŒ>ö¤ ýâàÞñèšÿx`ã‘?{æ¸Ë0˜lôJÿØ2ú‰è“Œ}Údø– dø%︔S2|LEÇT®³Ñ/•ÿtpS¡‘n:8éः“ßLäÏDþtl”)ü@_&reÒŸ N66)@ölà²3 iËfòäОC{¸9àåA?yàçA3½òÀÉcŒ`¯d,D>v¬BÆBä+v§ß„Lð2”¡l16(¦”¾RúJÁ/¿ ü2ìTn¼+àYï al^…Üب ºUÈY…ŒUÀU!cpUè]Ëu-×µ\׊kðj…]áeA~ ´,ÈoA~ ò[D»ˆÕú>ßÛ7O‚ØßØ÷6?µ§é¯7÷ŸQoΞ7BÄ×?wmö§oœüsgÎ?uÞlsEŒ+â[{l+âYËöcý\œ9)Î~.¦ÄåoÏ•í1¢=ñœØ_‰=l—˯³žž¡OÍÒö>uJ¨úÖÐÏÁÐ Ìh aÂ8™ÝË[U=RwYTÔl¿å¤¬k3¥CÖ¢»’>ô|ºdÍèy»É[ÚXzÐ> =g@[ë¼=¸6€¯¹ÉºE:Äš‹,~ØÊ=?ø"w ¼ÑßÛCÖŸ<,4›À/ÔUÖªŽh‘5=¢  ¬ôb ¯aXúb¡§¡¿†]cá®&x#OXƒÐ% ÚñÐ7 ¦µË£ˆ4äIƒGò¥a“4!pAèÍ Ú’é3ÒÑ®¦ð£°cmŒ™0ìQ€&ð"Äün„ˆAø,h“ÓL¸1Èž.þ‘'šñÈœèVЬ¥bMB'3<³Ñ=ïyÈPŠÝjh«_ú˜/ô,ë>ªƒˆ%ļ^)ß+Úkágmbn÷”G#ú*à[­Zä¨EN‹ˆÁó4Ê=Šíïßõù^ôÿÕŸýžëýgξ‡ýõ]þÿÖ ü¥žãý5ž{ý3g^—î¼ë—~n÷¿ó¼ë×tÖõ~Cï¯wyV½K·³j'{ô©OßѧŽ2ý—uÉ%v4ÁJ«¬k>žƒá1X|‡Æú‡ÀcH§\†EÍËiЛ1IÕCwè¹wªõèp…IÕ½¨§ªSÌPlr%8W"ã•­²¦¢·«ªWOÛ0ø öÃNªzsFUŸÚW»ªÚsªþºNæ3¼á­²Ý4®=°¹t<Äw茀ßèø5ªz˜\4«Úõ]ªV¼F3жQݪž3mפȺu¢{2^sJÕÉL‘5žGÃó¦Iª–ø“»dÍ̈ªf¦AÖ„BƱÈ3øÈ6¹[Ö·Õ¥„×ÀÞè!ëÉßì dÕÄwøÜ„¬'emç)§TyWU£œ©È3ÕKÖ¦œÿi'e¸âå¦ê<Ó?]ô”õ)ý7c£›é»™¾È:c±¬ÿÄu˜ˆ_邯·ø‡¯70ÞØÆÓ ã »h"ޱ;ít5Ú ðJ&ˆqfqÿ)ù"}~øÀ6®Ãøîþ蚉ly|À>Ѐg¼ƒE¬-ârd ¢/ˆ¶ ú‚à„LÁÐ †Oð)f…:è*bðBÁ 'œtúBÁ‰ÅáðŠo:°á|FA/ÃE ,<“!ƒÿ" Ã÷Hp£ÀBß(𣀉¶=J¡¿øÅ3b5F´A3›þlìiâ{,úÆB/Zq´Å|ñèO<ôã±e p‰À$“ˆ|‰È•ˆ=“ùO„OžøŸdð“ᓌnÉà7 Wð©ðL{äÎAÏt>ÓÁI'œtp ðU…°;ô2Å^½à[ n4rèËFÿ±·‘a`×ÙÀWˆkèçðÝýðsÈA<èçA?™òÐ'}òø¨´C†¥…ð(¿ÜBø˜¥Ú&ø›°O1ºÓ_Œ¥ô•âãÒ“2t-· ¼2ðÊľ¾ð¬€_…ØÇ G…°¼ª³Jì[¯ ¸*䫦–ëZ®k¹®×àÔ‚S ŽZä·@Ï‚üä·ˆvä° ‡-bocßÓüÜù–Ø£Ø÷$gŸeýԾþ×{Œæ ëï]õ=³Æû³Ï¬Dü/bÿ¾ïÈŸ}v%έÎ>¯ú{¿c÷åE µõZûæ3±ïß;—1nßg$*žý©X¶oüÚ7Vñ©ˆ=íqçÙgRöç"ÃΊ!ûÆöœ©ÆŸˆ í1¡ýüªoÞÔ[]þö¹H{\×÷ùGá'£Ë/^Ïvò‚³êv3×N^¬êuÓ6ˆ¶)ðÌçì7ù/G®КÁõ h¸7­IÖsÊ=6§-PÛvè çƒL>´Ï0É:ÑSºävÞ¹àïÇ\0¹ƒOdóFvot4pmÀ#ùî=´üàå‡NàO‡ÎdÚ€ EÞPð"à%âä‹‚ît±v —·—Üú{ÓîÌŸ~ÐÖèÓÄÚ~<6LbÌ&¡k*®A´¡ƒY¬Q<3ÿ±þbó ø[àS‹|Uð²`Ë*àj‘¯Š6‹X³€©å³V|е ~ôO“”Y<Ç/d¼E~hÁWüþ"žr‰çòÅÙšxG^œ‡‰36qž&ÎÑ„\v>â|̶–4ÉØRüÆ)r¯ˆs8ñÌ XŸÄ9š¨•%|$òÁ ™m¹fÌ’†È ä9b=‘Ã@ü>#ä9bD®W‘ß[è òz‹<¯â·ñ;Èñ*Ö0!»XçD.ñ»}½¿Û‰Ü­âÑb_ÿ:eZñ»Žx6Q¬{¶Õjý2•¿‹ßmÅo­âw:±ôˆ1i«‰¶X>_)ž·¿y‰÷BÄ™«¨³•Ù"ó|‹ß1ÅÒ#žEÏŠšX"÷ŽÈ%#ô¹uEnÛïWfµ^7ÊߣD}ñž–xï_ýó>ßpbu£ø¦=¼$)ùtñbŸ¿žZÛô¡é í`Èë/š2×kçËk}Ǥñ†oJ}Þ×gSÒ­í3{ONºS»~ô¬¿î8rHoŠŽ¨(ücí¢©œæ?>-’OÊ>—ž9©LÍþtyާ¾#Ñå·‹¦oЭ[. X>ÒGoºê®AÀwØàžYqÕµ½Õ>gêÜ"¯*Ö5\ðdêWúKzº¹x¸níHqÞìÚÈÔ'ßÞqú€Þ´óƒ» ëµKå5tK:/ç\fˆZ¦´Lð ®g×Sßñø¬ÄüuyºuUPÖœ‘ñÚÏ›”89GoúâõOãîˆ3œéϵ…ß¾zpÈ}ãíúhç¶uÎ þTß±uÀœ››uëÂΧ ÏܧÏ9çþº[=ß×Iúà{H½÷޾ÀcöjíàR¯¶°uè;>¸ù«+OÔ­3ç\œ–þˆŸSî5Þ¢ïlÚÁ•‹s‡¾óÂYžù|ºn5çrì™zÓ¬w\;x£ þÑËS>¸¢à:í`Ƕ˜ìe·›¾sòŠÓõfùWSåöQ‚n-¿Ú­YÃÞo?qQQî%àËq°xâÛYA;µƒo~ôÇ{Æé;3fß2èž@Ÿw2nHõÉ^¯[Kîø®gä~m”²Óœ{ŸéÉÙ¾ôïc–nh¾ÃS;xìý¶9-cõ÷Ž»ç›tkñÄŠ»^¢Ï™q³iÕ_ॻ3wÝ‹·'9ýû×UŸ]3y_r¿÷½»Wû|¤üd-«{ïÔ(mœ´¿>'±*|éºÙÚ%zÒÏý8õÛ÷ïÕ2˜Ã~ç«ï|ëpÄ€‡Ü|ïø¶eí§óívsøyNÁø×N»[¥¿—ô^ظÚïíÐyÃŽ»]ßù£ñXÔ‡ v¨²Zü&ý¨MVãnά–ÜÖÝà”Ç*ý¾tàû/8þ¤vèw6ýúáŽq×9*ouÒ™møáÏoyÝ›ªÏÙð»çŽ|¶<é÷'.è ÏÜ›ï¸ÜôÉóíËõÎ঄}ë_tê#íªù¨ûnΧ·UnëÝG9.ž÷òÈk‡[v>ts|ûGí·éeÛvßžèÓe§W`Øk9ö­6&Äð~ÕSúÜa+\osÌy[å8yjiîÓkw¾ê”/vÛÂ5Ëçéüæê¨¥º5'n\¥¯Ï½âÕ ²ÍàÉñÑ2{Õìç¶h‡ŠÜ~ùÝoèzë·=1öyE·flÚ¾!òa‡æ.òv¾ßlû<9nžž’P÷ǡڡƚä#æã¼ó³®ë^âiÝ*ç}S×tÝÞG~9Nžþæœ[o{¹E;ôàimù»?è»FÝõ»¦”3àN?/ýM}îìû¾óè7†kåxxæõYõ_.û“vèÉOôUéÉú®ôdŸ¦þA·¦½"F‚>çËO 7º/ýþÇ7ù¿Q:V;´òÌÝã·îÑw-ºkà5ïLtŽŸì´ƒ%%çØÇxÒï² ÿ íжqÝCß}NßÕõä¥Ë®žì´²ëœ˜¯¾ùöçvY+ý¼ì‰Þ«o-Úãô3TZÍÓw_Xy"¬0Éégá¾}sõ9»&_üXÓ>k¥—'fÄýeÂÓÚ¡“/ÆœócÜîö~æÔ–O²uk^èÌÚAËœãÄ|ÿ» ÖùàK?/ÿèægߨ«õ\xlþ¾5·ë»Ë‹8òP•Ï.9ÿ8æ¹o\wË“Ÿt8ÇëZéßgCÇ>èsW Öãqë/¦ï^rQ÷ÓC òg­¹lëÈ#ÀK>;'Çû† ë´ž ^¬²Uß-†ûšïíütkòí‹’mÔç¾dù¯Ï³: 'ÖI¿>»ùΆ—òj=›—<èG»{þטßLpÚ=ñÞ€8ÍäXgæ>óþù£?Ó.tïô3o1®“~VÎ;ZOð$Ãô¾r¬Ÿ{~¿µ¢À«Ø9ÿÊq¦ {SLðáú\1F/qÚa­Õm¯<¾\ë ¯þó­ï™>bΖ{f9åJ˜Úþݪ÷˼ ‘†kÖø8ç¡ur\ÈîÛ´ž¤Ž·¾ØäÐsñ’›bŠ—9Ç¥æsžy_§>oæG¸^`‡ƒŽÏ}{j÷]‹¿ÔzÔ:¼ç¾{†´$Ö9õuÉk½7êóZ–Y´èL9ä¸x~µûà–™S´žº|Q8í³êÚs“w Ö­në&=7ØKŸ÷ˆçžýׄ'Çà A7=ºÞ7Lë¹ëíž¿Þ÷£¾§w­eXUŒn½tå]+ÏÑçÝ[áÿü­›ãx/lÝýÂÜŒ#Žyª§¹õŠ®Òúï½hÑìiüÙ©¿‡¹ý5}‡Ã/ó˜¼+N˜í׆ëå¸Y1Òtá‹Ü÷YÏCn^E‡õ½ÓGošYºÊ9N¯ïYË-éôϲã %œ÷íz9nV<¸?jƾ©ZÏâ"žqúÞâµËµ%aN?‹(``»6T­ó/üîÆÌÑNÿ¬—ãeå¹s¯:½p®Ö³lCnó–Kõ½‰3 ¿~¼ëªAéóîš4„[ÖnðäøX™æ5¹¾NëYe›Øô½/ïúlÏé!º5à¶I¯ü£ÝÀËq°òÝ- Üÿ’¢õl²&úÞ–9wä˜Oö]ÚòIíäst«Ï#ޯܻRo³ÎÈÓàI¿¿øÞŸ„äNÿ}xzËLÉú¾éûW¼ä{§ßfDçíˆ?›Ÿž÷VnÃ+}ì#ÇÃK¾_^úvÍZÏ7b‚»^ß—[óГ¯/ò9f¿·Ø&x‡¿š×Íšw"}‚}¾2œh—þé½ùiÃVh½/ùèÖ7ëûæ_1ò3§¿|m ”6\3%ó~h—~o‹ŸáÖÔ¤õ^6>ñóáïêû6Ý÷þS/;ìØ|_ÂW éw/ýÛÖ>öû¡¿ýRëÅËsÖzèûN6ì혶Ð9Ïy­.=Õà˜§šoë^¼úØ>ð¥ŸWy¤w ú¡Ö;濪æÞû¶þªç×ÛȸV·ÊøÞiÿvéçU\åóæØµÞ‰÷Ì u³ã¾yµ`åÁíw,â~_¿h¨h}Þw;Æ]™q;xÒß«Î\rôÙ«µ^&A¯ªýÕgß3ü¼ýÎûbDG€gäÎqÕ.ý½ÚøFÒõžÑzG}sgäVÇ8yõ¨Ç‘kvÚ÷ºò¸ÏyŸfE;çÏvéïÕbú\†¾Q¯.¹®pþÚ訊‘Ãæ:Æ›ýþvøeƒôï×%çÖÒzE˜4óýµLóUOEz9ç¹ÐK7ÊuŽ“Ë~¼x ¥Ã9ÿlþµöè‘ZoÒ]Ÿ¼ñüïõ×îo»aÞÂ`Ý|eð¬Qnzsu†÷g↠ҿkÐzË©¿j½éÇóô_Wë¯m˜¹~Ò=Iv¿êÍ KJcÚ¾^úÓ*FIñ'Žy¨7÷ëÅO|q¹þÚ{¼c_f[ õf9€'ýjýÄ÷¡#Ž;î«Þ’DWóï/Ò»Î|aÑ}¯9ý4=Å-7òuǼÓ|…-€wÎ_¤¿×V}•›»çY­·fÂ׺¾×»Mûsñxo§¿äº¢•ë¥Þìݲ?XÊÀ—~_{à‹çNÿ¥Ü©‡Å]{ðH°Þ5æ™ñ^}î”Ç0ÿþ]égñCóòÝç½Òöt¤¿×ÉûHë9eÜ,¯ õ®éßV?ôÊBÝj G.ÒF\tÙ^ö×ç_²ìª/Nl”þ^—ã9ýü ÍZïݶ°ÞU¨ºÕy_ÝtŇ.ƒ¾sð?ý‘ïï>Êxß(ý¼nÇà‘Ë¿úZ뵉w¾Þ•?½ìáô®ãa̓#ëVˆ_½/ÜyŸÉ¸ÕyŸÉŸ#´ 2þÖzÅ.ê\£ÞõÃÝK<à©[m3°Þ,÷÷ÀKmŒ?þG+z­˜<òÁÓ}ÿÅ‘;2Ìþ½æÇï{§âüय़6>þÚú…8ï—6ÁôýW½ðAÕ­ºµ6þþ¯"Kôù;G¿üî_îOúgã¡K'®,[«õ…|l½FßýîÏ–Ï(Ö­b7Qü¶c}˜¿¾tFû-_'ý´ihxO˲±Zo«p@ž¾ß×üÛîÄGuë¬ß]~Å·ßÙÇŸsÞÚ$ýµ©¦ì½1¥Ë´^qw4éû“âw½Ðv•n}ô’™OtsŒÿfÛ¶îÖ>v”þÛttPÌç½Kµ^óÄíú~·ëV±ËpZoÎÌk*xø¥>|¥ÿ6«}[ïŠ 1êûg—L[áÓ®[mÃïçzùœ‹Ïñ†›¥ß6oÆ~­WÆåúþ'Æ“¨[›z|8ݹîn–~Û2¤çâî‰ok½+'ìTpLßo½­æ³Ç8çå;<¯ùå<çx±Ï{›¥·Ø¶Çè7Òǽø}ÿ®[Ó§´mvÆ÷Æ–·½kŸÏÁ“~ÜÒ»Cté@Ƶà^Ó÷÷$ŽÞë—ç¼O—ÚÓ›mÇ¿uΛ¥?·zÛær¤Më} C¬¸úþãqŸ?8ì+§Ükm ôæëoüü‹Eç9ã³ÍÒ¯[·i|"4Î>?9Öáýß¼ßY5Â)ǖŧ:î§Ò¿zùúvŽ`~íà3!W?pÎ\ÿM†5Nþöõe³ôk‡n§öh½å!ç|;@? õÓ­Ï»9h¬Þ,Ï‘ '¶HÊã¸;X‡äþò€Û¨¼ÕɬCê¾VãxéÏŽ_|e•sÞO~öÞícîÒ ùÃ¢Îømû¨uÒ9î¶Hvˆh=áb­7dëÚÕo¼¡¸*zéðÙoëVæû¼içÀK?vôLÛµäØh­wÚo{ ­ñÅ9OÚÏõæïW<êãç¼?¶H?npíM+^úVëe3ÏL®¸î‰!?<¾S·nc£ìóm9¥ÿ¶Mý“ËéÖ»µ^Û21Éá¿Sƒ7>g]¢[·ÇT»¦»ëó‡ÙR7o‘þÚVs|MQËwZï¿i>ýC~À÷Ü3÷w|§7Ÿ9qAXÃUÀI?m[¯_}¿±Ñ'ÿðáà‹_ôÑD(‹™w£n}u˜Øq:ïÓ“îµ÷~ç9åVé¿m_¯|dyË¥ZϾ"RÔ¤^[tÎ#÷ǧšªÛFØï/ð¤·ßpªW:÷CH6-}F?¿léÀô|‡׺Û,õfÛtáê´ïVéÏíe©µG2œzôŒìúØè§¨´¾¼m^ŒC޵â7-Xo¶Š‡œ÷ËVéçí(‘úd·SžO03¿§˜yM^ó£ñNy´ÛШFŸ'¦÷EÄ[¥Ÿ·~ê¨éÆZÏn÷-…cosŽ“ÙK™™9ø;ö9[¥Ÿ_¼™´ž­-‡‡×,põÈþ¢[_{ÃïO¾q¥Þ\ôÞ‡ãæ/ýû²Ü×k=mÙ/n6ܯX²óÑsäëÍÇ¿ŒÝ¶Ð œôï˶ã§§]žÿÜþoÎÑ,?´{sl¢Ó.ÿ‡½ï¯²ÈúÏÞCzï$7ý¦Wyc%(*º¨]‰u³ÖXV±ApUB°DDDDDE ¬J\%7ºê\ å)!¹i ‚ Ê*¢Âÿ7ï™÷»¬»ÿçÿíóýŸo÷ƒç¹äΙ3gæœ9eÚ)›ž8éÞJÎK~7å]SÑ6‹*ܺVÏÃßøº¹ê ;|07o}ZÉQgÅלè¾q¿0Œ"´÷Õ­~ë—iæzí`w}«ÇöäÉíY¼ùO_wé#áÃÞ_øÒ^‚úDïî”kuËc'Ée>8òK{ÙWg+ú4_2ËúùQ¾°háu÷ ;™}»!Õmk2´7=i ÝÀ¿XvÙÞÒTS®[ì½àÒŠøÂô…TÔ#ºvÃ);cç3ÚΫîœ÷íé|ðç·Ïk¸÷y¾î±#;® ½×ðQžèÙMv•¶³^_0åCAS¯ÿ`Ç×|]ëŽ7\øºâNôì~#íÙå·-Stš2³pÕÛoò¡„yõúݪ_ÂM‡?àqÒ½»¶ªyÀ‰ÞÝŸè*ÚÎR·E›x‡eÝyxÚ¥à£6ûËîã« ;¼ö+;ѳ'bוçõä+zŠÕÏ™7ò¡âˆQ6~€¯[sÞkú)&?؉Ž=4´~',{Ìä÷¡Ê‡÷ŽvW}¹ú -]¦ À”#v¢kÏLï×Îm?EÛqháã³/ÈæC“7wÿ~úÏ|ÝÀáúƒ¡˜Å7%ÀÄBy¢gnF&i;> |:ñ®gCƒ’øº­têW”_j':öÐøi;ýx¾È‡N¨liÚëëÖ¾ä®W^ÔŠÙ°˜bM»Å\¯³}{h½ÔméOS>M¹ª÷ƒ[Oâëþœt¾GŒ¹ÿ`ÌÓß´ý{lúŒZ%ÿ„ s.c‰éHz(?æïÆ‘ø ‡öa´•·vôó¡s.þâÓêø:!f~÷ŒšŸ‰ú‹’ƒë%_;ªr¿iè‚§ÓN;?ƒ¯Ûâß¹÷wð+ë®|ïÕÓPø¡ÎòÛgk;®Î‰ò8z2šuâ+?ÚÇÉLðRz=Ñ»÷ü£ëÁ‚ÚŽ uCœ5¦õYPÍ×íÈ´\và %÷Ö½{É¿Sûb—¦âbÅwM_¥¿Ð~+êý©yÝ Ÿ!_‡ø Î5<(5ÞÕ“nøyâ%|èöÆç^fSüphÿÍîOºàAôïn¡ï<…‡¾ìxj>µäí[¿á…Ów|üYiO™öèz¢w¯Ø–y~«¶#jýÛÏO|ŠÝ·ÿ™ÌÜÇxç¥ÛŽtÜò¢š§ë‰ž½NÝ!QøBéÏh]·HžL¶Kþè¼áÑÒÁ©Ÿ¨õ Òj=kÑ·÷먯ֆ¤iã?Þ4åÔŠoùÐÃ,ßqï¼<©Õw¦!定÷ˆÎ}ºYq¢6þ¹ýú˜7‚ùÐâ£n“øË¼“Öÿ•~Û@tîÓÝ´[Ô>±§0åþГOÍ˼Å÷N±{P¶òûÕ#ˆú¦Å¶ö¬¯Uû€Â{¼k\ñÃ*±Áúœ9:/ÖH¥çHž(9ºø£ïr¿G~sûOÚ8¼×ýËð¡ŽËwßaù ï¼F<Ñ´‡äø¢ñEßýMߨ­¯ŠØ‰ÿY|ˆô¹);ï>R´òªµ®+´Ð—£>ñE_sËt¸ÀÚøƒ·¼pkŠ’'[Vüñ“Ikyç£+^n[Ï[íºã¬;l.t%>é{ð¹ çïÑÆoõ?0ÚʇÞ{cõŽ ï6õG'jÛ_ö0û¿P®W™ôÝHüÑ·<è]«jµñ‹þ”2o¦ú2¯àãïgòÎîžÇϺ’+½ºQòƒ¾l4GÑWlþ°šXw8µé:sß±sËwW½9õSüÝ~ÁFÉ/Ä?Úx–ß_Nù4@ÑóÈÛ¾fŸË;û®xùè=¹j?ÙrË¢ÛÃ78¼ß?%ì¼)ÚxDùÓ«ç„q§ÇÝ'œÔü ï|§üƒGW¼¢ä×F¢»ƒÖ5µq±ë»’;½ NXòÚäo%d»ïWl$ú;ªŸ;å„Óþ¬¬ Hr|oÒÏ黿¹—£Ðú´!]ð&>pœvó¼Ñ´±=/&|¶ÌÉ~ÁgÞñ~$ï3Bå©õ¿DwÇ9?[·¸[£}y”_s÷ÞkVóÎÒ€üâÚ¯º‰¾Ý<ª×ÆzN¾æá'1.þ¹üzAŽiv~™¼lMÕ â‹n¢³Cö{ìµG]WlîÃ8žÙú]íOªï ‡§C­×u]Â*)ߥ=çù›Œ¨ Ü©³ÿͼS_æº]ÙEÝ’~Ò.’rB£új\Ãb:8×¢ø›ð6öÔ¸vKúŽîÛ{Â}O™|:6oÃÇ“ÆpgÌĺ_ž2ñ·±gÊ¿€/ì©¿âÔ´P_ÒµaÉ›òfŒÖ¹¸39zUõëþÜ44v¡£Ÿ?@|„zDÏ~y7¸5¾§Éõ\gæcMZ^)—l>{?=£Î©ôL7ѵ?zÎ]^.Wøž¸ üŒÞvs>8¥ßoâíÖ¸"ÛX¿WvFѽ?W(ò‹µ1}ÙÞ‡;‹WÝTµö(·…¼›uÉŠ8cß@Ù=D÷þ©Ï_9·s™êw¨ç[ì¹ë¹³"ÿ”Î/ÊT?´sÎ8ÿ%µ^Gë€Côï¿æÊÕíj=lôЭ+6l=;«S&Oβšt´5ÝþéÓ9k”ß)×uL;¬‡ø£ñE×–7p¯Ñô…9î–6úô\±cɧïã±?gÎkÛ­³ÄJ®¿Ï^âMÓÄB֫ڨܗtθ§b†ÇÜ&¬7Ì«MbÁy;Ê_ljªúiÎ#nŠž÷/-Xxw6Äõ·¦æ™rß&¬ê)cZ‰!÷¥]‘dÈÏ^âM­7~ðû—OÖFïØ|\î¼îá³}ËOÑñ)Ý ÖrhßÖœçæ8ö_lzú[¯Wj£×®ƒ'´…;ï¼kvòsAŠO_ ¿ãÃ)¯˜ûÍjˆ6 k4j©6záûðîæÎùûÏ{qð$Ó>±mþ°çžó:L»~á­¯ýåÛò\ð ¾Ø4¼mÍ–÷šòkôdý@w.šõÅ-ÏÕ*|Ī[¶ÉŸ á½¼¼äEÅŸ½’?„;sxTñ{î¿=ëáî\Ö9àê+ÜFö˜Úï³ã·.ëµ½’O¾êöûz‰6ùÛÊ%‡ÿÈÏÔ½ùÇý{~ñ…béÕoj¿ê“|ñÑã•×ùF›tÑ·“¸S®—˜ô!{Ií_»îÛ'ù†Î—˜ýù´’mš’ë«ßúåŒ}O¨ñéj~þË•\¹'²7"õEÀ“|Cö±6²I_0çΗÞz à‹…Wÿ²/Ò¿³¨uʾWúh#¯dݼdàî\»(ë²mãÜ&N#Ü” æWŸä :Ï£Æááñ_Ê~¯ÆÎÐâ¹­Gúß«yNö…:Ð'ùbû‡>¿Wë^#76ïÛ¤÷¿7Ò|ØíŒ¹3SÌ»ÜO{UÕ¾.Í'Åý’/„Uwí.óÜëp¹>4öù.Ê·Þ7Ïú®YÅ }&Ô—üаýÙÄç´a¡US–ðáÐO|Î}óRž“2û±Þí³µ›?B=ÉÒ/žÐÛ6o#Nºoô”CÜvxýàS}ü± áùÊ7¸ò‘9´¿mŽß0í»™ó¡Ë7wÛ5ÏQòMÚK&=ú%Ðz¨¡W4çÈm¢1ÇmøÄ³æ|žcê“.¨üRZ—qWÉ?+t„5ç[‹›®øúK>>?k¬vÅù&wéÇ•g*?~ÿüŸßH{ÎOÉ7ú±›5§îžk|øšåÖi[?ã]ùóûºQëuÄÇ.øH¾¹@'ˆæ¼í›³2œÍ|xîÍw¿`Ú+]$_”Gü[ûÕ&É'꣩9ϸuÁ¦>Üö›§ß<Õß”G]Ó^¾à­¹³ùÂúPÔ“ü¥o`kNýxZ<Ö;ÆNÞ+íœ.hÁ ÕŸ+;!a‘íˆÏZ%¿6I~¡ýÍ.¶ÌçëûÝ·:Uá/ÌÐÕƒæ:D몟Weï G}iþ¢hÒ†~Öñá×ýK>oœ©è ÏU´êÇ= QOÚ™$ç´¡OÿP5Ù^À‡ßÞ×v°xµi§uÕ|‰þ…¹Îg®Go’ö&­iCâ”cL5vT-qn¬Ö&JþéJ£R­SÐù1Ô'º÷ ª{xhC‚¬g½É‡‡üœ7Ôß­H9Ó%NÛeߡ~}ÍÖûPèÝ¿2óÜ k´“ /mHÚcU_±éÚ•5¦¼ê§Ý²ŸPô{—èÞÏêGýwïÒμ›×Í}w‘6$Ž U®×NÛçõ`×›}|Xÿ¼k™i_Ù‰ƒÌo(=h×g’²ÛÞ%¾èo—kçêîI6ôûS~W‘0Çh‡Ë󻛨‹\ÍyÐä“w‰OúϨ7mé,¯kCgÞ{&«6Úá#îË„3ÇAÚsƹJÀ‘üRµHx¾Zý£·¼uõœÃÚPÉÈ3—~rЀËGômÿR­Vú~æ¹fÑÌ,µ^ò®ä'y>ë"gm(^w¼vøHÊI7|ÝnÐÛÄ-ŠJÞ*–I_ûp$_Ѻ‡ö[ƒ.îk…¢Í”ý¡ùfÒɶïÔÛÆÏ»Ñ8 8Ä_Z·Ð¨Ú ~üY3àòíwûwîzX«“øÙàÄ~ÜÎ[…UxÉ-€Cüæ÷2.-ñ';´A©?%\>r¦¾a©$é`ûú|”ØÊ[?þL¬T©u×ÍÄwŽ5Wl8T¾N»LòÇà;úBŒŸ\¬| /‘º¼O÷fâ3Ç1.Õ.—ó`ð…i⤛vÙèÍÑ?O¯ã#ò¾ÃYt~‘Û†¯Úvâwa¼õ±Àê8ÄWŽkõ¡Ú ¿S¤}~.¡s%&Ÿö€Á¯­G^ìxm‘â×ÍÄg޳Vµ¯Ÿ±I»Ròë 8Õá;l¤ùˆPÏ.×fP»ÜeÏúƒš_Ñ3ZŸÜ6 xÄ_Ž «Þ_oêÉÁ+Å µ=Ò’8*ZÙ-Ÿ5}´>{Œ/$½8Ä_};tª žûè雎^ÇG–œÿÍáDÍð[øBýx<ì€ÍÄG}Ïè;L»m΃™væH»Û«·t+?›ÚUrSúQ¦ÞÛL|ÕG~´êOÎÕŸƒDª?o¬½ó.nûrÖ½›†ïRãMü©ü²-ÄW}i›üè¥"mÆ#\h…Ÿó—ï;O}Vùïß}ò\\5Ÿ/Ò [å7n!þê·†&hÛ ü3ùÈ7SD>þ·‰Y3ÅÊ[þÚìw×E(O|Ôû˜Øˆ Ó¶ëÓ¥ÄìǨoÔHçÌ'Õøˆã¼¿­Î 5²t…Ÿ-Ä?½3^°ê³ûµíäïóÑÔÙ›K·ªu„Á$Ñ5.ÔÔ'~éõžuÃ`Ò÷Úv]‘ óÑŠÖ…>ï'šþe«aGl!¾è¡}2“ÎÛWý~´$ÿ=sGÏxumUø6Õqq‘ǦîÑz;àßôˆŞ¼`ÚÙÛ…:yb­—ß®¨uLÉUý‘ãbèKó~Òâ—²Cµí·> W†^?ãÆ/¾‚Û> ò˜v[Ë×ñ}§Î®ýj+ñE·àÆØ]Úv‡Þ}ÛáœüÕÉç^™ù¬'ü¢—ĉ;Õ®~½­Rñ×Vâ‡nÚ@Ó¶ŸS/NòòÑ…ô|¿¸Kù-k&‰¥_EW±zò<¥·ŸtËsqÛ¡•NXû]vàÍœÝr›pÝÞæ­ÍS¯ºsV<ÊtÓým»Xvy—®¹úç·'«qÓ—™Œ{ ¨GôïÎÕD+z&4ö.úa¢ç›Ïœ“pþ©ÚßF|±Q®GŒmX5ヹ|LìÏú†Ûhÿˆ/È™»»õÛ×Pžè¿Q¿ö“¡ èדfò±”8·²ùÜ&n¡TŽüýýÅmDïúvÓÞ®)›XâÉÇð¿Ç—gpÈ敼åúõsÞüë”'zogÚèT‹I·h‘þûö™tÓ1¨û¶ûÄAŠ0“ïZ.Ô/~à?lX ˆ1é7°8^¬àð±‹<º(z¹ÁGêeË¡K_6ýÆâûUâ¢×_´ºÁÇ~ <çPl=·|â +›ÎçÓø¢<ÑÝ.Võµµ:/ÆÇ#?¾q†[‘Z¥ûüþ/ çÏÛ‹zD»~< õ2¾˜í<ÂÇ ×ÝûêŸ|¸­öÙùs>ùˆß¯_ó¬P|6@ôçrý}üô;Ïšqp5·éÛyi|~RYFî›âÍýnôï?õíÆ×ø%Çc³Íþ_}ÓQük”ãØ,Y¾ƒ´ˆý¦¿O žž¾˜àñ êåÛx»e,ˆFù.`{&Ë·^ZމÑFñ ôØÌ€ç½TƃÀ|õi”oÉ·Œ›]b2[å»0(ù[eLfÐÙ|'fºŒË†ú!ò]¼bŸ|70ƒ$‚‚ké ™à."šÞ’ A™à‚:щõò-dÔ õ‘±™g»ÄfF™°zg& épÀ œp” ï’ï$[d|fàØ‘€ 8‘À/¼|£P'j¶Œ¶£!ƒ¢‘Žîñ™7¦NÆg¶“h4ã3£Lì!ùNÚˆÃćлÊzlàüÀLÀ8'tɸù>Þ½—4]Æc“1äêe,拹^ÆbFýà˜j¥¸r©2³E¾‹‡tÚù&^3Å^žä#ã>´¹Ä\F:c©ŒµŒqÌl’ïÞí¡÷OôØÊè«°³*d,å.ëÙuw.»]ÆMF[9ÈÏqÈÉÀ5må¢~^4Å˳“ ÈǸä£n¾]Æ:ì¤ ºdÜ9¤­¨k…€[¸…2nq½ý£Çœâ¥2^±UÆ›è’ñŠÑ~i ÅÃ1'Ê€oÊ–¡l9úZÞHo[—ï¦X"þ^E—ŒWŒö+éÍjñFŸxw¯ øW!]xÕÉô‘þàÕ |M£ŒW,dÇq}\G7»×ÑÿI:ZÌ×&9®¨«¿]Ü(ß½ïׂg'€îäûµ ø€Nî{dLÔ÷è’o×¢¾g½|¿v@Æ@Úk©Œ3€f¼Ï{¥|»óѧI¾]»[¾]Û⯠i?àã‡ò~ÀÝ¿BÆlý‘H–o¹µQ ‚@´Ø ß®µË·Ý0'ƒ30ƒ3é`À¶Ëø€œBP&8† ÎDôcbƒ|ÃõBCdì¦f» õÂP& ¸…÷0¤Ã'pÂQ&ÜNïÃé± å;¶€ 8‘€ ü"Aë(à…:QÍ2¾ÚŽ®Åéè.ù–-àÆL—1œ#ÖGÆp¾±]$&ã¦ËøMƒøh—wlí$>3ãœ`'Qš¼[dÜ&”Oª§¯I»e̦¯iŒâ5‰·kEÌ×ÔOŽ©2^S—ŒÕxiH§!œÓ+èÍÚtÔŸ"ߪ]Jq™D,X—)c¥ŒÇ„qÌ®™€‰ºàjA[ôÕØYHg!…ülÔÍF_³1>Ù$âE|Øäç€N¹«\àš‹¶rQ?é<ÀÎÃ÷|ÔÍǸä£n>ÒHvÒ€]€¶¬H[Q× < ·p sà5Ò[¿ú[¹€S ü‹÷ÑÛºz<(À(E_KÑ~iŤÒ߯¾e([†²åèky½,âBéoã"]a—qcÑ~e½õ+âDUa\«€ÒU€Wm¡¸">T5àÕ | Ò5ȯ²AèñOèg¡›…vÕ½BÇzSèHCç }'t˜ÐEBï¸êCÏ"ô‰¡?„î86†¹¡'„Np•÷®1Ë…œ2Ýㆠ7d·ÓBFòÙËÿUykÈZC®Šq2Ô—†l !ÄûìÀ»î+eLºZz‹[‡‚ï~(è a 6<ʇ·ÈØË u$ø'mDuHs¼ƒ¼8Ð7ßã'< ø-a¶Œ¥ÖFïFëïBÛå›Ðøž¾LG»émôÆs&ÚËDýLün©#s'KØ­£”Ë®¹Šœg!“ÇŠ|+úcEB”)Ž…À±ùEMô¾¤ï ø£ŇèÍçÅu.k§7œEüfñŽsU½Õ,b5›¾Þq»ð¸]xÜ.ü÷· £%½;èwýmà&ŠuÀ€Ç„hŠ+54›€´{2>à/÷vgªNÆ3°Ë÷y1·<\ÞèEy/¤½Vʘ Þ€çúÞhÏíùÌ–oø"í['cxŽÉ8Tµø?äû¡=ÿZË4òG:s= AÆ¥Ú#cy¿@¤Ñ^Ú Â| Z*ãxf0ÒÁ€ì1 #8…´ÉXžn2–g£|xLÆóD½ÐŠg¥ÇóD_À[X‡K<ÏzϰùÄ6h—1='e"7øFß(¤£7JØ€À7ct4Æ4úŒoÐ ãFlˆŒo€vbQ&鸊o‡1ŒÌxàñèOò3¡ù˜¸“Dà„òI 2Ž'ÒÉH'7Rœ¬äÝ2žÒ)(Ÿâ ±›Šú©³)~gॡŸiH§Ùe,ƒ:z75õ'¡Ï“ÒJŠ_M± 2€oÆŸ¸f:Ht[€»u-À= °³ÎB: ùÙèK6Æ+å³ÑVòsÐVòs;4É®¹h+c•‡túXù¨›Xù¨›tÒ¨[€t`[ËŠ|k;©‡BäN!p,œ¢Ùôβ;8wêqDE|ÐÀ,EßJÑ~éRŠIªÇJ@_ËP¶ ´,·R|3ÏT›€tð«ÜJÀ­D›•³éMg3´ °«_…tÕŠ!â9ˆ¡B-Õ | Ò5bþ Ý$þ ,ô°Ð¹¡rŽ»ÆmºSèM¡#….zPè#¡{„Þ¾º>:Cè ¡ Ù/d¾óB¾ ™.ä¹áBþ²öXÙê*SÿU»NÈJ!']í;!…ÜŸŠ÷Ô~sÇœópÈ7Â1®Þè“7ò|šé­p?ŒY€•â „u‘ùߢ,S76šâËÅcŽ%bŒÅÜh¤Ø¶)(“ ^I¬´fz8 8f£|êæ lh• ¾Î.yø›³+ÊZA«BÔ/DÙBÁkàƒ"”)j¦xzZà[ ¼J|(G x¹ý+ì2àQ†¼ Ô© 8Ç×ëÜŽÛeÍnÇí²ÿ»LøYIÓ.šÖzœðÃ81Àš,ãM-•1ÖQÞùî2ÖøÙõ=Ð/O_<è :y¢¾æ›Ò^ ‹úæùíÝ(ã‚¢¾ÚóÏøt‘ØðNñA}wË8S˜O~€ï×EâÄiÿ6—ëh/ Ñ%öð ¢ý@´y„9´RÆÌ`¤ƒ#x@ÆfŽ!ècÈRWÔGÆœ‰í2¾(Æ!õBÛdÌ)Ô CÛa‹0»KÌ©w°#'p"€D‡ŒA 8‘( ¸‘ûd¼u¤£7Ê.ã­£è6Š=%DaŒUÆžjw‰¹Þ(cO9dì)¤ã:dÌ)ŒA<`Ä#?ýI@Ÿ0® çà•ˆ6‘N¼Dàd‘1Ö‘NB:Ùâk 4LAû)B'a SP?õSA‡Ôk é4ô3 é4à“Ž~§~:ʧï¡xê“eu¤3Î@:øfì£xR"nz&`Y|(ö¦u-À= °³„îC: élÔÍF~6Êg£­äç ?°s+°rk.Ò¹h+é<”ÍCÝ|ŒC>Êæ#tÒÀ³é´mEÚ ØVô»p Q¶PèQŒiàµPŒ+=¦ð/½JB(¶U òJ@›RŒ]iűÜEìö2¤Ëì¤^Ê+(ΘˆçªÇpÜ Ô­ÜJ´_ |*›)†¼Ë°«_…tÕ>ùÕH×`¬jP¾é‘s_ÈdñÏÐÅ®zØÐÁBÿºêÜÿ›¾uÕ³®zUèTC— =êª;]õæ¯é?¡óŽÕmBŸ¹ê2¡Ã„î:Kè+¡›\×çÉþ ýcèCߺÆÐ3®:ÆU¿üšnqÕ)B—zÄXGtÕÿ]:BèÃzÁÐ †>ºÀÐBî»Ê{CÖ 9þcà› àË +eÌ@üæÞ%cÞ`Ü<1¾žàWO”óÂw/ð¼×n°hïÝDq}Pּ苿¾õ ÐýôGÚºŒßŒ²¨øAàå |7p‚ñ=x)żÑãõÕâÓŒƒâôé±™1§"Q?|åp‰Á‡ñˆi’1÷ð=ßã_‚°›K"ê$¡Í$üM¶ÊøxàÅÌ©Tà‘Šr©øžüÓð==DÆÀk—qïš(î]¾gà{ú›2ë(ˆˆ¡bAÙ,à“…v³Q.xä ‹¶òP.x棟ùø-_|G¹¤ ÐNÒhߊ´°¬·BàUˆ²…]4}Šê(>ˆŸW øÅÀ£cP‚r%ø½ÔJ±ôXy˜eB~ÔR, 1í*P¦b7M½*À¯:$ãá{ úP#æ‡Pðâ_…Ëw”Ñm7·ÿ~»þ“M¬=ÿÏlùãvüq;þ_µãë$Íì2flŒ&ìzÈ‚ Vë ã:uÝ1'Ü›eÌXÌ]ð©hê¼=‘ö¬Å<ä‰qöD}/”÷,/àåùèqÈë"‘üxàüx”O@¿0V À7åÑFbʼnMÄ8%~ê'!„t2ÒÉ i2èŒt Æ5ù)Ç”Ý2F,h‘ x©H§^ÚtŠw/b_¥#øéÀ7<2 øLBýI€? é ¤3ΰ“XÎDÝLÔÍnÔµ oÔµvÒY,¡7Ñ—lÔÍF:å³Q>ù9Hç |pÍÅ8çv.Ò¹HçvÆ!°ò1Nù(›t>` ]< .@¾i+Ú¶‚¿ ÑÇB”-ŽE AÑtбW´‡âУbôµåJW‚¼ä•bìJ‘WÚ%cÑ"]†±,CÙrÀ)¯£8{åHW ]têV n%`U‚N•à“J;Å£­ì*”¯rʱûª‘_t ú^ƒò5ȯAù1—…lÿ ýûÏlw¡K…ÍîªGÿ™þkö¹¡Xœ†îúNè:W=gè.CO6¹«þ1ṯúŰυ^ùGúÁÐ  ç?ÝÃ?Væ»ÆÕt•õ†œÇØê2ÞíB®òûص^WÛU^ƒ¿þfoÿX[è~ÅÌœP!ãrƒÝ1¦îøÝ´ò@_½ö2Ó*cm#Ï7ZÆÕîq´‘öo1%{ ê·ƒ0ÖA]2ž¤•âd‡ ½&Š‘=±YÆ“¬PŒa蘊!5Fñ¬c+(VdêÇ¡|œ]Æ Æo‰¨—h'S, m&á{2þ&ƒ_ÓQ7ùé]2N4àLBý üÍÀßLði&x9°,(kA³ð7 ¸ev¾çà{N±cn3™ny¨“Üó0¦ùÀ)ßósÚ*Àw+àX1&…ÂFbô£m” L)p+µËxÑÀ± yåòé{»+P¶ ¿U¡j”«ÆoÕ€W“,i'þýÿXÇþŸhïþ¿ØºÿÊÚõ¿óºõÿf[÷²[/é1 cO§8¾ÌNSyB->MÓWLkwð³;òÝQÞ#Ÿ|@àå‰úžÈ÷lƧ‹¦¾Ò^H{¡]ï Šýëº>(냶}@k!»Ëíø¢ž/ÊúaÎú!߯MÆF¾?xÚmù£­È‹À쉒@¤‘}1A˜×Aàã ô%²6í£`äFú‚ü¤CP~b´Œ' '‰¨ ¡¨ŠòaÈC~p NáÀ!0‘ž G™”‰@™”‰ŒÀˆD‘vŠ=…òQ(…~G¡|4út4êG#˜1(ƒ~Ä ~ Æ"éXÀˆE™Xà‡~ÄÇ8ô;0ã‘ŽÇØÄ#?å3tI@:å1v‰h#å“P> ã”øIÉH'ßd‰Ñ¤SŸÒAñ‹SA‡TŒ{*`¥¡BÒiH§a Ò‘ŸØév¹“€ë$à6I¤Ñ÷ ¤3ÎìLô-xe¯LôÕ‚º´cA?-€…t–Ðwh7 ýÈF¿²…ÞC[ÙÈÏA~òs;ýÈÅ8çv.êç!Øy¨›‡ºù¨›ºùHçбé¤ ËŠ´m[чBÀ-ÜBàX‹Ð¿"´Y Å€_ \KP¦¿—÷À+Å•¢~)ꔡNúY†1,CÙrð@9Ê–F9àW ¿é Ô­n•€U \+Û(ÆsÊW!]ž¨ÂܨFùj¡g‘_ú5(_ƒüä×{íëòZü3Α ]ûì[¡S]צMþšîtÕ—®ºQèC¡ë\uÛ±kцþrÕ[ÇžSý5»×5®¼«ýkèCÇúE¬£ç„q]“þGëц=lè‰c×£…N8Vùïj÷kB®kûkë²ZÈéd÷ y  ù¯Çlí'Ø!gÁ×îWwüî>ðΨï‰à#_”÷_û¢¬/xÆøøã7ŒyòQ?°ƒ@Ï!ïìÄ*¡H‡‡0ð[ê„cÎDnD=±‘ˆ£ >ŒјG1À!ðcÁ±àï8à|âÐF<ê& L‚ø‹¼Dà“ˆß“P' “Ñ~2ÚI.)øž˜©ík; m§£|:ښؓð73;u31o2!÷2Û‚6²Ä¸e—làörÎÅ|ʔͲùùøkV´Qˆ2Eh¯å‹»¤ƒÌ¿Òéë½ãY.æ.þV ¯õ+ÑV¥˜¯—*À­Ž¦òÕ€Wƒ¼¡ç1žÿQkbâ¼Ê|~Àç ›~nÇíG|ãó>?ãó >Gð9*÷¿ñƒŒb0䃜b0 ˜·\WƒOÀ ·äƒÜbPä ²‹“È ¿˜’Á'`ð |&dQÒ^ÙÇÀ ~+—ç.á0ø ~ƒ_Àà0øL“{ÿð |yÉà0ø ¾›"×!?ä'ƒoÀ¦áÿ€Á?`gK[ ò”Á?`ðüÙÊà° ¤œ€Àà0ÈYv1>µ ²–ÁGÐïòÂG`ðd/ƒìeð|A¿GA3ø 2™A&3ø >‚~¦²™A63ÈfÃà#0ø rZ?×YÍà#0ÈkyÍà#0Èl™­¯—ÂG`Ý ²›ÁG`ß ò›ÁGÐíLÈpÁG`å ~ƒŸÀ Ó… dðüùÎ ßüÏ ãõ{Êó ~ƒ¬gðä=ƒ¼g÷ú)È{yÏà'0È|?ÁO`ðôó²ÿ ~ƒ`Ð ~ƒŸÀ  ô3Ð ~ƒN`ðü?A?èkÁÐ úÁO`ðôƒŸÀà'šaþ3̆ùÏ0ÿæ?Ãüg˜ÿâŽ5Ãüg˜ÿ óŸaþ3̆ùÏ0ÿÅý.†ùÏ0ÿæ?Ãüg˜ÿ óŸaþ‹³¾ óŸaþ3̆ùÏ0ÿæ?Ãügêæ?Ãüg˜ÿ óŸaþ3Ì&æ¿ø'üÄ¥nÇï7ÿƒûßvéXäzÐR¹Öí#׺›iÏÕ¸•î´ˆ»fºN¶Jÿ ]Þ ·Èó++åð¹Þ@ºZ¿î&×Ûä9–1—s,M¤Ãu!Z®‰·Ð9cýî™U®‹/u¹V+÷WÒ=qÝw°È{h-ryÔùµòÎøJyÆÅô¿~Î¥MúcòÞUžw™-ϼ8¤o-ý‹y.¹CúÉòüK›¼Sîær>y©¼³ã#χ6É5u»\w‹–÷×åžµ]î[‡È{l òÜrÑï³%Ë»?³åÝóZ§Óï·%Ë»@ ò.z;ù(úÙæyþ´^žAm§;Bú~wˆ\›Ÿ.×ç—ÒÙý¾ºôeêä¸6º»®ŸUu“çmêè<´¾h‘ë€M´7®û4]ÒŠ–wÖëÿ{çGuõ}½ˆ€ð"°Á –1‡P„Á ‚¥YõU_õU_õU_U+¡Dq%(” ŠAØDMxD3¦É`# ƒ ˆ‚Iù~gî]í¢¾'o¾/ o¤çѳ»÷ž>wfι÷ôúçõ¼ÞzOîÖëjzÕ3zk/òÂÑŽáÃêñYvFÀì|ˆvdè5ðÛ€Þ€ÞUÕmÐÛmC— ;l£êñ[$ô‘؉‘ôEÒ)}ÈŠÂÎ(ìŒBO6E¡' =QôEãhG£+¢é‹Uñbè‹¡/}1؃̘qõx/–¾XâK_,|±ðÅ"3?ãð3;ã°'ޏÅÑGqˆÛ­ ÆcS<4ñÐÄCüxh⡉‡ÆÎq²ã“~;>Ù‰£ØÚÇÕ”+ý Ø–ŽþtxÓ‰Q:úÓñ)ýÈÎDn&}™ôeŽ«Ç‹YðeÁ—…¿YÄ" YÃjÊ–¾lôe×M ³Ñ— _¶ä /‡øå`K<9Ä(=¹ÈÊ…>—¶\h´9isBç¤ÍI[¾æ«<ôåÃ<üˇ.º|èò¡ËG^m´ÐV@[m…´ÒVˆŸ…ØTH| ñ±[‹ð¡ÿа³™Åè)†¶{ЉW1~C[m ~•pJÐ_}Ç©új쪆¾újÚ«‘ãF§9ntº‘ï†ÇµÄ®úZü¨%¦µØWGlê mUU›ˆSv5Áׄ­Mè÷ÀçA¿>²<ènFw3|münCg;4íдcc;²Ûi“õ¢ÖßôúðéõáÓëJ¦çÐÓsèízzþ<=ö›?[÷g¹ÿz¦ñY߀ϲI?WÕïÒzÕš k½˜Ão½X€ÞóáTk˜­=Azχ[¯Òk1l~Ï¡ô¾§~¯6¬×9ôžÜA¥¶@c=t+¼ K-T¿[ÔXj!«¥S㵌꽹N¿½¹S­Kí͵0ÕÂô³í~٪גõiÌ–`½Ä­±ÕFõ3oû”5eÁzMY§¾Zˆ~çÖ­öƒXxazI¯ß~]»^_Ö§q\ô3—~f>¤÷‡„¨µØ²æYÖc ¶‹µO$L¯ÍîÑøkãz/o˜^«Ý¥×Ÿ ëgì!ú9»[ïÔÏܨµÜÖþÞaý®.Lï#éÕ{|ƒô^’N½ne“~o¢÷ûzôú´MzZ°Þ÷ëÖûKõð½^Í¡ßçõ¨ý&ò^ÏÂÒX ÷»5VL¿Ât³öoë}(.½¥_í¶Ö¶ë÷N½^¦W­·ðd‚4î›Ccpô(lk¿J ^[îPûV¬w¡ú}`§^óÖ§ö[˜o!SÆ¥÷©ôªw‚Öú6^ãÖ§ÖÚ–‡¬u G_8>†ãcú"°3þdGàCĨJ ôøm@o@o`Ÿ zô6dÛÐeÃí‘´GB‰‘ØI_$}‘ã*õŽÂÎ(ìŒBO6E¡'jT¥äÑÄ8{£Ñ ÑôEÃÌúbè‹A_ vÄ 3†øÆÒK_,qŠ¥/¾Xøb‘‹Ÿqø‡qØ“)ÿè­‚6Sæ»|¦ÓGL⡉ç{<±‡?^Ú8FvŽ‘ìôÙñÇN íÄÕŽît' ;™éèNÇ®tⓎî ô6"?¹™È̤/“¾Lø²àË‚/ ²°#‹8d¡3 ¾lú²Ñ—¾lø²‰_6ú²áËÁ–äå /[ràÉ!>¹èÊEV.ô¹´åBë¤ÍI›:'mNÚòˆ}6å¡/øåá_>tùÐåC—]>ò h+ ­€¶Ú h+¤­¶Bü,ĦÂa5½)ÂÖ"|(¿"ì,Bf1zŠ¡-ÆžbâU<ª¦@%ЖàW ǨýUÐWqŒª¡¯Æ®jè«¡¯¦½9ntº‘ãF§ùnxÜøQKìj¡¯ÅZbZ‹}uĦÚF¾7Ã&âÔ„]Mð5 «[«>ú=ðyåAw3º›ákãw:Û¡i‡¦Û‘ÝN[ýÖ|Aþ¦ñ–þ~¼%ïûšÎcßÔùë;ÿŽ–K¯mÚ«qòVëý¶½ Ï¥±i‡ÔZQ ßd\ï¡íÖxZ«†‰`ËZ{9»ÔºS 3£SaÖYx$}—­Ocq ª}Ÿ²\Öɶ†µ/+P­l‘ILX·Âtµ¬‚·%ëW­½¬}jŽ`<È;{Ùk&ج²&@ð߬5:è ·+ìÁ\ì·pä‡;Õž²p—Æy U ‚ç¤öœf +¾LlÊD~¦|g|‡#;YøgÐn ߯ormønCW:öÕÐWƒ¬JdÕÐüt¹>ŠŒúj]ÈžXkDÆç ÐxÚÑ3®Ö7e¿ïn]û¡qû5–b€ÞûëÔu0ú¦¢µO0Dc÷x4¾ÆÞ ¨÷J85>ñªWa­›Ðu2j_¡…Y<¨ð¸-ŒŸÃáÖ{†‡õë]CÃ¥÷Wô©=Ħq°Æëvj|Æ^…qlí)ÒûŠé w«ýÅáè —Ï.…å#˜Öâ@½Ï®kj¬Ö˜ÝÃzï"c+=La2Z{,Ô^Æpô…ãc8r"ÐðG`¾EÐn Û€×@·Ñ+U·øG§ zô6챡ˆÝ6Ú#i„>#±#’¾Hú"é‹BVvFagz¢°+ =Q艦/šøFco4º¢±!š¾høbC_ }1è‹ÁŽdÆßXúbé‹%N±ôÅ _ì¸Jçãð3;ã°'Žþ8tƇ8⮦ÈÎ@v&r3éˤ/¾,ø²àË‚/ ³°+ YðeÓ—¾lôe×M ³e.ˆ9Ø’ƒ¼â—ƒ-9ðä£\ôä"+ú\Úr¡wÒæ¤Í “6'myÄ?9yèË#†yø—]>tùÐåC—¼Ú h+ ­€¶Ú i+ìTÓBl*$>Eį[‹ð¡ÿŠˆK2‹ÑS 1ö¯bü*¶Úü*!Æ%è¯VSžj諱«þ6ükcŒ´Á_ #ünt»¡¯%fµÐÕb-±¬Å®:bRm#ß›ˆ]üMØÓ„=MØèA¯>z=ðy6©éS3:›ákãw6·CÓM;¶µ#»671³æ ò÷ŸŠ[ªŸ‹Zs™È\À?÷—|ß›çKnïÍã%‡—Ü]ònÉ·÷•g+öæÎ’7KÎ,ù²7öæ½_÷¬Ó›ÇþoÈQwÿãuÏoÌQ¿.?åXÿMnê]7Ïy÷?¬íÑ{Dº¶È¸Â‡LhÁ,±ðž{v­àX{=j‘ì-´pëú5vݨª7aaبý”Fˆ]ciêZd·®åP˜²gÒÂÚ QØŽa…wkÕàÕ58ö*l ×¥ðo­úW£{ùé!êqO&4™òÞLÚÑ.Cþ‘e ߆žJbUÉ÷Jì©ÄæJlhu©ÇDà_í² ì0a@o@o`Ÿ zô6l±¡Ë†Ý6Ú#i„>#±#’¾Hú"é‹BVvFagz¢°) =Q艦/ÿ¢±7]ÑØM_4|1ÈŒ¡/†¾ôÅ`G 2c8þ±ôÅÒK\bé‹…/¾XdÆág~ÆagöÄчÎ8âGâàGo<4ñÐÄCϱGF<2âÇÕÔÀNìíød§ßŽOvøíÄÖúП€méÈOǶtb”Žþ ôg ;!™Èͤ/“¾Lø²àË‚/ ¾,üÍ®,tfÁ—M_6ú²Ñ— _61ÌÆßlør°%y9Ä/[ràÉ!F¹èÉEV.ô¹´åBë¤ÍI›:'mNÚòˆ±ÊC_Þ°š²äC—]>tùÐå#¯€¶Ú h+ ­€¶BÚ 9v…øYˆM…ħˆøak>á_Ѩšö£§þbì)&^ÅøUm ´%øUBŒKÐ_}5qª†¾»ª¡¯Fv5}ÕØëF§nä»áqÓ–ØÕB_‹µÄ´ûêˆM:ùÞD ›ˆS|MØÕ„­ô{àó ßŸYÍènFw3|mÃjZÕM;4íèkߤ¦Y‚%y¦õç3O×øÏ®+ s0ÆâWæ^2ïúº9—̵üçYþs,ÿ9•w=®Ì£ü×äîkÞ4u¾4už$s¤}͇¦Î¼óïÜÇÞãóÈ\Ç;ÇñÎm¼ëx9_&ç0Þ9‹w®â£øÏOþžùÈô\䟇/õðߪVÆàßÖŸÙ©k<é:BÈžEÛ¬ …m&äöx¿Æ7Õ5ú4–xŸ®ÿÓ­ñ~‡4vx—ª÷#u社ÄÜ!]—;T×ÏÖõ= ëL°ÂÏÁÆ»–¢`K óºG×—îUµ(¬Ú>ƒªö¶`Z¸‰=ªþ¶… ¤ñÁºwŸÆÖµîV+ E '|®ÇÝ­ëqèú‹nUƒÃ ·é?ƒº&w˜®ÉÜ3&tMn®ÁA»Ç¨Æ U5­ÚÅÐ8v«yŽ u{qìÕµ~ˆoz ÆV ñÃÇŽô`…›˜!ÿô§‡ªz>ç»T=Žó‘™ m8}ø ø½,ƒ8è0è·ÑoƒÏÖ­“FÒ o$¶Õ 3 [£ˆsüQðG£;yѯhø+ù?y1øXE¬« ©Â¦Xâ _ ñ©á{râ°¯ ý5ȯ‚¯Uò>ôÙùm'¾Ðµ¢3y­Ð·J·ÞVø2‘ߊÞ,df¡·UÚùÌÆælìÏ&Ù£êÑq~åpls‘O.¾äbG.úriË…ÎI›“6'º´9iËÃþac2‹ÑS m1öãw1>C[m ñ(!Æ%诂¾ŠãY }5vUC_ }5íÕÈq£Ó7:ÝÈwÃãÆZŽA-ôµøQK k‡Õãï:hùÞHÜšˆQv5Áׄ­Mè÷ÀçA¿>²<ènFw3|münCg;4íдcc;²Ûi[$yŒÜ˜å/Ìï»MÝ;¬¿ÿßïIþ“Þ‘L×™~/ò¿ù½Èô;‘î;‘oñûéÚDÿ—ÚDãºö»]×~ÚGíwdÏ„f&q™‰-3‰Ó‘ T=«îû¸®ûîÖµE¡ŸÅïY]ªæû,ä‡iìö^]S/Hc·wjìöݪ&õ1Ä$Á­°À­ºïaº¾h·ÆpçPΆo¶Gá%OÖ*riÜäq]«È©ë¿oÒ8îÈÓí‡ãŽ!NUg$dH×í£m.rævûÕGÿ n]·hTׂ·éZðýw9D×]­ñ—tý"dÍïVøîR^R “¹S׆U5L¤ÔB?yHÕº>yB×3q(Ìw©5"µÎ Ñ5=ÑŽCÕ] Ñ5Ömºöˆ´yTÍu«†g€®EÝCu½uèJ&TíÁG—t$šph¡IÂÆ$b“„Ð&á{¶%qÜ’ð7iB¥!Éø” }2ôÉèMFo2z“¡OÆÞdb‘ŒÉø–ŒžüHaܤ@Ÿ‚|ÿ)Ä6…¸¥À“BLRГ‚žxRàI…'=©èIÅöTìIEW*ºRÑ•J|SS©èK…7ÞTlLPéQ¼ièLã8¦Á›¯KúàOCo2Ò‘†Œ:d¤1Ó“†œ4ä¤M¨ËA|ÄÇ,vÔa_8Ÿ:›ñÁ!ÿÄËL²šñǼ ÑÁ÷ i#ÈÏOâR })ý¥ø]J{)í¥ÒŽìR|/Ã÷2t–á{zÊÐQO6—asò›¡-ÃÞ2x˰¹Œß-Â9üåø_91/'ÞåȨ'åÈ)Gw9v•Ã_o9þ–O¨t°+So~V ¿‚ØU· ôW ¿Þ l¨Àö ôW`{ú+Ð]‰îôÔÓ_IÌ*‘S‰œJäTbG%vTbC%r*‘S)tØQœzäÔ#§9 ÈiÀ‡d4 ¯?ào€¿[Ñ€ ĸ86`K2Ñ€Œd´ÐßB ý-ô·ÐßB‹ÄŠþVt´¢£­ØÙŠ­ØÙMØÔAl:ˆMÇ„Jo—›%r‘|Qþ¼ó™§|ݳi™ƒL­'ðMÏ ÷õüÙ;˜®›õÕÍÚW~*y©ä¤þù¨ä¢Þ¼“cnåSsNÿgÆþ9¦~éÍ-§â9xsÆ©¹a´_Nè øê³_/vƒ7OkðËÅ.й–¹hm ø××#þG8Tír©K-xÿGŸÅœÇ‹‘“€Ücð1Á£êvœË±KÀ®s‘y6$ô©ZÕR—%]s?úE»Uíé>öª³RÓ.‘s01HÕ9Y‰èKDO"ú±c1>%ºUmÄNU{ ÞDd%¢7}‰èI¤¿å_ä`O‰|¢+ IÁj ]"¿‘—„Œ$x]ô¹¤{“±ÁEœ\Øà⸰#Yîô§Èoø\øíB· arOÄr2øŠ=.âíÂFv¸Ð_'<ØWÇyW'ý诃¦½uØ\m´Íð×asýÍè(Åÿfô4#¿ÞfâP M³ð"§[›åþmeØY&}‹ŒÚ:ð§ÛËù¯ç¿šzâWßõÂÏïìïë&Ÿ-èì ¿C>ÑYŽ>ÇÊÛ­¿ƺØÇç=ϳžd-ì·yìò³žéç<_]ÿúïüœçÛüŒçÿåš×åógÀtM¾þ¯©É‡G ÷ì8b“ºUÎ@Æ xfÐ?ƒþ™ôÏDïLxf¢g&zŽ U5NìW5ÛŽZ kOóû(xfÁ3 žYðÌR·Û`l îÔµùð÷h†æhúêÚ|ðãÒ5¨±±[Õ¿JèRµù7žb6Y³ýjô¡ÿ¸A]£o®EÝ«ëc¡k¿çô¨:Y Ø"ò†U>«5~ÏEß\hæÒ~ý'ØT­>©©|öÌã˜ÌCÿ<왇®y»uÍ>âw"6ßü@]‡Yó{T-«>5¾äPuiO"N'ë~aºî´'C{ò^]Ë™ VëzxØÎ]÷ýçÐw&aSÇ'Ix°-žd|I†>údt&£3}Éð$ÓL’Ñ“<¡R”|Ha̤@Ÿ‚üŽU :RˆY <)Ä#=)èI™P)M*<©èIEO*v§â_*ºRÑ•!ŸèKe,¥¢/ÞTxS±1Ó°1 Þ4t¦q ÓàOƒ? ¾4øÓÐF¼Óà¯ã? ýuÈICNrÒ“†rJ‰ƒ9åÀ²È*E®_øâ@ž_Èt Ϭ É£åSruäg0Î3ˆKq)…¾”þRɱi/¥½TÚ'T V†ïeè-C_ºÊÐQ†¿eÈ/#^eÈ­Ç¿ì+ÃÞ2xË&TêVo9}åð–ïrl¬Çörd”£·\rml*Ç×rxËñµ_+UŸè­€·?+Ð]AÌ*ˆYú+Ð_o6×#§»+Ð]1¡ÒÅJtW"£û*±¡9•ȩĆJôW¢¿9•È©DN¥ø!²Sœú •V6 §þìn Î øÑ€Œ|o@Fºä;ñk€¿þø&TJÚB ý-ô·ÐßB ý-*=mE~+ò[±¯;Z±±Uæ&ØÓA\:ˆKqé .KˆË|Z"y¿äoòçÅ¿”9É×=Ï‘¹‡ÿZÃ}Í3ö5ÇðŸWøÏ!dÞ sÿ9ÀÔµ…Þ<ß?¿÷æó^|Í}=ç‘Ý‹±éÿ¼Ç›{sqo.ï[½ûÇ%ßö_cøuë ½Ï¼ùôÔõ…’;OÍ—%OöÎã}×)ùïÔg=SŸíxsZÉg¿î9ä­’¯zsTo~êÍM%'õæ£SsQï{Æ©ûÔ¿.×üºg=Sß ~]®èÍ¿ ÃÓûÔµ€eûlȰoŒOï3"WÀ¿´†æ"ÆÎ"®GÀ¿ˆsyrgÀ;³[MçA³Ú£¸N%ßá Tu&îâ7vãöÕúM@÷¹|ÎæüŸœ„^uZ%ô«:ó ´ÍÁÞ9|_ íbªå»;ð%ÏE\Cø_´W‚RG6‘ëÃbd%âK"ü‰]ŒÎ0Ú±!ûc_"ò#'™‰BÞDt&boân5¥O¶$d$áS’Ü“ñ9 ¾$x’ wÑŸL[2r“iKFO22\øîâÚo ý)Øï’ŽG ºS°=û\èuas2]"ë]ö¥!×… uðÔÑW_1¬CGF§z

        •¢£y¥¡ê’RŠÍBƒ Íð5cS©Ü#‰c)²JÑYÏq©—û66ƒzä4#£¬O]†Zèë ­˱¿Ù-rÃÖz~×ÃWüzäÖco=²ë‘]-Ò':D4ðvÐÖ] t|vðÙ‚Ðw`c‹|"»œØ8ˆ5çJ øêÜÊ>Uà›;M3yçJ2Gjð͉:õ\È;òμóïÜFæ4w¨óMæ&ÖûæA½®Ó®±¶~8[ Ô<ÃÂÑÚ«Þ[ï‹]z.1ªëù9ýö}íÕõû&TÍ>ÂmÕê VkNЫ盾UO W½Ë•sϪ¦ê†Õù6…$ØêòÎVê™ïÒ5Æ6»õXk“ª/&8ê‚÷#µÌ^ÎQyÜ%õÉwGö,Rk½9©mHÕ%“=S‚å#@ÖzÂ>ULöÒƼœCNN°z·håŒ6õ.Pðßå} ä·r«”wr‚G+{gÈp)ì!/æ‚`ÝȾ Á^u‹‚.{/¬5€A Gðkd_‡ì³lVÁ(µzRKKÎ!y(y˜Ü†åýÚä{¬Õz½]¨Æôvê=n½Ï£rMÙS!5¹ä]¢¼›lÁü‘œNÖé ì§ušrîÊ~ Áœ‘Gy‚© Ïz—@·¤Ka@,A÷’^µÞqIŸ~Ïß’!…U°Þ%ÈX2ªž/‘¶ µ~O0d—ìU8áAê¨`ºw†*YÙ§ÑiS5¸[G0o:±«ÓáÃØ|ÙNôuºÕú\©‹ 6Kn)6ˆnY£)zeý¡¬Ó»Ë\ðuå¬#´p)&æ­ØåÕÓ)ò‰]çj…=&µêl ÏAð‡æ`ëÊ1’úbb³…GáQ2d ¥Ø+?"OÖ8 ®Ø-×( O=Hù`ía Q8¹j-dg·²ÝÂÈEvgB®§²VRžÍwöé:e›T­²ÎA… Ô9¤™vÒÞ‰ÿbãn…¡&xg²gJÖ­Jš'cÒÚïÔ£1ç»Õ{[«v]¯Ú£SÔ«0}%×–œXö n°à|V“¬ÿ-œ¤n…A,˜Ã‚ÑÖ¥0~¥¦¤ÌC——ô/å!Ûžë›a&ÿftεmJÉ>®ÆþAí»[«^ŠødïëëþØÜþЦ¬ˆgOimz+kª$sýÙÛn)}ë3ã¬;N Í^Xj®J­ÜÐûècÅžKφ7nè~ÿ¦&¯\cxÞyC{~÷'s{Û]ëVåÍÞøYïÿ»Ü\úĽ™•û™«f>ò¼ÛnzíBŽMÉ)z£ðWÏíï•o w{ÿƒ¿¸ÒKgn_QúJhc‘¹þû?óö÷Ì•¿|û’…Ÿ$Àï²ø3øè3AÏßžuôg{Ÿ[jn¿úÄ·¯ü¥Ç\\ÄÖ“ßžk® jå=§=}—¢ÿ^ñƒ36Ì3†_õîPã1æöµ'¸õÀ܈í*æúƒ®¹ó³+>2N²ÛÞh¾¹Ë\YtÞ/ÿ´t7ü½ÿ¯»æˆeÆpÐ@èG†™Ûïû°é¡'Ÿ˜ôwàÃõû¹NýÅdüVîsÕy ÍÚ6½¼éLKnl4†ÏÈûÙ뇷š;f]áÞpƾ8Èaé^:Ï•ŸÆ?Ðàl>áí×Ì•ê|5ó—Õ¸¨»õªM7G¢wQVûºcÌ}çìIí™1>e\xÇõ 1{Y7üj< DåX>ç_\íµ[vÝgîxäÔƒã?õÆÓ\ñÓO;ãX?»Õx86ðúýÚçû☽2&Ãp›;^(¸üiÇùâ÷ÂÚ¥h®ôN(üüPãâá}¹°·ÊgGYÊ–ïøbò<ÝÁUŸK£Ï¯_õhB¸¹\.»Œ#ÔqAž/Ç=P··ãfc¸áéma+óÌŸ¿;xðš÷"Æ ÏÌ‹(Yolzèzf]OˆoHøozÒÜyÑ/æçŒÜä·÷üñ‹·:þpQ^ôÅùÆðŽ–À‚`sçåÍ §Ü¶ÑÈ—H˜Ë êÏu™EЫã{ÿÆ‘7ŸüÄÞüWôÉû·¹óÆßúðæ@ßµåÅm§stü–ýåÐ;‡ð»­ê8ߟsaèËßõ»±¥is_Üïùþu9=滾Ü\{Õï»ï6N¾ö…óþºí0sÙ›Ûæ]¾î c¦×Ÿ­j<ôßs×ÍÙ7„ûÆÃû‰—™Üã³sÃòÞ™ï»_\uÓ=[»7æy§º¯øÙ«ÆGÿLÇG»nL5†ÿ¸áÜ×®ºÙÜùÒÚÚü™o\,{kþ½ï,œ´OoøÕ¸¸/À½óÒ»›‘ï¶¹Wõm3w¾yCÝ)Çš!^ô©¹œ›ç¡¡µÐ«qpWÏ® 2F¸™$eîüãæ/·m4îñœ´ß³ærS ®'[Õ8¸{YUzCÿk^»‘S/øÙ¡ù…“ñÜuð+M΋<>¿k¿Ø<Û9ßøÁÊÆè;ôksù%7¼ôŠy°1KÙ\5>î 9só}óg#áÛR6p¯¹ëøG»ÓÍê‡?ÞðÉßù¸U;î{cLJœç½^#éKï¸ê7Ì]gm.¿íñ¾ó æ€‹f-}ÅwÿÙÉ3jmÞqjÛ³M“>u¿2FP~QNÃäqÜ•0ç´'úß÷Å¿íûkVŸ¿n2¿òG}Þ O_ýeî_O·¿gŒÔÎH>Èëpå›$ßm«™_s¼ã`›¿:ûÚõ‘‰)ÆHËq™/Ž5š»<åTt¯3ºsG:O ›Œç2¹ëÔu{ï?ð«qp›uÛL™÷#»#ýèÍæ®¥·•¦ì½ßw½º¢ÿçß9ÎXðnî•?ÿÞMæ²ü«®ëñóG“[ß)xÿ[>ï#K_^Þñû_™»®*]þ“ßuwõ/¯xõãÉq¾ìÁ_]zô­GùΟmjÝj Ã#«j>8õ­ÊÉûÀ®[6ÿÞö§ÍŸ~ðÊAã¾ã¾M“uOõßÔ³kçäuhäò´×ϯÈ7wÝ¿~M÷Ís|÷£‹oÛ›|ÓwœÔ}9jü¬“a<¶Ì¹öĶ̟ÿÁÜõä‰YYýæÀE9õŸÌ™ë;¿<¼·v£ïþóª/ëöÿÎõæY«Œ‘µaÁæ®m¬=÷Å#ÞòŽ;eÿd^°lð­Á vMø®‹¯ªq²vÕÅo½Ù`7Fî½2í„‹¾cîúÀ}ìë>?~ö\ðoªN6—2þÚ–Õ›áSãcí¡×Å<ÙáÕgŒ<òß’a˜cßùì€?þkÄGz¼ üâöÂáÇš&ÇÿÒß,æNv€÷ø O—[†¬ÄÍ×ßÿ¸nò¾aŽ©á³ëjIÐvMú·ô‰—·Ö¿îçŸ7·¬ìYtä¥K‘­GHf=y^-rD¿½ðxßýþêÏ;ýøÏÌ¥*ÿ†_“[NL,›÷qþíŸsAÈBs,Éþò½îbs€Apî³7˜Ë$Û:‹ûÆ«j|ô^¸üá] cdâÉäc.Ù賿¸k붧̵÷{Ò>9Û\–øÜ_Î:>5nþÉe}G_qœ1²··û´ñCͱ&#ã‹“r}ÇSçíËjm‡=ºúdÛžQ5nÊÍ\÷Á~ðgœõ»o?Þ»8|öãïúî+*O4—©ù üêø¯‘ËAÞ%“Çq‹ÌRo™<Æ.}ú†®y·G|î=_Õ}wòxzÏ×Éã9ªÆÇšâİ…í“ÇsË¡±eã7ï6Ç®½æÁ‚C̈=Ñ•'¯=À¸µïñ¬gWzã軪q±/Þ¹ïEcKpͲtç 9vËËó¾|ó9sàúS«¸™K_m þÂa‡^ﯖf±eþ™‡s̱»ùB‰¹Ì;^Ì¥Ï vo½íèÕñ½áÑ]u=5y]Ùröžëg5ì4ÇÖç· Ýî—>^óÄù?2—~|ÇŸß­¿~u¼¯ç&pôIQƹ͸Î{jÿÛO¿çJßõèÂ{o=Ð\fKÅÓløÔñ¾ŽÁ¶ø{!Æ™­z¯9&n=ûsßuQ.¯Ýk.»Z¯Ïm{¶«ãÝ"'Ây“Ç{KÞÜï¬Øx±9öVá¢K¾°ûôêûÖ2.ÞûåÁ¯Ž÷µ2ÍÿéuÆ–Ú¾=qg÷˜cÜxC~ùŸÌÊ£˜i‡šË~—qqÆ‘K WÇñ—’v&^fl¹à/Ÿn(2_ |û§/uþÈw?’ÙZ›_Þ&³¿3õ‡ÛÕq¼zçëWćÚ|ãL²•’óÅþ›~½Æ¨²þ|÷Ésd¢X¿:®W•<ùÐjñù½fÍÙÎ皯ÖcþöκËb[ô|”BHƒ@ ¤‘ ´Ì?=4‚Á£XAô"(M‚´ ‚ ¢Ç‚cÅB@Q•âÑPB DQ,ˆŠ=êûÍ7óýÿ‘«÷Þ·Ö[çž÷¬’of·Ù3{öž¾Ðkº,ŸwEø×ƒ?^×ëš•KEöé!*Íøæ¨k¬wìú×ujB# 8]w©^õ惢r·íÐåÑ‘s£ÆÞu™ÛNMy€×õ¶Ò›»ŠÊÃ÷1^)^óØ”’›ûÈò‡ºoY›Ú].^±9ªí‘ש]_K ùy?ñnÛ{Á$ytÖOiÇ>½ÌSOº”‹g¾9eÁ?jÁÓõtû?{Œyî¨KTžùÇ2ñã;ò(ÁÔð…þžö¨ý¦\|ÒØ7ëâ Ú[^ݶD.ö²+<]ÏÓoÜ“Z¾ÉÝU]¾"fT <xéÎûgÆÉò;í‰3¹¸G—o=ä:U«ë{_Õ»ÛiÕ{ÂM‹_^Ûfæk}=°dCc@¹§?¯Õõ~µþv÷GU×þmcëu“Ýz?6`eèîç|æôj¶ñÚ_DO§üªµïñØ_­nW-^WýΓîxµjÞ?Ì=ërëãØ°Cƒã×|·{ö‰B=öhâL÷ø¼V·«Ìx«jýÍ¿>ß-É#焊ãi‹óôgßœ÷å¶ñžñB­n“ƽ6y¿¨z³ OrŸÝÁ=~Øïóô°ž‹çÉcw<´êÞ¨gœ$—èyJðt{˜¡ãÇ>Ä~=–ÇîÙ}n{tìÈmuº]Üdw£Á¾:®”Çž|¿ÛÒ˜?¯çÅÜqŸ{œR§ÛÇ4=?í‘ã–™û®Î‚NùU?ÿ3t‡GßË*ñtå’YO„ ­} |ݦ ­ÝðTubÿÝKÃ&î#í.öºè£=úÖq«§žêt=_Koíuö>±ÿ™¯~%ÕO;øöÔk³eù¬9 õ»kä‚͘N´ã:]¯WªéáÐ×Ýím¿ž×pÇǾùùè[«C=ã´é'v$U‡Ë%v6Ðcÿuº¾¯ˆN½5ãî÷Üõ½ÿpçeÉ-}e}ëœQÑŸ´öø#ã7ïHí~ÑëáÇêt½O\%rZMÿHì×ôe½ÿ4ïÇÒÏß8ìÒ™Ÿ\&ï¸îãÜ3A‡€¯o.¿ÛŽ´š@y£¬ýrþ´·â=úºÉv4òŽÍ7þê$ô|D×÷U=_¸qsoËÝÐ~WÖwß™t]í!¼¦ÿYš°w¡¦Ç¾ŽDýÆnd\[õÀ´nûªO_(ãeù#ö™3Ÿ ž®ç«_8 ûCYßç³Þ¯GÞäÑ·=ò'¹TÇ'àéz¾.âO·\Qý¢80vòC•t×W½ë¥®[¾ÁéçåÒÖ!÷â²<õtD×ûu/wýKDÀ*q`òé%Wþã´GÞ!Ÿ^§»Üw<õúU«Þ òôSG6ü¦?8 ´<è2YIÜ™–óÜíò5­´¸x]¯îU㜗7üéý€o×õºmÈ¥Û~_°-Üu꘮Ÿ)j:)÷ˆ8ø@õâ7² dýþ›Wûž»^—EÚðº^¦DÞûÞ+Üý°8øÚ–”‹ï\Ö×Úåîö´,ÿ³«ëJnO×ÏõzÜ+V^~¤MboYž¿q׋Yÿ^×Çv«Éq÷ µœ²þsÛï»ç½–ÙÚ.àéú˜fàÙálg·¿¨?S˜réOÍÆ±WG®l×ò¨³¾¾®Ÿé~öD˜8”öþ›'G ð´£ßÚpìè9½Œ¾lä§©yÚÑ1]O7ýù¬Ê‡ô¼·<Þê+"„'=ý ™çX¶<ðåÃÉ¥àéúºY_Ä¡ v*w¸ø\ظ›dyŽÝñzÖÓêu½ÍÔóÅâÐ4{"Qï2òþùËOÊegÕBì$àt}ͰÃ×kÅ¡EãÛ¾:r£<]v•˯)ì~váEÀéú™núýCv7:ZOµiÑÀçeù€¸iÿ ¼\.×ó¨ÀëúùËm²dáÞ{Ä!Œ)¤tû>õ]Ø×Œ/ô:†\®ç©€×õrƒšÕj{…8¤ûy+’Ö¯üúUOW¿¡y»‡v ýó‡W#_ôøÝ'&¾*Ëíp3R®¸6¹êÝ+;¯ëaÒA{ÂZ²§)o“ÇÇ»F¼îYY®¦Ç¾{E®ÿÒì_¾¾xÓ¿Íè5lñu»íòZí¼ÔW·ÝIOOý)·ù‘i]³Ä¯K˜SNϼÙñßö{‡ô<…»ß;>cô·ÿu¿,7~̉—OµxÀ×õv^ÏÕ ’:Ž.–ÇÎÙ•÷ãŲܞ—Ë/±'h×õwÝi/i‹jf>ìn·Çï|Qµ Yާ~ï%—?üM—´Ó/‚§ëq’¶S·UÇÿæûM©<þðä÷úxú?§8õz\×ë­ó–û(CT÷õ­[vÉ·òø‹/=7ê`.ðï½qܹü›*5R^×£VnüÓ†‹ äñí+Ãw`Ïjöèßäò/ÖÊÓ…³×õ8gÚÆ´e•»ÜõR­Ç¿òxÕÄŠØé1²¼‡]0¹ÂË^ØO×çBkëuý7Šjß·—ó;Êr½OÁig®S't}-ú˦#·ÿ|‹¨~æ½ç‹ÃKäñ³ÃÎê\åño¢SP›7[Êå?…Ñå§ëiq`¤oÖŽ¿‰ê÷Ne 8‘æîoN´9³¼${§¿Ðí<]_&ÕG•㈒':Ùó1²\-¦–‹|Ó.VØáÛç|]ow¼ñJHð†y¢ZëWžˆþjÒÅÅ´O³.½âõïAàu=ÿ ·IÌzþ¥ä‰ÔŸ¯¶ç5YÞõ×Sm‡Ü*Wf<ùÖ>ÔÓ ]OKõz8’ô@úÛ~òD¿‡ #wâ×¢×|k`¹R¯‹¯ëÉôgîz:œx¹Š¸ä‰B߯Ï+vÊò4517N®üè+ÿ¯6§ëÉô+âpö×Y»Jž1bî ?óè=%è…E/œ’+>ÙùÞýôW º¾V4´ÀäVˆÃ:Η'&,Jï\’ë±Smråá)S¬8ôРëk•êµc~‡¯(¬íx„ú³¨¹éµÙyé?Ë?0*k½Hn~çî˾W–]6¿zý+Ÿ¯õ¿înï¢ÈG[ŠšEï4äü*¼îÞ÷À¶‡®,ÔVy>ש&­ÿû‰²ßyý"Q£väÝ!:ÛœÜ|È6D¹RÇKÀký¯?¤QóüKg«Òï“ ñW‡.77Ùõråëß_újÅmÀký?P«*;þœjípûƒÍ§§]_0)Ûc_Mº<¸wÔˆv©¢æ£éýZ°F6Š:óöÇr³®OY–:ÜûjðºþjO¯\#jލŽi“l¸øÅW2ËòvcˆT eÙØœçúÌ ^ëÿ!=‘%jôþ'ÙpÙŠ¿TÞ0Ãc÷¾LXõikYÖw÷ƒŸÅžOׇ™¿5z½O6\ûXÁò÷;ÈÍz†, Qõðº>©\®J jíí`WȆ©Yÿ¼Òß³`ó‰Ö7Þ2¤^–å~ÿºg\§>Ñõò¨êM»Þ/j#_ýñ/_ï’ ·ÌëÚ"àÇ/§ëcÃÍc?!ôµz¾C6,Œ|ðãŠ%s¿Øižóx]Ñ»¹ŽÏµ´nÿɆåÕÊS:ò§õÿx=Ñ/j¯øòé+úɆ{¾ûxÈè»É×ú~üh‹}[º\*jgïÿùÛò"Ù ç‡É×ú}bÑ¡Úï–ý,jïüû«óxI6¨éT¿ýäk=>qváð·Î}'jÕêjÁ ÙðÚE#p}äk½=9îïQ/=~ZÔ>7tÆ{¡seƒÜuã©mO¹N}ªõó¤ÙçQûzö_G¶X öÔ¾M•ôèçS­Ÿ'ÿúTè>µv5&Ȇ½®œ^C¾ÖÇ“ j"l¹¨U«Š]Ÿ— G¿ëüÖËÔçZOÙV¢VË+N¶ZqnÀ'äk=ÕzxƬÔ™ýnÖÄ‘#Ò¾ _ëacW{"WÔ ·úßóDÙèsgô›AS\§Nj=üÍøõº«í ÙØéXYðŒqäëòÿídŽŠ¤EÁM›ŠecÔ„ía}È×åvëSMÈ+uj9åñ—ecêç3^<©Ëÿ\Ùó7ù¾²NÔ©Ù…ð¥²q š6ïN¾.ÿó“Ž5$y]#êtÜ/‡y}IØóäëò¿0ôôã_š#êÔöÄGdã„k¿x·!”|]þuœ/êÚ{[îL!ù˜}äëò¿4­2òLãÝμœ¨cp‡«v¾eãìã÷¦NýÈuJ?¡&6ÙËn#EŽdã›·x­¡üLëc“j®…‹:›L¾l\cÏ/{ÚÍgZ//«iÞŸËDÝö69©Wõ“ÅUÌŒîO¾ÖË˪:ßÝ,êÔ²ã{;dãÓIÝ×ïh3:Z?¯¨UÒ7ŠºªÏ/]ùåøãJ^­ŸW¨õ”§æŠº=cŸYúN¼lÜ:ñ‡÷RŸiý¼zµÏšQs~uº?”Û¿ò:œL¾ÖÏ«öö³LQ§÷·ÊÆÝ®m/Ÿã:õ¹ÖÇkfݽîË5“glŠ–ûì}äk}¼öVPÁÓ19¢î‡×ë'þR"k"v~G¿ú¹ÖÃæ¼wwµ÷'qĺkÖœ„ã²ñÄÈÖ/Ý“H¾ÖÃfQ¹Q—‰#zŸªlü|séÂ/£È×å/u­ubÜŸÄ‘ó½Ž?$ÏØãòuùË?þl‹Œ¸K‰=_‘ð¸lüÇ̉±©^äëòoüñ˜ÏwMGÒì"}coÞŠ òuù·¨]|݂ő>O¶øiãí²©Í즧ƺNÒåßR÷Ðð¿µâÈ 5á-›|6X÷<Ó—|]þ­z\$ŽˆùhèfÙäbâ+»ùºü[KlÇ%ލí­;O˦਻‚-÷~Uà´¶Þ×õöÎï˦ðîG.©Nº.ÿV=¯#›b¬âøº·ÿB¾.ÿÖcs/š»eŒlJÊÞúbÆ!Òu¹_›õ÷öå_Š#éoã‰î”M™±w„-½|]î×í°¸%z‹š¾õãí²iÀ}ëbdÜú….÷믗O»·w¥l*h1kú{×zÚåºÜodåï¿¡xCß¹%cóÎúùºÜoØËåy²iä’‡^I{ýB—sÛ¬kÒ“]geÓØª5¾)—çü¤×•É×å}ÓG§È¦ £|¿¼=|¡ËùfŶ©o.Žx¿ûm}ß²ér{¼â¬s§Ëý£Ã?Ÿ-›®øqÀƒõ;r¾ý¢È¯Ú'„|]î·Î {aì› Å‘ÖÏM¼aîã²éÊY¯â7¾Ôå~{á÷‹Ÿ@{2ãꦫíþ=猎[Óå—>ƒ¦?qÈGÔýRü@ø{ãdÓu?„-H_N¾.¿ÔûTDÝÏv&›nKþò59§zÿRë£îÇÛOÞ–2_6Mo9áÇÊ=ò~©õ±½å£‹ºo÷æŒõ¤lšUòEuíÇäk½l'˜yÉ•¢N­>\ßF6Í›{Eî‹?¯õ±ý =àuMv )›nïôBŸ´ÕÍÊ£õ²ƒ(–DÔ9íiùñ«‡:ë®S_iýì0ãÓ:oɦ»ë‹«ö'8óYÀiýì°·µ_#›Öm¾(Ù+Ç|¥õ³£Æ^ˆuDãN„ʦ‡¾ŸúüÒ6»øJëç5[”¿HÔm³7œÉ¦ÇWÜÿîÁ%"ÐÙ¿ô•ÖÓ;½6\7p&ýÜ‹v‡!öoz.­ó½ÛÉ×zz‡ ¤uê*QgÖÉö?ZóÐ Â×Ù·ô•Ö×;z€ êÖÙ—Eg=Díj-jïÙçô•ÖÛ;jv;î%ü×§­^Úò™(2óûËò×.òŠIÎüõ×Zï¶գˎ·EÝâ ©÷ß9B 7óWûçLÚóùÌÎ< ðZïêx[Ô©(áª-bd›7­'¦öûíå¦ëù€×z}W¯K‹ÑÎúÏEçÊîŸ=V¸ôþYà´^ßÕ|DÞ+Æ;åÌÞ4¤Ük«gœÿµÖï»zWìÙöų[ŸvÊI¾ÖïNµ=ÀëQ7Fm$}Ô½þ³ßÌc–¨Qí‚×zÞ9øË{‹ÛD=–+ªþy­ëÃÙŸ‹<]>à´~w.Hˆm?»•¨³Ã(q•Þ?"ªTxÝj“‡îi­ßj7O§ Q×Ëvˆ¢ª^Miý§õºÓ쿬Ãz&>{­¸Úè£ê€ZXêèèx­×÷"Ô†œHQ§ˆkL|\¥Â¿µâ'N>­õûž½Í¬›¨ë ÐcÜë4öäàEO‰1ŽþNký¾·ÄЄ^~l!.ÐòËæ’¯õûž9÷Q{¦ñ®•7œqêi­Ï÷̸öz=ÿ(ªÔ6ºàïÄŸµ§õù¾ÞŸ-jÙ˜˜â¬/«åÔÃ÷‰+v}Û}ös\§¾ÑrÔêyNqƒV$ªî9þõÍÛß¿S;0€Óú|_­ÊéN<½JÕ˜{^¶JƒWêýUÀk}ÖÚËB=DÕí£7õ~ëbq•³ë­¿÷_´7Ö‰Úo±éÌ|÷ú·Y'NëíµÊÜÿ^Q;çéÍíýDZ&t×è}ÀÀiý}`‡çÞ¢V»-‹.²'E‰ß§½^ë¬ ~£õú¯5ë\Ó÷ª ¥vμ*pZ¯µEu¾zùuîuãIÎ:ð­Çôºã,}Þè€= ^àÌ—8þÕ™pü¼3êþmƽN+Ž$”WOݱÐÝ/LÔv.þìèÓ/¾éºËÙç >0¿gÐûÐDÅdVýu rêzüà eÀ‰Z=Ï"ªlµeˆëÕ®”ÛžN×ß.cµ¶BDÕåj£ë@1ÃØç§_:£ësWAEGLÌ÷ŠªÑöA§]§ës×1“Ûí_+jL\[¥F±þóÅÍz?–¸ák; ^×ë.ÓO×8ýÂÀW›fõbf­q£ÚŽöá àu}îzÛ˜‹;,í,ªR‹ùñà^1ÓØó_Բ؋?¯ëu—ÒBßa¢Æ´Çªˆk¦_¹ÆñξO÷ºy†öK"-nÉwïR#²Ì|€³ÏÀœKrö¿¸ñºÿvŽ›¾³ÿÀøwº3ÏvÞo¹ìž»|J;pîù·)ׯ:û‡ÜóMܯçñqèmftWøý‹²l»-¹Ü¬–!Ò–ËUƒ¾š"î‘›t¸‹\u¿ÿ¼G¼俦oý~¿D®Òç%äf½_\®¢õølŽÜ¬×!å*½®æ¦¿ÊO¨ùDêîÛK–íY·æ™ËöÉÍötöå²LïûóÌ£éý¡ró¯·'¿½çy¹JÏÿËUªÕ̾Q®ZvÉ··NX$ËÛ_X»ç~·<«Þ·'Ð媗í BîyB·üö±…nrÕUmþàï+eÙwËþùz÷'dÙ•ÛÚüxj§{þÈÌËòPµ øµ,×ñ‡,3ó"å|\òèZYö\´Zñ’å™%É'ßìâ.Gyh`ÉÏV9óžy)=ÏçÄKîߎý;õvÄïg5#'¶ë}Œb‚Y×§ý¡ÇŸ½á3òúK'º×e¦¿\¥¢­É‘b–‰¦ÚÍšqá·º¿Úíû ‘ÐË¢æÁý)ùRTØGÄ4½/ 8Ý_ì6ý]ÚVzÿYQe£º^ÌÒý¨˜®××ýÆî öDš¨±§åZˆÊŸ¨ â³®oöq¯ûÝö²ÙQcóÛ‰J¼ÉúåçÄôö8pºßØýÄÔ´—wfŠÊ¯êU¤,n5ýµéÓýÅîlG!j Û³}q¨4qÀMÕÏ:ûs pºŸØmæ=jBm‡êìtŸ+,Ÿ2Z\rÛ‹³¯ÙÌã¹×Ù6Ÿx¾WÌšŸäfe.k>‘eOÙ#7ëqˆ\a&²Üßî¸dyä£jÅH®Z³ÕG®ªUˆqr•ÿÈò~ŸX/W©aiÎç²|¤½AÛÝNÿaâh÷·ñn¿bæ»Ýíê||§?pÚ“ãO®4õí´'g=õ}³þFmçâ}å¯]ëioú\‚¨šÈïtûÝ“ê7PqØÄ‰•µ›®‹˜#fè}çÀéö»Ç®þ+Äaù^TªS´c¯7GÙFÓív Cæ½*Ûjè/*Mܳ>Oœn¯{ìéÚ…â°jeñÛDå{¡scW s̾‰›u ¼n·{ìílCÄa5+? {xÓuç]»/s·ß¹Æ?ÌÔçPÀÓíxñ_‡oµçóE¥ŠòÂ3Å<§ÍT».®_¼nÏ{Œ¾›q%Fè¿ú~1Ó×gôlìñ°™·¬|â…Êˈyö6ÐQbÖXu°y°ëÔ÷ZÏ{ƒ•¡O‡íjn#*õ<œ˜¯ûa§^ë{oO`<'wR¾TT®SŸ¿·èsÀi}ï5ñT5N½óé¶¢rµ½ !æ›öu‹^'^ë}¯=ŒˆÕ'lƒ•zª¸Í´Û[ÞŽyúÛï×zßkÆIÕ»ïP+¢ÒœSrü©ñÇnûŽü­”å^þsÖàGTqsÖÉÍŸ¨ŽòQ¹JÏgÊre ©·ÊUö6ã?Ér³¦LÛ‹{±³ÏÆí'̾ÄUíjî}Ž?rÎw¸ùüuÞ7'?Á¯éóÕîsŽtú›rÝ®d™žuï.S»øcqï£\yÔ>(w~?àŽOÏ‹O~Å̈#ÁÔB¿8ÒÍ^·woñ’3Þ˜aâÕ[̸hŽ·Ì3ýÉ­ö1›áÔ—nï{ÍGÔ*æø›Ä‘Pu b‹3/'®2ý§3¯äÄ]U7Üý׎ÿìî_qðlÕ vøÒ—;íü6ã‡g?»ÐØGÌsÆÛ?è~çC{: ^ÊîFÏ^*ö•Û´Xdâ9ã÷7ö0ñÙkÞ9—-ÙÃùÙbß³* ¸BÌ×ã-àŒ=L² ŠCÎyd­MÚëö÷‹öÙ6b>Ö´ãÞ×À3vaæ/ÚbOûV‡¨žUÜnÊ›}-ÊXà]ØneŠ8hΟï[ôÂç+néãÖÃíýÕAÜæÌœ3öq«=%>˜‘Á±o†½Æé€ÓvqМ«Ù§–BϾÝÝ/;ã3®üó?θÝìq¯ÒåwÇË~m=ö¶µcÝqÅ–±ö€³/En™_¸÷ÍäµÎ>¹Åœ_]iÎOmQ§Æ´–+Ÿüç“I_bzžÊ=¾Ý¢÷S¹Çµf„»ý;öùGóXçGÌþ·ÝœWœ‹PRç¹Çµu§†·ÜóWÎ ³ÅÂä¿í;kÍ!_ÛËÁHe¨3ľô)É]Zÿ*ÌyE±PŸKnûoÛ­7íÓq•0ç×ÄB½¯øßÚÇ3þÝ×ŘÄbÓï”êù|ש»0ãçj4=u½Ø×N>tÕcåîó…åöñ _wüû[;›^Yóú÷ã7« ”Yιr÷<—±÷÷yþÁýíÿÛvèž×)7çcÝ~&°Vy*¹eÉžó>øAnÑóeŽÉ-j”Tü¢\iŸ†È-_Q–öÐ^¹âç>?4ìûP®ÔëQr¥>—#·ìWœ“+õüÇèÏÏûýüšÙ¯ëüv÷ÿ?áÄÕuØ­b²i¯Îüút/8ó®7›zªQÿÏïêž×tæyœx{¹‡Æ×9q÷"3>ÿÐà™óºîøÇ‰·?¼IMpÓ);LHh&>Ðû0ĶlX-*l·L{7ëH¥ÎzÔ?Lœ Fo¶‹÷*ƒX!*̼«W/1~¬t™ðXßïÀ3þHM3Ü÷’8`ÆÁÙ÷ˆ%&Þ.eÔNˆ¼ñGf}ꀙ‡ªxÍÿº§F,sÇÙξ§Rg~éÆ?éý3â€^çêTöåg„9çæÄIÀ»3ýÆV¨k/&¬sû×E‰Ëï^v%ðÆîÌ}1Ôµ:aD…ñ›Ktên/>¿mÿ²\]UýîùíË¿E‰ùÍ/rËG#j>uö•Ê-v¸C\ÿlÿ}wÆ}%·¶°7;ûbåÖX;uö£É­öu¡Î~A¹ÕìÏ.»åÕm‹?N•[ƒ7Ìë´Ä“ß.åãȲÿ wæ^­ïÄ´ÿý+ÞbùÿéýÝÿì]–?z‡÷ÿ–·Y.¼Å{á–óßhùø=^ûßdÓÎNy'òC»´N›÷cKùÙÕì}µRý~l«sú~_õΚûýXuÏ/6Ó¦Zßóëm/ò½êÍû±|·]oîÖö6oÈnÔ]p;—ycdƒ¹_xŸÙæýX`Û—˜÷ci3¾Èé;¹ÙýÀ æíXÚ«_€¹+x¶y;VÝ ®?rù#·?ßé¿ón,}Y ôw5{[d„y7v—¹S˜Žê~aøtB¦N.ónlµ~W$xúyoÆßëÍ›±ÈÑü.Õú®ïuÿ0r…ìÒnÄ~3z¡›Ì›" æM`ÂàîmÞŒ…Oøýfl8ºê ï®Àu…vWhwƒW7dï­nÈÓ­B»¤à"¦7{[$ʼ-²Ú¼ ý(èG¡›(`¢€‰&-¹¢¡\Ñ”%]Ä { i1êŽã“æ}‘l}‡m÷åúîWåîbá;Ѽ1‚þc‘7¾qÈ~4ãêõ²î÷c7™7FH‹‡wütóƼâ«Í;#!ú®Ü„‰úÙäM€~B½v©‰ðH7‘üDòÉO$?±^»Û$ò“! ÚIÐN‚vR½vÃÉä%ƒ› n2¸Éà&ƒ›\¯]t ù)ä§ŸB~ ù)è/¥^»ïTdKE¶Tê?•ò¤B?•2¥¢“Tt•†®ÒÐKüÓÈO#? úiõÚí÷„~OtÑÙz’×Ùz’—N]¤Ó&ÒÁK‡o:4ÓIÏ€^ü2¨· tŸ|é½HïEz¯Éú^lußt/øgB'þ™ÈŸ |&ð™Õ:¼È‚~éY¤gÑvzë÷lý¾¡º÷Z…ö;½Àô£|ýJõ]Öê=BûÍòú“>>Cô;ˆê-õ~¡z Ø~“ƒ ;ÜAà‚w¸9È*ÀsçÏžkƒ¾;ÛuNߣ­î½Î&˜|t”lùÀ¢«Bõ{—oŠ£ü"u×0e)O |J 3ý¡ßæ±ßÿÝeÞüM7o¬×ïûª>Óþw!†ü÷‹!ÿ«øñBìx!v¼;þ×±ãÿÆ»~v_7Ý´sFlТ­YçÌ[¼ØEËŠóÞ£Û¥»ÍÖ#Î{n\³w!× Ú^ä{4oöòÝ–ï¶ä{˜7éàÕ9Ú•˜÷X6ê®×xŸRó¦/°íG˜÷èh¾Èé -_p}‘³Cºy‹Ž6è‡~Ðò+5ïý’ï®?rù#·?ßÙžwÝoÑM4ïNÀ/˜ óvDZG`:NÔïPt„O§€foÑQž`hÏÖïПÔïw¾3<:Ÿ4ïÐ!Gð»~ß!ÈýPoóôBÏ™·+¨‹0òÃà ßŸ ‡Gx…v+]áÙ˜®Ðì Ínðè†ÌÝ Ñ 9ºÝà"À‹@–Ò"ô»2ÎtQÐŽ‚v:‰& ˜hÒ¢‘'ZÑè1š2Ä ƒdŽ!-†þ$†ºèNZwp»C¯;ºên,ƒô^¤÷"½é½ Ó {Á?:™ðÏv?™ètùYð΂~åÊ¢Ýô¦7¼z—ê÷VÔ›ˆ}¼Í›Àô£|ý£rôƒ_ÿó¦éà30ʼ™=]¿I¤Þ¯V!È x ‚î p;þ9àæ «Ïž <x.tëª0¡ 4ò([0ùÀ䣣|d˦]nÔo°¨w)Š£hº~;p0ú<®Ù‚æÍ@`†M7oVè·mÿ¯þ9±¡:1_ó8OÅw*¦C;–S[ÅnN¼¦b5'>k)%PVwÜu~<õ{q”?9±“ЇT¤b רXƉaœxÅyMÅ *öPñ†Š3šÇ*žP1„ŠT| b¨@ù{åã•wüù¿ÊŸïƒÿè}]Çï:>×ñ·Íß´w|l|3ßšb|ªãO3íkü§3ï’c|hžñE¦¨8s»y«uµyIù=ê¦ í© öàE{l»Ñ¼5¶Õ¼MO;h\{Ò}•¿â»Ãrý^ªjbþãÌ;HÀbcÀuÂŽ;ÐoŒu‚nðVóöíºódýÆiðºl‡—è÷I»Ò¾#°¯Hl7 ™³°Áô’àw_¯ßUo&C¿Y€/ ë¼CTLyŠË¥N àQ°Ý¼FZñ.ó^¿“(O0˜Þ"/J¿­Ô¿@§7²×ëaF?òrçSˆœJÉ;­ßªÏSãEÊXÌß§0@AòÐõhçÐïÙ—(<ø–_½’ýVYI”~ë¾0A¿)š7Q¿]V˜­ß/+Q»ô»£%꛿K(!tKÔåw-×ï>*Üýi e)DoEêï æ 4`†.×o‘Úo"·z'Ùþwa|ùï7¾¼°Fqaœyaœù?¾Fa;UVªÛ‚­/Å»³¶šwÐiI;oYÝìÏÕæôóúFóÖ'åmƒM·á» uç…|^بצfï óÝ–ï¶'Í[èÀ{ÓNÛA«´Û•ê·Ð}àåƒü>ËÍÛ…À¶‡W{pÛSï¾Ðò…–ïVó:°«mÎ9ü å§|:åò‡–?¸þØ£?rx7{üd 'r*Ÿ_­»ü týÆhå"­#0‘·#åëXoÞ@§ì´^¿‡ Í`ä¦<ÁçÌû£Óõ¤öûçÔwèuQ±å?d¢~'Ñ~û„"w(åݪÝJå ›îyû<=„Ÿpä¯6ï–ºÌÛç¤u=iÞ>‡v·Õæýsp#€‹.Üdˆ.#¡ ýH`¢ EGLiѤEÃ3ZÑ”-ú´~{1†²Ç£âÊÛ=ʼƒ½îÐën,ƒ´^ÔG/Ò{Ñ.zA¿ºïßbäÉ„w&m9øLhdžÔá@¼³ ŸE¹² ŸEÛé ¯ÞËõ»ºêÍ[ûíyðûÓOÅ–ÀõƒF?xöwéwjû“>^ôû»ö»¹è``…y_ƒ ;ÜAà‚eËAž <x.ð\èÔ…ì¹ÞæOÊ—L>0ùè(Ùò)DW…ê7°EÈX„,Eà­7ï""I½y£ßÁðLúÐú-ûaæ ÝÙæý\Ò²”RTL þ9cQªPhó8OÅvNL§â8'fSñšªtc©ØªyL¥b&#9ñ‘ŠT\¤b"'ÞQý³ŠaœX¥yŒ¢b[8q„ŠTŒ â'pÆ“Ž¿W>ÞWþ«Ç”ÿÓ>÷¿ãoÿ3_û{~ö÷|l±i´÷–´ãÖÊ/¢;¯sæíklÇ{—~óÚgµ~óÚ~ÐV;` ~´ktÑ‘öÔ‘ïN´ûNàÓ»@+„öŠ.B …F˜K¿gÝ•úïJF"o$ßÑðÊRí9ŸuZ¿µšÍïìsúm^A{/&->¹ÈQ€ ÈTNê{ȲU¿K]\¡ß¥N‚_:r¤›L:´2àÕ”·7g óhg׋´^¥úß~è¹7?™ÀfRžLt1@õCʶùî§úà³ÀÍ"}Àvuû 8.ýî·zk»ôû3Yú)œ ê„7¿ùÎC'ýÉ@¹ á3PSÑ£ o üRÎ¼Éæ=ax à?9 ¡•“ ßvyëw¶]èÂN!tó(Cðùäå#g>tJÐáPò +D¶"xñ]56U0»Ì[ÄÐLúPô9ØaQæÍnú°að¸0nmqaÜZÚâ¸õ¸õßׇþ;Œ[U´\×¥­E»²h;-ùn‰·¤·¤.[¡V|·¢Ý·¾5åo¶¦¶¦®Ú@« ßm6é.Ö þ^¥ülÕÝm[¾ÛòÝV}ï ¼7¼¼igí ÕÚí¥íÒ^>Èç/ÚI{tÙžþ¢=¸¾Èå -_hù"g¾;Û9;@׺~ùÁ~ý ímpý±7ä€vðà€@; 'Y¡HYƒ „.‚À ‚ni©Ž”¯#åëŸNàt‚O§Ú-Ó_#ð.í":ƒßšáÑ]Àï~xt?üƒ@/ú¡”1z¡”9aȆœaÐ& =„ŽžÂáNZ8x])_WàºRž®Ðî ¯nðêínÐêÿnàF\¸ÀE¨ø=FB?Z‘ÀDA?ŠrDLiÑÈ ÏhhE#W4ôc ƒ¬1¤ÅTh÷Ö¸îàv‡^wèu7ž±ðŒ%-=Å"oúC7qÈ~4ã Ùš= ÙƒzëAZÒâI‹‡w<òÄC3^ñ§õ›í ä%À/Ú ÐI@Þê7¼Dø&Â#ÜDòÉO$?‘üDò“ÈOB®$dH‚v´“Tú½XHÅA*:ŒþGñŽ3fWqϨXæ¿Ç4aþ(~¹»üëΤë6eÕòCÒ’2·¤Í´¢l­hí6ðCݵ¦,­é«Ú€Ó†öâ…î¼°¯í˜0·åoohxÓÚÛŽöჽø€ßž6ß8_`|áÑÈëŸÔ¡íØ<ðü €wùôEÀÂ3ˆô àƒh/)wGdéDßÓ‰6 ÿ`ì0˜²tF†Î»tWј.ð ?üpCÁ E¶0d£}…“Ö¾]·ëag7hG¨±<ùÈ L?ÑÐÈBÙüÄ@#‹¼àÅP¾äuG'±àeCo4¸ô0siÅ¥z¸Úc½î¢âÉGÖdä gòðw.z(¦L”=¾ÐÊ·_ ü+ùBý ¿»UwWÅÈŸ¢~«S¡— ÜÃO*:ICOCÈK£Ý¥‘ŸîäM#¿'òö¤\=Áé ŸžÕºy¤SgéÈ—ŽîÓIË€F´ äÍ6ƒô^¤÷"½é½ Ñ YzÕë.7SùJÊš |&ð™è?9³Ð_éY¤gQÎÞðê\½ÕßÈÛ˜>àö¦zì§|%rô«ÖÝvòú«º‚Ï@êk zÿèk eˆþÁct;ÜAº«ÏAVž <x.ð\Ô ¹]àåA#Yó€É&ýä#[>0…è©Pý¶‹£ü"pJ(K |JàSÁ´åÁð ÿ¡èe(8CÁÎ0p†‘6Œ´,W Ýa©ÙÍþv™¾®…IûWÍÃ\˜ƒùÏç`þOοü+æ^þæ]þœsùïÄ,%¦>êMÌÁ·EÝZ”£%vÙr"?è³%|[EñC›k¥|4ß­±½Ö¥ü ÓÖÐnƒ-µQ~zAË‹¶ï-/tÖ–ï¶|·%¯-°ÞØ¥7yÞðmvðiG›i‡~|àëƒ]øÀLJò´'¿=tÛCÇ:¾Ðñ×;ÛØ›twã]?dô£ ùAÛ\pýÉ÷ç;€2¯€ Ý-"w åD¶@Ê ?à„ ùîLGÊÚù;Á§öÕ‰ï`àƒ‘?=C³3ýagà;#cg¾»ðÝ]Àï¢âðCø~ø!ÐEÎPd¥üa”)Œ2†F~ðáä‡Ã#¹ÃIˇnò»×>]¡Û >ÝàÛ ¹£»nР¼”7¼ˆíºK¤Ž#Á„N$´£ L0QÀD‘|Ñð‹F¦è ÝýÆíhÇ ç`xVrPîîàu‡_,üb¡ÃO,º‹EŸq”'¹âÀƒ^8=µôzPW=HëAZÈ܇úìC[ „O Jƒgà‚h‹ÑcGõ ­Nê7|úÓƒ‘¥ŠcU,~ðû/åÈO.ù‘´…ÁÐéÿHÊš¯l ¹ûB³/r娾Dµ{Ê]ÿ"e Ð Á”k°¢­ì¹rUŸ\<´m£þý«×ïþãÆß‹/¬Ù]X³ûwÿ»ó^ãŒÞOšy)¾-êÉB––ØYKÚTKôÖÚ­°«V|·â»ß­±£ÖÔyë]ºkhƒm´)å‡o/hyAË‹|/ÊÝ6Š¾Û®Ö]‡·‹øxC§tÚѶÛñÝ9|€õáÛ>>ÈÜžüö´½öÐñ…®ïD~€õEÆÀv¶uça?`ýѶâ-¾ý‘ÉZþ|P¦ðÀ€^ 8Ȉ\}T¼ý ¾ƒÀ Úª»®Žðî¸Kwa × ;êÄw0¼ƒáŒþ‚¡Õ™>¯3¸¡Õ™ï.|w¡œ]ÀïR¡»¼¾C ‚¼!”-†"o(ò†Rž0h†¡§0p¶ë®1˜pø„#s¸JCg])GWàºÂ«/„»B¿¼»![7äé¦bJèGAy#À -™"Á‹„~ävÝÕFA? ˜(`¢T¬–®»ßhäŠF®hʃžb=ú1è)Y»“ÖØîÐëN™ºƒÛž±ðŒ%-±è0yã­ˆü8ðã ÍÈÒš=àÓƒ´¤Å“Ö—ï¾Ðë«ÊFYú*¿‡¬})c_øö=­»ðl`³){6…ÈFŽ+ª8šÙôÅÙ”)½eC¯ˆºÏ†f6ĕW6re#O6<û+Êßý¡Ý¸"ÊÐÿ´v 9Àç¨x˜œ ír€Ë9­]…@A™¼å”=ž¹ðË¥ ¹”!—2äR†\h ·ä.§@Å‹ê¼ä-†^1ôŠ)O1å(¦Å*zÅènßCøÂ÷õ !*f„Îph ‡÷pg’7\Å™È?\õ›ÊǨʧ;~ÜñáŽÿV~ÛñÙçûkgnçü½‚äÏ'K™þpĉÿ«3kçÏ¥(¨ü òÎY6Ç¿5÷mÎÙ¶ó×H”:-DùœæþFùÇ¿(¿¢|‰ãG”ÿP~âüónN,:QÓ²hí«%uÓ’¶ÐŠ:kÅß­ÑIkÚEê£ uÖ†¿½Ð±xmÕßÞèË|oÚf;Úžõï³]W]{ðÛÓÚÓ.}ñ…Gò;ЦýÉëCž?ti;Aàô!½#¼:òÝQõ{ÈÙ ÁÔcgÒ:«˜]Vëp)ܾÀ‡"o(ò†’×—v¦ÆUÔQ>¿ó¡ÛMÙ®êoÔ8 ù#iã¹|çÒ“Ÿ|ý/2õ…O_ÊV´U‡PÃ(W‘ÓmÐMbü†@oˆ¢¡úä"dÍ·ZùÈ‘½"~†7øáðFÆxd˜ì̉f*ú˜BÚTøÝ€LS‘g&mb&e›ß™”u:¼§+~Ï‚ÿ,pfÁ×Ò˜þëãá s©bã ±ñÿÌÜê…õàÿ­½ljϯ…í[ؾ…í[ؾ…í[2ó0ؾ…í[ؾ…í[ؾ…í[c͘Û·°} Û·°} Û·°} Û·ïœÃö-lßÂö-lßÂö-lßÂöí;F°} Û·°} Û·°} Û·°}û,¶oaû¶oaû¶oaû¶oï¹Ãö-lßÂö-lßÂö-lßÂöí5nlßÂö-lßÂö-lßÂö-lßžsÇö-lßÂö-lßÂö-lßÂöí9(lßÂö-lßÂö-lßÂö-lßoaû¶oaû¶oaû¶oaûvÜ€í[ؾ…í[ؾ…í[ؾ…íÛ÷ñaû¶oaû¶oaû¶oaûöý+ؾ…í[ؾ…í[ؾ…í[ؾ:'gaû¶oaû¶oaû¶oaûj?¢…í[ؾ…í[ؾ…í[ؾZû·°{ ›·°y ›·°y ›Wë6oaó6oaó6oaó6¯æå,lÞÂæ-lÞÂæ-lÞÂæ-l^A-lÞÂæ-lÞÂæ-l^ùc ›W±”í7g·ðÜQ˜mê{£¦o¯ƒŒÓgëíóQÍÖAΙ; g›yÀfŸÂÄf÷ÎDé8̾w¦ÂìY§ÏãÛû(³Í>ÊMf,[Òl-$Àœÿ›®ç í±¬ËŒe·š3€ ÍÆ³'›g×›û ½Í9Àõf¯C€¹fy³ûgþ`]d²ç÷žÊÙf?D½¹ƒfœŽí;h¼Íøv²ÞWiß[¢ÏkØw®o¶¿r„çV4[#™¬ï°ï/ŒÒ÷Ø÷no6ÖlöXV›=–Ùf¬»ËŒuÌËR³ÇòäïÜ_`ÖJV7»»ÐeÎn5{+³Í:ɦf{+KšÝISßìL`©¹»ð¤¾ÓÀ>Xjî¤9göVNÔwØ÷z7;¸É¬“DýÎ…ÞÍÖIÖ7ÛW™`öU–šó€ÕfO¥ËœÜÐìšsOádsÿÌ&½Ò¾£°DgÝwî2÷f›3̈́枙ÙúlŸ}¿LˆÙ#Yê¹{ÐÞ™`î“™mÎóU˜ó|Qæ.™éæÎÁíÍîL7wÉlÐgùÔØÚ¾w°…Ùó1ÂÜ)³ÞÜ=XmÎÓD™³}“Íýƒæ^™(s¶o¢¹pc³½!ælß8s¶fƒ¾‹BÝC¡îŠpîšQûÕ½„êlŒ½‡£Tï¯QûpÔ^uGŽÚiŸ»™lîŸÙ¨ÏÞ¨½7ê>›‹³õ~LuÇÞOeÎØlÿí]3j~Ýž'تçìõ¥l}ç…:3©ÎýØ÷˜{ g›ûhvé39jo¥šWkQöÚÓF½æ¤Î2Ùg2ô™"uÞÇ}Ça½9¿“n®÷˜¨½˜j\d¯U¥›5ªÙz?:¥Îoª{0Ô™"uȾïf}³½›-ÌÙ—Ù›2[ß‘¨Æfjì¥ÆZj¾Âž£p™¹‰R}¯‡ÚS¤ö ©û6Ôö›Ý¯Xaö¶˜³‹Èv1ô/F¶‹ÑÑÅõzx0Ú#¡= º£ÈEÞ(ð.ïð.ïÊ{ r]ÏKÀMÞhø†ßhðF£ÃÑàL¥Œce ôÆ ¿1È2œ1èh,|ÆBk,ðcI ü8ÒÆ‘6¸q¤#íRô)t.…ߥèðRÊ7¸ñÀnôi ·B¶ù“PgIÈ—ý“P—IÐ+u’Œ¼iø:ê.ôéÐ'ú¤CGäÚ@gƒ\èl•Ì}Ê€Ì ÐeáwtÎFžläɆnÙà4+êLòè!_™ücò…É÷…ÜËö'ɧ%–|Wð’üSòÉ'$¼%?OÆ)"_MöÉÈ“×sÈ×"?‹|+ò‹d¨­Ï£õu~©£õaÈ‘ý´—äŸ?B¾üÉW ¿€|­íOö¾lç“m/ÛñdÓíNv7ÙÛíÙÙ¨É~–mg²›Éf&{Y¶…e»—l]²seÛ}ã2;öÿýîŠö)ÚT±QѦ?±SÑžÿÔF½’}жþ‰mÊíRi­¦•å¡ýÚ!¼k鑟¡)dky„aMøM´nGØÑöŸï4±s%„ïLçF¤õýbv„îŽ>õ§{ê´ÆG{ÄÒ¹ˆrv&BZÏnaË9#›ø½ _v×B§Óó;Û8&+Ç(äw*Z8ú˜¡†Ý¥ ³ 6q!¿/áÎñäŒìŒ'ÝÍ'¬`ZR¢u¾nl¹'y‚èÿ  Ò3Ì6ºo@ç.è,+Ýk¥;Ô•ÿ·@ t¶@‡L#[扇¬4è˜^‰ KDYQöD¼O£wà†¹0u’‰zÊDd¢™(_&xe‚g0ø-Ý¢J6Å„€&ô!ÈRÖ¡B‘ ¾¡H EZʾaà†~: CÑ/ù‘/¼’-[E -iH‹¨dËXñÐ#é‘(s$ô‰„Q(GÊ…²Ç££ c"dECV4ô‰M<}зÓðÿäÜD”#†tÀß,ðL€^ xŸ†´ÄV¶ fEº±‚·éYHLß$Ô[ÊyIÈ›Œ|Yà™=ÓÑ6éÐ'4éÐ1:ÚÐN6ä·‡ä¡LYÄeϯl¼Ï}6~瀧S|ÎïX'íX'íX'½ö×I;îÎt¬•v¬•þvk¥ÿʽZk¦ô ,ds™4^õ¼]·±÷ÒYI#ÇLkâg&üܤŽãtjö¬½9fZ Ç+õà±]JùÙWÛe›æìOÇ2ÕqÌ´b~ÆÒ•ß=/dç¤ým_¾¿]ÁÌ 7ÍÚ&¾‹ßAoä1^B5çÈ]ª㥆ã¢z´sHÏñÓJ9~š ?K^ÈÏ’7sü4#Çínâ¸Ýz~ž|ÇUuã÷ÊülP ?WÊöÖ¥x/.ülMƒ¥æÎ±ÔŠ51_¼Ûœrá89Åš˜/ü|y‘æ|w,oWó¥Dƒãmà÷Ñ+ø5_~.¨œß_sç÷ÑG­™añH÷Ñ‹8Žw ÇQ3rïJ~݃cx—p oÍ]ôr~&ȽünÍ™ ~¶\Çï¢5wÑ›yŒ_~½Tƒ›æÖ3­œÇuqæxÝ ^w#ÇJóå±\Ê9>š¿c^¤Þ#L4 ‹{¥ŠÃ-ÅjñÖ` mã÷âtü3Çß®äøÛ®ÿÌÊñ·K9þP³&VK(¿O^Âq¸›4wÊ ,îr†‰¦Ü—óå˜h+ù¹&·ÅÝ“b·²Ø-„§DXJ„§+áÙ]s:c,#6òsXÅü V%Ãà%ì#é^ã¦mcwëï—°’‰î®K÷ëœ~št‡®òrŒ4:Ÿ,Ó¯`gó¥³Tz†åDØ¿„…$áx»rïBŽ£VÃîÚÑhé첕Ÿ³ÚÆÎWÑýG±#L`Âø%$ó»™ßËóæ¸k†Î¯ÐYé\–7?×_ÈqŠk1ÝÉ'ì_Âò•pÚJø]¾fŽîÎï½[ùÝ÷bv¾…ΪЙ2él—;¿`eÉ÷”°;˜÷41×€0‡%a«ï­‘Ç¥qåwç¡Ûð_Ý Ž@~ ä‚w ˜oÞá]è‚A º`У¼ÁÐ+2ƒA·ïBÞBÈ[º…¨Ã…(ïBÐ-‚.‹ÀoêotYšE¨£È ¯äAZò†"-i¡ÈŠ´P¤…¡þÃPWaVÃ\–pä G¾pä G¾pð‹@ZÒ"´¤E"-m‰rFB§HÔOê/ ºF¡ Q(_T#s{¢!'ôÑÐ'õrÅ o òÆ \1¨ãÈGþÔSò'@¯äOï¼K€¾VÈ´B¦ü­ ±"ÝŠô$Ô]ò'¡I¨Ó$è—ŒºI†Ì4ü?u˜ŽzJ]:ôJ‡®6È·Îù6ÐÙÀ+²3 ;tY5Ì­ÊFžläɆ¼ìJæfN>Ù™Ò#ûÌZYö•ÉO¾ÆA{~±Ön{ÿCöyi² Ù²«õoÛóSµw#ù‹ä+’Ÿˆ6‘üCÙ/”}BÙÔú‚Z?°=Pëû‘Ï'û{äç‘§õç~/_Žü8y˜ü7Ùw“ý6òÙdü3­_&ûd䑆¾x™ïE~ו|.òµ´~–ÖÇÒúTäKÉ~T{X÷Z¿©­¿ÔÖO"©=¨­$û?²ï£õ{dŸ‡|ÙÇ‘}òkÈ—ÁxQ|Ùg‘}ÙGÑú'?ÇéðE®š/BkmRIwŽsÕÌï”ó8EÓª”c1Û8s¿O`cxVR¬H_÷'”ň”ÎÆ:ó8?•j|Ší#Åô1²;v„‘Åó¡óþÒ}Qw#™Îú+gû,^£„ÑÂñ!¬<ÖÎ6ŽÕÈc.–òØ9¥³x%¿¿YÁïm2Œ'Âw",Ÿal§•×É›ã—2,'ºK6¼P“H÷"éîÝá¤;“tv–îDFÅB$,&£bØóø†Í7؃ǭ)ä¸ÁM{ÉÀcÕTp¼%wÇp%‹aHK¥¾’•Ç-lâÀ+.„wÆ›cþ–r¼_=ÃL’âË ÝC£ûÁÁc"¡™Å4´°Ï‹¡•ÇDý.pfø„ËIç‘¥{nÐc+à ¤x¿ÀƒÅ’¹ÛÈp7)Ö`/ÃÆ$\Lºß*Ý mág÷CV†‚Ùkdx™´L:Í?¦óü‰Î,–ÃôŽßÛÂã ²;½tÕÒÊÎñálÄ£®ã‘'ރŊ øƒ‰¨ŸD=ÃFš ýâ!?üãA—ie1$$ÜMÔoòeBæðËDþL²ù 7tAàŸ ¹Áà ¹™”Ž¿ ¡óBè¿u°°‘-/B¹¡mÇ"Є ,!Ð#òB‚|¡H EZ(d‡"-iaÐ? :„A‡0´AÚ.ù‘/ù‘/ü"´¤E -i‘H‹DZ$Ê }"¡s$tŒ‚~Q(sÊ£À3r¢‘7úD£ÜÑ(S4òÆ o ê#uùñÈöL@þè•€ü ÈŸ€ôð±B¦|¬i+h¬(GÚ ù“PŽ$ÔaR [þNFÞ4ü? õ–Ž:J‡^é K‡®éo òm ³— ²3 ;tYø™ÙÈ“<ÙÐ1¼³›Öô•p9èÛ!=¿÷>ÉÿÒIÛý‘¶7Ò±/Ò±/r­í‹tì‰\Ý=‘kx?Dš?‹Ø˜“ú•/׃ü…V·ÝÊðÚ¤{½ÞjÜv)J(¿×ÛÈã¶ë96Ì6ŽÿîÍpe¥8œî<JÁiภ<Š‘ÅàTîõx<”J‡3”Çm¯Ñ`Ë:XL)§7Ý^ª‰‹bå±Û›9FŒ•ÇEiäqQ¼Û‰ßîË1f·qŒYW~Ï·ˆÝ‰“°<4³Í<^Z!É‘b£¸³;¨—Åp·jbrºrœ˜r NŒÇF©á±Qôü]9»G'ãÅ&a¤Hq9½ù}ß•,†;}¾¥Ø(6ŽßÌcsy\Î&Ž/ª¹çëÌ0!”Øí:~Ï7”ãËV¨øtVÌJ†/«`ÅX9¾l#¿ç«×`Ë6q¬¿ã[Éãqz\…b¶“I Äm/åw|Y¬oŠõ8²‚ÅÜpeÝ8¦l!‹I±P$Œ|«ÃÀâŠË÷}/»çkc÷{%T!éÆãëÞ[L Ãv“bŒë8ª‹#)Åf eqØ <îw‹ñHø¤νŽã”zp¬R#ûXÈc‚ocqÁ)æáàÓF¶!Ìùn®1”ßÏ-RïæúAŽäøÆ4þ ñ‡Èñ‡îþÐDzü!˲üQ¿þèSþçZÐúCGÿf€62ÐŽ  ­‘Þ>rÁ#<À#<ÐÀ'|À' …™XÔõc/ôHnfر¼Ì@ ôõeOxe <ð $ø ¥¡>Á?þ¢^LÈoÂ{ÊmBº é&JoÊ‹²ÇBf,Ê 9± šXè cÁ?yc¡o,hc¡s,~Û‰ôq Cùã@‡:C}ÇG ê"|â ;zÅ>´q(o\ 3Í(¯|Ì 5£œfÈ7£į̂73ä›!ß Z3t0Cw3䛡»òÍmìÈIÁ{ êÌ>ð±€zX ‡:XÀÇ>Ê=RÀ'|RÀ'|RÁ'eHTðKE9RAŸ úTè’ ©(G*ê8õ˜ ]RÁ#à>àcêÆ€:2€—ºÀË^&ð5 ,”Å~”Åžð3€W ÙÑô—luðD?D½¢^LÈoÂ{ÙØH7!ÝDé-Ì‹EÙc!7òb!+2bQÞXðE}Å‚o Êg‡~±Ð7´±-Ìt‹mÞÅ6õS {xÄAnÙÚÐ)emʇ²šÁËŒrš!× Z3Êi†l3êÌŒ:3C¾òÍ 5Cçð1Co3d›[˜¹hl x¤@? t°€|,ÐÁùÈ·€|,àc¡r/ðIŸ”fV¦‚O*èS¡w*ê9åH”=ù äh}€¶g e;_kßËö<Ùî²ÝÞÖ^'ìó¶ë=²=.Ûâ²Nû­òýqoZ>cx¥ó…òúlO·=_H¶s[{™ìdí:¼×IöoÛµž¶k;²MKöì•ÖyÈn%{U¶QeûT¶MÉ&•íѶ¶¨¼ÏØöžú•lÍ+­õ´Ý¼’­(Û‡Z{P¶µçCuퟌÕýtmȦ±á´÷Þå5"£ŽÅ[ e¶—„Èzèbàq1&œÐžNÍ<‚3‹ƒÐMϰk»5òØÇF;ù»;X äî­,²óÀÆc8³˜E„KëRÎcÔ`®èz¯m<6hû¯dî<Å'§x40O (gqÇ]Áà ¼²xn7YYœdŠIHñaWÿ‹ˆ°ÂiXV8á0îâPè;ÔÊã—ÛX¬¯&† NXà‹˜°À)n Á)Å wqJ Ãè—°ú½Y 9Â[•°ì,VáR\9 [Ââ+Ï­`ñ•)F¹ôó\X<õy(ÓR†Nõ‰Ü’ý Ù’ýÙ·!Ÿ¦Œ7òM¤ýær~®Ó—cm48[îÌÏp´ZÙž±´_lä¾D#Ç~ÕÜûB¾Þz¾ëà÷º|Y)É'(áþ@%úÁ…ÝÛ¢½\{7¶ð8¡… ÊboI1¸|Ùž-ű¸òoò`¶;áKËZ•,æ“Séw[YLp£´ÜE1&w‡îŒ­`ge›T_Áb*Ò)Âò! éV?oÞV_Ž;¥¬V~ÏÆlMºSåÎöioް ó‡l::§GX4tŸ‚ÎiÒØ¥û„9CKy„©@k½¹È—[È0 r!;·„wÌ-åû| Ë­`X¹ ÍÜF¶nœKi-ìü^уΆ¶šùÈÉrg÷4¨Ë W†­C˜7èå0¨;Yç€<‡•Ïu·2ɶ$H6Ñ$¹tþÎi’^Éd—5²=;:G(áR@¯,g¦—,ÇAüQwŽ"†=æ=’õ ÏðÒ*æO¸‘µQZ#ÓY£°1t†’ô%ŒâGg —ô¦9*Ý™aÿP¤;,n '×á`g!+™îF.x;ŠU šOé¬$­Í;ð;«œc‘¬r† ä¨`K¦¤;P~éØÌ0ÔïŒîLѹU2ó¨OJ÷À?Šþ­dû¶ V2|(º£UÂ0}ÉÖ&›˜îE´2œÂj¢ó±T~ ' iiøšá_d2ŒßáÅÌÍE½üºç¯xtlÝÏyNßœ43 ÉÈ"û˜ ÞuzJz\2Otåt¨)@Ó>0 `tv] 0ƒ :c‚ §ž0’{ g’z_Iî>ÉÁ¸ë{Û§¸äÚÈh+t Úvç–yh ¦Úu@KHV?Yürt²üIšÖúo»óÐñ\sO–ŸÅ”9gV`PLVªíN³)Ë4&Á’n¶d%Æ¡d›ÝbNŒË“eŠM±Ü•˜Ÿþóexà™0~<þzzzõ~{yN”þJϸ‰:ϱäå9ÎcœÎc¬çØñ¥J¿ÿ“™e²»¹éÌ6K–=ïÊùL)©¦«¡ÏU~‚ól·©nÔÔ=‚²cùOÞê=zü§Õëx~çççþ-ù™Ï¿ÿ^^?ÿã;ÆÇsÕž©Ü~£s N™yi´¦Ø‰›Q°~zÜãˆöˆió2“ ÿœÆÿçôþÕÏÏÿxÿ dü‹ñ?a¬×¸¶ãßcâØŽñ5Öï{Ñ9®ÁÔÆ:É5:>== )¦Ä4þ¦'Ûé.ý˜iÉŒcYµMÐõ”2ZâÒí´,O£„Ž´ô`)Br;º/ Š™8kZ0êÉÙtCâÌË“z iúãœÿ6ƒÃ ÚŒN~s éXòg¤ßf$h~“:.cÑíÆxLã9vŒçøÉãÆ³4O¯1Çxxñô˜<žxtG6Ï»<&M £MÒpp›e²gYÝ쉖´,SVbzšÛLŒÙ`²˜Ýâíé©nþ³‚tºª½\žWûµrS»µ"+y=ÿ;((Δb²ÏHOÉNM£|SÌéÙ`…>Ë2ta¤ÇeNZœÝ’Jº¥eù™Ò®æÜ¦Ìm_Á;ÚUÞ8-ôŸ¬©ui*”ÉtšÄ©=-!þ*Ý~ ݯ3Ùã2-q¿¤g^ë%è$O{×j:ϼÖKàd^ìù+ËðÛ âÌ_¢~×™×¶þ]æüwL¡¿H{§™×¶úfCfâ¯Tÿ?=~1®ýB8™gÙ2¯ñ2 !þ{ qéÒ¥¹mÍ}ÇvÕÑqÎܰë¤Iêäh“À¬Ë²äý$KÛùk£MS¦pm"Ÿ/£ÙNïümøý4Qîam2^ž8ûâ´»ÐeN¯ë¢ëx~ŸçßõÿïÊÌIó e“?ÑËëÊëÿmüÏ un^¿iI¯ðüûÿ?«ý³,ެ;cM™–Ÿ×~~ûõ˜0®£ý¯Æó Û_ݤRÚ•ePÿYû^^ã;Öÿ®ÊÓ±ÿ÷¿ýüªñOû ¿ÁøÿéþŸ—׸ŽýÿŽçê=û¿dü#û¿1üÿÕøogÿÏkܯŽñ5žŽý¿Žý¿Žý¿Žý¿kºûôtìÿýbý;öÿ:öÿ:öÿ:öÿ~çBtìÿuìÿý—=¿Êÿ÷øwÜÿêÿÃÓŸèé9¶­ÿï9n\‡ÿ5>Ö$¸©ÈI§ûºUÇnj§±Kÿ!u;žßü™Ú_Šâ:êt«ç{·õ„ÐcÂGŸß2ãNᘳçˆSŽaà“']:ÖG¨ísï‘ý­ý|Þ¹#3Ãå“ගIJ¾¾?<[—.Lz|°OÃÈOÅüdö¯ ¶ |õf<[9%LÎÔÙ¯$·æ ýžùû[ ?¬xw÷·áBmç'¶{ÍïçSߺ£ècë9±ôdzѕ½²ÄüÎÙ·Ïîñ"è+ýãc'®ØV%ôgù„†ïí÷Ìò>«ü®ùvÀ“ç¾IÖS,="¾Ü«RÌk¼oÏøõàÓÄø4,8öHâпuÇÍ‘K„šOßù¤×ý™>ïGz†ùÄìKËöý0¯åÿ„™/}E<î'æYœþÚÈ©úSÞ:FaË;Í>Ó¡ÿ„ó×jê=Ò"ëψ¥ÖqoéFó‡öZó&ò»Iùwtš¼úù®AJ¹uz>í®—ßT~׈oóq;äÓôÖù’gKgžº æ/ ¹˜X<|ôŒOçƒ/®Y!ëjrhîsBMYäò)ýïóù|F e䆮béÜÞÓzÆõ|…UŸÍ>·HÌ/œ_s èŒþúë]}„¡Ù.~ ' Ç®{´|ÏÛ}…›x»×¬zllÔÜhŸ“}×.¾0bƒXšöúÒ=ç: Þ'CV?Öë1Ýô[œ¦/?Öovô¿ûüÈ’„cNaú< Ôä‰'–NRõY}»gß–ýbþŠ¿½.ÔÔ€Žõƒ=wžIÈu.9…,Y"ÔÄõX”wÑç”ÜnÏ™¹jÔCb>Ó t¬ýwô›ùâ­>®Â°7Üëý·BÃwòï/jü»;üßš(–V/+>WwF¸­ì…cMbÞîÞeŸ}¹ ô¬ÝwÜ2èîÉÖW„†Óï‡õêQ#ÔÜí|Cu¹›Xz"ß?×"1õ_ý©I¬w )_ðùò¡BÉ/Mß_­Œ‡š;¦œN¹ å£^¿tªÚ¿&ñööܽ3}Í¥žj7 y>l‚2¾jn~uUÊ i>ßðþ^&©ÙYm7¹Ý'ñv'm§>¥ö{êeÉý„š>S¶š=Yé·e×ÜpÜ´WÉôóO ÿjdÍ(á–†ïŸöÞ6üx?—–ûc…>²~¯-¸ë­}ÍB“×Ùo,óùD‡§¾+Fõ uKê§yž·û¾²Ä›²)åiØ50ô£A…ß‘¯ïkÙÓü¬Úžû<}Û®ubÁŸ?:øðáýàÛ—žx±¤Ÿà$—ëå­“ûVÇÅ‘÷_½uý£jÊyæÃ³çÊå ö¼×½ð•ér=ƒ/ë';G'lˆ?"4lù²éë>‘ýÇîïºý±”×oÁÊÓϤ}nC~Ö/všÜ'9Ýùˆ:/mŒ¬Ù›vL©ï#/˜2Ë£—ùœ‘çšë "7¿rZj2ë/;©”wÞ&4”Œ 6nŽ,-zúæàA>X=‰¥ ßéûÒμ^Õyr2ë?;}¿gÎT¥¿5P¯\* üèâÇ“ù´ÈóDQ«éÅëg‰cN}à WÁSðaýfç×~I† kz6}pô!…ߑۼ¿«ë!Ÿ¯N[{p³ ––¬¼³éú›Å|q܃º‡€žõ“]]7:ŸùtžÐðˆ.è£cÃ…Ãç^‰‰›ò™Ï¹º„›^Ÿ¯ŽScý¢»º_뻺tûjJåʼÒPøÞ8ûà`¥>WìHX˜·U,eQ̯Úúá»=…Ñ¤ÍØ”·|XÿØyrÙ}ß1«íb;¿»Éø£òûðš}˜nQëƒÏSgçYtº|x8pñÆ™Ÿ 1³š^ýpˆp8åÝo^Ú/–¡c*— 5¢j&#?ïÒp4ªýÒßò×’=C•þ}xÞñ’?xÂçyܾ·iÅÙíâ’Ìégö†} ¶çݼ_,›Õû‡ª¿©ãmjÄæaËÂÕrܵgä_ÖoWæÿ²Çv†¯/7e÷ðKêø½›÷xéQÇß~úáÛ}Týú½µeXÄzŸäö•ºIœ¸Øiúý'{‚ïÁ½Þ~ìÙd¡ÊtÛ$ú6vàüùMbé{Ç'V­?%Èíq7ë;£º5¨ïPµ^Xý)zªO!>˜¤Ž×ý_7Mìõ>èY¿Ø)‘÷RÛÓõŽÿóøkoEïC[—¯5Рô«‚#õb÷u›AÏûÃk+]O†¬]ðŠ§Ž4‡\óþ“ßVûñas¿iiÓ4ÿòzÐñöo5çÏ–B¡aЈiÛÝw©úF}·02𤠓ûϽVOõ¶¸XÆšvdýbWÏG.þÃ2Mí×·¼y¶h·RžCãó#úœëãó€×OŒÓ} –>ýÜÃÇbÂLÃÙ÷žö+ þjžuÛwòü¬?5…õ]£Ëjñ)¸uèú9kŸ)à󣵤nÎ_WŠ¥‡m“/.]% ^½ëGç­ cý`—páõ£z[h>ëw Oímy/ç1¥½•q9…µ÷.óK—þ8ZTÆaý…?6/ùÃb¥þVEà‹¼TÂ4V?òwXþ®‚Ÿžºa@Ô¤'…ú{o;ø˜ppKyx€î¬Ïw]oïüåÛUbÙmžÎ]Ÿ¬~ÿ§ðùáøü7 vg¡þäæl³Çh¥þ®µvÊä«~%q‹Å‚{î¿­Ï‘e gý ¼Ïž¢›÷¹Äõ®ošßuµ‹·Ò/$ ƧuH—ó]—¨óí'‡²VçõWÛu ëå¤þ£+ôõÏtý‡ÁW­—´Þ õY®|ÏʨílT¾³‹½·ÍÝy]9ø±~R>ió×Þ&¡¾rÅ=_üø”p0z¸cùüãʸ˵ø‘5nï¬ÐŸšÊúAùt´^¿_-׫Nþlùqá _ÎæÐn—Ä2÷¦ªwêf‹‹åù`*ëåñS².—õ¢Í«yQ8x÷ŠAU dûD)ÿâ-dØçŽõ‡òõ›&×>:B¨_»G’2.ެqîÄn¥ß)ãi*k÷òæƒ?[_/Ôå?jþû9µžú>×o܉Ñ?ýîgd?6ì‰ Ð³ößxtüçë õ‹ÃßÌxÕ¡”÷@ëÌ9Ç¿¸]iÿÒÏgr×4Õþ˜ÊÚwå°Sàоõ6ó§6¼¥´ßãÇ£ò½A­ï4P¦½üfê‡åï¿p+ëwàËúÁžqO÷þ뉄zKÒk_|M)××>9*Ý¢|ÿËþØéPÌÿ½)œkþ̹èY»ïYÙåË Ý7+ý¹>ü˸¡+º+¿<{ÛìE·oô9ËøŠeë%MÌÿ ôɸè,ý)Öö|úÁÕ)%ê8 ¸uIçC„,×ü„XöЉ¾ö³Ê÷RÌ—†kèYØ;µ[弨ƒê,8ûù›„IC–˜½WùN–½qw^àQ/\Öö®Zm¾yìêþÌÎ,šü¬·ÐK-ÿ7=Üs¿ôWçgÖ/ö6†UVUçóz7[9\0¥fû½qÇ+ŠýPömØöšÅËÕþ9ãÎw¤¨ãÓ‡õ—}ƒG½gôQõryDðé’¡Ìü{í|{cµâ·”‘[·ø±à݃w‹QÛÙ‡õŸ}–fx&ýT=¹ÿË¿³Â÷Ån½útVìವ)¢lgë/ûÈI¼O¨ã~ˆlï¸q@ß=S6ªýïAš¨á?}ýÜü/¦á{èÃú˾ê;wÞ¼Í(ÔQ/òZ*Tÿ#º–XÆìG±Ày=yÀúSëûjn ©C¨küûŸz$Ä Õ'v\÷~bÚ.ŸŸ¹0v©{Ÿ~oäÇŽõ‡}é9¤Ô_ݛ۞)~ï¸RÞê×-+—¬º]Õ•–~Ô ô¬_ì;~âñ{<ôBÝKÅ=‡»)vdõ ó :7üÉç¼\ïo罸£gù;¥¶£ÀúǾï£üßÖKñßêž” +¥T¯HÌ?ó^³¥Ýë"ú}‘|D¨ævR'¿1Q{Ÿ¾ nZ/9âJ?ç~¦\àÇû&ÿ‰Ã”v©›3r•ϲYjýD¾9ÂkðY¡K|ïGÃ7‹›6¯ˆüôÕáêzƒž÷“ò‚÷ðIVÆGÝÄìò:YÔv‹Y·êÐ-K'û’ïï¾_ÜôüK®G£ßWû›ž÷›¤¥ß¿µV'uß™y¡k…oµ4í&Ê󮸉­Èó¢ÚþzÞKް2o×õzïÁà¯s…êä‰ÜÛcƒb7mZ”wáÉÿHñ ô|ž¡ÒÝvH™ï~µàõ5Û7 Õƒó]Q¬Øå›^œ?澂¹b™)O\=ë/{y?>zì9Ó'çãÑOh™i¾â§mbþˆjoéY?Ù»ö½ã‰þÂÑÝ~k»=d]¾Èï*ýSÍÔõ¨¥Ÿ*vžžõ—½Í{ðµ#]„£OI Oj?͸tþéû=”ùyÓþ[G Ý~Aí§zÖ?ö®—4áè²/ê7¹4©ý"9¬ÚüÞʼºi7M¬Kļnü&º§¿þÔ4Ö/önÝuªÏ÷váhJÉ”yÝO Õ¦¿Ø^7E­¯?{óoOú‹yá[?žQÛoÿ®¼}>ÿ;½Y8zÈm놡zá¨uóîUì½Í>!AÏÑEñÏóþ²_®.ªÝ8g¸&÷ƒ£3:¿Ú'þÏBõìæ¯þ¤ÿRùþo^óзnï«ë€Óø<"}~F*ýðè½9¹®Qû·÷“nÓ×mTÆýæ7¿XW¡®MãßÑúï’*tG]o,)ð-Tÿaå²7>ÄÍäæ­{B·ùÚ?s±Ú§ñï ¨{¯ýR8ÚyØôô“ƒ„êá­·^ê.naþ–º®47ÆÞºê¢°QÑ¿öôö„’ﻫãjØ3;'õZ¥ô‡-·ws[þòru=lŸ/¸ŸR[¿ž”§ZþanžÿûTqËÍÒP^·SÛs:Ÿ'¨ZÄu÷õDXAªnÔ’“ÿjóÉ”‡g¬[¦öËé¼_|tWÔ‡‘šò¼Ü-Ëú`™ú}¹9{ãüîo*ß©Í[2z½²î´ZžéúËÆeíûcÂVÕ}gøðóéÊwióú©ïþR#ŸÏ|¾‘çáÚµ[®¿ßOù] ¯âž#ÄÍom<û§WîT¿ÃÓù|{`ãØBíƒ'+ÒíVô®:s÷ùbqËõéÁó^Þª®LçóAÈg¶^­ä¯]<ëÀ¾Ñë”ú¬jì='íƒãн·eæbºëžWçëé|^˜DŽé(UÿŒ*ZéPê³jbξ¡:¥þ¶ø·>ü¤#DÌûX‡)à!ðáóƒNš”ïkmÂmÃ+'¨ó~ÕËq{ö¬Rõ™öþMÃNOóÿ¿2¤Ö¡?5ƒÛŸ;ÿ²dQÊ7Bm”cÙ̓×(óTÕóÅèÔ~éW¦ó9=Lõïf°þ°ÇüFëÄW7ãNÏ}äF¡6¸jyKz¢ò»ê)2P£…q|vKáÛ“÷­µ©ëË3XØ3`H@NÌ;BÖÇûê— µsÀî¥ü®*–¼}¬ßÑseÿ·ì–N1ŸÜšÛ¿Ö?v3{FÈ¥UÊÑõB­°øÌ*k¦ò[Ög6âppÍêwcë'»Yê>¼§CȳŒ®ÛoºA¨åå‘WácØu‰«0Ùâfß©ëž3X¿ÙM«3kþ½jÝ9Ùek¹ò»JZ  mÊ¥ØÛ3X¿Ù݇6@& ²Ý\;¸?j&Bù]U¾çm—£„@æÏŠ[vxéV ÈrSÐ_Ì¥WsøÝ2FoØñÕýëû¨¾ÒuX퓸¾ø~OÀ7˜ŸÏTõèþ¿ËÂòV³çñœu7Å1‡u=tÇOž|ø…^¯w͵j½ßu8䣨“G^uúᙪÝì7¿@µæ%ºþ SÙÛq·ž×}˜Ö}gëp–µ`èôCª†žªO÷ÿpá'oZe^bWª9¬ÇûîøÉ_5ýà7a5fã™ÔðÌÏÛ]‡ßZðÚ_u¯»û5è©zu?ZMúà óRŸŒ[.,4‡-s.³ã'û˳¨à„¹á]wÕmk]‡ïXøØô¾§œöh¦ªO÷çyÎù·uæ¥^/šÃ÷÷”µ<òœ?y°á¢©ÏËlb²Ò±ó®ÃúɳùSnv}Íjæ7Õ¨útµ ã’Óæ¥·ø,ÉŒ:n¾óæe7Nµãn»Û¨Û“¾ ¿`QÞNÿR£êÓ}Ç3{~6g±y™,‡ï}Ò¾å÷S[oµã'÷ŸŠö¸íQs“Þ÷ëK•‰x‹S¿kT=ºï‡–a™—U/¸õà±eæ°®ïîøÉ©Ÿ<÷øsó÷/ýÖý§\}éåÛ.l Ýÿ<á¶þ» UŸî[šœ,7/Sr›ÃÖ2|›?)Õ¨ü3sK{ɼ˶,rçË] £ê“{¿ã2Y Ýþ¦9¬ÖAí¸[ž­3ÞÚøbí·žíò·ÛïUŸî¥±ž¹µÊ¼­ÿþ™Ã߸üÞ5íøÉ­³YÙ“KuÛmŽýÖ¨zt¯T—‚£æå_{ev_ø×ÍáÎ%W~8»?yUøžÿg¥ÙLãùžŸëðQÈŌзªG÷~ò6#¸¿8û”pk¶Ç¿'¶wò-3§ÚóõÃ˯®{ì 3]Çív|–ªO÷>sîƒ{Íal·ç'{KB÷ºÙÕ§Öœþ~–ª?÷êúzêoÖ„Ø #Óó¤ë"÷ºÎlUŽGÍ­R“ÍSÓ¾ˆ©/Mtìà‡ï^°ã±ö:®»þÔH­ }Ñ™ÏÌVå{TÍÏœüê}‰“=qŒÜ³íñ»»Üìþf¶*gµí›èäW·£v}nùðã = ìñoÿ¶Ã3\]¾ÿõûŠ«ßRnÜÍ»êæçåýó^§üéãù›'¿¿ûÏÍö¸·ïê›ÿ®÷عó:è¨zÐoM‹œu—Se—>øýcÛ͓ӬÌÙê¬ öÝ)æzðT¹÷¯ þÅÚRÍS?}ü›tÚrœÌ_ùibô{v¹õ=ùºFGµªÜH/:q†£Çì¦öýŒòˆ¾ÍýÔ.#¡íï¼Qá”g­*ÿ¾ÊÄÛÑcœlœ ;ãw¿o<áuQ±ëˆulÀt}Míc‚¯êCߤoÒ“à¯ø˜Cgß»`V\‹ëÈÌ«röþè §=¨U帩ê…))ѤO ,;v»ï¡··¿âìaבÜU³~Ö1ÙwÕªr?ÔqçÉÙ¾ ={FVmy‡^<øê«'ìõÀ#ÆÀ–×'töµæ¨r¿óû2¡¼Î97 »ÖµÏÚõgè±…3Þ©?¯ýø•w;Ž™ó Jß}ÿZ/×EÖöÆîq#tU=Ðë0æ“_‹øãæåæÐÑO®>Òù[Ç.^ØvYï W·lG>ëOÕƒ;Ò&ÊJ¬³k ÷Ãmûºî¡Ïf}ÉÕ÷Ç[Í7N&€§ÊÿöäßOÌ'åøSÇ!s¨ë×›8çqúÔú°m‡Ýjÿß™×ÏQõàöˆÊ›o¿x¼³¿¬ÏØúhx`å€ç$בÄ+{:\ÝÒ ^ü øª¼tûÁ·g•;òô3´ß˜CÒËÎ:j×ÿ#ó.”ÐÕõJɽëÞÿ|Un{Y ì*óÉ¿WoüÙº:'ßi?¼åxè^»ý;ÒR:æ‚¡³â=ë ’Ë=³Ï1ÌQõÄÚmÊŸêäÇšþ:û?C¡F¿W³³ï|dÇkß|kâÛö<§Kªó¸kõ¸:Uo~)»KuóÍ'?ÍíËé¸ÞòÎYµsm²=¿=²ø³Ìåw¾ìºH«OÕ‹_¨ùsžAFWõcì|žø¬0󉻾Qí뢜}ì’}@GÕ“[˜Ò ß™¿Žýð€Ÿ?ùÃëóO»íÜ,Òó@{Ý NÕ—ŸË=ûòí¶½>ùçäŸÜ1éi»ÜN|RZ¹éý8×uŽÃ½¾ª'ätÅ®óIU‘{^äJ¿çvòã?¤¿ÑèÒó[ðTýØ¿äÕçÒ~x«SCË/yçwæ užÏÕ§ë‹Ý¾2ÚúÎÓCN¹Ö©zr“>ðä¯ìyéл6½ŸÝüqaÞV§ŸüÛO?ºðÐf±Þ¿Óí¾³¯S§êÉO›Ü{â·ß3Ÿ”V4ê°yâìk‡›ÞpÆUê<‰3žš«êÁ j¼æ”§>ÿq‚N{õ`¬#‡=ôˆ«[{ÁWõáº[=>=xé§î|Ùüõïš'äXß_¸úiYóÉ¡œú¨Ï“èñ)tT}¸Z¯›ÙrÉkæ‰á«¦ï¸×wü¶îÀ8Ïמ7¾Wò·9”Ë\U®Z0ù·åœr¹+¾%‘±ì‰_}ýþ¢Ë–;zÐíVWº5 _Õ‡©öÕáŸïýwd[Ùí^lžø~D{C`±ÓêöØ]_ºÞ>’¶ñ(tT=ù®>ñ¤ulošy¢;¾má“N»¨Ú7§š«ê÷ïíøóÝ[8ùùçîW½wî±ËéĶÔçk_ û_úÕ.iòò«ßš§êÇ%"UL¨Ó¿è~õÄšÎÝñ¯þÈiwT{ê^sò1OÕ}NÀ<¥çÅ'dóè®#2úëhqu«õVÒ«ú°gü3q7nù˜ôê|ˆ;'f|’ûÌ[)®¾Wg,¿îﹺԺxª´É´ªâ¬3Êúuá;ËŸs쫬í×$ßc¯Û÷ýâì¼Ã+0óÝå©ê…c_óTýÐó=G™®ïl{g‚#WrΎҥǜrÑçžJM—‹Šuí~L6œz?OÕ“5_¼íWוàÈ›÷Ï{žÈð3Oø –nX¾Æ©'ßúè²_L¾ÒÕ©æçà«úQ3oŸ¬9ãvN&½s©«SËŽU_\3u>l9dÙ˜l>:ø-tœ“¯›âd¥ÎÕi-3ÕoÍ·ê‰kcÞ²w“^zÇ<Å`yÊÎü”)NЉæ3kzk¯îö*~í¶~á´ó­úâr¯—Úã£+beäc×ßG/šöã†3¾fT;xØ|«þ¸º¬cdsÌS7Z%}¬.¾øóÀWÜóWß=ç|½3Â>Ïâ^_µÏUηê•뛟ʆŒ¿yê¨WüŸ?Xl¯W?:cêÎö˜T§œú;ok;ÙfÏ7wËtþò3Nû0ߪO®K­c :ãMµÞe>ZüÞÔðµßvÚ«ë¦>|Åá—¶ð­zãºâA§ ¿Î<õ²Õ°Ûëðf^µ©úƒ‰®>9¶{ò]מ_î'ë§Á³ê‹ë{º:õ—Å/Žèrò‘øð¿gL½ÍÕW)˜ç\{„í„ÇÁSõãJ™^^õ€Sÿ´ ¾Q{¦Õ=¸Ýi§Õù\×îý_/Ͻ¼ªú­ªžô~Ç9Pù7çü·UÌóíqû£A/n¸vÖ×a¥o×nk8—¾ª?©Z³õ¯Ùõt˜I·ÇS÷ÛãG-6ÎzˆuŒ«À±Ãá¿z¼yôT=¹úšâ¯=±àcg}E¯²å{ÄÚnLµËãðûQïõ-1Kܵ[­s9ûò T}¹vl°´<¶~†eùë*g~þÈ3Ïf¼âãÈùÚ§¿ùzrɈüªzrÝ*kÝ.>+'UÍGîyñíÏMqfTôé¿·¸:ã/|4"ôª^\¯ÏØù‘Öí[Ýæ#½k¼*"¶Øv|X1ËUù¸ÚÕz"tT=Ù÷ñ]Å›Ÿ¹Á‘_N´™l½kÐÜåÈýü·šîëû¦«M_Õ—Ÿ¨ýTsø’ã¯1Åvô9åÎK>Þöºm·‡_YóËǼ1Ëô9Á¶GŸzÅÿ¡¿9ó‘…ªÞüôƒS×>ûæ§N½¹në½ï?ø‘Ýî=zÙ}žß~ÇÞ¯Óú4Ë´œí÷îß÷iž³¹PÕ§›õ¼ËÎçmÒ`\b×§‡ß\29¤k¶“_Y¥Ü}¥Ûž]ºþØí×BU¯ö÷t¬øSëkæ°Þwxøø#¯þv0Ìuø¥ 寀«ãAk•ôª¾èžP5{W®9|ÌÚ02>P½Â?ýÇv»©ùº:ÊŸžóúÂýà©úñs¹Rö{søÉ¿ŠÅ™_ÚT³k÷ ®ÃþîžòŸaNÒûúí—õN?}üVðT=¹EvÇ"ÿ`¿T{óƒ]æÃ›–ýù¯ÉÞN>Uýpµ+{OÕ k¸·ývsø(éá͇g^~Í‚ç>w¶ª÷&sšŸµ=°ìŠ™‹Mw=_Õ‹[‘ƒ/œûÖ2Ðj»N¾(~noŠ=?¬Ï»ÇGmÖpèb§=­Wõã—ÈFKœùT˜µÀnÓè…{þ²ÃÉ—µ]àj½óžõ¼ ¾ª¿Ì~ü©ôº&ó)kû(Õ|hè©k¿»í€Ýž–cÆSû]­ã¬…+ðT9ÿòŽ+¥qög÷…í•Z;þÐuCÆcöcÓïßûËÔ+\»îþ¤åÝ”G½*ÿÛ–ËA€­Î>¨XÕòó¡q scïräPûQ®V9nw‡à«zpP¯ Ûr|ëc~”àè¡(+¸êÒl×a:íI?¾ÅÕ —æG¯_Õ‡ƒ¿êúvûÙæSû:¾8”k·óy4v¿éÈo #çºZ•]ƒ¯êÅíe‡Žüý™bó©þ±ÿ)ÁÃ.Ïã¿«:}6É©Çz½ÄÝ?¶¨üðK+sõªžÜ®ÖiýekºèiÛÿñ¾N”¬Œ±ç«º\Í\]ß[eY÷•œyï"UOî°ŽŸÆ›O©s|v{|ü‡¹IWN:æÔ;kz¾Óզ΃¯êÉ¿³´Í§£¬ óø®¶Û^óïvözþö¥)ƒ®6k]OzU?îTísÎBŸ·=®Î¸ ½œûPjŽ«ÕZ6ÎOÕ‡CSnzÇu×jóiëOšy|²×«§>p²>¯pµZÛ%{H¯ÊÿÐ[ÖFùtó˜g¯Î3'Mÿõ–öÏœ}su̵KV©=×.Rå~øªæöþäGl½>-Ë}ÛŽÛë¾Ç޾²Íî7}×Rœ9E—u=vìq‘ª} rÝ©GOËq£ÍŸ›Ç^¹b_yÄìyå¡ouÙä;ɾ?·ë¥·Ÿ7®Ü UŽXŸŞ•»]>öàã}?·ëå!µÎîÚU‘™ÿoÕo-Vå}äy9øF¾Þ–§Ìc×ÿºÃ¿|¥ëP·uÁÇÕb‘_AzU¾ý‹f¸ÚÛ*Íg^ûÖ+w™ÇZ^+ÙöÇ}¶ýéü»¶»×Ÿ«rî¿C6þïµËùµ®m›>vjÝë¹N~¯‘»·¸¶ºçG‹Uy÷[Ç ·šÏLiô¼9ò.[oÇ"/>qÅäwíþòÐUO1Ó­°Ïn•ÑÈfOè¨zЯûígô|Ø-Ïà_“½î¼"Ï9¿ §ÏV_bVévµY–Yo¼ :ª^Üå/‚œó}[çG–ÖæÛr >~äºÕɶ]²–çírÜ~óƒ²Ó=UîÒíâ3{7_sdá8G®#_TÚÔåÔ/:›\ßM®j?|UîÒëÏü2¨¦éÌ~sð†ŽçßiõpøË6šï•ß%ªüÒØ\ü ?çÜ¥u½qµÝN~{‹Ï_—Ïá_ÙèZÛ}‰«yÛe‹²¡£êÅQu®Ì=/4Ÿ±†ã¥¶Ý ¶]š¶zÁœr¶¾¯º¶¨uè¨zr·¾oòÌ_»£Q½Ýþ ®~Üã»í~êP`3‚‹ÌJ=/Ù2f93±è¨úr÷Zë@¢¯gå4HÀUv»;Xí·"py½/9•ßxíN»ݲvâuŸ\ú¦Ó?/Qõçî]O?f˜Ï[+0¶½ ZÕ'Ûu§µ]ÜêÚ¢ì½<,þ$Å \!ž@H$$DÇ)„TB!AÈ$d² 9Ê-I!Ÿ ²ÄA·¸O‡%„RuÇ_<щ3»J‚\b– ®riQ.¨É%$ëAs‚4—2“£rHP„É¡9ˆ!›ç²Ñ*›m²ñ"‹è²€*‹d²"“[™¨È`TÒùH$•Mw^NXAh ¬$¬òP‰¯!¬%¬óP}KÙŠË…&‚¸Q7,âgb a+¡™ F¶ÄçNB‹‡z Z^©mõPO ·Ä¿ˆ<]'ï0ï!tÄqŠ8|øáë„o¾I¸˜ð-BáÂ¥„Ë—¾M¸‚ðÂ^Âw ßóP@ø!áJ½„«?&\M¸†p-á:Âõ„}„?!ÜHø)ág„›7ö~N¸…p+á„_Ä_ãAÂí„;wúGý„»G wî!ÜK¸p?a€ð+¿ÄSá„ßÄ„‹0H8F8Nxˆð0Ⴌ*œ N#)è*eX5©ÈœŠŒ©ÄÓ —†îÒÈCñtÒ§O‡_:ü2ÐOøÀ3ÐW&é3‰gTMjü²H›5¨š×lâÙij¡•ƒ<9ÐÊ7‡x.´s‘=Ü\êLyè:Yó O^ò‘+¾H;¹&›@Ú`Ð-€Où.$^ˆ¾ Éw|Š [Ý"è#s1ñbʰX — CÉjÖK‘±ÜRd*ƒO2–/#^N¼¾åð-‡Oi+Ðgñ à•Ы„^%¼*)Ó*ôQEú*ð«à=ÞÉÓDxO> 9'ÁK|’L&ídÒN&â£Bº)П}Ë'´òÏ݇Jß)ý¦ô—çë'¥”JéîGö‰îþPúAéÿÜýžôyÒǹû·sû6éϤ/û²>ld_%ý”»’þidßôÿe¿äî¤:·:·ïÙï¸ûéoÜ}ô3ÒÇHÿ"}Ë—õ)îþä|ýˆô!Ò¦ž¯Ïp÷î¾Bú…‘ýôÒþ»Û}ië¥w·íÒ®»Ûriǥ쥦ÞèÅ Žzò·:öêWC.ïjyö!­òûóEf¿d8~ƒ´Á”·?tÆð;|Kú±´ü„;Žº5Ž¿Çõ«ªL{^(6JÚÐ3´›ØqßÂÀ ï!ðDðw$¿‘Ô•Hl' œ(ô…D#S42EC/bHƒ>bá ,X,rÅ‹~<òÇSWâ‘%XùL Ÿ À%K–D“!™ßdx¥@3š)ä'YR‘!•ïiऑ‡4þN'M:´Ò¡•AÚ ò• <zYðÉB¶l~sÐQ4rÀË/¼\øæbëyØzxùÈœ|hM>¼@»ï…ä¥ÙŠ S^é‹Ñ_1eX·RàeÐ/ƒ~ôËÁ­ •À*I_EYL$ýDô6 z“k2a zž"v2:‡÷xŒŽÃ¿Êãðh]FƒÚ3tµrõl hÎÔOOàžÀ=iç¼°/êŒpol׸7eä hùP}Щ¶ïKZ_Òú¢C_Ú?â~Ðö#ÏþàúÃËÚþ2.>†øhRÍA´€ í±Ês,: ?ôÀï d—L >ÚÁ¤ †V0u-Xâä#»7Z¡´1¡ØY(´C‘; X°°!ÕäŒÇÆÇ#÷xpǃN<ZáÀ#À7‚xñHâ‘è ’zIÙDE< ¹¢‘+D£“hhŇpc#X,°Ø~Քŋxt-ƨæ-ü$ «`‰À%¨&/ ù’7 y’០dâÉè&Rà“Ÿâ)à§¢ŸTdNEÆTàiÐK#ž†>Òˆ§“><¤Ã/ôÀ3ÀÏ }úÊ$ž‰®3៭,dËBî,ò”M<›x6ñlps(§hç@;Ú¹ÐÎEö\òœKÈCö|òÁ@Ú àN í`¤-@¦‚!ÕT’¶Pú6âEð)‚ni‹H[L™#s1õ¸X —@·K‘±ÜRpKáS¼ >eÈX†ŒåÄˉ—÷«f¿‚´ÈX!ý!é+WJ¿¼’<ˆÏUñwZ}ñû9ÞI?ýL¤Ì'/~ 'Á[üéM†ödò1™øÊK|¥M!ýiÜãð‘cpw9rìéñ¯ÇÞîþOú½‘}Þ—õsçömçöi#û2wß%ýÕ¹ýÔUå/’þGúž+´>Gö5îñ·»Ù·HŸ"}ÆÈ¾Bú‰sûˆÕ?ŒìÜí¿´ýçk÷¥—6þÜvý|mº´åî6ÔK_¾ùRý€û‘Öúæmú£êåioÁ l,xÔ±@¾ò-oA| ¢<ÆI¤-\¯ªbõ%„t!ÈŠ®Bá&m'¼Æ“6ütÁo$u5’r$/QüE‰ÚOÀV£©ŸÑÈ= ªs ücÀ‹n,°X`±Àâ5X°8èÄ“¯xì&^ À+ü'K–,X°$dJBŽè¥"|*¼Ó' þéÈ’^:xðÊ ™ä7Ü,hfÁ/ Y²ùÍEoyØ}iò‘%œ Й€|øVPM€O!< ‚SH¼=!O1ô‹)»â¥ÀJÁ/¿ 9ÊùVÎßåЪ¿’x%‹ã*àÑ­øÃóœíIè~rM&O“ùlʰ‡³g5:ÿêÃGÇà£cðl]ð1°Cƒºh7Û»ó¤îy÷R¦ëE]÷¢Ì½€{÷¦îy£coÚhù4Йm“/vãKZ_Òú÷×Z~Èì®?´ý)_ÿAeþcˆ¡<Ç K@ˆ¼ÂH@ú±ÐK]Ky%ˆÍH<xu5ÞAÈ2Žø8âã;˜´ÁÈLÚ`x…Ïjñô <x(¸¡È, 9ÃcØuŇ<û’Ö—´¾”¿ï°2q?xùA˶Ã^þÐö'î| ö=†øÊc y @<€ò ݱԱ±ð‹œð ,ZAÀƒàDYÑf#>Žø8èc×ÁÈ Ÿ`ø„@7„<†€‚Ì¡ÀC‡"s2‡ #m|Æ“v<ññÄÇC+ɇƒßäˆ@ŽdŽ }$íD$rF"g$´£ …>¢Tó>¢Ñ]4rÄð!ÜpcH ,X,°8`qÀâ€Å‘ŸxôÄ#C<7 À%K–^ò%!oòɨÉÄ“¡›Œ ɤOO ºIžŠ|©È—*}°4`iÈŸFùχO>i'vrM@7À  [Ýt^Hž I[ßBø‘¶} ª&»ýC·˜´%ÀJ SÝâ¥ÈX ßRé/áS†ŒeÀ˱Œx9éˉ—÷‚´¤­€Vð òT ¼’xå°êªø¯ŠôUª;˜Hú‰”ÅDà“SÞN’÷‹&;™´“ÑýdxOFny·e é§~ qåPõ•î>räé ¥<÷ìŽô[’q飤orŸÓ‘þDúé¤íw·ùîöýÜ33îö[Úni³¥]–öØÝKûënw¥½%_V;;rL.m©»u·™ÒVŽl')'»mt·îöndûænÏþ¤òa /˜'uØ“_/#óëî}(ƒ1ä/€4èr,õt,¿è=~ã(³ä %ßáÔ¥hÄPÞ±àÇõë¡Ã€:$RO’È[¸ÉðH‘9(iS‹”êe‘AÚLhf’6Kê;åœ-c=èçÂ;¼<ÛQÏó‘k6SÀßRo¡[Dšbêu1銑³„x ¿¥à”ʘôåÈVÉ÷JÒÈ›wI?QÆbð›„<Öû7gtù¯g¡ïÿôµäÿÆXÿÑñ•{lõ¯Öÿwãªÿª1Õ—ñTƒÖökPß âqqÀâ€ÅA'¼xt¾ã%ïò’È÷D¾'ò=ÞIà$!k²%Á;™tÉÄ“Uó–‚nRäoäNEÎTäJ%ž4dNTM_:°tè§Ã;XreËäïLþÎDÆLøeA?‹tYÈŸÍlâÙÈœ ^6üs “œAÕtæ‚›‹¬¹Ôå<ô˜‡ó¤AïùÄó¡•? šÖ ”Ýä-à{4 àQÏBòXH¼<£šEÐ,‚F1eWŒ>Љ#O °òUÍâ¥ÈW ÏÒÕD—!_ð²AÕ\—óžå踂´è®ù*€WÀ«z•Ю¤ìªÐ…¼a+ïÆVÁ{"¼å-Ryt"py÷rrNêWM¾¼O8]L–8²LþèO!Sˆ[ý†ü9FrŸv÷W#Ï\H$}Ž»q÷'Ò¸ûé7Üý…ôÒ?œ;:w,t¾1ÐùÆ?#Û±‘ã÷X]xQ^ÞèÆúPæ¾èÒ<ûA úƒ>°€³º+G·ãÀ 7„2 E·aüFúñbsà„‹­‘ßt–Î3 Ÿ ^¦Ô?êO6e•^.ùÍ'}¾Œ¿á[ÀßÀ‹êÕG1y-†o‰Œ£¡Y ÞTÊ´œ´åÈPÉ÷ÉħH»?ºn2º?üS_/ó´·&¼Mx‡ðÂ_ ïzXw=Þ'|@øð7ÂG„¿þáaݯòø˜ð áSÂg„>'|¡Ï}òŸÁXÞ`,o0–7ËŒå ?½'ÍXÞ`,o0–7Ë4mŸ¬÷h Æòcyƒ¶Ð -4ËQz½‡±¼A§iÐ6´cyƒ±¼‘¤çXtxí¥A{iЩŒç ÚM#Koh? :%ƒñ¼ÁxÞ Ó1Ï…úþ5m«ÁxÞ ]6°û7°£Rß-Ãþ ìßÀþ ìßÀþ ìß0õ™WìßÀþ ìßÀþ ìßÀþÙz?û7°û7°û7°c¡^ËÂþ ìßÀþ ìßÀþ ìßX¡ÇfØ¿ýØ¿ýØ¿ýØ¿uwû7°û7°û7°û·îÅaÿöo`ÿöo`ÿöo`ÿÖÝ ìßÀþ ìßÀþ ìßÀþ ìß:Ï‹ýØ¿ýØ¿ýØ¿ý˹1û7°û7°û7°û·Î"`ÿöo`ÿöo`ÿöo`ÿÖ>öo`ÿöo`ÿöo`ÿöo­óaÿöo`ÿöo`ÿöo`ÿÖ¸û7°û7°û7°û·îÕcÿöo`ÿöo`ÿöo`ÿÖAìßÀþ ìßÀþ ìßÀþ ì_Î2Ø¿ýØ¿ýØ¿ýØ¿œ³0°û7°û7°û7°Ù#3°û7°û7°û7°YÃ4°û7°û7°û7°Y?0°û7°û7°Cì_þIÿÒªÛÁ!eï–ÍUé²ïÕy>ïéýºz}.»_ñýõÙìz½‡×¯Çúþz¼ß ïKö8«]¥Ïkw«»9Òç[çCô_ƒ^WíWk«Öù‘d½ïרÏÔç=´o‚z}·²W)±|øë;–õz¾Ð«ýœÑ缓õYïf}Öä :(ã ëÜw•>ûÝ«×fOë{˜EúüI«>ƒ2 ÷ýõY”Z½¿¸ÄÝÌZ½ÏدÆ.Ö^c­¾£9 ïeë1MZ¿±Ï§TëyG¿òy`S)Òó5î±ö!=ô<¤JÏEºõÚî€ö…à¯ïr6èûœûÔ=%kÒC­Yw;›õyÉ~ufRÆPÖ¹Éj}vr¯ÚÇ´æ.§õZ°¿:ïbÍA’õ¹•z}W©GùB°Öt‡õ¥¿:?noÐsZ}_©WŸYRçV¬õÛÓz 7Yû;hV>¬±]‘¾ŸÙ­Î†[gRzÕ½%Ù¯”1Ÿu>¥JŸQéÖçTÔyJËïA´¾·Ù ýìÓó“ÓzŽ­ç) z®²_ÍWä,¹µÞ­×|ëõ¹ò^}¶|XŸ/Ñw£ô9ó}ú¬Ëi}Þ%Zß™jPcQëîT¿º?eí—†è=Óz½oÚ«Ï£ëóž!ÚÇB­žõê¹Ð°žùkŸ Õú¼L£¾;Ú«×–ÔùPkÙCŸc/Rã`ëŽVöÏ0 î—Zû²Ñú L­ö¡Ð¬ý(Ts묨¿¾§¢})ÔêùΠ>³_Ïu†õ:±‡^+.Rç`dÎ#ãmyïy*ú™Š~¦"·¼+oÚNƒŽ¼)+Ãi³W^¢`XMå½LyóRÞ¯”7(«¡=Üé𞎌ò޼A7ï3(‹àÍ$O5ä©Þ5ä»=×@³†<Ï"Ýlä˜Mä­y¯d6tf“×Z`s€Í6‡ü͇øË¯#]z¨>—üÏ…î\èÎ.~ÇçI€¾ø‚ÎóÈï‡å0Yû7jÖ>ŽÕ=¹?híÛ…¨¡•åó/[ŸËmÐw¦÷*ÿErÖBîK[ç™BÔ@ë®F£ö÷W¯ïIïÓ瘆ÕY]Ùs³îE{è»Ñõú|n¯öëW¥ýtô¨;ÖùÜ}ê>´œY²üûEkßÚGÑ~å§(ëŒöU”¬|zXg:zôyÞA}gÃCßÛ¨Öw7zôý!½O—¬ö°Ä' øÊ³îsìSw:Ä? uG1Zß­nÔ÷;ö«3P–½÷ýéµúLÔ>µ·'÷®­³QÑú|Tƒ>#µOßq<­ï‚Dk¿Hõj߯º2 î†X~Ñw5<ªÉl5éf¡ÛY§Õ°²Ž²¯£Õ‘¾Žtó ?sŸ:c5½^ÝͰΠj%àlìW牭=½zåóHÞô³ÎC{æ~}÷ÙfÔ¾H7³_ù)™9 Ïó;sPù,™9¤| Íôf"ß4ò2ºSá3½LC¶iÈ2 š3ÁŸÎ4~MðÍ}òÂ3ùC‡ÕÔÓjÒU“®š4ÕÈ2üéЛíé|ŸÎ÷é|Ÿ±_ ¹kÐO=85ÈPƒžkàUÍä›EºÙèf6ù˜ ýÙП Ùä§Ø`s€Í!sàQ‡žëHWG^ê€Ï¥|æBw.tçŸ ŸyȰÚócéä ¢ù!jh¿€ô I·š  ¹~òмo²Ü…àÊ[ Á©'ïò~¼A ~ç¡­Äó}1ø‹Á_L^–ñ7½øRø,Eƥȶ ^ËÈÇ2Êj9:ØŽž–Ão9|VÀs8 àtE«éDx Ðlw%?3¬ÎŒY>ŠšGœw Qþ7ÅצuÿmHù’&Yš]¹'þ}ÄïøÞ_@r÷Xî*'Q¾‹ä,qjòc!÷ ÄϦuÎÌ_ùáß@rŸØòA·O½—{Öòc¶öKƒg ë t<ƒ¿/où3eÌÝ ßìV¾(å¼Ôð¹¾¼©|:_pVÝ»ŽŒÓÁ›­ºVuÎj:<¦Qf³d<˜­|`Ï‚ÏLà3Óà7 šÓ«”»Y”A-ºšAžæœUgùëø{žüÊxyk‡•/ztYÇï"~åͽù2¶„†¼[yЯ'¬¶ ¾óÁ[ƒìKH¿„ïòØBþ^C>äm§¥à­çZò´:K¡±Üª+j .ow¬%Ëeµ¤ƒæZòµ¼2>@G›øÞí6h·!ï&àm൳‰¼·AgôÛ(¿6ð7‘·6pד—vp7Óí|oG/íàì@—ïBÒmE_íÐo';¡ÙíMðj‡f;´Û‘·|¶Sv[Ài—qòuP>è¡;U—¸ø:Òv@«ÐÛƒ>ö€»¼èu¶Y÷œUÝe°Uȸ¼õðZËßKÉÇzÒlBÎ-|Û*òBg;47¿•°ÞëÁÙ!r‘v§¤o+ñíäi‡èœ2ö!í&`[H¿Sà{Uw¾ ]tÈw’~:ÚÉo:Þ¾Ou÷-ä¯CÆ’ènzÚEÞºd¼×¨–Ž»·%Y Zà³ ýu‚ÓBšN¾w!K§|§<ºdŒÝNøî$Mùè‚2u½§–6ºeü^78ݤë仜ѵþî;Œî;Œî;Œî;Œî;Œî;Œî;ü§¹³ö¤”>¢G·e§•ÍZvS«Ëo¿–óŒö…š­ï)÷j¨ïé·}ª´Ïë^å“Ézã'Z¿QЬý£ê»ËÙú½Ÿf}ξ_¿Yà¡ß-¨Õ>S{µÏ¦3úN‘¾‡Óª}n h¿!Úv£ö¥º_Ýs¶ü©FkŸªú ýÊ·ªõÆAˆ>Ë_¯ß:اÞ;°|uøk_OµÊg‡åsuPßm Ñ~WµïÕýÊÿªuç1ZûôhÐ÷zúµ¨dí·{¯òh½‹P4ÂÔÐÿ­Ú'ÔÀˆ{Óõú¡3ʧ’uºZ¿7Ô«ßKxSûÿ(Ò>@šµ¿¨ƒÊg”å·5ZûjÖþ[*Ÿ„ò–ŒåǵHû‘êV¾Bä]ë¾µ‡öR¯|‡ˆO'ñÃQ ì‚ýê½ëŽr­º#ïúˆ/ySH|È»r¯ZÞ²|F+ŸQ–OfýNBƒzÏFüDÉÐ)y¿ò.w©å}!Ë/”¿ö Õ ïOïÓo"Tk¬{µý®Ð ¾+ê¡ïM×ê»Ó{ÕýQñ#nÝ¡ö×wjõ} ½ú>õö)â¯ýŠÔê»Õ{µ‘aíCÊ_û‘ªÖo*ôè·‰õÛ ú¢jýNÑ^íßuHûxõ×oÕª{«ù=ÚçÔ ö§è¡}¿Vkߊ=ú-†Aýƒ‡ò;³WßÙîV>L õû Ñêñ9bÝEêQ÷‘äM¹K-~źø­²îÉúkÿŒÕú=¤V}×û º·dù“õWo;Xw³”/tËŸÉ>u/[Þ_°üš„hÿŒÑÚGzƒ~ÇhXßÏ>¨Þ_Ö=£íÿõŒºÿ[ÞT¾ME?SÑÏT䟊üÓge0 :ÓÀ3Ic’gZÕÔ¿jxT“¦š4Õ¤©†ÖtôÒÀßÓù6ßèr²®€f ù©o ßj ß€<5Ы!¿³ÀÝÄ÷õäm=ßf#ÓlèÏ&>üZàsø6‡üÍ!sàS‡®ë£:›(ï:¡}=úXKšЪ¶ï ¡·¿×’ÿ¥Ä×#ÿBùN9Ô“ïzpê‰/âw‘ü¢ð] l1t#Óbò´žK€/¾y—"ãRäZF9.C–e§Õôa9º^¯åð\G9¬oxëෂЀ®Àm€îJx¬„ÇJô³’|®D¶UðZ…,«‘{´Vƒ¿š¼­>£¦!kø¶–4k%¯”ÃZdXÍõÙjZÒ(¿è®zÐk<­¦*øÞ¿&ø5Á» MÐh‚F“è—°™¼lF›)ÓÍ¢[è6ƒ× ½fäiF–fòÒŒ¶¡ƒm¤ÝŽlÛÉ×Òî íò¾“üí§:-ÈÑŸø´ÀãBx\Hš]Äwo%ÞJ¼•:ÑJY¶B¯¼Nøw¢×Nò½^Ý|ëæÛhÉØÒú7ê×xÔ¯ñ—ù5vÏ·d®%ó,÷KæV2¯bNeÏ£d%ó&™3¹çI‘z<'s ™ÿü«=÷¼Gæ;ÌsÎãœo~ãžÛÈ\Æ=9wãž¿ÈÜÅ=_9w®rîå_ÍMþÕ¼äÜ9‰ÌGd.rîä«>ÿø ì5XmŒä5[•“åµZû)òP÷êå.·øï°ÞBèvÞ$³Þ¹:¨üY÷¸‡•?ßýê=ñO-~õÅǨܭ_ âSGÞ%“÷%eùOš7y“Rîw[~ª³Õ{•ò>LjµòÉ/wìÅOœ¼é)~Xå­,ñÓoùbÝ7â~}•öÛ½_ù,ŠÔþ»[G¼ñ­Þ·‘·l,ÿEÃÊ·¶¼U&~³Å‡‘øÍ–·Â’•mñ$¾ðÄÜQŸ­â?DÞ¥²îà(ßø™ÉÊ÷›øéŸ¥r/[|ÚH“<£Jù?•÷¼f.Po ˆ?Oñ©/2† Q>â,ûèb–¿Z2ÿܳ¢Õ›¨Åc&4f6*ÿ׳(—i„Y|›EºjhÌBgÓ$N>g‘—YÈ7=ÌGþEȲù擇E[nßg!÷"pAwxõüÎä·–|,w4çËøƒokø]%þ«ÐË*ô0ŸtK S ýµÈº¾«ø{)y]M>–ÁoúÜHºà¬NV]Æ:d«ƒ÷&pv€Ó¯äs“ôÑüÝÎ*dl€_iÛH»ºmðlƒÖ|ô×vVu#íÐhGoí‚‹,[¹Zíä«Ýl  Ú‘£>íä£zíÐk‡v;yiG®í’ö¬ê’v’¶Ù;uùè€o:Û Þ.ô¼K`ÈÖξíþèí^ü÷ÈÜøu_‹,ø¶‰t]àn ëI¿ØV/¡ó¥äi½¤ß`[ùÝ@·£‹MÐÝJXëÁݼ[H»SÒ·Uä'Ï;dÌÎNáOÚMÀ¶~§ÀÉÓNxï¹å;é¶’~yØÉoºÛ޼-¤iAŸÀ»Èó.ô·‹»Ði'8-¤éä{²tÊwʰ ¾]Ðí”ñiºÈGü»© ¾ÝÐè–qxÝàt“®ó¬n§äßè~ÂWo?at/at/ûÝKðÝKøÿû^‚{?º‡ ö¤O£ì­öu@µ–-Š=Ôër9¨åxS½ÍcûæÚ§Þs°ÞTKÖþN›µ®!ýÆq²~?³U½ã#¾y,]Eú½c¾ûô*¿ÜÖ{kþú͵zýÖÏ>å§Ûz9D¿ýРß`Û§ÞÞ´}¤ÂÇ¿Y¿tPù÷²ÞJVCëäƒêmëN𪴟ïåC5`PùBLèÖoxÖkŸªÕûÉ–°hý¦P³~Wè z[H|"Z~Áªô[n½Ú×ê{ú-‰FílH¿ÿY¥ý„ïÓ>Wýõ›ÄCGø kÐo,ê÷ÝB´ßðV탕ïaï©÷l_¬­Úx¿ö%î¡ýÞªÞž jùð²üŠEk?Gê­QËWëö1æ¯}¶6h¿­û´ïÖaý>…‡ò¡*ï Yï-i_¬ú=£}êQysÙzoÂC¿1Z­ý°¶ê7ÞµփʇXò=$Ö~ÅC´oñFí?l¿~Ë­V¿sÔ«}®îUo*Ë{–ß0í;¬^ûëÕ>Ƶ1èféw&ZÕ»Hâ—RÞ]¶|²†h¿¬õÊW¥õó€z³ÔòS¢}•×ê÷K÷êw™‡ô[¦þú}æZýFs¯~siX¿»¢ßk®×>ÉöjÿæCúÝ ývE­~¿b¯zU|ž[ïÈùkSÕÊWfAöû:¤}¿zh?fUú 'ʱ°U¿5GÙö«7ç Ïhg!úý‹Zýt·öyFº¢aýFˆzkÕòµÙ¨ß‹ëÖo; êwãà;1Z¿Y‘¬ßkÔo8£»¥ƒÊ'§¼¹*oWL>«ß¯ÈÖoX¼©†ÈSù6ýLE?S‘{*rOCži”Á4èLÏ$IžMhU“¿j™§’¦š4Õ§õнÌDöé|ŸÎ÷é|Ÿî ðf K yªw ¼kÐs |j€Õ «!ølò0ÎFŽÙПM^gªaù`sÈßò7>u¤«CŽº3j¸>ø\èÎ>ܹÔyП‡,óÐëÖ@w t×[ líi5õXÍõÈÓ¼x#8Ði„N#òlàû¾w¦ ^Mðl‚F4šN«©ŠLS6“‡Íèw32· Ïfän¯zÍðoFþfdo†Î6xn#ívêÊvô·ƒüì íò¼“|í§Eè G |ZN«©Î…ð¸4»N«)O+ñVâ­”g+u¡z:á݉>;)ÏÝðêæ[7ßÖ‡¨1õoä\÷|óÜ/»Ë0úËè{,_•÷X¨ßÖ¼Pæ„#çƒîùŸÌýΙïÙó<÷únÏëÎ7§û²ùÜȹÜÈyÜùæp#÷DdÎF½ÿŸæjçÎÏdN6r>&s1êôÿ4û²ù×ùæ]_6ç:ß\KæYÿ‘=‘¯ÚÜjôœÖÿê­H·Oè(NþYŠþ{çÖÅÑ-|VEÁŠŠŠ¢¨`Ç Ö¿ŠŠ"H‘Žˆ Òb 隦iKcL¢¨[‚iK:ILBLTŒ5]SŒé¦¿Ù™Ýý_¿ÜïÞ÷yÞû~¹÷êó¬üwçÌisÎÌ™ÙÙ9ªíWʼ:f~jÚ°ÁVy>»ÈOÐ9EåÇÛªÎj÷P¹P¹÷ô‘çôö@¯ž2›yîqˆÊ“·Uæmâ's#›ç×Ê|oM.Ê|©"?¨Èÿ#Îô¹«½ee3‡AŠÌ+Ñ_ê ¾¦2¿„8¿WäU펞Ñ*§54›ÅÉ\£ÍVʼÖÍ.Ês’E> qžoï:•s<-øÝÒK“ í–[ÕYÉðÚŠg­ × yZѯøø«3“KUŠz•ç:TžGoæg­U9ZU>ŠRy޲È5Ðæ¢<;wp#yæ(sRŒª’9÷Äù¸¾UnŠ™ÿZä&5ó_‡ª3•7ÉÜj#á'RÄ–Ô/âRàÆó<’öŒ¬•ù}Æò;xSyž ÎÉÀeqe“žtÊÓ¹Ÿ¾iðE½Lò<èçñ{Ï£ %âMdZ„¾–¡óà+æù2tºÙf¢‡eà*F‡ÑÈ´Œòeà™Åý2àfól6úž#âyø™S/C©¹ðY‚ý̃‡yМyÈóôÿ1ð ¿±àŒ¥n,ucá'ºqè=ÚñàGñÀÅ‹¹´æŸ@y<&@7yÀŸþDð/f0 àoÐ'<$7¼)”§@?|)àK¡-R¨—ÊóTÓCo!¸‚c!8‚c!8Ò¹2À“Aûf § xÏ>›zÙàˆ—lxÉFŽìzæ ã\xËE·¹À.‚þ"ÏC#‹E\žÅð±:‹¡‘|h,á~ ÷Üp_líP¾åÐ_N½åèuy­ !KyVʳi"æ±¼øêö;BŽ¥æ¿¿û{­ÿmï´þßÈ”{\~§õ¿åÖ¿ò}þþßö]Ö¥s¨ö{,÷wX×÷WÿÁüÊÌ…†Ž; «Ñ*ïsÊél‡ •ÿë€uÀ:œUyÓ°?Úß/På‡:óËVù¢×ÈÆ~ô ~U2´È â'ê_”9@ÅPbæaGðtOG—Ê' žŽÔï¸RåGGøèžŽu2ÿ´™£ ÀÑÉ_壯&:!K'déT ó·õeÈÒ <ÝÀÛ ™:UË¥"¿T'xêtQæLèÅ}g/•»ÔOæ$érAsf.kpu…NWèt+U9KÁÝ œÝjdž¨î¥*g)²ô€—)*‡u©Ìó&òT‹|%½Å<ÈO嬾w„ÌùÐùú@·Ï9tI‘9 Ã 1†PwH…Ì)jæÿ¨•¹¡E~ª!UŽhø ŸC#T¾èh™$ Ø¡ð1Cר<Ò›dN+‘;d¨(¯—¹¥Íükð8ÌOå+ QyEÀ7 |“Vågß° •‡ºJæ®aÀ¸àgØY•¿ |‘ð¤ƒg¸¿ÊW-~Ç©¼Õ¥*O ¸†ƒkxµÌ‹*ò½‰|["Éð 2,0ó[ƒc|G€Êw .ÑМÀ³Ùà™žHxž _SÀ=b«ÊW+óbOA# dnl3¿j´Êy³Qå*'6úÍï"—ʉ]!sÇñ—¹±§€o4ÇÈœØaõ*w´Â ßaè`6Ïgð;ÞÂÑ­>zIÈ  ÞуŽHèGò;†úÉ”EòlüFÂC$ô§ K¼æÃk>ºÌ‡ß|l%ç#w!ü‚»9 -„~W±¸¿à+_1øÊ¸/ã¾Lħ"îÿÄÜšS¸Ÿ…dåö¹ôŒ#ç‹Ø^Äô"–·Î3ñ·ˆ»EÌ,be+F¶âáKϲâ]ëŠWı"~µâV¯ZqªˆO‘ÿ_–ÿðÒ˜NÄsîqœˆáÜ÷$‰ØíÒ¸MÄhî1™ˆÅ¬5èÎnq—cYñ”{üä/Y±‘ YqãXñŒµ.¼HÅ%"a08Dæ±î°R垊“ù¿ccƒiŸ.5*zíσ±³ÁâY­Ì{-òw >£ò'bk£°ÛQØ[˜»ÀFû ÁNè§S6ÔKæÛŒô’y#‘{|­Ì:<“Åx‚L¾ §sãù;Þ¦g$Ï]ÀÏÀÞ]àš!úbà'€k¼Ds͆Öl`£Á†/̶HôAà™‡¬.hÏý,´‹ø]Ä_²q%SžlþŽ êNCÖ èd!{†ø Î,hçP7¿Iüͯ|)>g 8áËÈ_$.ð![¸ ¨›n‹ÀS*žÃ{)Ï3ÁSʳ$ð”Âw‚ñ·<ù<Ë„§R~!C)WÁY9M,W²çTKʇïbè_Œ,Å´k™{]2oªˆÍÿ¿ÖóržJËy*E}éZÁÿ¦u‚ÿ.û^ÿ+ö¼þoÞïúw]+¸[åùDµ >Öž;€»Ã&™Ë[ä;ïP%ó€vÀG:`ç.¨œ Ø†õ{U©\ëèÓø!›_¹Ê½NŸá‡ßûÛô£¾ß™{¹£—Ì%ÚCüOGptŒVyÚÁÑ\…ÌÛÞ«Fæ~øcdz*4õ;ÁK'l­S¨ÊGŠ<¥zìT.ó“vK‘ù@;Áo§™û½ütº ‡¬nÐì%âð Á'zÀSî»@£+Ϻú©¦Ðè*tS®rÁ£«nàìoÝjeÎw‘óGœÌÓØ>º;*DOf®wþö†Fo™÷]äl WoêõA¶>ÐísQæ.’­ò¨ƒ# :C6©œêÈ0¤NæV7ó„‚s¨ŸÊ*b1Ê#}TžHdJýHèER>ãëdþÈ¡â/8†^PùE©3LÀc²èÀC–aèoXÊ= ¯Ãà#ÀOæ¯:r ×0p » r“‚k80Ã…Œ~2W©‚‡ƒkxʵ ®áàŽLÃágšˆóBd>ËáàFø©Ü¦ÔtÉœñf~xðŒ@¾àŽF”«œñàÝŒïˆ*• ÞF ÷ðNßÈr™Ó{õgÓn£Jeþø)Øš+NæD ®)U2ïw<0ce.ù±ÐK½±Ô ž±kdNp‘w3ì¬ ?fC/Þ/‚¿$dš€^c€+¢žŽ"ðëu2ÄH6’ç1"þäY$e‘2lOÙxt9^ÄÍð0AàºDÑ÷,ÊgQ–ÅóÙÜç@/ ºñbŽ o.q!C zpŠˆIi£Ùð|>òNá~*2MãÙ4ô½Ù<‹‚ç(pEAk:¿ÑûtèL‡þtñ;E†J3ŸIùLÊfR6“²™àMs`x‹G4Ï£¹Ÿ%bÛzRÍ¡ÞêÍ¡ÞÊçPo8ç7—úsái.z™Çý<îcà=žcà->c‘=ü±àŒ…F²ÇQ'ýÅ!w:ŒCçñBÖ 2T›ÍùМ>Ò¨“|>4ÅE½Dê$¢¯Dt•$ÚŽ6Nƒ·$ð%+™gÉÀ&›ÊóTxN…çTð¤R–¯i<+€¿4xNãy8r=pC3øLçƒ'KÌ%+G´ˆïá-¹r‘+¹rÏç"ê,ÿ"p-Ï"øÌ¼WÈP1žó±Ó2xÊG¿ùÈ\ˆ¬…à+D¿…÷·Î?¶â{÷3Dü.bt+·âow[ñ¶ˆ³­øZÄÕ¢ñ/ç_:Çÿ«¹½ˆ Ýc@÷˜Ï}Néüý»ÿ#ós÷yyŒŠò<þ휽 ¦­z!OÚ¨—˜S£w?Ú}0vÔ¥^å÷Åæz@w0Ï»a;½Ä˜‚ ôª“ù¡{‘S¡!À ¡]‡Ð®£("æ·üž ÜdÚ}2²¹°¡)´ë4hN¦ž ¸Øà `\ÀÂfc;3Dö5žŠÀáây‘ðW1oåw¯kŽ~xÄO½sÐG÷½¸åGÙßèÏŦç~ôð¶ðåý¥˜ŒíG÷4ìòù·zàg±wÝÑü#ûîåÏä^ý‹²pú¡o¶ýM’Îl=ëûãgôÃ1Ÿ×¦Ä\§?7òYÏ]y¹áŸµ¾çÊ_6Û«’&âàk$|êìþKô9úáeã¾z:þ”þ\»ãCï;©ÛWµ˜Ÿ°¶Ÿ‘uÝ»7û(ðg$|yw!Þ&þ£¯üzH?¼f\ý [æéÏ~ýêÃþ]-þíÅ{Ÿ¾þíFæPWÐÙg:Dœ›'娷ùxêÓž¿êî;8êÏ#­ôÃ;ΟùîpýÙ¿~¼¬÷.cûÒ—;^Ñ{•Þk[ÿyCRŒ‡çEÅë½%^ðøK<û‡{Üuð'ýðk]}?zæ]½‘Ò㳫›—œ¼98ü‹ñ³3únö4¶¥‡Eü¨÷”üQɼGáŠø ðÁrðEH|hóçã½,8ýðÇ}ÿtðÏvû<}_­WäùðO¹çàvÝØžíznÿžÃz¦m¯ }y¼‘Q¼êu¿g3Á—"ñÕµè}-9ÿ¼.è¹×wèÏzÏÿöž“K=-8˜öM¤¿gdô|ô»‚_°Ú|Ê~.ÔO¥Þć_t?P?ÒY4d¤­ÇgžzbÊ»+·Ûó””}j¤gœ½«Ïà6Ô—v±ßwÌO}ÛæêGFd´?Þ¢^&ãçø.ïÔÛoÝQØ|ÏZ=è¦üñ•eÏió&ϼå9=ÀÖ´SÝ?>gñ¥™wÓ„Ùz¶ÞIñóLË·Ýpf£±ýÆ­>.»ËHÖèó©·µ£¾´›ý±Ï\S²+ÝÖÇ‘¢™#‹ªÛêO?UñÕæà\cûÆ'n¸õ‹#©Ï ß¿òéûçb¤½ì_2wÚî]£­ö׬‰}§t`¨mO/>ñÇ›ï„-ýÊØ¾!óç¡ úX~a,èwbibØy}¨–~CËÀ+ígчòЧYí§yôõ†/U=hûçÓA%Wµê~2qp|xò“Æö‡¾ß}¾÷p=XÑIi™ûÛ[]âôÁ»]sæVðJ;ÚŸ¶2ãž]3õî Ï‘çFžºûÇõ§¾ÏÝtóëéÆöµo¼Ó{ª>àðÏC«¦©Ç__{çc ô!RðHûÙ?héûm‚wê]$œ~äXn®Ög½þÔ+í„…„Ÿyå§Mû¿ºÝ²Cc¡ç3ÚÃùÁÔWörìlø¸ùçíö9òK°çc^çõ¦êþ© ­Ú¼p…±½ë]OþáµËöK.»_‰QýÊ 6/½)Øæç¨év úS‹MÇ7*¿i0bLŸ#ñºöpøõTÿ2ffjV¯>úшsçyØú}jLö­ïß3¨<}±çÓ_n×Cõ;?ôMŒ‘ íÐÒxT¿ã1çÃ#½î°íýhæŠäÎ_OÕŸj8p÷¹oK-¿ÔCUÿ¨øpúXiO{¿lðíÏ7GØí~ô–ªÚ[&eëåÀ+£?œÛuµíŸ•ŸÞx¼xï36 ®2\×¼q—Õ?WõG¿n½.w›~t›Gøçþô7/¹u_ÝïV~½§òÃûF ~½{Õ~/ê©~§GÍ„Àé¿Ûör´¶G]²~`ÊÙk“F:ýXƒæSžJn$=?ü·13©¯ú™^­Þ.?1T?úå€Ó×5Ñ´^1û¦ýwÛ»}8>fí§úh)—‘üíÇwÜRxÊMÊNZ˜¦×›]ŽÝ>Õg~ôT_úãqعö1Ë.¨'íaï·k¶ï›1K¯—z±õW]}îéÂ/¿²üfîƒ÷`:]#YáWò‚OÚÉÞúøÚ׆´´Û·>çè°AǟЫ+šõö/8þ“5~Mí5ªæ‹óºKµGríw½J··púóXi/{Ÿ]á?ø-产 ýËs;r]ýö¹'îÆvÓüÓì~/åLO߀?–Fœ‹SvòðFì<ÛÞëŸy.õí?ªôêE®éÅÛãÂõ_Vèóñ\c»‡(øÓ—ïÌÛ?\´‹½«7´m¿`´^ÿA×g¶åtÒ«çÿøÊ†ùév¿Ry2¾yÓºpcaÅÊ‹©¶ ž´‹½+„ _ÚvqL»£¨,ð½zšÙ1•Œî×~yØH}å¼^üžzÒöªþÛ²ïc=b³7>t›Ý_VÊ>~•QùÚ®Þ_ÑÌMni{û7N{uæ~‡®^W—u›‡^Ý3Ûû½{ýì~Çâ;å~Itö¦¾² Ól½K¼²¾bÏçŽþfçFmùÞ¨ü|ùUåIÉFªë÷ß]r5õ¥ì©û$%rÕHÛŽ1h4uDòø÷7ÿv wxEgZìígSFªÕÇÉößsï¸.ÆÝ¨»{vÜ¢?¹c_à~/„¿¦Ú­²¾Ó©­÷¥YýoĹxÙî{æ¦T¦¿pÑî7í_öùÞ¬‡ô'‹OYæ³Î¨üèÓ—^¾o†±Ðê¯ãe;ïÝëWŸèÇÞýjöÕ³ÛÙþð䨂×Ö>™åðýG×ǪöÞe,ÜyíÎÏWS_¶÷¸½áô[v;ûê—ñ$ÞeëíIÍç¡‘ßÝþž7´¨>šäo¤ÞüYM~Çà‘í¿‡Q슓¶þÞ÷¼Ë'41[⵫nû®âœ3žùͽeÃÛ‡m»]ðÎænÇ´ƒÝ?]|ýÚŠom=¼ßÙk}ƒ¢>úëê¯jØ'~Ñ=õÛ£íø%þ#a«À#ía÷Ï:ß”?Áá'D›ykÜúË?µÇ¯½O]sæ–¯÷l·â#Á$7 <Ò.vûÔýÝSüô÷]í/4¾ý€þƒIë¦míÒOþ)^¶ÿîªVmê;ÛŽßÞO®ß¹ýÁý‰Qƒºý4¶¹ÃiÃI'¤;v0_ÚÁî> ÿ³c´þþ•?ذæ ý‰®¯…o>ô‡Í¯ï¬~ˆzÒvßrÓÑî#ô÷ïµÏ‹§Îêûo´ù·¯+¿YÓðüfïíFÊ!ÛÚÜD=Ùþ»'?î{Å.»½ß7v'Ćo·ãÚýï$7xéH’3.% ׯüêN«_lÿÝ o|åª?†ÚõÞÑuì\}ÿúG½Ûn¹h'úðM+†÷¿!ÔîU?ÙþUO½êóF¿dýý?¾Nªm¾Ü¶Ëý j5¯§8ò”=½³¦¢™>,úë§o³ìȲðI;¨JŸØëÇkfèÇýú¯ y®¥¾¿{—_LYhTr×â«~Ö¸¼lïª/Žìq8L?>d¤¡m½ìûôÛ³5®ùŽöøµCc¾ðæ¤åÔ—í¿ëŠW<3ÞöÃã“Ïxký}ßS÷<ýñä0§e?hë#ñÏ/šL-éq.AÚÁãÝõÁ×Ëž×'¿¸ïÇöKm{Ú·þÄêê%.G_¼äUÿåZ§=¤=<ÓÈ©šÍÿñâ“Ìx¾×÷Ý?õû õÆöæ+·\ýöçN?œ íaçK7íø†¯­÷ã7ß}ò¾ƒìû}Wú7o¥ýþ­å?­Å@ð’Õo©Ïýø{Og‘ íc§ß¡¤“žýl<~ï{gõxÖ‘ëŠß·¹õ{;îPq‹=ÏQý–c/ Ò^vˆYLB~|û41ƒÓ÷]5`SH'þ9ðCüÞº«Œ´w;‹zÒ.¶ïòé•¶'ÖîgŽ¿f6”Íß¾k×Ü×å¢Qy[;fnMŒô€? q1êK;©|±g¿î{qäùD x›=]ûqá ˆÃÏYýþ¢èªoWìµû™…_.zëÆ¡»ùN‚´Ÿm'Ÿÿlí•»l¾N0[˜zú¢›ž9»ëZG>§^x1ªÓ-¯DœK”ö³­×æ%ëÙüèñùð»“_räËùì×€+ó{¾¶â†g\O”v´Ut×ï¶=Ÿ Ê\_9Æá'ZtŒw8rªñÏn·ë͉º%7x¥=3¾Á¾VYú‰Ä¶¿·h\fÛû‚ƒ+–ý¹Ãñ“õåS¹í~þÿš'&Jûz4ÓügóuBh­ç3v<±Ï{sÄ`W‰í÷iP-­ñ¤¾´£Gn;Ôô—àÆvû¸íÙ6í©ïýèû›ìêaT2  ;k]€zÒŽ¶ÔЉmK‡îÆòwÜä©ï}á>cË+Ýl¿ÈÜÝeq7ŸÛ¬ùõ¥miXr~F_ýÄ6½_Ó§'ÚzØûØ}«ºõ˜ãøùƒÏ×îE=i'æð³è¢~bŸàé{ï9QºÖר¬ýÎí}Î;|.ö°9#÷Ù~}ÖÆâù¹¯Ýxa‰-ï^Ç«½œvüÀìx×Ò»ï.öñPž¹£ŸxgÔø;^¶íkïÊ×ïj;ëÒñÚÒ»3Y íaÓR¯¿¹·ê'>Ù×;yþ1}ïÍϾݰɷÎ8­âåÌãfB=ÙîjÕ™Þêø‹Ö…Ù÷{Ëøú¾ƒ±}¹ï–C{jŒœ·š$/9äO}Ùî÷·zduaí¶^N¶ ÕZß›óÛw'”fTÞmN\Œ\é‡Ô“í¿nÁŒÕUÍÚð'‰ ÒVfÚqô^¯?vŒ;w‹ãï“î+f¶?äli÷}R³nú”öq߯9S[F:rÍù›Îž—®[±äÃÏÿuöfòz#§µ;íœ$ígí‹ÅÍ~8ºR?¹(cÛÖFkmþö\ùÉU÷?íøë|Áày;>βô”$íåÞ¤o~?õìýäŠoÎ~êÕDß3èãŽw”•«Š‡6n½‘qëu×<´émà¥]Ü]óüo«‡„ÚíyR´Ö´e¶»?­ž~àUÛîÓ?zæÝù7®¤¾´»„YݲѶ˓›q[/»Õ×Oyá£Òzs{ïX#Ó–[ÚÇš‰%ÇæFÙþ}ò@è†ÙWÛüì^ò[ë?ÎsüT®—9‘{Ÿ¨´Û.„\ÓëàŽo Ž~ÿ£Ï>"ëϦ7ðrÚC­›¨rc‘œÏ€OÚͪ1£³w¯ÔOŠå¤©glùvw¼å‹ò/ ¿`­ÓÈù»íÇyf/îØo’´—E·}Ãýäžu¿Äý¤W}7±ñúéß;vwxÅ«?]á¬gæ}þí¬çïÞq.YÚGù£ÅþçoomËsÊëñ_û?ÞÏ–·ê]kü«ív^,×Ǩ/í¢Xê]?ÕíÍ©µl`ËSõPß÷ì½Ö¨œüåV|käíðp®ŒzÒ>Ò‚æ}Ýýä—ú)3\séUËnûÒGFe§æß”7½Ò¶ÃE“oèÕêíkuˆdÓ>ôé*n´ø<5çŽï¦ç¶×«ô.E×Þ¶Ài‹ßë|+§ÿH6íC/0—'ë§ î85wßíz•ÇøÎ§®«7*{¯Ššpå`Ç’M;ÐW|üæò»Ê|õSªÙ•çÙ¬~E¬½ŽX©ø°ÚKÑsÖ-’ÍöׯŸ#‘Y…£ï‡N Ë´íôñr6®8ØÏ±K©?Gržãøy²iúÊœëgÅUÚþujÿ‰Wtß6úãÞOçn9°Òñs3ÌmhÙSÄ9Súm"üëñ§Ý?œz5}çŸ× °ÛsçÔûÙÒɨœÕÉ ŒÅbu¢èMê›v ¯ [/"{Ýõ”XVþäA»}v¬éÚ±ÝÏ8ëæt~°>ic¯'ïØËX"FéÑ–^ÀkÚ‰~ç®Ñ¾o½ÔÍ^>Et]óüïúö÷ÝûÀ£r‰>û·€Xc‰G§Hû¸ÛÿØ= y#íõïS_¬ÇÞ¶õ¾}ò#Uú|eTªö·Þ,Ùû ‘ÙG¿)Ò^îù4áófý`¯gŸú1"íþ¤©¶|•Ïå½¢¼Îi7sZçØÃËR¤=­eVZýÔ«V\¬ŸÖ>Œ›•o·_e¤xÁÑÇñc9oÑGËy‹QTö+wƒOÚÕºö75h>Õn¿ÓÍ›ÖÑ$¶]m{õ™î¾ý3Ç^qб{?1 ¯ñTŠ´£õjžjñqZ³ÛÆýØíðã]í¸|©µþ”*íçþ m‰4†Øú=-ªg¿jÇ)[ÙñæœF·•%]îkµâcégûMy$ƒúÒ~|¾ÝéŒy/Ûô¬ùÏcŸoÞ´-nŽQÔ¡ÑŸãÇKþøÜÈS÷POÚÇC­fíXÿG7êõ}:øËXý±Nc|·5*´ûÑm¿~–wëøµÆÒ'ÿ g ¢ž´ÍÄA·ç½§[ý¼tòÄQ¶¾œqõ9ß [_Û~4=»=>¤}<¬Ö߬xûtkØßª?²¤èئ :ý‘ÙÇù™Z)œSöË}0¯J•íþ´ìðÓúiÿ&'iJÛï^÷ެߢÚ• {}pSÅp£ðW3 Œ8·P¶ûÖ 7¤þ|¡‹~º÷rÑÛú{xŒOɤ•+,?7–É8”z²½+»˜‚~º¿¹°fÛÉæ¯gh¾‡~tä•ý­±ü Ø® ¾l÷í‹oú²Ã»ôÓƒ…¢;Ûzß\•væŠÀ§Ã¿y7«ÓóQ™FåÊ€Ê;ö¥Ùí¶ü­—º¾³¹;x¤ì`t>w°Ú–÷thaʽy-ôÍ¥·WÅN‹pøýž½®¿|}L,¡š3/”v`FïË?ÕOGš óº9{Éno¯ooîsåÆróõZõd»ïºmkÇÕ]u»?9-×9íöÜì‘8iÖwç{$˜ËÚÿ­±¬öÁˆÍ7_iUåËb?-øH?=ýÙ‹ß/k¤?´÷ñó÷./tè߬ÿòü¡ô1*._1cËÖçç9qèBi»ÃxbÉÝŽ^æ¼y2¿QþPܯãë7~âø×íoî½rÝ.cizç!·.¾9â\š´‹Ý¿›/¬õÓ‰?¼°:Qß$íÄá_Æ¿Fá;3Ƽ9|+õ¤]ì1_çFÙöp:í„+÷ÐI»}7pjÇk=ìöÝf.'õ¶ã­BÑ:[;ýwš´—½·?üBë›ÏÚýÊéœÆ”Í>dë÷Á“_ˆ%sÛO·½-^ˆÛë N—&ífßä[„äé§õ¦©õŒŒã¿}Qf¯óoËžãß³ã®eì<¼¦f§3L“ö²O¼õŒªÐOçÚýÒ×õû·xÌÌ­°ÇÉå«¡É—v²_x}Ö6Ý÷ß?,îÍƶ:³°ã„"õ\Ô—öñ„XN¾ó.ýtöƇn=²ÐÖë†}wæmÉ÷6¶™á]©Q$ãJêI{xbWŸ.ÇŒÖO‹Õöi¯Úz\ÿÙ]S/¹?7úíõ’¢XßodOqì+]ÚÇ“¡]¯2\åN³ö“[†|oó³>¤Mûë~ž`ëa›z_5BŽ›Fѽ¼ŸÙæ'.L—öóä³}ö÷ÔO‹éÙŸÚö»®|I@àÊýƶw'Š{ý®¸ÛÌâäúÇ{I—öR-V?æžrús—öÓ¬F‡mÿ¬xyü¢ÕÚÚãý¶G2®ybÝFñÓ¯~x]$x¤T¿Ûxóî‚hýtx윇¿l¨ß÷ÙU³ÇŒiÇcÛ–ltó¡Z£ø—ëÎ^5àJêI»8ôî‹©míþô´Xþ¼]_+–y¾;ãЫ‚êŒb¹NA}i'O5Ù;{ëøk{X´åîÚõ{oº9¿ëçoÚqÅ6¹ÎiÅñÔ—vòÔ~1á{N?Ýc`ÉàÕÎúÀ=mÌÇv¿¸-ù«GSG9ëDéÒ^žžúúŸ˜¨Ûx$&÷ØòÜukÕæ×׿èô7lúØç‰Áv?]üè&ºâï i/O¿p`ú=MV9íák2bóugçO?sÓÇ7¿’ãy•Ÿý±XÚ ø¤<3þ½EÆÍú鯽OwÚdóµZÆ!Ž~臚êè7CÚÇ3Êo-ùN]4 m~nãä:ípø©ùB¼¡1ŠŒZKSƒGÚdz[ãÊÂŽ]cËuê«¶÷ ¾â ~ÛìÏ>ÔóW§÷¬û$eœýþ¥X¾×´—çâžpgýZ ÙÖΖçÖqænŒ¹ÔŒkŸN†´—çÌíE-õSò=—~KùÛ„¦¿alûã¦ëǦ44J‡·c†¿xi†ˆF]þ§5ßÛ!XgÛÛª3ŸhôÀvÜqiüT*vÕLZ>i/5C»¾ª2Úî÷O==á“æI{õUƒ'L¬4Öé_¤õ¡*^/õ=ÿ¨ˆs™ÒNjÌí:“ôS;ÌJ[¯+LJå-íØÓò7kÿ„Q"š«å½Ô—vñ¼zozŠÉlqÎþƒ›¤Û‚ó¯»g}ŒQbû™Òž/xøhÓ°Bý”¢cÊ ´{ë§ýäþ£dü9F´EÔ“íÿü±k>ñøUú©Õæ@©_¿jÐÙ©GïuìF,[\õžc™²½_/¾±ÛùÔ -;tüég[ÞkW]‘õHÿWmªQý›QÜÖ\Pl÷]ÛŽK^JÄ×ô«è50hÏpû=ú¶Õ]Ã÷ýD¬Ö‡‹^ÏÛ4vZSðH{xáøþûOt+sø1àD»=¯øfåâ‹AÝ¿zt3Bv{=¬ØÜæ²Ð™ÿeJ»xQ­ÏÙóeñû½J½dhϼ1w]åôC;ǽøÚùƒF‰ŒC#ÎeI{xQ­WÛóâèn}nøþU}ÙëÕÝ×>fl«|x§ß¡d£Ôz/—%íàÅMÜ{ïŸú©ÑmxêDSÛ vÔ<ûú;SöyñÖFÛSÙýL™\7pÆ»,i/žßì )GŽÞÍÄ›D}Ñ{¯üæÅG/jü-‹6‚úÒNˆ b»éý”XÍk½ÇÖk–Ç›7Æìwô@T?d²Q&Ç êK{yi‰90:ëaß<÷ýÆ–Øí¾0<áÆÔ÷BÂ/vkø“ç+ŒmxÅ=çGYþêŒYÒn^Bè+6'9ësïÝë×Ý`›¯ø+Ì…GO*féIÚ%ø¤ý¼ô©W“áWgé'M3ÊÖ'g”Ìéºo’£e–ß—IüÔ—vòr·ô%íRïÔO®›øÄ‹ÅÍ-9í¾øeõUô{âq³2ù7â\¶´—Õþ«“×ý.vxó:·âvÆå ñ‚y¯í7¥æk°áÖøO}i'/W™5õ“Kú‹T#û‹UÑÅ£;:r«ñµtjËÈfiͨ'íáeñºþÎwœö huý¾ÉXzt™ßoÑÎ{Âm·]ˆ©¥íÌ€…úÒ^Ñë°/m»<9âŠVkÖ>cù¿QÖ;ð½a›:;|þøáëûfÙqâlj³¥¼BoßàÁv;žlû\ÏONµ·èWvíõ?wsö£\b'F©ÚgêèIÚË+j\±ø=¡â¸kÏ÷ž“﬛˜ÛI¦ROÚÅ«!Ÿý7z®~â`é9†ãúŸÞ:Öþæ c›œ_¥rÝ xi¯Þ’(ÞDè'¶..ünašqã}ͦœéú¡³î$VÏš>j”šáüDÇ®s¤=¼úá‘^Lô7™Š0n:÷ô…Åw•>æ W£ôÄñ‘¯­?¼lÿÚP¯½Î{¶\s¡Ân‡•íw|¾køGÎú[÷iÝ>k”Éu`ðH{¨UûϬ~êDôË_ ó¡±jBï.῜uÖ:‹å½F™Ǩ/í¡ÖÜnT¤ŸúŠG¿o·Üé“Ô¥·gªvdí—Ì‘í]û¹E?!VÃç·µ[½/lÕ[¶½¨ýŠF™ò?»ΑíúšŠK n%·oãý•q›c¿»g¼»®À(“ý$õd»¾¦öi7–ŽØ¢Yý±Z°~ÊØ¶ý;17Ê¿kfÏ«RO¶ïkû_}sÕ­Áö:×qµŸØÒ÷bùjÉoN¼ ×aþ4W¶óëŒîK>ëè¼/O½Ó—©–ÍÇ]ýùâÉe:v)Ûßî¿J÷œ;øä{ÿÉ•öðú„_<}Çüdûõña;výøî`›¿»çÞ×ŧÞZ‡ÑÃe{¥bÛZu3näJ»x½üþ)³®«Ô{ß[ùÓê¯{òMÜsgµ½¯§òÊù/î+µçó%Ï?ûHëgÿË•öñºŠ§ÞJÚ‡åŽäJ;yC­Ç¾/÷çkåú²C_­ó”h¹íÓóOºñ/íåhïo/úÈn¯÷…¸O?nÜ×çÅSŸ]{ƒ£õ¾÷Š™bç/õ¥Ý¼q…¹`ëì—¡÷kzç»]îK1”^ÛÙÙw%ßKÙû‚¯0_K$8ýÅ"iGo˜Ë•Óì8ø}3ü5î»óÚû{ÝyØñûG£‚¯_1Õ¿¤];zZ$íç÷ Zú¢Íç±}½á˶Þï;YþîÛ?:ýÙíéG/|nùµ5®ºñ)íè n,Љ])C¦ÛrW ½îãiùw;ë“™m®_3î˜=N(¿qÞc,’ötPl#~ï=ý˜ÜkTT3}ÿ½e޼af@ìÄ!‹¤=£vÈ»ú1©oc]ÜÚõMë2*û½ZFHh”u~9.}bo7þ¥ý4É5pö»‰]­©YÆú†á¥Ü=ШÛÃïê+›¥Ïfö½râAêI»9¨ÖýíýÒl}®äåŸ^)Ú`T®h6ªcÀ8g^²HÚÍ›jÿKýûæF>cCàìA›~c¯UœqñÖûÜÆçÒíþaÝ'~ß}yp²í7;Æ>ð¥±;ÁY?óy'X,íæmsô\ç»§ßôÚ¼±ÔX/ã[cǬû6»îyÉŠÅêçæCùÒ>Þ~íü¿¶Û¯6—ÝãõÍÍä¶]ìˆ9ŒJtæ»ùþ)ÏáûWŽyxB˜±î»ë/~Wn0µÛccÌÌû¾zõkÇߘ"øÖÜÎ>i/u*Ž{·ô½ß¿{b’!6O„žÊ7vˆ×³w?h¿/(táñ9 ¨'í¢.ÌÜ0«¿k†Å¾ÆÆ¥Æü傱Ó׿ ó Sí>áÑžá~¹¶¿+^g¸ c£ˆb?lûãÎ>_Õ;{š³.¸Dµ÷¤)kò^¤úÌü²ÐØðǘ†6¯³õ»Ó WGÛòÙãijos›A¨~èis!ÌØ`¾îò7v¶…‹œñ¯@µ«òË>‰]9; b¹fÁí¶ßìlšµ°åÙwœýèª]Åòpã7õC¢·öþÓî7ô>÷ÒܸÉvܳ³saÑ=Ö:óïÕ¾"Üxå3ýÐÀ':W%ï²ývƒfn °í{§•zÄ:~"íËéG Tûv/=íþì󵩱^­'ÚzÌ*iò@üwúHå—ÊïœyDj÷îæ@ú;bñiÆ#Æú¯—¬nä»ÐiÇ2ñ"íkÇÿ ”ˆ]Ù¹×èïÀíý÷³ç‰ë¹?îÎ"c'Ál×»ªíþÜö—e¢—lX¨¿³pá 9mlhwqõk#t;nØ)ã;£$-iy¿ûWFœ+Tí/>'øüý¹¯ÝšgBï:ÃÎ2c§Ü‡c”´Z!¾¤¤ž¾¾ÏÀ–1zÝ/ÓsÓþs;&ÖÆ~gûÕãbU©ád}¬5>ªøÁîÿ Uÿ_w¥éÑuµÍ—mm²Óžó?œúÐ ãñ«¹µ3Ξ—ŒZìÝáÈÔWãÀóâEë½î®>âM½±aܪַu¼Ún·Ç0?±ÞyÈs‘Ä¿¿ûÙæÿsöþ+Î7¿œ·÷ïv™ø—­ô/æybØœ™“çnÁE6¨‘îÛ{oH›4Ä·…¨¼5*g'°ž´§'4ã—ãT~NdhâÏl“*•c&DåâDÿ^ge~ï•{“úMoJySà›ß ÜÍ(oÆ}3à›û«ÜšU²»h¾*—&ð-¹o¹Ò-wLœÊsÑ-&xZC·u ò¶F—m¿ ÷m࣠uÛB§-tÚRÞö¢Ê‰ü¾5²²ò´£n{pµ¶=uÛS·ƒ¿:›{<ƒ[œ§ežŸU¥ÎÇŠPg`Qg`—ªó«Ð{ç@™ãGä í|Qåˤn—­òlkó¸ïë£rPƒ«/m®@ø¤n uû…ªüÒÈÐ>úS·?¸úDÝ p•«ÜÑÀUù¢«Tžh•™?܃Ðý êû©¼ÏU²ËáwtCÐû`?u–øyN¸y>™Kž[ižUyQå¬X#sP˜9'RT^‰zuNdÌ ažù¡Îq¬•CÀÈh™‹o$´Bá1C·ªÑ!òG‘ÇAäÀí£òDC{tÌ­1Zc¨?Zc}TnhàÇ ß}©øgîãæ4fZã¥+Åø( 靯E1&^:бÐ/ÍûWãûX'Æ8kl³ÆµKÇ31މñë·¬ñJŒUblã’{Î[ë¬itkŽ9—ž1m15¦ˆñÄK.;¬ñÂ}|¸t<°ÆÑÿ[ùj­>ß½}»Õ§»÷ç¢/}¸è»Ï¨6¬WçüUÉð©ºk@݆”5D· á¯!¶Û½7BæF”yF˼™E‹í5©ÁT(÷*•¹0½€÷Ư¼ùÝ”ò¦|•mà³ ýA[ê·…__`|á§¼¶¶8Ú—ªs}ϪsyËeŽ3O´Ê¹sAåÓY)ÏÂí îÎüí³®¥Ò|ÅÙ¶ÝÁßñ¾÷=}dÞΞÐìå/Ïþ5ϳ­Rát¨®¾ÐëKÿÐ=‚#°Jå¹Q¹íyT­BAäX sÑ›9æ¦,úƒýe~q¢Ècžsë’ùmÌ<3¥2‡ŒÈ3ø‘q2×f(8CÁzVžë:Ú%Ï ½/{\Ž—Ë=.ÇËçxÙOµáV «ù«óÖ‘«¾Ùûj°Fæ:nlÃh™çØÌqŒí6¢ýQæ ¬'°žèÛó‚Êoœ-ó7oü¤I¹Ìmìå%ó{qïU#Ýß;‚‹{oqOý¦À7-Wy‹éšE«œÅ5*g1åÍ)oÎ} •¯˜òÀ·ä¾%ø[ÂKKpµ‚—VØq+Ê||Tžâ­²[iM½ÖÈßšvnMÕùÛˆþ¿Zæ$n ¶”·åÞ—º¾ÔõÝ$s·Cí¸oÇ}»³nyˆmAå…+g¼›g¹»ÔYíµòü/ªœj2ßtOdè =ŘÃ}/`{È3sͳÓÑOïr™ºtúP·ÏJy¾»yn:|P}‘©/¶Ó\}ÑG ÷Ð ¬–]l?põC¦~➺ý©ÛØþÀÁG¸ƒÀîÐüàˆ± ™"ó@î"ã àAkr ¾‚á;ZÁÔ AþÊBDì îÁ*ßN…Ì©cžå-óæˆ¼o"gŽ™Ûm“:›=TæÈ1s°QùÕÊeî43_Z´:ó¼N怙"sV¤n(<†Âc(uC/È\=fþ3ÊGQ4|Ž–ç®UyÏ 5†ò1Ðë'ó÷˜9Ï„oËVΘiÅÉÖ8)ÆG16úz8c¡0 1ö‰qÏëÄx&Æ21މ1ìÿûºŸ)mWb¬ºtœú«1ÊŸÄxdE—ŽCÖ$ÆŸKÇ!¯5¾üÕ˜òWãˆCÄøabÌã…'ÜÇ1>Xc‚.íóE_/úx«·â߯ÿ¶úm÷>ZôÉVß+ú\Ñߊ¾Ö_婨¡/E hë†ø]Ch6Z#óÄ{о’ûÆØldòâò¦½½/ʼîÍÅ…Í´p©ÜìÑ2'{«P•‹g>Ø€ø}DFÝÖÕ*ßz© ŸÚ–sÁƒ/p¾ÀùžQ9ÕùÝ\í°ÙöÐm_#óT‰”âÜl3WD•Ê-s:˜9Á×e¥Ìceæo€—n¢ÿwìÞ_\[¥)šyÑ}OÚ£'t{a½Je¾ ‘¡7ûӚȾÀ x)ï\Êúó<] €æ@h„øË ðýly¸ÈŸeæsŒ“y°ÌœŒ2ǂȗ r%Šœí"ÿÕH1Ͻ¨rB ÷cýeÎËñ¬Çåx¶Üãr<ûwŽgU 9ñW3/m®ÁW|¯öÑ7 ¬¡?W 27Âoa›(k OüГ2OðxÒ4Æ/ƒ§1°é#›DpÑ–MÇ ¼^ÔõâÞ :ÞÜ{ãÞ¢ÿ®•]ASî›rßøfàn†7¾Y­ì"šSÞœòæ”· ¼øZPÞ}´ä¾%ð-á¥%¼´‚v+dlE™2ø “|µ†nk𴦬5íÔ>Ûàïm¸oÃ}[dlKݶÐi ]_êúR×›ñEíÐU;îÛA§tÚÛÚÛþ¢Ê_T*ó‹nÊÌCT!s›¹‡³e~a3OPœÊ\¯òS·3t;#_î»P·Ë™ŸÁÌ LyWÊ»RÞÍOæ=4óýP¿»¿ÌܽZvþÈäϽ•Ìi$òÿôc íÖ€ž´[OêöD¦^´[¯r™ÛÁÌ ÞÞÐéÍ}øèCÝ>2g°èBÐ_åÐí‹L}‘©/¸ú¢Ë@èB7Ø~àê®~àîW#»Ûþbl¶?°AðDy2!Óh  |ðjd—<™Bk |~´!Ç ø†V0´‚¡BYˆÓ?ä‚Ê)Y rXœ•y!E.H3ŸqÊM”"ó›y†\*¿pµÌ!læ ^#ó›ù~âTþdé¯røRw$m l(<‡VËaAä±0ó÷Àç(ê~tœÊã[¯òöP õÇ@kl Ì[$ò÷޾+ú3ñOŒ¡büt;­±RŒ“—ŽÖ˜h‡Â¬1ð¯Æ¿oìs÷ÜǼ¿ïÜã_1¾‰±Í}\»t,ã—ûØ%Æ-ÚÁ¯þjŒúGǦÿLÂKÇ#k,²Æ ÷ñçß{þjÌãûX#Æ™4¿ 5¦ˆñ¤Æãߎ#Ö¸ñWcƥㅵ¾,ƈ¿ÄØ Æ1&„(=\PùÞÀ×»oH;4„‡Fا'¿=«¥)6ŽàÂö›ÐÞM°i/`½¨ãM{{ã7Mñý¦ün*~SÞÌŸ Ý5¦9eÍ)kNYsÊZ€»e-à«%¶Ð²Zšw+øiÅ3ìÕ‡v÷Öøzl± emøÝúmÁé Œ/0íÄU-Ý¡½K圯R9ââdÎ7‘ÛÝÌå­Î”w†ÎðѺĩüj<ïÊó®<ïæ/sߘ¹×©ß=PæPë`ý«e^Ú¢¯ÅŸ{RÞ]õ¾'úì…®z­”9sÌ~2»È« âyp÷¥úR¯/º DÎ@èÓzý·?°ýÑ]`ú® x®<Ì@x¾Að1} ‚¯Að ¾`ðƒ/„²Ú9¤FæÕ¹|Ìüïõ*G[µÌÉ.r¤‰éfþó•Ï>GRdµìB E¾QÀ‚Ö(t0:Eæ,}Fæ,­1T®21G}›øêöÈåO>ðÞš­á©5ôZ#s›.àÚpßüm)kË}[èùRÏ—z¾ôy¾è ÷í¸oöÐhl{xm_'»žÑ*Ÿ)úôóQùIáÏï¬Ê9Z ó‰šùCáµÓ&™´3÷©Û\¹ï‚,]¨Û…û.àî ®®”wå¾+åÝe~Q3(¸»ƒ»;>ÔÜþôAþðà_#óºõ®‡¿à¿'p=á¡'õz"{/tÔ Y{!ko`{Û›ûÞÜ÷‡>Ü÷Ù$s„ÀC<ÀC4ûB§/¸úVÉ.3=ÂC °ýÀÕvêÇ}?øïOÝþÜ÷¯’]j‚À® ä­”~ðÑí@h ¤| ´Á÷ ô>žƒ¡ \0tBx‚¬!b~pQå¼+•9í_”¹êÌ|¦›dÞÒ¡þ*7iÊ;JÝa5*¨Kå=«rf«\žÜÏHðŽ6ÞB EO¡ÜÏ(îGk”˜€k4õGgË<ž£ÏÊžc(Cùø¢òy‚,ðòüÿ{c ûØ'Æ=÷53at—Žub\sÇÄøeÅýb̲Æ*1FYc“µ¿CŒ=bÜqgÄøb+—®c[ã†5Fˆ±áÒ1Á}d¥Ê5¹Uå†ç¡ð1 †Q6ì¢ÌÇ8œòádÞ‘¡2·îHàB) åï(àGñ{ð£ ìdƒ>Ær?VŒwbŒÿþ»¬aÿW¬_ÿOŒC±™ÿT,ú_‡þG1è_ÅŸÿQì)ú¶Füù_µNí{þgãÎ8É—˜j*G<ýQìºíי໠¹oH{5„N#.l£eðgOÊ<õ„ÇÆøbcì²1í×™špß$… Ý6Ö _òâÞ‹6ð‚7opyƒË›{op7ÅW›RÞø¦À7ÚQÞŒûæ”5§¬9°Í)k®”µ€Ü·ä¾%|´„VÐm­€mE™x|àÉ­¡Ù¹[¯‘]FàÚЗ·¡¬-4ÚRÖ–û¶èÁ—z¾Ôó¥m}ÑC;îÛ¡‡vÐhl{øiöÔíÍ¢¿¾²ûAÇþüàݺCUnûz•³ÿéDY'èt¶3u;sß~»PÞ…º]6ÉüÀfzhu¥¼+ðÝBdÞz3÷<ðÝi»îðÜXøð§ïð‡ÿ:™C¸°=hçðÑ>z¢§^ô«½ Ù«Bæî \ohöæ¾74ú@£õú@³Ï™ó=¸O_höO_ð‚+šÔëž~ÀõO?øéO½þàí\8ƒø„\AÀvp€ßág ø‚ø¡¯A5²« 0°ÁÔ ¡,„ö ©“Ýïà™Sy0úâ%s"÷!ØÆà‡†È|÷"¿ý°h•ǾVåªS9è¡9"Då“Ïô:¼#ÑãHê†Â_(°¡ðÊý(•G¾Fv;<£Á3^ÇPoL­ìòdžªœñÀŽ¥ÜìãÅ?k¬ûGcLkìùÅb,cˆ5~ˆqâ¯bH÷xñ¯bE÷8Ð=þýç_Å¢¼´}£û]÷‰~Îê«Dß„íhð­A¯zk@݆è²!å°—FÜ{òÜ“ööÄnÓ–M°±&ØUsÑO ß–”µ¤¬÷>”ûP·µðqÚ« ¼·…F[Ú×—ömO;v ~ÚÔ/Pª¸#ôº"K7Ú¬;¼t§¾?vãÎè½G© ú"G °spöÇÞúsÄßܬ’a êây0vÌó`è†ð,„zƒy6^‡‚w÷ùoÃ…ý•Ëa tÇòûò;w¿WÜòßu íŸÇü]ß¹Óß{œãúœë ®/¹¾â:Ïõµ‡ùÞÅCäRÿ–ë;®ï¹~àú‘ë'sýÍãg®_¸~åúëw®?¸þT±ÿiÌW4æ+}¸F®1_Ñš¨ï¤éË5æ+ó>]£O×èÓµVê0æ+}»F߮ѷkÌW4æ+ZõMF?¯1_ј¯hÌW4ü_ë®öÄâÿþ¯áÿþ¯áÿþ¯õU{ ð ÿ×ð ÿ×ð ÿׂÕÚ"þ¯áÿþ¯áÿþ¯áÿÚH5‡Æÿ5ü_Ãÿ5ü_Ãÿ5ü_ÓUœˆÿkø¿†ÿkø¿†ÿkø¿6YõÝø¿†ÿkø¿†ÿkø¿†ÿk3Õ7äø¿†ÿkø¿†ÿkø¿†ÿk±êû8ü_Ãÿ5ü_Ãÿ5ü_Ãÿµdµÿ×ð ÿ×ð ÿ×ð ÿ7÷µáÿþ¯áÿþ¯áÿþ¯áÿæþ ü_Ãÿ5ü_Ãÿ5ü_Ãÿ5üß|'‡ÿkø¿†ÿkø¿†ÿkø¿†ÿ›ë²ø¿†ÿkø¿†ÿkø¿†ÿkø¿¹þ€ÿkø¿†ÿkø¿†ÿkø¿†ÿ›16þ¯áÿþ¯áÿþ¯áÿþ/Æ= ÿ×ð ÿ×ð ÿ×ð ÿ7¿¿Çÿ5ü_Ãÿ5ü_Ãÿ5ü_ÃÿÍo ñ ÿ×ð ÿ×ð ÿ×ðñ½‹†ÿkø¿†ÿkø¿†ÿkø¿†ÿ‹½Öþ¯áÿþ¯áÿþ¯áÿþ/ö jø¿†ÿkø¿†ÿkø¿†ÿkø¿ØÛ¢áÿþ¯áÿþ¯áÿþ¯áÿâ}§†ÿkø¿†ÿkø¿†ÿkø¿†ÿ‹5o ÿ×ð ÿ×ð ÿ×ð ÿk;þ¯áÿþ¯áÿþ¯áÿþ/æ/æ¿lÿ¹gÔ»Íq*ÜÞÝ–«w·êÛ¡÷˜ïrýÕùîsh·¹N ÚO^¡ö“û«ý7Õj?y šï¬tÛƒꬳ›{ÊCÔZ{…ÛZ{¶ú³Ví-÷—ïƒÍ5÷zµÇ¥¾ErŸÿ¤¨ùOzOáì17¿É TïŠ×¨³ .¨õ÷õmfÚoîïvÆA­Ús¨Öãר}ç>ꬃ¹ÆfîÕñSó£•rÿ¹yæŸÚƒž­Î=¨Sßmª½;åj¾T§ö£û©=éqj_úVµ—ÇCíç‰Vs¨ KšûÔ½ÔS—Ú¯¾FîY7×ò¼ÔY Ñê[Ï5êÌ„yn‚¹ÝOíeSg(l’s/ó»+µ(B­û—ªµÿjùnÛü.ÔO}š¢öU¨=ïõjß»—úV4D½ÿNQûàר¹[­ÜCd®-z©wã.µŸh¥Ü+ÖÅw¥æ{?õ¾åŒ õ§7 ¾¦¡·(*FA# þ£à) ž¢Ðßtô7<3}õgð|õgÂÇLdˆ¦n4u£©MÝhêFC#ýÌâ÷,xš…žfƒg6òφ¯Ùà›í9üž#~ƒ{¸ç"ß\ðÎ=#§ó€™Ì<`æ{:áo xcÀÞXôËïX~ǃ?žúñðnã‘YäŸíxLàyº9‡-‘ç‰ð¿€ç €_þàZ®$h$ñ;Égä&ü)àOA§"_\*ºHv!<,„Ï4p¥Ñ¶"‡È÷’Ny:唋ü$è(cœòd+ \Yàg÷‹óسÏ>xqþxð9À/¢¡›<~çÁ8u1¼/†Þb艳<ó‘!Ùò‘-Ù–Pg  ø[ˆì…”R^Hy!Ï—R)õ—Cs94‹ YÍ"hA§:¥´a0eÐ([ã6N_Þ¿~y.}y.}y.}y.}y.ý¯žK_žCÿ»shMø½ <þçž_tÆí ÀMn{ÔVª=j^ê›ìlu`ú~1[¾G°ÏŒSç^P{ÕRÔwÙgÔw,r¯šù=cˆ: pÛ~µõÞ°F}çªÞnR{FBÕžµMjz‡˜­Þ!žqÛ»VzÉÙ€ÙêlÀzµÍåöm‹‡z—˜¢¾×®Ug†ªo\ªÔ;EõÝvŠ:ç¨N}C"÷¥˜ßoŸQßo»äûEqf ¹'Û_¸F~[iž{䯾±,PgÕ˰Ë|瘢¾é®RûÝ<ÔY‚êýãJy¦ ¹‡ÛKíãŽSg n’ç šßÇø¨od¢Õ÷šò›Mó¥:/)N}^+÷°˜ç&y¨ó#Ôž™rù~ÆÜ~VG(¿gšg*m’g‰oÅÅ>º°P*šßŒ¯”ïƒÄw6âûPñ~1,Bž‘&®hyÖŸŽîtùí¶ù~²Bž3hžÅ"Ï4ï„Ì}ækÔ·95ê{su~J„:C¥\žÕ4þŒÚÓã/Ï@1ß›R>þ¬Ú¯·IîK7ÏqòRßòÄ©ïwJÕ7<Õr¯ºØ£nžX¥Î>ä~êEõ=ºKžƒh~‡^-¿Ý1Ï?ôSû‚¢åž=q¢ù]º<§Å<¿É¥ÎpZ©¾ã©UûÚ½äþ=ó—ú.}¥üV¼Wßó„ñ,Œga< ãY˜Ð=tÃ….Á­CWG_:úÒá;Þ"à=⬠»#‹.ü‘è%™#‘!ØHà"ÏÊÐ|ú‡î&ÀÓø™üt4Üy>‘ß“x>‰ç“àiº„n'!ó$dvQÏE¹‹rå.Ê]”»(ŸïSà} ´¦ 7 ¾¦¡·(ø‚FüGÁS0±‡9:þòLÉhL@ÆèD¸äwbÏ×ø*yÞµØæ%ϽŽÿdèMæyø#ÐÝ4䘈ODîÉàžZ%¿Õž¿Óx>¼3¡1þ&#ç\+€k8£¼äaϦÀwxc‘-œ3/Êî5Çk2ÍÏ\p̧l.üÇB/¶J.¦sŸ $ä\Dy8“áoø Ï“À“ î\ð,®¾ P".ø-¡^ òeŠxÙ²y–޾³y–Kýe•OàŒú~'Úí»q/•—'Ûí\)õO¹<[ªûuæIˆÛy©õꌩPõMÏ&uŠ¿:Ÿ»\æ0¿)Qg§VÈó¦ÌóºÕ™S¥òìnóûQ/•»'[žuhæ8£ÎJ TgPe«sSªÕ7ç>ê»ó•`«ÌI`ž¥â§ÎS‰SçSm•gT™9 üÔ¹«)ꬪ •¯ Nî¥ò¸Ô7C+å·êâ» ó+õ Q´úŽh¥Ê T«¾'òRß°G«ü@kTŽ Z•'ÈGž·hž;ª¾mÏVç5VÈo6Äy®â[wó$?õ}lœúî½BñR«ÎÅòRg½ºÔy¯+Õ7JÕòìó[%uŒKž)¾­5s'Ôªó`}Ô÷ò)ê\˜rõ|üVÞü>>D—U¯¾‘÷QgEFËÜ æyY5ê¼unV„üwJ…:; Y¢Bä™°â›Ü¨Puìu–LúfÞG~Ëdž%­ÎÏZ£ÎЪ“gË„ñ,Œga<‹€öø 2ć®^}éèK‡¶ßs‘¿cá1žâ¡‘Œæ"O$òÎG$p Ôá~z ýqÔ¢– ái²LDWQÈ8‰ç“Ä3xšÎIèvz˜‰¾]ÔsQî¢ÜE¹‹rå.¡xŸ‚ƒú3¨»k&¼Ì„ïhêES/zÑÔ¦n4´¢içYüž_³À3<³¡7¸Ùà›Ãï9üž#~ƒ{¸çÂÇ\ðÎÿ<ø›Ì<`æ3ÜóÐe °1à9#§±è+öŒš† ËxêÇÃW<úz×|p%Àót‘€xžO‰ÔYÿ À¿¼ {ü&ñ; ¼ÉàHF–p¤›þtš¿©õrZ³Âgü§aïið˜Nyz½œêdPžAyt2à+ \YàÊW¸²€ÍW½œ åŸ|ð9À/¢Ѷyü΃ŸÏºt^ué\JÌ£Ü×ÖÅ\IÌļÈ}>D{ÿ›y5×sÚÚžÃ`ûæœEÌSļDÌ;Ä|CÌ3¬ù…˜Sˆ¹„5pŸ?ˆy‚˜ˆy5ÀnÍ9€ÿ‹Ø_Äû—Æ÷רþï°†îïñ_wFvˆÊPí–3«B}§ZªÎþ•çþ›9_ Ô™ÿê¤jõ=z„üÝ<)B}^¯Î<*W¹°¼äsâüŽŽgd.,‘CRä:ßB›ù_JÝÎ )ßJ‹¼*f—<Þ<Ϩ@žŸmž-'Ï95Ï–ß*ÏäçÄ‹³xÄùš½Ëå9ýâ›çˆ@y~¼ÈÉ2ã›pQvg}äÙ{a>ò¬ÑåE€?Z<Ÿþià™ÿÓà+G¬‘yǃ{"ý÷xÊ'‚o*åS¡39'‹¿Ð›!ÆvîgàS¨7øÉÀÆ7’ÑAõãÀ8p$#o2<Ì€Ÿ8êÆg.eó/Èe½\ô‹ŒqÜÇÀg ÷IÔI‚ßê,ñøMÛ%C³@+Ølèeò,䢋eÀ-C¦e<[&îyžGý<ðpÏßeÐ^_%Ô_?%+åÒßBäHo:´³ÔM…¯TäÉäo6p¹È•NYúÊäymš ¾\!':ȃŸ|Ú6 ýd ÚÈN½%ÐÎ…Vp™è2ä#ÇQ|”-§]ó€M§^ºÈ‡—%Ð[¾Ê3á/—k¹K%À"[õ ýå7Þ—S¯”çËù]ÊóBôVʵ^Ká{9€Ë¡S(~Cëÿ°w.àmVçWKšeôåâÄIìà$äB€€ã\ŒIôÉ’mÉrdùnY–eI¾Ér|“mY.lʘËVÈi.Pjw'¥£¥@«ìÙ ¹àFðJnááæ”n¤eŒýŽÎ‘-Üt[·´£yìçÑó}:ç}ÿïÿ=ß‘¾÷|Jο›Ü"ðî&Ÿvøµ+ïãa<#Œeþ¸FàÁ¦ü>ú¦Ÿƒë¦Ÿƒ÷릟ƒO?Ÿ~>ý|ú9øgù9¸¸öëÎ_ý„ñ½œ ÚƒJƒ>MíUÕ¯4ÁÆÕ>©©¡ð)M°JÒ(÷ œ@éÐ[•ýˆÚ“Ð*µ"'´èõJ‹~Péè&+-…¥©Wú‘ýRs'¦§«W:’ýJSw\iêz•®Â!¥fLÐ ;¥´é½ ûXé•¶n$aÏo½Ò¨û~Çtêõj/ĈÔYHSû€å^<±ýÀÇÕ~àN¥·;¢öJÌTº>ƒj_pÒ®w*í…j¿+½ÒßíP ”޽^é[)-ÞÝRç2¶Ïb²Ú+ tÆö+ÝKÒ3* Ì©ƒÛ_\§öȲ*Íû¹Ð!ûVÅöÌÒ+½Ì"µ¯Ð¹wãÚµ9q/ÍT{’w(]‡ýRÛáÒq¥ï°Fîå*4Ä&t~G”Ö¯N–…—'+ݧÒáPûB>¢ô‹°1(=£Ô8ÛêTûrH}¢Ø~çz¥Q$5wžb¯£MR-¶çÑ,µOl‘ÒØ¡4ÑF”Ž>éV©‘Û+v@î?)4ÑÄ^±½'3Õ>±cjÊd¥á”z ±=Ó©½b‰WhUû§Ó¿£Ã¨t„ujÿ®Lµ‡WDj…ÆöUûU‚•©öŒuª}Õw«½cGåþ±[iÛJÛVÚ¶’ÇVâá '—\5ÆKc¬4ÆBƒ·Q¼ð5’o6ü²á ~6c’MÎÙäœm6vÙØecgb±òÉdž »ƪÜmÄÙÿmpÚ§mcr`‡w!± ɽÿBü ñwÀÃAEàá[„o¾EÄ)"±‹Á)>%—%à”¯^%à•r^Êy©8»ì2x”[~9üʱ)Ǧ›r°ËɱÛ p+À­$÷JÆ«’s'çUp©Â¿ ^U\›*¸T嫎մW3ÕäPM»Nn|jà^~ ¸5créâáÜn-µäãË­|/¹yáëÃÖO\?üð¬ƒó½Žõô×Óß@ý ô7§^M`5ÕVXMØÄ ûfì›±oƾûfì[¸F-̉íœo‡Ïv¸·Â½•x­Äk%^¹µ‘[Û¨\6µ¯‘K§ŽäÞ)ÖÔôwÒ߉ÿþÝÄì!f1{ˆÙCÌâô'BÞ}ØôÔG›AÔÊb}'þDa-ÖÓ‰kéøÚyZGeZGå\ꨈµa|](Ö„g[ 2§§®'Ö|‰k¼ÄuXÓ‰õ\âZN¬á¦®Û¦®Ù×kb­Æ¼üÔM¬Ïâk²øZ̨›\‡‰5XâúK¬½þ»u×ÙÖ\ñµVâÚJ¬«Î¶¦úMk(æäÄú)qíļüµ5S|½t¶ßÄizmô»Y]©ûLéÅ´àúÏ¢k< ô¬RûMÜæõ+Í7ì’f) {°“À^°_íÇPÚniJ£8YîíÓl°NjØ/>£t…F”–}ë“•(c“ʽ#5 öó5¤Sê5Ä4A9ßà•û­ ýÍ-V©' 4o…F™ÐúÄbïÖ˜véM¼e@i I·õiR³AèÝ¡–I.[Im¡°eLêñˆýÓ3Ǥ¯ #±ÌCRç@Ü’rÁË…[.˜¹k¤þ§ÐrØ,êLÚ·¦I­c@êY†¤þèZl ­Ä²ÀÃH›&Ö¼·="µ‹óñ1ÃɆ]\òisp´ÑfáuùyK, ¦…ñ,%of°ãRË4¶màÚ„ÿ€Ô7¶Ñ· ^•äZkUâ=cQ "|Šà^¸FÞ> ¹7’{cæ:%õÝŠ„=Ÿýjl«Át‚Qßú[¯RÚJÁ£ ›b–ÁÑ'yÔÐæµ1êÅ‹¼«°¯·†÷Q÷ÒÖAÜšLùH¹šó¯^^>0z‰çßO¾õØ4rÞfíp ðòñÞ?¼´×“okÐ.øt`c'æv‘3cÚÁlç};íÛáЧ 93æíðju½Â—ö.rï&Ÿ^üz“R0zéïf ʱ)缂ö ü{EÇJúªàR…Õ¨,W\Ä©†c51ª™ÕcòQ¼›xnlkàSV þ5äãcȹ‡±­£^0¼ØzÉÍ /ñ|Øú‰çgŒüŒIs¸ŽüëàTO=ý ô7Ðß@qàÕVXM`5Õ„m@¼°oƾûfì›±oƾ…±já3ºóí£²¬j…{+ñZ‰×J¼61~äÖFníðoטcÇNr錄“þÎ1Y‚…ðáßMÌbö³‡˜=Äì!Nqú¸n}ØôÑÖ·_î/ý›t´Äó¿Øß¹ú=é÷ù[’¸‡ü_G:_C:Ÿ?:׿ý¡ýn4ý›Ñ¯ÿf4ý{ÑÿÿïEb.[uç§œNiE3V³ÁÕG­žû«ž\欑šsIÐåPÚ£³´¢ñï|«Òæ ?I¬#àǤ1¥Íÿ°²Ä. á²±2)=£Ô Îà— æ"ú9åÞÿB7zœIý°ÅðZ|HÞ~—dJ}8C AË´HéG“g ×,üT0 \71—Âu)ç9Øh§”†´WivŒKm¤´d¥ÛÑ/uNÓN)Ý8°—õKýq‹iÇá»ßå£JÇÃ*µã »¥vZLû4Sjy˜¥¾ôÅãJÛ•¥'G»‰ñÏÅ®€ÜíW* )Ú CJ箫­RcNèk¤Ó—>*5m^©ïÓ13ƒ±Ë Ç ð2†¤Ž ÞñÙ8*5ž…^”ÐÚ(ÏA¬Í`lHç20ÊÇ¥æFLëÙª47èÏÚ¡tÁÍ×õÔxΓÚnB[ÊeUzä¿%Mê=;‹¤þF±Ê‰› ƒ+ü øxÎÈRF—åŒq–,Ab%1Ä3ak—eŽl3ØfƪŽ>3Nf±†#39˜Åu†_¾ÅØæ+~¹¼ÏežYàhÁÞB^ò²³ 9烙W7ï½ÄÌÇ6Û|lðnÁÞFŸ±°‘OÇŽv®¡˜vb؉g'ž¾ö3“å–.x;°q‚á€K1ãSLÌbr(&N19“C1ý%à6cSv ±KÀõõìR0Jé+…cé¸,ÝÊ‹&ÎÃ`U«‚X`WO…h?„38•Ä©$Ž“œ\S/?ç.bºˆçßÍË…­›8nŽ!aëœÜâºíyïaœ<à{§ZÚk‰éƒƒ>Þûˆï£ßG\qýpõ3^~òò§n\–õø4rýÃphä³ÒÈø4‚Õ~#ØMgdYÀ.vì vAì‚Ø‰$NÛ †-b #®9´³•óVⵊµ ãØ ^›X³àÛŽ_˜þv8¶Ã±ƒ¶ý]ôw£‹]´uÁ¥ ü08apÃ\ƒð¸,Y{áÕ ¯^xED=#j?ñ7u=òÛhÍÄ×âË'q=!Öñçïbí_3œM‹F¬DýŸXï‹ÙT-rŸÖ3ÔÉzVÔ±‰õkâóíx½šX«R§~ªFµibM*êÑx:µîõf¼ÖœZ[ÆkIQ;ÆëÆÌ„zÑ8¥6ÌWu_QBw¶gÒAU‰:l¯ª}Št¿W G¡Qªgž¥o=ã9œŒd©OšE^I`dñy3/]Üó9׼‘¾d®Ÿ§ÅÄÕF¥^¬\ï àšÀ5qÌßFæ`X9CRã;ƒ‹{,X&|¬ØI½E ü,ð+ Ç,°s±·cW î%ðÌ" 9Ûé/&ŸJÆÓN_ޏG£œ1p2ʈå§L|cïÂ×&¾“±w’Ÿ‹WüäìËMü\¾3ÝÄróÞ†[|¼ŒC8ð¼ðvÏ Vö ø8‹û’øÎ¥½ ÛfÚ+Áo¶™þ:^ÅàÔa×Ä8¶€ßÀ±–nÁnòl³þ.òîaLBpq7,¾³±mçppáãÇ&L^ Ø»É=L^|êÄw¦øþ$FXp÷¬ˆ|LÆ?Ïö!8…Äw¬x/|à¼|Pjkö¥ÉZ?ö÷‡òoˆÿ~ø||îÃ\˜Ö­Ô}¶u+ùL&þ}pâsžÿé3ñ|gê³ÏÚ¿ž~®sîžëˆyëÔŸ:«|f3Gf“ÏlpõÄÑsMô|§èÉgœ×ã3‡Üçê•þ"¶sñÇûyøÎãý<|çã;Ÿœæó~>ýIðH‚GÒÔh:ÔBO{ã³¼à.¤m!\Âmá)©ß˜ŒM2É佈÷‹ÀXÄûEøld¼²xŸAÜÅ\›ÅÄYBK¸&K“zÜ)ÄK'›”Cò¶›Ê¥’[*8©Ä^ŠÍRø.P:¬ø]D¼‹À¹œ‹D8 t#Å¿´q¥å ö2Ú–ù,¥ÉŠßrr]ïz©ñ½‚\Wì—ú¬éôåâc%N.ùXð[ ÖJì²1W‚o#7øv0WѾjDꛦ‹ú[+×%.é´§ƒk€{:˜éãªèPš”à™èËÀÏ€ýÆh£C‡}¾^– %pÚ—M¼/§¯œk¹9"µÀËxm†k¦Qi‚3ö™àgÒŸ5¨ôRÁÌÂÏ jDÚ]ðÞž û-Ì\l\ä³…¹´§¨¯±w1^Nð p1“†ÆÑâšÀËÇß„­ N&ìLô›±3ÓžÏØ™¹^fâÕÃ̸™Á6ƒk†‡™¼Ì¢n‡_9ŒU.8u¢¦Û!Ž´[à`!§|°,¢VåÜ&êU^ùðmÀ'›bâÕÛ®yeãzp,áå¦ÍN;xvâTÂ¥‡£{\`÷ÀÝ–ƒöb8cS ßbø«Œ0k±/[ KÀ+Á¾„k[‚)þ¥ŒC)çe`—‰u¹—a_Áû |+ˆ"‡0ã_AŒ bTr^)ŽÄªkò Ï g1]ø»Ä¹¸.Ä«%×®o3çnñ5øY¶yÄšq©q°ñ7È«–6ñ}´û°ñÛGL1ýp¬Ç~ ñêÀ«¯¿údYú5’K#q™'`5¹‘qoÄ?€]»Ø°ƒØ± #n»˜Aòn!—Æ®…±j引حÄmÅ®u\–‘mÄj'F;þípl'F;ã—%eý]Äè"Fv]pé"FvapÃÄ Ã¿^½ðê]#KÎX]*þâk‰ßöYM¼¶ÿ¯žÅˆZ]Ôèñú\Ôág{“øÜålÏ\Ÿ§$>Gõ©¨I§>G5èÔúSÔžñg(SŸŸˆ:2^ Š{èYþþÿú9Fb­”Xj¡Äú'^ëL}žáQµËÔç^Ý9Ñ0NïºÖzæVú¹T›‹]óf#íâ f’øŽæºÀ\(ŽÄ70/5úLœ§âo"~ŽxÁÉ– ÿ\^Vlré·‘»<yÚ9·Ãßn.|Ø;ð+‚— Žù³ä/¶Œ£žåà8Åwø$Ç2ì]p±ÓïÆÇ"0é³ÃÁ!îEÌ»°ß•ýâ³Ç¹ßfæAÈ+—d.Ú½|^ZÀ ƒß†N-ø„± “s“ø~aLCÄñ±Ž1 aÂ6,¾?86óÙ ÓÖ,>{âû û°À€G¼Cø,ß/õÌûÖèÎùßÖËŒo‰}G¸*O?~]ï¾ú™Úì½½<µYñ¼ø…K6<¨]¸éäÛ)æË£wÌxïÚü¿jxíÌïNOEŠßqÍéSoÌÒ6Èþhø…/^ñ¼GÛd5¾Òùí~â Ê8?¾&-ýø>mäÒà›Ï5>Ý{wqê'­ÑáÛß»jÝÒµô ç]—yÐíý†ãå«ë]ø~ßÿUëûOÞ«Í­ºëù'>zF{z<·÷_Ê;£{—Õ}|Â5ßpòå·ý;-:|‹·¿«òhä¶îǃú+üǤÿ=㮎[^Òž¾Ùýúß_\݃鎷½Ý±êó‡£}3Þó‚ï1ã[!´ÿºokú~ =½ò_zóúµ*¯=WÕçΙՆŸÇãi=ÿЩÚÕ7µ™ÿ¶ïñhd¨µó¶\ò/Mâ5ýâ…—oµhÇŸÔ¹Äs×ĸîù£Ù?ùÑk·ÞZÛÕ©­,:|Ùܯðm-ãsõ7~eáâh$iðškoœÇQâåmØõÃl›6_òÒŽ·ß¶ñ™ü×â×/ºçKŽlÝ»3:l=uêwhc0™ÑÞWÖ?ì9ýZÊs¿¼3sx^‰·n|¸´áóÚñçÊê ÜÑ=YI æpÅ»»uÑÞ?pííÏÇ^^ÿã÷œœ±=¬kxvOûÐÑ=ߺcgŠ;:,F«ëÄäuÉë~|äuoö×7OÌËc3®o×~ÝóàŸ½<Ôy¯ámsIÃê{¾ ­[yã/žxã§ÑÞž½1üóûð—×ý8Á¯©ÕŽî¸|ìËK&ÇýºSïµ|m¦á}5^Ãbº?°?>ÎqÞÚªø¸wÉyqܿ╛voŒãhGdSÊ mq7uþ«?[ÿœD‡ÏüÅ®HåZ-륡]užîhDû«7òNWL^Ï.9?ޝlÜÒý‘vD 'ýeüúDwß²éÖYyi†±'>|øÝoD÷-»¼7ýÖƒÑH×'¤^ò3üå|8þÅÂ9/<µ+>Oµ#;¾ùØî'_Õ–©ÏÓ®“ÚÅ£×LðÚ·áÄݾ×>ŒF~ܸúÚÜ£àÈypìŶª–iG¶-¾ùà?£».ýü—òõD÷­~ìŠw*Nh[Öe¼zçѾ«³ßÛ›ŸœÇv$¿Yù×·>Q×éȯZ´*ûûÑo^»¦ýæg*&>ŸûfþÛíy]7L|^úöˆ¿½àÈùqlsÁ÷¿—ö³ôã—~ÝyÊñÇ%·.šÏìÁK ñå<9*¦ùÞžÈÿ)§c[[ëñï‹èίF¶/\89ŸÕuYñݵW–¯÷E#ibįOΛ£÷wîøðщëüÔçVîy÷ÚŽ‰Ï×ÎM'ÊOÞ|ïäxêWÔ=T©Ó®ôÛŸ9ýÝöÎøÄœ}וƷºå¼9Z÷--s½vø¡þÓ—­¾o’WÊ"2yhòú^öËšÂ[÷OÌçð}ƒ˜Ž‚#çÉÑ”ØÐƒ‚Ñ$ŸU£ß{±Æ0q]ÃO$-¿ïƒÅOÎ#‡-3 ïÕ/ûd_ Ñå×…~:7ºo­+ï[]æ‰q3kNÞÀ¸vËùp¤õ¦w¾½O;tÂ~ÝWžäý75M®ø“è¾TÃs«__ ×\õŸì½TUG×?ÌE,X¯•ÞÛ‘"ʹvԨرEì`‰¨¨Ø±£&ƨQMÄìØ˜KSìÄX°ES,‰c£1ù~sfŸsîË›çý?ÿµ¾µ¾¼Ï§k]¹çÎÌž={v›93{¯¹ð>Ú ~8WãÝWŒn«ñC ´YÃß~gë­¯ÎX±–esêH1ª|±éª<$ >8»×gêúÒä¿u7LÉëÙúQÝ—M鹋eo±åÐ鋚|A;Ág‡¾7³tçtM_œ¾2Þ“-§Ñiý¡~6Q¶Qwˆ²ÁÌÍïeÈ^ÎGüuÍžÍØ½®¨s!à >8[ûúTÃ1òéÆú@‘­ôÁgÕ“¢n¾Î©våCƒïGª^5=˜&æûÌñwò¿_/ŸnùuàЯ؆*í†9»Ø²ëN·‡ŽLe3xwµ>D}1¯gâ§ä8m’OÝ÷é²ct ÛPóö²¾?Ï`ÙßÌüàD¿¥ô~ļž©·ráü-[äS Ê…å+Ù††“óíf?dÙ§·¥97¹Æ’}?2æÙýˆúb>Kò3ö ©óZ>û[íF° ]_÷í[Ïe û¨ë÷ibK¦ ió8Ù Íã)‡ð!cïÞÓôè†Ã¾“ÑWçßE³»ºEäËÑBϱ®ž˜×>¼ùG4¹*>kàH›Ÿ´Ñ½Ö&®ÈÒè›r»Å”¦}eu¾çìý¬s™ à‰ù>};zÜåÛûäâE½bs½^êú=:í×*™‘únÿç>ïÁ²Ë²27÷ÛȦ—~0wüèݦÓżž6š"2—‹^rbì:wß4žåTùöçZóÕåV•£éb~O}ùǬˆ…•ä"¡oÙÆ¿.d6lú%ËöMoS´4+àê‹y=µþƒÏ†”—‹æ]zp„9±MïçNê|8Y£sNݘ'·7õ@}1¯§zM̹³ðµ6OE‘¿yÕgRŸÙ¦/ïõ<åðSÔ÷Žëæ¼ôÜ ¾õ¿4þÞ<Ùô°6OÓżŸr\ö½ùFš>-ümÖ¯¦‘Ÿ°M?ß®Òãù ëÇ[«íÞ®Íwò0®à“GÌw17ó©rá‘ö¿Oé¬ùQ›nwˆ¨û|1Ë©95ª‰%ÿôŽçõF;1¯Å+nýeàA¹änÓîÜ·ÓìˆJ·äËÜQm‰vb>‹eŸêÇ;|+Bx/0²MÓ»Þ}áz‚å8pż›%§¾¾Ó®£éÁ 1ŸEß[gyà ¶Pþiü³©û.«¨‡.:_¿Ž{ÐãÞZ}|3ļ-þûÓÆKåBÛþXª¦7E^ýÈÝû ˾Í!ÉrÛ¡¿öÒû{ͯpó8b¾‹)‚6ιҠƒÜ€ôÒ¦î®% ¾™¬ù+Ù›Çܼ÷½ÜEØA•ßT=¸‚/ ì\Ùp{}Ío.Èý}_óÑWئ¥ëwYã‡ìskZùœž%·ÝwÃo~ùJ6«•ôKx ¾(ì~òÅó©•å‚…÷çúÏ^®ù=›¸W=è†nçOõ­yzÕf¹Í\=ÿÜ–¤¸÷­u¿s†à‚»1Cíw¼/tØéeÇ>>iizÚ,ûXªçîUQOðCb6Ãä8˜Q.Ÿ<þ³çÖ³ìïOvÉì&·&=1[¿ ´|Qàå=â—´Oåü‹U‡Mºì¬úÍìÓ)ëíåû¡>ÏÜ“ú¬úYõ›t?-YðKþMè#9ë©„*s šÞúô6Ÿp–½St6-ló®yuüÐNðIþûŠ¡Ôæ7ÞØZþµ÷²Íî~5/ä(˾õVAÌ[͘ƔŽN¯dÁ'ù&÷m²Wiö-PvòŸO†±Í]Þ¬Ùq¹*OšŸ6}™âÀ©ãÁæï²ÏU¼PÎo±¯jR|*ÛÜ}ÖzOýuùjÌÃ,9Lµ‹ÂŸÑù!YðƒyÖW÷Ö-ç×8üãØ¶ls¤KT”Qó‹rê(Žê‹ù6E!›…ܱÍ[c^˜ z¿ÂþÈáÜ›¬ù©êÇXÐAð‹mÕ'÷›\Ù\¸’Ù“mvI\ëPûÝ+ùÙ-9ÓŽM¯óEãc~G;Áyã·¯-ÙÜV6o¹{4|co¶¹§ÝâîR$ËÞ,åŽù&RóK“àÅZ’ªÏÿL1ÿ'¹wáü$ê9éEó¢1м_jzv³èW]—°leY£éÇ©Ššð<Á'Ò…¢Í§ylxSˆŒ¦'Ó;µõúû:r®ê÷Dö&úLY´º]YÁv}~g >9~,¤ý•ñ^²¹ÛÐó#jËÒÍ+§={?êŠJŸ³íö~Ñ^ó{¦œÿ5/ìËu*\À|r¬hòÁävÍes°ë¯äÏØÓÇîÖu—éöùž¯ÍÈÓ1lªÐ3h'øâè7¿~½â–l棙ž ÷$ˆe?ûfÕŠÉ_²¤ÚUNHÛÞA}ÁGÅzU6Û5ž²¥¦ßد_×5Yȶ¾ýу®û®êüð‡#×Àlêºæ³Î÷?Ìüp„ÖåŒü¥­ï=²Ïzö£>äù’žKZ2t딥Aê³éÁ,Á‡¡¬Fv™­ê%™¥:B“¦hö`k†Ë³ì*_Êî„×´_å,O÷RýÀ|qèö)¹nm™µœé²`Ûz`÷‰ëï¶×Ö/Ù'½Pâªá5]q&éó7KðÁ!¾´TÎãÚpôx¶õ¸t&àÖ-U¿êò1KÌ÷Á– ïÄn ’ó²=øÊ„m=ã³»é´!,;#pÙåÓGu½:KÌó®gÿšØ¢šœ7Ë+Î4½ºõ^Ô_ cZèô_è—w6sKúíżï>§ñÍ^´ùËë_Ù°6éÛV«õ®£t¾³#f™ä¨ùWSŸ­Î<Ø£—¤ê£Y‚öñÙêÐ[“Ï¼È -3bgjv}[·u}l¯Öížb~&³©Ã þZŠº<Á9æ*Q#"4½çí^#Ùº³6Îm ^‹¡:žQÖ‰J¦éò9³Îýº1Oõù-ø%{ÂHšœ×ØöãJÓ<ض×v6Éæ*Úz[å“ÉÂ.ë|7[ðIÖ¶î©Éy5?ù$$öÛ>á蘼Y/u;\oøuß¿É& ÿí_쩲ž|¼œW%¾Ù…®[4>Ý^~´ö­í×Yv;R`“׌j¼r:[ðÇî-µ*¹´K”ójür=Õq¿F‡ýâï¨wI§ƒm''¨T6ùiá[VœB{Á/»j§qLÎkw¥`u­ß…³Ú`‡u¼«|fûã·Ý´uÏ,Vü{¥YÐQðÏg+rJVtþQÎsýöËzg'jzs§Ë‘ò=s·jûjÙX¼,9£éá)|X—éöl¶àŸÕú›¯ýÒQÓÇyîw‚mÎc;'Mþìžm²Î7®s+­hú‡ºÏǦd-Èz¸|ºîÍü³}ôý5Ík«ø±û¶n¿U¿5ËöÛõù¯ÒL}|_=™jέóóÁ'[ßÙÀwÄä<¬ž_œ]Èvr7]úšeCɦ(ËÓhÔü>ÐÅzÉ©yrwsœ~Öøþ³°WOJ«ÖÕ÷ïn—¿=ÓT¥Ú ¾Øilx±ªœ§ˆá8öÙš®OY£¡Ú¸÷¦Ý¾vf£ÎOs_løligÊ'ržã­³Xº° ±¾`{'nüæBÒßùº}ÿõß®Ðé4GðÅZ±ñ¤ê 9ÏÓÿØÔ€_Xƶ ø,×ö[Ç û¬ïËÍ|°B M¢ËiÇ-gÍ‹j³]C$óÙ8m¸w=_hjûÌ GkíúîñÝÎÏ|°‚üEÈoPý½Ý¾“äÞxjüNûÚzjÜæ¼é%#Ôu+à ~XÄW9õfèúŽö«wÿ~vð»Ù^(É!ßÔôÜDÒßšÜÏ|1›«÷Oå¼}M&6sx—eë÷yážµú>xn¯GKšåªôF;Á ŽÑ¯71IÎSÄ!˜í´ ßÕ_Ʊ½XÅmËjˆz °Û/ïg·ºËy¯|÷úLÝÀöˆqèûpk?ͺº:_óWß™9öìÃ$m= 8 _°)ÛªôñlP íS3¾ûÔô{–Õâ­¡;·wÐñ]бÖë3ÅÚ:’Øç…?Ø‚ |c&Z·ó à)ÍcYb}¡ÏèIÐxõ´ùMøéó;WáVq~ùíYìâ‘Ô ÙÞ/«ÔüMÃ'Ö&¦øÚ+üÁV+ÛÒoéø|oà;ÿl¯ô×Á”Y:¿y^IŽa‰bŒö °u´¿¨ù}õ7¹ù0@“×½]ùg£ùY{…<³‰Ÿom¶m`¨éÁ<…XšÇ`ëÁWëhx˜ÛtÚ?þÅ ¶7ýVV«¸,뻯ø·ÞÌ&l°Ÿý©Ú)|À6r¯¢êÙ çÐy°™e×øiÇYŸÅÚ|d•ö^ Ó®ùr‡ì3´ü±¹>gÙÞ?°ÚŸKÉfHã¼îšŸŸóIÖ«˜‰×YÖþ€Zý+7dcÔýÖy‚?vÚuQ¾ù¾œß¸éš#Vc9÷.|ÜÛ3Še™Ûúg6FÝ/'øá³»Š#­¯ëäÒÒ±ïZ±}.ÇöêüËû‰lìě۷1=˜/æ_Ž<~OÎ~³µ‹íë{fÉÓI ,ëÄ®„FÓ.hüŸÀ7ȼuû2_ðAæ¦Ã‹W>¬®ãåü…Ê m÷Íëß9dò /ÈßPíV‚ØÐý¦ù‚?²šä[}—}AÎß9Ü;¢r Û·¥_Ï“YÓôù\ufð_•ÈÁd?U½¤Éé|Áô>OÎçÖrN‘¦÷™oyUÚ­ÏçœIðˆ‡hr/Þÿéö}¾àu=VPõ@ïŒö ؾòÀ]q‡_êx û¨peåL/øíäðUVß/å‚NŸLm÷ãq¶¿Ê÷×doÔä3Ëör»ACØØü“;7•£àƒœBåEŠ\ðþï_ Þ×üýÂ/e{—ÿ|.j#Öéh'øaßàQ™¡³4~(öZÿþ%GïÍ2hxïë`9„o vßÅF*n} ž)‚_ö)Ë^+Íï, ã”æÇí¿;ܽj§Žšžßs6’'· ÷Ã7•Oûõµ+à þÙßíVúÏõr!Ùeu|Ú¸ÀÆýŽŽßënO?”­ÍOœ;Àü²ÿtîê«;ŽÊ…7¤UÓfúhû;ønFK,+ô·{ŸŸwÐäu¸ºÞM|r Û”↳<ä"1áì ýëíUư,åubS•ïÙˆÆ_\ÒbÚ ~8ðC¥g¿/3ÉEƒ }Wl,eǼsæëÐ|–Õ6)j}›b6êý¯Cûõühïšž®så"Á_ìàî î6¼¦ó3ßIŸÌF],júùV'´|pÈKYÈiû©E[½š÷F÷ƒåŽÝƒ†žÐÖ»YUK°r¥é»QžN^±ô:à þ8´=ázHàmÍß)¦÷½‡jíž«ÓýÙ¥¯Ü¥­óGÞW6ŽM~8,ÞÃËÅÊôÌÔøëqÖ³Z;iøì¹q?ªÝ ÇšþÖðY øàpñÄ!/÷ÜÔÆW,ö»Ø¡¾VÁ}¿ûší9r¥Aû£ØH±¾A;1ïG”me}ß³XÙ®Ç%oÝUóŠ»F×=|Þ#òB(Q0käÔ wÑ^Ì{®ßýõ7¹Ë§\¶\;¬ˆÚ"Mø~ù÷lOÒ«¾›×-aÃU?u˜ïܧ|ö‰†ç©M¸ed‡.%'ÙRÀöŒåŠx‹+w­ëùç´ó~4í+ɧÎõ‰kËW®z~nc>z?b¾-›÷Í×£µuèi¥;Žêš7®Ålm=¿g’² ×åk˜çãÜ*ô<¤á{úþ">Vã›Ã|òj>ß3>“óŒÅšñ§Öš,ó|œ[;“¾ox𝯇|ÇŸü"¤øæ8ÎÂÎkzd°²,í8bžO„Û.©Æ´÷ªrIsß5ùë;¬¼Fª®ã±ÖùƺÁX¿·” .´ó}‚¿Í«ó…\2¨ÉÛæ^ìHØ™¸?c{¶ûþXg]sÖ[ìg¡¾˜ß“‹ëù%YøIvd|µ‘ýö²=€âù`&ë+ðE;1Ïy}¾×껥òë»úº˜Ù‘uíÚ>A}ÚoSßóǪçDŠyfÜ-üä'ù 'ßšß4}vd_Ä™ž çTúÊ¦ÃÆ]õÂÒٱϊöbÞí©t>óé¢o<ö~§ÉÕ‘âØQÝ~Õåê­Iß ~(w&ùò×£ª]g4Õ÷ó >0wšºàú­_ä3œÍ éx]süío¢JÉîqR6˜Ø`¬ê–þ`0=X$æß¼†oDô’ÏÒ>û‘/o¾v g™Êk"6PØ_Ôólælèò—þþÞ¾9«2˵ùtþþ±§5ü3/]aÕ>Ìd)oÀÑ^Ìs~ø“k[Fèçêšúð@<Ë5¾ÿ°(ð#ß2_²ª_³HÌ{>ù*ÏñS:¶MXnüÙ¢ƒ¥4>ËÜ<ÀgÿíýJÚ/¦ó3€'ø¡€ö+Ôñœ+r¼àh–›uçyë¹Ã¢žý23ŽËxÔI6Õ÷ÇÅ&±¬ßÙ é­»U×íè"Á' Yí Ï<ÑöûÎ7õ¹=eH”&—¹//޽>¯fG3¯ï·nòð™v¤ŸØG³ÀSðOAQñ†£™Æïçi]ï£ÑÒo½*_Õüê=ʶf]Î ~) ¼uè“Û:~Wv;@¥±£ï=i;þV#ݾwÍßôjtº¤<ŽÒéþi÷¡)ßl`™Û:`¥ ýþüí=o›,|R̵mo¹´ég9>XÃŽ}þ•máÏå,S¬7Ye™ë†ú‚?Š/p‡»Š\: Ï¶*ý¬ñÇqÇûÃJ»Y¦Ø_ÕæaPÍŸÀIƒuy\"ø¥øñ¹Wõ%ÉÊëú]¾Ç~¼³ZQ?ÿЩVYuËüÔÆþ›™kØ ¾¸a+Ú þ8esd·GY™¦¯J/pÅÍŽï¸üžU ›¶oÆ€ºÝX"øà”¿ö“?·3ŽókPEÇÏåa5o/Óço_v«º‹XÕZ"æý”ØïÒôÓç=uù=º¿ÆO'ÚÖ9µtøÞ¨3*Ÿ»wáöµÆOÝÅ>à‰ù?záÙ˜còçüµîÌÏ4zœXi5së¢î,óI—Ký–°NÚ|ˆù?ŵ{,~™;äú9¥—” G–ùúàyª.ê~žéÁR1ï§¿Î÷-k,_®”5áPKí½ðÉS?O}nÔý±ïÌz\èÊw’Ñ^ðÁ©¥×B=äË|w3¶';i:Ÿüà×],óÑøöPÕ—¨/æûÔ¦ssÛ&ÉòåµÊ m½prÊ…9¿<ÿF[f^­Í=~«ÊíRšïŒö)©1›äË¥Ê>ߎLÝ¢í7f®êÆ5ŸÆ±êºp)Í;g›ëŸhvê ñTÓG'¿¼8À!µ–¦¯3;|[óíu5ùí«®–(¯¯È_È/ó/rZÃ+V*CçCqî’õRõôRš÷Çö­|;MóϾPŽóUÖè“—˜3¡ý/¿³ÌÊ ]#ü´§ùÉ_ìÏÑÇ#üQòN[Un~ò¾>ž@e"X/aõõö2Á§«ÓÌSLJ[‘Ög4ú0§N?ºöÑåƒc\¿FÛGí­ž£]&øä´ó’}K¢wËW*ukqóÅ]Ʀ®šX/Ëe&ÕÝ~y¿Y÷— >9ÝñÝËÕ_ÉW‚øÁ¤V؉-U>Ý1E·ÏB®µý|½_Á/§‡us/ ÕìÑ嵑«6?ìy‡ò›éçt¾#=­îãöVÄBRë®àŸÓÂ¼§lˆ0³ÝþDße,SÙˆÒçw™àÓüíRÏ0žW˜r€‰™Åؽýø"·5V|WY/e~(Ú ¾8Mï·Ôy½Â½ÄšËµ}³KÕ#‹Çøëô ú÷~®o— >9ݯó æÛó4ÿâªgÛÞ‡õóf׿l—GÎÓ顼†vÖæµßôåüä—î禿йÀ«”ÎY|Xó§Ì®E1žË%O¢¿Ø?ôˆîw¦„,Ì[™?N›§«$¿ê¼›]Ýa7ÞM§Ø§Ðí²8Ÿ¥ÛåTâ#§;nH«ä«feu¼àÜ7±EgçÅnö—hç) ?‹q?ÙlÝ—Øó¹|õ™rÀPÇϯËÀï›À2{[ͽҶ¥¶¥ÙådwÄ{n_®ÓúOµ»æ÷†$MkrO_ïLÅ=Sm?I[·,'»ôQÙ¢c!äëqN°›Ô—™7‡Ë5ö®`™üx–)Wå{Ô';4ó8ßÚÖäè:Õ3‹u Ë„ßlf¼¾¾]N|0zÜÉ{¯Njû¶×K½·Ü=ÁÌGݶnšñ±¶̤ý4•ný›õœ>¬l/à_Ä4ƒ†:-—)Ó?Zç‡ÂöGjý2F÷_Éî«÷;†¨û÷ˉ—ìúü'¹¬Mâ¶ëÕÛ0ó¥¯ú¶ïÑB— ~;¤jK6\ÕgËiþ›( xþeüìÆ:ý¿Î==¤ëbÝÞÿM;§3B}ÿ¹‚ø€¿Æ2\–Ë”mqG}<Êv•“n?}Þþ´±íÇr+êgxHå‡]ß­§û5+È?½åZúm\;m~Ên*Ëw57‹Oÿ‚íæË³Y_±¡|ù׿1Ú‘?ÊW Ó~ÓÆu£¿Ø±WW¾8G§ÊÚ ~P¶ÑnÜ×ôûöãßßX§>ËŸ[6åçÖÛôuªrìÙY;'5H¬+õ÷!+Ÿwÿþá°Åç4üoð·j•¨všåïýkÈÇ-º^⎸µ¶?2Xy´qÞš¼yþ»XÁ'ëJoÿqJÇ«jW¾£«oÑç]ðQá¢Kþ¿í©ã¹Åõåµs}XÝ;Él¸½–¶ú{†w¿*Ëý½ï­ æ|´w +(M9<µè¹ÎÞöµ fdÔƒ4h/ø§0””:¡ÙË[\ XoT×é¬àIÇO>ª¤¿WÊtwN̽dÖäUÆï þ*äÇ[eÈ·Ý—¿ÕaNsm_£Ð±ÊWù1iº\мizó]ÁGÊ1®v=n {£ù9…ÜÛžÕƒí~<ÒiE5}=4ÒÔc{F~?À|S°þÛÁϻȷç»ïë=¬°ßë¿$.»ŸõÊ_{à3ýýÁ»‚o 蜋J×ÛX•lÞ5N3ä”þXÄ”×—jþôhÕŽ¾+ø£ @y*ߦ{‡ªZ(΃1±ýЇQíß»‚ò>ÜqËÙ|ù޲b…™ÇFŒyÍ2º›ÜD±ÑâÞœéÁ{bþóŽx÷díŽòv?x§€æ/üaïÃQÚºNõ#G«û"ï‰yÏW®=zhô½C÷‘´yºÞá—/ú÷Õç©‚>Œï»bã¥+ ðÄ|çû¤¾¿´Ñp^wöFMxkà4VøÔíÁúUt>$;9¡½ÿt‰öb¾Íübãj:>Ê6M˜Æ‡Eö[†ˆ˜Ç2Û(/¢4{ðζ-ÿˆì 8b¾Í‰Ž /…¡Ñì\QØž¨#çjãÚm^TS­ùOÅ{jÀ|`û×ò—Ýýüàr°¢±›Nw[ô.Û=¦6Vv7Øxõ}Ä{bÞÙ'Ê xùËysl¾Úø+ÚÔ¿æûŽl÷°Åg¯Î.a b]‚úb¾ó~à¯VeÍßü’ŸÚLªÇоh°ËÑFÓ#»éüŽÊçñê=ƒU‚ò¼â²ü%{Öö+®:¡J²ÙšŸ¸[Ù&©¿'W÷×W ~8 ¯–E£ÿ]ƒÒ+yrîj[ÍÛMçÔõD‚zïm•àƒ“ô~SÏÝ®næG5¾*xþå“_N³ÝÊk¶®Ú{À„{Víå€#øáÄÆ÷úüvI[Üå·'êëç‹ÉoPýÈñªŸ±JðÁqE=Ì–ïnkÄß³â÷ëÞ8`…¾_Å·5/ýÅ&ª~Ò*1ßÇÅ=Aù.ߊÀŠ·ùÕþ¢í`–I÷'¾Z»ü³-ê‹ù>Æ©0§‰|÷å£"Û²YñÁ?¹§Ïv?Ù¿ûë ºž_%æû(7ßsS4?¹\œwÕø³øÔïŽ3tzý+Ϲúy5¡o¬Ä]þïMŽæÿÜøúü’ÇrŒ}5æÅÿ_r4óñD7´Ub¡ÅY)ù˜¸+q0â)_kÅ.O¡\EåÊ&|0§•ñ\Åòµ‚ÆUËÆ@ñ®VS¼+Å0Gûª¹"æ§’ÏðmñlûªÁ[Ä幪™-â˜ã¹:`TAq¯R(Ž9ê×L¦¸Wå÷*NÄ Uâ^…S,sŒ§ú³÷¦¼­¥Ëø8¤Xľ2Q<ó2W´v8åmEßuP¿ä£êÖ캀U°ëæR>¤XŠeŽºõñ\ߤǽRòµÆ[ļr ˜WÀÛX ¡w&ZÄ07RžVÀjŒ~ÏÆxnŒòÆ/(vyšE~VÀjºšò³†‹¸åÍÐo³ß 4qBßN/,r²æR¼rgŠWx.å”5žb•—ZÄ*O1­”8å1"g’;ž=€«xÀu=˸x¢Ìe^ÀÅ e^(óoÀñFßÞfÊ© \|€«Ê}Qî‹g_<ûWß§”/íý@[‘Õsëù @_€€çô<Q7uƒœEÞ£ ”¡Ì8FÔ5š)ÏQ´ˆ#ËãkñXèÁè78Yä-UbÉÆP\tôbù¦B2()èÓ2NÄIçñ³B+´ }JyKA»0³0J,tŒ'Ü,rBE <m#ÐoÄS‘«”Ç~Ub£c."ÑWd¢ÈWªÄD7‰Š \ÝPîn \çi”ç¸z€lôQõk‚§jæPŒt<Û_;³P)µ¢)Fº™òb›(F:ðqð¦¼wxvÝÛãqD_Ž÷E|W%W6Êk£~#å½+j©.úª›Lyï@·zñ"Fº’;Û@yï€gý2ŠŽq6 ^P.mŒÓZ5D¿ £)ß]©PmJ^môÛý6FÛÆq"×]ãÊ­ç&9îÐwÓ ¡•whÛ °œÛ x:%‹8èÎxvÆQî\"T¥ Ú» ½KåÕ\“E V%:ÆâGñÏAcwàãŽúîèÏýå±Í<@3OÀòD™gå¬C™ðöB™7Æì¾½1NoàæÃmhäƒrôã‹g_<û7_³PÏ~xöC¹ÊýÑÞ4óÍü/ã H¹²ÐW ú DÝ@Ô A wê¡Ì8FÀ5¢ßæ _óXÇ—çÐã¹ÌƒQŒçà2ÊI/â®·x*â­óä!è§%ê¶D¿-“EüõPà Ú„¢Ÿ0”…™D¼õ0”…(gp /9Â#Ð6m#Ðo+[‘QÉ¥W"ÌI$ÚGbL‘¥"æ:ÏÎsœ·üÖÀEÑÙüŸ¥]­hG+ÚNn7-}Z5˜š;Ì2±¥ÝSm·aÜn©6«¢­âö‰Û#no¸áö…Û5nuE;Âm·ª°´ªîÿ;½Ïõ½ªë17ÿMÏÿO:^ÕïÿŽ^çúÜ2ÏŠÛÒ¯µÔÉ\[æ³ôaAs%oæ\*¥\¡ ¥uÅ .·È'þ²Áwð¶-øÄón‹6ÕP§:ø¾ø¶FŒÈ÷Y|R3Wħ¶3QîÀtŒ£|€Q‡ë'ðX]”×M¡\å".5ÏÃÀsj66RÞÌÊ©€¿Íl)Ÿà9¡3úuA™ úsÃxÜ n¹"ß—sôã‰rÏÁV^øÝcöFÞ'È€pöN¾hï‹g_ÐÓýøá»ÊüPæ6þ ƒ?h`,X(Dyú M‚Ì‚U\†{0ð ®Áå”3á…ˆÉÍó\†Æ‹–aø޾Ãñ7e­DÎÑÈ2ÊO‰6oüW«7þkŠÕÿõ“ÿêMsÆñ† ÿ—͵JàÇJ)"î¿’×ÏÖ ±õ}Êëg‘§9œò`ŒUP×Ï6à'ÐÜ:©ªQäPò5— U`kÂg5åk†ìWÃs5ðX5´©˜ÕѦz2å÷ÁØj§±"¿O ôQòTíkš…±Ã³p°µÐA­‘€çHTò6ãÙøÚ¶ƒQäLt>èßý;âÙ°omgÊóƒòÚO)oójÊÛì`‘·mëŽõEþ‚z÷Ež%Çúª¼€ P·A‰PaJ~ŒË¼VCàÕý4¬F¶"£’×ç>åiާœ>¥”ÓÏMr-òù$‹<ÍÍ«Ú6CÝf€å„g'à턾J)73æÌ°ñ삾\ÐÞ%Ù"/3Æå š¹ž+úw®n›ÊÝ@swÐÍÏî9Bµz ?‘³ÇÓD¹zÐÖ °½Pæ\¼PævÞèÛ}{ƒ&>‡ž}@o”û¢_<û¢Ü¸ùw?<û¡Üåþhï¼ü‡?ðž˜Ð4}¢¯@Ô DÝ à„² ”Qf#àA“æè·yœÈƒÉssˆ¶ÁÀ3ø¾È£­ä€Îê?ý„ ßÀj œZšDþ%ž:8‡‚¡À) eaÑ"OPʽ)Oà†ã9åh¶è·•AäpRòá9xF¢}$ÚG–‹œÏ­Wk<·6 3c%’ ûj™[åïìéßÙRÕn‚š´´Ü.r›hi¹¬hû*Ú=K›§æe±´sÜÆU´oª]³´iÜžYÚ2ÕŽU´ag³,íUEÅíÓßÙ%ÕýO6ÈÒîT´3ç7ÿ;>3·!êž0æö¿Ù ÕV¨¹wÿÎ>XÚnT;Pnõ_u?×ùh.K¬´¼´•œñ1 7Ís` ü­ŸR®¶ ÊÕfÂum oU¹OyµM£\lùj¨S¿WÇ÷€W#G°`M̳׿àÁZ€]+•ò¬á»=`Û£Ú9€Ž€åYpDÛÚ˜óÚø­ö Ê©Vf‘K õêa õж>xµ>àÔÇØÇ€m@™!Œ±!úi8Ð#ÀlÄ¿£]´k’(òÛ7~MM"Z3ôß õœÐ§úrB¿Î3Ú¹˜(o} å8>nÐn%”›ßݹ?2Àðàß¿úö³ðôž>èÛ´õÁ8}ñ×}ø¡_?|÷Ãoþ€åŸ*ÜÑî·£} Ú¢,ð‚€›¿Sso‘»>8£Np†ÈOÇsœ…Уe´È×Ësœ…‚6¡À38‡væ0ì¾ÈG:€Õ ß#?å‘(o ¸m¸‘æþ<ÿnñ4Qü +«Ïßãëÿßûúÿt?ÿÿ_}üÿ¯ýûо}4ÍEi…|å”:²Ê­!£Öqø€.Ö/(¯W"åëÄsÀ©²šòuŽ àØ€?lr„ˆWÅsÕDÊYümÏ6†rv§jãj€Q  •Ê}ñì‹g_”ûO?ŒÃÏ~(÷C¹?úõýü1ÆÐ/x`LÀ+}v`®PÕAoÊ‚PfD™eFŒ×¸ÍÑOóx‘ožç =‚ñ <ƒÑO ôÓpB0¶À -B@§–€Ó2Fä±o üC1žPŒ5}„¡, ø„fÊ”ï}†ƒ/"PÚD múl…±µBy+”·ìH”G¢}$ÚG¢~kàÓšÛTŒµ ·{" «LK›iéÇ«6ÒÒ>rÛhi¹=DßšäªÚ¾Š¶Nµs–6ŽÛ·Š¶ÍÒ–qû¥Ú®Šöª¢ªh›*Ú£Š6èï쎥½ávÅÒ–XÚËsª½à6‚ÛnT;`釫g2,ýoU·ƒ¿ÿËžõÿéL†åY UG[žÅ¨¸ï¢êä15§¢’;ýXcž­¹ßyªœ,Ø¡ êVAȆM >à᪶"7²-xË–ÿoV‹lS=šrc,5(÷1xÙüb‡>íÀ—µÀ+µÓåö¨kŸKnøÅ1™òCnê~t°ÿ z&|€C}´©™j€>ŽÁD9Š»!`7âÈA£á²5=š@®š màÒŸfè«ê4Ë¡\Ãèà 1vWàê ønhㆹwm<ð›ê{bìžxöB^xöîÞ pöE}?ÀõÃü¹Á 0þ@Ô B» ŒÇˆvFôiD_ÍÃE®à`ôœ+Ä¢à„ ¯Œ'x·^-K(ðÅøÂ¸ì£]8`D`ü(oå-rGv$ú‹Dȧ”ãcmc ^àÿþ ûâ–~ò¿ë#s½QÑ7þ§íÿ“üâÊþ÷?Å/þ§í}s=K´LÉ–râ‚vÒSÊe‡æ¢p²†lYƒ/¬s…ØV†¨œB¹oѶ `UI§ü·S›hÊgo¦|öx® ¾©Êýeàhë@9ís(Ÿ=ÚWCýê€UërþÝLùìK ô]£„òÙƒk¢;´±þvå”Çý×Âw{Ô±G}{´w<‡h‘s×}9<yì»c‰P%µñ\õkc,uP¿ðªÃýâû”ÇÏuñ\z°`ÕnõÌBíÔê£mý¡‚§À£ð0 ®!Í"w=ðnˆz ·ž§F¹”³uã¹qå¬7áƒ~š`œM1ž¦ IS<7Ås3àÛ,™òÔc°BA—PÐ1 eaè7 c ÃØÂQœÂÑo8ú‰@yÚF m`µÂøZ¡ŸV¨ß í#g$ƉçH´o ¼Z¯ÖG´Ulÿ÷wç2*Ú£åÃþ+ÿÕÒÎü[Ñžp[‚±þ~ë¿ër=k¹léþ»~&p‘€£ÚJ 5èe Ú×àðKM<×-íð×ô´ýkNµP߿پøÆ4wï:¢MmðCüV㫃9©›#܉z O=Œ©>`×ÇÜ6Ï4@ÿ Ë€2—_´mŒ¶1ïMÀOMP¿ æ¸)>n»;øÒóíù÷/{âÙ øxáÙ ¸x®?`ç@Ô ÄïA¨\ŒF‘ ¼p AYÆ‚~Z¿Pà ø¡À!,×êÍ;÷¢ÏñOÚû§ùꟓùŸÇø<±RÎZñÜèÏðùŸçøü‚ϯøüf¥¼—·úŸ—ø¼Âç|^ãó'>ÑÞþ“°¶°¶ C%èP :TªJþ ÖÖtª*am!am!ÙÓýf¬-$8ÖÖ”#/5 »YX[HX[HX[HX[HX[HX[HNtæk ÉT ë ë É/:¿…5†#(a!ÁàIÐétºDgšãƒu†Ý.a­!A¿KÐïR½¯‚Ž—°Þ ç%¬7$èz ò/É´¯ ù— ÿä_‚üK ò/u¦µ6ä_‚üK ò/Aþ%ȿԓ|?È¿ù— ÿä_‚üKié^È¿ù— ÿä_‚üK ò¯Ü ‡üK ò/Aþ%È¿ù— ÿʽ6È¿ù— ÿä_‚üK ò¯œ†üK ò/Aþ%È¿ù— ÿÊÙ7È¿ù— ÿä_‚üK ò¯œ×€üK ò/Aþ%È¿ù— ÿü]Ÿù— ÿä_‚üK ò/Aþ•=aÈ¿ù— ÿä_‚üK ò¯ìS@þ%È¿ù— ÿä_‚üKÅo†üK ò/Aþ%È¿ù— ÿŠÝ‚üK ò/Aþ%È¿ù— ÿü^½ù— ÿä_‚üK ò/Aþù@ ò/Aþ%È¿ù— ÿä_‚üóû-ä_‚üK ò/Aþ%È¿ùçg±%È¿ù— ÿä_‚üK òÏÏJ ò/Aþ%È¿ù— ÿ䟟…‘ ÿä_‚üK ò/Aþ%È?ßO— ÿä_âòÏÿÅ[ýgÄ(¥ý÷h‹=$ºß˜lCÀ$ÎO*÷h>YÄPî7šh)ƒâè]oíÁ‡St‹óá1t¾&‡ÎØÈ?IûñJ<8:#^B{òÞâî‘SàíÉ'Ó}Ç2±G¥¬E2,ÎÚ„ÓžU:Ý{´µ8s“fq÷ÑD{ô´6±¥÷ǫž¿Ã¤¬Ob)Ö@:í×{Óeµ¸ éT.΄j÷!s(怑ΉfPÌ:KOë•\Ú¿w¦Ø‰Âßâï¡•s9δvI;’tO2žîG•ˆwÓÊ}ÉXÚÛÏ!Í–ü´XòÕ2Ä*e¯ß@ûý±´ÆI¥}3å±¢÷ÛÑôŽ;•Îõ”ÐÙzCïVS<ƒRŠià@k¢zG°Z¼ç1”³­tþ'†Î°§Ñ9öR:ïê@÷¹È¯TÎåˆw Êý.ò5£éz2uÏ÷¾”÷ë:÷#|Qå¬l†8C¤Üå´¥õV4½Hïâ•ø åCÁ@{‹eCÁ@k®xº×™!Ö^Êý1+:Ÿ&â&ðóðÊ{ˆ\ÚoL~.?{«¬¹ ´ÿKgàWÓzË,Þß+±lé>§íI†Ó{ˆrZw­¦;eâ<<Ñcn£@—(ÀB¿Qè7 ud´•ñ› ã4¡à˜§-pi‹vmÑ®ÿ`|í@vhÛmÛ¡m;ÔkOÔí<:~ÔéúvÄu®@ÏNàñNø­`tÍ;aLÑggÀê X«3àD÷hàÞe]PÖe]PÖe]ÑGWôÑ ãéÆÿïn¨× õº¡^7Ô{‹P·;êvGî¨Ó4ï:ôÀ§'ðé |zb<=KOàÒ¸Ä`¾b0_½PÞ å½€o/Àé 8½ï‹%CüÞcèƒv}0Æ>è·/ðí‹ö}Ѿ/èÖmú¿~¨ÛsÓpúƒ&ýQ¿?è;€@ãXà‹úÑß@Ðx ðˆ:Q>0£í`à84Œ~£Í`à1p‡‡¡À{(è4´L,QÞÆïÃÐvð†qƇ8Œ?}Ç¡î”@» ×Ôz#Po$Æ7 ýŒB?£ÐÏ(ô3 x^c0Î1ßà:¿ÅocëXà:¸&F`ŒŒqç8Ô‡úãPêODý‰¨?õ'¢þDÐib©X %ŸDà–ÜÑg"Æ68O-K£$àž„ò$à”„ò$”ó|ÙÉèƒç‹N.Ë¥™€ÃóKß’Mþ'ì¿ÿ§žUÁ<¾Y#[½Y#¿Y#¿Y#¿Y#¿Y#ÿÖÈ—ns­þ3b•Ñ9‹‹÷rº?b(šîŸ”S¬½hеg¦ûÓÑô~.‡b9ÓYµ\z?g¢X@tŖΫ­1÷”3ÒÎw/…ÞÕ9йtŸC9¿a¤ûÔ¹tÅDg§s,ÞÙ%Ò;»r:Gm¢÷v¿ÃÁâ[:Ý­¶¥w)âN¤ò^Àγ¥Ñ;<+ŠÇGñ‚2舑bò¥‰8 Ê™ƒE<\ŠN÷Ur(nî¬$R|>3!ñ¦øAɧ¯”λyS¬¾qæM‰b "‰t»”ο)®Pª¸Ó¢Äïs ~qÇ/‡îg[‰;Úüþˆò-Nœ=ãgTxÜ!å ¸-“‹1IøYpçO9/gçXøýN~/Ey_˜F1‰Ê(.‘âÿÅŠ³.ÊÝî\§H¹#c sä±t4îË”ÑÝûŽ¥8i¥Œî€E åì]Šˆ[¢Ü5Ó½p+q”Ç<á±ÛyÓ=›q6O‰Ÿâ Þã(wÆSÅ9½vF=Rçhºƒc¢ózVâ»_0Qœaï#Þ)÷Çmé>jºˆ}Äï¥*çrÌôn2]Ä +§ø‚Îô~2NÜUUÎèåˆ;ãü¬žÿÈAÜ·éKï&Mâœ^—8Š1˜FwÆËÅÝV~ާ €·^Q MàF¡ß(ô…:2ÚÉøÍ„±šÐé¾p·Û—¶h×íÚñ§hÒmÛ¡m»rÝ€GÀï€:[GÌQ'àÚ ôëºuÂoøÚ4ï„1uFŸ«3`u.n|4pî]PÖe]PÖ¥\¸ö]ÑGWôÑ ãéÆÿïn¨× õº¡^7Ô{«\¸þÝQ·;êtGî ywС>=OOàÓãé \z—žÀ%óƒùê…ò^(ï-– ½§h= púà÷Û´<¡þ0Œwà Gûaïô9øME»I(›çþ|ÍŒvýAßüÇßXÐy úü¨30C,A£Í`à80c|ƒÑf0ú †‚ŽCÃPÐi(Æû6ð{¿CÛaŒ38Ä¡ï8à‡º#P>íF€^#Poê@½‘Àsú…~F¡ŸQÀiú ¼Æ`\c0Î1Àu ~‹ßÆ×±Àu,pMŒÀã@¯q¨?õÇ¡þÔŸˆúQ"êODý‰ óDÌE"ðM>‰À-¸%¢¿DŒm2§Æ“„ò$àž„ò$à”„ò$”'ƒ&Éè#ø'£ï™¨7pf¢^Ôã¾®òïMœ¯ÿ=q¾øZ‰¯‘þÕúèïÖF×E|-¤®ƒøúG]ûð5åz‡¯uþn}ó¯Ö6–k˵Œº~©¸nQ×+êZåÿvÂ×'|mRq=b¹öà뎊kŽŠk uÁ×êÚB]7¨k†ÚZáïÖ –kËõº6*ëuMÀ×êZ€¯ø@õÿ¹ïÏý~îósŸûúÜÏç>>x“ûöÿ¯Äœ‹çX”xC1"N&¿›­ÜÛHÔÏ®ð³iÊy´Š”hqÏÚLw2¢Å %–t²ˆ ÄcÐð¸@<7£œ/K§óþ1t.ù©8ïÄãWò8nÊ]Àâ. \.îÊOÅVœÉ ÎVñódJ<Å\§R‰™ì-ÎPE£ß¶³É(ÎIuäþàÈ&q_€ß0ñ:€iŠñylÊ”Çà÷öfó8e=’…:í¸¹Mî=ÀSí€C;Œ©+èz]Ñ>pº¢¯hÀ}úâ÷öø}€ƒˆ%98N]ÐOoŒ;}Å‚±hËËAÏîO_ŒwÊ ^<¿|zàÓc€º£Q>šû5á"&Èà4ó?0{¡ÿxÀŠGÿðwÚÅ£n<ðçõP>€ã–*¶ ã<àBÛaÀop„6Àç Àç0ñéò.€9ó3íF£ÝpÐb8·ãÀw$êç¾àOÁ\ŒÆóà˜˜#A»ƒ0+ï`.Æ€~ ¨3c™A)Àiÿ ˜Pwÿ¼&áûô9‰×GŸ“s úÅx¦¯é€7÷õB˜)n¾bÓ(´ú$·Qh?퇣þ0ô9 ôHÀüL@ÙÀƒ¿ Û(Œ{ ÿŸIÜOÌÀuÊ€çŽÚO¬îS×I€5eCQ*`ÍÀœNàc~Swþ&Ÿ˜»I|< ï ^—÷zMå°ù_À˜Ø“0¶&ÿËaÖTÞÆ9•ÃŒ€1mfð:ø}ÆS±-; Ñg"êMå~ƘɠE2à$cü3¹^³OÿfŸ>ÅêÍ>ý›}ú7ûôoöéßìÓÿSöé¹ÍL¶úψqZNçäã(Î}ºC1JÌ'*–â”ܧxý1ç´„b•ÄÐYú\Šuj¬'ZÄÅægë•&”'MÄíWî–{SìþջߙÎÝgPÌS+Šm²šâF9ÐüÕ"î)?‹¯ÄïO¡9÷éžy åÉÉ¡øQ‹;6CÊ@ùrRE)%gŽrædˆ³üJCÝ=_Mwn^P<ÔDº·YB÷n¼-⢚).j4ÝCÏ¥øÝÎ"vŠ5òèXÑ}ôXº“žaqÇH1¾SÅ=P%Vª3ÅKM±•Øÿ¶C1žrì˜)€òÄS.€\ŠCeKñM*™b-戸ªÊ]vº¿KwÚÓè^{9Ýåq¦¼ô Ï}윹Ȃ¼Y' ²eA¶,è–ݲ!s.ôÑ¢_ Ùµè×B&-úµèχMòÁ#òçƒ÷$ÐMΤ,vn‡Å¿ßû¹WçÿùÎ1~9G¢ùQÃ܈æD4j˜Ñüçå܇æ;4סyÍkhCs˜ÿl>+å,4_¡ù ÍKæ!4ù¯î¹›rS¾aÊ5hžaÊ-Lùî¿Ëþ(w ùÍh~ðrNðjþ÷à-ÌþwÏ̾ÅjJšÎ]jcÁÏK-`õªÄš) ÎSªgçõ‰µëY­)ñM ~¼%;7žÖ@¢õ¥ÄšR¬!Ý’£grÒ³íèY>ÁLxÂÎÌTSiÁþ_”ž»Ck2Ž)`5GY°ó3éÿ¡ÒóèéÖâÿ[²³âéÿ‘Ò37Ý › ŽàålÎêÆÐú‚ö·ØÙ›ŽÐ×Ù‚…GÿY z%>+ÜXmBoÈá ,m‡=UèSÁnÐÛzªÐˆ>ŒUA6äpĸ@Ü!ÀUa|dt‡ln õ†*ú¬¥ã€ãŒŒ÷‚Ì>x…@¦P´‡¢?’ò…|‰Ð7ŸÃ‹Ï~Ð!ï)à—‚öàE‚$ì–ˆÏ‰à™‚ëp¼§ã=¶ áx‚ !CôŸD×èWûë¯ö× Ì^í¯¿Ú_µ¿þjýÕþúŸe>ó Ìþ-j0ˆ5Ä,Ô­ççS…òóZ«œ=XÁÏÄëáñ:bƒx±Bv¡xf«mƒš“üÌÖ9¼îü-~VU?¯°šŸWeÉkŠñšb–¼ö|¯ÉМŸáZÄÏïÌk㱺 b}\%¯+VÍkÐ[ðÚ sØùUôLñn jÑWðó\-x±BvÖ¸X/ׂ×ÛÏkÒ›óZcY¬VPÏJ^oLÉëma5Äúô– ê6Tóº *~®U%¯;6ˆŸõZÀêdJ5tmyý±9¼Y¯WoËëñ³®ÌxÝz;»G¬éPÇϽRòEEì F±6™¯O–Åk”U²Z»´æ–XëÁŸ[Àk>ìgõŒÄ³±:ó:÷j~FV1;ÇQ¬qԜׂpãç¤ÏáçfU󳳚ó³ÝøÙésxˆJ^çÌŒ×Brcu>é™êbí^ŒU„ò³fñú)¼ÚVGB¬çÛœŸ¹¥ä5%²x}ß VãW¬/aÎkýºñó¸ ø™\áí:^_É‚×ÿE¿e>¯?±ŸÕ Ïê2çgתøY]æì<ÊQj~FW1;“rT;—Ò=”Ÿç^ÁêŠçº7gçÕŠg"U°ºiâyµæüÌZ?ã};¯R¬ÿ{ŽŸÑù7^‹´3;÷ÖJ¯ægXÒ¹ÜÂϬÈaí “7mò(ÀWL0ØJ@›|”ÐS {àØcœ=ÆÙcŒƒš…ÛÎà퀱ë:GÐ9Ñäv‚,žàé»9C~gÈåyÝð |W´¹ÀF.ÐÉ<]i°\å ,7à¸Av7ÈéŽ>wô¹£Ï}èóÊryÒw`x‚Îtž ó^Þ õ­7h¼Aã C1>ÍòøB'_èã Y|!‹/æKUÇR„‰¸¿ü@ã‡yóŽ?püaëØ:ã0.ú€oøb| 컂wxa|p‚Á?ö † Áà ºúm(ä nhÃ@“ì0Ð…¡?|#0>¼"`»ð‰¸ÄÓÐEÁŽQà;Eg4d‹F{ ÆNŸ袆Ôà?cÕ4?FÆÅAî8ÐÅ&²ÆC¿ðIŸÐ&@®È•¹’`Ÿ$ÌGڒЖŒ¶dÈš 9“i^Œ4`¤# öJ}èÓ@?ô Ï}è3@Ÿ;g`.² oäÉ‚\YÐ#ëK²!s.ôÑ¢_ Ùµè×B&í%–.åÃ&ùà‘ùóÁ{è&gè\hŒ@sDúGódSŽLóãßË‹aãWuVÌ^ÕY?þÍ^¿)Ok˜£5üí½)£ù|_̹L¹|J̳L¿¹§y•)ŸúG¹TÃü‰æN ó&SÎÔ0Wj˜™r!øÕæ? sšóÐ<Ç”ã˜r›—óšÿlÏß”»˜ò–†9KÙïïý7ÌQh~ò*7ùßÉM,Íþ¥5ƒÄzœOøù•j~Vyg~¦í~^ßAÍë]âõ:1¶­²AáP^Û ×í±ÃâÙ–àÙ¡˜ÕðÏ®^'sv–¹X;` ¯+\ÍkÖ»±:Ÿ´f­ žù¶ÑÀ´ªæg›âµÞÝxýÏJVKÈ }=«Yýµ1°Í˜,^£á¯Ï…[¬¦ÐH+¡MÙœÕ YÉÎMí{‹Õ·§5viÍ1z¹b«/Fkû(ÀW;ÙŸcu l@«DÛô9C%©ØY«ƒ¡›’Æ‚x)é5î-gwßtŽÀ±‡}}Ðî^*Ð)0väRRZðö¨f5”èwž3ø;BfÚ1*Ø=|ýçŽ>gÈãýB¡¯ :ùÐú\AãNãè= XᘣpððV x¦€Þ ò¤Óï‘ÀH¡4À„ ‘´X‘4–þxOÝR(Ý%vöz ä Çu8èÃqì(è >Q°O:t ïÈy1.cbëþ‰è׃_,pcpоÈ–ƒÏ9+¸9°‘úè1F:=ø%; ´9˜ôy©ô>©àãs@›‰ÏaÓ‰èËn&p3Õl7ãsA—G_4^„­ò@›Ú<àb|0 1&v Æuô ½Äña  fú Cú#€ž9óAãCŒ‰„M¢€Eíy£`«(è ¢ÑCõ‡/ªA§5xªA«†~qèƒ/ÅGèâ@™ã![ø$€Oø$\b¡E"äJ‚Í“`¯¤K,ÌHF[2dM†¬ÉÀLF0Ò€‘{¤> ôi—X8’ú Ðg€>ô˜‡ ø@äÍ‚lY- <³ [6dΆ̹ÐG‹w-úµI‹~-úuèÏV>äχüù ›œI ›TÀê\ÿQý+úìÿþ™ïS^}—òªþÕ¿ûw(ÿ¿¿?¡ñ÷ÿËw'ÿîß›üO}gòêû’ÿþ÷%Ô‡ÝÌþ=ê±GKøLKðh¹Ÿ=ÎZáºU ¯CqŽŸ;ŸÏëP˜5¨µ¼…×û´äµ–‹x- ðhëÆk-ƒ»A¼ô´1gµ·Ú[²ZÖhoš³ºDVù¼æ2>w„ý:Vòº Ô¤¸Äk.ƒgçb^£­yƒºËôú ¯Mü®…ìœ{±VÚº§ÛVŸ‚Ö<뵫{«ÙÖt=ñ:̸îQÏë¶¿'°m«yÝ6èØ«€=ºm/±ZdRÍŠb^¿ t½!Cï"^à ´},Y 7{à÷©àu™;óÚ£/drÁؾõ¬v™ìä¼~[øyüÍYf{ðî?‡×(½ÅÏçwã5›‹y] 3^ÛBÅë[³sûi5ZgÔ<}±ºI š/@6k|¶®duG­ëø™ýÀ±¶ øÚ¤°ú£´ŽDP( +Æ6çuÔÐ6–æ1°ù8`Øq¡¬)­­4òŽ+âu‘‹xM5ðóÝÆC® À°o;|¶;ð·«gaо#€2û?ØN †íeìi.âÑï¥ä[e°#xÇCgÐûàÝ <œ`O'šsd±pÇmÎhs½ Ú]ž°ðÇ|]Á×|Ci<‹>7èê>¾ ÷‡nî¸ö€ž˜—`Èîý< KäÒ`¼xzA/ÐxÞ t^°ƒú½@ç6´E‚Îí> õ…þ:à†‚·o= ³Tà¯B› ¼ýÀÃ:ûÁ†~Ý:ú£-2@‡ÈÞÙaç ÐÁA1tAàTÏBµ`\ã:2cl(Õ|Â1>ŸÃ1>ò…ÃÞéôtñ°²GB¸Hȉ¶Hz Y¢![4t‹¦s~ÑÀŽ®ga_,tˆF,ðc1O± ÜXàÅÂñÀˆ‡â¡O ú4èÓ Oƒ> äÎÆçl|Î]6xdƒG6xäÀ®zà믾òè!Xz`é•v1Ž¢ôAÑ0§h¸ÿlÊ!æ4wh˜7Ð|.6¦<æ¦Üàå\À”ü^m•Wõÿ5õ_Þ;¦1ª)65ý>üå8ôåøóå=ä†g·Ð8óå}ä†1åËg¶˜~cŠ!î7ŒMqbÃýcï1àË¿ 9Þ‹ãq] çL±ÜïDzˆÇS*³i-E+%^ho Ì–h·R±Jb ZÜO­kmÉjÎZcͳV²z£´© °ÛáÕÞ–×M­Mg^˘¶ÐÃzZç³z;´VR'øJû@nÛbÜ~hëJŸ ³Ëgõw´Çg; ¿‡%«k ùœ@ëLqqíŒÏB«mè¹a]cÎj?¹à³Û-VkÇó »•iM'/ØÇú¹¡Í2ùÛ²ÛÝŸ>“,Ø€;hü¡ct B»}váÚ‰>/ xûþþöàì0Èàˆö0ðŽ„!àáLŸÁ°‰}ÞÐçp G$x…ÁjÌ‘;h¡c"hÔ Wƒ?ø¨éz5ð‚ñ9zª1/il5þiø2ðPÍ–šŒÑ7ˆ®ßàŸ ]tôø:\‡Z°-„lè§Ä–³tô'ƒ·8:È£2ECnÚhÈ ^:È¥ƒþ±°UÃ8;ô£ºfñ:¦Ù‘ƹ¬Æ·c ¯e ÆÃ® Gx;ÒØœÚ2Ø·vSÀø ÀWAßÁGÀ8´Îà Þöx·ž=¡§#x9R¾t>¡³#ø:aŒär‚}œÀÉÆÍàáŒvg´…âs4d ƒ £!‹+øº‚Ÿ+øº‚§dq£sG?Cgw\»ãÚ´àéz€§øy€Ÿdò‚Þ^àé/ôûÐkÈä…~/ô{CNo¯ã=m> õ > ÷…Œ¾Q¹Tà¥/SàûÁ~~ÐײûÁþàëöèPϯ@Èù! ä ÄýDã{à†£O¼ à_CcgÜOÁè ®g![ÚCÁ/áèF8ä]8äÓà:8Ñà ¹#Ñ ="iìÝb!s4øEC¾hàFƒW 0³A ÜXÈ ¬Xè ûÅÂñôŒx`ÆCÇxèã¡W"è5à“úDÐ'£-²¦PLØ4üR S °S@›Ns`¥ÓœxéÀK^:ð4T~ôiЧAŸ}Ùx×Ó†~]6è²Á#»ž…–9ÐU|=ðõÀÏ­ò襖Xù4N¦ñ)ýû½ó^ŽÓÿhÏèö‹Æß˜‹ÿsõxMqb²¿‰½LñÔË{)¦8gÔKñŒƒÙßî—ü£}‘´¢6ûçêÃ×ZÂG[¢Í*”ÕÙm h ÛµF›9ÆZË2µE[àµ}ÂjEÛÐç «)mKŸcÀìPÁÒA+øOGÐtB_'ø[`Œ¶¥Íðµ€ùp†Ÿ ¸¿lá›öèwŽÆŽGÿxàºa¼è|àŸ~ôY„9ñÂg;ŒóÅg;ºöÐ{<ü ŸÆlj®)tͤë]CѯÆ}à ½"¡c$î÷h´§á^VƒG6ÚÒ!W2½_躀Ïéôþ eé\6Æ'ƒ_20t#׌‰ÇgäKŸlृ>}:È¥£XGž:è§«giœþÑ2ÄZ’¯fûɦ{‘îAÓß8ÑßÑ=º_-žCZÈö†éo­i=-zÒßÇÐûTüé"öÖô÷2ôÿ¤éwCôÿ0hí«ÿé?»aÊÛ ÄOd¿Ábû†úp¡—ÎÜ;¨GªP7¤[äÔK+„6ßÜíî8œœüjã³Cî+ÎU=.Þsÿƒ—‘HéÔðÙ»ó…~Oö̹‘ò#ÑôŸù¨êû¯7¥1{]øX0>êß\Wt;)ÔÙ=Èï,,špú“?õr¹ß/Bq‘_—†>nÛu÷>ÁMXø½ËÁ$£ýß…ÏŽRÄÙ÷Éî_:æX uª“wGŽÿFh¶ö˪_/*ó5MGo¨¸<$7ÛüÛ@Rî²&×á0t–ƱtÒa’öÓz¯{ö×€£f8=¾_yq˜Ð‡êâç$.Ùî+´çrTYÞ0¬ô@J3ÿgBHj·çë>ËÄøqü^íùxëéD¨Ó}ÔÕóÊz¡ÓòÚ±/¾hMªüôíÃBIé_þz¥Ï¹ïHÊ‘¤Sk1®X·çp£4#všì+ÔÍnd¾~Ì#R•eUôZåÇŠo¾þbIm™@J§•úžvØ.XÆy_ø±äI½aÑæ§RÄ­à5Chmš·å­§¬mj.ËÿÁ¬Äº%Šk‘#Ã1ûHiºªâá”]&}I:f¹hçàÕ1¼Áe×…Ã&{ u›ÆØ&“ª’·.ö½Ü—”†lžu|À»d"㣼]ÈæWüÎàÇ:Êrì9e˜;ïmÓ5©:–ñÓ_c ¤yÑŒ£Š9b<ó‹‹=N¿È%ó¥Þä0Rž×‹ÏÚë¼Aö¤¶3 . #˜Ÿ‘¬áu-»eëÇücǽ™‹G_å¹`}we',Ï· +šÖ~dòg“]HÖÏ]k¶¿óŠ+óʳßÚ%Ô]·µŸl$U?ZÏëÓl)ünw¿˜’Ý~Áåç6O@Ïüaûí}¤ç‡±ò¼>ØðFéG[IÕóå™?mý†”5ÏjÓ¢0ªdˆeU¬‰Ÿé¾óm.§¶™™æQ¨ûÕìðÑØsB÷ÏÿºÚ¶Â“œz«ÛòÑã=Ii\_㬢ђgϾY©ér@èÝ¢Ý;¶'©}™Ÿ”»Œ-L3ù§`lÑ9ðýÕäT¯Ç½rÖ‘Ò„ãOÆìÞ@rŒ?äV{ŽqÌJ½oÞ‰yO’ÃØ¹Cñ”ÑC…®|]85¡û÷•‹/“R[+=naoÏý¬Ûª¤¿Ž’ïÿ…ÌO¶Ì¿Ùxû¾Û²ý»w?.•œŠ³ÔWªŽJó[rof¨É]2rr­Ï_1žùÉæ7çÿú,Ñ^0Òæ'óÉ©…«¿‰|—”~îY¿"‚äLjw«½ªôÌ>ŽÚ3ëíp“¼‚q¼aô–ÐIä[?$,9Û,&ó‚É>4ËÉ_Àx6ÿ1þÒ|‹\xÞGñ‚ûÑ©G}ýïÆÄ(êL~ôæúG;ô¬¸ßfï¸<ôºyÀcþ±¾Ì3çd—ɦuO0òy1­ƒÕ=SŠ?s'¤ÔaÛù±ŽIöy*1Æ3¿X7;–I4ñŒÞGž<Êm"Ý'ÕÊs¥›¯)ùÙî/ÙUçÍ?yæ«èìDõŒ~Ž$'{ ©1¤Ó"¯LOa삊Ï~Gr°ó+’ýj!óåÓ?›©¸I0¹†ÜxTZwª5ã]žß ×áÁ§&mÚó¦`Íûs;F]öt†ÉŸ•·1ÿXì<ÿB‹§o Æèk¯¶ÞJª§~Ð%tÙJR:(ïπυÑð¾!3m‰vüãíÒ”²-bþñá~¿»ïõÜ/Ý÷Æ´#7~=2E¶ÏÜñÓföçkìôF†˜•D{àLŠÛѽÀa~3{gãîwú FêÕÖÒúJª—Åøë@EåÛoi‰6bâ5‰Â8æ/Ó;èçžî|Dö·‚èË›/äùÝäXÛ¬ôRÚ)㫳ZËö8Úç»ëN‡ùÉ$Õ¨žX"MöŒ³/œÚ_ø…,ÇgËyšÊþ{qiç!+FJ~—kqy Dó›ŒÁWs"íÆßϾž »ž{Óy[ÌSRRØ»ÒiÉŽ$‡Ç8æ'1Û+?Œì> ÕwÞsÊ{ÑA¾O·:|Ró VöwŻέžS=DÿÜÇKÙ±Ò^¶Çòøhí൤¦Ùà ÕׇJ÷]–mdò×7†(o/ý@ÐLJ>}GûÁ¸zïÌyw#¤ù«Xšòh¯)ypæ×{´$óá·T#Œç_˜ÚEd(ÏÿG[;_ˆ¾Fj\¼Ù!¯÷o¼vÝìü>“?“¬½–%ÆGœa¦çŽíãÚ*=Œ%Âà‡œ¥x¨&ö·Þ—5«åû¥iü)ß=Â0Óývåô²…›á‹E¿ÞçÏ]ã¶iÁõ"5“´ê~Û’’§ËV{bäuw±8ÿ‚'}Ý+= w-\”Ðm”ä?5óÞ¿Ñ#{ˆä¿%?‡í:7õ=éy’ËÞåçÉbqþ…Å÷…];.¯ãûÒÇïŸð©Yž\hÕæŒ¤GÉs›Ç7Î× os?Óª~¸ºÚ»D¾Ï‹~!,ãö0õØÍe¤¦xÞ³>ýÒf»ü·8¾K´ÆŽèùx虬|ëÇ‚S#äuÿè£Õ?k¶’š-¯hTJRÚ¨ÿŠûS³ˆÎ¡{‡™uÊÛK˜¬YÒ·¦¼^3û~{í7¤¦BaQû¢)ùnÝÐ’ó¿}¹þºØ…ô‰ŒñÌŠYÜ$ûßñkÜýåydq‚lO²:nýžjéyª[úÛWImgùÅú/¯<šý órìÙ«1óHMÍçëgoü˜”¬¼ÝïáÀsDGÉg‚žÍûGª7†}š~CÖÛ$ÿýëW¯ —â¼’)Úö/ì’â<};ñ æ[4»æÔ.H0ñ@N7}/HñË^YîWgÛFKó¦{·i¿kowÃx6ÿ›°Ú÷[‹ù::æúâµÛÈiú¸=2‡”N<=|ow¢]òkjèÙün©«¹ü™ó‚ñpIjWAZwNGoðªšnIJ>jÓ:ŒqlžKo|âärùyy nþ‘¶Îäô¼!é-—¶—ãÚÞwF/Ž9!Øòø^›•pïö†*åí¥lÞË«„öm_?,å#ÆÝÆÅtMNïH¾—OßšâAŸÉû‚Kë˜vã…äŠ7ð˜lÛBž!¦û]0î™øÙŠ,rúl¿Cë‡F“RÿÔ°˜û›ˆö=1`À86ß—Žo ¼nò/Ü¿x,wÚ*­§©öVÜuôO¸áuR ­ZH÷£–†‡¿ó‡¾Ø¾²@Ê7Œ[?/¬ÜÚDÖëÞ¦Y7¿!ëÅï“_輊bšþ²xÌ/vÜþÀ°kêJÁXÞƒ®ðäô£V᣿˜*ûÕã'§ß-zHô4KHòÃ8æ;ɨÙß‘í[²¥É²ä½R|yúiNï÷æUÉþõâ‹VKŒ%yM^8Ú¯µó“]âòúº<ß®ì.¿#­Wg^S¬]¯ ‘ã€Ö…ËU ø<éW÷Ý·tx_à1ÿÙýñäü‰}RäûeóÉr×nnÒýz¦•wñêOº¾,—äGº¯ƒNŽÈ5Å¡ÊÛ˘?í)1SܱX%¯›æuÙØá‘,§Eçãw¿)Ù½äKëWö¬¬Ùúg²»0É\æW{Ëg½žÒÓÐày£±ãÄ(YÞ1‘¬+ ”ååqªö²2Y»Ï„A7C}øÖZà1Û—~cÚí±wåçκ´Å ËÉ11=/…‘Üÿ´~Ë~Ö—&{‡ùÙ~ :“ccÑ©o¦+÷“3YGMÛ6]ZgsÅtë Ð3?Úÿ”&Ü{#ž6Vóä<éL¡nxɬß]+åÉÒzºœùÓáîÓ;Vuè+é,¤Ê~Äï“RûçºÝF¢7ñ_ÎüåðÖÓ‡=#u¾ctûÛ‘Ú¶·Æ™¯i*ßï,î—ïMÅ0Á%ŒgþqQóô[Ód?N¡ jKRÛïÍüÆ.WI©¸Œ{}U‡>›~úã˜?ù´1"®Ùže½ß oNj­Å@R¶cšè D_ßôƒýOa<ó£S·mv½ÔTÎ;üŽ-Þµi©u¾:£ïèòýlfêý…Ò>PÞØŒ7°ä‡ù ×þì‰àï)vRUÙîy˦“äü~øÞn1Û%?Ï£aãš9ÀaþQ)Š×F0Ò´ãÈAR;é·šu]o’únôÉ-ÅÕy#ê·$¾†qlþ+3S’m{ K_°ª]GR[Ü×bYþ4EÍßæRÞ£Ÿ=ôµ·Ü*o±y¯¬y¯r<‚Å.nï6É/kkçVâFỚˆ¡|åo=6ï ¥lÿ×ôÜ•âi_có‡“4{†ù[vná´ô$b¨8ôs+k=)²mpî²ór~³’Íw•Ï€‹½tò~ÝÜX»‘öJû!†…®-Þ%eXœ[Ù¿I´Í(Ö••lþ«Ñµ½,7Ï${lsù´zð9ùþ¢Û’;—ÉûÕë”f¿cz®—ùÇ©^6†̕墷ѡmòý±iÏ›­’î·2 »ôŽojo-ê?²­¼¬d~t ‹<î)¯ªS/¥w‚,ïÊ^çƒ×¹KþP¶Û‡îÔËÏÑ•ÌN}žô©ýÃv²|ª¯ATIq¨aÎ…õOn”KÏG“|ÒóqáæÈs‡2åûo%ó¯êî#FýðãR¡N9¶ì'Ûli]3äNÛÔó­R¶ÙêkU”¹üœ2ÝW+™UkŽýå௠u–œ#íëÀMóE)cû"DOŸÆÃ¦bó›ê};9ÞN⋺ÞÃóF.8)ów9ø©Öå))«[ˆžºWb©òö*æG5m寧}ÏZöƒ6tãî„”ˆË„ÝeÅ}“™b1òìvißAÿÝûˆÔÚùUN·êÌÎïëúl×aiÇ;Ø$Å;eÃ>’Ÿû«˜ÿÔ#7n|RZ/¾¾âu\•Ó\žç¡;GgŸ¿HÊÞ©{ÿ‡eÒþ¯~G÷ŒžæóÃüåôøþ™ÏÚDK÷û×»ûÅ„_N“ýpä½7ƒu¡RU–1ë^§»Û¥ý}žWË÷û*æ?§?Hø²þ¾BøúýŠê÷]ïËrw¨ž¶Zq›¯etÛXY+íòç’ì׫˜ßœþÎFÕ³ÿ{‚¸,ä|…ùzÍá\Ì`RvµÞò¾y¿oó“34 >&ÛÇ­Ñc¿&ŸãùÚ'Äáê#ÉoËÙ÷]D‹¤¶ÑGŒgþr&¯ÙÚ°‡‡d» XJwˆ!$äó’»Ò<—Óp³ÏfAÁíñwûŸ«™ÿœ¡Ù”ÏAÉÿ®¿¸‹'qùynÞñlþZé¾,_³Æ:ÔÐ[ÞWL¿è˜<æ?gî?uü-r‘pý‹Ÿu}D ¡û¬÷´#å ¬Æ¼_QC´Û}[Îônzæ/µ<.¾.~¡°Gž€Çg/wœMÊ5«¾5è¤ïõ¤çðjæ'µNçÚa ®O‰éöƒû§Äàó(ìráR.n˧Hë¿NÜfM2ÅçÏü¡v2ÝÐUKëÉuwqƒƒ<>‹»Ý8”û<™·ë›cC‰!îÓKõ§þ"ÅÓå7:ЈMÚ/ѳ<8Ì_ Ñ?>¿~¤P¸6ˆn8¹Èþ’M„ù¤ü©øÝ£è7}Ú\Å8æ' M¼{Iv¹úäëÉ>“÷Ã;~?ì½ZL¶ö[ëôôõö²­aþa—ËþÂժ÷Æ›ü&eêô)dkJÕ¢_n &¹ ñ¨Bœ²†ù…aÞˆÕg¦ÙKñåUÚ­ªBn³ºN/\¥»øÞωaɾs·moü6kÆ5Ñ5ycóW+o¯åóþÅüÔ·53„+w/7úP7I~.-ð]|¸E3²?'LßéíNÌÚ:üŒ¼ï±–Ï?—Ïw^98g@釻‰aVÔÔ—å|vëýAóo6®&ˆãÆ$¯ ÝZ!Ç)k™œåûW˜½‰aêŠm‹|û­W{Òoäø.pÎDiOÈuµ^àÃþ[cí ç}§uS»q _ٱŽ“ý)ñŸüæ¿ÆíVþw¡GÔ¹ÞzGGÝÀ’r¹NøU™íÀ¶>)¨a%<*dÜD6‡KÁ¸ýxNàÂAþ‚º`~Ð3‹ÙlÍŸ˜¹:@Êã’E¿¿¹©_®Ú¯øp Núåã'®5{õõùx¾ºeÎ/wä8–ˆý£”w¤Ï”¼\/pãx‚;ò§Éq9qhtëòÜõÊ.+kñFºËd¹_)9’cÏèû‡´‡¨}j¾Ö ü8Èßwb¥nK“íˬGâoß`%¶ám]CÕ>z½À‡Cw#øKÜx¹Ù¹q#G¨ö¬2¶k!+!ÿ²<ø*óÊÎI¨GàÅ-@õ«÷Õ÷G?ø ìîÍ2í'V²ÌܪêÕ>ÐØÇå ¼8øv6{‡vÂÃòèsÖ‰RÎÛfæî^é1‘•,IØî1{2›{²Mm\ÍóxŽð°:|ö¹W%jư¸£’‡Ï+Ÿîó+Y~wŽVöd>á‚ì£ÿµkž6eÝùEÙ9#ã|û?²YÚ%&=Ð@{PØ·lnÐȹãkKPá%,¯»r?SûÌ*n!(ÜÎ,\ýn+©î~1ôÉÉZoÃ1ü9ù„“íº ÒjÿXÀlü˜lÐEV¢‡MÐúžÑ"Ô9U>á€[#açäxÔ_Uö§ÏîœY‘ Ò4y­,¾¾ìé§’çù„›AÓý^Ôj°Øã£¨~$,{uI÷©gLxhHæbeWò0«%9ÊÉ8Ñ­²1½´šùžûàÕê]Ñ-ÓË–~ÃL~¼Cu†?:¹¡@àÃöÁÆý>9Ò­4Ëÿ6˜Ù‚wÞšþ‹E®CÓh~àêÇ2wrÇ|3÷N½”cÈIòï,)¿Mgë^kÕ§“6PÄ[°¹“¾úuïoŸ)y[ ðää«E{X;>… –ý³î ã–žêß÷Ýzfkm ù'çñ°.ï MÆKàÉIöêq±ï`ÖÂÍbÿœÌLb³yúá~ÂËŠÙßMYxKú 뀹ͬŸ¼wüÅ,Æëó–ÖÊ¿ºð¢‡KuÒª/­‚¥˜¢üªkw?š·DÍ3Œé–P-Ù8ç´Ê>Ú@ø)î{øƒ®?K9QMûe¿ÖÏR—M|Ÿ™Þ|mxXŸ}ÒN!=¤Îg6n¶žÑöÔìЪs+ž¹ÛâIfÝYü]Íû¥>2}Ýà}sÖPc‚çæƒk7ת…"í«nvg&¼´çù"¹3ìiwm œlÿdÀøÁg¥¼®ŽÑ5ˆÜgXkûO[]ëzÈÜRÌTrOÌ«òwm ¼˜×å:™$åO57sÜeÖ sÆß™þ3·×¢Î·7.¶õêòáÉÚ±ã/\¸þ ³6¤µuÂ…™à”|Ü@¸(œY‘qý[¹ž Ç:¸Pýt(3w¿uë_³•Ÿµ÷ú- ÛG+ý¹‘ð¢çdÊ}È1]-6Sãq¦î§áÉ«¤ˬ»Ó•?»åo æ¡>ÂËJ€°D;&ì=¬¿ßO~ãø„™˜^v<ém%·7ÞÍÝU;¾ó!Ó7˜µìœ­Ôç¨l¿<·~F׬œtr»gÃÞÊŒóKðéŸð<Íëp=°R;2wÝ~ûáJêW½&ê¡y楩ó¢#ƒFi“~;…uÕŽG©÷·ª?“ÏÑÂÌÍF¾7.··O‹çÉÎ|+ÌÛ‘}JsêÇ.½”~¸Rã຾Lg6Ëz ;J؇›ÈÞùÕ°›)ce|¡óÙ=û¿yìcåqi½&§´õbŒç,&0ýÔGö&?eùƒærRíC¿óò²ƒ™{ðÆå>Kâ}“À‰K£—WjN/=‘Ù¹øä™6“¤½k&¿à`açʸ©6 œØ¹zùŽæ`|ƒ¶Lõçyà­úÃWW›m€qhì7 \ر¨}¿X,÷Žiº g¶ŒÉA…KS~àÍbþí±/&Õõ•v®ƒ³~›£Æaþš[>[æ2ó·Ú%=«üL›l·ô€[¹Þíg$ý‡‹:ö®æw¥Z¡ 6ü¶âVây‹ÀO¹L] ×c¹8O•öý­C¿ÿ¡RÍ»ÆÅ2Ä~õürèTiåA¹²3Ç1{ÞŽë×Ç œ(¹´Eàâ?îöªÖ ?8³Îy¦ªñœ¯ ³t=,¢žø8Dß—ú¼hß芷U¿·”M(/ýMùË„ßBâ3£í¡”ÜP,ðRvÖ“#LÆÕâ_?Œ"å‚ý [ì Sê{<ÝgèÍŒ+ëfü˜ÑMÍO±ÀMÙòÒ/šŸØ"qsHÄcJ\Øùöóú&…Ç™¡üK#ioͤu(qS,pSÖÿôñúîÒ-ûÏ+ßëqÛsòü>7åw¢ù3öi«t«ÎqŠŽêa!¡ZÙÁ^•O¾Ãì–³&¤oSí~>iG¦[®†emÅü ÜÌïÿ¨WC®V¦‡õK?<³×¸Ü.¸3é~UÊ­â{¶ üì?Ð%2xç-íàímôî"å‚CÿŒf¼:_àŸGm3K¹›&Ö7êxÙ/âÙå¼<6¶‡×w$®£: Ý1„•ˆsP¹O»šÒ¡ÑCé×­'ûú\9ýTkí ˆÏfŽ´“íÎr]ç(†Ý1…GÕzÝUra«ÀǾùáïŽôºvpáëîg×~Èó?\µòDg©Ÿó £_SÅwjx^àcï¶éºv0e/#sèam¥½aøù ÜOqx^à£ô£®=}hÉŸåøðî̉åQêýÝ“8Ÿ.¾p¹Ÿø¿¿Cîµÿ¯ü÷æ‰ø;rü]r²ýxþ¯ó²ñiÔ¿."ÏÏ©þ¸bMº&ߪ“ò^§Í1ÍQ_ ÿ&\Gx¿Ên(»¡ìÖH|«˜WwK“¼WË)ç•?åEÙÓB\«ðƒ²W=ñ¬¢ÜóÚ²–r°ãþV˜ÇVõ”‡4…¸VQnãO<«»‰gïn›Cù¯’(ÿ•“ò¯£Ün¹ÈM*s¯£Í>¸×7?(ûš›ä^¶Û£Ü¾ž8”pý¾ÝÄ£„ué‡ññÛMy×Q_Œ_ ñ)aü:fSÎu\óÏ¢|ëíˆS íê„k÷ûS®uÔÓãÞmìŒ{;£ž\ À½9"¿ºžß õæQnõx‘Û*m ºL¹ÕQW0ê ¾L|©hC®‡ šD\©Êe…{» ®.hWú–Eܨ7ˆ5Cä­Ò9QSˆÕIùªÆ*ÚÑmêž-rnF„®¥ˆÁsAü¦EBÄF%Ÿénâ0E9:ƒ¸KkÏR žAÝ1¨/ã‹wŢݱ(÷ð'>Rà¡Ú‡wÇáù8´3®‘¸FÑ–x q‹¢Ü垨»W„ÈéÂó·÷Âx$„Ï(Ê —EÞÚ²Ew.âQND9±Täß×s¸ã=Ö‹üò:§U©P:ÇhžÈq¯çuO!®Ñ”ËãÑ×B<£)"¿‹‘3«qŒ¢ ÿèL—tf¶Ë?:óï¨3=inw»(îÌ%ç,tÅšsMÜ%®µ”w 8lž×„£<ƒøKP¿Ên(»™‰£u¹cÞÜÑv¬OTâ'dzž!”'eO'q—ƒ^yÄMŽõÔå–ùÄMO¼%˜§V—)_d*q— Ü&„xKJ‰g0™xÉqÍ;™r`ÕRÎÈdâ$ol’/möÁ½¾¾(ûîn’/2» y<ñ‘—O ÖÆÇ¯”r~£¾¿Nâ)ÁøuÌ9¿ýqÍuù×RŽÈTÁCØ©žøÇÓ7IgŒ{çdâwçx*qŽ£ˆ÷¢Þ@ŒM ú„zƒpoÚÔH¹ QW0ê n$´!×CPMnÂA‚wuÁ½]ò‰Wý˼#\Dê¹ÑŸðÝÄ5’J<#¸·îí6øEngxŽÈƒk›´9ý‹Ä¸Eâ=‘fâG½QÇ(´1mˆF9ï‰6×7þƒçcPw ê‹ÅxÅâ]±hwl-ñx£Ü£Hˆg¯ÏÇ¡qhK<ÊñhK<úØÏöD¹'Ê=ë7ÏÝ®çšÄx$ ­ (' œÐH9ÞsDîÉD´5åÄÁÍÍs»ëy'w7wˆà€ìkI¸7iœÈéÎsrê<(¯>EB-ðü“<ßd_§Pœ—[ç>AúG.ÇþhC´A—üוÿMOþ•޼W?rhèC® xo¾n®óþJÏý7×T·5Õi†»Wzë¯8‹ }u/gñ½†.2¸Š ýÃõNSüêºÆÐ3†Žiª_¸n1t ×\w:ÃÐM¹ˆ `ä¼WÖrþ^b.Ó bC†ò»©Ìærú¬˜C=g/úÀya]7×TÊ×[OܬùÄÍ!xZ‡nøÝ ¿»—î¨×cœà‰òÄØyÖR.^<ÓkYD\PiÄÿ„ûZãZ›Á ó>¥ŽÖ¶ø»7~÷æò2…øY±¾|ð\óåò¸hŸ#88'ë}Ê¡k¡Ü¹èK‡ZÁÇÚq9åÌMÆOåÉÍ#~&´­s ñ1áþüâ™@´7÷•¸/8ƒòÚ¢‚úCñLŒS— Á³Oùj1Žáxw8~ïÚNä§í–&rÒFghG$ú…kQhwÚƒçcqO,úÙÿ÷à6x6- ŒaÏTÁ?Ûó²È•ȹ’ðÿÿ†àI✮‰NÁ¤ç¥EÝIxGÞÙýíƒ:û¢mýøÞ÷}ìò}œíò}üw´ýiîJ)l åbwg6p励u…Ypùé¹ÔU.uÇcç‡6øáºŸSˆ"ÉåWOyÔ1~óD^YŒ‡?îõǵN!"Ï,Ï¡Þé2åOÏùÓ;£ÞΩ”7½^puŒ#Î>”ч@”ÍB¼¡Þ ÁÛ„6£®`ÔŒr0Ê!¸‚ºB,B†¦6ÉŽvtÁ½]Šˆ›ýC]a¸7ï dzá(‡£ÜÕ_pñuE;º¢Ýðžn˜“nfâÞC½Ýñžî¸k)¸7}D½‘¸7ã…z¢PoÆ1 ãr4ÊÑ(GKÑhG þƒñ‰A»bPŽE;cñ|,ʱ¨¿ƤÊ=¸=ŒùŽÃ»ãÐÎ8Ôr<ÊñhK<úØ3Bð!ëÜê"?ç^äyØ{a|ÐÖ”J…X×ù–‹œì‰így"ʉµ"o°ž“½Tp#ñœì½ÑîÞèGÚœ”&òásŽDž£½úð¿ì|UUºè³!ô„%ôP’œ“„Ò{ï=!½«£Æ2 :–ÌèhFQÑÆJ¬;K¬Ç8ê˜Á¶ÁŽeôý×^kŸsÌÅ{ï»ïÞ;Î3ü~›ìòõÕ¾µÎZß· 7»¨Ü€È°™o[xÞ’ªò¢Ÿ/t|‘ÁX_d2úMñoè˜)–ãX)ÆH1>:މb<:"·-šçĸ&Æ´Ïvð‰1^‰qÊq\c’‡þ+ãsÄxã8ΈñEŒ)¢OrôwÇ‹¡c…ã8qª1ÂÌqÁ̾_ôù§êï‡öó¢7û÷¡ýºc~ª~ÜŒÅmöÝŽ}¶è«E?=è$ûhw¥3mÎÈeB]ÖT^ ä>í„̱*rYœ..ìq:°£€E]M}]*sVˆü`c(ç1Â'¦^çÛø.•¼‰ÔÉ©2ÿ„¨F“¸w§KªÌ9áJyOF–)ü*|`䘆|ÓÚe¾‘óËÈS m7ø»ñÞ fÂg&2Ì„îLä¿YЙÕ/s‘Îo|房Øuî{~ÑùÔïùð_à©ò„òͼ…Ð_ÈýBCå…öbh,o xKa ï=ùî‰\K©KK±Ï2ä_†ü+À]!|`è¯DÇ•À®ÂF«€[Ey®†Çj཰ô¼U‡+|mŸl2ëà»ÞYÆùȼ‘Ï[ü”y4DÞPw|#|}Üe.X#o(¼7Ág3ô¶¸©¼ ØhØOvö“[†ý䟣ŸìédÏí®ò`k[lêÅiØæ4Úû/™ÇÑ–sÈÊîÈ•Ûê›ö< Z£°û(ú€Ñ^*6¸£uæÙ¹Eæ2r`Ó~Æð<¦ËžïÑÌ-ºƒqV•7A<‹þÞKæß­ò_ó}™Ca"}ÁÄ\.hMBŽIÜOã|\ÐÑ…gð\ùæJ»rmU9¯¡3:“ÑirŸìf¦ÄªœC}*ç/à3ûLÃ>Óa:NÃþÓeîÈéí*ßµ§=ßèžÜRU®kì1ùgb™]*Ï5|gÁwtÎpWù­ÛeNI[nkpgCwßçð<»ÏuV9­Ûe^kÑÕÍóQù„úU.!¾»óÝçù.*—52ÍTy¬[Ukxy ¿°2oÐBäXØ®rV{ª|A»T® «ÊSK »$W妿›'2x¢Ÿç.Ù­.v)|–"Ã2ä_†L˰ã2dXÎórž—#órh-¼V€¿¹Vð¼9WÂk%å¶’çUÐ_½U”Ëjx¯FÎÕȹú^Èí…,^<{a¯5^2WödYsRå …×Z`×aŸu<¯ƒÖ:h­w‘9BEîM‘7ÛZÞÐöæÙû˜Ì%òmè“ÝþFäØˆ±§Ê%Ú%óhlB¯Mblv³›Ê_„ ›ù¶…o[reî¢-Èä _èúöÈaöŽlŽ™æXé8NŠ1ÑÍqPŒbìãžcŽa1Ö™ëÅblšcø§Æ3Ç1LŒ_Žc—9n9ŽWŽcÓÐñ}1ÈÇDZFŒ3ŽkÄCÇÇ1DŒæ8ñSc„ĸ ÆÊñG}¾èïOÕÏ›ý»Ù·›}ºÙ‡›ý·è»ûk³Ÿ6ûèŸêŸÍ>yhì¥ôÞ–C—:tZ—r›v¨ü¹a*¯6EýÝCÕðQ9s­\Ý2wÛ8qµª¼¹ÐÏß àN€öxLtçÂþŸ½IÜ»ˆ 8Wž]E_ÉÂ_†÷J÷k*ídªèë\Tž5l;9Ý€qC.7á#wK—l¸³UÞ´™3míe2Í¡}Ì…æ<äžœ{ªÌw6Zó©ó°Ïüsð™ÿ+þò¿š¯¦lÆw#—jîüœè4‚ö<ÂGåæD‘<o$x#ûTnÎX™ûÜÈÍIE=µK6çÑ>*/gŸê­\Ôgh;ÓgÖžÇ@{¬ËssŽãyßǵ©Üœà‡ÞxèïSyÆ€Ÿ€ÌÐa¢§ÊÑ î$ä˜Äý$á3£‹§ÊÏ ž+ß\i+®;†äæD‡É ºg ãÀž±Kv]¶œâù„ʽîdš ݹÈ8·CæÞœ­yК׮rnòÝïî|wçy>2·Ö|dš­”Ã캠Gv…Èï¬ú,tçw!Ï ‘k|!×¢nÙU.†Ïbp#Ç`—@wI—ì>=¡ë‰ŒžÀ.E¦¥Øu)°K¡» €eðY†ŒËa9ÏËy^N9,§\–cŸ輂ú°¹Vð¼z+{d¼ ¾«€]…>«á»W#ãjh{AË ½°•×I•ËÖ"ÃÚ0™ët-xë°Ë:dZÇó:ä_õ¹*Ï'2yóìͳ7ÏÞ'eŽÐ Ànè—ùå6Bk#<7ÂÓÇGæ¨óé–ùì7a§MȾi@æ¥3r{b—ÍØp Ï[xÞÒ-‡_èøB׺~b\}žø7Ô?ã¡9šëÇCǼ¡cÝP?Ù×~ÊWv¿†î©ãÕ©Æ)1>9ŽMCÇ#s2Ç lú£qÇo̱fècŽ+æ˜rª±d辊Sæxñ­1›c‚98®ˆqÀìÿ‡öù§êïÍ~^ôñ§ê×ûóŸê¿×™‡öÛ±Jæcö\Œ§õÈ\Œ#hG#cU¾dðG¹saÓQÈ?›†·3õØ9LV­1ÀŽg,õh,ÇñwÜ•Ïøñü­ ð™ý‰ÀL‚æ$twáÙ…ï®<»¶«\ŽÈ5}¦¸ÉüÆS©ÛSÁJû˜FýšìtêÂt䙎Ü3Û X·néºÍ„þ,dšYàœÜlä ¯ÙÜÏA¦9П ÎÜR•kXwžÝ… ýùÀ·î|è/@Oð=àé½ÒBw²,ª‘y‚ó}14—ðm rxòìÉû¥È¾[,o<–Ao9t–£ë +W›tWÂc%r­o6_-ú"Ú—ô½º¤Ë¸zk¡·6VæO^ Ü:þ®¹ž½ÅÕ%›âèmàÛF`7b»ÇdÞàMðÛ4(ó#oFÇÍȺÅKæö…·/ð¾àùBÓÏÍé?ço‹AyØçþ}î%û_Í×þï^ŸîqúyúÛ©Jï*g/å¯uɦyõø4Êó4dA›aå¢ G ËHžG‚;›ŽäùtžOÏåâùtú†Q´¡QàŽÂ&£iÏ£ÁMùŽFNg`©ÃÎ<;c—1|­1<ÖXw.žÇRÞcyÇó8àÇQ¾ãÀüxè~<Ï 7Þ{¼'¢ÇDäœî$䘄“à3‰¾Ò…o.|sÏ•o®à¹"³+Ï“é7&çªÜ¿Øk |§Pß§ð<…ïSù>•ïSá3ZÓaZ‹Ê ­éØnz·ì‚f`»|›íÜ ã7d˜‰ì3Á›‰ 3‘o²ÏB×Yð˜uR彯‘9ïgóm6°³-žOÚóÝÏA¾¹Ð ì\pçò<øyКÇó<žÝùî^ãßç·Ênoö\€} ŸxÈîœõ`!2,äy!Ï á¹¡Ë"`»¨Üõà.†æ`—´Èü»ž|ó¤ïò„‡'°K±×RäYJ¹-…î2`—a¿e»d·ºœçåðYŽý—÷Ënvø+ ½ü•Øj%¼VŠ1çUÐ_Åó*è­¢œWƒ¿üÕÐ[ͳ¶ñB/lµÜ5<¯áy ¸kÝdÞçµðZÇý:î׉ûA™ z=vZO9®G_ož½yö†Îèl€ç`7P6"ÃFp7»|°òú ¿¸›xÞÄó&äÙ ÿÍØb3ß6»…ç-ȳy|¡ã _èú"ƒeeŒ⟣oí8¦™ãÔOí7c…Nåï:ú¸C}ÛSù´Èmë£ÿ³>«Ù?оÐìÿ÷5 õ3E?æØo™}•ÙOQWFPö#€Éý(Ê{r}ïÆRNù;‰÷“x? »ºï‚=§bÏiÔ¥éÀΠ gÀ× x7áËuI·à ¾ÍæšCyÍ¡¼æò}ßÜùë.ü5è·ÞÊÉû{`«Åü] ìì¿Zž<{B{)ü–R–K©Ë ±Šò^-ê#ô×ð}-ß×Cs=uÊ›¿„…NyÞŽ²n¦›ÀÛLÚb•yÁ}¡ï›ëôË;ÎëUbßÅ{NƼÇé8×û\p}Èõ×Ç\w2ÆW§O¸>åúŒës®/¸¾äúÊÉ8“ëô5×7\ßr}Çõ®ï¹~PçøOc`Öð™5|f ŸYÃgÖF«}ñøÌ>³†Ï¬Ñ‡hô!>³6IíÏÄgÖð™5|f ŸYÃgÖè_´j¯>³†Ï¬Ñ84|f ŸYÃgÖæ©ß­ñ™5 ŸY£/Òè‹4|fm‰Z«ÃgÖð™5:wÎ\ÃgÖð™µÕÊ·ÀgÖð™5|f ŸYÃgÖð™µê<2ý™F¦á3køÌýšF¿¦YÔÙ+|f ŸY£ŸÔð™5|f ŸY U{Oñ™5|f~PÃgÖð™5|f-FíƒÂgÖâ¹ð›5üf~R£ŸÔRÔoòô—ý¥†ß¬ÑgjøÍ~³–£Ö!ñ›5üf¾T£/Õð›5üf­DùMøÍ~³Fÿªá7køÍý¬F?kœµÆoÖð›5üf ¿YÃoÖè{5üfã\ý¯†ß¬á7kôÃý°v.¾³qæö¯Ñþ5Ú¿Fû×hÿí_£ýûniÿí_£ýk´ö¯Ñþ5Ú¿±ß€ö¯Ñþ5Ú¿Fû×hÿí_£ý¿ƒÑþ5Ú¿Fû×hÿí_£ýk´c –ö¯Ñþ5Ú¿Fû×hÿí_£ýs~Ú¿Fû×hÿí_£ýk´ö/|Jö¯Ñþ5Ú¿Fû×hÿí_£ý‹sèí_£ýk´ö¯Ñþ5Ú¿Fûgî4Ú¿Fû×hÿí_£ýk´ö/΃h´ö¯Ñþ5Ú¿Fû×hÿí_ìIÖhÿí_£ýk´ö¯Ñþ5Ú¿Ø#§Ñþ5Ú¿Fû×hÿí_í_ü+uúeœïsð“Û”Ÿì¦öp´É½Î?ÚÃÑí°×¹EùÊÇÔùù‡³€>j}ºCŸwSk.íê ½³:Ó®Îл«½mræGëÔ]ê\ ‹Z«n•{Ÿ ÿÙGýþÙ.Çwñ¨áC·(ú„ÚÛ«Æý>µ¿ÃG­7}iwu¶¾]î…6öxøH¿@œ­1öZÕºN·ú ÕMùÕmr Ûø-ÕS­c·©ýÎê¬}©ÚØ£Ö~ÜÕùÁVõ»ë òµ­ê\N›Ü+møÜÎÊÿÈU{¦»äY|cOˆ³:_«ö†´Ëuoãl¾“òS¬êŒ~‹òÍ»åo¸†ïâ¦Î짪s?íòì¾Øûh¬5¹©3ü©êLb»Úwݯö”¸¨ßcÕÞ’jIŸÚ‹í¬öNZÕ™ÅVyžH¬§‹½ÙÆoÆnjOe¬Ú§½Cž5¿!~•‹òýcU,€*@¿Š à¢ÖºbÕz׵ߧöª8«}ÝarÏŠ±_ªög¶«³}r=̘XÕoÒ5êìc‡òãNª3^j}¬TþVmøv©òl“øÍÚðñ”Ÿç¢âìRs†A?À]®§kôaò·lcî°K®©ç"»d ±ßÓ˜?x©5{`ýxç^þàû#¯?üýáï/+ÏVlm…žÙ­ðµÚ— Ñ!ÞØ+Zà‚„.AðÂAØm+ô·B+ô‚†G0°ÁÀ#c0:£c(ßCá ¿Pø…Â/tP-kñnðÛÐet·ñ~8áèíppᎭÃ)·pà"¹ä>Y"¡9(—â°YðÑÈ N4²D N4´£‘9˜X`bÑ+}b‘'±ÐˆGŸxxÆ#X<ßãùxp1Ð)ù±Q"öNÄFIðH‚Gß’ø– dx$£s ´Sà›Š>©È•Ê»T覟l:°éðKG§tx¤Ÿ|&÷™ÜgrŸn¸Yè”…½²)ólpsÀÍ7Üàs€Ïão´óä’^>úä#c>¶( N"w!´ ‘£9 ѯýŠÑ¥úÅÐ/æ]1<ŠáQRðÊÀ+C§2ìR†¾eØ¢ ›mw;ß+ø^Á÷ èV@£»V![ðUÈT…lUЫ†^5ïk¹¯ã¾žuÀ×A¯zõ”A#4¡Ù„ìMÈÞ„œMèÔ„-ÄIãßðzñðá¹ïðÜwxî;<÷žûþ¯Í}5ÑþÅøZãôˈƒÓ?$vÜ ÚKªöfõýx¯®8ëkœaS±pºÔY_/uÖ·OÅ{°ªßŠv©X8îê÷¢ÇE÷íPñp<ÕþÞêw#'‡ßºÕ¹_7µ/¡MsR±qJÕþß>µg+Lý†Ô£~CòTg€ÛömYUœœ.[ÎSÅÉéPg\T\‰VuØEÆ–¿'ûN¨óÀ©¿+9©x¹êÞ :lU±æÚåÙ`ã7&Ou>¸Mîé2Îæ¹©}]5ê÷¦nµöî¢bЕªózÝ2¦„[ÇENU{¾:ä¾ #ÎŽ³ŠO&ÏI¬µª5ûµÌI›°ªß©ZÕ‹u†ÂI£°ª=­ê·«¹/ÂØ+æ¦ö‹¥ª½Ðí2®Ø“eœ±pQç,ÂT,6u.¹O%tRñ{|TÜ»õ{W·ÜcfÄÀs“¿-ç1ZÔ™Œn¹·Úˆïã¦~ KU¿‡µË="Þ±çÚE׈•{ÒŒý5ê¼F‡ŒÛ!Îmˆ˜yFŒ¼0µ­EmÞ%cæûBÜÕíXu¶y‡Œ™gü¶Ñªö¥õ¨½iNêœs‡Œ›gìáv–û¸£½äogÆ>’XµO­Mþvë¬Î=wËx@ÆY7už#VÆòG§X.ðýáoqÁËŠüVlm…žÙ­ð¶Â;ºÈß@l|W(tÁ B— t ÂAØm+ô·B+ô‚Á †O0ðÁÀ#gð rÉù ¿Pø…Â/~¡ð ƒ_ï¶q¥ˆ9pÜÀ'=Ò€ ÇÆ¥Ü‡·Kw=:¥ð‰ä}$tJyŽ. ZQØ- ÑÈ Ìvd‹/yS°U42Ç L,6ªä[,òÄB'ñèN<üâùñÈ~:% S œ.$b“Dl” Lt’ Ÿ4(§ÉðH†G2²¥@;¾©”O*r¥ò.œÔn9½H6~éè“tàÓÏä>“ûLî³ÀÍ7 ²°Oö€œ†ä€›n¸9ÀçqU¢G´ó%›ä£O>ræS.Ô“BÊ·Z…ÈQˆn…èÖbô)†~1ô‹y_< §2%|+¯ ¼2t*Ã.eè[†-Êàµüí|¯à{ß+ [ ìZ…lUÀW!WUŸœúTC¯:VN}긯ƒgðuЫƒ^=eÐ(d‚f²7!{öjB§&l.Æ\1 ‰Ãñ®~Yñ®¨?šO™s)1‡ó'1w2gúÑ\‰úaÌ‹~j$æ?æÜGÌ{Ì9»“}ŽóSósncÎkç4æ\Åœ§˜s1?ss.bÎ?ÄÜÜsˆù†ã<Üc˜s 1§ ó s.aÎÄœAÌ~nsƒSÍ ¨?¶9ã|Àœ ˜ós üÓ÷~?ý‹Íß¾¾ðó…/ü{áÛ ¿^øôŸsyQö¢>´:ýüb§YUÌÉcrÏ®yþÁ8«Îû¨ý*s—ÚsU£â§¹©x»düe«Eìõ±—=­=2™8#gì¯ê—ÝšØÇ+b±$T<œn+ÒKÅS S1"ûUü›6¹oVĘñ”ÅÞßLĶ ›_˜<£æ—*ϺŠó«l„]‚àã×&¢ì‹Ì)2.°å¤Œuè×!—úD\+x~Ȇ>~À†À;L\ÈÓ.»Òxðbø–€!¼¡C;=EŒÓè•~¦ðIxNc:ïÒ°k4¸)”W :Ä@/Ù2¡Q$ÆbÌFŸlèˆqš¹È™Í÷ðS “n6ï‹Ð§ù‹ºäòapÂÏAÎ<ä©V t²/¾{åaËlÁ:5ÈÕ\26P¾ ÈU÷èÖð½Rø4:Û=è—B£š5èÙ¯lQ¿lWƒjйVЄg´„nèÙ€ŒÙ\Ð-…nòäñ·¼øeC¯¸íð*pâÜ"¡‹x¯J¡ë 9Tm¿¼ð+‘«–ºT ^)x•”ûvh#G-ô„_!h@¯VÐAæRðµò„ê±A-ºn‡f­ðá[‹üØ¥JøÐߟFèÕ¢C­ðG°e­xÍFä©…o#|áÕL£€¡,«x®ô£ü:x4 ÿºMÀ7õ«>Dü^‡^‡^‡^‡^‡^‡^‡ÿ·ëðÿÝkïbLlqúeÄØP1èÃT,å?qF#V‹îSç¢SeŒe‡Þˆ™aUñƒTœÍ0‡¾[Å\öRqèw9ÄÚ,•q5ŒøË>ê|t»Êáâ¬âÑ·É3ÒFL:wun£]ÅrVñ™[äYi#F³‹ŠMßæÏÅKÅêPg8ÜTl¡6ÃÙˆÁé£bpîRç¦ÝT|¡ç¦Sí¹]Œ3…^*¿K‡Êïâ¬âq–ªXCÇT¬¡0³¾CÅÂsRg©se hqöЈ;ä®Îy´¨¼/=*÷‹›Še_£âõȘF<7“(WÙ%ϱ¤]TœûXë¾M戱õŒ³ŒÎê,v˜ÊÓ&ωˆ¸Ó"¶ˆ‘;ÆSÅú,UqŒv©XFƒê숻:™«ÎkwÈX$"F¾qnÛM'©‘qöŒ3]*ÏŒ³ŒUâ7(cç‹3ØFì£×Ï8Cé¤Îx[Õ9ïVuÖ»G÷vR±M¬*6R«<‹"∊óß"‘oßGÅŶۀß&æj oãý6p·¡ã¶Xy^EÄC '¸ôÎô”qQ"cåÙ«ÊSÓ"ÏgŠØûFžš6uN³OžÕ1ƒ=eܤ`/ƒß8î"sלTçURU~àBÝT%ô u—ñ¸sâîòìJ(ôüÀ …ž?°þàûCËþþð÷‡¿9­ØÛ M+4­ðµÂ7¾È@p±W 8àl>]‚Ð%ÛÁc+ô·B+ô‚†G0ðÁÀ£c0:†@/”ï¡à‡Â/Tè¿Pø…Áoï¶Q^Û »÷ÛŽIW=ÚáèÝpä§Ü GïHî#‘%Z‘àDB+ ZQÀGÁ+œhd‰.œhhG#s42Ç L,e ïXhÄB#ñðŒGŸxìÏ÷xxăn:% Sò%¢S"öN¤^$"[<’+‰oÉ|K†G2<’Ñ9Ú)ðM>¹Ry— ÝtàÒK‡W:r¦C?Øt`3¹Ïä>“û,ð (ã,ôÉBÞxdóœ~ø9à瀟Nòå!G-2å—MòÑ'Ÿçü695)D†íè\ˆ…Ð+D·bÊ»]ŠáQÌ»bt.†G à¤Á7¸4ᓆl™ÐÉ„F&|b¨qÈ•ž‹>x)üÍ6WøÐHC‡lìUÝøåбûäA'[…ûrh•¢s´òÐ%:¥Ü7À³çñÌßRhA¿™¶£gï+Å71æC§ý£ïÙrÙ±>iÐÏF—Þ5P™ÈÝ€-‹€Ïæo8¥B.dÍãorŸÍ÷R`· ù+Ïàa›<ñ¼JÊ®½Š‘g;ø¥à€_‰œµBfðJÁ«Ä&Û¡]ŒœµÐk¾… ½ZA‡r(K£•Ы‚^#åVK½ÚÍZô(…o-ú6b—*áS@;|¡W+ôE–*ìX+ÞA³yjáÛßFx5Ó(`°cÏU‚rÔ_Fðš Û|²kâßðúûðúûðúûðúûðúûðúûÏiý]øÛÿìµ÷ÿÉ=ðblksúeÄðüq#§¡Ï)ò§çªØzý*VR®=ß•Ÿ4Ö!>©»Ê»Cæƒ.Çt«ÊØå£´EÆß3r„©<ê»TÞ+7•÷ª]Æä3bú{ª|ê»Tìk7•ÿªUÆé;cPåUOU9bTŽX{—Ê­î.ãc‹\2"–-vi«Œ cäWw—q³8ÿÇT<¥\•«_ÅUòQ9cw©|‹.*MC|%w•¦UÅXPùc}TLÓvkɈ½í©r¯·ª\²}*Ÿ¬»Ê™Õ¢âq÷©¼5N*'»UåÐj“qºEŒ#—–›Ê§•ªrjíygEÎ#F“‹ŠÓ«rÐîñlD®-#>ª‹ŠÛ«ò¸ïPyiûTî-g™WÀo@ÅrjSy¸zTþH'•ËGåyoQyk»eîZ#ÊѦòL¶É\¶"÷ÉYÆ34òÚ–ªÜ]»dÞ#”³Š½¦â¯¶©xã=*“ÊWiU¹yZUžø“u—Êï5 sà9oceLÄõ­2_+ªOå‰÷T1YsUÞ‚™ãËÈ}»CÅŠêWñ¢œUôL•¹¾Œ¸Qn*Vk¬Œÿ³9WÆSÜ–ªò~õ©˜­}*痳ʋk•ñDþ/á&oãÞÝü¡ãøûÃß +0VhYám…¯¾ð sRt‹7Á /¼ t B— Aéno…öVhmƒV0<‚ 6Ø`t FÇè… L(üBá ¿Pø%Pqü݆·Awï·Ž>áÐÇÖáÐ Gžp`à GçHî#‘%RØ œHhD!{ðQðŠ'Y¢‹'ÚуÊí&˜XÊ&yb¡ XhÄÃ3Æ£{<ßãán<¸ 蔀N È—ˆN‰Ø;‘:•ˆlIðHB®$¾%ó-ÉðHFç”9•H>¹Ry— ÝT覛l:üÒ‘5éjºÁ}怜rd›¯,tÊBælÊ<ÜpsÀÍ·Ùó°}rä!G>öÈG—|äËç}!6-„N!2"C!t ѽÝŠ¡] íbÞ#W1ôK _ý2ðʨe(Q†MÊе ;”a¯íàVð½‚ïЭ€F¼*½ ø*øT‰{ô¨†^5ôª‘·Žû:î뀯ƒgôê WÐl‚f²7!{¶jê“S¢h ßÓøgÎ}Í9/8Ãy:œþÿÍÓ!æTbå¸înΕÄüHìwó!ê1ï1çB]0æ!æ|cèz»˜?ˆ¹Ã©æ b~`Î hOÿfN`ÎÄ\@ÌÄ@øÿ¿ä}7ÿ“¾¿—Ó??_L®Ê§Wp\‘a2}ûäRÛÚSæÏ2b[§Êüâf®,#)üÖ ÊÜÜFŽEwÿ²Kå-•qEî&ÿy¶»ÌkÄöï’qý¸ÖŒ;ócÞ ‡Xþ'T~qè®ãÞx Ïhã‡îƒ2ßéZ. 4üre® <ül—Œ*bÙ‹\©ž;dþE eg”¹,Ðö£®YÑËê.sм/aȆ¾aâ;ð1»dŽÆhF‡\aвÂ;;c£àc,´ rRvá Èš€½‚°WvL@þ à2Ä ý he‚Ÿ„]Òà› l&8iÐËDö\ôÉE®"ñw—\rÌEŽ\¾çB£ø\xf§ÊŽiÐÎ4‘7ú™Ø"›+¹ €IF Dò»€÷À•_ƒý*ù^€œyð/Gþ¾× W ï¼äd ¼k QÉûJì^É·íè»z À7@o;2ÕÀ»ž•ÈÔ ä†~-6¯¶úÙ€üµyУÁOâ9¸dè¦ð.©ðNE¶TÞ¥B+Üt`Ò‘3~é”m:¶K>øLaGÏ7 }³7 Y²‘+ÜpsÀÍ7øøæA;OüE÷|äÌÇþùÈ”/üʨZ…ÈQˆ…Ð*DBlW ýbèó®ÙŠáQx”W&| ô/Ã^eدl@-é‚[Á÷ ¾W@·ðª@ž*à«­JÜC¯zÕЫFæ:îëàY|ôêäòo=öl„f²7!{r6¡S¶hÆkàÿSy‹Doüû÷~þ-`ø·€ŸÃoÿ•ß~é¿8®ÿ¯ýÿç×þOµîÿ?½æÿ/²Þÿ‹É­uLå -•ùg×Óæ]‘Ã:®|wEßÉè;¹Æ!Ö¿—Ì cä¡íwˆõ¿Kå‰×´\™»|Z¿Ì½däcl“1ÿ¼1ð˜Ñ¦bþ;©œæ­2­ˆoËkÞ¡bÿ;©ÜæÈ1«[åÛBî3Úd y‘ÀÈwî)sžÏo6²ÎÁNs¬*ï 0sNÊØå"÷Œ-G-õzS‡Ìõ8ûÌ;©âš—ª|'U|óR•§ù8«\-2W­ˆy¾ÅEå«å¼2v¿ï 隈œY"Ž¿¾î-â^Ìw°‹üþ¿ÞÈ€ÞðÏÐ ¤ ‘!™‚ „.AØ"HÌG}+å´•2ØŠ¶»Ø­À– O2C —ÏUß0x… ™…½9 ™Ã¨/aÂæ¼‹ÀFÈý"°C´# íhG@;š‘ȉQàEA; y£µ½*€Bÿ(ôA¾èÆÀ+^1ÐŽ_Œ(þÆ~øqàe [|y—È»DlÈsø‰ðN„w¼“°c>ðIèT/æðM²J-…o)¼Oá] ï2Ð)ºi¼Kã]ïÒÄ3r¥ñ- ™2 ‘<åÈÛÌ•ÞðÏB‡¬“Ò½kF†jl’ís±}94rÍ…N.ø%Ø)ùØ!ýóÅüù œ¥‹W€½ŠÀ+‚¸EÐ+¯œjx•€×,þ‚[ÏRðJÁ)ra§rlRŽ\åÐlÆåÀW ÚèÕ ¯rð*‘­•ÈU}ª±]5°ÕЫ¶z5ÀÔS¯zhÕ‹r·þ„t7…‹Ù‚ [ Û‚½Z·y[·Eø§Â'ÿ†®»Šy…cŽ 1:w:gºþjÎ~j Öq0œÿíçŸÿMø¿¦ï{*Ÿ×ôu‡¬ßþ›ýѯ5}ÙŸÚ=Ô_~ªðQ‡î“6}Ï¡>§£iUþ¤ð!Í}#CýÆSù‹æžé\§ŸŽUbú‚¦hú¢ÜcþéùòÖžTùG{Ô|@æ\ùÚ'7ù˜ÌÏ.r1O-•yD>J‘#gÚ€œ®{S6›Äxš+sR‹\ì3½dŽMðÞ ¬·Uæ]¹DENæ3 1˜ÙÐ p—¹DôŠCÖ8îË…b¬à¹¼ l™/Æ `ó±o>ÏåèW„LåÈØ |3pY}²›¬G¾ ä®@¦ ø7WÏz>V Ÿ†w¹àä‚Ó l3rWC»žr*¡\›…|èÚŒ JàÛŒlÕÔÿ 1žñ®œçfd«8Ø ¼fî›Å=øõ‚†ÀGÖzðš‘·ÙšÅXŠLÙáü‚?ç5±ÿîý±=N¿Œu1ǽ±ÃûbÿßöÅþ+í‰ýßXKuúeäÂìvÈ?ߢòσçJ=qÖ•ï®´·Éè<™çÉК ü•³CåÃD–©ÆMæ\Ì:á_[UÎxx­ú·ÊüñÞ©*W<ï¼yç-ÞAǽ7 ÇäÙϾG`ûÐÝè©òÆCÓš>àû``6õÉÜñ!ÐJ†Æ&p7#ûf•?ÞKå*D¿-ðÞR*s\úöɜϾðóÇÏYæöÇ ÷î-ðµ€k×ßøZÌļÝ ­@äØè–#ßâàÇ»8ðãÀ?>qðIä]"ï¡—È»DñÞYýjk¶LºdÁ/ ›&¡[ z¥ð>…÷)¼Ká]²¤ yNïÒÐ5—wi蛆¾Ø!y2Ä\™2Ð=þ%ü­?ºÙè ílôÎF‡\qQ¹è˜+è¡g.trÅü™òÁË?)]Â1·à[¸ð-å@ùS$æ*\Íð*AßpKÀ-9!]ÇRpKÁ+‡n9²•S&ÕÈV..à˯¦Ùª±a%rU"W%rUc¯jlW}Bº™5ÀÕ@«˜zl\/æШ‡F=<›‘·ž«}[ ×‚¬-جy[·Z„?*|]ñÏqMÌq®`úÿ?7WøàÂï>Õ:•ãÚÔÐ5©S­EQ~ÿô¼­¦¯çèÛ9ƪ5×|Nu®}Õ?hè:MÜ)üÇu•ÜÿËœ±=*4úM”ÓØ8d €÷È—,úÑ>ùžŠ¬Éà&‹~†w©Ø<•ºš…k ƒO¦Ên¨O™¯|ÇRå'šþ¡é6)_í YÎFÙ#ƒX;á.×ÎŒ³/ý*þTúͰMžwñtD>7#×á•ëá¤Üû/bÜ‹³ñÆï'e¾71NŠsò"6¾ˆß/bÝqgd®8áßgåOÈs)"w›ØË&bìˆßžÄt±¿-¸CžK¹ÞÄïJbÜ9ÎÄwÿÕØ·Ö.ÏŠ‹5Vüæâ×!÷©‰=obßš8#.ÖrÄX!bµŠñNü®"~ûÑ‚rå™o±¯ÍÜ—&b«Šýbbo™8-Îj‹õgTÄ…gµÅ™g1e±\Œ>·GžW.È•ñAÅž3/Tœ½±GÅYëí¹2~¨8ÃlÄ!í—û¸<^N2v¨8#r2ˆßÄÙj#¿—ìÏÅž/›EœCg´ëZeœ±ÿ[ì]ñ^Ä~(s]ÄCùÂŒ8-r-Kì¡¿QŠøSgB÷¿ûŸßxëñÎqgé™õ×]7d[&5ºD%Í)³øÛÅíÞÓ.·LN»ù•§¿=¬üÝŠËcG~ëßÿôW~tÅPJúÞýç>òÉi,KnxaÓž¤WYòÜû çZ6„YÿV{K+| >zÔã'?¯i9pÿ7•f§e솣ÌZ©¼ù`Aëi3ýŸ]V_ëòv¢¾÷Ƴ?9ö®³^år¹ÅD­õønƒå‰²ÓÏ™qÁŸL<ËëεðÕ–‘'l{«ôýàΗÎ×>›§ïͼúPô–-Ë/® ºûÌÇôŠ ·ŸÕRa™%ñ çnÐ{ü²¿<üEÚ–gEŸµ/9Èÿ©¿~pï—'O„è{ç~å;~Þ³–5c§œçóT^öÕÉçÎoÿÔ®×n«Aç±+6¿ûëך-òÚ{Nr¥~pì }Îúž¯v¾×ÙX¨—|p‹GÁ}ÀçðžÿÊkŸ_òSnËðæÙ7Lú•]7œ¾jzò}Ïߟ¹­Ý}Ž^ôyö¸h××Áo5ð¹àØ9+ξÔn‡µoÆfMºÃfÏ~§Òßùa§ÿŸ½½“|ï§q³eͽ¯.?oð7zÁ?&Œjé9z½‡¯zjäùÕN–yfùÏ=”|ý»¶òïŸæúæw‰gé{-¾r߈96{LÏÚtý7BGÖ£î΢cŸúÀr`ÂÀêW²Ý-Sÿþ¥ó(ñúÞ¼ƒßw5ÜiY­ì\¸øûÚË.~Å¡\d=ÙבõBÁ'–ù’¥÷û9wvÝ §÷o~ìÚšÂüe}Ô÷¦$ÜöáiŸYËrÑ‹çÆ4å ìµÌ~ùë›|º"¬Ç÷ÈúóЃã&-yÔË|oé¼åX麧-3T½í ž²ú’=þod®IóÏÙ§ï]1yÏù{>°Õëâ{gWÎ¥NÚʬG>°0'ýÕrKïówŠ’Ðûã®òáÉ2}ïÈž´ù#.Ö‹ò®žzÕ«ÿ^Ö—:|#Æ¿ÒÒ{ÿ£\ñµ]¯”Âò·n»ËÿUÕö|ZüÈ6ßËôÂ-_-™Rn_ÖŸû;n_|Ýu?ØêOïMÏŸÐ`±—”k‹õ=⦅sÑs/ÛÚAÑ‚;>ûbu/td=ºïÖ¬+Îì¿ÃÒû›ýe½[δÛ!óü±É7ÎÐ÷ü)À;±ñ6ËZE·d¦h¿_Ö›{÷ÍH=:³ÈV{[·Ÿ3Û^îÙ›j7¿¬ë{lœÿ²ï_õ’ýßîzÓ ø²¾tÝ¿l`Ö›–Þº¿L^½§ÒÎ?oÛÓŸU´•Þ}Û·tû~fÙh–Øäž? Y_ºÎ žeë¿z˵E¿ûèWf9ëýÅ×^zUn†Ý®Ý<ãòüR{{¸vÍY/Dm¯‡{e}Ùû@Á¶³¦Yz‹žèxdtªv_|zé<}Ï]Nþï»ß¨çŸþ˜v[Õjàe=Øýʶ%WûŸoéÍ]±ãüÞ·Ìö¤÷·ddÔ”÷‡õ©/ºwéyaÿ¸åHõ¹àÉúйX¿¤ìÕv;ÔW¼¹¸Â^/.øbrñzW=×'³äÍ·–™tÁ—õá®æ_×Ýè9É´Ÿ¥Wµ3›þWÜÖëzÉ1[°çÆ·_l¸æL˪E}þô»Ñ3U{°Õë½²~Üy÷¢Wþ0Ö”ÃÒ?­éÒçfÚõºáwï¸}ö‘¾çêÒº.÷§ôôñŸ´ŽýUø²~Ü.û-»\{m r³×QcŸ±—Ë¥¿YEUvCÖ“Û¾zéÕé—,·ôz%­ÍûÕl{=¿ÃµÙ½ïE‹¯Ò3s·óšÅÇÏ4û ðeý¸Õß­ì¸4Kï¼Oß¾ò²:½ÿî™Ax›þUöÿzîÄòï^šj=~,÷Ž+®|6㇑v½g><ñ®wÿ®÷ï‰Iq'r¶œÆœkºõ¼õ#ß¿|x²üoö¶ü꣫KííÒC¾ÍÖ¯÷w(-¼Ý^¯ãÏÅ¢—YTšwÓ`ã—ÿX`×ÿY?~ÏèÒtÿc¦Þ–ÞeÕ–øïÛûûûnŽÊj}Gß³Ä/£sþù–-ª½ç]ò^OÕ̇¡#ëÉ ÷ŽÚôò;–ÞÕ{*ôÖÌö¢÷?t iÜúîov¿zÌ?вéª9þ//yÇA?Y®ý¬9ã|¯éàŸ~§óGïèý?26¾,MßñeŒ°Õ§Ü?nüìòÄàÉzpÍ¢5®'DZz=Ÿ?´0<ß.w߆¢é¯MÔwï\öÑ”k×è9}Ÿy´tNO–û¼ª]Ùx¦¥wÁÕßZîxSï%*-fñ=ºh…s_ŒÐ³®¾¦xYÎ?ÚþÌÑ ,½³ø`tx³]¿÷7=5§ó¨¾»õ1rèé—=µßÒßo=Þ%Ëû¼é¢cw5ÇK¯ÛãÅK~¢r ºiûé¶qf÷}×ÿ[×e–eJþä›<ö]·Ò:²ük—¶]uñ¬<{;žlZ?4ãÆ|ul¤­ýí>áužÇ WÚúŸD9BG–wŒ)‡ëkÏ]õ¶ú~håÓGw9é{–¾^—é÷±ž¸óð}úkQàåk–—¥×ýã+\¾8ÃÖŸ²Þzɹoµ×;á®4=`ñ±\ýnÈ'ÉzŠÙ¯wå¬7ÎxÑ ¥wý¿ŸQùš­:ãúÐØã¥ööºè£þøßÒoœu]ªž*éAÇ(wýü+ Ko‚ë¼ñë{õC©O>¾îðuúžÓ.›óío/sàk”·~iøç'¶öÛÇg9.è‡rÜçÞw‘¾û½÷ì;î¢'=WÁ:<£Üõ«çwFÔ=5ÓÞ^o)ÿí§;vë‡ ó[— \n/?1Lú_oç{¯ô_¯+ôZný²ÜÞO=S¸ç‡ V˜ýˆ~¨|×!ëÛmöÛ½k¦h!–uw-=’žnŽ÷õ@ogP\~䀽½¾ß—›|þG¶~óP½Ûw±a3ìrµí<÷àñn‹wÇÙÞË.òÑ3Öþ¦òâÝ gÔýÆ›GMzûÌkìþ¹[âe7|Ù^¾çæ¿^µ¬ÕV.»Ï¾tBzÆõ%Ÿ­\î•õäæ¿O¾pGM>åê‡.ÿ­û«×fè»Ïk¦§Ø¬'H|ðd½èàcÅÆ<ËsýfïwzO?tÓÁ½?dVè»Åhçÿƒ;¸`êâï뀗åk—û“7Þþ–åÀÝ/Þ|áÛ‹õC{J/lv+²µ÷X³ß½W–ÿÎß|±2ÊjדAN¶v|è‰W¬ON«²ÛíAo§k^øÊ²Pµ«¨sô°óž¿z²^Üñ‡úÀM·ø|ýÌ·œ¦é‡F_yÕwv;=>yë ümþlhÐñ‚×ö[ß'ëÅ®?¾ù°Ïñ6;=9A82~ú¡7pàÚóíõáÑÞ‡£®u¶ùµVS¯ûd}¸[xag÷ÙôzÒ3ê»·¦M´õƒ‡ŽçÜ}Ù·Ûä1Û¥êÇ¡#ëA§ažE–'Þxð¾?ýÉn—O›Ï·¾aµë%ë¹­~i…Mœ1 :²üUÿaë§žlúôöe¯è‡N¾)fBv½ý†®^4ýèûd=Ø0®`ìÕ –'0&>ú¡ï~[ðEÝ_íý›ÔCÇ{~ÿÒ&ðd}¸/gÅÛE¶ññÉnXØp•~è‡Ë®Ú|ãh;ß+Žî^»à[ÿ˜ JgûèÈzÒUŸònÍÑý6ùŸŠ².¼ß¯K?|Ú9mû_ÓôÝQÂA›¯Ç_¸#p w'x²>Ü;cÕ‰½ E–§®¿Ïãýð¨[ï­‰ù\ß=CLÐ mý\œé?Ü/ëÁ½÷lžúÒ“små÷”Q-œlåwxÂàoVíÿåé‹Nûø™gõN1{ím Så+¦±5/AOÖ‡û’Ø?íÍ"ËÓKë¯ï¿úýðÌ]¡÷…lòÿäHɬý‘Åzç_âf¾4:ÇÞžî—åß÷OUf~³Çòty’sëXýðÒ³¿‰Ìð?¡ÚCçÁ?éc®ëtà'Ëû~åß>}Û·ÃÙoØæ¹‡}G<µàY›Ý;_‹ëõ¨C·Î½-mø²¼ðü¢÷ªÌ¥¶vðô;bç.úµ]ÿ˜ºU³ž:×ÿmUþ}ô}æ5¶ö ì`÷[ï—õá§nˆ.zâ¦?hyfUر;ç¼g«×‡ó^ÿíû+½ìí}É7¼ø©­½%…¦¯Ùù„}žw¿¬fŒÈxyò‡–g»§Põõõ'ÍÜ8Fß}ÿ”'¼«d}™”ÿð²^<(ª.†©ß3½Eå¿õ]¿s.ÿ¬ýxÍ¿é_¨q9PÖs»ßö€¬7ÝõîÇûN˵é×7ñ‰7wZÞµëwñ–Üð{}w÷‰·\ûkÛx³UÂCGÖ—}•yéãI~6ùúÒk®ükâƒúá˧E¶çŒÖw§?Yû@K "Çaðd}é^óÒ=‹fÏ´ÕÛ¾]ûŸýõ ýðÕ#úŽfž¯ïö½ùCýÞ =ì½”k®3x²¾t}#¿³µ¯¾o{"¯ßnëo­J¬­µ·÷¹Æ‚­¾Ûú‹dýyø© ’º¼·ÉñìÖ)”ÜZýðMózðn½ó»"ë -ïë±yÝ5mcx²~MÕÃe³ëñey?V=™þËsÆrÒýð çœñÀÕ•úîQ»qÇû7ë¡æ:Ѓ²\Sþ¶i—çÔº×á×’½X`¯o²Þë¡r¼_–ïãÒ.6}Ÿû fñiÏ:ÛÛ÷ûW¿zï¾›õÝ>]áê¶ù[¥ßhÚz²ÜŸnÆÕYžÓõ7^Ñ]5í’Ø^{9û}Üâf¹F÷—~¾¹Þ¾,ç'škÎ|ç–6}ž³¡º.ýȨ‚gb ·é£úuÓ?_–÷}ãë;Ÿ µÏ3ke£qÕÄÌÉÞ¼1kþût5ß0×7 #ëî™ÿEû>±Ùå…ñÏ\yKùµú‘™‰…¯/è´Û52­áÝŒ÷õͲÍuèÈz ·|ÿ÷ì¾ñ–2zÄР™ûmås£¿Ôw'nMÄÞn’å¯÷{â1l²¼°ûž/xÁoÁßv§µØí1æø/ޯǫù—­¿xHÖ‡ž~xw÷–þñÙCÁ|^?²¸ `ÆÉ­öqqÑh÷_ßûk[¿“DëõåµàËúГrÎu)S±¼¸íö¢óú½mÞydùØê§.{ئ·éG˜ýmñ¬=x£ûôy–¯Ù÷½ó^[¿¨YótÄ]Oè»kÇßwÝû¶õ¶8c𒾬=ühi–U¿jÎ÷l~ù‰·^þÞÿ蛾ö…N‹ÞÉd–uÒ?Ñ£_ ÿãÄÓæCGÖ‡á=&œ´¼dôHÛâNýHHTï²û2ìãLˈ׳ -ëcÿþúMQw™ã–}x÷Köz¢æÕ¶þÆô‡÷Éz±¿o~E^ü2KÿcAE?Boû'}¬½~®¾ðìpͶõÒ`e»~²~ìÿóåe««.´‹ý ÝowŸý†]¿ß|öÄ]sn´·Ó9Âaõ·ÍgBÌy÷>YOö¿zžXy·Ù«ÿâWæmX,è윰ãè½óë§—‘yŽ­¾FÈþÂÞþöÉz²_Õ×þîƒúÓ‰ÀŸ¹sÚçÙzç‘yS¬1vlŸ¬²{še×ãÝ{^øÛSØÛËeïŬžì«w>|ù¹ž ÇÙúßDY_­Ç»e½é =îòo¿³Í?M)5Þ^¤?§wÞuèKíÌslí6ùéibE:²ÞôÖ]¼:íÒ7-‡£–/ÿ^?¢æe·mõ ¹¯BO1˳[Ö‡Þë®ûþ/ÅSlõáPÕ×þ>9U?Ò~ÑÓ·=>hö[/Uï“Ìß!ºe}è5…?YÝì¥Åü&Í®÷ÎÓW—·ë]9{³^i[J<ëo¡{§ž ¾¬½¯ß’}éË¡gk¾zx0Ï®oWÏâfïÔ FE¿ô{{ÉòîU~ð¡O'¿ÂTÚæOy¼ê²“Ó—è5~¨ÐM?ª[–ï!ãÂáyóW6¯Ù¡éûöâÏÏ.1ÇuûºÌKOÎ9të<ðey¿J¸[GîIyì¼{?rØPDïüëá¶—jló®„#gÜXüõ:ëñ‡e9P~øá–Š¥c^ÖüåÄÙߟënŸç,Oúû¼7>^–ç'¼ó×iÏÙÆÃÃ;¾ÝuÁvýÈà{FÞü¼½?WrûËþÓ´³é'CO–÷£X÷µ®·ë¯æwGÞ/ûýOõÌ‘½ùÒàe9øznaõ´|Ë‘e{™ñ²—óGû<ç½úˆÞ)V=Žê¹Í!mg]Qž,ç'G||똻wZ޳|ðªÍ¯9òÙµûî»4Iï|*µ0ØãK›Þùím'óî˜`÷k–åþ$µ3õʶþáȹS°´»½¿ùæó%ÝEÿ¬~W1ד Œf¸Äî<¬êÁg>µ¹×UØÚÍãgýÿ°w&ÐUU×ÃÏ „ 0d y!ÓË<’„0¼ ¨‚H «D«EÁ Z‡´j‡ÖT[Ö!jÕX'ŒÜ0gÔLâ”hÿ[‹8ãÐúýÎ=ûÞû¾üñ[ßZß´ôƒµyç³÷Ù眽Ï>Ã>{÷}ÒXU~…£ïÛWÄ4ïåeμu–ÚÇžïÎË[…/†ü|ô´8g\^S׃ß>eöL¬‘¶pÙ>eÿÖξÖY·Ú÷ÎüµUøåUë€ÛigO¤UБÕÛñqö=®Ù>^]à}ãÈçéßÜ|ížð ¿¼ÜðfñúD_Ï)7Ïî»ÕfìÚ}¼ýí¥æúž—òƒ×}ôhÛ{¾ž&µ±?Õì©þÛ%žËÚzÞß­Âï[µ¾%f“ïsÆ£ç,3ê«ôn·õÜ<ÓÞ·mµøÃ×ó™à"³gí7üvu´Ù¾ïÁî|4×Ñ?ÎùõV‘ÿ7ìª^âëÕç–fÏ¥+f?üÅWÎ<ãÒ'ãÜ}w彿¾Ò—^¤þ⿜íèËž†ç/¸ô²»Ìvky˜àœ ;çØOËø>?KÝPúz}ó[·~‘mö\ô›·/eÓúKu²èì“W¨Ñ[÷¶«/Ÿ–q}Þê(_ï“jbÏqû©±åäqÆuf»uÜ3ˬý|Ò³:œŒ_×Îoo,*wæÞÿ<©êµ‡ëÌž.ùâÜ”“LëÔôÒ4ó¬]—ª'àd9mÙ¬öRç<¼oì8UÂmwÓ—Å­÷}åÎ?©žµ/w9ëç¾òiç;ÔÆ×=×ëct"~ÈmÇ­+›Š¢žwÇ[öqö:éœð‡g]°`¹ËïOëùa÷ïfýâøˆûúÎûóq»ŸýÈ¥¯åô¯²¾véÓ÷ÙæÙç¼)­p𷯪îøä2_Ÿž7ÌžöÄEw$ýÊlÏ<ã®øÛ{Ó³ú­[à„/î½ãâÛW¹ë³¾½ÿ(œþnžÙ³%ðšg¶›íúüÖY—ž¹ï•¡ÝŸU~°MøÁÚ.søªO­Js~çÌo=;«J”ýÍíij!šiži¯³·ý÷ú¡_ßÛ=/¨/šíuÜ>{…;®Û„4ŸûúçÌFcW›=ýË5¾SæöSž¾H¶õ—Ü‹/|ñrÎW§/lòõ¯O\òâ[kÌžÁ—Õ¹tÊ`›èëx$ÊY¯ ¿¥N"\>üì¯sFÇ\oÏ‹æyjù¿eÀ•Çmzü÷Ή8Ü7PñÍLufÏ×ï^ñµÙö=!ÞbžŸü÷©7¯ØSùÁv=Þ{²URõÝÀʈœqþ¥ÙGÏG™m²~XeŸûo×ã»'Þ:tî5n¾ëϽM;ÍÞUï,È>Å\­né’—¹r±]óžhuô´o@¿3ÿ÷N°ÀlS³ÄÏG;çhÚúh»ç=‘jÁ׿øØêp³7mæƒY?ï5Ûn*zâŒOœuô…ÚŽ8_¥µRîsäâuueöV¼=eÅËûž¶eþjWú/œõðj}nïŽ×v÷þ°™kïëwæ×—&Öí»éKg¼z—\‘³ôÙZ³m¾uæœ?ÿé= Øòùõ‹Èû£mïÞ:mµïõ›Wì~ü‹h³÷‚½ß)¾Êl»º<äšÐ.óü“Ôè-”9¿=Fq’ïõWçýuQËb·¯Úu«ù§}UÞéi•ìù¾R)ÂvgÞ=0êɶ´‡o{oY·úÆj‡ïÛ¾[]:»Û\­ÏåÁ#ò}ŽñnÍ¢ÎõÛé‹€×ã¼ËRÛ¾¢Ï{Ÿ._ôêÙf›uÍ\¥×ÿÀk¾0?ûÕúwøâM}ÏèèÕ¾¼¯¢^žzºÙÆîø_¿à;NøÓ‘Só…É.hZû§N¾ùzå¦[Æ¿æÒ#ögÎ|6«+?¿ù"ßL=Ùó©{®Ü¥ùdG{é¥c›nõ½ñ¯g÷h2ûæ^øÇÛNrÆu­¶Ÿ£¼æƒJ\vûÞ’yÞϾ“¬…®Ù6oƵŸ…y}»N¯Üy´KóÇöí‹óöm;è{낲ZóôK̾ʼnßí=ñN³­è³y㚯õåÈ~g½Üc8ú®KóÁöZ50ÏøÞR×u¿ýÔìûÉJRÍÍßüí‚ëçÜb®·Ž ³)¯Ç»uL9Ï÷–9ù·µ ̾ŸŠiÉßbn~áÉž˜9˜ë;æÎ;K–S^û¶_Ÿª,Kœq{KŸ“›}ç¼vÛ…­ùÎü¶Yã\öº³ÎÜ0Õš˜Ýy¾KóÁ6µM>³£çß¶®ë½îø­·¾ýòngü6ï`£çÐoœsŽ‹ìsŠ.ÍO×[?¿=§gK—¯®~ﻦ…˜›»þ¡n¾œý^}lÖm;"*?Ø©Çë'‹wÞü؃.]b§æÐõÇywŽxßáóÍ÷î;/èòXgßw‘m'²SóÇÖ»-ÃßÛº?;Çó,ùé]ææîg‹Âo;Á9'^¯÷Ài~Øê½øÐÂô^ßÛíµ«.½÷ ³ï¹Oú¿ª~Êl³ØèsmŸ¶SÿSÖuÐZßÛ/¨hÆå­÷ê¯Ùïò¿ZFÿü1smo´SóÁS‰Û6Ÿ7i£#Go‹Ý‹ÃÇÿXõÙÜÁ7Üy¶IM„íÎùÖz½Ÿæ“N½?sÖCï¨ë–8ç0}ŸÎ+¯)✋¶Õ¿8zË?W:ë ‡ïvj>y²oËÜÞ0ϱ7yG‰ç-Û»¹¾/^úw_ÉR缺M­FÂf:ç<–A„-àÕüò¤º®¾ñ.§œZÛvön‡Ž¾#–!Kg¤u°èœ»lÐö¹ö¾°òƒ]š¶¨Û¼é¾w6*ÃQúí¼ë¥½²ÙÚVMsìq.ÒçWö¾<šo¶h»Tg=úÎm»¯Ù^kö}}é5ÆO“Í6‹-ךÙçG»4ßwùÊâͶÑ÷|öÈ¡Twݳ[ÿ#uõžoÐÚ–\hö«[†Ó_0Û­ƒs­}±[s‡jí•Oú•x>ú#wýǛ鿹xV—Í“,ÃMsÝMgÇ]¿x=Þ«Ù&©Ô7x†u1çÌSý'›ÿÎÎ;ÜõDеãwö"àÑãþƒSûÁ"g\£p¤KÏÉŸ]yÛÌ%.±Ù—ö´³Ï¸(á—Ñû&¦€O{ûËðßY_^¡6T»ôl] ºü¨íöÍ‹žz¾®jÇðèqßÜÓù;ßà=ÖÀ»ü3ÿÜ¥w¯ÙgÏçÎyFý;ç,Ý›·xͼÿúŽýó|ƒúÜÅìŸW’¾iÚKææÿ¼;{ó+¬oöèñ¿_™K´lõ ZfÔμJ=ëÿþzÆBs³2S=ô¤YoïŸ÷h>øÓi–âwûËš/6û…_}²1ÝÜü‡¶/o<ô…Oì–Ìzm‡æ®öh~¸Oì-dxÏúü6³Y{gCÄî¼IÚvlOÙ>£4ªÄ•·=šîÕ÷ú®¼È}zÿÙ[ÿô_Otør³ºÆ¼íç£Æò¢½ò3ó"±ãpäkŸæ“µ½Ö†Ôí¿Å·Þ[õû=.Ÿ¼?¢÷â[*\:„Ÿl}¾aø{ˆ}š_ëów·}ÚÂï”uñ®».{g=§÷ ~ôZ|ã“sJßвW6å–ŸèÐÑÿõÆ%s6|êÒ©Ì~CÿîðÏ:uÚå®oöYüãÛtªu1íú©e¸æÈû@à³! y%.}w&Õ²ræõuK.aåRìÚµì³øÇ÷K-·nžýá,Í:BÇn¨¸®ÒlÛùêϤíq×û,>ò]«n‘G/tûM]ÿõüÁó¦Úù˜m/,i‘õÞPãôûæÎ<þݲeðÕ™ö9.åôøßs(uêyk–SŸïë¯ÞåΓÊ<þÀËfÛ›¡ lÖ+5úù?Óã~ßU+OŽ.vôÓ¾ÿqéý{B݇§üÉåou¬ðáßû_Yg¸üýŒæ‹û3K”…‘;.Q¼žÝ`ö¸Ó)_~n¶ýÅ2\ò•Hÿ֗ݽùŠñÙî}ð3š?økŃÜú&ð—þæìþÝv}ÚtÍg÷d¸ü!çvÎú©[óG닞¿Ü{ø?Üöñÿá«Ï2û¿]×k˜í#­ ¦óeÃc\÷§h—/º5_´ÉyÏеϩ—.ŸŽT‘¦Ù~þßzÏ}ù8ŸØEÚû8w<»5_<4}Ñ™+SÒœq²÷¡ÿ4¯ÿc›cDZîðۜ惇Õ1TÒ§¾!µÌœâ¾·k¸á—Ëß5bÓU÷ÈíæzýŽ8Í–µÇÒß^šA·Ìþbî\»±›gÏ6×ß´$=f—{ŸÜ­ù¤c`î~XÑ7¤õýw}À%÷^åÚ÷ŒIêþ4åßñÚþÂÜ ï{ÜñìÖ|a™\wØgê•Õw²ùxUÔ¾W†ìûyßñ²>Û ï7×üðè·ÖCß2/}ü7.?¼ÿ‹ÍÏÿÖl;${_V?¢ð½ÜïêÛg5_ïö˳š/žPך3÷ú†d¿àìûǵ|xQ‘s¯¼q}Ñ‚«Î] œæƒ-»ŸVéZemÝú_ \ûiã³Ý2¤þƬ×öIÚO—úw̧ޱxÿ?úÖû¡ÆPÿê¤oŽˆ•µ~>ÍàÙ@Æ%ð ø3#oy#(;y¹IǬ ŠÕ±­‚à .ñù_#1k¡+Ù&?˜t0¸BÅZ>ŒiH·ø¨%?´IüÔFŠO³Fñû >Í€}˜©ø15~>Íbŧí?,>ÍêtÜÚˆƒÚ—­åû¿Ub×zįÙ~‰—®(pGk\¤Ä­…¶qÐ2žôxhOùñ·–ôêžüDêžHzb£ÄÔŠ¿ÿÀGSWL¬Ÿ_3h‰…öXh‰¥]qàŠ£ãüâÕ–‹?³ñ÷OùxÚß,¾þ Ä׳øú/?ÿÀO¦®ÉÔ5øÉÐ:ù ö5“Dù$Ú—Dû<‘—«ÓÏÇ?ô$“N¦ü¯Ä§mÑñºRÈO¡|J³öÝ’ |*u¤6ûù.«–˜´ƒâ·¬RâÑvH,ZÒMÚ‡—òÞj‰; Í™àˤ¾Là3ÏòHlÙS6›t6ùÙÐ—ÍØæÎ©‘˜±àÎ¥ln«Ä…õJ<ØN=¥çSoþ&‰ýJÛ ¨»€¶@WÁ‰ïÚ >Ô¨»È«}¹ujÇV|à‹É/>¢}À”4iŸÇ–ÿŸ*ñŸ¶_b¼6è¸*ž@õ”Ѧ2ê-iËÕ#>‘;Ä_c^Ñ c¸*ÕbÅhб\­8®ÕÚ÷±Š)àÈí1]ûãÔµÇôìOÏ*y\«Ûnù¢­¿¥´5¼@ø1~dQ%>Þ;ýbÃ7HlÄÖaŒƒºÅï{­Ž­3Šzƒ‘Û`òƒI–Øððbc²_|‡’Ú,¾C)V+¾C»%î$¼?ºÕ/6<ùcÈC»Ã©/¼N|‡’Q.ñu 5â°ŸÿЉ±ãßi:ÎNT­_|øX‰MÙ"ñáI‡¶ñ >RâÃS÷„‰_-ñá)+þC¦®òcÈiÖþ¶b $6¼Ò«àŠ£ãýâêžÔ qu‹ßPÚß"þç˵ÿy+¦ÎA‰|â~‰O]“ŸLÙ$ÒI”M¢mI´Í+qtºü|…BK²JS~JÄÃl?¡ä§P>…ºRhkj¬Ä'J~øÓj$n´¤Óžtø$zÒ;%^éŒfçÝŠï^#12÷KLwêË>ø,Æ%‹ü,೨/Û+±Ú¡/ûˆŽÏžÃXåtHå“Ôš£Õ?ÿ˜&þñmŽJ‡×þ:ÓÖß§•´u ÒþñLüc™MÏÙ:ÎÖoþºÍÖY¶¾²u•ÒSJ7Ù:i¸³ÄÖ3J§ØúÄÖ#¶þPzCé ;f´Òô£¥#†ëƒá:@ÍÿjîWs¾ÿoÇ!QsºšÇýcØó¶=_«yÚ?>´š#5NËÿsÃH:šH̲*ñÃŒ|íÿËôK°‡| }¡‘ÚÏrX³øU&=º–O‡Äã÷pàÂ×êe•7£KûŒTŸFñ™\'1Å kœGbŠÁcãù>¾QbòRÏðM  1ÐsDâ‚Qo´Æ5‰¯c~§?ãù›pXb|‘—¾$hI¢Gû)N&/S(›B:¥Ubrñ7­VûN‡WÒ¡9ƒ¿Ôååw/¸2Á‘Þ,èɦ-Ù›t¼Üú4:rkõR.O­­-x É+<¬}Ñ®âNí'±¤Eüƒ¯´\Çù-S²êÕþ3U¬­ Ú9]ÉÞ±³¤÷úöØYÒs+ã íV<…jñ¯­äÂs­ZœGT‹oün‰Iz$eGBWWâ5KüÈ7¶Ñ(æ–`òƒkùЇÁG$~:ù!à¡|(u…’Ú"þ|=|à×0ÒaûˆôèNñüÒcÈC»Â©/œv„«5-ù•âZ#ŽˆoØ?ù¤#á·Hú,*Vb¯wJ I¯ÄIj•’¤ÇÓ¶ñMÃbHR÷„ƒC²FbHBk4´G“ |4øbÈ!?¦UâGRw,ýKÙ8~ˆ£ãšÅ7>sà$æ‰IÚ7¾šæâ)=ñzÊK¨Ô¾ñU|¥„#âøÄA‰I]“Ÿ üdò“è›$Ê'uH|wÊxÀç¡o’é«dà“)Ÿ¬Ò”ŸRî+’úR*%N¤JSGªG|á“N¥|øÒ‹4ÒiàO§MéÐN¥ƒ3#Dû½ÏhÑ~c½”÷RÞ Í^ðe’Î&íÉ> z³ÀŸE~ý› =ÙŒ]v·ž²sÈÏ¡=9´/—þÊW.c—G=y”Í£leóÉ˧Þ|Ú–O~>¸ ¨«€º È/$¿°ZÇ«T¾ø iKðE”/Ú¯c]Yq*ISW øK «¤UÇËR1,Ÿü´k*¸Ké§Ò&í¿LéKê)£lÙA‰SIº<ÓÀ3­@ÇÔR~ø-¿ûÐQlÅ€ŽÍiùÝ'=\3 cF­Ž™¥âRZó¬úç¯k¿OÏ~ŸŽý>ýê¯W•NUúÔ_úëN¥/m=y´X~J*ý§ôœ¿.SzÌ_‡)ýeë-}¥t•¿nR:Ié"¥ƒ”þñ×=JçØ:Fé¥Oüu‰½Þµõ‡<>¥3”¾ø­ym]àïK}øÜ¯æ}{®·çøáë_ÿ9ýhó¹¿Ïuÿ9|ø¼í Ð|ÿXqnàƒzù=°–O‡ÄümË„r#ýâ˜ð}T¹_µžî’¿üB~(ý:¨}¬[qJ‹Ñ”ÃïáÐÎ÷±•|ŸHþFÒÏQ—WÅãU1E¬Ø!ÀG×IL]êéбåb¡)®FÇÏ„LRëfÚ®„MÚ»bÇDäh2yIЛÔ)±=¨ßCû’›tüŽð¥Ôq:Ò $nm§ŽUkŨ¥2øž=^êóR6“r™Ô‘…LgSg6}–£ÖÎЋLä‚#ßóÔ¡|¨‡ð¡¤CI‡Q>Œòaà £]£½sø1‘‹Šü1”Ü^.ñÔúšüd-¢QâQѼ±¤Ç6JÒ‘¤#á—HêŠòJ<ª®£Ä£:(1Ûk%fû Ä£ªó‹Ù^ 1ÛÁ7‘òѤ£¡-ºCâQ‘Ž¡|L§ŽG í±àŠWœGÇt°bQ‘žD¿N¢Ÿ'‘? Úãé«xhoÐqÚH'N @:‘t"óF"u'–øSµŸ½[O‹IÔ—´É/>;íóÐÆ1z’O¦|ò~=uN©”øS”O‰”¸ì —ý°Äe§=©]zzM+—˜S* þtÚ”N~z³Äš¢M¤3hcýã…F/ýã%í_&ð™àÏ&ðY¤³(ŸE›²HgS6|— }9´7‡üÊçÎ¥®\êÎ¥î¸ €- íÐUH~a­Ä¿êÖÓ~m/¢|Ѡć¯Õñ®ŠÁWB;J «¤SǦU1Z¬øW´c*éRú±´ELJ/ƒŽ2h,£lÙ‰yE½åà™Ó*uÜZ¾¼Œy°u\~:tM'=:f¬Õ1\T xk>Wÿ”®µãdÛkk[‡*½©˜OéI[Úºp¸ô×{ÃuÞ÷é:¥ç”n [é0ýeë.¥¯l]¥ô’Ò?JרkèákgÝ¡ôƒ­üc ù¯›ýωÕüþ}óºšÓÕ|~´¸–þóµ=GßœlÏÅöÚØžcU_ªµï ÄYflG4KÌ>Æ.ˆÏ(ø ž e\C×P~ ƒ/ÃëÑÀnØ2Œ÷x'¼RÇ•‰Wýþ±Õ[¾D&"IGÒö(àÇUêØy¼7¯CâæÁ»)3‘ú¢©?ü1Ð ¯ÆR~|O:^L<¢cØyÀå¡íI&=%DâÓцTµ–…Ö4hH§®tÊd€#ƒß3¨ÃK½™ÀgµjV˦rHç¨õ,ý— L.ð¹ÀçAÞ€î¾ÊPgø ¡¹ˆúŠ€-¢Š)SBS¡j‡fÓRè-mÕqtʺd™F‰Ý–¬¨å~ß+eÜþ÷®[ý׬?Æõêÿ‰µêÑÖ©ÿ¯×¨?¶õémmZ%} nàcÀÒ‡eáýÀµ'•¼ä oõŒ$=IŽdNB‚(Ä÷QàÕ¤Å:¸Ü/Î}¤ŽsoÅG¥C˜¿B©#TÍÓ¤C «äC:Œq#4ð£%>*sóàÇ?¦KOá¤Ã©+œühŠ@&"š%.sÊXÒc›%>õG’Žw$¸£Àî¨n=­Œ×8Òã:ýb¤ÒÆñ´q<ý1¡@ÇGUSÏDò&®Õ1Ä&Bw4éè‰J½1åO¬[Çœ‹E–cÁ ž8æœ8ðÆGzý:‰~ÍñÐOÙxàøžÀ÷hJo"éDÊ&¶JTê˜ žÉ*Mû’¨7‰6$AGR·ÄB%í¡ýêI†ŽdÚŸLù䉃 þ)ôßʧÄJ TêK9"ñOéëTòÒÀÆØ¤‘—¶_OŸéä§ÓÆthOíÊ |íÊÞ }^Ò^úÌK:Z2ÏìÒÓmø²€Ï¢|6ø³©;\ÙЖm9”϶ø-—ºrÍ¥î<èÌ£y´+¼|êͧòIçCk¸ €- ]]…ä¶è˜«EäQOé¢ÃØbÊê˜r%ÐTÒ¥§y+Њ½JJ-¥ÿK;ôÔ_ž2h*£ÎròÊ©³zË¡4N#=­IÇ\Uñä*€­hÕ±q­x«¤§“žNztÌØ¤cª«Öü©þÙúòhzÒÖÃ×£Ôý_táðõé÷­Mm§hëµ£ïªyC骣é%[ùë"»¥‡lýãNr4Ý£ô­klÓ ýcë¥S†ëÿµ«ÒŒ·sæ«tÂÑtÁÑt€ÿÜoÏõö9ÄÑæøásûð¹Üîö·…PóõÑæéj]ÆŠ©yØ/v5¼<‚ßFPv$Gv¸Ë©Qðò¨n¿¸Ô±:.u(°¡ šu¿ѕ|àÏÑàÓ-ñ)|8¿EÐgcá­±µ°˜ú QÀD3®ZÇVM;õR,š¼hêŒWÌ&‰ ]£cBÇ–xД½“ÿøX‰ÿ L|1ÐC~ ´ÆPw,´ÇB[,íŠW}l°qôé$Ò“¨k´O‚öxÊÇS>|ñÀ'N€¾Ò àO$HùDðM¦®ÉÔ5™ôd¥ƒ€Oâ·$Ê'Q>‰òèóö€ßsXÇÇM¦/’)ŸLù)à›B}Sè)Ô—B:…ò)À§@c*ð©Ô‘ÊX¥’Ÿþ4Ú›F~étú&ö¤ƒ?üå:s†ZK륬—¾ð‚+“t&¸3Í„¶,pe‘Ÿ|ùÙŒS6´d«u6ãšC:‡qÏasÀ ¹Ð™ ÝyЙGÙ<êÍ#/Ÿzóé‡|Êæƒ»ÜÔU@~ù…äRO!4ÅêøËEzš/o1°ÅÀS¶„²%Ð\²_âr“?•6M…ÆR`K)[Ê÷2ê(£Ž2ÚSFÙrÒå¤ËÁSýÓ }ß§ñ½‚q®®‚~©€¦“žNzz§V#3¨cuÌèÖ*ÅÒ꟭Gýõ¦¿°½†ö_ûÚkÞ£Å_÷*½ñ}ëZ5Ï«¹Ýž»ýmuÕü;|z´õ¨=Ÿ÷Ž6ßýϬ9í¹ŒñS1Þ µÎ„¶@ÊŽà{cDŸŽâï(Æ:~‰¤M‘ôk}0>DwÝú{c<ß'Rv"ߣ)®pÅÖé¥F<ß™òù›%2öIàõÐÞ)ä§ð{ xRáƒTÒ^ðeB_&0YŒOy¹ŒI.¼ /ä—ÁÃEÔS-%Ô[Bù©ÀNíÖq¿K©«”ïeà(_9¼\N›§ñw+ȯ€–éüÎßU:Nü±õOÀcýóC;Sì øá+*[•C|> °æ“€ù|ÂçS>ŸñùœÏ|¾ °Þ3|Åçk>ßðù–Ï¿øü›Ïwò‹ÿ ælƒ9Û`ïc°÷1ØûÁbÃÊn0‡ì} ö>{ƒ½1Vl¥˜Û ö> ƒ‰Ê`Ž7Øû1r'Ϥd°ÿ1˜ó ö?ûƒý‘$ç¢ì äß@þ äß@þ äßȽ9òo ÿòo ÿòo ÿF¾¬‘ù7ù7ù7Êô\k ÿòo ÿòo ÿòoøä­7òo ÿòo ÿòo ÿƉòF ù7ù7ù7c‘Ø÷"ÿòo ÿòo ÿòo,{äß@þ äß@þ äß@þr&ŒüÈ¿üÈ¿üÈ¿±RֹȿüÈ¿üÈ¿üÈ¿õNù7ù7ù7ù·Þ×!ÿòo ÿòo ÿòo ÿÖÛäß@þ äß@þ äß@þ äß²]Fþ äß@þ äß@þ äß@þ-;9äß@þ äß@þ äß@þ äß²Å@þ äß@þ äß@þ äß@þ­ópäß@þ äß@þ äß@þ äßZã#ÿòo ÿòo ÿòo ÿê¾üÈ¿üÈ¿üÈ¿ü«·…òo ÿòo ÿòo ÿò¯ÞÅÈ¿üÈ¿üÈ¿üÈ¿²Û6ù7ù7ù7e#h ÿòo ÿòo ÿòo ÿÊÅ@þ äß@þ äß@þ äß@þÕ=«üÈ¿üÈ¿üÈ¿¡ä_ý« øñú&ðßÿÔ»O¨ÕwÂÖ»I¯¼!jÿÙ5Š‚±s±÷@‡åþ¸Nì\dÍT%> ºäÙ+¶åö^(VlË›d/)ö. ²•rÏÐì÷–Ò«ÏÞ,Û—n±5¯”}Q·ŸíË&±5{s¯Ü=4êušeS.{¤9£ ·MÕrÑ*{¥±Û¬“{îN¿ûˆjmÇé¼·ôÊÄ&ys9 öåòî²Y¯ ­û ¯¼“jÒv2–ƒH¹§¨“½T§¼ÃôȪFýÓº¯‘}UÜ[4ëý•²W·î/bå£Fî1šå.cPö\^¹Ó¨“{V}·aùFÿ•b×Þ ¶6]òV+@ü%TÊùã&}wo½ë›wÜå׊-N‹~×¥îõ­}[¤Ü‹Tk{Uë~¤EÛÅ«{kM {ºr¹ÿ_+¶;Ú@­—-ûÖr±›ß${½.y# ïD+ÅCƒÜ«tjûž{½]){¿¹[éÔv±Öѵ⋡Cïý,;úHy#Z-û¾F±/Ø/¶=‘òö¬F¿?³îYZ´íuÒ ¶²Ú~ÞÚû…ˆO•ØÍ6Šýü~mÓ3Síõ²z4ϢͳÀ? ü>èðãkT^ªøã;(KoðÍ&o6y³Á7<³e9N9Ð3‡~›CþhœüèšËosi÷\`æ3|ÇCãñÔ}m;:O î(s0'PæðžH»N$}"4œÝUôEå«(?Ÿö͇žùÐ3Ÿúæƒ{>póÁ½:Ð/  aõ/~ð'¿ò )¿ò )¿ò‹ eøªù[­þ‚·šrÕ”«¦\5åów±ú{PoNöShó)ôÛÊ-¡®%ä/î%ŒÁ©Ôu*¸–Ò®¥ä/¥K¡c)tü>û }¾Œüe´ee–ÑÖeкl@oCj(·ËiïrÊ,§Ìrúg9tüy§‘w8N£­§ÿ4ðŸìOô–åtpŸÜéôýéÐv:ürøÏ ÿ è[ +€_Ae+è«ZÊÔB-¸Ï$ïLÚv&}p&¸F¹ŸQÏÏÔwêùÙ€ÞúœMçs0çs.0çs.åÎ…Î:àꀫ#]\Ý€ÞÜùôÅùбŠö­‚†Uа ˜Uü¾œ«Á¹œkÀ¹œkhûè_Ç­f0ë€YÌ:`ÖÑ[©zp×ÓîzÚ]O›êÁu t\Òä§£Ùÿ8÷ÐŒù±ýsÀ±ýó±ýó±ýó±ýóoÿ¬tïÚ€¯Ï¡?ß~uG¹?ì7Ùò&»UüyÅ¿_“ø ›¼MÚ¿Ÿu‡Xàg=(>þªÅ÷P·ØæÈÄf¹‹ðÈ[Äf¹“ˆûìFíëϲыkõEëvˆß½b˰·#ÍbÃ)6{ ~o Äž¥Iûþ³ì¾+Å÷_«Ü/†È›ímçb½Û{Ærí'IJãë’ûFØò5û½á.[˜ýŽÛºC‰”·ÜkÅgÑ~±“)÷ÜÍÚ¾Ïò]+wkÅG`—¼íöú½íî’ûÈHñX«ml¬wÞ]òÆ2@üUŠŸ’ywÙ%o/#åýeµø:jÒ69ê¾Æòy"~ªäJ£¶oW÷=ÖÛðñƒT%÷š b7Ø-÷›!â©J|¡4ŠÂ.mKhù"ŒŸyÛÒ$¾ »´Íå£0DüVнá&±9ì»ÃyÿR)o`Äa·¼;‘·çUâg OˆÕþW,»ûñcX¥ï«¬7è­Úß’zcùcÙ$>–:õ=–õ&&Vûf±lò›´=âÜZíkɲϕ·èµò½IÞ¤w‹Q‹¾ßší•·¦jX+6ˆÕò¦IÞh{Ä™´g>åfѦYÀ͢ͳ€™~tø€ñãƒN0>ú¯’²³Á7›¼ÙäÍßløo6¸Ž#oí^|-e6@SÍcïL £¨²ÿ4è¨A#kEp%"ˆIuöξ'½³ïIgï¨hÜã6ÆeÑA\F!Á ‚"\qI4¸ (HEÜÆÿçõ{ÕÝ?sÎüÿÿ™3þ8'§ºëÝû½Ë{Uuï£ë^tIÇŸñÈKÆ—!è*lƒ'¼0ä‡#;Û‘o8¾ Gf8˜©œ‹'‚ïèŽ_˜¡73…}QÈŠBŸ(ô‰; ì(°cðK þÁO1ðÆÀ[ÀùXøã ƒ>ú8èã8.ñà%pLGp K€.ºè9&Š#ôIØ›„îIø,š x’‘•Ü+S‰¾§bO~µ[ IÅŽTtIE—4ü’ÆX:t騒m:¶¦ã£tüfß]úf`o4ÐdàŸ ôÈd,‹±,0²°5 ü,ð³àÍæ/þ°sàËÁ÷9è–ÃzÉ?wP¦0yè6äá++4Vô·‚ÏX>ó™òÁ.„®9…â3r Á(BF2Šá)†§žxJà)®=Ëà+ƒ¯Œïeð•ÁW_9|ÛejT‰}•èP‰•ðTr¾Ìj0kÀ¬³Ìl¯Aÿ:樞:xê੃§n»L¥šÀn» »›°» ›šÀjEVøñ·øw¬¾ØÿÎúbF%ò'‘/¹çEî¹ëÀ‘‰¼Gä<"·9È_Œ|Eä("?¹‰‘—ˆDä¬Gn!b7#‡ùƒ‘7ˆ\Aä "Þ?2Æ7âz#ž±ü‘q¼{ü.bw#n1»ˆ×ÝcõßcŒ~´øÜ=6w˘܈ÇXœ9wÆà"þ±·w‹˜[ÄÛƒ*ÎþLÆ×ÿ±úrݲƦ£~µM½í'ëc8kÌÍTµ«·«ßƵ©ßÐøÉßÅ9jROWuæö¨wH:Tý %®úÓŽ÷E¶Ë-1ñjQÿRÔ­[o¢…øíšxWO¼g(Þ¯ïô‰w^ÅoÖÄï¶DM7Ç»²6x_V¼³ jÀ‰ß7;Þ»õõ,5=e­ ?4&ñ¼÷–õE}ähø#ù‰}‘|ÄÇ&x£¡Fn´xÞÂã)oe‘؉~a‡åí,=áIÄ®ì‹ÏrÆSų¼8ÆâK†.™ïñ`Zð‘e‰ÜŠKÅ®TüžÊX*4lÊd,S<±5¼LtÌ„¯ÿ7àÓ*¾W¡_1¼ Ð50UŒˆsBðkÑ»…¹lA§Æ*Ðdž6?¹׈MœkDv ü-Ȳ¡k -Èl’[u¹àÛ¡µc1vXYoVŠYS¥èQO!c…`• ùÈ´bk°YÅ|.Fï øJÁ,DçjxlvyK/»¬ ‰Ý6há­|¶q®Ì2ô´ Ýa*Fßj桟íàU@׈oìðÚða5Ÿ«ñƒúja´â;¸6ìkäœ ¿Ø… æÒ¿¬ üP‰-v0ù܈ØÕ(üŸ]ð@ߊͭеbO+2DŽìøwlßú¹o}ì·_Çö®¹îí]{Û»þß¾wýß´_-ž™v?nýÐU#æ½h¦«^4Ýêýq“ªiÔ­jˆú¹ÕÉtëGÓ®êä{ºý>¾[Õõq«#Ú«Þ£ôSµ«zù3Um£%ª^¾z¯r¬™ïx·ÒGÕµ«÷ÊU]Q“ª¿Äíýr‹ª?èöL»¬u䨟ï©êUÛTÑ^õ¦YÕÑ_áÖ¯ÆOõ¬éõù&ª¾5fUot±¬9êì_c•5NœuüÔ{2í²–Ÿ£·ª‡d—µ°uö=Õ»eªÖ~¯ªCê£zÚØUÍýõª>ÒLUÿo¬Gêèmã-k$‰÷PïÒ,‘5øõ’úxt‰/c‚8‚›]t Ð%@—È1‘c¶V w~KB©$ü– ]2²’OfRÀ)·šÔ2=HEÔA™^¤ÁŸo:²Ò±%ÛÓ±5Ÿ¥Ãoñ’©G¶ÚÀÏ€&š ü“Ñ-Ó‘,lÉ# [³‘~¼ÙüåÀŸv|ÕØbcäˆ\Ÿ4âÜ<0­ðXÑÙº]¦1ùè”ÏX>ç¡-»ìB¡?Ø…à[nñv™æ”ÀSO 4eØZ‚neð•ÁW&ÎÁW_9|åðU`%×I%vU¢C%úTÂSÉùêí25ª³Ìü_ƒ½5ø¤ÖÁSO¸‘?‰ÜIäKFždäG"72ò!÷üçhùë×™çˆÇ=§a592y‹‘¯ˆ<Åê!ó#ù‡‘{™wˆœCä"¿hóy…È'DaäGËŽí§»öÓ}<~_5¨ýTÿª™ªÕaõúaY»ßY3t»ªO²DÕç÷Sµ¨­².ŸèxN›¬Oâ¨É4¨ê“XÕ{ª=ªæþbõ~x‚ìÛ(êû´ËÚ–Ž÷G»e­ Q?ÏwPõQò”Ûz¢¦‡è?(ê}˜8šÖËw˜Å-4˜cð ¬?$úC‰š!Žš•|×À X ßuX,ûDÅ1i¬o±v…¢W$þŽDïÈ2ùÎwˆˆ[D¼Nx!èÙ!{$†€ƒ/b8F‚‰œø"±!r…ì•_"±?Æ£c$zÇÉçdp#ÀJ2Ñ%QÈ’Û‘qø;Üd0bøœÆx¸ièšM"ó™ŒäX8oAF¸‰ÈÓ8Ÿ oçÓÑ?šbŽéÈËä\&:ds̆§€ÏÅœ·"à _-vÔbG-óaE¯b0«Ð½ÚZhÄyô,À¥Ð5p>WÐCÓÀ¹hJ¡)§-ø£ú üV6Ž6äUƒköliÀ&òk·Œ\ø[°¹…±\ÎÙá±C_ŒN¥Œ[±ßŠ­Åà—"»‚¹+¿ÜRøŠ¹¬ø¶ÙÕ`ó¹Ì øJÁ,Ä?ÕðØÀ(Cn™Ð ¬ ‰¿lÐ6 ½Ÿmœ«³ ìBWpб½šõÛÏvð* kÄvxmø£šÏÕBè«ñ‡ÚFñ\k ‘s6|`_,·zíðÛÁªÀÆJl±ƒÑÈçFllÄ®Ft·Ãg’íVln…®{Z‘qì÷êÇöýÛ<ŽíûÛ÷?¶ïï#Ûóÿ}íùÿ·þ^]<ûÚ=þ¸ý Ö«þa&·¹CªX»¬w%B Q÷ÆQ?¶GÕ5©ž‘KTOoÕ'wêéåÖ¾Gõ5˜îV3|Эî•]ö6pôÍ1©Z²ÝªßätYGÇQO¶WÕ¿š®úé´»ÕÀòvë+¶Bõ¨÷Q=ê—Ñ£¾CÖ^tôªôRýì²Ö¬èYéèWoqë16¤zéšT?ݪ÷ÁÕS7AõéY¢zõx¨Þºenõg=U„2Uƒ¶GõØõQuhÛÜúy©zY6ÙÓQ3ËCõ!³¨éKTmZOU?«LÕ¦íU}w}TÿL›êq¿BöÒtÔQ÷R=Tß„Y[]ôØtÔ®õ‘uÖõ‡ÚTÞnY‹ÈÑOÁ[õT°¨º[ d}Iѳ×Ñ_Á[ÖÇrô5³©^ ݲ֭£¯·ê¹`‘}ˆ´éªe¯ªGé©j¹›Uï³6U³k…¬í.jw9zþz«¾¿Ð]\&û9jy­Wõ¼¼dKÑ ÔQ׫CõIÛ®z7x«þ Õ3ñ™+dÏ#G½//Õ/Í¢z/õ¾Dï4QO×Ñ»¡]õK[¯z{ʾiŽ^HŒ_Þ¦z8ô¨¾Hž²G°è‡À¹Ð2ÕÛ¡[õl°«>i+dO`Gý\æ#h¦ªïe•u2¯\¬êç‚f6`S 6‚„Þ©“±;5x4x4ô '™A`YÄXc0‚À 'œô A¿ÆC°= =âÑ'¼PèC…þÌEL‚ ×ÃÐ1Üpx¡ Ç/áЄƒV8¸È@g3:›ñƒz3ôQè…>QÈŠ; ¾¨Aîã“bèbÐ!þøcá…?ú8èã ”é@¼Ð¼Ž ânt Ð%@—]"ÇDqD§$lMÂ_IØ‘ m>«à{2s’Œ½)ÈJ+=SÁI'=RÑ#±4ìNƒ7YéФc{:ó—ŽoÓá·€Ÿ]Øa?º èŠÁË@—,lÉ# [³ÀÏ? ÞlþràÏ;žÖFØ9`çB“‹y`çÁŸvØyÐXÑÝŠ}ùŒå£_>þÏ;ìBäò¹P|£EÈ(BF1<%ð”ÀSO8ÕèW}|eâ_|eð•ÃW_zTr½T¢C%:T2W•ðUr¾Ì0kÀ¬³›kX5ØP‡Oê੃§ž:xêà©« ì&°›°» ›šÐ¥ ¬Vôh…_ÄÀŽ"€¹ò±¾%¿Ÿ¾%ÆÞ¼‘ %÷ù9ÈmŒ¼†¹sæ2F#r#g¹Š{ŽÂÚwä%F>"ò#÷9‡È-Œ¼BäFaäFîàž79ÃÑò‘#¹Á‘9{.`äG‹ÿÿQì´˜ß=Þ±¾ˆñÅôÿï1½¸Î;<þý=uÌn=y«Þ‘œá)kÆ:zîöª~»ðŒêP}ufª~ Èí¥z~ÙeoQûUôËø8?YÏu‚êïÚ#kl@Цꆮ5÷EÿÑ]ô€5HE}ÈÙ"fè}DWG=HÙç@Ôÿu½ƒE,0${ß8ê–r¼ÒKÞ:XG‘|ÄGaðGb_d‚ìå v¨EnÕEƒo$¸‘ÈŠ>špdD‚‚b¬WõÙ†pd¤1?qÐ&C‡.‘àDÁ“‰]q`'rýeó9ÌtÎ''ÈZ—Ù`&[dÏ‚Dæ1ÁLæÚ-åsr™¬‘šÊüà +ò5x2‘— }.4!èS‹ïjñk¶ÀD¯bt(À®bèkù^…_ªÐ©`Ü,ô`Ôò½c´¥àW£[5öÔB[í-· [˜çlØÖ‚Ì qDn z¶`S5v·€×ȱ¹-ȳa »’ÁlDö4âóFìHE;iÂoФs.<;¼éÈÍ'CÄè’Ź,h²Ð! ¾,°³ Éæ/ŒxsP2]r°?;rE¼½yàçÁŸ‡.yø=«ð!6ç3–^ùè•v>Ø…È)äs¡ø F!2ŠQ„ŒbxJà)§žèJ°½¼2øÊø^_|eð•ÃW_zTbO%:T¢C%<•ØVÉùj0kÀ¬³Ì|[ƒk°¡ž:xê੃§ž:xêEœvØMØÝ„MMèÐV+´­â¹)b ñÏÏí3¾û.ŽÿÊý{÷½û?â¾ý¿cÏþhûõÿé½ú?Ú>ý±=úÿ¿=ú?Òþü¿ëwùF¼ýß¶7/Ö±ÙãÙŸ¬WõÌM8JÏܪ®:'FXd¿G2/գ̮úæ"¤ê›»Xö\ý¨ªþº‡êSf—}ÊF{Ëþ4£T¯²nի̬ú*¬—ÿ-îÍÞ6Ù·ÇÏçø©þ¹èqÎzÙçÊߪzè‚1=Ær½EÎXxÆò}~‡ÿÇ-P5Þ½eŸ-GßË6ÙÛg<ó4aºê¥Û!{éN±5òBà™ˆð…@7LÙs!pê«‹î“3ÉæÖwÁ[õδ©Þ `ž{XÕ’·¨Þfݪ_<´S̪_È  ðQ=ä­ª×ÙvÙw`¾ŸfrëÉÀy_³ê_¦z ´«>·ŒÏjW=ÅÄçnÙ^ôÕ½½áE~ÍÑÈJj“ýmýVÈ~ð¢X¬Mö:ðC÷9žªŸ-ºÍ™©úÚ2‡sÐqN™ìÌÑdQýÄçŽ?8è˜Á1ߤB_ôÈ „6 þ@è¡ Df 2MИ&c&l1a‹ lôÁBç‚9̹`qþ`Öt(z†âë<Ös>çó‘Ê|†¢Gö†Á†?Ã8M‰èS$r%ünÁç)Ð&—^´ÉГ‚ì!y)Â×è— N&|ið§A“†¬4èÒ•m´ì³0/Ö€šz>çbsœÉ1?d"7ºLÆ2¡Ë/›óÙø'›9ÊÆžlÎgcS6¹ðæ2žËx.|¹ÐÔ#»Ý›ù^Vt³bŸû  /@FcÈ(«ðA[„¾Eø¦»‹°»»K±«;Ká-EV)²J‘S o3<åð—Ã_.>ck9¶–C_Ž_ª «BNzW¡s:W¡[rj±¹ÌZô©EŸZèjÁ¬…®õ߀ÜÆo`¼ñ!Z°©›Z°©œôjA¯Vþ1”øgä#GËCŒüÃÈ=Œ}RqS<2×8rßôí™9„ÈŽõÓûÏ÷Ó;Zü{´¸÷hñîÑâܣŷ"®5~3-âZ#ž±ìDZ«û~­ˆWEœ*bTŸ-.uÿý´±ç‘1ç‘{¹îû¸GÆ“Fyä®7þ£ý[#>d 9ö]…Ï<þc= }½eÏyÑûÕѾY|žÅœÌò–½]g1~Å ìýyº\1$ûÇîQ}<¡óó‘=:ýøóG?þüM²wŸ£ßu‚ìIˆ.cÁÛ¦zɈg&ü&øý¸öLŒ™à ßÅss¦ì+̹d‡œ û$ùƒl‘Û~|Žö”ýDÍ6Ù/0„L®½`dEƒ ,˜ñø&ÌX°â…ŽðÅ ôà/I<ã¹>cÑ/úhøÌœ à˜¯y)ð™ñ¿Üèb…`fˆg(çS OÏQñüA·0Æsð£™ï9œÏÀ‡ÙØž‡~Iè›Ä1™)â¹fó'büœ/ž'È‹….ºXñ ÆÎ"dÄŠgØ%B.ô%¬‡4¾g£w r˜¯\|‘‹¬ü=òöXƒUèTÃX>z¤ ;°« ºzìÏd¬^‹1øëÑ¡ùÙØY~2›À¨ÇwMàÔã‡/¹Ò,°Ð£[êÅó šzô/§þ"ñ|¯”ñfæ©úrèËÑ¥;ê±»ŠóÍâ™&0U‹nÍŒ×"»ÌzìiFŸtlxð5Ã× m36N²Ë>»­m2rüûwýNV<÷þˆ{mÇ~#{¬/äÿí¾ÛïeÏí÷ü›ØÅ~Û¿ë·°GÛc;öX¯Y<þ˜}Kù~úvÕÚñ3w÷/ð½à÷bÜ þ¬ûV·Þ¥Þª¿u›ì]êès æHl‰í#±a:Œ‚g£zÝú—¶©~Ø>²¿åh‹[Olô8;AõB„ÇoÖ½7>óÞ.ûeŠÇ­/G?þü±yç  xc7Æ ÓÎõS½³‘7–ï㘋YÐŒC§Ù¼d_SÑÓ4]ö6&0o°eöN@2B8Nä8]&Â?}'‚éŸX>í²/£¶Ow´“°u˜“öÈ~§aè|îbÙ‹[„“‘3Ùêêy:™53º) ªïi·ìë8?LõSoƦ"g*ç§1Ó3 9Ó87s¾ŒûZeïm_Îû‚í‹L_Ægu¨^£â3òf¡Ç¬AÙ{t6ô³ÛT¿Qìž ]ŸýÐÁZ¿^ÙÛÑ=æ ;_ÌÁþ9è6=æ0—sÀ Cþ°üÁñ_¬úuƒá†?ø!=Ãðq44>‡±fð3LÄÜBäFÀmãèŒì1Cg†.}Ì蜭™uÉ÷htˆyzDƒ m4´%‡ehS„?kD~O,vÅÁ“‹œx°ãáG÷xt¯÷‘!S|¯ ¿ùžN6~N䯾$¾'1žVÒ Ï’9Ÿ‚Ì\æ$…ñìKA^Êa¶¥².-ø; Ýòà+å{tiÂøÆ‚O,"/@—*!ßeb&<"G€&šLh2±3¿d3×Ùø¬œ¿lør9WM¾+#—ó¹|ÏÅÆ!ø‘Q€Œt-À/Eø¥{‹Ð»hH†‘ÅØ[ŠŒRdÖƒY h)òJá/…¿þrøËñI9v–cg9:”–!göU¡W•°—9«BNrª ­EŸZ‘£ O-xµÐ4#·?4 ³ñÆo`¼¡]†¤-ØÔ‚M-ØÔ‚ïZЭU+bMñÏÈCÜó÷Ú¸"§¹„û˜ˆÓ›‹¸üÈý/wÿ£ý-'‹ØØˆ}ÝkÔŠøõÈý*÷Ôˆ=xòȸñhñâ?³÷ôÏì;±çäˆíŒ÷ñÝ÷“Œ¸ÍˆÏ˜g­â/9UBãõà–ó—‡O›øÍÐ7CWÎÍ‚{š¡mÆÎf>—£OòK«´|¯G·*tmüØ^/ð°µ~H¦¸ ø šfqž9öy`¶÷á¿€q¦½{zÄ'ý¥çô¾˜W´33}øæÏ›µ ƒÖ¿G<8Îø®ï\þÞ®7nH |ûü†:¯ÏRŽDÒŸN½%4I+Ó.7›vÕ=Ò¦ŽÎ™óÀO7ß‘3è {yâíÃÿè8íŒ&¯ØÔñåÚÆK–U½8û8í”Ë?ùj\ÈEúΕ™ Û"K{ßüañ‹ûïÒŸŽ¾÷® ÏkSoúîÍÝ;õ‚}•ožõ¬iï^)÷åž1‚SÛØ8ã¯íþë;»oyå»×ô§Ï\vý²/oÓü´{v‡“¦^z`âÀ>ø||k"_Ê­Í3äj_<õŒó^ž ÿäõÙS^×»ÞÝ=î·¯NÖ‹®Úñ̨«á3I¾ûßÔF©müuÒo›÷vè;_ÜRðZÈ*½ëî‡FŽÎ™«ÍZzþÌÔKóõ’‡'¯úËE“á·:ø_ñ1ôZÌX§ýï‡}¿ì§ôÏ¿õæ†åzWõ-ûÎþj¹æûìŽ ®¼C/{¥ä¼kÂÞÓÆmýña¿Ñà´IœŽže'ìXª½ûŠõ·G쇿°ÖR ÞUf^ûâs[õò'çÕnö~±ƒ~í9«O_ºû€s~ßß‘Zýð*mÔáÛ?-ûý÷fÙß[h̯Þe›vüÛžãôò'Îú.÷Ô8pä:YÛbkýü‘ >­×û‹ô{ÿ|š¾sͬé^[ô®Ö¤Í—}µP»|褻º_zK/+¿1ÑÒ=]›,ýŽ\k7.¼°¤Ã¢=ÿ½9¿m;Cë ~fÓœo}ç›óþ~éâC¾vYÂþ‡c—ê¥=“\|¼v®Ôß´÷K9ÿúįv÷g­·äÌ;‚wè;·Œmz*æ½«ê «žÓêô£šmŠÔ~¹ô½…ýæJ§_zoÊÿqhì-úÎO‹Ö¾ÙúžÞuÝàížëÒ|Oyß!zIßiÛ/ù0W›$¿ƒ#×…>ï/é£V¿«õ>ñËçï¥ïüö¤à9ÖF½kYÏ‚S§øhÊùÓKoøà¦æoŸtÍç—r]è+N}w÷x—_ß,6ïìõ¿n8´Àî뼺>¾;b¦i®^vþƒ3מ¶~¹ô¾ïnýeõ—»=Ož}m©Þ7ì®®jœ«wýRlzÏþ¥^ví5'ízèøäºèqLÓçu°Éq¹ÍÓû†_ÓvÓ„²}3óVéO{$²mòŸµ9wÜzÞçÚD¹~Á‘ë¢gRU~ÒùµÚ¦‹ß\³'Ùßyßè;åšÌ×ë^Üa¬«‰ç°4µ¹-™_˜Ûè\ßÎûÄ—r}ô~é³°Ó_Û”´$¤­=}¤^]~÷ì×Sfëå ³&Ì×iÚû•\=%é›ì9¯«MMÝ#+ºI;Q­ï¾á«üvoÊrÚ£®-xؚ㯹D¯öƒ'×EOûáü'O Ó6Í·§Ïxîz½ïÔöwçÔ».¿êŒŽÖh¢=ýøIzeÄM“Ïxÿzøä:èùÛÆ¬à°CÚ¦5ŽbàoêþÐçõý˜·—8hÌ£wÊí½¿U¯:1ø¦7úÂ/×AφÆ{[GîÑ6}Özzߘ »yu©ÞåXv>zåÈ÷Ó¾\½œ÷ž>Ç‚pÎûæS¿ÿ°}Äs.»gœtÛ-¿|ø™ZWÆý¹rü½«þî¹Üµ¿Rëa×ø5KËÇh›çœxÑç‡9ïË}s[í÷|r–Kÿ‘âÿ³6 'Ÿ~ö½üS–îm®yüJ­‹Ï¼VÎ\z–s]o®z碕cWè}±¡iuSô®1Ù¬—O z~úÊíð©ù¸áצONtÎçf®ª‘÷û:ï·}y‡†÷¾ß5Ÿ3rõ\¨Í,ˆÝüÍÒ_ôòâ >x-¤iï>µ>ö<5~÷Â-ÚærÎøÛÝ®õY·î´«÷ÞìZŸ¡Ÿÿ)÷ùQN»*^XuaâüÇÀ‘ëbÝñ·ÿù¾Ûµ¼—T×,Ðû®éº×.^«wY+¯z,ç.½*ä¿]e¯‚^®‡uR íƒø#4§¹ô¿{’í>¯‘[ÔúëYåß»ô¶üùò»=Ãó[]ö潇öÌÐÆëzŸ\/ënÕ~Z·y‘s½| f»iƒÞ÷è­O:ÅÕlÓÎWã6qùìÚ Ž\ëúÎíýܬmI»xøßo¹Qï[yÆØ©‡.Ó;Ëž]”î\u›âü7Ì^âZçûåzXwpeØ£ï¬Ó¶ÜœóXý-—è}ŽÛtÞiùáÌ1/¬ÒëçÌ~9ï¯Êû„soyÎû¬ÅWÏÖûäsLïlœ×–›ç«×;ÂŽføäü¿ê¸ Ÿî\Ç[¶ Æ »×u}&\¸DïtÜÎghWªçD}ÑØK慨Õxî€'×ëúë_/³lжz¾vpðŠÓ\þ;4æñ¡ËžÕ;Y„­½O:ã¥:ùüÓ¦ó¶_®ƒWÅãrûkÚÖKBï™ÑÞ¡÷Ÿ>X}ßõIN¿÷g›˜vÛFcà—óþªx:_|™¶5u<+íçõÔ?=Íšûy¹su®¹ßV´ïKÍO„ Z¤×ŠðÏc³›>j] Õ–‡–úi[oüqBQíYz¿é¢Ñ«¯Û«wþ|mÇoó§¸ÖÑ~5ÿ8yö!ÎëbëÊoZüÙ½?Åó/½«\×…÷•ß¾ÿ€ns„K)¦½_«ùW÷+5ÚVE4µêý[Nyfy—ÞŸ_:yÊzÃð©ësägñ‹µm“¾œ}_ÞëÎø©¿úÚö‡Ç¦¸î¾þŸ\œ³ËˆôúÐ'Ï ô®ÐΓñxêþðÚG_\ãÚ6ËŸå éý]ßš1Õ©çO— {Êókãy«×‹i{õ ×uøµºO¼Õú$!¬¶íÎû|vÜŸ¥÷_å;áæ_‡9ïë6ÿõ­©¯;¯'ç:ýZÝ6ä=øÚÜëtÛKß¹ÿT×¼¶½ô·úö8ïW÷$·þ4—6]Íký‡ Þ¿$˜ÁS÷ñüþmÛÞ¸OïÝuÀå§Û-ϺòÒÀc}ȸM»X­×yßG­‹WED¡}x¶#té3¿´ãÒïêámÞÿ2ôj],ºñ³©ÏìÖ> ïÏ)l/ÑûŸ:mâ㳚]÷•+'y_{ÍÉ.û¨õPÚrò¢ŒƒÎëûCÏ÷ëþî™9ˆÑ;#¯¼í»S¦k—*=êúÞyàž§r\ñеND”4)]ûðņ/Ÿ/}Tï;ë·Ó¯Õ; ª>žVeÄQƺtŵÔºí4µ÷µ½5wÍýz¿X†·é-ãæŸq5ב#ìm‚^Å Žp¥Ðy_ß>åù€åwÖûwÅÞZŸàºçuÆ¿¼Üˆ{\qùµ†õþÄ-Ðé‡íéÉï;þ Þ?tvÊŠ¬yzgÃEßC¢ùßRÒÙºF·‰§û‹]ëñ€šÿáúîԶ;Ô·éý‡N?}å5ÓõÎÖ—ED¬×‰¬ìœЫy>qkâ¶;Ë]ú?Ùìóõ]#\ó}xô´ç¾©w^}êœs¦»ÍŸŠÈVǾaq®ßío_zÚƒa«\ówh™m÷·{]ë·â¸©î¿Æ™§4œ:øÑæö¦½CF¼ìïÕáÒç‹‹=÷§õþƒ-Y×ÏíògÖÓßÝ4Åé†û}¯z/îGpT¹ò¦;¾ÊºHÛqb:+ŠûÚ·_à3^ï Ê |a—Þhä—C*~¬©~è³ MÚŽ)‹B6Êùœèß¶×;ûk½“èóåëµ¹ê:nŠYwÒ¡ûáWëÀ/»ôãOÏwæU;¢'|’6ßùœèß_¹òB}©ˆ"­éÚlbçßä§7É£+¯Rñå õY·ŽÖvØ·üzpe¸KŸo¤¾ô™{ãÏw‚Þ$×9|rþõ®¦¢™9×ÑŽ®I×gžü<óøáèÍÙúÒ7Ï›}ͽyxZ϶ïÃà“ë@QÚ…ÎyÜññˆú§|® ïøhølçü-ýù‹ª;B0ò9½Yl7ܰÇu Éu±v©6㔗ô#S#2}ÿæôÃÀY Î^9Ã5ê~®žKzÓ;U‹¯Œ>Åu]#×ÅÚÔì$–‚ó¹³S¤ïO.vúe`úÙÍQk]ëlÎ ÇmÈ[¨7ž|[~€ïKàÈuñÊá˜eék®3òmgó¢W7†G¹ô›ûIÊø?ßâzu†©]b<ÔóÄyÝ}#×Ï+O=xðÝ@mç“¿¿äÕbçúˆ9ó¼È¡ÏõÎw~«ž5<Ø™ªûürý¼Róô-ÃÊ&j;Õ­ú@¦÷UQæx_¯ßu ¡gݯÐËõñÊÅõ;ϼd™¶ó§§wì Öxxçßs–ÞuŽcã@o,ÞsïTß3¡—ëbMïHqÇÑú&p3©¾>Py­ÈØÏÍNw8ã·¦ëOš2pÉXøåúXÓøsÊ#÷߬õElöùÁÓõÛÁösí›\þ¾ïÛŽ®âœ~jžßœ·ýøåzX3ê‰ÍÏé}ß©uÙW÷¶í‡Õƒú@‹×q[_­wZ…á¿éÍõ—ÆÜX’hÚû­œ÷—{¸eae¢s¾úYzíÈ Ò\ó~]Xæ¼)ÞzgÔgqI:ŸÍ×<óTÄö“À‘óþò4ýÖò}9×O߯åSÇã¯Ü~ÚeíùMï”û .ýG~0ö¡î'ßÊù}鑵Íë_˜£õ©|~`þ5í_,|Æù\kºÂ¡ôr>_’ëOëŸ1ñùŸª‡œùèÀS)ÕÚ¢kôι›îšº¿Woôÿἑ&øä¼®&É.¯Ép^‡ýÂËžvÙÛ6>ä¤Ý®øB=}’†ÙŽ@ <9ï«_ßÙqÂ×9íî¯6÷Câ0×z;¾üEß»æQ­÷ Ô}µ~ªcã <¹V/9ñÒ¿ÕúUü>°ýƒÊ˜ëf»®—{–<Í[¯Ë9$®Xøäü¯¾aé}§~¬õ/èï»âí‡\×Çç?×ýüÆÇzç“1—Üxu”+¸Wâ5®çäA¹VÇß!2.­yüi7ŧÈüÇ¥ÿ’¯Â/›¿Êu½”ó¿úŒÄ§þ}‚3îìýùqź>phqß²¹V#~×%.|rÞ»;‹^=|Å‹Z¿Ìô Þ)׃Þôqqê7@/ç½Ûšvý¾-.‹4;hÀåïŸî[Þ¸ÁרÐ;Õ|e›v‘×+î?(×E÷¹/ïëì~Ò¹.<Ã'>r\·Ë?Šû<×õì¸-dóèŠÊu±ê£û-ÛzSϱL¬wéyøŽùöôóyDÀ<Ý|Ý»÷×§ ž\«®ÙOYý m`Nõð³·­t­×Ã&ï 'ü wŠíÒ}¿9󛿗¼õÉ AF Ž\'«"fšættÙ‰w¹Ó¹ôúq´ÇÁ°“õΛo½o`þ!½ùÖ ŽÿS$÷÷ïäúXEðXfÖµÚ”èg—ÏÕ~þt2½3w¶†Šz“ØUѽ\+÷:°ÚÀmÿ‡½óïªÈ÷~N:!! é=„ôNi„üCÑŠ˜Å”Õ芋ºbhŠ bA£®Ê*º±ƒXPÚ_@XNÀUĆ„¿"b ˆÔû™3sÎù›‡½ûÞ÷½÷î¾ÏƒÏƒÉdf~ó›™_›ræ[zÖÆ{ôNíóŸ?º%Ì–ç?]> ëÃ`}æÇ]G<Þz‡zR.^{rë­#g8,yêx¶ç¢†:õÎ~wî9ñC¹=ÊnŒ”ú ÏÜÿ…󞦂ž”›×j‰Ä7ÙóñFÙã=f[óÛ9pÚæ³Ê‚ô—¿ü#í(VãkÆÖ8þ,ååÕï2ÿ²ÏËiãûCjwîÙgÅuqbo²×kF˜p®e?gÄß¹1Âmßög)7¯Þ 6°Ÿ²ùüúû×=ëóôÎÁñW¬™ÿ–¾dêÌ=z®åϧUd‰’í/–ò²úÇŒm™ ]ŽNµž5×)­ºí´`}Éö韬YmƦ= ¾”“Õ³ö$Äpt&ýøÕ÷N³û•é»âô—ý-{°TxßCú´Ä~Bõþ_¤œ¬2–‰Öøt–Ý5áÌŸi?킟úv¯¾4È s¨'åeÕÕÕÁkš2¬þwŽwüéç].½3ùÂ&Ž{U_²ñÅžZ™gÙ㙣â½GÝé²ã™_¤ü¬Š;/vûbWëò÷˜—Í÷¦äý^_2C8ÌVký>óøN±b³íá/RnVŠáÚãèì÷ìÐ;c6²ðîçõ%ò|ʳ•žØëÿ_¤œ¬”~×ÑùäÙbGÉ–7sÿs„_ÛYxÏÖÛ¿¾8qÛ›×Ùòö‹”‹W0z!®G§±mw†Þ™xÛÖÓ×| /™xËŽæ•Ý3¹Ó–«_¤<¼"Ž+2;:?üBì°èjµä³î~û/ë`í;”ò±Lxé ÷Úý#ò"{ž+FÍ:s¾­/eÎ3_óm±÷±Ô>žÍ”›¥IÆÆc·X•ÕŒ´õ¯²bÔï¢õ¥êœÏ\/M¯÷ÜâµçS_Ê‹¹Ü-Ï ôN±[—¦/­¹z{û+§ë3SŒ@šòR>·úTå]^îØý°X0Þc󟻬ãçÊ[-;¶ôwq¿»spˆ¹.¡¾”“Õúf÷ª[cé¤w¦|•˜õºe§ÍóNsßõúëïZ–·µzÿ!)/Ï_Ï–è·¯´ÖU»?ðòû±r³ÝmþäkŸÛ\¾ðÕM¿×ÚÏž)ëCOÊÉ¢wC…YëÜÝ{gVTµè!»z.¨úP_:;3µÿ,/}ÆçÕ³ ûˆzRŒÅÞ»ÑæãplàÆÛ~\ðDTMœ¾ôþ/—¥Ûõ‹W’rñTÉ¡=¾âp…Pïôn¥…»ô¥W‚«O7õ唃ǶŠ¡UW¬XÖÛýð=ýšÄ‰ÖxZqí!)ÃiÛaWú#²ØóÑïÍ«Ÿ{£Éæ_ìÞø†˜ç’æ: zR>~á>±r²æÃ5ÄX Øü þøÀ%nr¶ÏkÅëûC¬õÜtyÎmÛÅ^)7ó^Š+˸*Âß_ŸÁ×ù¿wáD«ˤ^šûŒÔ—rrïûßW®ÚëpL¾¸5¶Ôî_ðÙ7uDôXý3õÜô»S…™žò©mŸz¥üÌ­¬¯ÃuæàòÖo¾·ç»ÿç·dÝ©/3”|3µít¯”—9Åk/èp?š~Áâï{¶ßR^ª/Š9Ô»åv}Z³q€Gy)jÒá2ÜÓiÄ¿A7¼¾év}ÙÑ=ÂÚòÐ+åáRÕ®5¯—^·=éǽֻZ>Ð7žgÇõ_t馯W[û¶Ë}0bª¥×3½†ìyqçóv|ñ«!7ŽÙÆvl‘µåš¹(ö¬] -ùéØ>.2¨i޾LíÛš÷5L½¶üȯ†Ü8îûkæß®YǸ¤-ÐæóCßï ·,Ò— 3[ùŒ>M,£°ß¿rãh2Žíþèpæ7EïøÀ¯Ü™zP_ž5ýÑm½ÕQªäp*Ñ߀c[ló«!Gއ_qQüR»?XÕ˜C¿Úë‚­g~{ÿÄÖ~óòs' nÅ 'þ–:ãÜ¡gÈ™ã!gòú'žßãpÝ'.~|«wlÈŸíiùÁåW-µnóÖ¸Xç¿JyzDÉë!c¬w¼uvJÛm_éËﺣ²Þóë<Ð{²öݦ!\gw¸qúÊ?ëóΪ¬šá´ÆÅòˇ¥\<ý­þÊÅ:.e?­ù˜“ôø‡çèËYäλÝÞO9,åaÁ;Ab…b­Ÿ\/7¨uL¦Íï”»6ÖUÍÕ—¿=/zß…›~ÆÜ·´åó°”gG_ðèÞ{‡Úãùr‹¸ w\ú^ôÔ¯ÿnéåòõ >›¸È:?™ñÛóyèIùX´µJìÀC'mÇgOèç¾yýúò­G"^þ5¿Ón ¥ž”‹ç«ß3V&®Å1""³Çø¶0J_¾½éý†Š#Ö9Äô3AêK¹xá6#¶í‹8ÕN™lë­<'·åô£Ú6Yë+Î:"åa±ÿÜ~ú“èÜGÓ;Fm:²tu–¾\Ý£˜nÞÏ:"çÿ¥ßÆW—ØÚÿ€=/££–®ÿásL9¤¾”‡%i× ýÃA‡‹EЀ ¼á÷º'kù©å,¦¿ô +.Tç v\rDÊÇÒ•°R¾×ázR¤~vÿO»à¬+ \–Ý^‘óÒ‡µíýY¹Î³ã’#R>–—ç'ª ´ÇU Gí+ð—;gÍÖÑúŠ;·|ró&Ö‰7†u…×öPOÊÁŠ©ÆÃ'Þ÷éå³ìñpÜ–tNík?gŧæ_žoùË‘òá¼tù[Õ÷?ˆ¾¯»Ï{I½)ÇzGÉшWWÙý:îœñâÔÍ–]4ã Ë‘òâ4– #.µ®Rú¬wä4ñm ¯è9M¬Øõé]ÆÂ¤zÿQ)¯Ü*—Ãe„çëIõ˜Ö }…±]Q§ÏH?>õÞ»>£¼”‹•^ñÝ?þþ]Û/ß»a­cÛ6›µ¿kÎóЇEÀVk«+?oÛ›£R^VŠ0ùžûl; ÷­¸¤#èÊž¾8ϲƒjžlý‘ñµ­¿G¥ü¬»Õ°÷=]+Gùõ¿ßŽ#úû®w\úW+þZÑôÆ s§=aêt¤Ü¬–û6ê|Ò²Ó_uo[·Ýî÷|£–^“q™Ã%nÉÜø¡Þå<¸¬aŸ.Ýßyö9Ä1)¯½›ÞøÔãm=QçO–þEßZýxjÕ7£ÇOÊxÖGwªs/3VûŶœ“òóº¸ÎÕxÀŽsfœûKZ½#òç²³tUí Ëáôgu§¨6ç~k^­}ÆcR^Ä­‚G>Þïpfæ|{>ο°âÆ-=tŽ*<Ú´NŸ¾J8¦HêK¹hé66\íþ©ýnküûßÙÜ8ÿë¾üXþBÙ{û~È1)'o ,I%é—¼w©wˆÝ–K†éNa>ïßÖ¿cRÖ¨s$×U.ýî…ËôöCo¸ê×èΡ=²\cŸß“óþfpÆ›ß^H¼ùÜÃmÏŒÔÛ¿Ë}ç¹o:,{áÌ©>xõùþúLc[iõäü¿)ùµÎO\SÖÎ^sÀSoßã¬J~ïDU’[§ºGX£âužaÇÿÇ¥|¼uÅ×swÝ@œ*xåz{ûùTØòŸ?ô‡˜ç«v|r\ÊÁÛòÜ>êŒù«Þ¾#çùe1öxG‚cëÚo³üÃq)o«ý0—Ì×Û?ëùnÄܧt§1±G­s„iïÞ$Nþm»s\ÊÁ; /ûêФã×¢â]zû®w‡F|æiíßXþ佃ï”vÎw뇜o½Tl\Lb}²cÙ’g.ÐÛ¿tþÑuSVÕ~e§œÑÆ@Úþø¸œwc—ÿäV‡ë¢Ï¯¾ZK×Û¿™qaë+%Œ›Á >]HÕ5ó(/ç½µ  yú‰¥×y"€o·Ï“ µe­ƒœãÏ«y’µß0ýÒu«FN³Ï•Kyh5®qû9\g•zûá=õ÷û?¤;G<þȨxo+¾Tçò¶]=!ç¿UÆñÖ9žKöÏ>Ÿ>>èó)Oèö8ˆ]ñó^Õ§™÷ÇOH9hýÞpÈ×Ð?ˆw½ýØÎû¶®±åHÊ—uÇÇrþ×Ò‰“›® cÄ:_k?f9ùîðèãÐ"Úšóº«õ¶-ß'¤Ü¼«î+ÜP÷e̤œ.mÑ”üWÖ›iìG˜XÑa7ºÞŠ|2Õ>w›< ×ð¤Û¼K¹yW¯Þ(ïw:v v&>d¦õv±xî,™$nÐè ®”ðôãÓªåuqêËóÇÍþ{ÎL;ݱ[¨ÍùO˜i½ýèûñuic/¥}·Öƒá~~¬¤:RnÖáôþøÔ%Ö:p÷šö~¸v;ÎöxëË>8aÅ NÞœ×i­ÍsÉn)Gë¶e.ÜýF¹µ®Ýýt¡vÎ}ôŽ>÷–LÖ-q;Íñ¡¾”ŸõâZèœ/l~fˆ_ë±Oï>0`ëŒN݈âzÝÚ•r³žNœYVäØ=áqÓÿõógžÿ G8Ãa+®Ÿ*ï5[v°ÛCÊÍzµ°{ȯ]÷p‰Þúüè›êl¾–þô—óJíx[ù=Óžu{HyY/†½ëˆ)GŽÝý®yã/³ñcý/'¼ÖzÃù¸8¹N·ïK*ù3×cÝR^6â}‹5.{ëGÞS:ÖŽ›´¼cWÝjÏÓÝ‹?={N°uŸ§ï=‹nùŒ¯cÃÌ/½ÿ|ýYŽNñ5ˆÏ[èëšß/ðYoÙ KnÔ=§nMÊÍqê6þ<+þè|ñ $ç6½}ß=ûf|¯›znwwkR>6q¼Ðyô&G§ÜoÄOÌþ¶c€î4Ž_ÿdÍy¯[“r±q´¸qµÖîÿ5µÎo^¥·oíÀcÅ=f\`¯#Õþf·&åc£Úߴ·jæ¯ÿݺ Ûν_¾ú‚Õ£ìyqßísžµîó™ûn¦]èÖ¤¼l·yoîïè”çÉÐyöÁ/ëWÙó[týŒÿ8Ì?[N4)'›T\fÿÉû¶ýÿèÀ•£ugñµ>-»KŸ*í·µÙ­IùØ”}ÑéOMmÅaŸþ媂)wØýkßóÄi׌±ösœÆ…Të^BÃVqQºŸåߺ=¥|lÖøŠuŽ£›Ä'ûŒ QV<¨ì2å¥\lºù@××þ~–\tðÿž;/×Û|éÓ ýrkßÀyÆ•7_÷ƒ>u£8 ú™úR>6­¸Â·ü“½ö9ëå®gº&ëíGÞµ}®=®c} )›hßÛT÷­ñõ”r³I¬=ö¸Ty5ü½m¦{,zøûK¾×F8kù%ó>¤=RŽÚ7=ðÌÕóQÂðç¢wÆF†­wõÆ>{ßøI×̃ÇRl¹ñ”rÓ6vÂŒ¯/î¶ï¥ˆcß\â·ã«_˜4ám·ÔúÖô·}÷¡»=¥µÉóJGû{i~És‰C~ýY|™`˳¯>÷¥¬u}·§”£6aÝg¿`ß·zû/Нϯ;Õ÷:Ý^R>Ú|ôÝøÙãí~ÌžíÌwáÐç¦ ×j¿Ä”3uÎG})/›ãÐ+ì{$b9ÖÏÞçQ~ÐÞØÿºžøÀeÖ¹’º· =)?›å÷)6ÿ8A¯‹?±ïOöŽ9c×·iö¾À>ãk_ù:¬Ð•_ ·Ç×KÊÑæd¿Ž1açÛ|– ;3Ìøö_v­±Ö‹S*ÄÅTäÅKÊËæ ãͼ¯Î:ÜXÛýRûf¿®•÷Cl¹ñ’r³Ù¸F|®5Þ»¾Ÿ%"c½#vñ  ¶ûe,ç&ºõCʉq}÷ÖGì{[›ÒÖuî»Ýö÷iûŸž¿$AwŠkItýZù]õ¥|l6>§ð´îYë<‚¡¢-u¶ÜÊu‡>eÑ„aG‡ŸSÝí-åe³ºï°Ký´üWñÑË‚ØûNqš<õzëÜÿ:q-aj§-¯ÞJ~Œã‡³»Æ‰Øev?Ê®Øzñ*û{Ó?˜ö¼á¶w÷Tˆ±ÇÇ[ɼjoº±@Ô;Ê=øÖB_Ë«ï ¨§äøVShÉÇÎã®xx™Ý¿òŸª?n¿’uŒñ¡¥Ï êûË/{+¹ùä«EwdüɱóSãCE»_åGž«]ºV_ñß™7Äÿͺ¸n´é9ê+9×µŽ>èØ)÷õŽá·½6Îçwú 1mçÿ 7È}ÊK¹Ø¢ö£vÞi|ªwT=èût£®öëô¹Î§¼”ƒ-Æ3ÇNu?ËêçȽ·>µÆmÈ!¶–ZvÚüþ¥ÛGÊÖ~//Z½Ý±sÄé+¯é½ÁîçÆÅ{ŸÊX¶%è ò»+;>òIVü ³~Ž}5R¨oêçîœöâÛgè+ªŸ½{öW_ZvhJËï¿™›Øb»œÿ-ʘóøùoïê—¼–‘ªnê«ç,ùü³:ϵâI)[ŒíË$û°º÷`ùŸéç®^yؾ/°âŽ›Ï\¾V¿ZÝs3Ï?»}¤\lQë¡ÏÍý¼¹]ý¿›h“Ü'4¿÷òoˆÿNa,Ÿz?îÆò¿~ƒøo²êCzÛC¼·AŸ Œ¦Lõö=ùž½¨o5ÿ˜OáϽH ¿åÍü;n¼¯,û4©w6Ðkß:ù¾/´ü åW'ßÝðëBíÑÿÉ gµGᬒßo±Â|*Tos9Õø™ gµUšãm®fõ>«‡zŸ•¶‚È௰ ¨? Õím®&õ6W´z›‹tã*ÞiPïßSw ¼ œì†±šìö.×…±ZÏ?x ïR¸QäG kÌWd¦ÂVms{«Qb«ïqÕÉwï LUÚŠ¡lŒS½ÁŘÆR6–ºq”«VoÞC7>Sá¨B'ž² ¤&+üÔ^õæV£|oK˜¾$h'ÑÇ$—z×¾¡ÏûZ“å›öÂ4¦ÀS tRºÔ;ö³Ôö½ê--Æ6•±L%Æ<§5+Tè¤ÃSú<…ÊØf@7cÂ<…ÿLhe.–f6«Náš’— ¿ÙÔͦn6íæ*ÜÒ6…W ¹äçÂc.åóèO´òà+t>}ʧ|>ùù”/ ¿€þÀg¡¿Â¥ÝÂ.ù¾‹ñ–W«Äâ*‚¯"Ʀh‡|£Wàïz‘.¦ÅÐ-†nq¯|óÅx׋º%ô·žJ [B^)tJ)[êToÄÔÉ÷tÅ;_åÉêýü ã«ZáŒÒ¿áþòÍ_ñ†Œñ¶W¯Â¥n¥ÐÙS¾ó”ïlô8å;ÿ]}§¿š_Ác¯zÓ’´ÑØ¥pcf)œCŠx!«^Ì—W›Tcï…QNÚ=ôAV}˜/ŸVõFU½z¯¼Ka”“öc>üzFyƒÂéUX‰õ £¼Wá%6¹½kY¨pi+¾‘ûÀê]Kõ®e³FyQ oÁõê]Kʇ$Ëw³žbã íÐYêMË7|r§ÂŽ·0ÒaNõ¦%ôÂÑ›ð…M^¦0‘¥æ#²PáÆ@/вQuê-KÆ%úÑðÝ"ÍV mÅP6¦Eš0 ‡œºq”«‘o¦ÇíPä´ß*M\鄉#Ì]b™Â§l’¿ÂŸ§°ÆéC2}L¦dñžT´ÂƒiQøâð”âT¸â… S¼UšËTøOelSÛ–8óœ¶@b¾oOÂSz³Äz1°Ã¡›ÁXd0o™ðŸÙ(1³ü.øb…¿Ù“xÂü†ÇœmÒ çÂG.ù¹ð˜Kù¼j…çݪ0¼éS>åó[¤©. ¿€þÀg!cWX¯0¹{ä›hîL›4åEðUÄØ¹$ÖxWÞÀ !]LŠ¡[Ü"M½xÍx³MáXÂS‰Sš{–Äà6Þį—oÐ LãMLÊ–C·~+([Ñ(±¹‡‡ÈwòÅ›£‡F¸Jò+›%.·aoÅÂw å>ÓÝ_ _éî'…t‹Üô¦Ï3}œðkbLæî¿„ß2}–é¯Ü}“é“„r÷AÂÿôõ;îþÆô-×züÖ¸ûá+úú á#„o0ýð˜¶ÿdöÞ´óÂÆ ».ìö?³ÕÈ“a£…}6m³i—Ým²°ÇÿÌ›ö×ÝֆȾ8pâgÂz@¼˜O/äÐ ™ðæwo~úж/r㻀)Gvüz¾ hUaRˆzÏw›Â§°²’ù!õ+”vBÕۼЛ¥ÞämPxW.ĦL¾½•¬°­ M[Ñ´Ãßcøë¯ÞÐuÉ÷sʉü-±Z½‡Û,±§AcP«ÄŒMFŠˆ¿¡Ÿê¯Þ¯E—Ò)“Nß2h?=ʤLå³(Ÿå”bšM~6<çÀ{®ˆ¡Y@ÿ á­r…Ô)ì•ooÛ’WE]òBUŒþSo˜¿Ä¶MbЖ´H|×Rh—¶Ê7…À¯2Þµeœ*ȯ ¿•Ô­ä÷JòNų§âÙFSñì¿k<­æŒ7þËÔ»ª­no´7)DôЫVbw{Ѧ7iïZ…ãMZì‹û ‹>äûÀ³/´}‘_Æ×·Gá!"k~N©òâ,ùN»P§Aâ!˜<´ÀÜÐv7<Äm ±žÐ¤í ÒAõêö ±^á!’Fǃi;˜t0ãB:d²z§»J[¡ÐÝ¡Þh¯QXˆ-Òä„ÑvãÖ"ß‹ §~¸ð ­ C¼Z½ÏîT8â¤#á=zQ”‚—¨yêmöd…H¿bèWL"ñÆ·Ø©/q¤ãHÇu) rÚ§luH'N€Dê&Ö(¼Cú˜D:©V½¹ÉرdxL¦l²ð;™óeP›Â6„§”V…iX­ð ÉK…N*ü§B'þÓB†ábõ–z¦Ä07° ]C&ƒv2Z¤iÍ„‡LheB;‹v²(›E^6ügÓN6éìÅÒôæÎ¡l}Ï%?—þåÂcn«4Éyô/ü¼6ižóéS>må“. |ùô¿€ú…Œs!´ [¥ù60™ÿ!äQ¶¾ŠèSQ|ßx¯×)ßtXìŤ‹¡;,D¾yo¼íNÝ:UÝè–B§™/…§Ò6…ïÔ ßø5ðËä»î^¸‡ ÊVÀ_t†GËwøf“xã]à¼W’_IÝJx0ì°øOøNá3…¯¾PøAáûbV ¿GŸ-ÿ&|›Tw?&|˜é¯ÜýT_ÿ$|‘»þçd>Çô3Œác„Oq]…þCøÓo¸û Ó_?q²8Ö×Ùô&žŒ»íÿGq­ië… ö[Øîd·…vc… 6ím_kÚWÓ®š6UŒ?² î‰pÇ›qç—âlξäû‹/²áǸø7* ØVƒÌÀ À6R.(YaM@#˜ŸÁØ€æ1ù­Qø­ä…Q/Œ¿…“Þ¨ðYIGR&’1ФLyÑ™"9Šv†¼¸f…«J½xô;Z ŒAb­ÄqHBN“èGr™\&ñJ\†”YupÂ<…ïTú™Æïiðš^(ñÄ;Ìô1}É„V&u²¨ŸEý,þž ?Ù”ËN}ʧ|x)€‡x/„ïBÊRoˆ¿ÄjlÅ”+¦Ü0Ú+¡Òm_ÍÀ1¥|9s\Aù úQá’8h¿´’ù¨2q*>=Ÿ6zœŠOÿ]ãÓL5¾ÛVAÂ'‚'OôÒ“´ç<…ÿˆžyÕ¹át“we œnl‘´|%ò}È÷%í‹lø¶(¼îjþ‘ö#íï/1" ¼nÚêç¯p„•~mn8BÌmm÷Q8Bó^7´‘Ë@hÒVé ÚZ,±$ !ÒH€·`t8˜òÁ¤ƒ±Å!èléÒ!عÐZ…!DÙɿŠƒ—0Úƒ×0‘¦|8õÛV7ùäG4Iü ˆ^…ÓÍ8DB/вQðý¨ £ÚÑô+†1ŒaLcÈ‹!­°¹©K?âà%ŽtÜ…ÄÅ7)\nÆ$¡FarS7‘º‰”M¤l"í&Q7‰ùIrJ˜LÝdÆ/ž“¡5¨LáQ7…ñJitÃÿ©UØ?Ûîc•ºXaþN›¬°~Ëô2…­MßÓi7ƒtü<òó •­|ÒùäçÓV>ãQ@ùú_@ýø(dœ ©[HÙ!! ï›±í"ÒE¤‹+œÑ…û C…߃n1ébèKV¸ßÔFÝúWÏ%ðT RúW:ObZÌQ*ðÀ ,¢‰7TŽÜTP¶‚²Щ€¿áÉ‹T`— ü¡JÆ£’v*á¡~³ß*HøDÓþg1ªéûĤ ÿ&|ÛÉüšði™¾‹>ÿÆO™¾©/®‹é{L_#üŒð/î>Døw¿Ñ7.5}E_ÿ`úáL aûM›oÚû¾v¾¯mïkÓ…-7cÔ¾6Ü´Ù}íµiŸÝ÷\Ým²i‹M;ìnƒM{kÚÙBÉ·gˆÄ`w½™koôLJqòA|)ãÛª0×?~÷‘XkÈjòÕ¿Va©¹°{”d<»v´‚…]sÊp)”:¡ äk ²Îü†ÏS˜gµRtÆYe¢ M¹˜zìŒø·XŠR<å(›@^í'ò÷$d< Þ“ÓdxMFFUK¦S&3ø™™È{&å³(ŸEÝ,þ–MŸsèsõsà+Ú¹äå’—Çßó(ŸÏßòá·€úðZ@¹‚…-Eü½ˆß‹œ?¸˜òÅôc¼ #cUÂø–@³4Zâ —J<±rÊ—Ó^9ãU-±x‡Ófe« é<äG2æ3§?†O5óÿ»ãÜS1î¿6ÆýïŒoOŶÿ{±mhø¨µ [“6=I{’ölVø˜Éü«çŸÓ ã¼Aâœ{ã‹} >&ãé‹.ø6*ÌstÖ¯FaŸ·)ìsdÕºþ´Óü~5 “téÚ €¯xïOùþ”ï¿@ác–)lLÆ5›D»AðA¤@zéð\Í?ø &L:„téæ-Ô_acÂs(uÒ§´51/aðÆ„‘k“æ&^Ãá=œ1‰ ?‚òäGÀO$ô"¡ ½HèEÑV¼D‘ŽêRx˜ó$f ãƒÎÅЯ˜n˜”¥l¼Ä‘ßñЧÝxÚgL“ÚM¾‡t"u©›HÙDÚM¢Í$øI†¿døO¦^ò6iþ lKê ¢^ ü¤0î)´?Ø_áYÂÏ`Ò©Œc*ã” /ið’ÆïiŒYZ¯n%m¤“Î Í êeÂK&|gÒf&mdÑFyYäe“— ?ÙÔÍfÒô'ó‹îq®ð…}ã\÷WL|_w2gú6Ó¯™þÌôeŒƒáÃLÿeú­¾w N¶W{2ôüÐÉ|é{L¿cú÷ý[æá7~æd>æd¾åŸÅÎæ^®y?ÁÝO˜þÁÝ7˜~Áô 'óî~@Øÿ“Ù}w›/ìýÉö~M›.âáË\ˆOÄ@öä§øöÎÀ"Ì”øÆ®ñ,)V>È®ré[­°ŒÑ¿m ØzþÈ{?ƨ?È  ~æ­?íô§^ õ× èA3P&E3¹n‘"B¿Cù%TÄÝ”o¡a–ÖJ¬áÚˆ¤\$íDR6’²Q´M;Ñ”‹¦1ô#†Ÿ±üŒ¥\,åb)W(±‚ã©_+q‚ãÑÏ„f… LÙÄ…Œ¾%Óv2eQvP³Âù¥ú;ƒ)?˜~¦­Tú”ý4ä+­UªPz­T£ ñ“2™ð“ ,hdA/‹¿eó·lÆ3s… c\ó¿<Úͧý|þV@™l@e ©[HûC’%pyEâ'm–XCá½XÄìu'rcTÂX—P¯„6K©S ýÒ‰³\Fùrƺ‚¿WÐFE¯Äõ­$])tHÄ â¿ÿÛ¸üÅäÿ[{Ï}còu<þ?‹ÿWöšÝãðõ³° <þÿ¿O{ÿ¿ÄÝuª_] ç¶žŒ“MñM¹øŽÛ“qñìR¸ô̹W‹Â¥oìƒIß(1é}Ðk_dÖ·IáÌBǯVáÒ“öGü¡ë]xë‡^ö#¿óÖtåh'€üÚíOº¿°÷Ðîí@hB+߃h7ˆvƒàc¿ÀŽ à÷`x †f0yÁ­ ‹žz!¤CÚ=4C[¤¹HßÂÓ@ú>ÂÈ#F:Œq§~8cOáäGÀSùð½Há/Z¤™‰¢(øˆ‚ß(xŒ& húC;1ô-:1¤cIÇ’ŽmqÚoxºñÐŒ§\|Ä—O ½è$Nd )—H¹Dê%A' >“£Ž<ã<žÁó ê ¢N |¤Ào ü¦Ü`ê†ÇÁð‘ÊØ¥6*¬ø‰ŸÆü¥Ñ·tè¤C't:é Ò¤3 •AÝLÚ΄V&ídR7‹v²ø{6e²©“ /9ðŸÃxç@?‡2¹¤sÉÏ¥N.üäÑŸ<òóèouóá/Ÿ~䓟OùÚ(`L ZäöG!mÒ~¡Kâ!oyC([D"ÒEð:”v†Bgh³Ä©/ÆG“.†n1í £ßÃ(;¬Ešëd¦„²%ä•B§~Jß²‰],ðë |zÊ”Óve*è{E‹4íy8ô‡So84*IW2®•Ô!ü“°©â¿K›>ËôOÿYÜÜ7VîÿŸÄÀ'³ý}cßÿj¼ënÇMûmÚj÷ضïÛ¾ö×´»¦ýG{¿¦ý<™Ýtßûu]ûÚDæNöYô¤]ωíEŸ½ùéÝ$C_aϘæ×Ÿyî‡<õãg²@ÙþÈDx Bn‚šdx2@Øæ6d›ÄÛ…ÿÊGò{$å#IGùË0%úÑðMû1ü‹¥íXèÆ¶ÊÐ%޾ÆS/ž¹Œ‡ÏDòù[ó„¾%Ã2õÑŸAâ'2œÍøL½ÁÐL…ŸT¡ãÈJö4Þ2(ŸI[™”Ï¢|6}ÌÇÚÍ…N.}Ì£Ïyð›G»ù´“Ïß )[í"êÁCÑ6‰!^L~1ùÅ=C¼„þ•Às eKø[)å˨[Ïåt¼úü­‚ö†“_Éï•ðX¹ÃãÔ>詳þ¿xìj?TÜÅA<@¹xuL Z<*À'à úá!Ÿ” 1D€Bˆ‡þÑañ0·xLY< +KcŠÇO¨ï øŸÆZKc­¥±ÖÒðKNóSw`ñOþIÃ?iø'µ–†1Ó‚ÕÖ[”ÆzKc½¥±ÞÒð]Z”ÚÇe½¥±ÞÒ0Zë- ¦áÏ´$oâ×4Ö[ë- ÿ¦áß4ü›–¡¾Åf½¥±ÞÒðuú¯¡ÿú¯¨ïÍÐ ý×Ð ý×Ð ý×JÕý]ô_Cÿ5ô_Cÿ5ô_Cÿ5‡¼—¦¡ÿú¯¡ÿú¯¡ÿú¯ýNݯ@ÿ5ô_Cÿ5ô_Cÿ5ô_;GíQ£ÿú¯¡ÿú¯¡ÿú¯]¨ö>Ð ý×Ð ý×Ð ý×.U±6ú¯¡ÿú¯¡ÿú¯¡ÿúo|‡Žþk迆þk迆þk迆þßÚ¡ÿú¯¡ÿú¯¡ÿú¯¡ÿÆ·"迆þk迆þk迆þkè¿q·ý×Ð ý×Ð ý×Ð ý7î– ÿú¯¡ÿú¯¡ÿú¯¡ÿÆþ<ú¯¡ÿú¯¡ÿú¯¡ÿú/ö}4ô_Cÿ5ô_Cÿ5ô_Cÿ5ôßXg ÿú¯¡ÿú¯¡ÿú¯¡ÿÂk迆þk迆þk迆þkè¿ñ>ú¯¡ÿú¯¡ÿú¯¡ÿúo|‡ˆþk迆þk迆þk迆þ÷ºÑ ý×Ð ý×Ð ý×Ðq¯FCÿ5ô_Cÿ5ô_Cÿ5ô_CÿÅÙ„†þk迆þk迆þk迆þ‹5”†þk迆þk迆þk迆þ‹÷4¡ÿâ?±·€<>c–²-ʆ Ç†. y®SóÚ¬ú'ÚèUg$ejŸ­Qíµµªý6u¨Z›4ª{A­òîºñŽA´Ú«Ug)óÔ]¡VyŸÝ8W Q÷†ªÕÎFu¨M‰û«{DÕêÛÍFuß½M~ÃiÜó Qï Ô¨ûïMê¥6õ&‚¿ZÖ¨·šÔþ_‹Ü4¾õ,Sï$ÌRkF§<¯1ÞLhRëÆmêÌÆCÛ”ÉwŒ³xu'©FÝ-mRwæËÔ ê§út²út±üÔ¸꡾•êRûŠÉê<§^­1›Õ™þ,y®/Ö™âûPãÎ’¿¼K/öïCCÔ·Uµòã¾RºS߬¾Ý¡¾mTkN§\wŠï¯Œ;L!ò^«±O­î1Õª3Ÿ&õÞÂ,µwÙ"÷/·¢U|¢ö1kÔ^f“<ÿû˜âÝã>S²ºÓT'ïÝo.lSï.D«ïGëäû F ꡾­VkÒu_¶V};Ú,¿õ2ÎüÕ]„Éê®ýbyßÞˆS£Õ¾g³:÷q©}ÏL¹÷)îÛ‹; ÆZÔ_­GëÔ› ÔZÔ_½µP«î05Ë;¶Æ7¢µò1±/j¼³°C~/šßªî.x¨½ÑuÆ3OÝŸŸ¬¾ uÊïÇŒµgˆº›´@ÝOÚ¦¾%óß…o(,ï(ˆûIÆži´¼§dì“F«sœ:u7©YÞu0ÞLhVû¦ÛÔNˆ<Ç1îï6©»òÛä}y±ÖAßFзôm< î±%¿Šü*ò«È¯‚v´«h§Š¾WÁ“^̛ڎFp ^¹åŸKš©jò«á³>«©_MýjêWC¿šþŽd¼GÂÛHè¤ý‘´?’öG’?’öGÑþ(ÚEþ(òGÁÿ(ÚE£àaeFSf44F3Ö£ëÑð1švFCg4²1švFÃëÆl ¼ŒÖh¡­1Э1.ÒŸ¿§Áøið;ÞÆBw,tÇÒ¿±ð?ºc¡;ºãàkeÇQvôÆAoc[ ?µäÕÒN-¯åïµÔ©¥Nm—\Œ§ñпãéÓxê'ÿ\x9×%—ÿÁÞ™€ÇUÖû? J€¤¤P ÔÒ…RHKiSJr&ûdß“É>Ù'ûdŸˆJ¸¨Ä{# D xшÝP¸®0a+¡t%­@KB ©+÷ó;¿w2±ÿzÿïÿúˆÏ“öóñONÔ­€¶Bâ*$®Bô Ñ/D¿Eø(¢&EäêB×…® ÝbtJÐ)!¦b-ÁV þJ¨q)me´•ÑVF[me´•ÑVÁvÛlWÈ6v+±[‰_7±º©}¹VQÓ*üTG1Ôw µª¡¶5ÈÕ WKík‰§Žüê¨A=zõèÕ£Wz|x¨•‡<<äé!®Fâj$®FâhB¯ ½fôšÑkF¯ÍøkE¯½VùNœ­økC® Ýöý:ýñ¢ãEÇËñóbË‹­.ä»hïF¶›ön»iï¦ÝGŽ>lúˆß‡½~êÝO^ýƒz=¶ÿæÞŸ›Ï͉çæÄssâ¹9ñÜœøÿœË\øï0þ˜üæåú%×úœ‹åÞ»}ÎÓsƒýû”߈ôS·9^#&îI½lÛÏ-æÙå y~9nžaF(ÖEä1xD£ŠId¯“ 1ï{G™õŠ>}>iã`8C0ñl¼½P³†fPñ‚äž½¬õ¶ßc 3ïˆ; Ç Yë¸×¼Û©˜ò\ô´I]ÿh?Ýªï½ØGaæùB¶Á:UL!Y#n?7 1ëpÜf-Έ®—wdl ¤!ƒ¸ßºÌ3Ù!ƒŸ4`žÉŽ™ç²!æ=È#f]O¤Y{î5ëÏ·*†’¬_±ßµ_fÞƒtëZQÁP²ß¹1ï@:Ì{ƒ{0Ô¬9w¥!}oÏ~>ë3ÏhÇÍ»?aú>¤ý|vÈ<£Ýkp#ôYíE#úþ½®4Ô¬-uéÚy÷ÑÆ 3ÏrÜCiÔ`†ì$—yÿqD×ôØëË]fù6]Gc?› Ñ5åö{Ž¡æYl¶yWhXןÊ;B6ŽÊ6]S.ø‚²~Fðüäÿu“ŠO$ï м7dc"*.’`­ØÏ_#ô]Fû™k„yoÈmÞkÑwm ¤}æ*kÇí÷ˆÂõÙ«½&gH׳Ê3#Y›M[4¹E“[4¹Es4ºÑ´GÓC{ í1´Ç`;Û1ø‰™Ô!±El±[a~:A^çŸ8äá ÝAœât ï@ß¾ûêK½c‰-û±øÅ,þciÅþãðG{íqć8|ÄC2ñèçá;žÚåÑ^ŠýÙO¿ÈÖazõJ Žì$`' ØH ÎDâL$ÎD™ãa'‘XÓ‰+]ì"—NnéÄžGÝÒ‰?øÒ±‰|&²™ØËÄO&íÙÄ›G{61gã+›¶lÚ²e:Ù䜋¿\üåo.}#—˜sÑÍ•x‰)˜ò‘ÉG&ùøÏ'®ò* ­Ø ‰­ýBô Ñ/ÂG>Š&uáB×…® ÝbtJÐ)!¦â-ÁV þJ¨q)me´•ÑVF[me´•ÑVÁvÛlWì×)H%v+ñë&V75®"ß*êZ…Ÿ*â¨"†¨¡^5Ô®¹äj©¹Õ‘=:õè4![^=>êñá¡Vò𧇸‰«‘¸‰£ Ý&t›ÑmF·½fü5c£ù>òîÃv¾{©Ÿ9/r^Ž™}/ú]ÈwÑÞnÚ»ñÙM{7í>òòáßGÌ>lõSã~réÇO[TÈÌ2™9ܰ~Ü0™É|HæB2:ÖügöÜGæ;2ב9ŽÌkfÏadþò·ÌYs™§k~2{n"s™{ȼCæ2ǹ…Ì)d.!s™?ÐOgæ 27yÁ±æ³ç1¿ŒõçËØþ£6ž?ÖX~ö8~ö>0~ŒÝãv³Æëòüjöx]Æê2N—1ºŒÏé;ö¸\Æä2ŸT|gy—N°*ßGÞ•L7ÁÔw¡³çôƒ!¦8‚)kJYÞ‘Œ Áã‘÷Èä½äùcúζ`Πγ¼c¯q+¥`Ä và Nœ½ždм3æ3˜Èʇ!x²ZÞc‘ÓŠÕ©8q‚-i¯ÍU|:Sc@×uØø8QŠ)ïmË{Ï‚,ï) ¤¼+&¸p²îBðeí†`áØøÇ!Š}cã5ºQðŒíõÄÅ?Ü Á£“µ‚×#˜Ë‚ç!k%åýjYS’5¤·ç.w+v‡¼;#¸Ë6~ó6]û,˜ÏòÞôª}·M°%3ˆ/ Ù j’1 x‘ò>àR^éP¼9Á¢“w©sFuÍHÚ¤¾V@¼Y´eqnÉ‘OâÉ"·´Ãúî[f¨žb‹‰©ÙÚ²È-ÄS ×eâ. ‡<䈭ˆ¶bö³¯˜íjâ©&‹«ñQ,ÛØ©ÆWßK‘)%®jbª Þ:ù'öLr¬Á±5 ç&·ìt[ºØë$§Nä[Øß‚­NêÕ+møïÄ 6;ik'×^¾÷¢ÛŽí^rèC¿ú°Q#ŸìïÖ›xêÈ¥‰ídjh÷ðY‡7>š¨m úuø¨C¶‰ºxÄF”^NÚ±áỹVì6SÄ­—™&±MÝÚ©i+9zÐïÿ|¶g+ú]ìo§ŽMäЇ~;¾Ûñׇn—Ȉ-䚨[ßûıô!ߊÝ>üuñÙ…vùŽ~5ì£6]bç°^ê|äå#övàÓ‹\vºèÃÝÄã#úýÄ×/çL¹†ÈßÜ}ð¹ûàs÷ÁçîƒÏÝŸ»>wüòn˜}O\®UÙŠ‹oŸ÷Íùm\ÏöoQ~NÓ/M}$ÆÃj™Á‡òŒ¨­Še¿ßjÖdf›õ=Cfmý^³¾>Ô¬±w ©ƒÛ¿U±ûmL¿ƒái8p¼ËT±þì5AáúÞü‰ u›âöË!5Äࢺ öÔ°âOÉûô²nHp¬í5ûQfݾOñVlþœqó~¨YÃï1ëøGOÐ^Wb0ª¶)ŸŽ`îÛœ:NÅ”,U{-i¸YÛï2˜-óÊipU‚ت‚}j¯9ÖuG‚¯jc»„«ióΤáÛñÞ€³þhЬEWìB{Mj¸bÀØX«Šßdcº þ¶Óð šõHãº6_p[_ÖÇ ¶¬³ÁÒ—á‘¿Ä`ÜQÁ¹¬nY‹/˜2öšƒqƒÅzX׸ڼf ‚ˬCÖ5K²NÉÆô ò ÈšxÁ½Œo Ô¬Wršµ¯C†S L1ä}{½Â€âØk̺…½fíR¸®‰µ9y† /Ï~Ã/°L×2جãç*Ì`]¹`é¤á7|<ƒÃºÕp „üU·Y³4ªø66¦•ÛàZ(öª½"T1íµIa†wÇeÖ(ü*ŸÁX38V!ºöß^ë?ªëý…kÇÆX SœUg|«bÛØ7¡cÕa¸u–™µH³iT×Õ Ö¸¬™H›6ØŽ!Ê­#kúÓŽ^€mÊ«#ÃÜhâˆÆf4¹E“[41G£=©CàÚch¡=Û1ø‰ÁO 1YÄd«Å±³°o³%s?b¶ðï íât§}úôÄRÀ÷XêK|±ØÅ,þcñ;©Cî8üÇá?Žö8lg¡W¼_‡âñá:¼ŽG?ž:ÇC~ãñxòŒG68KñÙ˾ê–€­&ì&à/› Ä›€^"ñ&o"F‰7ûéØNg;ÛéØmÁNm GôìÁvmnrÏÄ^6ÛÙØÎ&ælüdË>òñ`;›X²ÑËå{.~r‰9—¾‘Ë1É%Ö\Úóˆ%Xò‘ÉG&ßùØÏÇ~¾ h+$®Bâ*D¿ýBô‹È­?E1¸Ðu¡ëB·tJˆ©„ K°U‚¿j\J[me´•ÑVF[me´U°]ÁvÛlWb·»•øukyVql«ðQE 5òjÈ»†ZÕPÛšý:e©¥îµ´×‘_5¨G·žízì×c߃/9xȱ~á!®Fâj$®FÚšÐkB¯½fôšñÙŒÏfüµRƒV¾·b§­Èµ¡Û†n;õðRK/:^t¼ØöbË‹Lº]´wc³›önŽU7íÝ´ûÈÑG<¾ý:=ê§ÞýäÕŸL3È\Xþd€,sâ9â>ñìù’Ì•fÏ‘d~$s£Ùó¢cÍ…ó™û=ï‘9O`ž˜ãÈ|Fæ1GÏ]ø=üÅœ%0_ ÌUfÏQó™“ÈDæ³çûãGÏ1ó‹À¼böœ"0ŸyD`þ0{¾ ó„Àü`ö\àºÞ˜ÿ#p¯\ø©[D0E.p(½á7d°EF”‡ÅÆk Óuß²\8 d½¸½ŽÚmø½Šß'ë¿…ÏI8—(ωàM v½p9 ®ž`*É:iÁ"º0Bñëí5ÝaŠ…-üFÂ(XŸv¾B‡ò§ØxŸCŠ[/X~‚Ý'¸H²ÆÓæܦ˜ó6¾‘[ñe§p! ¾¼àN ޼¬ïî#YÛ)k©×9Û]øR“Op4…'EðÏ1ØÓ!ŠÑ)x}Ÿv+÷‘`ã/S QYs.|K‚9*8M²þ[ÖŠ >©à˜d±E]3¼Êghsµ (7’`Ù<….ÅŸÏR~ÁÁÎ!®4|dá+MÚ°“†ýŒ­Š*ëL ȱÀ­¸ôèdà/ƒ¼2Žèïr>sÐÍ’8ØŸI¼9{õT+XõyrÝ$Çâ)ö)–R1ß+¨SÛÅÔ±Ž˜‹Øç&Î ¾»Ñk †ÚÜø.å{)1ïÕ[Šuäåöé-À ¾ÇòßA¼ÄÐKzɱƒz5°¯“üd?6z±×„ß:±O¬½Øl@®[øoG¶WÆFÔ¤“ZôŠ>2²ï^r-åÓ>dÝØëÃw)rnì7Q×ö׃‡Ï:Ž›Ý&òjA·½:‰œ<ÈÔ ßJmÚ±áỹVú@u­Ão;qvÑÞDlMäÔN-[‰ßƒ~r}|¶rŒZÑïb;y4wúíøn—8Ñí±…\qvñ½OìIüÈ·b·]|va£]¾£ß'ãrë;ÔÉG,>òò{»ŒðéE®kT/™ÝÄã#úýÄ×/ç˹ûæs÷ÍBæî›ÏÝ7Ÿ»o>wß|î¾ù?ú¾y¨ò° n·œ×?*ÀŸ`s½ËoMú{¶9îC&‰aZñ¿mÛlÃ?lxà÷.ø0ƒ‰åR|ánõ>–pÅÚ¼`Nƒ>høÁ¶)O£ÍÃj¸Â¢ ÿ­Ïp†m5¼ !†''ÊpÉû Ÿü˜bd}rÒ`GηÁ#QLò& ¿X„áÌu+ÏNóö¤Ã=f0¸¼gq\9¸lL›PÅÀ=wDù#m>Ý0åÔýT„Á9?b¸z†‹l@ùuóÐÆÅõ(Žp>wÂ)c†›~DyzN3¹áŠ‹nãã†î—ÁÉU®á©ÜDáûÌ+áɵ1¼"+׿«UÞÝå#†bÀ`;z ®úVÅV·¹ëC nî¨bç ¯Íj°y¦ ‡}¤Áöò.ûÃ]6d¸ì÷*^`3 §`S ïå9ƒ§;`¸"Æ¿Çæ0 3cÿ0Å[NÁŽñS³•ÏK8Á„§K¸ ×U°Um~·rÄž©¼¸ wŽW¹»l¼îåõ½€ãwž´ )wù…غ0Lù»ÎP¾›§vØp¸(woä€áD Q~ÁÒ¾HÁ;¼mÁ4½4Bqº/]f8³œkuDyÏgQp½/õ*÷–ð¬Uî,ÁSÜRáȾGáüÁݼR_1LyÃÛæÙ‰4\惆û2\9²§[¸/ãÔÆœõ)_™ÍŸað%‘»p›á Qn2O‹ñ}Ñ~Ã×N^t¼èxég^ú[Èt¡ÛÍg7íÝäÔM{7íÝ´ûðé#~öúé;ýÃ~|öãçã2,š Ÿõ=BÏíößßãyÀܳ€9ž©¹gsÏæžüÏŸ|ïÿ¿dxÇ Çâ$‡á×uþ1ùô)—×Ɇg—ßü¼QåIŸÇïo¿±yû•oÒæI8¬|ä6¹Œ#”ŸÌæÆÁæiÃMF|§ +÷®ð$VùŇ•o6ì°áZ1üdôƒ3œ†§ ý3|ÊÃksïbãLlœ¹Myx—Œ)7ŽðÒ ·ŽàžŸe¸x]Ê¥`óðº ç¶æ*_ûÙãŠ.|¼Â]fs*„(͇ò² û·r+B¾P—ËÐY˾sùÍf8zÉû\ôÎÍ6Ü<Ôlá˜ò½/œT¾…ó' ~.@/qÀp¿/3œ¼ÈF A<ÓÊ·eóÞï¢mÊ/\f‹¤vÄ“8®üe6·6/ 3¼ðÊÃksm²)ûVcgé2åæ]axx£ ¯uH"þ >ÅeObß rIr)¯ðÓ®!Ö•ƒ†«lÄp*ÓÊqåât(^ûÚ"Ñ‹¤n‘^åà\=¦Øí«Émõ´rÇË¥_xÊÖb#sÜà¹ó} ²käØŒ)¿®p’ oÂZò]ª|6§.>Ö{ ý áNÀ÷ümئôÂÛ)| н ÝdâKF'™\’‘-D.Yþ‰-™Ø’§uøàć“z8‰Ó‰Ž_Nôœè9©£“XóÑsÊ?~œøqŠîz¤ ŸBS¨o S¨E µHA?…8³¨%1Wɘß)äš"ŸÄ‚ì¤o>¶Ë‰#5J‡.üºÈ/•X²°ŸJî©ØI%¦TòI•6l¥b+[©Ó:Êg;˜ ñ—†^öÒˆ+¼Òˆ- {iÄ—†­|‘!Æ«86èdkò…Ô1 ê‘N†ÄÀþ,j”ž,öea#‹sËÁvÛ…ä’#ûˆ±‡œZ°YŽl#ñ桟‡l²å2Ob>~òÑ)@¶Ùâ-@®À©Ã³"â*"¦"êR„ï"|¡çBÏ…^1zÅè#SŒL12¥|–b£ÙRdK©[9¹–ò_N\åÄRNíÊ©]9µ*§våÔίFö7ƒ{n츱ç&–j9&òIìÕÔ¢›ÕØ«¦vµÔ°µÄT;­ÃÃ:lÕa£ýâh ŽrîÁg1t`¯‡;ia_z-ìk‘mtZÐi¡ž-è´wúmÄ܆n¾ÚÉ¿ƒ˜Ú©AòÄÖN:ârêE®™^rëÅO/¹ô¢×‹|¯ÌMd{ô»GyƱæ³ï»Êœâèû®³ï¹Êüáèyñæ Gsæ9u™ãë ùhòõk<<ûýèÙãÞÿî½h×Êx60–=úècY#ø?ÖýÝÀøôè1鱯¡üÇ_okœ_Æ–1eé_CÆŽwGÚBŽýÎôŽáN™T¾Wá\™Oœ‹°ÇåÎ7óÊͽحܯK†•—p‰ìó*϶ð.›T~§‹' ÿ)}t¾Sy¥–lSNÒ³ù\€ý‹§ / ¶ÏFg¥S¹[… I8›V³µSùµW»”cô²qå;þ>á#]C<h_Ë÷%{•4jRy°ß§üà‹±¿x¯r,­PA9%gÌbOä÷’8¬·„£û2l,—1Ô¨ò¾$b#QÆ^åßàQΘDäVêm‡DöG’_"6×àg²IØH Už˜ ȯÆGñ¯Æf1¬§/%y”Ó4IbÃ_’Œ5¨ÏZ|&±Žtl'ÉX†’¨A&û’¨U¶3刟\¶“øž‹l²ŒGØ—Œßdúf2ö’©S>6³d?¾ ù^H]rˆÁÉ>'Ÿyè¹ð_‚'2.ẩÔ[r:ËC®\ÆÔ9…—²]E,åèVá7{Uèf°¿ÝrüÖs5vkɳ‘Ô#_l9ñ”S‹zä©a³\Ïh¯F·™öBäÚ¨‘ ›•£RòhÁN#~+iëF>•XºÑ¯æ³GbÔ¬’¶öUÒVîÓÓo\g%¹†"SÍvñµ‰Orì!žòêA¸–Z7ˆMìÕâ¿üj©{-¹ôp\iïÁn#m=2öÀf 2-bÛmäÞ&ŸÔ¸‡ÿ6‘—lô`£C|‘SyôH¬äë’q²¾‡ÙÿÓûpr­üG¾“{ô=¸ôý·¿Ç½·¿åÜÙ÷ÝþÑïÞÊ5~$äŸÿ~Û±îµñ›ú_¿ÏöÏp-ü¯ŒUæî¯}tÞ±ýÿ¸¿6Ÿãsθáê£Ö'Q¯“Èù,úÌ"á%Æ“±Áoóäa噞Çï0‚ÿyü^æÑ÷çM+Ïý"â=eÚpNcãTúÏ©Ë ÿ4µ>zæ5\¤œcNã|qšŒá¶*_f¿³0ì…Q¿ÓC )ÇùŒlÃKŠþJì9d8I·*ý™ãÊOzV˜á$Eï,ôÎB/‚óÂÅØ<‹|æ» Ç 1ÌÇÆ|lÌ'§³Ñ={R¹­×"sÛáøÇÞê² Ð_@Ž Ð]@M ëe.‘}ç’ë¹Ë o)ò ‡ g)uZHl ÷+÷õBÉí°á* Uû ø» Âp—F)g©\Î"ŽèåRøK‘Ãr|,Ú«¼¥‹¦õúiÚٷًdŒ)ŸË”{ÉÃQJK±¿”}K©ÉRâ[ix±©Ïrü/'·åÔeE¸á(¥}±¬@v¹¯@f%y¯R¾ÃõøXM~+‰+¹¤!å"]^$¶"©ÉêmÊ=ºš|VË\ưä{91­A~ͰáÅÞrÈD.‰ÜÖð¹v¯rgÛ܈乎Ö×zâ]O,ë‰u=ñ¬ÇNörÂõR½x6Ș}Òð&²ï*¯¢“ŒN2þ“ñ›LìÉÈ&ã+™Ø’elŠ,dsÈ­¸è8Ñq¢ãDÇIœNtœØwb¿ùrâJá3…ØRÈ=…š¦c 5H¡)è¦c þRÐO¡&)Ø®Âg ÿ•Ã:ìHÅN*~S±‘ŠTj™ŠTjŸJ©Ä‘J¾©ÈWb'{©ØJÅV*9¤Ê¸•Òˆ%MÆÉìO#¦4ì¥a/ [.ä+‰½›iÄ–†½4ê—F|ɲ›Ä!cst2ØWJ=:°WKYĔž,rËB?‹}9ØÍ!¾öå°/Göc6ó–é'{yØËC¶MÆÿÔ®‡Øò§uU€ý¶ +@®¹"ò(Âw6Šè_Eø.Âw~\Ó:ä*F¯˜öZêQŒL12Åè–òY*±#[JÝJɳ»åÄUNŽåÔ¡œú”S·rêVJ Üøjã³Ü2¦Ç^5:È5ÈwìV“g5öª±Wlö:dŒO®µÔ¾…ïuÄ]‡­:É8ˆ£8È»½êÓ€^#Ç¡ÿøo$ŸFô›ÐmB·½ôZÐkA¯™ ÈØŸxÚÐkC¯íˆÛ©CÏv ß|‡Ä†|¹öÒÞK}{‰¯—œzÉ¥—\zÉ¥—˜|2Δqœüýßî•Æúqýw_ìè{aGßÿú¹Çu¬1óÑ÷¶þÖûY³Ç¿qo`Œ;òÑäã=z 9û=ãljÇÆ…³Ç~ë}©Ù㹿v?êX÷ŸòC‚|}CfìÞ"7]Ç¥\àçLÒ½d A_\‚Ì’ÆÔgÙ¸òf_,ß©Áâ!åê]ÌqZ¡|ÙçËõoD9±Wfî]>/ã8¬ÆÇùèG¡»hÐð~ó»X´Õp|c1í‹'•w{}?ŠZ.a;_+èóËC•¿7Q|`'qLyÑ[IN‰ì‹D&ÙHbŠ\¦SËHòJb;I¶åI]’ø}oÀnúIÄ“DÿLQžìtì¤ã;‰Ø’ð‘D^IøÈdÇ:sZ§«¹È$c7ûùrsè´4»Éè&““‹ï.¹náÛ‰o§S§§N|•ɵ›årÝ’k9TOÛ•Ä“GŒ•èç —"çHlTâ§–ZÖ"W/ûðY+×b¨—ë˜\[ˆ¹™¶fj—#1c3¾Ú8Æùìk“óq„þlå|†ïBrêÀ_7Ÿ®qý»°Q\¥´ñÙ†Ïrâª%¦üÕ’C1õࣇãÜ€69¯Ÿ=Ȧ¢—FL¾AÅA•õ 2þ~«ÿí¿Ø9Þ|Ö-߬‡þõÄ yÞ:£ø[û¶ÿáykªù+·Ÿ9¿ÜúÔ‘yÞóïÛûö“ß3¹ýƒ‘ß¾ñhKþïÿ¢te†ã"kýW΋yaéÏý]ãw9î¹þëⵯþbaüJü Ø~¶Ý`ÅçÝm¤û­©ÆËÚþåëߴξmçú'NóïÛãßž?o<æ§ÆïÆ°S›þ¸{¡µê¤3¯‰z:Þß¹ó·­;øuk©ÊcwĶûð—_9½óÝ“­©º‚§/í|Ñ¿owþk6¯õo¼ôòwß»å8kMö»/ß‘ñ=gÛm7f½²Áºô¢/üfûë/¢?fë?òåÞ+Þˆû¦5Uùä¿ßiÖ–Qp^£ßsï \ýŸÕ1{5oÿÆ’§:ðÅYWŒ\}ÅÅ_ˆòw^´êôé'Ógå9iÛûÏ®–W–4ßk¯yXS¥_ô¹[õõï{æsû;íß8tí‡b>´Ö:Sw ø;~œ¹a×£ÖŠ/µÅß×ÏñÙa××úQí7>¹óž—¬)×Mk¿šäß7¶é¸Ÿ}/ß¿ñÑ3¾³(Æßñë;ÿøä#lùGk÷M¿ý%kª`uÕgÏ¥ ý¡®þÿÆ})K¿s­¿ëcµ_8õìswØò•O•}gÇñÖTöåç_ù¹û‚ùÿ…—'Å<{qWGØkø›zú_ ¶®µ®Ô|ýÝu‡o¾hÕÁzîÐþä¯ýäú^ûwk*­|gÍ{±ãíß÷­«nýrüTÌX žo·å¡7ìU÷ÿlÅ5“ÿæïi9¥=?íÖb­ö´ßŒE…~ñDÿÁ~“tgWÜÛÿéßgwÓ+ýuÊæ˜–™~ÒsËŸ_¬?ã èkÿÛvõËÓ‘×XS±_^—¶}4æÃ@~¯ÝúöÛ[fò2yø{ÄZzqà¸`GûÉã¥cç¬=dMmxõÒò©òÀñõïëJ¾ ?îdÿÆ·~•óø×ô÷7 ‹þÐ8l-|áwwDmMÃŽöǯÿÚÛ~»ÂšºÜ¹âðÎöïk¸÷âgòÞðoüóíW¿wøu··ö—oÞ³ÝñæszüãÖÏnþ®ÓšZröãÞ{F0þŠæí>45s|6}bùš[>ðwågZûÁkèkx"û|2{Æš:{ãSï><ÚŸ‚ýûÈ)O®»ðë2c?ÿ9þýœö—' £èkÖÔÉë,‰ý¡_Áò?‡¤]üݾýUOçÖ ƒuXõ™™GnÖó9í'Oj>3¿“É]Wæmûy°ÿå¾ú‡CWîŠùY ¿OúÆ>³ÒrZ_}=é½BWêŽ[/?{ÚOžüFûè¥×ÿ$PkòWÜy;<ø»ËŽÿùêóÝ3qnâdP[ržu‰§üšÂ›°§ýæ©SÎíè¹iQ0>í¯þ}é§¼CèCþMì¼è„ˆ/Y«ýŽ;&{~û§Eèkyêúü_÷•^kM¾8tÜ;÷œ<Ÿ&v\tàDÿæSÞ^vÃÇ#¯ýâ©?'ãܧ]Öä®mÇ㎺-"î–ïΧÍë/=ÿƒ«æÎ?Ž7wj?yº=ÄóÒ¿m´&Ÿüõäºyüû–ï9°è‹õ3ýcó¥÷?µz‡kæü5sØ©ýäéŸÆ^‘ßóí™ßÙäÃÕ7Éš,Zþƒ÷. Öíõå·,öÿ$Ø?¾xùý't7Zû²ë ö]Ú?Æo– ð+Ödâû›nýÎH0žC´|p¥ó¦†Æ6?Oç/›wc}íã‡<Ó_¨ú½5¹zÝ¿nÿWÿÄ+ÏלúäéÁ~½+UŽlðz½¯+üÙNôµ<»áJÏýߌ ö§ dàò±™ßÉÄ‹ëÞ-¹5kæ÷½ù—þûK‹¬™ëkçuCqûŸø÷àøa—ö‡goÍ}ôªµ§[“§$VOÞuØ?ñü“_zþmŸË©?Ù_<þl`Ü€¼öƒgßY|Ec[ñÌñyåýëîÝpSz°Ï~;}ä‰ëü[V|ïÇ¿ýX¿¿ëäɃÏîF_ûÅŽÕ#õWuÿÁzåù®ùAðü4ñØ¡þ´ëƒ˜e«Šc*òoÑ:Yk§?yã¶Gž ô“àõv—ö—öi±Úzåû^[rÂgÎ3œpÜòKfÆe=v½õÆ»­eúûp¼¹[ûÇÿõ¿¼ä5ë•Ï¿öj«óãþ‰û.üî‚Ìœw·œ“{ Ëåï]öܧVÇ'»µ_<öéš…X¯dÌçŠöf0Ÿ»þÝ¿u<7MܱÿŒ«/y&xµY®«‡~ô“’/bGûÃÎ_ݸzÑÍÖAû'î±/¨ºÏ\ï‚ùêqßùä]r¿™5ÓÖÜYÁ)Á?1úí#×üÀ¿…³(‡40Ns¼¹G÷®×·ìœÚnd”öíë–Ï›jsŽŒ Œóý[|~·b|^`<æïÒó}p<°Gÿ®5Ç¿•zÃYÖÁSÏ^ðÁ‘ç‚uØôÁù[Þñú·<2¸ä¾›ðw™óçÌñÞ£Ç{W÷Íýgþ”u@FiŸÞEÿã¨ùœË/NHí[x›¿k~ùú[òzœw=Rq…õÙw¬;æýà–·üßþM}Û³¯×ï:“£Þõw$—¬ú÷ÇÐÓã½ëýOìý½ë룽+Í þN¾yç½·ýôÎ`?ýù³ç}êУóDðz´GûîUÙ/zýé™óÐÓ_Ï Ôqè½—üä[þ-Ìfž¹é.÷ _®Š^õúz¼w·Œ\•vâ[Ö 7¥q)ñO >Ü÷ÅÎÛý[6µY/'¥ µì¸4x¿V±ð¬gúÅÖÓåÀLÍŒSד™þ¼WûÇžCŸ{sý[¬—Ï´0þ‰[Ž[uè»/ù·.>YFþÎ'>SÃyí{eÚsÿë%¹ê1}š¸uÕe鿹w¦?o½ø‘òvà¼ì{µ?ì]üÃè-7™©çK›Æ†O^áŸøÚ•ÇŸ°ósÿÅÞ—G×x}ý“9$‘H"ƒŒ"£È`Œ<·EQ-1Õ\T„éDŠªÒ¢¥ªhQUÚÜÈ­1J™ Èpo YèWµÔPÊïsž}Îó¤·í»~ÿ¼ë÷[ïËZYqž}†=½÷r63Åò…¦¢Ç¹ï0šÏ†ºc¤å)骥('ßuhy:¾9«úHñíšÿ53©ËÂ46ÏÒ´¨Or/ï¯L¸u²V“ÃÉÁ^»Mì¥­ç«æuìd ×ù×å·£ŸVÔéóùéCù˜þ1S”ÊÉÄÝ;§,ë ËñM¾0ýÛgt?0§Y‘w˜!ù~Œô¢|šÓpß‹t>p¶×lÔñ™RÕÏT¶ô5ˆþõ%-.˜îËì¦Ï‹c¤åó«ƒ;D´VÔðnæÅønæ™-ïköÑÔœ;Â7µxsú˜½à"Оô \]¶¿ Ùç>Í|ªYÕó·ã×lܧù‚6ÏA=¤_Ðõé˜ÐG†?Ùý¶FWÍØâ¾;zÝcUÃ.>Ú°{ 35 ùióTÚ›cB?vš_Ý?kRÓ:©Ç”˜a¬jÄÅí-G g¦†} YÜ‘½Öþx¯ }×êŽ =(9“ß"Z±Ü¿üò¢®+ty¼8èêõ;f‚r¥ç–ër<.ô¡f÷ïC·•+–£ÌùÓ,ÝLqyèÝ­=3ù«Ž_³G¯-çîÚ = xQ±¬?˜7·é=Žx«û¯o™÷éó¡u-Vf3¤ü5»¦Í‹ãBÔeÃhÅ’~gwíè‡:>óùutÊb&Þ|öGº_?.äþgiAÍR;MnWI5]t?¹Ø»¡ŸÝv]¯ÓVß+Ši®­‡¤Ôæ×qÒ‡c TN*§§‚aša_öšß­¿f§L#&üt>Æ¡_„üï«¢b®x.Îùáû:ÂϘF« G=Î<.ä~Ïù¥ÝÎRÌŸÏ[wÁ}§>ŸŸ•pùѹŸf¨;!ä~·ãö¯Ú®Q̰Îî&éô.™ØvÖ6Í.›žÉ¼wv†’Àço ½ÿíå„Y1Gý~`ɈhϹs³×6 `¦Îº¥<µ•M»}%º×ÆqºžŸò¿Úzf¥š/_O½Ïª2UD™‰×ž½K÷'„|¹9¹r_³?ÕìF÷™–AºÞd6ïÚ\3‘ƒÔö#´uÍ !o±_Q½hÎìõëàç6Ü´öçUÌä«: Ýîó«˜{7”ê¥ïݘú¨ ýV`S§ýÒèb‡4ÔòS‹Aš>U¹óHztµîz÷|f ávbé/l½³¹ í„ü®Üo°wß JýfUË/Vní³F×»øjy°t_n€àÏ*„¯kÒϸZ©ÚѸIäž}þ~¾'rÁI{fJ}rØõ±!Ú~™6n…c]FÅŸ7wjû(Ush×µâ%¯_t.Ø?^Ÿ=›Á2ïfSƒ]ÚíßlÔ÷ï* ¥£Ö„S;êz±6þä’3ñºþ#ŠÊ~4‡M}ÿrÞ«~»õuZ…77[Ã,JUØÙù+Û7WCV}‡uY…ãÅ…mnõòT*oºÍÆ’ xŽÏÞnsŒMU§™‚zB~b¿¯rß3|G|i÷Ë¡Êçàg`é ¯÷*„ü*~wøËyÚ:¡r2õù±<ÕÝÁñ]fê”u3iê¯Â®Ö£CÈ•^¥²Ïîõ3Ngë|]rxŸÊp}ž‘½ÔöW´¸»RÈ‹ôðá¶J%¢îƒï~®Ëù£Å<c9??ÌÈù†vB¾b]êcE“ìßÑãž%v^cvVèñ“ˆc§Åóµ!úr¥8FúW¥bߢý“t–v|É ì»&D_|r˱N˜#oÑÿ2úrÝZW¶«ÂG©Xðõ¬£u¹Ò³ª/¿pû<øËQ[œÆÒ×w¼ùá@ÌÿJ!ïof„\ÿÈCâ­TðhÚÖ‰U; ì{þ–ƒÕQŸóËX:ù ´ò_½ñü€;G• _oxÌV¬jO» ¯ÿL½ çUãC—]Óü‹6?*óþâ×OÔ=Õsò éw°îçæ ›î×…ý˜:à?>;‹ø·RÈEJÁüì8o'~¼{ë5;;m}XUùÂóœ/2ß¶-Ÿª„¼W¶÷^4'S‹N|–18zë»ú|â»Ê“¯èöAØ=±ž@?Bþ ÷+ååʉ鹞0…ºþר{Eˆ>¥=“òªr'{¨œxúÖ s·rVu®Í’µÝ*tùT ¹N|sÕ³³•Í{Ì k‚zg/»wûõ+]¿ýøÆDK§øí„\…?•vý8õ¯çëtžëýCÓu:]^Ë*íáÀÒ³¶÷I+¼¦ÛŸ*!ïä¹rßVŽ—,ìñóÃÕ¬êBé“K/éúý+Ð7³ô«_¾z-=턜ù6Ï}?ôøgMÞúÒÁU]på©Õ,çÀ¸É?ž‡ÿ«ruß™ð­w’rüÅîa·g÷ÑΪø.pB3}¬yaýŽâ#¾š}¨&9½ñù°œŒGÊqu{o¬N÷#?~BÃr>Ü Î)ºÝ¯&¹ªÇgÅK”ãb«Z]nŒd¯ìýv’ÿôèGò;úaƒçªZ*ÇÎxÄR”U7;éjޝf/~²ë¡Sê‘üŽö:úÜÕ’ÑÊ1Ò}?:tT™ïÔËlÒn·o/]ß…ú$·#÷Õ OåØÜ{A/NñfÕªx›MÞ¾+¶ßg_¡Éãˆ)¤SP96àÎK³÷YuÂÁ g½uy|tçëËYÓ5;ð2« =ÉåÈk­ùJ^9¦nk…êûÉí®­òy夾~œMŒ’ëøW7 mÿ s_ôCò:¢†u¡ðwÜütÓéëPš·ùxËy¥­ó%¡lJï s]ŸûÌPg&ù¾žÁg¶R¾ckE…MoÔŸÿõÁ¾ÝX{±)%7Ã2² 3ÉåpVïi…~o*å™ÅÉ{—§³êöN·{û±œçK¸æêöÅLò9̵.¾;ÖW|[²«n»{ò¤óÚþ{ο5­~Í¥Wnä;ôze&¹Žü¦c‡#Jyôk+Ê?~|™Ýßé,Ûrw,F K—q›™äV&âŸò†o¼¿öOV͹â;‹m1ηŸt˜½Ê·¡_B}’_Ù÷«žÕý¬ô7ÊÑMm~JéΪŸ¨ØùŸÎÿ-¹ÿî_ªÇ]/ÓüA?$Dzq†²Œº;ÊÑ~ÆU7³ê¾k‚ß«î®ïåü݆l?öÒÔé‹CW @;’[™X¿ëˆj¾ ×ÏŸm)Ú{e@gw6¹÷Éu#Vê,$¯²f*ƒ”#»žëj³½ «žñÑýªËÙ¾ÝÙð›øãøÈ·»—¡>ÉëÐýI+Ÿv{B9’ž|ÖÇ Xõ¢-Zþç¶¶îÛBë6ᚺq€v$·Cu»XÐâ1_ŽÄ©üº^­µ¿Ú²l¢¾OÅ·g’7kë!’ã¡â +Cª4{øçØ /~îȪ³ï£Û“-0Z6¥Nl²Œ,$×Cü4éÅîš=¼¾ÅU‡èç;:µIÛÑRÃgKA౯‚6hëÈÉ£ù‚ãú#¹f™<¹a„rø¹¼ªß»íaÕ»¶ ^Á±IOl‹ÚiF=’ë¡„”Ã.[?­;û=êyF¶LLbiÿõHއ Œñû)eß]ùë‰ÿŸ±a{Š®éû÷›z˜í/Þdå~u Éó ÉY³Ëe+øÉï:]¼Û´÷uþP?ÚþЧ¡_’ûÁ÷ÔƒYM~eÏoóÜ×â’n_v8œžtÂF×O~zc³–M&Š~HŠ}”2òŸ:^»Ã¾Z3sÛ¢N§©l·£É»Tu³qRî¬:/¢,`ÃF}>uýî=ÙûÙ„9'æÍøí´#9—ªá›vŽ šÔmBRà,ïRÛ«é½èxóSßàµl·þ½¡’oéÈ>KLË4}9¤.["Xõñ)”^ccáõ9µõIÎ¥¡¶ï½óP9f7t_ϪO­Ìðb[:žYöev÷'óo¡>É»Dœ÷Ë8úà §tFž¬ÖuÅÅ…l‹²§z«m Mûà†º“$ïÕ}¹(ýo¿ÉY?G½|¬½ÇÐB]Éž³1´~@{’k ’‡WjúRJëDV}-ãƒYŸÕÛ7wZe3=œýÓ•Ÿ4£=ɳXug/hx—Ž›½óóy ÷çoæ_¾Ƕ4îãqòàgl<çëqúI’kñ&¼àV§¯NË–ú½—k_öjuà˾¿ìƒ!Nl‚Ý£®O|™€ö$ßâ¾®óžmØY)¡xŸU_ÍiÞý˾fi¸xúlBWˆ¾„ú$ÇbÚ_PJÞáº3X¶™/{° êñê‘üжí™{4öžRÂWm#¼€Çi3’ýXöÖÖnÏÙù¡É­H= ×âÕb¬rFú²®××{ýÞì³ßô{üvÆÐ3lüû­l\zý`¨;Er, ê;c”y‹R,öu«oµó;ßÀ[?W·§z°å~Ì)’_áч¦×7MÓÇoÚê¨&Л?Ïð›,;Œ/¤Î°±r?êÉ­^-¤`RLq 37:k÷Iû»,;Þí¾¥ì¹|ŠäTØxl£¼©û6ØThìÁÌþSW?{$e÷‹ã‡Q½G¢>É¥`ÿÍõÏü¬Õµü-²<š™!ÄIÓ²ìéçì^žÑ›¢ýwÔ'¹ÐùŒ6¿ŠªÂ>Úp )3§¬;¯m5Ëþrî…ð-—Øðo›Wn½Šv$§Šã”¢™_ôêÏÌÏÜÚ8äê;ú½Z?°a2Î:ErËÿªçò‚ùJÅ=Ì<¸àÜÍÙŒe#صe/}jÚˆ”놺Ó$Ÿ|¾+”puìy:ŸE}’˱Ž)¢shf~þ«gŠæ$°ì¡.ÊSV³‘Ü› í€ú$^G¸‡QŠÂ:å]ûÏèÏš{ë&³l¾û9¬ 9@½ø€ú$ýùD{O)|×óÊ&mÿíÛÎÚºX×7%Ù]e£~»°xáÔ3hOòÙ/ö“ «>œÿê\àW–š¸ìŽf³&·ò±ƒ;MòÉSÅ%¥ð›1Qì☙«Ã÷Ùy¯ã4É#/îýãŹ+…o…$É gæIË}Ó†œa#ø¶t‹M¨Güg|wªÁJáÀéü{Ÿbæ©›Î;eLgƒn=‹qÊPw†ø¾Oܧ)l=3qI¡=3¿ùtÏ{¯6cÙ$Ö_®»Ïß÷% éž3Ïé7kfeK–Æ—סñûG5|}Y)àá¨Ë̼€À¿À²;Ü9¬Ìõ UD}â÷^ŠO•Š™ù£ùãjã¾eÙ)ï­ùpY({VêËâïž'óÏŸæ)µ-¼"Ncæ¥^;>ÿn Ë©šÌÒdœy†øûƒjF†+‡K¦÷í8ƒ™—äÏŸ‚esóxå(×mgˆÏ?ð(ômG¥`ï¾10Ìüag—î Öi÷¤†Žku"Œ'ê¿wÎÿ­R È_0³ðÏFÚW4ÔýDüÎ¥û1JÁêþ `"™93¶ÑÓöЛŸˆ¿».¼Áwf•‚wù…š™ùÛãÞ^ õõˆ¿;ïtq .¹©¼ÁOЙyúS'î]Μø¹3X5JÁÀ3³6Þ}–™¹Ùjzpâߎ-WR•‚g†¾~iøUÀ/mš4îwÀ‰_Û¿ŽùçòD¥@ìS›gÓB»¿„zħm5ýüŽ8ŽR h?Œ™g‡ñ#eÀ‰/Û\øÆÊU¥ ¡•áöäA̼p\¯ø[°ËµÄ­ÏÌßï•ï•Ú/`æÏ†ýqw(â¢ZâÇ÷ëŽÂ")ªãÄÌ_÷ìÕí?é€LbŸ¹@ ¯<˜9gðÔ#'>˜Ôcu;%ÿ÷x¬˜÷1óÎõW¶¿ 8ñAœ¯+ù§'§æÅã;ÑoTÙó¤æ7ók¾~–03ÝKB=¢ÿ»ëcƒ:Û*ùtîÀÌ?ôêòÁ-Ä5µDÿ·Ü­ú”™ küÅÄ#g‰îM´ÿÁÌf$8þ,Ñ»‘æ'3Œž7²zs–èüzl¯·^÷ÚÈÌ'l6}tßUú˜ùô’OrVA/Î=D\›¯º×‘Ì|Ùwó+SoNt­Y=ðRò¿w|}âûß2ó/{ÜžŸ°p¢g-í[éð;†”ÓßÃîž%z侂ùAaî©A u爞Õ|›åè»J~ž0ó£wþhpnàD×Ê®üf¯’<˜[JÀ÷{ÿ4ñø9¢O½69w)ä ž3óÃYÁyK6Nr\ÆÃ›fk”ü‡#>>Ö§ 3ÿÙ¦(¼n-àD÷—ÒL¿½µM) õà½ùNàD÷¼ïÜa)"•‚{~þ.÷ŒnÑo0ÎݳÕíãJÝÛe÷àÄ[¯‚Ïçˆîé$¥€Ÿ ¼)ãC=Ÿ¢ˆõ“RÀ·_žÝÇ,àDÿæW÷ÏúáWàWùáâ_ðèþNøÁïgVŽr¸Ã,g¦¹ï[n¨»Dtgí;˜µpÄ%¥¤M‹ï+ˆ?.ÝÙê2&U)™¨n˜0Ëé¶ÏŒüækÀ‰î-›LÛ>þD)YÎ/Fm`–ŸÒF6Ù¸p¢7ÇC=ÈRJè<˜Y.Z.¸Ì{ p¢×„EµÛìÛJiÃäO6ØÀw¢Ó$ô¯´SWcÁ f¹‘™ÐÅqÃ%¢ó{®Þ‹ª”R¾mãw„Õ¸F9ä<…yt‰èݨ.„”R¾=²i « P ‚¡î2Ñ»õråø£Oü&ïᱚè'ž¨Ë¿¹LtoÛüÁ"VÓy¡_i%âêËD÷Z§(koJ<Õt¹”<"` àDÿŽOÜaIj•C ËùMV“šÕáÍ&°C—‰þb_èPæ©¶ÓÑþɳ!á'úw©æm¦r¨D½@Ïjž¾×bEìCCÝ¢¢Ï¥•ß(eX=yÝÝÉj‡gæ;!޽BôçÎ>ôIrt±R¦§;²šñƒÖ¾ZhœèßÍÃû_N+e;Ô€ÕÌ|§Iðà퀫ô+e¿Ô,x°lìï÷¢{÷¢ªfŒT‹x½fe]úÔ9a€Ý?ðë ?-Qý¬fã/u‰ï¿Àr©ÍYÎÒ{VŸ—çòž¹Ü¯—û˜ò~v?Ãx²c骺pfäÛI¦…Ì8{f΋ÉÌ(⦡´ÍŒtî̆x«XÌÈÖTöœØ÷1Òü`ƒø4º½\Ûç‘ë1¹"×™²,뉲ŒÿÖ._½–ÑSÉ?°ûÙåŽ(%Ï~Û õj¨FŸØÏV:Šþ%ä¹Û4¾€[©Õ“¿ÅþµVç9²Åy²øûÖÎk˜‘_3«[Ìzq5Ùñ3r«z•õ }K–íÉ/ôeÏÒ:©Uš'×Ìxô¥óïÔ%3á7™ñ‹/Ú 9Êн:iå:Tãƒä‹ä£ðcr½¦O^Æ-©&q/I»?a÷´LüX9wv¦û§L½þWwŽ ¢{ÚÌj?:¼ ¦óPfLâ3såºBÓ—gÄ=(cFÝío•h–*þîÃïS`ZË$Fº_ɤ¼=’në²ö]Ò/~[ë‹Æ/¯*_,à#z(KÕmÓ©òþ³¦7â~Ÿ¦/ÖóGœ?Ê}vm‰{ºšžŒ÷4¥‰ûšœå½w£z1šh=ÉŒüºÇÌÖ•ä.ÿn‰=MûžÌHë^ÖOÜ[4¾ÙiNÃã™QÜë²Ö ©’_2¾·û+_µvò·<çµêço|•ó5_5¯3‹ý£Àwu`»}‰ß˜”ûíÚ9µ´ ýá-®~0ƒeýÉÿ°#”õ#~±,u›-šñCÆ>GVÉu6ËR‰Y–§í;²>´Oò·yaE§6ò¿;üåÜ ?$Ä=oM¾Öey®%ç¿\×gñ?O±‰ή^zõK&ïÕéßѱNoÖKÜ 7nS^¬Ÿ8Ç6ŠsNãø¥ƒe´f#N]8¿ZÃËÿ¯ò“ûì½R>rÞZÛ?©Ïâ>#ËöáÚ75½–_fL8áðÕ÷élÐŒúþ8žù±eØböœÐ笫×ßúþ:øõñ­ìä³êDûøòïÙ0É?6:œc#Õ0ïEfñãó´¨ákí­ç£õ÷|†Wîù›ôú—þdYÞ‹Ù¢^ÿaÃÕíö§äývM_佋¬ü€ð*!ìu?¶ùt…Fß¡?YE›~ùbkk6TèsVeS~“ gEûÕr_UãÃ0:ßbFÚÿÖð”ûò:—ÐÎçÄy¨æå½-Õ€_“§´/²ž´¿r¾ YöúÞɳþ`ƒißžvȽ÷}",ü²1_¨Z̆Iûªnoöf#Åù€±Ùš°]Ÿ¶f£…žg©× 1_{ð‹îìòßÖñ‰&/+¿¬ÙO1­ý–µŸ×æ•û[}9ž×_í„Æg©_’_ò¼ÿIñ;‹“„ÝfYÕ[m®þƲö¶Zn1¼Á²Ä½qNIJrùÅâJÝîù©(ÄF“Öð–öÖ:žç‚Úü“ñ•UÜ÷·²uü×ä¯|±®Ï†ó]wÃbÍ?˸OìÛkøû˜zËj<«8ÄÊÞjv¸Âª8ÏÕú—vVÒ+ñ‘vZċ˭ìžFŸ´“2nó\³k2ž‘xHûaïKÿ&åÑñ_âk»b-+?¥•ÿEnl¸ˆ“Ä=î¿Ù=Éwyÿ^Ú±1tßñoún=ïYág·äƒü»Û~ÓXÏ¿Þû—qE÷L4=xJÿj=¯Fÿî_šs¶F“ƒµ>K{ õKÎgI—¤Çš^+¾jóLÒ3PÈ_òMÄšžIùkq÷NuHÓçÊy)ãZ?Œ’ú,ìô“Çi~d ­‡4=–÷ìĺ@û{Ãâæ…ä—´ïR^RÖqN°Uü#õߺ¾ôò]ëø^þ–x¹6 7õø¿Ç¹êÿgæ§yœ«þqžšúo#ñ‰fõm$讚/°œ¦¿šãeàd‹yg ݲEÙe;Ì3;è’øaïKùïì¡ö(;8‰¼Ô¨ë|ÑÖeÇ¥"÷4ÊN×i3™ç‘c:ÌJ#~–Rn<5¯t’ÈiÙ5¾!òIgм&WD.i´w5‰\&€»¡ìf"“¤¾·˜I9ÿÜDþèLÊeâÜ=€›põnMÝEþÐÒ²òDÙs¢x_ÑL¹½÷Ü+Wä‰N \%ÞÐmoð®™»x[ô5+¹¢Cð“Nosów}ƒ/pðÍ¥üÐüÝ'ž§ÄmüPöçï@/ÿ¥ô”ú¦bO‘?°DÏÉÐSäˆ6Ñ{ŠjžBŒˆzådVÕ|Ñô.%ÏaÂs—ð܇Ŧ‹üÂ&Ê1‹:­ÝEÞ”4zÛR}çx·/Zߥ¼Ìq ).ò-LJöqh<âg<ÚÇx<àñ›)·J|-¹1õmLƒÈµ–A9ž0FÆHÀ‰#c$žx"à‰€'žx—r¸ð÷ ùÛžªáÿ¸oç>ûðú¾º¾–~ã«~˜û[ù>­ô¡ÜorŸ ü4_Éý"‹û=î︓~mLÝ—Iÿ%ýS}ôO~Húî{dÞÓúþFúéc¤oÜ5Âý‡ôÜop_Áýƒô õ}|‹–Ûxiß¹m¯oÏ¥-¯oÇ!7Õvs›-m5·ËÒ&K[Ìíð=Áÿ zߺaúìЧ=ô×rp€þ:à»#~;‚'ÈÖün Ýi =qÅÿÝÀ'·<Ø6è•{OzÞõ=ÐÎòóB=oèW3üß'Äë‹z¾%â]vô鿎ÞbWó. ÷ƃ ”ß›ç¿ †~o¦|}ÁWè}x5o9ÆláKïØ«ï«£~Æjú-ñÿ¤$Êûx[>ŸKö¥<âjžq_‘»¯–r…ó¼áÓ)Ÿ|‡<ã&¡¿TÀ»§d´K†Žuæý€o ÿápà’ šR@O*úë’Ay»ô—¿£ŽãvÄQÀ úSO¯RïRè÷8þmð8þÍlð8þýßÿú yg5¯ t«!ø×8Ø l3?à— ú´Å¼±E][ÀmQ¶3ಶC]{Ì9{´µ‡líAŸC~ ‡¹d2ÑÖeGÈÎmPv‚:×ÎÇsÊ};—‹¾˜C ÐWc”÷¹zËÉܸî²Täë=®€»¢½kžÈÑ ¸ôÔ ²jâ.òˆ ~ôçšÝw_J¹C<`O<€›põnMCD^^ÐÒ¼öÜ3ƒò†x¢ì…ö^€{îU.ò‡€Þ¨ã ú¼k)OŠš3}63“ÙóÍ>™”“WÍ|ƒo9™CžWÍ’G¦ÑeðÔ3åÿUsïbÜæàKs3™ÍŒ€o™”‡7ý¢] ÆD½@àü‚ÀŸ ”OÍÍ ƒÁ“`îÛPn!(‡, ½¡€‡¢ C1V ”[ô¤\Æütž—7 mÂ@sÊa§%Æn™$òíåRÎ^5O/Ú…£]8÷ƒøoø‘Ayûx_ž»WÍ3z#Á“Hà‰ïQø…ºQ¨µ™òûqÓ"rú¢~4pŽ,|Œ-1 7¼ŠcÀ‡ôc¢÷à“_Û4ÊÒ8µByJZáÿyü žµÂ­@_,ÆŽEýXÔ‰Åø±àE,èŠ<ö.åäyex.þ¦»š3x·®%Wã‹Ë <¾q€Ç¡}pŒw§Ü|ñhX<ÿ¿‰rÈ´MoÆ«ïÃGQŽ5Ï0pL~ #¡–\\"ÆHßO<ðDÀkÉýµñoÛsŸÃm1ÿÇ}»ôëÒŸK_n·Múoé·ëûjŒýÍý3÷ÉÒKÌý¯ô¹õ}ì?ùTîO¥/å~´¾ßü7_ù_ùÈú¾‘ûE™Ëôÿ&÷{ÿ–ÇÔÚÇYû7îפO«ïøïú7ŸÅý•ôSÜ?qŸÄý‘ôAõýô=õý޵ÏáþFúécêû—ò+Ò§HòO¾Dúâ‹šó ¼µÁoðÝp[È׺n‹ÿÛvèÛuì×Á'@×ò z<®Ü u@“3äíŒþœÍ”Sª1·ñÐM—!¤–®(»¢žæHnÃñÝ}%åVWsªCš¢ÿ¦ÀÙ3ò§{G/|÷BŸÞW(‡Ïîƒ1}P'ž¯É}ù®y› [?ŒãïDùÏyNOàÚ|4åñl$Pަ@wÊ[ÎssòP“çÍlo |ßÁh|—ò{óœç¡À1ßBk)'x[_ÊwKÆXmkK´Mâë„Ñ”½måmk œWÉ!”sTÍ7êN9&xîðÔK ¡àð;}tœH9—x~¥Ž”˳U.å%O¾]PN¾©ø–оc!“”“Ñ ‡åQô.è'…Í× +•îð¿øÁhHáýõ¤)m)À7ø¥€N੤B&J¥?¼ÔW€›u =…>ñ×ÿ}ëŽÇkŽÿ÷kŽÿíë(!+³È½hÞ4Ä6(Ûp?’GÓßún ¹Ûæ‘)°CÙr²Ãxö˜Köhk¹Øôë‰ðÊvÂÑ€ÀsÉl8¡­àNà•3ì€3lœ3ðpFÛF(7ôÕåÆ(7"rOØÀ]PvAÿ®èЕû$àæZ.r¢ì†þÜP¿ èh‚ùÒõ› ?w”ÝÑÞ¸»wØ àæº<îŠ|…¨ßã7EÙ´yWOÀ=Qß ´yî…ö^èÏãyƒoÔñ†Þyß <Ùj~BÀ}€Ÿà>ÀÇøø¢¾/Æ÷E{_”ý÷K£\„~Àßôù£¾?xï~ùß9 1fsî÷ÐgÚà[ú Ýhˆ~Ñ.´÷@È0ß‚ÐwÐRÊǨæ,þÁè;8—Lld‚¾C–RÞÂPÐ x(ð E-¢(÷·š{)Oä,D›°”; ý´Î-{R^o5§6xŽÎÃÑ.õÂQ'ß"ð-ß"2)ïwÄ ‘×m#1f$x <#ñ= ߣP7 u£L”CœçqŠŽ9Q?8G ¼£A[ è_b ÿð!&W䞺Ay¡Ô¼ˆI"'"hnÅ×àU+àܪ–\H,êÄ‚¾Xð6cwbÁ‹ØZr/jÎ*å Wó)¢}kàÜõâ0Fð‹<ð8ÀãÐ6ýÇ8àã<ðxÐÜ9„r_ñâjªÊÁÈó$&ÇŒ‘€10F"ÆHĉ !uO<ðDÀÛÞ&DäIG{Õæór!}÷?ùêú>šûb釹Хï•>—3CúXî[ëûTîO¹•þ“ûKé+¹ä¾PúAîû¤ß“þNúºú~Žû¸ú¾ûµú¾Lú1köo¾«¾Ï²öSÖ{öÜÕ÷9ÜÏÈ5‚ô)ÜŸp_"ý÷!ÿä;êïáKQm }€Ü³—v^ÚwiËï þ¯¤¼n6ü¼°Åv¯æˆp¶C={àjÝp@}'ÀœÀ'è£3Ú6‚|Ïñ»ñhØT_Ê ë pE7Ôq>M¢(ß«;êz@ÎàoSôëiÀúóBßÞÐáf(7CÙí|ѧ/êû¹‹ü«èÏýûOÿ< ûšó˜}µƒ>·CÛ€¥”k5}¡M{Œ´RäVE»`´  !øRKá[(ê„‚ÖPÌŸ ¿}OÊ«Ú>ò¶L£œsíK8Ú·Eýˆ<ʯÚ4'(×h»t‘Wt$¡ö¯ pOBûŽÀ± èéÂcxÀºð8¸*€)('£.ü{å8í‚~»ÿ.à{ô¶)3…ÇÜÀ14x×Àcoàd@´Qç*ÿ—Tïÿ!ó þÿŠËÿ'Åäœ÷ÿÝqùã˜üñÀ“÷¼®¹93ñú¢”mP¶½¶˜ç¶Ðg[ÈÕôÙa^ÛAíLdì øA]{ðÖm0· #¨ëˆºŽÇpGô턲úrB_N×9?£ñ³Nä‡Þ6B¿@gã(‘W°Æ¨ë‚º.(» ì>ºÂ~¸¢­+úrÜ p7”ÝwCû& "‡8ô¥ ÊîèÏe÷Í”7Üx{O2QM“(gxÓ\2Wž=EîpÀ=Qß p/À½ÐÞëŠÈZ¼Ñ‡7hóFf=)W8ÏWÝ ß|@¯êø¬£œá¾ÀÁ}ø¢_”ý@ƒpöÜeôéŸI¹®y¾õæà}sàÙs¶9ðjúÛ }§‰"o8Ú¢M ÆD»@Ô „ ‚ð-ý­£œ§Üœò|áÁ¨Œ~C€W2ú ¼3øzB1N(¾ýöÞ¾ª*{ôçP"„j@Ðc„Zn ¤‘FêMï=„„$$X3ê8ŒË(cÅŽ :ê‰ ˆFE ±Å6âØÞw½Ï=—Lœ7Ÿÿæ½yó“Ï'Ü{ÏYkíµÖÞëìµöÙ{­©è*²L£— Ï´ª–øtp¦Ë|ÇïéÈ6œ~ª.«UkµC×GÆ™\›Éµ™\›ÉµY\›ÅµYèjV‡z„/ä3˜ßAò>fÜBÚ 6€{;U-W©>w‡®NÛs™‹ìs‘5ˆïA|êQùyð9ùç!×¼vU/|÷çõ¨)`>÷çsº]ÿ hsºY€l ܪ¦ûBî-äÞBáGæ_t³û w ¡µZ á7¸Pú!Ø`h#G0|Ón0íw«z²Á=jºY„‹¸¿ˆû‹¸¿ˆû‹¸·¼Å\_Œ.–ÊwÚY ÞbÚXBKÐEK6©ú¯KÐÃåûRð–ò{©ü†æÒ.U×Üz¾Ê¿Þsx_sw_sv_su_s´÷{éX{N–ùXæ`ïù×Ûo—9×{®•y¶¯ùÕû½€Ì©½çR{þü)ßÞÛ¯—9Òöë½çG™ÿÙ½8}ùöÿ»ý8ögÏkÞïì}92õµ¦Ô{Þ²ç+{n²ç¥¾æ£¾æ¡Þïºû©9ÇžoìyÆ;ðžWúšO’´,àô±~úóÙ_>‹WdÎ` ,àOæ Æà úy43£¿ÁŒÝ!À A>}ÀñaÌû@w(ô‡ò9ŒgÔ0ð£ãÚplôxþ€;žû#ø=~GÒþHt9²GÕ‹–a=ŠöFÁÏhhñ ÝγÞƒø=æÑßcå9²A ýùÐ\óàíxY¸™kÐZH»hs>øhs!ׂ€]¸S»—І¿`dÎ$`ñ{1siã$î/ä¹F[SøóGÞ%ðî/Ïè.’yDl›{sù>Wlß­ÜÔeèn™<Çè»eОÏ_m·Løç{r…ó‘aÙQåþ†T(77^–A7>A;¾B éâyç¢Í`®¹ ¿˜ï¡ÐáÓ|¨àÁk(ð.‰%Ðg¨\ç~ˆ\GÞPä …÷0t†Lað"0—\£\ð ¿aÁ’­ŽOx ¥½Ú ¡ã:*™ùƒ¯°-züÉ¿U\òŸü®à_“ü»Þô‡ü'¿øo‹Aþâ·Ö8F(ô—l0ýùÝŸßýáw69 €?úl¿úóǸˆža˃ÛƒÚ•ùÆ79†7„{C'CЋ¿} ãCßúÐæPlu(t†ÂÃP~Ã6‡µò­aü>ŽßÇ1FãþqÈ=œßÃÓÃeÎþñ2ï€<ôŽ~÷Gð{÷G ÃHd þH¾ûrÏ—ï¾ôý(žO£àkòBžÑü ìhÚ-±¿Ç ›1È1¦S=¢ÆÊüc¡5ŽvÇqøã€üxè—˜>O@_' ç ðq¿ýhÛ|?ðýø=ü ðþD¾Ow":šH['¢ÏáãDtp"°“ø= Þ&I¼½IÈ9œÉ´?œÉÀM†§“¸vº9 ˜“i “9ÌÞ#$sTï8üS>¿÷<óS¾{_¾¸<‹åùkûÜòLí½öîýõÞ;ÏXµæúÒ@nƒñÑŸÏþòlD—ä¹Èˆ\ƒKƒ¡=„>ò~(xCùÆ`†,ÃùNŸϘ8žë#Ä_†ÖÈÊEò¥ŸGñe´ƒ 7šñ2†öƒÐOøcù~Ç3VæówmÏ—g:˜ùŒ‘‰´}"°ó¸Ì\Ú æo²ŒKðçCsÑåRËØƒ‡ wôïø_ _sÁ›N;ó¡µ ˜eB/åo™ø­b¯|_,þªŒ[x\/¡à†¢Ç0h…1Þ°½0pàL´ÂÀ C¶0ðÂÄzúw‹¾¶Eù±ÖuÁi×4Ú]YS5¬Cí‘<Ù“oáîTkðÖZÈN½Ò_¿›Ü ßMÖg|ƒõßý~r§:çk­‹øé³¾n½'r“^éözWÝk_dû?8ó»Ù9ók ò×ç}Ýι ô®ÒzO¹Sï‰ìéãLе‡È:Ô©ßYöÓ{#ƒû8ÔåõÎ2Økoäf}æ·»×¹ ½/rӱ炼ßGJœ%cÇZ3êRû©ä½‹ÄrWYëB=j=Èz.'é}_Ô~.yŸ#gˆ¬gt ~&שý¢JŒ&ç0d®±g=W»œ5yc½§©P1œµVÓ©ÞÃXç2ºuæ¯÷V¹ULf½ç©P1˜ÄEÖÞ©ÃÚýÖcmƒ—Ð ëÖ!ðÞ"¸àGÀ×r®-Gîåà,g9ôVÀã ÚŽD¶HÚŒ¤íH`"Á‰&ºQÈÅï(xˆ‚ïht |4ð±È ?±ðK{±ÐŽ/Úqð‡^âà!ŽöãÀ?üà€O>øàá%zI|&É't“€K. ¸$àVñ¹J>«0&Þ“‘9½¥—B[)ÜOïú •¶R¡•†\iÜOCŽ4øHƒtÆY::Ïà~²d“¬ðšÑ¥¦ 7p™ÐÈDÞL`2ÉD?™ð‘ŽlîeC#Y³¡Ÿ ýlpsºÔT“ í\ðrÑ}.¼å2^ò ŸÇý<øË‡‡|ðói,]SÿÐ.ä^!²¢ƒBhWL;ÅòvŠ»TVB¥à”‚SN8eà”WŸàU€WÁï ð*ºÔÔW ^º¨‚j䫆‡jx¨§šë«¡¹šµÐ¬…f-4k‘½þëé£zpêÁ©§œzpêªð® ÚMÈÝ„ÜMÈÔ­õð±|C%þÏz¯øß¶ßOïÿ­=ôùÿ˜w‹?Ÿ1úÇñ½œ)üŽ¿ïùû¿uþ3°}Û7°}Û7°}cˆ>‹íؾíؾíؾ1RŸUÂö lßÀö lßÀö lß8AïIÇö lßÀö lßÀö lߘ¢÷bû¶o`û¶o`û¶oÌVû[ lßÀö lßÀö lßÀö3õ{RlßÀö lßÀö lßÀö…zíÛ7°}Û7°}Û7°}Ã¥×9°}Û7°}Û7°}Û7¢´Oíؾíؾíؾ‘¨saû¶o`û¶o`û¶o`ûVlßÀö lßÀö lßÀö l_ÎÛؾíؾíؾíؾu Û7°}Û7°}Û7°}Û—ýþ¶o`û¶o`û¶o`û±“µ¯û7°û7°û7°û·Þ1cÿöo`ÿöo`ÿöo`ÿòÞÀÀþ ìßÀþ ìßÀþ ìßÀþ­5"ìßÀþ ìßÀþ ìßÀþ ì_â!û7°û7°û7°û·ò7aÿöo`ÿöo`ÿöo`ÿVìßÀþ ìßÀþ ìßÀþ ìß:#‡ýØ¿ýØ¿ýØ¿ý[{Z±û7°û7°û7°ëý:öo`ÿöo`ÿöo`ÿöo­aÿöo`ÿöo`ÿöo`ÿ’{ÊÀþ ìßÀþ ìßÀþ ìßÀþ%…ýØ¿ýØ¿ýØ¿ýËù>û7°û7°û7°kn–¹·N=ç,[îçÙ«kåÅHR±¦u.0ZçÅØ©÷é†ê¼:/F’WÎ+¯¼‡õ;ž$¯s:çÕf½W×GŸ Ù¤Ïú齺UÞ+ë¸[½ÿ±òb꽺õÚœ¿^›ÛáĶvÎ+ko€¿Þ«»Ùël`…zOd Ôg·è¼WúœÉFûèýº-꼉µ? PïØáµ_7ÉY§³öêü›ôù@½V·IïðÓgÔ7èü‡õž¶hÿj³ÞÛæ£s`Õ©3ìÖÚεIåɰÖî’ôÁ:GF Î‘±QÅàÖÚ]¨ÎµEç¿òÑ{àÜê½”uV°Kï… Ökx›ÔYxyW5Õ_çÌØ¤rfX{ÕþϹÁn½ T“·óbY¹3½Öõ:ôž^?}~°N¯íµë}½zÁF½¯®GçÐÕ94Zõ^‚vG£ŸÞߪ×ùZu>vµ¯ÀÊ•ØkŸo‡Úø“95¶xåÔðѹ²¢ó„ÖYÂŽÿMž¬vµÏ·Ïó„õ^ßvS£§×~_·:[è9WØ­óeù÷Úï»EçÔ8ÜëLa…:Oéwì™B;–µÇdƒÞsÑ®öYXgZÔ¾y·h­sUë›ÖÞ ·Î¡@_//Pg“佩µ‡"Xí}”³H²QÞÊÞ9Wdí{hWïe-RÎ6%¨=²¿Qö3Ê:µÇ¡K­ÉûÎX_µî#k2Ö9£õSò!HÞyOi½7ìÒë—>zͦN¯ù@/Œñ­pîE w øÀ4À“^2Ðg"í¥ Ëx\.²³z+h?’¶#‘-’6#ÁD7‘´ Í4®EA'ŠßQðht |4÷b‘/–¶bá'~b¡ íXhÇ¡—8ô‡žâÀ·ˆëñà'Ÿ|ð À'p=^¡—Äg’|B7 ¸$à’€KnŸ«äødäM†÷dt–L8)´•Ò©B‰T~§!Ozu‹,À¤!G¼¤ÁK:zIç^pÈ’l²f £ ô憾¸LøÍDÞL`2ÉD?™ð‘ŽlîeC#Y³¡Ÿ ýlpsøË?Ú¹àå¢û\xËe¼äA?¯[…0ùð~>2䣫` à¿Ú…Ü+¤? ÑA!´‹+¦bùN;ÅÐ(¡Ú(§œ2pÊÀ)§ ¸2ø¬¯¼ ~W€W^%x•àUu©Ð¨ùªá¡ªÁ©æújh®†f-4k¡Y ÍZd¯…ÿzú¨œzpêÁ©§¾K…RMÐn‚vr7!w25Ak=|¬ßò¿åŸÄÍÞñ²+KŒ,ñqïýõ{ÇÂvÜ+ǾâÜÀ~Nl+q­ÓJ·øÛÝÚÏþ“ò¯­u+‰ZU[VžÍúÜt»>çÖé•K.@åA²Þc»Õ¹i+g†Û+Gœ¯:K!鶴ü›ô{é:ëµEç‚ ÐçÑêÔ¹ië\E‹Ê•då~ Ôy/ºô9‹VýÞ9X½{¶Î—è}¬‡ÖþÕ-:g[‹ÎÓÖ¥–Ä$ÿªäd“÷¤²ô&û2%·ª¼ï‘³`òîTöRI$9#-¹»d_¤äç”Çõî²S½¿”}N’¯Sö1É2›¼ÏµÞgú¨œJ²?HöÉ»R9›,y”$çÃJðcøƒ|1üމVï„V»’vWÊ| nœNã…Ü1ð·â¨zœ­‚ÏUà¬B®(ä‹‘¹œûi2C/{ ÜK.…߉Ðt£#÷µ—†\iè={iÀ¸‘)‹{Y2"kô²à1 ¼ô߀Nkø]¥à6×@ÿÕp¿H® ?Ð_ßÍôe3<5p¯ ~ê±.X-Ç5"S#×i»üfÚªƒ×fh4ÓfsZªËƒ~ °-È_ŠŒ·n”2¦ÊᣠœbîC«\Ú§Íä¯b ®¦­R¾—ÂwxåÐ,†çÕàÔµ¨Gz´WC«Jh"w°àV ¾×qm54+à³Ex©~WÓàÔñ½zUÀ5¢›pëÐáj¾¯F-À¯€m”ßЭC¾F®Õ¡—iƒ¾l¿ZUè¡YZ ÑÈ÷FdlD®FÑx-‚üzd^ÜzäYO†*vØïçuë~ÿëÖÿÓÎÅü¼výóÚõÏk×ÿk×ÿ“Ö«eÎlQÏ&Ëþ’t?tè¼Dn•ÇÙÊ+‘¤óØux[Þàäµj4lÒyìu»^5 T^ +wQ°Îã¼EŸoösαYyŒüÕYgO.;?}–m§Îmìu–í°®ÑÐâ•Ç9ÔÉojeô:Ë֭ϲµ¨ý¤Vn‰PuNÚ“Ë.Xç–Ø¬s9ûêólT~ «NC¨ÚÃ%u<çÙÜ^uúéüuj—µ·4Píó²ö–öès×n•CIòªZµüuž‰Ó¹[çšUgÛ¬¼v]z¯©[çµëÖ9Ý:ÏÄNuf☼v›tÍ×®U×l8¬ó:ëÜvUΉ)Ýꜛµï´Eu“wÖÞÓ@çn³ÎåÚO×p(Ðù'vê\Ï~úÌ[‹W¾gï®B×rèÔçÞüuŠ]Ï¡CŸ} ÔûQ7©³orÜÊ{W¡öZyŸ·è|‡u]‡ÿ®BŸƒÛâUßÁ¯ú;þ¹¡›\pIÀ%·ŠÏU|&#k|'£·d˜JFo)À¥ÐV ÷SèƒTèAw50i›TxiÝ*¼H?Ü ÚÊ@– dÏ@Ö t–¾ÛW…™ÈZýL`2ÉD?™;U8’,ÙÐÈFÖlÚȆ~6¸9ü傟 í\ðV#Kc$WbQtÒˆró¡YN<t©0¦ž ¹WÈõF`‹¡] íbáÚÅÐ-n tK»T˜SN8eÀT k¼U€W^…\¯¼Jð*Á«Bþj줹ªá¡~ªÁ©æúê.ÕB³šµè¿ykÑI=ú¬§œzpêÁ©g-´š Ýí&dm‚ï&dj‚ÖzøX¾ø°Ö?ïXù§â䟊‘*>öŽ‹%&–xØ;öŽ}Ñ'Ε˜Ö;–µãX‰_%NõŽEé¿cbP‰?í¸Ó;Þ”XÓ;¶”˜RbI;GŒwì(1£#J\(ñ w,h¯WÛñŸÄ~Þ1ŸÄ{Œ×Ÿ\³¶c¹Þùܽc7‰ÛìXÍ;¤Äe}ÅdôéßÅcv,æ}^Tb°ŸŠ»ì˜Ë{;vꉱt|剭ì˜Ê;޲×ÁíøIb'‰—ì8ÉŽ$6²ã!ïø§¯x‡ñë‰s$ÆñŽiÇ}Æ1½ã‰[ìxE│~*>±c‰?ìØ£wÜ!1‡Ä_´öSq…ÄGØ1D_ñÃÏëéÎzº?µ&#ô|µ~:u>¹UWÀÊ]±¡WÞŠ:wuÊKjå‘ ÖùIwêší:\‹ÎS±EçŒ Ðù‹êTM9 f矶ÎÖg½Zuމh•ëMòHŒ+𪓪ëŸõèüÐ;œ\mr¶È®O g‹=õÍ tÞçvm³Îñœ¤ê›Èya9³$ù¹¬<;Õ9 É… yíœ]²¬'ùB­³Ýúün‡:#$Ðp>ûչ&É—&g—¬Úª~€œ¹šË6«º$ ÜKðUg„¤nœ»X\Ëá+}ÇÀwL…ª!~‹ø+Љ€^üÄlT9™" ‡.âøŒv íDƒ 1;Tž†(ôƒü‰è0cà;Z1|On´VI›ð²JÚíQË‘ è;º)Јã{úaU/!^ÓYE¦Àƒ›vÜ\w·«3«ZUÎ¥4p‹¸žÿ9À”ò™A{Y\Ë‚‡>sÀ)â{)× h£¼5ȱ9ÖÐðU Íx_ì`ä:|¡Çrพ'ðÀ4p­˜r`Ê¡SNÍè£ø*ôVu|ÖÑÞjèÖ![²4 Sí7r¯ÜfhäߌÌÍÜËãZ 8-À—ÂS9÷ ¿YK¡_NÛUô]1ô‹¡[^)vP€n«h{5´Kù^ Í*ðÊ¡YŒ~VƒS Ú­ž U%4ÑW°Â·Ðà{×VC³Z„Wè”"ûjÆo#8u|o^pè£Ü:ô±šï«…àW£`å7të\«C-›ÕRo ø-ЪBÆjdiF#ß‘±¹á½¼–5m¯GæõÀ­Gžõ´ñó~õ~?¯û·öûyÝÿçuÿŸ×ý½}äŸ×üÿ³Öüÿ§îW—¹oƒzÆXväÖ:íÒyJ+T;ÿ•îË+Ñ&]£ P×±Ù¢ó—†ê¼Òí^uët^"]» UÕq´rù{å’8¬sIÔyå–й$:t^éPKb‡WÇ ºÎ¯®ã¸AÕ±9&—ÄÏ(X×ql×y’t.»vË.Tç²Û¢kÙø©»]Ïñ¨®ŸP§ë4Õùì*t>»N]Ï1@ç`Ý jÚXõšýt®é ª¦‚§®c…ª«`åšÔy¦7ª V® _Û®EçšîÒ¹¦Ý}Ôµ Õµ6©Ü­V½6?]»¹UçèT®”•ǵBçríÒ5œCu ç*ÿ´U×Í_ç hõªwã«ê5xê=vé\:ï]«®ùØÙ«žóf¢ŸÎE¤sQoÔ5;u>j_—"I×ܨóRwéþ}Ô€ÜùrSoòÊO}TÕ€”ú0V~ê:]ÛyçO׿‘z V œÎŸ¨íܪkàìÐù©»½ê@þDçU çïê@î죾³[׀ܸ÷õí7’W@òcY9;U^@©‘!y¼$÷€•›ã°ÎÉ sýU¨|ÀRÃcA»Ê¿måý Puö$‡îò ÷o§ª!9­\;tþŒ•S[r , Õ¹x[U?É•!9y­\]]*ÿ–äÇ’¼\’ó*Ï”:W«Uå.\’¯ .Zå(pwêœÜswéœÐ ‡NüDÀ_÷#=>ág9ô–¿\ø§/â’”»¾#¡ N$0‘è%˜HèÆA+ºQ´ÏÑð¢>žbá'–¶b¡ ^l·v÷ÑI)pqð~øñàǃŸ|ð À't«p Qø„^ŸIò Ý$à’€K. ¸U|®’OxJFÖdô•ŒiÀ&£³*~§Ð')È›J[©ÐJƒÏ4è¤A' >Òà#{éÈnme“ìô_ºÍß ýLà2¡‘‰uÐÏ.¸RèeÂK6²dC#Y³¡Ÿ ýlpsøË?Ú¹àä26r¡ í<`òà!ÚùàçC;ÚùÀÀ{òr¯þ Ñ!´ ¡]L;Å|/–ïÐ(¦Ú(¡RpÊÀ)§ œ*謆¿2à+À«Oð*À«¯¼Jðªà£{©†‡jx¨¦¯ªÁ«æújhÖB³šµÐ¬EæZÆF-2Ô£“zpêÁ©§œzpÖB« ÚMÐnBî&dj‚—&h­‡õà‹lýZbeï5ß~jâ’8Xb`‰{{çyîÓÂß1µ¥¼cØŸŠ]%n•Áï]_ÊŽI½ãQï\’vì)q¦Ä“vx‰{¯é{Ç‚ÞùÝ%¾óŽëìõ|ïýç¯ý£œaŸõŽÇÐÿ1ñ—sÑW}ÆXvle¯ÙÛ1“½6oÇB}Ä>ÇÄ<ÛØq }ç‰eìFb;f‘XÅ;Faì[q‰HüaÇsHlaÇKØq„?رƒwÜ`Ç }Å #رAï˜À;°ã€¾üÿŸòýûòù½ý}ñõÅÇÿÙ§ÿÿîÓ‹oTpýÛuþ·M^5`zT~i©óbåp«ÓùÛ‚uÞhðkÕ5#wê:‘¡^5[’TÍ+×Q«Êwdåh Uµ!%?›äd³r°uêºIºî9pãU]a«®˜¯®ñØ¢ê›OTùÔ¤Ö€U7¥Såb¶jS·«<›’¿HòYyºv¨zRg|Nª]-9E%w’•ïh£ª«(ù=­<ÁýTýpÉ],ùÃÅèQõÁ¬\ÆþªŽ€<:¥¦W ¿cÐÑ ðc/&IÕX íånµT·qàÆ@7†¶â€&’6b Qr¯SÕð–:"‘´‘Nÿ$›l¼Ä@'œ,äJ€ö*ì/‡ï« ™Áõ”$U-š)n•ëlý¸ WA3Û-ç{J…ÊeœFÿ¡‹‚`•Û,‹öò€Ï&~Ö »5è5GhÂW)<!W)ðkø]ƒ^jà©è°Z,xh¬áwŸ À–C5¼­Fž5À®öSË…ÍôO×ê­™6«ä“v›á³™V#w3ôùl¤ÝfÚ«Cž:äJf#<Ô!O#:oDŽ4øoFºè ˜ ®e@¯Ü ÚÍ„N¦øð’͵l`²á!¼lhg“Ã_.4rÁÍ…É\xÉEþ\äÈyó¡Ÿ~>¼ä£÷|` D‡È\ȽBø*„¯BhB»˜vŠù^,ß¡QL%´QB¥à”SN8eÀ•!{ô*À«àwxàU€W ^%xUðQ<ÕðP ÕàT#[5×WC³šµÐ¬…f-º­EµÈPN=8õàÔƒSN=8kÅÏ€v´›» ™šà¡ Zë]/ó¦øò/Øë;º—uëß¿rýÞ{íþ¿qÝþß±fß×zýÿíµúÿ¶uúŸ×èÿÿ­Ñÿ7­Ïÿ»öåÛþöÿ´µyÇÑúÐ¥û\äÇæ­†º†a‡ÎÃɽÜÐ핯x‹®‘¬kâÕùŠ7zÕJoÕ¹Š}uíBdðÙ©óÓÆÐ¯<ž¡:þ¯üã6èÚ…ŒÙáÑ*¿çðv]G7Z×JéÒµRܺæV§šæF2F‚?²]çÿŒÖuv;tP·ÎYÌïÑü­òèKm.«ö®¿Ê:¹ÇtyÕJïPSæ¸`]+øñ~ª6–•·¸UÕK÷Ô/lÕµÒûéÜÅu*w±ÔǬë¥lTµz¥vÖ~Otë†ðqb€®—Ή=^ù‹7éz_~*÷¿•øUÕÿšÜ­s[Ã0œö"ºTCÉyÑ­ë¦#«¿[媷júèú)uº~ pÓütíô:Ó¸C×OPùL¥®¡Ô4ôÔQ‰Öu w¨zËü½òó{f—ª/2 ÝÏ õª£Þ¥kô¹u¾]«o“ª×'¹Z¥6Ô°jt8yŒ¥N™Ô›Ì+i+¹Uåpµj˜t¨ÜÂñuº†ÉQU‡Qê/òWõ……ªd‹ TM©i^¡j‹y×)‘<ÁR+8“ÏLt“*útëzÁª±Ô#^~H‡®7L›!GUíÉ•,ù‘¥Æ€Ô[±ê .íq-œká\ —kà‡3¦—ÃçrtÏx.äz!m,§?—ÃÇ ä]Î ô¹‚ë+€YÎ Ú/Šþˆ7 ø(à£à5 >¢6Øh`£‘%ºG§<€dîÇ€¿’Ï•ÀÅó—JñðN¼è¹âê¥(øO/^KD†DxM> ˜Uè{ôVÁÏ*ø)‘X ½»Ñy*°ÉÐK†^2°)À•@'•¶S¥]ÚK]Ã_t²ÀK?˜tÚJ.¶ÒMÖ|núÅÍp³–ïyÈ_Cgñ™…²h7 ¸,îe—½®ç Ÿú(yr¸žƒL9ÐÈ7ûyÜÏ/˜µ´½Þ×ñ{-<À[ò _ðE´QĽ"Ú(‚VmSl ü– ›ä.Aîä.G®µÈYn9m•ÓV9픃»œJð+Á¯”ïÈZ‰¬•ÀW¢—àjh§¾k๞kà­†vÖ óh®Ÿ5ð³¸5Ð\\ã¿v¸ßÀýî7p¿AÚ„§fdjF¦fdj†N3|5Ã×zþ,JþÙñH_qˆر‡½N*ÅÞ±FïuÓŸZ3µc‰측¯ýÐ2ÏI,ЗßoûûÞ¾¾wñómÿÞ{?N_¾½wÍÛ‡GÇÔÒŸ½·Ÿî½¦*þ7}ëÙ#->w_¾v_>¶·omûÒö~—¾|èÞ¾so_ÙÛ7öÎý!þp_~p_þo_~o_þn_~n_þ­øµöžiñkmV|Ù“´cû®Þëµâ¯ŠŸ*>ªø§}ù¥Þû§mÿÓö={ûœ½×r½×q{û“¶Ù{ ×öjýÖöCÖº«èºvxA|¢O‚üT¹ UO,(Påû·ëŠoWµÅìºbRHrÿK~ôà`•]j…J= É“ìVõuN Tõ¤îÔ õSµ?¥Qh ªûî¯r°‡ªœÿá\‹ íÐ OR¹Þ¥@¸[-H^ö•>*çztª ´V¸UžpÚZ Íxðã¡™ˆnâ¡­Ä$UO ^`àÉ2ÇcŸñð· ø•àEsmŸ©àFÓ^*xÑèß ÝTàâ…hfÊÊõTà“d•ùÞVp?=Fó;—ë™è0Ùóá/~“ù̧ÍT™— ™Oç‹O€ž e>¡½xàâ‹—99Kh#^æh—I»À—1Òùße´“Då¡‹<Ú*<¬µðQOµÜ+„t‘¹j€[‹üYÜ[+ó)¿×Èw¹þZx(¡ýä¬~>m6Ac-ºk‚ÎZôPä«–BÖ -ø(B–µ2¿³þË¡³ü™_¡WÎýuôÓà+¯„—µÈ±¹k¸¾Næ4¡A[kàm÷×Ðö:h®EžuðÓë„xëÀ[ìºnU+Pjk¬oUq”õïßµOVæ½ÿƵ¶Ÿ÷Èö½ööÿÒº[{¿ÿ³ënÿ)knÿÉ{bÿëmÿ®½°}­±ý¼Vùknmχu?‰lد§&X«® ¨jÝ àÞÆñÀ@UóÆSWØA-ªý`] [Â÷!ÜOCºuM0·®GÜ®ëäp(2 å÷0à‡%éº`ÐÆýãÄ÷Û¤k‚?œûÃ[½jÙ»uM°Ãºe®I ß#¹?’û#io$Ï_èû‚ïË}_ðG1îG¨š;VJñ“tm0øMûc 9ÙÇ ûd cÁ ±àŒƒæ8dÎ8xÏýù¡ºVrïðªKÌïÀñÇ/X× ƒÏ P5ÝÎ Õ5ë‘9ˆ¿ GubÚ›ˆ ái"×N Vu~¤Fñ‰üžD_¨Ú—ó T­ÊÉþªN•Ô˱꣟“U-Ì“6©:Å¡´Áç>§À˔ͪ^ñhúƒç-Úñ§_ý‘}ªï±uĦBsí¬€çi›uÝbä˜N;Ó ¼j3ff7Ì€Þ pg@o&z˜¬§oîͤ™\ŸE?Ì¢Y´3‹k³¸6‡ûs 9™æp}´çÐæœª¶ZÐfUË(¨ýØÅóŸüü ª¾è|à’7èz^í^uƒ{tÍ`ÚŽG‹¼-‚EnUOtEª£dÕÞìÔì’ú¿K ± =,ƒÏèx%8Éð•yXÕ9 iWn„Ô* 6¤G¹2RÿLj|†¶êºž›U-ÏPàùεp®…s-\®1^VB9|.G§+ÁM”ßè|9í¯„÷B`Ü|_Á˜]œ+Äçþi7 œ(`£¸¯+i#y¢‹.~¢á¹ØhÆA ¿WÂÃJ‰Càc%4W»ز£Êµ)AŸµ€\ àäÑN"´ÁM„÷Dx_ë¯\¦ÄNå~­âw>trÐó*þŠÀKæw2÷“¡•ܣܳ®§Òf}’ÊýTäK¥½Ô£ÊmKc\ºÑw:¼åƒWÎïtàÒEèÆNÜÀK´ƒî²? œ"‰€É& ˜,äÌA/9ôu:«ä/¼<®US„‘Çõ<~ç!c>¼äÃkþQåÐ~²,ã§HÚ‘xBði£ˆ6Šàµ½” —ä-ï’åF–"o9m”ÓæZh–C´œöÊÁ/¿üJð+ÑI%rV"g%oï}ÖÞö…*úõ½>ôSëBJvñõ¬Õèk@°®'Êì«kQ3®}{sg2Îa¬a\Ï6ˆkóù=?XÕ…ŸíÔiœÏK¸”¤jÑÍw«ššAnº†~(÷#à#mr¥ë­Šž‹ ¿éq=úð†?V|f¾w}q^ãÉ·„¼uJC½ïŸR{S2ïÿÍþÃßö{Ò5gؘóƒŸ0«/®¸oý®€¿d\}Åð[h§]µµ"øÓyoºÞЍ¾òÆ1ã]'\ÿâ¢ßi¾w÷§ÏÖ¿ò^ΜÌüGÍû¯Œ ]Ôæ:SñcÖ´ùÌ™õÁzשŠ.ôº½ôŒo+7źÞòŸóÒý3'9üîøýW#æ­ Ù¯ßÙ– WNvÙøæêŠ]WÿõðÉ®YÑ¡‡êom =2¸Ÿ¢—{ùúλ׹º~¨¿äâ7§˜ï=P9¶Ö¼¿õÝ µ“S]³5¿«C^Ÿýç©öoðý~Οš‘Ÿí¨Úuu8åÙi'û»†-øÃ‡“"N7ß{dYHBO€­GóþÒ´çÏXû¶#çëßܼc¥k‡¯PE7ë“â)— uu=qm]ÉGGà«ûëï¾ uäCéhÔ£ÿš Æï7íð ´\‡rîÞ3ÐÕuã7»N91>¼lÚ4Û¿ßýÜÛߪå¸ê•„%û\]g¿Ûxþ {\˜ï=~ïC¿íô7··Ÿyæ¦Æ~fMÄiwÕRžGÙ×ì}e†«+o¾ëœ¯²õ`¾÷ÌÐÕ=cBºw}½ùá/7·oüñú©®=ƒ/ßùø fõeßåÎtMTz‚ž/qoÆÌ¾ÊeëÏÕ:òñ ³¶:ãæÅßÝž™ý„GÛÏ|èÆºæéßU—<ÿ´«³Ó5Eñ]=n4®“f^ô宿˜ï½=úÌm5›ÛÏM¯ùrÊT³rÉâŠn =2D‹Øà¹ë_ñØÇ›Gã¶eln¿æ–m¯o|Ú,{çø®3ßÌOƒ‹¾4'é×›ÑO=üàë6¾y`|æ·F§9z¿#sþwK]§©ñkVØv?D‹óãŸ<úeƒmŸ®7Wußz¸Â£¿~o?ã~ý"s;F÷ƒ§ÇÇ §•mÌxÅõƆ;Ï{ùƒŽ~'Y†êÑÇ6éÍê·WMxiH>øzœü6ð©ã?>Ù3ÎßÈyוõÓœ~ŸöëëŸ}ã"sÛù#^ëÊÜí ,Šõ³{¿3«£.š>òå_8ãÕGŸ›Â?þ}–Ç~ߘûeÌèM¿vúéôé ÝÖlnÓÏcÛn*KO}íÙÂ1®©êy==ŽnÑjxÃ߈k?YtÆÏsÿÀÒ™±KKæ™Û.þè„ï÷õôwù„¯îùÅoqä£ÇÑ-ϼ³Ñõú' ³ö·NqäLøîÉ´‚_šÛž#øâÌ•žñXºðÖ{ÏsªWéqsûÐûîØ6Áõúk¿]³åŒ æ’é× Yös›¨yÀf±‚wäÚïØöÜ¿¿ÿÊyæõ­_åþù s»ïe®õfÑò»§…øU¯Ç…ˆ{Òמqñú%o?öUæCæË¾ÞsûVsû˜SÓ>rÀ,š(sxz\l.[ÚømšëõÒóùíE·yžçn^3êÌ×NsÆõÐ.¾pik†þ]\%ÊÙÎsy¨'7Mô7§O÷ôÃë!¿X1âû=ÎxÛòõ;µI¡æ¶ÏËYz‰gÞ,Û”?ø¯×:ãd¨'×|¾qëC ®×Ç<5íÏÆ›v,útäø/ñúbñgaþ9Îsm¨zÙÿ—÷?ú¯k’~þxxѳzæ·m÷ìøÝUW—xƃæ:z\pí ¸žqºÿ™Y­7Ç$›~_ûFsÝYæ¶–ázÆ{ÉY‡¢¶=|ÝÿzÞÙÓÞsÃ]ö|cx¶rÝSú™Ûζ³$éÓwoŠ¿7ôȰ~Ç<Ï÷7ßP´ãs‡ÿ—çç¼uÒ‡æ¶<™xšÌ’ÒÃWÏœ3<=òÞtò¼m®ýi;æ?¹êLgÜ8éôÃuׇüáà×¾¸Õen[öË›.»fªkÎoz~÷¥fñk'ÊL =.’Öúdû¥yúqÿÜ=÷\üŒcŸŸ~0çW…¿ù˨kÏùÛ¬ÛÍm§žúýC/Î4‹.ÜÞõÌÐÑã!Är\ûG„W<ì*ó`ÿ_~´lk޹í̉<Áw{ìº`ïÉy·œ8Ô_àëþu¯ºÐõÚá÷}†Ì?ÏcÏÇn[pÆÆ!k¿p[XcÈuËž÷ôcÞúò=GÏsü°aj\ìþîʹ /yÀõÚ®~'V=Ú<8£cAéøwÌm Ökæ¸gfÈ~ÝuŠ=®‡©q°{ØüÔ¦;\¯Ý¶î½¶O™笻á*ß¼O5ÿÛbV]pï5Ãͬ[Ä#Oõÿî¶Oº¿Øâzmýß®?4÷aóà⇿¼öjsÛœÇYsÍ3猠O?ûÍ€Ð#Ç©~ß}mvÍÂÓã'¾–’4ô´—k«µë•CŽ·Ü~ïðý3̶ê—~ôÀײsÍèó÷^mfïÈßþDè^ü©qó‚z¾t½òÚšó–MzÚg¾zÂdÝè Vú0sõüçѳ:íÚµéÝwî¾ÑáoΨžgãN4>ñÑEGÞe¶‰ôƒî±Ÿ;à©ñ³ !æLò<^9>‹ÈïEçyÓ^;á±÷yüжëGž}Ë`_O\–¯ûÃã¯ÆÑ®SOøcD¸ç9Öy¤ñÜÖ¼|ç¹±ëÜæn.óÈÙvö¯¥eWQrш&š9ªŸ §ÆÑóÊOvuîÎþq`ÆyÎó¢3?ࢠ7˜mçtmzðƒ}ö8O›ç•¿áê¼oç]/e‡;í¿÷—{"îŽ6ÛZ_XL(nfÛýs¼/Ïoý"¸¾à7½tŠW“´Ë<øI@ùìô‹=ño›^/ðØi¦öw'yøWãçy=OØvß¹~Ìá±I=Îøûñ¡Ö»Úœ~×Ï-×<åg˜©Ï㿯ÆÑóÖcwŠ«SľüJ³Ûç_ÖëU9&~óá‡ì¶û¹åƒ¿Þë:ÙåÒãYû#Îü6BŸçúÝó|[Ô‰û~ÙZvËE/¸÷ýOq³æÉÙ˲·zø´ŸN¿«qóì‹—ÿ¡mîtG_Lxø±ÛïÁ^|t¿g¯­üàO\âZ¬í'çLJÄ6Ovâ°‘j<={Yê‚ß<àzy`ƹ¿Éç‰#ºýæ~×vE¥Ùo¿#SÍlŸúã2;|TãæÙÔ•Ü¿xœG/YÓà$ðÏŠþ¦Öl“Ñ1ø¶§ÆÍ³AÖ?vÙy3޳ù6Ó,rÁÎóo¤WÏù|iÇS]/­Z½ã½ ¾wÆÓ´ï>½/ªÜÜzç5·†Þá‰Ï’m?|äfÛ<éú‘®—xhû|ügÏó®ûä)¿ûÛêO¿l}âÔkß ]ïY¯Yõ«¿È:jܾ'Ï|ÖÁ’™u®}ž$rr;zYðFÚ.¹Ól›V1tÿoü”ÿàñƒÿn>ðUããé?~ûäÙþs]/þa‚@8ý»b¯ô¨ÙöZÖ/uÍ3WùWà©qñôo~x»lôEžyéE×vGûÞUù@“Ù†Ó²zǶŸçŸ¾j\<÷QØ{§¹^¼Ê ÌÇVMäùªÚwø·×O}ÕxxzÂY3~·Ü£Ï«¬‰ÞìN9g̺×cÌ6Y-›¾ØÌˆø øÝèêÐ#£Tÿ·ËòãÛr½=¾gðå™Ý95£n=Ï™Wæ=½¥-g¬™¾kÜ´»¿ø <Õïíw¾ú ùN¼#oïù¤lêœ-Ÿ¶˜<£.šîëŠÖþíßÍ—£ÔxhoJ\Ø´sŒkï7kå †Ù½æ›£ç÷wyÖ'Ú¿M½õÚ_šIÝÒÐZGo£Tÿ·ÇM?tñ&×ÞWdå×é·õ¦Nڱؑgû=Q]ƒÞ7ãìõûQªßÛ'X uí}0ýǧÇ4»[ixî‹ÇÍ6¼·>˜é‰¯íçÿ(Õï&Â6¼þ{×Þ[üÓú«Íî‹7ÍwéfÛ‘ÏW=}ÍC®ùê9d¦ØÏÓQªŸÍÉïß°ÿ´ežçÛÞ_=ùò€!ŒÛK6|úÃ)Ë̶~?õG/½«~~JVúßêÚ[G{šÇoê¾ü”)W,~Úl{ºßû÷¿øÛ¿ =2Zõó“_ êü›ûk×^YÅ=k¦£Ÿ+îz8ð%âDëñéñô<¾êï'³,ƒõŒ¯½×NB…NûW¦p¿¿Ò±·†­»£'zü¯¼Äuù]ÛÚ §úû ó쌭®½£k¯¿<}_©ÖÉuœçZtådydL/–Oõó'ß¼aÉË]{¾¸M,ÅìÞ@F?§sÆ|üàõ9ò¾^:|JÇà«~þý¥mõüÎ3n÷tÕ>}ÞãŸ9ú¸jü–Ÿ%8ãnåÐ.ß÷;WžW²sÄ¡==ÕÿËl>3ÛµgWå sO¸Ðÿ×Äýñ@a¬ÃWÀWÏ\™s²éVv¾ë÷a{ð¯Øcv_?C<³m`¸¼0ÓÕûàUÿ?fÞ„†:<ãfz_åø7M®9štÐÜú‘åX{âÊtÛ^ƨñ°óháÝǯ(víy´Ái/püÛûæÒúÇ<òoýøo?ä\íÌ#öúà5.v.xúe§¬píy`Òê“|/sø¸ç‘ΨÝb¶ ºÚ78§ÂÇcTÿ?zÉÐþú½êñ'öìðŽÑácÛ|Þü G3‡øÿò_zÖôóÏyŽŒQãã‘×_¾oß-¹ölÚ :üü.rÌÐŽ~N¿ºBÔíë°ã5{ýÈy®ŒQã圊–QÿeÏY¦GôÈì~ôŽw–|›éŒ÷ÌÆ÷³ 7sìçÊ5>ÖëÁ{t<ç™§¿ðËîþ•Ùöë/‡4â¬_Qãâ!ý\öè'óóÇožRàŒÓ'FW•¾\á̓քãØÙ5^’Þ‰?ìÚãþzôć5»ŸüÑ5²;Úáû¯O-~û¥OF.³çY¿Wœ#ž½3¾Çªq±]÷Çîψ>|ÏIÎxú[ù]§¼àĶ^õóÏ×cոئßîé?5|Í_&˜‡• ú$ý³í‘‹.Å%÷ØEþÔÓ›ç\ù|è‘qj\lµ^oŸëèC´:å óÒ‹Ó‡S–ø6Gyæ¿ÂµŸûó‚$è¨qrßÙÇ-š0+ܱ+cæo?>§Î<4öº7ŒYj¶}“›påŽáÎ{€qj\ly#^ÞŒyâ­Ýߺî>ðÝYæ!¿O?"¯Ân×~¿ žêÿ{æ!o=ý°ûGëÍ–yhÒª·ŸšíçØÃ3Ö‚½YŽóvK øjÜÕõæÍÃÊ‹\{ÆZ yúÿÐY8›l¶IØ6íj×ýœ,w‹¹|5îüÕwM¿ý&מ3¬@žþ?4cÖïÓÚ:z³Â´9žçvEê%7¾¼ß„Žêÿ;»mÝ{Ûý¯¸mOû…£~N®œØ´o¸Çß«œö»€GºÀWý~û©OíÙzI®gßã¾ãÌåWb {fáÇÙžõR{=¼âɲÙç¬x1ôÈxÕÿ·I/§œå´_øò;ïqä >íÔ7¿šây^ÛïÊìyl¼êÿͳ“S î+uí)}jóãSVš‡Âþ¶.%¢Ñl›|ò»ksBÌRñúã2Wý~ËËäè'ëNyãì¬)»õ¬Ó—\ùÖ÷ ¾þtT¿ß”vñòdW…c¿ÇœÝ8ÎRç~Ÿ}î·í„ôù0Œñ°TÝ+áû1Ñq`áÃ{R½ÄÎH2„i³Ùîéyþó½–ÆÏ‘ÏÉðªÐÌãÁ•ÿýÕ¾‚¯5gé~>¢îUµÜc\,T÷ù¶ü;r%pÜå1\}üÕåqÂè5g[NF.½oš%wiÃQãcßëZÍ\ è8uO·o‚ï°ïSŒýC8ã`Õ6꜖Û̓ğgWúç½ôWáäöÙvÈÇxP÷äºÝͼ^ͺüî§¶Ï>ñÎwg¼7lÁh=?3?¬ë8µ°7Ê‘¸÷);]ÿŒìÙ‹ÿãE­;®ÞuøÓ]Ë4>ÔþÕ¶GA9ö;·ž—f¾ç®ž³¼{Ÿ\(vb×t®s¢±¿ —ó/fóþJË¿æçÒ¾n¯Ë¿¶Ïî)F.ÜKÒWí÷D;?Ê“øŽ/Wæ~G°iÛj¸#O9>E{Æï ~gÀY«@íoÊì}F¸Ä…xT_›åò¬® ¹;KfýlÏm¢¬ó•Ù_•݈ôrÞEõVGüŧÍ8ÖNy¯«àuá –€;ɺ#qJq'Óº‰Åò˜6Ì´óƒõ¿ýÃ;›þÎÃ?=r“–§ÎãAþx©5ܾwŽó/žÈ(z储c5ãtŠ­£põ÷?v5ÂaîËøü«ï1玦‡Þ›Ÿç}Yp:ÑZ©÷*{_ï ‘_ÂyϤi‰IMúÜ8—÷ÈÏó½BÝÿÚóöQÞκËÇiܸÔ=¨³øòÌ1¾ezÿT.§÷7(‡çÿ)>ÿYÑ3ùÁåÂýmÑÅ'Mÿé–bÜf1×¾ï‹ày~Ö®—ïÙ„+ñ»»Ÿ=~D8åóóhQFæ(e#=ÏçšžÓ—o?a}tìëQ ®Œ¡W^:dîÕ}Áœ}ÿxä›C[çõ9eÿ©ïiÕ~Ó^g®ü±‹6)N:Œ ÐrsŸÇŠ;#y¾Ÿoø¢aág—̽í°s®Ž×zÞU;½æ˜9ß:÷M/ÑZ¿”ežñšë@y<ÿk¥˜iÚ57K·wh¹åº£ÿžiª„óðº5ÛlÓ÷EelÏ€rë鸺è}ëã*ép•>q~Ю3.dN™ø‘¶{Ô8Šd¼À÷”ÖǤ~_Ùmú3æì[“£7ý9M7ébî¶uø¾ÿ=òóü¿D§¬g͸t¼´ºó•9Â5ö§ÊuKƒ„“í½Dù=óZ‹|<ÿëžþû–sN«Åïæ¦„Y“ îaÓþ½W?xäÁEùù‹üŒ‡WýzÍôòzß©.ü…ë÷±¯&Wõ7øsì;°¤%Ó*ä÷ 1Ï>ŸE2>jø^Ñj‘æz“Ì:zðòÂènf=’–ó]ªÇ±‚n{B‹;£›ä±ÀÛjáñ®‰‡¿ÿw‡pâta–¾/©Ïkãq°iMì° ¬–GåýÁå´èSé$è÷ÚÚ¯úž¼°Nß;T¨{_mgÅxØÔU³P³Zž~í½Àåç…kNÊÍÓvŒÓãQKÏðlÖëy´jÏÓ·"?ãa³<&̶ZÈ,*ä°ÁCÕÁü+!«E-ßïˆr¶ß@>ÆÁfuoÞ*¯í „kÉè¹ë£…³_òì ÷‰r²v|Òóü¿¶_nÔ­Vz]›9ÓôûéyËü^8­ûíÌ’N¶ü2ï8QŒƒ-ŒiÛgÞ•\Óó¸¶ëÙó-ƒ§’c»§{CŸ+U»ÌûNãâOÒLr¾Õzï¢ íµŸ/ß>þ¡É[…sæ¹ãS[Fèsaù'󯔖˜ùˆf\l]E y­y_~„ä›…«† ö† §²[·ßEæ©q×ûµhÆ ÝÒ~ù?}­ÖU!¿Ù0¡§pmÞ0ò³%‰f}®– M¿£Vð9Ñì×¢Û^ú í¼Íxí8{ñ ¯‰Âµ§Ìès¹¤%ã‡q÷ˆ Wäc\l#«÷¾7[­l#\[ß­)9s«ÙWѳUÒ>Qñæ‘é%ïìG>Æ?S6ïÓêFÏ÷޹}»qš™Ÿ„EôÜ7ZÛU¼ý}ÿœ™fßÍøÙQ2æTrÊCz\Û"è`÷†‘Ÿ»f¾|oŸGõøÔžŒ$  ï­+O{*ÊcÕJóÞx=.mÃ3'9UdÚIæë /‰Z:}ú­ŽQoåæ!?㥶i"rÜj£íÊÆÿ·ñòæÆ‘qßÕý«]ºüùÓë/ë}µƒí‹;c/NR÷.²Úžü<°ü›Þòë/ìè/ïÕò¨Vn_ºë}¬#X° ÆËNYì=V›4ê.\Íô,-jÛiãÑ(*ÃÓ^Èy§/Ò3.ìózÛ1i€"\Ÿ.¨Zã2óBRùæ}FîÅ0.vE&t~~¢>?·Ñé&d²ÞÏ»\mß¶XkäqÚîÔyëŒ>›7áý?_-78aÜì"éÿuTGõ¼^|õÒ‘ºu÷·¬]ä“tZïç‘9êé /1Œ—]Úyúþ>‡G‡wÆ¿X;L¸~xè…ŒUÐÿ¢¾¹£å>#c»CϪ)ºK÷ï¨ú.–‹n¯ÞѯÎ3ë`QN(ªÖçF%ïͽU ãewÂ:BuÔA¹Ún[¸å±¸~wwn8äZ\Ü ç¹ò¾þXaw9˸Ù%!YÖѵôð,Ü}+¤œ9,œ«~¼ò‡âfáàûB¤g|ìÚ\¾,ûþ•ÖÑ?wļµc†pËÏëuì°íòc»h{ýÜßm¼}ì;ÉÃÞ}ûOÏî ùNiÖáfù`¡ñì¸ö;ŠX…#~Ï0v|ÿ§qíÎ?G ß´Sˆý^÷»RÏô:‰eÕ‘Y|Ó‡Ö±ç S-Ø&Ü#ƒ^b¯-D%Û»wÆ1NêhsÅ:F¯ý•.á¾{IñšÆåöþBTÜ6oÑ'ÿõ7¤gœÔÅþêÓž1vž­£žÇ G¸ï›ºä½õF>(û­ÇèóŽÆ]f¾ãŠÞÞ¯·NJÚÃÌÓwšÏC‡ ~Rúa‹aömû¶>±%ÔØÛÅ1~êäñ&ÁjïIn‹„{ê”»———šõ3h˜+k\‡­‘¯úgíh—Ï,¿î™—†¸^Ýhú#ŸS&XÅûsv„ÔˆJõžaã¥n^ã»Wž5ß¹´Ó-Í„…{¶bû·ã…óÖ/ûŒß×ß*Rãâx”\}Œpã£îðˆÝmCo±ÚKåSeiyË.Ýiï[lÜ"Ï=6Ïd’×.Ía±Â9œ.Æ» Ç 7wÆóü×/»ÿÎï/4ö×t=3¢NëwEÑcã[ËÌþ+)¾¬¡¥QëMï=u<ã¤þÄßn=ù½Õ¾ŽÑ43¯ó'iþn•¨½ƒjÞÍæ/>ö„ã»­f?ÏøØCbàÅj«}ÏŤÁ3f›qX( @Ì>¶íé_·ŽU½©­ÈÏxØs‹(ÇQJ¯ßv~îE-UUolµõM+o¿ð“>çÍïÈ}}ü·WÍzg|ìyœ>lˆ·Ú¯±{p¯x%ï&ÿ £Ïù>F¯ÿùdvØï ³Nã'{¦~Q¸íecï/¯ Ó„{5Û7;ÙŽF8È äÎÉÈǸ؃ÝgÀ™Öñ~d0ÔŒÇùý’9Ÿð9\TÎòŸó»»ö"?ãcoˆÜèèñ8ž¼fø¢‘ýÌ|×¼5$-èƒûm—oXð˜–;jÿUÜ™ÀøÙ›¹?ª~BùDßF™ñÙÖþÀ„CçÌ:V÷¶þRû3Þ ŒŸ½Êžÿ8=sônîòþØèyú  g‚¨èùä¤ôäc¼ì½EëóçñùuS|†žîºÛj}Ýjîø=K¿¿T°}8ÊaÜìåï^L¿èÖ¢|™ÑGuÑ g~nîYùL¿«;t?{éú í ë85;~™pïê•t[ÎTýî=ß¾—NPø¨¥Á&XÇ?•"Â]ûÜÙ„A;̼(»°ùPF/ŸA>…i¶åи:.¯ÿ½ NvÜýIâ´/Ìþ‹ÏÓV¿¯Šù´Ý¬Æ:N`¼ìÉõaäþ÷­qòBíè³{Jú? Î^”Çú^Iï(ù"?§˜f¾C!7Lfw)¸ö-ƒ¶ÑvÃj~ äË“ôB2É:¡ìGÝ{¨9ñ»7@è{ÅJqiäüOÇù2€ñRA ×v]Ö ¶³î7/Fµ]ηå‡3ú¥ô§G{”ÞY½’ÕmŸAÜ+Üo‡:¦ôšeúÅv7¶Þ鯾èßuåëþá®s(ÿúøèßtîƒôÛF~80^’ ëÎ a/Œ¯×%åÛ~ºò»uƃGµ^ùÝN½1WÞMÊïpé³]q¨3gSÏíÊ÷VŽâPmà¥/ýnU)Õ.åßñ½Ð÷Þ¾Êß*pHß Iÿö(›¾‘üNñ~·ÐN²U'Ž'és+\ùÜÚ®xžrwêIå×¾DùÛB¾ Ä­ððµEþ>ª•?{Ȳ‘|©]Š iCš˜Jú²G9¡'•-ŒKê¤÷5É“ŠpøæI¥·éÇí@úÈÅZÍ>µ¢Ž*Q>µ>D—(nÔ“Šuºò£Õ¥ühaìb1v±ç!ò¦ý~\ƒò…¼ñÈßtϬF‰ÒOý öQŸˆøDŒ_b™òOüI9ÌC•„ôI—”_zäˆô»”o,ôíÆ&ÅoZ¢xMNAÛS&*ÓF³’¿ùS›X䦡¼4ô'­žÅo:ò§£=éM,Š3>csJþѱŠs´•E´äÅXfWœ¢hk6ÊÊF8§Xq‡¶*Ÿ¼•¯.”‹yÈE?s·+ÿ¼¥Ê ò桞<„óPOÞ%öï"}å#íà.Å!ZÍœVŠ'ñ­ÊoïXö×/ýx¥(ÿøÏB¤-,Q<¢Àưö¯&}xan‹Ðþ¢*æ½®;»]×ÕÝ®ëÎ_«îôUóÛ¨|c•*•;^XW^{aü¼º”¿q¤í^¯üŽÇ+^˜åw 8ôÆ\x·*ÿSñS¯¸aJ7 …‘×wˆâ†A½~hƒ_ û&'nÛ?y/Ä÷Bßz(Ÿ•UŠõÒ·Ÿ}¶+^´‹¾áó§p—âG;è;$ú懾Ã!Þq#’wx¢o ¤¯Jôìß‘/qAˆ jb1Œpð å£2Gq7ypÁ mH+sÁHß”(‡l•Âø„¡Î°óŠa²“ ß”’_á´—Þº%·x©âGùQG!…òéM‹Þ‘¢KÿËæ‰Á<Ð=7‰¸X´…îRc—8Âq+˜CY™?|ƒâGÞ„Rö•ФxÃKg8⟈ñK¬b?“$*“†(^¤Oêbÿ’’Ï¥•Eè¥Üྊ|ó§ )èk Â)ÈŸŠøT”ŸŠü©ˆOóU\ßeÌñŽpz©âôF|ÂHŸpÆ.“˜‰ùÌD}™g!>«Tñr£ïÙ(+»Zñp#œƒ¼9¨;ç$û²—ü2BÙ¹)Šw»^ùµ«¸f7õä!œ‡zòºØšôyYÏb?á|¤ÍG9áÌYPÍ~ÔÈß½ô_¿]qÎ䰯ˡÏÂpææ.D aáìÿ^ú¾ÄÜ QüÛ$HÓ?Ò—¤I7zrjxêCO]Hú€A:ôœ§N#}öÿÑaÅÝXw‘Þú%}å©«H'‘."=D:ˆtéÒ1¤[H§>!]bë[\«7<õéÒ¶ø¿t€-ÿIî{Êz’ñ$ßI†“ü&Ùí)·m^b’Ñ$—myLr˜d/É\’µ$gmkËW’«$S¿äy!®1¯íÊ¿ï%åÓe{c^¼ o̳þö}POü?¿öÕ«y_Í>Àš=O¹ä|EÞ¾ÀL?Äõ›Î¼¯Òÿn1ó\I~+Ô„p0þŸ¾O^Íþþúù¹o]²m »¤¸©Î(Ÿ¸ˆ"3–}ÝÆ`è ‰x¤è €üØÆû*¾(Ä' ]ɱŠ ÿŸH2ioDºÇ2·S2ú–Œ¿“Ñþd„SÐþü‚úR‘? óŸ†1JGºtÄgÐzFú,Ô›5–ß9ø¿ä„ø\ŒQnû¥ÍÛÎ0Œp>â V0o„ô;‹r‡6)ÞûÅ5±(¢ýÆõ}éõ}iu·ëûÒ_ë¾4\ÍÚ }éOìÆ¼áY^ñø!9[ÏË–üžvGÚîèo÷.ŇƒpäõFZoàÈcéöïEàÆ§QùR/U<9¾¤8 1¯¾Èë‡zýJ=|©(_êˆ'_>½߻ԃ³ë±Ïtæì–\:û#ìp__ÅWˆvôE=ýׯLq(®Bà!à<ûi•>ÔëY´HžB`)㌴Á#Œrè#ÉëúC‚¸Ä…œd¿éô A(ê E¹a—0Ô†ö„uyøKG|øyæþŽ@|ê@82Eñ¢üH”GvoQ“Xâ£O6Fd×p Â1(/íŒEcÑ–XŒMl#‹;éeÅ!>>€ùéMúAG^zÏI@ÛZ™Oœîë¥tÄ'¢îDŒub5~Ÿä«øQ~R+‹Í‹«™gPr NTü‚ÈŸŒ¹JF8ád„SÐ×ô5á¤OEúT”ŸŠúSNC8 奡¾4`!ùÓÑÞtħ#â3Î@8áLÄgb®3Q^&ÂYˆÏB}Yõ,ª³QV6Ú–ÝÈb;ys6ç ûæ•>ØÑ×Aç"A.ú•Û¨¸–&*ìhGêÉC8åöe¿¼Ò{#sªç#œ´ù(§cV€pÁjæh"þ%âQ"nDɃ8„}¯meþõB”[ˆ6WÃâ™—Rr "\„ö¡œ"Zó$G韭;IozêKÒ•¤'=uä/éE[¢®ÿ¥IÿÙ:ÏÖw¤ÛH§]«Çìý©­¿lÝeë­ký`“®"ýDúˆt‘§þ±õÎ/é[ß®ñÜ»’ŽñÔ'¤KH‡xêOÝaë [_غÂÖ žú€téÒ$ÿIö{Êý_’ó×ò)ÿ’L'9N²ÛSn“Ìö”מûÛxÕ¿VöUí,yMÄÉÜbü4* ¤é4äs×i|ªpг‹yÈ|ï\úaü× xèµÁƒ‹¬Œ9_ÉïJ¿!Š_c¢ÌÀÅ)Qƒ¬ÕàRælí_Â|­!‡ L߉…¢­¡/úæ$ù#ª÷Ò‘qÔvűŠtÑø;Çä0¯j,Ú[Ê\ªqø‡ºâÙÃ!á¤âGE¾ç™ 5)@ñ1¬V< hÏÀ Š{é’1†ÉôéSЯTŒA*Ú™†¼i“tħ#œôø‰p&Úž…pòe¡ß9(7õçàÿsγ?ïAø‹òr›˜—¸ò7yòg0Ú•ŸŒ]Áæi#.Zâkü§¨(úSXϾ¾‡až®ï»]ßWw»¾?þµîSÔøŸTܘëÏpïE2íïŽ5Ú8펹ìÞÄK¸pßy{ ¯wŠâòÆXyC.ø /ùõ–\CXŸ=Ç*®¡&^î’k󿋼~¨—|¼ú!ì‡ôäo´âɯf/”Eþ&{õà¢ø¼›ÏÂþ¨›ü—õE¸/ÚÜíè‹>Ï¥~UŠc(œ9 €¸@´1iÉÿH„¸ `üJ#mðXÅ-„´ýK¯âB‚¸Ä… =¡è{hsw‡Bܰ*în´¾•#îîð.ÅÝpÒG }$ÒG¢ÿbï=À«ªÒ…ÿli"5ôÐCB€$û¤ŸôBzï ÒHBÔ1:ê Î8XP,£Ñ±  ŠE=±Œ†Û;`ÃrÇöÿ­½Ö>ç˜Á¹3÷þ¿ïóÞIžç䜵×[×Zïj{­÷÷b&œPq»óTÜnà'yÉ8B“HO"=üÉ”ûdÊn ¦¤©XÝ”ÝTô˜Jz*e5µGÅ熶²z@{´¦‘gŽŒØÜ*.7ùÓO;Åä†ÞŒn›ôLèÍ$=‹òEYÌjUñ€ÀŸ]£bqçžCzô<ÑÝ“²ð$í ü\èÍE·¹ð›Kzéy´«yð›ýùàÏ'>ùó¡·ú H/ ½€ôBòB!e·ô"ò‘^D.F÷ÅÐZŒ¬‹){/dõBW/1Þ–±*Œ˜àÐZBÚÙ¼áëÝ%ãŠXèF|päX Ÿ¥Ð] Ýeî2v“ˆ ¾ º>¤}Hûëƒ Ë)Ó夗·Ê¸¥".©ˆ+*b1‘i¸+Ðw%tWÂg%2­<#㙊XP"^‘6ü€õƒ®Ÿ°iѯŠ?çù±9nšc¦+ÅiŽb\cŸ9î9σÅç¼ûâ!ŸmßÖyœc“9.õ“œã"‹±Æyœã‹óØbŽ+ÎcŠCÌÈæá<÷c9ß5û{ÑÏ›±d̾ݹ_7ût³/}¸sÿmöÝÎý¶ó¼×¹ŸîÝ?‹>ùló^ç~˜ºÿ»>Xô¿bn±Iæñç:eUŸC›8~ýDÿJô'°h_®Ô½ðM.ü€¦ ¡ Aס|%_øG5b°ñ= šÃ 3Ü]6áómyÂG˜zŸŸóȾ„FµË©ÕhÑ·‘­±^2ïXÚê8Úµ¸óí.ú-hG¶ ÀL,“ñÎÄ´kßâ>Édt÷ÄÓl*v Îk‹3ÓàLýèkÈ›ÜLžÏBÆYðžÌsÄžžØ×\>|Ï£½Ív>üæ#ëðð{¿"çBpñlß‹]Œž^ÐôBG¯32†›ˆI¶„ßÞ´eïn»FÄ#[ ÎRÊa¼–ç÷´}kùVûÍ—üÐ]Ì+g%ÚçZUüMhÃö‡‘¾…‡A[øºN{Þ!ã° orŒVø­tƒð·(º“óñ¼&w™F’7ž#‘Iø8ÏQÐíªâm"ïhò„£1ÀŽv òŒ…ÎØ&gºÂÉÏâl"ƒ;iá¯AtSãI§ÜÄ}#¾&øyÂV[}'ÖÈøa“ÀŸDþ$è‹û›¢[› þä-*ž¦‡Š§IÙLAÇ©”ÇTÒS¡5õ„Š¡Y£âgB{´¦‘žÖ®bgÂk:´§“ž~Æ)n&ôfœP13IÏܪâe’žEzéYäÏöT12)§Ù§e<­9¤çtÈnÕY=I{Š4ùsy8ùæÂo.éyЛGÿ5}æAo>øó‘o>ðóÉ_@zé¤^ˆü ¡·²XHzù‹H/¢ìS‡‹¡µÙS^ÐöB7¯NÙ}/±ª¸²+÷¦={Ã׽·Îù5*†'r,%½Ø¥Ôá2d^V¦bwBׇ´°>”2,G¦åä/G§å”¡/||[d\OËÓˆÝ ÝÀ®v%2­Ü-‡ŠU¤WµÈž~Èאּ~È`ô•âïlc§9fŠñÒyŒüG{Æb cŸ9îõÞ/>ÛøöKóds,ë=~mÌ2Ç*sœã“9.m<2Ç!s 2Ç›ÞãŒ9¶˜ãŠOÌqäl{ÇæXÑ{Œè=.œm,èÝÿ÷žOÿÒrï¹´ÙŸ›ý¸Ù‡ŸíœDïþZ”i«‹Œcè¥â0w9ÅH¦|ûÓû#â› D—kmÖ•6áJ;u%o0øƒicCÊdœã¡[å´ë\h øÃøÖ­âSîÃ;U còÜhûnè%ü§ž×­bרÅÀ¦-ŽýcŠG ]áËKøÕG[w`Ü‘u<íxün9u›Ð.§oFlaxNäù$p&ñ=ØÉè.îjNýò{ãÑ!›õ4tšÞôÿWôWÈ0š³Êd\ßÙàÌF¯9ÀyÂךžØë\žÏ…æ\pçÁc´æ3ú Ä·‚¿H|(÷Å轘6ìEy!çW#Ø›ooê›ò=ßWÆ^Ê÷Òn?’2ôáÛ™| »¼CÅ‘„®/0+}ÏWBs%|Vñ|eà??øùmUíBü­oøŸ0§î›OÿúçÓ}óè~¦ô?#cÌj”±Ö­bØoTñf±©~y|(ß~äõÇû‹>›ríFۧnE"ÃðÝ*v=y#apnØ´[“Š[ á¿ûxó‘c>ùóI/~é»e·»2^Ÿ…"Mþ"ò‘^D/¦î#óbh-¦ ¼h^ðôv 2,A†%¤—@Û›´7|½i‡ÞȾEÆ0>Üó¡»”ôR`—öÈ8ÉËjd¼ûeèîCÚXŸÙÍ/v9ùË¡ëKÙùR¯¾Èä î dZŸírX ìJè®DÞUÀ®"½j“Œ{ï¬òûë‡LF_.þzæ˜(ÆÂÞãßÙÆ=s¼sÓþÕ93rþÃù²yÞ¢÷<ÙsÌqÜ絢=[Ÿ*úÒæLÃ6'} s?—'ù‰bÕÐKÃÏAŽs¨Û~Ôqÿ­rú1ýPW©O»v u2ˆº$ú+1E—!â³[NM†ÒÎ¥>Î…Þ¹Àã{4‡ƒ3:ÑMÄàAžˆÕp<Ï#$´„o÷QÐŒðC=šß£¡'|÷Žne)üq _”ã©ÛñÂöÑc2z? ПÎ,ÚàlÒ ‘yß‹aíp1<#·²xƒïM;ó¦Ÿo•U¾T|Ãg)ò,o2úðí½å´·å»e|p_èù³’g~Ðôƒ¦_‡KßÞeß\«oïò¿:çï°)—“|NñùˆÏÇ|>áó)ŸÏ\Œµ²Ëç|¾àó%Ÿ¯øüŸ¯ù|ãbÌ×\¾åó7>ßñùžÏ|~äó“êëø§1ŽhŒ#ãˆF'§1AÓ©ûÓŒ)ë,ÎLclÑXgi¬³´êŽãŒÆ8£1Îh¬³4ÖYã6NÕÅþ5ì_Ãþ5ì_Ãþ5ì_›ªÎ*`ÿö¯aÿö¯aÿö¯ÍQ{¶Ø¿†ýkØ¿†ýkØ¿†ýk‹Õ>ö¯aÿö¯aÿö¯aÿÚr5WÅþ5ì_Ãþ5ì_Ãþ5ì_ÓU?ýkØ¿†ýkØ¿†ýkØ¿®îŽcÿö¯aÿö¯aÿö¯Å©ûqØ¿†ýkØ¿†ýkØ¿†ýk©òþ‡†ýkØ¿†ýkØ¿†ýkØ¿–«Î1cÿö¯aÿö¯aÿö¯aÿâl†ýkØ¿†ýkØ¿†ýkØ¿†ýç<° û×° û×° û×°ã]$ö¯aÿö¯aÿö¯aÿöoì‡cÿö¯aÿö¯aÿö¯aÿÆ ö¯aÿö¯aÿö¯aÿöo¬° û×° û×° û×°1†jØ¿†ýkØ¿†ýkØ¿†ýkØ¿q/û×° û×° û×° û7îbÿö¯aÿö¯aÿö¯aÿÆpì_Ãþ5ì_Ãþ5ì_Ãþ5ìß8ƒýkØ¿†ýkØ¿†ýkØ¿†ý‹÷´ö¯aÿö¯aÿö¯aÿö/ÞhØ¿†ýkØ¿†ýkØ¿†ýkؿ؟Ұ û×° û×° û×°±FÒ° û×° û×° û×°á“@Ãþ5ì_Ãþ5aÿâ¯ÌåßÛóº«Ìél§[¯÷iê¼ûnu3MwïQçÝÓ”ƒÝjíeQgzÚ•w¹O÷³õ—Už µŸ{÷pzÑ£Þ_”É»Q†/7åË`‹òeà®ö󶨳ï.êìû&yöݸ“é©Î÷lR÷¨\Ôž_ž:ß­Öc¾êœÏéÛÀØôRçL·ª³ðnêŽf‹òoУÞk¤©óðê<¼—Ú+l•w5 ?Ê×A‹:ûÓ£îgYÔùŸVu>ÞMªqº«åªüä)ß­jývBÝãôTg‚ÊÔ™Ö­ò\«±ÿè®ö Óä>¤q޾CÞ÷2Öwîj—¦ö(·ÈóâΧq~ÈU!²ªsöåÙXá?Á8OäªÎYÔ¹û&5çíPó^µ.´¨µáFõ^¥Cž70ªw,¾ê¾Y™Ü5îvÈ;¤Æy$7ufߪî“nTg÷;ä½R㌒›:ÇoUsì¹Æ4Ö–[äSq^ÉØKu“ïgŒ÷1íò¬’±¾tUïf¬ê~i:Ëß.}4çx]ÕýÒxuÇt£z7Ó©Î-m’çzÅ:Ó8·ä¦Î<Ä«3ýyê¾i«ô× Î/ùÝxÀ;ù L@‡ˆÌÚ:pêÍByY g.P| l ô{äT;¸`ä Fž`hÃ'ÝCàâ!§à¡È \(ò…B/Z¡Ð C†0ä £ìÂ# ÚaЃFø 9e·BÊVð¬àY Œ"NÈ©|$ù‘èI~$ùQЂNt¢ uBNõ£áƒÜ1<‹A¾XdŠEžXðb‘?fqàÇ!Gí;޲ˆëQËpà‘€¬ ðO > Ÿ­Dà[¾«‘e5²®¦­¯.é„\B$ó<üdð“¡Ÿ ýtHAŽTh§’—J^*²§"{ôÓ5 ütè¦S_é’Ž|ägÀ3ÜLp3ÁÍäy&åŸE{Èê–Ë’lp²ÁÉFŽlpr¨Ó\äÈEÏ\ôÍ…G.< ¡SN!8…à‚SNåW N18ÅàSÅð.†O |Jѱ¥Ð(ƒ~°eÀ–¡ð𨠠*NÈåÐpÖSɳJpª _EýT¡C¼«À©f 4kàQ ÍZ䪅f-4ë)»zô¬¿üän·ý›m¦|›·ÈñÛøë;çÓ·Vî[+÷­•ûÖÊ}kå¾µò?¿VÆö5²X‹5ð?ZÿŠq´ÆåßÛÑ&§w5½î³”9CÉ“wí¾<òÔ]Çê>KžòCÔ¥|øYåÙoãÎ÷ ùÞÀxÿ¸EùñsWï[œîµxª÷íê^‹¯:¿×ªü¹+D­Ê‘‡z٪ª{“[äÝIÈ—:G¾EÝÿvUï"ËÔ—å×Ï¢ÎóµJÿDÆ;I_u×¥]ݵtWwÁ7JEƹ>Ou®o“ôSdÜ¿ôUï&·ª;á.ê|_¼<7cœñ;¡î†[ÕÙõ­ê~¦»:ë×Ô뎸U÷Û(}ÿ {ƹ?7å0^ù2Ú$}Š»1ÆûLõN3O½×lUï6{”@å#0O¾çS1qFPø$2Î º©³òñêÍ&u—¦[tSgè­ê®h‹ò'Ø©| ºªw?VyR¾ŠïQA™âñÌþg¤Ÿ?ã\އ|¯#| ¿+âÌ¡¸«nœÇwW÷NãÕ½õ­ÒçŸxŸ*ü³çôÝÕÔxåŸp£òQØ¢Î$vÊóûÆ;V`ã<噢˜VyŽßxïä¦ÎÅ«{ìMêþj‡ô³dÜÙqS÷ØÓä9£ðÝÒçK`ž:ë¿EÝÙéQçÝÕÅ4ug§Lúƒg„Ï%qöߘC7Þð@þòCà€Üz—œ[(£à-Ð W t¢àl g!È$àÐ3ùƒ‘'Áàó,>!è^Í<ž‡Š|¡Ð …o(4Ã! 9Ã(»0ê= úaÀ†A#úyÈe¥~­U‡~àYày$ÏÓ  Hô„n$ùQBVèDA' Y”{4°1ÈÝDžÇ _,ôb‘'¼XäC–8ðã#޶‡Üqèn²'À#YàŸÍè$z'ä4}5räAw5²®¦¬ªá]-ÒÀ'“— ~2øÉÐO†~ :¤Ÿ í&x4Q^©À¤"{ôÓ7 étò¡“|xB§œBp Á)§œ"ʯœbpŠÁ)¦<Š{ä¦>¥à×€S Jô­G¦2ø”SÏw%<ê)ÿJðêÅ3x4A§ õÈÝD~4j)¯&hÔói@–†4¹´i†F3åÙ ¼1×}þÄþgøk±¾ks=㼆õ)Ö-¬YκVq^§ˆ5ŠX—ˆµˆ¹q^ox¸È5†¹®k çµÄ/­#þ³5ƒX/8¯ÄÜ_Ìû͹¾9Ï7çøæü^Ìé͹<¶cÌãÅüÝyÞ.æéôcÆÜû×6ß>Û\Ûyží<Ç6ç×æÜšöb̫Ŝº÷|Ú|ß$Þ5‰÷Lâ“x¿$æÖb^ý®Ë¯ÃÝå‹ú„º{Rãtv®Õá¯Èð5½Qù¢ËSw½ÔýÄ­Òá[(OùãHSwýÜå=á“CÜ6Î![•Ë&åºIYó’÷‚zøtK?AâW›ÏÈ®Hø8wŸ…¿Æ`è{È3[Æ=Ap#Êd·'üûwHŸ8ÈN›ÓŘì!"Óµ.Æxd‰¦ü郑9Ý#Ð; ¸0ä‰.†ïžIð‰à;†ò7š1<‹A‡~Gñ,ø à“x–Å'±EnÕÅÁ#Gð@¦xäO>Ù’#¾9ðÍáwpåè^rô)§L×ò,C<ƒ×Zž¯…ÏZòÖ’—ƒžåÐÉF)e¿k¡½–rZ‹¬kE>Ÿ<ô¬¾ ¨«µ" ½Fp‘±œ2[KÙ×!ò×ùª®zÕЪ†~5ôêàß(¾¡Óȳ:t¬¿”çEèVA+c-ߥir+0ŸßÔy¥g‘±˜ò­ã*:ŠçžriY*ÆXä©„wŸbd©¾}*йz5àVò»¹ê(³&ÏóJÔ¡g 8Ðm4)³1C«Y+¡S‡žBfÊ  :5Và Zü®ã3™Ê”O¦ÓÊoI™òYÚ£|éÇË;–‡Ÿá·Ô³—?}yþïî ·È;4Æt‹ºƒÙ®|—z(ߥ[•Ï?Of«òâ®| ´*ÿL.ʯ~™ô…2µGù0µ8ÝG?¡üb[Ulš­Ê—©«Ó½šå¯ÉÃÉ_Ón£ÆWÝñÜ¢|šº)‚Mò¾§á·ÉUÝûLsºgsZùŒwÜU7|8y¨;¡-½ü8Å«û¡›”?§.uÇ]ùâOS>¹·HŸü†ïWåãɪü¸lT¾žv+O®Ê¯‹UùvÙ(}ÚK?¦FÜ›&û¦Sún2îïxI?þâNŒð %î®÷R·Ê»ð†o7'^ÅÇÙ¤îøtÊûñ†Ÿp7uÕ¢|¬Ö¨»?[åýÃg¢‹Šà«î5©x:í2¦ŽáƒÕEÅÖ±¨»¯MÊçT»Š°Uúc5îÖ»(‹u7¨CÞ«7b츫{BiÊ×T‹ò7Õ©îÖ“fQ~ó”ÏòMòž¸_ŸØ.}— _5F¼Wå›Õ"ý³þjj”oÖvéŸUܵ÷.Úð€wt€ FG6 0Ê*Š| ô,ÐÉ€^ ßÀ½@ä6Ø`äFž`tÏ/2¨Ã p¢Ä7|²ÀÍ.‹:Π¬Òø]ŠÌë “A^¸ ÔSßá”u8å`%m…¾š9<³‚|ts5’üHxEA?ü(èD_J{("L:äS½$tLê‘SýXxÇ’ ~,²ÄÁ#:qÈG]ÅS~<<!2H€uœ d%/GÔe‘È÷jàVÓÎW£o õ’ijdÊ'YR€O?¹“¡Ÿ ý”¹œH…v*y©à¥B?•òMƒGtÓ9úJ§¾2xžÑ-—™àd‚“‰¼™”{ü²'²ÍF§lheƒ“‹ ¹È‹~¹è™ í\hçB;:ùÐɇN>tò»å²¥¸B±F…F¡ø N!8…àQnÅàƒS N ôK _Ï àKáYÚ-—9eÐ/¶ ½Ë¨› t¯@÷ ò+(Ÿ5à®f åVɳJèW¡o:TÁ¿ UàÔ@³¦K.‹já_ ÍZhÖ’WÏzä«ï–ˤp¥¹›¡Ý \³˜{ˆõ°øsö¿ÖçŸø·bs¯\¬±Ìµ”¹Gî¼?.ÖIÎë#±&2×CæZH¬ÄúG¬{ÌuXÈõ‹¹vk±61×$b=òKë±ësí!ÖæzÂyÿÛyÝà¼V ^ík±.p^˜k1ï7çù}{Ꮍp—_ßê­26‹¸ã)î•›±¸Œ5ÊSôÃdø`ª‘÷=Í-v¿"¯Èð-Ò¡üW{*ÖíÊu“/¥nå_Ô"}Œ÷:}e(7QÄ6±EœáOÞŸº÷ß"»:ÃÏž—ôûtBÆœ~Á®òΨÿVkÅÕáóOø‘h…o”þü‘+‚ïäŠh‘qX„Ÿi]äQ®”¡Þ"ïwGÀ[2 ^4:"CˆøM~4ú#s²EŸ–[z1ÔS ¼Óàƒ¬1蘯dˆ¡ b #æ#À$ñ;gIÈ–p’Äœ9<Ï!?‘¼DtKø– k¹Uv¿¥èšÂ³rd/¯YË¡N9ð먫dLDî<äÍA¿uèRÔ-»æ:d­ƒF9yuÈS ¿ròËÁ[ ŸR䯆V5´ÓøTC§}ª¡S‡¬ðn¤Õ!k#4³àÙn#eÑxF½&å»ÂM…|—‚W Ï|~WÀ³’O1í·˜2¬@ïRʪP<ã;p¥ÈT ïJøVð)F¦àëĸN™U@¯ÜJ~—¢24 |žWR.uÈWNt›MÊ®†ºh‚VzTB§™+àS‡ÜMЩ°_Ъ‘Û¡õð¬ã>4š€mB¦zôn¢ š(¿&h4_ƒ 5”a-<ëá_ÿp›Ñ·|Mìˉ¿¾ýò¾ýò¾ýò¾ýò¾ýò¾ýò¾óÜÿï÷ÍŘ·Ñåß;ŽA»Š?kí»ÝWÅŸíVñg›”Ï©N>§:”ÏV‹òÙÚ®b}y¨øí[œâxõŠ÷å©b¸oqò=eUqh;•ï)«ò=ÕÙ+¦A‡SLƒ&éÆðßêÙËÿ”›ŠûUãäÃÕ]Åso‘þ ¿âž*žäFå˵[Å‹W±i;•?WOåÏu‹S0‹òIÕªb¸«X-Ê/Õ ÌWÆá1ã¼þ©o»Œ[9¼ Пˆ}MŒW1{šœb(€;úS¼d¼Ç)M*¤›òõÚ£âUî–1–åÉÂßé,t_Ö"ãÈì®båî–q°ôé÷_ÄË>Å#âeŒ€¹=Òç©ðY*bä®ê‘>¾ƒÉ Ö¾þbNEÙée|`±ÿè­Êÿ8ßq¤ƒù |ô£{¤×hÆÊhÊ"Ð"ý’Ç€Ìwôé—1¯&øxàCÀ"/ Þá”["°‰ôýQàÇð;¼SúzA–¡‡€G—DÊ*:1È’‚må›Çïžç {"Ï[eŒÌê.©Uú1OêÃPtRZe¬ƒµÔ[ ¸k‘¡œtŽø ÿ<`s ›ÁX¶–çiÐ-§¬Òà‘Å÷ZÒ9ÐY+~ó,صȹº¥èYNºœüt 4ø½Ž²)…_5:6‚SŽLÕ|GRk‘µ¹‘·œö³ýÖò |)ò”B£ÚÕЩ¤\ëH7’N·ÜJê½Qè:dk´á]}B»âÚuȘŒ|É-rû5™úN7:©ð(§œBp Á)§œ"꩜bpŠá] ïbÊ©>%¢œ Q 2h”A¿ Ø2ä/£¬*àQ¾Ъ@Þ5à¬f mªœ*èWQÞUèPï*xTSÍxÔB³µÐ¬…f-yõèYÍzðпÜôo@îf~7‹o1o±øóuúm‘ã…ñ×÷ž£ï=Gß{Ž_ÿ{ŽÞï8þ7¿ß0ßmüÚßkü+ï4þ·¾Ïm×êÒK­SƒFŒaô¹[‰£hs£y2Žæ÷èé§Ýˆá+ýµÙÝ+D‡Sœá2éÇÝSÍ¢b w¨XÃÀŒf<0ã‘oå2& Ãp&¢ÛDÒ7Êþî*®Z‹Š/äªây’?¹Sņ  ùS(»)'Tlµø^ñ!(xéGÞc·Œ± _é^ ¹¡›TüaøO§ ¦w©E¾*VD«ŠAŒÌ3Ñ}f“Œ[4³GÅ[oÖ&é£Þˆá.}ÕñˆÁÝ%§ Fì5èÍvN—Š¿Æ3OêÛ“gž2Þ‘w§Œ?k»ŒÇ°”ö³´]ÆñAE¬"áÓ~)ð>¤}Hûô¨xgÐ]îrç Ú¾ÈáÛ.c‹¸g"†ƒðu¿YWzÈxÀ–­*þ™UÅngÕVK4`àç/ÖBÀùçîþèä/êl£œ®èbM8rÐ)-ð·Àß- ü-§å´&ø pƒ(ûd1O&ÏÊ'½BÀ+@ÆôˆE–h† W(tB¡ \èiµ]F„A+ øpd ë*d GÆpd GF+ef…GºG c ðÐŒ@ÎHð#É…o&t£È‹…Gô¢¡ hhDo”S¨hÊ(™cÁ‹F¦X‹,±gä´+üxä§Ä#0eÐŽç;¾]N³(Û2x4ð<‘rJ‚W²Á¯ŠgIè„I¤Sxž‚¬)Èšl ´S í4ôI7 ˜4òÓÅú¼4àÒ)«td,>K;3ÀÉäÌ¢,² n8Yyrú—C~tדŸC~ùyÈ’G~¼óà½<Ê!½ (Ÿõȼ¸‘FÏø cyEÀ_D^yEä•ð»„ß%ü.£üֈߔ ²–Ã=å]ŽŒåÈQŽ~åbM‚~ë)Ÿ5âƒnk€]ƒNk_ë.§ÕàT#w58ÕÈ^ ^5rU£ß:ä]G{ZŸuÐZïuȸþ´œ’6‚Ûn#xà4‚ÓˆÎÍb®.æDâïlks-"Ö!Îkt[¬-ÄšÂ\Oü«1Lœ÷—Í5BïuÁÙÖæÀœÿ‹iÎ÷Ï6Ï7ç÷æÜÞœÇ÷ž¿›svs¾.æéæüülg²Í9xï¹wïùöÙæØ½çÕ½÷¡él¶9gþÏb¬œÍŸIïy°óü×<çÓ{ÿÙœóöÚþÙ<÷l>Nļ¶÷ynç½h1=Û^´9_uö"ÎàôöbÎKÍ9iï}çÞóϳÍ9Ïv†Æœgf¹üýt¡ÓœÒœOžÍ7Šy>¼IÍoRó8ц[]~=qÅx,æSÔÙRt…ÝB¾QÐ_ ÌøÎdKàë)ã˜ûZd¼o#®øã©ƒUÈ1!OÆ6€ü¾¤'zÈX‹«jdœ,ò,||Äod±ð=•ôTä÷  h‡ Gr†@?ZaÈ&Æoè…n•]ÄRž[·Èøâ!i2fj$2…£Kd‹Œhóx[É %)Æ<žE‚cíñã¡»Ev+ñè—¾…üÕà$¢ójðbI'бù¬è„\©â}S)ã x¬&½šßéÐL¥^2¡Ê'[–úLGî"Ê«ˆ¼lx– x`Ë€K„ÞÊe5å¶9«ÈK7=ªÈOB·ZžUA§ŒOÃÙ6 w:ú6úün@žZ1ΣCr6À§–OuR&ÒЯåS‚Þyb|‚Gü V@XC]¬ƒ×òË ±NŒÉ¤Kà·žßë³JŒ_è¼½ª Y%àÄX)`Я ~ëÑq r­‡Ç1þÁc<ÖSöëà³>ë)Ûõ½Öwȸh"gs¼\kgûþ'ìÅõíÃýú÷áúößþ÷Ÿ3þ¿uÆø×´÷ÿ×ùâÿê^ÜuîíÁý+ûoi.ÿž1X=ôH`G·#I"¸£güG£ËhÊa4òŒ&=†ôxŽA—±”ËØx3ql§*Ç!ï8ä·UÅb…¾;8îÐpï”Ãèxøf<0ã‘}t&ˆ9&e4œ‰È;‘òž¸IÅcuUñX7©x¬Ðœ Íɤ'£ïdäšBþxLéñ¾E<ÑhMóPøNƺԛúzt©à”Û4xMÛ¤â³R^ӛγéÈ3½[Åh…þŒ#y} 1Ýg¶¨8­§UœVðf‘7KÌýÀŸM¹Ïg6tæ¸Ê8âsDy“7§[Åç™'ú{òÜs·œ*xóíM¾/øÞ=2.øÒ#RÄP]Ú%ã¥.=­¦ ¤—AË焊ÎïåȹÜSÅóväð…†ïn3܈-)æÁï|4)x¡~Äó½ïnÚUpäÌ£ß-û¼7%[[«_Ôà“×è Ž~{›o{”­üî1_å Õ'H|øä|¶'O.{ñºoôî›þ÷Öª"ýȤyçx.³µÝ4â‚ÛºÙJ~puñ9À·Hø]éo>ûáz÷u_Ÿ˜q~dØïÛ/ò©­í²GßxQ­OO¿ýø‹ß²mx;üÁÑè>VËÛµw´€ßjà?èÝZâWÿÞ_Ê­wÿÑ{ùUí{ôÃ?|8mèÉ [[Uå­ïíoÐgÊ|[áüäϦ¾ù1øÿµ’‘¿Ý ^–ç<÷Gýð'¿Ù5ÇÖ–óȨg¦`+LÏXr÷3À÷ðí{û/—&·ëÝ7^{_V×.ýðëó—æ{ÝfkË.ÜX|ÃC¶¢‘žÒþ\e9l”›Þ^þÛ„´O½ûöç„ElÒG¨ò?¼§ß©š¨çlmuÛö„Œ³O¿÷ËÿXüxïØ„ìšð•¾ûÏLJø×Ü­~ü¥K‡x½nkûÃ;mÞÓ¯³—Oiåk>ᾬïã Ê­>¤wßõzþ®ß釷ž7ÿΪ ¶¶»ç}2ê†%¶Š=½ýÚC—õ¶ƒÒÝð¶ÕÞº[GBù%{úð­/5ÔÐõâ7­~ò[›¢;mȨ‹}ÿl«¼¯ý‘k¯+Ò§Jy¡+ëwGH©ïä‹lz÷Í^Ï ûd®~øºK ßk Ø3¯®Öí½$³]Øë§jåEÄí Ö'ÉvYÏ;â®N»Æç´“æQ'F'è‡ï²úc3M9l•Ù¬¿|ð²^w”\;ñ³H½û÷ö{èñ“úáËw\n½ÿi[ÛËÖÜh«i¹ÃžBd;xØ2âɳ·ÿù‚9÷.wÔGæßݼ¹Ì´W[Û†—jûól5Ã+¾?0)Ía'!²=<¼&¾ý‹ Ñ»/ËÿöôÄ+rÞî¥Å]íÐ+ðÍ ÓNùè3îŸç•ìï(×ÙN¾ºòŠÇ} _×Ëwꇧ¿j}ã¼÷lmz@¿šý»mk^K-xYÿw½Ÿxår½ûŠßúås‡Cþs‚ºr=/·µ•¾õî¼E¶µ‹ÎÿìóÍýÀ“õýȼçÖÝxPïÞë·/ÎÓ½7~|[sÙÙ*G¿ûÔáŒ+€—õýˆ0£ØWà³8ýÊ·ÎÓí3øvG;ëiøú‡i?9ê=DÖûÎo^8#>jV~w±ÝÞ=z×°7ò7Û¶Ú•½¯ÈÞΫ}f/x²n‘åT¨¬ßõ«¯ù2¦Bï¾ú£Ì…1ýнi/<}þaÛö!¥ÃO´ÕJ<àe}î¼wo¿Úïл¯ ÿøÇ[/ÔÝzsì=ÌÖvÒh€úbÉß¶nû%ÛO]¹tÉá|îx% ÇäÕ?ìD÷—íÊV·ìPÄ{q­Ð‘õ¸Sõ+ö~H Sí_臚oOøcÀ~[Ûâß^9c¥¾â“ŽÎyß¶îÏVÏG¿ªêSÚ·Þ½vïÂÇ&¶ë‡ªô¦]‡[lm²žmµÙ_'g%~h9&ëïÑQ¢À¨ÿ¼›.yî]ýtìŽIï¼uì†}Ût[ÛTð{Û9ø²>-òšoùºÂÑO ê ït´£¸G#¾µ¦8ôŸpÕ_žÕêó¯¨ ~ ù)[•*_{¿&ëûÑwWsàÕqzwÔïž~¥Ÿ«~( ðÄ›w_mk›óá©<[uÔ”w‚Sn2Û%x²¾ëegÝaSïÐ:Jõ±°¤bËÌ8ƒÇµßé÷ÙjóºÌrÊ?LÖûcÉ"ëIÇx˜Ú³ómýД¸õ¹Ý¼jÚ“ßíÛvdêKv¼:ÿâž«mµcWí_¶ÕìÇ¡'ÛÅã9»ÏeˆÓ»W}3gT…E­èñ^óµO¥ØÚü?mrׯÓͺì«?xͶnß×Ï,ÿë àËöðøã'G|».\ï^V^•öçÅúH9Þé‡úÝ—T©ßnkó:<ð®5ööPwbtügoܦϹiߊŸŽ€ŽlôV¶Û ¾2õZ|<Ç#ì%{=üvoæ«!8úÓ‘†ÚÛiý&¼kªYo–Sá²ýtÐI^8ñ5Ǹ±(ûí¬{÷ê?ò¹·†Ú¶}~â×AËô¥_ƒçÍöÙŽ:Z7-ì6Qïž÷üô¹SwêO¾ÿNøÆdÛ¶wÁûl gÄ>qg#ð²}t|øSÚÊÕOèݳy0´é{‚ï«¿ðfÖ’ô€ÜÇmÛŒîh–­aóÌp.O¶'Æþnþ9ç2.Êg¯çƒ‡äÜ|Ҷͨ&7=Põwë×yGÿ¶$|Ù>žð_òÄcÕ×ëÝ“Ú\—Ì>é(¿Î±fó™½}lëùë!ºTÝWÙMãøoÎì½äfs\žlO˜ã´»1QÒÞû„ßþ'íõ°íÑ?½1¥¹L_)Û¯m½?âIÇø.ÛÉ·n:u{ÕGz7³¹ îçííý ˜•Å ·m»í±Ëè¢íó¥†~KÞ½ïµ{À—íã‰}|Rïü£° ý`é„g£K†8ôi«=÷áͧìãbÝ3Óßÿ똽–SVÙžøZk¾ðùV½Ûè>*õƒ¡)Q%‹{z¼qÚëâûý¦UÖû“ª7æÑºSègþFüìs¢1ÚêÌò²ÊzÒzà áÏÓ±iÛÎØGýÉqßaŸi³2ûeµÏ«êgÿX{ÕÇ¡#ÛÁ“Õºo­\§7Ì+Ô^^]ß[à[¹ØÖ¶>ò­3ÓŸ²{öög•íàÉßýæ½w*­v{:þ¶÷£9Ÿÿ¨w}zaÄF;ì¨àÎG_Ú•mýëÄÉ/ëýÉ;‡Ÿ3-¨F?þæG¯j×4ØÛQ×{e{_ØÙåÐCµo«~íaŸ§Øš6žÉ¿wX(td½?ù䟦æÛ¢?VÐøBÖSöþ¡ëõ'^¹eà}ŽòÿpíÕÁ›/±5‡>öüú¡_;úM«¬ÿ'Ÿöó9oéýø+ ¾ÍŽÝ¤wÊysÀÜeŽúï4ff¶¦7^_¾ç–“–S²ÞŸl÷xáÖ{ÞÕï䛼Nïz©ùÞG‡Ž°÷÷Ûî1:r[Óc€OÕûŸ·»ÃÂÞoÉ(ô®]K§Œ~úÛ¶=_>83ÒìÇlwo¶_èX~Îÿ™õ»w®Hs”ã¶Ec¯ïôsÈo ‹öqÇ´Gû¸¡ÚÅã?º>øÝ|ýø£ƒÞ å(O%ǶæÄCK?ºÅa£O¼µä['¹Tû0–eÉúñ{ó=Wô_äë·SÚÝnÛvå°ŒÌçê~j=iö¯yT;¹dËç‡?IÔ_¿Ñ»ªŒúêþ«õzû@F’ëí;Bµ‹†YaCîÖ³(™è}µÞ•Qßy›ß;f½ìxhåè/Øêï>ô°íõðT;(|d³;ú3Ëýè!7½K,ÿž~Ò¶í×î7wÚN¾Ý~eëË©HUÿ~»ÿJ—­¿hMñý[è]>[½ŸØÔnŸwn;:òc¦Lz 'WTwì1G?©ÚÃX1À¾ª§(mýVïš;ãýŽ”ÉöyÚ6CÁŒwô¥JoÕžë¤HÕF‹‚ø~¼jÑŽ¼÷Úû‡®)k]»äc‡\O_à±äÀCúrU¯Ê®ýjdÞÏû§Š§ßýîé ô®1[Æ=‘1×¶íÏ!þa¯5ámMæx©êæ•Ñ!.ÑçŒ}÷ºwý˰„3ÒjmÛd;ÔÏ׊.>n‚­yÕʲ·‚¯ê}uÇ{¼©O Øæ³zèÿS0KGy”o‰8ÔÖüàuqÓ/bÝÙéÜ/éÇã_{÷ƒ^Ô_ùêâ›ýW[í%.¿tÆÌݶ Q¯·Þ3ûFðT½Ó{ýð×gôã‘FǤ¿Ò=§î¸ü„ÕÏüB_¢úà ÝzSaN½åT”joV×ùìv=‡ŒJÏð¶·ûWÚ ƒtŒÏÓ…‘}<¹Àõ–sh²Ð“ía× ÈÆI7Яü€•èN»¾rÑŸžÜtÕl‡]O¾‹}Èn×v½¢d{Ø5o`!]‹]Žãçÿnìà”gõWDijñŽ~êü×μµ!Ö>ϵ÷sQ²ìÃåÝ?èÇ™UÌ|$@ePШÖuèCëïc™­±)uîÃ-Ìÿ¢d;ØôÉ®ô7ŸÓÏyZjÙŸôû0õÆNÛ6¤Ü¼ù'Ǹ%ë}WS¿°7²‹ô㳪¿?/ûkýÀ–êýŸ.ýÞ¶M®+mb»`Ä{ÀËúÞ%íÒÞÎ{dÒS¿k×÷@eþ‚ŠçÆÚŒVÜtÔ¾¾jÍ~øf‡ýDÉvð”ØV˜q~Üý7|û6{ÿ| fþüvî³µ‰^aF‘}\µ·ûhÙžr¿¾þ©Š‹í`øáîôÝ{ôK['<=a«£ÞÔxoÎG6Èy6tdý?¥Öv½tý-훑öñöÀä;b–?»ÜQþÊž•>¶ J¾=?{æí{Ž–íá©I—Ž}qÌ ýØwß[ö5;ôë7úwë/)µ¯{¶ýV „qæ~„í9ŽAG¶‡§ä>—ÿØG¢"nÔ÷ùCRÒÛ6Éß¶áË;£?|<Õ>½kðÞ®{õ™èãžúþÇ'½1ö-{_áç¶ ãŒ,àU{8úäÃîcZíåpì…ÚMAKôýw+¶mÑÏůsÍ´ÏìýA´j{>}™¢vÈÙùØÀǰÓÛß´Ð7¼z^À{fÿ ßû×ï7¬´5Ï?ü|þ(èÈv±«Ãï‹W6ÕÝùÖ¾·&êû£Æþæ¥+ülÛ–Þôx`¤­é¥w.µï·â³ÝìíàØfcà°Ë±ÂbÄv”·´{ý5B4fþÇ8£úcš[cïŽý^l¼Ð÷|äy÷žïh×­œûRó}úlÕ?7ÊþÓ±>‹Qýƒªçcjßd_Ç=2ƒÂý“o ny8È·ÀSýAóßnbʧk‰`?Iß·ñ³çùrð³ºæ¹¶&Y/à©úß" ªC?&Çg{{Þ—síåÞÞpÒ´¹~Ñç©ö×\½uÑïí6õžjbÛçÀ-öuå±Òá Fn¿ÔnÿûV”OÿôJÛ¶Ó7g<Ôô“½<”½9æ-1ÎíZ?–éžtÕ­]ú¾ —„V¯w´SCE˳mËø®.ðT¿‘²H¬õXžžûɽúË?¯æüßœß8ì2Võ•SܨJýXØŸêè2õ—?˜ôÓGƒ"íc¯Øp>hŸoØ4©Ðåà«þ¢þ»¤;n¸\?æûšß4÷ßè/ï¿dÎÃûjõ¨æ;Ì}ßXÕ¨}ÉcóŒ Zýåßß4ëÏÞÄÊvð”܇vص´{»}¼œýcG÷Œ G{ªm¸fÚ«íûv>¾ ᦻ 'ÛÁSÏä¿òc{ƒ~ÌMlhøê//øíÇWÎ{ÜÑß^ÜsÕgo³m8Çhxà©ú>õE³×ïÔ‰aÄvÚ.ÏÞ3rw%lp”Ÿ²gsüh¸OìTAGÖÿÓ£ ýèßž=ô§—fÛÛÁÞ'_1ý…x‡ª˜ïQš\îûK[øDË©8ÙžŽH¸ôþëÏÕž\qêÍôsõ½7Žüê•H[Û¨† lêtŒ¯q²Þe·?K?*Fïáùë½µþCt¬£ÔþlcÛ Ÿm|¢ |YÿO·¿yé ïØëïè‹ýèÝZ¢ïÍêç3¦øyǾ•|¿aŸ¯5‰áñÖk¡#ÛÅÓ߯3+ýè#]^öè›úÞØ)µýã&ØõVïSìó‹æ'†ßÿÁ§;ö»ãd»£Âœku{;8*÷åõ½áCZ~i†c]i­8ôÆŽ0³=;ì)N¶‡g®~¶ü¹Uͽ®äÞ«Çß­ï <òؽã_ÿ»õ¥Ù/\àoløBG¶g`V÷ñ?ÙËõèï®ó¦¯çÛû—½+§þpÌI®!]þ·l²¯ÛìóÄ8ÙNžyCäfý¨±LôÔ÷zm‰ÕüÊÖFiÒµ;Úg¼l6ñšaÄ·úѪ¶+”MÕ÷zä~Þ=ÕÍÞìóæxÙl3ö‰7CúÑì¦K&N¾ÞÑÏx2g™îØ÷•û vûlú>Þ:öô èÈva£. uÔƒõ‡;WÿFßóåï“|:þæ¨×鯂ɾØ$×ÍБí¦öÿL9Žªù§Y~{^Ý4÷Á¸óå§öóÌyv½ð+GlŽua¼l'¶ÜêCS¿xßQ/jlòÙÓùýÎÏßÛikK9úÊûïнÕ<«ñÒ×­ÿâ^Çú?^¶[¹xQõý¨kÍydÙÇ×=÷Ÿ7àíglmt¡¯:æjïÐ;Aµµ¯päÁ‹RÖ~5ÕQ/øæî·58ä˜ûƺ,ÿOíý–š9ì.Aµµ_{äºYKÎ;ý¼¾‡ÕWø=;lÛ\ÀŠ?K÷*ˆ9ôùýßÛšÕûc‡<ª½üõúÛ·½V?Ò°ècŸ”ÚÛßž{¢“ƒ=ëmÛg}Ò•x}”~¾Z75Ë}_Ç<)A¶Î |û²G²_³ªÃ_ßóà¶æÍŸŽ²m'—æ~x²tŽ5ÒD§×yJßóð©Ìúζcƒ¤c]” ë»ÓCÌØVèGÖ è/¼'©ÃfÛ~ÎU“¿»þ*Ç~U‚¬ïÎY~°äÌö~éˆØ~¿ã~{zÏí7l›w›Ý¾¶O4>Çx¡ìÁ¾¿(ÛC§x ‘ìêxŸ?U¼ HÕ÷\1ÎË_¿ç¨GŸoÞ=¸ÏÍa÷jÁÞ'ÊvÑ)_‡èG†3}Où÷_¾1 ζ]´’i mÇ˸¥%x‹â⩱·Ít¼W»Š¾Ÿé{bî>p‰ö¥m{½ñ‚È¿í&QÖ{§ZïØßž(;ÿE—yúžåyv—ä8Úõ÷¶ŽL¿ÃÞ®ÿÎUý»¢úá׌_úžÙÓ÷Ýþ€mûÓg¾ªëßß¶^¹½xUïrÖ«}à=“¾{"¢ ÒQnb÷ÃÃÛ¶¾ëÚ‹Ö?ž²÷¯^ëá^Ž÷Ä»œr´ËÑn'$é“.µ£ÛÅèê×àØ×³×£jÞÿrøÅ_ë‡ï»â¹Ù-rôg“›ÿpÕM·;äùþÝ1à p¬Cÿ?ö®Ä­ê¢û[Ê"‚¬ê®lÞ Y4ÓÌ~.™€¢ †û‚ ‹»-niîš•™ÂÅEøª,÷júóå’VÚ«E”En‘-ïû™ïÌw†_ïûüÞçÑçá©yæ;3gæœ9çÌ9瞣šÇFÉ÷l§ U1\ªØšAÎê›Ò’œï(T³ëBe!5Ü•ç—Äñ¯š“]6¼l¢7ƒtá|ç¬~æj÷Ë;”×ý¨KØ1uoŠÙšQ-ñ›Äéƒù­HÜä¡PE…¾~¶[ÂYã>iç:@µÀ~‡Å»XÈ·$N'±‡ßÞåJ¸|;ëvbâä?³$}4­Íùn ä—Iœø½i´Ù¸fã*±ëŸ_¬«7çZL£&üï)¹^i\¾ã9}ð8’†ÈÙ¯lÛ9ZÀoý6Çk¹§½Àoq8uý ô A/Iœ_,¡‚àið¥ì4bµ¾ÝñÖ»/(ŃÙA…Trk;Q¾çô°üSú’# Nåƒn;gº²ªjE¨àÅcïRK¸x ¿M2§¦¯‰ûYÿ«‡7Thb]óÝÎâ™%üËö¶ ;*ð™Û~ô©KÀ¿’9],]1!ÃÔHê©•í‰_‰uÚåÞ׎ÊóSÃ\fÉý«fì‰Ïé@uS·'õTJ.zH¬#¦5Ox¸R¢S/ <÷dŽo*uòu¤Þš9úå?ôhMØ0÷Ùé¿)%£þýÉWJÞðéß}>ã8¾©ÛÇå5RxÒs‹t’çF½lssŹ•¼dÿù­=RŸM.ü?ô"ü¦ïPõ#V·7kù1 ÷ç >?ÿŸû}×1§þ>«ç~+VJ7Š÷aÉ€»—çw•|=Ãþá¶ÁÐ;SÚüGþZŸF²…žc þð•£ùî/ázš†GñNIátàN¾›¤ž¿W­¾Mç·xMWJ" (äJ^ê7]³Â>Ã÷\/ a“,¤>Öùúêä{ÄÚ~Äø´ &¥ä¹‹£›¬™òž¤p=`7U|¾'õ\Y~ÜG-â^²æxÞÊyo 9 îM ×§ý~ë¥òü}“.|9ÛNìßb]zðÐ`O¥„Çí»‹f_Láz!çõF¸_;÷±ì ˜t¥óyN-å?î;®äȪ,<áq\.p=\ø! ü+}s±¬lìpg³À›Ù!fÙ”ŽáÞ%Náú¡Ã;ôfK?+;b™³ª°`sº ³KUl÷ÏŸï3Í¿#äúhF•இn纯*6é¾K%–‡·”Ž QÌ:•q(¹Íöo”°à{†çJÕ`3€ÔAiè8ýObIw˜Ò0¼Yп¹Ç¨äÌ}YÚzÇð]Éãù4úª«6¿Spý±ddt0ŒŠ”û‡”7þ2CøÙ¹^!åÇhF•SŽüRy¿Õ9±\X».’X&7ÎÌzµQàÃí—°tßHÒÿ‚jÈ—öÙÑŒ.*cÈ’;›§J¿éÞ¾†®®mËœ,Ÿo–çÊöGˆöîWÃêô˜ÑGe‡—׸ü¥n³×Æ«Ê8-Ëü’ßÛý´DÂ×M t"‰LÆ<Œ^*~éB_®åGw¼VxKì×’SR²r›QÂ¥£s!}¹Ý?Ÿš—=Â|Œn*.Å­û?HÝüíôf °•T/ý’f_zàç•ÜÒnÔò”Ø4†ÑGEÅ µ'IÝ ÷Úº›í‰eêôa{îë2¾¥ä@i]æ†qŒN*eŒ»:]Òû,ÕF,ã¿ m¼y©ÿm<}wk'ìX ¨7Û4ó0º©Xh¸qöjƒÄóÌØLeüBb™¼ß¨tVÌÃŒ9¹<_úñ¡AóÆxF'<¾¢nâQ¼hfI<$–—ÞM¹§˜S¾èa˜øPÉV@9$Æ1º8yY}ðJø¹ßÉbz5åõ\·þÍÚ¹Qm~âMé×â+Æ0z8Éã³êF/÷ËÄ4ªæÝÒoúÿ0pTV÷íóbj Rr—ôì{Œcø?©šO‚IÝ@eÞÜ>{ˆÅ÷õäWâp¿X\ ä¿c~Ëÿ<|>Ø¡»¤—^.;ð”!ï6éƒ_¸/ì¨fhåÇÞTŽT/Fb +&åÌ%äi]÷—Ç9’ôgaq»òž*Táò“vv§ÛÑ(ÌÇðÏí줎¾Úòä;ÄÒ=ÿáºoZ$Ý}â¼fÏ2›8?a/Heø/W·ÿ±Ñ×L¶»„‡ù$ßÙ®2&%ŸÉEŒgø/¯ýèçÝ{Û?¶Üüyþib @w¢˜×½…—Ç!©w³xŒcø/WÍEIâÛ®6özÜOrÁå‹g”…ži^]tiørW¡ç©áÎã0£ƒò]+¿Þ“HlÿÛFbK'•P󆯒¿ÑWòÙT†ÿò4ê(¾Hl'·e¿ôc“ØwíoîcÒ¯*fê.ûë %W ;žƒqœ‚ôÙeO[ñï¡ÅƤörçú}éòœTón–¸/|]ùNËèáÄ5ŽØ¸£ö૲nD*f5|s˜’wºb¯û®øžá[‹»°Q5ÔøŽ8§Ú­%xbÈsú­ýŒãë—)ùü,øúX†ï‹é{¼ôS3û2©]|;iÿóË•R»¥ô¥®ÉE!„œËð~"ðæª‚˜ÎĦªmN¤vVż“GþRJi˜á¼”ïüý)äâX†ÿã2vT­³#¶ê̤vÊ.ËÐW×+¥îÍUü×ÉwÚX†ßãÔ:Ðá}bãñŸµÓ:r \«”zÒ´/•ü16|Ïð{œëû¶itCŸ‘ÚÙõÏÜ\¦”ªá7ñJ>‹Ç÷ ¯À‘Û§Æ bÿÆBÛ‡¹¤vAYÜi÷bñN*uX0$(ôôüŠÉ=–µ‚áÿع¦xR‡¿'ø~í”Ww]ñX¬hqðZ|+·ŸIücø?Æí©¶gò®¦ÌFjG°¨ê‡¥’ЧŽ?6CÄO,àð ÿÏ8FÇÔð2ÀÓ:TÞ$µûÌhSÌWÙ ¥üMcôräwêøq$¶ÄøOîÅÍðÔ|{ÁèéwZîç·Ò¨¬†â}C½ÙSÞ“÷;ÑËÿ#÷Õa’Óæ$Rs©±äå‰3ľJÛõ\úÌÇ âÒQÃjIÍù’ì·ço÷€ùé5»¾gôpD§ wò´C‡õ¿ÿ!Ú5Öî3_*âaJ% r-ÑÃaŠuS ± ß»(&©±¤êjÎ~ ”vÿd輚®ÂÅý“ÇèâðL@HlƒF‘)÷¯cÜë³öR#ávVb¿â½—Æèá0÷#ˆscöRSíÞoêdÅ|ïÓþc’öHÿ®æJcø?Äü¢ÿ*ùšÄýª9¶{Ó»þ$]ñûªñIMNˆwm:£Cê5êGlÔº×û²°ƒÔ¼{­þ‘K¾b¦ÑPF½°óær½Ý¤é»éŒ.1± ³Ûâ—Nj–”÷{yÓŬº·C”œ}¿nüéäz:Ãÿ¡¾nyƒ×,Æ÷Ì.[3bÙ÷^a&¥´íÊÚ¥E >“CÝ¢¯ÝÐüÏèáàí>#ý‚W‚ÏÐ(mï’Ÿ× ŸØò‘Ä‹·jxþ‚jž?YÙ ~F©ywíœÃœÐqƒw‹s¨þ~çÄ;1JiÐÝ6Ãû¤7Fþ±FÏœ˜‡ÑÉAn/³qtuå¶ Û[¶+¥ªûÅU¾ç‚TA£ù0žÑI)ÓtÂõHΫwp¾/yϸž¨Ñ-·É{›Îè§t ¤‘ï[ïŸÍ9EªWÕ¦ö_™§”P/<÷¯³Rj"æ'6gtbVÃû³SjæŸVJ‡P|’’«­7žÑƒy³@GlÜNQ=mzÈíg»H¼ô[{ßɯä ü~Òõ!30ŽÑ…¹OVg-±qýB;‡êŒG}wÞüª4ŒÜ!ébñ?˜‡ÑG ½}óÒ1e°>òüRºÆ:î;«”vQA×¾1žÑCñ55PYâ!ÐõâŠëѤšÅßÿŸÓàï€ñŒ¸]WèåâüLƒfL»6Yò_•¬Ú½H¼CÆ3ºØÏýÚ—®ÝÿjÏseþÛß‘÷_u·ö”iêà ó1zàñÄEëjHÕýáƒFÏ \¥îº‘ò÷*þ÷Q©ž…ïC¾Ïÿì/RuqÄêaÿP̶)ëÛ$ýC™Ëü‰øžáÿc(m–~&éÇçkðW-rèéýƒãæRëŒ÷‹˜‡ÑC‘Ÿúð—ñ¦á“7©ËóV½yÎú­”ækëöÏu>$岦L`tñ‘çtú ,àAµc’*(;hBn˜AÅË×)óY<6Æ1zø°/ `› ÷Cnñá×r?äæ[áG58Ä;>GûàF{“s>ðz]Ü‹ª %ÇœªŠþ oš^#âS'0:Ø“¹ï¥3-q‚®«œo5Ùªê³&²DÉÝ¥þ0ß3<d¥7ŠØTñ8‡œiûôæGÅ|™ä\ePÃó{÷&Nüô£h)g‚Ôj3·†ÎÞš¶EÚ 8ބ߆ñ3ÌÃð_xlEïY5—$½‚{â)BΜü¦«ÅWî—º%’Z„T¼‡2þßõÛ¹b~*±qÔsÔº­ƒwH|³ø/%‡zÝ!o3žw»¨>ÐË­—vØ_ gÖ8l®>.×Ý­fÿoNîÚsºŠÉRÌ`xÛœQ|2q±q;“†ÿ3i®¿tü4G1«áû&yϘþ%õì †÷ 5ÀŽÊâ‹ÏøýteÓó³3ŸþMÿ“p0¼ï ¿ª]R=Žl-9}¥rÞö‡/(æ²¼ïÊßÙd0¼üÜûÒ{/Þ&¶çTF+îÏéc~ýb¢Ä#Ojv€ùL>ɸ¬LFܯhKr÷wî}†œ~ö·­Ÿ÷^$ù×é?6öŠ]'ÞÚïvÄ>2]lo0mëöJyØï Èi§§²?¸ì$ñs*x}h“»š\kÃ~ëOÿ=®­ð8§ÛãÚ ÿ¹Ýè¿©üœ±–šOmÏAseàŽ?©ç¹í±×'Ñn‹v[´Û–ñº³ø¾Úí̼^æ°Ke94hž-5Ç=î™}&þŠx®-¯A‹12¯†ccíSyŽ{œxˆÖpŒN7xýÙL^ÖÊsoŒä¹¶°ô»¤ò|[WxîÕl–ç¾#àwEÛ5•çäÀÚnq<+`qCÛãÝ3[Õ¡MÄ`óì˜Ïã=§òœŽ,g‡šw«ŒçÝÂ÷²y¾{Ï»…þÎWX+µ-Öèt<ç`Õ¡ÝUÇkÐñ<÷:–ÏÕmoÀîÇrÜû`-ŸF^6ŸçÚܾ€Ëçâ‹¶ÆúMåµg1Ökû§ò<[XK±zÌ­/ä5gõ¼æ,¾ïŽ} ?`¯‘Õ†çÕÂØÀ2ÆfÕ\ökZåÔÂ÷ÁÙ,=e¿!ØwƇàœBšy>-ô÷(k•K+›å­7:òz²€ÅˆoC ¼~l¯ ØÂ°—°BV+V­›ÉkÃ6òš°Ù¼,Î-í¬³ˆÀ^#ÑŽÉ뽞È^Ûý&ôGa½(¬…ñQͼNk>Ï¥v4`ÎfõZ£›YVšÿŸæÓ§â"sÇ`®¬ÝÇÄòµÐœ¸4¯>Í£›Éëµ6³¼½4®šS°Çcîøl^«µ…åÒO, èO@_=Ëë«æõÂø~h÷ËfuZËà6eðŠ6eð³ ¦÷3›Ÿ#æzBÏs¯¢ö»ç'Ç^žD»­×~?ÅëËàûvh·+ã5f0‡]&þ€[;+Ïu‰{dZ´7óœVz^kcÐvÄ=tÄŽ˜³½#¯_Äk=‚G8lUÞÀs]¢Ýçç áœÊs`a.èwÉä¹°nðœ—ù,ÿ¹ZmWô»ñ¼X‰<ï%`qCÛÝÀkÁ—ñz3¸Ã€Í°{`>OŒ÷Ä™yR™ ؼ°¶×&–û’².5÷e>Ï™Ž}vNå¹/o´ª7ƒµ»`/:ôë°7]¯¯çµfÌ<神ç¼DÛ°û$ò|—X˧¹Uœ“/àö\¾8_´ýô¼þ;Æúa¬?ÖöǾý7ñÚï«ÇÜú"V[F­ûžÉj¾Sv€þ€5¼¦ pˆ}blà)^ëp¡tŠå¹ Æ÷Áù,§eúC°ïŒ)ãõÝ{|VÛÝ€s3 mÈç¹,Ñ6bm#`1âÛPÀ ØBK(öfà5Ûц½…çáèG8ú{¢¿g>«a©Öb7ðì8‹ì5ÒÀk®žÈ2^gð›ÐoB”‰×QÇø(¬ß í^+x~ÍV›3óGclt Ë£FóÏÓz64Ÿf æŽ)ä5ÓãX šžÖµ¡¹5Õºé€5¶…åT£ù:Õ<›€=sÇ£Љš_3!“×LofõÒiþ|Z˦/Æ÷3ðzé”÷PùAÿQÙKå-•µT¾R9Iå£&5™Hå•kT>QÙDå‘&‹Z×qÖäVÇY“1Tž´–%­å•šlÀÙ©rÊ*(¿×ø;å픟k¼œòž¿ópwS¾­ñkÊ£)?Öxok~Kymë<‚mÍ75~Iy¥Æ)¤:h@­C‹³oKß e</ÎÛølïÆj_9/.…¬†,­™àqžÀ…'Æz¡Ï ôÐɑלvndynu ÆywÞeL•ñÂóÕ‚&»Ý`èò/ൟ0FzéŽ5Yý¦`ü7ó†¸ñšL+X]XþkÀ5âNñ½‘ÞŒÅ:aØKæ×ã^àÿ#p¦&Àg¢ú&¾23’赆©?ѧ¸ ¸û`½XÌ[ÆÔ µÎ#ë%`lÚý¬mä[î±øX|¬þg=ðÿ³¨ãø¥gÞ£æ¥OåùÒ­¼6wÏsnc×»m«ÓÝÖÚ*Ï9ÚíN±º5v˜Ã4g¼ÙaŒ½#¯e:³Ç7ö-¬ž ÍkêpбGô;bGÌÙ^Çó›y­A´RYÍoQk0›Õ¼é€óqÖãòçBžóý.h»µÊ‡º‚ç=ÇZ®h»¢ßó»·!,?*­9膶»©UÍA7V+ܰyPë{b¼'¾÷Ä|^€Í sya¯^Øk'7–CU­›Ø;cŸ3ñWÀkblÌÝkwA[‡~úuE¬¾xWÏ{޵ºvoÀâ¶7`ñqd5Ç} X]ÀÚmÏyŽo}ñ­/æöÅ\¾XÛÏÀê«ùÎ1ÖkûVÿBžëg®G¿¾¬Užól–ã<ß`î€M<¿9öˆµ±v •å7N‚в2v<„Õ0¶ñZ‚ØWƇœâyÍÑßc ¯!ˆ~Ú´ ˜ß³Y]sʦCk(` ÅØPÀ†vÚa8·0ì5{ Ç÷áø>ý=Ñß8é‰þžè òëG s4±Úå‘€'sš°Ú&*Ñ…ñQ˜/ óõ¬½Ðî…o£ñÿÑ€3sGcŸ½YÍ$š›æW§5Þi ÷̃yú auŽhý!šgÖjܱX3Α×LZÁr¬S‘ïãqñV^×ë&Žô'`_}M,çî¿Ø;¸ºªkÿs¢IÈL’B†„B÷2C˜.„9 ÂL˜ÇX«hh¨cZ­bšDôiKû´[ŸM­¶Ø:`R-Jû7µµ¥Uk´Vÿßuö¾\šÚ¾þß{ÿÿ¿}>ŸË½ûìµ×Z{í½öpÎÙë'qÕeÚ‰ Aù7Ô‹ajžu®ý&Ω2ŸÊ<*s¦s=8q~tΉ2:ç@çü7qΣŸ˜s›s>sÎ_ι˹ftÎOιÈ9É$sŽÌ5ç™[d^‘¹Ä9g8ç ç\!ó„sn˜¸~œ8ÖËØ>q,—qüÂøÓØÏe,vŽ¿ÎqWÆ\ç8{áZQÆM/½îÃlq‘uƪ—ÂW*ëFÚi:wÚÑkîäÏ ÜLo•J{΂ïlúîìã:f3Ÿ¹V¯YšÌ£Gã.Ðœ×ø¦ðñ„Ç"/…«°¸OápyáË^ý£¹KÇ©,Và—+H¯¤W‘¿Šú¯F¦t>ü^cS˜¡¾ðô¥Œ/2ÖòYç®0A×#o ù£«?eüõÒ ^Io$½ Ý6ÑOƒÐ#>Áò-kJ®‡Pv›‡Â %/”¼ÐóçÂ<¦çNäíìU±¥#¤Ïé>=¹žœ\ON®'ÿ9ï+úé6;àó&Ö¡]Ž7ññß)‰:>º\Dú¢Ä Ø9Þ|ìñ´ÆÏÁg¦Ò¯¦Ò6S¡™æ¡1t3í´LÒÓOküjòÝáá>¬ñ«éw34n¢ŸÆNì×±ôá?«KaèÌ"6¼f#o6éÙÈŸCþÒsHÏ!=Þs{uŒ}ôŸgåCþ<ø{öÀ=z5΢›Æ±îÑx:^p¬5Ž5ü’¿]=ÑÍ^žÔÕ“ô"ì±úEÔeº-F·Åè¾ø¸ÆÓAöê½ÙKd^ ß«i†5²—’¿YKI/#½Œº.“5¤‡ÆÑ雀£ƒî+H¯8¯±«)»’z¯<7»š²«)»šüÕM ·5ݼ©‡7ùÞÈò—ô>¤} _㡱ªá½†>æK_ûœjÒkI¯%½–ô:è×Ù4VéõØe=õ^Oz=ü6`Ç 6KM¾åýl“zDaÿù#ÛÚt @×Ò¤I’$ˆ™w6¢ëFÒO«á|uÙD›l¢.›È"?ˆºÉœ‡m6“ÞŒ>›Ñg3e‚‘L~0ùÁäoÿì¼…ò[©ËVøm=¤0²CÐ=]CàBz›—ÂW2ñ²©K(¼BáÚ¯¦í6›9 ±{à½]w 7ÌKápš>¤w"k'ô;‘µsHá÷„CŽ.áƒjÊÙ¦ð/«'[DA:BÆkåÏ9ÇÊÜêœWe.•¹SæMç|é¼Ç(~ïœó¡Ì…Ÿ5Êèœû&Î{k¾›8¿M¼O9q>“¹Ì9]8ýµ¹kâœ5qžrÎI2Mœ‡.œ&Î=Ÿ5çÈ\3qžù{æçÜrá¼"sŠÌ%2Èœ!ó„sޏp~¹Aæ™ dpŽýÇý·?ë/ã'Žë´ål?EÆi¾/¢ìÅ~ziFœÖ£ñJøv·iŒÚ1UF¿EûÎêæÃµÙôÅÙªkÍáúœ.µ|›;¬±ÉHϤËy\€IÖ«1L¼5† é…èቬEè²».‚ÿbôXŒÜÅØl ýgÉ Zöý¾XZ.ã÷2ôZ†œå-§¬@î YKã+Ñså˜Â3±jÑÅûö‡åÖ@·›ù¢ƒ/å}Ñi-¿×ò{<×Ákº­ãúzt]O™ y çk×üò4¶×9…â_hÐ#ýHò(ßøÿFdoä÷Fh6aëM\ Âf›‡ÔÒs õÙ‚Ž[ÉÛÚ§pÂB°YöÚÆw(ºmwWXa²$ݯ” #o'úîDŸ”ÛI¹px…Ãkô»ð‰¾#ëtù ›ðÛê6y_xr?¹ŽtûÇXÇÿGÖð‰º}4nU˜ÆîìÓØ—øæúß”>é¡ðËÏê"x_Œï\\­ñ/©ÇTwIŸ™ŠÝ§Rfš·Æº¢§A3šéÐL'=}HãÃÓî#³›vÁx73Xãa‘žEÙYè<«GábÍ"6ô³¡ŸMþòç?‡ôòç’ž‹ì¹ô¡¹ðž‡¬yäÏ#íâAÚÙä{PßùÔo>ùóIχ~cÇ‚j…‹¹àôLLÒ ©¿'²=ÑÍ“ºzB¿Hæ';Ò‹Ðm1õ\LþbY³Sv e— ûxy‘ö¢¼ù^§öÖRd/Å.KI/#½Œô²…‰—ã«Ë὜ô t_¬ýj84q0¡]9¨q05&6^ íjÒ«»¦7õôFïIÚ'Qc_Â{—ƽ„÷æDß`æ¥/õ^Kz-鵤×RïuÞï’ô:ò×S¯õä¯ïÓx—äo é äûaW?Ò~¤ýàïü‘ím²Ð-€tº¢[ é@ÒƒjøÞˆ®Io„~vÚDn"½é´ÖƒàD ª!~3éÍè³y›)Œü`lLþÊoßl±y[‘¿~[+¼ÍhCH‡Ðæ!”ÝFz¼¶õ+ÜÍPl ¯PY³£ëö<…{¿ý´ÆÞ„÷Ò;(æ§pÖÂH‡!k'vÙIz'|‘m8z„“· ^»Hï‚véÒØ!="įe •?ç¼ë|7À¹–—yUæQçêœ;'®ç/œ/?kžtÎ27^8~ÖxáÜ÷YóÌsÎùÍ9¯9ç´ ï+;ç¯ÏZÇ;ç¬ çª‰kú ï9_¸–ÿ¬uüÿÉ» ÎyÄ9‡ÈÜáœ3>kž¸pÿYóÂ_›>k.˜x{âÚ~â¸?qÌ·)]L¬DÊM±ó9®p/Óø„ôí©ç4v12§sm:¿Ý¡q‡Ö¼´ùŒSÜ­ñ és³lj‰7›üÙ\›ƒ¬¹\Ÿ‹_ÌÅ–ó(3úyxóŸ׿Sf¾|Ãwým¡‡Æ-äÚBÆOxy’·¨_- Ãs 4Kð%ðòB¾ßKá³=—ñ½¬WãÓ'V k…¬÷eO½WÚ”¬’oèVQÇÕèêŸxóÛ>Øf eÖ ( D_ôXK]×ò{¬÷Ñe×Ö£Ûzh7 k¶ÙÀ5?¾ýƒý±›?¼üÉà;ÞÔk#r7’ÞD6aƒ t Bþf¾7CŒ ‚©S0×¶@³·Pv+´[‘Âw¶ v´ÛÐiö E¿PÊ…bŸíÔe;t;(¿y;·cDã>Âk'úîDÏ” ‡8×vÙÖï.~G g2"¨¹n¿ÉuþÿÛuþÇ5þ¿·¾ÿkkûÿ©ëúÿèýù1ô7)ÜÚ‹Ñu*<¦Ú4¾=¶› Í4|nZžÆ±EßéÐL·]€q_­1Ì@§§Õ013eÁV³? ^³ÈŸ5¨1Mþlòçxhœ{Òs(?ú¹v>è7ýçyk¬{ÊÏÖX÷Èö ßÝæC?þóIÏG—øûê· OãÝ“¿Ð~Þ=uõDwOèA¿~‹¨ûbt[Lþbò£ËÊ.É›€soÕ8÷¤—’^JÞRä,EÎ2ÒËH/“y ½–3.-‡Ïrä,'½½W ×ŠAoŸç·_…ÜU¤WQvՈ·_MWS~5¼½½5®=ôÞäû —ù>}Ïžü5ð^ï5ÔÑ—:ùv+l__Òk­ÇžôÚsÇžú®#o}â {ò6 ÷ì±<¿`[߯†`dúC€¼ô @~ú¢O 6Dÿ@곑úl”ùüäo"|6ÉÜH}‚¨rƒ°eéͤ7“ÞŒ›Ñc32‚©C0ùÁäo¡üdo¡üømEþVøm¥[¡ AÏÒ!” !½²Û¨ï6ò·ÑÆ¡Ô?^¡è¶Ý[a%o‡×vxï€÷tÝ®;ÎiìdòÃÈCÖNxï$½ì„W8åÃÑ%œüpòwQ~åwQ~éÒØ"]"°¡›¨RóëÄu¼sþtÎ2ÿMœûœóÌgk}.ó‘ÌCŸµîvÎÏš{™sÁÄñâZÙyßúo­ëß¿µÞýkëW»¢ŸB?ž‚Œ‹ø¾›_„ §cËéØÖkî¤g`ÿØ&þ0_œƒÍçPv.vœK[Í¥Ü<òçQÆCÖÞzY‚ž‹ék‹¡]µ%â«Ômí¾‚¾±>+Ñc4«ä›¼Uä­F†7>ã ½7iúÁÒk°á†~µDñCç Ê¡Sô›ù½º`Ú(˜kÁ\Û‚Ì-”ßJ=¶q}}rõ ¥ï‡BŠ^Ûa¶ Ù»¨ó.h#¨OD¿Z²üZ#MÞ ¼ú_µf’½Ž`©ÿžÏ»|Þãó>Ÿ?ðùÀÍ|ÏÁíC>ñù#Ÿùü‰Ï'|>Õ÷Pùg0†ì‘ öH{$ƒ=’1]ï¹Û Ævƒ=’ÁÉ`Œ7ãyzÆÉ`d°G2Œ öH{$c‰>?ÍÉ`d°G2Ø#ì‘ öHÆj}¶‹=’ÿø¿ÿø¿ÿô{¿ø¿ÿø¿ÿø¿ÿ›õ;ø¿ÿø¿ÿø¿ÿ;ôýaüßÀÿ üßÀÿ üßÀÿ ‹^câÿþoàÿþoàÿþo$è³ßø¿ÿø¿ÿø¿ÿéú\þoàÿþoàÿþoàÿF®~gÿ7ðÿ7ðÿ7ð£D¿‚ÿø¿ÿø¿ÿø¿Q¥ïmãÿþoàÿþoàÿþo4êõ1þoàÿþoàÿþoàÿþož[Çÿ üßÀÿ üßÀÿ üßÀÿÍ3uø¿ÿø¿ÿø¿ÿø¿ù¾5þoàÿþoàÿþoàÿþ/ïø¿ÿø¿ÿø¿ÿø¿ùn þoàÿþoàÿþoàÿþoÞ·Çÿ üßÀÿ üßÀÿ üßÀÿ͵?þoàÿþoàÿþoàÿþožÉÇÿ üßÀÿ üßÀÿ üßÀÿÍó‚ø¿ÿø¿ÿø¿ÿø¿œ¯1ðÿ7ðÿ7ðÿ7ðy×ÜÀÿ üßÀÿ üßÀÿ üßÀÿå½!ÿ7ðÿ7ðÿ7ðÿ—gþoàÿþoàÿþoàÿþ/÷³ üßÀÿ üßÀÿ üßÀÿ ü_ö=þoàÿþoàÿ†ø¿üá;æüG6ç™ hOsLîÓcßc'L_Ÿ¿HÔöÔõ]Îë÷ ¼ô»õ}jÍbÆ:Öû.7ó X¿e×ñzõ^lP½/eîÉÜõ{SaúLf—~j@=1Ïfºéó™V}F³[¿O5¨žÇŒÇK°©wYÍç2ÇÕûUfìwýÞ~¢>Õ£×U§u…D½¾êÕûºÓú~`¯~—H¿à¡ŸÝØô™Nw}®3QÇWèVç;Í÷ú­êÜÕìn}Æs@ÇZèS÷匧ù>–—~Ï?O¿‡Õ£Ïxª5œùž¿‡z—ÀŒµà¦÷}~ú½‚çÕ£÷€ƒê=-s/è¥Î~šÏxÜô»Zaú}­&}þ³IŸíW±äÝ]ÙšûA?}´IÇbè×÷+ûôûZ#ú-/ýÞVž~ÞcÕïmuëóúÙÏ:Of¾»å¥Þÿ5÷‰]ê-s-ê®c2$ê3¡Ýz_دžù˜1Üõ™P«~W¸IŸèWgBÍg?ú,h°>Z­÷ˆ‡Ô>Q΂šÏ|Üôy€0}.´KÅbsæ=QwýîV˜zËܺéç=Áúý‡š§Ï…RgvŒèsuÞ:.ƒ] 5ﻞÖ÷^ÝuL«:jÆbèÖûÄu/Ö<ŠŒÝòánäì†Ïnäì†f74‘ð‹„Ÿ~øYàgŸ^VxYÑÇ:¢–çQèE~ùÑÔ'šúÄPŸÚ!;ÇPßü =c);¢–ñqȃ&ÛÄó/ßðˆG—xì}<ô‚ž€^ ðN8§–ý‚ŸˆîI$’¨cü“ OBï¤sjK°‡ïdôOFÁCM¦žÉðN¦ž)Ð¥ŸJ~*ù©ðlÃTòS. yiÃj‘ŽžéÈJ§¼ Y6dÙeƒ‡ Ô5ý3¡Ë„.ºLødR‡LødÂg/m½W¾±}t‚‡‘]ö̆Fâýç K|r¸.ñê%ƼÄ'ÏEV.²r±k²ò Éƒ&þù”ÍG‡®ÀSâá ß>ù s2÷!SânJ,M‰‡YŸbè‹Ñµ>³NâÍ#ÃŽì´—Ù%MÙRÊ–r½Œrûù½Ÿßû¡ÙO™ýCz[D;WPç ®U ©-R%v­„ü«¨C׫àW jxTC_‹=k©G-í\K=ê°MzÖq½žòõ”¯§|=u©§. بú7¢K´Mði‚¶ }›àÑVth…¶:¶Á«ü6òÛ¨Oe»à׿.h¢ËAt?ˆ½ŒÉ÷&÷ÜÝn“{îÉ=÷äž{rÏ=¹çþ'Úsø¿Ù·¤ÿHiÒmÝ­âI»ÉØ%ñ:$®žy.3LµÇ«:[$¶7ßUÏSﱈ­ÍgšM:¦`·:$ï÷É;.ßÈ|ö9¦Ÿwºësî6ýì³[ŸOPçÞåÝwy®`ž÷ÓÏDót,¤>}~X¿÷è§ž‡šïÏTëø„Ç]ñ‘ä½A‰‘$gw䙩œ½1c% éxIúl“MŸ™ïÕñ ‡tì$›ŽaxHÅ1”s§æ3ÕCú¼Ó°~_ÒK¿““§ÏÑ{è³ô6S©G©7ϧ&ê3õ=ú\ý Ž¯t\=c5ÏÕ»éç%Võ¼UÞÙ—÷Í笧U3ŽU%—÷å]y>*ÏÍsB=ê}9Û.±Š$f¡Ä8”3ëgÉQè…>QäG‘M}¢©àáÄP§|®Å`ë| –2{àK¹xÊ$ÑNIÈOAfö°A—‰<ÁÐÊGï\Êæc«}ð.ÂVS~vLDf"å)›D: Ú$ø'a‡$ôÞCþtO†W2ºïƒ_²|¨c<’©g t)ФB“ M*|Rɼè4±¼ÒÐUðÍ÷R_Á)Œq²lȲ‘oƒ‡ Ô3ƒºfm&t‚%+ø¯™ÔIðYSu/ú –¦lCÃ2‹: ¶dö¬¾ll’ƒ.9ð¬¹dæR÷\òs‘•‹¬<äaßù Sp8dkSˆ½ É/$¿ˆö)†^bÕK|ùbd#¯vì`G®Ù‡Ô¨”²¥\/£Ü~~ïç÷~höSf?ú”cï Ú®‚:Wp­‚k•\«Ä®•ð¯‚u¨âz5å[ùTã[ÖR‡ZúE-u¨Ã.uèXÇõzÊvB_OùzÊ×S—zêÒ€¨#º4Aߟ&è›Ð· >MðiE‡VhÛ¨cüÚÈo#¿útQ¶ ~]ðë‚ö ºD÷ƒØ+^æþAþ&ã’ýÇã’aWsï#{ÙÛ°§1÷0Îý‹ìYd¿âÜ›8÷#²ïýmògû ú‚¹½ƒs {‚‰ëÚÎ\çËúÞ¹®w®ãËõZLÖè²>—5¹¬Çëoç:{âºZÖÒ²ŽþGY7ÿ{kfÆ¿X/_¸Vž¸Nž¸Fv®'®ëb皘v6×Ãε°sì\ÿZ½O"1&匈Ä0’Ø”oÈ|·Ë¦bJÊ»aò~µy¦»G¿óÕ§âë˜q›ô¹ˆDuÎAb{È;Äò•ÄA^hW±t$£œ˜q q§·:k-±>ä`‰1'q‚ƒõ™gwu¾9‘è Md—zÇÊŒe˜¨Ïç©s†ò•œwU$Ö°yÞḊyÙ§Î('RŸhòéó‰çÔ;O{Ðit‘|GC m,4±ÈKAn,õNà;Ú„.5tÞl}~DáÓ˜çf¼õÙ»Ž7è¥c[5éó3ý:¶•MÅ·’ø&‹PΊ˙L3~j¿zÞŒMè®qlÕ{ñæùøC:vêÆ±qÓçå‡u }we¬ÏÌÛõ™›ŠÙbbÚô©s,fÕC ÓFÞŸ—˜ªæùywkHŸ±ñÐgèõ9ú.«[ÇCÔñ¶ÝÖ[?LÇVíVñ·%ö‹‰uÓ¯ÎÛ˜ñUÝu\îDkß!&V¢Ž‹Õ£bÉÈOâ ¾Œœ/܉U{HÅ®—¸¦rNÀŠÜHøZ)gåZ´ìµ$}HGÙí¡°nÌ3ôÝ ãFâg ÎÄ›‘³ór^ÁŒ«\aÐH,óìÍ9‡¹ñ^*¶ªÄ¡1Ï«ö«X[_â‹›±°ÂôYú.uî&Þ[ÇXÔgéÝTL,3fŸŠ³}ZŸÃéQñö%~‰sã¡Ï²ÚTL,3ŽÍ€ŠecâÜxéó7Múœ:%ôêó§õ™ wk·|µ9»áµšÝÐì†&~‘ð³ÀÏ¿LÊXài—•:ZÅÆè…>QèMùÑ#jyC}b¨w :ÄPïìK¹XÊÅR.~qÐÄA‡mâÅ–ðˆG—x±ôñÐÇ£Oz%`«tJ l"2Ñ{å’äÿ$è“°C4IÐìfú'£2¼“¡O†w2õL†_ t)ä§’ŸJ~*|RÉO%?ü4ä¦a‡tôLÇNé”O'߆,²lȲÁà ê‘AÉ„.ºLÚ¤ºÐeÂ'>{Ñ? ^ÈË‚. ^YØ2›ülôÍAxäp=r¨{.ùÐ%9¹È9Àµ×jáY€üÙãr­™ûøÀεð¬%¯>\k$¿~-ðiAÿF~wÂËNûÙ©¿»Û¹fG^)|K¹^š§¶;ûù½_~C³Ÿòûiçrl\AûV o×*¸VɵJìY ß*äV!§jDmªáQ jxÔbŸZô©=©G6©£ýë¸^Oùz ÔS¾»Õc¿êÒ`UÛ¨&h›àÓmú6Á£ ­èÐ munƒWùmä·QŸ.ÊvÁ¯ ~]ÐD—ƒè~»SÎ\‹ËŸì§'ã ÿß7,ûç¾Gö;z¯3¾¿¹ð>°ìehƒ¿ØÃÈ~ÅyOXö%ô sßýÇïËþBöï OÜ?LÜ38÷²'pî>k kçºâš_Öú²Æw®ëe=/ëxúû_Ü?¾pþrùÿ×Úüï]—Ó—þêšßÿ¬õ¸ÄÝ“¸yr^Zð‰$¾O´_a¬èVqƒ$µ` JÜ!9Ó,ø%gCÎ:šqè)ì?9)ç€%–Ä-2ãø$ª¡A°ú6XUìNÁ 2ãÿ䩸÷Bb ÚU =‰ý#Xy÷NâeÈYß­VŸ]b Iüv‰a*±ò$Fćßä­qïªõ¹Ü&u–ÒŠ|+e#IGö¨˜‘ð<¤beHŒDx$)¼Á:‰¦|´MÅÕ‘xwÿ.™{°M$ŸDü3…üèÒàMž•15¾)ȉ“y–~„ à—Â' ]²ÉKA‡ ø&¡oŠÌ·ô×ä¦À·ˆkùøj><‹¨k‘|£>òŠÐ=™Eð+‚O z•Á³~%Ô£†ü"øÕp­;”AWßú\MÑ©Lh /C~²k 9¯FlÚˆžèPC™*t8€Üð: ó6ü®w`³ä4¢[õ͆gc“n÷Éœ‹-:áÓ m.é}”P~ŸèEù"d”`ß"l_BŠä7ß”)g vÎV· d.F÷Zt¬âÂ>ÊÚù](s-ºÛÑ»E®aÃ2øØ‘m‡¼x€¯zÚùn!¿–ë¥Ô«Z¸¶>ûѹ¹ûi»ý|· ¿ ½+H·"»‚k­2S¦Y­È¬'ÝŠÌjù–yžµè׊ìZ™w‘ÓIýê(߈ÜVìYOùzìÕ ]½ÌÏ”iD‡V™·)×Ššø´¢OëiuK´ þmðoãzõè¢~]ðè¢nÉ;ˆœƒ”?(ãÛä{Г÷Å»Ý&ï‹O¾=yo|òÞø?ö½ñ–uøÿÃ{äæÜ,ó£Ìq2ÏÈ\!ã}wûõø6¬ÇÆÓ_ÅçÄo¤ïÛ´Nk]Ç4®—‡ÆX?¤c= ©˜¬ÏÕÄzõÐx¯VÛµIã~õé¸*C:Ö«›ÆÁ Ö˜ìÕ¬_ã7Œh /…“cb´wi|°…ç`ÆŒòИÕ*–¸;jPÇrÓ¸±VÛ­cI ê¸.Vƒ°GÇ!Ô1cÝ4–ìiûà®±þƒ‰ýà­c“ç) ‰/(ïfüØ<«¼WcA iÜž…#kâ¼{hœY›Š#hb‹õ),‰7eâ̺+,x‰7%c‚»eÆ” S1%ž¬,£Xu\Ùnq\Ńì13æá€Â2cz¨¸ç‚!øï«5FMÅ£’eÖ¢n\Å£21ÈÞ ÞŒµØ£c¬ŸÖqÖÝ~ÄZo Õ¦qá{uìÙÓ*ÖºŸÊCÇ\·éس½ ;ÓŒ7릱Ȃ5™]cÐz(,xÁkØí®ñh{4ö˜›Æ  Öñf«n„Äk4ñ"Žk̈sÖ[ãÏ6iü±~§ÝŒíã®ãÌzk¼ˆ<qHc‘ èØín:~»UÇxìÖx=3â´Žóè®b»›XdÞ¦qá{.™Ä›5±h½t¬ª< ß«ph͘³n:NU˜Æ„ïÒ8d‡4üÆ„÷P˜d»åƒœÝðÙ ŸÝÐì†f74‘#jnŸ~øYàe—•úYÑÇŠ>QèE~ùQäGSŸêC}b¨w õ¡Þ1øA,åb)K¹8tˆƒ&š8lÏw<<âÑ%ûÄC}<ú$ W¶J@§Ê&"3ÝÑ=‰²IðO‚> ;$A“Í/µ-HFÿdèö¡G2¼“©g2üRFÔv!•üTd§’—J^6í’F~rÓ°C:z¦#+éÐØeC– Y¶µÅÈ ô©Lè2¡ËDV&uÈ„W&|2á³ý÷b›,øeA—]ü²°g64Ù蜃.9ðÉázºäPÿ\òså{DM%yÐäA“M>üóO«-L:À³€6(@¿}òAÇ}È,¤= ‘YH~a“Úâç>ÅèZŒ¬bdÓ6vìiGw;6°#³}KißR®ï‡Ï~~ï—ßÐì§Ì~ø—cë øTÀ§‚k\«äZ%v­D^u¨B—*®WãÕð¨†G-ö¬¥µÃjU‡žuT´Žëõ”¯§|=åë©g=vl@ïôn‚O´Mði‚¶ }›†Õ–«Z¡mÃ&mðj#¿ü6êÓEÙ.øuVÛ¯ƒèrÝö) qY—šc¶9÷Ó“8*ÿsqTœû7Ù»9÷kroŸ¯¿Ø§ÉmâÞlâ}~ç^Lö_²÷rî·d¯åÜ_Ñ—Ì}•sOåÜ?9÷N²g’½ÒÄ=Ñ…{!çȹÿ™¸ïù¬}Žs#{ÙÃ8÷.²oqîYœûç^¥ÛíÏ÷(“û“ŠýÉ’>Õ¿Ÿ^Ç?uS1;W0ÐïLðÍÛ` ²–œS¸¸ž ;Spp¿ÒĪmRøf‚÷&>ËÆ¶`Ž-;¯0„»@âˆJ|îu”ÙžŠï-¸µ‚ƒ.±e%éªã wLðÅÖuk,Ën…¯H~`“Â3ãŸú)üZê´ü+<ÚÀ>—Tbe 6æº>…)fbëP¯@è‚FÎluÛä§n[ v—Ä%ìuÁ€ìÁÇlÍÞ*†·àÖJ,IÁÿòJT1IMàïQ0$Ö£?Ô®qzÐw+ü¶A¿¼WavIìQÁŠ•øÞ‚íebÊ’M™hÊúØU¼îè.…Q$ØDÑÐDÃ+:+u±BgEV ´±Ð¥YùÀ+t éØ>…¡–M´¬§°uåÒÐ#¡GÅgÝC’¨suÝC½ö [ƈÂ@KBfÊ!…„®iЧ‰tH£|>6)‚&™EÈÉçS$ü)Sm>¿‹ ïlÊ¡w ²«(“Íï2xA[‚žÙòM»Xø]‚ìt)CF ôeä•õª©¬šhŠh—2êTÃwmQ…~Уƒú4¢WÔE¾Ñ½†:@Úºƒtߢ'ý3ZЭ…ïÒ-ÈÌÀN|2)Ó‚ìLdì…_'röÒ&Ô3 YY”É݇ՔœC½rà›ÃõÊåP\òså™yð̃&š\/€_v*@—ê¸O>È܇¼Bä’_H~!务/¦¾Åð)–4²Š‘Q /;v·c+;i;eK)[о¥Ôe?¿÷ó{?4û)³[í§Î娩‚ü ®Up­‚k•\«„%ü«¨C׫àW jxTC_œZYÇѵȮ£Õ¡gý¯Žëõ”¯§|=u©R·Ç¨×› m²ª¸ÔMði‚GW™|®2ù\åŸߊ±çï~¦2ù°$LcoÒs°ù»Â¡71Ñúæ¯_—ÂÛL™~|¡õ œG¢ÆG«Öxõ½§‡2óÎñü…& ä÷˜Æ:öÐñÔáá™§°ŽSÀ>ž½ ÷XpîWi<Ï~w}‘¤G4Þ±Ma¯=¯0Ž—t)ü4™.M\ø/ Ö8ŸÐ.µkÌãn›F—1>.P8È&¶õZ‘¨q?íÝVŽ(ìc; û­’ýˆŸÂB^=¦q=4Æ2½­ CÍþ>èî3 ð?M°|ÎkÌ4tñ=¤0v|û4†´ëÜ5ö1üÖÁoUã Ûžšð>5ÅGÁß2¨pÔü†î±`ˆšØiäûÃÇß[a!›˜ÇkkèoA¯ƒ óX°Dka#å7Žé¥rƒì÷¸[ã- oòƒú5úCl×øÇÐÅÀ+®Ga-lSK ¹!” ¡!§nšà-„`“mèC:þ¡v ¯Pl¿íÐmÓx¥ðÚ¯Á 9lPa vš`.„Á/ {îtWx¦‚_*8Fáð?¯±L¹AŸˆèÖØi}*N~¼öPŸ,ÊDÒΑÈÈ£.ø&B› |,cj dE+ºX¡‹SË¡hµÊ‚.šëÑÆ"3™±ðŒÅö±ôµXúZ×KÑ5ÙqÈŽÛ!+޲ ‡2 ðO \¼à•€~‰Ø)‘r¥Ø2‘Oô‰”¯æÚÙaÃ=Øp:§R—tx&©e˜,ŸRàßx¦ O |S‘ ]ê˜Z¦¥¡[¶ìóÐ!þéØÎFÚFÚ†>6ÊÙ(—3d¯ƒ K)ŸA]2Ð)}ö’¿—ºîEç½øTµ\ƒ~/ôÙè ¯lôÈF:ÊgCŸ m²r‘• M.4¹ÈËE^.ùyÔ'v(áZõ̓6Ú|µTÜ'ßðÜÏrlÐ ßùF¯BÊR—Âój9YD¹"èK -w9º• ¿û•P¦û•‘_¿jÊ6#» º2hÊ )'¿œ¶h&¯]Ê©w94娲’ºVÊ7y•ȬDfòª¨Oyí¤«ùÔp­†k5Ô§Fì@ù:ìØL^ݘZº@‡èÐ u¡ ”o€w¼ÆÔ2¶šhZàÑ‚Ž-èØ‚ŽØ¿ƒ>Ñý;¨_¶ï Ïw oúvÉZ\Ö]òçÜÏ8cw8ï]Ã{7Ðm7ðBÜúÞx¬ç}mY?O¼·í\+;ïg;×Ä×À®}ë]YçþµûÙÎØ&í¾ö…ï³_¸.µ¹ýå}í‰k͉÷³kKçšÒ¹žl™°~¼ÔíÏïaߥÖp«ñµ•|VW˜©+é¾ôñµ´‹/}z=y .ª`Çš˜IM “Õ>¶vXá{ZfÒÊA…¾~@á? x@¿Â> àZvZÏõõÐ+ ÕÃ×Ü[aƒ †êÚ1…I(ø¦‚%xæ‚U#Ø¥!= Ë<™Û¬` ¢à‰‹o<®p\û†¡à¥¤“ež¡žÉR'ÊW’WIùJê–õj¾ó¸^ˆœ:®ç Û^è÷b“ºCêK3¼š±g©Ìg´u3ymЕcƒr»º ÒM!z´ã“…ðkÆ®í”i‡W)´%\o†_;åÛ)_z^ ³íðh‡G5vhÇvÕÔ§:´CÛ ŸêÝŽŽ èÖ.:Š êÚ ]3ú7OÊ6cvÒí|wÐÈé@Î ™{h‡®&µw2ÿ&ïÃMâOþgïÁý{÷ßþÚ½·ÿ©÷ÝþQÞgþgº÷ö_qßí¿âžÛ×ûmÿïÈ=7úÉŒ`eíf`£™]“ú™øÿLü|æ€Æ$¥Ïίfá³ÆæµL1ó»4>)ýf}cm<‡¶òéRx™s©ÛÜ^…mâ•Âo.üæR~îÆ*Eôð°)\ìõŒóÑcþ!YJ™ùÐÏV8Ù&†)¿ð{z-tצðò„‡'<‘ȉD¿HôËÃ…ØÈ‚>Vô±¢^Vh£¸͵h®E##šëÑ\¦|,rc¹‹ bá ¿XÚ;–úf¡[!õŒ“u8:ÄQ—dê²>åÐËZ:$À+^ ²¯ýeé/y”+;Pv/2öÀ³TöÒnÔoõÜýè“á•‚Ž)ðNA¿,YçÃ#¾©ÈM…O*×Jd] ÒøNƒgú˜Z’ÙHÛHÛÐËF™ê=2‘Œ lÙÌõ Ù»ÈÚ™B{Ñw/t{ñ»½Ðî…v¯¬ù‘ߌŽÙ²NGn6å²óÔò®½s‘“‹œ\® Sžì]°C´ycjùW‰îû¨O;åöQ§}ðÜ¿BYçc‡BÊR¦úJìTmt%Ð4 »Dê€N%”/¾D®!» š2x•!¿ Ú2èÊ )ƒ¦œürô/ÇžåÈ/§Ÿ•c»jä´#¯Ù•ü®SËÍ*lVE]ªÑ»zL-=k¸VõfÒ|×Q®uÐÔQ×èy C6m@÷ø6À·ƒþÓ]t-е@×] z¶ g z¶ g¶ïÀîؽƒ:v`÷tî@çtî’5´¬]åOö)ï«9÷!ν„ X÷Î}ƒ8ðߺ_&ëzYÏÖ}0çüï¹Ç…þÿ_q|/¸‡ôïÆ<ø¬{GïÉšìÂ{FÎõ×·¿¼Oä\GM¼/ä\ëuÍÄ5Ì…ï1N¼ïƒ½æbßåÔe5}a%ýkµÌ×ô?hèÛ¾ôµô5?úLù~”YA_ÛH^0õ T8ØA´{ýÒ‡r+¨k0ŸÕ” ¦Ïl‡f=6Þ~Zaz‡BÁÇt¸Œ»ôO ¼èËèbA—|¢¹}¢(C~”|H‡ŽQxØéФÃ#œëé\O§\:§“—Q­¶ŒÔ']øàW9è‘%ã+´YèßE!·èSL:²ÐæASÊõRd”Êoñqúr¥ø=úWbƒJh«ù.ßÇ窡)Á>ðiñŽq£[4ÓÎÕÈm¦|u,'¯ýÚ¡mæw3þÙŒž \kƆÍÈ,Ávíðl§L|›eL…G;òÛÉo‡®¹íèÚ.×Ç”ÛtÐ.Èì@æ ñYìÛÕ¥|]žIJ܉‰ýŸý‹zÏú¶Ä‡Â¹NÚ[a¼iñ<ÿxÏhõï,/›É<˼vÔì•5ŽÑ§Ž^rwàPä‹*ÿBNŽ‡Þ»öãoú±l™¹ðò°‹q´~{Í/^_ô¬ÅG¥‘Ó§ä|óÞŽ×NüÖòòWä­Ûw‘côé%×N.qœðýÆm›|Ó-[y5ðò‘ë­Þ¯ÞºïÀËšü»_yæ?¦ü *ùû_8¶ëF§^–—¿íŽoDE~êÔóéáε]ùj@k³Çϳ'¬7Üøý}ŸZÂ,_þ_ñ¿Ëq´ZÓŽ*Û²lû›¿Z³ ¾#ŠoòµOþè"wË+_ÑèVýSÇèwŸÌ¿9úg}'küÓGâ-Ö¹µ£º£-¾õŠWξoÙžh}£ùžnëÛ¾Ê'ÿ]˜ùVe¦âoyeYûó³¾þö›ó¹_^]k‰Võq´ÿòþ/öm œ·*÷ÂáUGò·[^ÙôþÔ¡ò-k¹£O†þª-÷)ljÔÜ"ßö—ß¹³~Æ’—¿nYñÒ‡w…õ'ÃǪø\qòí/vÀÇrßë_î¸þÈ[/„:N.üÓœiw<·óÓ—çAoWô3«Êæž{ÁòJÎàËïÇþ«cô1Ëá“‘7»ìW{Ù‡g×¼àè\Û¶÷ÆwS)§úˉº^òÈt§Ý-¯tm Kh pŒ>rQôƒ÷|.òµÂ-ù‘%ßpœh)Üý›./K„ÒÓÑqé©û†§¹ú…¯êí;¶þ¶Ûõ¶¼òUGȵ‹g8FO}纀º¹‘#Ï|Ð÷ø;7X6(ýE;oÿèÊs–ÕJ>|Tÿx(mþÙgo¿ÙòÊ“AÏåžÅ¬pt„$ž{b ôªÝ|ë¥ÊEýÞòÊÈœåÍí7BàñP·["ߚ륭?<Þuÿiïøâ³^OVºÚ}­j÷ á†u¿±¼‚¾«ËÕO=?ýêÍ—;õwœ@û¹·þÆÙnŽ6:¹ÝQÕÌÉýcÍ¡=ãv6²ÆÕÉž}ïï²oÇžŸ_ó„£õ'Ëï¬ü0>ª<0bÿ$á++Æí9¼éú€¸TìñØgø}í”ãÄÏË¿2í¹#ŽVzE×àTÊ©þð@QÚMý³žï¿ÃTzéü$W}¾Ñz϶9iŽ“{î;Å'ÚxMC̃Ÿp´m¹ä¹´ó×ÃGõãƒS#7•í´ —Þ7'®ì úổ·~ñ¦q:ù¥3ß|?ÿ1KpYê÷ÀÇŽÖsž¶ßþô.Ê«þpÜìN–áK‡=úËç)_þƒ}ÿrµãä¿\ú•S7§;Zï>ùRïàIèU»ßß÷ጺo^?>. ßþ‹/m}/ÉYÇèãoEeøªq»ʼeϳŸZBÎÞZêh[ÿIó—®y~ª_Ü/^1/Ó2Ü¿çëÓ¾ñ vÿøÇi‹çŽûÅÉ]4ý÷߯ڣW\}Õˆõíuª?Ü¿°Ö:ï[=.;>cñ\0õ ú×kïE|¾Äqò¾R¿9:ü~ðÂÚ=e”SíßöÚflü‘exxÙëÇïØï}ðÑ}[.ûÄq²vñªô»£c¡ÙÐЫv¾¯6Û½iþLWùµé˜®qûÆŠØ"ßüÙË·>÷Åq2)ãÊn™mÙ¨û_û9YÉÂOµÿ±±ÆšØª0Ëð§O-úYEòëæÜ¶ðC—Ýö=äsE»e«ÇÚFŸøIÁÕ=”Wí~,õ­·K®úåÕ…æ@ë=ñÕOVÞÿˆãä ‡îëðŸÚnzõOÛ?ø9åT{øÜOÇ‚/·,P|-¯Ä­5ÆÕï÷üáÓ«ÆýèäSoÝÎPbÙ&½&õGÛŠ+?³È×5¬Sýâhóc]ÑÁŸ×^n|Ëëx½Ë>ÔNÍÍ}Ùqò}õÅ÷8ýÚÑÚö„Œ|®ùgêG/ þÉ´ÃX–¨qÈòjnäCÛ/ñpŒ>zôŽŠü'?½2ðÛÏžp´üáœÒ± ëÛëU8zÕÜ%K?øÐ¥G¹P:\ýó‘±ÝžEWÛù”WÍ2ò-ÏýáÛ;^¿>ª][Rðjm­sþ²¼Ú~£Ïí™ObŸÑ÷5Œ·ó©¯ßr÷É—-;ôxÙ¢ûE€sÞ[¯úÏ™vÓ¾eyõê»®¿Åû ýű8àçýŽSEoÞ÷ìÅŽÖ’ï>ö‡Å-¿·ro¾qöÝ”Sýäȶ+¿}ÝSµ®ú DÄöù.»ÞsòÈ®3ãþ~ªÍÿk=»ŽXBû>pU˜£­©ü׿<üŒe­Ê‡¯ê?‡oœýµ¯mË÷ãWO¬ôÿiK¡ËN_y1ãã”ÅŽS§Û™ç1Þ^3d‹sµ×zÕ¯îÕzëùLÀòÂK_uö3Çè—ïm¸d˸_;õtÚ­s±\qõ«õª_õýÀ¿øîå3,¯¾ù²ï G¾ãý⬨K/pœ:XõìÛm—9:oHÿÙ®òèU¿éSó¶«ÿqÑã­oÿ‹côJÏuÅïwœºöÇßè}yÜ>Îyr|žÙ úÑÝ}ßZ¼êMË“¡côÒ€žšgê]v>ôÓ³;¾ç¸_´_´eôþ3Ç(¯úÏגǾRððAËÓ¬‘N»8F;ožþâÈ!—>õï:Ú´Œ# ”Wýå®ìkb3-Õ–32³ìV8FÛ~àuÇ}—8N;Ÿr2—yèV™Ÿ6¨~rgÀ)ÿÖÛ_oÏ3[×|ù–û]ý¤µsßÁ´ç-2 ?5>ÏŽûÏÕ/¾U7òØ–3*ßÕ~-ïFYu™«þ_j~½ædŒÓ?-áþß;xßãðQýáÐǶÄÅcÓ-g2î8œxëÓ”ÿÉ÷n~ÁÏUï'dÂ/t´ÄÞ·&Ò«–rª½ï¸çËæXÎTÍݸàä•.ý[¶_•v¹ãaßçnxóÄVËö±i7 |ë{ŽÖì„‚-G¿MyÕþ·‡]“¿ç½ßÏg•>>?×Õ¯›z?9~tܾíÅst{Ò®óÛŽþøQÇYÖ_~ªÜú§w¿wï,g¾Ð=ü£ŽÑúëV¾ô“ׯÇLJ‡O>tOöãëÞö5÷½ûþæïP^õƒ[·|øÈ–J˙랪ùήƒŽÑê/tzUd:þuÎK?zð‡Ž6§_ú©v¿EµëøzâÌ—ý{nºf™k]PúáØòk"®ÛááÑ€iû¿g³D…üöw·]Õn~ñÅ)É!gÇýàŒÌÂõ!ŽÑ‚÷N>Q™êÒ_¦Ÿì÷ÃñyÊOõ‡/ç løhÙË™;ÞØúxñï]v̪™3rË^ÇÃ×_zÓJ‹e½Ú¯˜¶öµÍË)¯úÁM§ßõízˆò‡iJo›c4}ã‡Ei½ããïÃj9ãhûáÓ+_8¼šrª\ïu/—u>m9óðsoüÛ•Ì©‹YQþÒ¥wñéÙ­}ßâ{áºÂOõƒ/}µ;©òs%–3Oöz½•{‹«¥~ôìö¥.ÿ{øº¼·ßäþ—ë+Õþ=Ï{?|x¬Àræû¿qæs³£[Ï_2t…ãaU/GÛ7Pøí¯C¯ÚûÚ™»›Ž¼2ÓÕ~¬VßÚëÍúƒ_‡Ž÷»~ヌ‹_Ê_—µ=ðÝ9Áø¿êW«~äêÇæ6­Ô1š÷£Y¯_îè_a6¸s>¦œj÷/˜ËÓ"—ü‘5žë?i¡ÝÇ>÷ÉeÞŽþͯ{Çϵ®óWíÜ­ÇÙ3£‘Ÿ.µÑOö-˜ñŽãzGÿÎ‡Þ k.u­ýU»^’,‰ÛÇ×CgÞºèáoü’ù9ÿ ×ÒïŒüULfņÃSýV“s>œ`/ÕÎ-G¦î]¿ä;–3ïTuN¿»;{)jÞmãý»?ñÛ?úâ‹®uTé—=YÒP^µsÕSO›׈åÌ{«}6un¥},7°ôï'ý¶;?d*ríT»îUëq—ÎÏù\ã]ЧÕïç;û‰e›Ö§ã¾>H†]óT€Ùî–¤Q7†ø/ZÎüñ²ÞOïX;>.ŽÆOV ããf?£â´¾iÙyÓÊÈ—6üÂÑ™÷æÒŠÀ]ûä³ý-ÕY_ºóG/:,gœëèïþöÔÁu‘CÎzéñ¦3Hn(L¡œÙþ–Ö×îøC`í9ËÙ)Ûw­küØ1ºûÖ=Ç,ƒŽþ]GbwÇ?êhö@_DòLèÍv·|nëuõ×üÚk¼ÏÊn·àNÇè®Àozç°KïUáŸÐ6>oê}kÞ 0û…åŠçÌ sÜ®g=|÷?šëæ ?våÙ·Þ_϶¨õåÌ~`¹æ¡×-ÿúÊ£–³^—]:í»ÿ§æþø«ý[;Ú^Øêš_Ìv·\/w5ö½4îg×ßvÛ'g*]~)ÝfʸŸë~ïš÷œë—@³?XzïëðþÍ ó-g·_2¯÷vöÑYGž‰‰wô_ÎŒ3:¾Ôz»öÕªýo–Õ϶©–³ §›{ÿ5ßqµcæñ˜îž ×8‘òùå}¹ÞÕž¯|w¿·£]ã7ïí„êGOo¯X|vîø:ÿlŒCz„«þñf½]õéØ7Ë®rÜŽããÿ&Õ/Ž…GnÞ|È5ŽîºQîÐ:F­‹§®›ò[Gÿó§ÛÓwt8Úüà¦ß¼ÿåT»Së<—Lž¯½âêOa÷Þõµ/—eÙrÙ}®ù{“êÇÚE`—ü­¥—.?“áÝrøèÙÅ.{¨ûCÎ}¬£=åPÉ´?°_ߤúÃ1¹[Üc9»íâ·÷\¿È1øö‹ï÷†»Æ›{ÎU‡<ãæhŸs*ò@ óÎ&ÕþÇÌm}„ålØo·½|o™«=ü¶ÿ*í§£ÿVsa溤Úÿ؉„å Wù:ÇYæç_ù­$×8¹aÎê#!ÎþïšwÊ—3ã^ Õ/ŽýjË®7ƒŠ-gõýèqùÞ?—>cïø¼ÓyEÜÜ?=K9ÕîËšý½ï©sÙÍ÷ð]_­sÞ/qŒú¿û…±oýÒ!‹žÌ¬RKк«Þûßì} tTUÖ.I*Iežç9d$ó<çVÀVZQADdŠŽql@¥AlGZù[RI1iOl¢ ¦1‘JœÐ4â„¢-­¢ÿ·ï>çV(á½÷¿×oý«ß£ÖªUuî9gŸ½÷ö™îþ^;4(³~œý ŸÛÁ_>yxÍ‘ÇiïÓîlʃF¹C¹_ô„ïÏrÎÇîY¿ìÒìbñ!}ÃÒ¹Ïíâ)´’/Z^×ÞÔ7 œó Â?y%£³>Ñ{f·nQãòs{Ø’O ôµÚûAï´|P‚ykéÜ«¦Ž¶ Û?ÛûLŸs½—Ïõ¾ÅþÅŸÄѳãZà‰7^\ãÔCÅŠõŸ=X$lOœµ6{¥³çsý·Ž;ë¦Âw½z¤áÄŒõjÕ¹¿›gMPíN–#\ÿ­Ùå”ù¼³?‡ëÞYÿU—e74qöîwÄk”}RíÐ9Np{hÝ_šÿþ®Ç´÷Cõ…1øÿÅÜñSNýJoº¢Ô8ìÜ-àvÑúΤ#=—jï›ÏIzܭ˹n,_ñbë˜Ë…í!÷ëÒÏñ3Úƒ±^-àvÐÚqÿ…©7{`>Lʳœòа`¶ëþq"å×üaúàzoÝV4ûÝÍÚ §èÿ¹9û¿OrÖW×o[híükƒÇ~O;­ÊŽ ÛåÝÔTÄâô¤çúm[½sÖïn\~*v>QòˆSž³ÎÿnOôakF«DóŸ+ŽÝ;¡ù¸^Û>œâïÛ[¯ žÐ7bÄÐU;'ímÌšy\´)äúlû‡eÖ£ÓÎÃü}ßµýÏmÇ:ë•KöýÉØG]ü“~Ѓô\OmßµŸÕ® òyžšÙq¾ÛÝ¡âšO¾¾¶ûeŒo…\[y~uUÕ¸š·îÛ5[w ›¾,™*®yF¯_¤gýomÎpï ¾GÔè7‹ïX¸sx;ÂrÜÿ[ç¾I!ëë€Ûêë–gkƒo-¹yØa1tÍêûû/«Æø­¯_Å5 ×ÓNÒs=´ŸïóÙ}oíÐw?~¢ñ3ÔõÅ»ŸÍ¶WõI\ëwà£}+ÞFz®‡öçÿúìï0kƒú¶ØÐ/,Ù|M°õæ?Škõmx¬¯ YÿÖàïcßèüTÜqFx1Ô|ýÞîŠu¶Ž6|t΃‹XÿÖ:ÚMÑõe©ÉÙî•Ý2ñga£ÙTÜÃÎñ³ˆëÁúhéä·’mÎuÜVÿ›oéuÚ¡…wZ—Ÿ·ÈÙ¦ÓÎÓ‘ŸëÇúã˜_¦®ý›s«oçÔ¡Üâÿºàíún¥wÚu»u™¸&vç3¹m@~®/šícʯ òÄâ$½ äãzµ½Ý™£ n,|)àËl1´.Ýïâ1©y6ÒqýÙxÿÅǰ¿J^á#lt:vΣjŸù¸Þlw®§m鿼iÑ‹ÎþWu»Û[ÓrŽó­¹¿+ÍX&:õ‰Á³b>mw¥>å<_)ázÜ6;çÝÝ3œûß¼NCÛ©W‹«õŽ¿é¹þ¶=#W7¶hƒñ¿~î}Þõbè…¢—Æ¿(ôíÆ‰6UÒs½mÖÛ6£$†^Ùì3=í§÷Ü_ýú bÁS¶kï¿R+Qã[ ×ßö‘ŸÜ½¡,Ò¹¡o¤ˆ¡=˃žžt•è|wÑ-uñ/‹….n·¢—pýmÏ œdŠ^ç<‡è¥ ¤ÕÎýÓã©ÏÑö¬³_—rýmÏ×–´AŸ_î¾£¶Q ½›;sÚFÌõã÷T±ð‡Zÿ¤žcHÏõgŒ?#nîk(›à¬¿¾Om³Üàœÿùä¾sõ•Ãõ·=;iÇ Žjúu‘qb¨ÿë¤?V}½½mé¸~¶§…Ý=N Ñã¢/ÄÐþÌ>!:Ð'Ãøi‘ti»ÇOø´N2ÄÐ@ärÜýEçØ[¡éQH'ëEÎ'è´¿*_ ½¿¡¡íÕÑÉãŽs? ”ëaÛ·¡û¯þYxý­•÷ä?*†>ÖŒ6 ŠŽCEÇ7­ÉG:Öû¶®²ŽÞý¦6ðüïäþó 1äx´rÕ=>Îó&켯úïbþË#uîýÔr¤Œõ¾¶7wíи½9Ûñçsÿz÷q«*GÌ#3²þ äcýo£içîï´º¶óÕ'ÎqáØs×Oßþ•è¤cðžD·ä“ýçû);zoºK¸Ÿè1ôó3¾»·Ìp¶ËÔ›ÝW%ü,æö~÷òÉú(ýü¡¨ïkwm}áÄ8cürxÎè+\x…˜ƒÉ^ÁþiNý•Éz‘ãÖÀß>dö6Ƈÿo>5ùqѤ§f!½¬ŸÖu˜I¼« ÈýGXìên͵41ÐÄ< v¾7]Žô²Ÿì˜»éŸ>óµ«N~²àlሺ`öK›žÅ«l=7Êd¿Ø5훽¤ ÐÏ‹k„#¦ñLj÷Kç"±¿´´g§åH¹ìmŸ®¯Z࣠Lã¾3h®pľzËÈo/0ö“ä½,¤çúØ.Ïôâ“„#>nBÌÔun…t²ý?ðФKš§ èÇ+>‘¸õ…m{7yaEõ9ÒI}Ó40ê màl}¡)#gï);f5o•m™¼é¤~1y þ$ï9r®¤†'ä’cùìí~-s…•®¥%?씯RÚ]9_tÜrqÄÒ•o ë¢ ço묳éT/ÿZ¤c½ªy€}pNèkÆÇMgÝûÃ1›÷ó‘ŽõjÓ—ÓËœ÷\øQ8îYwÎÆ†»„•®-í9„ôR¿ißföf=!ë6½¶óÎýx.õ9Ỏ.rÞ§r®Ùî¤/ÂñÑ9^KVc}QÍrÊy¬f—çÇÍ»o¨#~¤œ9‡_ˆ|$M³ïóý±Àô?«:‚¥7⥜Õú˜f—ûˆŽ/bM«MÕˆ—rΩ]òÓ„Mš³Š›?Ãx÷åy±ïo9R#ûÜ?³ïÿ]æÚúÛ¿ý3¾¹ñRþ'÷ûÖ5oÖ쪿|ùóÔÇ·"^ÊߪwPÍþ÷_'W_ò¬p|µ9uåúW/åç}(ÍÎ÷[…ãh©í“Ï#^Ê/×vÚ5·A8Ž…¿yÿôÏ/å¦mš]Î?<óúí¾hÇ5,¿œiöw ˆ¿=J8ôí)´ƒ–¿“ ôóØŽôNqÐônå.¶©eù;¯Xv[\Â:ÍÞ³êÜ/~yX¤Sïs½ÏòwºuÍqToÃÃúö½–åî ÙoR¹fZ牃q£2¿yz-âYnë«ïUÞ=å|ÍþjÌ i+ljƒ)áã^JD½Ö²ÜÖÌ:º¦ÙŸ_øÞõÍ7ˆƒé öÃWº#žånßD cµfï¸yÒüï’ÄÁÌÝëš2!w-ËÝøÒÇ›µCšýÏ»h¦%fù~bº¿ñ,·Ú?±?@|?°©cyí÷Ä…Óú;¾}½xJ ž³œmËŸ·voðÓìtvÉUZÿÖ„ I‚ݨcyÛx¡Ùyýªõ?9~_˼ïÏòªý©þõÏ_k.›‚ç,g댺¢gŸnÖì7ݹëƒ×4­³ÂGöÜŒx–³õwÑú§^ýùÊñˆ—r­ü8䚯ýf9ìÔ|Ý­õ7V¥?ã|z)óTÛjš¨Ô¨ûzƽ¿Åú5ÃF;Qç}ÂçOmÇÅ"yß­“û«¢+¬E´ð+3乫•ï9ˆ™|Þ ¬ú±i½˜©_o-íGh"ºS4Þ«Õ{4¿-乂˜&ïó¶ó¾›¸âfA;=¢}&M V‰©¦Ñw~ü¶qØÐ_ïÉýÚлk{êuT»r~S*ŸKz£ÿËõ‹ÑÎUºØ“ócþ£ï_ª}du~gÔÒ»ê¯ê^àÙÎŒv-í´º?Ö>eÉ¡ËøíS½§2‘v B>íÏôEùìJ1×ÏÂÊíI\óÃñ7oÛ(¬úëçŠËV½ú²ÖÛ+¦ÊsßiON)û¹æBã\Tµ_¥?¥w{]}ÂUoJ?Fºð“óïsØ.kF‹Ü¥ÎG ùUû³Q3½õ>uÏÇh·ê¾®ÑO–Ô?X÷ªhäs@g»ä}5Cn5^µÓë.{»Ä´·ôlDû^ú¼%.£·A¦-íú6p¹˜Â÷ú„Õ}UÂOëV kÚÛßSÕ¿5'çÄνéâr>GT)²þ­gÏ<ðøá&'Ÿ|î&¦êÛÚ ¿‡T=©÷úy.®áþi”/÷ýÔûd†¾/7ëÈ¢ý€~,åýšvùÞ´ÇF¿ž!ë¹]¿è~HL—ãPûÚ¿]PóV¹“Í/ß*&¿83´½¢ï¿‹K ¾ýUPÝoÚ—ê‡jýý7ý»¥¯úzæ±ß•Æ ažrŽ~ aÈ)ï/¨ûÛ†ý”íϵ݉öqz‡sö¯i;Â^J=$&ò½Ñþ½pñ¤S¹.ž&÷Úùž©˜)ÏõS””n1+’ ûÂN/úÜ-fþõš™õ¦äÞâÐ_àј­›4»äßÞ¬ìZ«uôî7¾Ú«Ùå9¿²j¼Qã¼ß¡îµãêw ^ÐÏåþ¦æJòxQÌÛÞÓE;÷Ka¥]À˦ ëÅ´q‡q¥I_¨‹+ùÞ˜°Î¬¬M‰³e;³Ò.qÝ€˜ÛþÊ×+ž]&¬ºYÍMò<½}›Þ°Å<ý5ع†­“iƒÂxÑukŒÏ­úµÃ0c^a¿ëë>\ÿÍN·™—@^¾¯ Ùå½<û£¯§ïþè0Ú½™aU=Oæs£Ÿªþ ì˜|ãÕÂÏ›3<~3¿WúTvRGJ3åýje/½‚“Ç­ìçŽ>g|ZžÁ–9ƒ-óÿ—Kú4±~u?—c¥O!Èë†<îèWî»÷HŒn‹Ä䆼&Œ ¦ñŒ©g‚þ<1x"칂q¶½Ì_ù½ ¿w–ÄÒFYÞ e.”øÙvéû°Qbg–˜Ù“¥ßÃÃ3ñ~6öwè°“ÄÈ>*}"€ptˆp`£ÄïAà-¼­`ŒlÝÏa#ãFvƧ±ÒÇ¡±C‘>tû5$ü°f‰‡}\â`£O…C3b,cˆè>  =E®a l¿Ö}®aLÂè`ÆÞ‹FYÑ$vÊŠA| âcŽm”þ í<\Æ¡¼8ȇ¢%öÆ3ÑýŽgìÂÄn‰‡ ZIMŒƒM~ºA†ý6K\l”™ÌXØäŸ[Ç9Î…:66xHíÔãŒíG-:¶ò’•4ð†giÈK¾8Èç·Ž-2–ñÓ‘.½Wbe'Kœìö3I¾½i¸§wÓ3‘>³™ñÁuül¤ÏByYã'†ÞÛÕ1HK_ˆˆËËxŒ:æâ&ÆMÌ&TfÆ×¦wG!ÿ¨fÆ™!æä—œüƒëX%1g¼ÄiD|âsŸƒøÜ`‰Á=^bp¯`ä:–Éaö«HïÐÆbt—ó@#4ò@#4òƒŸ›Þ0|0®þ‘.¿‡1 @«ü@ºï^°Lby£¼ÔÝ9×1½¡ï‚ãì·‹î{ë¾ÇÃFmºG­p ’p¾ÆæŸ±ùÿ›ÆÞÿ{Û{êÓͬ?Ýwõxéϸ—‡wôw„Ý{yHð@ð@]{@ú» mÚ„º5A?žèßž{"ÞúôB¼èy!¿äóFÿôFö¶q±fÐ6#­ù€ÄCœâ| ‹/Òú6JÇû¡ïû5Il1Èâ°?øöïbßÇ„'€pÂÐU ÂMÒ¯1xoAà-å> Þƒ‘>$ßñCìc‡"}艆òÂкyX#ŒŒð|¡‡È1^ú.î•øÃÐSäÆÖŠ–~‹ŽB8áh¤FYч¥ßâÉŒƒøX„c›$>x‰ƒ²âP^ä‹Cž¸^öç¨c†niHNE¾TäKµóðL8"ä;R÷kŒ|#! ù(Kó4äKëa_„º_ãñŒœŽtévÒ3ð<i3V0ŽùÁÑ}ãy&Òg‚òÉ’ÙËCÊËšÌ~+ 3„üë8cÁŒ/F˜Ì„«L˜Å%c%~2âG!¾‚l>ÒŒBþQËØï%á”fù£Ô1É _âs–1ÞrâsŸƒø\Äç">ñ¹ÍŒsBþ+uÌ2”QI¶åл®ô~*½kšy]Œñ•:y S‰|UË…0ͪÁgõx‰iÖË΄ÙLþ” Ù”4IßÊÁì_YÇ ÛÂXfäk¹`û[.jJ >Kð¿t2ûj¦{ìuø¥{C¥dCÈ>Ї:¸i„Ó®·ådé¡Sc>•Ín¯©1}&›¬lñp»K6—l-t£ÛW²­Ãmêéìé©l§Â~n#•íS¶NÙ9WÛF6MÙ2²cj½J¶k8þÙ'²Id‡” "Û£lÎp{C¶FÙeKÈŽÝ {¡l…« Û@vAÙ‚ác?ûj¼§±žÆv×ÕX>4˜Ë&úîЯ;ÍhPµh+&”çÙˆ/ú¢â¼@×íÃúòF>3ò˜‘ÞŒç>xæKc,«hOþÐcÊDú@< D¢ñr¾xBã#Ú@(è‡BaÈ=‡¡¬p3c‘w“í1ù"A# y¢ð=™qcð<á3OÛŸˆü®­þÃ_Ñ+±úPvJ2cPÞ:ù %_âútàÕ·’q~uL_„ýзý–I¬°ëxÿ|»yÒqEÈ^Pé-ìw\Ç^ƒÀ[x ByA‡%†/è#>éC0¶„4J<ć" YB»$†Ò‡¡ý‡A®ð`‰Ý =…ƒv䌘,±z¡‡HÄG" Z‘ˆÊ’˜!ˆB8¼D£¬hÄG•8½ˆA8ñ±Ç6K¬ðzq'òÄ!Mœ‡N?ùâ¡ßx´Íx𙀲šØÏ:áöê8½(+e'‚÷$ÐJ­¤eì›°îtì^È“ }&CÞd”™’ŘʄuGؽ©H“Š|©È— Úä“z¤™±—G62®a÷Ž„ågC/¥4?Eü(¤#_äËün‘-ò‹EæDdžŒ9ˆ'ŸNä‰|+‘?$25äˆ| åÒœÖÂþéÉw|P¾k|-ø"ìÛÍÃÂ~È뇼þˆ÷Gz„ýÁwˆ@8 ‡‡¤À±ßôÁ[x ¯A—˜~èÁ6¶B („ˆE8ñ¡=<œ…!}Úxê1ñ±ǶH¬>„ãP^ʋó8¤‰;ÀEñH:ñà#¾—‡Óñ9ˆÏA|âsŸ‹ø\Äç"ž|sæ¶0.ùµ$Ÿ”dfÈåAßyÐetžy ‘y ‘ùÑŒ%EXÞùHGþ÷k°’žƒù·#sF~åÈ7\úHh€ùb+EùÐù(Ýè«ÐÌxT…(›ü—X…-ŒSXúUÈ_ˆz+D]ê¾¾h̦²÷ÃmüpûNv]ÙteË©Ñß '»­ìµ²ÓÊF+û<Ü6“]>-ÿ¿Ù#wµ»§³¹Ãç÷ÊÖžÊÎ*۪쩲¥dGÿG6S᳑}tµ‡Ã÷Ó]1Éþ¹Ú¼SÙ9eã†ÏûÉŽ‘íR¶IÙ$²G®vˆl«í!»s:{C¶f¸QseW†ÏûÉž([Bv£i»%£ ¡í¹Cçîàßÿ=Ðö<À³ íÖžHë‰öï…¶á¹¼¡#oðjF]ø Íù@ß`ÆmõCØo ã¹ú£~ý×H W´ƒüD| hfèuq F|pc¾†¢…¢žB{%þ*âÂÐß sødÆ[ ð4Nã$þGbì‹Bþ(Ð)Ïä/®˜Æìn‰µŠøX´Ñ"¤-‚œä®hc©&àBc¨&âYÊ,ω “Dk𑼆ñ?S »ÐJÅsÂî*#z]Œ‡J>{Òš”ü½j:h‘Ï. -%…ŒZ }“ž2ä-ß彌CWvx–(~Ëð%ßú42UãOÍ2îruÇGµeT¡¬z Óô 3•Þq®ý*ðDþjA§¦‡1Eé=}zw\= ²Ô‚Fhh±®™qE ?•ÞSn@\!ÍiAŸÊaÿ‘NŸŸŒqò:Äu òÿÊúÃuíñï´î8³æ8³æø?YsŒ•u{@âƒBÏn(Ë=ßF|ñßÿ=о< #Ð1¡ÿ˜ÐžLй aO„=öD^Oðá…þç…¶ê{cœðF¿ðFý{Cfô}3âÌ][q>(ßúóEœïX‰-ްâýöC^?äõGØ}Èa„>ù@ú@Ä"¸B⇃· ðDk ³Ä oÁr„àyH c†Ó0jÁwã…‡NÒ†¡M†Aþðd‰yÃA3òE,“ØàˆD8áÈ.Þ¢,œÂH¸RZc  Å€v âcÓÅC`,dŒEÙ±ˆKf¼é8ȇg¥øÆ#OüXƇŽãÁcäIÄ'@ž„ãŒOH_ð™ˆzH­$ÐIZÁX«IG%V8ž%CgÉ=<̦€N è–v1Vx*ò¥"M*ò¥"M*òO#ÁÃHÈA8ˆ#¡ï4ð@~ià+<–"m!ÒÞ[:Ò¥#]:ž¥ãYòŽWžÞáˆ×#_ÊÌÄ—|’”ƒ~ â3¡Ó,¤'<Ÿ¬&Æn-YÆCzÊ)EÐ9ùŸ-B=cì!ß Å›|}ƒ7òÝI~*ÉwIÉÆqÕz3»„ì3þ—B¥(«”l2d)#ûرå¿e—£Ürè¡ú²@Gäs„ü§U€OòVÚäÿ¬e“_³ ЭDþJ²³ gAºJЪ­JðE¾¹†ã…WA–*ÈR…|Uà§ üTŸjðSüÕà§Ïê׸`‰#ùgªEºZ<«EZògT‹ç俈pnëÆ2¾»>ÎÒçT{Š®ë e¿]×Ãmö©îÝv=¾æ ›|ª5‡Zo€gãNÙ]²·ÊÎ*ûªlëéîã¸ÚÒSÙP²Ÿ§Ú³û¯ÚÎÓÙÍág d+•$Ûxª5È©ÎÔ=²udç”mSvm¸=#[Ö=âÔk’Ó=¸Ú§SÙ¤SÙ¢SÙ eoNegh>´†ó¹¡þÝÐÜ ‹;ú‚;~=ÐV<ÈŽ îM-Ü=ñß4¼Ð‡¼Þ ²y£=z@SEØŒ°Ú¢èú ¿øâ׿~(Ëo¾Hçº÷Çü@ûÀÿÀhnÚA Á  … ý‡ ‡àY(Ò‡!}1h…C¿Åø>ÈÏq$ø+¯ä»¸|Då.øh<+¥q´Jñ?¿± KcÊ!¿å4¦ƒfd&ÿ˜¥ˆ«Äÿ*šçÓø*¡£ŠBž'‚ÏRÄ%‚^ ø ŸxÕ(# |–#_)Æ¢äFÆ´.ÁoÂÕHO>åj¡ïZè°|TBÏuHC¾äª!/ùº"¿›õ´¶€~Kh@ë”U…ôø,¥µ d°€†…Ö# Y™É“ã‹°/Â~Èë‡x?Ô½òú#ìÿþø€´ø"ý"ˆ´hw¨Ï ð¾‚ÀWÊ FÞ`ăï`ÔWÆ¥ð‚ô!ˆEúP¤E8 i‹ñ? ôÃ@? :‡NÂÑß»yøŠ@ÞÐŽ 0â#¡ÃHÐŽD8 ´¢ GÂQG#ôÑG£ìÐý„c ^bÁ[,äŽE|,ê,åÅ¡ü8ð‡4q9ùâA7åÆC×ñÐQò&€VÍ•¡ãD”•ˆp"h'¢~A+ ´’@‹|”'!œŒúL†,É'£¼äIAžÐMÝ”•Š<©È“ŠòÉWy-­[ ãrÈY4#¡ó4”†|iÈ—†giÈ—N6rÔv=dªƒþ2&užAÿQžFëè#a­f‚>a”f!mâ³@£¼·"Ð,?E(»ñE4¯@|1èÓܲƒ Ò” G\ âJÀù»¯@ú”K¾ÔK² (ïäë¼ º ßåä»å’ßërðDþ¬ËA£œÖ"à³üW€vhWàyÊ­ìa“A>+ÉŽÒºt*i~*¡JÈPª CÍQ Cx!¿ºUGÙôTƒòŸ[Msz†<5ÈSCsä©AžÈM¾EhÞÞjñ¼Ïki]BzFùµdÈ.ÑÇu¯qøÚdøÚÃÕÎ’¾ŽPë²u§[C¸žA¸Îß‡Û ×ó×y<Ù×qœÆïásòác²‹O7¿¦ñ’ÆIÿNH¡?Ý ŸÚ‡;êÖ¿4OFÝxCÞ¨3tlF{0ã™þû _þ#>õBkcÐE›ƒîÃÐVÂQ÷áà-2ãY4äŒFÚrêã´½j—K]äΚa;´yÈÈC×<äçÑžyðσW>åò)W€½èR€®ø]å çô¿ˆóEÐA_ÿ"øcC1z”À»„k%\+A÷t÷Á߇®>èKá[J{•¢H)ú•q½ ™eЖC[m9çË©ÿ úCÅŒ*¡©„¦=*¡©¢M«Ñ£;«±·ÕȨFF |jàSŸøÔÀ§5ȨƒO4uÐÔASM4õÔ_4 Ð4@Ó@}4 »9ÈiÂÆ&x4Á#ÿe” ` å[ÑB´Ìép¥šVÊ´s®šøwÐ>ØÐìh‚ð Â3ˆŒ.xv¡W<»àÙKÝõbg/ô½Ð÷¡w´}Ø?HÙAêwý•Œ»åïc»¼pùÜò;¹¿?Ε5À¯Üþ)ü_áÿ ÿWø¿ÂÿÕfŸ<þ¯ð…ÿ+ü_áÿ ÿWëÌ>ü_áÿ ÿWø¿Âÿþ¯7k ñ…ÿ+ü_áÿ ÿWø¿:V¯'Qø¿Âÿþ¯ð…ÿ+ü_m6ï ñ…ÿ+ü_áÿ ÿWø¿:ÓÌùâÿ ÿWø¿Âÿþ¯ðõ>Ããÿ ÿWø¿Âÿþ¯ðe™±.þ¯ð…ÿ+ü_áÿ ÿWiÿWø¿Âÿþ¯ð…ÿ«\ƒ!€ÿ+ü_áÿ ÿWø¿ÂÿU‰Þ«ªð…ÿ+ü_áÿ ÿWø¿ª6{ ð…ÿ+ü_áÿ ÿWø¿Âÿíõ™ø¿Âÿþ¯ð…ÿ+ü_ušùnü_áÿ ÿWø¿Âÿþ¯ð{þÿWø¿Âÿþ¯ð…ÿ+ü߯;Âÿþ¯ð…ÿ+ü_áÿ ÿ·ñð…ÿ+ü_áÿ ÿWø¿Âÿíý[ø¿Âÿþ¯ð…ÿ+ü_áÿ²W@áÿ ÿWø¿Âÿþ¯ð…ÿÛkOñ…ÿ+ü_áÿ ÿWø¿ÂÿíwÅø¿Âÿþ¯ð…ÿ+ü_áÿ2ׯð…ÿ+ü_áÿ ÿWø¿Âÿí¹ü_áÿ ÿWø¿Âÿþ¯ð‰sþ¯ð…ÿ+ü_áÿ ÿWø¿…ÿ+ü_áÿ ÿWø¿Âÿ7Bá÷òUøµÂ§>-ki>­ði…O+ñiyŽW,î}ó®X|¾ÉµîvÁ¼³šu·sfï[ÀÌ+­7¸#fßÛz³ævܵævHãNØóJñgjÒ¬½ò˜u·f_†Ç¼ Ÿq½ ™u·±æ=ǘÙ÷çš[Z0knG¢ö¼›õYñQøR^3§4gö» ÞĬžW²ãÄI³ßͯ÷~Dð¥üf¿ÛœY·å7û«§ ¾”Çõ.|ÎìyóŒ©i3¯ä]|n¯¹3ûDÆÍ¾·xæÄ¬kNiÔÌ'Ŭ‰Qƒ5c°¥F4Ö„=Ÿ´EÏ'½k"ÞÌ)¬‰õæ]xЬµ2øRñobܬµÝd°¥¶ëu¶6ÞDœÙë6¬ñ&"ëlƒWjÆ…5áuaMÌ™5¶ƒ31æzÿí1xRccÂÆ—ð©íCJðl WÂ…!5cp%bÿÆ=m~½žV†b²§MÞ= ®„)±ÅµžvXcJØxó?*Îìg ˜µ´£fíÜq£d¿¹¬£õNë}/×ËÞYû&óM‘wÙqú“5©×ÊÚók›Ìû¯Yû7¦ß{Ëú<{ïςƧü y—'ïëäýšÌ½ÉšY slö~þ½n/7Nï Ìuí Z¯ç:ä=˜ÌÇÙói#zMööËÞ|Ù×gÏùô;¸´)ý._ÞqÉ»|ûÜ„žÓ°ßßo2sc>3Ðk÷GCöêɾ<{¼^ï’9yO'{‘’g4€½p…ž§HžÕó'Ésú]X¦ß¼W›Òs$Éè(ùE’±3 ý“Ð' IÐ%q.9ÉØ^O?çS(›‚~)ðLAn x”r¾”ö*EÏRô+ãzº•A[m94åœ/§þ+èèT ¿Jh*¡©D~å˜;ªÑ£;«±·ÕȨFF |jàSŸøÔÀ§5ȨƒO4uÐÔASM4õÔ_4 Ð4@Ó@}4Ìê¦9MСi‚G;öö¢S9hzùnGF/õß]¯œCF>!dô¢wˆë!xtQ_!xôòéC—>Ÿmá1H}R>²¦TâXg^WbUy(EÇ©îUâR‰I}§ÑïLßIl)޳ö‹GºcH‰±#²v]bA‰%ö“Îä¬S—ω휸.:žsÇqÎúêìmß7:qYôu‰½$îrÖ„HL厧$~¢½ìØ)z*meÇBÎzt'öq¯ûp°_$ÆqÖ¡;ñŒ;†‘ö”¸…˜eÉXŧHŒ"q‰Ä"N âŽ7bWèÉ+$¦pÇoGüµ˜AâwŒ c÷;c}gœïŒññ½Œé±<¾cãeüî·Ë8û˜=öþgo/5Öv³Ýclg|팭é/ö¸ZÆÔÑãibhE ­ˆŸ±³¢ï(úŽ=¶–qõ>íÏ6Иy;oöLl6¿Þ—fãBÌ:Ñ $kBeÏ™ìC¶÷Äi,UÁOµ÷’y –ÚœkÙ„Yß™ qÔHðRí}cs.ŒÔ)³_lt/ÍÆÚnö‚ùõûMwa^c,Ø8¦s ÈÆ,õ™wp›ôZAÁM°÷lÑ{ l¼²Á) „-(q–ÏŒ^){ åÙ¼ oE²&PöVÉ»·$ø&Åêµ²__°´Òú¶'ûþeͤ¬¥IG×´aá“«ñ|ä]Hš<ãÑ%kZ¯·IBg/¶§cw&åRÑ'“rÙ|gKyì,DN:ßÙÔo6´ÅðÌæ\66dsœÉ¹\Ê—Q¾s|ò‡ôT].2ªD:yÑ?ŸòUèVˆÉÈ­BnÇe”kÆöfd4cO3uÚÆ¹29‡¬6η!§km\«ÂÎføTÁ£‰ºoƒG¼Û¨§6tm“ë|üØÙIùNt¨¥­Úä7ü @ÇfꬺïAÿôï‰7·nøu«þðëAþ€|Ãg€s=ØØ}çë±­…>V+ÏZ¾›|z*°†ãÚ¼]ž³èØ@ý¶Èsêä|œ-›ä‹>íÈnáÓ€.AÊ÷`O 6·À/m;ÇMèÕC…„žóí0èÁÎ 4-ð Oê,(Ïbxõ k;|z°³Et¦Bð JY¡^÷Èó™Alê…Gˆ²!tê¥îBØÂ?CðAD‡ uÖ…Ì^ä÷"¿ÚAì2~.ËóÓËóÓËóÓËóÓËóÓËóÓÿÈüôÿ•yiüÞKã÷öx¿Œ©ñ{{\ßÛck™»–ñµÌ_Ëûíæ°åYÒ÷ÛoüÆÎy³—ËgÖܯÐk†dÝ½ì ¶÷sÅ™ý\S.ì¶1ƒëZ‡cörmרmöz#ÁNž2ëò½f?פÙGì5kôgÍÚH¯ÁŽ˜1ØžlãFl1¹&ÌÞbŸÁm›wí1ž4k÷=ÿsÎ`&û fò¼ÙsÒX oÀLž6{¹fßñ¬Ál ¼ˆy³—+àÂlÛdÖEëu‘ö~®8ƒo4ª×Ikp“‡õÚH{_r¼Á˜0k# nÛ¸Yk0“Ç ^r\^Û¦(¼¶/9`ðÚf ^[‚É0nðÚb]ë"Ç ^DŒÁl2xóz]¤™<¬÷9Û8Jñ+bÄ൭wåFÂjó¹°ÚæM^¯Ái›p­ôœäqƒ±Éà# 6ÛØ"6›É¿6òì;Ä„˜£bAc²ãwå?Ñxl²÷AðM0 d?…à!Ë>4;ÿÁ˜É}0¯ñ Y°ØÞ÷`Rc±EòŰ‚&×Á˜Éq0gö±Åšµ¡>½g]ö¦ ž•`Å ¾ Im\å½ÅÞ{2¬÷¸ÙëEÇô6Á¡•a«`É Ýy£zÿ¹àáÉ>sÙw"ë_e?›½omÈìW›Ô{Ñë"5AcÒÊ^tÁ„–ý(²DÖzæë=%öþ‘9³Æ3Vï=Ûê×û̇V0emì¹i½ÿDöžÈ>vÁ™•5µ²×Oö°ÉZ<“vÎì-¡®2}f= |ʼzOˆ`×Ùû>à÷þy½^/‘²I蟄>IØ^]:”цeÐdÊ7r* -£\m\F]ù8nBçnø”q­Ú<Ú)•ï4ê:zððÛ<«8ç.ò™ð­B× ®g +þ~®gÂ'ú&úC=¿ý”ÉÅ?4Íð+ÄÆÂY=ÔÏAv×r ÏA—\däÂ'ýri«\hê¡÷"#ò¨ƒ<äçÑÆy¢+ת¤-¨‹|¾ (W@?/ÀÞbÚ¥sEÔOºS¾ú"ô.‚ü‹gu8Qﮕ@Wÿêׇ |}è\J{•Ò^eœ/›Ñ!G94åД£o9õ^¼ ô©D‡JÊVbS%¼*¡©F‡jt¨Æ¾j쬆w5¼«á]ŸøÔÀ§>53:li§\Ĩð¨“chê ©ƒ¦žzk€¦šháßÿ 2[(߄̦æà l»´M ¶·`{ ×[¨ŸVh[)ÓJ½µs®þØÛ Èï@F4Ax§uXÔ…ü.xvÁ³‹k½ÈìE¿Þ&õAÛ‡.}è=ïAÊ ÊØCâaùsbc‰‹£1]œEêÑŽ—Š{X=Þã:8çî=–»¢Ó›âT÷¾J7côÚhlÄ¢Òa–Â8wâÊ¥bÊà ]qKºçÂ%†tÇ‹Î:kê3ºcCÚïM묩ÿ%ñZ¬g¯¤Äv´a$®[*Žs㣻÷Lºc6‰Ó$6sÇe“¹ã1÷|¹3W.1–£Åà3Fb%‰“Üñ‘ÄDN<äÄBIü#q×H #ñ‹»HŒ"±‰“H<òVqˆÄ 8±‡ÄN<ážÿvÇ îXvĸã'q¿3Î_ž _œ ]±ˆé0û”üzŸÒÊ©`c· ŒüYƒÍ2{‘Ö›œd^³¦ßgö¾N¼„ƒÑ3kòLêüb‚{`㌌ü8i|h‚Áã™XÄÉ·÷  vZÐ`ðÄk Éfã£Íš`+4æŽ`äÛù¿&Lί8ƒA<îÂõŒ]Äö´ó}$èܲwEöØË~Á1LOÁ\[Áã¹xTïÇ‘[àæ ¾Žàé$Îi\}Á NŠÑ¹5d¯Ž½—>ÆäÛŠÕX¸éòWÚv½oVö!¤óŽ^éCz?«ìO•}¯²‡ }XcHάôÉ)ûYÐeMè=¬ÉrÌõ,ìIBçTtËš×SzÙ´S6²}ÈÍF×llÌGV6:dSÙðΖñe 9öq®ݪàSM¡ŒYQÅù*®çs-Û åƒÜFtmöèÛo¶s®Ý›¡kF×fxW@ÓLùnÚªóÑÛ¾UØ×-õ3úÖ܃®=ðhæZút"¯™ëÍе!§ ý;áÕ oŸNøô`O'|zÐuÙô³t€g2  .ÌkR¾[ÖëÇFßMÐ5 ³†ãd¶ói ÿ6P‡-ØÝD]ÕÉyy¾S® šÝŽÜ> è¤|<ש³ø¡mç¸ û{Ð!$ôœo§^zÐ/M |C“º Ò!xõ`G;|zй9=è‚OPÊ ½ð êéÐ^då¹eCèÔ‹Ý!ê Dý…à‚>ˆAê° ™½ÈïE~´ƒØ;½’y9ù[ž/_ž/_ž/_ž/_ž/_ž/_^Ïý??o.ϼí+"ø™v‹ &ÇpÀì³19‡\˜ñS`Æ…·-ÔA uЋŽAôk…¦Ut€Gû¬‰:°»ƒëÈî@F4Ax‘ßÏ.äwÁ³ ž]\ë…O/úõBßÇõ>hû°©½á=H¹AcH\)îùs'N–‡§ÄÈV¡KìÆ%tÏ“KLë^/ÃJìúvx„ôÁH\*±(ýÇŽC£cP‰9xSÀWJ<¹Þ ;†tãš;1¡{n\b¿hü@‰ë$¦scÒÖoÀ”Žz`˜»ãµ¥°¥þn‰I ¶Ô¼¸;ßs9ñ–k¹ã+wl%1•O™8ÊŽ¡ÜëËé‘Xɉ‘œøÈ‰ðµH\t”ç-I ½îœ~ù†˜ç­â÷<<}ÇŽsœõçN|ãŽkœ˜Eâ•·ŠU$N‘ʼnO–Z“‹àCoƒÐ¯"±ÇRqÇR1‡ÄËqÆ?gÈ gXûƒÝF“&¯9í¸ú¬Ünp0g Fÿ¼É8jò’#sMœÎCnç Ÿ7yº¦M¾•É·²Éà\Ü¿Áâ¬c¯É2«s‡ÛXÆ“&Wø¾%J®.ÉUhc[ŽëÜ„6žå”ÉAè5yC:ß·ëdHãíKŽÁ©Ì}ÉÇ%ùoc' ®ð&',ùº%‡—ä$9'Açœ}ÁØO¢|Ò&“odJcä ö–`Q 6¦äòK÷j¬cÉ'"øC’Or‡lÕXEI\KÚ¢q%?H°ŸR°r/žÕxBYc&G6ß¹üNâ8‹òÉðÏšÕù÷²xVf-h¬Ê3Ñ'ú$¾SáŸF»doÒ¹÷¼”÷R>úL®e";z˧l>÷þLè³9N›ÔX¢Ùè’.vHylɧ®¼ðÉF—b|Ë­Ÿã*ÎWa{>çòG5>pmW8ªñ7 'ôc¨>Å£S«v+†¶ šù]%ÇÈ÷S¶ ¾e<ËÚ8ïƒo3uåCFßmü®‚O›s.‰²mèÙß&ìlæw3×k±5ExpÜMÝ4!¯ iF§N¾3¨ƒ6t@ïôm¦ÿ´a_ßY”oBŸ&xt»>íÔk¿øm'´í´û€Ø‡ŒtÞÈîœÓÝù†w:¡_Ñž~-¢½K -O 'JС>³A_ŠœRÊ–q® å”-§l9íR¯rì¬@Vr+é•”¯D~%|*¡©FV5úWcs5ò«eÜ_?6ÔÀ§>5ð©¡Þk(_ƒŒZdÔASM4uÐÔASM=íÔM4 Èn@võÔˆœF©'x4Á#ü”  €ºjAF ö¶À«}[¡i¥L+}ªšøwPߨÐìdt@„g]ðìBF<»àÙŵ^ìì…g/ô}Øßmö÷¡÷ ǃò-ãyË_¼ë8A?/ì¿å÷Ëï9–ßsüó¿çˆ~Çñ¯ü~Ãy·ñÏþ^ã¼ÓøW}Ÿ!}×c|þj¿›yS&y¼ÁÀœ6¹©Ã•ð[ÉïU1ïwØäBŒ7ùI&\Øòè¶zÆàfúM~’i“ ÑoòãΠ̀ÆÑ´ñ~é+kü&ù¼ÉGî7Øó &â+?‰Gçѵó“ kmPç&‰`ý¢ÇºƒÏ0¹IV˜ü$!ƒKÏïC}ç÷ 9GL~ŽÒ˜õv.­xƒóKÙÈÙè18¿®ˆ“‡|Ú…õ;¤ó Ú9{·˜\ä”9bÆ…÷;¬ñ~í<ˆ>£Drdn»qHç S~’y“›dÈä'™3ù½&ù¸ÉOëÊ8¥ó&Ã'1^cæË#7eØ•£„:8~Z?æí䓟dÆ…õ2X¿´ßI±&ÿø°É}8¿Dn’i=L°s Þï´6Äm‰Êy¸°˜s$ yžq¯Xðù+Upˆ%/Š;׈ä“\åçÏêüÃ2¤pòÄS?ñÃY°ûSßÉÉ «ó‚H>Év¡Gç!Þ ÍÖ1¬dZãèKþ0G5^ç¹Ø§s‚]Òyd¸"x¨vÞ0ÊY :w˜å×øú “_?aVç,–aà´&B›HÝÉ8™k>‰Ø• ]-:&cGºÔÂ3½Rà“Ÿʥ̛é2ê ^©”OC×4‰«Ð1 ÓÐ1 =Ô™éØžŽŽÅ”O‡g:zf@ŸÁõä–Ã7“k9ÈÈ„_<²à‘¬íz•Ee¡stYè”#´è’³ ‡]¹Ð{ÑßK?ð¢eðöòí×ì>ê6€Œ>ÎçSO…È*D÷zäup® ±¡ßÅœ/F×bt-¦l1¼‹á] oöø õQÆÇõR‰O óQ®”º*EÇZÊ—¢c#z—¢c4òAÏ ê¢ÞÐV@Sá×ÿ*®WÁ·ŸëU\¯âº]ü\÷#Ûl?üüÔƒ»j©Ÿ~tî¦\­üÆÎZä×bc=×ê)_Oùz®Õs­žk7rÜÈq€úk•cê¿]›‘ßO}7£c3z4c_³Ä$Ø×Oý´ÊÛZ)ÛŠMm”oÛ¤‡Ðt¢w'4èÞ ]'zub_7úvÓŸú‘Ó ¯ndw£cÿ¼’@;ítÐ @3€Íƒ2V—1‘ü-“8±ˆÄ!îØãíÖbKl!1…OD¯Ã^*nx«ùe'FˆŽ –ŠœÀÿK‡|»\7ÎøÞÛ;ãøèñ»3fwÆë2N;ìkg =öŽo/5Æ^ '[ÆÓmm¶3fvÆÊѹlœ±ñRx&Ñã`÷ø×Yç=ÿìŒy£æŸß0Î] ãDƵÑë¹ÝsÑ2Ž]j.Ú¯ºñOd N4þ‰3.uƤÑóÎÑãϥƜK­¡qÆ™+Þ<]çS:ãÉ¥°Qœõá!3^¼ÑŒã¤êúµÇÚ2.¤íö£Ïíǽ`%²WQfÕˆÎQº;VÓßVÏ2&‹såŒãÚÐHû­á÷š gA¦ìüÓ³&ï4<×B·6dò*Àk×ÖMÎyûtþ„sý&'é¨Î™ yÚì< ðÙ€Ž×ë<=’‹T0ó%ÏàæKN/ɽ=Žô›¼?#:Ï×{hÓ÷ c+¼ŽÚ®ñù†L1tIØnr>Oè¤ÉðI„w²Gc‘'Ã?^©è“º ±ÐSÆô-BryF4Ö²Oãýg S¶d iìÿ„açïáZ ¿3ä™Ç¹ h<ÈÜŠ|/~ô¨åzŽ>}#úvڇݥØÛ'ü9îCŸ.yÎcCzö!§‹OmßðïâÓˆÝ~y>!£yµ0«¥´ÒÝÈjåzÝòLæw#òú9îFÏy~as?vuÀ³CÊɳRÊ`_òú±±½ú‘Ñ*Ï?dt#£ŸºïFN?rú©Û~)ƒ]ý:§Å)ðôêXËþ[êÙ÷¿a.nyîŸnyþí_ñÿ¯5ÆÿLópÿ¯Öÿ½sqï<ÜÛÍÁ½“ù7ŸñÕS÷Rž¾¿õ¼|÷›59æ±{%÷‚•\[EßY%ã¹Q“8ÁäÙš4y¶hƒÕ”]À3ꀀɳ5còlQ1ø ´"ó@~ˆâkø½†ßkെëñû è¢üÁ1‹9êí\[^£þt\‹œµè°¿_‡~븶ÖQn=ýq}Èä׊1ùµ†tžœC×ë<9ç\ùë½&¿òÖöëüZv^`~o@ælÙH½l¤ìÆa“_k…Éeþ‡C8e6Á“ÇäØšÔÑ#eŽ Ì³:ÿΑ2Æ¤ŽŽœ‰Ê±…NGÅè|÷Gñû¨iWn­a_Kò“°˜[ëØXSxÛŒÉ ›á³™úÙ<¢s~mæ\çââMàX† Nî¬øÎó{öœÎ]|>¶œ=¯sWž;»˜7ëüiß÷|Ê­ï‹Ñ9–%gèR]¼+ ©€¾‚²U”©‚o׫¸Þ-± çR/”ñ#ßü*Ïcc7òk©£ZÎÕò»›k±¹›kÑ£›rõ\«—X‡óõœ¯ç|#Ç7Ê1º6Ò¶“,è¡dÇÍèÑ ÿnøwp¾•:l¥l«Ø5¯‡™mèÛF¹NÚ Sb‰EàÕÌNdw‹Žô·n‰)°¡[tâz?ü €f°u€òÔÅvÒö˜Sþ¢c'Ö#:®X*žpâw¬ðNçÚDŸ·›gsð¢çל±¼3wÆÚîù0·.5V•1êß‚1ð׿²dlùVóVQsVo˜“Šë­qÅœ9¥·›OZjÍâß2÷ZñÆy¿®[7ÚAÖ îG½í‡+é[«ÆLÎgÚcúÝjüw5çWcÓès@Ð䳃ïùLéõ úÿÁôŸƒáw0eáûx®…f-|ÖR—ë೎këñsáû.úî¡ô­Cý:dz÷^‡yuž÷ ^ÙÉÝ.y%/âÔßVxl ™\qS:Ç] üO€GödA—\~–è19çЧ€2g`g†”•g zdP. ]ŠðÇDt)¢l":”Èox '™%è_.eñÓ¹wBëÇn/u釾ºZ®ÕRG^äP¾~¹G {£ÜW±«–k^ÎûùîC.xvÁ«TŽ‘Y ïFäÖR¶Kx »ÚÖ5û©ƒzê£Ý”é—{&üû¹ÞÍùn)‡ýÈí‡÷‰ð;…z?úÿ.úmÂsò¼à ùÉW6vŸuŶ×ûÖgÝlMwÝJÜòkù=ùDé ÛŸ~ß¶ÇîÙ¾/ð»hNáñ;ozuÊ/¬Óü§[âÇ3Ã=w}ùcGܺÑ:ß“ð‹®Ï!Ço˹ákáŸe=èµVi>Öôå'|ÿÚgî°&w~°¶õÌÂã“ûo;£öë½']ù‡‡ùÓpÏÆÊ >ý_W@?dÓ¿kÍ»/ÿ¶5}Å×ÏMz¬Õš¼öÆÓŸøQuxüá^™;%#ÜsÕ=±ü<™ò£vù«}âï7OŸbM_wâA¡•ÖdǾƒo?»#<~÷¾õµ9áÞ[÷¢Q6å'íò=ìþGžðb=\²åÑÕ_¼ÓZsþ3¿9*é k²0w_ûñ‰Û~rjO×úg Ãã7œtֻ沶ÔfïýÝW^ ÷¾pÁ{ùãEð›µù]yúEŸûmøNkzèXáhM¾oÕàAW~Æ©ÇðøÀ 9'?6dÅýªä†ü¹pÏ_YšÜß:J×cÂs5výZ¬|¥¨"ÿWÖôÇÏ¿.&5ÖšÜð‡ªƒr 7]³¢à™ÇÃÝW 'Î5vc]Uo¸íCÞpüm‹ýµF÷‡À©ŸÙòÍCž·¦ŸyàÔ™#Ÿ¶¾yðg¥fëOëm£ê¯\{ø‘.ýuû×Ì?”uäÕ[Ó/½|æƒ ­Öá7~ÿ‚×_gí¾+|ϵ6‡ÇS~RÔ•þøK÷×^³;á¹ZÝÞywþä´Ëg?é‡{ö¿ôð=ze¤î®MÿÂ9üpÛÏ+Î*ÝV}_xܳq~õµ÷/¶ãϾ÷éëo«„ŸÝ‰ûïRÿÞq¦µgÓX{×KµÖî£_x-÷Õô.ö¦Û·†»¼û¾ÓóF(o·w¸*·¿zæ?î°öØîy¶µëÙ¾ê×ÚK·Í:vŸ6·kã-'†»þl}ù©W?ݾáú“_ëºú#OX{NÿÒBmÇ[»vœõÄ¡£¿]ÔSÔœø†K?»ÃMß[vÓPºµç¼ËšÖžþîˆÝ».oœkk¸rÛ³Ž¿Ä|øÀð-uN{†»Þ󋘇þ°ØÏjí~nÞGÔxÏU‘~°ÇšžÆU"¿weøëkpWD¯óvCD×ߢÖÚý!ÜzsãŸÎ©<ÉÚ“û1®híZó‹U7œ·¹ìü^âŽ=ˆvû‡;&ò~óác&Û±6þ§·)â߸/ÿ]É<Þù…ÑmZ1bú/öÃ:}uü bOÏä¯^wv¤_£ðˆŠ©’—"÷•W¸ßS+ö:ú‡ƒ³ÇvòkÝÖ±Zøê~áÈÛsUÍÅgݯõõŸÝóÙ' ïüÔcs^±;Üyð¹ŒÝQAyÝ/ºG¶/Ô|ùkϧ§¯¿´µÑúzñëlxº!¼óÁ†–Ýû^uüˆòº?ô­fÝÿã—[>0uôb½ï;uuÝw¼Ž^áÖoûêSNxzÝ/L¿pžC{w¦ãÈýç~ÿw&ÿ׿í»NýÏ^ýâ×nßn?lß®GË>²Ø/êt¿ø€yìyè--§nÝ¿ªúïÚŒøóÎðÃ…çºpÇÇýC=%¿„N·û¥»¾Ò|dß"þ·çIOËÞ'Ÿ »G.©ÛüBxçW.{÷iE/:÷O§ý]õ®ûŇÎýÍM‡·ÿÌÚ³ð‡žU+oŽØ7±½û”_ðéÅvܱ+áÚ7R?^z{î÷’ûe½îý·óN½2ÞÚ{pÏW¿›¶Úš(?å=ºè#á·ÿàsW<{òâ}©^·ûUæ~¾Ww kâÌcö\RõJxçГçt]îü¼šh|v+åu»_Ó~Ìúk¬mÖÞ¸ìW÷mXé¿÷½þâ1Õ‘ûÂ΄/~ô²gŸ wšþéÇõº?|¼ã7Á“÷û®µ÷¼_%~fæÊˆÝ÷=x|ÎÓ5'.Ú½¡æúîûi¸ýÒ°çòGn€^÷‡á3fyOWÄŸö¦¼üŸþòh¤ø†»¾ŸÞ¹ÿm1Ïÿg¦µYßoÃ-r{»ñ‹ðÑýàú÷>¼k®`kÄŸö}$9ß DøÞ÷Þ§Uþ­¬Å~±"ð³ÝÑeiÚ!°»qó%)߇Ÿî7¼ïêñ©«=Ö^=ˆèsï“w¦V]u|¤îøãE;õRD¯¦;¾õâöûC®öÔýãk/ùŠ/]dííüÄy{ÓŸµî½æÅ¦Ó¶'‡wüî/Oí>"ÜxË ÷}ꌞkÐíÿÉó'þtçYÖ^3žºwë`þÞsÞñRvËÆcrà ýW}oÓîFÊëöÿÔƒ×UĽüÐb=ꊵîyþ’ÎÝ›/ïøó©;Né¹12žk8dǶ¶¬ÒÅqAƒî7¾?#û´Ó^‹Ø»÷æ/í+øã¬{¾üøÈkÍ›·M:õ“z,]*rm”ÑfcÞ¢_4èþ1ò½ŽüîÑç­½·¾zë©ÏfÝÓô•Š_´!¼óÈϳênëTsß7õïÜÿ ×ýã¦m“gž9ÒkíÝq[ÚÌþ¿Œ´ï=[ž¾ùÉæ?-Þ'Nn½õSŸ´gËÕß~€GÄbmÐýäæÞÃ!NZ{m¶+¬{öûêWŸ{ìGŸÅ~Õ ûÁ-?<ÊwRùJk¯Üî±îþé×ÿ|_xÇË¿úÎ…»ÃÍ·Ú7Êëvþ\Í^ï½ÍÚûâšÿ<ß{Œu÷}íý'ņw<øÃÔôáöpཻZ.û¯„çu;®|á‹Þ~«õ轎=nÝ}ÝDgÚ½¡H?ÝqÝøAüòhë,=~6Ø7èu»Š{ü0ÒîÊhùÝE‘ö»»ì[2ò_ì¯-¯ŠEÖYú>ØÃ‡:ëxíÿðÕýà wÜôÚÑ·Ýi=Š']ù²u÷¡ÿÍÞyÀeul‹žmÅŽìŸØ@©"Šn4¬`ÇŽ…€h,‘tcQcH'VT0Xbˆšl4”“˜Ä®”t5ÍÄSdËù^&‰ŒÏ§Ûë€[÷µ35‡-çÉ’þÛ:v \š«øóu$þ{½ø³¸oÞlì…š÷¿ÕÚHNø¸uRÏ“”—tßöôà_k·ˆÒOz·¦Æzq‰w˜ïÂ(‡OÃõU_?œbÙá&K:oÔ®Ö »«ô“}YwèÅL>·W{=͙θ«eǵ)/é»ý­…ùQ#ëøAñÕÉxa`¯×•½nwÑÙà33]ÃôH5ÞIܹSÎZÏOÒGÿ®‹o:ÓžNξ2£Åv;ýÜOû¶l»ÐÊîOÑ‘)åǾØlÑߘ+ÇÓ™S$?ìØ²/-î‡Þ¶<œL?ß,¸è6ý¹ÑSâºí-5ŠJ¿áõ¶¿½>™\´ðùpÍÑ7)’/v˜f{Gý¤9ýŽÓ÷>úÝ/…Eot}¥â‹;ÙOVeþt×ò’/vŽßúUïµõ÷É'Þ;iÔÛlãQt¾öãKÚãÓG˜ý‡ß ¾¤ÿÎOÞÇ«ñýäÁ‡"?¿ç‚^Ô9°çþðt£èÊýË¿¸Å˜ÝÑ4<)/é_·ÿò;/œÔO¾U xL/\ý™gÀFÑwö|ù­B#,R^ßHyIï]¦NÛì÷Ž~ò£a{[¬øHßSqç™C½/8rÒ$êÀg™‘z_¥‡—]»â¡$GÞR$ìºu嬶ߎpÆ÷—z ^|ðv›n{&sOq{ÛþßÛ~gñ‡7Øü?[ÎÃŽš"ùa×î­E>ÌÒO55Vß}eÜ··»fìUûs_öý¬¢å[1WæK:ïªlšŽŠ°åî”ÿÅôéÑߨü¹{}PÏ3_³íj”·°Ðm}›4*oVŸ69ü3_Ò}w°i(駘® w´¹@uú%ñ²úa$=¿ôÊäg~Í—|±ÛT‹ýôSª½]×ó…eaìVqÇ cžÄ—ò’vòù«¯=:F?5áÚòiw„è»öç|Ð}‡Q$¦å„Ÿyt¾äƒÝßÕèÓ¿kš~Jí‡ìšürX‘#çÖˆ™Ü¶[æŠfË¿ræƒù’?vïY7ý³ƒ]ôS‹?èøý§Îºªà\ãÎÏ…î3Ф½oÌ•rC=É»‡GÝÿC}?ýÔý‚ ôµQTøMÕµ·sScš^ÛòŠÎ7úü «ÛðO™fép{œwf•Ï^|ï³¶(2ͬLÛ>T|àðeªâ‡SŸn½«{²Í—§^íõÖä÷Ùò¸cë“cÏ¿ÜÝ(*ÍkÐŵDïyï¢[vgµðÓ»XtH•|°ëÅÆ»>ÿæýTå_†…ÄDêÛß>ÚsÓ¹£kÓç‹cŽ5~©’Îr™ãÒOÝ0 èÛµÖµ¼ò…±·F§A‹¿hm̶ø"UÒy׌1X&ïè§[ EzNß6¬í°»;{{;ƒøM#QÙͪßÔ“ôÞ…Ñòa]ýt÷qX IúÖ}#r‡Õ¿ÃØknç £œ’w¡–Ýc¯KN‹m²ŽÏØé­æ6E¤±×Ç4ô~Ò®0fß÷Eé¢Ö/GÉûÆUܯŸ–v‹=¾[Äìã=Ø–‡¢ï¼ƒ®î¯Ç46 $c.VLè¶—Ýè$éÏbMlè§çtþèÞ¼p›>[¶ÎÿýÚÅÚß¾$‚µôþŠNóÄ6äˇb®,ôÞÓæÚ³£¾dÛ9§—}¿}Ûü3~Óï¨?é o£fýÅcžXþÞ³–ú’¾{èÕÚwÓôÓ™ AßÒèÆ·wßwØá»ñ-_T«T9²«eß|c®Ü‡¤¾¤w!ÖxóM¡ú飂ïZ9Bv»ïܳ­ŠŒ¢ð‚„ìèsú@eoªý.êIº5žTËgc¦çiÁ-“7êÏvn7ïð¦£Îxvýúĸ#íö#¦'W~Ò8’ŠbµŸÇÖ:eßéÓ?>Òæ[®ò¼sÊG×¶;ãé÷à5Ÿsè<˲?H~)2—9ÓõÓ?Š…ÐO¶<>#÷oœq‰¾çÉ7v²õ»âSàH~):\Üü)‰vÿδ~·î¬ÅØx>}[BÙ7ßqú9ý×Âs—èj^¶õÜÉ/{þý½ô3}¯=8¡Ï‹úSž'Ÿ9óŠQt‡ÐŒ}ŒÙ½k]ñ`˘+ %_ìfQÿŸõ3ã~ï6iO…þÄ‘í3ÚE¾imI¿7xÊ:GYò¸Pòƒ¥ÿÎ,ºsÓã“ôÇ—veO­®N¿wy ¸âzBïÿíÅ'Gï2f[ûu %?aEú>ü›=ngÄòwØ×úc3½?¹i¼Ó߃ït­ëº×Æc®¥*þ؇´ÖÏl{IŒ¤ž'vU†Î²çA[ßH{ŒzŠ„zû}ý̱v<ÐÆÆãÑMsß;ÚÆé‡K&×úã–O‡GñAàÛ-’.mpèÇdKKvzó‘f¹]W=lñ•¥G,ûÔ±kJ~(¼ |½~æ·—VºBßÓ¹ì3ýï|£hÏ3Ÿˆö¤œ¤sáî’íïN¤Ÿ•óŸ¾éÚÂ_7nµì¤Û$} ï3£~ÖÜnªoÜ»'û‘oš;ã{ïWÞ_>çe¯ÏnÇ%w^²Ö=À‘ô.̺üÓ.Ý_?;?XPÜæÏ ­ †íÚϱ;$í*¨µÙHÖpðqÇ^¸MÍbu;í”~öîç/¾®·Ðs£'gÏ} ;/Å<€1æI~¡¼¤o¡¹MgËéÙ|1#¿aÏcŽj5¥î¹fFÑý=ùöjcÄèÚÌ™Go“ô.l) ŠŸm¼Ïªõ¸µÿyߥš÷_ïÿ¹¥·íuŸÚ·Ž¤÷ž‚!;ég/Šn¯÷îÚ÷PaׇŒ¢/Ïië3³-;žz’®{”^<{½ïÁ-aOÚí®¹œ±5¦ÁýÆÞz»÷®¨DË.¢ž¤óŒ~®¥‰ˆž½`YÚ3ÛG9ë>±«Vs‰³^X¤æûÖæF­¿Î‰S¦Qmm|3WݶñŽqV»¶~·åd‘²ï6~Ÿ»çàý\ÜóÉdßâÁ'‡$7›i}“åƒhØçOö¾Ò"eω٥{s[ÎI{É–·änõº«~{ÿÏší}2y~±c™ë[{Þ/íà(»@hñî¿êçÄ1cËôØ5®}ÏJ£(¦ËèçÖ©‰''ÕûƒòÊ>ð«zó܇Cõs/\nòKú0c ”gžè˜ÈÐF:û‹$Ý »ýðw5ìþž{#{Çó X/H½ïà[óÜ® Jõ[¤Þ4’o çÀ‘|°3åÉg8=Çîï¹ó×+WŒYiÑÃXøÇ—uG,ooËa¡LëÑrÿÓHÛ¡çnÄ\Y,ùc«×ò;oèç~¸ï÷;o52'üvþÌØŒyï¾Úþý-)'鿽]ëþ‘)ûõórÈâ'cÕ´»–¬þÞ(2Ñ üûyÖ‘ûÅ’¶ ]zÇ™ ?Ú|p^pEjŒqG~íW“:ê¶¾ Ö/jžuæÇÅ’þ[•={~^Û^ÜvŸq÷×ûšý|3|ì»66´Î(/é,·­gëçׯ?ñËë=Œû>Ö»œ½1Ã(Ûo5Rn_Uç£'¦¼¤ó–OüÑ:>L?¿óµÂamc-|u«»ø4Šúá·œ¯ô[Ôº8uä…üíÝ6;vñbIÿ-Kúlóøm—-7ç‹  ·ñàë¯|¯Á£Hì®.K´ét›\—9ë¢Å’þÏ~Ó%|þ¢)úyuÞ¸þÈò~×bNE“®YþÜ<=\îß‹M6ôpæ¿4Iïgo1ÒAE?£ÓA±—->2rƒŸ?Z{ºÃ‡3lŽ~ÍÖw‹¿ÿtýº%À‘ü/v?ÚvÐ/˜æk’‘ûÝ+/EC÷/¸¾ì½›ÚoX|à×Û¾}cõ$ýó•ý}Açl»aɃñðÁðnãV¸Œ¢äåuŸžrÍH“rA=Iïgžœóìóo¼kóÍ…?\=;ø„±aý©ï"ŽVÙëÕ"´å»Ïu5Ò6É{ãã;©/éÿŒ¹ìícË͹î36æÍ8¸*ÙwÌí°L{Ý—¶ð­ÀCm‹y5MòÇÓÊ^º°Ôç÷øXocÓùÜšßl©o¬¼¿ÑÔ鯢o™´ù³u”—ôjŒ¹¡jËÿsÙ8ËØœüüw/ÿ𔃻cŸ?÷W{ݶPéI›ŸÒ$<)¸ƒ)÷‚8îÛùº‘×à댆'ÛãoÑmÁôUgóö_qð_"ùà‰õ#ï{齚ú…’æ‚smþ~ìqó`ËáïõoNû£Öd«}c¡:¿±í‚%’ïw§ö×YëÞþ¼SŽñøW#bù~ˆcG¿ù*#ô­uîD=Éyj>¹ðÉøŸß=çm<ñõ ~Ï6Š>üd@”ÃGij¿Ú‡%’/ÌãÅ£/;tÇã©/O>ŸÕnIÜ»F‘ØF­ùšš\P_òÅÆ}b|—ÍWkoðŠ˜žbϧOÝR糿ŽùÐÂߦ˒ bâYäÈçÉÿ¤e¯z%_¿ècÀxê…½“7oEOßõi×½ŸéÖúw‰ä ó¶Å„ ý¢¿©Œ§å¼çŒ¿œŸìqHÿjÁ»÷„ísäz‰ä‡õjŸú¢ºðôö _š¾þ/Ö1Ö8,ñÖ·…ÕsöïÒ%_<8î‹Àg}Ó/ÎÞÑhÈœŒg†xÏòH£ðz£Wúv:e,µö%Ò%Ý­{1Å1SÿõF~'~Ù(”v‘‘Îä8|{å%½× œ5¤êàG6½.>e.€íy(ÿ® ÇÃZž±û/9ƒ¦~㜧«sz[¤K>¸È¡W–5øÉ¡ãóáÞùÙx¶Ù²ßê¹"}xr·W•o k~pôSºä‡û0‚;ÏÝoÛKÕ>¡%Ï>·:¤hèOF¡¸3`³Íj¾rö‡Ò%?Ü+÷ƒõ‹åµý{Û|µe¬©ðŒÂ²Š¯øúÛvWšÜ?¤¾ä{¾íXþå¹úÅoÌ…–Ç–o.OÖx¿QøAý_ƒkï4–¤¤»^}’z’î–zU/7Õr ±uÝíK·&½m﯊ÝÂNéƒwøðI¹²TÒÿÎ&cèåmÞ|î£ó+m­*õÍ>µñ»4¾¿×òaÆRk?k©äƒœRÓr±õ[¹9¬«ìþn{À#{Ë]£íy®ð£×îœXÜÇÖW꾇Í×K%¿¬–ó€=o–G<1ÎcõI›_¶})ªým>Wôµï}eîþù/ßüˆÝ¸TòÉ u>W.NSÇ75¶÷?ùÖ˜Fá‹·<¹üq=*È\8ËLöhÉõ%,ATèå ‘ãK>-1¶ß]ï³éMÞ6 Íë<ŒeÖþÿRIÿLe•Ïÿ*àÓyÛßûúòê:GBq›kÚd{_~™Ôÿ,•ôOó]]c]½|åw—>÷¬kìðYñ}ãFá+ÏÜH¼g,Û!Bg)/én­'ËåünìØÚ· á/Ÿ…å§«FÕÞ`,ß»éÕa¯Ä\Éô£öcË…ýE©­wÆ÷¿ãИÚ½¶öèʾ_þSÏÔ/N%;ã“aò>^/”¿xHœ8a¯{ø·À‘çµÛnïr‰½_¦Îuìy(仞j²_½\îû»"þøíÝA>—¨¼î{ÔÈx|ÒäßæçQϤ¯ž!÷«ôr¹Ÿhì§Ã·í3 Íå\Ã1ÿÙów†I_=û¬y!ОŸ*Úz>^#³«ÍÇ{Ä-µ5†Q¤EnØZ{¼Ý¥›BW¼3æ‡~&ýõUË7Åô ± šµÙ(ì×çÎ¥ “uò9UÏ\J12,{;ä»~»ºßP!÷qŒÂßÿî½#Xû>Æ2{ÜMºëwÔ:5öôƒóõ ý³7Û·øÔ¶Ï‹fä|ºùëo}2Ö¼°`dy˜†­£O3M~Ðïº[l¦ÚóhÅðfyk¯Ï±û_TúâcÍg—;z¥M§+áíù9[íÿÚós¦ä‹{”ž®gN0ÆÞö÷ú¾ßÝ™WΊƒ™cÅßÄÁÇç|5SòÃÚÚŸ]k¼æ'‡.ò|Ôžö®õuþØraö‘Ú¿+YÜû•3¯dJ>y@ìútqÎc*R{z³Ô°ç×½_}¤ÈÛcªó½ˆƒ¿{¨/ù$·• P¯P|øÜ²-?<´Ü(”v™5”—| ˜¥›÷q½âžb'É(®ï]üSÑ£pîw]ÓJ,qZvû8ÊKúoº`n8ýÝè:·iÚB£8qÒ_ÜíÈ“ÝfÓõÉ·Îüa^ÓM±í¹,u¿Ç^'fI>±Î+çîŸôDZVFI‹×·hÃŒBIW#[Þ£¼ä‹½òþ¤-÷•Ìê¿~ùªQrëÌW67°×%+­qÈ’|ñœº‡c·³õ‹÷ÛNuôÃëŸIÝxÍÖß«…ôwšìÈA–ä—âZb…îÜã¯4Õuo£äì…wËârþ/i¸ÿ‘ËÛrµ¦•0lšGòOñ„uâfƒEãÅ&1Ÿm‘b¶27JœÎ&¢”—|Q|ð£öGwÍwøB\Ÿù Ü¦ç‹#§<¾£^ ‡ßÄ5êÎ%ƹOãÌŸY’/öaý48ôµ3Žë½Þ›Öî^‹¿ŒÛ§|ø©Ž‰Ž]-ùaŸiNdè•·¾7ðûfgŒ/Ü·â@—{œþ777,->§ž¤÷~aÌzU¯¼M.–¼ýâ\ÞC=ŒBß”z'ñqè—-é¾ß¼ŽÒÆÖÏ•i[ÏÔ^b÷ÿ°ØŽÝÞ¯ZÙ\<ðøú’þÔüZ)À´þÄ8üÝÛ÷Ÿ.úÙ(l*l¼3gK:(55¨^¹¸ è¾ÊŒ#ñŸ ›óH'£°^ÎþAuZòFyIσæî˽rÑéåiÙŸGvw={æ©úö¹ŽeÇØü”-ézð@ôsý¼]¯\8{\ź-OG~ýËêCŸÕrô…ÄÓ¾§æKGgKº>°yÞSßáϤ5‡»ûYãè Gv¼öA]‡ßQ­ß­k(»2æÊ I×ç…ôDU8|15Tœ ÙúùhΦÏNí3 Û‹ ÉÆš‰Oˆ‘¥¾¤ï!e‡UŽùdÃGßfØý9zèoVÔ6 ¿\¹MÿÜ>WZ“j$P_Òù\9ø3k_þi—qTìŽM=çè-9ŸÛë:…‡c'®tÁ¼Î8Ú‘›/þHˆœ`¼ÔäФ”ã³ùôËi£cœûˆkÔ~©-7+$_¼ ¸Ç£¥^ÙÅ•Vò^©ý~à¥èñöÿaìù8ˆØi¯+Ö¨~Øt_!ù¥ä¦ýÿJÓ·ùù¥ì‡Ö=ú´·Ó_±üÈfk~rë§ä£’ïWîÿÛkÌo št?ü•ñÒ}Ö^Ûרƒ’mwå{c¼XAyÉ'/f¼qíÐP½R\ÇŽ,0^ö¤‹ñÁecÏKOÞïcäÈýµ˜++%_¼¨îcT(ü^Þ²hÂ’%Îþð“fÙü¾¦Ü<ðrøs¥äÃòÜȦGÅ×bƒwª=ÁË>÷JÝçô[êyûžúírãÞÏ•’oŽ úúÈ”rìiy/Ë0¤ü…jµJîÏP^òÅÑ#ÃÎ_¯´Ç¿â¤ùÊ(íüë¾;ºÙëRËŽ^¹Ýܤ¾äƒ£/5;%öyc…Ò¿—ÎȈ›ždí«ØvÄÊo‡¿‡©É/Éó{©âÅå÷¤?ág”®K8ðST/g]e6çc¬–çIÔ—téòGÅ÷çw×+乄Qºw¤voÓF‘—0p?6nŸ·¸åì‡[R^ÒýåMr7yL¯§þOÛã^*ŽÓïÙï¬ï?1y°ä`•䇗å}qÖ/ûî‰Ýõ’Qzé…O+ú¯sæ«ÍŠ-Ÿ9Ñ‹õ%j_¢bÑ£ÅUö7ŽÕ6 Eß×l»?'ÝsšÏ„(/ékˆãÁõëzM^ýÈä‡my9Ö5zg•§l~,l#6vÞ·å0GíÇÙü¸JòAiøK¬€_Ò+b_~~ÿɓƱ!_=–”ëôC[vÁírŸŸú’JåzP¯v”q̼~Ãü¸)mÞW—·«ä8P^Ò»ôƒ™åµýÃõŠð–ܹf‹qlSÝûgG÷rôæñxÑsc¥¸öøÈêI:SëêŠn»×œ;Ü8öò‡Mêtè%Ž ÞùØêŸ3o­’t?6©TÜlÑ+¼½´½éSƱ/Ö·îuúkg¼ÝýS ±RÊq̕ՒÎÇ2/Ð8òZ3í¯e™q6ßohNìþêýe÷¬”ëG^WKúS÷ÊʯHÎÿ¥žMÇã­^\sék?ÕX%Ϩ'ùà¸<7vöÿÞ©¿pðvãµí lþµæ õÎ8’îÇ•]S.nUÎkkŸa@Ž~ÇÉSŸ3ÖôÜõþOë’Õ’ÞÇ›õò³^9øS+ãøÂGϾ×ï/¶¼­)»Ö9kO#ÊKz¿¢Îç˳̓bãxæÑÁƨýöý%ký·F¾k¡ž¤÷+7­oËgµeæ˜ìŒÓÊ.s|t¼Q$÷síùåvë=ÎjI÷WÔ{Ùrµ¯tüŽžŸ®K8hÕX×þ·ëœuÍí’Þ¯ªs•ró:Ç$ãøC=:®—XmuÚc¯ÿ<$_×ÒÞÕËÅ­ ¿Œã/î>5z»3¯Yë“Û%=_}íËÐþé?ÜùÄá‹Æñs­îëY£¡5Δ“ô{MêGç¼ApsßeöþØñßöl(ãœ{ú˜kó³ê·¥<¤¯'ñ¯:vBµÏ¶êØ ÿÿûnÿRÔ˜U¾8à?3>xÖp©ø²y*”ô…jú±¯R¾´à÷Z9|N¨³ä×ÎW~ìI×Ipó§å’>3ê2†u¯*Z)Ê—=éz1|€]¯ÔÍŸÖZåWƒ¶ÐVƒ,éSÕŒ3Kù†ÀkHº!í5B¶%ò)Pþ6|”?{ði\¥|k‘ߤDNC^!Êçj¾ò¯å£âPå»Å›¥½fÀk>ÍýnòÍAýÀk‘'}lµdüZ’ß’ú-i¯ýkEºU¾òkO¼é7í{_u‹7[¬|ÚÇ*ßZyÊ·–ŸòiOý6—”ßÖå[ zµßvŒ_;ê·£|{ÒíU¬Yò;P¾ù€×¡Lù³'¿c¾Š3ëró©Eºù(߉þv_ðóßbå¿>DúéL_;ƒ—ò]Oº«—Š)»Vù­§~·xå¯ܺ“ßtwx¥{±òUOýr¤ŸzòýcUüXÒ=À¥¸õ(PqbI÷ŒUña«T\XòÀ=€¾\Wñ²ÒTì×ë*æ+é |/+8DÅw%?ÜCÀ'd­ŠÝ üÐX¯5”²½€ß‹t¯µ26«ðáF°4£5Œþ÷‘~u…?¯pê‡3váÔ~/é·Eø¾ô…ïü¾Œ}_ðïK:ÂGúq1ýèWÉ8bf¬VÚëWªüèƒ_džŒ¥%TQÿåë…öû_’qb£h#J̩պ¹Z7çxTëæÝ,d5M pMŸ§‰26%xÔðS±óUŒ—›ŸKÚ©å£|–¯Uñß‘­Úä×.P~.I×Itóg¼ºY2þ{ÝëʯUš›¯ËXÿüz¤ëÇð_êŸPñßi«AŽôuiÆ~ŒU1à‹åcÆ€‡ÿ‰ôUk†üÆàÓ|›€oò›@C/úî¡ü^’ïþM]*ÞLŠ7C{Íh¯Y±ŠOšÓŸæðH O;²ðZпàÜ’ñkI~Kê·¤½Vô¯éVÒwº7ýñ¦?Þ´ï}]Å–Ì‘ñfL¿—´×:KÅ›¡~›éW½ õÛ\uó{ɵ…^í\nqà)ßžtûƒ²}•[xàu8!ý^vLQ1à¯*—Às嫸ïäwJ“±ßÅôê ~¾àã[¢|\’î ¾ékgðïâ§ü[–©xï *¦L™œŽ»%(ß–àÖÝÇ-Æ{‰òk¯â»SÞŸ|ÒþÀó/SñÜÁ­¸ô·ž.¿¾÷^åÈÈ—qÚÍíðC øžP1Ùï ÒAàT¥â¯Ó^0ù!à’ â«CûPà‡?x¡”íü^¤{åªØ6àF0à…ýï!ýˆŠ¸6áÔgì©ü>>ÒšðQ/bÜ?›f õ<;Ý¥|èH?›"¶g?Òýh¯_™Šo~‘Ô~Æ«Œô[/bÛôŸ(ÒQi2~º©SÄ?¡›…NºWè\¡c-ý*tªÐ§–î´ô¥åCPè>w}|S¿ Ýfé0¡³„®²t”ÐOÐÁÔE–ÞY¤æ÷XBXzCè KG¸ëkþs»{ g1‡[s·˜«­yYÌÉÐÆœ‹­yXÌ¿Ö|k͵bž½9>340çL1_Z~Åœè%ñ>žÍ¸ ù*-´«Mûu ¤Ï^ÏÇÌ©«â^•Ș M ]úß\¼æüÖ”ºÍÑ|[€WKÒ­H·âÛ;VÆž~öL?·üÖ–vÛQ¯½ø¤É˜²…¼’ï¶3û"+¾ÀëB^ø¢ p»£ôðão>=(ß¼z ;¼HR/ˆ~Óf0} ¡|} ¥L(°{ñF™0`÷Ž‘1§Âi¯KÆZ>dEZÿ¡mDR¿?}èÏDwµèQm'æxTÛ‰ÿ,v¢¢g‰¬§¹”/wÆHÄù®¢â(èÈVMòk’® œZ.å=×-ašŒEXÇSÅ"¤|1¿^W~OsT,Bšô ‘þO=ÓzüP/^Å"$¿>éú¤ëÃõϪ¸à´Õ`­ò‘ê¡|¢¯!õyªXá´ßˆüF×Ý|£Ó~cðm"æð,;œyÈ+VúPõ"ß‹þ5 QþÑKdü3.!í5+Qq Á¿9ð›Cã^*6!éŒG plÉx¶¤¿-)ß’öZ¯UšŒMhúH_oúã]&‡þïü£¯5íµÎQþѩ߆öÚP¿ õÛП¶Àk ü¶ä·e¼Û…8qÊÛQ¾=éöY*&á%“tÊw¿ŽBפ©x„Às‘v‘vËX„¦Oô,éÝ—þûÒ?ßµ*®¹—ò…Nºó 9ív~hß嬊=˜¨|  }EùnàÚ ØÝÀ­»KÅ=§|wðó#ß/AŤ¾?ùþ¤ýïOº´ên=(ÛÜz’îIýžðGOà ?€¾P>|ß@ê’"Äø1žAàD`ÒÁ´ ­Cèkø…€oc ¼Pà‡R6”t/—ŒgÞ+Oùd‡0ê„?¬LªÞ±2f‘ðÇN^8c>á´ßÇOÆŽ1‹Ì8ˆŒU_ðí þ}/ɸK¯‘ü~´×OèÝ< ]Ä@Œ¿HàE¿?ãÕ?^ÅB,•j)Šö£h#JÌyB7ˆB[zXè_K÷Zúöf›QèVÚþ:UèOwÔB/Z±Ý,ÛQè;¡ãÜmHw}æ®Ã,}%tÔÍúéfÝ$ô‘ÐEBÿÜ®æ(¡_,»Ò]wX:ÊÑféKˆ¹ßÝÆs¼˜×­9]ÌßÖ¼-æjkž¶æg+ƒ¡Ãßͳîö%chÆÿ¸¤â·ò]“þÔ,vb™qZ¡]ä«.ãX^ò¤¬'ýó¤ýzиGÅ3­R1¼ø½S©ŒCÚ%FÅ¥Ÿ]STlQÚë|?~÷‡6þ|÷ üÖSÈ&r Önäs83ÁBæ€Ñ X½ø£0Úèí)c‰†“î¢bÈØcýªd¬‚HÚìO¿¢(%èZm—VÛ¥9Õvé?‹]ê§èUêáÄU„?4Æ@»ªbô@ïŒY Ò5ɯ Ô$¿&óE-ê×Jü“8=ŒA/ƒxuJåÔPÙ¨›«âô0Ox’ï)æfƤr_/QÅé!¿>õë‹9Øõ/¹ÅȦ~ƒRå»?AÅl<á'ü•Êé¦qŒòå/öÀ¯Iˆ[¬lÊ{Qß‹´ù^—TÜžµ2¶£åçߌ— ¼æ^*^6ýkNý>*víµ`|ZP¿%ãÑ2KÆÌnI{­˜KZ‘nEÚ›úÞ‰7ÅËf.ð¾éÖ.>ä·¦ýÖÅrúkC{mh¯ õÛR¾-é¶Œg[ðm{]ÅìY+ce·#Ý>BÅÊ.s‹Ù“ã+;BÅÉ.•Ó¨‹|iW‰ŠÕ#tå;Qß|}éŸo®ŠÑC;“âb{ª¸Øy2V`Wƶ+¸u¥]ÉïæR±y€Ý \ºû©8Øy*¶ŠMÚúþäû“ö'íOºðz€KrïIº'õ{Ò—žÀ ×òH à=©H:øAŒoãT"§þ`ÒÁà /‡P?|CH‡~(éPʆ’îEºé^ù2.·PaŒWý£½½dLgÈŒD^8ø„ƒO8í÷‰1;Eo367ù}¾ä÷½ªâD¤©xAô§ýïGÿû¯ß%“›ò‘”$ÝŸþôO”1ƒDŒ (Ú¢ý(Úˆs–˜×Å?÷¸Œ–þÂðgzWè\w]ën£ZºUÌB—ºÇR´ô¥Ð•îñˆ-}héB¡…î³tœÐo7ë6K§YúÌÒc·y8ºÊ]OY¶ªû^¨¥{,]c阛õŠ»ºãf½aé ¡„Nãê¾WjÍûbηâ [ó»5¯‹9Ý=öŠ5wÃæ¼}óœ-æj˶ Q1T »OÔàS“tÍRÈJ^­”›âÐfÊÖºÈc]úèÉïžô¿t¬GùúŒm}ø­eúÈØfà™F¤Ǫض2¾­åšÆª¸eÔoF™æÈOKÚn™#Ù¥•ø&ßXÞ)*þ˜°}‘‰ÖðJkƬ˜«À«]¬Œ)&ت}ŒŒ'Ö6:×mwâ÷NÀóó üÜ9OÅý‚F]ékWÆ­p»å¨x^.ËKÄ&õ§Œ?ðz€ðì ¿ô¤ý@`’^Áà,¾Ëà…ð n¡|÷â; Ãòd,ßÞbMI:yë^}ù»¯ßë2NXøö£Ý~ÂF¦l?ÚŽ$?Šö¢È3íb%ÖßÀ1õ¯‡Ç¿Ýn®¶™ÿólfa/ÿ{måj;ùßo'[6ògû8VÑAÌó×Uü)誕J1®«bX {˜¼šäÕ,“¢] ™¨ÿÔbk#Ûµãe|ªÚà_ÇGۤ|ú[ù®¯bX‚¿'iOÒžb>§|=?ÃXõȯï£b—ÜÃ’ò €×ÐÅ'QŶ¢~#—ŠeIûÈo >I7ÎUñ,Á·IŒŠg |/Ê{QßKèúêEM)ß4WÅÀŸf)2®¥ÓÒGÅ?'¿9õ[¸dL¬ô¯cÓ‚1máö[Ñ^+Ò­€çíº)¦%ð|hßø>ÀkM[­Á£ ãÒ†vÚP¯ pÚ’nKº-í´-•Ó^;Úm—§âXzª8–yr*ìëİì@Ÿ:ƨê”uQÖE¾‹|cÒ‰t'ÒHw'_pô¶/°|Á©3éÎÌ;¡YgÒ]À¥ é.*V%ýíÊÑšu…çº1‡vV·R9åv'Ý>w§~wêû1¦~¤ýHû]•±¯ü…žž?¸ö׌opé n=Éï üž¥rº ?øô-€öÁ/1 ¤~ é ÒA¤ƒ€D`ð &\ §÷ê‡/¤XNõ¡ÀelBKå´ß‹t¯33ŒqÏ0ðî ­z§7õzƒg8yáàáô£O¬ŠY_ªbf’߸}K¥Ú0c¥åȘ™B…ô#Ýqè¼~×U¬Lò#) ¼þô£?ãÒŸöûC·(ÒQŒ{mD 9s¯øgã»Ç(w·‡Ýc ÝiéLwy³ntøl`ëÜÿÙÂ7ïßþ#ýõö{„îúêÏôÔÍúéÏô’e3[ºÈ]¹ÛÏ–Þq×5BÏXúÅÒ-BŸÜl7 ýðgzÁÒ־ȟÍ÷Ö·îXó·5w‹9;^âkÆis3t¯/M®šüV“öjÁ›µáÿÚÀ¬C™ºŒ]xÛ“t=„¡cWߥâ«xð}Cx¡a•Š Lºq‚Š«b‚“WŠ4Õš’×”²Í€×Œ²ÍÁ©ù%ã7FÆøm­bU|_Êzß›¶½ùÛ‡ö}„} \êµNë2¿œÛ–©¸½´ÓŽ2íéc{¾;ð[‡xiöuŒUñy©ãòR1yãU,^ÚðMP1xß9QÅÞM”±÷º‚c7x»e»•©¸º”ó?Êù]WæbòÁ=± â;ˆt0óB0øó[8…0æ¡|‡‰°µ]2&²ˆ¹+D)œßÂÙüúP¿/øöå;ÂSÅ×÷~YRÔ"I÷§þЧ?}‹âï(ÁGB§‹ÿ‘ûÓÿíì?³±ÿ#ìëÿˆýèm/ºzú¿ß>ô¿×Æþ?µ¯Å< Æð„Ší¯bÃw d°F‚ŠóJ?k"³5žVŇ']+†{“ö¡®°}ÀÃX­i«5ãÓú„Š OÝ6¤ÛÐn[ÒmãU\xúÒÛQ¯ÝYÚ“nO~ÒHw nèБ>uº‡tG`¹€å"í"í¢~'Êw¢|§¼›âÀϼ:“îœ"câv¾$ãÀw!Ý¥XN£]ésWúÕÚt¥|7hÓ XÝN¨Øï¤»C»îÀë~UÅ}'íW,§]òýÒ=(ßx=¨ß\{‚[Oò{2¾=@~ùà@߀ȸ‚O í’"D:AÔ †fÁÀ&?x!Ôßꇒ~(ðC¡/à÷"Ý‹ü^Å2}eÂ0ð£ÿ½ÞÀë]*cχƒKx©T}Ç>¹2.½‡žt_`÷%œ`GäÊ8ôý¨Ût?ðŠäïHðŠ$/<"ѹýCdlâþ´Û<¢HGG”˜ÄÜ/þÝlGÿk÷þÌ~v·ÿÌnþßÙÌB? Ýä¾G,tËŸÙºfãÞlߺߙýG¶íÍó¹uÇÁ²sÿ̾µæakÎýGö-ãlÛ²b>LT±­¡e þ® kAŸÚŒem~¯ ¿Õ¡N]ø­.tô¤L=úUZÕ‡‡êà ¨Ó0BųÎg¢lchØÞh5ÖM)ÓTب”iO4ƒÏšÓ·æäµÈ•1¬[ó{k굡í6´Ýœ:Ð^GêvVG~sK'~ëD;€å L_`t†_;ƒ_¾»9…_ºÁoÝ€×ïî”ñ¾yþäùóݸ=h·°zR.€¿éG } †‚¡o0‡€Gc ž¡Œké0aC’ß›ßÃN}€Û‡ïðìÇØDò{”ï›m»ê}Ôê»ÿSm¾ÿ×{«â.Ñ|~äóŸŸ=Ì=V_øüÊç7>¿ó¹Áço|þPv"ÿi¬í4Övk; ½¤±¶ÓꪷѬí4Öv:Jcm§±¶ÓXÛiMÔû,ä_Cþ5ä_Cþ5ä_Cþ5oùfACþ5ä_Cþ5ä_Cþ5ä_ë¨îí"ÿò¯!ÿò¯!ÿò¯uW÷Ò ù× ù× ùׂÕý ä_Cþ5ä_Cþ5ä_Cþµ¾êìù× ù× ù×MWûÏÈ¿†ükÈ¿†ükÈ¿†ükÃÔžò¯!ÿò¯!ÿò¯!ÿZœ²±‘ ù× ù× ù×ó]9ò¯!ÿò¯!ÿò¯!ÿòo¾mCþ5ä_Cþ5ä_Cþ5ä_CþÍ;ÍÈ¿†ükÈ¿†ükÈ¿†ükÈ¿y·ù× ù× ù× ù7Ï=‘ ù× ù× ù×sïù× ù× ù× ùû=ò¯!ÿò¯!ÿò¯!ÿòo®/ ù× ù× ùס5ä_Cþ5ä_Cþ5ä_Cþ5äß|sükÈ¿†ükÈ¿†ükÈ¿†ü›ïþ ù× ù× ù×ó¾7ò¯!ÿò¯!ÿò¯!ÿòoÞ»Aþ5ä_Cþ5ä_Cþ5ä_CþŹƒ†ükÈ¿†ükÈ¿†ükÈ¿†ü‹µ“†ükÈ¿†ükÈ¿†ükÈ¿†ü ò¯!ÿò¯!ÿò¯!ÿò/Þ,jÈ¿†ükÈ¿†ükÈ¿†ükÈ¿¸«®!ÿò¯!ÿò¯!ÿò¯!ÿâN¦†ükÈ¿†ükÈ¿f}¦xTû0Èwóa¨ÎSªÔY{¼z+Y¢ü¸Ôøbu¦â¥ÞK&ª{ð×Õ¹|šòepB­Õ{É*u/5B­”¸­Õ}øbµ·èRo'՚м¿êRg-iò=•y/>BÝ‹ÏS¾ ¼Ô}Ö,7ÿ^êÌ%MîGÚw‘üä¾äß¿ø¹ù:(Swå#œ7Z¦é¥î%e)ŸUêÎ|„ò{'ïÌšï+CÔ‚,¹—ißSŠPëÈ<ùÖÒÜßôs{oYªÖ“.å!MùD(U~<Ô½úu·>GÛ”ª³u/!B½ÉLQ>òäÚS¼Ï4ïíz¨wšê­fŠº‡_ ý)˜wz=ÔÛÍõ~3GÚÅæýü*uÊ¥Öª‰j½Z ÷`ÍwžêÎ~¬|«fž È7žæÖK­cãÕ[Ï|ig›w¦<Ô9QŒZ׿(¿ ¥êý§‡ZãÆ¨½ÝµòüHøj®Rw,¼Ôÿx¹ßkÞ÷•w͵oZÿ–ȳ$ó]¨§z#׿æ¾ðZyCøm0ïWyÉ»ÿ¦¯†é¯Á¼³ìRwþÔ{Ð4µî-{ÆÂoƒy¿êªZûú©õoŠÜGïC͵o®òÙpV½Ãó‘¾¢i3¸ÑÀf<¢) þÑŒõÊ ¬NY²:åô4á S«ä’dy“È›D½Éà3Ø“©;ø“©›¾ äO¡îÆ| u§€×TðœÊïSÁiøO£ü4ÊO×éà1XÓ5ƒ²3);“²3ic&íÏ„¶3¯Ê¥N"x'#‘ßi7‘ßgÓæ~ŸCù9À˜ÃØÌö\`ÏeÜæQi>‰qI¢­$úœÉà˜ ŽÉ´›œòSȱ|Si'•vR©ŸJýÀ[¼ÔY@7q!¸Š¸„ )»²"ßbÆx1øˆ¸S‹¡©ˆç´9n¾Š|ÔŸ,·óXéçäïÎd=Ô{ôå·è„ÛÛŸ4õ&ý’ò+£Þÿ«óY/õ6=KÝ:«Þ§G¨»9ꬶJÝŒQç>ùÒ—Šyfâö^½LùôS¾²”£2åãÈS½ŠUo…Öª³Ü2u÷ÈSÝ“ŒQoÚÓ”ÿ£|yž$|¶˜o‰<Õ;÷õÖ=M½™-–¾‘Ì÷Ežêí{¬òñ²Vù1,S¾ =Õ[øXõî(W½=:¡ÞÅ{©7·ñê}|®z‹tVù9ôQgX Ò_Œy³LÝÅôT÷¤båû¤˜,åc©Lù•ñTç^±ê<9WÞ£~—Ä{^aÒšgaÒ“y¶¢ü/¥©ó±byFfú^ª’gË1kÕÛúXun–&}Ó˜w7Ϫû›>êMp¾¼³iú^òPþ—b¤Dá·Æ|A½´1T¬éãhàÞXú1œz1¹ò®¦ùŽ8MùEÌSþ—ªÔÛz—ôÃM›ÑÀF4c Ì8pŠf¬TIS\§¬NYrz–ˆrEàž1UÒLþ52ƒÄ‡rƒhsm¢Ì-Œí`ÚÎ8¦î`ê®’&ýꉑfý0Ú&þ¦¿Ã(? œFò‰%K?b©K?c3˜ÃŸáŒß`Œ ý´=‚¶GÐöÚ)>Ury0 Ø£€= X£Ä¸k4ø¦¿£1c ÓèGÿã¡]<éxÒñŒc<øÄƒËXà¥ü8òÇÑþ8`Ž£Ýq´;ÚŒ¿ñ—ä’cù ûè1‘ö&‚ÏDò&û$ò&‘7‰z“Ág2°'[ĬË“ðM  u§0æS¨;¼¦‚çT~Ÿ NÓÀZ•\¾L×éTœ¬éÀšAÙ™”IÙ™´1“ögBß™àžHû‰àŒD~OçD~ŸM›sø}åçcÎY¹ü™ ì¹à5ï„\%{ã’D[Iô9 ’Á1“i78)ä§ŸœTÚI¥Ôr¹´x €·€: ¨³€1Y® Áu!eRv!8-fŒEŒèÅŒÁbhºXüži´/b¦S>\3ø]ļˤ|&¿e#“v3ÏÊ%W6¿e‹5°ûÅ¿jcÿ>cbM#Ö3n똱fÇþ·k±&¹y=róD¬;¬5‡Xgˆ5…XO¸¯%ÄA¬ÄZàæu€°û…}?Óñé…=oÙòð•mà {ݲՅ.lsa—Ãö-þo±Ã… þŸe[CÛ?µ«Ýmjw{ÚÝ–¶ìhˆ¶ìgËvv³e3 {Ù²•…lÙÈÂ>†'̳(a[¶±°‹…M,ìa/ÿ_tùÊ—eŽôK$¾¤ý¤"q÷Nø+6ßsç«7Ü¥ò±ð)$Þl ߇œ¸Wcú}>+ß 5âÍÉ€bé—Cø¾1ßP$Ê÷ÆÂO\´ôQ3„:C.IqJ_‡o(u†‚çPò‡Rg(}JûqäÅQomÅQ&Žvâ€5’y:N”¯Á”c¼âÖÊ»e£Á1ù ¼X>Ã…žuèãXꌦîXþOÞhê¥Þx~/ý&ÇÑÖDÒ);ø);‘6†ƒ÷x~/ê ]E½ÉÀ˜˜%§©)ôe2ySÀy ¸N)äÏÞ<êÍ#o6c9:-§åü>‡:ó€s+¿'3ŽÉ¤ç3†ó¡ã|òçSn>4šÏx-¢oKhk´YD™%"Í·ˆ—A{"Æëè•A2èK¿‰xoY¤³èˆá–óòåô9¾-¢|*íÎN*m/¢Í%´9ö“©›A¿‰4åÑÿ,ÚL…ÀI¦T豄ß3À+‹1Ï€f‹ÀWÄÏ1ÚÁm¸§2sÀ!Yèú›œ,ÚÏ N–è e²hs ã bµ-aŒ]•Û"ækx-â· êdOuÒÁ;Kà$êR6ƒzY×å¶`6}ÉÏlÚÏÇLÚÌf<²‹=œ³ÝêýáêýáêýáêýáêýáÞýáê½áÿž{Ãÿ7÷±„NÍñ¨öGZìæ4Mú­7ïҸù~*sóIºVúŸ6ßã¸Üü?•¨¸21ÒOŠðMmú¯QþŸJTlé«ÚŒ/SææÃ>M½u/U1fBÔ›÷cÆSù²OPoßË”?¨XégÅô-xIù³•>Wl_¥.õf'Ç-挧òYš¢âΜPow"Üü–V©7£±Ž(ÛÇ}¬zCZ¢îþû(_Q9ê-O•ò£Þ•®•oì7=±*&Mòiê©Þ˧(ÿQ'”ÿûé‹[ø~1ý›žP>N½Ôûùxõ†>WÆ«~¥Ìw?^ÒÏË-—¤_á‡é–ëòºðm(Þ×~–„ß©¡à3(Fŵa<»¤ïoÓ·Œ—òE¯ü¤æ*Ÿú'”_}/å›*^ùŸÉ“>hÄ;ÓO•òU• ý3š±pJ”Ï*õN?F½Õ_+ßÐ ÿûæû"/õÆ(^ÅÈÉU>VO(?«^*^N¼z#‘'ß Ÿä¦+OC'Fú(7ãçD(?åY*ŽN‰Š¥S&ãç˜þW}”ŸÈxKÇ|_‘'ßêš>X=”¿þåϼLù4÷”~ÍM¿9)ÊïjŽ|‡aú¹º¤Þø_—¾¬Ä» ÓÏd¼òu•+ý°Š·ýƒ³¤ÿU3†Ž§zã+Ö¾´MÝhò£)ÍxDƒ40PV§¬NY2±”×i_§ýàÆ€g eRf øÖ@Ê Ê ¢½ÑÀD™[Àíà &=˜ºƒ©;˜ß‡Pot졤‡k冑FŸ‡Q~8Åòw,åb© ½b¡Çp`§¿#€1‚¶g÷ÚAÛ#èóÚ)>ÀÜQÀÜQÀ¬Ñà7X£«äòa 4#Ö‚À‹£ÿñÐ-žßâù-<æÓæ|~OÎ"Æ¿Íc,æPn e¦‘¼ ´5‘¿'€Ûh1šOï‰à2“À{y“È›®“Áe2}œLþdpM ~mL¡Þ|¦7…¶§ò÷Th•Lýià?²Éà8]|€383(7“r3?ø3i;üf2>‰´Ì%2‰ÀI$/ñ„\ò̦Í9ü>‡:s€3‡q™ ü¹ÀžË˜Í£~é$ÚN^í%1Iàœ žÉà˜LÛ)ÀI!?…üà¤ÒN*í¤RõoðPgu0& Áw!ø.¤ìBÊ.§ÅŒëbðYÌ8,†ž‹OÈåUí§ÓF:åÓÁ5òéä¥ÓöRêd€O&ß™äg/2éË2ÚÎæ·ì|eo‹b}íþ¯ÚïðßakMd­…¬uXݼþ±Ö=Öš6×6b=#Ö2ÖºÅ}Í"Ö*Ö:Å}b­MĺD¬CÄÄZwˆ5ÇÍk øÅ\Oˆu„µ~°Öîë±^p_+Àßæ:A¬Ü×î{ÑðæßÝn¦ÝoÙûÂÖ¶=44ízaÃÿO±ßÿ­¶û¿f·Ãw7ÛìB~ÿË|^«¸Š'”¿Ó\åÇ©TÅgŠU~Ms¤ïk¦-{ÅCÆE±€Bª”ÓDõ®ü„òÕtUúg1YD\ãGø#ïÄM¿$À‹~ y1‰Ò‡¦ðMb¾uŽW±ú„Þ÷’~ƒ¢—!´5¸CÀy(ý ¬¡Â¦£Î`Ê §¡Ô,~cL{¨Ðé´G™8àß”‹~ýšì8ÊŒ‘>ãàÓáB/§È­ÂÑÔ‰£qðçX~IÑà;˜cÅß}™H¹i¢>ý ]Æ“7>W¾×Ý&S~"¿M¦îpŸÍØMÞ ðÁï3À78Cù}šø6çopçQn6}ŸÍøÌþêÏŸùàÇïó¨+ß·R÷VÊÜz]NÍ‹¨?8#øŒ¥^2¸Ì§Lcµ¼Ñf°²D3‹2Kø{ g0æK‹ Æ(XY|g‰~’?‰~dQ?ߦPv¸LöTÆq*egð÷,pšFÞè1¸ÉŒÁtú2 g— ü™”Ÿ ¬™ŒÙ,h; Ü)ŸL¹Dà$ÒF*e)?‡ï9”ø&ƒÿÆk.pS¡ó\`Ϻž±H¢n8&{*ø¦’—,ì pJ¡¿)àœ*lÊ¥/U´Aû©Ô_¼”_%ÕØ‚©Ö ½Î8ePn¡7ðY ž‹io1㻘tùià’Ìtp̦­tʧ“—ÁøgзLàdŠ<êf‚6¸dÓ—êýuêýõêýõêýõÿÙûëÿì{ëÕw¯ÿ¹ö×…N\ëQW¡Ä-®B–Š÷å¡|]¥(ÿ±gU|Xå#±Lù¢ñS>dתذ>Ò7éC¶DÅýŠQ>dËT|X—Šý•ëc!VÅoÏWñy<•ÿ«,é«ÜŒë¥b€¥H7¦Eå+Ç-Þ‚Ÿò·˜çs!DÅs_ë7ÖKÅ^HS±cÏ*ß91Êßl‰[l÷xǺÌÏÍGV™ŠñîR¾g×Ê8羚,—ŠÇ¢b2”ºÅ{Wqe‹U!/éÏÑŒÏP â3x*ÿŽ)Ê/m‰ôÑcÆjðQþ”?­<sVø_7ý÷ø(?±*†C–Š/V ãÄ‹8cÂ?¤/>DÅŒOT±ió¥Ï{Ìôkë£ü·ÿ/öκ­êÌãfÃ’Åœ­qh %5vlj­'Û±-yQly“åEòɶɲ¥8CBY’¡@È@ %!k€ÚM‡By&…l,ã2vâ„„1Ë@S¶0a™ßÕ}ÏvuÚiéJãstôÞ½ß÷ÿþßwŸôî½Öy›¦g´^êmŠç܆ôÈ¢´gÝÚ´çÝvÈgÞ^1 =÷6Z{ö­SÓƒèО×u\Ó*›£éÚº¤6RèÙ]ýšö|”¦?ïÔtn×K­¡CÒOŠÒ4omš&}‡|V—q¥|‘xîmH×$5ήŠÖžñåÔž‹}Æ>ù ]Û‰a‰Å^M§Þ&õpç­Ôt$º5M¦1R/T<‹>¤k†_öjMßl¬ö ]¯Ôj =³C>›^<ë+¤q6F{Nf”¦#aÓž¡»^êI„4p·H‰.˜Ùëås¿’‰™ ^2ØÉ´%Sä97`«`«`§`W€‚»QÔžFl*à’B }©âE¼TlR‰W ÷TlL¼§Áo1~‹ñ[,ÖððN'ïtú28Ï$V&±29Ï$ßLødÂÇı‰±4ák"gµ5ƒe^ —Y`d?‹ØYÄÎ"×,QCñ7ÜpsÀÍ+¬\lrá—Ký-ð°0>ÆÒâ”Kˆ<Îó8ÏçŒY\òÄú¸\ZX‰oÓJ\+6Vê]×ø³˜…ð+d, ñ/·>Ep/Æ·˜¾búŠá[Ÿ|Kè/Á׆¯ lDJñ-¥–¥=rÉb'W;ívâ•Á¿ >åØ—Ãµå`•o—Ë™Jl+‰QIŒJâWhKâ;Áp‚á¤Ý ç*jPEÌjÚ«±¯£ì°kÀ®¡nµø×q^÷:bիޱ¯ƒóR8.í‘Ë#8.ú]ô»Ài NqðoÄ¿¼Fðñiħ‘š4Áµ ®MØ6aÛ§eŒÁ2ø,£Ë¯e´yáé%~ 1Z°ok ö-ýr¹åǧ>m¼·Ñß^ÚÈ%@ìvÚÚÅ<†þП¾¾7ÖÓú)]ú)a¿ë©¡õ“X;…¯›ÄzI_'\ék ÑöéOµîm¿>|}sªuMøšF¬eôu ×ûÐ~¾X»Œ\¯œj¢¯QF®MĺD_‡ˆ5ˆþ›r}­!ÖúÚâëÖ§× ¹ußaß ýŸnM—·C>ƒ6¤·ÞD›¦kfÒžYÞ#u/…ÖnH(OÓ23jÚeRóuJô°>ƒÐ(›?V궆4s†ŸS>¤½à”š@F°bÞ»]êñý#9% ʯË$¸$Ãs¾Wj-„4µ°KÃ71J;™8iض˯ÖÅØ±_%µ-S‰½?üÃmÉùLó%à,órË ?„W×e†Àß•^©5–Ÿ s·ÔS0áŸÍ{þùàgÃk‰˜`S—úsÉ!Ÿö“|ne>µÌ+Ÿ8ùbNVKÓD V £¥B™SjÅ—€[·f“Ô@(¢n¥äS§2ü*À+%V˜¥Ø”Ñ^võkƯ>e䘂M }â¤Ò— V*5õ3fþyKI‡·ŸúÕÂ7“Wö ˜sm—·?ö­Ô4ˆM¿,ŽÝðÒÞì”Û›Ípj&^3'>Þ}ðõÁ§έpðeŦóåœ/çx9¶Ë±-"V1çÅbND>6r/¥½”:•ÂÅN<;5µËn9”Á¥œš•“{9ù•ãW§Jl+±­¯¼JÑgíNòt‚á¤Ý 'íUÔ¾ ûjì«Á¨¦NÕ`×€]CkàXËy¼ëŸ:bÕ‘WêÈ)—ÂÑŽ‹~ý.p\Äm NƒxÇ¿¼FðÉ«ŸFrn‚S±›àÔ„mœšÀZÏeÔ`¹.£m<½Ä÷’k‹˜Áµ…-`·ˆqÇ?GÞúÛèo¯þ6ri#vßvòh÷jq³ Fåw}èïý?ÄéÿAœÖ¯ÒçßÕÿ;èÿsø[üÃwé ú|üoá7üßæÿ3ˆëØqZo­{„ÞÚJ©?/ôhÇ5}ˆ-š>ílš>D¼¥FÆhàDökzÄN©54±[ÓïÌÓt"úå­7¤9äÔt‰û5-Ï•šž'1&Q·I+¥þÚ¤A©!4l…öìd|&‹ù=SlR—Hh Æhºšq’¼RßVh€NkÚqí¹ò&M£x‹¦Q5¬!‘Â+œ¢Á¶i:=šV1õ_(æø`ðIsiz`Ç s´çÑón#qµÔEß,ÚgÏÑôÚVËçÔ‡4Œ£FhLlÕ´GÇJ‰V0í1k¥fpýfxÆò2ƒëÔô‚±‰]-5‚c×K´8ê6š$®×ôÒ¢¥¶C|‡¦ çø©ÒL#¿„~©\Þ‚(© œæÕtG·J-‡DüÅ_ ØVQ3ŽÕtÒÈÛ‚_1vIô%qœ´Uj• ½$ñLüt¸æñnà;"ˆgÀÖ€­Q¬•ˆíàeËF_1ñìpHÁ'ûj™Š]*6©b­……ibìļ»q,Úˆ›†º¨ïfÆ,¬trIç<“|-Û96g"˜fò6ÃÏ W3XfüÍø9¨y ïÙä›}6¶ÙbýÅf3.9ä˜CüœA9Ë…g.¶¹àZˆa¡Æåð·ðòŠuýKèÏç=Ÿwv^b惛n>¸V|¬ämŧž¶ò* v¶…´á[ÄyçbîM±˜‡åT¯DÔ“|JÀ´‘‡ þ6r¶ÁÇ_ø6ìíð¶Êé`\ÊÀ+c ÊÀ©à¼œ 0*À¯€Wµðpî 5ò`ë þb8àè öU´WÑVELõñÒ^%Ú‰[#ÆiPN/kÁ¯%^-µÄ«‡o=uð±ÿzüëá\Ϲ›~7Xn—œjº±us ¸Ž={Ä1<<ÄóÏC<ñ<ƒrªÚL¼fâ5¯<uó‘Ÿ||äç#?Ÿ11ü`úÁôƒéÓ¦L?˜Ax©C› 6Al‚ر bÓN_hn&þôçnè{½bý2r¿W_«è{¼úšdä$|í¡¯7t“Ñöxõçtœj¯7üwÖ§Zœê÷Ibm Ö£­Âçÿ£Íûõ=a}®?rž?rXŸ×œË¦¡"æëáûÂbþ=Ú¼[Ÿoë¿ãm>­Ï£õ9tøþ°˜/ëÏ ÑçÇúÜX̉G·Ã~Ë=êÜ7|Î;Ú\÷TûÆ£íÝï»Å\UŸ£êóÓð½b1'Õç£#瞣Í9õ¹æÈß|‡ïë{ÃáóG}¾8r~(憛µ¹Z^Ä·Bgñ*ìÆí“zÞqðŒ[+µ•"á9 õ`hOX.ug…FR Ø%`“È÷Ll´Ôžº³‰'Αz£qâ>KN‰ÄM´IQ¡»h §©.©‘$´ÀŠ{÷r©[ªˆ{#ÇÊV©×”B¼â¤1®iŒ£BMÒÀTÀ7+Ý)u…NM&m™Çc¯·{3± øæÀ9‡v 1,Ä´`Ÿ–þ…ÔÃ"îã‚ ôr^Hüb|RÀU¨O&˜ÅàXé³7EÜk°Í¤Nvq†·]ܳŽˆ8åäå W<3‰á`̲©a!¹YÅ}„<p¬‹\GžüúÉ!ž›<¾ßkhÏ#¶ Lñ {4)'b:àZnÓ üºò€í÷%ÑN›ŽÅØ{Åý‡Ø^âàbƒ«øÆ) Ú9P#;ø£Ü: P—80äàU¯Xò¬¢5pª÷>á'îkø¸É·^ä!|ÉÁÍ5ÀÏMœö+î_¢_ø‘‡‡ò𠮜{á g?6~ú‚ðô?¸@jn MÍö•rúû&ßûmÜWmOí›ØOû&~Ïû‡~Ë{úw¼ß¾ßñþ¹{jÿ×ý´?a/íOúÝî_ó>ZrÄÿ<ŒÓûh™}4qíÚ"Nëªâ7ŽœÆQƒñðÏ57žØã·jz‡´Mpjº‡ä9FêÖG;’þ‰ðˆ«é«îÓtºÁüúc£¤>gHcÕ¥i¬H½î„Mc•óIØM‚CbŒÔíŽ÷jÚöä=›ÉðžBßxLY«é­Âc*mS½#4Wi›†Ï4bMƒÏ4üâ¨á÷D<Þã§ïÁá{´OÖgœNûtÚ¢Á‹&FôzMƒ¼´ÍðjÚýòö?3FÓb…ËÌ}r:$æ±=š¦#¶³zäavŒ¦ŽílâÌ&Υľ”^ºZÓg¥Ž—EIíÇpbhC yÅl•zà1øÆÂ7VÔ{¹Ô.Uð‹sø©wBN+æ1I\OYÄšGüxpâÁˆµ%‡øãš~¸˜ãSÿÎNÈ)HHCÿpTðO'O̯ɡ8‰ýš*¶‹°]¥é‹ã¼ˆÚ'Á= îIÄL"fÒ>©št\NOlp/¤Ï€;ùÄ7ÓHL#XF°¼;xO”SŸTúSéO%ŸT±>ÀßNmÊÁNƒW8iÄ«3W¡˜—c“ŽM:µK§^™'ätÉoxVìëáï¢fò4‹y-1\p5ãgýðpá—M{¶˜ÓC¶NUŒC±s°Í¡ß~cŸ ^.>Î-àZÄzcPNÅÜÄ-¦Í ·|ìòÁÌ3L«˜ësMXEÿ œ®`_ æØôûcäÔ­ˆs¯˜Ïc[Dœ"ÚkàYŒO1êã#?ùùÈÏoø>ÆÏ¦L?˜~‘˜~0ý`ákl‚ر bĦ]̇ÅÜSü…ï“ý¡ç Œ¶?6roL|0Ã÷žnO,´hŒøîêÿ†Ï/¿noJÌ%Åü1|O*|?JÌÃ÷¢Fî'é{I#÷l¿ÿÛB}þ¾G>Òç@áûDΈÿ7]ã«ø|Œë×–£`LÀ.ŽñØEâ› î´-$N,×e,˜I.©ÃO¬Xb¦ðž@ÌDò‹#…xiÔ(Z¼ƒ“ÎK X±´§ãŸÎg,ŽqNÇ'SÜÃÈ' p³8^È+û,ñ½‰M"ןÏçB8å+y§K Ü àÂ;‹¼,ئ¯˜¾bñŽ…—>éôUÁ¡Š8…àÒ_¿qÏ€»CôkçÜN¿¿z®—ø缞š³ ^.¸4ÏJ»‡öòãòãåaܼäXŒ]µñ·Šà8pß)Ä Ç͸zàã¡¶^°¼´ÀöâïÓKŒàù± '~Ÿ µ¥Ž´ÐtoŸÙ?ó/é-ã»÷†ŽÔîó¯¶?çû•á«¶±¹EÓÜÊ¡ÇmhûT9þ¿šv¥ÚëN:kgî,ÃþËý¾±o†#©]OžéüáÚë“rûÛ«Þ™‘—}Ù¢Ì7_÷mZIœh'ê’u·^¨ÚsÍ—±õjoKíæ“_íºeìÇ“÷oU}{~óú˜_„½QÚÿèPetÆ^åìÛV¿éú@9ôÞ ''nkU{¯N¹ûË´¥jWYjúÇw)Ó%oµåÖ 6nœWŠ¿Sú+ÿ¹Zä[Jÿ´‚—Ž5Ÿ£ç§ö®)|øÄsÕ®K¯ÿhÏÛG”Y\S[¥¶òG}žgÂ¥ô7兀W®Ñë ôgUÏzýÆõjïÏVšëÿÑ¡v¾^þгžW戴.¸Wm©{³ç&ü;¤ÿ’æMz3Béo[’ж}‚ÚÛõXûïMP;Ÿ}ç§Ww=¬úÖ ‡f컥½å-ÏPúï?§à²Kv«½ÏÝóà›'ÔÎ'mOúPõ~u×ìÖ‚Û°öy÷|¶çò)Jÿ+G™Q¹Fí=²áî9öuj§jºö…u •¹2?Õ7ý‚yÏnù¹ñÝM¿—×á³s6®¸Oí}eÑÉmQ†î=Ÿvlûï[ÔÎcÍgw—ÎP¦ö}¶aÁÖlÕwRyèµÏWèçàhã™üÞò(eÝÐøžwÝ®›ŸmPûÎì~ £â°~½¨]ç}y㓜ÊdízòŸQ{ýE—LVf–Þ{hÏÉWÀÓÆûÊŸ\žž{|è:<ìLY“½×ð‘ÄWû&œ·÷«×ZÔ®ù·É˜¾iˆŸÿŽ«V¼hùLGð´ñ¿¨áó—§Ú”Ãk.´—ýôCãß7½û¶‡·ä åÛeXs`ÒÐuä_³-úÜc‹Gð“׃úbâW/ºS9üxÏÕ×o;¦öÍÙ»©¾:~(Oƒñâ«¿½µÅ7ÿˆÏ•ÃÇ&Ïx7þ_Ô¾¹8Ÿøì µ‹‹?©õ¤ê?™÷þÑ ØËñW'„ô|•#cn8OÝP£öÅ®xè¶ìjC¯V‡ÎOŠÊ­ïÌUÛ.ì24åp½o’×îݗ­Üh¶ÕïHâc.ðU©}óm«6å ñì|þà‚K³‡>¯8ÓÇw^¢Ì8µ žO3¾Û!¯“]ÿôà‰œN›r¤¡hŒwÜùjߢ“¶ßX›ÕÎ_Üòéï<¦öLœùЇc/¯‡]Æš•Y †ùwŒ§‚{Õ¾´«‹×=þ¢~]ªžÛ¯i¬{tx¼:äø?óQåß[Ææß3AX(ëã¶Ä}àÞg©oü¾Níã[jÕî7ÕÎßÕï0'ý³¸éŠ3/0ïÀ^Žï3•|ñÚ3k•WCÍ•j_ÛÔ7níû@ÿ<¨m½ÇOFìÜ…½ßgêÍO åûê’ª¥³fïSûniïy(0cx¼v>âžÜö’òý»Ät±ÚúæÎ·ß°zøû±CŽÿÎG·?ørYªòê ËŠ'‹ÓÔ¾‡m÷~qã#jçþ玬=ë}Õì®O®h8޽ïÅsÅ'k¨^¯Þ·!ø³Æ|µO]Ñû¯­å†ýúÖ>ú÷œß&ã}7]k|w³çoœuëp/ß÷Ës[‡?w½vÇÞ·5”OWIÁýÿuæ‡Cõo}ê—i×Ãß›åõðô–ë¶ü6cˆßÑ3^rÜý뛇?‡oíOí]ûö0ÏUé}±ÿù¡Ïa ù¹;¯|a¸N›åuòtæõ³.þ·UÊQyPûŸxbà•dµ«õW/ºèºÞ²ä?ÕÚñ“×ÃŽÐ×äLå¨ãï>¹#£UíûlQwGs‡ÚµøΉ\ô©º|ã—ÌìÂ^^;äõ¢½ùY÷îEíêÁÏ…æ;6©]\¥¯¸Km·ÿbù—ï;°—×ÃSâþQó•£òzTŽéY3éý;‡?wý‚p¦Úþ鉫Öÿ?9îOD┣ï‰áõ`è²U;ÛTtOòuEèv3{9îÛ­Ý}"aÛÐ}éØŒUös/PÎ8Ü|zÑÿ²÷PY]ÛºlAÅŽEéUÁ±DƒÆ(Ö`ÇŽØøM5ÍM 1ç,Q¢ ?j5É";VPzŠ-‰!Í“˜÷­5çÞ›ãËïÞ;Þ¹ïœ7dŒ_ÿýïµæškͺڜæxæ>9X‹^·Õ´K¼¬‘%ç[ˆþùmj꽘D/òźêÒZŒm%r'­žòÊž‡ÄÒç>ù@/*By¢ëÁYÏ÷}uU=½<£é¹’QGÅÅ„ÍMët|@äJ3ëT ;Uÿѹg½PžèuàÃS\;[/·?ºûݸ5ââ„’‰AýëXã¢ÈÔ^¤îÛa+‡¡Ñë@³˜}_-ŠÖË¿ØqyÛïÛL~¼¸tDE7ŸM½/õiŸúéº_ô®$ÿ-ý²…èùNòäþÞ·2ôж¼÷Vó¿‹‹ëü'ž.ùÞ»yüož›‹òDÏý¹>øþqS.*~Ô$lF É¿³WF}íèfõcÌ.PÂY|zNß½kÐYïLõ—轿mßë“ÊâõŠæ´÷/ß=öKƒƒ=EnÚÖ↱óÄRÑí™6 F¢<Ñ{Á3å¨âÍëX›cØ]qñÔ£ ËKEîªÜÏ.ŽÔC¿¥¤÷,zo%zï½Zw‡óM½\´K„¸XV9ý™'ÚŠÜåsßó+–Á‹yúW”'zï]ùki§¢¯ôŠk;Ú_Ùx^\¼v£ç'ísDîØ;9—®õˤ2vʽ÷¶:õqû3[ôÊ3½°ÒO\üqâÄã;º‰ÜÀÀ?öŸèbŽÏŠˆä“^{¶Zzc+Ñ=/S1‚éWTvù¶hØúAââ]Åà&çü:nÈKöÆbe˜öÐóÉk,=±•è'Í¾ß ½²÷®È¥Í^Å ?ÊN);,rŠô}¹÷*±’äå‰Þö''üZÓîi½Ò4,»ŸIçb÷5¿døÞ9çwºÀµ5õøŠ2ÏǦ»añÙV¢ïŒâ˜]ôÊ%{&×ëyQ+5cêÏœN¿ Sbêã{ƒ›Žpr³ì×V¢ûînß}ÿªcW½Rz‹KDqÿ²'¼»¿$rQÚuý"±â³.U\}<îÆ6¢kŽÈ3²÷.½R¹/-EqJÙ¡·/²ôAeèu'±âÁ¡¿½¾±ÅÛˆÞ»¶ÚvvoÉo•§ïÚî˜/Š3êôÞ´9Î’»zéu{ël–‘|[ã±øag³µŽdê•_ïqñž´WoÊØ;íÓC–}ìÒöµÑ&ßrÃ|8ÄÙ/\uÜsðº^åä¸""Šôûªñø}"W™g'=ˆýÁÇ^—Qø`‡›øº)ÇU˜Ì{îSŸŸÔô£æM¿ÈÐ#K¯wþÁ·È_Ï»˜Qõ<àŸl×[þÑ›˜ãSås=ýü?™ó¥â/.üqòV¦ÒØýj_s|V´’†ÿiãðˆ_Þ"?B¯êñã Ã#™r]\#ÒCäžÜ·üõÝëD†rãÇ¢ñÇÖ›[ìÜš«W ,©ûÕMEñ‡ÏW6½#rŸ»|èçQû…Íos塨7ân¼Eü±åÃ¥“̪´ÆA¹u-EI/ŽPÏ¢ëÐOß½ö°iGmŸýþíß {xÄ'›ŸøÕsòÜÖÖ8Lˆ ?t M”4Þ{Zz‘ä×Û|ç1®ÃŸ·äõ-⓬ˆÙ’zùÑ¢„è&råôpÏçÂ6X*úõ(Oüðwâ«?ó*¦ç>ð±I×’Àe»OÖÊš×4œ6±éµ3=…æ€G|òF—¹¿7wËêɯI×’8‡>¨„s¼ôöðŽþ"ã•§Æm™ÿ´¥ÇÞ">ÉŒ‘‚~µ­–35Q2 &Käüöû”¸Yô|‹øàö «Öü8xFOQ2kœ×¬gZ˜ãÈzC¬œóušO£¨G|ðò”s¯Ï͵ÆaÓ7Rˆ’Ç›N|}X¢IלêOÄ©GJsø3±²^³/{ù]Kþ·Ÿ¼À󺪼p@=7HG,Dä™sqIšXùÐâGKr1ŸÜN|ðô’´Ç¾z3ÈÔ×U%ºÃe%;>ÉØ.^ä|¶ëGsÜ3àÕÔ/d×v¢ÿ*ÂÃÿs»SÇœiÑóÀùXÇ /öþ¼òâ+'vé"ç„ü;ià/lñÒ‘9dñÕvâ“%é#ý÷Ú¾7ý¥ªê®ïŒÿþ®©ßK>Z9wÞŽ/¬qb=oø¡¶í‹½n¾ØðˆO¦¿Ô¾÷߯ôªëÍ~?°¿…ß©Y b7L6ÇÉV4퇇¿@=Åï›ýú®Cùחꈒ’¡SOØgÙ…÷ZôÛÞ©·1¨§øBO}oªïòþ'ôªŸC?œ2ã} ïêlŸÎ#~9Y1ƒ\_cÊëªIj"ŽúŠ?ôÇúøhq£[}˜ÿJ¾þ!H»òœeOŸ|§ìSÝê÷ª¿U-ºõG§¸;_èO<¹ø‡·¶ÍЫf-N¨¼mê©’_Ý»æGˆœ©kGžI«RçÛ½>Ƽv‡âý¹ðmïnhÍK«]ŽûßÔÎìÇ%Ò"ç‘§û ÓS-zJ±¯¾ 8Š?ôµÃÖ'ûs¶^ÝîòP·SõÅ%Ï?ß|¦¦³ÈI©óf›<aûäëð^Ÿ‡ ¼¢»¾~\çM0ñzµï°á);§ˆKQkÞýô“DKžVúun”î(l˜„~Ûõ}õ ° ?}¯W‡Žz¶²ùqiøÓ3š³Nä¼ÖlÙ¦z.(§è©gþÒÂ}ÿÁ SëË¿}9U\š¿áÖЀW-~RÓŽ‡ =ŠúD×7Ø^V÷¼Q>ªq#“.­½T¼$á°ÅÊì¹èaT_dt ^þÒ'€CôÝ´¯åû¾ªÐ«c:º®\ŽqÉYungd–È•\Úq¤XùÄ—]v_éw#›è¸Ù¯§Sð =zOŒºcê¹K½yü·6"—æ±z7öS2ÞÔò§~ÙËÒÙDß-¥ŽÔ½ó^ÝG.tDŠKïumöáL¯Œm/lJ–¢<ÑqÛÃJÁéÕñÚ/C.ˆK_&;žlånÙŸEŸƒÃ‰Uó¿køUdê=ßÊo)WŽôj²âÒãGœœ*0Gl5åÚ:±ªô؆—wŒCy¢ãö›raòe½úáü/ó—•‹ËZÁ±”F޽ËdžêýèAc\Å*^ß2ýôl¢ov÷ÖÏ?ž±E¯Vê#H\nç÷åo™–¼ >ÐΞbú«¾~Ê3è7µÆ‡èüö+cfõHŸH·ôø“ÿ/¶‹|/p®Îô£oZrLý·ôY6Ñ{W£žn>}öéÕÒýmtB\î4ê7O7óäàm¢wNIæÞë'òMù¯—¾ª]ûõârÊ‹Î/oúUä-{¶Éè±æzëQÔ'úî>»úTZôo¨wbÒ÷}¼Äåe·Ü·Öt¹°ös×GZ~ÆÛD_ûÜéý¦EµGyXÅÐÑâò¯ÔGþN䆺¿ùþ⣖_\դݼŨGôÍ›ðå/Sþxõä‚g¬¸œ›Òá·ø†_(qne®;Ø¢ÆN«ü"õ‰Þ{ü}Áß1õ|µ\N*=..\ü¹ß‘v"×O:ù¦ÿaÚŸ·‰ÞûÚŒë¹áΨ§Y\>Ógûú^&¿äFþòÅ™.–~$:Ï€CôÞWV–á}²ÖxË…b'S¾/WôHSÝûjóW–ßñÙ"r%_1ǯ#˜üø6Ñ}ÿ;ÝÖøÅXÓ«¥·¹Ô\»LëЖ_-ÍÆñwÍñ~œüZ«ß;‰?Þyud«CÇSÁçj]G\þö³ïÚ.M¹³dƒ'ÅãÆºöN⇙e¥=޾¡W“¾—Ý¥Ù“uDîäâšocŸ‡ÊšÊ?¤ù’Ežo_þ¥îSKïn¹ý'V½y-ÕÔ3K­:4Ó+À!þÈ—l|ð¼^=ìjðæN¿‰ËHGw´Èåõ‡Çʉm”'~8”™Ð´O£I(ÿ^LdóQZïúŸƒßIï˜~þ*Úw0ÖñQŸøáðZGxØ»,¹…Ó×ÏoˆIÇÒ–/ýùî—-þˆ87`ô@s½eÕ8µPkÌk—øãˆT_?eñÇðÆðfšú Ô»gÁ×ßÞ1øÕô›VÑ:¾ÞÖÏøâÝ%g&ÙЫ‡æl¼Û>[”vŸ³ñHÃï«zöm›óqMÜ]Dï÷Õ¼>zÏcУq÷ÅÚEéƒ3rÛ;µ·ä¬{vòc±—Lz¬JÎ÷½ãÞD7ôÚ.âƒ÷ëÜÙ¥¤z\. ¼!JÇví–ücï*Ãßæyã+VµzéÒ‘·-üw¼¿ïȧƒ~Õ«4Üžú„(MK9xøukÞ*—=¯^ÐㄽݺG–°=£¹lîñ“5/ÛEü!&ؽbüƒ>…gM&J3> yÔl‘{]X=v…Z€6æ†=E}â—Zw³èÝë¤ ‹ÞÏeiëvô0÷b }¹YNÜzXzzñOÁ‡#úžŸ©WÓz¿Eß— žéd3öô˜5Ñ«ÜÔ™5ÏÚEüRp÷eß_»¢W“Ý¥¯µÌs3Vì–Ë;/Ãî©e«•(OüðÙO½Úóó¾#6\¥;¾¢ÏBÝšÉÝ·˜EbU_µCw#‡øâƒ+Ò`B>[«Qºm”KÌKŸHu½heاg®Ìq;dÉiñŇÃ÷¿}åiË¿ì=ãSO•îÞ`«šfÑ÷ÂÚ‚\ÇËæ|jÕão¯oTUiÈàŸ|ÈëˆÕŽÛ7e–‹Ò#«nT?/roÿ´ÀÉñ ½'ùåæ8šë9Äu_9­iPKÓÿ-=öCñ¯I‡Äû$y²ÅûxÆ|ל§ç|”óñw«¥Ã_PðÁï/‰ÒÒ¥_=6ókŸë•›=C<ï/.ÜßóÊâƒÝ+²_›4^¯úB ¬(¥u±Û¾pǼ£óPŽèü1Fí©Ò%zÕEï·~ØB”Þ:vÒ5ÑWìÎ:Üaçç–½Ë!:ü«Z`³æ}Ç žh1'³÷—¿þùuý„%íÅîµrãp¸Þû¤R”¶dÀê¥/N±ô].ÑÿZ5êëUâ™éß}iòmYÝ1Ï®_ç&v?Yó#µ 9ÖâÛ\âƒOhŸL¯ÚwgöwŸmeÎàô¬}·÷Öº^¹^Øî$Ó~õˆÎŸ6pº‘ð‚^õöoWg=ßO”aV5éÉkœ+$ƒ]«^T õˆ¾ŸJ-¸lŸ5r;$)Ïœ§”9vÙÔ!ZþŒ¡¯û®¶uN¨eÇs‰ÞŸ’ß W½ÔµÇsö£¢¬þ¦Q?þ»Ø-Åê«çĪ3C¤†@y¢ógmŒ@½êñW6Žù›(«·/}ZÍN±[.«/ø@¬RÝjòDïÏ2ªžûnï.½jq£Ÿ‹W·°Æ¹^Ñù×¼ní·Týx¼÷Èá–ßDóQKßä?|ÆëAæ8)§¡›7[th®fL¿¤ìÍâÍ1]„½ñ‚]GÖ³ôÏsx¿ðˆNN=Ýç‡ÅzÅwã /Èeðþ<>éfʧ]ÎB'yYë0žÒ_»A­ŸÌݰ=«Åh½âøŽ®•Iã,~ÈŸÙúöKG…}ñ&Xêc?Cdþ·ö7ìÄ'o'æŽ|7C¯ØU¡)Þkõçƒ×¾š¼` °ƒ™úo?|¹æ¹y¨G|p ³Ži?•é¼/[V8¶óÇN;„Ö‡PŽèJ-GYç*ä¶ÿ³?‰2¶“†Ÿj_# J/s_Äð/} ýj'þ8…ÎBãé±8\±‹²£½·œ½« ûKO»§œa­o3Mýl'¾8Ízª-χ*Z¨‰’ñ,Ê>éºíµ;ã,:]E}âƒÓ©ÛüüʼnzùçÊAetžÂäOû®Æ«·­,Ò£•.4ô“e—íDÿÓ‡ªRî|]/WÛ n;ܘóQ»r7úš~Ë•e󈊔¹ë¯—ÓyQ¶÷ŧ§T…ö¾Îüm§uk="è^wkÆ#z9Í÷EÙî65ì³¥÷·F¿O¤_¿õ¶.l”|¸m8èžGt/âs:å=Žn¼ÞùQ–óK¿¹o¼ÞûûsÓÜ?Hœ*ìG/ëÿÂYÓ/Ï`=lΣóˆ/Š <ÿ ÑËI¿‰²=5yr°&ì'½öl©mޝÏ×ê7ñA‘Zf­—Ém–}E¢ì@³v]nEôþŽåÀ^òÍ«¢¦¿9~r4G¼fÍ»óˆŠ SF¬úæ‚^&O}ô¿z^­þÝùްóxz4C.£y|`ñSñCÑ-u0Ç ¯^¦–e½EÙñ}…nÎWôä©• E溿Í{ÒÞ~8Äg¼‡Ú¼¸B/ã}ÿ²ââµzAoòùô6ýšuyõ¦>“ {»ñÕ<œânì%úŸáýþ4OÔËHëц½ûüµž³¸šëy-ãä 7kg/ñÙÙj#YLþ˜^ ¦öÿl©>€ñ-´ ·–šò•×åõo—Ï}Ïò× ?d/ñÉÚÿÒ‡±_W*cÄåð…Zž9±Æœßæu¾h«{¢þ•ÿßׇößœás‘$fÊ$zéäÑ9ÎáF;¢ì{å°šýÏ‹½”)©1?âŸ3ïI@‹õäõ ß±òŽ^* Á..ðSû~z¯÷æñ¾ ³°ñùJ“/÷?9ÿªkÀëáúån=ª_VÛö)F;¢ìæ¤Ï5p4áä”H‹mͫɷøl/ñÙµ,9IÇô¹LëÃúhƒÎßUþÁ5“^yî\<þp’°¨2Àa>“£Ówƒu¾ë·¥ŸM´ü#š—šþD^§‡›whÜÝò÷uQ!ëœÍoL;T^·ûÑ+©¦~Ïóé³Ïï@‰¥å²ÎNÌ{÷ÿ¥ý ¯ä‡çÌþ›(o6xiyëÓNg\Kíö©æaûˆßÎöy´Õþjý²:°P”{t)ZØ1ZäuS"ã”ëC¾¿ï#>:;N¬1ñ¿t%íóJD¹C¹£múytÁÜ7íà>⣳¼ïfø—Ôñžž¢œæ"ï¥OœVÍuÐû>2ptø¶÷káAüröEyn~‰æ!¢æüÔ8¿bè+¦›%¯û™h~­—ÄÏ'xEyôQÿí¡}µïf­«ïgz„†f.tÐKÚȃ—?‰ò(5áy;ÆÊGaëæ‰NÊ3}ånÀÄžzñ•çƒbÞå=-ó{5Aäû°?ígºÒDÉ:.·MÊ,¿Ýô/ËŸ{Þ¹Üs¸EÉìýÃ_;@ô<×ì´­¬Û|ýÂá²¶½Xý|~åà/ò·˜ó½¼Ó#n¦Œ4ì.ê{q¿™yê©ý‚M^P˜o̧Dù‹9]^Lî+ò>îzläŽg,¿›æsÖ<ã@÷§rÊ#Ÿ„è|kJÆ¢|ݘƒŽ´´ìLþÔ/£wüÍÜdzmP8­yÓ¢÷9Zß1ùä‚[$n¥(ß8Ì'vÕLKojúö•›­s&¼om®“`~pÛÖú§ñôó•·;ùúwûr‡z·mòçnS,ýs€éïáÖ+:u¯~~KžœÑŠòŸí8ZQ×Ò›ûÓûý¶XùKLã…?¢Ó_¼e«?¯Žiwå…‘SÚ”6µÆA^ X~ÓÜ¿às&–ÿ|€ùƒöŸõóò6D÷6–~¸´øÏèŠN"OãlmΧVÒº‡¥2°wîªköìy?šëTåWoýôþãEÞjÃÍh_¬Ü:ªûï½B}æö[ÏíÜšëzöQQþc‡ßv#ö^Ö>ùAæµ<ÜÑ´—çæÈ…ºdQþÛ߆ÕL{#¾ÞØvv©ÈPÇÑPÏ ;í žë:ay»ËCE…Ãaó׬{ý–ÜâsÁì'ûù¨Çô•§ÝF:˜ëXg•AQáøìí^WRÄކæzô¦yp%é?Ôgz+wàQß³Ò=où‡¨¨›þÕÇ_´ìݵ+Îõ»¯4õ›5NLÿº;œ¿ýj¹ŽtöE¹Q×Û¤[ù÷œ‘ ècS®3ä¶t¶e¿28¨…# ¿Q ¯Œ¹ÑØôç*½|xðKιʓ,æùOc~fÎOóÙк¶~Öÿ³Ç`ÊEE‹˜¶Ï~ÏÒ;¬/øÜê±øvÚ’ú›Fëgèü ¨ð˜íéòäB¹ÁXôÉg½_ÒOÞ00åâŒþ GDEàæ9KÃ;Xx!;ú¬yþb%­ÿZûüùlh=Û\÷9³¾QUÅÙgÍuË }ßÂÈ®Zý`¿ÚðKWÒ¾*à±½çu¼3¼^1rÏØ[e"Oîf5,Ò{ñ8òy,Ë¿Égû¿qgô™}ô3ç>šÐ2Δ¯ŠùnšýÜÇê_4`ŸšóŸêøôýóÙ? s$˜w¾ß£bý›_xmØkËŸµú¥®8ê½Y¬PÛT“kû³Á1cõ"Z/Ûr~l‘·ÏÄk¯‹šèÄÝ8Ä|1(oOt«ÓzÑSϬ/í–¨ÀdÃ帿ØëáôÀ“•§¬ó°‡˜BŸX–àmÒ·(QüÕ-¾/|ºMh›9&ßËC+˺š~>à1¿ðytCŽŠøœ³9®çå‚ã¯b/Ÿ2üv㜹ßrˆøåÌ·w๮3ñ;-OKw¿—¯<~ð×bC®ÅÊvÎë,ê‚zÄgŠÔ“i‡N«e «_r{üN±¥Š#™ë‚+iÜÚ_;Dürf¯£Ç†é§Š;HÏÌ¢ï÷ùŒ.ÿ›È;qð|Û¾×-}ÅöÁêñÉ™WÿØQªŸš%¦Ýw¯ü–ã&òÞ'˜ú“‹#¯ ñÙô»rER?)ÝË•²;©ŸYz‰í"Û“¸‡‰/Î(÷w‰~’ÎÙ˜ô¨ì|dsàø‡­q ù‘uwZ·øü0ñÍu<¦~Rm_÷•½‚Ï-©>dÉË®Aó1u5ïý-Wj¦·åw&~9Cók“¾'è|¨fßþ{rŽX6vyIæÞ¨Ç|AçÖMúžxºnªçÉA¦ÿT9½IÕú‡C0ž?ýíç9¹¦ß²ôÇ͉ßô)·äì0ñKÏû ¾=Ù«ÿ÷ÖyƒÊåç§þ¬½)òŠ#BJßÙd®‹0^µÆ‡ø¤ˆö)ôãå__ÒÖ,•/v}¢a#‘Çû¢Ëåô©^>Ê?m– }wõ㬯*_uZiÞ,K_к€X~㇡¬ßzÄEr÷dö|ý¸dç‹Ç­þo™3|Þ¼±_Ô´“Íõ¶|Î×Ô‡GˆOŠø|Ð1Z·³úŸýùçùŸY÷¥òÔ5¥ºzÃ_Np¬q8B|RÔqƒÆçU*÷×93óµ?EÞãòâÔÇ&Wй>Ô'~9½¥ftÚš ýßKªxöÁ³oçuÍ'Õì‡÷~è›úâüQÖx`Ì'ÀNj;cˆw¨xjp‚ð>ïƒns®UÀ Φ«!Μ[ãR@ª^åSEP” E_ÃÐ÷°dŽ †çpÀŒðlŠí+c²ÈØ`]eÌ–kµò¦fRlýÀ‹XKy´"nSÎT?WÆ“9Æ"Q7°"ñÜÃâËü*&XçO- <[2ƯŒÍÒp£Ã(×–Ê™zcaŒzù‘¹Reü_Sÿ¾ v¸oƒm÷m𿳠–ò™ÆãXÅ1³R8v&ú¦ò1ÆqìÌ*ÎÙŽ÷Žk9Ö9Ê;‡ðìTÅqÏã8~&ê×üz(_OÚcè‹ú^œƒf5ǽÂ{gȘs*ÇЬáŽéœ»ý6ÇЄ4/4¬¢¸X’9†fçwDýÆvŽ¡Å9hÐ~“ÎÛ¾–s>¢n³Êûجó¶ÇsÔmŽºÍ1ÍÑvs<·ãœívÎ?ƒò-¯¥­VÎvÔo|[Ù)öºÊ™ÊùÚ£ÇÑ\˹#]8¤sµ£MW”wµs M/ÎÓøn·9§$ú㎺í\8v&ðkÞðpáÜì¨ëQÃyÙS­|3*';Ê{bì<‹8Vfçcdz—+çbÇ{/àÚï;&qŒÌÎÁžÎù×›7ày£=ïlÎ'Å9×ó9çzç[Gù.·)‡ŒêûØ9Ç:Êû¢¼/póCßýðÞo5åNW9ÓQÖx`ÌÒ('z ÊÆsôÎ8Ax”Oê<uƒ7íà‚qÁ8„`C#m…¢N(Ê„¢¯aè{X 嵑ù:ÃÃ8¹bªÉ8i*¯ Ú슱蜺¥P}£3ð"0nEd2d,ÕîY”Ï&°"mœÇÏ=Â8>ÇäÄF­å\—®”#HæÉéys—÷è,Š•&cqªÜå(ƒ~ÄDQü6—SÙù'í¯´½†­5ì«a[¥M­mO¥í”vRÚHi¥ 4lŸaó {woÜriפM“vLÚ/i«¤ªŸCÚ#ÃI›#mL혅FÎÚ± » 퀴RÇc þSúÝÐíRKÝ-õµ¡«¥^–úø¯òÖz÷^[[ŸJÝéBmÉÒ*hå˜ÉñƒÑw§LÎM ÕCÙz OýΩh˜Åù²@Ï& g“Ûœ‡å›&ÍÀC.IÏeš×PÞ«–ñìá{ëÎ_^i >r•~ºå©jçJ¹§Úr~)”ëPBñû:¿ŽÙ”'Êe¼íœ÷IúÂ(ï‡6üÀ‹~àSÐÞðüQ7<€2èG  å‡ Dù`” A¡ø-ïCKxåëêŠ~uK¥XÁ*ÿ“ôK)a$ÚìE¹(T¬ÁTÊïÔSò¸ôîû“÷ýI›Ã}òßÙŸteÛ©¬Š©ŸÊ1Ü 9Ÿ!x´ƹÎ5Îó÷ŽxvDÛN/§|ðìtc±'qÌÔ"Îi«‡qª]Sßóë€îõ 9§a>(ã Z9ßæœ;6ŠÉ.ՆбšÂy ÑF#Àl„çFÙµòð ~c”oŒúMâ9'O!©•Ó0“sóxá“Ê9 K8§!ðqÉâ|†À¥9Æ£9Æ£9ž[DqLö|ŽÉîE¹Æ[®æ|†Ðo­ðÜ ø¶Êç¸ìÀ§u:å2TyÈ›Îgˆ1këÊ1Ù 89ÚtEyWi#Pß-Œb²»‘ÚsGÜÑ÷ÎQŽºí€_;Àòp¥?¨ëq›ò–·OãìEÝï=³8‡¹Ç^üxöò✅K/èóŽ~œ×ð;Þæxë6ÊmÞ ¸y£=oÀóÎçëqœ›°€ã«Gq^Â|RÁ2®ºž}òIû¢¼/Êû7?ôÝcç\ýÛïüñÎxNꀆh3åñ.õ‚0æA(„1 B»ÁxÆs0ÞKÿ°BЯŒC`‡ N¨´À%0CQ'¸†¡LX*ÇzÇs8`„Fx>åiêG9»’i膱è–Fù–T¼wàq]º»P'ïíGÿHÔ¬HÀîǹœ )ObÊF·¨"Î唯9oSõh¢´#÷ú‰†]0l‚´ÒH}oèz©ß1nÿG½-õ³ÔÃRçÖÖµ†^5t¨Ô›†Î4t¥¡'ÁK¦^4ô!Æ[ú.uÐFüï”Íy#Ðg;ç„@[¼ÈUQ9R39–3äÃ%›\–æ§æùœó|Õýkú´Æ÷¶ O[àèXnk9÷håLh8ž~œGï:\ãÜvÊÚ u¼¥<ÅSÀ2ê‡1ó¾þøÝ¿û£^ÚÀû)h7pCñæÇ¹@íÿYæ*Pù ’)¶´Ê߉º‘à¯ñ”Ó@æˆ*¡¼Ò݉¶‹Ü÷óîûy6‡û~Þ¿³ŸçÇ4”ý„Ì«ÜHà •Óºˆó"‚ë`œêÜæ¼Õxïˆqul'ÈS*å­vªá¸ø(_7‹sWûqÞêBRõÃ8ïèZ¿ˆóî@Fœ¥ŽÍ'UÑ Žbè7@†h¿a篖óo´ÑÈóWÛkåÞAýÆ Egλ“U+w5ž›â¹)Ê6CÙfiœ‡±Šsï;çÞI¢¼ÕÍ H%©Ü;À¥…|FÛ-ý8ou&çÞqåœÕ(Óª€âô·F­WsîÀhüÛ¤Rþ6³¶£¶À·måltçœÕ¨ïŠúnè¿à»¾;prNîxv¯âœ;¨ÛcÕ°<ËϤU®<·G[íQÞå=Ó8?5`wÀs‡αSù©›àyÝ®•['Ÿóê^§Õ”—ºÊ{{q>ô­3pë Ü:VgùŒú]P¾‹´M(ïƒ÷>xïƒú>xöų/úê‹÷¾è›úî\ü€›êú£¬?`ù£” @¹” D›ã@<â96#ïƒ7íã9ÏÁxŒv‚Q?ýÁ8„`BP'0B?eBQ'í……qnô5 Ïဎ÷áddnl™»+èÔ ¸w»¥Sþ"™;¸G÷ŒswWÊ‘¤òaV$`EVd™‘I”¯SæÅŽÂ˜GaÌ£€[Tåë”ùdNìhÀFÝètÊó òù n/”í…º1h'&žrÉ\>JgË?ÃÎÖÎ,íë½ë…’I¤Ý4쥴•Ò.J{XÛvOÚ<ÃÖ6NÚ7iÛ¤=3l™´_†í’vª¶’ö¨¶º7§¯a_¤M©mKîÍákØi7ŒµÅ¿²ÒFüW탴 ÷Úc Ò°†¾7t½¡çïÕç†.¯­Ç¥—º»¶Î¾WGß$¨q'<×…|ÕEÙºÙœ›2ŠóR¢õQ·>žë£|}Ъ>ðvöÂ<ïŒö1N P¦ž NÔi}Ð0Žó¿†h£d´ph$íttc¼oŒúQ¿ Ê7IÁ'›ó“»rN*<7EÙfh¿Y:ç¦Â³Kå+wìæ®œŸr-å-oX-€K <·($uÔ2ŒrW©ü”À¿ðo•Â9Í IUµ>­Q§uå5oƒç6À¿ ÚlSÃù)W[¹)]ц+ž]QßýsóÆÇ ïÝ“{ ç¥Äx¶þíløßv·9'%êzr>JÔm÷íQ¾=Ê{¢¼'ž=1Vž(ßÏ€¼ïp›sPb<¼ 9ÿ$ÞwDùŽxî„çN€×)“òNvByo”÷¶q¾IàÖãÕ¸vAÙ.à£.ò{ç—L¡|Q>xöÅXú¢¬/ž}—øÄcâ¼üQ×eý³I- \à \ ~Ä÷@ŒEÆ* mƒÑF0žƒñ,}eà‚±Á8‡ n(ÚÅûP¼-"ÕÇù.1~áh7ïÃñ>¼ˆòŠªœ—h¯+àwÃs·8Î]øh/8GHð»ûQn2™ëRæ¶ŒDÝHÔDÙ¨Û#™òŽÊ|—Qh< u£€[T çEÝžÀ#e£1ÎÑÀ+ºs\¢ß½0½Ðïô;&‰s]J]#õ¨ü«m_ÿÊ®J{úWûêÒvJ[Y{T¼aÿ#XÛoþ«=wÃÆÕ^G5l—´[†Íªm¯¤ªm£þ#ût¯]ú«õWŒç?¬Áþõ_Ùþ¶å¯lŠaK ;RÛŸ–öã^_ZÚcݶ¶=¨mþJÿÿ•Þ¿wMWêú*Òó÷®íÖÖíßñ¸rBȤ†zu¼ðIãÜÂ.Ä2*pçù‹Ã'“ó¦3ä¡hÖ „r7ía|AÆfcŒac”ižj"õ+à7¬¦xß,Žòþ¹¤qÞ_¯Zù~ÑFKŒY+ÈNëxÊã«rø¢6£¶h§-`·®k‰]Ý€‡›Ô€ã޶Ü휣í´“ºuÊ×Çûú G}èg?ΜÑ×(Ó лê4D› ã9l!© •?¼Ô¨€TGcàÔå£~/|ÀÃMìœ;𛢽¦vR-Í«`5Ë'5ãYvÁ{ô¿9Ê6GÛÍ3)—l ÀjÜZÿxßým Ø-Q·%ƦúÚ*•sÇã}k¼o \Zã¹5ê·îmÐ^´×¸¶Áx´îm¿-úîŠö\ßÏ®¨ï†únhß ¸º¾;Þ»£=w´çŽºíâ9_|!©<ÀòÈä\ñh«=`µGÝö(뉲ž#O´Ûp; lÀêX^xö³×jÎ èˆçŽk9'¼ çƒG?:‘ õÆ{o¼÷Ƙv^ÑïΠag¼ï‚¶» í.xçƒw>x烺>xöÅúâÙe}—ÆÌãí‡>ø£ž?Þù§´€6¤Žr K Þ¢?« À Âû ¼ÂØn0Þ£à"RÝ!ûŒuà…F(Ú EPô) }Ãû0ô! åÃÑv8ꇣ~8Þwõâ\ôh¯+ðî†÷ÝP¾ÊwÃû´ØÏàÓøu_K9è#Q6u#·àôH¥|Ã*=Æ- õ¢òÉdô]zbÜ{¢l4ÊFcÜ¢'º„ó΃F½P¶ÚˆÁs ÞÇà}ŒÔR—Ë¿¿òÃkŸq5ìã_ùàÒÞý•~¯~¯ÿ-íRm[t¯Ýù¿ågÿ•OýŸñ§kûѵ}æ¿ò“kûŵuí½gþ#_XêQC‚¿ê`ë€Æu@#GÐÔIú¿xvÂs]ü_cWOê;ðƒ3à6ÀØ4¬†ègC”ižh$ý]е1èÝ<Ù´o"õ`7Œf€á‚÷.øî^i:µF;­Q¾u6¹Em§-ê¶E;®øß ïÝЖ›Ô€ãxîä2µ¼vRw ŽÊz¯ö^äByâ]Ô퀲«(/³7Êy£¼7ðî vAÙ.è{<û¶þ÷ξxç‹ñ÷Ç{Ô/—ƒñ{p îáxžÆy¸ñêGH4ŒG$ÞGâ÷iÄŽQRfÐ÷žÀ«'êGûQ^í^¨× }ê…þÆ·i+ÿÕ|»ûë¯÷Ï,ü+­ÅB¾Çç|~Äç'|~Æç>¿8¨s ¿âsŸßðùŸ?ð¹‹ÏŸ¼~‹4Ìí4Ìí4Ìí48~ævZ}^€mÒ`›4Ø& š†¹†¹ÖŒ}LØ* ¶JÃÜNÃÜNƒÍÒ0·ÓÚòýgØ. s; ŠJƒ££an§A1iønævævl›†¹û¦an§ùÒÝ ò¯Aþ5È¿ù× ÿä_ 峸 ò¯Aþ5È¿ù× ÿZ>Ãù× ÿä_ƒük ò¯é¼v ù× ÿä_ƒük ò¯ äõÈ¿ù× ÿä_ƒükí!ö¯!ÿä_ƒük ò¯Aþµ‘|÷ò¯Aþ5È¿ù× ÿä_ƒü«{i ò¯Aþ5È¿ù× ÿä_3†ük ò¯Aþ5È¿ù× ÿê| ä_ƒük ò¯Aþ5È¿ùWëò ò¯Aþ5È¿ù× ÿä_Í ÿä_ƒük ò¯Aþ5È¿º·ù× ÿä_ƒük ò¯AþÕ:È¿ù× ÿä_ƒük ò¯ÎHCþ5È¿ù× ÿä_ƒüku¶ò¯Aþ5È¿ù× ÿä_ƒü«=È¿ù× ÿä_ƒük ò¯æ5 ò¯Aþ5È¿ù× ÿä_ݹ‡ük ò¯Aþ5È¿ù× ÿê> ä_ƒük ò¯Aþ5È¿ù—ç»5È¿ù× ÿä_ƒük ò/Ïi ò¯Aþ5È¿ù× ÿä_î‡h ò¯Aþ5È¿ù× ÿä_®µi ò¯Aþ5È¿ù× ÿä_ÎÙ4È¿ù× ÿä_ƒükRþ埴ÒI;°–õ™„)åZÊ–äïkXD0Õ¾zŸ[Oã»vòyäyO¹æ§îE&ñþM&ïá”ð>Ž+Ï“y¾˜Iëƒò¾¤Ú×qå¸I¼f¸–çE¼~èÊ{=É|&*“ïS–ðÙ(/ž[¦ò9x;‘R>— Ç8ˆç;–«éLª\ƒTñœùŽR<ûf6¾wYÈ~š Ÿ—Oâ}¢¾Æw0Ó9B>Ÿ›c?.×1³yï(ŽÏW¥ó«|ÞCrà³ôù|ÖÏÔÇÓ¹zuÎ*ž×;×Òy+9_Uw5ói®ªÎÕ»òÙú¾7Ççl¼ŸTÈ{J|GÓ™ÏÕ'ñݪµ3A«·ñ™«Bºo¥æ®®t_Sž½Rë§.<‡M¢³öòL®ô5Õ¾’3Ï_ãiMUÍ]Ó8nB!Ÿ}ð"ÿS½òãù«÷–ŠxÕN÷¸ÔÞ’ÇNH¥3¾j½u-ñ•ë®ê¬½+ÍeÕûxò]ÕYûL>‡uïlºð½Íd¾»i§ø êÌUŸ¡°óÚ¬ù¶êžf ÇKÈ¢9¬:{åÅwIJél½Z«uµæ°*NÂjŽ•PÄþ° Ý×T1l|¶¢÷•œioIÍWÓxÎj§y«:SïJ~³<¬æ«%|çÌ•ÎYÉóÈrmWÝËÌç3V¼ŸÏg­2i_IÅJp`Ÿ;ŽÖÕÍ,:¥îm:ð¼6Ž×…Wóü¶÷žœù\V<ŸÁ·ñù¬>£åÀçñ“øLþj¾óV@þ¼ºïéÀëÊa|†+ã/äÓZ³ŠÃàÌsäx^w^Ë{X…¼þìÌsæx¾º–ÎyÉyA Æ'ðc1>±ŸXà‹º±¨‹ºýÐ^?”ï‡ñìWE.}àÜ}€wðnÞ .k Þ Œ'w?ßãQ.}Üxô-p@ã´—€ö@ô'}NüD”MDÙDàˆ²‰Àa0ð ü‡Ï!¨7õ† ÌÀ‚v‡ LÞ%á]Æ2 x%¯$à•„÷Cñ~` Œa@lÆ`ÆzÚŽzÃQo8Ú˜Ãs8Ú~zÏàù0g¢™€5°f¢îLÔ…6f¡/³ñ<Ïsð<}˜ƒ>̬9èCðJŒ4ÀHÃó<ôêÌCÿàû|_œ§‹h{!xh1à,Æï‹ñûbü¾ý\–H{\Ãöûþ¾Êý¹÷ý¹÷ý¹÷ý¹÷ý¹÷ý¹÷¿ÍÜ[3â I'íL:ëËBÒJv¥üHNuø‡øòÎ:#Å÷‰Ò)ö†‡™Æ{½ù´ß«b¹ðýô$Ž;¸šâ…Èó™*þ +ßcJ¦³Bꌦ+ŸJásCY¼Æç‡Rø^S&ÇD*â;ì)›0›c#Uñ=§,Фî99ÓT¹ç"ï;©3œI¼OœIçÿe|sDÞuRq øÎj*ÝmW1 ãù,çj:“$ï¶«}cg¾ÓîÂ÷’ùn{&ÅKR÷VÓýy·]žÉTû;^óDîË;:êþSï!ÛøÞSï»ÐÝ'ywIî!«ýžtŽ™TÄg;ý(v¡:×V+n¡îÓª}ä|¾ãîÀ÷Ü“x9“â%É»èrYÝò¢»2Þ‰<)ï È=Y¹,÷}åOµäÀg;ãè<”ºëžO±“Ôý…t>×™Ï{ÈηPÅhIåXIÙ³Ðâª{ívº³«ö½¬˜…*FÒZŠ“$Ïuªx…®Ã%•cÚéN»:GåB{Ç*6a:Ç'̧½+u×É‹ãfrlÂ*¾ËîEç9ÕÝ&ßc/೜Î|Ö*‰îDtÏ¢3W*N’3Ç+Œç}äÕ·°ãÅ8süÂxÞ[^Kq åýbuFË…Ï&ñݨÕ|¿¢ïX8ó=©d¾+µ–ïÅRÜ™¡E|wÊ‹ã.%ñª,º—!÷¦U &ÞsKâ}êL>ûUÄûÕ.1‰ïÒgÒyÒ˜*rãc?ã‹ñ‰ޱ¨‹º±¨ÛíõCù~Ï~À¹?ðéœû£ÏðnÞ À»%äêÄ»h'åâñ=åâÑ÷xÀGßâ74N@{ h/ôH@ÐçÀODÙD”M‰(›ÿÁÀð‚zCPoÊ ¼!hwÊ$á]Þ%a,“€WðJ^Ix?ï‡Æ0À܆a †a¬‡¡á¨7c1u‡æpÀŽv‡ƒŸÁó\à5ŸÙèG*êMBGð~$þ‰ßGçdÀN}’×(ôm`B?F‡Q(; °F£1h{ ú;uÇ`ìÇ¢ÞX” ¸c1co,p‡²ãÐÞ8ôcð‡>ŒÇxŽþãÑÆxà>8GÙGÑÞÔPDÓŸ‰(3c0íLD;“Pf2ÊL~“Qw2êNFÝÉø}*pœ §ây*ž§¡ÝiÀqꦢÝTÀLîÓñûtü>¿OÌ™hc&`ͬ™¨;ug¡YèËl<ÏÆó<ÏAæ s+ 8¥¡~êÏÇû4ôiú?uæ¡ÿ ð}¾/N €ÓŒÅ´½<´pã÷Åø}q!MÉ–‡%ømê©y‚ü»gìŸg tSs$ÐE͇Œy12æ>rÞcÌuäǘÏÈ9Lí9‹œ«Ü;G1æ%ÆüCÎ;äœCÎ7äÜ¢ö<ãþsc> çS,¿_úûÒÏ—>½ôç ?Þðá¥ïnøíÒG—~¹ôÃÿ§|pÈÄ?øßµ}ïÚ~·ásCÆ”¿møÚ†ŸmøØÒ¿6|kéWKŸÚð§!wÊ–>´á?KßYúÍÒg–þòuö“¿¥x•òL·:K—Égó\莥:—Eñ(eŒgußÒ™bDÊóuò^¥¼Ó"ϾÈósò.¤¼C-ÏWË»"ò Œ<-ïy«ûÜét–[Æï‘çàäï,Šç(—²äò–¼K)—©Ôy¶(ZÚRñÃ(>ˆŒ(ï<Ê»Ð2>ˆ<_&ÏÉ;-*nqÝÑ–±å]yÏRÞ»–1DÔù:/ºS)ïYÊû(òÞ‰¼["ÏÀÉ»ÕòL·Œ…#ï“ÈsoPoê BßÞ ôíAÐspxÆó :ëý´¡ÒnþÀ0ÊzÀ7QÚ9ÔK¼å3Ê F£~"ê A† ÌC(ÿÊ Åø? ø×ð’Ú ÜF£Îhü6ÏÉ(7RÚ->ßTiÇÐîÀ#mÚ˜‚²€Ï\ÀKEý øLSñ~~›=?}™çÙø}è5å“c6`Íìù(³õa¼æ£N2ðƒ2‹€Ã$ÐpêOÅó´7øOÀïSñ}.¾Ï@FaÌf£þ à;Ï€ó|Ù&àÍ@æâ÷T‰¿l eçâ·Ù€±í¦J;>¤âû|Œñ\´½8ÌžãcÚŸq›–þáy>hµHÚ1à?íÏ–m¡Í4<§a|Ód?ÑÖ|´¹0Ò@ãE²ÊÎG»‹Pv‘|‡ïièÃ"´½è6-.–0ð¼pÎbŒ×biÃÐ÷Å€¹õ–€Úç¾ìýuãûëÆ÷×ï¯ß_7¾¿n|ÝøŸ»nüWç´¤½Ï*»acýWDz@É¢”‡$拵Ü?‰Ãm¾‡G1PÔ™ô,º“Y§Šc¢xqªÎ!“MydT<*gŽ=šJñ›UN™l:Ç®â:óýûŠ;*϶×Í¢÷*¶ŠÇ¬Š§ØÏòÌ»Œ]%óÎ4Èâû?aû>ݺ£/ãÉË{ú2¨º ”ιh 8v©+ÅŽR1“)—‹¼¿/ïÊËx¦ò©Š‘ïÅwùSÈ‘qHe¼Au_(•ï eSΕŸÄ…bRÊXÐ2‹¼GÓØÆ± S9~¾bžª<6.|Ï¿ãÁ¸pL¬dºG$ÏЫ¸§6Ž}Z@gõU ÔBŽ¥ïÌ÷ŠâéΊ{šFqå½"‡^ÅÆr¡ø…*&–+Å“Q9ml|¿¨€ïü¯å{ÿEË™cÎPÌuÀ‹ãë§Pu÷¿sÛ¸òÝ€dº rÛØ8j ß3 £øúêîkT­Øúù'Ëî©Ø‹Î'+™sÜdQümyï]Åɪâ˜6~”çFÆeT1Ò8VV6Ý9’ñ²TŒ,/ŽÙ˜Jq;PÜnu÷߯÷Œ øþ ÅÖWñOÓ8ž·ãê;Sl}×1Ÿcc9pÌ›xŠ«¯bŸfò½Zºc$ïÓÊ8¨*îéZ¾[TÂ÷‹\)§ŠŸoãúœËÆòÙ¨ØùY?ÿÇÂò£»¸*NÎjŽƒUÈ÷Š\ønQ2ßýϦ;F*þ© ÇÔO¢û*F–î©x¨.c?‰îN¨˜Yù7KÝE¾ÕðÞ4޽o§x©*f€ Åã‘qøBù¡x7õ‡÷¡Àa8pŽ2CKèÞÒÀŽÝ“M1U¥›Ü/•ã DQ,u?#Ÿâo ­â»®¿?™cqeÑÝáô1mÄb|b1>±€ cQ?õû¡­~(›€2ý€S?´ßøôÎýÑø¼€÷ðnÚˆrñn Ú‰G¹x|G¹xô=pãÑ¿xÀMÐ^ÚK=ПŒIà'¢l"Ê&‡D”Mþƒÿ`à?xA½ÿÅÞyÀGU¥ " )Š ¹“„„Ò{B&½’N2!BRT@]ÛŠJI,ˆˆ{ƒÒ(¡‡¢ˆ ¬ˆíÿ=sÎÌeYwÿû¾ï¾ïî¾æó™ÏÜ{îyêiÏ93óü&C7™:“á7¹“…/xˆÑ)‰vD¯@ô äyσá `t ÆÁÔ FF(t¡ð …g(Ið‰Ã/qø*½ã¸O€_¾™BÝ$±¶Q/SÜcs´ BvOEFö%âÓDd%ñ< ûÅšm2tÉø0Y”S'¾©\§!/ ÛÒ OƒnªX‡ðÝTžgPžAy†(otÊ‚W¼²à•ýT|1•ë<ü5•û>sÐ?‡º9蟇ýyÐæaw>vçcw>2 ¸/ä¾9…ø·ö+Bß"dSVLY1v• k‰˜Å<-^×ί_;?¾v~|íüøÚùñµóãŸóãäwÇb}k”X'*Õ|×"ǽuì‰þ©úÁeO«Â©¨°þÒù™ ÊŠ[Õ^á‘û(üªå*ßv³Ì¹m̓2\a£æ)|Ô*‰SnÅ;pTù¢|TN×J•×µ^åvm¯òu*¼«å ǼYá¨:©|¯‘ Ï|ƒÄ´æl¯rK™¶y…ÂV­W9XÚ(ŒU“Ê7eQ9§jTÞ©6 sÕGá'T(Üó*_KžÌÙbÅQhVùÂk¥Ìá"°³F5/UžÄ3µbh5+LV'™—J`hYsÇ:©ü±f™CÖÑ$1 ¬ d®CÇV…“Ó¬ð%FºÀ°æ’­P ‰³ p¨n«Q8éN*7y•C6]a-lPx ‰§%0 D®Dk.YG•O¶^æ“jR˜é&…׺\æN¸ZVœÖ> /Ý,1Óï4K s‘GÑŠ­Õ^åS4«œU¦+°µjTNÙö+ÝŠ¹à¨rÊš^ë•·jƒÌnÅkm¯°æB¤Âk].1[EÞ.òËZsWÊ6vì… *gU¥ÂI¯WXéN[ËŠdQ˜ZÛF¤£Ä‰´â,Ô¨üéíUŽÆ@‰9´Båi¬Q9«Ú+œt…¯°Fb£‹œê"Luí#ñY­øY• C«^á²¶—جVì¬ ?ËŠÇê¨0´UîØå2÷ºÀcµb¢;©¼:f•ç±JæØ±b9)L­H…•¾Fakµ( #'…U©°Ó×KÌÊûZU^ž>*¿U¤Ê¹Fæ¹xGÖ|‘N*mºÊE»Fâ ‰ÜW÷·*\.G•ÏǤ0×+%F’ÈSkÍñÓFå™4©œµ Ï¡Få®m£òך&{…Ä„ù²Æbã8øÃ?ãðÏ8ü3ÆA;ÚqȼñÔ¼ñÜ{¡³:{aÓžMàÙžM  ¼áåÍ3oäøPχkêù`»|}°Ý¾±g"ò&"o"í1{&ⓉðŸDÝIÔ„“Zå6ÁýýÑ2zN†n2t“©3y›Ú> 7gô›@|ˆ^èˆ^çä¶"ÁðF·`|Œ‚‘ ](±È‰m‘Ûš8êÆá·8ìˆCN6LÁŸS=SÐ}J³ÜúÄ#/Úä$â‡Dê$âƒDä$ÖÈ­P2r’)O†6Úäfµ=BÇTtLm–Û¤4䦡c´éÈM‡gºgPžAyåðÌ‚W¼²à•Õ,·SÙè‘-S›å¶*‡ûlÈÁ†xåÀ+½òà‘puº¶( ™ûöH™‡p¸“Ä.½¾Ní®XžÂŒ­RxbÐõ0©¼àé2¢'áœÂŒ5)¼Ø> 'Á¢°ÄD,KúrÝÛIåb­T˜ŽÁŠK;9cg?tîg‘ùU­ØðH_dùnSx•ðœ÷—8Öܨ"މ”Ø]øŒÀŽäùRîÛ"ó‚û¶J\.q\)pV' WX=éËzlIýä%!#‰òûh›ÊB  >™aðZ/sšFDJ ®ꆡ{︎ÀÎÚ3ZÄèý`¥Âk:t è6Žºãà3]’x–ŠÿDüE}oìô¦N:÷S)÷A§êODç©Ø’ŽÝ‘;‰ç™ðœŸLî'Sg2úærŸ‹_r)+€w(õBy6 »¦aS¨°ža|Nãy8}!b½<’ä:RÄ:ÈB^…QÔ‰¢~õ£‘ƒn1ðŠ&¦YÝÆR7½b±;¹±ø+Žºq´a>‰£ßÄ¡[>™‚ß§ c ºLAÿ)ÔöC›€œ|HD|•ˆœDä$Q'™:Éè— m2´ÉÐ&SžŠŽ©Â‡Ü§rŸ†Ü4tLƒ6¹éðLG÷ Ê3(Ï <žYÈÈ‚W¼² Í‚6ÙÂÏÜOå>‡ûlÈÁ†xå`CzåÁ#yõr¹Ï‡&ß,š ¹.D§Bt*Ä…È.¢M‹áSLy1åÅ”cg :”ÇÃËŽùÔçŠër~·¾þW¾¸ö]ĵï"®þ.âþÓµïþw¿øg~÷ðã{‡+¿søOû¾¡¢Í¿Õw ÖÝÐtØ ñ!AÆË--'ÒŠWCÿëè$q]:2V:^Xº×ËŠæ$óyw¢u#ñh;ù(lÝt‰Oà°\áê2O80Þš%Æ®À Xýé ƒðMgúSg“ÂÚ5K¼›.èÖe›Ä.-ÿF`S‰<á]}Î.²ºZ$Þn7xwCÏn—†X‰µ+ðqT(œ‚@‰¹.0¬ºSæ†.NU kýœZæn™k\äï=Z–åÎð¾“g=ÓÖ.vªT9Ç·IL/+nûp…%&î‘׋ú½áïR)1úšeÎpSÛÇQbÝ \w+Þn…Â.€gß*‰•éŒþÎèï /çt‰—iÅáY®°wà×Úþ sÚþÐjN£v€xcË€f‰·; Ubñ¬‘×|ϵH¼x£)°uN€ÀÈÂýÐöGSC††ì•§À…:.ç$–˜/ož»Ò~®ðv­‘8b¯ÀŠ¥‹N#ª$¦®À8^ðñ♿ 룰 ðŸ‰O>4aŸ ûLð6aŸ 4´• Y&äšÐßO<½Et2¡“;z»Ã7 SÇ~îÈrÇîøÑºîðrG7wtsG7wø¸ã÷ 2,ñ€Ö½<ÐɃvð@ôñ€Þ<àáxxÀÃ9ðð€‡'òƒÑÃ<±ÇùžÈ÷D¾'ôžøÒzOlò¤xb'öxÂ#žòHÞÑðöÄžÁã’ ‘ÆÃw<÷ã…¿Ði<¼½ ó‚ÎKø•z‰ÐøòÜúäy£Ÿ7úyc£·Øû ‹/t¾|fˆýzùRϽ|ÑËü¸öÃF?ôðƒ/:ú¡£²ü¹Á9ÈÉÁWØ›Fy<üðOtÐùÈΧ~2ò¡âyσ“Íó ê… +„²®óáByu3°% =ÑS0ô-ÁŽpdæ:Ê0/B|bkmM|tˆ„>’ëHxDÂ/›#ñM4ºDSMY4õ¢±=– ÇŒ3ü‹ÐÏŒmfüa·ÝÌèjÆ'fxš¡7£ƒføšákF?ó%N&@“€>I|&‰OôJBF<“O®Sà™Ïx¦À3ž)ðK_šð%ö¥Á/EÈJ‡O:|2Ñ/?fÂ'™ð° o6veS/›²±)ÙðȆG.òr‘K½\|œ M.~ÎEv}©€¾Tÿì/€ü à_¯"xa_‘ØëÀkõ§QßB¹– 2Ä-EÏRx”¢c)ò¬ñ—x]¹¯ù½ýŒØÇü^ž‰k¸{ÿãî]GÛâg;ÿ½ü">¶ÅÅWÿŽú÷ÎEü{eÌû{gÉ"ֵŸ¶øVĵ¶˜öÊXÖ»Nø±ª-N½ò|Ù—^}¦l‹Amñg¶Š5¯Ž/ÿVÎù*~|Nb@ ì>‡dÝÆ_RXÚŒµ• ßd¸ÄÐdâ¨p³‘7DÄ$Ë%.¶}\è³N$†ßˆ1§O` <”ë)󑸂#)Iý‘yrKß‹9 ²Ü6|ÝÄ=¾vC¾3m3H”¡ƒÛ‰êR!qÝàã†Ü•÷ÔO7!‹2·ÞÛ$ηýÓùh÷Cĵˆ?ÐÏMÄ ØëJ}­Äïv…·†Œâ™6ޤŽ„FºCã…\çÞâ¹¼4h5æ7êyÃχ¾ª]’G&døaŸ [üEl‚,üfÂ'<  <ð’<–ðGFuƒ( †_Ÿ¾Ô ÂÁðGf¸X_ÑÍ—záèáOY4÷ábÝÄ/Ñ"ÞÀÑÈðë+öÄó<â…L®ãy/èà‰¾ñ¼C/ÖpèxÇÃ/¿%b¿õýÅÚC±þ ;Óðoºeˆ5?¤ÑÆAÈË€.šlìÉFN6rRÊ#“äç@Ž}Ø”œ|øÄ×Ë#’Hø§áÏ|x•`_ ÷¹È-¡^<º” §d›œJ㱩¤FË” ³„ç)ø ^%Ó(KÁ®±žâ×4±Ñ™bmB·Lô.BV6mj×È˥܂ϊ(+BÇ|x棻>±Þ¡‡û,bM…Ö"hÑÏCÄ]Ãå~ËúºöûákxÿŒß ÿ»þføÿÕycü_zV÷ŸtN÷¯øð?zVwõïƒÿ'ü6øÊsºæóDÊ:0f;ÔHL½Œƒ[Äû‚Â¥oõX.±*;ÒO:2:^’ËŒÀß¼•²´Ñ­Ãf¨ø¤=;Ñ6Ì {[¨ë°Aâ‡:0Žðr@NgG…E }gúJgøu¦ý:§+ Qôë‚~]ûH<Ñ.è×»^áˆ"«+²º"«+²º"«+úv;'—¹ÞÍ Çýo‡ÿíÐÜnR˜Èè]wät§½ºãs'ôs‚·ú9ÁÃé’Äu¥Nxõh•×V\QÊï„oOäöDnÏJ…+Š=ÑÙUĪ>Oº—Iâöâzäz‰¡Ù½‘Ñ~½/)5žàÙtÁ³”h•aÁdŒÂöQð÷ñ/2üá‡/F¡Ÿ‰v3áC“ˆ±Ñ„L?ʨc‚§ y&ìó×èÀ³ ø™àoº Ãwx»Ã×wž»ÃÏ6qÏ“a„;>p‡ÞýÜÑÏýÜi#wtsG'øy ôèäA;xˆX]<  Gh½ø ±´^âztðD¾'ò=‘í‰lOd{ÒŽžÈçŽþžô]Oè=ŵ°ž´Iˆ …Ÿ'öxÂ+>‰”§<^Ȥ~¼°/ʽ¨ç…\¯ 2\š€îÞèèM]olóC®?²¼©ë __tòE'_tò噯ð ýÊ—:~ØÄ3?ãÃÃýüå‡,®ýáéϽ?|ýE~ó‡oímtÐP/ˆX9A\Q'žºEðAnuãÑ'CØ(ö܇@‚œèB&blüRBYta\‡Á/œ6ŠÀÎÚ(‚6Š R¨[ÿHêFB‰~‘BõKðO4eÑèk†¦}¢‘mÆ·fèòÅ5üÍðÆŸfô3ã3~1ã3<‹Ð1Z3|Íð5‹z螀™èÀ³±á3I|¢SüÒà+®á™"ö8ðLg Ùø:_ec[¶ØS“á©Þ¹ÈÏÅÖ\±ÿ N.õ èGd`w| à[­…wü‹°«è‚üÊyu§á# |-ðµà÷Rt+E·Rt+E·R›‹T¼®Þ§\óÕ¶ù½38±¯ø½s¸«Ïà®>ñÿ•1ÿÕñý?ëœí÷ÎÔDüýß§]yŽ&bfÛ™Ùï“]y.veL{u®€¿u&âÕ«ãÔ«sØâЫϽþÞ9×ïW‰8mR›ÿþ|êÊó([ìtõ¹“ˆÄ™Ÿ]xw¦õk}¬c£?m?€ûA> ›~:ˆ¶Â|áb–ع®>Ó·7}ÛF˜®6õzˆuˆ²‘‘+|$enÈYÁ:Í#áãL_wÆVgdºQÏ ¾n艵zhë‘ðq£®›ø¤-Ý(sá~¸GÏ!Èv£}†pívAbo‹®ªÁÛ…ú:jðö‚÷Hžkèç ›5xi¢¾¨ÇXÔÄš‚\íœÜû‰5>âž~ø$ˆÏ`ê#ÓžîØ.Þð‹æy8ºE#Ó[ÜóÜýâ)÷ó‘[åxì‹§<=ÂñK4´ÑÈŒ¦~8u}ñ¿X³Ð+^¬{èO½Dt÷§^ ÏC«)èßlèø6ϳô G¿x숧–ˆ9[ÌØ‘ŸR¨[$æ'Êò©—‚>EbŽãy<2ágw.rr¹.Ú ‡¨…:ž[ð›e½:6#¿û³ýnNüÿ]üž_üVNä§¿ç[ fã»ý%~O'rlO®‘ÿ½Uó¨À¼ßuˆÿ ˆßÒ L.1uˆü«â·ìÂ6ñ?xñÛ.ñ?yëoµ*T” 2?ŠøíVF™ŸUüßúÛ÷mò÷\"÷¶ø¼øº5_JõÛv'õ[¯áòwî£Oäû/óÏÿáËTn:?s½¸ÒvGt}³)ÝWëpïŸõôtÑN¿Q1jêîcZ—¨µÇ÷ütHßÕ7~¯SþY·ƒ ó? ½š“¾iÜü?.y¤¯æ£­ødÂÅp½ü·ÏÚM,¹K»×Çt&]rj¤œçÞÞÞ!8C;½îÑùqÏ껼[×K×7ÍÞt~Qñ½Z)_/w~ã1ç­÷4Gÿ°»2 o•ô_ö‘¥ÝxéõÊÓ/j§çdnølÂh·ßd=}WÒÐÞo¥º½;"Ê-þ }ÓÚ}=¶AëÛá¶Ycv{êeŽè7?ö²íÞt~–UmϰêÚ{#µÓñó–ÕÛøë»ŠŸùvɰ}s[‡JWìМ_<<ìž½<²Ì 1Gµ«÷ÞÿÛ1øô‘|R­/íôدóÙ¨Ñ?? ¹Úü<«ÂÊ·î¶L“ÃöJ»^§NïѺvikøeæé™K^0ü¼ë¾¾GǾ§ YãùRé}æšÓ§îkxRx6b岎ká+ûY]Œ‹¿©ß«Ú©šœ;Þìô’¾kþ€;÷½Õj·sÓ£í%Œ»GŸ9üº€Å‘Ë “ý¦nð…‡êÞÔNý)aàý73ì[à3vËkú¦•oüÚ~óOÚˆêCfµ.Ög¸ðqßí²ÿÂGöŸº¯?ZöPþ{Ú©åNB3›}ú®gÞñôMëôMÍÁLlüÍnGÙ{ß;Ã臲¿Ô]û¹^£*-ßµî—}×öíe+òŽé›¥Þz™µY“¨/ûEýü›õ?&>£ _õñC÷|kŒ«Ãá! /Wé›KÓÏͤ¾ìõõyþë“öñxj¤õ¥ï:ÿÄ8—…?ÚÛsóê¥ï?˜¬ Vã¦ì+ßáçë »+d?ièØ¡yÜÙÇÍ)‡ ¼½î׺ÚÚóד©]æ.7úÛºÌG¾^¾Q»[Ž_½|LÕÄ­m·ã¦Bö“†èv¯oo×óäW£Ž=ð±Ö]õ¯Zë° ×7o\pSz¯}öqXníæ7óC…ì ²ýìzž<ÜUßp½ßÕÞùÓû7;ºQ÷›÷ì8ò £ÖOÝ+=á'ûMã µUk×¼«ÜaZº¬!ÖÞÞµÎå§óé›ÏΞûc¯d[ÿ×ËþøÇ1U~¶qÙo=6¼ßÓI;ù§I®sË&êµwÝ^èòÝ_[ÚOè½îºmúŒª^]±2Ùt~¶ì'Óöݲõ‹4í䲎O=5* ºÑw}ùk ]ÿ-ý{ÞñàúL9¿@'ûKã­ v²ð·w :mï/µýGt¾°k’Ûï{tïËš¾åÁäèžÛk£×—£®ÿ®÷ÎÎìG¯9¤>¸Ð>?œŒø)cÍÄN¶öÂú¥ ß¼dØáþu—ã…wh#¯Kž×©{½üü×A;yÍh÷Ù²?5Öæ¿fñ¡¼·ó¨ÏûaWÅS¾Asmó6õdÿh<ÙÅuSöëÚIÇ­Ã_ì6Æí[[;o|öî3Ýô-~½>ð _mŒ75?ÛûÅlÙ/øðà^G»ÞÚ‰/Ä@m²Ïµž•O^6ü;ð»w޵·kÙÉ ;ö·‹7ÆñlÙ?šz µ÷·MŸôdéÒkcŸÕØq ¾¥8¾eÓËëôò=潿t²?4M,é¹Ú¡ÜÐGÎOzíô¬cç¶ÌÖ·<2úïG¦ósd?hÊ»ûú†ö= 9sŸp™_¥^ûøÖï¿nqûȦ÷Ÿ‹ë_»?Òf¿^öѾ¢•¥]mý~²41KŽxs«!?öòÆçÜôÚ­[[ 0üðáà¶Iu†ªâ7ï0-3ÚsŽì'Mëž×®Þ>ߟ¹ðö›Ãwý¤iUÖÙ-/èU7®J{ý¢® •ãD/[~~mÎgyð‘ý¢iǬ’-Ém ;ÛZjnrƒþX‡¤áA×éUÖ 'Ã'”m?}sÅ+F{Ï‘ý¦éäs??7ø ­å„ɲê¶ÃzmË‹¥g¦æèU²ñ®æ{»Î‘ý¥éûÃÃí9Gk‘̘?Ž\ï7òäz•sÒ+á¿íÔË[§}ÿKßß “ýa¯“U1»_[æ¬nWDÿÚ{CéˆñÜšÕ¼[õ‡®wÿšÏ:SèñÅ[QïA/ûÅ^aƬ¥šuÖºõq½öaÝqÇFûøªêiuœ6¬ÿ¼o÷|rÒ6_óõ\Ù_öf5ìÔVû¸mqu]ÃT ×îXÒñÓž»5Øø9&úºø’6\|¼ø³>cÐ邨q_þ˜+ûËÞ•Ö‰ÃÞ.-mßx©K‹1~v….Èì¢WÑX‰cNêåžCŸŸnɆ^ö½o½ó¦ÿ£ííúßÚ±ôÚÕÇ?ŠÔ«nz ëÙzÙ=‹§.øÜÉhϹ²_ìýtò‡+Ï|e—œI±¤ÝZ£í|iñj‹½]¦gV}]ö*ô²?ìëôKCíÉåÚñ¥?¯Ì“í¾/u@¹WÓ»ŸŽJxë¦ËŸëµßnÚ‘ê?V¯zúý7Ç<l›'¡“í»¯pUóŠY¶ùU;öæÓÅïnüÊhß/ÛžÓ­‡aÏ »7zßécgmãPÅßð•í¾¯dÄûoœmèc _|ñtæ—œý] ûhµW~Ý­²O_zõšìö~ð5+¾'ÂòýžÒŽÜ3inj^{`bزöëUÛë›ÅëåO/>–XRK}Õþ_uøøÞ@íØ¤›V:މ5ìÚ~níöÌFc\î m˜!×¶ŽC¯úC¤(_kÇú® ~{ì½Æ:ôBFÊwâ ýw6âÁ†icå¼¢—¯v([ÛÖÑOóTQñ¥­½Ê¸ÒÐëñ¬â¼uÏã´áÄa¯%‡´Q_þ£ÿ‹ÿÌSýhDÊáÇs7hGwÝ:ñk/{;µÃÛxéé ÃǶ7ü|ÌV§Ã-ƺÂâá·~ªß´ùdËÞ3{´£,2Y?ɺ4oñg1.†>*N´Íãöxy¾š/.Ý*vÚQQZ8Õ˜'šs¿*ïçf×£ZƵz™÷²µ%gçÌ·ôÚŸ_»øÑkíöTß÷Í’Ð{ß´#èÕz¢ÆÃ1-vÿÜnÇîv—×ß¼@¯¾ÿü»Qo1âw¹ß€^­' C¿)‰™­IêýÐÍ׿gw»o^ê]¦?jØA6qI7»jÜ™Î/PëIè{3Ÿ¿ä¯y`P]éŸ^·Ç“»¯_9»$écß.bÀßdÌO¶xzZGzι}O7gíˆc«X™ßû±¸T¯ÜÒã½ «õò’'ÄÌM}Wˆc€ž«µÃ纊ž¯×~²î¦óýöêÕìâ\– ÖË›f¸¹­¢¾Šö[\{>üÖ¦š5·ô3ÚÿdδßÌk ¿÷î8jç†öõ´ü»±Ó€ŸŠ#IJÕn‘vxùª·ÖÔ}xEðQƒÇ‘†ÝŽO‹4ö;KésâÑø¨xb{åÝ/-{Í>¿Q´û-zíÑ%˾ºs£^}ãÑ cK2Œqµ@Å—/7x·|@ƒ¿ÃeWcÞ9ñþÉ==bØ!¼á5ÙMïx±¢C¹¯âMÑM;ýjø¥ßך>Ãü»1â­¿^}›iòsvÚæ)½´³Ï/ëçšÎ/TñgúÅŒjèßþ‹ý|Žèµç?ýÒkW”^=üÏ·~1p©^Úvï3,9Щ8óþ;îvu]î¡s/ÜõÉÐ}X¹84ÖðŸuÚE.Avî¡>ЫöÇJ>ºvèÏ‹wf¼ó ^ûãBíòÎö~kÿÓ¶œ9Yv t²4þ|wøËï™´CËÌ…ŸíöËgÁ ó ¹eE]Ÿ;ôª1,”íÞøå×›‡®ÐÅ--mþS±^û«6zÝ·¿Øçãji»[–R_¶oã¹CËݪþ³˜yŒþö㤅kÂõj¹Ï7æ»à63ޏ†^¶s£5Üì®êØ»þçR⇡…ýW©­ÙÐ_t³à·©/Û©q”瑬»k´ƒk.þ"x¦±½ytÞ{cŸgþ*n®”íÕðË{o/w:«ÌoÈûáÍVÃïo¦þyúå»ßíñG¥l¯†#9 ~|„vprçS«VêÕ êqµ õ²ç? ùaÿ êÉvix½nߢŮöuæàðÛq†¾»bÏÌI-1ÆÅô~‹&/·Ÿë”Å–·¬yå¼±nVÊökxÒºÑÞ¾pÈõÙŸ·Ä]^ØãA£ŸÌ¶TèÓ »öóèd»5Ì÷yqÇ/“ìý»ÙÚü ÞÚÚûƒ»VëÕ+“ï¼gqŽVÊvlÈ õ«Þò€ÖÜúM“[D¨¾»ëšq¥íBŒöyù›1ùæÇô龩eñ=MçÉvm¹»ûηŸ×šß¹tßkÏŒÒwü06f⋆žÖmÃ}ú§b€‚N¶oƒš—›­ÓÔ}úîIÕ‹¦§=oÌÿ¢Õƒ»ëð"Õž×=°òÙ›B´æY߉§¾;öå?¥¤z28ú|¹T#çèd{Ö[§3W­Ù´Ï[»“ ½{—zèâ´4â……Æùç"Ù¾õb×ð‡t{û6ko冻Œu/µà§õ½¾Ò«_³x0Ô “íWŸº<â ÅEkî%&ò³Æú”ö¹%éÏŸþy>eÖÖÇçëô"ÙŽõýn±Ü0á´}ÿzàRãì5Ø×ýÝYŸIé0A¯^uyCÝØ·.’íY÷yøÑ/í·¯óÞøTœé»§­šßÅÙÉð׳âäR/Kýqd\¿µ¦óÉv­«ÙùóÃ÷Œ1äÏü胩>×ë»tþ%rþa£?«q1Å2iJô²}ë¤ ù>³šV>0XßýÔ+.ÂoÔ«ÅqÌ—ôÒ]‹o|Ùœlߺ”ë>ˆ ˜¤¸µÅõø”>öq´{[Ó²öYïý#Þ«õµ3½tËèn‹çÌzzÙÎu.Oÿáò±&mÓOÝ^/ü̈/ŽÝ~)óæE†ß­a©«^l ÿo3âù‡d»ï¹°9U´ýó>òîêïõÝÖî6I¯fòeë·z±–¾Û©/Û{OuÏ©½—hûÇÕ.ØäÒ¤ï¹íæºßÞ+Ы—M+ØÚ¨—àœAOUR_¶ïž´ÄNç:Øûվ˫ÏÜóz¼ÝÞ=CŸöÙ>÷;›Ÿíça– Só¿IL2湇d{ï‘ñ‹¶O|eÖ÷¸[d´³µûÙöåzéãâE<µX¶÷î7ô^,MÚ>µþì úȹ®Mzõêg|­õ±ï¦_?­ÿ„‚N¶ón±ëúƶߴ«=qí6»­Öô‰ua5ìØÞiÞ÷¥6¹zé‡mº^z}‘éüÙλä÷ ö¸©Il‹º½o´ÛÛêb'^±ŸÈzsÉÌýŒþ²D¶û;;E€ü¢Ö4s\ÏmÎÚçúî‹ïkã=ù¯æË¡)ïÞ4ˆøb‰l÷wr­Šv?6y~štÚ'K¯ 8»¶u°Å?sûÅëï}:ÙîïÜ)üÜí㦩í÷N(œ£×©uÏ.WÞC'Û}çγ«Ê7¿ 56lv_tŸ}­{µ¥µÍ‚1†¼9×틼Ö7«ñÙþ;³:ï9xæfû¼ÖøÄP1Óëu‚Í}oÿUg±}_´D¶óÎn÷­{qæmZcö·½ûº”Ø×…ºo‡lMsÝnŸ_,ò\:ÙÞ5r¿iŸ½¿“­³ÝïõíFÏLë4Ôè¿Å£Ïz<~Â[â…£˜Ÿ—Ê~P|–™ò'»ÅiêGv¿Ôwÿl‹£s²aÏÔªwçüRløc©ìz«Hj Öc¬d½~Pâ– Œ{ù=Š^*´ôêG}Ù¶‡Ž³·{ÃÁï¯+-¯µ÷ßúqyÏï0N¯Öš›Ó–´Ñ-?úÜ~uq©lÿ?Ç®88ùÁ}ZÃKÏnr:dÌ—õr?¨WëõÃØŽ}tKë1ΖÊ~ðöS£"÷õ©²÷ÿñmIn·ÿbïLÀ«¬®…Mæ™L„L„ „pHB™G¾¨ˆˆ85Õ"‘ŠE¯ÚX«R¯ * ‚*ZDI  ;uLO 8Ô¦ŽˆVq„Öª÷]ßÞßÉñˆ÷ÿÿþ½ÏÓÛÊóœ'ì½Ö^{­µ‡µöðí¥:æÔݘ7ÉsµNó­®8|Ûæg_¡¼nÿ-zµ{Ûº©¯îøyÝgKûúaÔÔsïqíTó÷üºß–mÐÑý`KÊ-/ýÎê²:¥›žŒÜÖlÙëXϺkÞªîõêõSÀ×íÿÜ„íßÍ*³:©mö]e}õíï?»àÖ ¾vò³_צD5wɨ«wNúKÝ¡Ûu{?;çÍKÖŽyÑêP}dXüLÕñuXFUQžZ§×Gj®¦¾n×g?3³}œËê82ý›[^{Vu¦ÊA܇j>gSór¾sËMûÁ×íºùH\ÊÆMùVÇöõN ºSuVœÚýXîÃj¾:|ü•¯žžn¿Í´fðmÏxôßñJÕ–%MÓ=íß9éºäzÖ%W&Áó¹—òºý6ÙǃS­Ž ïίÝ®:/h¾7ñ®å}ý>â¢ð;θÚsþr¥˜¯_ ¼n¿§_Lëzxð#ÎùþÝîsÇÿ•§uÎZtê][Âûö…¾˜°\©+±Îçúm_º]·ãSû-]ÒÓ:ôþšê¼ÚÿëóbT›ÌÎ)T ôvÝžOUß~òÍ[÷X·¿yæÆÛªUçÂí£¯?­o?éÀGw£:µàºk‚ÿ´âŽºC‹t;nœwÆíŸŸ2ÓêUdÓÛªsù§ gÍSm{’¤Ç« ¶£¾nÇ ÷Ù‡gœwH±·~§:×ÏxyóÓw«6­u¥°ýåt{®×û×VÇE'1“=ª:Ý*¸á¡pu…öoúü¥Eº]×Ë®tΪó«+qÝ«æëó×>}-Òí·î¶¡rÒå9Ú>ä¯ë"ËéÓ·ágþš#‹>þòÏ”ÓíÖöÝÒìyg,²:Îß¿-ë=O{m¯·;žjÓöEÍÓç+”ÓíÓvñ;U¿]ñ\_;ÿ‚Â÷¿«÷ô·í‹J>ÿ殾}M9æ_þš'Çö£ðïéöjýå­«“¥YÎ<³ýù'7O=ÕOµ½V“™tÝ5jžÞGª;´X·S‹Þ—µ:Ä ßõ ÚþÁ†Û+Vm/L”‘¯æÑ)£ gö­7ëözÒvwNµ:¦Ú’jGàΧnéßÇŸÌŽM«<çÎó£ZF_:ñœ>p±n¿'̽O»Ë1Ùm‹<óÆŽÄ÷¯=½ºLµ-±ñ¬Gç5ýü£~:º]ÿXéyVÇ 9 ù¹Úá n=aM¨j[ao¨¹8Çy3‚¯Ûwõ%7œV¿™y£)+ÕŽŠÀ3NÞ?»¯}Í~Ÿ¾tû>¦çYú§}>¬vL÷JÛˆ©ªMVÃPóvËÀM_·ë£¿• Ÿ“¬Ž+&üñhÖµ£áé¿ü­­]µéû3}óçbÝ~«ôý«ãWöÆÚqÍšù¿ùä€g¾7ç(Öè]êæÊnÙ¥åu‡ôõOë‘Åc<¿ê,Æ·ægÇýáxðôÉÅâøÒ¨ÙjŽ^_SN·çÃÓgn}ûë­Î:RíØvÙôŠÆoTÛ̼ÄÀïÆª&YŒ~— ¾n·‡ŒÑa/?êÕŽÞU)9©Ÿ«6m¿Õl½/¾nŸ•7Ч}ã_ï (K˜þ|7pÝÊX¾¦ªÛêØ²íg{¾mS;‡ºçeVEª¶3ªc®<õÈZÿ÷ýú)¹cuüN6ºßR;ÇµÜøÌÁU“¬b+NOëýÞwÌ{¼ë+«ãùUŒ„ÇÔÎóîþÃÄ+ºTÛÙ×´wM¬š£ûøZïKO²7¤­Ž—ß{uÛÄýjgÓü {Mß¹Í9™xþשË÷ÏMÂÔ׺Së{‰î‡VÇ«G™ú±Ç?Ýyퟱú¥›UÛicÕåsÊÕ܈Þ7»Ö÷g'ÇŽÿf¥ÕñÑqHÉ<¤v.¼äåYåÕ}óª¹Çtù#Agä$>O9­÷[õy Õa»eEjç]Ÿ-~b㤾öÍÞPÛzëû;µÞozâMÙ °:͹ØÎGê"˜ÂU[¬=0ÕåãË4|ÝÍúÐêL’ ý>µóÙ[ë¯xKÍy}û=wüö<ðt;\®/ŽYÙŒûkеóõ yC<ç˜sô¾8øº=ÎÖçMV§¬v¦ž¨vnxáý ߨVÛìÎQMík^}üLðíöPçLÏëyágñVç¹ßN¹önµ«¢áÞûwÔ«¦[­ÑMèñ.[ÿjŽ^¯X7;Ýšñ¥Ú5ïúõƒN<_µêýoÕT1Mfðm½«k.|è©WvÝbu®ß»×ÿäµë©‹†<÷ò‡ðýîçýù•º ¯,½ãsðm}«.xaãW/·:ß=y|OK£Úu$èÆ«¿]¥ÚÌúg–>÷ßÖ·ZxUí«¿¼p“µ=¾'•%™Ú]\ÿYï´]´‹½O¢.‹æ€oë[Ý~Ã;C[Þ+²¶‹Öj2Õî‹×½Ûþ̽}ýÎÞ¾Ÿ¤šúÏüÛîAõ”³õ¯î”VŸü²µ½â¦s&|ñ©Ú½8-9áê-£ÛM?h}õ›²#ow«&A{éCÊÙí î6÷ ·õ—*j÷ºå¬Àÿ¤Zm·ºZ5éö_·ÃRÛ].´¶ãîèÊV»YÜÕlx·îЭÿ{ín‘µýZÿ[Ò¾^¢v|Ò——~¦.sîU-Ñz_ao•ZÛ|³_ÌCjOܺ ¿èÞÔw_c‰Ö÷ýævûnY°Ô«=µ×VÆ<Ï<¿Dë÷ãWïݘ«V{.ûÕ’åg£—%ZŸâבiíë˜ý™Úópù®¿®õ¶RÏgÖŽ™ïÿþâ=c<öuܹðài==Ôtê%Éo÷­?Z‰ç²Z¹õyxZ?›uÚ½.V3¿~ ón­ŸUg„}{Ó ÖŽ¿Îúä•G–)wÞ9o¼½¸ÖË£zhí4çïîJYر~½[ëã19]ž¾ÆY¿+÷Øè“NJ/®õ¡¯1¤Z;|ðÕãÖpåÿÑ×ww×úX}Ùïdejí´Õu¾rO<ï±Uã’€k}<Î,xË'¬ÆvOúãϲCN®õðø‹¯-øøak—ŒÎÌvå>}QrTbp-ÿš&Û1´vÉ©lãýÊ]??2u)ëç{´üOè}Qk×I²¸\¹Þxæñ×ήåÒï‘Ë Ö½hí’Ó‚ŒO”ûÒ}ϲ·¸–ßñ3v=¹üÛ´ß®SîkƒŸ{ýmúç=Zþµ×ÌÞ:¬v𵫷áÛï¤Ü‹_Ýåü¡{´ü-æ~én¬xÖc_(½í \Ëßb«§ÌÚ]'o_Vîç>Z³ùQæ—{´ü­Ì6»:æ[»g÷;rZr»×üöÍ}Ï×ò·>uÒ_ÆŸõšµ[ÏÓÊýÖÆ»¸ÿy©–¿-Þ>è·v‹U9ɥܟ4ŸõõSè©–¿MßW±öÄÚíÊ}ô®¿~1ìFÇ€§õÐöÌ•7^¾Âeí9ùò—’¯ªºúÕXKµÖÉ´ót‹µÇ¾V7[u…º“üÉÝÎ|žÖǺ›ö&ŽýÀÚêwlóBÕ¥÷Y€k}¬7÷íöüI6ÄnU]¿¿ðþ_‡×úX¿aÆÊ¿„]j¹cÅ€þIuÊÚôìGnàZìãR?Ëmæ³®±;CÖ|’_wh™Ödž»¯ÎþÃHËm»íSœ{Àµ>6ÚËžs,wÓæ=êå3UWýõu*ëÑeZ¥´m´Ü·ÈBq¬êºîþ%î7¾é;ÇY¦õ±Q¯-·™oºn_Q±/ü"àZOõRÁå¯ÅYî:ñª®ûzz·ŽcÜ-ÓzxÊ>>I´Üïlþ7ª®ÇOÞ6nî.àZO‹Ók¹¿ýäüŽÈyªkcü²‹*6×zØ$»”së­®äÉŃ«®S][·wMǾWëaÓ™VY]%âèÍT]/?²õÛû×zØlo Nµºìf:Muéup­‡Í÷ˆ™aué{PªëÀÓÑ©C¿®åÆ¿¬zèì¿Y]ËdƒæSÕõΊô'îù ¸–ÿé-מmu½8 b’êú<ý„?gŸ \Ëÿ¬¹¿Ù¥ïû©îÀ¯cox:ÀYgëÁêÖWTwò‡-îŒ!_ËÿìUñL>luËiíEª»ðæ›ÇŸ?ºîÐr-ÿsæ<©[Nég§ºO,™sdI&p-ÿs(ý—CvYÝ+èJUÝÓÜÏ]¶ïJàZþ-.BµºÛoˆ»lémªÛ>~Æï^®åßb»‰qV·p—ÿ¨2õ×òo•SǩӬY5߬ºýöwŸT†×òoÕzµzrŽÛàzú€êþÏ÷‡Ïž\·ÿVÛM·z&É…Ÿµªûνöª Àmù­žÿxåÈÕ_Õ©îûݺd£u‡ô“°j«¹ÓóHÊɯ¯„¯5 wTz³³n/C——ÛѪ{˲‡³~ŸH¾–{ëí¶GdõÈuѤ¿©îv=óÉ -÷¶XÛñ²öê}XÕýaÄ5¥/×roË}!kxúFk[¿þ›ê ¬Ê¼è›}ÀµÜÛÊß¼ëµû¬½³ßOZ=ërÕ3hþõ·Nc\¬Ðro«dä¸;r­½ËÖÀT¢zŠV-ýëyÃëvß&æxÊ×Ö^›P‹ê9~öüWWÒ_îÓòo{ègï™þ­µ÷ÝHY!«ž3ÖüüyüÕû´Ü¿ù2¥³õ-ÕÓ0uÚ-úÆõ}u~üÓ/\ñ•ê™ùz݈Ïß óímö±ªgÞÉyW^Ë8»¯Yç›û¡=×Î^:cqù+u¾Ürš¶Eõ,¼9ëÅW‘¯ûó>Ûm=Eõȵ…·~E¾nÏ}Å—§±ëªgµ½p«;t?^¥œl©žm~S>Þ{ùZŽ}7ÛQªgßã/5¼‚½º_ó¿ïé©Ë›OºXõÞ—w ùšÿ}²üö µ7âËý cד¯Û«=þ›¨àíÖþ0{ƒ[íµ1ã€ëöjϾ`ê«3gZûs]zc¸Ú[3åÁ!'`î×í¥±ÇZûOï.ùpy¢Ú{jnïuS‘÷~Ý^íQ©sæßžeí¿Þ>V{Ïûvó!cTë¯ò¶mâÕúŸõ·—- U—êûȪu•õngZ‚š©ïý©ÖuçNýD™j]i/tÔ%2Ý]»FµÊç¯ñ¬ë}hÏ:ÊðõÍŸæ;þ•³Oâ9G2xNû{ðÌ8ñ¬w¶®½ç±•qSÕ¶ûäB‹åì§{öÕÌùŠç^ͺÙÒ!:œ{žu´s¾íœ_­[•ðÅù“œûtÎ}=g?É9Ÿrö¹¹£fê{/ê}EµFýú¿ª»Ô soÍøAêb}žèÙGtöi¶ÿfÛ§²°ýÖª1zªÐç ŽþóngÿOµþ-VVp~~ÕÌÌC¥w]ð¢ºXßÇW­¶¹š¥ÍþYë°çF~tö>Õ"£Ì¹qSþiKV-OÙ ;^Ë=²X fØÓa­ó½‡§?ÿÈ£_ßþýý~éÙßR18ìSúyøvú‰£gçü²Mna_XiUésgÏ9Ç%ú¼Kµ\:ñœyïMS-×=ztâÚz5£@týU‹l¾û¦jyý©ûß|•ºØÜלñ¤Ü] õ¹j̽Wn9r8üùÊ¡º.½9vÿ²W•²ì•œsRg?ÍÓ¯ ι’ç~—³No½[¶ªKÌý§9ýÛáï»;§f>h_¬W3µüê’gú?þÞÇ›TËeWG/^ªZ´÷ƒù!úûý˳Þ*·}ÏÉôŒ?GÿŽ^ggÚ6T¥_¾K]bƱs?¯å²å‹ÕÒxÞ¦_ToV3äøè¢õªEN½«QgÞ²bÏ^¥Z~“uFlz¤£_ÕòpØšGÖ&÷õ#Ó>;ãH/£¦?5÷Іyæ1Gg>ûâGäsÚÍéo¾ó¢“þÀà™}TϽ³uÉö0ÞܪÅÜpæ)ç;¿[ ¡þþÔÒ9÷‰Î=óA˧f±´W3?hâGÇýAµhû¯fŠ÷’s2ûǪQŸ·©µæ|mFý[ÉÓóöúòÿƒñ5ÀGNg|=ûö{/¾´t’¯œž~jö•0ÿ:÷ª/~ݾ˜aÕ^«äF‡ÓþžþíôÛµrÌWó šøÝØã(Tk?^ _"xúq‹éÿ—?°Å¾7§Oöõº«Ô/Œý<¶=óôGng|9ýع¶N®ý¹6yÚ©Mï‹yæ“–}W6]õå¦jüù;}ÛÊgÓOV³ô÷žþß"§6¡ªåÜ1ǹ–qfo U³õw?žr—é{~ªE¨ÏÝçìÿxì®ï|âô[sì鯾íìÀþ½÷ûùJM}VÓñfî žvqæçû0“öÌSÎ_ýšl_wöÕT Öí¹µ¿S³t»«–37ÞVõÞMªEû¥ªEßÏW³_¾ó«ƒÃUËm‡Æc¯[vEŸöä G?hOÇî*9Ï;Ûã/´ÎîNÿìE{;ÊôO#—¯ùA¿ˆö¥ÿ»„?N?ë%O?ñØm}þ .—SÌÓ–yÆ…sÎ2Û¾–;Í3>œùÁ‘³Åø;fÞsúÝŽOóÝ…gÜùúf¿TµÊ²¿_·3¿[Åf<úøq¾úüŸçøw?ÿâØižsßÒg^õØG_ÿòÕïû“žyÄ|‡×7îìmÏùêúžÇŸqôR§÷g½8ßçúÎóžvöñc}åöÈåkŒ^U«¾Ÿæñ¯¿£ÜÜgsÚßÇ®«ùzvŠå´ûæO‡OgmÕljj–½-Sæég3ôy·šµåñKRæïòô ‡ŽSæ÷ǧ_¤ûø-ŸÊØd¾s?y†¹oÓzÞ‚ëSÓîrÎ!=tœö¹X¶÷>ÿÒ3?8|¿Ú§}óËæÓ>¼qðfÏüâÑ>‡R—êïƒT“ý¹rTŸÿÐ>rä²yý<ë cçT‹¹_Ó2^.j¯p¾7qü]^œñìø¹MãY¬R³¼½¥gêMžñæà9ß8ßûøÉžösæ§>3OøÚý~úM<ù÷Ó[—?ŧùG¾yùŽQó|÷òß9VükÔ:µßoÞKDGþ1&föJ+›qÐ`bd÷šy u,ì  ~ôë `ALÌkÒÁ” ¦¾B3¯MO¡Ô n(º ##ŒtØb·Zá æ=D·žb"êøQWD»ŽSÙdâS÷ê÷£€EmÖSýÞáb“ÚŽG]oÞ<7†¹#¦^ÇÖ‹9`Þ:$ ­XàqI:ÖžÄ=‘÷ eúЧîxô¿XÇ¡jÞ:lÔ1íxÔ…&õJëÄ~ç™6ëx|2íÙñHH'ÂgâQºI¿‰dÇ Fr£‰E /)ð’R¯ßÜ–·å}ÃTô’ ~*u ¢ŽAõæC·‰Ù×hâPÐï&ÙqHVêØ}vjêL§ÎôeúÍCû}CÊd€“qÀÄ¡®31IÀɤ\yYè4«YÇø“)Ù~÷~2Þ†À[vŒ‰I^³ÁÉ>¨ß!—÷Çí·ÉІ"wy9È‘ƒ>sÀÍ9اZÞ—÷%f‰Ló3ÐEž‹<å]MÜêBóžy“Ž£bÇ3éÕ&!=äB+—:r›M|Ávßxð¼:oPb\K¼yJÞW´ßU¤â|äˇv><æ#c>üäË{QòNT¡y3}~[JÞm´c¢ [rTèxÝß‹ ¼øHà#+t\y³Ü~Ÿq³Ž—2ò°‰áýBd+¤þBê/¤þBê/<¬cJ my‹]Þ²”wÒí8+«MŒ•:ædº/BÎ"ä,¯¨Ùk¼ÿdÛ²í?Ùöÿ¶ýïµë2Ž›´¾ì7«'›wŽ‘ßŸ1êÌ]ûC3€1@¿  í¨?°Ží¯AŒÇ ~À‚HƒL:xµž.BH‡P6½‡RO(õ„‚Š˜+ÂH‡‘ƒn8´ÂM2ààG0Þ"¨+¢Ã¼iLÿ\­ß6Ž‚v°(xîjbA«?e£Án0ñÆÀa^ˆ!~L¯y¿˜t,´bÇ×`bQ>zñÔâÉ‹§þ1&Îy:LŒ]xH °Zdzß.F¦Œ“à$†š÷‹I'¶ëéÏŽ»»@ÇÒ°ß,†Fr“‰Á /)ð’Ò ß9´cŒe˜X¼à§öš8¼ &¾¼¦!KZ“‰ÃÛ«cmÈÛŃÁ|ÀÄã¥ÎtêL_©cŽÉ›Å”É'£×ÄèoÞ1'“rYäe¡Ó,xÏj×qÎìxcÍ:>ñxˆv68Ùè5œìÃ:Fœ¼÷hÇ#h‡ž¾sÈË1ïà倛sXÇý•7©åGûÝcêuQ‹>æ"ÏEž«£/ðð £dŽûaÇ(ƒ\Ê䢇\håRG.<çR6·ÃÄž<x^£‰o²ZÇ ‘7“íXfô‘|äˇv><æ#c>üäÓF#¨{D…y«²Y¿ i¿©Ü«MOrÀsð‚f›¸Xe†6K#åoŽ—"ñGì7—)?Ú…Ð-†¯rþ"[!õR!õR!8ÅðUN‰[Ç3.¥lÙmªù ô«Cuœç"ðбHæj±%òOì¹·÷µÙÞo»Ë6‹MöµÇ¾6Xì®csÅΊMEæïÙR±‘bÅúÚAáUì›Ø5ǦÉåØ2ÇŽ‰í{…m[%öIl“Ø#±Ebw{#¶FlŒcSÄ~ˆÝ[áØ ±Þ¶Á±Þó¾3Ç{ÏéÎ\îÌá2Ëœ-ó´3Ëü{Äèx³y'•>çO{ÐÀ ¤½eþ¤3„ÐŽ!è-ÙCà#”²¡ô‹0Ú'Œ2a” §¯†7éxá” _DÒöQ”‹‚~ÊDËZf™‰§ÝXÊŹt|Ä„P| åcLÄÅ:öaŠÌ5ॶ™¸ß¤Ñ—Ó “¶ÚÄ2$/}µŽyž!s¼•ÀkV’~‡µúôÛJ詸PǤ+·òú{”®Ôï®ÖPG9tjŽjW¬VÊ¢ŸZ~UðPQhBw¯B‡•‚*x¯Ewµ“õ›®µÐ©…v54«Á«Ažòk¨§VúÊOþíOþms¿÷oòmÿ¹|ÛÿŸ}+‡ ´ÌvÌú¥7ücþÀüѧ?eêøÑ¯Ðé@úx m?AŒ· údé ê7˜t0ºnã%Z!è.”:CøЬaüdÞ†ntÛá L à”±RÇÚ¤®Èf~m&¾.´£èSQðÜ?ÉÄè·?e£Án4q:Àv éà1MlÒ±ðKÝqàÇ5š”§îx䌇Ÿxòâ©c@’‰ÏH'€“€¼ b/ÚLœdÑå@xNL2±9à3$1&A#©YÇç°ãæB#É’†×ôŸÒdbé’N…·TÒ©ÀSIÊ0q:mu¦!KåÓà!í ‰£ |0éÁÈ’~:u¦7›xwàd€“A:ƒtéLp2'ë)3“2Y¤³# 9²:t :;Ž.sÀøÙàdƒ“lÙ&~^’ŽSlÇî (zÊAæòrà)]房 î0—Ž­'±†íxÔëB§.ÚÕEž‹<å]àÏ01øt d;¯ÛÄö L.òäB+—:rá9x.ð\àyÀó€çÏkÒqû$ž°¼anÇžó‘/Úùð˜Œù½Ú´Œ îu&ÆßB7Ob…H ßd+@Žx.^¼xðà#té·Û%îŠÄ&±cývè8"b® ¡_ýBd+¤þBê/¤þÂ^mÊF%éx‚ò¾»ÄZ‘¸|vì‘¶¾¸#£ÐQrA°¼"ä,’ùXl‚üsì¹cÇ.öÛ×v;6Û±×bŸáѶÅb‡›ëmoÅÎ:6ÖÛ¾:vUlªØP±ŸŽÍ{ék[(6б}ŽÝó¶y"—·û1w,?Xì˜c»þ;›åØ'o›äøÇbs[ãØ±%Žñö‰½cˆ pæ~™÷½ç{g~wæí£Fï&neýÑ·ÿfÝ” ´{ˆîá+¾Äß ÊÓþÁðJùЃº©Ã›u<¤HúRäfûh²‰IN¹þàE7™øãÀbÈ‹fl“Ž/ñtì"‰nÇ ¢]ÒB7‘ñ”£cƒ'SW2Sà7e‰1D½©üKÇí´:~ð’ÆÿK)3x‘Ìau\Î Ê+:j⑟åÒq Êá§œúJiÃbþVˆß ¼ØÐ:¶ÄÛ)'¯Ë¡QÁx,]­ã—ƒWÖ@tÊ£ºµü*Á©„^üW¡û*hU!ßò« U%þµü([5àJ Íš$·3IÇîüÉŸî÷÷ûÓÿê¾ôO{ÅÿZþ´Ë´·ÛÄ“i01ìàÁŸ1âÌ}0Þèó¤Ðo é@påÿÐ 7ˆ~Ô¦§ˆ`pƒégÁè*„±˜¡lõ„Rg(}8”v …×0ÒaÐ ÿºáuüècá´S8²FP>xø¤#Gl×SNã)Š>uÀÄ¿ƒvhõ‡v4|E/Ð1ïdJŠ¡l |ÆïXÒ±ÀcÛõT~\“‰wG]ñè$úñð/¶½ péøv¨/Þ5xu$6ñí 1žB#1Ãĸ#H™$Ê$A3i¡Žs'±í’¡‘L:YÒð•)ÐLéÐÓc*|¦’N…ÏTàƒ¨cØ$êD:òiÀÓ€§‘LùÁ¤ OéðœNé u‰w—Í Òb¯HgºtlŸLÊe"&yY…:ÆI}"Ë­cð A–!È?DükêÊ&/œläËv›XÔÈ<ž†¢‡¡äEo94@y9ðƒ.sÜz*V¨cÙ± 7ë˜x.tä¢Ï¸Ès‘ç: §ûá.¯XCð0ØpÁG.er‘;Z¹Ô‘ ϹÀsçÏž<xޣȎ+ <x>ð|t’|ùÔ›ùȘ?#¨· YFŒ×1F,ÖqƒF¸MÌ=d+@Žx.^¼xðà#,Ôñ$&áHà#>ú…ôBê(„~!²R!õR!ðQÀGeèØI£à4F3j¥‰ãçÖñûÄÜ!G‘øà‰_"ó­~øþ‡ûÅbÇ»-Ä×^‹vì³·=vlï±|ê³·Çò­}íêÙS_[*6Ô±ŸÞ¾·ØLo;ùcöѱb{èmûœ}#ÇÆ‰Ms왳ÿ|,ûåØ-ÇN‰}ò¶MÞ¶È×îøÆ;ãm[|ãèŠí{áÄ»àm ; ó¿÷¼_¨iرH)ã_ÇYèGô@þ‘„^‚ø0}$˜þNåÂ(†nÀEdðk4±J黑ð~T¿^‹}÷·?õGC7º®NÌfí"Æ¢ó8òã¨/޼8hÅSW<ã#þ ‰1J½úB°h$@o :Nt™ø¡À“à' ^“c´k™ÝðSá-Uþvè®<¨BÇ —3m²‰ |ðQüŒvï3 ÜR`YÈ>>²á§˜q‘ݤc{VŠï9äå@«>Š)S Mü¸è«eòVŠL¥Ð¬FÆñûá³zU´E-åkÅ_mâ{"S-°JÁƒÞÊW&WÚЩ¥®*àµÐ¯‘9„òµðU+eè¡6 ÞFÇè¸jµÈQK_-}@ü{ùWáõèÚ~B¿~ÿ3þÿ¿ªïÿï°þðýÿü~ñùÿYýýñ¦ ©ÓŽ)NòC¯~ÐðæOûù#gã7€¾ v½2fÁ l×Ó@¸AÍüHƒÌÿƒ‘3$ƒ}<:!ÔÊX ¥O†Ò6¡ðÆØ #F¹pÊ…71°% ð<üð#á!x$º? ¢à7 ô‡Vúsÿv=åDC+ºYǶŽ7†t ð˜v·ÒŽi<±ÀãHÇ×1­%öduÅSw1ôK¨3~³ŽE9ØúåêK@¶Ò ð“С·2ÂÃ@ðÂï@øODÞDøO»D™$h&5ð£Lr’Ž; ýdI#S |¤Œ×ñ½%¶µÏx*éTÒƒHª3q­á5t<¤‘NëÐSæ`Êp»ž>Óá!:«¨¯b²‰i-ÁÉ L²f‚S UâóS.“¼,êÉ‚÷,x̢톀3} !=Ä­§áläÍFÞläË> ã€–7˜x×è`(4s¨0sh‹pJáy˜üÀ+kÓñ:%ö0ô墼‹|yeè|4< ·ˆ:‹¨£ˆ¼"x.¢þ"x*†~5ÿ/†ß·Ž÷iǬFÖh• — ±}Ô3ž$¾g ´JÄî"%rSG):+…ÇRpKÁ+ƒF¸eð^jþÖ ‹*ä­:Íòú£6åà–“WN^9|—C¿úåÈTO‚‹ŸP!?ø« \2T ‹Jø«$¿Rüò+;t\m‹rUðU_UèCbŒVÁ[•ð½jò«á·š¼jòª©¿ZÕЪ’9W?°ø}›í»g~,ûìk—eÌç?–¿ÿßí£‹=u쨷 õõõÅnzÛ̳—Dz“Ž}ôÞoÿ1ßÙg÷¶{bï¼mšcÇÄ~9¶Ë±[´û÷ì•c£ûäØ%ïû(¾¶È{ïÝ×ÿ;ãmc޵§$¶Å±)ÎZÀ±#Žýpl‡ïšÀ¹ÃrÔè]úãËvõ#ÏŸ<úeý"€~˜Á]òÿ Æ@ƒÓƒ{éjôÅʇ2fBÑq(2†ñ7 ¼hF,Ðnb$ý8ŠrQЊ‚ŸþuüÐt¨Ž3M1Àc(K?…¯XòâÀ)n`nÿ›²%òÊ—Ðæd€~ | „N1tJà¥x©ÌÕu:–p r•B+ ½%#O2¼$“_íù»PÇ.§âWA¹4x(nÓ]ºú•⯓—.s©Ìè© ZÕäeQç䯖MÙ2hÖ€_#ótËè3Õ2WQ_©Ì7äÕ ŽÌ}àÔ-sÓaë¸~GKø-oÒ±Œí¸Ç¤GoÖ±Œ-èYÔoQ¾Bæ*êµÀ©D/•’–üK,ãjx}T^PîgÇ5–øÉ2I,ãjþVó×^ÇXÍÊÔA·NÖÀëµ–²uðV /u´ísÈ¿ÿ—õÂßsVð¿m­p¬9îÿ´NøŸ\#ü³¯ÿ냶sß5B½i ðýèÿ~ô¿6=´ýùó‡×€ ~¤+qHß äÿAŒç úw}!ˆt0xÁ¤ƒiû`x 7„¶ ~h¿f~ízz#F:ŒráŒûpÊ…S_8º ‡Çø‰A[E2§D6ð ?QÔQðnÆ_xèOÙhxІV4™òÉè%™úS¨?…úSH§N%JÝ©Ëôô4Ø è‚·4xO£lé4êŒ^ î`àé¤Ó©?úÒÁI'zÐË'í§ÐLp2¡“‰Ì™zJÍB‡YðÅØÊ‚ï!´ÏúÃÒCà+ÚÙàd#o6²e#K9t‡RÿPò†"ÿPÊå _tk(Ÿ^xÃàauƒ¯að1Œ:‡¡+ºq‘ç"Ï^1¿hÖÈ:€²Eô• ÒEÔ_ÆÿËÁ->¬§îÑâ÷ƒW"~?m\<5à• wi†žÖKÄÞJá«Já· =”‚_*öºebW%~*É/¯ þË WƒleðUF.£î2ø.C–K¢ßÀ²–SO¹øÔS.y”«õ ¼TЮðZ!²ÈšZ•äW’_I~%ùV£¼Ìÿ×B£JüŒzy¥³ƒÞ«h—*±éäWÃ_2WÃW5tª)W%cXlˆüû¿]ˆ>ÖzÀw- 6ÖwàíÇ{ûçŽ_~,ܱ+¾6Ä×vˆ­ðö¿ÅxûÜŽ¿í=Çkn÷ÓeÞöž§½çf_¿Ú{n•yÔw¥ lE_ð£ŸøÓFþäùÓ¶ô‹Ú?]ÒFAâ7“LÓB!„r¡ÈJ_ ãoøá´w$í‰þ¢ÀjÖîQ }t2%Ñ'“èƒåÔW*ó†¬7é‡ÅÒ_¨¿’2ƒÅϤOdÀK&t2¥ŸQ®’2Y諘¾–EÝÅÔ[~1ý­Fò¥ŸC«†þU*þx£ùÿhhŒ†V%°ZYÇ’WCº ÙÊ 1Zú²ŒWÁƒ· xµÀ­Ÿþ-`ÕÔS-~2<Œ†‹ò–ø¼ ûܪ:ÊÔñŸ:èÔB·Žòµ´ÏiûžMzÿ[Ò² i{l4kóð£-ýhK?ÚÒ;é÷g3NM:`¾Ÿª7ç$n}·ßþ.z™Ù#K2öo±ù.:ÆÜùo6vÐe¾^iî6¹Ì·ÑËŒÌèûÀ¶…>çâuæÛèvóm@…9ï0gâãõšê{çâMæ;ª£æûè¯ý²$s³ÒÜ51ßRm6gã…æl|¥—Ml0ë¯^ó-Õd½óœÓd˜o©ÚÌÙxŒ—m\íõt“ùFÚm¾¥jÐg7öý«Pc›Ì7Ò½öÍêo¤'g|ïÌ>'Ï0ûgMæ{飿¾V£¶•žï¥+Ìyy›ùæ!Æœ™7™ï«:̹¹ËØÎ…æìü¨9;o0ßXu˜ó¡ óýôj¯ï¬*ÌwV«Í]ÔP}vdï©-3çç¡f_m²ùŽÚÛŽV˜sôe^÷R]æ;êÅæ;êÃfm²×½ÔvcOû™ï®êô^›}?u¥Ùkë5ßUg˜o«'Ûlìl›ù«×œ³'™o²&›³ö…ú›Ž¿û{ë…Çø.Ëe¾·nô:owû|o=Ù|o½òG¾Ëjôú.«]›õ½ï²xwÕþ6+¦ï{kûlÝe¾­n4çê«õU{ÿñ€¾Ë'ß¾È9Ÿœë7è}Ùo´} ·ÙkˆÑûŒ²î—ýLñÄ7;{ò˜øF²'+s¢í¬î³ÿöw5‡õ¼-û²ö^`£¶ñr>hïù-Öw÷d?@¾›‘»{²· ç„ò½Œ}ö׬÷dΔùÏÞ+8jö *´O#û²PŸuðY'ó*8ÇÁÿqÐ:œ1òo uŽ¡Î1ôw,²Œ£Žq”GÙqä£ìñ”=ú' Ë ¤O¤¾IŸˆÌ'‚"<çÿã‘cÙ'$(B²iE,A Š&¬)KŸ¤a“ˆáe¾˜?*¨\þßóÜûd†yòŠ~>¾Ú~>ÓÌsŸsÏ9w9ÏsÎ{§Ú dT ¿‚±­˜U.‡½=ððPîA®‡ò*dVS^ }5<ªé›jx×À»†~«¥~-×uè^G¿Ô!«Ž6סC:6 cr½ðñrßË}/|šÓ„œ&ê7Q¿~Íðk¦N3ušé“tmA×h[ mA§vú¸}ÚQ¸1m—2ôô!¿Ðw¢k'2:áÝɽ.øt£Ou{¸ß¿î÷Жd÷SÖ¿I½Ã­ûö«î;ÿµoÏê¿×ÚÔ|¿a VÙŸøü™Ï_øüUcñŸýØ¿ýØ¿ýi_û7°û7°û7°c©>K†ýØ¿ýØ¿ýØ¿q¤>Ç€ýØ¿ýØ¿ýØ¿±BïÅþ ìßÀþ ìßÀþ ìßø‚þ­û7°û7°û7°ãLý› öo`ÿöo`ÿöo`ÿÆ:½‡ýØ¿ýØ¿ýØ¿áPñ¦ýØ¿ýØ¿ýØ¿ý[øNØ¿ýØ¿ýØ¿ýØ¿…ýØ¿ýØ¿ýØ¿Q¤ã ìßÀþ ìßÀþ ìßÀþ ìßÚ7ŒýØ¿ýØ¿ýØ¿ý[û °û7°û7°û7°kû7°û7°û7°û·°©°û7°û7°û7° ãû7°û7°û7°û·Îgcÿöo`ÿöo`ÿöo`ÿÖBìßÀþ ìßÀþ ìßÀþ ìßÚýØ¿ýØ¿ýØ¿ý[1"öo`ÿöo`ÿöo`ÿöoí±Àþ ìßÀþ ìßÀþ ìßÀþe}ÕÀþ ìßÀþ ìßÀþ ìßÀþ—ËÀþ ìßÀþ ìßÀþ ìßÀþßÃÀþ ìßÀþ ìßÀþ ìßÀþålºýØ¿ýØ¿ýØ¿ýK|k`ÿöo`ÿöo`ÿöo`ÿr†ÇÀþ ìßÀþ ìßÀþ ìßÀþe¿ø§Ž³å]ìÓÏ<©'¶çÑ}4¥Ïqú4þؤ^gö„à”D곜ƒƒ,Zc•lÕ{Ï£5VÉ^[Y<Ëi­±¬ Û“âÔX%úG¿:Ïií?ÑûR†õï™Qz_ÊV}6)RŸéÓûcô¾”­!Xd^½ö<£Îò[Xdƒjн7Å:Ó9ª÷DF†`‘ ‡`–ø5fÉ”>ÓéÕ{Sf4@¬^‡ [‡Þ¤ÎuZxd«‚¿‹ZkÑ ôÙN—>5²'ݧqÉ¿$Ví«´ö¥Ïèµi—>ç¹U¯O/Ðg=Ýóì[ñ*ükzÞ£î 9ï©qÊôõg«×©‡ô>ÍeêwXëìç@^Y¬Ú»ù‘ýêÑ×d³Â5±ðÊVé},~½_}Bc–Eè3 N½v=¨1N&ôÞõ½¯%Víý´pÌ5–Ù¨>:£ö¹œ¥Ï‡º5¦Ù&}FtâïÄ?Ù4ÏÑhâ Á5› Ã?qkü“­sFTbEÿ'œõͳ§}Æ5‹Qø'sf.½}“> :©Öô­ý=ÐÄ*\ ÁN±öcùÔú½ìáŒë7üHµn/¿§Ÿ3¦Öâ­½9«ÔZ¨ìI’ßä÷pYg—ßÍ“%>¤™ðO‡_íH¥^ì&µ¯FöÚX{kÔ¾ÁšßÄÅÕùíBð)d¯­µv«Z‡ÌžRø²î)®¸õ[|¬Zç´~{‡,zÆ¢gl@¹éqè¯8hâå]<2ã‘M}›ˆŒTú1‘º‰ÔM (—>‰ºI±Ê­OAVŠ|§½)Ч S:'×NÚ᤮“v:á“ ÏTú'•þKƒGòÓ†ì4d§!#]>dÀ;ÞðÊ~ƒW&úeÒÞLxdÁ#‹qÊb<³i¿‹±sqíâÚE?ºÐÇ….9ðË>—û¹ÈÏ…g.rs‘›ÇØä¡_ÞŒ 9ò¹ŸÏ¸ç3È+@Ÿî¢{!÷ ¹WH½"ô)‚w¼»=*äw"£úNtí¦¼z ï¡¬=Èí™R!W?eýóˆß/ÿÄÁ–˜Zbg‰™%F¶ãcyAH;Æ£Ÿæâ9;~“ØLâ1ÚiÅbƒÙ±—ÄZv\%1}lÅRvEÍÅKŒÉÜ>_‰Bã"úÔŠy+Æ‘ØÆŽiì³Ñ:Žù¿bæØ'Æ&“„Ç#á1ˆÄvÌ!q†ÄO„Æ#H| ±@x ~¿ø÷ ‚>½øó¶/ϼšóáÅ_·}uñÑÅ7¿œ9<ç‹?\|ð–oÍØÎëW‡úÔ¡þt¨/mûѶmû϶ï,~³í3‹¿lûÊâ'Û>²øÇÌ ƒ¸ØòmßXübñ‰Å^¦æ²Œ©µ¯bHcðyõyÊ­ G×ÚO·@Ÿ‰wêýÑ£êÌ£…‹çÓ{Ÿc5ÞÚ+'gÛNÎ’YXv«ÔùDÁ¤|Q{ßuÞpLá &ì9–sè²'ÖÂ›ÐØ±ê̹ìßÛå÷qÁ0“ß¿å\ºìŸ–ýªržE~›N¢NÒŒ2÷dÚš ¿dê$£g2÷“©“L[“‘ŸÍ½ìHõ~64ÙÈɆW:Ïél¡C¯Dh³é¯ìAõ»f&:fcß™ðsòI•÷¼Ô¡9Ôɤnßó¸—I½êåQ–çR¿•f#«€ëhsà_m2RÑ;²<©+ï*êÁ£À¯SÅ´¥ˆ{Åè\Œ®ÅèQÌýrøÕR¯–{UôeãÔ‡N}”WS§>õ”7Ð \7Ò‡Œc#÷¡kdŒé¯6ÚÖ¬6Ʀ 𹿝yÝÈëFVãÕMºi‹Ÿ2?e~®ý´Ç~t¨RÏZÚÖ}rkáÓ„ì6dv ³ù Ôí¦Ýmr ]í÷#³‰ñè†O2šÊ»ÑËOŸw3fmèÛÁǺµ¡{}Q ò®¡½Ýðñ#¿›:~i 4~dvÐ~duÐGm³jù¯>~ôj£¬›:~ôé¦N'zûE'© m7õü{Õ²`?méGÏ~ä÷ c2ûéþáÁßv÷­ï[Þ·>¼o}xßúð¿ïúð¾µá͵alßò±ýO½N,ïÔýl±!¯î€Æ”òk<ë€ÞcçÕx³ú¼Š[cZÏ„`”Œê3í1óoX窈 Á(Y Ï.úõÞäi=¤÷&G„ìMžÒçÝ:WŨ>çâÒ¸cúL|¬>ó2ª÷'G«s/sX%«4VÉdÈùÅUÐx¯>m=«±ÿ<:gÅ„ÎY£ÏΩ³1æ•SaÃÊ~AÁ‡µ°ÿB°ÿ¢õÆQ}†12ÛzB笈Ñ9+6+œ, ß::,oE„ÆÍr«3÷vI„>{ïÁRgk¬ýËcO+JŸkÐx€SzsŒÂ¤µö2O†ìeöiLÀ€ÞËìTxµsX&Q÷z“:Ãoa™DêýÌz?s@ãÆjL“Á0ükgØYÇíÕ˜&“ÿ:ZçµÐ{›'5ö2mâÒù-6i¼ÀI}öq™Â€N˜QûÊå RÂ^uIðu;9Fá îI2úÄÇjŒlú#1ê3` nžû$Fc úCp²aX‚%8üwbŸøçÉ}ñQ,Á9Ü@·Îs±YcœLé³ 3 WM0,œ—>æ×{»GÕ90qeÏ·œ÷”sV²7Uök[ç7£Õ/Ù;.{Äå̦u^sTa±‰+-xÄçzÔY/ÙÛjí¿ö«ó–‚Ñ {¬ý §[ÎM þ‚쟕=³‚Ï 8k‚Á x rUö¤ þšì‰Ü5k¯84N§>_ŠüXøÆ¢g,4qÐÄ¡¼â ‹—tñÈË„g<4 è–ŸD®©›HÝDÊ“¨WÊ8%Á;™ëx¥@—Âu mN>œ|wB礮“ñr2©ðL¥½iðHCv%z§!; Ùi´9 éòo|3à›ß xeÀ+ýÊá•PáCc”%± ü²i¿‹qsQæ¢Ì…Èl¤¼>mô-eµôE5tÅДr¿|dð=Ýò‹|Ƽ½ Ð¥…è]ȽBî¢kºÑÆ"î¡«›úndS¯Xø£O1÷Š‘]Â÷ƪú¥è_m:–É>eð)‡®º øWÀ¿Ù èWAÿxÝ@yè|<ÜóLª§ ™Õ”WS§>ÕôK ükà]CŸÕR¿Žë:d×Á¯yuôC:7 g:6 Û /÷½Ü÷§ 9MÈi¢~3õ›á× ¿fê4S§™>iAßôm¶Útj§_Ûѧ~hg<Û'UxåC~'2:¡ïD×Nè;¹×‰ì.êt£O{¸ß¿tè¡-½ÈHûÛòOâk;®=ïgÇÏákÇ6VgxŒ,ñ°½Ž,q/m´âZæåܲ᳆®%‡Æ§¡1©JÌo†Çš_Jl)ñ¤Ç)ñ¢½¾ Ú1 Ä{ãÙ±}hÅuË…®5KÌ&qš£I<ŠùcÇ]v¼%1–ÄVSÑ牛B×™í˜ÈŽ…ì8Hb ðøÇŽ{옇9lÅ6ÏH,cÇ-¡1‹Ä*vœ£Ø±‰Ä%‡H bÇs„ÇÌ+ž8ÂŽìØ!4nx!4V`~[q‚Ä¡ñAèZ4só#ûD7Ëï·ý}ñõÅ·g -¿^|øÿÿýÓúîËogÞ…ûìb¿V;£±g4þö°Êƒ`á¼ú5fȨ΅0¨Ï‹ès/˶¶àh[çû&î‡`}¶‡œÙ³0°‡5þõ¤ÎÓ¶Iã[i|k§ÆæPyØxlYy×,ÜjŸÎ¿P¹Y¬œ,›4þÆ2UíRùW$·œ“+’3M°¦/cîlŠG “sß6Æ“`²JÁrÜ&Éw„¬$ø&¡s2íJ†W²øtÔI„.’©“(eôI"¼“厜lh²á›(¡Ë†6í*€w64™Ñ*ïX6ó4UÞË^µT˜IlÚ—ÍüÌ¡,:™è[Ïù.¼hKt¥RŸöç0.yÜËÛ¤pjó·"è (+¢n1ºWÑwEð+GßrÊËÕ™ðdÊK¥ ™µð«…o-tU´½Šþ©„5õѧ]³)¯¥~=ë©[Mý^õhn£~:|ÒøäP¯]¡é¦¯Úл ™~xùåÛà釦ƒï|ï¦Ï;è‹nú¨^~þú¥Ü/¤~ê»)+†¶]Šá]B?–@[Î÷Jt*å^5ãQßú Œ¶T¢c9÷šà_}¼*è³JƶÝ=Ð7@çMÐx ¯æo5´ÕôkúWÓ_5ðmbœkà]+ïzú¢ŽºuèX‡îMèÛĽñ3ÐÉK{½èÜ$>tMðkÈo¢~3üš¡oö«×XóVõZk‘÷:ýÔ ]‹ôú´£g;òÚéßv®}Ü÷¡K'<;ѱYÐwr¯›þï¦m=ðé‘{ÔíAÿ~té§-ûÖ×ì[_X°o}}ßúúöúú¿ûÚú¾½×ÿ^ëëòNÔÏ.ìß²Ÿnï¬Æ…Ðùtfuh¿Â‰µòé¬ÒàCq¬œ:›†¬•32VŸwŸÔ9Ð<:§Î”Ætë\Ð3?Ê£sFŽé3ñn>©ñ¤œÿcLçƒŽÕø£{6ZãKMi|)·Î=ªsëÄj,ðÑ,pï<¹#7«¼‘È*[gP玌 Á¨Õxà±Á¼‘HT0/›•{""$wäP&xLH~ £ò¸ V•… ©ñtž½:O…[ç‡žÐøàÑ#dPç­(\ ÷Ö§sEO…äŠPy,¬œ;Q?p@aâZø«4Vøf…báFé|у WÀ ‰Ò9£½3|,$룸!V~ÉX¾Uå°rðÄèü’ƒ:oô”ÎéñCÜ:Ïäf>¥qD"5–ˆSc úΠ•{rLaŠ[9z–é¼ÒÎ<=CŸ[|(,·tT¶ø@H®ž•ŸCðÅ-lq¯Æ'·´'$·ôèÇä–P9('EpÎS8i¡Øâ«£4ޏGçä>yBå‘vïUX‹â'úTNÁÇ,̳4–â˜ÂV\G ?Å©p?%}Pá¡H~ÁEÌ2Ác´pO†4ÞÉ”Êy"ùJÄœJÁYlÁ:ÜÉhánUXj^2<Ó7+<5ɘ>¤qŽ7+|Á‚wÜÂD[¦ðw"o(ˆ¥+ý‚ž±Ð”£K÷ã¸/äÅC¼bt‡ÆÉßôK¤^"õ%†Gï$ÚĽd®S•‚¬®Sho ú¤ “ïNÆÒI]'mvÒ·©ðJQa@<ÒŸ†ì4d§ÑÖ4éCùÀ7¾ðÍ€o¼2à• M&úeÒÿYè‘Åød1–YB¸¸vqí’kÆÌ…..‰¡gTh‘‹ü\xæ"7š\ú;]óÐ/™ùÈÌG¿|Æ"ŸúÔ-@Ÿt/¤n!÷ ¹Wˆ¾EèSDÝ"îQ×M]7¼Ý(RLÝbú²xR…,%´µ„òä•¢)ú”A_†®eèQ¯²QÎT@[Œ dT ¿" Cä{àᇇr:WÑUȬ¦¼újxTûÞ5ð®¡ßj©_Çuº×!«YuŒ}:7 cä ¼ðñrßË}/|šÓ„œ&ê7S¿~Íðk¦N3ušé“tmA×h[ mA§vÆ }ÚéƒvÆ«2zú߉ŒNè;ѵúÎ)nuQ§}zøÛÃýøõ CméEv?eýâÇpßúgÇ×òbµãiÃ?<Ž–:4v]k·ceñ$6¶ãa‰…íø7f2T;æµã[;¶•É,±l(†]x¬jǨv|jÇ¥ƒÒ§VìwÚkî¡{»íXÒŽ'+f CcB‰Ãã@;ö“8Ob<úù#{¿í8Nb8‰ß$v³ã5Æ}nï·›I s­êœ9+oRa¡ .ꉣ Np«³Jò;ÈãÒÊ¡òÇN¶ä=\Ùê&0F •¿-aVaºÊ£51BáY%F*Üçxd'F+\ºDtË_“þɆO¢ø!´-yHå«If^& ÎU#÷¨ç¤~ê˜ÊUî¤~: ¨ŸÿtôÊ?š×¨z¥tAßMŸöAÓG½4¾7¢åmµ¼Ù†NmÈk㢃¿èÛ>ÝèÜðÊ…&Ÿk?×~¾û¡õC[€¬B® Å'¢=nÚ^Ly1ýTŒ.%È+¡OKUßRÚPŠ.eôYm/£}eÔ+C§ h+ ­€_ü*¤ +)÷ÐN<<”{ÐÇCy}_}5ôÕ𨦟ªá]ïú±k¹®Cï:ƧYu´«êh:6 £>^î{¹ï…¹MÈi’¿Ôo†_3üšiW3ušis :µ »Z mA§xµ£g;}ÐN[Û)kGOò}´µSü tíDF'¼;eáÓµJ½ú{¸ß¿î÷Жd÷R·ŸvôË»Z^–ò/&ä{¬zÖ[ÿ>íïû~ƒøçý!¿?|ÖßöýîðÙw°søOü½áßé·ÛÿOØÃÿ¯ü;ƒÌc§~nL,˜ÃzµtÓÙN•OÍÊw«sfNèœÇ1:‡Â°Î¡àÒ8ÙS:—‚SçÒ™Ôyè]+{‹à:‚ëñŸ§tÞLŸÊ›yp@ãe‹/½5+›ëEÐ/‚ßâ¨yð²áwÈ ÎE>K¸^²Icf/ÐùÚ _ИÙÔ_¶IåÔ±ògºtþ6t[=«ó¸ùTÞååÞ’‹~ÎçæÉ¡¹JcgoRyݬ¼:.ŸyJ½z­ožšn‹~Jåz³òg¨ÜÍV.úH•“E°k- mñïWi íA•Yò¡ Ïc¹ì¤z•KÎÁz¶°´Çt^úHcþÇoÕ9ž#unzèâø$Ц¨US{S¦¶WåaNð¨üÊ ^•úÄ1…·+ù–{WòjJN6É#9æ>’[ž+)_É|Z9£sÕ;5¾öpHnMú>Ú§s±oRùØ3¸ŸêR¹ÕSá³Æ£s¬C³fPaüJ~HÁض0´#T~+ŸN”Ê©³vHç­S¹3$†ä§°Üê¯ã{þ€ÊG-øÕ >ßfXç¸SynÇ: Þ¹Òg“*_†äÙ‘œYÔ+„î<î7¤ð‡oXrüHÞ‰$tuTN׌Ê[!¸Á’C°°%?Ee¤Ê¥ãæ^!òJÐ!Ž:qÐÇÑ—ñÐÅC/±4YÈH±¿ºù.eÈM ~’ôS³$x%Ñ–$®ShoßKøî„Ÿ“68á™J»SÑ/]Sá•JýTêUÒç5üM§½éЧC›.ñc˜Î¸dÐÆ ägÌ*w,=3¡Í„o2²èã2ôÏâã“8ûÙÜÏáo[¡ó!3¾9ðÍo.uriw.uê)«¡]yÈ΃6Ÿ²êp]Àu¹øÞЊ«\½"éOÚSO7íp£¿›6»ÑǾnø»¡/Aï’Yå–¢K)üJƒRø”s]Ÿrx”ÿ½Êé‹V®+iG%}Ô m%ý_‰ŒJt¬¤ï«(¯¢¬ ™­ôò*)GnŒÓ¬r/ká_‹¼ZxÔ"¯}ëé:ÖS¿žúõè\Ïu#÷áÕèU®f#´ÌFÑï­|o•ïèÑŠ¼Väµ"¯y­³ÊUmC^òÚ׿ú­ƒöuОÚ×Aû:°±dtÁ³ ž]ðì‚g<»àÙÏ>ôè£ú éƒ¦š>hú éƒ¦Ÿ{–o&ÿlÜ {­Wâ—Ðõ^;V±×xí˜$4 =ìxCâŒ[ãµq:>n­7|ŸõÇÅ·?Ib‰æ‹Âýÿùü~{MØöõCýüÐõaÛ¯õåÅ·ýwÛw=|]XüïùünÛß¶÷ñÌçOÛ~´íC‡¯‹¿lã„Øþ±í‹Oê‡íåž×÷ ÷yçóu?nÝx¾5ãOÚß-¾ªí£ÚþiøZ±ø¤¶?ê{ÎçsÚ¾fèžïðõa{m8Ü´ýÅPÿP|Ã[´¯æRãné2¨1ù]jiÀÊg>©óÝÒOT.++!ÏŽˆ€ÎuË\H?-d~.Væ·ˆçßbúi14‡lҹǩ·dRç!âÃØ.óª%…Õ•wpõÞ`n5Á¼¶’wbÝ€Ê+'ù«$„ä ´òËmVy_×D©¼‚’?üKQ Ûý˜ÊM%ü‚Çoáî»Už[Éùz¬WaóK.¨õ“*×àð N~œ_å…Z+ïcäÅ!'qMˆU¹î\*OÔd%yT ÁïO¡,…ïk7«¡r[I|®ó‘_H¸1•{*ž…ðÉåž¹qò®6…~*‘w4z—È;[ÞEÈ)£]•´µ=SQɘ¥Ó‡ù´-WÞ#´£kÐ%¾•.õøÉ@^.4.žï5”»í…§yù“j)$™•èšß–Yõ¸j…·OÞKRN™ ¡÷ÉûÙ>äô¢‹]ÝÈïeœz¥œë^ú¨þ½±j餗~é…O/<*ic/Ÿôê…W/í¬¢jЩFÞ}ROÞkÔi¤½õÒ©K™½ÔkDN/ô­ÂWÞ_r_êÑŽVdôÒŸèʵÝ{is4]ÜëCÏ.ä÷¡ã‰”Œ¾ý*N²þý#÷÷þ+®«Í·¦öXOûGìçý[{y÷íãý×ÛÇûY×Ôþ_×Óþ޵´¿kßîÿÏëhç/øôxûÖÑþ9ëh2wÝÚÖ±kº4_ìk¿e:Ï×ûaûG„䢯†àú€XonLåR:ÚgÂrÍA{÷¢¿Ö¹I¹Ž€w²#ft>júã`ž s½9·Pü=®!gQ¬ÎG ý"ìsq´ÊÅ´˜ëÅ3:'ü¡Í‡p º/AÞ’Í:Ÿ)º,噵û\í²Í:¯)÷uë|ÔôÁòHÙËÑu9¼‹T9ç£þa´åð•§ìð!=Ö,Ó9çày߀çÜ_©ò˜Zù¨½:ïúEF©|Å‘[uÞ9èŽT9MšP¹ÏìÜP’Kêhô>†{ÇxÂrRSv,´ÇBsì”ÎKMãuú7«rP¥òŸÅ8ÇS¾‚²è¹º”¯ ,*Zå§²òÑÁï„•£údœ€¾'Lé|t”݉èrâ„r¬üÌ“:/ÝPH®jhWB»Ú•ÈY‰œÏGéÜtƒ:7ýx2}p2} ŸhÊ£Ñ!švE£o44ÑÔ]ƒ¾k¤¿ý*dzäK]³Iå©–œÔ’ZÜ É;wó) Yg#-|ÖŽª´’ŸKrnKNWÉ7½Žþ_Çõº½Ê‰¡,†ú1Q*V|\â_Ó†|ä|‰ïë÷*7åÜ•G64–äj=o³Ê3+ùd%_žä¾;oF¹'ntÏߪrlmجsf-Pyãb£tÎWxUò·’¿q³Êõ‰ç~<÷ãiO¼ÄÔ/¡oÊà€^ ðI@^ <“øä‹_M4Iô]ý•²W¹KNôvÂ/úzô÷Ò©´3UüZdxÑ5•z©r=¼ÔK§<]|z”-C§*Æ!ÙÐfp¿þUŒ}&ü2©“Åu|³$Þ˜U®X#r )ËE·èrà™Ïx抯Ϝȕû³Ê]˃>O|lîwE+×­€kŸøóÐ §€òô,¤N!zQ§š"xq¯ˆ¾rÓ7}Ð+±üÝЖ W ô¥Ð—¢K)¼J¡-¥ƒJá]Nýrê—#»\®™{å´¹’~ª¤_ë™2Fô{%ò}üí‚®Š±¨B^•ôôU7 «Þ5È«E^-òj©[KY-òê™õð­G¿zê×S¿}ëé“F‰3Ý(ò k…g#òéƒV¹æ{«|G~+2[%î`\Z‘ÙŠ¼6äµ!¯ y}ðjCfýÓAû:h_íë@ïøw0~]ðì‚g<»¤Mðì‚g<ûУObhú éƒ¦š>húÅßSþ…¯“ý-¼ùÖÇBׯÄ0Ã×Å>iMÌ |t£øêó­eÍ·†¾~ŠaûqkWáþ±5`¯cÍ·~eûµ¶ê·†®_1gçÖªæó/?imJ|IñÃפÂ×£ÄO _‹ ]O²×’B×Ü >º·ÐöŸÂ׈Â}!Û _'ò(¿e¿H•³qÿe:Ï9:Hù³:Ç9sí æ\„Gåh\È<^ˆý,ÄfQg1óe1´‡`KK ]ÂÜ]Š®K±«¥{U>óÕ3*ǬŽÂã0¯ÊM¾Ö­rÉJÎе”­GÎæåš•Kq ý±YkÇßuc*¿÷ZxÄ!/>Š’¿ðIr«<¤qðZCyõ“|*·cuR&UÞï4ôØß4¾¯ç“}š<7ý*©ä{\N.d¹&UÞÇ8Ú7ªòç£wíÊ‚6y…Ü+”g"|²ø” O÷ªÐ¡ 9ùðÉç~úeÈ;Ý+å|K¸.á~.õê™7^yŽs]OŸå£g zyÑ¥y¹”·R^6£Ì«•qóÑÆBèªèr«hC/ü{¹îåºWž)ÈêEN#ãÚŠ>­ô­^>Êzáí£¾ž>dôE(³íCNüû¨ÓGß÷QïDdLÛûÅf?ã¿óǾñ°õÍñ³ÅßûÞÙî§6üµgYfÁqމsFÿ8²ºÞqÀÞû_ó¾m=õ—ÿ°ß†gÕu8'sä‡<¸0·ÑqÒ§F¬©2/Øí®M:é}Ǫß]ûÍÅ[eÉyjÜ19Ùp¥ãÑ7*/}ü—ÚòÌ@ä‹;Ü¿¼tà §vu,ûu¾9òƒ¶3Fvå8í²¶„;û6/H¹ô¤¥O_â8Ç;Ýqóüb¿†“VNœSgëéxtë—?óè¶ ÞŸ?ã‡>½Ö¹ùâ+·uµãÔs^ýí± §›ͳ.?âà"øxŸ%w¼þæû9-»pjóÝo˜è«ßØuÆwÌ‘ïnzcKÛoÍ7oï¸÷KÐXôO>Øuú{þÜñè‘ü5!n‹XÉK‡Üí³ûǹaÿÕ¯Ýþ+sãc?~ñ‚EÔRõÊÚ±-ózÇž½ùćG˜øçîØí™0G®»ç¶+Žú¡ãŒÏ_ú‡=¯¿hn¼÷/æ|ûûÔSõˆ¿ô•Ÿ­vì‘îxè'Ž…º´˜5½Ý?ß0¹çƒ¡û~•9rÙæµÿ늯8V/<ìâ˜Ý æÆÂ¢7§9ŽVôð XüžøÑc)ŸËzÚ±çøìÞÊ©Ÿ˜Ü/Ÿr˜9rA÷á?|æsã~=ŸO^x[ì¨ùñDÉ×.¿îåÍsóc÷Ôóß[ØP¿ÒåÿÞ¼Òñeÿáì£^sDWg>óö Ž×#jüŸ8ø†;?¸ú­¹ñÚ½9mIÜ¢š`{ê&=»âvs¤êüÕÛî÷™ã*“÷NõDûã?úFÙÿ¹w¥cwÙÿø…™ fÀ÷ëI9âzñµ×wí±Û ½ßÇ7D6~5§Ø±ûØñ¯ÿô)3pÑ5Ÿ/üùs$.ªtìèuæÆÅo,¼°z5¾™³I}/̵w×SÝ×ö6œWW¬xóîÉïox¹luñ†ÊÌ‘ué{nk‹cõÈ §]¸ÂìOºgï¢÷'¨q€¯ÿÇ\—Ën‰™kÿ®þk.¸,1ØþÍ×u?Ütqp­¹¯âí?¿b÷§é·º‹ùôˆšúá©¿?ìzÇ®u³Ü‰¸uËŠõE¿ ·µù=?8:Ãì˪Û>ôà ê©ñŸØ~ßÝÏ>»_°}Ë‹·<¿çOÁöìI¿#}{Pó¾þ‡…«üsöØ;yÍEÍuw:V¨ú±olWódi鿺ÅncçìïÏÿÚMW™‡»?Ì¿ùs$±!渋LÇÉj^˜}_j=øÈçîΓíjžL/†ðÇÎ]b0Yf`w\îðm2GÒ2O;íÏ÷™~Ç5¯'¿]½šêçÍÎ.ùÜÊ—Ï4OvÞ\ü䇿HFâ…Ñ¿8Èìwuüú‹î„^͇Gé¬cŽ»nnvÖ,¼&¯ÿà °ÛÿÌ7~“}æòàø&ùã]o}Ýn¯Ù¿Íz`ÀOÍ—=\ýæ{¿»$ØîSrJ×·~´Ë\–Ÿo+ßùÂäªïÏõCÿ¥C¿^vÿjø©y²çú™ÁMŽûôÎyÿÛ ¼4’\ñõçÆcø/)ß9f·Ûì?w½wäÆ8ê©ù°çÌ»Zî[k̵klj SÌÀ«‡Ãû{çä#•'âÜxöŸwϪû§à£æÇîm÷·_·öÇzîeÕfà73É·>g忲1kã…޳ôóÞ¯žóA;5Õ|Øýʹµ%Ç9vÜjM|3ðæ£=•l¿tVçqy?3ý‹VFùF'¡Wã¾ëƒmÏ_œŽ—=¿âœ“±‡œ·_r¡-Ïìýï¯Ì\ôÅ ¡Wã¾ë[m~ôÕ¯ÌÍãÍ®áw.¸Çœ6¾úÇãkÛÍáÿzÿÇ)‹gêqè½æö²É‡Úí÷|Ô|ØuÆYo½}ÃþÁ~“Çâ]?6§©Ÿ}z:Ξ†?\ððöªIÇzm¯ZDZ¿üãM1ÃéðSóaçýßaDuì8û·7Ùú+súè»·d–_´§ƒº½—o}Îìór÷ÀÛÔSã¾sÃÎÙ¸ôÇŽC/ÿÍXÛÑsóqzUsñámæHÄÎwëqÄéç{ŸûÕ£êN{6h?¦š;vµqå×3ææãø{w}ë¶¡åsóqzíîÕ³æðŸoþEû—ÏwœûQ»†š;ÎßuÙ]§?á.æ²â´÷Ìé¸cOÝoÍÁæðÌíǽ~ã³f/L‹·0oÆÔxÖÿzýí79ÆïüÁ]‘ÏTšÓi±7¼“ôEsøñK6¿ý‹ßù©ñ_»ÕÝþ Žñ¯ÝtåuQ/Û›“¶piæÁùzÿTÛø—š›o½k2¾ZŸ5ÆLy±lrŒ·ýø²½+æž§ÓEý5ßùâ•sv<ü“±ÌnÇyÜ}øŒÙ3tÁÚS/šcÞˆºñÎsãúý3]Ñp}ÍÓ÷˜ÃqÓIÜ`v+ý Wãl~óæ¦ëÞ¹fnþ=|qßOkÍéžç%lxõ•ç®òGsøI¿ÌĹ÷rω·½ûÞ™;à£Æ}ûKå5ƒu7ÌÍ¿ñŒßŽ|ô‰ætçK§-¹å«AýŸšèÉ^×ëˆQöfö<Þ2t^úÂàsxLÿöü{¯ZÿúeŽñ¬sŸ¢‹Í鋞[K?Žç?öµÙv³÷à±çÞK‚^ó#ï.¹ø½¯Þê/¾gòÂKï7§¯¾øé{—›Ã–ùg™=½_<ò‘úØ7ÆÕ8?rYb®£á=ǸwϵïÏœœW[f®z·ëÔà¸mmíx·ºÆ‘øÃgî6•iv_ýŸÏùà׎UÿÃO̓GŒWÝÙmÁùÚùÝUK—ï˜{Mß8䘎ޠÞÖõæU‡:ÎÖ÷»^þöû§5Ííp\Í‹‡«o¹ïѧ¾áçí^²«Óœ~è±ýßðeŸc·þ¬4>é}Û~Ì®Ê÷¾Dgˆ~j^™µ÷JèÕxo[õ¥N¿òÔàüñýàù…çw˜Ó¯sÃ;—aßòʶ˜ïæš=§Ÿ”µí–>ê©q-ú¹ÿô˜ÛîãõOǽ³üù ¾™´ÓýÓ]Áùw˺w¯Ì_ç8gösW>ø¨m?¶_ûÆ5?è=̨½48že·=¾ÿ®‘àÒ¼íÊ/¯Z÷üÆÿ‡½ï€ªòèÖF@•&EšTé]AŒCÔDŒ&bLM4$ÆDc4 ±R4!±„¨Qc‰h4 çıñ‚,ôrЈ€5š öûÌ;ó¾sâÍ÷—µþ»Ö½ßÖbÁœ™Ù³göž½÷”3ïkRsðw—Þî±@ÌÃQI‹ÞØCz0yIÉ,N¦ÚìÖw0{IØ1Ö_©¹wû_;v¸/äOÍûja‡Uþ0¹kf~×ÎâÒ !÷Þ‘VÆ-RóÀ+ãç¾4JŒ×”ÍÉ%I­¤_菉9`TØÆÐarΓÍÖ§¢_Xœºi¾Wõ§yDçÜ›Sº ý™ëyzQóvÒwGÆŽ–/¦I©ÅZìÏö=&ÿí¿—}S}í)âqœâšß´¿óžË’6kD¿ê‰¤7³R ŒÚÀMãPŸéöi»d•¡?qožõñ+5Oœu wÆ×’vÆ¢÷ëîÜ’RNß/èÙ° 噼,.énöíˤ(š6xGjNYŸ²ív{I;ÚbÓWÉRªÝèèå³PžÉ} ]Åí!Æ-2ð=©9­ãÖ„ ‡kc¾ïÿÂË;É ³¥¸¹§–H©*³KA‡éÃ×ÓªŸÜýYŒ[øènßõ(5Ïn¾ýZ[¡Þ†%&ÎÁêz%¥—߉OØ:L6]÷æ±àTRu;¢vý{RóüaÞ]Š`ï®Áv“’Snw¸Ûr”É}㌦y63IQÿŽw׿*æÉâÂÖ¬ð'’¶OßQ¿Žu#>+NG?«µ’W4™ùõ™¼74®»6>ü8)´æçy nù|»îRäÔIû²ëº6ùªqWJÓíÔ¢'¨Ïä»JöÊB[RôÖAIÍ›^Ô]{ßPÒöÕÞøFƒ”ŠUÕœçPžÉsŒÄįV“¢ ‡c>}½Rjþ)dð›FŠqî—ž¿ú¡^;L®kÜíçÌj× »½=¦¸Ó!çå¯Â‘šÏ‹+›µUè3›ï„p}Ofí“ó7r87JÈYÛãÐÐàbÜKKW~#i_Û¸¥è“¤Œ±yG)™†•û@‡Éyɉ[ß?ÜJФ5ð”Åb~ÕP‚!’–­7I?>ß“iø<¹6¶å“û¢ )Ÿ…Œ„=óηG`<Î6¹\‘!ä{ŒÉ÷3oûrΣürb_Áoƒÿ?7Œ_Û%–Qo }<Æä;3yêb÷åÃHÑõEgvÍZ%57z¿téÊ:…?)åæÄÒùá?¡<“ïçÌt!uc„_{Òp(Çþªè_Sþ-§?wô¹¤È›µ«Æ)Ü~‹xâ˜,éõ¶Û|?9„ö ³ºsd°àÿ¢În{‹¦O‰¢½©Á¹®®;R&¯X„¥ðgÇd}Pã7uÝ1Ô¡´ý;IRó•ÃÛóG úàóбÓoéê>@rŸŒ—ÌŸœT胞¬RƒO=Óñç[Šþ¶ºøèÐLIkC•פ¤Oýì¤E=Y¤3£9¼dˆÇlÃ/¥æ;,~вõ™”õö‡.úǶ—å.}MÃ+Èá½Ãûî¶ gÍ/­:a®¤5;Òӽ攓å.-£ËŸ³eä0U÷ÀM¤þÑœœg+º‰q²•ÊËr–VÈîørĦ3iU~õWœŸÝhÿJŸFe<ºÈ†Dõ÷IÜÿ†)~í¸¬Ò*º:~çeUïŽ vüòXQ¬:ÿêËç] Ûá.æ}WjXO“žÜN%ÕȃÛq¦kË輄Yh0¬¹¶©?°ÿÍŠ#;$­×[FoÕXKÉqò 噜×ÛÐÀ·•9¹+Ð R¿eFû Ç’6bÒ1,­¤d–òLŽß÷N4\g÷9JÉ{.&õ+¿Y´zÌrI;hô鱿½¨ÆIlý‹zLŽ›ÆüF5…—ÞÿKR¿°dóœ1ÛT¿“Äâ·Ø–LŽ›Ó)ÑähÒû7¯o8Nêa%þ| i1Ù‰pÅn¸vŠ(Ú²õ˜\ü`æ;N·_QåstÃçs.5OòšqùÓ‰·»Hw%çn qlßFÄ'˜Ü·u_0é³›öb_¬fhí !jüUÿÉc«Ñ÷ß”´–tlUãþd|øöëWI7e]v‚É]ñ÷Šœu¤<3H}â‡q—¦&JºKi`«Æí|\ľÀ &缩Ÿú6–œ%Ç^Þ9±uÚ:R?¸·ýÃíªj.ú·{"^Y‰8ø“»æaQåÚÞ䘬Öþ¤>¢ô…+6kÔqÑèvl[÷æjaç™_Uö…@‡éƒFwzÒ rìÇGW?^Пtfò'õ榳ÒçI)lßå™ü4­ÇÕ¾ßÖ“ceOj¥ˆqdûÈ’æ~Uè2çL)ÅRžØ±-ÅLvv}´ô‹=nbŸ÷wºprÑÝžþûÜK‡„]²’' º•¼¦qêý'býSÌôeg‘¿Î±áGrÜgÖK§]ƒ¼±"ôÃýãw_÷ÿD臼ܚ,ÖµÅLOvÝ9üY‘ÊÏñ÷?±}÷k[¢«°º}ÕSe+çŠÿó·˜éÇn³OÞô“Fíçñ­Í+¢'™ªi]ñºØ ŸÏü9ËDªýM‘ͤ è1=Ù}lÅkã Ö ¾ψÎls†èŽv­Øàò½jW”ýýÚ½IŸ£>Ó“=«¾éëlÜœ Q]/¢Ãd{Ûq)åðŒ±©_@y¦?Oܸ´xÝ‹äÄŠnS†-¾Kt… f3|ÊK¬'‹™>ì5¯Ò,.!'®§ üHw£ùÇ%­µ<ÁU?•ºŒ.,Ķ”0}Ø;½ýw#?@Šyü¦è‘®`ôw±Å„=6™o*­KbýѶg®—ŠyYÂô`ï¹Á‡ãSLHñ·£4iOoݾ¹×~(ö7ø|‘’ïPK}õ˜Üóåé3ŠßþVF¨Ñí4rnùýõ>W­–Ízè½A’OY¢ÈËt—&ÈSJöËþê3Çw•ýMÐarÏ_vtÀ³PRÂç³NÞ†'æ3Ý s."/ò¸˜Û=1K˜¼÷9Ñ çn¤„ËM÷혦ȫ]Õý>¯E¼TÂä¼1JJî8QΈîKy‰öo¯)ÛZªÌK½ñcrßÿ¦—©vö$ãKèí§W4uß#ö Xœ¦ÌÏn7ÔùPÂôãÝÖ,{¦Ò9Éλˆn¢Ç¢’ Iȹí廿sï«þ‘Ç5±-'™¾`‘ø`ôrò~ìØµc^!ºÑo4ë iéöKòn1oN2}8Pàq¹Áö$95âJRsQѽþÙž¯_¾,ø·“ë2¡>Ó‹7 ðy¬:ïN£Ó‰ÅëÀãw'·¶ëV¶nëßsÔ¦/½ÿm»3ätlô¶»QÉD×w뺙£Z°~}ÜjvDÌדLþá4 ú´Óû<7¬™¾ŠèbÞºŸ{n‡°<~W÷•X?QŸéÁ!¶®WÇûLpÊÙÎ!;T¹êºç~Ð{Ê#—ðù§ÈQ݇<ÉôâfÑŸ¯n#g¾ m3dÁH¡Þô¤Rµ_êþÊà•ï´»¿LØ×“L øº»´Ðíèw†oÞ!iîemŠY1k—VØVøÙSL ¨Á4TåXzüàµa1–jºîÚéóKN–4Éå’®Âo§^“Z§Ÿbz!ÙÉ RÆ÷Óë*†¯ØÎHšB·ÎÝC9&wiÈž↓²Ÿ_Z²0‹Ôœn¾t›¤) ‹?{ñª^™Ü%êf –ªqE¹¼`PÇ»n{żÃ)BïŒéF’¤êÁTz,|v“˜§˜>šèn~#Ý!åC'iÏg>!uëËJ?6•´öÔ‘™ISåc-'”gò.|°é¤ü«·}ïþJŒÓÊ´°ÚŽzèØ°eÅØ1bßܺÊiõ°ï§˜¼ §¿œ=cÑ8•ÿòãÚÎî’%ú³„.Ä6cÝ@˜ ÆÛÓäekDlËi¦…|¾¼U^ºÅÃnD¯#ô—Çmê9ƒ²šÉ¿ðμwÜq&^G®fÌ#u E,iMË&^œ#Mcë9”gò-²’lRÁåW÷Eæ)·G’æ÷K‹¿Ln¦ï}ñ•WQžÉ¹ˆz»ÙOI=é?‹ÔQ3s©HÒ”¥Ó“(ÒK¦)â\ñ4“wQ Ï=#KIÅf“[— Tõ¼.3µÓ‡v9ÂoíÏöÞºx·z.“æF52trÿÒ¾â?+°ºJú¾NÈí#‰¸p×ÃI·Ol$Q<âç< Çä_ÔiÌ.ëORi¾ì×è`WR7yŸqÛ,øùIÞQã)~î…úLîEŽòQ#+ʼª^×½óu¢ED¼à‡ß«PâŸitû*óZlË&ÿ¢.Áwò†#•|®îÕÑÖ<ü°y+΋Î0¹¹È©Üwj|\ÁϤ.jõ볫E»yc-.‘¦;üÙz2ã[Ôãòg畤òž¼áDê<«3k‘*ÅÎM‡PýÖf£׃KNß/Pǽ*´Àì–ï"!‹SC\½$iè-ƒ7ÔóQõœî ׋شåÖUΤjüšõ jß#µO‚—o“¬Ä!ÒôEòÁ'Êsù÷·~sÀ¨î꼪¢Û-'JÕtíÍï©­ñ¹ì>b‚j×jÎíYû‹Ë§’†FùWERÆåJG)̇ÔA¸9©©Xäu«|˜¤™xqöõèJœ!î9”q99åÊ[-¤ŽÝ'"5Å&—3Ê$ [ÏI³m.¬õÊs9õdz}Q)ì>½ÖÐÔÚdµ¦ã`%oHs¨Ö%£<—“‡÷ÓäÂÿÐ(aj³ÐùBÁRï¯b÷çÊÓüm¡wåL~Òƒ:“…Ÿ¿Ýr~d'Uïjr<öèö«ê¿óö­Ÿv~ûmÔgò”rå—è\݃¦‡åš…s†ˆò .PK/Í™|#ÉÛ°D¬›Êy\Öëತ÷oÝ[}_º·c…àJd÷¢?¥<,“z=RÆYš}ðÇ pUŠ‚“oA+=0Ä:yå/çz–¬òyúáí{'¤¼³tÎÊé'ÍC/ÈMÕãƒÉ¹`÷Ø3§Ø]u:5¤¦Ïfãƒ>c¥¼ÚéIŸ^^'Íöû%¡,Ê3ùÌÇO õ–òF©ñÌ0ÏNx&åÕÛ}`ØIÈ·œÉ·`İïoÞûaüþ¢:¾¦»Úû^Rí_=HÝ®ÚíÙ®‰]Vœî¥×o¦Tm¯hIýÌW<£ oªý®¾›êwdÀua'è…§9ê}ÌÙÙôàh€Ðö̃Tðºí´/N:zùÆ\RÝäíæ=Uµsú?lkó'Ê3¹Ð[A/‘úZ9ðPûS°7ùY«¤¡§Ç©êþËÜXycPÄçL ¨u™²D­_ÿèÐL·°2ÑŸ£‰·Ò×c~ûzymj?Õ.¤ó{a¢\|OUt{å=r£rãÖCR½WÞØ”4üþTº²OZÁäèœ| GξæM5@ôƒ×uÝ’No&y£>Ó‡C3Ò>ößœ•·yU=®^™¾ìrÍN!×ý¿FÆx©ö:clDfÁ‚"áO*˜¾b~Šœ¥Û¶¦BOªç{´XTíRåš÷“É*éj<Ÿ¡ì£V0ý8H¯‘,Ó³œÿêIñãRÝ¥<¦¿R†²QÉäàøæÛkw ¿|Žé±ÃЋî®þ)ì@š=,ËuÁÏÝT;ZÉôä€|ìØ–œã÷ «#æ,ìð9yXTEÎÉ×¢^%Õö•Ñ]Ô}Ƽ/<†Y¹v’ÒÙyê1¹ïÿzÝñ çÖËfHÕÓÄ#_ENò–Ó‹KÁRºrÎQÉ便û;¯ü‚œ+—/nˆ8ðjqÅ7_|¤î*ëìôFjxSDRÉ俞Nu´ çМþ¤ªúÄ$«™Ã¤¼¯´O]é*eÈl…‹ýàJ&ç}/ÓNU_~ñ/¦ªã]ulÒÛ·Ÿò^dyϱD«Æ#ìPè%“{>»§£öç¶>VãûªüKù3¹#åaq4aG?Õ¯g°ýóØ–*¦ùnGWoºøùEvÓ¤jûŸý?Yý­”7gSëà ê¸d°{E¨Çä½÷º`E~‘ä…©ÊuùU—3DÊ{'ô계^Ru£¯/Ey&ïŸùúþ¾OTÅä(åÿt´ûÉ)C97­brþ¹ÏÇÔÃ’ó4JJªÒwömßq‘´CÞ®6‘2•óÂ*&ç=ûwÚÛæÎTÇõ<5Ç¿?TãŽ*z-é½Óª^ï8?Ù¸p¤»wgÚÊžÄ= *&÷=þó¢Læw ççÒ Õ¡?Ã}èÒG#í@Ôåñõ#)ƒÝÓó¢ŠÉ÷Ö|zãG­wžÝÃë‚ÞÍ&ßE„ ½7J¢7¾Ä¸³ó[±¯YÅä¿Û^¾7LÎ×Ï}ïç¼Ù¤ÊõÙ§Å}ŠyëÛ­cšÑ%¾ûŠÕLî»hXÜþ;Ò ‡æ¤Êоỡ‡½foróc “û®Ô/xOÜDè¶SÂbRyyV"hO¾žú¾”Áî? <“û®ð³­f¼¦Ú›y»n¢š®<娨ýœ°ŸÉ_Ò•ƒb/¥ ~ަúój¦;Ù9=i`÷QÔñ­ÜmX1qÅ3u?1oó]ÛËw•øMÊà稪ªfú³óòúcïª\¾•/è’ÊÜ(‡Ám•8EÊ8miþÑãRÔcúñÓù¢«ËgiÔöäkžªV.>³èàÍù¢t€Ž©ëÌÌÔh®Ð›j¦7? ÍüqiÇ&Òp¾¶qpÛ%¤rΘ­Ú¾Ã”þH™Ê=…j¦ü¾"¹ÐfñÔO}›H%ÓÆeÊy–”¥Ü©aòWÖ<ÖžO*éjÈõ ”—ð}Hÿ¯ý¥,+º`üå™Üó².yå]éN.Ð[ÉìHe߉{FÆ })©÷Ä2Ù}±­aú°Ã|¸±ýÒ©êø\ » /|F*éí¿cU{¬ÜSÉj·~Å@ÌË&ÿm¹ôbÜ3U.Ì{µM¯%âü¸’Ÿc³eÈÿ<j˜Ü·>w^|ßoPø«¸µöÉöñˆ³}~¨Ìv‡Ýûþ¼†éÁ4ÀŽ&hXSª!ÒŒêS¤<oH™F¾ƒ!Gy&ßÍl\§ÇLU*òׯY;Ê^Œ+‹›¥LyùŸ‚úLÞ›è¶ü½yäVe¯]û“ß?.iYõ‚”7ÖõKSÃ)S9'¬er‡/…eU§F+zqã¨hYí_öôV×Çy‰é©#®$ }e÷­„?ªeú±}¿EÇFÏûs_=ô€T|þ˜î˜ »ÄãO%NÍ¢Ñå\Ðaú±^þºàO>–ª¦+fÿ¶öƒ»ÅøðxNYÿdQ©LÌ=¦'ëZ=Üܺ4¾8¥ÏòޑЩÎÍ‹k~SôBÊRîÕ2}XùçÅŠÓ–¢ܯªãóqð“~‹\DâÝ‹iVï9eÅXN9{&è1½Pî#¨ýy÷kjÑH½Øÿ)nó»~‰òL/VìÞ¾Æ\"IòÅx•ŠáÏŠl/Œ~"°3$qC¦ËØ}Ò8§ìú^É•T úÉn}OŒ—5½èÒ ÆYÊ=©:¦_Ó[.Ž Dÿ¿w«ñ+×Þ–òäí5ŽÊš7í÷M?ÒãKÖ²”`’&¾VžL Lž”w¸ôå9“ÄüÒÉz@–¿JÁQ¤ Q¿ó³¤<ñ¥ý÷Âÿòèm³K…=Ó1=ø–Nß½¤‰Jan)”µÐÊQ/Þ?õÈvO*æ1»*ä¨cz±fú*ºGšèvЗŸ«v¨<üñg¥ö‹8çî«Ù¹ ó‹ÇGªÖ1½ÈeY¥Ód-w”;ݹˆØ ¹ZzŽÝ9Â@ÝŸåv?¶¥žéÇïôµ‡ŠùÙDoÉÇIb~±s[±?át{`ÙðªÎ”é¹ Çôc#½žcó@õcMŽî-=–&’²K Fgl%Ëbx"±QõµžéÅcOГ!Ò„Ežå='R†Uň͟+ú©ÄY(Ïä¾%7'¨Ñ̉4<Ù}Ú«=)Û6¬6öz¤a÷D¥ÌÅô ²#QžÉ}+Ýå÷îKšz<ŽòlÕ{·zx¸Kš¡ôYoÕnÔ3ùo§×¿Û}Ošú½u¶7)[Tk×úQÕŽkâå‹Zê~G–ì– =­gz°ãYf@ÁÉí¤‰îÂEn$eìûc’fä¬RnKYò1_8Ê39³ãᩪ<šØý4Uîe³º½pºÅOõcyûÉ@ÝÇÉ:#/„þœer×Nÿ•z\ÒÄïí–%/Îÿ¦ýû’æ]ù‹¹b¾œerýé•?îèú—£¼¼ñ@ÊÞ7_;t÷SIC·¿¶•HYì¾Ê39÷w4¤‰ß«-‹/Ú—:l¤¡×“SÎ*öå™w¹4÷¾ü ib÷üIYÔÁ ·¥X¡Ç“3—­ž ôŽÝ«1`o П°¤ÿ{à8üƒ%ý?û ºÿØôg<ë·ü>H(ã ã,ã̺áw<Ç™mdooç8³È7†¾ç°·¾ŒïpœÙ=œÙönk»|Ž3 ýotûrþÞê›@çLPßÔDïͯrfBÔ7<0.ÝþŠ3Û±æô:máo½¢=³Dþf~>Ç™ãoæ#ß¿ùzàÏå-Á¯e:3ü[Á~X¡Vè¯U+Ç™Md8VÁ“5ò­ÓÆì_ÞËÏæïå‡ê½—_̱®ð™]:Ǻ²çxW¹zø²‰üͯ; ÷J~+í;4²·òeLÙ\þN>h9!í´…™FgôÏü8ç33)cÉ&±÷ñ]@Û%Š¿¶\À‹«/Ç}W¤Ýb9v,ÒnhÏÝžcg!í~<ï‘Æ1cѾ'è{"ßsËsX±HwCÿ½BŽ–úîúÞH{£¼7ø÷>àÉ4}À£>ó¹Ãq`Ñ'_´á‹´hú¦ßŽï žýQÞ}òG~@(Ã× @Ȍ縭ù³5”cµ¢|0Ú F{Á¨Œö‚ÁCè…Œgø«Ô„F1ìUú–X}&Žá®†•37Aßt¡o‹uG~8è…Çów‚ ùÛbHG¬ä¸«öì}úþ.}S,éHÈ;ú‰ñí Z=ùÛÁì=±(ð•Ï0Wå÷ÝÆó7ú[fW¯ö>°ü¶êÇ ?cƒú½ùû3òÛbÔöüã£ÿñÑéÿøè'Mçkë—üfWgã(c&1LCÐ6Âü4Jb˜2<ôÙx%{3KÆ‚ãïf•s\›Dþ&g!Ç€LÐÂwã¸6éøE}SØÓXö¾–ŒòÀO¤; ß}õpmŠ™i‘±àµüMN´g6ž¿Ë^¨‡kƒòæÈ·ðåoržE9Ç‚ãXðÅ×öÁ*I >–cÁoáXðÈ·Nç˜6 oþl9¦ x´bïsÊos–sL›(ޝãð‰ì=wÏ}¶Gy{ôÁ¾•ãÙdëa¿£¼c’Þ›œ å„´“–¿Ç‰þ9ƒçBþ'ò»bw-ÖÃyÏæïàÅ5”aYRŒw6p‹ã¸–4öÜÝv{>ÇvG¾G:ø”qÝ£8¦»ö9œ-Çrâø4åÌ$«î[~»xò‰ã¸í[8^;Êø¢O¾ù—4ý@ÓOË̸(Ç^/æ˜ë(žÊ9Æ:Êfs\uðÅñ/Q>íÇqüô- ;=ôB‚üPŒYh,ÇÏA: :†1 CÙ0{«Ÿ¾Ã&ãæ ?Ü’ã¢g3ùíÏŽŽ¶{¸±·T{ä2¼œH¤#!ïHèC$Æ·'hõÏÞö§o€F¡¯Qi biÒ÷Wé;ýCGÆHã¸è:þö'êÇ ?cƒú½cv‘ŒCm õ#ôÇÐ@øbÅ+þWZñ³Ô¿*¾”úO}IýäóþQñ‰Šï£>ú;êë¨_Ó÷aèÿ_ü•⋨¢¾Gñ9Ô×PCý õ%ÔPßAý…¾¯PüµùÔÎS›®ØoËYÁq¦¶YßëÛ_ÅöR›«Ø[jgûªoOoó±ƒÌ 1&†àÙ²5Jãø^ ÷6žczs/è€)dh ûÐ!‡ãq™0<.‡«˜¿Œúæ¹ çÖõ­Ò8ÎmÇÓÊfoÿÚÅrlÛF†aŸÏÂ%´ë„ÿAË4»¦sì*”u…ÌÜLØ›ºò[º­ìÍ\ù\ÐôŠeïâúhyø„ÏýQ?u‚ÀGxF{!iì­Ü0ð–Àp¤º#/ÜžáGE ^æM_†7A1t#ѧÈ;ìíá¨t†Û e{ß^ø,†ú£âÊâÊtƒâʧ¸’η4Æ—ŒËßdÕr¬DÌÃtöλ!êùêá%b®'â21.çx‰Ô¾ær¬7üBÛ­äx‰öü­÷•ü­w_þÖ;tÆù¦È7Åü0]ÉñÁOðÓé(ß1Tï­wŒS'KþÖ{>ÓôÌ’ø[ï gnÏßz‡N˜#ßõ-ŸÃKŒçx‰åÌ,YE1¼DŠ$c—Ç=÷æ;ò­sø{ïhßüÙŒç8AhÃi[ð` mu +HÆ9G¾Ý5Ž“8þ¯ï½Û£¼=Æ×e09z8‰(ïˆöQÞþÀ ôÒ8:úçŒòÎÙÑ’¿ñŽt×rŽ‹ˆþ¹¬äøè ïË0é»în(ïÏñií¹£=wÐwGÚå=ï‘Íð©¹õD}Oä{æë½á>žcÞḇ ç…¾{›0ÌCoôÏü{ƒðäš>cG¢Œ/Êø‚¦o!3Ý~ éš~È÷G¾?ÆÜ<ø—3“€òh#m¢i`ÃVÄ!?ôƒŒö‚Ñ^0ÒÁà!2½¤C@/õCã8&#Òaèè…i&}¿¿{,KôÂ!¯ðD†Eßê¥ïÉG€×”@Û=BÙÀò»òh+éHäG"‰üž Õ3‰½!Lqe¬yô-ªœaHÉQi —±h÷‚|{maoÿÊxŒ¨ƒüŒU èõŽcïëËoÊSÛA}ýQ|4õË_*¾Wñ­ŠOUü§~L©øC´-û>Å×)±$õqÔ¿)>Mñ_ú>‹ú*ꛨRüK*¾Eñ%ú1%õJ\Iý„òƶâ ? ØÅöëÛ}jó©×=›®Øqý·¶õmµRû¬ØdÈB¶¿ >µ«Ð Ù7Bm ;ÃX†1kÊp1Œ-9†Y"Ç‹…ìMrNlȰ>ëùw¢ö uÌÁ³9äk™À0)d ÔïŒ6:£®5è[7r¬‰VŽÇŠÿ» í.àÉÞ—á®:†2¬1/m¹ Œ+ÚsÅgnöÃ4=PÖzâ‰ÿ=¡[ÝhÜ º^hÛúå š>øÜå|QÆõýPÆŸùƒ:Q?´P'О…]Aè{ø Ï!(’Í1ÀKô¡;òÂ}ÙûÚëTÆ5EzD1LÓH-ËŽÒ1¬Ò^‰,,û'5ø'M7ø'ýwŠG}¹Ì9î@œÀî6áØÝ™!]ßÃ>a~åpœH”7†¾cÌuwé¶[8öh·ƒ¾µËåØCn/ùíAË$”áÈøÝhßù¦ÐÓ\Ž=zâ9~÷Žß ý츅c¡|'¤;×N g†|³4†5 ãOºqÌ|ŽÝ|‹ñzX”¾¿iKÇ ŠåD:ŽA„ö;#Ý4:£ kŒ‡5øµF}äÛ mƒ>ÚlafÌi[ð`KÓzDHÛÝáøC(ß é> á‹6|QÆi_ðäš~ éš~È÷ äûƒGô1é´€6ÐF èB¦Ô¢~ê¡|xBŸƒ‘Œö‚A?<£!ÈA~è…¢ýÐxŽzaGä–Ïp)(nÅ‹ ˜àÝ‘G~øx†EJ1ÂeLp”(dn¤G,ß1ŽÐßHô%ù‘H÷o=ã¦)Åb—qŽP> mG5r Óx†)JñÁ{¡í^h«—–a…ÊøF¨ƒüä÷6axî½Ó9>8µ ÔîÓ'™úåç}ðßù]Å×*¾Uñ«úþôÿdßSß'êû?}ŸGý›â× ¿ñgÿ;ÿ¥ø.Åo)ñ,Æà_ú"ýxrS}…~¼Jý³*{¥Š½×YÛNí¹¾-WbVjÃõmõó¶™Úbj‡ïò±ÌáXo§tÁh%ÇtsãX+жZŽ·‚òí!öIzxm?Sjo‡VŽÃ‹ñ0‹ålH›£Žy¡À^£¸+VÐ%«¦ÖàÁ4m ›6 c ݲí.ojŸð×4íÑgðåÚŽ_GÐtBY'ÔwBÚ ¼;£=gôÃmwÅç]s9¾™Žaš¹£®;Úu-7Ž_†òÝ@ß ´¼À£/>÷CÚeP7Ÿ‚n h÷ •LCè_´êÆqÊ0&aà·{<Ç"C½Ô‹ÀgãèsB†9FqبúFá³hð¶zN/ŒU Ò½-V˜<— ¸®+ÿÇrYü{ÅÃÿ/cáÿŠ8X?þ¯Œÿˆ}ÿ+âÞÿ 1oëkc+c|–s¬Mè¦!ò ËõpÑ1öFàÃóÉ:dŒ±4Fº-Òm¡3m1~maSÚaδCºÝŽEmvÃÛ¤¦Àù&ÐQ䛀/SÔ7EÚù¦w8®z"ÇÛ4àx›éøE~Çko¼v*ffÄ ¼™–òÌé>Ú2/æX›¨k‘$°¶,Cþº%Ê[‚7+Ø+ôÕ }µBº3Úî í Y[£¾uÇÚ_6ȳAYðbÞl1N¶HÛ"mKýÚ·=;зCÚíuAû]ßÿÛã{ðí€þ: ŒÆÓ¡œ™5GôÁí:‚o'ÐuJàØšWåQÞmt]‘ß|uEÚô\–¦+ÚwÅÿ®(늺nÈsÏ0ÀÜÊ™™tG[î+9~¦/íò@¾ò=Qß}òÌᘙà¥øî¾»7/¤½Ð¶—V'彑ï1óF?}@Ó<ø€†hø"í‹2¾Hû¢M?ÐðM?ÔñCÚ4ü‘ïüÁsÒ¨€6ÐF úRÿ‡òA(„²AI0h£l0h£ýÈ,ù!ÈAÝP䇂ßP¤CQ> ºy„¡lÒÝÝ8Ž'þG^8Ú ×2üNŠÕ²èC†³Ú#‡ávR÷‰>DB×"A«'úÔù=Ó›ŒÛ‰tÆ9 ´¢ÁGtÃO“1;‘î•ÆðÜ(–Z øˆAùäÇ ½ÞƒÞ ¿ezÓùKm*ýQ|­âcé¤ú_ãSªøÎç÷^ÿ.Æ¥>Rß/þ]¬Ký¡â1²|Þ÷éû<ÅÇQßö¯üäøŸöu¦ø²Sߥø-ýý]Èãoý”⟞ßïUü‘>¶"õAÔ÷PŸó³çKý‰~ýw14õÏÇÐçþγ€.Zä3•´ K-Ç8oVh«3ôÎÚ’c#Ï´m¡û¶¨cç†_Ô±Ëg*ÜíuÁ|°Ç_{´i>9€”q?Žhßm:¡¾h;Gq,bðÔô»j± úàš®øëzã 'q|áxŽ-ÜÈq…ц'øðÄ<öÔ1Ü`/Ð÷JdXÁÞÈóÎaa¦Êû Œ/Êúá3?ÐðG_ýÁCÊ ½Ô _A „σ0¶Á¨‚1A:äÇÆçaèkw´ÓéîHG ^>‹Ð1\HЉ±ˆÄXGÂ&õÍžÅÿÁÞ™€GVUû>[  sè¦éÐÝtWÏ¡º›ôîtr*se®Ì•¹2WæÊ\ípišË³UðÆk?D¢×ø¼Jp"âPÂ{v›§ T‡‡ÛèUQ¸¿}öª¤^žß÷’ï«Ô9{­½ÖísV½÷Ù{-É =;·Ðq¾´o"ø‚ç 2íþ‚þ{­þû?RßýõžËþKûîoÕùëȾû5g=õ—ÏWÿ%ýö7ªÏî‘vÂo~¡¼&?­ IŽÚ1>`:ƒß…3ôo=mv:ÎÄÎ䚟IÉùYœŸÅ5>‹vYï2îñeЗÁ¿ g;ùp~6ôhü;šóhî¹èIóSp>w÷Ø9ÐÏ˹`9ú¹Ó&ßíyü†ž‡üótÿyçSÿ|pž¶ ÐwØ.@þ軀úB¿ð¨äÅÅŽ‹Àsò/B~ ü1àáÎПo혀Žý`ØÏù~ðíǾà=ï0€ž†Dê'Ò>‰\—ƒœ„ÿ ºN™G‹=§£ÿô3uñZˆð3sqŸüµúà¯Ô~­¾sd9üÜXÜ?^Üç ÷q#£÷uõïq¸þí]WÔ |þÀçE>äó'>/ñyYö0óO1†RŒ¡c(ÅJñû¯Î–ýS<ã(Å8Jñ,PŒ£ã(u‘¬ƒe¥G)ÆQŠq”b¥G©U².q”♡xf(ÆQŠç†âGI­—9\ÆQŠq”b¥G)ž) ÿWÛ¤¿ˆÿ+ü_áÿ ÿWø¿ÂÿÕÙÿ+ü_áÿ ÿWø¿ÂÿÕ>Ùû…ÿ+ü_áÿ ÿWø¿Âÿ•%kxñ…ÿ+ü_áÿ ÿWø¿Ê’5ø¿Âÿþ¯ð…ÿ+ü_Êû?ü_áÿ ÿWø¿Âÿþ¯*d~ÿWø¿Âÿþ¯ð…ÿ«zé ãÿ ÿWø¿Âÿþ¯ð…ÿÛ{Ëñ…ÿ+ü_áÿ ÿWø¿Âÿí}mø¿Âÿþ¯ð…ÿ+ü_áÿöúdü_áÿ ÿWø¿Âÿþ¯ð{½þ¯ð…ÿ+ü_áÿ ÿWø¿=·Žÿ+ü_áÿ ÿWø¿ÂÿþoÏÑàÿ ÿWø¿Âÿþ¯ð…ÿÛý|ü_áÿ ÿWø¿Âÿþ¯ð{ß<þ¯ð…ÿ+ü_áÿ ÿWø¿½gÿWø¿Âÿþ¯ð…ÿ+ü_¯½Vø¿Âÿþ¯ð…ÿ+ü_áÿz}¡Âÿþ¯ð…ÿ+ü_áÿ ÿ×keþ¯ð…ÿ+ü_áÿ ÿWø¿~·«ð…ÿ+ü_áÿ ÿWø¿Âÿõ{…ÿ+ü_áÿ ÿWø¿Âÿþ¯ç¯þ¯ð…ÿ+ü_áÿ ÿWø¿)ü_áÿ ÿWø¿Âÿþ¯ð݇Pø¿Âÿþ¯ð…ÿ+ü_áÿ:æ€Âÿþ¯–â¼z|‚ð˜Ë!ëÔšuAöžÇ¼?·×©GɸË-ëEOÉøË±6褌¿üf©½2ZÞ·û_a}ЄŒÅb"âLÊ¡8鳓½qò>~TÖ EÉ|àQ³ÒžÜ)1 ŽÉžÈXÙyXÆe§%nGbL-Z¿>-ïSvʾÈQ³fÈŽ_+û#šý‘ö:W—¬𔾣CÆh>§MÉzv‡¬i˜uöºö(™ktÊÚö13n›ßâ‘õD“2†‹–½“>3i“µEq2¦óÉ;˜q³ÎÈßEKÜ·Ä>•±ÞŒÄ@ˆ‘8n÷Ê{šÿŘùM{ßåa³VÁÞ{’µ¹Y›ä“uºã&^‚½^7JÖ*¹dŒxTöeNËúh‰¡à’=š£fܨ×ÔÛó¦12†t˞ͣòÞgʬk²çTcd½½SÖÜL?Û^{?#ï„¢e¬é‘ø c&ƒ½nxTÆœ'eg¬ÌËzdÌyXöwNËÏ(³ÏÓ^ƒï•÷CÇ̺ Ý·×?M›uÆöø3Vb1¸M<½îØž·=&ïŠNÊû¢Ó¿·×>$äY¯ß%cC26$£7ÉÈHW2vXðXèr¢Ë Ý Ý Ý‰Œä¥Ðv)´] mŸ ü)ð§òŠì4t§1ziÔKCn:m—-Z¸2¸FàÏ@N¦þÀ“ ¾Lø2çÌ ‹vq!Ç…r\ð¸Ðç ÉPü9ÈÉž=zôèyèɃž==yèÈom™Žèà(Àžì)À¶ô»)wSî¦ÜM¹›ëéæÚ»ÁU„좂ƒ­˜¶)Fw1ºK_‚üxJBfxR O)úËhÿ2ä—!¿ Z9m_­œúÔ¯€^ö °U€ÍvWr^Éy¼Uà©BO:ªÀRMÕȬF_5íUõª§Yuè¬ÓßȬƒ^½õØå¥M¼Ü‹^ðzÁã…Ö€¾ô5êx±¹Z#X› 5QÞLy3ú›‘Ù ®fêµ@kÖ –Vê´¢«•:m”·a[íç›1êvô¶ƒ¯í´g;|`îk'X;±«sÆ »ºàíFO÷Œ~õpÞæ0÷P×ϱŸc?ýàèCNrúÐ7€¬d ƒ”R6ˆ¼í€'=ýôCÐé¾›îËë¿7Ó:¥¥1úÒ}iŒ¾4F_£/Ñß‚ct¥ý_?³ýQK1ЦÅ(š8‚q²gTÖ4ÆÊZ›qÙËKpÜìëÔïFì÷¤‡e]cHb dOHÞ›8ͺxý¾Ô^ߘ ëã'Í;{/KbMÉ:ÇqOK\A¼7=)q‹œ²ŸgZÞ&HÜ¢1Ùcîø%Gå]L”Ä.òJü¢i³Ït~_ÏŒ¬íI0qNìµ3ÃÈ!ûÍGe¿yLÄúÇ)‰5'±ýæ}^iïS“½>‡e=dHöŸÇÉžŸ€Ä6š‰Xä•u‘Sò®(Fö¢ûåë”Ä"Œ‘=énÙó:jbé½öû¤‰}䑸GÇÌû%½?ÈŽƒ+±<æ“Þ«×éýëvìÂXyWë•øHã²—}NöÅÉ:K¿ì#š01“ìýDѲîÒoÖ]ꘄö>÷Ùs#q”Ü&.Œ½?aÒì5²ßoÅJìC‰cï;71–ì½ Qki§ÄDôÉšÍ1Ù“tRÞÇHŒD¯Ä`7q˜ì=JÇ$NbHâÏ8ä±Wâ$•ýò3²g>ÚĤ±÷&ùäýð˜Y˹/dö&éw½zo¼ý®Íab1%’ýIðº(K:mÖXéwźۭ÷+Ù±=²Gi̼'Ök¯ò±#’±!½ÉÈIFo2¸’±Ã ™.»¹NèNèNè)ÈJ¡ÝRh·TøRôÞLä§"7´}ßiH£^õÒ›ŽÎtxê eè|ØáC|…š†¾ tTS·ûê°Ûű º 9.ä¸Ðé¢Ý²i·êæ 'zôè9Ðs ç¡#zô<ôäqŸäÑæùè)ã¼zíS€¾l* } h7ånÊÝ”»)ws=Ý\{7ø‹]„ìbtƒ­˜¶)Fw1ºKh“t”ÀSO)<¥ð”¢¿Œö)C~òË •SÞ€}åÔ¯€V­¿Á^¾ ðyÀàÁîJÎ+9¯‚¿ ~dÿžöµûöá~|d^÷ßuŸ]÷Ñÿ–}ïWëwGö¹#ûÛ‘}íp?ß´ûØáþµî[s=í~u¸O­ûÓ\O»/­ûѺî?ë¾³î7ë>3×Õî+ë~r¸ü¼é¿îñè&e Þ´Y{g“ø–‡ÍÜ|~ŒŽYiïå3±¤íÒN‰2*{³½²OrLö`OË:æ8³ßÚÞ«0ûõZ,½VKÇŒKEvꘉa¬÷/¦»u6Ü}Fg“Й58†ßs¶~†aw6¿ïÙÔM{:rSÀ”æ\ý̤n:¿«.Ú"»r©[8fâ/r^ˆÜB°"«|• f}u8«©S¦Ÿ+”U"¯L¹èªD^%²«i”jÍG½ZÊ»ÁÛMÙ0¶×ƒ«™ èkàØ‡œVäúh‹Vøû©ß޾;°¥’Ï0ÇÔ• «¹õÈÛðpöóéó0ò†‘; ½ŸÏòšÑ[Îftô"«¬ðtSÖKýV¾;ÀÓMy3²{‘ÛA›uƒ«;{‘ݬøFÀØK]m7#KѸ½èí†6‚}½ÈòÑ#´y/rzµý€c„ëÚ #Ü+~dèçŒ.‡ß¾nÊ»)óQoì#Øí×ߨ6ö° €kL#àA—Ÿót÷Á7 õÀ3Èqùd8?Äù¡c2¶ÔKóÅKóÅKóÅKóÅKóÅKóÅoÕùâ7Ë\±~Þ¢–âŽÎ,Š;:%ñìwJΙc’sÆ!kæ'$ÖSLDLûI‰õ´SòÎ5{6í=5N‰i?!ñGcönÎçžqÉÎ)É?ã0qªí8¤Ó²Ÿ3AâÛOH|•Ùc3.ûâc$Æý1‰E-±ŸËÞøìwK<Ò‰Gš {?G%‹#büIÙkã’˜¤ã²O>JöÊ{Mœ–ù½òÙ:-1ïwJÌûÃ’›fFbBí”}óG;Q{ï|Œä©qK|¨ ‰ƒ-{ |²'gZòÕÄJœ¨€ì/˜–˜ø±/Ê#1£Ž™8¦zo½Ç&Vâ™zMìn{Ÿý”ìµ’½©NÙsXâJMIl©(‰/å’S£²·gFâéÇHÞÄ?fb‚ë|3öþü³¿Õއ}?“&Ö¾‡*Vb£z$>ê˜É³#$9rƒß+ñR™ýA<ö§sœŽ¼tÎS§L>;Fÿ”‰›oÇS5ûeíXý>‰M>aâ“ÛûþÇL¼~{ÏŒÄÊq™«v¼þQRçÕ±ãYŘ«ö~¿äÔ7{oí¸ýQ&¶ª½ï?Zöþ;eÿÀÄÖ±÷˾¢äØqHÌ~¯ìû7¹utL+»[ŽÉOFo22’Ñ› ®dtYèr¢Ë‰.'t't't'¶¥ /…¶K¡íRàO·ð”éÖ§†L×> ÝiØœF½4ê¥év ™î~´ ôf€?üÐ3õ|™´q&í’‰¼,ôgÑ..ä¸ã‚Ç…ø\гi·ää 'zôè9Ðs ç¡'zô<ôäAÏãzæ4CŠè´Oö`Oú h7ånÊÝ”»)wc³\nð!»ÙÅè.[1mSŒîbd—Ð%Ðë(/§žRŽKÑ_Fû—!¿ ùeÐÊ¡•C+§~÷Eô °Wœ4Ã<Ø]yÒ gªà­OzªÀÑÁ§›ª‘YÎjîý®U <5ȪCHþ†^‡>Çõ\ïzìòÒ&^tzÁë—º èkÐãnliÄÞnä4R¯þfÊ}”ùåã<Ýè ÎíÓʱ{Zái¥NåmØÕÍÎvhíèlGf;øÛiËvø:ÁÛ ÎN°ur]:áí‚· ÞnôtsÞÃyç=àíouýû9öƒÝö>dŒ ¯}È@Öçƒà„6HÙ ò´ažôôCÐA?½E=u?]ÿ…ÇèKq‡_Ÿ¸Ãz ¥ÇOzÌ$ã¥ùqRx\9&Òc¡ÈqÿDŽ}ô¸GsÂã›ðØ&<¦Ñc™ðfñÜóÁ¨ÿ{îYKÂc’ðø"<®)^mÞ9rÎ9<6Ðc=t¿?Üçìëë>ýß{?þÍ´îƒß‚¿iìëi‰Åï“ÜNqf?¹wJò+º$g“Ûì×ûÅu,}§ÔŽ‘?&±ÿæ$é˜Ä I¼·Éw¨÷%Û1˜Nš‡z§Î“¢cŒì ™x@:?Žó®ó¥Ø1Ý1=}§‰§©ãùé= :/_&Çéüþ¦Ó)¯=U÷—h“t0$MëÌÀü”Ñv¹Üã™`È…–Bäå‚/[rÁš‰þTÝ·âØE[ærœ –B]<¹§ÌÔ_*ÐUÈ}\HÛ‚¿’¶­¤¬2ÆÄ,BF¥~>‚«Äo¦ûªÀV‰Ž2tW`o´ 0”©žJýŒÕÏ2èIÈ©DFµþ5²«ÌÏn˜ûÁ”†}ôã0vù1LY?´,°äÀßA}eÔïS7eEØÔÏù0ßÃú†þ^ìè§ú±£;û±sGS ÆôŽð=@Y5zªÀTÅy˜«[í1jð×ÀS£ë¸Í4f6Wéç4zêô3zz‘݈/ß^löbƒ`l¥®WóÐF7£¿;gÌ£¦)ÎLu6Cëæ yÒL“¶PÞç­|pÞ:a¦NÛÐßH¿~¦kÛtÛ€½úíèç?ò;ùöóék'mÙ©Û¾lêæÛ¯¿)ës˜G¸Àá×} ðûÁ݇ütèg?²30c¦cÌ|ùìP7=0c™‡ ‚vLƒúwli÷Ò¼üᨥyù¥yù¥yù¥yù·ö¼ü›©?¯Ÿ™G£–r0œ\”ƒaFrð$H¾Ç1Éù¸SòÁOIlÚXÉ æ—XWÑùfGM¼+ݽ±söŒš˜Šv.†8É ,"ç¬Gâ^MKÞÙ¸ˆœ 3ûÊ)9¦$^­SòÃOHìÅXÉË0.yb$fíш8Œ;%ní¸ä ‘\a~‰_;'ñk+$yâ=&‡6$9ÃvJ<¬1‰‡å\´cÇ6Jò4x%–í„ä–¼´>É!6%ñ±b%?­GbÛNJ.±˜Eyäg$O­CbÜ–|ò3’WÌ!±n½Ë}Ìä/²cCFI<-§Ä:jbEÚ¹lç$·Qœä7òI^ÛqÉ??grpÚñàÝ÷˜ä¢?)9Éb%ß­Wr“™xC:G™w2Vâqy$WÙ˜‰¡{MHâÊ;$’×äP²ãSNK\Ýh‰­ ¶¤9Éc?irßêœfv¬ÝhÉš`r‚Ú9%ŽJþÜiÉ-%1xëhÔÄ;ÒyÏtÌK»^ç<³c|q]±’÷Þoòˆê|÷:_¨kâ´ä½O01¾Ò§#òL̘ÜgÖŒÄæ=%9uwJþ3¯É9aÇä•|3’sþL¿‰ýe缕\3&6~’¦SÏ=[’9NW2˜’±ÃB—]Nt9Áå„îÄ®|Ú¹9)ÈL¡íSàOAf ü©ÈLEvßiÈMÃÆ4ê¥!7 ¾thÐ2 eÐvàÏôLýAn&ög¢#“vÉBíâBŽKc…Ç…ø\гi·ääÌp!zôè9Ðóœf؇ž<ôäAÏã^ÉC~8 ÀQ@;`K׸ýØâ¦ÜM¹›r7ånìvƒË þ"d!»ÝÅ`+¦mŠÑ_ŒìÚ£ý%ð”ÀS O)<¥è/£ýË_†ü2håÐÊ¡•S¿‚ëT½‚º`ó€Íà+9¯â¼ Þ*ðT¡§ ¾*°TcS52«OšáN תždÕ¡³NC¯Cg:ë¹ÎõØå¥]¼èô‚×;c†E èk@_#ôFìiÄæFln¤^õš(oÖãtd6#³ÍÐZ µP¯,­ÔiE^+åmÈkö6ÚÏÖvìmGo;øÚ±¡Ùíðu‚¹¬ÈíäÚtÂÛo¼ÝØÙÍyç=œ÷ ¿Ì=ÔõsìçØ?c†e}èëCNú5€¬äR>Hù eƒÈ ÐŽxÐÐA?ýШÉ=¡ûÐöŸî°ë1ºŸ/‹¿Òø{)Ë럇%<þ ¹^Šúóq–_á?öØ*<¯9–Šœ×_<^Òc%=NÒc$=6Š\W®ÇC‘óû\G{üãŒ2ãœð¸&<¦Ñc™ð8FaÂc|ij,žï_2;Ù½yÈÈ×õ±©þ~ÚÁ ½Ãèž3Óžz*t]#ÈѶaÄ%Ӳȭ ^¼m/Ç•W§ ž*tV¡§ }Õà©FO5úªÑQƒÌxjÀW‡ÝuúyuÐëBfJ·^tzi?/÷‚—ëà…ÖÎô5êv7bs#´F°5Ak¢¼™òfô7#³\ÍÔkÖ­,­ÔiEW+uÚ(o£ÝÛt{ÕǵnGo;øÚ‘ÑÎ5n‡¯Ì`ík'vuÂÛ o¼Ýèéæ¼›óÎ{ÀÜæêú9ösì£}ÈéCNú5€¬ä‚còAÊ‘ Ø€€~ú!è‡ü&÷Ý«å¿Ò¿ïöß›é}Ä_ó]Äßâ=ÄRþ«¥üWKïÞØwKï^ÿ÷ ú~vE-åm -ÊÛÆoG“³!¬s&oZ¼×ä#ÕùŒvƒ/! ùƒ£M‡½‡%oŠÁ–crë\G ÐJ‘™=É)yÖÐæ$°ì§­‘WÊ'ßeò'RÇ⻞ÒÃ&’Ž“ŸˆÌ$lKO’®- û’¸ÞI\ ̶X´…, ›,t[ð:i/'سFM7¨¥ÈÌEOõSáO…¿9©úÚ€'Ì©ÇLW) Þ"ìJ×ꤣ+ƒöÉ@f6dÒ™”g¢?ýYÈɢͳ¸¿²Ð¾pdƒ9¹ÙàÈFv6r³±+›¶Ê¦}s¹Wsi'{){s©›KÝ|påëv¢~>uó©›OÝ|ê6qÞO!õ ©_ˆ-E`)¤NeE”!«YEÈhÒö §Ì%Øâo |%`/·ÞR°—‚©ü¥ØZzÚtûʰµåÐʱ¿–úƒ|7`›<ìöP×C=å-c¦{X žZä×bW-øjÁV –ZpÔ‚¿Lµà¨EF-2jÑQ‹œZôÔÃSO=<õÐë¡ÕCk ¼OÓ^ Р7@oÐm‚¬&Ž›ô±Í™.h/x[(o¡^/˜ZÀÙ‚mCàoŶ6ÊÛ(k;eº¨>Ê|´“ÞÚ¨ƒöé }:°¡†lèGºÛ…¾.ôu¡¯ë”éÒöbóe½Ôé¥N/uz©ÓK~î©~0õ#»ÙýÈîç:õs=‡À1tÊty‡á†o¾aø†á ðm÷Åô_xlÓè‡ÁkÅãXÊ7øÖË7¨ûÐá¾óâ5ắÌ=ógýãp¿xñܱîû®“þîó.îç.ŽIòZkÃ#c“è~ë«Å&‰œCŽ\3Ù}¥¾çâþæ+­!÷)Ç.ÑýÇOI¿N…Ç¢þ.ó5jw´s6r/ÄCXŽü£&Ÿø%G%ªËäÔùouN¥Ki¯K‘·—z«± œ±ÉQ£Ÿ¹Ø‘ˆÞË(Ot™ZûÑ»ûÁ›Œ®µÈO†¾›:ÉàtN˜üäë°% ©è[?a¦?`Hå ºiüN&!? Þ ên„wçúÙ _vïÖÇ^“?' ~óó±W#?½y§ÌOI>çàË@®“O.3SùÈͦN16O™é 'zó‘é¤N>ú ÁYŽ®rìñÐÅàIC~9¶–Ó~¥´eÚ)ósUó¦œO‰~‚½œ5´K ²Ëõ3ˆúô5!σ¼¾°¡E(o¡¬ö÷ »F?oݤŸËú9Æwm]žý¡n íÜ¥ëêç<åú9_<|úÀàá3ˆÝ5`ìÓÏR°õéç)rúô3}ƒÈ©§Þ vµ §…ºCð Á3½üC\ŸêÑv}È¢íÚ¨?D Ñ^CúYƵéÑÏL¯ùÙí N/rºÀÐEÝ^- ]CØÚKý!C+ø†´|° i¹Z§Æ=gò¼ffÿ½Ö|Ý?Ò\Ýë½vø/«{«®Žœ«{£ÖOE½9ò]þ¥st«ù¹×snîïm-p¸ù>}ñ|Ü1÷÷2ç‰ZÊÅ:‘‹uLòySg92–#sù„yD®@Æ t®˜1ù«/á:\’<ßÐvC[Iý•3’ó›{(Á!9¿±o•>Gæ~êÅb_,ç±ã’ÿ;:"7+ú·Br³ŽšŽ n“³s º×L›\Žk‘»Œk±c-¸/wHŽpê\Ž­ëh»u ’ïq2"G+²Ösž vºès ×Ar7Ä™|¢´ S’«YÁ¿}'Ìc²7q¾iBòµÂ³ž,pnž39Ƴѷ…vÙB»lA¶½YØžvRò¶"+¸¶Á· \Û· y»)ÏâˆÛ)¹(±)\ñœÇ'˜<’ñðÇ£+y»gLÞÔݺ¯7Ÿ=è߃ܽÔÝ †½´÷^dïEv¼ÔɘÜâ £&׸ΗZ)”§ 3þlH¥N*uRiTÚ'U÷µáO×õÁž‹MÔK§}štÿÙkºB™ÈÈ„–‰îL}]tÛsïd¡3 pfaS²r©[¤¯6ecS6˜³iŸz=– ½‹SJy.væ¢+ŸòRäåƒ'<ùÔˇžO½|êê±õš(/äZbC!¸ ©W¤åQV„¬"dé2ä!§„z¥È(6ݯÝׇ¯|Êt­‘] æ.ø±£LK(+§Êõxä´éÊUÐ6Ð*ô8Yx=Ð{´ÝðTB¯¥]ji—Z°Õ‚­µÈ­G-¸ÛøÔsÞE;µa[=åõºLk<È[e=ºßŽ- Р5èq z›8n⸠|Mð4¡»Ý-”·@oW‹oœ2]ÉV0·QÖFY¼>=ö@¾ö÷·ƒ¶ë M:ÀÛÞoÂt+»Ù…®.tuéñÇÝèêÅÆ^lì¥N¯sP§—û³Z?6ôÓæýÈíGn?öôcG?÷ñ“ g¾aø†á†o¾aøºŸ­û±úOw^Ç.5Ï¥½ÖÜÙ+ͽ֜—îw‡ûÜáþöây­ÅsUṩȾíâ9*ÝÕý×È>ëâwɯ67Žo9Å5yÍ<ÁÒü³y¦Wš_ ÷É"çŽÂk #ç€Â}žÈ¾M¸O³x~'rÇõ×ËYŒ-+àßÍ=w ÷Ü%s&—u<| ú9Ãý´jÒ䪎åÞLDÇeøB"Ù”gsžîl0dß 6ØóÀ›¯Ç‘YÀµØèç„þý{!8‹Á[ŒÝÅØS ½{,ꔂ«˜:¥ØîAV9¿¥àͦê”òÉ‚VæzôfAk⻉ó|¾[à+Ćä¶i¼MÚo¡uÁÓƒ®ty°©=5§ÍÐpïð´QÞ¾ýÛ }ýØÖG½^ʺ9¤ý•ò^Ú¤ï´É‡¬€ÏäŸÒ1KìýßQ²>+Ö¬ÏÒ1Sõ>p½w_?u\’ÿß¿¤KœÏýè°>²¾²êçwÄljN~y(&¿ìòvk¶ô+7xö¿YgžþêÑŸøž·¾yámëo|4ù‰š]•Éõw-–¼#ðÒÔMŸo¹¬<›ù|yðÈûÏÿØÇvWZ{]ÎÙ¾OFϘ­ç«Ñþ‹Ï]q]òŠÜÙò禽åGæõžxù2wüºÉÏzðŽJZ{OÔOƒG 7¾ëmï]‡œ)#çg³ïÛfÍV<ëúž+­O¦ýò–[w%?ýÔ#þÞç­à·<4÷bÔ·‚Gz>rsáS‰Ô Ùõ¾6þ‘¦ºÁíÖlÙ¶Å5þnÞ¾ßxü'÷œ¼ãû‡ß¹µésÛw–]Û<òÇ­åŸÒé|îqÛNë.Ï’Ég&­Ù¢{>ôå{Ÿuâ¶òóßyQFpâ‚5}Cïß¼að›ÿôÂãð;lþɶ k¶`ëC‡×Y'Ž\Øøß‹ó‚W'dõn¯ ÞPuã»?ôÄ1ø†?ôàc?̸ٚÍ;ëƒ1 5Ö‰žkrdõéäŸ^üáwþaë§‚I·Þ’ºöL+^5ÿó…«V<:ûG·ËZoÚ9^[Î×?ŸÛßeo·fs¾¶ì®ÿ±Õº(ܾ ¡ûÆWÖηï„ÐwzðÈÀÃw)öÒ…ëö¸¹?înèzjk—5›9pý£ÿÖ:QUÿÀ³_~™ú¦œ×t^ðÈ £©'ïý4üæ:ñëÄ[ÖZ³){J‡n_m»÷韯M»Ú:Qöô{?_œH¼==)óKÖ¶|oÿË\<ò­ƒ{/Þ}õÍõýÆósÏFŸ½ÇšM<à»óÖ”ü…ïÞ:SÐ8'®ÈþLËuwY›=Ô7×ù›÷?ûØ·óµf÷í=þëÖ»­™UÍÏ'6t5oï ùPÎÿ|¹;ÞùÜÍuýÖ·Ýô!ǬÙÝ|²á™ß['öæþ¢í¶’C÷ÿ~ì«¿¼98÷ɧ¾žðÑà‘£'ü^¼Žzæú~{ï¿Dg®ÿD¸=­Ù]‰O_S;»`¯ãƒ™Auí^±{µ¡Gà0×ÿÛ¿°qÁ¯z¡¶à_&¬å•ôþ`ˆþ·|ñ‰Émè‹y¦48Q=¥EY;óðüçþœøÌÖ[nyéGÁ#Ík®}_÷»Ï=a®÷½‰Ç÷Œ{Íÿ…^úè;¸å掃î7¡} ×ê g<ö¹RëÀ“öHðÈíg•l]u¯µ1ÜÞO˜ûá^óC8ýB/Ä×nþx†uü—ûKžüt„=?\ö©;ýnkï©e7OÞý@ðÈïæô¾/gî‡ï\zNùÔ#¿µB¿þ̧ÛÝ=/÷ø‹gþêÙ N<ùîÈDkÏØ;ölÿç„à 7ýàÜ?ìXðß'Ì}ð·¼ý®ò4+ôóÇÔû‡-ÜŸgÿê¼s‹¸¯»iÜŠ—ß½cF2¾ƒúæ~øÎ÷ žsËç­ÐÓlºùö{üqù *:é› ÷ÓS¶"k×]y]è}Á#_¸þ Ͻg9æ¾ø®ã»ýÌOJçÛ9ôøWè_­—ÕLWüfYpâÇï{ðÖŸ¯?õ¼Õ⦅zæþøîàgûôŸ¶B­JûYÓ­«Ï~âÞÏ'ž¾gûÉÕO¯¿yË/gŠ?¿¹/¾ûpÑ#7µïX¸ßkz>ÅQ³àG± ë>Z»p}³)ðùóßþ ^ÿ‘O¹>ü݈ßÕ'Í}sßä©;ŽÍ·_èþw½tíX«uââåÃ/žã˜o‡;£ž½ã{³÷[W=üÂm ¹áûÝZkΑgî—û>9üÄ¿ÿjÖ OeŒ?Ÿ÷>ÿóÜÿ»÷»ÿ…ç çÌ™™5kÖš5kM[ëì1›þsæh7þ`g—=bÍS§ä8qF$^™s»—^óˆ˜ÈôÊK“ÇT΋1 æG¯;æ´íŽÓ’ÿÎGc{®/(Õkî‘Zò›M—«-†,~q¼Mï©}ºl/C–ôuÍË6½OKþ;/wŒZkÏC+^ÝV¿q¨=ýª Œì~ º|±º•mŸH} <9J™•öìzE¯ÉXqöň7lú\Ù;û¹;+ŒÂ>ŸÝ=í½·Ýú%ù^úöãüR[¯™îžKÜ2æŒQxK†y·õ»ïGŽÀN9-ù\z}mÄî²·ôš±éÏm~ð߉õÆ6cûòñ/~\êšµá õ‘S_ò½làWÃ7è¯×$Ï9v¢ð6½rÿí~v4²Çû”^ÚËX;{œøF=9Ê6Vß³£[[®ãL…¤W¾Õü`ÚÍÒ³…«ö½øgÚ·6ýG§½1}ï5àH¾—]úuþÛ^½&äUa!è•O¶8Öÿ{»ý×G–ô5ÖˆYdÁ=‰çk$ÿË·?s똧õš–ó??|¡±^¹vÇøÚkž´ùþÑ“þá»Ør?¿àäÚ?™ÿj$¿ËŸÎV´F¯©•™»fŸ=~²Þɨ³ôs£ðKæ×¶= Gw¾÷òÁ¯?7Ö<.×êKþ–›b7P?}jà [?~L¯ÌÞä=ŸoíññqÂÉOj†k1gþ’z’¿­×Ø£ÕOoŸöЮ¦è£•kòz¦Q¸?¶lìkël;¹Fò·BXIú(ý´œWíqtϧ¹Ë¢Þ°û»ïÁ:o¦Í5Ö®hv®yÊEêKþV¼>»õ²ÊúiÑ\ÿWí~>úRæãQO…G„á?V¸Á­o¬ØéÖ¾äsÅ·[Îö¼K?Ýý˜)zåÆfSOl8oóçÓã—×ý±]wÙQÊnµôgäs¥cNDË:×õÓ ÅO{œ¾àІ?8Î(ÃéÖìq*lj-§_H¾W.ôH?þà[ú©Ó‡»¤|þ•MM»Gx×ó5 å8qé cÍÑa ¾ä{eQöO‘!¯ê§Šï°Õ(jtîhã[mýö…G<_óúáì`«?'›ÿã¡—òôÊÛšyº~ʇE~™”ùïÒã]ãAé75OOއ#¦ºò¶èqòð„~I¿¼mÏn~…ý"Ë"9ßé·¸ì}Öåã§µí3r\y¬ïš¤Fé'ó×ÎÉÿŽy,d߆ô.=ŠXeþ’÷‚=ßž‘ãáÈaØçê'ï3 ½2°â³iuï3ŠýW¯ªG9Éÿ#6¬›U⩟¼ã3åI{ü^˜5bÀO‹&vÿ;-õÈÏCmü‡FDüùn…ÞWÍGÿ$gä8©ŠîúãOOÖÖOúm[rþt½2øŽ[Z^ëoõÉxéSŸ>¶pFŽƒ*V>²øpâGŸ³=RÚÛýëQÔ}±Í_IG{Sd²7xrœT¸ÕX¼¨§5.O¨õŸ5N¥muøXkýbÍ;gäø¨š‘X‘õíUýÄ {ß60W¯ôˆîúëe£(ìÐÌÆZ±úé>:ñü—’ÿU÷Nýõb›ûõ™Îw{¥úꢹÈWlúÅ}.8jë‘/%ÿ«žÿ©eôÅ·­þžeÜ=*öä¿5±ÙŸ6{ôî¼ð&úmj½`ÙÇ_ÊqRUx¡S÷Ù ,>ž`Xrý- ~Å–ËE/o`% ýæü”ûô¸oÆ>þHƒŒ5W´«ömŽ{ƒ³Ÿ8R?ám*2½"gÃûÕkEb>å6cÍsAHyÅw1k.ùD?~vpò‡o§Ûø/pia»FÑÏŒûûK¯lêñ2õÕ80—=ÙŸŽ+;¸"}ÈÅ%ÏÒîkÏî8á½ÆÚŸX“üç‹.\M}Å÷ßÃß[ò´~|c›©Ó“Ð[æþ†Q´á’˜1­ñ¢Ö©Ô“|>Ú  ²P?¾hðsïÝû^1ù±û±Ò(ZtjöÖ~ûíyè+Éç£ÞaæqZóÐñã»`âZóbÅÄÛFü|îÙ¾ßÝ:rFÈO£hü…;:¬÷¶äÆâÿW’ÿG»Eßöü ‹^ÇŨY™eókümÏ/éwÁG·›g6]|øJòÿhRÜÝ>Ù¬~¡æçò¾cõŠ1k¾ÿè&F‘XÆßyÒž‡¿’|>:äû[N¶Ò?ß—Yÿ—OsôŠáá5«Ç¯ |“ ºíµõË~ùJòûè˜VM’ÿ´íîÏžþéÅúZéŠ~oíu«¿ÝnôÁçF!__I~Uöþçã÷/z7«Ÿ^áÈ,IÙ•c1Jfç 2²«f=ä1 »å+Éߣs}¶Y‹Iúçs¾ùxæ½ÂÑוaÍìy!õjS¦j#[¨ÇßQOñWlWÍ}EÿÜãµo hcѹü÷§ž«nËÕÄ>²üì³i·<Ðsˆ=_žU|—ûyúgbùûÆ5½üÄJcûî"i—kF­@C3¯U|Í~¹[«/->~ö²ßåÉõ‡Yéò]÷ܳõEÿ•æÆšê’GÉW{~=«ø;ߛ׫Ÿ- ;|v¿6› íû‘š×Šî÷Àé&¶*õðÿç6aEêmñí31í]y]/_?ÿ­7øEëVuf~²eŸYëë³jˆUáð!úgÍæ$6Þ‘£—¯¸/¤MåN£è^a 7ÖˆÕQ놔W|Ÿ&üH½úìúØË›éåóËê]Y°®o•‹Òΰåû¬âÚ·¬$ÎèÕïôùÇÃW_ÑËg$<óVjÁ” ª³¶á§gÿg mÑ59Ò¢{uöÀ™wNi«—OxfüÛGÚô_VYÿ½fÙv[…o£9N<ÿµâ¿\êÕCÿøÒ¯áÛzùÐmOfmyÝ–Ësv}ùû.—¾¥ž«’ÊwFÙçümÖ¾r­züþºµŽQvª½þþZñWí³¸øôiâ°—ó÷Ü®—-ë|›Ï«Cìþ.½+{ò”.Öx´æ‰¯¿×{×:åqLÿ´™èÀp½lDé՘̯]rxþÅOs{´þÉ—;?_ŽÅŸ²žš÷ÿ’=.6o;TùÀƒ–\føNììÚöß7ŠÏKLéŸ Áomõ£,¸Ó˜«“ž1Š œë|ÝÒ_ÿD‡oßÓÛWÚÜHÿd•91êe­J?Þ¼î»ÿ[&=¼¢êU#»&¨yð_‹©§ø<Þd¨þÉ€ïÿzöÎKzYãòáí'!#…EL9Å_S=O¶ÆÕ'~A¯þü‹ÃJ—Õë¾zV£H›÷‚ùÏYûEÙï9^÷ë‰ðâ?‹[Þ ³æ¿¼ëÛ^çmû´ôÏ«çj?pÍÆ?Áy*|_«ÖYÙë4ßÍ=/ÛóÜ7jãS½¹ÅL£hÈ=3GÌ/4îÇ‹NQ^“ýö5tÌÑ?üæ©Uo¿–¬;ï+¼/ùõ]FQ¯µZå”gŒ»Q†}Ú%ž}£¦õ²úý¡Ñþ‘´ì%6ß›8O³éªÖÁÖ<æ/¾4´åî[5~D·/Þ«hª‡AºsrÖ„ ³¢wLî®[çtÖ¸ûV—WR„YtûpáèÁ…ÿˆ·éxÛ¯ÛŸøâe{¿#Ìç{^‘ó*ðÔxÉ­}a‹÷›vÿ-oûtãUvÿB#üüá{ÿBÒÍÈþM‚Û€£ÆËÒ°çsz¿dãÕyß©oÖÜ«;µá¦>ÒÓÞ¯VÌ‚6_?ÿòëýžŽ'#Z®7%C?öKëÒ|ñ¹~è£yUI…__x¿VZ”‘-ÔÐÖçÿã KI?¶×TÐú!±ÝõÄ#vÿXµpWHãn©¯¨§ÆAdÓ­k¶~§Ëëî÷àÚlýÐ’À)þ‹ÂŸLCÍXmà §¼íWÌ*;¿Ô¢Ë1S÷{™¯QxuÿS‹O«å:z’ïGþšøØÑa ÖzèX§úYµo³×3‡êšêÖ¼ãÚê+´\­@ãnUβ·ÏKþ9ýèGb¼¥—Õ)XÛ¾‡pðÇÙ ƒÃìñ-×ãÖ9ÚÝNqÓÐÖÓçåx8"Ž!ýÊô£mÝ;ésiŸÇ³F!“Îò ÃŒ»_ÔŠg~Å:EnôêGÌmáYúÑmõNöo¦,õlκŽFá§báö‚qw‹Â¶óÛcW|'ù~DÞSÐæˆÞ7:òê³_Øüoá†îÇYóåêé ý¦>ægïS}'ÇÑÑ/>qߤ-V¿šËMk}PÊQ(÷‡­ñ»ÚWLè‹lùþNŽ—#J_í–Zð—ôƒÔ‰¼ƒq¼²ÓCú¯2VËú”—ãâHóíåéɆ~Tì&õÐ.j·âáõyFáb1Pº[vØêþ¿y6ï}•zr\þù—˜½3æêUæ6ÈJë¼í yüXÛ(Ì2/>Xó‰Ý®äÿáŠ+»{žzB¯:4}ëõµ‘úÁoGù¥”bVÜùçcuÝÆ_­xl'å%_¿üÇËá?4׫¤}¤øë÷ËÚþ?ŒÂÇvÞ~ÿ‘nð%_Ëu›5¾ªîzcxY¿Xt>ðñ之Â<óbƒµ.°äæ{ÉïÃr?ȪW5ñƒ½Û‡>aé“o=£wúÌZ*;×ÒCßK>Vã»*îþqƒ.ÓþºäÉ}¶üj>1a^Ï4øëëg>ŠLx‘ò’•êÜ¥ªmjç µ'ZãìÀ¬ˆœþuKèwóG?û³§µ.º«¹0tï§¾äkåO»/?÷ËB½ í²q߃v¿‡tÝÖ+v„µ+|hu("mÙ­«æ·# 8’Ï•Ç3ßùàž#ú%oÄé³×}v…úÚ¸ßXU§ŸXROò·’IôÎú½ô#9Û—ß·øYý@‡ŒSλh·+×ÏÆò¼™z’Ï•3CÄŠÊâÛ‘®æ?ý€÷«‹~t¼bË×íbCç>«ÿw~U¹ôñÍ#ù_©Ö‡O¾%vJ,~íÿ©VÞ3l½ÒV\»» kœ¯j´KæëÄó?HþW˜Ç“ö9ÔaÓ\ Ò÷X=ÎYköºµå[û/ÚtûAò»bÝ蟗OXcõã0£ À‚³¿ðʺÂ^¥Ö}ŸBÇî†?„>líc­Ú#–ÏOއŠx±Aæcãá†Æ8d÷KìŠÏ ±çሻkÖÿøŽk¾Žåß¾%1U¯ÜiÜèû'·õó^µÆ( ËyôþÖSUr—ò’ÿåæu o½2K\ )Ö÷‹kJW?´Ïß"ÅÁÚaãNq»ÄëÔ“ü/ïûf•Ó+ÅuŸ:ç­ñ·¿aÂõéG¶é/®MôÿͺG°²ÁOÙ>«&Ø÷-~ã¢ì£j îzÜÞç­# ÌVzß…1ëSRlû¹PÝ'rjÝkÍ—?ÈqR6³é=¹ý>Ó+މzw}ßÇ¿ÌYélgëSuŽº²–¹PM<AŽ‹R¥ç*òç/úyÚVÿöíK’¼õG£PWºî¬”÷I¨/ÇGé/mõ?6Eo)çG½"Ó¼øaÍûûÞíU}žÕ+Ô9ë¾×–ý}ú£Pîgwš×yºS^ŽƒRóÚKC½BÙ‰ûä¹¼Q(÷K•r…òr8Õº¸¹kKíWì{dâ°m ·Û÷žÄ®ä¨—-yZ±Qü{8r\8×¶8èôš^®ö+ö­0¾ìû2bÕ6­±¥7W˜Û½¨/ÇSì/¢—?f*D}ߤ~ªYûQpðñ+çÂY÷vVÔ­Ør|êÔ“|v6èÀ ¶ÂÞG›úçoßí·ùf.K²ôµ–Å¢aäꑉç”ü>tèêÊ+‰Óõò~I¿l}ê5}_Ë•^¯ærÜYrP^ò÷Ðê\±2ÔËÕ=±½îݺbÐB£àL«î3²–\«]Ø ÊKþ lèåAbÁ¹@ß»wíÁûÄ[󬢫‘uèŒØA£žäãA©?­y¦¼Îõ[oyÁa¥÷>Õé™f íìqäÕ ¿¤Qãh…´ \ô®ä÷A¹ßeï|µ9è·O-zíUç–Pûìßåy±-o?Jþô27 ô²ªÇîš;ã }oòÖ tkozÞÕrí‡÷+°^Bß«¦¼ä÷¥ŸËv?¸göÞÞúÞˆi÷Ž{j©=^®n¯Iû+ÙÈò3/FQOòû€<_ÕËžOè{›{f¬ØÆ¾¿$×IÖ¾érq\ŸýSâù‹’ßû·u÷x¼âª^6«ûZ}ÏõáŸ|zn½QðÑŸ?¿—´…r’Ïû…™µå¢^¦æ¡=çÛ¦?òe›oâøæÀc¹8}š>žz’ßûÕý·2I‹_{ªæîÚàkßWÚ¹{ê‘¿ ì{%ß÷í{÷J‹Å±öz]Þc³Ò{ Ÿj¼yÒ;6„zyèêKþîS÷OJ÷õ ü8ᔾçá:S¾hbˆk[£®YóæryÎH=É¿}jœ–JCMß3­:aÞÓ¶ýSp(~ç3¬y*k¶¸@J}ÉϽÕ{_}ên½TGoü‡¾§›¾ekßÇñóêW-~då\›újÃ$êI~î}´ ~ù×íôÒª£ï§¥}£Œ‚]Â`¨°ùÿ“äß^µ_QªÎ?JN>˜öSÑ£À˜„&ko,ïen´S^òqÐMNX|(í² ¾‡5þK>Ø1yäsFÁóÂ’µ¿•¹8V¬ø#ùºçªU÷n;iïKµŠ6Ó’›’M ƒ½¿}Ä(Ûð3ÛÏœRý6p$÷Ìñz~§ôRV‰woÞ¬—<°zãM1 ä~¨‘9Ò¼HKyÉÏ=æñq_½T~Úx/»°ñÕè‚ÒØ†“Þ72å¾<õ$?K„ج·×ñ¿ÿ1#±b…¥‡Jîˆ{wÕ’íqT|Ä88º¡±œI–•7p$_K&ø^ÿl•îÛãŸÑK†ÏÈû¢Á£@΋Æòa3voÚÑò’Ÿ%‚=o<ª;…ZÿüW½¤×vožYdä}[ñþGF&«ýÕuÏ_’ü46œ|úJÄ\ÝYѹ^À}EÖ8/ KZòZž-/j¿ÑeÏ.›jʶ=qIòÛÛc>±úéTûDV¿kÝ÷áÏÚýÎѬq½ôåcEÆñ¡öüvIò÷ûâÂÏZÝyè‘çløÙÂÓØ+&Ô?ìù'Ã$¤µ¯¿”Eàâûc€#ù¿[oÞÔsg-«¾Ó<ž²ï ‹ß¿¡_ºQ°B(ï»ö™¥Ï˜lÀ‘ãb—’S§IÖ–ºÑ¶tú´ìP£À%_—ä8ØenÃ;t§ê×î/~ñÈFÁSÍ>lóì,c)Dìqõ+ÊK~ïÜ*(åºóéÆw¾P×Wß-®‹_αéµÅ°à“å+­yÑùAëŸ7ùÞ¦·wÕãûͳߨ}X×½ƒer^µåïg9ŠÕýx>;+?K¸­:²o©‹o7Œk…—-o—åxxWHŸ oÙãIíÇí”v¦µÞ,¸§é‚§N12Åpžù+õ%ßßžûòç‹vÿŽ™N¬þí ;r2è¾™FÁú‹Þ8ó´‘ù˜¸`°ú’ßïÍ2né×íñøYb"Ã8}ò·üüp£ÀîuŒÌW75÷vÍeÉÿm¦ ®;å9þÁÊ}ß­ú ‹¶>¸,ùÿ_Þõm¼îüÊWœÐèˆUÙ˜=6½žýUŒcÙq¸ õ$ÿß«¹._éNqMùÀZ«Ÿ;~ûæñA [o¿ÖNì@¸æKêKþý¹ìLy™ö<$NGûγú»ã£…©w-|Ú¦óÛâ¢ÆRkݳL\šµx’ïêþ—îT÷ëvì¨éÝ÷™ äÝœŸŒLs[zZây’Q/Ä8u|XϦïÙw;Mÿ™¾#ÙÇ cwâØ2é ë^|æsß2ª#õ%]ïœê¼c‡XmL¾dÓíu¿ž/¾¾ÚÚÿ\.ígêKþšT»½@wŠÕ¼£….´Ö#ïÞáza,—÷¨(/ùú¶¼ï¤;¯WoÐòvúŽ%Ã×þÒÔmüˆíD߇(/ùúÖcâ¡mg”²úD6ŸF¿tðÖ¤*›¾J.”ØôýEòû*óá^ê+6²Ïè;žìdûÖ(v˜±TŽOÊKþæ›Ï¹–ê¥-Ž7¬ŽùÔÂcû¥ï/Õí×Ë(˜ñÄ?†7¼×uþa,Û…»±›~‘ü|­Þ ã.íxÞ¶›Ú‰¨ úöwO|÷מ{| 3`Z…=?]‘ü}ùêŒ?׾öcCÅèÛôíKî÷Þxb›[¿Ï¾ñú1ë=Í2ÉoàH>oQï*JÕ~Âö.ë¦æ6·Þ½,ÏÄväëæÎŽ^j.Ï&éÅÞ~᫉=Œ±=w±Ú5¿S^òõÅñŸÍ™£ë¥âàŸezñÖ7_¼ýYäuƒ¸8ò›E˾º"ùûœÜDZöGK…š¹Çš¿‹?òøÇãã‘?q}%Ùº³\¼~HžkŸ;]‘ü}F–¦ú¢‰.éŦùÜ»ô4׃.ýFyÉß§Wšz©º×êZ¿·ðì\ë‚ÓæÏɵBÒ­û±™æ5º${ÝqEòûIe'”šÅçYýzÿ'ñ'Ûæ×Eqa®­e/)¹¶õîUÉÿ CÍ‹=J1zgm»¤¿_zýñKï͵ñûþIãbR–±œEbæ¥W©/ù.^ °”±ékn×Ü¥¿¿¡¢ê¯õÛŒ‚Jç²á=3íõüUÉÿ\yÎmŸãË÷Fï~b”×¹¶½öЏ@zÅ^ß&½·/³þ›?Wå8yÈ|ÖÒÙ‚S&÷¡õ÷.TçŸÿ‡ÝykÙjÿÀÖoWåøY'×z™x­6ã/ ß÷V;Ö­KžlÛ9ò<Ñâ_VøF±£9n\÷ÊÊjï/xa£þ^­ÏŽ4;|;0EhkÜe‰c©»Yß^•ãG­Ã,ú–]ŸñÈ)ûýÊ6Œ6­v¾´w¨'ÇÉ\¯gÄŽ™^.÷õm­Ö—ýýoFÁóG¬}}ƒ½®¼&ÇÁ`óÚŒ¿µoøî·|ÛöQ›^ò|ÚX.ïÅSÏä¿1Òs§öÒ‚ Ÿòo=óW»|‹Nïf”>õþ,Û®”!íõ©²‹Õ¹)pÍña¤ËûdV¿Ëg›_ßí:uÌĨо%.¸ê<ÛÚo¼zÅ%›Ÿ×Ìña,1Í®zùº]Gj×»¤¿[ËÿÔå»ÝôÑÎ?_Üh¬ü¶Ó¥ª0ê™ãÀµÿhµ_¾íùíW¤ëï|–y=þT‡ìŸ½ßwÍ俱¦Cƒn{òß²éôåÑ ßFs­þ½cn' 5 Ôù°Kßy§ù@8æx0î—v‰…G…¿˜ïÖß‘ãÔŒ;Eo'u¦¾9.Œ˞‚:jß#JúÁ ú;“ÞøD»¾ÍgîpoÇÆGÖ$žÿÕ'Fî¤Æ¯<ºÈ¶÷*²ÍÅÂ玵ãt|Óž‡ÔzÐ5¾WÑ„­›1ŸoÆ|ÎöøïwáfÌgù/]Ñæœò£•êaû³PþìsÝüÙ#'µsTl'dºNŠ›?-ÚðD<á¥'øÖ¥~Ýt÷µJÅ|Êró•JY¯,êšògŸ¥|rЖCùÒÊWq_ɯŸî÷•ö¤(ªàÓö¦ñGû ÑÈ_Å}%Ý蜊û ¼Æy*îk¢ò±Z¢b¾’ß„tÒM½TÌWðiZ¬|Ú¿Y†[ÌWðiž®|ƒTÙþ´üèù-ÊkžôÅÚ’ü–àÛúµ¬’Sž?eüiØþç”/-èÙªXù´w(öä·§6)Ê鶤Û¯¶´×–öÚÑv;`µËW¾ìÕž²í«ÜüØgKŸ­¦/{‡òE;Ð2z檘®¤ƒ(D~P±Šgåpó_OºéNô¿Ó&å3Ë¡â¸Ëi9Ø¡|Õ«¸­À ÉQþéÁ=4NÅk¥/¡àF:ŒòaÐ+ œÃ¡o8ùá*+鈇թ⯂sd¶Š»zNÅ\…æQù2Îj´¯Š¯JhÊÄx©xªäÇ@3‡¿Œ¡ê†<ºî’¦â¤R¾ ebéG,éØ*é×ôÝEºk±Uøˆ>m…ý£;´ì-{øJ¿1¦//pêé%cjõd,õ,‘qQ…5ÓmôòRñP ¤ýxàÅC—ø<UÄA¾ô…ßüÞä'P7t‚˜£nê꛺:Û㦮þ嶺…Ìf¨¾“oú,Mó°}•‡Jåfì™jå÷2QÅh¯Q1ÚS¥¬:Чó†'ãÜsÓ 1ÚóUŒvÇ ~/)ë•-cÏX~/I{C+Úò‰S~´ dìÆúä×Ïp‹=Cû R•ïKðiH{ ÓUŒö*£òH7º¨âÏd©í¤}“•ßK§ŠùH~ÒMœ*>{¢òyY¢ü£‡ªøìNÆ¡b³oRñghÏ>ùÑ?ò[ÄI¿—-ÈoA~Ë—=WÅe§þ”ñÏVqÙÁ©m¶‚ž­J¤õÖqÊç¥SÅd§~›\éó²­¯[s×_îþÁ]~…>ú;ÝãîPèW|e¡_„Nqé¡+Üõ„Ë7t0u€kÞó»k.s¸˜·Åœíš§oô‰íŠ‘!æ\×\ _þiNs©ký»ùÓWöÌǕ'Í!ÏTo«FÆpðò—ñ¼sd ­†Õ*æ,x7º¦â`Ñw_Æs>›ðÙ”²Í*æ,0ü€éW M¥5*~íùçH߀­Km¨×Ö_Æ“q¨„édÆŸ·öy*¶l‰Œ© d|“Š¡@ºSŠ •%ãBóB™Pê†!ÇaÔ p÷(øœÄÐ^ m8íÇ#¶FÆÓê ŽÝ¥ßÚî›d|+ás¶g¶ô+G¹^Ôï%Æ5åâkdìÛVĪM`|%7¡ÊÃ^WÞ´?oÚŸ7íÏÿÿÿÿ·mOÅSÑ/æ!Ó'~º›¿u‡òµ Ýk‘®í«âælR>×ø#¿|­Cß=‘OÒžùv,Óés=[ÆS‘/}ñ¥}ßj÷ü&92î¡éotSðiêTþÖãTìÃ*9u5OT±Èó•ßuæ?ðñ+V±ÉÉo>-Èo~-C¥Ïu+V9HøSÆ?WÆ>ô‡­h³éV´ÙꚊ[ž§â‚Sà·!ݦZÅ.ϾÖÛÑ^;èÙ.ÇÍÏ:ýk¿IÅ;¤íäwv§›õM2Þa °)®¤ƒHQ>ˆ¶‚œÊ·:å;¿#íu¼&ýªwÊRñ Iw&¿s¶ô«LûÁä“\¥|©/„öBÀ'”¾„¦Hê¡ä‡Q>Lè:ʇCe©N~8ý ~„Ðô?œ" _$0"I™HpŒ¤L<F8E#š~GCóhêDS&É2.lò¯åÆ©8°q*þkŠ7‘«â½æÊøcMSTlWÒÍ“¥Ol?`ø•¨˜¬)Ò,3ã°VˬfÌUòÛg;hÓÜÛƒ{ð  \€˜Kø-6ƒÒ˜3h·#¿w,±‡WghÐxÁàÌgHšŒuƸ ÿpp‰ØÄøDч(ÊEñ{4c1švbÀ!†ñCûúÑ…ß»;˜¶bi+6OÆgèFºéîàÒƒ:=égœ—Œ Û+TÆŠ'?þ¢Œ›–@û ‚—7÷]oÚ½Ù7íÞÿnû®¡Š/NÛ QÅP‚Öåk!µHׂvµ€_Û_Æ0ã ]T1¿wuà]ð÷$ß3]Æü¶bH2.ê»Å% l=Êz!^Àò¢}/Ú÷f~ð&ß›¶½iËX>ÈŒOŽŠ7ä¥b~“®/ê_S1¿Á¯¼o@º!ù ³T¼¡‹*ÞP6Å*ö$ð稔¤}‘9ß<·XC)*æ7馤›’ß”tÓ*/!YÅÿ椛§È¸ßÍi¯9íù9TÌo§Š÷MùàÓ‚üU¼¡ óÛŒYIý)ãŸçoˆ6[‘nE›­½T¬¡M2ÖPèÝú¶!ݦFÅ~[§ŒõmÅ¢n{ÒíÓT¬oÚî@Ù¤;»m€_° ìØBfœohH:(TÆ¢­ àu¤|GÊw~G§ŒïÝ Ü;ÑÿNð£3éÎУ3ùÁ-˜öƒÉ†ÞÁ¤C€’.c…€Oh€Šß'ã'†‘£~ðÂ(á¤ÃÉwÊ)=~F#úE#’:‘Àˆ¤Ld•œî£(E›QàŒhÆp44¦Ñ´bh#†òpvvÐoùxÒ…ò] iÒ](K¿ci#–tìEãx]IwF7ò»¥Ê8S"Ö‘_œ~wFwhÙz8dìO3Æ0z‚wOúÖ“:=«elS×)Žü8ò{Q¾W¶ŒG*bÅ?žt<ùñ×d 3¾8u€•@ÝðIs€˜³Å?—®vég¡—oÔÉB»ô°Ëv×»î6ðºUèU¡S…>zÔ}ßVèNw[Ø¥+oŒãÒBï¹tžÐw.='tœÐo7ÚÆ7ê´¿ÛËuÅM†n–­,tÒßé"—ºQ¹öx…Þq×3.ã®_„n¹Ñnv×7Æ qé¡ÄÜïšóÝçw÷¹ýïæs÷¹Ü¡ð¶Žã¤ã¯vЯ¶øÎouè'¿yfÈxpbhÔc<Õã÷z5ÌÅÔ÷&í Ÿ\;ÆWÆ÷­ïT1~ T\7à5â÷F% §TÇqä»IÅôÝ$cÌ4ã³9í7§¼_–ŠÅFý¤[ÆÉ¾þ”ó§\+ÚjÅgk>[§É83mè[ðiÃ÷¶q2¶šS ºµOW1Ô€ÕïèCõÄü‘ö!ß§ZÅ^}àÕ¯’ÓŠ”öË)¦!ø7dü5,–ÓM£ԩ₦ñGýƤ}ïKÚ||i¯I¨Š Jº 馤›’n }šÒ~3èÓ |š‘ߌ¾6'Ýšt4u¢i#š>Ç@¯òc(列à䀞pêB~ÊvŸXú üØ©&º’×X]ÉïJ~7ú×ün´ß­ZÅ…ßÝáw§T'=€ß#[ÆíüžàÜ“~ö¤NÏ/6ŽöãÈ#¿W¢Œ+â“Æ/ÄC“xò{Ó¿Þ¤{Ó~oú’¬ê&€_u͹Yü:ùƘð.ý+tïßé[—ž½Ñ6ºTèQ—½QoÞx§âµOíÒ‰.}(t¡»Þsé»ít÷=ìušKŸ¹ïg»tØJÿ¨¯\zÊ¥£nÜ×¾Ñf¿qožý‡}"—¾zFèwÝò?‹éÒîzÂuWÃe¯‹ù_Ìý®9ß}¿Û}ž¿qÏ;EÅŠD>j1VjÑïZŒÃÚТ¶SÆpöD¦=ùÝ“t]ÆB]1óY/?dÔ‹ö¼ùî >}jdlÇúÈpê5(Qñ—żÊg#ò!ûøãÓ—ñâ |MølÂgSx×:5V3`6ã÷æàéüjTìdà¶^K>ý)ãO™VüÞJÌ}|¶†Ç­ù­5ðÚ$ò—¥â!3®ÚB³vô«ti*cwVõ¨@_ù-Ï hÄ÷Žäu$¯#u;ñ½Ÿùìœ/cÃë`>C(B½PʆÒ~(m„ÑF84 ·úΑ´I™HêGA›(è =cÀ/†ï15RtºŽ¥XÊÆBãn2†±ˆYÜúõ o=h¯'4虯âe‚C¯d/SÄ*ާloÚï-ìu‡4M=¤ÓÐÿœ]þ¯²ÉÿOÚãÿ޶¸»þïfƒÿgìïÅ^¹+¦îÿŽÍýÿÂÞøÖÞNU4®ÀŸ˜³á¥V‚Ø"ƒµ¯µà]-æžÚÈEmòjÓ‡:^*/ã¢ô¬C»žb>§¬'ùu‘ϺiüÁ¿ºÐ¤ézŒÁz”­,/êz1漨çM»ÞäyÓ¦7y>ȧixëC~}òë«>éúЧ—ŠíK[ €Ý<Ò†Œ£†ÀhDºå‘nDùÆÀk ¼Æ|÷¥¾|÷-–ÓKÒMH7)–SMSÒMÁ«)x6¥ífàÝŒüfä7#Ý\š»9°›Ó¶¸øÑ?Ò~ä· ­àÒ‚t ò[‚KKpm ÝZŠ4ýó§Œ?0ýÙŠï­h¯•Ð%|o =[Ë)­ õÚP¯M±œÞÚ›¶Àm ¯ÚAëvÔkMÚÁ×öàÝž²íi£mv§BïÀÛä;€t¼ ç@êR>té Ê‘ àw¤|G`wä{'¾w¢Ïàe'ÒIw&Ý™²Á|æ{p¾œZC€œ*9͆‚W(í†Ò‡Pð#Fù0ú®áÐ7œ~„S?ØÐ7‚ú‘àI½HÚˆä÷(ú]£À) ØÑà =¢)M™hèC:†üøàõ´Û…²]ÈëBÙ.”íB~,ÈÅ’ËxéJ~WhÝ•ü®Â¶&¿ùÝÈï^ÝHw'Ýö»3æº3ŽzP§uzîQ%ÕBOúÖ¾öFOêÄ‘ŽfœSª‹^àÐ üzA‡xàÅCƒxòzCƒÞР7õz“—@ßh;Üû)t‘øçÒ›7ÚÌg+»t×v°k/Zè—~záÆ»ÂàýlT×ìšS]s¢kþsÍwðêŸìC×ß5ú[‹þÖvžðÈxBÏzð½^†4 ¼ 7ß@׆”mÌÆÐ²1Ÿ¾ÐÆþûåÉXÝ­ø½•ðmËom¡{;à¶c<ð[°áQ ŸÁÐ7„Âi+e¡±ŽÐ˜G5æQu„Æ\ªuP6Q Ì«k ù× ù×BÔ[kä_Cþ5ä_Cþ5ä_Cþµõ¾ ù× ù× ù×­§ºƒ‹ükÈ¿†ükÈ¿†ükÈ¿¦«{ È¿†ükÈ¿†ükÈ¿†ükÔþ(ò¯!ÿò¯!ÿò¯!ÿÚpeÏ!ÿò¯!ÿò¯!ÿò¯!ÿæ;qä_Cþ5ä_Cþ5ä_Cþ5äß|›†ükÈ¿†ükÈ¿†ükÈ¿†ü›÷‡‘ ù× ù× ù×óŽò¯!ÿò¯!ÿò¯!ÿòoîí"ÿò¯!ÿò¯!ÿò¯!ÿ¦-ŠükÈ¿†ükÈ¿†ükÈ¿†ü›oÜ‘ ù× ù× ù×ó]ò¯!ÿò¯!ÿò¯!ÿòoÞ}Fþ5ä_Cþ5ä_Cþ5ä_CþÍû!È¿†ükÈ¿†ükÈ¿†ükÈ¿yþˆükÈ¿†ükÈ¿†ükÈ¿†ü‹}k ù× ù× ù× ù{#ò¯!ÿò¯!ÿò¯!ÿò/ìp ù× ù× ù× ùúTCþ5ä_Cþ5ä_Cþ5ä_CþÅû ù× ù× ù× ùo5ä_Cþ5ä_Cþ5ä_Cþ5ä_Ü × ù× ù× ù×q—QCþ5ä_Cþ5ä_Cþ5ä_CþÅÝ ù× ù× ù× ùg·ò¯ ùÿÒ=nú1(ùg?æ¾U¨Ú»Ú¤îéx¨3ç¼ÞG¦)_Õj}’¦ÞGÖÜpW§XÝÕ U÷ÔóäÅ|#™¨Ö(ùêÀWù4ÈQç^ê®zŽ\¯˜gס7ÜÙ U~ rÔž˜¯º·S Ö,ê­T–<0ϵ“Õ½§òm¨|«7“êÞz¶¼gjú8p_¿+êO޼j¾ Pgã›Ôù€‡òu¦îò”¸$ªuMŽô{`ž„ª;í9òì<¤Fù?p¨sôly–núA¨Qï+ÔË4ußg“[Ý-‘÷cÍ3µ×—,ÏèÍ5SžZ79•/åO!N­¡2Ô™D\K™÷k}ÕÛdu>GQ8Õ9…—º{›¬Þræ(ß NåÁWÝ/JQç¹ê éîy©»öÉê­gŽ:×pªõ˜—ڃ̕çâݧy—À_ÞC2ßzæË{÷æzÌKùgHVë°tµË—û•æ›O/uç7T½õL—6½y÷Þ©ö/‹åÝ#sMæ«ÖeÉêÞ}ºzó™¯|4TËõ™X“õ/}À«ôìÞ}àMàõ¥l_`÷Gr:ðtàéÀJä3Q|ÒÇDú˜H»‰À»…r·ðÙöû³0ûÁÇ~ÀëG»·RöVÊÞJ™þ”éO¿ûCçþ´›DÝ$pIv°“À#‰1’ÄÀx@zpˆô9¹\H¦ü@ðHb m ¤­”H[éã ñíCûÁ´5˜¶ÓÖ`h1<†€ÇPÚ œ¡à;”v†ÒÎ0ò†‘7‡So8õ†So8ýM~ 8¤€Ãꎠý´?œGRf$eFÒÏ‘Ðnä9¹TEû£)7šr£)7šöGÓ·Ñ”M¹ÛÏÉeÌÊ1ôe }¬±´7–rc©à“J~*ù©à9ŽïãÀyœøN™qà=¼ÇÓŸñÀœ~Àe&Ðæx:29M„>¡Ï$ÚÄ8™n“éëdÚž ~“«äÒi 0¦#öÒÄ'x¥Aƒ4Ú˜ NS©;zÓèÿ4ò¦‘7 <îîäMîtÚŸNûÓ«ä²k0gk&¸Ì¤üL`Í¢ü,h4‹ögÑþlÚ›MÝÙ”™ fƒëè8‡¶æUÉåÙ|àÌÎ|ÚOŸæg!ßB…Ð,d'8à°|ƒçbpYLÿ—¿„ü%ÀÈþr`/‡žËµœ>dAË,èEÝ,ðÌâ·à°‚ßVäH½oþ»yoçæÚþæÚþæÚþæÚþæÚþæÚþ_¿¶ëúÿSëy¡¿3’âÔ=ÀuЩîÙxÉ»6æ»õtu70_žcšï×½”¯ÃdyÇô£”/ßô˜~}•ïÃdõ¶'G½ïqª7>^êOœzçž®|"nR~«”¿%/ås)QùHÌ’÷{Ì7ðÕê-¿z”¢ÞåªsÒ*uÈW½JQïãs•¦*å£É_ÝELUç¨yò,Uøœ÷ýÍ33‡:WÍP¾› ä[\Ó×¢¯:_Í“÷ŠL5ÊOM¢zC_ ß™¾}•§åc1CùY,PïèÉ¿5_½r¨7ôÊ×bò·¸I¾%ä´ý-Ë—ï|Å_óŒ•òƒ(›]GòÛHòRàÃpMŸû€Ûhp¼2}ר}ÅwpÔi[¦NX‰|&ŠOú˜HýDÚžL™)ôõÚ¼~âõ£½~ÀëÇ8»•²·RöVÊô'¿?ýîûC¯$ê'1F’€ì$pK¢IôiãaéÀ Òà”ÌXH¦ü@ðH;ig"eRf m —A5rù0˜v†ðÛ`Ú †cx ¡´;8CéÿPÚ Œaä #o88§îpê Çáô7ø)ÔO‡ÔAû#Ày8¤ÌHÊŒ„n#É=Gç(>GSn4åFSn48Œ¦o£í){;eÇPn 寀Çú3†þŒÖXÚK¹±Œ…TðI%?•üTðÇ÷qà¥ʧTž|×iúTõwóçïT>UnoÖ«¥ÉfÆßI“>ÁÍ÷5¾Ê7x†z»^å‡'Eù÷ÏS>Ã=Ô;öté?\¼ 5}Nù+_ÿÊçjô»j¾ÁñUïpR”ïÿ\凪Jù`ôUqRÔÛœ\»§Ê-&@ŠŒ•cúˆÉ•~bÄ #@¼}~]Ìø6é¿E¼oâ;1Nú4ñrÌ÷ñÙÒ¬ŒÚ¤|YÕ¨·òÊçLªz3Ÿ'cÿßVæ{å‹&Uù„Ì“ïèÅ{uaÚšq•Ï«lå÷Šü¾¹êm½¿ò•ªü`å©XAÕ*¿z²É~'$ÞÚ~a“•¬bùÖÞŒAà/ß ôW±²TübùFÈô‡å¯|à?¤@¾¥5c«8ùÒ¬ùÞÞC½¹“~±L_XYÊV‰4µ…Ÿ;¨tí^}À«ýíC¹>ð ¸÷v_ÑoÊéÀÓ§§ŠH—ð‚ÏDñI¹D`&/‘r·øJ³¾4ìGŸû1¾ú¯øõcŒÝJÙ[k¤Éߟ2ý)ÓŸvûC¯$ˆ?’ïIÀN‚ÖIô3E|BƒŒ—ÀÀ÷±ÀK¦aä'Sg x ¤aà;¼”H[Oƒø ýÓÖ`Ú üÁà=Z ©‘ËŠ¡ôm(p†Bƒaü6Ù!—ãik8õ†So8õ†ƒãpú›üòRèïꎠýà<¸#)3’2#Áe¸„~£Àu4M¹yà·ÏÑ”_œtðLÖhÊ¥ó73U=Á‡É´›FÝÉbÝÝÆ2RÁ'•t*ýÌq|mÆÑÖ8ÊŒ«‘ËžñÔ~Àop&Pf}ž^(3‘¶'ÒöDè3 úLÖ$ÆÉ$ú0™¾NÏÉÐw2e§€Ã`LFí¥‰Oú˜Þi´1œ¦Rwõ¦ÑÿiäM«’K«;€{yÓ©3§Óþt`Îæ `Îà÷™à2“ò35‹ò³ Á,ÚŸEû³io68ͦÌlè3\ç@¿ÿÁÞ¹Åy]wÇI¬±ãXAHBd½ˆU;² (°ß. Ëjy-]–DzÀ.°ö»çcuÿÄ'Nœ8ò‹S„: óz˜z “_ ›61lbp…Ï(ÍøFñ”×rÅ®™¸Íði¿—ùðy?Î \[Éa+Øí¼ä}¹‡96ƒä8ßAò$Ç.y,¨×^ð‡àÂvˆÂƒ!â Ágü°Æ‰7 ïqbÃcœñqâŒËõŒŒ“ß8ñÆçÕG´O‰˜-›nð[ño· ¾!bvPÓ8ˆ`ÿä^ü¬Ü—_¹/¿r_~å{å+÷æWîͯܛ¿z×ñïöÞ|^ÆŠ®Ã‘+è:¬ÖšfØgÓûYåi=Ý£zÏØ\­ÿ“Ðû–/hm‡CJÛ!¥©‹Oyf›5·dO«£ZÛ,Oï{Hëê.ê½­:ÕÞV)ÍÌ<½wì´Ö7[•¶ÇõظJïSzšô>W‡´Îó›‹´ÖÃtšÆ.ö[ŽêýÐ×é=d§—ìuÖZgsišZïlNkYõŒØm» uŠ´Þ.ü¶_Ð{Ê:µnçŒÖ2Z¥uw}ZÿlZk ­ÒûaÅ´ÚœÞg6Wé|¦öÅ:¢÷Æ'oQë é½g™ÿøµ7»hó¦t"Ö)]ÐÔ>í ­qLíI›ÒL[§uÓ|zoÚ#jZÑMíã¾ZïSkÕz1­å;£õ|çµ®Új­­æÖÚ¾ØÜ6£ö±M鬭ÒZkÔÐrHïÃ5­ô~S{Äghí5â|¢SëPL+íÒOÌk= ü÷8õ>ò`ì9©tySºNZÿ;:õÞ]ÓZx^ë´1¿÷Þwþ´Öl[­ôŸòÝzÜ“z?Ë ½§%xZuJé£Þ‰Íçõ~¸¹zÜ"­S1¥µSOjýÔ£J;*µïå*µ÷å>«Ú7µwý”Þ¿þ¨ÒUMí‹KìRŽK)¼JáUJ¾¥|fX86¥p·€m™WKw<<<,+ÏVyÆÎ žõ‚ZÚÛÀ+#~5,£†eÇ20ˈ[ÆyfÇÖŽm96娔cSNÜrêUoùU€]Á9R ŽK5¨ä}%8•àTÊ{jâÄÞ‰ý~xí‡ÿ~bíÇf?6û‰µŸßS*j_E¬*bU« ÞUÔ¢–üxßÁ\5œjÀ¨£޵¼¯ƒcsuøÕaWÇ:òuƒï>¯Z‘zêRO|¼‡w6 Ø„›G;|àÚH›°k¦‰\z‰ÕD~MØ4Qc6^xz‰ç%ž—x^°¼ðl¯NÍØø˜÷á"o\[xÝ^ X-Ø´À»Þ­Eªjƒ_x!b·a×F¼6lÚÉ¡µS?qýðósžø©M‡ÔÞÔ¾Ûu€À¿Szyy†SçyÕNuå©¶©Ÿ¸]`t3×Í\÷ApƒŒ…àw!â‡Àì³Ì©\zÏ«¬û>jÔGü>â÷¯NýØôS§¸€3Ç!p"àDÀ‰€¡VrŠP§a^Saò‹aÃ&†M £ð¥¶£pe~ŒgŒù ð'ÀžÀn¬9$¨e‚Z'ÀLÀs’±I8L26ɘ¬—S?fooöóÒÇ/íá¥w_Ñ{ùÿ¡÷"½çZzŸ—êñÌþn¹žÎìáÌþmiïfökÒ§¥÷fÒ‡Ifþ=Àì±ÌþÊšq¹·2û*ÎùT?eöNæß–öJé=’ôDf/”Þ™=ÙûHÏñÿ™~çJ}ŽÙßHo³\?“Þ˘=Œô/fßbö,f¿bö&+}É/×—ìɸ:5Šöhû)­Q¤ö®Íb<‹ù,j˜íÖšm{”V±Üê”}Ï×Ãyý÷9v9ðÈÁ6‡ómãj­KlUºlù‹Z³hFk'Ôþç²ç%­µóJkM´:·Õúbë”ÆðÞN½«Si ‹îAJ/”×6âØŽ*]OÇŒÒOÑ sS ¶%ÔDZ >Öå¶çnê颵N¥&:Â.r©†s5u¨$¶‹º¸È§VÖ'²^’µ vµSJCXö–­fÜűu‘ƒ‹øNl*ÝJ ©~ äYM.ì«ñÝ;§ôÔjÁõ𾻎UjOY7yzލKŒ¼÷Nì<äÚ„mƒUk-€ÕDŒfò$æ ÏÍpka¬•x6YsÀ¯Œš”É<˜ƒÔj¹vìB`U€õõª<¦n×VR›ýðg›0œBÔcœœÂà á7Djˆ9.ÏðÂ9 †[` 16BœqbSçrgœq™·»&±ÓÃk/s^±‡“—º7S£fŽŸ>æZäAÌêÑ V+>mpjc¬ Œ6޳>~¸tàßAŒÆ:È/€]»N0:å>’3]²ŽÂ¯žÝÔ¬›¹næºá¤AæB`†¤nÄ ³Ì°záÙ‹}/X}Ø÷QŸ>â÷¿ŸxýøöcÓOíû9þÔw€XCà 'N„¸ŽQœa©yŸWK€81pbp…Ï(”¦ W¤uð[7§õ›÷hí b¯wC‘Ö¯˜ÑÎà䀓¿bo\§õ‘°ÙˆÏFéœiúp«•Æ©q¿Örž×ZΜ“[Ži¸<­¡4£÷öÇg«UéÄ•1oL+8¹¬_ÒŠ›×ZqÄÚFìmÇ”Nª ¾Û­Z×™í«´æõÚ1¥ô-vÌ+}çýp-8ªt.v2¶‹±]N­ÇÄØ®óZï"OiKT‘ß^ækÈÅæTm5ÓJ"_ŽÍi¥“*Zù Z?ù´Ò–(`¬`Aë)ƒYH…ð)«±HëK€_œ§ôÙìë´¶ǧœÂ9­¡ÌñÛG\sÆi¥±Z ßÞ—ø”V[£¼Æ¶ä~¥«l¡ð,`[ˆe»[ ظ¹ jiÔȸ•:Z±·ñÚI<˜Uä`Ó&¹ƒiçxØ…'±ìàØ±±3og¾œG¸åØ•“W9ËáZG~üø9ðsàçÀÏ{§ô^Ø;±w‚á$®?9¸àåâXÖÉz^UŒ·‚ãïŠúO檱­†5ùÖ`S³ –oµø×â_ ‡bvaïfÌ͘›17µqÃ-Šo=¼ë9}àÖã^žÁn…ócÇÔÒn »F^7·®ØyÀêß–~|<ÄòbãË 'ï‚Z.6¿™ø>bù˜÷1ï“> ߢ^JâÛÊ1“|É»‡x­ü~µÂ£\ÛW«e`;8íÄn'®^Qbû±ç׿Ì ¾<Ô-F›ñØtßÅ\ã]Œw1§Ž]̱2d¼¬ |ƒ`áÚ#°cŽ}|A-“Çñ™EÖtò#=Ùû,ý^¸ô:Ëõ7Ëi³H#Ò·˜=ËÒ>eé)?ïûáfböÒ{¤÷f±ôþsúwÇ—öfÿþ=r³g~áý¤c˜¾n^ºV6×ÈæúØ\ߘ±ü:Ø\ÿšk_ó»éK÷gYnM+kYóûé²n•õêrëTs}j~Oݱü÷Ô[—YsškÌ+Ý6×’æÒ\?Neüì}èÇôÎqÕé@惑‰M>¹ðXcå9¦ôT×R¿‚„ÒsÚÇ\çEÑ‚Òî*Y¥´ÛEëTt‡ò‰Hk6a_ ¿R¸”X•~j)±7žV·56ág`»ù‚Ò¾5Ž(M®2ÎÍReädpÞ—ËVÙ¢ºíP}1q+w‚ë„_1µq’k˜øS_'ù2VÅû*xذ­"v15¨!V ~uä^ãÌRYóެ°mbÌ '9yÁ°ã׈O=ïíäëãºÛŠïˆúèieÎùV‘§›60ëäúF}©]£`ƒÙF#¹×`WG®]Ì×Èõ€ç:âú™«Ã7€]~õrírm%^Ÿ`ʵH°È¹.Qj%~Œ÷>øzåú…ý¾cøúx$þ˜\¿äZ…o1¢ðÃÖ?£>þÂàôQë(5ʵ“|Æà9 8p ‚5†ß˜\å:CÞq|‚pèÁ¶G®I¼S¯8Ça>q^€å˜DÉ1ÎñŽ‚§~qñç8ÅÀŒË5Hæeì8\·óœÇü„UõQ©Ÿ_ä>ܯêܯóþÛÕxï-ý¾Û•®—¿©{n¿Èý¶_Åw–å^[úuùÿrí·á;ÊïÕýµws_íýzOíJ÷Ó~SßEþm¿—æËXÑzå}¾Oë½+ßLxg‚ Ÿ5ÄZÖ°×ð{~3x7û´Þ뜺t®¥nká·–YðË"¿,òË"ß,rÈ7Üìû•~x!µÚÇü:ìK˜ÛG×SÛõ§ÕewƒUéW–Ušâ9Ø”2—Fެsµö+6y¿‰˜›x¿iFkŒSïÍ`l&ÆfòÚ¼ õ_©Ù–“Zÿ•ÜsÁ¶‘O.Ç+>[J‡\4Måoj‘‹¬Á£»mÄÙFÜrÙŽÏv|¶Ëkøm_ÔZ°Ôg;ˆ½>ùÔ³»B8ìœRº›;ßE»ÜZ–±*{áµ—¸{±ÝËÜ^Y“OkV¸ç“o>X1âùÀˆâë“5;ï{¤'À§ßVéaà#VþaòlÇ¿ÿv|Ú‰×N!ډ釓?¾~âùÔò°ß0>pãø°‰3 } ø]àw1×Ãû0¸]ÌwI¯#öÒc€#ˆOÌžµ´ ’O/ùô‚Ý‹_/|ÂpÀ/ ¯0œÂÔ!LÂëp¤Og`A-Gá3Hý¢ÒI=¤¯á8Dá…C”#ð!Æ9pnÆñ‰cÇ>Ž}û8ãøŒã3‘›¡ÖÛòcöÒc¤÷ËÝó2{€¥÷³ÌïJÊZÛ\gËúzé¾»÷ZxÙ{5é÷hÒ÷Ð]î^Œ¹ž1ïL¥]ëÓïP×÷B÷¸sb­Oµl%üSçõØæð;ŸVñ71os ø¹EJ9Ÿ×ò¹È\!\JÁ+å˜:9—j8‡ Ž·ÁsñiZT‡Ä!Ÿø:ñõ˃×A|êy®—ßubû器=GT ÔJŒ6üǰñ;Æ|+>ærNò:JŽqžãp‹ß¯tÅóð›pf¼ëŸÒX/Šž½àx.ôÔ;wî|ãâFûmƹ—ÿûKEG«ŒÌ–/¿zêGç’ÇnŸ¸a.ûï-s§ÞžþÆ÷ÿh)Ròé›wþdä³÷;ÖïŒ<<•œÊß¼ïSOº;Õ{ëÅ *Žñ…u¼é5Î}ýl±=û©ä±‡æÝ…u‹É§7?ÖRðãO&ž~k[âk7bŸ«ì÷¼øáGgcƹû~||Û£_Jÿà ?ùäµo$ŸÎ:µö–ǘœZóÄ­{<{±·*û½gš¿zï³FöáŠßyå&ã\hxm×Ö7¯®ñlêOo¿§äæß¯Hξs»óÂW7ÛRî]É©Ï<û]Ë;ëÁéT8k«>ü¶q½Yò£ï‡ÿp©Ç¿ò¡ÛzŽX^»uldõ?7%g¿÷Ò“«ço1œ*¿äÔ3ÑW&b¿ ÞT ïù×/¾vÍçÇs›ÿÖõÈG?<þšÿ¥ç"KÎ^°”µþ ˜œúœa¹6ömì§•ý“;οúÐõÆ¿qè»á7³oåß¾6º”ljë66¾õPŽåõö;Z,o%gÿåáÝOœý¯äTÞ#ÿt¼èApN*œ»Ï¼ü{Ï]Îã쫇>6ûÚîKyœ¸íBì°íìå<^Û}pþ³ —ŽãÁ'>½f·ç?ÀSçÉóþ‚oÚr³Ç?÷é¼í7$O4u>zv19û×Røû’=–^wºÊz1Gïç n½»hÕ=ÆÙ×dù‹'O|æš·¶8¾oyI啜ý³¼ožþeòà;çÄØíø©ãþüÖ—K^eÞ8{׋wøÏÇÍ:$O|s¯w&gŸ|ì©uçÆׯ9Xô7öäÁªÍoؽ‡Íóu><Mèîfo0ÎFîý^öÅ?½œ÷Û»+òòJÍó99ûøÝóÓÆvçà÷ø½÷òù›£Î‹3ó_çŸ2Î6NþÏáïä'Ÿ±ü Á÷Çáä죩8yPgìÕq?ó-¯ý~Óåã¸çweºtŸùõ£¾Üÿ©þ}j±Eã¯Îƒ3_~öÛŽý÷_>~«¾øñŽä3çÿ®øó‡¯KÎ>Ñ´õdyÞ¥óøÒñÊQÇÿÌ]|ÑÛü#cîoý«[>ž›ü‹­®»ÿýÚäìÑ?˜ÞÕð¿ì}XVG·.[i"v•¦H/Š€¢îÏN¬(±cÇŽÑDìXÁØ0–`ÇŽiBL Æ2¨¨€(ˆ 5*‰IDÑH¢QcŒ¹ïš™½7ÇóŸÿž{ŸûÜ{þsåy>ýæ›™5kfV›¶V_6þŠ´ú«¢¼˜ß¼ñý,ãjX«…_µŒ¶ÑS¯£4냲XÆ×.Uou¬ÅæÇ€ÃZ™™Êˆy΋<]þƒZ¸ì©µçŒ_4|ÙÑMçr².|dÐ×·C[¨s~ÑékÞC–1¨¿ 8bÞóLÕ¿MòøT-½î@¯j‹öÏü|ýx÷kÝpvTý›,~zæçl^ Òkyô*Àóžz{í¶Ï¯jã¬vÚ‘—µ¨¦Z[Ö?úpïÒ{?û³Œ³3÷~mS]m.Çoî\1ÿÜGª« +À“óÞÀ¼ýâ;ôy(l|°Í•/tüŽÙ¼´êôÇ£ŸÇŸnù}òçj°lo®×wï nó‹Ú@ÈUÀô‘KÉ®‡ÔB›ßÁ™_²c¾uï¨XFšù†±_ÿÆæF¿5Ù< å=ä²-#v|£\çÏŽEî÷|Ñ@–±üêˆÙCØÜ³MNݾ·åÅüçnx¹°t®¯Z°íó+ÉYŸ³cóÞí<õ…1Žï¯rÝÐç›×ñe•ÚáÏQOÐAÝ2´Òé¶`*ŒÒçõØ·—öšÏ?“¬ê]=4H _:¹Ã'3²y)ßÝ ÍݬzküØPÐI®:çÑšØiú¼D\ú"vÈ7:?·ªtÛìâ7~±ï¥»doQ=ïõÿh•í66oUL´þ?|ÙPÐMnµ/ÚNì>@-¨óõ´cÙñÁióÂÊXƸ“á3û©M…>`ó½ ž “œßO®ìùL½ðÝ‘I£×³ãGÏœîV`Ã2¦˜ÅÞ\þ™1þ äœè›»¤|ŠFGê…ôéû¦æNe¬îd³U›[òdÆ’÷6{VWd¿æôØoÖö«Áß Åüç@Ë<[×Y ‹’ÛŸÜÝöoYµzÙ÷4èiþˆC_Ì}—ÍžýkéÏ–UQ_ÐCδOs»XôR/DÕp¶ >ÉXÚÂñieYÆBåü°Ùlöà9Å)_>@yA9‘¿ÛÒc¿_½àöÓí:y“Ú>•ãÍ®çýPCI7ÆVãeÝ;ÎQÛ¾`³ÿ8ãí4x®8¢Gѯû_® —ÇOŸP/êôrþÉ!pÜ £¯>¨š]?µm®Æ¸ÑåÀœC‹—— ò3浑 —³ß•Îw?¯žtϲl’òÚέÅ2ˆýx˜.÷攼ÿì/׿QOÐÃÙOÎo[ô£‡ÞÏó\]Åëý̪?ÚçÒ©á,£Ëµg}¡ëí9þÍÿº¾2àú8›8î¡ï£ÕóÃíîÙR§ÿ,ÏïRœÊÀ§0Zz «É 6[ÈgÔôrvRs«v9®êù6×iYV«'NuZ³ v¤ùØ,×-ÖËFyAg;®ñJZ½T=ßxq$ËŠ¼7ÓenO–áÔð£o^[±™æL9Ö´ Ê‹ù?[µëŒWWÏ[ÄgUi ¼bÇ8Þ³­eÐaÐíP º^›sù]+'Ag '¼RŸ·ü{G·Š:̲,ºnù®#ËWÜ” ÏÓlBÃòê‹y?CÔœ¢æ°3}m1?|ùb@–áúi·÷N×g³…<0•9‹ù<ý×ícÉ÷ÔüO¿ê •¡ÓGÖ™[Çß1è.`2i.ÕMÊWîÅüž^ݰ핦?é|“ß>ðÕ ;c~²ÕakbªãКS<›Ýe±{õ‚€#æ÷4i5ÇP5¿c!Y’,ëj¿>ÜÝœet²û+7û&›MÖÌ|”óyº>g5ß%{óž»}YÖÏ«Úþ¶¸.ˈøkû¥)ó*ÀóyºRHx“)¯t½‘¯<ïm~¥ËzâóÇw?ýíÚÃÇ篯5ú1ô³˜çì8ÁjúF=Wjû]b?]nf½ª³ú«6ýíþ°Ý-G7}ÞfMˆLÿmöAƒ¿œÅügïçøþy[õÜQŸu×M3õñ;a²tw^/–Ñc]ÕeÃÛ°Y¶¿&XÏ„zbÞ³cÞ]“Wï×¹õ ,ß Ðñ9QéÊÖ~?5ú×Å¢ç…M¾º¾žÕífê ¦2AÙœÕsqçsÞïÊNXõY]¿Z=ðWß÷w9vg³É|Üð'Ê‹ù?uÉió˜?š«çzÔmásŸ¨½ß<.ÍŠe4¬}÷è¥Alöž»QÏ/\Gy1ϧ6NIóO¼¤žóÛáöòê9]oŸðøÆý§"cܬtüÉv›MЇ4A}1ï§Fîµ5I“ê¹ZÇ!9ëýU´ïºÎÐÿbþÔÖBž°ÙžÕkžœ5Òÿ.‚>N8j y¦ó_Þï8>Ùaà7¬ÍÜ–ö§Œq”ü¨Ù³¶ÿ ç«–†Ýë"èåäwV _¶¯ªæåý ÔÞ˜×E[ŠkÎö3úë]ìx;ícMO³Yr>u:qtrrAʯ—õÑç;o“ï˜äþþ§ÙþqwiϰÍ}Ìæ^ÖõöÌ»fP™ËOÐÏI2··—êü›7EíóÊ£¿ÑïœQIê•J,C-,»ÂŒÍzÈTæ*èå'Ëfj^—Naƒ¯év݉5.º:Ëèºù¯§†~pó~Ü¥óÙQ_˜ér'wO-%ïëëIv²Þ¼gV6è©Fù©îŽË+Ì—˜ïc®|Á­æ®}cLÍÅ줫Ë÷ϯµa$5SYü¸E½£3=Menb~Êu¿fgå.ú£–òìd@@Ê´¿?cXdÍôüžÍxæ3þÞ•±¨'æ÷H«ØŒMí†øÂxàj®¯Oš‚"fÄÖÕÆ[õö8‹ïj×Îf¤ àˆyÿv¼Òdã£9j®”'û„šÏ´YbôÓf¤õš¨Yš=oЛ˜ïÃ?öJíÝ©¹~çj.ýˆãÞ5ñîjÃþ‘rJ'71¿‡?Þ±.;B—¹BßèzòäÌ8ÿ1‹ <üÏ- ×éeæFúÛxb¾úÛ¼ÿÜõÝI{@Ÿ¿!7S§ôײC½Ï²õÿÌÝužµé x‚2E?Õ\‡ys,¾ßÂNn›Ñ³ìÙP–Ñv4fž¡'Üļg ;QÍ•rK§Ó/–.Íž={èÔ6ÏØ¬ÜßߘñwóÿMêV0 :¯Ùq¯[[vòÛ‚éÿø¶í¾‚oÕŽ2-íÍÞý7äûž«Ómòuû~ÖúöX!.1Öw*²~PeŸn‡æ<¯éøÕ7¾Æø_ î»yÄYcüõoûiÈ,ÕG®£4½¡ËcwA'K†Õ[çœòÅÃÿ(o`ŒïÝ&—÷Üq3è¤Û²°u9?ÖôÙóž “ƒ\Qs'ž±dšßSYf ³FÏ[Ûºèò\®gQ_ÐÅ—­sh‡A络¿õ>±ö+vJ¬‹YF-SÏÝi'Þ´÷Q_ÐIÆÈï"ÆÝRs`䵘7–òú2áW_OÏ û¯± ôõ«MéÛÝèÿ}NìTÏ7ó6|Ä2`ÕØz¤ÛåÒþA}AÖ_.ýÓìÖ“?[Z%–š¾Ë„¥Ë<^í‘çJÃ.o,èáó?×.ûÚÅÒàKËSOJB«éråTÊ„-Ý Œy”v¥&f½ÛÿÏq)]OÐÅgÃûxOQ£ ¾tÛûä÷€“ú¸Ÿ:ôI·”Ågõõ‰¶Î™âéýñNÀtðIbWÔ\S~üýgûÙ©ó¯ÿ|ªœ6èô=£çÚdØíÅ|ïï½pÿZ›ïÕ\¹ŽÒèæÔíåáͪ„€¯ÂÏ·H‹6ì¨ÆbžÓzuŸ»ÛÞ×Ú—ü©Ùñs:øî™?ðļïÛèt:zT'cäºõßFêiô£OQpÙ¦zšc¬÷šzØç6iê“Ð/•ƒîî»iÌËë»›†=ÜqN४lî;Ôq[Ôt°—¤§ý ?s?°ìƒ÷ty›m½µôàA–!ÖoºœÕ÷šúØ;èÍŽ§_ôA»M“SôqÉv4_eÞÊUç#m=fôCÐÅž{í7/úVÍý!#=™eûï9ôÇ_]5î:ÂýûD6ÇîøÝêϨ'è`‰ŸÅ%jn¹I–ÝÅ:aÅÂ,ÃÙ6øDÚgl6qwãm(/æ÷†±_ÿzüw5÷Ej²_I5– éšmK4yшoœëû«rÁX—4ô°ëïÏBö‘Ø¿aÙ³˜ÙŸyaØEkZè¹SÛ7`³oô®¡ê0C^6ô°k]Ьüž¨y(½ãÎa–½– :–4úLx6gÅZ—ëë _<ļïòé÷ØùÖC5Oð—>ïÙŸ\,l—eÐOË·ØÚ°9—aÅÜЧ‚v&]ˆkõç|5/iÖÊQ×~Õù!ûøî²[Í»ãúdEßúܟ{æ£g¥^€#è`G⽬Éõ«yÇBƒMdÙ…NyD£ßñ%›×jî'½ò0~b¾Siû(î…š'ôË.ñž±óÂyÍ~fóÇó@y1ÏÛÇ>½ygu^óHÌÜDzÎY?çòXc¼…^Õé,Ánü« ¢GÌÿ¶:yS[wÓùN[ÏhvmöóÃ%1¯#ŒñzR·—¬| &ÜgЃ‡ ‡-“‘E©žóßrnn»éìtåL§Q%¨OÛðõ_ûÓb¾7 ½o¬##|JÖÝ¢óÍéZ»_¼7©ŠA—cÝ焌ÖéRÃCÊGSYSAåþ›Þ¿éµw}™¥Ïëiï‚oF·Ö÷‘ôs«½¸ü²²†&¯OÐGŠÙ¾ÓŸuqRÏÑnå„Öìt·á¯]¯OÑä[P{õõ¿B^ ¼ ƒ r¿ùÜþC¾¼~ÿzjùžrc~ÅÊû–@«›¨»¨'èaÝG3k•ÖîmìÇX|¿9ùÁ6c<öŽ[þÕåem¸su]þ§*˰ã ].&Tþe§Õ'»OÐËG¿îÿóÞÄåÆþƒgWwëØ±Ó7gl¼Ä˜ßº‰>•l»ûòÔûiW >i*èfM8uhˆ®wó){þJvÆþøºÆ?­5ø„OÇ]oÍËì]¶¤Q&à:Y-Î{t{!?ꇫî+wëü{FœöK«vU‰ò`óµõsSA?+ÄúÚÀg‚K÷PŸç3c†Vz°Áư»¥^Öé¸Í&²ÜLež‚n–‰õ¢~~˜ÏI†ëãfáì)?ü\¦ã¥ÏŸ§ “ŇÏÅF?d೟Üì̆˧£ւ^‚š²–-xï±õO!‘¨'èe>­•™j>m+E½`göÎ[<~ÈDc~Ä:X·ß ðœS©Æ~‚§ Ÿébטo¬2.Ibg¾š3í—Ÿ<´}-¶ò¢cž=ÝŒ^¾pþŽÔõ¼­u!†ˆ9ÕwžÅ†ýŠu‘Ú\ž¯x:éz;íã‘C§û¯¡ÝÎì{¼Í˜ßkã¿N˜ÿÈàkaoèöåÂ&A´’¬€§Öy‹û7ëýÜõþÿ·ëvæñ£‘Þ9dàÙ½75d·Ä6—³…Ü·¾¥¶“x.ë)M~§6QÊW­ÿø1ñXvvžõÙ*Ø}b®Ÿ{H¼ŒñôâôÂÞåÔlù¤DõÂèñÇîþyŒÝíq¤ßgæÚ¾±Fg(Ïé‚Íà˸wô}° Öì\xD·—Ξ9áXý­!d¿4»i!‰·ÈrÀãtÂæˆþçRhzèìÏÏç&± ao³…צ9`HQ_ЃÔkjù§1fíd9Bþ±ô?ùA[ã®sÒl”ó½ðZ˜wT :üM»®,§azí€áXú«ÕN…º±EÇùˆ©L¸OcKr¯жnÂw:å´0Ç ò¡!‡­ø†¾¿¹8üyÓZãMš)Ý”ôÙº|Y¼‰î(/èbóç§4³¶zÑ\ºȳ[ñá±!ÙF¿ä¾áº%ÒÝõ}l ©lþ ›z1dVõä GõyÉóÞH+û¶eúŒnº³ K—û0-Å9[rÚÿ½5 ~õô²Õ‘6btz¹Ø÷ÉŒA Y^—ßGÔëÞØga/«Í$¾K°*)»^ɘg_A?ÛîK?¸æ#õâ´öŽ ¸­oÞ¸ˆ”gU>5ô–Ü_Ôì÷%t¬=vàzÙΗuƒ{%+ù!]çѪÎiª~n¸döÙ糞™ 9à+èhûÁ—“ŸÝ­óÓŽ·_Íj¹ÐÀëÈÐêó ² ù"÷q4=½dLA»ßj^3ô¿¯ §ÔŽÜÐT/ž¸·aÎûXÞ}ZzéòAãÏ%Ýù8ê zI-{ô²Ãë!Æ}—[|ƒAŸÇsõ£7l²¾ÎÒå}–ÅÑAó9Ôô³ã4WäêÅßí _ÕëZó½Ünaº½–η•#uûo1Ý2é¡ ¾ ŸK,»SC-’÷1Îa5úbùÇÚ¼èë¾Å·çíyÑýsƒÞ}½ì ¥‹K×Ô"Ë vnß]ËøéK ¹tµÇ|»KÅúþÞ~l椿©ÌOÐÉnïGµÖÍÖù»ˆ¶/,véó{®àÑý¹G ú»L±“- [: ëÓ_µñÍë?‘·`#ê‰ùߡšZtÖþœ×Ðmº^Ê7}“<éŒA?‡ùO׉Q»Vúí¥Áo~‚ör5ßC-ò\·?ó|ÿdϼOXz·]¾ÞÌù1KÔt°·Ï±Ö%ºs ʬÏòß_ö¸x|2K/àŠ%ŠsAÔó¿—ÄΑê%¿¦ÆÛ²üõKB8=Ä ?­=)ïõyòó¾7¹ÿÅx¿0þ/õæü,ÿHò¨1?c¼é ½š¾NKnÝlп˜ï½[IÀ T/͸Þoj·­,ÿ^ˆù¶sÍXú§¶I»ç²D#ì˜ß½ë:O[pí;]ž\âǤQ:½wèpjw€3Kßô¯nåY¢ùQ£+¡¾œo0y—b õRþ7—ëu¸¯ÏÛù‹Lk²y;ñju³Ðåú>ÐÒq3ÚîkÈ%IËëÓɃz‰›Qöì<±s·$}—^ÌìõqXBÇJ‹’ ùí/ç_Ò³6ž—^¤›z:œ?d}?ÖΘŸÒ} ÞtY—»KÝWî:Y#ÉàIy•³Ó·mÔùó²Ê7fŒñâäåmô[ÒŸF¯Ÿó&é‡/÷§¨—c^wÙètš]°hyåÇ]‹Xº”·‰µÉ b¦²I/Ÿ¨sgô»êeq•]p÷üãiŽ,ýúºACu¾@yAû0šXY«—·×Íh0©»^ùÛ‚ßX:V ž>ÁWöº>j Ê ºØG»ÍëÕËGRÎþ°°»Ðg–KÿUƒ y–ÞõÅ7Ÿèûì‰D½]¦öE€ }ñýiG_ÓßêeºÍ÷ާ®Ï/Lü ëî†<û²}U›¯ þþ¾Ù×C}mìó:ÙGÇe]N©—OÙúk½î.,ó>´âñXc^i{oÞ}?>‘¤‚¯a×zÙGf¯ÝkõŠ<ǼVätÂc¯Aw¶M.‹óÐøõ=웸¼Ãúº¼¸ÒüÆ‹;³z² yG{÷]:ž¥‹ó ]o& ¼Q_Ìû¾–üze`Õƒ}Ò:² žÕ4MÅÒù±ñ@–¸oéI„­¦²@1ïû¨W¸™6‘ÔjÚ8(4]7M¾ ¼”Dþ¾{Ô+ŸÌ<òyV +m¶%xÞJƒ>…\0äŠ3ß 1ôz ”rô ¿žÛ]§÷‚´Q°Ùï$®O_ÕןRÎò9PÊ —_VÚÿî¤^5ç Ï_ÁL¾áÏÒAuý,‘_[ƺ5PÌûž›Ü°T¯ÑÁõkV²~åæ¡ôõ´¦ç5ú‘ë. _Àó½çËË—+u Öùøjb˜'F¿¾ÊoRÕe©n÷J=«ëˤi|Üã‚öÌä 1õêìaNß)`…÷×ü=u´®7’}Üáò”ó¿'2Q±ß¦^åäU™<ìlWlí`Œ§‡¤™´áø©,HÐÜ÷×éîêñå'Æ ×DZ°ò’3s_77øJêû0I‡CnI{aÀÁÂ9‹©Wo}öKÉ“súxÖ(h1ø:ô¿¶9A—ãI»:®ñZfÐK´Fú˜žW¯þÁ7LX¡[|a~‹ †ü k¢›î³¤“»ûÞž·õ]ðkÏþÒåÅ5ºæSzÔèWÈjËÎÎÛñ¹ÑÚKc^Š†ÞªâÕÂØÇ t³D I¯k-³`qçë…dÝöÍ3øâÜÅÆ]G4®0>‚nèö9ZP¯‘™>óOV8®uå£=ÜY:¿À©K‚4‚ŠDyiWyøIæž êµd.@Xá’KµB˼4}È’h{ý×OP^Ðñï¤^#ñÞï´Ñï]) ä—°#é¢ÐS–8”_ÐÐô™©¬™ ‹Âî×éúZùÆâÿf…'F'Ï]Ó„ ók’¾?»T®óu:n&è õóÊ×÷÷uS‹ù5€š¬Ži¿Ë0Öæ~+¼;õd‰Õgo³¨þ#ê‰yOà Ê›§sq?hb…òÞ›¾Ž´ÈßysøCÎ >7ôG3A©UÚú»¡÷§8r3ÝDÕçí¢…—Ý£Jç :ç×ñÚèýKûVÆ<6ô°]œ¯¨Å|;n†>ÎëÔl¶Úà{¹ õê :Ø&ï#û†>eÝ—˜:·óƒœx×ßêu¢ag5t°õ_¨ã’JÀYv1°·Ù®µ¿òëèñá¯Ó }%ÞAŽ -âüÜ!oñh³í¬™y3]¿þ;{Y“§Í½hëôbÚÎh´Š]ìæ3cnuð¹¼×©ëŸæ‚.6þòüîÅ|µ¸ôO³£ÇG°‹2+µšx×X_kz²ì€½ûÈ/QOÐEЏg©^7¯¼‰5Odc­.¯wð1ÆùWÚX Õ퉤VQ™?fÎ1ôFsA¤Ýx]ÜçbéyÒR£¿?Ú Ü_‡%êäþl~OÔó½îâÈà…ÇW¨×#ù%v1qí­ŸUhŸ®_þ˜%µ¨³|áü¨'æy-­úëFèóv]¬ zùxz“ yQFûRßkë¼Ä›O_n x‚>ó-í\©×“ÂwulÓ…]¤ÛôöôñÓö;u;¡¹˜÷5ZÕ¾ÝHŸ÷ëdÞý‘hÌûÁε¬rÌŒ}y£­[O#ÑjØÁbþ“…=môO¼Ó0ú'ÏAõs}¹¯®Ëq>fè×`A'«Å;õú… Èwsõ‹9YÖä”r@žïËó–$ök ~ ô³Ò™T¯Ësµ‹×/—úËÀ*gË¡%,©É Êƒ®ÔByA'‚ÈêZõW¯Ó)»ÿ;좴‹å=;Cî úH¤c¯+GÔœü&²‹ßÿp;Ñjz…{ÞtSQ?OýF}A'Ú~ë êîϧÙŇÛÜ'Ö¹dœcÅð‹+_Ó©µ]» ã&ècþv%sÌáê ±_Í.¾¬±ÿýéÆ9–8ßd‰åÚ,Ù²õ}|p³ZqÀµ¡ê 1/úüY[ÜÿÛöËXF1å†tiØÕ-=Œ§ëü……ê ¾Í­Ï[QƒUŸþ}þS–‘¨¾rJ/#÷YPžÏ»º„´´ÿ4õ†Øï5ÆKÚú¾øÊóçl< ß‡YJ¯ÃÆì1ø±ŸõÃ_F:hUY½i¾`ŠYìw¬èýcK;ï5öéX2æýÜw©<3ÆŸÓƒºÚcÂîµ9ÛÕ›ô<äÄ~V4›ÂøÈwtÚ<þ;; „Óƒºfî„ÑûÓª¨7è‚{!+šÇܲcâ~ [*î-£<Ÿwu-]˜ð­zÓkè6'ËM¬HÞëÉØÀ7VØÒ½¤øŠQ^Ì÷úÁ}îùípWoÊ{EKî¤MÝUÁÎXÎ/°¥|û ëÓ1ï‹ýõ¦¼çTD»Äfﳌä‰y~‡öÜ·×Ç9DÌóFq_R½ÉŸõ`¾’WE7xždŒnM¾jÌ—° û$DÌÿf¾½:J·WoŠ÷"~ÜxzÔª'FÄû5¶ô§›=}ò*DÌûVÁ/º¾9a5½œ4èië¡¡ùm›øÑ¶ÿ©;Æ:K؃€'æ;?6î«ÞŒ§Þ|V´k÷×ï®ZÈ2.uZQdýÒ°BÅ<§J»ã&¤æ»Y×XQZxCÛ„v,ãÂ& ê‡ëã ï+„ŠùÞ!ÎçÕ›Ù‡ [ŠÄý–q‹6€û)„Šyß‘ãJ’@½ÉÍ}7Vôéf÷[}oãDæñ‡úùÉRnÆ1ôB¨ ;¾IϬ\6N—vUƒŒûè_Šû|ú¾ïû/fâ;ý½õáÿ_ÃØÿï>ü߯BþÏùò§¿X9¶9Ò7EŒô…ö*g*î*!]©XúI2¦(ððE[t>ê‹´`ÓÙ{ù¡m:?â1BÑ/¤ýK@¤ŒŠòh+xv `ÊXŸH¡ífÒ'Æ ê6þZÉ7©`”F:˜Ò¥2¾gœð‡Jê!°CÒDlÏPKé“*Yø„ CÛaÈK±=ÉŸ*ùRo‰tËb×3RúQϱ¥Â‘޾‡ ?°äã•û…"óV×¾Õµ fouí¿š®%žŒ“cW(ý>ÅJߊ€W Mßiº®¢ŽûG±H—UŒ¹@ºKÓ[š¾Òtáü¦é¦H÷hzGÓ7š®!ÝRQŸhº„ôGÅX ¤+H/N } é’û$ïI®WŒëK²[“Ûš¼&Ù¬ÉeMŽ’R,}Ȧšñ¸©<¦išŒ±„±´@ž­)0ŽU“¤ØHáÇßôby¶A›t#©Z ˆ‹d‡ÿí¤WŒGõLé»íأ͠™h¯&Òµb…ŸVßÿ×AÝ: 2žÙô–"–©C„Œ] ´“dl"ÀqœaÒ¯*úÚÐAøÏãqKc„/UçX{ˆûÒC]W’ H»¡?îøÞŠR,!îß=Цñ;ðóÂx/#ßmÑ=W_ôÉl`” @›tføBÆ´G›ÍÒE :Si(â… ~8ê„“þkg¾µ3ÌÞÚ™ÿjv¦ƒœ×4QŸû[ðØÐЧ•@ó•@7•0¶•Hž†É8v«ÊH›£¾9èÖ<å ÿÛ«*H[žè¢PƱ3áƒy®š#}‹‚¦-Ó¤nO郰¬o y` ž±~ÖHÛ ‚ àÛ }›Ì >¸“Eœ[à[-LúàF_ìÂd ;äÙ½>KãEüéê/¤î8Çåk¸È8v©2Žê×>5Ód ;ä×BùZÀ¥¶}ÿÛ9Òÿ6Ê×A{urdü:ôµ.òë¢ïõìeì:ô­^¡v‡H·åë#¿>òë'‰¸uŽè›#ÒŽ¨ïˆ±q ”~·›p!ß{ ’¤ßmàE>Ó(¾5Wç"âÕ5gKÓÅ9NøÜvAšüô¸ ¬ ÚuÁ8¸z Û®™Òß6òÉGù¡ 1J~ÜQÞ=GúÚŽ¾Qƒ›¯&¨Û$GÄæ·¶ôî•ÇÄFÙ¦Ñ2pöDYz;牺žè“ÚõB>½O¢7CÞ(ï1¡w+Þ(õAYÀöž¾(ï‹¶|‘öCY?Ôõ^tØåýÑOŒ‰?ÒÀ…îC ?cDwócE,lñtg*cBwŒ‚JEl<ŠåÝ cÐcÔmÓ™q0pi&üÚÒbp AÝÒ‰€ê }Ú¦ ßah; ùaH‡Џ7<.Ò-Q¾Æ¥UŒðùMþ½ÃÑ~8ê‡#?¼TúÔ”~¾IF<§¿7íLM¯’NýGºTÓŸoÚœ4Éš^¬¨ ÿ™$ý§ÙŸoÆýª¨×4=¦é°ŠúKÓUoÚžu‘¦{4=£ùZ&=R1–WE½¡Ù˜šnÐtAE;“ä¾&ã5{“äº&Ó1¯ºü~ÓÞ$ù¬Éb’Á$5KcZ*}1CF* •Ji2(èÇùæ˜[sÌ­y¹ð¹lAv)Ù¢ uK̽eŒô¹Œß­AcÖh×øØbNmA¶€Y õª¡|5´eg’q€gõ0£ÿۗʘœ(_“äÚ¯Xµd¬Íd'sV·XÆ×Œ‘q4ñÝí9¢ Gàâ8 P¾Ê5ô”>ýI~ ® ʸW7KË’ü~ï ò;ÁãUŠX•£’üëÓ»UOÀ¤wzÞ oàçƒ~ù¢¬/ÊÒ=yâ[Ô @{h/õ‚?0è~Ws{ჟî0P|J: N¡h3 åÊ…?ýpô'œtÛ[ûô­}š`öÖ>ýW³O=å¼Qß!3x<Ì3Ùg&ã,ƒ.*¥Ëø0&ù•QÞõÍcdœeäW‰>î1Uo&ã,˸~ȯŠyªŠ¾ZÚË8Ë€m‰ºVa¾ò­o ~·OX'É8Ë(oƒöm€Ÿ ÆÒù¶à Û#ÆLÆYFýjhÛßíRd<@äUGÝê 2. ÒöHÛÇËø€å2Îr¬ˆH"¨&òk"¿fºŒƒüZ¨_ ¸ÔvÁý®"ãÃXŠøz|e4P¸×M‘±•Q¾`×nõPßå¢ELAŠSùõ¯~²ŒCúiò¡îˆ¶06N¨ï\œ^ÈxÊ€M>ŽBn5Œ•ñ”1ÇHïFÀÃÙ^Æ„AÚ™ô Ò.À‹|+º qé Øä“üä¹!ß °Ý0æn(ïn)cÁ¶;Ê7ìÆ˜£Æ˜ïÆh«I˜Œƒ>ÿX÷¥DÆ}Aš‚VšgO”õD¾'êz"í¼éí¾Ò^€å Øô¦Û;MˆgŒ½¥õ)¢Úå}Ñ–/Ú¢·€ô>Ïxù‘½ xþÀÛi¤éPÚ @:¸¢n ð.AècÊaL‚Hï•Ëx4Èo†ºÍ1öÍ#E|GŠÕŒt0ÊcŒ‚s„Jh!âçPLšàX!¿À¢{Dt—‡Ç¢A¿Â; °Ã+‘ñ›‘ßé–(ß*LÄ›ä±h/ø…#ÜÂËEì ·´ÑšdÉVú«h›jq5ýIº³¢Î$]©éHÒ‡š¬¸útž¦ç4;ôM½Fú¬¢.ÓâzhúŠt”¦Ÿ4ÝTq?ôŸé!ÒAõOÅýЊz¦b ÚŠû¢oêM§¼©O*Æ£}Sg¼©'4Ý éM'TÔoÊMöÿgä~Ey¯Ù¾šl'™^Qžk2œla“Œç~­/ͯX·« h¹ øÃÂ$buUEÚÒAÄæ²=Y'˸ª 2~}ŒSµh´Ú ¯¾:h¾:ÆØ>BÆSE»5âdL-”¯‰ÿkÊ8ô%2^à×ÔÅØÖ,{Oùõ=EŠA±ãË)S*{…: À? ÃD ÕF€M¾¥ËØð¨ë-ãÀ£MWŒ±ê¹áww´ã޾7ÅX‘?Oü扲^H{Óß}Ð7àì <|Q—ÞGûy ¶ñG{þh/izwxz;ä c\!Ý eè½Åd¡ûïÁ¨ œƒÉÆü‘"Öžt§“â¾PÜxŠûN±hyì*|oùBĬ üpàž)ãSýMa¾£Ï\ç›™½µÏÉ>ÿWµÍÿ+Úåomòÿó6y„œêxÇÈL±¤ìe¼F¤+‘ ž­Œte”¯Œ¾VFysð‰9èÅi2V#Ê×ÃÕ^õï€|Ôw …üúÃúH×G?ëž#Ê;ÆÊ8h›bò87ŠMÓx7@[ P·tLÃ8£ßÇFŠ1àŒ6œ#d|FÀtAš|²» ¬ Ò®–26#ú䊱sC¾àº!í†|w¤ÝQžüÜ’¯ÙÆ€Ý84Δ1Q· ð%ÿ“ÀßyÈóMSÓ!bÉO›'ò<3…¸õ/ä{‘ÞCYoÔõFÚù>GôÛ‡lràà Éߊ/ÚñÅ8ù¡¬òý€“pöG¾?p&Ÿþ(€üÀ @ýÀD[ôî9¸V‚0ßôž5ˆìò2$ÒÍP·¹§ˆÙIq%›£-z‡Œt0Æ#ù-€[‹hãŒÞˆ„vÆ6°C^ˆ8î¡q2$ÚC~ú–.âºó˜Èo™)TF+“ˆ×Ù ¸¶üp´Žòá˜ãð"ö; ‰¾µ&¾%ùJš.}s˜þ?Ú#&YÑ&¯¨5}H“óæMבnÓìñÿÈ×t×?»Ÿ é§7íò7ï)þùGºçM›üMýRÑÓ¯h‡“þ ½AºâŸÙà¤Þ´Á+î-ÿ£» $×ßÜ_ÖdxEùý¦½­Éj’ÓoÊèHaJQ,]ŠïW) dz¬ŒþUΑ1sñ[ðC•ÈZ”³]Y€nª"m Z²LÀe¬PÆ ¼kU*ã⦂„о-ƼÊV}ÚYŠx·ÕSdL[>h§¦ƒˆa[ éZ)2v-è¥6êÖŒ:¨S‡d^¬ˆS[yõ€›hÎeêƒ'(nž#~wD;NhÇ ð //ªè¾!ê5|!cÍ¢JEœAgÔs\WôÍãH~ëÉg¼ðpÇïî(G>§ÉÄk‚2MÐ/´çXMÑ^S’Ahü!z¡mo¤É7šúäÜ|1&ä É}òCÚ8‘ò/8€C¾ …IØ ¼OoŠyYÔ &¹€ò-€½Í I1yyÌXÔ Ãoa˜ƒ0|o‰ßZ¢íVÀ§`„cÂ1­eXŒaërIô÷¿jGÿw³¡ÿ;íqÿïÚÑÿÝ÷¶ÿ•mèh9æÀ]?òxñ˜#¸TrÁ'RÆ<î•Ás•#eÌsT1˜c®Í‘WüZ4U%UÆ<, À²ÀxZ”Ë8®±2æ9ð´Y®%êZ¡®àZ–ò­PÞùÖ1ø`¾¬ËeÜs”±®6è‡-ø×th‹|Ûb¶Zl…˜çøn—&cÄ"]¸TO•±b‘¶GÚíÛ£| àZüXí×(”qÏÑVMä×Dº–¥Œ{Žúµ k£ßµÑvmÀ¯ \ê í:h¯p¯ƒt]ï´R¸×Cùz€]ézÈwî¤[PÞùõMøŽüúÈwDyG¤)ž§puBÛ[’Ç:G[ ÐVƒt!î(¦]CÀj˜)D_£ß88£ÎH;&ÅPr‰1Îy|sÀuŸ"Ÿâµ¸&Å¡?ÃeÝÑ?÷rÓíS<€&–2žyšˆeN>Ðy ó,!V›†ÉøåÀÕmx¢.ùöÄ<{¡.ù´õ¾äËÕ céòä_ÔùäÓy>À‘|1ú¢¾/úà‹¶|QÖeýæÜcá¼ý‘ö.ä¿*ùéB|¢í@Ìa Êç |'ß,Í€G3ä5-5C½æ(Ü´Öpš£`Ìù ÷ýôƾÚivZ Ýズ!·B¡ è]k(úš%ÔBòÃÐVX¦P<¶:ò[¢­Ð§V"Æ=¥ŽöÃÑ~8òà …iò­1þ­‘ßøp@šžüG÷w5§é°Šúª¢½[q¹¢ K²ÿM»U“ïíÕd§j²úMù¬íÿÏlÕf§jw *Ú«$;+ÊÊŠöéd›jòóZ u+c¬ª`üª`œ-ð›E¦Œ3±·$ƒ~Ø`®«‘m‰y³#ûmÔ=Ö8`Î0g˜GÌ¿#Ê8o.Å]lk„y¦˜}ÏͰ(.”+æÛ ôåŽùwGÝÆdï&(D”ñ@ò'ßôÓíy¦'ð%_Ì^hÿ{#íäoÕtí›.§mû“ý8DûÀ!ðÉßTÊ¡~3´Õ }kFvúÑ8„  ½i%šE0¤[âÿ–H·D[­€C+”'ºÌfÎ[»Èì¿ÞãUûè_}Ÿ‘îìPœúÇfüìßìW|~Ãç >OñùŸgø<7ãûfàóŸ?ñy…Ï_ø¼Æço¹Æ? ÖD ÖD )k"k"¥ª´Á°&R ëÈzk"¥>X)Õå[g¬‹¬‹èz@ÁºHÁºH©'ßgA'(X)X)Ð Æâ,ß$`]¤`]¤`]¤`]¤`]¤`]¤4•÷n±.R°.R°.R°.R \è%@Þ'úHÿ+àü¯€ÿð¿*ïE€ÿð¿þWÀÿ ø_ÿ+ª<ÿ+àü¯€ÿð¿þWºÈ=\ð¿þWÀÿ ø_ÿ+ॗØGPÀÿ ø_ÿ+àü¯€ÿ•þÒ~ÿ+àü¯€ÿð¿þWÀÿü8ø_ÿ+àü¯€ÿð¿þçoÛÀÿ ø_ÿ+àü¯€ÿð?¿“ þWÀÿ ø_ÿ+àü¯€ÿùð¿þWÀÿ ø_ÿ+àüÏ÷§Áÿ ø_ÿ+àü¯€ÿð?·½Áÿ ø_ÿ+àü¯€ÿð?ãþWÀÿ ø_ÿ+àü¯€ÿù»<ð¿þWÀÿ ø_ÿ+àüÏߣ€ÿð¿þWÀÿ ø_ÿ+à~ßü¯€ÿð¿þWÀÿ ø_ÿó»„àü¯€ÿð¿þWÀÿ øŸîÃ(àü¯€ÿð¿þWÀÿ øŸÎ^ð¿þWÀÿ ø_ÿ+àüO{÷ ø_ÿ+àü¯€ÿð¿þ§u‰þWÀÿ ø_ÿ+àü¯€ÿéý¿þWÀÿ ø_ÿ+àü¯€ÿéÍ¢þWÀÿ ø_ÿ+àü¯€ÿé­ŽþWÀÿ ø_ÿ+àü¯€ÿé.ºBüO¤ëHßÄK¹›)ådçCâ¢çX9¯i¢ü¾‹¼3#Ï(RÅÞCi&ì~‡(VžY¤‰ý2þ¦ÒLÞß4É;œñòÞ{¦xcÉï9È;ð‘Ò·Aм _,ïYÊ7HÒþ‰gÞ|ï-Gî¿™Éu›I®Ý’åú­PÞ ”o–âåÍLéÁRúE”ûu1òl$EúGp‘>¢åúùÞ)NÚ]™âŒ„ûJpo8ÓÄÝ%þ†Ó^¾ãŒk>þ&*ZúMH–ï9‹å]ûtq‰ß³w‘gòqâ½Ë™ ïÙçÈwSöâ¾=Ëi’ï9“„ÍGwVù¾a–Ø3äo:]äÖ8q•ß] ÷Wùš/EúN(•o8=+øLHwìiÍÇ÷¥¿„8ùŽ3]¬ùø={{é?!B®ÿ’ä9J¡<û7“ï³Âä­8ùÞ3MøWàg,òœ%ZÞ{Jo¸ø™‹™\3šä›N{ù®3R®SäûÎby†â ï4ÅÈ{Miò~@„¼# ÞxÑ]¦%òÜ$Yì_Ò[N¾ös‘¾bä0Uìkò·fr3LÞoJwœ²ÄÚßÃw‘g,1òÎnªxûÉÏ[,ÅÝþ4Fú\H•wôKäyÇ7F¾ Mwöù¾©™¼!Ïe’äÙL޼»`)ïò›ä»Ñé§!MÜi 5'Gj/¿…8yö’*îéÓº›Ï÷Yä^k´8á¾2…¿~—ÁAÞŠ–÷’åݧByîb/öaÛ ½6€Óxµ^mÐVàÔx·ÅÿmK„)®¢œ x*à©€gB9òM%ÂDo‡üv€ÓpÚ£_íÑ÷ö%ÂlÈë€¼Ž¨×õ:–S¾ò:!¯àvF_;£ngäwF?:— 3¿ pD¿ÐVúüˆRaþ¿ƒr]1Æ]Ñç®èSWÌCWÀë <»Ñp»¡Ýn€Û­T,º£Ýîh·;pê¸Ý·;àvÇøt¼žøÞmôÄ÷^h¿àöÜ^À#8F¢‘€Õy½A;½‘×°ú _}0¾QÀ% ã<¢0?Qÿ¾h¿o‰X‚ôC~?ä÷ÏôŽï¢NüÖ¿õGþ¨Óuúc΢Kt±Xª Þ€÷à1ýˆúQv pX,–1ƒ€ë ÀŒ> žƒçà9õ† ­!€;0‡—aø}Ú†òÃ0Ã'p†c<‡ã÷áø}8~Žß‡ÖŒÏà:¸ŽDþHôy$òG"àŽÜQ¨? eF×Ñ—ÑÀo êÁocñÛXà<uÆ¢ocQvð8ãПq¨?xGûãÆlæ`"êM¬‰€5mMD½ÉHOFz2Ò“‘ž‚:Sk pŽ+K¯©˜›©HOì©ho*`OÓñûtà1õ§£þtÔßR¨çxüòñÀ%mÏDÛ3É–$žþÞÞ÷y»&»&»&»&»&»&ÿ—\“+Äÿ¤«AÏ\§$HÙš%d çsâ…HáˆÓM²¿Ry'ÜSÞŒ•çžiâì“¿9·”¾#äÛóä ïÏ-ÅzÃßù$ˆ·•t—œî·p¿fÒ× IÞ5Lç¥Y¿{n/ߪGJ„ òbºx»ÎÏR-¥OÂqfAï5¹oÂbùV(L¾gOoÚ³ ÿItß¿Š”oÜÄ;"îKÉSúSŠ‘ï=Så{÷xé»0KÞ·1“÷MÒ‡a¡|óî ý”DŠsþî=FúXJ‘ïßKä£Ly—ÑLú/‰”oŒÒäÛ÷$ùf´P¾wïŒøÛ÷éÛ$Yø5¤wFü\6GžÇšI_KÒßRŠ|Wd’o‹’äû¢áÓ¿y¬à_)U¼-"†üü5LúVŠ—~QŠÅÛ"ºkH~–èÌ”ßÃoSé=Ï’Ÿ@ò7H>ŒÈ½1ççµÉò®P¡¼/ä"ï ÅÈ{û©âÍ<¿?d)|!’OCîSÅAúU‰–gH©ÒÇJ‰¼?ä"ïîÇŠûûäãß!Š”w“ä;øás‰ŸÅ¦ÈóØéÏÐSúYŠ•çMiâ|–ûh±”ç´&qןû:L“oãKåû#Oy')V¾CJoåùý${y2RÞ‡L’ç¹9ÒG“¥ô‰!Ïv“äùnŽ<ãµ”o"åYo²8亮õüÞ¤½|§!ßÙ'IŸNéâ.%…q2ò=R„ôÍ/î=qˆYò.d‰<Ïuþ™z&‹{PÜ/S–0§MIÒ¢I¼ç÷ SÄ[îíD¦Š3Ý6h¯ Úk¼Ú ^´Õ8µÞmñ[ôOEÿT”SO<ðL(gB¾ ùíßùí§à´G¿Ú£ïí‘×y×y×õ:¢^GäuB^'äuB^'À팾tFÝÎÈвCAÑß.Àw(ÚŒ@z(ú:xFÿ¡(?õ£lúƒ>Fþ»€Ñ—ÖÕ(Óén¨Û ÿ}vìîh·;ÚíŽv»£nw”‹E;Ý£;­ÅQ¿'¾÷Äo=1/½Ð~/ŒU/Ìo/à‰üHô/cÔù½A;½‘×uû”ŠåFÆ? ãü¢€spë‹¶û"¿òú!¯ðéÜbRÅ’¤?~ïßû£NÔé:ýiM\¢Ëà=xÞ€Çô ` DÙÀi Ê B™AÀu àžƒçôaê ÌQ4¶Àc` ÃïÃP60†F ` ½q(;yÃ1ÞÑ7œÖäÅb94¸ŽD™‘€5ù#‘? xŽ*ˤÑÀsàŒÆ¸Œì1ÅbÙ4¿ÎcQg,ú6åÇßq€3ýã€ÛxÔø0f0ÆQo"`M¬‰hk"êMFz2Ò“‘žŒôÔ™XS¨?ho*Æb*æf*ÒS{*Ú›Z*–bÓñûtà1õ§£þtÔõãQ'¿Ç£|Æ‹ÛöšO6»fŸ“½M¶5ÙÕÿ7íé7méŠv´fCƒvuûY³5»9¯‚½\PÁN¾\Á>¾.íbÍ&.‘¶ðÒ.•¶ïCió–‹»cÜ[„x«M~&é]6÷'™"üýÐ}òùC>5èM½ƒà¾™“ÄÛºsLïé ù× ûuô¾|!ÓgºCM,F¾«Éw2—b)îÂÐ]:ò£Ao²ùÛ‰,á?™ü>’oGòËC÷•é=!Ý_áþÍÄfòeÍý(»?Ñt¿Žûà ~©éÍ#ù¦£wÕôÆ„îÈ‘Kò£IþÞÈù1¢wäC’îàÐ{@zÛ@¾~è]c;Ôi˜í]ä6éJŒ•ÉE¾u@Lžâ^V[¤Ûb\Úâ» cÒãÐ}nƒßÐêšP¦'~ï }P¶ÒÝP¶ÊõA_#Qw0Ú |úÎ"}‰¶bPv(ÊôEº/Æ,í¾‹O •¬1È›€zcPn¥ñ{,ÉaŒÅÀŸ„1œ„>O@^ÒqÀ!m€1øýLðÑþ$ô?}Œú“ðûà< øÇblb0ÞPgÊM@ù8|&¡þ”†r“PoÊ̾“P~ÊÆ% Ѽ§¡Ü ððúŽßÇÎ “g#€ÛdÌÕ4Ê£ßç4ÀŠC_'^ÆbáŒr3Ð×i”^3?ôsÚš|¦aÎfP;€3}ŸA°c`MCfP}ŒÅ ªCeé7ôapŒxô¦Iðÿ{»Ÿûv?÷í~îÛýÜ·û¹o÷sÿÕösÉöÔömÿس%}K:‰ôB’”9BNp^%~Šþ5ùܧȱ)—ï¼å;•8ó%]Üñæ¾îì¥OúHá÷ŽÇ€É”oXì…ßeþšrÄ›DòëÎ}ÖLj»âܧhš0%È'÷™-üƒï#ºGNqSè 8ùr"ÿyäωü6qßö‘2–Lªôq_"}Œ˜„¿=z»Bïbì\Ä»nzBoé~9ùç$Ÿxd‚hþþ$Uú&•þŸÒ¤ßèáŸü?ñ÷2–òÍL„ô…_,ü÷‘O¿ÿÁÞyÀGUµù?€ðF 0b!„XÀBIî„ôÞÃ$!dÒ{/¥¢A Ho°¡¢;VzGE±‚Ad¿çž33YVßýïîgÿûî¾äó™ÏÜ{ÎÓÎóœ{Zæ>?#W©YåÃ7©wgÔûçÛ³ÊWzL½î¥Þ¡iy¡Œ|PM2'”À¡1rý¹ËwÑ|P±*gi³Ì/òžˆe•='”—Ì;mäBiï£ª÷ÏWË|¥â‘_ä4ÞÉ4«\¥«eŽ‘_üßxïÜ¢Þ›i‘ùRŒ¼øNêõ.z½ÊµEæ4ÞKwV¹LCU>Óz•3j‹Ê}í¤rG…ªü¦snÍvõî‹Ê©ªò–º«Ü¥…I³Zå0ýZ½Oã©ÞI¯Pï¥oQïјUþ_¼S#ò—ï´È÷Œ|PÎê]ôXõ>z“ÌÇ"Þ0ò—"߯Yå1-P9ó­2_”‘7ßY匊•ù·Ü-V™ÑxïÆM½{c–ï—¹N­ò=v#ß©‹Ê­«Þ?hRx6;Õ».êýv³Â¶i–ø6"§¢ñnª›|O!ûBù£]að†Q O6Âc‚×ßù!Û™~[d~R#¿éjùþ‘GÊYå^ UùN稧ÛU>}g•S¿@åelP9N·©|úN*—1óÅn_lò¥]¾´ËWèÆ6_ìöãÛöiØ¢A§¡O+H’Ø ‰zõþÔûSïäŒÅî±”PMuÔP_ eÔQD]2ð+$RŸŒÑ\‡ÐŽdñ­¡´1$ÓŽPÚ õaØ™Ly84áÄ0Ýá´9œ>L{#ÌtFÀ‰ÎHtFbO$öD"7þHô§†ªmÂv¹MO} r“Å=qˆAf,òc‰[,zãˆkœÐ‰ì8h“)OÀ¦|Ÿ€¬Ú•ŒÜlN„&ýÉÄ/ š$h’°3™²qÜ㾘ûqðƒo®Àì1ò_4Èa&˜±*ØMâŒø:Ë|œ&xLÐúѦ`Ú½‰ú±bŽG‰û±Ø(>‚¦^æÄ Å¡ð†ÑÎPtF #BÜ‹y9Ȉs)r"ÐÍu4×ñȈåo±+QÐàÇxÚOÅú@ÌiÈÌdhÌ”™Ñ3ùã‘;ž¸¥r]á,‡¸bÚ•'æ Ú–C}µ˜Ó(+æºØSO5vVs]Mù䥊¹lNƒ~“º,hrhC2³Ä‰ÍÙè/ÃÖlê‹)¯æ“ƒ¾ >yÈÏ¥-yðs_]>vç#³ ÙùÐжAW(ÚŠ}…‚Ûʰ·H¬1ˆO }i¢˜c‘U‚žl,㺌ë2q­ÕôË ôLDV}¨Rø+iK%òjÄúà ú|5ðÕÀWžzhêÑ[®Ièš„}—‡ìtùܺÁéò¹õåsëËçÖ—Ï­ÿwž[ÿ#¬¯ÿQδy–ŒùHÌ MjlÜ)Çã9Ï’èï÷Õþ³*OžÂ0¯Wت[$–¹-à¦ð¯Ì c Eå4=¨°ÜTn¦X•ß´Iæ·Xç6–‹Ä 40 Uæ=ùŠ ¼,7…IP!1±.L§m2÷^§c ÝMášfëj‰§e`¤»+ìÖ™OÅÀ.Ø.ñ |Ož*gªYaÌ‘¹S ¬-/…gP¡rn[®Á‰‡(ro‹Ü,—¢٦°ÔžºIå¬Wøs$Ž”‘j§Â{u‘5®º‹Âª±¨¼P[TÎîf™·»Û1…uà)sCù»Í ˦Eâ,ùUe^U[ÝEaq™×j•W0Væ4°^­ çÀIa˜ÚàXUNU'…¥ª° æH‘SÕÀht–yg ,.‹Âkl‘د÷ÀÈMè"ñr Ø…»Þ"sÕX]Î*ox¬ÂÔi’ø°ÁÀcwSø]± ëÀSbìù çHœu÷à”Êgã¥rÖË|†·lSym,2Ÿ¡ùºE渘žúj‰©näVuQ¹¡Ì*ßa³Ì³*0Ö œ…µ+s ø\[$¤‘ÕEå!7+¬®f‰Án`"8)\“Ì'5¶Yá#l‘y¥ Œ7…ãeVøìÍ7Väj5pÚÝdnű- ß«EâÈÞ‰/ƒTþ)/‰ßî×¢0Z$®‚ÈÃhà¹;)Ì./•ƒ§@æ37rKYe~VÅnäduQø± #¡Iá"ìTØ]. ¿+Tæf5pØWËÜŒv—³ÊËJ½/vûb—/íòE—/6ù”Ëp?ñMû4ì2¡#”¶iè4Ag‚ÎD½‰zêý‘ãáCÚ>–º±ÔP@]uÔÂH] uAÔQ„Ì ì Æ'ÁÔÓŽ`ì †&;ø…7TØ€/B©Å·¡Ô‡¹È-@¸ø†6œ8†#/ÿ…ã¿ñÁÞäF 7žHôF¢7›"‘‰ÜHäFâŸHtEáŸhtDsÃu rcCûcKûbÉíE}'Žº8dÅ!+žø$`KþOÀŽÚ•€ÿÄ>ý‰èO¢>‰ú$üŸ„IÈGÙ8ÊÆÁ3žqðŒƒg6™±ÅŒÜl®FOŠøÀ“‚¿RáOÅþTlJ…&¶¥!7 ãiÓøƒr[“NÒáK‡/¹;[2(Ï€>ú äd Ç‚œLÚ•Iy&ò3)ÏÄ™ÈÊ¢ÝÙbï _6õÙðeSŸM}öä 7¿åÒž\ìÍÅg¹ÔçQ–OY>eùÈÌ'&ùÐæ£«9…ø®þBÊ ±«ýEø¢%è+¯Y%È*ÁÆøÊ¸/㾌û2îËá)GV96Uˆý7¾¨$6•ÜW"»Ù•È®ÁŽÊk°£þøk௅¿žzÊ롯Çï“Ð= Ý“(ÓÄšKìgÅ_Û³n±{ðË8"ÿ|8"¶=_Û}Þ¥ûºKÏÙÅ>NìáÚîßl{7ú¦±g³í×l{µ¶gïîNÿvOö÷ÎážKì³ÄÞJì©Ä>Jì¡l{§K÷Mb¯ôg{$Û~Èöûl±ÿ¹tïCÿùWûÛ~Gìs¦ªõ­m/cÛ«Øö)bryoòÿmo"0jöl÷•ëºBbÕˆ<§F¾RüÔ“¶º¢¯ƒEbr ¬„^ V`EŠ| "Ç«tý,2ÿg¯‰_&òˆŠ\û"w·À5g(5ðsæ·s³ÄdC§À¾ù¯¯lRù^·ÈÜ®¯Uä íLæëø¬{Fà` lkà€Ð®ð¸¸)l2tº «[lh\y†Y%™ÀŠy8E®V#òµŽ„wd¨Ä@50qI¬F1|ùX·ÈœßާŽÇ"w,²Æ"c,òýÏÊØ>º—Ì‘(råøb.2_öh“Äâ®´1úØÊ3ì%1mƒ}d^U4ÁÐÇJÌÊ`³ÄØ |j2Kü×cë%Vm0úÆBÈgì™C5 ùwP!>Ø}<¼èŒæ;y¡è㹉ÀždʶȪñÈ Ã‡Ñ‰ ] ðM]|“ÄÀŒFW|‹ÄHĆxø)AßH¶H‡‡XŠ•X]Éâmr½Äì2#Ë-ðùâk ÷ã±!•O oÞõ 'ÚQÈñF¿÷6‰?/°3Ž7í÷sQ¸¼â›6CÎ0/‰ÓpJâk†ÌQx@õ »·Yb¼¯…·‰n _†P6Â,q|ö¦?ºµí +{GºI_ë`›Äí¸AF>yì Mx“ ³Êüòc¶Kü^–Oß~Øê‡~ÄÞýЧ!+ú è5h4tšÄš \ûSçO?tþÈóG¯?t´=yÈ D^ ò‘H ƒ  ‚6Ú á{äD‰½rC•„=!”ÇPB](åaø.†²0±‡BNôab†ïŠ,rIŽ-áð‡#;Úð³rù•tQø8 _FA]tQø(î”\šEcs46Gcs46ÇrË},÷±â%±èŠCn<2ã©Çñø>žXÅcc"e‰ð$“O"<Ù´7¾$lJ‚7Þdø’áK†/:3 efÚ—FœÌ´ÏLûRD|°/¾TøR‘Ÿ*ÖýÔ¥¢#x¦á¯\>¥ð§á³´SŽ#Ê ÐçÒÎlÊ'@?}°Ó‚ ×qÎRl¶ 3‹²,ʲ(Ëû2q®,ágì*çÊÆ–lÉA_rr ÉE~î)¹Í£¼–6çQ—G]ò W€¼â_ ]r ˆGñ("EÄ£Hì…¨«j‘ËÖRøJ…mÂl(ņRxJá)øêÐQ%®á-¼ø£Šò*üQ… UØUuJ.u«ñE>¨Ã×uÄ¥Ž˜ÔaO¶Ô!·ë;ñgÛÓ\ú›k±ù«ß\‹½JÛ3é¶ûÛ¾DìI.Í‘aÛsˆ=†í<ú¯Î¢m{ˆ¿—ã2ŽÞG¯íïºÿêܹ홳XÿþÙÚW¬ymkݶ¿j{æÜvm+Ö³b-+Ö¨m×§bm*Ö¥£ÔúѶn´­SÛ¬/=C¶­/]ûÙÖ|“þd'Öw-rÍåÁ3~Ëv‰Y} >v¥ÝrPbf÷æºÏXw7Ù½»Kœ¢ÁØßÛ·Ä_êÍ3ÛËYácÏ‘8}¯Hà% ¬MÅÒ9½Ñs ú<Ý%ioâ4Ø"1\Ó–ÁŸÔÀj’˜@á~LbG·'º}šåc6˜oO‰ÿ#ð3>ŒAŸÏv‰™9Ø*1ŠF8K<½QØ7yƒ¿–¸ŸZ³Ä/ò§|6ˆoè†`GúƒDY¾ ñ’8/!ŒUAØ"ÖÔ¡>HÐ"KC1ï¡;жˆ9š:ì¢!ðÄÀÓ,ñ´Ã<%ŽvrBøN aèJó0¶'A›„üqbÞ¥>¹ãhC zÆ’G)ð¤!7 iЧÀ›n•CE¼éÈÉ ,á”FÂLÜ#/91ÔEAÃ}8öÄP–$æMü—‰ŸrÑ™ _>þŠ<\§Q–]îj9ôˆ!§€x—@“‹ÞÚÃuQ<ÂH6]ŒÙøº :31«ã9>¯¥­µØS‹_k‰šZÚ’‹½µ´§ûk±«š*A‡m¥ð‰rl¯…®œ6—cSʪˆ]üµè¬¢¼–6TQW‹=uÈ®#&uèõÏ4õ&¹/1þþ£ç\ÿ×θþ/ýVø?{Îõý7ÂÿLg\—ü;ãúß~¾õßy¶õ_ù-°8ÛâY¼r»Ä¯¿Š¾ÙÙYáØëÎ>Ͼ+ýÂÍYb0‹!ÿjlîÊEl낎.«%.³ÀÐëÂóÕY]áí_×P…k»ÂïMoñ ÏŠk…Âp¦ïvsW˜öÈuk4x]‘íºEâqöq‘øÎÝ)ïŽÌî‰í,°áöcžÝ§äôÖÓEbtº…J,f7³Â´gÜuC¾[³Ä·ï]/w…ùl’8šž´­—YNîÔ»sïŽ~wd¹›%γ;rÜ‘Ó Ü[æ&vºo—øõîÈq?+±×}¨÷ÀFäxø(\{äx Ç9´ÙcŽÂé´Ê©Ñc›Ä½÷ M´©IbHx‚Ä¢òúa`?döÇÆþÜkM ëÚþøÇž ßùžb]ºZbÝñRøòÐ ±(¼h±î¤~ˆ¨ß.qç ìL슎¡î G¾¡± Oÿ£péáŠýCá ÿPø‡SøÓN³~ ´a|{#Ç9Þ± ‡³AaÙ7KÌBodxo—ØöQ‚;¼i{þæ,ñ:£ào˜(ãzíÓ¤p<±k˜¨ã>N”!srFx),Ob0ÝaðÆ‰µ16&]¡ Ï“öŽ„~$ô£°e¶Œ:(± Gf8v„‹58¼áÔEàûüEŸŠ‚. º(úw”ØW›…>h£ðGr£¡FO4¶Ecs,÷±ÜÇr+î1&–6ÄÈeZ"õYØŽx|câ±1žDxáI„'Ù‰ð%жŸ’Kºdø’᳈ox“¡K†×Ì·Y|ÓÏÌ´1 ?š䲯y©ÈN…/ ùµ¢^ì[ðcmIÃoi´% }ã±m‚ø@?ú ÐOÀ¾,tM{¾-Ü[Ä5ºjñ‰yY‚†²,ʲÄ=º«°)¹¥ÔeŸ’KËìÎAnrr(Ï¥>½yèÌ£.º<êò¨+@^ò ˆCÏTq( Eè,"EØ^DŠQ„Œbd”ÂS O)6”bC)<¥ð”ÂS.>ð•‹} ¼åð–Ã[oeUbß"ö,ÜW#«ZìWð]í¨#uÄ¡[ê°¥¹uÈ­“…XŠ?ÛþäÏò¹Úö¶½CÛ}BÛó§¶¿yl{¦$ÖÜ—ž#ÙÖÕmÏþìÜȶF¾t]lûíâ¿wvô÷Îlïø·=?kÖ¶kÔ¶çEuVd[g¶]WþÕ™í}ÿ¶kŶ¿Aü³3¡¿:ë>ÛoÛ®é|œ¿;4©5Û¥k5Ûïcþõ¹ÑŸýÞ°íYQÛs"Û:©Í¹PW7‰ÁíJ?vs“˜·=ðqÚ;ؤ0{}/t½·I|ÝÞø|0ýs0ý¾4>b„Þhüàc–ø¹ƒ-þ|Jn] <]dзýÅ<‰œúJ2BЂœÚ9†òì ¡Ma‚Ù!´)€ñ$áðŽâ…¼ô÷ôhbìĦúc vðœÅ ;€g-ú1‡¡/º8ê•€qbžàÞÚ$êS¸¤qÔƒ7½A\§ómF§™ºh’à1ó¼Å`C8¼éÈʆ.[’(Ë¥>ƒë ìHÇÆ aßEø'Žòä$‰o7ùèÅA— Ÿ™6¤ +]ðQ_ ]í¯‚.R(Ëâº?Õâ·RlªEf1)¢l*@Fßµø±JŒÇø²Š²l1þÀ“‹üZhkio-¶ñ]J}þ¯BV9¾*Çþ*Á‡ÜZtV5É!¡}Þ¢QV*ç+ñÿ-qž%òÒˆÿÿ¿ãw’yŒ÷šeŽo½%rs‹µ„ˆø-Uz‹ÄÁgQbË/r‡‹yBäIØ_â½uñ;~ñ¼ø ¾ñ~y…ú}zƒüº‘C×,sªþWÿÆ.1øÍ]\éÆô™ÒþÞ›ý.ÖºD%ÝT¨½ÿbõÀŸ;îÒ®þÙÉj›ŽÞ·¨ß­~{Ï>;çxÁéK%éÖº'^û08\ Õø2øt²>)vèÍ£¦¬Õ†‡š>­|²=&ƒîá®y+bïi§uµé9×±ûè_h›ï}¨Äü}«n-/ Ì÷¹I»qßoK|¬ú¤þþO{>w~‹ÁÿÈæ½{ÛGxk\·þæÚål¾óÔœ‡ž?¨[«œÓÜçi·Ï. X;i«>é¸S÷³ÏÎ…¿Áàtø?潦¹¦ôÇ úE¯Ï»b¥Xûв7yЕ5«õþ]_Œš¡[kVU¾SqV¿û‡°É'¶›Nœ“þ_Ü!mŸë·%Ú‡O”¬èßÁ7ë„§¯}¥Ï—~ïÜV]éòy¢n:¹Ó§‹Ð<úþÈ‹û»ê“}¬áÏuÚ‚Ù–xíé´lS…öáþ‰“¾xÒ·fíÑ+ ¾ß>¿m6ÿ,¼vÏ ‹ómq‚_Æ÷ñ^'†=dÑvt9xç îZëJó/ÝãÐ/Äj´õød\ÿ¾Þ"mGTQÏ›cê´VÏ•Ý&º¿£[wèo%^½ÝÞ&ÿ\¶~vÇød<Ÿôúê¢yT¢¶ƒ(=þ¸·¶q±Ïþ«²½qÜ=ᣎ·Ó§NÊ÷DÍTødüZÜßX¼â8|û³&¾1~«½ßoø~ÑÊA{âuë gU-öÔ§­™zííI?À'ãØò̧7m]Sh§ßÙ-ý—¤ñ Ú÷ù=Ú]ßÑïØ%þ±ùyZÒbß¿m~’ñ]ZøÚèIñ»µ+”wúŒÏÿäømöûõ4Lœ»Üö¼éÖ»‡ÝÖès¥ÝSŽÞ,Zd:q^Æéymåǿߥíï‹'MÚ:÷š÷¯:Ãó`¥¥+õÉ3z¾Õ£Ï*èeœ—eN¾áp\/mgÃãaq3Öjk]¯é=¥úóO¾~ô¨‚MÁ'ã»ìTjÅü“ìÏÝÎ¥Ÿ¼à³8^ë®ì^sƒeã†cºu¦kÙ£÷Ç:úYéÍ.÷i~È‘ñ~jáΦçi;_¯ëüó9öçqu€oIÕõ}üÙúËC5[‹¦žsÄý¼ŒûSºtªßÖQÛyüÁO¨~U[µàÞÈ›·6:üõ¤iÙ=S¿Ð§¬^µÀ'ã¾|Ò[“fj»:îûdÿÃï¯ÓtkÓÀc]n¨ÔÆn˜¾áÄÜ:}ZÏM7–ÞÜMë-Û‹ÙV çYWi×Éöi»ú.[2ñ±bmå¼Ï~Ý4ù ‡êyóÊŠÚ}zÍïzÃØƒb$pŒ‹çeXÙþçyk»´[¯z)ð meÿòýqÖ~¨Q»åZ½aÝ3ѱož4ø]Æ}Õ5w¸n˜¾Ñîÿ])s?éVõƒ½®xùׯGuÓ­Óú¾ÿgëõëwoþãMøe?Xµ,ôá7B^×v‰§ýöµÚŠà«VFÞ9S·ÎmŸ˜© m—ÓxÍu½ô†Å¿½uÛ ãµ^r\_ö‡Õý:×w>ªíš;oÐ’÷¦hËß ¼êôûºuúkÇý.^Œ÷jGm×Êy×/ïqF[Ô÷—iQ¯èÖ¼15ç[ô†ÉÏ}Kƒ^Æwõo›çîYdoÇ®·v}êüúûxôÔÓnËZëxÞ"7ŒÛ:­^óQãuƒ›¸è¢õ‘ý ¹2þk*~}á˜åm×wçþÿ öTßa ulãç‘ë~ò©t´{šèFŸæˆ×ï2þk;/=³éû~ÚîëÜä·ÏS[¶`Ês_t8¯[ØzË×kC¤^}ÚÕÞ¯®^?>çµ÷¾ùª¶s§½»±â]ÑÚÒÓ§Ãfhòûhü࿌ç‘Óÿ›ú½h}×Üæ•4$SŸöÑÉCíæ×šN\q_û}vï{¯ì`÷Ïî¢Õ'ƒ½›µ¥rÞu´gXy3¦-³?‡ ÷´sY:âŒ#Žd?XðÈôNý>Òvßòí‹'k-­_^íóS¾nqõÑ{Ò~8º$jô2îë–<×8ïdš¶{Yúý“v®ÔZz»äMk:ìè÷Þ¥oî÷™­O¿êÚi>oÀ'ûÁºµ—lî íÞ"¼W{2rÓÜ»òW8Ú=¤®fWö0íNÙ.½áâ£ýjØü€Ù?ÖLeºÅ>ÏíÞ³± ýùbí‰{"œŸ×ß1þK;´~Ê®†Ÿ{½ÓúéaG¿¸ ûźÏ?¨ypRwûø»û§¥‘ßú¤=þ•GçoÆF;æ/“ÑAôéݺ0ü,ü²?¬WóÀ7§Uo®Ñ7){Ñ÷éÖXãÁ±ÛÓK­͸PŸìëM÷Ï'í¢½{üküú¾ië’­÷]ýÍ t«vŸµË3zÔÍ}×íeǸô‡ìë7?òͧÖ{µ=ÆpÙ[[üש¬½ukÂh—‰Ásô§×œÿªdô2în=ZÅ”¡íYôÝäò—h½ÞçÖÞOŸsÌóc~ÜñÈÞoô™u?®X^ŸŒÿõ¼Ûüµç¥™;îø-][ä¿Ý£$3Þá/9>è3™D{ô­æaëÈþ°ÁX&„Øç“=ŸŒÎI½q½]î£<|¥¡íu«zÎl~œ‘sÃy¥÷8žË?d¿ØèÕ.fžy¾¶·ÃÖ'š?Ò™Û¿qÇßFéVé(}úý·|·3þ!èeÜ7¾xºýðÑ·h{=~mÁø[ív<4ýÀ‘3÷\pôç>!å·¥Úì×§ ÐSÈ‘ñoź_xh{C{žêtÿ ÚË–é9z½nu~jÓÅGõéõ†èeÜ[ãïŸÌÃ`kçÞÊgêÇz]°÷ƒ¦^“þxçÉtk gÔïÇ{èÓi7iòë-¦eœ[ï½²ýÇN»µ½Kú>ÿÈÀ¾Úý»7ο)çÝÐ0'vñ9}ÆÔxÑC —qnÝÍÃßdwö¾½¥iߊ´y§³6F´›íGá#Öág[¹(ãn½ù–Æ3o}¥íý1îÕ‡ž^¥ÝßßwzÑÇxsƒXxߥÏ4ºÛÝðÉ8[+ºá¹‘ööîëyD¬í÷sž®¸¢æÐÛŽ~c,~׆ªñofÐ}»¯:w'òdœ­‡ùÆ›ÚýµÏ{Æ+ó^-²Çoö÷Õ_N_ó‚ÝÞ‘ ŒÙú!rdü7yõ¤UÙùöÅŠ ò¢}>m<3óÝó£kuëMÌhš6XÅfÿë^}y…+rdü7e{æÓ›µ}ebáü¥6ã™ø3÷dèÖþkç?“¦ÏŒ½òŽÅÇ¡—ñßTm,€´} 'o\rPkvêÖkŠ~ÿðF³>³ÈÔõÅ9L'd¼7ÝóÕ¶²ë_Ðö½¼£Ãß~ôÕîZyåµËÏVé­ßÿümÃÛ£µA‚ýËÃv?t’qßÔpÕäÔ7ªìë”}_ûMý>[«yºá½’»¦Øûw+£Ù¯ÇwÛÛ7ãÛâg ÝdŸ'N:ÉøoÛšø—íþÙßÓè€ZÉ{SÄÈ¥·ÊyÊ>oÍPýÌöœžt’ýaSÖ}/»®Ôö½·õö‡Ùãhéô·† ¯fÚûcëÉ¿…O¼ñQmð¦C·O;6OŸ)çeÛ|†<Ù6ËžÎÚþªÃ®wn(µ¯'GþôöÕ‹ŠëôÖ³}^úví öyEù×¾9é¤úƒXöXÖjûœù¢w åwú¼Åkukû'ÅJPŸ¹åó-w=½ŠûšDmžÚ~ýÊGÖþÚ¤çŒü8´wû´¿ ›šÍúLvy¿ …^Å}£Øi;æíýŸ.n:ñD¹­ÿée Ã?~a‚na«|ƦÏá¿vª?lÉû|Ôª%Ú&í;{µØÚ«Wßw¸¬öb¦c>5ºÝTGÿØ.ÆÓ£úÇúÇþ¸iÕ&íƒwô«>q»³ÿwŸöÒÛG =|öèTÜŸœvßêëØÇã –,·ßöüê“»¾ssD­?Ù÷ß¹_?xË`W䨸¯|Vì|íí?0EöÞƒ¶øë ù·/üh€n½baþ³§uí6UÞøõy§­¯d!GÅ[í,ÿj]mŽ—>s¸yúÒ¤X½õÇÏçß[ù±>+o‡ÿ® WqýòûçÛ[jÞ~ïußè³ïêÚ´ð¥EzëîÞ‚CŸµö×ßÿüô2®›;ÜxâǸ״©vûÝÞÎ{¯;ã:ï'ÝîßÖ×r‹^>þ»}}5kv`¼–ÿ3rd¼7ßüTʰßGkÅökåýþïŠW¿9ûÇs÷ú3¿ô¬¢7ߺ'uÖÓÉö2¾›‡.¹æ•O–kGÇdæ÷µ¯sô¦Ï^½í`¯OôV9Oêýÿ¨¼w6íl/ã¹ÙhN•=N³~n~üôu6ë¾°³üįƒóÄBÑaÏÃ…]Ž=”0È1Ovu}í®ìjÇsö뇇zÞs;òdü7‰œ™W¦Ëõ‰­ú#ïþxà·XÆižÆþkè³Ò—UÍfüj/ã¼Ùçð·©ÚA#œÕúÂÏ<¶¯Z~³c}×î׸+öÅ볜 >ïÍ}?Ý<¬§vP„ýŽôG_¸®K–îêð»œ5ou?k`ßè–N´¯ O¶Wý Ý-¬Œ*ìóÔ!u®a{Þš³ïÞ™ú‹cÞjÝ”6ÎoÝpû8Úxá§ç‚–¾çx~Û«qÀØ&Œ¶{‡…~½ê¦¯ôæ³Ë_=¹}´Þºþ&±2³ÏËwoÖD™NvPÏÿ¬Ð5[/Dj‡h}ÜþûôE3®Y¸v·Ý/­o^1½Ü©Ð¾i¬Èùö›eoÁ¯ž{׿9g3WÚç½CO¸Ê·RLœ¾dr´ÇXököx6Šeﯟ#G­žº3ð[çj‡Î}¥åEý±—Ú½3ðÈ›Ïmµ‘vˆÙ³Û’cúâ»Äb—Þúô¹ÒÞ^n‹ô2þ­~y8¸z†v臷Ÿjv¿ÉÞï–XÞûqóÈåö~ܺ$ké³oh›'àWë»Á±¬Þ´ûù°qlRco¯>Õ?zš£_¬XPé±.¶®Öš˜6Ý«'òdØ°ØØ(h‡Ã„³ô'–÷™ûèëôÖ÷uëÛwé‡Lõ ¯Ý½Œóufë?‡éÜlÑííxò‹±_Œû¸‹Þú˯‡oœáÐw…ŒóúÊ«YѦ^èûÆì ƒìv·Ì7ºt«:‡´“³\ÅÄí ¿Œóºo×nYñašvXî·ô¥½ºõõýq­}6«ËF¿’Èèe<×%>ù0 vX®¯ô¥ó÷ùeEû:¦Q¶zϵ5„åÝaŸÛÿ^Bì•öö-=ùÍöÙ=ïtÌGj¥úrdœ×¨uÉ‘ë=ûîôEú2Ó¹%®§žpŒ+·-òz¥ËwöóØY7¼õà/_Ã/ã¼FLsS¾ÑŽˆYë®hûx½ )ßWª[Å®ä¶rÇ8ÙœAׂ_ÆuuDúûÙ§ýíãæ‘°ßB“°ÝëOuúâƒèÝïÛû¯m6«¤KybÄfäÈx³–ëøÅOרûÛ‘Ì—:ž;ù†Ýž§¨ØûiwÇøu­ñ€ÛægÓÉŽ2î+ÚJÞøœvdZÖs§$ëËǸ=Û~û‰žÆ@ŒïЍ¯Nd4¾§yÜÛü»U_á´ïë}þ.ŽøVF°S¿zßå#Í.YQ{´#ÆãWgӊë~f ÃßòÐ|líd)¼³92îOùTZ)é¢1Žÿ°÷ù É¯MqÌ+ê|uÌ ¡?œ~¤ƒ£Ÿu”ñ^öyLK\ }ü9ÚE¯Ï³­SõU3ʯ”å°gÀ¹^WŸnІ³»ÚòâÛ¶ñÐ1®v”ý`iEÌïëÛûãѾ]w4j[¯ê«>÷ͰvÏ;ûyMcZÉ‹žŽ<Ù/Ôy¯v4lÌÜ3Wy꫟{$þ³óÇvM<”T©7÷×ï)ü>ÙZäÂR;*–i‹_Ò×ôýbKòMº5üÿÎÙõƽbõ›éd'oÛ9öÑéßî]ërL_ó/ì}tVUö/¤÷Þ;é=ÒÛý ¨ŒRŒØPdD@EñÍ0¢’€ØEDDÐt“ï£DE%@€8ô’€Ž ŒàõýöÝçÜ/F|ÿ·Ö{k=×ÿÁZß ÷î}v=çìÓîÙOŸ:;¥ÊA4ëÍ"JÌ6nðª€Ï~Üæ1Ö.X;F»IËEí6¿Ë'¯o·Ö+ngbν/ÓJʱÿ—ùê §F=?ö­èã:Q{Ùéâsö1úöûR}{£ÄèÏŽa²w‡1žuiëB›ïyϰOõkAÔrQžý¾dÃ6¯IÖþâØ™â9Ë^X(ê<¾¸®Á[4'ݰzâÓëEõH}Á 娝‹ýÞß9i¸0øv:.¿ý» oíªnmñýoŒ6ÖÔüµú¼rÓÑ·@‡ýùÚ£O¦½£uFé u¢Þ}ú\ç×;u®ê®Y«{G4Ÿý¸ÐB –Zgî›´ ê~t×ʵ+ÅœèwΟ±ÅÔíÈþ{™×'µÎ?½êøì½Å¢~ÏFô ì_ÏÄÝ(Ç~|ñßé=Mc {t¢3 =w£hH÷¹ÏfÌbøÿ›¨¥u…Öñ¡#ûqž\Oí¬šycLþfÑ OS‰æÛÙÎùtðØ_U„öãQÃß¼ngø«a{Ù¾_?díŸå>ƒÑÏÚþ×»G0tdÿÍV5[ë\/"æ¯6g·m¿m—µ=,­+øìÅxëxÌ‘ý÷È ´¿ ~é­sM”zEcÊ^iŠf^Õ<>ûi Ï›´NŽû¢ñºÈ7O\`­ß‰/œ´¶;GÝ_Ú<ïкœëÞn ú§h¼eY䜃Öy‰1RõËI÷Ÿ6]î'¨úÕ3Á<ö—ÍFÿØ8ÖéÆQw~c]ŸûCÕKÇÞvéÁ% £ûS{ú£¿¶­É/׺Ðê7s®»a]êßD3Fk7õ¼n8é~Ôd¼×º0ê:ÔØ gÅ|÷§—(=­ó¨‡muÏœŒrº_µçh9pÍ‹FÿÙuWþ¢‹•§ŒñAcCÜ‹åeëãc>æ¤ûS[Àë™Z×Cº¡EãÙ¬Ë÷zzŠfs™£ë —­ó@'ÝÚ¢¿êòß¡uñ¾†h*½yÖi¿ÑüÙúÀò/‚­óK'ÝÚ¢M¾¿öƒP#v½üÔ”‰µ5†]i³hb¦ƒuýé\Ö·îýJ­XççNìßןˆ}vĵOj]ožš4ôSŒïþYwç=O>`mgÔº_7ÁœÙ¿ËxŸSë¢Ýg¯·DӹІ´ž}¢™†§÷vˆ*}ù¸øìÇ7y¿ÎXG誡…µe¢éÌ™¦W9‹Z5|䲨zû[›ï~|åØŸo=vÎ嫜‘Z×;:û¢·1Ohj{{ÕÚÛŒvÖâ{Êoä¹Nëz‚3ûõíQå­uZï󈦗Ë,6?¤ˆ–Øï:WŠ*®§Àgÿ­Žrì¼Ö÷ÏZ—å?:OM×7ÌÔ°G f‹]5‹Eõøºû·ôæ¡ûñU.ÕTN1Öqº6Ù—¤Ý—/ÿãczüþ±Æ~X ïk‰êçþ±gŸpAyökMÄ®ßò°1Ö•ºÚÿóQn׫ÖúW÷Ê׃×*¿Z㶪ÎìÏÚu,ÌþçV;zxó¬ ÿ6ìÖˆÙõ¥3 y ý]دõ´œ×}ØÚ>Öä÷ÄçF\h,.«hòºÁ¨_†_üDû͈¿.ìïÆ¡Î´rmÕç-˜³¶ÿØ'×»l­-W¾ºt¬±ÞíÂ~Oîvñ%Ê¢1Œ6NÍ¢…÷ó¬ójö³Úî:3£ÞíYÑ\¾h©ËakýUY»ÐõsQMÑd0â¾ û»åîå¡NKm´.¥_Ä¢­—¯±Xý5qïëÔdûÕ>ö|ûa5¿ö¿9Ãþ]§3_YýÏíÀj·˜´söcÖzôÈ€IGŸo°ö.\,ºÙîEùšÊÉ5ÝVÿ‡=õ•Sê÷FÿÓ2{œ£etu^FÒ1ÖöíÂõÁrz±á“÷ñ°K_&7Yý`·òò¹ºaV¹Vù_¸Ûõfç­íÞ•ëÇš÷õ lÔã¤æž_ 90š)žó†WZôa¨½¨Ö§AÓA‡ëÇÚ‡î¼Øpø”Öõó¹»ÛÜ <®-˜Ýݳu­uÜèÊõa>,ºSëâùh¸þ5ÏmáãEËùûîÛùîPQEÍÿØ2às}XÇ­ëÇ¡wÅ.¿NÔ¿öbô´‡¬öÓ·gŠ­ë‰®\ÖßQ?hö8kûùaâOÏÙ`ø±¾ÉÖrú^ÑÒõ`cÙÖ¢šV+ÿ„ñ +׃õÖÃNë¢iöìC¢~¦£ã%~¢eGÐÆ¿$tTëú÷Þ3£gõï­?íùãcÂ,Ç U@«€ÏþÛ ï&Zû•“¶(á%êÿ%ïšâ©%=²¾˜iTî2娔]÷}ã¢w­þØ¿§n×òj£¾Õ·wÛ+Ÿ% 3m³ÎüNTÑi›[. <ûóÃc¾83_ëÚ|ù¥!¹Ï‹ú‹Î×ýxüÏ%Ý׌ž˜°Ò^˜©Y_: |ö߇-gc³|ØÚ^øYÔwœ]õcDÉ9µuøUTQ”c?n£o`h]rÿVõ·õï^ÒýÕbƒŸÑo¹±ß6ž÷xúû*”ãõQÿg»’1ƒ¾fÚzçykÿàÎ~úhñ‹·/¼_ëâuCQwîå°óÞ]ò¥¬/f>¯b]Çtg Z+xзv=eó\ø%k|©›ÿÃÓ ?(9éýê“ãW ó=Õ;öÏlÕ©EË¿- Ã~S×fXhõãyáO‰á¢nð§7ÕÞô‘Ìw,JÜùY¬ê/Qžý¹IߘˆñÙŠQ׈ÇDí×»’|#6‹*žý¶ A<óéX­«ð‡ßÉ&£ýÕZ^ÿÎPøíÏÍÙGeXýàÎþÛÔç5Çl­/É×aF?^ÔÒhî©Ta¾íݹ[âg‹J^·E9ößfRgÒ6­‹;PQ;/ù؈e/ý¢ùû§u¹Ý°áîlMÔ>•xÃθ4a~ªî–eïYåó`ÿm^ ¤°Î»¨ULþÈ:_|Ì.iÜÔ»DÕ#³ŠÃ6D9ößæCßpyà>£Ÿè”ûºÊµ´]>ú¤¨B´Jãaö×–ˆŠÜ"·k´u§¨-öJÙ–ž Ì h@ô©¨ÚÕù°Ý¦ÛÏþÙ2¥—N,iäµt³¨õz+÷ü Ð_Ž×ªsháë/Àg?m¡apÜY­½áö³íF½¯éü4øÖû sÇWãKŸÍµŽw<ØO[¾i?vö­“¶kî5« ׸ff þ´‹[¬óLöÏV ëóVis¿ ì~ÏKÔLv~Ö#ê·ûaëä“ûÿ²§Lë|ìÑœU¬û 5ÙOd¼æ+Lݞ쇭r]¹óýd aÇÿYg®;<¶ûÖKþk§cÑ:o.Ü•]S.jž¿S\øàlßyKë,xåmû1ñ¢¦¤í•úI{T;ÛõãåKŽÍÝ¡uÆOYµ°íMQóâC…S7bžãÉvü˜f3ãÞÓ:}õ4QsîõŠGâ“g{}rû=gÞ¹7Q;&ãe-íÊç¼8Ûç“7èÛNíËqsD튞ÕçNœíóI¾€¥Û6ÿÍÉ Ï‹Úƒ›„zâÅöøôV7@¦hÇëi¨.jOwœ~ù—Gg;|ºI?€¦ãýqQKË ^¯ÎvØæ… Þm´c/,¤/jß×¢€³þÛôåíØŒ²Ì˽¢vNLœõßöæOãOßü¥vìζ¼ÿ§WoÞ 8ë¿Mž'9FÝdÚNQ›ì±íš(/ÀYÿ6¹îtLµ_÷qèÚgýÛä<éèåù»7•ˆZçŽ ãʦšº½Yÿ6Ú5Q¥ýR?Ð&jC¿þzÌ\ÌŸ½YÿíZGÇ/ ÐŽîù¹ùñwÑoå•ç ºø“ê€ÇvØ^µºpþMó´£d|;v‡¨½ÿÔú/»0òf;lÿè²ïnÔŽêÓ€Q¢viõÌØ ¨'Þl‡í_ê£v”fÞ¾¢ö“Oßúšµ>y³=v`6yéòDíèT=ˆÚS?xÁp¶ÇŽ„Äû¾_²\;:vÑWÏ ¹ êx=p¶ÇŽrÚhHÖŽêÓç0QçiŽ­;’eêöa{ì˜15z’ó~íhÚŸL±–«ž>l ¿£D;*Ïítû°þ;Ö½Æf§v„¦/ ßµoýäÖ!Ï”œáñ ðØ;¾Xœÿsv„ç'¢öûîZnjœí°Ó[ŸøiGh4ðàí¢Î>{ûד0öaýwæoê>sñZíȪY{N¯¢Ní«û°þ;iú6eƒvä™[iE@Ô-ØUVü Úƒë¿Sß4iGàTŒD½§Cš©®ÛÔíËúïܾôtìw‰Ú‘ö¯xåÝ)ê—ò™™º pÖ¿ÝAßhÖŽP”»ñ>Ñ×ôõà^ôW¾l‡ö ZH*ÐŽ8LØvËÚEÃós[q¢/ëßNÛ+»žÓQiZ_‘%:fŒþáÂ뀳þíU²®Ù7E;¬/ M ßMþÖ1öóeýÛõn9W;¼xÆmIæÙ¢áÂêòožüpÖ¿}ëù¹nÚáɺ£D×÷¿îÐþ6à¬ÿ®¯`†™§¾¶øzó”^ÑðÏ﾿g©©Ûõß%÷ïó¸A4|Ü0õÈ…&ÀYÿ]ãô¯v"Fî¢aËH:)8ë¿«ò§ÿñ…ÝTíÐÙüt„ ÐO1 õÓõßµRßÈÕÑq쟟 çZX’ý)à¬ÿ®†a¡Ãªc´C\Ÿ0²Lx¤pà¬ÿ.¹Þ~è5Ú0!o»mmwà¬ÿ®ýENŒÐ=¦oŠÆ…úp©ÿe}#X;tëš ¾ž+äͽýÆïMÝþ¬ÿnçŸçV×Qx;Ó!š¼—4µztÎúïz°jTy«v(| "¨½h¾úymÂVÀYÿƒ?ë'ËEÓt:ø4ïYÿ'š^¼t`õBøÃ¿âWôR´yà-ÑôÆ«Ç.Ãþ¬ïny>ò ¾6]4Õ}ÖQÚ>p©ïúƽvP?î?K4m{w{—½-à¬ïn:uÙ¥¼†&„SDÓ…€-ïTãFSwë}0„ꕉ÷èAA²šÎzóë÷ö|5w`νxÏúØùîã#ïÍ¦Žºw»—Xß´ú{Û‡¢olxïYß|NZ´„tŠñw€ÔSî[¸ýñ¯ïøÆM´Ì™œÛPÒªæmÀc}dd,™þKƒhù|VÖû× Æû¿²Ã'ýÀˆ0g,[½ñU̇e½îÙ:"ä¹Oµý_4 ‰~ù’0S”ºáKÀe½î\øEúÝÚ~Ú>Ÿ¿F˜W/\y|áß—õúÜôM˜JhûéåüóÂ|讋τ.ëµ®îÓÂâòò˜¿_D|”õ™Ž}Ö®íçõ6a)múOoO)à²>_pIüÛÙ›…eæçƒÎ>0 ï¥_iµ&jˆ¶Ÿç_ÂòaÈ®ÏßD= ”úÒ; m?Ÿ[klÏ®t®[eêb}÷Ñ.õƒöj܈÷RÏŽÑ oÜñ‹¶÷Äš‘tÐn>àRO§ö5è;©bÍwy‹£Ê.õ¤î1¡CÛG­ÄæM±–†Óvß.õ•ûéûø{ ±vÖØaY"NI}yMÛWV}|÷àQbíÚw´¥7@¯ ©7ÍrÆ…kûõñbí¿û6Þqp©wÀ3)6n7jûôåºab›Ãò§Í“LÝÁ¬w»:Gù¯§NçÓ)Öe<ßõÉyŒW‚YÿöC×vøÖ&k{7&`Æ-ÖþGÓõ{‚Yÿvù}É^>O&ÖéLJf¨y8ðØí<^ÒöÒ°ûŸmbùŹOdÎvh_ì9s¹ƒ—¶÷fï£;½"Öýxk·ïq̧‚ÙíOÐÛLm/ï·ŠõcÒgÏ8Û¡ýŽ [ŸûG‡¶×þ©ÀʽÖyïú[Ú]Ÿ¨öWŒsMò¼¶:Ÿ«Öƒ´kxOÍ+Ôù=u^C­ó¨}XM“ó»Ø_Ã…Y?~«Ö›„¹ûc§Cß.Rë/¼û¬g‘å+µ."ÌëÖ×ÅÕÔÍÏ­fù€QžvÓË®Wë¾Â¼çµ}§. U¨œ uæoªâî{Ôù@aáù½:·&,±QÓZ÷ljÅ·¬ví©Uê9è2ÕlµŽ.ªõP£QóNañÓR•}„…F»·Wë¢Rÿæva‘ý£…Ï=ŠÊõ?;5]J–:õ¡!wÕòÆý 65 y–°Ð)ßÒÇÕ:¤°ðùBµž#,´<°ªV­©ú¦Æj¦úCcB¶o.ËóEµ_[gçØþTèZ1G?îüµßnìï5ó:´¡ŸeÊKÿð ¸KXî =wÞۄEŽ,tº"àSaás¢jYXnzì“à'âDÕÄ_”ß2úèûõÂ2iøGkÍûsËr~fÈ©æÃjÞ.빡Ÿ‚÷ÿ»«áì‰ó;KÔùunÃ8o¢ÎßÊ}µnÔç*¥§<7#ç÷꼯տ.¡fÿ–²3ÜÞ¹EÍ·•Ý•ü*®~Qëwu^»g,ÿ›¶_žkëÈVûËsê\t3'",–îú\T›óÌÂÎ%¢’NÑnÈiInJš¾òó9a‰¤ÍE¥¾ü’z{ÓÑ«„%ìšÔÕO̳?Ó'´%§-·aWµ¾£Þ«¿Jµÿ§Ö÷ýúé+×»Ôz’:¦ÎçFZè4@È_…ù¹ðK Ÿ]«Ö³ ýå¹gaáù¿¨¤ÏϲEåçˆ2ô”ë,Ââé–µ¹V­Š*õÌíÌSžÃ5êá…~õÊó×íJ•3üºK®ÛÉs7Æy?³~ 7ZX ì¬=߈ªg×ÒŽ’0÷æ®Y9t™Zÿfžÿý¦ì¿…y¯¾bôwfý8ý4QYµ€¾t^o•`R4Ùw·Û¿+\ž¼Ã¨ÿr¿ÂØŸ­äuaCÙbÀÕ¾ !?÷ºV˜éøæ·³ 9³~ýÞB½!ó×sŽþÕò¡¨L~D}9Þè¯õÏJ‡³ù;:a&odAí G¿ô›~KÖ—ßØÙø®”Ž%Ž:¨íOù¬û¸;ó·-‘úAꜚªWF}Sç¸,Cžhî·AÂ,Ï!WzéŠÙú6ëDa^@§zaþû›–¸ŠÙ´]tâ¸0Ë}\ù½õ™¿;1êGÿzÔÿ½Ú·SõJýÝuA_`6úÁÝú6¼X#¿Tþ3óyUo……>×÷¾ÑïñY7ÿ`aæõ]C)¯˜í9ªaéÏÂüÄ„éSÇëšúg»·ªõpQNð¾6Š Zõó/m·Þ<£¬þønÑãƒú÷ʯF8ä×~0öÑÌò¼’j?êü—Üÿ^ϳ»çD´ŽúÖøÞH®ÛŠJ>—«Î³ŠÙIó^šr¯0ëÃëÁV)?/¦ó† ³ìgóz©!§‰¿0Ú…<¯aœ¨¤Ý”QK 9Õ¸ÀB§¦ŸX©ð÷ÌÆ8JÖ?aÖ§ðw÷´‚KOñAΫ {©¿ª?Vã6õWõ‡–çéã"cœ¦âœÔ¯ü1ú_¿_÷ׯ¸¢_ü5äPõ5‹ÏuõLÉ£žûÕ?#Îöû«ÎËôÔ÷–ù7ÑN“jÏÆ_C_žG ³ü¯˜Ï· ó3ú‡LFüé7n0úw¥¯´ëoìáׯ}*zʪ~üÞøª_\Ôv‡Ò‡ç¥ý~ñD­Cã7õܯ?ü½ñqŽEÙ§r‹Þñƒ…ÏS«sF=Qþéï·~ñ\µ/Ã~Ÿ‚¤mg­ÑϨñ¼Šs³õcîÖ~ˆ¿Sþ3êMªü«äRý¶j?ªÝ|xXõª5ì¢ü%å5죞ûõGýínÔOËPýŸÑOe¯Ðnýfü©ü­ôQí\ö'¢B®Ë©~[ŽËŒqŠOýÛ‹´³·ú·7e7eŸBYNÑ“ü |¥‡üËwÒèÿ®æìùïuŸé%wÏõ^Ó«ù{þËûMõ“¤ý!ƒ~èøœ·6³A¿`ƒ¶Aßé9Ò!‡-Úªí4™Ïv‰2z³Ìt6Ýøö½2ÏùxÎ_è¸#àŽàéØ,ófÊ<æÍÜéyËgðݘν2_9ž]Úä=™€»N“¹ÊaW·D™ÿÏn½27ù4™ó~ñÜÏ´ŸìéÄ÷cRžOà{Ÿö9õ‡ÍœïE¿íÙòzƒ§Oç•¡{1)£/ž}AÏø¾=|?¦žãe ç&÷O”9É—p¾=Ï phý›Ö°©» .ó“ÃF§äÝY ~Á Œþ†Öõå§dþ—Iœ2Ï¡À …ž¡°K(pBÁ',Qæ'_ ó%‚O8ø„'8áÐ58-þŽ€®‘Ð5r¸¼£½•sËè¹dfðšz~çÑó¾ôp®—hàGƒg lYc kL«¼#íöð]›úÚàG{›ÁèO‚çɼÊ=ò^mðN!xN(ô …]B >aÀ ƒÂ@7 tÂÁ'|ÂÞÊ]qp" [ü]#¡käHÎéBw|êyèòd¾¹M2ŸÜpy¿v3瓉†.ÑÀÏØ.²Æ@Ö˜M2_\&給œÔ±àqà‡2q›8GŒ~¿öxÎuMù« Oø$€oÊ%ôpýîm”KD¹D¼K¿$¼Ký$Ø" z&õpnçdÈ X2`É › :ÉðW2ø¥žxJ9ç|N<ð”CjRÁ+z¥Â~©Ð%´Sá«ÔS†ÒÀ7 ò¥žxài€§ž¥žx:à逧žx:à€gž1œsDgžxà§8XsÞèrÎ]”É9Ýòñ+,ç8™¹ÈÄ÷³Òý«”ÇòßЫúýªA2ÏÞHÎq£ßA¹ÓäýãN2ŸÞp™Gož¼k¼MÞ-î%ï§XG±‡þQÇAã…¾9³(îŞ8¯â»ŠéÏUWù±úÆl§).«x¬b¯Š·ÄŸâ+ÅVЧ*Žª¸Ù?NRÿEqQå¼¢¸û\1ÆQlS1☊a*vQÜ‚]ûŠUlRqIÅ#ŠC*þ¨ØCñFÅcT|¡¸Ò7¦ô%GTü ¸¡â…Š*F¨ø@qAÅêç`ä±Ò;4 P? [Ó}vÔ£ÎØÃÏôݨ#àô=!}‹æ |gØÅpü¥ï'\{8G‰wwâ< á‰r^ð§þzC^Ô%ßDÎOÃA¿Sœ+>þ ôâ<ñzîKà÷Êœ—(Bsð ~8ðÂÁ'å"£d®ç ”Ë’†ŒÑ¨ûÑÀÁ»XЉݸ(ÎOù­â¡W<à ó8ï墤\ó”1gç`,„ì…½œKî¬×ó0zñ½Åù+8ÿc~ çc¤{ìõûëA;}Œ~&È]RÁ9êK€_8r€~ç0Ý{«ßC2ENœW±(Šï®§úýõÀ)¿RèY yJaÓH¾ùêø|ÀÕñyÅ€«ãó«ãóߎϩÝV°ýt“¼ßþm·AݶÿmÚ¸ë ;hlQ_l;¸¡;Lì@ƒîô°ƒöhKöÀ·ÜýŠž@Ó>vœ¾¥§ïÚAÏ ý }“í„úë„ggðsF[sFygØÔå]PÞÏ.‡dÞÀ])–€Ÿ+è¹åqþf7”w=÷‘2—3êŒ;ìF߸Ðw(|/½žëÏôM„èy¡<}_ çrîáü6Þht†žÎÁûdÊ<Ο΅ûâÙüé µŸ“Ìs¸à~(ïzþÐ׿†ó7ëy›!St€ x¦s t¦3Æë½œÿYÏwúÁè éÜ`0ðé|u©!€‡@Æø)ø¡À ÍPà„'|ÃÀ7 |Ã@7 ¶ Ÿpø.tÃÁ'²F'84^G™ÈDÎÓH¹)¿¢ž#z¸ÌÝ!óጔ¹oZùþþhÀ£Á#åc`‹Èü˜™ó²Æ‚g,äŠNxÄ'n 爤._Ïë ÇÃFñÀINø$€NÊ%ôr®F=/Ê%B¶D¼K»$ÐJ‚¨3¾øù–ê‘ÿ Îý€z€²tf>p‡=Oý)Î÷JùéC _(Ê„'<#fpnX=7ýÎÑ5(ŠsŠEã]4xF÷r¾ÙX”½¸Iœ;><Ê9§+剧áddM‚Ÿrfp>©œ%œSІ‹”+=ås'qþWÊûJ¹W)×j>håã:ù1ŸÆß ¡åï*À(g+åC§á&åZ§¼çE^2·T"Aõ|ç [ÒÊyÓUþ(ÊãEù¦LË™LôÿrÎN¹¶®ŽÛ\·W ¸:n¿:nÿmßž(}ß&sH¡ D]XÃysl§»?mà_ØÀý€-plñl íPÆmÃŽâAw)öx¶œîRs¾ž@Ó¡•»G”wOGÐsB¿á„gºÏÈ ÏÎQø¡-Ñ=7tÇŒ Ê» <Ý{BwŒè¹*w¥8ÓÆÝÝùàzn‡îp/—¹*ÁÏvñ}ÈGßnÓwÖz~J<{‚Ÿè{îµ€sUzß}7Ú›7êœ7ìîc’y*ñì?Ñ7—¾À÷¾Ÿ—ÌM ¸_+w‰ôM}gG9Ëõü”àIß§@fún,6 ý@Ø8°ƒsmRÞJ=G%ðƒÁ?ýA0žƒÛdÎJðN!mܽ†'z†'´»Û0ð ƒÃ@7 |ÂÁ'|Ã!k8l:À‰€lhÐ5t"'ÉüY2ÿe¹ÌqyBæ°,—ù*7qîÍhcØ×pGðpD]qDyºÖ p'È@w–:£]ÓÝžÎ(ïŒòtß$Ýé‚g<»¢_pÅ3ÝÇç ™Ý@Ï ôÜ@î\sœî"s=ºûËý˜àt/•Ê{î ~žӀ^Ü•Ñ?tOw+wk>(çƒ6AwÎøÀ¾¾xö¾/ô¤{Rü@Çp?èå9üaKÿyœ/×øàÛ@öð„nt_D lxˆóëFP w•t7A0ðéî€`Às@'ïB Kd ….¡èrñ œP¼ ß0Ⱥa;þ N8øåá}xD@®”‰èà®78‘è+"Á76ˆŠâ<ì…àŸÂÿÁ†ƒ ë ”‰†Ñ  ybÀ/2Å7åcÀ'ræC r›@·¿Ð,€ À»v.¬t HfÈR9 z8”B‡Bà‚f!#h¼2…4– þÀ-ý"ðÖ w p‹`Ï"ø¢xÅà_ x1lT ZÅ+d@ÿúŽ úTüW±_Å|ŠõWŠñ*¶«¸®ö(v÷ÛWŠ×ý÷T\¾RO7ÀÜñìŽ6â:é²àã¹=óÂ3Ý/çMóÔcŸrªúÒ_¼§»²üÐæüÀÛ4ýA'²€Oh¢N·t§Ý‹„÷Á “‹:¾y€çRûýÿƒFÿ†òùø…ƒO>ì] P.?‘s”B§(êÿÐî Á3 °A ¶Y;CŸbÐÊ¢~ ô²P¾u úƒwü_ ¹³ s þ–@—è\:¹À-Ï\ð.~!Ñ€¬¥Aܬòá“RÐ,Åß"ê PVƒ^ÍKðÞüšW@_ åÊFeÀSƒ®e S ¾eÀ/~1x•A¦2à”Ñüfç{/%8l]J?ê'Á« vÔð+N)цîe°GÙ4ëå9/ù?™üQç"°Ûÿµ½’ß›ü‘öH®ÎEþßÏEN øcÎGÊ¥o ß@´»xˆº0°•»ØÝ²Q^.[ÔO[¼·Å3åo²Ã³üF¹…(ß=ê§=ðí!«h9€ŽìB¹D(߇#psŒòDPÎ'<;£½:£¬3h;ãÙpÐvÁ³ ž]wų+Ê»‚Ý™í9Ýðì¸;à£ºÃOè{èþ]Èâ»x¢¿ {céîWOà{N÷¨z¡¼èy£ïôF›ö†¼tß§ú6ºWÓuÞõÂp_<ûߺûÝ»è8Ý_è²þôÿîþ€ìàM÷Üå \ l”y‚ [d <|‚Ñ×£ÝÓœ8! Ú!Ð%Ï¡À …~¡°O(p²ð7 8aÐ? ò†AÞ0ð ŸpÀÂ#€¹²hÞù#¬À2DB‡(ÈEó°lèŸ þ&èo¢yxdƒw6øfƒv6he– ¾Ù¯?~Ù=&r@34s@34s@³”æà©á½ ôr(¦Óøzä– š¹ ™ ›ä‚n.dͽ<ø+²äAŽ<葇ºGã”É,°|Ø®´óA7ð|Ÿ@÷Ð.€­ h  WSHf”-@™ÈQHã¼/~!1 K!ä(^!pLÀ)ů´KÀ«ô‹`‹¢VsEÀ-þŸì x]Uµø»;@À)ˆ SIÒˆ ¥-4Ð’›6ÓÍ|3ßÌ7óÍ|3' ‘–úÄ÷Å-(e ð”˜0D¦P! |Ä*$Jž€DÔ÷*Z”'ÿß:{ŸÜÓk*¨OŸï³ý¾ôÞ{öÚkÚ{³Ö>{¯%<@ÿ2ñm$>‘{óÂú·TláŒ)œç Ÿ¯ò\µŸ‘Îç›íÇ?OÞ_î¼ïË=_îóïÖ÷»—ò·~4z^Å}FÀSŸRWähÆWêJHñc‹c'´+tœÜŸ¸v1×¶Gò.®C¶uŒÏVt¾ž1:ƒ¹q:¿Œ¶­Èuc²ø8™ëâS ,qÌ›ð'ñ'}ãÁ» ñÀǃ;Øô¹„¿xxuÉ<_s'ž±‡îeò]ÆøÐÞ%þ(øvyôpí‚—]CÚ ²Î¿,˜÷t›ÂÎËŽ:ÞÓE¼ÃÞºsÞeÈì§›9ø½œèAüiñ¡Å¶-»Þ¤íYìOÖÿeÝ_î—ÖúÀœ¶?ký?Fûý–íÕö%ûÄŽdÿ‘œ ÿß²©mKr>@ü~Ñ¡e3Æ^ô¿øùâ§‹®cô}ú=¬é‹¿è§»uloí?òkŸ]Î!ÈXÈš‚Äõ–~#µ®Å·|mðí‚n° À&“L"r'¢çDè&Ñ7 ^’Àî$øHb “˜')̃~§€'E~Ïk÷Ô |*|¥ò#Z©ÐJ&Z©È˜&è>ݧC+ZéÐJGð‘™ÐÍO&üfB':Y´eÑ– Ùô˦_6ý²‘×~póÚu.®> ‘¥Y ÁU½"àŠ˜^øñÒî¥Ý ŸÅ|/†çbùL1|Ãw ò”€³þJῘRh–2¦¥À”³ •¡Ÿ2ôSÝræI9¼U k´+à¯bZ»ì•ਇz>ù„/:ðA£ žªè[M¿j䯦­š¶jø¨o mµà­…~-ôk§µ»_Î:pÕÃK=ðõàj¾5@¿úÐk¤o#0è©^›Ðc´Z¦uXÐ žVð´B·™ZÁÓÎ÷vôÑŽÎè žxðÐ ?ðÙ /ÈßE{í]àè¸ûÐg¸ú¡]ö£ï~úöÃg?×àa€k{õzƒõï_ù½:=¼ïnÙá¸òp\yP\){rÕ‘fÏö¯°…ý+ì_aÿ ûWÇ™}Ø¿Âþö¯°…ý+ì_­5ïȰ…ý+ì_aÿ ûWØ¿:ݬ{bÿ ûWØ¿Âþö¯°µÑÄ´Ø¿Âþö¯°%ù»°…ý[¹y°…ý+ì_aÿ ûWØ¿Âþ­søØ¿Âþö¯°…ý+ì_aÿÖ9 ì_aÿ ûWØ¿Âþö¯RÌ~Cì_aÿ ûWØ¿Âþö¯²Íû?ì_aÿ ûWØ¿Âþö¯ŠL<Žý+ì_aÿ ûWØ¿ÂþöoåÂþö¯°…ý+ì_aÿ û·r`ÿ ûWØ¿Âþö¯°…ý[g˜°…ý+ì_aÿ ûWØ¿Âþ­½’Ø¿Âþö¯°…ý+ì_aÿÖ»Kì_aÿ ûWØ¿Âþö¯°k-ûWØ¿Âþö¯°…ý+ìßʉ„ý+ì_aÿ ûWØ¿Âþöoå?Àþö¯°…ý+ì_aÿ û·Î_aÿ ûWØ¿Âþö¯°…ý[û<±…ý+ì_aÿ ûWØ¿Âþ­÷®Ø¿Âþö¯°…ý+ì_aÿ²¢°…ý+ì_aÿ ûWØ¿Âþ%Ÿ“Âþö¯°…ý+ì_aÿ û—Ü ûWØ¿Âþö¯°…ý+ì_ÎŽ)ì_aÿ ûWØ¿Âþö¯°9“ °…ý+ì_aÿ ÛWØ¼Âæ6¯°y…Í+l^aó²OIaó ›WØ¼Âæ6¯°y…ÍË{f…Í+l^aó ›WØ¼Âæ6/Ïu…Í+±yy~Ì}Pú‰½øÌ¸Í˜ý³^³vÆœˆ×ç'¬÷ÕQæ}õ^ǹ7Þ#e½¯Žqœy›1y¬͹èH³ojPÇaV«MŽœ&'E¿~Waå±ÚäÈc5oÞ]xÃÞWûM«iËÌûê““¢ß±VäÖç¬eﬕÃ*ÞìÇš2ï¬7™œ#&'E„ã}Ç´9krY ›¼‘ú|¶œý8èÜÛ Îee­m2{h‡ÍÚH³‡6 ß‹ÈÙ+§U¼9ÿ6çØG;¤Ï‹Xù)6…֎䜷•›ÂgòYÍ™³o~—b1—•Çì¡2ï¯=Žý³ËçÞ&ÍûëÇÚ ëFšóo{õ{lë \Œã Ü”#¯•7l)VŸU±ÎÁM™<1æ}¶ßä·š ÛW;dÎÃÍ›5¥Xs&n¯y·=er]E˜Ün³Ïv¯É_1er^E¼Ã^ÛÈ?³×vÁœ‘‹6y¯<&Å É}5vV.Ú‘ÿªÿÏì»õüçå˜üW±¡\’·ÂÊ5mò_E¾ÃžÛHs>nŸÞkžïJÞ•É:†¬]X{"ÍúœGïï“óþrnHÖ=å¾¼ËO1gý7éýÖšÙÞ(gŠäü¿œ!J›Òïñdm,kD¿Ÿ—½|²gBÞ¯¥ëA¯¹\Ë¥ÍÃ8dÀc~”Þ‡Ó:?€ìñ³ÞÙ-èµÙC {äÌ’œý—õCkÝdB¯VS9§×ÓŠ£ôû7yf­™€oó,Ø`€I¤=¹Ñs"úJ¢s$ ÜIàN‚·$dHB¦æC ¿SÀ“"¿áÉÍ\pŸ _©ÐI…N0©À¤B+^Òætø ®¥C+=d€#>2à#º™àÉDþLèd‚#‹¶,Ú²á1›¾ÙôˆÇläõ€ßC<äÐ7ú9ðœϹÀä“‹Þri/FŸyð™Çg>pùÀå—ùÈ–/º¶ØBà +„Bä)DžBpA¯¸"æ‚~¼´{i÷Âg1ß‹á¹X¾S ßÅð]‚<%à,…¿Rø/¦š¥Œi)0eà,Ces:,*‡n9s¢Þ*µÚðWl%|U‚£>èùä¾|èÀ*xª¢o5ýª‘¿š¶jڪ᣼5´Õs½~µÐ¯g8ëÀYǵzx©¾\ À7 £è7@¿zðÔLã´ÇšÐc´ZÀÓ žVð´‚§yzéÓ‹zùÞ.ŸàíDðÐ ÐïDæ.ôÒî.¿×úÀׇûè×ßýôéGÇýè¬ý\€î׸fÅòOy;.·cp‰»Ãׂ%¶–XÚŽµDâ_çþtqP\+±¬ÇJì ÿVœºTL*±(2/Æ¡áûBì¸RbHgüx¨óª÷Ù1ž3_̡֓%6“¸Œq:èüix¬e¯-KlÅø“¸Jb*ñ­ìXIb$‰‹$&bãæ¡óH¼cÇ9ÛH\ãŒiì8Fâ;^±c;‘XDâ‰=$Þ°ã æ‰W„Ç;HÜ q‚ÄH<°T ~¿íó3ç}|Û·wúõ¶O/¾¼íËÿ.>»øëâ«‹nûçÌï¿›_Ž,é“;ýq§/îôÃ>8¶³è‡‹nûß¶ï-~·øÜÌËß_[ülÛÇÿ:RÏmûÌ™u¾ÌkΓÍémÖ²Q“h‘uļß<`ò2L›ý”Ó:ƒõN§s/XûmúÌ–ä?³ò¥Æê=‘Ö;È}:_‚쓳ދEš\ ûÌ™ã€É´×¼Oôê³ÇÖ9€I}ÖØÚ'­óXgmbu+9'/gnäÝ¥¼×JÕ·Œä ýJònÉû­äzŸ}ŸyÐN‰Ðû^äLpZ¿>ï›Þ,p&ò=‹k‰l傘1{û=&Çò„Émj3 ›º QúŒ²µrÊ䃈uÔe8`r¸ùM·y“ ¢ßäo[fò+{ç°"u¾$ë ÖŒÉßæ5¹“¦ÌÈMŽsXS¦.C”ɧ4döB.3¹Üü&'ÄŒ#ײ/¬>C¼#/Ä´Éç¶Éì‰ ˜œËSÚͳöEúCù!¬¼ËQ&GDÀä^Õy"¬üË‘&Ç›ÇÔm2yÞ¦MæÈw¨Ý°I×mØ:xpíÙ_(õäl•ìU”Ü­’¯Ur¨JþT9w©s0I.fÙÿhåcÔnåyǨáàý rGÌè=’âÚ.™ïmFŸó¶ò½E™ú ÞCÔoˆ2¹"†uÍɽ,çÍìüËrÎCöàɾ;k¯i”Þ[š¥Ï¦K>,É…%ûú¬3j ú<šœG—³g£æÜÙ>“kF箵raÍi·Û:k«÷vJ®Z9"gÖä ™¸Úr~LòáZgPbôY7Ùû¹¸_sØäÄšÑçLdªœ‘³g’kKraYû‡Í~Ø9³4R»õrfdW¼>bý€¿]̱`æ´ËŸL"0‰ÐMD_I(?—ïIàNB×IÈé‘OtÂ|IG ß‹Àç†Nínú¤ÂW*t²à7•¶TàR¡•Ê|Jã/ý§C+ZéàO‡ïtt‘1§ÃŠLdËO&:ÈâZÅ&f”@+›~Ùô˦_6p÷×7¥C¹~tÙ¾ûÁÙ?­C»xàÚ×,Ÿ_þ‰soŸ© ?OiÇéÎ]2ág)±¸ÄáwËMIâí𳔇ÊÝhçxq®ƒKœlÇÈáç#Ãs©‡¯;×¾íXUbT;6}§œê{:×Âí8òP¹í8ÑŽ ñ ìg’xÐŽ™V¬Ç\8(7£Äqεq‰ÙìXMâ4—Y±˜½..ñ—½..±“ÄK+IœäŒì8ÈŽìØGbg¼#1ŽÏرŒÄ0ÎøEâæûAë⟄¯‡K|±ÔZ¸3ŽØÁŽ$Vá:ãoJ<`ÇÃËöý±³Ÿþ/õçm_>ZÏy+~Ÿ7ùxFBy¬\<‘æ,ÒS+mÁäâ™6ç&MM´}&›[çÜ·êžùçŠæMÎý]ÛÌʃšæÓ¹öÓü:?g")È–Y´¥@3Ø ¾0&)Ðw0K†ÐÉ…^.¸S‘%ƒö py€/—,dõxõ’a.òÑ·¾Íò]ž³ÀA·~ÊÀ_O{1øšùÝ žfx-A†æ}ú¼N¿›ù]ì~Ʀ›á·¹š‘±Jþôíµü-ðU l 2øƒèµÀO ø;ÀÕ½vøîv|ôp½:=ò®÷Éó<}\ëW?ºè‡ç~øéçÚôø= ÷?Y«“‡×å¯Ë^—?¼¯üðÚüáµùÃkóÿ¼~üߺ6cî×"“صÌý€ƒGN´Q“‹ *”Ûf±V²ßäᜠ幑ZÉâ.orL˜º*n“mÆÔIö™|hS¦—Ûägž7y:=º&—ÔJ¶êªÄ‡ÕUÙdêt8j%÷/QWeÂÔ=ô˜:&?³×äE›75U<¦æá¼ÉEà6yÑ&L~æS÷pÔäÔ‰vÔV™0µU6™ÜhSŽÜh>“£yÚÔs;ò£Í™Ü^S_eÊä 15VFMýÃGÍäi“#ÍmêNšœ¢ñ¦¾Ê¨ÎËcåHÛkêD˜Ú*¾Ðùh©7få5ùÑ¢t®+Gó´©}kòŒèiVýÓ{tŸ>WmåË•aê”L•ySÑmò¥˜œÍ¦ÖŠ×ä)69 "L å€É›6mj®D›¼ý¦îʄΟfÕ^‰2yœ½¦>â>][a±KÔ__#Ѫ«iê°Äë|¨V-–½¦V⤣¾r¤£‹ÿÏÔKôéÜ©ç¾Ë˦ÆrÄy'uÞV©ÇbåuŽ6u}‡¨•mj*è‰V}ÄÈPýÉ#!¹—$•‹.Úä ó™œ±ƒ:‡õÅ“&ܯAò[K¾(+7àÎ'õåkVL階’ŸÎÊÁ¯ó¿IGÉédåz59æ{¥sAI^ÉÅ ùܤ¾…äRˆ›Ô9¡Äu—œN’¿Iò.¹unXÉ'çñ­3üóÚµ—üP’ëÉÊëäÖ9™äLþ.èîbž%›l"0‰À$“ÝDô•Dß$äKws$ >’—$tÂïð¤€'E~£7ðnàSá+þS¡• L*0©ÐJÅNÓøKG÷éÐJ‡V:´Òá;]d!_.¿+hË€§Lpd‚# ³ø Ù´eÓ/¸lxÌF^ø=3:ÉA/9Ð/ßð L.0~ÁÍ_üäÂk:Î.˜|d©‡V>ò哎 €)„ÏBèB¯z…à*„Ï"ðÁS0^Ú½ô­En/¼ó½|Åà*¦¾Kà»$V‡@¥ðW ¾Zh—W ½R`Ê¡ •¡Ÿrè–Ã_9ó¤ÝTˆNà»ÝW[‰ž+ÁQIŸÄòò O¾NUÅè°©ºUਦ­š¶~×€·†kµð× µÐ¯g8ëÀY'º€—ú‚5߀Ž ßýFè5ÂS#0è© ^›ÀÓ-àiO+xZÁÓŠ®Z‘©=µó½}´#_˜0`ðÑ ?è¶^:iïBG]Ð颽ü}àî®\ýÈÐ.ûÑu?8ûás€kð0Àµ®‰¿lý³c{gž±ð^b÷?—óйF‹K.1¸ÜdäÆáܯ.Æì\«·ck;†¶cf;^væóræ7”˜Xâaÿ²ƒ×îÃcà¥ö°K¼¾–/1ìR±«·¢çƒbV{o»Ä©Î¸Ô™wÝY—)|]ß[:ãHgìhç4´cDg<茗ŠÿÂc?æš3γb<;¾[*¦³c8;~ ÝìxMâ4gl&q˜Ä`öû;Æ²ã«øe¡ØÊŽ«˜óV¥ðTʵRp”2ÎåðS/ô¯€F×*¯¸Jà|àðÉ'üøDfpT‰E¿jø¬FgÕ´UÓV 5衆¶ZpÖŠÞ S ÎZpÖ³\õðY|=¸€o@? Ðo€~#ôéÛL#ºodü›Ðo´ZÀÓžVð´‚§º­ŒQ+xÚE_ÈÙ>£]€xà ÀC'ütÂg'¼tNk× ‹ö.pô‚¿ö>ñ­ÀÕ‡ ý誽öÓ·>û¹6\àÚÀ„±jÃÓ‰û9‡cMý¯Sƒ¼k½:ŸþZð¬oT¬É«O¿¨i[ߪÅÐõÓOïÉÀœŒ¬'˜üÃQ¦ã©Ãej†õ;r»uÃßu~]ûw:^7gj02'OŸË=¡•ž`hO =‘¿lð&—ˆ\‰ð˜¯ÙÐI¦_2ý’é—L¿dú%Ó/™>nàÝ{ïÞ 7tÝôKC†4øJc,³Å_‡¯t®—€ÇK¿tôÛ$|Ò–lüg o&0™ Ú}Ë¢ý³à¡ šUÀ{¸æáš‡ktã·6úæÀwãÑÞøÈ‘Op—Às<&´k×\ßóà7^ó€+Wø ÀUô)€V!0…à*„§Âí.A¿ú^hyi÷Òî•8 <ÞÆ•¤o cÜ%ò"wôJ°¯ø(CÖ²Hí–§ ÚeÐ-‡¯6h—_9xËé_Îú6ñW‰Þ*ÁQ L%ô+©mU\¯âz×»Ñcm5À×p½†ëuàªßp×Àkü;Àg²×ÃS=üÔÃ_þЭ~º‘Å>üò \¸šÀÓž&Cp4#S3cÐ/mðÒÝ6‘^Úà£mN»ÀÐè€Füu€¿ƒyÝÁœí¾ønáønà»´›ÜCŸ>‰Qħ“Ù±ÏRuv–Šoì¸&|ÍYnv‰YÂã”ð)n¸ƒØñ‡ÄÎ8ÃŽ/Âן{ÇÃc;~¯±#1ƒÄ ÎøÀŽ ì˜ |?ù»©¯ãÜ/eû÷v.o§/oûð‡ÚcîôËíÜ+ö:´øÛKÕÕ ÷«Ã÷š;ýæp_Ùö‘mÿØö‹Y¶´lû¿¶ïkïMÏϲ”O+¾¬½?]üVñW—òSmÿÔÞ§îY¶ô>õ’%|NÛÇ<Ôz´íKÚ>¤í?.ûÓuè›ç12c¿Ë±×åÀ.ÇŽW0—Vð¹’y¾ ùVq}Õ”©‹í5õç|¦îÜ©57ajÌÍ…j]¯¦ßêILÜÇxL9ÚŽ65­ùŒD¯‘£¦†5Ÿ[|ºÆÐ¿®pB¼®I&µ¥6œÔ&“Ú¢’O_j†ÆÂÃZúîˆÐuQwÄèÚ [ ½e¯®ý¹c“®û#9Úw€/ö€®ûsê”^Ö8~.`×ÍëºnRk4»ft]ÈäbÞïZÐ5xvÐËIóº^i×Ýàu»uÝR·'T³@깑g×Òù>¡k¥OéG™ût½ãldÏÕõ¤–Y‚à?Ø|®¹áÇL…àH _}rø€¼^ž»%Ày÷é[O mÉôÏFÞtäL¦œÙò|C¿yè.Opƒ3yÈž \6²VÑž)Ï>³¡[N[6}+«¢_Ž<»„y¶B¯ApʳHp!s¼´¡“6èøí…ßBy~ßEß.úzù^ý.y~ɳо ÐhƒÏ.`ËGôíÏžt݆ÎkäÙ‰<]ðÙ$xà­\]ôë’ç¡5ðPl<“øîG_ÝŒCütó½ ümŒI2v3ÞmàîFÝÒŸq €³[žAÒ.màî†×³øŒ¡½/^ÇQÖ¿w³÷÷ZƒûG®¿ý3®½9×Ýõ¼üßZs{7ëm=Ë²Öæ|.ÿ5klÿö(ÿO­¯ý-ëjÿªkj‡ZOûßÚ‹ü}-Ík옹oÍáq’G ók9c°ž–3W0Þ+h[­d^®düV"ïJl~%¶±J|6`WM›ºÁèùˆ}¦n0¿D¯GŽš:8ô˜úѦF$4:à¨×5ljG;jÏ8êÛCk5¸Ïc¼ŽšÁ‘¦^¿15ƒÁwß#‘!’f0¿×ð{‹×Ô Ž6µºöéZ]VÝ`h®À}‚©9éÕõº¬Ú“ËLÍ.ø;÷Å›ÚÁ£¦v0xׂw-}Ö‚gºº”ö(àwÐv)z< Ýž4¥»'ÓÿäA]3þdþN‰Öõ}Nñ™ºÁÑŽºÁü> š§yL½®yS7ë ±¹Ö-˜š]èìtèŸÎïèMº^—ÔyŒfü×Óg=²®ï×õ¯äF¬©Ûœ‹¿$à΄ΙƒºF¯U;˜>gÉwø; }mßô³&tý®-èspÛâM apÍõȱ|¹¶‘kéüm†¯ÍÐÝ ìfÚ6‹OŽn·ˆï[w |d"«Ûç¨Ë%× —t@×ã’ºIRßSêinÖ5¤æ¯UokF×*–šGR;YjûZuµº¦§¸R{ìÒa] éÒÉP-_©¥$õˆ¥’Ôèµjcè:¾qȾ¸xí†Hí%©§é‚ž y]ðoÉÀe‚¯€ï;Á·˜àÛ ¾àÛ)¾6ü%À_¼%О@{í ´'ÀW"Ÿ‰À%Â[¢øÚÀ'‹ÿ |2ðÉÀ'Ÿ,¾9ðnäp›‡|ðZ ñ<¥ñ™Æ˜¥Áw8Ò„wññG´Ë”¾ ]© údÀwp™ôËD–,øÏ¢ý³è›E_×<\óp­º ŒK@p NôŸƒÜ9è#‡ë9 Ú+„¯6p„?`óà5O¾#Wp´€¯à)@Æd,@ÆæN!ø á©žŠ _ý"¡®ô¼àh£¯W|v~×IL@Ÿú–H ŸhµÁ¿9Ëè_Fÿ2ú”A¯ E”A³žÊé_Nßrè•/h÷°Ž¾~úT‚·›þ•À Y)q ø«À_E[¿ýà­¢½Jb—¼5ਡO 8ë´kYƒ<õÈSîzúÕÛèç‡/?<ùу=øë&ð4Iœž¦íŽ6ÃO3úk“øHô!q ãÐmðІÐè€F}:à§ÈÜÁÜì¦O7ðÝÀwß |78zèÓCŸ¾èeÚß–vü!1†3®XjÍËŽÂ׳콒âkÛ~¶ø×á9|»ƒÖšl_ÖöMmßÒö#m¿ÝüÉ:ñå–\«q®Ñ8sè.µcû3öÚÈ ãYï\ÿ@¯²·m98W2Ž«Ð÷*ÆgÕŒ©oÐ!œUÏ’ï«Óc€=vÎÔŸä3’¹°9q¢W‡l;øo<Ÿì)Øü)3º–úi^]/øtðGÓ'šk[†t}Ãm´móéÂRØÍ\Êd¹oŸùÐO†‘üzH’åžA_7}Ëåï5ôÉá3GlÚåò íº}:*F)ý»€ñ‚·‹öúTÒ^)s’ïmÈØÍg7¼u˳„¶úõ¹—ýÍÿ.;5þçò®‰n+|ûÑ_Œ{ùÀ½{_ò¿îzá¾_6ôySÜoôïàþ;î¹~::.îùsº:"_ÎÇKøcÙ'¿›åúÀž¶„; ä†ÁÔz×ñÅ7=÷øöCgØ¢sËgî}ý‘7l¼®¾±mý³;~÷vOdfÁiÁý·ýø­Ë/¹*îû†îXÉÕœþoG¹6k<Á=wýØõàscà›´ðÝúñ«>ü¥ág\/LÿÞû»ãO îÿò¾¤Ùžó;ô‘Ç^ î¹mf€Ÿ³ào«Š¹dåy׺^xñ¥sލy"¸ÿÆùññè¼àXÞšÓWo}Ìæ;þç±–\®ÛcªßØw#|þ2æÚWWŒ†ôqÃòž Éï ŽÅß?¸µõq×¶Ï=uÉÛ?8.¸çñϸí×o¸ŽÓò€'Zãyé»OEÛìzáMwÁ7Æ÷ï[ûõ’÷ï´õ»xû†ö·Ö¸âïÛtljۆƒ{¾pæý×w¦k­Æ žx ÏÈÃõ?˜ô”kvÕ+¿>öÿ éM¸¾¢2núñß ßûËOÇvV&ÍÝóŸ®3ï8gSÁæªàžË¾¹ç+ç=éZ©ùŸÏÂwÇ“~÷#÷Ž»f×>úð­kn É7üLÔ²Ûû¶ÍŸïû…çõ¶k‹Á·û[çwþðøMàÑóçΗ~úÍo}.Ë5ûþ×›×<¾?Ä×Ëâ^‹þ|pìc½½ºë†àîŽô/ÜwõµôÓóá?.^³uåké®Ù‹\üå'ýÁý_Ëzl対»ëßÊ^¹ç¬àîœ\ÛxA;ðz¼¿rÕ÷üde‹kö²ÿêr}*¸bãïO^ý«à؃‰¯¬®î.Ë}õ¼/ ¼ï¯Þž2ƒª\³‰·ÕÜÿÐs×&´?{äã6>¶=¸»å˜öütæÓ%z¼ïþ~ð¨ëî<`óïšMaøÖ³?ÜÿHÕ3í ×£çÌœüã;\õø¸N¾ø'??5á<ðèñíKÞ{ù'ê\³ÅëWì~üJèö½w˧ï Íë§oúèËîve~ïþÈgýè‚SBã}‰ïÑç?SÚ²­Ê5Û{Ú‡‚îÐøh¹ã~Tvaq\åýÁ±œñà/î²ù±ÇÇuê³o~!v4|z¼¿vaÝ÷®oqÍ.ýÕŸ\§±ûû¦_#ÄÒœ{ëåÁ=ë²{+g¾J=Î_»qïö›/sÍöþêÖ[Ÿ îÿŠÿ£}QuÁ±ù{ÅÇŽ î)¿ä³¿¿jx=¾cÇ_ð•–{/rÍ~pfßØÏžf>¬êùê#ƒchoäs5ö¼^ïØž Š?öâ×ìÕUo.œ²'$ï­o]Ùìz-¤ÿoGœzþ–×é†ÿÝUõ×»Ž^=þãÇÌ\ð\Eôâýfvè®{²<‡ä¾õœ'ò^éñ‰‰¡goýºkýÑ'|8ö[ ÁÝW<ñ»Ëïz¯m7—êy2ŽÏ¿æ{®Ù›N‰àVÀ}ë–ĵ׬ ŽýàÌOÜüØñ¡yu©ž÷œ]Èë!×ì­—÷·œáwÜW}÷½ùÖ×ìûnp̺ݭpżZô©_}Sp÷cQ'_š_—êyqÆV÷È—\³w<ôßé ׇäùÊ7¿ýíÕYqsæ>0~ÔÏ>ñôø‡ìq ͯKõ|¸·ûS'Ì¿×5ûÕ?œóÕ÷w÷Þÿ OFjp<Úšx‹ósOîÃ;0Uúéypß9ß8ãý§ßãš½ÿÜœÏ}9•ùs÷{ Ö~Qãç½±jú÷ÅÁ= Ëï9®þVúéùpßw,ÃvÍ>vyMWË‹!=Œ?øÑgÎ}38¾õ*,³Éu±;þ?;¾8¸È·ðèyrö1Wgªí®Ù©Õ]w};%$ÿxá•ÿ¾¸Ÿ¼øƒÏ¼3&¸#8^úìñ¿h~&¸ûź‚oßE=?¬®~òöÍ/†îW\tÎÕ±G…仳õìîˆÈ|ý+’gËkC÷õ¯¬/òßhÓ‹ÿù=oüÃÃWD_øŒk¶ãâ[–ýáŽ?÷–Lº~;¿ý‡·¼uËïWßmMøÐ}`‡ž7ÝxãVïÓëy>\»,ï'?îŸÜ÷ž³¢;BóåYë¼ú–ýcÁ2é§çÇçùÿÔï\³ïÉZó“ÁýßÉN<úõ§‚ã ×—ÜÝÿöâø\mä^¼oìÐóä‘ÖÄ^ôg Ÿ}æÎ§¿ÒËw££nÙ~m®sõsÈ~ž‡ìh‡žG|ì;Q¸6!={Öm¸ú7O„ôò½ûž|þšÇã^]ó™þþì/Ç_é¹4nò‚E;Ø=l Lè>½CÏ£àŠ»ïÿÙqoºf“_/ûÉÛ˾ÿul‡ïº–àž¾ÀÀ+_<x=&¯?â©/¿Píš]uåøIp¿Ü}z&‚ã¯7 ¿yTpOuß7ËüðzLŠ[÷ÇkBz?iû¥þ¯ÝÇ©=ï»à}m¡ù¤õæ:ŸÑyü§? îYõéB~þez~âȇ¯d=œ}§Øs²w“š[&Ûz^ÛŸÎÚr0@ñyïš ïßöúå¢oyvÀo®ùîÈ'âФ&ÅÏ;õ“îw—=ë™Õœ©Ã¶—¨ñ°÷­Ôã{ÏÜo¯WÎ,¾}È}ëÜÆå¥AœøNÔûzä-ǘQNz·«7xãI§¯{2®¿iÿv³úxb‹ÉïGÊO=}ÆE_A›Ûî5Kœva¹j<ì »Öœð[°Y]Òeäì ø[õé« Ž/AÍ>É­é)6½ðó]ßþxx5öMJaî¾Ë¬³šF/ ÷Gj‚kN=jÌÒÏ5n«ó»{O©×Z©>òkÓ•…®q[Ðâ÷¦ VÙöAëæI¿?^jËß@5ö=¶ïãa#¶šÕEãnO…ÞÎ?=Ót‘k<t,þ!hÕ‹âf5¿SN‡}r51O˜Õw„ sæk{V-7«WM ÍÏýÞæßÑÏ¿èÒ'cŒ¾÷ÞÄ|Ÿ øPn3¼RhVånØøŽßDéÀÐè.6+žj3ªú ±©Ç¬›ÚV^ñù`r+ïCf•Ú°ûgPÅÙ¹u<íþQíuΛÎu¸=¾Ÿf»Ûú”Y…ñ»üæ(›o‰wüüþs©6ßF§^÷eÚ×öü©æ[&¨qpp›¥À̪e'¼<ø€(á}ãþC¢ ÿc߈Ýi.;ùÎqé‘Åÿƒ‡¯öÕÆÍÌ*¹03M”Žz71û£©v»¤v\µNlºïªù¾)§ø~È3-`Òm˜Uÿùãá/ŠÒñUïvóDÄîÝñÒ4?Û>›u·ß]•¿SNñÿ²;̪žËÛOüè¸(M˜²xÇvQ]ÚàÉÝYNý¼âÿ¡-sË/œhVÉb‡ÎˆÒY'îh9ù®~õÑšfôì1G¾þ7±Qn+ü¼ÕÖ/ƒ¿½9¢ÿ­—™U^Gï {P”.Zs´íŠçD®£ÒÿÀ+~:1©`Kß3fåO“§M8)Jsb^ݸûE[Ž1Z]^éâËÆà?–Ývs¹½5Hñù°áóDïî0+™5~¿­Z”®«išŸ&X<Óß­^ÅÇÃ>2åþ/ÍʲþÇ-J·<<ã¾×©ïšv/yÆtÖÕ³êàßK+bÐL³r¿e0ˆÒÞÊöì•* ¤t¥•‰MSýÞø sðŠ_‡ål»è”Y¹kÏÝ,ADéöÙ^oÑÍæsâMpà)ç8±ç‰AŠo‡§X—YI§_Ÿñ-ÏÝ;5½O°=ß:׃Nêz§7{æ®efå­·G>òÁj{¼¿|ñ›‹ ‹‚k :çE[Vü;•“ïâæ¯‹Ó§¬;ï´KÀ£øzxâÏW¶ºÅ¬Þ`ìÇEˆÒ·?ýN$´³õ‘ÒkÀkþÉeÁ£š•Á_­:úû%QzxÔ†;š·uõÓÍr›õÒ£ÀkþMý2ëó½áf¥—xdö¯‰Ò²è¤G»¡Ö†Ž½žôšRtìÇ¡vÿÖüœa-´ÍŠï}—ÜA”Zfà\›?¾/¯{ùë[]íÜ$¿F¾fσ5Ÿ§wy,ñ×úfE™µ±"JÿX?~ÍÊ2Q §ÿݯˆM—䂤xÍ×ÑÒJpÙu…¿ìŽ™wT•«…›†¹ö' üϽâ4מvõzg|äa{ÜÖü–«ÏM®ùºâ¡ú–l G{ ?¹`Ö~Qеaà¦Ý›Ä¦Ñy3ü´Í¦ˆæwëŸ/¿¿îA³âÆÆ}ZG'´¯ªÿÐu¢@É™=? Ñ|ɰìõf…µ­|£8ºøé¥žý9åµ­e‹Mr×8jŸMïÍg¹L¿áI³¢ÿ(¯¯¶|ì²g®·60íöûY <±©é΋G§Úú{ˆæ¿çù/F%5+Ú—<Ùá©Tqô¾[lù6Q Ó™U¾ÜT ¼æ·lÞ =fùŸ)}'~á’‹£ÏœÙôêë íùFËÕF¥Ÿl}2DóÛÇÚuñ¯üóz‹sFžG öï½=9Ü–“ˆÓslð‘½N¢ùôá–Ï_êa–ï»kZÈûÅÑýU¯îøCQàã½ð·ÛÙúsˆæ¯¬mn…‹¿åOß[üxÂSâèÑÙû‡¼ÞLìù¦ÎÅ_nI°÷I­é¬ÍçDÍçE§VÕçœYn-«»ˆ£g:5þjðX{=ýmŸÈ?÷^óù6¯:'<Ž˜å9r#«»8ú[ë½Ë»²Îø$â—éc·ŠYs¿ùêÉCÀk¾Zfw°YnmsLpñµÌ7( Ço/Ûûh|ÙîÏs Íz½µQÙ[öz{îézøúç\ó“kœ Õü~ssð w˜Ç’Î>×þˇEÙ»¾¾\áG¹Ï¾Ë.’ý«ù‰vt±…y¬Çöùý¯»"ÊÞ뻬e+_±ç–w>©Ûð¢Øøñf¢ŽÀkþ}Õì—åÇšÇÔ~„(“«ùä$±çÚ5Úí5ÄÆEÁâ–ß$œ¦ù§Öó®yæB¹û€(ûjì)H±ÇߌëÐxýí}µ8©PæÛúx˜æï÷ƒӊڸ摲oÇÍí(Êþ8Rçþ=½ìq8Mô¶¼±‰ÜHÞf÷Ó0Íï_…µ¶Ú,ÓëýcþMg‹æ¿ˆ=‹îz¸E«ébÃGÇ—Ö+J^ñµXï#”YËã¦âXL«Îëbi¿çbÃ?ŒYتðŠÅ >{xë×™e¯ÌmЧì qld7iIÚrŸ½øÓàEψ úÜÂ5 S|,–æÛåOͲ­÷¿•wø”8–¾ÅóîÇ OÙ%bƒ’[à?‹å´t§Yv­Gfõí/‰cÙÇ6sȹ/6Hñû ðŠŸÅu=oiåâOÙÈ÷ÿ\ÒÓKÛ°´à«uñbOOëŸk¼oŸü]Çãߨû<Ã=4ža5Óçl6˺f~R$ŽÝí3£]K/›Ê^ëïµ* œæ§Mñß™eÊîÇK(f)"ö({D¬ß°upž§×üúæ…Âg>N3žj)5ªk~>öRá5ã<¾{Ô<ë’óõ¿ÉžÊk¹üêuÑáÎY®ñsT{¸äëØ;AÝwI쉰„œó åµ\^8?`Ó#[Ì£oÈÚ×ıOn5_¼ÊægËW³¿Þ“)6ô“ öFÊiy¼tËoo=e}ѲäűӞ *Þ{”ë?üéÝÞ'¶¯åò\Ñ+Û=`•Û¾|'ŽÉéa–Cìñ^ûã†gú‹õK;¢Àk¹”ÇO•»ö‡Ž®]Ɉë+Ê}»¸pºJì±Ð5s­o×›—ª?½Ë^÷$iy}WvÈ—æQie »N”‡|¿µ$ûŠÍGcîFoÿ6®ñ°Þ'~ZƧ§(¯ù©æG»û]?áHì7.>•|ë÷e»†Ûr_Wnð/vÉéºj¹‘:ÝÞ‡NÒ|æ”ñvûÚYŽ‹oåã\vç6¼wZ<˵߷îCË0±Çm’z?ÿhÇ[ín·D”Ï:Íg})ö4ÙüôšO¾ë¤Ï|–¤ù¯Ï•KÕ~£(Ïšvë’#‹ÄžÆ'OÙü±[h¾[Ó£¿Yúß¼³÷tå7õªwà¶Om}¢Ûßç®ö˺}!Ö[fD”m¯%éñh- ÍÒ—ïv»ï5¢ü–¥í¿þð‘qüÞ{÷<ç:[÷™çþNö¦œm‡o jæ0Kï‘„å÷Äõüh‘_=ë­ú¿Ò®Šï‡~î/wš\ó@éMî¼g\gQþè›[o žé:uêõõ~ªÿœåß• ²"³4sù®Àˆòªz¾óÈN‘ÿæ£Óýó\zn}칇ü—`ÏŽP|=ôúyi–¦|Þz^øQQþf´o«õ¿ˆü%}†ø¿,Ö[ÇÔðŠ_‡,55Ù,“ÐeÏ€]¢üâÞ÷ú%ÚçÏ*_¬»bÊ+”S|;¤Î1ÌÒÖ‰(ÿüñGÎVf‹ümM™ze¡ËîZ÷º¿¤„rЇ²ä†\[×x.•«áÕ_‰ò+¾{ËÛ>—¿÷¢<ÁvÍ;ëRäE€¦àQü;$­”q®ñ[ªÖ#.» ÂOnh_Ÿpn¤âóÁŸÇ×+_nÁÚ6Ý'*CÆüvª¥È—Û¡ß|-rŸÍ üvKsà_\?™%›‹î#ú~@EÊêkÈØ ŸB³ïwÜíâï:õmÛU#ŸÞ%7P³Í#ovÕáó!¢bAbF|û5"_’ÿÀgbõ…^©ø|pæƒûo¯÷¢yDíºä¼båÀ.¼ÓÈæ÷zyÑg©ëÜcÜ5Š»lËÏHÅÿƒÖõž9®~;¢Ï-+6ìv×³Ï ó-³iŽk>ʽiã«5‡Lð¨ñpPßÓ8rCŸõÆGé¢bkèÎq=éw¹êM¹Asä-àßÖ±.™GIƒn¨xôÁ:þA䯙Ð2çÖ÷Eîyàw/ðŠ¿¬íS/óˆµa— BîØg·{vÐg7çõ²ù¥®3š?›Ùó‡yDÝïlùŒY$òå4ñìí"!ž}Ç;À+þ¸íáOŽŠFæ½^¬pÈËS®{W¹o-=¶2 9¥øx`­µð3t|Ü(œÚæËé•©O5²åÁº¶6Ï5.rï0‘¼íý‹QŠÏ²ÿ|©}hyÄG,?^:=¤q€}_jbá黿\µog¯ G)þXî_ïÏ!æ‘ú~ý~îÖBT6¸éûýÁi"Üûƒ™JDn÷“MÛ.Ë^ññÀdyЕi–ü^ða׆\ú©²¥Üx¬ù¤Î<ÿ¬k¬=~N®¼ì}€QŠ¿¢¢òh‚Yri÷·]z-põGeç 6ŒùJ]ò¸v›µëF¿âûþ_:̽¶ål³DÙ×6=¶úíáÃ"æÆ÷Ën,N87Zñy¿TCÞ÷¹ä²¤ÚZàŠÊ^GFœ·Ý'òÚÀ’[ÄZµ¦¼âû~}¯Ð)%ê¼É¦ ã§n}Âl¹Xht}ðüM.û|­”â)Ør>Zý÷uûÆ6]4:»ì-Q™Ô Ôû|]‘?oá;§®¼ãšßÖJ­Û¡¹Ý¯£Õ¸Ø¯Ïƒ\ô½ýü‚6+>•ãGwþvÐ=®ñ¹6.8âÍìHÊ©q°_Ûý%Rzº=+*§í™9`uûÛ™¹ÆÓZfÓï?m¯ F«ñ±?z[»õ­ÙíxÁH¢rá Åíû>eßK’×›òsÙ¹²ƒ˜÷G«ñ±_šmŸüi–¨spQyãM«Ÿòù_{&`‡È yâÓ7â^ƒ}j?Ñ,‘ËÛ3e¢ò®ÛFwx{£È·Ä½•È­ûí“^/`Qã`Ÿ¾GW²Íºˆ *ŸíYß§Q‘²’•Q™Ù_ËõÚtV}=ìõÅ5öÝ`)`³äÞ[_ lx\Têub~¸4§›±ºßÖH-Tç=Û~£ø½O‡\ùÍ,¹sÅõ!'ß•ÇjÚ þ”=þz\á›w«ëžèjKí…R^ñy¯Ü¶Ë»h–¬Ik?¸Ç5ßUž¿¶jÃÒWíñ7°I»ÎÕÏ7½äüÕõàQ|ßû¢uðn–dvøhäÞ¢Ê8|÷\·{œ¬ÚFçÍ7íõøò•?§œâóÞ9ÖÅ{œÉÝξ-E•ïî=‹Ž¼nÏg£7Ì¿d·X­Î])¯ø»7´fù´ßš%ƒäÁvkQØ©áëçÛó—ê±FÙW”S|.’·Ã®yÅ,‰9…ú»¨Šhõ©yý%+Z꺗áŸ.{x¬â{‘š§Ík¹Ý]TõéúÖ‹v3Nú;ÿ«=o)ù°ï5U|/º_^È0KÚûz»4Í%÷UI5ÍOGúºÎÍó›ßr¦hi×8Z£ô¬=Uã (å£ÀWž¼`–°º|zÇ^Q•ög½©«ï»•&V+þ¯ø^„õöÊû>gIã9X27¸Îɪ–‡~Õð¾Ÿ\÷;vŸ¹ÿ¦Ï%9ï‹ÙóÅXÅQ-X]ÌÉÞωªÛäÓM®{)»™Çbõ÷þ‘vN¤œâ¿Xhn¦Cî¢Ì[$ªžê°úÁÍ¥b·¼ÖQò™Xì<ëî–À+~¿«ö×MÇkãDT½ùcÏKAE~Ý»Ë;.ÖäÕT÷~y«øünæô×õ+4òxñÃBQU2aÉííÙãR÷¯“_k6œîºóËö|4Nñû]yû"²£éø`õ ëÞï’“ª/úåÜWï‰ü6M¾Ïmt“Kßj>ÙvÐ8Å÷wÔþ»k¼;Þm*oP‹ªË=ןu­È·Ì³©.}µÚÚ†Ž³ù=Nñû‰Öƈ‹ëBï—¢ÚóÙvÿØ)7¶~§øþöï?¼6ô‰L‡ºÿ-ª[Z6É£ô¼1>ÏI7ðНoß\‰e:žª?1ØŸ¨—wüð‚ݬJ÷Ïr­k×Ü™ž›=õK·v+>¿e-ϲLÇ£‡»î?qVTGÝ~âà÷Ùóx§¯{Ý;ó€X#g:(§øý–5œ'›Ž»Ÿ›æxëZQÝïJJÉ„¥ö¼j„ºú}m¸µ¶õû85Þ|©G绯$˜yÊáU$ªG¼{×ÝÜÚÑ5¦ù…ý£ÅÚRk#-á\²âû›rzüË6¿Ô=Q­Ö6ýW£\¹k2éxßß°ŽÑ™ëÚ\²¨^h)tW;v[ÛW3E®º¯fËW²â÷êž‚éP÷ Dµ¤ÒãŒKNr­eoðŠÏ…òÒPhcÓ1bëâ÷»¿&ªõ¹ÆnyËoi¢Xûüšá“¿^ñ¹Pßp¨û`®ù¨zçºÍa5cì~ÂhŸÑËkäèÌ>fËI²âsáè—§¾½<Ãl{0e®¨>ôÞê;~xPä3Š{ýÖO¬‘×]Rî^ñ÷õ‹§ï¼mÙ Ó!¯¼u^Tcc›—&ÙüU÷zìy?Yñóõ~J˜óè Ó¡íËê‹C#R¯t°ù¡ôžSßÙzw¼âëkò”}ªiË<]œ¿UÔÔ}nÒó1»½ýå…±†Î{˜bõ=¯ÿá¹óнß<^ñ÷µ™ÑgþLék:’¶¾¿©k~¨i¾»2|ígv{†þ(wÄšÔ‹reNyÅßWÕ};Ó1teåäe£EM{ipåØtèûöÚ~¥œâó«jîºÏâêÓïQâ¨óyևБ;ÁÚø³ÇÕxÅ÷‚­å·éH^î™0YÔô:ñiÏP»µ½æ×z|ºìñŠïú¼ÌtÈíÏ-O‹ëÙvL׊òGeˆµÖ²ˆñ9^ñ}‹ÖÕ‹æÛý¯ÎDÍä¸{ûÖó´íE?y€x‹k>\c £q¶>¯Æƒs_Ì1S´L5I§WÌüÃn‡·®ó­ÕW¤Áåiï¿MPã"¾uñÀtÌqãu~ψšë·ÜöÀcþ¶=ÑLn‹5}Wc‘¡œâÿî}r£­—é˜×ªºiE´¨¹ýÞaÚ$òÛJƒxÓÎ^ñ{·ÿ1ù¢Æµßã»S1¾.¹«y¤ôä;‰ÙãF^—ûôc×}m=OÙza‚»Vüô{§?#íùJÙû¢æÅƒ-“æ?Á|ií_Øë¿½ 5.^QÔ:·¦°ôûýûÚý GÃMí€Wüß™6˜‘ýý¯ö-kt®›Þ Ä¦?ªþsžç¿°Ç‘Óîž ÆÃËÒz¹f™é¸æÀ²‚UƒEÚ/´ëÓã(WÓI·Ûãx‚âÿKzŸß!·fÿ$j޼öýƒ±ìú5r«¬9 ç&*~¿¨öË™G:¯®s[QSåõaÌ#׈|ÿ%UÛL¬“× ?»xÅçÆ„‡³duÍo5ŸÍk4ô¨}OÎÉ'çúfÝÀuC½ßž&*þ?Ÿh=\±Ç¿Ò¢æ«¬eëƒÒlºµ=éÜï]7Ç:H°ç݉Šï;Ì»¿dê6ƒö„¼Vq­¨ùáp“åˆ|´žùì§bµýÐxÅßç2Æ´ê™a“D‡ âx’Eüq—Ýk˜ØûÃ땞âû³ù‹.ç<¶Ÿù[Ù÷Çý¶o¿T¾Ó©Ï\÷˜ô>¢=oNT|¶í®™;ßäÒkÇöýéѵÈÖ‹g¿ôlØk½/8Qñû™¼G±$¡[ͻ⸲ó]t[»KSÅ:uß?áœzšg>£ÏëK–ý0{ö‡â¸¼Å|M‘½ÎÑ÷£œû‘¹».Þ˜ÿÇA»¿'©qðô“=ñ~÷¤=ïÞ '†Åñe)1ø¹ä̵.Uû8vû'©qð”:oÅ.¹ý\Z÷1âøÍ ëÖ¶ç?k#Õµtì›IŠÿOþqpÉ´__¶ÇÓ–;ßc Ž[Çs]v¼ó=bO5_ŠuË­‹+¶É:*ðÍo-Í7kOžÊç§åï øžÚ_:}Ñb‡ö÷¨ý¥çªøH–¿t`Z’nI{[Ro«@í/=Oû{ ÑþÒót,T 5OÅBm-ýt$ؾÒÛnÃ<Óf«Žƒ ›ÚÒ/mÉo m©³u¶£ÏÛmU¾ÒÛS¦}ºŠ­Ô¾HM§¢u,ÔÚW:0“•ïŽÔÛñ²›¿ô]:*ðÈïŽÎ¤;'èxL;´Ÿtí'ö+?é]’µôbíó‰ü®àè Ž®ôY0tƒ7x«öNÝRtÌSú'$@ù÷Ù¥cœFëØ¦”¥|ýFù0àÃ(NùpÒáÛUÜÒˆ«#Hw'¿;ùÝ©¯{‘ŽE ‘ôw¤CÇMÒ1G‹•ʰâŒ2¢éßhhЉÖqE)S¡|¶ËS=¨³4ô Q~¤ßvéŸÝŠEE:–6Ä^V±›¤ßXé{Uú¢Š£ŸâÈÛ¡b‰Ê8¢Ò×Šå šât<ÑÊ/¬å‹*WÇ%Ý7Dûl‡Þ~à’ožûeéX¢”¿YËÕ.»ªV—{Ôêò\Z]þ¯®Ë¥ gé¶^Ð~W®ø'IÚŸ¶LCO]dº.ã¶îvß«—Îg«ŽU|sÒ ²T|s+ö`ŸÍ:¾¹öݘ§ýoS·×*7ßñ:¶ùI“0EûmtèØ')n~Ý⚟tóÛnï³ÚÏUºŽ}R¡ýu§¨Ø…>À7¾9´4'¿y‘Žm¼/ù¾À·ðQ>¼elC+¶ùe7ß›UìËwc‚ŽmN{[žÕ±Í³tìCúË?ZûnÜ®c è¸'ÛuÜÒ­“”ïoË#é6Ì#mòtLDøÕ–~i»JÇ4÷Ð1ÍéóvyÚ8eÚgê¸'ÅnqÍ3uÜêíLGhïLG‡šj-Žà ,ÔqO€ïD~'pt&Ý9IÇ<Gç³:æ ´ÑÞ ‡öߘ¢üKwq¨©ÚŠuŽ®àèzYÇ1opžöÙHÝÒuŒ“ :vyºŠY.§v+V9}Z¬ã“ÓŸa”>Œòá”'¾CÅ_Œ |å#v©XãVŒqò»ç©Øâ‘´#2Eǧ?£è›(hŒ‚æ(‡ŽN:z«Š M1Г©ãƒŸÔ>%³t\ð³:xºö) ±¤cIÇê¸àÑÊ÷»Œï"c¯Äèøà¤ãN*¿k2n‹åW²BÇ'¿K©'+ö ôõ)Tªªo´Ž½½ýÀÕ¾ôƒoý §ßeå'^Æ^é|9(ú¥Ï§¾Òn©µÏjí³\Zûì_Ý> Ð|.ViË:ãÆë$YûÔ–é³:þs:Ÿ:þ32TqX^Ö» }ƒf¹Å§#Ý`•[|:d¦ažŽ MÝžà÷Ü¡}‡"¯^ÐâU¬¦”FI:6ÝY›ŽzïÐ~µ™[š@GÆXÒM£u ièl ¼7tzïÍòw³xíW›t³³Ú·vºŽMGº9ø›CKsêk^¬cÓïK¾/´¶оµ7«Øtr*ó‡_º›omO{EÆ£nI{[^Ðq©W)ßÚrÊó‡ðúïÐqé 3€þ @=­I·NÖþµÁÙ†tæ‰6ÛuL:ÚÝ–>m›«cÒyê˜tàhL;ú¢=F@Cø" 1‚üîäw¾;õuG$íˆ$?øHhŽ¢Ž(ÚEQÐM~4éhÚ ÑÐ 1àˆG1´¿oêìQ¨âb÷ŒW± e\<©bbIÇ’Ž¥|/ú W¼ŠÇcÅÈ>Ž~‹#?ŽtܧNJ‘þÞÐO:ž´ô¯)ãäXþ¾¡¯øú‚¯o‚òûÝz¥¯º~ðEú~ë·K©0+–Ï*G¼¿”qå¨Hér©ÃÝõ·SW;u´S?;õ2øÿFÿ:õ®»ýæÔ£NýéÔVð¥/:Pê6Æ„K‡9õ”Sç8õ‹ÔRGH½ õ»:@Îïr.—󸜷åü%ç^9·Ê9PÎw?iú7kŸÇE:NýYw—ޝɏ¨wYÇÔGƒ\ÀO~÷¢m^”o”¬|ü7a|4GMùÝ~yÓŒïf´¯þ]:V½Cùç÷ƒG-©ÓŸßè·Öàj]¡ýâûèØò´?t'þb wå÷`O«±yÝ ulFh £¾ißñ¨Y@_Ç’×WÚé…*b­ýæQk¿åzÔÚoÿêö[ˆæ“C‰ºÇf•í¯½N:Ÿ­WÅfÜÕ-t‹‹’¥â Ë©¡~’ŽcçÐqìH7`œ4pèxvÈDÃí:¾p ð{îRÓˆ¸¼¨Ë ØFž:¶0x]ÐqQ¨§1°é—&¤›@GÆPp5MÐq…¡³)ðÞr½ ¼÷.55#¿éf2}Yù“÷ÉR±Q¬ØÂ:¶0´5‡/¾”÷%ßx_ð· ¿E²ÅúüÀá ~ÐàM-tlÒ-é¿–—ulaú£õøSÆ:ü³t\a¹vgŽ  ¿äüN=­åO»[SOkp¶!Ý&]Ç>«c Ó§m7+Ÿ÷VLádS˜v”iÎö«t|” …2 £´w¸¬ã Ó˜Ž” „7ÀnÖq…£Ul”Näw"Ý™öw†®ÎäwGgpI}mAôY}Ø…2]è.¤»€³+õv%Ý]Á,õu¯Ò1QÀÑ-AÇgýB…@gc ”ò¡À‡ÒŸ¡”#?Œü0©Ç Õ”NùpàÃe" !‚üˆ"¥ º“ßtwhèŽHhޤÿ#¤?£¨#Š6DQG4GÓÎhÒÑ´!b¨3bÀÍ1—U\ãÔÙƒ:zî™ â&Êx-±ÀÇ’Ž%?–ò½hs¯›ÀŠÝ|4ÄG:cÅoohŠ'O:¾XÅ5”~¾û@_ðõ_ßd¯zû«|é_¤\ŸQúƒµbµ+ufÍéòŸpwý}µ¾¾Ú†“úYêfw½LÿX:Yêc©‹zØ©ƒÝí:©s¯ÖµÿÈÆ“úTêO©3¥®”úQêF§N¤,}øWúOê<§~“zMê2úüot–ÔUR/IäÔ?NûPê§~qê•¿Ò#N›Qê©3¤® ßÿROHýðWºAêw õÁÕºÀ©àñßÌÿ/V«œïs¼s~Vm´ârPg@×*KLJ÷Qñ¬ê{êXV:|…[ø :<íõ‚?^:þ¨œ‡á{cúº ßM.èØîÌ[ÞÔáÍïÞ”iîf…Êôá7Ÿ"ƒª9íòΗ¼ô“ßvÛÊ%Œÿ*;ÊŠ»ý­iGkÚ܆2m2uœ(êjÇoíãuütðtä÷À­:Æ¿wâ;ˆ2A;tló,Ï<¸P]ê ¡í¡´;¨3šÃiK8¿‡“Ž þîüÖߺƒ/’ߢÀQ”‰‚†hú št >¦P‰€Ói³+V*4ÆËo½ £W’Š?%cŸÇAg\®+Þ98zÓ¯2C¼CÅ1•q›ú¦èXMÔÛ¯XÅA±â—Ëq%e\þ‹wû;A^{¾Ö–¯µåÿ'lù;>I÷1´Xq4t|EÒu­:Œ¯:y*î´ß0Þ-þt€ŠËdÅ7¤ë#çõ“uŒ¦“:¾!ã»é'u|Ãt·ø†!:¾!ýìEÝ^”õ¢./`«8×nœË‡tcÚÜ„tÊ5î&—Uìꦔk MMI{C£7ðÞàmžfÐØ ›‘ö¡ÚèC¾O±Žk ÍÁל±Ôü¾”÷Þx_ú°ù-hG êhOýÀé?ÊøQÆ2-ù¤ëø†5µµ¢ÞVôe+êñ§Œ?iêõ/VS^8È ]ÔÓšvµGkp´&Ý:ÚPGòÛ\Pñ Û&ë¸U'u|Ãß>mL{p¶'Ý~—ŽqLÊt ž5ÊÚéÞŽÀBL 0à $Ý ÈïŽN¤;ÓþÎÐÕ>v.VSp8‚AÀч]HwÖ.ôG—³:¾!}Ø•ü®Ð ÁÉ:®!ýÚ >tKÒ1 ÁBÿ„¡ŽÆ^(ð¡´-|¡”#? ºÂ€>œt8øÂ—ihˆ€†ÚAº;ùÝÉï|whèM‘ôo$4EÒOQнQà"M^4éhÒÑÔCŸÄP>|1ÐÛÃSÅXìA}=Àß“tÏ$Ã\ÆZŒ>–t,ù±”ïý½’UüGwQª›8è‹>Žò½=UœG+ö"ø{C¿ŒëO:žò}BTœE;§é¾à—qZúB__èí½ýH÷?‡Žk–¤ã,?@êd9çËÎý׿gûÛïRÿ•.ô°u¯û>š»ýî®keƒ¥nu·Ó¥¾”ºRêI©#¥.tê@©ÿ¤~sê5§Ns×eîçñR?]­œzÈ©ƒ®Ö=î1úÅ©[œ:Å©O®Ö#îºCê w]ÿ,á<×w·Ós½œçÝms9¯ÿÕ|î<ïw·Í圬h³âýÒ.u¤Mžào:ê3êÓ¾:®ìY+Ymˆ¼{2þ½èo¯íÌ­:†,åëƌ½&|7Ù¡††Œë ~oÆksÊ5gÜù’öÍÕ1_Óù0ÖZfêx®ÐØ :Èk ŽÖ|·¡\ð·W[êl =íø½=íè°UÅ[ $/¼À :¶*ßi[gèï ¾ ä ¨P ½.Àw‘s ù]¡#Úº!ëÝ ½°!” ¡oB¡-¸0ð…‘F^x€2#ÀAÝÝIw§®HÚ ®(pFQ&štpöôQ1{ÒÆXðÆ‚·´ÆQ>Žß{û¨x­r˜ÇËo~“±Æ¬X©|÷ÍÔqQio?pô—<”ºXþû¯ØÓµ{ãÿ\öôÿ†-ý?iGÿwÚÐÿ—öóÿµíœ¢û椎ƒš¬c‹ƒ»rP‡±R‡~¯ãPb^þÖe¨ÈØzŒ‡zôA}ÒõÁUØú”m¢ã‚“n CT¬Ó†E:Þ)2èIYOÊzQ¯°^ðЋ²ÀÕØFÅjêhœÀ‡±Ó˜²ÁÕ„tÆièn ²¦”mJ=MjŠñFþ¼¡Ó›t3æ’fÐÙŒºšI[™º|È÷!ßÇáœüæàkNŸø’ï»YÅ—ÓS ò[ S-¨£ýäN?`ü Á2~ÐÔ’öµ„æ–;tlpêiE´‚îV¤ý)ãþàõ—úºÀ@~@‘šîZÓß­©§õ®¿ Þ:Ú@WÛœ:ÚRg;ÒíÀ×®PMV\pÒíešüäw€†ð³4t¤ÎŽIWŧ¯ÁÎ@êèŽNäwG§Ë:.8íîLù ÊASí¢|Oœt©ÛÀ×5þoãCC04ó[0íî ã€Óînà áïð‡ l(í¥l(é0`ÃÈ6 Øph W8í WõGPéÒÝÉëN=Ý)ßÚ#¡5’t$GAWtF‘E:š¼hÒÑRWÂÓpÇP>†ºbèïÔ݃~éîàîIºg²Šo.c’ÇK:Úc¡µWºŠ³Û«BÅ$ƒ®8Òq”íMÙÞÀöÞªb’Ç“O:žüø³:>9´ô}HË‹2f|ßB¥~ú‘ß~鼌ã&c¥õ‡VLÆç õ©œßå?w=ü÷ìcç…«mcçÞö_ÙÇÏ6–zÒ¹§í®ö±ÔsNçn'K½æ¾·}µÝìÔWRW¹ïmKä~‡Uê§Müìa§>øG6°s?[ÎõÎ=lçœîœË¯¶‰åÜ휳ÿ«öðOªí}bÀKù©Ë¸©Ë8© _ëQ_}ú¿>ô×'¯ß áACú¡!´y2¶=ñblx1ŽÛˆ¼FüÖ¿ñùÏ|þÔw)øÏ`mh`4è$d “Œ†z–µ¡ÁÚÐÀÀ3ÐSzÊ@OÍ´Šaf09è,ƒõ¡Þ2Ð[†¿~ÓŒþ20’ t˜3ÐaëC££~'Ådc0Ñè3}f°>4ÐiF7}O—õ¡^3X¬ Ö‡‹¥ï»¡ç ôœÁúÐ`}h°>4ÐyFo}Où7ù7ù7ÃÔûÏÈ¿üÈ¿üÈ¿üÃõþòo ÿòo ÿòo ÿÆ8mƒ#ÿòo ÿòo ÿòoLÕúù7ù7ù7ù·Þ{#ÿòo ÿòo ÿòo ÿÖ2äß@þ äß@þ äß@þ äߺÌüÈ¿üÈ¿üÈ¿ü[wdù7ù7ù7ëÌù7ù7ù7ù·öæ‘ù7ù7ù7koù7ù7ù7ù·Ö'È¿üÈ¿üÈ¿üÈ¿¥K‘ù7ù7ù7ë-<òo ÿòo ÿòo ÿò/ß×È¿üÈ¿üÈ¿üÈ¿¼ßm ÿòo ÿòo ÿòo ÿò®¤üÈ¿üÈ¿üÈ¿üËûEòo ÿòo ÿòo ÿò/Ï« äß@þ äß@þ äß@þ ä_žkÈ¿üÈ¿üÈ¿üÈ¿ÜW3ù7ù7ù7¹¶3ù7ù7ù7i‡È¿üÈ¿üÈ¿üÈ¿ô#`8ïHfzÔúðüOúÈÒkȳ±†<«×énwà/ë;ð)Ú߀Ãí|®ºk݃Ñ>ôZÒ:‡ Ñw©r•ßë,&P¿WÌUw¬;Užz]™¢ïUêµe ¾[•«ÏeNj? ú~ü}6 ïÇgi_úýbºöE°KŸÏøè7Y«ôZÓ¡íÁhe:ýXû¦WÝ—?«ß3&é}Ôíú-—‡¾w•®ß5îr»;Ÿ î;tÝîæ« @ßÁÊUw ,úcŠÞ-tó[¯}¬Ò{²Ezê©×ª ú|g•¾g¿C¿…<©ßCúè7‘ ÚÇÁ*½–Ý¡Î,z]› ýäjß…jkÝëòÔwó“ôýåÍÚB±z«fÝÏÑw42õx‡zSií èµpºÚ#¶ÞW* Ö9’Þ7NÑkäíÊž¶îvxêûIúli«>_rè÷—>ÚŸB’~‡yVßýðÑ6x¼>sÊRgLríl½ÃôÑ~”oy¾dÝï÷Ôwü´O…,}¶tRïQê}êLuÎd½¹ Ô÷úÓµ…íÚnߪîõË}këíe€¾ßŸ¢Ìõ¤ Ó´{<@¿ ¤ &t˜ÐgR—I]æfé­Óžüð%€oåQ~åAç`ù¡ÎÁÀ ¦ïƒgx†€'‘ïDù ¾Dð%—m‰´{(ðC—±¢†‚wx‡wxeüáÔ=˜áÀ ?«–2nLý5¼#ø{ð#™àA[F‚$øe<…‘Ð;ò‚ZjŒ·ôe?øÑà ühúLúo—¾×ÇœUËéïZú¨KþXp׸“ji"}ãJÿ¶Òg­ôC+}ÈNïpJ–Ò¥ô/)}DN î‰Ð=ñ¬ZÆLnp“¨{tNæ[ú(›Lû&7¸)´IúçšÊ÷TÆÖT* ìTpNf*mIá“ÊJ…ŽTú=•¼TòRé¿4Òið2úÓ€›Ü4à¦ñûtðLvºC-—f€4Ì€†™Ð0³B-ŸÒáU:NûÓéÿYòC̪P˪ÙäÏ&6ùs(;‡²sj©5—¼¹Ð<—ºæ‘7¼yµüšO}ó©`>}šA^yà̤­™äeBK&¿/  郅¤R÷BÆÝBúpKÈ_-Kè«¥´m)ý¹œKÁ™EYà̪PK¸eв ËrÕR.›:²Íg68³Áq<Éá{%¸VÒ¦•àX þ•Ô·*ÀM×ÿ³ÞŸª=ó©]Ó3æk×ôµkúÚ5}íšþ¿kM/×óÈý¿ÃZÞr/ux–G­¿!Ÿÿ¤¿¡UÇw`¡>ŽV÷Šœo¢¬û”ñúöÔï¢6_õ.*Ýö!hÝEŠÖ÷*7+ßCÖ½¤ý¾}³ÛýJíO0]½~ˆ,Ÿ‚!úžåfu^l½Pw˜¬÷R»ô™q ~/µJû#:éöÞ=Oß·¼ ï[&è7ï»ô»)Oýî ÝöMd!‡\õ~ê‚~ÿž¬Ï’wè7ðžúf¦òÑb)ŸToz-¿ƒ«ô{ªbí¯(PßÇܬîcZ>‹<õ»øt}Æ\äæ»(Aû/ÊÕgÎÅú¬ÉGû%LÒw®rõ»«]êí¼uí¡Ï££õ}¬L}3O¿§whŸGžÚ‡a’ö}´Yû?*Òþ }ô;­dõÂz¯µUûDrèw÷>Ú7R²~¿Ußÿtèsí@}þ•©Ï·w¨wÒg’u֠ϻӵÄÊ¢uGÔGßMÖçßyú ¼B¿×Ð>•’Õ»ý¡” ž¡Ô3T®m©s(e†ÓÆáàìpú5:dìLË×eFfªwò‡|ó5šzGƒ{$8G®r;ÑgáYêî™õF?D½Ç¶îžmÖ÷E‹õùw¡>÷Ðçà ê\NÞ@zé2 àñúe í6©ß„^“ºLê2·Ê¨ ÐM~ÂIeÚ¢ü Ê¢ü h,?Ô9¸Áôý`ð Ïð$ò(¿Á—¾Dà¡-‘~ üгji0 œã }x‡ÓžqàNÝÃ.ÿg´'Ñæ$úxGð·Œó9™î²?Á?ü#Á5zG;Šr2áhÙ¿ÀËp£MMý ìX`Æ’?–þKÞ8è ®±àÉä'“ŸL~2ô%Óßɲñ¥?ûIÐ)ý–Of2y“›îk)7‹¶L…¦©Œ—©òo`§‚s*0SiK ŸTÆG*t¤Òï©ä¥:ôrˆt¼L£=iÀMnpÓø}:x¦;vÍ -3À?f@ÃLhÈ€?YüžEÝY´)‡²ËitåÀ¿ÙäÍfíœC™9”™®¹ü>—ü¹Ð:—:æñûrê™GÞ|òæSÏ|ÊdPf íÏ K )“z2©?s³¾úKù…ÔµßÂ÷…Œ·…ôû"à–Ȳða }¸º–‚g)8—B_ufQgx—Qß2hYŽe”É&MýÙÀfƒ3œÙà¸N¶ï•àZI›V‚c%øWRß*òfÃkÍ#ÿý;øs®g~WÍu­[œëçÚĹ.q®GÚiÛǹ¶p®)Ü×ε„\G8× ÎµsMà\8í§½/mv§­.ísøbÙæÒwÚßN[ÛicÃw˾–65<·ìhi?K»Ùø_±¯>ëbܹlcw»9µlb§=ì´…v°Ó–ö¯Óö•v¯Óæ•ö®´u¥ë´q¥}+m[i×2Æ-{ö’¶cåÚyúoó-—©üËÉû#–?àxíG0Yù”÷ë,?!Êw|;.ßPX~h2Õ[GëÍÄfu—L¾ïµ]݃“o%œï»û¥«û[Ò§ï@p&ä)ÄC¥ÞÏ(Ê!o$õ ¡Í£¤¾:”úÆœÔ ´i0£è³m øÆAû8x:øqà•±–dÉ›(çfÚ4YêoÚ4iŒ÷™àÉßsèË4™½‹ùÎ =SþMÞ¾@Ûbh_Ì÷×BÓ h_Aþ 9§‚c9|[!‡¾9Ô—B{H|´it^+ÓŒµŒhµ¥5º3äïÀçÀÇ%|2 ýZà—@ëê\ž«–2 À»„>Ëá÷åÔ¹€þÍ’:rhórh}9àËo4/¡Mó u õeñÉwù9R—Ÿ#Ûí9ú³dy*Ë4BCu/‘ó½ÄE~Žœç¡c¹üÀ‹å´'‡zsàEŽÄC¹hÎ!%õ­ÌSÛqÖšÌ9W×î»Öî»Öî»Öî»Öî»þóî»Öî¹ÖÞ£úÿÝ{•z8×£Ö—hÀÒ—hîßñõ^ä·%Ëí-»§ö¯’¥ü‰Z÷Ù}ô{ö­Úߊ‡ö¹’éæóÝÃí]ÎVíWÔCûbI×q\Šôûœíÿ=S¿o/Ö>à£ÿ6ž‹õÆ=P¿×ÉU1]¬{ò!ú{®›¯ÑhåãÐò5Z¤ãºê·î›•?xËß‹~óžéæwÔC¿AM·ß½»|W¥è8/»”ßD+ÖK‚~ÿž§îç[oൟø\ýÞ¡}‘†¸Å})ÖþH}´O«L}Ÿ¿Xû% Ñïâ3•¿ëŽ…Н"cÂX¾Ù µöBõNFú“²â§lW¾£ä~ù.Hú’oÉåÛTëmP¡òe*ßÍXïé£õ›ú,õ®^ÞãX¬ÞÖKÿðV<x4°BÇ•ÉÓþN+”_Hë=m¼~K´Jû¦/Ôþ³<´­díGk«›/Tò¶kß8 Ú?ÎfõWú®·Þ¨w¹–Ô\íg«P¿?ò°ß ¹¬}n]P±i†*?•2VŒõ&i•~Ïà¡ßì˵Z’~»Ÿ®Þ Y¾¶|´_$íÓ~•ŠGcùÙòѾRS´¿­<í_+ZûØÊRï~“Îj_öÛ•-ë„§ŽI“¤âÒ àï ü6€ô™¦Ýàíú} t˜ÐhÒn“ºLê2ódÄiú‹üð ¢ì p ¢ü Ê¢oË0C©w0í ž!àžD¾å7øÁ—L"´%ÒCeèÎ4`†wx‡ÓžáÔ;œüòÓèÏáàM‘ÊM–kTàÒøž 3Ÿ îéÀMç÷YàšÅß³ø{ü˜Þ‘Ô;†òóøm´üÐîÑôÙðwcéϱôçXpŽ×XàÇBç8ò’ÉK&/™¼dêJf,%Sv‚ìKpMà· ü6AþF?M Ÿ&R×Dúam› “€›­“¨w2ß“ŸLû&7™:¦PÇê˜JM…ö©Œ©ÀNçÔ {é“ ®TèH¥ÏSÉK%ïÙ¤ÓàcšC-‹¦7 ¸iü><ÓN[gPÏ ðÏ€†Ð0fò{:8Ò©?2é´'¼YòCþ,ògSn6ù³é› ÊÎ!oysÈ›KÞ\ú|.4Ï¥®yòCÞ<òæ“7Ÿúæƒs><Í \´dÐG™´5“r™àËä÷…а:’^ìBúr!}³¸%ä/KèÓ¥´m)ý¹œKáSõeQ_8—Qß2p.Ç2Êd“Φþl`³Á™ Îlp\ïrø^ ®•tîJp¬ÿJê[EÞhúÑZïÈNîëøqÂÖZÆ}#×(r]"×r]qõ>«\7Èõ‚\8×N[ßiÃÓÏ–­î´Í¥].íp§ìÜ3½Ú–v0|®µo=þcÛöÙµaÓZã”±ÿæ7ºX»IV~ŠdË×xˆzh½‘.Ö¾@㕯vËGMžŠë&}åÈw¾V|¶Ê?¥Œ‰#c¼H?â26›ŒÝ2¸¡ä §}Iü=’ú&2ÆFB˨ Ê/ú5|§þñ—•ØI?7r +6¥Q. šR(;ô©rÞ•ßäÏAŽ2øL§­iôë4঑7Kê<à¦CÏtèžÃ|8šfAËLêšI3ù}žœÏåüI^mÃgôÌ"–,®y|Òù-œY´qßsÈ[BÏ–s9u/ îyü>ÜóÈ›¾,êœþù´=C¶z—CO¸3ÉË¢®èYÌBÒ ù{!ý±<ËÉ¢Þ%Ô›#çvú#‹z–ó½|YàËá“ÜRx»ŒòË©7›zsè¯lpfƒ+øë˜sø^ ½+Á±œ+áÝ*~7®¸Íáµû²µû²µû²µû²µû²ÿ¼û²µ÷aÿum×ÿ½ÙÚ8þ q6ÿqž”o‘¿ñ•”¤c=ê¸ÚnqµÚ_R‚[¼§ í$AÇÖÎS±a,_$ñÚOvžò$Í'Ëïh‚޳½]ÇñÔ±¶3íøœ–_¿í»d«Žå¡ãm§h¤EÚi‚[b§3äÿ±w.PqWw§jWšº šIBHLâ«B$ ˜dþÈÀ00Ãs˜ð&<†Ìðdð‰æ!5ZQ·]¶'îÁTÝD{ö„øHÜÕó§§ê¦] ¬kŒºVãƒlÝcVmºŸ;÷Œ4é9i»Ý¸ çüÏÿÿ¿÷÷ûþ÷Î̽ÿ~_U °Wò†j‘Æ©š€ÁNhU[ÉQ#0JÕëv)ÞΊ[ .¢ÎR¿ââWõIõ–Ž*î›dÅãÙ/k.…¸âT­ÒÅÍ}Xñâ$«L-ªné^U{0JÕ/MSõÁ=’QÔ ¿|Pr„ø»£‡wšª×Ò¢jöËzâ¢n“àÝu C|SÅ9Õ§x§*nÐ(ÅjT¡Ý²Þ‹à2¸jLñ$*N—ª‰Ú/9ÀCuÉ£UmT‡ªÚ'kÃ^¨×A´ª—jV5SûT-¨£Š#9Í'§ùô僓¯ôÙè³Ñg£Ï†®ùbC¯ÌBð i+¤­P´a»˜*‘-Â÷b|(Æ~1rÅä¼›%œK/!¾dJ°QŠRl”‘³2b*c”![fÃr¹)C¶œër°Ë‰·œ¾rÚœØØÈýFƲ½äµ¹ ä*h¯§ÙJrR…*ð«ð¡ ªñ¡šv.ì»Ðq‹¾qÐ_C¿=7ýnúÝäg}›èÛD_-}µŒs-¶k±U'úêè«§¯{õ`Ö3¦ øÑ&b"Gbõ çÁxMøÐ„&î›m"—M䯋Üfú7”[6±ùȧLãÔ‚½ìµ€ÙнV0[ÁhE§ûvì·#Ûfûa¹½ $Ë­]'XÌŸN0:ÁïÄ^0YÖ~kâПø ?S­ái¾ÿ{|!á½ÞéöygÚß…÷v‘¿f|e¯ÆíÍ"K,öbb¹çbÌoŸÅ˜Oì¯"O|º}”Ø?…÷MS÷Já=RxÞ‰ýØ nÿÞ÷ˆ½ŽØçˆ=Îé~‹”ôO>‚.ß2|+» ¹20ʉ±›å乜örÚËÉÃF±ÃÇäb#rÈU WA{%•ÈV’›JòYn~Va¯{ÕÄRM»‹Ü¸ˆÅ….|®¾ÖÐ_C¿›~7ýnú7¡» ÝM`n¢¯–¾Zâ®ÅV}uôÕÑWG_=öꉽž\4Ð×@_Ãa¹$ðÐçÁíÄÑD~›¸oÂvó¹é¨||¾™þÍø²™<ùˆÍGÎ|`úÀlÁf ˜-`¶`¯_ZÁhE§ûvl´#Ûf;˜í`˜/×sî«“˜:Áè¿{ÁÉYq&>'ñùú;W¿k¼éï^¦¿{ùs÷2ý½Ëô÷.ƨ?ü½Ëé¾s9Ûï[¦¿kùÓ¾kQû•i^²³à%ëU\˜ÑÜÂý“ð!napçßb˜›¨¸zz$gæ\°bÁŠE/½XdââÇB·â¦mX©\Ï3+Ž2Úâc×~ÆÓXòS‰Zà!¾á äN;)yçM@6لÊ{¼Í'ÿóûï0q,à~ÝQÅÄ}:ÇB›ä-[… ÑM¤-1Yq0 ›xPr¥âWÆî3î“°„í¤–)œ äkq¯äXEÛr³Ä&— KºŸ¾g÷*Î!l]Ö+9ÍwÁRô—‚©‰ëݲ&{ÆAU—ݦ8ŠwK¾†|LÁvÊnÅ Î{%p>úÖ8ɗн•ÑŠç“qY™¦ø>Šã“XV'Jþ_CŒä Õ]·)Î_üIgl3°™Ž¯kY“(9ÃlãŠYxk»%¨Å&—7kÅ>Œù¼Ÿ Ø6gv°aàÚ †ü0‹8ŒâÛA±‘ÍÀÿLÚMÈeÒ—C>2ÑËÄGù(Û„Mò&ì™°g"î‚8¹ËB7 y'>Yñ1 ½l䬨ÏF6¼lbË&gÙØ2#g¦ÏŒ=3ñ˜Á6ƒmF§Y'9ãrIW̹ŸŠÑ³Ðn{G|òâ·…œYг[´[i·‚åÃf)¸VÆÌʘ—ä ˆ¹Œd½\ûˆÁÎÙN›6;úv0íèÙÑ+B¯½"dŠñ`§¹"ä<ä2²$F.]䨕Ç¿¥`”‚áÆ·Rá 1•é˜\:ðÍ!öHb‰'xNtœÈ{DüØq‚åä:@_%ñW’ßVî½boƒNxUàUËej5~T#Wœ‹>}.òí¢ßo“KØÆË?n°k±ág ÜȹÇÕÒ–¾ZlÔ¢_‹n-íuø@¾ùzúë鯧¿žþúqµæÚÃø4"ÛˆFòÖÈèàÞK\^bõâ³}¯ˆAì³Ðñ¢ã'>?2~düÈø‘iŧâiE¦™6pÚÀm#¾6°;¸î຃X;„âë –æS²]ôw!×ÅØu1v]ôu‰u¸X«Š¿ðï‘ÏôÌ;òy·ØMóë{üzSžaå÷Ö‘ëâ3Õ½ˆ\ÿŠuoøùuäo±ÅZU¬QÃkÓÓÕ¼¨ŽúêóæÈÿŒ\ Ôú/ò9s¸æE¯Z«‰¼Ú¢ÎMÞÀÃ’ SðY .½TäÒ°3/NòÓ ÞjÁƒ)x§žàú‰?*yO5pµÃŠ>‡ûT®×Ñ¿~í¨äKY‡ÍEø¿ ÌuØ\Ï}6í«%ÿæZìe㉼dãCvrÄ:œœÝ’Ã/Y = ¾g`C'Ÿs6³ûäK,6r6Úó‘µ&ÊÇȃ“^)6lÈ1ì9È;xm¯§­€{+¹Ë&¯NÎvÞ§ÌœÍØ¬ïËâ³H\G1~ç`§‚öbÎVñùv11ÛÁ+ÆfØ.tìâ};Xâ.%Þ ñ>‰œ[øU„¯­\©À÷Vpƒ\Ál£ý6|àÄçØAr$_ì¸ö#ëýà´ÒÖ*l0¾A+>˜³!K{@Èa'8&9°–GË}AèïlžGMÿøÜzõ—xõ?ùêÏù Š×Ìÿ[>į˳§ÿ­çNçêo}¿ÎÏž"Ÿ;…Ÿ9ý‘ß‘OóyžŸgŸä~ž Æl|˜³ñy6¹›ƒ9Ä?½9¼Î!޹Ë%‡rˆÓ¬¹`Ç‚‹^,z±ÈÄ%Jn¼8ôâЋ£mmóh›”ÜžóÄ:üx³â–F7›©äãRä.í–…÷»IÈ%a7 »Iø’„þâÅÿ‰Å`-&‹Ñ_‚þ‡äÉË8 —!Pä.ë“< ‚s~)úKÑ_ æR0MØ´`cú˺%æ²½’0Ÿ#…ëø¸̼qÉY¿ŠsÊÅ㇕1ЧžqXi”|õ«N*nzbX öêdÅSO_:öÒñ5Ý£87±k@ΊÌÎk/Ev8]ƒìÚÅIß/ùÿJÁq`s-þ8Ð5 o Ý€¼lCP.‹Œøa×®9'˜&\*eÒ—I_&}fÚ3‰?SœÁ1aÓ„MöLøhbn˜°gbܲÐËB/ ÛYèf¡—%öÈf#WÊøf3þÙä0{\-ÁðËŒ]3~™Á6ƒmFÞL.sË{ò– v.9Í?| ¯E x|°0†ô,èYгÒn¥Ý ž•ÜØ ØÊ˜g×ÄQ€~rØ-@ÖN›6;mvq¦]è¢W„^zv°‹+B®¹"䊰Q‚o%øU‚_.rQ"Æ ýRôKŸ€Q*â'v¯#±n'f¶è;u ë@Î ¦“{'ñ8ÑubÓ‰^G%ñWb§ü µô»hw£WEžüøYE®ªñ§¹jäª?GL}5ôÕ€Sƒ¯n|õâƒ[`0~n0ÜÈlB¦ki¯e jió{äÒµÝzú¼øYOÛõÈÔ#Ó€žg\.i‘kÄF#>5b¿‘yàÅ/qzÑñ ‘t½ãréëÓŒ¿Øß ¤¯u\.ƒƒØh‹‘ËÞbë·ÜæPþuK—èÇnv»³.òÝEý]bÍ®3¹:Óó¤ðÿ¸O}–þíäéž'éYRhC5ÍG+ÖÌgûüH¬ƒÿÐ3¤ÈÚ©b{6ϦþOø9ÒÔ5çÔµfxYGÕ~šõceÔï?wšZ—«eÊ:ðFµÎ‹|îùûÆ»"ÖpágQ®¨s†¯wEä#O%®9ȤvsÆnj¯â¥Æ~šX—`# {iÌ¥TðÓJnjÁÝ{óẓ’ wÕn¹½O¥?£Wòö¦ãKçl0²±³ {`iœ ̃tìæ€•#d‰Ç˜æá³…¸ó9ç‰ÏäsÄçòVðlàçcÓF¿ 2…܃S Žœbt2ˆÅŽ#F¾läÀ!>C±éäÚ)äÄû6óÅ!lÑ^ÊøTˆÏƪL;màæ`ÏEŒnñ™F…ø¼ ϵôÕ ÛøQžƒüV+ú%Þ·é÷b»½Øò€ã#rîÂ1ø°÷à:ÉW¬*䌭ü x>äÈ9Üàø‰ÑOŒAtüøêÇ·€Ð%ç~ñ„¿pŒ·ì ¾Ä®9?˜^tƒÜð? ì [â[ÁAõvE{P|–é_ÌõòY+G|—$¹î±ÐwO}ê7k½²6vøsZün¬²_rV‰ÿÛo‰â»)Q—[Ô¢¿¿¹ñ‡jôªšÖê³8Ð-?#E)Á­'ø¦þÔ¿µ‹5³ò¾/—–>6b4|zòÉž·='´#ÏìiŒïxÉð»ŽkÉüF}$ÿúm}ÎW '†âšW?Iß—ð“蔥ïkW¬:v<Átµ¾íêÅùO?Ø©-ûÛÓ÷êLì J;ZçoŒÞÆÕ޼ô¦ï‚Ár}¤ÔtÞ3 ÇM…uËv}Sß÷lý²›²µ”³nM{Þ¤o}û™aç¶-©üï_{á‹!ðÆBx÷É_óÒÊÝÚ‘_-èjøÅúˆË{îʻõ}!µf}«ŒÇx¼,JʇÄÚ‘ã)kŽ}·JÙ|(ý‰Ò' c/|ÖÿäGèÍ(l,¯þX»öµ[¿¯o+›ýô¿zr´„WþkgÚ^ 8‰!œÞ—mýô…÷´ Âù:™ôÏ>ú´>rËËïï×Þ¬H)7Tï×¾Ðùò-Ùm:±çK}[«eçS[ïÒbÃyõ…À»ûößv» Y:ÿã]ßzô!}ä¾ï_ôÀ©†×¯hoy§Xøðªwjÿîˆx\!½³æV¦ÿð mhö¡çæÙ¥ì¹î³¼áØIû¼Y~ÑŒÃúÖ5Ÿ-›ÕdD¯;¤×óÚ3Ü3'Æaèò…¾y<<þúÈàO¿¼gEÚ¤}îVß9 ]%ó o9Z~ðç+¾£]29¶ï\¼ÿW/Ö†Ö}ŒÓîÓG^/!âýú¾ÙKOµÞy‡¾uÑàúåÖß"/çÖÿè}ì‰|»6$ÒþÑ#á|ê#ŸüËûçëû}°òþêçÂóHß s¡6Sú Žœ·½zWã5¾-ów¨î×÷]–rÉÄü=ÿõ=Å‹ ÿ¦ð÷-Êô¿·[»vÏÉ%+jôm·ýÕ’7¯¹T›!ç¯ñ¸CΗ[þfq𱿞ÌÓûN=¿¹rÂÏѹ ›\l8ž?ñOì¿ÊÞ§oiÊ}jrœrÞÜ`y£ÿá¥?Ԇçžíñúèåç•ý|öÀ¤_qsúo\y¥¾]¼êFO΀í“_í´îц*¹åá“úèÚ”Ñ랟}—VÜt´ïq}{ÛŠ¼-õvôäüðþÒ>ïÐ…Õ“yÙ*ú¿¸Rµï}ñ™o¾2iw¡eÁ1S™¿Ùáyæóe㊛·71߇^:ØQ°úz}´ñÉG´åN¾~â/:Ñ=ã¦ðëTßþ`Í;ŸÕŠÈChžèjÞhCÇæÕ]9ü3}ôöúΫ«.Ô÷]”óOU+5m‰òkûWžGz¡ù¢;î<ÓtËçÚЉ«–=²zµ>ºsÙûEsì†÷.þ››>_ºK8µ}ËZ×ù¯7•ÇÉyêͽnÇí·>Øÿ²6üïwt-K}êÞE—Ízjr¾f¿àûkú¶O¾yüõóŒÇËåûœïº›-ø…Ižû­ÒÁWÿS=ôñ̵¿«|:ðñ’•ú¶ç¿ÛöËK’‘·Þ“VÑ0ú¶6L5³Œúè»Ç6ôÒÆ/Ýî3=ªo{çÇ[–5X‘³Þ.ßw&æÛ°)ö'Ï}ò½‰×åèOç¸sÔÞ¿û¥Çoú‘–2ðú•·Žíз>»vÕÅ©à„Æ]<4´OÃ:1^Õ»Ú¶_ãÔÇ¢3>zkÞ}à˜£ Ïç›ÌÓ»w®ø4gÖ丗‡Æ]ïÄzÏÓAm8ðï?øö.}ìâ;ÞôÅëòõ¦ßáÙùàŽWÝÈËq½qÃÖÅ3_¾mÒÿm/Ü|jE¿>zaM¾/ýlǹ¼ï³w¼µâɪ§´…á×u¹ï[>Ô6–i“qÜÿí±‘¡ïM¼®Ç’n>ïÎù_NŽ›Ì‡¶’WÑ[ÓôžÆ-vÇå“ïßårü»y×±=\þ‡èÞµäècWtî:ôâmúÀc¿Ikuý@ï91üQá-…ÆãN9îÿÍÞ—@Uu,k³UDeFdžQDT6(Ž€ÅpÄœ‰f ƒr &A£¹D“ˆIŒ&& Š1ºq暉(ÃáÄDÀ$b4 b¦ÿëݵOŸëÍý×zï­÷þ÷Þk!ôîêªê®®®«žÔäxÆ&bI°³Òuâôò㔲C-†Öƒ”ïά¦€çr깟&,éåaÔ»ËÜ×mVš¿>rè¨ÃF½+{÷›‚u®ËÃfßyhÖ¤ëŠîBë“ Gó´þ|¼_lrÏ:¿íN›±]뺸0MPš÷;ÍNŸ¦”•øOš’ñ¶ÓnñlÕ{]xã¯QD{Îàý"Ðr+ç†#rßÛÏUÌC½S~[Ù`©”m›ïõÌ ãx˜'xÉõúEBogðþPЋ)äX¹ntbì÷ý•æÙ‡º*×[Eû3uj¾(øYë”&|ð~R¸4¥ôÇårÝüÓ÷V _-½:>8õ„Röúа²³ÑrLRBËÊÝyŠŽôÝ(¿¼_<“yÜüþͳr]Þêi_ç\Sš7Ëö¾óR¶oVíñ Š.ÝâÎöQkÏå½Åáw‹Üj¹îÕâ+Ÿ ¼¨4ô³¸ò‹ƒåôPò18¬®?žps&—÷3?çÿv¬ïëFùÕ|æÔâÓƒ•æâ†¢ßNýXyyîžÊy4Ù'Ò9˜i÷Ãïï…|œë@ýï;o(Í{ïxv—qÜ,»ôÊ…~g®*ù\ßPŽË½`Àoƒ“3Éuÿ8?SšKü2Ýí’÷¾Xüîð3µvVtÐî廾öl&ÉŸ±i»C®÷ÚT¶)ém¥ùX•nUà ¥ìjUßwûJÑññð\Þ4~û]ýh½ùW?Ù*ͧݺ ºÙh÷Ê?c-fì?cn׬Õôø¸ÜŸµú‰ÆÏnËõlÖçÝEi¾¸Åi|qºRöÉÔoj2¦)Ï»Ç×ûx.ç'ƒ²åI¿ùÉõÛZ¾_]}Zi¾|x§ëî—”²²’NV–+Çlßúú»£€'½^´Ålrkƒ\_ña¿®^›•æÏw|>~ݧZû&ÜœÅåûè¾Òò­ÛæËõu;\‚^ŠãÉõ“Ž'ô³érâ–KÖJAk k”çò|XÿÔ{ýGÔÉõ?Ù>~û©½JóCj¾:£¨ÃOa»R°€ø7gq9®]f“=eÜacjpxhôŒH”ûíú¨^\&úÓ‡GëœGÜÐÚå¹ÜVN~øþßZ¢rh€ÒD¿l§´t½:ëÇ1s„þüùåÛ+ÞU ü†•Ñ£<—ã²°èïØÑYn˜“qýÏ4¥¥ç(EŠ Wʘµ}ä;Ew}fœ¯žËiÑ“—Ÿ^÷ã›rÃfó,Æ)-nÙ1ßçMÕú»¢»4çsóÀ€çršÇªóÆIQ¿·í›}ýýµ¥ßñKËE9ÿi‹å™ŸÅ¸9‹Ë/ÍI¸E=ÿeóRâQ£|ZÂÂOF¿Ó ”]TJ·# 7gs¹Rÿ“Ú>µK}g—Ò2àhzW!ÏÓ×âÿì¢.ìùTÑð&”ãó²DtBŒñ_ÿ–g~jh#ø²Þ1ú…š}Q Ï®¬Ènì7ÀÃçiS=’×¥ëÛ¡‘ÍºíŽ þ‡ütÐѯy¼>}ÀoÆùRὫ&n8*ôf6ŸÇe¶Ÿß§ð¼±]‡½!%?£´$}¹6ýåJÙžuŸ¿ó]‹òŒOíWà PŽÏÛ–r{,7NW¥%yñÅWo÷ÕìŽòLè„ßòxÀ«r—W~vñÅ­ûf þ³÷‡å_ªí0uîú³³ÞWÊJ ˜Å1®÷ ëB†¼z«ZÌËg«ýB^Ëí°à›±“wEi™]9iÖY?¥ì §ŸçXOT G½²zø·g«ý@~øÛG²Oø•_r=—6?QiY(½_üø.Zv1ôˆ›RÐ?|ÆwónÎQå.?¾}›áç ïŒvEÕE»g¹Ròø"¡gÇß×nœgüs÷Ú§ÊÞ|½ ¼¼_<Ù¸Úå·”$¹ñý‡6œ$+-Na$ñýéEu⥕S ‡x»<öHW¹6îÎáýbÓŽ? {>-7~”þÒ™gÌ•–Ígåô­¢Ð<¢¯wPŽË]7Öv˜õ¯v~ïÏµŽ˜úâW…²žë£Ò²ÿÛt‡g0oe³ÜÓ•.71_œÃå]«Ž’QÞúˆ·œî~Ki)»Ô|­Ëh¥ìÇž¬E•Bu™0SÌËçp¹o]^úù“¿¯3ö?=ŸˆþwlÖp¯3a79?F»WHë+ã|!÷­w¤‡9S"ëÙp÷íV¥¥ÚZw1þÑø¿kãæ53T¥Àh¿ g¾ðÁ§¾¢]ÒyØæ>ù£ÏW˜Ëú•Ýï¸Ñª´\è¿òîvo!ÇOþ(]³o•RaööŽr\þÛúßÜé¼ü3YÿØ$§u•–kË­gìôOŽoã¿U)<”ûÇ÷sjPŽËÛ';êÚ~•d}ÏäžÝµñIii82cgÞ¥Œúe¡ßËjšèwé¼?lgj©”ÉúínOö:ï$Úñªó‰g®›ŒÇµo®óúîYc¿(,á*7M¼†ø¥uLÎÝcÍ™B>ݾù1õ´Rè0÷vñ«&üpyïS&ÉMÓÞ|ý¡ùJëäN9?é|”ÃPPé¥ðqð\žû»¼¸¨òEnʸŠ­JëlŸM Í¿S'N¸þMú&¥ !C[Â͹\žûW•zÝõ¦Ü„ÕêÂO†+­ múïº!Úká{c†`žÉ÷yPŽËõ-óG1Å5ŽGM«Ø‚v€èß+^z3ìò$å0f;ÇOS Çe•í&æ{s¹œß*‹\pù¥£ž4apÇÌTôçµÉ×V9(‡s—ùdYÕ+/±;E?žËåÿ6ÛV~HnÚ8¶ï ê[JëV ›‘ŸÃ«Co›×Þ×콘gÍår?tù`Öì£rŸ·ç=­‡DX•wûsê6k¦XgÍår~§ç»O`J(7ØÌ˜ùb ÒúôÙfYW”ÃSÿ<åtuRðtÉ—öG"Ïåú3ß3ÎÊM[³ sJkáǾÇ÷+‡ùüVÌKær¹¾{釷~½¾\nâõWZ·…¿»¬r€rxXzbsE‹RÀíoÂÍy\®ËNú|õ…Ü´§ä³ýJëK…7ónÝí‘þôÅú5J_‡¡—ë!¶<¹ð±ÜTjýÁ×îfJ+߉o½Ú°ýòr8ÿ±/[—' þæq9–ן~c è±EÛĽB~ªYìCëï»ìzžÞ úù<.¿ÒÝmYÑçÍä&ÎÒúNòcg¦<&è¾qÒæÛ@1ïâëV¡oó¸K¿¬Úx¥=Bnºî²ùÊŸ”ÖÒCéuÑÿ¹ÁS Tð5€çr+ó˜Ÿí”¹Unún¢_]Ƨ ¿ÜA=¡.ËIþ¹¥€ÏßÏåV–b¸öõÙórÓL1 •Ö#£ß{«b§è|\R 6øŒùˆègó¸Ëøþ›Ü„QåÍ’žJëÑÊkz[ ¹|7ϳЪ³q=Crãã|.ß2²wM|Ÿ\i­’­ÜíuO]àjãÊqùRÿ•›þ|Ç=ðÊ*п _8'þ[Mßï$Ì{eÎX¹¯6®ñy‚˜Îçò>|5e¶ÝÞçeƒ¥þÖ¥]èËQõ J´Ç÷‡ßnýÛ ãüÒ8¿™Ïå_>-þ@Ì{ã¸kèiþ¾ôú ´ïÁG§.ûY9ü­ÃöÈ *wï]|¢øG”ãò.çç_²ÁUݨü˜ËºýeÊá»ÏwûÖJÛ~èvð\Þ1=úwùfœq¼1¨ÛõÉã¸\nç<üÖ4±SÃÒ£D¿ÏûCEùÐCÏÞÝk´;†~‘=0¥<Ön›PZOµŒ»zižq^]²—¤Fùѹ—è× x8Ò8fü-âýáÄÒoa.(ìD¼å <|ÞRþùßî/iS ˜9 xð\Þ' lƒ½B6о‚A]6ôUÊ¿±ÞósÙwJA÷nµCwî<—÷É1êF’l`jÑm«lÈyÁ¾çËÇEû‘}Ñý–’Ô«½«¨ï".÷“4?1ô`7“¡74ÿÝ`\Uðý]Ñnê6–¯I=y?PØöà{:QÏ/o¿óŠR¡^gX£µ7à¹Ü•œ‡±Ò~[ÈÍÆuåÚç|dÃã¿_÷¶¾alç Ì6ëv¸hëK”çrWØ)²™,äd¥n€Ë†'7f·~}S£û ßBïqyWû¿90æ£eƒÅ ú/_J6<ÕÍÚõýÛÆö«àí¢µ_ÂÍ,.×êlaí/Ì­6`ŠlÈS))lµ[/ö¡³¸\« Gßúc×#r¿_!꽡 ºeµ±Ÿ²CŠ”|ÉØ¯´û _jz“Åå^}â=vÃCnBg ÔE´Ãª¿‹í~TðÄ öFmœv'‹Ë¿Z›ÿ^ûõÄF¯(ôOõ\©˜ Åm{Ý\Ñ=ó¼å(¬§³¸œOù²ôkrÓglƒv¸lȘ豲K²qžQ±°h†xE7¥p×'uŠ[—û©™êCnâç$bœœ6­þ­›^¢ß­>ð÷ÑÉÂÞ±ÓšÇï˜ðÏûÁ©mlÁÛŒù®zž)’+Çü’4U©x$â²ÅkeJ>_·žËû?o‘›Ž:§ÁȆ±I¿¬pÊW*Ø1Ùk/+ùÏ&_<FÂÍÅ\ΧÕãñD¹éÝêbk_/Ù0jÛÁýóoG‡lìgù˜eôx¹YèÙb.ÿÓ´ŸÚôv?=D*F&¾6d²¨ï›;Ÿ-Ý-ä>]è <—÷i•¬§Üݯ½+èÌB*:nn.ár>5°°´¦P¬W©¤°»_ÌkúĦSæŸ*º¸ÉU_V="ÆÁ%\ÎgÆøoý…-'ϾŒùóHFQ©PKû)ºÇ±hÙµð\®gæÍYøŠNðÍÏÏ…œ¬§®¶óGãz¢âî‚ß h¯Ã7Ø ðp9ŸÁjyïKÿ©>dC×A¥¾wfù¯ìymŸa¯q]£ûÈëÐkí3D]Âå~æ :›èüÓ@û Z;VªÛ:±€çr>Ó²¹x€Ó¹‰ìªÁüµÆðß=D»ý2{âó¥ÝÅüa —óÙ^ì€ý¸¨˜ÇÝ!Ý¡gÇ?»vÊ\Èí§õ3ŸˆèeÜgÔÝíÉn(‰ñ` ïgDZ8ú¶Ÿh.؇IÏ÷¶qV*îôb7‹„ݦy…q^²”÷‡³O-L]^Šu´Û6ó†=>¼ãFK™RñÇ®?´¡Ÿ'9³Îúà¹ÜÏò}[m?N6ØÅ>éÿ¬q\¯äó mž ìÐRÞζýŠ?Wnê~%ÊOëQþ÷7ÌCŠöî>–º¯°£K¹ÜÏY-=¶å±¹IR/Â`0fņ[J¥y`ÿwÝûLK¹\ÏE[ «ñ^&ö-o<ÕAûèÏ7ô¼ TÜ7w|×_;¯G9.ßsó#‚îˆþ¥ÿê §‹ËJdC§o^´Î.ú|ÏæÌ@oØ?¶MÌEy.çsÛ»d’õul#ò¬—×=ÜÙÛ]©d·8fìRt+N=öÞÏå¨îÚ/Ú"ë«ÃËטÉMìÞÖ¯E{=k¹©›’ÏvÃmÑ˸ÜÎñû…²þ@wÝÕ®M¾ûq“³èÿl96´T»'!侌Ëñ<[–ŽÝ"Ú‡Æ÷¦¦»› ‚~æV&i%ŸŸ_ <—ãùÈ{%E¡Í²þégnÎ 7}ªû9¼­ŸR9·yw[VEg¶ïÜ;£]Ïåwžíêu õÚþ߇Y>Z(•ì¶ãÐ]ÚýÀsùŸÉ6Bÿ&ä9)([ž,7ý}þÜ<ÿ&QOÕŒ¹×»Úý!ãxµŒËõüò]_~´f›¬O9Çn’ŠýÖ +G Š«õM ;ÿ~Ûdã9£ŽZ'~(Æe\Îçù½BÑ~Cs^oì6Tn:;%mTÛO¨Û°:"Î h>i\G-ãò?¯c ºD=Ù5Õ¯ B¯OO}}ÃYQ_¶KÞ倸¯°œ÷‡óÛÙE+²¾»gÍO}–›*Ç&_zÓh·*ù}21Ì`W颖S¿Ø½ä…‹Þ‘Ùia+°ã[ã’óý¾“6û=±ÂŽ-§þp pÖW¾r#[î½} v8»GøåAÄÒçw9ôÒî¡õ Öö^“?Þ]ÖuÜô|Ü.‹'|•Ê¡›^Þò‚÷?¯£—SÿØ÷ý+‡CmÅùÿquâ"Æ×'öæÛìýL©djpè9%_Ýζ@yêêR™87ç÷KD»¯y4oNz¤¨7f[ü•üöoÁÖ1Ë©¼ðê»õE§äF~KÐçvQÈï§ÏÉŒý}9õƒç2ØÍ(¹qÇï=#ú}SºuÀ¤°=š^‰ûB|Þ$ä·‚úºí/7jçiªáS*Ù®ÁÐÍŠîá¡MÏ= x’÷¦ü°D“Ù®®û ¹iò©¤þãcµþ«èøAàIÎO Éè´ÛYn {ùƒG‡A^Éo<%í+1¶Žß—<É÷ñ 'îý¼Znì­nPÊMãßEKä }S‡å ¢_jóº$g¾>—ù}`¹))»ËΗÖ(•ãÚ_šqèaqeÉUÆyË ìX2d«Ü4Ü¡ä|Å&!Ç)k_ï3îŠv_Pô«$ÏÍáÓ ®ö÷Y<Ÿ‰½ÙÙåXÁÿduÃMѱÝèÇÞ4‘Ç?ʵáâŸË£­†ËM´OU©c׿% 7³I~»Õ Êr;¾ò¨‚]n}®þ‡AJåpõþ–Xoe“ü*çüðû&÷xø¼On¢û0Ú>/騢{‹Æóâl’ï© }Ÿ/Þ’v0‚›dýŸßþb?aßÙqà¸ÄüÆjÔSÅø˜Mr?ýñ¨1EËåvüyüiYoØÎnö}€”ûÞy\ÉgË>ۢݲIîJþb˜8¹ôÐ8>ž +~I™.øYr‚­è”|šå™MýᨺòÌ|ÄÕÚGÖ—fõŒµ¿!øY_a´#ùœ,yÏx¨_øB>ÞxXnÓ³Xwo®¬ßºøqÏ]¢¬þ¦|QÉ/J~î´ÀÃyŒ>Éÿ%vÀ0Rn:ãèY!ë·¾úË”›ŽJe–;ÉVòµöÏ!ù³kÛηäÛ%¿}ì–&ë7_ÿ:ôüµG´}Öü}›Oûå½"æY9Ô/ž*b'$rýÝ7®X;_Ö?²'é=“y-ÉQë4>jí <Ô6°Ì—äz¶,½ 9òý ¥rÈç Íã-´ýRú$õ˜ÃxA®ççú²~îÁqÒæžJ%ŸŸ‰ýÒ¯sZÑõ„ür¨¨ËŠ%r}:q”õ“,[Rz }T/ç9Æ~”CòXÿÝD¿z¹þƒ¯Ýþ¼‰ùR¼ºo­Tz«ªâ>tɹ«õ–_[ ×W«3nY0猡ò¬RéÕõó‘)›ù=YÀÓü £é‘ƒÊõü²¬ï¹1ä‚e–hgÿ¡3x?¡Ý‹ö%Íã`Ħvé-×«× a?¸çØ}!oj'MN›gzñþ“mb?%—÷¹WŸú²ßÁ¯åz,"cÝ“¿5cÚü¥F;j|g°’Ëõ ÕOP'¬r£ÁçÎ^›¥B¯øù€ñÇæãÅ1†‰ñb%—ó¹BñÛÓ+äúØ]“Ì­“?šxéƒo’EýDZƒÎ9B¯ì6¾ja÷¥I;p9Ÿc×Ô{“ëç¼êj¹Sn<=åõó#FiöAÉŸüú­N?Þ<—빇Ù˜ïåzöZ"û’ÜxÌbÔRY¢_„l,°™1KÉçïrPŽË÷íËÔ›×ÞO»ÛSn,åçüÆy ¤áòÂZ㺑æ½b|[Ir§úÖ‘iÜÿ7¬pƒ”Š_§ìÞ¾iŽ¢³öõÊ©ªM¸¹Šäì¢.åºæŸ>ˆŸ6EÌ[v?¾eïçMÖü;éÊsùžåë c¹:v]rçrãv¯òºƒ#Œï°hÝ,Ú›ëµè/«¸üÏ^žËÿ…¸/Íç¹b>´éç=þ!;ĺ‹§ñÅB_¹<„¾®âýá,ß_‘ëÔëêåÆ5…NÅÎÇ” »u »•³ëŸU\îg·\ï|èè ¹ŽÏ30o™çì÷l´RѨèJ>ß7<—ûYfŽ·’ë¶©_rã˜ö/ V*˜¼°pJþ£;Ø 0ÀsyŸ )zâô5YÜúòÊyÙQnŒ,ÈK½(Úý›SO½P¡ä÷û¬ò•+(Ïå|æõ¢\Gç.½»”Ÿ¹>NìÝxíübóGµw3b\Íå~æ¸z@ ÚY]’ù}AŸï›÷7=»ùÝPÔc5—ÿöJîáOå:~O^nøv^â˜#b~]qá9ÌÜ2Êgsßg_?ÝC'䳚Ëý »®pó;¹Ž½¦ˆ‚ùÏ~å.JÅqucIÙÌïÞËóLߟm}'J®9I^ôó¹á£Ð‘Wö_3îmúC½`x.ÏÓ Ë°Ïå:¶ªîÛÏØ_˜vÛ¾-êK÷¿6aõ¼¾é!¡_«¹œO³gc¯Ëuaù—.Tmó›òûË¿¿ð†R¡^»ž¯lR²^B9.ïÓ0ÚãÞ‰ýê?$'1Ž­áò>½ãFKiA‰|™·‹à;z¿#)kÔÂ8¾lR'{ĸº†Ëûôëê`ùòí§ö~n¼Ü°v懇rviûÅÊ&v}ûÄ{€çr¥ýKñއ¿Çóõ¸Ríÿˆ¾øð”§¿Äv¢€‡Ëût¹ÚaÅ;6º9­õH~¾p¼Ç ±O§{ã±OnT‰÷~´?doÖP¿àëCþưqÁßЇ»N2‹væïíŒë§ÙvÓ]»±†ú‰:̬‘/ó{}rCÈ‘a9ÝõJÅ 5»^ËWžfÓýÏöžúûˆú©ç rƒû¶£XB>¬·ûl3ê‘Fר?×P?áïaD}nÕ±ôrƒÝ[§Â­šÄ¸É÷׌ï ú¸–úËt¶ñ˜ˆòoWíýý­ó(¶ârÊ™ CŒ²Éæ`ü²ñÓQŽúÇÖ‘vˆzð÷±˜ï­È´8à¤T0uý¬YÙ¥NxQŽú ݇½ÜæÈ^(Êõ·¿YÚ¤TD± ‰ûŒó cÿZKý‚ßwõe³¡Ñ3äúæ.GoïÅx­ªëtãúvß7í¶–÷ƒSÍê˜|ùãÈõërý¥AîÓþD¿îÍ.žQ6-üdØ=™‡{füßרþ;â_0ÿ&±Çÿý~Ù¿,jàR}÷å™ñØf(Û ý»S.7ÜÙ…b —˜øqË᱆»´“?’4ŠñUKq’È?n;ùÇM1‰7@q #Ëf_nÅäËÍ…üæsnjÜaðg ëŠ;lO>Ý@³{ ùÓM"ŸnàÑã“-x²-¢8Ài‡òvÅáÓ­™b ¿G±IÜá Š;Ünw¸˜ûCQý¹GG´—cùrK¡ø5g,bTQìŒ9ÎÈw®æÃŸ êä.àÑ¥†‡j¬a÷›¢úp  ˜hë>í_8—Ç%s½GqÀ‡[ň%Ÿm,}üµå‘¯6ä{"í |ž¨—'ðy%˜ÄFÚ;Â$†0èûD˜Än3‰\Bqƒ½xækÃíÖíÒ/…|ÿ£Nýî‘ïÿ<îMœDþþK)>0Ò¹<öY@;ÅFÛg` âƒÀgp¡LÚ?|çñؾ! øÐ ©¡8¾€E;†¢Þahì°$ŠÙ»Ÿbõâ[8Ê„ï§ø¼è/HG ^Í‹7‡Ç ˆD~”ÅÞE~ÊGðxpÑ  šýÁCÿî§™ùYVýÆÅò8pÀóÔ+&ûÄa~•UÿqÀ10‹ÇVcò>éØbîË™ùÑSýÈ!=é8{ŠÍ[Êýà©ñ òx\^f¾†Äòx¼,.A‡ 7ë°áyf6ü³ gú›Cõl'_g©úß ý·SýÓÙ‹ârî'?b÷mܰæÐ5ó Šý£'ÿö)ä‹õùbM3‰ÙÁ}ݳ¸–¨‡• ù¼U»‰?Öî{Lþ¬c½Ÿâ¹?2Ðì^K~YAÓù6àÑã-x²-æ>ò™2;”·N»ZòW–Â}–Ù·‘OVä÷(1‰ÿ™Åc€ªþô(òïèBþXÑ^ŽíÜ¿™Ñk-ÅÿÉ _¬Õä‹5?Èw?.à×ur ðè‚2½-)îÊôF™ÞÀÛ'‚|°¢­ûÜ£x?yÜÿ*2Uß«àíšü®‚†;ÒîÕ|(õ@Ú4=ô<Æ'ÒžÀç‰zyŸW’I\¤½cMbú€¾O¬I<ŸvŠåžûîÿGߪ¾È÷E»õC»ôK#¿ªµ|¸V}ª‚¿*>t«1{@ÿŠû§@:éðœÀˆ¶ Î@àLø Ž ” Bûƒ`¤ƒ‘š!€Í¤C øP´c(êY„Fh„¡^a¨G8¾…£Lx)Mþtêü‘h÷È\îã5ùQà)*‡â ¡|4ò£3xÜÖhÐìú'‘o;=ÅB½€Çàyê“D~ìj)Æp Î¥"ži,Ò±%<à /Š5„ô ¤ã@#é¸*Š7”Àã¾! 7$Ç\`q…T;ÄþiqS˜ÍÖlµf£Mcƒ3[¬Ù`fo™ 4{ªÙQSªÙOf;5;©ÙCÍj6P³yšÓüT2ûÄl³GÌ1û£Ù;höA³ šMÐl÷Ùx¯õÚØÎÆn6f³qšÉÚøÊÆT6žšŽ•Ú8©ÅÛfs—\îç‘MT_ÔgÈ Ká¡Æ8K#Š<®™•÷™hºY#Ýõï^Lþkƒo¶hkÛ,î+Úßì‘ß3‡ÇsàsGÐwB_rÒóé’sù^FÝܘž§{.Åî²çÓ%¯òÌæÍ€ñC?àõÇßþÕÜ—^ øý À¡£|0h„"/ß‘#cyÜ,&ê˜k pÇP`âªÉÿ0ʱç1®ŒkÃŽyãÿüycÇœ±cÎøàœÑ…dÌê1ÆïéNÐëNè› ãNzŠåŽþÒmÚx»Ä’ïþ*>˜#mžõþûKù0¡ú¥Íâq‘Ôx‘±ä¿ml |V€·Ê!þÈïA¾j‘î¦7ñãŸK±#Q¦»~Ò(v$`l\È—?òmÀƒ-òmÓ¶„üÚ‚†ÒvÀi‡´=òío|{àëû/bGÖ<àÇur¼#ð9¢ŽÈwÏNH;ŸS1ÅŒDºà{sÿýÎhSgŒÎhcçZŠ5:¹€¦ xpÑóx‘ªï~”é2½·øè:}ªøéŠ:»B.®Õ<ºÖõw‡ Ýë^C1!“È??øñtáqêYùÑ&>÷È?øí[ʇb_´‰oÅ|D›õó¢xÀÙOO>øQ?ÔÙ<ù#íšþ át€ ÷Ë€t@ Êcy¼û@à Ž ” Lp¯ ð >‚Q¯`Ô+ċdzÍfû€3ð¡ÈE~ä†th„¡ ÃP>ßÂA#épðáÅcÙG ^m$ðGæqßÿ‘ÈB=£ryl€(”Ñ€hÐìòýSx¼KS€¶ž€ç´uL÷yÌbfªñ@c p¬æf(ð±¹Ü‡2‹Êü«q„tÒqà9®†b€Þ`Ð |C’D±˜>3›Çþ1å3µÏÌö2{Ëlh°Ù?Ï#Ì2{§Ù8͆i¶‰Ù%fƒ˜avÅ4¶6¶³qœ¿¦s8m¬ec*?Ù˜ÇÆ;Ó±ìA_áh×NhËNÀÝ©™Ç¬eþ®-ª¸Okk´c÷ Š£æE~¬ÑN¶€µßO19˜®B~Žhgàr†œõœt|7Ðë|ÝÐÖöÜÿ¿‡8¬Q¦;øïžÅc"uŒ Ò6 aƒ|ð` œ¶Ài‹~b‹:ØEPnÀÛÞðö(o_Jq¸(ŽeÅDJ X–h0힀ßù÷(7ú¸c-Öœ€ß øœJ(Ò½ß é^€qF›:£M‹ˆ½ š.û)ö¶~Ò(îv-û€>h«>¨—+ÚÁ8\A×µ†bAnn:гòîH»#í^K1RxœðäéEñµ‘ï‰ò^öWô½Ð½QOoðä üÞÈ÷±¤GhŸ*Š¥tß\ÛÑi_´ƒ/êíËæWí?õî·ŸÇÏö~¨“_1›í´?húƒ†?x OH €t p¢Î œÀ„2A€ Ž >Ü£Á¨W0h†@–! š!€E:<…"Šü0ð†òa VÍÍC8¾…£^á, ˜”‰Žˆ*n6"QÏH´S$ò#‘…zFåñ8ÞÌœDƒÇhÀGƒ‡hÐìòýÓ(®DÅõÀóÔ+m“Åãš³j|oМQ§XÔ;ð±yÕã-Çvó¾àÛ÷ÅsC½ú¾ÚÍ´ýš¡*øÛ¸üÁ»?èàw~G ø B~òƒÁg0x®È#¸B€+4Bñ; ôÂA#mü6Ÿ?‘( Þ"QH”B;Eã[4ê üý3xœ5ðíÔ/&‡Ç{|Q¿X{ÿfPû‡ßq:§y0Êfó 6–°±&®cßÕì?g½Ð±VèX+üwX+$‘ ô35ƒb¬‚n'èU§,Š™Šñ§s ÅûB»XRÌÔ"Šý6.sô's´©9à- WH[ ¿+pwMÀOÅCyK”·,¢¸``Á ùVèOVÈï†ünÀ× énlí€zZ{á'…â§Öòa¦{ÅOLwÔÕiаð` l‘¶E?°EÚ4ì€Óðv€·¼=tÄrîš=€¿ò{ ÝÓ’â§‚^O´‡êà€ò çPÇ1Gä;¢}Ñ~N€wB¾h9V/üÝ °½Ðž½PÞü;gQ¼Ôv“x©y/ßzƒŸÞ€é ˜ÞÀÙ4û¦dÔuvEWä»"íŠ|7¤Ý0†¸!íî™;ÒîÅrñ€L=À“ð{P\TÔÉå½¼xLT/ä{ÞðÞÈ÷Fyoäû€¾Ò>hÔ¹/òû‚Ÿ¾:CËù¾¨§/êé[MñO‘î‡:õ«âñOý@Ó<û¡üÚ(æ)hú—ðx§H ?é¤3íÀ¾ƒ„2A€ Ý`À£^Áûùp9‡€fàC@#í žB‘…,Âa¨WʇCVáà3íÎì/`"HG$ê øÈn:¢’xÜ9‹5ùÑHGƒF4Ú.<öÍþ Ñ¿”b´‚çàqx€zÅ ­cžbÚ(F+h ÑÖ±HÇ"«ãqêYRÌVð4¨š›§8¤ãŠxì:¯u0ð½Á 7é!i¯•+<Æ?®L÷`Mmó¿:¯7µÁÌöjkÓýYf/M×Ú:Àô¬^‹9Éì³U¦ózÓ3Af“Lí‘f‡4¤Íí™Ía¶ÆtNoz†o:ö³q_ãÙ¸®álìÖÆl6^›Î½œwkc¯6Κžïkã)«g Å€dã&úC§ýÔôKÈ¥ äb^ͧN«Ò]QŸ®è“–…%à,gÅæÛøÖ 8mo›ÀÅe‡ïvè«öà½à{²À;—àï”DaEѶ}€¯h¸¢o¸¢ºá·xtkãñ“=¼(n2ú¤'ú’'pyÆ ýÔiïfЇ Ü}Çt|AÛuóŽüF~0›ç¢½CÀgh5Ÿz…>éð \QèÏQà/ ¸¢ïñØ,&ã”\1 üA3| B¹A žâпnHE¬¶»Ö·ÿ»ÏKÿ'ÌIÿ£û×ÿ]æ£ÿYsѳÿæ¡ÿÞ9è¿gþ™Fm×L±rÑ?$´•œ î„tgèrgÀv†Œ:£.] »]2ð¹tAYs”5GŸ6ÏÃÚÂð øÁbü®Ðå®Ð‰®è]QWK¤-QÞå-‘¶¼Ê[!ß énÀß ôº!Ý mo ]¶FÚô­‘ßðÝÁ_wÐëŽüîÏl¶¼M-blAÏ–Ýl® x;à·C¾]5zìoþnøà§ð÷@~ðßÓ…bÝ‚¿žh ¤€Ïø@ßøsDýœ€Ð ¸œ@«òz1Þz¡íz¡¼3øwF[:#í ^]vAyàs?.øÖå{#¿7ðõ½>hŸ> ßiWŒW®(+ð¹!í†|7ÐwƒŒÝ‘vGÚiwä{¿à=€Û¸=‘Áå=Ï i/ðç~½ï ùy#ßù>øÛ¸|Ð>¨k_¤û‚×¾H÷EY_ðæ‹úù‚w_´]?Ô·ÒýîzýÀðû¾øñCÚmí¿ýñwþ@^Ò |hë@àDù@ð„òAh À‡`ðŒ2Ál~ z! z!€ÎPà E}B!»0䇡ï…> õ ÏáO8Ê„£ÿ„×òá?éÐŒ@:Ñ8ú \Ðî~\ñÍýÝ ¿ÝPÖ ýÕ¿ÝñÍe=PÖ:ê ž€÷œôÄõö†Ü}ðÛ¼÷Åï¾à»/`|Á‹Êùƒgà Àïà BÙ`À#/<†€¿ä‡1]Çßá%ÐmüŽžÔ3|F¡£P¯(|‹h|‹FÙþ–<†9‹?>øb€;tG,øŽEùAHÂï¸ý\n0Êaý§ã®CÇþåÿëyãÿ‹ýKè›Ù/ø¹Ÿ_ñó~~ÇÏøù“î?à? kD ¶I‚m’°F”°F”ºÒ¹ì”;%ÁNIX#JX#J°W’í‰ÂfIX#JX#JX#JX#J°a’3­õaÇ$¬%¬%Ø3 öL‚=“ô܉ª¦;æô&±˜ß ø‡w‰yt7ª™Ö ±tß\Gw¤ôôV*–î äÑzTOï¦"è¾”Žûðoæï¨Ôùe½[,¡·‹Ít=€ÎF2èUÝM¯á÷©Ôµ« ­_Sè̤ˆüÔÒ»,{òSBkÚ"¾wËÞ;ªw\è<%î*Ó¹ŠžÏoÕ;í±t¯=—î¶WÓýv3z™Dç-Eô.²–ÞFºÐ¹KÝÑ*¡{Zz:ƒq¡;)tg«ˆÞMÖÐÛIK>VïÄgнøî A½ËeÏïÇ3ßê KºŸ@÷%òèþ»ù=H£3™"~žÝ—P×ÇftAwá³èÎV ݃Ï#ÿ¥´GÝLkeòBkæbþ~íY³·“ì\fà†‚Ï¡À>â?8ãÑâÁC<ðŃõI\à€/øÀghƒ<†×0àšÃAs8ê7u¸€¸¨Ïà‰:ôâSÿDöuODÝŸ; °£@s4hŽÍÑ 9ù£+ ùI —zc?ýp øZcÁ÷XÔo,x ˜±À1ôÆÎ8Ї2ãÁãxàœãs<à'çМø‰À9u˜ž&g2ûÝdÀ$&0)À‘˜TÐNíTÔ7m› \©hÛTÈ`x˜&ædП ¸ÉhÛÉàq hOÞ)À;x§ïð÷x ¸©€› ¸©È›Š¼©À1 8ÒP. åÒ@' ßÓð}:¾OG™¨Ë |ŸQË—?3ÁÛL|Ÿ‰ï³€g–ž/‡f£î³‡Åtžïs€ƒÅ£Ml:覃§tðžŽ:d‚ŸLÀg>t3‘—‰¼Làš‡úÏCÍþyè+óÐWæ×|Ð]x£jð³ØN Ѧ,6ÒBà_,~Ì"Ô7 혺YÀÍb“,ŽÅh_Ça ¾/î%À½ô–Ö2´É2àdþÉ—ƒ·åàa9pfƒGæÛ7¸³Á_6òs@“ù‰e¾Wá;ó¹¸ ¸™ÏÂÕÀ½4×Ç:àXÇþýuz¾”[oRÇ}£Ž5{žYÇš½cÍÞ±fïX³w¬Ùÿ­Ù%¦ÿÌvç˜uø*ú¾È¿_ñwóø]yÕ?P÷ᡞýÖš¼QÊ¥³3z£”Aï½kéR½÷®¦ûôö¼SÒ“Ÿ X~wH½ƒØLgÁiôV©ŠüýÏbþö[=sñ¢3á"znF~ÿ2è.~5ÃÐÄ":n7y¿”K>Ûè®~Ò_ÜOL1ñ#ÔLwSLî(š‘?ÀòXûÀûpÝío#¿€ ü­ê°”Þ6Ù“o¡,ò/TJwþíé,9‹Þ9UÑ=(ò˜EïÇ÷ó7äêù²%÷ÃÃü£0?'ì5»ïÄÞT3ÿ{ì-;Rý ÆÒûò\zUÅÏŸÕwæ.ä›(ΜŠùy4ó±¢Þôâ÷¯TEyô½šÞM™Ñ»áz;œGï‡købõÌ:€|åÐ=É*~‡K}£îEç×Yü‚꟰†ü™ÑÝÉ4þÆX}¿PLï×ké »=ù.L¢snù0l¦· .üów¤Þ¥ àÓeÕïQ÷_¨¾³ àoœÙ=1æ³pT wÅîOª¾ -ùÝ1Õga ÷y¤¾{H£÷V:òyTEgÝmt–æE>Òè\­„Þ¯ïçgÜê“'ÀŸC;|Äßg¢mgâÛ,àŸ‰ï³@cx™ï³Q÷ÙÀ5üήâË£tàH|:覣L:xOG2Ñ?3Ÿ øLÐÍD^&ò2kxÜ ?¯–/©–¡=æãgàÿà_< Ѧ Ñïÿ"àX„6Z„º°xªY ›ÜYà}1ð,Fû.?KðÅ/\™²x€,ÆÚ2´É2à\œËÁ‹Ëµ8³ÁC6d Ü,–M6òs@34Wáû*|_š«€{5p¯îÕ ¹8ÖÇ:ö7è¯õh£õøöòÔ5 û×áìßîŒÖ@Æõ[ë mkœ×5Úší¨®e´µŠ¶.a붉¡9[g°5[G˜®Ð^êZA['˜® ´µ€6÷׿ùÚŸÍç£ù›‹£¿©ón6çfóm6¿þ¯˜[ÿÕ¼ÚtN>nœOksim­Í¡µù3tÊ8ofsfm¾ÌæÊlž¬Í‘Ùü˜ÍÙ¼˜Í‰µùð-š·Óü—É—étæ¿Ìç[¿7h¼/ØÎß´«ï+óèݺžîÍTó÷ÎÌÿ#»®¾YÎ¥»2YäŸ§Š¿WßCÓ{‡ýü½{ÿÌüލ¾‚‘?”ÙÈfº³]Dï–QŸx”—7 øF€¯aȉ²#?ª”û0UÍ}–$ç$äα(;ßÇ1›ƒúŽdÀLÀ÷)ö\Íb6uœ„r“€R=;Fý¦ NSŸÆ`‘Nc¶éø™>f¡î³P_kx1Ú'ø3Ð6s@{ ûAæ‚þ\|Ÿ ~¡Ü"àXŒzÍï+ÀÓ fÓ ‡¥€Yв+ÁÇÀ®AþZÔ›ÅgX Üs?|/ü\𼯪ɨ%ù¾J"¢:òUCïÛ-ÉVùÃÒ‘O¬Zò‹eO¾±Ò(¾L ÷¥¾E2#Ÿ£)äw´ˆ|ÕïÈvŠ9ãEow3èM|óÃÞ10_“ªûŠESÄ}Ù³/ê;y/òU£§w¾öô6>…Þüñ÷IÃÐfãj¹ïúaàoØ~îGkpÉàsh'f¾® ø=å' >ÀÓÔypML2ê96—ÇŸIµ7yŸÇ}ܨ¾³J¹?Ò!¨çP𘆟xð10ñ(òñÀºÆƒ^ü=>EO\þH¾À&€×´å0à\ÃAs8è G=†ƒ§)ø=p#7p#€g*úËHð5ôñ;‘ý\"ꔈ2‰€Mì¨f>õ š£As4hŽ®Ñà+ ùIm|I0ø’‘7|ßcQÇI¨ßXÀŒޱ 9tƯñàoðÎñÀ9åÆƒæxÀOü„f¾¤˜ˆü‰ài"ò'‚f2ûA¹dÀ$7óåF ð¥&ôS!‹T´k*ð¤BŽiÀŠþ3 í2 |þöÎ?¶­ëºãšaÎajlÅŠ,²â$^‹fŽbÙŒêQ¿©ß´(Š¿DŠ¢(Š"EŠ)‘A°FXÜÅØ²ÀÈÒÌY›Fí°ÁE·ÅÓÖH ÐÔÙVD¨¢U-@ÛÒNkþѺ"Ó–¦Ýç¼{é0ª¼&h“8«|ïÞs¿ç{Î#ï½$Ï×…_qºàè‚£‹úÁîç¸Ü~pûÁ퇫›×ávô `7À5 Ï† /ã¼ðñâËK»ví>Æø‰ÅO»Ÿö1hР}ƒp Ò#ß1r¤}œã4~†ÀJsž†wZÖÌàd‰'+ŒÍÓ4c§7BŽFÀÁ.ʸQb¥o¬Q0F±a#Y1b#Î1°Æˆ1NîâðŠG¾ã+ji•€o‚ö¸ ø$ð9¯ ò0æ˜)ò“"ï)0'ñ9Iî'Áž„ó$ý|fð9EûíS‹jY–;vŸ3`Ì€1#ÇøŸÁO¼h‹r=Íõ‹ü}Ðëô²Æ0×å{©²Nõ€¬Êçú2Ï—9½ÌÙK{¤¥yziN.óo™[ËœZæÒ2‡.Í—e޼վ¨ÌƒKsÞÒ\—ë{i^ûågç±2‡½æªïæwÞ×Þeç«\÷Ÿ™«n5O-ýV ú—T·Ù¢êý\úMu\ý~ÚÔ\«V5uä7Íò[Bó7ÌgÔï•¥.›YomNývO4VDãBjµÈVP¯ç&^/½Œéå5ÚŒ}3ãZ±íÀ_ýro—÷>ìú¸ôqÜK_¯œãÇÏø޽ÄãÇOœƒôû±$¾Aø2.„MÛ0>¢rãq Û,ܣę—{!¶iÆŒÃ1É5Lb“¦?MYâÌ‚‘–GxåÁÈË8îqÓäy.ÓÜw¦á’÷ìBØxÁõÇÕVÓ m~ÎCàú;ºCÝÂQ¸5Ö Ï¯ y Â;H{ˆ¼ã{”ø†à<§!ò4„Ÿ!Æ c;Šÿú‡égÌ0ýÒÆÿ(¼F„× ->À‹b; þx£sú'ÁÄÃv ŒI¸Mó8F[œã8vqøO7.÷kx&ÀLÀkšñ ¹'ƒ9Áuš"pKa›"—“\I°¦ä^~Jm¡MÓ?%màO‘œä^Óð›aÜ ×a3à8/€³ý=YËÿŸýÓíïÉnï¡nOv{u{uû{²¥=Uyïüf>©óoêlÒÚI§6ée‡u½ÿ­·yH×UšÓu•ª´†Ò¬ÒP2kTèzž­½¨k`Wkýì¢ÖSZÐu–ªµ¦RQë,èzKÕezÚsJ_ɬrHk,u½ì ªf¶©ÍY¥õ¼Zsé”Öé\PÚKfÐ ] Ô¡jk›ºg•v€©Ý¹C×dqèš„§TPÑãíS“û°®Õ’ÑÚÜç”¶§YÇ©B×uêúܧUnÑú4ëˆVéZ¢a­ùyVÕï6µ»whí¯Òä1k=]PõžL‚ ­EàÐúN§´®÷‚Öö¶è:PV]{´¨ë^кO;´V¨CëX´6¨Uë•æ“Yܪ´¥¸YjNÕ=¶ªõ@+t (»®9ZÔµFêF¸Yk欪;jÖœY׺ ‡µ6c\냞ÓõF/(m‚{°³ÁÓ¶ ìzxÔƒYO®êáP^ýªž¾cgÇΞ<;xv|ÚÉgX `5ⳟÄÒH,ðj® »&ìšVÕ´¿™˜›ñÕÂc‹]øt§ Ž.8ºˆ¡ì~ŽûÁí¿¨–2nbƒ×vô `7À5À§ ^ÆyáãÅ—ví>Ú}Œñ‹Ÿv?íbР=@û <&à¤=ç 8Aøñ=Æñ Çq áwHÚ3Œý0öÃà Ó>Fÿ0}Ãøëí#àó®o¬(ö£à‚?Ší(¹ˆ‘Óø1â#æ±µäŠã7Fœ˜âp#ÏÜ´'ÀNƒ„ðç92‡ 0'ÀL‘¯× æ$\&á6 ö$yŸ¤?ƒÏ >§hŸ¢} Ÿ9°s`çÀÎásŒ0fäÿ3ø)sÁ©j ËœÖü+¯—V¾n¿Üz½´Vk[¿Ãò«¡ßÁsîÒð =ܼ֓µ¬éx\v WZ»Éº­|ͶÕ:móÚ¬´+ß/_sñ:1×Y¥õ•¬­Ê¿S,k©ò5Tùš©´^’uÒVë£ò5ÏsTZÿü_ûê¥5ÍVk™íuÌ{S£ç°åÊÔž‘GÚwÂgW…®£Šm%˜•N­™º¢j÷—ê šõRÃüŸQµ8M]Tü߸ tQMÍv«ªÍoÖ…§Ÿ]Õä¿Óû¦þéa¥³nêufTýøêÓª®gµàœR¢ÝRfM\ë®*=xS;£õӄϦÃJ'RꂚµäÏhýMüÄhpnœU5èmø°‘£ãçTÍÏß©ÖZðUJËSný†ÌuàÛA¿f}Ø𪗶E¥ÍÕÏ|wÀÁ _ pnƦù¢ª»ßŠïÖ3ºªÌÙ¼ªjë¥wÓAŽ:hëÇ7Ïšz«éˆ«¨|x6ÔÛM?|zÓkWšZNÚ#Ä·~'v¨z¦<à£oNii Ð:­´ÄûßwVéØà³¬zúëy /ý~ŽqÁ. ÏFŽÉs˜¸Â2— ¯a¸EùÏ‚¡-͘9k㺤éwÐ×N Äž—\ržd|Û.lÆá™æqŒq]ØŒc›kœ¶™Â?OŒIò‘%Ž,\\ðÌ“#—]mOsÞOìÓ2·£ûÉí^Ú¼àzióÒæ£ÍÇ8?ø~Úý´ûÉ_€k =@û ƒð¤=Hn‚`á¤=ÆCØÁ}¿CÄ5´¢¶¿‡±Æ~˜8†é¦o¬bá€?B#ä`¬(~G±üQpb\·ù?ƹ#Þ8¹ˆã7v\rÆ8yIÀ'A{ìØ üMàk‚k5æÄŠšv¤àsŽ“\ËI°'á7IŸ|NÉ|Žö)|N;v.®¶âgÀ˜‘cüÏàg†+ý¸Ëé&É{‡ù·ýù̶nÒög3ÛŸÍlþlfûs™Þç2ïä3™·óyÌûôY̶æ×ÏÑü²k}J9güNr²3¬´kÉÍ®*]›N»°Ùf¥Wë“êÊô6”¶ìn8ížUÚ_Vï® Uq¥¿pdUëUk`°o¼¨u‚»'£µV´V°Ué1ؼZ Þû𹯨5ÈÝþ*­\Ôº +eºÁ³Jó·i»‰±7YËôÕÔÁÔh Îjž+ÕÕTâ€Uë4œQ:ˆ¯†¶šŒÒ­¯YS:ÂRßÜÔ#ç7¯—i6Àå >®h­0Y»xµ^xµ`ܲ¡5Ãh·b{7|n¥¯‹<ÝJßmN¥U» t†EK¦2RO½v]MUñÛ²¢µ…ðYwJéõöðß%íô·Ë9øÇ|̪5{ñyŒ1Ö³J«Wt¸¤öºu]ÿô|]ëõ’³ãp8~Hk÷ÂÉ6«õèk„—Áó¨eCë÷§ 6|ÚÀ¶mÛ¶¿>ògðhàßÀ¿Á¹|îãÀh£ à6`×€]ýëjzÕ„ß&®o“ÌßÁíÄ®™þfx5ó|j&Öf|7ã»έøk§œVüµbç Ý!kÚcŒq3Þ!\xl—œÁ¹]Ö7à÷ïvùÇ'ñwò\éĶœN8w2¾sNMéºáÖM<ÝÄážUSºnøõ¬©©^¿Q0|ä§Û^âp2ÞÉx'Ï '×ÚMŸ|ôaÓ^±ö1¶Î}pî#ö.±g¬ ®.ƸàèâZ¸‰ßMø…à ßM¿üyðáaœŒ><àx°ñ·˜}ØùÖÕ´ÓÇccØÖÕt¶Ax ‚=Èõ Á«ÀØx!ò"QþCøs–u aÚÂÒ†ÿ0#ŒŸˆSM5c`D°Íà'"pb—ÙPÓÔ“ØÆxŒÁ3Ï}cÄ0Ÿ8ãr´Ãoœó"9'w)l“ðH’Ó$×¥(ÇÄŸ‚S ûi0ÒŒIcŸÄg–ã<ÇE8dÁÉc–Ügá—‡wÞy¸äÁʃ›7m‘ØŠŒ/’ÿ"ññ]ÄG÷ÊZHæ­òW¾/^þ]äò5ÑåêHlëÕ½}½ºÒöÇ–­÷•KóÓÒüs«¹eù÷¨·Ú?.¯?!sÃÍóB™ʬ–ë)p3øÉ¯[î…øËÑæÃÞC~bøða_ ¿€]¶Øx„à†sq¹ßÊ=üŸã?ʘ”Œ'ÇAî“ø.À1—ùN yÎÈ=Œ¸‹Ä“âÚ¥äÞ&÷Xâ.2¦ˆ¿Ü†º]ä±)©Ÿâ¬ÒR Å_+¿÷]éûE„½¢_ô{¼WÊ>Ñ»µG4gùÕÙz/õ Kï©ïÕwvßɾÐÏÛº’öƒ®ô½ wó;ºïǞײ­Ã¹¸…g…Ò€¯…ãõ‹êmu'¯‡B_„¼Di`!®ˆØr%ßQòÅ6Šm¿Q¸Fá•u<ãpŠ­«)ëyƒg|CM]DZç< Gàœ—sø%¹fIð“pIâ# ~j]MoÓऱK3& ¿,ÇY¸g9Ï‚‘%Ö,<³ðÌÓŸ‡ypóàæÁ̓Y„wÜ"|‹äµß⺚ß+7`™;Êßåö}Êë‡ÊúæìûlþziïþÛú±–_ž~liÞ»Õw7ï#•ÏSeŽºÕ¾Rùž’Ì57ÿp«½¤ÒœRæ“—ÛS*}q«½¥ˆå­ûK–·þž¿ô[þÒ<®|îöå­{IaËû¯§[¥4¤EG|'qì”ù*Á­ä¸r]éÍîfünrv„±GÖ”Îó‘u¥-+/=ø¶±—xë°½›1u2ÇÀn?ñÕáÇ ÝŽOCÞoi3à`g\³CiXÛÁµƒÙH<-صñØvAm cl<1¶ ì6égl›pá±a‡z©¶1¾›Æ5çeU½\[hw`Óßþ›ÁrsrÃÃM^Ü5“»VÆ:ø÷¿kâæ10Ç?\‚øt0&pN]ºvÚ|Ä; ¶OÞ—°r”û,íaâÞP[ ø òÅW¬c<à»åÞ ·¤øÅWN!ÎÄ™3D{†ë–Á.ÃuMá7D>Cp)Hç8eÁ6LÜ|gÈAF0ð›áºeðÅ_óÇ9y$?qúrœg8NÂ-ÇyбY°r´åˆ)Ÿ<>‹ôÁ/’¿"Ï—"þo+½tÙë’šÓf”µÇ/µLåûbR3EÞSä·ýò3ÁZ§ò~ õGO~k/ŸÈç òÝ:ùM½Ô•½0ù­¼Ô•Ï d_L¾«%Ÿ-wy‘z'òÙ¼—HMVù¬B¾Ë%ï_òy‚|¯N¾/'õV峩C ï5² c~ΰøæg ¢ {²OiÖ±¨ù–Ô›•ZCæïN«fm›9U¿%8§~_ ¿?÷F©¡.s™³Èï&d.!ñ›õÀOëú ý^8«>ÿßSÉoŸ¤ÞÊ/úwÏí¯NÏÉ‘ñwÏÝ}oß7ëê:]ÑíÞ?n,={âöçÿvíÒùòsòÇßy½þÛÉe+^éߌ4ÿÌîWŸª¸9jÜ~ËÉ=ÿýïÌbüùG^[3j|ŸùÖó¯? ÊÏ_?ö—s×û¥§þù;÷]{—±üÅžç®úáßÔ¿u|׋öWg,jÜc}ÏÞsôÃÆÒgç^¬²œ5–¼û…ý ƒ}å—r?8?ràö«ò`_­ìgêþ­ñño×ÿHáKŸ{~üׯÊXÎüù·®±e矹zöéÆß¸Î¸óó9쮞?ù‡Ÿzñ¥ùkoWã»íÏÛν™×'ßÿ{Ï+ƲwùäGs¥|Î?sçoþÊ'?6rtí‘[î¸Þ¸æè¿¼º¯écà„Îï¦ÿÑ?0–Ÿ<{ûƒK—ø,ß{aÿ®W柩øòáÏWZçO^÷õ‡ŸL<ʸY5îß^~í c鑇¯{â‰#ÆòmÉ¿´wfþü®»î»Î?ð—ÓW-ø°Wσ…õÃ÷ßüK¿øéUc¹âtæõW?Sÿ‚æyžè®þäJ×uþ¥½ŸŠý÷ÆUŠ8ê:/|#ÿȽ;׌¥?ú«ìuO?v)þ—^ÿÍ{¼óç¿úÐǼvþ—ÿôµ&Ö§®ë¹Þ÷ôO¾f,üûåû¾þ_ÆKß[øêãÿËÞw€Uu|ûºA”¢RD齃 ];vì5bï‘hT4À† 5Qc¢bÇŽŒX7–(v hT¢ÑØbLbOŒï7{fï9×ëÿÞïÞwË»÷é÷sff­5³Öšµ¦.ë²®äð_ GÉ3Y?'<œÄøY²öÚ€ýF þLV/Àj‡–¾XÖìL7¿¶ñU¹¸úòæ”NŸ“€K¯¾*h/ϤR6*W¥pßK–î_~âV¦ú» ¿ø§âÏÐô@Ö=0[ót×cÒH…·cx¿=£âTü€Çä „¶6ÍTL=J%€\\µoä©ÉM]×ò6x}µTëWÎG=º˜”$ŸuÙ¹ö‰hïÄëÛW?íÍ\~Æ&ÁWÐGÕàÜNâÌàȳ\KZút|xL>J|WfÅ®kI*ÆþeÙïyrq¨ÃÏu¬Ãe—発9 ÖÛ¢<“ùéµ› Û„y|åÉ/Mç‹m«o°•gÚö‹^úú3‰É¼uÂàÿf¤"¹ØûµC]r1h µÃ/dUËsÇäÌBùZ‡#I(Ïø/ 3iVê*Ú—dp~ÔmrÑæ“uŸÍ—uŽ1}ÿ$¼ß2—´;õvL˜Ð—4&²í®Fcœ,‰=®zû "o’Üz5¿_vüEÞî_Ⱥâ¸ßÏuIÓßÌé]ëOœwJÈs“‡C¢[Ømƒ\Æo˜b¾ˆTÜ=dæ½^²îÛõOÉ™ƒ6Î3å¿м±dÕRA[éXN*.…Oéc>Ëã>X/É™_7ò ߎòŒ¯‡ò— úø3?R§»3!†ŠcAË¿–{ ½ãú’ ¥²Iz‚zŒ‡æ–Ÿ(ν,ú«iŸ­®_‹*÷%TÕ“uÛK–›y¸¨ý„zŒ‡>i±eòþ¢biG,%-'¹”ž/Qû‡jw?O9S>0N¤1¾Rº©Àqö÷¡ûÚ’Šeg¢ß^6—usmMz–\QûõYwxƒõ¼t~+©Èòû¡Ã·‹d]ÚÖ:Yëgz'3~D­Ñyq¤BaÓTQR“ÙvʺÏŒœþêšœIG÷p#Ôc|;¸q€OtÍ A§÷°Ž¶a‰¤bˆi«‹Î›„Œ}ÓkÔUO„½™Ìøyp²Oõɪ ×oV”ñlSRÑ¥ûœ‘–¿>üùÒÐóÍ…<èÝÿ—„|Nfü>NyÚðÝð@G·“‚žþ m{¾—œñ'Ùxã¯)¨Çø}Òta­3Ú±=w™Txî¸úrÀßwjåòÂrƶ;nÚ…zŒßú ÊòåNÑîö®™xƒT˜çb¤+xG,oW¯™™œ‘Õf¬Ÿ&3¾¨g×àÅËÓ¢ìóÇ|úÇ@Rþªw£ 'n:¦~½cqg79#¼æƒvóë‹ñp2“ƒýç^õë”K*êîˆÝ¡·FWùíÎÇ-žÜ—us¨€ÓýáÛŽ›k:¦0yØÏÆmRaXµ¹»k )?ýŲÚö²nÚýè×{ËéÏNÂÅ@y&û6LIíFÊß´òõG;5úËö;Vc§BÞ‡lί¹t¸œÁÇ+m˜Âä`_Ù”fó"Iùó{¾m7 !åÛ³-‡ö¾ ÆñŽ#m:O”3öØ%ßj0õ¿÷ ˜ÚðjRþ0ÕËà¤1)_u~PºAY×f–»ùù 9cW•ÿŒê”g|Þ{soÔ7]kò~GÝ|]HùÂVÄߺyùË3[‰¬£ì»¾xs}ãü"6½Œï{y;ʈͯÑí6)Ÿ1içàZ1²®ki›5!>?÷‚g°ŠØ18¨Çø\|²qݯ[í!åÔݱ›IÊÇìMøûìaWš­ ‘:çhv%ãø…>Õããsqü—[b÷­tN±[NÊûÄÛD~?BóûtþoŠÎxÖ&LOT¹ró9ãû:Ü]Ú¨òýO|M|Tyí\©àƒ_­A':ïÖìrÆsZv%‚ŸŸ3¹Ø3:§ÅWµHùú¿ÖûýbCÊ£\wN Ù.ëšÖ.mßÑB°QžñÉW[^,ü•”¯;ý§[cD»|Oo¼2ZÖu0ÌÝ£TΨy©Ëåùè‡Ïÿ¿Û~)·d{MR¾fÀO/†ü-äÏqSnÑbY×Ea”6ndØ,¬zñõ™<|Gݚ홤ü›Ï»w×Xàµ|r´ƒCŽèG.Z?2ýãÙçL.¾kÚèp»¤ü+{¿¯C‘òZÏ¿l=>Sð—Ù]9ƒš½c°7Ÿ3¹øÎ!åbÏ SAÿ’UhÙa-}áMÁŠaNÄßWáp9 eþ¼^ÿ3ùØýd숖â?¿ºÿcÁ¼ÞuÜâÉ…ýç;Íí_­Ö7§†¥—Ê/UoPŸñ¿£‘³îµàÿÚa“È…m6ÇSÖJ‚Žº!#ý팴yœf¦2¾¶£ž8)Ïþþ0)+#V„Ÿªã ì£CÎè{¶I~rÂÃiŒ¿»~e‰¡†”gMA Ðþ9·‹ÆÞØ-ëìj¾mÑl•œ~õöÝcß/CyÆÏ]ÛÌÃå½¶|Q4ˆ\øÌ¨ìµ¬ó²;|pƒ•œN‡Å[—Qžñs×ø¥e‹¦]mýýÍTr¡á/O8 ë˜$§>^k²e½ãiŒ¯»štŸ°Î¡ƒàS.-¸•\èä¶öHd¢°/]ó[¤guÑø3cµT<ô'ð{ãwÁ‹½Õ)'’òåý©FŒ¸½×™Ìý:pàéMoÊéÖ b¥Î¯§1þìÿäò¤Ô)‚?ëËáawÔôëBÈÓ/ýþƒ¬dº¨Ûä?5}Jÿþahì­ Àa|.ø¼ <R®kºsÁ‹ ä‚Û[ãy±Óe]Ÿ@:£•3Æýjz'~û4ÆÏL2<5 å%Ê€L.Xw©ÿmÛIªþËÊ4Ô-áátÆÇû¯6z(ú‹z{&ÍÅ8"­ŽK=¹Lè­âš¹æãšf·¦3~ï4¬ý{\é R~éü–³«f‘²_ú[çœ}+ës#§³þAyÆïýC~~›#úëvüÛIÚ|–”]ïþÍÀ¢ÿ™’Ó×Dþ1¿{¤ðë¦39Ø^|åc—6¤œû-jûÊö^Ü|A 1¾:.Þó·ñN9ÛeMž¦39ØÎýÃò×™÷¦·lƒQíÇÊ:Òˆ!¶×ÿ^'¿°rØ­G“‡mºú%ÝH…ñ/wÚ'^$e_¸ÒŒg´™NrÓO”g|ߺ~Iéêø¥öåt†BÊÒά˜t硬ë®,ÄÈíºÿ8W^Æÿ­ Ö×ú±©ð ß¶óy)ÜãÁc—šÂ.õnòWlçš_¥Ù£L¶À9nr ó+eœ%e]æ\KzZOŒK=~u¾þ°Š¸«ü_L'\Ÿgãÿæ“7î63‡_® ¤¤Œ|–]¹½è÷m> ]HÈóÓD¿Í`r±¹ÝÓ'•-Ë„¿<`É?_E~qkެëF åh9ÞÏÕ‘¢_f0yÈçþU'³§¬ úöô´fŸ‰utÆÿ 'ÎÎË Z)úãùfâkºŸœËœYññžVª=ÓÖÁ2ƒ·ÞÝDÒ㓃 Goüœ1ó•Xÿ²ˆê;ìæmrî£þ'îêÞ yàr¬Î2•a¿»^2ùPfaÃMÉE÷ç,obKÎEôÕõo: ãÃõÃ?/&g2ø 3˜<¬§V=Dë[a㻽NÎÕr¤<­¶_F-üÆÚ¶ß»ë8€Ãäc=¬sûkkD;mŸÔZ°—œ}‘¸¦wÆTюɯ—ýØx·<³Õür¨"ê39XwgBL|I0¹Èçqg+«k̉<(ëÒ³’¾yu\žEÕ,tÊ3>¯SÜŒÞäâÄÏ. Ÿ©á=ûÝ–öËguÓsß.óøL¬Og0>¯¥n£Ý#rqf\ŠÁj;rvẉ‹¿ò¡,–gVö.=Ù¸ê1~¯åü»øÅîE bÉÙA?m½».JÖÑeõ_ªòòŒ¯kHë> ÖÆ£‹+WÂâ¸jvïl“½Î©7ªëš§ê­&׌¯kn%wîðÉ'Zý‹;6µ©4ºKÎÖö¹Øc½àSOűëw*Ÿ2¿×|“û`Õ'] 8ÿw9sm¤SÆé6¢˜=òö—WÏ­7„?”Éø¾æ£Úp¹Z’‹Š›9\ãÙÝÃzLÞÙ@ð}h—1×gÊ3gMü}Ãú‘¨Ïø¾F]·<·"`hn29C½'Œ»ŸN€%í&Ïü¥+Z0 åß×ô{Þ£oןÉŨ ž#g&y¼ÎiÿJÖatšy>PžQüjWèP”gü^ŸÑªÞ›S¢ß®°sõ¼&ïgú5{=±[ •ïbŸa”—›šãƒûD;f29È;YU$—šÐ}krÆè­cÆoµþÙ9]:­P×ÚZÐßDYøÓäwvNæŒ5yç…½žÉä Ï}PaÏ·b^|‰­+iéÓkü?îúÍÑ6Žküý}и«V!zýÂäe5['—”í drú³Ö¯ö~yKÖ)Ó²Oä9õ¾|äŒòLV}š=çŠs¹4îûS<æ‘ÓÍì^¶l ÿ¿¾²ð&Ïqê<±å1NÏdò°ÊPa8¹4m(7"§M~û­íÌJYgõɲojãÁlÅ-3r0“ÉÁ*ŒB1wçKÌßÔäûTYZ×O¿v³Ñƒß»þRžÍæí g1þ¯¢ËF1àÿzºÁU_ë·S˺.›šuOÖ±õ˜ñÌíãò¯®wÌÉxtª/äu““¼Ñ§¿k(ö)/[+{Zúäʡۋ .è­Ç°u&u<™ó¬å~¸f€ÇÇŒ.^#—KÿpOÛJNŽ\¿7«ÃHÕÿ–çÿvMÎå(ÏíÇÜŸK>i°—\ö?tjkv?r2œ¬Ý¿X›wÌUçy³™|¬ ùëeÝ£‘¢b=Çþeù1)¥»ÂI&²®Ã‘¤qÆ}4>Ìí4äPÞ>gaf3yY{dÊ ñ£o’˼.¦;O ¥—OäýXð¡eÓÖ…£^Êsغêq€ÍGEñ}ÛÒ GXYuýÄæYš¾hú9›û Š{å$Ú1;lWíÏFhéÒf‹VœXf(øÉü}ÁÏšöK>;0R̃f39YÏé¹¼¹~äêÍÚ:)5 :r¹ü?‹I­³>ücë]¨Ïäa}IoWÃÙÇÉå·2ö¤“§Ïü>òs!çLog3¾¯Gëœ^Ä‘Ë7§tš²§'9±¬éª5jjëaY56}¿­MC”g|_ÿôøÝªCÉå?”…srb˜ÃáCMe²<ÔIÎ"‹î¶þ­gÂÃ9ŒïàUa¦K®ðyþ‰ϯöÊýÝÀ«™ÎWëï¬îʆ¼X'˜Ãø¾¡ãÏúÏ:M®Ô3pm>ögrÂbßÀ¡o†hë¼Î1ßÜ!W¦n®P&äJÙÔY»8NŽ=Xj6või¹ðedÑÚ°oå¬éSkýøÍ"ÑŸs?·xV^Yi:LãÇ•ÛÎXÔ¥¥m?Øò‡ÍB®jPÇú­Ú/zíd|ÞBµ·…!¹ò\š<õhžæ›àÙÚtc‡ø‡-ºñ^k$¾™@g´ò<ºÛ7ÍP•Àa|Þ)í¿Bão¥²l×QÐÿúçÅí~Óä®ðÙò•¿ÙËÙ®š,éLøYŒßŠÛêõ7©´TÉ1³†žÞ›€þÜüçÏ£säìöô ÅR”g|ÞâPç·tÓ©¤²Q²gþG¯\{:÷¯½BΔåMβû,õ9}Ápß·ô§RJI¥·ÏÀgËW‘£ëzÔŒïæ&댺yÙ>„rŒß[—¯<áyŒT6Î÷ÙŸäèh¯¯O8*ëèrëëå(Çø¼ÕL¨H%:¿y£šähHäå·ë²r¼áõv9»YÿVÕE?¢<ãïV¶^C*Û4l3ËÝ‚ù=j™Kó¥Úºr!¨†K$äý—©cz7E}ÆÏ­l_WóÃ*» ‘0e#GŠä…g‡½‘ o&õ3ß°P³+ó¾ì3:2EF}ÆÇ­üœDå„3i÷_#iƒ=Šž"úïæK·ý¶ üp6;´œ*üÊyŒÛ\í!Á·H%ŸGáü×ÚA·_t¥t#²µðŸæ1¾n£³ˆ>%¤’¯Ó1µäLÙ[A² ÐIÎîÒà\íþ©¨Çø¸mÔ9jaI%=‘º—¾¦8 ?=Nôú‘œ}ìÓ¢´æ¡¨Çøºmhn¯ iBŽáu?9G“ãðÚÃç ý*¬t¸‘¿l6OÈþ›nô-z6ÉÁ¶a7oûÕ¬Á©Ü1­çè§Îäpv÷̨î5Uz´ñ?ƒ Tõ™\lK»OWv]EʆŒ–>L‡ïÓ䔃jrŽ=5ˆ—PŸÉÅ6: N‚¼îWÖÈáf?¶¿Y>H;GWxÙ}Áº#VZ;r¸_¥?ó˜|l£ZöàhöìJ9ÌÖ7…^Ÿéô2gYšÆ×ºL˜ð£àk6“íʱ1Ry®uÛÜѧHÉó¨® ~• û¡G7Ë9lòLvðyJeu£ÅQ¤älU\ëÊÁv¾HÎqS6îQÉrL+¶˜T>mkµ<ë%)É3yî^{´ —š™ /5ÿ(û†r@õ™<ì8<}†pRÅôŸ”Œ™ÒóF˜·\¸ŽþpDGr û\²z4ZŒcÙŒï;cÔh›1©ªO–¯jü/QšÛL~ûúyŸ7‘sØzê3¾ï<4±´(:™Tq;.Óc…·—úÙú¡F]ÆLøBèc6ã+  UôXÆÂ--ϾÒòYEOÍ¿*TÜë@ͿʡâùPoœÎfòPЇ Z©jRƒîŒ¹^]±’ «Wßv\ξ·êQIÂÃÆ÷®U±ÊúÖêÉÊùc4“orö–˜ <£ãÁE4y¯¢»ù×É¡Æ~Ï&YhûÐ…Š{ÐNèÛò—Ã䢀íÛˆþhït«EÏeä ÖÞÔö#¸#øÌÏEjýÃädõ¢§‰ýþª¤¹’Åš(r0íyέ»`×GYžßôqš=YŒzL>v RJM.ªz>(Mé©G$’;]ôÏ?õw‡ÉÉ.:ûìÚ]à§§:§}E‹ø«þB±/®úk9Jv/½~arRÈínÕ7ÙW÷>Óè:0h”É ž–²®æÖ”QSÖÊ9û”õ˜N3ÈvüóKR5®M«¨_›ý¯w™môJ•u&÷œÕM“sØùЄ‡ó™p>‹vÓeårÙÿÅê‘K~_$ë ŠÌ‡nèBüùù j¥úMVÇÀarÁÏáˆvÓmïôÈ~çlƒ’\HG»f×Õñ õÿuk¿´bTR5߯¦d_áTù2Y.¼ïñ»w™¯¶þ•ãcn…©£ðÃç3~ëø:qÕ"º°÷7Ù—x¥Ú«Õ¹ðñ `6ÔÎG«öÀAíçùŒïE? þºÖY!_†ÞÞtì½½Ìz{ŸÇª@BÙy!9›ÁE}Æï¢Yô€Ö+Rõ²w^¢Õ¡Ä²®®²1)çXÓ¢<ãk›‰~âç¢÷Æ[Ÿ*vÑö5üT9§Ç=V ;6Ÿñy7=Z}Mª6]]ÿ×zRüG³‹o‘uvÛŽýšµW=g$ç”(-öcãûwlŠTq;ªÒS\h^è±åª¬ctË9¯Žû5ì;MÈçÆïï¾úêï«C­IÕÁó˜6%Åís,VÈ:îg©|›ß@`ôð3¾÷Øœž¼rwV9Á¦¥‹c_x[L€¿‡ÑθJWçóýMï09ØÓ˜:TE¿òsÇ{ÞzÞ}ù¨ðÃͪo”g“ç\³ûÄÙl!O ˜<ìé¥Ü&UÌŸ'{üä:ͧ¬s Ž®·æoÍÏ´=^ßm“àË&{Лù—;’ªŸ•ƒNdÏxߣmîó<¾=ŸºÇ}à,`r±‡ÉèåX]²ÇµQAô¯æ²®ÞŒg37ÄÉók6Ÿuó\¨Ð¿LöÐcé·’*ºJë^L¾û¾ ­*¸PÕ_9‡ïcióã/˜'Ú]xñ¢è·{wk7ëß±s¶¢ÿøyì<>à1¹(æç!´vðu·ïjÄ,œ&ö—ù9Ô`ŽÏ³# ûúŠuƒ/˜œìu<°y„ƒÞ¸BÝÞ-{ÉîUãƒê…ˆu¾>ÆçÑÿLî¿`r²÷³»}˜­%W†;m·†ìnÞtqèñb…ï‹q{…zL.öÍ¡#.¹Z÷ëVßHŠ•Ú\ñ†¼°Nè´~³œ}R¹`òLö¾‰x"WÑû0A¤he­&ë½ökç5¿ð Æÿ}Aý~ì»ñ”ÖoWù9Q5]Ô“.$ZŠóˆÃ)ãÄ9êœÝ+pš<\èéL.öõìõ'¦"ÚþÎUóŸ&/:ÐSïŠl·=ØÙ䶬–÷ÊdÔ>m]v¾õ›ºµÒJDÿ-dò²oú«kpí‡Çã5¾è®î>úÍéubÿ‚íïhã/—¡/ ™¼ìÛøÇ³à#CÈÕz‡n®'w5ºtyW>Ÿšæ 8lüÓìÏ©¥‹6õ½ G““}?ìjýñ<7ðgl÷ö»vÝà•QÙ9&²Žnï=Ü)ìÞB&ûÙ¾¹jê3éq'o¢ó*ë4`þmqŽŽŸ§ÕìÕB&ûù~´JçUe›½)|2*rÎ 3mž®cøÄø¿ÉŶ%ølx¬`ÕòëZ?²sì¢)/´}c>ãßB&?ؽ r•.¬~£ÑW¸hBíûUÅ=ùþ…)¥œ£+ŠG}&'ø9]uÝ¡ŠÝÏÑè,ámVëtÍïN[¢ñ7§=¨o!ä/—ÉËÁè¥ôD~;ôôÛgcE{Û~rãê£vb\X¥Üüfç«ÉËÁ^6{OoKªî¸öþíZRê4ûMÍuê½m=ûÉÂÿÎerrp|ó_ö÷¾AªØ¾)tzWÇù¤Þ}e+çÐë!_½E=&/•eƒ¤Š³Ä} BÅ--ÆŸèñWB¸ÜküÊertð䱫¹†¿’*Ì®0 …u‡>9ÿ#êÿ8²ü‡]šß¬Ù‰\&?‡Œwüé·Ã—Tñs—…µ^¯Ùg§ _™n&ϧ‡¡¸¢“CÊô.ˆTméLOB]:Ùÿº¿ž?O¼˜ò1¯û$h×±0²ëÁcísD?”¿´¯Ð^ŒOüÞŽ¦·‹˜|R–㓪¯•²ëâ‰1–Ÿw“ulŸWΡ«FîëPžñ_î¶î‘Áï¯áïÑãA‘dWQÏ¢fûçË:æGË9¿¶=×å¿åwνUÍU²+GÙrÇï£©ã¨æ,bü/¡³B¿Y¢Ýô c„–ÞÕÇÆíŒn­èχôàž§˜G.b|/Q¦å½H•²\Ô—ìòR)Y§,¯Àn±ó{(Ïø]2ÿgÃ{î úáµmߺZKÐcf;´vÑSa¬Ä¼Ÿí“ƒ’R倻FwÁw¯þ*(ñÖ䧨õøŒ+מi|Ëf~»ߘ”üI7Š>&U\Ÿ 2{ÿøÇ†šŸP¤¸A3äìÙs—\_ö<áábÆoeY!©¢§§“‚n~mgl¨‹>ëöÅGÊÙô¸³ ôn1ã÷a*³IUÿ±åοßíx査i¦¶þT„Ñö«û·äì½Ïzëʦ }[Ìäàð(Åð’ª^ÊŠh¿ÉÚ„ÐÄI‚nz«Å킜ÃöOPŸñÿð¤Ùã¾ñ1Ç|’x=Nv¿ÚrÖ—µvòqJôÓbÆïÃJóýIÝÖÜúÙ9f™Î|þMNø8,ìâbÆ÷Ã+” ©âû›;Í|ºå½QéÔÆ¹l6ŸE=ÆßÃʲlKRÕx÷Ç¿½¹Iv¬‹Ì¯óê'­]œŸ(ÏøxøXãS½6Í%UüœáŽ¸Ð›”‹¦¥Ü?䞘ï,áü£Çß³“*[:´Óø±ýzùŽ/‡hëzE|K¨|Ñôj ãï‘wÖ‰«¨×aO¶/îÖ=æA±à¯b†í5¹Î^‘ÞvèçýE/a|>Ò¼¶L©2¸QãÂ;Ínÿ¸lì¯S=Ÿ•íױñùȧëkü¹™T²sÁ]Û[5ëZ°ñ/¡'‡ó·õµ~#Æ7Õ_\Âø~dÝvz¢“TB;®¾G¶·h'm½°U.ú~Y§!‡Ö=_Âø}¤´Îø­'ÛJzë©Õ²½çþ?x]—‹n+TûŠòŒÏG”å‡tRÉï}ngç¼äݵ]fïšÝV§£<ãóQKå±X÷Üc›I¶+×!kò¸»²,ì(~Éø4^Y럌ÏB¨õ6¼*ï¶ £¹œó%¾iø´û÷?á­ÄÿI±5>Ä>ÿ_ã¿+¾ý7œ÷Ë=þFLr 3 úl™5Èú§ï¨æó˜Ç>üÞ,›ÀEïºâwâçñw{]xÜã|þæ `#m Œ‘6AÚ$å=oÑ¡¼éKþòÍÏì y ¹­ƒ¾¯kÌß¡ÎÞk©‹6ÕC~½Tó¸’¿E—Àc Ž…Ç%•½¬Ä=NäoÑUêÅ&Hgo[[ð˜Çy<æ1Ò6‰<æq g6œÇ%(ãq ¶=¶hŸÚo‡>²Cy;”·CìßiûJ½Çiz±vÈeñ@_Ã$Û¸RïÝ9ä7*åñRYì%žq;€þuzÉc§ñx¨ëØ.H»äóÆ.<Ö@‰^üâT½÷å|øûrYü}9ÀðHfoË)ñPß0=SyÌâj¯0½@ƒ×=§8½‹y|b´ß'‹Ç%ß‹å}‘ï‡6ø¡ ~Å<Ö0ðù£½þÀXHd±xÂÈþ@¤Kyüà7¸„™à$'¸’ÇNâq‘õá1€;´7¶`ñ'è›xÊÛvI,nûØáH‡ç²7|è{ÏJ܃fR”xÀ©<°1wX‘<°‹¬¼w‡t4ÒÑ(]ÀÞ VÞ¹CÛb@[ `ÄF,Ò±ËÙ=ô½;o..•Åþ`£k|°Ñé5>ØèÿM6šêk*o÷þf[J ;új™4ß ÐvCè§!d̰€Ç÷ áoÅæòwô¡F(o”ÅÞÔ×âG­|þ^¬‹Z0jƒ^cÀ0FÚ4#m‚´ p˜,ço¤Ù³wÒL Øb†|3䛞ÙK;(ôm] þžðÕ…¬ÔE›ê!¿^TÍßXKäïÅ¢Žà[$ê½[ƒÇ~Ëjñ^¬?ùÖö,vöV,Ò6I ¼`ð#õC@Ò!(‚t(Ú ÚB;´7¶g1hl"úö,ýJcº†•0óŽtørö¾ž‹!Å"Šž¤#ÒXüúžò-`E3SÂbGјDÔìD#òÑÅì}^%ÚÚb#0b‘ŽÍc±”·hYüÓ8:6Q[Bÿ©vYµÁÔþRÛ«ÚÝwm­jg!;Š}Uí§j+©m¤R;ˆ6(¶Ú=jÛôí˜j¿TÛ¥o«Tû¤Ú#Õö¨v‡ÚÕÖP{BmµÔèÿt|߸®Žçt§c¸:nÓñZ¬VÇguyÉâDâ÷HÐ…r±T^ÑθRÞ·ô߿şüàK~ð%?ø’ÿq¾ä”iÏùMÛ½WâK¤êÅ@¾A >à‰è6Lå1â¡ß5ø»¹yìí\#Ô5BY#ðרŒÇHä±™¯p׎â1(ÑǵÏ0Œ‘6Lã'<¶àóÏgqï;ô ýÀˆ6 Ò /õƒ?õƒP?ùÁ(ŒúÁ(‚þü”¼PàÍbñÄC‘nìÃb†Ò¸–a¨†²a€XသÂb‘RóAcy)±-Q7yHG¤³˜a4F¦ÓéHÀŠB»£X<à }¼èÓíŠ1è—”E»c‘ŽÍgq/•8–hWðÅÑñ„ÚúÚ_jwõýGjC)3©Ô·ƒÔR{:4¿Pµaª½¢¶‰Újo¨¡öEµ%ïÚÕv¨vBÿuj ôÇ}5æú]³éxMÇf:.Ó1—Ž·tl}w}wì¤c¦ê÷Ñùµ:Ò±O÷è8‡þ’Šy<]È…¡‹£[ ¿£Ñçfè3ÐoF}=:ŽàÛ2b‘Ëc‘€×–ù<ædÐ8íÏÞ‚Ç‹]Îc€ ¯Ñp½Ø¯à­#p9g”q¾Çc·fñ8øv,“•úx ,–†/;Qߺ€:A¨ 8!€Ý8„Å ¡ñ6Âð[8Ê…g±ëèÛàM¨‡¼¨DË$0£ñƒßbAc,ÊÅQ[óa}ðƒO—^ãƒO÷ßíÓýG®úpž–òQ,ö„vJ(kàÂãH¡ß €Ûúc˜¦G ò^ù5Ñï5QÞõ ƒFà£Q%“ÄcIf­—<¾8ÒµKØpaœÈã‹—êÅÀ4LSÐhŠ´)òÍÏ åÍ@³Y %øu€¯è« zë&ñØâ%ïÄ’¢iÀ3Gó|òxlqÀ·€ÞY†E5Å€6XBV,‘o…´ò­o…|k´ÑôXCV¬Ñß6><¶8èµAº>òëýb¯Á6ŠÇ/æq¤¶CŸÙåñ8RóìQß>ŸÇ á1¤ßðïÂâG9 ÝÐ…Åmˆö4üFÀß(U/núÏíuœç„¶8v§2+*‰Ç GY/ýïR¢' iWÐæ†´úÏ i7ÔwGÚùîÀçî Çôz }¨ïú=Ñ>OÀô žÅ<.~óJg1Á•xP€é š¼«ÙÛþô}Àó_àðE¾/pú¢¼Úë‡6ø¡¼hòÍþh¿?`û_è =蟴)å?é@ÀBÿ~à¡~0ê£|0ú+åC@à‡ x¡À Ü¡ÀŠtã(ƒÆª CÝ0” -aH‡Wx*‹ÑAãkÐø¬JÌ*´#x#ŽÈbq^•˜UHG"‰tÊG%±ø°4^y4ÒÑÀtt)WÜ1€ÚcP&øcÓX¼%n9Òqh[êÄUs]¦ÿTMm3U²wm1µ½jLždcUÛJ¦oSßgGßµª½Ô÷õm¢jõíŸþº"µuú6Nߦéû•ï³_ú6‹öµSú1ßçcR»ó>›£»‡Ú}£o_TÛBíôàŸÙj¨=øGv@µúã¿þZ¥:æ«ã½:Ö¿;ÆÿÎy“ÅpÐX†”5 ‘6Žš£<ŒÇöÌmÓâûAîŒ!/ÆI<®~7A»LÓuMÓ eê@Vë@îêFÀ®‹ßê³7õÇ:bŽ~²@¾%ð[B†­RðAÚ å­¡w6€iƒïú ø€^ÛkpìèXH}æîºàƒ´Ò }Xì¼Fàu#´Ã4:§#Úå˜Îh“3hs ¤]@›+øëšÝ@Ÿ“¯úÃ#‹‰¸'ðz¿WY¼QÖ°|@·õ±Qßu}‘ïüÐ?ðÆ8üK˜« š‚ASH1SPÀEÆ€ÝeÃP' ß᨞ÆbÒXý¥÷7Ê*¶¿Fÿ;ÿýƒïþŸç»ðÛÿÿôÛÿ-þz"çð)q‡xL3ê¿cÜ1€Ž@ П¨kˆ|CäVóøeÈ«YÀTÞyF+#”5B~-|’y\3À« øµ“x|3´ßãŸ1ò‘6FÚÄ‚Ç}…l™ ÿM€ß:h |¦ Ï øÌo†´Ê×AºN >ÀW´Õ…¾×¼ºY<î+àÕCºà×£öðÌQÆ4šƒfs¤-Ð> À° ¶vÄiK´Éø-‘o…òV°VÈ·k´ÑôYƒÿÖÈ·‰âq_ÏéúèÏúho}ä×GÚ6‘Ç|~; ó5Å}µCÙ£ìA=ð5@ÿ4¼€×€úæÀí€<ä9wí!`5ìF!<¾k1Ñ7ŽËy\WÀqB]'è‰úÎxœÑNgàu~É⹺ËrÏ´¹&òX®¨ï†¾uCß¹!í†úîÈwG¾;hwmî€á¶x ­ Ç´{"í ˜žøÍ¿yá7/ÔñBÚ 0¼-xÌVÀôF_û€&”÷<àôN_´Áù¾(ï‡öù¡ ~È÷CyÐìöúv`vè @›@O ú+ð‘Dù ÀB›ƒP?ðƒ/õƒ‹ÙÐx!€^…^(h îÐ2f~ãå,žl*„¡l`…!Žtx:‹GãéÒØuMP· ðD ¼¨²‘ +ù‘HG"…vF!?*Å“F:ùÑHGw Ú<1È‹±Y,ÞžSxãP7e㨿FÇtúÚ`j{ß盫öUµŸï³úk³ïóÁõ÷ñ©mSíµeÔ~½o?ÿ}{ùïó»©ú—öö©½¡6Fµ/ªMQýl};¡ÚÕ6¨6Aµúþ5÷õÇ|u¼WÇz:οï€þš°þø­ŽÝê¸ YPÆh}¿Zõ©“í4N¥[òkˆ¿ ¡+†(_2jDÇKèLmô©I s¡LxlÀ6Ëåñ¯Ñ¯u xÜk¤ëAžêAFÌÓxlkäY¢¾¾­PÆòd“Ê\­úIÌÝRbS£=ö {Ô³GùÔWƸãÂãMã·† µ!ð6·#èsmŽ ×)ÇŽ-ÎÅÌ5s-®€ëоw£ä{ZðØÎøöBžúÀ;…ÅpönÐå‹Ê¾€ë‹ò~Àãüßõ;ô¢\ þB à  Á( C +¡€ ¸“XŒéÆ€†üð(o›Æ§¤1¥› ^þŽ@™”‰¬¨|ÇéXàEù8*?ÔŽÒÿ•ëÕÿ÷_óuÿ·ú¹ÿh\ùÕÏýÏöoÿ7û¶ÿž5édÞŸ€+YðXÀà‹TÆÔØ ðÙ €©´!ò ÁCÀ©é‚Ò5‘_ýb„úFw#Èõ{AK-ŒµRðAZ€Y×¾Úh§1ôÙic¤‘6±ç±|!7&èGSÀ2E=SÀ1.3ä™!mZë ]§˜ u¡¿u§.讋¼zÈ«¹¯¸õÊØÐbŽ2æ Ó¼˜Ç†^[ ŒM£-–H["ß²” ?V(oý°*fC‘5h±|ëR6,Ù _lÐNä×¼ú ­>h«|[¤m‘¶Ey[´ËΞŶ|;”·C{ìßi{”o€~kx €¯õua‡@ò¨?‹¼†€Ý°Œ¹)n„>j„>wD]G´ÝmuD;œ€Ç t9Aœ€Çýï ¸Î(ëº\P×i”wA_¹‚6WÐê ~¹¢¼úÎ ðÜvCÚùîà±;hq/`êG"‹Õêz<‘=ÑN/üíX^€íØÞH{£½QÏ´ù Í> ÍiÀöm¾Àå Z}‘öm~È÷C¾úÀxüÑδ14€†€6t¢Q6¦;°ƒÐ® Ô B:°‚Q7ý‚v‡ ‚ò! 9t…‚®Pà ÝYèÆH7Þ0” CÙ0À ¬p¤Ã³Xüç&ÀÓu› läE tÒ¨‰t$Ú‰>ˆ¤6ýº¢+ éh¤£‘òÑ€ƒ¶Å m1h[ ÊÄF,Ú Ù‰E~h‹C[ã€#ùqTéxNÿQ[û¾5çw}ZàÔl#eàû|\}ÿ–Ú·wד©-ûGvŒÚ°÷ù¹Ô.©þ­jƒTû£ÚÕ¯¥ö…Ú}_Võþ®­Ð÷gU;ð>?–Žõú~ì¿4®«cú¿æÏª¾¬þº±zÆAƒÕq7…Ñ$A? ¨ÿ >¢o !5!—FÃÚô3üxæøÛøLQÞ}kŠò¦à»t¤ú­dÞ2aŽo |,!Ÿ–Y<¦9~·‚ìZáwkÔ·F}kà·Áß6±ú·ú Ëß¶ø¶Gy{Ôk€ò€ã8 AkCàl„:ð·#èr„\:gÐí:\Q×c•òÜA¯;þö@yàó@]/ä{¡-Þ©,>¶pû¢¬/`ø%2‘ó§:öf p¢LÊþ ê¿g0ʃ÷Á 1‡ L(ð…ncàh\Àܶ0ä…ÓO5ôyMP7¢€ÇOQ€ üÑȱg®\,•Û{ÿÖÿ+ü,zžç>ÏñyQC9×Yã>¯ñùŸ¿ðyƒÏßø¼åçðŸ'L‚ã%a.%a¬—0—’jóõHÌ¥$Ì¥$ŒýÆ~ c¿„¹”dÎ}9Ì¥$Ø ¶@‚Ó"a.%a.%ÙñûØKI˜KI˜KI°l…„ù”äÌï.ÁfH°æS솄ù”„ù”äÍÏïÃ~H°”YœJ‚a–0§’‚ù™UØ öD‚‚J˜WI˜WI˜WI‘üìæUæU”Q¼J‚Ý‘0¯’_kżJ¼J‚’0¯’`‹$Ø"© [`$Ø# öH‚=’`$Ø#©3÷c1¯’`—$Ì«$Ø& ¶I¼J¼J¹; %Aÿ%è¿ý— ÿô_‚þ+÷¶ ÿô_‚þKÐ ú/Aÿ%è?½» Aÿ%è¿ý— ÿô_‚þKÐå¼.ô_‚þKÐ ú/Aÿ%è¿ýWÎ}@ÿ%è¿ý— ÿô_‚þKк—(Aÿ%è¿ý— ÿô_‚þKÐeú/Aÿ%è¿ý— ÿô_‚þ+><ô_‚þKÐ ú/Aÿ%è¿ý§6I‚þKÐ ú/Aÿ%è¿ý— ÿʽrè¿ý— ÿô_‚þKÐ ú¯ÜiƒþKÐ ú/Aÿ%è¿ý— ÿÊYeè¿ý— ÿô_‚þKÐ úOÏýIÐ ú/Aÿ%è¿ý— ÿô_9ý— ÿô_‚þKÐ ú/Aÿ•5xè¿ý— ÿô_‚þKÐ úOç/ô_‚þKÐ ú/Aÿ%è¿ý§wæ%è¿ý— ÿô_‚þKÐ úOïëIÐ ú/Aÿ%è¿ý— ÿôŸžÃ– ÿô_‚þKÐ ú/Aÿ%è?=Ï#Aÿ%õ^Æðîî'ü;îî/çs.c½9WŸs%±½lzöG[§Kçû ÆzgòùÙ¾ÏÏÏuÛóûûézëy>ülw:»Ã¯œMäg€òõÎw§²½å P¿ÇŸÇÏÕà{ )|VÉÏ%ð¹X>¿/hÁï ¦ñ;ýÕ|>–ÈîѵAíÌw;“JÏ|+wû]øžC*?TÉý¯D¶Ï®ý¶×Û{Èc{Êùo¾Æ˜Æü4e˜ŸOåûeüο?3”Å÷#*ù½B{~·0™ß/\ÎîÒý å-{¾O‘Ä÷*rùÜ®”¯[ó³ãQÌTÎgñûRÅlMS9OnÌï%&ð»‰éüly »O¥ìoØó9a2Ÿæ²5PzÞ\yWÀ‚Ï“ø<1—ÏËø|Ñ‚ï$ñóJYüßÌåsÎ2¶¢Ì1Sùûùìþ#=㤬ãÞãû'ö|®™Âï•å±eÎxÀŽÝñø=¿ÇÓßQŸ np'oä.2–ø €ß ¸›w3”iŽ2Í·9ÚÐô·Œ€Ñ0ZT³%Ó–ÀÑ0Z¡\+”k…r­Ð†Vàa«j6mtkÀm ¸­AsÈEÈEÔKD½DÔKD½DÈa"ê%¢}‰Àߟv ¡èl‡~h:Û†v(×eÚ#¿ò;VÀê€vv@~GÐØ4vD™Ž(Ó ø:ÎNÀÕe;£O:ggôkg´»3ÊwÞ$ÔIªf˺]P® ÊuÌ. ­+ýoW´£+ÚÑp»w7ÐÖ¿wGýîø½;x߸z€ž ·'pôŽž¨×³’- ÷ì^ÈOß’AS2Ê%F2`$£zîGhïG¨÷~û¿õAÙ>¨Ótô½} ›}€«/põEÙ¾hw_ð¯/pôC{ûn?À퇾ïýЮÔOÁo)À•‚¶¦îà€²‘ˆ6 ¼A(7tB¹AÀ3x†ÿ”‚~\C@ÃÔ\ÃÐÃ@Ã0Ð6 u†ß0ÀßF ìÐ5tDÙ‘hûHü>ý:´ŒÑÀ;ðÇ ¿ÇïXà XcQo,ꥢ^*p¤÷8úA¿ŒC½q¨7ðÆÞxà™ˆï‰È›ü‘7 m™„ò“Ò™mWþ}8—ÿ?gÏãÃÜüÃÜü¿{nþa^þa^þÿÓ¼üÿÁy¸ôážÿ»7zòÞyG/ïZðýÏ\v¶OÛÿÌbçfÞ{¶Ï‡Ÿí+à÷4]ø;=Yz{ !ü.N«Ç…¿§—ÎßëyÂï㤱½På|z¯'ŸŸQ7æço†ówõªù™õDþ¶^¿wmÏï]§ó·{îñ÷õ’øÝë‚÷ßÑQÞðñá{¤iüü_5k/‰Ÿ,a÷GÿÉ{{ùü¬O ~;™ßÙ)à{§ü¾i;ÿCïR·NÙCMáïûð³5ø¹ ¾ŸšÎÏ –ðsB5ø½ž(þöO¿ßSÌîo+{­ü®O³/¿Û—ÏÎÛÓ÷û”û?ü~w"¿ãÅï•ò»Þ5ø ~/(½w¢ìÓVó÷ƒìù>O2ßëYÎ÷{*ùž=?¯”ÌÏøçòûCeü¡ü¡(þÖP?¥€íï*wÆíÙ[*ʹÅ4v/€ž]¤ïøiwÈÙ’rÏ(ß#ÏâçKùYÆRv†‘¾ ¨¼G”ÂÞ$RÎ/³»Ftß·m¿OîÃÞfQî¤ñ»Åüþ@"{#°Ê7Ëg®pgÈSgôigÔm\m+ù=ò¾ç›ÅÎWÅ£\<`ÇF<~ÇïñôwÔ!€—€ú ·ÐÙ´$ ÏÐÍ€»`6C™æ(Óp›£ Íц€Ñ0ZF ´µ%p´Ž–€Ó åZ¡\+ÐßåZ­P¦5Ê´¦¿nkÀm¾lúÛ€)mP/õQ/ð!ƒ‰¨—ž%Ò>§hh‡>h:“€»`·C¹ö(Óùß°:V´³ò;¢\GÐØe:¢L'àë:;WgôGO”ë œÑßÑîδo7 u’@sÐÜ0» lÐÕå»Òo´a(ÊuE;ºn7àîÞu=Ýñ{÷J6-é\=@OÐÜ8zGOÔí zzv/Àî…üdð-4%£\2`$F2ú§7à~„6„zá·ð[”íƒ:}@GÐÛ§ŒMsúW_”í‹v÷ÿúG?´·àöÜ~èû~€ÑíJA:xFV Úš¸€kÊDz Ú4ðÏ Ð=yƒP~4~ ºÇ"=u' Ÿâ3¸>Ågଠcè:Fà÷‘ c$Ú:¿FŽ-£g4ðŒœ1èß1À3øÆ¢]cQo,ꥢ^*ú5<G?è‡q¨7õÆÞxÀ<ñ=y"ò&ÎI(? yC蜂ÎMè¿ïqýǾDž¾Væ;zseþ‚þRætŽ Î¨Ï¯ú÷Ô¯¯ßúæÔ'§¾7õ¹©M}kÕV}fê/S?™úÈó¹_üŸéïþ#_·XÏÏÕ÷qKôü[Õ·UýZu¯IÝg*Óók/éù³W¹«ú°?rßõ÷YUõ÷SŸ0ÿô¿ü]´,½wKù™\~fí?£–&Þ¾QÞ5®w+è»{ô}\å}½v§‚¾_£Üý½M Øez悾õGß½ ï¬4-ÍÙý»ÈjvNŠÞ Ô–•°»uô\qS|7Åwk”k:­Ñ¶¦(ß1„½ÉÛ¿wÄïÿ‡½ó¯ªØ®X¢¢¢DéE‰¢R!™ôžÞ{/¤W¥ˆ*6=i‚bÉÜ¡^¬Ä$èµ`GEEQïÿ7gæœüyﻯÝò^ø¾ÃΞY}ÍžY3ç쵑Ñyü¹rõƒ—¼G?ä–c‡@ùA¦øDB+²D=¾¹è• l‘íºn‰“Î{4]¿rTçµ§ß«®×5LŽéHÎ:R®Êïg}'å ~/ÅQ½›"sMZó^BÓå  ùd^™7RæJ’9†dÞIù³õ½'UDæþ“¹Ç­ùÕô»,%:ÇúV•ÏÈVÅå¤~WÛO¿¯Ý òNZë£Óïm»éw·§ëÐõÁ–¾Œ_øú‚ ¬x~àùAß±äwPm 27¨­A2¤“Žéà¤ctàÓÑ):)ðH9£¶t9Ñ/÷—àÅc—xä Fæx`c±W,ò„@/~‘è3‰þPúCÑ=½B¡L˜ä‡]Âàl¸ü G8¶)8¨¶$ðˆÀ˜‘ÈI{ô  Í(d?»þÑø*Ú1ÈC,òÅ"K,p±Àd"G,ºÄIÙQ&ÜxÚãiK6œäH.ñ‘¿Dx%›ˆ.‰è$ôO‚nt“9 I蕊N©´¥"o*r¦B7 ^iÒ¾Íj›”½ ødà› à2šÕ¶) ¸,tÍ‚W2dÑ—E_úä O89àäÀ/~9ÐÎ6¹ò+¹ò«Bæ|Ú'ã§ÉÀNnVÛ®Bô+D¾BhC«ÅàƒW‚>%ð.Áeòƒ]ÊšÕ¶¬šåÐ+‡O×*úªª-Z5ºT_-ýL›u¿!ÿuæ§½p~Z¹¿9÷L”‹uÿbÛ³Øö'¶½ˆÜw0.¬û ügÝ'`§?œ™Úâw»ãWk¬.ãt[l.ãr‹Ë8\Æà2þ¶ÅÝØÉSŸ;ÿ=ÎRÿYâà¿%þ÷âß+ö½PÜ+÷¼þûó7ëÚrgt>‰\Oü¤Ê5iÏwÎ%á§kÃ5é\’#TIYcÆúÎn¬Ê#!sFÊÚg²†Šµ¦™›Êè²]å°–¹Rd®5wp}û¸#S0ðžÈéI»;¼|h „ :zëƒ~Þè m?làOŸ?2ûCÛY‚ –ðÜríl‚¼irnå |4ü£á ¯td£/žyÓ9 à™íLèd›ŽLéènÜç2s¡_À}.ºä‚“ ÙÏ"®•Ø£9+±Y%<*±m%²W"Ÿ?°±rÝD¿XîSዜñðˆ&ºñÜ' W¼S¡• ݹÆ"Sø‰À¦"c"²&"C"ò'I™±iô’ ŸŠŠhO•4jÕt˜Ù¡ŽÕÒ¥^葎ìyr}'›¿3ùd &<*¡;™¾búò ‘¯lì— ÍÉðÏA®d*&ûRÚs$}tÎ6ü<ÚÊÐc2öžŒ¾“á9UàC³ÛKø³øbà« W½2lQ|™„—k ö,‡níUبŠöjd«†_µœ7:óUwžñNwè<ãýGŸñþ«ý.¶óœ·óœ·óœ÷ŸóœW®—Ó:óÕÇþ'òÕo=¯Q½®Eä|NÍé]Ï2÷œz–t=ËZUsZ†ysF8üó×V9¦kRÕuFKþX_ZÖ¥·×£oÖ5èáy£Ÿ®¯â óz†êz¡èÔ×I厖µ ­õæÁíwFÕÛ¤sx6«úQÖzòÈ8h»ÎÕÙ¤jÊšFÖº•Í*Ϥ¬5ú¹„¼/¶ªz˜înªvÒøc*»¬•.sOZk"9ªüò.Üû¤ªzÖî\ݹºSGšÁü wž­`Ù/Oúƒe\X¢rÎs d „·×I5í{CÛ[øqõCöP¡³:EJšÐóߪòÏG"_¸Œ5bU ùhþiVy:C°}p½®ßMåÿ >ãÀ'[D"[ÜAUß)\þ-ã pã:T]§tˆC§hÇA« T¹ðŸÇuìš}/àJ‘µÞÞÐ/`{d@/¸ ÆEpðÉ„Oü³€ËB¯,xe!CÖvudƒÞ9ȃl9àäÀ/Úy´å›‡\yÍjùÏGß|Ú'ã¯ÉÈ6“á[ýBæŒBøÿZÅà7«#ïx”À»L~°Kxeà•C¯zåð©âZE_ü«è«F—jà«kTþØ«\¬ÿþ+ß‹t~'ÒY¨óû\  }ò?ýÈßúýÇÿ¥ï>þ§¾÷øWùÎã?¸×è¬_õŸ¬_µýœš‰à_ßpN­Yð{ßô¤¿§Œ÷á×z½ × ø^ÈÔ ™œ°‰:9qïÔaÖ°š€ì7tèZ³¡º~ <{;ê2õªöho`úÛ'V×-‡gß:ß?vïÛ¤sþ;«zGc¶ª¼ôrÙï?B×´ª×9»ià§sÿÓ6ñŒZžeÔ¹ªfã@àBoP‰ªK.—q·fUƒÖ Y7¾ƒêWðBÛxéPõk<Áí¨®s…|7Su\œT½(YÓFÖ¥½ü¡Îº¾ °C›tàœýTkmèjRõÔGUµj¬uj€ñEÎѱº¾zÎÑ¿@ÕƒsTÕ¯±æwÔõaá7¶FÕ„ ¥- ¹#JTí)Y{Ýš—_^aðõZ ê?ÊÚSÖ:°àMàÞ ºT­vYÖY\°£ ø.ເï® 0‚>ŒCá/hw;©B"wdpG.ìè.?èåmdö¤Ï“>Opšôòwø¹ÈtR…iÁø<˜ûø…À+^qÐÌ79c¤­Á Å6¡Ø&™ûdàB‘7 Éõ*´Ë—Wè„Ë}ü2Ñ©¤V…{‘ðˆD¦rÆX6v‰GÔFÓ N´£‰7ÅHÞèƒb9cà‡®qÈÿLàãàï8p’I–2A;¶xâäÊÄ)ò~ ø)ðKAÇtIAžøUÃ+E~à—‚¬…Ð)f¶O£?¾4Ú3±O…´7t2¡Sý|p2‘%›ölìœM{62fË6dɆG.z–:ª°3=s¡“ |.ðùÈ™[£ÂØlR aå¸"þ.’Wà‹€/¾Ø"h—»TÂÒ_J)ý¥ô—Ò_Mjh¯ÀŽÈW.ا;Ô[l °5ÀÖ@«9kdL)cBùOCr¯#÷7çïklû™Îºkÿ¡ðˆÀÌ5èý(®È|Q|b …ŽÈ!×<¹."Cí!rýF²\/àD }ȆÌRä CÎPô‚NmÉÀf!C¨œo¡¿8ú#€‹CÎBdˆ2[ˆ ÉRh&ûùôGу^匛røVó)GþrúËá]­dÚ’å=¸ù[”ƒWïÂ5%Ë+vÏF >ir}@‡ tÊ?[Î×|²‘«Z\+äzƒóáSn>¯†n t«áQÎ5²]ÎßЬÖlå|Pí3¬ÿþž¿ëí¬ë×Y×ï?z^ÅØÿ_{VÕùÛÝ?ž_ý³Ÿ]uþf÷¿í7»õ)ÿõ)k/P{9sL½®¿~ôح'vèÙ¤–à^Ðë|/dêÕ¬–F'ôuâÞéè95*kTýõh»=F;êÚÇÝtÊ\U;®7ý}ÀïÃ}xöÁF}u-9äé{P×”¡ê°ËZÞýàÛzýÇéZÉ´÷oVu+eMÄñtÝÊ]“¸%º.;´Æs?H~qP“ª_9žƒá7¸Y×®DÆ!ôA¦!Èxð£ðËM©ºŽ%¶îÍt}ö]ϲY…CGèš–À=¨ë´‡ªúvÖú‘øcí£:Tmtk=I]C¸Ñð]¢ê£»À{´Æt¨Z’b€ Oîê¦ë¤Ãk,°c»@ÕAnº¶ä]Ï]Æã÷ñô‡Pu%£¸Ÿ0]Õô@ÇÚ&ð ÄŽQÈMh¹€ï¾ ð.2æF £@FŒ O@GÐ/ä>]Ü‘Í>þØÕ]î耦ÇI&yÒïI¿'²{"«'ô¼7ˆ6÷¶^è®öñáêlrAÇ›$c÷ èùÂÓ™|i÷Ŧ¾àù‚ã+cqà‚hËE'ä …—¿Œïù;¾(`ó¹„V 2Êvîƒh’¼ `ðƒ‘7˜`d gÈfå£w2‡ s(¶*G‡Pô ¥½Þ™Œï0h„C#½Ã¡Ž.áÈ’ tˆ ?’þH`#é„G$òEM{4íÑ´gÒ^ïhÚcà™†í³¡ƒ¼%Њ‘ûhÅá·8d‰C—8d‰ÃÇqÈ• ßdÓoWác ¸)ÐMn })ðLÁ)à§€ŸýôHg ôSà™‚žÙÀTKž|Ò ›F_Z“ -ÓxÒ°]|2±W&´2¡• ­Ldɦ-;WЖÍ}2f#_>´Š¥ùs‘¥ø|èä¢k.8¹à䛽ò)À.Ø¥œàs¡Uĵø"à‹€/¾H¶C¿¸RúKé/¥¿”þRúKé/EÇ t«À.ø¬B⌠‰k€¯¾øàk WƒÞ52ž”ñ¥ü'ƒ£ ýVòü3,ä謳êð÷¯³*c]ÛÚ~ËøïWÉÕ“ž6u¡¸ÓkÊóo9“ú[Ï£¢þÿs([Õ å(uøãïgœ§{&•êðÏQsv®“]¢¶ú=¥Ï\O®NÈ8¸ñ2¦€þ'Uçz´Œ!€ŸMWÚÇë Îl?~£à19GM—6/dHû î½À…<èâdþèÈO“ªËÀóï/¹f"ç¹òŒº@oÒQ>ôOn|ðAÀIõØ ú#€ /¾¡ðˆ€oˆ¼ÇÖQõêq’ë/ÏC <’Ñ1¸@ôóç† þüŒià›,éÒžŒÈÅ'†QØ ÞRä Ã…ðˆàï|h‚[ŸBæµdt6yb°{&|3áW"e@‡ÚKhO–°Ð/„v2Ÿ|ÙÇ'þjúJä< ^!<Ò€¯@ÎllSŒ™ø¸9KÀ/7—¾|pòi+„w5mÕà•È5ˆû¢nú]ο’|jh«¯zCàéܬ¾«ë—ü¾BÚKÖ£²ÊÝMË]«ÖE©¯õìn„Ê-×D)¯”_þ&Oær‘¿½“ëŸUÆ&µ†IYå%ϤŒò¼MÊ)s¸Ê37ùýŒü½ŸünIÚR~g"ÏßäÙ›'®¸ëãã} éŠÎ­#žè3St$SO<*ž¹~—ÔÈhtºuñˆW®6æšW8ûk'à¬ðûzž¼ô¡í;ÄÅZÿŽøo2ú×].¶œÖ?÷ÞëÆÛî{U|¢1¯ê…º¼&…7þ¯G®~â»Ý:"&t«ö©[Þ2¾îþÒ)×ãžáYCW_"îÜx눨QiƼ#zú¹}$®WðÐ9ªè$æ|øÉ­—Š_ïqߎn[–ùW{Dïr=¡ìo4yiUÕƼp‡ûÚÜǸÒAáÕ|ùãFq‹éÇ1yE±k†‹kôý–¬i–ųV¹~üá‘'ÞxZ.{ß«{”¤üdÌë’pøº¯'CO‹}õWPò’©Ïà‡Öì¹¶Öî‡-ÃZ÷¦uw3ý0:öÍÏ®±ëW÷â¶Û­6ÇÁ“jì³tí¾‡)g÷ß®º´¦É.çæïæOù1{°m¼I«Ëf7ê B·~wï âFÅzjœìƒëê“ñ¢ã²+çŸý5KlÞwó˜¾ßün4Îö 9?u>ËË=Nì^‡}ïÏü­òã‹E;ƒÍ±ã„ؼ´ÿÓ ¿ëo4.— l5ê¦ûgß›Òx=Î\µwìÀâýDsøã¯Ûõß,‡AÂB£±áÞ1·ÎgÌ»ªcx{òðÔxØõo¯î{wh?:¶ë{s¢Äæ”^»æ}q‹ë;·–—vû4ÒÆÏ|næm.íúüBðÕ8ØïtÿÔK?Z&Ú_ÿ¼ˆØ~7wíðûÁSŒÆM“^óxæm1Ø6þœ¯¹nÏ=æø[¨ü¾ßõöôqïNí+.½æÓ)‰§_ùÅ;õøwËÂu ×Å‹a³‹<7MÙiÔ½úìGïÞ{¥èµè Fþ5à+ÿî8}²Ã«Y´Ï›9mUÃ!ñtÍ7Î9CcŒÆ ±S\Þ™fÔí+}±Æc$ðÊûã/Cb/Ñ^ýNTiàrñôàëÿäº/͵MÉe4nöíí;k°Q÷Á†EÉà)?î¯ùÈþ‹gˆö¬¨ýw”½k§›Öû®]æ¸Ð?°ßÇž1âÈfÇ‘79Elüøƒ9—WôµÏÓu+_©:øâx·ãõʯF†¾ûÉçûÅ‘^¿\rýD±ñ%KñãcZ Ëu,ðèØkÔmºûí‡núxå¿2³\&Ž<éÑçbÅÆ,=øµ«a¹¥|aó£÷™ÏY½òßëÄ*ŽÔžI[w•9ž7zÜöúÝ7-´ÏÓ÷œ-ÅvÔýÞwýÖƒŽòë¯uƒ\òÅ‘{>ò}æú{íþØðsМúk¢íÏ«%àÃ3ƒvœ°û¥î̇÷„͇ܳõÚ¿®“ƒâ*>Gò‹Ýî,6¼8>¶[zˆ)z®L»éçÞ>ßÕk?ß}3£§8ùÁýO jÂ:]¿Ï”ÇçTô ×úÛç麺ý»Es3øÚïÞÃÔ&ޏ§x}ñ#±ÁëÔªú‚e†%´ßM³NÿÙ¶n¯ýëÒg·Ãç[Å‘aO¼ãV³Pl¸Áñ³‡j Kzõ¾Ä{MyµìÏñbíïñwÈ‘+Žô<ÐcкÓbýÉ..Cβw¯¾¥Ð˜çY·ôÐaൿGžiXpûQqä¢ï~ž#ÎÚý·~ßýþÏýò’a©q~îkŸ×ç¼õ ÍþÐÑãàvKï­)ϊç^9½ì‡b±¾.¨ßÎ6º~qíS¹yµ}>œ/ë„æ¿kÿËÿâpö!qø£Qɧ~ë]‡ü¾ëò Ë}Ñ“O÷hÌ÷Ëoyï9േ¼?üÆ•¯ˆÃo=xçs—UˆuG¿|á¡4×OmÏÿÜA×öïj>¿OœÁRbúw±ö¯zìãíðöîÊœuµý~]Éý½’º·›þ~lÒ û.ª3ã"Ç%UÞ$åÒóú™ -1;§‰Ã+7Þß}X´xê×û Ä—g Ë‚¿.Ri®o‹õüÝþ—Ós~}Iž³ëP—˾OÍwÜ–¼nšayÄѧÿÊ‹ŒºwòóÿtÓ·ãKôÞ–èõ\Ç)‡=ö¾úÍëgÅÚ{V ;ô(óç[KnË^kŽW½~Ùç³%z>üÊ£´Ì‡oí¸‘©Åæ?±ÖÅçù‚3Õ¦?™¯¾h M~Û¨æo8.Ú&UAa³X5ûÎy¾f|±·êÊÚkíñP]ÉÃD¾øøÊ¯ûN<š[¶u hcöd‰«.{òǪ¯èʽ–º¿2賌¹?ymFýwÀ+¿ï ó4ÊJÇŠV¹{‡XqGцHßéæó§ÖUà•ß÷z-fýì=¢uço''ˆåÓ¾µ¼×`XvÔÿùã™îæøÒó‘žßÀ×~reMŸ÷DëSrCü Xöü5ÏÙô.󯑼vô6Ÿ÷eÚϽ¥Â÷ˆÖ'»Œüdý_ÄÒ]¯Lì6ǰÈå÷ûÄ]ò¸àå?Ûã‹¡¶q¶\ûW†q5‡íóKëC?­ýâéJ±Díìû Ëëg{4–n–{Œm0êtœ¦ãè)ÿïýøŽ¤×ÛýÝZwùE8´ëöåÊÍñûÆê¿¤í¸TŒÔqËÜÅòßÓ_Ë•ß÷6.¯ß”\Ñ:ãŸûe‹Eï]ó— gmús]à•÷>ü}p~Ï~¢µÂu¡Ë¾9b¡õ1L·Ùߘ›¥Ž/Wþ•‡¬Ý(Zsª/[÷½xrÂÛ9ÙnëmãɘûBóÔYï¯ü»÷FëÄ/ZÕ¾Ðn·'üxrþþ*ûsi[wlãÐþ|/W~ÞóªcŸ;î<)Zªû,ºfªx|è‰Y­ãΘónKí[%wŸµÇus_ j÷ú¨?,Wþß3owÞž SDëxÏ^›÷¹ïýö™)7™v~§çœauµÇõséëÊ£k>çêÕV±'Ù°Ø÷•­êÉ~ÿhzÁðÁ]î6,‡¾Üfô{Ø×ÌÕði7ËHsÞ^¡ÆÁ9LVb×þ/üRøíA±à±.k=cX¶]”zÛ‚™ÆÜ‘›¼œW~Þ£æÑzm™\¹Ä#ËÒW5þù-Âgo¹Ã˜û‰ª³ŸY¡ü¼ûÝ·¾¹f"~áˆã;âáÛ•,rÛ°LÉyí«Šûº®§¦_1•ýÖ åçÝVu+DËõËOõ.æomºù>kÍñwÉ}½f¶™û¢ÕÉì·V(¿ï޶>€¢mQMÌs¸köÚ×&–lë?3.°Í‡+”Ÿw«ý¥ýyhiI~ÿ’[Ìõ|n{vÔ:wýVÛÓ"£ÍÛ«Íø@éaž¬P~o:ðù; Ñb$ÕÌèÝÏNöwO­Íkm‹{ KÅ-Ëk'¬1õêU(-g>gê§¡¢é‰}¾{¯a÷wËÖû–>õ‰˜Ep8xõ2»]çþv×OŸ¼Í~x¥òsSE„|âDKCöÄŠ³Qbæ¥ãSË?0,Ó~xà© sŸH˜<6Õ^ù¹)YN´Ÿ›öxt}bóŽbqÿÁ®åO¿êkÆ[z\Ûâ¿ÚS­'Âï7ãþ•j4A ˆ–ÚÐ¥?¸UL¹gÈÜ ¯©¦_~V7ê´¿}ßW+OyÒ·™ÏãJ5> }Û2ÝÊAT>÷ìÝ×2ãn§Ø×ËÚ”K|§Â|WªqbÔ^ûÜ;æÛýÛR²ýq R½œÿÚ”1U®_Úžo­÷mÚ/µjÿj>—+Õø1¬ÇHÅ¢E†Ï} ²n—vµÿ°+ Ú+nÕ㯖YŽ|5N^iï×Í·½,ZäjÕbÛÃz@gXò£äÈ4j?¾AΈnÇÔ8xEïóZØÔF{v±ë1NŸ_[â­ ƒQ«îÁSã`×gß_=í‡Y¢EíÇí~ô¿Öï·•­Åæ|ºÿøÈ ›z+¿BG]S¬…¢åîˆíŸnŸj¤:H9ÕÑß° àÞ€Á6?šqwƒ»nò¹b]Ðs\©óeÛ½‘è—û-†—añ]Ü{l¦}~®Ý³–õ:Óî j<ì$J‹Úz—hQqQØ^ô…k®íœÜ°ÈãÛÓïÙŸSÛx°«5v.Úæ<5¶}}øÐ¦·L{(;ÚŸËGó¦õ_rè£ñõò¤Ç>Žç¼]sû8ßbs?ºZmŽO»ºM´\×.GœÝ¿Ý÷úû/ÍêmžÓ'½«ß‰2í*«oí0íºZ—m:žké8µ£þãñF#ÿª—ÛÀ÷›öúcw›ãµÛb¬ 2þ7É‘f³«ñä‰>a§Ff=³Þ˜Ã`*?¼Ã|W«ñ±mèŽá_Gáy&<]÷š±páÿcïÌë,®ÇŸ$H€@ÈBö=7’KÜ("QY"" ¦ÈA4HÚÚŠ0î©‚F­²C7yƒ âRYãžÖ-ŠV,.àúýÌ;ó¾ï•êïiÿèïéÏsÉ;gΜ™9ˬ笺~ììÙÖþ»ÕnàÕ¸ïlK¿híÔžC.1êÎñ÷µ©®³+Î /]óÙyñW·Iô]–9vt•ß÷”/˜sùû”3ç?ÆC ^9ÿ¥‡†WV!YÆmXyç±ÉÀ«ñÝY`n´8ö"ÿÆ–ä¶žx$­eÚ¤v'œñ~ùÊó‡~±Å¶gK6½%%Û±‹«Ôø7XúØ]ñÎÀFãw_õ˹½ß1ÐBi_øVÆÓ öø>*§UÍ‹m¾QËž~¶]^¬÷ÿíñ]­Æ·¡T|5Úÿ¨ÃÜì:¸ÚX^ðø”yóßtÎ?Öš¶>^e ð¨q÷ª}vÏ¡‹Ý}oúå+v¿¬´71ob¥Ó/Ë37¼ò¥0–Hèi£¹[­Æßkl»òòÁ›<‡ä.|äeÆŠãR…ÚçjKnýøÈưVàÕø{¯ŸõÈ»œë9tù7×Ö^ÜÅXyÝÂÖ¨úMŸcÔ~7(Æ–÷%HÇá– ŽÜ¬Vü°ã”{vù²ëÀc2˜Ý¯«ú?ÿ~Å-öyÙsyG9Å;ªþ”ufâ(‡/ÊJ{ž ¼ÇXUwâÂ~>a4<"&&ÙúxñÛò i޳n^­ø`Gêô=óKq¸¨æº²wÚý¸Zí» òXzË7¶Ü.n˜óÑŽé+~\£ødûÛeC×ÞëÐuÞí…—°Ï/Œ5=>Ÿ<²›3.z?ÌÒ/‹o Lz3·—Ã/k¿lß1}Å™Ž×9têõ¥EçšWͧÿåöFü³ö¼{ñ° r|Šo¶×lòŽ,;`ñ³±¶öækR?Yl÷÷☊÷}Ù¼âíMB=‡´\7⩌íWæ [äFþ\c±¹íŒ~]£øcÛ·÷ö-¼c›çÐÐ|tõb»ýë>~,ñº‡íóLÝnŸ~T|±mÁvfJw{Ém o>°íÔú›“½ôQº½¯Ñp_˜ûªé­Î¸t6 â“ma+ OÝ5Ösh‚¹À7Öªfñç+íudCåêãͶùd‘)®èŸ5Š?ê7oZ~Ù#ƒœñœ~—ߥ9fÔýjÁß»Çh¸Lv”saÑ®+J÷ítøl­â‹ú‘kêö}Á¿Ùkü¾Ù`Ô}òžý}ïSãþëfyÜjÍXtjÞ•·ºz:zx­â“-zßX¯sŒ„tÏß5ÓéàŸLŸdï.úîÔΡ+_tì×ZÅ/[änóø0Ï!e—~õ'c“ÒFƒÞg´æÓ‹^~&æ•U葵Š?¶¨ó#g|ë¢ä€ñxÚ w÷kÃ+·©&lµås‘9ÝèäðëZÅ'›ÿ6m÷EÅwØûã‡v Úz÷Wkǵ\ZüæÕ÷,û°hfŠqÛµ;ò¾NñËfymâŠ;<‡ö.­œòñGÆæe{öÝvïãFCûWå΀Åo_¬S|±yö©É“_\ï9ôìƒ#§î[il¹tɽ¯<´ÇhˆÂSÆ"µnsìò:Å›o¾áÉÔAWy©ù—±ÕÜOqÎÍcGŸXçé§Ç·êÍ¿ÌœÑæì—­S|²Yé}§?Õ>ªÍ—[woð>Ôïè¿®hûåwöþfÕÕOK“æ¬ç×)>ÙlN§+ìû‡ÞN™¹æþçíyN}~®˜{I m7½g&޼·>Ĩºø…fåwâ“Í#J’v ª÷úè`ù¸[?1ê¦ÞòôDÃû^ˆ\yÛv¶ÊÜøZëÓOŠ_6«}GNzWåÿÁÖÃõ/|Õñ ÷vû¼æ1Ø<[?Yýoóï:Í?ƒä„¹³ÏØáohæLc[yèr[®¼™%g¶]]4wuô%¯¯tø`½æ}þpXݧ2¶M9ó»7~Gy¹]2áÛ.,ø•¼Q@¹øµÏâãÃîOû[9Ù–óm»ß½îÛ… ~÷½ôå>·Ý®*óPx4?Éfßô¼Óžáæ„ÁØÞsá™ ’z;ò­ïŸYòUe·\áÈÃzÍWZ/FK\lž±}ÎÑÝÛ#{ fµƒªÇ6­iÚ ¼æ&«œ{q‡o”²úÛ_ÞxjR^—¿ã?«?ª¦Š¿Œ5<š_ÌëI³}?uhóóŽÄù›Bª× L"2ÛöÚãRU"þeàÑ|inly«{*ÆŽWßyÇ;1”{]ÞvxçK?ÿ0ä¼Ç\Âé7µÿïÌëÔøo”ÛÀ¿yÌáËŸÞ;>ݾñ/ÞÏåÆß{<êwâáˆëÁ£øbƒÚosøRš—9íÝöý‰ìNI޾–·¡ºo°ùs¡\E÷ ði¯â“:s}ýoÞ2¼¯ï—ÿõˆá}ËÜ€2n7^þTðŠêz›'ô÷õ¬Œ7 ¦y½Úð¾ êË…Ï5Ö]» xÅë—™[žÃçŽ*œÛhËcÃùƒîË;0Üðªs%cá·)ã6½…ÿð‡ÉÆB¥Wã·ú«—åI‡çˆšÇwî ¼pf¬­‡½‡L5þùÓ9Mû¿£œÇÕê¯s^©ç¿Ç¯økõÛ…ŽÜj»mËÅF5ž«×¼ç’ÛöÙòvdÎǤ¿aìJÞó›™ÛÒ ¯¼EÓý°£Ï6ªq\=ÎܘöÑûä»~u÷>ax•Ý·ôðjþþÏ‘¯½xiYGcÏ›c_ÞæØ}žn­ÇªúºèÝQ+=±I÷ŠÜÜZXÉsTÞâM³íŒ±×ÿ›®U;ÛŸmŸ(§ÆuyÇe¿º÷SÏQµ¾5öfL{ýxæ‹¶ÜTõzdÚ™ü‰À«q{,Ò¼ æÜçQû–6íÙpCã·ØvÖ{ÄTPFUàªm•£>wäA=³óêСô•ñä§gNßÒÎãÌã•a}ìuy•:Gâyjšsr‹çèöq?ìïáØÙ}¸¶ÍßQéÐ#—s®¶÷'«Ýô·µkf8r¼YñIíÇËŒ“Cç{Žªù˜±¯vúŒ§¬4¼÷› 0ª~¹wÆïμ¼ÿ‡ºlxÿ¯O´óUûŸÆ¾v´´ú-1¼æµóËþ۬ƽÖ|nì9ªî·†ëÆ{î;:Áðn•}1μ~iÞ¯_yÆgÊ+>¨Ýý\Ø‹éW;rÑZþý°‡zÆÜ)®Ì’/ ¯Ü®_|À±êÞåÕø×ê{ÿGQ¿ùìCÃð÷û b‚3î/›Œ9ÞR1ñ‰™ÅŽÝ¬øâ¡¨”óv¤=á9f."Œ&Ï—5á Ûëj¯î?ë|rѽ¯~WðÕ»][ü~Ô_Ç úÛwzM3ÞY]÷A¸¡÷EnÿÅ ¼ÝÀ+~¨eµÛ8ÆsÌ4;QFSm»¦5+&^µ®°øxÍæõ¶Ižc¯©žºt«=nzö›Óù ßuô›|mä*qÖëjá´{‹æÔ¯£C>[à96½á³}Ÿ?j4:¼òpâ–~´Ï‰«¤–ŸYãÓ^ÍrÖáòSïìþß{i]LÓëŽ\ê}›O¿:|²Eó‰>×>vç ©éŒýC®ÉŠ;#—5/æU«?–7m)§õƒ:ÿõ[{ÁïÓo7öÓË»vVßzêÅÁÈ•šWßêË?Ö=rcÿ¯§¯Íxn¬m×ìu»ÞG²ûm«ç‡B:5zx™-WÇôþ‰Å×ûkØSûÜ;Ì×Lîì+è}1»Ý[5è{־ɱŸ|=äû‰N®Þµ%$bªÓŸÊÚt.–ר*uÆg«æFíÞGº{Ž+;gìß™Ôýõ[˜Ÿ©ýS[ΫóxÊi¾ÐçøÖ{¡ã)Ë–}ÿÚ4cÿ q<6Èá³6IMâìß¿y¬ux Oi>‰47=ÇåuÔ›–û[ÞUðZµÓž££Ýum®CÏðZyíÓÅŽö—žã“»´½:ÆØÿõ;åwÝg­‹€Süð€y̘è9þÛ™S7ÔOEͺwÔý{ ïziw‹Íên^ñÃ2óx4ÑÖ'ǵzjàê ]¸ÝY/< å {^ºX½¿)9Q¯øb™œ_â¼ß:îMºz«3Œ§&o¿Ù¸ÞÇN1éK›÷W[¾Ëㆮo8ýV¯øb©Z×:t5§­|{×ã)µìŒÃ¢†7xÂm;¾Ä}ÇySÅé‡ÇMCm,¹YNÔ|äb›â—¥j>îiaÑà—þ™ñt}Ò—WÜÓÕðÖ?VûÆënc‰º¨¼æ}o´å· &]íj3žn=uÄ/v8vjç"©9Û¢äЇùÙ¦Æÿþó?ÙsÅ›O9ï.ïò n_ù²ñŒÿÖûÒ'7Þý~ïo}é/Îùî65Þ÷«}2O‹â㙂CII«æ±þ6ÇømäT¹s¼×û÷˜ûž–õì~£ã­Æ37†í[šô¾¥%ê(ðz\GÊ 5ÅN¿îm“/;mþ|F3Ýõ­½Þ×ç”>ý©Æ{©÷n¹£ê´O­—íýçg”}sÆGé {ÞmÉ=îÛÔ¸/Õú²Å<¶<ÏxV½ÃtÆIÝã,9±]«:G±Ûój;oè´µ£ggOy.ý‹G¾SçÎ}”!^óFéLgݰ=þG|òªÞ¿Ôí6žÝÐ5àÏûG;ó1C.œjl»ÛÝ£Þ8ex´ü«ýÏ«)ûŸ\ÛÍxöµ¶È…›Ÿuì«^çÞ¶÷–y[§RNËûó‹OÞpížW ~ZóÀ^ã@‡g¢–¿[@ÿíî4æÚ+Œj¿õÏ>>LúK<å§þ‹½þŸã¯ñ\¬‘ÿX#ÿ¬ÿFù¯B÷y«ö{WªýÞÑŽv‘:îr“ŽÛ¯}¼ßþ Ž½\¦ýÞ5*ÄãUœ4ÓçKû¼_`‹öSªã¦³ÃIß0>q—+U µŽ­:îr…öG:œÁåÚŸq«öWîãÓØ¥}àÕþDìåF¿Á­}àÕkx´!lò}lúÀg×r{ÙöGºuv£ÝÉï^¡ã7ÐÎpÚNáÔN="}b/7ûÄ^®×>ðhcmŒ€W#¨/’6F"w‘M:võEUhÿwäG“ }ÑÍ:Žô÷ªÖq‚t¼åùÚÿ}ãÖþï•ÚŒ%?–üØ&ß­DÅx‹kó‰±\«ýÝÅëøÊuÊ×éç®Bù¹K R1̘ÊÀ&1þIàN’¾5Út,eÚ•|PÅbH)ÓqÈKÔ1À›JÙ´ AúähÓ±’Á›Íé¤3âu\dò3hw&ueBK&ø3É„¶,ú% œY+T¬ãlpf×±IçP&rè»ú&‡2¹Ô“K™\Ò.à]´ÇN8]äç•èØÄ-*Þ–“x¾ò·'Í…“˜úòÛ”ÿj¢4ö¾?ôô/Wþ²elÓ·e ê•i‘~†L?{+Tü;·[Ç"nV¾²¥!ÓÏýWDºˆtùEô}ÑIåK[úñHz ébh-®V¾¶myþWÛæsvùœ]>g—ÿµvYÊi¥îSÊ™>Ë´[èl¯ã)Ô±­öåÊ÷ZûfS ~õ¯öñG[®c*QO [û¡…æñ>ñ”À×|Aà ¢þ &Ÿ˜¦óu<¥6Û´Rûn# ¾à í“–tHšŽ3ШýÒ’ß™tç÷´K“öK[§Zû¥…Þ0è«Ö~iO똨*¦R· í Žt7êìF;º“ß½Rû¥¥á´+œ:©#œ:{Ä«˜©fL¥;µRÅ1ˆ_mŒ ÒFS_$mŒ\Àç öIK}QÀG‘E~4ùÑÐÝ¢ã)A¯í6LÇY¥|ïFí“–ü˜JK)øXòcÉ=¨ã(•êJ'uÜÕJíƒ|}Òtì¤z¥^JTì¤Ê&RWb©Ž—lcŸT¦|]%T~g“iWr³ö5;^û™%/5^û˜oj«òËeú˜%?ütʦƒ7šÓIg@GõfŸA»3©+Z2ÁŸÙ¤Tzý’Î,ú9 œÙàÌ.W±Y³IçP&r軜F¥ús©'—2¹Òï¢=.pºš”IÈ#y”ïK~ßå›OÆkʧ/óÁ—O}ù'•ß>Óÿ-4ö¾?ôô¯P¾‰¥ÿ[›©€²ÔU¤âR™±š(_Hy7¸ÝÔånQ~€¥ï>§IÆ·-"]Dºˆü"i£O«øY2¦Ó@ÒICkqŠyaÚ!ùϲþ6XÚUÙa²¨Ó¶QÒI»#mŽ´7ÒÎH›âk;¤Í0«ùý½ ðÕ÷R×[:ÞÒë–N·ô¹¥Ë}u·¥‡¥þµô«¯N•:TêNK/Jè«}cZzNê8ÆNÄk_”-:Î24Th+”ßGûXÆ éNû›u,´VåC:Ò­¦ Ñò¯”79÷=©ü<›>Ë´?çÓÚg3㜤|5'–)ÍIä'‘ŸLÝÉБ¦Ôé8a”Iã{ùéŒKé,É»’?ùž ®ƒ*Vp^™ò™_®â€I?ÊäÊùåÝ¥Úg2uÕjÿ’óÕÔåÜ^Šß¹9Û¿ss¶ÿ–9[¤ï:õ»G¢\ÇhÕ10ÏvÍ:~éöðWû:_>žðþ5Žÿ\3&}€. ,á| ø¡«CšŠõdÆhVª"œAÐtð¬8˜ôsÇ“:&ù¨³8ƒÁLÁ¤ƒÉq9>x;ƒ°3ùIw®ûéX˜¡À„"£¡ÐJ:,RÇÁ¬Ñ¾{ý|ba’ßòÝJt<:®½KÇÂ<¨ã Юpê§Žpú¥GšO<6 |=_mŒ ?¢Q A$ø#éçHèŠô‰%@~4ôF“Žwt«Ž%P¦â`öjÓq¨»7å{Co艡}1”_,ð±äÇVë¤ãHÇÕùĽ6^¦Á×Zûî®`J•áÊ&RW"eé‡D`“@4^Ÿ”*8:“©':SèƒÆ>¥^dzo*xSiSj›ŽeIÙ´z¥®Ó©'6¥ÓGéÀgŸ|¸3À—I]™ÀdBK&0YôK¼‘L8³À™ ÎlÆ)›t6éÊä0¶9ŒmíÉ¥L.4æR&.à]ô¥ œ.Ú—G{ó ):ò(ß—ü¾¥*‚Œ[O~>mʯW1Ædü3Æ&4ö¾?ôô¯TñÅd|ÍÒôGe ÃTœ/3^A£23nêvS—»UÅ“1 d¬‚¤‹H‘.WQ³2E2„»€t1m)¦|q­ò mÚùOÚe_;,í¯´»–­µìªeC¥½”6RÚCi éÓfYs:i—,»#mŽ´7ÒÆHûbÙ_êÒvH›ák/,;aÍï|çuRßKO;mnérKoK-õ°¥ƒ¥î•úÖÒ­RŸJ]jéO©¥>´tŸÔy’Ù7ѦãÑÑÏíÝ:ž/ý@~ ãl¿-PqxÍ) üÒ™ñ mVñpe,‘pÊõ€G#øoE–ê8¶|æ{¯ñ*ÖGïx5}‰_,üÛ¢cÏÖ«Øñò/u&—P­bo$©±2¾F*åÒ€Oç·ô25­É€¾L`3¡'ü¹Ð– \.i¿çQo>øò+uL“*†Fcë&o@›ò.c¶ËùƹyݹyÝ¿sóºÿ–y]šOÙæÓ:>C…Š Nê¸PŒG»t{ø§}½ŽÍIyàýñ& MÇ…jTê Y>ø@ðwpéØPu:68ƒÀT­ã›GžÊOÇù„ÆNà>œÁÔ,Óà A^CȾ3å;“ßYêáúǵãDIýŒ †Bs(ùañ:Fé0pt ò‰%õ8å»•êØ õ:ƹ[ÅfèÞ¬ã3¸~"Æ9mèѨãD‘î ¾žÀG€/‚6FԜ߼FLj‚ž(ê‹>Šüè0ßÜÑàîEù^ÔÕ‹>ìîÞi*þhoÊ÷>|1ÀÇP í‹_,ù±äÇ‚?.RÇ…‚¶xpÇO]ñ2}ZǃZ â˜'P6üÊ&€+1^ùìO¤îDêN¢î$ÒI*nyr©Žÿ)ôI }–Ò¨c>7µÚ'F9¸Ò #ütʦÓ'é´1>Ê  Œ{tdÐçàˤ®LhÉ„–LhÉ¢L0YÀd3 œÙÀdSg6élÒ9¤s¨#šrhO.mÏ…Æ\èȇ \Ôë§‹tùy´'öåQ¾/ù}ËTÌu;=Ÿt>øó•ÙqZ͸éÐØ¾ë=ýç«øñfÌtê*we #Uì 7½úÜ´ÑMÝn`Ým*‡;ºPwé"ÒE䵨X2~—ŒG5t1í/&¿x…Š;kêrùOÚ埲Ǿ±v$ÓZöÖ²µÒÆžmW¥µl¨´–Í”¶Ò×NZö‘ö›¶Ñ²ƒ–Ý“vβq?eÛh‡mË,æ“SÚ-ßÒÒ>IÛdÙ#ËIûcÙio|íŒec¬}A_["툴ÒføÆã±lƒ´¾sʳõ¾¥ç¥Ž·æ˜R¯Ëq±ôøOéoKoKmí#ZúYÎSšüTlB9=­bÑ´ƒæöô?ýÁ\pŒs ¿6ê¸4ÀÕè˜{ñ:ÎyÁðM0CàÃø²ó‚ÇÓ …OCáËP~ KÓñiZT¼äî%:ÞI/ù´ŽLO`"âUÜ»Hx8’6Eñ{éhðEK}E~/Ú 1ÐC±i*NrmˆT,(ãÑô¡\¿'“H™$êM&˜ä*Æ\ éT`ÓÀ‘F?¥‘N‡—2hkù™Ô› Ž,ÒÙôS6mÍo.uç‚×Ež‹¿yЕþ¼zs.|ùM*Æœœö—rŒ( ¯ØBt‰›6¸©ËM7u €¦ôGxʵtó½XÎGä|Yþsû|/Ñcîç÷÷óéõ\úGÿ/Í¡ÏÍŸÿñùó?:wþo7—êq:¨D׌cÖ¨c©Â›íèÃvÔ×>LDzgÌÛ7êxªÈŽ?<äŒ?uP6€üpHù@ÒMJt ¿ðÀD~ùAŒqéŽÈhÇr>5>qÇ «eƒ‘ý``ƒÁܤTGH `Cè«Îñ|Èï |ghë -]À×…ºº@[`B (<J~´‡‘F:Œúº†éfàì =Ý€ïÎn Ί© ½ÝÁ¤cªVª˜ªf¤ûPwu%Ÿ°BÅKM$®DêN¤î$—ŠŸ®dp%Cw2íO&/…þH¡¿RÈKo*xSkT\ÔTòÓ(›6_Å‘O‡ætð¦S6z3è pe@GueŸ |&ý -™ôae²€É¢|ùÙÔ— |6élÒ9Àç@[ð9àÌ6—>É¥.Úá—‹<é<ÒyÀæQWeû’îK»û’ß—ºòÁ•O[òª˜¶ý¨«´÷¾?éþàîO~ÿz£•²ÔU@ÙBpV¨­…ŒºÝ¤Ý”uŸVqiÍX­uʬ‘.‚–"pQ~ }1ü¡e ébú¹˜übò‹¥ìKÝ*ÿYóc˾J{jÙÒŸ²¡–í´ö Î¶™Ò^J[yö\ùliÙC_[ˆì˜vPÚ?_Ûw¶½;ÛÆýÜúl›&í™ï¹úÙóhiŸÎ¶M?e“~ÎYvèçle~ÊîÈ=Ë~H»aíµX¶Â7¾¥´?g|mÂÏÙ‚Ÿ›{ûι圾“±¾ÛÁíàÍöÈI{xÏ^ó&€ñ à·àáÏ@ø¼°(Ó> ¯ã\#+ÿŽôC§0 ò ŽoÍßÊ„0Î!ä‡@S`»€£ ßCá¥P9?©ãWÓ–®Àv¶kÛYñ!©+œráõ:>$¿õ$¯§ü |Ï6“ºUÇ¡†G"‘ŸHÚE~4õD7ªx‘½À}1àŒ¡îXàâ %n¾ŽÍ÷øƒ:f4øû §}À›\ð‰Ô“žÄ&#šrI”K¦O’땘¤Ð)Õ:t™Šÿ,ãÿɸÏ2Ža:õ§ƒ#Kê'`³©#‡üêÉ%/—z\Ôã¢Î<þæQGõõ-×q£io>ðù”ÏoVñ¡û‘ßšûÓÎú¦ØÚXl!8 é_7¸ÝÔënRb: ^Åz.¦ÍÅóý~~žþß²ïýŸ0_ÿwØïþgæêÿ?çéÿé{Üÿªyúx=-:†q ÆSоvÈc»r>ôU;p¶GþÚçíoߤÄÛxøÃtf|@~øãù€?¤½À×øàï@ÿvg8ƒÊUlà èꈜu„_;2¶ÁщüNàèÎNàF†ƒIƒ3˜t8C )øðu&¿3ùï ¾.àëB}] ¯ ù¡Èx(üJ}¡Ð=aÐF:Œ~íJ}]Á×|])ß |ÝÀ×úº1¦Ý©¯;éîÀw>œt88©#œ6õ {ßÞí|Oò{’î ¾žà‹€žÚA:z"ùY¯ÔZuEE^¸£é¿hh¦¯£¡µ´÷‚Ö^•êë ®Þä÷¾7}Cùú2Úb€%?–üXðÇRwåã 3Øx¾Ç/mmîC½} ¹4%—@^åÀ“žDêM¤Þ$·Š©›ŽdêK.™v§ÐîÆ>|)ä¥ÒîTp¦B[j³R½iôSý˜F~:?¤ƒ3:Òik4dŸAùŒ&¥ž3é›LÚ’ MYôEùYäg5+µM}ÙМݬTxøsÀ™C™ÊäRG.er¡!—v¸ ÝNuºè<Òy´%ï Rù}iw_òû’ß—ü|ðåS_~‹ŠÝt?èï|èíîþ¤ûCoù”- îÊ‚»Ú i›:ÜÐå¦Üúix0.è·"ÒE¤‹È/’é6§ZÆ‹(m,ébè*†Îbò‹¥l[ûØV¬xiSÿ_ósi#-›xö\ü§ìžeë¤ûgæà?7÷ösK›$íÑÙsîÙº]¾wΞ{Ëy÷ÙóíŸÛ÷ñµ%Ò†À?ÚǶì‚e|çÚ–þ÷ÝÓ¶æ×ÿèÜÚÒÛ–¾>{/Û÷^l9ú>jG¿´£^ÆÔ¿T oº(> G ãÖ¸ 9†Ÿ;ÂkâùP¾uó wBàòBàÃÎÀuaüºð=t¾b‘0p‡ACWàº×˜nÈPwÊvWw9W°ß{ò[Où¸žÀER_$¼ Qå*®zô|W½×>àéM}½¥‚¯b¤î! ||ýq´%¸>ŒIèJhT¬™¸@ŇN¢ýI”I6 Ødú%¹ Ö…¦ÒiŒsÓÏ€† êΤL&ue‚/Kêú-ü9üžC{sø=·N³:uô¥ }iO>yùÀ烯xû‘×?^Ås/ íÀW=n »¨xò §ˆ¿E T÷R6¥Üœ»Óðï»'ûŸ4ßûWîÍÊ;JŸóù‚Ï—|¾ò3ï6øáó5Ÿoø|Ëç;>ßóùAïáòŸ`m'XÛ &%XÛ‰zík;ÁÚN°¶¬íD¨~[ÌÚN°¶Ø(Ø(ú“ ÁÚN°¶LpvK°¶qúÝk;( ÁÚN°¶Ø3‘ªïÛbÓk;]¬í“ÁÄ@äê{Ø8Ø8ÁÚN°¶¬íD¡ÞŸÆæ Öw‚õÀö Öwû'ljÔ>æ«»´¦_€xŸ{OMúÞSš~‡X«î´›s7—z[eîq¶hn}¶S¯ß$éuc¹öШ×~Úg@¹¾ëÞ¨ïEEê3Ÿ í? ÉLJ@©z£%÷EÍ{Rmúþ{‰¾ßP£îÁ˽Ró c˜ö-P¦öMÍ÷Œêlȼ©î›kÏúmc“¾'ïçóƱ\ß—_¡Þ:šçF~z]êÖçG•j_ÖôGШ|˜w®ÂôÝŠ2}÷ªVß«oÑw°ÂôS™>gªÑþ šõ¼5Hû-(ÓoÐj”ÿyGÙ\ׯk?åúþý õvÒ¼·á§ïn”è{[ Ôf¹'l¾§ôÓw¸JÕq~¹¾¿U«ïæ7ëw•ajnlÞÓï*Ëôýj}FÕ¨î˜óåH½¯ïoÔw¸Âô9ÕxåëÀ|OY©ßTÖ«{\æ»ÊH½·\ªïˆT«{"Ò×¼Ë5˜üÁÐ0˜üÁÐ<\ƒÁ1O›Z²–SL 0%À”suœ-çÑöó¨ç|è9ŸzÎf0CèŸ!ôÏêÒª–¼Ð?PçÀ~(8‡B÷Pð ¥ÝRß0Æhx†gôƒ–aÐR l)°¥´±”ßJ©¿ܥྨU-.ÿÅ|1õ_LýSÿ%ôå%Ôy u§Ãé³áàžôÝêAÝ#€ÜàF7 FAÃ(hEŸ×(`FQWí.ƒÖ2`G;ÚFƒk4¸Æð} øÇ€ tŽ¡ÌÆ{ ã})eÆÒ¾±ÀŒ~,4ŽïXð]Ö¢–0ã¨wåÇkyãÈ»¼E-kÆ“7ž/ã¡g<íÏïW€û pO€Ž ´mmŸÞ ”½²U-®¢ÜU༠œWÑÖ«è·‰-jI4 Z&sy“èçIü~54_ ­åŒQ9ýYN[ÊÁW-¿çd~ŸÌï“ù}2ø&ƒo24L¡Ž)à™ž)ÍjY5•º§‚ø¦Sn:å¦Sn:ø¦3>œÁ¸Í ¯gPv¿ÏhVK¯™ÐvõÍ¢ÏfѶYŒÅ,꛳¨ïú÷h½Ü•´¿•´½’úo$ïF躸9Ð0æðÛào¢¾›HßDú&`n‚†yÀÌ+S¶ÝüwÎÿÇ¿Ï}©sëósëósëósëóÿÅõ¹\—Ÿ[“ÿSkr!e^ÚèJ¿s¾ªµïŸ“ÿ ?¾runñ£3Ùù>÷~Jwä?ºC٢ߕjÿ?ú\Ö¥Ï9êõ[T?íh¾ }>[§ïSºô[Õj}§²Uûç¯ß”ÔïWK´/ zýþ(RûªQþ€Ì³Úxíhz‡dúJó¹cyP¿pi?'+Ô{$Ûgß}~Ûª}•è»Gú½y˜>c©Ð¾‚šôYKöT¡ÞÍÊsóA¼¾›T©ýôñ!T¦ß£¯Ðw2Oê·K¥ú^f­zÃ$Ï}M¿*‘Ú·Ðx}\«} µê7¹ñúmS¹¾·Y§Îxä}FóS¼~¿^¡ßîÖ©wìæ9qò (ýôÉwÝE%ÊÞyÕê=Ðà õn×|ßîÖþÿ½3ªÈvE£3ŽŒˆ@ Š¢@Ó·³ïû¾wöÎÞÙÛÁ%*h€ (¢ˆ¨Ñ0*h€Âzƒ:6 ¸×aqÁE?þ·ºª»ó3ŽóÏ3ßçï7“–ÈcGÔ]½'ªÎT4«óT]ꬼ»zo4Vþ-Þg%Ä™*ñ[´8//ÞÝ´çìQçª<Õ™ùTu¾j¡z‡«S½;ê¦Îeå+û™Œnu.ÃSý&m’¹Žìgå­2gý7éCê̼—ú-:V½7ºP½;Ú%ß5Ðo@ýä6@Ë 0|ŒèeÆŒ#0F`üàá‡,~‡eøî<þðñ&˜ì€}àL t±O <ƒ€f4ƒ;zAè ¿|è„ o²„ K(°¡À†¢c(m¡Ð …n¶ ã3úáÐÇ?áð‡8ü#°eÄ!¹tˆ„g$6‹„N$t¢°]¼£à\Ô!¹´ˆ.b!b C |2%½cË¥G°qÈ­8hÅó=úñÐGÎxä‹Ç×åô%€“ˆ~‰À$ŸˆŒ‰ÐN„^¼“Ñ!¾Éà'ŸL_2})ô¥Ò—J_.vOC–lXà%—!åbm ÝjøÔƒWÝ*úëiÏ„V&úeA£Y²¡“ ÿløg›>Ùôå@3ùLà™°¡ ùMð7Á?½óhÏ£=ö<èåaÿ<ø@¿:Ð)€~!ô ‘³úEÐ+¯¼bðŠ¡WÜ£^%ÀW¥Ø·ÜRÚKi/· ÙÊáW>øªûWÀ¯9*àW…M«µ Úô·@¾ø×ÐWƒ\5ÀÕ"C-2ÔÒV |ü¸nມK.ÏiDþTÚíëñ7÷K®mÄzF¬eÄ:†5ËÿµNëÇC¬'ë±6ñ>¶±ÇèýãsGL.âo{;bíþ±µˆ«EL-âiG,Ýœ±³#N1ñéñ°#qð/ñ{Ôéqïé1¯#Þíë:â\ÆŸ=Æñ­#¶uĵ¯ªxÖËT1¬#~}WÅ­ªxõˆŠS1êq—þ?çm3ËüŒ"7²ý]¸Xy¾[¼g-òÛÏkw¨ümí*Ob·<ãh×ÊÇuÎQœ ¶¿£¶P·¶Ê¼‡~Ì~´ûvªœ{2ÿ®ýœaª:gÜ)óêŠóÁ"gŒ_¬<ïàŒ0¾èà‹<¾ÜÛ~Ѐ~´ñYò¶Ëœ‚âŸ1>Áð /;D@3 ð s*x)Ø –Ï4xÄŠy¼è– C9ò™“òá_„\Eð(§þ%ð)xèZ‚­JÀ)§¸èW¿œþJð+¡SÇgÝ!9%ÔcËzxUÓVíêÁ©‡n=4ë¡WÀüS½rd/A¿"è€S‚¬Uô•s]Ò)·ŸÊáU…ž%è\ŽNEüWáóà ø¬‚O=ðEÐ)Aÿ*ô,‚N=x%𯲀SN=ÿUÈSƒßª„bÞEç*ä*8ð¬‡OºÕC«œïõâ;z• :‚žà ÏøÔƒ_ƒ­j U“Ü"k@¶lcn =>11~þÅöIéX·ûŸŒsÿQŒû÷âÛŸŠm…o¹Oÿ[ò,·ªZ]ò\Ÿ=·f³Ê—Þ¤rivÉsÆöúrf™kYä²ç¡kR9èLªfÙ$™÷\Ô·õRDþK?/yNœá»¾UåñîgeÅ;ûÙX«ÀÍ tBî äj—K€è„@':!È‚,!ð 6ØPx‡¢cvHã3úaЇ~8ôÑ1þáð‡zFÀ3="±e$t"¡ (t‰‚w¼£€‹.¸hàb!b!Z1Àį"èÇÂ?—¶8àã ­8hÅó=¼xðâ‘/_Äcçw¹\ID¿D`O„n"2&B+ ÞÉè ßdäK†V}9èŸÌg*}©ô¥‚— ^*6I…nÚ!¹ÜIÇžéÐM§?ÜtdË€f&òeb—Lhf¢s&í™è‘E_64³Áɦ/œlhfÓžÓ-—J&hš°§ <2˜°u.úçÑžG{íyÐË£=ý  Sè [!ô ‘¹Ð"—WÅàƒW ^1ôŠ1#_)ò•°ÜRÚKi/· ÙÊáWÍ*ëØ´~è\ ¿*ì[Õ-—e|f†ÛXà_C_ rÕW‹ µÕÒV |ü¸nຘFdh¦Ñ,럊¸Øþ'Öé?µ>kòºÿ{êjˆµšcMÖ=æX‹‰u¾u®½þѺ«ÿz«ÿúj¤›kM%ÖSý×Pý×OøöoÖAøøoÖ8?·¾ë|m_Lj5ŒX¯ˆuŠc}"Ö&b]‚kÇıþè¿öø©÷†Ö¿šµÆ¯¦6L“ª‘u\ÖIJ×6íQõè»ûå×kWùA—¨ú‡ UýÒÔ~¹û›eÍR{žþ6YƒÐžz¡ÌK)r}ÚëV-QõÉš,"µ½þ(|üÉÏGæ•ùîDÝL?øùe.y‘ûYäòyïì9+-²^º·»¬¯)êeŠœ¾]27¥¨yîÛ-ë‹úr=ÞGncúçg•µà—Èœðl•uE#à øÒ ø ÞÁâÓ*sõÙsY£wüD;òF€…IÐM>¥]Öƒ·ÿ´Ý|“~r$}rhÏ>Ø4xe@#if•{¦@?Ò¬²Fhü³à—†]“1‹ÿúr ™~íùÀ¥ñ=Wô_‚Að*B¯ø”ˆÏTùhÉ¥?œd®„O5xÕ´U#Sþ-Ïn`Ëé¯F‡jä©®ž1ô×a‹jñ‰½ë‘9úñÈÍzt‰‡N=üùžH_"x‰ÐH>$C?Ù’‘5YèŠì)ô¥Ò— |*z¥"OªÐIØ_¦3fÒ±M:r¥C/Ü dÈ/¼LhfB3;e"s–ˆ“èËF†lhfÓ—žÙ´ç _ãÑ„ì&|cBvôLÈ’ Í<ÚóhÏ£=zyÐËC†x@§:ÈUýBxB¿zÅàƒW ^1ôŠ)ÆV¥Œ›RìQ n)í¥´—‚[†lå=òÑ^nØ»~ÈQ¿*ìY…¬Uж ¿t·À¿†¾äª®j‘¡–¶Zàà×Àu× À4 C#0È?Ûþ½úFbî·ÿþ;Çÿôoõêý+¿kü§×7ú%~ÏøOÿ-ãòwŒÿ¤ß0~fM1P§KÕérW5m›U-GÏÓjÚžµE½ò‹Ðï"®{«š¶ U s®‡˜Uîh …çP®‡vÈÜÙö:3f•û¿[Õꊕ5™Dí•K¼TNí%ýòÿ3¶‡AX·ÌI}à U UåÚFæi­ªr?¬ê€7­MÕî:¡jXU-hpWù¸Åš¡UÕ@/tö¢Í \¯Ã²6€æ©êÙt¨z6\2«Ú“]ªF v—õ´D›iЛvBÕžn:m‘m²Î»½v¥›Ìû|‚¬û.êi"»/ÿÁ^2o¿/|ƒé÷…·/¼}óÅ^¾ÐòE6 ý5tÒÀ×h GC#<ð †^$´Â1 Z~ÈäOŸ?zDvʰ)€¶ä @ï@xâ»,äK>ú…ð4#S0´Ã°WrG#K0ÿYàEÃ?½Bàí,` ‘1aØ'LàA3ŒqNØŽeÐïñ óC')üÇÂß íH| íÈã2|Kå³ zÑèÑB`¢‰~ƒ_øÑÀÅb£XxÆB'ŇŒqÀÇ—¡_<üÐ;]áŸlò'`Ï\O&“ƒ^Iâ[&A? }R°oc» ßR ‘¿øU—zB†iè˜Æuú¥ñŸn42ÀÍ'œ p2à›…üYÈŸ²„èËB®,úsÀÉÁ&…Èž\pf!#m¹ÐÌEž\ÚsiÏ7¼|èçC#ü|hæ£W>8ùŒ‘BìUˆŒEÈgÆŽÕÈX„|EÂîô›±“^æã2”-Á%À”ÑWF_øeà—ƒ_ŽÊÁ­„w%<+á])ì€Í«‘»UC·9«‘±¸jd¬®½ë¸®ãºŽë:q ^°+¼¬Èo…–ù­ÈoE~«h±Zÿ÷{ûçIëÇÚæ§Ö4õæþ3êÍ9òFˆøúçö¢ïþô“nÏù§ö›q®ˆqE|ëˆmE<+bÙþq¬¿›+'ÅéïÅ”ºýí¾²#Ftă"žë+±†ívûuÖÓ3ö«YÚѯNéBUß:ƒá9:C€ ¡ÌC'Ó {q›ªGê)둊ší7—um¦tÊZt—Òg„ž¡[Ö 1@Ï×CÞÒ`èHû4ôœ mM<ÿàíŵ|ÍCÖ-ÒÐÑ(ž¹Èâ­ŒÐó‡_rÁ+ý}½dýé Cr[A± üÂÜe­êÈVYÓ#zaÀúC/úöˆ£/zúkØ5Þ‰àj‚7ò‹gº$C;ºqà&ãÃô¹‘Ž<éðF¾tl’.d.Ý‚¡™I[ }&Ú#;ÔÔ~4v §­1Ž= ‘à ^¤˜ßÁ1Ÿ…írš)7Ù3Ä?òdA3y³ÝʵL<“ÐÉÏtÏà{>2”a·ZÚjá—‹>fÁ = ÅóÕ¿QÄb^¯Œï•‚íuð³Š61·{Ë­+}•ð­†VrÔ!§Ućày›äÅþ÷ïú~/úÿê÷¿~ ïõþ3{_ŒÃznÿëþRïñþ÷½þ™=/£Û?·ßõK¿·ûß¹ßõkÚëú¿¡Ô»<­Þ¥Çiµ“½úÕ§ïìWG™þ‹ºå#v04ÃJ›¬k>žCà1D|‡ÆPú‡Âch—| ‹š—Ó 7s’ª‡þžÐóìR5êÑᳪ{ þ0oU§˜aØäRp.EÆKÛdME_wU¯ž¶áðŽm‡WõæLª>3´/wWµçšTý9tÌg x#Údºi\{as/èx‰ïÐ ¿‘ÐñoRõ0¹eQµë»U­:xf4m£{T=gÚ®H•uëD öPd¼â„ª“™*k<çõ“T-;ð'wËš™‘ UÍL£¬ Œãgð3‘mr¬o¯K ¯ë€½ÎKÖ“¿ؙȪ‰ïð¹YƒŽËÚÎSN¨:òîªF%8S‘gª¬M9þÓŽËpÅÇCÕy¦ºè;.ëSú3f`£ôÍ o&²Î\"ë?s.âWú£áë+þáë Œ/¶ñEçtèøÃGÃ.šˆ£EìN»]v#¼ fœEÜB†F~ÈãOŸ?>ð§-€ëp¾ Gºf![>ß±O ´á ïk‹¸Y‚é ¦-˜¾`x#S4CàrB†Y¡‚z„‰Ø¼0pÂÀ 'ƒ¾0pâðE¼bá›lŸÑЋÀp‘èψÅâ¿HèÆò= Ühp£Ñ7üh`¢­D2èÇÂ/~ ŒƒXdmÐÌ¡?{šù‡¾qЋ‡V*ë”ai<ŠÀ/·>fd)‚¶þfìS‚î%ô— G}eø¸ì¸ ]ËÁ-¯¼r±.o%<+áW)Ö1èQ)ì¯jä¬ëä«®ùª©ãºŽë:®ëÄ58uàÔc…–ù­Ð³"¿ù­¢9¬ÈaÅŸXÛ8Ö4?·¿%Ö(Ž5Éé{Y?µîp¬5ÄãŸÙÃú{{Wý÷¬„ñÅzàô=+ÿ‹Ø¿ÿùÓ÷®Ä¾ÕéûUïwìþ±¼ˆá±ã¯¶^kÿ|¦"öý{ûR"ÆíÿŽäOų?Ëö_ûǪ">±§#î<}OÊñ^døi1dÿ¸Ñ‘3Õô±¡#&tì_õÏ›z£Ûß¾éˆëú¿ÿ(üdrûÅëÙN^xZÝnæÚÉKT½nÚÓ6>CøŠý†"ÿÅÈ5Z3¹ž Oà¦5Ëz®Ã¸Ç†!ã´…jÙ=#ô Èd }¦YÖ‰žÒ-—óä „¿?sÁäN>‘ÍÙ}ÑÑȵ;Œâ»?ôŒÐò‡—?:?:“i . yÃÀ‹„W´ˆ/ºÓų¹|}äÒß—v_dÖøô‡¶FŸ&ž à'`ÃdÆl2º& ‡¿xB3ú)èœL 0éÀ¤ S&ú¤#&×™àdòÝÄwßó=ä´~!:"!62#‡YÌÃÈ`Æ?f„2c3r˜Å³L<¿€Ï‚~,ÿù›Ç!C¾À…F8eà&@£ < ¶°tÉí‰ às‘µ¸Fð*¡×ïFìV‹|Ø._<×Àk„¦Ám„F#>*®Q´¡ƒE<£>xþ­âù‹Í+áo…OòUÃËŠ-««C¾jÚ¬â™LŸuâS<»àgEÏÑð÷6K™Å{üBÁ[ä‡|Åï/âÝy!—x/_ì­‰3òb?L챉ý4±&ärðûcögI³Œ-Åoœ"÷ŠØ‡ï Šç“ØGµ²„D>X!³=׌EÒ9„¼"G¬ 'rˆßg„Ü"GŒÈõ*ò{ D^o‘çUüv#~§9^Å3LÈ.žs"øÝÆñ¼¿Û‰Ü­bÑêxþuÉ<´âwñn¢xîÙSTw©çŸñ°ü=Xün+~k¿Ó‰G“öšhKäû•â}Kñ›—8"ö\E­¬V™ç[üŽ)=â]Dñþ¡¨‰%rïˆ\2B‘[Wäv°ÿ~eQÏë&ù{”¨o Îi‰wñþÕ?ß³ŒÇÖ4‰oÚýK“SN–,1|ub]óûæÏµ¡¯>kÎÚ %¯õ“Æ¿)M2¼&¯O§¤Û:fõŸt«v͘Ù_íøè ÞYYñð‡ÚySß;6<`|Z%ŸÔ½^Ï=q\;–óÉŠ\o}G’ÛïOߨ۶^¸b”Ao¾{ê=îÁ^ÀwÚáZyÙ•}5†SõQI—•hŠÏy4íK}‡5#ÃR2B·u¦q—>÷Œ»ëoô~W,éƒï%õÞ3æ¯9k´Ë|ÚÃן£ïxoÆ——™¨ÛfÍ=?=ã?—Ükv¼ÅßÙ´«:—üö /ý¥sg¿ê³éºÍRðñ‘'vèÍû³ßtåàMvø/N}ï’«µÛ¸g²ÃnúK“Wžl˜0ÛpàªÚju[Åå-ö~ó‘óŠó._Žƒ%ßÌö ~I;ðúºc\±þRæœßdx+óÚ4CÎÝVzËw½£öi£•æž½ç‰ÞÜÍàKÿ>tnÙÆ–[¼µGÞmŸÛ:NéÎñãvón+™XyûýKõ¹3g˜W/õ^ú÷¡[óÖ?{s²Ë¿_­þôŠ)ÈûœçïܾÆðò“­¼!ü£µ«¤ýõ¹IÕËÖÏÑ.xГ~~èǩ߾¿ov€Áþ{?ý¥7EºÏÃphÇ·­ë>Yà°›ÓÏs Ç¿ÊÐpÙÝ&ý½´ïܦ5þçhÏ~ô‹¸çõ—~4‰~‘ËÕ6«ÿ¤µÉjÜÍÝúÇúk]òؤߗýîà >ªüýýŸN¿f„sÜuÎ_“|j;~øË>w¦és7þþ©>Ýžôû#çtEdí)pÞGmþøéŽzWHsâÞ Ïºô‘vÕ ê¾›ûÉMU[ÇùöÓGŽ‹G¯zaÔ•#lN;œ‘ÐñAÇMzWùö]3"’ Ýz…Æ=Ö#ßjcCïV?֤ϾÒýÚ±G\÷±MŽ“Ç–å=¾î¥—]òÅm_´vÅ|½ëþ__½L·åÆ_U¥%èóâ.yùœ xr|´ÎY='ô©­ÚÁ’A7_|ûkz—Þömoe¬c^Ñm™›Ÿßu¿Ó?óû?ËŽc‚Ž7OI¬ÿÓ°Hí`SmÊG–÷ã¼ëÓî«_}äqÝ&ç}ÞÆ¯ÓÖvßÜO~9NÿæŒoz¡U;xïImÅÛ?è;GßöûæÔSàÉ83ãu}Þœ»½õà7ÆcëäxxâÕÙ _,ÿ³vðÑõÕ)úÎŒÃ3Sÿ¨ÛÒ_#AŸûŦGF˜<—~ÿÓ¦ë^+§\uêöñÛvë;ßvöoMtŸœô¥¥g8ÆxÒï¶ÿJíàö«z†½ý”¾³ûÑ —_>Ùee×¹±×]>ãæ§]vY'ý¼ü‘¾Ëo,Þíò3TÚÌ×w[u,¼(Ùågá¾½óô¹];'ŸÿP°Ë>ë¤W$eÆÿuÂãÚÁãÏ&žñžsÜîò}âÄÖst[~جºÁË]ãÄr÷Û‰¶àK?¯ø`Æ“oêÓzÏ=²`ïÚ›õ]%÷|t_µa§œœóǼ׮¾áÑ;]ãuôï“aãî5ܤõz]ûòsc†ë»–ž×óøÐ"—üÙk/Ú6ê#à¥?Ÿœ›ë{íÆõZïµ…¯>TÕ¦ïÃ}í÷~º-åæÅ)ƒ7éóž³þ×gÙ]Æcë¥_ŸÜrkãsgk½Â›ÜëG»ŽzÿרßNpÙ=éÎÀxÍì|ÎÌ{zƾc>Õ.~pÏôSo0®—~RÎ;ZoÈ$ãôg¾t>?wÿa[e¡O‰kþ•ãLþº˜à#ôyb4ŒYê²Ãz9ÚjÚ½^|x…ÖQó—Kþ8Fß=}äÜ­wÌvÉ•8µã»Õ×:ï—ù¢ŒW¬5¸æ¡õr\Èî›´ÞäÎ7¾ÜìÔs·é‚ëcK–»Æ¥f8Ó²·KŸ?ëóùŸã€ƒŽO}{b×mK¾ÐzÕsx÷]w mMªwé5ú‚Wšú®Óç·.»xñ©~rÈqñôÏ!­³¦h½õ"¢pÙgõ•¿IÙ9D·y¬ŸôÔ}þÞ»÷]žÏ_ÿà¿p­÷¶7{¿ºëG}wß:ëðêXÝváªÛV«Ï¿³2àé·¸Æñz9.žÙ¶ë™y™9ç©Þ–¶Kî¹Lsê¿ç¼Ås¦½÷—þ^–ŽWôN¿Ìgò®Ve‡ûLn¨×zWÛ'6}Ï ;?Ý}r¨n ¼iÒ«gÿÉaàå8XõöÖ…žMÕz7Û}Ï1ëÜ["Žb֫ߥÏçî€ÀK¿?{cñ®£u·h½{×Þôг÷9瓽¶~\7ù ÝfxÀ÷Å;Wé-bÖu<é÷gßù³Üå¿÷Onå5Yß;}ßš—~ïòÛ̘ÜâÑcœñgËãóßÈk|±Ÿ}äxxÎï‹ ß¬½DëýFLp×è{ójï{ôÕņ#Žñ{ƒ}‚wú«eýìùÇ2&8æ+ã±éÿçÞ™Y>|¥ÖwöÜxï}ï‚kâ"G}êò—Ÿý¥PãLÉãº:¤ßÛfz47k}OúlÄÛúÞ-Ãö¾ûØ N;¶Ü•øecÆmÀKÿ¶wŒû~Øï¾ÐúðòÜu^úÞã{:§-rÍs¾ÁkÊN4:ç©–›z–¬9²|éçÕ^澯õý¯êyw¾©¿ìýõó÷d^©Ûd|ﲇôóê{.3¼>îC­oâ³ÂGÏpÞ7/®:ðü-‹¹ß7,ž0:FŸÿÝŽ«.ͼ<éïÕ§® =üäåZ“ Ou®þò“Cîqæ>×}1²3Ð;êG׸êþ^cz-ùšsOi}A£¿¹5j›sœ¼|Øë£+qÙ÷ê :{¯ë>ÍŠuÍŸÒßkÄô¹}£_^zuÑBý•1Ñ•£†ÏsŽ7ÇýíôËFéßµîKS?&XëaÒ¬·ôW²,—=åãšçÂ.\Ò|"Ï5N.úñü³­®ùg£ô¯}´ÇŒÒú’oûøµ§ÿ ¿rwûµó…è¶KCföÐ[j2}?µ7l”þ]‹Ö[O|¥õeýíã_­Ñ_Ù8kä;’~Õ[—–Ŷ¼ô§MŒ’’óP_Þ×Kùübý•w6ú<ϺÌþx(Ò[äüžô«íc¿·†<꼯úJ“Ü-8OïþÍäs‹ïzÅå§é©yQ¯:ç–Kì¼kþÚ(ý½®ú˼¼ÝOj}µ¾Ôý½Þ=xÚ_JÆûºü%Ÿ+Ú8ù¼Ô[®^þGk9øÒïëöþÔÉ¿V¸ô°zj÷~¢w}b¼Ï}Ÿ¹ä1.¸{gÆ)güвb×™/¶?éïõò>ÒúfM¹j¶Ï¹z÷ôokî{q‘n³‡#çi#Ï»èVŸ¿è .X~OõÈgŒÇ6I¯Ïõž~Ö„­ïvûJXI8XÓæº¯®¿ä}·Áß9ù.˜þÀ÷·f¼o’~^¿cȨ_~­õÙÅ;Kï.Q–d;ý9 /˜8ì±m ;Á“þÞ0êffì´¾î}2³{³Þ=+èóY=Û\v#LýÓDçý¼§MYd_úCÅÁ—?½ÀWë{H,Äòôn”õ/ží_ ®x4ðûAƒ—~ßгê™Ç’†ßæ%,•õî§¼øKÞpÄ•û/ýÚQ!»øâuŸoûZïÞ^¸ðæ{Çè6ë‘ožÒ®Ô[¾®\yç 3ðÒÏ©3ßÕö†Ö÷°}BÐ»ßø®áÆ3Gê¶9YOÔÜ9Ñy¿¶ÈçxÒoç_³l÷Í~Zߟ¯úä¢û¯Õ»†· ‰\¢Û ~ùÞ×}&ãV×}&ŽÐ6Êø[뫨ߘôîn_öã~oÝÖdŸõ¹¾^úkSÔøñ?ÚÐkåäQ÷ž4êûÎÚ‘i pêµ aï[•gu/ý´éáW^ÓÏ}Àu¿¬´ ¦ï»ì™÷ªoìÔmu wUª/xiÌ oÿõvð¤6¼pâªòuZQȇ¶+ô}×ìútÅÌÝ&V%o:Ÿ 6”Íì¸áKð¤Ÿ6‹èm]>NëkÈ×÷ùY~דô n›ýû‹/ùö;ÇøsÍ[›¥¿6×–¿3¶l¹Ö'î®Qf}_rÂÎgÚ/Óm^0ëѳ=œã¿Å¾¬»±Ÿ¥ÿ6ûYß2­Á<ñùB}ŸŒÛu›Xå:©·då7Þÿ\?¾Ò[Ôº­oe¦˜ õ}sJ§­4tè6ûð»Áõ<ŠzÊÍpt¤ñØé·-›Ä„±Oë“q¹¾ï‘ñ_ÇÅ&é¶–E^îÏp=w·H¿mÚ{~ÏÄ7µ¾Uv.<¢ï³ÝTûé‡c]óò-ÞWüÖz¦k¼8æ½-Ò[íËãLôeð,¹Cß·óÆŒ)í[\qÄEqío;æsð¤·öíÐ_x6ãZp¯èûz“ÆìñÏwݧËìŠé-ömƒß¹æÇ-ÒŸÛ|‡owû¨]ë»'Sê9éw[¥?;E´žx¾ÖºmÝš×^Ó÷_³lÄœ7u›ó ¯;Æ9ðÒ½Óv.=2Fë›ö»>B+g|±_ΓŽ}@½åû•ü]÷ÇVéÇ탮¼~åsßj},æ™ÉõýW?2ô‡‡_Òm[ÄÆØhÇ|ÛONé¿íSÿìv²ív­Ïþ˜˜äôßþ©!›ž²-ÕmÏÇÖ¸gxê F˜XR7o•þÚ^{tmqëwZß9¿m9ùC¡¾ßï7§îîüNo9uìœðÆË€“~Ú¾A¿ünS“+Nþáý!ç?kÐ÷G *n{y¸XqºîÃã“n½çn×>å6é¿í_¯z`Eë…Zïç~"RÔ÷§]Y|Æ\÷Ç'÷škÚG:î/𤟿öD+®t­‡>™b^ö„¾¿`ù²³3 œ~\çiß°Ô[ìÓ…»Ë¾Û¤?Ÿ/L«ûèÿ°÷%àQYÛ©ì{Bö}ß×ξÊ+AÑAŒèHTÔŒk\Q‚£Ôˆ€ˆ(¨À(ÄÒ‘ªC¶Nd éΊŠ"Š(*¢ÂÿÖ­º·zgþçÿæùþç›ùàyšôéª:uªÎ©³Ô­[çf9Ž}q}ŸW_BûØÔÞÖ8I§c3ÛÅ:Ž.P¥b¡\/Û9Ÿw`7®–ôô¿Í|€ö?‘pû‚—'éQfaDÓF¦ÞÃØÎù¼ãàÊ#5y]ʾÞmw&Ï’ròÌrh¦Q½=ÎÙÎùܸ«Qöm_µ%úªhÚßä3í»aº9gÌñö átÁ]¥9OG}Îßv×+ûš§mØZñ<í_Öý²ýáétÁWß_ÛöÒ&ÔãümW·Ÿî–ó²2}ÝÞ ík´wëµ×Ëy)œ•øôI©ç…¼ëz€¿¦¢´#ŠÊÙ½QÎÏ ßœüº®›ö«;t3s7g¬”z”ϳ”kÊùÞ~‚9Fʾg*×mýu‚¾_ÛßQÕà¸7fì§íü³B7ßúbÀ #ð÷ßyÐó/KОó»#ö^ÕóØÇõ2íüumáÑk$ên™føâmÌm¼ïÙ¯á'SÎßhuãú$eßC¯¦øøí ý_.»íXAœ®7/r™ëE¹´ñ"u#í8_;”]µïueßOL ÿöJÚÿ˶뫟~“n^|vôN¿§µøõ9?;¸_¥ì«R7L©Å{üýûG¿¦›F?8yã&)7”ó³ãƒø7–?ºLòiÜÔœ5Û¶PKäÜïœïúIŽ‹…©#ÛéÇKž>°;G®ÊùÝñ™ú@EÙW`·pçÛ©%õ‰3n…5™ÞuY§ùáGMœŸ3Kò“í~N}Zò‡ÈÈIºyý)pø˜§èò`â|ìäë@ÙçéÞwѲź¼[JžÙ%Äf^Õíê«”kñ€®GLœ¯S]Þ¿níeÊè鯗gÞF-cwvüaÒ/tsß™ªS~XyEÂÅB}ÎÏNÕŒVF?gü µ\<ë ?ïhºy÷M—\~rƒŒKMœ|þ”Ñ~÷3ÙNoSË¥}%5CSu}sÛKfmx[É#Óá1…é~‹¾_gâüíäû¥Ú|(£Ü~êúÙ2îήý3.¡›ÿ’é=Å1Tþ ­=Þ4qþwÕ0rÿ•Ç'ÔÂܹ¤%z< ø!㘿›G.ü9Œ2úBnQCsµ\{ó—ŸZI735s×ër}F©,R¶ ¹87Èž<Èñ‰çM–VÆ_1%‰nÞåÑrì.Ä••w÷ôÞhÇå¡ Áò {g*£w§;ž»”Z¦]¼áÇÓÇ)wœ¥Ýoãüîšr® "¨ŒÞ¨:âÔRwÎu~Ý<šœrÛÉW¤Þkãüîâñ|~ÀžÒß,å®öhÂ[kg =›òMÿ4ý<\º\#‚’ó]–øÀ/cn¡–Ç.™s›QÊÃé;:¼jCç ÝæJ:ÔmÇÛ¨¥îòüm3¾¡-9“FîÔý)ÝmãüîbeÞÜ­Œ·m{sÌkÔò̉ד3Ó–[÷œm~äm¹NÛ8?»¬j@"é…џܰœZ¸>kòÑòÀKýã?“ûÜ>Èý¬œ¿]_Ý诌üôиˋ¿¥–n8U4zm¹=ºÁÍ~¿_½+×ýÎçnÕ­¸XùÂtè>Ô²èœ]"}—¶ðýißvp>w«aÚ#ò91SWžÔõ¾åÕ׿ '?¢Ï{ {zPuŒûÏ·#;¸‘zt—îÛÝ_üÝc?+#ˆ^O,?I-Í·|<寴å†ðbÝó‹v\.ºì©=õÁAedMà‰[<&R ·çºlyòlîª;Ëå¾.³Bߎö\.ºëê'!VFž{¬ï­±RŸìZñ§Ï7Ò–—V¼;ÐÔFNx|âãF¾r9é~nõŽ1óŽ(#3yÑ%uhËö¢ý/­Ø õW;ç»™ïk*#ì©[Ø*ju9Ûo¿äý±ß >ˆ~ÿ>®hçü7—­¾ì¢+þ¢ Ÿ*÷Œ6ÿ óÏê¶~õ»!ýRøþ´¦mèær`¾âá¹CûN*ÃGÞŽ<¼ÌJ­î>W?þqm0ÂäÉý¿vÎwóµ/¿Q¹¨CæÏåQý“ÇîYG[~\LÃt‹+ŽvpþšU÷¨Jî¼ôž^żxdüéëùéº?ØòU̲õ¥ýR.:8ŸÍbÜÃ￸pzxžþÆêùúîï+~–ãû˜<Ír¿®ƒóÕ̼’¢Êðj§ß%ï VUü¦-ê6×cÒ/êü~‘ÐÊ0o/çÕ?ìùSsR¤|sºµç@r^;‡Ž»è™×t9ž»ãÐØs¡Ô:¦òו~#ñ}½è‡´±³júåñ?¢½à/ZÓ×õÍ0ßç¢Ö˜5e›<¨ÑÛ2|£¹‡.àr„vœŸ=Žs°«ùHû¹ÖäŵJf¹®—Œ®Ç>¿ªÒ*íLçkOÈìYΟ,—ô^<¿èª®µúz°Š¸_§Û®fT¶¶/ýŒNÎ÷ž fÈoV†Õm{WjÍ[óPéÆsÔèûaê-+µçÒŸèä|ïÿæsZ–Éqû9m%«ï§Öâ¬ËZ¾,”ãP®û%iÊ;r¿ŽïoçÏ=w¬[ë(÷ÆNÏX±c÷ÔZ;vlªA磱ö±ÏW¦¯—q§Ø×Ñý°N.=‹nº·¨šêt }ªnÌQëE©SJWËyy6Œ=!PR¾¸aáóž+t¿NΗ®¯•¡½;}½ïþ•ZÇÎüä¥Ì¨‘=þàCÅpÛÕýß­ûEîur¹èa»|^bæ|ùAj­ð¨ï+õ®ñÞÆKÏuV²µç:|?Ëfž¹œôÚ¥~wϘne¨ÙãÃÑvÔz™á½™ï×êñ±f9{"¤ŸÑ奓ËK/Ž¥ ­œÃžXRë•ÇiØÍ«õumœ1íäjôËùìâòÑ;md½§ ‰ç’ÖÉOOvœNÌ+°ÃºêeÎ{QŸËEoméϳ_´“ü|viA`ãSÔZÞÓ—©ë}#óªÇ +ùšÞ~E´¦?»¸|ô6<¸ÿï^ª =¾s"Bj½ï…k^Úê.ùøšêP+éü¹­¾ÎõyìârÑ»rû ó¦eèÞ͈„vQë³fƬö–rº!àñOÆmП7ËyàrÐ˼Ñà¥ÊÐ#"x’Zç¸þíþKtÿĸó“Χ®oÖýúÆïÿõO{2mèàrÑ;°gý®OëúkèRõ@µ.œöå#«+$=l×À.M—ÏFD/ï.y[Êg—Μ’òžñÓï'¾ÐL­ËZú<.ß@Ü“Ï{Ùêø½Í~m—“£®Ã_/Q†‚~_²äÌŸ¨õõÊ-:qLmd‘Þû¦âh·‹O_.¹Ï-Dçó ú0šZÅ~‰Îî/Éç—çïûv ¹áçKôq ~^ Í6Aêõu[½êø+r~Z›Þüàv©Wž ê Œ{ø„ÜpÿXìU7Ì©õ­ <¿Œ”tõ,û2áû¹OÙ]mËepCêÃKú^¤Ö SoÛ3Bì4ÂC‘r}u ¹àçyä<¼0òká©Ï¨õýœ°ÉJ5v2Eÿ¹Î¹!Ït ¹Øû‰kûrßkðÁêÅ÷z=@­-'{¿úIŽÛø¥\téçp¾H~v ¹Ø³Øzäg¢ òçÂÔº¥e¼¥~5¾æ¾o»×ÐFöØ?aê þ÷¸FdåWóÕƒdºn¥«M{í¤œ·VŸ÷ môg‚ò³ôÌB.ÚUGSô·„¿zçOÒ.ue]v‡³ŸÍo”ò`ò@óæ¹Ý  o‡GßE­»Îþ|‚tRã®ùì <×aü†ÑßtŸ2À¼ù‚µÔ:Ï×ò‹e콘6~;~Ï”¯Ì¨/ø¼ëÊ×½ícu¾ °Ó† Ôº¿¬!´gPê©öM?=”+ã±ï¡?ß2krÀÌ|®2ðfÙóWÍÛN­‡w¥úGµ¡½—¡¯ÔÏü97Úý>Ðãêþœ\Êý·†5‡3ÊycîÐúÓRîσ̦¿¥çö€zžôËNm÷‹:üGjdnÏòçdÜ¯í§›5yØøÉè^eàúo£?>:¢ÁtÀîª9S—G[®øMås]¾ž¤<ö¹`^ݽôs¯EêÔ`:àú}°[¾oÞp[¿†.àÏ™Ð^ÈCGäÞ7¢V+̪Æ.¡~Ÿ¹^· ëRœ“ÒÇÑfwxãÎOÑNÈ…ˆ ì«3šæ¶Óèg†.;i¡Æ3mý¯u'ÑlÂi+êWÛÊ‘¾­üù¶>ü¹›¾ZÝ2öÜsh¶ÔoÂ_ÒùÑ#äƒï‡jvE±>Ê<}Þ.ž8ûË€tÝž´r„2.åû26ó*äg…J°bݺ¨vú×_iøéÀ”ÔáŠSt9nU+O•qü‰y¿|¿Ú†N!7걇«ž+tàžå† »ÓÖ¬yýÝ­/Êý:.Ç6ô¹¹Aeˆb}ô›‰IÖ::0çá'ߊòÔý•V®_¤Çå·âh¯“<5ÐT¬×ΘßJš~·rËåº>jðî [ç̤£êP´ò¬>ÀV¬êñ´: w {Lø9­°‚Þë¾~BäBãY×Rõ yáÏGk;Ø2¬ëYøÌº8I?sC×õëû k~Y“v,í…ÿù«z I±ü¢$¢›<ò¿¨™*ù ÎU4¨Ç=sÐNø™\Ï)–ÏÿX:Ö”M¶o:•·N÷ÓZË¿‡FÿRßçÓ÷£{…¿É÷Ç ;åZFÌ¥K¬íeÊ!?­1Lc”È} ~~ í9ß{× cëÄ-tÀân} êIÅSè™VvÚ.íiÚ8à÷õ=»ŸA;ÎïžUÉ×M®^¯\ÊéR,Â+Õäz`ォÊu}ÕÊN»¥½"ù÷!ç{ÏSë^ò8x@¹úIZ9çÃ…Š…*iS®8îü\ë–n:ÀŽÎZ¦ûWÆÓì óÒšÔ•$ý¶¹\ôÔ°ƒË•ëÔð$[±üá²»Š#gkýÐq~WÃcdÃ"^–ëDœÔåäC.'=×2‚?Q®Ÿ°tšó‹Ë5^O_MÊ´~è Ã2fÁôyþœv®x„¼”.d‘¯RõÒ#[ïž}F±ä¾~ëg§4¼tP}ì_ Tˆ8C£O?× %šÜ—*÷K>ò$ÎgÝÄçY±D¨·ÖŒ½ä/BÖj|£FöE‰#m`ۤ￞‚»iáÃl§Jî»îärg^?}Çé¢ÍÊmB>ú·«1~:x³êðIºt{·œï\ÎÌóÙB¸U¹]¬ƒþ·&°“nÊmC‡ü2©’Š÷&òó‹Ô8pçž‹¿÷§ ß° Vàáre¾W=ªLgÑHÀJ?ί᥃ü\‰.gš? ÉkÃÙÈ·›ß_(åu'—3óÄ5kÛ&÷*wyíg§:Ü4˜2sðÆre2ï—aìÉó”ë+drë{€Ë—Ùûî_vGTév²ÿv B>¬-ý–õŸ¶¥ ÓFn÷‡ËW÷¨ª@•þë^º²÷Ü}tpÉ”oÎD)ZÜBÕãñðvr9ê~]}±C÷Ûúùy0ÝÏ\k7}Ý®gó~¥Þq”n÷vr¹êæq´OúÝ_€Er<Û²W =1‹¿šötïÀ,9ß\>e\¶‹ËUwüÎç>}'Wé‡óˆZÒgýõ‡–Ëßñû÷Ÿ=W®ç›TÇVÆ»¸|u±·†ì=•½¿² þ©tð›ñ'ƒ^þžÙªg  žßչϺ õ¹u-f"ý•½êrÉ×Ç1ä<Ø2õU9?ì8ï¡mòü83#KWÈùÙÅå§kò¦çJ?«ìåñ>Š›Ù·³`·ÜGèf#‘óÂÇö\^º\¦=Ðýƒ²W5dt¨¸¡Ñõã(=¾lÐüˆ]\.:ùs2Ï{×üa(?ë#}‡®zociÀ9Žö"Q¾OÄ÷ÛËM';Å|Ù[ºŸ½—™“W6ÊyùýŠ ó¸ 91/š½ÔßOÚÅ奓û¡ÊÞGs^@(C‡îŸüàƒ7O§ÆýlCþEÝo«ÿz1=~ùÌŠ£»¹\t0i ; ìåôС'=s¨ú¹±G/½îŽä7œ½ÃNÜÉ~Õ×ÛJ¤|íæòÐÁ ){¯­b'yéPãþεʸe}"Ûú•|eQ/+íãn.'â\Ü^X¥‹6>O‡–Ü’~ðFjdá£Ý6ÚP7þÎ'¦E >—ƒþþƒ²—m; ~H‡Ö?;é‹Çb伩ÛL‘Ú{ hÇùß‘¡ˆ–üŒ¬éZøãÉÏ-¯_yñ8W¤Í_CCg›Ò×'ãåÝ\>ÚÏ92Jôõº×ãàþþúÝth瀻Ãä{@Fö4û¡Q¹¾®xíáKŽýUÊ÷n.íêÛ3×(}ü9³” ~þUê1î?hïéØð…ËEûºÙìä¨ÒÇ÷eèÛ&ºx.5ÂùF×t>ߟ”ýïárÑ.ö#û†w¬™¼fO§}CüùŸ>ç`÷ï£>ç»úÚO’Ò§¾ž4•Çú†ÛΣFöJÉàß¿¿¸‡ó»]}œG”¾må…còè0þwüê* Ǽ„Ößß6{Ëwö¨Ïù½cdû„¡ñ):ßú`Ezž9®ómX=Æ ß/0>ÃRøërW£úb7/—‡óÕ1:ÿúE°:|Óü—†,×äH¾GYúÖ·¼.—vm—ƒ·¿vË•ÅJ‹ß«t”eZôù®gÓ9û-Ôç|ÞA_96«Véç,‡gÝž|ìi‹ä¯zlÊŽÖÊÞ¨B;Î×6~SéïÙ /½ÿçn•ãfOëÓ­rÜ©f$Ÿû8ŸÛrOŒ÷{EécÇ`O¼F‡Ù©ñ$ÙÿóŒyè[©?8ý’}œÿmì)ÖÔ\¥ï¶øOž]Z@‡ß»²iœ;äLœ/©G‘u|ês¾›N±›JÉǪS~a›Œt¸ë¾¢ÒsÍR^ûⳑkÿŽr\.LÜo’ü»ò<òctøÀƒ_=ü³óîJÓt>ÎöXû¸<˜îd/zýUéãï_Ð៽®=VE—^¼Â@&Ñy|~QŸóÝÄvõ•·•>~^ŒŽzp²]®ÜåïgÐg¿2WO™{ í8ÿMêñ€´Kúr¦õ,ÉÙüô{v¥ÆŠ7æÍþìSú¬úšg±”³>Î*ößG®|bâäSë¨Q}œOçE&elaw~œ°ãÿþSïnüwÍ_r!7û…ÜìÿÕ;Ù¿1ïÀMbÄ1€Yî7õ~bÈ´=äÂÞ,òAT‰»ñŠ\5â^<àvŠw½ÔŸ—¢‰çƒPs3ŸËR‘ëÕµFÜe<,î2®³ÉÉl÷ ¾;Ææa9™Ág#➘I"/Ú{ùŠ{ñêENˆãâÞàô6säSÁïñi9!Bø]2¾¨ã }Ñf Æ1¦JÜ…Œv~®"7óL›Ų̈ã_Íï™ñ<À€:­âžä‘ŸôwðOè ‚,ƒÞ`´ ž)òC ïè À!Í"?3ð†VŠüÌ&®õų̈vZÜ“‡>Â1¾ü^e57èˆ@y$pFbž#[E^ˆq?Þ0¿/z’ÈÇ<,rÈU‰\Ì}"s•ÈÅŒö± 1ÎÀóÊÅ5‹<Ì)â^<ÀñGÄxu<÷r¢«ÈûÐd“spÒR‘kó˜\+î½;Âï?Qs+c¬)ÀZ,r)·rµžVÉóÎ¥­y“ÑW:ÊÓÍ"G2hÍ@_hŸÂóÎeš¸ ȼd¡m–Iä:îlÀÙ­"ï`Ú@GðæoNŸÈ[\ÍïþQóNOÞR‘¯Ø òM´Š|Åè¿ žçÃ`9' Ao!ê¢nÆZTÃï¶.:ÈsM°ü{Å­"_1ú/©áwV³;úؽ{¥ ¿p)ð•Åð;ˆÔ{ø€¯õËkD¾b¦;.Øè 6ºÎî‚þO²Ñl½ÖŠyE[õîâqïmŸ¸¿2k¾Û÷‰ûk«ñŸŽˆœhïØ*î®E{§*qmŸÈ/Øy©È3€n\€Ïe•¸»ëѵVÜ]{PÜ][o“¯ °;èqG}wÐîQ,r6€=cÄ]nM<úóªwךÄÝnX“ÞÀé œ>ÀéØ8|L"?pø‚&_Ôñ¾h3ãS-î°E;?_‘»©NänB;Ôñmþ ÝpðOê˜øýpj.ƒq-pOð¾ ð:ô£MpÈo€¾C*ðÒ*î²ÞÐI"‡p„¹ŠN 7¬•«ÉðI"æ "Äæ[WŸ‘À‰yŽ4qUº¢êEÞ&Ô®â9^£ŠœMÕ"_Ó0Ï×Äî®e9_cÑ>4Æ‹|M­"WðÅŽšŠùµ hŸè+îª]Êó2±\°,/SÒ*‘ ó˜ Z“;mS@k úJÁXS€;p*àT”§¡mÆš†ùIkæ*žå‡MGy:ø”¹Ê­è+í3gw&¾g¡mæ% m³gÎîlÀÙÀ¾ € hk9À›¼9 9xrkø]¿ê]¹À“úóŽó»uÕ|PÀQ€± ÿ‚&ž“J½ô¢n!êa¬EµüŽ`–J½p±IäEÿ%µü®_–'ªóZ úK—_Y Ï[ÁòC•_9ê—.Gy9Ó ÌΰÌ>3ÛÌì°­íe6V³›ÌFj6Ù;fØ-bvÇÖæhv†ÙfO4ûÁlÇù9Ì5;Ál‚­¾·ÍYÎô<Óéš×t¸¦»™žf:ZÓÏš^þ¯ê[M×jz•ÍÓ¡š¾Ôt£/§ÝÏnÙuX%rÒUð»¸Õ|(øîŽ:^f>­Þ̇Gý€z‘{¼‚ü¡àfáÎ@ÖCQþ†ã{8ðD@æ"€#¿EιԚø½Ñê½Ð&q'4¾ÇC.ÐoB¿ã9ý%£}2~O©äîN*ó[1G騗Z3Rx~äÌîòPnÀx h“ƒ:9 14æ¢<·–ß/©æ;ýy_Þi~çs¾™çu.\Ëïpfù›Ù=Î¥Åü®f–«Yõ.ø…ü ~á¿¿_"øÝÌWï®å¹è°áy¥ìÁ3{À1ø@¾ÖŠ/À^èÏýyc½y/y<Ó°pø˜ENàðM¾M"—§ÈåY#îù<Ñίžç³Róyb¬þ Í¿Ù&Ÿg•Èç ܧmr¬9='u‚€7ôƒÞ`ÀÁÀÌ|@Ђ9‚9 9-òT‹üÀæ+ò Ÿ0Ô ^Íó„c#€3´FGƉòHàŒ¬;/¯æ$ 4D£~tµÈã 8pL Ï“sPä3‹ú±f®vãÐ>n&Ïß|ñg<àx“ÈePÉïMM@ûDŒ9øWñüI!p`lè¿`)ÏIªæJÀX Q·¼,2ðüf,Ÿ©š7p1è+Þà-AŸ%3ùÎ,gh)p—¢¼p鞃ås`9B™Y*GýrÀålý2ÛÄþ1›Ìì0³¹~bÛæmg¶“ÙMf#™-dvÙ#f{˜Ý½ª=a6ƒÙ f4ÝÏt>ÓóL¿3Îô9ÓáLÿjºö|Ýj«SÿU¿ŽéJ¦'mý;¦™Þƒœ²ûÔ ~sÀšs4‹;Â1¯.“ Ê\ëø]áî˜3OσâßÊÝ@üœÂsꆅðürXcQ˜ã(¶6jxnÛXÔ‰ƒ¬ÄW|¿84¦¡~:Ú¦£n:x•¹Î-™ø› 3€êÀ«´ÏAÝ&kƒ\ÔÉ­ãù<Ô<´ 7tå»ò<ù匯¸ AG!ʊѦ¸çÙ¸°_gwÁ/«³»à—ý§øe,ÎJ|ˆ«¹¦Çcœñ€ãAOÆü ¨Ÿp„çSO¬yÔ'Nœz“Žó|R,oz2p¥¸òÜ›)h›ÚS;•Ù>À©€ÓÐ6 åi¨Ÿ†¾ÒQžŽòtàN® àÊ­€3ÐW&àLÔÍDÛ,ÌCêfΜ 8tfÎFßÀà6`Ü9À›ƒº9ÌŽbNs'·žç¸RsZþ<ð+ß—ç¶ÊGY>xS€¹+¨åyÇX.w–»½p¡‰›—¢bžgŒåsUs¸o1Úo ú/=%u<‡¼šË¸KQ^ ¸ô¸È‚ò2À嘫rÔ/\Î`¶ö™Nfÿ4[lk‡5Ì쯭Íý¿Ù[[;kkW™MÕl)³£¶¶ÓÖnþ–ýc6ï|ÛÆì™­-c6ŒÙ.f³˜½b¶ÉvOp®/³?šÝÑìfk4;ckclíËoÙ[›Âl‰fG´}D[;ñße#˜}ÐüPf4› Ùf 4;Àô¾­¾×t=Óó?¹±‡\Ú¯9ñ›C«ÈyƒysÂü:A^PÏß!óÎ!Bà½K-Ï èŠº®E7üu«â¹Ý1NÀ“DþfÔõD[/à÷†,{Wã¼>Àãƒï>KyÎ5__>uø˜yž>573ÖTÚAîƒÍ69ø0¡µ"羇ã{è‹d~3h‰B›hô¿1‘²‹5:âP/ßãA<¾'øŠxkEÞ»Zž÷. ß“ð= ãM É•<Ë¡’‚º© 'ý¦¡^èHœ¾2Q/tfaœYø-‹}G½lÀÙè'p6ú76—ó–ºrP7§•/ŸÜJžˆåÏËþ<Б‡9ÈG½|ü^`à¹Ô\yX…LTð\@lÙ£NñA¾ôJ¿ô´È„ïåC9[ÌÀ³Å6ßQGõ ììþûýúÿM>ýùþü?óå/øñüøÕ¯<3‰œ±•"ÿóë¡ ì "׿Õm°&êDÎX¬]GÈ©#xêº;UàrÂ<;¡½3ê;—3èrÁztA™ Úº¢®+äßm]»v«æyÒÜŽˆ\i(wmî˜S_‘+v©È Z=Ñ—'ðy¿'úòB_^€½€Ã óä Þ56ùbÑ¿`Èèõ_´ñÅo¾«ÎË êå£,eù(+ÀÜ ¬ Uä¢\ˆ¹,DÝ"à)ªäyöŠ.\Œ¶Åh[\%àS ä¤ÄÄóÑ–w)ê—š¹Éa¹ûÊP^¸c/Gýr”—£~9[ËL·³šýýg¾;³¥Ìg·µ£ÿÌGÿ-ÿ\³ƒZ.NÍö1{Çl­Ól—f§4ŸÜÖþh6æ|û¢ùçÌ®ü#û ÙðóŸ>Ã?_çÛæÕ´ÕõšžÇܪ:^ÓíL¯kúûü½^[ÛV_C¾þæÙþù>6³ýfž3Ó¾Xäå†ü:`Nð»#x刱:vf:Ó rm£Ì-DäÕny´{T‹œ’ Ý m¼!ÛÞ˜kïV‘OÒÀódû¢?ßZž#{LÈ' \~˜C¿a™C2x˜ç³+æ¹"ÃÑ>õÃM"5~‹B»(wÅ¢Ñg4¾Çào ä5mPžÐ*òDO"Ú'áoþ&CN“!ËÉÀ•‚º)c*þ¦‚¶4àNÇ÷t|Ooåâ˜QÇ]·L´É환Ó,Д…ïY 9}eã»x ˜“æ£MÆ‘>òQ§´˜D¾hÐXˆ²"ÌGÑ$ž{»‹Q·¿•¢Ÿ2Ô+ÃoeÀW#xÇþýÿØÇþŸèïþ¿øºÿÊÞõ¿ó¾õÿf_÷²Ÿ[%øÑ'rOây|‰‰/eû |jyN_¶¬ Ï(w@}Ç|ªñ?A—Ú;¡Ü©ŸV¾ô;vF¿.Å<÷¯ Úº¢®+úv¯]™î.7ôã†vn¨ëŽ5ëŽr÷&‘åiôå¾<¡/<Û¸=Í\•xöìþza¼±®½!ÇÞ‹7t­ú÷A>(÷_ŒÍå¾€}QLˆÈ' Ç€Ž1hã~hã‡úþ(÷G¹?hòM !8P™ @@Ô D@Ô Ž@àBA&ž{8õƒQ?ãFýŒ3pÚ‡ÎPÔ Å8BÑ>s8 8ÂP' t†cá 1ãŽÎÀ˜›”G ~$pF‚/‘€#Q? s…>¢P?õ£1OÑÀmæª3p è1s5 8å±Í<qø‡y®x4ˆ8s€òàN0q•›ZA["ƒ1ö$ÀI€“€;cK]É +cMAÛô“‚q¦w*àTfïÐo*Æ‘†q¥1»‡¾ÒPžŽòt”§wÆ‘yÎî ´Ïœ Ü™h›‰¶Yh›…¶Y€³@G6ø˜ 8p6pзcÈÞà͹ 1ãËEŸyÀ‘üy 5uòñ{>hϾÌQÚ M!Úbœ…˜ÃBÔ-‚ ¡np1Ê‹£m1h+®ÐZÒÄs<—¢~)àRÈD)ÖFê—1;‹ò2´/Gýr”—£¼œù»è_Õ×ìŸvŽ”ÙÚäß2›j»7ý[öó·l§­½´µÌ2[gkÛÎß‹Öì—­Ý:ÿœêoù½¶yåmý_ÍÎh6F³/lE;ÏÀìˆížô?ÚÖüaÍNœ¿ÍlÂùv€é[¿WÛÃ`zý|ß÷|_WÓÕLOÿ#¿—écð˜é5g;xoo‚ž…\;`^ð»#äÀ4;¢½ÊœQæÌôi>¨ï‚:®à¥+ð¸BŽÜPß r톺nwÐãß<0çž(÷B{/àö?}™¾3qQñìü!oþh€5¼U\ŒXõ Èa0èÁ:  ¡Àyƒ|‡ƒžpÐŽ>"Ð6u"Ù_”Ež(ü6ÑøƒþcÐO,h‰Å÷XàŒ[ËsmÇ£ïÔO@_‰Àˆ¿Iø›ÜIh›Œu“ ½— Ü)è#•}@[*hI iè/pÖS&û n&Ó(ÏÂ_h0 ÔÉE¹¨ŸÜùÍÜý+˜Äs½a>‹ØÚÅßb”£} ú*aëóR ¼e!<‡|𕣬œÙyÌçÔž;¯rŸñ9e§žÛ±û Ÿ3øüŒÏ/øüŠÏY|Ήçßø@G8zŠ@O8ÄEì«!& Ð[z‹@orÝE|x H ¿„’ & ˆ b!$ÁÂ_>#aø . Ðkz@¯‘hñ>+ n#Ðmº@·è6’,Þ¥A\@ çôž#ˆ â’Íϙĺ@÷è>Á ˆ H‘8w‰¸€ . ˆ ₸€ . ŠxöØ€ 6 З±Al@qbOú“@Äd>ˆârðÅ O â‚ø€ > Эñ¹Aè Äñž%7ã]K k bõ]^Ä1î%н1AŒ@#¨ï!F ÐÇ1N&ÐÉ1AŒ ž)…n&Ðͺ™ÀæÄ1žVÏ5@WÄúš@_Ä:›@g«û¥ˆt7î&ˆô7þ&ˆT?:œ F ˆt9Aœ@'èt¦ â‚8@¿èw‚8@Çèxõ=eèy‚8@×Ä úž@ßè{õ)è{}O'è|‚8 N ˆÔó²Ðÿq °qAœ@` Ô3°qM ˆâ‚8À>¨{Á°ö N ˆìAœ@'0š`ý¬‚õO°þ Ö?Áú'Xÿìk‚õO°þ Ö?Áú'XÿëŸ`ý³÷»Ö?Áú'XÿëŸ`ý¬‚õÏÎú¬‚õO°þ Ö?Áú'Xÿ럩#XÿëŸ`ý¬‚õO°þ [ÿì‹—Ú]xÿ»î¼ÿmq@ŠØZ*öº]Å^w檟%ž$Ϋ˜Åy•bñxˆŽð}$-&PŸÍ¦ˆwÌšÄsÚÿÃÞ™ÀÅUýŸ×ªE­¯h¢AQj]0Řaöaöaöa ­ÚÒ¬Ô¥R—J]1ÑŠ;nµÍÅ-Æ-B4‘lŠÖ%«Øêkl­ýÏ=ÏeFšøÿôíÿßÖ>>0ç<û=wîsÎ=ç÷É~ân9>áwƬGŸ13Ÿ·¡²þ=(ÏÝ`9 î•óà£rÖÌé;ÏbžÓgZÔY3ó™&óƒ9*ûWúåLx¬…»õ³Ú< ëá^ÙÇ2î·Å«Ÿáæ|!XÖÄ{ô>cóìY˜¬‹÷ù?³Ë{ã~}NÜœ;„Ê9´y‡¼Kžùv93Þ/{\ôóßÜçÒ+ó‰q9w&û]ºdÏË™[ËüÂ-û’eŽ"û_zåLy€ßþä>9³(ûC½²¦>"ënÁr~Í#ï¬Gä½uœcs˾åA½?Æ<Ï"gºäìù ^§3Ï·…ÈY ·œEÐssosì?uÉÔ}FÈ|ß$kóNYŸïÓ{kÌóê2—qÈ™¸^}vÝÜ« ûmz?´¹*ë€^ýnÜœÓ K,gÖ]²ÿ¹O¯×›ïÉ=²¯¦_¯Ñ›gá¾|ŒÕËg‘Ø $²#ñ!Rµ#Û†^~Û ·Ao×Ëêvèíȶ£ËŽöq½ü}vFaG}QôE©>dEcg4vF£'›¢Ñžhúbˆq ‚cЃ 1ôÅŒëe¼Xúbé‹E_,vÄ"3vB/ïÅÑGœâ苃/¾8dÆág<~Æcg<öÄ·xúãé'ñ»ô²`6%@“M4 ÈO€&šh\'>9èwà“ƒ8:ˆ­cBO¹ÑŸˆmèÏ€7ƒe ?Ÿ2ÑŸ‰ì,äfÑ—E_Ö„^^̆/¾lüÍ&ÙèÌÕS¶ôå /¾b˜ƒ¾ør±%y¹Ä/[ráÉ%FyèÉCVôy´åAë¢ÍE› :m.Úòñ5ŸXå£/Ÿæã_tÐ@W]ò i+¤­¶BÚ i+¢­ˆ¶"ü,¦"âS„ÅØZŒÅøWŒÅÈ,AO ´%ØSB¼Jð«ÚRhKñ«”ëPŠþj諹N5Ð×`W ô5Ð×Ð^ƒ:=Èñ Óƒ|<ü¨#vuÐ×áG1­Ã¾zbSmÓ¸^Vm&NÍØÕ _3¶6£ß Ÿý^ø¼Èò¢»Ý-ðµó¹Ðt@ÓÈî Mí5fö‡ÏìŸÙW23‡ž™CÿsçÐ3óç™ù³ßüÙ|>«ç¯7`ŸÅù5ø,d]9LÞ¥õë=æ~1§ß~±9óáÒ{˜Í3AræÃ#ûÆFd/†Ýo:@Î}¸ä½Ú¨ìsÊ™ÜaÁR ¬‡^÷`b©…É»µaÁR ¬–.Ák—³¹.¿³¹‚©Ö­Ï暘já²¶=(˜-a²—l@0[‚eˆG°ÕÆeÍÛ1mOY°ì)ëòÃW ‘wn½ú<ˆ‰7.ûHúýÎë:dÙ€à¸È3·¬™Èù½[íyVû±¶‹yN$\öf÷ þÚ„œå —½ÚݲÿlTÖØCdÝ#çG†eÍ=Tïå6Ï÷ŽÊ»ºp9GÒ/g|ƒä,I—ì[Ù ïíB伯Wö§m=jÁrî×#çK†ex€ìWsÊû¼>}ÞD½×3±4BåL°G°b5¦›y~3XΡ¸å,Ê >+lîm –÷.Ù/Ó¯÷›x2A‚ûæ Ž>-cžW ”½åN}nÅ|&ï»dÏÛ€>Klb¾…¦Œ[Ωôëw‚æþ6¯ìqÐ{m–‡Úë¾|ŒÀÇHôEbg$ü‘ȎćÈq:ÛÐkÃoô6èmØg‡Þ½ÙvtÙ±ÃN{íQÐGagvDÑE_Ô„N½£±3;£ÑMÑè‰×)y 1ŽÁÞtÅ`C }1ðÅ"3–¾XúbÑ‹±ÈŒ%¾qôÅÑGœâ苃/¾8dÆág<~Æcg<öd©_ôVC›¥æ»üÍ -ž˜$@“Àÿ Ä6þÕÆ5rpøã Ï?bè ®t'¢;ÝÈÌ@wveŸ tg¢· ù™ÈÍBf}YôeÁ— _6|Ùø“ÙÄ!ÙðåЗƒ¾ôåÀ—CürЗ_.¶ä"/}¹Ø’ O.ñÉCW²ò Ï£-Zm.Ú\йhsÑ–Oìó±)}ùÄ/ÿ  +€®ºè WH[!m…´ÒVH[mE´ág6êéM1¶ãC1þcg12KÐSm ö”¯’q=*…¶¿J¹F¥è¯†¾škT} vÕ@_} í5Èñ Óƒ:=È÷ÀãÁ:bW}~ÔÓ:ì«'6õÐ6ñ31l&NÍØÕ _ó¨~´záó¢ß ŸY^t· »¾v>·£³šh:°±Ù´%ÒoÎÔÏ ÞÒߎ·dÍSdN25ÿà:MÍ9Ô\CÍ/¬y…šK¨y„5wPók®à?7Ps5°æÓsû½åôŒ3—·òx+gWù¹ÊËUNnåÙ*Ƕòjî3‡þGæÅÓsbÿ|Ø?öσ­ØÊ­ÜWå½VΫò]+×ÝàËq_–ÜÖÊk·I>ûšä±oJþúNÀ߇£å–½M{'¯GÎÛö ž[°iGô^QßdBÎÐö žVÆ0QزæYÎn½ïÔÄÌèÒ˜u&ɀಠǰ>÷©ö€«}2 [Ã<—¨÷Î+l‘)LXÆtP{YÞ–Ú¿jžeÐ{tƃzg¯Îš)lVµ'@á¿™{tÐáÐØ sUa¿E ?¥ϔE¸ç-Lc€(<‡Œ }æ4YôeaSò³ÔÿŒïdG"+ÿl´Ûoç³¹v|·£+ûjé«EV²jéA~†ú~Tò ­UÏSì«æs5ýÕÈ®V|ØR ]-6Ö¢£–X6ÑßD[òÛøœ…mÈkCv6ñÈeœç¢·šXåáW5×"þ&ü«…ÇÅ_Ÿk¡«íÕËdµðåCßÄo>ÿW#·@Ñ·&ø«á«UÏ#äÒÞ¤láºò¹ Ú"ìkÇöZbZ¬ìE~;þV££ Þvå?öU#¯]ÙO{)rÛñ»–ØÖÇ&ô¶#§ºv·þn‡¦NÙ…®zh›ß޾vd6cO;ö{áó*þv`C<£rO¨Ÿ™uÖ™uÖ™uÖoþ:ëÌë7wufŸÒ¿Ö:+÷ýWrÎôúªznvÌ`º¾ëpTjT„ûÕ¨˜ó .¿ó ~çqäLn°œÉõJŠ ²Ç×)Ø£rn!\ÎæøÎÀMáêŒæa˜àêô æa œ“ë\9£ë’3sƒ‚µ&çtû_'Hð°{_'@°»å¬î¤œÕíÖSø‡õfâbO.¶Ów–ÁÜ["çº5†¯‰*õ+úäÜn àùzåìî.ÁÜqê³z ÛÂÄD ”s{©g±Ap.Bå _·œuÜ »œéíìì]‚Çc—³½=röaÜWëB‰5Ï@ô >O€ài{£gBïoþÊyß]RûÂ)8Šƒ‚¥ g]Rc@c*šçC»Ç+ø#r8PÎJ¸ŸxD׫0÷MH §>Whbkc¿Æ86Ïɹbú"<ú|qz#Ôßnå£0;Ì3ÄrÎÂ!55z³{TÎ.2¶2Â5&£yÆ"@ŸeŒ@_>F '}‘Ø $öGâ[$í6dÛൡÛÖ¯ªnñ‹N;ôvèíØcG—»í´GÑ}þFaG}QôEѬhìŒÆÎhôDcW4z¢ÑC_ ñÁÞtÅ`C }1ðÅ"3–¾XúbÑ‹±ÈŒ%¾qôÅÑGœâ苃/¾¸ ÎÇãgæbK.òr‰_.¶ä“KŒòГ‡¬<èóh˃ÞE›‹6t.Ú\´åÿ|ää£/Ÿæã_tÐ@W]ò i+¤­¶BÚ i+¢­¨KO7а©ˆø¿bl-Ƈbü+&.ÅÈ,AO ü%ØSB¼Jð«ÚRhKñ«”—¢¿zTOyj ¯Á®vúÛñ¯1Ò üMð{Ðí¾Ž˜ÕAW‡ýuIJ»ê‰I=´MüßLìšáoÆžfìiÆF/z½ðyÑë…Ï»AOŸZÐÙ_;ŸÛ±¹šh:°­Ù´yˆ™9oP?ÿ©¸¥².jÎÔ¼@Íüs•ï[y¾Êí­<^åð*wWy·Ê·÷–g3¶rg•7«œYåËV.lå½ûZë´ò؇•q÷¿ÞÀõüÚu_ù)×ú¯rSkß<÷Ýÿ ³¶OΈ:¥¶È„ƇV˜Ð ³ÄÄ{îÓØµ 7À<èÕç‹ÔÙB·nP°ëÆu½ Ã.@Ÿ§41B‚¥(µºd—ÔÇrjÌufÒÄÚ ÑØÎQwkÖà“{46ˆ‰ëÖø·fý«qÁÞC~Fˆ^îÉ‚&KýoíÈŠD—Mý"ˆ|;zªˆUÿWaO6WaC›[/óT£« ›U _-¾Ôâ{-ýMªÙM|¶“6âÔFLÚð£ ÿÚÕ†ÌläåÀ—»AÅäÁ“ôy£zÊE› ¹.Ú\´åãW>ró‘›Ï8®&fùøX]tÐlÐËV…´ÒVH[ὌUE´ásöaG1~ãC1¾Ws‹±±]%è*ÁžxªÕ3€±ÝÄÿ¥Ð—¢·?J• ümGf vÕÐßD[í½ æ¡Ýƒ#d{ho§±¹uÄ­êÐWm=tíÈlÂÎf®M3ö4ÃÓŒÍØèå:y¡÷"ëôáS»’‡ïÈê ¿þ>w"ÿ¿Þ“ïü™uÒ™uÒ™uÒoþ:éÌ~Ô™µÒ™µÒ¿=©êØ0ƒAîþ òq©h÷«¸kZ]߉i/ƒR×7Dc!¨ÚGöK]±@©Ó'µÅ‚ü°^+!Ôƒwƒ`‘‡k,Þ),ò Á{élÞ@Ás æËˆÔ‹ Ì—~Áî Lò~Á6 LòEfâ:waØ—Ü)Øäƒ‚M²—z¿¡Rï·GjÇøÕì˜ ©Ó%Øf“‚ì<˜aÁ*L¯ÔìÃ0Á†éÑxf-à`]?Ma )¼`U[Ƭ "õe¼RwpXp„KØ)5‚û1HêÍtkÜ4שI¼AêÙ„ nºW0ju­C“¦Kê ÖE Æ§‰@_üø‰¾H쌄?"ñ/’v²mØaC† zô6ì³Co‡ÞŽ-vtÙ±ÛN{íQÐGáovDÑE_}ÑÈŠÆÎhìŒFO46E£'=1ôÅà_ öÆ +bè‹/™±ôÅÒ‹¾XìˆEf,×?޾8úâˆK}qðÅÁ‡ÌxüŒÇÏxìŒÇžxúãÑOâ‰C<ü èM€&šh¸ö ÈH@F„ž8ˆ½Ÿô;ðÉ¿ƒØ:àOD"ú±-ùØ–AŒ2П‰þLdg"$ ¹YôeÑ—_6|Ùðe×¿ÙØ•ÎlørèËA_úràË!†9ø›_.¶ä"/—øåbK.<¹Ä(=yÈʃ>¶ñÏ'VùèËÕS–è  +€®ºäÒVH[!m…´ÒVD[×®?‹°©ˆø¿bl-Ƈbü+×Óžô”À_‚=%Ä«¿J¡-…¶¿J‰q)ú«¡¯!N5Ð×`W ô5È®¡¯{=èô Óƒ|<Ú=´×»:èë𣎘Öa_=±©Ggÿ7ÃfâÔ _3v5c«ý^ø¼è÷ÂçEV º[ÐÝ_û¨žVu@ÓMú:6èi–ÂRy¦ùcÍ™gê üg×Ps0ÆâWæ^jÞµ¯9—škùϳüçXþs*k?®šGùïÉÝÛ¼iú|iú²æ?ÖÜÇÞcÍyÔ\ÇšãXsk/÷ËÔÆš³XskŽâ??ù[æ#3s‘¾ ß…ÿRµ2†ÿº.øì.©ñ$u„=‡¶9“ÛLa›Øãƒ‚o6.5K|@êÿô Þïˆ`‡wëz?ªîœª/qˆÔå“ú£RÑ«±ÎVø¹ØxR·ÆRTØÀª…‰yÝ'õ¥ûu- ³¶Ï°®½­°MÜÄ>]ÛÄ|p—Ôáìà`©u×£1MœðP©ÇÝ+õ¸¤þ¢G×à0ñÂíRãgXjr‡KMnäž9)5¹½Rƒƒv'<ÎqÁ Ó5ÍÚÅÐ8wéyÎIýxqî‘Z?Ä7#P°Cü0±##Xã&fª_ú3Ât=Ÿ ܺÇÈÌ‚6‚¾Hü‰Ä†H|‰„Þ†,q°¡ÃF¿~;|ö^½LE¼QØV‹Îhl&ÎÑðGÃîäÅp½bà¯â7þXäÅâc5±®†¦›âˆC|µÄ§–ÿã‘}Õè¯E~5|m*ïCŸƒÏâÛ]:3×}›ÊùÐÛ_òÛЛÌlô¶©vþæ`sö烜q½tœ‹_¹\Û\d䓇/yØ‘‡¾<Úò sÑæ¢Í…nm.Úò±?ò±!Ÿkϵ+€®ºè  +@^!m…´ÒVH[!mE´ÑV„_EØS„ÍEØXŒ}Åø\ŒOÅØXŒÌô”@[‚=%ø]‚O%ЖB[J!uß=R[ú9|žÓ­k¾ÏA^p¸`·÷KM½ Ánïìö]º&õ1Ä$Ñ£±ÀͺïáR_´W0ܹ”sá›ëÕxÉSµŠÜ‚›½)èMAo ô)Ø›B,Rð1ßRГŠ©Œ›TèS‘ïæ7•ئ·TxR‰I*zRÑ“ O*øÓÑ›‰Œtd¤#£éŒÅtä¤#'9é“:År'ñq"ˉõØÁ_'-øàT¿ÄˉL'²Zðlj¼L¥ƒÿ3UñÈD~¦úK\Ê /£¿ ¿Ëh/£½Lµ#» ßËñ½åø^Žžrt”ÃSŽÍåØ\Žüh˱·Þrl.çs«â¿þ ü¯€¿‚˜Wï d4‹ äT »»*ீ·+&u:X‰¿•È©„·?+Ñ_Iì*‰[%ú+Ñ_ o%6Tb{%ú+±½ý•è®Bw'zè¯"fUÈ©BNrª°£ ;ª°¡ 9UÈ©RtØÑ€œä4 §9ÈićFd4"¯?áo„¿[‘шĸ‘86bK#2‘шŒFd´ÒßJ+ý­ô·ÒßJ«ŠýmèhCG:Ú°³ ;Û°³›:±©“Øt›ÎIÞ.!6KÔ—ˆÊÕ5oQó”}­M«9Èôz_·½·õgkþ0S7ëï«›µ·üTå¥*'õÏGU.jå\s3sú¯ûç˜þù¥•[NÇs°rÆé¹aŒ_Nè øêÚ¯…Ý`åi~¹Ø…’k©k£¾´6üókƒÿ#œºv¹ªK­ðþ">‹¹#'¹Çàc¢W×í8k—ˆ]ç!ó8lHеªU]–DtÍCþ<èíÒµ§ù›¸GטU5í’¸“‚t‘ó‘•„¾$ô$¡/ ;ãS’G×IêÒµ’àMBVz“З„ž$úKñ7Iý*9ØSªþ¢+ÉÁz ]ª>#/Éðºés«>ìMÁ7qrcƒ›kàÆŽõü£?U}†Ïßnt»–©ž‰øàFN&ŸÓ°ÇM¼ÝØèÆ7úëöÕsßÕ«~ô×CS†Þzl®‡¶Úøë±¹žþt”á zZßo q(ƒ¦Eñ"§[[ÔóÛʱ³\õ)^d´ÒÖ‰?­Ø^Áo¿­Ð4¿ünPü|nÅþNõ½ÉßVtvÒß©þ¢³}η›?ÿˆ}±ÿŠë=ËZÏß³ö›¼ö?y­gfç«û_ÿ•×y¾Ék<ÿ/÷¼þ3×w\35ù÷Q“;Ž@ïØqÄý¨œ…ŒYðÌ¢ý³éŸÞÙðÌFÏlô¦kœ9¨k¶*µ§ù|ü=桒9šþEãR›¾cÜRƒû{tý«Än]›/ysáI$fs‘5wدFú–}¡R‹º_êc¡kŸçõé:Y‰Ø¢äê:}f=jü>}'@sí'Ò¢]×êS5•OÄžù\“ùèŸ=óÑ5—Ôì#~'aÓIð-”:´ÈZЧël™õ©ñíd§®K{2q:yBjø…KÝ-hOö”=RË™¡=RûÏ •ºÏè?—¾sá §-<\ê=Ó®ÚˆK)v…÷H­gx¿Ý÷¡û¾Ê©U\éO¦-‚þú#Ôÿô'c_2qJÆÆdüNÆÇdlJæú$+lK'_R O>)èLA_ <)ð¤‡ô¤Lê%R3©Ð§"?•k•ŠŽTb– O*ñHEO*zR'uJ“OzÒГ†Ýiø—†®4teª¿èKc,¥¡/ Þ4xÓ°1 Ó±1Þtt¦s ÓáO‡?¾tøÓÑN¼Óá¯ç7ýõÈIGN:rÒ‘“Ž'rʈ“9‘åÄ'²œÈ*C®_œøâDž_œÈt"ω¬L•G«¿*WG~&ã<“¸d—2èËè/S96íe´—©öI‚•ã{9zËÑWŽ®rt”ão9òˉW9rð¯ûʱ·ÞòIºUÀ[A_¼Ä»°½è­P¹66Uàk¼øZ¯•ȪÄÏJôVÂ[‰Ÿ•è®$f•Ĭý•评·›S‰Ý•讜Ôébº«Ñ€}UØP…œ*äTaCú«Ð_…œ*äT!§Jù¡d!§9 “:­lDN#üØÝHœñ£øÞˆŒFt7ªÿ‰_#üð7Âß8©SÒVú[éo¥¿•þVú[éoÔéiòÛ߆}ØÑ†mjn‚=Ä¥“¸t—NⲄ¸,Á§%*ïWù›ú±ð/Õœd_ë9jîá¿×poóŒ½Í1üçþs5oPsÿ9Àô½…VžïŸß[ù¼…¯¹·u•£[›þë=V>nåâV®Þ·ZçÇU¾í¿Çp_û ­õ+Ÿž¾¿PåÎÓóe•'û¯óXï:Uþ;}­gúÚŽ•Óª|v_ë<*oUùª•£Zù©•›ªœÔÊG§ç¢Ö{ÆéçÔ÷•kîk­gú{Á}åŠV~øužþgܧï,øëµ!oÀÞ1>­5"wÀ?µ†æ"ÆÎ"¾+Ž€÷ò,ä΂wv¯žÎ/‚f1´Gñ=q”úÞà@]gòèn>c÷1_­ßDtŸÇß¹Üÿs‘“دo«ÄA]g>‘¶yØ;ÿC»Ø«kù.ÂÎD|Iäï"¾Cù]´Gß‚ªŽlß‹‘•„/Ið'!t1:ÃiO†$ì_Œ}IÈ[Œœ$d&)zô&¡3 {“vé)}2¶%##Ÿ’Õ3Ÿ“áK†'z7ý)´¥ 7…¶ô¤ Ãïn¾RáM¥?ûÝê—둊îTlOÃ>7zÝØœ‰L·’Ç÷]=ö¥#× õðÔÓW_=1¬GGf—^(ƒ¦ ŸÊÐQ¼²0ý•R†-ŠZàkÁ¦2õŒ$ŽeÈ*Cg×¥A=×°±…4 §åúk¨•¾NÚ:±±û[‘ݪžaØÚÀçø߀Üìm@v²°±Uõ)J4ðvÒÖ ]+tüíäo+6vB߉­ê/²+ˆ“˜s®¤€¯Î­üçSž¹Óô9“5WRs¤–ßœ¨KæBÖ<ÈšYókn£æ4wèûMÍMÌ÷Íò¯Ó!X[N?œ­P=Ï0q´öèwÆæûb·Ì%Æ¥žŸËïÜשß7©kön³VŸÂ`5çý2Ø ñ…Ìzýú]®º÷Ì:áºN€Â°ºÀ®1‚¶ºzg«ê™]à–›Ý\ÖÚ ë‹)u…÷£j™)lxuªå.UŸLáî¨s' GôžA+'µèºdêÌ”ÂòQ@æ~Â]L¥Qóê2qr‚õ»E3g´ëw ÿ]½Tù­zTªwr VQØ@6·Æ²0Ö:·¡°Ô¾E…®Î^˜{ƒ4ŽÂ¯Qç:Ô9 …ͪ0 Ô^=UKKÝCêý¡ÊÃÔcX½_›zÕ#ûíÂÓÛ%g(<²Ï«sMu¦BÕäRïÕ»9…M 0TN§öé),užBíÓT÷®:/¡0gÔRžÂTPk½K [Ò­1 – {I¿Þï¸d@ÞóÁ·dDc,w 2–Œëuã%ªmRïßS²Köhœð® ýTaºw…iYuN£Ë®kp)l…yÓ…]]NƎ—íB_—GïÏUu”Í*·T6(Ýj¦Ò«öª}šÊ.…e®ðuÕ;;µÐÄ¥˜Ô˜·Ê.KO—’Oìºz4ö˜ª½Po×x ¯¡iDcþ(l]uT}1e³‰GáÕ2ÔJe¯ÂøQòÔG…‡«ìVßQ&žzöÁ<âqr»ºô^È®^m»‰‘‹ì®>„ú>U{%ÕÚ|×€Ô)Û k•u kl ®½dÚE{þw)wi 5…w¦ÎL©}«*ÍScÒ<ïÔ'˜ó½ú½­Y»®_ŸÑ)îט¾*×V9±:G¤pƒ·ÂjRûc•ÿ&NR¯Æ V˜Ã ÿ¢½[cüªš’jº„¸ü½?©Ùw™ÿÙŒäãߌɽ6ò/A©9ÇÕÚF¿W÷îæê—"?ÙópÏo=[Ý×4Úœùì©m-AoeO—d¬=gË-eo}f;ûŽSÃr–«ÒªÖõ?:϶¿æGOˆ©gÝ×÷¾S³%×6:ÿü‘Ý¿û£±µý®5«òDŽ®ÿ¬ÿáß]n¬=còÞ¬ªýŒU³yÞã0,»c×rŠß(úÕsû[òm£ÇÝ>øàϯ´èŒ­+Ê^ k*6Ö~÷àÇbßþޱòo_²ð“DøÝ&ÿo†}&èùïÚFçýÙžç–[¯>éí+á5Ö¹ù”·O0VF<µòžÓŸ‡¾[Ó§äÁYëæÛF_õîHÓ1ÆÖÕ§¸ùÀ¼È­:ÆÚƒ®¹ó³+>²ì°¿Ñrs·±²øü_üqé.øûMþ_wÏS–ÙFƒ†Âî82ÜØz߇Í=ùÄ”¿C®ÝÏ}Úϧâ·rû¡ãg¾ZbÅ 9#ZNè§O\Q„ݳr ÎºÍØºný^hŽ|E®ÓÐØù1sî~Ú6wóç7„&«JB⟩¼×/~¦œÇ~{ñ{ç¿¿ÿwß4¿Òغñ¢ÑEçí³ç‘[:wÞý‘í˜sßÜ=7ætcÕjÇÕO%`¬jܶñÃÃ"|~mÒãâÑê#–õFoµ.(üVáæYƶý>|æÙ¼¡ûŸZø\Þ€±êüƃæl‚^ƒGÞt¥§45ÙFÏÌÿé뇷Ûæ\áYwæ…¾8¨ËÒ»t*ž+?}ì˜?Üâ×›ôøx$õêoÿ´,Â6zþþ§_vjœ±í´Å7}`Üï».ßßVÕ+ÆÊËjÏlZzŸÝz| ÿz]ÙK_ÚF#®»&zîþÆ6»waß~ûìx¬ï™7—FÙN8xÖߎ1V|4oçî­ûÁ¯ÇÅ0Ñúâ·GÚF£Ã¾Øsè“Æ¶¬Ú³ûíûô¯{ýVÛÛÏ+Ê~>ûŠ­†Oƒµ/uï8»õ8ßuŒoûÉ«Û?5¶U]}_ú¡Ë#ß=üê‹þxòjcèñ=úñ~çÚNy+ïÜ2‘r //ëq±6cÿÍ[.ó]ÇøÄÞ†çΘº/·u¾úË‹"ß|}ËÕ/Üe³ì²˜Ó«ëÿ´ÉXqÍ—ÛªXŽ<=.†>0¨m4êÁСñfcÛª¾Ãr^Ë'ž»W?<•³^>öúêÏmëñ ¿Cw¼£î,Ûhäû!×Ýy±­/êΧ/4†¸›O|û5c¥¾_m‡Y×ãe=.†êo½jÃÍQè]”ݱæcÛÀ¹»ÓúfGNLÖ¸^¡Ì^Ö ¿CÑVÌ»ÄWGݦ÷ÇÛ9íÀà„O­x+~ràI;Ï<ÖÏn=†Ž ¼n¿Ž¾8欌ʹyŒm/^þ´ó¿|ñ{aõö²Ç4Vz'~~èqñðú‹¿\Ø_í³£½%|e¾±íów‡¾ñ½ÈEgåG–®5†6zì n•@ÛèÅÝ%¥a»ŒíG\xÚ3c艓»oLÌ4–çžqЗ+—A¯¯ÿCç~Îþï'û¾çæînË{ÇØ~êó+n{þOG>ܶ{êº.?ë‡/¤í¹ 9z\<8¿ë®ïôÜj½å¼?\–}ž±=ï’S{_ŒöÝg›_ºóÅ›–Ë«v]¹à¬#|÷ù+z\<°67f¿‡³Þvkí«çœil¯ÿÑ+ÇvÜî³ûÖMÛS¡×ãàÆžYÓâ*ü7=il¿øç rÇn²âmÝ?~ñÖ×ÿ¾”}qmô gk`a°±ýò–ÆSo[o ¨HË ÎsÅÐëë{ÿú±7ŸüÄ6ºñ)¾Ñ§žßÆö~wHØÃwC×V”´Ÿj›'ñ[öçCì9À7î6ëë|JìEa/Û7îv.ýsÇ›ûûâ~Ïw™{ñc¾ï—›ë®ú}ïݶS®}áü¿l9ÌXöæ–ù—¯yÂ6Ûòg³ƒ÷ÜusÎõ¾ñð~ÒeG¦ôùì\÷½ÄüwøžWÝtÏæÞÇmó­ë©Ÿ+~öêñ18ÛùÑŽÒl£ÿ³î¼×®ºÙØþÒêºü©o\,{kÁ½ï,œ²OÆ7üz\ÜàÙ~éÝ-¶±o·{V l1¶¿y}ý©ÇC!^ô©±œ‡ç¡auÐëqpßž\dãa’òA´±ý6~¹åœc¨i·÷äýž5–*¡àûd³w/«Îh|ͲÛ6vÚ…?=´ h*ž;~¥Ùu±×çwÝçºؾ·²)æÎüÚX~Éõ/½bl›£íG®w…œµñ¾smc[\>t¯±ãøÇ{Ó›Œ¡š‡?^÷ɾûq³wÜ÷ƶ 9ßú¾²e,½ãªCÞ0vœ½±â¶Ç×ûîƒÚ.ž³ôß}tÁg§Ìª³[ãÔ¾{‹'úyeCùŹS×qGâ¼ÓŸ|ßÿöïÞØsÁš©üʺŽrß O_ýù„¿œáxÏ6V8#ù _|ÌËU` ‘d|»½vj|ͳÆÁ=~uεk£’Rmc­Çe½¸³ÉØáý(·²w1Ô›7ÖuzøT<—©§N}¯õü_ƒÛÌÇfêÔ¸»èØmGo4v,½­,uÏý¾ï«+yþãl¡ïæ]ù³ïÜd,+X±êª}~þèqrë;…ïr˧Sã}léËË;ÿ+cÇ•#eËœëûÞíùá啯~<5Η=ø«K¾õ(ßý³E£[Íaø=ÛØªÚN{«jê9°ã–¿·ÿñcè'¼rgЄïºoÑãdÍSƒ7õíØ>õ=4vyúëT;î_{cïÍó|Ï£ݶ'å—ï:éçrôøY£†ñÎe¶±kOjÏúÙŒO.œœ“=h ]œÛðɼ|÷—·òƒ÷V¯÷=^ÕãeÍþߺÎ8{•mlõzFX°±cË…«Ï{ñÅÈ·¬q§íŸÊ – ¿5|áŽIß÷â«zœ¬^õ£·ÞltØÆî½2ýÄ‹¿eìøÀsìë>?~ú\ðoªO1–2ñÚ¦žðéñ±úÐ_Æ =Ùié³=òß*Ã0v~ë³Vüð/‘ÉxúùíE£5Oÿ¥¿YÌ“ìëú O—[FÌÄÍ×õßý¸~ê¹aìÔÂg×Õ*AÛ1åßÒ'~XÑÖðºŸzÜܲ²oÑ‘—.µm>BeÖS÷ÕÎEΘ·ï{Þ_ýáùgÿ™±TçßðëqrËIIåóß Î¿ýÓo. YhìLv¼|¯§Äbœ÷ìõÆ2•mÍsãU=>ú/ZþðŽõ6ÛØä“)Ç\²ÞgI÷ºWíOC«ï÷¦rޱ,鹿4ž}|z<ÜüãË޾â8ÛØžþÞÓ'5v6Û2¿89Ïw=%o_Vg?ìÑžSì»Çõ8¸)/kÍûýÁwŸq×ïºýxcç"æ>ð®ï¹¢óDc™ž¯À¯¯ÿêë ÿ’©ë¸IÍR’n™ºv^úôõÝóoüܺ_õswêzZ÷ëÔõ×ããÆ’¤ð…S×sÓ¡qå7ï2v^{̓…‡‘»c2«NY}€1tëÀãÙÏ®´âè{ëqq#^¼sß‹¶MÁµË2\ÃÆÎ[^žÿå›ÏC×VÍW‘±ôÕ¶à/œèõõ¾ájuì±mZpÖá\cçÝ¿x¡ÔXfcé3ý›o{z}}¯tÇAÝDO}¯l:g÷us·;×´Üá—>^ûÄ?0–~|ÇŸÞm¸~}½¯ã!pôÉѶMê1àcçSûß~Æ=Wú¾.º÷ö„ñeö4<ÍO_ï_2Øÿö¾º«bÛ›IH#½÷Þ{'!™zBñ"PPŠ4A©¡ATP@ðZ@E±–$ êÐ åjH =REû÷›³çœÌÃ÷î[ë[÷Ýï{°VHÎÌn³göì=ÝÚÕˆÑJ¿¼UëØ“Æ~Qt¯ûvòeDàõ“áb#Õ÷f?aýõú®¹Ç¿ÏãGóÖÎQÉk~-2ò•~k:o“ý÷ŸêûY1Ì_º…ÕLÙq± i3o½~ôï÷>ð3¯˜à‚‘v_viØâaÎóOõ¸I„ײš…ïý~äáûøY󮥟—Ì7ú#1Z›Ý-n£¿øvØHõ¸¡ùÜS…qc;ÑÊè¾ü¬Ç¯&U½À+&ªÿŒ~2E 'Ÿêõ™Ñ‡>¸î2ÓXî^Hñ~6z3_b6ƒW,íù]ñ瀧z]¿fE½oJ«‘㛳†áæAëÑ¿n1€£ú{Rôª³êXÍ1Õ¡ó³Ãø r”n§²<€§z[£†Í^¬¦q"ƱüìømSŠf¥ðŠç+7Fòe«Ëýú¶æ.6Q}­Àà`àUÈû…yßäÅ“øÙ9¿Äœûr”±ž¨äËf4eñOÍÀ£zzì×»ß:k`5W~ZÉ~>ÈÏ"˜²ÄÖØÉoòeÔ€Íh7MTo¥bz%u+«ùÃ}h¢O:?ûÜýûæõÿ!ëûÚIOäO_¿^¼]o.ŸÂá ~¦‰êmqú£oþíx.«µª¸4iž¹ÞŸÝ½ýÎ=ÜXŽuÀ“ݩ՟1Þo¢ú[“øÝ÷û°ZÏGæ<鿉Ÿ=2ÿJÒ„qÆò¬yäìƒïÀOµ>{=rÊã¼HÕçüuàËj#î-xaV.?{V´ëyÅò²‹#£‡ãðüµ5–?#m¢úÓ'¾ãõ¦í¬6%$êÃY1zÿöÇW¿|kÎxÄAÍsßß·œ/3S+xTÏ3:]á¹[ï¿j î_p‡=?gÏággóŠ'Ô‰3¾,ÄõÀÇÛ ›©¾'áËþù6½ÖÞ­N¸ñs¡«šMg`Ô×–å[;í*Œýy3Õûô­÷GµÞØa²i²®÷skÜy9ë+­Ÿ³~g±ZùEkw>n´¿fjã–­Ëi8øª¯Ö.üôÇ× º>Î ®/]ÏŒò½lñæ+ï¸íQÆ™úø¼™ÚÇ89ÞªÝ<ë·½#Œrެ>³l›±?{aVÎ¥}÷Ç ÍÔ.&hwŸyšÕ~T O²Ÿ{ðÝÙKßó2ÆÏÒÏ.WÃIxÔ¦ÁIÙôö7êI Kââçæ~ïÒïs5;çË_ÚŽ³€GíaææU7ƾÖO?œ¶|mpì²…üÜãϯ}ƯI'ñå4O çAÍÛ€Oí`Ê æ­ÛC6±ÓO­ðsún~îX¡ÙmŸ=gÔ7Å­Æzj¡zž€ÞÚìúvúõï^x/Ú†ŸkµÿdÚ„4^1g~XÛ±&¾Áf€Úq ÕëX1=ì¾Woo§i^C+Î}ÿÛÙ×¹Çi3ÚD4xòåj6Àhÿ-Tߣý£çÅ?uD¯ïÓ.+#{[ó6“¬;ü¿01ú#é7¼mï6ø±ª÷1kYVŸŸ±ÓDŸ·ÙN7?9ýüCƒï™ýÅ(þøÄϳ¯8Ô¾­»üºŸé3äC¼ÍýÒ¢é‡õõˆêhøãå³¾~ôÜJõ=.ö‡Ê“½ÿ8C~—·Ž˜Ø\o”Wö?+ÂNœB¨i´¯V¿?Ùí™ø µ[¦ïÐí«-zPh>å/ªdÚ|*ð¨ž~á õ‡¼-å«ä½¾õ­N‡ü¯ øxTÏ}þ6wtÃNvføäç·•ÕéõÕfØåµç…©Z?ÏW˜¸=—e¬§Vª÷‰ïz=ìc·–™|yùØŸ.åÿeãæ'ôr?¾}︵;û©Ö­êÎ-gŽâmw_ém·Po—‹i¥e€§z=óŒpSrÞßñ·£vßíà »kÇvkëÝôKõð=?ÛËOµyŸW¨á¦/_=!²öÐXGÀS=LªS'¬Y½:Mù(?¯aXÝÄ7y…˜»ú_}ï®’ß¿ xÙ¿ÍL¼lâíº]Ö‹ÕÎ{¬ùyÕÄëO¸Íw°DeürW­œÆy³óî÷êižBï÷Îϼë‡Ûÿ~šWH?¦Å«¦© <À§z›H빬ƒ$Ç» ùù%ó«r~¾W¨ÓÞ|Õê-à©þ&^6‘6ka¶ý z»=ÿÄNÑ‚xŒ8úš_õÂ÷®1—wêqÙ©ng ¡?{X_ÊÏ¿0ù(†>ÆþOkZ½ž§z}È$gù¹ÏâYCªuËÊ;àçwîz뎺lÀÿ#yLjù|Õ÷µb¤ xªG->l(îüÛÖÛóøùý;j<ÀžÅ0ì¥7øªo6òËù%€§zœ?}GÌÊš*½^hüËÏ׎©šÀ+BÔ‚ñÕfêÂð¨>—({&v¦ï` 2¾;¯.ç;ò Ú§ µ3ÃÅvª¯¥ïn}ì·¹¬áõ#ozñó×ߨw©5ú7æä`úQo¾êGtyÀ£zZfïktà Öpäb|F{ŒÞß´›^YU”¶ØØ_PûÕ—ŒYÃYá8üx»“:Ã+ÄògtË•íbµ¾ Ô¾Oõöø‡ï¹9o]ÈH¿¼ÝÿÛI·¢}ÊuéÕ[Ä¿çOõ$ýk4 Oz{×¼=ú·ÿ€Wxýq±ïÀy|Mü«h‰zj§zZAë=¬Ñ-bKÜ'6¼½ÿ ù¾‡á×ü7 ûx@ _Cëb€§z’ý™^Oá÷‹ˆ‹·ç[w~]}˜WĈ‰¹|ÍgßÚx¿ xTO²_aiß%ÕmÇÛ‡]0µî+£Þ£ÞYúÎE¾¦ã‹ÃGžEÕAõµº£Ln5k¤8Ÿ·\çR”m´S²¾¦qÊ%zè úZ+zí€_Xãèü¶Îûðö‰×”— Ÿ¥8ÍX®ª§uöêÄ)kœ×÷Å{®ìãí3MÂï}è>¾&[%8ªõu"°\Æi>Ÿ·/:²§å®^|«ºñpTOgÙ•2Ô‡5~4úÞÆ)¼}¥G†Ó&Ýè‘þŸYÿôxÏ„2ÖX]=im¯;yûº¯_„Ëãk^úDD"€#½opRõx¿ñÂäÄ£½"tûoîwï×w`œÓÅöŸò5¢—¶è:TT7^ÌŬ]î&Þ¾Ýv¹²´WôöÏ™þ¥;/ ¨î“½Úp±“ô¿Qu¬ £94!Þ¾Ëé.ÿç–Cûïñﳂ— ëõèéìdÀ“Þ7~u~÷ê­¡¬ …zxÅ7¼}ïüoÊ8Ž.£uÀ“Þ7"è; ôÕåžTÞ~¨ºÿäú{y…:=dÃË0:Û¸ñÀ“þ7J{h*ô,\hÇÛ?«ûmGÊE^®ºã^Fú<ÕÃFš(ÓÛkÓð_Ü<í¼9y÷¾k6¼ü¬³ˆÜx™ºÜ9xT/?¾]D¬IÔÎÃ+yû…Êγkxù±ÃMëú|8ª—MÊüE‡^ú5=òAINÜo¼ýGŒÊL–òòƒO »vˆ—ZÔ°ù½¯Oúßô”yïK½YÓÒƒY¸ó³§NmÙ¦Ñåe™}…ç3\ì"ý?‹(ûàÞÛX“Øý‘ó8ïpQ;8^^¯"_CñàIÿ›ëE cËšÞÞu½6nï}ÀsÄ O^Þ¥NÔó5{¯Ýó~õ£€'ýoižÃš>)ïôéÞ¼#åÀ}ÑÊÝ”_žþ`Þ¤4£}uQ=æ½iÖk1ï8Þ|t|-7êçKÒÏ«ßÞqÇŸ³fµÃxÇ™„±‹<›OúxµCL„­bÍbUÑëmÞqöªËÇ$=l/P'¬X3ÉË;.ôY}#ã ä“¶#8Él\ÂZ¬?}ò¥)°‡ËjCB>éáµ¼alÒÕÖBq'︱¯¶4ýÙ—¤‡×åú@‹ÜïÖ©Œ64æä“vx©¹¬eˆ’þô+¦¼Óò ÿ¦.^ =¼!ýzËê ït:Wæ\=Û?ù¤—wEu*g-bÙñÈÞùZĶ ýÏv£CúyO¬’†<ÄZ‹<žwî{éÞóB^ÒÏ{¨õ¨í XËñᯯ8Ê;÷ŒÙò ÔßW¤Ÿ÷°\Çü_X õ‡¼sÿ·fiÎÈ'ý¼¯n?Kd-´¿•w3ì{÷Ê݆‹_“>>ëî-—ÖOž¹ÛŸwžR÷M ŸôñÁÇy¯d±–÷¶ù½ˆw6ùþ2ýêפ‡òœCǾ=ñ kUžœ3?ì<ïlf²ëépä“Ê1ˆÊöÅZiŸ*ïüº¼tÉ%?äSù+´øku[¼Èìüó¼óŠ:nA>•¿âó¯*¹Ï“¬5èJhuØË¼ó§Ùc‚¢ÍOå¯,þüưÖµðÎ?><‘³ÚùTþJ±‹ÏÛ™µ¦¼Úë—ñ.Ó’®Ã‡.^¤òW¶æ½*†|*ÿž"Õq±V±½õðeÞåì÷¤³¢ïWéaϯÇ\Ž:ó.ÏÀÖ;›† Ê¿‡æuxW€RÚò‰æ_OåßsnÁm *ïæ]i{vÆ×#ʽ×cÎ?¬*.±Ö¸Oà‰žà]‰A{¬Ø€|*÷^5,î ½ùÍØóù~Þ•±aSÐs·~CåÞ»·bú3É5¼+¯×œG&Ûå7Tî“rOO Þ ƒsãË×Ö7OåþP].Ïá]Ö?ÿÞ0´×o¨œû挋4\ç]Ãk×[GÝŸõ ­+#ŸÊû‘ŽNá]#ï°¾tôð •ó£ê}ÓêæÍ`­æ‡~hKíÇ»îWÇ+Ú:7à¨Üctxßõ»x×èŸ3žk;õÃ76 –nȧr|cð;Ã?ZÂZMÞ3uÁ˼kì⤽ùð—¨ÜŸ,¹¶l{Ú“Ww= öïYW(n•Ÿ[fÎx¥Þ’µü^¸ÅóÈÞ5ñGÅq«Oåç´O…µü¦v`¼kªGÄwÇg]Öêýé£åçÇ.<µˆwÍè=òçšçŒò^"}ìïý’ †ð¬å‡YÃïx•wÍ)ú¦¡ùsä“^ö#˜vçXÖ"V4å] ŒÎÞù#òIûëÔkéRIÞõ˜Ó;)1뺕‡ôrQ,BÖ¢µ§Uç‡Ú¯¤­?.~Kú9 ǧ-oñ®§Ú kO‡ióY€#ýP·µç]›Êo‹4Ë2Æß’~4© ñ¬QÀˆvwÞõüµio¯05ÚÅ·¤Ÿƒb¶(w)kÙ§n8ã]/¯~öPÝrf¯í_ú–ôt0aëijÑÏíT; vz÷[1.ÏìG>éé ‚“赬E®“~ͽéù-aÌZÛ·ô-éë XË&uã2K×ÖCÄ®Ö+ã>§oIoÅìvð.ø¯/ûìªüŠÈùÓe¹—š³mþú;Òß!Ó;B\|ÂZ–9L{ö‰¡lˆœ¿:=Òñ¯g/ÖæaOz—|Òïyî£ùJç“k9ËFjãÔˤÏ#r\û Í?²Z±Îù*»ìp¤Ï£´?›5׫›¢­/‹åÔÆ ltÕ%oõ3\üžäh¦yN6UE+`µOŸÿnÖþƒlÌ5±p¤Ï£bUv` âéµ¢ÆôyÙZ1tØÂÆÒþ*À“>›Õe¡VûØ]»“?¾Óöq}Oú;ºSÝXÇšŸ›:t÷•Eúú·\'éíS±Êœþ kžÿZ¹•m«…–º±ñ´p¤¿OÕðÜœ5‹Ñ¢÷b¶ô6ubµÈø}ºÔëm]ð{Òë§¾Y®sÍ8!&”,´yUÀ‘^› Zî{`ÕD}Ýx’¶|…ôø)­{aœEçΨÓàyÚ|‰æ_µyÍÏkó¡úo9îÕú[ÖVÑ0íÀ½_CvÎîÓô¿{ôÎ Ojûاò÷Lô>h¢l²«þ¾rR=~ú0àÏX3ͳ°ZUmñìA±+åÑ7GõW%í ÙNU«½_ltÀfJûœ¢õKW¨>«òªabÚ¸—ÕÞ¥ÑÚà¨>« ˜lqz#k’qm­ÅÚ.b³h?›úОêµJöÓMZ¿0àý®9éŒÍvPÂÛÑN¶žê³êu`Κ԰ԅÕFo ø¹î›-íùa±,¶óÀS½V -¤fM²=ÖúŒŸîê/W§³ïçe´ïÏ8FûCyùE~rüm¾–æÿùZÑjJâkWÞùü‘Ky…ÕmöÍÇŸÕåY{T@çkßU7éó„ºüê±o¾vܶòOÿ±†—]]ùëÞÀWxÙØ}¦?_<¬ÏÉy`^á.¿ãð29/R;_ÚÈËÞò+^¼"±(òÂG®z9*Üí‹~{©V›o4ÎKÑ<Ÿ/é¿5û×ê­Õæ71#ÇöÓ>F6R®k hôgZ{ðž1úºÌté—kE´5Ù—Í‘qÂ4µYc\øõWǬßC$ô.kzîtÔ€/±Z;õà›Nû²GýÅ1Ùß5‰m¥Ï^gµê1ªÙêGÙ Zw<õÇFªi¬I–ëÅj~Y,v(°¹r]_îã<õÇÔe³»Y“:Ì·`5ð&›WÝ`3Ψ à€£~ãØ+ÓbÞ=œÈj¾m‘2›'ûkÙŽú‹cŸªŽ‚5åo;¾™«‘qÀ#Ϸ͹þ[ਟ8&ç=šÜU‡ªíÔÏVL¹Kœ\ÒíEÛ×,çñôu¶òö·ÖÿÂË…¹¬ÿ‚—mW †—Ó8„¯õQ^a«v\¼Â÷%±bÄ×®?•Â×6‹ Ä`¾–Æ?¼¢ÿ™ÎW–…òµbXšõ5¯¦nÐÖÛ‰æ?d­K¡û9ß­·«žøZ µ'ÍŸŒ•õ­µ'm=õ¨ÜÿÙ9;*Üá„ÆöFçXíä™»ý?¯é[÷—³¥ž'ýÓ#¿°×ÎþºõAýðq‹ÞbÆ5Ñú«Ýõ‚ó¬DÊ7SÛy•Úïñh›»ÊeœXÓ\þB‹Ï|6“öŽÚïqµúG³F¼ÆjÄ)Úá²Y~ê†QÀQ»=.Â…ï³FU é¬FÆý³è<਽W§k—°FÑÊB÷±š#î ‚Vfó徉YGžÚíqu;Û@Ö(få§Ã>2<ñä±Qzû] ýÃl:‡Õú9OÀZû‰…~Öê­®Ûëã[m¼¤7fÊxu®Í—ã–…²?™§³‚ú¢ö~BÎ#7оRV#é-èï¼ÍæÑù"ÀS{?!foûV±†êÂ1«ÛÓF¾ÅæÑ~ ÀQ{?!¶Éú…5Ès 5bvÿvV"N¡,yÙpñºlçGÕ”¬AÆ×5´Ž®ÛçÙÏ•¨ÛUó'Û»,OOf5êñΑ¬DÛ¿p]¶wXÉÖŸ,Xÿd5ê±h¶DìêUÃækãÁë²½ÿ¢kqn\•ç—ô¸RÛwÚ3ŽÔà´v®¿ªˆðµ¨a‹~ÎXÛ¯¸F®ïUо-¾fÍ‘¬ºšËsÎÚú¯4¿Ô5¨è´¶îÊ+ižŒ—©ÇE‚yÅu±A¯ÁØÕð0Mƒ×ÛUÏö×ÓŸÖâ Ì]OüøêÖ:@=pËZýÕ5úüG‹¬/m?¬6>ž&Ûö[ÿÍ•~MóG h›=Jö­—F·ad?—ÍJ½xõBýÊI«’>èºX=ŒÎee$«éãõõ•Û²ùê6ÔLÀQ{>©º±%L¬rýº7ˆºšäÞÑË™•Jyhëðש=ŸÛ¥olcõÒ?žR·»Ú²R9þY@õxj×'åüG=Ù?;¥NSßËJåøuÁWbÂ<ÜpñGjß'e»¬—í픺òˆí£w<ÅR\ xj×'åüM½\_9Eñ:[Hõ 8j×'…Oqaõw¨ a§Ä­¹¿ëã^-NªxðœCÇô{zæÛgPk¬ÇKŸð :¢­Gó õ8¾·±?£ýÊz¿Z)vMGMâeÿ˜ýô|'^é¢.ìêã†J1Užä•aã®m~á{={ôv©µCí÷±›ÇMz<¥õ—ò\—6¿«Ã÷ē笴øŒµŠUÌ{a­îâ@E¥6/ÇÆÉþS›WÒâ®Ú©OýÝÑå>½ÕÆÁ%¢ìwI˵vþ¨ôÃ%ÒÏ.‘v Å µñöÔïœT§BY}š7zöRvªB ¤ÙRÏI¿xicÞðF«W‡ó%ìÔ›" ÍÑx pÒ&©Y½vYDk“Nèþ~é)u†-‚5xæàI»ó—uªØSØ©un¢geÉò?ª^‹2ðÒ.T·2…ÕÉóç§–¾óõê¹)ºK#ö¨6pCÚÇ/ûÃdø£‡ëqEåpu@Û—Â+åŸø(r£¶WÊó«käù©Jq:àn¾æÕ__¸» y*}|[Iû©ôq­Ü¡·Í>ÿj«çxDîÿÑí¦g\qÃGH]¨3šaÔNëóWÚ¯©Íóhñöby6®Óâî¥r|~RâÉóºzü£ÅÛ'Dí:Ò´QbD‚v!ãÚ‡ÁΈ°eë:V­ºe´w¹ŽTª­Gý$ã1z¹ŸyFÄjV-ç]µ¸z¹ôc¥+»m©W'ý‘˜fذ‹‘ãàêÏÔ‹{Øro—bÔŽðÒÉõ©3rªúÛ‰Û‡®ÔãlmßS©6¿ô“ôO´†¡u~V-Neß…ÉsnZœxiw²ß8#ãÑjqíÅÈMº]¾ê©c/íNÞsF\«ã±•UK¿¹œâP½½Xþ¹ýó qmTážíKϯb~ÿ;¯ü,~hSÇ—Ú¾R^©†;ˆëßL?õDð·|O/uñ¶/–ï RYm?ߣ^·á®íä{äþì²¹ïï[öy4ßã¼uarÄrc¾EÔçS;ó²0[‡ƒ  ;1ÕÿŠ·Xþ7½¿ûŸ½ËòWïðþ¿ò6Ë­·xo½ÑÒó–ÿßãUÿM–íì²”w ~Ð.•ËòýØRüTu{_­”Þísƒî÷ï¬éïÇŠ{~a3¦ tϯh›!߬M¾‹ï¾›åÝÚæò ÙÔ[ä#[åýÚ€·,‘ïÇÖªH¾‹6c 9­'w»8L¾‹öjc'ï .‘oÇŠ;ƒk ¹l!·-¾íânòn,ú2{з¯êö¶ÈPùnl•¼S0Žâ~aðq‚LNùnl½+â<£Ç›±€w—6ùf,äp¾kÝõí&î†\nUäFÔ7cAÏ}·|S$L¾)ðô4—oÆ‚çVz3Öºòo/Ày¶h{ƒ—7d÷-oÈã]M.Ép>3º½-â'ßY'ߎ}?Ð÷ƒnüã¤ùC.Ðò‡\þ(KtÙ î8¾ ßI£;lWÑݯÂÝgÐùÆôyƒÁ7ò?4ƒÛè YýýØÝò¤…‚wè ùÆx…6ÈwFÜè®Ü°1ôŽlä ý°6r©áàÜpä‡#?ùáÈo#wüÈÚ Úmä†#‘ ÜHàF7¸‘Àl#…ü(äG!? ùQÈ‚þ¢ÚÈ}GC¶hÈúFy¢A?eІN¢¡«è*z‰ÿäÇ ?ôcÚÈíÇ‚~,t Ùb‘ Ùb‘‡ºˆC›ˆ^øÆfÒãA/üâQoñÐ}<à㑞€ô¤'L¦{±Å}Ó àŸ:‰àŸùŸøÄ /’@? éIHOBÛI¯äzßPÜ{-Bõ^ÀôGùú—Ò]Öâ=BõÍä¥#=|¸Ñ;ˆâ-ñ~¡x X}“<2A7¸™ÀÍï,àfAV<ð À3ϰ•îÎ6Ü {´Å½×9€ÉL.t” Ùr“]å‹ßUÞ@Žàˆ»†Q–"ð)Ÿ"Ð)†~‹‡ÒÛ<êû¿UòÍß8ù&ÌfzßWô™ê¿[1ä¿_ ù_Å·bÇ[±ã­Øñ¿Žÿïú©}Ý ÙvnH`ƒ ÚšrC¾Å »è]Ýã=º*ê6M†öxnD·w! ¯h›!ßì‚|³ß}ñÝùævòM:ð²€Eò=–ÔõZÞ²T¾é X«¡ò=:´kÈi ZÖÀµ†œýâä[thƒ6ôlJå{¿È·®-䲅ܶø¶K3¾û«¿E7F¾;~ö€q“oÑÁîæÇ1ô…#ø8Ùu{‹åqMçz‡Îù½ìxðp¹ ß¡ƒ®Àw¼ðÝðí¹Ü@ßÝ\¾Azî7äÛ¨ ä{€Ÿ§½?ç žÕäV¼ÀÓ 0^ éšÞàá ™½AÃrxÏt}ç<Èâƒ4ß0zWF{ƒÎ´ý@Û:ñŒ`ü‘æyüAËzôG ƒÈ€´ô'¨‹@¤7ô¡«@àgxM¦{ûƒªÈŃo0ä ~0h_èñÒBŠ´Pð…Ì¡([(x…¶‘k C^˜ÞmoÒ…AÞ0Ð^8ø†(¸áÈG~8òÑŽüäG ?2D€vhG€vò"‘‰¼HàF7¸‘Àn$ò£…ü(äG!? ùQÈ‚þ¢ühÈ Ù¢QïÑ(O4èG£LÑÐI4t]Å@/1àƒüäÇ·?pcÅèÇB±-y±-ùq¨‹8´‡8àÅohÆ!=ôâÁ/õÝÇ>é HO@zÒ@'2&€"è$‚"`3ð“f ? ¼“@? åJB»IL2x%—Ò{+âMÄsùæ`ú£|ý!GÈÑüÒíä›~HÏŸ~òÍìô&‘x¿Z„ ™à‘ º™ÀÍn&øg7 ²2à€gžxèÖP-CÐÈAÙr“ ˜\è(²å&ºÊßAo°ˆw) GÁ z;°ú,Ñí Ájùf `ÏoVÓ[Žªÿÿ´ØP‹µ˜¯{œ'â;ÓA5–[ÄnZ¼&b5->ë % ¬zÜÕ3žºY¥ÅOZì$â!‰HÄ5"–Ñb-^ÑÞA1ˆˆ=D¼!âŒîñ…ˆ'D !bˆØ@Ä"þ^øxáß5þ¯òÃ=}ð_½¯«ù]Íçjþ¶û›öš íæ[£¤OÕüi¢ô£©Òjó.YÒ‡æHßY Ûˆ3÷Ë·Z×Éw‘„ßCݘ¢=™ÂÌÐûîoí‘oÓ£XÎ éÖÂ_á»ß*z/U41Ûò$ÀÚÃÆìç;vJoŒ9®óùöÚµËdzãÔx®€u¬þö,¢÷I½Ð¾}`_¾°]?Èœ €^’@'ø›éPñfR!èB– à3ÐÕÞ!*Dy —:ͼýò­0¤VÉ÷Âð;åÉ L2 ‘ãGo+õÇß “ Ù Ûh˜Ñy rç'rf”"ï2½UŸ#Æ‹(c!þ΀<ùv4É®3@;g(½g_$ðÀ·ùù WäFo•ùÑ[÷ùaô¦hÎz»,?Þ/+èÝÑ"ñ¿‹Pþ|Ð-?(¿a½û”/p‹èÒ"”%z+o•o fÐ*z‹T}{r‹w’Õ·Æ—ÿ~ãË[k·Æ™·Æ™ÿãkê¸Sôa¥ÔT} ž°;e|0½ÑÎ{7t{ãs|ÝN¾ƒ¾C¾õ‰òšÂ¦MñmŠº3ƒ|f°Q³ÝÝÞAÇw_|÷½ ßB¼9Ú©hY€¶E)½…n ^–ßr•|»°Vàe\+Ô»5hYƒ–õù:`ûA®~hs6ôl„OG¹lA˸¶°G[ÈmgÞí tàÛA6{àØ£ÜöÂç7P—ïGoŒ: üHsŒ#äuDùÛäèè6Ó{ˆÎ é ùQçòýÑô©úþ9êÛô\E¬€ò»ßm ½“¨¾}¸Cnw”Ã}¹”Óc†ñísOèÁ0žàã 9<以ùö9Ò¼.È·ÏAÛ{|ÿ¸>€óœp} ƒà|!£/èû‚¾/`ü@ßuî?Àø!ÍiþàéZþ(›ÿez{1e@Z€ˆgPÞ@?ù:è‚^ pƒÀ3<ƒ„´ ù:øC¾`àï—o¡ƒfh† ÞB‚´P¤…‚w(ä Ý!ßB‡Üaæò=tð í0Ð ƒ¼a¨ß0à…ƒo8x„7ùáÈG~8òÑüè62D€vhGˆø y‘È‹n$p# ÜHàF7ùQÈB~ò£…üÈ“#Ò ¹’Ųå@wy—¡ìyÀe›ÉeGC_1ÐK øÇ ¼1àþ1€‰XÐ….b![¬ˆïD:~â@/rÇ/|ã §8¤Ç ^àúñ€GZê#é h  ŸÝ'€o!äIïD´åDÀ'‚Fâ ’À; ô“P®$ÐOBÛI¯äUô®®xóV}{ø)€é/bKÀõþà™n wjÓ‘ž^Âèý]õÝ\è`@µ|_<2A7¸™ÀÍÿ,”- B0à€gžxèÔÙ³Íå;Ÿ(_`r“ åB¶\ÀäCWùâ7` cd)~Áfù."ä/j£§ú-ÿb¤ÚAoÙ¶“oè–È÷s‘–$”"bñO‹Š¸PÄ€Â@»Çy"¶Ób:Çi1›ˆ×D¥‹KÄVÝc*3‰I‹Dl$â"iñŽèŸE £Å*Ýc‹ˆØB‹#DÌ bhq€6žÔü½ðñÚ¸ò_=¦üŸö¹ÿŒ¿ýÏ|íÍüìÍ|l¡lhï½ÑŽM„_„îÌnÈ·¯a;æUôæµå:zóZ~ ­öƒ-Ø ]ÛBŽhOŽøvB»w®3Ú£+h¹¡}ºCî€s ½gí…ú÷BúB^_|ûƒW’hÇ#ðI—é­Õ4üN»Aoó2´÷B¤eƒO6äȃ y)8y¢ïAÞÀ=ô.ua5½K~q#°q€‰­xðêñ(o2þއÌ ¼¤%”Ò¿ý¡çdü$6åI„.2D?$lßýEÿø$à&!=c¿¸ý8z÷[¼µú)€É€,ýÎVq¿ñ¤#?åΟbœ =2ä ÿ(gÎdùž0x3Àe?ræƒVV½;l0§w¶ Ð…8ù ›ƒ2ä>y¹3tŠ ÃAÈË\>d+ï|F‘› ˜*ù1è#}ô9°ƒýä›ÝèÃǭqk¯[ãÖÒ^·Æ­·Æ­ÿ¾>ôßaÜ*ú UT—ª>MØ•‚¶Óß½aã½ÑŽ{£.û@}ðÝí¾àMP~ب Ú© êÊ´Lñmº›ºX3ð7+ÅÏênûâ»/¾ûŠoÀ›Þ¼ÌÑÎ,@Ë´- ‹Ú¥%xYB>Kð²D;±‚.­Ð_X×rYƒ–5hYCÎ~øîØ~³èÚ€®ÍüÀ~m@Û´mk {³…Üv mx;àÛßíÈ8öÅôíQVÐt€.€çºHsD}8¢|Ž(Ÿ#ø8Ç |œv[pFé ù«ÈE¸ß4]ÀÃ<\ï |Wðp¾ðÝD zn ïŽ2ºƒž;ÊìÁrz€†`< OÀxBOžàã‰4Oày¡|^€óBy¼@Û ¼¼ÁË´½AËü½ë8Àù×p>"¾}Aß´|ãú~(‡`üã‡4Èéžþ å¹üA?ô kҪɽ.¸ zÀ Ï ð BZôyƒ¡ÿ`è&ò?4ƒA34C@3õ‚´¤…"-¼C!O(h†‚Wèez³= yaàÚa yÃP¿aÀ ßpðn8òÑŽüpä‡#?ù+2D€vhGˆx y‘È‹n$p# ÜHàF7ùQÈB~ò£…ü(äG!? ²EC¶hÈ Ù¢A?ô£QöhÔCt¹÷è9ücƒòÆ€ øÇ€~,èÇ‚~,t ÜXàÆ"/yqÀ‹Cšš]ÆUSˆÝÇCžx'>¾šÂ†¤' =t c|'ã't!_"àŸ}'Bö$ðα%xä‹¿“ “!c2äOF¦ =²§·?äèò÷1#äKG^:òÒgP82@Äà?msø 1"ô“ ú™"vn&p3Á; 2e ¥°Å<ð À3@ô– ¼ÐÈ©¦p&0¹ÐQ.dËL>ʘ/~¶r@Žà@OEàSù‹ 1ìªú-ÿb¤Byg0pg0p#m0Ò’öK¿%þ ‡Ò3†ñã_ÅŽ"fì/vQV=6ÔÖZE_Ü}½õfñ_ϸ¯g¼'b½›Åu7‹åºÇo=×dE̦£E¬&â3-Óâ0ƒiq—¶n+⪛¯µXJ[Çí;‰¸I‹™´XIÄIÝ㣛ÅB"1PÏ1ú_Å;Ú˜]Ä5Z<#b™&ŽéÃüUür+vù× ˆ£6¥Â•â}Ho”¹7ÚL”­ÚpŸ­øAÝ™ ,&è«LcŠöbÝ™ÁFÌöÄñw_ümæh€µ@û°„½Xß mÞ pÖ€±~àÑyýÀ§êÐíØx¶À³¬xÛ!ß}‘àíÁÓé€w@{qD¹!‹ú'´igðw†:£,.Á¥Šº WÀ¸‚¯ðÝ€ï\wà¸C6Èàöå‰4/ðõÚOÃNoÐöcyäû@6_ÀøáÇ4’ ‡4ü€Fò2€€òe /: ^èe€F††™H+,¥ájÈfê¢B‘ Yà 3C^ä̃|yø;z(D™òPölðÍ}&|+àó„E>ƒ_ž€ÝCÝU!ä?+2Fƒ^4à üDC'1ÐÓ@äÅ ÝÅ ?¸!o òc!o,Ê œXð‰m æ‡:‹ƒ|qÐ}ÒâA#ñhñ7°ñHO@zÒž %¡ºÜDá+QÖDÀ'>úO„œIÐ_Ò“ž„r&ƒW2äJCÞdÀ¤70ý¡ÇþÂWBŽþ Ôm§#/]Ôø @} €^€ÿèkÊ6úËLÐÍn&p3«©«Ï‚¬ xà€gžõc€Üàå€FdÍL.`r¡Ÿ\È– ˜|è)_ül>d,€À/NÊR>EàS:ÅhËÅà_ þƒ —AÀœÁÀ œÁHŒ´$C/ê°Ä¿´nd_×K¦ý«æanÍÁüçs0ÿ7ç_þs/ÿó.ÿç\þ™˜¥HÖG›Œ9ð­ n”£7ì²÷ü@Ÿ½Á·~ÐæúoØžI)~ SÐ6…-™ ? š–Ú¾h™Ag}ñÝß}‘×°æ°Ks䙃¯èX€ÚŒôc ¾–° Kð±Dy¬oºV c :Ö c \kÈØ°ýÛo7u76 kmІl@Û¸¶ÀµE¾-¾íP;ÀÛ—]5uKöÛå°‡lö(—x8ð<à9@G|;Æeu„üNàãûr·3à!¿3ôè š.è]ï]ðíŠoWðp¾«ˆ€ï†o7Ðw¾è»CNwÈìŽò{ L(£à=ïxOä{‚‡'äöDZ.è¦#ß p^àãºÞàã ¾Þ«ºó]”×åõžÏ~êR}QǾÀõ_Ðöm?ÀøÆ0~Hó‡|þàç™ü«©û @Zh€vä,Ïb!ʼ@ð ¿ ÐÎÂOt}£<Á+¸Á  œÈz!¨«¤… -i©øN…^RQ¦TÐM¯TÈ™ ^©à™ z©/MÄà5²§ágˆø½4”- eI^Ê“Zi •&þŸ4È”YÓ„Üø;§‹¿A;t³®ˆ¯@/ ðY€ÉLøg& ü³ÏÏP.†v =1”¡.²!6xf‹ ògCŽlÈŸ]E®#m%rç/8yÀÉÏ<à‚^!ÊRˆr¢…H/Dú@À Ä÷@|Ä÷À ý! 1yC@cdýÞ‡ ¬C »êÄ¿›ùká§…îoÖü°¶ÒÓwëv÷¹Ýý­ækµõÞîã\È«ûÒ›ùN!sw)|¥æ…ïºÙzæ¯4_u³±k÷që_W5Ÿ"úh1–ƒ^ñ|i½¡‡Þ¢ÿCÙû ]˜ ¬&¨´S”Ë í¨/ê°/Òú¢¾ÍQæÀ5G›±¬Eú3Ô“%p­ðm™­€czµœ5xôC~?´—äÙ&´R s ê3mÉ|ìEx¦ÎmÑztß å$~ƒO:Ú 3dIq¬ˆŸ¼bðNE9²À'ù¾h Å “þ¾(k®°1È š©+Kô%¢Ý£Üà_ l´‹Á£å*´…Ý@®lÑg.´U#þý«×ïþãÆ›ÅŒ·Öìn­Ùý»Æÿì¼×©÷ r^ ß êI,½ag½Ñ¦zCo½A»ìª¾û໾M`G&¨s“*êLa¦¥øÁ·h™–òÍPî¾~øÁwßuÔu˜ð>æ c:hÛø¶€–€µÄ·%øXBf+ä[¡íYŽ5èZÁ`­!c?Àöl?Ô ÛÖ2Ú ­Ø€–-¾m!“-hÙâÛe²¾ðí@Ï8öÛr¥ˆxôðí‡=Ôu9‚·cuaN ç;r·3x;ƒ·3ôç Z.èó\€ëZ.øvÅ·+Êé |×jêòÜðíÚn× es‡NÝ!¯;äuGy<@ÓzòŽÇ~ê=ã >žÙS¤Ag^(‡à¼À+„½@ß¼½!›7äñ1%èûÎåõ®Ò|!“/ð|Aßw?uµ~ ï?Àø‰X-Žº_Èå¹üQ–è)²€~ôY‘Ø@Ð D™žAà„´ ð‚ƒ o0d+@~0ðƒA34C Kh†€OÒBŠ´T|§‚^ª(Ê’*üdMESÁ7õ2uái€MCÙÓPˆ4È‘%bEG‚fúâ4”) zK½Ô}h¦¡Œià•¹Ò Ox¦ Š¿ÓA;p(Cúer Y€Ïñ(`²ªÉd.ë2¹ ø3”™7C9Êž žÙà—2d£ Ù(C6Ê yÐ[äÎNžˆÅ7ðò o!è‚^!ÊSˆr¢…"ô ¡»øˆïø(¾Ác ˆAgh ï!"ÎDÞgBþ!¢ß>Fü>]óãš×ü·ðÛšÏî鯵¹ž{ÿÊ÷Œ'5‹2ýå:‰?þWgÖzÎ¥(ü ðÚY6Í¿u÷mÚÙ¶žk$Â?õ\ >§»¿>Fó/¯_¢ùá?„ŸèyÞM‹EÇ-í@Aûêºé¶ÐuÖ›@'&h¦¨SÔ™)þ6ƒŽÍ€×WüàÛú2¾9Ú¦Úž%êßr?Uð­Ð¬Ð.­c ýßmÚy)ȳ]{´ठݼñí(ú=ÈéΨG¤¹ˆ˜4\×Q¸äÜTÀ»C^wÈ뎼T´S1®Båâw.èz ÛýGA~_´ñl|g£#?ò †ì©)|RQ¶‚=B F¹ Ęn+5‰à7ô ¢ï€|A§²f·´r!G.èàgà†~øúCÆPÈ0 °³ÇÈm¨ÐǤM¿©iä™61e›¾³QÖà=Càà÷ðŸœ9à«Ð#¦ÿúxøÖ\ê­ØøVlü?3·zk=ø¿µ—MìùU`û l_í+°}¶¯üMÎÃÀöؾÛW`û l_í+Ã嘶¯ÀöؾÛW`û l_í«wÎÁöؾÛW`û l_í+°}õŽؾÛW`û l_í+°}¶¯žƒí+°}¶¯ÀöؾÛW`ûêž;ؾÛW`û l_í+°}¶¯®qÃöؾÛW`û l_í+°}uζ¯ÀöؾÛW`û l_í«sP°}¶¯ÀöؾÛW`û l_oÁöؾÛW`û l_í+°}5n€í+°}¶¯ÀöؾÛW`ûê}|°}¶¯ÀöؾÛW`û l_½¶¯ÀöؾÛW`û l_í‹sr l_í+°}¶¯ÀöؾÛûؾÛW`û l_í+°}±ö¯Àîؼ›W`ó l^Í‹u6¯Àæؼ›W`ó l^Í‹y96¯Àæؼ›W`ó l^Í‹1¨›W`ó l^Í+°yáؼˆ¥T¿YÒËxGaš¬ïD_]AgëÕó~ÝÖAnÈ; Kä<`/¹OaL·{gü(Sï©–{FÐy|uešÜG¹[Že‹º­…ØÉó3hÞPËäXv<Öm<{¡Ûxv³¼¯Ð\žÜ,÷:ØÉûgVu»&ì/ÖE&ï Ñ÷T–ÈýmòšªwИËñídÚW©Þ[èFç5Ô» 7wÛ_9TŽs«»­‘L¦»Ôû ýèþõþÂýÝÆº“å˹Ç2MŽu«äX7Lî±,•{,/ÜäþB;¹V²®ÛÝ…y&pÜ[™&×IvwÛ[YÔíNš¶ngKåÝ…èNõL`©¼“æ†Ü[9†î8Pï-4ïvp·\'ñ»É…æÝÖI6wÛW&÷U–Êó€ rO¥AžÜÚí;yOádyÿÌnÚG©ÞQXDãYýŽÂ*y?aš<ó·CÞIh'ï™)¡³}êý2nrd©ñîAuod˜¼O¦Džç«–çùüä]23äƒû»Ý9'ï’ÙJgùÄØZ½w°—Üó1TÞ)³YÞ=Ø ÏӸɳ}“åýƒÕò^?y¶oŒ¼pG·½nòlßy¶f+ÝE!î¡wEhw͈ýâ^Bq6FÝÃQJûkÄ>±×FÜ‘#öcªçn&ËûgvÐÙ±÷FÜgs{íÇgpÔý”~òŒÍþ?ß5#æ×Õy‚=47 ®/¥Ñâ̤8÷£Þ[h'ï-,‘÷ÑTÑ™±·RÌ¿‹µ(uíi­9ýö®¾©*ëW6KE,{Å…‚E§4éF èÈR(mÓ=ݳ¶i›4i’¶A¶¢ˆu¥ BA(e_£c]0E-Öq›º EŠ¢ôûŸwo t`Æáûü¦ï÷ë/Í}÷,÷Ü÷^ιïžÿ¡\&)'3„çQ¾ÏiŒÃV‘¿#ø5V¾Ç„öbR\$½«’‰wTN¾ˆò (“p0(§ˆr$¼›:Ÿ½›~"÷G!ö¦89F"Åf{Q¬EëÒ…B¬MTs\ÚSD{†oƒ04$L µ¾¢Gìm ¹‹Ð-ü“¡[2l”ÜÊÃðNïTðMŹTœK]èÒ@—º4Œ7 z¥AfèÒq.òÒ!/té°a:hÊ0Æ è’~°_tÉMl” 9™à•‰þ™hËDÚThS¡Ÿ m*´eÁþYà“yY°aÆ—~Ùè—~Ùè— ~9hËA[ÚrЖƒ¶\´å:y¸‘ raŸ<Ø/ºæa y_ì’žù“ú|è“{åc\è[€¾Wl\ù…ò¡ôrà¼ãsàq€¾ôe 7B¶ýK`³ô+þ%°e ô*…MJÑ· ÿ[`; è-ÐÇ},ÐÑ ¹VÐY!× :k3ŸÊ!³t|w@ç ô©@Ÿ èVÞh3ÂfRÜ@ÅÊS,L±/äžõ~’bZŠg)v/)>¥8‘bBŠÁ[Šó¼8E«yc2ŠÅ¼ë9kQœE±ÅEÞ¨cÌãë\lãÃPüâ[0_R|Bñň#¤XâŠ|}ò÷½~>ùö^?ž|xòÝÉï&û\~6ì/ùÏ^ß™üfò™É_öúÂ^¿—|]òs½¾-®³üØÿ>*®»óú§˜ÓÓ>*æôŸüTÌç¿ôQÏçŸb®ÿÉ7~©´Vs‚÷¡÷%ô~„ð®¥}4j±‡¦š¯å†5á7ѺaGKØJñþ •ï+!|gÚ7"­ï×ñ<Ê!|4ÚëOyê´ÆG}|O„´žÝΗsF¶Š¼Œ8žk!áÓ)DŽ…K`Ò <€j‘SÑ.0èp)=<—‚ö‚JØÄÕ"_"DàÉ©ùOÊÍ'¬`ZR¢u¾ä`¾Ü“Š>©ô?hS³ò hßíe¥¼VÊ90ÀVüo€>èl€v5_æ)„¬2èX^Å +ÆXŠ1öbœ/£sà]†g¡6±ÃNvØÄŽqØ1>;xÙÁ3 üÒA—ÑÌ1™ É}&úgzø2” m*ðU¡M…¶,Œ+ |³À7 ×q!l–…1f£_6úe£_v3_¶ÊA[ÚrЖÓÌ—± ¡G.Ús1æ\è“ =ò0Ž<Œ!c/ÄçAÇbÈʇ¬|蓚Bú Àµ]†ÿ пr‹1ŽÒŸð,‚^E8_†¶â|̈v#bo#Úh,ߨ­c(¼ô-E?x–AO æÆ}, ±@G t´bž¬èo+ÉØÄc¯¯ œ¯}¾W‚ÿeGÅ3¿s´s´sô÷¿NÚ™;Ó¹VÚ¹VúÛ­•þ’/z©ÖLé7°š?ˤûU!æÕÅÏK{%Õ3­Uì™tŠ}“~§»ÚçuŒÀLkx¥2QÛeƒØû$j»¸|öþÔ,S?™V'öX‰Üój¾ÿGz¿'Þo7qwAÂM3v¨ïb9è-¢Æ‹ÊgyÇC•j¼x.ªìû€?mƒÀO {É«Å^ò6Ÿ¦¸Ý­·[!ö“»®j°È+sнA±¯\Åß­Kõ^ÅÞ «–ZˆÀR«ó©ùÓaP ÀÉ©ó©ù"ûËk|öÅtÀò5_|p¼•"½Iä¬Å‰}A"-Dä£;ŽZÇâ‘òÑkŽw»ÀQS ïf‘.Þ Ã;Ð'½Qì 9~w Ïž ±·ÜO䢫}rÑÛD—8‘ƒ¾Á7-¸fZ£¨ëâ/ðº•>xÝ-+-NÔriøhÁ"ǼæL~a¢IXܵgp¸¥Z-1>h.‘ççƒæøÛÍ;HàŸþö?ÔæS«E%òÉw«ON¹Ò‹»‘c¢Î—‹˜hµ"g®UÔm æ¹bRí–j^»…ð”K‰ðt%\"5Ï5§=ÆÒ>bµØ‡U'ö`5s ^Â>’òê¬7ÍÅsëï—°’‰r×¥ü:ŽŸ&åÐ5Ÿ‘Fû“¥}úM|o¾´—JÁ±œû—°$ï ã]-pÔ<<׎ö@K{—bŸ•‹ï¯¢üG±#L`Âø%¤Ó˜ßm"//Fà®9y}Ú¿B{I¤}Y1b_µÀ)öpjž# aG¨y=Úï/勆œÁH¦½þ§÷ö«y½F ¢]àCE­—À‹j57ˆÚ9fq­Èßly›Õã‰ðËgh“¶S­ÀuŠ8Ä8–å’ ¯>S'‘ò")÷‹r8)g’öÎRN$aDP-DÂb"< ©†M¨oØ&pƒe¢nMµÀ nØKJQ«¦Ià-…ˆ:†µ¼†!-•JøJFQ·°U`×r\©îLŒÀüÝ ð~3Iª/ƒv%h”->ØNQs}”m¼Þ ²ÿ¼(OˆZƒ°o²?ÇÏ \NÚ,å¹Aä ŽšB8Ÿ,ãµdÆ©9î&ÕLãØ˜„‹Iù­RNh»Ø»¯âX§1{Õ/“–Io æûi?±?¯å0¡Aà÷¶‹:ƒÕ<§—rP 'ø>~Âá œBغ} e¼VÕ,†}Ši2ô+„übð/ÝÈkHH¸›°oúÙ!3üìèo'Ÿrí K;ä¦gäÚ©ŸéÐ9ú§Ãé-|é8ãÊÀÜf€Gh21–Lè‘ y™hËD?ÚThSA¶ m*´eAÿ,è²0Y˜»lôËF¿lôËF¿lðËA[ÚrЖƒ¶´å¢-m¹W.ôɅιÐ1úåaÌyStÌÏ|ÈÉGß|è“qçcLùè[€¾°Gl\ù…è_ˆù,Bÿ"èU„þEè_„ö"ð1B¦|Œi#hŒG æ ýK0ŽذÄ׿KÑ· ÿ—ÁnØÈ½, ³@W ä[Ag…|+è¬àe…ìrÈ.ßY>èS+À»¢•cMŸ—ƒ~;¤ã?ýžä¿éIÇ÷#ÿêÝHç{‘Î÷"¿·÷"ïD.í;‘ßñûéùYÃï9麊zP¼pBÔm7r¼6)¯7æLÝv©ŠJäõ¶ˆºí ãøï1WVªÃ"ê¡4‰œJ›Ð$ꡨy ÎÓy½JQ¥YÔáT‰ºílY'¯‰"Õ⌵Û7øÔE1ŠÚím#Æ(ꢴˆº(1ç¨ß'0f]c6HäùÖðœ8 ‹Aæƒ1Û&ê¥Usœ©6JÏA=«†»Ñ§&gÀ‰iôÁ‰±ŠÚ(QE!rèy/†ð0 #EªË#ò}ky wúù–j£X6~›¨Í©u9[¾¬Ê'ÏןcBœ®Ýî'ò|U_¶é >„SËñeOcžl‹ÈóUø`˶ ¬¥Èñmõ8eg×C¡ší䜮۾AäøúóZßTëqd¯¹!áÊ LÙj^’j¡Hù2Q«CÉëŠ{ó}ÏÊóµòü^ ÕOÔ õÆï­ c»I5Æýj0¯#)ÕfSñ:l)JQ÷»‰×x$|R çÞOà”ÊV©ZÔ}¬5Á]¼.8ÕÜ |Êi$lÂ|ñææªU"?·æLnnä$@Nh@“šDÈI„œDèž}!+²!+öMÄ5•y‰ Mm"tLlçîQh“ 3 ó˜Ú$Ъéè“ 7<’À# zÐê1N=äëa;=즇|=äëA«‡z讇|=t×C¾² ] 9&œ7Àfð1€| ÐÃ= ÐÁ>ð1P?èaø˜ÀÇ>fð1c fð0ƒŸã0ƒÞ z3t1ƒ‡ã0ÃÆfØÑ ]Ìàa3x˜ÁÆó6œ·á¼ çm8oÃyÙ çía‡ ;dØ¡§zÚ¡g%tª„N•°M%lSÙÎÝÛ*ئŠ"ä/Òá[(N9ßÚ4Å ´&íü«5ès­?{㇎øC'PŒàx}~¯ï]söõÛÏ•î»þLþøùüj¯?¹;íK{ýh¯ÿ|._™üäsùÈ^ÿö–|c¯OLþ°×ïí¸×Øw Ù×ÏÅ\Iþ­×—í¸†|.ÿ”üRòI}ýQòE½~'æ\ò;;úœ¾kƾ>¦¯éõ-}׊½kÄä3vô o÷ñ U~g¯ý:øif_l¦ðµhnè¡ÕÌõ–jcº`Ü]¢Æ8®Çnuß)jˆWs¼' ß(j«z|jµµœ{…À¶·Š:àíã‰pí%L{+¯ÿ-aÑÁþ}•¼ÖwÔŽÃ=0XÔ0VðšM„ñA5TwtL+ÇÂ&\ Â'ü7ÂI#ü7Âwòbvþ÷øjÝtBà•֞ɕ'<ŒØVž'?M!pÇ”¼^ aQÝdÂŒ úU„A&aGÔùÔÆÁù‚ŽýJx¯T¯« •×R¥:©T êúœàõ`¤z¥ U㜚ÎóúSjØI Ô˜µ‚c–JùùôtjŒ[ Ùj0K¡ßDŒA >)øž}Ô°·:ª¡‡òK‰ú•â¾+¥ó_Š>È-…Î¥è[оå /…Î¥8_Œ¿rÊÁ¿´å°ƒ}ʉ|*¡k9ý~@7-ôÔÒ9¢Ú*1t×áÏ„?ú˜`?Æm"z|·AÿJznâÓ™•8_IŸ©ƒØÿ浞Îužó×%ÿ¿¶Îó{^ãù-÷¼þo®ï¨Ä3÷ŠtM(…œ&QïVq·~jºÂö]kD½Û`Qïψn¸Wºùà¶5ŠÚAq¢vP»¨w[#0ˆÐ×_%pÛš^¿•׺•0‰Böo­¨w,ê5l¢@å_-ðüýDí jŽ,Õ Ä5ØÛÉëØ÷>!ðÛ¬>À QóßÑ¿OÇ5"\M©~ôè ¹}¡GßfßÃqÿ%üßvQórû×pÌM©&¡Œ×XàâòCDÝ[—¨{ šAqË­ÉËÍÉëJµoÑxu¯eEßÛž›úLíÛñ°Ç5 ›Š1_‹gÁÔ^?ˆjxJµoyíC©ö­UÔ½móÁqŸë=[i¾Á\ ñp<ß`ŽéKn§q€¢vÇícðJn¢Þm5¯E Õ ømÐi¸GÔºéP3:ŽÀØFÀö#@;vÑÊñÛ¤:è;²NÔº=!êÜ‚gH¨½$0úcD=œ‹6r<` Ã-ŽcKøm°KAÇë/êܠߨ^ïf|+¯‡"ê²âü-!¼>k<ô‹‡â¡c|ƒ¨YØÌk¥Æ· L}QÃý§£ÿt£¨Ÿ yÓA34Ó=¼& áW‘‹"aMÉÎß›{$@Nä$´s—&4‰“9‰Ð;ãK„¬DÈJ¡OÈKĵ”y‰ Mm"tL„ŽIÐ1 ´I™„9L}è“@—ú$ÈN‚½“@_Š¿$È/Ÿ$ðIŸ$ðI%øh`%l¤/%tQ‚—¼4à«ÄX”‹ü”‹<•৯ò£é“|uðOÁuž»¤À.ô×༆|l´kЮ¡övî‚i1v-äj!O YZÈÐb¼Zð×Â^Zð5a|6è§…¾ZÐj۹릭çt ÕÁÞ:èh‚î:ðÐA®Ž|mè¤ÃXu Õa¬:ŒU^zŒS¹zÐê1N=dëa3=l¦‡|=äëA«‡Î&ðÑCo=dëÛ¹»h€lx˜ Ÿ:ÀÇ>è`€|äÀÇ>ð1Ð8ˆø˜ÀÇÔÎÝJ3ø˜Ao†ÞfØÙŒq˜Á£c7ƒ‡²Íô?ìg½ôfЛ۹KjÃyÎÛpÞ†ó6œ·á¼­»§v𷃿úUB;t´Sl}*a—JØ¥v©„]ª`—*Œ©Šü~òßè oLr¾õŠ=|÷ž+Î8WŒáWøÆ7PLàtÜ[èõó}ý{¯?O¾»×oï诓NþyÇõ¯?îõŽ~8½oõæ{ñ¦½{ Ï·¿Ð»þãõ§;î/$ß¹£¿L~²ï:÷]'ù¿×z:®íx}ZògÏ·ÎC~+ù«^ÕëŸz}SòI½þhG_Ôûž±cžúù|Íó­õt|/x>_Ñëúúƒ^?Ðw ÊïÜ{µ~ÿ¼6dõñá|óÞ½kDj?^oQÅ}/ ‡vèªuqOtÃ|vkµüy„Ë»öòQûX-j'¢O'¯Üó¯,Õ<°Šþ¼fáÒ6Š5xVô}”KÔ&mÿZÎS}rªG3ω¼îxxD×ÕÕ¼žÛ`#¯“L5 ©> áê_ÈkV8ÝV„N8Œ„»8ú1ŠúåV^+ ª•cƒ8Õ"&,pª›C·àø:Ž»8¾côKXý1¼†á­JXöj^+‡pH©®aNkàõ•§5ñúÊT#ˆBúø@^O=cŠáõˆã­¢® ú«EÍr•¨k³×¶Qcìê8Žé(aåC5ýa> ;º'B?5䪡s xª‰žw¥Ð/ |ÕС4¥8W ºRذ2Rœ|y@ƒ>ŒI¥à§‘ñGŠz–SèPºr褡ßHØQ^È4a^Lô»Ëaø”ƒ‡v Ùp®m•ÐQýmàm£ß0èjÂwèLào_ô5· ¼MÐÑFçHñBŸJÐV¢Í†~6ô«Äg%>mбý+¡£>Á[Û(a)æšæwvlåOåù‰:ÆLÞX‰b¤r¿31‘SÄBÞ8ÈyãolC1ÍF~¿Ql"½onû:ãÖ–Òg+„ÇŽÖ þÎXz_¬±D‹À~Vùä}¡_o…X‹uм®8^GJŠ D<ÐÌñ…úò¼-z—K÷Þ€vQ'´šcXQí-©WgKu,ÆAþ`÷Ý XZÖjæ5Ÿ¤šhgä5Áé¥å.ª1A¸;”wÞÄ÷ z}RE¯©H9S„åC@Ò~BŒi’‡çÒ„7ó{HÂÉ âï%ŸQÁßR½GzHþ-ýTÒ;9ªUE¹3„ ÄÔ{È‹¹@X7”·AØ ´o‘ê3Rî…´0ãà~ åuPžEö ŽQ@{õrOð{ˆÞ’F?Ãô~íô{¬±ß¼qwJbž•ûš”S‘Âß%Ò»9Â& ÌòéhŸaÑP>íÓ¤{—ò%s†–òSÖz«Ð¯ªšc@TAvUßïXµA¼ç]UÇ*¨mxTµðuã*jkçû÷D:+þœühä;!ÇÂó4œ°eyÇÖ!Ì'ôr*Ï`ì8 Ï yN#ߟbä:“oI:lÚ£Iriÿ!íÓ$½JÉ/káïìh¡„K½þ\/¯'ñ‡íœ5{,z”*8žá5”5qÌŸl5Ÿ£²®³„Gaå<Ë9° ?Ð=áÀqºNr¯á¼PRï+ñë'rpîúǃí°îƒ9^”w:Híøæ–yèLoè­fBòúÉã÷V—!ÏŸ¤ùzÿßâƒÃ£0·c«tcmš½¦rl¥Áf/¶”…VØLÁQÑa4÷:]ï¡ôÂä²ÈY˜ÃVQVz΋&`RâćFk2Œ..+´ü¢¬èˆóËúrÂ|dœY(û£.zpa§¥œ ¿BæÅ4쌘3Béùþ+¤Žùw¥JrHìÿö%Ûyü†Ç=ÿÑ%ì"dÐC~LTÔùžÿttxþËÆDñ ŽúÍG{Žã¿üùÁóï08¡ZÝð믄 Ÿÿðh¸ó Ž žÉû»°§ÀEÌ¿<"¼sþ/ÅqQó îÿ¨Îçÿ%9.hþ e[±á‚b?:~)þ‹Šè8ÿò¨hYgüw)¹, @_l   :ÀèpXícÃ(R­«]fÓXFô"‡ñý*8Â1í¡²1¡²˜4¹lldÔØ(ù興pYDL¶¤‹,Àf5ÖV›ôg1…².R*‹”ËBcuQ¡®¡¹Fnˆèåw› ¾B#äi²H+3:V-ËÐk"£Â#Â#ã´Qº(™¡PgˆÔGkôúBM¬ãt’£€œgÐ{`\­ûÙÒžß7íaC_ÈSÌ(n`ew»Ç«rØe/¼û|yŠ‹8õè ‘/Ü!Ñ©A÷î÷c÷Ò'{•o¹›64jñÆ‘ ¬:pw­î³,v¿ÒÔ=!ÞÅÒN~ÐgÀ¦™]5è>z%ô“# –º÷í¾zÓö=ìöE_éÿñjÖk÷î][ïT±åšÇ·|0ÝÅ^[¸ õ“O8]èÆ¿ÿñÓmúåî¡[åmó={Xøäwfõ}h5›½xœiXD&[·,hÏ? oÐ3ë7Èéš@·ä›Gï=:a¥»¢öïwM=¾‡-¹kNüÚ‰«ÙàCÄ=k2ØÉä7–ÏŒs±7ÓOD{Hï¾{ÙQƒe~ÃOõ슀»Â‘Éæ~ÓhÙ‘èbÏiÞ}loNçg8¤×¹'|ò×wûK·MX¶—]™ÒÞß¶¶žé´_—›7¨X·à/>MǼ¼~°"úãÕœ.t™Ÿí:ììþ˜û¹ÛﻬoÂ^öò¤5¹y=Û2~ž+uK6{&¨ëg1s\,bCtLŸS| Ëq¥ jÜ}ÏK¯5-,ÙËÆÝ~²~s½:qÚS3p=¼Ù¼íO¸Ø8ûU=?ø‘ÛE º××ÎzSê÷®ØËÌßËrúÔÞ—ýÍJ¶íÙÞo.Ñç± ¿]y;ïs±9 ølÀÀY|þ@·$qx]IáZ÷ñ°;_™þØ^6³ûS©;'¯d¡ŸýôD` >;{Ò\ó÷ò¦ÛêÄü.²8µb¡i;rºyã]Ïïe7¯9¹æõ2çM­õG’rØœëÿµÍÅÞvlú¿æãkÝ“ßôú°Î°ÞýE··Û_ßËâ×ÖjBe{'ÍY¸SÅF§>Üze±‹-±¶áÝxN× ºÇžÔ$’7¸<®ùöÉÏ÷²¹7¼é£,/èè¤Ï礳',#û|Xàb÷$]¤Þ$æ¯ðâà0Ù[cdÝS6}yò±nûXþÞÖØµûV°ïÞÝ“ÂNµ g‡2]ìÚ×û-u*Äüî»n÷~þÅe›Ü]t³»>|Í>6ØÝ>mñ‘lòÒ½O<¿9‘í;Üãöë2\ìXDÑì¿Ê9tw šÝ½ø•MîÚšQ‹&†ícCÛ+íëoz”½zsà-£žÆ¢¯»ì½?付ßÇó{>žÅéÔ “TѼϺÙ=í¶§—ENÞÇnozeEtÍ£l™cÉ]ow‹c½§¡ä%.ÖOóÒë78]5èŽ-0ß~Í÷ì¥Gþr0kKüô3|û(«üá¶¼5ÊÉìÀ×ëüÔq¶ž  ËìŽRõæ-îíŸ ‹}ìXÀ«·¬/YÉõ1f2[õí’•ýfºXtîÇõÛ¾pòùnVÝNÓ¸­îñ}v͈™·÷õÍ5ï­dÝåža SØÖIéœçbU}ûü4ÉÃéZA·hæ³ÏêžØêþ¤Ë²WW,ÚÇæÎ{yA¿[V±{wœèï÷ÑTÖejËÓ׸ØÐ-¯ô}ëNçWtHQ¬p2¼Í}µêÕã·¯ÝÇ^ܲ9¯ìþUì8fã‡[§³›ÿøÜðý¸®¯<¹aÁ©zN ºÍyý^|×6÷KSW°u;ö1{Õ‘ý^^ÅNš“j[¾Hd{žû‡‰÷¸Ø‚Ú¯b‰œNºžå‡­úÛ6wóGÛ_ÙÇ’O˜®»öØ*v´`ñ_ûÛ•¬Çè…Q7Üåb?¿µêÎSU|þ@ç#>²ßv÷ꫯyöí}Ìôí·«\^Ïþ6¾zÒœSJQ:*øsÜG§^˜ûõ¡]œ®tc¬—ºdÊv÷š”cÆk?ÝǶ>wýqϵõ¬×‘Á…ÆñÉLù¡ʨJØsÁÆÐŠZN×:ÙÛN×ËeÛÝû|ÏûذÆw™:¬žÅ¾>ÿó‘:›ü§ëR« ¼úÓU´t»;µGoãÑ€ýìOU“s™XÏä3ß®|gp2;’||î”4‹\µª×C‹9]+èþÜ'©2âñíîSߵf?Ûëžþׇ³ÏŒ+àªyßá>Û¥1ëy•˜7ã!Åú£OuÝvó~öuÐ}­Ãmõ,t{®åÑÊÓ÷éï?ØýîÛÄý†þ£&|Zùí#ÛÝ[6wÙ4~?ûdùW³4Ö3÷WÁ·LêÌÖç«!'OÇ—&‹û t‡oüùa÷ÌíîŒUov™9}?³}ž^ÑPWÏÆ}ôಲ–ÐP3©×8{;¯aMS®ŸtófÖ3sÚvwÍWÝC‡«÷³gÊ¦ÍØ:¿žMéretÅõé¬ë#Ç¿ŒíbQüÔËRÉç t?Lhžñóíîð[îX`ÝÏ âçÜ1ìÖzöþÂwÃVoQ±ž5Sk§¸Ø{}¯ÈÿGŸ/Ðùïî2ÂúÙ6wSÏ»ýcæïgëŒ (}a2¿zVÏ¹ìØ€ï]儞‹yã˹¼&Ð1Çm³÷¬Üæþ*Gø‡Ëö³þ‰×Þ7=`[÷ÆíÎg‘õþ‡Æ¯t±'d ˆy]Åê7F$nsÿÓáÜû6ïg+Ý$x%[ÑýëRz©YK\ßù77¸˜ì¦ý<żRœ2}üì¨Ã[ÝO¾½èÓqîýlÚ‰%) Ô²q6^¶º€¥]Ðÿ~Üo×m-™:%VÜo kÿqLX⬭îG¿ØÿðkûYô‡]['._ÁØO¯=vëî<†ùÛ–Y]ì¶ÞÓêŠäz*@÷âÉá™=¶ºWIºæÍ£ûÙ„7o_hÿv9#oi…Kœûí “ðûÚ8þ­zÙÄý:›eÎKïÚâÎn88ôe¿L–©Ô¿œÙZß´P±à ¹±3‹\lØ;¾y}®˜?Ð-é{gú7_mv[F¹ç lÉ4ç„íl9ë1×]š•ÎR?;5y\™‹]Fš˜?ÐÝð£âUíf÷M‡‹·Ž>ÀÚ÷$©Û‡,g?œûÝCƒSÙä5!#,.–uõƒ³ò;øü®6á«æ¼¼É}xE—å«&`¿î“/ƒ<º ÿœÌ*ö}ü¨ò†<ö·ëvíçt­ {w=I6¹Cžþ|æØCk¾èÿ|øröÃÊçòÂ’ÙúJz ¸˜[çÓÙdÅÝ7Ä­u±Ýãî O{™Ó5€îøœ÷æ|¯_ë>º* ñÈ÷ØÀù_ÝúÐ'ulÖëÿxû²¿§³ä¦J™vµ‹í<õŒ±Ù)ætîÝûÛ Ö¸¼#áª+íÓs7Õ±n¶{o¸jb[D?uxNÜ8ä¹+Äüî‹ýxÜmHÝÖ>z‡¹ò¢nÚSZÇØµÃ'~g[ðêœñ‹\Ì ¯¸ÓÎç¯ôÂ:ah—Þ|ÌýbØ7_¯æaõ gY® ­c£zÄEΖÁ¸>.ÖT2{){3§kΪ\önbÎ w–ã÷ùË4¨jáóº!ï¼0çšÅîÇ®lê1±ÆÃÆ}èª]/=ÂÜžóÃìÏ…7‡}ë³ioU{¿Ë­|þ@×åõâƒ=v,t¯)£;ÊÃw§‰\Âl?À£fÕO+`R‹ï¶inànN§]Õw~—EÜïÞ5,èòe–ñ—üA‘=ÌÆ÷ü±²¯†.ZQ)ßçb]'|öញ||Õ 3|ô—ÏÍ^à~åä{ž%«‹Øþ”'WÎÿöþª©vÛ¾±cÇŽØbÇŽëŒ(Š ˆJ‰ VìØ±GbA±`b =ô€Êz(jDAìvìØ¿ÿÊZzïû<çããùöyÏóœ{ñãÿ›•5çµV×,t¡kwjyöÇç¬(zùð¡vüûb´°T/vRûžF» Ïè(åKp«:{hÿœ©õο‘Ð׺p ®¢®ç†u|š¹ˆ?n‹JÅç‹ï ÓPÃ9Ö×GÔQÛ¡·,§÷ØM+箓ó~ Ü7åbÆ+­Ø]éæ‹»Ây‡œá²¢A õ’ÛŒ?¤#³^›çöÚE¿jöt¾.u¥p«*1 ž¨hÂ~ßQ·²ùœ¹Öý;eºO:@®õ§uÚó®a»®'2{ï¤5›6®>Ë•ú~½êwGE—.ŸZÕUÏ¿N)r™ug»ÈC”öhôÞ„­:º1ô‡sð@šöóÅø)¥˜»ýÀU•ÈŒq%¿/2ä2£%Ö¯“Ó©—=¯ÓÑ·]FÜrÝA¦ûçöK–Ù€¼¯—2UtºOV˪|NܘJ§¤ÙQEÖ™® —èèái‹7KÎn§/¬}WÇ.d¶cȽGI*ò=º³k†Z8~ÈÕ]\¬_ït‚pñœŸs Ï>o}SE½&I+ãs"ä$†Z§¨YC‹” ƒt´²ùÏkuËÉPÞn;QËò/ž¾ø¼ôÛ¼ëÅý|NŒ\ó°é]ø†ÐB‘Ç×.:š™óbö ¿mäÛµÿZE žì2<ÿ=Ö–£cÇ(ùœ¹Û“j¬tš2zîh]aª£R¶ãø`ÅVâºÜ¤‘nô¼­ÙÝ/Utµu¯Ó#½øã‡Ü“¨Ž¶œ8#¬ku´º^‹Þãü(=#~ñëè©41$`¦ón‡˜Ã?ä¶æ:¦)OŸ%U•§;Í_]¡¸·çG9lùSß[­²­ñâ—Š&²Únqøœ¹Nøi—ÂÎѵ ·Ê½B}ö.h½m“°îô¤&‘wjÔŠ¡ñãšMÿ8†zäz‹OÕl£¤¼ÒŸí¯_¡G—º•Õ`Hßè…ã—žôá‹9NÍ2’´TØœÏy—Š£ ÜV´ ¥WG¿…笿B/ÍÙ YC7×¾µ^{AJÇzÔ¼õè÷u)ÿ:EÈõœÒäA§0’¿=â²Âù Õö®´p[EÛš›M¨ºd*m7þÕΫ}oí˜]5{ó91r/³8NÆ ®Ò¾×êœÿëÈôÌåtvÆå¾Ÿ:ºQ¥3Á£Û檈»*ºÑÏI‘[Þ±rÉ#Èl¡SÛ5®r·“í•K…ëW ˆ*ªóµ}*³ùã‡Ü»˜óâ±›")!ýÃÅ­úËtÙz×äÔ˜%ԾǤöÇœ©ÏªmÛ|PQãán5j¬Á?äz´Û‘ÛB%œ§—©–>-¬(Ö›^»E6:ôЉ¼×nÎ\ùYE[£F)¾ÖðärŸ§@N®t ŽQ©÷ÆZ.B®ÚO»ÈI³ØŠˆIkÎ’÷±T4ü¥_åÄ-Âù‡ÜÃ…dÍŒªV×¾@Xx±´Išü:nôaâhw#³úuùSYÝÏ|N¿NL “üÙõÆö( Åµ‡ô¨³ˆÝz’ýÁ·ãTj1.£¼Aïzßrï›IÏù÷ÅhÖuZ Qf&Ð…§š¥¾u (gê~ëêS—²9ï-ë<‰–Òà }áç=ÉÚ½’?"ä.Eœ‘ÅÿJ Œëk_äÓôÅ+¥­ZÉ+OoØó‘' ™\v!»g U®“rqmþó"FÎññÚÑíú%Òâ·å'ªäçÓ‹Ií?ôý¸†~˾qbÊtºûÆ¡qY£º7¯áñY|NŠw÷(À+‘Ã^uª8›O¢qâÊýçodçöÌ»íIùÜéþV%¬Óø×)C'×ázÇéÈ¡Œ×C¶æÓõÅMÖÄ]ØÄL ()¹’|û#Ö‘¦1Œ²´üû¢@n§×³¯ó…¾‘OÏ'—8ÿpÛÂÜ^yöj¬ò ·jeÓY*rq­a·áø!Wé–sÜåŠDš·Ðìöüù´êpõçƒvû2ÃòØÝ•ZåÔ?0}…ÿý…ã‡ÜlߤmEI´«S³ÒVù$Î8Úßgš›e¸aäBGkmr/U‘­GŸÝ{_ò9£å¥âMWê·÷“DuT{ì=¾äQëa7ÍG¿õcÛGϺKÎävÅd÷”O*êäýbщç|Ý!÷dµ÷ªâEIÔkîÎâ‰Ey4láȵá3·²=­¸¶ý€½ÚÒ½v§Ÿ?îz䪿wѺ7WÓýæ\ƒÏ%‡Ñgwõ«ìÏ"¿åX-7v§Üì-=V~SÑ7·ÊåB…óoE©ØÒ†»¬¦£>Ìq.—ZUëcFídJ.ÞÝçNCZzœ9þ÷œûz¤©pþ!7ÿ¸ºe´XMs<—fL‹}+õߟ«ÚÖ Ýɺœx‰÷“¿Ã×]1rÇß•–8ª‰¿/˜K]Ÿ]h[–±›å./ûpá¯õKëoSº[¿úr[ËL,z©©÷[×·us)é[í÷×oîagþ¼:p®+u›øéàÒTé}­ª¶´âs2äøû…j ?È}¡¤¥qÛÞ7+ `?lŸfÄUB³2«)&¦¨hã¶Sâ“Zaý‚\nÕïfc7«©ñ¡‡Mä'´ä9¼þéšV¿ø× ‚C.4 "´{­ó*ú]7 ǹçû–윹GMÕ¸¯ùfkiÛöê]|jíe¿ïUOö·°NVýYWŽréi÷2Ž©Iì5lÝas-UL4¹í^öû~]ÄÎÑãUôLmvóÀAáü[Y*~8Á/EMm«Mhëò<‡RD[ÏÞ¾—-i»G?:Ï™>Ì“:„àºc\šÙœk|N„܇¦õß R©Ép™‘C ûÖçU·ö2þ{7 õÞßèUW\{ÅnWóÃÇnM¡¥_×¤ÎÆï×å@¹ïÔBÿCînú÷jªóxÔùZ]r¨¼oϖƹûmúó—Ü„u™ŠêâS_ëdȽºö6hSžšî¤=7øQ6r.µ{·0¹´}ܹh¸-þл úŸŸO‡©'.óÇ]ÜÏ#ë_US÷2OfÓ ßWãÛìggšÜw¨XàAž¸íÇuUû­­²O3áú¹¬ÊÓ®FÞPSñ€¬‡§\ñó\”·åîg½/ßÇ'Ëøû†¸ûœ;Qf.¬_{pÝ*÷‘šZŽÙ4ÿÇÃewm°+ ¾ô<Ós -ºgÙ¥`­ŠzfÑ ë—U¥âÏ.{¾9¿PÓ¬ê*+ò²¨ÎÊŒDëÃØïû.ƒ:Ò wVÑ «¨ëq"áüCθmü£·øývÅ4šº9‹öÌ^ÙùYøæ¯ßØífÌDÒT©‚¢¢é7 Ú†ú ǹ+[¿÷ÝR¡&{]§mgÑ— æ{·`ü÷ì㨠ÎÊ7Ž*êc¸!&?äôã“^šüRÓA+Ñ!Ëw™>°~ÁÊ®X*w»£b4%ÞîÙïÆ\¬_œæ$hZ Ç9ÃSÕ’én|ósmÃ2iÿž@Ÿ^‘ûÙè‘Ù!Žé#iÔV§ {W«¨aɱ-s¬„ó¹¤u:«“LÜ· ¦gR³”†›žãøµ¿0̸Ò+ Ù{¥Û‰5*Ú~mlbÊ1áüCN;’ëÜÉ”=WÞõ{óLr²®ì’±'Ì™e¹q$…<×OX¨¢ßëÃñCîf­#%Í“©ÞMƒ_Í 1V‰ÛLk²*qV{_?EÕ»>¼?Ç]E4‰{ÅÂùçS*~vÕq“6Éäd¿Ööòö 2}tÍøTà>¶üUí2¿!c„ûÿ_׉3<®Ð)™dÆ©nA“管}ö±¨±I—¦¥¶Ï‡£E¨(hb÷¤x3áø!7!ïôÖF4ªéÜÒVú½Œÿßí(²Ïd×v*êhxÐC8nø÷iFéÛöH¦6ŠoKgŸO§Ñ;?³/ãû¡™…¬pì?}–[ö¬æs2ä s¸íÉ´êfô»«Óéžåå†5íö ëq;zºtKp½n**¼šúZ8nÈõê¥>8™ o—e:yß²ýés;sz[pQîXÚÆ]ÆÔÅq3¬øó@ƒ\è§g>âdÚÜr¿ÅµOi”³É%ÿøáßõv ­n3fÙÌÇÑÂs<Ây‡ÜŒä3Eê4ºséÆÊÌÜpãgÍ5Z¼©jV4mnõ«Úf#á~ËêR±÷ÔcóÆ$Ó‰N}ï9¯I£‹«»îaÉÍižnAvU(Ì“ES‹1YçŒøŸ#B®FçSÓÜ(ès/×½¥»Ø¯5~Õ>WNF+ªT»—Mܪ"s˜°ÎÄ¿ßÔÑ%¦b|2ys_ûVM£Ñm‚lê­ßÉøçW†£ÑÎ9:]´ð½«Ðçóô¿;<¼@Co¾TÏ_ßÌ_X?§Îܲ,'Zø¾Nèoø÷ÜUË!ÇdŠd~í×ЦÀ…úz×·³JýDö²"‡f]öûEGÓ¦j³ô2þõ)ëlx`?'ÿÃÙõ—åÎã‚—ïMަû&·m½]¨‹ø÷ ûÕËÛw×Pñԯ㛙ʙóôâKíhšÎªòèCÑ”¹Ù´ìÿïõø÷Öïš~ùÈèGS–{ö±*†f¨ÙµqŸ‡F“ÉÐÔ‹óuü¿7ZS*þÑ£RÅ9ü†òÈèÓ*·Ýßgoc\õ+¿í@/ZV‰½q>šv]™ùkh]Ḡ׸†îÍ·ŒzU„(ûnccO¼øæà@ûoö¿7ÿÜÿïÅø÷¯¸Ç*ì“©K‡ÃECî¤ÒÆsFw}º•EDr H{_¬Üæµ<šŠž‡‡WNàsRäBjp7’©ªáÁƒTÊ´cDSÉVÆß—KþÞg™FÓÏ·ÜÖüï%CnŸxËøãø¼uÝtóiïTjwdlj3+üØûÜ »QTp4#áVFµË)îë;C8>ÈmZ­Ý_D2yZÞ°ùð"¹íß0G4ÄWXÿ£qÜmç£QdøXÔç?äpr¡‚%“aÙ²ÿ"]>£{y~3ãï×  m§›Úv:õ·ë)=rMvÚ>¯Ú/™öª>-hÜEò.×oÎi¸‰EÍ1Îé¾¶}ñ}ya×¥(²Ò] ;¸œÏ­-WËÚÓ{êË/î„»Hþïï<=½Í$ÿ36©}È+zx믯£ˆ¿(ô/ä0Ó¸‰’©ß©øéhà þßÖ2þþPйÏ-¢hk›cZ… ×ßÈ­{¬ÉÄFÉdµNš³qӲךÎ}?Ö‡…ö²ÒÿÐj×ÿÕô#Ï¢ˆ?øœ9kny‹þu$¡qÀ'º@¾ gßúy`ã®î÷§Šÿ‡¾“Þ„û$\¿!×ÐpcLMï^k% ž§•òª[.cž}œ,º†Ž¤O#¨¼÷EQÞ¢ÙBð9rú±µ9•¨ÉíÍÀ[K2ÎÓܯ{s|-ašvlƒ¼îhz ÞyÔÌ+Š<˜+>§AnæÂ¾3ÕÄ}K¼M~ž¦Í½ÒâîçEŒÿþ` •éý"Ë!êoë$=r¸èqئ¦Ò€º§ýÇŸ'Ï‚·³~¤ÍgÓ®œ×s  MÞ[kFíQÄ÷]>g´ë2wîÉ 5¹KÞkÕü<]V>ÎkŸãÏç14‚k-¢hÁÁüÑO¿ ǹ#âþ¦+Õ”%9;­Ý½Rg·Ÿ:rãŸGM©Œ¾•]¤Ó;ÕFýl…û—È}ÿ1=q¦“š>hr¤Y¡)äÜúu¿‡lWST|ï5oÚMÃâ÷8ѵOÑýºD’ã#“%˺ò9r£­ÜP‰»«8ÏŸó³-ïN²¡ìoK¾=t¢À›Û")vF{“èÂ÷È­ [wÅÆ7‘Fe^ i¤¦™Ži7ûŒ®ciöæîu"éT§¯ëö7úro–œ­y{B"ùÈI¯N¢VÏæX÷ú‰ãÝú¦{ÿ+©`] ZbI“ F  ǹ¹ƒt#¶H¤B¥&w€w’ð}‘5u¨Ï­ÔÆSÇgGrÞŽ ßß{Žß†R±üÖ€›õ Ä>|zzªm-ù!õ3ò›@Šq!¹Y6tM~x}ë «óp‘I¤pÿ9þº->žìO? éǵX×jÅÎÂó £hÛbÏ»>«"( ¯¦Æs¡~"7ïÔ•1Í—$P7»å§àýI Íkq~Õºõ¨|lg+êÇŽT©º,‚ôÝR>i-ÔO䮞Ûj@MìUѯs¯DZæº*èèL:w¥ž:*h]tº|d_qOMJëä¬{.9ó!žzÄ/;RíVÅzŒvn/Kû|VvÙoE÷›v/ªv!‚æÔ6ŽÜÖKXG"g»~Ê­ÐäxšôÙòšo=?$OºÌ ëéQÔäGðáÎ7"èm}·5ýŠøœ¹S‰uCD>ñÄ]…4ê’@ÕºYõÎð¦«>0wÿÑÄu³†%tã~CÙF[þýÔ#WÛpÁO»º¨¿O–óí|Ž{-%•ó«#Å6dÑ`ññç#hMvê̇iÂñÛX*n¾×<>â[I\ªÍëŸ[qa[ùuí2:ü®-J–-e­ÞiàŽšosgƳ‘Âù‡œ´J»Ìqq´pùå KZÆSd•b©®b9qOUL¼eG†Ç´¦Eû’%Ž‹N ëJäøçÁâhóÞCýc4ø¹\Ùl¼’=~Ñçû9;Ê^\«[ÇZ¢­^˜uVXW"—0!gFõ®q4n‚¤´Ò¬8Ê¿ârëýèU^çéA±ïò¥tü耇ýF ëKäên_Û}}Y,y.h«Q3ŽLß6=xÄGxîφš‹®VÝ!‰ ­§ÅF¿áú¹QýR¶”Ž¥•ÛŸGnŠŒ¥Û[Š/LXC¹9¯+w¬6š —-"hÜ.Ý•ë/„ëäüÏÆ¦MŒ¥5÷+nõµ‹%cuñ¥µ“ÖÑïë7–ÒÕÁ?-œÂ;ÄÖí.\ çóîYL*±d}m[µNWфآÇûe*º]©Ï•3|…çþlhlp÷uë#Ãé‰ÇGÙj­°~AÎpûnЏ³}:®×N¨uÂ*Ê—N‡Þ¾ébO-_”b%NÊçw¨™.?äz¯YÜ­Jx® šê6åžhõ¥ÇÖô7l&Ñ—›Ú“óÂ…ïÉ„ú‰œákhŸhÊóz²ÒÇ(jxÍæ¹}ilý‘á}iEtàרøp [?¸u_þýÔ#7ÒröÍ@M5Á»rÌ6ŠFŽ»³¥/¿Íb O;Rö—£rY8­ö¹²é±±pü6•Šímî[ñ-’°on©ˆ¤‹W׿é8Ò—RO÷vã4™6‡] ¯d.|¿-\7 çpr—ûÀHºäÂ}"#éìÍC9m}é÷õ†±Ñîak{†Ó[Yláø!§]¾¢ti}Ó¿jά*÷㾨ö¥ØëkØÕÝvÔ×ðŸpZºmüÌ'•…ï_‘ÓuÑ^…÷yt\ìzm8 Úç]´ÐOx^Öš>”_d#ÂéÞ*î@áû;äÇpOb…Ñ©®g 'X†“Sñ¬n«·’%·üø1‚v,§s" §z©‡»š ǹ㳚—Ô £IÇëÍ–F÷E&'ÛF†åz[¢ê¶µºËº„ÓùCß™‹…õ'rå™ÍF %Ã×5ÃèÁñ}ï·Ü.|^†“Ùân´§ë)uç±aý‰\‚ã½ÝÞ®Jjy¹ñàèPz:™[Éì _›­-Ý®ˆIÒ¦þúUÝÂÉOêtñ»½°þÜ\*>ºÔoU²üýþ¹a¯ò‡÷ ò§ßëë®ODe^NádeXøó9ÑæßÏ©¥5Ö(f(©NÓ iÍöìþŽfÌŸÏË¿^¯ˆ‘[l=}³Yê¼Ôr^oÝKÝvÙÛE˧Î8¤¼gKà  ᤛVÖ ä©ðýrüó>§©ÜÁÊ…?ÏQßÇ'j*ó‘»’2:“=Åsaí 'Oî× Ö/È%ÚÜÚ5üIÙþ Ï‘›õ­¶SZì¦ßשK5z}`5ÎÛ'-®{!¬?‘Ëö÷k»ôë)z…«®ÝŽç¨òçˆ#wÿy~–2œšÌý¹šÏi›»kDgßz§¨½WA‡çÕÎQö‘Š“ö»éŽâ“Û‰“hˆ>eúhŸpq$ÆkðáüCŽÿÞ9˜ÖË^I3ÏRÎÌvÓ<ÓáíÎŽšD>u¿Þ>NæjÉÄ-s…ç¶”ŠwW•¶³‹‚>z…u~¿ç, ºóCßÇc7•\_íøýþDz݉{‚*œngL5—Î?ä*½Ïëy‚pqôæ¬ÛYŠÙ=|ÄÅå»iAÇå‡2§L¤nãîÏ7Þþ·ï‰ÅÈñÏe£9Ó:ö;K¥­íÚ¤(vÓ´øœÚÏʈ«vZœf^0Ö çr¿†Ö5í°þõ _¥nev–ª9wÈßM¯¸Ç19µmÜê–Õé{·ñ¶#’„ëwä‚vìZÝjI¹éÖÎ06:Kzöè×öón*ÿìsŸÓD*éÁý¥L8Ù=‘Üsª/|ŽÜÑ%f§jmË_*&Ñ“ƒÎ#K[‡S|îƒ@ûÂõrîC¹oB÷“i|€ìêã3t~ìœóÇKvSÁõ>#ÍœèçÆcžýÂé컕¾×6 ÷[kYcjU³÷Ñõ®_GöÅqûýýÔ’‡Mß/.ü½—p¿Å÷÷ù@¾7Ö{ŸÓmþÑÒ%w‘ácÑÜžÝ(žðkJ8EîànlÇ 9ÃmÉc;éÌ.x†¼žÞœ8â¹?ÅY.úÐ{ÝðâÂáäR³é¶¾Œÿ\Š‘›}$Vr*;©Ÿn]÷,©nÇ´‹ïµƒú¿¾´8ÈKJ9Áš%Îþþ`Ü›Ý;æ‹z€/ÕâKÃqÓZܽå3[N›Úí]×QJ»ºrOî…“áñ²ÏÂur†?Wj-£ÛÍÏ&^q–¦OºÑ?!g+]Za67ô‚¹q_»7 §ÆOÊÞ®žûC®ÝäáUBVQë{×v?KÜŸUö£™mWÝ]Õ•ÚVOùTíCñß[Ï!×ÓŒ{2cmÑ¿}å±å,ý¬zaÉç{¾Âß9 Ïw„Ñz÷]³¿ð9=rüýÞ)ÂõÄY:üøûTÇc¾Âó9“H³êè@ûZè'§Õ<Ë¿ŸF~¥baÏnU5=H÷ÏR•mÕMÇÅú’b`“P‹DÔÿ/ÉÁ]þÃy‡œáñŸ)³™ÔPèÎQ¥¥­†úéK¦÷ZFÙí~¢ÍëNnµãt—ç ëNä.?p}åÙk9ÃEmÛ6çhÁ둃º{ùQÂ$ãG ÇØÓÇÍ–½kNC ¤ u¹­»b7ôí½ž•¼Ù¾"lÝïç²ü„õÕ:܃{*œl;7×?Nè{ȹù­´ßÂd‰E}SÏÑ3ÏÙ£êmú‰ý¼‘<·¯}8¥œÚo&\÷!ç9 .é·1sÃ¤ç¨Æ’^kÔ=·Rœ˜û‹ÄIø]>-fk8í2U7Õ‚¯ä2Fmi<Ôo'êç:g„’6”˜¾ï²õÏß9$lq$}ÁÐÇ…ã‡Ü|ìºá%=îvg¼]­Ä}{è]Í‘ÎÛÅ(;œæwõOï¹Cxnsk©xy¢L·¦bãŸ{T¢b¥[Hñ¾¬m5æÁGvâ`5ÖWÍf¿ÖNW„œÆÝ¿©bá¶êc×>ƒE¡ÔЦçi¯~t¨Ê°ë‰žŽT{uÒÔká¤víÜ3§›ð¼4rogxØi±ÿÛ^F‹CiȵÍe.~”×äÞãv©“iŸûÏ‘__…ÓËíQµÉ’ÞSŠÜ°Z7B¼‡aA‹nÑä…’ÉÂÓ}øÑÄݎ׿2‰ §¦dô`äöƒCùßO†Ü¬^Äc†?ÛkFYÇœ5ïGøÿ÷léyî/"Èp:¥ Ïm"Wìü²×ùÎ'Ù8ÇýùœÝhÒ7ýöúU%Âé|§:Rë­Í…¡|Nƒ\Çau"í/+XHžÅëêWèÎeý›fq~T7ܧý{íúPqqüÆOáä`x°Š¯/zäì8<0>ÅTCî®.NÞNi_u+5uPžÛ£™ðg»ö“ûÙNiüq7ÚV*便ua#¶WK=NG·ytœk³Ux>Ñ “á¸K´îÍ[ð?O„Üø-§VùŸfüßMDÐÓê_ö qÛJ'ŸuÌîóc ý'Ùñ(œzwö ÌèÌÿ~bä–Ÿk˜u†ñë™êóêS|ëæ[©Ù…²n6}‰ÿž?‚üâÜògZ ÏÝ"w¸ ÷ÍÀ9æmøƒÈ²¿^x§–§µ7ü¡ëŸ÷3¼:÷@Üeȱu±ÓÕƒ•ìÄå&g|¥‘Âý_ás<•ò”}vžA/+z™TÕòŸ3rkvkWuYÊÕïlq¼8’V›íÞ6c39×Ú6î‹”ìZ=ú(A‡Zÿ8ó§ð¼;rŠ—Ì.D‡1ÃãçS¢èÒÖÑiÇd¤ìiˆýUOºšwQáAIu,ír— ¯€Üé³ãúÞ g%]œë¦ß‰¢ ‘‡Û~u]Gí~|Z¡?æI©ã¹?¤Š ƒ/ömR*ü½‚¼Tœ÷eú›¬f‘ÌÖzÆ«7 ¢i¿ÄسÃ<šµk|ô*)ùö| sAÓÇÏšXbÍçDÈþ,Ö:оúMWÅÝê°‚Ní~ýÈê½Áìó“$+ÚÛþø&>'FŽŽ'šLM<ž8ZEŸç¿æ¼”$‹¯×ºöÙ•ø¿ÿ‹¾¿þÞDþûï¾Uì`÷‡‹*² 83¯i†7.#ëN!þïI"ÈÌpá,ü½rÞ1§ª_S1þ¹¹ŸÔøÄÖE¤hžYcåwjÑsé—UÔÞðÞÔAØ‹á?ŽñâökàFyqc¼¸^Ü(¯Þû{Œ7‹ÛïáÿiŒ7&Œ›OÆ õ5ƒFÿùpß¶Fÿù€ßÎ`]Œþ>ì·ôn;noÞFÌíC1ñSKÁ`C„i‹FüTE1Œ+#~hð(°†Ñ0ÆèïC„¹©£ãŒøIÝÜäí*< &ƒ£?yÎÙè¯!ÃSÀÕˆŸrènô×ÀáiFü–Ü~ Ü4ë߈¹M°fÃð‚¹FüPbnüÚïÁÄܤºÅFÿë€âåFü¸•Fü°bnx·É£¿†¯7úkp± 6ý5ÀØ×è¯!ÆÛŒþd¼üøÆ»`·?Ø8öÂ>£¿†0úkÐq?ìøˆ?ðø˜ÑÿóÐã#~ðñ8 çŒþ‚fô× äHˆ2â"«Œø¡È±Fü`äxH€D£ÿuH27”ú‚?,™²Ç@iF QÎ6â)k!òŒþª|Ɉ¬|tpÕèïC–‹ŒøAË7Œ CnÂ-¸mô×ðå{pßèÂüȈÄüÄè¯aÌÏà¹Ñ_C™_ý5˜¹ÞñšßÃ#~Pó'ø F mþfô×àfnÀà/àNþJPª@U¨Õ¡CM¨µ¡Ô…zPL 4„FК@Sh¦ÐÌ ´„VÐÚ€ÚB;h/Ô#/ásÁ½wÜ¿ È@ å`Žb%… Ð1 —|@e B!“@h…¢f Þ =˜¢È9€ ÔP"= €V(€–à JЃ) ¢ÈA`é (KÊÁÅS A cR1ø€ Ê@„Â*Ð EÖ¼A z0EÑu¨¡ÌQ„¥:0FAƒ¨  D(ЭP¬-Á” Soƒ*ÀÅÜ P&(ì6 5”ƒ9 ½‚@Æ(úbð”M@ ‚%xƒô`ŠárÐ@X axJÀÍÃd †r0G3‘BèÚðÓ´Åà*(@+4Kð%èÁMÈd †r0GS’Bh¡,Ф¤Z¨s4-)€Vh`–à JЃ©0­V¨ 48/P@ ˜ ÙÙ€ ÔPæh~R£ŠÁTP"4F €Vh’–à JЃ)š¦ÈA½¸}µøI» (4TÊÁ V A c4[1ø€ Ê@„æ+РؼA z0Ecv9h ,Ш½ t`Œ¦-PAˆÐÄ%Z¡¡[‚7(A¦hð  T€¾( LÐüm@j(s,¤:0ÆÂ@ Ü_ü·øÿx„í?ë#£ÿÙë£ÿ¸6âÖEÜšˆ[qk Ö?ÿ÷¬¸ZÂ}f•Â{Ãå@¨ "/P@ ˜ (Ù€ ÔPæ(RR£`‰ÁTP"0 ÈAæ(fRãň¹©å ‚2¡ÐI ´BѳoP‚LQ@¨ E/P@ ˜ @Ú€ ÔPæ(˜R£xŠÁTP"S ÈA`âê (ZÊÁ…W A ca1ø€ Ê@„¢,Ð Ú¼A z0EÁv9h ,¸Iô €0A1·¨¡ÌQÜ¥:0F¡ƒ¨  D(ü­Ð,Á” S4ƒ*Àë/P@ ˜ˆðóAj(s4)ŒÑPÄà*(Œ4Ph6ABÃñ 4 Ð1š|@e B3’@h…Æd Þ =˜¢Q9€4Ph\^ €0A³¨¡ÌÑÔ¤:0Fƒƒ¨  Dhx­Ðü,Á” S4Cƒ*ÀÍÑ P&h”6 5”ƒ9§‚@Æh¢bð”MU ¬%xƒJÀ ×d †r0G–BèÀÍX > ‚2¡9K ´B£¶oP‚LѸ@¨ 4r/P@ ˜ ©Û€ŒþÚŠ“[Ÿ´ûç^ÐÿëµÎ?ëœîýß²úï¸âê„pL¹ß›ûÿK ´B²oP‚LQ°@¨ 0/P@ ˜ ˜Ù€ ÔPæ(nR-T€Š( LPøl@j(sB)ŒQÅà*(Ф@+LKð%èÁÔä  °@Aõ”€ Š« È@ å`ŽbZ¡èZ‚7(A¦(  T€в( LP m@j(sl)ŒQ¼Åà*(й@+vKð%èÁ…Þä  °@á÷”€ š€ È@ å`ަ … Ð1„|@e BÃ@h…æa Þ =˜ŠðóA¨ 4/P@ ˜ ÑØ€ ÔPæh<­Ð„,Á  c4%Kðè„&e ^ €0AÓ²¨¡ÌÑĤ:0FCƒ¨  Dhp­Ðì,Á” S4?ƒ*ÀÍÐ P&hŒ6 5”ƒ9¥‚@Æhšbð”MT ª%xƒô`ŠërÐ@X ázJÀÍ×d †r0G3–BèÀY JЃ)š´ÈA`¦í (4pÊÁ ] A c4w1ø€ Ê@„f/Ð ß¼A z0ÅBÀäÿ²>qƒÖGÿÜ 2úgô?qôßõ^w¾Ë„ãÆýnæø¿I!tÜëA¡ƒ¨  D(\­PÄ,Á” S5ƒ*ÀEÎ ‚@' ‚2¡ÙJ ´BãµoP‚Lш@¨ 4f/P@ ˜ IÛ€¨  DhÚ­ÐÀ-Á” S4tƒ*À Þ P&hö6 5”ƒ9š¿‚@ÆXˆÁTP", $ð/ë%nñû»üÿÊõÒ?k¥ÖJÿ¬•þY+ýgk%Ká3§^»þo^ €îç¡HÙ€ ÔPæ(ZR£€‰ÁTP"4 €V(n– €0F¡ƒ¨  D(|­P-Á” SEƒ*ÀEÒ P&(˜6 5”ƒ9 ¨‚@Æ(¦bð”ÅU  ­%xJÀ…×d †r0G!–BèÀEY > ‚2¡HK ´BÁ¶oP‚LQÀ@¨ t/P@ ˜ ¸Û€ ÔPæ(öR£ð‹ÁTP"4 €V¸ˆ¶oP‚LÑ(@¨ 4/P@ ˜ ‰Ø€ ÔPæ"ü|£ÁˆÁTP"4 €Vh>–à (4#ðèÁÍÉd =˜ YÙ€¨  Dh^­ÐÈ,Á” S46ƒ*ÀÎ P&hz6 5”ƒ9š ‚@Æhˆbð” R š¥%xƒô`ŠæérÐ@X ™zJÀÕd †r0G£•BèÀMW > ‚2à†9J ´BC¶oP‚LÑ @j(s)ŒÑ¼Åà*(š¹@+4vKð%èÁÞä  °@ã÷”€ 6 5”ƒ9ÜŽAÿ²Nýs_韵Ò?k%Ñ?k¥ÿNk%á3¥^›%xƒôÜ •ÈA`Âå (1ÊÁEM A c81xƒô`‚‚g2PC9˜£J!t`Œb(PAˆP%Z¡PZ‚7(A¦(œ  T€ ©( LPTm@j(sY)ŒQpÅà JЃ) °ÈA`‚ì (gÊÁÅZ A cn1ø€ Ê@„B.Ð EݼA z0E‘w9h ,Pô½@%`‚`2PC9˜£!H!t`Œæ PAˆÐ,$Z¡qX‚7(A¦h$  T€…?P&h26 5”ƒ9šŽ‚@Æh@bð%èÁ Éd †2¡A9€ÔP¦hX 5”ƒ9˜‚@Æhfbð”ÍM %xƒô`ŠÆçrÐ@X zJÀMÑd †r0G“”BèÀ S > ‚2àK ´B3µoP‚LÑ\@¨ 4[/P@ ˜ ñÚ€ ÔPæhÄR£)‹ÁTP"4i ÈA`¦í (4pÊÁ ] A c4w1ø€ Ê@„f/Ð ß¼A z0ÅBÀä  °À€û fÅ¿¬—¸>ÿÏzéŸõ’ÌèŸõÒ?ë¥ÿ>ë%‰ð™áÞWîg‹ÁTPÆý{* €V(Z–à JЃ)Š˜ÈA`¢æ (8ð”) žÈA`è (CÊÁÅQ A cJ1ø€ Ê@„Â)Ð EÔ¼A z0EQu9h ,Pd½@%`‚‚k> ‚2¡K ´B1¶oP‚LQœ@¨ k/P@ ˜ pÛ€ ÔPæ(äR£¨‹ÁTP"y €V(ø–à JЃ)€ÈA`†à (4ÊÁÍB A c41ø€ Ê@„F"Ð MżA z0E“q9h ,Ðt¼@%`‚d> ‚2¡!I@(s4( €ÊA„†%9h ,ÐÀ¼@%`‚ff2PC9˜£¹I!t`ŒF'PAˆÐø$Z¡ Z‚7(A¦hŠ  T€š¤( LÐ0m@j(s4P)ŒÑLÅà*(š«@+4ZKð%èÁ×ä  °@#ö”€ š² È@ å`Ž&-…Ð Û¼A z0Ew9h ,Ðн@%`‚æn2PC9˜£ÙK!t`ŒÆ/PAˆ°@h…E%p;2(ÿe½$5ú¯ûO&¿Ë‚‘B^*NóOÝÜoF S·ö{1Q}BØç+†~ÿïÝ}Z<˘OßCߎŸ.ö{’PNŠzWõrgªÅ®_9º#BØÇYNü¼ÇEý{üGƒŸÃMK½þ2†Ý>÷°w•1Tg ßà‰I^æçÝ÷ïuë¡_¥~è=œßCÜÒ…JÓ§‹cYBÆ´ÀþÏc„yª³èÖ n§0W*tJ9ykÄßæàm/×qµÑ”½‹eÎcv/¯>:–^]œì®Š–’|NÍ>•»º“{Žº´TA»E¦y(ìß‚œûÓû=¿­ˆc“¸±ÓÊXjž1âé­ Î´p`ÚÀñ'=(ºsJÏïúúaüàj¶°rG#/<Úø.ŽÙ6–Š#‡öwítÖt¼×§©ë®N%/ngûH*xÓïðÛDaÿän-;|¤êâxaŸ‹8jT0,7}]k¶4Ý÷jÄ‚itâ «‚Ž‘ÔìFŸOWøù¡2ä&fèž?Šgu\jˆïÅÑfùêo¦¦öŒ›žì0ayôç&D’}½“ËÛ¥ óf‘óæ¶ŸtL`͵qùIÃãéæåÏ“žqcù^Þï87UØ/1RØ÷œŸ3¬An—7¨0u$u <Oil§qg°f'íØµÚƒL«ºëð¨H²>f[ôX˜7‹ÜÇ›•n4é›È c\š%PâðO™¾¥^LûÜùÊØ7nTÕ07RØN˜¼£T\Ù.júÛ£‰¬™m§s+·&Ð3yŸZ–ƒ²LîcÐÖ•¢RêíŸIçžÞ•8˜ ó‚‘ûñÛp5‘ñsõh¿Õ¼“'x3~Ÿ% ÆÄ/Œ$uþ†»Ï.óï§¹/'öï’&±é›¢ËŠç'Òó~-õAKXx~—J½kº•aƒûHª}¸Û–y[øœ9Rú7ÎKOb\ë}p¸HxQ­vG-eüþÁÎd}ïͱ ‘¼9£v»IÂñCΰMKk5›9Åâ혉I”ÖygËý2¶£JÙçG](`VZfëHºý…;±„y³Èñû†©Ù>ɤ×Ãs’èXqÃCr÷¬Ö…À {N¡ôû½fÛ9ãs¶öØÜôO¼YäÊ¹í®¨YšËòý‡©ésÌÝ /sW±½u%žhëNóì§Î¬¼!’>‡¨É„ã‡\ãj_GèÌSm¼ò“Õ´¢qï©â5¬e·¯FO£Dn¼oh$q» ï½Ä^ŒüKÅoD+ÏêV' s“éN²E¡ÿ­õ̼8ôݰöždÑðÖÒžYøywŽ,wkÇÏÏ!§^1p m2kï;¦Å˜ˆdZ`å:CWs3Œ+0™N÷†µ˜I.{¯VËÏù#WÀ¦f)ÌôE‹ ;æ)ôƸ2 ¶Û"ì³?zÝÿªº§Œ¤ ­C†¾_*Ì[GîÀ†ô›‘³R7 ÀóL ‰Nm ]âÇžNrwúäáIËìÂÛ.ÙIûVX¦Ø®â_§ ¹i×¶lžŸÂ®*ªò¤íy²›;æ| §mŒ›Žrs%ôK²’스³‡_¯˜4Ÿó­@ÎÜܺÛçÊçYѰØäy'ÎSÆÔKgµÙr6ãšmÛ;÷?ûdU»P š(Ì[Gn|Hn–©ä<3Œ“hq†~¸ÙýêvÆïÇ<…F6<ÃûòÓ8CÖQ˜·Ž\ÓŠ‹.ž=Ï ÛŸ¸@kç},rÚ´ãÏ|²Yø´Þ¤Œ1™ûmNñ9£¥bý8nÐÚy6¥â«ë!³‹ôqAÌ€¶Æþì÷~iƒ¸ß<НävlŽr¡ÁÜ à ,Ôo_nèñ‹¤Ï]¿ÏFÆï‹íL>† t¢„ýùßOŒÜå–Ü„š ,:®í¯Q*™ÞûdßÔx'k›Ø¥µ -–~£ï%ìOËç¤È)-ë]p}}™6ºN¥ÁËÛtl¿“%öR4{í.!ç7ÞCúu"~ÞŽ0o¹ÍŽ;Zíy‘M;PûÀÆŒBö ®4ÞwãçjN¡~ïRyü(é@¥üï§@.é`®÷ГÙÊN[,’ 1ýüZÙn6ÒðF¹’wíƒÏ+O¢cg¹—ùÏ™¹ÕFþß¿^dý?ù´m¥¡ VkíßÃ|ºêO<àFó nw÷Ž¢P[7÷‡}WóǹÝîÏ\pNeOî|ë©¡]~ôêãÀø¹¾nôhlaÿÀÅQt¯lÆp‘ŽÏí*ïàÆ%§²QzEÚY y\Èüñòe3ŒA•¸‘¡ì̉"nŠM“skøã‡œŽÖ‚1nʨþµ†¬¿ûT¼r/³¨ÏíDìJ;{TdîE™/¾V;ï¹–?~ÈÝ‘qƒÆ“{-¯Ó7¸ÝÔ¾ÛË’^? ­—<…ÖÎ+;âE¶®æ›äsRäž½]{ôçKÆÄŸÖ|ÎY›F‘ýºT»;uã×2l‡<9ІÇuïÁçdÈñûjØQ‡ÚAš42±¨j똼ÝÕí?¾ÁÁ…¦ºÝ{…KÙåÛ\<·”ÿýÈQfõÚçj˜ï†³S“ŒÓɪ8jf;£@v aü[µÒ™Š-î6ZEü0þýÔ ÇχÖ0Ã6fé´$»háÇŒŸ„ϵ”Ûa ŸÏjsÊ?û ǹEæÆ­ïk·›r÷céÔêˆ×è;Yô¡³ÆÞk\(- (òD`%ž¯Q–zL8~»KÅ}œ¹WÆö¹.²{”Nã[ö <ô"qS•Zí™B£äÝæ¼ÙEü\'>'BnRòv×#niÌ0Æ£W•-˜rréþ?õ¥ËˆÁcæWóǹ¯¹ ±ÒØÎ[?ºjVeP¶çš:Š.Ø#Ûaµ­VOökŽ¢F)v¥r>'EîÔè¶¹÷ÒØ¥|ÏÏ­22¨qyöݵ²4É}ͣў4ÝD2(^Eï:p¬ ǹU†%éŒß5“rï}n|¦Ë!¶¼}÷®¥Ë§ÓÔ½JêÞŽö›\Ç?äÎåqƒÖÒY؆3ïLÉ$åú.ïn¹±…½Ÿ6ÿ6ä7>Ö^™E9&F-×~âs䤒ŸëÜTéìÅâš:“IkE^ç+öf¿çuž}Ñ4ÇŸ'ÁçôÈl>Ðíc:KÈÌà )“Jón ‰<¾U^óಟ'­ü:Âã´C2 å?£=¥â×Ô§Õàal¢apdmeÞ¯GÙÆ¾½=äEÓ¨GýÞÀ(’ ýh³å¶pþ!wî’e݉~Œ›øfWåWLº¶}Ú1VZýÄ(͸]þ‚­£èsÞ§ÁÂñCž›Qº æ^p¥îø›Y4«OZ“¯1¥77Å•j5ð6všE›+¿ytm†pþ!7šfšÉdoë˜}íœM}¼ï}4õ8ëφ…2lo~0ŠÞÚî{úu8ÿ¾È۶пO‹™™Ìvôåô+³igvÀ$–vœq¿]O UšàªŽHÇq_¿%M8~ȉ¿ím×'"“5:ºgþȬlªÄã³s‡Œ›t(MB½æs+°(:U~s°e ÿ:5ÈmzùñõžÏ™ŒŸÿ˜C7Ó‡éówŸ`ñí¸ó§ÐѪ‹mB*GSVkn`=ŸÓ#w?T¶öe±JM¸•~]½>Z÷¾òIv©jFòÖæ®d~ýÚ}ýhЦôñ3>'F.ŠçÑ,›%?¿Áß^Kƒƒ”ƒ®Û*¿ï°+iòë´˜õ#Šð"~oºž?~ÈÍ6iSòvZ6ÛlÉmè§¥ËÞÜàzë1’Û9Ü•L‡Žûöõl˜®V)|NÆý~ƒµ/ÒNg3~w-mí?xçë,ã÷Ó—Ðx“md×£èfÜÓ>ýknàrÜ´½Ï²Ù® ýŽõ˜KË[§XW{¯`¿çv? z¿s}n™¼Øåêù•Ïi;ÎmØ3‡Å„,QÞ÷Ë¥Ái6«?7f›#Î9Oóp"ó·É^²(ÚÓ¬ðAÛùã‡Ü(¿çOJç°µ}&ú^Ï%—ñÙO«¶f‡M–-Z9™ m%1Š:^Þ¡Ü>œÏí-Ï,+Ñ1*‡ÏM†Î#³Ò8]äLâ-<9‘j.€¢hé€V•îtç_§9_»œcs^æ°z§N]Z’'ÌS fjõxñ3Æv·fIÏ®D‘a<²ÿ~Š‘³¹7hpW-ë“ó.j&¸]2ë×f±›–ÇÌ=ã@Ö':xôýEÇm«Ëº.â»9—5 8Oײejçµ%µóiÜøÎz3n(ԊЉô²77 ;šÖuˆ9-œÈÍæ¶?¢eøm“]óiÑ«ÝÅÓ+³åëâ-gM¦uI«ž¶ˆ¦/Mæ«µÃø×©@nï†oƒ½/kÝ—O‡wŒ|Y¹q0s¡¹{*œÈ¹ü¶ÉÃþÑdhÿ|Nƒ\³¤«F÷*ç²öÉß·}È'ßÒõµƒ ók$亩m¸Û°h:µîàÓ4>§GŽßÇ5—=jÜ*÷1PüH—õ5½ƒ™Ì$7`Æ‹)Té¢÷Ót›h2Œ«OásFûJÅk„wqËe‘sï-Ù±»€>Ž´XÌ^²Ëã.q¥mólc뉦ìU͆·ùœ9~Ž@.Û-5¸üfÕ¿ôàWß7Áì÷>¾wzÚë:E·+¹¹1ÜÅÈ=K)ßò>4—íhÙn‘}ÇK”at-n^ÛSlrǼ{“.»PVÕþëj¿@ý4Kõ[ñP8ÿ˪ôáÛÏK¹¬ îÖÁÞ—(çYÿm V§Ø[¯‡ò„ G<¤yËŽÑQô$¿¶j½pþ!·wÓÐóë^ä2îDÚt²ºi{Šñó['ÒŽÊëÛMØEf÷"Ò¾… õ¹Õ—‡WËc½;tn”Të2í}ØëÅSl´aCe{r;_~2J˜/#ô?äúqãV[æ±ç1=ª\žtY˜‹}ŠñûAÛQæM– ÆyûiÝ¥¥7Ë„þ‡¿ß}ëfèw™ÄGÓ|W¾f¿jr>ÛÒÅÁ9²—£¨<ÄìÓÚéÂñ ,÷8ü†mØ›GS_Ã@[áø!—±¶^÷1yÌØ¹(ÿM¿+ÂÜD|^6‰Ä[w§¥åÅ;D sB„ú‰ÜÜ@˜<–öðöùêë¯Ð¤Ñ7†ì f½i`¿Yç&Púö²Å¢i™ú¼&œÈM{b41qÓ×gj¯PÈõäã·às-ìW¾õé™é3¦F“Ëõ.U ëäÖ¥¤ÌˆÁÏóoÎmd®£ã^±·\V³~cœ74ß9‘z»·2ZâMö|}Ѥ©pü{ïìn›c˜®£‘FÞ/o.ûë|Ÿc¸Àˆ&uõD[•¥püsínýî€82ÜðБÉà]SŽÎfužšÍö:‘^p·SZDSÝ…FDVÂñC.­R´yLôú¹ÓÌå:ònÜmZ0k;\õ‰¨‰á‹æ¡ò9£ý¥â t/ܹSëÚ*âײ õhªsª:6˜M²ávhOü<º¨¿õ1r†q" ó˜U¬¬æµ_ñv¡ªÓïóÖ–ÜýåÓßE û| õ¹+½Ek/~Ëe®cœº«£6©?>)ØáMyŸ›¡ñ^Ü(*<çôaÛlþ¼•"×Û:¹ß†¹L‘ùÔ^õK'Ì‹U°]Së:˜ÍEêËÌãÎ[~>Ÿ“!7+Ë{…Qf.ûpn×ó墫ô0ÓeÈŽe a.š}Û·«†ÛË(º¾‰kPBýDî°µyâæ³¹ÌÆ0Àø*¹Ö홞ÜMÁÜŽ¸µÈ¶"í5M}ñûEhËd^ ëä.þì0|Õæ\vlÑͶ?=¯RÌÒ [éå“Bÿ³¦¦}\ªU&ÃöµÅüºNÜ-܆ڹ ‹É7Ù›¯Rýš¾V¡ÓO²I7n}slbKžÞ—ZýèM‰ÜØ4 Ÿ3:€×yóiï¼¾¨Ÿóã[–_¥”w?íïxR¸à@ŒÏ<3šè?ô¼·‡"äøû]¹lõ‹ï·g±«t÷´¤Ã³“ì÷¼Ž>XeïÛM—žÅ•¬Î?äøû_ZfÜjyÍm÷® ýø$«e7T± ωÖ4{“óêP´0—J8ÿ›eºj‹W¤–Uò[óÕøûUú;ÐÊaãÉ?÷'fÔœ?ã¥o4qÓU*æ ý¹9#±N+¬ã 驯ٕ˜K'Ù ÃÉ™ºì‹®:iz4…¨É®_á?rjq+>-3l³Ü¿Êž—LšX]ÁV×ð)LŸåD]†o;$úoëd r«Nø\nj¢e5·Ç…4F:½Ôº‰‚ýÞÜRS°ªëÁ²q·ï¶֟ȽÅ}òrX—Ë÷š/,¤Ð¾“lgÔW°gÓöå5^æ@fó7ÎYû!J˜/!¿ƒ8ßg7Ø©Èa¦Ëz}ÒÆçõLo½9Éšm±7Oz»#âð¹^¯‹›ò1Y¸~@Î.ïîÓ ¯ÆÏŸ.¤ˆ½s›‡ždü>ƶ„bsøÎwþz^¸~@îêÁe]¢ºæ0~~p!ŸYÝ Éñ¤0¿Ù–Ò{çŸ¨Šº›ð@Õ¯º¬<“=Ö¾|ÛÉk´sT];¥ßIÖSß:uœ#å÷4u_†u–=·M÷L>'C.Už8ãyT&;s*æÓªÄkÔêLÒ##'?_x2M5 &Ц´Á»ÛtÝÅçÈq«›ó2¿?ý5r¼>áu£xÂÜûN‹šdŠ£I–$Þ\š#Ü?CnGŽu‚Ü<“ñs_®ÑRn|ÇÖ“Σ”9Ü,ÑüíŸÂñCÎõø±`õà æoºÒ4ð'ŽC—Õ•=O2ø]« Ô&E‘:Ë>šJ¶Œ}û¶¦pÿ%¨T[ì0¯×© fËÑì::9«ó%‹“lØÛc=Dñö´ªxÑy;Ö/£§°ÃÂñC®ónhñŒ»J4ëyŽ_â|²úI¶¨7!Î\oÔë‚z!"ŸukùûŠbä>Çl¾¿Õ4ƒEÞ©]PËú:ýz0ùLᇌûm›¦L¢‚šÜ¸h:m4ÑbF-áþ'rÙÛnæ'^KgÝÇhÅ'<®Ó¥M׊†'…9–Îd(“ÒhR=>ðba_þþ¼ ¹!»âïíNgÜTá}˯ÓÝmK%ºy'ÙÊù‡ë伟B¥m?x…EÓEgn²ŸS W{FíõlÓÙEðÆÍþ×…9¼ ÆãA)~Q£_£éJ÷š“ó…û×ÈM*ª:ñs¥t¶cJ¯­KO]§oW¤ŽòT°ÝÜxìÉÓhIä'U³Z*êwüxÝû„û×ÈóSªONc2)wåu¤‹Ž%wÏW°6ot뮞F‡Z®²­ñ"š¦‡DÜ7N¸}¸Tì÷lmÝ´%iln¥[Îq—¯ÓØjÓJoö fãoìiÖg*—uJßz.Z˜ëÊ¿Nr—ÜV´ ë’ÆÚ>P×éËav£}ƒ™aüwˆMˆ™gòde4•¶â&ˆòóLwOÃÌÒ²?¾ùzt;¦~oŸÌ䟸+¡Åu*ibæDcÈMFçsRälWGú¾Þ§a ´w5)¢´!ŽQve]}±u\¹0š†~nÝsÐkþ{rQI—åc4¬?÷5CÇ"âçl³ÉñK{Yts$,Ý­¦;ê¬ïwtÂ÷Èåe™JrÞ2öf±nêú¡EÔâøûÉ-kœbü÷Z“Éïþz}Ñ´mÒÚ søœ¹-VßàÈX»,£íŠèí5 ãLN1~ÆdŠ‹+;^KMZcðýr¯¯¶xÝ>)•ñûëÑÖt8@tŠñßã:Q—ŽUß?ƺ@çŠ!|ÿwï‹ç½‡ã¥²a¸JÊXSD’¢MÓ͆žb;MZ/lãLOq_Tª¿|Ä®þ{.rgË7W,¾È›D6Y¸»ˆÜÝìÖcö)V¥7UB:µ·¨Cm•0¯Jøþ¹Ê²WNä_`³šÕ‰J)¢£šWkyü{öž»àJ"Ã/¨æ: ßÿ!tn™u—¶X–ãךs’ŠhÏ™ú÷O s¿Ýˆ›âU¥†Š¾Æïø©+¾ÿCîábîÂyVÚµŸI~q¹ï§Ž!Ì}ÈĆ çº ç•Š&F ǹV†Á)¬ë/שÉ÷ŠèªÿÆQêE!ŒŸ«êF3Ûpß(FÓÌüw+šÿ¾ÿCîÔÕV™—§°ìfã2Ê‹(iÐýÃf±!Bw¥’®i¥ÞOP¯½£<·fó9=rw çÙÜ™‘Ì*îÛ®\Pµ˜Ö׿ÇýU³£/‹ûL¡wÃÛ}„ÏY÷©ÝëìåsFGKÅÍ®'<‹U³Ù[ÆõYÚ´˜,E—[·>Í|’åo2#\(¥ÇÞÚ—õèÓí»Žr(.As¿%±¢Nö¯W™“òñwüFœfü\g;ç¥ß¸QLÆÅ¨ÊÎw;/õ¾äAoÒoª¤¢YêZ¼ççÊè‘ã¯oÂØ±Û¾kgÝ+¦9™çôeâqé~C7»‘;7Ž»‰Š¾p_àð9£ã¥âÕ±G›ÿðR²ÈSÔïý£bz5;¸bçÉPÖÀ‘»2v%¾o¨(£ÁéI/½„ù7ÈñÏýœe/'rÿS1%4»ºrÜÝP6 ÌÓ•$Ü2Ø\ECo^Ú¹¡Ÿ#7“_Tã [žÐä[þëbÒùä\–µ c£;®¨|ÄØ”‹zþL쬢E÷,»¬æ‡!÷ú…fP{Û––lø¾˜b§Æt+œÆT9£lãV»w÷ìroe®š^ìÌçdÈÕÉëwÄ}g0‹¹û±ÇÇb2ß÷eïÆv¾+È÷üìA¿ç¸ðÿ=|N\÷k½,>ŽuNÓ‹“+}.¦É µ·Ú…³š÷¿µˆw™Fuž»=Y>EEü÷-|Nƒœ¸á§WÚcü÷wá̰üo,¥¤ZÓ󧩨¥®°É‚öÂsKø÷ ú_}²3èÏëóX:ðážá,0Û«•Óx韟Ӕ›d¾ŒN”Šíߪ•M`-޶ MÅû‘•ÓyæÓ…l47¾­”V»nÐw ¢9ï-ë<‰ž[Bîög%›ê¹—ä.D‹é[@’û’æÃ'{?•øûj*¢:èüAá¼CnJãeçù³Ñí¸'fŠi\IûÕ“D2MÕEw/Mö m¬Óüë#Ut÷õãáƒjñ9)rË­Êv–¾ìkåÀ'Åt3­°V+¿Hö î\GýL7ªQ˜uö€•Šª7ÚØxÅaîԉߟç5¬YÏpUs}1µ[ºðpV$»vwÅ€G'§ÐÉ»V¦ž·æN!g<¼z7û„ÙÌ*!»“G%þ.õí¿F2~¾ª„æ5õZeƒÏ‰…WÛJ!ùÂÜ7äRÇ^SxÙÓ¡%Ó“®Óðk^¶êÅøy¿.4paö\yWÕßæ™ë‘ûjÞ¾§Ñ®¥Téëú_O²Š©ýί-ì¢Øõn³š—J\hûë)7¼©høå~žßr…y‹'KÅe¬F´Ç-y]™5¯á…bêÐ噧׊(vµu¯Ó#%äÒ¡“ò›Šæ}}){±C˜W‹Üä?FY$§Ñ#Ê+>¡þ½1â¾@‹büüHWzÙ¤éú>*úb±öæÊ[ÂÜ>ä¦Õ^ƒ¥e=›úqê­µûiŸ¯Qì÷½Þ gê½[£¢ÊÅa ùœ¹—!“]†çÒÇÔ· sPß_=jõvÅŒhÆÏ[”ÒÙ£™"ÕR]]•;jŽ0/¹àdûö=¢úÇ+ÑOŠÒj/¸ÿ*š-|”ÒåqÂtÚUëØ‚*«TUÞ³ÙÛã¼Eä\¸Û“ÓŽR ýô̧˜ˆ³ Û¨˜ëû‡½z<Ÿ!\?«ˆþB˜û†œC%‹#Fž$Ãó*¦îm‹»(T,kh·¹YgR¶‹š•*ªû hÖ„ÊÂÜ7ä^p´`ªÂ¿”Óþ©ÍTUìsYÝÏ{ÝfP¯o+Î ÁyôXæ1þD¬0·OQ*~ñxÕ‘ŸšÂ=^5¦˜¾–w1ZÃÆ~^Vë]œ'5*r«â†º{!¼µE}áø!÷ôçó%÷ç¡çQ/7§õ/&»n˯‹aã{fkïžFÕ¸1€MUÄŸ Ì«E®gþYÎQØ{ek_LNnŠÀýa1ŒŸƒæ.<¯ˆ:ÁÝžwæe"×ÕðÅ|¨0ϯ˜§öœÈ¹ó§Nwö[½§ïx•0Ÿ^˜Û‡\§·îN5¿­lØàg}œëØøE ã?S¨Ü~ø°ÄY*gíï3M˜W‹\%ùù5MšGÒ3£Ëæ6/†BïŒêq)²ˆèpåãúX6¨£¨Ó«©4Úcþ›ÉN*âçt ó¾‘[?¢vMSX2Œo*"÷ušwÅѱž§´<=+q56’6B˜wŠœâ\¥-§'ÄѳÄQ=-·ÑÏoV^>ŠýóX ôà$üÛ&pwŒøÏ‹9þ>}<½æ.Ì‹(L•ÚɤE«R}ìIç«ÓÈó{¿×—«„yÓÂÜLäæÍeQ¯gY~vÙó͹ˆ&¼–ÔmB{Îݾù5•òos‹UÄMµ­RY¨ŸÈ“Y"-.I4QD&¯Òè6Ä1¾nxÐûˆÓMîã8ðß«}9툚}þJ¤ø6]Úet-¢Õ÷­¼ì•qŒ_Ï`ýai²¯Ú8|®¶Ý½b‰pþ!×Q<¾‡Û‹$ú2ûh‹kŠÈözíá—rãþ<wi׳šv*áùa^ô©R±·áÆŠš†GÕmòðûuºû|гõãØãÉûíº3ç`QÑŠˆIkÎæf"çiL—L1w‡‰?ãºÝçL‹f™Ÿâ?WÞ™üj†L¹‰uàѧòu‡& çrÇ*æY²;…>uP ªqå:eø/ÿؼzüŸëïºiKšì¯"ÃØá‡Â¼oäøïÍÎÓÐíú6S_§´ÛL¾7Žgüó δ à…|äÔÁ7æ\þ*ÌûF®rÃñ ¹õºXivê–“×i]Å®ZŸ;Ƴ SÆßhäáB[n-~5j¬Šz¬Úƒ¼…ó¹R7ÉÀ{­.ï:Ý”Íè*ÁõÀïóö‡/7XýÕòWUaÞ7r߆Ũxs‘TÙ7—Wõ¾Nõ[>ø¹PÏLªŸX[ÿ;ùLí:àÉ ñ÷)„yíÈ5Õš=.-Uøë:õ¬Ø?vO<3?RC¥žF†q…ËUt1çp§$¿RñµÛ'ý·îd¤¿Ùúâƒá×éä “wçÆ³Š?nîiìIó‚g.7GEüç@¨ŸÈ-úÖúãn:~±ï­°×)4/òW—Ú Â}]OZ€«ª&ø|ö ½µhób¡ÿ!÷ÎxÑé{5Ôzò*\ºãø-lï8m\cïDìëyRïBÿ[?P?ùçe„þ‡ÜëØi«RÞkèd÷Åï5 .ß°)kk“PœFsýç¯ëô-šR{HÂjûý¹ ¹‹¯æNJ#Ã×7¯ÑΕK\ó=º³k†ÚíÏ}ŸˆÚæ sO‘³0©ªÕŤÑè Sîfì½+v\2\—À /»Ð‰i»sÑÑÂ|láø!â”zÃ$ª¾Ð¿FµsMÒÞO`G®$ßþXè(<ŸMç$ÜBN˜7ŒÜžAWJ-H'Í´‚ƒ;®Ñ‚ ®]yœÀøû“ÉjD×äf*:WÍ·8C˜;|ºT\ôr“³T›NOVsgâ5Jÿr=ÆöYËËÝ™L†q©}U”2¶NånÛøº+Bnà·ý7E†o ¿OºF>—·hò:- ©t¡ZCGr¶Vç*û¨èY—¥rõ þs-FîÈ‹~…“Vgл܃½×(›Ú¹'}N`†Ç±V:‘_µÏUZ›ªèÒà‡ßJ' çr’!;–]¿šA?>ëìZ\£—sœÝœ«'2þóïBåjèÞàøñs{…þ‡œák¶Î™ô© ÷Ít!9ÙÝ7™Ð"‘­5ü qw=;¿¦CË÷o¹YUX¿ WgÙæƒÖeRWÃg…t´SN…¿DÖÜ𬄠oTûûzPƒÜÔnÁç;\Î$Ãi–YHßmÜÎzÛ'2ÃãßRdMê0­¦Š"VÊ…õ'rµã‡œ6e‘f_¸äûÙBÚžiŸ~Ð+ñÏs‘ãv9³åãïï …õË™R±Ç×.U—fÑdž3oØ^Hƒ«ÏŒ^µ1‘mšÊ}#áBöÉéçÝ3£©ëžE¾ÖÖÂurÓZ*5Y4ëÁ‹ùóðû-+çµ/‘Ù¤MÖXãLgl÷_*ðïµâîdgÓä*—žÞyu•N®Jýz-8‘õæ–Ñf EØR¶!š~©ÎŒ™ýõr/[]¥í™WòfûU:ªI}zïã«c³7­ÜæHCø…qÕq¦§P7‘3s®P«–{÷^•$ÜÏp ûG?Çqàÿ~E8ÿ«o¸Qš/ü}ÁJÿxéTË%Iοs—žZE¦ûçöKÖ-ÈùI¹'‹ò…çý/Ó×9ža%1MÌ\‘O"îé>»Q4íÍOŸhWaÝ¢,;zÙãñº|ª||ìѰµ—)­dg«‹“˜÷$û$R,æì¢ï­:?U8~È¥Œ¿yàé|R=êÚ”u¹LçœÒ/Ãë4kVÏwf¥É”Ç-^DRâ˜Åê=w„ã‡\›Ï‘ÅÛóóééž¹ Ín_¢7yÜ­I̾KôÇ$rêmøËZÉso&?äøûÂùtà†mYÏí—èÕ›{îás}dêôî×zM¤uŒœî¨#…ç‘…ã‡\'‹®¡ èÄ›sÙv/Ñš_ï0¯$Ö¶r½`ÛKöä} ³ÉÊ‘¿ï‚ÜVKî‹ø7pûéãzqðšcµ)ÈíM|Û¹« /»ñÕ=’¼Ý³Ç×í'œÈM2ÜH,þ.°@x^>‰Ï2†¦÷{cI[—žòðÛÏçôȸ^Ó«Ý‚RâUnQ@#§Å?i‡Ÿ]#°ôáHI#oõo)<Ç.Ìk-‡{pOFPÌ=e“³oòÉîjÇU}&²{‘ݯN™=‚~_¿>6ù›Ð÷B?GY@†Çç“ÍÛ!‡Þ'²ñ󼪨* C9jIOèôýÔzBßCŽÿ¼PÝEók_±Ë§¨ÀQâ+ªDvGñÉmÈD+Ú8¿³­U»HÚÈýyQ¡ï!w¼÷Åz5ݪ}öâKù½¼°ëÒîDÆÿ}’5EÝø P4ޤøÀ´‰ïž õ9vþ@ÇÁù4tÞ8yòé<ŠàŠ' ×ß¶Ô¯GL³/4ÝpƒS8~ÈIžD¦Ô+* þ¹¿<šõ|æ4¯™‰ln+§ñ&+&ÐviK÷ÁO"è¨wtÛ a^;rs#½–»]@A_êm(5Ê#›WýäYS?ÿz’ðÜWÝ›rþ•Õ2á~5r×GÜÓ§´€‚,pÿ•KßvªÆ_‘&²7¡Ç>í?6™6/ñ~ô>‚øú-ܯCS~Ÿ¿Ÿ¯œo¾LÉ¥æ;ô–ÎNdѸ+#¬;g¼ô=¤ ÃòÊ”ÿ¼ˆ[1ìêŠ5r)¢ÝÀÖ9óÙï¹÷\•®w2‚¢ ¹‹ü¿‡ýõúÚûÅ–LÐÒ¹ùkgÍÁúh]Í&-§:ÐTÃEð.pŸ“"×Ú^:8ïÇéA±ïò¥Z ¬{´¡k"[>uÆ!å=[ºñm×Ê;åáÔ¼ªs½±üï%C®Þ¶3›Úàýç¿ÇÓ+üRÞ}D"ó0}fýjƒ5ÙE_ʶ-§Â=['?y"ÔMä–»ïzi‹ã½Êð…d»k•ÈpQf6çÚ27,ÜÃéûwn,ÜïD®ã•ߦPEs®åМ~Ê—aŸ„çÅ”þ-Ì]ù=œœ§Ÿ8_*\¯#·vìŒøÝaÔú瑈‰õsè ¤ÖÀ+ ̲Ñ5?É”çÐdò³Îô¬vÛ§?6 }/¼T<öCŸâ<-mm×&%›Æ±g•«D&°¢ÕŽßï7°¦›û»]È·Žîc çr|Ÿ) ¹Ù§ž½œ™Ma‡|Ul/Öý=½WG4K‡¸¯ÇDÐýsa:qwþuŠ‘ë99P_w.>ÏkN½HǺS®Úޏõ ŒÿcE×äþ¢$‚ü¬ª¶´: \ï!÷b€òG±}mû¦›”Erÿ»l'°~ã†n¹`Oüó¸á»¿*CîÐäÊÓö, ÈûÜÄeÑ ß ßÌL`üýpò¹óÆ…°pâ¿·Žr# ±€l¥ËꨙEˆªzŒwK`M”çöh&÷ôz£uáÔ³27±^8~Èñ×[ùdÓ§t&-1|¡œÀ–_I¿Éž¨ùÃŒ—³ÂéçôÄ™N¾ÂñCŽû¶³"-Ÿ ™9eÒÏnÝÆÛŽH`½/ß÷µª:ŽÚÌîuÛÞ-œŽÄx Ò\8~¥âŠÓjž=’Oì«Ýyÿ=ƒú è4¼GëÞîÎLDZt¬é…²n6áäöʳWcÿ¹!w¯Êòi—äÓÌ¡¶‡gPzY:Ö(ñ÷GÓhÃ…l856ˆ­%?ä'kÇÌÎÎÏ4ªá86*bC<Û¼Û9átÈ8Zª¼ïy3Œ¤QÖµu‰üçLÜç‹ÍóˆÞ!¢¤Yª—Ç³äæ‹4O·M¤©[R?|zFµ¯O/·x5Ÿþ¿Ø³ûŸ½•þÙ[韽•þÙ[é?Û[‰û—ð™àÞ7î¿Ûd †r0G¡’BèÀEK > ‚2¡ˆI ´BA³oP‚LQàþeîÿIsàþ™oòï›oòŸíÛý{Êÿ¸¯’ö_æÀýO˜mòï˜÷¿;Ûäß5 îŸù¸ÿ¬dFÿ¬þYý¿_ý»ÖE\ñŽ7÷žpYƒ*ÀEÊ P&(X6 5”ƒ9 ˜‚@Æ(fbð”ÅMò/óqÿOšûöÏ<“ß<“ÿlîßûMV€“‚„ù¸¿g¾ýO˜eòï˜ùö¿3Ëäß=ï[×ü³>úg}$3úï¿>úgmôßcmôï¼oÄÕ á˜r¿7÷ÿ“@h…e Þ =˜¢`9€4P(`^ €0A1³¨¡ÌQܤÿ2÷ÿ¤yoÿÌ/ù÷Í/ùÏöã­Ð”¼@!ÌÃý=çíÂÜ’Çœ·ÿ¹%ÿsÞðÑùg}dôÏúHfôß}ô?õþÑÿ¤õwžË„ãÅý^æø¿I!tÜkAƒ¨  D(X­P¼,Á” S3ƒ*ÀÅÍë_æáþŸ4ßíŸy%ÿ¾y%2PC9˜£I!t`Œ¦d Þ fáþžíöû¬’Ç\·ÿ9%ÿsݸuÅ?Ïý³>’ý³>úïº>úŸvÉRø¼i„×mÿ›( „ûY(R6 5”ƒ9Š–‚@Æ(`bð”M Š›å˜ûÒL7¨¡ÌQ|¥:0F!ƒ¨  D(Ì­P¤-Á” Smƒ*ÀEÜ P&(è6 5”ƒ9 ¼‚@Æ(öbð”Å_ %xƒô`ŠÆàrÐ@X QxJÀMÃd †r0G‘BèÀX„Ÿ> ‚2¡ÁH ´B³±oP‚LÑ|@¨ 4#/P@ ˜ 1‰ÁTÂüÛßóÜ@+4-Kð%èÁMÌä  °@Só”€ œ È@ å`ކ'… Ð1šŸ|@e B3”@h…Æh Þ =˜¢Q:€4Phœ^ €0Aµ¨¡ÌÑT¥ôoœç:0ÆÉ/PAˆÐ¸%Z¡‰[‚7(ÿ‹æ¹‰þY'ý³Núg¤1úgôßed#|ž´Âë²oP‚žûïB‘r9h ,P´¼@%`‚f2PC9˜£ I!t`Œâ&þ³oE(v­Pø,Á” SBƒ*À…Ñ P&(’6 5”ƒ9Ц‚@Æ( bð”U Š«%xƒô`ŠbërÐ@X øzJÀ…Ød †r0Ga–BèÀEZ > ‚2¡hK ´B·oP‚LQÐ@¨ x/P@ ˜ ØÛ€ ÔPæ(þR£ˆÁTP"4 €Vh–à JЃ)š†ÈA`&â (~>È@ å`Ž#… Ð1š|@e Bó‘@h…Fd Þ =˜¢1Ù€ ÔÂÜ[s4*)ŒÑ´Äà*(š˜@+44Kð%èÁ Îä  °@Ãó”€ šŸ È@ å`Žf(… Ð1£|@e B£”@h…¦i Þ =˜¢‰:€4Phª^ £ÁŠÁTP"4\ €Vh¾–à JЃ)š±ÈA`æì (4jÊÁ[ A c4q1ø€ ô`ЦîrÐ@X É{JÀ ßd †r0Ç@ A c,ÄÀí”­ú—u×ßÿY'ý³N’ý³Núgôßc$>/Ü{Êý\1ø€ ʸ‹"%Р˼A z0Es9h ,Pм@%`‚âf2PC9˜£ØI!t`ŒÂ'PAˆP%Z¡(Z‚7(A¦(’  T€Ц( LP@m@j(sT)ŒQ\Åà*(Š­@+^Kð%èÁ…Øä  °@aö”€ Š´ È@ å`Ž¢-… Ð1 ¸|@e BA—@h…ân Þ =˜¢Ø;€4P(þ^ €0A#°¨¡ÌѤ:0F“ƒ¨  Dh­Ð@,Á” S~>ÈA`ã (4ÊÁÍG A c4"1ø€ Ê@„ÆärÐ@X QyJÀMËd †r0G“BèÀ M > ‚2¡ÁI ´B³³oP‚LÑü@¨ 4C/P@ ˜ 1Ú€ ÔPæh”R£iŠÁTP"4Q €Vh¨–à J(4XÊÁ W A c4_1ø€ Ê@„f,Ð Ù¼A z0E£v9h ,и½@%`‚&n2PCˆÐÔ%Z¡Á[‚7(A¦hø  T€^ €0ÁbÀd þ—u’Ôèÿÿ‰'áˆ,óûïæ¿ï÷ šhP“öççvï<èϾ‡¾-?]JÍs[϶q¢œõ®êåΔûìÅ—#Ä}~äTï?÷ìÚ…ôûßs?F„ŸS÷G#÷e. šÞcx5†f½6IhïÏøý›$Ô0=dWûâ0â÷“â÷õGþž?+ÌŸÑP{ËGN;Cã…yF®Ô¾÷ˆº÷ÓÃh{'‡Ìv[ùý)¤ÈE(ÓúÕi“K·, ?ÎÔíþ²Þåø?û4rÛê©Â¨7aKØg¹ †B´´Å/°ßÆqŒÂ®:ØÜxÏj¯½õ :וr㤎†‘çÂÇË ûEp¿ß×ÂÛ'ýµÉ:ýÚŸJ:YÑË;õØ[nŒéQ ùÄ/Ét]F÷Œ¸å„ýU‘u¦AßoCµ”ÝçG÷Åi²Îæþ¯v ÌÒ·kÿµ 'ÒL±=èö·ýÆôÈ­qpØáü:‡îŸ¹Þñ¡éEbÙ´¾I×ö.±p~\Ì$Znذf[ô8“?Û`?bC"ÍÒÖ‰ÞØ'ñû@;Ón;ÛÁadØv¿…0—9×äfAMvdRwgϾš:ãÞí… %ü™saبE ˜ÒúÔUþ牑[\¬_ïÔ3“\–u3N I’÷=.ŽI`Ê>v~›§QäBn¢•ŠNÏý¸æX½6¿+·#¨=­:?õH;Mèßæ$ˆó{Õyƺ ä& êäÿ-šì«„ïéŽÏõïùç{6½Jü~TÂ>sÈ-XU_·~P-¨6Þ.bg=u[³gwõ„?ó½ž ãvÔ æº û%!Ç¡5žNÎéIë‚oŸ½Ö$áÏ>ö–óËS»6 #nZRî`¡~r¯“×»VC.µ6¹—jÃ雿d7·þ ¬Œ£9ÆöN—j„㽚&?ärÛ7µÔP7æxvÙ$vÜ0#Aاl©«êONê&ì«Ìï ¤AîÛrn¤(E:œþ6¨Z(uôèûeöÑv£ 7XAJÃ]õ.¨F¿ZiWÿŽrµ¯Øee_H¥‘âVx«Î‘¢‰ilJQ‚0§KJõgÞ·{J‹œ6¹ÖŽ_L©¸ƒUûõÏ‹tkIÆ›³ngiÇûN+Ôu¿/Ù4šg<J;.å$OŽrïÞÅœ_¤»#Ó?ÿ¬~† x×ËrP"ã÷cr§%Ë¼ŽŠ %~nž°Ïrurs3#7_ åׯ.éB=wd ?ëšÈF‚SÈqRgÅvU(=êþšúçr‡ß8™yžºŽž¢om÷Næ=; û‘‡ÒºzÜä0a>rã}žï­~ž5¸cs³]0Iòv­ï Kdq1 ßGœv¢ä%þ_’ƒC©ë¹aŸf ǹºc2÷Û¤£ÑÎ9:ÝIšÖÀ¥KÐÎDƽ›&‘Nôð´Å›%gCi¡aQa. r´¹±uG2e ã&C§Íkv·ï},‘ñóE…}sC‰Ûõ¼Õ2aŸNä c Ôt!nåm¿'GÈkñàFbYò·–mz\–Pά;×ÅïC)˜ÛªpübKÅ’–—¿Ï«§¦Åu. ¢g{Ö^z%‘ñó\iÃãkv´ £ÏCWû:J¨ŸÈu¶8^¼Ð!‰ c(š¤à÷fçG}Ld'ÖswºµÏ©>;ôyÿ±Fk½„ý®ëu'Ù¢Ð?‘¸éÁÙçÉnXm«Õm’X³‡vìZíA ¾/î8d@qUÁ)YØï 9› {Gf^J ÅõnÌ«¿v/J\4h|ãë³=~e|®Z/¬ s „¹6ÈU?vfæ š TyLåºVì&ÇN/:=]“ĺ”}Ú—íåÁ ™ñ­óèáý9î–&Â\"äêÛ¤§O?Bö3ßA…M×Ñ­ÓIÌ0î@ò×yë¯ßØífŒ0—¹fî™V–ÅÑg£»‡úÞö£Ç/nXõÏKú³>3ûr¡4tLñûqó9=rm øbé7ñxÝÝl´ü}Y³ü¸éÑñ‘ShŸÈ½’ßš0Ú™R|ï\˜0%®T\ÐÑéöøòâ¦áÚ¾\CÚž+••ÔæJ]ä¶ã=Fî-ºf9*ÌG‰û½l 9îÎ-ýÙ~õ¸,¤}c5ã÷±v¦Óé%V[ÂHüåüªëAÂ|äο1mýmŠŠøù^³)¥š{ð”öjÆïïD†íÝÆ‡Q}ÃD8~Èl!«+7‰¦·çŸlU:ÞÒȱûz«YÒzn °3šr†‘aüÃ@>'Cn]ÐÅCFÒ0™õ†&vloÊq|ÂÔ,àΑåní\hü§+×d#°ÞÙwBTa.ræühb3|^Ή™É¾ù”w›¢þ3WåX–¿_Û¥aÄMiÝ:Q¨ŸÈ]¿¨»y0JŠ‹j3a ó:•Ú"mšõJ ­ßá;uáÆwãý\cؘ_˜+…Ü‹Òf7%uàÆß^ÅBymÒ£ÔlÖ®5òÑ«¤ÄGGU8uˆ?îFñ¥bòäÄYz“›a9w+|Öø­ZØ7s:Uü8Ú¬sjmÚ_’pñ.ÿù!÷«-·!îi2lÇÚp3³¯Ù#À«´>ÊfëŽÌ û –ÍXƒëØÑ[Â\7伸eX“ m¤URîË|Ú¼L~˜ÌÔùî>»<ƒ Û÷Å¡E~æº!7tkΆ¦vÁ¼ŽmìÇöŽbå“df›˜r¬¡É 2,ÿ!ç\ûeï½üë”!ǯ‡Oa¬ÝªlÛ¯+VõJaüçÜ“¸]ìÆ„QÆÛuÕ¯åßrÍw»8à(- K°¨ýj'ëÒÏU²i~ c‰¾^92Þny:ã@ñs2…ó/þ÷>|‡„}´÷°ÏM¬Ia7;˦ÏÌu§õ>EíaTåò¤Øc¾üëÔ#Ç §nHB²šg `UŠ“ã4)Ík Òôöé0jaûÊ:¾•0×-ubC©Ñ×õ{ȰÝ©½L¼uw+ÇÒVîoçt«Ô•¼­¸eÃÈîÙá©Ó» sÝkmÖ4$*aé/p<ö±åÓ뿚Qí<›Vãró‰·ÜhÝñë‡ÎÇykdvÆî—0× 9n êç¥i–å‰+²5lgċꚞçÿÔÏš ¹ ´aÄ­òî sÝãç-¢!?kW­Ôg?[Qô.9wêy¦u‹Ú°`ß4*YŸS^r%Œ.qãç® Ç¹¢×Z6ßÚ°™«-7=ÛÏTòê—Sgºâ´´{™R‘¼ð~ÈÏ0z¾Ês~â;¡~"׫I‹÷ƒK½Ùé…ß·çm=À¢]qWrž=ã¶‘>èI“ãj¸njN•¹qñþÂ\>äžLá&ÕËXKnòëA¶°Ï„ÉÞ­.û¬N§-“¸Žá´~Í‚|Yœ0W¹G=Ó?.”3~]wõ2Ûu£ÙÌ ŒŸ77úþN£¸åÆ+~Þ¤Qb©¸ß ãÒ¡8Þü¼æƒìÖAwѱ° ¬ä:·a§'ÆQZ†“Ø´gë|„¹˜ÈÅg9ëâ÷³_QWŽ}ìrˆ½³t^–÷êëûàÐ×Õ“=)¢r‚éÐqáÔ”kÛMùœ¹uvïsyÄ¢fæŒM=Äθ:‰_t»ÈV jÜæ¤”ª¬Rد^N>׃L–æb"' ìÿ¼­ÙqöVÛÅÅç»2¬¿rÌÌ‹æ£|hrº¹ÿÙpJLµñÊOæb"×·ÅÌ}{ãìØ—¢–¢ ‹ÂõéTº}'è„*3œz6æ)âß›·±(*8Åšÿ¼V·1~ÎU·Õ¹ÙtÃÆÛSÉmdèEyb8­ypÙoïTa&rü¾¹§Y`Ö1gGb£.>¾øëE¶I<§ðìó©Äí.ß`g8™äÏ®7¶‡pÜãç´œeuwuÞlYÿV:Z¶½g*›`0-%›¦›ÜV…SƒŸÖº ±Â<̤R±ÝÓŒ¸ªÍCÙHÃ…ÉA>3ÑL;7•yšmÝ#ïâI_ì ÖôÞNùܵ|N„œx®r"®mØ´QÜ$ƒìI†Äîlt*«_G´Îüêt:W5³ƒ;ÖéÜÝÓªÂqCNv¢z}Ã#Yý¤‘Ó¯`üõc¸è°ôîL:‘ssïópšz£WIÝÛÂüos¤õÈ­¼—={LXËì”:§Ã>¶~ö¯wœ`úc Î\L›#ì»Nw> {í"̃Vc]'¯-‰g{“û(XîeŸ¹ÿ·D!ÌužEϋ£jEÐDVÛ-îÿ¾ˆ‹Ø³-õüööDnuwhá¹ÈÁ¬ÍرõG†Ïê[íº2ó×кÂ7o–ÂÒç¦ù§nöe7,œq&äϼ~þ_-¨ÑþSM…0¹£ë_-C¿ÚÁ-d7±CFè÷õ8-ìsìùçu~8“DÍê ó “Ñ÷šVK;Ïnóóé0u³nìÔòâi6²uÚ®ý=¦S®dáÏi+"h°›ØkØ:>'BÎTW{UÏX×ÖÍŠÚòac[ühn{æO}¯üqöõu"È•kå|NŒœI‡7-ó.2Ÿ7ÊÞmŸ,eÒW¬ýUt†5—ä¼yÆ“–qÛ¦ŸŒÖ/ÂÕâÎK(}Vʺ€ZJv.æbÓ÷$TÑg¢ïõWtW gó9r†eF«4ö{¾ô%o¥oÁ`%{äµ;â{ ñëäâ¦Öº8ó91r^/í´^ÆzÄ/;RíÖz2_yÓqó<%ãçËK(eóÂÌÀ»´9tvñ$±pü+öÞ»6%%éå{ìó¯MTSÝ{Ûë%;ØéŒâ¶+þðy’.‚Zt?vúË >'C®Ùtn'ãtV[ýô˵Ñ~Ôs[çj_*™ß­ƒFu'÷Uï·Ý‰ 3ÜexMþýT '™<¥ñʉéìy{­¦È©N¿gF…6¡æ<ýÞW¾õÄà”ä áø!—hòHá{8ý:p¯ÌŸ~JYLÜnÔG+E1/n\‰)ο>þ ªE’¸77‰”Ïi;ÇÝÞ?šÉøóä µŽsÉtŠb‘ÜXcwâîò™R$ýPôœd^{#ü˾+rív+“qS¢8Fgã,ï+¢Ø@Ãn4Y|ë—ÑüHZöqFàw9Ÿ3ºP*ö¸SÒ©I{ÐÑÐèéécׇ¾ŒbçÚ5ÉnºÙº}:t7p[$îàóï§9n}s‡,ÆÏÕ9I¥²Ý «è?÷Aš~ŠÏ˜I¡¹Büï'F®wÉÒ&þYlé°$Ý%írXßûLôŸyó ý¸¸‘t3îiŸþ5…㇜ÃâÑ—\r³Xè×—·Í &n‡UŒ›¾<ÔNJõ¯wÙ³È7’°Hüõè Ÿ“!ÇÏ#ÏfæÍtq}Šì5˜òËJÅtïüÁOJ­5íØy$ñs~ø÷E\û/g¦l6—+'¦!¤|Vï¢h½Šµ?½hδÕRšÝd@÷€Hê¢2:[§¥Œ?~Ȭt;M¾>›þ¹Æ¯ÚçzÕèèó‰ª?siM¢_4{¹5’ÖÞmgQ<Ïé‘›13·eþùlvÑØz%E§éNL»ø^ÏU©4ˆÓ¸ ’ú¿¾´8È‹Ï],w6ÜËf[E5:ÄŸ!;y•>~¦1Œ›ºaoìA†e™$’¸£Ùr?Ÿ!wåâ6»ÝCrXZ@Qä‰À³ttë» )Ãc˜:²ÇÐ^MܨúFÔcJ$åVýn6v3Ÿ#·Ê÷ÚæfësX«=Í•;7ž£âe]ïzÄü¹ïiÝ+D²hn$©];÷ÌéÆç¤Èµ±k“¢HÍakvyœZ3_I}ç¥Ú_ÃÜó¾L“åBóì§Î¬¼!’ ã¡ùã CîÃéqXqh™Õ¥€®;\B©Ðè^åš;bXíZ[úìêäB}†yn¯±?’J¹ñÕ„ã‡\dŸÉ®í´ì÷÷tv7Kn·<ÃÞ˜q+Úÿ«RqX$™Rð9 r³ -£'U*N‘m;WöaM}±±]#!6óaZÓó‘´“»¬6ο‹¿çPhÙÅ¥žµŠ «FÍ$_Žaƒâµ+SOºÒbëé›ÍR#)Κ°#œ©¥âj‹mB*wÎeçijç\Iaë–ïið1†íËßhÃÖ{PH«ß¥DR0wºOãs"ä.gÔwnE.ív`Ô§Qd”Þ}JJçXa^†”>¾2~ö…HµýàÐùœ¹ Óüv™¹Ì¸êàÀNÑ”þaú¥…sbÙÅÇÏs:}ñ¤¬ðˆ…;rp>ä¼Yø5ŽÏI‘Ûc´¨i¯¦y¬–E÷©=T”&+v‰eÛ}÷WôžA]¾‘×*Фk³mU¦ ǹêGͼòØZ»AÓ^^TQa´ñ”%Õℹ13èö=îImJÞNóŸrç¯ÿw1½ÂYhfCå{&Í4v‹c×SêÎc 3¨a“°£þ,’ÆXöð¹ê"œȵv,œÝ$Ÿ¡˜´±¹C7³6HKÂãXYƒ§MS¦SËZ'#·l‰$î.K•}Âù‡\Ç*â&峃ո ÓXÚbeêþ.ŽmX|íäOâî®wÁñYv"¦×Máüc¥bùææ“UÙù¬yÙ­fºÚq4h»ò«Ýãÿ¾O#wîë͆xÕì7 Î?ä–ÍrÀ%W‹Øë1·yhÍêÏMŠˆÿ3÷ÓåuµÙ~íÐoG.l1z„pþ!ÇM‹·ØPÀ&Tæ&ÁÅSå±%IòxÆÏõ WÃ…t$ÝÙ4øñÕ]Âù‡ÜªÔ“f½î0þ~[<ù–{=”'Ä3~îÒT:*z>Ü(4’Žæ×Ý:8‚ÏÉ{^µ_ƒC/±©vÚÃŽ&ÓuÖ÷;ñ,ïôÖF-¤$}4k|÷òHa»pü¸×iø÷—XÃ-n‚8)® xeZ7Ùë:m›0Ø“Z|4ÙõZEQ'Æ=ÉÎ?ä~u¾jóKlw»ùuÏ|J¤víûÀ,®{âÊ:ÉÆ8*õŒ¢$n¬+Î?ä¾qå{Úe6³¬dDǨ$ZºzúΟ[þÌ·ïÏ} Ô&ŠfºÍþä*?M©xQ“LñÔËÂzEM)õÖÜ~›ÿ{ã z\喝 ZýÿØ»óà&«öoàUƒ „E R e 6 ‹^QƒÙÂÚЛ„E{ØÃ"d ö°4 ´´i¡=iK!ekÚ²h!bÄP¶ ï÷NŽ}gÞ÷wæç;þæ×gæ3ó rÑåN¾×¹Ï9wNZßNš?óë‡:¼k6‰Égm^~'ú£wÒhÓ‡þ¦ªê)lôë¢Ä¥ïÅóóxéáŸç ·›Î\?Ô½¾±Å± ó™ÿqÅe¿ÞK£›ç¦LïÚ/…%Њ]ªÌ8ú¢p²_"EþýHu¯~\o€ë§|掗JJ§ñ[j^ˆY“Â"ç%Ž øÔüwÚîJ¤FÂmqHuó§-­U¡±—ÝŽK^|„ Ô6Ú™—R6?ñW~þç÷iEݼQÏ›÷÷²È}åQÚÜòÞ³”²|©wôVÿ-‰4T˜¶k©s£nå’•Ÿí]äe—WÿÒ™É2èû}êÝjšÊÂÇ"ψ¥Â4Ñ©-µŠ‹üý‡ºÜ½²[&{YÅðs&éû7zEÛ3•=ÝVhÚ8g8UÖÏ|ë7ä’|Áñ/¶ç×/ ¯—ÓÒ7¼ŒöŸ^{áZ&5Îö¶›œÊÏeNñ]…•‰DÊî­d¿ñüDÝgæÖYY½€É…cè2}8vXrõ©,2_K›+Tœò0”H¿õoQsî^žŸ¨‹œ«ZÀ2ŸV©YåK79ϰÿ–“Ê÷ihi¨°,mçë IeÂ,O#×jžˆ¶Sø˜¯züý‡ºá“¶fíCyr§,2D8ÖÅ"çzÆ“¨ÿØÇJÞg­øû‘ùöžnsfQFß—žt™çbÞÏêLzI—@Yý·*}ÍíôTÜï›áUxn¢N”÷rSý>n¨i6…†·éV±ÐÅ"ûŠFÒ3åB]«NƒÆfð÷ê&_®F!‹:ÞVø!›*W~µõYË ¼ƒFÒÓLW¿]­ì´c­øÀ¾ ¼ïe_WÖ;v¶V§BF+Åõ'DçPžËÒÕ¼4¶¢p£<’Â/»Æv cs‘_7ÔE^…,2“Cs:¿Û{eA[”VrjÎäR.Ëߢ}‘HwÛü±§®Œç&ê6V¿Z‘±™ÄŠǨahÑê uÓËÖ *i+4<–HƒÃ <7Qwp÷!ÙÝí…,¼,½êõZxmˆ&½lþ¸øBmÜŠ&Ò»ÑÂo_7ÔÕ Žk\9» w 檹ØôÕ\ÝŠô²ýZ­'­žc™uŒ[Pwmbò%× YÚÄý†®Ê¥Y&1¤¤³Èyr±”ëYÓqýôDòÈòFO㹉ºÈ~BfÜø›¿YÕã´à`Î]®¥³*û xb©¾DRoO¤/žÝzþË$~ýP×<¼¥ˆ©ršOyðÍqº1-§ú‹×°çÕœ~ݤ¥‘õVÙ K雹í¾oÓ_¿œëÊÔw–Ý]زˆÍ¸¦;Qå¥ï-þáaÏ^fdG”½øScŽã÷ ¨KlXäý¨ˆ¥>ýâ%ó ª¸HZ{ÖöôÑÇãú8Z(O•ÖÛÎÏ=ã÷ ¨«{þ¶òBß"¶@8.Kì¡zÕ·8v9Â|­„ƒšâɜٽȪ³ÓìÃ;ŸÕ‹ÔiQ·Ìpç+#‹X÷­wžéͪ^#ɶñÅvT83:žZ5\žWW‹¾ ¼]öðû>ÔÍ­¿£ËƒÉE,¼,P3~5pÜä®Gù~¥8úi㆜{í$¬6¾iŠü|VÔýp@b¯j,bGštÔü.„] ŒGÙ7Ÿ +ø#È”¶sì‘Êö¿Ý¿¹Q§¸(ÿøÆ²"öz’÷ì˜Ú'©Nx#ÆQöâë¯.¯,ÒÒã&u«¥`œ9G’_?ÔE§´:9Þ\ÄÞM\rìƒõ'iÊ¥À÷?\?Ê÷qÅÒ¼j¤%R5—ÝRýjäûŒ:v]©;•Vkàº"öè‚{@åڧ蓽Sžm®”Á"¿¿a$œ’XÓ•HÓuš•éøõCÝýwߺ¿ÅRÄ2¢žέ;E_?´ËÛ6Ì`[2·:½;„N•~]çyQ"½^øá÷í¨ o›{ó4É:(ÔíÁn?èóéШÁ$Ìü{"-¹%L@ðû=üýkßéóx}ÛÓøŽ~êºÓtikµÅ‹»g°Îo ‡6¢Öá‰;µý¦é[ñZ~ÝPwhž0A^Än ÃÙêg¨ZR—ävC3Øûç½;˜"çšÚ©Ýè9'[âóe¨ÛîºÑsîŠ"ÖäàÎâ9«ÎÐÛ£ª,Ü:9ƒý:÷Mÿ‘3CÈßp×¥.vZ|vÜÝçù| êÎÐïM{.,b Óç¯yùõ³TXk÷ÚÙ«3ÊæÙÂq¯±“pZÙÍê|¾u]g=ºï^Ħ훯Zt–<õîë|$ƒí-îÖêL¢–:žlY<ÍÎÏkåó-¹×•Ò_ç-b-«üa[òçYºšTyÅO÷3Êæ;…wé5“d ÕšÉù| êpsb}<¤ˆÝ˜8kÁ±iù40îi¯fí2YøXÔ¯âÉqO3þ»åvš\²¶tpeþ¾CÝ1ÑIw»ãu²]8è5Ÿ.wÓeüœLæ\Y¥änµ2¾óUqÜ\;íþ­}Å##ß§u‘ý@E¬zŸ/®FµõÒ¨oΔæÉdámY—â©öxáÛNÉ:~<éŸïDÝ´Ü•ÁÇÒ"¶W]2ô ^šÝc­$¶cÛ ¿ù8ª8yY£jÝìü|6>_†º#o¼¸]éµ"6{+Mè·ÇKûw<\´c¯ØöŽ7ãaÙÛ¸OÊ›xx”_?ÔUK_ôñÄû…lö¹X‡Óï¥ß';UÃf3vuÒòU7Kc)áëý?ï|’Hµf¬™zæ&ŸïDÝ«wÇ–®=_Èz7Ìf¢· ¨}n+ù×ÛsWüªäLßatk5I ÷©ÿ{¬(áx¤.êøueTx® Ï|4"£O]‹ûv“;“•½ÎÆox?z}%;=\¾§þ}¤NŠºWS>w*¾/d›ºþÜ~Ú²NO;îc|=`-òmıÓùüÍËûI"uJÔ%î¬5uV! ÿË (ÿŠï®êc÷>:¹¸ù0ŠÜŸÙi`åÚKÛ2>߉ºÈ:M!{Ox»?* Q ~šó†›µ¦íFî‰-Û·öåké;[óùÎã­²/=.ØÛ¢êZÛ“5þk~w%ŽKŽlÃu¸¹¨òŽA|½u´“Û¡V!+-ru`# é Eoï|¥»lÜ?)oÒž©N;Eö]ñõÔEî? ˜áoÿl¹¾BÎÉ9ŸkÝ,²Gª^ý£vz|è×äö ùzêÜwÚ" Øón#s6,¤{ñZƒq»l\•"LsåëD'®+‡|TT£ÅŽæ>%l„+¤ËÊ7×4ÛéæçÐÆÑ’¼~Y¶vÊ)j1²ÎõHu¯ü¹¸~ë™ÌÙiòζò"Ê—|”y-ÇÍfÇmyçZÛ8ê䌎©0ÃNÃ6Hý¯3 .²ï¢€MΪóÇŸqE”Ú§ïüî~7«ÐõΧº Á¾¢ªózâçz}ZNÊ'|uIÝFoüDVÀ·늨 Ý«.}ê.w,Nr*Aa§„åÐú|uç¾?ê©ø‡—5NmZ²ÞSDI=^»^Z5‹59»Ü¶ìÃX:ôý¢Â·ºÚi¢ÕÝùéC¾Î‡ºŸW=LjÞË®üáxÆÓ"š¸¬Æ;1Y¬ciÓ»'¹¯Ê•Ú#íähTõ’2_7Ôµ˜íeÒçÍwΑaI¬upÛ,öC—–ç6ÝF5G]~¾n"ûùuC{Øã;½¦{Yý÷Wo|}è9ªT¿ùxõ'YlîÔ_NŽž1œF´_ï|ÿ¸â+ '“FÖë¢<וõ*ÖKÛÝ˺?8‘üþ7çhW²ªqÝAYlÓ+—ë}ù†–®¡K¶¿m§;u-ŲC|}u7ü ¤—}æQn–yŽ^þ ~·Ù“²Ø®1êvÕ´#è† +BvúT–²`w¾N‹º¾³ªO8q.Ÿ}²%mé¢ßÎÑûmi`Î*{}uªßV]ç…LÕ… ö|u×ÚÍ™æþ:ŸÍÚ|ƒóÔ¡Ömkgñóªø>m­;Üó¾Ë©3¢Î”^M9¶f>Û7Gø—ÏS­•¿É×\Ïb‘ó²GÑþѯoå Í?›foø2ò{±¢.<ü>x– «)óÎSïåÏÚ}W+›¯G“0+ÒšôL“5ñã•‘:7êD„ij¬¾0Œwœ§Ü>ý?«²Yx»áöÑôSןbOµp¥—°q?ò}úQ×<¼ç [œ/lÜ8OWèüwge³è{ðÊE}ž^,=ù÷ èJ5ïñýyç$lPmξ^všÕjèïËfÝÙ‘ôÚÃe]º§ÛùúF¤NŠºÈ9ägØÓ7ǹ<\ ð®Ô‚l6îâ¢äãÖxš»'oe*^goþ¹mc³‹‘õy%ê*>U³ÿ4kÓñpÛ/&\ ÿ6™·Y)²~jÓ ûî ~Soxa¶SLç‰ñIøþÔÕl5£K+:ÍίY5Y¾ùüñöºCoä°î3×s³µtJwóÁò=vŠœ3©3¢nNxcÞ)†AÞ¯I'/Ð ê6Ò8‡¥ ÇV&hi¿²U¥jEvz!7¾)RgE]`áÈñQ§Øâfÿ.tj.èÿ¨´sNÙú„ç× ½¼á ‡í´æ‡c"unÔêñ¼Ù¼''Yäœò‹ä¾/Ž©< §ì¾æ}a¹çµýR8!˜ï“@ÝÉk;Ž/:ɶ”>Õô»H÷ߪz­ÞÔ3@Ø)Gsë)Fwã Æ³mŠéÏ÷IœÄ¸Ï€Øaožd½ÿi­ñ"m÷`pÂú¾_=ž¦lùÃAÍc4h™|ê^v;lÏcÇÝ/j¿Hò~9h:šÃpóª~{lÍ\YzúÔEæUøþ2ÔÍéò°$±eÛqjsÎáËé£ùÛ oä°È>“jÔã–æjÅlW:#—ï/CÝEë{¦yX¿¦¸ÒJ>ºxh\ëw_?Æ„UýïÚ&P̺¸Ïn¨ôy׆nôæû[P·w[w¥nÖuã§ß­hí£ çf÷’cáÕ˜¥ñÔm÷lѾO40él(ÓÅ÷¹ îp<êÉì¦mÏ0Í\u§Ù—ÇXí«g71ÇÑ'á_~¾&ßõíœÃ÷¹ nÌÄàâ¼~'XÕÞŽµ#M>:y¡›óÈr;?‡}µX>pЕZúõûQó§-å×uIoL:°âÊqölè„Üç‡|´¯ãòŒwíüÜw-Eö;è— y—¶á×ïÔue¤g‘}…>ª=ü»yÓ»8ØD_ì´ÞéñÇ«G‹tuM´s¿~¨ûä³ÉO«ÝÎe=ß\ùù//ÑÖ+î}>ßQvŸñäà‚kK$šeútº¸3¿~¨kWkúѼ‰¹lÇ KÞlu‰NMÝ:z{¦ƒå/P_=Q0”„M[C>pÐ2axü=ß_†º9Ÿ\_ñø;}X÷Òå—¨saþòá8Xdýq}< uf¯™ÚVXˆçûËPw¼—¡r¯9ÇXkm^ÿ–s/QŸÜ@-Šq²È~êá47êËÍŠ4\÷amV­¾Ë÷¢®¥«AÐuŒå”þ|eÁ®KôpÊÖ7n÷p²VŽ¡7ïÕ’j—¯@qÎAo¯Øô]߈º¯û[k/\šÃîÔÉn}êž?£Q¯N~L!:ŽÆDMœ_1×A1'ßJ6ùþ\ÔEò/‡yÖͬ½¨ô­éõU“Å9NÙ¿O—f.ÑŸ>ä ßó…#øþÎÓוƖݖºWg³]¸»ù¶öešVíí×û‰²Õï/”ºzë›ï²:¨•è§9Ñg"uRÔ…—=$Ùlа¬Ñå2åmÿ]’‘p GRÝ£Ž“‹O:hé„mê&ðý¨{¯íøµ6g±Þ'Õ諒LJ²giÒ²Ès2 d}_Ñü’ÅUÏ©ϯê Þ»é© ³X‹S×Zt™¸«ß»VéÛ?¸°OÕQñTµGÚ‡÷û„ð9ß_ºì ~œ±×ͺùU֬ݗÉsìIµ¶Ÿ*›˜|mìW´ƒ"ë…|.ê"ã27[6ìÊ•Vy—)áë…gꡲù–!·©•ÙïÉŸOAÝÇOëîL`l[©ð.S¸]n8ÄöÝ[}¦ÿƒ¡4¦Sç:ÑM|}ïÏ=ý×s™,òüÂjil^áÕäClõS¶Æ %·]8XÝAëc+ÅwÛÎ÷WŸ¹®œÒc̤o3Øômi½µºB­Âÿ•ãït¼=çÆ8% ˦WùþjÔÕî9ºÙÂGÙ«EòÙݾ¸B-ŠKW¯}xˆEu:½á-m,éÃ;ÏôhKìZ+ãû«Q—ÞK81ú[_Q¸ºBíßhP½oÃ$ÖIZùÙ´#èαŸ ¯lwP¤?ñýÕ¨¦¾ÖfÏ©ty~à [—4ýÓ!IÌ\ãHJê8:.o¦û.¯ë!ë§‚üíWh战æ¾öÉìÅ­ÜeŽi ´aõÌÞ?%ãõyaoéøûu‘ùýTö»°ôçRß\½0™5 /°&Ðò 'nvÐÍŸÕ7µŒ¼®£Îâ¾ö÷†íT§°ð²ç[ÅôÈfoSùL2»¹ÕŽ;ïxú¾çgQV8Hýí„:úõ|5ê:/Ÿr®`Òa–x¹}LbëbªÞìVµ£âì¤Òák——Åѣꡜüyª7°Ñ‘Â6üý‡ºðvÝ5ɬexC~1ÕÒŠ¾Ôã0î:jlAÏSš©ÆOÇûAXfû*òóiQ'ìæ«›œÄ.mßŇ¯÷V“o¦Í?Ì&?oŠ—–"û ””ÜwJZß:a´wñÊ!¶V-<ÐSLï=Èîú¥ó0“l4Æ6ꮥ–ª¶x):(²_Ž¿ÿP'¼*ÖT8ÄzU6uw™‹éåuëßÏ*þëwŒ_¼~>Ôñ÷çÃP·ºÊÎ=[ OÄ?ÿXqw1¥ÿ–‘`¥°Èü­–vÞ<ß±R‚ƒ„ÕÕ+îÈó7~Ô̱<|ò¥“yQá@ÿ#Å´/éÅîhE ›Þथðð`­ƒº´°ùÅ@þ|_þue•ö¯uõÈÎ6÷kwáùÙbʬv ÷Õñ)lâý ~¢T”™Æf;øü>u—õš·C}Ùk¡ök*þXLKJ¢ûFHa'º¸zã‹x’?¨÷DZ—œ”ûmëé­bøó)¨³Ø}g™}?K¾-òº_LýK¼|öIJY^×;©~³ïmü|,o?ôfäëiQWyÏ+ïýþÚ>¶"gcì™—J¨sýU•WªSÙ–‡™÷E/…öô[†þP)ýñ+³ùó}¨»p½oÔÊÑ66©Â‡´R\B³Vö\òaR*»õõ±˜ºªÑTš´úÎnü^²Ç¶1ae¤ÎŠºÜo}ܱ`7øñõú=”nÚ³WÔq±Õ]…~<šŸ3ï ¿ö}‡¯êšî™¶do«]lwÚ›ø'Kè¸ùÉ+½g»XÃÚ.ŠEÂÓ¿ÑC0žˆÉZÖ”ç'êÞœÔ)oÀ†¬Âú¡¹×;”qW¯Í§‹\¬ÛûæwßOL …½Dõ¦"¯#ëБßg”÷º2²®´}ïž:8õ£Úz©òò™ÑilÂÍôæ?Ž#a51ö´ÂÃÉ'‘ŸOŠº‚i_”¬feíï¬ümEºQa}êÜþilÛ–¢–Þ{ZzkAôZ9ÆågŸ™|)ÀŸCíùGmaŠð‚^ Íh©³za‹¬ ÅÒ¶Їµ«9ø|5> uKÖäíÝ2b«’[obéÚ‘Ô¬xÉ´²ç n·ÞHè+áç†øõC]|Ó>ï…Úm`ƒ.ïM+Ö–ÐŒZ¡sÏ.¦•­×…§•uúöÅËßà×uc:wkÙ¹‹ÂÄ%´rÝõ‡ôZzÙýíÚPqåÇ±Ž¿=çìFÝg§æ×{ÅÌ"ó‘%ä÷îÊ»Ð-ÙG‹N´œW6NþÛóµ¨ëžYCµlØ ž{%tkû8ýø¥é,sgëÁŸõ§ggŽMð÷ßKT®ß­˜¾V\ÌçKhN¯¯÷ÉOgó–nWþà‰§š©¯ÅYÑÿ"ëçüú¡®8ðÅå~sئ_òþØ1±„®}Zkg¼ä«-lSOЧã=«´ûaŠƒ?WÏŸoGï¿lÈþ¥3¬±G˜÷ž9ÎØ-ž„ÝàâÁ¾ï@O埑TþIƨòÏH*ÿŒ¤Çg$ ÿÓñׄð{þmÁA!¨´`/ˆZJ0€ EˆiÀ h Ѓ ü AÀ©Án§+ø@ŒðS\ÂP ð‚Á¨8!R¥Ìàá¡©=ØÀ„¨Là†Èª:°‚ÄXÁA!pµ`oùç$ý㟓ôú<ÉÿŠÏIú'>OòŸþÜíãçIþSŸ“T~^[ùØÈõ?clT>.úw‹„œÑóë-ü^„Z5˜À !#¤t`ˆX*0‚ ‚ C€iÁ^!Ì”`'@ŠpÓ€<<è øA‚oH &pCäBXÁb„¢ Œà‚ È’Z°€DL%À "@5`SèÁ~ \Õ`7„@ްÕ| Bð*ÁN€A¬3xx(+@6ðƒ!­¸!r„¶¬à1\FpAdt-XÀ "„» à„Hö0ƒ‡¿ô`?HÐÔ`7„@ŽÆ +ø@Œ&¡#¸ 24 -XÀ "4%À JñõÁ š‹ô`?HÐlÔ`7„@Žæ£+ø@üùüH ˜ÁÛ”ô`?HдÔ`7„@Ž&¦ë?ôÙ‘ÿôglÿ?;224t-XÀ "4w%À ¢ÙkÀ Þø øA‚€Lô¿?7R×Ä”þŸÇGåc£ÿ~c£òy£÷øHÈ¿žÂÏ,ü¹Ìàáá¤=ØÀ„•Là†È^:°‚Ä2ÁA!Ø´`/ˆrJ0€ EèiÀ € Ѓ ü A ªÁn©+ø@Œ°T\ÂS ð‚Aª8!R«Ìàá!«=Ø@ŒÀU\X ð‚a¬8!R„³ÌàáA­=ØÀ·Là†Èä:°‚ĸ`*0‚ ‚ CÈkÁ^!ð•`'@Š 3xx3P€là šƒLà†ÈÑ,t`ˆÑ8T`A&Å× xA„¦¢8!R4 ˜ÁÃŽô`?HЀT`A††¤ xA„æ¤8!R4+ ˜Á×ô`ˆÑÈT`A†Æ¦ xA„&§8!R4= ˜Áàôÿ>WÛ4V5˜À !£ÑêÀ >£éªÀ.‚ MX ð‚ Y pB¤hÐ0ƒ‡7kèÁ~ y«ÁnÍ\Vð]FpAdhôZ°€DhúJ0€ Å @óã"iTù¹#åóFåc#wTùØèß06掄÷º‘_3áç’áÏ´`¯ð½ ¤”`'@ŠÐÒ€<<À øA‚@Sƒ Ü9NVða§#¸ 2„Ÿ,à‚P pB¤F ˜ÁÃCRz°$M5˜À !#Du`ˆ¨*0‚ ‚ CÀjÁ^!l•`'øA‚ðUƒ Ü9ÂXVðÁ¬#¸ 2µ,àB[ pB¤q ˜ÁÃ]z°$x5˜À !#ðu`ˆþ*0‚ ‚ C3Ђ¼ BcP‚œ)…ÌàáMCz°$h"j0B —âëƒ| FƒQ\Ž,àš à„HÑŒÔ`7„@Žæ¤+ø@ŒF¥#¸ 24.-XÀ "¼°•`'øA‚¦¦¸!r49XÁb4<ÁA¡jÁ^¡*Á6ðƒÍQ &pCäh–:°‚Ähœ*0‚ ‚ C#Õ‚¼ BSU‚œ)š¬Ìàá Wz°$hÀj0B GCÖ| FsV\šµ,à· à„HÑÈ5`oê Ѓ ü A“Wƒ Ü9š¾¬à1*0‚ ‚ À@8±Ãüã$a|Q>N*'£ÊÇIåã¤Ç8IÁ_onþ}Ëñg:°‚OøZ)ÁA!´´`/ˆ`J0€ E iÀ n Ѓ ü AØ©Áná§+ø@Œ T\‚Q ð‚!©8!R„¦Ìàáª=ØÀªLà†È°:°‚Ä[Á"|5`bèÁ~ ˜Õ`7„@Ž Ö| Fh«À.‚ !® xA„@W‚œ)^fðð°W€là Â_ &pCäh:°‚Äh *0‚ ‚ C£Ð‚¼ BÓP‚œ)šˆÌàá Ez°$h0j0B GÃÑ| FóQ\š‘ÌàáIz°$hTj0B GãÒ| FS\)ššÌàá Nz°$hxj0B GÔ| F3Tœ)š£ÌàáRz°$hœj0B G#Õ| FSU\š¬,à® à„HÑ€5`oÆ Ðƒ ü AsVƒ Ü9@Vð[FpAdhäZ°€DhêJ0€ E“×€<¼á+@6ðƒ5˜À !c@ ËŒ“”Qåëlåã¤ß8é¿Ë©||ô_7>Rñב‡_ Ѓ ü¿…pRƒ Ü9ÂJVðÁ¥#¸ 2™,àBM pB¤9 ˜ÁÃOz°$@5˜À !#u`ˆŽ*0‚ ‚ CXjÁ^!8•`'@Š Õ€<ô?w|¤á¯áw*|]%À áï"¤4`,èÁ~ ÀÔ`7„@Ž@Ó| F¸©þ{ïÕ¶µicÆŒbÀŒ3fŒ³Ì˜QQPФ…3fÌ ¢¢""É* Š" HXE.•ŒJ2 0cÆü¿»öÖþNÿãëñ÷è¾ýÝñ·gŒgŒ{Îå­Uµ×^s®µöÚóÎ T;1ðy@Oœ€T}BsàÔBP4Ž@ Ê‚¤ p*P 4%ÀPcà bA50@@/´\EÀ (AÐG°5n@ j€!‚¯ø‚b ƒ@l œA,¨Ìbàò€6‚´8%¨úÚæÀ ¨…n”=tàT "ÀK€/(:öÆÀÄ‚j`€à/^ h#ˆ€P‚* Ä`Ü€ZHFÀHA9ÐCÒ0.@j€!’ˆø‚b £ö3ˆÕÀ F ¼@ÐF²' U@ÉǸµˆ ø‚b ƒÄd œA,¨HTbàò€6’–8%¨úHbæÀ ¨A 0DR“_P tàŒ3ˆÕÀ O ¼@ÐFò' U@Éи¨†HŽà Š¥1p±  qŠÈÚH¢"à”  è#©š7 ¬pRPôpM€ P`ˆ,¾ è g ª’³x< D-N@ ª€>·9pj!‰G å@Iݸ¨†Hòà Š¾1p± ` ^ hc2 \¥Aé˜'‰µþßïµÅþwÿÿÿ®n‘ð™ù"±çD5—ñu |èŽÑ…“ZkïÑï]X~³Ž_üŸºŽNÛF›yÚPæµØõ«‘ßî¶W—÷ £¯\ùœS.Ä×sXO¿ÿžkÇíü®Ôòðjýe«ïÑãoÝïdz~~½¾îô´¦/ÍŸÅ„Õ £ç/¾œ!Ô½‚nãÀô…ö6SLãìÍíîQÉ_öµi™ðÇß¹ÉcUÕ;=ŽKjÞ ¡P÷ :ÞOa/qUË íîÑÒpöÒglã}§-IÑÇ­dñm>Y«äÅ=ÁºéÎç—\½s˜:?Œ*Ÿbu¦h v$ü©äŸswRÊgEÏ×~Ôršà+\P!¯1L8I£kÇ.N0¿G|¯–uµðŽ‹Ø‚¤)^½RPÐÎpG¨ݘ]õ­<¨ï®Ró=jñЪî‰s ÌäÈ =3 r¿5ñú£[ ê|OûØQ#Á—ºçÄ£Ÿ¦…)ºÛL¿Gšr+1 l£Î¦¨àžËÈsW)NAñCæ¿‘ñu1ÄЙ¨sÊŠ_z‘óô¾ ²&Ü£»õºŽ-M`¼}¬%—Œ z}CA–¾8vàªàë ݉B…5C½©Uƒ¼7ßFÞ£g>›Oœ©“ø§î‡å Ù-Êq='ع÷º_Kð….øíÏ}‰ø:Þ÷H'+¿òóˆÄ?~¨|]÷0jc"½âªêî@×_S0é2-ž3ñYã®÷hø£)Ó6$þ©£òÁÿ¡gÜ-ž+OºÖW¨ÛgûVןŒTY[«uïQþ®°V"Û´3ÊÈÖË–¾5à*`)¨×°éýûlê¶Vˆ4eŸ®Фö­Jêß#Á€É}-é˜s+ â}r„º;ÐÍo¶\WlD–šgÿ¸]™$§¼ø.ÔM‚Ωo¹ÏÓ¹!tâU´ï猻tfzêƒÇÅIŒsÝ‹‰ZBÝæIv•ÿ£Ž/tY;Ðz-9u™õèmÔ]2m2µìéÓ$v/Ïóân3ÊîôcP‘Ÿ\¨¿#ôtú¹|U*ˆ¯k—ÚQ ý2‰-OÔ»qdËBÒØ <‘ïï.Œ?èÄϟΞÒWI¡£D†Ó\ïÒÁÒžâ¾Ðiì¬;/üSW[cGV&Œ¿[¢£§¾u:Z;œ¢‡p†¤ÿ­½…Jj-$Má©ï[†ͰÆtQc›¿Î) 'Þ'ì.Y麜¼Mbçörƒf$»4²µÌPAï”&«Îoêî@—ÝYRïPhqî ‰³ïÒú躹¿&1QÙÊ;í˜Ñ&뀶Ñßä´[c`!ÔÝN.Ùè]v ’¶úõèQ0â.u9~ò¼}=ÆžO˜ÐgIœñ¾Vr¸ñZÿ^#„ºeзàgÕŒ”„ï|—²/ºWÒ”1®zZwïEäÓpvË™7ä4,þµÜx–Ðн}ÿ`ÑÀhÚ–2:¤Þ]jþìsd3Æ9¿±ÎÛ”í÷ÉΕÓÃW´Õg…ºeÐå{Ñ~@ƒ«Ô{¦üs‡We4qúH§ÖÚŒ©ÂWäÚ¯œO;|[ëE\“SÈÄ:᫟ãº]&yT|•xÚ2ò2³ëù9‰ñõÔæRRÉ“ŒäÔ´ý×Þ±æBÝÀÛ¢Ö×Íj÷ ¡Ë-‹+bËH[kèí{I,éPyìñ 3(q£Á¤…³äÄ×ÙêB70DÙ®|_,ŸœX^è]FU¬m¼b“˜ó¥õEÆSéNèФAåtp‡ýýàBÝ9è~ à*TÅ‘ÊÒ:ù‡sÅt»søü‰ßuÊ'QϬ¨ýúÉÿ‘ÄеÕYÒܪ×5Šu›æØÝ¾Œ""º5f‘Äx_ô dòkñûÊArZÕ÷XÊÀ£üýé Ỉ^mj®Ñ+_ÊÀieT¯øå«1ý’„º^èaƒ§ 2ûÊIß{n§O!~B7ãVãñÙ×ã©«ûÕ·½û–QëV!!W¾%²¢CG×ÿì5™V¿®Ú×FNšrjfÂøƒ®™EÇÓhÿ—~vŠ&eô2¦Ã³ð‚DvßµlÐ¥PcRì)Z‘ó5TðÆt|ÚDÒ”/~UJvß} ½¢Ù÷5sÝŠ_Ï¡¬Ïë]zJ¼_­P7ðN…hŒéÍFIdwf\ÃÕ9¥´Ð®ÝáKˆóW VE†Ï§3g¸Bw¡4_glRâ*¡n tŠüÇ5bTkö¦sÃJiÔÄ£í—YŸúÆÃöv[Dc:. ŒJŸëpÎBÿA§ûnBîòÛŒÖwhZ>À­”Šü´( NdÛœr÷>Ö^L+]ú~ow?”l9Ûz¡ît'ëW·16Q‘¦|ìºRòr®»òNR"ãý·—R3Í ¥qqA¢å+„ºÐßêí×=NE7¸òÛsJ©Ìûͬ¦%‰Ì@SxÑ‚®– VäJ¼o¸0n;7ÝèœL|}ÑRêiÚšå¼Kd|ì%$jùéUn¡Ä¹˜³æ/Ðý´¹j·ð@2ÍÝ2Ô¿KÓR²œììT»~Û=oßω®æd]áê—s7„¦¹Gš¿C¨Ý×&Ò‘þO’iäCËs1ÏKÈýU×C)M’ضN)ö iòðž‹û‡ÐÝikg_ Œ¿"Ì—Ú~2rV u™uÔu®º„:.ÝdÖA;‰]è$Kzl>M‰½.r<„rý+~” ýÝmO»N')4ìÒÆ˜%4Ïf縺_Y¨4yX“.óè”i×~ßj÷k|eÊ^¡ÿ [ì™Õ­U*™›s•!KhâÕž%2“½Ù04_#'„øú¦Bþƒná¬ÀKŽ[RI”za¸“U ™Ý(jD÷™v¶bG¿– èØšùùæßBþQ/Ò:¾Ž}*eEôÞ›=ª„T郬Ñ/\ ›˜oXHY…q~ž:¡ô^Ý¥ÉØÏÂøƒNêXWÖjLilVZ—Ðýù-µï'²›}j nhFyM*êŒùBk4†:|œWA×1ÆëR‡óid²ñ]7ÝêbúìÚwÏ“D¦±a/0£nƒ.ïK !¾Î8ß^9tm­npøžFaÛUcínSvåíû³Þ& þÂf4ëÊæ·v‡üciWˆžlå&é4vå,—¸€b:ÔZWt§&‘]Z²kóBßEtº·Î–£ CþqêC·8i`êžkéTĸBõÅTü¼­{6úÙ:½\nJC”'+Ï¡[·;áÒò:tS.Ûߣ6´¡õ±­S²K·éÑ_'w>iÊì¡$6aÇÇ·BÿA§±'ÝœAÍ4²˜â–Ÿm•ö8‘Õ›ÛÕìùbZ6ôËò BhfÝà=:×…þƒîçyθ7ƒZ~6ÿšW¯˜ª½ –OÈNdm˜Ô¬î4‹Œ¥íæOyL¹²êw„üW­4·&õ|vþúȲ"šÅžÕ®#ÿoqp4¥¯/¿L-N¼Öß$ä?èfýH̤ïÇ7˜QD·&•hµ8œÈFlýÀ,ŽM¥_]zÏÒ˦±_­S ùºF\¾—I;÷CEt¤ží³ªe‰,`(—¦_·4øõµJ~ûX¨égǰ´ˆz,å*T& ó‚iÄûSUâàºZ™ÂüºiU+žo>¦&Mä€"(mU.n•ÈšO3{özôLªüûãÝõ`šúÆT5.W˜B׵Òr5ù¦=£üu‡êÞNÉÿšÀ8WøCóLˆ„-­›LštwTè?èU—éT¿Næš‚¡whOÈü.¯„ºz H“® þmBÿA'•r†š× ;ä±n©{‘Àøºð‹èûˆ4§ì`*Uä5¸Y,ôt_Åí:\+¹Nú“2›õ]‡.ͳ“ø9ÕjkwóÝæÅÔêU߀ïÁd*+©#W ý]Ztiª¥Á ê¶vNÏ:¢;ôd~-ïýMÙÚƒÊö3Í—ÒsÏi#Sü‚i¢Õöoó…þƒN¼õÚû´7hþÞSkʛݡî·Âß êøÇuˆÇ¸‰ÙnÁTÖÊõØÊBü,­õÉÚáírƒn'^ÙPz›Ú|ZÜèÕ„Dö°©ƒi¹ÝRj"òßôÌ*˜úŒ•l¹·TÐU,æ ‰Þ$¾¾èm2I>4ËÇ,‘MþùânÔbÊ®›w¨]0½í»êBÅzaüA÷)¦nù¥ù7é%Ë™}aýmš^PZòÝ.‘Í]<»Hw™y,18ç]*£7\™àŽBþƒŽ÷•½I~·¯·;:æ6½ÚùhÇ I";ŸWö±À”4¶…‰2:fª·T{ƒÐÐ-jbÖ@t÷&ufëí:·éy†xeµmâŸ~ß1+`Ø…LÙNöË‹x-ÔÍ…îgßøë²èÀE›ÛúY·Èýì^º‰ìœÎÆé··, S—ie‚§ÐÐm«ßô‡®E=™ô)Ëãýʘt ‡y"»½s®^H¼¯O0ñ¾EBÿA§±a>Ÿ%ÔM¼E³îææ4[–øg~­öŠ?ãÖ<˜ ÖY5Èi'ô_Y…HSδ ‹”FÞ_nw¼EgS äv«Y ξÌd }âŒjƒi¤éñ÷#/ ùºîÿÊl˜MûÖ?¦¼‚_|gS©¹6õ,¤Ë»æõž’—ÈÚÞbrä…9q³¡êƒ2²oWaÞ6F˜¿@W6¤¼ϛٴ¶ãÇäÈ ÛâD³Ëõë‹Ö‹¨Q©¹®kdÄ×óæ/Ðil,>fSÆüV  h¶ÙÃ1wžõÌòc>Ìß6&ÅAFÑí¹ˆ.ôtܪU§Su>^ÜÛÙ¦€6yè{d$ >9sÉõdˆû2èâ{qVÈw+D 5‰9ÁÙûv) ôsÂF…&²v?óG:Ì •—߬µ—ý£þ´>t¼Ï_mܹY¿»ùä¶§±Yè¡DÆí‚µ4•ø}Ù?ò˜ºœžšÄ•Ÿ=›O†§»7\ûÚWºÚ|ÁâÉ4e«±-d”þ3bÇ;¥ÐÐeî5»yñ\µ4÷éÑùù”—»mHÀ DÁWn2½+˜m£c.ûÇýâ ݽFœãHÍ3ül‡&ùäe©îP/‘Uʦµ¿{g*M–Îl°Döu£/tÍÅîV”–CfVËZKÓóhRÀÌ€’‡ ljÏ͵ÏkÏ$$«§5­Ú¯›&ÖÐMŒN>RU”C6ûž¶'V_Ÿ;7-3u¯¬d"Ò+oòs·ìõÃË¡³4ò[Ç9ÄUGí426Æ~K¶¿ößòÃÁ œqìÏ>¡¦ÿîUˆxß‹ÒØpßÏ%»+ií c„~XDqíÖªž–ÑVC›KZG…ø Ý[Î^´&‡&j6fr)ç~Rd…*-z;õ‰;™Óݾ.{»dôøåÅüE=…ø ]-CŸ “~åÐtÍH™ê>ë~:'q«¶Î5æÔÑRÇýüÍÈ¿‘¨ úïÞï}†\â×_èÇ;«ºœ½—ÀøýOsrsXÓ¾l•ìû6ÎÐ=¶åŒ»rIÉ•…w˦#/Zzš ôß"Úôæ¡GåÙ?Ö9¾ÐYh lçÐUÎÖþ{ŵˆ_ú³*A¸_L©]ú†™!]eTWcÈ Œ¿{¿}vr(a‹CªµCmH¸Ò¶+ú]¼±™OÃÙóéþΠ[Jü|[X?@wf¢þY#ôÃinZ_p“××›‘v„©Ïš$?¬7êµ^È ¾H© ë#„þ»_!úÞV»¶]UŽà r“nXäfhÑVýùX‰ÇÂyÔFc$/ûGs}èÊŽx¸—åP»:/ŒvKoP§èN Ãbƹ†œl¿€ú±&.“eÄûÊ ýÝ`/6ñÉ:£ßÝ7¥Å êúzJó¢t¶faË’‹ÑÏ™•m-eÔF/âÚíBÝèë6Þ9ý]³~Ø´E»¯ÓáŸvOÕ=0cq×ÊIzyºŒ*êµ9´å}"œ¡ããtmv]­8ÿTMoOrÝøé;ÒË$­Û¡et´gØ-#m­“ãv äu¾Ð=âÂΑÚ¡Sû]OS5u-þÚ Ï1ÆÏk¬hmÙg)Ã÷äýy ºËë¸.‡ö¹Ÿ®Ê¤Þ?÷û“ëñ?û­½‹'}Cœà×=¼®º¸Q§›h-Ì¡Ê éÝÉ2iÌè†ZÏ>`·¸iyK1y“Úv’ŒÔ;E¾CxÖƒ ‘ý¢7Žc†åÐ3Ûñúyç2Ƚ||‘ëŸýrÞ/AFwû[öoâ.øn@ׂKãº9´Õx°È¯aí]Ó¬kM·?¾àS|¿6po"#ß¹¦ |7 Ów¹xí]6Yÿ¸”ºs[:=<¹÷ñY76¬¬5.ÍRÒlß·Q_uîTÅ¡ÿ ‹ŒN2– i†Ë³4rÛjþ.å©ëÒí®éb01¼õ°2ªI± › ¾ÐÝYŸgq%›ê÷­|°Â"net;;½³;{$áŒ×ÍIcO;[FMƒ¬k\¼x/tÏÄÜ̦N†ÞuÎM¥FãúÌrgܪ¸å3aAF?>\N?y :M™üùÙ¤÷ÚBïÙ”T ÷êÖh«“;sùÄ‘˜ ¾2j ™xñºrèVj ‚²)æ\£·Æ )Ô=¿õUe¬;ãçKÈ}2g|.#-³D¨û_^!â÷Ã1_’¶z3<…üCÌšÕÖƒ ?oq|ïf :ºs¼D\ºÐ·²0þ û”# ñPgQýXý¥gÂ’I2¦MY€ÇŸ:àg´Mé{ÈhÅß»Wßãº7VõÜt6‹Žœ½²qJŸdÒÛº_"—œúãëÃÍÞB¥2ÒØRä ýÝ"axj¤SohŠZîéQeïÉ4Ãf-éN¾×@?ZFšéô'aüAǹ>Œ8V~{Ê^*a¿ú4‹2?ú¨ß^;ºRûõ¶ŸQ˜OsÚqQð½.À”{@r“7äb<›ÑÊ!•k‹Î0ç©Ï X{â·‰d´Œ‰G®^*øÞ@w:öäúôô›„‹×‘L“¨ÁvéšT/6ösç£^Û‘ˆ³cô“Ñë-‰Ã¦8 ãºC—æêœ¼IÍ>Æ+‡/M¤i£¼f;[ŸcA{»ÜV>°%Ý—ŒîdNzpï†Ð+DNνX,ºI—9[ IñëäóŒ_7ØÐ©ùy¯zžÑ»gáêä¾ Йײ¥C‡›ô5ßñÆÝñ´½ÜVYazáOÝxn—uÞ~µ¿´AƒÂøƒ.`·À¹A>ÜŸ¹]£Ë¾­äw{³«-¸'+ú2ëSn¡³Œ8W­;søë"†îàk£~~XyûŸ<G•sMG„ʼ¿obIEœM-¾gsng@ð-‚ÎþXÀ½*ÛÔ¶ÿíz Y±4ÏýˈÈYYU›k-÷>·¢ÓâõFïCd¤w†´é/øNA§wúVCI·äÁÙw¾Š¡ß#o_hà#ì/Y“f™vKF;,GÁw :Ç—‚ûå×Iù|ctëºVYç­ã[¶åÈsù^¹ 5©·ºA÷O2ze3oZ&¯+‡Žs_xé:Íц^¥[×$݇4ðe¢•º½?±£«ŸÆ/ðJ¯2ÙÉâ2<%nØî‡ö´H§Î²oµƒÿá'‚®Ñ'+ÌðÕô\Ÿ3¸¤»‡B¯+šû1¯îíÉ®ëèº'ëÓ!.4ãýÔÄÐi†ÍA5½çì:ERå)Ù½F ~îkûïߎ7nLSöŽwï=×9s¿/˜32RSÁ•…/ +ô¯kîÏ0Y{Ü-É–ÖkŒîƒ©³iÅèŒÖ‚ïtq+}º¿Ë¤ïŸ²aátìÜPw—þŒ_64í×ÍhI­`:lõl§ÿûTЭW×/Hʤ^·;üYI‹#Úù8/à¿ãeζ°e0ñëR¡ÿ “n䜬2iý£½ên³•´Ný1pMDã×—br~_‘f6&˜:ž¾ Œ|ß*+D£Þüª<Ð0“Ÿ¯}îâ”0ºÒªïëõYäΰʊ:Wt¹úAL›=÷—ÔÕúº§#¸Ö ÒØïýSÎ9ÿ]6 ÙÑÇgMª°¤:š ¿`šΦپ“BÿAwzwJ‰Ü>ƒ7qœp5”]È>çøÇG™÷åúg¿‹¡ó9Ó%ö†nqîòy;Bh_þQæ@†ÅYÏ$1õß½À\æLå:Á·^î|û KÛΤÓÙZ+ú,žL=î¶Ïü) b|¿Ù¾œÁtt+ˆx/tÏ-{´wH'±EŸêc¼”·©˜ö"ˆñ>h¶d¿aå ¯R¬‹9›¥+BÿA§±Åi•NaõGÒ’R‘õ™Ãg]aJÎÎh¨=µÕ<¸ ¦ª²}לùû¥:mÏÙq)ñi´Ä§ÁÖ¯‚èŽÎ½c¤ìá¤#gÆú,§²¡3kߺLœ œGˆà›ù¨BØjÓŽé¶itî£AïÒ@*k(zU«®Œ…åŒùVÐÛfÕoÓgo«}y?J}è® æ2})å>8€æMpµ¹‘/cS£:-Øj¿‚–r;ÕhïÞ³Ý\Á7ºÜ9õ§]I¥Å#Vª9?HôòLð_‘³ë8§½`:þ±é¬µ­ßLè¸Ù “Têù4­si¦l1MÒzZ;Ô…{ðcGC6›mIÆuÉäìÜtùßç ÝÍW1#H¡ëUÎ=^–]¦n{¶ïú\Âøô«ËYÊ›xåx' ñî¦ø¸± :Ø3òâׂ_-×Þ¯,ýñJFlúI–Øœ¦ç›Ò<Æøç^v4±÷fׄó ^ç ]R⪼³÷“¿ÂSä©zÒq¬®’µâìÀ}ìȼvŸðUhïpÙeëÁÏy ºÀ²–X¡'Ñ'‹ ^É<è”z¼:B¬dü¼Ê–„0ã¡—3Gt^.ø Cwì g8•Hiœ}‘“å×鲿õ%Ó Ÿ«•®¶¡#=¶cÉB{¸ð5Dð‹~R!²6 Êd“@Ò‡KÍGÞ?A ?·ßW©üã‹Ö£¯éȶ{BhSÌ‹À>µy>t¯¦U+ÆÓ;KîÉýQŠhù>4 u8 ÔQ÷<9VLŽ/Öú¸ä‡甄þƒÎò ç%Óo±dº®éêÓâ½TÕkÇéŒmáö…Wó˜ÜŸ¾Ð•D'Þóè+øÚì¤[ò*ý/žáöùºU<1¢JÆš/ øECç3…Û°ù“_¬øv¹2*œýÚ~°Þç:–4ÃJ)+„FlŠ$“µBÿAg{[?ë•åUÊj4åÚ¥Žtoø’#wÃYÓ“fOõÄôž³MoJËÖrŽÇ‚_ôÓ Ñýz3õwަw9—›,lO¢p«äiÍ"Ø•Üf± /kŠÚì¨98”xÿhÁ/:•úæù(êTyô˜ÿ†TP½uÔˆYŒ_ïÙR=ѵf‡Ò‘óUïçMåû]™å;úI³×¶Ð{Ñ},2æÉÙˆ?¾}kŒ<%”êµlE‚ß7t¼k­÷xâê°f û2×£á‚7ìþVΘy9õ±pMùhJŠkÍn{Ú ~íÐ2ã2X8¥Ë÷éU•®`“ÛÕëxqN${9‡{½œŽû>%mJ†ÎÕ—'Ïäýá}¡›ù45²n»pò<¶Y¶žíl¡vžÉ~ ÑëÞòàrªÿÞLmÑ.”*Â/¥Ïﱇï?è~…q†Jâ\Ç/|ßÂ6õÝ×±Q»xaDå°ÉöÄûð…ÒÌ&ί+‡ÎeOq¿LA®‡¥ðâøØý‘e4{ѹÄbx®5˜ÀÐP:þ3çu¾Ðíì©ûªoæŽ=8Æôí¶È÷Žfü¾½­à{J!õ­×~Þ ôtõæ}®e¼Æ¶i€œ`E V¶áQ4ûÞ~ú>ÇzvtÚ–3p %~~(ôtEì̓_Ú¿š{²êÊ®;ûIn ¹ú'·®Ñ©ÖÝPš©Yhð:­g¢ÙlrÂæŸi©×Á-s"ÝØ£`‰ãþ«üÆ”µwÆl« %Þ×éCgý³fbçiÓöû'êtgoS?Gm+¸Êª(p†m°ÄùêíPÚ¬™xð×EwJQ¯ÛY:i120a‘[5Ù°ÙÀN1Âs[ê®1D %ޯ׉¡{:’3;EÓ÷—•¦¼÷`ßu)XgÃü-Ú³ê6d¥1Ä Î ýÝøS?Þc)$ìbÛÛ¾É|u6†­dѺ=»ZÓó30Ã¥ô›Üƒx¡ÿ  8|ùöõ#´îNù®…=ÙÏsvŠ3cXîø§Ž˜Îqi*×sü–¶}x º#çvuö»Ÿ4Ç.Ïx²ÝÇæœYò&†Ù\:ÞÃÃ’Ò–ìläü6”ò³ŠâÖýÝoßñðáK·]ÿåɹmkÝØ?q7“›þèÈÿ1ÞµžWˆ0 Ùlþp9%5¨(9Í~Þüõ±ÃÈXfwÄ‚®ˆ×šCòÜgúÐñÏ1Dl2fq-/žfCöOœ#³ˆeR䇮¶¢|Qÿ £zÈÉ¥XŒÌÂëDÐñ¾äkÙ;ÏÛ›ÆåŸf½ü83ãH,ãŸO‰‰ÛM<ÕAN·Îí0®¹/Œ?èÐÙ·NßÉF„«Ì6½8Í.7´ߗó©ªégkZ¨Ù€‘SÓûf'>æuÎÐ\Qw­xr€ 8ò#ìÀ¯Xæ¶jRºÑ[Z¸ÊºMé(9 YhØW¦+ôþ~]¬ëÝó›Žýù~72î?©š÷Ç_|AUBÆÀyr:ûËm÷·ÑB¿A—ñ0+쉖;+þÈΟfíòsƱ›ä‡ÒÆ-'å¸È¢Ùr*©ò¾T!ćrèú­4lš:Í“ÍK®\¸ï4ûòýØ·çõ®±GÍëûìh¾‚Žv®Ñ 5’“öBîD0¯ÓzQ!rwj奓gÙÍVÏŸÎ>ÍâÌCÇÞu¸&œÿ[A¡¦&ó>ö“Ó3+­6ò:}è8×ZñçÙ7'ëœÓ\}ã»Up·‘õðådj´Ïôh'9ÅÇÕ”ùèïåû :þ|¬+íbój‡'«W´WïD×ø?ñ!î—3ëPa…׉¡[ 1öôeG¹íòšS,k3 ±*ž¥îzt' Ì†¸]èN¡´èöÍ7ÃÎñ:gèìBG ù±Ñõ¾”¨7Ÿb¹W)aAP£=n&þ9oŸýœ3²•ÓÚyË÷ÏÂëTнºÓmð„pÖ·ýª=+v`§*ç<ÿHdæs¾¯l&¦á“‚ ,íåô]-ŸӓוCWÿ¢Î誖˜‡mšX;9{l¸W·$6(YÖ¼Ç ’¤;ÅXNõÖr3^§õ ×3°ÿ¤Ž/"„÷v³m_žFÎ“Äøçß‹iiDß”ÙSå4ôÊM›÷BÿAçyøà’„H¦kZØÏ~ë?|°î·klwœwY9Íji&Ö4úyUlû¹ðœR˜·@w»‡Ó¤ö­õ8øàìäg˜wš¾rƒÄš:©·¯üˆöøóñBÿA÷+û˜®Õ«vR½µÏæë.n0°΃3Œ¿Ö¤±MÏ—Ó«~Í <úº>[/Uú§'²¦C‡ùhW'ó}wšö 8+œsSS™ŸÓiÄ î¶S· ýž ‰ñï¸Q£º3ÙÉé^¬w™ª“t%õ²qïM[¹°ÿ)Œ?èb¬[Õu]Ç¿fÒ½àŠ íÅç<80±®­è›Öýí;Ì“ÜZ˜<ÔÆßÌãëq'÷ULlþsçR¥'_5ˆœÐë;,M¹ô0{ µà¶A"C‰ÃBÿA·KXTÂóë3´h—âªÍžslÁ™®'7¯_J´ð`}›’P:g’éÃ\aÞ ]Ìö9ÛÔ¨Ø8Í :^t{‹.†Þ9Ö~e+¬çË„çerjóåã¤ÕJ¡ÿ K{ñµ^¼u2«óÒÙø Ô¯öüy!>çÿ<çøuvU~í(9mwÜzgm¼ÐÐå[\q?s#™]_\\ØlÏEš·aœjú‘ Ìétp×'64óѾ¡5ßäà8®Q‘¿ÐÐñë¬VPùyÄ’K$×OèWêÍŽ×ÌÉÚ>ØŽ4a¹­âó\t ÷vøé•Â4ÝØþ2‰Ü6(û°‚ø~ׇNùkôÈ5©¬Ð…›(ЄcKãý£}ÿø}?Qô|´ßçŸ÷™ºPYÿFñiì f5á÷)®_½×Gû^f_½ã— šmM|œ“×Ë/ ñ:éî ¦4ƽådÐø ­oѧí¦ã—ÿìKtã¶ËéÆfSöŽòtÏG6žÆºúñÚ)íîë°«ìòŸyuÍ—·çÍðûlêqö—ÐÜ÷ ]s4sJ:‹v1èµÎYF½ÞÖ¹\¿“ËÝgr?3ß‚ª¿4g'æñú]1„…þƒî¬kVéìÚìX³'ò`2?nÕiõ"?Vhck9 y,e¹sã¹p¾L˜B·o·“šÎÊ}¿:U…¤ÎBc7?–hôööÉ ²˜Õsa™œøñ&Ì?ßVˆsݳ0ƒÕÿr±1ÖËŠz]+ôc¡òb«-s-éˆõáÁÏÞÈ…÷µ„ù'tv㢿>Î`^.19l™‚D‰í†[õôgG»Ž:»PLö&k»}•ÓsõÇí\…ñÝøÅƒ-:ie²¤²„õó½Âhñ‘m‹ Žú3~¾iMÜé¯|-åÝIN¾Ÿ&Œ?è4ac~&Û 9 ¬¤ìÚ¯šõ©ðg-tŽúUKA%;ÞNÙ‘ Œ?èyWû¥&‹·.ó˪N…ÜôøL€ðþ‚-ùWzô‡œ¸lßîˆÐÐÍ›âö.“›ì?Æåb8eMß?Âsx ãß[°£¯ÍvWhá÷q»Åcg ý]{ÿͦÃ'¨÷´4t]xe?¢ûý@ÖOw·ìÒH{â\Ò›b¼óç!„þƒN³Íq\ÍlÏJï·/Ž -™‰¦™ƒ˜fY‘fG®RÎZN‹WŸ:šQ*¬ûÞý>'¥f¡»}Þ´9’ü›4ØÞõ Óc.´%ÛóÏ,{”“·Ëî5?>ë>èøýã댎'µŒ¢ˆcæÛ»Ë®°!£õ~ÛbCe÷×NoV,§O¿n0Vè?èú­Y"×î:+fp¾2Šò}öNkÔAÊíµ¨P×±¦ö‡¿.+>/§Zš*ôtKŒ.É®3î-“ÙÑd"º^(Ý"ýsEËèÓ˸YrZ!~d?»¿ÐÐIO<ߤÿâ:Sô¬ò<š.\êŸÑH-eü93+ŠŒÓ®£œ¦Zù9D‹ÿ}¾Ð™p…ûß`üs¯«¤}鳎O{ÙŸü>`ì 9q§‹Ý®ñût*èjÚ﫲öÃâÓêf¿ªß#dµÃÙŸûóR­ËoaÜÖŽ??Ö~8¯+‡.Aþ¡tuè F¦íGæÆPÛ7«‡õü)cüso[JÞp É#Ì :pˆ¼Në}…ÈT~p±äé V™f6æèÆX:±eïÈé^ÁL³íWËžš± kKº*hǾ´-Ÿµx>t/BÛÛv»É?=źm=6ßïí=5„ilØÕöd<Â,{ãdͪlk9ý°ï Ý>¬6¬–ÝdcÖœÐÎHŽ#~=ÂøçöôdÓâY«Ç+(ìÕ«3œùöÄÐp²:ÝøôM4e—ǘÕ×hßÉEÑþ¡Ìúؽñ!Yv´?ðšóÙv j&*Jræ÷霡KPr7ÙRî5€6ñ´hj÷KfcåLœu&ô¨£-ñyWN|\ú:î­Ë§?n²æÑkôê%Ç“»ƒºpv¢œ%>~žÙë‹5 °Xd=y…ßOáÛSAw½+·¡ÅnGê{~^Ÿ@=mu"ßõRüÙOœ×zÁ³ÞÞrâ×OBÿA·º×ƒÕm³˜É‚›w¾vK¤áM_Ú´KÁîVÕ©£mE¦ã’MË©·}rIxîð¡B´’;îë™ÅZÏß=ÿUa"M¼ºY†‚ÕÕ,À—ÑÒÓ“?M8#'̓bá¹t\kÛS²ØË¶/­9–D‹1úÆÔ c+5aËh•毜š4˜áÜPè?è¾6Õm:¿:‹9i^ÄaTœ:û{ƒialé+ëA­”ËèR¦AnBGE6X²·k¿O.†nÝží‡tÎfs&s'kuñXeúÖ-ŒhݯÝð¨%l:¥Ww‘‚4Ûɦ¼Î:‹/gÜ4Îf¸ís m‹-<þmŠ?±wúã¦4³&nÔ=X£ G¹x/tÜÛ(³™æØK¼ŠöwËòÏ/ô£”¶½Ôó·!?qF«Ðõ š2³óÜ›Íùß§‚îf‹!n+Îf3{ÍÎdŠ”²ÎgŠU¸s/~Û‘¥wÍÊiÐíO¾õãuåÐ&x<ŠKÊfÖ žÎ{»'™bŠ×Œó¹L¯r…íI¿-xÒZ½*ºøzì[¡ÿ>VˆšÔ9c‘^‘Í–ý(îAÉTï×Õ}µøÃé‰ú+H³-f¯ þ“ÐÐ=›~îV^ýö°‘Á­Âi)ÔÜëð¬/:¾ÔýP§Œ¶‚47–*(c^Ü‘%ç…çFÐUX¤69Ñ;‡©ªÞ=q”§Ð…õíýíñ¡µ­¯2ˆ^A׃µG¶[¨ M–\&žA7\ä gœÃ¦ànª§›JIº‘–M{“Ö/Ͼó>-§mîI¯‚$úg²;zÏ «¥ya2‡Í[ñºëétg`LúÔ•è÷ùŽ^šf ÚP Û0HxnÄý>nøíÉaqÒ6-Ò¥Òãý^F¶:O»¶¯¾éiGqCu›oýKþ÷ø—üÇ:ÝÜüæ÷ºíï<éï<éï<é?Ÿ'ý#ýëçH\œpú“ûÝÜÿgÜ€ZPFÀHA9ÐCÀ2.@j€!˜ø‚b ƒ`f œA,¨nbàò€68%¨ú|æÀ ¨… h”=EàT "HJ€/(:˜ÆÀÄ‚j`€*^ h#˜Š€P‚* àjÜ€Z´FÀHÿzáþñÂýWx¼ý¯ø˜ügþn}Lþ÷ø˜üÇúܸuþî#iý9kýýÝGú÷˜#qcÝYè3îw࿉Èã¾ ‚”8%¨úZæÀ ¨…f”=4àT "ÀI€/(:vÆÀÄ‚j`€à'^ h#Š€P‚* ÀhÜ€Z’FÀHA9ÐCÐ4.@j€!‚¨ø‚b ƒ€j œA,¨°bàò€6‚­8å_Üÿ#>¸ÿ ¯·ÿ“ÿÌëí¯Éÿ>å˜'qó‹¿ó¤¿ó$g­¿ó¤¿ó¤y’‘p¿©„ïmˆÿ&¾ ˜k AÊ8ƒXP ´ÄÀ äm0pJPôÐÌP ÁÍ8)(zv&À¨@ 0Dð“_P t3ˆÕÀQ ¼@ÐF' U@AÓ¸µ@€#‚r ‡€j\€ ÔCX ðÅ@ÁÖ8ƒØ¿~¸ÿr?ܕߗЌ€#‚r ‡g\€ ÔC$< ðÅ@çà÷æÔBb4Ž@ Ê¥ p*P ‘8%À$Qcà bA50@R/´‘`EÀ (AÐGÂ5n@-$_#ठè!› 5ÀÉY|A1ÐÁà7Î T$n1ðy@I\œ€T}$usàÔB‚7Ž@ ʾ p*P 1ßñÿc?7‘ÖßyÒßyÒßy’Jëï<éßežd,ÜOjá{G åÜg!H™ 5ÀAK|A1ÐA3Î T41ðy@ÁMœ€T};sàÔBà3Ž@ Ê¡ p*P %ÀIcà bA50@Ð/´@EÀ (AÐG@5n@-W#ठè!Øš úë‹û/÷Å5Ž@ Ê“ p*P ‘¨$À$-cà bA50@/´‘ÐDÀ (AÐG‚3n@-$;#ठè!ùg ª’¡x< Ä(N@ ª€>¥9pj!iG å@IÔ¸¨†Hªà Ь1p±  áŠÈÚH¾"à”  è#›7 ³pRPô¨M€ P`ˆÄ-¾ è ‰g ª’ºx< /N@ ª€>¾9pj!ùG å@“àTÿažÄå÷¿ó¤¿ó$g­¿ó¤¿ó¤y’¹p¿p×”kWœ€Tq‹ eÜ€ZXFÀHA9ÐC3.@j€!šø‚b ƒàf œA,¨vbàò€6Ÿ8%¨ú„æÀ ¨… h”=IàT "hJ€/(: ÆÀÄ‚j`€€*^ h#¸Š€P‚* `kÜ€Z¼FÀHA9ÐC 6.@j€!³ø‚b ƒ m œA,¨Úbàò€6¸8%¨úèæÀ ¨…àn”={àT "øK€/(:HÆÀÄ‚j`€Ä ^ h#Iˆ€P‚* ¤aÜ€ZH FÀHA9ÐÓGûÀ¨@ 0D‚‘_P tlŒ3ˆÕÀÉG ¼@ÐF"' U@‰É¸µ¤Œ€#‚r ‡¤e\€ ÔC$1 ðÅ@ Í8ƒXP àÄÀ äm$;pJPô‘üL€ P`ˆd(¾ è 1g ª¥x< ¤)N@ ª€>’¨9pj!¡G å@ Ö¸¨†H¸à Š’¯1p±  ‹ÈÚHÌ"à”  è#Q›7 ’¶pRPôÄM€ P`ˆ¤.¾ è Ág ª¾x< ä/N@ ª€>&æÀ ¨ÿÃ6_Ɔ8÷ÐÖ#¤~uvî¢&‚ t[§{·ž‡ë¯±Y}ƒ.›†?íÚp=ÞæÞ°f¸ k1blâ|éNÿ0äècÁOºû8ïDkl¯Ü ÷¥WG=8·’t÷´Úü¤³ ½HåŒ:ÔóÔ‚1©·? èÜš5·Æý幪‘Ý–67iH›³&”Û¦¬L  ý~¥ƒAµ‚œÄZ'áuåе¼4&ùç‹ÖGÔ*ËkßMªr)2I¯2¥]]%ürµ¥ãþt£‡…ïç(øÙÔTˆšÝ{ÙÜã§ÝãsgS_ߤNZm|Ææ·§BÝ3N¢;:öÍuÖá9aÔXcpÏû¨èC·€³{LËa½4…†²H½¢é¨æ¦L¯Û•°§«–Ó0M!»0šª)Ì#ø¡@ç¥)ß—ÃâûšKNÍ¢i+_¼=ž¹œù¶Kk°åû â}±Â¨WÆ-ß ~(Ðñq'‡…äÞ^ôrP69•Ï?pT¼Í¤‡/ï q Îo^‹0ª«)l#ôt16{JÆmCÜí«ÈôʦŠb‡%zÅÛ˜ÉúŽNÑwHcX7ŒZk ßãº#Ú«–æ°´ðkýkåPÿþ±]ªUÎŒ¯Sí Ô{WüÓÏ:¾ÎqËïÆÖÎ!‹± OU°ìÑ!õ>I¿illÝ?èªfr•ƒrØ(»:Ïl sèÛ˜2ÿ‘w³ÛîÝ/îu_A¯úŒêý)OA¼"ï·¤õ¥BÄûe3ÆØ\Rgoêøè( Ýpù]Øc{êÜ.$ûWˆ‚vÉgžÈËåuúÐ J’¸¶-Èfó§r΃¹tÏóìå3ÇN°È%b×#vĹÜmÃxÕtG¯A·`«}Û&ŠlöBvÛ̬^ù©¼XåÊ~ç‹\ÎVà´‚Îy—¶ º*øIAÇûÈd³2½1{åQ´¡rhßÖîlœÆèÆ–ú^×ó)âQƺFýZ› ~RÐqQTÏ"›]¼TaT½2vZõbÖT–//úàëkGÍ$Ï]E¿/y_9hÀsÁ:måàl¦);v1V¶«žyé”àgºœzÕZ¹²dIñ¾F‚tëk:Ü«ŸÍæreêòò(.kòêÆ}O3ŸÈz þ¾&s»ì #“·Ÿ‰î~`Ð}s‹±øQœÅf¯”ÔQÖʧ¼Ë+š<9ÃLé¬ßØ×øº@a‚„Ð_+D¼L–pçÓñÈ¡g¾¦z1>?:Põì ,Â7aB½–BÿA§£pž8nWkšá2ù²}>}/ªóyvÈyÖZc<ê@mÔú<6ŒÊû]ûx¾³ÐЉV?Ë7›Å6廽58Og–.izd‡·àk)¡wc–ôF¼Ï‰ÐÐñ~YlCPò¹ùTd8©òˆ¾û]}¸ÍòK™aÄ×±úºÉ)ŸÖu“9j «å“lBçYG/±˜ ^®|gGú–ÏJnöO?7è^t¬QS¨X@&7W¶6_ê+øÊÚü/³^«?7èìõǺÉZp¶H‹ ¨¦cä´:…¾LÑÌ96ÄÌšÞÍxYª ¢.¯+‡nEó¨Ú“Ýd3³;Kê* ¯þ–Aw‡^þSÇnÌÚš{›n*È£.W‰JðSüV!*¿soìí®7™jüèÍ# (Cá/ÞyøòŸºy\u»¢2í6MÝ=7TðSüö»Þì Ö{ÆÄnk* ¨íq­eÏï\f2_¿çf£­„<F†š €àç]-MÁíì"W†³E!›4-áP_?ÆûÈŠizËý|M¨yýù‚t;8›ùÃ7Øœ3rŽŒ+¤aã6D[¹ø±Ò‡a×ÛÓO®§{=j9­³KÁOºÕ=¦´6¹ÁÒ^Ú—9¬*¤¥K:}öcœ›¢^wê`çá© #ÞBð†îX‡¯Kn0OÐà³…ô¿æâÍmþ¬Àtî“.»l©á ë.†QÆÑö³R«?SèêV– ~z™šþ˜ñ4µÎÞ÷÷OnÀj®˜‰ˆ±£Ò ÓÚ‡ÑnõRÅîÕ‚Ÿ"t»ƒ5M‹¸Î4e7« ‰¯ÀNÄœ¶¼pžæ[Ù²÷õC|z»o‚ß÷ _7ð:{ïyÍ®ã-òÙ¼fM ;èÔÃÒ'Çž:nÓ0Ö £ñ=G‰Í„þƒîd=Îáö:Ë¿>ÿÅ©·hBÉ›G;1;³%SŸ³§ýs´;mjF¼ÏÐÐñ>ž×+Òéxvý-ê=t”Ϋ¤ ¶³±ñµìÈ”sTó¡ÿ ûb¸£dK©š…>Ιeè}‹Ü•­›;õ ë=hH-6Õ†¦¾~öüd:æg<ãsß ~Ðе5è£Ô R3Î5¹oú-Z’yåêѨ+Œ¯û)¦´®ŒÛ^PP»Å†o§ ý]C¡•š½Yº>õÍ-ºi{áL3éßÀð€»»Nº*„útBÿA·oAãû£ÔLÏÓaXœÞm ÞÜ_«ÈJÊ|¿mX?ÑŠúj ®)þáÓ[ï·¡f»Æßpi,ºM;GIïo#¦vF¾ýŠÿô£ýQ!òPœÛ™Éník»«žÃmZs¸_“íeì‡ÄÈ~m’µà‹Fs.NKÿÕIðĮɖԫSÎe2MyW·Û4<ÜÞ§ÖyÙŸz‰™ ’¿Rm\ÿÎúï‚4t‡Æ½ª.²Ëd‹Š{ÇÜ&Î…4|Hð?›0:’{Q&Ô©ü„¡ë˜?îEû™lU¬zܲ{·éµòH–Ey0KJÐúVuËžJ¸òû6aTÍÙ¶Žúº3^+zÊk2X“ð¯Ý›Ô¹C²AœV«N^uvDÇåÄ×' £éŸ76z)ôt‡¿Ý_£Ê`|ß;d±Ýõdý™¡¬ÅQ¹¯ÃŒ‡þ1Qí[†‘¦lÝJÁÏ:~^™ÁfûYV;̹C~×ZEÌ©eÛoR'L´£Ø¶ìÓ  Ìì§3®ÿ ;k¦ˆÉ6Í`fù]ÎoÛp‡æ.ß _ºKβ¹…” )C‡õ©wOAcŒŠ/èyñóg…È%~{ëv2ØPÎfçÌút·æzƒ÷rV3âG‰k+k2ɨÁ¯ ü®Ž=òÄÂøƒîmü³3*Ò_Wô­íy0µã;΃[ÑÌ%o›ÌUP=Íÿ}"èx{¿tfðá멚»w¨ÉꯎV°ÑþFÓ:-#®Êç-ÔøÆtÕse-í×¥3MYÖ_whïMíV‹rLý|Qîô7Kéƒ*Sœ.SÐqDuƒ¡ÿ {É*¥3®JvV×"š©ÝoP7Œi†e ªè<³Ë5_ñ¾µBÿA7CSÐ9•™™†„)Ý*&®Jó¸ðöhƸÆ·YÑg­{g‡–ɉsy=Ý[è?è܆TÔÎ3Hf]úq•À‹éó,Ѳ×÷"X/qœ˜´M;¹©¾‚xÿ!Áºè6ܧb/‚Eê7/!il¼^«¶‘LSvÞÑFð/TÐÚ!ÁM*× ~ìÐÕdèhuÜ¡bü|¨„.nî}ri$ã×¶”Æ• n­ þÓ}^|säu*èNO™â×MÅ”®÷êÍ(!÷ï ªe‘êÂ;¿±Î»•‚xŸaüAgz]Ë$ýc1bÙÀrûºè=õ—ã÷HXz¥rp{Ò”ÅE{še”®Ðµ*Ek5br“Øþò·¯–í/¡-š¬(6èпeíhRûÖF%¸.Ï_|9ÿ8BÈÐñõé“XÌ™ëŽc/•PfyÛÙÎG±ÏUM?»/µ¥çÃð äô4ä‘íÖ½Bü„îj«³)‘1»Êä6ñ%Ôvd€¥ÿÍLc[rEN¼Ï3¯ÅÐ)Z$ÅnJ`šeýª×–sò‰þãÉ× –ï“"øACwÿ¤ã’j¿x†$y:÷] 5ÖL@¢Ù ÙEë•3—QPµoÉæuršù윥Maý]Ôc‹£!7®±Ú1¯ŸÈš•Ò“Õ3ểfçÞu•¶O\BŸŸW^~³VNƒ4 +aüAÇû€Å±©…%×Ë JI5o¬ÓHq4ó\ÁšÓ¨[oRÿ#¿—C7¾žÊ©~Ó8Æû®—Ò‹§£«ÖD³ÁË\nw±6§[œýŒ•œ´«’¼KBü¬])âïóXöåýšäùËJéEí¸.3·G ë1sŠ«à ÞåtRÿ´Í´JaüAW>ðÛæ+cbØ8ÅG¶–RÎ~ñ×:G£Ù”å·….¦´F1;{ô”_/\ðó†n£Ã÷”~Ó¯².íº•ÒÍž_»–ùD³£›èŠ>-¥S\™j]9]›±µ¨DÐÕ øÑ\¹(ZØÏ*¥Þ7<Ó#¯ý·~Дïï&§ŒÁã&f ãºü-³î^Åø}ÆRªÓÂk»ÍÒ]oÐÕŠ¾Ý|}=u¢œxŸ"!~B·@SÐ>’µ;±Óòí­R2bÖ~]eøRn{–‹iñ÷ åü rúøé©_×J!ÿA—v¦óìvìÖ”Fö_à{î÷t{ÔU±wS¸C ˜æaõNßCIcßÙOˆŸÐíñ‹°žmΆaÖtòW)Eï\aæp•);\pcµ˜,g,rÒ=”^©7èk#aýP§RôàðþVJÖï¨Ùâ²VeT¸kƃèÓWW…üñR+úéÜ ¾ÎøPÂ"§Åàž‚Ÿ7t{J¥sD(ع‹[?ìUF¦§[lw•õÿØ_ûñ. ªmÓr࣡ÿðA×¹íín~^¡¬Aþ’ü'Fe4nù·޹Wÿø¾ð¿;„øõ‰0þ Ó؇ï üÛÊÈuÈáéËŠ®²Œoë¿=>µæûlõ2)„‚ Îf JÖÐUÏ?”ÍžóªÉ‚¥e´gêÉv³ ¯²ß>Çí¿ö¬‡þ&BÿAÇïç1\´—-V–‘þ¡&çgݼÊVˆÊð]@¡?.¾Ê!Ñ—ø­·¼„ø Ý@õ™Ç/†0ÓŽ ,l-£ÊŠgO"ÝêUX‰.¤°ð™¦ëš†RQkÎNÐÑpËkÓ›ø1 å2ºûaÍŸ|ˆ4Ó¤‹)ò¼¢÷µ¡4 ÌbÝ™0þêVŠ+[uºþøÓZ··nºkmœÒu½ëAZöõlÀt z×9·y÷PâýÖ„ñÝ7‹5é?#.0KÁ_-Ý=æQòúôÛÏ­®¦ vͬ¼Gçºÿ ë<òûÚkög·ºlXF½žv)žÕn?%ÿZaÛ]L[*6$ÔÆu ›`µýÛFþzŠ¡[ª4m[=äûrçðÇ©ò2ʰ¼_ ¿—Æ|zü<«U¿RÄû¢]¢×JºÄ”‘ãÚe;”w¶Ó«7«]ï;Z‘âvçOk…P½ˆk·7 ýÝÁ«Þ3×/¼LFew½|”etÇwÛ ½¶NôÛwŽs«nÜ(„fp¶:¾ t¼‘ÙÚ¼_¿FZFV±­Ún¦ßë› Z©ïž–ÓÂdiÀ‘7¡»®<:rˆk|ÖpæXß2â}Ï6PŽcñt\Ló¸þ`Š ¼í›)Œ;èzœµ8¦DšÇ!go¿ô<üÁy-%<¯õÞ­•ï­u yÏ`Ztá—Y§Q¸ƒî]¤û‹ Ï+”~CòèýÑ2ŠûHö—'®¤ß¾4›¢[#eʨ×÷ù´®¯0î SeW¤§èÈH³Í¼§Œ’z<¸ªµ{ÕjkwóÝæÅÂþœŒjI<þ˜)Œ;èfÞ4N¼²!˜¦ŠC׉7”Q·VÆã¾ÚïGgFw2ŠJ–щ©ãÜž óΕ¢‰š€Bœ»E mMœ]™7ОJ9›Û‹¨%7ÝË’‘uûC®.}„y'tG^è™Yt”“”³™4-£m¢as|šÚïGfNRúžŸeÄ=…ÜöTˆ›ÐågZÕÈNA?ïÌñ<0¹Œ^(;=u?oM¿}¯fV <Ù-˜nÝîtlÍ|!nB÷¹Ë¹f}ÂÈuèìâÝCOÚ_ºþsÛúí‡â;gÛ¦îýƒÉîIí³™ç…utø²ûË•Ä?-£¬«…w\ijèÝ2n"`-øsçÎç±PÈ{Ðl—iüII»—t¿Õ¤Œš½ÞØõ[z-¶1qMÐı6ÄïoÊþ1ßQAWÿª×U{©WÇíÇ.%‰ùàZv'æ² ±µ}ü`Mõ†‰ODÊþ±~+‡ŽßŠ s3ƒ¯^(/%õ£m­“l—±3{x&‰)®·b–‘è~È¢^ß…ñ§])²ïáÑùPÝ÷è¨ ¹^Jµë̵kaÇfi ,,i^‹+§Vo–Ñ„É+KÓ‡ û.Ð]¨»ÎØ¿v$YˆÇè…—ÒÝmý37_\Á’•Ç¿°„FÜøš¶Iöq+‚îz÷)ç"©#÷¸àL)õ|òÓ5vµkÜhÿ½Ìh°ÆPR&|_¡ÿ “û7ô{Iõ‹§]Qì,%¿mùGd¬ÃÓ6—™’÷ê:[}çȈß/úº›e-æ4¶Œ¢_ .º%.¥wMÚí+aüsÔùD˜Ñ=:ô`vÌK!~BW¥»ÍcSv =úøÌ"Ìãý—Œ4`…Ðó„ýGýlÎ=Ðæ-Ð}<г³Éˆhò7_ë0¼G);|%b =»ëËkͧÏO8C]™àw,Ì[ ã×_Ñ´kÙ¡_›ê”Rõâe7Ú ¾… ȲոêÁæ2ê׆õ 5æ- +E‘¢øIGk]¥>ÛËm•%´1ãɹÙ#ìïñ¦ìo5ö …Œ46½ý…ñ¿ßw•ZUŸ„ø Ýd¬ê=žÄq¶„œÏ+û¸ñët1eÌ­¼ÿýŒ¶/\­ê,<÷ƒîÓÕsÞÇ’j‹¡Í%­ªÙyßgð½Í¬»æA¨˜LFô`Ϊ—Âøk„ùÇδ^+ޏ]y1…kÕÊ8ÙuÛ?›Û¨^FIlÂŽo¥t}ÑÙµm„ü]ŒçìG_fq½bê²nvë§uv±–º·ª„.¥9mw J’þ㹊:‡àÞï]“â(<ÕëÃçùÅô@uäDg»ÝÌY纛í‹ÅÔ]³Á'¥þ·ë%d)…ñé ˦&í¯Ñ˜ù»ž¶íYLol¹ã´‡|¶£iòúÅ4 ÁvéšT)y°öÕýÂøƒÎd¤IùÅר֠¡«=?‘ê­N׆‹œÙ“'òǽXBïζù8ýµô·ß:ßÐÕæŽ×Ü¿&ø‘æ˜Êø½,÷Ò’Ò,¹À#£Í‹¿ì.ä?è~öë7{Æ„xÒÃêðÛ/)9_Û[ûÍ#á¹_³JQ‡ƒ="·F{Ao†×Í̧î'TÍæ…ÓV.ýµ¦¬HE;'ɨÓXÝó=ÎýÝ誖ÍÓSiOeŒöñ|²Ü=K§ýòpâçK¶Uß)Gi¦¡]…sgÐå?õ?«•F¡æÜÆa>išQùC7œ~ŸrsXÓ¾l•Œrº[Þz-œ„.5F¹6aLMœÐgI\Û|Òo¡(û¦ÎwJ¨k’á2óžZ­ºÏ|"œ÷„Ž_g¤Qȯ²Ä»ytlJ§Æ)iÉ|q­Y´=D1ixO™Gy/tüs4*~?ü¦½EÜÚÎòO†ÑåâFçϘ;P˜å”© p_ÇŽ½s¼Éá¼'tü<6øý¶< ¶f½ïÈäÿXÔ•NßB‡,X²'—Ê#Ùž¡ôÛ_ô¢]ÈÍ>µdÔöM櫳s…þƒî­mÖ²™êt øìtD“\ºë“}—ö¡4BÞªv\š5-Ðl˜ÈèÓùέ<ýøsub蜼ýºÇ}O§S²}ó¦_Ì¡ëyMÆ%ÊCH}Ö$ùa=ë?×…ßÇãuÎÐý¼åô¬×à Ú²¾CÓò9´àÖ‡añÓChPëïGW`qǸLe¤yŒc"œû„Ž?AåÞ-“³éG²j㛇Á6Öx®57ä ád¤±go!ôt ¹c1§2¨óð^ió³‰>Lù§µMéÛЗ=Åþ2a].œ×…րeÉtDÌfgQÇ:f§¿– qÝ–z4öRÉ㱞HS¼z%ôŸN¥H4¤Óèqo2èñ–ÚnÏ¢¡S.“ý‰Ÿk4Æ•2:à“œ{[è?è8÷óž2i⋇7'·È¢¹÷[X¼*¥A¾*ïKíi€öã]³eÿ8‡)‚îç^Î87“äœfàMêºüñìa?¯ôWè—·g—ÓþµƒŽß‘QÁ¼fËuÅÂykèl4…2‰;ý1uìMwyÈâ‘£®Ðì÷'l걂~m) sqWsNš?Oî æ1æ™Lò¶gÖªÂdyë cÇ s—tWq¾Ií~‡Ûð:_èÏÓ›”˜)ø-Þ }SËiÍ–% /'þÜÆw¬è4ÿ=UÐÙzéF]˜IOžYÌ›¡}ƒ¶M–|û@—ϦöÔï hàúG2Ò,ï;òºrèbvÄÍ=Y[M6w›úEû]'6þAü›2ë½Â൓-OZ{¨CµŒ^–rF±Ây똇Üܵ¡•¾šŒ.^ÔgÜuòµy2«G…?é4ÿaM¿r>U5ý,#³œó pÞ:ÙLî šÆ~4Þ_Vª&/Ïõ‹6ùÓïç¨Yùœ¶Œ¼ƒr›Å*„s×Ðm¾Ùìmó¥jÍ4SžpRÓÅìv¯[úSÍFŸ˜jqm[ ×[è?è xþµ^MÙuSãµSSÙúβvr¿?yL¼±™OÃÙÁ¤9ŽòYˆŸÐMid]㢦þÜ´>&“ÎʦWæG›Ïz¶¤yîLì§wìú:'›;Ç.{«i‚qýK3…ç[—I_Ûí²m{ê>xBÓ)Á´óâ­³yFtÝL¹¹øžšóýÔÿõ#§à*_zÔ¼¾ÏŽæ+(8î®XšL¡ê*ç/yŸÉrè&ip¨…s¤wôàª_­|©ðÀ«Þ¶;%´ËéŒ~wß`š±kq©,NðÃlY)y/bÒ÷T\ÌŽ§gv»5_Í—ûP¿·ÍSJ%ÔAsP ˜â †Ì#ü0¡Ëop³8/[MoŽ_í{äe:iUîãç-ì¯KèÇ¡#õû8¾Dð3…Žs½l›¯¦ÖëGcI™NÅÇS}¯sAد[NWœ¸“gÁ´xNPwgÁϺ±#^55M¹ûúñøthÜØéÜ9á²µ«Õú¤þé`ªç×Û~þ$ÁÏ:ÍvÕ³4*á^XíEÁtsÖ°¥!ÜãA·`òx⊌+ø˜âï£Æèéãû->ú¨ß^Ï4šåÝkתõg…s1Öô|lߟoðýüo¾®Ÿ/ôtþ›žYyÜPÓîµ’s(žÙ¯;à{†vð±ìsSL«ï=9q*ø¾›åÐÙ·Ÿ=0C¥¦Ã‡Ò¥«^¥R÷y©-W><-œ7ÓÙ±ÜÁä`új»äBÆ'^§¥‹þ>^”©¦ÜeºJ_väìûØã´ðœGLV–λ6=¦`›¾?Dò¿Oºn?­gº©éQkOÌ—\EºNYàIÝ~|Ú\îmMæ#ïwjB{>Æ®‹ž#ôtü}®&eídŸShFçüCâS”Ý­ÕÒ …6ä’ºç辿ð÷z=v-™²GMšíGY 8``•7уâÏ~{d ²#½ÉAËf…û…BA·lÕ› %jÊׯ7·«Y y«ìõÉÍë»ú†=q»gæóB(¯:wªBè7èÚÏ­»¾ó5igM Ú©BZ– Có>¤És¬r‹ú¯ %”™“g„ÐÍýZš,øÏB7¤Îé·o†ªéÜ´Ÿâò¸dúZ¾*I+ü˜ð^‰äÏ1üƒ¡»ßåÍÂ~ºjÊãŽå¬J&£Ó‹N¬åB}5F%TcļâNaž.äýеZUŠŽŽ~mù»LÚͽ.Ò.™¼u ôM8@üóy MÊlÖw}§ê5lzÿ>[ÿnè§L\š“IüyÕm±ß~/uÕ·šXïð ò—ÏèöM?„2RtFíÇëDÐp¯#\ɤ©Öm§4ß©¢Ý šþŒÞIšË¬¿\8ÇBüyþ{Š¡£õZ?’ödÒÎ&‹¼WPQx××¾êé[éj’1–fNgCèzm.CþÏÐ…sÇßeУâù;w&QÕɨFó6ÙÒר£?ó²lIc‹Bß×Ìu+~ÍëÊ¡‹ŒZ°1óÑÁîÆÅ‰÷¦Á;?ûä=œ3¾¶§}Ï™ø,„:|Úas±¯à¿ÞºR4`'÷Cy·z2.gX"™ÞóÔ§/óUùò¡x9M«¥q:¦ž9G¥GÆ ýÝ6eúÚK 3ˆ5^yúl¶~`R³zš:u1;'!sýíó{ ¥Yßžü|¾^è?è*”êýÛgP‡ dIïãiíõz)²•kXÊ—[á3ž9Ð¥Í'ÎÔJ^ ‘ó:1t3Ãý×K¤“SßrŸ§sãÉ·ËåäZ.[Øþ»S¼ü,VÒã³Ézî¡4°£6.©à¿]¿9ÜI¥tºçç©zú¦ö 0ÛÍš¹è˜åÜw ^+—¨Jʪ;«¶›ð:_è–ÇOìÒß6]˜w\£ˆÑmÛîc|þ”ÐæÑno=¡I¿ê¶í·’ש c¸ƒé4q[—iíâ(¥}Êñ-±u\¸u_N…+ïÚ¢Œ5Ù¼®º“7fß*¸›F>Úc½U±´É§É³™7\؆u»S'ï·#cÙÞ¾†C†ß³—v¼N«M¥ÈÒ7@UÛ+4Õ;ÆÒ®ÈìÏcß–Ö®ž+³¡Çv‹&4 ¥_a£7 åuúÐiU¶öanqoëýtŠ¡mfA!åÇY²ùÕ£©ÖäÅ…5ôûÔnÃöx!ôtÓ4HiÄ¿x•Ôjû׫N2ŸüE=Ýw‰iyÉ)Õ“Ž¡¤yÝ#Zè?èêÛ…mÝ“Jwä>§î¼JË'÷¶(qýs9åгCBiXñ­m¦ßy3t^†T®I¥ª{æ&zžÑôÉfù¬+nìÃJç1ik^襼Ik:L ôtšÛµK*-vçgΧ(š¼sýû¢ÍîL³m¼Íšb®V'˜®¥kªÇáË…ë©‚®Þˆ§åçòR„ýÜ( {ÎÕ{0þù¸ ñçHBIóZÐL¡ÿ ‹ÜÅœO¡ÎíÛø+¢#©þ€âMÛN±¡_–_èPhK?m»Ž®{2”J¢ïytú¯-òùõ²}RhB‹cz[ô"I“ô³TéÉêDNtýÔŽR’ï§¹y‡òý†¿¸{0™+´3û>îÏ668ÃÖ«ë¤Ùÿ>j(5ïmxñξt{¾æžï²'YxÎN…鵿F¯?ËvŠ\v¹ùrÕ[9… ç¯xº€û F½ê“L‡[Õéq^N-‡÷¿Üæ¾óJÙ¿¼(ÑžÚ¬äV ¸Ž³fï°—ï7è.úd¹3_%̳”´³Î=©ÕÐóì÷ûo_ßp­CéUÌ¿=öñýÝheÇ®u¶©hTõʸ¡ºJº8ýBð» l–æ…[2u+Jž3+ÛZNçu*èæf?®?©“Џ» ºFA“#FìL^æÍR’.N>°Çšr:ny08”\š¶™SðÿžåÐíڼзÍ~FŸzvh~µXNÜöoÇ‹¬‰ôŠ«ÊŠ’fïùÔ}J(Mì©°µXÏë´ô*EšÇÏ7“H÷ÖÜ%±¡´ìP@¼[äEæqs1ÛµŒâVoÊ™»>”ì(ª¶åuúЩº±Ý.M“È0öÉÉŸˆÿûÈìêÊ!>LUwí½ìË(ëÜ«F»”¡Ô`‡î¤ Cxº’ÓgÜ·ÏM¤Ù{³rü¶ÓÃQ¿T>,rJÁÚƒ­¨EÚ6£½ÏB©“û›ùÏ=x:©Ì~Õ.÷ªi×¶Ù;5qðY³Çé㟷YÓ ÍBAN &L YR ôtšmäÂx¹~›4OJ“6iwl8Ì—ñÏ“lHÁ½&ÖJNªCWìºÁë|¡»®^\\Ø,žÎ.âfðW(aš»Ž(Ý—96TQà ;Úþ«KïYzrËK‡w•ó:tù›¶ 1šq>iUïëä š¾±½ù.»ËŒOÎŽ¸Óø®Ðñû¥BÿAçëÂxˆ£T›[ÑŸÆ’¬ËÑØ7 ü˜UÏ.ñ=íè›è]Îå&rš{fÉ›‘¥BÿµÃuƽXKSî´ŸñjJõ:`>풯ߟsÒ/¾—ٳס4jjÄ4i;þ>Ó‡.¼|·ªNXÊœn}¿r–?õÏ5¨=¡§?ãϵÛPÖÛF“çÞ%ŸîÜm^'‚nÝ€SÆÇ·»ÔÖÚª4- qõgšÇgnb»ºÛж¸´G/¹íq’׉¡[Þòã¾¹¯Òö“ê­}6_¦C Ü·=òg£zê÷z8Ñ’<]ªíË¥=ßF;æ¸ð:gèÖ&µ¸©ˆ&êüõõ¢3¾$wìU+½{ã²N^O ú´3{CIU(}v=X¥c$Œ?è¶®8Rïy5°0ËÒ^w‰¼óu­2 `6­êº®{³„R¾·bV_N+5ª„þƒnxÌDó=£(´=·ÀºHçO<è¹o_Ã`;¨õRjßîÑÀÜ/oû®ºP!Œ£rèÆÙZE¼GR"÷Xi¢7ýì9çTRtëfòc¿ñ! Z°Lu³I9­1_°¸Õ¡ÿÚcÛœ›ÉEPöO]×Å'݆3>ùÀêk^¶$îm¼']äÂs°½ô·ÒßHÎZk ý­ôïQ‰ûG"ÜÜuã>Û¸w6ýªAmîŒ8î«¿uþSéo½Èÿ¹z‘ÿuþ³z‘ÿUuµÿÕþ#]ÿ'çFçEçEçEÿúyÑÿms".Æ8 ýÍ]Nk\jqïâ·C( ÈÚu¸w·p%¨ªÃ½C„ñÜ€ZbFÀHëqïVàsÔL€K}îŒ?>"ÈIpgÍÑ_@_ÆX›;óŒûTƒ†ÜÙ[ÜÏ h7â΀¢} U¸³ˆh¸µ°ˆ4Ž@Ú„;›…ö¨:ܳ{´ò€v î2ÚJPÕ‚{–‰öP Ù8©.÷¬í#@›—VÜ3´ °%­¹½o´t¼Ûp{°hTƒ¶Üž Úy@[Û›Bû@ ªô¸=´Ü€ZúFÀHA9ÐC0.ëBþO×…4n@-$A#ठè!)š 5ÀIR|A1ÐAÂ4Î T$P1ðy@ÉTô?¨ ù_U;û_í1ò»$7§ù½Nû»gô×Üèï¼èß{^ôã~'œ„>å~7÷ÿ™7 ”p¬Í½çk t°Œëp¯A50¨Ë½÷Œ{äízÜû·øl Uõ¸÷AñÙÀ ¨…@g´÷~¾Ÿ pÑæÞÓµ†„’†ÜûBhè (7âÞ[Aû 4æÞŸ@û h7áÎñ£} UM¸óäh¸µL€#6ãÎ×¢}WàÒœ;ç‰ö!‚­D‡;oˆö¯q îÜÚÕÀ %wþ íƒ< ­ËBû@ ªt¹ó)h¸µ°€#¶æž×£}pàÒ†{nŒö!º¤-÷ítš±÷< íƒj`ÐŽ{®ƒöAÐFà' U@‰À¨@ 0DR_P t Œ3ˆÕÀ C ¼@ÐFò' U@_Ÿ Ü€p‰Å8)(zH4&À¨@ 0Dâ‘_P t„Œ3ˆÕÀII ¼@ÐF‚' U@ ˸µ¼Œ€#‚r ‡df\€ ÔC$7 ðÅ@‰Î8ƒXP øÄÀ äm$ApJPô‘ÍP Ò8)(zH˜&À¨@ 0D•_P tLP‚* äjÜ€ú¿°fö¿Ú[ä÷üˆ»}þ>Sû¿s~ôwïèïéßqŽÄug¡Ï¸ßeP‹«‘…{äqߥ6W« ÷4Öæêáó´L€K®N~/0D“Ôåêµàú4ãz\ý|6¨õ¹:øl´põðÙ@ ªpïõcü7 ¡pÒ†Ü{ÎhѸ4âÞ·EûÀRÒ˜{ïíMã&Üû‡hTƒ¦Ü{phäífÜûXh(AU3î½ ´Ü€Z¶FÀHu¸÷$Ð>‚¯ piÁ×GûÀÁXÒ’;?Žö³±.wžíƒj`Њ;W‹öAÐnÍïDû@ ªZsç Ñ>pj! G mË¿Bûð&ÀE;„ö!¾¤wícà bA50@27 ƒpRPô(L€ P`ˆÄ!¾ è ‰g ª>>x< #N@ ª€>Ž9pj!ùG å@Éȸ¨†HNà Š•1p±  q‰ÈÚ¸±EÀ (AÐGR3n@-$8#ठè!á™ 5À P|A1ÐA24Î T$G1ðy@‰Rœ€T}$NsàÔB5Ž@ Ê’ª p±  ÉŠÈ®pRPô€M€ P`ˆ„,¾ è 9g ª’µx< Ä-N@ ª€>¹ùÿo‘ßó$îÒü'ý'9ký'ý'ý{Ì“Œ„ûM%|oCü7I-®¾'®#ׂ”qm®Î$úTÕæj+bŒ7 ˜pÒº\­9|4àR«}†Ï†p’ú\ .|6ÐA°3nÀÕ‚Â=ª6W“÷0ÈÚ ¹Ú8S@ ªr5ZÐ>pj!HG mÌÕ¬@ûš&À¥ W;íCQISî~´tP›qï’£}P šsï4£}´u¸wkÑ>P‚*îO´Ü€ZÄFÀH[rᄀ}fà¢Ë½‹…ö!µ¤÷NÚ:ÚÆ­¹wSÐ>¨m¸w$Ð>ÈÚm¹3ûh(AU[îì8Ún@-{#à¤í¸³´hÁ߸¨†Hàò€6ƒ8%¨úHæÀ ¨…¤a”=$àT êã³/(:H0ÆÀÄ‚j`€„#^ h#ùˆ€P‚* ddÜ€ZHLFÀHA9ÐC¢2.@j€!—ø‚b ƒ$f œA,¨Hjbàò€6œ8%¨úHxæÀ ¨…äg”=$CàT "9J€/(:H”ÆÀÄ‚j`€Ä)^ h#‰Š€P‚* ¤j\€ ÔC$Y ðÅ@ Wœ€T}$`sàÔB26Ž@ Ê’³ p*P $À$ncà bA50@"7 ’ºpRPôäM€ P`ˆ¤/¾ è`` œA,¨˜pÎf^ÿaž$Òú;Oú;Oú;ORiý'ý»Ì“Œ…ûI-|/#व¸Úäø,)àbA50¨ÃÕ…Æ}ò€v]®>1î{ Uu¹:¹‡À ¨…àf´>W?Ÿ`g\pu,qÝ€!‚ŸD›«§ˆ~:„Æ ¹º~hTƒF\}9´ò€vc®ÎÚJPÕ˜«·…öP Ô8iS®þÚG@5.͸:8h"ÀJšsõXÐ>ÐA°5Öáê‚ }P Zpõ)Ð>ÈÚ-¹: h(AUKî½}´Ü€ZÒFÀH[qï3£}màÒš{¯íCqIîýN´tÐÛrï¢}P ô¸÷ÞÐ>ÈÚí¸÷¯Ð>P‚* àoÜ€ZHFÀø‚b ƒÄ` œA,¨Hbàò€6’†8%¨úH"æÀ ¨…„b”=$àT "áH€/(:H>ÆÀÄ‚j`€d$^ h#1‰€P‚* DeÜ€ZHZFÀHA9ÐC3.@j€!’šø‚b ƒg œA,¨Hxbàò€6’Ÿ8%¨úH†æÀ ¨…Äh”=$JàT "qJ€/(:H¢ÆÀÄ‚j`€¤*n@-$X#ठè ág ª°x< d,N@ ª€>’³9pj!QG å@‰Û¸¨†Häàò€6’º8%¨úHòæÀ ¨…„o”=LL€ P`ˆ øþ‡y—ßÿgÏkÿ'ý'ý'ý×Γþ+æHÿêù‘¹pŸp×’kSœ€Tq‹àdÜjs"ø]ÀÁJR‡ó²À5:\Æu9Oô9¨õ¸Úþøl´ësµæñÙ@ ª€>‚œ9pj!àG Õæj@ã· š—†\-b´ %¸š¸hè 87æj³¢}P šp5BÑ>ÈÚM¹Z•h(AUS®f"Ún@-U#à¤Í¹rhAÖ¸èpµÌÐ>0DЕ´àjj¡} ƒlÜ’«í„öA50Ðåj¡}´[qµoÐ>P‚ªV\ ´Ü€ZÜFÀHÛp5)Ð>¹ piËÕH@ûÀ]¢Ç½«ö‚¼q;îq´ª‚¾x<  N@ Ê‚ p*P ‘ $À$ cà bA50@ò/´õñÙÀ (AÐGb1n@-$#ठè!阠5ÀIH|A1ÐAB2Î T$(1ðy@ÉJœ€T}$/sàÔB"3Ž@ Ê› p*P ‘è$À$=cà bA50@/´‘EÀ (AÐG‚4n@-$K#ठè!yš 5ÀÉT¼@ÐFb' zH²&À¨@ 0DÒ•_P t€3ˆÕÀ Y ¼@ÐFr' U@ÉÚ¸µ¸€/(:HâÆÀÄ‚j`€¤.^ h#Á‹€P‚* „oÜ€ZHþF€«Ø.ýó"±Öÿü3:õ§ÿÿúO_…ëˆJ‘¥¦Às8Åv>øb^¬u¥qkgœûóï ª2Î d|ýJñŸzf™×bOÔ¯^D3bݦ9v—ïKåBíEžJÞK¿ÿžkG„v:æ®%¶ '×ù çV}ÝvîûÉAL®r»-÷±¥“aã"{ˆ s·YÓcBýè¼WÈš8&Œ°Hëu|ùi·ˆ¨ht?ˆ1[§—+ÂíhË<ΙQN®í¤Ç÷\ê@7ZSENËŠönœêI»¸tà ›®),lOww±Kw”Ó2Má_¡~t«¶öŸ*ø„œ¢e>-† •²íkF,²ñ±§«væE.–ÓÑÇæÝ„zåЩ¸2à·‚©&Å.XlêA\µgñHéŸzá\5zÝ>¿}1xV‡J‘®uja?{y§é+7HÜiX Éú­ƒ¥LcWnfGÛ²Döß¿…Ræþ[¾… õ? +lST3ëÓZbõóŒM²\ù`d;)û]Ù^cPJ#Gí,8Rw?ßÐså?=ƒ¨¦™+µŽlµçÆÓ+ì[íísZ_ï>”Ž­~úMöV¨ÿÝ…§½Vô ¤‡/V-[yà$½=ÝS'à ãª]šŸSŠCò±¤}¡äÔgóu£©BýèvæwêO«4…HNÐü®©¹=]a¿ëðÕÌå ɇҧ3’ŠwýÇ]›²Òµ~4/áFiÓqÒ”ûÓ¹Â~ו¼ž¡¹ ´`ìÀà)“…þƒŽ¯K{™Œ8[Û cô©×¡GvAlTA;½m‹mé¶ÅZÃ[Öÿ]ýèÆ?33w¼D]jôBn¥©1qÇ?±u6Æsz­µ'Φý,îÏZ:[?öú¯c¥hÒIù íLoJ>ÞǼrõQºÿ¡SÄȧAì ëÞ×tä 2ÑÌE{®,J:Wè?èÖ‡e-ÝÜé)èOÝí%{»†,•Ó˜vœÓ”P? :uÁáÒu”Ý}aõÝcô¶ÅàžUlò‘3c}ÎXQ[Md~NrÊøœØw?¡s»{~ÓÒnÓ¨yaRÛ~œÎ•é9ïÈxß215é´kã9mù±ïžRÐ9C·ã“EP¯ä¥Ìoç„Æ õNP´öŽëÑfü÷?8¤¶MNÝ ïŒ4ŒÆt¼o’#‹·<ßMõì%_®…[4ñ¾À¶$ êñÂ÷™e©£yûaüA§åø²dã¹íì¶èý¨­ñ'©þ³¿üZ²_OÒ„m±£%6öÃÞ¡½oíFý*Œ?èÖi ã÷1ƒ†G¢N¸’üZd‡nº Édø_{òö?y2Ä]NßJ¯T õw:WŠ´ëŽ>5báav©kÇÈ—Óݨš³—êÈú­®«¿t9]^ù|c´œ’}³Ÿõ¯ CçvÙKÇÙEd×_Ynär0úø ál¾ŠL¯øåtà¢Ímý,9mv+Ú?ý­Ð\{G7¦±k1q§«#ÃgÌdé¦_®ˆYNÏϺÄä ïhÊýf ý×ùwÆSŒ÷Wp§þóBš[²§¹èËéÔOËFÍÏË¡ÿ «4ÑT6dWKÌl[äAÅ'ßgKÙ·ùno ì)ú‡6Êi”óîl›ÐÐ ú|}ž³›¾¿¬4录ü ·YÈô7›úÒ–öšÔhªœ,6qÈ…þƒîé®Rýy¶þÔ<{½­§èÂãˆüQøÇŸ–¯ ú:VåÐmÑ×]duÇݺjÝÊ“¢]£eè÷>§«XÙe,¢y%þeÑÅ×c…þëR)ªŸ¥œwç;ûd‘vH¸'mh¸%âè«6rõRÏ}[¥1¦ ¥âGö³û u¡›I\$¼ÌÖ„¦íçpšû7{8§8€ÝX»üƒÿC;:eÄ’ ¥ýA=Mí.Ô „®—ÆxÝsöÞõÏL{oкÊvhäªê¤¾+ȨÓïÒC¸Ï2߬ù)Ô „Îmβ/ýsý™‚+˜t†z7-5¹Ó*•v䮘mjxø~ÅC9¹©ºìþ¹C¨û]Ñ â¦emÙ»Zº±›ÏÒîÌêâ\›@æw–3¬[Ie#Žï++§‰œ`¾P7ºÛËJêí_ĆÈgÛîàE6óV‹ë' þ¸+)Gú¥ï—b9½ê×¼`Á#¡n tÉ´ýàÈ+l¦kÐö!¿¼(Á[½ÏI7ˆõ»·lŤŸô¤þ¤N‹bÏê˜?*j.ôt‡“}¿6±n […E\=GöM›ÏCÞôþr»£¾„ŠRúGmÄý9P3 „þÓ¯½éÄ4fcF®Épp9O¾½&7 [$øÙS‡A÷zÔß+'Þ'[ÐÝLT…š„°þÞ_l—\ £&Ç>u’1ÞÑ–8—W‡ß~£Bý@è87öö_BÙÔÝ?t:ô÷&¾þXû]G½…ƸCNºbÎáB¨ÝEï˱•³ÌJ¼È à‡7™k×_õ¹~ãëê‹éT®›üu¡ãª»ŸâàE:ªÓtBZBàŸz‘û;<¸k%§¦÷ÍO|Ìß/¾Ð•7»ÕÇu­’U?H0z{Û‡zì9³&5û`Rvhž˜Ò5Ó=9E…¸½;µ’ש ;°§Å°å-ÃÙ¬ûZC$_¢ƒW.›ÍîøÇß´þµOõ>¤ ^K²z<¯ÇëÊ¡3Ðy†3Óµ~gž,ò%‹á›ÌÝ `]Þö»ãhMWêsFIrªx´û°ŸH¨»ÚµRÔFå¹·Î’!úÒgýºfJ˜~µJ+å° ç¦ƒwåÔcò—}mZ ã:™CÛÂYŸ#X'‘ßeÚ{ñÓ›…‡kÄd¶¥~%á-\Šä‚§ÐÐiì<#_þ2µ»ÉpÞÆ¹DìzÄŽr÷ÇGnÁ÷ä¯ÐÐ%}´Nœ×/Š%ªë~o?ÝN–63Ó[Àž¦rFÍö4"\e¶é…œøúŸBÿAwáçËiGÕQÌé챚9Y~ÔtÐ×R‡€?ñóZÙÇ‚Vµ4NÜyÈãIÂøƒ®óèò)VÑŒs±žëO‹>¾3{y»›þýnž=ßî÷"¥¹‚Î×+í4¿‰0þ sýÎD3=Z?Ý‘^é·$€]yʵ££žËß5QP~öOQÜ¡ÿ «¥—ë{yÏUv4;3ú²MÙMþq¤§(€qêÉ­0_r·ø9髜6å^›zs¯P÷¸[¥(ÁãQ\Ró6Å{NÄíÇT'¹ ̶eÀ_9“N!ž>ÏäÈt¯¶,ãëëC׉ô,&f©G-t¾C§ËÊÛþÌ**³ñ³j1MÓ·Dè’Óô;«úÞãu"èv9¢ý«[,ÃärÃàúA´@3ññg¼¿¶˜¶ kö1^)'M¹É·¼N Æî$4–õN*çíD¸¹çvù3>î‰éþLÁä‚P÷:§;Š|űs%Oßz…šý,yž]ÇŸ­Ôß-h² õKí0y•œŒ =4j‚P·:MÀÐ8ÖŸ³ÓοB æ·þ‰ßŸuã÷‡OYƒ09ñq‘¯/¯‚î“öðIÁ×ëϽH)ÌÅdiÉ?Æûq¬ „gg:UtQÐÈý0¥êCÇûÎ]coõ¬Ô“‘ìô‚Yoúû³§'N¥$d:–ûîo£´.Ö39þ{ju¯)¸²„âÙ†¹f[û_•‘u ÉÂjg6µçæÚçµWÑ…õíýíQï„:ñÐÝíoÙ¿‰{<ë}x.·Â :Eñtߟilœ_­$ó…ƒX¡‚Öœ¡›Po:nÕ»Y+ý˜•¸¶…^yÜÖ6¯oÀšt›_80Ȧ.å*ð+Hc eÁëÄÐYö:{]ëœÀΦ„PM÷owÆIØð¼9÷+(éR{Dz9]†ŸÈ뜡Û3Ì{ÈÈ Œ÷¡¥[ZoZïø³žF'×UPÀ‘­Î½ð:_èŽ'µ4Nd7¸YÌ`äÇ“ó§7#¿·¾ÿ¸[’-u^°Õ¾-Æ‘G¤òJ§ŸB½xè*6sàDÖdñ½ç£pß»^⟶ €ilêÝlÈI³àPÐ*qHÝc©|¿—wÿ]w<‰ÕÕlH(èÙN®°Ë4#Ϫʚ¸*¶&ßž5Øì>‘×iõ¨•^/7ض9‰}× ±~C 9U'ÿõ fª1x¶¦ÚMn^Z×XA†ûw›'áuúÐÝÙXð-£(‰[ÿ³W«nJ:”TauÍŸÙ”du•´!qæž½ú"]o€'¯AÇ×gllÂSÕ†JÊŸzqÃVÀ•™ÜeK±§¸Bœ êrÉíbL8¯C÷ÉËØa„+cÓxΚóJIÇ'7)=èÏx?!;šz&+­ #Äóº õþ¡{ÕgTïOy*vx6ç,IÑI¯æ–š‚º+è$7ìÏ)èØn¢Ì_t¥#?›¹~S±LÝ Þ‡u£ŸÙv0?tu—wËiSïâ=/(¨fý°i‹v ýzJ´‹A¯dVŸK7nQ´áÑݽ§}˜¤{Ñì>aöTßоÜùŒ‚ê'Ž<¤U.øl@Í•c7IfS0»soM+•VËO^`œË†î;*ž:¶$û¸‚î~÷<ÌLð)‚nš{ô¡ù;’ÙŒœ#ã¢\£)ãÂÔ€˜#Ì=¿Öôâ[z[dwb»‹‚Ð ë¿|n vicÌ‹Àd¦±+ky•úõ_¶|׺Öeúôæ“Bl)ë…ˤn ?1Û­ïQÁ纮òÁUò’ÙóªØö%§¯’ÆyówÝñö.öC(¨áúÇ//æ >7½*Eý·)ÉùœÌªÛ›|׋¡ÓzØ?YÀ씦«¬—S—YG]çªÔxw›„'¼ÿ>tz§o5”tKaóVßêíCÇG÷Ù÷zosŽa(¡N]ëlÛòEA¢OÛ?gîàu"èêi ÈSïûKu¯=.¹À\á6VVÑЊYÝÃèÍÉã·Ÿü|Š Ó”§Þ”ÂZ¶¢Œ»Ñ±tÃgNj­ñ,ãžþ’~¥«©ùuV½a4±Êw¦Ñþ÷9C÷ýèp‹G>)¬·,¦8:8ñ¡9æŸSŠvñž»†²_Ú0«M½uæ ùó×Ó:ëág”#2R7ۊϣܖvuM‚n6ÎkVS;·Äû?Þ*(ùa½2¡ÿ Ëxð©î‘W)ì›SUüÒÅ×hÿB“P…O›Ä Û³+ISÞ·ãý]í—þ „þãúá¨Ùâ²V©lµçÇ Gž]£ÙÒ«±CnïÒ mÕ³ *AA7 ?=Åš×iõÆýb7vFϱ©ÌÓ„3Œ§s7^Ž úÄxßïå´Aó?´Sc„Çëô¡ã\·Ú¦²³VJê$PÂõVzfµ®ü™G^•Ô*]© ±ûýl8Pð™‚NSfûp*[®Z<ãÌ©¹̼ bOê®ÙP`KæFÓßgF)(îhúºFý„þƒîóÍÞý…¤2Î¥îV÷D çì‰óƒX«ñK4ØaKÞßÛ\ì­ø‡ÿ3t…-ûù»ç¦²_gWå׎J¤‹#Î]éÄæÍáfþ¶t$"Æó}º‚š›WTúMðyƒÁeü¢êTfõ2±Æo\ >vþK—à ærpIaB¥y&zú¤ p½ÍòĹü÷TA·£dKiûiŒ÷‘I¢(ÇŸ7—„±¦m㮄ŒA¼®.Ó©FñÕûßËly]9t›^ú¶,œÆž~o•¹×ŒQåØï®±#tí|dMì^ûsYqîé-÷ñ:-ĥܩРóÓ˜ñ<ΙšQƱƒ]7|ÅzSð¹q[qvÝ.ó0ÒØ w|¦ ãÜ= V§±e#F?½b¤¢ñ;?<Œs…µ *[yÇZÌãV@aÿèwtE«¶›˜Mc'Ä•_·îRч K®½ÂZhŒLWR£"Çq¨èóCý®7Ÿ7èX̳Üÿ‡½óŒŠjiö>F0cÆÄ1Œ(0bc¡ÆŒ•ì˜1‚ŠŠ10ƃEqf$™ÑF2F„Añ˜PP0!fÌï¿gïsÞOç†÷ñâZ¿u\ª†={wUíîêþ/;”Ä”Ÿ4Û«°xúz¯qó³w0‹}/½ûÑ4âêªM^G‘îq< · €Ýbß[³Î'±xs. t‰šT¹cê.Q±{+Çü9UÌ/GÑÏm6Uˆ:a°ëÔ¦ÒýI™IL^¸?×½ï%¡— bÃP¥o Óµë}-s£¨vPdèÔuâþÁ®zÔ¹°ŸO’Ø»ÇIc»Á® 8©ÕF“îëåø³âêožñàòæKi«ï„Î"ìNïßðÆícÓÉo¦]¢üòkv÷Þ«bÞL.¯[èEåŽø¾Yv/Šìu Bg±MžƒëæÉ”T9™¯þóYåª ´¤ðêÚc;T¬TO¦î›[ŸÏ\A]·Ä¸üÛ–B§v+¹L`Ýdæ‘5Ð\9(¶vl±µd…Š•êƒ›Æ7g¨?ßÍþó{¬Ð9…ìþõ‹oÍ’ÙýÊ\3ÚWá ®*¦×'õ$ÝtÔÍ(z¡q°ª\KèœÂî‘aÁÈTËdö}jX×fi w¶¹ËU¿æ§]x(O­MOý¸R‘ÐÉ„ÝÛŸ…»;&3þ?¹r"u rW-Zªb²Æ÷n5Ø7…¦&ÛaG 6¡3 » ;\’Û%³“W¹Ob©ëõå;“'S©ýWF“þ:„N-ì¤mzö4ï–ÌÂ÷páDJmµÏypm5;ЧN{c:ù®Q[-€>nEØÛ°»±¼g2ë±'~þø3‰tÆãyÔË•jöbÎÃi{ÏΠµ S^äºFS• j ž%îŸežƒv÷ðw£ß&Ò V奟¿¨Ù‘æõS¬œA{¹L¢y4¹_Œ3øZ(î~þF¥ ~¿”铲ÇY%Ñ»ÝzÇyjX¸Çõ[N§-†Í×wD^ÐMǾú˜°¢NMfÙ†[|kz%‘W­ÁOhXçàD×U§’SJ‰tÉ(Ò¿‡ }LØé–gÚ'³ü°äF‰{’Hºt”<ùž†ÕFzáMÉ »à~ë㚈›°k7…+`%³·ôù¼¯6‰Š\u¾“§aUßžØü"Ü“^õÝçw=Ц~·*WrDÄMØéç “™K„êRÇêÉTïjÈÊN×5lS¥À^;ûyyùš^CÛ¾‡Û:C¡ »üµϳ~~$™ªDŸÝš§Ö°RÏå¶)kï`èd–ˆû»÷Ã|ŽÄøÑçßd ÞåUiA€†í]5¥÷Ö$wÒë3DÑŠm9§.ÜúÐmóNÚëá€ñêwNñ:)"™+~ÚLÔ°‹‡Úï?ÚƒôóFQ´íÝÅW…>-ìôßo{ÔÒþÊüdòì¶zGã~¦_çô¤é^ 6ÞEQ¥Žµ;w¿ ôiaW·¿©¢G“jžm’B{꯻´§†éó€ñY¿6¦ÑT}ÞÊÎ]ü…>-ì½y5qÕÅ$V´#?`âJ 0n1×÷þâ›SS¼)³YÒõz]£I¿N$ÆìF^í•qS“Äœ+l[Ó80…Ö¼Y×¶™»†•êúí°ð4»zÇ|ÌòÕnñbÜÁΘˮ+“؃nÓ+Î¥P÷˜v5ìÝSŸÈ1SÈô[R¹÷_£H·Œ,ô…a7úSÚSÍ¢$öŠO/¿I¡ˆ/åNŽˆÔ°X.·£™Bߨ~ú,žkž†n‰¸ »:wïW˜?9‰ÍÓ ¦Ò—ÉQ}«eh’P£‡ §Ð«~¼+·"ŠH7Oâ(ò^;äË¢…:öMb÷ý-b§LL¥[Ýîµ~ªa¦_ǵëûÖ‹Ï»$ùºGQüޱQg®‰ñ;|Së$öo×M·§Ò³a7ûßÀs]ªK1‚Ë#:G‘^¯EÄMØÝ/zÒ³KÕ$¶Ê¤ð¯†©¤ÝxI››ªù¥¯³µiÌÑô‘Q4áZ×¼¯Åýƒ]íÙ{—Ÿ™È6Ÿä iTX|ªúýͶkW^t£zºÆ(ZºÝÿ¥µ“¸°s?¿$ èJ"Ó닥ѓ¾­roË4¬¿½•ßͱ®TMW`FÑ em§GFâþÁ®fNv~ÔIz}»42tï¸fK yàs¥Ý› f5°‰:’z½æxØÅ¾·à”ÈŽ¯è’z6º›±™.§Ô¿trj«~ôjáEú¸ª·Ë…]µ^5†LMdz}Á42vj»cÆõ¯uªãƒYŸ¸QT½ ±·¼»Ð‡¶Ês°ùR5sCßD6¸qØ‚QÒ©†4ÄsÝ<õ¯ùÄÊ}f î¬¡FÓÛ™Ánä Ãñ+ÌQwæEÜ_˜N½ovè»LÍôùß›ü×·GÔ¯‹ }vØ-œQññ‚ï lÏ‘®ùÞgÓÉûA¥Ã‰ûԬ炈‹ÃñœéÞ'£Äº»¸°£Ñ•Ýï$°ý=Ë//ùœNÝZÞ IQ3]Ú9…ð²Õ¦üž(ò­?bÙˆWúç%v5Wns*õ¯4ôôŠn—ÉýdŽ]¯'j¶¢Qô¦I\nnÔÿ?a·&uYƒA›˜¯né2ÝöH}áýBÍîŽ0\Ô#Ä“¾Ê=5¨I9é[»¨[`gÞÌ4¡dj“^ø~Nžv™^˜ÞqétCÍLƒÊ/m>Ì:Œ¿|Aé×EÝ »ç•¸pvslPCa|…œÍõWªÙrÝ„+)WÕÁx¤ÐûãÏÏÛ=ëŽyû¦Zó}ŸózqÆkáU5;0À}À¹¯.ô¶qÁ£Šˆ×KöÑL1þ`§®×­ñÚ/—X]ÏÓ¿…]¡‡g¦\s^Åôú†¨uIø³¢h¾ÿI{1þ`7÷ûùžón]bw&\þìþú •„ Y¼šTli¾r0žvW¨8ï}I$é×ÉÄýƒ]‘ž›E_b:9®Wé’Ÿ‘;ºͫUåá× ¤›®xù7½ûص¯î<·þúK,ÿÀëY^ë®RÕoÝØŽ;ÂŽ­Y/+{"éä°FÒ0—McÍ‹ñ»«nŸš%z\b– «æ\¥¡CÖÛÿg_; ‹jãJ/ùãò òoã=vÛúõަK,³I‘Å™V×(·ûÓ!ÁgÂY¶Ÿ)üyäï¹°“†6,r1¹ÄôëÖ×È-nýËÕᢾs§±Ô9=ßËœ±ÑÍÃ*ˆügƒq4®oÝa 7´µÞtùyd›]}5)œ-9ß²‹ëXºW{hµIï#©ðAbËÔ½"ÿÁN¿NÏÒøôOÓë;Ê ÀÌ1œickLc§‹"5Š ]¹SWÿþì ;¿ÖûMïÚÆ±+$KÍóo’^GêÓµ“9:“nú»:ž/ÝûŸw°+œÖöãŸ÷Ï³ÚºÆÆLŠl4ﺘ^ïl é¦ëEÐ`UnñJ…È{°«9,z›—â’útŒ]•ƒßóúèwM7¿qv³v¾Îm}šÉoxM«—E•dwR£“” ƒ<ß:aU·¸ º­!> qvú¼}JÌWgÑØ•f»©\0Ë—oŠøfëLü_ßÜÔÐ|‹v–狺vÇ’*\w9ž¸­.–çeјó­uª¼‘Ù\R×jñÚ…øêÛŽçZ¢èçkÜMÄOØÂÛ‚ó—“â½!‹¶ØyÕ­´=ÓɄǻ’N.ö¡†,GÙ5\~WogбTÇð$Û=þô­†U´4»Û²s®±AL¯sîNqºO}t\Cú>+1þ`ñs^ˆÍIÖgðº‹iizjbûµƒXaí°‚±îÓlCNë =ØÂ_äÄøƒÝ³WüA?ÁðòUgu[-Ý«÷¼Ï}÷u¬©®!êzñŽ\ ul×oâô×âþÁNÿÜž`AéM\ŸvÓ’þ9W0—Â5Ë]E_©†x—òŒ bÞv2Û#³ï£Zr_V7zì÷5¬äñú~æÉØ)‚é"´t|Al›,؃Èv7Çy»Ñì|AMinÒåWEÜìœçÐÕAÚ?øáQ¶é*oœÕÒŸkÚó?Òá—¾º^OU-æÄ:ì~ÞpC>Ê^¨¦­T}ÕŠý 2ÒÉdOt# ½0,¹zÍîà±N»¤É[;=7×°ÇÍ ³iÿê<Ç]ˆ_¥ý¶úýj:ÃÛÜꋺvë^'E4tT³ÇÑé?gÓ"× »Nÿ5‰ð²×ääØÉôGûų´UÓS¥¶wz¾w°{ÜÃp‡…§ŠÍär­M³©ü÷ùos½¨tÜU0<9¿Ò]Õßòl(ìŽðÇ:ð«Ì_£[eSà9ûÀâÄ´µÑì¶:O 'Uø ¼ŠŒ®E-i[GÔ-°›1qÚêeáálúE¥Ù$ë>ýÞð¹dåØôÅãÎã©Jø®qƒÊ©I×.P]ÄMصµûëÑÔÃl¾n¡2›¼ßªêZøëóêoø¼bG5 ÄÛBc7íò‚º=È?Sxˆu}qJ‹ÞÙdòÖ£uË‹¨þf·ú³='‘Ûá•AšÉjÒ¼¯{}–•°v¥–Ë“*‡˜ñõ#›F̦F·ðéä/ö—¸‘µÁÆÌÃËÕäR÷dú#7aWoB/¾ôÆžôã ÙÙôôJµ˜à¡KÉÚì¤éÅÁž47i¼Õ5kÄWôÄûìfªWÅ™>È,>V 5—MÍÜ Woè¼”†{¯l;×›tËÖcÕäÔŨëxQwÂζ˩ƒg`%³Þ¥ p˦>çî$ÍêãO¥ëgçð¿¨ÿ¶n ;Ý´å¦PV¥sÔòÛS²)ìuºé·?–mEƒÔ}=¦P­ØÕ޳ߍho-¾‚+Þ÷`·è˜´ÏÐÉûÙèA]/_š‘MW"Çç-õ^,êo/ÒµÍjUT+díàÏÆb¾Ó®T‡|/»![Ý/fN6yÌœ±ðÐâ_ºî$¯j\T‰¾[?íóæ·j”¾›5d¼y×Âl:ìù`˜íƒ%b…q•à9GUTÿrÂíÛ±b¾vßx9Zm;tiæÌG‹²iHǰÅ)–‘"ÇõQ‹k“É¥‰eÊçÝ*±OGÔ-°Ó¯‡0«–ü ›ÒüMe«È—oWØ1QôM¨þöžï ;Ýô¤rËóy:v^@6½µX–ÜØz%é×Ù]hbbõ­Wª¨½kúèvËDÝ »„Á¼Ay›X?Áõ%î»yìÁ*ên;f÷ϱ)~òÕA*Ò¯#ˆñ»Á3zFÕ¨¿•M×:¢"ʦ¥Ÿ–ÅÎ ¤o3‡)sŠ&‘¾ÿDE†¼ýòƒ¨[`÷Ó(1 åØÍlg\9ï‹°;¿@¶äZÜ*Õ?2.ô¾y²ŠV¦žñ­ñE¼7ÀN¿.Ì<|[Ý >·–j7ó¦ßS7ѪÓÉr9*šÑÃÑÞûŽw]òöíþA¶j=ÓVÍúÔץѾ\1ÆUA“¼s$/ì<èù‚õñÎU´ÈÐ/3ÁKÔ›°;±£|æC3Ç;³²ÉxjÕ‘/*¨tNZ|oÉÙ|-N¹è™wIÄMØ™dTóµÞ°š-î0$gY65>í;ví+9?Œ­™íùëyÖµ{ôë³° ¿õuãÂ{Œw«n^’Mó/ËÜ‹~*hA·¯—W½÷¤¨¶Þ«Ïªÿ¶»yº…æÅljšß¶~ˆGºÊu”f¤ò©¨ö¤”zs:7C\áe[†XŸ…]°½g…gîóß5à:/›:üÈY^2j¨÷<èŒäD¯VÔôÝÖÄ¢N Þ.vºå‰[rö=Бqº£AÕü´u¤ ÙîTeèR³E*z%¹>¼ªÈ{°»þhü+7›QìåEÞ€˜M÷¼Ÿ8a]Ðmlp#_°¹ŠzgÜÔì˜/Æ]WäݼKoºûIÅ&!®”´_ÿáø–õ¤Û¦°Ã•tÛé¼U›O0ƒÝÔ9)ÒÖrꈬÞqLºæÚ„Œ[~»JGz*ãæ©h[í†ýž‰ºv;Ûò†É94¹hk˜Áðl²Xèâ8Yº‰ ÄÖYñ|2)qt•ÿõǵõbÜÁn™iX÷wsý(hÆÿ†,›bÎÝÚíLUW¸wOKÁm¦ÖJ¶^O×™r«y®m4DEú¾.1þ`W~ ãÆÞ[vRÖ®ð­¥íåǽ·žjá–n4Êô¾ÑzÄ[}›¨;a×kùþöí¦¢Öþ!¨sõýühµlMÓ 7\©Þ‹QŸ÷mVÑK¾CGä=Øõ©Ç["©Og¥uçH-5(LáÛp•ö;Ï0ä_Euy›ÔJ1þºç9Œvþ¡íÞý„â¾NÎ-um¨4œaL•kÊŸU=íF¢žrJEãt‰@ÄOØéÒ 6”Æ­[„Wv-휸gï1m0u®¼×¸k¡;ñ*Èñšw¥Þ+îì¦é&ÎЋ'¾»®âýfI`¥OL•4ÅSyÞh¤'çiÁ@MCwUîûÎ@Ô°[ Ûq4–¯xk©¢—ç_2 %ùg½¿Sî¶ñ.±O¨ÉN“µ"∸°«?jÔ÷aÔ}­í¨äQZªöðئ_‚i®®±q ñ·ŽÆÔô¾‘Óì~×DìNÍZõ×ìW‡¨Šmy˦½ð½|Qws•Ó‡Á6íw<›Bþç†m*ßGMÇÖ]uÉ}«ñüûÔ5<¦Û÷nþœk¥¥»×=뱉˜öÆî QrZ³y’¶õUH‡»õ¿¸Šþ\ØñîË»ŸÃ)södÃëxN?æ”2òùFj9m@ÿ.rZéÜyžlššÜi>Õî‹=òtÿ5PÑ=¹Áìµ$Û±¨O·¿ê²ËŠjaóÕ4¯î©ŽgzëíÌ`ÊÛ™+ãý¥nFŸœâ,â»Z*§o Ê9ýDù{“~]Yô%áçMʬª©h|ïž…w²¨gdÿ5—l ßç†ZXyQ𦣛'NUÓ´úßìl-òì,u…ýQjÙýÀíýIY¤[ÊÜ@ºå"_õ¦KÏ-[L׈û;U0ßht”lt«³(4fñÂè)qiþ­Cw‘‡x»àp5-­Ì;DDÜ„]Íëo+FŠõ­,z ¯½ûNÉFê«ÛxâN¹¼qRMC¦´^–/ÆìÚû…|¼ö=’ Žæ{ø®È¢Ÿ?ºÌyphÅæœa”ïN|Wæ©p55ÿrêCÖr1O»˜Š#ÜoŒ"y/¾ KôéS¨j†óÈqžô°‰£ÞŸûëÏD_`Ï<ý~ÖhŠTì*|7<‹¼¶ŽíY>˜,¬¶Ÿ±ØãMæºD¤¡š6 Ùóâ¾Á.9µ5JÚ²6}0ϵK9®°òº1jݨýÙú°õTZóx“Ýg+ ú/31ï.úraký-×ÿy ÝÛýPzÓ,‹´.u}ÝŒ7Ò¬À˜Æƒœ§Ó÷A#vº×ÔÐëŸFæÖ÷»Â®ÐŠÐ1Ú¹üý]•³¨Ž46®‹ËzzVͼàûòbŸÆkñ„:*;[²ì̤ƒ²3“þÎLúÏ–ääâ™àß÷-à,(*WR¦wûÛèÝ–jºý®z·Å@"á½ßð 2€QÞƒ ß ¶á=±ð ” M$<{àTmy |#:E;Þ«†kR$D¹ï™Âµc$G™5ïÝÁµýŸ r€1’¬¬__ƒïÿ„æ­ ²Ptæs¾ð ¤HÐr;>÷ßÀÉZfÏçÀàI>'ß uåsÿuÍÛpIO^7Á7ÈF(¯àÊtnÿo×FeuQY]ô¿£.âqÆGÜsþ½p[' ñ H¤äe:·¿Îm©†Ûïªs[¤H`r ߣßÀÉLֆoP $–|ï|ƒ `Ô–ï!o Ûò½ ð ” M$A{àTV¼·׆¤èּǾĆ÷ÕÂ7ÈFRÞß ß Jy¿!|%HÉÔø•-ï¿‚o$W' èÀû€þ¹Î­±3P‚4‘”íPÙñ5RøF’v {¾Vß@Ф-ïÂ׎à#Ëþ:·Š|^ ¾É_BA0F! üT‘Òs!y]SVýß®ÊæŽ~ßéß­>âqÂOÜO~Íüÿ9%HÊø”iÜþ6·¥Úm¿³Æ-O`öÀ¨$|o=|#¡9E¾Ç¾ NnÉ÷Ã70F²“µå{_áI;¾¾A0²â{qm Zñ=i¸6 i H‘(å6|?|c$M™”ïKoP $íù> øÀÈ–÷ëÃ7ˆ…¶¼o¾¤ýC[IgÞÇ¿ Ùñ~:ø1 ÐŽ÷uÁ/P‚4‘°íPuáý.ð‹îôßзU‚4‘øíP\`‚BÀ (þ¥>23(Óm+«Êê£xƒß³>úwœCâc=@Ü3~]ü›+üwAr~eú¶¿¾m©nÛï¬ok$ág1Á7ˆ…~&|%H Îø•%?+¾‘𜀢-?³¾ PÞŽŸßÀÉPfÅÏ0Àµb ±æ{éqm C$J{àT6|3|#q:…”ï§…o E"•·çû;á#©Êlù>CøÅ@ÒïwƒoñõmK€ YÞ™ïÁ€o`Œä,³ã{à‰=ïI‡oŒºðiø1 ° ïÕ…ïÿ†¾í?Õ)­“x}Q6TV'ü~uRYôïW#Ù‹g-^üÞRü›„‚þYP2P¦mûÛhÛ–j·ý®Ú¶ÆH^2 ??~A1´áçÂ/ÈF–ü\=ø1 Ð’Ÿï¿@ ÒDÒ³>@ÕŽŸw…ëBt +~î® H‘åÖüü\0²ágÞÀ7ˆ…6üìøJ&’§=ðªöüL øF2u [~6|)’«¼ߣßÿP×–'a{àTù¾QøFRv ;¾¾ƒ_nÏ÷ÑÁ70F–uáûºàIW¾¿è¿®k r€1¿ €³ HPpe³© Êꣲúè÷¬Êæ‘þ÷ÔHÿ´>’‰g)MüNöÀ¨@.¯µ œ€¢LÛö·Ñ¶…¿±¶m.0As ?÷¾ MÞ†Ÿ¿ ßÀÉMfÉφoP $mù¹´ð 2€Q;~>*|ƒPØŽŸÓ‰kJ&’¢=ð*k~n!|#IÊløY}ð ŠDÊÏŒƒoŒÚó3ÌàÄ€Âöü,-øJ&’«=ðªül¡¦okÔ™Ÿ3ß væç]À7P‚4‘¤íPÙóýÿð¤í]ø~tøR$qùQãVÕƒïÓƒ_$~' ñ HQÈAè¿ÔG<¯—ÕGeõQ€AY}ô?]•ÕFÿùù#gñ¬ðï“®ð1 ÿ,”3P‚4¬ìP\`‚àå ”)‚™„‚`ŒÀ&à,(:W2€‚žð1 ˜!:%HÑøÈ&N@âA "`ÊA(ÈÆž2΂b A0u! !°:? ­3P‚4tíP\`‚ ì ”)‚²„‚`Œ-à,(lW2€‚·ð1 ˜!˜;%HÝøÈ&ôN@âA "ðËs-o|>0F5ášÒø|P Ìœ¤‰a|€ªו…o$ ' 0åú¦ð ¤H ò?¸Î&|c3ø6ãzð ŠÄœëâÚ@0jÎõïpm 3$g i" Ù jÁu©ðùHJN@Ñ’ë#áóIJÞŠëôàó1–¬5׋ÁçƒB`†æ ” M$3{àTm¸f|#¹9…%×0€o E²“·ågéÃ70F⓵ãgºÃ7(+~¶8® d#k~Æ5® Ä$H' °ág Ã/"aÊ¥üŒ[øÆHž²öüÌUøÅ@bËÏþ„_Œ:ð3(áÄ€ÿÇÞ{€Eµ}Ù¾˜1cÆ\f̘1Î2cFEA)r Œ˜‹  ("’ h! Ta¡"eÜE.D3ffÌ˜ßØµ¶ç¼î{û†~·û¾ã÷ýþÝçœ=¬°öšs®µWÍQÚςŽ%]3àøqø»‘„Íò}Ñðwƒ2`2ˆïÏ…¿C3¾Oþn ¥f|ß"üÝ èXò6@1„ïã‚φdn†òýDðÙ€)’»tßן !Ñ›/ ¥ÃùÞø»A0б"À xß]â_ê#‰Á¿œaú÷>›ÓýMÿïýsFè–` Q"®î²©ý݋ɤi³éÅtÍ~â»ÐW}ù÷?¯ˆhÛÈoóúÿöò uÛ±ØoìØ…¤o“[6›l§-·-O‹gýñHoëYiíïëùבãuú;ǵ½:ö(ñîó¯\¦šU˪µ*÷'Á÷Ù:7\{í­xü™¯"t†#ªwŸšr”Ò2ÛŽ:y™2Îõ¬Wº™ÍJ)%};÷ŸñT’ÖôôúÙ¬ïtV.Z’ÚñmPôwp9p™8qŠKáŠÍ¬? 3ÿ•—"­$ôq0 Š¾ÔÇhÕ­ ¶ù^&«ñÏz®ŽØLýŠè)¦]Éônâiæp¾cƒ A÷¡üì”ŸŽ‘àkq™îϽ3àF[ªZ©ïò—rÒÛ\‰§Úo–6¸¿…ùñAWÉÝýæ\»ãÞù°üö¤ËôyÒ5ºêO¹³#fVvp ¾ ¤ôI<ë/$ô©@÷£ïPrœ‚¬§U2½LÍþ°# `ý¤hßVKcÃÒÖ8b{˃ù¹A÷…›R(£hT°oÃËÑšïè@æñ~]>|u¤²E_µíš@BßMá}Ê¡K4Ô|Öæê¤7½D+Wwí>¬Í²Ñ7r&uÈŽ»Õ&&P|L‰»“óS„.üÅ¢~M¦Ðœ´%£·Ý¸DƒŠV–PÑþ9 å±.Ô@™qß-¦Fde>)a~˜Ðyšrmø Ú>²‹_½S—hfü†‰C–ÐÌNçïÍ(p¥³3+}¶1á/¾â1ß-¿Æ®ô“oŸ»ç½^ã¶Õ Ο.Œ]×Åå¬”â¼æY$7¶Ë$ÕgæÇÐoíI¼:å.[‰Ä™ö™»Om¦ðüV!sMÜèÊæŒËÚ ¬_óÄ.;Ýhðúá©TGol‰nö’iOn"Û-âõÛ»Q«ÔÈ-÷$Ъú…ëc~¦ÐÉïmÛs$$•Ö9 ¿D±¥¯–o+ô£ §Yß–s‘'Ðô&3ŸuÙÇüL¡3Ýkñîi* ýÇ/‘&)²Á½6~ûn¥ßWZu5âpT}Z—¿ôf)óÄîë›´5TiÓš¯†ß 龕ø»U²/mí|—\ˆïFßp_Â_|BµÐñÝ®‡mÒ*úêü1· éׇYŠŽÑ¾Tá#Ö4&)ås|2x´gŽð}C·}÷˜³ÅW44ç\¯Œ§ éÜ1ÿ÷nùÒý,¾±µ#Måmsh’•z»×Q6~#19£–¶:IK¿hYHE'ÝúMíÇú(;R}§á)_Ÿ'tu›;Q)lü ËjUµµÊý$Y­ýàØŠBš¾eÜå\•U{ÊgQ¦#uÐ7tL¤QGe5ïœtbè6$Ôª›yô$xºøÆ¤™…T0¡Ñ¾g~$ôƒu¦K¹3^ü—H£Ûåu¿Áú¶@'ÄSä _`'î]È| üHè¿íJ&§w_Ú±9‘ö>ÿ}ëæg ݉žçO8E«Þ[úß©SH n*Ä|õ¥MÏÖÖMót#¼‰ªfgiðnÍ,£*¸ˡ[·«ÜyŠΉisðÒEºµc›©Ü‡ôíp¸Ô±îâµ/&Ò¥å+ûšMü}µÐ™L¸1ùÜÍS´>o_¯À-)ãWN²e¤·3œµˆšäZUîšœH&{j¨uG˜Ÿ0tá«®UyÒî4µ­×ðຑiîäœÏ"»õä²ó˜:n ?½îEÆšDÚòzÎ é`Á¿Ø`T‰xZµ;ï¿»&WYí£Î_ èvdéðû’Õ”¹Êáz`ôB:d6¾uk«Déâzihæ ᮇ%Žž¦¤wUÓ§&üîÃÚèÔ¤‡ÊwšýÁßeÿ¼Dæ#Çüءӊž]éöõ4éí¥Ü h×áµ£ZvZN/â~)¿¼u¥ñC—ß]žHïumë û,|/èônFCÏPý1Mݶ. K6¯Œ_¸ŒxeŸ@gªaø¤ôíÚD:ó¼Òûà®ÂëÉ ü¨ÏÐìõWç]˧ù+z]÷>»ì÷}6ý´]µòʼn$ÿ¶Ôåô(æç ]pÌà£ïòÎP“¬D墭ùtº›E`ZÆræ÷á@o¯vÀ&’Чœtfîüq–l>3{T>Í=6äÞ–Ÿ+~÷M*iYë€Ê7‘ÖÞž¼É«£ +†.ü’îi“igé×ê¦æ_/}冊9ßVRß]Šfëö8Ò¿µÎ÷iéÀÖ•îô`~У+úœ¥q/‰û½@ï{ïzw(|ó‹p¢û‹x£ÔDZò©ÆùEߺÇóùÀx–x½îHðG[õ;Ïîž”pbo1>ŸAv?i)?èFgÇX¦7:G¦Ñ>µÛ_ Wfý[¶¨¶Š&o/¼xõ… ½Nþ]ûy"Yðmóú1?oèneõ¿rvê9ZÕˆïP–Gž¯T>ýqÅï>ußžºáÖH¤s_ê6ª;ƒùyC·4+kúÉ-çèܦ%wwæQèêCç‡,§¥Å×–ºv–Òúª?ËÞßO$U“EAËö2?oèjfÍ.<žyŽ‚l.\¬;%ÒÖ|‰ê¼Œ¶_tú5¬®”ÞÖ·YÓÿZ"…½›[ón'Á§^ ÝØýmû}9GšJÚŽ3Ì#ÿù6ùÝó¤ [¡oj"µÑÛ` ºbèò®mÝ“£€k¯Ì8O{;löÚØÌ“ÒÖW6çBãô_\"Ï¥r»'è Æ”ˆgÔ¿TZÏ#kÕäòÃÎ7t´Ïû÷t¥Q¸—ø¦Í•òâ©Úí€.§w :t9²—_ïàHýÄ ­Õðó4WoâI“ùvæŽt×nøkU"ožùäIKA'†î}ȯÜûg9r,3}µ÷[.¥W*¹{s)Uø ñ óhŽïçY'ÙøAg¥7Lçˆïâ7çl.IÍj¥LÒ.§ÑYfÕ²§«cùÎä‰$øE°ñƒn¶QÛo•µÔQßh3—š÷ù1oÃÛ•¿ûð5÷yÜQIô»šöJdãÍåéõ\iÉ.âÝçRJú‡³›‹½H1 èÔKö”k`‘µÝFIÞIë+e~ìÐéí'DZÒEX¤=¨–K®5ð0œåþ)›Ž%7|¯<´‚õÁ¶§AÅQëÖàõÚòmÙ¸AwûNä~u&G‚Oh6õk·¯Zpð*¢c6Ï:cÏ|Ô•¤o +fã ߯6œ£cíFîIΦE·÷½™\w59ï˜î@-O.ž¸Z¥¤«×Z.š!èdÐùÕytó± G¥}Ô¼œMå²­^VYó»ßí#Ÿ~åß²””ñv]õ«{ºWux+ŽB§íXÔÜ#›ª®°ú¼û¦7âv¢­%ÚÄÇJüÎYÜ„nÀÆÉ ®#Á6›N˜5¯ýeöÚÑÚòál¬7æoˆËUï†sµ?èúòíãƒÏ‘µh͌⩆Õã§Ý_­¦ ?àÉýî¾8tNÉü:ÁxÄ#½_ø9j\uÇ’7-²I?'¹æšÕ¬?» -vÐñu²’ø.‘ÑN‚N°>;K—žÆlÓd³>«™/«3™žœî¤ŠVRŸ=a¯FßtbèxLÅÖ³”§‰÷~’E Ž>¬uoÜjֿ׉:U÷¨¤E ¯wæÏòÆ:Ûü+œ¥^%±Õ÷\Ì"‘¸ûµ„ÊkÈ÷kø`Û¦ŽTË55<×CIû×­Y˜'t2è,2Ûon}†–ukn É¢½®oZ¬]K]‡IWÞµ±gý•Tzèóö#úÑ3W~i{†F^+½fp8‹Æ¸ó™w=Ý>\×ÿ°·¥(¦-õŸ¢¤fKIX¬¡ðcãÝ9ßf² §iз*O¬Ï¢dz5ö÷ñaý}çÑÀmšüZ¥$çæ­›¥²ùgŽúÊoýÐ-†§©Zì•Suݳh²¼ç¶ÝÏ|i꺔³ñ¶Äñm“1~Cž s´;ÊætÓÏÉ7UÝvŠ®XÄ;ÍÎ"½]ƒtÙ§ýÕ¨.!R¤þï•4ѶoÐΗlþA§‹<\ÿùöž[k\í°~b¸Ü{3ÙÆí ?Ûž&/n /ÌSÓ3YÇ—·'dRöÈòêMj‡Ð.Þ¥‘3Yšñ3PE£’›ôï™ÌÆ:+¯ˆÀò©jjŸÑížnX&I¶Lo'mBvÚž®êD£øvæU”ûìÅ—=ÙøM¬è3~„* HY¨êIŠ£¾‡› ¡C¨&Ä9ßÅþF¹’Í36ÿ ûø>¶}“ì$ºKßv¶Ï¤º}ª 1[B!^¼óº]²mè:öª’ù³ñ›„x­o|«¢èÉ1¹YÆ™”k®\ÛâI‰"ÌÞ‰ìié×5ç\N*™?èôí² U´ñ\Oë„Ú™4¶ÒËWÅž»¨Â¯µ®bPÌ“DÌ£q¼8?èÔô ¶i¾©æIÐÏ ²\¾öNÃP‡ªªO[ ­°~¾ã’.8æëD«Xý]o+`H™ÕäÓO}È Ö3’¯T‡’˜·yŽ”üö­ëïݾiŸ—lÝÝ3{銥›èçøØÑqO3XÜ £ZzãC ž¦+¸]ô·utoënºx=-žœ[%]¾›AÏf%U²YF/*~\ŸêD‚ï¯ê/¯§…îëÉè®C(hôãá©…ôhë“yÂhÐÝ££¿g¸Ð gãU¾RÍ:ø²çãulþA×áC¿§Æ*(ÁÉ@ñ0;ƒN/ì6r@ã0Òð¶caRºr3·ØdµŠž‚®­¶ü~¿3ùžÞïsl÷Ï•bã݃°Ž?®—³:.ƒ¬×öËZu6‚DöÆ!˜Gu¿ Ožª¢U¦ ¶²ñƒNØ_”“QûæKÏ ÏVî½A-ë ´¸Ðg>ÌÏRQÐÝùÞ¾lü Ó‡Ó¥è×Üù'ï Ì »Ï²Ž 6ü²£‰+u¶J.Ÿb¯¢€ñ?›ÇÆo*êùކVâèýäb:®c­žÿ,³wuQÔm¸Þ¦ÂCEƒZhmD#ÙøAwzþžöÚgQį~×uΠÁ›Ê2ö} gýù]hèÖeW/yªèogÖ”Í?èζ¬]Ë·o5«ÿf°ºU)ûlϳ- §{müdëj:“á\ïv‰6*J}µöªÝ6~Ðuãm9ÃöѾÍ{à ªùýÁS®F}o1ÁÇ£šók@]phó@ƒ–lü ³T¶ð;yw/-jÝêå”:4q ôKžYUødW*.:ö´¯ŠzÇî~½bÛ÷„.­rd_ñ^ºú®ŠÕêÊô¹æ·Ky¾´rËs•·ÊŒ.\*ùlß:ÞEHT}óÏI§}Å{c/Ö‹¤çüöHSÔŸÒ!C›·RQÒ©z×B„ýuƒi%âö‡o«ç»›Gó+tš2¼ÕâK"©oâ*Më.ô® ºÎ¬>wWÞ±@ØÏAçºa@ Ã'‘ÔéÙžÜA·ÓiœêZưª¶Õ:S!¥³?;ŽX壢¶ü°4aÏ »zõhÊ9óHªbÓɺUA:™p~q±M7u¬uÝlÍê¶!ý¦Šô¶¢Žì¹t~ã[ŽWfE»iÝŒñ™é¤·½¶ '¿¹³>­73x§¢jÅft:/|/2èôv‹£"hÂÁˆé=RÓ)jÀcCÛ0jt¹…ïTÃE”H馒\ |\ëÍÒlü ;säü¦ó\8Õ2ÿÖV¢J§]a+믥 ñ™$;õÊ©—Jwªˆwt]«›#%†Ó³“›’ÆÈÓéÞÛ;}ºŸßEì3®twv#ÏáµnÄ`>¬^Téæ áóCwÓKµø@éo«]éTÙ.‡»HxþëÂÖ;*êpÇɲy6?‹±~Ö¶ £J‹›önº96];¶vŠû.|¨ßöÎÍ%ÈÓïí©¼[Љ kðÀ®êöÝ¡tÿ¾üìW¯tZè‘#}hó[u ø©5ŽîŸ«¢>]¤»2º:1ti3uakš†ÒÙéÝ'ß_€ï…üY+”íïØÿž. K]6Ê…q—@—òß°Ç÷ ±þ¹Î&ž|º3ÓwMèï}›jú‰¨¢:CÚ-/ìÄžA7¬È!õb·]4åàü2·©xŸ²žµš> ¥¾»ùOÇ*:«ÜRíÜAá9•ºWóÌÏÆ…Pw}ažN#ÎDõ³îF'œµs&†;¿*lqב¿º¨³ýn%t ¡Z›—Õí×?=ç û½o³Ÿß&{¤¢ çšo±eÏý  žì?õâé$ •s…Ò©íœZ!CÂHvÀóØÜ…|]ÿ뉊ºó›½ðù ¦—ˆG|ûe¶“v_çA:ê§û§j„Ñó;ŽÏF»Òˆµ½fî*VÑ<ï”ËÆ:ÝñÛ_¥Ó*ó>âƒ5ÓiïõycN†ÒÙ¡“J\ûœ§g_òúˆpЧWøÓìS§ 6O£Øà>ãCiuÛñËœ»Ò®^#ªrä Ùs#è6/=h»)tíøÎ?hO£ãv——¹à>àïþùŽ®”ÚiiZóïÈÓY+ 2Ùs[èx71IP-^aÐZ^}ÜüͤQiô1ûוŒÍa¿ŸS39á ÝÂöýši†1Ÿ"èËÆvmg½‰xï`~RÐ]o1ñÕØã‹èS'~c6‚g^¼[súNê¾°ºFdãBÅF¼³_Ò_|çÄÐ(èoÿ-WJÆÇƒe—§Ñ‰ÚKÏÄ5ÛIÞó5Ëì¹R´Q%‹ÃId®OˆÌO :a¿Õ‰ªþîå»4 ß &}™tGJ…W_4õNÆ<;ð&SÉætëx›ÂµŽ†éT©ÆÏJ"³`ÒÙëF&ú¹$òÐ/Ùù$è~L|šq¬ê’„œo¼Ì(DÍíŧî ~uöÆù‡%‘àËÊÎ'A÷kÅâ[Û®XP-ÞæT”N¦Mß³´[M­=ÿC“Cn´îíØµg‚“˜o;W]•Q/äIÇWx¨õI§RçÆy=·“,UŒŠ_Jå_ø7Ið)Ùçþf6ßf•ˆÿëFÜ|×Ñ?w#WÐwC¡Û6æåJý?LóH>˜D¯ho\ƒù¹A—›óºr§jã8Ç®!GªÎH§£ê· Æåtÿîù†QÎ.¤·ûMH¢e½²f9ßgãÞ.ê†%W·¥ó‡tÚ¼íå›×ïwuŸ&zÙ81‰¾òɢ)tãÂ/$ŸIœÏ¥Ö1›”»4â.Q«?×Ñ‚¿rÙtæŸø0?>è~ÊjT7áÄAVíç›N¾Ïªfoû]o,ÕO $ZÚôÓñ ;æƒ ;gŹð—¡îK± x2éä6æïîHî^쪻ˆûÓè—úðxæC ]/ýÂv1gt´GdyL:õ¬Ýù§Éóm„b¯uN}gòiÙÕ¦ã«$b~ÂøAçx¬ÃµU<¹S1¿Ï=žNÕDéR¶Ñ”÷A3—wt¥›óø-IÄ»Ú/x)øÌ®x^·ŒãœùkéÔ¡z^ûù¥ôtû®ô39nTÞ¿KユŽÐÇOO¶+ñÆ:á\àJÎͪÝã.×Òé[ÐæòË·’㕉í‚'-¤ CÚô³h~„ù× :1t,_r&|è»ë’™/~Œº8€>^›å=·Îbª´ug§e=ŽÐ[ß÷ñŽa̺Vvk]&ÈÖpg‡ðOöÓÉ?L»oG»Íä߼Ŵªž‹éúÏ‚O¥uкñÛkö‰`ãÝ»äÍë¸ôïMúZUÏ ‘U¾}1xíKuvÅûLŸ°ˆ®‰ß^u:‰bò=7zÍ|Á ë=¥Añ‡^8ýcñFôñf¥Múù0{wzýºÒ©Þ®IdßllýuuXü„n–¼©¯¿ÛFN¸3ÈoQÛ)m¼ŸÇ0x0zKø°$êTã¥û»P?¡Óo+M—qÞ«Þ=KÆzûˆOÁ\í)ù5t¹õ3Ì™Né¾ qwCq7Üá,~Z•ˆ££®ô(|-ãbªImò‡dÐø-•ۻݑý>ÿhZT÷vSç$Jz¿$âÇ)æ‡ ]ÿñ³74ßæÍñO JÇg°ìMƒ/77^=Ç‘üq#Ì÷ Õ­=uÕ™&túã’¼¹'ífލ:+ƒ.Ü)Õ¤zSÝ_Ϫ×:ãHoìöK¢5úíÂøA·¿Ã;Û‰¶>œ£lL#‡ *Ê¿^{Úo:Á—s'H+6ÎIbç ºÄÐýÏ:eûp~õx£Ô >5*si™7ÕIþÚ¡NòǬ,×&±uŠpŸÉ¡ÓT›´¢Ro_®µþeÐÝOèW¤7µ=%?ç<Õ•–ŒË·ÊÅëõ°çO 2ZèFŒàwè}¹TÇçI/}2hsÊÓÒ¸×2rvZêîŠõïÃøñ-î$±yÌætM<‡äÎŽðåùǸÛ2H¾Ô±mMµ”÷¿jÒÑfõ5°ô,A¾Óµ{ø„Ÿu‰xo¼ÑŽÌ<_n îŠýád»ù×ò*Õ6ÐäÃÜЖï.Öy˜AÏ?„¤¬c>ôöTãXMÓ“è°›Eÿú/¡êx.cÃ4åÌÇ´zO—¾C6ü>O=' žwÔ+wÆ/žrª'‹—ÐͽÚådFg®wS~EœAíîùmÛ9pãï¼UÝÔ¹XžD›øão/Ø|ƒŽoáRW—2}“A âö=–ŽÙHs˜Ü» WK+¹¸§~ˆÍ7èFê7†¼9¾ê:÷1ƒžö½5mm 7ËxƒD);» 0â^õ\·^5i» “C÷kÿ`@Æ)—¯Šªû%ƒ.˜Û?ôv¯äN/cCoL,M¢7;óg½oÅÆºw—n³Ÿ*ãú]]mù=ƒŽÏšàÔÖi51ü™ÒzU©v/7óÔáæ…vñ›˜ï3tç,´+M6r¡Õ™,غ’&ÿì²ñsûïûj[ùÔ kú°y6·DŒàw%ªËîÌPÞÀ:ƒ…î–8f9eÈùDR*ã·J"ãpºß´«3¡kx`hÚÏë8ù’ÚáÏ+gR·>Œi·Œ6 >f]ß…Þ…^[>ü’ŠùZ²:º—“¶Y˜¼–köÍÛþˆË2:w¨ÏÜñ³‰w¾tDõ?V ®Ï.7]{såÎäŠtiŸê™ô¬êM«ŒçËÉzï€iO«Ÿï¬žš¢"§¼w˜̧ºÐþüÁ/.y`öT¯š™Tº(§w³É«h®ÞàÔŽz_¶§Ú-Õ_✺ú× ¾±Š u?”S'“ÔGVÄ7øºšê&zux¯³û½~¯87§/èJ}øƒ+¹VßÝëeRT¥GCÖ¬#á©„nHZÍ’DÞGJ¯/X#ÜÅÐ¥÷æŸä­àó_C£Lª4øÌÚÛqHÈCötÁpIýQÈoüê±óVAg`S"~iŃO—qÎü"“’?µúqµµŒ6ê %úØsÙ©“¨ûÁÎ_×…²ùp~c)·»ÇkêÛ:“–/Ø¿!QF—-§=i»Þ‘ÆFN¸Z;‰VÉ]r)—å7è~îmÖåÜ-O.á½¢œëIâ]Ç\ZTñþ‡š?]2ª]å$ZpÃïx¶œù=CWdÞЯ»Üƒ;uÊ1yJ·LÏôf‚7å¹Ô›ÐÓ‰îå\j5狊 ù–,NB{µS‰qîb‹éüÓLŠ»XO“„<µóR¥ E©ŽÄÿ$ò¶Š†wûù& «O žÓ-â¸uG4C2)bWâ±”æ>åë:jW¦Å÷i÷$$AEv|:JeëpèÄ“Ó7 óYÀŽ^ÔrÜÈLª¹{…Ѧbª¦ˆG¦°§ï³_d>öRï"½VÎêè,<[y¥Üqã„ýºLšÓK["«ìGm?ÞèÞCcGú²ËREk'°†Í»y%bæGË=ám ­3é³Ý(‰xë&ösæQ.ílHEjƒØ:­ …qA'¬o\8Cþ1Ÿs&…9•9~?•{òwÌâ.ñ$Tw1t—ªKª´ÏtæºêfÒ/îÛ‡gé[(oó€°±öV´µM¹±ÒLE©ÇŽ 3Ÿ&è$Ð ë'.v<ÿK˜Lj ß ÚF£Jå“Ì>̦!S,úVíÏ :tÅÝO}ÜÓÆ‰³Ü9ðÛŽLª|F’·98ˆÕ{Öô謻szoŒƒ¸÷“¦lþA—ߘìÈ}íö¥ÈVžIí,§ŠvÚÓòùŽŠ{s©—ç#o]{}Þ±©ÔÈŒÕ'Ð%¹æôXëÀju¡³VIoÌÆÚIƶ÷Z%]›O©ü´í¬¢x©òGÔ+–ï +³h\Úhµ×g”×qÏÌLJ©ãSTrq'©—6)Øž²†‡X>RÅö©X¾³-ßî7©òÕ«™´#w¸&¼cUø¨7·fúi;™ê7–Ù¸áúº[ÖöX_šIg»d­ß3!„:?Õ&m»¹: N§¢”_•®'Ø óFŒëûÕü¶ÒÛ›ÔåT¯ï™´2¶põvÛZºdCÆ_'º?ÝqÔ…N*º£Éú~§ÅKèØ9^n–kжM£,²t»VîB·¿ð?Ør$,–ÂÆ]PÒ‰‰j³}_„ûX3¬¾…pæñóœÎYTmâ‘¥u‹B(ý\Ô¿özøú·í+•Ô>çz??G¶ïPw;q£W,{›:<‹Æï鵯vÅ.ÊÙçŠP?ŸZĬ°0RI+B}oV5fëèn%îð?wÚ™ãwÇÕ–Yd¢ªy¬Å¼Pvu.û½…’&îMXë´ŒÍ7èÞo/Ý¿b» ×(µ.É"·à»’*ëÃèM‹‡SÉŠœg¿ñÚ_I·7=²ý8›oóKÄB‘rK"¥Á>Y4¢{[³Ö©áä»àu鯓³iæÓéo7ŽWRHÕµL®²õtbýƒ7®Á6ßI9û²ˆ?~óYÛw¶¢³k.pTÒ “Úvv/X¼„.åºYŒ;×¥ÉöîXí÷~i¡¨¼›ôÇFúÎ%ývèj%=ñªv°‹3[ÏAWzØÁqþø…œ»ÞH9‹jö󈳛Κ½½ôpñÿÛj…’Jïñv ºîC.<_Äuz^íÀ“GY´èÑ©®SöPÄìÑÛL²£I­}”ÌP’nXÞÏÕ•ÐíhvùA»mK¸Œë wú…ñó ¾mî^R·äÁ"¡ë‰ºRYG%=¯’;Ô¿¯ð}j¡{ðj¤¡.ƒÓ?&nžM~RÃöO^íýí¯>¼ë®· ”äö£g¥ò8æ¯Ý׋{ÚntóäÒL6ͦqÚÙAëöý~Ž­é°¯é™ÒDZž7îéw6~v%âÆGß8t)WUW˜ì;1›ŒøcQ4ýë(Ýê_v$ÔÕ‰4hú¤eÝýXà/¿Œ;x|PyºS6uÙúb™ÑÅ(jõ¨®é{[²yèwb_"«sØzº·ox/·œ³]”õóèÚl:6lô]å~òêV¼ÿé4êðyùЇD¶_ÈÖÐÕ;gº»›ñ N¾EýàYX6= <ÛÌîøjÛ¨kþOkZ>’ÿ…N"µøþÃá„›ÐuÑ Vp6úƒÙTÞkâŠØrª5i˜|áùY´í@öÙ/uik‰tož6~äÚaç™7~ÆåŒl²-5Xµ£}4{NbIGÔÅ´U©û·$TŽ,^Bgx~Ñ·6WqÝý®Y§ßʦÀJ‹ª7ºÍâÐLªlg=À0‘^?Ÿå´\ÌêKèÚ)³^ÏöâôÇ+ßfÓQ[ïíÁÙ¾«%õ^Rï†{ýDÒo§îbû)’±0¯×pß³æ&æP‹ÑTC•õ¬èå¦Ê'|$j;Ô”Í?è*ãÝ4º¼†KÝÀß99tsôì†ýÓbH,¬ Å=YŠ%F"Ûwbñº–š*+ÎǬåø_ î—C¯êòsˆf_ù8æÆ&;:6’ÿj"=©ºhiù!V¯@7`ϼmÞ+Öq‡¶i úOÌ¡­­nx¢ÌŽót4ö¤ÿÙhDv‚Õ›Ðé¯YÏõ9êôØ6‡îª6@~ˆJħýêZÎèûwhÖ£melþAÇž¿sõ¾¸VëC–ÍÂV[|oúDÃÙ,~B×qÌŸ¦ eœæùË™!9TíUÛYó‡&amG®/Š%KØ>‚p_Ø—ˆ…ºUÆ5zú jl‰ŒFWŸ{˜œìרÎÓ¹äù0±uÖà¶oÏÖãÐ‰ÂÆ—È8×ýófŠŽåP)|º,šP§rwÿÙTY3猵q]9ðüAs6ÿ Û»säëö“½¹Á½£}2r(¸]§©bÙï1fÒ)¿7?ê&°ó×Âç“@WþݙܛÛþZ„”‰÷ù}Íò›ûãØïlgÐþJ“*³Ür4•Õ›ÐU[|7fkîȺð§iws(èè†~}ll&-7üôªc->ÚÌå&?ècíJ›îÃÖª3çîóÊžtçbA½xzÁ³]:‹ôÇ:$TÿÃC¶Ÿ ]d3IÛ¨S>œþ¸ù‡jr¹ðc¿Äx:Ñ[Þìõ<ĉÈÞ½µ#(q.ÿƒ¶Ÿb_±¯ìË çérèà¸u>mMH¿Ú•Ø£þ‡ tÚ©U‡›XütÀ<úðíg~ /÷y.¿á®#‡7îó×Èè]Í›«[îµ¥´xþ O}ó ]?¡Óÿ|NëËóåVC5jÝhÙÜJ‰ôQ¥Š¡Iç:8÷O#»Î=Ù,’­ó  ‹Ú`Ѭ«'œ3ÔѼK#ßuµI$ƒ!"šIìhÜîº}suñ$ípcJ×#lü [ˆUôÏy~\ØøAéÛëè ¿x"‘Ø>5òTÜߤЧ„:%‹T,~Bç5’?±îÇÅÖ{·Æ´«Žz¿\s¬YKåïsè̾ÔvO<í°èwhëC¶^€NøÝœ×ké4«U=t4ôåòë¶;”ô…_vÞšK=&ÖLKXOç¿æg.oÇêè®+ûëÝk·§&ÿà\GúcmMT4DC[SU}O›ùðlþA÷°uñ„Ú›¸ö‹J¦YÔ‘Øià¢j ç(fÓñ¥ü¹xö»ÔÔñŸJÿôP2ø§‡Ò?=&ÿsôQªðN’²û‚ÿîþoë§ôO¿Éÿv¿Éÿlý”ÊY?îÿž%ÿ_ôä®ð,ù×|Ýþ5Ï’?}Ýþ£¼o½þè«dŒÄl€”S$j)ƒ"`„¤md@Ê€ ’¸D‚B`ˆ„.^@ J ÞKöfÀ(@10Fò·øð¾mûyôO}ôO}ôUñµÑ–ºè¿j=ÄÇ6ÎüwÂÿýæ@4  ˜ 0I@$(†RbàÔ ˆ´¬A0бf<€c4 ´ ˜"ÀI#¼!s PLü$ CB1ðjP DŒÖ èXü§·ä»·¤ D‚B`ˆ¤ ^@ JIÂKfÀ(@10áõAЂr`Š„"rPŒ\Ì h@0A²‘€HPÈéÿo’ÿ/zoü¾mÿš/ÉŸ¾mÿÞ¶jP DH¸Ö èØ…ð P Œ‘-@Ђr`Š-rPŒ¬Í h@0Aò–€HP ‘ÈÅÀ ¨A)!±[ÿOøÚòÓêÿö:蟽¢ÿZµÐ…úçt¨¢úg_èÿ]ñ±Á‹'ÿÙy½ZPL˜¤@Š€‚”9 (&Z !˜x5("4k t,¸™ ÅÀÁÎ-(¦~R EÀÐÈ€”F ˆ…ÀAÒ x(Æš hA90E•9(F¨æ@4  ˜ ÀJ@$(†¶bàÔ ˆ|­A0б@l<€cf ´ ˜"PK#ms PLÄ% C šx5("xk t,Ø› ÅÀÁß-(¦HR EÀ‰ÁÈ€”$ ˆ…ÀIC ¼€”‘¯‚ð Å x(ÆH0 hA90E‘y‡ÿuï‘ÿ/zkWxü[þl÷©ðfûð­µ2 eÀ W"A!0Dò/ ¥@„dl ‚Ž%f3à #Q[€ åÀ‰[ ä !‰›Ѐ²aÿã½±+ê þvùç™Ù?uÌà?Oô_mOèŸgfÿûk#~¾ËظñŸÿïÖ èX 2@Š1—ZPLȤ@Š€‚š9 (&r !žx5("@k t,š ÅÀÁÑ-(¦–R EÀS ¼€”©5:TÍ€P€b`Œ k€”S])ƒ"`„ld@Ê€ ²D‚B`ˆà,^@ JÁÚ ÜfÀ(@10F ·@ Ê)»ÈA0B72 eÀA_"A!0D/ ¥@„„` ‚Ž%3à #YX€ åÀÉC ä !‘˜Ѐ2`"ÂëƒHP ‘dÄÀ ¨A)!éXƒ` c È x(†ÿ›µ#¤îÜ$úÿöò ýz’µåÈJKÊ9¥Ù^½l6ñ®e})è+ßNjWñÝ$Cr–ý¾ž^çMжkO~mâNG|{d"Ö‘£SÇëÃë¡#§Í™rc&Ù§KñNâ™?ë›]vÚaÇ‚~›¹yW;zÖÑØÎ›Ú\סÛ3j¬iIÕy;¨Þñt¥ß…õM‚nò×µ]‡I7s…k¢Vßë«&¡¯”ëßO[«wõªu’õKÂõs˜¤4»´™ã»ørãt4,9²}­UjæwaCÒÐ^?&͈§ÖêÄÜ™³Y¿èîÇü\³©š?§á t4ãíÆñ– 5Øõã}‰„ôíG-ãÉû•$sD%Ö¯º×›Õ\4ØŸ«íX{d=è|¾•=ÏTS~{Þ°ÜR~tï>eb<óUfý’ ^ÿuÁ­Eþ܆óÛN»¤fÿ݉ÖûpWÕ#žÌÊÓ$¬Ï‡S‰x‚lÏÜ×ý9ë'ªSõ®©éüb—1œ¨ý Œºå߬_Ÿp½׋Ž+ªÀ }ÈutCeY=ó–š½'ÒÛ!ŸWü¥ï‰º¸[’jS·(y`;ÃjÖïÌ–œ­Qí°‚Nõn¿¥ëK†ëÍ8äi\wÏ&ÿ5µ“éîëÇ#×’PäÙ…CmWü¥?ž × «c0ëN·½ß0eçÉô©ÃX'ë,ºâ÷ª‹ã:Å_úÅÊq}„Á÷™ c¶p|W’¶ø•«¿~\²1™îí¸Ýû€ÒšžÆ×;Ù|±‚66ªÕÀÃõýƒN胻…3“î^„ûàËÃWr“‰¿KÙZ‘ÆeíwM­w`yû46.ÐuKØpøýƒ-œàz”Ž:v0:òŠæôÍy—´FAB?56.Î%âA-»÷¹8q+·äµ¥³ß¼£äÔnHÕ ªsÉF,¾n·â/}²E¸¾W61äéV®vÝÌ£»1úVËž¥ñ.×g`ž6üuªÏ«£”f}_ûhœ=ës®`>¬ß túöγ·q³šn²m<\GÚ“-úO«yŒ"§÷H=ÞÂüc_~ð=¥`}~X¿8èô6§ï·q×£µÍc>sÔ²nqÏ`¹‚ÜõÌ…~èZ\ŸYøüQ¬íÜ…£]¼óë¨Tåá}jбß~n&)ŠË|$=2¢Í××Ì:«5‹ÎvâÆLãŽtdý`èõ>3QR=Þ¨Ôžì¿÷¿DAÆíãŽ<]Àú¹”ˆÓf×lêßo'ôõÒÑ2ÏS+Ñ=ËCéE£$4ªH©‚ùŲ>GÐ-j•Üô@å`nVl¤¯<æë/jK‚¯£‚ùÙ²þ8¸~ÍvÛƒksAÖÓ*™êètØy™õíc´«ù’îgÚ¾=õ@óybýq ;²¶{ÃFWƒ9û¬øëé=t3&öFZ«ã´Úë¢÷cÃ9ÔgÍâèî Ì_€õ§‚î—a†¬“ÕNα;ß±ç8=Éwì±¢•|8h¬ ý'ÆU-)f}q}í5KêTÒîäŠìùȪ£iã&]MSgþÔV”³xDü¥7ë<³Z ë+Mká,~¾n= ³Žê÷¬kåðå8õ¶˜!©4y 9ú.O²MA«5ˆwkÆúAw¥6ÿaV×aAÝßÚëèjqaK³úqš¿Áçßͬq2æ¯ÿAñ 3p-w8`5ì£ù.®±ÖwV¢s Õl¹~×< I ¾†:UÐ`}#Lïpý ó[ÛG<ÙÅí©¡Öi«£µëg„´Ø˜BSæ{.v¤nmš]kPÁü±ØxAW]oLÊ5<4㥴•Ž6¼×xS ëóäL3FóM ò+/è„þêaÜ¢åÓ<¡“Üx<ñýúúźo_Þ°HñÛZ?^Ð }­Â¸GFžËº5×Q·Õ%­L!Ö7‘"~oø6÷áÛ.ÝZ^gq:Á·"œó‘e‡÷^ò»ßù“}¿ÇµTÐL}ƒ!–—p}ëÏÆøÄ‡³8¦£×í'Oïµ=åwßÓÛòº-¬® ¢“ný¦Žfñ:õÓáyã"¸×¼Jã}=Éþà×i>MzØ÷cñ] /²ø'-¬à;GpBí1òìò×)TXHiY[çÒ–Úâ˜åÏâ(–·nËúOA7­òÞee“#9zñ™¡}O0?kªh®}G+†~;ïûAx1®¯Ý•â?=‹äx· —ú:z_¿Á«æ^'Èì£÷£¨Ñs¨éíC“?Å‘¾M_ËOÐÙèônnàk“®µO ¨sûÌéeCgvñ†Ö Zô&cä(–Ÿp}ÂèÅ›[–íæVwx¯k[GG! ûš)e'H©*²[9m>h×êØKÌ¡?"è¾VûØsÙnš·ÏÈ€©Ô¸ó6çoRL7JÁü&Ùøàúe·Ïÿñqwõýâ&x®"/õOýí«¦OË»d>àåçÌ!¬Ïtúòjõ^nʨ~»‡ŽIýí¯0êm¹¥ ¿ýù ¯±¸çV"¾x³ò¨örG£ÞÄeOÂûŠ(i°ß‰~=÷¼ïŽëÿôåáúñN‡žrÚÇõÖ ëè­]ÖÔÖ©Tëݱ/b¨æÇ—~ ÖwùÄ@g©7ÜÇ]Ïív›JYú»e r¢¡Û[Þ RÐ åŒ5±Ì×Ûg7VzŒâFê̦RPµM#ws¤Ò;¯Ö¸mEüiüørù&æ+‚ë âø¢¸°]Å…+Sé˜øôè­•èŽ÷Ç—Pg˜»~Å,9ËG¸þ`§qkìç–g\܈û¥hKçÄõa©4©jÂF£\ }Ö=IX÷Å»cŽ}Çêè®±°Ø:{?wcÒ̪fgSIðM·£iG¯=•)¨?o'Ñ®_ýâûmgn?·Ç†ï ¨£°–ßÌ ^¥’yLå.·µ¶T`Ìw"Tü¥µ{‰x”øœ_­z¸܆€ºMu´·ãÛ)½ÚiH4:§^7O[ ¯ñ¬ëRÄZ.“ÓZ¸²úºþ £,œw€õçDœKé;SC•ÇW®;pÅ|Z9hÚ ]>¾¿˜í®?dõtzûßàÜŽÙ)‡b¯N®ÊX¨Ù ùí»úêDíü-âÿ2O%ÐùñvÞ÷pZ{Øöé £„È7›ïÕwhQÊÙ»-ã ÊxÚ2&Úù. ß¿ :~^­)ç|§Oˆ’!_l«v±àXŒ†\Ο߲ҙx7º[P‡·«²zå6nÐý¼êõ¬s9·Ë¸êìz=u4øbQ„†zé‘\éþa¾t< þº¬ž€Žw³$çÝ?WoòûŽ˜{å×h(õõTˆndþSR|òó_ï÷bè7òNÞr.%vuN×!::J{¾}´Ô°~µît{ ߘSA}ƒ]#–¬g>0 PÇν´TÎÅG×ÕV££ÖVN_î«! ãP·þ'¥¤oWzMAÅn}Kf>0ÐÍ+ˆ;PÎ’ê¼BQÏ&…¯žÚVCú¶v.ä'™Õó™‚¿Ö7ºõ+µ+²¶É¹™»ŠëºYê¨`Õ¤üÕ¸_x× Ð}Ž”Ÿ8$±Ú'm´ ì\7áõ$Ш:ðiñn9÷òlùÁáóQ?»ðíòo\¥ã±=)¶m<àù@Áê\6ß ›5ýÌù[±rîfÛÞRµõhtÈÐNC&úkG‡cø†ÌÈÝÒzLЭ¼ßgاcrN¸ut"öªl_ †Zñ6.ãì¨^ïó)§”ÈÛ½¾d}÷ ÛëÙâ`­::PdÙ%9EÃê1 ­Ÿs+þä¬ xgEVâú=4mË´rθ°öª^Ût4fù¹ ××5Ôòܦ%wí)¿Šõ£X‡þYÏ,,žï]:a·ŽäUwÊg¾ÐüîÞiH^Qñt|øšŒõXý‡ëõí¸cQ'q³Jóßi~ûsJõ ïãY¿`áó‹q½à_­£%ñƒ{W}£ùíÏdÜ©´÷‡€xJÞra^ñ;q½ö¹ng>ßûVGv/>»y?ÔÐú¡ Û÷s¥Ï¹Óe&‘ñTcÝãvÃß#tú¶›øž[ºòâtµUÉk.i~û÷Ö\XÖ/:ž|Äv1?A責cíÚßÃzðÓÊIKµøüY±a£D®t0üÉlÃÄxzó芣MuæC‡ëŸ•yóÄõ ô :åÜ …»â›Ž¢2ŒBª5$ôÃw¦«F³Êû‰'ã]?çת/|ƒE%⺃Šßgæ’ਡzΊ­ñéÌÜתfÄUjÜaÒ–§p½à &çRMøŽ¯¹ÔnǦíVYÚ:ØSÓN¯•½Î‹§”ïÚYûÅÌw:‡ÞÕ­1Î<¯ô>¸k.Ù}ð°ÝvGC¿x;Ïö”øÂbÊ„ëñtúÚ²Ëß²Ù<‚N½DeÌÛ»^«œ”s‰ï^­\CÅxm{Z?wìFé“xê[Õ%4›GÐ ý圿ûÄ£õÆçÒ¨Y¡?Ÿd>Îö4yÁ•¢ùµþâ%‡®Ï¼ÖžV¹´~ò[IÝOþŽ·úa@NÃ&vÍâ®ß9:3?ÞIÎ ¾¨¹´ìØ–»f'i ogÈ‰â¼æY$°:‹Å?è“@×¹ÅÀǵp±š„—§ÛŸ'·¥}°dÓ0?SgrnÁ ԲǾC_Y|„Noçs}?Ç»cßè}žÆïÎ8”´@óÛ¿]è›@Oˆ¯ØøAç7Níùãô~nÌáý¾ ;O5/ì·{3GCñçÞ6üàJî÷<·=Jø‹?¨ºŸuùÜ~îuø#™íèúVîÖj¤†x·ï·QRº©Ž=jò2Þ…‰'¾¼ÅüŽ¡;ýöµÎzÑ~ΨÓÜw^sÏÓõZ[Ü :iˆw1H“ÒßÛ·Òß';o¯³Sðõ5ð(ÛòÛnfû9}ûz×óT¾ªA¥‡55¬Ï°”Þ”Hƒ”ߨ«ÞhBðAׂ·k³Ÿ¸¢M“_«ÎÓΙ›,Þ¼O¥ªy Pâ¸RÕanŽÖJ¤ÞÓxG:æ7ÝõŽí†TÝÏYW ÝÜbÓyêÜ1 ¾äi*˯.¤oçÝ"‘v•L}&ù!¼O tYÛm.\Œâ*t¹º.ì<íã’òž¤Rëaöt w¢Áã[£tJ¤nqÃ;=Íd~ÇÐ èœ9oFJ·a¦u¼üàyšßÉͼó[Ôý»GÏ¨Ý õNµ^;@wí©{÷OÂ8È¡úGq&z#òótøQ òEuÍoÓ•ç»™$’釀KËj³ñƒîÈ«W¹(îòÀŸcœ3ÎSBr×ïç0ÿÚê7^ì©aÑ>_×Q‰Äï2T©ÌüŽ¡ Ú1ÒÝ%г›—p¹éóTíõÖnuÇiˆ{'”°§Ez#«DšÜôsÛÝõ˜ß±g‰xzßÁ)ý&Gq5§®­~zž„ý …n:rdE¼=%O¿¾¤sl"ësÌür¡«×têåï}¢¸cd±øöçóôÀ«dÂ*ijKïýÊ©‰ï¥ÃFÓ²;‰tejþã꣙ß1tç¿8¼ÉjÅ= •EúÕΣ'x,ô×P•c£v¾~êDÂzWI¡}F\˜‚ùUC÷~¥ßèx×™mó(Á‡/è5¤·!iàÊö”$ìw±ñƒɹlÒ}\l}[‹û}ó¨ÓÐÛæÜ4Ä|é+o¯8PIug”5^zXÐÉ¡+rÚ¾&`\Ýñó]GÄ#aÁÅQ%I¼:å.[/Ü_Z\¿«uKo/Û<ÚTís•6ƨgôãçN×g&Iª¥d~Â}\ŒëÃN_|{éá>NXgåÑO›Å×Z¼I¥ai?_Ü9îFú2íR"etŸ°ÿÅ7Ag°´D|û›e“y“z¥Ï¨+Xw; ½?m»+Ý0^>ªTžHN¢‹½îeó ׇç¢ó˜¿H*ógt"Êm=pçîD*ð¨2‚¶±ù…ëõíá¿ïã‡óóóÈbMØíõTj7{£Ÿ½Öî7Iû5ûh"5ê¹zXOb~ðÐ ~Ây4ôQšçò²TZeêpÀ`«=Åv­¼ï^V"É&t›ya$óÇõñ ù¾<êZué–õ54Bß ßž:ôU>Ü~!‘J¶_À$\/Çõ£=sö~oŽìÅoô] ‘9?~wÕPˆ×ê!V¡´­É€ÈÍñú.šJظ@··ß€NèžïÝ‘ý}†ÿ'²½3®ŠWm%…•ùÍߨPÐCw¤ÖU‡2Ó(nìÕ1vô½@®ª£©X¯¤4»´rò]gö-•TPëZ~‹‡Ë0ï;sc»LŠâ¶”õöÉž|l¶ôÎMEÞüC\I9óñÚqí•4üýÒ¢õ9lœ »P‡7Ö¼@M>jÛ·ÐPo}Á-%]û)gª(Y}ç-Œ®·ÕæAŹñNæ}Tã´}e•ÇîŸÉö“)¹oz–HžVGÚÇTñÆ :ùo¦oŒâTM-Û{æÝh£LAäÝy®Í•’¥²…ßÉ»‰dÇ?j,èdЕõ±öLH¹@W cƒ S)c|f¨ùAW¿ò°ó¡{‰”îwI¹°­p½××Y™qbìî(.¨ —úìâºu°iè±´TæsçBÖnôýô<‘ÞuØÕ¢— ÓB7-$i÷ºcˆCöˆž_ ºó¶œI¥“[³–ÔêîLž•ø'g‰ÌQÐC×Ä*l½UÕ|j1¡KjHz*ÛÇu¤Z=d]«ÔPÒ®…¯:m¾?ƒå%býËäGqÖ¼v›|šky….§Ò¸çÃîìw Ôµ'§UVþu>Awºûi‰tŸìžÆæ›å³Ï‘Jz;ÝKöt¯µ¾#aýÁætÉÆ+Tg§å“ÃÆ ~Õk ny¥a÷{ÙªæßLI/²,¦Odó ×è/ þà–O öÞ,·m¯aãá@NIuêNUҧ³Ën³ù´¼"oä“Gç¡ãqßÜ¥}Òj˜#« •‰#r\_¢¸3¦'>GÖÊÏw#òi°á}¹ê„+½Ä7è@ILVó %ñÑL\.è´ÐÉ·½µÿt4Šûœ×¥ûæÄ|r«º2¸ïp ó¹q¡Öm{?Q’±ÝñœÚÏX¼ƒÎVo´ÅõKé³2=Ÿx7ò(Ìß Ó}~ŽÚ!%!î(É ~ʾ;oX¼[Q"ui˜lì†(Žì¹.×ó)®M½ÔRÔIx‡t7ŠMq[x¤šŠbª„¨—tÆWÝÏãÓ£ê9Eqž¿v>+|†qrM}ëS’J-S#´t'·âñ^~VRã—\Á”½l^Aw:ÒåÔ QÜ!ËôFk~äSì•SuݹTâÝbgw£Áõø• ’ôi²?›WÐ à‡«I7u\˜ýº”¬{86•='v¥Uzà %õîÃíÐa6¯ »qèvÓÅû¸Û F¾ÙÞ®€ºÌXÃñ@*Õ±^Ú$ð¾™^dzrº’ù²ùÝô…Õ¦LRîãø§ce} hžÞ`uÞg׆ÝÌß¶¡¿’ù_±ù³1‡bm÷qÂóÕJÜïsô~n*óÿ”Е)FÖƒ•l]-|/ÅÐM÷ùV©þ>núÝyßM+ -t'\Qí©v«õŒ:ºtþ¬Vi¡¤³3ÂÛ­`óle‰¸Õ‹‡[h÷rüìØ9¯€,õ˜5Ôü׈’to ­TÄv|áÏ·â|LK{oöœ6ô÷ÿÎX³çÿÄg»ˆÅÂçáï7Z30q„Õ^ÎEÿ§€Ö5;<äÊ YVvhØë‘=Í–½¸dŸ’:Jf>ÎÆºqVTÞc¸— øÀ'‚:Òm¶tí4 í6Z6áÚJGzj–Zóóy%Y.©»0L"è$Ð9ôy>wí=ܺ7K ç¯/ ®}ýT¬ÿ¥~ÆSI4cÌæYg ºYÚá ÜÃ!y;÷ó/ ýæ]Y:[CüìëwÛ™f®‰0ÎSÒ¿±c÷Meã ]µ~±’íÇvsGÆÈ¿ÖØY@6‡þ˜ª¡ô€õ•MƹQõýkëG(iV/ËŸ)gØ8CWžöåjòÄÝœùÍŽ;{ (8¹ãÖ[ã5¿ý[·æç¤D;(éà›ÅÎ[ X…îVYór"¹cÕß$.. GKöŽÖPàô·¾Y:Óyo¯¤{ƒC£»Å :ƒU%bïŽ/oOÉ ëôÒ\Üô²^Ox®áD“³²Ï™)iXC¾¢gã]ŸËEƒÒ"¸k¡N­ƒ’ èøëÖÍÔà«äHõÍy‡0%½löró¢@A'†îAͳ?;Žˆàž`Ú2¥€šÅVaï¢~MW@&µp©{ò÷|[3øå9G¯¿æ§bèެµTR(gÅo¯_Àç›æÑ÷äï}Vᜠî³îg¶ìÎæ©W‰X¨ŸC¹ûÆ•eŽ ȧfHOï‰'éåݶofuw¤®í3ºÝÓ))áš•U5wg¡«Òå¶¶µb纎_yP§ ³ŸÌ8ùû~ÉÀŸ\Q’þqš?›ÐñOŸuwqÂyƒZµ¢(è½ùIæèÊ|UÔûöT›ìû”@ǻԦD‡pBž* ˆŸâ'÷9I‹=X_$¥Á² ÷¹ÕlÞáú¡ß̉iÂÍ[õÞÒÿN™Õœ¸²ÁImàñòæ27ú¹ß:î<ò@}þ±ýN6nÐ)f«1×{'÷è–EŸ‚ûtmaïcÍßj¨FYSs‹ïRº¿xB½¢kJ²Ê;ð°qƒÎ{ÔðõUs”x!ô:t“¿v\U¤¡}SÆ t¥hó&Y‰J2öɨݞÍ;èú¤®÷nM0ǯZ—PΛ†#Ghè@%~ÁêL÷{ß*Ù¤$ýqK6ïV—ˆÓ§µsovpKý§8=A\ÙžàÑäâJiÝÏ;m)ûà³—Nˆ¯^]Wäšcóºe¼ ̳ .qT•ä…Ðíóo9õſ̟NIŽó<=•l?’tWWšúæávN–žÐ•ì©o­“Ô‡ßÆhaO/Ó•—ûýµÞ’@×ïë%ów¶qŸ>ÞØü¸ÛÉßÏwdæ]9yPI>Šo‡²qÃõuã.ä:È®yÎíëËšË//FÿËþ£þqy¦’’< ;Z‡tç6k¶íÝÊ5½58¿ò«*~=½cU‹“ÌɉÖéøJ²µj¾&lÜ ‹ìw»ÉWÉ®Gß°›k'Ÿ$󴙺°5.ôùˆ²WÔõ7ˇ¸^ð™ àðÇÊ ˜¿µ'ìûÒO+Š÷5Pý¥¾0XS"ž¼½ðâÕ›¹»¡Ñá¶Ý›¤d4òZé5utLù²ñ›_¸~eÿzO«7qº­€œM̪' sà*öeøÓ)W•ô¥·QU]!'è,ÎèºÒžû;sÜÕ‹{·ZJÙsW%óŸdãƒëÍêṳ̀†/Wjªû0DÊüÉp¿ÞžÔ"f…’¦8O/ËÆ×›óå„77ë^Ç\¼¯µ¯J޶Ìqã´íù‰.$ì)ijÔø¬_­Ùø@·×ìäýhóÁrãú¼4íüã,Öa‚qì_ÆS‹ëïÍ9ýjÔ²õÜ…5}Ba\¯¥º–ÙÜEMM­ÚÔùv½¥\ùÀ7w4¶§).gTn)é•c5®n¶ åë_OM7Ò÷mµkŒ:Z@/ùcA&œÞfü¾=Y…—ö8f¨¢•ñ ¾î^Ãö!×c=Ú¼ÿ‡iÞl\@CF¤Ì:´ˆü"iµÕpI›¾*’VÛ¬\ü‘=€®½~ÃÀ—ný ë|¸€ÆÎ]þÝ>l!'œÛq¢ÐOÕí"'«(‡·'ÿ$èÄÐ õÉ&êdh%ŽÞ_@³õ¡‹¸Šçßž_F]š­¢á‘ÛfûÐ õ‰?5Ég̪~8cÿ¥äÅœCŒòþäÉ®T¾(àí+5Ùißd‰›ÐùùžÝÒnðª²æAÁ&ÔñAºV•½<¸¯ªÞ/q¥î ö>ôœ®b~ÂlþA7«ßFôîhç¦å[ ¨áðûÕ Ö{rçšl©é^îBˤ;_­#é/°ñƒî°ÏÖ»gÛH_Îú!N\9æÞn)w§ÁÔÚó?8ÓÆ]&Žj¯úË>k1tvyçõ êñ±‡ác¬oä}ÌÞê¶”Ó*<ªÆ;QùÖÅgck¨(ˆßž+cÏq6”ˆsÛñ'“ƒ©¥Þ讀Þ>0ŸÝ›''ŸöhÂåŽÔzÕÄ/~)™ÿ4?è~¬úcS›ä<ˆ¯çó’²4=<¸Áü#{ ¾•™û£žŠ\ù'lü Ó -ÛI–¼3_IšvÌyj·«XǪz7¼µ´—Š 2jNŽ[!<ÿ‘@gêz¢û‡´6à„cå¦5˜°{µ+ÇŸâ4h)¡Þß‹×=·F<ãü{Ž]õž%ËWï¢~+šØPÝj9Ú°ñÜÆ¦“v®ë$¡ˆAþ RüUôÊeµ˜*<7’C‡"l×âI¡´þEÆš˜Ùtréô€Ûßçr6ߟ’úRB)Û®®í§bë\áy¨º|«Üœ×•Ã胿Ëþy3 hpRpmúLN›ìzÑÙÝ"Ý]zV;¤¢B‡¨cmö:I¥ÉÍÍäaôHËàбÃ?²Ú³à„ºß‰ªZšùXnU‘¬ûÍäÂs[ƒ%âA9'§h¬ÂÉÆvjÍ€ ´Äb¶«ÑšéÜnïó埛»šÑÁWEeS®¸Ö˜ÇΑ@ý¾Åé1Ãé°Kýã•GPÉšèe:+®‘}Æ•îÎn´¤¡¯ö|´Šjþ˜ŒÈΞC×?*ªNPHµ®rôÆi* ««§¯”K8íËýÃz\u§ãÞÿ®UÑå>°çpÐ Ïí"©Î°Ïmz . »3}׌ wáîŽ|99ú½;=Ø3ô«]’Štóù'ólü {3›<‘t_Ñ$öÍ€Šªž\”·€ÛP«næQ7êZú)$[ªbqRÐÉ¡£G=­hànš=V“«è[@…=œjf·ZÂm,ÿò&m+a0™T´÷—UëÁ—…÷©…nò¬[[ØMæ¾²ï]@Ó5y^\E*ø/ª(vò—t|èÙ~{.\@7ÛÅ(І/æêðÇíÈ@õåmDS ¯|ùg vHV"Îx÷ô¶Ïá=dü¢eää£enVëö»súí³ÚSë­ù™a*:¹„ÐÎÆºBÇP×ùõöR°ÿ¹Óa°žÚÿuz›..\Åù´.}—yç9ªHÈkì| tw®Šß^µ—jt´¿?µ=î;eã¸_ö\E]|ÎÇ“·pŸaJ…ùC×Ãß9ßK)+w–çݵåNŸaøÈÞÓ ]ª¢9{-û³ó[¸^ð¿ÞG·‚7ºµÅ:¥uU/—»s¹Šó:ñ'/ogwS(¾2ßMèò}„/sknË2Qî¿»`† 'øÇºÐ&»ÂQ•“h¡¾``ç ëfvFÔ~@•î³¹1Ÿ»¶³C”÷NWzòŒ7ºM¢ÚµøµÂ÷PŒëgðÇ*5Q„Åuƒ@ãº>±ï™#îàFŸÀ&¤t­ýÁÈ3•’(汸~Ê"6^Þ%âù'ï œÙn?íXÒL“ê§Mh+îîÌÍ~;îÉN’Rö@ÞIófËñí4]¸¯DÐ%ã7Ü÷ÓÁ9M–H9,2ŸMs¥šßV6lðSEÛMx'bv×󧯽º FW6Yÿh€õš{x“«•ܹ,˯5]S]h¹ý¡f)ßTÄGÃÝÙy è:?òÝŸŸp€=—F^Õ?0vç,—L³ðw&ÏDQõSŸT4Û™"ÄÆ ºÓüÏxšÊ©íVÛarwîáNþ òF¿3s|?«è퇉s6›±sv¸Þ¯î´#¡Îrö¦€ŽôuÚ|ƒÜ8®VÙÝ'Û)dþÕ×Vøþ ¯ÈF^Žcç#¡ëquÏ“ÖqrºÆÓ3D½'äQ®Sûïò>ŽTXO“Ù ‰Ú¼Ô:³€+†Î·\ô«r4)×HzWµ€ü%8ÙØ;sAÃ×öš‰º{U?z$ÑËŒ Ӕ윤êÒ)>ƒM¦¶> —+!üxR¿ÙyGn€]§¶§k;ÑZþ—1IT¿ÿ‹%Ἲº¦G›Ë£)¢Í”N?óIo#Ûß‘+{©J[_gâÝaoM" 5«Û|ût‹²Ýº}&·Àë:˧+K¿‰§8qÉ?œ¶Õ?æJ/~h踟ïìƒK°ß)AÇ?¥~nut©'¿ÿ9ŸŠ7 ®±PÊÍ7÷÷7ÝàN5ŽÕ4í±1‰=ôÆ :áüÂAš“©ÎˆüO󔊴þu–püîÔ®‹èõkÞ(7‰Þð?¯h%èäÐYüð5ßÜ*†Ì|¬Uû5t3Ë×.çjÞÿÖò¸ÕÒoó/Ob¾Ñ‚N ].ŸN6Åà+ŸOM¯å”vZ°š;|·Ôq„ȃj¹mÙÅ/é÷ï¿ôãÝ5Ñ…Wó_ÇP­˜97G<Ê'»£o%w7pö÷J&7:±„ZÛ\4 š“DÃ,â†jÀÎÛù¢ñNã‡èËíý¢ä»ùdºîYHÓ+Þ\à¶zæéçÑóö›Ž–}T:¬I6;wÑÎ=+ûä¢RÞýF>ýq9¼ë>_Nÿ³ƒóÜjHÛý*Šv=ö|Y ;Ïݖу{ I¶½šûÔoד_6ïr¡÷ îÞÓHX_ ºbèv;=êñÒ-Žjœ˜¨6Û—O]¬\ô«º™«8WÛoàòcd‘DÞ+5¹/è üJıׂ59—ãhq`ß–N!ùô+/EZéÖfnäždé¡nt<¡SëÇ’$zÜSTø|"èÚèò ŠË7«;}S>i&õóÙŸó^Éӂˋ¶ÌóH¢¼3³nDïaóºîúƒ ÚD{xåÓè«ý– ±…3›Ê€;…Wk´±ñŠ$Zqd××£ÇÙï 3sßôlmÝxrJžÒ­Å‚|ª¬C[¹êzƒhwêï6sÅŽ…IÔob–÷‡nÌí>+¬VÆÓ)nÍÔ¹ù–èfòëP —°~H›~n´­gÕÁ&öIìù3ËsÐ uG<-ØßáíÄ|Š »Óló†mœÇšfor^¹²ß$ýÅÏZ Ýæ‡Aƒ¾ôL  FI²QÃóiÕî¼ßÆÕmv2.q¨ éÓ&¾OšZ:<‘ÅOè–êž%P³ÓëgïîšO³-RlãF^B¥æDÃëù~éî”DU fÝç'|Ÿ›JÄq6+6%nwç…Ô<ŸÒÆÞ=²g—Ü?Èî@MvØÿÒ%‰ý.ŒtêA}:vI¤6Š™mîB§ËÛc]È T5®|2Óžø__NG\ÙcïàŽ)Âý"†nHµ/7u+éÅŒú}¼@›£ÛZõº•žÚSf8“HŸ6Ç : tóÆ4˜‘šH'¼ËÝúàuíµ~ÿÇû[¸Šýó>Ç.¢bÂ|`ñM?~Ð}º²ñú÷òDzè5¢Êóü Äí©RuÙ‡.{k‹Éeöä\’Öôôú$*ûÎ/x ã]Åy’#êÝŸº@?ò¯ÉWO ø½î ½(‹¼Â§³{‚N ]Ç+ÑWÞ/VÒwÙe‹˜ ´ìð—sêp¤y'¥is5Êô$ö|BxŸÅÐ}<÷6Å5\I­ŸäÕVï¸@£ÇoÛoÀÝG5Òf‹+MÕX=QJ"¡nt›KÄk?笽v^I=GTÓz]  âó' ÞÂåš;:^áF+ïÚ쌔'±ºe‘0~ÐG›vy‡ußrõÏ‹öèðýæºz›¶r¥“oYÞí°€Ö½»öLp½u°ØwNø|bè:œktl~¬§N·/¿?ñ û?Û¸#¯®7˜±€´¶Ÿ^L]•Dý¯:“»D¸_$Ð}ˆw »ÜuÐþ€ïjÇ隘,ÛÎÍà—ûMÜ)âÇ©+Q]’H=§W˨÷ì÷kÐ ÊžêUs*Ö-|§¶Æ÷2¸^³ÆöA\œõÃe§]©O؆ô›*Ôù•wG}Ã~oµe泎6*Úñ3bÁ¥Ê¨ðÀÛ7ÀË.¬¶3ß^³O„%âuq¶ Þ¯§…n¯Ç‘ 6+Pw=Tì)Í#>KO âºK2ü™ê@­õ{Tt°ëÐò^Â<*†®«¸ñ…H½ü²çñÑKyôÉOv#Ý;WqÞæK¹ßË3ÛU¤_ÆiXüô/O,Ø2üøÅß=Ry]j……L»³kWqAñÀÆzÐ=ݸÉÚ¸‹ Óo‹íS‘KZfÛ±Qy´ã;Uª‚@®â<>}·F}{D”oÝ_w1t_»ó'TÄÿ¦©oÍÉ™ü£™càï8Ñ|)ÿ‹ß$Îõ: tC渟½/Í#©å¦D…K ·að1Ûèú.4ÕaG“$z4qxíQ«Ù¼Ãõ-õ?0UÑçao)¦æQZƒ×÷«gr]ùò~œ+í¾êZ•$šî–yaÄ–÷ Ó?þê“GÇG½Œ›ë²“7Ϭ±ò»+«GTÔ3tòÔWuØxázÿo:Õ¼Žð~óh³±¥öa“í\Ëñʬ8)ù´à̪Øï X½â_qžê<9ö½V‰fûïýþÔ ïÛ_g¿c ÀºøÚ®/ãïãúË>umĵϹÞÏÏÑ…VnëÞ+輊–]6þÑiö{C\ÿV£hÚ ëØæ½¢±ß¯áúÆy=U›3Uä]óΩ½qçiçÃËOçüÚÎu 8U_ìîH&=~e§¢w'./8–¼”ºÿÓcéŸKÿôXú§ÇÒžK|ß·N 3èRIèÝÝtc±JÊîþ{åß›x5ëÁTѳҋy¡üéÇûå*@ñßú0ý½oåŸý½ÿî‡ò¯yÅUxæzõ½˜*zWVôø6þÃ/Ž÷Í-þ£ÓßûWþöcªè_iÍ|QþôŒãýs€ößèÉôº‡eùÞ(Áxèšü}™þìcùg¿ï?ýQÌ™®c$ ´ ˜"I#ÑÿzK¾/“ óÐ :–ÐÌ€P€b`ü·¾L},+ú}kþð‘«ðÑÕ€2`‚„(‘ "9ŠPƒR B²´Á@ǧð P Œ‘H-˜—î¿×GîO”ÿ^¿ï?ýQþô“ûÓO×H#$oóÿ"~r¾ºEÀˆ„ÞL|UÎ÷ünøOmõoÖV|]UQKñ5Ôßë§ÿ“µ_3ñµÒßk¤Šú¨¢6ª¨‹þ©…þ©…þ#j¡Ž¬âë>Nx°ñç¿#þuÌ h@é½'eÌÓäO8ÞW J1‚øWúOþÙ¯ûï¾&ÿš\…G® h@)ýу²¢g·è/8Þ'·#àYü+}(% CC1ðjP DŽÖ ˜õ ¬ð F°,¦˜R ÿ?ÜkÒ”ùßJœ#A!0ü£§dEÿí 5w)!x[ƒ` cÜ x(Æ¢ÿõþ‘åÀ´£àu !’ƒx5("$ ´ô¬è·­ýÃÇ­ÂïV Ê)’‹ÈA0B¢12 eÀ‰G"A!0D/ ¥@ô‡ßí¿×ÇíO_’ÿ^¿í?}IþôqûÓ÷Ö x(ÆÌ÷ö?»ß²x„Ð_›¯C Xâk s‘ÓXèûù‘ðµIE=Â×!5ÈŸõGEíñgÝñ÷šƒ¯7øZƒ¯3zç¯+øZâŸ=šÿíÑTÔ$|=Â×"| Â×Zƒÿ:µGEÝQQsTÔÿUkÿÞ¾ ?ϽØ8ò¯àÿŽ?¼Ó4@„àa ´ ˜þÑÓúOÿ¿{¤ñ~² Œ÷•e½«y/4j‚ åÀ5ÈA0Bp22 eÀÁJ"Ž-¤L™¯ÇŸþg¼_¬D[h™ EÀõ‚9 (&‚ !¢x5("Ô Ö èØ‚Í x(Æ  hA90Å-rÖ«ºÂÛCú‡g,pMÈA0B/ þ[¿ê?ý=DÌ7V4  ˜ hK@$(†àbàÔ ˆDÐ (&ð !‚½x5("k t,TxÆÊA0B}ad@ÊxO4$‹`PÞYèQÍ{žñþ°:¶X5@Š1’ŠZPLQGH#$s PLþð†ý÷zýéáñßêI]áßQáqVá+FÝàÔ ˆ˜ìvo³ /ØŠ¾ÒÆHÊæô/}¨ÿ¬+p{Xàþy¶ôOÝ"3ø¿§nùg?å¿ö~ʯÆácŒ)ÿ½ðZk ´ Œ¿Ìšù{ü釯ûÆ-(&t t,è™)ƒ¢Åãã_óD«ð޵Á@ Ê ‚¦óù¨ðEãýcµ  ˜ ¨Zƒ` cÖ x(Ƹ hA90E–9(†ÆfÌëãOo4ÞCV ä "X‹Pƒb`Œàm€”Ss)ƒ"`„Ànd@Ê€ ½D‚B`ˆ /^@ JIÀKfÀ(@Ñ~øÈ"a˜ ÅÀ ÄÈ€”Šõß hA90At€OJfÀ(@10F’²@ Ê)’–ÈA0úÃV Jˆß£Á@Ç’›)9(†HvfÀÈÿðX«ð¤•ƒ"`„dhd@Ê€ ’£D‚B`ˆD)^@ J‰ÓK¢fxÒþ{=ÖŒpÍ h@0á÷¯@$(†HÆfÀ(þæ±ö§'­1’µZPΟíaž´ÿÙ=Ö*’{ìÚ¨«*rØd3"$„õYÁõ²:­‡*ð¾*»}ÆsÆyÒnï¾tD ·+[ÚzÖ )~Žì8:HEz[€–¬Ï tùöåýÏSLøH£µß·píF¨K8±„¦zy]ôVÑóeþ~ ×ãú÷í-ζ8OÙùŽœàÓ!!Á×NEE’óY_•-%âƒ{B”—TÔå  ýÈ¥¸ú22¯ùsßËóã‹}o-QQÝ «§ÆB?tõô†³*:ñ.äWîý\úáûs|÷¨íÉŽô£jŠãZÙ¤,5בõÅ®·ûçDƒû*z±t¢xrz.U.ûðæ@#îìl¾!¶3Uô ÑÛS^e}q ›^¾°u«—*jû¾’wÁ¡\ºuéõ¸kÎþ¿ûW|ïç,0IiÆúŠA·TßhHEB߸\:cýÓf8g]òÇ:¤TÚç|¿Þþ*SÞl¤cgÖŸ:ƒÙブWO¢’Œ—ηÝr)ù³ÿeß.[8Ýeÿ^Š%n|öÞ·óU4c_LPP"ë+Ýè¥}=EIdÌÛ\LÎ¥Zƒ/Íí²•»ö<1±rŠ”ÔÛ½Žîm®"_¾-RSÖ§:gÅÃìžI”)ùYkw÷\òŠ,Ÿza+×ËÌ—³»àB3Ûo™?.YI¦'§;©¢Y¿Ž­¸ßçûÌlkžD3“ûéP/—úö+Z­}°•»±y«çÏÎNÄ›¥ÿ©¤ºú†³¬oÇÖ ÿø$òïóìÍá2õ{vØøúè­Ü·Ê|ƒX{ºœîÜd`%½5d˜ß[æŸ ÝžpëæÛ×%Q¿©£-\Ô‘¾mã-\…?°Ð¯OIÝô£Y_8èôvsáIÌ·\GóõŸÍCý9¡á|:|†¿”¦ òÌÊbãÝ6£6‹Z%'Ѿ}ÛNe6ÌÚ#k²™dü\ÞÓŽ”A­žÕÙ¦¤=óz\éÍÆ:½½mQëÿ¦c¾2~\ï&-ßy(¡Ž'N×(=§$Á–õƒNè'˜DÓ¬Vrž©#};¹¾ÜåoÙ7öÏq`ý–•ô³¾zö«=¬¯tBŸÒ#ômù0y_5©²qæ1_®ÂÏã[µÓö·*iæE‹{9Âû4,»/jq{ÁÈ#¤· i £þú>ܸÑÙ1–éŽôú%X”´|Ýq3ÇHæ ]A³µ½ÏIß]©íÛ2 š2,9Ò‡«ðy_„! ¨¯¤€Ms¯œ)aýW ë—~Û×(ø•üìp%úJ¥íò|Úº²WfѸ´ÑjPÃðIéÛDú¶ìRð[æ ]Õ¤î.~©Gèù ¾ñO%7εªÜÕ›ÓEX¤=¨fO‰¯:—Ç>Kd¾1¬¯t[ŸTêñîÅ 7´ü% É¡U3;uŠÌÚÈ ¿|&q>mè›í3‘r®é’Žoc} [eÞG|°¦šFér¨å€&‘Uç¯ãüéäÏ¥Õ⛡ÝÏ$Róá©…a†ÌOº„({÷I­Õôz¤!ÞZ.xµ¤ÅËUœàÓfE§>Uû•HúvÓ÷X_#èBvm±©ÖGM{ÝÛq»wÕvvø^ÒRî{‹ >Õ,ém‘ãÄ›©«¾!&¿m%âÌÒ“›’ƨ)z’Ù‡¦õsh–f«û‰7®UÇ1_|šÎ [ãÚ¸y$RSýÏâ'tŸ‡??be­¦)Ò믳©KÐ;ŸãçrOgÌ›õÉv:Y]œð¦¥]"­àÛä7`ñº®»ZÜ2X¨¦/|›¥ülšöÐx±s¯&,^O§–מ‰¯ÏL¤õ5›´šoÄúRA×p˨¼SÕd°”wÊÊ&—!Ý^e˜Î&á{¶¤)“ZÖ›”H•¤,Tõf}ý soéGÇ]j6ϲ©Cýgó>;3?1+:|úÅB%’Þø[ÐÉ¡[Ù/¦mÝ85×»ÚuÇâlJ:Ü/nÜbúÚÐ&C3¿n™Ÿ‚ ÓBg×5Ï(甚ž~ãn6ukð.pž÷RÖ—z>uþq¶q›þ?èd©bŸ‡9jü²³iìÌ‹º¬`ý"íéñëe˜A‰ÔñM«óMX_ÍíÈcO¼ÿqUMºÈÓáÁõ³éºmî㙕ýÉfèô†ä½êݳ䞉$ôeó:¾»pó‡jZžÒä[Þë,ò‘¿z8~œ%ì ä^Õt cê¸Ö?ètÄ·G&b6ÿ úw©©Š¾!_]ú »¶¡Ýš]?Òò ¹F½]ïŸ@ ÃóÆ=ýÎú2Bw&¸^}ûïj2ÛlP\t,‹—·nÓúè:}~ZÂú!óéñØÎÅ7ÌèÔÙC}æŽæ­l{EçdÒÛ/‡g‘~Úµ‘‘áÛ[Ösi¶QÛo•h÷¾[ÍcO°ùàïœLÑ›—ØßõÊ¢â©ë×öëC_¸Ý,YQð ß o߯S µÆž:ÀúTAÊ­¿:Ó«Im²HÙpå^ïÛ~¸Sæî(ŸEeº‹¬vÙä)ùöt‡ùö×FÉôêTjû>#³¨ËîïgÛú3?+:ëñ4ݼwí?1®jI1ëëT"n7t‰ã±Ytžž}ý´…Lëï~×N1—êí¯9¥á¤:ЪZ€Os6n¸~¢dþS2 ~ó«Ä¯fUoÓu¡ÅØ@âÝ%.U±#ÝÚkçýì˜$Ë{Ð Þ­™eT%™>]æ H2éÕ˜ æ‡m£Š|iq¿o\ž^ïY§ì¾?XÞƒÎäò}>7Þ}°ðR&½ïétµÏvŠ5‰Èî}ÆöÙ±õ×Ð ^0:Ël#ë]1Ý]qŸ<ø`þîÔL*µ­®ºtf;µZ¡FÕ!ŽÄG¥ÆÏãéæKóQvlÜ Û;»–÷<Ü—þS/žN–gR•ˆœ=;GQÆúG×Ýv Ò»ÖÆ¡ñÔ·­Qéä[,nB·FZä55éû¨hÍæLšz°±ã´·A´}܈#^ÏíéØÿÛÑöñt°^÷;ïv²~ŒÐ > jªÔôËÇÑ 3©‘aqm³²$þrzÕÕH ém[gÄ“y¼_—_Ù¼ÛQ"nÒ¦ÿé×*5ñ]éfÎÊdýóƒéãµYÞsȩ̈&|»Ó)ñdßw–i·x6~Ðùó¶zˆGO–Ï™¼pD&M¢a±üvR¬cA¿Ol~û—·iê>å}?èx7¬«ÕÔãLÞØu]0~Óït aþoÖÔ»¼—Þ}ãiÐB›Ð›"ÖOº²ƒ|c95yò6•F™dáô¹×›Ó»(,eÊ[b6=jpÇüfûxÊ)+ºèЕÅMèfè¡«©Þ7ƒ«sÁùg(ñÝš[·˜MžIö›³ÅÓòˆÌ9 °º:½][35ÍvØvüaE z32œfõ5°ô,™M¿x»¡ñÄß-j±ºº~‡?¦V->B{ãvdæeÐÆÝƒZA|”nVw½¬nÕ®J<Õ9>qîeAW à§{„Ü,‹Zj2¨Ë×Sü¤‘¬Oþ¼ß¯¸ÈlÞ—ˆŸ'½ôIp„B.vß¿æPÅ8Ìèû¦Ënrìr¦jÜ ­Æêä±á_ãƒ:¡ojÝhº?bëö ¹ÒÄÒûÐnÒOË)T3vÏœI•âé _fïfã]ãg¦ŽÜD‡Ò-z°:ƒì?Yõõh7Uô?¾õàHn‹ ú”ÒkÀ(bã¾ìo’D|@· * ´ª?õënG;Öp&?y̬žÏÔr⫱Ç[³ù«Þ&IEz›‹YÌwd7UôUX5ÇùÎUq7ŒZEx :9t‚OŠÞ~äßHm Y\ÖÍhý”Õ¨n4‰¿µçí Øüƒ®GѸa7ó•4ÑírŸ š¦7êÜóÛÒìÐtï]‹$ô³fó:·ó«X+©üaà'¯v´6¼Ï´ò{X¿\ ‹.»9ÄLA4¨¿s\[¶nØY"F?ÿíÕDšªoà—A.û+íQÙï%¥Šo¸;Ÿ¶wñ1«ßEAµ\&§µpeãÝ‹ísí¿ÎO¤C¿Ï=nAõš÷Ñj¾ ¶r ~è úfS¹lZ<ëg Ý^¾C|ùxz<‹|ŸN“­W¶ÚS7ŠœBv"SÚRÍb—·ö Z®7Dgñºð·å·ÇûYèûèëÈÊ/îfF‘þ¶š)¡KnM¥«$ôqgóºq»ëöÍÕÅSÃô˜í®§Så3Õ6¦ý¿ý }y{Šu Š)õÞ‘¶•t|wϽfñTÖ¯W+ÃÜt;eÚ±½Ë÷çèõÒ5Ù‰N½Ï\vB¦ gQµ4O¿°¼Ý؇¿‚ êª7LJ§ša‹+YI÷Óý»çF9»Ðh«ö?|ÔóÕÕy‹MÙøAgp­­½±‚ KÏí»™”NË_ÊÞ賟*ü<ª\XWƒ<QeøÕölüBJÄ/ò~=™3¡û¹ètæSEw C£6X¸Re—s¾0î·Úš¤¾ZËÖ}ÐÍÉûúÙóXÊâm¯BÓiõ„OD‘qû¸#O¸Yƒ·¾>‹£+¶‡WÎêèf>ë²ïîÜXü½ÓÉ1õb·~‡£˜€3åÕì÷—x-ŽïÞzö0ÝÙè4dùšt ²»rohV½Í–ŽÌ ŽJÌÊÜOöcóºžáí¿L¦Ñ³&>à‘NýIoÑz?u×òö¤osÇ|FÙøA·çå¸J¾‡Hè[™N×÷:´MÚO×.Ì­eþÍŽR_ýRŽ£;=æ÷¨³“tËô ز©y·“çìt:þªþ›P·´hPÚ )lénmþŽ‹£¾»ÍÖíaëvèÎMnÜnÅÐJ¿—¼or:}ú°0y`;9]³©bÓÉz­Éé:$$/ŽŽº8Ÿ6dùoW‰¸{ÌND΃$·˜ÖÖ›Ò©qüµïQ!rÊhphÆKéŸ~#ŽÖ9Du»ßõs‡®êПµ«V:H·Bf5XÜ7†ßí=«íO9Ù¼²ïÝXmKsÇÏj4æn=YÙûs®çÅÐMӜѤ©ÊWÎéôªwÓÞó¢©Ê3‡+ÛIhJƒâ½îÅ1_86ÿ ã»æÏ:)§¶·rjmh™N3“ÂV:Mg?ÏéüÅžö×îpV×üÙÙüƒÎ°}`ÿ·õäôcÒŒÝõÒiéÜØ&aÑtaí>·ôOŽÔ Ñøþ8j;aBýщlü K[öæAHÉ~¤7ÒN§fÞÉç¼s£‰¿ëÎv¦eEë‘jãȸVQ½q/ÙøAgUõ—MnP51à|ÒhYÝ¢5ÝS£©Ó®™C3®ºz–OÖƒ q”æÁ'^6~ÐõIþ¢h¹Þ{òzÐÍþÊÝÜMžív;ïB3ó•‡eÇã¨ò‡o?óÙø…–ˆkë—ï¥Ë%Ÿþ¸™Fî__Ê^l&» q¹ÛRé@Þø(ް©Qc-‹ŸÐ­ñ|˜Ø:k7ù¦Ë'Çä¦Q7é¡7wFÓ×ã[^p¤Ï¡û­•ÆÑ—©¼ãËÐY¶¹k¸5’ìûw¿í&Z°éÖéT4ë§nOƒ;~Û19Ž?D6ÿ ë•wù|Z }ÝÓhs•vs´U²þÑv43d…Ù©‰q$øs±ùÝý¯ê{Š&áô–·¥>FMë:¶Zär6óÛ méèÇ:"ãÈnwêó!–lü ã»w7½JãwöQ¥‘Òvqàö«)á5¿›Gúv¿;ã¨áá³iÍ:³ñƒn—el¤¯|EEkJ¦lH£úã­ž½CQ½?Í_wi>àmvÏÆ‘_ϕߔ}ÙøA·¨RôÇ2çzäqØ‹Eidpjõ‡Â€:ÿ£ÞXïö”¥ò1.½G(¶FîógãV"üìwRËî}.Nœ›Fnávö­Rc¨Âÿ¬´é©†ÞÏãhqL%d?¡{{úYxë‡;Ès0¿C™F3É’¦ÇP…/EûÖfÞÇћʪ5Õ²ø ]÷©)M]¢·“àç’F•v?½\óhÌïû3¸'?Pqä{³ñ‘Õlߺûq …âÄ»B„·Çøo7wó®jÚÄsHîlWâ¿¥–ïâÈäŠtiŸêÌ:ýöÏËPk‚ËÇz‡ÌžŸ–ÅPÐÊ;e§Ÿ¹PZ«á–\‰û‹Oºu»MF=_¹™ª¤›üMKE- ¸%‰¡.ò-êÏœé¨L߸—Zö¾Û±º7ëçïjÔw Ú•6ˆyª¥ïÆ+Eö1ôjŸ‹÷J':³âç«£v‡¶û¦µNdù:yƺՕ>y“Ç*Þ¡AKÛ"7ôâ¼bÈùð„®¥ŸhÿXÞ -ކT}PË„å¿ðññÄàw»Ü7Ò‰sæÒ¼“ZŠ£<¾VÃ|íiõœFµ×#~õô|?èºvy×ñÙòuãõ´tul-ç/b(¬G—è›$TÝ;ltàÂ8ª¥7,dã]Ô¾qùJÊUoÔw‡–´Cú¹¤=Döi?PÙIh–Åôݣ⨑ïpÄÖíÐyè=‰¯®«¬ÐÒì~íöU >D¾Móeë—??÷×~àÐåî,ÐêòÜ©³r{r£ùZj3õÿaï?Àšl·´o;vìØc;Ö;vĆ = ({ìØØ=ÐÐ;w@‘b ˆÅ‚;öï¼s_8îç¿çÿΞý~3{æÓãøÍžçy8 Éu_k­«dÒ!·iùÎÛKYœ ¡—|:Ïæt‚€-~÷3¬Ãx ½X¿+íYUøο:½ž]#%=“ô¬^ŸÍ?èìkëGìì=ƒÄ×g¿è¡¡*Z·2"~wùî`ªÔï^ú´VJúÚ³ùà3Ú²_éx Üý`VáÀ *4>¾_o‰ó‹URG“ ¢­¨õ6Ú’ªõÑÆöV„¿ëpp  ÓóƸëÖÛ8×Õ'ë~¶O£×fnüHþuUnÞe‡ÐY«üØ#ØøA·çÎ<1;¸ŸÕ¶è5’Fk½Ã%%nÔÜ<ëÍè Kr;áß!ñ[ínSnnÌÆ:ñƽ‰‹kîâhÀ¤ uÒÈ*pù’%!”Yâ?oLck²(~kÞ¯¯òo^O ÝÊŽGzlÜÃåžáp7•TÍÓº¥þò7Ïú8O»ÎZI æ\¾ÛÜ™í›A'ô{ßÏÍ4ã#_*Éz&7-‰ $á<ÂŽJo¿\ë°[Éü³ÙøA7àÕ¥¥Þ2.Ø$­¡É®Tê¥Ûø ¤™{Ü­ÞV—‘Sk*”t4?Ì÷ ºSi,S=¹‘ÂÿCj³Ç{¬’ûÇÈ-÷vÈØþˆ’šŸ®'OP1ÿ'è&=õYhÕã wûÝ7‡€!©4£Q¿È3Ýé¡lø·¾24sï»A~J'ÙÞÁ$ˆù?ù”H>­ä®qîüO¥“õ¯4´©Èö÷ì©oé6íÈ£JzvIù¹ÛgA'‚®þ1‹O³;ᄟB]ޤ¾ V‡+ƒm)§*¿c®$Yå±=¬˜ßt?uÇ›G¹#Ï*/´+J!÷ÏSR~ 'ü†” ZÃJ+Ù¾²ðþ¤Ðõ©Î;íúp3t)¤[^4 ¤ ßÉ…xÇ%Õ›èíðu›ÐÙäîT8¾?ÆümSÈsïã†æIgó>Ý’Ú&×>ÔØ\IŸíŽ·¼Öˆå?è¸Úó£=Áž;ýòå‘R>h¿ñÚltKy]wé¯õ_î—KçV¶cãÝ€ŸÎ ™uŠÛ2pðú«»RèrƒS®}ïÒ’¾¼ó‰”úìÔ²~œ’Åm8ðå9›ÐélJ›*8KåÙZñëSèDÍ£¾¶]‚¨³Ëó%§žIéMÊL§‹Ï•”\¶õ]¨5›ÇJ$2ÝŒ÷!i¬4|i J>5z¥u[/X‘¸0ôíð¡¤[ž5aóºsg̱ôãºß˜mÞdGUÓ¸U¯ûÝš’Ç|(ÔÌ¥Ïôܓײyw¬âsôç–6ï\ÓÍ=ˆò¯Þ®e9ÀŽj¥œvÀ9”­oY¼ÄÏ?˜=²ÎÊ—\u~z¯J¡ }ŽÏ_oD ‡ßwXF /ëcÊÖ©Â8Ë¡kj´yþêwÜ¥§QÞEøæ{Ò¨ ÞÏÉü£A'i÷r‡oÝCIÆÛÚ6ž+tEEÂâ«s¯V‹k½Çç^o¿IîÅä@ÚQ‰w*]Lɽ·Õ%·±eÜáÌßðXÅy]'ø×§pHmùtÓĉ¦8ʪ¨+…’° ø™Cg°úC·¾C”\ͳòNs"Sèå¡èVûJhh‹_,ŠéÛÒNCV%ÝÇß@ðñÓ;Ž8Qÿù¾y¡ÜPÃÈiz™)”c÷½Uo=é_;fh Œ>LL«_#”¦èĘßt…»ªž® ã|ýüÜ#¼Þñ¯ª¬ d5±cÞ±ñQkVC¼^ë=Îfyl¾Aw34ñ¶T©âJî~Û±«z*jôl’uÕ¼A-lHwìÚ&”‚&ýôšÌ|× ;œ|åMÞƒpîG×Yþ/z¦’ñ¾ñŽc$ÔÖ´ó~¼t( ç‚N]r½‚C6­OsÖ|ø•JÏ[ñÆŠTqrèúÄÒ^¡Â¸áçï[ÞôiÎæC–J ¼v^  1¼mU„ͯýßã+Ê&˼ظA·õæÒ—c&¨9a•JE;û^  ßÔåçæ­¯%¥kÛ^v±^Ï| ¡[kTû¥š;>Çuví©TµòÉ ÇÃ(‹_4’Qù7£Qn1¡dûÕ#~Áw'O nà‹="¹¾sªTï’FÝ’Hnh—3ûèŒÊVi4¬ÅƒhmךßîMG:›dHöóPŠ¿4áÀmá9‘@—оÏȺ÷¢¸.i7§ŸH£{[(&Ýñ§ŒÏù‘Ÿ:PÜ©v­¢_„’êpMT¨ÌW:§y]«.ßÍ]ÙÖyW§GiÄWíGúïª5²³)÷=[)ÂëùªÏUÉ |LåÐ\°«7¡g çÞ°çša=9jWs¶Ñv+ªqtÿà+EöôÍpÅõ†oC™ª S@—YyQ^ÄõN¢èç0Õ£¡iVQãü©Â¿iÝuEïÔï¡t(¨ðë¾U‚N]ôÕÀÆ+×ÅršÃ/ŠôÏqdr¸Ù´wcüÙ¹‚ ××ɵré채÷W }­“׋:Åqcù0Uõéë‡×¬çûÓÚYö±š6Ö$싄ѪÅ>u²Þ ã wëôuüÎp×·Êá7¯ûi(H>wÜmþ´½Æ¥Óëº[Q‡Fe‹;Ö #Á/”tgž7{±Ã9žkÛ€¿y¡!ïU"¿VÉþÔSÿцV—,éoÇÒ)Œt62vÌﺓɽ§4Hàì_ïë¶KCK'®‰ØöÊŸjÝ|X6¡‹}zâß®dzÝlÅŸì0_Qèÿ¨O²È7~Բ忭ƒ?«0j©èŸ/î(èäÐåð?‘3»zVû졆LFØ>¿Ò?€ö-¬kÚÂцLÊïÞS¤†Q'·Ñ-š3_Xè"ïòF¤‰\‡Æ¼O:ùÜxÒ'·_Uø¼}±žwüüÇ0JÚ¹íõ÷ºÂó©Nw,µ7‰k÷ôZ·/£±Nø¬AL£j¬?43l¤Œ^Ý‘µ±©ŠÚŸ;½z?èbŒÇ·nÌ™÷zÐÍeéÔ·¶ÉìWýÕí3æ?©¡¢¦÷LËZ3_ßS%á¼3™k}0óûøàtZ·þIÊbW\wúv:»˜Ú>‘ÚïfîÐÊu%óõ…îÁàùÙðn•qÒÄÕ×ÓéóüÉ«ûïJ¸t¡M{²Ûl”:Œü&XMHüÊ|}¡C‘b¯Ÿ•­¬Û¯ÿ)ý ²ž¢©5é¨ß¯×›ª[x†‘‹vá†aï™/3t[Ž-l11•û2¶æ—ÚÃ3h{+½¦§†ùÑð.©K׿t Ô•> ÛF}Ÿîôp+óe†®Dy{LÏK©œ|ã=nͲ *è±ÃrU5?ªðw}¤mëóÆ0ú࿊«U&¼ž:~W©Ñè4ÎJw'ƒÎ›m2\ÿAAþ;Ï”áFëxûð‡lþA·»zW·Z‰i\)WãŒÅÍ êõmûÙÕ5ü¨âžJZƒ;ѧóÂhËþÙ±‚®º)+Ä÷‰?ox–ÄÍëì÷+/¼Rﺸ 8Œ°XÂB˜ùjû–H~ðÛ0žWd³o­û¸³tuEm‹]?:5êt~´È†¤Õ­ÇøkÈwát·t"è¾$ DåÊqOôøŠ³tiY£yëÆøÑv·Ž O]¶%¿Æ·k_ ûh to/ ÜEÃ}8Á/ØÏÒÆã1‹zöô£ú…?_ÊHrW5»ó·°¿ñ«–C÷4ö†ù®5îÑȵ»[µ?G¹µ¿´rHR°ý@j>lò×Ç?ÂþÆŸ^n9¯ÐpïïDôÈ›{ŽÚÕîqë«‚ÕUtÅ(6¨ÞÛ0juU©É(øÌk ÓÙy¦k8auŽ&gžOÙo¬`ç˜2ÒÙ©•†Qkcû1K]1tKΊõÛÜÓpÇÖ,Ÿ²ùâ9x«sçĺ 3uÑ•ë=왿ju\b¿h óµW”Ht×?¾ásyÜnƈª™4ïGߡ?ùÒ¾x¾ ³%·ñçßý £KÃ;®Í^OO÷­Ž[¥s¶Ìúðvh&mÙ`š\UAºpyÚôªŒ”×i­¢’}ü : t3,[Ñ-k×f×Ñ™tüF¹E{±‚Ò&…N:ßÇŠÞLèÒpòñ§8 '2_{èº;dj<"Û³Ûø¤*“^Å~U¶RPãÎ{íl¿J©îIE5ùíflþAW0Ï{ûª©éœÎnðq&™gjîå‡+¨â^æÐYiöG©èBýjncóºO¥u?yÎOçúè.¤œ§ý×—·öù®øuþ¹ch³qk§«þæyÑ@g¸z«,B–ÎI2Œ¤9³ÎÓŽ.ÓbªOô£Šû£=¯lMŽ^¥¢u·&ãQg¾öÐÍÉk‹¡KçB{ÛîØwžŽ¶÷xà?Z6çLû€*Vd­K¼*bþó‰Ñ"A§ç‡¼é}Ӭƚt.!·"ôy²)×=Â"4§¬Ig·ùHEs‹»'}8&èDЕ}Ô{êút.Ën|ØëoçimæŒýüèù#,mihð«Ü^†áó\K ãOGoJç +¯›Ø?‹z¹ßò£ ?4íÑ»?ô§¼-ucaܥп]µíÚ–tŽ–v3³tÊ¢uÙ·tvó£—û[~Í‘Qé”±Ýöö '矹š :9t­4WîÅëMÕmÈgÑy§œ0ýA~TÖ÷츹œrôÝŸU'œæmÙ+¯5^Ð) ³ñ<’{+‹’þ?YûBA}[]±—¤:Po͈!®ø<„ñgó?ù‰?`Mçt×o ²©ÝqaU<'뚬mÞ“ÂâTµye¤C›wÐu=´ýÌ×tnÊäfVÆgSïËý-¶+HØo±£¡ÅIVãÜT¤>]+ߪŒÍ;<'—r?¶[šÎ-(X5lS6m×Ozs³‚B§œ¸ýÚÔ†æ¬î7a¬Š„sG6ï Ûäe9Å6sܶ1xa|6 ï3nÓY_‰Ê4z;­hʧ!¡½'©èí«lsçlÜ ÛÃ_SÃó=Ó•Ÿr(¤q·W½ªûý:gåw}W'áó,™úTú]ÐɡӾò°’Iç„u@½£™®Ï:ù‘zoÝ;/êÛÐ]¯VjUŽŠ¼ûºH&ÍaãÝiË76ƒÒ¹²ïüAdI¿©UKìG•ë\ð]ZÛŽöyó… Þ_Þƒ½–SÙøA×·r·V#W„}´Š”„5ë[×*ü_ÏåîU4«év‹ÆÃÙøAg°’wvMçŠý¢’Í!Þm “ñŽ“2ª6`}íççU4cÕ—‘lüJ$v[î/8['³»å°xŒQ.>þ¬ ~õZtBE]uÁfaÜðóV/•9âìø‡É‡’lréz¶M—å«t*ºÚ¶Â³ö¤øÙ&e•ƒŠ,‹?¹ä: t —9‹Ï5ÜlùëKOäR‰ÓÊ sÔ¿ÁÀa©fvįn¼§ªhŒî‡ “B·}Í~SŠ4œîíhs©c«s†þ3‘ÞF{>¶!­ì =ß³-îÄ$A'‡îÖΔGϲ4êä©&{ЉtV0ßi+â]2O8©hÇé1Š/5º©Væƒc4ÜE‹IÙ5†_ ´Û]ÆWòW9¿ëcI3sôL3÷©Hg7~ƒt-ë÷ô@žœq¤Ý~×eháÚ±•^¼T°ýÅEÌ'[ÅögظA÷4þL•'{5œp}Ê«¬É‹šàGºã± É/õ‹[i²Št×[²q ,‘Ä[mº1ù¼Ô±ûÇ£w.Pë}’/e ë%¿6\øëÞéçž¼S¤ð<‹ ó»áºt¬•†ù|^$ÍÙßôëú“$µù€E‘ó£N?r*r¹Ú_|¬ËwÐ-Ý6v쉩®¨~dzÆ„‹äT>cÄ" åËÂ)\8~6ö¦ŠükmÚ1h1‹—ÐÅ\ˆ>HÃ=Q=´^½ù"eÞ;>©é ²Æ³fù+rz¹!¬VÝpÎoY¼„N¨»4\vÌÀ}/c/ÒâÁ;âÌÒü˾í4ÓÅmd8]8é#5¾Îæt%»D—ÏÖÔp×M{û¿¼Hëüä/¥çü©3Ý*Ò–kÄYMéÛÇlü ›îßé@È#ŽCÑ÷¡~ûK¤ðÚ&YíO¼†:µïgOªù|…NHÚºVž—bènÕÚ’ÏqNøR—(¶tƆ“»ýIw§¾Œøêí„}8]ªbþðz}A§T"Q·ùQ¸‹ã <;œÜìy‰ø]’#X'®Û^íS•62 /´užN#æöYÐZÍ?èøjìã|Ž[ûÙêuæ%ê^óXñ£~þ¿òV“AKúРp ®ykÚùr6~ÐeÕlÚ‹ãÛõ¬øýYèäý‰¿ÝºNaKâ®j½`䟺‹ÚlþA'܃å¸5•-‚.“Ëüùn »ùÿ:¼ç|׸ëE-~{¨`åp6ÿ ë˜à5öjÇ»)7q¾L‡¼#£²fùÿ:öT´(&«öS–Oз*È60{[k ^ú2u/J>vÔŸ}¿EJκª¢U_.÷žÈƺÁº)ÛU«àRæ½Ë´Þ«8êÌ}ÿ_õ ’“AއŠâÇðßTæC1tuÏŽ?wÈ${ǧõfWh´lªòM«º­à/ÖZÑʼórŽh6ÿ‚K$ÞÓ{ÄÇ´Hã*5Jp4í n°|pâê|T›ô}64¢ê¬€9UÂÙyË{е/6Ë‘Êù4œð¾ïî+!3,‘a=›9óKMûx;:S“7t§#^:Jcã]Ä~¥Ê5¼¹¼WæŠ]§ì0°^íðx÷ƒ¦Êhå¨RÅ$ãprß|zC¥T6ÿ ó⯣©\T«Íõ´´`ÄËIž×ýéÛC±¤ñE•Ü^ãH‡pêÚH;¦ˆÍ?èZé „TŽ?…=ÒZK¾†?ÜfúùÚQ0OFC[¾½R犞ñ×H²ø ]ˆÄÎ>oh*§t©Úx¨–6^ ›üd?I‡¿šÑÈžøÛ%3©ho÷Ü@Ði Ûû¡îä%S9¿¢ZÇŽ˜ké~ÂÞã-dþ¤ îøÜe¥-u¼:ðÇ[m õÔ$”ÅOèšn{;bÿã®? Wk©ÕÌÙÅËý)áÂÆ;O/[ý÷IU4³$èø9¿IÔ ÏgC¹îÛ®Üké̓èî%‡ý)`A ®l¢™è6zUä112`™’ÅOè¦ôîsäéÑnoòȘÙ1Zz=¬þ«Ëþ$œÓ[’¡nCLE>)•ìÒ¶²º:ÝuU—‹×Fë¯ii^›Øk¢¦ôt‘WnãRÚ^ßùùgõ—Y£YÝÝåÁnûOHánT%9ùVKÇ}k@Âù”ŽWê•}u 0oØz:‡Í®Þo—Â9Û.IkG…çø°6ߤԹeý¸¢—*š4Lá”ÛDÐ) «ªÛøOæR[Ö®µµo9Û^›?¥4€Ý··¢Ð†¶K»>QÑGý£ÃÄ‚NîxçZ2·v€ÄA9=Úiß;·ªH¢‹/¾²&Ý5২wøåÛM¶^€îp\Âyû¨dî²áãªÎËóˆ–Øœ¥ì¶-Ùë èéMF¹ÃíãÂ碧,‘ôÑŒ%sí¿íQô9˜G_š}*¨ý>à×ù•ד/#ZuÄs­w¾Ÿ¬”t‚o2˜õÚùKt%-¶‰j”@OöÌHÉr {üvÓ€pjjª 9 aãÝ5~;pB2WÆohæ‘ïƒ;Ëíÿm?Ýz]ÏÒÔ>á´ø“[6~Ð-ùݶkûdnòþ¡A[?äÑn“*½6Ö “=ýv ¹˜:]˜ ÞÐ.œ>Z¦Nï>™ÕеhVo›M¥dÎwÙ}‰IÓ«4ÔˆÒáþä”<ŽT/5óìºzáÔéònå®l½ÁA—vŠ“¸vÃÇÖk0è*™LþPs,ò´ìP¯ï“ÌH_釷šg3TÐi K9“»=—Kâ_mn¸fîUª>óÕûÁ=ý©â{k×x­¼ôPEEu\FÆõ`ñº9Õû¸y'qûb¹èák¯RqÁrûΆÿ–o;Ö]²¸ö ëz¶Þ -‘TÑm¼%q÷½m§U>~•²íêó¥¥?Řï~Ø}³ ­û¸ ¸súß®ƒEÐ}>Ù¹ßÝÙIœµMN« ÉWiè³ÑË÷!=¨ü^8Õ††”6ÄG£"á¼°ž•@œ$?Ú|`'ìc_¥±ùc|ôõ'Ýõ~3[òÁªèx±ŠJƒ¬¬Žgû-Ð uRW«{“™3¿_¥I»–ÔèßÞŸ÷&èõŸhOý·Y^3œlªw+¹gÏö«¡´wKñ›—‰ÜÞníZ·¹F¾Oï=ã•]»ã:𡯽¸»û\UôÑ–¶^ú¦ì”Š<åy.ÝöåÐ2ºöñLx§»ŽÒ0ŸTž•ÕÁÐ ßϵ!þÛw³£T4znù—yGº¶\üÓ+8QaŸÚôʧݪܴî[|iN{ñ+7kªºÏgL*ž³Eëì&ȱýjè¼wðV œµ¸ËÍY&ùôµÓp{»t_ú°eÚŠ·í­ië×JXº„SfæôÄ]ó]1t "-i’Àå÷àWªùä¼Ð¨‘¢’‚n}æ¿8lMu3[/};?œno  `ç ªIáÊáy®÷ã¹f’ó²Öç“OapdjS÷úÂÆÅ†ÝÇ 'áû ì\º®áfkƒã¹#Ñ«ÇÍ?œOÇÌ” Ɇ rÜu8/û‰-ñßR½³:œ¤9³zlœÁÎù ;zš_ÇsÓªM™žOu’V4köÝ—¶œ[õIïŽ=}¨?ú¬U8»wÎÎù <¾uë9â9a_;Ÿl¶;9>ÛáKý·ì{~ïƒ%5žsxÜpòîw«É);o€NÏ8î}æÝ=½oæSØ–ðÊWOÑ­þq×·Ü]Ìö×ñþøtüž7@Wô6÷CœO÷ì"¿Q˜OùS·š¿xt’Ý[w¦c‡¶,éíNÍÏßÍëÎÆº¶¶çª)¦Çq÷¯HÉ®Z@Û]šJFÝ=AjÑ%óþ'ɺ»lž•m8鮟·bãb)?a㸑 ö®2, ³˜f«Ro§Ùº DΤû:ñ”pú:D]Çc?ç@»žElŽhNGõ¾ÍÃ΋ ›¿Î7lT•X.±dÊÆ=S (¿ó»þÕ®ûÐÝ™íékó×°Ž›—zö¥VÍÆ:o™Ç–®ËÎiC<¥ä‘:m–Ÿ}½8×wùd;zóvåiq§p M{·§á{vÎÝÊW«RûáòÜÚ»¿s) ÉCwƒ¨>piÙ’F¯l­ú䉪Mk'èйÙþìñƒh.ž/+6P|Δ5Í}ÈwÞ×Y ;jöaÂ+õ®pº(ÝkÒx»ÝùÏÇMÝåÑ\n±xëþšÞkß"·N>ì{ð2š}¹<-!œÒRô¾–æ»RÇ?½”þôRÒûÓKéO/¥^Jü™ÞßöLú½?%ß3ÉÁÊ„yÛþî»"ý­7åïý’Ô ˆØÌÿâkû»Ýßó\©è^Ñ—²¢W’ ÿÅÓÖœù­dÿÖ“òŸí“TÄ̯îw¯Éoý(ÿ^$>  ÅÀÚ¸ (FØ2 EÀÁÛÈA(bs)ðZ À.n@ JÞx€ìß¼ê\~ëûmøïôG’(úÍ÷wŸ:1ëùýêCÉ÷üþ«ÇŠTô÷{Qþgû~ÿÿëGù{¤R úÍ·Âc¥¢ç÷ÿ^”ÿHÏïÖcå÷žß¿÷¢ü½7’’¯ (@0@"6rÊ€‰Y ¼è#IK€PƒR BÒ6 ›%pcà” "¡›w åÀ ^  Ù›9He@Œä/Þ@ ôQH˜/n…Ÿï¯Â÷ûæûP¶e1‰¯þÕë¢?5ÑŸšè¿²&úSý¿_é³çá÷¾‘Åü߃€dʼç~÷B‘ýÖ3Ò5 ƒPÄbÒß|xÿê;÷÷|P*zsWô‹,†~¦ÀùóVøÍy³žÜ}"Ý€”‚¤9ðÙ,` ÅÀÔ¸ (¯'øõÊþâ}bò[È2 F°•o ú¼àÔ ˆˆÍÈfAÙ¸%(†Ò¦Àh@90BЖ(à&@@# K7óê­ð™sû­7ßÒx€lø Pþŧ÷w9#Ö‡ûÿÔ’ïÃýWϾgòßëùŸéÅýêiä ”qÁ³×û7Ï“Š^ÜÿloȤ÷?ëwò{îßûBz€l–` P‚b`ˆ„k Ü”#$`P€"`€dlä ”1’³x-ÐG¢–7 ¥@„Äm<@6KâÆÀ(A10DR7î@Ê’¼ (@0øÍË·Â[Ž÷:á;/ò=!+úAòµLû?{Eê"½ÿ\]ôÿ•zèÏÑMMÄÇ 6îügÇ¿¾¸5(å5Ì›×ã/þ%|Om%(†`¦Àh@90ú‹/ï_}âþžwIE?m7 ¥@„@hþOÞ 8ë¥m€ iä ”1‚¦x-ÐG•7 ¥@„€j<@6 ®ÆÌîwϾ¶;Ѐr`„à+ P ˆM€$€2 F`–o úÒàÔ ˆ´ÍÈfܸ%(†è¦Àh@90ÂÀÉ€‚yñVxÃÉë¡-Fð—o úHàÔñáýÝΘõÏV‚b`ˆ¤a Ü”·ùÿõ(qJP ‘XL{»ÿ\ïlÍ’1pJP ‘ L;ЀòÎÿæ¿«øÍ§¤¢ov(b$3)ðZ Ä&n@ J‰Îx€l–ôþѾÙÿ¬OÉï}³Ë€ÉU ¼è#ÑJ€PƒR Bâ5 ›%acà” ")›w åÀIZ  a›9He@Œ.Þ@ ô‘Ì%À ¨A)!¹›Í½1pJP óÞ­ð‚ã=JøžÙŠßê#<>zÎÒþÔGr½?ûFê¤:‰Ÿïnl\ùφÿûM€$€2þg˜§®÷_¼GÜ€”™9ðÙ,¨ÿÅO÷¯oÏwÄÐÈA(bDé_¼t+üÝxÏ‘b`ˆ`i Ü”#OP€"`€@jä ”1«x-Ðg>ºnñ1 ›`cà” " ›w åÀZ  X›9He@Œà-Þ@ ôÈ%À ¨A)!°›Í‚¼1pJæ¡[áëÆ{h@90B(H&@þâŸû»§ï3âÔ ˆ<ÌùÚd³DbÔöïûŒ¸5("$sà4 !ñÈ€$!    ˆ‘”¤Àh>”¸5("$,sà²YòªðÏUþæ3b Ü”#$7P€"`€Dgä ”1¿w¼è#  ÅÀIѸ (FH’2 ø¿à3b Ü”#$[P€"`€Äkä ”1±x-ÐGR–7 ¥@„$m<@6KØÆÀ(A10D7î@ʺ (@0@r7rÊ€É^ ¼è#ñK€PƒR úÍ;·ÂÓ÷á;ª+«—ø¦â¬ÿO½ô§^úS/ýß«—þÔJÿ¹Z‰Ÿ«r6fü{çõ¦Àh@90bž¸¼O[0@Ð2rÊ€AL ¼è# Iþâ‡ûW6>Ø ÅÀÁϸ (F†²¿xáVø³©A)!PšÍ‚¦1pJP DM;Ѐr`„ * P ˜.ïÍ–Ê€W ¼è#øJ€PƒR B06 ›fcà” "P›w åÀ[  ˆ›9He@Œ .Þ@ ôà%À ¨™n…/›ÈfÁ߸%(†H¦Àhþâû»'› ƒPÄHRà ´@IÄÈ€t@@#ÉH7Èf Ǹ%(†H@¦Àh@90BB’(HN&@@#YI7Ðýßž¸5("@sà²Y04.ñ²5rÊ€ÁR ¼è#pJ€PƒR B 5 ›Ucà” 2[w åÀAW  ›9He@Œ€,Þ@ ôœ%À ¨A)!X›Í·1pJP ÈM;Ѐr`„À. P äM€$0[1‚¾x-ÐG7 ¥@„„`<@ö_ük• "Y˜w åÀÉC  ‘H€ P‚b`(‚¸ (FH42 Z ¤#n@ JIÈx€l–Œ P‚b`ˆe Ü”#$,P€"`ð›m(b$3)ðZ Ä&n@ J‰Îx€l–ôŒ P‚b`ˆ$hä ”1’¢x-ÐG‚”7 ÅÀ Ó¸ (FH 2 EÀÉÔÈA(b$W)ðZ D+n@ J‰×x€l–„ P‚b`ˆ¤l Ü”#$iP€"`€„mä ”1¸x-ÐG2—7 ¥@„än<@6KôÆÀ(A10Dâ7î@ÊÑoÞµEÀE à;!$üV/ñyþϾҟzI®÷ÿ~½ô§VúS+ýGj%söRàÔ ˆ´ÌÈf̸%(†h¦Àh@90B€“(v&@@#øI7Ð}B pjP M;Ѐr`„@) P 4M€$€2 F•o ú¨àÔ ˆ`ÍÈfÁÖ¸%(†¾¦Àh@90B0–(Ì&@@#PK7Ð}m pjP DâæÀd³€n \€CxSà4 !àË€    ˆ‘ ¤Àh>ƒ¸5("$ sà²YÒ0.@ Š!’ˆ pjP D"è€È|‚1.  Ù˜9He@Œä#Þ@ ô‘ˆ$À ¨A)!1™Í’”1pJP ‘´L;Ѐr`„$& P ÐL€$€2 F‚“o úHvàÔ ˆüL;Ѐrþþ’¡ (@0@b4r ¥@„Di<@6KšÆÀ(A10D5î@Ê’ª (@0@‚5rÊ€ W ¼è#ùJ€PƒR B26 ›%fcà” "Q›w åÀ‰[  ‰›9He@Œ¤.Þ@ ô‘à%À ¨A)!á›Í’¿1pJP Q ˜w ù­N’ê {NÆì.×[û—ÿþïýɺ7臗HBÓû×iÛ šú‰b}" ¨âŸsg¶²j¦ð¦/|ƒæ¤çòâÆ GúÆÛ [™R³"ëöA7Âiè½iûŽ.ÙB/Å—§Ïreÿ]*¼RD‰ä„©»|´2ŠË;=*wZX]*TÞ¡=J:{‚"'|~ÉwÙ˜Ù_è‡ ‚n£a»a7£¸wÆ.Ž/ zëÏ•~„6®|–k¿f =.}³îøp:Ú²Ž«ù}A'î˜[ ·«‘ܨèçwwŸ+ †!¯:•î>Lºö¨†KiYåkwªGP3Óo†+˜tNk­½59’ó=Ð÷Aemõ{¾îl¿9‡è‚ìá»Ý!K˜cëÌúD@7b-ï«æ¾/æÿ‚::éýè¸áÉÃ6ý\Û±Î4öÄÔ¨‚GáäØä»ŸOÖ'º ^ëM›§æÜ6¶øô¸€Î^šªÚ7Û‹&¾ySóÆšÅôb²ß»Éá”ᾡ²xëÝÛÚg:®9}†k7ÿÈ™ e¨•YV;íI±îâÎKåÄwŸäN§.7 Ú&eþЙ½Ù4~¦ò4—´o8Y@•ô®_éI{‚×Ì·§%|½uád¥k0Áü‡N—H¢ ùF†Ü õù+«Rmºó(«ª'}ïkØ¡áv;Úïù¢ãŽpaÜðó!kûþ,=ε8{B1¥^!In¨²K=hr“ôÙ¶4îE#IŸ¨p¨ï5zÎ|£ {”·O­tWqÑ/c´7,$n›m–ß ò³~¶"Ö–$î6Ù‘©׌ƒÅu˜ot»>µ”¹†qîv§Ì’Ëéê—fdxÐð8[Í܉vÔØ¢ÙF—$ô!f¾QÐYËÇ4[…rí<ãÞtéVHW&ñ ¢=è<ªë7{º;7ùå¨Ì/šùuC'ôoWrßZL´è[H‰«eÑc{z°~º$¶å;lFÐÀˆÆ•Ï1ßYè„>!!ÜgÇñËö)¤‰¢Ü”/­Péä›3ïtXLkøÇÖ6‚ÎïæÓ1ßY覫>ö^0w¹ÄÈ›UHƒ†F œç°õcw"eKqÙT¼?]{POæ;{¦Dò³Ý÷®Å\ƒ¡[¦O(¤v&‡Â׌ÙCeßü«~ß¾„V>0\,Ž`þ’Ìwº¯ œ3Dp÷kóÂB²° 9×âš;=úA›IKé¤lN»G]"èJã!g"'±ñƒÎ°óŒj^þ\ܶõÚè¹…´³Êú†ýfï ·ãƒ¦Öz·„ª¿Ï8Y»C­Zrsï5æ;à'àÇeM´k¤´®_ ¯³2{X:ïuþ5Ï›ç´ñ³3a¾CÐÌã¥\NøÝû«d…´§Ï¬—[(³ oÀ¸˜Fu]7#œüGÖ:5ê4ó}†n¸®ö)îæ ŸcU—Òņåmv½•“ÐOÛt6‡Â©uÉî=Ë™ïtýuøqîù™3¼\ i±—ÃÆqýäT—oG¶Èž>ð¶gGÂéeü*ÿŽ™_7tÝ›uû9o¡÷oŸ´¾ž=íP¶ÓwšvÀ¹¹‹Õ»ÕÔv`v85Ù;ñYÕþ¸ë©K$kõw­r”¹ócä–BR.]Ucµp·í·3ÐŽxWçÓu#ƒµî­Zð½A÷úݪûq“ÜF¡]…4zÁ¡þ7«mdýmìiñ©xr#˜ßŽ “@÷áF¥ëMúyqóé»ý¥…Ô¬¡QRÊàõtüçœÖƒ¯:Ðg?#h¸´MßG£:Ë>Ïæ­;ÀãmEŽÒs¿öïœíÖ°~m‹YÿóÒÙ+trèj6iµÐÀs7÷¸ o0RHÎEzq—N¹’|³H²c¿3íÈ~úüó±:çýþ“™› S@'øHmç>åònx^¼Æ‚Ãjú¿ª"è4Ð-Ooþí»ÕªëlX :¯×ÕξO«ð';ž}±Z¯ôÍyšGÑ«%ÂøAç9®ÚÔ¸Í{èšÝ‰ÊŒB*éÛ(²§ò}Ú˯| ³¥M}æGuˈ ïÀ¾™­ª: ãU"9<¡®³Þ@Z¹>ÆØÚ»§koúÞŽžµßUö!‚…xURì^,Œ~þþëfÑT^¿~¿åÙ–{ŒË‡sBý$#Á'ô4ÞÔóÌ‚¥‚N‰Îë0­yþí–-WHýªÎ—780õ/u¤›ûC“£jŸ¦y¼íá 'aÜ ;tëãðggŽRÌàËÎøüw¯¤Íç»[Æ{û.&]{ü[¿ˆïà+¼/9tN½°GÇHÖqT‡"<'Ã[*G½Ýf鳯LŒ^ãDÚkò‘WC"Èjûü^^Â稀nw‰Fõèò ªêïv8¶RZŒ]gy}1w¹ïìãD¼›ãÌÝ<šw*cãÝõ]ÞÔãKßî?ájœ)¤Á9ŠŽK-ã¢ò×ryûS5ÞÍ%‚:2Jdyº~ÁÒ}ÑOô¼EÏk•x.LðYÓl%^RîwÕݺ7â;±FçX¾âcó.ºD2÷³­Ñ¸ŽþtsÆò©/zY-vå+{*]|ä¸'átÝ7^O]ÝVÚ«MœhiÃç-½ i˜OÎfÙ'WN<¾’¿ô¼ EëgÎÖÆTôó\*Œtý–¸*ƒ©ðñÏYã÷’öôƒ;æ®Üò¦cÎ.²&öùpz«6]|l5?èÖššîžý*ˆ†¥<Ñ,ßVHñ ¯mÝb¶’›•® œ*±¦8?«\ÍZ¹Ðú¨ò®£0~Ðùñ6ìâjÙkù´9« iMŸ»Šî+¸Ë÷y£Ò3þø"qr-NìרÎA§€®¯ñÄ…'­•$ô-¤‘QLVpÒ‡¶)¶t+`ÐíñK"Hg§æ!<ŸþóÜ3­’Ñ©PÒµ‰´@=çØ¨Ë§¡+¹Š¾y!¬¬Þ-‹ '·µA=F ﯺMwgf…QÛ$EšíÔBªš÷êyËw®ïR½j±óŒ Þé¡õ;¾tz1%’¼½J£p Pïñ¯1¢¾\õÚ0Îó½ßÑm´#}¨ß¾ùr§2 ¨Üå–Fx®EÐ :?Õ­æÔPw÷ãJ= i/o }}-÷l®AîWcÇ_ñ/òßZx®%ÐYèPž¦=ü´iYHŠ¡­öÚÀe_ÝÙK¹Ô¾ý¸´§Ñ¢æsÀâ&t–wK&7Š;Cæçw,ùX£äçl/(K7r-LJg†`e6öúö¶'¦EPÞ0ùØßYÜ„Îa9ïÔ§¦s¯šþ¾€ßÕ«”wggª—™í5ØŽÎZ: Ùõ½wNþÌâ&tBùH²0‹½Sõ^½ïýJÕ¬t×°O'¯R[©3.Œ î¿¿žO t‚/w$ñ])ç_( «í›eˆ6q«Ÿ4RjEt#˜ÿ ?èî™ñF‚Q¬&tÝÆäM ßÀͬlÕ°×CKÒÙ,GPVwƒá] Ï™^l‰¤zýki‰\=XÑ¥hÖ““2Wiõd·dOß–6^–tjtó0«nÔÞ¸WIpuA'‚έ[ñ©'Ó¢Iœì“w`G-ùl2q­áZÎnrz ûk–tw•_âTÔ’&=wt螎}¹±Iq4…šÞYð͹€6]ÚZm¤§×_qwï±3VTCgôAm2?+MtRèô†r>ïC|7|ƒ9ô¡Ë=¿c«¹Õ&}$þ5mhL`°Åä~¨s7ïlÆætÒ»Ù½‹!ÝòwXEk/;4•­æ*|·u6¨Ó#hí ¦«Û²ù]zÉ­äêbIˆOt½ÕºéM›»±þëöd~¿ý§•̃Í?è„Ï)ŽšÞY;¥~­ò1]=óyôníÝ}Õ¶t ’Ì ƒÁ"hîÎÕÊÕÂ|(†®‹]ªaî®8ÒÓaä“nÙÜf=>l³zF¸#í^ÕÄl£YįúG7~q%ûÇ.O­O©{­ƒ òiŸÁšÎ*oâzœül=o1mjT«‹~ëó/èDÐÅ%ön¹+žù ç“Å ÿ„56sýù¶˜ÍÓ›eÓ™áTšÚ§ª^›Ð}ÜTïÝ£j ´Âßbû!ß|JœbqjÅ‚-\þèþI[‹Ès¼KÛ=áôý6o„ÈætÏ?XºnKësO¡/]¶§ÛÊUôc]»+r27&œ†'Kìì…8/‡®9ßæ¹<<_=iüÐ>Ÿ®ˆÍ²óßÊYòO’- ]Q¿æ½¯*ö¹±ùÝøG–$Òn¾½ÿä|Ú“š4rÕV®¢ÏwE?òŠýÝøA×}»¤×²‡‰¤²<°ŸQ>™vµÕÓÛÊ…½otyIOkÂ"jæÒºáìu…q/†nBjÈò›3’(ÔÉ¡"ŸLœ<ܸs ÷¦þüµý ¬©ÙØúëȩ̈Vq6³¶ :½ø‰®‘iùQySù5êìX/¶á¢-œà‹iKí\‡~ÍÝNó«Ìïd.Ìwt+ì’Gµí‘LÇ´#o_£;©^ÇznỞk×ÒÄž†Þïdüa3âò~£HxÎ$УS}Ž'“æY¶çeÍ5ò¾spçöy[¹j»ÍÏ©ÏÊhѱöš§á¤K¯„ÏE ݨ§µÛ=ùžLïåWM®QýT“(óm\¥f6Þº"?‹žïNµÇ&ùÎð♺ióÂ3ìR˜_ö5Ê/vêZ¸h;ç»|rÓOmÈ48ï¡Uо÷lü ›¤kÀŸBíZó¼¯Ñuwi«CvpGWÚz£ªŒ>¹h-ÆwÁkñÕ/нØY$½ß1•Leø·Ÿz^Oiý¡]â®Âß3õ@À਷*ê³îƪ›-Xý]i¾m*ñÝÈMú^£Žþï\WÆïàš»ðõmÈfEÁ°ñUä±cÆãÇ-Ùº/¡Dr­q¥©¯¤’«®1û5ªú½›mšx§¿²³sØ+Kò˜jñ¹Ç½(ïmP5›­û ³Î[¹ª¯q¯RuÅûò«dxϲcfƒí\„SÛ·GT‹h”Î\EûOèÑu5«_ ñ²ìºÍ¾4V/^¥Nã§–oåjX.ù´Üw}lTEã6~7hÙƒÕ/ÐÖt|ôAeÇò…ÊUê·¥o_çÀÍœVKé™»ç‘n›Ä[õ˧K7~ÐÝ·èõ`h޾ ˜ÐÆÿ*íÙÑgÿÀ~›¸ª%ÅWû<™Ë⿊&Ühï0è‹L?è,g<™þfGíM¿o5Ùq•\MD'6YÇm½=ÖÛÁ—ïmUu|8ÙQ¦¢‡}—8*>³ü^Ü»¾¾†ö˜ò ƒ¯’¯é{Ûz¯Vp>Ökæ —.¤aî'»®\¨¢Ï¨F}6°ø™ˆù ûœ4´m‹YÌòÞW©Eùôº_‡/ã¶<ÈòN>"%ó°Ún>–*úÐ.¡’¯0"èÞ÷ikP:YCŸeuþÜà*ÙŽÚâ=dãn¢Eßýž/,Ù~„Š2/\Kô?ÄÖíЙ4ÉT…;kèn«Óm>æ‘mʧÍrsR†ëWúlEíVj;µZ‹¸dôªzÞ<á9“Bw¢œßˆÒP£“¶MýŠò*| ¸œ3m­¶n·¦: ™|Tëõr?¡;kÒÛñ“JCƒãkb žGùÊUÃZ5’qB]iM3†‡ö¨¥¢†šK23Øüƒ®ºm‹)½ÎkH¨Gòh]½¼}³^ÛrV7.¶ ÝnE§ßV͘ÆüÞÙüƒî*?›K4dçòxùÖ< 8ðY.±·â*|[êSíæmÌg ºbèšðÓ軆Ž=]Øåƒmíš}pò»~ó8úqÕDŽuX\v-Ü\:Á'_Ðé%!^ïž3÷Vãtú6=ßgIÈoÐrç¬ñ¿ž³E±}ïýJ¾U>)öaóº%Á£†5ìN}®î¹ù½[Za¸åjõT¥ã1Iƒ=æªÉèR#ìoçt‚oJ:él¡êçÑZû¹' ΡòeüNÀ\Z=5Z’<:Œ2ÎÆ,}u†ÅOèæÇè9«æ¦ÓÂm]sƒÞhéµ~eÎo’5¥¿)Øÿ`¶çwØÃèN틵Æ&±ø ]éð/ œÓIóâÔ°ùZêÆEÖiúNF£ýVäIiiýQ’´ma´úú Mvlü kÙûNÇê›Óé½Óì §ã´t÷ÚÕ-Ú:‘Ð7ÚšÔ™K|gÙ…ýÍþº)ÛêN;s(ltZ¶¯êL‚“ óaÆ:¡èÍ"‘>?èÌÆ-Ðk”Nçff4Z«¥ šÍHp¦ziF>Ý mÉPÚödÒÛPÚ|¦´pñZ6~É%’o¼[l: \~çÐÛyZª;ú˜ý­¶Kh[â –ÛP• í§y¥/'xR¶þƒ® ª†£éÌGFK¾ÛRRL{8áêkª±°çj»-¡ô”·#;Âö] ËŽ_]÷ËÕtÒÙ%µÑÒx§V³-z,£/¶WŽÛjIëvë…¬%]›êflþA—U»úõÛéÌ_ì Ýv•/KZµ‚ÖÙÇÌš`³ˆÞ¿ ¿³*”Füþ®¿”­ »#Ù¼úíÓtÒ=fš+¿üÑ…|¾€òVÇG±¥>:ãyaÜÐ ~¯é-~á_ºù µ°w3Ÿ²~-É r<¬ŸÏ¥Ùo#“%BÉ_?ôÖ9¬~NðëI';ñÛšsR/#Eìv98ÝÖœ.ó6’=B©Û%ÛÆŽeñ:EÕ w¥çÊN•¯(%¯ýÀèÍ”½ægÛ.“ÍÉHÑìÕÃPÒ?cqÓÅœ­RJ${ßóžtJÿÁ°šËtªéòù žo%¡.˜K­:Üè_'”„ùÈÖ)~²ôqä‘j6]&þÝÍ{¶B{O:àèü_¾ÑÛÞ7<æ¶ð{J Ó=†3ˆwMô~™.öZÑä±÷N×R­Í dW ¥¹Ó"Ò’º±ú:Á"ƒÖè ŠKôòâTÓüwŠyÒw@Í»RZY'{áëJ5~+±añº¼yyã/Ñí2©QÇ‹»Øù™µÝLß¾}RR朡»Wä ?¯ÀÏëŽ'ZfŸ‘øšlù%²w­ÿÖ»ù ë%5h>û͸Çl¿º;œ±âÊ ™:£ÃK´Dß÷“Á©ÝT±^<}ÍdÒ£—Ja¼ðó6 Î<ÇûÑýÏ‹‹4†‹ÛøåÊî_óFð¹Réͳz%µD‚â½h|ÈEúÒÂx¶aƒ=”Ûäî£öiÖ¤+Wý•ÌÿˆÅIü¼n›Ÿ÷añºIƒ]¤& ׎¿~dé¶fYQÏ‹£††ÌUþ;¡ºN9+6´­—Á|¹/ÒÜ»³°BÞK!«v„öt•’§4Ì'¶±’6´›?Fxî¥ÐéÒž§ùâŽ>HÞÒ°÷Åò}´BgXgA‘î–;û< ¡½“fÝ|ЂÅIèxwЉx~×kß˾ tãcÄÉKØzw]x?éÜü'!—І´:˜‰ù¢³å{,~Œþòò¹ó9ŸGª‹‡ ï5TRxDÑ¢UÓØ<ƒnø†ª?Ê0?_-|u0@ïEM›¿qèCO¶ï·€F]ì4ëÖ%e}xýåâ\á÷,†.Ðì…¬Õ³t÷$`oB.µ·h©^ˆ9]|î>z ”Fuäz•tó´¶Æ…"'ÓJ$µÅùׯ§¿Ëìê’K•çU5pow6õjÑ·M=^ÕûSÎt%Ý+æ‚Xœ„îÒ¥Aþë Ó©íŽÙ]ìŹ4}\¥ñ!3²ygC+ÌÝfÕ]IÛ×ÙÞ {ÎÆº~më†\D)Ÿ6È´8‡\¸¾Læ¤öK¦vª"±¥!mOuÊÖW2ß6~Ð5lv|—NŽÆöΡøÙ™¶­’®|N¶!Û¬q¢…×CÈ>þHŽË06~ÐMš£Þç•NºikšC-GÅ­q?Hûk ÌÐÛBwÛeêíêÌòtÝr†´²@Þ9Ú–wÍ¡~[¼ô ¾!–d}f˜I©[ÆõÌuby:ùuûË_çîzFÞüÆeÓÖ¨![µ{‘¾(v×Á¯t+ãYdŸ6±Ó0¿ëlü +|tHî½-:UU={´:›¾,,{ºò0ÙðÇ8Uç‘ÌËÜìÕˆjâiÙd© «3¹ÉÎsÃŒX™Nº·ß;›bæñ‘àùwþ²þPƒ9ÔæÞÝ¥“B¨gÌŠcÕn²}2èî\u4¹mNõ›/Ñ<É¢In'eQ7²q˜E݆x]Ø„÷7±fzØÚÏl]?T=kf¤ÓlÝÆkHï}lH:v%ñÖ‡«3©ß½ôi­$!t²ÐÙôk„ “B§·×ž_ÐÉ“uö{ÍÉ¢;ÕcïÝÜuŒùóÌ¢óüöÅðú¸þÒò¥B\–C×µFtM£éDÅ›+m«ŸE½®Ü]Ù9û8ñîO{RçжU PÝÜ7âq•ÊÂë) Ó½&é´Ól]ç¬ó4%s}ï•·NÐg#~6O·Z]:&„žñÇp?ÙüƒnØç÷EG*áý‰¯~?±ådÒ¥‚±šëçOýšMøãÕ™!ôºõ€Îç°ó!èì|³ÄWR4äÝ”7’È$×»ï-køþŠ»ïTƒ%FãC( Oá³lV§@×A—05${1i`§Lzr«Ë‹ñ½}éðɦͺÚб˜Ð¤ñCèêÒE5.7gu tºm2w yyµÁŒÏ¤ÞÕæ¹|0ó¥~ŸíŽ·¼fM%{-§j;‡0?WV§@WÞbKIß%²’¹.ßñàõ³nYÙó€ï/ÿËÜ€––Ó„üÍy:½»©†’üºý®8G_VÅÏÙóÞ—Nד'¨æXÒýιF¯‚iØ—E§ÇÖfó:KÞî´Ÿ†v¯–wé¹à-Umg³1 òÜÀßÄXD‚g0élÉÙüƒŽwµÏh¢¡áü¶X‹s´®ð¦â¸¿‚gXPB›íϧ'Ó¸##STlþ¥—H&Åt/ãèp’ J’³´ë¬{?׺~”zÒÒq’M}¤òÕ`6ÿÙüƒÎ¯Ó‚Ÿ§9zùÎð‘Ïѳäèe|óe~Ìx}Wô2סeUFÐ^¶OŸ•w8sÄ»n/š{–®EZ“ìGd6fǬKšìò¬JÎÐö~Yü„®F§‹1ß»sTóG¼Wë–g©×«•ïøýŠ×¯[¼ŠÇ<Òm/Ndu t:¤’4zz,gЭÛôdpû{Aù~¿|ôòž=¬“Ù:„IzßN4b÷# ;R“Ë¿r•A>íûW ö£-­VÛ¢ç@ÖÇgö/üL‚¯0«W ;ÈÛØÎH£jA²G ¥´}“ÓË"k¿_çžünŽÇ»`*™ÁG¶N€.'ëUåNÕÒhIÐu;ëN4ÌöfÀíö~d›ù¡Î}©Ãûw­vÏKOr9Ü…å¿ Ä¥=çÉãRézиR{äÏ' 6vêw ÊïnÛü¹=}Û=`ÁÃSÁôæT• ú²ú:á|2•$Á±NgÒé¹×ű׋Åoã9ÙÒÈñªyW§ÓÓü‰ËÐ5 xÒ4©a*íåËöÕédºcÚ½ë/T[gnMÑcì}ÆŠƒ©ÊS«kÛ±::á¼6…>Üt ?7,ŽÛVs•7ý7?ë-߯':ô ¦i‡¶¹yžå?è,¿ž1K–BG ®eP-¢Äõ6ÝíSYRw§ê ¢ùЭ>åv¹)›Ð >œ)ÔwÅæ Ö—44²Nóþ>Gð¼hGÌ.»%¥Ä%×Dl ¦-»ÿ|œÍ?è†[óø‘:™Âyxo ùÌì|ÓªŒz~ÿÂKªôiئ›Ê`Zþ£sãöÅlü 3³æ+›d:yjµõHK ië%œönàOÒõNÕœbEë×Zu÷|0íM*¼ÆÆïl‰dv¢éçëo“¨™ßÉk=gbú½‰èOî=®žª±&·Ñ-šß¦ê[ôšYÅÆºŸ¥Çû×ÝD׿î[uó×>}­W7‰?Í\‘˜nºÓ–ž Ù3ón0õnÒòÝlü «Ba–\—$ÊÝ}ÿ™ž;GŠ©kVvèáOUñtxØÛSZêbíQè„ý?¡ûté{AIZ"].æˆ9’öé_zø›Iú¿{x¬²U±øZyíý`z×ÊóµÙ3aÉ¡Ã"Ósí´DÒÙNžM£#Š »Åú‘p~ëHãÛñ7_0ŒËû ÷TÐ Ÿoéì½æ¥‘Lg¬‹y‹h©s$Ǿ%ÎA׃©ÝìMÛ,5Âëi Ó]pI M)s"&—§R¯¤êY«ôý˜ß—#)åè`òãmI²ûÐ Úo¾gZ¥:tCtpï‰Tš·ãàcNñë>”<Öñú¶¼?®m·„±uùI„lʼn[Ûâ Écç´!©4³Ðu‚NL¯7h=­›`³?,˜ùµ²úºMÓ}Ö‹'³Î¼#[ »oZÓÇUAÞ[í®§Ú’.ì“Ù‰€ýûU,ÿA' Xùt‘WÙþá3r} ­L~3g­‚jt´;|€ ­9xÏÿ`P0Í«÷ÞôÖ6~ÐU⯛4‰£õÒ¬M›E)wrþ>OO51¿FuÖÔ²sèÖ”`ë |?¡î“ÄÒÀ9—VŒIK¦Cy ]ÂT±ÿÄ_±!N­é>!Í?èÄÝZÎlK“t…t2KléÛ1AAü-…)ãlIdµuûÁþ!34ÐedŸŽÙCRÏEt®z2ujÕöjã` ~ñöÔÝyÙ¾+6!Ôµ&ïÍêè„ó®r=-F šD®=‚Z¿\¢øåoÖ(¾–•bq›¬~ÉÄ:€_ÆÈ£iz¿ÀÝf%‘pn«øåo¦_uÈÁ¨{nÔˆY J™=*„ÆÜ©Ì)è$Ð}í¸ºhŒ]­u8ÛêT"%­÷¨¥ovê—¿`’½EÀ#ÔŸ1Û7ž?RÐI¡3]ÖÊ-öv$íþ©rô¨DjUË7bëÖ“áO˜Ž47xQû»CPÏ#êµÒ²ùny99’×çœÈÇavƒœç'h„î"©ŒÆåUi»µI]ëìóÊ•å?èœy[ñ5É{ŒÙ©ñL »çkïtêįû’º *ÁTëæÃ² ]Øüƒîù~¤¦ ƒìNû‡%ÐØÄþï>A½w,÷·ØnC\Ý> ßa>DÕ½½yÈ#6~Ð-LšP§r÷3´ùðè=NOâÉmëY»[çNPf‰ÿ¼1­i°tÎÁð`ºÒ{¢vQ)›çK$ë¶öž\ë4‰ª³,|O<Öîl½¾ÙI»mSƒþvVtá¿Ì|ˆYý Ýê4ß½ï„SÊúkÆÓpÛÓV¢NΞz£“5o™t£­8„t×k¯ : tw‡ü\u㌊Ý×£Í]j:Ÿ?I[¿lÑÔšJüI½D1mÒ&ÛÐÚ¥u*i"C¨û×Óú?âÙøA÷iĬíÕ­Bé]ØeÇÍÃã¨YÄËAž¤&³»)•_l©ÑÛ‘Wì BØþ ¿ó¿îwRóf5§•ÆRßZwäÝrNRñ‰A©éöd¿!·ÏÜÛ!tPeþ-XÍÆº“ yçÓªv˽K²O,yçã»'éQ—‚­8Pš]¢ÓÊË!t»ÇÂu<…÷W ÷¹åçwA4Lwq7–â‡L\š“|‚îŒäo;R¹™›öER}ZÉßf÷r³J$±MùÄHºëßb¨è¶òÜ÷Ç©óÙ7ë«ç/&·D÷×çÂC()5°Ï¼ñ½ItWïMº?2:€ù¦ÇS@ÙˆÖÇÝ×3¿ä@``]oÈ@‚NÝüg¢35úSðëU³lc¨ºë­++f#ÁŸ{1=­ge_ëd%îÎ\Z«;»ß ݱ‘|äô£—–½«[ÇаM›^ߊõ¡´žüIˆ%wÒ=¡„¤´ñ®ðþäÐéÂÎ"Û·‰¦á–»jšâCótÉö¤;ÖØB]óŽÖì~'tü)†÷)êû¤ùW¯}Ñä°½_Mqjóun±om)œ_¡Z|™º[Ði û^Ô‘Ì9ÁÎw£ie{»ãšæ>Ô-‚ë¤÷Ó†æmáçB“\+õîç$芡K 8¿Í{Ò1zÇ/ DSrbù­S"_¹î@×6¶tÿJ™]Ý»!4H7!Ùøec]e¶áI³NÞ4iÞ›ºÛ¯DÑìmÇžlìCºk„fv“sÿàÔŽJ*îžôáXA'‚ÎckC»›?“ã×9³Š¢ºŽ6ß×¼)¯MïÀ³£etèóø{JªþnNö‚æì~.tÓ§êV>T’±Ù£‡4ŠF¾±¾h1ɛ֟Ì?tÒ‘tiõ¸’Î/åfÙýjè.s¬gpÑ“x·ì>¢èIëÊþÉ›ŽÒ¹ÕV…{üœèjñ±j%ÅóÛ”…lü k} ¹rï¦t}zAÕéŸ"iè³SÝõÖ¡äîÉR—KÎdþ ’íŒÓJŠÞد…»ð¼( ›Ú­ÅâMö{¨zÒÇjï3"顉½ô¡íaòÖ#ÿØcgš±êËH‹@%]è±ãÜpv/º+œÅÇçSw’§É­òF‡#é§¶Ë”»óQÌÁôéoŸ9Ñé²^ÍÞœTÒHÝAœ +†Ît–ßfó[©Ç©µéΑ”0¾Sw×M©âÞB×þåFø\„qtz9X?è6Þ7Ò†C šÉÇERÀÙ}AsŸyýò5×}\^JⳃÙ"ö½ètöñµW‘á3uF϶‘´>©I뎾^ôn_é)×}v¤ªqqtðz%)úó>›Ð çžNÈíµ»8üP“áöF;Ûôúµ&ìÇ+‰w}üŠtS&7³ú0Þœø[wÔÔcµºþ·D¯_~°*»’*Ë­¯œºÀÆ/§â¾¬„+ Y¶tãY5õÝó!Êë«ñ§2“~Z‘?÷*éíe¿:³ú°ñƒ®ñÑ’&î§l8Åž{LJªéWn®}Öβմ±¦¢kõxK_î_ : tî Ç÷ŽòåœÅÒã¡ÔT\ý|í~ÒªËS*çYSû×{㺅’Ãì¸úk.°ï5@çõø€ƒs‹uœî±Y§¦¹ïÜW%y$ë7Ç3&V³¥íO×ÕM_Êî5 :½ÜIsó¬7£ƒ6s§mùƒ 5ù×ÕTã~.\\Ëf•˜ÖZuèT( õ;‹ŸÐ qd;·÷zJXô<5ÙWµµ¹irøj;®ÐžøÛ‚ÃCiÄÓ1[BíØ÷Rr+ö¡ws‹ùmÌ)jòjÞxùÄG^ô}⓳ÑU±.®=²ÞDïPšÛgAk½eì{)н«ü"`ÆœýÜÏù9û÷IÔ”¥h4ª†…;·q óŸ2ƒº9‡R\oþ@Eð=•CGK»™Y:yr¯Õ+ü9XM%'÷Ÿã<éÜ¢ƒžµsdõP(â·åž:tš¯ï{äî\šÑzy?5Íô÷\¹¼»'óïu ˜ÜÉß{†RÊ!~ãCðgÕ@çÞK¹ÔçÚaŽÿFt´;ç‘‘nÛ¬w(»_/ü|1~~o×ÚØ›ÛâÑýC5§{Uo¸ÔƒêoXÝ}ÒJ;:õ:äü¤A¡tÈž¿h$ü~zPÿ´ômÉ1®öšÐ ÷l<è–Y5ýmEšô܈Pò<~¿úúÌ/??¶¿c{‚;¸dÒÃ-x?íÚå6‹ñð ~•^ÏΚz›šI+M¥—uùƒEæ gü¤_n‡dW·9×<~í÷æß¦m(Å~ÓÌ:%a>³øyË{SÛgp n^áwëvCÔ45ðY݈ªždÁI9Í·!Ý1îîPrªÑácM'èvö™9ó‹·ødB«3ß°c¢g#ô<©}Va¿mÖvtû&¿qJXÄšîzÎÆ ºˆíseOúp{»÷ÚŸ‹ç©d¸Õ´#±djxÈ¡¢Œô[Õì_”J5Êš¢tèdG—n0_Èé¾F3SM·r+-«ÛÞƒÚv/wvãȾJ÷yÚJl¼ ;ãö̳z|çª[8ªé´û?»€ýt1îZ¡»Ô‰†äÌ>:£r[÷1Ÿà‹˜g¥7›ik‡pÂ9š’wÔyn/½¼~òÕ°7Nô9©ÎϧÕÃèN,ÿÅæ à߬ärªò„Õ¤ ÖùSñnb÷êi‰_÷Ê¡ìü_ð3–@·ðÓ쎕¡\ä—uªQSҌà –Þõë{6ùǧ¿›õ6”Nìá^ÖlÈ|º¡Ë²öú[W2¿Á ¦çcîÙ_vÒ²Áü ê{±&*vÌ—Žý>Û :9tf6~ù ù¦AÛ¤³RÕÔ í¹[úìüõ½§ï¦[ Fsý£±2t èîX6òIÔDpsÓ—ŽÞ{]M-¢»ÜÞA­W%Ú’^ës—‡Ñ)ïVâ.Ìçºo­wWÞÐþ g¶Ñìåµ7j2 ©I–;•”{l¨×yÍýmÃèCÑ‘Jö]ÙøA—Ÿ›f¢æ®­U'’V¹¨õ鸓’øk{5l(¡j±¯Y§0 ÞÈ_ÜeóíR‰Dx#¹¢8qôÈΑÔþÑ–ý‘KvÒÞò©×ö±!ù»çæ £‘öWƒŸµaãÝîÈœÓñ‘܃!ç›4IÜœ47Óc;©ŸÙëÐm¨çeÃÇUÃèɾƒ)YÌ纨Éܘ×(NØg‹$ëš‹­_lÛIsù0ÒÇŽ¦ÖÉÉ9FK{ˆëäö>)t>7žôÉíÍ ßˆ¤! W¶¿h¶“h×_¾sÚž¦O™Px¿4Œ¢×ñ7±™Ï:tr;ƒ¶Eo¢9a?0’:ž_¿»çÔ$¼_:0kâ•÷ U”{.b‹a)óY‡.Òl䨂31œp¿/’¶~sz˜Ôu;;Çq¤ ¾-í¤¢õ]‡ÉVÝÆ]÷=‡¾%αœ°E5/Ù¼2ˆÝJ|Õ⺘t_ëi¢"MôEÁ¾ºý¦mìÇuéÝ·7.Šv§|l*j¹…Þx4¿mábâ¿Õ{/Œø[p>õ¿{½Ëˆ/;ù/ÆqÂ}(ºÚÝ÷Ñù¯{ƒãž ?|íTùªÏUÉ ^Oî,9ž{&)œ¡:EKkíãQg#Oå¿ÑâHì{wtÚrÇùF#ºôý?+”'p^5Úïéÿ&ŠöÄ­lZãþ:÷dìó#/2t{5\ÐI¡K|ð@y{L"7„ÿ:CßhZm>ð癵x{Ãþvd;¨ß—<—0vãÒÞýºÚ»$ê'qÞ"5sVF“£e²(kÝZ²Üsg„ê¢ ¥ÎàwÃhQþ«9•ÒØüƒ®J¤Ócü$Îôl¼zIJ4õïhLõ®¥n35ÛtËš¤ºƒ 0î%³ñƒ®Æ7™ka¸fn£Ú1´füú;EþëhËŒ¶&wÛ°ó¿0Ò}-4PÐCWUôêÙ,›î}¿Ic¥1dQÿBíY…ë~Í÷GÑÃ꿺Œ×‹¿4á€ðþô®”Hfë ßS¹meüebhÿÇ]S\·.›EØvÕž¶ K^ÿül;ÏÆ]ÝÔW«ÅµÞ§rK¯ù~7iKÃíÔ;2i Ý-µ!Ò:ÐNÓ »ú1ø\to”tãFÍ¿”Æ-Þà©=²"–ÆïS \E =‡õn²˜FòÃé0Z£3’gãÝ4þZâV޳ï²5àaA,åW1:ã||©¡8vÒbÚÊ-ºx$<Œ\V.IÖ²ñƒÎÄýmµž5\·Á]>jGÄÑá¦[Iq¡ýOú)îîu$ë:ü7>0î}gÿ9Gx èÆô}X´v‡†ãO݆œŽ£Ïƒß_ÜœïôëœaË^y­ñat}J×3‡µ‚Nð}T 'Ä¿x:¼½–CéºåXØü^3{²üQ˧ûVÄÏ‚Y›çáy×tsÍ^ÈŸÎ-¾¾-æ¼"žž µYz¹§‰ô=ü¬kز{éalÿZÐéiK$3¿ÝkÐ$, ã…Z'ãêÆ:ÒäVkDQ&ÖT#>vû“ 0úÞ8kóœ lü Ëµ)<ƒûÒís‘…"ÝërúµïßÕ­V¢Ù•0½,ëø·¦‚NÝÐ]ú?ÛË2¸y›&éýèšHVª¤ïÊa.äÕöM÷BKúx•¿`FéªG—'±ñƒîâñ³±7Ïfp[†š† KJ¤û§FÕÜßy9»ÿgIüªpæ÷0êäÑ09.žå?èâ7~¹r¬íYîÝl‹Ž“hÿ…„ÐÍWPVYÑ«®VTÙ’ÿšŠ>§<ßBx=…¶â{ƒg9×ÁDï'ѲDuÇÞí\Ù÷t­Éò܈J;¨¨VÏç?" ØøA×wå EÃëg¹´­¾ÆòÉ´c¸IØÄ8WòìÆ_ܳ¡üqÈTñÕËç‚®ºÃï2ïîé}Ž›Þ³ý)´õÞÃ3k\íË ç§ÑZ;ggã—W"îÃã>œ\tHÁ¥ÐáÍÝk­]Iû®ØüVWF[eüN¶Šæé© A7¡{š_ÙsœO=þd#•<û…~;¿œö‰ùJÓ2 =Å1á*â¿­8Àm“0~Ðå¬â7Ø2¹ªOöò?I~Úìv!áþ–½]Ä?q*êô!¾jñ&úÓcòOÏ$¹ÞŸ“ú&ýëôMâÿÈØsÁvÿžwÉ·ÇÛï¸<Þþy· \$“ÿdï$Ó¶ÿqÜÿé^oÿ¨wÉUOî ï’ ¯·ÿé^¸üoÿ¿Y#ý^ý©þÔFÿ µg*üoùÏæßó*ùïöuûÝÿö¯Û?çëVá}kˆ¤    ˆ‘ ¤Àh>’…¸5("$ó¶ÿqÿÛÿé~nÿˆGÉeßí Ÿ’ ?·ÿ-þ·üôý©þÔFöþÔH[#ñõ_ýO¯‹øøPáwË¿ßÏ—ä¿ÓÃíw¯Û?þmÿœÛï^·"$Sà4 !9È€$     ˆÛþc^·ÿÓýÛþO’ÿª^Û̻틿-™?¾mzj"¹ÞŸšèjMô¿q߈Ÿïr6nü{û÷¼Hþ;}ÛþêqûÇ·íŸómûÝãVŒa<@6KÆÀ(A10Dò0î@ÊQÛÌãöºoÛ?âG’Ê€ S ¼è#yJ€PƒR B25 ›%Vcà” "Ñšw åÀè7?’ ß¶ÿM>·|ñ§^úS/ÉõþÔKê¥zÉXïß|nùßÝÿN ˆ=*“ÿfï¶¿úÜþñnûç¼Û~÷¹5Rà ´@ÉBÜ€”’‡9ðÙ,‘·ýÇ|nÿ§û·™Í¢1pJP ‘ M;Ѐr`„„) P ·½?õÒŸzéO½¤ÑûS/ý+ÕK&zÿæuËÿnÆÀ(A1ÿ÷!P™9He@ŒÀ%Þ@ ôÄ$À ¨A)!¨™Íœ1pJP ðL;Ѐr`„( P þŽ×­ÁÑx€l( P‚b`ˆÀi Ü”#RP€"`€ jä ”1‚¬x-ÐGÀ•7 ¥@„l<@6 ÆÆÀ(A10Dp6î@Ê‚µ (@0@à6rÊ€\ ¼è#¨K€PƒR B7 ›|cà” "˜w åÀ Aö¯[c  P ,L€$€2 Fòo úH$’¶ÿq¯[%(†H:¦Àh@90B’(HH&@@#AI7Ð}$+ pjP DH^æÀd³Df \€C$6Sà4 !ÑÉ€$=    ˆ‘¥Àh>¢¸5("$Hsà²Y²4.@ Š!’§)pPŒLe@Š€« ƒPÄH´Rà ´@IW\€ƒÿe~·|žÿÞßþS+ýï«•äzê¤?uÒ¿NdΞþså_[Ü€”ò?å4 !XÉ€.    ˆȤÀh>‚š¸5("9sà²YÀ3.@ Š!j"    ˆ¥Àh>¤¸5("Lsà²Yð4.@ Š!‚©)pPŒ\e@Š€­ ƒPļRà ´@AXÜ€”‚²9ðÙ,@ ÅÀÛ¸ (Fà2 EÀÁÜÈA(bw)ðZ @/n@ Jßx€l–Œ P€"þ;nH .@ Š!„)pPŒ0d@Š€’‡ ƒPÄ"ü>Àh>‹¸5("$sà²YÒ1.@ Š!’)pPŒ”d@Š€” ƒPÄHXRà ´@ÉKÜ€”’™9ðÙ,± ÅÀ‰Î¸ (FH|2 EÀIÐÈA(b$E)ðZ )n@ J Óx€l–< P‚b`ˆdj Ü”#$WP€"`€DkÜ€C$^    ˆ‘ˆ¥Àh>’²¸5("$isà²YÂ6.@ Š!¸)pPŒÐe@Š€’» ƒPÄHöRà ´@‰_Ü€” sಫ¤|,úË?ÿ{bˆýL^…ŸO&·®ñéñ÷×¢œGx/-*þùSßã ^Ot"Á¿ÖœªOð×Ö‘¾…¾¹:ÅʔڦMnÜÎUÅúgna¾›Ø— ý!ð:·žºv%“ÛØÚØ~ŒG%çoêiOSk/|ß$Њ~XøVÑZ¥óY±¾Ð?AÝiÞ£Íy.Œÿë 4¿úÛçÞK 8¿MF÷hj_GE­WO¬ñü§ÐWBÐÿð<')~]"Óоœ{GY’vQiƒ€'v4ëî~—yea”Æ·kþÁúë@WðL¥ª{ž›ÑqQÍÀs"óv’ûã¥ÌØ–ðû\ £ºçÝÇøÙ²þ:WK$ÕšÌâ®~>Ïw|÷3¬C:éìÆŽJõcêQèâ¹.)Œ<ú>¨¬³þÐÝZ²eé±Y\6ÁVlJ§k 7=[ÒØS£ ÙW•UÖä†Ñô”Ü›Án¬? tB¿ð,Nn3dåÚ»é´òc×ömJõ±XÓÞýKe…´or¾éÖߺîßšß OÏâ„~¨4s–LtäÒÖOPF´ÿqvnoó¥~O9té~ý3‹³ceé~"ƒªŸ¥ñv7#m×ì+ãN/¦f 2Ö5R‘Î~n"?èvìo=óÁlŽwŸ>ú5ƒ.¦¨=¿èFͫή×3Ê™tíI›©h¤g绕ºÃݦ<²<›ëe³ÿ‘ÅYw¸6®Åæ)\…Oõ#ëÕ›½ðþ~ïS ]Kë.)UC²¹ òòm/RÎRy—Ÿ÷K¹…‰wÎh·„vÞò³ìó,L·k%’æÆŠ îEÙœŸÿ³9CÚž£//:õ5Ì–q{úí<ò¸ÙÇuŸF:ÛÌÛlܠ냦U©žÃ•·2ßï±ù-NÒ~ÈRnVÀœ*‡v8’“îA #ûî½?û~c}Y  8AI¿îÝù –wKÎQè—²_–qå†áÆód4Á$ÐùÛ®0â]F,tRèê}HV˜ŸÃÙ×½;çѨLzQ)´JýŒåœÐWÒŽ®7ãøÂh¯íWxÖW:Å£37åpi;'í”Ic¤å>_εÕ5³!ç‡I]ņ1ß&Ö—:§¨qÃÇäpB_°ót`ÊQÇÚ+—s}‘„¾&aô*ò½_íg¬/tÆ}|K×äpq·zõ¿îpžê:bªZÆqµÊî<ÞgMú®ž£æ–‡ÑuÙ`íhgÖ ºuS×Þ+¿™ÃEmÕèt¯wâˆÃØG.Ün/“[åÖô¡[ß!¢š*zó¦æ5-^~‰ÄZgì–ÃÉOYöïžEû³[¹TvsáÒy›µg6¤³ßj ¢­.w :t‡_7ߥÕÏå_Ÿ,rT/²ÛÜ…â½Ù÷l¥HEºöbÇØ¼ƒŽî¿(ìÛ<—s+kTCû:‹U®bn³Œëç˜6qÄ*{Zo­¯·¸ŠÌLúªV'°ñƒÎr©•ÉÔιÜÊcÕn¶6˦ªK_>=P°œ“ÇK¶<È’Qo3qíE‹ṪU˜rè|s~¬ia”ËŠlÒ¿gd6U‰åùêÉJ.(p§_AŽU3³ºþq•ŠjíXQ·_6ï S{$öUÌåtíÉåÐpsƒÆ»f¬æönvmE3éåñ¯ª¬ *jX~öʦúlÞAgü¾iý׃s¹!Õ>ßÈvÍ¡‰’7úž¯áŠêG¦gLp¤õ¯ª~=³BE§jNi8)—Í;èît8®z Ðo0‡²&^Œöš½žsÕ5Bv éò€ìy*êÜj힟X_«‚É,Íð+¾ø=«žÿR½ó \êð<©’zÒFîÍ!ÆEÇetÑbRv |žB6~Ðù¼¥iC|.z./n¬ðÉ%¿uO>¸‰û4×68æâ»ÃžÅë;«ˆn­¼X_@èT|ØÃ8èÂà×\Ò G9÷ü€Qóå¶$ô•Ç8Ì-ÿ2ï(ë+]/—5áÍ1î‚âªöcR„YÁ&.sXw‡ÌA6t ¨¾…é½0 ¶¾Üïc'Ö ºmƒÆã9˺yïF›Ô 4dï«Ì”~›~=×|·°C~aä­iýƒõ•ƒîæÞk³Ês¸ŒmyáNm/Ò¢M }‡¦ãsáÛ•ÛÐñÖ¡iïö„QÁªF믽gý7¡å­e{ó½×ÙMI›/R».!yÔjw53øð(‘=ù¼m§l‘FÛãNLZ6‹õ„®Ö¤a §Ü®I¨QœÁË´¿Ū-ä\EÑ;>5ñ„©è­7o Îú:–H²zMO ÎáÌh¿ká¸Kÿ{çÔÔÚµ},(vDEÄ,€Q늬Ø$ô¢Ø ËADA Š4M$BT°Ý‘@N°‚ŠbÁ öoíì ïsÎÌ7ó}ï¼ïÌyΣ3¿?,— Y{¯µî{ßY3_ü¡ç‚mdæ°‹r¥œÛމê”cºOæ’?úXKDPâ ¹Öþy°ô°×ˆ › ÍxôÔ•KÄ0Y™øhuK¿=ûùrK.q¤Ò·B(8Ósuo›#„ª’n×7ÁÔL¯ñt1ãsÀÄu-ýç8è®Ì% ‡Šæ„y‚ÜZiøM„{¨ÁÇðx{~Ïw½ÄÌÜ>f. êÏjÔ—Kêǧ÷}ZRÝû¦'§íñ'}ÊšÆIÜÁÝNóþ´‚fî?Ô±ý§oëÕ%—Ô(®<7œrÒˆ=·è™¾žÍµ×è1Ì ð!Ÿ`Ò—™ëˆ:^¯ 7’kåDi»væÐ~=„žèÍ[£©ú%@Ú·ACÇÒókPÒ.Ú`ŠœPS•ÿ¸-j 7·?Nº+`:ƒÎðèôÞ`çê<÷‘Ûé¹²*•x]¯£Ürb­:±èê„"`ÿH?Ò¾÷qBÏïv‚¦Iã©å&À5 ó˜ùŨ›cz@ÕÀIN†(ôxöE°à£{cܯBÏvd|ç Þ0àÆœdfþ-êh$915í5OR6ºeƒË¿N>„?ÎÒ‰™û.†ŸNÍõv1´Žƒºƒ_Æ8&v—“EC(g"P5š{¦û°ãä=5nz3HVy ôÄ0ìëÕO¥>´Ž‡ºÓ“ú=É! >¨h~ùTJ;ˆàãäåœ9£¬Ò\ Å¸ôÐk=1Œ7¦&ÓѺhÔeÅW¤MÎ!ÊñpzÅÐXuK÷ª] ù%,Z=Ëš69-4ƒ×+—K}séÏS†:3ãW—É!›Kµ—XCYKºjà rµV:¹é¼˜G¸»;W FÖõ‘‰o˜ø¡ÎAiÌžC6ZQ Å@êòN$ú‘i=V† w ÿ?1ˆ¥j|72÷Ÿ¢Žý:µvWé¸â6!ö5ÿz1xnŸ>e¦}0¡}¡7BW¥qº´=ÖjTufær¢Žî—³IÉtj²g1|hŽPm4=IÕß½î¸rbö÷¿æ,†Xßcý¦0ópQ7Á5 bey6Q¶íJ`ú(½5º!„r_¿Qà[fL»ÐF ;l·-ÜÈÌ/Frœ`B6Yp`v°ži ì¼ÂÓüEiλ¹¶2Æ S¶?æbÁÙ¾{[*?Ô­]¦¾c(/›ø\ðë.ö*»þï5-÷†ê&WýÌ… Æ‹^'fæù3ó§Q×csE;µÙäYUKÀiOÕù²Prdª{ÓíÑ. |“7^ËZ ?ªNô ¶cæO£Že§%­M¬u$Ù%0ï;÷‘ø[(±Ý—âÄøZ‹¡—ÒÀƒ™?º×RÖŸìMŠO放ïP U«o>èô+””ï¦&ÿ9A êá9á ŰòþÀ½v†Ìüé*̃3œŸú0‹Ð¾à¥ðKqÿRÎ×PÒºÞ°Rñ‰õýý•àF!3ÿuaÁÞ+ž&g]ÉñK}6”‚~ÃÙªÕýO‘«ž–‡ T\à‰jïxWì«éùÞŒêèyøY¤ÒÝÛÌÌ¿°)hÖØ~Š,Dyîtw…_VT§,fæ-Ò¾Ô­Ö<ø‡kÉÜm|àEr)L[ÙÞiÒêmï«}û0®Ç….]þº;ýz<ÔÑþ‡Yä¼NÝÊѵ¥0oâÅ–cÂÚò¼çð؉OšC#úìán†X>eøŽ÷__}›)?ÔQݯõL’vÌ`AÚä2Ð}ý=Xq!œ\ª¦ d6A‹ðÅ•‰bø8™ôÑ•‰ê(7”•U™L?]YsÕ†¯Ì8CžòǾCàù,>¥¿G GµÖYbâwë´ åd’I~<šá¶Ô¯ &¿tkŽTc*z:?ÔýLãʽB3 .¦nG&•Áý³…w ôÜj.±øÑûæÁ¡ÊDÉÄu TZÚ–I² h¼«(ƒŸËþ0ÜAŒv}$ÖÇœaê´½%G;âë]þ`d4ƒ‰ê¶’y+£Wd‹¬á‹~–æé£' ôõï’„°jWÌgƒóo¼l÷ñoC݇ç®>ŸI¾ÜZæóyx9Œïºå¬íÁ’èC:ùAVŽb8¶Fk½ÚVÆ¿uù-Íú÷Ì$g®ù8 LËÁØCgø·=„žïÄÌYƒ³ÌrñéÆ¿uôz"ƒ(mï6—Ãð»?ýúE”=»–3,l¹ùŠÄòË©A]Ê™ÙÍYdþã­ŸBÊAÔ½dnjjIYÐ}« dêRNVbè}ü k§ñz€y7åÞèI‚ b½mѯü«åðÌÆlú«çH£Šß ï~®°Nid,†.#7ŒÊgü£P÷³;•`2ˆñõÅ»*«Ê!-|ààé9´ìýðýYnÀþãËûö¯°Yd¾@ã?„:z$ƒìŸúèò¼ïål×Aa´'’´úæª) ÅÌ<^&~¨³_rBèm˜AJ´.W\š¾ÿ¸µ9Šì –Û< äe /ã¨éæ£f1÷êh¹ ¢ûSÿåÝYÀÒÙ¼aDÚÓ²,{O)~.†Âµ”óã‚:þgjà}:ñ®-<¼¡–ê4v<‘M*´¿^ì}"‹W<ƒrœy;ÆÿuôÜñt2UiØRÃn ’m\Cætë¢eóx#Tž÷œÕµR -FÔ•Çø×PqèZa|60äŒÝ£¡ˆ¨€Íú:?ÎÞŒ!»ƒ"‡ýáç†C‘Ex¿SãN}™ø=Äë…}Ì N: ‘d¯½^ÎYùã·õ‹%ô¼m7˜Û5rnb™’®6ÇžÌÄu^zQC¦Ê…¥´²êO~ôž³6–Ðû\\X´Sàtá±òîllß=ŸñéFÝPß„bq;|½'u}.¨€1ã'õ~àK²¨±Ð÷]@s‚æù 1Æî]Ãøt£NçâDl=ï[å¾~¬¦V±$z@fçß] C×O{·ˆ¡ç—NïÐ><Ôݾ~Xÿˆ;$–*Çc*adNÅØÚX²D¹uÛµ€O=$@uü(w:~¨£ïË;Dã ,/fA%ô²4-ê8á,–KÇuÇ6>ÿÿNFÊ»pñ¿¨„ Mº]Jç\ †k FÇ÷ñåx_Ž”v¬gé×SyTǦ}ÄedÍöa¿‚*¡x&oÁþH«¿_xšìé%g ¬>îTÊpÚ”…:ïi¯n;xɈJN䬰øJø¶çЧû9²têÑ9œ§ž°»“re=V5õÝ*`üQW²5úûF2rÇ2fª„î2³âX?!iõŸ)ã‡:ÚŽµÐX ¾F­ KŒ#ò—æ÷LߺC¼åd*†Ú‘ÆŸÔ3þ¥¨{سDZ••·Éݽa‹ŠÞîó䪨mý`ÀÕiwóÙ¡³£3Ré¸ËP·÷úu‡KËn¥=r_(msfˆí—êï_êpï¿ó³sÓq¨AÝ—Ê÷yŸ®Ý"õ¸!z H>msl‹¥ˆ,ÖÓX:åŠ3d^¿é|;  úð–ÓqWy\Ǿ³f’û(Ö-ìhþÖs† ;|ݼYD`Ð\¢;6¡ØñJè¸á¿‡­î%Go’_-Ä;—)€žß,"­û²3'6f™-—0~ôë°Q'‘7à•wƒxˆ-]öÚ*`äžêÕ¡B©l®eéä¹@VŽžãs ´æmeÜP7`¨ó„ËoþçÆ^}¼Yâ©É«D¤u>´"â ËÜ Ðë'úõx¨£}¶®“7;oM^ÀSýsÆ·íŸÜ ÝuŸTö¶xˆh]4ê(—æA¯>7È7.Hc]/µ7žx6nŠ|äŸÞíJ¹,¥ú-æ¾C‹²qI#ûE“í£pÕ1ª÷µñä”âê­G'7‚rYzF¯¨öãïÔ]סpÕåTâØ ˜32QJûa‡BÏóÞ½Jo§o $¼ .Xû¾TªëØ‹Öö™ÿ¨s*Ù¥>cuÃM|<>Ï¢áqQS&à0Z9H`Âø^#Þ2÷ê|Í9x馳jø|lí§¹~•˜PÕéWƒ;(—™«$<}Ú¼-9ôûd£Ni'(¸F|([…žI¿Ö^—×׺ìpcæÇKà&kØ”×ö´Ÿ;usRÛcf¼J ×ø-zòTcrO•ßû,&¬ÝÏÍŽ$p¡ S¸cýX ÜôܨvýyòP·7mE`ûùWɘ‰÷[½S(¶Õé •ÃÔvÊ{gpVÒý%~¨|¨µô H&Ë(ûŸ pý¢û>w‚„$t¯Û¤~Ñ ®µôU© Ö|¯Œê”ööåWåRýºKüz—¶I`(ië¯FYŸHÿ¤ñ[Nm:ÓñCݼœž£· ¾B¦Píh¿*‹.ú¾gº„ô¿Ù0Æd’3Ø·øñ50ßR«Õþ|':~5ulv1Ÿšœ.““3©;  –]Óõ}´VB|3w6«^o(%fæÝ×ufWAtŸçË^z^$§Ã¾¯øùÆrë\¿KÀêCÝ„q/iuë:mlH$t½©‚ý÷ÎÏJk—Ø–o_PãëÃ%ð –ó ÎПK4êpQ~ÌlãEâ+žy@ºº ®Ÿ¾’HDµë-¦>ö€+û(ƒ: ÌÞAí0ñCÙ¶÷Ãú4‰‰ém “£6U0êäš%ÕêI¤Ã©wo'ß«•F:¸a÷ ön'&o¢î€eŒIó¶2dê÷Mתો]ÂÇMI¤§ŸúºÂÇ®°×‘rf’íÓÁäÍÚ:öIÊै¤ØõíxbsœPŸ¾]DZ}DK°2KÚ|h•ñC]fØÑa6ÅÚ?¶ ޒ铜S’Úö/©§YÖ“1ŸQÛ‹}èÏ…:nb©É’§8Cpð“O˜|2´wb¡}!D›ZÉH öjlÁ–MLüPG×Ñ „ráÔõ«‚a&o^J"­×G©ÀÇtôjI›/Ž2~¨¿º1¬¥:–ó*^Tóûï_¶'‰h^pLxÅ”é ó˰oõúlúúŒFÝùŸ*dæ1¤‡Ð¯ü*Xûä”7‰Ì-*N8½ÝÖÛ}Ý0Ï?Ïßf]ZNÿ|2ÔÑûeQdéÚûO´£ªàüá~©G’ˆÚÓ}ƒ ‚`z'Ǥ]>8mõdêaZWƒºgå-b­4`¨õ ²ç½¶&e{žá ¥Á—î–ðš§ÍÜOêØíú;æ¿ßN<”F1UÐ)ÖwHVh¸H’§Â…PƒÒÏI’?Ç…ºÎú=¦t5=MÄ'å³å—1bÙ}{yi}þæÙv8Q²ad¿_:O°Q§§ð9wÖ(„Ü´<ؼ6­ ÞpK¬m:JIk?üêæñ‚@ 4<  šèë…ƒ:U¥áFé¢`_¯JgèFèI‰r{á”+D$Ü+7—ÀD¿ò¡vLýCÝR僀£DÛ(ïkAfŒ~±Ÿ|‘”´^Çb¯áäC1O( «˜ü‰ºuÊøɽOÕÈ« ¢ñî‡ÓvRÒê›Ø‘²qü*†áGg_ Ìý‡:fß• Û²ºCAäžwzã¾IÚ¶Iïkãzøzôm§å´®uSãNbåv'¾NX\Tj…Û».ó¶[½æŽ0½A£ç­¬V¿IúóT©«cDZ]ŠgŒ$ã” €*ðfŸèÜÛVJh?\{Ø·»Ýçâx1POqkÓ:êrú̉ø£êâi?¬¨‚Ë!ݪ»›Hɼ!wއ޳‡¹VE]bZ÷ ˜û¯®ÕÔ Ü{>»_«–N».0’|jûëŒ=(Ûôkâ¶çÅÊø¡®ýxùé§¾0”:f?¨‚ËûV>gIÉpê±î GøHÙ܃›AŒE™´Ž‡º½ó—á}”ñ×®‚«oÏvW •¥4Ë~y%Y…7‰ÁWgnkÃäOÔ]Hèû¦CèXÊ:nzTOî-añ&H í‡Ém[×¶ÖCeüP×e’ǯ¼Å!m¯7o̳ «åR’hš2rë×¶¾S±€2Leî?Ôm¾Qd¨g[ÎõÛ!%ÞS5ûvrrêiÝYŒ6Á÷¿­aî»zì³5*²áýˆe5?Göm¿8RJZ}èu NP³™ûuk•õç ýØ÷(ª ÅÚ:\x[Jü>_ò­>ÂeöYÄp»ßÑ.n-Ì}‡ºG»ŸL[ÏŽoyh¯9åUð05ëûÃ")iõ‡KNši²"X ƒ/º™.šFßÔÑ>Q1@¹Ñ-®‚Él‹¥äò{jÃß±m¿L?ÕbåAWæ¾CíëpJ¨öïݦYOïHIÉ·ìÊHKû¶}ÙÀ›n¼ ßg4êFŽ ¢pxØ£{£ð¾ó¯Kf)©?R½,å\·dèqÅm>dʸ¡n÷Î/|Âå2³ Æ|µ_ìƒ÷Álias˜z †îJãd{:n¨£ÜÄ' E¼—r©‚Yw’7¤¯“iá©k90jöl›ÅmþÝÊø=­cZSƾ ŒttWnÐJ‰<ÌìN­ª(íï\Å@¹}yª2} êž$nX°°³$TYÇ|»H€uüەꃘø¡®oô¦‡ö×€uí#7Aã^3TÅ÷`±–ˆîLîn îgŸlY9.ÔgRŽíLüPGûj§Àe’ Øóšbæ/’DZžûì¥cÜJøSýŠFÝ´ù#wJŸ[é¾ßÚUÁ‘“#Ô¯$*‹¬¶º¿N€!TZêÇÔ=ÔµüÚ’~¨8¨Ýxó7 hö{å°2‘øPv¨å¶p¢úÁGüùú&]¾6jS÷P§2P]'1" ™R'"p}3…r€N$Þß á›̲ÿøp}?™¾ó9æi#jçö:POu¤+`[ý_`¿ÌøVÙò—ö Xœn__ñý鸳PG¹döžtfÊB4Ç‹:î¿á"yØ›:ÁåÒ'k0Ã&€ý=¼7ù´Ž:²@oÉÅæp‹*s'`»íJýéoÉO~…ðÒ-gpm¿`¬½"”ö]Øu;êR»¾3¹yf7jëì­€$SêzD’4{È×7æÎ@ï›Çó¥îLþD]¤î¤ÇæÞ·àÕØ*`ÍøÈcEÉÆÓù Ÿw„²Ô©Ä.6Ìß-| Lß‚º¥{.é·AçEéè¯ó°²g7«× ý<Ò¾ú”?ÏøÇ2÷ê†Õîñ9øù6ãw­€)g‡ <_„뛓OG„Þæå2~ëh<ŒXàh‘5¹ÿ¨8lº°18™Àò³[´c»bün¶¼ÊóºHxXì#6püÊ µçø6_PeüêØMÔ±#Üv¥T ºGŽŠ7«^$t^´‚;Õ²ú…ñðá™çÅÏRæþCÝE×Ç[üe`Í¢+ÁuF;Ñ,ž„Ð~œ–f´&àÃÔx¨94·ã ¹Lß‚:õ)IžË@mÖÙµQâJ8ï»{çObBûeZ‚‹ÝŠß§ÄC@Tö­/=˜uê¶qƒ_ï…;lù)í¾%Œþ~{ÄtO1YúSϧ9Ð BO½ýù.]xøïEkŽ^N ½¹’ǵ;¹•p$'‡·$›¯a ³¬á¯È»Å0¾ÍÏU7Ôa‘èûòÅxíôÀÕ}~%̘{Ó±äT1/ý4¿ò°-txY ú2:ÞnÛ›lìÀÄ uô¾X:P®¶¬JøRf¼Ó$Øi9á7Ê”Ç?´ãaT‘Þû/èýˆÔéÖÎÅN/U^Öa'D­Ð²À/¦v†k,qÙkkPb°ný%òJ³"OMÍ£u*¯êØ«ÃFì‘Ûá)ºN—@ªN¶û†òÆÖ6îœçñM“{ͼµŠY¯£.eÎë”±rhZ>{Ö5§˜?EÉùxú¹uq϶Œé™ ®2Ö‹ÒbðäòîŒXÍ<_æ@͇ µÚb!ø>œ12‡yN„ºB£©B½±¹ µOÕ5;¶>±–oqV&ÉÏ §tyÌÊ 5!„lmž6ÛŠ®2Ô Ó~4çÕÒ\ÐøDm)ÝɦcGíŠ"ª+tÖ½´ä€ÚìNc–_‚ìUä̱eLýCÝëð‘íÜÜraPiŸÓ^ìbˆáº3¤0’|÷X¤x³Ö©ºMôâ ÿÔ'©¼®cO¼¦^}(†ü<#YÙ«Þ9Q÷¤C$‰Ü÷tŽ·¿5´l¢ND Ÿ~йò³nGÝ=ê1jd.ôÎèéPR]7z¸‘«}Îúzµÿœ¸´Bë¾¢2~¨ó_OhË…>oNí'Á£˜þîý8KæSùÌ,`Mí½&çBˆxøÖ¬oÓ· n~ß©²»¹ÐoÀà CûŠÀ¡JïÁÀÊ3„öu_ÛÒî˜ýÑOuf¤Ûú+ôûä¡î×]®˜1ó/m–A€õÃÊ+áDW<ì¡ãKø±âç›ÁS„®¨ x7ß®9­{ñfúÀ"°›ÝëŸ,¨<<4b… Œë8M?—Öý3eüPgT5 ³sœìXÛU¿ì¸?8–ñQ‹Of+<ÛýüM‚%¿N®2ÏiQWS¾ÕEW+N[½zË=Ø_æù@­<ŒÐ}#˜ÜY-?å-a×…#w´§ã®ò¦Žý4ìŽV°~|âí–‡öºƒ–¯è¹öyi]¯™ö€-µ :f¤@ëX¨ë¬;Õ_2%¨]†… á­PåV¿ñü¶çåá<Ûá¦êBP.û1ùu£’%ßrææÁϱ§‡}Y\¾c^>rÝË'†©'¾.À©wZ6¶Iû§]±‰éEßïÔ¥?Î ŠXší^X!n(`žSóIë~ñ@f‹¯E¨~”§qΉɟ¨»Veqt·y³?Uç’ûEkàë­?³^G’m—¨ÇUQÀfé¬ÿfÝ€º· YÎɃ‡g« ŠYÀo:å_Ù‰O”öÀ«9 ™QÞN}—€ñ½gêê8ùc*8çõí™7îÂBkmÁ’0b¬|0e –œoGÝî¡‹¶9>eêê¶œ|vÂÕ#ÊxsJâÖÞ…ôÈð…sšÐçz­@¹ üiDåm{M6¹—ê™ ó6Ø”.¾356œ"Êcå†VÐOi,€'/©¨Ìý‡º‡£K~mË]£¡•Güó!ÿµà %wVÙÈò»¯îpÊYW­ë@eüPçtÜÛoá®<µu‰X':VpŠZ¶‡S{Ó´oÖð+Œjh >ÄcÐ%&~¨“QåuMDýr3¦Üª1àÒIrqãÐ÷§Å¶PR«ƒK ÁŸöx¨«Óü°mÞêH~–ckU–Œ^×Ò%˜Œ{òÐÎÞ¥õyE °zV¸ÏÇeÌsZÔmšà›÷ÝPÛÍS¹9°`PÌÚA·‚HŸ¾;ž ±]ê ´†U ÊFÖ1ëuÔ½ÍêÿúÞçþ³’Oôûž –NK¾[Döœ7ç߮䵛õ1]&Ú+4#™¼‰ºÂ©±{*0¯|â&è}8‘ ÆÇM6÷1=Au§œÐm™õªz×MIz)¡¯/ê|4—ï™”{úIV6ü±úÙ³ÅljÒ~xîøéÐëJ˜‘–ÑBæ9-ê”G0o*$gA±ûÙãÌÈ6ex¿¼R.Ïw üéyÊ»:ö¥Aoð>.;ÓiAÜØk…SçcdëOݾÃj8 ¯lLðó݆«†µÌzu¨Ûûc.4¥NHdBX¼iÓ‘Éþ$zE½iɰ:“Z'ÛÓ;ÝÆOeò&êè}û\f_9̪+_[ôö'ô¹VgHä͵¯£Ì);{f¿uo;ÝÏÌÍ…®¼wݵ¿fÀ·ëé[îÅû“ÓwšmäB`Aàšê/æ{;ôz‘‡:ñó ÍÇ“ráõ"¹Ô?¦(Öǵí”áíCiò ú-i[©3}E£n)U–ø¹ ´U˜‰ ¤šóM¿ž†èä_©ãÞ˜ò›æs;2ç[PGŸÃÌ…¢©çŸ‰Óai—]ƒ9{—ºóY®mqïܤibö©{¨»ú«]E‚M.x•Ž›‘ éÇ*ÉÓò>A<Ï<÷Ûë×oN³Fú©…w_}`ú–÷uì>išãع`u6û³UÑx1B僔mßÃèU6êĦCh=社êš6žxì94ø“¨Œtlºe~×Ýp‚ToÜæànûØÑ“\W `ŸõÀÑÙ_˜s¨ë«K}ƒKl‹/V~”ÁÐÇ…$!¤\íÇÒ[›ì Ã›Ý?“W  G-ßiE{fÝŽº©Õ~˜qä°IÙÉ`«ç þ‡€ã$æ\騢7XÅo6.Å÷I¿f݇º’ÉÊ%(·sÈ ýì£Æì ÝŸk;sgÚA3u ð¶îõþ2^0žÖE£.×j^ü-?9Œ¸ðÅÁŠàõ-Z½ûô1Òõ~}“©ž=P:¾ €þ~ý¹ÈPGŸû•C{\= õ½ ÛÜ^ôã“?[•¬ìéìéê 4h=¿®Œꜩí9¼P7ÖàJoAŒÍ=ë—üÛ¾o |ìÔ[ë_ÛMè+¥óµÊ‡:vFÅÆ§#åÀvå“nB;«xó~Eþmß›óýA… áÕÑÄn`ÌäOÔeµûøí'®§Öëf¼ÛÛé&ðï\~…}ŒàÅ¡õ4ܤìI[!„',¼þÉt¾f£Ž>¿˜sæ»ÝÏš|Œ²ü^<ŒìkÌð>oÎmëçL©Ç WèóÔ¥-òî”ÓFÝÝázBº¿Õ?”@:Œ8Ãî}Œ jë_}¶tÂϳýõnß§u<Ô©f˜¸k\”?wó9, ¦Pï*ð?NZŸSSiÒJ͆h±[˜ó¹¨óÈúyyÏûl(¸KýJ…Ëzº}stI·® ëº@󌾪_ç aîp꛹ôç"CÝr{u‹iÉÙPîE}Á'LìCjz’Û7U¾5”9µûêa$„j²{Ë1‡Ôÿ¥óãóŽl ¾m|j^ „n,z¶%´žOy–ñf†&×íïÀaâ÷±Ž]«¶jÔÎÉÙ Ü&õ¹:9{¼{k’¨#³ ØCÙè¯ó&a~ôÍ9°.ŸÖ±Pw鱨Ÿðm$׿Kþ.» ©€3aÃqÂ_96%YÛèø Á˜ØÅÎéÊœDÝd*QdAÁºÜœ7í¯B¾èø‚ñ!mûµ©gµ¹BÈ,zYß=‹Y÷¡nxóö9öYpÑ*äÉ0óó ÿúcD­ qÏ 8–`ûb¯—¨§)á=™ó¨£ÏégA%u™†\±òš­‚Ž‘ÇCñövq‚ïþS¬ë#…à“^uÑÉÉŸ¨Óº´B%Kž ½7&t¯» 1Û“¦W#+©eÉ)¼>3º [U*„¾»On/¨gò'êVX®cygÂÒÍÔÂË0°wÀÁ%9„î+Ýà|áÃyéÍB8ñ«ý^w&~¨³ØÞ8e–~&,Sì7ÌþãÌÐä5Nà’Ì]öÇb6‚õšc~öï±®(÷]éëEåS{ŸÅæ²®¥ïB}[ Öö®<1ˆìÐv¿é¼ÔA!ࢤpÊ]æ|.êÎÏü°U±/4oß®,”B;5×ÊÂ9Á$ñzÏòPG˜}wõsׄãöÑWÂܨcß0ÅvdtEN£EÝßý’|NmÀ»ƒGéÂÂt!ô ŽjÑ:ê¤ öÇåb=¡—h%ÂVgÎQÉý“¤ëÍ'<\™çýB¿íúX]#æ|'êf9æýí–®5‹¦¼j–@þðÜ[]§‡pêkr\àø¼ã7àçÙŸê\i]4ê¨SáÝÒáHü¸A•bæ{,!¤²ZKœdDîlq¿ŒëÚIéª{B«ŸöïY‘ÿ™ó|T~Ï@’©üžôw™Dýâ2×õ¹Qÿ· ÂCR‘&Dÿ/þµTÒ2F<Rƒha3CüÒ‚`Rã"шQÇg‚ðT¤ ÑÇ„ÇAøH¢†Éx!R¤aýú×z""¤Ѥn†ø!2¤1À$ÏE¢¢Ž ßá!©H¢€ƒð‘"Dš{„xý‹‡-ëÁ‡äïæÕöOö± b|H(϶w’ÿ×¹¿{£ß½ÑïÞèïÛýú"*Ïx2ñ¦>Jk†ø!2¤1ø‹­&,6â…H‘„… Ì BäL23F<Rƒhar3CüÒ‚`²ã"шQÇ7d‚ðT¤ ÑïòŸçaë…H‘„… Ý BäLr7F<Rƒha²7CüÒ‚`òç"шQÇB`ò[ýÿ¿‘¿›?Û?Õö՟Måà5Ò:’êk~÷G¿û#žÊÿlô»7ú½oôßé¨<áÅÄ”ú¹©¿³@‚9“ Œÿâ_«Ž Ëá!©H¢ Œƒð‘"D “ñB¤HÂÂäf!r&Ñ#žˆ©A´0ñ™!~ˆ iQûÏô¯å!©H¢ ƒð‘"D ƒÆF¼)Ò€°0Ù[ AˆœIüƈ'"Bj-,fñ¯5øö‘±þ^^lÿTïÚ¿z±ý»{‹´öGxé¨ü~¶ö»?â©üÞ?úÝ#ý=z$ê^ç11£~.}ü3ÂGЍ÷‚IŠýÏZ-LZfˆ"CZLb\$Q ê˜ÐL’Š4!ú˜à8)BÔ0Ù±/DŠ4 ,L~H"gá¢g­"CZLê\$Q ê˜àM’Š4!ú˜ð9)BÔ0ù³/DŠ4 ,,ñ¬5þöù;z°ýS=kÿêÁöïî1ÒÚ'QýÅï>éwŸÄSùÝ'ýî“þ}’1s½É˜÷m€ÆE¢õZ˜¤LþâUˤe!r&#žˆ©A´0¡™!~ˆ iA 0Áq‘hD¨c²3AxH*Ò„ècòã |¤Qûõª BäLB7F<Rƒha‚7CüÒ‚`Âç"шQÇäo‚ðT¤ ÑÇbÀù‹W-åÅæ…H‘„…… BäLÑ0F<Rƒha1CüÒ‚°ðõYO/¶ªWí_½ØÌ?D†´ X¹H4¢@Ô±8› <$iBô1p>R„¨aáf#^ˆi@XXÈ- DÎucÄ!5ˆy3Ä‘!-ˆ}.(ulL’Š4!úØPgü铨*¿Ÿ·ýî“þù}ÒïéߣG2a®%9ó¾ŒOD„ÔPÿ&(³¿xÕêcÂâ |¤QÃäÅF¼)Ò€°0™Y AˆœIlƈ'"Bj-Ltfˆ"CZL|\$Q êÿ¡^µ|¤QÃdÎF¼)Ò€°0¹[ AˆœIôƈ'"Bj-Lüfˆ"CZ,ÜñªUÇ¢`‚ðT¤ ÑÇ"ÁAøH¢†ƒx!R¤aa±@‚9SLŒO$Q êX\L’Š4!úXl8)BÔ°ð°/DŠ4 ,,DH"gŠ’1≈D ‹”â‡ÈÄ‹‰Fˆ:0“°W-IE^ $‘3EØñDDH ¢…EÙ ñCdH b€Eš‹D# D ¶ ÂCR‘&D 8á#Eˆs6â…H‘„…ÅÝ BäL¡7F<Rƒhaá7CüÒ‚`#@}“:ú_ú#ª®ëü7ú£ß½Ñÿnoô»/ú½ôŸÖY0× õ™R¯ËF¼)Ò@ý[LNH"G 0Qq‘hD¨cÒ2AxH*Ò„ècã |¤QÄÆF¼)Ò€°0ÁY AˆœIvƈ'"Bj-L~fˆ"CZL†\$Q ê˜M’Š4!úØ q>R„¨aâd#^ˆi@X˜H- DÎ$UcÄ!5ˆ&Y3Ä‘!-ˆ&].(uLÀ&IEš}LÈ„!j˜œÙˆ"E&k $‘3‰Û‰Fˆ:&q„‡¤"Mˆ>&uÂGŠ5LðlÄ ‘"  ¾„È™äoŒx""¤ÑÂb`†ø!2¤1ÀâÀE¢¢Ž…Âá!©H¢…ƒƒð‘"D…¯x!"¤Ñ¢b†ø!2¤1À"ÃE¢¢ŽÇá!©H¢ˆƒð‘"D ‹ñB¤HÂÂâd!r¦P#žˆ©A´°p™!<$iBô±q>R„¨aQc#^ˆi@XXä, DÎØh¿9!Ð>ÌüˆÏulƒ¦‡{Rëeð¾»öW½T4,½¿æÑðú¹“-©D¾.øfxÜŸæ9°P'yrüîpPÓ‡8Ès2¹ÞmZùa¨5\ã°3P®Ògׯ=–™º¯sl.˜iÊþk~ï~5ïòi鄉§_¸€MzLŠ—Wx9̱32ó[P÷²•·" «4¢å~º®!¤ÍWÓ£vw¼‚ôüÚ/‡ºK-Ëì¾·¡e§ÐOBa¢Å %ôu7fNm4™õmè³›ñçC_ó“éé- ýÌ.ÀÜàQÞ3N1¾0î`#¸Ñ¸QYu±VóûÒ~€2ÔõQèÞ„…ÉfÎÖó°éÍqïn§I·2û&ƒ×î°zdÞãU…qðøä ©8—ÖÕ îŹ®©Ï¿Ü€åÝss3/Ƨ÷å']Ÿ&·ª¹9=ˆ;оq0öú§3CúÒ>—*ÍuìÄq3ñ#¾^ ןšÿ9F¼â ÃHêEê/ÜayZú ëÌ88»sbÁö$úõX¨µÁÉcõÌëÐ#ýá›§³c``¾ª× 3~›¯Î*¥¡D8 LG5|¦?6êé>óÁ< ,ýëÇÊõÑ¡€O,”ƒ)¹à :]­¾”ñWD]ìµçY^©`"P—EÁÁž¶ùEƒÃÛæxl¾9ÃÐülÜ};9üÝ5&~¨SŽ‹;™x±Œ†G ùÂ=ý‡·ùȇ>ø<ëeRÜŸ|£QWœt1êç•kPv€2à8Cj“k؆“«ý‹w.}äªÔXÇÒ8xñ94‚‰êèù¸W!q|\ÇÌÐÛ~ð>_ïð¶ëZiŸð-r“†Ú<ÌÄuÓçœ4ûE23Oë,¼¾}{¢mZxÛœ 36Íæ#tEŒ(­Si©û?ìýTTÍÖîbÆŒ3f̘1Î6cÆŒ™L“1cÆŒ#˜QRwch2"PMnÉA3fÌ÷©^KÏ÷~cÜqÏÿ~çž±÷ݾcüÆÞûÝ<]«»VÍY«ªÖ|$¯ë6-)­JªU»”Ÿ¦ùf[fè =Þ/ÒIûfhG.a3—^(£3ÏS¿û8 ¿‹t&šBz=;'ôºçEß]/ôûý Ó©}aScOš3¾­[Cw­ô¿{Ã@ì?èh :ÓèwujÜŽñ¤‹²&Ëêœc¹ ›»ö¸èDs×ñ*#ã#Ž­]N‰ýÝ[ú§–,¢ó…ݧ(ßmÎÏ_SÎ3ß”7Ž_Cœ)mXÉ]¯ ¸Îñsêw;.úcB7rw†QR¹[ªƒNRÎÀ9øÉ/ˆõéØ:Ÿ#_ÜdôúöÄñÉ>¢?-t÷¿µ ]Øñ: éÆ+5ž U7G[Up‘mnÔg²ZâD W^9P"•Q§‘Î!]DZè¸ E°ÙU*êÞâP;N—ckFÚy³«ßRÆ­Ñv ^M惌¸[ÚÖ{B{eÐ!Hy4ô ¤áÃŒc´µZŒËÓxï?ñe—•C…õ6ilµ‰ãïË# çY§"9­\sòõµ£”´>æÈÏwÞì·Ÿ@'^¾ÊCFivɹ$Ž?èf÷í¼?µ­Œš¯›ýA•âA5÷šÝ‰­q韒Ɔð´ŒÔ›y!3ÑŸöËïú^þô,|B?ÃG¨[`JçwÕ/ý©Óõüùᤡr4,<«{LЙBgÍ/ßÛW¬Ãvˆ öo:ðÛýöÑêÉÈJcl(|?7è¶,QÎkUy™4eºrPEç5{¼ª]b&Þ¼õ õÔdôÄ#w\j¹ØÐÕÙÔlüª—èé¡ãñÑ)ûèjTHÛÎÍ.±ßþ/OyùÃrò<âë»÷’ØÐYÍæB/R¿£õ3ËÞì% {s?Úð M}p|fW)•nœÑ¸ÞT9ñl©[Sð±.ƒŽ»Wó9Kšr^º{hi¥IçÚ.—w}’¼ÜßúÁ«±^d>²ÝuÉ.r4èðæbä%v€Û}¶µ£»; Øè$'Á'Dôg‡nü“4ïâ²dXTûµäü±ÎÕe6ßga{ì¨Ì‚;6Èéä§þ¶EvèÎŒÝlš²í(m»»õ‹e’-Ýl5Ürùå?õ¡}¼©²DNÜÝïìwÑŸ:Áoî X7l+]˜¦Ã®\f¿ý ¼Jbîö˜,'¡¾˜à îÝ MÁñÝt,D‰™ÈFêšÙÿMËW—ÙÛ¢¸C¤”ìªÐ&ýnDvèj^Uyä]ÝJ}0«±žúîXlS ëÃRõÖ7ñ×·¡É×ÎÎYÞ[NïU\X{HЩ ü1V’¦|qÑêT¹nu@/³`Üú)ç¬è·kè$§³x€û:Áïl.¥Ÿº²ßåøJº}Á¥mpwfcéqS{®%µjüf¸²œæ¦Íº£ûïÛ#I»žKº¾š´‚ilÄF9Ñåî_7ŸhâÃJWŒ~½äª }Zv:««œ4áPGø]ô »³±jàì]kØïºâfŽú.þz™ñêÄë+¡£ŸÙ—ÇÈÉý|Ï5Ë-ºŸ• ^qck¸Ý—yJ‹Çã÷ü¶úŽÇ[}+zñèÒ'+9ù¿[·+g‡èÏݸ ïi†ö°ç]xåÕå´öè¸EUhÏàÂØñ¿jÚP›û±oÉE¿±ÿ «Æí6[b‚¯áêáä²%D߇ ýfKî½¾·¾wEN9U»%輡+ãvÜŽŠ>CÆô¶Ó»€‚õ>ìw]îõ½TWoÊiO̓ýœ :tšŸ+è|‰Æ“™·ŽF©y׸š¤ˆ\yàË 9•Øå·¾ßJЕAÇÝŽ'x²G†•v7 ¢8½ ‡ÎOóetéЦ÷ö4çœÏáÃrzëÜs¡« ÓúþHâïÆ Âa+Æ,°´4¶,uÙˆs×}YêÑL•ú–Åñ2ì§ä¤eÒ~xvkW¡ÿ ãU°?eœg³Ò|÷ ÕêÍFîqHòùâË~×ë×þæ×£]rÒL:?Uu¯.ÞìýÚÖ?sf’‹ïuðcKê‡deÚZÓ¡úuÆ»É)¿ýÇ9w)t«‹ç®šùò›ç9.¿.þ^y°aéËÆ–ät``[ËcrÑWCø{7ü½Æu‰³:—5¤ô»/óhÊìÍiô9ÕšÅrŠº|BçŠBì/üýö1|æîËp³yÊîõb‡^…yNöezž†ï$–f+åÎä$Ì—Äþ‚n–ÉÃjVsýÅz™¹±9«¾If¸û²kÜ"šwik^KAf®¸]ÐGì/è¾?б³Š`±¾Ož?ˆ®mv÷'úk^uó¦ýÊÍèKÓ^†Ñz êö²dÊ„*±¿~<’¤ýh4qû9ó°Ÿd¸m<ùªî±¾¬™ãÁó•æTgóVÝN£ÔDÝíð( BAWÏè[GÓ« &øÎ¦–{±35|YYo>¡µ¤–ªÛk,VÐÙ_ ÑÑ‚N]DN¾»i»+ŒßEm»/¦šÖÿjôÍçO·ÃGZÚ4X­ ]ÓŽç£Þ :Sè,V¿5=p•6ËÙmòÃŒÒÕv5û ½š«öui<Ášö&Éì_mQЙt×%ÏÂt¾ò éôõûíÿ¥±hã˦ʺûš$Ó¸Ã2t§ù?‚Î:Y›˜6Þƒ•L¨+íHÚã]÷æûg~[MdsÛJA³x™|• SA·µ¬×,½—JÆ]€OU_EuóZÍý:×—µÓâ·¦Ù{[8}1RжÒg™Ín º2èæû{íôö bGÃßöèÕvÝ\¤?æ‘/ûb}¶mN3kz¸4¡Á¡ 2Ø9 ól© ÓúùHbYsžáŽyÁ̹~ƒ´ÁéÔÛ©¯¼}ÙoÿÅu áO7Tˆ~½âxƒn„ƨ;„½>Uî¶lÆVúä¸>iœ…/kd%Û/ÿ`A§s³j›ÖPˆþâxƒ®‰GÿK;B˜fúpv;)¿ªçxLðeFÆßuW˜“ð½¢?‡8Þ Ë?ü°ÝÚ5¡ìTÖ²¦6wÑú¡#}u}Y˜ÿ†”ž#ÌéSÏË•¿ä¢¿¬8î Ó”…ìÆtþÍw{³‡œºÌ[1ý™[Ó¥O¯‡kÌ)hÿ°G¾ËÉòdö ˜DAç ]ú†äXËGalezpíû(D+ÂpM¢Ëâñ-hšGãÚ òßÊ v ºc®F,<ÎŒÛñ™ÊAšúäG×G1>¬Ã·E}&¾Ãó³×Gs}ô*múYЕA|þM@ò´Æ]ƒÊß¡ó3.ßßõa›õÌx²RJ)FŸ´‡Œÿoý÷ë‘ÄõÜå.7¾G0^¹UÄQªÛÑ1»‹/3½6±~V¸Ù¿ÿ¥è2YA/zœÇ߯ßuE#YÉÍBM·6û°}Ÿ/K3n1÷Yñ9AAÕ§Ž?4î˜8þ kf–ÓÛꆸp’^ÈìvȾù27}¯µìhp±¢kpcm¼w¨Öî±âøƒnðžáx"ŠbÝÖ9:Gtõ¤—í»~µ—ú±“Ûú^_êlO‚Ÿào_*qüAWozÀÚ¶¹QLcÇÑò4M¶ukw/ÆïŸæ“ÇÜØXNŠ-#: 2ÇtÜ…¸áî›Ì,qLµæ]ÎRÉЃ;ÊÞú±–-VŽH]`C¯¯wø|5_NU¾ÍêúŸÇt'§4tÔÍÊ[¸_81å<õÍ3]KÕý™òñÉŽƒ¬hͲª¸/¹˜7Ö*qïqSÐ…9í,v~ÍÆ<¿¾ÐÄå"ý8±æžÎ?–=oÖ“Ž[,¨¯iõˆ×OäÄÝ×Í·‰ý§U.Ùè8tù…&ø$z“ý›ª³3ý˜o|á8æ=Z»Ú—¦ êB‹ãºÙƒ|÷?œË›7¸ò¨ê}²È§Ãe?öåãx¥‹99ÊwFw˜Ž8¯ñ_Çt¦GWPbmÆæÛ°ßÜ݇ØAÝ>\ý˜¦þ/sZ_t}déMomèí×[Ð ]˜±zB,ý‡}éü‰ãgö.ócCVtëx³¾%u[ªîyTAn’SÄñÝÏ^ªõg‡©DŸ-?r©×»Å¼y~l¨¦­èÓ›¯é‹.*èÖ…š˜š‰ý™âtXsóÏíöH7ÕŸæœù¶™¹û]'ü¦þFL©ä1ã]—­I…þƒîmô<‡ô*¶èó‘Ý:2\£*ÒÍm_¹ÚÌ׆ä÷ÚöŠSwã¸ô^ЕA7wT?ÅÄ qìRa½3§LätdgÄ›«~¬$a¹¾éH)E¦5㎜´›ÛutZÕÊ%á7ëTÄž‹cb>£©ï;;Éïùý©?ÜDSp^A2—šòæ#t­Grú8&øO]¡c¶]©ù3MY×ÚR ^ãè¯ á¹CÐI ëô|Œ–|F<›"½u£ù«$Ø÷ìÏ„|nCÚ#ç;¬ I§LU‹ãºš°ñ,×j@ЗÈkd=h¯oË©þl! M=ReE‡BçÝ;좠ÙÓV÷Þ•'ötNÓùŒ"]š6Ϲ¡’ž˜ÛX-€®üóÐEG,©ßºoWÎ…NSWœ¿@§ù׋XïÙŸN­ŠURïºgÊòg6õµ¯îíoAG£Îoy/@W´qKzp«œ%ojåD§®ž9x»®?»ôiÉÈÙMÍiËè!K‡*¨Å㈴âüºQ_W â%²ä­&?îL§¶/oÖ7×Ñœ {æG›QÀènO;(ÈʹçÓ1âü¥z¹ä†á/c-Ù¹’]›¬îSäÇ}ïn^ôcwmk2ØÚœúà:yì›IœóøƒâótþúžÉý£YÏqÏ×}:B§»Ž,1ÚêÇ¢Œ6ÛWÇ’¶—Í ©£}ƒÄç=è4M’˜Îí¦H½¡tÓèRŸÍ[ü˜P¯ØšFΛ{l­¡‚¬5(âót^Z•Ø&1Á·+Œfé_ÜpÂóÔ.;Vº<“ŠëÁ º?tn§ûÄçè„õÂ$–ÑI¾ûXj-~n7ªs¥Ÿè_gGË4…©Ô³Š©ÅüÝèÞ¶IÚ'3»Y©Ãƒö†Ó·ýÅ—WÎðg÷Y’>x^Ð,)(Þ‘¯´ŠùºV#¦d6ûîÛîlbmTêé7 ðg·¯mGóìhý§“užõT0®ÄüÆ:6™rÖ‘´g…÷âAþìN‡þ¾ ã¥äÞ°åÌläiçÉÜáZì¿å’•#ûödwJ‰$-[ß©Ãk°Og:4?qÙŠÊûs#ù?~=è¬Ýê[¥°Œçíöî¹A1#RÜ^~ôg.Êaºö° ‘­Ûu»ž)ŸÅç=èZhi Ó¤•iQ¤]'?=,ÁŸ™¬]Pÿå3êjöe[žœç7-ª³Hì?èl bZì««f·zVP÷&I^žê×ÈÕŸ þ¿¦´ÈÉvH××r âF˜Âú‡tyKj,éf¢f›Œ›Ôðòâg‹õüY]Í‚Ø jËm[›+¨Vëj-ë :oèz4ï\¦£P³Ù|ûX4I½†4ؘæÇß1Ó?>žú“«]6MÖiTÐIkí¹âôQ-Æ— ÈÒZºÍ=~ÉAÍèRÑZç‰æ Rkóˆ&®—A7O Í&¤²±Ñ×V(ÛÆ’÷»/«”F~|úøjíëµ Ýëç÷zÂujÕ,—ènêG™Töký„Ýϟį–=§»›4>Ùg^¼q·¥·n â” Óƒn·‹*NeŸoq£4FÙA÷jÜÖõc_6k8§Ò’HlúðˆBôÇtM¿žÞXÚ9¹½˜¸p¤Što}ƒ#?úˆë-ÐeÕ_ßïà‡4¶°ÛÑ-Vqä]óå¢äQ~ÌRcømEûyù|äMïÁÜB\/ƒÎ5;ÞªÅÐ[¬d`YŸn·âÈñ|ªQ´÷Û÷ý·½ðTw¹l]MЕA—¤>6üÔú[,J'â âEŸÄÏÀôù÷Í©³ÿ„¦1”§—þjùkq½ºV¹äqïí' Ãn1â6¡§âéÇà){>óeÊ5cFMó0#ãaÆeç7+hÑàóç׫¡ÛÜ»i³ÜÊ[lÚÿÅÓçö}uý}Ù” /¾¹x˜ÒÁ¢có› žŒ<¼2)IØ÷‘@׬·ÏÑÛ½Ó™àß›@ÒÙ“w/ðefq?ÜêÔÆ}}äî™5ˆK1ºiûÖµ׫¡›µ8òJ¼u:»¾Q5Ê2-Ÿ_¦)çîdJ‹ª´¤ V[]Ƭ½"î7@‡dºÊàl:‹~á ¦A‰$Ý帱v‚;…Ù`‹‡fÄ]$UZ Ñ7VÜ/‚né-Kà ·Ó™ziëymÏ%RÇ© #.ú0ÁGÏ’ ûxÊü"§î./œ.<öÃTÐýºm†[1ƒÉF”[GVK"Ó…Ç®øû°ßþˆ ×Å•© uvÑﺾÅd°Ñã¤?σwzqÿ£--Kz8l÷I…¸î)üžZµË%«Z~ MX‘!úm%јfÆž›ðœªy¬ ¶'žõ ¢´É}Òz‘âz5t+º†>^º?ƒ ¾®Éô*¸fìä˾,ûÈž¹OžØÿuËpþ×õq t›Å  6éTzPt`2µ>v|ß’Z~ì²'ߨ³#ãûn!¾8ºtþöQ\¯†nÒÄs3ƒó2ØÜ:F{5O¡³•Õ\.OGžnÈ,¥tÔœ1Å?ö}Ü ë¬1JÍ`%;ü¢ÜRHõ#턞£ëYÛhðöÎÖôìýìIKÑ‚_£ØÐéÐyP«LV覽òñË*›ä¼,ßÖéܲn4¥¯%Mœbäëøý¿õtËo†>PÎd#Ãf.U“õ·.·gú±aKNéYÕ¼ÖÉ¥z Ÿ6 ,‚Bt— .&>™ÉÚÐ~¥ZM?®Î=ûº·ß?ܪxK…é<I¦ÇïµCô9­ƒụ̂ﭫL&K*3+–J‡ÆÝ¬Vß ûX´îç«àëÔ\¯žõô8Ñ/ºAš T&ë”™Pwz@*…µáF]¾Ì}÷âœèG–´ÁëZÖ»ä´[Ùfš‰è—µ]ÓèN‡2Y²-_iL£IM®ºt¯æÇ„õ[k:We7y%Ÿný¡Ó¶àŸ` ÝÞª½}GŸÉdå|9t_…2Þ~LXÇ“þñ±Ö7E¿è:uñŽoâ›É¿¢4Šüò"j©Û;kÄQS…-i©ë6›¢ ýæ«ü„ßÓº[F}(½ŠßSþ¡Y¦Ó-úèÞ§ïÖk~Œ»~aGÑK]IWÐCµ+[‰ýË„ÏWìC3ÅýŒ[tú~G?µŽ?KÙå5MnOÞi-î=Æ}¶×>loŒ8þx{J¬]˜N?«hØc£?ûí±ýåÇ×G>+%î㇊>SÚ˜G¬Ù4Åí˜*Û†÷MK'<µŸ÷ПMåöÒìi曇?"(ècÞüí‹ßK:Á¿+“¾>:¤«$ƒŽö¹y³§$€ U¯‹½hGyë·ÎÞñSAÜ•©c¾ØoÐ ë÷™L“žÃ3h³ßðj‡þø-œ°|C·Fà?|M¡³ÇÓ½~ÿW·_mÚ'“j½ÿÞèyJkΟ†ÆJi²Ö°­êÒ ›ån¾}¦ |#3Ùš-5½œI.g¿`¿ýøúvœ2¥q ýö?Ñôt'džÅíËdBþ¹MÏd‹בý¹¿ô–ô/lHõ¦BWˆþ´Ðuñu²Yûy·c>z›ÂNÏÛjÑZÆÎˆ}fcMLÙüºf µ;yVæwCô§…nCðÙÖ?¤™l‚óÛÊ 5²(¿E¡¶í@»µgÈɉf6d:|\íw/Tgï9´èïV·\ôÑÍdÚ %—.ŒÉ¢dÃâôÍæ2öKé7yîhÛ?÷—gE7è rÍv/“É,´µÞ”Eñ®Ñۆ˘& VØÓÒµí=SôûùCÓÐqWÍ®]3Ù·:G Ϣ୅럕³ß¾–…¯;xSÐÝÉN3¢úš ýݬ[—>®›É4Ûfo³¨—b«ßûrölű´æ«](Ž/Ké÷º–¦ÿ kp½ë†koß߸ŒÜç¹æ-¿xÂWÁë4ŸüÈ…4öbóÉ籤q˜£èÏfÙ(;ƒõ3í Õ žåÒÜ.9[íËxÀ…ši ¥©_u‹-5Ž‹>)нœÆW2ÏÎ{½ïPVg7IƒÖWØ…“¡ÛÖs&íökêî½§ C2ý»¸ º2è’¿­üöøx‹ãÛåEwh¶jùÅ+,²vøT¥¡¹ŒëT½æsU+O^°Uôõ®‡¼§›¾¢Óê vBõ¤Ý¨fÙ¤WökËæª+,Ì]¿»³›-Åá. Û£ zg«õS‹þ¦Ðº£~ÚbV³A81=›.²6A¦}®²Æù¿†§{Ú0”2ÅìÞ£é¢?&tã^wž>»_±4pôËÝÙäÒ®ýw[£«LöëÊ—·žÖÔîLpNãç ÚÿøÔ‚ñ— ý]½—Ñõ2Ø:!e6:ãµo˲«ÌÕ”ïpẆê[:<Þyñ…åEBÿA×ÏûjÀ“t—vibøûlš–×gÙº«Ö 2ºË5–•EqIûíHð_ ¤ß뵚þ«_.™]ºô»ã¬t6O1¡hIZ­qùš8mû56÷T§ÃkWÚÓ­b×¥óiÆŽa¾³· ý ÝšS‹v™cþØrÀKƒî?rho¬éÝàŠklOµi×ÌßÚSÇØéÍ;­ $“'W£å þ»èÂÚnI¯S3Õöžõ¡.EÔåO ×Ù›ò‹%µèÉ•Yû¤SKùDKï¦Ð¯lçv÷SwÿÒ„™ç’üXDS—Øëì÷}¬™ÎŸ ¤ß~ÊšþƒîEòÍ.˜W ÷w.mi·ª°s#%+æ_;ì(ëŽâÔš´w»Ö~á÷ô†®1ßæ9z‹iuŒ^g›K/Ì÷ÅÔ¤dÓèÁËü¶¢?p ¾>Ë„þƒnV­»ï¿ÛÞb6½»¼}—K×®Í3Û2CÉ.„ÔÚ•Ÿ`C#SeÒλ½./î—2èž/æ6·Ø¥.®éœGžiCo©–*YÔ”Õ{ﵦ—ü8¾ß¨¸Ÿ/î† ÷§Vƒr‰fZÑökÉz^™gœG5j\µ*0W²ßþáïVèi{\B¼®ý~¡z© Óƒ.yȘã?Þ§±Ö¹›OnΣŠÂ±Ý®Y(Ùù³C ž`E†Å+Þø’0/5ú:³¥cÓÓ˜÷¸¶Ù²˜s|Nû=ž+Éæ½aƒ'×…óŒè,êÚ[¼Ü•ÊîR­Ï»|êÔ}Ôw¦Á¬j_ŠÏ?ìéÞ¯L)Jõ8ÈÚRЙ6ü½™ÊVñðߺ€ê¯ýÑ£ë˜à?çxìlù 7ÒÃùÆ¢pnÐùßho×'•ÕL±º›+) 7)Ü<˜]P±+k¬ ½¹'ekË@ÒØ›-ú:£¯¡ûª™óäÎÊ42WëM?³à?ûp§®~œ8.4¶˜B¿« »™/@‘¥fÂ8+ #ùý2ë`æ“Á£ÕŸq$¬£Ïúº˜jÖ±;/ªÙ¾ T@¯}ê¦uq fC×`KXSè±›Ç?¤çÁ^…›—Nú¯Q¹ÄréÊ•óœÔ,ôè ÿTÀ^ ²Ùüg¼Ïžèññ¥ Èc›µ±ÐЕv9ød¸šÍßßþÐøäÙÃŒEžÁì·_áííµ[«)¾ÿÇ÷þ…ñ ná‹ÄÇ®µÔìãŽY«ßu.¤9Aù•Ñ臟=¶}>lGÛù1Ï@ºw¤¤ÿÅ+¸5…îNðëŽõ擄S›¶/ºdTH›j¾J<û"˜µ¬±6ÍÇž:´´›ñþp ée;¯¨“)æ?èî?|0ºÎ©ÆW]‚ i!œéÂÖï”^•®v ¾›1M(ú'‹ùº¬Iü K “ø¬Á©Ê¾xÇ;, a/VÞ·;é@ª“/ µI8O¹Dè?èø© ½öà¬ÿíF‘…4¬ÉåNî^!¬ÏÇ>Ú·8ÐÔÜúc2R Ùzv± +ƒn¨rþޤÉŒæw ©¡©Û §·CX]'_‡£¡öÔV§Óµsï}Ç·{ñp¨pZË%‡ŸÕÅ 8™H8½"£Z½éŸ>׿W ÃÓg¥MÒlÌÒ;Õþá,¡ÿ |¡“Xp¯ø“N‘ãôÓ¿/eÖ-ù5ýØû¡¤`{ x¾WŒŸÐ5~ÛxÉÆÁIln—ƒ>F‘®e÷+v¡Lë׉^³?YÓã;‡”2÷@Ò, 4œ+ôtšå…lj¬¿Ýç@­ûEd|JǼý–P6í‰É½ù¥”¶ÿÁs-è„óÂïR?þã‘ȆŸŽœ¯S£˜Œœù T(kÕWRb—oK1|; :Á'Wø~Z:è÷Ť#Y‹²›m{“ä¶Ò¼º:ôO<˾¢9IºÈT¡ÿ Ëi^ † ,¼ðUHÚôb²“·*˜[;Œië…í;þÍ‘ZñCâz¾‘ÐÐ2›™Õ}oSÕá'ŠñÜ®Ýëôœ0kÌ'ÌÎäýÔ ¤Ñnñä.Ä SèZ·*ꨟÀ4?ωb²y—1t˜ØŸóš|UÝ3 4áõ ð»¸A×~¥ºvvR< }´çþŒˆb²lùªÛç0v_·º›Åm' 褨´q ¤|󇵜„|ë ]ç”üA»,âÙQ>=-)¦÷‡=®>*œ³fì–[dþ¹}‚ÒÃ%&ÃîµÆ» º¨Ý×&xcùíŽ'ý(¦¹ScÕQNáÌõÎBë>vtd>7à $ïÖ‰uÖ}âutW›W¿‘x4޽ÅÖ—É¢’æG„3§æ‹ìõÃlÄsr ê§Ú!vº˜ÿš”Kt%UÆ—»Å±:ÙIþ'Ç•PÀØ>ŸÞ gÂya+ÒgÁófðö5A¶~bü„Î{X ¹A¸Ši¶Õ¬J(8Ò·quÏð?ë.ÂþÃ?㼺¯gÝÝÆ«˜÷ÀŸ[’w—PÊþ¬XÙñpÖâÇ¥Ó= ,iÛtûœÂådßë@|¿ýB~7…Îsø²–ÍÎ2ö¡…oëþ%äú~²9t‡"N.?{Ȋ漓2óÖÉÍñû« ãÈ ºš X¶krÛÉW’Jh]êñ_]O‡³íµ ¬Êܬihz–kgÜ×ܵ³[-±ÿ Ó¸¿GǰÚ#êªVüíË’€p&ì¿ØÐŠÓÏG`^“rº»‰ýÝi#Û¡G¢Ùc)9ïjÜ¥#›ä‡ëÇ„3ሔfjiÉfî˜,Î?¡k}=mwÚï}þ»d³ú‰ôba8»Ð€¿  ¥)ë~UP‚÷tŸÔ$!¯h5-—lºäöÊ41ŠI™à'¸K£íx†³ßë)Ö“o¾+躧¿¶ËFá~уn¨æ†¹Á̾.ï²ô.Ù~z¼2¦iã«.ãŽHiCg÷÷.Õi?Ægº=·z9ÔŽŒd~-§vXwWô§`ÏÇŽí¹ø† =_ofþî÷>œ˜ÿ Ë{&ÉŸÁ®”[¬ß~ì.þ:ÄÇ`xÄÿê‡kÛ l2d;ÒÿuZ?Aç³ÍŠ Â%­ïÒ%d±¤Ñ¬•…iû”ÆVtúLMg# >ÕbÿAw56ª—ñ0vnnª–qÒ]JÝ¿gÀá¡ÎOÚùŒh¯Þ¨ Z*×Ú ˆñº’™|'2”-h°°Žä.~Í Á"NK]eN;ªíjÜm±B<_/èÊ ö±BX“¤ò†ïïRµ-w¯Ô`²!#gÞ1£r;¾ã¡  uøÎ†Ç´š•K¶gúÎÈ̆„V+tÓ.¥zqµ;¼ gšc.Ef”»Õ¾ÇÔq 6täœ-OÅþƒî‚çþCÚ±>7M]2Ú—Òiù/s‡‚pVÑÄçiË(sZ³ÿ`##½Nçáþ”@—η™j(Ùr£gÖ (¥Ãêõ=צ†ÿ9úû¼…ŸïÞKy©‚κ ¹Ü‘û*Ká±R²ëqâö¨´pö®G¯¶ùó¬ÉŸbˆï7öLtÄHqþÝ6_÷®Ì÷P—ü‡sKéñ¸ýþ&ÅáÖÝžm9vëP ÷ÐïÞÐyÊø9›¹;°–ÒNoê_¨Bœàv½çmÉílÒÝôªa¬Áé^‚NòÊàžµJýÙ`¶V—’f:Þ!‚·â ׎ÖV|ˆn[_A4Æì‚® ºeÎgå:G|ÅçÙR2möhRɸVÜîéÐËmI3}~/×_Äç¿æå’µ«ßFŒ{™ù&Œ×¥C¥Dþ‹•_–E0á|± ÙVŸØÇ¼PNÇ[;÷Ž*Ì—ô k÷æðÁ¼'™fúàYJC¦wigÁ^1h`²ÊЬ?ø<8qCN7ÖdÎZ)Ìç%ÐÍøÍ¸¹_\ç+¥Ð :•‹ÑÂühñÙG“=rüŠ…ñ ‚îÕÛq#FíÚθ«m—›¥´½bÊéܬpæâ%õØ`Ju2ã]’Sâkþà"´WäËÍõ¹^.ì‡ÔÐÊ)¶”n·´o¶Cά§Çµ±É1#¾;$TNÂyqþÙ¢\"œÏMü¯š¨JiD»ê/΄³&«ø“˜EW¿s| \N¿÷©5ý]¯yÃZm+YMî7Žå g{ö¯üÙÝ’Ì Í¦Ý$§Ð§‡Ô½'>÷áïùêÆÑn;é ²LK´³¦[õ÷A·qAö~gýƒd}1Eÿvt)+¿öcòú÷}i{†XÑŠ'ܬ'××…u,7èt‰‘•^?F÷FÚMw¿QJ!G=^u‚Îo¯Õ°¢ãÏíýJFú=•Zþ „ûË:>ËnÑÊ“Žv¿WíBh)½-+uÛ½#œ%_~öÒÒÅ’>eYPY"£pïÏÉ:Z‚N]$?.åu–øÛͯ•Ò§ü`w8[ØÃFÿµ«™k6de´ì«§ïÀ$AW¦´¼©(óóÜG~¥$win΄õesʹ[ÏlÈ)í8¼ Ì×GÐiµ,—øÞmtnÐeþK)=TË ²ÂÙ>}ªcF‹Šå7îšÊÄ}&qÜAgĶ´7´ñ¥~ÜòX)m\1mëÆÎáL³;¸‚R;=|2o Œ*L›_üP¸O$Ð ›ÃWüiýî1&É{JiQ¢2ÁëCûr)Ûýô–åä±»Bǰ©Œl¯ü8ÿêŽ8î ‹òùõxq¨ŒæV?Ql(%é¸×[j%†1s|»cÇ–Óãȹ#L~зkÚ?#Ž ëXnÐúh´³¤XAÂ~})õi»ç‹g{{¡Æ­&M©U¸uïþ_(²Ãî³#…~÷†®î×ú£Í?\¡÷W¿8´¸”Ô{Œ9·.Œ)Î`¯êšŸhÉHxE¸ŸUÐÙiØ\§)Ñ£Î÷žZJgž-ïñÑ*Œ!¸·Ü½Ì’V¹G?Ù@F%~ ÷úmteÐÍÿrá¨ö!%•èòƒ`¥ÔbQòƒ¶aÌf󊥊lkÚ?¦£Ub-ñÕ®Á¦Â}­Õª\ò±Y¦SßãA$K¨±¹k)9:´}±wKkÔßÝhÆM)30Ü»$@Ü—tzÐÐ ¦û¾¶Æƒ£ž7ÞyýBóø`‹)¤-N¸¶ùTeåÇÅÝKÇtê%×¶: !á}Ä»´Hó‚R˜xNΖr{ó“§´mÏ Â÷3…náõÎ>5Ž…R¿Ç›ƒ>?¼K5>,ˆ””†1á=zMÛ@eŽ úÚâøƒ.äÝF „‘°O—VÅ4}uûy³ÛwòŽú©]YÐýû øÇú¥7tõlí±+œ4Ç3ƒîR»™³Íöçɲa~Ñ ÆPׯZÚ>×=¡sÿ8éªÁ–ÒW=}—ZÌÙ:çUN;4iÌu×çf´ïÕëjQýDÿvá:Ëx?DM4½â)®›Ü¥Ú#õ,CÃXݶ–÷¼eJÂ}@—¦˜O¹ñMºåíÅÛ;.¹Aqbw ¶¸KΙ}[; cö~ngrØ”ô7‡7jôÉŸº\xi=‰„q¤ÝÚŽn¹ÝgFѧúüDö]ÚÔ.Új´}«v·Ïò> ÌÈ±í¤±•Uþ$ìã‹óNè²Û'f6q“øÛBw{Þ%þVÛÿë¾^¶×njp£Ö÷Åy'týÚi§VïM]ørAƒ»®ëYËÉ0Œæ?Ó$ ¢‡ ¯<è@_ßð1qÝ :ƒîwVס¸5‹#ƾ*¡­_×<¸Ò7Œõß³êò²Ý–4ãýá¹kº¢½Ð”úÏ*…ûÌ[÷÷y0þÑ>½ÌrûVù0¤wK<²~Ò+ÚÕ3Íïmýâ§MbJ…}t)ægç Î¥KM–þºVBÂ:TDwtD–i¶ Küé÷9MÿAW=ǶÍ4£_²jÄá¦TŽ4Æ^i¶k­hPëÃOyûÿc}]«u¹dÇüïFÎPQA÷lt,!ÿŒÒ¹÷ ã«Õu¬h¶õÎéWùSÞu~FÐyZŠ;PE9³ o†M/!Ëþikôß;±$>Û=ºÔŸ„÷'…q$nˆ9Ï`q´ÍáXÇ·½K¨ÈôA׌Ža,?eüýÒ4sê®YØð'Ïãv˜&Ž?èº!ú½;G'ئɇê–ÐÜó¶í•øÂÝÖx&.2£™ÝjHZ ó§g–ò[}Äø ]TÉÇìæÕâis/~b²˜†:åïN|Êr§„GkjJé[V5׫çOþ™ƒ>uâµ7t³”¿F sŒÏÑÓ«ùmžv ×u—Óáµ+Û6,ó£9Åßæµîktå»êú,*ЧiZ?{ο\Lš×w„²ß×µë¼yž^ºEoPŒwÚ#®›A7=yÀ±Ñãèü¥ÈG3¶Óû.¶“ŒCÙšÛQ“nm_N|Ö]zË–¼2ëß\)|?­6å’¬·±Ù ¤Zf?nq1ÍmwÅ7ª[(“ßÐ7ãª)iާåùQñ“仺-§¿5¸ÿ5ë'ý­-ù·†Òb %þT¼'øïÆ?Û¨?«~•@¿:?3Žû dý›øÝVý›×Rr²–|nöÔ{+>ÇDûÀA^ªËç:hè àµæ9íƒJ ` ¼@ÐF2W @ÉÁxµ˜( 2 ‹Äa Ü T$)ð…ÿYOI *€’Œ ðj1á e@ ȸ¨HHRà ’“p‘ è#Y™/´‘¸$À(AÐC"3ùð»ýâéö¯ìyû·îöß¹‘›Öÿ|nôw^ôw^ô?ñ8ã"ö9ÿ]¸Ö¸Wãïâ»)iuþ~~ÛŸ[0 €´?ÐA5jÈÏ)A*~#~^:´ós¸n ùù\7ðj1ð kÂ÷SqÝÄÆÀ½)ß×Ãufi3¾¿„ö‚´Qs¾ÏöA%ÐoÁ×ÝÑ>ÈÚ-ùú/ÚJPÑ’¯C¢}àÔbp7.@¦Ë×eð¯ì{k¾>€ö‚¿xƒB ƒD`Ü@$¨úH ¦À dm$ pJPô4L€P‹ ĸÐÑÃç7 *>’‹)ðY@‰F\T=$àÔb2.@Ê€.’’1p*P ¤¤À$,#à"A%ÐG3ýð¹ýâßöïàsˇ—ÞÿŸÎþ΋þ®ýý¯¹Ñ¿ò¼ˆÇW±?ùwæÿŸ ðj10 «ÎßÅÿ×÷¶5Dt²ü ;þÓ¸7äg©ñ÷ÀTÚˆŸéÅõS£Æül)®T}~Æ× ²€v~Ö× ” ¢ ?ó…ë@-aCàdÍø´ l Ü›ó³h HK[ð³hè `µä{ÔhTýV|¯íƒ, ­Ë÷ìÐ>P‚ ]¾w„öP‹Þ¸(ºüÆÀ¨@0@"oPtŒ€ˆ•@IÂx, „!®@ Ê€®>¸¨H(Rà ’‹p‘ è#Ù˜/´‘x$À(AÐC"2@-&%Càd  è"Iw UÀIKúàiû¿ëÕöïàg‹Û毟­Ößy‘›ÖßyÑʼè_}͈u7±Ïø÷Ò¯ÆkeáYüZªóšM¸§TüøÙíüGè€T4àïÞA<€Z ¨†ÀÈñw‘pݰÆÀ½1'× p¥:üÝ |6ÐAð5jÂßÀuƒJ ß”ŸUÇuƒ, ÝŒŸ™Fû@ *šñ³»hxµ´ µàgÑ>‚¸1poÉÏÖ¡}`€ .mÅÏx¡} ƒo¤ËÏ¡}P ô[ó3/hdm pJPô L€P‹‰Á¸(ºHÆÀ¨@0@âoPtDŒ€ˆ@OŸ <€ðc\€ ”]$càT   I7(:HFFÀ D‚J äd ¼@ÐF¢’W @‰Ëxµ˜Ä Ë€Ÿíÿ®_Û¿‹Ÿ-Ÿ_ü'ý'¹iý'ý'ýkÌ“ ÅûM%^·þ´¯ó‰ß‘·… eT×›D¿‚ÊOÛ2 ƒ iÔ€×k€Tý†¼nt h7âï¯C” ¢× <€Z ¶†ÀÈtø{¥¸n_càÞ„¿ßˆëÆÒ¦ü=;\7ÐA`6jÆß÷Bû è7çï¡}´[ð÷`Ð>P‚Šü} ´<€Z è†ÀÈZñóéhÞ¸ëòsÒh àK[óóºhè ø7 *>’)ðY@‰A\T=$ àÔbÒ0.@Ê€.’ˆ1p*P ôõðÙÀ dm$ pJPôpL€P‹ÉǸ(ºHFÆÀ¨@0@r’oPt¨Œ€ˆ•@‰Ëx, $&®ÿ¾¶ÿ»þmÿ.¾¶­¿ó¤¿ó¤¿ó$•ÖßyÒ¿Ê<ÉH¼ŸÔâu «Æk”㳤Œ{u^+ßíßÀã¶è"h÷¼ÖtüL‚¨´!¯yÐA@5jÄkï@*~c^× ²€¶¯E‚ëJP¡ÃkbàºP‹Ø¸YS^#í#0÷fü]u´ ¨¥Íù;Óhè hµàïð¢}P ô[òwIÑ>ÈÚ­ø;h(AE+þnÚ@-{Càd­ù»FhÁ߸¨HRà ƒp‘ è#Q˜/´‘4$À(AÐC1@ ª€>xƒB ƒcÜ@$¨úH8¦À dm$ pJPôŒL€P‹‰É¸(ºHTÆÀ¨@0@â’oPtÄŒþC|n«€°xƒB ƒdlÜ@$¨ü7ò¹åùýï<éï<ÉMëï<éï<é_cžd"Þ/ü7åíJ€+P‚ þ·R&À¨Å€e\€¬÷›@{`ÆÀ½&÷=À÷hÒZ¼þ>úè ¸Õæuàq€J _‡×#Gû hkóúØh(A…6¯ÓŒöP‹AѸY=^·í#H÷ú¼~*>»>¯O ðj1€ kÈë5B‡€j Üñº¸n`€+mÌë×Ẃ­‘¯£†ë•@¿ ¯ç…ëY@»)¯+…ëJPÑ”×7BûÀ¨Å m\€¬9¯÷‚ö´{ ^íqiK^ít£V¼Ú•@_—×@û h·æï§£} @Áßxµ˜ 2 ‹Ä` Ü T$ )ð…@IøHP ô‘DLÈŠ!p2Pt‘`Œ;P*`€„#Þ è ù7 *>’‘)ðY@‰I\T=$*àÔbÒ2.@Ê€.’˜1p*P Ô¤À$8#à"A%ÐGÂ3^ h#ùI€+P‚  ‡dh<€ZLŒ†ÀÈ@ÐE¢4î@ª€§xƒB ƒ$jÜ@$¨úHª¦À dm$X pJPôpM€P‹É׸(ºHÆÆÀ¨@ÐGr6^ h#QK€+P‚  ‡Äm<€ZLâ†ÀÈ@ÐER7î@ª€’¼xƒB ƒ„oÜ@$¨ú˜˜/´1î<¤ü/ó$S­ÿÏg™Ôÿíï³þÛÿþ÷O¹P}ã£\RÊË è$Ò¦æ¼âÏ *oü~èÐâ?ÿ{ð£.º1uCÙW-Ÿ ÉóÂ…æô]þ6{†¹1íÍ Šú‰~O;ha§Ç=òÆ,ÿ±n Ú1›³yó¸u‰¤qmSï¦Å$Ô eËtŸM|µÕŠ´ª?¾üƒµÒDê#˜B'1˜|ä~I¢èGTD²ãñV CÙÉF¼À¹ ã6z¯üH¨ÿ(Ô›pƒnT±¡yß1I¹vجµê"Ú;U}e§>ÚÓøùÙÒdµä²X?Úä³À+¶@¬ûá®^C6y'‘î–Z¶É—‹hÔËKŒBÙÅUÓ[~îhKBv?ê& :tuÎŽ8²ÿWñ*©m·‘™±ÿróP–9'øÜ®lŠ–LÙ^ËÀ^½q8rÏE¬»ÝÆ™».O¦škVd:,,¢òçMB›9‡²þ÷¿*ïɬèáõÔ6uüÈȧz•X7¢m¹äu‹Í^1Éd±©oEÌ€"º¾cõ¥¶¡lxvkÝ ‹,è„}=Ëu-ýÄ:+‚Nº"^&§u ýÊJ?n_¯ˆ †TÎ eçõ”«¤fÄ«tìå÷:1èN-~3¬xe ¥4êµ²ý£BšÝ¿jpþ¡L¨{nJ³‹Ûn6èGB}YAg Ý—"õÚ¹i)da™ÚîÖÍBJÖ~÷£CíÐ?uǦk [ú‘à3!ÔÓpƒ.çã„‚ÝÕôó†Tíz¢ [~1þq/„mz;qS´)é¬ UtkïGùòw£»4ëåBW÷á¨sª5jª>ÉsØ^ûBjäÕçøòÇg•»aíâGu*[ëÍA·„—µNVSѳ:kŽ+$ÁÇ)„ >å¿·iß £üèÚ”ˆn«â„ºjeÐ]™³ÑßS7•4åÖZÒØuúó¶û†°!š‚¼VÔ_(Øýº&ZíÊ%ëFÎe“J Û¢¶WS@λëçÆÈCXïÝ-‡X“¦¬¡ ~ÕB½=èšj äS©æâ¨މä£õi]edêQÛPÚÝÍ]¢lüD-±^ur¾ñ5•ZNÿ>;÷tu9ê`Ù0#„½àßlh¹ôZŽÑ4?úíS¦é?èvíÒ_‘5.¬6ñt, _ަÑBXÏÃÃoÚФÓ®~nëG‡–74nc'Ö‹‡.ôÚUÞ{Ó(4½só%c (îéôÁCØ‘ u±þ´áà;_8B¯î·uBoè"&r£À4ràö½Í hEê×Uõ«‡²u_Ç.ó5¶&³G_×oyâKìÞ艄ßE]¾l¯tM[ô鎼÷ÅÇùä–â?-¬VèŸÌ²KúY=Ð^éëÇc†×ÆmtBý¢[äfz¸‹È§sƒyEïP¶”Ûet¶¤¼÷>øRÕß\;‰ý×¾\bêÚ-uõ–[bý·|ÚÁíýðý¸‹F¿¹4°£NÅôb_üIÅzñÐ u\owÏÔ]˜O›G×üú¬<„=Èð´õ7sâ.2§Ã|iRhû¹ë­ÄºIÐ}œÞÀ©g·Èu“NõwÝò©~I­á÷rBDd ʵXRûœŸ/iÊÿæ‹u“ {߉;ÿ¦¿Ko}È£öí^ÎhÂÆi ö[RúéWõ¶(}iÕì¾÷§Šãºé)çl^NK§'uxÅÀ<²Šþ¼ý#tB=!kjTÒÒj¨Ú—º·Ûx`ïg±îtÃó{í^—NÜÕÁóPQlÏÙgKCXFàˆÀZŸ¤´Gá5•ø’à#'Öƒ®û#ÝÔ—Ò©èT5›ž‹ò(áÀš­'>Æì)‹Ÿ8×l“T¬›Û‡gjOÌ k  gNÈ¥ÝKå6cõÃØÑ³joÞ*%ÛUjç¯ô¥Ù_Ç©7üót :÷Œ^‘A%±“|¬—K7 Ž×[3,ŒõðÞ§|ðÌŠ~×Ï»3ŠXót[S“wö]—AXøÖ¯·s¨–¡îD»!a¬/·ßÜ’ž‡Ô¸ÖÛÚ—ÔžÆqj‰ã:%·¯Ý—AWY7­_'rh±y½MÛpãÖO9×b¶ÍõŠ ê URÛZxÈy¸¬QÓØp.´$Á'Þ—, Qˆùºªïã\C3(õ¸¯}÷ûÙ4ìJÍ'›„‰¾VtA›'_ªhÕtûs±îtÜý|zl!éë”{g“é7§Wš‡±¬¼R«5Y.7»”ïKµdr+û-¢ß t?b·½˜˜Aa»Ÿú™g“Ò+Ÿf0Öÿsêl7})ñÑÔò“/ ¾Ç¢ß töŒ¸âr+ƒr¼¥T³iwh²w³qa,8:°ƒAc[ZVTؽ…m²ìRcÁƺSÏ<Ý#23ĺ‡wèæÑ„•½,Ÿ•µ?bKc4B?Šäå–O >9*èÞÞhö’A7|Å˘ªËwèÁ†™»ï cÜÌ7ÛÝ–F,ò.íäG«/¶«å¾Cð­)ƒÎvAx㠸μœµÅr|©ž<Ö?ŒI¿/?SÝ–ªö;Åø×ñ£q¼¬p+¡=-½rIÙ;“AÕ¸¯wîŒÙ×éå'ÖíƺÏ9“ñÈ@J¡Çãf¿{î+ÖÙýŠ Ëê}a£o\¥9ñʪY´Û9ÐîxnاM³‡”ûRÈéñsêwýn ;z§Ú”ˆ š¥1&Ê"ùÏs¥a÷Ãþø%ŽSQtå«/M]6ððÑ—bÿAWåÿ w|¤Œ8¹<‹š-ýfæ#ô·Ÿu°";½µ¿uóyµ÷Â8rƒN¨ëšAZ5ƺ5hŸE6³Ç΀®/s«kõ§ÿ5[¾c®pŸyC¶ÿ'ffd¨1NºMémj~ü0ŒÕÞÆ¼ú.²"Ó‘ºA³´üHc“¾_¬µmÀ“U;3h|Ç>ã‡Ý¦žã&Ø> c‚ï§5­ÑGùÒi‹ G›Šu¡‹^Ÿë¥³:ƒÞ=ñÁ 4“Ú68ð)Œi¦Á-¤´!]bõý›/YÚè>i½X÷±S¹dgnp•A½¥‹Í­gÒé!6ƒF· ÿãû¥)¬ëG ž¶±–Žç/Ð ¾â44pÌÂ%A4wôémf„³}Ý;ïq$g¡îk1‰ “@§^ƒ3Èy{ͤ#2hìÚ˜­ÙgÂÙy˜z¸m‚™mÙd?Y½ÞÇbÿA7ºOî™'í3H¶0m`bP:¹ ¯¡^[Î<“Ÿ<*v¡ýxEo?jÉË‘‡ˆãº/ŸE\¯‘Ae}¹qQ:í™d]þpy;2wMWÏ_.ô„ÛLáû >¯Â¼Ç:MùÝgé´&÷ÎÊ!Á·èôù‰oæ©"Øý½;1“r!MNÜ/‚ÿ‰Ðž ºIl¨‘žNWž÷mÞ¢ŽÝT_÷5dªe¼«“ø\åG¿}¸5ýÝÆÄŠ»¯¥“cyTÏÇai4¤ßú¸‘ó"ÙŒ§&º/ìÉtݬ-1,:Î¥„ö´:—K„<•Ný:Ü[m:<*-ïnØëÉßk[qÜùѼÕ7âŒ÷Šãºðëâ§¹¤ÓTý-Ê£Siת£úzG²ô‰›{XÇHiææ0CôCßìÄ‘ªãB\’@g®)PœNõͽíWHRéÀŽ^Û´oF²ßq(¯æìÏÕŒüHðqçŸÐí®ÃÓiÞ/½cRÔ”3¿ÅÚ±…‘L˜·ÙÒÁ]á禭ô#î‚×9Vì?è~¸¼ŽÕK§*>ãQÓþoƒ7ÿÉB6½SÛÛÑñŸËë5>ãG¿ý5ý]©ã:;>ºEãê]w-7…4ÓäÞ7Xèî°cOÚ£ÏοèGKê‡deÚ ßOÝ´¤0c·¨YýÍ#–§Puaô &ÌW¨(+ã§ä†}/×—4Oâ`t;¯[ô¸þb#UE2ŸÖ×ÏÕÿÛñô^B·G2·©w¾ ÏcŽËZm|𴺔Kînýb™är‹š–†`¦LëÞÙ8÷å ¶üƽ¡s;9Qþ‹N#jú“úù‚ÛSÞþdzÐiìÓ&Ý"‡±ouÒJ¦ÚŠ!;¯ Šú3ï™7üUÏá=üÅ|+øJ ;\|¿¨Ã-Jöëå¸òP-=VcÝ Ql=·—=åHkœ|ûf?%•K¯üt¦Ðlw]ò!4eU;$Q¿‚—ÇvDF±à¾5ðhì@#2ª¿jÔÓŸæÍ—êÊüÝ ÛÙŠ?€§ÑòËíMi]õq‡ÞDý™Ž2ÕÄÿþ€ÞЪËroŸM#u†K1”H“ßR~jw“ýþ»Ýüq¦º?ñ¿Ú?OÌÐéyù¤¼qL£À“ü(¿aÚóãn²WÖ“¨ÍL[ nâ^ð±¾?錊±ÏüË «ý¹F]I­<º)*Ê"„¸x“]«ì×êíy)”%®=ØÛŸ~×I×ô_×r‰Pg8,õn÷»ÿ5žJÇ}¶á&;#ir@wô·:ýžçiúºcJçM¤’PÏ6žj?7ñÄÍ?ókÁ¯ÉŸ¼ŠçÔÙ0ZÐI kGóÚ I¥œK9ïúÄ“Vö«’_á7Ù²‡êG'Ú‘¦L¦‰?ýö¿Ôôt˜Lhº#•–Z²‡Éqäðìff7™à£ä@V|K+,üiDõÈEÑ&¢ß"t›­î)^§R÷…AU3Ìâ¨Þ¦®ÎiÝ£ÙÀ'׺•ït"³ÍŸ„u%ѯº­¼|sÇT꥙HªèÔù¢ºû7F3#û#:­q¡c]¹Ã¡?Élö-Uì?è>/<òmÁ 5iÊ)_TѤšî=LË¢™ž§á;‰åJ|*ýé쯅ø‚âü:½y”š.T6=÷U¢"Ó-﬌cØ‹EK‡Ü^Iß÷YZ~Á_¬-úœv+—dråÝj²Òÿ0 Þ†ÈÃŽÏÉzÕíÐJª­)`ìOõú¾ø¤#Œ=èŒ>~¶t¶Zôÿ`äý&òOïX¦]{®èš ÝéÊWüé·ïЦÿ ;Ѳ_ ²µš¶}>ܦ™y,y:w¾ø}G,Ü»o—NTÿö´¤ähšœþò½îcáû™B×ðÞÂÇã§PTUɽ JøÕ Û”äXÆÝ_t Û‡í˦Ô TÃIÁ“e‚Î º_c»Ž?|5…jÅ=*¹Y;†ÆÔxž!ûËZîQ?{ñÅŽÖO[¥ºÖ=€Úwˆ¯²½+\§7t•~•ÞEkSHóx»4šú¿zìžÜ•1ÙõÞw°±£KÓ ?´l@;gœŸœôKø]TÝ~¯×¥Å ¿<È›4î†Ú«¥1ûãú¼KåÞ‹¿üIÊí0tEŸZè„:è)ÄŒ¸AøM:”ºwbÐvöÇ·Fðç ;û ú íiuÇüZóüšL}.>Þà²>Šj'8ùÍØÚu §.w¤S]øÈ øGÔƒ.;°ÚÔñ‡’i–fAìñU°ÕT¬çØm—&t¢{ã¾·@¿}"5ý]°Q³7Z³’Ið]¸Amëu0×W±iê:È(Τ³nÿüÔyå¿ÆË_VSè?õdšz'-Fu%’<‚¬Ø7[Ÿ›c³pg:áͲºL  Ð¹ÈÔ‚Î ºÕ;†ßœDEa1¥ÇÚER်«7©XÕ×Åžm.9Óý{C*¦9–|ƹ»o7t×Þ$Œw"‰ÚkW´0øm£ÊP±þqÜŸ:ç6žÎ[LÄuo±ÿ séî_?èkeÿ8·ã»C(·ª Â0Žmâ¶w¤Tªœol@~ž¹2»±ðýÜzü^çK <´w¯ ¡WµÛV¼Ç>¿ ÒYJ ¿šy2€‚ÚC)輡ü-¨^Ó’ÒkB¨Qб‰ÆqlßRjáLSkÎJ>¿>޽ÚʤìÅçèâ.FvMÄþÓÇóŸŽ"÷åöxæpµÓ`Úâg²ídsÓø 9’à›@1ÍÄþƒ®UtEo£AñT‡Û©ž "¿+'Ù€¨8¶Ó¿Û‚I]œi/_Ý\F›ó‡,nôAì?è’j¤ù/}G…áú!c»ÑÏ‘‡7>ŠcÛõ2gjd;¯Ì²­ŒŒO]ö®ÓºÕÓ;­<G7·Æ9†+éç¦Êb¿†ñ¬ùógˆHÎT#|g“o5e´/‡¤}Ú¡{ÿKÑeòˆ8šûtöÛm“•4|©ñ® ƒã™ç¨œó=œèÁíJë†÷è‘ýí>upoèZ—÷‹oþXES5 ×é岌içųjý9œøhOÕ{ïmY”@KµÚøMû%´§‚ŽÔÁ¦¨¨±éÑ”x†»üÑÖ.ž àË»l)üƒTÑã}½œÍÄþƒnXëù{VS‘9뮾JÓ«%F®‹ýÉm¨§v„ ½1P6ÔP쿞åù Ï•1ŒzÌZ4£ ÙUJžéZwæ–xv¿4­éy+íÐiº!;šêotnPM%'ó™½¹õ;sšÿcK<êÔõ–‰>ÅbÿA§±¿jMï<[~œòZFÏ6Ü]z)4þOª ·]<%£É{‹n… :tµf†oi|SŒ×2ª1{ìó´x&¬ãØQÛb-É.…[©M=%èÊ K8“vYy Šz·˜7ïÇÔªXÓ˜Í~Ͼl*ZWl'Ö/—ýã>ÓêU.IŸôô{ó”4(ÎåšÙÚ}ºÉðÂOñLðSµ£:a÷‹÷“‘‚Û•÷tzÐ%ôžráÅ·Hjs¿•§"Ô¦š®nt¡nÖléÉ¢)½c/ÉhÏñÁÛ¦Û : t.A—kçDÒáô.Û *}郥nR«&øDIE_25˜ª^T(öt ñôÒvE þɾ”©¼cŠ^ÓØl1kÑ_YFKº'¼Ý\{ÐÐш†“*8Û·ùrwÙÑ­a×Öwè¾äbEÍ_Ìûrᨌò^½ìÝ8[ì?èºÖ÷R]½FÏ_Zºœˆ¾L=t{¾Ó9iÒ¤±%ÿÄhe$ì ‰ýàÿ‚¼ }’:¤ÖeZy=}ÉÚö lVÅÇooXˆ> 2|«Äþƒî”7”%ùeדa3.‘y¾AEOü.g× ÈXsÝ‚¦û>›b&#Á—Fø~Z½Ë%ëgmBìܲ}¼)çùGÃöMØï}˜Üוe[eôyÍÚó³‚Nºq™ß~š“›µNÇ·©íS›$üñ‰ãn»k®ÉhËF‡[n!‚N]ÌÏ®cÖï¢×~†Î>–¥ÍkgŽëL˜Ì¦ æ4Ú6#_FRž)tÇç7q¨P’à7|žº­stŽ@?à!fEÍC¶ÔídÏ'z2zžõlð–bÿA§äÝ{ê:]æ+úç¨Á+É¸á ¬bzñ¼Ò.öô½•vuKèð0»ÌþØÐ õù¯ÒÜOï¾:C)’Šç…sØ€Ìû»ÆÕt Rjyy|™LôÚSA×(&)aS£+´2=¸ÇöŒÓT:ðÔÅzN ì÷ó³à‡*#µ¶Ì¥¦\ЕA·~&wªSРS—¦xQó>¶zo÷$°5Ë-¶ìoMšéát9-×LÌ…ß¡ º®7¾ïýPâI|Z[vî(é)F|<¦£`&C´Wß´¡Vï´|äÄG£‡¥Ð_Z}g—‡ŸŸt‚ºDÙ,óyìAÙv¯:¿õ d¾…-©´Û Ú·Q.®—‰ý ÝùíG=‡y‡_ò ·wëßÈ\e û=ò¯mG‚©œZî«kWÕRÐI »kTÔÙvØ~ú–‘¸¦Ó‚#”[|Äyjî5ö«n¿¹¦v4ijmƒµ&r|§Ä~†nëá#-mì¤%!'=¿Ï:LÛuºNÍ¥dÏé¤}3Äs·'#§_O’ö]_'ö3t]ƒ÷q%û ]Þ-›zˆžÌ©vngƒ ¶UQ¯ab°-u°ÑéRo9 Ó›‰ý Ýoÿ¾ÙÑiÅþ®Éõsø¬-ùAÌÉñÁy¡”~p;÷rÚ<ùPÝžbÿA'ú2ÁWûͶëù•W0³rîùô@Œ …Ïx)k)' áWqœòï§1ZÅü Þ¬ô¯{€¶<¸ôlNÓS»÷Ì0k²t}iÔ\N·¸=ý1Îö+—ؘ–[ÍèãÆâö4yä°ŸkŒŸB™Æîí‘ü–¶ót ÜÞ u>'ÆYè?š½LðißOו»“f‡²®¹ ê­è©ó¸NÕÉ)óÁâWfýÅ8 pžæ“)²$}Î'jô‘-c!AMß_ñµ¢Q>W§vþ†yª|W_Å< ]iN^ûŽÇD_õ}”¸Œ†ÿÙ¯å«àˆ—ޝ–¿ç9Ð qÿ$K¬]¿‡íOwÚu|kuøŸÙÝ›øBŽŒ®wç=1ÎBׯýê AýO3aÆzÖ-wÔ:ÁNðeæfvôàÙÉàrràÇPbÄ< Ý ¿üo‡Öc᧯Ë·‹dû3RÂ.™;ÐÅ4ÛY©ÃñûŬ*ó#þ¾sà<š]d¯rÞzmOs§ØïJ’çß`—µå%E hgIqüû9r*¼a;hæx±ßú—Kùñ¢—ØSgs£™Q,¼É¬uƒ|0_÷âÁ­ rªßíøÜ‘ báïóør ®[Ó@½ü ~‡÷—Uӻ͸ÉÜ ¹Á3É[m>s¼•œÖ9ÌÑã*tOjñ…_öuÂ…®Ëí£íÞ<›hÍz®~¨Â‰Î H¯[\.£ŠOÇ’¥íÅñݯ]×Nðgˆf³¥è¯´†½·aÜõÞg„#iÒt¼Œ"r£wAçݦË/âg°QJ¯ö¹³JÖD=ôjËò«oÂTÁžlåÑí„ÊHcó¥%輡³ŸÜhòB9›bµ¸öˆý¢o\,[¡I4¶Ôúú†ù6a˜×$ò%>q¼A—Áoä(X-Í…í§.›2{Œ f|–‡›÷SK%£dçz˜‚Šã º³£¬†,›u…yí^‡ y€6ÌLthf¨bQ—Oè\QØÙ0‡%'Šd¤±‹:*ÎK ~ï'^e|ï{éz½c½l©§Š ?÷%¯ž”Ѭµå4´öyÂót-4¿×Yl!OHéÀõ;‹¾ªX»Ú流ÚÒÍ—ç_’Óœ˜™Ïo‰Ïе™Use‡™JVMs éyË úV·ˆc¾?+¼²£@e번ë >ìµ¾°ToЕ͈x©óKÉÚØ5?–þüM‰røÇ<¿ê{ ¨eï@Kž…™ì““õ€ /‘CÅx ßtndÄOç‡ieÿ‘ý&ųwu¹££#U”šëžSµ»κ.ÆKèøi§Ç¦Ál§Ã¢Ø~ G¨PÑlODLÊ›’ãü0£NcW®®X7ˉMä7¶L¤}áBßü[¬“ÞŠqµöÚü™w,0ç+âÂ>)t‰/¾ÖºiÆÄu^_23øx>š9GqØ:ãÏ~°‰ÖÜÚ¤rÚÑþW­Zâ¾tF3»;µHT1a=VN£åcô 3ØŠFæ6õ‰¯öGlB«˜æ¼­Ÿ¸¯;¸\²ÁõööÇÚqìsZôü‚K Z=ÀìHùŒLvq?ÈàHÚ|ÅNNÒáYãÅ}]èø)°ïÆqŒ® ³Ûý,š=b?>)“ºi¯|üÒ‘|Ƥ¶ŠüpX}m…RÜW‚nõçácŸ‹c{o4à*5wY3bȨÛ,Þ®™ãAGz~ü®Y³Ó˜‡ŸÏõô;/\§)t‹t;\ÇF(Ûuª±áýXö,¹VÐm†Î9°+ÜÌæÇ|Ÿy\NÂ÷÷• kâ¯ï™Ü?žùtlžqæì©5'F'‹õ«n±¥Æq{ª³¼ïzërú}þDÓÐ çÇâ™æØ©§’ô† « ”ÅnÛmÝ}޵Óa£½Ž“W[>ögUÐ)絪˜Ïšjnì ò•_H§YLð;·¥ Ïü`§œÎ>ußì9GÜ×…îÁVUÚ ìKTƒ_ÏjÓϤΣ<³ØŒé­Ì?N–ÒÙvcžMÀuþ(:Òü¨™p^AkH¹ätƒ”÷­XÙä!/?'“×Ùæv•d± !µvå'ØÐÑsÓïör—SÍj×¼ôÎqèA76§ioŸ£ ,%wYQ­!4¢ÁXÛõïü9g{l}#+Ù~¹¸Ÿ!ž+„®U_I‰]~[«ÙÀ ¥I‡c›Ž¼Ãž®:ÒWJ¯òVrúɧ3gD?bèØûM?´Jd[Ëó}Kš†‘Æ~Ñé³Þ[µ·ïh;z1’éÝöOÿc7è¸ËäЉlϯ55jÝ #Û9õ¢b|ï°FÞ÷?;/w ñZ./‹VËé·?£¦ÿ Îq'2ïm¢¯‡Sÿ Šƒ÷îßaŽS&eÆ;Qä~ãb<,òépùŽx® º϶<²¿Èœ-;|Ù¹=‚BªmI9Ù1›9 T4xääB35àå4€† ¶ ÏB'Œ³$æ>i½Îȹ‘´7¥èvŒy¶èOº’º:qóö[9ý~ÕôßPÌëv«Ùn\ y³¿l[ï47~YÝ€lf{0v^Í•ôùŠ}h» 9 çq…ûEºú¯ÎYo_—ÄŽ?µ>¯Fµl5ùq÷çÙl§á§#ç»P—ÂÈŸË."e´;¶X_èŽu[Ýç"‰eÊù{y½¼kVÏæ;%Ç[êD‰Ïº.Y6SN!õÎVë§Λ˜B§»|Ê»¥÷“XÕ׬—Q7éÒ¶Ù—-rþä‡^ûÖŽy?-–¤î37èZŠ/Ù©“Ì%LZÄNGÓ¦Á3Nç°Á;½¸ÿÑ–vȾiùEÜÕÌE?bèçdñù5†–ȃ2Ç¥ç°Î÷ýr»=’þ™þW?vtv ÿ Mf.õO=¯¾<–jz'¦þÈaë›6ù91KJ:K6ÎCœü‰ç ý:Ë=•Ì7Y®Î…°6ÉjCCƒÜ?ë†^|y]‰qÛ^ûþÀ¡ÿ†•Kª~œmÕ#6™ñÕï6MT”¬œn™ËšëÕ³žgGß‚ùF)æYtÀÏ(VÐéAW](“Ù‡‹vtSQƒMCÎv>›ËJϬYÒ¹•åê̯-'Á¿w®ÐÐYïx°4¡A ;³¥ƒÏ¨÷*²Ù~G²77—µ0,ªýZâHÒd¹x_ø~¦ÐEYñ–¨õ£„—VqT°1ëÇ“ÆyÌ™][ωͺÖÓ£HNÂùDÁÝ ºQÜŽxv “ìîbäWGÕŒ“-™ž÷ç~¹ÒÁ ñéwrò¶ÐÞÐy°PÙ¬U)l9_0ЧŽI~ºçýy~¨lQºqFcÝåÛ>žÂyutKè‡MÆ‘VsѬ«±Qñä÷áíÚØÄõ»ÿÔWн#%ý/^Ú+ƒ®›ØcÚÕ&ì$>ó.‹ù™÷çùèShŠãCÔ:µÃ%k#Á7^˰\Ò¸~˜ÿ†”&¬³%PºÇšV«†æ3zR£úù)ö”9ìò¦|è4Ó¸m³…þƒnŽæ`s S{Ý<åÑ8‘ºêo=xÃ>ŸeÔL¸±§µ=Û:sìÖ¡ ›£]ÞtòL¡ÿ k¨9à—Âgu.ù²5‘.˜ÒöÇ…|–ìªÐ†ñ ·ÎŸ#Q°ž;Uè?è„vÔ¬FÝý-^'RÑH£Yù o¶ƒoHiXÛÞnOUˆý>Aè?è.¶wY6 ‹šÕ\›æÓÖ,‰Z>ؾttõVk¿I¢2AJü©°Ãt ûÆBÿA7¥ñøÀãƒÔìô”Üúc2’¨ŽÛî³°#‡.³µ%Ÿ¥mX%ÚSçEz ýÝ{­–_>ŽW3¾»0*™î÷ìW¸PZÀJJú¦V»iG§#ž˜7WAƒÜ¿¬êo ôtüܬyjÖ_s@2™ùëU X&_§héˆ'£,V(Ȳӈš‡kŽúo8žÇ”çÙ›©Yâr}Ó‘º)tÝ`Pç:%ÌHsƉæD}è?ÅJA±ü×YA§Ýâ¯ÏÈ^Í ¨^¶+…Þ\²²eÛB¶´m¯ä/g](ÁæE™©³‚0ˆÛØä úºSµøƒ‚š½®3ÊÀ­2…VDXL”®(d“†í™³i%õ«V0oŸ‚zÍÖj[É ¡ÿ öGÔìÞ•¸oŠ¥jê{H^Kâ_Èv:õG¨_I ýÍðH© GòÉmîæOúºÇÝílv«Ù‹öN{÷$©É0Z¯óW…lq£Æ%{V’uÓ;f­V}Á®Ðdo!NxCç´?eb˜»šiŽïôK¥q:ùaW±Sp'¹Õ¼ÖÉ¥z :öŒo„ã]ݧW];?€ûE+åÂhÏTªwM¶>`e«yUå‘wÕ‰á×nY,§Eš va<”A'ÄE5{Tz4¨ø{*YÜòŒö¿RÄÎ!:ÏÆsø3‡Nj¹¸ï9^è¿å)ÿ™ÍÓhÜÍ ã×<*b«ª|›Õõ·£šÊ²<ÅüCixîK^¡ßð÷Í¢¦=¼‚ï…›G§¦:œÎ¸«¿¶*fK6×s{ÛÀ޾4í…ŸHA‡^…yNÖaš~ƒNðßV³[ù_;_ê{‹\=TÍœ^üç|¦f9y™‚”ܶÚc€Fg ]Gñì6ú-ÅíåÇ×GnQÚñÅ÷êï(fVen§®™Ú‹þ篛C -¼itnÐ5ÒüW3Íò»[d¾b¡áëÅ, 3ßÈp Züø©£‚„8ÚA£ó†nmFùYG'5Æ»ÌN§fMsß¾/fÂ{ŠŽôýÛÁúuÆ)èó~ ¯³ÐoÐ ûjvYõtH:õõ´É£ {iÐýGLs'º¸:â…_OñYöùÊB¿A·bBÉ€µ ÕL3=lšA ü$Êö%¢oµ}±›¼òðµÐl4úmd¹d´fcýÐ<°¶™S¸ß|A/Y üåi¯#?x£ Ô”×Õ»Õ*ôtü4Ö›ájÆG]Ï´ ªsÿù›%JØ€Òã =©´6ßVPÞóÀÀêaB<’@Wûâ“ò‰ÝÕ¬}à‰ Ïºe’Ìx‡Ï¾vw™~O¥–*Ê8¸uð9uq«Ï¸ !¸ƒn©æ ˆši¾)ßžI¶©cº8̽Ë:$ ^iG·wÙ^û¬‚Nj6Â…qÝ¥»=ÞÿLaö ý>u.Î$·t]ƒc{ï2a¾‰8}ЭÞd 뽄þƒ®ùí¢êã^¤°žËøJÊmò³ò½7÷Æ]ÆO}ç-–þ‰»Îì•9R£SAw'øuÇúwRØÁ»|aý6u™]cÔ³»ÌÑ åjárû}À!…x~Ÿ4º2èª1:7<…YWÍf\v›ìÚ×W}lUÊ4Ç$–ÙPƒ[ëŸRý6ª\2u£îâóçR˜fzÒ&‹Î>Œ¯?¹”•kâgCš×uN*èFk'ÕÓ½c5íèA×UÐR˜°ÿ˜EÓ_Ýgº¾ôÏ9ìHjÕ°õ¾pªcdÚxaÜAg¢¨ïzÚ,…Y.]¹ržS½k}滩¢ôϾÿÑóAEG´­_›üÆ ãºš)Vws%)ìP¤Öਫ਼YôÊíÅþÖJY«‰77Xà@ ïž–ìðS°Ž:ZwÐÍo6¡´Ž^ ë8­c”wliO”¯þÖö{²pµ›éa'ª5¸ÉÐQ1 Ö¥ …~ƒNxï ™íú¸­ÑûÇY4z^ÝZK–Üc‹5íB7Ý<[MSPzxN¾»é(¡ß kÞ™Ÿ,JfËÌ'6¼C«ƒ½<]:ŸÓ'ëµ ½1á™âûnB—Aw}ý¶è…W“ÙŒ©c#<ß¡ùg¾möðޟ瓃Q|˜ÙÅN³®‡F§5º\RùoT%3Mº_|‡F$tè:¿Û}6g|[·†î.äXíÒÇJä;îþ¾eUWaÜAWãMyŽÅ’dÝÅ"ì˶;Tg|û‘V÷ÙË}×ê“¡3¥÷¬6 n+´àv!/K ;´…¿ •Ì–¶;yVæw‡–keµpù>c®/ê×Ö‘‚õ_^Æó‰mûù3tÖþ£é?èZ5¸vÃçWûѪ¿–‹ï5ý]¬õ ‡5™I¬Î÷Þ6oî݈·ªi—±—,sÆÙ•vT­ÔsPI …x®v¸ÐÐÅ•ìœq>‰=ÚÀgU³lÚ¹bðZiç2¦®§í¨èùª©’éÿ¼_TÐXds_šÄØÃm·˦j½û,³]Æ4ä´ìiò1½¥Õv+¨Ö¬N Ÿ/’ý‚":Hb©Z|BœMütê¬EeÌjx]¿þ»è÷zÊ&›ÐùS,…ñª5¦\Ï—û¾$²'»Þþ¸%›Îè‘0}]kôŦöÛíŽäó”`Sк}ϯn¿:PÐ ç-Ù×^|/›Ö{úÔïDk†§øÛ½(vš|Zò€ßó•·±šþƒ®]Xü‡˜=‰¬©*ãaR|65¯Ú0Î*¸ŒmnÔg²ZâDëìù ‚œ:~,èÝ'²£7¡»´Ýdèj£D¶p´i‡³iûN“we•± =¡IR*Ù¾óþf•ö]ëÆ· ¿5–þÖXrÓú[céo¥Kü©xOðß¶p‘ è#P™/´´$À(AÐC3@-4Càd  è"Àw UÀO ¼A!ÐAð3n T}CSಀ6£¸%¨z”&À¨Å ù×ÿíÿŽÿÛ÷6ù?á‡ë ÿÖXú?ZcÉÉY:Œ¯;¢} ó?¬±ôºåïxôwnôwnôwnôŸ77úWšñ8ã"ö7ÿM¸Ö¸¨RRà –p‘ è#€™/´Ì$À(AÐCp3@-:Càd  è₌;P*`€@(Þ è (7 *>‚¤)ðY=ßþ¯z¾ýw/“ÿž¸²®üýh蔌ºñw‚¡•@¿;7:´{ðw$¡JPу¿«‡ï <€ZLf†ÀÈzòw—ð]‘ÜŒ{/þ ® ÙI{ów9pÝ@‰Ï¨§íƒJ ß—ŸYGû h÷ãg§Ñ>P‚Š~üL/Ú@-&LCàdüŒ#ÚG5îøY;´ P¥ù™/´t\ñ³GhTýÁü ÚY@{?‹öT ágÐ>ðj1) Æ÷Hñ¹†|OŸ *þp¾7…ÏY@{ß+Áç%¨Á×ìñ¹ÿ?¨;ÉçAüžÇøÜ†ûvú;Oú;OÒú×›'ñ9ÒßùÑÎÚ®bŸòïÍÿ?àÔbp2.@Ê€.‚•1p*P ¼¤À2#à"A%ÐG`3^ h#ÈI€+P‚  ‡ g<€Z €†ÀÈ@ÐE@4î@ª€¤x‹¾¸ýÞþïù½ýw“ÿ©'®è"÷n¼† þ 1I»óZø{ ƒ$eÔƒ×tÀw•@_Ÿ×ÀwY@»'Çß(AEOþ®5¾#ðj1¹ ëÍß=EûHvÆÀ½·í$?i_þŽÚ:H„Fýø»^hTýþü$´²€¶í%¨0àïd }àÔb5.@6ŸQGûH¨ÆÀ}?+ö¬t0?³‹ö’­Ñ~víƒJ ?”ŸaDû hãgéÐ>P‚  ‹Äl Ü ù9&|60@¢–ççiðÙ@IÛh?ßÏ•@$?g€ëj1¡ Å÷zñÙHðÆÀ}4ßsÄg$|)ð…@É߸Èÿ6?â·÷ ü»Çöw~ä¦õ¯7?ú»ŽôŸ9OâcÝMì3þ½ôñïLÈâׂ %®@ *€‚– ðj1€ e@͸¨pRà ‚p‘ è#ø™/´%À(AÐC`4@-ICàd¢/î_¿·ÿ{~o<¡üW“ÿ©'®ˆ]yý;è€P‹IʸYw^ :$-càރץÂw¸±¥ú¼>¾+ÐAB3êÉëôà³A%ÐïÅëÅໂ, Ý›×-Áu%¨èÍëg }àÔb"4.@Ö—× @ûHŒÆÀ½_í$Jiþ5Ú:HšFü}^´*þþ^)ÚY@{ ¿í%¨Èß³CûÀ¨Ådk\€l0ïí#ù÷!üý´ Œ¥Cù{hèðµ£aü}´*‡ñ3ðølàÔbÒ6.@6œŸ Æg#‰÷üŒ*> ©KÈÚ£ø9A\7P‚ŠQü¼>xµ˜ü 2 ‹É€1pªÿ6_âs îøw¾ôw¾ä¦õw¾ôw¾ô¯1_2ï7•xÝøwRà y[RFÀ D‚J  e ¼@ÐF“W @ÍxµÜ 2 ‹`g Ü T?)ð…@иHP ôMÈÚ’à ”¢?î_ß·ÿ{¾oÚzÜ_í¥ÞÿÜ×½+¯g ÐïÆkøB²€vw^K: ÝyMSè€P‹ ̸™>¯ñˆïŠ„f Ü{òZƒø®À NÚ‹×¼Ãw:HvF½yí5\7¨ú}x 0´²€v_^c í%¨èËk¡}àÔb’4.@ׂ֟AûHšÆÀÝ€×$AûÀIT:€×Æ@û@ Õh ¯Ñ€öA%ÐÄk }´ówÖÑ>P‚ŠÁüÝi´<€ZLĆÀȆòwIÑ>³1pÆßiDû@߿LJÏY@{8Ÿ Ÿ ” b8¿ Ÿ <€ZLè†Àe$·× tàFñwpÝ èægîñÙ kôÿžÛ/I´ÿÀ¿ó¥¿ó%7­¿ó¥¿ó¥ù’‘x?©Åë2.@Êøg!Hw UÀAK ¼A!ÐA3n T}4Sಀ6‚›¸%¨zv&À¨ÅÀg\€ ”]BcàT   0J7(:’FÀ DŠ>¹úš¦À dmP pJPôPM€P‹ÁÕ¸(º¶ÆÀ¨@0@ð•oPtˆ€ˆ•@Ùx,  -®@ *€‚¶ ðj1€ e@ݸ¨ðRà ‚½p‘ è·á^›hdí¶Üóí%¨h˽Ñ>ðj1I kϽØÐ>’†1pïÀ=ÁÐ>0@‘väÞThèè¡}=î‘„öõþç¹@ ª€“´÷!€è Iuçõð¡•@¿¯ËŽï ²€¶>¯Žï ” BŸ×©Æw@-&7Càd½xÝ^\7’1pïÍëÇ¢}`€ä'íÃ똢} ƒDhÔ—×ÉDû è÷ãõÑ>ÈÚýyA´” ¢?¯g‡öP‹ Ô¸Ù^ß í#¡÷¼ÎÚH°ÒA¼ÞÚ:H¶FƒyÝ´*þ^ÿíƒ, =”×!Aû@ *†òzhxµ˜¤ €Ô×"Àg$m£áüx|6¨ú#ø;Úøl´Gòw…ñÙ@6’¿‹ëF‚7î£øûš¸n`€„/ÍßÄg$#à"A%ÐÇd€W“ñ"Áçö¿Î—xޝÐû{®ûï|éï|I¥õw¾ôÿÍ|éÿô\ÉD¼WøïÉÛ”W üo L€P‹Áʸ(º^ÆÀ¨@0@0“oPtØŒ€ˆ•@Îx,  '®@ *€‚  ðj1  e@Ò¸¨˜Rà ‚§p‘ è#˜š/´X%À(AÐC 5@-]Càd  è"w UÀAY ¼A!ÐA€6n T}lSಀ6‚·¸%¨zæ&À¨ÅÀn\€ ”]zcàT   ðKÛp¯p´tŒÚrÏj´*~;îŒöAÐnÏ=|Ñ>P‚  ‡„a<€ZL†ÀÈ:roM´¯‡ö»÷x„‰Åxµ˜d uæ~rÐ é÷.Ü× × „¤À d‰ ɸY71pïÎý| HXÒÜWßè yés|WP ô{rŸ |6ÈÚ½¸ß¾+P‚Š^Üw× <€ZLz†ÀÈúðúêhIи÷åu¾Ñ>0@R”öãõ¦Ñ>ÐA‚4êÏë £}P ô x=^´²€ö^í%¨Àë“¢}àÔbb5.@6ˆ×kDûH´ÆÀ}0¯ˆö¯t¯_‡ö’°ÑP^G íƒJ ?Œ×óBû h#A 3äµ”ðÙHØÆÀ}8¯éƒÏHàÒ¼Æ >è ™%¨Ék|àšP‹IÞ¸Ùh^󟋤o Ü T^)‚WMô…$Ì…ø?¦ZÿëŸ\þÓmL¹dG»ƒz‰ìwý|—ìÁúgêäü©§ß¾mwöüUû]W_ðWuøS__Sæ§âOý·ÒGîaU¹c$±þÚ)^ØÁ7@ úÆåPŸ‰ïNJê=`ó¾ßoÒBaO·›ZÖœgø»NP™¦^‚ ºG³:ìM`ÞßKGÚMÏ¡”½ƒw÷ìö€µÜ7h  ÿù»^V•FW]À©«'ŽK`ÔcërÌ¡»+7˜ó€­Õ¶KéZŸ;‹¬›(ÈíâÊ£îB=*—¬s¨ùpíx6bYËfgäPÏKŠªµ 0Õ&^ˆÞ†4v\Ïå$Ô¹êŽèA{ïÇÛåañL¨¯™Cªi'ô°k¼ÌkkÑWBNï×rƒ¢ :…Û¦ äÏkŒµr¨chBÍâXáÄ;5:î´"ýs¥‹»'Èégîü&Ôq1…Ž»tèw‹g=4…qrèí¤'GÉëÁŸúòB59eeQ\ÒþB=èô5…(ãØ¦]g:ÍožKÒ·1m¾]}Àfž©=ñ½–5qWöô[r±„: ÞÐ=¹2kx÷qŒW‘m=$—’×ëNY™ø€•7®}aSc²7 ¬y×iäS½G‰ª¯PÏ:^wè86{ïþ‡ós)o†îÎÑwüñýü˜å¢O€Pç© ºª³wå¾R1[µë‰©®¹4A»(Aöñû]/U¨£-§zÅå•Szu9´$è÷UœU±n“|:—¶~dæ­óÕuòu8jOß­»[ySNOç,ÿi™Ðžt¥ÝV.˜0CÅšhÊç’¦`Ÿ‡Lâ7õDŒŸ sÏß?4åw]ŽþBÿAg¼ú]çf•ŒñªhçŠsiTY‹v7¦Ç0¡zÕhw°sØ™‡LðoÒÐjøÊø=£Ï:â(ÖƒŽW3užæu˜u«ñÒ<ÚüìXËœ ‡¬º¦‘5 æß£Ï[ÕºÜÃjÎøošë,ƒ®it§CõÎE³‹Õ>ZçnΣÑõÇmè˜ò þö–tØ5ølë2št*=(:P¨·¥5¶\"øÖÞdÜ=úôù¦Ã×× ¬éä–…5-ù]/R¨ïe ]·cVÍoÝ`ÜnòÏY¯=Ì#™Ÿ/¤O£ó: K˜ñˆ¹EHv}ø¶hïÖGìûïÇe6´•—»\%#MÙ»ÙÂuj+—l¸Òºuy¿p¦)'z:ŸæÄWŸéwò£v]'|ÙaM¶TÙV þÝ4:=踛^¾KkÊ̶º‘O1Ö5¾ÄïÙºZ‹ÃzV´ùìÏ—“÷ËþqJ  <¦£e—+‹FæSgxê;öˆ=©é¸ªÊׂ¶q›Üµ2rÕ- uM¡K¬å=;êCstáN¤ùTCïø€GÙX‹ÇÙU»š¯Ú|z¢ìw\úº…ÛΟÂzï_¸¨¤yͰé±Ó§üÓ”sœmFß^´hy·±Œ²¿%\X$Ü/ÞÐì:÷ù×Ê`ö©®·î¢”>eçÐ1¡>¹)EkÏ=1íQu£|Ä$ZBÿA7åóêzïB‚Xü¾Š‚üY¤[´ËnxírÖÖꇵy”)]»ø3äIJñªƒˆDBÿA7ÛÇÃrÁ%ãî)ó HÙsÒtjUÎÎ%ê)WIÍH¨/ð;Î ý7¾\âs-ì³¼—’Å3nLS@‡[µsÕ³œ õÀÌ)õ‰bÃauñªP³VŽúº…a¾>íÒ¯±Ã4»Hé_@ÝRu¯Œ*g.uUä7ÕRô$¡ßð÷6cÒ«ìdCû^â (v÷ÚG¥³ËÙ½õ ŽüècM¾)o¿†PôÅx§=#…~ƒîùInàÈÒÆ]µ½W@»ç¬Ú¯€2çŸÛ•mC›—ct¯Ã.·Íu…ºsnÐ}}õB5¼‹‚ õ> È®Žãή?òéƒ_µjhK7ß-ºPg}(ÛÖ»(Hèooè¸ øÓƒ2¦™Þ4/¤+k²¸¥å“:{o?™³-õ1Ôr†MÀ?êø©Æÿö`&Üv¼_!-îÒ#ò‡'ÆÃÖ9¯rÞJɦqhõñ hBÀÓRc¡®^t›½bF:ú1ufIáK£BÊØ©ØÛÁ&ŸÆh&.6öm¾Û›¡b~O­ å{óí×+ò}Øäá^3ÜÌ éñ«edžçÓˆç‘mЬ(ÿgæ§Š†TMc¸8Nè7èk&b—¯jÛlc!E+¿Û8ŸfÏ ôxgA^SéÁKºÙÝwHíó¡ÿ ›òI{Èx…7›Ø¨É°€c…Ô)v™dó<Ò«TiÅï5'^ÅÝóž?Ùݵx6~˜ÐÐ þ‡XßëKÏÊ ©0¶¤Uï¤<±þ½M°ŸxöÑ]r7lÆÅ¾BÿA·öè¸EU_ϰ ›çk¨ éûú‹/åш GëV 1§9›7[‡ö¾;Îò(|ÝOè?èævK»7'Ó“ùèqÒBjÑ<00`Så6˺${˜Ü÷ÉS1_ ý ‚nÑÐÕFv—N°”ÆÜ¢F·<ÚÊ}^},)ؾz’%-°ºß"î—?Ý»Ê'’c…þƒîýÕÆ/->Ê4aG«ˆ®‹TÜ+> 6õø`kE¦¼Üt³ª2îèë§B]X­‰å’K;û=ÈZr 廊H™æó”_¹bý_kê¨1€ó§î0;Qè?è–Ž~¹»ú¶¼q÷¤”EdýJëôÓì\ú=ï쬨´q ñëÏ íI Ë:Óq›íìm,vzóNkG‘EôØÆ:¹tÉ&äùê0+ü&üI~wBߌ«B¿›B7éùè“9V±>š‚ÁEäÙºDÇ}s.iÂ~¼%||ïß¹ÅoŸåáBÿA÷þ‰ËÕOÊ…,ÿAE¯ÒeEô!sá¶óri ÆÃ‚Ž-Ï}½°š?%xO÷IMëÞB7¸¤ÅWÓÖKÈ-ç¦+9‘WÚÑ]ƒúåŠukÍÉÛ™–ö§†®]Þ«…ñ ‚N7¸AÇ&«Éäêô*¿­EtkFnöÝz¹4@SÚŒoxá>?ZÑ­ãÍú'„ߥ º«Z~ MØF‚`mÿ²ö­úY]çaq°)-q]Þkè“ßþïB]X­Iå’ÂÂnŠˆÚ{hÅ&ë)ngŠèÆ1×Ü÷©9´*~×++H¾oLG«D?Zå¼5aÂN¡==è&mý¡Ó¶ÏAºööôË‹E´-¹,ú¸"‡ä|œP°{…8üè|¯ûƒš^î3 tÕ5 G)k[ãå7C‹èþÝš»6íÏëš’÷Ýð7³·áû…¦ÔV)öt–ó_îõ]ºÀlªˆº7¸¡=NšC}µoi—aFþܾÙÙOô3â„t‚Ϧ'%ïqúT'­ˆÊ ÅËùS?}4·ûYãGÒ´™$; |?oèÞE:ßû :=!¦,§ˆäÝz3¸u?¢;:"ËŠÚö/íZ{»Å÷ç7Žp_« ;´yùÛÜ.ç©{âÒ9a¥ETívÖ/²éEÀ¯+_ÞÚÐM£K}6oñ£&«Z¸/ötïS÷?xŽ'–o£&mxò¸ˆx놾¸‘M‚/‰-%ž Y?i‰ŸX?U“Ë%õ=ç6p¹DÃ_ÆZ¸¾ÄýÒ<3õÉîlºx+·«ëx;Z¥1Úö£‰µÍ‘âøƒNSFþÎeš²¡]í÷E4ÁJú`áìlÚ¾noó]í¨ô…olŸW¾B¿áï;ë”’?È—4eÏ¿Qò’ósšwȦSŸ&„Ô´¥Kº™´ËôýÄ„ë3…îÊ‘½±7Oú‘숪ãÖŸET`ÌÏîPC—±á}VØ{ßÑgž§úR-•kí†?„z¾nÐm—Ødû?÷§Î˜;Ô(¦ ÚGSÝ!^=_çªÉÊ*w¸çúŠõŠ7tϯ/4q!iÂGíbº3¤ô{Ö©;ôb¨ìGþLKJ‰|øPv×—TA6·­ì„ûD5ù÷󺜜.Ÿz²@»˜ž}zµ­ÄéŽøùt~÷hŒ,_ü»ÆýÝÆU3¶§g*hм¤y_ëÓ>É™×'ßýU-¨§ÆÈWœ÷ y]˨\Ҡθ`·ºW¨ëë —êÓ»U[çu¼C†zôzÕ’4¶‡Ñ¾bÖþB¿A7pñäùÍ&\¥îY-“Ó?dÑï癥óÛ†ÅûŠõ|…|.ÁßÕàkd:j瞟¸¾µìÏÍHÉ¢ÜÞV­šØÐÑ^|Bè+úŒú ºnK|ñ:¥®j:u~÷ëvåìðÌŸ ¤ô:xÅú¨÷¾ÔÆÝjÐ^_±Î4t5×J‹Yºn­b½øIìë,Ò4™½ÓVJáS&Z¼zãK 4‚Ó…~ƒNø÷Jº¸ðèjÅÔøÓvZ84ëϸÑtOœ/í8¼36A§‚N¨gD§nMzú½ˆÂC™É®:YÔlʇû[“àÛæKâûíïPe$ôtíÓ»«”߃h†í¾ÕªŠHÿèá蘰ÛÔdÿUoÛ}Vtº_âó#¾ÿßZSOô6<5ÞL£.[»úm9O8÷¾Êü6q—Õæt=hÚ<熾ÿßzÐ þâ!dÓsÑÌqÏŠè„=7˜»-Ö?7'Í4¬©/y®9±³¨¦pè¾]8wñ¶:¦tîÑäò+ž²ÛeeÒžk¼¿Ö1£yGG^íëûÏ:ïÐU6«“õæ[q7IAí?]XÐâp&©UÜ~NSÚ 1L÷¥.^ZO"Aç]Çf=3ž¬ ¥CãôsÈ(¢å«Ž«>+“šj jVÐP<–Ž…®ïɈ.ç„~÷†nFÓii¥OCÉ!loŒ1âóøhv@['“ÞÞ|vªýôåi«ný}©è87ÚÆ« :½ò¼… ÃhTÜÏw‘ÌëÜŸ(ÏÌ Ã¨©ë ŠL©Ÿ 2ó%îöÔ±‘0^Ë Ó¥I›¸ä0Z°}qƒþEÔv¼_ø÷ üÍéø‘ÒZSëùŠó3!ÿhM-—L;ãw®8iÂgqWÔæ#3DPKº–¿ïÖ[è„ç'A§ÝÂZv\½Â)yÖ£{ß÷Qî›ø|‡ÇéT›Û‹m¶&°®/mòYà[ Î7¡kÓªÑ.Ëj´ûÄÅyßÖÑÒ%9­ÎïM§j±%Ñ+çHéaíÍ[u;!®„]ÎXé$ü.¦ÐµŒûµ Øsü·ç½>šQ§3ýìšNÍÌrz[ÙRµ½Æ~Ö}ÅúÃÂýâ]Sû·=µ’#èñYG§é³Šhe®jbiÌ-ªZ9xò‚­¶b=y¢ŸÙ—ǤÎúº3ô'¨:ERð¤Ñ|0¯J9ÕÃŒE·hëì?Ç‘R2_¦9ãC‹4´‚ º wt;º%’žõà ~Èç»çè™|M£éškÒ<ιøˆÏCÓ„þƒn_è!n=kÔñýç|º_A:í«OŽ%ËŽ–‹ Þæ“|A‹¬¯SiâÇ|Õ‚ºÖÔâÊÊ¡í«ù’cyTÏÇaBœ—@'Ôƒ¥éûÌRgæÓ™7K·W&Pä¨üƒ VÙwi~ôɇ:^¾Ó>1SœwBç’ÑþGÿ‚Xr° Z‘O[ßÅmN8œ ®ÃÙÒ°‡©NÖ|þ‘oÝ »ÿ­mèÂŽŒâÊ' Û“O[RíÓ¢º&P·e|ÄÚÿiO˜Ï:oè¼öuöu²a¤§12ȧ×Ãç[\ Š'¡n¹£è{ëCk^Õ¯Ø=Rˆg*èŒ[õ<±û:£ ~M}•O›Æ­l=,žÆiŒ—œé 7(ò¡þÆsL«MŸ"ôt›šÏŒîœ8¿Õ¸U>-5}>®Ndå¥/®gôÍ™øê¸W•è/%Ž¿™˜‡ðtÜQEçì½.½Í£&ÏšÚ®3Œ£ÓÑÕ¬cw:“¼)w$ö¡û«-6Æ‹>CÐu:ñDE>ÃßÝ‚î´ìÄ¡%•K¯üp¤c5ÔÓÏõ!¾Úë6AôšùÛEEouÒÇûçQ½šÓØá)*Ú¢1H±§ºcAz<±{Y‘О)t1ó .9¦"î^{Ú-ôÝ[¤ßdtÞªå¥Âzvt2ʲ]w¹ÝbqüAwú\qkÿpq÷´I‹ò¨Å§ŸÆ+ciI—+]žI銺­ëKzYÚñÍüÞ¢Ï t?'tª*UÑzM Í£îßçs¯XÑHJ£{oS¬åKþÛ;æ)ï :toÏÆO­GMgx»OlGþšÆ4ùãã÷;No·A/ØHЕAwF›€8*ÙÿÃêsy.uÔ›Ùòê±hÑ_ÆŽÆ«7üêØÃW\÷}¢fas£‘8Z§YÍ¥a¦“lfEÞ¤Ž|Ù°…#]××£}Éùmå…·DŸ!è,—ÝTÃ5ޏkÁœS¹tþÕ:óMȪšN¥sE+_22þ®»º@hOàÿGÅ«OŸ©éœK{_ÐÍ)(þ6¯E0ãç®û:¿§·A­)øÂiã:5þ ñ”_Þ3ðÙF\§ü¥©íîPºiVr9½¶- þþÔ{WžI|±àK¥]Ä«M¹+®ÇÓU÷3ïgçСn³sÇćа¡#çlyjC/'a$"~j¨˜ÿ {]óÛõÕ9ñÔâñCwèØFÅ…_~¯{8Ñ…ß\^nº@?‘þ_ìÝixRçº7ð8c­)Ö gœQÛŠÖ*ZõÆ:ጶ*Ö £&dÆ:„ZœQkűÔ8à°ŒIP£ q¤­UœÑ$†Ô‰bÞÿb­xöþðî}ö¹ÎuNϵóá÷¥Í-Ã÷ÿYk=¬çpË=ÍnLù™:î½»ê\óôi'öÌM %ú¹+%Ñì.ujUžÊÕ9PÇ:iEnõ½Ï¦ï§m4m;¿?W5|Ý 2‰_/ÁÕyQ7ª¸ášÇ#œüqóÏäí1¼yÏÌmTºÏåÌà€'ÑtáŒ#)-ùï߈|yžˆÝ¸ÍImñíÛqßM}Þ;W9w3U<[££1"‚Ÿ·%QþÒÛCÒpßw1ê‚§¡œTnØ×¶½YnZ—Ù¸ÁŸ(»¿s½bG50'ÑxËšg®OâÆu]^Å›ºÌIìnÁ_'ºIRE¥Ì÷OÔÅÕ|´áÛ=NR“³rµÖnê¶·ö‡¿…¬x7ï1e- ¿r"‰ßg…ãö7v’½»Á‰›êÜ*hv²æ"z8¯¶÷øùhšÏN˯'QážüÉ ¸ý½¨»?ívÔ›“L1Ý=aéiI­'/z ÖÑ·Æ­Í–¢é"{éY}<Ñ=…¿/óå¯d›š·³;iôwžj•b.Ò“îã¦N%õþ¾ÕÜiQ•1c¨b2…ŸY>‹ÛßTŒºàeÌ,'m}øsƒGÍ/RÓŽÏã™6½í£­«t‡#‰Ûÿ2™îº1÷‡ÕÜ~•rÔL<ÛîÊõ djÚÇ7êd¬=˃5ôIp!F½f¿îKøý-ñ÷ܼÉÉçÍR&UÕëàlûŽV/笯Au¯^xñeÞìÃë•㞟u£«ÖYö)^×ÅZÝt~;úzÈ¢× ß­WÐêzõ‰º–DÍ3zÊ•pï£u’l)ŽãuýnL¼Sù5ÜÖÂö Úðëi>˜ôªÓ£$ò\úö«W·¹Ï¥uý¾‹R'±»3ÕÈ:Oa<œ\ɾÊ^º/l¨=1îjÓdšÈ._¨Åꌃ؉Œ“šž©{Ĩ?ÏïÏ»Æ~m{¢w*±»ÁlŸLOÒ~‰>|« ù Ÿ“Å“kñùú¶Â¹9Uè<=nµ¿ÞÅQëíKÚüPá@8qûj%Ó¸úIƒÞòûË¢îÞ˜íNâóüsw}ßy¯ÏÑßÊé+úühÏ©Ùkó²š&h4£ê²dþü ·ªu {ÚðG'-îgöúø9Š®ºµðé¤MvîúN$mmÍ.(Jæ÷«ç>_jÔ-Æ®˜qÒÔàîÅímò|˱-öÒÏsç„§öq+“‰Ûwšw=ê‚ÛÍ/pÒ‰†³nwè~Ž2C÷M|ɘíí†¦Ö ßK7õ~•½1™~`—ñqufÔë¾7ÝI[Ž.[üøõYú`]mMÃÕ;ìÜ>¾ñÔ4Ýy8™ü™Ñ?vnÈÕ9PW‰uô±I+¾‰|•u–ØÕ…KvÙKsá­/±SõÉ4öPÛ¬!ý¸Ï™uc«²+†œôEëšǟ¥*‡«J?šŸl箫ÅÓOìö³“ià…å=ެæ÷™/çæqNj¼q–BÇÕÍØ§UèIß ãh•:ïeÂÜdê¸'ÁÖ¨>÷<Ũ«ÒªËнŸ9i¥³âgsª¥¾Y#lßöL±ß ùôvæ°ê^çú®“é- 5I7q'G­¢wÛˆ–N:Ýam/Ο¡¶Õjÿâ~¶Ç¾·ûë—{£(¸­d- µÝgoò–?Ô…Ö¬ŽˆvRûŽåÐ’ÎÐÚ…ã+Ô0í³·IØ–·óT$ï~4Yn¡Û7Ï|¸e*?~¨ãòÆIÂà‚¾3v¶{…NìY%—,Ф÷«ü1ÅB£^|úád~nÔ­ÚÌ^Ȧ_·½VÔ¨w†>Ï Ô‹µÚë,uÝ/‰¢‡ï9öw±…¢~ZóýBnjê‚§ñofÓ‘î<ºp-—2^´ê>zàA;;{Ü”CƱ ’ùã>~nÔIž´(Ú”›MzÌ‹ÿu[.u 9=¾Ó!;¾¤kÏýK!o×·þ<™6¤ùÃÆp¯/dT¾üÙæ§'ÿHÍæ÷}Î%»<@rØN£Ç´?ˆ£›ì6jõ-´ߢ𸾉ºàéµÙÔµ£F£›çÒ¥iýn-jzľۼã÷ÑÝâùy¤…^¶¼»¯8žÛWXŽºY\°àx›½ºZ«ÀE]Ò¿\\P/Õ>|û±£ëqTboÞö«.šøøîÀ×ÜëS£ŽM¿™M/õì U™ò^‡'ÕÓ콦¬iu«\ïé*—ö·PËŠ{~/Hà÷WGÝ›#÷„NÉ&öì¶Qã¢W]©S’fß–#¹˜C[uê¿Æç¥ó½™Î¦Ü>âfÔµ–n¹«Ì¦ÚÁ]tòY劷¯¦Û-¹ß§o˜Mû§äôO°üÝ>ðÔÅ$|àžÛ5›âœënžóåPíGN>Üo³‹‰ŸXåB$Í;Uÿ“o†YÈ—6yBÿnüPwèä‚ÜUÍ0´pEJå]ø¼bŸ„£ö¨íS¾‰ŠÐÐËMìDÉB˜D§›¶q2:_\ÖšMå¢Û_52‡ª,yQí£ÎÇì¥óvÕáƒvú²ÇOóŸáÞ1êš, W¯0³ky‚¾ui‚ ÄŽÙ¯Um¤Ûáôã›–»w°ðû¯òû;£nªëà³÷ïdQ›ÙÞÉÖ»§ivöÜüË»ŽÛK÷sæÖõX(xx´˜ß_u)´xÜðL-8¼þ£ÖÛOÓWSÛì¼>.ÃÎ]‡ ~ï³3u u¸p{ñ¹÷Eº­?üÙá,Ú3&bÎÄq§ió›þå^×;a¯úzð‰¸ûcýíÛv[è!» ð®ÎŒº*ÕZG¾1eÑwíOjV×=M ®Ÿ~9éö‰wûËŽžð„JŽcü‚ Ab¹ñCÝà?…….Ì¢A•j´ÿrŠGUØÃzÒÞÇž6ïåÅ(švæÆœæÇ,´}@Ø€£òã‡:v¶Õ8:‹ê_îõxÕ) \ïÑó{»}f£”6¹Ýbè+&î“7iZæ`7’ã/D•/ß=„½B”E}ô\ÓzÀ)ÑúÓ®{éñ÷üë5[¤áyv . äêĨ ïp°ÄÖ9‹¾/î²]Xî‰kì¿þç‡ýã#Ó7UºK/¯îÚ-ÔÃT«É6#W'G]pYA“,—ûÃ*yò,EýÕˆÖ™ö±'µ¯e%®Xèi<»²…«S£.û#ö΢7ÕØÞÄîöY~s¦Ý™vyåöjTØù΄Ÿ,¼¬ü,Ž?Ôqû‹g·.ÀIÑîî-¿¯–eG¨lË¦Ü kúnÒY¨ï¶Õï–×rã‡:L"~.8ŸI«üÏÅó³i˜3ç\ÊŒ,û’¢ïªgN‹¤¼øï:gYH­c¯Åsㇺ"¡ìC5“.…ù¥³©æ–‘Ö+YöÕŸ5_t£¯†¸ýÉ-tGéÚóÜëó¢.ä”km×™4+¸¡f6 vÈÖ¤tɶ—Gk¬»‹™€…†ÎIÝ)ëÏߘ|ùÞ;Í^̘™I'ë²+²i³jãÖm¦ìwûÕoÿbT‹V •ö·àø¡îΤk]^ŒÎ$nŸä,Š>õpió7ÙöÒóÖ Ã“J·`H? í—çzqï‹uµï®Ñ}Û-“œ¦§/Fè²èòÅç½s"vnh4—+7b¨nZx»ö%Üû¢F]ÃàBçL ¿ÝGšE­;Ï6–\wÚKO£êhÒ}q€–_ä^Ÿu®û™»íÂnLŸIÜ~ݧì—ë|Ø÷Hì»u…ýó¯?6…ÿþ¡n† aÕNÍm~,büÎLj‘ÞêæF×);wK ýâüœ¡.! „M÷suÔ¯YÿTzÄAMÂÛ_:6“–~ÓtGÓvn½E uK8>aS3†Š¦ì~6o'?~¨«3hÍœ–F…{$÷»Ô̤u'SmÑûOÛ,¸ßñ•%š‚ÛÖc(gsăA¹qù:_Φ{ýuÞ?ÿJÄ]´e͈ú9ö­’Ì×EQÿ}Ò¹š2Tº¿gpüPÇí;î Ê‹/gÿ¸ÜAÝUØô9öÒó‰‡ÙíVQÇ®.ŸØ’9ê¾ÊKJtŠ$m»»¦»ƒ,mýîNŽýØŽõ½)Äõ5†??ËênM[ñCþ;÷¼C´´÷™”Éþ"bWD¤‡ÓEÕš¾¶ðë¸:=ê*Ž ýøÐ;Y¿Ñ¬y8ÇN쨗\-¢º¾vŠOÃ釄þ§£Š-r§÷òݹ×gFÝçÁ °ÔÁÄOÒ°ñ%]Ì.¢?ÏÙöÍàp~}ˆ…n9þð‹é|ÿD]%{õŠOÒé€ô»«³Nró•}EÔðQs|Â)xšã…JûTpüPúÅ2òé²ð8bwmÏèÁdS«ëß?Q·3¡­wkáqª0þÏò³ï£Ç´êòÞ >úíÈÊÈäïbi-‡])JáŽ17®ãú’uK(;ñ3ÝqbŸýVÝ1Šy¼?ôúAß»<¿ÿ¶á½³Õú£mtâÝiÓ¸ñ‡y$óe㛂㔹› £ž•qd´ÑGOß°ÿ(êÙ²ÐÙøæ=Ùs¾-÷œ«£î1Ó¡é½µÇèòiOÕö™Gi]ÞÐ"õ\ÅTiþ¼ªYC}ofÄa õ|°¤|Ú"®NŽºÒÇy•2Žy}”¶®[4oÐT•îOü4³cç¨ûckÌ ¸×§FݼŽqQæ£Ôtµ·ß™:G)ÿ½õG·ñÑž^¦ë'nXhÑ-ýO½åû&þ¾äÄùÏ›¥Ðëu¦vvÙ(¢üê£s>óQÛà n ¦S‡¡ÒÜ ŽêŽ½ÐæN³ØˆÝí¶‰ÞF¨<úèòî'=š×ˆ¢7mlV2¡c¬Ãꢢ®~=±…6Û«.Èl´êÖÄbu9]Ñtu÷ަž÷ë\e6Cóƒ’7Ô—çoO§§ö §“;~èõ™…Ôô÷ž!»‡Äв¡43<[“« Ÿ/×û·÷Ô8Êÿy³rj:[÷´¨ž[ȯ£Ž¡)=R_þžÂÐ@všÔÿÞ¡N:ù½Ë²Ä4þúM:=|©žqio!ý4(%-ÑMÏ™»oÐ*†Jƒã†:w({¡2 ØŸQôM§-íŸO˜³¦ß;ŠJŠc3GŒgˆ;ïËÏ[P×rìø¡U ©T»B‹Mòéµx^ò„„ÂwÇkñ ã7õDõž–“øª?~¨cÏvE¾9BÛÖp›F[sw:, ény·düËÚŠÙææª-»Zù‘œ?ÔM9d{ëšq„ºZd¡iÔwι½û’ÙñyÉSO8Vèq¸Åß»uO6îi’˜˜~ëÎ&iuï½°¸XZHQË7üì*œJ½Þ¿Û¹>C¥¹?ÔõÏSÚ«=L­?½5jvl½zZ”5¦Aá»ó\ûŠ|×ê2ïúSpü&äË/çôÆ¡î!úu˜ìxêà4ºeþQ©˜ä÷µ3¦¾Ë¯K›3\_ñÇ ¨›9ýô½Ñüöiô~hÁËý÷h&{¹TNÜïo:ù1»37Ü€ºŠ*v…åAê¼FÕ'Ýl¼zïÝz³„Á77Œ—24àj³È./5Üø¡Ž;>9Hû/¯‘J QIÑ™÷HÈ.c.Ö§:ûcˆ]¥àÇ=žuÁíÊ·XiÎüEÏÏK%Mâ¬ï—ZîQîš ×Ù(V>qº0Ccê ~5üÜŽ:ÓŸùy-+§]îTœàÞã×—ÅÐwš&í± õh}"~ÎC®Î:[¹mMú ЭîìYRiæùüÄØ¸{¤™zü±(Ž.w›Öu†În­Ø¹ÐË·£î…ꥻҳ}ÄþÊ®AZ*Õºw·VÖ°{T?{³yHhø3‚ ÉTðë䱕7§’nÇü›} ”´kÙößr#©jýO—Ïδ¼;ÿ/Ôqç¿’èù¦ÆµÖã}ÈÎì:¯B\Õ nxEúÐâ‚J ]} x¶e"~Zã®Ã_«W/ßEU®žkº;•ª¤Þ¾¶|D)ƒ ›b¨­*/¦[y†ÿÝžu¥ëÎç®ìÀ8·e/\~V@ÁŸ¯|GKe—OÅvbh˜ïYë?Žòç9Q—ÖaVÖ íºeìWi(>W9Ï^[§€ß/<žZ´®™þ^ß¿çÎë¨QW§nÿ‚VQÛ)¤IƬH|ޝŝ]yùy>5»ñÀYü»ß;q¿ƒàÞ=êf½ª+(?ÅLM÷uð-Æ÷&,Ìd{y9ŸnV;÷^ßcñTïmϼ,|þK¯/Ç uÜyØ­Tr}«øàÍTZâ-_?-=Ÿ¸ëq4{Õø³£Ò4¿2¤Íîy:P×7xv3qç·Riw•:?;~Êç÷_%ͺìÖ[ ¿®{_¼¨ã~´‰º–W𡟄#¾6wn>•îÇÞþEîp½ÇkÏÚmùà"žzR¾|ÑŠ”‰EsL4~éÛ*¥QXÎgO+MΧ\Y¿Cý™H²•ïÙSÌ۹߽òㇺž¶™wµ©{¹P”F’áQõæó£¡I™¯õU*3üïaùñCiéæa×SÄ{[®xZ¦ÑWg;ÙÐ1ŸöœmS®CÕ ž&¬Â¼û}JpüP7fø¢HQϵ´iâùr »¦QÑ E§ òi {XN±l<7dHkÒZ¸ë5zÔq9a¤…#Ÿ=ù|Hmèó¼×ÆJùüº^†Ïõqܸáï¹ó^+éH ûCÄ4j;ªzÖGyÔº¤†=¬z­^Åþp˜¡f·“.µÌã®[:PwåÅqÓ3˨ðíÈþ?Ì@>N¼çíy%"ƒ %5ÄýŽ!ö,²$•»néEÝ/_ »×dî"ª7’=ŸF9ï—¼?ädíÞò›ÌH’í¾`êV]œò¶{uþºlr ®øô€IóèÃ#Ìß‘FÍSŸéNïÌ£©^ýÆýêhRD³+ŽšV<-–ᯫ£î¡äÂðþ93©/Žn%iiädRg›–åÑ´”Tiµ‡1t¤"ûC>†&<ñjè:î:©u{óÛ1DRBFnüϹiį•G‡ PO#ö*êŠ} -Nü¾m¶m,7n¨ã~?¤äץѓ’º/~”GÆ“~5kâ讘½²Âð¿â¯ë¡náçl ·Þðã«aoÒ(ï¼û“ çQSùեű¤ÿ¨Ï2džîŸR=²‡ÿý:êøù¤ý{¹ó¢Ï6\ð‰0J³Ù®ÞhCÜ:Nþºê¸ëN³ìm‚Ò)î.É^ߥqŸÇ[ýî¼·Ž”»¾êEÝþ~왕yöOW*c* I§µGfu¦«wé<{9¾^mÝØÄv¦&CÜù¹1Ô¢ìÞGe÷> )»÷QÙ½þ÷Š,½w­†ÿ\°ïûï+@6ðƒÍJ &pƒK:°‚Ähd*0‚‹oj2Ð^¡É)Á€MOfð€ Pñ¸Ò?»gdÙ=¶ÿñ=¶Ù{ýýwÝ I€€’ƒ¬à1KFpñá%-0àÂL p@¤7 ˜ÁBô`?H|j0A9èÀ >#U`2Ð^!0•`@ŠÕ€< D˜*@6ðƒáª¸A€ •ƒ¬à1‚Wõßt/$9èÀ >#ÀU`/¤tßÈv?¤徑e÷Ó.›#éCþšs¤²ùÑ¿ßüˆí5Z~ÜÙ÷†­W‚)•Ìà!š–ô`?HÐÄÔ`7ÐÐä +ø@Œ§#¸øf'-0àžô`?HÐ Õ`7Ðå +ø@ŒF©#¸ø¦)-0àš¨ à€HÑT5`Ñ`ÿ‰ûD–ÝSûßSÛ„ŒLàG:°‚Ä ÁŇ‘ Ìà!‚Iz°$*5˜À „–t`ˆb*0‚‹4h/ˆpJ0€ EàiÀ "ü øA‚0Tƒ Ü @0ÊAVðA©#¸øÐ”ð‚!ª8 R„ªÌà!Vz°$\5ÁŇ¯ ´À€Dc%À"œ5`Ô Ðƒ ü Ap«Án>Äÿ÷‰Ôƒ ü AÈ«Án ðåÿâ½"KïÉ~Åš–Í“þKó¤²9RÙy¤‡yÒÿÔüˆí:~<Ù×ÌþÁÅ7'h/ˆÐ¬”`@Šæ¥3x@ˆF¦=ØÀ465˜À 499èÀ >£é©À€MPfð€ Qz°$hj0h–rÐ| FóT\|#•ð‚U z°$h´j0[XvívíH,0ƒ„èÁ~ tÔ`7@rÐ^!”`@Š€Ò€< DX)@6ðƒá¥¸A€ “ƒ¬à1‚MFpñ!'-0àBO p@¤A ˜ÁB¢ô`?Hj0K9èÀ >#£!«À.¾9Ë@ xA„f­8 R4o ˜ÁB4rèÁ~ ±«Án ÉËAVðM_Fpñ -0àA p@¤ ˜ÁB„…ô`?Hj09èÀ >‹ñø`°!#-0àBG p@¤! ˜ÁB’t`ˆP*0‚‹+h/ˆ^J0€ Ň[fð€Á¦=ØÀLàBO:°‚ÄAÁÅ¢ ´À€DH%À"05`ž Ѓ ü A˜ªÁn Xå +ø@Œ U\|èÊ@ fð€!¬=ØÀ„²LàZ:°‚ÄlÁŇ· ´À€s9èÀ >#ÜU`ô2Ð^!ø• øA‚‰€Là±ûXàßëßÌ—ØyFÙ~leó%}HÙ|éÿê|é¯>WúWæI2þ³æàŸ·ÿMfð°…¥=ØÀ4,5˜À 4/9èÀ >£™©À.¾±É@ xA„F§8 R4> ˜À 4A9èÀ >£)ªÀ.¾AÊ@ xA„†©8 R4P ˜ÁB4SèÁ~ ¹ªÁ.¾ÑÊ@ xAˆÆ«=ØÀ4b5˜À 4e9èÀ >£I«À.¾aË@ xA„®8 R4t ˜ÁB4wèÁ~ Ù«Án ñËAVðA #¸øPð‚!¡8 R„†Ìà!Dz°$b<>˜À „‹t`ˆ6*0‚‹h/ˆDJЃ ü A0©Án ¤ä +ø@ŒÐR\|€É@ xA„@S‚)Nfð€a§=ØÀ„ŸLà‚P:°‚ÄFÁҤ ´À€DM%À"D5`¨ Ѓ ü AÀªÁn låÀ€D^%À"ˆ5`Ê Ðƒ ü Á—_ &pƒ-XÁ B¸ô`?Hèj0w9èÀ >#ì à€Hüšžìžgx> Ä$€½»¥l3?’‡üu×#•ÍÊæFes£¿sI þ3å⟛ ´À€—ý÷Ф”`@Ц¥3x@ˆ¦=ØÀ445˜À 479èÀ >£Ù©À.¾ñÉÀ ¢ *@6ðƒMQ &pƒ R:°‚Äh˜*0‚‹ož2Ð^¡™*Á€ÍU&pƒV:°‚Dh¼J0€ìÚ$4b ˜ÁB4eèÁ~ I«Án aËAVð \FpñÍ\Z`À "4w%À¢ÙkÀ ¢ñ+@6ðƒA ¸A€Pƒ¬à1BBFpñ!-0àD p@¤b<>˜ÁB„‹ô`?H6j09èÀ >#ˆT`@Š9‘Ìà!‚Jz°$.5˜À „˜t`ˆj*0‚‹8h/ˆxJ0€ EjÀ "  øA‚pTƒ Ü @PÊAVðÁ©#¸ø•ð‚¡ª8 R„¬Ìà!W:°‚Ä`Áұ ´À€Dg%À"¬5`Ü Ðƒ | B+Á°ë“ì0ƒ„yèÁ~ ôU`?˜žì~­ø·1!`ïÆn ön-ÿ1Ob3þ¯:Oúß>T6O*›'•Í“þççI*þ3þ¯ìcËAVð±&¥#¸ø†%-0à˜ à€HÑÐ4`ÑÜ øA‚f§¸A€Æ'-0à¡ à€HÑ5`Ñ$ ÿì½X{×î;vìv,(Ö"6ìX =;öرcGlB"J'Ô‰=űaÇþÝ“8{Ÿï|ï¹Îóœó¾Ïs>÷uý®ko÷Üf2kf­5ÿ™¬(A0FÒ úH Bà Â@ ¡Zo æ“«‚"`€Dk$@ *€‰×x5Ÿ„Í€P-0DR¶^@* ’´HA0@¶ À \|€è#™ 'e@€än ¼šOôfÀ(€"ñ[/ •À…@ ¤  (X P‚ `Œ"!>@ôQ0„À„2 @±Þ@Í3à@ Q\¬€PJ`‚b#RP Px,€(A0F!o æ‹’ð  †(RVÀ ¨@%0AÑ)((`@” £ ‰€Ð}7!ða  Pì¬7Pó…Ï xÐCB+àT ˜ 0ŠI JPŒQ4EÀh€> ¨x‚0P(¨ÖÀ¨ùâj<€h!Š­%¨Æ(¾"à4@…X©žÏÊNÏo7EA¿ë÷ŸW(â}Û 3Zy$°éo¦MW“Æ7béÒ‹Î/¡ºÁ:nÄýeÙg«ë92 t²ëEã¼wcÜšdå•ÄÒá'l·ÏyB¿7î®ó¥Örú:åÀ·nëü÷çü²¥Ðõli¤5¸t‚á|”´jó¯”-¦Oxß¶t”µOòQyTÇÙë9?\t—ݶÕœbLíóÖ¬¤¤§—n´/iù„>ºJò­<èí¼Se}#4V70…÷™…®™áËö>÷}˜Wzyß] ¤ñýÚŒìóá1ù/è«yãA5 #¢“¼_çw£çP*<á2$ΰÖY&ÊšŒ¯¤“´Ÿ“÷˜vYìé°(׃lM7ºÎ((ȨUjëœ? º6ˆú™^ç™Þï^U’üÆ×ýÝÂÓñTÖpÁƒ†ÛõSG¥àý[yß=è¶-ûùñ¢éEÆ)`õÖý%J*~²Öz”÷ãê¹K²z@÷í\Â’Ó¸ï'‚n»M×ÉW¤Ì«âÉ*ÛÄ‘s奊ë+SS›û½ã—Qâ‚_æÆ)x? îxJ lœÕÃ4ÊY¥0G'ôôŸó˜¾½e»Ó(Ý€^Åßü¡¥Ð©Nl¯µ0ÞŸiÐ2ÜËvoù®?çßuøc2ï¾¶¦¯¾m4\xþœŸ‚÷ãåýÕ¡ûÈŽQS0ó£†ç,»G?Ù1ƒSÕ<ÌÜ€'¿+¼/+ç³­eãàU1`GªŒáΫx:xaQ™eíÇo>­·+å|ÿmf{]A±6c \ü`Œ.~Ž¥ÂÁq)2±ú?5žOù“Õ(×ÒR‹½{M¶ºR÷“½ž Êä4õZ;炌.~Ð$M/ÏÌ—3:Ãñxš½""Â(WK¦c ·èÍr£ˆÄN&Mƒyßœ:ºz§Ï- bÝô>7õ~<=¹;àÅðH-µ ÉþrØ÷÷ &ïû¾k ÓéDÐ…'µþøPÁ(ÚΚø®w¹}¹P|û÷pèì.ËI[ù|i0ïwn¢ÓI ë>fýŽŸw‚™¦9ò®’òèp}Ü—uZªü¶ðt;¿4 rHσ‚©G‡ö~é«ÓI¡[Ð&6Oïá%¦Åû±¹N·(qÒ«ôˆyZ Z·'¸ßZâüî‚ùãÙO§SA÷uà³+ÝKC˜_…ž/z L¤–ê,ô0ÕҫʵÕÔ‡i”ê5!˜¸ù´†:ºõÉÛ xp™áüÁéÓ³‚û-´t0þöàKô`ã´¦ ,ƒy_AcîúsÂu¤ó-ežÚúÝò6‘ºµ5~dôì?o×_»=G¼z;­íÎ÷\]¯ngn¼…Q¬žØ«‹u½¹Û«‘$èOÝíÙmérzûøXÉô jVbzµü2w~ ¡ ›5vâ­«LT黨ª$a?}Ï÷G´ü¡Y¯¬Mî´Èg÷ºéÈ/¬‹I÷:Ÿ“uñƒŽ›+Æ\›{AXÔ+™î͵ø:ÙôÙµ¬}dÅ[Wš_Oxÿ®#nnÎh.~ÐõkØã—qys¢ÿÏ)³Î$“;Nï­èkÜ(cˆ+Ýú}„GŽ‚â¾wèÜ/Ç‚‹tîy5£B™'ï}†Òjzü¬“õÏë®ÔŸµñûªà}–¦qñƒ.äô#—A%áŒÖqÏ¡ˆ­ 96Jj:óÔC _ž8rÐ<7Bòk{°Y0,uRÖ¸8ƒ‹twOÕpîµ ‚iâ¨Øü‘!»u"ËåKÒÏ„1«o¹ós+ƒI}ÚêÚã:3u:=gÔ1iSÓ)yçE3™È?•uÅçí9p¿àÖ2ú‘6n~‡`jp¯´brÏYÜõ¾ÔìS"™.é—>^§¢SgkÙô}þ€†è š–Ó\&ÿkEßà*ÿ.îúƒŽ‰ïmuàZ$3¶ïÙáÝ¢U|`ÅNÓà´ø²âÚF+¨éù ëíÇWùàq×t¯ã¦NÛ1,ŠÙwÂéƒŠÚ †.s{@£ßë'ˆZN1E¯#3¦SVLÁm/ÑT.~ÐÍ™˜qOÅØ÷˜‰Kí—?k“ÑÿÝÔ”—6JYFëÞ^ùÃ*˜÷­çãÝN«‡iy¢™ûhÿˆÕר͛6ë>Þ'ݘÝmî”Ø™½ò‚ÿwt¬Û×ȃÑL|ËQ×H¾Â+¨qÒ}ªšû?@±âL#òÒ—aâ«c¦sñs®šÍpþ!×èñÉî^÷i`ŒA©t— éÆÀ¹7ç˜;žzâRa«£¶­V8Ä0·C/8ì:…X6ËUÏ»OLaîÙýsÄ4¿ûÑ-Þø<µ>ëh:‡‹t]j6ñ³ÌŽa¶®)ÏpÞpš²Æ@÷i¡ÃÈG39S§ÇQÚ‰6ÁöôäËeƒçrñƒ®£n@`,Ó}äКïâ¯ÓËŸ¬AZ1õ²ÑŒ«iîL+nBÊæ}Ý9:ûè¯Ûne¤ËÙI˜×ùùXÅÔ;åpªßVgJ8×Üà¸G0Å ¶y³Øš‹t66/guýËX˜¾úrsÄ Ú´©KKßb*¾üc˜|Çn¥m æç÷/ââ'®ò³P2Î>÷fÕÛpƒô>N~Й8M™7QǤ[/ûe³ö&E¨"ú-Ý{÷s\Nìt8A4γVûê»VÎãâÝøvìÄÒxFgy“öh—O¾G§çÒiÊ ºñnsݳÁ¼Ã|.~Ð5^q[»enžiÇ–[·ê jv¼Ç‰»˜8oÜúÉî4n܉â.Á4@7З»ÞUÐÔ/Ê‘À42šUÐ_žBõöØ>_|·ÚçÈhØÆ•?ÔrÌ¢zõ6qû©…nññåSJw$0]Jfö~œB‚VÑ彌ïVû–²ÓËkWÍ'ãâçZ*¬½0~Dz› LÛ¦Òœ•%­?|(¢òßÍ\iøy‡LÔ¿“EÑIŽ-æâ]ˆ nüç:‰Ì䤠U÷f§Ò«GŸÎD&QÙÔ{stu«ÎK{$–´\Âźóæ {¯˜”ÈDöšïyú@*©²ÖW´Ø[D?æ²ëîTÙ–5Ž &ƒº65=Í}ž:Só— ׉̵”ýí¦ÞH¥·W"»üžZT=OVg;ë½Mû-µŽs׃:Îç,‘ÑØ&'ê}O¥ƒ/{íŒ2(¢é·FGĹ.§z&›' ï~>ýä[î¸H¡ •µzdU™ÈŒ@™4(¶ ]z¤àµþÒùL“óËi¼nlUÞå®#tƒuóIŒ…Îø"ºÎì|êNµÏ_ØÑnúó…ÁüÜXîüÔBßêºk_Û$&ן`FcÆ[ž_t§z^_U}àú îzÐs+ Y{™“IÌÃ1!Y'n§QÜᬭf]ïTÏM^¤kð‚yŸ]îº@ÇÍÇLb˜I??5h¨¦!{N|küæ6žyÜ$Î…¶wŸ^9-˜Ìvõ6Ý$åó't{æuçK3QRàIjªõ|àŠ‰·y?cgÖNµH€ú—¹ÇôäD[þúƒîeÞ½:'3œ/š† oêpø6ïgìD£ú_š8a`0)CûЊt¢ÕM.ÔŸ–Ìd2rk¨¦€´Q{ínÓù³CK†Lp¤ñç)Ÿv æç¿.åâ` ҢÉLÈ)ÖUM·>Y“hv›Zž.iåuÁÔo—}‹ìL^E¢Çݲm¸øAÇù5'3Án¯·\jNŸ¿è¿Œnz›¿Ÿv &®Lt‹îÁüwN§…înžYáÛëÉÌÙñ†”N]k,³ýÙ-òÚÍ^P‹u:®ò'ãâç^*Ü[ìg;°<™ñš¬ôžä‘Nú —$Ü¢¼º¢ZF7i:;–»c0?Ó  +NfÇ0{:± :ïg}‹46eÍž;QåPÖ'¸ÊG‹tü_ûÄ÷a˜]‚íOæ§“å™BM]Ñ-~N¦¸ú¸”Þ}º¨óm."èV'mz7‘av¨¦%ÖÊ i:Â[ÔŠµ=ïBÓ5=öÎQÕ—óõ:½Y§ÛˆFw;m–AsÄ®]˾Rôã°!®ÔñÊ­NŸÇó~µœN Ýøy±§od[ùk›.ÔãÓÕÏ×nò}¸ý¶öj²qç}÷¸øA·V7P˜al69M–øfPûEÍR|‹WLÎ4·ê¾›Oõ—ËÜF¹Qç½²Ÿ!^·X[¹f|¾„nNºžU ΫƒbvÐkåõ™ózŸC>Uù)5zvÕ;n‚·¬ðD} ±.W—òÈædÓÛúÑšÍQfö>Üçéy” ½îfâ7‘á|h54©ÅðÃ.±¹ÔòjDL¯NÎ42xÀ”‘¦AôiÇŒÕï8:n}+‘1Þž=ܳ‡† ݇dLX”KKï (j\ìDÛ3Ù…ã b]=Ûp:!t¢ŽiMOš'2œÏ†Ò“Œp«‘K÷›Mo¸ô£#]ŸîjðLNwöì_ù«÷ýDÐ)ލ:oý•Àp¾@znhZÛýr ïÃ:^:Ð/‹i >ò*ß^.~Ð=ñ{ÝÝ« ·¾¨¡yú®ç—äPøàÝ9n¯ìˆ»‘“…Î舿_€ŽóëM`Ø»‚¢¶^ 6¶Ì¡ó½ nîgKG›°†—râ|’¦pñƒnW/v8é¦[(ÖP“·?çfÓ4Wq­°¶$;0­Ïn¡œ댳øõè.5ž>‰‰g<'ì.öDCîe SûȦŠ/î›kK7OušöÉANF??¯Õžã×[V¢̼×~³m<óµ)»Ò–G:»ÅñÙ´µg8gGºe‰üoyIÝ€å5ëÄ3ûƲŽnØ>qc«¶½•L½ØèÝÏóhu ƒ7½NdÒ|š~¤Ò‘\où4¿2Ø©ó^͹¸A§Òt5Ïí§dù.êr95Ö· –2&“¾2éñ3É|\úÕ‘{wzaÁ8.nÐÝd×dÆ2W·ÔHòxÝ ãµòÞdÐRq”=½ht0ñò¾@Ê*ìæ9¾ÝD.n«oï[Ü5r‰eL&;O±ý½¿‡Ë2(âÞ¦\mK¿6<HËR]¼zÿ˜ÌÅ ºÖƒæ×½U7–Qm¼»°g>5qa÷› 2XÈ®0ˆèX×óÛBÇù^r÷±BèÚy9Þ+‹aZ.Ý1»³E>y{»*íœA_Nœk±IlC•)½—­ ¤%ßNË¥p:tâћϋaòÖ³Wl>5½~Ì6óqzUÿKeF ý‘d¸äa‡+·¸õ' t‹³rO»ÍœgípwåS‡67?O–¥WÏÂ^~Íùõqî|–B'2b¯Øh¦¯ÎHǥƋË"ÇtÕ… èZ7Ó(Z´,¯=Ä–÷å $ÉvpÏaî|ÑBççdÑ*%$Šégsú1=ȧޟÖü}OMÛt†vãgwŽ©H5ä:hòùø­.rëQ ëÝîk>µÛ¬8¤&öè>î@ᦋ6¤ÿ–ñëœÜù"€.¸¹ãŠ^Ï#™‘ß3v~lY@Îös;\M-*ܺտîHœ‡Œ¯CÜç ¡ ¯>W¼+’Ù¦3b+ Èµ­¾Ü|”FãʤSÌ>:Ñh]b”‘õŠÂ_,¹øA÷£ãþš[Œ"™·íÙ¼€æ¿Z¼=rfEœÛ•ïLõl—YuQÆÇ…tzk| PE0Ë÷´¯øj[@o2 –¾è’FuÆÎbšáÖ}ÍiENÌ_þq —¤ÐÕÎ/~¼$‚éEÁŸ_xÐê&ã­î©Rù9úbê™X;hŒ·ŒZä·Û9]Ÿ_§†nÖ¶¤ë;¾‡3Í´û?M¸³›ï;¡»6[}rcë0fjmÊÁŠj-kZ;í&]Ç:Ø8P½œ¶¨2êÐmÂ×­ùºÝÙ¡»ˆ¯2œÏqÝI–,U®¾I— gwÁ‘Ü&ž-¹ßCF¬Ëà‹ .ßJ¡‹>D3ï†]aê¾õ>´i!Å­=¦_ÖöfµÏÆÏiÆT]dôÎòØóocøu2èfVºwìð*”™+œÊîPHG×¢‰¿QíK1C×8ÊhN©ÁÊÕ½ùø­©òã eÖ>YïE¯Bú™’¶£ãÂÕþêCÕ~ÜÀXFܺw<õÖ– w>l?^Ïã2S˜¿­i!1ƒ2—}½Nûk•}ùyÖ™ž\Y:ѼžŒ^±öa9\ž@×yòä¦ãCBÝå.,¤ˆe¬ñòu2_Ä:T8Ñ«)C;¹‡ðõ“»n…ÐõüäèôìSy5ê-$W#£—6×yß6ºûòAç·sxÿ !?èLüvÜhht‰9öª™ëž9…4o¹uý¬¯×ªûãÔÖ;:œ0 àëüX.~ЙM»ØÑcI0ãìøÝ;vq!µ;•÷6üeül2qû[Ò¼ñ¶“L œÇ _ÛàÎO)t…±Y/"|ŒKªÿ‹W…”nûìëŠk|={7mgÀßOq׃ :Ö}¥MaÃù£Ò™C»Ü›¼Fæ§²ÂC–Ò"݃’¾/ã>O ÝíÞ}E5ƒ˜s é¯*¤om¾ÜjøQEþúÁÅwç/¥.¿k]Æq)×¼Ø0¬5§Ó[W*Ììºçæh9sÇEø~]!YŒ¿“{9FÅ÷64ÙéS“Øñ¼?4·Ÿè¸õ÷@f¦ó›.7Ò¡âòëT´AgL*"ÿÞòüfüó:.îBè¦Çli$c.lÞèž))¤‘§ìØ>PE–K>úÊ–t‡{`Ý=Î.ÐŽçⷮʗ$€)?ZE!êÓf·2›¡ô«ívî¶§©7=Ô?€– »6lÚEþ9tycæU0Ò]RôöÒ†ñks.õf¨kó!p$o¯^G' àóÙp.~ÐÝ-_e)œêÏô»ÍoÒÁBš±!÷þ…MÉü}—Íu9à¶¹GqÏ[ÆpñƒNÅÚÐlôcÖÎkøjàÑBz3Ý+Cs=©zýbKdö¡õ(â}¦è ?èä=®µÏWH™_lz:QH7¼´v-5Q>;üË™_çö§Ñ¬}ô²I\üÖ— ÇéÄE&ªkXYH¡[¦X˜HÜú„3ùf—˜ø0þT<©ÝýÛüõ]ÓqÂä] .0£\G48[Ho-êfÄz%Ъ½ÓžÕt¦vC>Îð÷§…ù3›8qqB÷`Ëá#­Ï17³Æ™y¡üŸ}Z¹µ0ž:}_Ðwâ{GºP·Ñ±1¡þü: qñƒîRÜ}‘â†/3Í䜛_!yéOÛÑ+žjEŽ;úæ¹fÓÜŸÞ3’‹tÜ}ý¦ÖæØ %²Bêÿ¥Å#ßÝqdéÔdr?ÒÙ ]øûçI¡[vÅ÷¹×æÓŒÎæYQHçfÕ¿Wªä¯z·ûÊ©?-j©ÉqáûOèFpRL‹ñ«­ )¤žºAIƒg½ >÷Ù¦ënü©ó§;}ú*ùûèX/w1=Áfíq Iúð ïÕc±ôsa׿»(;ãs—qþ¼ ÿœÏ³Tw=añÍõǘֲ¶ä‘…uô¥üÄÚã;ˆ#¦»Ý/ÅÖñ'íd'þ9-t{±œ½û¸À[ÞÊBÒÙ¿6‰¡óŽ­ýŠ¸Ò´õÆN ñçŸSñý't]FÝ3³ëw˜¹š3P°)©:=Ð?°ß,šºy׳·‰=#ë¾…ŽËküõÝžµ%ކïgzem:çr½÷ÑŒ"zV«æùÉn´±‰Å·(œgí.¦ÿÚÀ½—!Nðz›$Úu/1à®gXJ!9ôz·bO$uÐ-Dº’ISÖÉÌŸâö§¬hÐÇ”‹tCúš/q{»“ù5Áñ†oòàwß³Ýc#¨(Æ8rlz´OgtÆçÝ!\ü ã|$ ç×THå3l} §£ãT~[èL¿|(ÐÏŸï#¹ÏÓB7­hë Ô½ö©ZëÛ…4xÌŽ¼³Âpâløi´Î¸ÊŸ®aÆÅoC©»ÎW1·;}W£¸$§œŽò £a)%þ '8Ð,v¹±•?õ ÉåtÜý¼3c4dÇ¡— )sé¢ÒñŸ®Ró¢s;ÇÙÓô¼£‹÷§ò±c{-ŒãÞWB'^ðåÈî2+ƶäÛú-Ï iá–vçú¥\!:h‘lGkºöíýd?{Á&ú¡\ü {³(´Åé’‰Z¹çºóËBZúªÞž$ßPz¿4zÐãÓþÔE`3®Î^îs$Ø>eT—”aô­q‹Æ³* éð½&ó Ý/ÓÜk Ùt¡=hÎDûóïYpñ–B7éhôžY›V’y׋óG}*¤ËSî,)BºÃfè@ÙŠþôfôâU÷{qyZÝÖN£>¬ò¤ÝùmÆå|/¤¹[íÛö}Lêyæ_wt¤×›75>ëÏ¿ï5Š‹tvÍn4±ÏßFyÏ*õnQ㉙™!»Ôßl'c“åD:Òþdøjϲƒ¸¼¢·±T¨³óõÙI~¸ã_çýê`² Gã Ò³î8<¿­˜F,ýj·?ÿÆ}žºS›¶/ð³ØK®Í»jp‹f.]Ûÿöö@j/‚×…r?m1³ðòçß—àê‚:vÕþQå~èfnQá³É%¶/Èû£K«Ÿ~.ä%uýè*ñç}¡8ºÞk–ÚŸV¦e«íqnQèó…®«gùš®WwW»Ð‰½™&öþ6ﵯ÷mî¼”@—3t˜¼gߣôé¶j^ýÖ·(þÊô]7®IIgK{^Lì[-Gú“9{ÛЙ{¯I MÌùóÚÇIwûÒî-4?²ËhÇÚVùõí57g²l¼Lo¨ÀŸo« ?è #»‡Ý?ACö {¶Ã-Z>e_ºJ~–ì,»åe;Ql/A*ÿ\.~Ðå] ½ø+ò%²¯v¾E¢UMËÞùЬÆì“GGâ|)ýiF†lÏP½ö\ü6• ÷_6=qï€IΦØÝå¥Þ/½8ý¿æH‘›Þ‡Y¹ùó}g.~еï{NöÕÞ—Vé Ío‘»Gšp¼ì8­T×Å-Ž#}¯õ4fËZµbë ;¹¼"„Ž»9Göš+Ï xÓáu÷+^8ëzZæOÛׄ»òý&¶·¬íÒÆ¯çiÝ>o–q€Ž×îo+&á÷£Fƒ.ûÿí>H‚í}ÜGÙÅ^$]ûƒýR§™y“ÝîêÔn élð~ºÑñëò0Þ?š«*èJd&oWÊý¨M|óíå8î‰/wˆ^Ư;¸ÓlOÍ«øX>¯qñ‚®S«ßë'àºÐ­ƒ#ΙõÇþìÈß¿º“î6<ÑŸv\šg³¤;éâµ¹T(3›Ô±ãüRÜꛨí-ºþáË—ä%nL•_d/]¡ðçýëtè)ZáNKFÏSö]]×ò•œ{e6d£[všçJ§ê³oÄøS“VÅ{frï- ¡«Ü¿ïôìšvþÄ®vžhöE÷šºÓÞM7 ‘Ókö¿[TºâöÈaê#LÝ¢IAW6;ѵƒ½¬KÜ«tõ¸øAw¤ô}çüAÔXÛÏ[ZYH±fknº)Ž1ÑmòÖM}àHŸß~ËZpÑ¿êýJÝç© S¤N¶o¬‚ÖD·úžù¦Ž×Ö®¡oá×/¸ç§þ{/S ]£ÅÜÌë“þjvåºjí÷Ÿ™)ðaª®ÓÅ3R+qÄ­¯Õä®·-¥Â']^LM &î¹q!™éŒ7}™ªë`Dh±ôó¢ª~¿5w½Awo튉v;.Ñ´ø~Gæ ;bÚuçýóÌÝ%R™ª¦˜ºåE‰ GE—ÎD·ä®7èÂF>ØðdxUJ¶¬)Ï`¦Ì;äáFÚî ’qÁåK?è¶èt„’®MGÿ=vKæ¹þ™…ýÛŸÿ0Û&7)ºUÚ«êþ¢wýA7ÐÓçsöÏPšâ9î*ᾄ{¿5ˆi3±éæFóÜiûÚX¿~]ŽË³Ú-U>ËWè˜`qÝñy ã÷×hÌp¾ƒntáŒÈìâÇå½ÖÜõ·µTÈù+_¥²þû"\ iv7›ú²›—˜ªçmÜ{<»åÈ·—­¸÷2ÐqëàWiÌèGÕ‚Búž3ÀRcs™©òé½¾{ÔŽöø~\?LÜõ÷\$Œ~m_Uœ<©ÊT«v5* eƬ½{î´² ¸ühêÔI\Ü «Ó²üE׊Hz¿!eö‘!TëÂfpƒ¦ûñÙ#o:Ñê¦õ}o/£Z4ã>O·ÞEìjÀ>£RªÐök Ì´‡g¯éæL•7GÌMv–QJ§{i ¶NáâݻςڥS¢¨5ûxΠ€ââÆ¹ÄmId† 9kËsgé7YÕó .~Ð9‰G5»¹!Џç‚ù´¦ôØÇc“îýJ©i“zGFst ’œN ݱ3žFEQŸ=!µoòi=)§žnšÌpï­¸ÒBÝ »¬j–‹tŸ»… ¯—EIOØ|jÜ;áÒÏçÉŒnÖ²Þ5˜€^‘rÙ4UÆý>G û©Lóç?¢¨Å»Û_c3óiBt~´0‹ažúô}Cƒ–Ñ‘âFƒXýpç‹ÞvÜg³v½Ý¢éÔók$äÓ~Ó}ú.Q1››ô¤.§5[:7iî/#é°VÁ&1Üy-€îå]1ç¦DÓëOoŽ|¹”Oó&¼±÷ŽŠážÏ,§ô°ýÑñ×!tÜi|íãNúæSqÿ»¿ÌºÆÈ »—¦/§Â†c²ÓÈøß?ð¿#‚.Z;Ñf¬o•¯y>UœËw›}É{Ú£¬ßrúÖ[÷ÂUùÑêâ¦Ï…²kѤ·3÷Ü”OWB_Èáž ,£&¬ »ZF}Zl ¾8l.?èº5ÐÚåI4}Û˜ìçžO7Êï&õˆ¾Îlú¼XÞãšÅßÛÿÓñ‹Œ&K|ÆÜæü~UÐyÅÉ\êÇP«‘íö~[’Oi#ó˜åF7˜*?úks»õòïÇp¾½Zè^Û×aŒ¡ßל‘OúÅgf·8|ƒé¥é‰ÌæJ\ÉìÓöÒóã9ß^½¥Bý«KîyXÇÐú€#_%Â|ZÒ£°ã­o7˜Ä —Æ/ßãFܺw 9¿Ži˜½v!?諵ÎtC ÍmÅ>±Ë'ó™»ÛM¦[LB½²dwšþN©hÝ,V{vÄe§B§å±WC+n­UÈ»åÓ˜5­ë=¾ÉtÐä·rﺜ$o2ϼnHß¶º&®9Î}?tÅ{Ÿ–§Åîu”6ùDq±îÉ‹R˜_[zö\Agî4ÛëŽã²ÓíMÙ¶¸y\ü ê8bôÛZègñeuƒ|j½!@Úýn ³êú®¼Ëî+èòåeûÓ&ò}')t÷Ù…¼X2kQ°Ûúg%<2wì¼TF1‚ý j‡y9Ø9°ê}.~ÐÕßûðÉãѱÔz÷’–£+òèêÊI“hR™*_â–e-6[øwŸ`èLϼC&Žå}£ó¨ëÉÕ«GNLcªÞoíUöùXª8‚ïOè—ÊýOog©Ðwü¬†ÝÇR Ý Ly”gõºã¶È4ÆeöÚ#îW\iãþF˧V?¯Õź_¯#lÖÇÇÒ£ëñ¨dyôîØäùßÚ©îw.bzÑk•—rx ]ô>~—Ó ¡[c“ã>¿,–’†°/ÌäÑLýþRÛMj¦u«•#Òç9“ÙäiQCi@«öF<áöSÝÅ•…­•”~˜5ÈΣã’'ÍzÜV3Ï;ŒjáÛ͉œ?œÿt\ YÚMŽûÞÁ†‹t­J~L3ž $î=»<újS™ß½_:Ó~ûdß‘Ž<é°vÍâ@þwÜñ”BÇ­ó+iÏ*ÿ%»OäÑŽíÝîHg¸û.Ü7Œ«Q|Í+gMÌ«Å}ž :ö©å׋J:–UÞqùÞ<º>Þ.+¨(ÙÕÜéÞ¯“ŽüûÛ”:ðØèqÙÜ÷ÓB×·È|ÔÝl%yè8ìgƒíD&ÌfCG%Ír"öW ¾R7ݸ㩷«Tè7™ýÂJšö{İeˆûz£|É ¦êwf¯wlÓHÎßoðþ껪~ÇGì[íEyTëwà¼àò †{ŸHL?nÖøøýW ±Ok|åtBèfŸ˜RÒfi­Ího;3æ·½SÃôL†{¿Û…¸çctã¶ûÓî¿9_vtéuœ+¾ˆ#4)#ýÇå‘W–|Yã˜L&×uûè³\ɹ ™*ûI Ϫ·a´?èfèã8 ¶\´¸dpýêT·ö.A3i…òÈ}WÒ-kRF«‡O’¹Ï“Bwóªõ‰oâèfƒØÍݺçQÇûÏÇ}>Å<{Ö>oôKWzÉ>~¸Èÿž’;ž*èjìëauÓ(ž&Õð¥¶Ì£Ûï[îÌûœÅpë®Ôuפö“.’ïR»¾8ºu߯.‘YÅÓù¦¹ÍjçÑ#ã5ƒ—f3«/v¨ãµÃ…‚vyvìR ÙÝÍê¼›;žz»K…Üúu<}èÈq Åzý9úz6Sõ\°ÞIåá•8_t·ã¾Üq@g<±Ï—š‘ña§¡÷DCSãM^ß%‡áÖœi­¬Mô÷@ºó¨¹d›¥#?è.Ę×.ÑÆÓÝ‹njûia»ú[s˜h»³õœ)üÚõɾ$ÿ[Dе0fW¨¹ëž9354k•φÄ{9LÕû}=?œ·q™œ_‹tûÎlAˆH–ºËgÊ% ݰ®x5jd.³¾y³_5bšÒ9^šì('ö×¹“nr:)tQ!Þï»&PqPÉÀZ'5Ô‰„&îÂ’ä\JÜ®´ÍX—Ç8Ì_h^>Ú‘*ØŸáv“û8Å—Ó© ç|;a‘H׎¸½Ý—KwïxLzs1Ñ=ùlÏ¿Ïô÷ï§….¤üéz߬DºÁ–#ë\zhW^‘šÇäxå?Ô6´§òþ/¯7•“v×8t~Üõ ··Tx\ÛØeŽ6‘NÏb߸ΥmôRsñUƒ~Ð1ï£'6Iâ?œCÚgãë*lòö)ÿñÁ$Ùq •©œ^7N69Ó[L·ÿ‘ÛÿÌ£Dõ_ìsÊ€EÀx5_Ì€P-0D°^@* †HA0@ñ° ÀÅD|€è ðÝ€'e@€Bc ¼š/:fÀ(€¢Y/ •ÀEIüzÝzÐC3+àT ˜ ¸‰: áç&ýWΗüGæoÿgÌ—dç&£ ‹€Ð}w!ða  Pì­7Pó…ßìÑëö/ÉÿþéOoô§7úÓý¯÷FlžñàãÎVo¼€ T$)1‚"`€„e$@ *€1˜ø ÐG2OÊ€ÉÍx5ŸèÌ€P-0ÄYý xÜþGþmÿ‘/É¥¿­Tc$}ð  ž ” ‚5ðj¾8˜ Z`ˆba¼€ T1‚"` Àw À…E|€è£È'e@€¢c ¼š/@fÿ„¿­'e@€âe ¼š/dfÀ(€¢°Y/ ü/ž%ùÌÜþϘ%Y LPÀÅ@ Š€й%¨Æ(î"à4@…^øúܲ}ÍŸþèÏ’DïOŸÄöIú£ÿÚþˆÍž|<ÙïÎþkà Ô|‚2@´À Ë x¨&H`b EÀÉÌH€Tc$7ð D'ž ”Ÿõ¿€¿íäÛöù’øü øÛª@%0Aò)((@” £0ˆ€Ð} !ða  P4¬7PóÄ xÐC¾ð*P LP`Ä@ Š€Š%¨Æ(>"à4@…HøOøÛJ€Tc1ð ‚&ž ” œ5¨Æ(t"à4@EO ‰x‚0P|7à Ô€-2fÀ(€¢èX/ •ÀEH ¤   Yüþ¶^@* Nn1‚"`€Âf$@ *€1 x¨&(|b EÀEÐH€TcEð )ž ” ¦5ðj¾xš Z`ˆbj¼€TcWð B+ž ” ¯5ðj¾› Z`ˆ¢l¼€ Ti1‚"`€‚m$@ *€1 ¸ø ÐG1OÊ€ÅÝx5_èÍ€P-0Dá·ú}nÙ>£ËŸ~éÿº~éÿ¯½Ò¿kŸô§Gâ®Q/>&ì¾›àÏÄ@ ŠØÏCr² ÀÉJ|€è#q 'e@€Df ¼šOjfÀ(€"ÉY/ •ÀIOü/àqûy·#銀Ð}$`!ðä½Ûþ+=n5@ _Š­x‚0P(¾ÖÀ¨ùBl<€h!.z+àT ˜ P‹m JPŒQÄEÀh€> ºx‚0Pÿ„¯­PïÏ:Òÿ}ÑŸu¤ÏþèÏ:×#Yð甚ß73à@Ëþ}HTVÀ ¨@%0Aâ)(Hb@” #©‰€Ð}$8!ða  ð¬7PóÉÏì_ÀÛÖ(€"áZ/ •À X ¤  [ û/ö·•‚"`€`$@ *€1 ‚ø ÐGqOÊ€ÅÂx5_8Ì€P-0D!±^@*‰ß HA0@‘± ÀEG|€è£ 'e@€‚dýúÛŠ€Ð}2!ða  Pج7PóEÎ x)Ð}=!ða  P­7PóÑ xÐCH+àT ˜ `ŠO JPŒQLEÀ¨ùÂj<€h! ­ð*P LPxÅ@ Š€а%¨Æ(Ê"à4@Zùš Z`ˆdl¼€”’³5ðj>Q› Z`ˆÄm¼€ T$r1‚"`€¤n$@ *€1’¼ø ÐGÂO Z`ˆ`¼€ T1‚"`€â`$@ *€1Š…ø ÐGáOÊ€…Äx5_TÌ€P-0D‘±^@* ŠŽHA0@² ÀI|€è£8 'e@€be ¼š/\f@ ¤  Y P‚ `ŒÂ&>@ôQä„À(@0@ѳ ÀEP|€è£ 'e@€i ¼š/–fÀ(€¢xZ/ •ÀÅT |€裰 'e@€Bk ¼š/ºfÀ(€¢[/ •ÀEY ¤  @[ P‚ `Œ‚->@ôQ¼…À„2 @1·Þ@Ív3à@ Qè­€PJ`‚Â/RP ÐX P‚ À:K³Ód|€æ/ý’ˆÍåÿÝWýÅMAГì-jâŠ?å·L"e§Ý/g*“zÃïÎ=§æTÿ÷Æ·Keu÷å3)G®ï×ņÖûÅMïÚÏ‘Ò╇êV˜Ó1Ö^ÒOÎû®á}Åô#ø]þ4;nΟ£•œº"jŸDãØ±ËmrHÖ#ì톨üêù˜ìT“qGå”׉u4ss! ø)¶¶6›{®Zðq>Så¿´úceÒ´mòj_[v{-¶çü?“HôÙ¹C²)¢½»ôjÓÆ<ªãìõŽÎôpaï6ËÉWØì€á:N§çU*\{ܰö¼&IôUêY¾:›ììk=£oÝŸ„~Xá\í3ÔоáØ&–œNCÖ’)êzI´Z|ôõfÊ&Åõ Æ‹&?«ö“˜`Ò¤ÇÕòj?4V'„nܘ²»—¿%§Ï¦>öe[¼=£ó“ßE;;Q½ß'zÏ”Ó: §A—ÊŽk}žHÃj½°+È¢ÜèÂg²Ϩî6ƧßGš3üu¯á=å¤ýt&rÌSn?%вmª[A"±Óy?]È¢ÖwêÖ{ÆÏ/q ?ËìNâ:rÒ?>ãÊÅ :+Ã.Câ©ï‡yKöºf‘pçû’ÂO‰ó™°§Á³n›¢'§%OÔµŽNät*ènœ“Nkr1‘ôüøÖehi'Ööê©}J×<®ØîIµ'p/9ŒJNrÓpßO ÝŠ¸oá“·&ÒÆY=L£jdQß‚Âù?2ŸRi»ϸê@z:q9é8çɈT~žÇ¾Raƒ¦¾K¾ÌK¤Í[B§ÒdÒµC'ú‹yÊÏëv¢¥SY§T9m.øx·ÆN'€.¦ÏÇkƒ†&Òç wvw>—IVD•ì¹ø”ºÈ7/t&£KΞ‘òj]ü û²P³É^?‘f ïýÖsˤ,óç?Zî}J}vݲ¾~OLi•ÆÉI6Øä}D~žtÛ_D-ßy/æºÙ¶¾7<“FíÜó«þò§‘Òɤ© îÄľȕ“UШøf­¹ý”@×ÈóŒmÀ嚪¬o¤~&…²ã¨æ>%›Së\ûÓDózÝlÉ«ç;ëâÝ©º[çÜØš@ ®­ðNÅ›ä¸7ú)q~IN$iÌ"“ÓéöÖZ?æç±@—ºüâ\'Ë~y±n ŒŸRÀš6Ç2H7n抜^ý¾üõ?èt6T­(ýJÔÁÂMtþ}™¹u˧´ÂrCè®7v´Ã¬iO“órÞ¯•Óéí/*†š5ûy/ž2ïŽ µÌ »xƒªù”b#¯Ž²˜aK“uƒiåtÆx\ùºÏ|ü K»saAsi<…™ûz«C}>fá¹w¥äžõÒküP¹¬©>x·œ2–;} xÌÏÓÎY¤µc<ÝèýP=ê}:çð¸´zŽݬ۰§‹¼Ú/L?èÎm^ܾw¶Ä{Ü>VÂûï8Òæ>Í[VÒ[vzµŠt'mï7öŽ¡’”ë÷¤Q|§‘K‡-¡ñG GÇjé“ÿ:„.÷ææn‰ kÚ«®Åí1ÔÆ|ãÌ›4r=’=!mW …,Û¥ëLg…¼ÚUN­YÛ¼Ž‹¹øAGG{N¾35†: ¶jû{LÕ¬ÑÅâ„{ 5ÿ^Ûê`_Ò½u“SgvÌ}+N'….½s£Q_:ÅÐlv¬X—4úÿ=ÿè¬ÚRëxû‰·]IjÊNd”ósŒ¹~GÝl!@4íÌøÖªFk(z +!ÿŠ»#ÌŠÜÈÖôTØÐT9E ÙAÓÜõ®…îă¬²I7¢éâóío½H¥¨³¹+¦·/!½ô5eݨ;.ÿ¾œ8*~žÜ¡Ra'÷ðbç“ÑTà\oñü¬TŠ xWS¯„:¥ //SºÒxïÍîÉ)l͘QS¼ùyŽÐ;™³×9š¼§tØ ˆH¥ ï/vkñì ?ÿօ⿚¯É—ó~Ëüu.9û„–¾˜øz«#~ËÉDg»”‹tM›|î±§4ŠX7£f®©TtbíägþOªýjnvioÑæ¬œÒúŒîuœ›*…î̹{må1Qôv¥¼~ñŒT¼©ÜБ'4N7˜ÚžFèXx똜<ã¼ÞÞ¼ÌÏS…®[¦QªW%uX÷hà¨Tjøã} ÃÍOHqx®eîG;êxøJþƒµrêpò¬"0Ž›ßª…ÎaáÙÔÏ £hɇQ–ÝSiFryóÎ.OèkŸÕÇž^µŒÀõ§ÌÜúàE7wYïp©P—nûE‘ÿ¦Û¦ ›¤’oûŽ~asŸWßü›#UöØÚ²GÐ:9?W”›¿+€nÃz‡ËCG’GyÃðo)ä—eÕcÛØ'¼ÿ³=éÄNÚ«öÕäâ]œ\èäœI>~ï&¦Ò©é)b em™чµm4žÃźyŽZ]ûAº±zgSèèë.{®g=¦`ÖŽº ½=òåÒÉúA¼O:?Ϻܾõ­fgFþ‘¡‡­¤Ðê7ík_|LmG¬³Ú÷RÌ×9ïûÀÍOÖ;‚úÐÏ3o¾w-=o/ü¼1…ÒÎú-é}öqõý‚ñæ˜&M>#î {öOëÃÏ3†Î¯ßóY‹çF7÷ñË»Þ`ñÎÇTØÇ±íkgü#¨½q…œ‚r›(¯øðñƒNg«Û"‚Þ¼>U*Y’BÎ…›OnvLï{öFIw"3ƒcu¦jå›»,‚Žu/Z˜NY6]V;Z¥ûF+«ýóÓgßN-Oø;Òž.«¾mL–S¯“»–nãtèl6~_çN!y!5,ǧÐì£ ðÍ“BÞí¥ÇGŠZȨÉéÁ›§c†7ྟºes‚j-˜N¥-"> šBKý7º‰ú<æý œèÀ%››=åÔÈzU«&pñƒ.:UÚb\½pšòâÌR»¾)¤k#Z>®¾k¡¤(ç}Ozrñƒî‡™o×>LE$oO?d”BåžYBÇZ*¹Ö:aË<¢5¬“€œ÷9çü7ô¼K…ƒJ‹6îñ#?Ö¦½C µˆ¹xðÝc-„¶ZvxµMJYÝ}ëªÔ.©N'€Žóÿ ã}ÉS(5xàæ‹)ZÞ‡gYF4™4׿€y>Éwx è²&4›…Þ–ìt†»)´uýžÚσ´´Q±ì†±þrº7ìËü#߫ã"‚nøÙÃzË[_¥{ók¾n’BÓfî3Ïß§%ÅãEÖÃ.£ºœ þ¾À‚‹tÍe¾á ½¤ÐN3¬v×Ò]ãìN³¯.ªúMNÑ¿kܾ´d<?è–|¯¹ñqN(e<õ]2ËJK?~|ÑFOq«¾Î¹8r¾*lŸ£á30”L°w)ôÁhyðCS-Aä”d˜±oï“]Ù×óµ#4½w;·mÎ)4ÔvÞÎ)Ý̈f¿w(_ŸB2£`í”ć4U7pÕ• \ÜQoe-Ð5ºœ“Þ±RáŒuƒ:7¢ÝcϘG¾½]^×q힇ßâ“3Íš<7o "·ƒó™@÷»ììÆûå$üÖcÚNÿêò6¬t÷Œ‡äñrù…ro7º6¼«å¦A¼+çÏ#„îpöá9¾Ò÷ˆ¨Ù«ãRhÆfƒ%!†y? 7:¸çà¤à]Aôtbí ÎSÝ¥íÏV¿YȯW¤Ð!ˉÉò'ˆºi—+5R/}ûË3ˆ¶t)Mü}„;.è6í¸¹î‹^ ͶfS¨b³•mÌ•ĺ³>^êBsRæ|«ïÄ]oؾ茶úO·Èøº˜J¯Gìm±åAµ¿l<Ã#ýͧR×·Êèk××9•ü<OÇçÔ6éìtn¸ç³DÇ2·Y0[–p×t5}õÛ_XêGN_Jmýn§ÒR‹½{M~Ó§$ÖPÀž…}?ü8<ˆÞ±íÿYk.nвî[~´Y7À}¦òùׂÔb:ýôå AnÄùPñ}'çS¨…®x k\)¥aÃ7çïÃýÅõ5½{zÓƒ±¯¦ú}p%ÖE»§-.³¬}iç«wuàA䕼URâ|QÒ¨Ofêpé¢bºÏÚ0v¡e7ùUù{pñƒÎµµx½Å@)]¾tú‘Ë 4rÔëQ­ÞÅTå7$cíA|ƒx?Îù\ü [dÔæÜµGi¸fü²öæiôK1Êc×÷{Õù²`pÒMkyÕõÍǺF'·õ»ºø"_·Óhl‰çǵ¹÷H·|ìãHf{ô´E‘A|ýZÌźÄzeÉçî^ Öm&=íášFg„1y²{T5X–övÙ7è„SY‡îó¤ÐMøÙß={Þ:¶Ö,Þr}y'„žé²ñUÍo¯:žññöáÓz/äâÝǧƜ§QÎ õC÷"»Ö}ø:í=zÑü¼£=~u{PÛÃA¼o5§ÓB'{òû°Âê<-e—5N¤ÑòOcGNÜ#ûúnö¯v9“«î‚¢S§Ìøõ†;ÏôN– O¯œ]+ûù9úÒùL“óÒhEEoƒ'oïV¯;rëAt!o^÷£[D\ü ëììöÓá½lׯÞFE=¶ox.-ñ.Uù}ÝO¨»åÅë Úse‚ô[=[.~ÐåÞ­9îåã³ôãñs¦ÞÕ4Z{ºs‹]{îÒ9kì䯭C¶RЬñí%½¸ÏAÇ®6-=Kƒý=OF§£ª× Í´»4‘]f±Œž±§w{ù©^,I´áâ»:‘ZìKX»¶Ä4r¶4ïµ¹Ù]Jþ2L|uÌrÚZâ†]A"Ïîé«·p:)t±Qíš±ö¥²¡+W1iT¯ù›“…Eäµ­(? x9¿Î‡ë/5Äãt*èº5ôQ…&œ¡¸^Æ÷;]Dv»9vl9­w¹aë¢ ¢×oÝ<ôà¶×b{ÖMê¬ÙÚWïÄ´¸ëiôÌ$ø¥ïÂ"âî÷—Q¸É„é6¹A„b8)å7·S¥Â¶óã–ȇJï4 ¿]L=·Ð ‹¨ãzËz/»Óƒ†Y &ÆÑý?\Fp:t{BïfLùuš¯‰(܈ïu¿SNãÓÏîÐHQý8 x\D¬k×ø'\B÷¡Öü 5mOS¡ÁÜÊÁ8ŽEÖuW^½C‚.i„}Ýh«îŸ‹³ÛsëW§hJè—ö¯•i´¬Özéô wHÚoÔ€Vn”f¸64)ˆYÛ¡ö\¼ ³Õk¢NíZ\Aœ{E]þž6îYêŒ]rº=Kx/¨ÊÏ›‹tf²ž'©ñìEåœWë·«Ùð­ß)¯v§kkÆŽÅùÈõ¿œN]ôvaþí~û³ñ½4ê¡3œ»Íûb,#£¡Öf“?U­rqƒîQÊ÷•ߟ§5ÆãçN L£’Sº¿?~›÷…_NÞý2ï8V‘Äœ}2áÈÅít©ðµ/­ü{!IÛÞ¬·î‡3ýœ¼ùMíïAUë:\ü ëpfÒ/‘v½>Ó½†+ò:çÃSHU¾Möç˜ú»õ¼ß§Óó)rîEò]-îÛ§Ñ´ýåú#¥…Äùª;ëÚ;¦Ž‚ºèn¨¹8 cÝÐFþØMY ÃŽL_‚ø5¿Yønu!Ò=˜q¢oÊ¡sâ<Û?œ]@⾟:W×» mºí¢„ ¬Ch =*´;&Òm3öÆÍ‰Üšdå•‘…ÕÃÕw8º{l[`¸ƒêèŒ¼Ó¨É ÿ-õÚR'ö1W+g~=7¨j˜‹tܺ£„>îù®œFÚWÕà—aßÕàêKgŠù(¾Ôû™4]ñâÎk)t7šÉf½o&÷®^9¡._9qµÕÖø’Ä wêJ!/Xc€ Zag1½Çrn?…ÐÍÏyØvÙ\;Ò•óiÔeôÄ&Íbó‰[_u!m³V—Îâ×+¸ÏAǺ›~{9—Oë]y¿+Ÿ”'—ž=tRL+ÕP…£¿ç}'¸¸a{Ým¿ßX>§Ñ°Mó¼~ÎnÔíƒV9S½^ž âfá:Ð=ÿá®s)tU¾Å–º†/¿Ú»F’ÇóëA´:i™|Ü(þzÃö''|{ªÎl&¼eúüš½ÒhGϯ¿åU÷÷7.‰æÌú{žÕBÇù•‰˜ž=†LÆþõÌrõh–“Gž"½Çã÷9ÑÎ}´Äê ªçÕ\¼|K…øŸ§F]pbô‡%Ÿm!Íãý©&kÓŠ¾²é–õ}¦¬á¶`{~„9iáz&>'9ÿ°øyžÔúãC1}{¨h%ÿ{|…¾U~^+íâ4+üö§°Qyd1(d½²£+ÿ\-¨ê¹'è‚§»ÿÖj #oºÄê΋y¶Eu×Ë£ŠSæÞ{âJ£F ûÔ½Ë jïÀG tì*A«ÔuLÿÕñ}{à<ô0kçÍ< él„ºÒžç¬!xáä`^×çtRèJûÚoÄÆ·Á;‹Dœ÷)cgŸ<§¡›¬í`WâêtÕm"~Ñ †‹³ º¦ãhöÕM ·Þ–F›ÏÝÜ䢡*ÿ”ìQì nÕú%§ÓB§[žMØÂ$ø8ÅßA>ø¨Œç2\CU~;æ‰Ôåó‚hò…—ß=¼¹|®w¶Tè×–qC›m ·.ˆó¾Í³éßi¨mz'?' GÞ¯+ˆmô2_Ï÷•П¹:F"aT £ÝÆ-L£·¢gV¦åÒ›Ño…Ú“.LÍÐ×LZ>-¾ßWB7m:ngX÷™6i´yàÛ,›Í¹äj8ÆH>ÁŽF}ýXtªFÕ<»ºbª˜Ï“Ð=6Ïh=9a;óáêÓ“/—¥‘îvkH.¿iKögç ¹ýKNÃóÛnXÀ_oЉˆs½ÐuóüÒ†Ãjô÷1“'Ú¿~›C«žž¾fhK»Ds“~È«ž pñƒîfù“FÖ;ýáo—ìJ£CÙéz{4{Ä>…ªS)'·­?'ná¯;è¦tuú¼áàÆßžuòI£ˆÈC¥§rh½‰ÝE½ý¶´;ï²{ç÷rZtôTF1?è¦â,lw12÷Ù>¼s5¸WZ1¹'އߋ7ŸËi|'öÍîsôΕ [é@ï`t5/§‘ ¹Ló¹•MrãÓ©íÈuPɲÀ;rÒÙ¦·äëtúkz,»ôf;Ãåäÿ¼6Ï[î˦*?’ù¬ q–œÎ¬ž|kߟ@Çù?mgN n¹>1ý¯ëÚ#²©öÂøËnÚÓê;ÍßÍ•ÓÏkªÕoóqƒnp{‡cG#%Ì™OvÆ=ï¥QÛâèœú/³hûkÑÍ15¨þJÖaT^õ}¹¸A7hUëÏQ7¶1㟛®ÛWŽ>#ÑeÂÆ³Y4ú©ÎÊ {:hÐiY‡p9Ý—~^4r&ÝA÷a”r¤[ìæu‘þÍ—ßÒh‘ó ü±Ó²x¿K{ò3Ro''˘øsÍ ¸ï§‚N÷˜/dÓ¹ÿð7'š¨ÉáÖí15²hÐAýïëìªß“ùkýÖB÷£ÔXØ2Ë“éV±÷âïNjJTŽßp+.“¸/>r=üÎôռ᯻ó¨§c:}{3o3o¢2]1HM?xýî½.³êþØ£H5{«øûè¸ý^Íìó-û0Ó\M;Ê7þ”™fRYkÖxÚ†X7PƒÐ@ZôÚv@Ë0N'„ŽË‹+˜„·†¾/P“ãsu÷Ÿ2ˆ}›È²‹ˆ~¶LÛ>?3>kÆÌ«(æïç ë1Ë7»ÄDÌX´J ¹¼LM./G¶{™Qý|¶Ž+ë¼Hû &cÏøøAw¸»²gÏlrýtkîv5 /|{ý¶{=)µ_¿ÝŽï/äÔsÀ Œ9?è^®|äz^¹˜Ú•u8RS{Ö9ƒNžßjÕ¦—­zø;­~ 9ïOÍí§ ºFÏÛ9‰GÍd¦\>h>檚(øó Ϭôê<æNmKn¼ $#«Ÿ;-öpëZè8¿aÌV]!UÓ¤ÄÀ±ëÒé7M÷1ñu¤Ÿ‰Á»Ãù>g¿ Uý— ±«wîªiÈÛ étÎÔÓædCG²¾üØèËš@þý/n]DÝ»ìBÝ ú°ªhKZ…šj·•/jŸ«¦oQûi²ìi\—šMü,y?X~= :ŸÁ¬Ñ÷ª¯»!O§Ç9½‹^îPWåò0@5fD 5÷ß<¶a}nIóš§Ó|miªavÆç.éÔîÌÑ›F«©^ô£{ûމ¨é»¦‹6 äýë¹u É…*Ÿ2'ª—·0ï™Y:]º¤Û/Ü?øìÙ¥ãÒj_Iþ¹9?ètË7+Üèú¾²;·g¤“¾Ó©é¦7Óh‚!Úôa5¼•ò賌lS[^^9”;?UÐå³ËvûW.8¥ÓµKÛ6@þìéê²|wØB »´tkPºŒ:éŒj¹û]-tçn ÂV‰Wçï—Nó‹®?é± ïÒ{ÛXʨVÝÉçåq÷ózK…œOÔjºõܵÏçÓé4\þàÂí®idÞ}mM_ýE¤³ýô“Ñ» µ2› âëtœoëZS˜·Ò4"F»Œrßò*•¸¾z1½½T/k¼\ö÷ûrèt¶¬åk©ÿ!Ïîéé´¸A@Ô±ðÔªuHj÷ðòµï—ddÜ+LOÞˆ;ž"èâVDO_|x=±OÉ[iÓ©låêÝù«RiÝÊöµýD4ªeo.ȨٜCK[ññƒîÕ`ç-7P_»"Û©ßÒiËó6ÝˤRÌ4ÖhÓ–’‹6½›¸Iö·õ))tÜû iÜËÇ™še÷œ%…V¼«À‘°%1Íš°g®Œ8bn?U«ÞËÙLœ_pM39çæã—B#6­_ijGï3E-dü:w-tÜù´•|½øãÁÈ ŠÏ¿ã³Ù:…·c*,íH[üÑ™fÉþ¶Þ§'-²n½Qó·‘î… ZÓå{J3ƒ”jÿI»J/Ÿæ“eü: §@—vaôéàÉz»š5¢Î —{šç3é7y_p[jTud“ým}X]aÓr£Ý:ã8­IY41²ÿ½çfu&˜v_°7 €_V\Òˆ;ÏDÐ-´P•½&!®_Ê ý­Ë¿Í°¸IþúÁÅwç/¥Âm·TN  Ê'ìàŽ‹:ï=³Ÿ=k¿®¾±v?¾?ƒF¯³ßø&¥>,ìso1µl³µ|ü¤U}mpÌÞz|ñ þ9·ˆÖ 2³\z> êøpqóCŸ£K¨Ûùó6ƒ¾õ×+;`x£j]ƒ:²Ozh»Ð9_^Îtû» ϲ¯Ó—²Æ_Ž.²§”u_ôœàŸKrë¹Bl÷bš´ä×Öëô£Ýäuˆ{ÿ  Êÿ–‹ ¶9»,1µÿuâÖ‡ìéô¥¨Ó14ÓiçÔA«¸¼+ÁvºÇ ØßÓ¥’%Ó°¿Üû’תúâ|výùõœÙ\ ë¥3f»Ví™><|ïçpê£+8Üó¶kSðn´zþµªó†¢ØUú½þ´ÈwQ—Ë©üº?¶ ÑÖvîžíô>ê‡j.ö#cKeB‡kÕï--ljÓc¿·?}eºöž3Œ«7zþ¥Âô ÷Ÿgž©èvðûÑ]›-¤úˆŸ~rÉŸº/aŸrÇM€íÚIæ·ê¥¢°y¯}½o[S𞟬ƒüIU{ùƒìÙ|íŒÌÛ&Þ¡¢IºÚ𼤮]%þ4àZpÓnoù¼…íê ôvš¶Û³ÛRì¯l'5îe¥¢@Ù^¿[é øõgþy0¿Žã’cqeíT<`ÊHÓÓ‹èÖ‚~H-þT¹Ìýh¿níNß·"û:C }DÙKèT@iñêµþbI@ W§UØ.çuKÛ æ ]þÜ{ˆˆ¶÷¸¾'h¾?Õ^µ¯kÓ \œµØ®AËp/Û½ÛI1÷ã^'ìïÓ /1É”eŸ­¬·¥æf<ß?ËŸ:–ì?°Š‹³^@©°Â¨æÍ&Ét¶cpò‡v4éj]ãÆ¦þüsYn;¶k{ÙìÖBŸ$š{M!›.´§;Òb×7ö§äVûê»VòuÛŵ]®zŽý¨Õƒ}¢™AEwâ õ&ÒÛàsŸOœ³§žºBîGÙµoÄíiË]¿"è–…,pÞl“HÖ‡º_>nOËR]¼zÿðãŸorÇM‚í¾v»æhs#ê^ö;ÃÒžFÉ yµý¹÷`»ðÇÛ7~5.©wÅ­úœ#±#ß͑ه^øQy„OÑæÅÜv*l'îܧr™×vZúùåôõØßBëFâÁ>ñÔ¬í¼wæÏliðÍðþËüˆÍ:#Ûò}Wû¼Í÷¦a<ï+kKWšH”!óýhŒ®³çó¼¬T¨WgUéýíqÕ×Ñë&ì“B?ªêuÇÛ­>CS~|VÒ¬s‡‡ØR”»¢êǯëóyÛ¢ ~ÄþÆ¥:GlÃþíž?l§ƒ’ëµ œòÛŽŠg¤Všlò£æªì')×ù>ºVû®„íN‰¥ûÊ”÷5tydëb™¿±w/óºs÷ql8rŒAÓ6±´15Ù¡äš½ž,ZÝä‚ñ¿£àŽ/¶Këè¼qßvjyº¤•öc‡hÂQÙÂ*¾±ÔX4RL½WL2ëçéÇ¿ÇÇçiè:e®\òüdtõ{P3uFÁ~ä^¯ëçúR.¿i±Ý¦iMXæE‘ÿ6Ö(RL–§užêÇûErÛé– }cZ¢èd4Û8»úã=À¾g-¸¸j*w^ °wÿºV8ÛlðÆþ¿5Û9’Vß5©4ÁzõX!9óVÊŸßÜy/„îZb³žcÓ#ªÖK‰û]”d׋Æyïäò›Ûý–_Ûzfx¤jfK+*v§Ï¹&åïgærÇÛ-ŸWäÜ+*œz—÷òÕ`siÕó8êtÞJÉ¥ãÜikùç›Ø>7€}МAÉcSv4º@ÏM¸õÔt?£É‘þ-oi±ýÞÇ]í,»m§ˆ; ô0$ƒÞ¦î¸.z–æû$ß1èàL\+%›¨´†/*ø~NQ*Üè±þöò !¹wè~5ƒ ÏHlºNö¡'ìki]¨ƒÏ}ãˆÛRе³`àb~ºÃü jì•Дš……ÑôèëÄúßž¤ú­:,58êJcÖµÁ­”´çš&]ããÝ©¬ðÄN04´e͸ êü˪󰈣ôÕdÓÝu÷\ù<(%ûwg¯[ÖáÎ?t-bØIݶýtÎ>ÂdÐåw–†©òç‘+]X̾("¥—ì²ß*>ß@÷­0ÜòÅ™­t·§ÄÎ!=ƒXèKíöT¿O…&ºÝÔRÒµ99œN ö½õàAê-ô-š½À3h•Àî÷×JH÷ÚO¬Í뻕}5ˆ÷{žÃÅ º¶!Ù¿Co¦½Å~¶Ë3hJš&_¿­%¾·Ÿ}=Gê¾ð½ç­RR…;ç:ºòý!tæ«·³‰ûÏÏ ª<ëÛê¬Ô™Ö=Y•X3Ïž J{e£”ÏœN/¸T¨:ÞºHØzµ»fÌÎf™ämñØ}»…Ô݈}ÂŒþ@·”_¯ãöS÷^’'ñN\mœI—ól× x¶„Ñ=Ö­gO;Rg]ÅùÂ>½YÛ‰;.BèâZ÷»®#ù¸Lš0vÄžfV2U¿3y—Ü´^}?þ¹*÷y"èØ.üèÒ5¤Wº«~À‚L>Þ™&ŽŠýÁíIâ»0æv?ê/ˆê”<•ÓI ë?ò&¹¿XIãu?pˤÈöFnSUÇ?þ,ô|ÑÃöó¶(®äûwè¸÷Vðïcd’îu®{™ªß‘5xzβa~üz$§SAWóö¥%-ò])Ô½óûS!™4ldèÐ….‡˞ͧšF:Ñ•»mwGûQ²làÂIs9ºªß}y¾ro™žIý¾¾ØWés”ÝôMνeÎ|]ô£ø¢¨Yú¥|ü.• «î·õgŸ˜R’I®×gN“9ÁT=°?cèïGw«žuÅ÷£Ðq¿™CO¬ÙÌžE=ržÚþ8rš©ú]×±Qw¾<øQÎã…¯mpÇS—_Çñ~ÜYôð¦iÉÝý¾Œ©fs΃+δQ÷‚0úŒsìt>~ÐqïMõc6X+L³GfQëíóê4õ<ϜٞQù¥­3uÌL(¯ñáïýƒºË¡ìMg&U|´\Eg7šÝ÷ï)eªž«r¿—òïÙrï;I¡{gyìù·1‹™’›óGî_Eƒ¯´xÎËáÞOw$AnÿGߤ4K÷àh?èø÷癫ìë0G³èmvÆÌâÇþÌP݃@G*°ìâ=¥ƒµÈo·sº>§ÓB×tÒüoF¸1o–Åí¾’Eì¯ Fȳ]½M7I‰’âþMÔ¶}üÝ\üBJ…‡'í&•x0†á3ôRÔYT)kQ_îÈÔ-šte³5n÷­§ÒÚŽyn1ÿÿ^t•¸Ó[Ôšø0ãžeQ¨ƒû¡ÐFA WÇœéä­5£óÖú÷ùüõ÷>æz¦ç´þô²©dÏ®Î*˜­§þ˜YèBs:[<~Âïoýˆ:&Ž}q#£[Žk—M–‡Ú-ykq‰ÞxænºáF%2“·+å~4®L:Åì#ÿ ]êPöB[ýy·2ßɦ>¹µ\ .3ó»<íykÌ2➇úU½¯ÎźS6uí'øK˜)«TWzÌȦ¥‡ü“Â^†2Üû5˨qàg£{‡ýøûÉÉ\ü SwU˜¸ƒ9r8äè—l²y4ú³KæUæÀÁ&ד—ûxÿ~ôåÈî23î=O-tk¯ÿµ“aßÚ˜&ɦàº!ß›iÂî>Ã~˜.è„~ßr ûK”±\ü.— qóóÎwþnÆF3®¦ùélZå;ÃÙìj8Óø±ãŒšnär©ç‡#É~ÔcDfßqáÜû¶è¿Ú/<›º‡ñÕïéæðlulpEÁ¤º§_ÒæBML>tüqÓïK&qñƒîN{ÃèÅ, Lxé®Î¦J·óÑ"™ kSýÕ ÎûæYp“8?Ú7ÁÏñqï{Š +?$~gÑ>fuÿ”¹Ž²éÁ¸ùFV?#™‹ ·¬+u"öW’' «aí:€;.è¶ç6íºkÒ&pç§mM>dÓâͪ÷·¯F1ºÇ­íœh½îÅóªë{Y ]¿E/ìŠ9Ȉt?tÍ¡Ukc†ŽE3ºv²…5\²Oø©©?9tQûpí\ü »÷,õã®î‡ÝãŽ9TG’åÒ,†14 ºúÜ͉<{­M73÷çß“äÞ Ö²ÇÅýi÷ßÇ0ìKúÒ!9$lfbS+-†á~¯îDö-°g•?Í`_ƒm-äâZ*üe³¶ÔÂÙ›)^ñzÂd‹2Uê?¿"‰eŽ+ØŒ8S÷ÅìåÏ÷½Üq@ÇÝe§t\ñ~Qɲ³zî!%Ã?/!óòÑ' .ø“Û]Q©Rî½`!tbTíæ‰Gj7ýÌÙ9´ÎþÀ×Zq ×׊©tÚÖÓOùóï{L`ü™}ôgö‘ÞŸÙGfýëÌ>bç±u=@ÏœçH/Ðô}A?ПÏ]bþú3+éÿ{VÒÿÊlIvVR0F2ýw³’þOΖ¬š¿]5[ò?cw•GÉ_½Ü$‚ÜóV* Š˜HA0@A³ ÀN|€è£Ø ÿIÏÛÿ™GI•§[_D«|J¼þ23É€÷v~ÎdÙ_üo=þ2—›õxüeÞ¤‚÷Á­š¡dÍ{½)y/\vŽRÕœnÖïMù—ٓ¿xâªùyÝU¾¸™ÙÍz¿ þ2‡2Œ÷3ùwó«ý—ÞëOßõ§ïú¯ì»þ•{.¶ßbk;Û[±ýÛC±½Û/±=Û±ýÛ±½Ûï°}ÛÛü»õ3úüyÅöŒì~X P‚ V‹þE|€è#q'e@€Db ¼šO*fÀ(€"ÉX/ •ÀIG ¤ `g,€(A0FB úHNBà Â@ YYo æ—ð  †HdVÀ ¨@%0Ab)(Hr@” ÿ/Î}TJ`‚)>@ôÿÏ}¬š“]5÷ñ?cNv•ŸÈ_ýÖþQ_Z5_Ì€P-0D°^@* †HA0@ñ°ø'¡ªGø¿}]æíþ×@ª¼ìÙxhõ¸Ï²^@* †HA0@ò° ÀÉD|€è#±'e@€Dc ¼šO:fÀ(€" Y/ •ÀII ¤  AY P‚ `Œ„%>@ô‘¼„À„2 @2³Þ@Í'63à@ ‘謀PJÖ³‰Ïh€> x‚0PHˆÖÀûàc/EÀÉRï‡ÁÎu®Äù§ øÙÍÖüÌfå¿©'XUßÀ†õÏ3ž?k ½µ†—^âÏ3žþ¦Êƒ>Œ?Nìße ¼šOjfÀ(€"ÉY/ •ÀIO ¤  Z P‚ `Œ„(>@ô‘…À„2 @²´Þ@Í'N3à@ ‘H­€PJ`‚Ä*RP d-€(A0FÒ úHÀBà Â@ ![o æ“³ )(HÖ@” #y‹€ÏÿÀƒ^´À‰ÝH€Tc$zð ¤/ž ”Š€5ðj¾ ˜ Z`€a$@ *€Ãx5_<Ì€P-0D1±^@* Š‹HA0@¡± À¸óóÃø«˜Hðû¬þïò¡ÿG½V¥ÿ?Œ*ÿ0Ö‡^ûO ÖoÕ¨Aï#VåG/å½1ª|W­€'ãýÄØâk D¼Gë¿Z ØuÞẆ÷a-(Ò¼·ëQ¯•¼_F•«hx1Ö“Õ x‚0ÞgŒ-îÆ@¼yïŒG¯±ª>Œí_þôaú0‰ÞŸ>ìOöïÙ‡UùÜ+ùãÀn+>@Ãî;’šx‚0PHrÖÀ¨ù„g<€h! ð*P LÅ@ Š€’£%¨ÆH–"à4@‰S®ÿ;üîÿWÖ—L Q$­€PJ`ò_2Öó¾  ˆZð^®>@*y2   ù‹§«5%ïS¦Ï¾SÄÀ›÷vÕCv ‹÷+“òþ®ÀEÚŠ÷,“5_´ÿâóŠøBÎz½Z Pòþeú¬_ þ7õ0«êÄz\NãSñŸ>Lïÿ|öŸÕƒýé¿þÿÝýßÜ{Yð瞊ÿž&ø31‚"vßÈ,€(A0Fb úHrBà Â@ éYo æ ð  †HˆVÀ ¨@%0A‚)(H–@” #yŠ€Ð}$R!ða  X­7PóIÖ xÐC$]+àT ˜ ‹$d!ða   ­7PóÉÚ xÐC$o+à” ‚õ‡E2÷j>©› Z`ˆ$o¼€ T$}1‚"`€`$@ *€Áx5û¾3ŠƒHA0@¡° À…C|€裈'e@€¢b ¼š/0f@ ¤@ôÐ þ9/Ø0P(VÖÀ¨ùÂe<€h! ™ð*P LþI/Ø0P(ˆÖÀ¨ùâhÄ@ Ô ¢`Zñ~°RPÄPc`¼@(ú‹/¬x((´fÀøðþ°e@€âk<€‚÷ˆ­dk   ú(Ð&ñŠUí`Î+V¼€ ”q3à¤@Ãu3à@ Qä­€PJ`‚¢/RP ÐX P‚ `Œ†@|€è£9Oʀ͂5ðj¾q0@´À„ð*P XÖTú—~‹íª~§ñ§ßú?×oýYóúÓsýYóúßßwYóç—šÿfÀ(€–ýl$4+àT ˜ Á‰$; JPŒ‘üDÀh€>¡x‚0PHŒÖÀ¨ù$i<€h!’¦ð*P LDÅ@ Š€ª%¨ÆH°"à4@ÉV@ôÑH'e@€ÆÂx5ßd˜ øKÿ%Òûç×Î4ÿÝßWôßýwÕ?÷‰ÿ³ÐR!;%ÉpÁ1FÙi÷Ë™ÊÔM¬¡ZUý·êàê§?Óã~ný´|~#²¶ qóÌiÖë‚w>Ûýyÿ€5´jÅÖvN`~¿ËŸfÇù¾Jð9_ÖúgXWûϧr¨;žüX<³Á3wûS}r6ø[ž‡?‰3¦“âçk&…Ž›zœáæPæåøCãŽ-H`ÎÉYCL15ÓRùóó¿¹¹ *è,^XTfy‚z9¸oƒ„ªqìâøÜ‰LkËÝBL6.Ó—:øóó@͸ùЛv Xv‚Ñžß¼Ñ=3‡Ú ë&ëv/‘©òkÞQcWÓî ýùù§¹ùWJ…ËüÚ¸û$ÓâÃeY«G9d8<ìš³wRõ¼ŠMs‚Ïõ§Ä —Æ/ßÓ^w\ÐÕØ×Ãê¦Ñ)&©tÛûõos¨ÿ yBa2SCÞ¨ƒ&ß‘D™}î;ùósþŒu:!tŽ?ò?xй.Ï xò;‡ …*:¼If¸ù"Ž4èØèqÙÞþü¼ó®:ºF£í>Þwšq׿æ’ÜùÖ¸nfÙÜæwë-p¢Kq÷EŠþü±¦\ü »ø¬tbíi¦õþ5$ȥ춟Á½UL•¿Ã²±Ýê½òç} “uñƒn·óé[|Õ¦I‡êÌ¥“¾-È; bªæ‚ù] ¾Ó¯f¯«ÍźK¾‚ò1zg˜Q:ƒ–\2k. þ bnº÷øjº+±n·]ð~ ¸øA7zBÀH¯óg˜g™¬qK.uN\çrÃös-äiÎTwÚÑùVØ£UŸW8F¿«¥BÍëî‡òFø2úìø÷E¹$ì°í§[á5ÆG6(¥CíedsgÇÃöÄÍËçüFÐYT>|$Mòe#Z­Üs©uˆm»Ç׆µ£k¿Œ¼œØ‰bU¾Ž\ü »ÏŽõZx–éiâ„r)ÔèØá‰×«}/ ç/v§¤¶¦6Ý;Оanɽ'qñƒ®n@òYÆÊ,!zêž\rŒšÐ±QŸÌ6—™K›¸‘n¬y‡šÅÚ+µ²äâ7çù³2Õ¤§øx.5Éj¿²ù‰Ì›,vð™ -cÇTáó¸9èÜçI¡V2dB«_ç˜7ÝØA`¹´ýé2ƒ/7˜9¼ìÞ×ó¾ÆüÜþq: ºŒƒ±'—ž=Ïpþ%¹4y“ñº»snVÏa)“}9tutï-Ôé´Ðµk7£öÊN˜àWknŽÍ¥g7¬§È¯Þ¬žƒô¬Ëì1µçTå.“…• ]‚ž­ÚéwÉpë¾æôÍ\šçdùrEýÆâÚlõÉN4geIë«莘u¢›Âźî9ûûÆ\d’âÜ×ääR^Öq·)ÌÜAz8ѱ ¥Më^àçÌs¾¶BèÖêŒí.2Úý)Î¥©É­*¥z^Œ÷ô%_ûæPŽG­1tÓ‰ c¯¾ùFRfCgvrn.E˜\¬—/HeB÷{Ÿé(&Öm‰¾P;זDzʧqñƒNUïê’{RfY»b·±osébÌuê©Ìï°ÀI³G»Ð ׋‡ÓÉ(úqØŠ.îRèÄ9ßNØGH™# «‹äRbÒB•§2™j}®u!ñbÖ)XFW&Çv_um2?è Nšg½ú eØ)†µ4”&,+/š•Æ$ ŒL_Û¹Ò®)içœ_ðóƹÏÓB§\t¬Ö:S?æñVU­Zú2(îyôpbso튉v;\«Ë:·3Ò>pŸ§^*lµ‚u\õcmn y×HC³^5¶_ßCÍÌ}fÛÇi®4öN²d©2€n•‡„ÔŒæ|mÐ%™†ÍÝ‘âǸO1ÛÖBC*µÇ㧇ÕÌóCǯ'¦¹Ð¼;~¾Ç.Ä ÝÛþ%÷yBè|?ذž?£´ž¹ÓÅPCWzyß]ðQÍܬ۰§Ë/1ÉóJíAÇÍåãë~¹ÚŸ4ïìïù5T`Å:Y¥3U>ˆ_¦XM ÷S™æÏp~Ôè&[È–ýØçÏŒï8Où´+Žççè}o¢Ò™‘ºÇδâl°Á‘›¤ÓÕÊš‹tïšTÈvgú3ÞƒžÔÔkh’ÏÏV‡ 2ªçÈEÆžøòû™r8Õo+ï_ç¤T׿ýÖ fÉ ­ ïløÙ5ƒ‰lp¶FµùuÚ-¯¦Œ¯ÛÜ~j¡‹Ê2j¹hlÓî˳aS‡h¨(dë"§´ ¦ÊwÙêכަ=d¼O ç­Q*\||ù”ÒLŸd¿Š»#4$96ÕgcÏLÆÞîÃÊe 1)ëÆX†™ÉxN'€îI&k„ÀŒÚ½8Øy¬†.~}fáy0“áæ¸ºR—c]Òn–ñ×;·ŸBèĵÛßa^0Ó¦¶±û4ICÝr}o¦|Îd¸9Ü8?oß8½ÏHF:»äœŸ¸:Ù¾õ’žýdLÚdÛ‡%S5ToLÈÊbû,¦Êç9èùk+C½m÷10–¸ã)®Ïõiæ'me̹ë–uZÍÕЫ¯¾O#ò²˜*ßXÍFG-LºñZÆÇ:¶JÕ:%cB÷ÕIö_¤¡>!†´͘Moð¡“³+íeíf{ʪæ.sñƒ.ë]ƒ 32dÌK³­Š!vJJÏØ?J–Í =rCô« ýœf'œN/ªT¨³I”3M—&D=ÓТÔÓ¾Ó7k˜pÙý-‡8Ñíærë@ïËí§º©3JäLsÆ®q›8 µôMþ¬a¦Òä¾t¢_öì`6=œÃNjætBèVôz~ ©MÃù¨iÈéü©›¿îhªç8.i³ÕcÌZYµ.~ÐLÒ6¥ fBËãÃTYrä5¨n³¹/ëŒíD呵®ôq’јã?? ñ÷Ð-jÔI¾euÓöÐæ¥ï 5T÷’éÎ+ƒóªëû­Eµu·FiëÊ >~ÐÍ^{ÄýŠo£½8«{ÆC ]¶Ÿ2±­(Äpt¬Æ‘÷«Dý‹orë„w© »ê8¨Cn3zóãqåòØb½¢pÃÞý´­áH}š·(¬è'£½mÙ†…û<-tdºgùç æŽœ50ÓМ§›Ì¢ò˜³ÙŠ´q^þþÆ2~Î%÷ýô¢K…)Þç¦Þï­`<òÒg½ü©¡ŽLõåÅyLa|cW&ÚžòWI÷Þ*£W»Nk ëÙqñƒ.ùkãg)˜ï'sö:×Ï£ ÚËù=kå3Æo#ãýÂrñƒî)kïõVÁÜÍN Í£-oô¬Ú§°x÷³‡¢dÔ³XÕQ1›‹ƒ :ËF5ûìmÌ k§Z$›GRÿ#Ò=£é —~l%s¡ë×Þô>'£“}{ú}ÜÍ-t‡ŒÙÉèÁŒn<éô<ò™&±ýºú?OÑ…v5û^Ûê ŒZö8èäø_L©pÏÑ¥…oæ3%î#j*ä‘ÍÛ_žW>ãç`‹I¿´ù¤N^2juв¼öîº@—Ê6^ÁLïWW8å‘[ÿcy‹Ç=£™LÃE‘'iµ%ëTŒº¹ñZÐð œN]£Œ!¾‹3vw>¯›²*Ö6~!ßÙçMdm'ë;Që{›÷Û «ž?­‹tý­/5ô<Ììrt/sÚ–G݆6 éßú5xyô¥Ültoeõ|iý‡üõ7×3˜)(¾x`ÏÁ<êè\:Ú°æ3ŠqT-°nЕ¿oÛqs’=œ=üu =?{ÛêµO)ñjÆî ƉةӭßSÙ1ÔœN]Ðúw[ïo fМ ®üžG£œèà´ô)™Guœ½ÞÑ™ Þ3|ìYøIï8”ÓI¡sXT÷\ ¾_·ž-bäÓ¦:‘³“ÌŸR»Ç¯Çê«Ig¸WFñª§áN|ÝƒŽ» ffGœÛ•ß&ŸºÌë–ù¦ÿS~n²3{An[ÿ~žh¡Ó#m…ïwï×É“=ò)'`¯y‡6Oyãÿöyà Ëî÷ãûe©PqpÛÅ•ÌÔvkç˜æSöå@IÔïRªê«úi½çóñk3ß—syLќ໵BÌ …“涘OêMÇÞ*+%§Ö~E |(dÑèžI+dÔÄË`~ÎC. ¡3¹W²{§‚™¸#Øéö¬|šÞä[¿“ù¥TUŸêO²jÿ]ü ójá3ýåPÃù åSÔÛ§gÞ'–’A··2¬i+kƒÖZVå/ÀźääÛæeAÌ­HÁ‰/+ói´,çXLP)½èµÊK9Ü‘œuª2ÚÃŽqnÉÏRè¸ë-ˆáü†òéÈŽwÍ•Ò‘Ž¬¡¬# Ùejt}={Ö>oôKîóTÐéì·-ƒ˜c¬½áÑ|²røÒÿí¦Rª/»yåõk'2ßúÓ =®W~®:?è>o è8쇜ñlü-¿ø"t»Kë÷r(åýîÅ|ÿ'ãý•frñ‹+6<ç^k½TÎýÎŒ Ë'£~#~…Y–ÒØÃ×',u%¾æ×±fsñƒîWoÕú³ÃäLVÅÅåRU>•NÎ7=> ”6}^,ïqÍÚÖb;g}Ûꚸæ8!tAu}s㊙G†5%ö¹ùÔ§4ðó–¥t®MÏä{3ÝÉûâ¡ÅÃpñ~\ü ó=ùzüÃU̾¬qY>4a3éK uìt½Òå¾}ëýµ·€U¾]\ü ãîeÌ—ÐÛû2ßåSñâ—c¿—ð~®´ÅhƱ+gdÔåhÌ»ž½¹Ï“B÷¡d@¿òã2&U}PÍj·.§Néµ:•ÝáØBcÚ1tøæü}2ÞG”tºe¦Ž2f;&ºy\Û1ñHpIµëÆá¯’í=eÔôöïáY§¹û-tÜÝæ9ºßn4ÉëôÔøc%¼ÿ‡ß >wØ ˆ°àâ û°gé¦u0sU£s/- ó߆6—ÐÅŸœ 7;ÒIãMS†ÛÈÈ;yrT<‹t9ÊK¯ü™wSX#(|?¦Âò˜sIµO_ÖÒ˜ó1æ2j÷5ñIð$n}AÝVÃ.£î™ù3\}- Gß¿é:«„,söŽ:âHý^.^n"#n]‹»ßA7àÀ¾ì´h?æè„ÖË èþ®ÛfŽ*!;Ëny™ÆNü|sï_Íé$Ð=úùniô ?æ÷s›¥’RÚK»äw/áý;(¼ kd¾ŒÉ™vv%¿î]×nvÛ¯J›îž( M‡Z´5)©Îƒw² (2Þ[çQAWàWðay_)sl#ëQ@­õ§¾«ùù í[ìQw#¦zõg&KѼîÓ4v)·.¡…Žó ¾ÈÜÌ݆[€úr˜YðöþªÑÆ!óýZ2ÛÉØdªªüõ—P*œºb[ÿvƒ.0eÉçî^ÑPМþ_'ÜxBUsÀ#n==!ñ‘ýmýKÝå9V3?õ9Ï´k™]˺´€b>ü,ô ~B‚ü6õr\ˆ?«ærsñƒEæè™†ç˜fíûž“}-à|ŸŽ<¡Í¦îë¶Ñ…®±í¨¯Œ÷ÑÏÅ:.Où2ã=§Nk\HÅ¿*¦ŸY÷„â¾wèÜ/GLá³ÆN¼uUFC<»~P?èúöëúéBÆFgWcTHíƒCÓ/}BÎ:Ã?gúaæÛµúΔІe÷†qñƒnòg}Óñ—|ߨàøIC ÉxP¯aæOøõQgºº~[âüP±«@Ãó{qñƒÎ‰y³æù¾ÓŒw;ˆ¿FÿÞ[Gßä {¡y±a˜íëþôÍêÍ2Ú÷ s˪–\ü cÝ›Í8ÅTT~¾2Ö¦ÝymݬݪZÏßB¦Kã'˨†WÂÆVm;rñK,Ž;U§Å¶–'™®Ó{Þ-_UH®µÕ~B~Α嫣iÓÀ-^÷–ñþO}¸øA×Ag„pœ™0¿Ö‰=…´wãËÞ<&4U£’f9‘áÉÂúb#?݈‹tŸß~CF8Æì­ÜÛo´o!ýX·,ÃãÎãê:›ØaÝ£2ÞW¦‹n]^]eÛ6Mv9et6®¡…”àø$ü[òcêP—u¬pùØ; ¦Ò¾ícÇŽeÕØ4–õŸ êªXÁJ ¨¨ˆØc¢.… 0*h,H衇D Š{ìØÖ÷:9'¼ì~ßó̳ï¼Ï<û}ÃÎüfvœ\sBîsîë—s_ôãòã§YxngG4;úÐÆ C·ÿlWÉõÆ~±Ó WÒÇæS×—ß„–’!nêÍBr7LL„»ŽÔ]7‚.'rDÚ¤³{c×­®ö1'ŸÂb'Øxì,­è¯E=}× ŸÏÈïìkÛ¦ Û~ÐýÈvF)¶;¶omÌׇù´tÿëßV-.åæÑ-É—g‘¡´v[­O5ÚñÙöƒnýó„5!3$±™ç†œ«õ1Ÿr¼–vª7¥”Rd)Ué IÆLw_%ÃðçL¶ýbÊóœ9å»-–ÍõÑP®ù”gƒø¥tÇ~aŸS/EÄæL}³Û~Ð1_ãìͱökG§µÔЗ+Â3½y¥dbX`s£œˆ;ï¥ð#:—qøöý†õtLZt“Ò±ŸEÌJ€†¶xòЦ^)É™8åþ®d˜¶9Ê[,ÙöƒN»‰YøZ{?¼EØk ý$Ý¿÷ÄmEÞϲ”ßZ†›kJlûA×{ê!mCïX“A»[Õ·ÕPã=|)ÕR7´ª`¡ µ2f(—÷mǶs=·7ó%ž±}ú|J¬¡¢‰2ùoYZZ$εñ ýc^>û–÷ó¨Fα6/ç:OC͆.hÞ↖Ë?r¡¦}»Ô¹†ry‹Ùõ"èL3#×öhj{(õL óB }û’¼îQ˜–Ø~{y‚B¹¼SVgr«Lðºë÷˜æI/ÓK WjÈã|@KÉLŒà!WòkÎG†²í…Ï+Ò7Ü{šåB6 ¼ICKJøÝ3Öj‰ËK§nÍ;jÍΆ›Wñ«á:èjo6i=ÄÇ‹ši¸Èj·†æÌkpf–îœÚ¡ÝNw*+²ï›u?”ËÇaó„Ð%v°°ku‡ž21‰‡4y÷ë…¬_µ¹bfÓÿJìxŸ]oCÇÎ3¯§ÍüÆÝ¬5ôð¨ü€–†g pþš*¢øo-ú9Ô–Q“‡6žgs¤Ð±9Ö›HùdG“ø )Ì'øNµÐ»Ž ¢=c7öÙVGF†XÛLöwTB߇IVßFìóª!ç—ž¬«®­ð»™¿zlÝ…:‡<ärR ³4LÜKèÁÊï›ïÉ5Ô#;Ó.+ï>ÅægŸØ5MD[k'#Óáµ{LŒfs(LbËû7#仉­4$Á·ÝrŸÌ†ÝŠY¤v'ÛÔ²ü,k™8¢[oÍæQñ ë^çr]ëžû錄™ÀÖP›EÝ×zß§I3'Üi6×b,ê×ÛÒOFå˘ä*6ßI]½>-c¦ ¾†FC ­Š“žºOwÞn}¿•;fâ,dÆúm?èÄ#›YÎorˆ®MÚW}dœ†2ØkδºO‹ìŠ]žÚºÓ­Eê£%¿‡ÒÆøÂ×~\n"t;Ë×É>Llޱ†Þµj_ßGWBÆ<w«ÄNoÞ†Ò‘‚è˜{¹¼^èFJz +õçÖ!4T]٠È«%Äÿ°©,ÐÖƒÖœ®þj5®—¨~VÖ ‰Ëë…îºHå{x\½µG§Û\B™¸†ß’“b…sh+¦o-Øœ(->o=Öu×`ÓãÄÎâ÷HYö¦ÚøŠá¿Ñì{àIÛ÷^Úп¯Œ[äò¥”e‚8UúqGí *Ìá翆Žõ…:æ²ÚágáJ˜Ÿýq¸ŒØÜ./ºALìH` Ù/¹û)<ýÏÔ&skçÞ£&LlxË¥”³cü¾¡2*Ù·Oðy¶½бÏu0©êm°˜SCm×oj|ø7?¼„%t ´Ã}Âú—/ÝôÉ7ӊ¤D§ydqMCòoAß_:ÜãònSmë³ó­dÆu¶Ý”Æ|Ñ“Ä[3¥«M”†úÍnX6 ý=JPXŒ\4j%=ò……ˈY¥œ´Œk7è«Ýº{sÙ)b×Y54yÊë·žÓÖ-1;; ö ž0¢È¦ƒŒÖºGM»€Ëƒ.úGµÛgç†ÐçÅQ­Okèg]§!-®“q ™ÿ&¯£Œ6­c’¸¸öƒîõRõ¼õÃàÓ†õ` mØ·¿¥ûžbzÛÍÊâö47.ÏYF}™X¦6l®˜I\™À¿Ã¾•ËpŸo~pÃ_C[wwž#,&•³õÆm®\Ý/£é"†8l8ºw홂2Œ"iý•Õ  ëí'-ïû8Œì÷]Òt‹†Òìæ¿úaZLƼ’ËÏKv%Ö”ç¯ØöƒNó˼Æ]“NïX£À U2³Ê¸=½ø.MÍøEsÖ†þÄ$®„sÈÙöƒÎìRÏ€òpJ¸™r¬«§†Âníy—Ú_—ÞrèN-%A‡Ç¢csYºî‡·]¸°ò ì=Ų¾“†ž%ö©ÍÛx—²¦0(î„N½Uש2Ra‚|Yº>%ÉncÎÒWv¿Úã9º¶½u›Iw) hJÕ?£¿-ÎÓ´Ýv¿Ñe°Ðýû´Ð©ðí¨õ9ªq4åø_4T—±ÃvwÉÑIpÑÉÆþOžÑŠ˜Åa#†±¿§I|™ tíÆ-Ÿ£¶näöƒ/dª{yYDÆu뎓E3?í—‘u¾ó¶ÙùöƒîëÜ‚ã7ÏÓ WûÅ^á¯ã?Ý}³ˆ«ŸDÙ´g#÷ 2ºçô3%ö{  {{ñ†`즪?ñâB3 ùi¦<âí-â|ÎÆ8»vÁ }<Þ®ùáS®lûAǬV?çG’D1øH UKTø|Ÿ[TÑo6×5[}Ð[FÚjÇÖwgÛºž ¿…oÿI3\ê.ry‘ON¥Kf ±.¢N½Ž\íô›ŒÊ©Ñ~‹Œ|¯I^'žguRèªçyÜ«}:7*\/Èçr\Ѝa«k§Ï u£þL|º¿1]Ķt?œ4m œÚã®o‘”OÊ :›Þ)$«ïöê‚d¤82ïÄÞ#ìçµøüw‰ÓÅí»åôb<“@•O;K¦u:[X‘/:à]ÙñêÇd$:Üûûø)l»%” î„ÞmzM+§#™(Ÿ6M?>8um!]®µõv‚; ˜xû#2bײíÝE¥ƒ÷s›‹ôãgß—¹ÄN9;®Ƶ¼±~†•ÛŸê²ÿ´ÌXß²í]ß+feÒ­É»¹,ŸÚ ì%hYHŠˆ^Ãú´XD†e­ j½´GÌÀEl»A÷¥^îžQyi­ÛXññYù3º8§¤€Ü‚S,³oz’“aJF¶3Ë¿Ì:Ê^O ;ÿq‰¶9={2aÆcÆ÷YR@¦¼è‡¾.¦é;/]=üNFí›uÏ|¼Œý=¥ÐíšV²Ï륅Í)Û;Ÿî­g:²Ü…×µtÄštö—€»2rÚp:uÏUöþRB÷íÁ”#W—èg¦›l•O•®5ëõ( Ã}‡gÌ»²˜Ö$MÝ?@!£¹±ÂAž³ÙçU ]´×Ìm™&—isÛµ6›äÓœAãj”éïpëVžÄ®ŸÈè{á~TŽìõLQW ’Ýœ1â2¥,XöãÀÓÊÙû’]ºkÈŠ ».ÓÃ'­Âróèww˱ý¶ß!ãþðíÎù;Û~ЭèÉ$×_&Ã6»˜ø¶ÇÖÈh%Óm˜»±í];eÇØ ’k4ÕHCycgW³ŠË¥ˆ¾º­ê_Üiñ´Ó5fN’·O—m?è®Ä^þyMÂ5z„Qk´,‡š)-ãÎ5ÖEѧiÑòÞ2’¶N¬ãóë?¡›»ý‡wZ×éÒÆ Ú æiýÍ—-Ì%¯ºJ’[@_›zw•Q´Ä²ëR1û»(¡»ñrÄŠÀÑ×iyâ¬uõÄ9”òÔfGôˆ\:†§@ããB»Ú•›ŸçË(„YN<ÀõŸÐ¹DícFlT³ÃLeÍ%9T˜¹gÀŸriÍt÷he;š$(è>CFe¢}ç¿õcýÁDU&øiÄ‘ã?«¯SNº¥ÿìY94f³Sá9d̉µVš­ÇsĬNüt‘ý]xÐi˜ióäbÙ­hº]Ý–ø¡$+‡B-œ'79íBÞ'¼.dÌ–ýÔyäçͬNíïÇ~Y'¼Al}–Cz±ƒWYXMZÞǺÇ:-psÏÁxѰÕ)¡kÜ£øíÃ7)¯…g§ j*iË$ «+®gæu¶K[Ô=u²ZO.bŸ#-tOG;NK|z“\—/„Õ©)aɾîn«i;FµmÏ- é©règŒ ™mÔgÙ¿Ï$þn—ÇÐÇ,ѹƒ*5ml:róÄ5ˆÌ¿Ì[@<ÃÆ¨Pn?(÷üAg븷Ëù½1ôÖù㥗QjŠìú>¸h›šËËu¡³âµA´8”òî­XÌÕ/©ÆœßÚrëýÇ'§Ô4¨ã e]¡šf l°o>Åo?íÖ/”>Ö÷I¸2Š«_ 3IJ¿E- …ŒšÌ÷õ»>T]±ß”Ý8ÂÕo¬N !VùÐ-b×ÙÕäÏ”íæjòd¶!Ït&C¬e›PÊYuõÒHwÎÿ [»©ùÓ[tªÞF”,н(0M|žMöÕ{½Ü™~‹­»Í”Jƒ£T>·‚9ÿƒŽq¡ÑÃci€ûú´¾3Õä4dJÇÞ²lê³$¤ÚÍZó+ÖÛ¢† ¶]–µt†ØôC±”œ°¶QÏ1jªî×yJÙ¬ìŠ|g‹ÞË'9¬ ¥+ºl äúÏ4Œo¶„N:§‹%ÃöÎþ¸Þ"YòúÙ÷õø•ÌŽ‰PâöÛ²í]j{&pXIS/×™µ©ƒš’“[Ù‹Î"cî§q¿·ÿ‡m?è®ôJóô«$Þ¼;} ª©Zµä}fg‘q_YÒýG^ÍPbç͹qtVq¼”Äîó˦H+f‡G&-Ž<þD²ÎÛÏBç[·.ëÏÛ¡s¹²`úÖJšu"ùã,u6™ûÍï²[šI©¼UMÂ,Ýi'3ì*¡(Ç]e=6qãvèØüp%}Ô»ž2=“Mƒ—my~bT&-z=uºˆçFWìŠöbœgÛºÐÓª+‰qÍï[³ÉÇ9Ú¶¡>ƒŒû%‹f¾ŸX ×ó0lÔçÆíжÁÔ‰#vÞ&›fìÊûa)Í Cœpw*òçðBˆñù`Û/½LÐçÔË€ë=â¨c|¬i›þÙ´£Æº¦ýgdP/ÓGëÊt¦°Ñ:÷g+CÈ,Ý­ÑØ^ì÷äA÷µÃ@GþØ8ºÕŠIzϦœá3ôwÍ2裚ù!u³¼½Ø>„.Ÿ¶´¡'Ûè&Õ*~÷Í#Žf.fU³È¬ýzþˬt.^Hsö/¤I!d°»†œÿAgØæ»5Ž<}НeQ«y¯øÏö¦sû¯…ôʶdù—š¶âZœýözbè¬õn»"(Ž ;üî<~?î“ê­„¿¦SëÃÆoRÏ÷qýz‡ÐƒLp2×~Э°jmrVG/š ú^ZE'®ù8­›N3ìøõ³¦&¬Bþ0/¡„.ŒÙ&šGL/ÐzHÍn°ÚIŸ˜F݇‰|îÍv¦eCô=÷×ÓB×_öájMm-VQIã,ª•ôìÙÓ i¤mö|Úç gúÝÛf…ÐÄãµG½3a“Œ2›aãu}šÑ¹kxi&¶WóÓ(3u÷¯Ê Î4ò”úÒ«ö!Æûœm?èÌ^”÷1«Ol]˜Iï­gnJ~“JµÂϸ.ZïLGÛ+Òš™†Ð°µùÜóa™©I<¹3N×{g&ÝhçßêìùTRµ+­åLÖo/umY~Šª»NÔ÷fuB躬¬~ÜÔ"žÞæN˜oæ˜IË}:4xë‘Z±Ó0MrÿÔüV Ý­à6}îuŽ'v_`&©&›ü {*–̨Sa'æI'nŸ'Û~Ðe»1 ¸ñTGßÒÎþ[^³W1û¥Šò&f>ªm;—:¶ïk;íÔê%tÚ^ÌÄ`<Õµš×==ƒÊ¿YðR‘a;çùÙÔî´Î®÷“„Á§ÿ๬N ]‚³‡vŒM<·?(ƒ\OŽhs}ƒŠØ÷fQ³ß¾Þf¤½Ìk/{¹ñ{f™€Ù½oå™A‘5ú¶±§"òUÛYt¼³ÿáÙ'iußÜxWöó<|þmvƒ5†fû4«·6-T´#<>¸4s;x{Ò¸?‰m/|¾]¿G¶öø^)îL%—AÉOjvzt/…ÆËù¿}ÖÌ¡ø­ÌF…St®ó¥Æ—zrõ tþÌvÑ~ñ”zp°ÿ*M:©Þß/ñ;™BªÙ‘<:qó§ˆ{_Žm/è¶ÕaÌâ)=FyÞ>4²Ç¾¶pJ!ý§Ý…§;s󃧸q ûwI¡ó4ýø휩“Ð}E:;Ü-úf«úüÁÖSî5Ÿb=Kéú¿ßèá|Ÿ±óÆJèb™m#íâ餸¥0qx:µî]$ÉH¦¥ãVGl}5Ÿ’O›˜c Gµ›ml¾ò1;﯅ΰ|×<žBÏy¯ l˜N¿½´8cº)™¾Î®®Ÿtf>–ÕìORÎ/G½t½XIV™`ÏûS>±õâ ÊÅ>…i”V[ÂßÃO¦%»ûY,8èL›§:Tï~’yˆñ„±óâ<èÞ¥î*}fO탺ÀÓè‹fUa½I4šÙfß^H»–{|‹ïq’ž*Úñgÿ>t=È»ƒçû½ 3c“FÛq·ÔV$Ѻèþ˜¶s)8ÍcRê`c.:;¯*„®¾a`…þäûû`J£ZS™oI´fPËæµ]qÍ›ß3¯ÏÉ?øˆº§¯™…8z’Ø®(¥^é·ÖHžÄÍ+8Òíð"ï:'ÿà[Rè:õ;ÿ`/ú½‘ÊÓΦÒÚ®£Vnm›Ä͇:Pè×Áµ>Ji†øõÀ¥¿q~ÝjAáá7á#†e–TÒñ‚îy•Hƒ²þ‰ ùùuòæx)·ÎÇÞ/Zè.¹t2»Gƒ&­Tez¥ÒUÞ„˜DbßœI÷ Ò¾K’UDl“ìõL²ËJ?MüàbËàêÂa©tʪڞø‰dÝøØÛá³èïðÏ2¥tãèWŒÈ8¿ƒn^ðàçS÷ÅÑöÈ‘Ò/uRiñ´q‡¦$Ò¬Þï¦Î¡Nk¾®È‘Ç+lûA7¬Iâjþ&øÁÊ%E{òT¤,=ë0Ð<‘›œKŽC˜;BJ†éŒé\ûAÇîË£j'? ‚P‘÷´Y¯Šˆ}͉^H›Þé)¥« øãS—³:1tº^íJVãh­ˆÙ¡"Þd‡^Á'hVðò_[~r¢'Õz¾}¾CÊí—c¿§:¦ºnmÇëUäfè0(Ÿy§©V_:Ñú»HJ6†…o®ý c߃‰£Ô”WÕ»ÔRQÆ×ïq Ü>' ZÍl(“rëëœßA7ª{Çé}âˆÝgŸBco—ê¬îÅû^˜u?Ô¦ÈÄSJç~¯Vqí§.l\Ôm܈Žq$Ýeq5 8…Ö|57o'§ðR¦áç’WôÄ9û¤Æu#¶ý Ó¬žöí~“8j3eÔ«E)Ô{ì…VÓ\ã‰ïVxHùx¹N±µ7”³J»·+çwÐõlŠ\_-Ž~ZŸQ§æ²òo’ùý»?‡†w0¹^xàüºMþ‘B‹7J:2òã/þµR¨YÓÄü7xŽ¿¼Ž[ttàª1—YÑ 2®ó²í]¿•>qZ%½%æèdªs;#:áz±ï™Î¥•.kü*ȸ~À¶tfOJk†å)ɳóÆv2ù×Þ0-aC\Åû¥ÅW^Oþƒ¿*¡cÜß6AIKúmðpI2ånµž8<®¢ÎéU»qÞ­kÁd˜Ö8Æ~O-t­ò¯f<½¤¤ô^ÛN¦žŽï~TÒÅþÛ²½˜Oü&ß‹¾< æö²:“œ2ÁâevœR3ëØ ™Îµ˜3ßâ´’[ÿ^@† ËÁ¨yÐ%è›àRRö–—}î&Q5“Ù×–OVóö…ÏGW*ZÎì&¿¦7®\Í­óAÇÖyJnÿ@Ý™5±|ö›ØŠõÈÐÉ›-Ö÷)³íÝTf»˜·’®6 áù®K¢‚F£Q ÆR3ý¢Îuã]i¬ó¦U9þ”õma#V'†î̤å;&,PÒ5¾Õ É㓨Íú–GÚ½e\Ÿãžû@n“ÕI¡û1—é`”tÃåâ«6IÔ~Éâ‰!vüèIIB—”@’{6Þó?è2L—6!PÒ¨·Gã^$’UØ*ÿ$˜ŠzsgÝ…å-oÒ¦\iéÛ¹ê §ª³ËMªÎqª:ÇéïsŽ󟈻/ªòrÿq^ïþJ^nåì»W.Ìÿí<ò¿z6fåóÈÿ•\ãyäÿ[gcþ+ç3ý'ÏÆ4žÏ¤­t.¦„ËÅcÎ!gÎeqY0Ìy˜f0L»Jç3ç13`˜s0Õ\~ž Ò9LºJç_úU:oœ9É‹Ë|Ñry¼ö•ν,ðßgŒK¹3—Ì`Êv@ @,aÒBîÌ¥¿ã™—ªj¤ÿQTUUÕGÿ¯ÖGǺˆéc¼Lªò€ÿYðŸóòþJp嬼WFËŸÏ ÿŸœYYù¼ð%£Åx^øÿÖ™•JP¬aB" À †dÄÿá3+}œËg1žWiÌÈc ‘ß›Í çΩ4ç²ñŒçƒ—÷ùï<i¥L<æ\p1Pp9,Æs)*.èÏæ ˹ó(y\öŠñ,J“Jç€3çPj9LØH€”ë¿ùù“L=c«UÕEUsGÿµQU]ô÷¯‹þÎóFL_ákR•'üÏò„ÿœc÷Wó„+çØý»òTþ|®·):ðr <˜#ð*ÎøÀ „ÿé\ï%SÅx®·˜óð}€è% F€˜ÂlÀÈð`>ŽÀ¨8#â/´ÀÆd$@ôÀF%@ LaZà ä@x01GàTœ¡ñZ`ƒ³ åÀ†'RPÌ`~v@Ìå3Y*–0Ca¥ì:ÓJùÁL†Šð¸Ì:ãÞ&•²SÂ+eÕ1gwK¸¼`&3Ŧ*â2êŒgvs‚™¬=—Ìd¥5—Kg<§[t€3v~@Å3ˆ@PSµø9ÐŒÛøgâ|à˜ÃÔí(A9°†É‹€3¾ÐKBÔÀÅs*­/Wª“x&l.×]VÕI&UuRÕRU­ôŸ®•˜ç]lR•ùûÏ2ÿœ;÷W3¹sÿÎ ktú" À `Ä@ôÀ† @ Laà ä@ ÌaöÿbŠ/àñ  åÀF#RPÌ`:v@ @,aBBÔÀ†$¾@t€ƒr~@ Ê5 K¤ ˜Á¼ì€(€Xâæ‚ ¦06ðr <#ð*ÎôøÀ „-0‡ Ú —÷ËäŸXÃE•rçÌ*åü2¹'z&‡ŽË› j.ßטw"¯”3çü¸\_ÆXùÀ‹Ë—ÓóJy¾L¾Iy6Ë—É6‘‚.WΈè%LY€˜Â À HA0ƒaÛ1P=°„ APS˜¹ø9ÐÌÝøgô|àÂ˜Ãøí(A9°F! RPÌP0§²‹¢R½ÄԫꥪzÉ„­—þîµRUôÿwÄçî7¥IU>ï?ÊçýsNÜ_Íç5fÅY£“)(fè°í€(€X¢‚ ¦èÌÀÈðй;? â:z>ðá@ ÌÑñÛ P‚r` #)(f0; :ÀƒA8 P‚r` Ã)(f0;   –<è€PÆXøÀ „-0‡ÑØ P‚r` ã)(f0!;   –0%!PqæÄ^ h9ÌÊH€”k˜—HA0ƒ‘Ù1P=°„± APS˜œø9ÐLÏøqÙ¼ŒòW¥l8óJ™¼JPÎdÅq™pRPÀeñÚ1PTÊ‚‚.ƒ×F*¾\œð*eïª8“e²w½@8Ðr¹oö@” XÄE@ € Ùø‚p æxðí(A9°†a‹€3˜·ÐK˜¹50…± €/àÁèPq¦Ï^ h9ŠæôT PVª&UóIUõQÕ|’Ò¤ªNú;ÕIvÜ=U•§kòótí(Aù_ÌÓõ*®£æ/´À·=%(ÖèÈE@ €:u;   –èä… ¨):|ðr <€#ð*Î øÀ „-0‡9Ø P=°„YPqÆÁ^ h9ŒÄH€”kt ¨)LF|è¦ãü€Š3 >ðá@ ÌaHö@” XàD ¨)ÌJ|èæåü€Š32>ðá@ Ìalö@” XÃèD@ €Lψè%LP¸<]S¢ø9Ð^¥]g–|à–Ëϵ åÀf*R.7× ÆjÄ@ôLŽn¥¼\u¥¬\_ :Àƒ ;? â ™¼@8Ðs´=9Ð ÛøgÞ|à˜ÃÌí(A9°†¹‹€3½ÐK¿50E ¾@t€‡¢€ImðªJõãóUû”ªê%±ÉÿY/UÕJUµÒ¢Vräîæwe®-¾@tÌçÑI9? â:,>ðá@ ÌÑÙ P‚r`M¤ ˜¡s³b z`‰ÎN€˜¢ã_ :ÀCGèü€ŠëùÀ „-0G'i$@ Ê5:M‚`†Ôˆè%:T!j`ŠÎU|è­#ðªÿbï?À¢h·t3f̘ی3ÆÕf̘1·Ä&¨˜1¾mÆŒ šÐЀ3‚`«ÉM´ÉZÅŒówWWáÙï¹ÎœÿÌ™oæÌì£×õ»öÖ×ÛjjU­µê©§×Í'^C rôˆ(@50@b)P]$i!p  ´M3Pñ Ü8 úHè&À (A 0D‚9ÈzHöÆ@  ù‹€¨. 8‚P( ¦À(A 0D¡5ÐEÁGª€Ä8_LŒ€ÈA>ÐCq1 ÕÀÅF¤@ tQx„À€* @!2Î@Å%#àä è¡H P€j`€¢%R º(`Bà@  ™g â‹›pþ@ôQìL€P‚`ˆâ'rôP(@50@a)P]I!p  P4M3PñÔ8 ú(¨&À (A 0D9Èz(¶Æ@  øŠ€¨. ±8‚P(̦À (@50@¡)P]m!p  PÄM3PñÝ8 ú(ð&À (A 0DÁ9Èz(þÆ@ °Žð¬ûš¨ÿ¡O±yüú}í¯bâþW“Q!Ü÷´CŸª¡JRt;òb¡â \øÉeKTÜïß›ÝVoVyÅŸ¶spuÄN{M¶çã=ƒ–».®kÖÚ‹÷ÞFYm\…Ì÷ëo3ç™ñþˆ™¬ìj%—?ïGï†ï dAoß¹t}¼ŽZ¾ÛmØ’µ[6Àßÿ+?:yÛÛÅM•D™½fqä8íÈó'ÃÏ =i8=t¤Õm–⽩)? :Ö]tg5C}ÒºgŽŒ£Öý&' 1 § z,¾BDÚ±‘ͽþ>G:ZÒiXÐC†î®ªŸðµa½<ðж8ŒŸ ³ž÷q—ë"ö1“Ÿ#]ò㟽²|÷¼JÑ© –n4íªy½0Œ]½ä1ëÿ6WEŽ¿oiäõP²›¡8GÖ1&–ÂK_Æ7Œ¥m½ x¼ÍŒ8¿n­r_ÕãV7ïB ÝëÝÇïÍehøø“¥×%±¤<¬”´J«u:]™óËŒ®®} {꣌æ«û[0Ž›#¡ÎðÓڽ݊HÚ”‘´(–о3ÏUçt¦Á‘Én3Ìù¹Ü²Úy]ZNV…ðz$kxE¬ë§¡A,¹ÚL¯tøü€Zç{²žbNŸ¦Ô)Šv’;%îY5? º‘s7\?E¬k´ëÊŠxyùÚµsúiæéÆÃ\—ÈÈnÿ÷+Ó÷ñó<²jç„F‘‰Ã=߆Y1tþÉÈ€¯ª:¶ÀdFó¾è-9½VF;‡ }sçS]êý΋"­}à•VRèheBÜxýõÄûÑ“æð”ú]¦ðóã l{hë(zUgqzw Í+àtàN0±ÓìT‰ø9»²Ú9ð\ü [²Œ½¢#iUÐ%×ï b¨Cø¾enýƒûänß1Üh6>§jׯîýæòsß¡»uöXTø¥HÚx%ÏʼO u)3ˆ­{ý>im7#Öñ~TžÄÍQçç§BW?iü±áK"©ñ·­[ýŒ¦Ý[Ûè½îŸÌµàÖ‘˜µC^êYë§ÄÅ/»B8gü(×ÎÍ"©Ë+Å£YÑtA¼qÀçkA´k¢´mwoÙlÖ0w³'õ]äžVnÈ}NtÆóûnlA'îîhûÔ?šÞ/ë)ý×Í3]žyRÊÑQ—¦¯ççé@ç¬ì¾ÿçž’ù(Êçí¦ _ZÑ‹Íe¶2…'E°öÎ]øù9´ vM,v„QåÞ{ŸŸTç‹t‹ÒÆ•{¼¨Ö¯É“÷éâ} Ëx¶kLû¶a¤µK²PÒã¾?ùÑMÞWÍœ÷ñäçVq÷­ºr²PÝ ¥.Õó'MT’ã#½4ýð_­FZ™QÓl³jÃWär¾²÷Å(~žt—\Îí^Jö±F–JŠûî¤óÖŸ:š&¾ze=-àn T+ubŽñó¬ ;ÿ(õîÙµÐKÍ(gHGvÁ >ÿÚo¿ÛÅ=¯qÏ“*>þQp–ÏŸÐíX2jrö>ù„ÎÇ¿ÌP'¯z¦ÌÕßóZÍf±Îp2:þBùê.üü~è¸y> ÊÇÝî4ƒ¡3“žÔ«+»Bw¬uí1#&ãLçýcd¼ ?Ï ºñVŠ:ÞHkkð3ŠBï¼O6q¨õC4'Í >)³ dµ¾¶\ürQ÷æ~w}èêò|çz»(DOmÔf”i†}tïfAº7fÜmvlS{>~Ð]™—3104„,VoÞ¼dcmÙs¡:ý…MøÜmÈØ×¿?§-ܦO~žt;ç ç†ÐÍtÛûFѧ—[¥r¼kÂ`r°¤Mys×7’Ñ03Òòç#‚îç§&9iÁüœ­Hò¿vw²×ïyHZû }ûÕíW¤äü$ЩQEK—“vì©K$-Ê:ˆzÐÜ‘ìÄFKºþðmÆcOÚ¿íy²õ.nΞºsN,_QtŸ¯‘$½{ÀüH+7ÒŽ%=gIî;=}{…zÖÎåâÝdÙ»ª¦÷é쯺“%Èkº7|5µrá}Ü,IÆÚRGy’ÉÙ \¸ó©N±“5& "YÁu£”šÕþä¸tÕ…ßslC©¶U}ð¤ñFùúçùû/¯B(~¾ý¤Ò4ˆ>¶6wÛ<‚õþÑÈ×è¾K2¶ ·ïËå ‘ÕÆ“‹túkJ»ÜÉ ¤ys;˜}œAuìrî4¶9Mמ–˜šè[PùcÿâiƒeµñäâêáŒ;‹é ;6²a µ<’Øk‰/÷]9Í‚´cÈßxRe{£ñ} t]»¼œ×,é­Ûö>.œëë¾pâ!*VÄ/V[ÐcÜe:ÑžtœgNüüMè"Y»ºG]DOÆ×§C¹âÒö{ÈùPk«ÂŸ–t²½|èžµqáâÝê­3¥Ð67÷ú›ŒÃiÆÖƃú88ÔÎ#kËoÎVËHœ<ŸüOòóä ãüžè¸:ó7§þ̲Å~>&t«¼Æ'ÓɆ–ü ŽØ)£h‡;ë&ðù:‰1³¯«Ñ]ú6Î!Ý y´Ž¶p›1—þ|wõ&;r®ig§PÉjý¡¹øåWe¬]L‡;¤OêF¯½Þy…NÜɰWóšRûßýËŠ¢¶gOÚr÷‘º°Â?,?ߢҺOt7£½ewæu|}™9qšßx§ üœ}ϲÝèz«ùü Û]ê%ߤvNÆ AÞ~b¹ÖÔøÃÍsÜ@Šó];p”Ñ‹x“ù÷oòùº®çW¸yÞ ùø%_CévêìYñÁÎLÑȼƒ¥v´°Æ!–‘¹ÖŒûœèôÓ’?õØtZG)¶¥†’¶¬v¸È°WuÛñ¶ü|K¯Ú¾‹tïv±F%þÄNï¾èJQ‚ë§»2wª‡tx+;ý¾ÇS=jn³DcÁù’)¡3ßÔíëU*ÞÿÅ"Þ!”î|™»ðø w&ç\/ÙsÖÄNlµIÆû8,çâ]ëv7v0;—…Щ„^k¢>úðuQA×m: …û2sž˜–.m)þ=ŸO7üýuœÑ"Ý<¯š¤ Tó~ù{/3hÆÏ/¹*&·þì€Jï¿Í•@7Tcyôt ±SÜÎI4¨Áëš_eø¹äd5SÓÒ›ï x¿/è8ßfOÞG[A—ÎøIü®1œo­ Y}Ýz²÷ßã]·ã®×¶Nw#iéÏÕ#-Թ݅ó»è:Ãù³Úë’y} ÷ßüë4Ðͽâ¡ñp!ËãéÃOš(È×ãÇ-E7nº +›÷फ़7]}·ãpÖA.Þ:…ÂÑwŸßjŸ¦}3Þ4UA#|Wì?²ùSë_l8£w“Á/¼h³ÖÀ’óç@W3Zë€CùÍ‹Ú[ŽVÐul'Ç_·í¸»sbjäÒËBðЫֿ˜‹tœ?ñ)JœÁNWЭ!oÏ•¸Ë,î“\º(Ýš o}]½!Þ‹êõv¶:É]_"è¸ùÀGéÁ_–WfõWó1bGÜÞ†ŸI;Ŧ§Âr½þv]J ÓLMl1à­^á”ÚSA;¥ÂUú#î1÷.ï;sÖŠæT¾Ò½ÖÀ›²Zv|lÊ}N9t‘¯ÏìT¼ƒ6ÖØ¯ÄñžyŒl~âÉ=†÷u Ãõõ]È›Ÿ¯Ìé”ÐÕÎA<Üe¶ŸÂ-i¹oçÀ9Û¬èªûŠ9ul¼ù:Ã}> þ~ÐtíàYæÌÌ’í8æÞØ%-bœ‹Ý·­êiMnu¬¢y“í×—’'¸xëU¹¹¬ÖÌÒöGÖ´¨ ss7šÒü>ÓÔ¼éä³ÅdàÞ(@u×›¦[ qØu‹Ó  ãæHïdŽÙ‹ÄuòuÙ¨E:¹÷Þ÷…FægïZòÝ›Úoüb<{7ï³ÝOÖxûÃÍgTÐÉF2‹›ÁÌì·oì²#AÓŒ½åü`>à)5,k™}(ž²¼§œ¦}¾ewŸóá”C·ývä‚¥>瘱!^=º)hVq”oˆXÁxO”íþdqs³å$ñÞüHhÌùb*¡KÛÖcÙ_™ÓnÓ"5È'ý‰¬ž- ej³†¢hi”uï)½äÄùCqþè8_IWƵëãî!È_76KntÆÔú›·ÑÃÊi‰Ö0ûœ:ÅB.ï»3ÍÎþäÒ3T;eTªÎldÐ,y¤-íÛ¡ÜJÎÏ×^ÀÅ:ÎgBÆl»ö@Õ¬Pú«ÊhÃÞâp†’|ü…˜Òêá“ÿͧU]‡CÇlÊŠ½.o„òóZ#Î?SL»O¯ñÝm'§DOë—sFOââ]jÓ€³ó×ø0½Ïì<óV(%«îŸ›!Šd„Ú©ÖÄN7¶–ó~¼0tìÓ™~ /v(ßmÊÓPê1ì¤û—îQ ÿüJ«„â‰{Ýä4~kËÆe߯sñƒN騰ù6—™É¬ýgç0úºž5¶‰b<¯>l¡¸#¦è®þ‹»•Èi^˜y™óCUByµQƒ+â+ŒW¯wkf¯ ãü¾n2̯€+3O´¡ê¶[®x5ð¡¾7{â‘p?è8ì¸8à*3šµé†‘ ÃÉKÇÆ+™êá±3V0èsN½ã£çCQG5ŠS³¹ø•T7~ÞâÝ¥?ó:àxêjM…w8囯’)›ÏNð·%­ I[>OsÇ@’ÿ*(yîuÆegN½'=ÂÉN=¡Ï©¦Ñ¿ã2ï­Â¿=Ž×ŽÊ]ŸBèž×KìÓ~Ù­áôøbè—Û¢Î{±¥±St»L˜ÊÅ:!ûôè›Lú׋æŠpb§µ:E3íX›(™ eu˜’þí—œz·œÖÞæñD.~ÐE8·h¹þû-Fº`Y³å"èõ¬öOÞ c˜ÁÏ/¯ocCŒ{½ú[?Èùyã¹øA½”5ˆ¹Ãl±ËÜp|u–¿Q¼ó‹a8ße1¿þ$§.©÷"náâÝÊ‚Iƒ—ßeÞ‰N–LºA1oæøÖ‹e¸zeMGÙ&§¯žák†Îãüª5Ð ]ðôÄ¢)LÉÔ˜Ï?F’øçÄ®#E±ÌÒá:K6—[Ñ· 3v=©”ó>×u¸ø•¢¿v·W‹‚†óu¤s{û8·e40´ÔH¬hòב3^Èéô«`ùçvñƒní¸ mî1¦åöãê*"éRI?ß°¶q̪¦Aêt+Š˜ 8»±ï?þ™ós†®41£ËŠ/÷­Íq›(úQQøec£µÏ(²"M~ÐÓá£|('ueãoݹøA·þ†[p[ç@†.õÓÛEYº3 óRâ˜Zo»Ù-f.÷¡mkYç¬\ü ‹þɆ1ã;ûº&?Š®]Ùèö x†Ë bbÝvÛ[ùðýÇ@.~ÐíÓ6AÌ+ ‡‹ã’—žr¿{>ži8Ë{YFw[¢M­·÷¡îóRpŸS Ýë‹-Péï3ÏZ˜Y7‘1tkB±MÂxæû÷Ïšà9v¹ˆ5jð¡§#Y§4]m_¦N¤mxï3ßèØ—wu•4ù§S»Þö Ì4}:½ç½=üÀ>ÐùÐÑvÀ=ïÇ]V!ä|†‚™ެS—’ŒÞ™p¾8ñ[Æ.<ÙÓRß—ƒ+÷ÖÎGŸ¦Õ  ÛËÚc–3/*šÅwݤ¤»‚-ÞÑs™FºOªÞî±£«zÜìàC^û*'ï>1W«B÷ÊÝ9W[³¹kù‰“~Jª¹¶äxàƒÄßýûË(sÇ—Ö>|þœÏõЭ ‘…̨ÿ€‘„¶,,RÒÃïñ‰»ª˜™[-*]£Å¿Çùlkuèš°ã‚O?`¶ï…;3ššú^Þ±}ŸŠ©õ™’}þµ9æ°õ˜PÎ…Z:îßQ0ú,¿W3/šú똯b.`!¬(zü—ù.>üÜì Zºâ‚åÝ._S0ú}ª†~pЦ>£V[Mbj×õFzo}ðâŠï‹>•‹tó>ZüÌÊÜí½ëΛØh²žf³©úHSë¯-7’ì/c|hËÂÁ=O$Íáâ§AÿèϬ]Êø|8âüþg4_Íj—XœÄp>½–ä¾7(íô3*y]9il.~ЭøÑêh9aŒÑ‡ö-ߌ¡™-¯Ÿ•ÌÈ>D½ ¶¶¢]»Æ-¿ØÚ—j}¿µñƒ.t¡ÅmŸì0†óO¡gõÚ$ùœMf¸¾ÖНŽmvºŸ/MÓ>øsqA×éjHöäµá û´¾5$†>¶z¿ðE2cè5yê¯úÖÔAoeËu}}©Ü¨Ú6tw^$ÐÝ?¿lïÔGáÌ»WkE}ˆ¡;Ò–8ÍLa2ã¯^š"°¦õ±Yè8}I6 lDkŸÉ\ü «/xý|©EÓjáÜR±äÉÚ¾_Naº-y<.¡5™&Ýø©‘/Ý\™¹°…w<%tKnY!~Á„v¶;±-–’ÏT}¢“ʸvn¶Ýô‘Ùp†z¼/Ù .~ЕdÚ›G2#çÔÍÎŒ¥.ÞM6®Le®º½Þ¾h™µq UVúð¾ËÜyÑyT!ìâ6ó§HÉ·bŸ0b)ïE¿ÛƒRÛã—2TO-éxpM[rÞwc6?èV­ë}¿rus˜m FÅѶ” 1²ÆiLí:i‰ðÀÎwˆ;ç2‹tìêbqf3È»r—ÃÎ8²•A%HcLŸÜk‘cAÚvï¥ßÿp׋:­½¶1üoüqÝX&¾k —BÎ’ƒV9?©õw ØA÷·º7ð%mX.r:ts&6²«{,ÓAû‚$…X7Øq{žPòF«~,=`µRÇ—öõ¨ˆøu–»o%Ðí;?Þ¾çˆXÆæ¤ÝÞ¾ßRh{Æ‹VO¨sî’öGÖXP÷ºþÂñô‚\7\»Í]grè–v`Ÿ¼bïæ“×›¤Ò‰ÖÑ»=¡·-Wí™cN¬[Ñ%ÜÚt"ãò®º_º±’>Ëc™]Ê|/\I¥…ýž·O™ô¤ö=˜ê¿(u“[?控nØÚ÷7Òmc™ÅÚ©”º¹;zàÊknË›ÓXmcëCeö[ÍwÇp×µN%®ëÎìÆXæ¸Çâ$“4ÊëÐxÃØOècQÞ­3,È©yûù™ß}höšágνäŽ'€Î·¹²á4§XÆüÁÃ#®¤Qû¥NÙ×}òû=aíÏÇù“ðý't±£×]Še&fÍ9eù5Ì5›l—½¬¤3;Š«ÃŸY‘A^[ÕËA¾üû ®ßAWÙÿæ³Ý¾ˆß›ÄW® Ò)*#ß_š]I‚]OMŽÞSÏÎÆ<–ý½Ÿ@Ǻó¾ËpÏÙé4»WÓ‰Â+I÷îšBS[ªét°|øF_2è sµ÷9åÐ¥mïds="–é÷y|Û_ÓéæËðž5ޕĺ¾´»aGýtšû·ïƒ»^”ÐYɇÿÜ—ËÔi£Ø>fÁCZ£Ú¿¹þáJþ>µ'Åêù½É—zÖMipŒ»4ÐiíÔ±ŒÅùsA×Òº£ÁO«¬+‰™ÞoÎíÏvô¬ï0Ý[]|yÿWþþ{R! Ž>^•—˸í?îøâûCr:·IgæÜJ0š]·£7È®›ù×ò÷tœÏl,3.­î«ýÕä&*é5¬²Ö—–®+“F;à:‹ÇSbüþþƒ.oÁ¯ïËq^Š”]ý«ir«“ú;ô+©v]£S{¿;ÁŸ}È‚N^1Žâó'tœÏV,³¯{‹Ö¾{ÕôdWŠá,Jºf:Jwk¸5Ý|a2oêÃíBw?H Ó.㿊eì§NMMۢμέªøí—8ôäñ´Ä`ôK±ç^ÛÎõ!rèdÍ¿X7|Ë\<¾ªÁ0µš¶Þ-9––QA¬›Ê“ºÖ4(§ADj€¿Ï‚ë#•ÐõݼlÚ¼x~ví8:ù«š{I[Þ«øý~¿h8ûæÕ‡?Ÿ|ÿ]мP3 º¼"«q½2¨¡xUÚ8¿Šß¾÷gzè}B_·(¯ðÛ’v\©ó´BÈþièÜ y–‘ägú]÷Ê Óþî¶;WTÔúŽÓdEÝ&Q×9_4î>A—w…5lŒe*ލùŸA3’ÃÏ +hÍqáÇ–=mHw[ß 7^ûðë ÜsŠº„Ò“C q4/‹ {û:ƒšVTÏêWA=Ë®d÷)ÇÏ÷ýrá5ôÕ§ZüšÐœëËäÐwÈØ1×%÷¼˜I†Í|.mñ?ÎKm±¡"¬eð.nÐÍ’¸¯ Á}P_kà›Iôpéõ³ŸÊéùd¶Ó´&Ö•§E¶µ/3©±ïÊ=/j c»‹G©Èï ¾Ï^Zd’õÄ„¾SËÊiJ•|ŽÑ+ Xnê@Ê+pþËJo(·ª áÙÄfK˜X†ëÓ3)cù¨Û¡ªrº“¿nÇ‚nV¼¯œ¿^óV»~"€î£'û€ËlÔ>ÈdÒ=vÙ-°¼Ö¿Ž îŽ/Ùõ؇æi ¹õ !tÕ…Wš»ËL˜Ýg‚O^&¹uhþ ¬œÞ¥û4[:Ì’ïû}¸¸áï?dmÔ¥±üsi&õJýyb†Syí~$âžs}øu¯†Üz t}V­™ßy}Ê«;|{gÑö=Úçn)ÿ]—Ç8åžX«kÏÅ :é¨{ÊåÛbnBß–S!ûz¡ÌŠz:½w¨›U»>P£=Jè,5—;¢XæeÛ› ×oÌ"ë£Uvo§•SF·¡—c§Šé~•¬‰â©¿_a8·^݈“&ö æÅ2§›—¼ly1‹^NÐ8ä ,'“Í]ƒ‹mˆum¿ÑØ—†­qÊé¾Þˆ[/{†þ(3ƲÝhÄ{‰Æ¢³"‹Úè ¾6¿U9i—ÿ;ÛQgïëyƒ}ùõ^Üz'tÜþŽXf«Ö1‹:µ–œÿñ1}ê5ÝÂ4ÞŽ®ÏIv~"žO|ûY.š:»ï ówøqú•É—ŽÖÉfƒ:÷-|L§  Pî´#¿ú?ŽtæKK*ô6o0–‹tZÃfP+ÿŸ“{gÓ¯àƒ ™Ç¤]¦°ùÏ Ü};Œ»ï c]И¬F×ôæŽyÆÙÔqꛩ¯<¦Úuš}fŠqèsøu=íz ºCv¬Az 3ŸÝb›MÖžoë{úq­Ùo?ȼ.•Ð=xq¥]Ïf@—ÉâÏdÓƒgÛâ‡í|ü»oDx©žƒ¹z;Ž‹t/íÖØÞÃL¹×näà{ÙtN’Q™¶þ1q¾j–”Û}S^[G¦pñ{^!ŒïÌ>°Ç0NCü7¹eeÓÌ×7}˜û˜ü¶=[w>Ù‚½-¥¿p}ްš=i÷~CÝf£÷7Ç cøçülÚ‘1â…Ñc²wI™ñô»)’ü‡Ÿ ÷¡N_"_ŸÉ­ ¡ëºtžÞöî1 w¾shgÚÇÄøžk÷wQ’³ù‹Ž1>ÄÅw·Þ ]ÒækÛ~F3Æ'T'ÆäйW=ŽÆ4{L¹Fgt6ZÑ£]×ó¥yÈc 6N¾sëêè:GÙ^^Í$-´±i¹§:®zöñ ¯¯“è5Ñš¹YT BAQ *ç~>9t‰…eÝ"£ö-ˆþ®:ûôÌŠ…¥~ï³Ó{²c(ê2×Oóï Ó.ƒ»E3·mgÍ+Í!M]æ{ý„GÄ­/‰É¨MÖSÔ¡NZ¶¨Ã­Çk s 0ß1š™è©Ü¶òA=”¼ëö£ßýØþÓ½qiúÒÅO ×IçNçâ÷uÈíÁóqK¢™¯Á³þÊÍ!·5ž²{Ñ–á›o þîø¸áï}Ín`‹fN<µ¯ãó1‡¾×iÙL°÷_‹IfÉ”úÒðq‚ÆßvpïÝ„Ðuc_¯èF3ZûÄ6¹Ôfè ùóGt=êýÉÖPÇÛ}uììK?¥¹WïEòïù {âœ=%©BÉ,Ðn@Í¥RÛ§ÓÇÌ}Dµë÷œÿéïçf.nÐ|²õõŽH%ÓiAýÍÝæçÒ¬àÍߎ|ôûþñNaÜ|iq#v'$÷þ_]× mÜ{»(™'élÂÎ¥ígê¼èúˆ´åú•)ûÚeOéKŽ&îo?‡÷iƒNq.pzæF%Ã=GæÒÚ“›Wh>¢~_Z1fÍ­i,ûz¾£/¥š§©;ù}¹Ð±«±;g*™ž‹'Õ_ê—K¯”o.½Ñð}¥5¥ö¯3¬q_£ÿ< f0÷9u^V/ÚoÛG d´6‹L.µx‘÷tOž†_OÓÆ…V‡æâ9oðÔ./æÎ‹:._0̰7›¯6.Ê¥Ù+]–´ŽÒ>tÛz½m©ÞÔevÏ÷åÿþ=-t[}ÞÝ­¼Ä0Ûš²F.Åt軸Ÿ†ßhGÖay³ö­ð­õÑåâÖPÈ0×£G6ëÞ*–L–ŸéyTC?Ú²/Š6P§……÷®G]é²lêc~tºIuû:¼ˆb^–t³t`]Ø7#ÿªµ†.U^ûWëMdÕúãÁ[}ÉÅõû‚Ÿ¯¹ýhr膰v….Q ›] gæÑ§ráŠ)ƪùºÒµ“Ï&r=eÞ{Ïa_¾ïã΋ºÓýÙ7/QŒEÀã%vëó輑º*È@Ãûqn¢VeÇW =êË×Aþ=;t¾3z$Ý*d®¥¦ñÝ“GÚeÔ&Ú̶!zéÇÑã û;úÒK&}žÇfîx:¯§ÍŸßyy0’Ñn¿”GÍ׿Ðu)(£ßînͲ²§ðé¢[›D¾´6´tôâÜy@ZSä%ˆdÞ…dÚÝË£ë¹Ö['û–Ñm_ù¾Â–_ö%Kv{ãN~_tYÁ¨~*®™EuÛPF‚ÌMë¥ÛP»-d‚/5l!~Ö$„»>EÐ5ïºéݪF»]ùI5)ó¸6®Œ¸÷Ä6d«?©çÕi¾µû ¹øAǾ%kÿ>œ¹}ErÿW|zgk2Q·Œî÷´õ}8Ó†6ž>Ý×»ºçÌïë„îýã¸åãO„3Õìcj—|Êè|ÁvZN) ïòÐZiCSv”&XͬÍcü¾Nè†e,èÎôqœÚ©Q>µ¶ èáSJÙm‹l‰Ý•õÍÈ—vj7Œò>³Ði—¡ï…1ƒf¬±{³8ŸT*?Ë×v¥dº)»IÖg[zÕòbÑ'ô#½¹îù‰÷)}ç_7ר×ãØ‘mwF$mʧmWØŒ*%v7ò_2;ÂÃo“«}ù÷9ü¾@èø}Z ·/.ŸÜKf}µýQBë7œ’U·¶§1Wû z+ñýÛþv!tY_YãâPÆA*v>x-ŸòfÎýØ8¾„¢oV"SÙÓ¯‹~rñ­ý?è8_^3^ÿÞø|r8˜¼øÞ©2U­î¸¤3ާ]7õå××ø}¹Ð-Ùèëòd™‚™ú$ORù$ÜC½XRB‰‡¥sn„Øñû+}i¢—KwE2ï3 .¦Ž 0ægœ[ý3ŸÖ¶£ß­„zœ yÛo€%?º0¿·Ô—v|¼æ² ïS ݯÀ=ïL0ÚtÕ©€´Ÿ]UL[j.·i|Õ–÷mö%½K3R_¾çö©k »Û§âWZÓÒs„ïý1´ñs‡'׃Šéj“}¶×µ%Ù!ë)â|)dæ&ÅÙb[.~o*„Ú75„io;ïý™Å´'nêÖš¿ŠïCT²Û³k×Kì¹øAׯÝé`&(˜i™¹b9å‹õžWLåÑíÃ÷-³¡á]YGc_J¾.[o;g?è‚¿ü•s®W0S´ÐÓïÌ™º¸ýõ ÃNŵûü)ñÐàßnùÒ;¶íõàt"覱¶÷î3ì.ñºv;¤Q“"ʽþnb¯V¶´º«Îæåw}ifEøÅ0 îsJ k¦ýbBSt,Oô °›¾Ç½"2ßÓº¥MR7i¶¢Ä—Z¾N/ÜÐ;/rèØU‹"Ë ¦|iîèørœ—´«z»‹(/:º4ÎÙžX÷òNíý¨¸×[¿¶òßk€îSºøæyU ÃùÑGe`ÆX*¢!†w»Òz#½¾À.Ðø‘¹ÙûÍüùøA‡¤²bÖÀ@†ë÷ iÅÓôº~uŠHûõŠ#›øÏëGY%ÛGWxóßk¨®v*²›üæô=¦¯vÃr!}u)õó‹.¤›;7× v I«5ÚÓÆÆi7„p:tdž±ØŒM\ꤳ ɦ×ðFž’BÊÞ{iïË!4òPòÌ/_ZövÆ“sÄ}N!tJöF `niôH°+¤âç‡vH……$rì“´uß&úøy‘£ú¥oíú?è8_Þ»Lõw_ëÐ¥îÛÒVÐäÃíÏ,¤c5ƒ¶%« ¨Þ¥·oF ±§ƒÂ÷¾ˆõåû>~Й·`wBÞb:‡t7xPH«Éoêž/ø}õ={­²Ñ_Ý™ÞTÂé”ÐuÜh·ÿ†› çK_HmãNX¬) Ú}~ìÛµ7}kë ?èfìíg©ƒáöñC·jrV뤻Œ]Q¶¡ÕÛ»ÞèŸäKªçËÎzcÇÅïm…óïög&îß”åý£˜SúƒNÖäS‚}Ò Ý16TwˆÊ¥ò…/ÅØBjäît[e3VØtÉÚ·.¢–QO׸}l6TÙt¥±²Ê—ÌŽ¬štþüF.~Ð5²,[Ôé*c¥ý‚P]ÞZ0uÙù|ú6ñÞüKol(cÕ„ˆ§J_ª¯5çt"èFi7L]f:¼z;e\)ŠH4tmþïõÊ¡#ól._ñ¥i†-†tÝÊé$Ð¥6?:î–ƒ32üõmã¹EQ7ãÂðþù´W–ízEfK‹qµ„=ð%v×™¬§“CgæuéþãξŒïÚj›ùk‹hDÃSÞçÑë>§3ÆÝ°%¯õË5¾t'¬EÎE î¼(¡³_'Z*gvxvc¯ö¹_•G}êß|^¹Ó–Úª§åW¨íç¹<¡®Æ{QŸd/¦÷€%c:üUD{zoJî{"Œ‡³w:®—N—5ý+ç%Ó÷ÿÐãã÷®B8¼3ã¼À“™®}P+¢V7²_X–÷;Ÿq¾Óøù*_ ÿ~Ó  ã¾ŸäÆXÏ NóÝŠ(¼èæô¯½ò¨vŸ™Íâígíq}ZÕ,c¢áó't[´ \˜ˆ-‘®QÝòÓÉïr©Fÿ–QÎJ1Ýξøð%ûÔNSG;pñƒN¿¨÷ºÆn;OYvgZûÅä’Ö^þ…5áæzÖùúìâm½]q: t'’Ù/6cªZù=mVD_»ß÷ñ|îïõ+÷>ulmqß²]³»p3?è¸ïWœf´¯ÏbŠÈûœîi‘y.5r=3öa¾5 ©hUl }Ÿm®q+8:¶Ëx~÷£|1Âz_ru )Ÿ°hD.… •wx½ZL¤}‘àK&£+›¼ÙÂé4нé:ªoÜêƒL³:Ê{Ö‹¨A«ù—ëäÒP“E¢:sQÇ.²/}©úóÉ‚óK9Î{äAí{Õ]ÌŠös¿/Ì."öv/IËáûc[ÚØÁýÅÈL_ê"-6Ìå΋ºˆs»*Z6ÜÈpßó,¢”Ví]sj¯+b«®»®­Xp;*l§¾¯ýþÌÆ5>îIyaMšr:M™h¦ ä;àjæ#?zyüNS2ÚÄź¦{ ÝMZI«w¾_r¬¸ˆ–øôõŽí‘S{Sñ¬i5&ûñ}2ÿA×éäÕ  Ñ›éaé¶¾ªòlFvµ®Ï&c¿êbÀ6–õOÝÃÝrüý‡MJ3öÒñu÷ŽžÄq¶¸Uœ;~%›FÜëãFry™¯‡|äö®‡§H>o¾¯ýžé!ªó}ñi8GÔ“]ÄÙdĬ÷Üd#@÷ß$Ô·6_pqƒ.j°é¦ŽÇÉQú)íGNÙ}õ}¯œM'G;ÙÃþw5%PÒ¸8Œ¿ï>T¹úz†ªGº½ É,¢G==Ãë~Êú]×?FtÆ#®/íïlñd)Ÿ7¡{|Ö7½xêyj¶ÌÓvp*â]šñA™Å¯wØÏ° þöú’ä€@xô ßAWsbcäÕF—èû™G÷¼ã‹ÈË ññû§³hªŽÃË‚­6äÑ•}‚÷¥‚~3‹$î|Š kÁ¦¿Wz‘ØOÀE”zm_ÿw+³j¿Ÿøû¼4ZÏîxäûè& Ôn÷¯»Ó_GR/MÆõ<8éjƒ¬ßy…«?¾´êÕú¡m¸Ï)‡Îu »óXF¬Ë½Þí"JopÜØÿC&áa=ìP>ê—…àá2_ú8„}²å®/%tŽý¼» —Ó§ºUéW7^Þä÷ËÑÍœ@=ÇñT»/f®&·dBŽí¸ü5}èl>~ìyy°ê|½·I»]¼K9÷58«h•Qû=CÊûò e‰6>ÚeÕC>~Ÿ*„W×õ,÷ë-Ö>(QöÁû~Ð¥úÐvì´êq¯d¢ðs!JÓ\\«TÓáÐ’”}[,È|†ã½º¾ôkÊYý‰ø¼ ]l^^X¶î=þýV!Õmí¿â›‹šök,¼´ =yò¡‘?|èѫɺ*WæÏ,É?³‘$:fIþ™ô_g>ûKÌ_ì¹cÿ}ãâIÿÖy’ÿÒÜíÆy’ÿ8#I ä è¡x P€j`€b"R º_ðçu+Žÿ ¯[1ƒ| ‡bf $@ªŠ›Hè¢Ð #U@€Âg œŠ/‚ÿls’œÿž·º(îBà@ Ø›g â ¿pþ@ôј' 5ÀÈéÏüÈ?=ÒéOôÿFÄæ>æì¹aõ&@  Q‰€¨.’–8‚PHb¦À¨ø„f€?Ð}$8à” "á‰ä=| c  P E@ Ô@‰QA¨$JSà T|Ò4Àh€þÿÁ ÉiÞö?ã I_(Œ€ð Âaœ€ÔC1ƒ| 'Àñÿ1^·Æ(N’…Ï­ð "fœ€ÔC51ƒ| ‡g $@ª žHè¢øà4@ÅÐ8%¨†(Žb ù@…ÒH€TN5ÐEGª€EÕ8_`€ð ‚kœ€ÔC`1ƒ| ‡bl $@ªгèÿ>·z(êÆ@  È‹€¨. ¾8‚PhL3PñÍ€pþô?æE²ýÍß¶ÿ}Ÿô§Gú¿ß#ýYGú¯Õ'ýGõHlžpäcÊþÜì7N@ j€!’”ÈA>ÐCÂ2 ÕÀ L¤@ t‘Ì„À€* @r3Î@Å':#àüè#ñ™' 5À‰P ä è!) P€j`€$)R ºH˜Bà@ š' 5À U ä è!¹Éÿfƶpþ@ô‘„M€P‚`ˆ¤,rô (@50@Â)P]$o!p  ÌM3Pñ‰Ý8 úHô&À (A 0Dâ9Èz(Æ@  (ˆ€¨. „8‚P(¦À¨øâa€?Ð}Áœ·­>Š’ púWxÛ:‚P(`¦À¨øbf€?Ð}7à” ¢Ø‰ä=>!p  PM3PñEÑ8 ú(’&À (A 0DÑ9Èz( Æ@   Š€¨.Š«8‚P(¶¦À¨øÂk€?Ð}bà” ¢0‹ÿÞ¶ú(æ&À (A 0Dq9Èz(ôÆ@  ð‹€¨;Q„‚áþ¡?Â¥óÇ·MçÏ:’DçOô§Gú¯³–ÄÞï>nìÏf€?g â•pþ@ô‘¸L€P‚`ˆD&rôÔŒ(@50@’)P]$à4@ Í8%¨†Hpb ù@ÉÎH€T$?5ÐE"Gª€‰Ñ8Ÿ$€ð ¤iœ€ÔC$Q5ÐEBGª€ Ö8%¨†H¸b ù@É×H€T$c5ÐEbGª€‰Ú8Ÿ´€ð $nœ€ÔC$u1ƒ| ‡o $@ª¾Hè"ù #U@€b` œŠ/ FÀø ÐG¡0N@ j€! ‡ÈA>ÐC1 ÕÀ@ðçqk€â$úWzÜ:%¨†(db ù@EÍH€T95ÐEÁGªX/7@' 5ì%C1ƒ| ‡Âh $@ª ¥Hè¢h #U@€"j œŠ/¨FÀø ÐG5N@ j€! ®ÈA>ÐCñ5 ÕÀÅX¤@ tQ˜…Àñßéo[ PÔE@ Ô@^A¨|Sà T|ñ7Àh€>švŠPþCŸ$Ôù—û¤?=ÒŸéOôgMé?»O2æ¯)ÿÙŒ€ä³ÇB‚2 ÕÀ K¤@ t‘¼„À€* @Od œŠ4Àh€>’ pJP ‘üÄ@ò¡1¨HŒ" j ‹$)Ž T’¦)p*>1ƒ| ‡„j $@ª¬HŠO¶FÀø ÐGò5N@ jX¿[$c1ƒ| ‡Äl $@ªµHè"i #U@€$n œŠOèFÀø ÐG‚7N@ j€!¾ÈA>ÐCò7 ÕÀÅ@¤@ tQ„À€* @¡0Î@Å #àü裈˜' 5ÀPðçokÄÿJ[g â‹˜pþ@ôQÔL€P‚`ˆ"'rôPðŒ(@5 šg â‹¡pþ@ôQM€P‚`ˆb)rôP8(@50@!)P]U!p  PdM3Pñ×8 ú(À&À (A 0DA9Èz(ÎÆ@òïô·­†(ìb ù@EÞH€T}5ÐE Ž Tvв3PýCŸÄÖøÿ×ö'ýé“þyû¤?=ÒïÉ”¿^ØóÊ[€?аÿ” pJP ‘°Ä@ò’—1¨Hf" j ‹Ä&Ž T)p*>éà4@IÐ8%¨†HŠb ù@ ÒH€T$L5ÐEòtþ@ô‘HM€P‚`ˆÄ*r ºH²Bà@ éšg â°pþ@ô‘M€P‚`ˆ-rô¬(@50@ò)P]$r!p  ØM3PñIÞ8 úHú&À (A 0D9Èz(Æ@  @ˆ€¨.Š…8‚P(¦À¨øBb€?Ð}à” ¢Ðˆä=c  P P„D@ Ô@É8 ú(P&À (A 0DÁ’5ÐEáGª€…Ì8_ÔŒ€ð Ï®' 5ÀEO¤@ tQ…À€* @A4Î@ÅG#àüè£Xš' 5ÀÅS ä è¡ P€j`€Â*R º(²Bà@ èšg â °pþ@ôQM€P‚`ˆ-rôP¬(@50@ñ)Pó‹FÀø ÐGq7N@ j€!нÈA>ÐCá7 ¬{”õ?ôG"6‡ÿO¿¯ýAüŸ}ªnN ìw í)ºy±Pq†úLlv{~záïß‹ Þb¯¦ø³Ãv¤1‡¬ò"-yßêÔu[ãc¥}hOÛ;3íÞF”P|x¯óýúÛÌyf"í‘$8Žv ©4†Ž¾Ý¶nh!ïC«¦ïÙA®V4íùð$Uí|mnþœý|ãGš±&ˆŒ†ÎßìWHn‰&õûµRïSHœ¥Ýâ»æÈE~®t»—ežØdpŸ&¼)Ÿ)¤mß×_êýLô/ÚŒ SkïZáS;ŸŒ› ;ݽó«ûty÷–ûv’݉ŽkRov¬pgP׳4þ›õ}»·a6?—ås…ÐÐjβéŠ`úâÅü)¤ƒÇlkñ%ý÷|í»Ê/¾ñ¡Àˆ›Ý [òóÈ ÓŽk:B»Gæ\ø2³º~½ ëåšNµ~£sB/Û†ûð>™Ü¼ôªy”¡Û~{¼›:eJ4°dõÔŸ´³&Á.ôL_?ÜïÃWszs.méû.>dý*¤iÚv~{¼D¯‰®×CéÖv€hÕO´,ΦÑì0ÏÖzædáøÒú^[º·1büðeÜùTBÇiž±-Œ\&m›¼/¥€Â_= ð1•÷s4£§‰ëVf÷óáçjòsu  Ée ©CöƒÔg´ç¹ïð–7Ré}—so=7£Ð3›ããúÐÃV_†\ÂÏ“«ÁñÞ[»Ô ­LZ@Cã¦Ïò[“J®7îÇšdN []»`¿Ý‡FŸýlÂǺÓÙ 7‚Îy`¤^*ï+fNâ—sFw³÷¡ÅÝþú0ûœBè,ÆmÛ]z:’yzŬ€ô¶¾î\?&…´ãǼÌéÚżÙUC|xßt~®t¬[ÏîEQ¼ï2~¾uWǼښBE_öÛFl3'Ïa© +䛯"NkóÔ!­]ü Úýåªwß:åù¥¹9i/—P9š'›ÿ‹ŸËçÁÐÖî]¯€ #M·%ÓÓ›æ;˜ÑõâiƒÓnËiÅ1¿›ÛvòóÈ û0¬»^Õ\%ìÐtjŇ|§;neÎùd~ÎòzºxxZa‚·œBí·¥/ØÌÏ#ƒNìÔðÍM%žÅŅ=«¾>6/™–µ”›ûED§›xÚ×Û)'­ ‘+ÿ}©EÍüñ±I4íÕDɧ˪3÷›$óùDDfs¿îé?ANÜ|s~t=·|ÝeM¥åsÛ„xçÓ•hUß%)I¼?æ:z6Ë-[ÝPN‘eâÄæ §BwIÁ^HÑÔÆ¢þ£ƒù´b¤LÖìL‰µ/×ÒÔ±w¦${Sç“t®ÏããÄ#þä‘1ÔÔåyݵVù´äêÄäÁ‹’xßõ5´8íÖÉ}oºî”5+$ŒŸ‹ÝþïW¦ï‹¡½[s&Ì̧LË=“Ú¶O"7ó]Ë'ŠÖ’ï´9ݤx“Ï,v2:?×êK­a •¦VÍŒíŸO¿—ë÷ÎWÑ_#؉ÍëèBùüg¢Þ”QÅìññƒ®í=§õdžÅRëyr§éÍò‰›««¢&V?VÕ[OszŒ‰m^ë“ÊǺ†fÞ:'bé`çþ«z¿Ê£E){&,PÑø²§]7šÑÜ3ã¯úèM7/5f²òñûZ!)y <ø8–ŸG›G¢c{v4©§ª/Jg;d>êqÊ›ÔjŠŽ?ÁÝGè\9ïÿ6.Žœå5ÞÜÉ£•]o2¹—HXÛå_Ô€ÇÔÉ›^Y±ç¸ûVÝWöv¹G:›Ô?›GKG72÷^•HK vN¯f,ÉE8ûea©ñ¾·\ü ËvÒõZUÝ:Y$ÖÙ”Gz/k†êÕO$¿´ÍÛ¼¶¤gÕz ʽjý)¸øAÇVÉGãiö”žÊäQü‡ô@£Û 4;ýøÄûg‘w‡Îk¥ùàÅÏùá}Ö¡«ð]9­í…xú´voF·¡yÔòºé‹Ý¦ dkØ7™õÊÙéUûïpñƒNZ¸¨Ñ®‰ Zw¢K¯r©ÁñW¯ë„ÅÓè†2½qUfôÙô«ºÁG¼±îÙ^G®®è|«N7õÆþlÈŒ sRsi‚Líì²)ž÷½0#Ö ­ñaÙßæ·  ã|ä(;Ô÷¢Þ­\rX¸óîå¾ñÔo(;aÐŒ&õΓÑR¿åõ.åó'tϲ|:"‘$¹ßkæœÌ¥__j]GÙëÙÁpfTV¼nâëU2â}¹øAׂµ!9H—ú?T‰séI]×D÷sqtãC›ôƒÍ)1wÄasYíÜt.~ЉرpY‰4y†ðH/ã\Úmï(œGµþVŸ–ÆöI”Q ;6p=w9tý<ÐSE¯ÌÜŸ­í—K^ŽK¬»¤ý* šñj;M¸Œ6Üqê´—Ó)¡³Õ\å¤Å—}ªŸKêÔ7SÉc‰›OnA¢ ºµ·•Ñ3÷¤1EÅüEI4e¥¹º±O¿>ëâZÞuV3§ùYÖV/÷¤Û蚀øü ç—œ.iuY·Çj–$‡ÚíŠX󻾿,=W”'q¾p|ü [z¬¸ÙMÕO¾ºúÑš:~p[³^ú14t£_ˆfT~\ÛØ“ºÅ}^¥àtèŽÜ;"hÔ'™žèDw™˜Cܼ¿h~.âzú°!zÑ¥]þÑ2`Ù+¾n°cÆrg«d’.ôà~§Êrœ=r:š\γQD¬›}å:rjq ×†|ÿ]èv"n2ݾܮ̤&›N—%«ZΉ¦Ÿì?ï.¢{'}MjèAË·?6‚áûèòs*úß|–L—ZNΩÊɦ¦³¢ïÐýÇKÿ‘S•ãþ·¾GçG…°Æ(ëð«~)´o7[á³iJÐ œA%ïk»žíftw~¾&Ÿ?¡ëà]6Ô*…X7 ýsÙ¤šà:ßyŸ’Úi ²Í(i[Õ‡ˆÎÔëó¶í‰f|ü cÝGø¤7o4›Öž4Lù»yúðîñ·:-‚N_·®EU~ ÉÞîÛ|}v6Éi1JÉP ¶ÝÔ·¤Ö†acW{Ñü&ï»Yóñƒ®kË…¾*úÆî/²¾ÙÄÍejçHÓfÓN5 ›{ðþ5Üý ‡ÎY›J­çŽ r©›M«t,†[­Ž"vêó¶ÞÖ´èUÖ[éw6=täþGü‹øÝ—g¬ÌxbôÀ|¬ƒžo æt:?+„'Î.P¥¥ÒG6 ]È¢}®Œ‰ .—<ü¯„ZRÁÞÕ$¸ç Âå]t¬²CÃ4:ÈÚ nÈ¢…MêÏaÎ„ÓØ¬£ˆ­þuç¡çG7ª¯ÔÉå!tÆe—mLF¦Q×_, gd‘rý¯{ß„ÑçªæŸÏ­2ççʹ‘ÃnÖዟg Ýíã ¢|W¥‘v _·,*_ðeïXÓ0ÞïÁŒTÁ>fžŒ[íGëé{wq#uÕÃwøû:ÙÖˆ*ŸÏ›InntUA~«;1Õ³ÍHk_ßÑx?u.~нm5¬Ïùª4j˜?óÚ½™ôzkoñ­”Ö“llÆ?¹ÑZãcÇ ÷ó÷ß/ÜGfgê7L'­ÍâLê="m@Ï]¨Ë¾ÔFõÇ™Ó1{Áû•n”óüæÍºÁ|ýƒn¥v_:­¸||§¤_&Å›²C¨­v€µ9ÿ¼âFe£÷èvœt^î)ÍŽKçýR2¨ô#ktB6› û‰Í)ùÊÛ¦w{»ñ¾f|ü 3]¼¢íŽ…éÎ^¦ê º˜8äÖÿ`Ê\×|_=srèeyòr‰´Öσ‹tÜ<Ëtóëî¸-—3(èÓàþƒ[ãy÷ãTû3rÎYpZJÒ¬“×gÉ¡ÓO•ªvL'nžd­Ð&ÊûtRó×À‚{ëÉhîHãèÅR:ÙùÔ»3?Wºq«oN|y$²ÿb |­&»| ¢®)áÏë¼Qvv`p”±”Ú šXÍæó't«ž î6ºÎÏÏ †W§ël ¢åeÖ«D¤ö8±D•” Ò?/.¿Â÷Ÿ:•Â|ãÖ‡ÊÓ©ý‘5m'V«iJʺ„Ðwôù¢g›=âuT Ë¿¿HJ?Â'mͽÆÏƒ‡îYË´ ¡WÓɧ͔FkbÔôfû¢u.;ký,ÈrÑÔÎ’æRªy|ò“c~®*t_r}œq;¬µHMÝÓ|£|½G«oùGl¶ŽF©÷¦—Üq¥Ž7Ó~Ý<Ã??@WÒdý(—€têníðÃBMa'ý÷í½G vz9¦·Ñâ)7Öîw%¯ÞkF|±â~> t–£Z•¥ë–²{”šœ¦ß;б&€ºÛ½ÑgàzX³Áé­»+?÷ž¿ÿþáx7 Ã>5P“E‹8qïÿaFkXû¼Ç?žº%×ê­X€Ÿ¯üëÎ}On=¤Bºáè~÷wßó„ ÝYZë_Àź~m{jôn¤SÊáuÏŸÎ{Hk&?¢7ã=íÂ6>VS¨?þQéߟÿêT Ï|\ü 3ôWÔ`Ótb]dÿÒK£|çýòW~û›M³›îQ^ìBâ3·¾ççùC§žº¡óŒÉé4só™qãî¤Ò჋îoz™Ü†}Ì´ ú¬=M׿÷ŸÐµ;YÖvÙÜ·l{¾ •Tí>yTø’ÔæÛòeî4ýÀ¤sýf¹ÖέåâW·R˜¹‘]1H'Q…å¼AÕ)4~è:ÿæRŸß~ '°ÆF®¼o ß¿@wvQãUÓ蕌Œ›B¢\¾˜&'ÝÊ}]ÒÎYÒdãö†V¹Rk/aÇ??@×ÍoÂû-ùi”Ý/4¶ï𢟙¾“’d÷züÑÄýVÄÞÕB+Ämÿñý't‹{}ñ1,Ö?êÖ=+™¢Tau^ºÓ[I`ÿåŽÖäØxþ>Á.W2úx B6•ûù$Ðq~¯øœZ¿÷dŠ>ç1AùÚ•÷AÓ¥)Ó÷ºç›ÇǺ›«ý¿ÛíJ£ÝZ£Ëdš×èûÀí…¯+~ -<ñÃÒ•BËçí?9Ÿ÷Ó€nûx³4ŸWžDìY¼z޼·Ìmÿ¹» %Ê2£¿âúä×ý¸øA7Ï»«Ãšaiôkm“–îk’hÇaǤËO;¥ß¸• ½LœÞ¢âÞ‹“b,߿ԫ²]bÓ&iĺO^ø©¢5êSgEŽÒ$íBŠ˜h · ­¸òaîÐa|ÿ]Y§üÑSé k$£¢C' cKè¨óûŸ4_LFÙob\èý‡ÛŸøúÝn‡¹ÃSIzôQ/³Ù*º^P­çvŠÐ]|qN¹5Uµ[01SãB‘‰n}퉋ƒºÓf¡WrœSiT#Ý'Uo){ӌû]ï´´¦ió×±·&ùº² ?ßBg 0]xÈ&•²Ž±Fg‰jåÚ6®râoæˆí?_®s%ÛÖ=N7á×_ sÙÝþð»I©”’¯Nû)Äs§C^„g¶¿ýÚßtúpåâpÚmZ¤&‹Ó)¡c»©Ö­RémÄûÔ dʉ1µ¾W·ç§›¿uù›ï‡ºß&ÌØõ$…ÎLÜ3dñ… X×4Ç௃LQìZÑx1MÑ”ºüí¹C§~¥0 YÑM‘Bެ¾n=9¾¿HqŠtbŽ<ÛÓZBï+ÜëºÉâéÐW—±kÚ_`øzG¦\Î^¾ìJ}oö,¶XÂÇ:Eƒ9Ûë M!ŸA{÷ÝžO³ªºŸa\˜Z¿¥¾§¬,¿9»R³ž‹²†\åï?èÚÕ™ÿæñdá?Ú¨Õ8jÖ¼»}³KîL­ŸT/¿zç6!~>[l­ùçè:hI“ùç¦8R7rè¾v¯Së 5n…md™Ø•Ú7TVñ÷t>a¬QS2˜µŠmaG%w3†ú0¼¿ßn6{§+= ªß±ÿ±ç³RØ2xC2=e/÷q´õíêÃéz~ ·n±ÄS.ÄßâZ;§š‹_ƒJ![\&$“v øÃXª(xO½’ËÌío‰S¶éÚ;õ}ÊxWÒK±j1k0ïGÝnÿ ±ºÉ¤îÓe÷Éc±4xØ‘Hºx•ÙWïBçé¹¶´Ò«ÑNëW.µ¾Å\ü Û›õ¡ N^éKÛáÑ>–œ¯æ¼Ø<Ο)›ß3†Ñµ%­ S ïÏéDÐ}rïÖö¢o=Ý`¿ãòתʴ°ë™é¿Ó»Ü/Þ†Rr¿öôìZë‹ÉźÅ.=ÎlßœD­Û²o˜bèêÓ~Ol·ßdvع5K|oCË×¾£/á®|ßÎÅA]êš9ªF“Hkó³-†Êê_ºfÖñ6Ã?_ÒÊ›f –’öôyññƒŽó1H¢˜£×–'¡ºöÆÝaºk%[²µfÁ¶5RjSm×»q ?è:Gv’ÌVñ¾ìÑÔ¼Cèµ›ã˜<ñX”T;¾¯–ÒÑ ¿†•Bn=\E¥Î3̉¦A½Ü+—ëßco¼lî¾íÖ\—R÷Y³ZN½ÉOtG«ôŒZ‹U|=¦·¶¹}¼ÇtVÔÛžìgGœ/’”ŸÓÏù; ¡Û9·äÒCõ’8Pob4íï?ïbzF Ãù„Ú‘Ö¾^_J‚îN0^ÀûA7Gk<›H/~²äÑä0õT^Ä †ïéq]µÁš¯®tðq¢4Ü…ó“@7ÑxÁ¹©q‰´ä—à|D%½9Þémãý÷ T[ê¯5*q¥£w¦É¿6âŽ'‡îªqTkãã‰Ä­/*iÉÝH£¹ÁÌ}ãº0±¡î’ì¾óQG÷«VÝÙoÏý|JèÞím˜í±0‘λ²F¨JB3‘QÐ&„ЭCNO_1=øËòʬþRâlO¸ó©nÕ¾íKåíéÐ7ÖØPÉû_„0ZÛ¼Îbø.EtJJOØå¿F•ÂØ}¹—‹èË‘åç'ä1T5eát—ÌGT;—gÖdÖšuþÒö²°á®t¬«äuyÍ«ž—eÍÐã¬ÏÛÇ/S0ü:žžU¤‘ÖÎÁçâø…F´É,ްöë¿¢èÖûÐÞúú¡L{í‹=1ý°N;ËÜ—þm]C±Ó»ƒ{'Ðqöõ‚g͹ç·Ù¿,”Y6ûá‡Öæ6ü󩔊Øe`)÷óI ãâO{ÍdÊFD‘ñЖ“ÆÞ c [²Ž¶4vúÌ-_Zº÷ž‚‹ƒºk[×_Ž'‰âæòs‘tÕò—k‰pfÆþzÙÓÁÛó¹‘,kú5w½(¡)®òÖ˜ÅÓ¸»÷æ,ÙI'>./pÚÁô¼~ä|Òõ ôðò¼œ‰n$Óîºa§Ó@wÊÇS¼¼GD3™£YÃ9[ èÒ£Þ®nô`ßÙ¯/Úq~Ÿè>8ÔͲ)‰!³O¯î £ï?ÓN¶YÃTJÖÌó ´¥ÈA+EgÝhýÒÈïó/¬àâ×ý®Öï6†œ GŸ:¨ ¥^—“™É1 »ª›ÒÌŽ´¶¶ÃÝiÿ¶çÉÖ»x?EèÂ]¿UcH»œt.”$Ï~~“‰ý¯ MïZ–éNNá»ÛuìÊû)Bg«}ÁM73nÖ™=5”üm":^‰ex¿#ÒÚçézð>ÜñDÐé÷‹*\èMö>7½òAAZûöq ïóBc]ß|ãNÎóÞõÚÏû™B׃:‡nœÍûr*è‰ôصÀCqÌvÙæ‚ù%¾n˜áο‡æü)åÐÍèå½|ÂG%õ©›CN ê=&{¬ÉÛ8¦ìÚ µpÝ-ùǺó¾Ð\”ÐÙ7êõ©±\IØ×³]´mÅÛÀ”Õñ ÿþfhowêî›Ñ5.} ?è;ÍR8ÏTR<ªGUáz«ðoß*>žù»˜;qþ•œ?¬N“JaÛ]ç·¥U0|ý€lêÊ+³û'0¼_=p9ã…¿;ﯼ‹t“Ø×Cƒšà$ë¿míÚÔ0inЉ&ïó#AdkÚ¼]'nM¤;å&N-+Iæýh¡s¼¿9nåÞ(º½ðÝó.hÒÙO «˜½ƒöUüjEÓß]ÎÆsÝŒ© ~KbsñƒÎ÷u=ç¸Hú­ÜúæQiít¦$2WÝ^o_´ÎŠÚ®·Úy°•°¯Ÿns: t“N÷´C>4àñ¶ó·CH}4ðªØ#‘Ù?6hOK+:¬`_xP¹QµmèN'‡ÎcÛiDÐUv9oWÕôÏwNzŸÈÖëT °"öéuëÞ›Ó)¡»Ï–ísáÔòÝnÃþ“BhÔ®M÷fÎS1ïOWym?mEC4J»³Çƒ÷ßäãݡئèœÃhAåÏWè…Pà iŸ¿Š±3.66Õšú»¬ZÙü¸^Ûܤ“-§ÓiZ)<¹É`oH‹0Jª’ô~YLÇ&ú™G6JbêDEl^$¦&>‡†ìÐ¥„ý5hÚ±ûd¨ÝèpŸÔMw9õ!…ÙêÝ¥ÓAb^o{zü©'ïO9‘‹_3<¯´²_ýëNÕê üÞYš¦2Ée‘~ ‡Åä»wrÓÆú2:ý*Xþ9;/蜆¶™n\HVzÝóß®»OëÕ(R™Z_ç·ìëðRO~z ?èÚôû<¾mƒ@R¥9 CÞ'½[ JíõÓ˜ZŸ´e™?<z’òB{Tžþ\ü {¶¯Üîá {dÁÚ·u¸O½³›]÷uLûý|{:dÀñ—‡<ÉÑEÐK3Œ‹twš·+ÿ>/€ô„¶8t n3²Ë‘Â4æ;²ÛT<Oª*¸õ×KÇʧÇ&sñƒŽó!¾C«ãvšåQö¶ck'¥3µþj­Yû³M2š¨5Œâü¿•Ðýì¿Ô÷åàÛ4ô‘zÕ„ˆ :47Oáw9qwcÙPŸ»éÃ{düów}j ³ ŸÒ}ùM*bíè=‚¨ú–‘aÛ¦™$#öÍ‚ üêÓ¼ŒºÌ8HA\üšW ²vÕÒëäúˆJ„‚èîÇo÷…›2mÖÇf ´´á÷wÈèü3õ³]c¸ëSݘҮxä¾FìÛ¬ÆÖA´Ùë[Äûœ‡L­ß~#¶c”Q³§¬Ä¸ó)„®Ù½¯½šÕ»J‰³Ö—–Ï ¢n=B_¬ê¢fjýêØ§ûØf²¿ÇºÅ÷· 5x™³v§Ãƒè`‹ú­×ÎU3ÆŠsÛ{YÓÙ_u'K +ÕQ½  ?è´6å‹}iaþôŒz݃èSþÛ°‚Ýj†Û?`EZâÖµ×§?èæj39±7SIý Ú·íl«×ÔLéÎfg ²¢­zÛîßè#£%z›·Hš¤tåý‹§ –‘_zñÔ˜ÏÔ´¨ÁØÒ,5Ã?ïÑLíƒcíçü®Õi œv[o™¹±o—:VRÁ∢†¿ÔL«·å6Ç-ù~AFVs£;Ygµ"müZ =œfív‰¼íÿ#7~ø? i5 ƒy(÷ùëàIïtãa®Kdôr¾v¡_«@·\÷æ½ÃÁç¨ü‡âÇ»¤@ºÛ£d›ÃÒ f9N:[cI7°Fè2Ús¤ÁçzÝj´ŸS]ä-öEÞIâú±@Šs[—Vç`ã½’}²¢AšéHÙßã ‚.Å8òÚ–ÂCtÏŸÝ Hó¾ íÁð庾'œÃd¼ÿ w t÷“Xã_GRÏH†j2Þ?–ƶìg(Ë•QŽî¹‘{pqÃßÇC¹cÆrYiDé†ëá¦U&#¾8äÇœE6Ñ“o2¾îµÑž%tC¶† ê;z“3ym˾Ó3™ZÏ)ï¶wô¢ï'åÃ.øÑþ} þ~xÎÖÌo ;™zãWEÒe³Û/ÖïÊd8ßt[º1S{aÖÞ§\¼Zâ¹ô@÷œ€²CÌ$ÇW™Gqþt§4·÷v&s<¸¦­N¹ }ôÝÁ4©–Ñȹ®Š˜ÏÅ º®ÝbjlŠO2¢†æÓ|Õ´Zãê­Ôd2¬«zËž6Ttt¡Þ„(±OY­}juBèÎìêºYÕð\üôð<»æ«ëåáWßæÊ†Óœ‚Èùd|rl×lfˆÑ!f]ªmHnWZÙóïq@7³~¹&sØ5†]ís¹DÛÃçmLž“ÍpëÆÖçÒmÞG /þ½Úd.~ÐYÞLé_gØufoÿ â%AĺHOÜÍÔî'¸u;ÝŽ^4~kËÆe߯sñƒÎÈ{ܲþo2Ú¶·Õ}*“%4:æŸÍ|^ayõþO+j’käqFÇ‹RC²rD¸øA·LÛøßfh¼&ÌlÆ}}ÃQ567›÷›·¢ÓSίˆŒE^°¾¿t–qñƒŽ}{i;ä.“;Žux¿OË½× ¢êæ0·Z[þ´$ÎÿZF->˜àRåâ d?'{Ú\˜ýu"žÆÜ'íí98‡YÔœ½-©r {åÊhiy;ž:nÿÝ=¦XÌþ¿`òlq'ùáò¦ö}ѽŷ:•‘sÔ¬,¹x¿V•¿¾sHw d¸÷kÁôõœ=“@•¿÷#ç¸^‘¹§ÔÞœNÝcŠò1kÙeA`jì½(YoP%íëQñë¬9%oÊHZôBFÏÖOn»•ûù„ÐÝ¿Å.¤Þg^7š`(©¦…N9«_IýœÂZ mÍi’¶ÁÑškç\’‹¸¸‹ ãö—3ª^¬cÍ?ÕyCÓ•üó9†Úzõz'£×Ï—ZlNáâÝÙ1_[°»a·ø„Ðí=Û½¿­ S5óSw³ v7õ%|Î ƒÉáR?îsÊ¡{1j¢ÁôÌÝÍ~e½”¥V*® Òn‡%{[’öãµòâŸ×¹<¨„îü…ã« S0[bgܲÇs[d§o¯*ÈD'^u~¬¥gÓ½èQs›%‹\ü ãö}‡2Ü~ÒtxéMË´cq²]ŸmÖü¾Q/º>ϳøIo.~­+…ã´ÃÖuóí4<—Ž—œm*­ ­-¶LLÄnûöô"ír‹ÞP.~ÐÕÑä= c’müªÂdìå<åçþ ªíoes‹8ð¢Ã§Ä*¸ûH]Suˆûö„pæn¶a¥Äc#ô·¬øí:,è¡ùEk/:¹}ÁÈÝï"è´Ûý®D0¯ÕúÛo‡ÒAزas*èô@rÍg²ßmbrb™ ÕIÎåâÝöÂd&i7Š„Qa^ŠÎúaìdÐw“ĆšMøÜmÈX/Ò rÝpí6—çåÐ}Tl ž¿:ŠY²˜}CF“eïªfèWÔú¦Sûa/ ûþÝL½˜[¶ˆ‹t³CG.Œd˜àkžùSÃ)´ëö‡ít*Å$ÒNmMîÿª“{CFún’u½f™pñƒîÊ‘”ö • ÷}†pò~¿úfU9‰ØË³5mß1ÜhöZ¹Ö›˜²~¿6•ÂÀO;CfnR2ׯ„¶‹± ñ¨{R3Êé§4÷ê½H+JÚåÝihmž0ä⹫i§|%óÈ`×ö3u"éÝÃfë…—Ó K»}çÔVÇ>^¡ãžó{pñƒÎAw©í'a4³ Õ:Ï+’nWD~ð+'v÷e›5Ö¤–ÎÊn:IƯcŒæâ݇v—;ž¼Í4Ú»_¿Ç„(U)wªœ:ϼMGLÏØ,ã¿'ÁÕ t»z½WuoÃÜ|Þ³¦,Š>õz~£rkùïë“Ýݽç“Û¿ÎÅ]¯(¡í­Í1L}íÆ\†üÙe–Uå´q»ó]L½Š-–tdë‘ö½Å,.~Ðy5Q<ý’Ãpû-•Ô~i§SöSÊiedì+u€˜ªŽàø÷<¡Îýþõ°™£cöiÒc“’âgÝ´ûÙ¿œú.rO+7SÇ%=öˈë¸ó¢Óñc_û^Še~ØøëªR’­{—ò¡­Ëk÷¯û”œ7 ýÄ®S7åîwt}7/›6ïC,S5·pII¯hz7õJ«ßÓì'êÊÞµo³{Õ^Ÿ=¹øµ­íß㘬¡ìŠg4½Ú¢4¬ò1%|Ûü­ò‚%¿^+£¼v#bŠéqñƒ®ùÏýÃVÆ13w\±¼\Mí³ë¬¬¯~̯[XPÙ—é¿6­}ÎhÌõ›Ð±»•[·ˆg´åjb )ǰzþ˜&Õgw[Ðù:nXÛ‡ó}'tNïÙÈx†[!†ÝF}í17Ê÷Ð?oNÚíëe\Üð÷_l™-œÏpëð1´ù@ؘíÿÞÿj¾.ð­èdís‰÷œÝ|vy¯S#é7xõ²õ±´öâó¹/$©ù¯g ›D˜Ó·;º?œ—»ëjÑÔoQÚ¸µC¿ãsÀtôÖFÔ fãû„Xréönü;ûÇ4&¾Üwå4 òÿ9¹÷Ô3µõ²÷œaäP±*±×áÆ‘ÁÐÓaÃLSªÈ†–t}X'çoÔÞç#¹ç<èš°mW×Dæí¡÷×Í/ÅѹŽkYLyL#[ž¹ÈŠžÍŒí¿õ½Œf¸¤Þ‹¸9‰{N€îxïÝã·$2¦ñÝfþŒ#Ÿ=‚Ømÿ® —OÎxDèE½[Nkoó˜[‡@WT,õ ˆKd^¹±ëñ4xí fçÚ<¦}/bwû-“6M¯õ¢;Öº‰ƒöpërèØoÉx¶W1‡6µ9•O믽þúˆœ0 ˆS=v›÷d/Z£ÿlú«ýí¸çtè>?sLZª}í6èåøí}?ú}ßuÓ.Lyñ×ó;î9¯]í~*ã<§Ë.A`‰ã‹W¦$="×mÔÓ¹éVÊè­¸Ü)¸&[«Ói_Éû«˜;‡ØH‰¤Ø1>ðÕösdzoÎeÄ=4áâöës³“ík çDÊ·ËiëñˆÜ ¦<ßñÉ’¸å"¢+…×ʇճ¤œû'm®î‘ñû„¹ç{tGmcw>1[bß\]µCEƒ»¿>ë¹ùmÙ´?vÚ! Z«wÎ}Ç0ô~ˤQyü:tú³/Ø’yL«Ë‹^ª¨ŸøBl¿5øïZГª·{<~zÒù•nž…³¸ø±?ßâÄW¬“™ksãê®K¢.3<¯Û?âÏŸ}øbÐkˆŽŒ>æ,=°²Ù|.~Мíú³Ïõdæáàm}êæ$Qôgã‹F<úý=Ž Ÿã¯ Q§/¯ÏœÍÝÐ땺û<™ù9È¥ç—ÙɤuæGF—Gt)xÞ[…¿•ô-ø~™Œ_¯äÖƒt:T ¿mÍp~kÂÜ?Ûîûè¸dò¸{þlIƒGôd{\ÎÆÖ´ÈRç!±ŒØÝ·[që]è:ç1/FX§0ågØÆ.…l—Ù\ûZC±3ã.ûZÓ‚»-ÌñóY°`?è–7ÌiØÏ7…ù=|ô¶ ÊU¿ÍÕPËÜ_cS]­Iwå7WÉø÷(3¸øAgå«{½¨ …¹Ù;°eà T:_™T8‡ÑЦ׀%c¬‰ývVámÎ:¦›pñƒnû¤ sœ[¥2fdž={s%•¼8·ÞÁ_C«7o^²Ñ׊^…=è9l²ìoë¿rö¼ìhÝêçôTFÿy@Ìàîi4ïÖ”Ð^ç5¿ý»Óî²ïeÄ­/ðï óÚÓÒµËÎTf» M£ËÍ3†{îÕлtŸfK‡YÒÝ•nÕs~xÒíZrï54е}5àò÷Ë©Œ {y´M'm_h­¡yEL`æåÿq? YíǽGÑÑG^Z;ã^qf*#˜¡{vô™t’Ì:ÒŒkH»ì2ß‚2;¿îõ ¯Œv=ë;L÷÷þL[E7üHe†íª—º·ÑCÚtj±yÍ$ 5ê;æÄ­Qttâ«ê< %”V,ätBè8ò4¦ªèà•0ÉCZµÔã²×ÍoŸqí×7}eü÷¸Ï)‚®²zV¿ÖsÓïIuÿªùò–…´Ü•ÒNCÛì¿î=Æ’LâO¯J}(#nÝ“;/èÆf¿‰ÉµOcn¢íð«¦í“ó·ÔÑüîãÃ׺÷T>“ñûýø÷DÐ ÚñÛùÓiÌÛ¨Ù“v¨iV‡O-÷e–‘yc;ó—‡­ ÍÿÏ©_q½ è4Çt?è~X™…éÝLcRl$½§&kÐÝ}ËhàáÓ˜Bñïõ~_+?è&k¿h˜ÆôíÜ2$ÿ•šþòë:æûÆ2 3¿7o€-éÖ{ylޏ7 PÝåt:q/òSœÆHM÷Ý9 ƒvÛ5xwnl)nž0´ r§OÅ!/~ß,?èVõ~5£ûû4†Ûç•A) ‹5©WV»o•´¯¿Ý¼øýjÜyB× ½òâzé ûmåö¾ôKi•,M-¥Ú÷Š[:Ž¥ÞëE[49[¬ûr:tÜ÷Ò#m¡È ×wú¯v-¥ÑUs6ý5Ä–®^2Ïl/þ{1Üù”@÷jýж]Óv5¬•~&=X5fv=‹Rz¡Ã¾´!×aY²~^Äî†(uà΋ºc§fâÉ71ý¹wUÀ’LZ78³ûÇá¥ÄÝÿbrf_ê{‘-ܦOn…:öÛY­‡¤3Õ?šÞ<›Iƒ/¾É¢æI½÷ ²‹k¿'Næº:g&îñ¢º[«çŠùýÐÙO~sº‡N:SaÝ+o^ÿljHá§;xS²I»ÅÏúÙS'öp½¨™ÐoÛ³ufÌŸÚæItþyæý™ùß{öûKÌ_ìycÿmã:ÿ¼Þµÿ_¶?~#ÿ5üFþ-3´ÿOçCÖÎÐþ—üFþ;ÌÑþÏòù÷ÎÑþ—üFþ_›ù§7úÓýéþïöFÿR_Äæ>Þì9au&uþyýjÿ->l¼Eþkx‹ü[æfÿŸÎ…¬›ý¿òùï27û?Ë_äß37û_ò©ÿÈö5=þôGú#?ýÛýé‹þï­±9‘%ûs³ÿÍ´Î?¯Gí¿Åwí§ˆÎ O‘í¼l1ƒ| ‡âe $@ªŠ™H袰 ùyÙÿ+/‘ÿ.³²ÿ³¼Dþ=³²ÿw^"µ}.›?¾´:ú"‰ÎŸ¾èϺÑþˆ½×%|ÌØŸË ;;ìŸ×—ößâ¹öÇO俆Ÿˆ8‚P(T¦À¨ø¢e€?Ð}\Ø&À (A 0DQ9Èz(pÆÀüoéOŸô_£O2â¯7%ÿ¹ ñgâ:ÿ¼Þ´ÿßµ| ‡dk¬Ç~ÇÕÀ û½T¨nköû‘8>U­ÙïéáøÀ¨ø$m€[öûK8>’¶ pjÇ~džHââöì÷9p| ‡„nÜý^Žª>»¿Çj Û‘ÝgãƒPÕ‘Ý÷‹ãg â pþ@ôQL€P‚`ˆB!rôP4Œ(@50@)P]ŽA¨Sà T|±1Àh€>Š pJP QŒÄ@ò “1¨(T" j ‹¢%Ž TŠ˜)p*¾ à4@ÎH€T ž)p*¾øà4@ÅÐ8%¨†(Žb ù@…ÒH€TN5ÐEGª€EÕ8_`€ð ‚kœ€ÔC`5ÐE1Gª€ÅÙ8_¨€ð Âmœ€ÔCr1ƒ| ‡¢n $@ªмHè¢à #U@€À8%`'i°ßjù?ôIB?}ÒŸ>éOŸ¤ÔùÓ'ýW铌ùëIÅ.£:ÿ¼Þ´Bà@UsvFŽœŠO®FÀø·dgÈàøH¶&ÀIe‚ãC$_q+v¦Žôˆ[³³p|P Ú°3p| ºmÙïºãø Tµe¿{ãg â¸pþíÙï¢âøHè&À©ûH"Á‹õÙïæáø@ÉÞ¸#û1TƒNìw–p| º(Bà@ 0˜g â‹„pþ@ôQ4L€P‚`ˆ""rô8>¨(0" j ‹b#Ž TŠ)p*¾à4@…É8%¨†(Tb ù@EËH€T15ÐEAGª€Î8%¨(x" j ‹â'Ž TŠ¡)p*¾0à4@…Ò8%¨†(œb ù@EÔH€TU5ÐEGª€×8_|€ÈA>ÐC16 ÕÀÅY¤@ tQ¨…À€* @á6Î@Åq#àü裨›' 5ÀE^ ä è¡à P€j`€À8ß àÿ}[ßÿì×þÓ'ItþôIÿ }Ò÷É”¿VØóÉWA¨bÿ.”)p*>Yà_õÞÀñ¼L€S}Ö??0D27`½  ‡Äf܉kTƒFìlv¨®.;#Ç J—Uãg â¢pþMØÙ½8>¤ pjÊÎÅñ!¦¸;ËÇzHžÆÍÙ™š8>¨-ØÙŽ8>PÝ–ìÌA€ª–ìì;8Ÿt€ðoÅÎÃñ‘„M€Skv&Ž ‘”ÅmØÙH8>ÐC‚6nËÎèÁñA50hÇÎŒÁñè¶gg—àø TµgghàøÀ¨øÄn€¿>;S„Doœ:²ßmÇñ!¿¸û]kè¡ P€j`€¢ R º(Bà@ `˜g â‹‡pþ@ô8>pJP Q\Ä@ò 1¨(<" j ‹"$Ž TŠ’)p*¾@à4@Ë8%¨†(`b ù@ÅÌH€T7p*P QìÄ@ò Ÿ1¨(„" j ‹¢(Ž TФ)p*¾`à4@Ô8%¨†(¨b ù@ÅÕH€T[5ÐEáuþ@ôQ„M€P‚`ˆ¢,rôP (@50@Á)P]o!p  PÌM3Pñ…Ý8 ú(ô&À (A 0Dá)P]4ìô>Gðý‘Hç½§é÷®Ný?éóÿ§ß×þºOÜÿ*;U ct2oÒE·#/*Îð~äÙTû{¯]c_F™SüÙa;÷XGY§åénO‰aŠÓ «güžwÉÍ÷Ùö{~Ã÷ëo3癉´GÒà8·Zç&ä§1^\é_×3›Ç[/OXLµsïÃß¾V™n¨¿ÇûJu®¾m~äantsb,k žMWœ¬-^|("Y”×ÝCìèXÇN êoöªkÃÍ€îÎ[·—Þ×Ò˜5Çlg¶È¡ G-÷µeЍpû¦éfmiùî £—™yýÍçW]ùæ…ƒ{žHc µƒ”rh¸GȨüET=5eýç®iLBÙ§úÇ_åPè/Õ¶ª…4‚ý˜nb:ß[w¹ÐÇ‹¸y³¼/t#½¹îù)•™¤<™KCö7N9[H£ú7(Y›)&‹c†¥xQQÑà¤:Ἧ"t‘š,ÏU*ãäÉžø\º¾ÍhݹB:{ææ¹566¹áê” ­½)V>×/)ž;Ÿ:]*…Æ›g‹ÝS™–9-ºžÉ¥oÎÉ[SH½¼^ZÍ [Ê` ¼i¥ëÎǺÙît5Ø&•¹ÔÜnÀɘ\š,iÖu¼Aa­ÿ0-éºeD±Ä›˜ì‡'–ðñƒN|îÕ^•ÊÌò«ÇO¹t\93H\]@¬ C· öÔàjVXs[o*ïäôsïëÝcÓ2tR™¦ÁWw%öÏ£MÃÜ«¯†ЄáË<~-ß@Cµþ½éþ¸±S7'ò¾ŠÐeE”·íš”Â4>ÑÝüõÊ<ººtR»{ ¨cýe-n Â-Câ—Zzï§Äź ‘‹\zœIa­“Îmw*^·Ê]$, Ò᯦lÝ@%«ØIsÞµst¸øAs¼*/wA Ã]ytεðó¼†䵯ròîö4£ÙøS³þò®õ/äâ7*…±§Žå±/óhZÇ]déù4’µëìhG]X»öÞ4±_䦽¯x_¾®•¯ëîLoªNf®²6!]ò)×Ü~ø·üÚ럾=¿!ÜЛŸ?ËûbB÷Yõ䯮3ÉLóvMÎÉ'ËKNW}­óéBù‰Îļ?¦yO”íþ„÷Å„.Ñk¢ëõYÉÌ¡²£úwwåSÝõs½cFççK.&í8½/ ^¤[ÑzæF.~еh5æÚù:ÉÌHï­H1ù4ÈäÉîÑ ó©v.¼ôSÚœr/²‰Ktv!§“@·ìáƒ=¡ILåZÑ2ƒÌ|Ú9|SNÞïûïsã +¼H¹æÓ‹ù;9º)}ޜÄÄ5lÚÏæg>ËÝ£éè—÷{¾';ït•e7oëØÏ›Ó)¡«£X–ÄpsÉ ¨Itƒ‘ö›ò~ßìô½¶õ½i…f`ØGwN§N9åI²¼PŘgš´´€Îyw丼ÚëŠØhtåMM_;—iÏétºU ?½êÝóûIsÊ5* Î…:öÂú¸NÍÓG|êcGìt±u«¼yŸN'€nå ÖCŰnC®М¥õ÷OË¥Q“³÷é,²#[ùÏð5Þ4sâ4¿ñN|ü sÎ;4ëíÛD¦iF‡ÞêêýáÌÁ®¹¤ÿò膓Ãí(²ë£ûšéÞ´7ØÏhfWN'‚Žº[Æ5'2Ú1Ð_ (¹åÕ-½×ç’ˆ½üBlÉw‡«Þt'¬2 §“@×ék?…éÂD&y¨pÒ‘ùQgW B~R>é2¡-iÇ9é{óùtªÉGlø•À ´5l;³”õüçߪÉ!Û¯/%/NØÐ“ç‰}¿´ò¦·ì˜íá\ü ûõræ U@“jÆd-¤ûiÚ$äÐàçŠ×·±!Ó­Ñ™nøùrG­lñÓi »=’M° Œýª‹‚ …ÔrêÍ #.æëZ¶3[L_Ý+3ÆzÓȶ;#’6mæâ×½R¨sëL—g͘q¿ÍRÒ qØé³œß¾ŠÓ®îÕ½ûvôí¶uCã8º;ï7¹þ‹gŠF†ä,EÞì9xÜOÃ:ØõWƒƒ:6d6çìÕÝýéñ-'÷ª÷œNÝêñ [·¶‰gpS,8S·ˆ*7›­Øý3›÷M²¡Ç÷¼ãõö&Z4íèÒN'‚î ;½M]̦[ö:<ÓŽ‚š6Ì+Îð&Ó.éßm[p:9t‡J;OÕqˆcBŽ.ÚÓjC­Ëì¶Á—U,ÓÆ-TYy¯ˆ¢÷eݘû4«6ŸP•Õ_òâo*¼£n”’Ïét•Bnþ~,3hÈ™äyÙEtb©ÁÎi7²¨ýçîn-diøÙïÑÊ­Þ´Éòxúð“œNÝCKÖI2–‘h³‹È÷sä-²ho‹A3UÂ4²5ëØáM»ê|ʸ>ûù„Ð}œõ:àxj #½ªë°»C1uìÂ:MdQñûï6~ã6Я†¯r§zë¾ñuŸ?¡ãò[ Óq‹ýÄ©FÅ4sú¢hךLb¦÷›sû3î÷È2qbso:Û¨ÎO[uºôŒa®û•ÛZ,/¦ËÃ~~ÉûÙR™~]‰ùC/úµ}cá©,>~Ð5íûÓàyj4óãèñ†ý‹é|ìææ§3)Å«þ觺â3£GÒ-/úË7pý~Ðuÿ|;÷xJ(ãQgˆÊ¥²„Ö¯]½¢¹š†Ýâ»æˆ}6ýªnðQF~g¿H„Ö[¸øA÷až{Ãéï̲°ðô£ßK¨¬Çž­“RÞÑ›öµ ÿkg•ݽhf×®Ë{…s: ta%)û¶´U0­¶ÎÊÙѦ”jŒ†LízHúMò[ÌxiN§|Mj8ЋX·‡öaœNÞ³ÖGõ3ÊÌÊ;Ñ ”Žv`'ìÿ¸ùÓ¿çŒëß[ ¯âtJèØ§»6SCvÚ^ÛI¥X÷tyF^:ueí¹],(m{'›ë2ÒÚÛ=çtö|êÆJú,fÖF}\¹°”J[ɪ<”Nz½ßtI6±$—§Ñ%+Âeûœ:½*…ç¦Æ¥]·¸Ï8·aïJé‚>ÛX§S‡ˆªÆ#¬èÍ“ë-B;zQbä—æmšs:tkÚ·ñ8kÄ ò®Üå°³”î¬}~êbfÚïëe×ø›è“^t¬m½ÞîB>~Е_P2îSžŽÚq¼”†ªE„mK£Ÿo×dMZû6?/Úœ2sQf,w½ˆ 3]ÙäÍ–{Ì´‘a‡òÝJémÉä™ Û¦ýö1>°›¬éÅÏ[åëtC'Û0¶­eG£Ó©wg÷ÀÔß¾ÐÖ{>ý¦’QÓéaÞ‹¹¼+‡.XÙ­Mÿ´;ŒÖÖVQJ “8]”J^A çÆZÓÍú'cÝÖɈu»ëVÌõKJèvœTš–)o1ÜòRvuÁúUShs³šÉV´¦ä^~ßv2~î;§Ó@÷~ìÎðµî7Ý.Gæg—Rû±Þ—Zz¦˜[x×’úhÀ=ù¼Æ÷Ÿ½+… ®MrŽØêÏ\аÆ¥äøôcꘙ)¿}½?´oùfl@­ßÿAwpéÇwãç]e‚7*Üôª”Úl>¼ló‡äß÷Q£õ?oñö¤]¥m»{s:!t¥ÛGWxw¼Ì°ÓЧ}.¥æ O/xà›ü{Þê‹Íe¶2…çßò„ºq.;sê=ñann¼%¢n±U÷à‚dºóWžuúW r>xÍyàGOJm‚tzé×Î,íͰnØ—š•шð r¾&Ñ,qJhÛå–Ô¼|£ÞmœÏÌgrè¿›tæ‰Ê™Y‡_FÆËŸ4¹’D‡–ônxÀ’ŠJêm[—îIŸÔ“–Uñý't5ísOïœé°n½Ÿ»”QÞµs$ÑÁ[×–­[cÉûÑ{RÁž·Ó÷DðùºÝÞȸyŽ<™u4(ãë–Š¢“F;4znAÛ²36 ô G¯&ëª\ùüÙ§R(ÑKr6qœ±«ŽÐa@íŸ]|É_E^Sîd ,¨²ÿÍg»}=èE¼Éüû7ùøAÇåS Ãå™2Ò_ð~Z ¹Þ¸?j’9½ÚßNžæñ·ûH]Í㓟{l`êk&—ѳˆU±-T¤µ«hFÅVö’èñ·¸‹ ÓŽ—í¾Ø)ÿ…Fe”Z_¬ÓV•Èû°¬'ó½ÒH{ÒO]×c«%?è´vAÝwÒÔä7ö+£­/þJžr$‘vlîÜ\3XDÙžªúßÝéÒ ~>Žðñƒ®"ÒÖ2fèâ|ÊèéÓGëÖÍH¤×&öy·–®Õ[±àv”ûßëtºÚÁÄg(e0kŒQFöSVš«'òsÐ×P6¼ îdÌŽ…våûè†õýÙ¶ÛEúÒÎN¡‚.üÙx‹Mé t¥]™Iý l1sù³×îdñ¤®k¢;ß¿ô­]Æ¢—ÒH‹ ³ûàs~Ür¦úÖ…ÞOg ¨×èþ¶Tw«ÃÔ±|ÿÔÖjpƒË2rl^|ç%½®±Êcyu}έéêµ”ŸSȻӖa s7çã7ßÚ‡¡XQe¤}œê˜@çö9[FÇ­£’µ™ú·ýÜ©ò¥,cY¾®ùµÔ´1¾~”Ôãñ“%ÃË(fªYêµüx|wõ&ë"âæƒ»SéíA+¬øþ:®N]!{Y ,£Óö²g/ÆS‹yÏm_O­Ù ÖØ»ÿ½®{xÓ‹mMýéÚûícû”‘w*}í;/ž2.é.ù%0£ìPß‹z·ÜIÈ>žIùûº‹G;étƒtê±+Re´Æ&êÍázñT¿Îðm/åfÄvù™ÅîÄûƒsñë[ëGp›žX¾¢÷û¡fC¬uBãhâtvåÂŒ62{͸^øë_¿~•«î+æÔ±¹KçÖQ\æet<<'ýäÆ8*–Z5~¡}¤±¼OÝ:ZpéMÇãj)qý)ÿüÞ¯v½ç>ï†>än“¡]Ìc)E\ñþĵµôë9›°¥¼O_ÿ íXЭ½m0E4`ÁÑ÷Ì1œØ'–_·^KS—÷4ùqHJÚrq‘þƒŽ›Âûˆ—RïÑ6>Och|…•¢Ž÷:j5*ã©ß))eLvu¨̯¿@§]Γ= Ë›äù•ÒÇmM®ÅШ×i›¤b¥NßÛÏ*RZûœÅÅÏ Rÿ3pÏ»iÓ‰3>gÇ-[ÄÐÐvß{,¢³ß–½ˆ«”ò¾iœN`Pë—Jî¸?uÚ[J·F_þ”(ˆ¡y¶âzuÖÓ£éë&»ß“R.k_–Ç}N!tZÛ‘¼0Úqmü‚ Ö¥”Õu÷ä¯ÅÑtØøh—UEÔG{áK‰óùá΋:¯¦½"ýKÂ)üõmã¹&¥TvcF¥{4¿þ!¢7ÉCôWou¥¾#g ê¿“þƒnþ«f‹W= Ñ‚)«Ç–’ažoñªhj©¸#mUº–žßyy0z”+µ}þ¬Wõ1>~Ð]öñÙ¹:’î²íµ ”¦MÕ¶©{4ÕÑí®¡½ìXøÙ®ä”/zÔ;t#¾_ëlPE†³,OŒÕ-¥3v ¬óTIé±ç^Û¾Š ;µ:uhŽ+±«EC¿ð÷ŸAí{†^ Ú}­îk%ݯ¤FÕíM¾›RééG&»¹ÐÛðg.]s:tÃY;WJRqåãƒz﫜Ûgž’÷7¥NWæü:¿Ä…&hXùü Ýæ§1ÆCm£Iµšun)¡¼D/©I%í×N™ÒÐ+C:&»ÐÓ„‡ûtãëtÞO*¦÷ÕDÓbõEÙþê¨5baøçâôù°I©k]þöóI »ÇÚZ¯ˆ¡uÓg4ê½¾„†G÷žeÇЂäËGG묢š=.{\Èau¼æ#ùøA·<ø²_—ÔzØæaAÝ)øœa»œøE}µÉjê¸ôèæ1N.äôW~¦ßu~ýºï&ìŠI,Ñãå·õ,¡Àå-Ê{Š"ù·-VáSÖ‘]æ†ã«\¨>û¸}›¿ÿ ãâKœ¯U ´((™W'Ц¬Ûýmk†ˆ¶¶b;wê%Níý¼¿•ÂÎõÐyÅçËWLƦw÷­ÞIƒu+÷uI[Oª‹-'çT]â}óøøA§µtŽ£Q™‹ð¨PLùöÒŠ2Ác®ƒ‹5«£¼gýðï{ÂßÐ¥Õ98v÷÷8ê:qÿ¦,ïbJ^íy~¥õl»jr–u<»¨ñóÝà×?¡‹¼Ê.@Ä“ÖNt_1Í™?Æ}ƒ"üw}x´oé%?/ÕÎçâš,gIFáÄN÷×ïeFK–±oì.Q«%¬cÿþº<'Q—Õ㨮UÔ!o£bª¸5©yºKÝiÁ:µ¯§º3\dz»ô·÷JèV¹ÜM©öNàóH1µv«7_§eÍÒ.Xˆè£OÓ竞\¤ÙoߢÃà>§ºqÛvãVIäû”"ŠzÝfË©P*y]9ilýzü4þdéuÉþùa`¥9À''òþÍET×ýöúÍC‰u1X±ŽŽOak/ò×=ßB÷ÿcï= ¢Ú¶6m̘1c.s™1!ÆYf̘1—’Ф˜1cBŒ PŠ¡Ì(¨"‚i¹ÈEÎÁ€bÆŒùWí}Ï7þþ»ÇèþzÜþ~ïϸãœÃ[kW­½ç\iÏ÷nï»R§ä8’Úäk¿‹ô¸Ý†õKŽ„ÑΖS=¶u“’þÆ­ô«£Ýâw}„çº~kg.ØÔGCÞÙ%M]vÐèï7¦Õ ÆÓRÁ_΋øzôBü„nß«{G’jhYóQÌ ÈLù±WŽËmrè¶Þ7zá ª¿¿ÁŒ5…üSê‡ËG^hè«m¡_‰QýÞíÕeÎÇPÁwdîÍÑf·y‡ß_QB÷¤…Ûéc“ãé·uÒ‘ºôâfë½­Céà†«ÛRMÑ1ö¸‡ ó>ü}¯øáí–b~±ôgnWz”O½|B›¹äߢó5e’·q¯}gÎOæ­ùdýåÉŠ³Ù!ôt[ _FµZ±•w/Ús©Ûü‰]„çºÙ“â²4×èQy¯¢¥6ùt]Õ«A4„†œXrx׆%t17Í$ó­' îÝ·Ë'ṃN—.ê$Rç¨^Åš‘ùÄ¢˜Í‡›Â>ß"ÊùÖùlßgž‚ŸœÐoнVän[’HCn9\ëß4Ÿä=-ξIìçø`N×§tÆË“–5µ™9^ˆ›Ð´¿½Tv=‘L/_ßæó,Oð™¾Izæí‡¥·6§C’ãnõöÕ„¸ 5¨‘$¬wäÑWÕ†š‰Á‚_úº™;V¾Ç“^ [žù-ÄM袳‰f53ðrêäžGgŽTÛ1Ð>˜Z®új:e‹9=b¶¿›<«Ö•ùþƒî°¥»Ù  I´ÂìRÚ“•yôvܱ²üÚÁ´@uÄ9Øo!1÷Ø=Igs¼JxîúbÝîzV‡ÏIôªø`tÍ!y´1c“¦ødµ «±!áübbnر»=éÂ*›å›å¾t5ƾ|˜8>™du–,HÒÏ#ßK[åõ "ß3½YJÛµ<ãIŽòÉS"…þƒnü ™×{Ê“Ig»žŸKQ—߾ܨÖá¥?,hÿç O* ÷#ÊGX·†®‘›Á‚”âd göé¹Ôu"sR >Î+„xíùuèfn^ߥO¯W­ Âm{.½ÕnÜâž7¨§wÅÞe;W’CJÄ ³“=é¾É»¬£„纕9Ÿ7N]›BCcg8בKî“å‹‹¾_£¡º :8t ûÁ·œ×©¡;¯Y4îÊýúy®æÏ}r)z˜{ÿ‰ñÔ­3s¤µ Ý4«ÌCðÿâ&t̶B]+•¢vÞÙUýmU{T×0Ì7€ú¬p<|ªó Þ–ØnÎïr^§×¯LrÄ¥Þ$ù”Tй:FQp?‡ÔâYevW©Â¬yy³Í+IgOÜß-ës¼:eñá7¬ê#ö§3òS·ƒòVPjÿ)Úååôy[òÚ¼rþw‘Bwï™zíÞZúÚŒíôçPx‡Ò Ñu/Sþpf¸¶’tÛ1r *iörîWaߺÑ7»J¦÷Õ’¸Gþ<Ó¯Ùô0vÓºêO. ë•+©ß¶éA¦ö †wZ/ÌÛ¡K~âç¸j𖬕mͦõÏúõûvã±]ë< ZÝû¾qÛÞ´¤m¯Ø¯~¼¯_Õz¢–tË:ÞÙäñæYó'6çÉìòÈ;MZZÑëF=k›ö êl{[!Œ;¡{Ͷ jéÒø¦÷¯Ì¦AË®´ixŽ. 2zÜÝš\=–e¾YàAlvÜq‚0oè_&yzC>Ð_KÓuÆWÙ$‹ øüí‚’Þ„ élXdžFWwg‡Ÿz5Ô¯Tè?è^o¿Z¯a´–9ÎN3ÿžE‡ûÖ&^qšz.׎­>цfÜ”ÜwЃ®1;S}¾$Ð}š½øY‰–j ÙVÿel…zœm½e‹Å‹65¹$¶¡Ú½gÜjiíAËÙ6ùO¡ÿ ûѺ80⻖Ʊå cY”}#õËõ“ Z}W»ä²‡5u±;—:©µ9:ýŒðüAwÅÁ|ÎÂæiÔ=ðHP³eYäSÚÁ;0Ó›ÊB™‘•™Î辪E´œ"íØÎë”Љsšk^õI#Ý6Ï,š{4þѯ.^|ìøýn9-èÄF¾Bþƒîò˜´‘.Ò¨õˆ‡ÝL>eÒÓ'/]åTÀŽ(,©‘ÎN^uÞƒï?èÞÔN[”F?LNtéÍeÒ…¥#êìûrˆ&ë6p-ÉTg$)ÿÇ9=£2 û¶íÒˆ¹O·>I-ºœ~åJ]ZĶÜÝÎ’<;¾ë-ÖS„ø ÝÇÃÛãc÷¤‘nÛkV&IÛúD?ÜEÁ磊M-霣?ÈÿqþA]÷G×>¬öM#sÝÄ!“¦XniŸ´k3¹í[”qï±%©7Ä®ø,§l[8CwB÷óîèuÙ—ÓhSógþçgÐcõ‡ÇÞq¤*¿æ¥¿Æ}{ýRþù© t—¯5‰0 K#™3s¨Í ü§lB2ƒR¿ög¸ùïgrÁ¿Ixþ 3šä^Rð1Þl×ûx 8{×zÑgpu[´[fàaGºå…09]Ý>¼Ã 3~\-nõç: ŽßÑžtÞý3Ò©ã0·Ã?ÛzrU~E…õV ñ‘ÓÝç’j çΠÛy?r÷÷jéÔó†·viÓt29kPm¿™7§öö³ÖsåU>·|ÿA—”>#¹¬v:Õì¹mf¹Öš}_n«àªÎËðþºraýS˜7@7×8ðJŸzé¤[–Àý6eÁ¼ÌZ+ü¸w.Á=8ÛÐQ‘÷ÊIå‚O˜pî º‰:ƒÖtº×ñöòÑ qŸî2ØYcòi®À.»uI+JÒ:wvÃ}#¦ç(ä?èòêð1oFugt¶©í£ä:Ì}4<¶… =Ô.y¿?¾AÈ{Ç>±£¥^½úT¿Vï'œ+#~=LNµ™ãœpN ¤rÚC?ˆo8®¥þëZ×U çÌ ‹YÈVÂÒh²2âRÊùªß59ºµ,€þŽš×e+“´‰Ùþi…sfÐõîÿõÌ¢4âýãSh_»‹Kz¿ ä.›Ñ_w׆$ÜÛ’{&ÿ¼N½Ae’”ÙÁ'÷¦§‘n{Î7™6E5ù]ùuîñf gM­Ø6Q;2ù´ëÉ©q¼N]~à·%Ž1iä5¼Úô½ “‰÷•»ÁUùn™ãQÏ¢¾ñûÛÂóÝG‡ùÛ¯‡¦ÑÉ ª÷ÞŸD]KcÛÝqW ~âVÔwlP‹Á}=„q²ð¼A7eëžþÓ.¥Ñ¢Oáù&’ø“ºs“ ®jå^ê»´G‡=(&fVøEBÿ±ï·fU³7ÞiT˜É¶Ò)Ô+}û”’ î[Ãf gWXR¯ê'‹c¾#Ov|ïÐQ8ç Ýò‡åÈÛ\Þ$@ÇcËú9ïæž÷d;NVdÃì÷ã[Ý'/|µH ëñB·¾ÉOf‚<Ùà¡Âj¦'õÔöxßõ¹pÎ:ãµEÇÞ#ßqy¡²(ž,ÜO¨<»Éñç}­)rpƒŽMæx’þ¥ùNæ¼Nop™ÄqÎÈ~W‘_GÝÖzëÏ'ƒà>ŠÊó!\Õ>æýïïÛYy þ–¼NÝÍ×{Üé—FV±Sn$kè¥âÊï•·¸‘¿^†ØRxäÝ%ј×è–Ú ãMè³áSË4:’õô÷¼I:ÑaÕ# åª|ùêxÒf¯¸”é¼N Ý­˜!£½~j)ä ;ÈGVS†üìÒþ67³ su ¯ŸÆ9¨œtjèäŸ2_h©ñÄWÍ$b)ÆEÕÉ”pŽ÷›v #›ÐÞ#<ÉvM¬QïCW ]¿À¶oÖR›Z}ãë1ÔzáÀ¸÷×ïpuW]pð±§)ÌXíIÃtøöô†`^Ó‡93kiörŸvýbHsÀêù×w9·F»ºìªmO;ûµØá¢'5«°ïZ7’?Ÿ-‚®Ñä¾Ç¦ÍÀxsÈ’'§¯GS÷øcÏå÷¸q +¿-òµ£¨ çT‡<髵_ÛŒfüï"îŽÕþÊý¿O[Í>9š|Ú¼ì=ø>÷€M;üìháàS§õ¬Ú_æû:m˜“¨k}ŒÃ1›;u+Šìyv̸þ®ÊŸï}hºýMÌ£½onš¸˜×¹@§;”–J=oŽéÞzXMûTwÂò§¸£’{×—«ìéX“V.Ÿ{Ò)?ãǃÇó:%t¡áF­8Jßn2#ÂHŠ™öêe½8Ž;Ȧ÷+hxç’‹™Ý¼ÈKnZPٌש¡ûÕ»÷ô)cRiü,åÌ'“#im‹C%Íç«9v*yC©ƒ0OòªÚ—æû:÷Ëeu®¿O¡¯ûxŽÌ‰ ‚õË{vOPsŠ cÚÕt$v*çø6/*·ëýÙ·ˆïw=ã2ÉŒÐ]#Ì.§Ðœžk[FÐìnæFGpsçÉD>É´°èŰçÛ½è±I…]ø Á_º?W†ZÎKÖSÔ$±4v´WEpƒŸÖ4ÒÆ]/³rô"OçÍÃã} %ÐñóÎZÇì¯÷©éH³;—ü:FrÂóF_Wì­yÑž³z¾ç}#¥ÐÝ\z¶±OP2énÿvjêî]b^ûP$—j·kT÷Ùv¤;Æ‚ïw2Z¤Z+ãu.Ðåan“‰¹´·”qT¯¡í×îï#¹-ºx[šöÛsî%…‰³¯¼Õ…×)¡ãÇwÉtâÍÜÚˆR|97Š«ö àÞšÙ2:¶Ÿ ½èÄ2ö¾˜Ð}Á,_}=‰ÌG\—™vŸ¦ŽŸ“v3$Šë$Z>¶Ö~Ò ãôŽ‘nï$øbBwìËšÁ“æ'‘ôÙ¥d“†÷i¯hVðÓ&ÑÆ9»k,º³ÛñXÕ¼˜ï¿¡˜5Ñfÿ·DÚ¼hGi¯™÷(rÊÕÅ£9vÚÜï‡5e4óq–ä#yÓ»¡·óý ‚îÁ€šzq§É3yÞ‡vwéÙê³® ÑÜ…Ã쀜 Qé®j{{Ó`ÝÄ^è?èúuœŒ)L"í^}bôˆÒ;4i¥É‘óâîžþœcSÛ¸WÛì¹-½)~>såuRèîä†`æ‘@÷ØFÅš˜ØÝêËÎÌð˜íàp ¬Þ«Ý˜^ÞUëÈ|ÿA7ÉS´¤Ú¾ªd?ÏŽpºvþjÝsob¸¯9ï>…Ú’¸áz“­½«öÝùþƒîRØÕWw;'P½sojÈóèÁ’Í]ÃÄrÇßwòosߎ4AŸÉ¼éŠòÜ‹Ãy?T5tW± x²ô5Ì8Œîñž{;:–Ûëw¸WT˜=¥Ïïæ±]îM?gÊsß,ãûºë–qEËâ)c?ÿÕÇoSãòe›‡ÄqUçJy]oas)ß&eLNÖ¥פ8`~÷¡Ô¼Ì¯]Žãøs*ÄÜw}6y“.\<æ}bEÐØ^]<ÑGC{-|FÕu¥¦æo»_o­á,2¦t’OEœoÊÞ ð¦Ïc|j5ÛÉû-K «étÓ9ÈHCÕw¥\˜žu‹:î8¹ý€†ã×mÏØ¶¶È‡¾ÎHÚ2àØ¾ÿ «üÅ€Æ ë ·èþð8—WŸ4ÜÚÊ Íê^²£z®/›àCžÏµÏ7å}š] {ydÑŠoËâȨÉêS;ï†PýÝ×¼XÏñçCl©ë I“C†>Âû‚¯0tÃ^÷Öãs,í[ÊÆChí¤É µ1ñœ0®"ïzó üiÒ:Ë2ß^§†®ï¶íצ‰|eoÒ†¹ùûWôIà®O¾Ýmm„-éË5Þ62ûpƒµ øþƒNqyÝ„žb ƒTÃ…oÒðn[KæKàvmd'½ì¨âûÐþ3¶yÓ-§…û’õxÞ°2ÉÒóe’Æ·bhI{½5 nÓï=¿*«ýJàzØÙb*cGSvDß0ò|½y:MJAî+Ób§òÒÓÄö‘§T¶‰òf8;¦ú ÏCîò3;𿧺fMo°HD ÔÁ”9‘«zC·]®ï#Ìûù~—BÇûnGS„y‰úÉÄ Òžë{*fL7CwðÃŽü‡mö¨[éMNª¡ºöàûݺÄÏV‡‹"Óo!iUtû±oýKIÙú¡mw;ZÖïш/½_[^§„®Ûâ¥3êºEQïÈé½W¨èPnRìÛ¦Éæïš èæYîM. X4i¯SC·“S`Æ%ä±¾[2·ýeÔ–óóeT=ïeQÇ·ÞÔÕùj÷jÍæûº¼.™ ®œ‹¤-Ôµ«¼NžZÕ8“•ÌUù«7úÖ¥A ’:w‹_·}&ßÃË$µ?õ]w§O$lèè’Ùý:=ÙúRa” nb<òþlkÊ­_ Sa}u*ßÐå²ã½¡”UsÖ—j¦×hÄP6SNáŽÜö^æwÄŠNF”ô·žê#¼gÁûÇK »òù¹s’$‚îu®,™²1Tõ?]Ý[”ÂÕj]­ÅQ‘]Ý=kò)ÚïÏœó:)t|?©©Ñî£óo] .^§4ER9ŸÄ‰Ï~X’tQfð(Á·œ×¹@ç[ͦçÂjjÕ/@Õºô*œ’°»Ÿk*g¹`ÑÄ£¬0ÔŸýò§Õ:×Ãjö8þû)¡SJ=c|•£ŸSg_Ùè*I»6>“ZÊužØglén“f¾äçÄ&lÓùþƒn޳öÕÛ¨V媱“¯ÐÈéúm´Ü¡«ËŸos¶¦c-¢²ªø û¦|?”Bwèp#ÓÈ÷isÙ–‚iûü©ÏC®ÿ“IZÎ-̶¡ß×SO~êéKÍÒÛ왡Ϸ§7¢LÒ…˜{´ÛÃwHú2Ùéºiÿäé%ÏJú}ßàKü|÷¹Awt»ò»سڀº—ih\ó:½¯hÿÜŸ"íñ+w|…} ßÐýîØcšaòʪu/I5ëýlÒÛ4·P˱Y_Ð@;’ÖèŒ!¤/õ7›-­6mßÐ lu¹cf8Ù³áʉ‹¤{í¢I7ž Ýñ-ÕŽ¦gûï—­÷¥½áì`î`¾ÿ ó/­Øí–F¡“'X¼~{V¥Žâ?)ûî3liËfö´#¦M¿µ3}iq}6ÂéÎ÷tü>þm*Ý“ÓÏÑä5kÖÿªr{'ÌÛHôz§Ë-;_¢€¤cÙ%ïèú:Iþo=û¬P o;Þ~‚ßyŠ,³É›|3íÏúïïíKñqoªw«U›ï?è‚\f ŸÔ|2—|ŽD½Ž(òž¥ýù]ò¶¸:%ûÒ\fþdß#Ë$µB(!/ÍhM°ôíÿùµI›vé\Õy÷M®Wúnû ûZ£ùþƒÎ«äœ×Å웤×ñÞFÛ¨³T6¦cÚ³tNÏéUÞºãv´ya³úÛ&ø?_É÷tºc{9ÁômHç̼4\°ÄtÓÎ־ğïÃ÷t‡Ùqû4-Ò½X ¤œÆóõÍ*Ó¹±Í×;ï+ïKãbLv6«Çÿ.Jè¬Z5¸~þå9«bV!GÅË^ƒ28KѸ#íó-ͬãéKãøŒ<íÓŸþ 3uêbuèÂ5ÊÈg \§ißhóX×Uœnþ¬#ÕÞå=îƒ/ÉLf3:¾ÿFVíóÐʂӢ Sd×fµëÆ+œx·IãFŽt#äpæÖ^Uº_ºûEoT™¤AçÙý.]!eË=ûmKN’þªÉ7;<Îà\ÄŠµìHžšû’Ë™5%¦-ùþƒnÑ=sÃΗ/SÙÛâYcOÒHÛ‡ÁõÚfrÂ:y›žóÕÇ—øãͽøþƒnd ¶ó}‘÷ZÓþñA?2ÿ¨ðül–É ëÔ ÛÇø’aÛ;yüï"…®Ãªý®1þç‰;™w][ÇâVJü"öfrÍVDeô¶²¥}!±Êfc}i©áó ¯wtãûºÎóšß=K«•<Ø{‚–Ü1)½šÉE ?Ú±×yÅüœtiœ¯p³!ßЕ)Ÿºú mµüæãôþ[Ѿ/O3¹ªóàæ¿¶-VÍõ%~][Å?Ð= œ9¬û!?Z“ÜcWòq¨æÖ2‹›ÅÕ_|ÓÛ†ºè6Þ«tìMnVã‚e;.ÇÆsÂ^';~œ O´>wcB7mNM“û‡|ɶý¼é~ëþ^ot™äàÍŸù{ÑÎo©':î<ο¼>‹k£[øµ!›÷ÉÆC/UŇG:ºÎºƒƒî4²øÉírËã4¼àuôç Y\}‹úcM‘Ñ΂f]CïúÒFûã â>´æû º¾s‡Fì§è²öÓúggq‡"ûìP)£Ä“ýHö¥œƒ" öñý…¿µ_–µ°¯ËŸë{•謟ýgÝr[VÂÞå/|i¸‡ôêñ[B¼„®nÛÓËz&:щãºÿÑÌÁ _ŒÌþ3Odo‰·h£ ‡ìX¹eo¾¿ ë;®ÝËGÆÃÉâÍ¢±£ËÓ cñË âžÐW£­yóí¨ö— % áw×ê~5tË뤴ž•oÏñú”ÍÉ·| xBåÓòçu±'vŠ ‘‚”­£ëlüуtý+Û^i¾ÃdÜkÿ¾4Wï°Vþ„J._ÕJúØ“n¸ü ùµý• Ôéô¨Lò+ýÜèøö®\ÿ9ŠôýhîÆyßS7<¡xµ&ÑŽN¹tt½ìKlwØË(ý­)ù·V’‹ÞßZIÿj%ýW¨)Éþ'î ö»±Ï6. T1•(€è#hI€3Pr B3r š pþ "À™7 •ÀO”  ø™*€ÁP @ ô%À¨@9!Pš9ÐAÓ8P DÍ€PƒJ`„ *J `M @Œ€+  ú¾à T ˆŒÍh„Àlœ€?(†ÔfÀ ¨A%0Bà–%Èâ¦À„ FP—Ð}x p*PDøæ@4Bð7NÀ¿ «Ý‚ö‘ Ì€[[VCí#$Y;VËí$ Óö¬¦Úÿ/T3Iû?¨¿]UWòÿæšIà T |Û@û@4Br6NÀ(['EûHÖfÀÍ„­×¡}`„ä-ÆÖÐ>0@"7ÎÖ/Ð>¨âlöèdó9´T |$›W } ÿŸ¬›ô××öïØÈEï¿þØèï˜èß{LÄbŒ“Ð×ìwaZ3àÔ !@É€ä+SàÂ@#xIh>™8("6s !È™'àJ!.È ¸5¨F‚2 ¹ÀѸ€0PÄR Z `)Î@ÊÁÓÈF¤&À øƒR`ˆÀjÜ€T#ZP‚\`€ k \@¨ba)P-ÐG@–g å@„mä@#kàüA)0Dð6n@ *‚¹ (A.0@`7. T1½(€è#èK€3Pò6¬ÆÚr ‚ pþíXÍ-´aÜÚ³ÚOøl îÀêá³èwduwðÙ@Ê;²ú/øl À‹ pþX= |6pëÌê2à»#$YVß  ™veï©ã» îÆÞ—Fû@ ô»³÷vÑ>PòîìýQ´ä@#$/àô?¨±]U?’%8àü{³÷Hp½HxfÀ­{Ÿ× Œe}Ù¹z\/0@24íÇÎwãzA÷gçŒq½@ ôØyW´T Üˆ»Dû@4B5NÀ ;—†ö‘TÍ€Û v> í#$YÙ`vNí$\Ó!ì¼Ú@lÌÎ- } úCÙþ9Ú*P>”íã¢} !Q›'à?Œík¡}$n3à6œí¯ }`„D.ÁÖùÑ>0@R7ÉÖ›Ñ>¨âQlÝíðM€l4[ï‚`` \è¿Õ{d㙿õµÿŽ‹\ôþë‹þ®ýûŒþ{c#'œ…þdß™ý7s !@™'àJ!–pjP ŒÀd@ r‚™)pa ˆܤ@´@Nœ ”Ÿ9M€ð¥ÀAÑ ¸5¨F’2 ¹ÀÓ¸€0PÄ R Z `*Î@ÊÁÕÈF´&À øƒR`ˆÀkÜ€T#bP‚\`€ l \@¨bi)P-ÐGÀ–g å@„nä@#sàüA)0D§™7 •ÀÁ^”  ð›*€¸-«‰‹öè·cµYÑ>Pòv¬F(Úr •ÀICÖÕgÄg$ÓŽ¬N >T±ˆÕ«Ãg-ÐïÄê¦á³ ”wbõ»ðÙ@4Bâ1NÀ¿ «g„ï†Ddܺ²º:h!1ɺ±ú.h I™vguFÐ>¨â¬ÞÚZ /fuÐ>ðÿŸ¨±­Z ß›½o PòÞìýW\7M€ðïËÞÄu#1š·~ì½4\70B¢”õgïG¡}`€¤ijÄÞÓAû ˆ°÷EÐ>Ðýì=´T | ;Oöh„dkœ€ÿ`v¾í#ùš·!ìœ+ÚFHÆ2cvÞí$fÓ¡ìÜÚ@lÂΟ¡} úÃØ9(´T |;ƒöh„„nœ€ÿv>í#Á›·‘lŸíügl-ÐGòwþ£Ù4˜·‰ôþú´ýý©õþŽþ]ÖØ³î"ôû^bü;)P-») p*PDZæ@4B3NÀ”C43àÔ !ÀÉ€ä;SàÂ@#øIh>¡8("Fs !Hš'àJ!‚¦pjP ŒDe@ rª)pa ˆ`¥@´@ÁVœ ”‚¯9ˆM€ð¥ÀÙ ¸5¨FÔ2 ¹ÀAÛ¸€0PÄâR Z €.Î@ÊÞÈFö&À øƒR`ˆàoÜÚ0Ï´Œ dmY-´ LÛ±šòhTq{VÛí­4L€ðïÀêJ㳑DÌ€[GVߟ ŒDøl«³‹ÏH0¦X½W|6¨âάî(>h~Vÿß ¨@yV‡ß ÈFHL&À øwcuéÐ>•pëÎ꣡}`„Ä%ëÁêt¡}`€ÛTÌêEá³Å¬tHjfÀ­'« 0B’“õbõi Hx¦½Y\7¨â>¬n®h~_V?× T ¼/«c€ër ¥ pþýÙ{Ýh‰Ó ¸±÷‹Ñ>0B"• `ï¹¢}`€¤j:½‰öAbï¢} úƒÙûhh¨@ù`ö^Úr ’± pþÆì=´äl܆²÷Ð>0B²–™°sóh q›cç·Ñ>¨âáì1ÚZ ?‚gEû@ÊG°s•hÈFHø&À øÿ‹—ˆ8(" Ìü_ÆIl|ñwíï8ÉEï¿?Nú;Fú;Fú_#ýÏŒL„{M-\³þ (A.kÁɸ€0PÄVR Z À%Î@ÊÌÈFj&À øƒR`ˆ gÜ€T#=P‚\`€h \@¨bD)P-ÐGp”g å@„`iä@#NàüA)0D 5n@ *« (A.0@5. T1‚®(€è#K€3Pr B@6r ‚³ pþ "X›7 •ÀÁ[”  ›*€] @ ôä%À¨@9!è›9Ð À8ÿ¶Ìí#!˜·vÌí#$Y{æÇ‚ÏíÀüGð¹@Ê;0 |.DL€ð1_|.’ŠpëÄêÓãs’Œ¬3«“Žï pL»°zÝø^ ˆ»²ºÑø^@ ô»±úÅh¨@y7VGí9ЉÊ8ÿ¬®(ÚGâ2n ”‹YýHè€h„¤fœ€/VO:$93àÖ›ÕwÃu#$=YVg × Mû²zWølPÄýXÝ%\7Ðýþ¬þ®¨@yV‡í9ЉÓ8ÿ¬.ÚG"5nY½´ŒXeƒXÝ´ dM³÷çÑ>¨â!ì=n´´@ߘ½OŒö ”³÷ZÑ>œM€ð7aïù¡}$k3à6Œ½o†ö’·l8{ï í$rÓìý´*€x${í-ÐÅÞG@û£Øù{|îÿÀc­j\$Ñû»~ôw\ôwýH­÷wlôï²~d*ÜOáºL€ð¥ì³¤Ì€PƒJ`„ %J ÀL @Œ€&  únà T ˆìÌh„Àgœ€?(†„fÀ ¨A%0B`”%È’¦À„ FДÐ}P p*PD¨æ@4Bp5NÀ”C[3àÔ !øÊ€äbSàÂ@#0Kh>‚´8("ms !€›'àJ!ºpjP Œàe@ r‚½)pa ˆü¥@´@¿-óªDû@ÊÛ2ÏD´ä@#$ àüA.0@Ò0íÀ|ÓðÙ ˆ;2ÿ.|6Ð}ó‘Âg(1?#|6lL€ðïÌü]ðÝ|Ì€[æ3‚d]™ßÚHL¦Ý˜ïÚ@ÜÕÿGû@ ô{°:ôh¨@yVí¹˜Õ‡ˆ{²º×Ð-ÐïÅê/CT ¼« üL€ðïÃê£âº‘ Í€[_V§× ŒeýX½H\70@¢4íÏêâºA±úyhhþVÇ í(Àê‹¡} !Áš'à?ˆÕ[BûH¸fÀm0«ûƒö°l«?ƒö’±©1«ƒ‚öAeõ8Ð>Ð}Ví(7aõ Ð>ÄM€ðÎÞ×FûHêfÀm{oí#$yÙHöþ*ÚHø¦£Ø{”ølP>нCpÍÞŸƒa@ Ê'±üþwœôwœä¢÷wœôwœôï1N2îö›²v%À¨@9û[)s !`™'àJ!˜pjP ŒÐd@ r‚›)pa ˆì¤@´@Oœ ”¡9M€ð¥ÀAÒ ¸5¨Fš2 ¹ÀÔ¸€0PĨR Z à*Î@ÊÁÖÈF¼&À øƒR`ˆ@lÜ€T#fP‚\`€ m \@¨bm)P-ÐG—g å@„€nä@#wàüA)0D°7n@ *‚¿ (A.0@"0mË|¶Ñ>¨âvÌïí-ÐoÏ|‡Ñ>Pµg^»øl$ 3àÖy¾â³’ˆ¬#óÅg>[Ä<0ñÙ ˆ;1/F|6ÐýÎÌŸ T ˆ|Ìh„Ddœ€WæÕ…ö‘˜Ì€[7æ…ö•¬;ó.BûÀIË´óÐAû ˆÅÌËí ¨FHj²žÌ³:`€gÚ‹yG@*€¸7ó0€h~VS× T ¼«íŽër £ pþýX­k\7¥pëÏj.£}`„Ä)3bµÑ>0@5ÀjТ}PÄYmT´´@«Ñ‰ö ”bµ"Ñ>|M€ðÂjç¡}$c3àfÌj¸¡}`„ä,Êj‰¡}`€DmjÂjZ¡}PÄÃXm%´´@8«ñƒö ”gµfÐ>àM€ðÉjo }$|3àÂ@c`ä@# L€ðÿ—q’”å˜ÿðÏÿz®©jÏîÿkmJûô¹ÿ៫þ—ÄW!ÐQ™Äö—"ûRÐŽ÷M:J«†¼<üöUýóÙ®«–ÏZü„bÜlêÛi9½XhðÝÄŽøz ©]È‚Ž·ûÒÖæ×'=ܲžF¹LØñÓ`ý¸ò.}úJ©®% ÚyqlÒÐÈsž\µÇb“ž'©I«nG6ŽyòÇWØh×âMæú > Ct:)tš–Ï|¸=îôûQz’¾÷|~¯“ø ½ÔcFA¶¼rs_¡Ž[#Î:¡^·ïu‹mŠSd¼+×­eã'¤+¯ÛÄ–¶m»±8ÖG¨g"ÔÛnМE;§êæN§ºldwš^‹×)2?=êPØ«¢´û¨eéÿœvU'N ÝìîCBªå*¹ïÉÑë;Í?CCXYâ‚ÇUu`É;÷Öý"Ïv|Ýü}Äêq‡sîãøzeJºú°Ib»ˆÇUõ_©µ‰òbï\¡î¾P7BR&9ùMò>åìîÙàú!#.(iO=ó{ç.>¦ª:–|w_äÖøUe#NÝ&]™KÜB]¡¾³¤³é:ð˜ªê‚žùam.úñÍâkÍ|wãû ºÙ#Ü·÷ç왑ÉYºÝÿ»VºêñðZºÂ“¾B¾.†:õÂ)>^fW9 3t;GŽïõ^3ç1UÕ½påt}óüžúòýÝýµ÷/ äF¬eòÏÑϰsà {L¬û9m´%]yÓ,Ÿªú.::VÕüTëÜÛ+'?;yžúv>ßVôøßü‚ò=Vå>ô/žº=çŸd «­â ºìÔvÒjl·AüµÎcêâÚ>ögCâm¡}ö¡+ÓO¾5ëªëïRèª3î·ŠÓÙùýº@f©xDûÒ:¾·¦±-ZM*ó¡{›¯Ž[åÊ×'ÑS&YýéäÇï‚8Ö[mC/’®LIÞ#2˜£ñÞbM¿oɪåû>QbNÝë]ó¥šÁœçzæ`p‰¶,P´öˆz$øZS÷Ø?›¾ó¡ZÿÊhÈ_§º —–NTt“ãëá]¦™Í#›k¨a«ðË#¬éÅÖ†kZûÒf/s²³N'ó§žg€»È ¦?‡]h\Ý÷éÊäž´¦þ3˜ƒ»/¥<\ôzE¾=èä•Ì ðWGWÞŸ¯·âòˆê×Û3ðHwJÛð°Î³9¾Ô˘Utê¶@×h_jvDD(שó×+O\!˼ÝÇŽÊ«šÒô£ élpšìí5d«rÿÜAs%;²OÈmN“ñ¾Æ‚ÍWéΦÝ{›="Õü×'äÙ2j«+´Tõ<ðõ¿J¡o,ïg|-Œ{xkjÛFShÜÐï5ÊŒýñ=Þn¾:³^†/Í,ÿÔã]ø8¾ÿÆ–I‚¾}t&œk׽ͅҞä?âê§¼vˆU=õ[cGæiÕ&çúRfóV׉ cUXÛyÞá¢rrîdê_£jÇûVD+›×t_ýÖŽÞY$-ªñ|Fñý]vÙ1Å޻ܯI—Æ]~µEBŸgi#+‡×ÊžµùzïÑ_áþäëçH¡ø$w‹«Ó=ŽU‡óθN¿W\2mô.ülŒŸÆŽÆØ\#i›¯à×%Ô¹‚Îý3JºÏy‡†ÅÚß +Ÿ[g=¤m§2}/ž²#Õ3{ŸDÄ—Ó Žz.xÆßŸJè†Hlýg™>àúßpyÄLíÌ{Ä•ù<$¾žŽeÿJù\Þ°ª¾R_¾ÿ ó}í 渥ß|/ ŒQQè§ŒÙÛÒ÷QÌ9ÁVð¥õ¡z÷¼fº;òuØJ¡{;¼íûÔjî¤zý¢Ûc‚èȃ¢×Y+’¡×¯eõÛQÞ›Êïk}hLÏEá­øþW&i°n·ñ°mj.‘óYpývÍ︰ž§éCú’po^ÎY;šÖjå§I†>¤+Ïw~ßÐ¥æ;¶:ñRÍ©»ÛgŽL÷í7Û÷}Hkg±HcO¯.ö¬~²Ø›b:äÇÕÛÁ×ß“@÷ªiÖâ‹#¸ß¯˜H0I~FºVoòêõ}ù+ÈÀ‘êïh»òÞSo¡./¿H¡Ûý¬8ª[\ÇûœÜ$›¯6ŒùPJq'm^M5^E{s}6ÄG¸ÏMøþƒîÞK Šäøûâ&厒p&³”N{‡—‘ÜfsÛ'„tå5o•Ò<Ý ¹š.öaéC5nŽõxól<ßÐñ~„Q\¤£ãÃÍWB¨«q£€~>¥U>K4uÑ»†ûR}èÇ/¥·¦ò¿K)t“™­”E×ô{M³Ã}nцïŸ9ÇM¥B]²Uä½{UÛÃ>dÌlÕÓ„:ãË$Ï_§Nʼn+fŒz‹ˆ»¯vYXJ*f«uÊ‘ò×D½½´Ø‡j7ÛÙ|ÃÓ|ÿA·Tg€Í9og•¹Biãð—×',%þ¾søSç‘÷©7ãûº´-•gíæò˜íTd(Ù˽2Ê;–R‚Y‹9Ï{8÷™¹ß×ô¡WEßÎë-ÔY…îÞÒþÓ›”Fsúúî>í6µw˜ßùSRšª+|jOMÍêÏîéC‡f±JVóøþƒ.ªÁ‘»Mb¸ÜÏ¥Þ¦»ÙÅ—¯jKhèeOd2{â¯ßG¨È×UBÇܱ¿Ëc¸ôŸ'wÿp£Ó”}/¡¼”/s_´§…º‚¤>BžáujèôüÅö}Ão}ue¯Ÿaô69aVÁŠê¸×e[Ýd¼¨ÑG³ÚømÌÒ f¼®º/îûÊ Lb¹m'm#?{…“ŸèÅh½ž%U>e¤³ihåKºáå}^§7ýnzÁñÇXî׎‹ƒ{Ý¡˜F­'ļ)&æbe¸È‘Ÿ_ÛìÖ`Œ'÷ÕBæêäB×ì”U˳¹±œËН#îМÆ}ÎíºY,øJ9RÀõqCº-ô%Þ/cßоëÎMèDZ*‚·—Ý¥97h°©˜*~0#¦UTñ³a~€;âgÿ©#†ø.æûºÈ¦Å~kã8]Uõ{4Âoüº9Ê«|ˆÈ"üb–<Ì—ðáS¯}áu.ÐÕ¬¹îcåý8Ž÷{¾GŶ9=¿‘ä~ë!Ë»­¦¹þ«úý õ¥ ˜Q ¯SBwÏc󓯵5Ü­Ÿ¬pø}J›eþ¡ïý"¢‹ƒ¸W«Èg™ßo…/Åõ6ÕÓ‹ÿ~jètåNgh8Û!]ß}¼OǬ-ÞYD= Ôíýç¬¢ÆÆ#f#o®ñ|ênëÈëJ¡›²ùÚÞ7ž®,3ÅèÖÅTßaïàÖ㋈ ëîu¤æžI/Ú¯òêžò:½‰e’÷Ä…†»š±+ðò|ŽºIS>­[Tå«AõîÜ¿0`‘¯ùï'‚޹Û뵌çÔI›*šÕQSJýˆ»—Ò iXCælO‘O×4öü¹—ðýݳÍãv-ˆçj»ôZU¸RM»3 Õ' i–‘E½l{ºS(õªç+ø¬ñõŸ¥ÐÕÑðŒç¶Ä>°|¡¦ç>‘âk–…TU_·ÅïMã÷½ð¡ZþW¬ì· uª¡;½ü˜’ÓÆs:[SQ¥›\ëÖ¯Pø>ö¤³_+õ¡]ÇØ€ž¯‹­„Ûñnýîyèø~&{"HÕ®SÍŸ ¨ÊW`Ñé:›l^ûã,^§†nÍÑáïOLà$‚-–EPpfÇÚËïÐü»Ý/ ©íH˜í/là[åË÷tºŸcG=ºZó.S#©í´W÷î. Úw>×úéHuÒ¥=ÅøLðÃåûoR™¤¶ÅøsÚànò4³¨ÛªHi›þÕ‰Äß«È÷º´í;K_¡¾.ÿ{Š ȺçY§+ÛÜ:Š®>Ú¦L¬WPå D:»¾ÄT uÆ¡3R¶z³Ä0‘»ÿ«ëèM»£H”ópÜ“Ô|J+o´Ò¦ž#U7u|ôdž# çW6A||)t› ú>rb"ǪRE-šn™”ã“OÛuÆ\öthε} e¾40`SXû6¼¯€ tm÷ÌÐo¿>‘{v٤ѽEÑTûþPW=iþŸqÏP§:˜Šúòóü½Îfôt"'Åh¼oB4õH?÷¸qï|ÁÖ–Îû¼~'Ü_Þ~þÃ…úþÐH™Q{R|"·½|÷7Ÿa1Ôå×½šÕ+óhéɧÆm©Æ-ÑӾûÐqñØ?óºRè6µ˜½cöëDN/¯ÄPm«6ÓûÅæ‘ÅÊkýeäÑGZýöÌûÞt¹Ý½ˆÿ^z¦e’9Kæ}^Ú$‰£›8ÄRa‡us»zçQƒøøèk»e´“Ù©·õ¥Ò“M.Þ°áû ºÌtføícÙÑXú2.Îö±Eýx"–4O’Ñö ó”-÷øÒÔ§æÅóó>.蔳»%ÏNâœz4y¸¼fí°hÝçé€<ªª·o“õŒ9ÐæŽ¬r7ßž:Þ¿5‰;Ý}Pñü-qtýÀ]#·_¹Tåcä¤3jRP?ú)¥oy ts;é:˜ÄõÐÞÇQëýg.XÅçÒÄnªŸÐ·§û6»š® ëýZ½;Å_§ºF9v·žMâŠÍÙÄVC·× è™KE'Ö/îÜÊlªé8Q!øà >¦ÐÅ»³‰J×¾cßçꢛ å_ÏÑ‘Þ}Õ|2UA¼¿ ¯+…ŽÿÝ’¸Lþ–ÇÓè6JÄr‰¯9Ò÷ft§ ÜPñÍ1Ý›Ée’©º–Ä­2¬ñ¸ žÄä\ãG9tÔüÐÌjF«(i%»äËÊ‚×俟:‡cŸüâ ƒèŠÆE~ùU;™{†,Ó¦ ‘üßïWõþ’Mõ3WV½¶ê¿+È{'stüm cÕ”'ÔKæŒÛW+ì³,‰ÖšEN+ಫ| Èjö¸¶. ÿð½UC÷¦ÞvÕá†ÉœëUÅz˜DÜØCMúÈ®òã"¶Šâ÷Ä—\«M½¾òà'àÄ Ðl·È2™¼6þ2u˜Mýª[ Õ!}aò}iÒ“»ÇîXò¾MzSÊ$u¿*mn¾H&OVž³M6MÜñÓ m¡¨/éìQ¿ ¾Røûz9çFá{=zîÜ`M ½. hpüa–P÷Ú‘øºÖŠø!J ûÚøùí5’9¶*ø-…^ž2áÁ¥,aìHº2ñ j¾™­¾6Ðeaùg·ñ·9Fz©}qý·CvYôÆç‰ËÒé«è³‡ÎQPAAßøjw…~ƒn{ЗG‡pŸ¼¼’µ`A--í~³«AFß,b.Íî³VÑè”Á+¾Ç+ªæG|¿AÇûb%qm3wØ÷˜¢¥ôkû{—I¹.úkÊ^9Òl?“ð’³ ªè&žp®ãœžÄ \ÿJÙ4GKj>Ú°=“B•_b ô¨½|ïöÐ}êWì^Ð_ð¡Z&éÚ †Ï’˜$n¥n”F{6œ˜I{W:º½Ã|v]†õIW¿N†¿ßÒÆÈô[H·`‹£ñü•iDÉ]'pM2©dFçHNߎ&äÇžñwPèäÌöÇÿ=è6˜?ôq¿Ä1÷ß5WӨΥ'O­Š3(¤33@´%¾®®¢j~Ã÷tãj9<•ã{%ÕÍR‘FKŸÛO˜AU>b–Õwé:FA‡§ÎËÔFðŸ…ÎàºËØQÛ“¸îþ›M'÷óÅ•;·dþ2Šû¼H»Õñ’Ùʶü/¡ãã$úMW ÖkVn¸®èm@º0n¶§†‘…oÊF§ËYaqÁ÷:]yçzI_Þ<ƒjlRÎØ¼>˜ ö¾ŽôƒÙ^¶;Nù×µusßgèÖÌËÛ4¡"‘s~3jÉÚ º>Û¨õöéÔÔaÉïë©t9è~ËÅ z³ì×y½5|ÿAw ³€¸ôDÎßðÙê±2)0ÕÈ`yµt*üðÃöüpGê^öfݶÝù½.Ô…oÏ:~þȵðXÑbµe&šŸ¸x=6NÔ{gzo¤#µÊ¼ôý}—1v´„-¿·@üÓM¸…çº5#ÏTûdÀÊo})43›öeš^:-M¥K)ƒ>w³'ufȦ +…ãû&_ž?èèØ¸€ÓMíäЛàå›î|Hù“'ëF¹t[¤ ÞoEè¿e~,ë2™Ešj,:2·çþJürx¼¬¦=Í›u/!ÿ’‚6éz„çºr‡jg?UÄsOSoךº!‡òÇ·;;¯] Þµ¡͵£º¥ÖïV¢ßß²»ÈBxþ ÛÉŒcâ9ÿùË—¶ðÏ¡›ú1óµ!Éô ¯ùÕúζ¤[¦l§ê‹ó:)t>lYëLñÆ 6ìI·-çöhàô ¯+e¿Ë¾3nJâ8îN/³CyÄÜn;rz´®GîÎSö¤†œð¥è®K4]„þ3+“Ts-ÃÇåÌü½ðÃã<Ú­ÇF ÎÜÂ¥µ§–-Ö Gì!åÐWŒB…纅ÜqE¿èX®÷ ‹Ÿn×̧ýÝOÌõ8îÏ~Á¥±#›öŸéKl®ÍY>ÎK  ªœ¾ââîXng˩ۺåÓœ©=OÛ:ÇÑ™ÄÌ®ÎãþÛ¼¹xÍÁ£OÞó:)t݉_$ŠåF¹TœŸOïvÐK2ˆ£uˆ¶n»mioèI_ٰψÿ~.Ð}ÎØ™ý£2†“7c+—ùôúæj¯Ï±tÌfY£»oeÄܾ<ö¡Ð<ó›ç ýݨ‰›Ÿ–©b¸%^«¦>ÙOÉù·/„˜Æþ™Ï^?íU<ô]ܳVÑ2¡ÿ ;»÷xèN«®µÎx,Ÿjï;9;üIL•)µq³´ÿ‚íÔ/ Ït9ýs´Œážôye»ŸË§ÀGG’ºìŒ¡x–€l)šÙ;îð¡ç}XÄâç¬2 ïͩمäÓe·ö9ÚV1æÁlt%ŸìCæÃB4𿧺€Å£zÜ_ÍmëÃVòÉ¢Õ÷]+nDS•Ê¹í¥·;ùP«PëÞý¿ ýÝÆÕVînƒö͵_Ѳ€&7i=ÿÝÄè?û(}î%NØÖÇF{ýü0X*ÄOèx?Ç(.LµêÞˆt6³Æ!YiÔŸqëã–Ö›ãCSk^Ýi/ÄOèx¿¶(ŽÛáְ匪=T¿g¿íQTåql¦»ck'â÷øßS Ý¢Ï]UÃêDqKåm ¨suÛE÷;Dÿ}­InÝçDÊ  ËÆBüœUå‡É}¯¾åaʾ ÷õ¶SL$õ/ù¦*ö·¢dŒ)½7éìÝ…üÝÊscêÉQG«èZʺìp®tÝšHrª«¦‹S,…ýDorü0Åk¶Ð³1Ÿ=ö~QÝ¢n÷æð^@¿OÔ©¿»s$ÙÔ׿¶¿¿]ªPæmXíM[xkï6áùƒîWhS‡M¿ ˆ÷›‰ eé•û¬üã7¿±bÿ´¯BþƒÎöQûÒÉõ#8cf£ù¦€¾­wýpÇ=‚‚óp/4+¨ÉÁkJÛÞ¤³Ñ°Æ/Ðe6lîÜ㌚Û윺«L¿:xmuê;9‚MÏ{^gà Òf¸ŒI¿ìMI>¼„ç:VÏ Ö 5w³Üv] )aXƒ1¶5"ªÖC©°ßàÛ Þôc›x ýÝ‘3´Ý÷s\ß㛌*¤1‹Dß{j2Ö!­ µ†IË;­ó¦Õï*N×Häujèf¿Û9i®ÿ.°†èÍ‹y¸Îå‡=Ï®W yp%¥¯«¿ô€Ä›Ú¾³:è›ðüAǯgÞçÞv¬ŸÖÉ©†”WFõR“%ºhú`%Ín¨»Ã)¸aá®áeBüœS&Áìðçî¢{œßZö$ÒñöNOîq¤wtÔÖ~s,hÙTMQ~Þdq³KÖ¦Bþƒ.œÙ VÞåŠ:-ª8WHú>;Ú{‰8ʼÓÐŽ»eA/×2ÃKo:Þ{Ýžf¼Nã:‹-‘MïrK¿öIÝs·&gµ]Tw÷:èÉŒA-èþ¥:µ.ʼiëùùˆˆÂóïWv‡+´x>®Uf!µVkþÃûä5”­”[гÒãÚ®AÇèãPfì-<Ð5>uz“Řpî@¨Å‹ë¯ iN‘£8vÜ}ºÐvŬ&—-¨ÏO':4?FézÅÕëæÐŸ^´}ü0N_gdYD¦yq7¢ýïÑÃôÏZ_IÆ=> Jñ¢ä ml¯Üãçßjè¬mÒF´pºÍígöPí‹(´Úѯ›Ý#t¦åzÉJzÓ½òÒó›^Uû8|ÿAWÏöȘ{C¹ÚŸ¦¶ÆEô`K÷ öÞ¥ìUwµK.¯ Ý¿v÷"g¿¡3ê}àç+zsË$ú †…ž¾Å›²…ª"Ruòz6¯ò¹dyt9µkݾµïÙÅ•^d¹È/öó"áùƒ.À“P…pn¦Z³)¢IÇ£.\·¿C4{¼ë¼{+(Ú«(©|’×?æ›褋é§MòMî•ýR»½;ŠhÀÛ5—ê„Wù_Ráäñ•­ÆxMUówè–íyðñó³`޹Ö9{‘Y‡5]ÔãÃiÏ7fìeAõC›ÌÜ8È‹j¼Ùü+dÿ»¸@×tÚ›>Õƒ¹9›˜CTÍñkþ‚«adŸ³7$ViA3Þ>úöÓ“dµ\W}âׇ”Ðéì¦ÚqŒ>´ÿ]D‚‡>óiF -ŠË:?° cý˜Ó›'íW{HöLúºCŠÊ-F«¸šCg‡šæÑ’¹ꬾMoƯýdaAÇ÷Öß\s¨'õ Yw¢V¾ÐÐ5;®. ºÎ½Î9õfä»"ú5p]áÞìÐ?Ïߥ•Ë&µó$ êÝN>úo^™äTX»’Üãâ®jSßö¦Ž¥QúÓbWб©[-›ìIû²™¬ÿ c£"?ã.¿lqÇì¶Å4yú]…õ[Â8GJ“†×èÒßÞ“Øiˆ"s¡ÿ “³eGû+s­ë< ˜Æt,0¹E[mBæM¶\Nºá ¾ï'<бSkͯ_æŒz]i¦_L‹zd4 B‹_¯èß\µ”‘WÜç<?JûÙ „çºVüÆ*g±3oÔfóbšÙlËÏÉ!4äÄLu–PÅ—Cyžó<ˆ·OæЭðôz· œwf]Ygü.ónºæÝ¤'yì /¦: ö ãN×”ïòû=M²’ã°iøŽb’Övja“˜ûîÈÝ‹¿UÌWJ¡«Í.[®äÎÔHl2P^L;[Çý&½øõåï-¦ì…ò ÝvÓ)!ÿÍ/“è–Ožâ&:µü˜²˜¾Å¯ t9üçû­4Ú¹/ÉÛƒžÅ~ÜÛ­ƒðüAwíë;ß–ŸŽsž:]û¬*¦H§ºm¿¶ ¦šsMvÏ=¸”øùM|;W=*U¿@g¡[PðáÔÕú.(& µi‡Ñg‚èRf·Ç†ñËh# Gí=HÄÒ@W¡ÿ {·çà oON¥w©A;m1Yvݺ÷D§ ºÀìekH‰÷ÿõ U¯6x¼Jè?è6VÜ}îs„K?ÚvÇТbjÒÕ¯¶RE}&Ÿ~ùÝIJonpR õ Þ-¹žs…þƒŽ÷ŸtåÂR¸¥Ÿ_Ó ÛÊ»íÚª¨©:ùQL¤”ŽÜ|®_jíA Õ$òßO ÝãßGýÍŠ¶qc½¢=×~)&×6½ì%=oÐÜ+y5®©¥„I­¼¿õiøÃßõ'ÿ•B'øàqÊÕõ}^T/¡Ý>ZÒá:±Ù°ùQ)9ܨu¨«œ¾žMw;¾]xþà¹eÇ:Œ|KèûÔ1ŽõÚ\£?]êÔ–’±órïúÞîÿxÞEÐe®Ø·xôZÒÓud ù¨¾Ú1"£BV¿¹±Œ&ú$Ý 8Zu~…ï?èä®sž>m»‹Žç4Ùï *¡Æ5–ÈûË.‘ÄõèRÚµe݃)G«öWøþƒîèâ¤Ô†ÓÐUv°{‰°}•®¾ñHž÷a Õ|vXüºáQZgû#²÷dþû¹@7QqnINN¼x uëh•±ø õ,ÿì+[JÍuãŽÐ3õF£•BþƒîÔ¾Q¯+rŽÑ‹´õ–ÐíЊ{sü«öIIg{¼ïˆp~DX?ƒnyƒ²ñwë§]ÎK9éв¾bfÒrõe²èq¯æåÑRe5déÌõGþ™ÿ ëäuøä¾Q§hB¯Ã}k+¡W!;o ´¼D¡Óß…ù·\Aì•¡üˆp>TØo0/“¼oÐæ[0%Õ+¼ãwy8Ú;ÙûíÌiô«}ÕC÷¬ ò ƒúï?òßS݃&E7¯§|êK(wæ¢fÖC.ë^+(Ä+bÖû‡‰¿ÿ…þƒnŒE÷Yý+/к¦S–ëã:'ξÿ²óya¼"%ÍíM ¿¥¢ØJ„ÐÐE³Û¢ÃeºÁ¶ñ»¼xT? ý9ÒÙažXNTx©sˆb‹D‹zçóßÏ:¶z7{Âr4=wúáûº4ù;ñÙ?÷‹^1. Ú"Ѹ¸F½Öðí)¡rü]hº}ëqäúݭóWsÆ+«ÎoÎ&ôò!êuyT·gÑBÿAöbøÜ9ž×ˆ_ç+¡­-K‚JN ó£…t#e€hëýCÔ0À¹Ë°þ ]k]`½AK‹Û]ÏêPBm&ˆü-§œöEÌIw¬¢Í!*zS6zX=!~.,“ìUžŸ×÷¹ŠÞùEN©Õ¢„&ÄEŸœ¯ö#Ͱ£9ñ>vIg{×\ˆŸÐ Þ}äeɧ 2_sõ–Qý²tÿ´yÀ°”õ"  ú-sJõ²Mß:鯭/Wz òte›vyV¿ICßâW1ÍO¾Ÿ–ë¯ Þgy!ío± ·æAÝ>‡”]ߎKËnë‡P‹y\ú׊bê"Kêú¢–¯°¾·˜¸pnKáíƒ4½_¬zøQaߺüaÉÕ_7ºEKæv¥GÅäÑe¬¦ÃDob«qiK„}öƒ´,¼ØxN'þ÷WB·eî’&-BéfýÚ9…iÅd^Ý꺗ຌ~ÚžÞ^sÊ}ÞÐ@aÜ]e); y›&Èj¶ÍáŠ_xaPJÏæÄõºhsPxî…çºQY†F5 #Éw΋ɧmÐÝ2÷£Â¹íôäNϲ[M’nÙbª7•I®ž`óá¤[>TÓŠÎÇ®=i}n]Ú×søJaè êÿyÙ¶4^'‚ÎPÚñÔ÷áÔêLuéÈ=Å´/ç@÷€íûhã×v][)øG¤•ˆ®žžÂ¾t¼/æZòtýÂi¾ý´hC¤ õYáxøTÅJç4×¼êsðûaRèöÿè*œ‹Æ-ÐÙ¯h‰GBM«Jeöt/¢Ïg#–Œ9°[Îlÿe¡0~Wp•—ç¾mI{¦ \Ûòó^ª£ÏNÐóûÃzKÊ$èôÛu¿DRÛ&‡÷L+$ߩDžŽòãÖo 1±PXжIGêðÝ[u™ï?èêdm?-ŠŒ,7<»PH¹‹WK*Nâٲô­a³†³+ös þjÍ·'îá›À¢g£hnûµƒ ] )ç:W°öÉ®jÝe²Œ<ß÷Ïq twëõ¤+Ÿ£HwñN#…¼Ýto u)´œÛ:¶€2î¹-¾ºé"ç¨ œ+‰¦ ®í£ùþ3ê ñs)ÆsO5 ý÷ÄÐîÇWÙ) h¶™$>ÃÿWýã÷_ɇ,(€ØNâ>â}Â…q t|^ˆ¡©äSÛm. ÷ò¯ýôÊ/sºáü'ßÐ}dÛ¬ícéªx™•ãœj”™’õ}àîä6Àµ¢ÁS«gfïûÇ9)t×:yͲŠÎqЋ"ÛmNnW¹¶ý‹ºÖÞeEÖº €}·W1õj(¯sŽ?7Kî-z“T³€æ_hïyëaÇÿV4.þIfŠÑ>]žUâï«åÏ¿™RKWš|;¾¥(ŸžØ~Ý0ò7w]x„Ù~+ºÔpÿÅ]÷Ñç.,Íc„sÐݱYŠ¡B¹Ü²ËÙ’Oo'¯9cíyc«‘MZZ›•˜ÍÚG÷Kdq 9á| të>Yxýp‹£vº.ò)pk›§wJnpMtó,·²$ÌfÇlÝGÝØcà'醵rWµî¥{-lê>a¾ÉëDÐõk3°ÃŦºûŠ-äÓ«‘¥NY½ƒ8þ<»±ìà™°W˜ß ÏtËÇ ذ@C^ly¬Y>šÜ:Äm™gsKÝÁ‚÷îw4aúÞœ‘Bç¾jï„ '5d78ܰÆË< Ì’íYº,˜ X”>«‘µU>uJhµ·êü ßoÐyÖšVš]¤¡«Â[¯Rç‘d”CîÅÁÜœµýz[éŒÝ÷Röºôï±9¸º`Ùð­ÛÅÓ®àž œ}óhQWÏ#gorïSÎ6˜7ÀŠÌE[fw²—xr¢úq!ïA·¨¿ëÚsKãéØûî%y´ÝøžÝ™!œaçË7žÙ[SQ·5óÇãûñçâ…þƒNw\Ê/žtÇjÆæÑšê¶Eµoqê­,ðÙPx4[ ØK/ì?›/œ/[Ž<´r×òìxzƦݭòèÔКÛs#nqa#³7XkC#¿tè7ìÍ^ê]´ÔfÜ/^'‚î{Ý®E}[ù6¦Õë\ê=4°æÓm¡Ü…Ãazƒ§ØPdÿO.uÞGêW§GöÉäã­ºã+ΖLK ÓóÕ{¨s)UßeĉÁ·¹ÜÓ ›*/Yïw¿—ö­Ùn¬7¯“Bwx€êèãý “˜~îX.•qŸw×zz›ãÇVÔ÷uæ’UF{ÉÀ{bÒ«ü¹4èN$?6Rp Ô¬Á³6Ö²\Ê/Ùa5EÆí(í5SôÊ’.Î4[µGØãÏÁ)¡ë¤[@N Ó'º¯ºÎpnhÌãs‹Æ[Räù#]²í©Úßäû:Üü÷Ö‰IÜ“­,äRW‹[_wf…s»^K£GW³$~?~oÕ>%ßйMzTö|I"•ža(sh@û’=Û6ÜáøõXKÊ Þ¶Ôõ÷^ò3/í}çÿ»èIË$®í§M$ÝqÜ9tîºN½Vw¹ØsÏ_Y:YÒÈû³}:ÝG«ŠMz&mΙA·÷]FØP.‘ü/»«;îÈ¡aÈž ÷îþ‰}ÉÉ»‡+¹ÿ®Ž •0߃nÞ ûž¢×‰”{'#âr”î!»ÇÍÐÔ¶&þ>uåç ø{~ÿ6‰øu¹Ú°¤üâýv÷¹'ÙÁoª?~fÂWWa~)ŒW »<Þæø„$2\Wÿy65¾žº ÿ>7]wÀ@FÇïúÈoq¥œˆˆâh¹°Î ]»¥ï¾tH¢ù¹ œÆ„f“Û5÷QÎ>à^[O¤63léVQÍ’ÁÛ]ÉéåªÓ/äBÜ„®0g׺‰Ç’èçþ9»²)z^½îçWs\P‹Á}ƒÚQå×·ö¾®´ãj½†ÑÁ¹NèÆì~§¸›Dgvhi7=›†\[æžÑIÍUù†‡¢æØ¯a¬gOO&§ñZí*ä/þ°º5‡ dzÉÄV'=Ì¢Æ}.XùUÍóÕçvˆ…?ž¾ãÐ Wêª[XæuèDÒkùC:%5û^É"ÏæçŽ8ZDpUïår»[¤­w¥‰‹½Çß–B÷ªäÓñ›£“©ï摸%²ècŸk½ë'Fç l…÷t\‰Öé®ÔñŽòÕ ^W ]â}u Ù…drm6æäþLºðØÎrÁ¢(îln½>æ¶4?ýàjñ6Wºsî˜AàU¡ÿVb^Óô]ö×ÛÉÔ»Åܹ?§d’"ɦ¿GD'œo¥¹¿Ežâ\iIÂWDHá\5tÕ³ÈÍR“,¬¯eÒ§®ßb;õ‰æø÷Ÿí©_bŽUåÌýÔÏd·<‰ï t±M³cs“©¸Üb´H›AÏÙÿæ„ó­äÝ(û÷°¤ý4áäŒà¬2á<nþ½)þ>7·ÛÕÛø> 6F…N8žF—®Çv©ÞQÃMI90*ÄÝŠØ[Ó—G¤FVþ¯|ú º«Ø›™)4²eGË…iÔ)Ð~¡Å ÷ØŒ«¿ø&Æ—ûXŒzˆ„ñ#×õom¥¿µ•ôþÖVú[[éߣ¶RU­7™p_°ßŽ}¾éÿk,ýï®Eù?ªÕýƒ—É…zÝêÿäKÿ.µ(ÿ³êuÿïð2©ªÙýkQ²ºHU^&ýKþsÇHÇGÇGÇGÿýñQÕ¸ˆÅ'¡¿ÙoÂtfÀ„ F€’Ð}+ p*PD^æ@4B 3NÀ”C63àÔ !ÐÉ€ä\)pa ˆ¥@´@Qœ ”¤9,M€ð¥ÀÁÓ ¸5¨F¦2 ¹ÿ›kOþêsÿ»û—üW©Ï­™ pþ "±™7 •À‰N”  é™*€IP @ûoTwò?«>÷ÿª‡É¿Öçþ׺“ÿÑ¿„=Z׎þ®¹èýýýû¬±xá,ô+ûîì¿›7 •ÀÁJ”  p™*€L @ ôÔ$À¨@9!È™9ÐÏ8P Í€PƒJ`„€(J M @Œ`)  úœà T ˆHÍh„ jœ€?(²¦À„ FЕÐ}` p*PDÈæ@4BpþŸ©Ïýïîcò_¥>·è#±I€3Pr B¢3r ’ž pþ " š7 •ÀIQ” è#AJ€3Pr BÂ4r ’§ pþ "™š7 •ÀÉU”  Ñš*€‰W @ ô‘„%ÿ‰õ¹ÿW}Lþµ>·(€Vü¿ù˜ˆôþŽ—þŽ—þŽ—ÔzÇKÿNã%ö¼»ýƾ›ÿN ä@#*àüA)0Dà2n@ *™ (A.0@P3. T1‚œ(€è#àI€3Pr B4r ‚¡ pþ "8š7 •ÀÁR”  pš*€T @ ôT%À¨@90D5n@ *‚® (A.0@6. T1[C  úÎ&À øƒR`ˆ`mÜ€Tþ_àgRDHæ@4B"1NÀ”CÚn@ * (A.0@Ò1. T1’(€è#!I€3Pr B‚2r ’• pþ "y™7 •À7· (A.0@b3. T1(€è#éI€3Pr B4r ¢ pþ  Aš*€ S @ ô‘<%À¨@9!™š9ЉÕ8P ‘hÍ€PƒJ`„Ä+J „M3Pr BR6r ´ pþ "a›7 •À \” üoð3QƒJ`„€ (A.Чÿw?6Îø;^ú;^rÑû;^ú;^ú÷/™÷œZ¸v#ü;P-»* p*PD\æ@4B3NÀ”C53àÔ !Èɀ䂡8("Gs !Pš'àJ!§pjP ŒHe@ r‚ª)pa ˆdÍh„€kœ€?(†ÀfÀ ¨A%0B@–%ÈÎà T ˆ¬Íh„Àmd@ r¹)pa ˆØ¥@´@A^œ ”‚¾9L€ð¥À Á ¸5¨FH2 ¹Àɸ€0PÄHR Z D"Î@ÊH„öhK2&À øƒR`ˆ¤cÜ€T#$!P‚\`€„d \@¨b$()P-ÐG²’g å@„äeä@#$2àüA)0Db3n@ * (A.0@Ò3. T1’ (€è#!J€3Pr`ˆiÜ€T#$LP‚\`€äi \@¨b$S)P-ÐGb•g å@„Dkä@#$]àüA)0D6. T1’²(€è#AK€3Pr B0r ’· pþ "™›*€É] @ ô‘è%À¨@9!ñ›9ЃàüA)0 æ‡ýÓïM¢÷ß÷Éý;Vú;Vú;Vú;Vú?=V2î)pm&@ rY[P¦À„ FÀ’Ð}/ p*PD™9ÐAàüA)0D°3n@ *‚Ÿ (A.0@ 4. T1£(€è#HJ€3Pr BÐ4r ¨ pþ " š7 •@Œ+  ú¶à T ˆ|Íh„@lœ€?(†̦À„ F –Ð}màüA)0D7n@ *‚º (A.0@€7. T1¾(€è#øK€3Pr B20r ƒ pþ "Q˜7 •À‰C”  ‰˜*€X„öh>Œ8("$s !ù˜'àJ!’‘pjP Œœd@ r•)pa ˆ‘¸¤@´@ILœ ”’š9àL€ð¥À Ï ¸5¨FH€2 ¹ÀÉи€0PDHŽæ@4B¢4NÀ”C$N3àÔ !‘Ê€ä$USàÂ@#ÉJh>®8("$`sàÔ !!Ë€ä$gSàÂ@#YKh>·8("$r3àÔ ’_Bb—%ÈHò¦À„ FÒ—Ð} $À¨@90Ä€À”˜—+tÿ2Nb9¾ãßu¥Œ•þOŽ“¤zÇHÿ·‘þÆGÿc£ÿSã"sá>a¿%kWœ€?(eŸ‡ dÜ€T#)P‚\`€€e \@¨b0)P-ÐG0“g å@„àfä@#:àüA)0Dà3n@ *¡ (A.0@P4. T1‚¤(€è#`J€3Pr B5r F¦2 ¹ÀÕ¸€0PÄ´R Z  +Î@ÊAØ ¸5¨dç•”e@ rÙ™nhg å@„`mä@#nàüA)0D 7n@ *» (A.0@7. T1‚¾(€è#H€3Pr BB0r ’ƒ pþ "Y˜7 •ÀH„öä$SàÂ@#±Hh>’Œ8("$s !™'àJ!’pjP Œ d@ r’•)pa ˆ‘¼¤@´@‰Lœ ”›9äL€ð¥ÀIÏ ¸5#Jh>’¡8("$Gs !Qš'àJ!§pjP ŒHe@ r’ª)pa ˆ‘då@#$[àüA)0Dò5n@ *’± (A.0@b6. T°sÜHÔr ¶ pþ "›7 •À ]”  ¹›*€ÉÞ ¸ó™ÿoã ©ÿßÙ{nUg»µÿá¿çþ‡®ú_ñÿ¯´(“4iÛçä…¯ÉBýÓ£¤³ån›öçŸ}ߊýž¤ábÜlêÛi9õ¬m:xWgkŠ»v¤vÅDzáœ$±úqˆ¶²2&[ÖSE„½¯q;kîÇf0"Õµ¤F;zs×L«ÎW»W«)_¿B]ÖË–»‚$Óµ©G´©™©ôÂøýÁw \•ŸøóúžýÜy˜ü‡$˜‘Æ×•BçšÀŒ¨“‰UWy¸,•Žt <Ô,‘ãë€ÚÒ€ƒ{ì?L•}vî´”¯]aî+ÓO§’ ÑrÀ« Ù¹ýæ­‰\•oåéíec¶5ÿ´]2ÇûäÈ_ïÃB}RÞUݳ¶¬²j2mê=u½ÏÂ$bÕ|“¹Œ½¯{Xl“QZ炯;ìÿÃW ÝþA܉5“iÆ›Mâz©ÞtÇš£ú§p2]!v[šÚþÉ'éìÃ4^Wð„÷3vn¼Îà+‰JÅ›7M$¯[ÆÌóIát¶Ý“ìè§Ûò ×C‡…z•6¿›Ç:ågüxðxÞººéýø<÷M¢+3×îŸn™@´Ü6µmQ*§+çzÕ^¨x„¬~¥ µm3—ï?ë2 «Ff¿%‰†Ô-–O¬•@‡XØMËUùå˜:u±:táuºQÇëÑcÞ7[]hf›D‘‘“OÐ¥xzÐÔôÀÒÅZ®[Í€e›ì(+äí¥­GèõIë]÷óíI »ßncÉ€‘¸ÎiꌙOdû¾x¥»– |\y6ÝÍ– `…FxRµsÒX^'…îÝɾ¢Id\û”Áðr 9¬Ó˜¡åª|U?eÍÛµ¨ÁQ¡.?.Ð ß¿÷íÏDšÚÅúóæÃ ‘*ª»½Ór?)xùÛÐä“-f5Û~”Xµ¹gûgñý]»q¬¢w"Ý1`•5c*ñ™)Jã\‡ÚW<èeC¯{ëñY{T¨'Ìû«¡Ó,_”Ù#<‘Rû®ïV=+Žnê7lòŒ4®î…èë¯_[“Ÿ»ý¸wÚµ­n‹vËx])t|ÿ&Ò“)£êÝGa'«=¾-c½ù´º mý¦ÿãá3wÚ¶òT¯’A³ùþ³Á}½ŠU¢L$ÝcÙ>ŽÎ_¿õåÊÕ4î+WºÅ†ÖêŒ^å”ãzpͯîüï)‚nû±&­\&&’í¼©Ã"biߎj†½òÓ¸û«š¾l+£[C+#-¯ÊéDjxÁ§t¡ÿ 3|¡ŠìÛ1‘v¬m.ªgKã÷„ïnZ?«òZ’hir:U.ÔË|Ö¡ë]øÞãØÇbîÈò†±i0lû¨tޝ{lKëC®vk_&§)Å­çñßÏ:¾~sÔÔhƒbè¥)õÞ²&[úHSÃc‚ù]ïq§ß9­ýÕ½yçRþ÷TB(ïew&”?M›´žCgCƒŸ_»”ÎUϾº´Yº²dN`4xš#B-ï?¯†Î¸#û¥hÔŽÕg~FSI—Y’ Ó9ÓCƒ\‡±üå‚ðd¾ÿ 3™²ì”…$AðÓŒ¦ç ¿x,n–ÁUù·eÇ8v¹-'å÷µÖwÇß²2‰÷„­&4N )¯ò‹WMަ )W{¹OÎàîMõnµºóýs ìPO¼[eOOùÔygWå×Xó†ÏˆArz6˜9•éóý]“¦2Õ•xÚÇÂ¥"Š"V1Ü ®Ê²¢Pœ-§ü§Ì0 ßÐú=âÜØ ñÔyÕŒn5$Q´¨ZjO3¸*_¦»ÙÅ—¯jåÄ×Ë÷tË}R‘$žÎwžÑ#ïE$=.N¯g˜Éñ>2bUlO‡È‰÷ÅáO%tf߯×'Míô˜KÞ‘dYf“79“ëÊÊo¶µ¥ï ûLxï-§w²Çn·*ÇóýÝØå[¾¯KÓ~'‘ôÆ!ÏþжLNþѶÅϳ¶‚¿£œðÇ{ä æûºAQrÇ5Ô<5¯úØ—thxŠf«*“›3êøÎÓí¨Ë…U¬R.oúÈ-9T›ï?Û2ɯ£“–i¨= “Î÷_Ü.Ëäª|K3—ßP•î u° tã,tRf_ÞEC‰ý —¬›A|]þ,®¿¢¤=ÍûzÚCÿÈŸöt: tšcÇd•ÇÑæ_³÷RÓÛe“§·œÅ™¯Î¬—ñq×óèZ#?¹P7Yï?èjV?5ÙïjÕ{Óãå%5uþ|ýéÅýYÜÍ­ïUföv´•O…N¶9ýˆøþƒŽ¹™;ÄјSïË'š«ÉòCÒ#w³¸ºB{¶‚ßœnÍÖÒt¿(¡›~¦½ÓÒq¤x´Ýójº¾Þ-ÞêuÇûéÊ(OÁ ä‚?É@¾ÿ ë7’=a±‚ß5GÍëV››Ô.ûOÜu;}lrCG9ñãV#¾ÿ {8aù˜A±ùö}ÿ€^|¹ÙèçälNÝ™9_Y“ÎNÈKN¬jèé…ùþ³+“D¿ZÑšÄI³Ó£äÄ××ûù@×еn×íFÊ€X*‰êw*ý>uzq<¼í™ln¸®P¼%…ž X󱜊õ•DÌü1Z×Ð} 3ž{øC é‹é}j9í¬b…&› ¸¹Hê~À’X•¸“ßäDIDzKÞëtRè®EZ»ÔŽ¡Ì»Î´Š»G“äS‚Î¿Ëæ4úþN5¯X’±Î€Èã×é]­m&ïvŠ¡½ýj¼G«&6Ò6‡éËÏZÔ±"]Gyйîß¶!%ðýÝÒ!©M¾ö‹¡Á:Ãä»4}ùúw&äp±ß×|/ó²"æ¢{a¿µë:þëî–ü渚Ng÷PM'B®Ü™d|—æ6 Rù¬Éá<ŸkŸojM ™öiáûñºRè* ¹M‹ß$ÕŒ ¿C)½Ëëp§s¸il˜þÒš6ê Ó=¨1+»çË_§ž}™„¯OMíÄ3FºCÓiœÃ±ªãCÙPíçßxîA¶íçM7Øð–ï?èä颩hsÎ §U'Ãû$þÈáx@mçì™ü΃¶î«õ¥F‡J¾ÿ Kü85zñ³(²¬ñ|eÆ”pbOáľ¹ÆÉ\ØÜ‹}¯{÷YuÝs+…®K?½#é£È¸fœUafú°«_ïe¹\BÉýó±{eÄ\‰nM÷ Ç¡CÑ%: tj0£ß(Ò¯uQV¶,Œ~u:7ª†{.Wih’µHF{{+Ífvô ”‡‹^¯è?X§SB7• #:FQ^­=öoÊoSO¿\.§gÞ~XzkMºî7{Yoš˜¾ê『~þÝÆÇEAù‘´–•¥ß|›rŸ Ïó6—«ã{tXj.Æ%¦-<¨IÌ“†FFét¥ÐM¼ã!âô\ãÀ+}êݦ»½[¶yÜ!;ŒQà©L* иN]ªwÏk¦»cO~fëP&iá~ȮɬHúk¼UÔ‰Pzot¾ÑÃyœ"vÍ£ddrg ³.¥¾ãÚ½|düï?èn§1ìHJµfÆ•¡¤«gè’ǽÔs»»¥…-‰?¸?˜œqÁ¬¾Æ×çã'tû//HAQŸÄKÜ#oQµŽkŸÏèþ˜>þ¢ #;jÎl¿Ë…ûl(ßÐM ýð3Ó9‚B÷nÓÞ\x‹òZµ9îQÿ1%-eeöü€ÆËû_Ï÷tGį>0Š æZ”[B?ÏÈ¢jW<¢åuRZÏÊ·'We™o ÿfŸÜ›>‰ï?è.3²25élÕÝBH?]$ÕÏ|Tå“NS|u»2̓ìM™#¯SCW<îG”šæïyëBsr䫆‡=¢=CR.uq±ÿs¿„d…'ðý]óe»çt4U þk7iÕ”›œzD#w©æÚÑK»Ýþø]|<˺{Àß/zŽe’½SãNÚ¼âhÆŽC3|Ý$]ßÝÈîÛ+——miÇápûº_夳Y5ãïOtû0Jo6–£Ö#§}ú+˜ô1>YnûH¨W)£G5Füª_óOœàû:í«ïjQÇñ“ÁÔ3ÚqGÃÙÈcìÂÊo‹lHÙ†ÿçs$…®Ý„ì6S^ß§7º4ʓӫig?´ñˆL#æh¼·XS½]K؈’ø:«üýéÝ÷ެë}j8øÌºÛ/ƒ(®ÙÉyߺ>¢÷ô0󢶦­üæ×ó ¹ÕW6í÷dßÐu[ôÞyÑŽ{Ôy˨A`ÚP(Mï»øå¡½×éôâ#æIgàyÎzæðêññ º?w©oQÇ~ÃÞkt\GßÜù•s 8(|!=„ñu>ÿAWý“uæ6ïÚñkkEþÅk´ý¬±tb‡$ÚüÌÌõ*tÌä§\¸Îž|þ[]&É™:§¦Éý›´æHªåï‘×Èû©Ò;¦ÑCÁ¿FF?¹”ªñ ?®Awí+…L®WSèa ‘y¬ëªÏ¥ÔÿKü,±Œ¶/Ì¿^(§W36vNiÉ÷t{¾ö¶¼Þ ˜î3»Ï@Úñâæ2›âRªZ·ùV—9ÈiªÊää׬>üó]ãwoD¬úp«ñÔ2Ãgï¼ØRªÊc㢓¯XÊéq›3ñ¿6·áŸ?è2™-æ<ÝÞ>óhõ@ú4fÄ4åµR ¾ÐÁ¨±-uZìs#±Â¾¼»´ÿôN|ÿA·aTÚ†‡èoÞ'1€v¶~–ðÝ«”îܱšÞËŽŠG³Àâ^õ¼óýè啬 ®‘®ìôñZÐÔ}ËèÍ¥U~‚¡œö†%n_[“ï?èLò×D½½@Ágý~îZ@¯_´xi©0þs ‹×oÜ‹åÂ}-âŸ?'ŒÏZªíªq•VíSµ™jÀG$¥Ô5ônòôÉ(dø°qU÷YwþùƒŽUÑo&õ§é_†_é?5€Ž?ü4³{×RÊZ\cq7sšÐûKõ›c=„õ_^'®ïÒãGñ—È÷jHÌÑ$?>﹉~)1ïeö4ë¼Ürþ[9M×-ôæûºv=ömvt‘Zï­aÿ4¿„NYµ<›[ÏŽö˜T휜N7`Yb¾ßð÷õß™Þyê<…›ô: ×w~Êéþ%äP§ËçºJ©+άRVýjÝøJ ][ŒVk5?Gù±güð{$ÖÝÙ{‡s‰à?cC§CœîK–SÙ„î¥9¦Â¼ºp'Q×ú %¿=Xº3€Â"N47šZB×s—oœÙÁšš}¼Ð¢DNtüü»ºìk̈ø4,e3€Êc65K¿R+º¼PYÔ)FNS–<êñj ÿÜ­)“ ßò£øùÑú:Aƒöáþ²ûpáêS1yô:¸`a%uկɉ¹ÐtlÄχEÐ…•uéù|…‚Ü3ò@Êù1§Ö¯ÄbZ—5rÒ“»–DW>?wN’“~òõ­½›Žãçíб`xÇÓÞd”=Ô(|V 2cÂsÅÂü×òÞô.8ý?–Bwäí¡©óò=)÷ä›±^$ªLéÐÁ¹XðK±"ÞŸÔƒjàfyóŒ×¹@§»ÝZÊ)jç]Õß’|Å[ï¦Ó8wÃQ·µVû%æb/G!¯ó:%t|öƒÔæBiÏ'S®Q›»ì’EÅTU·¹AÂàK{ÐRÃç^ï˜À÷t|ä}ԣő[ÜÍkt¦üNÅžEÔîM—ÛÝ‹¬É½µÿág<“<Ùðº t¼¿– v6ƯÝuÁo¸èÏýR_7Qò|ð¦óý·¶ÊOyƒàót®Eû>)ð)"·S=×/³°¡m'm#?{yÐ(ÝÂ#¿>$Z[åÿkCXYé7¨9{PeETUǺvï·ZZ{ïüq:LÉ÷tkWg0¹¯f7hï£íõÏ+¢/ ­.…ü²&+q”ýÞ°*ÿp¿Ž%…®ª¶®\y[=ÞÕäwý":Ãìa•Öî—YŸzŸjœ*¬›A×­3[á\Çéêk«¢Ú'^ì\RH […_aM}ú„u¬Pˉö~»óëtJèVöÉèßdõv.Þ½²…}˜Š¾/qŒù\H…MfÔ_öÑŠ’´ÎÝ>Èé~Ù‹¸î_ùõR5t ~uÉ8»‡»°~Ó©†A´äGSŸöû ©jþÝøµE-®¡Ç?ÖKK¡+·y±áÚÛW‘›º2ˆÎmÛ±nQ!ùÿüúÎóÅ»ŸO¨Š¼No]™Äü™¡ü~ñΨiþÚ~1A¤¹™x{tŸB úiy¸ñMª5³Ó‚ åÔkîÐV; „ukèFWwg‡×ö“Á‘7¢`jÜFuâÔ÷2v’þªgKò¢ZSêõ‘S†C‡ý?øõu t‰•_Z·jtŒã}ÂiÊÄžÛšh ˆíb¼^iG¦ö †wZ/§îÃûŒ âuRè1[¯S>\ƒ/[½ ¦%çû§ôô, %#f5mjkOl¶Þ^ÏCðïàu.Ð5ý^ÓìpŸãÜÓNsFלw“ ßÍ©¿¸€š6ˬèhÿg\&Za¨zÒ‹ß?PBwöþ7çò»~ÜÏßñ%Ìj2-ÿk—Z¤[²§$ fL„~Ð;µÃŒß¯PC·îdÁÞ­V§¹ß5çÝíBz5NÛù&ÿŸùe&Öéê!ô ß^)t-™]Ý%—QXoÅŸò@öpº›OK.fv{,£A3ÆÍ}˜*Ö ùï§·¾L²tæú¥•g¹Šæk/ž®u‹†Öž³§ã¡|ÒÙ{ØP˜ˆ%v99ÎkšWg!ßžºØ–#Zžçxßì[äÑÿÇã¥ùÂ>Š5í©Tý]݃Ʊé€/¿ÿ#®‘²äËêe¸Þû$ýÖ<¹EÑ«ç†õOUõÚ Xê–…yÑ…¸·ŽßnòûTR褔¾muó"÷{é™a/ç„RÏ)•Çf×ΧcŸk/WL³¢lÿý²õˆ»ÏÃÚäyûðû….Ð͹±¸fì·K\;ŸÚ;æF…Ò…íâB òhqp¯Èé­ˆ¹rµø&'éºF§ëNçëÑ+¡»ßaÚA÷™þÜoñÖ©Ãnsø©Êû3þï_9¸Gÿ4p¸¨î÷¼N ]MÞh‚“%»s!þ·)uà!3‡}yòß­R¶@Vµ.ÁëJ¡«ÞOãSöò*7âÒ›„~†a4½úÁI ó<²Ù¶|ÉÕtkZÜõõÄŽ<(7sóÜ%ü÷ÓÛP&±×r ‹ç5Vì£üë½#FõÊæÍ6dtvwTýΞt¾†§ju¡ž?t7w°_úg=³ºßºŠ0ÚzëuÌŠo¹Äe¦úœ+£Ô¦–5çšxÒ•Âñ}“¯IùþƒN8¸g­¢ëÜ0ñŠK¯—‡“ù.ƒ5âsé˜Í²Fwßb¾è~.¥pœ§?yºìë6Uq ¤šóSÃ)>zvüEŸ\r?à±ÔÖVð ô¤…ÍÝÙ ýâtö£&wèÃÌ;£ï[ä’nyÊÕ–Ä;¿mÓÌ“®úâ^×úºñþ³oÖYÄíªË AïP[KO›F¹Ô®ö‡š%¶tílæ²ñ‚o¡ÐÐ pV|NþÄé¦ûwéJ‡Ž%¿rhwûßµvëÙÒ̧·{]ò ±›ØŽïCP ]Vy–ÞųÁ ›ïÒßgí|“rþÌSê,ë»Éz7æo˜Ýã÷Áõ6b¾èi>ûÍè›Ü*Ñö]š:¨ÇfÎ/‡Ú_³›½ÙˆÒ=v+dÂøÊƒjïä}ò:)tÛn7™Ôþ7%L>É©Ë}êñ¶ïgÿº9TµÎSåk=/oÓ„ Ž×¹@÷Û×>­zÈ-ÎFgð|Ÿ×-ê¶&;›ªüeÂKΊµ=<)Òm{uñDÞ÷C ]bÞˆóצ„r½ªŸ,Žù~ŸfY‹½8e6ýZj9o¯}Õ¼˜ï7üýô{5FÚ> åx¿ë0iƒ÷dY6÷­‘2;ØŠºø<æIÌ…(s›p^ºÔÓ‰™]osVkílùèP¶ßÊŽý²©â|›Ï[W:Òé.ï—NYêIÉÃ4[8/±©L2FgÆí¾šPm G÷ ÷ÿ!«ÊÏ–¬›~Ú=s'E¿á·ƒ¿Nt:»âÓaÜü=F˜Jq4ݯåŠþáY¤ÛÖòs —´› =aþÇ÷t«t1áï+¤¦1í•ôveÑ‹`Eî¶%öôúöÆs]»z >«¼N ]‰½þÑİp.OÂ.XÍÇó©Yt©ÞÄnªÛQÙÊ+=þÑž tõ¢[öcïpN’© TGÔô¬åò…ZgÑÉK©Â®Ëh§ìiüZždÚ†9_ >6Ð}î”â–~‡{¹îV‹ï‰j*wË1‹)ϤcØ…ÛPû]î¦ô¤’&‡ 7òí©¡c«w~óîrõrÎ;ªAÝ?ŽïÔìN&õøÚ„[ÙãG6 D\9úl²ø0ïU ]`»E–#JîrÁ§Þ^ŽA­'ùŸy8“†êà¬I"µí¸Ø³ê>àûϹL2±î·ú£VÞãn ¾=ÖüHýnw¨éñe™tt#3úµ&Ýo{Œ×]ô×”½|÷ »¼tÚ ¢—÷¸²eÒùâôºÐì¡“Q&鎙ÚPô w£æˆG“Ø#ø–B÷”M7d÷¹–êÚU¶Œ¤óƒOzWˤ~õ‘ÙlhJ†RÖAíIïëæmnë'ø&Bg¤^°þå}nâÍàíšÅ‘4Á»v?méŽCË(ªoÇÉ“{ ó|ÁG :.ûâÄr›Ü¢&CÒž¤.mL¾/;AÕZY&¾ß`Kɵú¹éâ%Ì/y?(%t¿NîþáðäçWýø© o#)Þ ‰i†]íÚ¸¿y®vÔ²¸¬óƒf^$ì«ñý;e±ˆãæÊÏY24ŠÖßþvHIuYìh°n@€çˆ…íÖBÿA·È/öó"-ǵ/“Ws"Yôš}íô2ªü¾ÈL·ñîI##~½, üÛ6—IV´n{'¯£š ¿$±¶I‹¢Þ)+×uLH§Ì“÷45ØïæI‹=ß5|/¡sº5cÉÑjnÏ÷jˆ¢I78–NF¿ïäöÜ[;ùxRù´ü¹E]ÿ=èvÌÚýk¬»šËº):öeM4q[_eø,O§ŸçW§ÛQö“žÏ·x’³÷òü÷ »^(õRs»GÞÝö2*š®Zhp³w:Usj§K žD¿ÒÏŽ|g¡ó?:oJêG5—ñ1¯ZN‹ªx7ÇúXeuè\h9×V˜w{RÁ´}Î]— þ{Ð]²Hô¹[×íZ¥k¤M MÓ¶Xü%&ª|ìý­™Õד֧ޙ˜¸Kð-…®mÖsIöœŽ÷¹¡W½C z¦Ñ†Zn=îÚÒ‘žæŸ=»ê/œ|g¡»Þ´O#›ܵYÓÊÅRO·ÄŠei$ø°Óqv%žÿðÿÕÛR&ñc¶ÁW"¸ýއ¶µŒ¥Wgm§Q¿êÌÙÞžMññ2ûæIìt[ 7Á7:§6‚[q Î±éá±Tz2ôå” m•85™W9èÞB/’[EDwœ ø·A§³9ýÁ¼¸çÓÎFqÔö‡y»ž¡Ú?q~x­¯yš ^Ó¬|/¡{úràËm#9¶Ë¿NG?î´ï=v«–Âc~j9’.mOòúG\rnxj®¯ÃÈH.Š˹G{DŸ+×I´Ä¾±°_[GJ™÷¡Ç[Oêòeý†¸•ÂóÝÑ`60‹äZTgÎ… šÜ¥þ¨:ZªÖñOöäT`ã}ܳêºùþƒî׊©î—¶Dr‘ç\Û·PCü®ï^w6•æ_P㘫)O|éoçùŸöRèÆì0^[t,’k6níÀ5² 4þ}cxªpÀ–¦T¸îåäI?gÊsßý·µLÂ\F›ßˆäøñ³†¾U[x|x^ íZ¶nÅÒçÂu"ÉœÐo+ôtšÐ&Sã#9~ý;ž‚«ÅÍUmK¡9Ic´iW­©Öà&Æ#ï{Rßu'jå ýÝ™,Drs6¸;\?Ou×H.tI¡ ƒŒÞw·¦IíYfùçuJ¡[ôqF­Â‘œ·õ“Oã·d’ïijÿˊƾ³'×i˜´¼Ó:+ÞߨºÃ“wößW'Š[v7ä¡jpý.|¨þ´6™¾ZûµÍhfM73¿è¼Ï“Ê^±ƒ`køþƒ®À.»uI«(N·‹ë’@_R¢,kµI¦Ú¹“._ßfM?Ý®YzÆSXæujèö-¹b3FÅXUY´>1Ö¹Hvà’þä¿]Çt78u´\hôn¯+…îò‹yK†Dq:»Øf‰¤[\‘D^þ­¶ð²¡vågCz‘­ÎXŒ×émC?8›â<6ŠÃ¤ùÄóe‰t¨ó¢‡Óª'Ñ0d‘v"±]ÛVõ½èÞÂ=_æ… ¾Ïй5?43Š›<µìµþåDJqo³ªíÙDÒÙmŸ’Q¿'M Mó<éûƦM~MüÖ¡ÓMFqÕó^u|›HÏ=[f¼•H›ØŸieônχ+Þž´çR·ù»ð¾ºRè"=Ø=Ø"Š»cý>Ùxh-s^=ÿh~©æ¿>!Ï–ÑÔu½÷f!ßòûLBü„.}öæ‘¶Qܺ•÷§ïL¢ž»Ä^lJ o;ìî­÷’QÏàl3»þžÔ½Ý–Cû¿>ŠÐ\káxt]·öÖ&ŽqIt±pûQ÷– ÇlòšÉhžLÄŽÚñÂóÎ¥ Ït¼_vG¥»ªímœL¾æoÇSÍÄ&å66”Ô³Ú€º­<‰ßW|ƒ¡»ÊŽ{nâœê1G½dÚ¸È1$}Q¼°^lM]t džÂùþ÷ÔÛ^&ɱk¼õ¬K·GvM¶îd2ÍݤÖîø­!ýÑlåËš¢Æ^³-ö aºrBÿAW> aPÿýQÜÈÏ71I¦3QÔG/hþŒ{tö~ÄŸGúºO{í“B/w)_?š¤¡sì»­·¡Ë3'Ô³ò 6ËosVè7ü=sÑÜx0Š»Þ$Â$l} õx¤íQyt nÜGFW³,¨eçA‹&¾å!øÎB×"& кa žÞ‡§Ð’Ñžž\ãþÄ£«ak} ÷ ê“ª74Þ ä=èN‹ë9Åmù9yÛ›š©”×a¥8IGEŒŸœÁ|oïO“Þžä<öHú ºA˜ž×šžJŸ/åî@>é6méå“1íW{ÒP½¶®ó÷Gé¿\N0•ñ‰ŸšÌ‰ý3®:PÑwì4Oì‚™§à¼£LrçcÿÉVÐýÞ˜wcDQ*©«ÚüóM yø=¬½m‡ŒFè²xÐë-Jï& ~ÁÐ ôHwè€þ…tx0­¹–—Ž|:Ý=¦Êw’Þo1ê9z´Ç?|%ÐýüÖÉØÜDK=Úü>~rH Ý jú!ð‚uç @)m ;¹&ÄIüýÌ¥_û¤î‰â Ø£–f÷PP=Œ¦»¾ßŸˆ%–ôĵdúíWrú^Ç£Á°P¡¿ ‹ýêgæ†ûÐ>s"©–î73¿xÜ+ºÊ‘Þz$³LG;Ö¿H°Ù,ôt·>ôý¶9Š3m;)0æ²–fÚýš19šöÕa î+ɧ[Àó }7'Zšãèú¦î¹{ÏYy³Ížë“µälY§bMµh:¶ïÆ WVüÙ§ÉlØÜ¹Ç!ÏA§Û…vŠâB?Ê®öø ¥úÁõ$¦w£ˆÍî]Aåç:=žÕ˃֋ÇÍ›v‘¿õv–ITNžOÝGÚê¶ÃҨΜâÜŸë£hñ˜Œ¦½Ï¯ j…}–õiàA–º¬ðœí¬:ÇÅñ¿_åŒðÜõ¾o”°^»âÏ~|“#oD/ Ïtîì¸ü’(n‰¼obŽUEí˜8§,’ •Ÿ˜µòϹ¤=-./ª“×I¡ë2õ©yñ¼(®×šöJ£Mº‰B$UùHøz¼žù,¯m1þœç ;(J‰ª;-Šso»S¦4 ¾œáä23RØ÷° !!Õ0ò ãN×”ïåuJèÞ}ºÕoòGÿæªö‡sÓÈ÷iPDû‘tõc³”U}-h—gõúÒ99$½tgÌëÔ;«îË(.òRÊùG¿ÓÈA— "¨_\oƒQ=-È+VÖ~Þt9½èR±ÿÌo¡ÿ ã÷Û£8Ýnv·t²{7´›]=Lð¬õ÷•Ôù»(q÷wŠ--ê/Ìó\Ê$cÇë/ëÅÅM^QüxZ:ÕÕ?¹8§Cý?ìÝ tSeÚð"ƒ¦²‘Ò€A( Rœ°i@'¢@„€ °¤…JÀ‚ ¤Ô¡· Z)AÃÖ†­†µ+7mA‚€†®)Ž*ÅÈ1"è÷¿É>ègœsìÄs~ç(øÐÒ$Ïÿ½÷¾÷>©A܉œG›Cëo>„HaÇ ¨Û$Ä'r\×{áÞ)¡ÔÀ‰I=xâ´ï¥ØÄ[÷{Ä«æÌ‘ÏdsžQ·þµÎßɰn¸^¶½ÿwKK¨Oß­ã/âøyèŠu99[¨§}êÑäOs¨d¾°Ñ­3Q—~(zãëE|𸥄æuÏM+ïç£È™õªŽQüvšåêÜåÂGÿ̧QÑýjJè‡æã­[ëýÿ×˱°ÚCo?‘]ôdûü¡.˜ÓE¼E?íí¥JÉû׫ÆãSxj¤&'R¤p9?:‡°¨Oû ë—¨[³?iЇ‹ø‰CÞÊص”,‹Ùî«ýš'þX`Ãî<:ñî¤>‰éì8uÇõhq{ߤ¿yÜÏÃJiæØ>®çöSÏNÂŽ·1䉋9ò’lõèØà«ø’àyˆ´å.aûí²"þ£ÞŸ ÞüRš²æûjBÊâå= £’ˆ RŸŠ-×>¬“¢nUìª 2‹ø´&×҈…—|ÞêuÖkoY•DÁóYóBÇaÁ×u#ît}>®ˆ¬Õù—.¥Ô‰…š^Ôjñr놽I”Õâ1SæÁæ=£nxàÆœBþØ™ÉXò–Qd\ŒÞ´{/Û?˜H]±ß·ÿ.ëŽy÷ÔÝ_pÔ6¤¢ßÜYØ‘WFÎ]˜e—íeëßznßÃÍ£&g‘põÅÃ>é5ÊŽïVôÝQÈ÷ï;mö¦2êsýÖÚI<ü¡Q²´ôáû£Û§df‘þÅ·¾½‹ÍéF]°/ò¯\eô¤HùôüÖv*NŠzªþH-Ÿýê‚ϲ¨K¬naq,;N@]s| :Œ/䯾, æ.£ïuK^¶<Ÿ&vÝÜ蛉Zz~ï„Së~É¢™&œyg¾Oaù@…üqárLt9y-W›ç‡ÎSpŽo6ýóÊqëz±ÏêÛ|šò_ÇË´Ï”SÛ¢È%ÝM{h«UØð’@ SNTt8‹)i™1H¬³ N8úzÖÁç?˜hy{T9­:6¸ùè{(ðqX”@Ë?ØZ|üµ,î¢ZÚ„}þP7sË»_¿SààI’LÏ(§S ŸóÓ´Ýìúv"µ \p›KÏv}}ù¯Ã‚uÔq7ü²¼Þ¬3¥ç–Ó’§û¿8½+t‚ž½úÕ¶¸ãsiÄ…´ô`]DF2¶‹hk«á^8›wñp9I7Âì¢æ¼cJ$a5“üõ\Ò½õ?Ùy2Ô­ñw9“ãà?ùË ×/”Óš””÷¾-ØIßn9=zzZ"½÷âÀ¹ígÌ¥•Ô?oc¯êºsùÊô¯y~³°­X\A÷÷0Žë¸“–æŸíõê+‰ôÂTQ«ÈîFR¼UµÐq†gA]´J}C2…ç‹f  +¨LÓH×ͼ#t½„*ßž¡Vgͦ´/j—Ù/ø¾æP÷â¤Ë¾Uõyþèn]½¯WPl£I)± wÐßc¦~xàºÚ]kº:nó Ø',¨‹Òq%wÞ~^º´ÉÊUÓ+è̉û×ïLßÎöhI²„N5›E·Ïùv ®bÓ÷ϵ{x?¯xò›÷/« ×“NEþj#ûðXD –¬Sútl«™EB÷ª(fóºQWÝ»uá¼EŸðÁ׿‚NN«ÒÏ´QÙK{ö­hª¥†ç>ý¸ÝèY4eu«Ætvž,³FØ$ù„/ɾ‚ÆÅm¹¾ñúGì¾-¥% ;Ùg±÷)Ë?Ô ½´iŵE|‡w [ܸYA â´÷å_Ì£6]fL\Ó)66ž³!­Í,úîʾ##Xþ¡.°ýîá~Ækcw;ZW’ÇóÖÀVeÛ(ØßÙþËLjY}áy‘3ø÷Ó¢îüßÔÍø1ˆqª¤ŽƒÖŸm¼m+?'‰$ìnÃe†Ž÷ƒ¯ê²%Ó$ ÙÇgímÛ_I''d¤î™±…¾QøÆïí6š¢+ý¯}‘ºÞ|ýP×ò¦N‘4qª‡wÀ¤ÔJö¸úf†j3Ï»%Ò§ÍÓ[-’gÜ1¿Þ:kõÍÓ'÷ò“„»VUR¿„cùW[o¢Å¾‡»Ä$Ri¡°ñ=ýŽóðÔéãWøÇ÷ÛËë¾Hß´u׆½ù€•­Çµ”¸$ƒú=ERÃ^¿Y5Ê‹) vþÇáLK%-ŽÝ]ý—\¶>Чî txÁççöú¡¡~èJ ;_ÌÅ Û%öïžPDo¤ë—„NGÒÔ=œk†?*u=]/L`ç9Q'¤áÒ÷òyÿS7«æ7sÓ_ëgü6°×y8nÚ¯µ1.„UöäClý‚ºÔ“¯®/rïá…éÝ»º)¹Õ–ž5ÓÖ³yéz.p¢6ê/¾|©Û“ì<5꾜½ì\÷=ü¥r´7µù¡²Sœ}]è>L¶?3&oÞ-ox§£îiáÛ[°›o²¦ÿ±Ö:7­ê´¡¨ÛµÔw×c¯LOFç²3÷¬vGq îhû³ VŸÙŧn¸RýFº›ÆÖ¿žÓ‰Ö²óðè]`cOÚçá=¨“\¸Ü»×³»ø¢Â“L+Ü´¯“6÷fÊv}]C?/þ|ÎØÈtZøŽ°q’åßl|Ž.þíP¿ìüóqË{¶ßí¦WƈFô³YØ>êá4øpÏís®¥Q ÷æË«v°×ovh¿ÐÞ±dX^þ17-™Úó³·v®¦W–´Íywòš;£0·gr»Ülý‚º¦êjÑÐŽ;xëÓëÎl9í&¾Ë¨½=}+Y¾Žd÷•p$Ü­Ðì¶~AÝ3YSÊŽOÞίþÛ}©þŸÜt r`î»® Ჩ-÷Mš¡Ÿ^1ñcŽ$çgOÈîÊú'ê’4° Ùg㬸ŠN¦èüV¯eµ !jÒ˜xvÜÈݱ޵ .pxãÿˆlWo_E‹¾Ù´¶t™²ç™>OÑqå ŽrdÙõ¾'‚?Oꊎðóøósó’¢ŠÆ y¼ôœvÉ­ÏÃM¿üªþÔTê+lÓ‰cÇí¨»ú“¬Ý“ÛøÖ§Nâ_EÝ…ÛP§,ºõõÚdrèˆïQʵ‘;²ãö95Já,{ÓÌ-üûéžËÞ¬¢¸È–jf}@Gt§¯dåÆSÔ¡´aGV&“ËE…³Øë‡º#×ÚNÚ{}/ lx¨¢¼®y|¿ÂDÁ}áñÜžLâÅ}ñ‰g×ùP÷å íÒ'[ù’_ßé<œ«¢-É.^:jUÏtÔ¯/EÝçu4U%»Ï!øú¡îüÆE•ý½y;E7n1¿ŠÞØ_ÙxÜ\ |,{kÙuŠì¾éàû…Cé““7/Çoàñ½²Š®EÄÆ R̺uÜìGÉwô ê,Q’ûÊ×ñ~¸8ÿÇÍUtîWÿÝ›9šÔ?y[æÅD¶®N&ÇùUÏÆ•±ëD¨Û>A8pXË7ÉSï{=q0çÓ53§SÓ.1x“ÆP›Õ¦•ùÛ“i·QÖaü>=¨Ã·åÀb ÛgT…ãTC~ã_ÆQè>íã'{ý:-™Øýêüo=ò^}æQøùág…Ÿù¿{>¤ð޽'„Ÿ›ðg«êÝ»³lÿÈ9m¿gîÈ¿{ŽöÝ2wänœÓöŸæÙºAŒ°Tvð á©3¸@„ U‚là)‚U&p²U€¬à BW Fp€äu8϶®æ´ sGBÏÑ–†×FáµQDxmôß®~ë™áõпú‹ž½ÎÂÏE¨U×»wçÖþ‘sØ~Ï\‘÷¼ì»e®ÈÝ6‡íö™µ·Ï«õ€¡¨#8Àr„¤,à1SØÁ2¨ÌàÂT °¤W ˜ÀÉ‚VQ‡³jëjöšðÔŸÐ3²CÏyÖ1¡ã³ðzèÏ· ¯…þœk¡ðy¢ºY }ÂÀ^Sáï-üž¦Þ½;Ÿöœ·ö{æˆüÖ3³ï–"wÛ¬µÚ³iݵfÓÚÀ R„¤Làd©=XÁ¨Œà?Ȩ:°€ÄWp`ȶZ0ƒ D^eΦ­«Yk ‘г³Cë#¼}~sÎZx}ô¿_…Ï…×H÷ÚIø¬sì5þ^2üš¶Þ½;“öœµöŸfŠˆÑ´UÀ| C×ÞesEî¶YkµgÒzjͤµƒdK-˜Á"§ `/H¤0“…ªô`H²j0‚ü GèêÀn#€Uu8“¶®f­ OÓ€é¶u’°¾¯“Âë$."¼N ¯“þë${¿9Ø÷-ǯéêÝ»3iÿÈÙk0“5ièÁ  i«ÁðƒM\fp ] °¤hð0“5{èÁ  ù«Áðƒa  ¸AŒ`Pvð A¡3¸@„ÐP‚là)BD&p²@Q€¬à F ƶwßüµÚ3i½µfÒ:Àr„¥,à1‚SØÁ2©ÌàBU °¤Y ˜ÀÉWz°‚$`uΤ­«Yl·ÏZ ­“”áëmáuRݯ“Âk¤ðé÷¬‘Tì½ädß—¢Þ½;›Vh¬ Ѓ< A£Uƒà9¯,à1š° 8°ƒdhÊZ0ƒ D¡¡jÁ .¡¹*Á6ð‚ÍV&p²Æ«=XÁ4b5Á~£1ëÀn£I«€;ø@†¦­3¸@„®XÁ4t5Á~£ÁëÀn£Ù«€;ø@†æ¯3¸@„ P‚là)‚A&p²P€¬à BC Fp€äXÀ b)¾>p`È0Z0“…ô`H>j0‚ü GéÀn#˜TÀ| CPiÁ .!´”`xAŠÓ€ œ,Р+x@‚€Sƒà9Opƒá§ìà)ÂP&p‚äGXÀ"¥ `/Hœ0“…¨ô`Hªj0‚ü GÈêÀn#pUÀ| CkÁ .!Œ•`xAŠpÖ€ œ,¨ +x@‚àVƒà9‚\pƒ¡®ìàB^ fp¯ØÀ R,4`'[ (@ÖÛÖIÚˆ}ÎIzÛ&G­ÿ¿ö9)W­ßw×úïÐ?ÄþÅX£ôü·à¼Ýª[σk¸¡æZÚ2%œßezç¶£n=g6ø<ϾôÓ€ìëí§(¥Y^¿êSimjzvTœ£ª |%)¾Îy]«³Ó–ó¯ÖûñÙÔUôÍߎµïÃ'ðÂS¬ÄÛ’¨lÓ=vN =ï?øüÔ5n1hä`3¡ÓC%¯œ®¢•ÉIÇfNá/ÝÓ ¹c å¥VŽýüúÔÐ|–àó#P7ùÿØû¨(ÖoÝÅŒ3æ6cÆŒq¶3fÔ&£¢Š¢ˆ4 Ø`ÂŒ¹ET”«A…&79ˆÚ€(f̘ïS]Õœåì}Ïûìïîýíãã7ÖX®~ìîšUsÎzßêù¨y`}©óŒ¢01ñÑ=Ö™öbûgµÿ ?èVfÏyÛuÝ~z’°Á"ix)¥Ë[Ï‹ »ÈpólD¤ö‘ŠûÞ­çÐ,~~ 7Ηd½VùM/ùÔ¦£JÉKÔÍxÂS)ÃùgŠˆ6ÞuævRÙX–r秺㉋߿èvŒFä} ]J÷7•$Ïx{‰y¾îhZû­"rÓk=@÷üvêòõvEàl~þtSú´é’˜|‚žöh{ç˸RjÜ#+üá?ÆÿzS¿7 D¤Ècô¨]ü¼Š` ?:­úïû-³õ¥-n[ÎL™XJ/Ÿï üVs™ ”ú½X9a±Ó…ÛíÞJª[ÈÏ„Nʺ>þ>C£F_Ь˜TJWܼ¯¯{î_w¾¨Æ·Ÿ¶#Ö%~~"?¿ºáûîhqø<}©ÊØî1¥”Äv½ÿöð “t7rË›µ4òf¬Þ qv¤}ì×Úf­ùù-Щƌ¶–; T1µ”&\ðø°®ð*³·{â­ãç­ÙÑ¡†×›;òó[ ;–’÷¾ÁÊKdWÙñÃÖé¥ÄMÍ+ºÆ 1Þr6PÄÏw³§g%ÝNáç_í¯Ʊv>Güh¬Ê ½”Öܾ÷óÕ«†÷{¡ ¬ ®“ûÛ“;¦u?ºÇ›é ̸LqùK¼­º\g$CrïM”™ÑškGN¦=°#:èô¡k>nx½Êé«? >²3«”ÆUYT™1û•~_fAœÿ—-Ù³äuÞ;~îtËT–¯R³¹9i ²`æ{ƪ‹öó-‰¾×}¼=©ìºò¾x½á¯ÝF¡Ë®QÁ“¬wÛÞd8?AKÞÞ¬XÝϼŸ^ÏNMê@Ë;ÕŒ¼‹Ï5ûý¼ˆá‡CÎË‚œUƒûìhÆÂuÙECøë :ïÒõ^'ù9ø¥4ôþÅ÷_íC™…ªïæ$8·¨{ÐñÍÔ¨s½‡¼ tóVÌŒM ¸Nv-{kÎjÆ<øÊÌ6£î=’j×—m$þ¿¹ø@½ùý¼q³ÛA”qtÅnÄÕ{Ó—NOÃõ¼7Í”!Nm‹mþð½@7á¹såÆì`²Óû4^XJeÞ†=7Œ gÔsW»7›mËûcqßK]iƒ2Ÿ‚ô<ÿDö{œï•‹•7Mg:ÿžR™ä*¢>{º]·£7ýæL¸Î__ÐúX9“u“LToXJU—P³ø¡ñM‹D¤7jGƒ2âç“AWØMà{9%„Š[>èh1¶”°c›“"e¼é,G'±®g„v” ÷®àP?Ÿ :‘Ã}+Q(Å)[üÚ3¢”z|³¦É8]^Ì.¢ÅëïeL9lGç: ¸Sº˜ŸoÝ¡mg­- %›tqÄqäÇq^SEUŠH†ó 3¡8ûÏYÖAö´µéÄÏç„îlUxÎx #ã†Éß÷/%ó»ÔFw]s‰ÜÖ”VÉn~˜¹•„£?<9SŸ¿¾V 7ßió0âf©Ê'ꆬE¯i1ãÆÝùœaJŠÙíêYo#íÞ×Bžmä}l ;ðÙ±W÷ádÍÚ>i—RV›é·™ýÑuýÂës–®Û÷mSÏuââçNÇØô‹zú¬¿ l©N ÃùÃý¯>#Ñö¦‰g2wž‰ »Rœ£—ÿ6œîØLÜtP³”Tšu“Ç0¡Z>|ÕÚœ8ßöm깩\ü ³fmí #h {šü*¡eá_&Ï\ËXüÊ·¾‹9ì©U=¿t°i« wH¡s0êÝé\b9‡}©Ø¾ÆDU@â˜Å #ƒ|Þ›ñ¾cTñ‚5@ât2è\3²üv Šäçž•ù»ÅctâþûÐÎ&޹ItÉ*âÅÖ(þºƒÎy»ÌáþHÞ¶„:^r^<`f<3Q¯ø¬öQ3zºæ“x§|›ÚÇ‹ß!äñÄÀÖ}ßFRWë)Ï6•PZë«ö}Ëã¦YÍçÍh”ʰ`+=~˜Öö¼?諌@¢(aÿ—ÊÏé%T_SÅ“=·˜«®= B›‘Æ¡ÉNÖnUûpñƒ®ãÂÜ#nD‘($T¹GVBîqnm›÷ºÍ¾—žçgN-2šÍŒ¿¸•μó.ÊŠÓ‰ ãü†¢I÷Ö€ÖÍ£JÈ£µ¸EÚmfë°ûË-[ðqÛF|þäâkv¢g4å=R6?XBg*n=ší’À¨çêߘ_{eÏ£mêù¼\ü ›ëä>|þÕh 3ØxfǹÒòsBÿ©w¶›éÔr=?ŸÓ.L»™!àÎtíÙñ”£épkL^B?JFõÐb¾¾ðó@¨ò±•±žïßë£=9†Îv›ò|†[ ë±eA‡g sþkø±‘÷ët æ½`òÌV¼Ûá*!7ÿ,†v’ïèPBœŸ¬Œý Ã7QçW¹`Ïþ…Û©Q@ ÅFgÞ¿ ºÊ%'¢c“cÈPciãVÖ%dúóÛî·i2Æ1g%2Ýâæ,o'Õ˜Ë;œNÝúýw÷ÿC¬ëÇûÕ%ÔkÖzÝF$2ÏϤŽ{PfMÙÒK¸#qà}yÿ6讚ozex,íéòåé¸ù%´cÆ ÛŽ$2-m§FYgEwtOÒ>±÷±2æâÝó­³ÛšÇò>Ï%¼/v"SÃŽ¡ìfIÒ’ŠŠâ·;UþB/N'…ίÕà²÷Gb)âÌÍñÃJ(dOʼ£Ó’˜—âöjAGŸ+žï·Ü*R|oä>§ ºLk°Kª¶¾G eT´6|2‰Ù Ûòîì{æ¼Oõ Ÿ.: ÏÅ] ÝÒUí·/þK.&'|mYB#V¹&¿«Nbê-ØÛrQˆ9h_?:ÎQíÇÇÅÏõèÈ‹‰ŒNUÜø°åÔÏb Ñ'tŸt—ùÖ²]Ë%5æÔFÞïÐ$­jT.~ÐÙqŠ7‹#=•qe1mj=¢½¥Ïݺ~c£ˆü·“6ÖíÚ›¿þ »3‚ý‹âèvxóÆEeÅTnw6~Ò³»ÌåL»ÍíÞXPƒ¦Þ=ÍÞì¢d¿ç¯Ìm¹ï'‚βvÑ8eµeí2Š A;UN÷Õo{KâîÜHP#ÓHÚÇ×bèzªn㉛[ZL›-šræSÚ½‚,éÞÓÂý—vÿq¾H¡«.í¤h¾#žî<<Ú-4¨˜Îì«oóùó=&÷þÕÓVü\qç?â.ƒÎñÔþÚ…ñ45ßYcÉÙbz±åfãÌ¥÷™(ÖnXÊÎNV4k±Ê™tb »¯7äâÝÒˆ&«]{Ý"£ã%‚cŠiø˜i´4ä>Ã݇Xò~9μ?%çc©qýúÉ)Û¦:ߢíËÆà-‹i™ãà«C›&3«®|œ?|„%-˜SX^=È™¸¹à¼ÿ%t5Ê­¤oÑ×¹‚~Û7SàÌCwމ’ÎOW=_Ü™X÷×cœ¿§º‹óO;éßæï‹Š©âѥ̎QÉLpçÎO†%™S§ §¼îtæýuWqñƒnðâÏ'íïܦ+V»¼ÂæëÒQÞ8…Yi?\w°9qñu&÷²™¾~Ɯߦ:}¯5 M q÷3ƒ&“iƒâ±N+Rêò¼Å}[{Îäwjñ˜HN'…nÈÀ+/'ÐÈ• (¦EûÖ}ow5…q}-º7¥ž9}í°1V>Ù™Ÿ{ÉédÐm|»t¹µàiÔÇíh»bšt}Rÿ/)L÷IíÎô=i^wÎÿÓ)¡;óÆaɺ“w¨©Ê8¦ˆLª—-˜'g>ŸéÑþ¸Ÿ?—Ú™÷¿âŽ‹ÆQÜß:ÜѪC²‚ yu=L8zfˆ¿œQ¬«nsù™%íLýýäírßø¦Ú%ŽtÁÃÚ­ucÈ£>Z‹"ºÐ{H¶NýTfÙ~‰éûÆÖ4óâá‘õÅüýç¯+„.í¾¶aÊ;†Çí×WD¾~6Oe6o*ßXlMœ_˜÷‡çü‘EÐq¾[2º{Nº ÕÅ"ú¤œci Oeø¹Ò4+wóÇ=ÅÄùóþÏÐÝݯ•ëòò`oÍì9ùðÙÁiÌ¢ð‚ªãbk²Í­s¦‰˜÷oÔçâÝU‚“цù’8ÿ E4àãÅRýiŒÚÙ°[Ö ­Ä¤S¬Íù#Ë »`¼T!£‹ô­ÿ‚"J}þò뙪4æpwÖhÙ‚Nf„Ýê!æ}xÿ`è4‰ÜÖèŒòæ³FèET܉yÜcB:SòJÿÓùuæ4íô¬cûGˆéTk¨5‹ß1Ô¿•ì/£1Q67†·-"ƒ½=œšx¥3Š7>¦âf´{zy„ƒ­˜N»¦Õ~é<›‹t›¶šíJj›H*;¯…t‹ÜßWæ§3Û½^Üp½aJÞÏlê]ú$æý_¸8¡ÓòN>0‘:¨nä éÌœá÷C{d0/:xj³)¿ìÊû¥rqAÇÍÙO¤·+NV‰(¤ýᛚ”Xg0*ûš ¦´:@ÜÝþn¤²¥éÁûC·P•©ˆ]Þð)$_SÃÖL—NìT3$Ïžuó¬mð:‘#Æé¤Ð=»š©×rq"1C^ço.¤}nOÖ¬—Él,Ú™,5#ÕB„;}]˜±kÄqÎWYç#”HWOi§œWHS½­Ì3Èdøû/úPqoåDowâüºùøA—^zÕѺÛ/êáªfìµö _&£3»žŸ(Ùœ¿_w§ßM‡­ÈÍåâw¼JÈù &ÒãË¿vy4*¤wN™»>gÖÕéM¶½¿E}r§½qÓí¹óZ]|»WLÖ‚DbW±ºWÐ¥!»oÌËbTcM,èn¡MU¿ßîd9Ìvgpçñ\ü cÝÕ§âx¾Léÿµ S@Mö„ì~'ÍbŠ·»Ìµ ܬ½ÝôÍ÷5ïÀźË߇Ý&Ò¤õåáÍÎPJùþ)‹97Æq݉æt=~öØ•™î¼¯ÎÕ:¬ºÕƒÙ‰¹‰ü|ÙjtxH£9Ù̶ñ5§ÆCûжítçç¢Pé¤Ð%¢Š¶šHîJ“>,) ­°Ä¤9g²™cãd;jÌè3kŸBîT ùs~Âæ^* :Îÿ*‘òwŸØýjX Zçõ¬:› ‰o-Ü`F昖#Öº“ý⡽½S»rñƒî…SËD»Î‰4z¯ÙÉÉM èy/¦Ov“%ÉEçlF6¨zÜIf…J>’‹ß‰*aIäÍ'¸ŽVNõY•O¬ìK¡‚± 7¢ï3ºùë¤Æw2§ýWôïp:tò×§­h‘ÈÏKΧž?b­L~|Ë L”‰Y»ú'îÔáç¥ÓŠúrñƒ®íÃoŠF‰”qª“¨çù|qûõù!> fkÁ¤ÙOn™ÓÙí#2·…¸óùóùUü ó>ö½»wýDª1h_Ýng>=1ßw>+^ÁÛ³† Ä­ï¨u¹øAWO÷ÂÔé¿eÔ®Y[Íåùüu¦`Ô¾jgšznm9j/•·\¿Li>˜‹tß·M²”ŽÌ§¦-.ohÔ)‡9µí¸{ICkzàñtÂÁȽêó’‹^o¬ÑåÊ<¼×ïåSøèykÎΩËÓï‘Í󜽼?ü4•N ]íÈÅ{ó_ç‘ÃP­gsµﰥǔ-×ïå}gp;'«„Uf;\â8(ÏïÞe“žG ]¤£ór˜x¿ãZÁ×­ÈeŸŸð¢Ü²lÙ…f!·Ÿü”Ab9Žûç ?ÍÀoBZ.ý|npª°&—9ÁÚŽ 4'®ò$Ÿ;sò¤ÖøøA·qÛq$·Ì˜K®\ÞáÑ9Yð€ Ïõ7§-·Æ×ŽxçC7Š‹t íz,¡nqþ¥¹ÔÄgÖõãÓó˜Ðª/7² Á€ùÚ™iž4Y{tÕ¤Vâ{´Qµ©_Mé¶O«Ö&?ö’þÈ ±ÝçrqƒîÛë—²ñ82#ÁÔwf êÑ=¼l¨ÿ“:ÿÎ'ÛƒÂów19‡æ‘z_\ýoÎ7c™Œ\®;(°OŸ§*¾gPwÖm_Ô£c9Ó˜µñsÔ`Áñè£O(¼ôóBnBFŽk‘Êÿ¼^Ð ß|¹ÞíFI´Ý®kKåPmíßö“ø ½díU³MÈÖˆUzòëw‹¹øB·òù› ]ߣ?dÓ~; Þ\fºgóÊ9¡¹ì·À”8_0Oâöã¹ã-‚ÉèÁ)ìQ@‡«o³I+zÔµþÆOy›q×LiÎÇ‘ÞU'=éÔŠé=æqŸS j™J†z<Ía뻘lº6/ªÑ„98~|>h´ñÜ‚ÙûhÍ·Sþ#ïsñ’B×h„"j¸4‘Ž]š3IœM\Z$¹ŽyÂ÷ÙfTl~p—dÖ>Þÿ˜¿>¡kÑãªóVô¥^G}ô³éÙO—sº½žP{Ö–ùê•~™ÙóéûÈpK~³¼/|œ¡Ó-òkdm”HsÔ¶;Ñ2›V_`Ðâ \›ߦ£?·ïóCãl•óÇqamŽs³è¦Ã‡ í¿TRcÆwè* zíºB$áÉû¿êsñƒîmÕ”ñÍ´IeG*‹–]êñny%©Ï{îyOþ|átBèÖüظ³JF+æ˜÷4_•EÆìi™QIÏÚKbÇ[PqóF6MúxÖªøAg{fÊDe¼Œ8¶,*^–ñeZL%©ëÇ÷ÆýÇy{P‡s_4Í/bè¾±6$eÄù©eR§£;n÷«$Ϟ솜9Í4uërÇÃïû¸| …îñ>÷½:ëd¼ït&½pÐmòô`e^‰cèýÍ=øuQ®îÈ cïvê C_ú;ôÊ쥙4¹CºÄrg%%üŒ³–;šS/Öþñš½zØóíòÁ\SB—r«YÔW†Ö³ËÇm2)½›ë0…y%™ì8%(ü.pþAÜqÑ8‡ûHvYþCã[Ý^ý&#ƒÛ®ŠŠZTI– Úi^Û-hðÕÜEz·<¨AÄ´#ožqõCݶ©Îéç†1dyèzIÍÿòøííñ•¤²ë;aIƒmÇ Œ<)cæî– |ü ³V-@Ý¡[¯§m=?+ƒÞœ¢åÛ»Rýœ ¿¶:¦¼›~…‹ƒ:£u}#«ŒïP»_&¶o”A–=B>[6«¤ØkÏâäóS’tR='Q\Q盼Ÿµ-ÑÞÇû­rq—A»òéøV ä;‚½²ÓéÕ«=>² ~]ÑŠŠ/¬j+½Šºs$Ês‰÷ý”Ðíé²~ñÅÛ¤ºÝéN3¯Žt:X¡Þ_¡”{ûõ0ð¤£é.úŒó.~ç«„½—Ú8ð6±®ônåiÔgÖ¶mõŽWPíµe^á18??ÊV4EýgÛ©üõ]#¯×oêÅߢê[F«ü"Ò¸¿O\Áï#™Ñbé¢'sr=¨•E€wàÇé\ü +òJ×lÆ-RÝfx¥‘G :f¸©‚ÿ©ÝÇ”ä&º.ô‹"èØ¿¥]VvÚu¯:ŽÆÏ ŸÐ9†Ý~ùðø R?r;$Í#ñ$Ç“‚>Ò¤©\ü {òñÓîLû82V5þ©4£*~ïO•¿– -©±Ì®Ò“ŠJ|\,µˆ‹tÎk<okGf¯öžRæ§Ò®Øa³6wª IlÛ2Ï„ÖWtWÎi¾T—¯×·k\¨®ë×óVóã±äk3©Ø4&•šºåE4ª w5Ës’MèHë]cƒ¦ì£‚AAõ£¸û'tÓ^Çl÷ëKË Ø;ÔTZŽhÿPNªrk=VšR6³æóË…ûøuœÑ\ü ëe0r|T µQ‘¥Ò¬"eq«òr:ïn5íØ=Sâî“öQœÍ¶¬EvC¸øA—нšlJ=bÓ·f•ÓUSÉÃo›RC=vgÒ“fq Ž\ü S…§0šž¦7=¼0•n†Õœùq«œtóM<Œ¦˜ÒžßOï{…xÒúî¬cÜï)ªøAwt[擳›¢érˆ Ópt*=ºîxXNnÙŠdBí¢/xgâI²c‡…6åÖÓ ólÐk•¬a4 gŸH¥‚ø»eþ¾åÍ>fÖÑ„”s·Û4¬ð Ê.*ƒ`n=:öîïÓÙ(z[iSYN±~Î'ï+§—÷ F‰êòüé÷½º$4ãÖc.V G©Œ½¢¨¯¹ {Øc9%í®gº£œâeUa–æ"Útø‘mÏ8þ~½?èô¯N¿öìa$>)j–('­ç1! ¬Ê©ÿf«u;EôîãÜUžzž¼O*wLaýósÎ^wФg£›GNô—Óâ£æ-LWü¯ïÇ>µ×y‚'mWÊqë"è:©ü‹¤nQI<åôÅàUxàŒròi{+:ÆÈ´®>p¯6?è&_8Ù36-‚ä9ÝV}µSå¼öŒ(§1ªל–·›ñ°‰Àƒ¸×âÖC¡›úÑþëâöOä´¯jc·rRï#r÷‰|>›ÌÅ:U[Ô.‚ø5™Òx°œ~v:˜6*'õzNvY»Ã>Z¦2.ÁÅ:UšŽ §9l›¬%§¡[ Í=^)©Ùíc‹oZOöÊ{«þþ<{«Z§Ò¢T=HNÎóÏ.þB­ûoh•«T?GE»cŸÄ. áó'w\Ð9¯}—ß§e8ïO›BÍKéúíX%=ª6›"P¬§ô¸ö+O8ïSŸŸ\ü 3¬´™P?6ŒJëu•”Bw|î¹Îº¨¤±‡ïŠ~5[O¿,¾<1¹äI4F=N\Ô‚[…nOJMq¶i¿ŸBG¿±a<•ÔäÔ¡ñÙÅVT2åÉЕäI·w^Ÿ¾Ùs0·ž j»©}-KÕ0¸0…>Ϻ¡ë¼EI­w ž·Í’v íÕó£'Õ“ÜÚÕ¡3©TíJ•o‚nO¡BÇÊË«”uþ¸’¢OÍ·ßõä÷Ùfqñƒ®h½ÿ•÷ÛCi¼ŽÉÕ×ëR¨,EV£1SIƨ2½ÍIlqeÎÀjO~}†ÛWPB×£ì|Ôƒa¡Äù ¦PO¡¢«Þ%©ŸOY}¡É«×è#SfÌØ9Ÿ»þ.U ¯·Šë¼YBªíG½ ØtWG³‡’NÌø<õd#3Ú?¤ÿØžû(fÝ”U#Œpë¡Ðù>›b¢BÆVSuRè{ÿ€Iš+ÉñmÀˆ^OMy?Ã}äd¹|Ž9·ß"„nTçCÏFIoÒƒ …wJ¡àÓ×Ô<¦!&›œ¯1¥W¥6Ïi%¡ªÁ­×‹ «ÜÏ>€s“âËDw›¥ÐÚ^ ï7¤=®ûœ» Û$!î¹XîýÄÐÙÛ>÷ýpà±WÅ[ê*¯ãÿ˜ÆÙ°;§f´7Á«×øÓ:Q•ðð(w\¤Ðì;5¶~³T±2¸¼÷—dJ¾¡·?Óù1Y5×¼±o¸ fi{HòÇþŽ ºnMGçï ¦Ÿw:'j’©ÛŠ5CÂV>&õº4çc'¡(vzöB.~нî_{õy%¦Žµmò"™N¬¬Q=â1©÷wTÛ'í$tf­é¼áÜ>†úÖK{:E[ѱ±Ë¿ÈŸ&“Ä\ö©Åã:ÿØë¯:ÏÇ}k[´„‹tËU שyÞ—.n•ÉôÔíù†òêGÔ¦3»ÒhBã ïo-ÞGײ¶-ãâ]RÙÜy\§†Ú=mî?âû0>.ûèâjg‡åRnßQä§® t=8¦S…ߣ:Ÿàä}%éÑyžü:·¯-ÆëŸN2[þ.€,/oÝãÏUaØ)&Güˆš¹WȘPŤs²m«÷ñùŒÓI¡©º  7sVçà8”Íüvyí#Êó›’Ú}¬ qþòâö¹ï#ƒÎˆ~Ze¾F;6 §ãžœpa™tâ#êzÇáò¡ ÕßÛºßê÷¾㎟:ó—“F¤_¥ß™ûÛ­{LÏÞî×ñq>°ft`xi¥Ç6 õôËé~/‹{?ËUÂââ~×c_¥ŠzKoâüø±i‘Oñ›‡ü¾99÷c+Ê>þú6ââÝy땽ª\¡IÃ?}H¦°ô«m—Ý}H¿| ¯†%XÒ¯wk£F–ï#I±¨¼oæ:.^ЙÖJ|ÛÎñ§ÝCœ«Ý¾%SUц £?¤ —Ìí7XYSŸ¶'~KÈ>ioN° §Ag7qôYk.“í€6åë¦Ð¼…E Mòë×ë‰{¾É›Æœ1>àêÀ?ÿ]ûÉ5# íü¨é’ÛG·B> ¾úÒkØCbŸæ\Ŭ§œÉ/» mâM>ìíÁ}~ÿºØ)Áã6x\"ο7…"çŒwâ{½d7ë°žZþ9ädo/Rm³çòûï—Õû¾RbÝÆöJ¡¶£V|ŽM+£­ ϰ¢R¶Íì)á}¹ã©„nQ–AÙ»þ)¶Ñ<‡zÃShN¯q»Ï•ñÏKXRÑ—rA¯´}ô±cë·ãC¹ï§á_%\õ°áãÑçÉôK÷»f”Bú²ê÷Omˈ[?¶ ¿p“‹%ü~1÷|ˆºÓ±Ëµ¬9KîÇF» ?·¾£Û§YeÄõ¯æ¤ÚžéäEš™7·åžBwþb…^͆Ӥº=1N¡'²Æ§ö(£a‚Èwæ›SAèãv/—y¿ÿÈź[ýÆ‹V;EhVGÍß”Bö»Ýwþô€¦)r®ŸÜfNøRÝz5ð¢ù£õ—Ê9ºY3Ï- /8Aeƒ$®7Sh1ûd<àŸß7§þÚYc2â×›¹øAw@¿ýêŸǨ5{;ru«BÑ&äÒš:pu\'_ JH9Ý߆¼(m³åÇËåüógб»›A«RûiMÖ$]J¡…ž·xæð€Ò‡Þð¼7Ù’ê±6À¼‰ß/àâÇ~¿þþcŸ÷¡Y§ÆíÛ•BAÊz!JýÁÞþË-ù翽ùý'îù+UÂ6÷Ÿ´ÔýpÿýE ©/ïò€¾2}-gůëx“3k­Å?]ð$×Ð¥ÁÞ4êʧ˜†ÊÚütÖlW¥”î9æÄL+ÒzVÞðjž7}uî_Â_Ð}Š+õþi±Ó.ÍŒFTXÄRõs¾4¼,N7w¿7-ÚqÁ1«#ÿüt>ìÏizî%Õc}Íä”0ïC±òP)©¶÷Z‘å¾Ú}C'{ÓÛ#™Ë?tã>§ºA*Ãi1Ý,få$ÈŸṲ̀Tý<+Ùý>ò\ñÜ‹¦³öÚC¹ã"…NõØmo'²>òz7‘Sû[6ƒ¦Ž)%½½ƒÆ8I-(¸²öR®Ä‹~ pùr¨ w~Ê ãžsÙFMö,»»g‘œÆ\òïz»Y)PýÐÜ蚀ºzQ_§½gz-çãê±`-J[‘ºÙRNŽ?5mZQR·>7áD åebWOUrç™ÆÕ*aõS³µ³i÷Û-еÎrj½®¿·O| ©×ë_²rNáEíT ŠüóŸÐqýà)Üz™œG6[¼íX ÕÿøýWæ~3JM?c¨ì­^áâñÄÅmÛ®7b Ž›w?tSNv¯FmaSB;ôGýšš»ÊÒfÔ~þ9Bþúƒní¥Ö'Ã.md´ÃiÜ—Ëé¢Mû˵ÓJÈìÝÙ¤¹,hnßœt“ûip»=Çñ×tÇî8¯¯ëÀeN¨ü^!'söAN%t]xg[KzÛCúÝÞr¿úwV\ü ãžãÞÍ|‹»4pâO9”:Loú¼˜ú½z0gF­©–ñgíç×O¸ï'ƒÎ£ ›pܘûãÙožJ[úܽSLš+ Òߎ^Og^ŽÎ]²s?åç–53Ãé”ÐqþÜ ÷ºTÒ•פlo1í¹Þ¬å½ðõ4îÚÑzRïýüs |ü®U ã?ŸcáíÅ\6©íðE©ÔàÍÎ_‘‹‹Õ>½4Hõ`Íþ?ò’ºˆþk½7d¸:•J{š|ª¬êYLO³æëžÛhMç-:^*næMŸ>\íÝ!™t+†ìYjèÃL™–é3È;•ân~H3¨)âë¡%õsœÞ¥ƒžõŒ—¢cç]kÕƒˆG×µì†h*Šk¶5ãn‘:ÎT­1µ¿}ëÇ> Éÿ> :Þg™¹vs÷Ég‰©ôöi`«¸ÓEü:‚9EVŸoûÌ‹L~Œ~“¹…ÿ}t¯oõ®}<÷$ÿ|p*EŠ|ëKìŠ(¤?ûäœÍ`¢÷|ËöþÃ7[ÝÕØë¯nõöeRÚl|7P#"Z·|ÚlnÉ©7§8ÆŒn?]~¾ þ~z]tþͤwüïàëvzö/‘ò4£²»îžF‡-š/™Ð»ˆ†Ä:Ó£½9¥XUæbîM÷/ž´¸Ïÿ>, Jø³í»Â¯1g™©3 B¶MIãýß ù}ºI6½GuŠõgšÏö™v9Z¹:?mi–>:p&Ä”ÊÙÛãKèC®\´Ý¾‘û†º_—û½z…IK´©ß"=æ[œµ + Þõ{/2%®/ò¢”ÁZ“ã¨~Žó sMÓvW§·étìЦ4a瑦µcLIõ¸û\/>?ñþìÐ?Óg0Óâ*ã¾hëûÞí2hÄ¢ó?ÇUå“˰.#{\1¥Î7£M]¼èðÕ]#Wó¿Ï„®4U©³Óá*3=mÑuç äk=&u4“OÝ+½÷_¶7£æQWw¦ ôæ×øß¯@w?¸Óôñ9W™žm–ööZ›Aq‡Oz*ŸVˆßŽÝrΜÚ.Jf¶Øÿ‡ÏºÆõ*aèá…k¾¹Æàd?sÜ-ƒ TÆä“j[©‹%©߈گøA— ñ½:ÿÄ5†Ëÿ4{@=ÁÃ)ù¤~ž}L¾NßÓûIæ4û`Óüï ›¤Úx»Æøv›ö-3ƒ\WÚ>éß:ŸÜ]«#šhMn<~6ÚO[LõößÌ×?èxì<çÎOtìnN×YLÙ%“/VgÒ²õºáM—çñû5æÄî.ŽÛ¿ØÕÛuS¹8(¡+?yØß_ [møiO&¥kïêé7$Þžû|üœ]ˆh´·ðî>ZÂnÇuàã„>‹}<95á~7’I¹›¼ŒmçÕíƒîðñÛ1 }-zŸö)št÷+Æyœhv™^é%Ⱥ›IÏŒ…=BËs Éådf7œŸs³?¶5ó"«×ÑÍ3øßùA7¿k«y è:î2}’I¦ïƒy&ä’/ûøsS¢”ÙûGyzш¬Ç{§5äêƒ:vwm¹åuf[Kö—–YTÔÍiqÇÓ¹T&e`eJwó[t®ü‘çÅÐû5Ãâî™ëŒj¹m`ï}öÓÖ\Rÿ¾Jõ“¶äÏßA§k=øcâuf»ÿ·¬ás³èX¿óäÒÿ«g/7£#7.{´r{.}ú”¿þ ;Þ¦“xÖóëLÚ>醲hqþ”¤‚þ¹äßÕdq›kfÔýƆ9³ÇKè†þð„tÜ˜ª²èÖ‚vmVýÎá÷ÙÍêâðÏ߃jãz(6ÉÄØ/|uÉ98‹Vv™öx@qÿ’Í••ta»„v:f»Viòñƒ®}ÄÚU3‚îy¬,ºl–ÐäBX©–ó>›R¶Ç‘µùoö©Ïs.~Áêß1U“Z¿É¢.wݶ=Co^,7ß&4åû–}4ºÍØI K,™¿³”þÎRkü¥ôw–ÒYJì?Öü9Á7öïÖb j€•øÐDÒG ªIÌø9ŸÐô€-øÇ<%á¿Ìåþë_òïû—ü›Ë­ö/ù¯òyS{ßþÿ2OÉ(€& Ž8‚PP (@†ÀÈùb¤lAPm' 2P tQ¬¬-.} ± è ‰€/PM5!p¡ Pä óOØ‚ Ú(€@d ö_¼oÕo¬ÿ­-øëûòÀüíþ_Ù±}ÑEOô·úÛýg÷Cl~±åcÎVk$@j.’“5‚b …D¥Ä Ô$.ð  ‰$&Ž (6?_òŸ3¸ÿú”üû>%ÿÞ nÁ‘›Úë6(¶ŸH€ Ô]k Å@ …EˆA,¨:(4"à @EGA(¨!Càä|AÒ¶ (6 ”¨º(XÖ@ ŠŠ—>ƒXPtPÌDÀ(€& ›8‚PP (t†ÀÈÿ Ÿ[µësëø×çö?ìsËö=ì?l]c{Çž\®âÓäÿ£}Û©{!œºû ¿kDÿoï‰þöBÿñ^ˆÍ Ž|,ÙïÍþ?Càä|2Ò¶ (6’“¨ºHVÖ@ Š—>ƒêFÿk¾ö_ÿ‘ßäß›­­öù¯òhû§—m(¨>ðrÀ=` €h£° Z ‹Bc ¤ h¡èè1ˆ5@EH|h¢ #Õ@€e|€œ/VzÀ%ÐFñ2 µ@ÅÌHA1ÐBaÓb j€ øÅ¿ácËú³‰ÿú×þ‡ýkÿÙÿ°§Ž¸ç_ÿ‘¿ýÐß~H¦ñwè¿K_Ä^ëb>fì÷ÒÁŸ‰€/P°ŸIJA(¨$-Càä|Ó¶ (6š€XPHp@d=Hþ·$¡ Ô ÿÃä¿Ê§íŸ^¶± èðy€/PM!p¡ Pp óÅGØ‚ Ú(F@d è¢8Y)(Z(Tú@ bA ÐAá_ š8±…À„‚j @Q3>@Î8=` €h£à Z ‹h ¤ øßð±­á}l €ä¯íØÇöŸýÛcHþöKû%¿ý’Lão¿ôߥ_ÒãÏ7ÿ¹uñgÖ@ ŠÙ÷B’Òb j€’–øÐDG ª Íø¨:Hp†ÀÈA-ÐE³RP ´üôÄ‚ ƒd(¾@4‘…À„‚j @¢4>@Î'M=` €h#‰ Z ‹¤j ¤ h!Áê1ˆ5@ W|h"ù #Õ@€dl|€œOÌzÀ%ÐF¢6 µ@‰ÛHA1ÐB×b j€’ºøŸàõþ =Ûþék+µ@W€Ï¤ h¡Àè1ˆ5@G|h¢ø#Õ@€bd|€œ/LzÀ%ÐF¡2 µ@…ËHA1ÐBÓb j€ŠšøÐDG ªÏø9_üô€-ÊÃ×¶–÷µ5>}mÿþ¶ÿì—„Àço¿ô·_ÒøÛ/É4þöKÿ]ú%}þ|’óŸKØ‚ dÿ.$) 2P t‘´¬-$0} ± è ¡‰€/óÉMˆ€/PðÉNØ‚ ÚH~@d è"Z)(ZHŒú@ bA ÐA¢_ šHšBàBA5 ‰ 窰@ ´‘` €È@-ÐEµRP ´|õÄ‚ ƒd,¾@4‘˜…À„‚j @¢6>@Î'm=` €h#‰ Z ‹¤n ¤ h"Á -J „o$@j. €5‚b …b Ä Ôð  ‰B!Ž T ‡!ðòñ¸•óEØ‚ Ú(0@d è¢àX)(Z(>ú@ bA ÐA1_ š(LBàBA5 P ç‹–°@ ´QÄ €È@-ÐEQ³RP ´PàôÄ‚ ƒ‚'¾@4Qü„À„‚êÃß–-Œ¬¿­øþõ·ýûÛþ³_bk¼ïß~éo¿¤ñ·_’iüí—þ»ôK†üùÂSö}…À„‚jöµHR†ÀÈù„¥lAPm$0 2P t‘Ь(€&’›°RP 4‘ì„À„‚j @ò3>@Î'B=` €h#1 Z ‹Di ¤ h!iê1ˆ5@IT|h"¡ #Õ@€k|€œO¶zÀ%ÐFò5 µ@ÉØHA1ÐBbÖb j€µøÐDÒG ªIÜø9ŸÐõ€-J …¯A(¨$|Càä|ò×¶ (6Ѝº(Ö@ Š …>ƒXPtP8DÀ(ø"¢lh ð=€#Õ@€c|€œ/6zÀ%ÐFñ1 µ@ÅÈHA1ÐBaÒb j€ •øÐDÑG ªEÌø9_Ðô€-J g$@j. ž5‚b …â§Ä ÔC𠠉¨¬-J} ± è pŠ€/PMQ!p¡ PT óVØ‚ Ú(¸@d è¢[)(Z(Æú@ bA ÐAq_ š(ÔBàBA5 p 狸°@ ´QÔ €È@-ÐE‘·RP ´PðõÄ‚ ƒ@|h¢`ÝFA(q=ûHƒÛ«üãÙnÙ¿ñÿÿ¹—§ø—ÿ_ü/ÿ­þ'Ÿø? ®r󟂵Aª|Uq^«ì:‚š7*¬sê| N²ÞþÐȤί mÇvR=ë| ÔóoÔzö}Äx»dÝÖÇ‚˜zÊâˆg#³i\›ÙÖfçÚœ›婞ËÍ€.ª¢k³‹7‚vúXðÒlúý²,²ó€ZÊÚUµ_GÛ¿M]ãoàÉûwó~ÑÐÕþjüº %ˆ ¸Ú÷¥í¶lrÚ“`•ª™C›Æ%Ž[pq 5ºb]µVäI=Û Ì|jÇÍ QB7±ëûì8ì×Ùôq|î¶œ— Z¦Äa\7W™÷¹ãæFܨ¾³rŒhü6ˆ šüÊ£~t6.Ê,lž­ vÚášGÆÔ÷Œ°Í~mOb]}¾ôäçF@Çù[3’½o¶,Í&Õ˜¡0©ìZ ÖYLö QW<ˆ÷3åæF@§ÇÞ1˜)cm¾eÓ—ÔÝŸ*hmqmq>4³%ëdÁû{C·Cºpç¶>ÁŒï•ógÒ[*(zlõU{­hí»oþWÍö»×ÏÏôi>;ÙŸ{ÝB'vbQ0³E50GAö"÷lÁ"©}ËY9Íûž?§EýÁûøøAgÛTFWæ3˙ܯ5Cäöäüô΃ä=^sÂêS:?{ì>’Ø.]ÅÏ „îè©1a²•ÁŒjÌÿdex¾l2¸¡‚"NO_Ò¼ŸI»r[í»ÃûÖB7ûÉ­ãñæÁL‘÷Ø”¸ ê³{üÏ·²i©Ñ AH3â|X<(¡êEJÿ¯¼oíÍ*ázû í‚™Ûi¬qœ‚‚÷O´Ë&ÕxŸ ¦¸Ïz[“0wš5=ùò²$n>ž:ok;Þí f,oßøXj£ ‹å{Ï”Míû°´ø.¢"«¬oÇÍÜx?#~ntYÝŽ®Ö9ÌhÝO›ì¬ iikš=Ï¢‰O,cë]\Gþ?üK¯UºRÙÀîÁ¿·òñƒÎ}½ö”ÞWƒ™¾…K5 o ~eÑcf§]EÐ `ï?J×$ŸÃÜ{òs¯Ø÷K‰ºdzNA¿}>vYôóÖ”­…׌I‹~Ov|íFÞ ª¿ü<ËÏ»Âë¯|929"˜qŒ“¼½¬ ð³Zëe‘åÅìÛÆ4Geðâþ§O;tÉì€ñ`橱wPÚÄ[×8iÖ¯Lu°'3Ñâæ«»SÏîÉþ §SB·;׫áÒÌ`&al×Á#pÝ|[¶îš_R&?Gw-é]œ°bàdw23ý`·)€û^!UB•]¹‚œ¶•\hí‘IMï¹r]\‘ÓóÌN·?ç$áõD¿6—3gœ{\žôAA’2«š%33©å¡£+Ÿi‹È}óðõ ]é^ãæÖÿâ¯7è8_í`æk|‹ßÏçÁ¨99[fÖÍ1ésÞÕõëTÖ‘3ÛGà{q¾â9Ô<97çVR}h˜´0lƒˆ:2ƒ—¸ry¯?1¥ÊwH5ë>h·k©ì‰ÆŠhüëã|SÇÍ_õçîâçÊáõoë÷kÔÇ;CsKëiÂ~ÄrßÒ©4Ãëä¤ '×ñsJÅÄûÔrq‚Nxeîñ„+ÁLÍ—ý%G—#_p8x²A55âH®ÍZÚÌŽ‰~¼G='‰‹t?%ëš‚™> n7˜´>‡ ûì*O¯ËsõYÛ§1ÎÎå ­róòƒ™Î˺ž“ìÉ¡&ëwwÈ>œNüX,™nDÛ^¤Yíì±›"„·¦{×ãtè–'µÛõs®£]£ ŽåÐ÷§–®N'¯¨Úö•«h·ª 9‘jìÙN'„noáÝS^½ƒ™ÑsOýê˜Cƒ m8^'®…%tüøÈÞÍ=úì۔ݼ¯;ï+ ݯó¯sº¾ b¸ãšC.;êd}N£Õ w_+B ëüyWîú‚nEÑ¥3Gƒƒ˜ª¦{Ï›äЇÙMJóî¥Q½NæéïVQ¯î=’j×說;' %[~®#tÓ²- žmb²®g¬x•Cïç¾–nòI#vúîM#Z³™upúc¾¦ º3š]/¬ÄÜ ¶ùr½\sªk Ã4ª­`i¯!§ Ÿ –»î"Ý¡/Ž•™pùJ "bÕ§¸ÒëŒ [®µs©ÿ(÷óMº¥‘±jpó:RÙ?uÚEÃTƒ×8FX•°JÑûÁ×=×ÎO(—.–i8Û”¥Ò(IëWµÃEô{RKí¾Î»ˆóÏäó#tœïÅuæŒH¯Hwz.…ø§/{6µîºQÙŠut"ÞŸ‘‹t—SYcÚ@&#ʺ^é èžO:pu*­VÐuüœà]gÿ9Ë:ˆ‹»ºS†î^Èܽͤͥ…¡þ®÷º¦Ò/ã¬C¦¬¥ðÛA=t[ïTÏãââª\~ `XwÏ<—\²öër¤\NÑÒ/ÉZƼßÀvâ}ï¹øA×fLγ˘勞öt>–Kúõ ó‚ä¤Sw• aF·þáç.ƒ®rLÈ‹àŽÌ>ÿŽsû_Ë%Í] ŒÝ%'å¹6WWR=ëãÃ~γ'—Ú¯o7òyºÂ{õ×åܸÆ|š­ÝxH\.nú4uâ|9…ù—9:¼‚¿©ù+Æžf›#$|ž ¯®mk53Æ5æH^ч1é¹ôÜþx¦eO9ï?·‚\BÏCÝ·Ñ ö0Çñs¡[|)>®öÁUfâýëS}äRû½åmǾO¡W Ù¶+øóÙ<Çm¬¹3ˆŸkÝ ÝVúo½ÊZÿ<ÿ:—:;Îù’u7…Tvy…+)ϦǾĤ:m˹º(‚Ne#Ýé*Ó©Å͸˿sIõuަðþ”«¨;þð¸Ie¿~,ætbèúÆôxR~…á|“ò¨õZÑ÷ÍëR耫C7Z¶šÖ0¢q6FÛx¾?nµ©Åè÷;¯0k¼„ŸZ÷ΣM.Ýß” L¡Í¡]æQ§=¶S‚íy?,>B—?ððæ½3¯0MœÚM·™GÝ7Îñ{Ÿ\w¾ì+ïc:·¯-fÛá|ü “õZXÒà óh™Rñ´B—4ÏöEƒÔËÌBÛ#Nñy”x2ÜjPÏûjoRûþ1×8²Jè2œÄ{™QÙÐÜϣꎬáȽºóLíËûýqñƒN5~ùøeæÙ¡_§6æä‘á0‹A­ÂîÑ­Á·D¶™kÔõKíCÆÅ:•ЮËÌKùkg<È£­×õyb÷å‘Útyb€ÿB!÷ýDÐÍ=¯h,ºÌ4ßÓÕôöÓ<úñt•ÿÝ{¼ß›H}É´$£W ?èš8?=ó2ã0ê‹OM=çtö׫»´.2¥ùó=²ó>ôäý::ÔÈcêéY|þ„îžèW³Óƒ/3;U2R“u¶5¸~—îZtÛÀXD™]ÖÞ^Cý˧õH<È×?è:4`ï¨.3ÂÑžœ©ŸOMÝÚÌ<ºá.ÕüðkøÓc-±Óô ¦¬ùc޹ºœ§z1M¿ø1n”5Ïh–Oïµz5]¡{—¤Þ]c|/QƒïÞŽfL»ó>–Ô+âã…:–¶ ?·Ì¹ÉŽÁmŸO'§ï·yö=‰z?¾’߯Òf\{öÐÐÀP=’‹tónš¾;›äǸù¹¸íï0†ï7¡[5úüù‡ü˜Êƒ}\tó©Y7æjÂŽ$º¼½Ó^ÆûÎ/ ­³2^}àï ë¨_°Õáü\ò©¦òîìž “øù½Ëùûž¥ô¹ùö»Ñ3¹~Z ÝÌ!¦Å&óý˜ééñ.CfäÓV ƒM¶½“x_®´°ùÚüW'N'ƒ®¾ÖŽOƒFú1ÒkõÜýå“nuÖã‰uõ¯á2=·eÞkþ¨cJè¸9Ð~ÌOwÖð1Ÿöˆœß[ÞM$ßQ:|­¢ùªÔµôá`5:Z.ÑUB½[Qó=+/1F¢Ã^Ç-ó)$:AoþáDµß}X)7î¼ÌˆÆÄÞ:i8?—:kD§ííKÌÓ¾ÇïhÙçÓ^ÙÑ ÃD~N¥Õ»Ú¢›"w¹úúåâ­žz‰iÑTwˆK>û”Õò}ÏDÞwÒ˜Æ-;ðaÜ%ÛÃ&ìŸ?¡3Z÷hìÒKÌë÷7Z¿<˜OCêM³å©¬®¿f]„Ó]þñýÄÐô«ÔNíq‰iíÚǵñÙ|Úpÿhfh°Œš˜lþbј¦°Ëî‹iÛëæÕùû<èN`E¤Lï@£©ù¤¿±Å„^Ûdt&ÌzÂÄÎÆÓBo^ªýв]å‘©Á×?èöÔK°}–$e¦ûÞ *:Ÿ–¦^9¹d’ŒÚ¶Ë¯lDΪÁûsù:Ï_Ðu^iŒVHÊpýQ>Ùç…¼ÔHF­g³Î“F$×!P7Z—äš¶ ¹ã©S%ÜðÕ5h’«”q|ëy77ŸbvJ^&œgèÇ/ʨyFä|mź5ý˜m»#õÌ|ùþ:îûH™3NúµçS÷æ²Otz½§ƒòV¦QPû)FMš8ñý'tœ°”¹ÕôÎúo¯ò©~~~xÔ;äë?ò~·†køû–æ ï#Çź'² ª¬ùRFekð-Ÿô*úê”̼CÝõ}=kM¿¯çÒ®Y›?æ´‹¡st¹½òtÖÆzZG§wÇ™‘I t$ndÀX=Š˜©JÜÔšµ½8ÅÇ:Î7ZÊpm éöåÜõ ô¡Û‘·K^˜Ò¢4ϱƒy"~.ü?Þo­Ù©€G] h•÷“Á®ÇoSà‚seo ÌÉr~b«¼êyý\ü ;ÝrdªÇeT™8»uŸÚ¶bùÎùonQF~_Çé],x}_uÝåâ‹~bulp’¥”ùöfÅÉê!t¼bJfß™·HíWϺ>o3€yØ8êq©ÿ]õжJ™l+aBç1Ô¤çÛovÒxR¥[ms~^í4†ŸÏÊźȆëu8$e”þ;\›X@¸mkÞ0¾ÎÇ-ï¡ÃØ'ç¨}a¹øA·~ñÅ'­p~VöË*›^@wi ?½-ŽL{Yž>ĸ>q3ÜÓÞo§C·ÿqûƒ¤Ì‡köm˜[@ÓŸ, yKïn=?Ù½bï3ƒYÒòûä°…üýt {h k ¨íÙÑክ±”rÎêÕ¼±kÉèŒQ¯àäiÌqÃëolòN™%e8»ê“Ü+'þG yõ7¸×Ûs ?‡~sö™d÷©%|ß ÝÔ!gÇ÷…néñy•ÖИw­jü=bøþo éÌ®ç'JžÁð¾*\Ü⪄:-Ç4›cY@ ±Fê7áý[×ÑØU—{øåLf8¿cþ~¯ïy¡.])3>ÿmR¡ Ž_Ùœ‹aÞÑtbÈ€K=DT±º™þ÷ž ŠvÕi¾ÞAWæ:¡*ç ”™{öº“ùÖZ±åî£] ¢éÙãåŸ×˜ð~tƒ¨¡½WŸÖ3øûèTví8?$sûX~ÞY@?ÙqÔÛ¢êü9_U=¥ Ïbè^4q82 çñš»-p+ µYÑOê—GRë‚Ý:9¦nÖG+äå8JûÙj¦ë¾ß„îÚK¯›Íq½mfà ) ‡Æ—:͉¤J½š q£Ìx­¡|Çù„È S]¶¤Lðx¡îìÃôû·ÿ¬ƒ7#h€$¾µpƒYß<ïoÇÅ ºÊëáo½•n¾wµ°ÚÔ4¤]D%w|Fð~oüzf<ò¬*1\dŽî|Òºñ…ÚQ2ñáÎðºyÄYå«Ñ Ì Þߘ‹tõX’‹ÌeáEù1ÿêÕ­¸2Œøë’_ÿ›ON*þzƒîæ@Ÿ’U/0åo;Eô * á/Ýs‡Qåc+d4S’v¾×dûÙäå—i·™ï7¡ó‹­\°gÿ&š½Í( ˆví?é%…ò¾“&ÔkJh%#ԥ槻opçã]±Yï+%“.0)ìØë¸¶ÏðuJ¡Û¦LšçcBìÑÔÒëÍüs=B ]ú{‡Î¿òÎ3EŸ·Ï³—PvÙ§ !uïÇé{1m¶šíJ⎋ ºg¿^Ø=Þpž)]ÙÃÿš¼€êwx3D÷¦zý™¯#]馕fÊ'~=ºîצNèvžá|— hðÀÄ!¿4nЃ¯ì†‰Y]žå}Õ¸øÝª¶ï§Æmôqy·+Ûw?ÍXG¼ØÕ¡ö½m5a¤TJ+³uÞ´Ü”((ãxáãŠ;üº?èš«þ|™zê7õîYHï;Ý[ÜàbÝyݭnGRæÏù ›ùøA÷+ßñyÿ¾ ·.XHªe¬Âstîž ÔÞÚ„??'Ò©ë‘÷ÇLá¯?èÞ™e¬™'?ÅŒRýSHgGÍþ~ïêêýó³ƒòœ q¾HèëÂŒ]#Žó×tiKWä}šqŠá>!½Ÿ5u^øz_þó™’Ï9yRë)ü÷ä}ˆ K¨œÝ¥ì$³²·ZøBš¦jy³ûIêµÂ­µ)¿¾­OCU†›üý:tœÿðI&ê×IK ©òjã3ÙqÇèÍôGöŸĮ̀0…5XBfn7¼6…ÏŸÐu—ïÚð©àSx …}'“BòZè1ìÔô#ä}ÔGÿA­YÝzÔ!áí›ëBùø%T ?Ç4T^\r‚a§Ì³)$á©x}§ƒêuXÚ”¼^2èÇ*µ?5?èÌZ¹Zuœq쩲³Þ|Ùuc‚‰¤Î†«c«iÓ“øUQœßœ:{›£}œñßÄî<Ò¼µµÝ"f»×­'Ÿ7qÌ)#Þÿ—Ó‰ ³Ð»-ÞuŒ>²3ëh!-Vm`í¦F2ÇÆ-šÕí“r¾D˹øA—{¨ëžq2‹eÛuM/R#çQN/üì)5¤§©»‡UÍì¯,Ò7%•ýHÓ•\ü û!¿1'¦ßQ~>~!y{uZ®øeÂû ›Q¤!ÛHšó¾DœNuÏÁµ›$G˜ãe&íNÇÒÆ±3ÇκԕQû„p>„æ¼_ çû§„îVá£k×>Lå’ѱɅԮùî™1kÍî¾Ù”8Ÿ{S ë0zhØHÎ7Nãúp-»­ƒ:û0B•W!Møì7dd=sÔqç„•ÇMéõ[›Ãl×ñ~`œN]·“÷,»{ˆ1MXÒˆê²gJ˜á¿…> ° >’;ºu3§¦>µœ¿™óC÷¦ßÁœ ×½™žEžÞv¿ ©fì–síŸdFm¸3wÊv+ê:üa߯®üþ,w¾H¡“çî°Å‹‰­w±ç,Í"Ò‹lÛ°äÇf™ê€­¯óWãë<?èl¦¾=ØKC¸´wxÚ£m™T.Q3îsÿþâ8¯Õ¨­ÊØÏš´›·šõj:?蘨výzñd–©\DW—ø=¨wЗY¿Ôá°ÍÍ u~á†Ý²~lh%äâÇ ® Ò»-èíÁ¨lšzQ·é¶¯JÎ2jßYµ/Ô?ßOÕ㥂wæ×žF჊¨cÞɽË_dõèTÐÛÏšvöœ½Õ¼ÊJí»Îźæ-ï…ŸžîÆLv}æŒE$Yµ³ÝÁßRF±®ºÍåg–êç øuîÙ\ü [ëZ=çt¾˜YðÉwEôÛúMÑR?æÞ‰ˆ³Œ,h€ÊøÏŠ¿žÆÅQûKîaÖÞ±ˆÖîröÆeFµ­:Êœ_‡´¦AËÆuryÀ}?)û9óMkt_;1[ÜK·¼žQDúKrï*^ø3‹öF&KÍx_<×øVÇÍ9?Wt³„}ô¯827F.]í2¯ˆr‡ÍS:õ½Ê¨ëç¿dÃûÚ,åâÝk¬Å³S˜;_ÙÆ£ˆºöŽ}@¢k ¿.Jã›^¾×†îO¼þþ8ÞS¦öí°gšµ>³æËŠ"z½tÆ$ù¥†{Àœ¼3Ù iÚמ]ÈãüNÐ=díG›Ù2sî´Õ÷ZSDµ©ÁãÏ>dfŒõ6ö†9íRØnæ}ærñƒN3dM©­áæDÞ…£[M‹hN¼åû̱AÌ£{Å»›ZÐÇq—´êí³%Ãý‚†qÇS$Sû7›1Õƒ®±*¢jç¡_Ÿ{3ØÛGï3nGµv£g¯Ø3ž‹tÔÚt7f>„Tx¹©ˆÊ´ß-mþàsÓ…Ýñ7'Ï Á¶é³íù|Ý›‹û9Q^Ǿ/`6ÝÞ¶Ô¾ˆ’Rníé¦Âp>{æÔBÈ®ÔÙóu´-©'t¥O“Y‡{fj£´ˆzoª\´ll(£®c\nOØö¶e_•N ÝbK÷ù#ígÑ¡‰WÜ?¹ÑÉN‹írC™~½ì—Ž0£±ÏkM¨¶çû¬®*Fb•ð½/kµŠ.ÖMr÷,¢ô3?ûÇY‡1ê~—óÞÊßï¼Vù’  ëZ¸¬£Çsb»ª‹¨SmÕÝÚ0¦³!ë¤gÂ×¥­ü}`k.~‰j;zãxì±ß±"ÊPŽ¿’éÎ0ï“g¶2¡’WúŸÎ¯ÛF_GI¿$ ¸øA÷;‹5첣ߴÐW÷Ln|í|½Ycÿ«ûÞJqçÛ6õ:?èžG•zíÜN“ß&»%I‹èZñ¾ ¸£ŒÚ¿¶zÃàϧn¥p×maë¯èrñƒŽ½k¬ý¶‹F»N4¸VD-R|E»G2üý©–©B·þñ~2èZ>ZY5­Ê…Ö9OúøðFu³3t‹dÜ¿¿¦£½h:qßÈmø§+¡[Ê–=+7~ݰˆ¦i{u9iÅxö<·¨{9ï/è@ׄ–V9ûqñKªì\öãq~?¯ˆ>.lTöáGÃï/ÐËÖ‘õ§o'UÚZ?A¥@wÍpŒæÖ[ûè›æògL z´ÉÝ%:šQû öOù³í»í|ß3K¥B·k\Çö-¼©¹"úŒCrÍÊí¾tW ³dÏ’×yï¬éN¯¦j·“Ê6²ýB•N]ÞÂ̪ÆÓ’!ŽÎºô"ò»Ö6äÇôXFí Ë­#oçשtbèFäî/ý9ȇr+˜,Tàºýx»kófqŒÚ÷v©°ô·ÆFRÙ[vÐWé¤Ð v-é?&òõ¬ÕÖ+(¢óç5_ cV²v•ݬˆuc+ý¾îf :‰»þ ÓêÝÙÞfò1ºf´]âRŒºÙÊä’“S“‹ßÝ*á‚¡F/÷ï=I·»—G*QW˜ïŸ'Ýb|6²+ˆfÔ¬è²íäf;h_@ÒÅò̹\ü ¿kÑåÛ_)pJø^YD:-ÒFŸ1¾Í\ÿØ.kóP32Þ>´ûG2½üxþüÙ\ü ûœ8øbÕiâþ]D÷‡õÔþ|›QûawTŸî$“ÄŸâ&…\ü «×yÂv¯³Üu˜,¢sž šõI`Ôëc¢lévQÖݦó¯9Läâݨ‡/ýï 9O¶Ù®|QDå½NÕz‡éèßyÿÕ3ò®.*\ô{'½zØóíòÁÓ¹øA˜ ‰6{qÐtñ uó]°éw~}‡v6·$Z—Ú·›‹tá\3ÇK©väâ½ù¯ÑO¨.l†á®säÏ©¯æ_ú°‹JËCR»|2àâÝ’Â-ý¯6¿D/2&÷Ýý¶ˆ\®[$m(ã÷ ͈u\¹Ê©îþC¿{UÂ9ƒ–fLU\¢›{lŽö|WDq¯GÌž¹DVw\ÄçtMFvRûUsñƒnêž±öûÑVݳwßQЈ‘m_^1[æî¼±÷)mð\†…œºªi»k%?è8ßáË$Õ^52:—ûšW=—1\ß|½`Ïþ…'w‘X+ÕÇìå*.~Ðé%ìÿRùù2™« ¶ŠhÀ–KLG&2ê}>î~e”o¿sq5?è^t׿ä_÷~'[™å˜È¨×ëÜËfúúï¢d›Ôëšã¸÷“B'¾Ð¸ÅÑ)WhU³£’ ‰ŒÚ÷›óÉÝEìrâ©\Üðzi§7ÆÚϯPÓEÕŸà8úaÙ¤^Ó=ýÖ‹zDT8fu«Nd¼Ê/"£7w•ÐR=¨q•fžÛ·q ŸÐyÉÌ$F½Žû½÷¥¡Ï–ìæŸSâãv¿J˜¼áÃïë}®QÂxã É8¿Š%· ²ö'1Öá.øä¦Ä®~ô(ÙÍ?oÄéЭj¿}ñGÙ5²í«-}VD?f,;5­0‰)9&{Úm’©.çèÝtÕµgAèã¥\Ü 3üýLwÝÚ:83¯$×yó ¤Ð–ýï2íOUv\0§ ÎUSwyï¦Æ.ŒïÐU˹¸A×rœòÃ]Í@bW?("ÿEÃÏ;Ýe&Ú¡OY’u#öŽb7Õgmȇqç‰:©ÒtfË«´1fõͼ"ŠJô•v—™Ù}é +rœB^ÙÎêû.nб»ÅÇ ®Ó½þ#·º"OO3 Z7cì=ÆÉ£Ñ—=¬iëúIƒçì¡zlÚ‹ß}u½¼Nƒt~œˆ¼YáR>ïÔ=†ß·$Ö½þÔgµ¯9?èæ­ =èÄûœQëòÀ#×>ßcx?V –W‹û¾r¢Ã’eq:ä*!÷œU0þ?SŒ¼iûãSÈâûŒÚ”]}?ñv'9}6¾Ú?ш‹tá5Ÿ¢†Ý †õFn{…~€Ýåûqõ~]žþ6gϽGb]Û»êó×tFŽk}zƒ*ìo×ÏAßbÙÉÊjȧûŒëñ⨄‡¦dÓ¤Ïç¦RG:{HcsÇáÜy-‚ŽÍ9×oÒÏ‘Ú}Úzàýæ‚ìa.4…hõØdÌź÷Ç ¶MÎ £}ÊØgqºþJT«ˆIrF½>;ùݹ¡‚È=uϨâêqÎáÄ­ Ѻ¾‘UÆÞrõ>"ß8×íû©â]SïžfoV‡Ós}öAëBÖÿHñœb9ó…µ52£év)gtt®Û7RÅ:©Whùóá”{°qM!ÝB÷~Ú„I{ßEЋ°¡ ²–R›‡†ŸÖöʪËìÓÝ=¾ï¤µúûöéîáÎtq¬}wmÿÜL!Ÿ'³î9Sê³±å•ϽwQdï ~Ù³¹|¦„.ÕîÚ¶Ð_¤•‘Sùel!6y­ëÅ4+}R3g€)ÿüùb^¹÷ÓH«–ê™Ò(’Þ¹V~œ\âþ;‹ß/6¥böñ¥»ë~Ï¢Št/w{šÞ<’fÌ÷껫s!­Îz;óþœlF½*n~¨Ê™¯|þ„.œ]†mIòiÛ4»5-$¥BÃ}ÎÉlFíÌ=?º‡.¦³pŸSÝÝaÞ=jµ#©¢¾BgÍ·bWOVe3­,¼?š‘j{ý¸ ÿÜ ß¿@÷‰m;‡DÒ¶uY6+« (uè—v»(®ÿ3§IsûMºTäBZ}ßvK3àú%)twVŒ7FRíò8ƒ¯EXyàv“é F½N^Ùiíœ÷Æ.”Ì^ŽÇ8 :îùÃHâžÛ+ ’cC¯oP0ü~ _ÐFùqØâÖY¸ú®„îd“çí%‘ôXödÖ‘¨úx*Ãjø£~.ãä”mSÓw×=G«Š_z•°…ê&’ìT°€¼¦õ­`¦ÆÖoöé£ å³ ‚ÓÇEÝ*ö±çw‘¤kzQÃûH}:y{Þ‡b“<âèäi™":áÂf,'zÝòŽîéA†\ü ã~Ež¶ìRõïõj‹ùW£®[Ãê›978æD¥[fšºññƒŽû]Uig¦}îµ¥€è^ÐâÈN9Œú÷UoÂ>^jþ‰öò?õ,w¾ˆ¡ÃMσ}·£hóƒ/ÌÚRµƒ£s˜ooÙ~Óªï\·›ÐÿäÓ"·×Çùç2\?mIE ØÆÅ™tÎ4 •‡pëCJèž”Tõ,Œ¡kì6áƒ|bUg•Ëpëbä={KìáÝ|pñˬzE°‰8†v´mók¦"Ÿ&,6¾è^›[—'*¶(v9ïÄû¶óë.Э\ûž¾âûuamÔïå“í¹-C{æ1»–[EÉz˜ÑNÕèNôá©íÏ¡üº tí§5Y“t)†n¯rÿ²<.ŸÖUG?ß63É7ñ0šrÔ”®Ü^1mÇœÝôEƒ}¢Œ[A·ý·¡¤•k °`ïˆòé­ÎÞ{C6ä1=¼ë;÷^dJn]Ø ~g~]Ó‰¡k³¼vÔíU1ÄíäS?-‹µ?å1ê礿?×ÎÉÚ…~t™ãfÛh?肯Ÿz¼~d ÿ»¯|bWñzDæ1j_ótçG»‰ùß5qëC2èÆgÙCfM7š½Ú›O““ûO¿X’ǨŸÃÒûرõÛñbb]yx ?èvXˆ/Œ¦ °½Q#¶çÓ±jYæäßyLfïöFSóL©©dN¬Ïl1¥¦¼©ß¯Q.~YÈg_&¹”DÓ};e½U>M±°ù|ß|†ÝÅéoJøÛ’æ‰éu¥$ª¶=?èœîXÆÙl‹¦Ç#&}Ž0̧ۭ?—ÕÎÉg ØŸ¥ž4%n9^Ì?¢ÅźO>~Q4ݹœ¼×w^>}yQyéíæ|OÍ|kS oû!Ø¿ƒ ÿœ·ž/‚Ž[‹¦Æó•…'åS³ ïÙËgæß­i3¢Ÿ)Ù*Ö:OúèÌÅ ¯W4k±êá‹(z’°Á"ix>}§´ŠÉg^í=¥ÌobJg'³¯pV¯«sqƒŽ½º÷ßâO¿í¬Ò×>ȯ«' Þìü¹Ø™Öw_¾@ËÓÉ {ô$¦Ú<0ŠŽN´é=ªS>mÛ(QnþϨýèWÇ»mºwL}~uàâÝYyF£a‡£è«»ã—OZáC|kû0©+N-­ojJªmóÎ{êžgTÅ-»JÈîʶu‹¢s£[z?­—O½µ¾:ŽœSPwß7gø†/A.ôíÜ­5ÃôââÝú­çìuŠ¢ø6mjóhÃîU·¶0êçÛU·1\øç­sqƒ®gûã~' ;•ræÈÔ7yõÁ¯kc߯dÿÃ)Aæ´44¹dúñf·-:¼i ·ßÝð úàs²g¹ÖÓ<2­Wÿ@÷» ÷|‚%= ý~¨<Ì…´O‹×õ™ÃígŠ¡3ÕuñÈ8Eûž.˜X–Gªíðwuë'>úmgš‰Õ×9·ßÝ•ù¬„FÑ2ögùy¸»sá…>…Œ]7Ǩ²õ´[µ&¦³Ýï|ØÏí/Ê  Q4I/VDQµõòyÒò躬oM…a!ÃýÞ`#±OcŠ›»·ÿ5ŠÛo€®Aä Ëóï¢hÑ”É8„yÔ6zFÞŸBÆ}<º« qçµ­Î]Üʲ·ß§¨r¿oD}f·{£óhRïÚñòB†{Nz#©~wוÎÛO@çhY¸DMÜ~F‘ÆÏ;¿ ™–徨@éå}ƒ…‘Abê9b׿Kƒ§rûEÐÕvq«¹9š¸uÈ<:ÕïöÂÑEŒÉò„ ­§žªBäBªŸ ìçöyEÐm:p¾¦í¹hJ·l5gèñÞÛ-VD÷Ü_}m±`î‰"†ÿ=±OÃy¸»ÊŸ¹”Ûw•BÇíÏGÓ.ö1<§<:é{èmbÓlÞ$©MšåωŽ?‡¼ÒeCû£/øýZèNè«:{2\qæ¸Ûæ<Š¾Ð«[Ä«"æûÖŸw:äÙµæ«É^1ß(^·}ÑB¦ïß™Jg*iü©ôw¦Ò™JêoÖüyÁ;öï×b j€’•øÐDâG ªÿÙJÿœEùÏÙݺÿ2[I IOˆA,¨:H‚"à @ QA(¨$HCàä|²Ô¶ (6’§¨ºH¦Ö@ Š«>ƒXPthEÀ(€&’®8‚PP H†ÀÈù„¬lAPm$h 2P t‘°­-$o} þ—ÙJÿ»Y”êÙJb j€¿øÐDG ªEÁø9_ ô€-J ‚a$@j. ˆ5‚âøâ²Þ]ÿS¼NþO=áÿ—ÎîþÏô:éùŸÔ#ý³?úWÿÛ¿}ѿߩ{¢¿ýÐß~èÿÆ~ˆÍ-¶|ÜÙcÃê €È@-ÐEb²RP ´¤ôÄ–!ðù—“êyÜì|I[”@IÍH€ Ô]|k Å@ OˆA,¨:H€"à @ÉPA(¨$GCàä|¢Ô¶ (6§¨ºH¤Ö@ Š’ª>ƒXPtÐ÷ˆ€/PM$]!p¡ „ ó YØ‚ Úèy @,¨éð¿Ÿ+©Dn$@j.úk Å@ ‰^ˆA,¨:Hü"à @E@A(¨Càä|ж à_¼pY®ÿ)¾%ÿ§>o:ÿ—ÎáþÏô-QÏdû—¿ó·ÿûôD׊þk{£¿}Ñ}_Äæ G>¦ìwgÿ¿!ðr>Ié[”@IËH€ Ô$1ðr>¡é[Þ³D=ƒ[A(¨$ƒXPt8EÀ(€&’¨8‚PP Hª†ÀÈù«lAPm$\ 2P t‘€­-$c} ± è 9‹€/PM$j!p¡ ؾH€ Ô$rð  ‰¤.Ž Tg|€œOøzÀ%ÐF0 µ@ÁHA1ÐBqÐb j€Š…øÐDáGú/~¸¬×ÿÏ’ÿS7ÝÿKçoÿgú•¨û$œ:÷ÔþöIb¿}Òß5¤ÿ>½{½‹ù¸±ßM&¾@Á~$*!p¡ ¸ ƒZ ‹Df ¤@4‘Ô„À‘÷+)ZHrú@ bA ÐAÒ_ šH€BàBA5 ! ç“£°@ ´‘, €È@-ÐEò´RP ´HõÄ‚ ƒÄ*¾@4‘d…À„‚j @Ò5>@Î'`=` €h#! Z ‹m ¤ h!Yë1ˆ5@É[|€œOäºÀHA1ÐBb×b j€½øÐDÒG ªEÀø9_ô€-J a$@j. †5‚b …â¡Ä ö_¼pYÿ­ÿ ~%ÿþn¬_‰-J ‚i$@j. ¨5‚b …bªÄ ÔWð  ‰B+Ž T ¯!ðr¾ë[”@EÙH€ Ô]ik Å@ [ˆA,¨:(à"à @Å\A(¨wCàóÿ_ Ûgüí—þöKb¿ýÒß~é¿O¿¤ÇŸs2þ³ëâϬ³ï‡D¥Ä Ô$.ð >‰é[Š’š>ƒ ÚHr@d –õÇEÒ³RP ´õÄ‚ ƒ„(¾@4‘…À„‚j @²4>@Î'N=` €h#‘ Z ‹Äj ¤ h!Éê1ˆ5@IW|h" #Õ@€„l|€œOÎzÀ%ÐF²6 µ@ÉÛøÐD"×¶ (6»¨ºHôÖ@ Š’¾>ƒXPtPDÀ(€& ‚8‚PP (†ÀÈùb¡lAPm ²ñ•‚b %Àû1ˆ5@…F|h¢è#Õ@€"d|€œ/HzÀ%ÐF2 µ@ËHA1ÐBñÒb j€Š™øÐDaG ª…Îð?ÉßMA(¨LCàä|ñÔ¶ (6Š©¨º(®Ö@ Š ­>ƒXPtPxEÀ(€&а8‚PP (ʆÀÈù­lAPm$ 2P tQÀ­-s} ± è ¸‹€/ó…^Ø‚ Ú(ü@d è¢°RP Ø_аÓgÄ öý’Pão¿ô·_úÛ/É4þöKÿú%}þœ’óŸMØ‚ dÿ>$* 2P t‘¸¬M$1!p¡@ ´‘Ô €„‚j @’3>@Î'<=` €h# Z ‹„h ¤ h!9ê1ˆ5@ÉR|h"q #Õ@€Dj|€œOªzÀ%ÐF’5 µ@I×HA1ÐBÖb j€²øÐDrG ªÉÚø9Ÿ¸õ€-‚b …D.Ž T»!ðr>Éë[”@IßH€ Ô]k Å@ AˆA,¨:("à @ÅBA(¨Càäÿâ…”@[€÷ µ@…ÆHA1ÐBÑÑb j€ŠøÐDAG ªÊø9_¬ô€-J âe$@j.Š™5‚b …¦Ä Ô:ðr¾èé[”@EÐH€ Ô]Ek Å@ RˆA,¨:(˜"à @ÅSA(¨SCàä|aÕ¶ (6 ­¨º(¼Ö@ Šа>ƒXPtP”EÀ(€& ´8‚PP (؆ÀÈùâ­lAPms 2P tQÜ­(€& ½8‚PP (ü†ÀÈù&@Ø‚ Úôoûݲu¾÷ÿú¥¿½Òß^éo¯ô?³WúÏì“ ùó…=¦ìû #Õìë‘  óÉJØ‚ ZH^ú@ bA5 ™‚XPtØDÀ(€&’œ8‚PP Hz†ÀÈù¨lAPm$D 2P t‘ ­-$K} ± è yŠ€/PM$R!p¡ X óIVØ‚ ÚHº@d è" [)(ZHÈú@ bA ÐA‚_ šHÖBà€h#yë1ˆ5@É\|h¢'G ªÉÞø9Ÿøõ€-J B`$@j. ƒ5‚b …"¡Ä Ô ð  ‰"Ž Tï|€°ÅEØ‚ Ú(6@d è¢øX)(Z(Dú@ bA ÐAa_ š(RBàBA5 h ç ˜°@ ´QÐ €È@-ÐE³¾@4Qì„À„‚j @ñ3>@ÎB=` €h£0 Z ‹Bi ¤ h¡hê1ˆ5@ET|h¢  #Õ@€k|€œ/¶zÀ%ÐFñ5 µ@ÅØHA1ÐBaÖb j€ µøÐDÑG ªEÜø9_Ðõ€-(Z(îú@ bA ÐA±_ š(üBàBA5 `§ùù?ú#›‹þå¿‹ÿå¿ÕÿTœ8¿Ç Ó@sʳf í]‰u;?Çé”ÐùÖ^õ%–ôÚZ‡~È¥Ñ9^t¶*ax?'JtÅj——+ï‡ÄÍå×È­Þk³»o¿8šaj{ôi. ïÔ&ô@I;õÑe½+ 1a'lpßOŽj _õV 2Ì¥fzX -a8¿tâüš]É©ýÍÙ廸8¡»ÐÔ£»â¨žÁýƒF¹$Úpâú¯Ü†›"¢žæ«tßÍvUϧââ];òRµ¼Þrá &—¶.(ó˜ú±„~½µ#ßWÄûe¹R‹ÔÔ{7Ü8Ÿ1t-—8±')Ž^võ-Ó Ï¥_;ž§ïèPÊtµøii/¢AõÏ=ºÿÝUí÷ÉÅ:ÖÖº2ŽÒöÞØòJ.5êwKre\)#8¥÷^hnB¬Khà;77vâçg¼ tþËXcó8~.s.Nú¤ïnTÊtœwdw?Sâæˆº;Õ¥íGN§„îB¹tUwîàÎJn®º59F•nêéNöI{s‚m8¿3!tmÝei—fÆÓÐ^=?ڬȥÃÚ´Ò⣲»<±žb„ÏÌݼ—ä/VdÏyËù+ya?W8?žÂ:±Æ¹ô1Ã5¿Lø€Ï?h+;ðâ^Ú³Øí×´Ã|ü ãæ2Æ“KRÉ ‹‘¹tïµ"TÛáÓJ¢µ2ëÑzš©õ¬¼áUwÞÇóm‘B7S ûí¹díyáQáµLÁ‹  úQÖtøñƒVäFažûËò ÖpqÃëïßÐf1>ßlqû;5Ï¥ÃGf§—=`*Žãʳ¢wƒ6ž­°s%ÕØÔÞ‡º=÷B ïâxtjl²ùKÉRµרŒáý1I5~þ”+íWº . ã}Üò«„kÆNxv ÇŸuëU‘Cgzß\¢[Ƙ«ªuîñ}Õ™nj.nÐ *ošð«oª;?o|3Þtÿ—µŽ½éÛæ—W¤ÐÝl“¨»-–n™ÎrtÒÊ¡­ôîÆ*ÇGÌ©“ó£MÌéô\*UèF¶¾Ö>n׸ã)ƒŽ{ÿXê­7¬òjã»gÒ¢SÒGLHÿE«™QW•Ñ–?'‘÷ხñ<‡zÃGÅ’¤eÇ…¹?4êA¼ðMÊ#†iVóðéA3ÒU Üs#ýÀ½>~ã|Î4 «„ª±N=c)ò8k`© IS´Zwzûˆ9»}Dæ¶3jÛv}W?7¡2¶àü@ЭœÚbÛëæ±¼?‚ìíkzuzÌ|zPäºu–99„ûéN±é{>Ïâ}¡q²~î¯ ?ì‡RA¾ÛF3*[Še´dvJüæ^âçÀrñƒŽó爡Åó¶Þ[  Œ®vmÛ<®›Ë8ñÙ<ÏÙKï×Ì]3òýA—v_gz =Õì,HWPfÓ1ÌÎ>®›KÆï{‰ŸËźùª šïÑ}ãËŒÇ ç¾ž.L»™!Øû‡¿» ¯G¢µŸïm½øáVÑ ÚrÇæd %Ãù¨­§T‡¥iûí%~¾47è^.=”Zãq´ßÖ!o¯+hõ¹M»(οu=ÍÝý¦á÷wâú!þº+B~¸%œãÚ(–LG¼XítQAõ³¾Ô¥d.W [Gm²¦%G—·ÙìÆûqï'€î®Û÷z­[ÄRÃå—W68® ©=šw ™«dZ;ïQ2\>7'Óv§ãdU®¼Ÿ7¼þPý‰*H“×[«dÔþuÜ<=·?â,Âëõô%ï ¥3Æ\º)ȧ`I• e9ÃÍ;5¯««å;‹}‹øë :×ÌñŽ8;³:âTPÖ}¯e ½Êþ| -1}›¼ÚàF«/ÚÏïø…ë£¤Ð­^º0¥¾‚¾u[_=¦œ³®_Ï[ÍÍÉ¢s…a§WµÏ='¼¾k;‹ønT%àlÊòÕ ˆ˜SΨýb,ì!æJGÓ]ôgN§„N5æçÓ·Hï_ŠŒl× fŽÔ¸¼nþlT—¶_ ¿‰yŸ6þ:+Áyب´û\/ÃÇL£¥!Ùôt§íŽÂÍåÌEŸó1a%&”¡pì-ù ¦ÙG¢<—8ñ×tÞSzZÜÃõùJ›5Ϧù‰Ì ‘¸œ1tXÑüÕªfš„¬)ÓÆ=?®ÌtæûèØéCÞÇÓzok;²I÷eëGÖGÊ™'žļÑ ¯¬‘ûÅ´r€•ÎG>nнbm ªbh€çóŠÙdö%oÌ¿rfÚº]ß·æˆhX7ÍÔúýÅüBþ:ƒ.à±Ç’´2j|îŠùølòvŸpvFD9£Sø~ršÛüÔòâb ŸÇBÞ×:‹»gÒüBcȇØ5›¾+D7¥÷Ê™{}å}bM¨¨Ã¨¤î8žOoÄ·*àÞO]t܈É&^1tjëÀ›ƒ¾gÑuv za9Ãùˆ™ÒR•A+•çŽxÖù;_ß [Ò:§º•i ¥P¾ ~˜Eg—4°ö¢¼n¾üŸ=Ý®»RíØŸ%‡Ûóýei•°ì|Ôƒa£qŸ¬5qiõí,Zy}y£È_åuýÞð€-§ó,\‰óeãâ.€n…Å㉿£)Øçý± ³h£wtç5í+˜vŒs7KÊoý¢·G¸˜ŠJ|\,µøûè<25:™§GS„³Üèæž,ÊêöºÝÙAuséÕs¿?,w]Ý‚÷™…îMætÑôì§Ë9]“,Ê1štû™°‚Qµ¿ïÑ—ú_]3”3qs(9:ë§s¿ES·=ã†OÏ¢î¡A©KWTÔÍù,™æãÞÖrݵè0¶1÷ý¤Ðí«ÑU«W4empÜ?‹‚ÆµÎØ¾¡‚Yžà¿PhFû|ë5Ö‘RjгMòu:_—û½Š¢Ç {'1šY4§Ÿç°÷»+˜-ïj.4H7¡>žÝ“ý™¼_?èÖÿò-¼Eí³KêO{™IõÆws¨¢î~·8Z'bjÿj?e.~ª„•÷“´Æ;GQNø›žÍs2)ãœ×Œ*nn¦ˆšh>­~çäDFSÙIÊüõ]ÛaOÚ”éG‘]ZÙî>ñ™t¥8G/?¤‚É™$ž¹ç§ˆ˜Ö Ô™ø9õ\ü S¥«öQ4ìæ»Ó¯.fRIFòÛ¶‰ ßWPP/»ÃWZ;ÿÑ׈ kœ0ÎSCI[?™û!ɤÃJºÉ­`¸:eJ£tø&êìD½Bš«¨ä}f¡c]"ŽERì$ÖÁ5“†%Ìží^UÁpþ©¦´?‰$ïHÇ»ó9ƒïO “ELÞu×9’Êcz%o\›Iµ¬ kmE]ÝÙû$véÇ?î·dÐÍÆ:ëERÿßǧٙ´üñ²ßNÍ+ëæ¶¯N¸‹;KGâó?è E zGReE@ÙŒ¡™tžµqïQYç³ñüLê¸eޤ(LL|tÏŸeè×5*;þAœ\&…GÖÆÍÒ­dæ|ÙŠ’oB–~šJvÐ ¯““.œä®#t¡ËÝî—gD-Ë?2hå&ýׇ§VÖŽ]7al§­ZÛ"¯÷ãò¼ºyªEÐÏDÙÖ·åÔ®zMã‹+µOìÝ–wgßÛF\Áõù"辤8¤í Ó#íîePè4qÒaQ%£ßjLûïoi6¸Ã6ªòò†Frï'†n•ê‚ 9Ÿ5ÇL¿žA=›èXo®d¦¯ªý¶úÔjÞi+µÝ;Xj°ˆ¯{Ð}*µ ¾7)‚œ·ËîÈ vUg S%#ù†;?CêTT;ÿsöVÞÿ™¿?€îÒ—d-nþyGôì-“qèk¿}•Lºç˜3MV’WÖÑw+ï#ÈߟC× J{Òüïá4u6;Ù6ƒæòyáh%c×ë°rVÚ âüÓ¶_g¸ø=¬†äØK÷•†Ók³FLËtmCƘŽ*™¯LŸAËÆ­¤}›öìjnOòS‰åøë:£7 ïÆ…“Ê~³]Yýû]XÉÌ`ǵRxéæ…ÜŽœ.¯ð½SĽŸº&Í U„Ó ÜÝ´ýœN«R{8DW2Û7žn‘òaqþAvtDtýtT{N'‚n½Üñø\ÇpªŠ¼öä\q: °¡Î•w+™¦›ýmŽD7ÿÛž¾ßd 0øøA×°‡¶°Ö œï›ÒiaÈøà¹•Ìî¨Ëz³»¯¡'jö®u±§ù¿¸|9ÄÝOJ¡»¸5æå•á”ÌÚÞI§óSž¨¬dŒ^› oº†5h<çâ »?Ö·dÐMP Чo9¶ÈLé´æøácŽo+™²?Ö_ž°†Ø.Ú'~ïƒÃ­û)¡;À°F"aĺ ŒÓiÑû’šë?+Î÷ڈމît´¢¸ïÝzÍâÖ4U t8ÅD„ÑXú•ë7%Ú^{Ó¯ºÙÞw•ú~„FìßÉLÄtIØ•Î0ª02÷¨{:udoÈ;>a8ŸõTØeî뙑køyÇ|ü ë«³ç@ÜÆ0² ÞÿÀZ#κŠ7>¼]DO¾°…y©>Fêb]föoàÎtŒsw=«aÔ=µŠü§Ñˆªê“IEu~bjú_‹¡ÛxfÇ9¿>a¤’ÝI£Oº­?/âý¨—‘zžð×K¹’ÓÎüý9tMj:êü%‡ú¬£xuh,Ëc\Šø9íË)así—]­É?åí¦oÜu+ƒnç¶>CU„ÒGDµÓ®4zX.ûd¿¾¨Î¯Â“—¾Ðúÿr%týŽ8ûX$†’4ÀÆpéª4r‘–E¿]\D†ÝÇçv6¤ Ö>¤‹]6îÂÔÌåëßã*aŶ£7†] ¥ù÷“oÒK£¶=ú>9¾ˆÔybµÛq³Ùè ¹úËź‹†×Ò¾¥ZÖî¦SÙë·®~'("&?û¬÷2CZã%üÔº÷*ú¬˜²¢æ?èÚ>Œ¸™cJÜõ–JQémk{4-"gÕ ô•´ÁëDŽüÙr2®žÛðº ýA÷xŸû^u¡„fض~^*M.ZröØûBºÞ¢r³ÖåtëÔ÷':Âet?=/Îï8?è8¿ÞPZ©ÉNÂO¥#Û_wkÿ¨Xw%»ÍK‰uƒl×l1’´~U;œï_ ±§Ë—§ãBií'ñNùñTZ™¹uÆôBZÑÚwßü¯4¡~ìªÛ†úüÜiþþº_:¬Óa(u`W0S) æ“×û[…j?aºÒ5¾LÇ:oœ0–[·UBw«Åïç›…Rʧ·ß2V¥Ò»+Óçß,$n]``ǸBœO(¿.­¬¶ÙrÞåÖ«úîpmâ¢I©”»P¾Õ¯¸õªY¼|+âý:¸øAWð\X¸4(„8ŸèTzÙèÛÅ•§ IíwËÍ÷Ï¿ÛÃãåâXî¼B7´Ï§ iëCÈÐîz”nóTê³néˆÇû ë|B9¿úúômú¨E7òñƒÎˆáâÂǺ×Åš÷^~»A&^MŽ/ˆ““ió§ËŠfÒÒ~i–d­$ÎÏFI Ðáßõ§Š[¬¡àI®¡Kƒ'0ƒÆ²Ž{Üy&„.rÂøév)AÔþØ8YÆ9¸Üª|aqÝmã¿ä•µ1ï‹4ÉDuõì̯oB×ëHô»ƒ‚x |Χë\/Ü.à}zêüêÇ];ZOêͽŸºâ37n?q¹Nþ•pG-'óŠïûNûçO»ŠB»FM³1®´ÜwÅ•ï_ «_@sy }{wªã§9r*­þb¸Ü»€z ÖMk´oeÝúù-—!3öÉøþ:Î3¸}9eØÅJ5·çC¾‚*úï[4Á„ùçzœºvCwNJtµÕû]ºåÎÚïÐÕ÷Û÷æ¹­¤KsLçÄ}7VïKqñ«À}N¾»®ÑÊ×g| µå´é¿1z…®`ÿÀøþ‡±Éx)™>–¿ÿƒn“Þ–?®·!§GÓ7<X Þw"_Ö–ªËæŸ}ˆº‘ò5{¦]¡ý›–ä~O!› _7š¶* ·C+¢ü/«¯ †÷±ãâǀߎÕú“jåM …·‹›˜\“_çWØ×iï™^Ë—1ìîNçüº tsYËÎþt¹WåâAå)”˜°ûÇÛÜ|r_p~öýßkhéÉ^‡ìŒ‡´Ëè ùýèZÏ^ùüÍ„Ëäzl“²U~ ­ |1¼QT>yõ7¸×Ûs õþùÙAy΄ d»ý|ÿ ]~¡DÔÍØDj7HN!¶Êµñͯóä×™>þ›­ÖíäûOèZlt¾D½ƒŒ&HH¡/ŸF wçSú—3¬ÑŽKq û µ`¤ãØ~¾ÿ¬¬¦á®Â÷‚”$âéK"Rèòc³åMóùûÅUtŒµogÁ¸U¤øÞ:ÉǺî‡;³+‡ôv9ë B?¾xü(ž“O“[¿É*Ý´’÷õ°`Ê÷°;üý;t×nî>ù,ñ<=:d»ºÆ/…¦EïÓn6*ŸDO, ©YN™Ç663ßnÉÌЦƒNø}=èš.¾#õhxŽúŽh´ñ\ 9ìÚ=Ÿ$«ónW.ãó‹5cõ:ºy¦wÝŠ¡;fËž€gH›µu;‘BBï [[7ͯ˃ΠŽuY¸‘X5Y—_¤ì÷óÙë<ÑË—š¨ å#òþ?ìÝgtSWÖ7p“¡D„&ºè‚PL ¢nQ£Š D1– ¶0M´ aJD— E¡ @t™b 0Ñel°lƒ-B”€A„&ŠAôçu¯y2³òeÞ•¼+OƳÖïÃdy#YGÞûÜsÏ=›f·æ*P}ÿÅΤuž1ì䩃îïêâz­ ¿ZvË|z¸Z-«]x=B¿¦ï»tb, LO óMü¼éã£IÉÃãhy•¬_ê`¼¦×aD‰gY…×#Âüa< ê¾¦Š†ó .‚k¯´bI ÏÑò[ˆ¸–õn7PÜéø±1:¶(õàî¿òï/(?_Áß?ÐSâÖϾìüý9šôa§®»Y´£ïú«T!4`ôéô.Ë'2¾ˆp½Ž¸,ýo|>‰jvž5!{Ó9ºsöh½ÈCYÄ÷gH V¯<šñp"›X0ò%âê÷ŒRŸ©>šºöÎu•4OÈ¢Œ½Q÷/&^÷óã…¸9Õ›÷Ùprpñ}\ú~|ñë…¾¹ØV±³Á²NÂ|qüþˆ–wëÚ©çð½bË?ªýMñ÷Ñá±S.öŸ8–ÍQp…qCÜ =×Ág {ÓÓeþù²k¿Ç·ËÄdQ7¯å ù“ÁÔÛ<úåÁ±…×üø!ŽïŬínîºqŽ,Ê®ÅôÏ¢ôž3ESS¥ss†¤m;°É×?¿Ì¿<ˆãûOê_éKH[wªm-<ä¯t}(9™Ø·I,[¦HÙfã¿ÇA7q}ù›×^-o*›7kÛÈd‘“2ž}y}kõ,²êŠï¨ØqkëàŸÃ+µi–ØŠÏCRÄ}?k¡þΫ,pYPÕIKú뚊eQ•fŠcs†Rרø†×ŠgB¿^~ü·-çåÒ¯®Xë@ásÒ‹ýC×y3)®æÛ₆RéžG6}išÀ„~Ëüø!îͽýaÓŽÌe?Õ[·ë&æK<Þ?'#³ðz™Ǫ¢ôN`Û"¹‰©0_A¿fû5ÿöˆ½ž¶ÀÔÏžIõš­J®·~0ÝËÝp¿ÓñB_>΂8L* ²âÆŠNúbáø’m¶dÒ„e¿†ãQåÎÛwžÇ²^ ¸+B>Î8OôÈpã¶wT«1Nún ë^ƒ¥™Äu]+È ¡ôÐ/œ%;ù—ÏŃ8–Ä5Š]ÎÔC>ˆm©wÒÁbß0}&±HýݘÄ:|æÕU×ÊqLfW˜;Z¸^¿•¯Ð518ž…V ©¾óÆUÕL“DfR¨ävÏ{³q79Cúßü×ÏSЏ=¸Îs+¿~‚ùûÙ2ÏËôÍ|÷÷î}žÝkjúbòㇸŸD‹É7¬d‰¯ž'iv8)ûÕ’hG›Ìwãîâ¶= ˜ÀøþYüß»q3¢?7¬¶šñõÆIs{½xS¥j&õÌ_{}ÿPú¤ß¡ÊћDZYv®·· —ñÖ0o…¯WL¹à¤¡w.—Ï|î¢ú6»ó&QC|ËôM<oõÆq1æ&ܸü=[ñ×PÌIHnµä¸èd•†_~!Ô“1ì×Nný ~ü¸ß¯¤ôÐÂïÖ±¾ÇónžÅuÍöŸœÝç¢S9có¼¤CieÆ0÷%®q¿̃¸÷6Äývåúü)zQ*=í¼üé.rЏ¿¤¨wï“ßÏÄï òâ:qm§QmC7±U\›¹Ú©Äu-?êêy­y}${C£ñìôýŽ ÎÍâ×w¤ˆ»š7¤ÖÛ-Œ¯—©´)üLqS[Þ¿çûàN`|oáïqâË/ÚcÙÌJã*CÙ7•¾9Y¯£á#î[{}•Û¸£cÜÝ< ®·ŒäÙ¶ma;^¬ºømL*}üó·äfPò}”ýÃ…¾T:æüúmíF}„}Hˆk6Ìtyw»[yNâñ9©T'ýÞÈûßeГXC–*AC~9ùð k\BÙfN]þ÷³ îÁ{ >¨ìøµh=veÁºTÊ2v9½k@}h¸­¡·Ý¸4:Ö%ô§DwCaü×#öÊ™6Ù[Ùà¹ÁÉ3§R¯¯Ê-/—AüuD¸°. cªíŽ”¯ÌÇy×hó“ù¦ÇÛXÀ†˜TQ9T¶Óy‘ä²ç«_‡Ó’GéiáÏÆ±ºãû5x_ÁÇÝÆõ‰»\¯»¬ì^܉¶Óž¤RºO†ÌºH«z<íºúƒHŠàÚ“?ˆe¶üUwƵæã¤ˆ»WŽûw°®vqçé@òƒÝòà‹Ô%p£=ªpÿ([:²¬ªZ¬°q V¤âJu'óU]è}zžŠÏö¿~íÕìTamýÕQäý1nëC ËïÙГ«ä¿gÄN½>úùœ]¬Çëæc/ >OOïq7Œ/P|®ƒs­»eœ¹f` ûthB­-™|ß-âÆ-›ŒKÜÝÌì¹TrÅ´óT]³8n¾üBáýwjrfÙÙͳbXDÂîŸûôùŒ?Ķ)fìawNMOl>OWK¼[p3F½Él7ºZ¯8tŒìP {ÿ@·øû·zðã‡8¾/÷^X¦;|ž±ÕÛV2§7[ÈjIc”W#owa|_fþõ<ˆ;Ø||B1»?¬[oÞy*0ýÓ7Š Fÿùb|dþ÷ ú-_Q‘Î^=4ÏÆ¸¨Ý/ÎÓñkê]™%ÒiíȈ¦Ù-Âiàú„eËv… óa.â6DíJk\,Qèg™&ôsK£ú×òÛPÍ»uãÕ+òë¯<ÎÇ)—yÁ)–È.Ê˘OiÔåîü÷’榟ÿÃHÚý\¹&CÙœ™¥*ÕÉÇiw{×0‘µº>nkn4^'øÃFåz¦Qoî2¶F-ÿrJý5o5Œÿ~óqÄ=-Ã5ŽÛÏ>jÂuÐM£„§ccv”H#¾Aêäo¨åÚ’µû"¿O‹³ î“þÒ»wíg?ô½ÜyÍÛÆ%Âó4jétc¯iútOÅ÷ŸgüúN~ü‡‹™¾Ÿ`)ûK—ȽšF—G÷õÚ¸ó…ûlè§ýÝ_Z=’nã éÇâ< $û¾t€µ¬ß¨Bò‡é»Ð·¨cûóÔ<°àI¥gp;¡‡±C¿ØÚøzóãw'_Áý+µ~8À¶¯ê†KùtZ·{ÀG™ÞTj=ðÁŽõO£hc™e+†ÜR3×}S„¡Gw~üwlyBûý0ãÆ•Ÿ——Nw¹íÖñ©D5¸Î–Ñ´ Ýßñ&CX™[Õ¢µZòㇸQƒè:¶9ÈÊåÆ~4cs:•ª88ã~[䙃5¿œ6*¦°_2ã×±\Çㇸš7 4gd5'oLl‘—NµzÌœø8×Ieu]“š†ÅÐÌì'yÅr uóA ΀¸>ìyþØAÆum]ñ¹¬û6\›á$½&è—î £‰«ªI9CßO³?~ˆKv­…¼=ȶuëôq‹þ(!¹ùƒõt­×ÈoMÞTãcÜPViIïߊ·áû:¶æúIqˆuévÁÔdÑy*¦×Úìs´àl…®ë¿¤«\zY3”ñë¦|?GâJŽl6-:î»ÜuäG Ï\ ó‹~ù-×}Âú4=Ìã6â¨Y‡Àf~üî"úþbý§:/èÞ¿H5²+¬Ö+ÎQaßùW‹--¿³î÷×åÇqûËs J'± û‹tºË¶a¶çg)®#—Ùéô‡s[-m8„ñë¬5øñCܘMËΕ Ib%ÜŸáÒý"¢¤¯î&ž%n•E,Æ0ãû@òý5ˆãç‹I¬AÇOß{xä"59|i࢘³4"¨ÚÖ/ÞFëô!Œÿ¾Éø¿?Ä5¬öƒ§ñ$&)ÝýÆ“‚‹´üÃ?{«žö•DRø‰×†’%TÂ}™OøñCœo<·q#™5ÜþÕ‚Í2¨ÔÒ£ëÇ¥ž¡À¶½±È×~¿½·Û¥ær¾§qÖWc¾ÎŸžÌŽm+ùÁVmµê -õò«3Âu~”ð<1a}–?Äíº=}Ë“ÉlÓ¾¡Í«oÈ vUfÿX¡þ™wó—ÎÜmpqGV×ò£ºõ(0~÷òSqn*og{¾Xêʸ”A±kk\o‘ušâ¾¬­¼Ö>оµžÜôË…¬ ŠKüqRÄ5KïÖqûP;«v²eÚÆâ.rßÇmŠ;M}_#‰ßo'güz~×@œq7‡rkíì{î¶jM-­=¥ÚOOSD^zó#„뱦,=);ǨéˆÓ Î={úO¯ÛYÏÙÚ›©˜'Ý[Ü¡qÜýSdã–Lᴥዙ+ËWúÙâ ˆ‹/ûÃ⾟f§o×ÚÏEŽzóËîªÝ…ë%¾±q¿Öú¢öËv¡Þì`ßUuÛ’x«ÎIaY•¨0»âÔ›ý„ýœÝøñCÜ×µ¸9GXŸe·Î-pQÒÔ†õj^:Aû¥i–(1ï ô3HK3¢Þv*«äÇqACöGÖeiþgU«”ˤ(WÔ°§³OPa_ï»?Õ~0è“/iã7ù]§/êÃâ,76Žïq”ÍOœ/-Ù “Ú&wS/mp‚3>[ZªeLáó.É=!ôçF¿Ïê(kU<èÜÆÎ™´²×ÄÖsÎ9ˆŸ¯Ä?ß žâû{—nŽk{qÛQ6UzgÇå!™TêIó¹9Z‡°Ï#æÝº`ߘú¥Nò}˃ä+Zv¿¾Pzñ({²h{­ÑºLÚyëþœ5œ4ïßS!FØçÀRÄ5ÜàJac|eS­o£»'x’Þ­[Wíð•já-ñÏÓðßSâøõžÆ=ý°æD6u8¼ªb¹ÉIdYT=Ù¼i8-_¶+>tôhZÃmäãã4ˆã_¹²8……‰Ú?x{=›î-_ú¹Xù]üT_'”ZŸê5”}¯^—3 ®JàÆq Û:?­ÉØ—(py½ð­Á§]ë‹0ZU.çmûtÌG{ôYX:ÿûY7¼w‡1…«fisIv‰6 oå®UòõåÚq ·aÆÓÛžà?Oâ6j¸'îR„õúKÔ¬ú-Ãø¯’Ôç:ùm=Ï}t¾ i4 ÷UøñC\µJò¼÷SXõf%§[Ç]¢¹¿$Ïœ}íºå@zÝ(á>lláþ~üå+ø÷sŒ¶‹Å#.zfñ„vhÞÇÑWÞ¬EüõClá>"~ü7ÞÿÓ”´rÇŸ×/Q±¦kZUúv?=^êÝ8ui4iöö,íJŠ¥½å ö]CÂùñC\ ]U:ÆlÅÊÿz>ïe–ß·yQf"eï$ì£'¬÷óq ÄÊ·‰þ¸àËä7¬’Kë.[Ø~…î=»üš.L˜WŒ£¬ôð«ø8 â¶$÷l:Âè»#ÕäÒ‹¿îy¯òìò:«Ÿ¾µi÷ÔÃ_¥kdÉ´¥Ü'WÅóÄ•©µí›É³³Üqª—{6äR¿­ƒ®/·–„û?ÔÎóø”è—‰Âs+|œqm®5³O<]=éx.}Y+®¿¤½™Ž?k§Ý×e¤0?™ô¯¨Üc{ílv›õ­ÚýœK'οz#빚ÛBªkˆ_g˜ônÝ00~ˆÃ—¼‚«‡¿®rÓšUsFVh¶’®íá  §ÀmÅ*“ÈÐëv«/üý!®Â£®Ñ—“YÈõ­ëNKÝô¬ï®VnÅ“RõJ297‚šÔšH!“ŸP}ËÇi—ÿ¨vÖ„°dv£"·ÐMÇ.£ÀcŸGѯ»k½'R‹Ÿ_Ø®YGQƒ¢³%‹ÎK *:/©è¼¤¿ÏÙ’…çÝj…ï÷ùq¯¡ØÁ2$, ˜Á"$/èÁÞëƒ[Øë­°î_Õ¿ä?íõÆ%Ó`Ђåox~’ ¼ ­üÏî‹«Óßðü$ ¸AŒâ§ØÁ2C ˜Á"FèÁ^¢PªÁN¡hÊAVð€ETFp€‚QTµ`7ˆQ`•`;ø@†‚«3¸@„â«=ØÀ Rc5˜À)f¹p~’ùwgrÿ®çÛŸÕ÷ÏìùVý/œ+ý•ó¤ÿdŽT87*šÍ‹AÿìyQá|ˆ›ýç@\.Ñ ãÌ}Ü¿¡#8ÀÁHDZ°€ÄHJJ0€ýúÝr=Ü {ÝþýHþ“Þmr$F¸AŒ©ØÁ2$L ˜Á"$OèÁ^"™ªÁN!±ÊAVð€‰VFp€‚‘xµ`7ˆ‘„•`;øþ zÛjÀ N¡ ÈAVð€FFp€‚Qp´`7ˆQ|”`;ø@†b¤3¸@„¤=ØÀ R*5˜À)-9èÀ  ˆ©ÀðC0Šš,à1 œ `È0ÇÑ€\ BT€là) ¢LàŠ£t`HP,U`ø!ÅS pƒ…T °ƒd(¬0ƒ DÂ9‘ÜYÚÚ?±Ÿí_ÑŸ›ŸÔ+Z*šûýÿûpóž¢9OÑZÐ̓¸\¡Æ–ûý¹ŸQƒ œB’’ƒ¬à ’– Œàøƒ^¶îëeûWôùO{³q½lEH¢ Ѓ< ARUà‡`$Y-XÀ b$\%À>!kÀ .!+@6ð‚ÉY &p ‰Z:°‚$HÜ*0‚ü•þÙ½lµà ô`/HQˆÔ`§P”ä +x@‚"¥#8ÀÁ(ZZ°€Ä(`J0€| CAÓ€\ BqS€là)ŠLà Ÿt`HPU`ø!…Q pƒER °ƒd(š0ƒ D(  Ѓ ¼ EAUƒ œBq•ƒ¬à Š­ Œà?£øjÁn£+@Vá mÑïú²ý™}lÿоlRnè‹æIEó¤ ¢5¢¢ùÒßk¾ÄýÍ„±ã~?þ›ÌàâÞ’•ô`/H‘¼Ô`çô²õü[/Û¿¢ïÈÚ›ëe+F2U‚là)’«Là­t`HxU`ø!‰X pƒIY °ƒdHÒ0ƒ DHØ Ðƒ ¼ EWƒ œB2ÿ'÷²•ƒ,à1 ‘ `ÈP˜4`ˆP¤ xAŠ¢¥8…&XÁ|¹U`ø!N pƒÅN °ƒd(~0ƒ D(„ Ѓ ¼ EaTƒ œB‘”ƒ¬à Ц Œà?£ˆjÁn£ *Ávð VfpÅVz°¤(¾j0S(ÄrÐ< AaV‚làzögû3ûÙþýÙ¸ùFQ?Û¢y“!¨hÞôß0oú¿2g’ ß9‡ðÞƒñß´`7÷šHTJ0€| CâÒ€\ÐÏÖûoýlàçö!ÉiÁn#á)Ávð PfpÉPz°¤ÿ½Ú¸~¶$RÁ>!±jÀ .!É*@6ð‚IW &p X:°‚$HÈ*0‚üŒ­ ¸AŒd­ØÁ2$o ˜Á¢x?[èÁ  ©ÀðC0Š’,à1 ” `ÈP°4`ˆP¼ xAŠb¦8…Â&XÁ:Á~FáÓ‚Ü FT‚ìàŠ¢Ìà ¤ô`/HQ0Õ`§P<å +x@‚bª#8ÀÁ(®Z°€Ä(´J0€| CáÕ€\ BV€là)в àÉïzµý™ýlÿŠ~mŠ ¢u¦¢ùRÑ|Éôß1_ú¿´Î¤¾WNáýÉAVðpÿ&’• Œà?#yiÁî?èmëû]o[8…$'XÁ$=Á~FÔ‚Ü FBT‚ìà¤Là’¥t`H¡hjÀ .¡€*@6ð‚U &p ÅU:°‚$(¶*0‚üŒâ« ¸AŒB¬ØÁ2f5Á>¢P«Àø'ö¸µ‚$(ø*0‚üŒ €,à1&J0€| Ãä€{ZÙLÿÚ×–«÷Eó¦¢y“!¨hÞT4oú{Í›ÔÂ÷†ûl¹×W€làåb¬Ô`§¸ä +x@‚D¦#8À2$6 ˜Á"$9èÁ^"é©ÁN!ÊAVð€ QFp€‚‘ 5`ˆ, xAŠä©8Á2$S ˜Á)$V9èÀ  ѪÀðC0¯,à1’° `È”5`ˆ  xAŠ„­8…ä-XÁ$sÁ~FrׂÜ F¢W‚ìà¿ÌàŠ€ô`/HQÔ`§P ä +x@‚‚¡#8ÀÁ( Z°€Ä(&J0€| “âõÁ .¡Ð(@6ð‚…GF°ƒd(D0ƒ D(J Ѓ ¼ E‘Rƒ œBÁ’ƒ¬à ˜ Œà?£ iÁn£¸)Ávð ÅNfp…Oz°¤(„j0S(ŠrÐ< A‘Tà‡`M-XÀ bP%À>¡ jÀ .¡¸*@6ð‚ÅV &p …W:°‚$(Ä*0‚üŒÂ¬8À2j5˜À~îY7n-XÀ bq%À>¡¨kÀ .¡À+@6ð‚_ &p Å_:°‚$˜ ¨Àðw’›,ÄJð¿ó&MÐÿþïúA¡ _h~;‰½Úñ0«o„†¦ôZù´D˜› ÿÿÄôýæ\X(œ£ÖÖ·Õ‡­*=J8磎/«5©ðüVÚT¬ úÒÌQïâ¹×±àuN,i¬¾>6‰µÔ¤j:ËMW~²¹;s)N­Ã¿8Šj©w4«Õ$ÒÖËíÛxîqqgûlé;Ä^ý\¾ÒÎunº9.é}6dIE¦Í‘%GÑÊùûöMÝ1‘VêûÐnåÏ­ð ®UûC­ûŒ;ÄœaÃ.5:ì¦èSs+½7…ªê¸J£hk3®ÝRV»šSy£pžÇÓ|Eç,OR’ï ;qNsfGŽ›NåŸ\‘2/еuv9‚žÏÈûêJì»ó/¹0)âîõø\ùøƒÌ¹jzåyÜ×vò=[óÖT®oÞí’SéÔ§÷Þ»M5©ž¿Oâ6&'æÅÿv€•Ù?êù‘2y$ž»oãçBY³}#&¬Û¡¡3!/JÅ$GS¿µ%z>âã4ˆëפژÙ1Xÿ‹ª«æQXî¢xCÓ ¬$w¬Ö mJã&ÅÒÈÜî²? çy NùjQÛ7ö³—Ò´¸…”Gi}F¿nVLÏÄ_ô¸FáùbÞÓ?Ä•útïìܘýlúâoŸÕPçÑ™PI·a&ô×&/w\ü†IÔ9i”choá<ĶV.æF"kÛ£Ò›2òhtÝs-ï=Ç„sX…ó§{ÈÂýÉÂyˆãº*ïOd¾W[Š¿žŸG³o,©râÐB¦³µkY¿Q¤p¾øáœ>᥈ ÃaÎÍ£¯ª»1žžôô¢vW¹¾eXŠ“©ËÅ6á/Sµüø!îü‰±ï•IÛÇÎìÚ=nѹoÚxÚâNo‹'þ}Z×èYÇŠ¼ØÍþ‡½÷ŽŠbÛÚ½ È6`@0cÆŒãl3f̘(i@’(¶ÃFÄæÆFÄ„‰Õä&i$« (¢Û€ó}ªWµwï1î?ç¾÷=ß¹ßuñg U]³j͹V­šÏÁ¹×rk¦íìå~öýÖ0ÜÓó‰÷§]A&öìµï»:ÞGð›ÙhÔƒZ¦…4íéAÿ'j}:Ä>Z®¢‚ØOG¸žcéµëy– »²Óè[ÿBª¥,yP4ì0ã×ÏZìû&%×[ªù§wñ8èT=‘8•üµtÁVµ¬e‹“ IwÙÁ¡]ްQ/¯ôkkMíÌs˜ßp£z1æû»˜ˆý »÷¾¡Ã~ÿv¹ƒ`8UHÃÝ®¹žh+gZ?/î#ëN÷M…†Ü¼?‘ºµ°íÃBâ}/O°÷ñú%™Iv´\ÚöËÕ¿úikâÝxÈ Æû RÌQæ¹$û$k3Kpä´'ÿñþSÎyДŠÞÜà×Eçó ÷[>ÁiŒ"zgôêÔI½Ó¬ƒ`×ÜËZ:4zЀeóö˜r)tólwnݳè8[Þúí¾³­‹(³‘ºÆ¼þáLÛ—Šå¶qóøÕ¿K?è4—kÏ1–1_ýÖºOÍŠØéìÁ>¹,Rj/ö]s§Xé»ÍIbü »=W0` cƒuJ/ØŒ."ÝiSÏY†‰¾{´ðJòÏ*¥¿ú“kâÐ ©÷c9‹8.BÑùYë®&eSÌÿ¶|Š-2¯×£¥Ç šØÂ×4ÊRìGݸüë»*²e[Æ·[„ól¢n²%ðKÜÙË»{›…tȸSLáÔe4fFt=úd?[àmŸ»½ˆ|=Ößr‘u”þµâÈs[z¤þcwÄz7júsXYÜ:±Ÿt‡¾¯s/Š ašô½·ˆ†¼^š2eJ$ÓöM;0rÚv#~7öŸÞ†÷9”@—»sÒÛvþ{YÕ”VêÃE”°ëœ‹IY${îm·ôÚ[{ÂÍîjÐÕ“Ì:ÃÏåñƒÎ°mS÷eCw3¡»xƒSEt¨l€zòšK¬SÀÍú’%ôxÑŒ3÷<ÿÑP]‚_½nc•AìØpƒ##.ÑÕA{¾•™D1î§ç úÅzÒ±ÁÀŠOääø=wNî`']úè}­ˆ¬vÌ‘DE1m?dÐ×étë•42uÊ™5ƒÄ>ŽÐ¶î§ïqk [Õ\èøZD]Š2ÇéX^f¢ÿˆË´Á¸<¿žjè¶f °ž5s9j²ˆìvX6ž›{™iûKµL»õ¼Ú;©Ø‡—÷ÔùúDÂû®b1‡ .¨Š(_:öÕÑW˜¦­²§- ßaHXž+9ïºyš÷4…NüsÆýL‹È4 tÔ™‡W˜¶O¥è«ñ«Ÿ¤&~Ði}6ŽŸŸ]ZD»:˜x¬žs•µÒ4ø³¡É°ÖÒí}m¡ûUSIÇã©æ¯Š¨ã©ÈO[²®2íýñöÙ¥îzwÝÅ:†ÇAàJ˜W ¥ð¾»jWÑË|‡56¯ýzžë¥ ÞÒ{¥ØG–÷ù”CÇýžü¨FÅžuzÅÔ×hè„¿R¯1­ÑÉÂÓe½ôzH[„vží‹éZ½z;nŽfƶ-“ë;‰¾ˆ^T_h[Ÿ)t‡–¯˜8Å:˜ßYâ׳˜Ú^·+¤ß Æ}ãQŠð˜˜ySÒ²”3úxŸK tQ‚Ío‹½4\cø[LO–n:úêí ¶xð©W©=\È}Ë$Çr]o±?µØºR]•Ù‚/!¤~KÏF£‹)Û²²«ßõ›Œ÷ít!M[Ý=^ÿèû+ƒ®êûAŒt(,ïhÂÓÉÅT;ÌÌsÅÚ[Œû͹Ш¸O?j¾ô$žŸxŸa9tõ>;×|³î0 Ý=Mf“âõ ¯³#n3'×ÎO·ßqû߯缯:Mû`#9MHqo8~!ÎóáàÄ_o3mßÉêso®_žà!úlpºÄÕ[ž_F[QÇbj½gøÏÉ'î0ïM6-p¤~Ùw&?Os'Ñï€ÇïûIïy±cÄëóbšRZµá1Ìçux¯6åö4×yõÂùgÜE?‚)<~Ðy-w½Þ¾Ö Ê\ÐÐytN ã}fíhiÞÆ+Iršedï•Ò Šè9÷ÿTýr;¿X–…Ñ®õò]Éö¾ð¡eý.>?×‘Ç :ˇ÷„u¹ úfSè:WÚ¤Œe—úlº»ô…=Å„U ²ð!u×›´êËãöCë»w‘NEŸyq«m1gÕß¹¸a+ìLì)¤yOë/R©(6qïo ÝŒ$v/Z)ö}.¦‚}Õœ;ωc­{­ZÖÕŽjEÁŒÆ“×½æ¹N]í׃[6¿D&¿ò63À¸wD¿tÈ¡8Æû6ÚRѦòA^ÑÖ¼o¹º»ß–Ô3L¿$úwQRjüáEÅqÌÏùÊÌqŽ ih—¯ <©õ‡¼®Ý¢{òøA7îÑÔFk¢¨A³Ø¤¯‹èØâÇ“G5‹g¢¿©Ø_Ò‹¬¬ž9£tõ*Ókô¸LWÛöÇEôØ4oÈ÷YñÌåóTg_z%9ü¶bŒq_fÞ^ççIÎ~?˪‡—i–]E‚" ‘[»ÕÞxv~™PH.¤vKëžüØv•Øó»ÆgÂ:r_šµ|ëê¸üÌ«]E”UÐhÅÈÜxÆûxÛÒ÷žyÇj¸ø‰}I;0Mü {á€[ú*M}¯H¶M,¢…mLÎ=hžÀVÛîò¨=µlWµ¸ØP å¿ê¡ÑÙBÒ,ìhDÞUŠN ï½ãV¹}i]ã½sãþÍ´øÜ÷Ã/3ýÄçBôS€nðö‡2¿ktZ÷•ï+ET5ÍGõâfûI“CÍ8Ñ9e…¬ý ?\Ëë-ÒÓèäÐi“ë¤ã·1óÎS°ÕiœÈ&½ œ¾²½³è—ºZ¼.-yü 3³/ýÒíëôúL­ô‘¨«>—–_üË=‘Ù˜G—þp¦¯Zö똰Z|þLyü »õÚ¤Õ×9Ñt|¯ßº9aE´ýlŸ#Ù‰là¡Ï÷[˜ºP0™ø¼ºšòŒúÄm0Ôœ§ŽN¹dòþƒ®£>ESð]…2í@íýqztŸ$&úúÐþ·m›ÝY%ú¦ÐèL¡¢dxƒ<ÂÞ^|‚zóX\r‹IŒûÿ."ýcÒ_¼ó¡¿û H «ý³­Kzû›d_8àÓìEäQ–î4§<‰½½Vgf/'z>\èï-öÍÍãÝvÁÞãÖMÑß±ˆtò*Š-’™v=©Ï’˜ñü¼E_©<~ÐÅ;ž±1í­è+».Y_DkÙÌR>iã@gÛGÕêæ#öÆã毗ޢý·öÕ_UDÞ vÞÈKfÛÛJÌ£Ž¾~É—¸ïPNÝÞœÚ.m—ݦ„ç?t+¢Íͺ,•tV2î/åHfGþéå'֤ѩ¡ã~ß·©«‰ð/àzâjìõW2mßaMšÜ½†zjŽòß§S­\Òò‰mÿj›ï»?Èm MMQÞ-ÊW2žG—›>í#÷ýDùõ4…n¹{Õ‰FµcÈþó»å±ÓŠèT^l‹Ð)Œo.ÔyøÚ°±;üIp7l°Ý’Ǻ;?w6ê¸#†$š }œpÓÛŸÂ<ÏM[u*ÄEô¿\£õ›àñƒ®îŸÖ[Ôg4·ka‹§ý‹¨8ifói_SبÓOX[¹PPo¡¢Y#úÞòãÉ Ó}ÿõGÆvFDz߭èfVDy{79µ—Êx¼©Å¨¥£–­þÇï“Cgº`¦ú Ñ_¼ˆ.6íb¸åZ*3OpÊZDïÊzv¾{µ8ßýh ‹žÿ=¿=)hÛ°ÖN 5ŠhõOóé'ꤱò‚‘·#=.[û‰ý–¹N Ý™n·ÌV)hSæ9Œ|…túÜ„ÞÆ Ò˜{“Wâ:Pj‚`¼±êσŽn¹dx·ƒÛ_UдիGx=,¤«ãg®ížÆ2;S{âóÜUd;]¹wÕ?èò1ê·®Ti§‰&©…âx—ÆôFüU’6ÊŽ 0«i¸h9¶T=°:ƒº§É‚l,u6}¹Vvµª©ó/?íθï¯-uÑ«uee ?ê~Åã@Bî[e ]AȲ!ùö±d[mbS y!ݨ>xN¢W:ÓöcOÛð¾ñÀQ«éïþS2è¤C¹XZòå…ì¯m…Ô'ðÅÉ·Ò™ÖŸÉOÓ`ÛŸ’¶5›_ÉýíäÐÕ x/?–._¸ê«G!õxöåŽ^K»=3/ì€=•ï6Š¿_MFÅr¡€â¾c è46 â(z°°PHGsÛÅ«­2˜vAp‰ík+ûsº EÝÜG]5ú É(FßQq<ƒu”ÖmÄ%'š™T°²ºŒ?šògÈ ÑOO¯\Òiѓԭq$½t¬f¶y!éV[]­Ö &ú›‘° 0ä“?]–ܹ­×™BÇœ&‡$&Ä‘f™Ð¤ÌÛÕLkks—]¨ìaüæ° wù9׿†¿¶žæñƒîR¿y¾)?ãHŽê=ògz¼j^=î.+‹mrkͬÅÔ¶ßKûÏü){rÆ“š#ÅøAgÙïŧ„AñÔFó]÷² “:Ýc ¿V·ÚÑm1–'½ßØÁŸºœÚái÷É’A÷¨ï/…g<{-PwÌ~l»÷k~¢kß°Ç㫉?§Óyü óê[ïíÈxZ˜½aý´+4QÓ ùÞ¯ùPäEψ_|I\äñƒN-¿óŧ"žÆ•Ùú@YçE%=7U1îêDÜÇÒ—¦å~aÄýÕнòæPwÓ‘9D6Ú¿€zÄ UOP‰~}N´­"/wÊO_ÊlÕóDüHÑϲz¹DxÚKg'ˆu|-t\0@ÇKŸߪ­l²û‘o-Ÿ¬8'ÑO:¡JÜ·3úô«ý0hLu êÛÈY®bß v6ÞeçDž-ÏtN„<}±ÖîÒ2~]$н\sÆ nBíÚ‡w. 9·‚·ê¤«˜Ð¼e³ETøç°r=Ý5”·y›ÛŽÜ‡ÏºoMž‹ýš úÍÐØ GV1Ÿ{ºLý¸ˆJ:Wf ýµÓ¼Žµ;?Ot¦ ‚‰ôÕMóWùôÀê©$±S&ãóWg’Õ 0œ}×_ëËãÝ‹Áp$‘Îè›ûãn>Ùfü0`v&›ŸúÙþu¢3}è!4¤_KÛô*>}?8›Ç:O‡Í=”HuÚÝ p1ŸN}›;éÏÍ™¬zZƒÞAÎΤ)oÎúÓÖQaNˆO ÷ùN¤h2®Ûtg>-‰Ý³þJ&Û~F0ZD}ß=> » ñyøüÕ(—è£JZ­ŸDŽóÝÜf¬È§²¸uAÝÔ™¿â~·Eð\³ý«)sxˆ´¢;¿¯M¡s³xwv $‰†Úm­µgR>mx=öà{ý,öÇþ®–lp 'u[º¾õý•'ðøAW%Mq;½2‰ìš Ž=ùôfOk]ëY¬yÌ&ϲv´`…yŽÝ&?*l1ZòãÙBçqt€QDÓüäÓý…K¦[eiýiª¹ƒA®… ³ÒÖõøï“AÇ}’hÙ° uÊòÈ""ÞIêšÅøø¼€ê­k·®¦Í’½îïzˆß/rè4¶‘FÉtç‘Kr]–GšiãŽ,ÆFwšpþÓzjOor?_Oó_óûZ¥ñÁYë”ôhÉáèsétª`(ŸÍ´}÷¿Yã1ÞO¬¯ùs$‡nÌ‹F’^QJòS qLÝ’Kîki̶àlöòëŸÎ8ÑÓ?wÇÝNö¥½TW{ÊÅçºQš½Jšæx>,Ç&—O(<²$&›Íêþʼn.¿Þ¦^ÛÕW¬—D?RèŒ4…^ ͪ,2,ë—Ko{¸Ÿ1|žÍòGgêµÞàDnç?Føhý«xüj•K¶­¤Ó¦”BÝ÷Lœü²N.µJúécÐ8Gôu¤O:Búyþãþ4….`\tÐXi U·¸³ýSÙ}úy¶ƒ×à–ß]×až½ólú#»îJ¤=ç¶µ?è™´Hý0Ï6‡ÍÐ$";òñ3Ô}Ûa%íëxR^4Aô“…nÌþº½S”)ÔBx]tŸr"7ÎõÙÃLC,ÞJíHpÙ )ó¤ƒiî_+ƒî–AgŠø˜Bïæ©åí|Ÿúþè3pé©ÖUcàfG|}Ù›rº:5-µã]/¡M*Õ쀇ܧ¬¿jÌœ“šÃFo\Û ï"{²ûÖ÷U†«5¾u­s+1~еڦ»¦í”Tz Øs4ºO3?.hðíuÓú«txQ4nT•·èïÅujè^§íi°&•Z¦ZM–CyƵ—4¾ÿk~ËïWo1ˆùO¿\ôõM%óÛÁoÄäPTÁ®çƒé>ã×ß‘Zm‰ýfÔÛ›·å¦øñL¡›:Á£ëÆû©4òBÎeÓ=9t¡¦û)Î÷­q¿Uoѯ•ë$еýÓàÐ2½4b¹'ÇT8çP|yw—¥÷Ùåý ºd>jòÂ{Þ4D³°->Ðñõ‰4ªk|ãôÙÁ9¤×=zUÄåû,ðÜ·Þ&íìiÂÜ7u7Ýó¢›ùW¦é?ÇOè6Ö*ö4 =yø@ZÝzaÞ 5÷>Ü=›}°¥¾ÂrQCO*”Wøx-‡îËšŸå‰[Ó¨üIä×À’ljØc«õâO÷™Öß®¸›M·:»<(2yÔøË¾yü ›Þî}Ÿ7Ó¨ðŒ0±Ì¦367ñ7ÊeZ_ªyšBwÑOš×ujè”{ê¿_‘FNkŸì¹1›–Ußr{[¯\ÆýŸç‘!ýêóÒ]|ßΟ#Úå’kÂk“FéÔµ ë|nF6©¶§dfMÈe‚¸äÎñ=Ÿ‡èc+ƺ®ŸùOžNoÞ®¼`Ö!›Â÷9¸tuÊÕú„Sÿš‡ U¸“Ï€× çÄøAçñ¶m£Ê¥éôäz…cć,ŠÙpÔBæŸËx}?‡ttsÏ,hä.¾æ:[èòmp±;Îî9ò¬CRÕ=eWšËÌë + sI·~ÓÕfnZu?è®”m~4éz: Ùýؾ,šë"¬Ðä²~æïXç9ŸZý8pnj}7qÝ@|þ s Ø,?œú+Û·s΢1Ó·t<™ËúN\±á¶ ñõ]7zß×6èýbÑϺӗwÇN}‹ëÙHÈØYô$1{È®7¹Lë3ûxÌ®«›ÝÅ}âüºç÷üOÔÉ îC”E*÷ç?4Êc¢o-õmì};ÅÕîJõ†ÑwƒrI‹ïö2Í Õþ&m†fÒ™† ³CyLë{“ zþ¸NârâûøüȺ6O¿¯=džAÒ‘;ònŸÉ¤¸iѾÃlpbý ÝÍMŒÜeЗLijñêLZ;áä¨ëòŸ×Ø“à†T‘ãDší µE?gè¦éqfô¨ §y!•IãÛô Ž8Ƕ4‡ůyœ{¢Èž&õHR äþ¼2èõžÙgÏ l¹K+EÛL þ¡7õ2^jd³~úBZ¾<ÞLßF\Çä:9t,Û1©º))/mù¨¢Â­Oõ)Ïcz{ßà ϧxùÄã)‰Ö´ÄdXÛS£Äùtö·üd¯ÒT44`ú¡9Õò÷¯œK熬‹œ~n®èɯ‡€´Öó=c3èžòKGU$6þ4Ïg^K÷×I~7‡´ë»‚k»ÑT1ïýQ.ùl´4Z94ƒÞÜ,má©¢†‚±s¿|ö`ø‹‰aïæþò}ßKñ¸AWYÛ Ä}ÿp¼è³³wMÍÿµäFÙ$ÿí“i} yÜ ›Ò>tÉ¢îd¬1RÑíµýÚ»æ3íûNí{‘£­µ—T‰ã&tÝ÷µý<÷×tëù±ç÷hÕõx)û3Ÿñõ.ñ~´%ÁåêÈ]qÞÝkÁþ½IMŒ˜Ô+ø}öNöH>—Ïø8ióËgNc?Wɯ¿ºŒó†³jgгÎîÑïQµ3ºþHÍg+>'¹, ÝQ˹âº=¿ŸÐk}ðñØoé´ùLèx*¹K ί°šõ4ŸùtQy:er¸Ûçc‡yt\/8Òµש¡Óí¡Ü÷ä¯tjß•ÕI ¸K^'Ž^[¦õa¦~k<°É²¦sá±}ë´æ::å’ÁÒ»YÓ©Åja'Ý]rÚ°íð(#awHýQ³¨Ä7"?4o*ñú\ôá†n—ÿ×AÒ»éÔk“½9ôðóÎ}—ϳÓ)DohÎ5»QÄßóóûYÝÂÎi†É7ÓiŒfÃ_uHÊ)_XFµ*˜ßïÞTjݨsF¹›Å?žs[è õWv\~&®·Û²eùOò{Ýxvͯ_ýÌZ+­o--Û—6æé7ždÐqßëtJUTë¾ 6~´96ToJ™ö¼èÕÐùîŇ҆¥¯*ÖÞç}ÐõÑlÔH§Ù«–÷ŸeŸN5ú­þã¯1eôlapjc+âþÆChgË¥ƒ’Äq:ý²Ón®é´,¤‹}5ÓIXm˜9¬Lô³ŸJü}zoú<9}U¯=âó]؆%ªyé4½M«­!§ÓhÚÕÕõ-}Ûgˆëñú¢_²8ï«[.)O•ªG¥“ºpkpeÅÄ[nk×¥LÜo7‹š×ÛèX­óÙgÚN'Ž›ÐumØ(§²;ÎS°!|–J_%“·*ÓúLÓÁŸ³[ÌêÌ*2JãÄuèÜælÊÐ1N§Ï“ìN®ß–Jû^Õ^¾Ç°ŒîÏÓ›×Áz>ùúÜ[÷D¿3“i6¸‰ë.ÐÔm29ë[ñõ’Tš\ò©Cfµ2 Ї!ц¸ÏsÆ}ûø: º;fÎx”Fk¬]s ²SHßß ¿ãÛÒ_û õúžk:Ž­ÑÛÝ|t.×É¡ª ÜØ4 öÞÔ§NÃîÇKKéöójï‚:/Ôú|2MùaÈÏS]Õ—¹!ÍÂÒH¨¾w7O¡¡½ÏÉ.¥gÈòþÇhÿ—ý}^«†î„ñÕ¯3eid¬1ÚRÒüOòK’ÄRÒ”YsI3kaÏæh^ÄqN=Ô;Âð7?®è,?;ÇYIÇMƒ£Kµu­èwêÈä˜5›_Ÿ?è:NíYÕ÷‰fÁ@I¥ìɬó¥ÔTc8“ʬã5'Ñ¿›ß/èæLfÐi”Õo·ëµÉÔtiû úÇKI²¤Q§OƒgÐUæ™}+Ù†/û.hÂïO[è>„ýñ|^y*I¯Ýè5Ô.™¿O -ýõ^ýÖÚn£¶(ìXSëä7#OòûLþžI7ân¥ÒÞCUu’Ix[wÿÏRš(ØVÿ5‹tÇêÖíïiƽؤ{mƒ¸î ]pÛÞçJÿD]­1.N¢]7¯y²®T|ŸbMÂn«›ŽóÙßׇÐy¯ ÖýÃ6•öµšôÁ1‰æu^ØõÛÊÒ_yïòè¬ï{-dý½ß³ùÛÅuèt ½?téJšm^’hÐåè¹ëK)M˜¦VŸGWVC$ßÄŸwúå’GæaJ%ÓŽ%#ZÅ&ÒåEl\¹¥¿ö‹ˆëKì±P¾½ã÷£N¡ÎƵìšHÆ?»__2©”š¬øl9~•5åŒì{sCþböf|ðÓ/Ãx]-nÓëïu ϦPÞ¦Ö‡¦´L¤éŽo/ç³DŸØÅì…yÇïw‹ë.ÐÕ÷/Õù²&…êõ²ywænü`òÁJµërâzĶàô®}©Ebü k+m¯²žB·}h[@õÚ<ó¸{) o†M¥@Éí #—²òê›qÝ:þ#…&½ÿlÖ®Gmý3þÌö¥¤y ¸l µPe-k·‚]ì8eΤ<®S@w¬}û¬þ?”´Q(§ÅÓ¾åoukU*î÷™H½4œ\ÿw5tW›, ;œ­çañtÀ¦cOãRâë5DÿoWq/ÆÏÏŸÆg]I¾sý±zt<¥”_bkXúkŸC–ÎCÝÚÛ\Ùª¤DzX~¿˜BÓ#~íÍuJÚm!Ì€ã(õö—–=k—R³%ƒÓŸOý¿]Ù…ü…^SZqº¹í»ºÌURJÙâÏëÎÆ‘YÈR¯qÕJI¼?ÈFØv1Ê• »HöŸ?è–yør·§’ÞÖèÞÞ6Žû¹´îZUBû =ÆÝ÷šN‚Kðî%®â|N|ïÝ´ûÕ§~ª¦¤c™-î6Ž#é*ã×É/K(ø™ê™ï€Y´%ààô)ãûgź:a–dœ“L«ç7ï’KÛ¿;}z\RòëùÛóy죋¥ŒÏ+¹NóÛŒþN%SÈö*d¸Xʸü²þëœê[d„Ì£{ÇK†K™]ìwY­šâ¼:õOÒ·dšu`Ïú=c©ýáâñÉ%¤Ùf3¾èk-e<Žüx: ÷©‡ë9M&! 4}¬ ‹¸ïEŠèÑW|> ½qJ²ÈÙMÜ—%®{Bwêêâek$SÏËI·B´(½QÌù3%ÔÐD|þ köùviÄØ$ºrJ_ºŠÑ9§ÕÏ6–ˆû¯fQm·'/gzýã~‘C'ìZ þ™HCWï7ñ<†^õ›À8Æ}ToÓÛ´Ù÷G”*76öa‚-}¹²í‡*݃ñ:M?¡{8·cüŒc|ßÀmº]þ¥Ì¾W i^cØ‘·e/ɱÚ+Ù´‘Íeuøýi MýމÉ(Ô$íý„„[|Wë$lÿëgO(ò÷ÖÑñ÷³ˆÏtß 5âiδWÃ’ÇÞ¢Üfª["Ö»v”·t••Õ6/³Y½ã X¿@w&OXŒ§.ÓÓ‡«2oR˜ð:諚ø¾C[ê.,»d{³â“ÿ°yÏß÷* ;&lÇoOñ·æ'xÛߤ×ßYþ¥&íz¸ˆ-Ü絤Çþ~Y E||æ“G'7ûoPÄtß}~Åê_¾Öú©Ë¿¶úàÍ„ÙÓìAbü•‹ûâhÃqï{o»A—ÜãÇÛÞUSôùîCzÍ£›Çöž;ã͸ÿ¹øÞ:þ¾/Žv„r™ÝæU‹Ÿ9%VMÙ<û?>:‡ú>Œ™‹7ã¾Èbý]*ã‡äF, =6¿‹¦æ¥õ¶º¢Ö®WÒØ§ß'¯ó×±Åùt¯^î{,[Kã_EnMŸM‰*ùì·§Õ¤ØÝ¤ÇÙÈ9ôPO0 ÷f³ê‡n™øYŒt«4^´ðU®uªESÞù5¨ÍÃø÷7Þlrç‹{U Äùtþß ›w;¤ •f#gNÌK_OG êņÿQÛdÁCþ<è4.—¤uýcƇmþ ‹£×hþ¦‡Ý\­ÕÔÎ(©ÉúŽÄßûz2ÙQ·‰¥øÞ:ͶÁÖ1´¨Ï–MÆ_£A®ú_"Gª}wµxÖµú¾ižìVÈ×Çf’Q<~ÐI º͘q‡=Lüêöõ*}µ %ÑÝÕÄÿýE”ú%#ae/vU*L4ÆòøA7Ë,ëû¡õ·).nÜ:u•œ3úÖa¬¦ŒÝK ½Qßý‡Ü™æÅxžáï©dÐ ã[þ$òU-xs`öUz­8©oTMM¨èõщæ nµÙ‹­œuõÄq~]äÐññõ&=¹S¿Ví«TÑø±s»¼GÚzœŒJwùøòf+m„6þÜ* S´eþuoR7Ͷ„+Գߚ~ñ »-öÕp ÿVLJ¼s÷a¼ÎŸ?èöùž½0²ß ²Ïûè5ÁýЏOàYõbðÚÝŽ¿ÿ°:ÃÝWÜ'Âu:F¨ T!à~>3®rsß.Wè¶×âx»Åhîú2ƒ±¶ä×`yrÎ_Æ×ÄñºOÉ~÷S7^§ÓóKô(½Lw#k›9î‘øÝÕBâû¥|xÜð÷3Z¹ÔØ|[¸ÜøÀeŠÚn½ª]çGÔïU†k¨‹-íEÖ^¼Ã‹Õ±v7ÚþˆÇÛºà ûW_θJcôwö´¾LmÖ¶NØXëÉîïjwxñýcžâ{X¾¯J]ÝGq7ß¼ºBÂìÚÈø2Õ{s%¬VÙÃ_þà·Û9\ý¼Ö“ñ}+mxÜ Óllr…Î5¸ã~ ­áÄvßzHÎŽA·ô§;ÒÌïѕ̓ñõ7èB~tˆ˜Øë2i^ψ"Ënwït ~Hü÷8ÒN«>'¶•z2þž­#tóg›®šEü{Š( {üôr—‡¤Ýç$ì>)üá)®×uãqkR.É‹‘ÙD{\¢{ «Ï°ˆ¢+‹&®ôâs—=éðÓÚ&çöÙèàŸ¯8?ž)tšÏÞ#in˜å'ƒ(jÐî‘N ÉCªé\ùi{=…Nþ«øw/qw;?èÖÊ‹¯½žz‘>||z¬MÙ%zc\ïQË÷¨Fx„ÓÒ5v$¼ÝmnéÍþ±_ºŠ^‚súy k4¢Ö‚¸K”TÒiñœ¬´;É3p[JÚRv-ÛKÌσxü »]ýô° Ûg©±æ„.ÑxåÔÔ«QèTN‡2“j±÷`øÉž,jÝÊK‹OJxü ÓØ`?Ž •Ÿƒ¾Nà% ›v~Ïò.®¼õlßòk|alɪ•ŒïÓàûÔÐi–)›†ÓÅ—Â@‰ôâõ÷õ~@|¿öí{ 1_öãñƒ®ÕCÛ#NÑŒä.'W]¢Õ:Ilæ< ‰®k{4ëmC?:­ýØÌƒñýt½yüŒË%šÏÖœ 9ÂD`Ð È·-iŸ±„·¸w¸3Ík±â>Aüý ÷‚ŠCGÑV³Ž®²¦HÅgu°Ó®£³FYÍ6LÖçûç$øû½„rr |²à~OÔD6êö‡bʱ^ÌÚÓ†0áÅ©ãu'¿î¶Ðzº5¯«>L ÷µÁõóë¯îr¯˜ž å~GêSòÅwº{ãRpµª=t|ýïÙLPÖzð1»¶þÔÉbq…ñ¼(ewKæ¾´ëÙž4ñ‚n‘æ¿}$¯êU~á²J7?á[,îÓ^Dæ–B%&e|ßrNÝûe³Ö\¸LÙ·Ë·LÁyÖkÞ\:±˜š8!£,"£ãŸWïëÆøw]}4:5t—_ŒCJ ¤t_aBv‰ _SÅÎÖÅôлÎÎïÝÑ›ÜÝ…nâü¾‰F§cR.¹÷¹s ÞÅÍÄ÷ÃEQÖûo&¿/úõý.ÿ>Ö™uÖTþ‘ö}†C'áIÆn•ë­ýÛ‹äßïE¢í©­UÀÉÆÈA>0Dâ±2 *ÙïžÚÿÛ=µ]€ä‹~·ÿUÿ6S$ok”b"·RÔÀõ PÌQ¹9ȆHþÂÛLˆþ[=$Ü:¿ß­ý®‹d:¿ë¢ßuÑÎÚ‘ð¼Ëĸ ¿Í¬šÐ÷ ÷P ç£+ô_Â} "A…®ÐÏJq³R^]è‹òßïsû¯z¶ Þ#æH]~{üê=b ‚€RL@@ ˜ !Y UÀ Ê„ÐG²’ *€)’—5J1‘Y)j`‚Äf€Ts$: ùÀIÏÈ@4¨fH‚¶ ¨€>¢ø€HPL‘ ­APŠÉÒHA8P$O+  ˜#™º9ȆH¬–@¢A%0C¢µ¡@ô‘t%ÀD‚ `Š$l ‚€RLÈ@ ÂZô¹ý¯úµ >·•À ÉÝ„ÐG¢— *€)¿5J±°RÔÀEÐ7(þV/ uÆïzéw½$Óù]/ý®—þsê% ñžSˆçnŽ?s©&ôîĵއÊRWè!‰Ø‚J`¦'ô2ĽT@¿ºÐS÷>ˆÿÛÕ«MD-€´®ð}>t@¿žðM:t TÔ¾†¥8àZ)7¾Åyc¶ „oqÞÀ²KCáÛ9œ70ÄàlÙHø† ç *Ycá["œ7P}#áÛD‚ #á ¥8¨[)7ö ãøä­@€‰°Çæô]š {rq|`ˆ` d T3$[ T@ÉA|@$¨¦HÖ (ÅÄa¤ ¨ ‰ PÌMq| ùÀIÆÈ@4¨fH:¶ ¨€>ø€HPL‘¬APŠÉɸ9ȆHV–@¢A%0Cò²¡@ô‘È$ÀD‚ `ŠÄf ‚€RLr@ ˜ éY UÀIÐÈA>0DB´2 *¤-* d)> TS$Ok”b"µRÔÀ‰Õ ¨æH´.@ò!’®%hP Ì„mA(P}$d ð‘ Bô·ý¯ú´ þ¶UÀÉÝÈA>0D¢·2 *¿-* "@|@$¨BçlÁM!(ÿV/It~×K¿ë¥ßõ’Bçw½ôŸT/YŠ÷”R<7 áÕ„þãø÷0PY]¡6~0ÇÀå¢'ôcÆõ†Ä,« }Pùoð¶u1ú¹áøÀ¥åB1T³:BŸ+¨€~]¡ßŽÂA>0Ä jYO諨fõ…þ.ÐÐ7úŒ@"A…¡Ðïç ‚€RŒ-€„7¾ÿÇycp¶„ïÐqÞÀƒµKcá{hbà¶4¾ÏÅñA%0k"|'ŠãÐ7¾_ÄñA$¨0¾£ÃñAPо‚ð¦ÂwE8>€ PÌ‘\€äC$K Ñ ˜!YØ‚P úHà"A0E"±A@)& á@ Ld¬@P€*`ޤãä "Yˆ•À É„ÐGr’)j`‚de€Ts$/ ùÀ‰ÌÈ@4¨fHl¶ ¨€>’œø€HPL‘ô¬APŠ ÐHA8P$D+  ˜#Aº9ȆH––@¢A%0Cò´¡@ô‘H%ÀD‚ `ŠÄj ‚€RL²@ ˜ éZ UÀIØÈA>0DB¶2 *E_[[ ”b²¶RÔÀÉÛ ¨æHæ.@.úÚ ‰ÝHA8P$z+  ˜#ñ»9Ȇ(, DƒJ`†¢@pG ª¿ÕKBžÿ½ûw½$Óù]/ý¿V/ý§ÖJÖâý"\SáØà"A…ð÷1HYƒ  , áz‚ŸމÌ T| ðû94—B}Äbp³¬)ôyÇý*Y-¡ß8ŽT@__è{ãƒHP¡/ô_ÆñAPŠƒ¢‚p¡/-ŽAÒ ü!ôGÅñ9M—:BŸNbµ¬+ô‹Ä¿]WèÇT+POè 0ÇëR_èO0Ä`ki(ôIÃyƒJ`Ö@è×…ó* ßPè…ó‘ ¢¡Ð¿ç ‚€R¤-€„7ú¹àø´­@€‘Ð_ÇæÄ]š}.p|`ˆÝÒXè¿€ãƒJ`f"ôÀñ è7¾GÇñA$¨¦ü­APЉÀHA8P$+  ˜#Q¸9ȆH–@¢A%0C±¡@ôMq|à"A0E‚±A@)& á@ L|¬@P€*`Ždää "1Y *€)•5J1iY)j`‚$f€Ts$5 ùÀ ÎÈ@4¨fHx¶ ¨€>’Ÿø€HPL‘ ­APЉÑHA8P$J+  ˜#qº9ȆH¢–@¢A%0CRµ¡@ô‘`%ÀD‚ `Š„k ‚€RL¾@ ˜ [ UÀÉÙ¨€>’´ø€HPL‘´­APŠ ÜHA8ÈúHè’ÿÁÞ@Eµu »0fÌ`Ä\1cFTf¨˜‹œ¡ÈE’$ˆ˜ÊŒÌPÌ`Ä€ ¢«ÈEÎY°P’3æ;w­}N{ÿûÝoô½£{üž1ž~û¼ÝsÏ¢ÖÚs­j>H 4"<\àÍPDÊ{ ĉDdˆ .þ$‘ ÍgDä¬ßHÙ?öIB…ÿøç °ÿe`=ÿ›óÉA…½/±>BxÀµ¼|Zþ}®Û²àûÊ™OKÚ? ê;À¸®%ã¢F´øÞÉ÷nñ«‰€z´[ã¹4<Ìs†k×yå2©ë¼-ܾø.è\6þ´³”:ž^×ó˜ó:8ê¹Y@ûA`\ô‘Цü(â¡cá×9*›{éî^¦ªœQÝvÜÐøÞÉŽP?ëÁå›ýÊpúò!H}¬–V œÉ‚—áݩOËm ;מYEH˜§ˆöƒÀ¸š[gR ®‘À;ß]õ@•Å>ºË[ûTÓ>²6¬o ‹À¸Þz»¬++¯“Ãéq×c`e¯6󒺔ÃÑWžÓú>³éK“ÜÅï,É÷w îÇf.£ý 0®ÆŽÑÝ$Ö†¼;wcÀu”ƒ»G]ë÷íÀ¼ñædç€+Ûû¬ ý 0®q[žö1ïhò‰k?6âœ)Z®²õQ´x=Bö_=`íjNžzø9%ÐTϧÏ>ã±ß|³±Äýì^¼ªÝÄCepçV¯×.8íbIfÍœc°¡ùe1nøÞ3©¾Ý"ƒäïÁ£]©›F»”1߈3x]œ0ߊT+L{š°’ù¹|rŸÓmòyÜTM^çX°›|ÿËÍ2HO{ÛvTg(ÙaX_oIúŸ>¶{ßZê#rù~ð2·ìºCòÞ8i` ;»M]Õ³¬Õo>ßœëlA¨ù1Îß?Íþ¤Ñ]â“ñdýˆ±~Ùã×ñúR ýN˜ßÍŒL,Hž#9dBÇã6†:%$‹!Vœ†£oˆ4ºÄ,•”2o¼=P¯  a}®éøaÜ1ßÇeëÞÇÚ·$lÅe­O•‚c}Ûci'ìZ½T²SÊ%ÐïS†q/_;zŽ¿Gæ~:iöÛ8è9±»©ýÚRæ'ÅùrLPÓAŸX?vaj ý>×óÛå>Ú¹t,±˜¼Ã÷œõ}øh¥©ýŸ^o;&+êd+çüœa~~¹ó5jÜtvþa\·—–õ«ÍãHð¤H¯ã…÷ÁÊÕKa’\íM¥VŒ–’”ÌÂûçÓ8>Æõéð]÷ôÈû$íÓ¶QC &³WóÐÎ¥­¾xÚÏt!é'×:Ò8!ƹÈ_Ý'ñ–æçîd=€Ï{}ÛU]´”ë' ¬ïõe‹1Žúºn/š”RW>?妘[%¬ï¦s«òœbTe¹)‹À¸…Âk^B߇ÄcÞ| çò‡`äZÜ|tk ´xi~Mâ>Ž3ã2?7Æ =ð‚ll<É>pĶ£C<,Èé6á€q ~;"vt•3pݨ†^Ô!Ô@=Û2Œ›»4TYPO>-?ÑqÁÇx¸£X²E­LafzH³ü+¹tëÑBÒyÐi›±™Ì¯>¤žßKù÷‚¼°Gdºý…Ý`ÇŠ‹³~ýÇz¢ÌûŽÐ†kZ°€ì J¸<{-õ¹ó0®¬ç­„ÄÅIãR®AÖc±´Þ¬º¸˜õ÷w€)ëÊ*j“n Esiã<¹¶ /wÒÓïÑ!ø‘@] -Þ_Ú¯i&ÙÔoéõ£Øøaõ§ržÓ/"0æ›2±ßW ä ì€ëBÚ»‹*Q7Õ]ÊÆã¦ì©ø5NBF~¬ä[M@¿ÙcÈ`âÖ>Š;R{kŸÚ9ZüàòñøCúN*k¶JȓӼ[Uó$ðú¼¡©ÖÒbøØ>qÅ-7!«Ks`ZpÏ×Í“éç”`ÜI…¤ •(T¨™¿K7³,ý‡Œ+†}ob"¾¦ ¡ÁÀÊø‹µ6ÄÞ¹9Wo%ý^d÷{Ó)u»é ä¾>'*’@»µßJ:ê=S9´Û»i]_4ñöe¦q CëùvòÆN 从tíŸa pbû„„°çEðá^û[ö¬O¼|›æèó‡Æ%mäÌÆ dÎô ­Ý`„æ…—ñEðsàâ-ÞY}½Ö>NòñÃ8ê O$eŸ»$ÝK€K‘/\ÁÌ¡·*]œØz¼Z|ÌòñøI\ØÑD2 ÁKGµm"Œ™kºd»KóJ9³¾ÜK`ØâÅ=ç_u ã‡q&ÛÆ¾M$ÏæLy•"Hý{gCgw‡×Û\àñ…)‹Œç³~ÿŽtü0.¬òÑ“1‹’í¿™*åJ‹`±ø„ŽPN©í®þq´øâäã7´Å×–Dô ’ò^&¤ÝIZ—* ¡*ïpøF P_Šó>‹èøa\q@ïõ…Ÿ’ˆòåCþ³“àñVÝMݯÂÉý «úMv†+UM_šó™×Ü•Žß0ÜO ©ê°$™8ØÞ~/ܓޚ«ÂGn)„ A™â;ްG¶i|ù-íÖÏ-?Œ»ÌéãO&¥ÆeFUI$<¯?Ú¢°¥?9Ðýüšª2¢×vú÷ñ1®yªþ¶¢7É„zé“áéùó ½¦²ºbßâ ‚{%œ0‰æbÜ’W^%BJêÿ/ÚŸ Uð°1¯G!è ~ªø•ÚÕ šC§ ÌáñD³+]éß'Ƹ½O ‹‡ìI!‹åB¬d˜·fŒ®Scœ ˜’½ú¦ ¸šýçê~+ ^w:~ç+€¤µ®¶¶¯ RÀ蕙 RÐêá,ÉÆ­€öûw£ã‡qòr:4•pš†Ô‡)[Zulÿá¶.8Ã+ÎÌt Úöt)h­×nîºê=&YCTÿõ'õgãÇ«çS_ÆqÛ¥ðTV//tÕ(€¿ßÞY™v_­[úaÑñø²âÚ±W_¤¹FG) ¼H©{qçHç­Q¾¤æ»F ’‡ï°fýliãš5 ·½“F¢‡~½^²+ &œíWÍ’7µ†~kâ ;#ÏÔd[€ß™Á‚·Ð8!ÆÑ¾giä]m¡ƒeG)¼oÖÝ·ár1žÐ>…¦ÌF¿O1Æeº¬ú$<(?Ýe>y‡dölÝ™-Þ_Ö7º×„9­lËÆã*Ú]—„§;U©vçtˆœH†|æõ؃§q¯òNæ&P0ó·®S?ŒÎݺãwg)¹õ-n¦ÑÞtxþÁÕv÷Ü|ÈNß³LrÓž®žH- Mìm]6~g£üÓkÔ)i³Spѹg´_6αy@>„5ŸœöU-Þn—â·ñ_è¼VP­ç·é÷íó|)¹Vë°fóÁ +k\íÖœ.5ºÕßBËúµÙ&ÎÏî;ÿ0N¾•êµÎ„’ 5_”äïïÑ}†Ë„ÛMciº¯=<;äÚ?Z'øg˜ÓÝäÇRRdߤþæd&\}¼ÞånPïîÏNú5-ÙÃd°Í26~gùÓ}m”l’´k§8, ž :÷̓†«Ü·‡ìöëº'ø°óãf¿ûó|[çtæOÊ‚ƒ×ñÝò ß…{.Ýq`ý¶í`ÛÖG»TgÓ¸Œëà¥w¾í˜t²=zàR³AÙp̸¬›·vÜØTê’óÝúÖ4o7µgþQ'Á8Úo4Üå–·Ù ×ôËk­ƒ¤KSUý>‡}NÆQ?m:™·©"rE§·oÌÏ…w?]ŒH[lWý|™¤z¤_QœÅοáX¯?îk<íŸN,bµßÄäÀ’“WÖ9úå‚ᨌjƒ—Ö}(íWOLJq—ô÷ÒÛ•NßENQ­ÏÎ"Ý«O.œ¾Óa[I’ lô*<óKÏŽù‚éüäc\YøÛ¹ïO¥3ÿc.PGÝtwi½.›¥0HIõ†?Œ»9œ3õ¤“'‰ nåÂÉFN´¸h”·)ují7Ÿ4–û‹VÑñøgk 'ÎINg×›yy.zÏ9ü~îší®¿Ù±ÅÃüÔ4.ãöº^Z^”Njîø{GÏʱpÿP› s4ÊNªt€/yZ&M•æÏ ŸS‚q•»¹ÿéDbÉÓ~樂Lºe³u×e³!¸Å3ké—HÇãºË&†F4§“™ÙsVäȃïõ¬ÇySÛ([9¿‹#ó Ûƒ|ù@ó)Œ¨çKÃ8¡x¹ªejy‹äg}h‘Nz˜üR°ˆ2q‚MÍßð ÕÔ¶hô£îIÇã¶ê/Î uvgK¦¿Ëƒ™’~ºúdÁ'Iš0%ʹµ¾Ï÷I;ù³ãcٖܳ|Ѽ "_ƇæƒS§¥ãW÷É‚žòÆè.à™ê<î§+œy~>e@?Œm²pÄÓ ²Dg¸çó•ù0O`}óÌÝLø¸³kÝ‹6¢Öuö§¥ùÄG}X„z/óA³¹0´­q&(¬¿´|O´ ¼ºÌm€\ ÓÚya}†yÑñøéÖâ¥{ÉYé380&¼–Ön™Öœ‘ž"¯#Yΰ?€Kä þûÛ”¿ª¢qŒ¢õBwKTÁM\Ÿ—/òᧃ8“ûbØœÜçG­RžÍÚîêåÑ)«hœ ãäí¦%¤3w3´Þ)¶%g—fÀ¶©º‡ùŽ >ñå¡'v˜t×ëíM:ÏFÖó»,»ì?¨(ƒô_Ðs}7“¸ì»éÄŸŸé@½1Ðÿƒâª vvÊåõÒ™4އqæž¿mýk3ˆQº‚ e_¬==Iûçítx»þÄÛ¼®ö°"oôΕšv°hžîù9Álü0®mþõÒOdLá´è¥vít•{:,Ì: yéó»9@7MÕÕy£ØøaÜ©]/t®ýÎ ßS¼ý !Î!Bµ`T:4/àDxv¬/º3̾+ x|†­7òÍÂaÛdã~Û­ûÌ+„µnàà]-…Ñ? Àk^ßÞ.®;,ÁžA±agѸŒKœùð%ÆUs…µ…úÃçGÝ!)Äîânp8°þö®po‘W\ȶþaœö}ϧçñsζK™Ü+ãqËßíÔ•B éÎ7!XwÓ6›f†»Â9®};ÿ0ŽëFݯ9ƒ˜'G'…}*ƒ©ïÆ x—ÖZw[ön½âU÷uaçߨz~OÇy1ß_fÀ1g†VN-‚‘{^:|:˜:…žµ}Ÿ»0ß½f]>Ø&b7ÍÇøÍZÆ,.Í )¾K¯ªúÁ!G—Aé“Ó`Týï8WÇò·ýªÊo0¯ˆJ†v=]Êß®u…î^tì×ÕcD‡’ÆÐóAa4^ÇmùѦg· ²Ý*ÊE¯÷wUø>|a2xïòc!‚0ý ±w0+»~À¸Ã[ËÛ«M'œEehu È-°ªIpðþ>»¨]®Ðr½ÏîßÒñÃ8zÿ)X™Íª2 üï¯IòˆKdwàìBÖß]ÀG5D¶0ƒÆE`\ãw­Á#qBï#•‚õëkYK'‚î˜#š½ÖzÀÙÅö‹ïÿðh]·åã‡qrýJ¿t²ÛÔ¼²OH)lš6Å:¸8¾iëuן®¯§(ðXqsKövó¥q2ŒwõEйWR²gßÚ!>ÒRè¹î¬ø0ÎèÛÎyôÕü”Vß½2ÊZüòòñSÏOQàvúRbSY91½Män?`SôV=þèJÖwÇÏçæºj{´ØœíyôÖYvþaÜ3ßø¶ù‡¤ä©áì7cg—AâU~0Ù,/ÿ%únpàdMÇõEà|&M-7žŽ;ãÆ{tŒãYJIÓʨ^N^e àoóX0Háå7ò:eŠ þfÆö â ñk¯Ì_µƒæbܯåjºU)Ù‘n”y¡ ‚Ïü@ ¨¸O¯ E.Ðfù¶î+o:B®ò·I'±óã>5?Z¾éKyk¡£ÕX^/F¹¯Ýð]{»ˆ?Çû6ìpÞžù‚é÷q#ïÿÜù©2(˜Þv¡T^“t·öy &! j; w‚‚©¥§Ííþ=ìúã²U£¶LO#Ü]¹þºå0ŒÓ¬z3$&7¬¡°÷Ñ@~9~Έ³›¶ìq¢ã‡qŽZ¼¼ãwÓHcEÿ¼®kÊáì”·v»³ãáîâ鳎üh¹O+jý;åã§VÏïz²Í$éÑ42ÓÑ_)Ë]“Óc'ÆÃZþô§»·öynñ©ÑñÃ8£A§‚7z¦qO›‡wkÊ¡äĩн{ÂÁÍ&Úïwd~OØ£Ï5v§Ÿ“q_b&ÍÐ4rŠkÃ=¨Oµì^û,Þt 8k㹕e¯õ>{‚6Vß°‡ô{bœX··š½rÙf'9xÕ¬DÞ¬‡ MŒ}º³™3|ò:öëA¡gë}ùøaõ¥® œ8ZoÍ×ßLÝxŸyK\ Úüá?OØppŽÇðilü0N¥CÂóʇ©„ö¯þZ×¢} q0ל§žÒUoJ¹´Gk¿vùøaœ\D›J¨—¹n6®»ÝéO,ðÍÖ3<.êçðh½ ?Œ£þ‰Tâ\ýÎͯݓݴgÇB¦Ø¬ò~±L5xåO9ÝH½¿¤0¶ž?|€¯Ç¼ù©Äæà¥x%Ã'm ]˜ŸÞ®|ê³j"ýœBŒ£~ûÒU~Cà Ìõ9ÜÿÓì»ðZöP­ø¸3óºBуîn$†Ý?ø=Óß÷hºB–ð—%nŸûjÊzœ½¸ãL0mR go£z:A8§#N¦Ÿ3ãLs¿d›B–­4{ÖÆé ÈuË·áÂ{ÃðdgX–Ô¤íC¼ÞÑç$2Œ³GÞ„´a&éÊë=!uÊÁy:ÙÞ­ë§|ü0N‡Ó*÷O&éÆ6šUÁ³õ z©,¼}šR«Öõ^?ÚÕäl÷iõ£ÉÇã6ks“ˆñŠuKrvU×u¾ÖT—«P«ÜÖ&OÀÅf›²ôáuq^–Àžkñ^&‘Î^=uø«Ø~5 ºÊo ¸Ã8…Ôi¢F/xüu–覻q#9-Jq™fÀÁª@b:`jÒ«ËðÞ‰{Rá Ѽl³éážP¨oRq6~7ÿù.^NRiÇï;kÕ”jèºe¸ÏK›Kp¿Ê;ùO¢ Ì’ÜÁòÝä>Ñôù˜ 㨿%‰<\˜¹y¨[5Tõò2üp:íï»0¹Ú©õ:ºÅS,¿ñx]|èûí»†I„»ûØët5PïæyøÚñ>qvn7µã î—äâ:úœ‹‡qô¾m"äR¨,É®†˜ÑcF>·?Ûr”¦Elu€ ê}¶kã>9†‚ÒñÃ8î*úìÖD¤ÿжCs5Hãæ4é,Œ´.ƒO‹ZÏwæ5£ã‡qgß±>Û3‘xr!†=…aâ¢Ñ+Þž3γ^xÄ:´x1Z=|òñÃ8Ü\ô¿9 1ÿ椾ð)|*>ÓÃß5ÄçÔÄ\r„WWø;öôcÏ!h\ÆÝ äÌï $îÀí«žÂëùj J '`ö¨Uï ‡Säî¨O~­ÞMùøaœ)ÿìi­¶ ä;· >þ†.4¬ÿê¬ò{ªWÙì)×·¨4V°ç·I½éɺ8 ™x;üÝåÔ§0{¤Å1Òp"jì#RE0U.xñƒer Ÿ ð:n‡FIЧ„L»œioÿñ))7O‹?&æ£ÎE5ºBˆg¼ökõ¿ÉÇ‹;?GTB|§=çö”A› |k÷‚‚÷ër¿ãnPS0¥aÀ_¨©œ|æ/ü -¾"B*ûq&s<ÞûöÇ™;@I½ý£ûn :""Qù‚/¨-jƒ[xú½1nhÊì—q‰ÕP÷…gË`LÏèÓÔ6A½k—í¦¸Ap„Û'7±/´ÓyU“©«OÇoBËù÷ˆoïh_¾L_2ìN9 |¡g»¶:¡j®lâ ô¹2}qí5Í4þO¦]üÛ^(£~ð1fp¾ä¥ÝÐFˆ¿ÝµcéŸÖ}¶|ü0.»í›c;Æ—qÉ#ÞûÊ`yÀuM7Ò?S«¼:Ît›ûk;ŒöºÐ8ÆmÜÒÏ¥ÛCÒ±üözë2hØË] x“Üõ*î韜€>÷ófžNêP˜XÏ·2ÍRôêù€üîÉípd{=Uªwm™6/^'¨Î ¾Ÿzh=y¹{ÞLý<ŒÛû¹û²U}î“ÅËI±Ñ2øQÖ»÷n‡­äУ%Ïã/8CõìÃgÇ]ò…¹ÁûÈêýàcœü±Ë8bØ ÿ~Sš ®)<õ¬ÖØE‚Î96DôþÊj0ªUòñ7›ŽÆå>¼¡®KÆ6ìyÔÿ© ÖŽi{&(„4ElŒÿ‰ëƒ\Ï|Ú`ߺƒFkÒñãþ¾›/´µÇÞ#Wom‹™òE®“_ÝéˆÜßú"å„;»>€X[-ó)Vóèøaœ\ÿ×'†ˆ2V@d×ð×ÐapøQr&:¹]Æ%èÎé.‡0ó¡`Ü{îM;ähÖ­ø«Ck`â¹'ö³NÔ*žÅø Ú©oÚžåÛÆÍX1ƒŽÆÝëú$·É1Cî…•x×.´"9=œ4ør†ö|ÍþéÁQ˜TÏ——¥£·Èö;–™Ž5лܾ׮ðrtÆå¾ùnpá²TõYýj(~yõjÛ :~¶›»P‰&ÜíE¨çú¸Î×ç#íso~Åë“-†Ãôªg¯†ƒ™›ôÈú½ð1Nýᘞ]cn|«ËŽ.©å?´ã¬“Γ_‚ä êt¿àÇÖm6~·¼’Ü.¸p,h"G^›Õ@]ÅÕ—ü)‰³æ¸7IêÎ0·×ä• »}>ŸDÇã&O/u½pñ y»ƒ¢xpMñÍžKdèF½Î]5œà‘JÆ®€¾¾ìy¥ ?Œ1ÙÝ~óÍHØ™»"¯¬ËÆ~¨¸LR–nîgõÈ‘=ïò…‘xµçú¬;?Œ;<5êlwÉ%’}D;&aW [rÕEöÿ¹¼³ûCGØ൲ێÕp™ïì’?§/?Œó5ûªQxžôk©Áñ˜òxhÛ_«¯ eg?/æË?î7\=r PoR:~“ëùC†˜ŽxXpŽÄì-Z7îr Ì{XzafÖU²p³w‚ý–Y¹Ý—¯eu¦-?ŒÛyéõ§­"ÈûÓí2•oã<;ësìÎÄë$µ^ëpÒg—£¡AW³—<¥q|ŒS|Ìǰ]8¸|RªäA (ýÜ>üÑþóñe¿ ¤k,¼½(rm‹/œŽÆ­”Ÿ€a¤øFg×} 5Ð^^`¢Éð¼•cB¬E°²îîåÚSÐîŽÎ· Óéøa\¯Ï[Vú}8H亴X,PM47¥¯ÿÒË~r(r üÓ+qáOß4Ÿ´‡½¯Q_íTºž¿Eâ¬÷÷p…¡r!ÜvÝ1‹ŽÆýÑ Q™+&•£\ûÖ@ aå± ¢ÛD½oÀµëÓ\[ÈI¸x ,?ÀÞ‡¡ž9õz~‰WAL`'Ü«©„³ÞÜ!©‚Ø_s]@­Pä;¥c¨t)ë±ðõ4yãÞ5p"Á5଼ûzÄ‹h«pÏ·îÜ]r¥ÑÜ´_˾ܘ§IÇǸ²32Îo…ìy#׿{W ËfôÄå/û ]Ç9õ!¯†·–Ü‹CÔo&Ä8ùãþ{àÙáûß~ªR}NCTv¾˜"Æ}ˆ\Ó>d5˜ý^omÄü{·g ·Ó?\õï÷¥V½²ö=xê)Ý»×ïÖ*/¼ÙèÏ®ËæÊã"0Nö¸Ç©içòýs.ný\ünƒF/ˆ%F~÷;Øs¸5°N.P¢Þ7 ÆÄ ÝþJ? ¿ÃÎoj jßÊ^V_bILÛÓ=Çe9óÒé¦uD=ј}ÆÉ0Nõ¥–BÔòS°?knej ñôÀ¢·ãwµR2\gLç~ÖÛº®ežÑñ›‚ßKîç z’6ƱáM \‘]úì¹æ>9XÕ/Àžç 7Ї~Ñi³î_ß'ãªop"Þs0ìQ–ï«èãòcá8„ú]aLu›Ów½×±vóŠîÙM§ã‡q9ƒZ¨¿òÇ€˜/xR»±¸nÇúï(ú-‚£\Ùi\ÞWÓqbœV–ͽð{—àÅÉéÝw××@’Bà÷¯¹I£õ?߸À”|^‡•ªk€ziœã¬üýÉŒ‰m;Á¥üZÌwt@¥Rp<ñ;%ÎTìã ê§µçÿi¿h}¦Ÿ3ãR#Ýßl¸r0éÂöÏkÀHÓìOƒú#bÉis;:ÂèÔ©¿z½`u‰úð$W||ÃÐó×€³žÁóÁeݧ?¤Há¾®ö?FÚÃG‹ÎU£| ƒ$°c÷_ô|a\{ãó¦í߀{I6j²€‹ï*–=&|—5oÖd !¯PŒWPþì:w6¿©õüC+<øÑðAíHÓ¶êüõÔ•)¥ÉéÏOg/é#ê'óîif¥„zõxW6á-L½¿6>¬¬éA;¾˜²8äÈŠŠ.BxØ´õc”ƒ?»¾¥óŒqFr×m8Ï \¿®´ôM?Í+'$⸰†5B`¼ÃgVpܰJÒ°S›ŽÆ]Ñ-·Ìøvêl„&j5ì~¹„ôÓ_5óÖ ;àîò \¾ò]Ô›)æþ¾§ç],¹ÅÇ.†ŸÈ¬_tkð—””/úu¶g¾ö X¾¬¿ýçEÔqã/ºíºuöé4”T–º}Í’HˆÙÆÜß_Æ;À;3C»SƒØ{´Ì[Šq Þ¾‹—í§+ÏŒ¯;‡È¢™=ˆÑÀ)wrAåÐo›.=ƒ@³mœy¼ó–bœ.§©?~"VÖ..ˆ©ó´e¿ú;$㬣»ìv‚«#o÷¼=!®Zè÷pžOÇoZ=ŸîGc{Jv³ny¿9Óöa1týn‰3Üät´š@ßã¤õ…‡qô~bpO‡Â£j ðȱißDbÐiè¶QIÎÀ½×o‚?¸Ë…h´žñ§µø°ïÃö·5ÆŸ«Ó©vªy'ªðÓÕ]êãåaÜë '¼)’À·3?«æ¸Õ@úÀ¦ÓɺKòç-t„¨rî°í”3@¿‚Æñ1Žkú̶Iú¾N tô»â~¾)™höðÍ.ôr×gCd‹»®êY¥_!Æ%Õ”¾J·ÏÅÆ›-j {Ý×á3ôRÈ€†Œ‡ áó²ÉS޾X*¯wxî™Ê¼Á·w»ªï÷ Dö¾^ Lí8e¼×¹P§÷ê¾sëþ…Þ—bçƵ‘‹F“€³ÑOÒ©“ë¥{æë§núyíe¯·bô*æíÆ8îîuTx\=(Õ’N­vÍÖi«ŒSÉÁŠ ¹Ïo9ÁÑ<ë^. V³ë2Z_dÇÝ­ — Y:s.›ój`æ±€”†¨T"ué>[ÙÌÖħ{å§û}/Îk…õüþE±Y/n'CÇEéWýºá¾î¸a\Ñ×T²õoj¨=Ô--+Õó…ÞMî#;'²õã¶DþP¸03Tp/ È `Ìþ[´ÒˆþëÛQí‹…Ð~õ [_ˆÜ)à';ÿ0޾7˜I…Ü ´Õó|V»1¼+Ð4ÿ L餽誅/˜ ÎùéÖƒÖ]!ÆRioÒcb*ÔÝ™ÛómŽ TñeO¤‘ó7¹YÖð'çKc÷¯>pûC¦p¯«Ÿ÷ž{Lz2Ò>Ü’ÄË f÷€3X›ÿò€ÕVl?çÿôZG`\Õüį¿;¦ÁÍ»¸1¿"Õ¹ö¥d~ÞÇΩV@½â>ðùã¥á}Siœãn8‡¿ßà“rø1L:Ò«ª‡HJôGF}¬jg¹Û ÞðO³ ãêë¢ì¯IÑ­>é¦[dÐîxóo»KRÂ~Í3•‡ôñ_ý‰Ãw§Óù©0³žŸŸñHrM …K ¸° ¼nßÞ󅔬>–l®<ÃfÉ>&)ÖøÀGé°ns¿Ò:ÁÃ8ú\H ¶Çc_jÉ@/@aXõtÒ¹oÂ…p°‡÷ÜòwÄr¦ÛýHW¡u‚qfòåÓÁ[. •Á7c¯¤ê tâ0¨í1ÐkÙŒ;GÛú”œ§ÛtÚ³óã¨'5ºžòh·†'õÁç4JÓImШŠ3Å ×Í{ÁÇzïë_¢©ï[Œq:]NëÜ(J‡ç©ë:ÊÀ½rOÒ'• ÒQþ`ϱõy1{”ŽÆö°ëá>ì½é§Ð¿qäÅ—¢ r°}Mµ"‡Ö÷U/ö}*hö û Æ•95¯œ%È€´­~\{ “wÉO½—AZ¼Ý›ß“µÚ8þë|aÜ…U.¶kC3àQdÕͶ럂~\Rf B&¹—Ñöɳ<; ïÙóßÒñ›UÏO1»d;¼:¼mN5»-z å3Ü5—-È$ró!ô’d?KI‚ó²„.…ô|ça\æ–]°[3îó¼|]ù)tê¬ÿ8b{&éžÛFãš­-Ä(ÖöZdó¯õq±¯•þD_Ì„²±&ÝŸTƒ~é&gI™ä¬äèm—q¶ìy§-P¯2/BŒ;xàNôå!Y0ºvëéì+Õ°Ò_šíÝ.‹¤Ÿzu‘ì˳T£¶ÛCãE{›EtÜÅ÷æôÜ E'²À¼êåìª!?ÝàÕ¯…Y䵿ö4­-v°cØ©•C®:ÂénÜDgõãèû#ÙPþŠû!N5\<Ýál|Hy5w{¬}…={ïЉ= óE‚q•?Œú ΆS®‰_M©†OÞm ]«²È쯔žO`P‰Q¿íÖŽÿÚ/É0.´tëâ÷ï³{«vzwŒ»ý$¸jb6IŸ0eû#p„ïv;R ^ÑððÒç4Ÿ‚F=àÅ¥å0oˆÇ­zb¿1›Äô0ÛªýÜð¢én’­»~ už‡qaîùmï^ϼMÜÀ*hv¹:«gVvëß'ߎô³‡.ŠiÖéÐñø›glŸ ÿÇ|.ÜT~?øËƒûäQËì-ê í åwÇê^Mýy™^ç1ÎÅéGh¬U.4÷+Ù·fQ¤X]_%0É!cÌ øÏºÛ²ëö{©)tü0î©»âþ̸\ ÷ «`ª|¡É!…íÔozZ2/½>Ð÷ÆÓñøÓGî>Ô%ÍÚ¡ +{>ã‹ô“sˆÉû…õÀ â_¶ù:v%h^9C¯;$§¤zãÔ£iyÖ—{Ãì üîxp°ñ‡|¾ã¤—oÙº €63b<®O¦ß‹ ã´Ã?4.4˃œUÕ>¥ï/¯<8—ô9ö¼oði#°â~îµIÀÞƒYLÇov=¿Ã¯íC§åÁ ñÈו‹ŸÀþ!ä’YòUF­÷uéï5èyÄøåFU#NæügIýŸ@ïùïU¹dÆ“™:ŸSŒZöÇìzšÎ>ÆM®É³œŸ›¦Ÿš:ëi%h¹YßU>žKκÜyécÌÞ“Òb¾yºŽ 1®ô[læÆ*ü^ˆN}FD%$O¾ê¼7%—h*é­Wk¥å¡›œ•&CøÉ™Ï§ëÒy&Æ8Ýäì(G…|øh6mªÔºÖïœiYÔ¶ÜuþDYÇ™AèãÅ…¢IPwoCÈ÷W4.ãÔ&¼ß›÷ƒß%_ëŸó[‡ï>7°÷t,Ø{ì³ÙóZw%GÏ×|¶NWÀèn÷u dÓ^¯ê·–-¿ï±ü†­»2Œ ^ÊÝ(ÈIçíŠÛ*€<µXµ¢ì{Œ)~î`/µ¹oc=ßé~BA³žÿÌÿ×–ªè|xÿi‰ù (\ÿ´s§¬Ð~cõhr¡%ûÝ…€Ðûƒtü0nwÈJiNe>LU­?xåM9Þ‡ìMÀoð$µÄ¹°üÑ*e•W–¤å÷=òñø¥!—‚¦þɇ‘6§sú^ĸÏ5=?ßj€;†¤^ ¦-¿+!tFÏ#!Æ=0ÃvÔ°àÞZ ´)‡‹WHŸ¢K pðÝõó’Œàᦠº;%väŸç‘ãÞyåÙl˜[áñÒö?–Ãó=_UÃ1îTÛ•Jã Zê ¡ûPú9#0ŽÖ(]qx›nEx¶Ÿ7Ùûp[O-ïoú~Ûj¶üޝtn®Üð28ÞÿaYϽ ’†;Iý–ß’…£üÛžP¤ûÆõ~°ôÙµàHûb‘·Î¡ 4†önK<ø2áívCvÝ`M$‡úMºÍο9¸Ïºs#ß7¢t¿°gùø2˜RUèØ7Œ}Žï*3aïcš·ø§éøaœ‡Êà1 `ÒÜÉ}},…X³}£< z×oÿÞªð,ëf½B‚¡ë4ýûø—ùmÜ·²ÈÎø¢êu¿ÜÞdÓ×Jµ&=ô±‚èÕZs—†Ú‘ÁGNF^¼Oã„·ÚîBÿ˜à;`ÖŒ¼õ¥°ë¤aº‚ šÎü²ÎÞäÏ ö8zß”]ÿaÜáþÇŶ# ás…ǵ乥ðcÔ<gíhØ\¾âb´ì¬~V3¯“3ûý˜?Œ[rì÷¨¨e…°¼ªÿœ%Ш›5só´È0ùÐÍÿŽEË{/äŸ÷³$sZÞ‡.„¤OvÈâJ W`?¿ÔÑ l½3ƒ®]¶NÝ7Ú…Ðõæ“a\Ö±þÂaá…01wëÃ;%P»kÝΙƒ dw£ÕÆ(ר¶ßçJÆ=êb¥A÷å sëù³í'juÂøÞ£ÎÌ*å“á•RPwQ®s6lyï‚hò:ÿèÅö/§•×¥›yU!d·Ù2;èg1lH·äF§¸¾ØÊã—>ôû:ìxðU„Þ?£uq_Ô¬B?}°ö9?5úS{ê­êç àdú¬Ê'a«ÈÊŒ ;f*Ðù)ĸš±µKæu-‚f ^ó÷ÃäÈ ƒ…_ë!çð^ƒæáHt›`ç¹×“œr;û^W@Çãä¯ó)‚|ÿšN †Åл$µ¬ó»zxíüjû埂–÷ȈډNÑÒ›tŸq;žíŸõmbø9ÖKP)†_㜫Õר®Q¯;D°ûQž-žz:~7ßßï}ì¼"Ø´+ðÕÏÊ"yad…î“zð´|wðP­H-olô8èEþuý‡qÚ§vö[QÄžƒÁí÷¦ÎͯQÞøoÓL>ßó&Y÷ K‚…ô>ŠÂ¼zþsÏ‹¥ÎE0|ð׋…øŸCkSê¡Kü¡•!žæ ¼ï-/à’7¡ïƒÑyÆÃ8ú;Ÿ"x‘o´²~XÔ^ØY[NñsD–p³€{ÁÕ‹ô,ù3;뫟7zÏe;‹ mißi‰•…pÒ­ W^T=¼=• ù¢Ì ^ïºWÆ^„ÞwS§ã‡qîâÁùÐ"Xô¢€ŒÑ8-\7KvÏL»_u–ÃJÀ•7™ZÓÍêa_±ö}i˜ {0Ð÷h]âaÜÐA›­IXq¯ùgåÃ.r¡aY=dy=û|å© {/}Í¿æ'ãöÌšò3¡ÏÛ˜³y7´Ûk׃ô‹Ò|»Žæp$fùû¸ÈB¿²˜üí…ô·’Xáo/¤ÿë…ôßµw$÷ˆÍ î{㎭‡ˆ‘8¤ QÃB%D‘Üþæï>éï>I¬ðwŸôwŸôßcŸÄÕŠ@6¦ÜßÍýßÌPDÊŠ”âD"2D‹– F$H3¢ŽEL„D eˆ4=DŒÄ!Mˆ8!†ä!ŠXìøH 4"<,~fH("e…PñF"¢‚…Q€#¤QÇB)B"2D ‹¦"Fâ&D ‹¨¹mÿºÚþg¹ÚþºGþkÝ#ÿ»=µeˆ .ê$x÷œó#ê¸È‹ærÏ‹1?¢„ ¾Þ<î¹%æGš5Ü‘0$QÄÍ÷kÊ@$úû$œ>÷I ÷Ib…¿û¤¿û¤ÿû$î\³1ãþ.5üï„H’Ç},R|$‰F-3$‘²¦x#‘ˆ QÁ‚&@‚ ÒŒ¨c!H¢„ÅN#qH¢†ÅOˆ„!yˆ"B>ˆD# £ŠHY‘Ô@¼‘HD†¨`Ñ ÁˆiFÔ±ˆŠ˜Óö¯£í–£í¯wä¿Î;”!J¸Pëipïc~¤ Q›Í½ËŒù‘ˆD#‹¦ŠHYÕ@¼™Ëö¯Ÿí–ŸMŒÄ!MˆÚh®¯+æGòÅ1\QÌD#c¸>—˜ E¤lAÓ@¼‘ȱ\ß?Ì œ ÇõŸÃüˆ:.x¢ñ\4Ì(áâ§7ëÇ…ù‘&Dm"× ó#yˆâ$®?æG¢‘ÆI\ŸÌ„"R¶hj ÞH¤:×7óã"*@‚§pý+0?¢Ž‹ªh*×Gó#J¸ÀêMã~Ïù‘&Dm:÷»rÌä!Š3¸ß7c~$iœÁýÎó#¡ˆ”-̈79‹ûÝ!æÇ“_€kp¿Ãüˆ:.Ü¢ÙÜï°0?¢„‹¸ž&÷{ Ì4!js¸ß¥`~$QœËý>ó#ÑHã\î=}Ì„"R¶øk ÞH$"C¸ŽI$‘ücŸÄWø»Oú»Oú»O’(üÝ'ýwÙ'é±ù$eŸKñF"w,,R$‘ ͈:-”!JXÀô1‡4!jXЄH’‡(bqã#H4Òˆð°Ø™!¡ˆ”> ĉDdˆ BŒHfD £‰@Ê%,’zˆ‰Cš5,šB$ ÉC±€ò‘@æ±mDxXPÍPDÊŠ«âD"2D‹­ F$H3¢ŽÅW„D eˆb=DŒÄ!Mˆf!†ä!ŠX¤ùH 4"<,ÚfH("e\ñF"¢‚]€#¤QÇ/B"2D ‹½"Fâ&Dm çÈÄüH¢8ˆs5b~$iÄ91?ŠHÙ"¡x#‘C8‡æÇEC€å\^˜QÇED4ŒsJa~D‰‡ùyœÛó#Mˆš*çØÁüH¢8œs½`~$iÎ9G0?ŠHÙB¤x#‘#9æÇ…I€â\˜QÇ…J4šëIù%\´ôÆp½Ñ1?Ò„¨©q=º1?’‡(ŽåzEc~$iËõ,ÆüH("e‹âDŽçz¸b~\üHð®—(æGÔq1MäzZb~D F½I\oEÌ4!j“¹˜ÉCÕ¹^s˜‰FÕ¹žg˜ E¤lAÕ@¼‘È©\(Ì ¬ žÆõ"Âüˆ:.¸¢é\OÌ(áâ«7ƒëÍ‚ù‘&Dm&×#ó#yˆâ,®WæG¢‘ÆY\ÏÌ„"R¶hk ÞHälî7ä˜q¬Éý–ó#긨‹æp¿©Åüˆ.ðzs¹ßvb~¤ Q›ÇýÆó#yˆ¢÷[7ÌD#×)ëBŠHÿ±OâÖ÷¿û¤¿û$±Âß}Òß}Ò}’›/ÜwÊåå#H4ÒÈýÿb‘2CB)+Xˆ7‰È,`$‘ ͈:4”!JXÜô1‡4!jXì„H’‡(báã#H4Òˆð°š!¡ˆ”E ĉDdˆ IŒHfD‹¦‰@Ê%, zˆ‰Cš5,¨B$ ÉC±¸ò‘@$iDxXlÍPDÊ ¯âD"2D ± F$H3¢Ž…Y„D eˆi=DŒÄ!Mˆm!†ä!ŠXÀùH 4"<,èfH("eÅ]ñF"¢‚Å^€#¤QÇâ/Èù½1?¢„ Þ Î3ù‘&Dm0ç;ÆüH¢8„óîb~$iDx¸h˜!¡ˆ”- ˆ79Œóab~æG‚yœ—ó#ê¸ÀˆT9? æG”p±ÑÎyê0?Ò„¨à|i˜ÉCGrÞ.ÌD#&3$‘²EJñF"Gs>Ì‹– Ãy]0?¢Ž‹˜Hó‹`~D 4½±œçó#MˆÚ8η€ù‘ŸªKZ⹃ۙž®ƒ€­Ý?}V5¹&vèzryxßÔ~[hœãžläÌEÀN¯SYPY´[lt¸Ô¯ü¸×o°„–›ZÑa=Y­qê[1“a\M¬jª»M,'ºñþ¿3áFHØËÞ»ë йêDnc(i»nÙ…éëõ=±¾ªüz¾tgþ(‚uGÍšfÂâ¾<§a›êÀxª‚‘Ïs8j¾Í~¼(ˆP9뫊q‹ÌÉñ°IE0Yæ´cßí X6Qaê¿:x×´¯ÔwˆyKÿ^òϾ]|Œ çÚŽ ÅïÓký–ða®õ–f§:¸µ*~ÎTK¸Ô…ëøDhß{ÖWã~rmOUŠà.§M=–¯]¸–up(ôüaw'kX”õú£JÝZBû˦ã‡q?¸ö~½‹ÀzÚ7瓃ÒáÀn6Wôê`ÆíñG|¶!r‘üZ"o7ÙÆE`ܶÂ-ý7tÀùR4*-\ yw"?iÔAÝ«R¶¬k ë?2–ŽÆYÿžÿýÍ«Bè2ɤH8P ¿ß˜^j?¶ ˆsŸÑ ¶°záá/m í×6†ŽÆy½o:Ý.³äí§«ü ­ƒ‡Gm3[dËúþëïSЮçÛ®Ï:ùB!|7U“×9 HßÚÄ'뀳Âk&%3SžŸ "‰Ãu·m¢}Âx—ÞîÀ‚… oÇã— ×›/týQ áq Ÿ³ÏYõ÷c¦¦¬'sü8c)íÇÃÇ8×Q_VÂÔÕ¯#z•¦@ƒý²9ojÁFoçNõ #ïm$½ ·›ýb}©1Î}ÁÉçOF¢hŸ_µRÀ°yÃâÕ²Z”ü¨±æôþ@îøä_}1ÎD©õ¶…p|á¡=S¢“áIéf¿…EµPøÜ Ék¼üýƒ6ÎÚ@¨ï™Ö³Œ³Ι¨ bÅÚÕ#&$Cn“ú¯iµrù¢ÕJs¨hÃ5Z÷¯º+Á¸ñ— Vj<,+ù‰‘×õ\P —6í1Å òÂ…üW‘уƒöìü:ŽÆÉÛè)€.ÖçëøIÐ;¶‹}ÄÕZØbÓ{bÑg[ຣ‹Ú±Ï9œ®D:õük2îÄ-€œ_ÅÏ'‚æºDOé‰Zè°X7Ü&Ǻèý&¼D¨œúx7½§ð€-°~¬‰ðMøØnQp-¬Ž˜·|ðdæ_YGno^}Ëõ¢º<Žq¶ö '+@ßk>3‡´Iߨ‹}rWׂÚÃãù!;àߨ%–ëÉ{Ñóà˜æöò8!Æe.VÛ Z›_ýº|¸s &AæŠvµÐ°lÓI·{xþg¤ j=¡}ð¸7ˆqü0®—Û#ýø|X˜ßnØÖ¾ °&sØÑÊZzfF­®°Å»ºÐ>”ô{‰À8/e•WƒÂòö;•€Ö•±ï´jaX`ªâ‡_6°¤s• oÈ–k—Ml­GÉã$wæêõ2Û€|8ý2ôdMG ôÙä5É\½xY/®Ó±„ä¼—µÝR6°þFÊtü0ÎÕ«i{ºQ>”áô:üå1¬X¼çˆZÐúðÈÿ®Ð vJð·.ß@¸®¥Ãìh?…ùõ|êçÌ ]<Ä#ñìTZÿZ§·á¼K² hÊžm ÿì3ÌÃ8µ{ï»wÎÃçj¶yÇÖY„t«…ÞYÏz Æ›€oÕáñsÊ}€4㾬³÷4|ež.£ã¡ìî×>mð¼2É]ü®ò9ÄjüÝNß x^¶rÖ’çÆ8ÌïJÇãäå¨G´ëtwu‡Ê8¸=ÿl—‘ÙÏÁßÛxããx”v|´¬%ÔƒEçµ ãN¼“é‘ ±¹ã8 ß ­þè9„½Xþã”è>-·séù§[ÏÓÍÐò%/ÊïrlcáUXÔûk˜Ïªñâ£Áv0êuåbÝæ5-û zþaí˘âÏ’MçÜ¡þ½7¼“ÏAkcÆÞ^+í iQòa½skØ:Móñ1î‹ W°sÀÓÌмO@ ì¹òàÜ»]ÏáÇÇÓ5ì ±'<_C¦¹=^¢@Ïw!Æõñ¯Úëq6ìíõ©Pò]˜{zŽó‚µÏaŽhÐÈ ÏšµQeakȶûU™|™ã6E<¹÷N?4×<´91ü.”ÚwÚíúÆu<ÿ棟 ,­7«6îز?£çÆñŠ£Ÿö~•uùû¢#ƒï€¼¿®Åshç#ª :fòþ-ë ?Œ“·[ÛšËj´ï¼ª¾ kë‚*—=‡÷ÙâdÉ6Kh·aøÊƒ7ÖÚ—n"?Ýß]ó߆aWžÎáÁsH:|pòí–p5z€l‹ÇZ²ÚÆáXdõ8:~ êù“ÚÏV³»” ²m:íëÜ‚ŒcÖ¼PË>xv•­±ë_e#áÙšÍÆsZài™ ÑœèxE •_#‰êsvW´ÔçË×l® ûÚ÷^‹ŽÆÉËDBÈ5WJ7¡÷óWªy=ŸCàæ77ܲ‡çýfh¯&òökfËèøa\ÔòSOÞ 2`î#ƒ£ªû¯Ã©y;6¼ÿñ fé|N9üÛ¼;Kàâ’Õ„ú›éøa\jõžÉÏÓaŽÉØy…K¯¤hœñ¯ºg0è¡îç‰3|vÝþÄΘ3S`Æ-:³Ã`]:L¶xqÏùW!üùÁã 9Ï`ýÎ`à׿J :@d§”/>J ~6 Æ]ÿ9UeD¯tr“ÿé²+ÐuúþE¶±Ï`Â9c:ýr†ˆ}V³.Æ’´ùO«2 èøa\Íñ¼‘·.H!ùÅHKëQÀµ|öts‘&ª:9Á´j“ Ï™ä˜ÉügC—®¤ã·°ž¿ngåY»)R8²¸»§ÂÌHè4õý¯¤½Ïà•ÊÏsQó yZ¼ùÖ¯þ-}éøaÜ åÃÔbÓÀM.²¼ u¶„=¹Vj€d|ÏN^­@>¶O\qËM@Ç㨧> Ö.MUõ¡`TÁko·gÐq–âØIlÀ_. ’ö«ª² ©÷Lˆqæ½»®_› fõ×ô(¾·?íúÒò~Ö-Ý>Ì îläZû³ïs?ŒkŸú½ãèY©ð‡wP5­ä<$ýèÐoЊg°ÈåŠøŠ%H#½ÛG­&í9“õžE`œÙsͶq)0 ž™^«9Áq³tã?£2“_–½­àdÀ”ìÕ7ýˆ·î×kîwé<“`í—ž–r¡øY˜ÕåOYÅ”g0ädá  G­ 3Þ¸ôì _"×­eã‡qš —5zÄ'ƒ\SÜÿ,8ܶ^h2â\Z^tì¢5¬Q·?£°Û‡Äen¬z‘Cý ‹êùtŸ™ Ó{wkèÊ ÏíÕçd{Í»9dŠ-,me“Rº7‰ùÓ¦äŠ5ã-j髚o v$HÎ@~Ga»áíñs¾Óýó`—[ôô‚«7‘oÏÍ™WãÜö<Þ2= –¶-*ºsÚç¿}5èc L_d²qÀ^;æó&-ýDåã‡qg•Úì\Lÿ€©KlÂ!£ç%ß‘550$¥Z=ÀÅèüò"?æÝZqä=Ä×xFf¿ {"¬´þ6!wëIèÊ×{S¿$~ïjìÁX~AåEž|üéz^“z*#0nÆ;‹| ·g 8ÑY÷&…>¬©üÆ—eöðæGHµ·ù§ßR‚qÛ׆L[^& \ƒócp§¿dɧÈøþÀ%ÎÊÌ6dû–7žò"ë¾X]@=‡2Œ»ÖfÉü}:8Ø)° ñt e¯p¬†›rÚ>^„úËéß§ ‡ëæ!®q,ò\û±o†èˆ ˜7Ûk z—ÏyϳB¶Ïò$Y Öq~Dýˆ<ŒÛ³â¨Å»Yad˜›óć¡êÁáuÿk+ÞÛÂ|Ÿ´“?ûy’º1ÅZCB˜×ã6jèèðæàU¶ÊÁC`|têƒl§XɵNµ…]U~mò ¡Ë?ŒØ˜BýBŒ³wšþamÊCà®"žŒ?Cª T.׀ƀh¨ ÂMÅß±‡¸“”¡i]6ÒïEŒqú½z¹ÚôæË…IAo¦i¶Ÿn X=ó\2ôSËúîBÞ/9Øð]ËŠŽƽ~úùø­û ¾íÔ×?>¡r…åˆ-Sj@ç“YÙÕ¾ßÝý;/Ë|;[||õ¡J0Î6Vqïçîq°÷K ê¡á»Fmô‚Á5̯è öÚß¿X,$»‚.Ï^kGÇãÔ?Üݯù4ŠŒ—jf€žD¿æqûûj^I‰àÍøž†µB2»ÍÔ‡y¡ã¾îá;•¡?b@Þ^|ÕèºP<Éÿ• ╳všT¹‚nU'^Ì."_^èǘ‡q¸8ïNn=ß8t Ý@êˆËNù2øuÞ«`ºš O+™¶ÍÁ™ÜßâÕe<óBcÜ)N Õæ.TÝpYõJ(ˆ6†J‚bep$àîC}W°?íé²e.¤€k׺y…1îþ¯é»knƒÃ˜øö—µBáÄâÉ)ѧepÃoȧ{Ï] |ãx½iÎĶ»` [êcœ:ݸ2_@ôžC²wÊà@_Ó¦™N¬~:’ä‘Üý>#0n̰y°8Úûqfðп¾z¢’Ÿ ‚Ù›^p€qF³úoªt eE\ƒ'Á8Ù’öÏ®ÃFÍ¡ÓBÀ|Ý‹YÖB<¼ïQ¶ôѲìÔi™y½OÛûÉlü0N¯ŸúfË5W!]j^VØ#ÆüÞzVʦ‡W»%„†«µk6Û“Ö\GV§°¤ž?X.˜ˆ‚µ9ý”,z†@©SøÑd-õ®y Á`þ q÷`!¹rryÓ™y¡1®nÔŸCdÝe¸Ü©ÃEQÝ~(¹6íñäI209ûD9*YÝ4UWç²"ÔƒDÇqǨÝÛré"Ü\kì#Ù'§>Õo~/o‚¿EúÛµøZ‰_ñÜEµé<bÜY'Nx}ŽŽpäåN —c³µÇwÅ¿/×8*$Àžy²‰Ãû“‰K:Ðù"Æ8«Ùë\ˇ<üg …m¹NµO!Î:Éeá ø¶"+hÊaCbM„³<,i¾ŒiÛùBòp8Ó°rPñ‹Pøæ´ç×´§0¦Ïp™ÒGÈ{j/Ö5$-ž ùøaÜ G#¦…çÏ}Š© ×’ö+SßÂÖ§;Tn®unÖ§Ñð_óZ†qŸöpëÃðq6·Q>H}¾O!D0íÂîgNÌŸcLèýSêWXZÏ• Ûø{](tœ8R¸zí!X}XwpáSp±¹ÓfCšŒKÙŸzv£)éÜw°Ò7:~wÆwY¿¯ÃvÁ<ÇŒn‡a! \q|ÐS';±¤z¤_QœeNv´YzÃþ½;?ŒÓžpröÈ1Œ,êu.ð x·A¸áC5X½ªŸ7ö­CK?er9}oì'ĸ>/_ŒhÚéãkãG: ?kÕø}²ªátE®Ê©5öPÓA9ÊÕ”„~ríûë¬+?ŒÓ:iæu]2ËPuè®cÇ@¡óPͰÈj0ëtòˆÞ/!°u¸Î¹ô6c’ˆŽÆM¦Ô¸¬b5ñMzwÉ2 êwŸÊÍØYÍú5ÛÂÚ£§(^[FžµÍS³þN¿O ÆÉ/§vo%¸8»?ã8ߎy¬'ªaà¨t¿ ¶p!±L't«Y%¿à¡ùdg8|—ÍÂ[{‰mÌÔšc߃‡šRþ|½jx ©»åìˆçŸ•ñë)Dà380æ ýû–á~¢Úg÷þÚƒDòZ.Kw錮†‹]^¹·½e´¯t'XæúÃÔ„ÆiËOøcäˈŽf)'ᩦ…ú²6Õ0@rñºå  û¹™ìþ7ãcod×0Éõ“dìð¤qÕÒS@F]\0¼¼ ʤDV+u„·/Wóu`'§µË¦qBŒ{qÜÆ~Bá2ÙíëU…§á°f‘û-ñ*Ø1OïÊ’{NÐ¥¢¶iñ ý³é¸‹1î‰y»ºá,±ø22zv§3 R*^Rº¥ 4>Ô]ZìÄ<¡ÓÁÈ•±éštü0î\w#ü<ÑRló­×¸Ø®%5­iíP«¥½œ˜'­™ðy‚bÝ'Á¸Ÿ³;|+—^$z’Æõ0qшïJ“«8ÉbÛ×çZý»³ÓbÎÚÓ8Æì©ø5Nr™Üxüõ™Ëgá€Îo­àÎUÝ·ÜO«Æèù:ƒ|—à~l&SX^ÏüÑ»maÙÛI•g{Þ”L°¿á üi¶Š5 „aŸKÇOˆFäúG%ÇøÅÛFûüºJ¼7˜yu9Ҝʲ×Ò'à§´úî•QÖÌK3ä—„W½¦[ȯ 7H&§Cºpîõâ„OàZÕ Æ…¿,ZæpV°ó㎄oôMŽOJ~¢~R%aÁŸ@òÖÁKÎÇ™·ø« ßé‹Wß踋1NñÊ›Õn‘Ƈœ ôh_é?µûÚ'°êå‡SÏš½¿Ç‡žï—µÁ·¯ËmòæAìð)Úaã£nJýlŸ€æÚ׉n;-€Š_BÂÕºœeêlü0.{xKíÂÛä—G탱uáñåìC©óŸ€aïi·nŽ·º/[ÒÒoŸŽÆ-ânÏî¿Cú­œW »w ¶IËÈÙÑø}ºþL¿Ø†ÝO^…KTC—²ñ[QÏ_{ã]’¶Î]¢Áé[î\OÏ’™ž@tc‰{@Ó—yFm_üe‰Ûçn¡ã7}³ÛÖt—äñö$.´éO¶ÕU‚üòÀÁ¶éíl™+¹¾ÉˆƹtyðèB ¡>ß+0ì‹N›Ê„J8ºWÑ]qš=\‹L˜Þm˜>Ì8aµw³?ýœBŒóýÔÃÏá X9´ŸÛò«0óUCäïS•PóåÁ‚öÐr½èºxaNâ*:~ç”Ø«§Z,™NN´kïw .'¾XÕ¸¦6ÊìÛ«¯@ëº7?Œ›t kŽì],¹6Ëmû‹u×AÓ`ø¤‹†•`7öÉÜa7íX}Єìô=Ë$7}èøaéÉšâˆÁúõ:7àÔ‡©E¾S*aNIÉñ½«…­^gëËŽfTÒ8ÆõÇ]5=pŸÈõ­ënBǧ!3_(WBÇ£žï5'Ø0?ÐT¸Çµ]ïGãVÖóÕì•“z8< ¶oWІ‘j²ÆO0èpP¹»ÐýÍlpßÚ;ÿæu/:~×CÒQ7xüCrcS©KÎ÷h°OÍjZ^N]øæ`a åb³E°uyø¢”?žtü0.]Kz[øì!y5pb§ È[ðvŒÃú°G³~ÕmSW+k3ÝŒ@]ÜtVw)!ÆUdrèâI[Éoƒ}è•'ßÏTõÙBó3Nxb 'rˆ~N1Æ âÄ^ÈïS[~zÔÞ†õŸjîÎÙT^ÏN¼÷X`îOœ©aG_˜Ñ1œÆÁï‚sZég/8nwFüX·í³EL×^›¼OÁyäì˜_–ÆI0nç€+Ûû<&³\öɾ ÃÇœžV;ær,Ñv<9{?o{Œ¸'Ë4N†q’îï0ÈÜè.tÖ[p=¨Kó92ÿ­’œúÎlgEãõüagõú¦’-å­Q¾t¶]¸âhYSŸ+¹-Ž>™»ciŸfqWÐ4އq»ýŠò}fHˆ|w>9†Ýç-‡J³Ú©é#@Y.0µ€?þ«*öÒ8>Ƶý:vØ Á¢zrUy ÈO)‡±WF¼‡Ú1_¹1Ø Ü<–Î3!ÆM´N˜ù,]B*"Wtº}úìmlléWâf£¨él!£ouÝðdž­ž=ùøaõb$‘¿ïŽgûc!n°ÑÎ͆å@ŸƒZƒ‰=7–¹½OŒ<ê›;¨;ÉÄG¡«ÎÚa‹üƒûŠûCüs;œé¥J¤}áÜ1ý ±w—À2[,ì~ æ43x«•@¨ÔiU †”ƒ×Ô¸t˜Á¼=”g]Æ}·Ü› ‚e¦ŸL³C¹uÄIÛ0:/ð8ÙfÓÃû%’…o6ö•=¼–båaÊAïa@.ïL™OÅ Æ®9óü|Š+¬šz¥ÛóUB—ã†-tžè×ó•nOk>ŸHîmž#¸<÷ˆ®dÍò¦ Ne˜ðY`ZÖU·ÊF;1¯¥;h½ÞÞöÞV}0½“Óü8n57xœö£½Æ('‘ òý!øíŒb^Tª_šý$Žæ ×ct[N_ûËÔâÌô·ºš]ÍÈìa…ȦG¿'>oz¯³ÊVI„úŸÂÆÇ§g=(ƒuk—n}ÝÑ’ym[ëAÈþ«¬]]áä\§Ö+·µoGAöÝ;§ –ÓSõ¥ã*ÄãsÖ˜êKId­\8ÜÛú§Ë`ø„Õ^!﬘ÜÚ.jÛ}¦ÿ*æ_wùíÅ} {=ùXmå-5ó°Üv® ¬ÞµÖEù<Å<ÜÓï“’ˆÅÖ¯Æ÷àÈaa¿Mâ28M:ÛÒm˜§ÍåÂ$O¸œÛ#îF˜>Ìl\êµiÒPÓÙX¤ùE‹z¯¾h=Q^ÑÕæ“1¿¼zµmŒ§˜GôJ†0™ìŠiî£ðü¬’žxý]XoýT¤( ú a™\Œã ‡·ß¼é¥ÏÎçÿÈ#Ÿ§x¼_½­pÉL&Á˾)í{ w·O™¶_rû"!ä*›tq’9tøqÓ¯ÐÙ£å: ªÞÖiÍîBïËÊð8ŽÜý²ýnjDonmf(æ•Adìç]ÚBEÍÍôŸ…5yéœÇÜa’߃ £gz±ëvÝÚ‰ÎWƒzþäKÇßú¤ÎÖL`µ÷÷ä¥mÊàÏé£OXÂÏ%“Îΰ±¾í±47tâÄk0½g0Ý ——ñ* zA–Ô­ý„v*ʽ.ÔdÒ‡bŤ÷y˜g­üÂ5…dT54‹|$°%2bÑ”ºR(]²Ï¿Ÿ½XÍáî@¹CÚx¥yc¹A"Q8m×î–÷b€¾ç0ŠùцÂv!w#ߨ“ÕM̤±ùÅÝU)$­÷ÆMì÷;é}3«?½YuÓTÛö8»${̸ó»u™Ìxµ÷Ýž¥ÛaÂæÃe16´æq?Þ-íãz\!·ZclÖºS)¤½‘Æ£Ý °­ªë˜äØRð8©àl¦ªÜE#ÁãX,9zHð=•ÈoÇ-K„Î:¢o7–õmCõÐmâõᑉΚŧœ`{Aœ! û3éèÐåÛA1ûƺñ½FÐù‰Çûõ]u¦™F©Ä=¡K„ñùç?Žw)…æ^¹çšõ¾éA­hÿµŸSEðæ£JÝñc‡àJáæk—MBàwô’ý²W<8øüãä·WûÃ’{NõRr€ A™â;ì~ža=¿öKæ˜ñ;ÒHŠïÒ«ª>I°Êþ`%i)~Îpõø7ÖÌÏÙ,Šî÷Uw‡S¯=Û—uB®Œ~Ð3ºušõøäG­MWÇ›Ùö†~K¬jô}¯©tÞb'§÷ðïid„\$š z… nŽ/Må;Ëe{…­Iú|ÁVüœr}ù!,nW¯j¸¿5|~âñÚe­ï>RrôffÓ™UÉÐÔqgö½Î¥°öAÔ,ss»Öëý¥Û‡Zé t¾yBçiž2–8ÀýÐscÞû¨u0p­¦éa ~3úoYé÷a8Í#Æl{hÆKU Ø{&õÑ·î†Ð»,>ðK+6d´ZH\°\ßEü¾›üóy³ó,Ú×yÊ1£t²¥Æ*©Û¾è¹ÿüVØW½_îwÛ0ÓòÆJsÞp‡_" §U}àøÒ+÷NÊÜ¡åþßÂüã¯ÊÁK4Jñö·vð5d{£’†}‹‡šž˜ç®æìù>ié„>¿M…æ¿_XÊG]úlŽ0ãó¦íïpqñá›}`þ5'O?r{yÂU·§³:Ã[—7¾·…ØnKÓ}ía¦ùù¡çòés8#¬·Üôè•A´´í®§ÂË<ÝiW\J _áÑmÆWBaï£ürGøn{8‚x3Ïå Ö:-ŸÐd vlnî2ø»÷vÀ½½q|œH†“ÁÝéºÏÃ<ÔÏ–AèóöTè×¶ÏÆöæ% ÔñôºžÇ–±çE¶0¡ûÏÈ¿VAï‚[W(.gû ÿÈóåö›»3†›}¿ŒŸÇ?=æôÛ2HÇSgwNƒ…§7õ»´¤n<èQ|Øq9<þ:KtSˆù=˜?x9¼ QïfæËžkáqè}° BŸk¥Á{þ¡ÚsJØs0Üš¶=Çýµ=|+ýñùž+Þõ¬>Þ\ä7Ýá☀'{v¦ó§¨°ÞºI™äPۮ I¬Þ¼Ty\ üØ´§}g¾|á´Ú \á¡]幬ޮÐéØþÙ¹ekÀrÄïÐA~c£Ô-mw¡H ú°ëëåh*]Êz,|=ŸÎÌÓœrqœ§O&)15íà6E ¯g„ÕœèWïð[ê'1‚–û•ô}WæGµi½þ¡ï· •ßCNN*îÇžoÙÂÂù©çi æiª¸Ø}çÅLò*mô7e"….%'÷+”ÀåØy›ÜLalÓøò[>pvÊ¡ÈþëÝ`«ûÛÆM÷MàÁ‡[ËÇY´æ‘Ïs<Þ2¹Ð9“”Å_¹c!L‡Ègœ0ºŒì†¾^kÞ#ó„† xánà#·Vÿ›‘\4/„?;`mAôx Æxï½öÚ€Y„·ñ÷º¦Št¨œe Ü6»†¾öÈjñŠzÃI3î c76`£þ–ß:!¢ÖãæGÖgÏíCç/·SAÊ¥#:Y¤N;h÷àápFü8] u_Ü[VcgϽ4Õ¶ èýKw¸~Q|÷O›¸zóF—"{1ûü]à£ÿ€ß…Ýû0 uë}Fù<Æ<ÃøzÌ›ŸE‚jr¶°É€;§žîÙ}¨zuÿ:óÏšíÝ€»ºä§Áâù‘¾`Û)g€~…;È7$aºMûÒѶ]ÀQ.€¶†É«Î·‰ï@ó1}o7‹ÄH†ö›•¿›V(†C½úL¹jV>>F«Î9ÃÕ__üe­÷‹D6Ü2›Ö<òyŽÇ«[ôs÷ «,bôÃ/?4ŒšÿÂÙ¢¤‹ã™œ¶„äÌÂûçA®Ÿà¿žäßè ÜSnÑ\öœ C¸kéñeôŽZÇ,¨žúÞlC¸S¿³e], ¯É¸ÈÈïN ¿üÊv‡.œvvÜvÈxúè|ê¶@лâCé@àînëAÿ^ W¥+÷Ä2‹po éÊ‚¸DáyýÑÅ ×Å2¹Vüˆ+ÕZ­½!ÓÝf.lØ \5S9² Šê”_4É|Ë3!%Eÿþ. 70¯ì²ÇMŸÎgÌó(éM^´J69[2ýÆ‚l¸µ`ðÏ=‹áê •ê/+L yW»Æ¯¿Ü¡ÿéc»÷­õdÏüA¾}›J#ß Î,€ÏŽ¿óg¹jÂçbãÍÝVý^Œè<7©ç þ˜|>9›l?&+êt0îÔµÉ×øV‚­w.šm2†M‰åצz°÷ÐÜ!\Äí<ýaÕ7½%A*APÉ=¦0À<ß¶>‰Ð_@ç9w±(ó~ÓlòöNÆ2×_Ùµ6õ±ãó"¸ÐórÕ·X#kzBžm£òùg¶ö‡Në7ª¨Î ‚ô%UO_.¡ÇããñfWÁ’–M¨_6ò·ù®Î,ç«›OÕšÃEWÁôžBðŽž5eäX¯–0­p Œå÷É Û² ßusÜ©³”óOo^Ôeh»sÚ]Vµ^÷Éç3æ!Æ ö$dg‰9îTs/R‡9ß,µõ–Æ'/ØÀï£ëF®f÷Ýì¡xdàü}ƒáýƒ «Êv°ç £¡&ÔtÊ¥+š rk¥BŠÔØïè<Ç<ѦfÞ Ë&Ë·r¯¬Á”Øu÷Wî/‚݃¬žNȰƒá]â¾ùµzÇ©ßךvs…q3»3 Œš2jwk† îõx{ ÞLš'󨇅?øM¦—º^¸ø!V?Þÿ¶DTÄ®#€³¢ê¬õ‚‹—ŸOiçáOß4ŸdÉþŽ€Ö<òùÇ{§9èCn·²Úº9á[Q.ó?Á÷á W¯nã+ÆÞ<’gm‰ÁÚª-tn½nZ+ àÆ=çÿ¦Ç“áñúp—©Ósˆ\G)̃ÇñŸEpÏÄíÚ܇·Ü× þrkËÓ"ˆÏ¨¸hµŒÿÚöŽŽtxprÙfŸQPÝíA¹ó€Voº|~›Öó;ËœßÛÛå“ö\̃LËÚùŸ acކ¨K³ZÞwky®3°Y¿ûy›“W{öwƒ-Ák¨¨üÖZ˜»¹Nq|##ÆÍ2…—¸ žùCß?ãa£¡UŠ{vçÉ‘×eŠÉy Ï=ž,.„¡Vçwï´‚ÛEA$ÿRðm¾Ð»ó%·ÖûC-¿K9ù1ÌÃn¼ùi=ÆùXƒÞÔ«kâ–Ó?÷ªè×z9cgŒÎ¹ïZtßhÐêÿ–Ÿ˜g¿eVn÷å¹DÙ öÎ͹ùpGçÀۆÅ`4úÕè† }àŽ6d`ì™¶c¶öI+˜¼Ã÷œõvG ï…,ƒvg¯:,]ðM‹þ軽®Ï¾¶¯Ç6Þ×¾5_Ë8n‘ßF¤yÌêù Cfî7Ë%«s,ÌÜ\›æÀºÚ…п(6ëÅm3h7ò_yøïoSþªÊ–í[,!ºîÈ+ÏiÆ­yäç /â—žò“\R;6¤€ùÑÓgÂÆB©nôÃÝ%–ðÓsehÙÛ¡¯åWry°ød.2(Hò†WCË­fäú´~^ßÍ}ß7ŒR€´¦²\û±ô|çãñ¿ánLæ’­Í’ýÚ‚éE­ŠÅ= áDû>!ê·l@U+ú9á«CXâVçÒGN0•Û^žðkyOž=WþªUœeÑEïÇ-Á̺.ï|õÙ>=?0Ï3¿ïÔ§å’«Üå=¿>ö¸ÔßýmäOÖ°F+çüœa~~ø¨†Èf8CËûroZyŒÀGWÛÝñ_¾hµ‰˜l¯»è«]Ð&øaPß4ó\“6ŠG¾Î%ic5fn*„Þ7MÝË*€ì3ÎŒgå/ Ú@$§ë=æ å?`Zb¥ôP‡W‹[óÈÏ<Þ£}Ùûžæ’…]£É8޵ÊT®@¦úÛŽùvP4ÞiÀ3³Ö÷~@ßy벩¾&W³—<¥ï%Jð8‚4‡Žãžç’³K5>õëY'æùëßYÚÙ3|úw´kÝoÔÝãnÜ:»ðjû=I®ì=ðt‰êxõG:Ÿñx©5œ°=ø„*Õuµ(_¹˜¹÷Ócäͦs·í–;ù7;•û9ÂÀ‹Kÿ\ Ó¾½®· —Ò#¤_×)†ӟŸÎ^Òn;ŒPºùÊØïHè|6¯ç›É |.rÐBíø©"ˆw,8òxa¬Ôª8Sì[’¾*T¹@Žw;-Ø+¿I)ÆNOÙznÍî¦û2>t(sA'‘2ܺðdÃ~Ü_Û%üwêH÷ õü^“W6ì6È!*AÉ÷·—ÀÝ™S§Ï_™ž ãÇû:±ç®psôJó奆@?çÚò>3È_;€œ[¾CÛö„…/ç)¹ë…N¬Ö¬Øòn‘%ÈËt…9¼°=˜ÑÇO¬È½s¥¦ÅÝEßgvì¼û­5eAŸ·MSkÑëHchöáÞ<§y,ëù™â;‡'ŒÉ&=í~N›]w~y謖ÏÊ\-TÊÌaË?¶(,fëÿ,B÷AúÐNçUM¦®~kù<Çã¥Vñ,ÆWdùãˆÞåðfö™&i<Ø6Ò,¡ÏnspTÕl¿¿½&«ãÉ÷S­'/×o}ÿE>ñ8ŸVMPë–‘E–©ý,4(‡-Þú¼|‘ C_$þ>»Ò;MÛya!´ü~¯· âêåÑ)«èûBQÄBï=…ú畘S•óÎó±ðG"JXü™Lùå˜3•óÊ«3˜”ùäÅH£õ£ŠgS‡<ŠHD ‹@æøÒc®x%æ‰dN/Î{*E”€:O#€® ÿ¨‰ÿguð?×Àÿ§uï¥æý¿]ïþs­û_©sÿ¹Æñ¹º†s1Œ›“8U˜KXÆy…qþ•!ê8÷Âfnâ|#£©8ŒmD̬ó-QĹ&B”p®‰¸}¢ŽsMŒ”1×oÒÌí½˜Û× ç™„¹êDH¢ÁütS¨›.QÄyæ”!|æ¡ã\½HsÏ…"͈sÍéá‹F”˜ƒWç—7R†ð™KŽóí"eœs—yã8·nÄß}Úß}šÂß}šDáÆ>M}×ÜçáNX ĉDdˆ žÀ$‘ ͈:žÐ"$)C”ðäÖCÄHÒ„¨áÉ.DˆD# ŠHYQÐ@¼‘HD†¨`‘ ÁˆiFÔ±hˆ¤ Q¢‡ˆ‘8¤ QÂ"DÂQÂBˆHætå¼÷zXø£,þb¤q:u·r~{Eælå¼ö¸ #yÌgŒ4iP?kðlê°×Ã…"QÁÅBŒ4ΡVÎU¯Â<õш ó®rF7 ÎÕH ë«‰ÿW÷ þïêZK=ûÏ÷¡þ+îAý¯Ô£–:Ä­m|õ 7rÏýpDpÏûpÜ›¸÷qÌq¼½™˜ã‰(áX{sàX—§n߈‰Ôá+ä~€cY†ðqõð ‹D”pü¢‡ãͼbDÇ.Œ-è¯8 êÒUÂq DdˆŽS4óæŠÇ<êÇý»Ïù»Ï+üÝçüOØç˜±ï’˧ˆ', D¢‘F„‡'°ŠHÙɬx#‘ˆ QÁ“[€#¤QÇ“]„D eˆžøzˆ‰Cš5,B$ ÉC±(ð‘@$iDxX$ÌPDÊ †âD"2D ˆ F$H3¢ŽE„D eˆ=DŒÄ!Mˆ!†ä!ŠXxøH 4"<,DfH("eEIñF"¢‚EJ€#¤QÇ¢%B"2D ˜"Fâ&D š CòE,n|$‰F;3$‘²Â§x#‘ˆ QÁB(@‚ ÒŒ¨ca!H¢„ER#qH¢†ESˆ„!yˆ"P>ˆD# ªŠHYqÕ@¼‘HD†¨ðð¸H¢‚EWˆD"ÍÜž °„{¦‚E8‘ JܳD†h`Q#RD ‹³‘"êX¤Åˆ ÑÃb¨`Á#ˆ w4ÒŒè1×½ q‡(a1dn{u,êH¢Á|öÍ“©Ë>QÁB/žBýõ<,øbæ­`áCxXüƒ‘&ÄŒùé•p!ðf^z>.¡Hóч"͈.¡³©ƒ^€ EÂÃÅ"iB̘kžÇ<óq`¤L‹ó“ãDÿ§}ÎÿÙó¶3 ÿûûÖ°Ö®ÿ»zõÿäy›ìuHˆèá\áá¼Fš¸ûD8"Ù"Ê=sãÞÏñWÂqDdˆŽ{4¢‚cÈc.ã~³cÉÝ b‹·ˆûŽ« ÑãÞ·Ã/OÇQŒ4"Ç8„‡ã̽g‡c¨ˆcg†c&á~ˈã$FŽSÂÃq F9¸ç£ì7‹jl/Ê=ͺçQ`cÙú÷–.ýÏËz~yC,’š\ÿ¼Bp Ûe7çpõ¬Ç_²2HI¥5ŸÇUB³TÛñõÓløÌ[áãÜA´n벬õ{¢Þ4ÚÿBˆÇẠ¾“Aˆ B%ß¾µò;— ½oXuÄÇÔ&¼ß»†yQ´¡ÿüê¶öàýÌÈÝÎD~1Çjˆ‚éÍ ¢hùú‹¹S%¼SÛ–<Á-.üêmòƆ¨¶[ð-€õO‹.:]¨öƒ6—º Î+p‚ïÛ( ëÔÚ'H>ñ¸Ë¾¯;W”Aœ‹OXÜ«„˜SG~{Í̆XŸâE¿Íà£EçªQ>pnô÷õ‡•û®{VÿXPäÚÖ›¹ûÌ3žíÊúÕ+õäº×u¯o*í;,Á<ïÚ…V$§§“-éû†»wÓgè )+†*Ç›Áªà÷'Lý€z™Ç“–þ¥ëŽš-4uçÁåK;¯éGÊbUìÔkíÍú™Pï© ó tŠ’N`ŽìýÂ'0mçbëÒ¢,Ö'Ø ô>)éwzM«Ÿ©eÜh¿óÖ<òyi]Ï? ίˮ‘’'Eü³×Øì-³‡$Ý­}æn_Ö*/¼ÙØ:súå@à¬~6Â"HN`žõ^@ô¸†§ø½Ë’œÃú‰b®ËÚðiÄN%ºv\¿*pµÝ(Ì—ã?þ8ïUÚ¯—ýÄúAªõ¸úý¿] ëë) æQ=b¿è¹ …ÈO£ÈjHXUvOíN:´·è;WÖ× ‚#Ü>¹‰}!zµÖÜ¥¡ÊÌ“iÀêÀÙ6hGßZô¸Ÿ´ÚÉê²ñ¡þq!æIð0kF^2IürhŠVV5lÖ£×¹õéP¡ß÷Æ9 ַεµoXK¯–¾”[âì2VТý ?hÙM5V5ŸÕgê+cžIQ«¶x%“çÓÊae»§~æ~Xô¢tXýHj³Ï¸nÿùÃÜX¿ô¤¥/dtšî’;k—AèÛ)ç~'hí«ÚÃ)rwÔ§ùðx‡,nïIš'ó(ÿô5gf2éx6>äüì§`–ö~þEåt¶þ[ÂÑ5œÈÁ¯¥Ÿ ‰ÞÛ½êuOGà5IwÚúðµÕS—µmí×ÕaJ^ÌäÓí?$˜g9Ñ÷ÿDäºjó§à´=m„u™t´úÙë¨5¼Øt¿ßDþ: ý_Nv«¾úž±jíFëZ[æMèÀúïšA݂ѲR=Ú[†y¤æ4>“D’Ã>}5| í»\uùzB "³)m÷Ù²þ¶bˆ1P¬íµH‹„ì0¬¯·„Î\‡us1&î×1%E·ÂÂúãʾÿ²ÍÇб¶ÌWK×-[œoÜiØ/‰Œ®Ýz:ûÊSX1,3«Ÿ…þž;¾EWuÚÝò¨oíÎ|Ñ®0e]y@Å@h ºgv’´« ¸šýçê~Ö–î#x˜§F›3ð&’Î\õä¡þR¨ßøÌòN“yC6ÁÉ!Q?î™ +ÖÇœ×Xd †óŽoú´Üõë uÓBnO¿Ô¯ÕÓÕâ!‘Ÿ?˜çò‹;ínŒO$ú®ÉYZ=e9ýsß?iõêD…Ã4;=k÷µ‡ƒØ÷=µõû§ +§ñ¢RC^Ïl»8¢TqmЦÌÓDûì 1Oí”vGÞ¿K Ï8ÍÄì>^VÚwx÷ ºogÆåk4V_ú“–>at>é·öq£¾Úv­uôŸý.ŘÇûîb½ äfÎÞºy2HràDiðð|ŒßÔ>>ðÎà¥/ÈÛHÎ'Ô«hÊÖg}P<ÞÙ\û—VÊ‘Sî_û¥x”7""Q"~ø:?Ô¡y"0’¼Ñp‘¹}.6–A\Ø÷¢níÓ˜×@ bm ·nñ«Æ%í¯lWºtO¾í }ë š·›Ú³¾Ä]èy‚Ç»çh¼íä^ ™dô;&ÞWóxäîLL…FÍùc47ÛBxÏÜ^ŽíWCºÉ1öö„³…ô1 ¹Þ!l'óbôåQGT*A‹Ç”ö}£ý e˜'PwûËúg„4ýê^q5Do=uµ5w¤Â¼ Q²Ïv+è!™´ô½$«š«Vgö8*£' ÞÌúðåç3n¾¼Ö”go˜g{g=˜ô Û¹ìÝO+ëù4Óį??&Ïkæu:z^“ï¹¢)H…o3͵u6‡ÜílŠÞ®VoÉñØ—šF†[àÁ¤Ÿ²õ/[óP¿Ìzz^àqý‡‰‹F¯xLäšðë2ˆz‘<¹#/Ú¿;±pño¸<Êdáˆ3kÀ€kOØW“d ÷êõ±€ÞãÏÈ¿‘ÕÛtþsÇ3Žè·uç#R[r¡²×}t ݧØ|L‡žzC•L`i×?í®ùÃhÍÌ :·f%ø3/ð/¬·÷tÚ6Ž™L hýZÏ|Tìúó,M÷íµÄ6žÜÖëýN!W/—…¦%¦¦@ÀCquœo==çÇöåšD®ý° ^ôÙëâ 16‰¦ãl~hÑ:¦ÔÚW”öõd}¬1û”þoÞ? .cÍWè¼A•áÖ Ý£)ðdÿƒ_£9óæøÁüM½»h“iÓO+>›ëý‘—C$¶l?üM‹z@¿k]0 vR¢Ñ¿ý4˜gV”èÚ¯ðûä³rsRî¨ô7SÞd“;7õWæÙÀˆ¾©ý¶ ^ÍÖ]m2JÞ¸Õ¥Š†/´æ‘Ï<õ—Æ‘+§¾>Õ»â~,¿4$Ü&úì’ÛA°râù}#€úâgêû¶m½>”ÏoíÛö÷«’OýþÖÕ¦n¥ýìêù¹½sËÛêÄ’±¯•þ¨×€{BýšÏÛ“Á\.ôt€Óàit¹ÝZ0ªUòñ7€Õ=?x­7tû+hñï$&ýîÙU­C¸6ws´á5M:¯1õCÜ#ëוFLž‡y~tOß4+Ï ¨=Ïs`>¸µÌG7ŠyÛÌàÖŽ=O ‹­ayÍ)õvlŸÑº_ ñQº’õí¤¾>æYÊ·01ÄóÀÝA–ÕÀÆì샖o“àÂL_2ß®làÄækaeÆ…3±ë³Ö¾À-yäó76Hæýì.yºsë65Ûð}’ã0ûFÔ­>_¾ã¤5l~9Lwjm`«—jü¶b³Ä 3¶ï³¡ó³èºú†›ªwI’ ' «n]ª_ýY“߇uøäRkÜ*×ëÓhÙ?uÙá×}ÚôU0æ|†úÛŽ>lª»¦ÓÖ'ÂLŸþóÿ˜Â®º›óXÍ犫G{^yk5mnʤËkµü'½Ï!ø×>AŒy”‡z¾Õï2ioñ`‹gr TÛÍÉ‘Èê˜9hëºU¤L÷cý²õ ½^µiñ¦ÓùŒÇQÊÊþuæEñ³jŽ[V ˜ô˜x;ü]輨Ó&‹V¯)ó@KÖ”– ÁÈï~‚`'í»-ÁãÈõm/nuVPGÇjWXãñþÔ§ìºp˜Ì?hþ(éM ¼Ô>ñãW lÞÿãÇ#¶?³f÷wFNÓóÓˆÕwºOâq‡ï™þ¾G(™u1ÞDçk Œ)Y1;3_#b¾m*Ög×ö-ý¶I‹_|·êìãqÆÂVoõÒ¿WŒÇýd8¸“ÍÄäB‚§gÍ·x~ñd2/Z/*«l&¬líço{4ÀmÒ,=B¿ç ×´X—Õ>l:×ýG«÷âfÌרqÓ¬ÿ?=ß"0OÓž¥ÆÏ‚ÈÅ~KF_þRW4’'ñHàÇøª–­€˜`µÑ^bWÐîÚYźz¡ž3kæÇ´d÷1~h™Ôíõ Rû¡Õrÿ–Ý? óóžÓJ2ÓŽœ¹z½ÌóJGîòò•Àwá€AÊWÂk’³ü¤,Ÿ”*ÑÜ¿’˜ˆßÍô:eôºÂ°5|~ãñx^¶rÌaÇõòŒ¥¿kàUònrP [÷ü®N™jÑx–(w…/yZ&M•Bï{Z±}½«àXÏòœibÚ~´ ªÉéð 8Í^÷±xöRg€1(Y˜õú£‡ënÛ¤OžYšÍªâ Ý^ossZ«“ Mkÿuù|Åãf­ãô­PÁÝŽÇã>vsÂKi ^ ^ÌàÒ‡€m…[\ vÇÓ屯DYYµÂ´M@=n~p¼ËÑůڷö½§¾æ«£÷§ø˜‡»›}éî~èñrî8üÆùpwl´Ù6ýìKà)†žuèäÄú:/'rd/˜ÜwÐGÍgBX»m×çŸÃ:BLÀL0ßÛ^jkµ¸o Îå‡$õt"ü?Ø; ¦³¯ï#®+vWQWˆ=öرåÒB“–؃"ƆØcÝèê‚=Ö±¡¢býEׂŨˆA]e­Ø]]ßssÏ/ä™ç¿ó¼ïûŸyvFg¾³;;ë½)çœß½çÞ|?8OÚíÑÕ×<].+Ì.ƒßßæÂnUJ×àSAÍFMÌýì<ê«6fVO^sgVn;<Ð>-¾q¼Éuj».\ e>–ë:øm.Ü:¶f•[ÂI˜Òªcø”[Ä5ƒÙ¸›ØßK9F8æûïàëJŽÓ62hb\Ôú|s¡ñÔm¯¯u= .fé:ªaàøöÏ¿®üDüP/Aä>_å…Çi¿kG³²ÇàSPb[øºN-Xì6ô¯ãà̰_+{ƒxïшügá7“Ïæ gµ0±vbã ¢À#VwjÈ‚BÛëÒàx l ¥Dè÷pÞ–\Ðj†$\?Nùëa>Qò=BDÞŠ=«Óï×ë!1ët³äñS¨S _pìÆÕŸ£ 寎ý±²ö}èsuA×ä8òáÇyðÃÿqÿ¬] ›”},ã,´vŽëöãpû¯NÙ‚”ôý+‰;è-ˆû3~ާ‚ùGîÌÕý‹óÜ7ó]›óü¿â ˜pží¥Ù Â()›Ö×÷„µœ?î8í|ìýB¾ßð8—'lÇξöylñŒãMõî8ðÙ‰$¸chtlõµ\8Ÿó(ϧÝqògWÑ:J&x¦8–}÷ÖS8u!Ñ©Cùþós +ŽcüPqÆ}‡ýüy†ßÿ]K/GíËcºd|é¾Só]ïËx «_‹ñ¹; Ü[M‰ÞØtÐWõÛa8~ž½¨„r`\½y»q¼’²àdé¦cÐó¡æN/—`âÞv6Üteltwûâë %¼Q‹óMuP“á\\ì¼*‘Áûû¼>Jpžê7 } ÒÀŽM±+^É…G¢&\õ?!ý˜ó}qÚº ü9Ó]àëË!tN3–;mXÔù%î3l}}ˆ«Í:~!ðË>—³ƒøzRó­Z4¤þÔd°qªŽçB—Í»ûÔýó(¾+›Ô6®æ†?Ô²‡à?iCìÕj aKÙëý ûBTézeLjû<¶¸Æñ>aÕrxôì :×o6€ûQÐ^ôƒ„ŸƒáÕ-tS ñÙk&DÔí pŽNT³÷ˆ»‚ãŒcíñi‡ JU8—}0¼&ê³BB…Ù–i%‚À†ãªÝGXÙûÐÑu­…?Gš%Œ‡çï^ĽOœ"çˆÇ_¯špÜŒ]Qî¯WæÜäý¹ö*¦qMç£p¾àà‚É*Z÷tF‡T¹U:¬½ðlËÉfϯOƒCÇaœbç¦M jÐÀxö‹Ü×Hô¶ó lñ‹óìê´³T1ÆYoŒ±-nKzÿSÆX³hþcÚ'–xž)q-rÆÄýF}†Q¹ÿYÎãЬë*o=qŠÎ=qž¶¿Ž?ütëX?|VÌOËsa}… {–ýz¢WõR¾øØ“úh Ú/ö^tí7.»±¯ýœMœÇß#)~]â´Xóà(Œ©ÔMqrn.œa¸Æ‰G@<_ÿ ”Ð'a*ÁA]»cF 5Èš?Y–=ˆïÍ8λ÷±éÏÌpð“9dƒ×Mß}žçÖ*Âc2Ê¿Áïs…êçè)þ¾£ÄGLìo?oã÷Mx\XqÜœR}Ê6ÓŸÇÌȻ߷˅ʟªF¥>< Qî«îz\ ´×µ¤¾Ï×Ägy]Ëïö»ªšyæqsË?œLÏ­B¹È3ãuÚ“øáœ¿è }¤²ípÓóѳ}¬C}û>¬³þ0¬¸rÊò8K%r´~N÷Ðì]3§ß§÷ƒ±KÅEŽ·?¯Åç«ëjýÀz½´?«ÏãçéVê§ê=¶œ†ØC½Æ¤TÊ…ëÖ¨ÆYà ùÕ¸šÑÞ0½Ì?p$p>RCû¾Ž½¬-ªž_­ 8£‰žªö¼q±&é³nJÛm.; GÇoù¢è¡ÐþB\á0Ÿ»HíüœÕ³.¾¯ÁóDƒã<¨Uö×Ýs~ƒr«B,åó­{½ôÒIïu†&nÕoÔݤ8Å*·-SÝCW¯Š•;lüÕ:Jã)îUkv£Ô8©«ž‘k…ú÷=‚žþúêQ·dàÔ÷Æ¥—m#¾îm'ØðœaÑpqTƒ «ÎL¦û2Ÿäœ?ûQÎ92}èÜ…sâM8Ï4@ü ätU¼ÿý²’ªõèýüÉ!˜ssÆÍò Ä}Àn-¥@çäöó±/%r'9W[O<¾qžñC_uÊõ,¸d³”Ùk…1žÄ¯Í=D¼Ý: ÓÖ)±ùÒ(a#+OU‘c+æØgÚPþ—¥¡ù¸VwéDÖ)9 kž—ž´È #—l~Yßr^x’þÇäÕ‚‰s U%eqë r:]áü¹q%ÜJׇ •u~™8vLìüçÅ9oùúI‚óðûçÀ…ãP+TS:û4 ¿ŸË‘¥ºà:b|ópý¡‰ï—=ÓšOhà8æÎL×Rf/yý¢^kІÙHî`)W —ó¡ÑÈÈèyI#y]ÇyÆù§^½sv¿¯õ;,Jf…´ú¶zû ¤F왵ÔBm‚hßãj6,mùÐ>Ï{ÌÌáöu’ÈËa.YÒÉšw¨>óŸÇŒó ÛÚ»q^A*L•5–ËëXaˆcjMØpp“äì6Ú‡8aÑÕoâszÛùq£ýÛ¹O»R~(Ó¯¼(_žµª½ØÚ¨nüÈŠó>±´dÌPl*SuŸ“.–ïê0ò ¸Œ0->œß ¤ƒ+ÿVqÈ8ÑLk”ð}0ï\øÃ휒0FÛÀiÿ‡’À÷aJâLÉxþŒ|¤pϪuðôÛ P0yÑëË—îA÷¥É1íá ݯéu£ý”TL‚Wiñ°Ôq¬¸±Ï?ø­ò–ÀgZ;§sm¹*KT|/Þ7ï±ñüÁy‚ÏüQ?¢ÿE|»Ãûи{Ф®ÛCU̓v)çOˆ)pκDÞƒ²°ýGò?Ô‘Ëû{|ÿ¥Àyö˜"¬¸vÞæ¸:ê‡Üƒ·on^˜Lçˆ øýÙúk}Œ8ï×ÕÞ»¯®8ún#ÀûøëJ•Ÿ7†äÙùûu›[ÃÖ»yCäØÿåùƒóŒ}|ZÙrä%Ð.U¾ßƒ{Í.V¾™• krUeVOT@“÷FôóÐ |}&غšæŸaë–Ÿ6Þ¸ ­ŸOÏwô„zmÆÝÛ¨ühÌ–îÏÒÀáéÓ|ý¡Çy´ý<œ—–º ûOT÷t*¿ÿ¬œ ¶ã雞"÷^({|™Üè0?OFÂ5ÏUº¼æè\L T·Þöm ¿„ôI{;μè<ÿü ~/„óØŽY¦^†e‹ÖÍëú<þ8¼·äãEÉpz×÷Õs³½@1Ò¥ÑûÎÎ÷ øyÿ`0ï‘6ld }^U Ud·Þbs]a†¡B߯îå™qž¹®$¦Ý¸ ŒnXåDLµ]¼L†3A·Ne”õ…\*æOŽ,<;¦lS/aŠm!Ðj~8~G¯>vþ§ïé¼–Že weU“|á«çÎóÖpm|¹þW À¨Œl— ê%/Ü\=&Çw¬ŠŸïù©{’MÄóÊ&Û»6x|Æâ}_×›q¶ ®™«üsÊyìºÏÙMýè9¡$N!¿¿ä0ê‘¢c¥F²õYW`ÃGæ@µŠwþrê,qKày&Æ{5®£ž(ð{Eoä>‹ÓÓ®?í ü¼ª/¬zµu›‹P(¿lÍyÞÊ‘žo½Á¶ÍÛŒçÎóéÏEåJw» U:ŸMôÌÔ±¯J\Š8@ül\§a”øXc蜣… òËdž¹ä`8¹tM³-ûK¿ð³üéY•_òN ð¼æ÷Š8O£“·Öm¾ á•ïÇ—ÍóŸ:,,ýr?ÌŒ øõaÅè™\;hÒ°IÂÙ.M#Ïvè!\ Ü¿nnF(ígÕöóBk«U{&:BÜ/;—ô ƒíuÙÁ5õ#qžZë 3F¾ 㛽L,}ù.LÙügŽ÷üýPë̉¼Ü£jè±¢GgŒ@|p¡î½­×¨<¼þ¢¤×^tOiº¸Ž=iÛo7T‘ÑæÇ?yBÝgV=¼S çÏ¿÷rå© ÔSzÂñɉݣçóy¬8ϰvýý'ôO‡•?ßþ˜ ÉÛzD,Ü{Zu—w_ÀNé–\™(ð××Y<¿ { =èùöÞÎ…û^ü­Ï›¨GŠW³1¤ëî†7ü-.¶>³¯Eô>Ý}êΡ‡:Á6ü¶]/ ¨ïK÷U|@m`ögßÎÛþÉÎÿ¡Bõ#Ûwvþº¿.Áyr×tþ8pO:Ø®Ï/͆®ü—– Úg]?W˜ùº 4Ï8ÓÙ¼¬'p~y7èØ”}BCaÁ”SÛ;ND뜚ð£ëÂy£¾T…s­–vívEYkqˆæõSóìô4Þ9‘-N´K Ɇém¦>ÙÔzhÆWÜPÆ·›}ÝÄ×aMé~ÕZ·€AòJ[<Íêç9ƒÃ—åM bàu™[“k­íÏóçñ5ï]Rßé,98?pjålP­{^k‡Ó>hÙ·eÍÅ7{Á†é¿{NYØîåÖ ª!üÄoÏÓ“bþ|hlçØŸï=(çOì‰ð úaè;9_½“óýgq¨ùúU‚ó¬?Ñævbý 0FW™c¾x—ªRöÀËüÅ7ÇÕƒð™Þ5ž(ˆÏS^·}èyÞÓ>-îq¼<”΀LüöƬ¾ e¼s}ˆÝ ã·~8éRQM¿ƒˆøýºnàÎð¸?øˆç9<®qœ¾¶EŒ«>há¸ÈÛðróÐ9+…]°ýĤ…å B ÅãÀ~!Óê+ÿ—×%Ög[üâx¥ï1ãð `—ó[߆>Õ½QÔÜÆ1–Ê÷Á¢aÜß=Gàϳð"¹]]×Ò# ŠeÝœ݆Ø÷éâ¹3ïÓ†‚øÿÛâçqûáˤó2 $ç]xþ-hm›ì²x'TžaO¢‚ã{/λ(Ìøýšûò_äNÏÒн—ˆÿ²Oç÷bCáæû\I‹|3Î3èn줡»pžmÆ9¦Í·`aPàåGÕvÂî‡Ü;ùyÓïæ üþK[A|½b߬è=+ŽWmÛËvßÏ€—µø¼êtmèÁ½‰PíiÙ¨¹m{CÛwæüü«›@}"àç  ríÝlhCáÓϦVËøëuˆ~¤ÐDÀçW2 S é ~¼uöy¼ß¯LÏÊ®åEzØÆ>Q}¦@ë:Á¶ì¯­!>õà<ÝWr~ïþœÿDAûÞß’à<¶k6—3àx¿3“gY`Ù¥Û%Z§í€ïJWjÛ,zîËΩœ&P?Y8üâÑŽŠGzCw·S‹—7 üs&OÿØ'kUD¾œ¿žNâ=6^ßqžr ÿ’>ÁyVX»x…ÑÆüXÐj4Ù~Ô„ñ}é÷S3w—ÆWU üuzA»”ÝÍ»´ ß}É뮿ó{^oä|}×…ú|uyà<VÓ3 kÅ9šµÀ«Q3O·‘“+$yêÖ®­p þ"™- ,}µFÀí΂x>'îÇx?¬PÞ&&ödÈâ·r±N]¯èqžÛž¸ÝÊÇ#g–”®kÖ]q»ž†®°8OÒ@ö󇵳©®µÄs¸{Qã‡L9:Õ>ÉœKìïGì§²ÛÈmZðu«‰ÅAÇnß¿~–ŸYûÔròW·o~zÀ6ðׇ¨Ì_ÁÓŸçZç=K ójA¼×?éæ­ø™ÃUtö™|W“'³“ʼ”óûÞžÀÏ;øçfÆynIJv&Tõ\÷“‹ñ&,™íØgCÏ­0 ÿÀvÖ®ƒàÝúËM‚^Ü— üwìûG^×nÈëA³ßÏÉïËù÷è!þ€¯pžßØvÆ5®]^6ªìPœgÖª /»l}ýTŒ;0FÚ@ϳº§!ð~œ bF­.þMcúýRšœ¯û-ÄÍnKýP)ÏŸ1AlY]=Ø*£ZË›Püuw„ßf˜“¥Í©v-.{¦_K\9Wð½#ìÏØ"æ{:wkMçjÙòOë; lï)¯#Oz (Ô×ãë0 Îs®ê®±íkgBG£¯~Ї,(]åâ¾37ÁéÙ&庣¡0ÿœ ~¢s~O²£ÀÇ‘Aö›O‘›;5§ó×,ù(¿#µ'¦Ý_ý­ŒÏö‰¿Ú¿(pžõ.SµîM3áÁö±øŽ³ ‡m«Ó7BGÿ¦¸ÃCóÎ߯úèiÝÒCàqÛÈÞgæßýÎ#CÎûqmŸÃ5äùƒóLXu&¬r»L¸ûýÁ{·dÁÏk>¸'vÚ»hcîF@è‹RÃçÕ.Ø~žP£· ¾ñ¹ÊïäÊi]óU?]ã×.y> 2×RfAÑÕÙ hîBMÆU°¥Mà ÿ=/û9¡_ã½+ÒûÒ=¯§r~Ïੜ߿ìE}#º×ƒót«;úp&„®˜úÝ—°¯ü±ŽÛy'¿¸cý ‘Þ¾ÐcÁÊ.Vòó_ ŽÿÌTåf«=™`™S«»Ã èi»¹b²R”Oô]¿s¾ÚΤ¾~ºÿçEÏCOàñÿN.þ“ŸÿøÒý<:gÆyVhÆz¼Ù™ 7žV›µïäuðZ9¶°÷7K—>I G ïlì›.lrÚqçV¨Æ Vú5ŒöújŸ®ÁqZÿñrk¾)“ö+×á¢ïä#a˜×o®½Ö†Á«¬‡/͘.ð{G=éœÎ ø}~ÿOã쬓­Ë„;ѳǬ‘_—6ûö6ýqTµ] ‡+êûûÎè^„³k$ïÚŸ&öÏÆµšÔ¼ýŽÇõmUÚ³¾Ï„ßKïyýÝuh¸0þèúé+àÃÍ×ß £8ž)L¯óðø—¸ ¨ë¿tÏêisÀÎnÓì÷ÅÅsᵤ«WÅ€¯Gˆóð}b&X×O›u)2ËÂæ-ƒîs\øõˆvÝ_|¹ÞL½ \O[»0X üþ?ÖéÏôO†x~ñxîDCý/r¾î sa>ç±¾m‘w'“~¯‘ —/®Õ~±*}4øÍk µš¶Jë>‹~—-$Én'}‚A­±Ä ’s<<ËôÄW?’ùæËÈ£ÌØâŒ”_û’™‰'Îø?IäEÆüVÓ‰ù£%ŸUy¥’¾ž8ÌWUOlÆ4‘ç=óc<ŸâÌGññ$ÄkI!–ó³’Ïx,±|˜‡ª‘xEýSï¿cºh‰ ^Ôã^G~ã¢Ï”3ù‰Y‹°“™ßøWžSä™ÊØ=)( ùKŠñ{â‰óX”ßÃüÄ Q,©Äî1RÑãáå»ÇT„ÝÃùÌçžØ=¢·ý·Zû­ÖêþyµÖ™þ \ñà˜·”˜Á©ä¿Í)y%9ÿ1‚0°uäA© Š3±€­Œ÷F^“®ÄeQ¿ÜÇÑÑü(€ñž­(%&|q)õ-8ÃPd=»bУòP*bRJˆÙdFI±(Ä“¼´·ÉH\Ê¢Ü&# -*˜M¦",¢œÃBb6%a61&e!ã³Id|«µßj­ÞáŸWk%ôß™÷ºš8€Ìw]FŒãtGî·Î˜2ù%9÷11°cQÖRÜW=‰ø~Œ]œÇ8ì)äŸn Žc›QR þxòIg,˜$”+y£[È=žø|òB/¬Ä¹|Œ9ìJœ—$J-*|Ìó<%Áä1 Ò‰»§%sÆq1KXJ¾æŒÝ¢$~pž+÷2g¼'b›ˆÓ¢%ïrâè1F°„¸,)ÄÖ¡,ÄÌcåŒÌø®ŒM–‡’a‚ˆÁ¢&0óŸV+Ï•XyI”¼ŒœÞ€3ËD_jÍßp€'Ï©é¿x bäõ$WbÒ'¡\™W5ñ‹û‘3^™Šxeù(53ññÔŘe©Ä™eâãɰh©ph‹±YÑ¢ÒQÒ"Ü2CWΗ•bq‰G±›dìÖzü·Zû­Ö:ü3k­”^·ÕsWçÂ\‚3åË+åŒÁ[’sä]‰ÝÅøªŒŸ‚’`€ˆ_¡&Ž*cÄÇ£ QâTȈ—Ê‚_‹JgLxâ¿ç3V*ñ¶XBh‰?Á[â¼Kˆ­Åø§ L1µÄtgŒ ñNË]CœSÆ•P`"% œ‰Ýž@I¥E¥¯q²Ì()&Y<1Ú+e­É™XñÄe— ‹ñJ•˜„ñÄÀbœÒ$JH¥;ç^g“2D*JV—sR‰ye@å×ã¾ÿb^)0(‰µ¨t”G£Ï=Š%æzQãCæ1ö:&| ñH ćÙÜ,T>JM,R)1®RQ2, FâÈŠq®LT(Šr®L(',:”…W E˜IE‘¬˜¨ˆE*2®¨¸h‰q%²Xþ²?¬ÖŠ5õÿw-ëhÑú)ÖL±NеQ¬‡b ëžXëÄú&Ö4±Ž‰µK¬WbúOÕ§W›Äš$Ö¡ÿIý)Z{ĺ#ÖöY1n눢$gÿ1>³+Ö =*¥"þ„8ÌùŒõ‡_¬%%Î cù1Þr*J†uÃHД¿x1kØ—¯"N2c"ë@ʸÇÖGÄ0²Þ!;£Á H ŒšÄŒ7ëÆ¹ÂŒáOÜ•Ȥ#^° ÃHÁ¡ýNp1[E>‡’Øs®˜‹zTJ…¹˜Âö›ÄVᇗ‚’`°Pù¨BvVÝæ_l2Y.™–¸r˜k&”æš®û× ƒT‡²tþŸL†ye”[¿|[¿ü3×/ôº»‹ñHõ¨ÔœÏ]I(W f}IΉ—“‹qGÞŒ’b€Ç£ ¿çì-Æe x#¼•^†3¶G”±Þu( JA|÷BÆ%Ž–&„e©ÈÙYñÄq—3‹qA•˜(ñÄÊR³%’8 ,y´Äÿ,dœ,L¤$”+±Ù Ë “J‡²ñ¯RQ2L2#1ØóJÊ«ÉYWFâ®ËˆqÅP…2ÛŠñ;cÝ RåÎyVÅ™ZT:ʃXEéIJŠG¢”˜¸ñIJRb'¯S‡² þ=ÇHß„3ÕU˜ä)( &º•ÏØê˜ðfât2¾ua¾µ @<ª¥!F§ŒØUé(, &T>Ê£¿*8EùU (g °X”•ØUŒù(²Š2ºpvUJvct:aqÑ»ŠÝÜ×Ï ö‡ÕÙÿ-uµxM-^O‹ÖÒâ5´xý,^;‹×Íâ5³x½,^+‹×Éâ5òs}üOÔFV‹×Ã]ôù;³ˆeeçÄ#,,ÉY„Œ·,Ázg@å£ÔXïÌÄd\åBÆÄ`LEÉ0 ”ŒŸœÎƒœ&”qÍ()ªeEy`Àšˆ¨¥ÀU Y‚œBüãX”…­Í0˜SQ2âÇ¢¬?pÞŸ…ñ1¸MàFT!;{ žŸƒ= 劯C9·±‰¥üfâëQV¶žsçüa…ÇDy`B))4¨B”šX|L”3ñ†-(ÆšE9aÂèþ†5Ì|®˜LzTJEü= &–•RcÝ2£$˜hb «1áÌ()&]<[ë¡ÔĦe -*¥)ÂjclÚtâÓŠÌ6q÷X³PΘ°±EØÂ (gLÞX”%#†›ë•&³‰Úø­¯õm]èðÏ\*iÞ|Îo5 ÒKpF½•‚’`0Jr.½ƒ:ž8­ŒEŸŠ’a€)ȵÄceÌyÊ ^‡² Ä]elùX”¥$ž;OÀ¤3çÝ“ÏDÜS5ʄʯÍy§ŒéîÌîy£’Üÿ+ãT‡² ˜¨ñ( J† k¤¤U¡Œ¨<öï˜À)Ä7EYQJLæ$”+&´•‡Rab§ $˜Ü†&œá®Æ$7£¤˜èñ¨B”>•¸¦"ó’q¾SQ2,F*Zbšz`10 ,(…T!JÅ!UˆÒ`‘H"®i,ÊŠRbÁHB¹bÑУòP*,)( C1F¦3 1M¥XTtÄ4uÆâ‹²¢˜ÓR,ðeJ8|]WÿS½-öµüßô·ŠÖÀÿ®ÇU´®‰µ¬h ûO÷ºôÿ³~×ÿKm*Þóbõž±° XcòXë‹ú;Ζb]‰G¢4X[RQ2â=³/\‹JGyàoB9×Ù‚R`]I@9c@¤¢dñ(FʃCCLfÆ_Ö³3H vŸ$­­0@ØºŠ‚DËÎ1P$ì,‘˜ÇŒol`k'wÎ2f\Z•ÊΉS¬`\Z”3Qìßð‰SP 4*¥Æ€3£¤˜»ñ¨B”ó7%ÅÜ'.±ƒ2%ÃÀ4ÒÃÜ óUÇÎÿ0W¥¬”6åŒ9‹²¢”˜£IŒa‹9ª/ÂNB¹²^XgαÍgç|˜ ßúaßÖ=ÿÌušÆ-dßo<ÊR‚3ëµ(3êÿ°÷`MnÛÞ/vìØ±ÇŽ;¶Œ¨H± ½ „" MPKìØ±c;v¬3JGPDŠ@° vìØï˜™óÍŠ~{Ÿ½ïw¾rϹËçùo×Z[ç Éã-ÿŸ&st Æ©×ǤŽá‰MÙôY(Lp9J“<•_‡1ècQ:˜ðá(%Ê?®cÍËP(óŒ/¯…„JEébAÈP(s, 9çÈ`Èé>=‰=JŽª¤ÿÌ™ñ:ôn*–³âÃQ ”6‘=JÒãlxJ‹J†ªhÅxð2T>JB÷ø[3¼‹-U…²Ç¢‹åÜw _,ÝûÇ”¢bQUôŸ;0Æ».ýÎ*%ágÊG‰°@ÃQJ” j J‰2À‚•Ó³,Z{”UIÿ XÒÅ"–¡*PæXÌñ(tªe…­@éaqG÷bLw)y*J =†»* e€E/GisÖwÊ€¥M •’`3ˆF)Q&ô7T o>¨x”.6 ªeŽ #%¦…ªDÙcóP ô°D£*QöØH(]l&>¨TzÆ€M%¥@ébs‘¡*PÔ‰O¬Vé¯ÿ¯çiöAÍùÎ?;ÏÓì_Îsþoö©ÿsÍþ#E)P•tÏ{ŠûI*J{I `ú ²PØKä(mü ƒPù( ~à±(ì%á(%Ê{IJ! e€É`‚ɇҥûK˜Yt_ "Šî'aRHèý*LŠJº„‰a‚‰G¿÷ЉD÷èULŒ|ºG„‰M¿ÃJ÷¹±Ž%˜ ±(L¬Û,º„É…R¢L0iâPº˜82TÊë4%Â:BU¢ì±N(=L®hTJŠI–ŠÒÇ:áhTJë4†?¬}PY(LF9JkTë3œîû`mêc‚*Q&˜¤q(]¬GªeŽõa=F¡*Qö˜Èñ(ßÐï b-VÑs?úÝSLî81û®+=Ï7רS¢ßsö’„ïµ {Þškzï?Šï ßa­äûÜR¾”¬®µxžäÿñï¯ÕÀ~——K>Ûví[–#Du¿Íj µ/­Òé.D-u̹úÈf¸¿Út¶Ý˜ ùÙ ¬ŸÒÝvzš[rÿV-î¿'#ßbA¿6; ÒÒO,bþŸRŒ³pËIiÛ·9PlDGã»pÆnúέ¹+ÁÂzý´vó-A>£þ–çÕç?“ÏÆ ƒ‡ëÃ#FØyÂ2úc4wæþSÕÔ| y¬¿½•ƒ5~ZloÎâÈ0ã6ä@ã7ÃãÚuº ïVËàcêŸty¾ÚöÍèÜ<’0ÿ*}Xú,²á`h`?³ÅªRku:žÇSaªq¼c¯ºWz– –•Ígܽç´ÄF}(>Ñ';‚0×`>!. p?è8 çCÕµÉ >åÀÝ'Û¶&dCiÛÝ—Ž^{“Cüߟx¶sˆàç®ô² d›¯ÚEðߦã)q¼– ¢}ªç©Sk¬Ï†5c¨Æ\`~ànXbÐ3#r.|›Sµcƒjñä~#îjgÁ8¢~ó½ÑšY.i¡2¤Ê…F¦}7M2ˆ'úïÃÏ ù”Ǧw‡x€ƒÿƕɅ ók´¿Kƒš5x:Aà쨹g‚?ãÚ›ãµp¿ŒS øâk-|=ãb-ÏÖÉm×÷µ¢ŠÝáJÔÁa.{@©²þÆ#‹’Èý¶1×9ÙßLˆ£ÊkÒèöàûÓ¾ÝËÉ Ò² ÷£žs›¥KJ²Ï?q‡‘¥SÖl \DÒ ŒÎÇšBä§©‡ºß˜7C•·8Î7XþåŽÓðà§Î…k³ÀryPÿíîÄóþñgú-ÜàÇ EÈ›²EdÞ†‘þÃŽ§Qs·ZzÃå}›tŽõVûñ¨òÇë]’:꾟?ï_š>È, ¾é^óì;%˜øˆØóáŽ3l¤TóÅDðõî®{{H†Œs€æ¼ÛüÈ5Í>ªý}ófœM9Æyíê0ݲ^.,ÞÿøÞðÚYºîÂÈó†á¤Ë‹þV¯àJvÊ\‰|1üB‡ßm­á`ûklˆ›ÑÂsÜ?ˆÇ7ÙR\üAÌxTS6ôñÎ…Ä8´k4ù™ÝœœÍêFÝ÷çQËÒ6}û¾#;HСàÜÅ„ùT‹áLî’½v"p_6uU^ãx¾ƒÌÆYc?RÙ·u¼ÓÄ‘+Ô^DvZu½|³¥-´UÆ·îÑ~ÔÎ –7§NeŒ×¨R.ùùšéåÀI“Iæ oð–—&½K]BÜ–j-í8Æ´O¾«yÓl!¡tÛ¦{ð|ò…ÇkïŽlìÍ9ÜÌWQ„ãÍTGå€ÊPò6\8³öÃÙ}ËÉ}[ãg}#ì øõñðz2"ø»fl9¾2hc0çW„€à›÷s÷YéhÓbÝ—ËV œÈù˜œóŽqίÎìu8Æ7½¶ÚýR&LéYoʸS+ÉG#ËÙõ\¸ÿè<ÂüËLA[t~ÅÆoÎÜonªÚ÷HˆÇø«@ðÏRå5ÆÙivæÞ“M9°Gtºx´$&UKŒŸõc ‰N¯a¿À‰×C¤Úg¨¢åå¦ ŸOª‡«>…wš¬Ž£ÊkÒÚ·Gä@A¶Aî››Ð_~uðóäh’•¸tòÉgð˜s³ic½H"p*ß§vl0ê³èõŒÓ:Ô€q8ä8NË¿lÏxäÀ«&¾Ë¬-2 n£ˆG7‡ãO9õÆö^û^q%’pŽ$”S»$?¸<`´ÛŠ:ž¿óšp¼z—¯à˜WïI¾öåì(þåo.ßDnŸžÙ¡fŽ+ Ój«Óéä\2ãmåî·,àÐN æ¶oMx=rÙ-X|N§_¿=[ˆñÖ ’’ÚRPá¶ÚÍ#olçdc­a«ì@+-´âÃU'uŸ~갿þìÉœ‡Ëù ¡åÝ?]ê5ÎÇ‹U}KJ‡/ª™MØC¤áÝÒBæ¹Âý7J£çá}e\DsΡá>E8NëG /§Ý…`jW¨“Íî¯öî½r;IJÇÎÐrYê³_æEÔXËÊN·VzNQ¥BëærÆE“à8§ºFœ|“p†ýXT—o«í›ä°“¨ì³bœ ì¸åœC[ÉÀõ)ˆ|;>òªTGCµß¶*/q¼Ž»»á£ê.4 s¡kx¯|3§ã]6ÚŸ8|Ú–—<,]g™¶fN”Ñlsî/“'µrÿhì?ç£oѪ„UâߟGÌ?J†qºRû²ð»ð}… œl$‹?fL_úc]Ï4>ÓÇf?¨¼òl‹óoÏ5­°rI-Pë.äÞk¿*À2Ž}=ôµÔiyp¨ßáý`·óõnׂÕúN¶ –¸)f‚di“ƒùÁ ø± ~ØY®Mö?5‚“¦»Í¼Áò_„q˜¯Ù]H…AQ_&C¶ë¸[-†ï'·/NÉ_çâ‹R.Înøu&aó 0m<îØÆAÖœOeN­ ËeïÅ—¢÷õÜr὘ù“qN7‹#Á8޶l4ÿš ëüOn• ›>îXñlì’åÞséšîඪX|,#”Ä¥N81®c—1¶‚¿›G•×8^Ë%ïÄk˳á*ÅáuLñž‹Þ“f$KçWÓíåè kgÑ7vYô0%æÊ)ô/ýW;ž?oMYÞâ8Û»Uóõ-ȆàfÒ'’ DvÚ#õ¹·Ãcåâi »(Á!ÂÉÒ:€êMtÛP1Í“ûäz«¹ÒªüÄñÚ^1ü˜§È†ãZ¥%IP1öÓª.¢X²1¡Ç.ýiðYl³´¶{yߦ`ó7øR8hbõÜPøºmNqgýYÀü9߉™ß¬1þ™ª|ÅñÇäÎӲܑ ‡z¯¹“§>D—®÷>B2÷ЉŒ%¸èIG†‹¯cS#~¹÷}Ws3§åXˆ#p·4ýš•‚6÷Й• g{,ÌžG×ÈŸÖZ{”ôI>Õhho7Ȥö ³f‘ÇFãÄ•ûxðâ¨òyV¹DµLpʆV^{wåôIƒEÖ+Û¯9FLªû¼y:ÃÆNé}.Œsô¤°Âpï´b0‡¦ÊW§uÈý¦ïÆdC¯Ÿo¢êÝKÏiÏ=]}Ž“=íjE-r€Žý†¿ÞÔh™Xóè4)ôn6ÿÈža60¤g­b—»öP/·Î°ë;Þ‹5ùj÷ƒ…ÑT­6ÙpaÉܬ³ Сùðg{ oK[½óèaæï;|ê_àˆ¿Î lºz)Ô0ÖäùH1Îî F5)³ &àð‰&7nÂK£µv”ž ¢êgŸÞ´¯kºé+f…‘ÓÌ[»Î–󷬡ù©3zv°a¾#̧„yí£6{Ò~F°ùª ã<2kÅóYðqiôûŸpšyþܼù$¹W5m­Äž× ÎPu_ã¼>5§Qˆ£Êko]>nÊ‚ˆÙžÇ‡þºãûfžÐ±=EvûûeϹ3 óñ¶çÛ\ìT§N¤Õo¾Ú ϾzÏÓ~‘YÀžw7ÀÎ&·–[³8²®çôÆIýœÀ´—UƘ¬`âr©d¨U'î«=𴦙nêñ„ç’*oqܯ謹Üo…ïï ¢_¥7`÷2—!K—Ä‘³3(Ç™seƒãÚJ­e0»qÖ¼ágçq?ÞïjÞ3ó··ü§¨5×±ÇNŽÒ- ¨»lµ È%?ÿGùô¾Úƒå­\ù`‘£;V‘WuÝáŽ|ï‚E«¦ðï§â«ÎÚìÊïb Äø€|‡qjÔ—¦+ªeÁÅzîr?Wlî}õÖø¹§‰ô{Á`§Rø5vîè‹AœCé¡öcfÜ uUžãx½ûÙó½øØiúª¾â÷†¤¶úyš Lx±g’‰öeyݱ~0ñT%¦©×ó­Ó:ìõ2aó)ŽÓf%"ÜÙ˯×`|×3dq—a²bW¨ú£`]ó`²ê¨ë³¹á^PI1@ífqŸÏ`õs˜q¥9Ç©›áÚ)äl*výSÆux·oÚÆ3ßÎé]Ö٩哆­#}Dd{‚Ë$&^²‡n\DÀ—†æm|C¡]ƒ¬&ÖŸÕóÎ/ÖW$lqj¹ä%V¿ª…÷5 ]:(ð,1«µpÄJ]'Î "ŒÛ9 ®æsJl¼,ÛjªæÞ1îÈg1ãR˜ÃÌŸÝ›wV²8 Œ3{©Ø>yÙm°‰2~øäÙUˆZãóV7÷,YûþU³Oûí f»±›·"_Ï­ü™•á¡®?¡…8ªüÆñ˜ýëm¸Ó|ĩӯB‰žó¡÷9òÚmefçÖ0°üd·Ç‹ ['yðÏ2>M;ŒS£^.ÙK1B©™ðèg—œ½9W ~!½~ޤ·½²ç¤©%X/mÒâ¨YŸ]Í4ÿ¢‡Ú7^ð1?ØcÖüÙþ€Ç»wnÕôC‘™¼ÿ_²FýUSœ#¡‡ÚÔ‰Å|ê©ßØ£=úS£¦—ˆmÜ@Ü`¯©»é¥oSÉB—ø·Þ0[ß}ÖJ7°Ü¹íÚcnœ?Ä~.)ŽW³ƒ®¤Ê<PìÞ§ó0ʦu§­/‘U},8ߨNm=¤4ÇŽï{Ú‹›]v¬™Æë˜ú{qôëû~&¾3N•!穱~&Ã8Ÿ3Ü{t= ,JR²Û9œ‡IU’üj\&ûK=lz>t|ÀÉ›¹ç <|`Ó§Ú®1“Æã§óÛ/~þÿ¤[9—ömâýãVµãÑ= ®Ì¿˜áï×ä®çe’;åußol9ÿËŒû¶Oçûºã9ï“q78Î'ºÌ8ž ëã)€å,*y=ëÚeÂü­m€¯‹ïOÿÃÏ«ÊW‡õÛT¸Nñ"gÎBÀÝ.ßÇ4»BÞÏeh²ÈFØ7'lí Í,6Y6•÷_gõû«ÊË9åóÚ½¥@-º ns~Mw8QÝï ù1P·KÓ¥¶0«R'¬1öÏž¶Õl{×Y01Œ’dCAàô7·”V›dÊû$÷½ÇñåVÑ{ÖLMËF^ͤög ß’Ðî©WÈxÿú½jØsŽbS~®àù:“8d’z|¶^4ý«'ÁñãÓ›iLJ†¾¦ÆÃcNÃL: ês• é¢×%ÇÈ‘ÏÛ_\·ÙoWcÓ2_Η㰱ÏYŠã|ìo:måðdЛ¿ú’_ÝÓ ÚNÝv•ÔVäfS9×ZŸ,Ù±ºWB¼ç‚K@Ø×ßÕµúœe-Øû*Ãñ>uïiŸ”7ÊéËj®î7£Ñ5ÒrN£Y¹¾1ç¿û«ýÍNŸ0žÐÇTùˆãÚÝ1}ÓÖ5 R¼Ï,Ø];bc¿n¾½ü¡¤ÆG]`á\ €5'[Ä¡cæÝò‡áçRg]ßã ­Þiðwã¾ûoÕ}ŠqåUãÛ ÎûyûS"´nø`ç„“ð¡ü–×ôê×Iuãê ‡†¹ðõ-é0tý¶úSÕÏ¡/^\×]o]ü[±ð{ɉ>Ù^G‚ÅQbœwS)‘(>]ØVï­Éq¨Eq¿s®“Ê¥ÚG;ïºýls¥Žä‹Ž5N©‚`Ã3 5âVZ‘å’'žmÇ4H„i?¼Ü/ëƒñƒ[Œžøâ:¹ÝyFós]@…ëjïLļ, R¯C59Ï"g†×‡ýe›øûu\æÎšvÕ’ek P·‡Ÿê¤|s!ak«¼(ž⓾?Èòúô·Ýî©~Þ°çŸ'à¸ëÝZÌðìgiûm{:$ÞÆ &Dà9s>&a}$&ûúÔˆ« óTPÁêü§?m«˜ñ œG©òÇg?ïMx³>Óæ}»#pèUMù'- ¹ýòBœ°ª—¨Þ¸ƒœ{  6§šÌ>¶Ë¶é„˜Þ›5Žølê÷c¢åt¨ÝÈçY½ Á0þÝfÉ„—s »$¶ú@Dø…O´`üý¥wNqæ¼`vŽ$Ãñ|Æè½yäs¾¡é{¡Ý§÷eÞ ×ͳôÝ]…ñÄÝjRb‘/H&Ý\:j‘Ðí¾¹ÞêÏ[àe–'XÒó@püÀªâÐ[.A‡CóB|”ÄjgWw¨{“l]¶Ì÷M•“šûëmm̶ùÁÔô/îo’ìøûh¾‹§û±Î<­MA8'Så-ÆÑU-Ä/ÂË•­Ë^‘Ãôƒçu“ÔQ-Üí ]WÃ/‹ZvC]XùÞZ~1™0Ç„ï7›¨ã¨òÇ )´šiöò<ŒWèö€ƒþ[c ï›dàÅ}† °RójT¿5õçÏ-s8¨“Úmí(s°oùvl颷êç8û|ùyñ¼r‰ÿÜîßžN?7~ÈêÔÖÙ >»ò&aœŠ)ÀöÉGÀÁ«¶cg›úƒ°_ÁÖ'V|¼×âQGÍ7Þ)«³ý$C¾¯ß‹å/ÆIцpÂ6êÖ´m´ ¼JƒæÌŒ½I,o‰¢Fÿœ$ÔxäLè=ÑŸsBGóy pÏK1›ï¼7™Yò+¥î8pª6ëötƯ‘`\Oµ8ÃÏv€kÅ…g¡I7‰UCÛÏ]ä“s7RUjçúñçæèru´vµ/cÕqTyŽã5üÑ [ùiˆ¹æ?2`M ô™¹íñMò³1%›CÿGw}3 †¨6Ný€>å›´´»ªÊo‡ñëã€ñɶÂΕֺNÚ äÁõ}|¦ZÁÆaŠŒÙ•Vê¼Ö‘±µ•týÐìv`ߨ“³jÈÅ]oYŸÅñ‚ki>²Í)Xöô »‡ËVøÖbîcÑÀ2®A/éÛ•¶ )òÍk]jïâÌý¶Ïö‡²øÕ;Úø¬….{û>µœºT½ŸøÚÛ{ùɯïÄ_™ñ9ù¼ãÌÛj¹uµÇq9Ããl—{[97lzžƒt)è5’—~ž1_½_"œÇ½èP0uÈ ø“wèô5Æ1Tbœ°èû‹Mßn£öÞß“¸Zö¿’a“@:=k™ï¨ŽÃ8·ÆÂ|›õe?ÎfQRYƬZ m…>_µjö)‘dŒŸÛÃëš=Vújt2‚Aà› ûÒª<Åq®Í¤€Ép6‚®Øb ðà}/nI$ö×ñ/o·ÚÂYÉ•q+«YÂ_Ù]óýAjþ!;Ÿ Â~…*q¼E#žd¯‰‹ÕqWv t¬]s‰È"‰´(Ð](±vÿÆZ¨&‚ 쇰sþéêñ„õ®*/”K–ä%l]Ñy@ŠñªA˶Á€æ^ÑÁ‘IDÛðÑã÷ðFñ'cÜ`ÁÍ‚ÓúÃüág÷6ö‚%‹¯­è4|:„Zé²>啚K5fûiŸ#MÁþç\§8kÆaoÕFìXöpí°/}·ƒÅ _ãl'Rxܦ¥ßÓMß?0 ¦)e[NJý8wnÜ$Æ;|¥î·llúÛý- ÆIªîš}âþ8,‚¶—·ÃÌ6?ÞÞO"ûÚHêÎûêÀû7œ1|\pŠŸš Äxà£ÕqTyã±ý’H(¯Q}—éŽí0˜.‡'“áK–¾~õÖ™süù=0Ú’qúê11Ï+¾€ãýøxj…¬ëËÐëͶ¢)ɤ¾‘¬_Ø )çÒ{ÛW àüù©°@uð*…ˆ%+>~ïÈòAŽãñsr<„àî†-»†í·$“’˜/Ó‡Ù¸ãb9ÃÖ—svõ„ýSÛÊ 3áDÝ ZI› ¸tü‡XàÇ®ÜmRTel>È9•ÇkyÕò¾£çðñ•dóK9”ìÎë’ L&+‡kp¼ç¾º”üm »7Ÿ{Ø¶Þ õëh ENUß·c\_-5·•ñàX%ÆÙ7¦Þî±'—‰ëÍøKIo¦ÞvêBfŸ¹9ÂÐ]½nUaÑZÁ‘É”\î ÓÿØõ*ÛIG•ײr‰i³7ZS£HÑÍ÷–§ì^­µŽÆ§ñíöÚ´»&åóõî°jùçvökƒø>ÙtõúU•·8ÎB§Ùï­—¯&ûÞÖ-¯¹ Îî5Ø—B†´ GÃæpàðøòû*þüs”²¼Åq]„V [ï!ÎD:Ìßé¬~2)Moi*É>{îdŸ>VÀú¿7”EÉ÷áÏ·Ù0$kîíâ“Þ0súÞº—>×bù‰ãYWû™çÒÊ6šuõ+§ç¢Su<ÔïÞ˜®¹#jƒpï€ís²ýh Æ :÷hYéäÃäÝ–cw<> n—7<´™{ŒÌXãÀû·=LiÑ´ét?¸lÚ zïå^ šµw„™};¯LûÆÏ›´àÃAúÁ:ðÿÎîÉH1Nâ³®NÎfGÈûà€Xݧ‡Á.¦õúÅiämhÕypsäó\c˜½Øç„Oˆ?œn1¸ïé¾ê{9¼Cà3Þ£ _dz>%Ã8—?nïÐ|ÓQ¢ü¸í¬øI,<éyìÙœ}i¤ïã ó–;r¾å`È1Ë|R{\캾û”C¿H~$T=ÿúîú^+íЬaÂí£Ï­Âæ!ô};½¯vŽþq~.8ÑqîQ˜xiOU^a÷`€ñÞ‡BÊNï—‡BõiV'+ûÙ*|s©šçZøsă³VUbáÞQ5ÕãÆ*0NZõîA/O’>;|ñp<“çëoîR7|üý¥®öpàfþØèÅcáêójï£{Îà}}Tÿðígæ*+u¶cÝÛÍÁRdœP%Žßà¹Sy¨C©ùxb¬²ò(¼¸ÕW[2,4ÖòÒnrÙ¶-L¯úÜÚŠ6\~pá ~ÏК¿oN¬.•K,Ï×,|š|î\¸öÈ•£ð&Õ>à§k:ñ˜ãí°3ÏRØ×6„{£Œ_ñ„Õ()ÓúƼ|6ø»Xóõ‹p\ÿsí*ö^8M6DÐçc`¶'¼gÖ¢tR}L7«£Ï¦¨÷Å–ï?:;>§DÞK_²6ùîY›Ò Þ [o¾û§xíµÌìm¾ßÕ篌[Îöµ$g\›µÏG+WíŸÙì|:™e/O'G?./›?¦<ô:½Ÿ‰z½?¨lë×+7>ÿ•r^ê/1ãcÿW»šóóe¾O‡q®¼}ïÌr5”.lOBôMIg/§½3‘[?›òõÅd°pçCSé Öâˆþs~žo®Ž£Êw¯[z‰åíVgI¯t| ~Õ°Øir/œø–26T{ü*OZqj–5ÖÛõû:í¼Õ÷Û šå,µÿÁîÓÊq,Ö¶Òò³$lÉ~‡kÆñ‡¹­´>¤¸7Уmu X¨"»ã’Nƒ /éÎ`ÈL[5Iq × S†tœ›ùYü)Kl[YÄÏïp\íè½u6Ÿ#ßn÷ŸåÃÎLëÑæ±ý¡åxÄÖØ|Û rü;,¿ñ}ô ϶ üŸh¯>s`ÁÅ'©3fUŠ…sga_bôî-ãÓg]‰q†®Kþ¬wž”<­ò NŽƒãKô|Lo‘w14@‡[lõU»Üæ ]¶œþp4e6ÿ©äÜÞJ±p>­És×Z\. 8ž8ª÷ôó$¢FÆÜ:p‚þ o‘aR|Í.9ÃÜF}ŒS%À~žéêýv?ÀHG•ß8žýSÝèk%ç ýDû´Ó0bPýŸ©×o‘p]Ó`î°ðNã.KŒýø½‡é p”lšÔq`Ï% Žc>Ì\¹kî²]FÎ~oBÀ^µÀ WóÄÙs¡R¬«ZðCÀ°Ã&ïá\lŒCg‰Í+.«Ìãeç΂þ£© ff¨ÏïT×GZ8Áá´Õ7»øÓ+·þÍã&ÃéAtEk©Þ¯:â`k^Çç­š¿­Ùçä‡ÞÆh¾!ž¼4ù¸ËõÙ²èÀ¾ Rn§›òj”-„ôK²™Vj]/\©Sq×QŒó *}/ §Ž£Êko.~­‡]"­7l\áTë<ìÚÚ?·vF) ÉèÐp’ÐÛN OX‚h\J£^Á [(’,[;šŸ0¬ÇɈ ?—HÊ¢›òIûÏC›õŠ{•DØïeû+–àëg¨ß¨_p><ôJZ›¼w¾1ùªÊÏ%å’mÛkÎ0Ù™ˆÎÈvÂë´omÞ<“œ2?ßcà õsˆžÚmúè×J}R?0$æ½ã 9·J´ÇüxÅç¯XªyËDõ>¤*o1ÎņûEás¯!=ú¬F.À¡DOiÎðL~?à †wu/ëŸ߾ œë³ùìõºTø]ØGaçœqš©ÚWIZF­~ë†\„]Ï õ¾{d’/c›Öi3Ϫ.¸ÿ5ßâüß—úÝ\kÎæÁR§Eô'P8^#ž£&tµ÷"t½nGïM™d±ëà0Ÿ}PuØzÅ™‹žðó­ËùeÞ°ié©SaGÆðsvŽ#Ãq “ç\3»NØý¯xØ"{?z~z&qÚT Ú¸z*8ü0©ÿ/¸ô­.I|ø¾œ+LšA¿9åŸG¯Ù|Ç›3øÞÆ/Æ„ä›4]Ò[–Žùð«ÖmbéURë`‚‹ú^g›/W1öUßïc\èé²eå@ÅÑ7bw¿õÉ‹ßOzªþ¦®¬c_ßG×® 2{²‰_ƒ—Àds«)ï o«ÓàÕgWþ|󎽫¢Þúªß?a}Ï8êoÄÂïlÝ=ZüØ»­Ç}G‰qü}—Ì?¤ ‹i:§]‚ÁÌ»-½MÞëMôð­&…Ç>k諆µk_^§£8ßœ­´––KÚ±/(§º4*\†õ²I¼M&¿­xá¢þþM[ùà\½®ÓݧýÛç.Z*p›o©íµ‚íN]†ì¨Gõ†kÝ!‡~®:íübªúû Ý-·g>Ò÷½x{‹ÅÓíÞZ¨+·ç럿ÞWU^⸪ån“›ÄÜfïBû¡W ÓÎZÑÑÃîYÆ:U !lÓₚº^°~¬CÕWGoØ’åÜÔ{|(ì [³¥ÆèP~¯ï¥X¨7!ߨý]VgRŒ£pþôÂlöM2g5®Ï¯€´Õº[¦ß!ªiÐ1;¦ZpyÂøy Ûº^üó0TŸï±óó—báwÆ£ìÞ;‹#Ã8Ê÷´áÜ$5Om9èÆU¨9ãºÿ–;¤¦uqj™ ¨ÊÜÈ]½®gûº’ßöQä8ûHi}ÜàžcÌ5˜[÷²û‹ëwÈÌ–ŸÎ%¸Zgyõ­)ÛÝàì¶q–õ»y¨ïGüvž‡ãØT ºê°8\z4yþ*³ëpðr·’Cï¾K?Ém—[‚Ýí’Ö6îüýrWïÏfxd¦Šf[ÀK¯Kg}Q‰ã9îÚ¹7þQQm?½ û~ïU¯Va|v+õºõü¡ˆ”ž#Üù÷#­yý[¨Ï‹„ç«CS~Ÿ‚=Ï´–•KLU_¼J$3Ûì«·`zeVZg‘ŸszMZrÆß?êß÷¹¿¿é ïÙ,tl`¦>‡eç!ÄÂ}šèÉïºÌOšÀÿ|–×'×m©“xC"¡4ïfÝ0R²J«ú€,Ò¬$÷Ó´ŽŽp!¯UÝ€áÞð¾<èħ8;îo ç-Âù€Ð7v7X»Áî© |˜ÿºËsŒc9wîØY%‰Du½x·6vë¹'wréôlAó,Wõóírþ9KíÇ–0À9ê^G7Ð>å\d?‚ÿ뱘}ñ¹X¸×À¾¿ÂžÏRŒãT{çAÏáId@dÁ¬Â67àiÃ1ge‘¬ÒçŸ]w‡fïhQÄÏ-­øsy8ë>ÿjþÎÕVò{Å&û«÷(RŒæû®ì\I†qJ[Ð/ %‘´ZÞ•ŸWÝ€NC6é“E‚t[¾™å ¬Þ¡Ö€¬óýåv<¿Âa‰—wöÈ!õuBÞV'¥¸Á½ÅIÛžˆ…¾Åöq{²:À8K–è¹fM&ãFÏ«ù³òDW[ç’p3‹L«ú0¯xí4¸'ÊxåòÚC½^fë#[ïÕ/(⸰û¢ÏÅ“‹È™»ó}?cÐ3®¶OšÌïßcœ½¾t‡:™¬:tvã ‹›0¹ÓÞÙ¡¯²Èæ6Ñ!›ÙÁ»–#onƒqökº_³ÜOyYyzòýµáPÁðÐÉ~XI{ö}]ÆÑ9»5àð‰²èøa[Wç¸ê+iç˜M&Øœò×wQŸ[6SmhÚÀ§Hw\B/†ÏéWmîïõKâÕ¼ûÓŽÀÖmÃ!¦jì ÏAÀžl],Á8­‡Ñ«Òq¯—I‹¤p×Ë=š•M6]®ë·Ü ÂÒ÷·u³pƒ‡ÏµpåkÃÏgÝù½U'~¯¼D?ŽÝp±)?_wQ÷yUý`œ^Ç­Í->¦ÿŒQ㡎dtÊúlÂîÑ:Bbíú=¦ÿî“Ù@Çs÷ö¶€zW7NY`ªÞ¿ÖËÂ}yƒ%½†DÊÙ¾‡ 㜡ׄš¦’öÁ©µï&%‚[qølÏãÙäurºý¾9BT¾´¬kæ ù-}ñk¸äz{Jð$`÷_Ç«ïÇÌZÜðÃÇNþ˜_²sD9Ɖ°©Ù«^©Dâ7ÿûÁñI0`ÒÔy½S³ÉZŸºk®L…>ýÖ¦OÎ ôÛÏíFYñyŠßw1Rß“¾§Ã¾0á·û, Œ³wq¿²,§T²º™ë+½ÛI0¡Î‹_íʳIŶØ7Fú®`_~âr£{¡P¼ríãwSø=`ëu¿î ë<–½á¼ûõ·÷ÑßÞGÿ•¼¨g×IÔ)-Æ8:ƒ:‹:§Å~Ñ÷M“MϽ‘¨W[>÷„ âžî ™ÿ‡o› ÷ ×äSWq$MæIçhú$ùp_Z0RîãF9S‚w¸ÀOÍç¬j÷J2ç^Iœ}˽t¥ì)÷Ó¥üT)÷JÒâ‹æ¾º‚W’à­áÅ9SúœUÏ="åÜ;\Ÿ3ý¨G¤9÷ˆXÕ1Ü+IÂ}ã(kвª)û¤²#óW¡L?÷ˆŒç^rAÜ#’z‡ l,ô ÎôÓå¬)…kJÉyÕš>s1ÜC\“YmÎYªÿÊG\ĽX~sJî%®Éø“sö”gOQï9{ ?qÊžRp¦ªÀCüÄãyÓÑäªq¿–? zóšh0Q(WUðY‰ç<”¿{õß½Z¦õ߯Wkó÷E“·J¹T”mÍýÍõ8—Jɹ21œ·ªé;eÎ=|5™«tp“?øñ¼@49×”ñ¯áwžÅY3‚Ÿ¯ÀTrþjg]ÛsÖu%çëºeÎy5‚Ÿ/eVr?ß(Ω¢¾v±ÜÏ×€sª*9Ó&Vƒ¿*ç…kÂý¯¨7:å¯RÎCÊ^Ä8UzÜ×NÁ=±Â¹¯õó ç>é”wÎ9U"ÎÄIÕàâTp«¦_–œûújrXí9ð_yûRæC÷|³*¸¿¯&·*–³r$Ü;zhI5<~©z*ç ìÁãWÁ}¯4YáÜëOuMÿЊ‘Œ_%ð(+°’òY±I)¸'Öß½úï^-Óúï׫uø¸š¼ÖÊjŒ¿£àü@{΢¸Ø©œ×ªéµ.ã~ÈšÌVÊá ÿƒIQÁY‚2TíÙœKQÁy‚‚ï:ådÇ *QöX@ Á#¯ÁÌç|Šp O J‰Ò×àñ˜£b8GŠE—ÊyºŒóÓô8ƒ(KƒATɰQ¨J”=çUþÁ•rîä¿òŽ¦ÌŒpîGk€CÎyDðÔâ8“ÈJJ—3‰iTçR ì ÁC:•{òk2*)?#õøòkzÖVŽd\5Ÿª¢|XlR©œñw¯þ»WË´þûõjÿ{”é&EÅ¢ªª1~Q*çZJQq5þârgq¶®¦—÷ÞÖäëRŽ‘ìÖH%g\F¡*iÏæ¼‘Jι|ý)§[Žª¢ûÖX@©‚'7J¡Á¹T¢ô°¨d<#9ªe Á3²GÉyÁù ²8ÏȇóŒDX€QœÓ­Ë9Ý©œÓÄypºœn=΄KÕàt+9.•2èÀØp‚·?åÃQÖ‰>¾éѨ*ãtÇubì_êí‡ÒíÂüÁã4X—qœG½ýÿdu‹8XÓß?Ÿ{†kò©Çþ¿áNÙ(±¼i>ÿ"î®ÉŽSj°»¯ÊîVrv·àõo‚ŠþÃW¼Šó/cxãñáü”ªÀÀüG~à”£bÏýþõ°AE£¨»˜ôï^ýw¯ÖúïÙ«õøëÒäSÞœ&¹¼:c#pÞ\%çAÅr±“?¥S›1¡bÿ`ëh3º&%•ó4™è”Á’Ê™ á(%gD…£²4øœ•œKˉ²âyQQ‹¥Ç9‡%¥ä¬NÊaÉGI°èbQUœÙª¢û!œ1U‰’èþÅš2á\bÊŸ3oøS´@M8Ž*ePÅkp‰ãÚ3;åÏÉQ•—˜rX´EøçEŒ?GyžQ—XĹĕ( x”ÀsÀBâü9}ήÊ×àWUq6q4ªŠî‡p†UÕ|bÎø¤ÍÁ•…2À&!Gic£Bås&‹ •’`ãˆå,+É<ºxÞLÌQñ(çZÅ£´±¹¡ò9›A`³ˆ°ÙD¡²8B“ùIù,Yÿ€¡@éQªj$ãÒ |–Þ |PYœÍB{ ýE{µÐŸ…ž,ôa¡÷ ýVè±B_z©fú¦Ð+5{¤Ði/ÔìBÏúœÐÛ„ž&ô1Ú·4{•П„žôõ!¡÷ýFè1B_z‰Ð?þ³=CŸÿw-ÎfНÁøäAøfç×ú‹Ë+0R¢ñͯªÇØ’±ô¡ˆuìCYãøa¤rŽdg(I87)–ó’‚Z0F’^+Î ÇLÄÙàYœ.ÃZS¢Dû-ëŠ>(ͱŽâ;3~£&Ë[5¢§÷ûVë †?0}PY(üÐå}—(¨ÿ_ 7Êä®È8ÜrT=7§ [L íÀTÔd éú‹e"ŤIEécÎÆð¬ÏßóŒ¿çZÿ=çüçÖäÚR¾›“<U…’p¾[g1Åq®­ &J—ó˜âþ`Ûêj3þwJ C†ÊBéþÁBe¡DX02Tç3ÉPù<Ê*ι¥¼7m,¦ Ƨ¼7)*¥Ï9•©(],2ªB`V¢”(,º8^x&¨^€>œïT…2ÁbŒÕe¬'sι­âœpÊ}ÒnË8·±œN9P Îm<ç…SÞ[,ªªãÜ*P:"üó"Æ{£œ¨hιÕãœ[Ê7ÁFå£ô°Ð£9ïÍà0¤hñSÖm o>œ#E›&ï6ˆó2µ±9¡òQl±(lá(%J‚ # ¥D™`ãˆãÍÃäþ›‚3¥(S\Òãl)J›K8JÉYšA(J›M4*%úƒ©ÊGéaŠFU¡¤ØŒRQúØbxS¢¸,”6'9JT*E¿…AkþzõŸ}úÏýgÖìÍöä?û1íÅBþwzðŸý÷ÏÞûgß•hýÕsi¯¥}Vè±´¿Ò¾*ôTÚOi/¥=ôŸõÏ?{§fÏüGýòÏ^ùgŸüwzäŸýñÏÞøg_Ô쉚½P³þ¯è}ô½¥\]]L”8”6&K*‹ÎŸ°×)j0ŽwJ„ ŽR¢$˜HùœÝ(ìoJzˆsç(¿;†'˜UEïhb¢é`/‹EiÓ9* %ÂÄ Geq~%åvëcF£ª8»²e‚Iǹ•q(LÎpT~ ÆéNEécßq6w8&mVkÆäçî”>&p= ¦w+;2^&²¥=ȇO0ìQ ”ˆO4(s2%íö;g;ŸÎÝ0ñSQú˜üÑ(= F¯ ‚e€Å GicA¡òQ,ŒX:—ÃâˆCé`„£òéÜNƒ]Gù¼úœ¯]I×5œ1GÙÚ±tï ûJ%½{ÃyÚ±(^P2%«PR,²T”>Z /6TÊ‹NŽÒÆ>ô÷¼ïïyŸÖÏyŸ ÿ¹4¹¾J” &yOtT OxT¨,”6$9J›R*Ÿ2Õ±9Å¢t°A…£”(JQ }ƒþúÏì/iöS¡‡jöεÇDû¡Ð5{íwBÓìmšûL´ýÏì3Ñ~$ô!ÍþóÏö›4{ʶÐ×§ ï-öŒpzŽXƒ1ÀeøTÔú‹Ï[AïrÐsAü@´9×;‡u®ƒ5N¿£‚’?$¬çþÀ4ÇާÂN?8=Ïú5Àz çL4ª e‚…ª ÷±€âQÚô;¨8”.“L‡±Ãu±¨‚Pùt¿‹+šrÄQzXdѨ*” [4ª’ÞS {è(zw ‹Ò¡} ¥ œq,D{T~8QtŸkSBïãc-*é÷ZñÊ¡÷ƒèy3½„uVIï×Ós.” ÖTJ—î©`-e¡|ðM¥ßmÂZ‰¢{'X'ºg‚pÝ+Á:ˆCébÈP(süàãQºøáÇ£DXQô;¥t?–îð„0ÇüŽçŠ>ô;¢˜ "z×ó7 e€¹+Gicî¡òQLžX”&P8J‰2ÁÜCébþÊà<óÕ¼K)œ™iþ=ß›~¿g/ç߃²çß}úîûÐ{ôÂ÷R+ùwè]ŸT°zÒâŸ}þÿ.üJdnZÊåå’¾N/V-¹JzÖ6¼°³-Ôúv*$Ç+jœ»þõSkh+twgí»DàtoMJ,T(…A³Â¯Û¬ù ~±N¿ýÌ0HKy]½[-#8_7¸rÿÎÓéÍ"E•K¼]#¢wwN#½Ÿ«ž¢Ð0ÿžwÉÛÚ—õ,rÅ»“3CAð{|º¿6MN¸Ç£4‰VžiäÚÏ®âÙ‹R ³ÛíÎé“ïËÖçO§Ns½×áK÷…€À |&Ž1G‚ã¸/js}iXyö6rÇÏ—)ðhž—ãñ™wÉé®ý;Ýórç>m3¹ŸŒ”× ˜YuÀnQp²ô7n¨ǫݷ«44"ûÈ¥BÝû}km½KîP{à 7î¿>Ò[”<é|ÝJàfqÿq¨w £Þˆ ÎÿùÍ'R†q"IöÚ¶óÓȰU­ê{œ ó²Î:|¼t—ø-)pÍÖvƒå«¶õœ Ô5¦åeK5žù{WûŒ1¿È7bÎ+ùó#Ç8·.5·Ûê|âiá]bvGZÕ·Ôêl¿s©èãLîËfÁýûFâµ¶çìõ›Ÿ™Ç«;¬µÍ²à4âRdߦ* ÎfÔßúå.™»¾M׵˜Áì|K¯½»B87É'r÷£â¾x8ÎÞ%Û.,˜–F¤³/¿O I‡þ£ž,×j™CtfŒM6q‚çÔ2_Æ«±…©”ìj ¥6wüBõsi­(—,¨úòæ†_yaÙåàé`êÙÑÓA?‡¶{:´ÌŪM^ÒpÊ©P5Ï鯱'·'éûs_XO`ü´gâ„MúŸ9ùBíc$øÉªòãPŠeÅÒ4â{àà»YKnÁ`[^LsÈó‡EÒÉõì!ªÉÍýkºÌ‡Ïë–Vè ü'|-/»‚sD+ÄÌß} ç™pŽ<ÆÙÿÜnDÇÝiä\Jýg•:Ð:råì"i™Tïá»F‡ì¸oo8÷Uë YYp#iå¸DBU^ãxTDi¤W»1½’ÖfÀœ˜ã’Gá9dSrB^Ên{8x`ùÞ{ij~“ð¹±÷…ó‹qJ-uÞ›FÚîvéyK'JýrmÈ!w}j*væÇó6Œôï<¨çJÚ«yª;Þï:Òé ãiÈq<•MÑé42'`¨­ûîL0Š*+È=•CfÏ>9Übª+œ^‘1Uù.œû‘‰ˆÎ¨ë×ü²¼q¥Ü¯©R,øÆ3ÿ2Cµ_‘*o1Îéw}M¤‘èåׯlîvª~ß\’¯ß­ûŽ«MÝalft¯•v‚ï( Š6 k+H^ÙfRB¥<æCU)fþ{†à׋f˜Ÿ–ã0nJ:i2ìð†jòÛî±³MÂç’½{¡q½¶žpÇwáèî–s¸?V;"ðƒ“G•×+Ë%Õ(Ö¹g:y2ö‰ë­Þw`ý‰ðÁ;å’o’w·÷6˜Æ}©"€ù¾ €ÆÆvÏ^˜š~v"gÅËÅ}êwO'7;Žßué\ȉ¼ójB.Ù,nUoŽ'÷ãž Œ{6FS;Z'ø¢O »Õ~ŸªüÄñ:©ŒÉÒÉôáYãÚfÁƒ®ÇR:ÏÌ%÷;?_>Ïš^[í~é`÷£ÄëÁ\¼òõ^ û‹&øœ ¾Oª|Åñw]Z¾äÍ4‚Í_g¤Ut\óeƦ\Âø/®0~§Ù™{OB¹_qW˜|-°‰î‹‘ÐëQ©÷T‰š&ÄaDFðu¾ïÕÐŒW Ã8f1úÛ7¿J#û+®»±2 Ük:—ÔSÙ:®C 6 8HêqUù‹¿UÁíÏVÒÈ{Ë×â”,øÖi¨½AZ.É=iõéðn[`<¯0î3Õ2.ääEIGýæ«§ÀqrÇ ¾¼8?øoÚnKílèïûù˜Vi.Ù>éÖÏþklÀqÁD­Ÿ=øZ-Âú…5¨ì›Z|=öSâx‰YÏ7HJ#Ž÷}ðʆæY†ù•rÉb¿× .ÙÂÂWÒDqµP>^7Ru YÝCÛ# dvƒu?úÌRsÆ?'UÛiÌyœsµª\r¦uÆJ#)=©Aq6äIÌÞ?kp„õôÜ¥´p€„yóÍä󀤥yìáuŠ)àªjlæjþÀ|ÂUy‹ãŸÚ6÷læš4¢Õñê¬é ÙPmøÕÈ¢÷ÈwÙ›¡3¦r_º™Ð¦U£%žÕºÁÏ]ðsÖäõIp¼Äq´”FÌUÆ«wáç\­Ïeã啕RÂs‚ó ˆ›ª Íù|ùKqÆ·I#A®un·¶¸ †þqA¼î‘#´ö=”rÕlž7ýH“¤Ç õß;Ä·oëD8ðz…ù‚*q܂԰0(\hä»ð¾«¡õ¨÷Èpß‘SÝâÜÀh[Ãi©BŸêBfY¥ûu õ†¨F »,¬íÌ×¼B=/`|‰œÃÊ|òä§î €_éÒHñ¹Ö‡;½ Ù_®«wâɵìòpwþÜßK]ùöYpšçë<G.­õ¹F‡ 1ã@LüÍ'Oq–ûN8ÓÈ88^¿%½è.è6¨Ò2û9&=Uû[7¸þ5¼âŠS¯cCزáI×M×%œ#;FG•×8žù±ŠÆ™£ÒˆçíAŸºµÍ{jˆ÷áé°gÈcC©Œ­O4ø0K_¬.ÆþæË«µº\ò1K5ïžiD§›ã»pÇp_u&µÌ#!OsB—»HáÚK>©á¡jõ”Ù»Ão·”·“Ú?/npæ<«çl>‹ãuZ§4Jo™FêùVµÌ[“ÕößÚ‘0$8­ýîU§š ×_µ˜¾6ЯºÇ¼ý@ઠþ‚/ ó1÷2ë™|cÏ1 Æa~‚iddÀíä9ppVÖPË1ýaž8¤ð´ZŸw/–‡ œÂúŒ ¯cµo¯ª.p¼íÙë–Y•§’þßm9vló†&=ê|Ÿ¨ð=ÑîàX¹ï”(3\ði'ü} òÏÉ:ZS9Wî˜ùÑëÀÈÆuK¿M„Q}!hsÎÅ8ýVØ7‹¤[º~/ïÏð°•_Mï“9SV~¼ÑžÈœ'ï>3G˜?“!/?'Ž㜹Pp­öÖ5ɬ=üÕtEg&Ÿw±ùš ãä¿Kÿxa[*1õ8·öh÷<ðÎ [fzŸoã’ 4«Ç|µçÒg û¥foGœ=V¸àdÊ}bW<8¥F‘;4lué𱑳8wªa¼šÉjNp›úÖ:“ŸÏðé¦}æK µ¯.ão°¼Rbœ›öö:t7…êo4ª Ùį|~Ÿ|!ó%G¹ãL‡òÏû×u«:£cšÏàë7¬·ÉåI=Î ¬þÝB·&:¸ý6ŸÖZ‹y`ýóüÕ™)dkGJ&¾ûKv%ש“Oî¼šÑæåVÌÏKƒç—…qÿÈšäú2eüêsÔì󳆂ÃêpdÌCŸüáu`jÅ„šGHù<ƒå›ã<&Ѹr{ؾȼ!÷áÜ]ÇåòÉ“ ÔPÙïÚÕ`í†ÙBŸ'M™“nJAàÙŸ »<Úrm °}²zæ½úœÏf¡—øŒS>{{F¸S ©^¿«Þüû0»àúAù¤knƒ#ûÂmàÁ®óEýÏæû –Do{¸ÔSSøôwå˜G擯ákª?«ã“SÇÒ0h›gÝr©³5ÙJÃí¡þ¶Þ‹}[À¡x…Œûc¥æË¨êãlÅ*t0K&§%Ö’[äÃå¯ j¼ÝœOÖ¯ë±ÑÓÛ NVökõvW8ôëp}’5ù1áiÂÙšPdIÜšû'k¥î:÷QÌæ÷–ÐnóŽØƒ—¿\‰q´g}o¥]=™l˜ÝhZìÊ|xàmÜÇÓù¤üêùê_R§B «g=vÏæ¯kAœWH>6îl ÷e·O¼m¨^7°ùæWñ¬¯cœ˜ñ}„á¬~Ö•KVU-yy5‰°Ï):VÛ°gÜ|òqðSï+Î0ͺur±hçHÈ£#Æmäñ¼§öeÖãì9lüÛ>ŠãÔ¢øáyIä+Å[üȇœÐ>Û¿Ê'‹W×¹{Òìg¶XUÚ|0l>ïìyc öq|ñn{ŽpÎ(ÆY[ké˜mFIdø.Ïc·z€Í‰‘“ë¦ÑOšùxM#—¼Äê®Á ô5a^Æòl4ï¯OÕ|=ÆI7àëMÆKbœf¹•}·M"=7œªiY[^ÊØÓ­€|LðØn¨‹ùÖòsÇm8×dœz?Wàà |Ö# ûfžü¶Ï+ÃñÃßÄèTžH|÷hŸ:§îÔ}¡c*. ÌoÜlÝ)iÎOðÍ&‚=ã'çÏ›·êÏE˜×krDågMƒ™­ÜV&’¹ïÛvos n½Ã?hU@ª7ì¬mdÍ9I0Ûd€d_]k"¬?n¤‡íª}¼Uõ‚ãã‹j+k˜H÷¢z>Zrò™WéU\ýdÖq íœÒþئ@x;aÃӯ⩄õ1˜ánbÖ=p¼U]àx¯;O²è·&\.úx·yµBØál©³uvùPÏ&²ó¶)ðyÓÎf‘>3€qÞ§G£ç£7ç‚Ó˜œ¦½÷«ýêÙ¾Ï$8«Ö¦ÆlßH+º\¢Â×´K eÚ–=g .„U!ýW=^V@†lZp÷ã³ÉpîXô»¾38ÄŽØÒåïNCuÿö„ßO^nto“çd`>霷„q"6ŸŸü6þ&y®­ôzë^×g¯¬ÿisa>ñfœy)ãJ‹Ó-Éçk׿Ãý¤9ïÇy“òjëÛ›$‡î†+ a¯Ý;ï~‡ ȼ£§¬,Ü, OÃï±Ë~B¼c~)½ÍIÕ~,ë U>ã8o½ÃÏÖ~sƒl*ú4úù©Bx‘µçe»KD8¿è˜ÿÖU¤[mÇ=ì0q ñx»ãæ„Z60ígö°ém¬Õó6V7“Yã¸/ýS±ÆnJeFòÒBh7vóöÑY8î¼ÎS6œt€cckœö/UšD™“ö·®<¯ö>Âë]²¼³(HÝÿŸw©\¾ç—˜ó6øzÇo‡«­6}o’ô¦»¦µ,ïܹ›ç¾,Ps`Ù:"˜óV-È“÷Äí×™BaØŒñî‹&¨×aÂïׂù•3?nÆ!¯CŸ®xª ©á›&„-‚Ä–GN6,$ µƒŸ¼Üå z  þÕ„­’èãm›G(qœö›ôs>Rà‘”äS;†w=o>°4M3Ê˺ê¿Â WçqÿðñD8bœ^ÎÇ[_.i: Û†Ši ÒõÞìù‹Š oJ–mº]!9¸üûÚQ~ÈÏCz¶¿n Fã’÷[ß´ú_(Âñ÷š|©§ ­r/f<;SFmº{ˆ,$²R’3/KÁïÒ f žú ûü¼Ån·Û਷Í_ý9±ýï Â~ ËSu¿îû"»Ô²¯¤¨Œ·ù¼¹½?¾²é²çT¾Ÿå£îOi¶[­ª»[Bv‡þÆÙ«Ç_ÑÝ<±ó²ßëIŠãïÛñcáÌ¢ëd`g ‡ÏE}êÄžŸg I¿æ_bñtº·Ù‡ïO Ï!œ‹©òǹþy˜Ï)ñubã#Ú’Ùî,(w¿Ù!­Ôœ?nkw'+`y;]í;ÎöcÆ€ðÜSå%޳ejÒÃaK¯‘–ÍkOk3ùìØ[=ا¨¼Š}\³ÛïÉs.µÈ]­’êuWºòç·§šwÍxÛ¬ž8nöéŦ¦\% ÛŒM {†7?ÿ¬ýª¬jx+èDy×X½—¿°~$­.xõîÿe n>ûjÚŒ`09uuû»ûÏŹ ãtñ}6Î}Æ8E~cÞ¬ét•ŒpnÙlǺТ¶"‡ü*$[—õ>=÷°TP¼µ±¿°þ'çK8Gy>dùù±ûž‹…ß41«ïòaüoëg­ å÷eÉÍÆì¼B\{|œ6ýð(4é·¸y騘þ¶À\L–/ןï m§ýðr¿lN„}ÏfwÛ,6ÓfŽc±$÷ÕËÞWH¼È —RàKv‹&¾½‹ÈøèMâë·í ·÷´Öí½án&ö\c_‚À–g[£.^&ÏvÕ‹úå7¦|?®ˆˆl\Fe6sàOîŸ?–ûØBöYTùˆãÕjãÿ|T¯ËäfJ)³ô×Í,œ‹H¸5%•;Àî±'sÏŠ<…~FT?Nû x¥ó+î ñ_\q;T<ØØv¾)?ÇbëÆÁ‡róÓQ—ȺÁ”àU óN~¸jV™bÔ>*Û¶Ö{ÁÍ“sc;‘‘¥SÖl ´Tó«…ýjá9Îø¦pýÀGcGŽqÚÜHþøækœywKºÚÄ©€hã~ÛWàx#)vxG¿´:ë=òÁCÍ/៛úýî}ž$‹ù>;Ž·d‘å¹™ýã‰jÛms1œéº²Ð8¾ˆÔnõeŠ%?'øŸ=8çÍ(¥¼e[õxK•ŸË%μá"顈ûþåB1Èœß=†´"rý­ãÉ.g§@ä•nÃ¥vœ[Òܼ¾ËpÉ7¾¾sS×±áŠ-£voPllÙïXÛ—áø«›§ß¼ÿyözDÛwwŠNå쯙_D†´Ÿ8ÝøÊDè«o¸Á ýӊÁˆLî—¬±vŠzX¨cá|0ÝøÙˆs§ ±!x]ÓM_1k¼ú\Rà.jÿ«Ö"-SÐÎ<Ù»)ëK"ŒóýPœå˜ñ§Éþ÷÷Õò)–ââMm»7ˤéNj¾ÎGO:Qu$lÝbÇùš6jNp<»®ÝcËœâÝ]Ó™Uë?$Þžò€0~˜³šÇ*ñmÖãóHkb…]G¿w(°yµ»z]'ìû½šû©¼Îòj,¨Ž+&ªã¨êÇ»ÓìNAõ±Çˆ 7_ªk ÉÈ—$­Ç¥7m8ïs²zߟխ xÈ ›é¹3ªÇ¡§"í!Œ«TRŃ‚ä¹¼Ú±‚KSÔûãŒcì粄+éF¸½:UyãY?:¸#QKÜ îë{„ãE•O¼´º BgÝŸ¿Áx¨Wø¸Ò´Ç@õ9øŠ97pƒŸŸ½]à5¿Wç›ÀIcõÍÎc´6—KÂ/Žyuñ99ßCÇ·%Ð`RüÞ¨2xÞÿè· -Gñ¾[ fçà½ÕómƱ±RŸ“\ŠÞ×sË…÷bϸ‡Ö~nF¿/‰0Îä>•-Šç$ÁÇWùh•BF|Í‘KË`€rÖ™'‡†ñ÷¹/aõÝ„s–çÔqTyãµôü~­ÕÒ¿uŠÉ'íR¨á+š$YTÆ÷#G ûƒ„÷?"ì³1¾¢‰šË¦Ê_oÃÙ¸Ãíî#*|SÓRX|øÅcïùe°þÒÀØ¡c„ói"ÌoØy€+ôð¸4΄×/¼ïßÛ˜. ªeøOT†qŽÛÓ ßK¾Ž4ådÏRh­ÛA.(ã}ͯßżëíUH6^½Æž+ïÅÂþ%ãZwc}ãìjª³1¨ÓRž6¤ÖÀ‘¥°ÙpGT—Ð2õ9瀶ß5˜°ý×±ü¼pD¿°ïgâ;5Oާ4Îû5° OkÒÿC_è–2¯c#KÂöõûÆï£¾O!Ì£„û*lùý±ü¹ÀòT‰qè*RÇ&†Ì¸{¨k)”œÙ}~¨O´Ô~ó'78×Ùwßc"ìó üMz;DÇ`¬z>$ü.Ü‹a|;vΦµ¥\rT¹6‘Üìà!gz—‚Åâéºb÷2ȘèPöÖĘ¯ë¥„íã÷&B°÷ÑZ}ïOØ/î 0žÜV‡RÜ6î&ªëJ¡N‹úã§b=¨ì†0ês‡~Ã_{òuwG"ðA«ï©œä㮞Ÿ œ\ÞWù}F–oŒ3õÔ³1cz®$½¸Ï,…ÏŠAßìÊàÂä·ñ±-ÇÀ€ :åK¦ážÚ¾­}.ž› æF ÷ôÞß<ÕqœÓÎö÷¤çLà GO1æO3V8ް¾Uòøb…g)lìµcñé)eP/ur¡%ÝaÏ£ýI­ü‰<ÖßÞÊÁ<ÚàQ 9ø¤›Aì*ëßž ¯˜N«Zùܵ©³{†•Bø’~£Vš•[×¶ç÷ݦΕ$çÍî=1Ô ¼÷û?®·ù$†ÈÇIዊ¿Ú‘sVMX~cœ˜¢kz¯ç¼ÂRxym¶ùì1kþŽÆ_Äì~AG˜K±ƒ,¿—S.1/xø–û>¸\U´[tº®8çnÜݨ ¾ ø\8/?¿ &Œ8žþ|\êC<Ÿxƒöþý^cú‚Ð/ž=5ÊdŠç†òûi1Âsu?ÜL87ãõ©RÈ~´sÞJí2ˆ{1óà—ëã`ã0EÆìÊžçÃàÓ™Wç†t¶‡æb§:u"­à¼ËM»^.õ¡ÚuY_Z"ȽÜЗœ·â÷רë‘`œ“Mû4òžR–ÄL›¿tç¯èn®î/%ç«„ëúÛzén%|^A†ùS·ô¿q¤q×7S…u‰zrieÒŒz½ÇÃæüó׊7°ç¶ã¬]ÿ²‰ï²ã°ìV/ÿÚñ¥àiYáÖð»v¼R¼ÙÜ_ ý¬6*N%lÿe8aãšó窥šï.¬t"Þòû>gÇšÍ1ÞÝN@°¨kýE)¤Ü–uœóY /­­ík:Ž™Ñ³»¤k‘·N¬3ëû0u_cû pä·•KFljó9õ$4èr-¶ó¶ñå%ãf¼U‚À#–ަx6¹²õÛc=É`"ŒÃîILRŸÃ«êÇ;ÛÓ.|ëª8X>™ ñ}ðj:ê¹¶4obpÄ 6®©ãôòSáë ^v|ßQ=ÞÇESBÞu®¡^²>ÂëãTNž8lÅið=¨“Úmm)l{°ØÏô¡¼,wç¾s1‡Õ^Ó¾E_œKØþÝ(Â8ÅŽPjÖù&Ñvä÷†pOýw_¸çTé›ý$VG¯Þ‡£LÏÀ¼Œ:5Gl,…3oúc'TBËÜ©Íf»™ó¾2Ä=Ùü"`Ðxr`þ»3Ýøú~¼úÞ…ŸÙ¥öawj¯®¸ó!Cw>Ïf÷e§éÞ&þSÕñGL)dV~íÑì®’?¿Í ž `Éû®)é_ú5®$v¼ú>šðÜ­ùHywÀSPá…[³sv9Ž/Ê_oZÙYh¾%\R°©rnØ|ù’®„Ö—‡$4¶:›ðýõ9«0îÝ÷;z7›dÏ0>æœbŒóðXû¤áÏÏöº¡kíWáûeÛÚ2I ‹¿nîÜr¿9“ðçY ‡EàM÷ÍS¿®½ú/SÏW„çû~Õ„ßÖKrŒ³yZ”‡óƒËü>B)ÔÒÉ2ü<æuůú—CŒa5yU·iÑ<¡?a_Kà#ÿ¹.“é¤E{¼0…^Cé̆ÅQ`œiJÙ–“Ò+ðEÇzKÃRPßÄ8‹Ý³N>5‡ 9­Þ¤¼šÍïßIH“GCN=?nø‡ÍÛøþ¶.Sâxc_]œµ¯ëU˜L ¯†ý,Ë÷qÇ(ÁÆ ¸á@~O(”<µœjóÉy¹ÞbE]ß*#¾×¢ªƒøs»Õ³Ú€k*>¶ö»Xª5ï½M aõãÇž5²‡8‡~mw½!ì>tF¯7ézAiÒ·àoO<~[—‰p¼Ý•Mw~•\‡ÐzïήQ'‹¾º½Z l?×,¼O8s¿ïYu]š±åøÊ ~®ª>•†ÓN:å·ó" Ž¿¶½AOÀrÞÓVÝ*JàzÐØ¹WdJv©gÿq˜1uŽÚ©ÿ?ñá:»g`B_Æ/~ýD<¼žùo÷ˆ¥8þÛÆNsß#Ð@uq¨jmÍO~¦„ñó÷ÙÅZAñ™qß¶Ì'ì\¦y2ž.HÆ‚°_"|Ÿ@•Ç8^bÅ¥¥' ~ÿzƒ[%Ð=·ý½¯þJ8-;ÙÙdÂèýàÝúMd„Ÿ¨÷Å…Ï]è3ÂûÁæ«üü ÇWaÉ)€4¤'$%0yap }/%¸î´êzù¦œ•\·²Ú"õú!Èðóq¿s“€õs¾§&œß ëÍó$ÆiÓúq¿›ÍoÀ̤$‹K+JàZíü}R7%xÖMnçš0cÉøñ;Íþý"ÂÎÙÇ‚ÐçÙyE…XèÏÂ|Žå;Qbœ{q¥Í^X߀Õúô‚e \x%jûÊY ³:Ë$ †=mmuq¡0ßÖ_êuŠp¿^¨¡Ÿ²óöz´vâ|+5£V¿u7षvJŸÈ°~ÙaÝG%,Ú4h†~ÿ‘0Q<«•^Oa÷Åz©ŸÿWµ­6M|d “Ëvú»è¿—è·=Ùñx¥XØ?aóa~ãÄ­nXü²ñMx§·¹r‰ Ö©kÈÙǶJèf´Tî:T_/)Œ$l?¯aûÂæü¾¨9÷·ëåÖv}Ç{õyí¯9ôã÷ 0NÕ·$ÿ´£7aRRòÕµ%ð£¾ÙÚIJXù¹ï’ò%à­ì ÎàfóºÓ&Öª‹Sùþ®,ŠŠø¦«ûS,ÜcQÕŽu¹±Ä·Yüø¬ukÛøB~d¼­g8%½Ök<ÔS M»ùuÈ6³Õ1V‡<;vŽë Ù›µ­‰ÜÕ÷º#ü[4ºý¶-°û`ž œû¨êã¼–ìzWa”;f È =U ú§v¼o¯«Þ‡cJláÄשI?6oBf?¨¼òlKäµ™ðjü¹ÀÅE‹QÊnüܰÄÿ¸"™ì‚-«—]ØÇp•ÞjG"˜3Wîš[ =ï剛)aäùg×-'Yós¼¥„O¼û#ÏÌ¢úÜM©ð×ýº«m~mÛ9$G—ßëšÂ¿—ÌîõŠ0Nré§š+^%~؛݋ÁÖbæhE]%ÌŒ46u»TÝô<*]Â×§½Ûç˜ 7~ÙžñèbÎ÷Å?‹Ù÷¾‹ùù¶p_‰Õ Æq.sî÷pdxVŸ¿¦ë˜bÐ^{+þH5%DxXõšÌ×» ‰poH¸ÏÂÖ3ø½©Oâ'ƒÖ|è“xtã×· ŒÕç ǵد¿½Žþö:ú¯âu¤Åßê++xÅ£DÜ[Vð2 BåsYÁËH;š{Ì ^FÔgVÂù¥©ÜS[Æýfõ9»#‹s–䜳$xjWjøœèq#±$øQ¯Áw-–{*jrKãþðѦÌRêÅ&ø¢Pž9ç*éríTΕç¼Rê]ιÒzœWªà¼Rç*épVõ.2hÏØ Îê ¼RÊ•¦Þ*”#@y¥šžµAÜ?[“UÎÙw‚‡¢&£CðPØwÔ³H`Ds>Ç?ó+ ÿ|³M¸­à GYÒæœO*xU  4¼lí¹_¶¦O‘ý^Ù”Ÿ$ð£UrœŸDùÑAÜßöŸq þ—ôïûw•iý×ê±Úü5+´þbD+Pz܃R`D‡£”ÜR`DëcbÇpoJ­D™p–]÷¯¥žÞœe'ãþµÔ×;–3SÿZêí-Å‚Hål¤^>|hêï-øKÅqŸ8M†]üžµ”_§@éaEsõ‡Spv]g×QƨŒ³ë(ZÆ£úœ]—ÊÙuQm“E—s (ZÒžyd¦rNe×QƨAGæ NÙuš¾™áÜ«V“['ã$ÁN“O øÂ $©†Gx /èÆ–ýµæø`Q®¨=gÕ èJ”dà_~œRîM«É–þáK«¥ÁqF]c9×åŸñãâQ"lQ¨J”=6÷Œþ{¾ú÷|Uë¿Þ|U—¿&êS*°—SQúܳT`/ËPÜ¿T`/`b˹—©À^®@™ ,?îo­Íã lå*”ÉÀ¿¼\}¸w±&WÙçßbm F§6Œ î«JýZeÜóõŸyÌÿ+Ö¡°õ÷|õïþú_¡¿Šø£, gÙ\û8Î0¤,kïÆ4¡®½ˆ3 )ËF\ÒæœdçØ¡âë2N²àO9ÉᜓL}ãcx!øüÁHØ…šžñ(s,’x”ˆók*5¼§õ¸W|JŠ}5¥¯ÁE¶ç\dêÄ}âµ8·&žóX©O|/4ê¯à¼BsT,çÖ„s^!-@sîO™­A¯ðOn eØëbqÊP³&¥…„ÊGéaÁ¡(=,Üh”¥ÿ§0 e€Å,GisF¡‚3 íQñÿÄ[Ä…šÞðzXøÑ¨*êÏù„³^àÔPf}JŠM!•3jbxsðáücƒaqc?øJÎããŸq (=l&Ѩ*”›J*ŠÞîù{þúwÕú¯×_õøÏœ¥ÅX‰>ÕþbÑGq^¢¨Æ_,z g&æ×ü‹E_Ź‰Ñ¨J”=g'R>‡>&| ªŠ3)Ÿ£eŽ/ð‚Pr”6C*Ÿ³Âb5øAO1 UIœ«§ÁS”r6¬>N /TÊ‹HÎù`>-gˆ2e(%gÂFsæ>X4gÂJÚ0Îb>Ê .¦-ãèqGE;ÆžAåsGtÆ„5éÈx•(s¾^”¨cÏWvú‹Ã¨Çy°” f‚÷‡ƒ¯Œ2¾@,gp\mÊdŒá\FÊØŽFUÒ~‹…®@éa±G£ªPR,úT”>~ /~ÎfÔçlF5/GisÖ|þ ¿XÜÚœÿš’`“ˆEéh°_õ±a„£R9û•òòÿfÁ¿b6Òº¤¿„¾*ôR¡ =S蓚ýñ?ê‰ÿ+{¡D‹õ@¡ïÑ^'ô6¡Ÿý»}LèaBßúWýŠö¨ÿLúŸéK´ý£þó¯z}cøÃ’ò(Óž2(Óžr~Duc:•ÎÓê2¶´¬ãI`£´±Gi°¤)K0%¢|{T%Ê{ƒ¥Ç¹=U()&J*J“%†?d}( eÀ¹Ñ”­£ËØ<ŠÖŒ Çù;ö˜PŠŒý÷'k'%Âd‹BUvfœLºpÊÁÄÓÇÄ3øƒí—’`ýÆ¢t°v¥˜"LÈ(T%ÊSÒã¿*”“4¥od OV)gø ¬9O\TçäÈQÚ˜ÄAœÕ,á\Õ*ƒÌæKEécbÇð½* EoÉÿž÷ü=ïÑú¯7ï1à?S>J„‰„ÊGI0£Qù(=LähTå¥bBG£”(Lì8žÜ&¨T=xˆ(Lx9OzÊFŒAU¢ì±§‹ÒÁbG)9“ŒrvQ>J $U…’r>ª¾Gч³Q °pä(m,ž T>J‚EË9d”%ÏùŠQ¨ŠVŒ‰ùGX`1¼ÈL8wQ‰’`ÁÉQ(}Ê_DU¢ì±å(%e#a!Æðb4GÉQU({¾^”g4ªJƒÓ¨Ïy¨”?fÞ•1¨µéý”¥MùÓü±p”e‚ŇÒíù‹Ú [Žªà<êTJŠ…žŠÒÇbáïƒÊB`áË95ˆ³)“ZŽÒ¦÷wP±(lá(¥—Z‡óO•(lq(] ö©&˜ •ÅÙ§”y¯DI°Ä¢t°‰„£”(“ƒ Iëƒþ¢=öÏþúgoý³¯Òž*ôÓ?{éŸ}ôÿdÕ쟴wÒžIû%íš}ñŸõÃÿ·½ð¶Òø»ÿÑÞGûÞ¦çI´g2¡å(mìq>ümR D5 Z“+•_‹q õ8sQ‡óŸ³8o1µ.c?G¡*èüŒsŸ%ØËbQ:˜ŒáÌçXž˜”±¨@éa‚F£ªPRìg©(}LØ>ðAe¡ 0å(mLâ ÊlkÁÏ4¡¥(]LjmLêT”å8cbÇ£D˜ÜÚ˜ÜR:WëÀ¸±á#1J„} eI¯@éaâG£ª(C @ÒÅ"¡”tþ†Å…2À‚ˆAU $0•(,’8”.ç3S6³¥‡ªBI±pRQúœƒH È•…2ÀB’£´±˜|8Q‹J†ÊGI°¸bQÚX`A¨|” -¥ƒÅÎYÌ&œM«ƒ…K÷»þÞ!å/g¡ °(å(m,Ì T>Šº Äþ=Ïû{ž§õ_ožgÂc*Qz˜¸á(%Ê8¥Déc"Çðd6GÅ *è?cbÇ£´é™,JΓÜGàN¢$˜ð±(íºŒA)GU¡¤X©(]z‡‡ÒÅb¡*PæXñ(FeT¢ô±@bx‘øpî,åe ÜÊ Îœ•`áÄ¢t°xÂQJ” QJ )¥äLËhTe+Æš•£*P,09gÍšsÖeóaÁÅ¢*Q”{‰ªBI±cQ(,DyÆšµGÅò¢”¢RQúXœ1¼@6¦çÌVRælWÆæÖ¡wQ±(,ÞpT*J‹X†ª@™c1Ç£D=ÿbwÛcaÇ¢*{1Ž·œ¹* e€Å.GicÁ¡òQ,üXΗ¥Lo%gzÇj°îãPºƒþb\ œo]Ε­@™Sö&J¤Á”•`ÈBås¦,eÜW L°Ä¡t±‰ÈP(óƒÉIëNKëßßßú?½·Eû Dë¯ùšÐû4{žf¯úÛÿ®½®ÿl¿úŸÝïú_Ñ›èû‹½H{ûO*=ÃÄ]FïÛa¯Ñ¯ÃØÖùÚŒg]1¬M°ÄQ¾-ö™¿ZŠ ’ŠÒǾô>¨,”ö 9J(•’`"Å¢t°o„£”( gUkcψG‰0Ñtèœ •EϱOè`¢gŽØ(=Ê£Æ$Ì¢ó+þœ¨*”û@*J“3†'¨J„IEÏ1Q%˜¨&˜¨q(]LVªe޵Ñ;o˜À©(}LâžÈ>¨,”&´¥I„ÊGI0¹cQ:˜àA¨|”Ý=¥ƒÉŽR¢L0éãPº˜ø2ÎŒ6Æ»´¤¨x”B8J‰2Á‚ˆCérNt>J‚Å‹ÒÁ G)QÔý)îïyÓßó&­ÿzó&{>fJW†ª@™cËQ(Ld9ݳÇd¶GÉQ•ôŸéþ=J‡ž¢bQÚ´—¡âPU(Lø8”+¡byòûÐý}”‹Àa1D¡*QöX ”F4ªe€"Gic‘¡òQ,–X”L8J‰2Á‰CébñÈP(s,¢x” I†ª@™`AÅ ªèÚ +U‰2Á‹EéÐ{Â(9ª’Ρ蹪 %Á‹áÅ烊ãìp b,J‹QŠŠCi‹ðÏ ²PXœr”6h*%ÁBEU¡¤]_\—ÞeCÅ¡t±xe¨,”‹8 U‰²ÇbV ôzþÅ—baÇ¡ªz1y,J‹<•’`±Ç¢t°àÃQJ” ~J‹_†ªà\ò8”.6TJ‚ý!¥ƒ‰ŽR¢L0aâPºØd¨ ” =Dé`oP ôèý,ì ùô»Xÿáô;¯Xÿúô;˜dùô»­Xß1(úöAe¡ 0ùä(mLÀ ”&a4Ýg¢çþ˜ˆæ˜ˆñ(&cªeI©@éÑ»T˜œY(LP9J“4•’`²Æ¢t0aÃQJ” &n]ÿйý®*=×ÇŽGébËP(sLæx”: U‰²ÇÄVÐ;S˜Ø>(]LnªeŽIa¢+Q&˜ìq(]LxЏ©ÛK<°ïRÑï« õ[ÉïþKùý©,`s$-þÙçÿñï¯\àÿmW¹$þùk« IBí*&ƒAÊ>Ýn™ƒà£\|°ÃÙKÕÜ/ãO–7똨}½˜W$IOÚŸ–dï˃N|Š3ù°Gô/0ßFÆi=²¬›ÁÇ$°¬wùÚÅ`”¢ÕèE)l·Œ¿¢k*¼Šï,"ø@'49`ùÒgççY€Àç|J8בû«p.ÆYîw~ù5ód¨ZxíPb0´ÑeeY)¼ütÑuÛXØJ-C‰ÀÉ›6£çÓU×fsÿ»i øh >U¿A“k©À8?)ÞuS2”¶§ôòè]ù°²z~)äÝðêf †Àxäajß-J›ÙYèk¨ÛÅRíWÆü¢&ª}’éøJß]Ç~ø¹ÔdXázzÙª 89.71£žÍ’téld *Œ¶á„s*È턺“‡Mä¾Ýq+ŸCæc<˜¿ã'ií.—ˆ´©#z Dõö~³ú€ÐC¯—ÂÉõ›ƒw™Ãæ}OMIØß³ â—K«_XlGUà™2ÉÏj_}Á¿“†aœ®YR«§)@iÖ-V>€nç~ô>V KšzþÜl#W•‘EÎ'Ãï¶Öp°"Zi¡®:©9Mk¯efoóý.fþÅ_ÄÌwјó­™ïÆ1¦ãæˆ6ê2íÔÜÚÙºæÆRèµy¢8;Ë6]Fwn‘ÜrÑ$òeá±Q ãB8ÿÈUíëWMÞßÝÐø³ÚwCð‘UÕ Æ©U:xÞ,E Ì®ò;c4úŒ¯ÕòKX)4v0ͪÙß•óej¿ÂÊUm :ó ·VûT¿Ÿîºòê§ObîgŒCÄ|^d§xÓ;ǺÅ)pï¶þùƒ@ØÕøq6¥ ÷u`Ï—¤óá³e¸Œ0ß›œ+Î-‚æ«ßèoxXdyµjL¬aäñ [Æ–‹¸zWî3dÌ}’ÌÔu ø¶¸7¯¹nÆ› Àx$œû€q˜?f*ô(R´µ*‚ñCö¶Í*ê{Íæ¿7•b‹E„ùÿü¸Î|Æ Aðëâ¨ò}š ßB²£ßêÁí­™wk*JÀ±³[Ï£ÞÊBÂùQä^„õ÷Ò&ãa^mÇçM`ùŒã´Úy£´¿WL/;SoGµ"2«ß€‘q%гÃÕ¶qææðÂöŽé›¶ ç4Áw‘q]\ᤓ™U ϪŸK†ãu)\{äÊ™4¸Üw}ýÛÊBèþ)lé£Ý%`?#ÂBzÈì6ϳ«ù+Rà “Úž§f/¸:, ÛÎu¦ögžš 9ŽLJù.}–ëçEO»‘X_+Sö¯,M!º‹îÖ¶…kVÕ>Z0‡p® q˜ä/>ÙÐ.œÀü¯¸Ïëï8>uE×i›mU†Þ…0ÄìKròÌX´]§çÕËPµ¢FÅç‘„ñF:?Ä|€Lã(q¼oçx8¥ƒsBƒ5=ž>~6v%àdÞ ðãT` ø¡Í4+ï`Éù–j?0U¾ÊË%q»+O‡MŸ©aW!Lè°Í¾×ðÎßs£n¤GÞÌ#Ânvû–¦ëʹʮêçƒÀßüÅ×€ç+ÆéÓomúäÜtÈ¿ wvL÷B«¹*¡u ôy6sS¦—+÷o\Ì}Õ¾ÃÌg|ç –q®ë î“?˜ç’cœçÚy™¯Ý2ûCÌk³³oÒ×bxVvY÷’³ ¨ðHÛ‘Ó-÷==пLÆY ¿sw‰8GÝëèfß¼½;ÙsÉ1ÎÆ‡šyŸ¥þ!snÀ©MÓ¢<Š‹aÿÀÆG;Ì™ y!ô™Oî‡À£Q^v7 ”pNm¹š'$[(’,[;šNl"Ã8%©£ÞÕ©q  ;`±pcÚviz³V/ kÖŽpÆ£‹Î©³ß,5ÿr"”½ÌˆyÄ?“Äyпûóa/}£®õúÞ‚ ·Ïí<  Eàîu÷cqÞÓdäɽýíá ™¤™[Bá¾Ã$ye›I •nÀøðnjŸbÁÿSðoÕä *0Nîý÷CnM»T ßX4p`ÀõÅPÍø°UB[ð¯ç9«åµRÑòrÓ…ÏŒÓõ>ÜÚCí3'øe2Ÿ÷ <Ï™Ÿ­ã0NÞ-˜¾Êon÷oùà®ØpÌ~n1\¿Y|.Tj õë-¸¦ûlÂù¤$ñõÈe)óm¡uZ‡½^&6êç‡Ð?ìgäÖËù<óg˜¯™ÖÞrɧ——&M^t êwÿ©÷<#z†¼_ãS âåãdíM­€Í§"?[2eä÷!ûÍà{À”èü×f¿=§D8Þõ¥aŠ×ßGôoÛóa¨Çò§N6Å0õAtô+ßz¯|}|žÀ'!_—itÉî ªpÅÖ›‹½îß‚šaéûÛºåÃ¥)k«Ž)†šÖÅ©e60L«­N§“sI“™%¿RêŽ#”>Ýq:ªƒ©í¡~GûQ¢1{½R7¤Ié ‡þP[nqùCÿ|Xk:dŸo¯b8|úZË%ö`üëÖyŸjsã­ å·'éïôóÅ¥ËtOE„ÁÍÄ9?ׯ'%}úÕ²î;±àw`Ó´ Ž÷UÅ8õ_X×?ƒûçÃÇN—ŒZ4.†íË«ûúä Ö«¢ÜßÕæÅbÆS2VÏ÷¿y—ËxTã¡Y¥_׺7×RŽqB»U¿QPÚu¾¹}~Œè>æõˆêâ­Ÿì8™Ød²o6ñq d}¬æº qTùãIT ƒ h´ôNÞ÷Aë©h\çÔðc²ž¡¢“'ì[°hU‹!ádˆ']7]—¨ù»Â|[•¿8ëSp«ñÔ'u—Ü£UóšYï~ÀŸÓ é·šæ«ûDÆ0æŸ:2r»†k3þ7.½Ö¾rÉÍVÝ­jíÏ€ú ÏlwwpZº8äTæíþißw´UÒÎQócŽu”®[á ·|¿_yؘOÝK±ÀµÓì"ÿ¹$ÏêØ© 8¹ðDÓ>Æã]@çÚAÓ_lñ€O(Ø;’û‚C†Gfªh¶´X=áyÍÁê>¥æ¸q>€¦¯¯ãä^ ‡@’âî–Û3åw½]÷ó»áü¼Nã~á}Ü`üÄSn5žKt·É\»˜JÔëQaþ7?YŽgmG+3ôtf­´Yœ FD¼¼ù¼Þ]­Wzz­¬Ô:<ûíü¹Dðyµø:65â—+øúê7êç¬æÇf¤úÖìSCOÖÏy¾bœŸ;}÷œõowþl›ÓZþ`t·šT{XW7ÞNiÿ¼¸¡ý_ïó6»ÑÒ@`ón°¯7ìNŠ_s`ÏÏb’{gÇJkµ/¡*1NÜIjœœU>ÁÉúyðýibÄÆËEPïfPݶ_ì`}ã9C‰gCÄÖqÍt ß@ź_âè¼k—ïlц¡µw錨°ä|SÞ§1NÝïeOILÝë盨zypðkÏgWq~no¡wð¥|üôt_§GÁDàß ëÁÉ“Z¹46ï,Ìg^žÁ’^C"åì}ÓÚ_.)ÝÿgØ™ÐØãnéIJ{*Š®t_VSäâø·ÅSù¼ ¼™Ajÿa6O7Ts/íK<"ö¼a‹É¦ye`ú9¤Þ»³÷`³ß´öϼ‹ WX×wñ5¤@±ìßNÆGêÏýå pi\›‰öN°Oè™Õ€qå›r¿akÎWd|t Æé¼ÂÅèôƒ H¼}êê…÷à­‹cD Qœûõp‡žµìHß° òzôÔ™zŽæë”Îé ‚)NíÐÎñyÉ7qˉëçv‹ž 1ƒ¨Ñµ«Œóá‹^—~Z™ð`©Ìä ÷àÎq§÷Û:A|¶ÓÖ0GwÎ &Œ§bÝu~àimÊ9æ& ð"wE” œÔ”û‚ZA—¾›/vÙÉx¾2Œsååý]¯GeBßç¸5»Ýv‡9ý(„“ÚûvÌp‡÷Ñ=;.:L(•cw§©°ié©SaGÆðùÇæQ¾ËÉ|×YS:q둬~0N£½2;ød‚·Þëð¥¹Pcjtß[÷ ¡KÛýY cÝ9o2˜ìLÅÍôqSÏÃ…>+ð,ý»Ødà±Ùñí'p?e¶ï¢À8#U½LÝé‡O¸\˜#qÒ=[ë¶+®?uƒ,w NŸIt»v_)…{§BÅ£&J9×a*÷í¬­æ û Â>ª~0ÎÀußo(B2¡Wò—æQ¹ ²¯.Ým·¶Ü.…#>Ôˆ<˜|\øx×8°TýÀ“AX¿1nãGñî­é]ú©~Žjúj(—DzŸ³1õÌú¿=s¡š÷í=EA…ààÑfë¥30~Q0ùác0-𺅚ËÃ|ö%jΠ°žau#ðØ¯¾ãØî–ä÷´Í„#“l ÅÇW\´.„:χ¯M rä}r|Yf"Ÿƒ0/öÅ„ý=ö\dý@‚qfúzÇZfB {-«Úry}ÂÇ øñSdvœ/áOzR;ä–PìJAV·SÍËc¾÷Õ8ŸÒ Ú|¹úðˆ1Û‘bœ'Û«oÛ5>êU ÙP³,¼^TºÌTË€¦žÜ÷Z›ïót†mÝ,šö€ã±77èÈx‚2ŒóvWÌGw½LØ´|©cÎÕ˜MË_§ö5¢ÎÝÖ°öé yÉj_"ðоEë<©?F&‚ÿ³ìP ³¼fõ€këiÆæÖéÛ+Më/‡€´£ÚÃZ‡±úÁ8c×1:uJou.:‚. ÈþV—žõ2hUh Ó†×=؉?)ߨ"á^5s5/RXo1þKmpîºÿRÍ-Õ\OÎggõC_ÏûÒÅ÷ûeÂ-ý×µ³sàE¯Y“Ê  èUâ§ζ@«&úrçæ˜ÂòÖm¦Ô 6¿¾õíÁCÍH•úóa¯ÏtUý`œ@Ëlûo#3ákLcíÐî9ñíšË±Û°ÂàéÛ$=haPPûµ$@½/͸FfÜ'¼JltÃcE•˜ÍçÌ9g˜ï‹,—Ìmx#¸õ°L ]fZÕ]0®k“têl5l]«3¨–óµýÉó3°:oÊ ®aç©îŸœÿÌêÇ­sgI÷Ý2¡Ûè'Ìnß…dÇó¦m*€Æ5Gµ¿/…ã£ÆY÷%WŸWÃÖ9¶é}>ëSçHÎäü‡·bî?/æ<3Î]çu‚q–ܳ¿Y¨›Éçw¡vÈQ¿ýAðxý˜›åîܯ|:çX©yj¬?[¨÷Áž3[_Œ=ãjû¤ÉÜÏãlÞÙ,Òóª¸öùÒÂwAkÍ݃ L ÀráÆe#O`üÓéäS—ñžöINpd2%é‚0Ïâ¨êÇ{ÿëhã™°ºᣥ¡wáÑ‡í Ž `Áò}’=©Àö±ýȇí²lœç3þîdιcu%Çqjv\Üâ°c&,½°sb°Í]8àÝ1KŒëÉ—K¶*së¸Ã•ÞW¤A™¤ú‡o?3Wy€ñúóË,#ÝÀt÷‹oAÑRöÿUyãiÇ¥žêèž {R7ðë~Â÷n¹XÓâF7*9ã”:Ö"*¨Oû<`åpíŽ÷Ü!D'ôÜÑnê|pj`X.{/ø”ÜÏžå5Æa<ìLø4ÏÀ$ê]6¬¼½Æ0áZ>lwïK58Ó·ÆmË3þjÅ ÔÑÜêõ}ñ󴎳zîçî³ÒѦÅÂ>={>1þœÖ¡r uóÞø5(åvÖlÈŒ³È›q(Ì»šO̲ÞÅÎÞã~N'?ñé¨#öÁ7ž­ï§ü¶ß'ÂñTÓš»pÎáã¥Â•Ù0$02Ãb}>ü0øø`—Ž $Y­ë}Ñ‹°ów˜cã}^ÑÁŠs€Ùç&Áqµœ645¼Uà–lXæ6«ÖúÈ|Xš}Ü¿ã;[¶;Õy wrëójCŸšNP¸F\^£zÌ nÛPÙW ÂúT•Ÿ8žõ‘‚'P>®Úeë¥Î3>¹çCó3ú9às‰qÚ ËKkÐ_±ñ[ ï&ê^6ÖEÍsžKluÄÑÇ~4{ÎÊ0N…÷ó°Uç®~΂%kè­4ʇ÷=«6Ú›;øé·#÷?—‰:¥Ûçþ¨ùsÂ:MXŸóm¶¿ÈâÈ1ÎÇs3gd€hþÏÈÊÂ,øÖxO¯ÍÝó!*=ëæ©}R¾¯ìBìn—´°qç¼Ðñ p2®ŸÀK`\:CÞWXÆñ0~dNêgÂ6ãŸRå¥,˜·ðº¸f>Ì}²ªÎÁ\_çù?éö˃ð>l_}°çöp`yþH¼«kõ9ËZ<âûâF ¬ÇUùŽqžÄôy 3áåUUK²À¸ðþ-­²ûÐw{Oíˆ7Îÿñ “(èÆOͧ8¨ò°…øŒŸÚ™åûárɉ" ÌÊ„çNå¡“² zXóC¯ß‡Ë£?çÊ;¸ã´Jù>¦¯šçÞ?þ¶Àõ(,Oþ°¤[Öß1ξ\â™°âåâ>õ»gj™²ó>Xî“™´&…ôÆ/»‘Ãi«/nvñÆuíËÇéÇëꞘñr‹YþÆËa ÆÙÔŠ.”qý¥Ú¨Î?¯¾µÌ»ËGï÷¸VÇŒ«7Öa©im°ÈzeçôN0lð´ÃSÔóS6ox*øÕ«nö[Ù¡Šñ ¤‡Òî¿·Ì„oÏÀoþ÷;ð q¸TËí>d8OL­3Ú æ½H˜³ßÖ‡°¼ äç¿vg@ õñé…ºd¼Ëñœ7Ì8S2Œ¸oK¹­v&\ gGÏI¸t×h¸ñ}xº1h®‰•=èl6Êxùއܿq£$1ÚL}n]jn7MÝ·…óhUàxŸô¦®»ù1ƒs)î€ÁûcÃ%ú÷!ïÊ ¿ä³6œâ¥Îßsieͺú€ó ÉÇÆ§ƒ{›2“os¾ˆ…ó£oNÕ+§±TsåUu‚q"B»ôéõ0ƒ¿ž;в°¤YtÛûf~yÛ¬<+¨4o^Ñ,ÂŒ¢xú&^ ð„ýî/9—,÷à<‘¯âŽ '5s*ÿÂóÊ41«ïò­•'ä~ÓwcîdÀšñ9iÊÛp¬á‘´ïìinÛR[Zqþ¥#é¡jxÀÀ¢ºßfY‚°~â¨ê!×ï­SÏÞº˜ô©½!ú6äuÛòæCÜ[0à‚ß.+>ÿ7'l¾ê¦îÓ®[fùöf~](¾lÁÆáx'H7­_›2 ¸ËŽcåÃoàš†/QæÁ‡ÁÒèÓm„ý-Âø6.Ðüù³.•ËgÀ\ã5ul U'œŸ ç¾O.P`(_?cœá9K^õðÈ׳S‡Žxš u7§]N˃P#:²Ε$¬¯8ósu4Ú/ Ÿ9[Íç>WaÝĸÈl?@ŠqöÛNÒ5>Noñcï¶Lˆ\4ýÚ’Syàô^ülÏÈ©'ä6lÞc ˜6wlã k`¼OSõù Ûx'îK1Ô|/UÞcU=•ݧ3½nN6Ê„ô>«Åúëó`xnËW#¥Ðài/ŸQÈÞ]9}²^KÕõËòç¯s~UÞãxì\öh§Uïô"Æ™*næAІUyŸÚºó¾X$=ÚçhØÜr)V­©9û/ª¼ÆqÆÇ§Å\} Ö>«;q”<*Žæ4̓[G{vãÁ÷zÀͨyÕõŒ¼àÓöÍ7í›d|‰'>;©û‹*q¼Vò€!/p¼‡P,Y8;¾·_Y}^ç<¨ß;¨aŠ™Î‹Í(þ/Å®ÔßÔÜ^+„÷­jNÛ^å|>s¤\¢lÒâèŽU·@u|h“{‡6Ïû~ÞïY¡«ï}Ï…l¯U8™”ÍWÔ¨¡=èßn7Ê >¾?Ô¹E²­ºÞøy!ëç8î£-¯Ì³ÅŸûxÔöŠ÷·àŨ¥Ý ïÁ¼&‹e¤Ð:ˆ® < ¥ˆ…L ážÅÚ^Zš«?aþÇø!Æ¿Ý÷`œ u:¯ü6 öêT[n~ ö´Ìl¾äê=ØóL¶à ™8.Z-«gD­»ïu ئCIhVÀxÈv Ü‹¸$;ýbö¾54çŸ7[7I1N£þQ&“¯¤Ãº¼}“¥¯ôÌØz.v°»òÀŽÐroÂ÷Oy¿ó„üÝM凼 É›7«'Ö€ð?ô6Ó‚9ÃZ6¯=ÍQÍ-Tå5Æa¨t°¼›õüq:éͽÐhí=x÷ñt\Ç ûB-?wÜÖhW è«€>3AI‚·šëSVí”2uòOqÓf¹•};ýÆ?”cœÃ·÷?üµ6«EépãóþÉsîUþ·qÏnXñó_’=fkPEß@¨y«ÉÀhoo¨SÙÒÄü»½ú^ãŸý›ì¯Ž²É|ŠÏ‡0ΫiEÓý Ó!µ‡çÓÿtø6Ê(¢|Ú=Ø8àQÓ—ž6œkïLú|ì£ýdž?ì¹Ež#4¡86ó êù¿OØÏÑœG*1ΚìG·‹Òajµ¥s¾j§ƒƒ×…¬Sð}»ù4ft{`¼ç1äÀÆq¶¯|¡hð…û‹Jœ8·n¢:Žª.Ž–KÚÛu¹rw`:4<Þå}j4oÓ÷¸bÈ=¸ú=F48Ãhwëq¿+´¯–¾ßÜ‚2Ûÿè?6ƶš»}£7ëU=àx¡¯g]<>Æ:zdÕÝ›YÓƒ;=o}¯K¦ò:nÏïóÂ퀛×΃ ßöÎ V¯_…õ•°>×2 vm«µÀ³4V5þÑ5ª¦3ðúá÷J‚8÷u,xªƒ&×Çþ¾nÅñ®Öž÷ìÕ4ÎSN…^ûnIê%äÂÉÇ¡û –9ñ{fv„Õ}ôq X½«rìoõ)Çqb'®DMƒÉ‹÷Eh­JÇ‚m»ä±¹0óçÂÌMø¾ÛTÒ®§S×WF3Ô¼«Cïf-ÉYdÇòõ¨Ð¿Óø¼2"^ØmÎÎ…Ý—ŽvŸb Ï<|œ¿ß™D-È¿»ÿH P¹®ãÈ®6iå#ŽáL¤ÃüÓ@·vŸK+“R ¤Ù‡Íææ‚|ÊcÓ»C¬`ÀíÒ%ckNåËÞ'×ƒÙø^«ûÖ\Áúó±r‰êØÑ4 ̇>©÷ff 4\ûJÇÑ7WxŸáè¼™;FòÒÏ3\üÁ¥Ñ•7ºV¨Ç$ÝÌôeêþ¯ÊCoèS嶬®i :¶ÑOÙ—úµtÊvol"gmÚ5ßÜŽsœ|AwãO—zWBA%¶s–©Ï’§GõúnúÛý ŽÏúC*T•”ʯ}M†Ö^5Ë…¶‰×*Ê.›ðýòá'˜ÅèûÂŽá]Ï›\½Ÿ«>î¯ñUù†ã ™7K–” ÓmH§&C“çóÄÝÆæÂÖP <4W½ßµzlNؾ·$Ä·5ô :'Z¬ ‰d}Ç9ô¶þ©®©pyUôž5É0¶ú‹âÄ!¹êýn¾n$ç6Þ°x÷ܶ>y1ðûa'uŸPåŽ3°1Ú J…µŽn_]Æ%Cý¸Á›Ÿ÷Àü˜­õúçd~^ªCF¿ÝÙWt.®F¸l"߯`ûØ 'þÖüâg·Sè›°}ó«$¨ëçñrI«\¾N·Æ™ i7Ró—ß×¼ivÚ—í‹) Ïï°«}¯v}I èDWî¹°ôþŠîÇæÙÀ¼N¯þZ7Ê^ÑNÝ$p¾¡[XõíÚmAྪòíx¹ÄHM-Žo†'Áµ˜v}¯½Ìª»¶K´WïsÛ<ˆOúþ â¡UÃÖëf²c5D¯Ÿ‡©ŸÛB ÷ó4×É"Œ3«Íô#WÛ§À| Åš'Á…;¡ r7:ÛŽÎmâŒl²Ó¯)޶¸vo³óF~d—›ïÄÉê}o!Þ¼ #ý;ú}_‚qÆ\Ü:ØèE2ÜìOg,IpÙë]æÐ‹9°'é³ïË‹N0kÅó O ÆWœW›Õç$uU>âxƒU¹d°ì>ä\µüD¸øâ`Ïê;sø:Á‘Ï/‘žŸ6$ûÀg­â­ƒŠÆCƒB³º1α ÇI³øé– Uíì×F/Lä\·¨óé}µËí@o{¸ÔSSø½^_õs±Ë@o×)ïõ|Q\Ý_lþŠãmxúUÜ®k2¨®]‰áĪC†ø¹õçþà †«¤¶d%½~µÃ‚\vVù/„3¹sHöÚH5/¹ å©„‡ ««ïÍiÖ·ãÄ–.=Qž­m8f _ïvWsù„u,{þ€Å7m»ŽÕøó¾†ú~';÷fû½"Œ³¨#%hâ뉵<[Ç1:UÎ 9Ü+rö‰ÓÚµ!]Ï¥ÔV•ŸWl°S*>öx{ixµ+˜Xwö1»GóS|‘.ïþöκ©2Ûã…¢”±Œyd:/‰ žV”îð”B €A@ D©ŠB… ^ ¨¡(`ñ•gAÄðh“òRÒ‚˜+„§‘A¼÷ÿõì/ªTç.î]‹uËZ¿å¬Yš¦{ïóïì³5¥÷šÏ¯±Nà ›+‡ò·Óå>]Ç$n£Ë=Ll ÙOGÖftèÑ­Ÿ{¤òýѸèïMö 8ýFædÝ/)IñþjýB?¥(Ïi”Ï·àó'–<ÈÝN‡ŸÚpýé­4vÛÑɱ÷Óß4X{þ3Õ#®ˆF®W6îIöÉs åüä§”kׄÀ2õ¦~3Ö¹+1ãìcMÇ¿,œ³•ÌKç¾2ûÛâh¿™ìKø`X‹³OLK²ßøb‘ =ÉšÖ>iLÑ)J\_á~³4²Ï×6ÈÞÊç‚X§B‹*µ/Åm§J»— >>d+Y¬›²«˜¾™½éÛ¿Ä ¥±ílÖ™¦¯ =Ù=ÿhk £kFGŠ]ºâ©Ô.?G÷e2Õß›ë…>ñü6ò‹mhµ­ÔzŽóXìòbJܶ(;í¯CiÇžý?˜g 1¯·üûÓsÇ‘ò¼œ¢÷×òþûëküøå¥?¶Sô¬$o°Î›çzì1æm£+V>7kg½ä&dh1ß÷ –ñì™öK;[¡3“Ÿ›´£Ùkï{òåôæœ_RdߤÒoH¿k×öU/+çh1«ÏÎÕXq÷°1ÛèÅð‘«¾V@Ý_¼í´Ó#ó“’æÌHÊyoºçzwü…ã™Ñs¥ÿ°mt}ðÀü ?DÏ·”þG#)÷ìiÅ:%é«o£ª‰W»Í”Ý}åÁýæb:1ªúŠ+­ðóÄ'¡)ÉóŠó£F½¶ú꥔VÏæ÷H™Ð”þPå{³`=Mýsd“­´ûc±!ÁïçÜÌ*;ZÓš5‰á%9}ä}€ç5ïÃŒ´ç¸.³$°Îó=Qu¦‡(±×Â÷ðsÒ‡ÕŸ·ªˆ6·ÈJ¼fLÜml=½þH¢3±wÍ÷û}¢?—ìÃû~ÑÈé^ëG–Å'ù‹ùúÏÿÁW7þ±ŸòéÄ[þg'/õüµñ´Ÿf‘«JÇm+‡Yøœb8ï—G+Û¢Z£çVÒ·.ëÔÙ"º‘Ò_Ï׬³¿Ù²…ç_È'×á—{Ýå¥õ&T˜•QDÝZšò»|5Œû‚Lôy߸SÕ»‰ö_Ëþj÷²ŸRöK+çF•øÇ:½Ïî)îµ7–ÿE4zèóÈÆ®6c=R²‘Nò}„WWwξZyûìÛósù–$=Õþ«=.N‘þee?Ô@‰¬sºÊ+‹‡½…¾¿ñÖQ›‡f·;vyÃ"ZÓaèä_Æã}lGÏSñ+OF–f’|n$ë ìW–})JÿÈä<Çj¨ä֙תÆÄ-»6Sé;›ezh_—P|‹Ø"Ø{Uþ¦&ê=0íÐ}Cºy†”\ØÇ“â½~„áSjä¶§Ìé5/~Û(œ"ïãä{mês¡˜Ü3†K ϽÿI…Íä>åNogöÐ] ^ª:ñ”ŸÒ]|w×)OaÚ{ãzxzn WkÑ(“¯[m£û{ù}ÉŸçüÙþO?oèÈ^ö¦JÞ`οNo0}õ+ÙØyèþJwdÞåÇõ¶ÙøšgÌo}=Gû‰+ºÏ)ZrÿcÃèïGþ<Êu0™ßŸSž“°Î—-¯W¿xp#õ?r"ñý]ù´ù¡sÛ ëüÔ1íä^³þ´¡yvíóƒÍžÚKÌzsÒs”9vê¶Î3Zóù~£h½•ÿ”}äu¯jú »®’?Xç­ONW^}ÉMÝþ!N¨ò‰æ¼\±Ç?}1³û˜Ø‡Òeÿ ¿§•ÁqÖ—pS®¨#ù¯ÿøäÚ¾ue݆ϋ•uX§iÉÁ¡›lï¦<Ü”O½®¶XTïu?5¯2lZƒ=éÜoØß3äê‚ZîÈ ®%7”©ÑóUÙÿpuÑæ!ÍÓ.¦È÷<Ôþúl¬S{ÏŸÚ}ú½2ªãÛÛçæÓÀãµ'øiÆèó¡iȺ뙶õðª-±Îü¯ÖnY‘ŸÓ=ºŽ²èq“OÚ‹Ï_»ö›ö†/(ròÇ=›æ“±ÛŒFÖ!~:ÓeúÂï>Á}GýHdM±ó¾>¥DßÓ»é<Ÿwòºûú¥]è˜gÒ¸+ò¨{âÜî× ~ê?¾Æ”m!d,H÷½3y$ üørÏæ-Fò¾ŠHÖ)yŽ'÷ ²ŸN9¿Pâ6fíÃwS{u˜Úv÷³çщíצ׮ï§Êý×ÏäJõM×g_Í ‰K7öjðЈh?˜rÿ` ùÞ–Ü7ÊóT¹ß+ɬc,9 úœ~ÎõÙÙñy”ÚnwAFE?ÑŒ?/JU®÷ÌSͽ“×Uåy´!ºÏ–û_ùþ–rÂ׬3tðòâZ‡ÖÓœ)›6=µ6’:4Á%m-;Ðè¤fד”ÐðBÒnSšÐfÒîg7áýI§è}Ü'Êûq¥Ï3í¦÷a,X§ÉÃ}RÇ7]O³Ve?3óë3:Ü~’9§M½Ø`èê³}öq ¢zs6\lÜdçáŒAókÇSGQ¦‹,TüÖ«égÎ Šæ¡ìx%þÔáÓƒz³ï½‰’XgKÊøƒŸ$}Fï­n¼©Ùµ-4üldÒâ:ûèµõ/Þ÷ó©ÇIÉ¿ÁÏTgÕZ½2i]j¿±U3Fò÷~2zß Ü_™øüT9/öâów_à:ÝRïTΣ̢#S;]*¤›3šthÓOÞz¶¨Ù6v°äó8åzß™d+ç±—RÆ´\rŒéæûy¬#žfÖ¼¾–ÄÝjÍÖytt‘ØXÒ£çÞ«ÿT9ϺŠ×ÇÌ >#gôl™Ù5Ú÷$×)ù—Ö1L«½adÓæki~‘ïÛš½ó¨k‡päÇÕ…4·hpÅÔ*=¹¯'”}Ž•äû9¢ëço”ç-Z|ÎóïÞuä}sI¼Õ—Ø#t>½8q^!Í;5¶Å•O»ó~gµ­#ÞPA1³ÛOi–þß·»é|Õ€Ï{C¼®š¸†–\øäËÔ‡óè×a©o-›\HûîIzáoʼÀ×ݧÉ)ÚO>O»OüÚ`ÿÒñÑý“ŒåܪËMçg¬#ºlꬢï^:9z߃y¿Î»î¡' ©ràK½Â^ôŠñÕ¤AûLô¯¶9×özšd}”÷ÿr?(ד}íêó3ÖÙŸ&Þ¨^Iõ·zâ[åцúS ?VHwv:öÍî¾´ø½¶'[wîB— —Æ÷o1"Úï.ÏÕï dãó^zçÅsÍLËéþÿzÛ3¥[ÕÇMÕ I—ßæž{¯ ù^X¥Ì™ îí<2úžOZÉ‹Û|~ŠÏAѨ³tdmª¹õÙQ¿Þ°eï„à^:´sI–)iPô:¡üÞGñû¸:3äŠc’ïéè{,å3’Êg$9bî¬I1üwŠpªJ—•½)r>’œ+)æ#Eý’˜n(cfï­ rfor)oŠŸg­È™½e¹Ø›,åV½•›J:ìýSÏ_Ýpb¶dy-¯±Ž˜;«ÆÆñÏ,< ÕÜr;¤³ZΠËQÍ.÷ñ :ᬖ>ûÿ¤WUx©,<Ç\ΑŠa'•pª†…§IàåYæföRI¯ªÄ!1l H€$±ƒ 0"Yr ã!`Bâ¸Éãa`Fy–½ªÒK%ý z•ŸA=·×ÄÎjm)7ƒWå£ ²*—“°´*ÀÎÏì•N*õì9=û¨B<=›“ÖÄÞ?áfÐ±ÏÆËs{m<ÇS$´‘ݪAžA—Ë n,c®ç­æ¢Ë¹ž†RŽá¦Òªæ{–5]ÃŽ†P)ÿßï9pÔóÑì¦Ò¢¨8yŽ•è„s–רòsçÕØþoâØƒ#ýbÎgØ±Š»úÜ&vàˆÙèÒ¯ªcïŸz.ºp«:Ø­ªžïià¹}•_õvÎøt‚00±_Uø½ìÀÇsŽ-ìг·Ú—¨Ìõs°ÿƤòJ8y¶§t‰ÙžÂ-¬«Ìü+í³=M<÷O$¦™röŸpV‹Ùž>žíiåÙžzž‰îcÏ„˜ï™Í3MìW=û&¼ìœ°ƒ ÏM¶—áÁù=ÿŸÚƒcûï„IåÁ)Ëÿ'æ|æ »«ƒe¸«³Uî?7õœOáÿs—רòsçÕX ÿLÁÅ¿#ç¾ÛÙA!Ö dK%Åa-gÁ ‡µ†Ö>•‹Ç§ò«Š`·?HFÐg³[ÕÌnÕˆðP |<7Þž1éWõ‚$† ’ 4H’Å ´H'3Ç tHˆ ’ÈtìW•ž1éŸHVù'Ôs”Íì°Ö•rOøT~±PÅ/殫ÌO.í ²ÇÇÎ3”¥cL$¨øA2ûÅÂ<>‡½ªfv2 ÷„ž}>>ž£lçÙ«ÂcmbǪp™Øc-f)›Ê˜Åz«ùôr«±”ƒB¸Ætª¹¬eͩײƒ"ÌŽwžõ¼z»Æt(*.á™õ.ºq¾õÿ©Æ–××;·¾jùÿK`Çt9怯cõyÍì÷‰¨ü³zv8ªgÔ'#è÷(>Z‚ß"ìpt±óÌÀγ00 )r@ÂÀˆÉ$‰„€ ÉâZ$Œ„‰ã:$ ˜k+~Zá=s?Ш<Éì­ö-’ËÉ^³ÊÙá‘Üp£itÉÚßúÑê+Îêž[-i> GbfqrZÙ‘¦ewc€]·b6½Ÿâ­ŽðLëvû$#‘À4HhñŒkG~Ÿßó8ªý>ö–¿õ˜U~Ÿ²<Ž& 7в»:T†»:Gåpô²CÍ ¼<çZx½åû×òúsçÕWÿ…ÄTáÆ\~»@¤¿[ƒ@¶²CRÎêIíÝŠGÒÏ^#ð«<¹ÂŸf`@Ðç°#×ÂŽ\‘VàçÙþVö§IO®h&$ˆh‘$Nf$‹è0.$Žè‘ GÐdH­Þ4éÑ!x\ìÎ4éÉâ ²p§iQœuov§åÖW|i~Œ¼Ïn¨ø±m_bP‹às6½µÇç÷ü‡jðÊF} ­Ëöš´^ +ÃeËŽC ‚Ù—R¾ß)ßïÜyûdþ;…Åw‹Àu-‚× ÂoöeÛ€èÐ.:¶ØSä•/VxÏì Œú\vÅZÙ‡°Ð!lì=“¾X?Ð"1œ ,|ÚH/Ð!I\ ,HÐ#a²8i¬À’…?Ä!l ’Ù+½g¹ ÂÞ³\á7BrYA.'™ðlûÙ› â„óT¾³HÅwæío}gav!9AHå³°KR#úy@.Ð Ñ LHx7Ð é LH~7Т8A˜Q¼@‡bàb÷Y2Š‚ DÄý%ŠƒèP \ ,(> GpeqÁ>%_N%éL2±—[¸“’QT²A ‹d“’âÏÿ´ÆŠúúïÖÖÒuµ¡ò+ú?©¥¥ëèí¬¡¢~þ;uSÔIQ#K×ÄÛYÿ7j¡¬ƒ¥kà þŽÝâ÷[ÊýèZ—„±’âË–ÞGáË6±ïѾǸ¸[»„vá³c$# ³9(³Øi&Ô77Ð"H ̨o^ Cк@X¼> Ggq[$#˜³9 -*w›hÜvyèèYìVàÉúlWGq»•ønÅž „A{³çM¸o-õ¿[ 9 ÷µ½‘â¿Õ±Ã1 ¢ž‰ó1Q»<:$ „ÅþI½2H¤\ A29@èžFÌ ´H2'3ûÞ,H8ïŸp4Zˆ> G2ºØÏx+¯µ†ÖÂk~ Þô•Ÿw•ïÿbî¼ýŸ‘׌ˆïë:¯ D*ÞðuëÈvàzt=Û‚À€w Ê{«A°;@˜ônvÞÚØy›€°ƒ Ð#ìÀ§òÞ€‰á`A‚ø€I’ʼnb~Œ„ÉqH’'$ ì  ì½ÉdnN*“ð~-’Ë–¨8ÀãØ`çmH@ÂÙA‘x¹œ|VàZ-þ†ÿfï= šìºôoìØ±c;vlÙ‘†"U$ Q{ìØ±c;X*' (MAÖØ;vìß¾sιô?3¾óNùæ]ëq­ká¼óx6IöÞ÷i¹~XŒÑ¨b” ‹2U„’cqÆ L±@U¨B” Uƒ2ÆbuCiï6„12…â•£"Qz”Œqoum?œ±o…ÂdÜL x Jƒ’`¡G ŠPnXðZ”‹>U„rÃâע̰D¢ŠQJl)(slQ¨b” ›Bk JT ÊDk¨L”6 µåi† 2Ql¨"”6-Ê ›H$ª寸âE(6•h” Pvf4Ð:þü©Çþ_Ûï*Ù#ÿ;ö¼þ£ž÷î{©Œþwæfÿè˜ðzµ(3üp#PE(7üµ(3ü #QŽ‘r”µm‚¶‰1eãêQrì11(Sì1*“+̹0¢«üÅÇuâE™a‰D£”˜$)(sL”(öà De¢,°‡¨QƘ@!(J†‰2Æd D¥ Ì…óILªBáL“J2ÆÄ AéP2ìÑ(ìá(sìQ(5&ž1&^ˆpwû‚ ö„p”%ÇžÓ’²°U¨LÌ"á‡p3J87îc`rjPLД“Tƒ’`¢F ŠPn˜°Z”&m$ª¥ÄäÕ¢1SPf˜Ä‘¨b¡®1™SPæ˜ÐQ,©Q™( Lî¨ÿ€a­AI„óALxÝßûbÏ‹ŒþõæEnlL!iQ)(sLÞ(–ÀœÓmމ¬Be¢,„½yTÊ;U(ܹÀB¢˜è”“=U„räעÌ0ñÃQZ”)€JØ»GY`!¨P™(s,ˆ(”eŽ…ÅŠ#•‰²À"Q£Œ±PBP:” &e‚EŽÒ£äX<1(S, ª%ÇBR£Œ…=}”eŒEå†Ò ûûX\á(-Ê„1Àõ(9[ Ê N…*D)°ð4(c,¾”e&ÁƒÒ£ä¾?+H9* U,ÜÆâÔ $X ¨"”ªe‚ŪD¥ Ì°hÃQZ”±p?…*æHXÈQ(}[ÆGiPÆí)O\‡2ÃAiQfX葨b” >e†E‰*F)±øSPæØ¢XDe¢,°¨YC£Ô(cl ¨L”65Ê›DJ‡’a³ˆF™`ÃGéPfØ8"QÅ(%6”96‘(T1JÉxãÅ(96•”)6–@T ÐZdéúÇ[rnô§Z²wþ+œþi^ôß}(ôªÿÉ9‘ðR„÷?ØHT1J‰p Ê?ä(aNT†~ÐÂ=/á;¶øA«P…(~à”ûKªHØ‹Âþ"Ç$ˆîq wd±—(±¤ Ì±‡D±‡m *e=D2ƂҡdØC¢Q&˜@á(=Jމƒ2ÁdÊDYûN˜PEÂ~&T4Ê“*¥GɱOÄ L±O¨PÂ|Hø~,&œ &\¸p {‚)öª¥À~ AI„ù*R¸ Ü©ÂdT {HÂù>&¤e†I‰*B¹arjQf˜ ‘¨b”5eŽÉÅ6•"|‡×7Š%o *eI¬Fc"‡ t(&´å†I­E™abG¢ŠQJLp-ÊLØ7ÂD× {F@ëÔˆ}¾ºßþoþç6s©Ð/9ÿ”¬ivyÜ¿ÞåIý¯¾ x0©nÎyØ_úÕ´''£2oBÞì»"ú.uùx¾ŸÓHà~Re,ŸßO·~Íú”]Yv”¢šïàüa“Þ6gþ”' d H{!~ÈÐy½öœ‡jF•‡m¼AG?¨¿^ñf~!}8O(7݉ñ(ÜDŸî+Äý~ákc[åá ÊÐÝpmÏÐýŸ‡m7'{·½"rÙ©Ï}0`f2Ÿ[à>À<Ž0ž Çë÷þΑY;áâ&ï+¥æ‡ÄÉg¼Ì€ê¦wul¸d°Î[„úª2_¬¡@9–Œãt¯%8¶¨áPÖ¡Rƒ¬ÎÃº× ×¼Îd0ßw8 Þõ̵Ï0Bù¿c˜/‰P.›Ï/>ÿ*O;ýö6ÛáLJ 7úœ—Ùõ—×X’iÕKuØØu8P_^'ò¸Í iãUc€swòÛ¨|ýRGˆ~7Üï/Ãff›Ñçå¿p¤ÔgIËéõ“C·@ Pæ<´>=qúŸ g?û`ÑØ‘ùG:’¡Cê~j:FäûqÞ*çþr~å˜Èa‚¯Ü¾u0ó•Ä8;›­¦ŠMãuw=žß›w‡ Ð|MôŸ©ëž‘z±&Ô·+€ù¦9¬œ÷kÈkÏ|ÎÂŒõÖC×É/Ô5óÎÃØIŽöc›dˆ>-Ü’ó8OûËóßÛ¿' ddzzCòj¨¿Ê±â°Âó:iáõЯéPÓs™²c„£è¯Â¹€W#®ßÕW’oe~¿~~oêüÀ#“–Cäüš£oþ8ŸgLJ—_‡ÉgZ¬ÔßχùóyBºüüþЛ“ ½Á¸m¢èçÅ}HW˜ñ™_Æ9U/kÊ; ¡þ¥;ö ÐÄo÷ùÊ{ÓaþƒæŽ Ý€ò‘”Ì·ÉÖ<Í|:­—-̨}Ôîþtk‘ÿÇ}Ãf¸û&ò_üö”ÇÏcË¥*X·ÛÈÁ|TlŠ[éõêyÃwë=!èQ|ÛǧE¾÷Ÿec1Ž!¿q¼£ç©p “ÞÑŠøÑN;uK¯t°ýZ­ûð‚¬¿Ô"-sªØî»ŒÜÊwµ9‹œÏl°÷ª#Î7ä5Ž?Èw`Ü×F@^b©Àu °$}ƒCÖ÷40²]T¹óÔœËK6øõÆ€YÛ£}UF0Þ± Ð÷¡XäˆJo[ªKE;æÛFy^ZŒ3ùZ¼mú\{¨NÜwuwDÌ«ïs) BÞ v›ÒȃùöƒvMêÝh¾ë¯¾·}ÖãÓ—ÎÖQ/žvÿ&å|Î ¦¾^”Ÿ£Ç8ŸóÞ¦}8=†lÎŒ™àìÄ­­jnHƒv¤¹_òp˜»N€ùŠ~=ä­¤¿M5Ñ/jåù+Y›Æ~ù˜µ²º}À¸Ù¬Ÿ*U‹0ÁN0„ÌÈÚÜ'ݪ_*$ —=8Ç$•ó.|™o®OTáùÏ!Üç8Ÿ‚òá¿I¹?vIf Æ¡y>—¸Ä)ð¥%@ÏŸÎs:âçsÍa—J>ˆù¸ºq?|‘ƒÛ§´Æýœ›Œwïí>à»Ô½ÀxòÜÖߥ‚ûrÃÖƒ˜¿&«ŒW·£ìÖØEdÊŠ eúç$À²Ïq;ÛöMƒz£N[èñêÊ„¨@%ócv‡œë·+ùôÍx’¢/¢”ú)~’ÖºÞ`¾½ñP¸¯YQ¾ˆÕÆI[zÿ™QÄ2Òç¹ÓÊÔ 0¢ì¥/å[§Á¥RÃNMlj íV/°±ž•Od^ã…ŽÑ?~³q´è«÷nŒ÷Òs?J™¡èçi¨ŒÓÌsñô¢Uäðã ªVJeŸÛ¬fÔ–zV¨0à ò¾4ßÙñI(<ʼ®³è{Ĺؼ¯p?õE½Æ%´³cþÁ4ŽãL2ÃW“Ÿ«…†Aी»ú‘ Ô/ÓŽßüFž¥L9¸œÛBãZ‹þqÜßû+RãÎcœ±ƒ?ú \Cn¬¿ö¶Ÿ†À”W̦ïwk#å·ŠkùÃUÇã[\weÜ.…èŸLý³‹¥~ ½•žöUÊ}òKö/=Æy±(hY׆ëÈæéwš›çÆ…O…6n×eªcÏOG‘osNÒ¼ÇKß±@¹ð#™oniQÄJæ§–è<¸oƒE?NCýœÆ<øråâäfˆÇ»‡;>#º;ÅÃê@*ì¿àl½q®3\ZÚgJ -|犓øµœ±`s³00àLkN€S#“\Ûü*寋úà î³h¨Œknmï}m#y.ØIÖÔÂ×GW휞 ÍóSlvõ€žGj—Ž»èÔ·Ú“½ïÖÌ§Û w[u¼û¾Ñ×)çþêãH ‡ÞLÜ -œ¨¶Ëd„,<ý6D%Ýõãc^7CÜÆ2_?W‘³Éyt”÷Y(åþhÔo˜Ž¯Äñ#ÆÜ»íÝ;i$#¯ïÓÂø^•“¾¥@:XŸ ÐûÀÆå£ðË~gà>ÜGžiš8ö›½³«êsiƒ)Ÿf˜1ž¶œùp1<Œ³ {^½Yåv’°§ÞkÒj'Báˆ-yÍO¤@#£³RçøÂÞ›ûv)ãÿ—Oì™õ#·¬ð‡åÅöÓ»ø€ »¸ÀŒGÉáóHk;~ºm“x“ßÇB·…rGÔÇßYxï"ÛÃF‰`7s^Só±)ÐT–ÙÐ"Ö‡ÍK|™Oß`hÕ\pb‹‡ Îfc î›'­L´XŸ-Íü •¢Ÿ¡n0΀¸ {»ì"²¥}&U¯˜Ý ü¢R[§Àu£»¥+.õ†F³2*”í3 ¸/çŸBíB'Ñw‘öRÌgzè/y¦Ç8]|º·¿µx™:T>®JŸD˜à•;fà‹ËZ¼§VÅ}°ó£g_‡š¾Ðŵþ<æ>~ÌïXJŸ×e!4iAÖáñÃX¤õit¦@vQZªv‹ÁûÈÜò[ú¬Zš÷ÎŒsZtö2|˜²þçÍÊ.Щô¨YeÖzŠ|ñt«„nޝlýèÇ8BÕ .¼“e@DE°Y0§F÷Ѿ ßZcïùD;Z7gÎù¤y_Kí'˜¬õ^,J¿IÓç笹 ÜÍ:õØrÚû×àæ œßIçS ùåÜn F†-õ2ÊÕfþó•!ÛþÊãòV^ |J&”ó"Ã8–²„•ªí'Ô7²Ü­0#ø2®[}ñöOX=+Ò?ñ¢·È÷Vº˜]ÿ>ž­Ã”Àyó=½UºŽ^U ^wÒÑø‘#›—0~Æ™`gÑ1¿tx¸nÂøŸ¤¼~)‡Žú!«1ŽWòƒ^ ×$¯'î«xkX,ŠÚ7ªíËKÐj’®ž@ùZ@9›v¢¿'åÄ÷gýù©”ûhO«~=Éßš•ãhý`œ{Aw-Úf"YkÇUòK‚ mN·yyü,Ÿp÷Õ–Ø‘@×KnÀ}5çtjеÉ^KÑ?˜ò… ¥üyÊùŠ%yžzŒÓCÀ½v>Bf; 4 È{‡u&]‚d/SëŸÞÍŸz€mÎÌ·ßUôÛæ>úüyJëÍñT#Mì˯=ŠºG‰z|Z»“àË‘koš_‚K'oV®U2_WWÎáe¸°&¾œsÀçé”­äœZ?'þ³-Ny’%”3›_€îl½Æ½N†O¦s|+*52NgçF(Gˆþ†ãä·G=µêŸمâ:Ÿúf;Š>ƆúÁ8¶cïcäÅØ·ënL¾÷\eß\c“AÚû˜Ëšû^¬ï„ŠëšÛþÓÁ’ùÜ÷Î×äÏ9îç~ÃX0ŠmFëãôË¿²|v÷b:ràÛ÷.ÀŠÍLJ'CÌ•fe¡œ't±‰ë>û~(ùíP4ªLÝxGÈ ìi4˜­K1Å)ç\Ó¼”ÐúÁ8®3­îŸ‹! —*®dáJçu^Óm’È ¦­½·kíÞ)âçÂ9vƒÃJuî6ÞO|ÎQŸ~9[WP>»Ç?\лù½½1d؈•®’éáUjà’>¦É`}b§CHÀpæ·&ò(xýq>`³j)µ}=gó¨çRÎ×NØ¡auƒq¬Ö¸Ÿ¿ð2†kfÑéᾋ ž\)èõEx·áæÖuN ímGσ“¡?Pgù ge»ãêÇÛöΩQ…‘±dIHq"]s_ôJ½ýTÏ..%ŽYóìé3ž¡¢ß,ã£1 }ÅÈJ2c $CæØÛ£v_„Øn ¯Ž{á'dg­–– eýÓ”¬ñ0Û´õ¦;P`ÑÏÞç8ÞBOéš5MŽ÷%Ó\®/M†C2¬öͼfi¯g·®â oŸÆv,s5”sOÈÒ5ÂDu<Õ4È_¿Á†ÆžÜf”óJô±§\‰¿ø>Ë0ÎPû—Uœ<õ‹¢±qɰîåŠq+/‚éÜ*áï¸À´£¯/ °œ w›àûäþ´º¨<–ïŽ"ƒÿäïsIÿj%Æi3vLð˜ã¤û¼Ïï}H†=Â~^ ¸µÆîà`+†±zíDøúƒó yŒãÈïí£è~‚8,®üY~ fª^¥ozy­ûFÀ’禮#…ñÏ‹pdÊc¤üI5Žs°Z\ý`í r6sÄþÕ.Áû3öAñÇ.@ÛåSW;-T‚ƵàH|µ0Æ¥jO~$/96…óB‡‹û[†¼Äñ*\­/á$iæ2göL|’$ï<öÄžžt¨KW_&<¦Oa|ä ”÷à½×öxÖl:{Þ‹ë>¡¼Æ3Æ8‹Iëq9V'ÉÊ&äÌÓk— NÆ»¾ÑM±o”–¬íòÐì[×¹(› u…ÇQã~P#ùQUówCA~q|×F\/yÙº´P~fûdÅÒIçƒöYösdbæ;_ KŸÓÁzñI²pVß%Æ?/Ád¯âÄÏ9I µ:ìžÔÃ41Áçúv”ƒ:Þ…Õÿ‘]Õjviµ¦ÐßOŒcÈgOk¿þuýSD¡ß6súøË06ÉaèžUIP/I²ÃV »W}VÉBÅu6ß­4wă”2>4_qÙ—ÖCçï:E®?üÔó{þeH]q9v³SD<ûT-e‚'œÆÙ‹‰Ñxð‹yà<ÎÇ–Íœòî½ï§Ðù’#ÍO×Ô¸´_¡î1l§4IZéSûìl–Ø5¸ëCß­tšÜ2@ô½oáßmñžÑ ½ÚÝçkj ò¸îPmô;©Ù€–…9}>J·4vª-Ù~[`œ´q­&o¼xŠŒ¾uwY¥€KóV5>%Båºý÷íéï †eCÃ@q™ó'hßpÎgáë&¾ŸÈò”æ7ÆÜ¥MŽœ"çÚ¹7.Œ¶š9Õ½–göH1·p†yOî^hu9ˆýÞµ˜þpñݾþäƒÁbC~ ïÏÉHUÖãSäq»º¤íá¸Ø|QãK{ÁMŸi4  \þ®ÑêסlÝМðç÷ë6ä/Ž32înO§f§Éð¥WX}Oóïn쨖ó6õsp‡kŸÛ®,sl ›Ÿ·&tÿßÖÝí•ð1ÃW|Ÿ)÷d€È•1äíÙYËW±ïwV>Ml:^9bâ’ )WBÊH!¬ãò/[€Ùó~X® çœobXþwfÜ[€J9z%ly'åû{|~Gùôy'Á8´"‡žŸ ‹ö(ódea¬À”y»FB¡QNåüK¢ñhÓér{ÐýòÞ"ßÞö¨lÓóüb)Ÿ¿s¾!ß1NÿJy»CúŸ&ëíz%íjž #òÉZXÐÛ|ÂÅJ iþåÔ‡IŒcÑšPÎ¥#¤ºlt*íkÉ|²0ûÒ⺂òhoã\Š÷rÖÁÓD ËMš¥AÒ˜Äe ó´Ý}¾ÓôR>påꮩíôÁlÿ±!áiÊ“pù±s›ßæU‚«©底ª$òoé¾,]—¨0Ž6½JCÿ柳ƒ€­^“åf”®×@ª…Ë×;„õhâ#žïÐ>ÙzDMÝx¨E޳CO¢¦¸n(íït´¨“+ð}CþcÇÊ­Ö:õ=C„O·ö±4(•ú¬ö”Ë¥„ÀyCÛó‡íUV®q}bÁöE'Á¶ê×jú•Ì8uϤgW7i¿íd9‘KP²k1N¯¯eð‘p†Œ2,´ÒàëͧçŸL!ŒìÃŽßx¼N³'?K ˜&÷C9‡råŸJéûöLäEÛ^~ߥ)ëûçf)açé ¡Üõt^8ÜGWž@DQNŠÓWw¨êXT;to0¨{Õ9`~ÚœõýþŒ÷"ãêä\¬¨V…Ì×_ϱêÏ[Ïz¥ÃååûƔߟ'V,¹û®ª d¼î¾éÍéP¶>’¾î8pÛ+‹r…%8N}f/uÖ„[ç&:F¥Cap×r—†'ˆóžàoKÒõƒ5ésädVÂγÄsœ ëÖt>~”ÎÇd8Þçßîô«!¹/²ÈM‡Ê2ùÙ)Õ`ð^ë3e8@õ’˜ûOàٱ k<´"”›ê ¯W_Áä#ú¨óy]· ºoÚŸæ;Æy0yÍ‘Nû5Ä€5o§»ösJ?gzn¯_m½6öl´¡üìÉìɆ˜Ù•Ú¥¼ä )ÆÑ!eüÅïáó6a6ñdñP }Ö’òU'Ù%óäÅ4 ‰­¯Ÿ7Þ=V4½–Ý*â<ÜÞ—]çòm'Ô¸sâèD¾'œ+E9.¶"‰Ö×3±Ñç#]©1NøàAÓŽhõÑÏ€‰ \&¾·<·æ *UÍÖ(G<˜ï¾w7²J©;ûZÛ=‘RÎóS)}ôcçl,ß1ÎÅoÞN>ª!aÂ1å™ ÆK<¯«Ý ïˆóBÊGæçD„ss)¯jë{Åy>_G–äÑè1ÎÆ°ŸnÕâHÒ­ù&‘¯ÀÞO+Žõ?qN䦌hØîÒç-!l~ߎÐó¬b^ñócÎËâû%×ûFç dY×rž×G XðÍW@²Â¹íbå9h_uçëZ‡]DŽUö°žvô&Œÿ.rèv­Û1ú½4C÷¹G—Ò°?ö|Ý÷wå@û#Û/Å8ؼ_fÇ‘žåºö]v÷ ª"5›ÿã,t¶/Lë3³ßç—ÊóÆ}#t=ê ï}‰¹=7l˜^.¼ (èk3Xþ6#Ýç“§$ÙŸÑçƹ‹³Ú’xÒ¨uƒ=ú¶W!Õ¯MèÔåg¡×—þ.ë›§ ÍV†Mô„jþÑK¼·"ƒ]cV„Ÿœ“ ݣڛŚC­ò»ë†¶d|Žq°@8F\ïOëã40Ç“G;_û/¹ og^XirŽDF—WXz3^ƒ3ãׄ–©r®F¸zÆ’³!½àìÞ‚sæl«=4•*éOt<­ŒÓýý°Ø]ñäªSãÐn·¯Â¶*Ýò­‡y•uw½¼GÚUäÓZ­2í&3ŒñIà³ïÓ{^µ„ZßZEIò›ÃÛŠùÓn ‚¬òÊ2Í/Ò×£Æ8ËýV)ºí‰'êï´, NXl²1‹‡C;/t}QàsgV¬Óh¤VÊÎõŽ —Û›ôo»v4–Eu†°ó#80àA ®w=ëû5mþ{gÆ/²¦õsžŸ×Å“/Oöhnz *.°<à{-¹Ï)çãô\2(G®5ã~÷öñ$™xÎÛ´êZžŸEÞ;½gÀÎ0ÎÚiÊqe<ù° UEÏk°©óÒi±Ëâ`éóI&ׂ†²sí¶¿hKøþ?Wç~þy{ódÏæv¿œo%ÈÌ;ÌIèèOÆ€××ÀfùŠÎ‘.q0Ê÷ÝÄ h9 °{3¹û$þ~jï·9ÈÄsð1‡5 {M× 8Þ¶÷ oNÄ“7U^ËM¼Wõ¯ëhÃt?ß´æ@˜í|aö°Ã“`áÓU'ºzŽìô÷ Þ'yÝó}3Î 3Ô ÆyrèѨ©ø9œ™Ù²Uÿ*™pøzÆÀ»¯5У^𱳇‹õ9B!gë¹”²ß<È긮Ñ=-àZçA™Þ…Œ“úLÊç |¾EÏh%Æy4tö2û ñ¤Á§‚^CºgB—yÍÊ׉ÑÀ²²ekäðbÉÑÊ`1*[;lU;a|2x(,Ç»Y‰q õ ¼ß£*åZl‰'SÛž¼Á=zE.yÝt´Bûk.ÁùVÓ·5t?À‰Ðõ‹-ï+4ßqÀìx°*žht[2= Øø:¸3ë©|‰Æ.O=hÜk Ìkü³Ü<£ìùï>‰ßUÊ+ÏWlO6všêO_¯ÇUôVxñ¤ùþcOÆmÈRÿyV¥¤3pÑt—s•ÎPO8ÍÃyh„žcyÂø§YÎà ¼Åy å«üº Çñïdßh¼,oÃÿž wú Êg j‹I /8:±uÐ8ÆÝjEè9]?q~ÏÇçÏ º?MÇ7"²”6~O‚ÆÇ“¬ éºÌ+™°ö˜wåfgÀ´îšy^eÄõ%íÏïø:›æ1}îKpŽy,<žì_jè›L¸m"ž†çï¯Øú1–Ï kÎcÙ=“°^8>_ÓñÅè82ÇðX_OZÚø¹%7Ì‚+i›Mˆ; šä-FZÙÀ±ûŒC¦°}{}çSšo`õ³¬«È4ä#ŽWÝp‘$ž¤ç÷Ý}dP„¹:ãÈúÓlßj »í~"OŒò'Ae“®­BDί¯ŸÏ=8`7è—}gÆyþ¨Jrã ñdÄš2SzLË‚¨õ·ë-š}&—S];9¦‹x>HÏI±ÿìY¼óFê@8óªà@µ¸"Ozò¼Õo_µx+èÅQ;ÿÂ÷Q q,fGw÷'AÍ^¼+ âÓ»+®MÄ×3¸ÌæQ9ñüþ†gÏVnCH¿Ž²¾L¼×S’[­Åñ®ïþ~ñèx"7l¤dÁ¤/›eãO³õ@%¸0ëQîž[ŽÀæé„îãØ@¥ÁýÔãÓ(/Iã·Õ`•9žwîfÁüˆœ“ËÆœ†3SË”K+°÷k?O!t>¡„‰e„ØHqßÝ—ÚÙ;¯ÅcÅ“¹/>¼Zõ) ûôÞFüN³s…&ìó7aÏ‹ÆÄ³Å¼‰!OÙ~¥ð} >_¡ýfà¯óŒ“ؤVÛ+qÄpýÁô:lÍètõªÛi |º.ìý¬Âò½:™´£Q¹ˆyîâ>‹õ‘‹Ýú(¥óÉOÒRöÀ_Î÷e§jh=Ÿ¥¡qdHȳ2©}¯C³m!þ0à4ðùÕiÎßîÕ°a÷pZˆû eZn–ÕX&‡›?úÜ>áT,¥ýñ‹”ò¿å3Ñ8JŒ#-Ì?üeDig\^‡æÅ V—ix‚7ÚÉ_}±…„EzÍò-ŒÇÔ•Í¿]Åû†tÿô»”®+ð§á¼m(ë/tý¬Â8Žþ¦SçÆ{ÃŽëpbüÖ2ñO§áb˜=dÉ{¯Vûñu Y3À&Çz´5¼;²¼.Åö§¿JË”¸Ã%k»Äîa:Ï#=ÞT+Ú³ð:¸ú¯Ø·ëh²<7†y8½;i5·ù†³­÷ô(¿MF.xý%Ã}¼<]ùJ˜ÌZ4·¸R£/R~ï§Ü°f®ÏÜ"§×n'Ìè¢×ù¥r$hée›S×áÇòÄim†Ÿ‚ò>µ¤FöÎPS{åAr’Rì³w¼ûc)8A]EôþUÚa0à°|Ã;ïÅ{œCÇ9І:Á8†ãã{gˆUßýîê;×ÁnÅÑ©-Ëœb÷ÜE¾ ï‹*ƽ¢÷ezå3Þ—Ò|(”RŽªÍ/ë£ÄYgsêx†Ô]”òôùçëÐgrÓV Ož„¨·—^íçÈê'&s‚ˬóº[§Œ+Üè½¥ v'OÊÞ_ð2}jórv+Z?'¡Î’Šc‹O“/–)Ó~6ÍÓø >^OÂ)·ÇÛ¶_ÂêÂ÷»®»Ú²{—Ø~@š”¯ãáhbœƒkÎמ&aMU9­í³¡ßå[{e'¡Ë„¦e.”±æóE6ß2'ô¹-Ê—ì͸Ò¥7 .½_ÐJJëÇ­qo‰{çE§Io 0jÍ©VÐä$Ìú~Ê'nP?~ŽB¯‘ôr^þ®×NÜ,kºîÙŠ÷[ù¹]¿üzþ©Â8Ò‘òÅ‹ÍO“]ÕÚß~»:ž?wvv+{ü¶n“’Ðzºïn²+«?a÷8 ¯çIq‰ŠÅ6Àï{òþÍïÿP8Ý?ScœGyÕc“N2*üE@l6x¸ÕµoÁ ȱÊ7ªëuþyÐ{µ Ý7v~¯ŽÇ1ÔŽWŠØ®‘Œ8EÚEGY5<ú˜§Í:w~?4«(R.î§Ò¾Ô€¬¶ÁjeÏ _P·š=cE­ôü Çûtìp÷¶åN‘ü˜}ÇÍ^dC£1ÞË,8»Jg4³Ï&ž×°Ÿ$áS¯ÀcÒ`ØQêÃ蜙þb_f˜À}ï#–ä6%È´SÌ}wœ$Îú\ªS/^˜´K#=VÄ3yñlGp©µxÈgsv¯²"WÆ.py Ï݇Cœ`Q¼û•!ÅûˆüD¥›Š¶¡œD Æ1\»kt’èÉOœ×t=Zëíq‘Në,“íËÔï•ðû±<Ž!Ïq¼Ø½vNý7 ”»™êÚw<½ç8±šs|Ò#Gv~Ö‡p®1=Ï”‹÷ yã|ôªñmB«¤qßè‹aËs ÆNÏÏSF‡j}h+;ðóÂï#Æî¹=kåªÑ@ÓB Åã\W¤½ýBçá8^6»–"ÇɵâóÞ¨þxø߯ÇÁÿæîÛÍo Ö çw»ë2=¨çv.1š=?Jéþ×Wiÿ7[;JNR>¶Ç?0,tñP¿ã$`ÆûE_Sr |Â.ÏcOcaZ©-¦½nÚ©û1Ý‹v{rÚðòU¿wÏSùýZ3÷¨keÆ¿—ÄŸŸ†<ÆñW xÒºÇɵ¶ïç÷9péÅ” I±ó©Cý¹20ŽãµmÑÉhÅõXÒ,rp£i’ÐÎìë¶Û{cEñ!Ë2±ã ”„s”)ßÑVäòß“ßoãçå|ÞaÈã 2ͳ>ÎNkb‰ñ‘ÅkÙÜ€Ôúç[ KHw¦w€ñÏ#¬z* ëÛ„ç5ç*sþ&½P,¥ç3}ù¼‰æ1ƹ;\ø‡±¤Ér]•ï X¡­æ¸oq,ë›-øyaû}„ò‡;‹ûKü¾¿ÿNï‡öa÷|Ñ>Žq6eûw‰ýC†ìk˜3û¥ß_av,”9a¹úÕ“–,O¼Ù¾¾%¡¿Wñ^ßÐwÙx¬õÁ·ÒŸÓ… ¥”}>ôü_‰q„Ûþµ×Äz/úxÍoUnb,ÛG7gßð&ì~1á÷Yùy=?wáïßâè¤÷¯ úåÜP…q"…íÅš1ä~Ë+M³oÞ€]'2š×öŒ…›Ö,ËýØRž¹\øÚ“ÐûÏ]}~‡Ù²w|Çõ°g½ ëÕ;)Gç›C·›ö5ÆÙzÏþÚc1/Ùy­Œ£ÌØpxiÈ!2ÿnC+£\ð{-Éò¶:7tµg{×ʸ¿¡ì}p‚NnÏ›0U/¿}Fõp©xöŠ6¨&ð{ì3b³ [·Ðþ­Â8c¿ººl^wÝÊ…KÝ wè¤¿k ¦±ÃŒ’SB‰>Þ×6|†‚ñ™‡±øŽâ½oú¼-Ïú–#›²}Œsӵɞý)Èç¡>{ç-Í…†‹^G¡û“kAáæÐÎHˆ–ݯß3À8Â*¡ñáýdlׇA{órÁØóÅGwÿ£Pîè}Ý3èPõ[ô¢ïÁ„ߣ¡yeÇö©ÿ:Oãç&ÂíÓ:—láÆ³C‡JŸb÷¤“ d&WúÅí“í#Ó¦|Î]ü!~èäisšh—ïâÖf¾*ûõؤI„ΣšÁ£íG_¹ŸíŽVF?'‹\\~ÞDÏ¡ÿú}1Œ³ßkH·;Ï÷Uί\>"'Jæ\¬tÆyLZ½¼À®u¨¨pJŸBø¹ðgáºï:'àûÿœ›ÌŸ'ô^ÓPàÏ}Cý`œÂ2¾öb¸S)º„÷ïïyøT¿X¥í‡Á°5ñ^çу§~ó“ÑÝnÙ@¹.™§:«íØsí”÷Eþý z®Ì¾g€q^ή£?{e7Ù.Óµu©šÃÉõÏEŽÀ»æÁî†9ÀùÇÏ.·þ<‘P®u ‘/íh¸e÷r¾Hé÷ŸÊ²ó#qÿËP?§‹ñáF~»É½¯ Oº6̓YVštÞt¶|nz°ÏGèeÔФÙÑñä„á TÜŸ§ë/X²ÛçÉY5q>YuŸOqD”/LxS´½LúZ?'-{ç ÜEªË…Ry°{EJ£Ò‡ájåijûáRχ¿zïÍ‚=',Ù=¾À÷§¼»äwjq¨ òêºrõ‹_÷µ§É¾Y“æ|Pº>σN/.ʺ†ç)úÐò XUj îÌ$Öç-Ùº²Ð{ñݡ۔ð„á+±ï›¼”ò{Ò%ç-zŒS¹ò« ýÌÕäBÀs½rBŒ6|1ñd$­¤ž:6}s:h=ðûSÆmªrù]oÝ)dÚáú½Ùþ~»ÇR ¥û#}ع!]]*=ˆÝ‘ìØré°q¬<ðý÷íÁ‘ƒl½h þ½+îí¼`&ù4,R÷ª Ûg²~ÏŸßWä?3F]I‘LÀï}ÐúÁ8[$ϤF¶‘·sënߘìǪ̈3ù ÔùtñÞ–Á=Aeեܸ™„Î3[Þƒw½*£„á¯xwÀºœ£GU[«K»“¬Äõ¶¡~0ŽI‹v í¶’€mofM<oƨ—çtBÇì¬÷©ÐûzÓ}¾J¡…pý…Ý/ðgý´ž˜ßô\Òã—y·ãxÜX³2Ô| ùXä¿Ëã\›9©e?ۃ윷 ·h¦/UkUu,²fûRŽ"§|Öö÷z*+îÃñûxW/TÄ)$ý^˜ ãÐùÄfòä^§¯aûóàÄWkç–Ùs厔Ë~ŽL÷ql ¤â×)5ˆóJ>W6É>v¬ì—ïO¨1ΈÞ3NßDl_;kû_˧ՕFUpP¼H¿ÿ;…tñЏÑÔÇŒ”êqjü‘Ζк@¸ïãìã‡]{,å÷è~xv®Û€ÖÆIºiÚ÷~«(SæÉr³—y0RXÎõ=.ß<¸Ô»ŒïLO#l=Iø÷è¾@ óãRº¯úHJ¿oÐT;&Þ—ÉëÒúÁ8'¦”¿ž¼Œzåa)-̃ ÷Ü'*ÍÂÝ/Ã[õ¼ØèýÙ™„ž$[ŽHi¡ñáóAöœ~ Ö­ÏŽlDã].ݯðÄér»õdëâZQöÏó@óðíEÓ{¤0²ãÔÑóîÏ$ô>‡Œ8”l¸‚óÔÖŠ‹Í óšæÛ ~?“Ö Ž¿Yi‘gnµ–•wãhE®ëYyìÑ`7ÉïñÆD9DTìZºÝLBŸÿMIÏòÛLúZB·Ð:ËîY‹ãóï»ð~d¨ÿã ·¥Ú¯&Âê NW|ÛâsR×ë8/T¼~×ÖÂV[º™Îú¦™xï…¯ õ€ã$§~¯f3w%©ßttç[ö:HëuëvÔöhhéúua`‚3Ð{µÓïOìûÀâ÷HJÞQ ¿—¡®–ÃrÛE‡çOµ)Ú?ÏyŸëìt_<œð{!'^ TNª n+Z^;J¼‡ËïXŒf0CØ÷IØ÷0ÎŒ=‰AA÷—Ǫ_ûÇâï}(jo±“t?˜î]¿üHìpv œdz 7¶máž}ó$b웯ÅÝúpÝ&;h±úòKñ~.ÿ^_É{?ZŒó"¨[«#È—7ë~¨ƒ{/’Ë'nÚÇîç9A«²‡ž=ž:¯Aá^ßQ#í†ÿ¾cÈo¯âÄÇ/¶e-"mûN¹ã©ƒÙQ“îILöAÖ€!…íÙsz¡ó%~Ï{ûþ›0¢þöDúÛéÿ²'’{2¨Gnàoá:æÍƹSÜ'—û´¥0È(–Ôà¡pž_óÊåþm&ÌRÏX1à pŽŸš±¦JzC*˜onIfªà®`¾n&ŒK¸©‚’î7Šàƒ¤GÉ™®þ<”’>H‚nI=c¡D±BT0–_ó;‰aÌT‡Éx(JÆC)FÉ™?¸)c¡2^ª†ùBF”` Ä0OpΙ*é )cþqœ5õ_ð’ãøq_ðÿŒ/¤îŒc,jî+'Üž~÷Ò¿{©Êèÿ~/5f¯SgD=.C~óÛÕ3O'Î{Q`«Y"2>ªà+§f¾ã!`-p¶”à)WÒóÉ”ùÉ2ïqÍ œ)Íø0%ýäܘ?fIvŸàµëƼ L5“1üµþ7Ö‚ÀŸ.D)˜ofáX %ùÓr Žƒ2ÅâS1®”ÀYP3vŸãJ gÞQBa ¬…(V Œµ ª‚yíJg¡ˆ±û´ÌK.²_AÃüuÏrÝo>rræ=Ź4ê?xì–d,èSŠ{ìþg¼äôÿw9÷åT1ž”ÀCåžTÂÿ¨¿{éß½Ôè_£—š°ÿθåÖ¨ó X¨‚wy cõqNî]nÄ8Ó™Œ[£þ¿AÇø Ñ¿y–Ë_šóù ÿÀqàœ-ãK—ôÓ‹`Ì Kªˆy G0ßr9c¡ $gœ>“L9cÖ˜bQ©³Æô?à:èP2,¸è¦”Ñ÷»7§©ÀåBe2>_ct™1oNÁ³Üœ1RPæX QŒñ`Ž…ªbŒiÁ›Sƒ’0^À@Ø|Úìª`>åWº¤‡_8ó*7aléÌ?xs„–1P•Ìßø?ãçý6gÕÄ0„[ ¯ršòw/ý»—ýkôRSö:ôFÔg4ü7¿c{jQ‚…ã†I2ÆDaÜSYù¿<ÙÃÿÀ€à 0ª¥À¤×0ÞiDÕ¿¼Úµ`?pöW ãâȱ@bP¦µ)ï4ú7v¡àu¬¸…( cžêÃP`KšþÊ€¸ÒE(7æ]ZôDI®´B‚ã $X|Œÿ%ð¢™Ç»’ñ¿„‚T¢´Œy(0 ÔŒÂÆX¨nÌëØŒñŠ?G`šcñF•à>h™¿q$cé˜cAG±¢¸_Q%x:Ñð8.É~(dì/îq‚Ò¡dØ ¢Q&ØÂQz”C Ê›ƒŠñ¤ÿ=¿xîÁ¸_çT…*”RΩúï^úw/5ú×è¥öÓ¤åéDÿæÙlʼã5Œ©Ò•ðŽ7fühãéDÿq!pÅ䌥XÒ3^Á¸Ñœ£Xôöç‹é7:¥GÉV`é˜a±D¢Š™×s$óWÔ£,XÁ7^ÁxЦõÿâa(KG‚EÁX:’ÿ€¡GɱàbšR–¢ UˆRŒ ”D`¡tŒ£ɸcæX‘Ì3Þ‚132QX jÆÎ°ÀB`ìh7,X-ÊŒqtŠÛR†bJ»¿ø“ÌùÄ ¼h%*eθd)Œ¡(0£uð—æÌ Á_Ú´;ån>ÓØÔ(cl!(J† !e‚M!¥Gɱ9Äü¡È:”©”²i¹W¼ ¨÷ôß½ôï^ª2ú¿ßKÍØïY(|ž¥(Ÿ1eI«f|Zç ë}Lâ” &r8ãÓÊË3O|LjÕœM*B¹aÒk—6²ê_^ù)`op&™†‡¥AI—6æ7f£‹&¥E™16­¾·±ÈôWG$ª¥ÄŠa¼íÿˆÁÁ™Û»ÑM‚ã Ì°ø"—LàoÄ0Ÿý@Æ%3Æ‚ D¥0Ž£Ààˆf ŽpÆà0ÁBU¢RP挿!m cÒZ`ñªKp7­9rTÊ-²À‚V£Œ;Q™šyõ+çQ(ò@T&Ê‹]2Æ‚ù½QĘdÑ(lá(=JŽÍ eŠ A…*D)°1hPlŒõøïùö§ Ì±iD2™À£@ î×Và‘ µ&üz)gþ3½ò¿³OòÞÈû!ïÿQßûg{ïsBoã½ìí_¼wñžõ'>ïOÿ“}‰÷"á9#|ÀØkKS¶ÀB” 2” ã_ëçGàÀþ{ü G¦`ÜC ö•K†rcÌkÎ<,þCà”©0‘ ,&R”°W(œ¹Ô£,CóʹnŒ×cÖðßçn¢˜lš¦”]*B¹ ü ጓO/춤,W&_$ãQ+1SP挹cÄ8„×U`r·§üé@LÊÌŽ”7(ð¦Íñbñ@T&Ê¢ƒ#kY‡’aG£L°ŽÃQz”:eŠI­B¢XÇš?ð9GGƒõ+ù{ô÷<Èè_cdÁ~"áó*EÙˆ:” “6šqZåŒ$$p Jƒ2ÅDV1N«ÀªÖ $˜Ô¨"”&·e† ‰*F)K°È"QÅ(%&} ã³F±äDe¢,°Ô(c,„”%ˆ.Á Ó2vJ‹2c|VÍo\E3,šT Êœ1Z [1UŒRb1¥ Ì± ¢XQ¢4¬¸( J‚E*B¹a±i›üŰ.F)%8Ê‹/Š ¢9e0JZP–µÀ!3Á‚ Ae2£ƒ2e<ëL”)j *e«6£ŒÆÆf•añF£L°€Ã—Õ YÝòŠdXÐÑ(“N”?*fÌ"ãhŒE‚Ò¡dXìÑ(,øp”%ÃÂflVAƒ2Å& B¢Ø 4( 6„TÊ “J‹2cÌëb”›D ÊEk¨L”6(Ƹ¬‘(á[L—UàXyFõÒ…>Ê{hÉþù{ßü½gÊŒþÜ/ÿ;{¥Ð'•Fÿooüïì‹ÿ{¢Ðï…Â{¯A™b’Å Œ1ÑBP™( ö¾2”U-0#Qz”\`¢¡L§Zà1*0!5( &eªå†É©E™•ह1£&k¤ÀJC)Ÿšs…ä De¢,0‰Õ(cLäÆMx²æ˜Ð¨¢Z”½h‰­fÉ…*Î%0ɵ(7Æ]4ÃdD£”˜ô)(sLüH”&¿ U(ô;, J‚…*B¹aAh›RÞb$ª¥ÄâHA™a¨P…Í)ƒZàÌFµ¤,5½p/ &%â‰b…ˆÊDY`©QÆŒ©¨CÉw͈q¦SP¦ØËBP:”Y&ã) œi=Êû˜eŒ…‚Ò¡dX€j”±0÷øk( ö±p”^`+b‹A™bªP…(ª%Áb@¡Ü°hµà) ŬEbAkQfØ¿"þžþ=4ú¿Ñûþ4”³8ÅÂßR’cÒÆ°ÄU Ô(cLà”%ÁDŽ(Gù´n˜ÐZ”&u$ª¥ÄäNA™c‚G±$D¥ Ì1Ù£X¢2—V2ÆäAéP2,‚h” B8J’cAÄ L°(ÂQ)(,%*EXÃ2.­ö7ž¤9M8*eÁØ´EŒ+Å )•)0·± Ô(c,ª”–ñ·ÝPZ”Y$ª¥ÄbKiòƒ[(º@T&Ê‹O2ÆtkNù“f-(“[‹2Å‚ ÖÁŒAˆÒ $­)Ÿ[‡’`¡† t(l´eT†3&­‹7eЬbÆ.¥Ä¤KA™câE±ä De¢,„3LLÂ"¡·`"jQf˜Œ‘¨b”“2eމÅ’3•)ÜÁ$-Î,1IÕÂý9ᾇp6ÉÖ!(J† 2Á¤ GéQrLÞh”1ãV‡cë…sHÆ©–a/ˆF™`/GéQrLðh” &y8J…} Pè Ø4( ö€TÊ ‹@‹2ÃBˆD£”ØRPæØ¢Ø„!•‰²ÀBQ£Œ…y*Eöó¿çQÏ£Œþ5æQnl!Q¨(T¡ðwLZ ÊX¸ë†ŠF™`‡£RPf˜È‘¨b”:eŽIÅ;•‰²ÀW£Œ1ÉCP™( Lv5Ê>¥CÉ0ñ£Q&˜üá(=JŽEƒ2ÅBP¡ Q , ÊTØÿGe¢L±8Q™( ,*eŠÅ¢B¢,°hT(J†Å*FɰˆÔ(c,¤”NàtcAE£L„óT cv+Q)(s,²(Vh¨Ì&q»%8J‡’añE£L°•¨”y ÊñNAI° U(=Ê 3¥E™µ¦Lo=Ê 5¥Gɱ`cP¦X´*T!JÅ«AI°€#Pz” 9†³¥Î°¨CP”1w *eŠE®B¢Xì” >UˆR`ákPÆXün(-Ê ›@$ª¥Äf‚2džÅšB *eÁ˜àÆØ BP:” E4Ê›E8J’cÓˆF wÝPj”16”h]±ý—™GýoÍ¡JöºÿꙣÊèÿÎÜIx=)Âg†=H…Òaÿ‘aß)B¹ahQf˜‘¨b”ûN Ê{N{°¢2Q˜,Q,aQ™( ì9j”1&PJ‡’a"E£L0™ÂQz”“*eЉ¥BéQraO‹%˜,Føî,&Xˆ0wÂ$ËDY`¢©QƘl!(J&ì]aÒ ½/eŽÉÅ0•‰²ÀDT£Œ1CP:áž&e±°g…‰)ÇÄŒîx w„½*LNLÎp”%Ç$A™b¢ªP…(&¬‰0g¾÷ŠI[(|? k>eŠ5¯B¢˜È1(Sa®$|¯ë½H¨}¬u-Ê k=UŒRb²§ Ì1á£Ød •‰²ÀZW£Œ±ÖCP:” ‹"e‚…‚Ê„µ.8)é€Ö·ûìùŸ­Àþ’R [¿m¶¢^Û¤Åö£ma,øD_¨tf¦Žñ{}à´qî<³{EŸ‚õgÛ²în[Þm;Ðìì 鞀£"ÜsÙKú$H0å ¨HýRuVJÖë`sFã]SüöB‹†?–¨>÷aÿn.¡\g3èø2gD°ùdH_Ôc½O€è#Á9ô'õ¥’áø·nuL-uv:1à ¶ë U^™­ë,ö eµŒ†»Â*§É-7þTê'ÒšP_t;àÜn>>ó aüUê×¢ÄñSû.îêœ<™lð.?ÊzþþOËÏ«½Ôí={ÔßÑ ¨Oëlæ+Ú‡PŸ[Æ9 ¾Ó*‡rCÈÍq·•Ñ:(èR;qà£=PØhcF•)Ræ{3PkÂ9YÚædvDU[‘k"Œ§ÆñÆÿÓð8äÛ£V­Ù 㛾ˆ{ã¾»ß2d*÷ ä>˜ð¾nõ×½cF0[#–¯U¡a®sÝ…^ÎÌ…~F©2…éº1Ýã¡y·zš~¹:ˆ +›>þù.Ø7òa+çá°ÑÅêA“ÁsçƒRß4PÿF{‘ÇÍ9Ìí’W^Ú9ÛNô³1ä=Æ¡œˆ)0èÄ´FåßéàA¨ÏÀ‘–»˜Ïœ3ô~scåƒF I»ýý[=¹hþ‚MvgÐåÛÀý¤’.NÿQ½r‘40Í¢—Y‹œ3CþcœN“â;´î9vÀö:ðWÔ8Ìÿ%|:4>ß>C ˜C™‰O­Àkì‚ÙûF.&”CÛŽPßc°,T¶xï{ÛL™½¥úgiAÙ Ðâ=N¿¼5Ž_£÷¬þÞ'–î¶}¿«;僇©®þú¥jÆ×î ª_wzd2—PÓät‚<0=Ιù<;B™‡F ¶ù,M^_­KÜáïRîc_’/¡Å8¥nºœ¸Z¼ÞìVkûæƒîÖ+ïG¨Á§ëpóvºAÌ™WïÊü<ûÎ×å¾¾»¤Á~ WμßOR#ö,·b¾ts õ¯kœ3•¼ªËÔŽÍ@Œc¨o\5“Œ¬‡áFì §›FæÃûÀaÁ*év¸¾~TÕÐÐA0N“ÒßëÎ\Â8Ì÷pãu¸ÿÂYUãxÂÓÍùË&(+ˆqùÐvRTNØÐmp8:±{•¦ð΢JÁ1á>qK'Ã~ Ì€ •ÛŒù5Eìôs!å¿·à¾Ú¤˜ú:j1ήMmòj§l…m; i>¼š²©SÓ­°ÝY}6ÕFiú=8:›Pާ P¾ L9jcôÝA"7•>Ç^I ¶±µ¥¢ßª¡0Nå]V_¿^Úú|8y³ê Ëó›AÿuJPZˆ+”mæ®-<‹ùÜ`>¤ƒE.=cÈût|þ—nò•JW7p–ûÍ;A˜-Eη´Í»‚lë­‡¬¸Õš3°óÖà»Ã棜“Kç'.ÌÓšñqè¸*×÷ãñ—'{ìbœô|~ÂtQ~uøvþ!@yƒ å^ô†ÃGtÞS†Ù³~ÊüTqœF öþ¹ ,[è4?¼ò¡ÙÓìv_¬ÖBÜìÊßÞÖ’åÉÎ'œWÏ}Ȩ0õÕâ8ïú<ˆ¬ºg7<‰Þýp,æ{@;k`ì|M½þ#ìà§¥ªÂÏÁÀåq…œøªcÉ©Q0¼ö±ã§ÛŽçMO.œ([¿ãŠà¸y݇Ý0íø–úßóáœÇ«Œ²VË~úíC‡¹ët§ÎÏ"Óž¶îb|xÿ߸EîlÞ0œùi¿9Û-  öÜ¡Ï/£ŒÙŠÓí–¼˜¿ºö>ÕmHP>ÈZM¹ ]M>zeœ˜ª—5eÈ,"¹>Á»ÂUw‘»Nç12Ö¯ßHùOê#¬øÅgY‚q4a½†…¥ì… 1=/Ù‡çCQRîøÇ­VÏB£…M¸€óý[.JT„û@s_Äy}ûûÅy4ܯðø¨&ÇžÛýêŠq´å­#Úìë;$§°¾öF6HÍ©µ–E¥œÍ¿áS?®¯ð´í:uìvº™ ¢G͘e;ããŠoÍ&Ü/˜óv¸•ïÊ|µ¯p.ÍaØ]^xRæ³:“’,ÜÜÚJÁ8V³÷ÉÄâ2õº;„ù Ó÷Wã ì¸nˆýËÃPýító¶Ò|˜2ôeì¦B7ò²UWÓ7G¨¦¾÷iÂÈ™$¹ÉÍË•fù²n‹zÃw»–Y·h¬È™£yL_§ÇmAÓÐzܨ=B˜Ùäí—?îñM*MH>ÙÊg8ógœIèû7†Dë‹æEL†»Î‚³eðùÍ~³·svU}.å|)Ê!¡ýLq–ïª -ßþ(+¾Ùf–uv­õ=„\«ñ¹ÓÞNîðqæ•Ðü™„®3‡ç°p'Ï þ“ûAs®€!¿¯È\üïÕIüyæÁ‘–æ•·¸:•4rJ|ÛeŒ8ß¿V4ºêlÂy¯§~–Ê=èe%rE ùŒãX–ûâí‘cPl‘½à%Îëh?„>=0‡Ðçj4 j[wÁÌK]U<ø:”ó ùŠãìÂëÇÀµÙùú=òaÓàr!Õº. Á›#R¾Ôó…¬+?dqAsÈÜ‘šI>{\ae¹…6ÙŽ‚m?Žœ™5òÞ ÇëwgÚƒÞž10­LÆÌ [vºbœAÄøéBÃ}¡å–C½›Ï!a›5”»ÂÑ¢NõÞl ‡‡ RÙóD˜èÏÍû—ÙcÏM^û+_Y…q¦tÛÝ´êþ0¬ZdùЧӊ2Î+HjþBó”ý>;Ix‚Í×g”S¦9Áœ£DyGo¤œãLyŒ…q†<¬7ràÛèsM·q|¿|(÷ÔÔÖüb$Y²mAǬ“ÞpöLs\AÏ!YýT6³¿+€çwÿê¯®Þ ²ãòǽhÅñÙÝb!ù‰}ÌÏ>ù`sviî–5äsVcÛ{‚½`;pá>´Så]d»*:÷çó CÞâx{_¯o:-:Ú3üý‚=îMqí°ŽÐç¬ Ðå£JœŸ¯ûXÞ;jˆ?Ontð•8žjñ÷Ï5¼ÒýÆ£¼†ù1zaó;×baíÅ5¡ŸzçC©¹“žßœ¿žœÑ´Ë6ÎŒ38‡¼¸ÓôõðöV ÐjUÝW&×zåUnÌ·HÞôXÊŸûôuµ£yŒã¿’^¶[Öí8ô2l8äÃØåN8ß@Št±’Z9A;ÃBb6áórê§ëÌúò‘ËrXãc^7CÜhÙ5Îg<YøéÖ­]çÖx¦û´‘ ¿çüsÆ`gÈ]ù QØä™ä”£ñ£švRqžRr¢Äq^¶íÝæcæq˜º­êç€òù ¶¼æ°M¤H:+>çGêêF5ß;ðuŽ~k½çí °n|͹ÏÇøïÇaYðù}ÊåÉÉ›ÕÍÝLjÖ=×Ë>¶^ôÈïÁÂ¹Š 5eÂÒvƒ^ã=×åKF‰¾â†<Åñ¬¿¼|®í}ÒwÅ,ÛU!ò=ÊŽ°n+yêwàÃìÝ^ÌÏ8Œðù¹S£›×v?ø¹2:„ùºH->Ì}´Íj óc§û;Z¿´x\Ÿ_|Ž¿ï”ƒ3¯Él'™ËW)ß·V2ÞÉ"—s ½ö¯Þvk[WRž¸Ç«'¼MOB›]Þžð]#^«²xš¸mº×toŠª ŠóÕu ÑÙd•i:¸ÈY ë„þb^M™_õý‡f¤ôõÙ÷‡6äoflš€‹Ôœs»U÷n½×ÁÚÀZ¸ì$·Êõ¾›½L Ë âp’ja{Ü.Ú¸?;Ý÷ì ²—õ8³í¾”û]Sßm àCc«°IoÎô?ŸÜ¾d–û ƒ¶]iá¶‹|Ò´2ëþs$`ÒH]Šf±_^¨ž/uyklÿ™õÑ{bóý&ÆÑ¢yŒqˆ|§`$fwƒb¬h'< w“Ÿe¶ÊoxÂ,Ãz6éöyô–†ÙNpìÔ§íº+€Îk°ÿyÙº´P>÷Aù¼»¤¹ã<° NƒbÒÛæµŠt°CXî×ÞCŽ, Yëàï­K›ï1‡8Œž?¤k¨-ì¹ü:èË oH|ônéþ‘¢¯:}ÿÞHù>ö ruè–‰Ô¯Z…qê\”å%¨Nƒq“{w{ê`òˆ/¥ï!îfÛ~˜2N6mÛüB»ÙdXá‡6oâº1?|G Ünã)|”r® åú82n;}Ω1Nݯé¯R/œ†  gZVx¡ƒ§;þð ÞKâ‡_ ³ÜîE íêµû9Ðý¯®ðqÀ†rµæ8ÃΞ}j:çñ~ÎçCÃÒö,êiDÏ´'»ÎøÛ_œ«5îç/¼ÔÁ½×>2™ñ>â§ou§ou'8w,ma™Bötñ°þ×úŽr(ú3.L¡”ïÏð>’‘Ó2ܪåâé1N¥¼Ý!ý+wS «ƒr³ºÍx¶k¿5ÚhR„×Ûü¼ûõPBùˆöâó“ç•w}»÷3Ÿ0nS¡”sW¯ŒÖOVŒòQ΀|ŸÕþ'wtPËyûRëý¤ý†ñ‹üg9ÃÜc…¹ã¦‡ö°`ŽƒÈáœEžoÜ—Û­ÑÕoc«ÉD³¡~0ξ·SdÏ;Ù¡]Êçêàá©R+ß½ÜO×qßíÃÁbñ¸S‹Ï‡¾ÞáÏ×>¥5îçÜä"Šóå(§JñËþ¾ ãœ_µ»÷ñ·g ò³Jp]ÖO×f¨£‰zëñG}-]A“>ûÎÓ«“ýýmîŸ;å g|í¯RùeóyÍÊ—kSa'Ø“qA)C‰qÖ}ز䩥´f|x“‰ŸB ü ‡È„õߺÃҙf^_¦Î |v.Ϋ3ÔÆÁIZÔ_ ¦[D¥k¡z…Šñpµ¾Ã͆3ñ}{Ó)ô I ©:ók[ë)ΰqÕž=‹w†ºï.rG)Á‘͇KAׯ›—¾Xx¾p¡~0Ž½ÆµàH|<¨f LVgO»ÇIƒ6×ÊT0žÖdB¹0îÀ¹Ô¼óùQ¥œ ½¶¼“rÎ}žÒù ãô¶kÜØµÅY¨ñÃ&3j š¿«y¤õ¦²I¿túÔÃà­eŸ~ ÞL"yŸîKš¥¹;¿9¦tý%¥ç—w¤üÜŠsZ õƒq}LoÓ~ÑYH<¬Øóµ·¾½žzwf~ 9º:xýÄm èq¼}Ç‚Ù>‰3Ë×6Œ¿Û’í×_eç7¥|†ò¹)K‹qº}¯ù&÷óYØÿ\¡ëÀýõÜ€å’X2 GíŽ9BTñîènŸ”„ò{ó›› ˜â‰â|‡r@HÕõ/V˜ò­Û'nIëã,jXôÙgÁ9Øía]{m/”’ÜIø>!–Ú)}Ø ‡&ɽŸjY|ºß<Øòxu¼Ù^&½F×ã¤KG›¡ÃNxÂi•f›HªÖ‹Û¨ïhà,Æ㬠ŸÖÇu¥x¯ÂP78þ²z•­½?ªÌîQAq7;[¿Û{œÔ.ºtgF­‘°öB›mró0RËçBv{ÿ1¬^ýr~½€ÿþ”»Èž38î¼#5qÈØãœTkú÷<1þç„Õ’¤LáVë¶—FÂÍC«'œ #¾gg¨^¥ý¥_Mûqr"dÌØ:&éã_|ʵ磆úÀñ“>®í"ÍH€ô*ox–µ:AºýæÄßõÔÏ«¼ÀþTÝÑ;·M"ô9Û¶ô|ØÝú/î îó8œgTò>ˆã<•wúÙì{,¯åýÒìjDvÞ9ïBå“äc¹MÛÇ{Ý×!M¾ºw°yë/ž‡p/c¨o¼áœs½[ 49š+>fß\5á$Y˜ux|Ó·.l¾DžMõwú­/ä-Z:ñGkë_îQèqœ²w½§žMÊ{̃¾_'¤Í½p’\¸~W_çs«§=ª^>˜˜m®“rÌöºÚíc+O˜™ý>¿T;‡Í)évZr"öO}3ûöì<ìëÞ;“O‘ê=´gG ƒ þK®v]B‚Æ4ìæ 7FZßê6’¾ÕéêZ>L\GqN•!oqÜÖ öèÛ>J€°pã‡û'æÚ£[a«Sää¦í•gOÌò3”ÝòŸ+t¦Îm)Éù–á¸æƒ„•4öICm×ûäÁÚnq熟"EÕútU› Ê %¶­ÂJo6öÏ8ÊŸ8ŽôXø³Õå ŽŸåAŸ­#¶9E*uKÑôß:æ×ÙïQùD©-©„¿Š›Ÿ …5ésäd=_Vá8´^ ¹áà'’5Y§…§Hÿ࣠àa0ô|p Óçãåâ¸ÁÅÌgª${A¼öqìh?%„έóæI«"º^Æñš¶/Šx“Ú–ëÛHðõÅYå¬ìpš¸wß¶­ÊJG¶ßîOʆ.iQÝÚÎÍw`”?<9ôhÔÔ¹¾â¾ÝO+’òóìËñšå‹èþ‰ã´:×4#ÆÍ—uS- ͽGªf&A'§î¬mìÍßçË æÛXý,ë 3>ŽØ×:Ñ*oj?ì|'qýÄÏ}¬qî¦CämᢛÏ`ïÓÛNÛ–%PW¥5ŠsaDî‰ÜòwN“‘éa~/¸Ã£O=¿ç¯r&týå|}È9<Ž!_oÈVL°jB »†öʃ\°ýQ­Ÿ!–‡wStö‚£ÂqN2QØþ6 —¨:$ØÆþ¾íeÖ`šŸ8Žýô{Å7»صyÍá Y¹pÕnÔ†ÉgHû–ÃäÍæx½ãM¶g¹´Z=K õ˺Tëx<ìJWíÖÄ_쫆¼Äñ&Ô”/ñÂñ¢Î• Ó—Ä!ÖRá£ýYû|o°0»gè?Â?§ŠÂ6F@xðÌŸüþ"‹ï sÎoɾ§Ä8k¿ExÇ."ðjú˜¥Fûs!˳߹'Z ™fx£¼ ~Ÿ)Š%ωIË×Òþlž Àî‰ûR|Ÿ‚rÂ2믧½bY"¶˜iÛ¼û¼¹°äå«RñãÈÍFOzÞéNš­ ›8ŽÌì0«pÞ—Ñâ~TÆéìÜe?1Ž!¯q¼ÇU’OxKÀp<;9šökyiæÒ8º¶‚î{}g˜ßã꾪ql]åÏøÞð /P‹ã¥Õ¹û¸¹Ö K:;"öj†u˜GÊGFö:¦?ÞŒ<Õõ~)½eRÑ@_Îg×Ëü}6ä'ŽW>g‹Ã»áZ8sÜ:`“M.ÜØ“q®r<ñ|'}º£ïPNª¶½âP_B×?J»g/®'AŠçÑÙã×L÷§ø¼Žß¯K>2Ï´ð&­£ÜÙᕞVY®…ÄC¯1ÏK÷m mâIø–^ö•ÞÙóó`Ò¹NÃw}(áY‹¢Å;~ÚÃ`¸ÿ"·ë@àœõRêξÖvOD:§Ð8ŒcZ.ñá­³Z踡ùçA’\hÖΪ2YO6Vl°Òò%Λ˜°å€’Ð{a>Ày`tª?ãÙJ—sÈ\3þ™tÞáý.Þ^6¿öaŒsÜW¸`¡õ¶:¹àâŸñån<Ñmw¯©Þç'bk¾;¼ÇŸŸ³½|_ö~ØÁÙÖ{z”ß&ÎÓä÷#è|Üúΰãt®“TL„ºø´¹Q>ì_ÖH†³äËÏš.¸^ÔF7gÛ8r[-|À¾PW½•vœ©b185Ô"_uÙõãâ[¶üMú­ÁÀy!å¬áÇ«„×#³[$Â"—6f¯n@4¾ê:ÑgIë½6ÍóS<`íÒK7ï哘åUHîýººàTÁ‹ƒ8Á²œä3‰›ËÂퟧïÄ;|‘RÎç@Ówß«´NôÞŠãÎ÷_8:îö—}zœqvÕÚ¦½VëÉ-=cÈžîž Ð{«’qäÚç¶+Ë ¦3:'®reçÃŽâ½DÏ' Â"Zþ”ÒsÿÀïçêãTIë¾yÄòDØsiAÔàƒ7àEÛ»Aóçœ#ŸÒÎ ÏÛé+l…' {.Šü——›#så"¿~^Ä´¯¦¦?¤ß–©»¬¶ƒQ¹ûbÏÓçˆã<ƒq³¿íM„õ×LٽꄞèÔ²î|=Ël~(çÎøÙÃÈd[a‡?ê§6Ù9Z>œÝgQˆïWÏÈë5üËÂTã`ùOçÁFy²#I£U•'BKÃE“0k]z*Ûóä®éþKE³\ؾzâ ׬{ÃAóh˜jî»Ãh©È ¥çþFÀçW×:  zÞ.Á8tþ•‰ËÛº=æ½8ä¶ù<©©Û:?ÀÒ Ï–|r÷ßwòÇh¶õZÇ#A{hi›wËÀŒmÓîvRz¼º2!*PÉÿ­ŒcÀ%æ&¿ƒJgÇ0xB˰ýúó„®«‡»oAFÉ)kzÎõ¥œ¶~lø©tÖ¢¹Å•}‘Ž>oš¶dŠ ãùÒu¤ãLØ1|ô ç‰pjoµ·ÓÍoÀÌ—Òñ+MHî±kŸŽn•³ç£¡õíå†5s}æ.ÎKçq&øÊí[³ûÇ8î³æ }H„u…™Ñ 0O/\­°N ôÞ§ÛW$ë­…ËQ@ùÇ2ÆE§ý\ã˜oË R|M„š†Œhæ½-ás`1s:['üÎÐØtBú8ñ¥}a`u«Ck»9‹<Ì’÷Ù´8ÞëGÙ£<Ë'Áy áBlì80dêŠBy»V`™™upÃd?Bç1£ 9Ù!n‰ÇXpnÐåĵQ“÷=~>8Æbëçh¾ãø†ePý$V[uoå@½tiþ]Myú¹“Qá29|6xd¿lt@V_Ж ¾sÅi¨xoèYŧ,w=“òŸÞ¦ŒíÔ‹ž?é dÚé·Ïx´I‚š˜ÝO¯ç@ÙGƒ£õE įâ¥FÞ¹Ãøó€œ¨´¥T§”Ñl¾d† 7”èüV‚ã¼*œW·#¾=b†ÏKÎÓS]¿´ dÅùPÓ6ø¼¥ëžÆ„®¯sàùùß5ä+Ž·ªG‹ù·m’`ÝmŸZ›âr võœÄÔÕ„ô_þóMm37Ƶî },þâ¥ËöMŠ÷í轈Bq_÷Ì«‚Õâ~½G©Ä8ÕÛ‘7»q4&=Þeræ3!t?Á“­Ìá®ûÙ—–“‚ؾI ÷Àøó1aÍæ{Ž?‘òûÕsöÎØHÏTG f>\’t¾‘UŠÖºOê©%Yþ’…;/zñý ²¦¥±«lg0P>d?q>ÅãòÇù¾ÎžúË’ ‰á‚fÄ}q‰¬¯ÏÞš•,î\+‡=‰_ŠK¿Í«ÀæÉd¶¾Ý0É ?ñ~±!ñߘ8Ñ9xWÔÀn[ß(ò:î?Péx"r¡¨F—VŽü|•ʽÖhà÷Jèú`xO%|ƒ¤…:‰¾_j—>ÿ“à~Ö¥ãÞdÃñÈ {Wä'’·M‹·lö™­ñ%ú#7áÄ>ž­™¸ÜoU¸6f0A⼓÷«_y»ôÜG‹q~}à¸þtœ–ß\!-ȆswnH3N"·ÎÏ Î9†ë¼E÷†žy¡$ü^ý^è=¯aâ<šÇãç¦tþÉΗ„ÏšHI0Ù«8ñsN6(ÉO5$‘^;OÙÕþ9Î/÷Û{CIR¦ýlÚfH ´{x/`„…LÌÇ·7 demèÛ-1 Vµ·&§³a•÷·Ù3’HÍ fÕ]?„YUýå>¤¡º{ŽYË1â÷pøýTC^â8'T·”%$Á–Tl› ‘;GUX¯I"ÏÇ•Ž­Ñq$»§8Š5}»áPÓqì¾Ò }Ðø~£!/q¼OÍo®¯ìܤHÊ÷™†õýÖÃ}·=ðï=òã =5:>/ˆõ¹°/¯ÃØè}îÆ’韜Þi’ ë{ÃH· ¶îdS ·Àëv¶e߇±÷±*íiгÝ×RÚçoòãÌ ,HíQ. >wìã>öüupIJ}ñàÅRuåK±rhÝhú²ÅŸFN«+_Õ¿€T—N¥}-ÅzãÏ#þœhÔÒú󼺖¿ÌÓ”§¥°|h‚õhàÊ_‡{]î÷Pá"élõâi÷!`û¬ÿúìí¾¤ÜR·‹1Åç0ç#óÏ›ßÛüu?‚ÆQazo' Š¿&O=xf-~q%®ÕEòmÐòJN(àçÕ…U?ù§ïlGe÷G°ý-Oñû<øs¡äëQcœ8£îƒ6þH„¼ á"àu¸¨R™´t‘Üš¹â#>÷VWé}z{³lm,”ÚW¥Qæõáìû‡ƒÅïñ÷Ïß°‘¦€*OŒìÇîÓ`zÿ*š& ©Ý,ì:´5^$åªÚíœRÑ è}*'bºöÇÈJÕÇ‚j®D¶håPqŸšß»{°øè—·Rze »Áîçbœ51ÚÔ¸ŸŠRÂãux’‘%=p‘œÛÖÍ­McOvŽeG\cV„ß2ø½E¾@ï |’ÒyÝ')_ÆÎO:Ñú¹] ¾­ä”’> ­Šh{ί+Ÿrÿ")Z¼ÅÞu»ç)¥çsã€ß’óëù}gþ}C¾¯Oç1ôH9ï»äù£ã,n°°å‰é‰ð¨kðXõç,8PÿÈQ3“‰°*!-‡»ÿHؾ8?¦û¶b?¸kÞðhÓÃEÒΉê v¾ÁÎ91N…ãÛ=f…%BìaC$ º¿»+™L¼¹ígà@ ÷»Gà»m3fŒ‡þÂuF“¾âyÚ/ûç8^Móøs½G$}®gAOŸØ~?“ɘ¶ÕÛ,ÂÖI~Ä[ébvýûx˜×øg¹yFî&àé¥> ~_¢Ä}--Žmúd‚e",-ëtåðÞ,=´CQ;É䃟ðEËAà;¨eVºÙhÒ©ô¨YeÖŽƒ9 |´k…ƒ…}¥wMƲ<+’z-‘}¨Þ| 4¨Wm_)vÏÇŸ7leiëD¸éjÿbgô·£8™´-ü¸æRàP0L,G‘€1p{ÞÞxÕÆìÞ¤ Û*b÷Àä@ïñ°óþ;8|t£wy¿D°1yr¿ì¾,0\㮉Ì6ld:²Ïû®Å|â1Zœ7Ñï­ôïcò{þÞ­šž­¼ŽÍp|˜hô=aN"¤ ÓŸíY0dÁñõý¬.‘ÜS‡cû˜¹ò{$ï^MÕœAþÀÏoy>PÎý;±oðýê’ßÿ‘aœµÎ ÷JáKTÏ"ñsé&¯4å¹'·1î>Ë Ûà N„î‡ø³ï ö…ãs'ÇŽÙkk^Ù]îÂ{ñ{£ Ùù>Žo¸VS;F\y°uDt,·ç{õ˜KdqýÃÊNÉλHÐÑÍO"f°ÏÑ \ÞØ¬|î–o¸sç=»ÿ÷^JÏåûüò½Æqë÷A>ÿ–, t³ æò»eû¼ºDêÕÉx×7Úf5{tî窿úìùÇÏ.·þì Þ—÷¬d] z'¾Ž¬#yïÕjg±ŽÑ?{ýíqôÙãHðâ:ˆ:„:lDÿï‹à/òo1¨/qe Ý(T1J‰‰‚2ÇäŽb È|tKrS/ñÆ<0aþ‘1Ì»íaèóW0þAI†ªà+®@©™¯xó7a¾â™ÌcWU‚ûþ›¯¸P\ò+‰fÞGÿ 7Aü8§Êì7Oq­ƒ*nIYÔÜS\`QǰæžâæÁû'†*g-¤”`þ1ވ̿HæÃË=P„& ÿû§aMAÆØFŒW¥aMBÁüx.ƒãRÿ³>rÌ ©¤§¤àÍËýäþîµ÷Z•Ñ¿^¯5f¯;Óèßæª1 ÷ÆT³¤De¢,0¹Õ(cLðæY’(øô†3ßrSæ1§aðx—þæ×ëÆüËKrÏ^7T4óì gž½¦Ì³Wàʯš³¬T¿yö \À”%Ã"‹F™`¡…£ô(9ãX™þ“¾æ£F`UsNùo~½¿Aà VQ¯Wà«j_•ûõš1Î?q¹ßyf ŽU1J†Y‚c…*D)°h˜ç¹â7–•–ùžËÏJð› a~sgÕùu ÞçJÆZýg=¨"»º¤ï\ˆô//ª¿{íß½Veô¯×kMØ¿÷ß⮚ÿÆÇÉD™cRG±ÄDe¢,0ÁÕŒ#ÃDF™T¤^ÄÑŒ(ÇÄW3ž—ù?È 4ýÍ“8‚1ͰP"ËZð%Ž@éP2,œhæQ,g,kÎÊѰbR bJøg2oô?yzêÿ ¼À"Æ üc]ļù ¾Æ(óV”¿*z£ •û»1VN‘ðw,h-Ê ‹:UŒRþÆ ŠœsÄ̘§§¶Ó_L±ß==-˜·g‹ |‰"Æ gŒ γ.bÖÆÌ‘3¯?ÝÁ']àZ›bc Gé?P]Â'ýï^ûw¯UýëõZSöºtFÿ6—Õ˜±x¸oi4Ê“:¥CÉ0¹£Q&˜àáÌ»´$3QðMV1ïw›ΘdÆ•ÿ1ÿ÷¢ßü“•̾$CQðPV¢b˜‡²Šy(K˜‡²žù›F–`“Eüæ¡l‚oD8J’c‘Å L±ÐT¨B”‚qÉÖõ?ã/ðz$%x=¿ù'  -ʸå³rÿdϪe|VîŸlÎe‚ÅŽÒ£äXd1(S,4ª°á?Ï~Øûñwv1JÁý¦Q­('MôžF1V÷¡V2ÞO±ðw,æ”9t+êÀßøÆ%XiæXèᨔ¼´L” ?UÄø%˜iߣ¸+õ«Ö•à`3vn$cþ È(ÆÏýgýê¶Š ¥gÈè~õÏgÿî±*£½ù¬û½õFÿ6_×9¥GÉ1¡cP&˜Ôá(=JŽÉƒ2ÅW¡2cKÊ0á#˜¿À—T1¦šÉ?èÉ/ðÀX” $ùòsΤÀ4’`Á¢4( ãMêk7U(ô\Æ çlµH”%ÇŠA™bq©P…(™%ÁB‹@¡ÜWMàOþ3^þ™Œ?)r°@#P™(3ÆIA™´¢œÝ”9ãì¦0ή UÈ8»ª?p)£KøþëKpÕŒ…½Y”ºW-U,ü@ ã(c«e26€ã«™v§L¥LÆÛ D¥0N@cîcÓAéP2lÑ(l á(=JŽ$eŠÍD…*D)[Ià•» ¼r”)6J/ðÖ€Ö­ð§$äßê¯ÿ^oå=õO=“÷JÞyO,Ù …þ÷¿ÕûxÏã=÷±ÿlïâýŠ÷)Þ›x/âýçŸé9ûþ>#<[bŒþâÖêP²ßøB2ü0£Q&ø†£ô(9~° ü`5Æf0ªðƒ.X´øA«Q…ÂY¹ÀÞf¼m ¤ÄÞë=¥GɱÞcP¦Xï*T!Jõ®AIÞª¨á?ÇRŠS;ª%ÇÚiC+•‰²ÀS£Œ±^C~ã!št¤¬3!+°ÎÌðMŒ4ÿ‹o¦gÌë(Æä±ÀÄT£Œ19CP:” “4e‚uŽÒ£äXg1(S¬13LàÂþ”cƒ2Ádÿ{>ó÷|Æè_s>cÁ~¯B£›kЉ¬B œXLh Ê“Z…*D)0¹5( &xJ÷—QŽ ‰Ò16cc›™b„ t(B4Ê‹!¥Gɱ(bXa¸¡´(3,”¦£Qà ™aÁ„ ´(³Z”רg¬ØHTÊq´9ãL`i ç"XX”‹+U„rÃ"Ӣ̰Ð"QÅ(%ã›™cÑE±Â De¢,°Õ(c,”%“àkB™`A†36‘yó¿øDO;¥C™c¡F 2Q¦­('6eÁ8±™Œ*bœØˆ?ðcP¦ÃUØá/¾™‰°'‡Š.Á7‹aňÊDI° þÆ8Ó¡$Ø”Œs&ð¶摎ñbCP™(sláŒk‚M#¥GÉ1ÑbP¦Ø@T¨B”‰%Áf*B¹1ö‘ÐX”Œ½-Á*¸g@óUø#ôÙÿîûÏôÒ’=ôßëBß^ÖÿvŸä=ò÷^ø{üg{ Ðÿ„Þ÷{ßû½çýÞë„>'ô¸ÿjoû÷úšð›`Ó”ú‹+«GɱŠL1qT(=JŽ ƒ2Å$R¡ Q L¦–Pn(-J‚‰%û˜ŒÛÚ “-UTr­e˜tÑÂ^&^‘pO“Oƒ2ÆT¢$˜„&˜„¦˜„*T!J½Hƒ’`/Š@¡Ü°iQf# UÜðŸã-ªYr Ìj &¸ “;Š%¸UŒR`¢kP¦Œ§hŒ ‚Ò¡d˜øÑ(ì3á¿ñM;RvšÀœUaaX`aèPæØ[¢Pżì7ŽZMa7Ê ŽAÉYñ¢dX@Ñ(ìá(=JŽƒ2Åþ¡B¢Ø?4( š •)ÜÄþQÔŸò5(S,@Õßóµ¿çkFÿšó59‹[dôosh%˜È¨"G‹ ­EI0©#PE(7Ln-Ê <¥ÿ©@E¡ôŒ)°´u( @8J’c!Ä L±T¨B”‹BÃxÚJT Ê $¥-Á…,D™cÁ„£RPæµ(#²1i£PÅBOd¬í"”¬.en {þXXZ”W$ª¥Ä"KA™c¡E±b De¢,°èÔ(c,¼”%ÃŒF™`†£ô(¹_Ê R…Ò£,°0£Xq*PQ(=Ê 5¥CIZQ­%c¥Acñ‡ t(3l!(-Ê ›A¤°ÖE™aSDiQfØ"QzÆ¥ Gé„506 cÓšbÓP¡ Q l”Hªå†D‹2Ãf‰*F)±©ÄüÆö6ɾ æ´…?¼×þ³=–÷VÞOÿ‘>Z²‡þ¾/UrOê»'ò~¨4¢=ð¿ºG%ô·ÿé}ª¯' ¯Yƒ2-õ‡¶¥À¤AI°E  Q üð5( &@ª冉 ÄDH1¦‰à†’c2D£L0!ÂQz”#e*ì]¡Ì1A"QÅ™"&IŒp7N¸«!|Ç{ˆ&L &Œ%Áž*B¹aÏТ̰gD¢ŠQJLª”9&V{X¢2QØ3Ô(cL¸”%ÃÄ‹F™`ò…£ô(y3ʱæ k!Ý„{n˜ŒÆ˜ŒnØ ´ÂÙ&d8J’cbÆ L±þU¨B”ë_ƒ’`ýG0­“W†Ékõ®Î÷„;µn”7­ÆZ6Æ–cíÆ L1¹U¨B”“\ƒ’`íF ŠPnX»Z”Ö®9@$ª内 EI„uÖßó¤¿çIFÿšó$76n±ðwL^5ªå†I¬E™a"G¢ŠQJLè”&u$ª¥ÄäNA™c‚G¡ Q˜èj”1&»J*Ö„Œ›­G™a¨P…(‚%Ábˆ@¡Ü°(´ŒˆÊöß±@T¨” %U„²À‚Q¡2QX8jTJ†¤fEȸÚÅ(9T4ª¥ÄÂJA™cqE± De¢,°ÐÔ(c,¶”%â‹F™`á…£ô(9` Ê‹P…*D)$øšP,ÈT!J†…©Fcqº¡Ô-([†……ң̰`ÃQza_ 7 ¥G™cG±"V ¢P…Âß± 5( uªå†Å­E™aG¢ŠQJ,ô”‹=¥A™bч£´(,þpá eŽM •‚2Çf…*D™cSA¥ Ì…óT!Ê›„ ¥GɰYD 2Ql¨"”6-Ê H$ª¥ÄF’‚2ÇfÅJ JóÓÛLª¥Z—ŸÿîyÒ?znW²gþÿ}v÷{?Tý÷Ì“„žöûü¨d¿zÔ?Ó“Jö#¡ñ>$üÞZáýÇU‚jªå†}G‹2þ‰*B¹á®E™á‡‰*F)…ïÎ⇟)ôüð•Âwd1L1T¨B”Aƒ’`2Dß×b Q(|'“B#$ö@AØ7RPæÂ÷0ḬGD¢ŠQJì)(sìQìˆÊDY`"©QÆØ#BP:” {D4Ê{D8J’c²Å L1áT¨B”/eŒÉˆÊz& R¸/…Ih"ì9aHöš0U¨B”“Rƒ’`ÍG ŠPnXóZ”&k¤°Ç$Ì‹°¶£…}%á^Xÿ`ò {ÕÂý&á>&±%ÁDŽ@¡Ü0¡µ(3¬ÓHT1J‰µš‚2Ç:Õ¡,0ᣄ}h¬OÁ/èwJî[ güÂ}ªö½7vOÕŒ}'àŸýîUôo½‚çJ´?±Üâ¶Rw #õÙy籟iaó:—êΪ6G¸ødçð,X,¤WÇËdèý­ãGš¿“¾´¿îú¼©7p¿+÷â/=HŠqtHÙÎp½çkÿ cñÒbœ.#K-Ü\3¼Fp×,Hw›ÝN3æ2ù¾ñÔÈÆ5¼€úÐûçe¾oËõwÈ~¯~@ùo¥œ›ÃyP%ý>ôg…òá—©³´phª¦qƒºY¸¥å ;/“œ]›æE.v‡ã7¿‘g)>DvaKpoæsØGôUJNþø³nÅ·"û¿”ôç4º[ [pöÄ”[ µŒƒ•Gk.¾œí2 }±ª`vÿápÝènéŠK½Ið·%i‹zø‹œ<êïÔ…ù"½—¦Þó}æÔÍlŽ ìÓ·7ÌØí•Gýv$GpÁYï›íÕ¤íSŠ3¡‹_¦ŸÇÇËdö‹GI½Ê8ÂææÚ§^缉K=šù®ýå3"͹‘oöó“”òˆá}È0ŽÉQ•e|ßü¯f¢/0çÈSŽF/¿-õéÀ8B6Õ¯Á]©Ñ‰L0³Øe‹g Y8¾z—Ú£`ÀË›úʯ)ú¬SŸÓÌ÷²X:Õ©U«¨äŸRÎ)ék¨Æ8ïëVÝ[ ÷m¼ñ#Ì„IÑë‹×ÌL!¶“*vh¢€¯î;B‡¸ž…ƒ'ÌéäÁxƒ™_òàîoBýïÿÂÖb«&‰+ÖuÔB÷ñ6ÊÙ=líd³í)äK—õÍ·;0ÝA¤—鳘¤ŽÞ¢Ï åýÅ'äþÃ4Ï¿ø–é1ÎqUÅÛñ[2aÈø‡ï7_H¹L^_6îéš<„ø,»#=”á4/mE_½‡]kÅvŒ~/}W6É>v¬â¿-£{²M‚½P]-œ,s´ýèøû7U¼p-J!”×èÆøí®¢Oå@„»G:d¹AÃz4yå“”úÛ~•Îô:†#”9a¹úÕÆ/À8×TìZZ ‰B \'eBp٨­R‰«jPÑý—#`ÓÜ´âOõ]ÉŽt€5zºïn²+k¨èsK¹NF‡2ÖåÞ+ 5ö8öìpO–Ç”7§Â8Ý쬪-¯…ñÓŠ¥.™°ûнîÏRÉ×… ëÒ•ŒÞb¯ÔÍ8—„òà»3ÿ®ÒäõÕºÄþÎ8³=™ÿ£Æ8+Š>JÊ>"PR^Í·2aíפÌUíÓH¹×~žæ ¥¦^” É´«?±T ¸ý˜éãÜKô¥¼š/Ì×ô«tè-rüúžîÌ—RBë㤮\!;w”€¥ÕϲõÚg‚c^S—G§bÓfð‘Ož0¢¯CÍšc<É”¡çJgΦ>S-û“ñ>Ðû†¦š´n¿pxô§NýÆ‘ f0|l 3aóé›ML·¤‘×µë½hÒ *ø Ý1‚Tìô3mÐ(hú!¯}Í0ñy`6 eaNŸÒqwÒg…¾gþLÝÙ×”Öëràì‹ÇÜð4u3ÁÃFpäK#µ‚fÙYÍta>ŒŽÄ€¿ã˸XÎÀ}¹çq_kêïNýu%§rµÎò¡Ü&ª…‚=ÿ(?O‹q(ï9Ü¢{\ék \ kÞ(ƒ>éWÿúF?h~ooN«‡nÓðtÇ´ñJäÕuåêDî;çrþ àRÖ¨Ÿó÷dÜ'Œ³ñòæÕ^%€Á®µÆ5Vωä瑊Õr®ú³: €ð×Ñ]šøŠæâó’óãmO6všêoÇ|:™ß&ÆÉ¹~»’Oøêê²yݼ«°Ýq‚ëçcWH›G5Jñæ~פ_âç·±^Þ_0¾ŸuÉ(ßÉQŒc¨oâŠk~?ûßÇ_žìqVv÷zÿì ©¶ï\ì×§py Ï݇Cœ÷_»¦Þ9gÞ²áplã>ãé®4Ïqá)Pgé98{ò~L÷¢+P$Ø 6ºJ¨Ï° çS¯»ŽÞ°‡ 5ö8¾Á¸>"¿ÑÏ8Þy‡öCî;¥ç~xžN‘µhn{•´¾°áÂÚyNÌ[Æx½ö"ÆrêÀ­uœD_Þ3«Z›­Ò¼‘p·¬À³ò‰Ì«chÝèïóuÎ9x}`ëÇu[¯ÀŽ¥fww•Ð>åÀú§DäGSw9{.ÛŠùl’8jI¡”rJ­˜?$cô @vùíÑéÚ~ç µÔÙÍýü¯@Ę>®¯¼JVþÖÕ´…£Ø—)çJ!úôrŽ)çpõ9¶bþ~ìy€qzâoUWwNí¶°kÜø ÔzÜh‘úäUòÉ¿iƒŒ:ÎÀ}¥9?•û;NžyÒbT”•øÜáë)îküEY¿a|>åxÈ0N¯»ëºr„j¨÷"6üH{ï*‰+õE§qw…¾“ªcŠ÷e}Æ„YY-/W°o{l}¦—#{.|ýû8W¡$gE‰qú^V½øðê,ì¬}ävåŒ ¹Ú¾Öøš×ÈY]õ*3zx²y¿ã;‰¼'Ê™swc¼—žûøQJçSo¥Ü¿ŸöwÊ×PaœíÏ"·Ü/B @Ê pXý¹ç‰!×H—¤V5Ú¿)>w¿ç¯ª½Úg8ã.üÅ9å\þå¾å”óK×9jŒ3Ð\xRƒÝÐýÝ£Í"ì³âÑ==|¶É¥È¥£>ÏÎìõYÀ «KúŸ\ÕCŒCóŽ«Åq¯”~Y­-þþV®Q*b¥:¹d_#†ÇA–ãp‹Z٠ݾ;0þˆÅ/¼s=Žs*¹‡tí÷³°rUÝ€*“2 å»ù³«f’6+7¦¦ð>ÕátÊæ“Îàð¡ý¶ê×ÁúÈÅn1ß[zë÷㞟…­¬?—és›Œ½ežIŒ^\ÜÂ݇=¿‡±üûqÕñþn ã Oë‹ûv{Œ_-™ÿ4ãÇbœ5eïW2Ë9 Þ¹x-›i#÷Né˜I&ì\”Ö@)¾k<Ì6m½9–Xïô¿¶`Ý3²SÏ#Ö¢Ï6þqÞ§!ßqü‰SÆmªrù,Œˆì˜žçŸK7lt­81“4ž:¨ÂóŸ#XŸ´"ÔÿÞ^ä$øÖ.»jÂë>âsœûâSN)ã1àøY†ôüY9e?b¥k,›·ÐbÆrüý{Ê.ú\t‡þÝïq!ÔwÞAì”ÓKôõäóúeú9íócéºW…ã¶"Ï‚I‹v í2 ì;Û­e÷f:_w6Ï!®WïÖî(îçp.–¾~‹”O3þâ·òþwÈãºCµÑô}RcœŽ#ñ³0øÀàK]2`ÖVÒëg3‰¤ô‰'y3\`N§]›ìµ$Õ5G£jܵÃã§j Ä}mÔ´ãU7Øcs¹©Kj3 ý·Tšÿ´B˜lŸõxÀô¥#h¾cçÉ-7þŒŒ{Ûõ>·«fÀŽ[ë÷Ÿ¿žIr2~*np…äôì¸]ëd@çV@÷[\Ø>Š'ËÓRêïY œKûÖìôçAl>o¨Œ3ÙgO½S_ãàÄ™uï’ï¦ÃÍ”0§´ç™d¹×‹ ‹Î{0Þ‚“èÊy&eKuüBíœçÀ¹ÄœG\’Çaô¨@–¹õCÛË~qp’Ì•©é£l°h•q齫ËÀ†ßG2_Z'¶>«”ã:x>ð¾Ã9‹ôý•²çÛŸÂ8ÍF¬Jú ‚òV]4éà_mmüѶY$`wÛ7ùÀާ­.uýîÈæ»=€s)¿H—7,íª=øšñT_I¹o8å®Ðy¯ ãøŽj@é`ÑÿîævöY¤éÈ™SüÏù‚Í‚95ºväþÓâ<”?§9û‚S.®.¨‡ìNMfuƒqè¼N'Ë-Ƚ°1ß®ÒK?)‹ÈBŠôó.ûŠÜùî·êà#l0pî6}߈uIóü™”>ç€qà©´ ãtPµ-Sá¤Ú>Ëèßrf:ì¹`Áü¨,ÒFrÛÉ,ÑGü<_y©µñ¡È)cùü~ºõ‘ŸÃÒ}™¸e¨ŒóÐT7iàõ¥yIê!é°ª³mª">‹L´ïÙ9ª¢’ͯ{Ú?, Š[he÷¬¡$T‹ãPîbzW¸¶ u:Œ)mÓÁW—E"„í–v#¿Ï–Ð×Ý(Äl „î"çÂP8^‹ /}8nM«ÏšÚ>Âz.)Ø÷6‹L™8å’ã7qÞI/c’ßFåë—:¾=2“Õ΄ ëÖt>~ô¹Ø¿ íô}~=.ð°&Ð6pÒÁ+/§É–Š×Iì"ó> †³ý¥¶„ûžsŽ,­++ÑßÇ¡óU+Æ¥q$‡rÉ4`ÀÌËÓa€ä~r•&×I¿ï_Ÿ\œæ”c>8Ǘ祻áA'ãòÇs»¿aÕž=g`KÙ ¸tN‡–C?ùi~]äHJo[ªKEWàóµןi±Õ–í“Ò¾©ÄqîriýÍñ Äí½©¹œ_kêî´ºNZÎõq>¢w„ÈšgOŸñôŸ_õ† X);wÔ;†óIèç¥Âñ†¾[é4¹å¨v«®Ï”4H‘4rëæz]äBP.oðy×û½g ^Uw¸0ëQîž[¾Àç³¼>† Ûº2è66at oª1Îô½0òi0`b>¤ÁèsGÞß4­_~qèw û½âz‰sš(wÚIäàñŸ¼þès‰ÆÑbœV†D> v‡“÷™¦Ãµ®Ëã^'g ºÙ´ò€©ò.²]ýØï׋}Z–¿fø ûM=ßùü„ó…~Y`œjdKp~³ÓÐaóUûòéà×oP«~;¯“ÊWöÜëãƒ÷Œ_}ÒN ˜݃Ýî+ú¥s#çÎ89| ü²ÿ`TP ì¾È"7ù¸JÓßé_¥Ç•!uµ×‰¡,ê+™·ÛïB(§²7Ðua/–÷¥Ý¦„' _ñHÊ÷A(׃î«I0Ž@Ÿæz âͯ/»ù= 'W'÷1ßM«TœTׇ¯ïؾBûq/ óÿ‡ÒaLƒý;=–Ò} ÊÑ‘á¸÷3…“pb@ëú½+§Ã‡×öËd“1Cû7 öaóxW |¾¶|| ÌÞ"†rÓð}Z8þ8ÕuÅîÈ“ѹ–üNŽñrwkM~Ä Z©®„i âp ¶âûϹ6üs¦ûæ2¶?Ç8„8~›…ÓVuzzínðqF\¼1"õ©m6‰îóh´¦”·È59â¾ðŠ‘,/jäyM&òcøÏ_¹7t|µðþ̘3ÿã³0ßüÌŒ¸ai õ” x3*›l ¼Y=é¥Û3"tÿ«Ÿ1¾ø qýÊv^Ýý÷ã$Ð8ZŒ3óUÙ¯Ç&€~ÝCŽú¤Ášª?­µ3³ }ý.ìäÐȈËæ«Îx›ŽbýqnØ6s>­l`Åø»ìœã ?ÿÍ~mýp0çÅ\eŒ<ÐødÕ5Ù¤i+í—%µáÃás=ªØ3þ“=áóBÎáæójÞW&¿¬\¸°¯ã&°s‡'²C²Žå«gß]*m·Ä~Òhöš {³IÆ¥¶ýœÀÉó™äX…QPS{åAr’‚ð},Îóàܳ’@ Ž[¥i §æKŽÃuÏÔ6 ìL…ÚÆe¿ÞGŸŸëê Þ×îm}{ßøúÉ,÷ÀÛþ-üÂÕbœ+Í,\“ .6šÔè®iðP©Z©¶Ì!{+ „(¸óJÊ+¡—óòw½v*çÁóx|~ÆŸ{%û’ÇŸi=dIËé± Nvlé]1 Ç­É^1:‡|½[”Qçµ< œˆ°ym/Æ•ðaסv¡“¸¿bÈ÷ÂÙa£{Aw-báGëÚÍõ&iðÉhÞ]í’Òï bíµûJ6¹&t~œçMŸ7Ä}uÃö‘ÉPñÆÿ§¡¼Þ—J±Ð/ÿÊòÙÝÓÀ¬ÃÕñ9DØ…(½É vÚuebð$Ε±µFÎs&ò¸i¿ûĸa¥ ª<Êì9 ø<ÔPÇUÀ¹&ÇÀB¯Úý‹º¤>sojîÅÒõG󪵎»a:\#”ÍçzC¶ý•Çå­†²s{öœþ$ÝÛfÊì-Õ?KïN\ºòÑÛaà׬OÙ•eŸãø­r°ª} Ì9çzdHq*dÕl0µ÷­Ò¸_­Í-78CDµ¹-æ–ÇytdïžÅ;o¤»Õ§9Îp—ÇËC§›}•r>*çÍêǯprr¹[Ç Ušr\ù 4]ß^p|•Cè9º#çLÃ’ì§ã·:z~f+ò„9/”Ÿãó÷kÑ¥Z¶.¦œ 5ÆÙàe®)XyfÞH[àý,šÎ²xyõg9wYôù¥<·Kjël¿œÔšS;¬ ‰¥xžË÷éèsúƒ”îŸÈ=À8k]fZÝ?q&ØYt ÏJ…§‰k3Ô&7ˆ×‘`…K€‚ñ¬Ù¼| áó0¾?Â9çü'ÿß)“qã0N­ë æÛ›­Yu'.šîr®ÒìqY³È¯&(³®­kx«ë!„Ÿ ð>^ÃLy*õkè5¨ô´çRΤû l?õi¬nÒØ>A‡áB»»)ýÞ¦B†kpµ™]nzoq†æ&ýÎË €¥“a¿< ßä÷(øÌ}´Íj do²8ñ›xžÃûÛÆ2ýsNûXÝ_gõƒq¬½ÿ0óÊA¨îÝzid<~>ÓÈ”Ñ7ÈÙaÖamý¼Xý„¹3+Öi4RAؼ]äȦ«Yï¤óÔ/ØÙÃg‡•>%¹9†úÁ8½_­«–ûó´k:ï`Ö¡THsK9sýÒ bm^­SãI#¡[DõÅü¼˜Üê~:oÞ]Ov^âtžT›íïVƒí‡p&‚w—:W®¦3­Œ3Ò¯ôì-ÀèÄ‹Mm¶¥ÂºèÐUß ×~ú”*½Ü‹í×Ú²{É‘¯—-'`\ê1¬~ëƒÃÃuÆw¨-•“§MÒL’smËRg7Z?Ï dƒ^Å,É eö–T8—ZÛÔµT.™å[üÐa¡ÇïÜ9ÂÏñøþ4¯Z¿UÁXrjÉÚ¯CáVÉP_¶ß„qB÷n/·óÜ~°…ö›0ާ°|­ŸK. ÝiŸÓÑè=›AüÞá|{zÞò×¹Çë^s4Ší¯¤tnÿ˾– ãLª=Úz?°î‘©K&?zÙ1—¼x¶ÃÜ×ìu8r¾áë~ŽÈç/ôuK•ü‡v(Îö'éóM‰q:¥h»ÚõŽªJ…ãkÔGJsI¥6ÕlZ/´g÷¸<áM~ Ý91\7Šìsm%´ó¨U) 4ãÄ}—F%ÍwÞ_ÌGCý`œþó¶õl¹Öí×úÞøTX2¬¨ŒíÐ\Ò÷™UèŠvö¯;‰Æ—ïÏ‘›‡V-N8ë÷ô•ט7(¯¨¬È;‹ú¶Åìd¶Þ¥óV5Æ‘Ž½Ý¶ñ¸Óúvûw.©´®×ðÊ%Q9 ÖMW:ÀôK ~G«?Byw6âó‡ÎS8_ñ›4C8–o`ÚØ€kþc)‡I‹q>øÙ_ºÞfN=sÜë§iÀ‡1!¹„sÊí ¼9ǘÐ~)ï]ñ87¦ äH ýޝÇñÜpKºiº.O=hÜ ó¹éˆ}é‹sI›÷½Ë®pΩå÷»õW”ÐÎNÜ4ÔÅóY`ø‰ò¯킾ûÝÕwš¥‚õÐ÷ŸÍvç’U?KPUñ“#u‚VN™¹‰‰w/* Ý×q÷3øþ”!ÿq¼ÞYG-Ó†í‚`'h– e[·霘K8—·©p±ÎxNÿŒõ#V û~ÐÑøñ¬FW|€ïçÒ} ;ñþŽ!ïqü~…ŒßŽþ¦S禂Eq’ßA}.©;¹Ë9¹«7ãM…sÒI¹û"—¾NüúxípXÛK›1µÈ øy"ÿIçôýPâø{¾ö.÷y' ½Ý.bîÑThQsᆰŸ¹¤Î©gm;zcÕ¯ýcíýaèz¾ìÜ¿Gȹ²|ÿ–sù~%71NÆ”•v^{X 8)h$‰J…ÁÂõ½Fydsw×z¥¼=aNÝÁ«g¶R²ýánD®øf:)ÏRäGÒuä )çÏþœ.,hÙ= ÿÜ€“.'{o‡ûgš]72¶XÏ®Ô3ñ/]Á\U´ÓzðHv?µ¿¸ßÍ÷«ø¾·ññó®ßö÷eùJ÷‰µÂç½àe›Q[aš{­Ê3mRÁïÀædó!yd÷§¼~n&Îp§ü©{7—ø²sd p:ÚÙ¼½³ØŸ97’rû²óúüÖãøOV jpk ̬¾±ÑÔA©°ü¾zRÐÈ<¢öÜ~þéì<¡êxþœ$|¿Ûp¬ÙÚ87”ç=ßï1äÿ‹Ù×AÉ?ŽonËãË M…Ðf6N‚òÈ•¹9·µÙƒÄýAÆUç£|Ÿç?Ï×›eM7À=z¾(Áñ·%l?æÞi Ð{8?”î󈙖G¶¦¹µ˜h#gç/Îüœ˜ðû„”Óf%ž?ðŸü¼…ŸWêãl{3kâÍ‘pv}«>©`›_£ÿ½…yÄÊûÆÉ‚æÙ<¼¬­sáF)áó%z¯Ëø½þzh~ËÙ¾­?%Æ™Û:iÑ~×ÍpÔ÷Í–$ü<ÎtéУËZü¼…m'“!ü¾2‰¹l=èÄ´!âzp¯IJ«•ý”âû•~×xÀ÷—ÒPýЀÖr¶¿Gç9*Œc"]˜úp´ÌT:=qH…Jí†%wÝ•G* ?9ß3bë“]_Ÿ¯[xìXØñ|ˆçídÇ-V_~)åÏ¿’ûIjŒsá´Í¦ª]7ÁÁ6ïV% L…ð6;šÜ:žG2F]I‘Lu9å†5s}æ® ü¼†ž¿[ýÂïÕâx÷×·¼öaVX N ­‰ïO¤?Nl’óHt碹I6Nœ#È×™dÌáïÛ^f fóÖÁÀëÚÿ8&s-ë;¡lÓùuö{¤B©oN­óóˆÇËòSvŽ]] w‡„:K*Ž-¶%‹k—i¹Yæf›+Ĥ¼ñŸú­5öžO´cçô¼ÔèeŒî³n„¨{cº> J…ýæß6¼Î#Q^Iç»Â¾¹MoÄÜsùÃu–zV¶ûv&ûÿÙ§´Æýœ›ü΢ãd/t]Óo#$‡>Ôlb*4~v½ÁÝ :21êÂÀÅnì¹Û]¼ïjØ(±÷«¿Ø¿ yãÝ-3ÙûêøðI:|ay_ìqã*~–èȵ‘Ÿbï¸=ÿ°&Œû(î“óu Þðûü¾ ½‡Nûµãl²û¡ÔÇm„—çV\Yéœ Ög¸”é­cüvÈ|髲vbuÕUœ=Šoûø”%”Rwöµ¶û$åçÖt_xà/ù¨Â8×vMm§ß¾\¯ |ÝÐ;>ŽîÜÓØ^G¶|nz°ÏGx|ZXx'ôÜ­{]^ÀÏ™8¿–ÇS™¤FŽz>ío°¼Ç8û3®ôÚ5ct2Zq}ïœT¨<¯ùÄg#uÄßpal(œµZZÊ‘Ð}ºž„ó…Ù½GñõðõÄ¿õz´gÇšI¾Õ_®Oøp×_)ÂÊa¼Ž¤|{ùíÅj;~_ްu¡÷’`Kȱ ϰ¡â:œ¯¿ø~ ¿×o¨ŒcšáÝl’$lðxÝë&æ•Öëãsû©:òÌ®ÉÃÇÖ¶âú†žO::OÄÎáå04öä6£œWbŸåùK÷i¤´N^á|Ø0Y kœ½" z_nÕm¾ŽÜ³ožDŒ‰ûÙ3¥.E·„¯_èýI1?ß5ÔŽ‹M(5Å=ÊX'.oë– Ý. |¹ZGؾã£Ëؼn©4¸Ÿz|Úp%´árÃÅçhÉßW†ãÎj%œ¤-†>å>ç§„¥‚¾ù€Ò‘{t„>¿œy}ò焳”š&“ÀóÔ8KQ!b?:gì´nðC»_òH‰ã—)w7uÿåE«ÛúK±?9ù\¾¯#Ãz\Lò¸ä ì¾=¡÷{zÏÜI|îý>Ÿ¡÷Žì~9_Waß„sF_ ÀáÇW‡˜oM…·sÃ5áÙ:â–SwŸ®îlÿ׉Ðq[’.ð¹µxÞÇû­{6OÂqû.@̓†‹É©9—W¼Ð±|tƒò†>ßß/>]‡›·;`Åæû–b¥÷½ì~ý^Žo;2÷biï¹`][˜¦Â蘨ˊ²ùäÝŠÂía+\ؾ¥ÿ½ù½àû½||ú½»_¾· Çñ/%™ôžÕÿÿcïL€œªÒ=Þ=ÃèCE4¸TGÌ`F‰:“sÑ KPŒ£bà9xÝ åŒ’Æ #BpòFŸÆ¼*Ž—‰:Oãz⨺uŒ£# n^Ü "`PdÞÿëóîkªÙT”7Õ]õ+)-¹,ßÿë“sÎý~Wˆ…‡œòî±ËWˆÙOYÇô)ÊÛÎ~{ЪA“´§YªõBÿöõ~ÿB÷k]?ú~i[½¯]m¨þÑ,n™ýV_ïë+ÄÔB÷ëç,Ê3¼iñ—Æ öÃrŸèÎ÷ŽNå~4Qèϱúþ„¾j÷5»ñ«éýÑ?ê7|߸E4ÜxÀ²û†å#™ ƒñ}»m£>$ÙÃÍ}oœÐïYiï½þü¬?¯é{ÒmyÀsÔùñâ¸-{w«Ú"~ò̬£(ÏrÄ×lù¥Ýsä½×7ž"Õ½•QRÿ½êó}N¨ÿ©×Iöõ}Ϲê•û.lX7]ÜMÛ>}ZDáÙq›÷8©(7/~öÑcÛÏ]T®ƒR¿÷ îë ãïK/ñï§èWç3ùþÔ~] ÏQßï§Šö ÕMêÞ"V=ܫۘ¢|ñÉtb¿Oêú—jßl¢ÔûûzŸLÛ>Ï÷º_ö§ëÿŘÄþ\pˆÊ žóÞ·¯ØÒ| ÜP¬_Ù»±ElYùØyã‹òÍ=òÖãòyT“Þ/–Ú­îáås€Wüêýµwýz?\}~Vû©Y<çÁÆ«þvÁ'Óä›ÖÆ3Ž›Ð"†>7ù’¯&åÜK§½3xª(×<~Ñïÿþ8ö¯ÿBªón‡Tû‚ÓÄÛ ß8òöûBíçúœNß¶ßÓ*á9¾<°ñî óåóŸüà£ZÄ‚Ÿ^翦¥(G­ÝÔ½õw' µ^ I½¡ÏÑÔ¹JSû¾¦>oTõw‚Pû êó~Ýg«O§ÓÎéùÛ¶ K-âÜM÷Î~­(sŠÅ÷Œ1Äè“g»~µèBé cä}…¾WöкÖðµÚÏ3õ=D}Ï@¿—Ó–<çÅÊí3¬ì5ò/:JÓ?k‡n¸nñ>oåäØÚ£gÞ6Lô›wÈò%rÿ¹UŸÖûsú\M¿ÔŸ/õ>Šý~ºç¬]ñìˆûÏûƒ ÌŸï½üÀV1ýÁ)“/~§(ÿöhSaá”Ábz¯0–—IÞÿîíûæ9§Œ 2Þ8ÿõÓÛß#RçŽU¿~X}¿UçCa<§ícÈ ù¦Y7sN·VÑûäȰÿz¯(÷:mc¯¹ÿ*¦½~Í·™³ô9—t¶öà–à©âÌòÉÝ–]wúgœó¬¨ =Æö:cõ&ÿ“ÍËFΘ7F¨s¾7Žç÷NÓu7Íø£ü°â\Þô^‹8kŸ;yèâÔ÷~®ºì×o/û8"?¿¤Ï–W{&ï 6¾ïD>o$ê3wþÍ%õâØæOþzþü/ýêýŸÓ>SyÁs®ûlêÆ‡>½AÿëÍ_]»w«°Î>¹×ÝŸ嘿¾¼â¶h@l P!\$õ{·úÞŠZŒú=ìŸÞqɯ?_Ûïõgñœ«Ð=z¸IZ÷Ô_¹¤©Uì÷ù´¾ª_%»]ýõ^þ‹ƒbîkŸ~rDÏ‹¥~/¥qÓô[zu$÷³BŸÏ«{%ÕöýDû}ÊžSÚpóÃþ’röû=»/¾¬UœðËÆYïõZ%Õºg’ˆQ|pŸø,©Î{‹õÉõçÊç÷A¡öK¾öÏ4û;ÚÔK¨û1MBç -?ëV?û ÷è·ñf9mÕð/N[Ø*ŽI?snâðUò¸âůîýÞ$1òÚ•O.{x¶TõÚCdûÊËã=.m?7RߟûŠßÇ›¿r¹¶øÕÏ?I 9+þ†©>±¤N}uÍPêš¡´»ÌPªãß3ù¦}<ik®í@Ý–/Êîš°+ÊÅþ’»Oµ È>¾Îf—OÏûÖ>¾4ÏŒ²¿„æ"YÀ DØ]b°÷T;¢Jìá‹Ú<| vDmmvdœ]|ä˜N'B%ž§gë†lîSš‹Të/1Ù_â³ÍEŠð¬oƒQΚyHég ÍBÊxÔ<”0ÏŒtÚœ%ÚÁGA ×ÌD!·´žYÇ3¾kÝP¡šy‘4ɰ9¥Ãì”vó ¤2{¡2;1ÿ¨v®w–ý zÖÍUÉ:¡Õ5¹#=²«?võÇ]Ýü{"o)yžR[™#®]z[s±¤k|¥4_7îÒ8ÏŒóÙfЇØíT¡£À³Àƒ"O°ÛIÏÖÕn§ ÏŒ‹± ¼Ð)àD¢ìA°?|.1v Ó)fs:%Ùï²µyq ö:‘§4 \P ”y”ž¶9ôÈ ]ëBˆ° fÆi/t”çê’:Í»:Sã? t¸Ù “áyºÚ }Nä'5AxÖ$Vωsð<Ý‚ÍC³ÉÃ53âȰyIMö’zØMμ;vÔÿ\;C7Çs8“ÜLÇÌ'ªKúêZ?vôGê]=ñ‡í‰NþwälÃ{Ggk;çR_4Ƴ4É-šéÄwg‡Ug³4=¶9ÀAü"“¶Ùw4ØÉ~˜0Pü)àD¢6w3ÍÐtí¯|Wi›ó*ÇîæÎf㑇¡„°–4pñŒqí„!gs…Ö‹P¦¡sÍÄ‹€;îRÀɾfš›@ÈÒÀÅn»²ÍËàfL…çe&yn^ÀæfÐŽ,rˆ&kffzÙÓ\fGƒÅsŵ§™æŠ“§AÏÕ˱&jó‡&ÙJsï2ìg¦9™B°³Àƒp'@„òð"èI»}q‚]važ%®gdR0…šýJ_]½°k­¸»ôEÿšKuÊ¡•ÞÊt{û¶åœÉÔø@iÎp–] äb(ï¥4z6z˜ÝYUú1Š<¼4”‹]ÏÖî,r1xxÆ0¹5^{ò0ÙÿGNš8;|ì´×Î,‹5>üæ-à@`"  ö0TÙZëµØæ†š6' 9—k] Qv1úv8—c<_˜|Ëv0Ø]ËÙÿy–ÉeïaM–ç kÿ‚öe‘ÿ“|öyàC`-öF@–ý 4W¸dsÙÔ±++|´ÅŽå Íûaï§—Ëä ³saGýʵ³„ó<ŸÔ4†ˆ¿Ã_¿3Ÿ¥»zäî×#ÿÝú£»NÍh'Wt~Aò%§¶ãK.Ûü¦nv%WöTΈl'^Á;º<(0Š<Çþ=+9,P¡óÌd{jì>z‚³9’3À½¿òLdlþ®<;’M>„Åž©œc_jg>ú¨ÍSCžä*}®F¨² û&œY”Ø+H>z;’Ë ˆàe€›}‚›‹ÂÃnrЇØ\áÙËä¥Ðn/r¬ZR“«ÀÇ^ä »)RºÅ.¯(Ø\^ä¨ð¢øì¨Öxä+ìÖþ») vÈk‡WŠ]<Â’N& J À~ O¸|aJûx´çœÒµŽŠ;*‚}•[š\‡ä‘¯ôSnž,»)´sšÜ‡¹/…éQîx/{{rÀcóRhùVÉ_›bßjäØKe›ÇÇÁî®0è‡:dó¬FÙ³ê;J¹«)è&»(vÔaíC#°€Í Âóí 4…p¢1Dý¾xÊ}íê³Ý·Õ ¿MÿÓ}og{îsö·«ú[g=úYm/³{V;ë_ºoQÏú¶ýjgzýÙ§ë”ë+¶ ÷`z;žèÊ^U;¢«{*¿…ö zQTI.,äfÑ^ £È²ÀÍží¯'¯ªE·¹Ÿ³½;<``(Âp¢×DØsáéÄacŽÅßÀ͆Îý.q ”û*_`¸Ùí\! ; <ì ¬Ú¼^vßTÑ[Bì½ÐN0òª:€yÀ€ ØÛ¬Í>„"bó©¦è¼áȱ›9É!1AøÐ/,à@¿ˆ€0 U DyàC¯°ØñG>Š0Ð+RÀ‰E»ÖU]몺Ýo]åãg’+•`YàAq&@„Q¤9vðÄ·ãàÉÕxRMgGªª ˆ¢Î7»¡Éýå@G@(ô»¿¢ä…¶¹¿òÀG~hr‚PϾÚKy¢µ»'ÉÁ°Ë^;¿Òìò ,iàB`b  ‚Nн_f'>ûÈ7û|´çüÑrã×<ìþª€P_åöôS>ûj?åõÉoÿ§´9@yì}¢cÄ£ö>vþäáL€ªÍ÷E^TòØ—@M³•ÜÒyàFxã rd‡ ÈÉ®¯°«ÇÁþzíC±Õ8ªÃŸ…pNh)àD3ˆ‚ )¤ !æïðÖS Ó×Õ#ñW·Ýþøm{#õEÝ·Õ¿k/ü>û õÀÎúß÷Õû¨ïýýŽþ¼ÉÃJ®±ø6|‡A]f;^èªÍÉêe'4¥ rìƒÖžÃûY}(T 8P¬PŠ6(ܯ rÀƒ"Žƒ ‘³xPÐ ›:gó”‘¿Õ‚ ÐÓÀ…b²»5ªô¹…Ÿ”ÙgHÞgzYä€Ûÿ* ƒp¸Ž8¨ôU.Ã,ð°ï¹JÎ2&¼ì0¤à˜ |Å‹ŒÝÛC˜²6¹]S–ÁŠ’» °¨‚ »]6·³Ð¥€Á‹‚û Éûê@# Ïg 8Æ(=+œg”@=+ œ[ñEA â4p!ȱ®õ]×ú®n÷[ßøç$Çk…™^g’ ÔyàeÇb„Q°9àEÑ&¹pM¯ñ»F@Ý®).èÈ;©³À‰‚ ÐÓÀ…b20PôqPŠßâ„Ax„$‡ÜŒyàC(,à ³V`q@Lá A¸˜8¨€‚“Nöºæ!²€A ƒ,ð P1›¯‘<ÖUvã×¼YTípY{¸$‡Îyàëßá¶Ž PÞ{AL‘ãaŒ²óÞ@(ã |g’9ö¹’÷¾ ‚l†}®ñÁÊ{íAx  q¸†|ÓŘNöÝkkœ=®£”Ò‰ GAi'<Ù4‚4p¡Ä@Ñ2ÀÆ÷wxî©ÖéëÇè‘;²‡¶+÷Îjûõº]Ùãv¶·íè>ÚÑÇèÏ* <(œ¨‚0 (¼(¢$Rd»mÛSM¦}±>vT“76òì§Žƒ ¢ðRÀ‰â‹‚ Ó´^CFhŸ…X¥~…bÌ/ 2isOçi†âôÒ¹'гL½ šni Ø›ä‚5AxQ¸ PAô"'zǪ „¢Î ;ª ŒÏïaÊ-M…n‚<ð¡à-à@ÑG@è?)àà… rÀƒP$Ø)D8ÒÀ…¾eDß ±CZû£N¸ž˜Í3DˆÒÀ‰ EA}Ñ)àD¨¢ è1iàB‰2"pàB袠è1iàB‰2¢ÇdMíŽw­ÇºÖcu»ßz,Äÿ¥ òÀ‡â´€ƒÞ•àC¡&¹XM>­(Ü(œNq”@€ü×À‚ƒ° ;\(ð(ƒ =Ü(ö8¨€Š>J €âO`‚<ð!p PB‘N:»)à s%²ÀƒÀ$@„œ p!í((‚˜&¯6Âe@(  „ÓÔyàEP BlxÚÄ`åÛö"¼Ipd€AŽƒ !ÐYvcG@øî;²ƒy¸ô(½ãŽî A¸Ñ â Bh YàAcH€* U÷\Ž»¼GîÈ:Œú!õÁïë,sk½î‡:ÃÔë°ïk ¶­¶«Ï5é÷›£¿K’ ÆyàCáXÀAŸAxQDI.$äeŠ* À@q¥€“Î5é/ЬJ{f(²4p¡Ðb  ‚(¸ p¢èœ(º<ð¢ð’\|&ÈŠÐÚZ‡¡ }´G†‚¬P?BQf‡ÞQz(Ô(Ð] ºGžãB¿‰¯Ïr«"ƒ𢘓\Ð&È Ûw€"O' = J €‚O'úŒ=&¼@TAý%ÜDT@Á#9º+p”©Ç à¦sMP>„%É p!8%@xÒÀ…Å@ÑO2À~B¸²´—€•A½$Üè%qP!ô’, ›TB|ó3gš{Ýg‹so }‡{líÇEø›~¬ T?«ãšÕ_KÔ4ƒ:÷ºÕÆÒy_/Ýs Ïš$Nëþî‡ßÙ*ýùÌ™·. оOg6}|ì*9dÔ~k*C·ø®{âýWLÙ{¯î½êí^b͋ݞ}l^³äylížJå¡çù™xÎÚÐ…×_½ü9ïÒñO´ŠßÝuîsû]%ç>kåkãÙ»z©Tž¹Cµ×¢ÝC¥ç_àÆ·ÞZïÿ²G¯+'|ÓÆsJsêçöì‹ÜëŽ+ÿ3ß*Ü…±}®?}•ë”÷Ý2äDñÀ9/œèž2K²§Gª¹)Ç´ÏOÕsô<íûUóÉx.Ô:=GåfùóËÆsö'­¢O²uóéÓVÉ; ^=Â8FñìPTY·¶ ƈc FÌP ôjQ1*´"hƒŠ`l0aFã´‚‚ ÒfÔÑÅ€6Ú¥A̘1ÿëôÞ»½ó93÷»÷¿óUIÕ[:åÔYtŸµÖÙé¼ÏÏI?‡’A±÷ï¸Ø[ËS¦UëhÎ8Œ=ïýžhÅoÊuþ™N7-Tˆ#Ç8w#ôͤ…êù8cƒk êp¶P:1‡äµ*¶ÐÝd¿ ù¤Æè9ܯ…pÿ=[=‘ÖG…ûüQ~NæÓÊxHÇ×xcZÓu[HŸ-­:’×à×­OÊ!”¯R~æ`ýžvŸÍü»~_¨o… pî¿§±C«oÔׇúj«1Ž8¿A§¬ÊÛHŒŽƒ™Oµkðòðã˜Føy²«½¶»êWŸLÜe°77{ó¹²"§ÚƘWÙ.f¾xAaõŸßoS&âþÍî=Pa©åƒkûe‰ØfE%}l%¥Ìkø5øÓƒüñ9Äýþ¢™­k“Q/6Õ?4‹PÅBýXú1ß—^Ú<ˆ5}±`WÍG"î7T‘/mŒq¤Ž~oÄ[ÉQó–FU7]ƒ³Ý¤Åk1ý=uàøÓ’½µN„Îs<^Cp.µÒúcr®÷¡þlfÌÇ© ­Œ³ç™y¥K¶‘·×Ö阽î!c;ôò z ÃMßv P?CÍ!ÌÇœñ Ü™/-çb÷QIï/Žì¿âI0NÁ܇.»_m#“¿èÇ;žºņ×:m÷Ë!Üß‘çç­WŸ“SxÙ8Ï—sæ)?Pz¨ÍÆAcüHŒSeØLÝ®=¶‘f©Un¦\£Ü… ¢ \ÖË­ ÛS“¬B¨¯£9¤ m¦‘p0îWÙXÀHéhý¯¾ò…Â8}›>œåµ•Ä©ËÂ#2ñþ垪2/‡Ð¾`Ãü6§^—Ǭc/ægæÜŒrÓõ™/¦«–c©©Œã–n¢l+éy{rÌî×`á†wŸ–ç¥»Š‹­†Ãذҡ›3¥Ü_ùõø@·ëù‹¬*yÀĦÙÃ~šUN­mÞqû‘×¢î3ƒÏªýÀóý¦˜î)Ãiý`œ {Ž|Þ¨³<ÒŒòÓ`·XñêQTi¸cÓòU¡#àóÖ†íÎäêÏÜ–ùÏ81_Y­ßåzéh}+r*t^•ˆ5¶n ¶ý‡iqï:¤Áæ•6Ibsˆå'§½¯ƒ–ï¾ÎXÌøy_D¼žÚôáž©¸'ó‘ÍÓr ¨¹–7©©Œ“Þ2÷Ý|¿mŒ×ŠNK¯ ;’CëÌÚ¿}<­6O±²æ<2Vì¶9ÓùuÓúgÑçÂæ¯U âÏê³Gãˆ1N> ,Ü¿¨O¼•^LƒÃZœ”ŸÉ!šg{˜ß™YÍVŒ ã~„ÌŸÒ‚qözhùhÜÏŸó hÿ3¢õƒq ­róz¿ƒäŒùxg$o{º8%‡ÀfóI=,{CÕªsê '¢Ç‹õŽ-t GZúíº1Ä•û¦‚Í%³p“*•¡Î³g-õ.Ô…•¤ uÚ8-LS?‡úýJ¦Ë$«›“4cZ0úJs·Çñû;:@å܈v§6‡±çŽ#¡|8|ðç—ñ1Ž0mªÞ9ËÅ-áóŽÃË¡•™¯ôlˆž!8§õƒqZ6 ò·('O2=¦še¦ÁU÷:i9ìyÓ ('~>¡|H­ïÿGäÄ~Oú=ï×áÅRÇ Jù9ä±Í–{­lðA6åfÀ²¹„úvÛæ­åWðq÷÷¤¾¼Œ—ÛšÖÆé¿·SµSrByˆ×aÉÛ‰ÅqOrÈ€-N‰—؃UwaÏIÂ9NÜ׊ó¹¯õ+ïù÷OŒq|”?áˆj'quX8ÙHt¦v­•ᣓK^68b~iì¨õnR•çaó®9÷0ߨpÈÓÚ©•ä–·Å}Ïèsžr %ÇrPt¿ˆí;‰NÁ@Ù]‡î¤~.¹æÐkë¼LGHh òi¹;”=·ïŸáþ¡Üo}DÁ6ÿ±f/Y¿~)zÿLÇÿj"Ã8¡"¡³í$ÂS¥±ÝuxþàÃÉfsÉâ?÷_Ï⤕öÖë7›0Å3Ôq48Y+ë—ñ#´Ï…mŸò:Ž4Ô>)”ú&Ë1N„‘×íóv’W®á+e×!è§ô)ŸD¹d¼O½“º˜ŸºF Øx§%9†ÝÙPÇŽ~ѽµÏÓG; Ü§† 'ó¹±ãê ísPS?ÂýÁêm¬ÚEä«õåÅ¡^ó ó‰#-?½™©Þæ å%s_Ο§¾ˆÃ¿—qÌ0NNÑâ!ÑdįͤžÝn€@‰i85—­Þ½ét7FùF†ÇÊõ¹µ"BrµÃW)od>rÒŽ§º9\÷2°móý¶'yhýy5õƒqèý‹&Í*5KðzÖn¨4¢,(—Ô™Øz¥â…9h°‘ál¼Û‘äæv¾¬{Êèç ÔòåçíxßǶÔ¼1hç„yà“ak9ÌŸÖÆ _õ(ÿõæh²}gRшù7 oí;»ÂCsÉÝW!SöYì>õÈ?6® ”Ëæ RÛ~ă׫ÂÌæbeà\y>ÔÔÆ©±N”° bÈï!EöäWºêÿ(<—Œuk~4ÃØö~ЉéN˜¯/aüh0Up0´à…5_½6yÅüw¿ˆ–µµ¿ÐrÉƹ¡¾‹ÉGïd×IÇ:ÆìÊ §<-½ûÖý|­pM.i»yųGÀ¡!qF #^g?ɪV“s]§ÚÁ¬niùRœŸÆyŸ´OÑy‰ã¬(t¼Õ+%šÄö³ë?©ºz„Œœ|XžKŠK’^Ý< &i­aÚù÷[¤ãï!@ý_ŠVd¦?»¥ßYÅÕy["vÑkpÊœh²þº ·}”0oS‚ý—#¹$|„ÕêQaX«‰oBW†k?@uµ”Œ…ñëÖVÄz¸vbš!ë¾­+È“Ÿ¼¿â³cçYŽ Mž/2&nª¼=öL»šËÆoΠIã µ>÷”?0†ûØÂ²SY×WL5“¶ý?Fþ\ê%:Ö¾ù$Σõƒqjh 1£I‡F:û’•pÞ«V»¬¢\’ú¥E»á.°Æb¼þï…ÌOÔ”pŽç¶óñÁ‚ãũӂuX}º°ù6óÅÅ8¡žDÒÛ?šLª}Do “6'Ì|ü!—xŸÑ[ZÅœ¼z$WÎg¼®Úù)囉µÜtþ¢>ô#A3­u¦}G†qâp”³ì@4©¿oSþd%Œµ»Ñ`JÝ<²Ñ©Ô4þ<™·¯ZÍ áŒØ‹P.Á`-G†s>¸Ÿ¾A¢gŽÔÅ*úÞË1ÎÖ½†k.\&ÁÝ-lÇnWBÞÝËzaíóÈ•©_EŒûLÇNXÈ8Læ„Ö¹̸qÒújØXíúçwkëJsès.ãœéÚÅqòŠhòÖdÚ‰÷•Pv=Rý±1<¼) 6ÁN ÃîØEl}§ñ½2âV â>ÝÌO¸DËÉäþï”WÌ|r1NÌùF°*šøÿôN>é°(§&H//ëö~,ȶ u‰[¬½?Ì'™ûkãpμ¦^ÊKÄϦ)ÇÎëCªÑú%Ø8NýdçšGFd–[7²†³küTÏ–-&t}«á¾×·}û( £õ€×“°ØÕ÷~ 9ó³Í2Ï\%Þµ®ã“G('uM1­ÿ´st´¼'ºÎä¨åÇkò¯Gy©»ÉÀûæÁË*¡¨Þ¹ÚÓ§æË•_ž×3µa};ŒP?T[bôxIÀŠîS`Þ£ó³£|ûçòñ$çô|µn†qnÅ÷À[¸›l÷¿™ýV g5é»tn| 8?$ûqßk›JÆ7#Û¦Dí|>È^ëÍÇ©tð©èe‰4ábÈW~°2ŒsºIõj »ï&¯b†ûù꧃QÎ3(]“G:úWI2vwQë¹x+æ³õ†vZß[Î çq4ù× 3Ùïn‰ßO’î¯-¬ Ò¡¡ÇéÌF{óˆ{ÃÔO.7Ý¡­ôÑÔe„η2ž¦8¯[“¿x>ϳV6ÝMJ~‘E-ªžŸìØT|1Ä_]t2Úmd Ó° ç\•®xbâ©í—|\«ÉS¼ž³‰°Ò¶›h°Ä Ó¡—äQB^I¹xDÖØ ®æ% Ã'û"r"âÙ…xgȪüÛ5…C0,ÜY«cÞ‹Àßë›òwØz(õÕÖyW"ö½z¢žó†ÝÄ:#û²Ú4Ž/ÞndS剸k¹Uá24Ë*{:ž£õ‹> à›[Ãü¯L  [› Ä#z]c¼ne¿n!Qov“êÇêØ÷H‡åIϪ¾hy‡œ>û›¿ú°³&’‹ çmpîõý¶ÒÞžw‡2g“ôÕ_û‹1NÏFú,ö¡û 4OÕi©ÕÜ~wHùÎY7Úvr‚ß·ª¡¿„ÐñB'BO+-o‚ó³8¿ÎsíÛŒq}êâŸæ´‡¨;ýépsëV™Ôî šcÑÛc‹4ym¸ê©ñR$ÜÎf# õ…·êÇÿû<Œ¯òué¯Öƒ1Ω¹BÁí!kçœ<é“ã‡+žÖw‡ ;Vßi±×0ðl•‰ŽÅ’e L”H»wuˆwMgè!Çݽµ*–¸iŒùÓ¡‹¸Wë¡SîG5¬º®ëd CRÅ;<Ï,&ÌGŸ Ñ«ÙkfsG-¯Žó¿¬ëzèÀ#ï‹y›ÉgªpÌûë±äBÚÞñ:qéP¥•xÓÉ™wÈñ…þGf핞?O¼ˆ0^y=þszïɶ ówí:H»^ÏùÓ”§ó gã(ÛÆÏ±$.qBö•tH\ݹþÙr¿oiÀ¥®CÀÿQì—øwa„^¯&¡|w6Ž5²Z¤l~Ížƒ_D”÷8h¤ë%:ïKÄ êö½ìKâ{5ÓÍK‡ˆáï ǬºC &îê0t:lâ$cœ@cBï¿›—ŒÓ®Ÿ^Î÷~8º‡´ó›3‚ÖÆ©]M ÜÅÇnæŸÃ8¥[o·<|‡TŽ4ŸßßΚ_¸^¯obãÎèAó^k7W÷Ëx•ŽZNŽAtôDןDo^è—¼Þø>…¦~0Íç½Ä2}fAÕûé /ºwsûµ;¤ÝýÙk‰Ÿ;´Ó€Ãùø~{¨û2²½–÷ÉãЧÜ^÷V¿¼Z5Wì%» äðtS'‰\xp‡ÄL4.4r,$¼ÀqáäD£©É÷—U¦jtÊ-ªå7jê¯g}ìå§Ì½Ä¶ú&GU›PÐ:h¢ä§»DÝ(µÖâñæF†l@8áã=a¶Þøµ”ãJëDßwbyùBË»åëF­:o8Þj›'`œ·#:û÷’écìJZÜ{ƒŒ’§íï’ɵSºlµ•À¯‘ÛÌ#œóIùv£€s”ù¼”Çãó:O Ü<5ƹðpY —}äMõàóÇßÇëÚ}z—$¾™¸üÙìq°²{Âù~ ðñ÷Oç×oŠ/Š^hëïñHáAJ?·±I•“o*¿ÚG. ß5¶lòMˆà{—Ðç—'(‡4 Ôõ]D(‡Õ˜­? ÒîÏñûÐVW’Öÿ㎠e|@ÆéÃ8¿ãÔzi5²]öà›0¥G¥c}Ý%;:ô}²è…+èÃ>/Òn ãwfãx-gðò¥§zm*¿qž7WS^<ÛgÄ8ÛâvŸ¨nQשS–›°Ý{ö$×mwI}§qqïr~–’b0ἇf³l«>ú2øú!宼Qžû( ß« ŒÓ³ÖëS óýäóÀ÷O%߇¬½›)î’w¯ú+¤£ ëï°ÄÒ%$èÜ¢ôx;rlÈ´¤5y~`ûüùOÙ¡îs,ü¬ågÄÞ¨•t Ê}NÆÀ8úOjµ¯bOÄKß½Ð{|^ŒuO?{—Ød_J¼gM¼+qܾ˜¸æÖ[³ÂÏp^Ý­Kóï\‘O»íú|á…–oÀ¸.@yt4ßä§|ùÔÓ{ª&f&ú¡ÁïnÂþÿEknÞ%O è»Ä’#³v,$t^0’Ííµë <Ÿ§jê¯+Ðy÷‹'ªN›V]Çë†E¦ïYS|—4ô‘4»TÛ$ 9æ& aœ¿IO•­SŒ¢õ€×)˜ü3µâÉæ¯Fl©’QÏ:5ýt—4kýë¡×“Ü`èŽG¤‘22qøÙÆ“2t‰Ëª6ñ«ú0Î„í³½Öô %â×:æé÷÷Ç3“Z[µÊ€çÇËg4Î'áZב°y¡Œ¬ð©¿XA«Û#Ú'nå øßàûžŒÓÄê˜~~cŒóÉÌ*äH`< ´x¹¿8N”<8^Í2Ÿd¾¼\¼ÅÓ®mŒ_.]Ffi&,ö@ûðDè2ýd§¶½Æ±uÓwZ~=­'80ôx› ³l~ŒqèúJùiÙ‘Uà× Ú„·óÉ'Ÿõßf-ô‹ö#ì ×ÎÇ®÷ôúpÙhPþ­£6Ž&¿ñzÛ»¬X–v)ˆD&ý¦ùd@xs¿\³åùĶuðÏÇù@aå:{'7Äy=[¯žøë%Ó¿y@³¢å+¢ƒFÓüÅëŒÊè²góÓ’ÓÂôø“9àÖðçÓ󉮫(lâE/ ó …l?¾8j¦‚^«¿N«>Q»ž‹EÐ3¬¥ÍW¼îxYç)x|«{£ÕÐmµ]®2ŸTéžšúy,|~rhܬ“ § Iu?0ßÝ4hó8wè òIÚyç 5ݰ;Ç`¶~È8Ý'¦Èo¼³ÛRµz»ÉŸ£2ÀLŽË'ûÞPÍ ®Äu_yjÀ"rýüOÃcgö#”³cÏÖl´\Îá<ã¯ú=Æ™÷A'æ1Ÿm.ž—ÇBÎylÓS“ËmÇ\ívÄ~îÖf]é„E„ñ‹ çc7Ž˜ÐciŒµ6Ž&¿?•ˆ£›  ™äúžV²­)°Ç/pnt 51S¨¤»bGç»N7žZÈÏ%¾Aùªô9mŒ×ùµ8T:ëÖ¢y¼>ȃg^ÅKê«ÉÅš?µ_ÖË– yþ\5?#QË=ƒ¼ÁÉ{dž#…î@yëÅt>‹×(6F-Ç&öß*jÒ\M.M¿o:Íó’R>æÉçMNo§Z°} ãßøOˆì ¦NèR,âëí”Ìú3ÆYs©Æ˜‹$‘ôê{?Ö¢V&<}ª{²k;5éçá¥H7 uš4=Ø@;ŸÕÖí¥“I«ª”Y3PˆÇãû›|ŸX“ßç嘥y5z+ÈœE[LëeÂÉ"ýçÒnj¢ÖÓÎé£#h¦Í-æ‘ðÕNGc¢ûk矔;.Ò~úœ.)œžl‰¼õû¾‰&ß1ŽN¿Þ'+ˆ÷«<+ç–™Pxîj³!–jâ\û¥˜ØºÂ¢­+;œOšCèþª®vÜD÷éÅlݰHÄÿäãÎ+Óä;ÆYß|‰S»I BÇo™ Jë–rÖVM†ežUò÷€9~¯³Ãf“óòáÑ—SL‰üCÐÄSVÀ¸Âý€ß:Ž-ñýû¯¸Ãgp‹…dœ‚Äï×;jÔ?ð!^?ÆUM†?Žtóx,ø½,{´<”ðùíw}§¾ð}e~NãÚ±Œ[’þ_ñu>—ˆ—›ôÙœä¨ ûž®Ms̄މƒ'.›¬&Y‰Â±nîÛˆOÄPÂ9à|ß7¾˜bt|_í¾Õ™u[:ź/âëtžÀ¸ôgäa¥VAŒ®›_S†d‚tô7½ÙjR¥ìÔê;û$°GïÍ>Ï&”_88ŸÎk,µûp¼O̘{ÄÂ'jàW?1Æ!ÂöEsÑo%Œ02átñà°ÍËÕdm“ÅçIÀp]åáê[óȬ¼²S6ŽÐ~oœÿÅûÏ”Š(×y ›ïRžŽãäXxwUÆ8³ ®/^› ‹r®ì9¾EM§–ßÚê=„ÓšÉH“ Ÿ&zŸ´ÿýüã¹ó~Äóƒó‘5uƒ×ÏÜÞnÇa¼>]7Í„‚¡íS÷ªÉ…YIÉú´vd„®Ó‚+äÝÖÇõfÏ-;”paӽܯm{(*¨9yŒz|?ðu}»fq);?%|a°…‚|ªëw622?;®&™[nÚÜü[¼Ø¸¶o{׫´fÚ³¾Z+çfòy3]¿ðU>'cœú½§vì•Wœß<.-ÆÔ¬á÷Óy5ÉÙ¿fé™SŽàzù˽góɸÁ~]zÛ°ëö[Ïî«×>ð_¸Ít?cã`Ó~£Æ8c7ØWAV=[1Ì1'ïÓwtššÌõH–ŽŽ Â)£ºÇ°sÀÎô]ó£þ ]­´¼kº®ñ^;ÿ¯È¡ÖùR"YSÿc/©][ Òe‚G~§+un«‰mòŽ ×FC “ûŸl“>?â¼6Ê©ue|(=xìÖíçÈâºÐ!eõÅóó5uƒqjèô2¶RˆZa­Âªd¾ç½Ùjò´wë/wpØ?y}ƒ.ûeÚùÔ–±Þ2ºzÁÂ=mœ¬[Mƒ jß{hÂöõ>‹üÛÌØtÁ5vb¶4œëGëãDXÏ2ì7ZAl½‡žøÐ4 <×4KÜúDMºoV/Ÿ=Ë FfY:á7Ÿ\—ê‹`¥øz{x³/•Ãu†²ýª·":« Þ‹ÝEëÖÎ+×Ô ÆñÒ ô$©²pÀ) š½K«¥÷QM® rÏO7·„c&é!ŒGiÍöïú²ýKm¿¡œ¨"¾~M÷ƒWãPÞi"¹½ìêójƒ²`–Agó•5 ȶ ÆŠ _/xe'=¸«Êt2ÈH ñÓÎcéý飭Oï¹íºûïEýaÝÕ6dí7rŒÓxäæ­Ó%’¿¦®™’ ¼†E4/ ÏÏ¿=ê _fNÍY™!%œ{Ê×8ÏŠ_ŸŸÛ£û¤}¿æÜcœmz4‘H¶Å¼óqÛšç^§íjÚ­€„µm&n¼× á‹eÈ“ ²TØ^ ?¾U6LË[åûý|ý‘q´òãò®ìü!ÆQf9=îz*‘˜·½à1êhì<¨[èÀr-¨Uðñ^°ChK‹‚‰fY"xŒèr1¹ïj;0ëüp}ž×hàç>øú–EÝŒÅ.Ÿ0~°;Y{_<¬ï•³þz òªAíg}YPdfYE9¦€X^ÏŽ‘@TìôÁíMBÈþ«íu»ýä¬}0~œöyÍÎ9ˆè9K¶îE÷CŒ1ÎL÷– ·M$šc&ñY°|ìN?»ñäf¨`cì£s3^eëÞž©ý<´OvÔr[{‡œq\•ÉÖsµü;ÚŸh1Æiëе¼g»Dâ9ò§ˆ¡IY;Ò½Ux`QEÍu©×ÄZ ÇpšÎ œÇÍ×aé>Q{ö¼T2¾æM‘R gS–÷eÏ?šoŒ³âŽÿdùê{/ZÜÌ‚i¦&Ÿ¶Î- ©‰Œ1`mz,|O›`íyZZGm9ß‘íÇç‹:ô*™yÁ¤ã¬ÒëËðúøpªïßêÙødàÝ ×Y`hßq£ÿ’Rú¤Q†g«Ñ°SUmËF—PBŸ#bèå*Œ¤-7W¬=Úu†ìÔµjí C¾ªO9ÆY9Iø„ äAÛnñMoAÏ”›Áz‘ÄuÔÐò>›GçoÒú¶Óžo¥ù*zî¶T[Ÿ|½ž»¥ç“1Žî”¬?MN ‹™ŽSZÝÇHG«-dkg¶y3î³Í +Å s>.äãPÎqäÏ7Þÿ*r}ÕçÃÕ§i‘lÈØ±nº÷-hrz¦kìîÒ׫YËËU]À?döîNý ç^ÎÐl| ¾ÏÆ÷8YW3€°dœUÊ‹Öѽ/^³üK¿]ÇJs@õܪ¯2˜|¨€ôšà¹îD©䎪j5ží9ÃqFÁ.@yãnÀ÷Ÿ8Çžñ9i½àõ§~\ve‰y ÑoÁúÕAf[“ Høý»çÛ\ò„®K‚vy.Ïö÷œ@ÈC +h ÷k6\ËÓv-1˜Öö ;·=€­3Ðúcœ•;·ù:›$¹u[;u Ê]“ǵS7£;÷8f"‰KË—v¶ôcdGàó á·mÚ 6ަ.ðzë#mrËë&¾¨Îºq'Ú>=§. ¶)Íï|”@óÏ[âj’à÷ð^<‰%œŒ½Í »ç¼z^@f^‰nâåàVÊtÏ &kÔÖWœØzßxjé”×ÞSÛÿ4yŽ×«;¾Ò‹ðxQ³ÁÈ›oAòÈo;èGÛýäu¼ ÿùÒ³g’_ÝæÍt”;Á‚sÙ ºûâWõú Êóמwå> ç-é9ƒdŒ£¹½ã‰fÙñçÛ Ýï·>³n!y7¿¶¾ž•.÷[Ú}LÊ 2-ã×O6u\!*GH_p(úeš§·¢%']ÓI߈èû Cr·éú‰¯oã0é©IÁ~"Ì~tº -FØ7lUHŠë‹êÌ~ì ãŽ\ªþ LJ<®¼ó~–â =sëc«ÆÎÕziãhòXᄌ³f¡?[¿½ Ù6–;º’õ×?é~È 6Ç›®M"ígýZâ ‰GßîíÐÓ Šç5M[;õÏ7šëãõ®Ô¿[ÜòÌ~2»×~‘³ûm85«¤«¨Üvò s¨3h¶ë¢Ç‘©‹‡¹¸=ß-Ít©‘¿výôÒÅ ÝæU_i÷g5˜æÆô{cœ‹ï¶ÚGÈö“4Ka#ö6ØO³NsZHº¥ÆÖÔΑò m<…•fwí¾Ä5aAf€v=˜ŸÏæ\׊ûŒŒÓÄÐäÀ¶ÓûIÕqQÃ민 í‡ô85º¤NWÅEfýëni½q<èZþÞm“p8?wËÏÛÐ>\&jÑmöÔÀfÃéC³‚é¼Y†q4Ç5Å“×Ï•³Žº ;îΙê<¶ˆæ,«Q¥»Àż£‹æJI£¾Áö˹_åç:”ïmomr/ñ?y?ëáwÆVÌÖO1Ž¿Ç—7¶%áSõȹ ¶ï¾´3÷+$)6âvrWvÞ(˜D‹k p*^g… ;o7PËWçã$>Ïäã2M`œ¶ÍŠ%½tÍ1ðø½•¯_#¤LèÑéQt¹' aE,”<&×Gl tcçá½ÀgÐ.å¡§ã´}…ó¶ùûš:ÀëÓçë¢Yn1RÁÜî{kDÅ„ÒÕGªyAûG½qÄB^¦¶¨ÑÿíXøü|ìÑî“àDÊÇ<å/Úy9ߟåçV+òZuôï‹…Ó5Çg ÷›Å–ÚtQAh#¿ªÍ…$<ÉëJð oX4x𶑇fºþî­=ÇC×_ݸÏÎc—Š(§ÖB;ÐÔ Æñ\êg{¨V"‘¿¯º¶FXß9½nK|!©S©ÕòÖYÞ ¾»ß©íÇ ò¨—Âü÷¥ûˆ"íø•?Oøzç»kêãÈï®Ü’¸.‘Ô;ô²W¯~*øh!¼HSHƘÈœ§ã QŽiD\JNÖÊŠò'yXˆsšù¹!¾Ž^¥íÃJ=hùÁ8OgO^®›Hœ5/V¨ ;oi‹²¼ØÃî@{ [—NòS>~(öaï+ eçFßo ç^‰BCn„ Õ>4uƒq×ì®íiŸHv„öy|ÆG ë,ñÈ/$Bšï;ïÜyƒdÊü»Ïó†ù}{î¬=ø¹Ò;ósÓÞŠøyizn{({NÓ÷&äçMÛ%÷Æ 7…í´y*˜¯î`gü¸ä„µ?ºÙÒZÖsDB6·’Ÿóº^1TÇLhëªåkóý.¶>úU¿IÆ8'¦Ú†&,:@.îî¸JëuåË›¼-$ÒmúžØ0šß1X±Ü"€´ßP¶hì‚ßϹñýF‡î?ÓsÞj¼n–p¼¡É¢Úþ´ÿóm*¸–{)¹L§ˆ´«¾÷qeÅ(h×µ».±ö&ãÚ´8Uý—ñÚç‡ …­­–„,hø\û{kê£>¤­&¬ˆI ónÚwD~‡?üZ³ZfÇ×;À!ŸV†‰œIÏ:½úŸ5‘pÕàtý ÙºëkßoÛ÷j'«ƒƒÙú&Û—Ã8»'ÄÜÝ'\uhÛ¢›*è³v_ï#?òÂØrp-豪mC‰'ù#£Ì¶L€ÚCœ<íëë/ú6saº‰êÔŸEü9Î÷yéz½ÏbŒCÏÅ“½uÞož}G"gòáMŠˆòU–²Ìí4¯4üýDB÷O½Ø:œ ðóݜϟãüþðù¤¦>0Žzâso¯ˆx²|m›éž© ÕÇטÖE¤]Ç~‡ëŽ@çÄ–ÑúÞDƒ¯'µ'ºÇõ²p`ë§ý´çè:ês}ßdM›~ÐB¶î‹qhýÄeÍÙªfCý~3Å]‹HÜÜ3M;ræçbÈ‹Î!éÎ6¯¡}nð|âão~ÿéþ{ ã¼i5x¼KÊ~²[À¢7Êûño»<ëWDw>Tç¦}†Î ¼àÎ8˧î >ä֮˥ŽöÿežÏó—žCaóŒÓO3ðÞGêvŒ^{£c6<|{¸Ö§¡E¤ç([ŸÚ6ž@Ï%ÿ æšD´¿L‡³.ùÉ÷¬µqøóŒÎËhž©ñú}dóóIè^ò!øç:Ÿg¦‘ýËì†N {å»:÷µ#ô{›Èî«­–oϾ~IÏ„ȼ-3Ü[Òç°Nåûb£¹[Ö7ÜGî=qÌ'vˆbSû‘Î7[¼î^<îT9šŸ³Ì›Ør?bî$àó¼"‹2¿=_Oäëpî[ÜMâ/Z§ÓýcŒSœ4º¯Ë—X2díÑ%£ædƒZ1Þ™YEäÈÁ€øo<Áhýç±Õjû±ñøDàó7ºNf<¿ÔÝ6%¶Ý÷BÄÇOû½ãìú©ÞÁ¯XRdÞØº<îN—ô±ZRD†—Û¦LÑ÷€ùš àB÷K½Î«F?/u-Õ¯R'ýrQùçUW^¼×ŽÇèût¾"Á8G“›ãÈ{95Göôêælp³~h¹a}ñk¼!ÆÄFÙtß?+)@{.`Ýá`͸ð½Ÿ3qiðÜ*?\lÎŽNÝ0ÛúëñÆq´Ó¼‰@zHÅÜÙà(~ÔÑõ×"BëÛ6Šf ˜wÕŸí§¹_'›b“çó``o–¿Ð´RUö}VfûLþ:§!Ç8=­¾ßC¾‡4kúxDËÙÐ i‰OìÞ"²üX#Ï_ÖŒÄú«§O!>í~«+’°}{í{$t¿ÜÍ<¾Óâ™cÇÑÚq¦n0Níy³:›±‡ø ð²ß“ž )XÆ8õ´Ù) ^ö™uj¬Û—ö‚§§Ö1z4Œ½'7øû‹-Ú$¿_V¯Û &Æã¬*/eûƒg{RÓDñ«bä1}È—«Ùà\)f—ÓiŒ³òðj»G°lwzÚÜ'“ÉôñÅ›ÎyA — ú+òݧ±Ðžk ã³rÑe ëCCâ†~utªÜdlû-µR 9˜wÓÏ&/²HNÕÚ—‹Hä«Éõ?ítºŸ:…À¨AKó¾?®Y¦UôÑŽcy¿¦ç³ñÛ/Á8 FjÉ’¢ÉÃkÂD#~–µ^Ûòv±ë÷ÑÜ5z,¤—ÖòžT-€Ðù‰;·b ¼îùûJ|=ŸK¡ïÑõ1Æ©ÚæÚ‘O£IFƒÛåÃßdÃ/2Ë«Q÷‹ˆSó§=‹æ{},€˜> YŸ¿Ë‡Ýw çrµ}‡÷k~ÞŸ®7Ó8Œ3êä«®C'ì"—êLyÞ^'95è§|WDž¾Û_§úEo¨%Ï;m¬?ùØxh¸´òxp?ÔáÜë ¬Þ½µë0ôßÁ.×Ï&™Î?dzI‡}^¥pßt…µÌ?LL)J¥'ÌttâP†ÿÜBÁ½¢_SÔ_`Έ™g°à…ÂøÖœWhP—²*z5%3OÆåüšB*0®ŒÉõG>u!ŒÉeö>ußð­ã*0¹a!xgræŒà®b^•íú=¿&{Ƹ6®ÀœáÞè¦ßñj*cvqßx£ Ì—o|…ÕŒq­`Œëænó^Mâ?arÆõßaÎüè?z£LçŸÓ ØçQ ÷“2eˆ‰¢'¬©á½7ú”éúï2¹t¾a]Ëÿ7Bð±S Œ0áeŒåÊY\†u©ŸzEεÀ0¬O=Õ“ëZVçšÄ¼ÕÿÈ?JÆØ8à!%þ†åª¨ÀÈQ¶ þê‚çF¾ÂjæéùW™…ßã^»0ž«in÷6ûóºœùL)¾ñ¸’o|>KÏ5‰ñ\eÌWØþ˜×6Âãâ<×¿ÃøÑôF™Î?§7²ÿ6À„”¢TºÂ>æ3Ê“3D_XG¥<ׇ&p&Ì0™£XB ¬BåŸp& §û{*Ç•óÁÄŒS¨B‰±âkBÌX…ëZ`ê(*°\Îõ÷üÖuOç[¿ué7WÁo]à\G±‚8…‚ßžÀžà^ÇËU`êÄ¡ ±ÐBPj” œe„E'C•¢ì±ø(#,@ªe…˜„2fžëeŒ'–\ÁóXðÞ“`¦¢L±H#Qå( ãr¿usmjFý9‹"ò¦÷¿+eï9׈y½§2ÖuDŽ«àlŠ:UŽ’`¡¤¢Ì°X"˜÷»à=‡2Ä A©Q6ß0\“*pyT-¨¼Ôøw…à\ÊüHÿ*cñ{¬k 㸚UàQpÿc‹ï°®…‚õeœžŠþÇÂ÷ŸÒ²î”ãšÌ8®ßóµÿÞç¸þÅÞø£7Êtþ9½Ñ‡2Ä„ ÑÎhá÷‰©@é g0Ÿÿødç“YŽ2`,FUïó.Œ‡±e ŸÄ¸²œOfÃ8Œj” ‚‚ c1 ŒkՓij«Là[ÏÞ€qz¾õ…a¼YW&g¾ðßZà0S£Ü˜22¸O³ÀžX= ”š UŠ²Ç‚KBcÑE ÊP.X|I(c,ÀTÊ 1eʼáË¿,µ‚g³Pœ¾(%Ê ‹4Šª/ã0r_xomfFý›97#êVqʨ-cü Á¿Ù ;U†rÁOF™2çïñÌâÏìïð3~ôƽQ¦óÏé¦ì÷)¾WLÊ$]áŒ(þ¨2”‹¾p扲jÿ]–™aõßÛ»Qñ¸.˜ìÉ(S ĵœaf̼é+r·n†1ó§A¦X‘8µ©(3,(V$¾(%Ê‹%’yÖÛ`Ñ(PFƒUвÿ†Q›Ì¸A‚½ºõ°1þ›!7¡œZ‹¿ÁuüWln¡}§Ö¢7CŠR¡Ä(ßñ[V·Àw4À‚•2ž® *™q3¤¨d”©ÀB•w§œÚTÆ©¸å(Ép¼]þ„cÆ9µ‡›!ä¿ðóŸØy?¾ž¿Úÿþ¬ïñ^ÇûÛÿžö¯úïaB¯ø¶w}¯oý«~õw{•ðÌQ÷OWx÷s eI’¤'œÍÅØ¨2ýŸU¦B‰1±âP†Œ·(0f¿ÇÍ0f¬Å2” &_2cÌ ³Ra¿¸å* ŸdÆ”lHyÙLÈT”&eKLCÆíQ£l0A(#LRãÉ ll($ª/* “Õ“5U†rÁ¤MF™bâF¢ÊQLàd”)&q$ª%ÁdNE™aBGUà” ¼ Ln9Ê\вÀ$—£ °§HÑBà_3VFEþŽiÊ(øÉ(S쑨r” $µ×¿æ’)“쯲1~Œ§~Œ§d:ÿœñ”‹W&|oºÂûFxÿ11#Qåz¹{Ì}ʆýwYdFXØö˜ÔI(cLìTÊ<eŠI‰*GI0ÙSQf˜ðQ,é9ƒÌ“_ú [2Åba¼3,Ѝ \X%Ê DŽ2À"‘¢T(1KªeE“„2ÆÂ‰@•¡\¾a¦2 Uв0Æ¿£T(1Wœ åŠÿ“ñ_q²&£”qaYÊ 3¥FÙ0.ã·¼lÊPà22N!®•Š2ÅA¥¢Ì°£X1û2™À…b…íûìlÉŸpÈ8Ö@2Ê›@$ª%ÁfŠ2ÆÅš‚/Ð|~þS{ã·}Ñ\ç§~Û¿íµÿýoö¾zß¾÷$á½XŒ‹*C¹è ï™á=ÆÄ‹D•ëÿû35ÊU2büÅR”=&mÊ7U†rÁNF™2öb9J‚ÉœZo&0fv™pv“; e/œWið;_6Š%º/J‰²À„—£ 0é}QÉŒµ(C•¢ì±’PÆX8³eŒ{-p ñs¡ °8¤(¥ e,&£L±P"Qå( L*Ê ‹&ŠN*Ê ‹'Š/J‰²ÀB’3晥B‰±¨âP†XX!(JŒ‡2Ä" alE1ö39Ê‹.RÏ1þ™U.œMÁ"LE™1þ™ÀŸ•`?KE™aaF±‹/J‰2Ã^ñçÏcG ÊP.XÌÉ(S,èHT9J‚…ŠN{Gþïýïéü³úž »^¹ð½`R¦¢Ì01£Xrú¢”ú”=+G`¢JQªÊÂ;X‡(CLÚ”eƒÉ«@aËP¥({Lä$”q†¶ &u2Ê;UŽ’`‚§¢Ì0É£X¢û¢”( Lx9cÎJQ*”&È7|íR”ƒ ¥FY`QÈ+pgU(1HÊ‹$¥FÙ`±ÈQe(,šd”)N¤À{DI¾aÎ*QÆXL¨2”ØÿŽR£l°¸¬Àlþò_1¸dãÎÚ`Q*PFX˜2T)ãp'ý‡; e$ð QJ”®/J‰2ëF™ŒJ”²eÀ¸³*Æ•£ °°¥ÀæöE%£L±Ø#Qå( }*ʬwV‚ e†M Š5_Æf´À† G`SÍiáç?µ7V\Cû»}ð¯ô@Þû*ö¼ÿë>÷½uµÿ«ž&|GɽÂDŠD•ë > ˜‡(3Lª(–X¾(%Ê¢²ðžæ&™¥B‰1ÙâP†˜p!(5ÊO2Âä“¡JQö˜„I(cLÄTÊ2eŠI‰*GI09SQf˜ Q,I}«Ö“Uζ\8KRr³…¤õmð;£VΘÙR” %ÆDŽCb2KQƘШ2” &v2Ê“;²eÖº0Nv’0†ÃD7ÄDöA1ÙÍ0Ù£XÂû¢”( L|9ʓߥDY`ÈQXR” %Æ‚ˆCbQ„ Ô(,Ê D†R£l°P(#,ªe#ì}b?*7£Åã‹R2N­œ .|QJ”ö 9Ê LŠR¡,°Ð"Qeš>\2Ê‹.U^UkЉ*GI°SQfXŒQ¬ }E¿³b£à÷÷|þS{ÑqÚoœöO£¹°ÿ_HF_”e¡'x­`bbJQ*}Á[ëeˆIRYxWï&«e„ +C•¢ì1q“PƘ¼¨2” &q2Ê´úï¬m &t*Ê “:Š%¶/J‰²À—£ 0É¥(JŒÉ‡2Ä„A©Q˜ø²oøÛe( ,„T)JŒ‡2Ä¢A©Q6X ”ˆ UвÇB‰C•£$X0©(3,š(V8¾(%Ê (¥B™b!E¢ÊQ6ÆøwT)Ê^Øw@çáPr”™¥B‰±ØâP†Xp!(5Êæ;¬n#,Dªe™„2Æ¢Œ@•1fwò0»“QÆÂ>J…2Æ¢•¢,°p#P*” 8eˆE‚R£ÄXÌq(C,è?àwKÿ„ÅŠÞ¥DY`ñËQŒ­B‰±Ä¡ ±„Íw–†ÿq=ñ¿;6û'îqþQ?ûwÇbÿ“=Køœ©(3L”(–,¾(%ÊB_ðºÁ<ÁÄ‘¢T•oÌ3”!&QJ²ÁdR Œ0¡d¨R”=&VÊ“+U†rÁ$KF™b¢E¢ÊQL¸T”&]K<_”e (G`JQ*”“1îgšŒ¾Â5LHLH)J…cbÆ¡ 19CPj” &©e„‰jЉ‰*GI0aSQf˜´Q,q]0q“…÷]1qeÂZ&¯&¯e€ ,E©PbLä8”!&³¥B‰1©ãP†˜Ø!(5Ê\2Â$—¡JQö˜ìI(cLøT)Ê? eŒ=&e/¬‘±Aƒ¥B‰±ŸÄ¡ °ŸHQ*”ûIÊ &DØïÎÛ ëüX8©(3,ž(V@T*Ê )Š“/J‰²À¢’£ °°¤(J Ìñ÷¾w¶Wò'=ì{ï=HÙ“éT@û–ËOþsº=èȪÜkl –ï$½êé¸4@¯›#ui\-÷ úùt{„û~9q>ìQ±7Œoâi«úHtÊ+w×µ*“ åj õ"«JM­&0ÿ$æ_ŽqöÌ¿;&朜~rļ¥QêVÛÒ¯Å=²%wº]͇^@ý)'’g_í>“ Ú’é5{ôœ Á£¯Li3c–Ö?’ûó<0 ½Û‰!@y‚Ô—'ã¨Âï6¨#'¬y‹x¹¡Ò³{$Sc_#gèuyMùrúÒæ¶þ0™ñRF3¿çÑÚ8ÂõÔx½>VU^<~±ô­ÿô²guªýR ÷È/'òQ,u‡E O/3é3™Po?­\E}ª÷ÅP¢¯·}èVÒØ!§É\¯˜žä4G5ì‰ÍmUl¡ë Ïo“¨5VÙ1§ö&æ‹98¿V¸Œ1^çneÛjd›IÂi;Ça9p§i¹wŸÑ·!kA·cS¶†Á‹Ôé91ˆñÀ`z—Ç ùŽ@ýÄGÅ ãõªzžÛy<$Šìí¢ž°dUüöæÔÄ8ñmØ^Å©…kµQ°6]w¨êøtòBa?eË,ˆûÿîù&'æÿ‚>Kˆ®|þžÖ'‡úض§¾ÂõŸŠ·¿(ÝHl[§_5ݘã‡l>Óå6x,4¯3x$AÚÖà Ö׿ôÎq¥¾Ã™?í=ç0>*û4Ž ãnÙõJדùcÎÏ·‹Çû^Ü ’¼ÉmHûµÍü9«FÃ/ÝDׯ `~ò>Ì—r0óE´f|b­Oåýd~'ÔçMŽq¼býàIR« @Ê0h»n`‹ª·¡Äóµ,4u \¸yW]݇pÆÑEϲh[æ+nË|Cžj¹<Ü_ºÃ˜Þ äÒzIÆ8Oæ×WŸJ[AâSbuŒnäÀ¸I/>¿·N-›rñ°#Ô¢'G‘m#†èô^á 93§ öw¡*F92©w¿s™Ò‚.»7ßMýªÕgq+›Ýªôò¾ÚÍ•ƒ3r Ž›ŸÑæÜ[@9Î̇ҚP?&7 œ¯¾Ì‡­‡Ö_ˆúÕ–‹8²"?ZÇà¾8æâ¢¨aû–W)wWtÍÉç “éþçoÁÛû»o¿ïÊëÔ®a<×4Ý›ùvÓú¬ÿ?öάÉlë÷`¬Q,±ŒÆ†Ø±!¶¬P¤ ªQ@bCt,¨3бbÇdžb}£ÄN$–(*ØcÇ~×ÎÞ;Þ™¹ç|Ϲ÷›ï>ú<ÿgæä8ïJYk½»½ÿõÇoä墟–ô€&´Ði}`œ½_®o/™-œ«Ô¦ðN¬~ð¤óç×AÖ,ðèûà 81½Ó9*ŒÓ:£Õ«FÖoºg§/€yƒò×¶¿ôõÁðµ!`(ÊÊêÜ æv©?ÝÆ…ñ"¿yo¯¦Ö÷íÇ|Õ(gWq¨ÿ•·ÐÎ`­…Ž»—w©}^üL¡6\-ÿ´w´Ðä¸êtxÿaF¿^Ü‘s“8çú%Ò¾£Ã8v³Ût›ªj)|ý5mNù¦Z8þH·ëóç\æ‡è3ÏTíã8á9¹í™ ƒm[‰‘›P®«3p>õ³z+å~ƒ¥}ËM,ŠdÔ·Ï tíZ^r³ÖB@%û§/‹rrŽ| ˜Øø×'x½m»¡ÆµPy¬‹eÕ"wà|ê›öQÊý/îÜ2¢Ÿ' ïµýùÅ”S-Á8”;âM—&¿lÕF ·šŽp©›› ¡'¾F]ò‚ÃËÏx½zrZ>gñÙµ¹c»¶Ö·ô©ñüjÁèaåÊ…3¿x™‘ÿÀýe9Žúr92ß=æ+ŽqÆ}²­u¹_àýâ–¡´ÐtäÀÎ5cs¡ÿ·-±_²üŽʽùôõÊ‚0à\?Ú/1ï–XY/IyiôËl2zὃ‘«b¨ŒãhH¬ÒðåÐîøy~}©êÚ14j™5øµîÊ!p¬ÐsÚ‚þŒ§ œ[ã´£è–Bn”wûƘoü¾]ºNu'éÁÊ'£»Ìâêo¯…}{-¸ê˜ «VðõyãóEÔÇ=TÝëì´I¶1úmSž³^Êù7Uïzy90žE Z?•‹d†N<¹{É©¦·* +vj• ë×ÖY§ZgSy¾?J þ^CÖQgcÞ}Ï'¦¼6{æ׌ÖÆÑô!v‹€Œ~-•Zº›Rä[-^Þèi—¿NÁ8? â©OG.fôäüîûÿ2²0îHÉ}£,õ lJëãÜ{èÛÙÄw)DÍ›«I©…äM _†¼Ë=FRwúÃ÷ò¦ÂAöáĤœ9’pœoê ”S^(åî;oŸR®òÛ74Žãĵ3$¤ãh¦ÑD-lkGŒFs`шó©fxå¶^n,©fg¥Ñ¯‘sØ9?•ófx¿(íÓ‹qÜú=xf¾c%PŽ£Ö9»}­›ö‹ß¿iÖƒñ9£ÊÛ Mï¶Ã5Ý€ûËq^žß¼”æ´¨0åí®†¸+ܪÖÂÂzŸýÓr€Ðºº@905Šq·#wXIïœ÷ÖTšT(ÈLŒ÷Ò|[5Æ)HìovpãZX9½ýþÀ1ZØ2Ôú–lj8¯^é²lj_¨ò´ÓÒ¬QQ y[vŸ4šq˜‚Y¾ …ò›w‡õëkÁüì«Cn@ù€– öûPßNƹVr:ÅwÛ­ùzpªÌ+š4S$åÀº*µ–Ê9C5o}íqÛ¢„­ç_Œþxh ,ñßbõ7%L+y­E4ó»3¿ÿoÒNнkøŽ@ùÛŒ7\¥H6ÉãÖÊ ¬2{Ôz\œ,R:Í{¾%êµ kzǽ|©}~ÆÀK£…ì·NysšŒbü?æÛçoôÕ¼¸JÃw׫å¸BG¹·ÒÔƒöQ Æé2jÅÛu¿m†®¯&k|–hÁàÓº"rÛÍ ™XÑ Ö)ˆAâ¸QþÔàŸÐ[ÏH{T–}Uù?ù|Œòجiý`œéý6ݯ¾…õE-Xý²íÞ‘9ðÁõ…z›¹œ>iò©8'\¸u"#pGˆ‘ËÀ}9Ÿs¡¼"fytç\æ¾ Ä8ÚK7>½*| ¸°x‘FxÄÛ:"æeYGYépë ãçÔUCÊ/Tç]sH:?y(¥üú‡RÚ—ÊøÆbåýpÏvúmàJ0“kµ0ÄÆRe*Ïs÷†ŠüŸáa…ÑãJ¶úí ó!FŽ2÷eþ~žÕiê‰ ìñÚiý`œ/ªÞÖUvÀ¦€b÷ »´°¹¡¼å±Î9𭡤Ïcß!œ7`ä/R~†'ãgÈóEþy¸¿ni¾µãøöJÍYÕ3†J%kã÷v8bÂIËÝb¶FØÀï7®‚“m|Û½CÁ2«Á¬þæ}˜®½ñ÷áã ÒüN^ÿ¾Îf-v‚r’nšZ —SvÎxø:F¶Øg¾åk0X†¤f· wÆÛèç¡P~åË]:ôî3ϯ¯“S9û½;­“ªE²NÉ¢ûªÙ;aËE›ç•2µð.§oxãœlàñßW#pwa¿ù×£Ë~ €Í8¥7èß/¸±ÌϸŸ¹¡ðz·ã+öOÆë]Z—z¤@ 'òkTz¯gs¾wï•AЇLw÷œ[N(·Ö¼«ãì~ÒÞŽÐ.H}Sl¼oñßæ]7‘aœŸˆ»¨Ç.ð74&-¬)ÔpM6<]׷ǽ‚Á@¿g_sÞèûöÊmtΓækî£^š_¦Ä8ßN}•$ß >-‚-¶žÓBõªš³G¦gCGÿŽ å ä¼eAÓ¸à|åiý â€¦’¯ö1®;q{~?¡ýžòjb1Ž^Xù4ß|¼³úËñl-öòªUkx6¬™·ãà¬m~pñΩ-i³#…߬äçšÍ bx?ivd­à“ æÚ4™í†Û{há@—9WG> ÍÞ™ââã< ïËé‚=~ïG´Ðù„ó…gëZgà5· ƒwÃÈ*×úiÒ´ÿÊî@»>ÙàXÓù ( ¹œ-8Oùôs¦jKÌ̦†A½WæQ[G…@P‹-Ç*¬ªÅúpe¯‰ nîmä©êã$´¨½ºÀTZ¸»§ýÝ Í²á•ÅÉ ×Á®_{6î"näÌþ|jôv‡Þ¡¬î@E–[,뀮>1¶5›1Iš¨1ð*9kä¡”çjR­H¶gÏèùçûîÊMÕÂdíªZËM³!jm\úÇz¡°Ù ,>nâ¾ÆÔ§»Ÿ‘ÇE?§”öe—àõ øHË}p¨Ò‹Ýã6kaã1>žYðZùpÕÏCúÌög<¾ à\hÊA—9é_7Röq{+¥þÂÎ@Çe¬n0Nâüo,¯îƒÅ}¦vðYŽ¿‹½]Rľ,¨ò©Ýœwª>¯½ˆ3{0ó1vÊ)qÎåãXÊqõdœ'Ê}Pbœ€Ý Ʀ õBfãýªÛaÓüØ,¨ñ¤óæç‰Œ7Pà¿¿´?üóÚŠžFî´»N‚3†—FnMÞ0ƉµNèTqd4ÐëÓÅYZ趨»ã ß,(IÞeëÖ'œ­+„ ”_¡€““w9FÍíÅ8uNÀù[œ3GûAOæ³Mç³*ŒCùåI°ÖÑ»JKüÞæj<Ni—3V |($«Þ§‰LF ·2Vl˜&hœ_òß™']÷Ÿ\û*ﱑÿA}œÇãØèoNM¹Ÿt¼­…û›_D…[dAÖìeQú6Jè1-±khÄaÆ3å9©©/ã%ô€©[üNçÉ€ó·xëõs ‹;Ÿ(OG‡×ïz”¯tÞï«Üƒã¼tݸJñO2!ëPÈM¿€|l£˜#7‡ |¼Ã¹KM÷›-¿Wèhä£óߥäIOà\oC½TÇïË0Ð9îú¹]ÛDkAìóÑçMf&ÜRmU—Så+…Šê˜JÕ¾t ßdok4sƒÔš„ôã+ô­·U=óÎè7Îó¢´ß¸ãP~ÈØ=dÚŽ qþKõy&'2aD‹“vþ0¬ŠùÞy}¾Aç'!l=ÃßèÓÏý͇ïù²áYf?púmUï«èøX†qæ<šZíÌØ°dtýèUË´prM;·[3áö¤ªK¾´ógÜ.OóCù:ç/r^&õ3×ÇEÔg›Í_0Îî'rO·ëàâü»MpÜÚ.5jqëÅ™pv¦ÊuýñÌ—Z.”w 3C{ØÙ±_¯n«{]WêeŒÃ×· u‚×O°{‚©ƒýå[µøÆ2ÁoËÀò+æ¿3‰[ûË|Î+Q%ŽRø r2Î u€×™©XxüúAÆÑBË9‘aZE&ÌòÜà¢ùÄ|Îý„“æ>+úò¾1Ž|3½¾+ȧÌxA×›c^¸cì!È ÕÛ<[§…7q…•{dÂ˽ƒb*áÝA²£N¿ï¹_ÚŸÝÇGAݨ®îS"|P>n œ§2÷qÆ1`%†N+,ŸµÙª2Ê×Í“rø¶,CÁåþ‰LJ2ÿn¸?÷Žçѧr6~ò>Nàÿ¤| §2Ü1“E2²ú™wú0<¬˜ï²cŸ „Ý °i¼âaß›¡Ððôœ …·BB_=¼Ä8¿ŒûÜò¯3¸mA£"Û# ¼aýæ-:_ÕKÐØ¬¶*Ð" ªEÛ'· &ðû/çoúǾ°³Þ‰æ-^§Èß|÷ÙG€P&ÖŸÖ‚gÏØ®ÞÛ2 0xúhžPI«½g¬íhÎO}ÙûP²ÏbäÇò¯·è¹dâöŒ#páœkat=è|66¬}NÔ‰¹uÉr°(Z œA/øPN?`g(›'³u–ûF^,ýþèzx,^¿‹íøC O†q§œŽ8¦…©6Xu’{ïÏY¥«FSÌ{?J ã'àë÷”3n gU{ó¶é})·sî]o¡qT§`Ií¥!uŽ‚âži¸þN'‡f­”fÀØVç}úǒýfaçr>çpn0çúðõ"Ε6ä7Æù´¼ä¦Å»£0üóÙ¶nµ°£cÏ·še@Ø +w„b\'s9×s4y眸×Îö?0BÆæt}_‡q–]U§_Z›ûŸ+F-Ÿ¯aû©Ió«dÀ]Ǻ7hèöU,ÄA·=ŒóÇÇÈå÷[¾nÈyÏ¥yË&¢"YÇ­©Žb8¯*%|¨>M Žë@ ú&š[=ûì™ÉßPyn”:ÚÎÎUœ-PÇž>êúYʹ¯¥ïŒsàšÊáÐB¡Ù†´=W®AJLvÜ[/èÝ¢W¶úƒðÓ$w³'ß<Øú~´\;³ò°r`™ÿ¾wÁòOÒ­góâgy—á^Ê0Î/ñì8ŒlåîÐl´bßMûz}ç5¨:dÀµº#}Øz€•`Âþpî6åEø³õ<(š=!®Å7i ™fN—]oaû"çmøðÇ€ô͇… ÐBæ½N‰Úi×`I·æ³nöUÀý÷¶_n,ñ†6íYaqwçAåöO¾y÷Çøç¡ëneúO,Æiü¥cÞïO2n(wé$×kg#tý’ìÖÈ dëR¾^ÇÇùtÝð‘3O¹Úά~hÆy™’X·¦æ$Øö±¶îÛV Dz~¹cav ÌÃ3Ëá!l~$Än{Wæj+ðuCÎYáýš#8/Kñõ—€$_¶/‚q‚Ô—ª6 ?×Â[€–Ý?®ÂÜž{¢/¹„Âù¶¢>­—8O}L¨k«¨nl¿×Ö8îâ|/¾ŽMß]w×aœþaSÛŸ:w¾vÚ[ ¦¹÷¯¾œx.ܘc“¾#ž˜ý sOÍ÷D+z²~ÔÛ8.ÚТܔ¹u^ûÝ—¤ŸÇ¤f‘lüSU­¼N§á!YF®ƒ÷á©_–v¹ 1Ç »ã<~”Yów*…@÷Ó C“îùŽ q»·3îopt3î‹TþsÓOSЭ¯¹íÔ—²|oŒsrÿÅ9…Óp°ÚÍ=€i£ëû¯½¿#Æ6}¼æX0®»õL à÷`¶þôûªÂæIŸ¤ÅÛBƸx”¹OÉ0N½p[LiȬWt±Îm¶èÜØþjâìì™æ¿@÷=0¾àp¨×ž·†ƒfeõNÇö|‘ÎͺrøÐF¶?ã^†ï¥Ä8á†6†Â‚m®§ À¤f;Å›„+8è÷C—›ùÂÝiêòåÍÉž$3½>ާëƒ|ˆÞÞHg»ÎmpMÏãCcçãœéÑÜ}ê,n÷ü6ñÆþØXk¤Gá¸+PtmtŒØV`óÁùðO>“»×[xCàõögÝš`{_Ê[+€Ï•nî3à dM;rxè&/¶k!оì p>ç·ê¯ÑéÀ‡[5ÜwËê¶|L¼Îk`¢l{~Š˜9<Å›ý.:)ÝçjÏöqÔÄÑ©ÖæC€¯_ò¯÷eîo•ZǨÁ7ê÷Uý  Í„¯RÊ_ÇÏWæ mêÇÖk ´_Tµ¦aå1ЫÙaäæñõxZïžl}…Ö•I-ÎÑRÃó'jüfñó;ù®v¸~ä“>t²T0no#vÿk"ðßåûyWmwLó×éx_¦ùŽqÕIÔ¨ÁM´/Ö¡O4è].êòæËôëÜ%•ù~„À8k_/¤¯ÿÇ×x=ψ“{ߨ¡—a±zV3+iqdkâ%Ûóuí# ü¾À9²†¼Åëø‡YŒ {ªöU­ðªéQð¡ùe0P…ìC_­‹@÷i{°}–Àç‘|ÝŽ¯G³u6Ï£}%ã8O~ø é“j6Z=Ö§|ÌËðNM¿y &‰>,•…r½ñýÒý6 ãX_àç¥øü´cÔÓ“ÊðvT§j`ã‘Ε΀H6²ÕûÐgÎÑЂe—ØzM(ß_e÷±ž‚êÓ¸ˆ«–=h±â´Ì‡s¨é8#Áë7º²te0^ÿõØÑ‰â¢pëäå*UÜ.Á”kv åýG`çzì}>TÔ‹ös¼NâýõùŽø}Ô¾œ0³ÛÕ°µQ+]çr— ¸Ù¬ [†0þ’³@ïŸ5„Õ,«yëŒëƒ†üµ,’µêGN¨Áж÷ÝïÇOP_„¥7ß¶¿ús0°ùµÀ×'ÅÍvì/9è¾Þ8ã>ÿ>ç¦YÚ¯ŸG?¯¯ÿtûŠ<÷b54ªRyVçE7àièý‰“g_„yޱ?¹-„6;ú´,:×_àë<ÆƟ0÷;ãÈ fpžPîdßÏwjö-sáõ݇l“½SCx÷.3£oÀeËÓ{wy^dœ§Áƾɇeƒ­×¬/pcçÌ×7ä3^oûQrê \·Õþ>øXwý¶±zƒ‹°Â1÷ƒé APµgÓñ-=„y‰g7ݽÒS û§.Fî<ÿ>èýº/Í_¼îe 2±?½µ[ºßt¹‹ÛLnkZtæOi²ç"?oã(Ð}x¡b§Œ#U.À¹éüº<¿øzEéõÆ™r¿F¥SÏ€ÓÖíA]nÀô©Å—ç¸kúíJ^§ `çN,‰^mrv^¨À×Ëkè¼=¾n_š?¯Æë÷¬í§o>·±ëoªú6‚.°ûP0ÐõGˆlžçÙz¸ÀÎEñuvã¾í¿ŒÛˆ×uó<‘qü ´)·þ¶æS>´yªÖù”œºKé a±N–Ö¡^À¸ ?¯Á¿>OáûL|]}æžþÁAtŸÁ¤v‘Ì©öòîêËøýŒì»®ðf>4‘ì¼Öä~:DI,ïÀå¡l=¨?û^•?”Mns›zå7>òùÑΛNí¯ì••]·Á8‰Ý·<Ü}ÿ TÞ´wÖ¬Sù0xh¯;¥CßUcKÜGçu¤[×z3P œ¾.ÀÿI¹«÷Œó¼ò‡–>/êZæ¾ Ã8çO}ÀÊ> d·oÚ|88þÍ>›.éP«çÆ9¯ƒ#Ø<·_ŸX?6ö=ú¿ ¥tø-[_èZfü¨Ä8Ÿ?-¬bæp¾V©`Úy|>´ÜÑðŠíÍól_(;ŸáÂøÜÞ¦G-Ó:ñò·Ÿª¼ÁzPµòÃM€Ÿ³£üf9ðû”¡N0Îéü©/ûN= [í\~úi`>ä΃œó‹ÎÃÕü»ü»…Bújù™»½Ø÷í-ðyJwÛ^Þ¿ dùõÑxNˆ¿:Þ¤œ8ƹ3êç°)gÏ‚_=}çTç|hÑpãk÷ó8=°ÿãº?×G÷ïØy¾¾Æó!mT´v%Rsi¥¶ý8íƒlß ãÞV½T,Äj̇¢ˆ íß[ž‡Ä‰wCUi 8õàñy«^¡µ^ïÙê'Ðs+}[Ù9È·ÒÂΖÚ'¾‘rî2寲}gŒ6- ßb*œ»càÅÎù­©q'ûa´øiNäi_Æ•LÈÿ%0@`ûi¼¿³ùJ‰ñ|"ý{ýØ|Šîã˜Ô)’Õ°^7nΤTH‘wªN>Ô.AwEHƒáoW{¶×Øþœ@yºJß9/ŽŸ§¡û›o¤tŸ§?[÷fãŒS®õ‘SO¤Â·É l\?æA§a»*®KƒOÓT°y³Ïï%Lî“P»É&¥ñþÁy®œÛl%R׋ֽ²ýIvnŽí;c‹è)õ^œOB+÷Å8_Ö<<ûlBš‘/H9É=Á³Cšºçâ`ƽµgçÁØ—9Ÿ’sË|oJŒ3õ÷'gk\M…Ì¢- SLòÁïò{‡£žiì£ãÌûÀöœ–…â C>nãû¨|„÷ÿq_­j7ÓÉÊœsˆÅ8+j+L|*c\ÃMÓ Cˆ£uMLfžƒo­ ’LGpÞ©°÷Óy‡ñæ=Gé â§sG/èÜϘg†eÖ•a~ÓkRüøúí×±§rd³Q1S4ð.p»Õ™†y mñÖ·Ï€s0«ø[•ã?sþµ·@óa°@ûg/ã<œïðßékÂõíN9—YÇRaœã[Gœ°8­wò„(^¥zG=m~¶­R¿wýƒ‹l8.ÛÖO ýÈ:Þù˜t;±¯q‘ç5]¿‚âr›„Ðó£jŒó,knj–Z¹Í~O8iš^ÕT³¥_RaPn÷Üo·½a¿´ñÇçþ¬Ê‹¨­£–`û APä1}݈¬Z°QìZiÌ×:ì>? Ø:­ŒCNS[&k`ftÅÚ]Í–:ä¥2^«ÔÝZÁöCal^ê%\”×ñyÔj´ÆÑ¤ËÂ(¶^Û¶µš8m] è¼/Å®M÷±pÜ­j¹¶óh¾™Ô+’…׿§¨‡õ¿(áFQ§‹×aåsŸûö¦Bz¹KÉ1?ùƒÏáqmÚú m-§íÜÔÝOH:ïä~h²Ÿgyë=?­›QƹgÔ¶ŽKëT‚qLšœœ8¦G{þÂæ˜]Ù7µm¨@ç3Þì|¤PÎ7]_‰ÅëU?ñBÜø“ÚéëÜšâyDÚVKŸ< ¶ w×®:Ê8Ð.ðK(¹ƒ„ ³«Þ¿ñ ` ¼í@v^ǰõøB)?ÇÉç·ô9":ïWaœØ—U|l•ªfK«ö¸_M%v1Ñg¡y›¿nmÆ÷W„™÷Î'œX¥ø<‘î—8]¯,”òsœ|ý©ÌùtŒ“ЙÓÀö?]¥wÓÔk-Î\óÛ¹db(<ì7t¼ÌAú’swÚòucC=àõÈhC<> ¯h×jó›\´n–Úæö­µ·-8¡¶®&øu6ñ[è/ð}ºžÚÝxÔ÷â"™ï¸þO7ÿššÝä F. JŒŸ»oÓ¨?ÏéÉ«÷¬ÿ·†ƒ¯.)ºú |‡ïÓó}#¾ÎDÇt=A‚ׯ i7ÌNƒ‘ù/~›°+Ü?|kÕmÄh&­²¡ó2Ÿ8“%6Uãü„V[»UÚ :Î+µîÁÖs~mzÿä·%ŽeÖ‡eg}ÿƒ¹V¤AÇ®y÷nË…€ÂÓ½ÏÀ•ƒ`RÕÏx®ïæÌmÇcWø:ßG4ž+eóT~žžö=:®SbœÍsªîü=íWåB³Ü]í3^aÿË7Ûøz¢ˆ=d쯜ÓͯÏÏÏÓsÑØwɶ¾ˆ·0NÀŠ’å q¼XwÆÓ3rÁtØÕMZ½:¾\z{F?/àçîY?èxÒÝø߇àûRüíûtÞªÂ8U&7qù9 2ÖÍ÷äÂóÞ« _UC­ÈÇ,ùþíGi…B]V§¢AŒoíe|.„Î[ßJù¼…î³ÉÙý–kÂ8³:/²ª–ï{’P.ÄšZ÷În5Øä„Ì z³}a~ùâ÷_Ö ø:GO‰Å§‰µìçÁø¹~N‹>÷Cû‘ãèNÚ½Ì]œ Õ¹0ïFôå5ôi˜èðj¶/;_ÔعƱ·~Q‰ŸG’]Ð59/:DŽ\|ÊZ ²ÌsOþ\Œ™± ü¹°cõ£ÔEóìλ+ðï—}ŽþeÖÉe‡®Ï§±>™ùmÎÜ‹~(@âˆV/âö°sÜî0ÄuÞ<›iƒ…o5¿íŸèk|Ω©UïÏñµ*}NÃÄx>œÿÿ†úÁ8ÛÜZ¿[–%¯n°9^;Z±ßZ$Ÿ0+>ÈΫɀ®Kø † æ`読ƒ— `ç€?Jý,7Åú“”?EŸ§¢Ÿ'ãÐ}Ë4xÙÎÞY6'ÚÙÛ|1غ\ ;?R“7ñö¹m9ñ\%ßo§ë%¯¤¼-8Ûa~ã:Raœ/ù-àö÷…ù­bC‡æ@¥„~U–ž†+_|û¸(€ïÏ ÝRö¶ïÝÑO çcö 3?ßnªƒ1=·ÌÆWxÝG¿aXÜj9Öß)òæÎûÕê4 ¬ñZ&¸bç˺ t)í['aÏýáu¼ÖÀÒP²Ð•m^«o> îyî"ëåÏÏ™ t¾*ðzãó]þ¼¤!ïÉΤ½}ññ2Ž/«¬ö˯šçže$‰'œ‚ê.Ÿ;þòÈ›ÏOÙyžpál=+ŸŠ[Bù8Ãx.‚Ÿwæû@U/\8·w&í³Œ³wT“W«vk`Ä›Á}žgC f‰iÇSÐ*åËj‘XΟ§è88Bàó}º¿èü¼6?×CÏ1Ù–È0N|ve‰†Ɇ¤KýÝ}yÒø¼ÛO®©6OŸ¹ ܸÏÂ÷/yC~ãõê½¹-.«!ïý[X%fCÅ>ó[»= cÉmFÔ^º/+ú(õhß 5®ÿðù0ï?ü>K÷—Ù~^ßäáàÃ&£q¾bVþÚ©yÙàµé7?ëI'AuØêòˆh/pYzd®÷T/>¿(Ï?ìm~ßœW]>prwvÿmÌk>Ÿ.ý¼ ãø»~p;2@ôœ`6<«vÚfM›“ÐstÞJ]c?¾Ž!ŽE=Q´÷3>oKÇÛ¤4^KùóIô9zÿQc:ÎÕ€NûfxgƒÝúv]=ÁžOU[gè|+@`ã%ãø€ïKFzöi•ùZÊÏCžÞÚi°‹Ý/Ðaœu…7­n¶Õ€‰­Ä!°G6Ê•ß~F«^ÝÀÏ- ì¹ ¿øxŒCø¾;ï7¥ÏE˜4,’õŒ±êêÖNG³¯Ç)eCãŠÒ·_߇4eî/›ÂÁœ)Bæâ~ìÜÐP>wkkÜ/àã~šÏ¶™Ð?ÿýI~x6ýÿç­I¼^·šÐ?äsg˜PáHÆ7$IúOó¥+a¯ï½6eßq±óû•øÔ‰°8bê’µˆ?÷J±cNœS£b¾ê6Ìsø¯8rÆ7,í³I^jÉLlâ©Îù]ÄS½4;ò;ö«Žñ £™— ã¿r¯MÕ¿Á8ü3V ÷Vוâb‹™Ïæ_1¼ßù8N¸”çŠëÿSSÚW=‰ùÛ•æxEþ…·ú^ù£WÆšüóz¥9û\ù&Ô¿3š1½ÌÿR$ùâyçú –0(ïøJ‰ë’µn|/(W,’$”XLÖ3ð5æéWŠA‘È<‰í˜·ç_ù³+Ó«´ßRR–K x´§°‚+ÍþŽwX̘^1̇ʎ1¹ç]â¿Áõú3÷%&ÌC΂•0Ï»?cñp¿öÒœl `±Ç¢Š öï¥=‰SXC(ÍçážQßûó{ø^ù£WþSz¥ˆ½fnJ9=*Ƴûz·Û0fvi¯=ón/Í…1NñmwÅÂHB‰ÿ”pzRÃ"šñ²‰ÿq$cõüû0Žq,¸'áeOÐÒLXæÙ^Ì|‘K3a‰o{1JŽÅ—‚[Q6Y"ón'¬ž”R~}ùÿ"Ÿì¯¸Ä9‰y·Ç2V™üoxÙœ{¨C¹b'±"——òý®Å÷~ÉÅÌ»½43[õ'žÉ?Æ“?zd¬É?«G’û¶˜½o õ#aL3&i4*%ÃdMD‰0acP:”+&nJ\‰xCbî£ä˜Ä)fÄ[ ? JRX Ì-LêxTIeâu}eƒ žÀ’<•²«NžuÄZÀ„Få× Ï a|æIJø>I(q-röã¢äß1b‹ã'¥9o‚¯¡ô(9IJ=²§ˆ¯¡ô̧4¾ã"‰ù'˘gé_yÅ+ÓÌ +W¤¤,–x'G3æñN.͇ùŽá¨gL³X”ŽùšŽc1JÎ<”ÿU®ÙŸq.¸‡²¾ãŒXÂ4‹ÿ þ÷•/ÍÏÎgþ£q(½-eÄþ㢴‡2aš™÷.ËŠù å½òG¯Œ5ùçõJ *%2¥, DÆA“ý½æíO[‡rÅ"HB‰™×ö_é_¼g©Lþ÷^E¾ë$ò]aOJD‰°'Å t(WìII(1ö¤XT1JŽÉ“R‰xìã{BéQ sâ%¿'&S<ªÄ‚x¢bN¢l0±XrE¢2PvÕˆ·æ&Z4*¿:ñŒÁø(&]Œˆx#`9&_J-òÌ*Æ+ŲU×&Ï#QN¥¨KÎÕã{ÀÄŒCéQ 19;I9± (诸 ,i#QM(£[%ùƒa›ß”ñ¹»¶“YIÖø0‘%˜Èqÿ"ëìÏØz”_²fŒZ²"æLZ;Æëþ36‘‹;æ;^w1Ê‹=J†M %ÂFØgâï8Eq¨b”D Š8mÅÍiò‡÷JÒ#ÿIýñ_éÿN_,ÝI/üOôÁï{`éþGú^éž÷?½ßý«½N†JA‰M)£6 “KŒ}-UŒ’c_KAI0áâPz”¢a;áo *1#l¬ ” &cKÈHTÊ® ñªÆ\ÅäŒFåW%^­X (&jLuâaˆ¿1&lJ,"^_”嘀ғ3.µˆ ÆÄ$Ž/ŨµÆdŽG•Ô!ωâ{@Ù`bÇ£JHÃOGYc’§×'gÖñsµµ¿á6ªPæ˜üѨü&”«(ùƒU«kJ™Ú‰(Y)f­’G$*ìÍZQFc:Ê ¥ø_ä¦éQ ,$5Ê‹)U‚RbQ¥£ll(¯–Wäßð³eŒŸ]‚R2¶a©E¢ÒQ6Xˆ ¬#Q(;,JÊ 3•’aªی󳕨$” 6Eœ…äX¸I?Æu?Æu&ÿÌq+»n ùwSÊåÎGYc’Æ¡ô(&«e *A)1qÓQ6˜¼ ,#Q(;sÂQÂ|ÆdŽFå[ö”;¦ ñ·Çx˜àI(q5âëŒõ‚’c²§T'~¨¥G)DÄxÜá‹JGÙ`$°BˆüŽcKŠ"•²«Kž-Ç÷‚‰Ê@Ù‰ÉóƒøZ}ò\eu—'óT”ˆì½¢QæXDѨ|” ‹)%‚ŠAéP®XXI(±?ƒ¤,ÃV‚…‡ÊGI¾cØÆÇ–$,o9*U‚R0¾$)ÊHT:J‚ŇңX¤j”5j<ª¥Ä‚MGÙ`Ñ&°ÂDe ìceÎ8¶ù(s"J„ƒÒ¡\±°“Pb,îØïøÞz” ‹]…2g,Û|” ?%ÂâAéP®Ø’Pbl±Œ)Á†ÊGɰ1Ä£ô(65Šsa’&g0Qe˜¨‰(&k J‡rŤMB‰1qcQÅ(9&p J‚I‡Ò£d} “Y„É,ÂdŽAå“u1LjkLj;Lê”[²ÆäŽG• ”ØSÒQ6ØOØ@ •²ÃäW¡Ì±¢Qù(B"J„Ńҡ\±(’Pb,ŒXT1JŽbNÆXäœ-‰ ‹$%ÂB‰AéP®X0I(1öXT1JŽ”HDö1±ˆÔ(k,¤xT J‰}C tÞGÖÈøßãÏrñçòö–GüÏ}êJ`"iX$«¢¼¨6m¯´ù }.‚‘àü(Kh.”ö –aœ^¿ííôÞ¯Ì4nÖgߦ,Pµž3ÿÄ‚c0³fße#õƒ%ßÊÙÇV ®z\?;k @ýKìŒ\a~}r=^¯Aêz•gu :]Ãéø~}ž¦æݾÄ,åPG0Ø,  öà½|îÃɹ‰ä:j¼NË‹·½¯ÖÓÀ´±;ݳÀîî1ï6½AÝ;ò’Q?õ†¯±f•Dҡžúõïw8ë+¼Ø¹þÝŠõ>âwêsÿ僌<r=^ïm;ŠÔ@ûçÐù§žY0Ýô8ŸÏ{´°Gß¼]›{ãCÊ5(PÞQŒygvqô§!Àù*Ü™ó“Jç…I£"ÙœÀÃì­5@ãeÃã‰ïVšƒeý£ZÎIí 6Ììód°ðür…Ôcs ¯ûÖä ¯Þ®?ÚÏö´y<3Éâ…Ñ•s{Jû”J0ŽYæà̇v¨~r0^* ²Ý–`]”bô»f<¡õ¤M…[4ƒ§™û9÷~}Ê?|"]]¾ONrˆSßUƉ}5éEb'ÌÇþ×Ü^dBxàå6ç…°3«_Ëþ¡“ÑÏqí„£Uíú]Çý[ÿðáþ:þ%{ÀòxÒw@¬ ç¯ [ÆR²¯À9Ò”«Ð¨/}”òÇïJ©SOæ—Dy;±çÒkÝó=·Îm3±ÙêÅ™pË)n®$4¯¼¯|'w°~³üÎïaõ_ó¸_÷§¡>;yRîcM}S»³< qTG¥R÷úpŽ*wvÐ…gBó>þ=‡·M yVÁ 2<`ꉖ=”ÄÂ. n^÷FæÍ>œ¦r4òëÞøYzwé3)÷¢37 p3Ü~i¼Ûý(4Xìð¬–M_8xrwc›“…AÚÚKŒðº[7e°>hô•¦þZ/¥œÛ\š§)Ã8OG¥C—8 X¿jñhí… X’¬û”iu”ù³õa¾;C9ÔQü:ÜçŠÿþÜGótKû/*1΃찀Jë5ðæLgÛñ‡2`Ìø;S/ˆŽ‚ÊT.q¹ÚÒöMµlŒ@ã !ýlÚìtΕ.ÍñŠÅë»,]âµQ¢Š]¶+eÀ‘ÙMëÙ›e¾JåAthõè{CÊ1 ž÷ w³µ'øþ|ìŒ|õíWáuª·sI—áuÎožV/9"Ä£nˆ²,Ž2þR-òZ¼ƒ-ðOƒ¹?«ë@Æyydx_j¼­O ¼ 9åÕÖ#~ “«eÜgsÖ»\xÕª ¶Váüì„~»’G²÷¼ßñû.÷)+Ý÷tÇÔ£¾j›tù‡Š:wË€›=Ý*¼©zn¿‰¹Ë® Lé^·v¥ðÁå øÜÿ“úÙË~ÓÜÏûñò¼q‘,²©é–Kë4`ÀZfÀù—]/>'Cî°SW{éÔ/)B œö¿¾Œ/Î+â|JÊ= üOG‚qºÔõÖj–ïÌÙ{ãâ5RNõ‚d(Wl¿ y¼3(‹¶_±«6V õè/p?&î“ÊóŽúЖ7r )O’‡0NáèmyaÈõ å·å×àâ¢f#«mK†ÍŽs–´îÏü’'ÔgsÀy îA/}jÜùȳÛ ©Ÿ.Ú4ÎßñÊ©ßã¸Û]ÝÕYµj~훑p &n8wfX2\üR½ï ©¾pwyÿ1ÂÞùÑ˽Â÷M§ùáÊü4ë1ž‹9óÃR¹ †üÇ8^³sž=m«Õû” _½ÙÝÏeÏmœ =§G¥Î:#g¾÷“…*kÚÎ1ËG0ØDë}€s‹iš@É„!§åU ñÛž/Wû?C}`œj >¶JQh 3þ¥õJýUð®\¡³îLéÔcð”ý˜úÆõõ~sÚ~ œáìé N³§Û}ô)¿á½”Ž<˜¯6óÑÇ8Ñ+[‰&Î×À™‡¤_…Su×´Üžt–ò{¾k¸(|Ï=j1U >팾ó3ûÙº¥·ñ~DýõÒ}ùÁ4v.“o:Œó(Ù©ƒæÛþáqaAWaÛÊ…{¬<WŸÕ™ìÜ N×rý-H;ÕxŸà‘\ó/§¢ì¾óœ3Æý1KóMšÉ’gnÿ_ìT“ÙÖ÷±cÇŽ=+Ø ÙP‚ †*H1btDQtDl‚رcÖˆ-(Öš`AcÇ V;öo?9çdÐuç¾sï7ï÷Þ÷[ºÖq—s=;${ïç´ü&ž˜Î:Cëó°ÒUûÌdIø|+ó=ìZZÝ1\´Ðj–þó¡Ü7ØøKBâK;û}¾ˆo;tç¨ËG17¹°y õma©®€r@4ûëŒÒ›ùp×hjÆ1ó­uÅ8°*¹ÖLpîžE†ƒÅéee„OóVÆž{úèc|p½ï­®~0ŽiZ“‹¿:åÀ O›ióávþ¦¤ÅñiÌÿ¹IþØ®V¥53‰àÜúšáùÌ9=ÚņÏiRxRÿWñ¨£š7ïË1Îí…+Oç@õÂOÍS‡çÃÐùÚîøùP.TmÒ*§ï“’ôR½ùfÿNgåd[¥ê‰]àæ½§›½•1?ÏÊ@9ÒAü{LÅCÑ.ßñ!"1—g³õC>8':_é¶& ^Ͻ<éiûz0MWÀádâËÒ͕Ύâþz|݃O¥F63¬Ôÿ¹¢Þw~Ó‹½íØóã¤Èï/Ûz>> ˜çsç oçÎ á‹Ò`‘ßrçã{²÷yñhxàà¡NþÄhDݑʙ_§ ëoÀy_nàG½©®~0Îð5%]S s€ÔôIY½ö¼ùmŒ²$4 ¢NŠçU4Ñï=ŽÈ>L#ÔO×\9Z{IsÊ!¤çkqYÚ—,XÞS¾kÆÑÖzã7\%Ü@sç`gwab–»ÞWôµ¢öÉ*‚P~Ý`Bçk}¯ ‹Þ÷˜óUèëéÅ>ŸV´~DÅ’[ŸWŠ¿™ ¿Nj^»¨Û9йõöRÃ4 <ƒ!ðΰm’élBýr.ã\vùÎ_Y„ãûN–áçíü|ši7gὓÄïùT8–û>c³#ëÏs õS–’ºéûêÝuÖs¼¸¯!7öêN_¯Ç×aX/eôºÚY}SÎBi®Ô%sW*|±«QÃtú»gEž8/ŠTÕo;³yEã{{³çÙ½Oðò#=UV.ßñ«äç„Eó.=.dÃ5Kó#.AgA모;nj*l^6sbËnRè\[I4QZ6Úm~HF¬‚gg'=?“÷î“^_“?'S¦ç êêã¬c²¯,6:~¨Gjcœ²)]úIS!¨×Å=v ‚7ÃB“·VfþÍ.¤õÖ‹-³ÏÛ±:ª¿ø|7äÔØ¸ÎŸgú°+1íGÙPgþ޹­¯žÛÔä½­S¡«8fG Mµ£È›Þòø7c=ã&°õó8¶¼U—M©oÅt>òNLy Cô\q]}`œ§ÃûµÞl’ Ô/ø ¨{´ðs ˜*Žuï/†‡N mY<—´Y~èeÇÎ>„󒸿(÷û§>¾oİçܪk…6l¾Æ|1N˶œM*áú-L:.1è äˆjv½u?¬V{Qµkoæ3Eè¼Ñ[ïËøiÀù‹”OÿNÌÖ¯ŒgGëРM±ä¸oö´€kY°xk5qÕ.gï**ì¬ÕB{É>Õˆ~‹&œÅyL|>Ã90œ»]~¾.Âñ»|´í5l¬êÕpÚñÓ§A‘ЧVÄ™æ‹Üþhr_4Y5ƿαR2§{³ž­vØçÕr?PºŸóNï;ZÞ_[‚qL*IYNÈ‚äm“p x~O¨|äê‰Ó»‡Ö÷‚Öï÷][pv.¡óÙ´ÎÕUACYß´×sm¸1çjQ>5_È1Nû {÷m›Å8~§áé®í¯c•)ðÉÃÄ"»¥ãþF²}"ÐÖõ‚늾ZÛGýó‹ÎËÊÄs–žJœmÿÝó8ã|<’Ø©ÿ—LŽºç›Uë4Ì~tÒÀñ)@ýñ‡ÝšCD£ŒÕ;{’µž¶÷[9ƒ ÷Ûß‹°cûïůǎ\xüÝ;1]fþ·Œc‡qFÖzdw¬FùÔ¢u·óyеžêëÀö)àt¢×Žun°ÍZpˆŽ$ôóvÑûóþÅýxù|†û¸–÷s×`œ;]9 ¼t Í<¨µ÷AY⥃lÅñ»¢Hó…ól*;‘Ûõœkú¿éÃê³3PŸÖ{bÞ¹o3]ß·§uƒq. Úý6N´rZ¸lXT9¸¤»É܃°)±Á&Í7ˆßÜv~܆hÂ÷ÚˆFÚT™o ǧ'ÙNˆíÂüz õë&ÊY³`>·tÇ m±äZËE!®3A÷˜hždŸ\cyæ¬ôM=Tˆóæù·kYE³u†ŒpŽ6_÷òõF¨,Y}öƒ˜úb<:_aœiÑŠ}аLè1èHïÙ÷r!¦GAÖÐÏÉàvjл,pNkœ¸)š°yùrLvm—/óËucë÷*ú:ª¾ømm§ îÀx´~0Ž­“ +¿úר»>nuÓ·ÙÅdØò¢bäɸmkŒ-1š=']È½é» ®»ÀÞsÕn{‡@Ýkßúž[û^Ì9©Ô÷[|ý£«Œ“³¸ô¨r&,ØàvÚ@– ÍÄÆÓ’áõ«Î¸ÔëÍxÑ$TmÙ£}G7½u½œ‡µÍ_[ëy›¹köÔ$½KeŸÃ®Û|ÇkŠÄ8WEçžù?Ï€ÚK:FYÕÍ…ZYË]ìHfœÅvÐdS×´»ç±u“'áëÚÌÙü¼HÌy ó6,ޟ­Ÿ[ÐúÁ8ëÏ;Wµ?ŽU#;O¸} :zXT Ü’Ìö½«å)Î#;Ï÷zgâC¸¿0Ý×é‘ó¿|¨×ì6ãZßÕç5åÓúÁ8O–W=œ“eŸrÆŸN:Ò&/ümS2›Ÿ¾wÃI›§vÉÝ8æwG ÂßêW>8×¢¼/yŽ Kç1Ë›ö¶K÷9Gš Ä»d þÚ¡£n¢3œï=êÓi㑤YÜè^ó·f|ûAz.'çtø<eÖPmÍ>w¶ŽiW,™r(RQ¦™òÜ9sE§`™¬àI“åÉl½`wÖU «M(×À—pî6_—qN2å—0N‚5›'°}Œ“Ö¬þ{¯¨[wÔçÞÏs`áÚŠÝÒ#’ÁpÛ¶`ïÖ0?dQÏæAQdÐÛkÏêžÌ·ÙŽñtmôqtu㕞ìܤó7 àbîV·Ó9ð©kÌ;¥g2DœÛÐjâÐAð¢eŸÙ¾Q„æ‡#á\ÀÖÂr®‘ÓwGëKÊžË4ŽãÌ »:À^­„·Á5‡ñÝÔ é×Åk]á0èxÓCúÁ!†ñmõTÎA*¿§ÁñÎä{å¾$PCÀ0˜çÀË9öîª jxZÒàŠc±3Ìî›â—X7–øÌ¬ù²ÖB¹“Öú}]^ã8Š5•U»GÀ¦X¯q£(KƒQë{©ÁÄO˜á8BÖ1aâË?/R6©·½çlo¸PïC÷ݽûÙëò·}±¤“¶ã«ö¿„§z~©ÿ2Œ;½·­ ËƵª‡ †žâsþ‡6Ųs)7òn}«†«¶Ž† }ÒÆï3 Îg¤ói ›§u¢y‹ãK Ä–“`½©ËêÙð<&Ýtlê Së6«é.¬¹ñ]åç»]Åw¼d^ìu‹ŸSññ?ï~yih€„ñ”éøßH¼JqúÁ ¨µGeCQöàº=ç€_ Z71¿ÝJ›Ïõ™ON¸û¾¬÷3×å/ŽÓ°úÎõÞŽ'`ØþNñ7¼³áîº xì@ï÷ß¾ƒêž×|9W$‰]êGxýrÞ¹.?qœ~Ó½‡ѱ_«wϧ¾Gw˜9 ïÏGoËUY5æóº!b¡û12`\xx©x—VöžÇáxçû FíÇá¦U@7q•lÈí••X¿çXº}Ö‰ZæýËó’9Gb o 'þ×Í jß fÏÕÑz^5ßo¢ýr58~XÁ¬ÜÒ‚c>­öÇK·² ²oö‚•€ãöÙ¦÷OÜÚ¾5ùÝ´XÒôt«Ä`©;G´‡€†•—M|1D?þ® upE8½Œ»ÞžóÃÁ¥BÇ{S² æè;îîß:ÌD#GÈVµ²Xò&ËðÞÅS.zŽTy>ª >Ç'÷œ´GtR [žÝ<ê\*ë¼fE:ÄÔ7øÒÓ¸]ý˜Xbšîå=VÊÎmm÷‘òü&Ž7ë GàÂæ³8Ê‚oÔ>#çìƒô ý=š¶®#×ü:®û|¸UdŠàZÕÝ!°*æÀ©»]ôõÍç9†6ª¾-Ðçã+be3³;í«Ò¡—ÇTóÍ‚®]Ó[—jöÂ*‡ƒ)KÕ¢†g2¯Ï'+œà*Ò-5l}•›CaQÑœ.7’] «Oü¥z£+ëÏ_Üt«ÃÙþ¤Œæ/Æ2Ëûæî#é`VÖ»£YÏ,Øg\=ïUõ½0ÊðŨG±"l¿µˆcÜô!Œ³;˜qXô\°ïÜ\ùI,ùplÚ•™þ|A—ßçÂñ¿öH³qï÷dA[;[£÷À7—G9—¸€@q_Ö;Ž0® û Pú~Ñp|i¿}ÍÖ‹vðKga†@Ï‘”g¥ªÉÌõ+ÃeÒ~u§Ç™0¦£ùã²$0žêZùN%˜ætgµŸy¡¼e7Ây ^KLö.I¶ÕïßOqÝÝnyî3ý¹%]OÒõ•ãØTïòíÌ!0?Ö±nÍ´LntäBÑŽ$رä†4ïú÷YÛ¼ÖÔ8òîà³Ô>m½ç¹Ò¾<”ís<×ó˜(Êî»õUÆ©"à†ŒAÝË'ˆL¨åøª¾kH„ ëÓzf¾%Ü>>É5á}œžßשª´÷ܶžz~lí­rx¡?/(ÎJ©Ü´Â Æ·§ëƒÅ’+òf±ËâÒ ‘äž ]Âvœ˜ÓˆÛÓF¾ø~`Äf4%5ÉÄð¼}"lo4¶ÓŸ_óŸtýîÈÖìüã¬úÍ+®N,["9¾d&œœ¶°æ»FI°*¤Þà–ÐdýÓÞ—\º>ô!”Wkœ3Åy@K“øG¹±õ.Ž{Ê9¼ºó¬T½hû’ÀL¸xs¶í«ÂÝàØlí±"kP¹'Mq„rG~ž±5mkþ¤ ¶™ñµnÍR:/Áñºrø,޳>)®kÎïl¹a7ðõ8íW±ä|Vu§]S}غi8ðõ-_ÏÐýˆl¿aÈw÷"1ÎY“N«bR`ØÔ¼üÐJ™p-óâG‡Ý0@X¾Ô ½²ÕY óÈøðˆ]m}‰ð4±¼ëÇ9Ñì·ë§oż>¿[×bcI™l«I ´X½AµãHÞú3ý‰ ?­1~^o¨Æ¾Ñ„ö=O’ÛÅȺÓÊ!@÷ƒezNV¿é¿gŽ›ÿQLÿÁßqU4ǺNô‡.AaÑB«MA{2 ´ÏçtûH8Ûùu±t_22Üät4¡ynÞc=G‰÷+¾/»n÷Mí¿{ߊ0ÝOO†¼QæsbÎeÀ,“§g»ªàtaÀ·^2h.`UEØ×¯í~eÝn ØYÄw·ØgǸ±ƒô|1¾?OçMC¡C¿³]m’÷ºc±¤n!ÏœPC?‰¹ý²Â Ê¢_çì‚Õ†+>þÑ·ø>9Ý·ðv.«J;í¨•Q•õ¯ÏlŸÓ­Ÿé>³ãXä÷w¾¸\ µ=z¬ž™ƒV^=yæéN¸X¿Ù´¾·\aIÀ‘WããïSÂì¬ébÆÝñ—O†Ã7Œ¨ÀÖQ_Ät݅̓éù–ã<ÔìytÞI õtš°¾×ï]=v‚å ‡I[‚]áä-¡¡Ì'×ßßµ9cIø¾;Ýÿu„EÇ]´+Æ?Ñï?rNg±ûK§êæu×¶8í¶ ðVš7ÖmRªrwèÏIÓîT.ì=+–ŒÊ¢™;¡ã÷>æqø~¥®^pÜ’gz™Íß>j÷&¥=3`‰xÑeŽ; yŸF •ý`cF¡Y°c¬~žÄ×É|ÝÇÏeù>cy¾ÇIŽU3Ù ÂnU#‹ øØºÊ›1·3•=t:7cãØÌXÎûdë2gý¹¿_ÆÏiu8Ö†âïžïŒCûsèð¼=2àXsµ›–mzne ó6ç']¸KØy¸~ÖªÁå¯/õœ=þ\¤ûѾã)aœ]«®)龪«B+ïn˜ϲ7<}b·Ú¤=tš0ÑL/+&÷¨Köîöü åpÙ¯K>>çñy6å”u¦ubŠ}}yİG©»`Íï†ÙO5pó¼kmÏ÷ÛÀ¾bm‹©­Á¼Aƒ6:Œ!œŸlQu“Q¿(:08|††&§n2¸ò\\g´jáî7¶ßïûàøÙ>Å…Ý?í€K÷rÖj âã>›‰k¶Á½…Óšµv‚ž• r7[Ï'C^¾¬~cºáÜ'Mò˜ £ÇÙèÏKùÏïû;Ã8/&jýg Ø×·VQøäkÀâQ“ëmƒŽqGëJƹ=‰#Aêûò$™fo_ïl;ˆí÷Ø3.î+=G‰ŸßS®&[7`œßF)¦NŽÝ Ç®ÝÝ•„¿OJ½ñ ¿­pöËØ¯¯—yêÞsœ~¹G¬YÎo±Í±šãºmp‰‡~Ÿƒï3òsY]:¶¢÷w"1ŽaÉÉ7ö+¡Â°é{35¿iØå–‰ð¸×“_v½÷ƒœ¼}×Ì'tŸEªÞòóY>âQž<ãÄá8MºŒ3¯µDë§O:W"Ã>%¥iaMš*©ËG?=´ki Y*LKÔŽD }×[äÉzwKîùÇ=Q]¾w*–ܬhóôÞÙU°1gQL›ÉpÛ^+ØïÙz(èÕïÚŒ"oxï=zgê×X~^Kâë;tØ'事úußWå÷ )ߎñ1ŽâwG‹Vã—CÓÝïÝgh ÁaºëÙëàè¥ë 3½ÜÁ]wàÇî¿Hô묨–ߪD8°ü.óŸ”#<躜Î$§NÃðŽ[Z-‚‰õŒŸ6OÐÀ!máê ¾ °¿‡­Øvå0¨¾ u ¸, ì9M(ÏØ®OVôëOùÙr§Zµ l'GAðø’à9J |›¢rü%|-Tô^×ïF“!pÃO¹]S1ŽÙÜ † »Gd ô\#ã8[â7N¾1 t8çx²eÍÕÎê5lßG þ1«¶¸úc^4fæHߤKžÀ×9vû²×>¼Åî§âxt¿¥qÏ[ÑxžF6ß|äþj°h¨ÓdG)1»}Ä<ŠÐõ‹œó倾_£ô縜ßHó—íçãø”O<ž¨v-Ó´ž­ÃÙ5,‚^­ _†y«d°1[¤ž¬˜Ëî­ùΤûuÖú~K?'Ö¿…÷³ƒå½}¦xõ+~þA~–¿ž^ f±“·úŸê»–'ß‹bóPGBÏ›,ïòÏ?Oy}”?—2èŒõ{êdЃŒ_É÷r×™35PK¢»q ÁÉþž“GzÁþ9×Çœÿ8Pnh=vÞúó~OVí{ƽEÀ{ýý6ë$ɧë3Æqµ#jaÒòdŤÅAË4PqjÃ'ãaBŒº™£—T;¸yĬ©1„¯ûhÿpËw¦Z<Üâ #{ÜèÞnO ûÝßÄœ³ÌïŸêòãÔÖœIKž›,¹Ø/Io·þJj”.1’yfm?hÕxÜÐ×K£ çÌóõ’ï™/r†³}uCýùTÜ»ä¨ÂX/ ÜB{šï§z£þFË’‹>Žk4pßü¾“ŸíBèÕ=ÃÙ3Ùî wŸ(ö¾µá|Dý=~_Èb™b×¢#úçÌê‚´wV°õ3Æùôhe£¬«Ë‰è ´Á ƒ X1Ézt|,lº"û´ÏÌ4â~SE>ÿ£÷ÃÿØÇå\Y~®G?{hýöz—®éŒ¯Žq®ï\2æÉj"ü×&¦ ÜïP¿ða´ÞÚõº…ÈÎvÛ›mK>‡ ‹/xÞŠðõ=/“2>j©¸ÇÔÏÇ—½ó{Çå÷¿5'± Æú5^ëÈ9?ǼjÖЩÌ×wÝÎÙ°guurå‚Ì^ä¼fÄ‹ù„퓱ýεÕ?gÕvž!Ïzµ„)Ïj–Äô·ÿn^„q|GÔ^0£ë&Ò ­÷a¯ èßaà„©/¦ÁâÊA*¯÷ÿwO§mŽ#ŒOIN]"ðÏÍ¢B«¸ÃX³ß#Rštf¯ M[oqNŒÜ—̆5k?ûú<„ÖO—bIò΃¦¿oUz$Æ Èµyk¼¬”XµåIOèôÔgnqŒK?ˆø-¼­ÛÖè}s7P6(ú$´¼ ¨z¡SH¸'6 gÀXÝ…Ì ´~0NÖ«â[Q;¶’ M‚ξššµ ZO¶3 ùMÚ„»n`à…Ó¦qd¨S“€·öRBÀ€×“BT^ð@œß~iw럩5úUGP{èp©OãTÒúÁ8ñï“ð“ØN~™3fÆ›Ø 8—Ô4p’ÃHØ>(·µçiص¢‚raóù„ñáÉÿÚ²fã‚Ø>J ´¨¿ïlaXX}î“é­áb+³íY¶á0P¦õƒqfë@»;ÉþYmVe@ç”÷ _o9‚ïhߎûºÀ– oƒ¯ÌŒá÷jHTîáiµ?N“ó U Ä#Ù|¾9»Çc Es+Ì«ktßÖÆÑXÞÞp¸ŠD÷ò8¯~weÐèî¤]ómÚÚ*WØb°p¹I4IÈŒ¾~ƒÐûØÎ ðžÈ2ý}éλVÜW£„Ùi3À•qnéóZ‰qfMëâ8eÍnâyù­Ýõ˜ €Æö CÞ'væ)S’Úx‚ËG›¼éßæèïóýUž¿½~ ?é±ä¡˜ïGðsP:dû¯§±fÕÜJ#’ȧåm{g-:D\°Tz^ﺯԟÎî Y°ùŒTϵæëúœ|'n>úKpÀQ´î1!± ]ÏaïÚ*o»í!ºiyƒLØU0Ï‘aÄ"åå1;™F¼~`ÖíI˜žkÞtOþ·=K}a@Æ×§·SÇÂáeL—¥W`œæ†°uNÔ¢F}`–±»nûN´~ºKu·Š&#÷’;´¿I&»·=“ØŸœù³àNմ› &ëÏYowj¹÷[X8l¶amÇý^@[ø=øiÌ®ÏF0ÝÈ-ÇϳFÍ_÷»JëãÐý}D»V¸h• ]:>«|Eè¾b ¾ÐmŠIÅ©¤§î Õ¶ù6#¥C&›g€öZ‹.›Â†&ç*+ ™Fwµ=tþAóZ‚qèùÄ"Ü®ôñË„'ß2ºzOšÌOÝË–îYî7v:Û‡´…XË_JOv¶~OŸ¿†öšúïõ–Ø:ãÐs}5©qï|ÌòL8q¬ð »éb«±˜/‡ÊîVQî g‘F_×u¼ÞžCXCIã£õç>ý¹_Ó{(6ìïÙ:ãè0êAɤm¯&é®e‚v륮¥ËˆÁ³/Ÿ÷¹ù@j›oçDâHÖÿþý¾ïÂ×]üÞ=wÔó°uõƒqpÒÒRåv4˜yùÍ Y0¼¿ÙÑQ‘ËI_“œñ^zÀÑØ¦Í†UžK(çØžT+m,•}ö‚È-“°ez‡Õ‚Õãü•Ùï¶Þ‘{­Œ#dO‚Y ¹W­Ø-·s4$½sþä Rïí¹ÛK†¹Â§ƒ§?~IØþ™{¡n»yö>PõqM_?O¶e¤Ÿ‡ ¶ýŒçØ…ÖÆÑ]·oJzÝ[ûqº[Øäµ¼ïêJr¸~£*_e@9ÐsHƒKÍ¢ ­õý€Ïg¬#Ù“bn³ïT`÷{°}ÕÖ´~ºKrö?{¶æa*©q|å°e!YÐoà†âßV‘1õ78æÂÎégzß¹[÷„¡·ÈÁKÛ{³ûV·Xœ{bz~aÎö«[ÒúÁ8 ·}ˆI#¢ í?/Ì‚Fµ ?6\C¾novúŠPŽñ B__]ÂïYYxokµõ¢5èï'ê~¾ÓsÖ¾ßÝ‘`œÜÑ·¯H^§‘å³âGgdgÁù&3ÌN*Ö’yæõûtMôdç@3ÿwt?Áx>ÔyÖæSN6/ý*¦¼zw}éêã,rµQ'ù"IÂu ÆÙðÕea­QOH÷®§¥iÞàô¸âÚÜÂ÷ËÙþ7\/¬9gÈhvþaÈî-|ó×ÁÏGtõƒqŽ&íÜì0¹¾­æ7¿l°wøÚØmËz¢Ê:ð¥úe_ÞÏô\æE.ÂÁó vî6˜=Þˆ7¿iºÇôÈWýþEù:UbœÝ™[îå7K'¿D^’m‹Ï†»ê—àèäP½a¿öÚ6jëžBøýT~•߃àçÛªûÖ†ç/Åt~×è÷ØzãŒ_svpñçtâÖ~dõíÙÙ`5ñ}¾×²Í¤0ðãĉyrxfôM½Ã>Œô[.OZ—æ|ï[„0±ëÏ?ø½þýº@×ÿEçü¤ ž¯>Bžä«>tþ Î=MÜ®$gݧ5_øR/7d©2•_zÿß_â|n¾¿t>TØ0•°û$t?Π{±D÷µ¯GIQÔxï“Ýs ¯ƒpA3‘<ÛÿeÓÎrØzpÔÐÑ.á$hÅòõ®¡úïkñ{yü܃®^þÃû…"Œ3Ì<~”LÜðeîä(–†/0b+I<6þuš˜¹¦8ãÎtÂ÷+ùûÂûß_ä?)—¼?Ðý̶´~0ŽKƒYâ3qÇHfâáðð 9prÏ•­oúo#ãGœòÍÒ*Íj;lÅþéúûDîêw8Po€¯ùþ=WøÝ<^Žqj=ñy<Åû8)ê¯,ëq †Ô\ëQPk;ÉX|óxcoèç#QXÏœNøs`djnÍßJ%¬ÿ÷×ß+ ûY÷ÅüyXþÞ$Æi·y•Cídíðý‡óÝOA+ÛzíÎo'ÇžììÛªÀtǬÍÂùþ„þ܉ÆéÂîû\fçmwØù‚›Ñþ¦Ä8™Íò\w‚œ™¯÷fÜ)¸}6÷[÷Y;Èáèñ©ÓÝAw]ÿÎTr0°Ñ§–úûx|=MÇËÓº¼ÂÎ+{±:kLëãè¾ö×à$ùf³ÌØúð)øÚ°mÿ3-v’ö_'O+^ïŽS+˜õ?…ð{V¯+ àûÉt?âãÏ?kµ‘³°ÛG ßG,Â8÷l… Š'É•Í+ÂêæÂíuûÏßIÒ>´oÑú’;ûžt©”b³üy1®ãuþ8'áÏm~ÎÏû¾û^“Y±„Þë%¤,$îåúá¹ Þð"fÂ.RAw àwW´Pï9Fv›9öï³Öøù- ‚%ÏÒ”ïO•ê¿Hûµþ^˜®~0Žv}ë9c]ɽÖk^àš\¨ð¶fóv*Rô bì?(èV1pV¥0bìw·Åþ«Îl]ã•„í:»Qúýl]àx89Œ¯½Ñ-—î¿t'.;ç?ªj»›LÞ’áØ¨Ç(hê•ûÒvÇ$BχÁ†¥›yB¿'%éÍnŒÖßgâõX~Ÿ_Žã_m»5áx ™e=2Å×"¬ö™²yLñ.0}jÈî×…:3Þùîìï½?ôû=Üo¢yËîõáøE×¥õçuÑßÅ"íº<ˆÈšõðÚö=¤á±ñö ‚Fy&K„²ýý÷mø~#_—ÿ8Þ/ö}†j½ç•äù”âÅ{I»]5ïÂVíÁç­kN"ô9êÆæéؾݯÐà8ïó'M×.!“–\: /*=]iÙ|?iž«mD`»ï<™ðý:ß±þ-Óß»Øi@ÿüô`úéÁôŸäÁĽÁ„ß]k@ù„å92ÿªß¹”ys–ëßáÅþ£ÐˆycrîÖŸ±\ÿŒ»Å¹ åY® ÆråþÀœå*p·´Ì+8œ1 ˜ÿœ–ù1ýžŒPt‚]:óÇ,ÏÞ*Ï(˜×RÆÝ˜‘åx×êr~çÌ›®'¾f,b5ÊØLX×ãX(t:J„E‡*EyaqkP¦Xàñ¨2”ü¶ŽcUü«~ÈjÆÕ‰D• d弑¶«–ù} ¼Â¼Ÿ=ógÏ4øÏí™ÆìµPYyfÅ¿ê‘ìÅ£²¼¯)ãVü;>ÉÛ ƒeʘeŒñªBõ”Wþì™?{¦ÁnÏ¡T(#LÎP”eXñßólŽcœ&sª%äNG‰0±ãP¥(/Lp Ê“<U†’c²ç¡Ì1áXÒ+3ƒ3Îf†ì.v J†‘ŽaQÄý.vy~cªÔ˜òäüüè+/p~ÒYAɘ·¼Àúñú‹< ó—/kû=;¡Màk˜2.v)ãühÊqlKËù? <4ù\lÁÚ¨+eý GãnÂŒ’a§wÎÒp,T)Ê Zƒ2Å¢ŽG•¡äXÜy(s,ðVäŠX?†”»ñ¯zB§3ÎOªåUÎZ`Ý0ßgߨýÙ3öLƒÿÜžiÊ^[‰e£•gq$°dU ´(+LZ%Ê7U€’`«PFÕ(óVUŽ ùw0»ã࣠<Žør¬¡Ćüg¬!î{Ïùk(¥EYañ(Q¥(+,¢8T JʘÞÍ‹*U¸Þ…É!°oŒ}kÕîQè|4¡å¨W¾9eB9æ­ÀF3ÇM`l4«øDJT ÊŠyçÿ,IÊ‹=UÄxj”1~$ª%ÃŽaˆC•¢¼°hP¦U†’ccÈC™3‡Ð æ­eÔS_`„ 9)üá=ó_í“Woü³žøïôÃÄBúÜÕÛþ«žV¾—ýØÇxïúÕÿ–^Åû”ð« (£VŽI‡2Ç>”À  ”e…}H‰2Ä$ EŒZL”xT)Ê Fƒ2Ť‰G•¡ä˜ÅXG"üï¨2‘àˉ¯¯ eˆ2¶¬ÀÛæ\¡ð8fWVÒ¢¬: >z¦¬À0³ê$|‡–2Ì$?ð†T¨Ò.Âw^„ïoü½lG5Ê‹=U‚’aѧ£DXøq¨R”&’eŠM U†’c3ÈC™cCH`MAÒ¢¬°9(Q†bÊ”MG ®‚   n!§…?å{æ¿Û/ÿè•BŸz¤Ð…¾øWzâßÝË÷B¡þoí};Ø{œŽa‚…£Œ0É(-Ê “M‰2Ä^Š*@I0ùT(#LÀpTJ˜昌 ¨2”“2eމ™À’SÒ¢¬0I•(CLÔPTJ‚ «BaÒ†þÀD+CÉË1´ãQe(9öº<”9&vÂ?ahsf£ÀÐN`I/gÌFCLþPÆlT`3.mzKó€?[ þûøÙ JQ^"Ág_Fß“²±Ë„÷T…*EI0QU(#LÖpTJŠI«FcâF¢JP2Làt”ˆ1kÓË1!ÿ·’%¿¥EYa(Q†X¡†,@I°8T(#,pTJŠ…¢F—ãCJ°hâQE()š•€*Ö²ŒÓ]‚²Â¢J@•1Vw<ª¬•ÀòÀž€2áG Ŧ@iÛPvmx[Ê®•bñ©QÆíQ%ÃBLgÜÚPTAÁ _9fm©àƒÇŠTŠJ`Ū@©Qe()®êofL¦£DXìq¨R”½eŠ…*Cɱä¡Ì± $°F @iQVÓeˆM!U€’`sP¡ŒÄ”Y«A ߎ¾.0¾ËskÿÝ}²ÿÛ¾øßÝËï— uÈ{Þ_éuWŸ+¿ö¿©Ÿ ï‘FøŒ„3QL”{” e„=*U„’bR£Œ1"Q%Â9(K"J‹²ÂdR¢ 1¡BQ( &– e„ÉŽ*BI1ÉÔ(cL´<”9&[K8E9–v{H+PZ”ö#å?ai‡£ŠK[‰2Ä„5„ îÔ5ø’SNà­ ì(ü1qãQe­F ¾”¹°ŸÖ†²jU˜ÌFí¾gd«QÆ&‚ç«à_Н‰ñ± ±Ç„¢´Œa«DbÒ‡vúƒ‰ЉŸ‡2ÇäO` @iQVfÂ÷Óðß`1„¢ P, Ê #U„’b¨QÆX$‘¨” ‹%%‚‰C•¢¼°p4(S,žxTJŽE”‡2ÇBJ`Ť@iQVXTJ”!ãÓXÿœoýœoýÏ÷'¥Á?žoy±+$§¥F•¡¤˜¨j”1&k$ª%äMG‰0qãP¥(/L`p€IŽÒ Œ0™Ãÿ&· eˆÉŠ*@I°T(#,„pTJ‚¡BaQ„£ŠPR,5Ê $U‚’a¡¤£DX,á¨"”‹&U‚’ g(CaO­¹À+6Ð’œqºK[ÌN[i +,ÕÀ LÒ¢¬DW ÇÀb E ÌÛ l Œ’añ¥·¼ëñ÷D•¢¼L¯h|­XŒáßZ‹R2 7¼ ñï°@Õ(CáÎ[gÁ‡ ÿ·ÐQé¬he(5Ê‹7UÐMøž?¾~”r8ª%Å‚V£Œ±¨#Q%(w:J„‡*Eya¡kP¦Xìñ¨2”‹>eŽ…ŸÀŠ_Ò¢¬° (Q†ØBQ( ã{aSG¡¤ØÔ(cl TJø¶r8PÖ·P,-ÿÇÎ$ÿ;zâÿ‹¹Ö_íoÿ[çZ¼ ¿ožðz±O¡¤Ø£Ô(cìQ‘¨” {T:J„I‡*öÅ0‘”(CL¦PTJ‚I¥Bab…£ŠPRL05Ê“,U‚’a²¥£D˜pZ”&eˆ‰Š*@I°')Q†Ø“BQ( ö$Ê“3U„’b’ªQƘ¨‘¨aÿÖÖ6R¸[†Ik*|o“6O&®9&nK^J‹²j#p©°>QjLfãvÏÇDÉ0©ÓÛ \|LtŒƒ2ÂþŽ*è(xÚâß¡Œ0áÃQZA˜øVÂw0ñµ(«n‚w þ^X¡¨‚î‚7þ;”C8ª%Å¢P£Œ±0"Q%(H:J„E‡*Eya±hP¦X0ñ¨2” 'eŽÅ“À HÒ¢¬°”(C,¦PTJ‚E¥Ba/ G ÷j±È¨C;ý³èÏ"3ÎÔÎêMÆÂk±ò9 #‚ú[2ªŸ¬30Âìá~WâWÇÖ•ë}îö]•Øyçdòðh§Gi6ÌoÅå;ž–y±¤a•v›ÛgÒ)­;㫳|jÖQ“VÁWÚÜ®î_24a/îM&”oç¤÷—g¼=—„ÿ¤¾e6@ý²hÆ9º¹ähit1X>ûS¿Ð3pâ·—×Ü'©Igé¬mc²=ú‡‘×C÷½SÛ÷3iQUpì¡~¬‡ò£2È7É‘Âmgàà3ï™N©É­_ã—¼ëî5^¥,ºs çÑ蹌ÜoFGŽãŒ¹~¢Ñ‚ê™ÄÿF•è_žŸý«§nßÒ(™PžœD„XxlžJ¸?õ£„®Ï¡çPæ÷ã¬'ÇYÜ¥ûÒ3C3ɮӋ¯ö? wÇ»9:™L˱ ¼å”‰s¢M%Üšú0ÚAÚ· ×’ülõþx”#Òu¼*n¹Œùãøw|gÊL2÷·Ô Ñ7ωù™¶'’Éõí—Çu‡ÃtÑàB}œô~ÉÜ߉ÏyŠåýˆ48~c±N&±­µz%9Cª¨*·>HD—&ެvÞ¨òdr˵Útëw ¾k½Ùçß ®~¶¹±cøC1å=S>H¿ï}»1ιK‚IÑÙh,=Æ“ßî_t4 }éèõ«/ô­ê<‘pßTî£Å}œ¸/ ÿyîÐåkqòßñŠ zKî¯xàü›<‹¤púôøó¿K!Ó>nlÜd«?|0 pÇ1’‘Ì7Ø–q*l€s®8‡.’ùË}ç{„q¢,úμ”E¯$…9äCqËŠ[ÍI!¡7®‡Ú? !û×ÇÍC(7QÜŸò­‡é¹£ÜûºS¿2G‚q(æ9›ÜWçLØ’Qw›Û¤’ÎSÛ¿J¯$‡SŸ&}z´r4y3N0È’ƒ©}…­òSvÀüˆó´¨0þã„âøk§¬Š¾Q9›nwêù`#O5;•´¬oãšã$0ü÷1ÉAÄw¯*£w­‘Àý{²”NÛNçHõãS_~:n$Žûø‘úÓÒ{Yįè•W¯žçA;ÎR]óm*™â{§…›/è0GÆA„~#ô¾]µ¼&7ZThGóÇÙ%E´ê}9xõѪȄó°vêˆúŸF¦¯+—üÒÓ›q  õó,ÚÕ©÷!ã^•z»Çg>fÔGƒãMm™Ôét¿l2yþРÇ/@JÒĤo'ÒåNyB“@yËܺ£‰¥ûâ×–‰2 ƒ::î{ÿ PžX0óaz!¦>v¿‹wŸ|½¨þ{Öï¨oYÆé¿èîîÈÙÄ+©føºQ@”Ôïí £CD±wëÈH7ظS)˜¬30^!||‘ñËZ '=?šû‰Q~•¬­d}åÐ(Ç g±$~Ë_ËÙä­4úÖÍÌ p:q™mŸCÄûð¢ˆK—]Ài¢@ø˜ÀùÌçO¢÷K侜ü÷¡<2(ÿyŠ0ÎÄÒ˜ÓîÙ„rªµPh°4¡wâ!â1dJe=ø-½ÙÕk&Ÿš)Úóc=™ß°Pž¤™ž#Çóå\`~žhÚÀï|Ø$ghðñ}onf,®•Ý´Œyˆ]EÉí\ ïàƒöª¦“I“n’[ã®y³<ïÁê¸T»¼¨ÏáM÷Äü¹HÇ·dþNÔKŽqj÷©áü6›xÎÚ(`Ž\ ®½yÒí0©4 Ž©äˆ+ [5Ïîæ©‰„ûÑqlΩä¼=êggÁü)70Ç¿:Ýýsa½ò¨scÒi¯´^‘>ë0 )08”¿Ù~[ÚòÖmãR ×sÛ9?Œ¿~Î'¤~‡€öWÊ)Pbœ“ïέ0è“CfΉ~÷äú?!Æ·‹_ú9 šâ '¾Qä…‘-#fMõPzêyݼ¿ò~º£‘\§|5 ŽKyñ9Dn-ï/‚ûàúaßìÓ‰ebš}Ão¾0¯~ðͯ«G3¿áú¼¤þx½™?ó}1÷-vf{¬…Aæ?Ëü¼„8÷àŽdnãÛ^„û!/‚æj҉ߤsµï½ö‡{©EƒFG„O»iáp \øŒ'ßOŸWÜOÐätجÖul€úE³yN¯bÉõ†y¿wÈ!1‡6:Nò¸ÍnÅÍp„4~ßz]MþÌ×{IÞ~{ÖÒežÌ?ßÞiÅž¥·d0ÍÍÄ$!ç›Ø«ñK›Â¨—bÊ÷ÎåÕÕ Æçwdû¸²måŽkŸ–\„ÒØÞC!v^óµô>ö|ŸÔ)”´ë¶úp»žÌk°Þ'•ú ½cþdïij]¢¾Ú,óbüÒþ´N0ŽÐÅZøåãäa9y¡IÖ¢)o›%=w¬ÿº|ép(9Ñ£²AîÒnóïÁƒa„ž?Ë9Üó}ù¼‹ú¹SN±ã®{5%9äʗׯ.BÿÈe5'%ªƒwýã¹2nÆ8rõ€ð€”å{ Ðó<¹ÿ÷;x%‚\\êÈ|¼©Ïp$Æ9%`msÈÞ¯†Y‘&— ¢ÝîþU%-?Z=Tr‚Ö :å?ž¤  ÑšüzŸµ³ÇŸ²RªçBñ~ïP×vÏÊ^î`,Øl.£þæJŒsÕw‚ù•Q9¤·¢dKQÀ%˜~h«i—{GɬFJÂCêé{+Û+å|Òó¥µ#Kêm+¬ç<é3?Ífë1÷!3<àw3Ôk­ŒC99äÃ6U¯÷ñ—`àèÏŸן§\ôþÀ9èºúÁ8Çtòv¶É!ã«PŽI¹Õz^xÞçYúv›qöƒò³‚ÉÕ'{öTLóÒûóSލ˜ñc‰ùO:o³dþu”#cлXòÆe°¯A³"‰i'ÝQp ܃W…/ >F¨OßøY­ª‘8ˆñÔ0¯[ OFW8ZêjøÐ¨¿­%œ;%yì:®ÇÕÙqÊ&Mj7]æZý2ØŒÔVOd̳C5ó§Ž€Û¯?ÝÖÏšùÓÒ¾ Äqžw œ™p"›<œ?¤]ð»Ë`V©S-“çÇÉ´ÕßnÖlìÅþùüù}Qš£ãWÚ°ç(]ipœ0'¡3f“RAæüúW`Ê‹{8³>A¨ßµÕ8~ҵ̮©aÙ„ú­^µg,Îj|O’ª+4?µÒõ‡`róñ©7óLüiâ¿s¨×Ôóåÿaï< ¢Ê–½abÆÜÆÁ€"Ž$¥«DÀÔ’£1¡Ð1 cÀŒ*ê8¢¢â˜P -†ÝDAÁ MÁŒ*ꨯNï½ÏC¾qÆûÖýÞš»ž®õ_Ìâ^ΆÓUuj×®S?›42//ÑýÕ©« ¼ÚÓùÃîsÄñÞñ“Z…r1Þ„L°6Ôéã-r4§M™ŸjýÍó|ð:4?M#£ßÔ°tç*”˜´N€{çˆA’Üi]àpx è#˜q½¡þ°A±/8C§Ãu¾s×(Ÿ‘ò^"ðzÆž³³>ÝK%烎.øµÎ5µÑo2¨5!M;® ’j ûì`«ñäÌ£¿Gõ 'ô-»:_uª˜Wçf=ø®GRÎ;¡ñ’ÍÇuÚŽ Ô$-•¬¾¼{]ÏkÐðåøÚùv„ô˜7w[ßѶÐíÉ-;ëÊ BùcY5ë $]ÙöýXÛÞéŽy†3áyß·Ñk»´ç¥P²íܯ‡ÝûxŸM¹ýE^8ÍgÔR·v¹„è°9ïx}£¬L÷‚+)JëžÎ½Eƒ:-I&4Ïwƒ_OØÔ¾«žFF üc€û®1зä]‚*~ðz åYª¤|^·ÒØ7^ŸÎÙO!ý§\›¿ç:l³¸m0"™Xö5 ÛÕʼnq}¦’÷Æ)jl ”ç= Ÿƒq¸K¤|ÿBëÔ>é}Rã:çú¤.HZ˜Bæ»Öþä™u¾·HéøÃÔdâý¬Ø°í¡Qp~q̰ßNL K=®œ¹ÛÜ(e›ûðý ÿ\¸?Óù¨tž³–I©lTÂ's“I)dßÙÈþ®Ã¹vù1q«“IfXçq[•v:4-Úvg ?ù<í€+›ß;@üÜùþˆ×µh§×—àõ‡™_Hž˜BšV¦^ZÐøü°=ìäãÝɄֆÂ4õµiA݃ÉÄnÓ7¥¹fÜasÆm6óŠ}=&/Ü×àƒ”ó¬…ÝЦ»Ý¨Ÿà:O›„,uB$öæ\4¹ßýœòôÙd¹Nó«ß(íèx6ÇÞ%é\‹ðZ¿ò‚žƒcÞ2ÕfœŠ&pöüæîÁ&97-¬ënCý×ižøàöɤ¢ÿ«¤!>7àÈåg;Ö$“m®èi{€ k¥!½^š'ÛÂÇÆÙ-J?>šZ2Iœ¯O¹=5`§ö¾[…®cĸ ñ\ÇnGï¹ó&“V¯ìž%,¿~-¦tx›L&î\’ô±™)Òÿ(Ê ×ßï}Nž[<;È ªƒ½óÝÇYÂ]£fG â+¤;¥“R¡ ðº1Ÿc®ñ\'Èzü”ò%Éd¾SêüQðïÙ!³nÜ)…8Ž2<¸ ãÉ£Y¾N¼ô#MwεlPÏMäªQ®›©¸ï¹<šú†%ã¤Ñç×¹ßíÓÏäG´«Ëu3n@î™b¯<… ØÓRkó*wˆ_ãl©Â¼?$€x¼Á7ùf²2èÙøIe_,8y?sÊÌriÔ³~;?¦½”rž7̸"¸NbWÙÏd²zk›`ié HóMÛœ´4…˜tëzÓÚ‹ÕQýˆƒæá-΋¦Žat.ù[)ý»*¥3—?:¸ðà`æ×t-ÓR™ïä7Ó¶·K&šñÏM @á;åläâùÔ·oóopÊ´ÍÇŠ¼sÊ-q‡¦Í®–ð„ð_>õ©Åêo¤œ_M÷£”Ã!Áu¢›´Š°y¨$/ì¾o:|@Lݹ·éá?RÈOjê^>ä s­‡/ï:Çxë=òt¾3Ô[õªÑðÉNP³âýÇ‹+پ╔æo¤%Ãüç¤80ÿ¡a–£KOÛßQn’×?­_ëS•dò„—†¥À…®……6/SÉ2Üu´ pLjÙŽšJ܇O”jä ¶ÉŽ™æØ°8j”ÏUÆx, Äóªœ5®c½»Iÿ÷ƒ”¤ËéËFÏ  ²b^ñšid­ãô®›>ÉáĈŠø–S‰þ–º ™‡G‰ùÖ©Ö“•¥Ë,Åýç”Ò}²Õgõn-³RY·²¾‘ù„hÂR»BøcÐ#w“4r¬Ôh@=•ì{5×$εë|_ÌííYPвCï^J Z˜àxÀž=g©]KpVš$Dûಾ͆²‰ï‰iŒSâ{^Î\|eQ¡u')«ƒãÍ#ßJáÇ{éá?}”R.§3ãƒS®  ×qY”~;çð9’Ò´±þÖi…p«v®v·Ei¤ƒ©½ÃL“ìgÝ(ôϱhïÎò=GƬ#rŠfe¥\ä¸iy°l©ÿà:WÞ´Yt×è©—ÒB¿·¼+“ß^=‚ûÕ¡º•¼=Ào×’áÃ]Iã¡®Ÿ™sN6ˆù`Ãk?™¦o¾/Í»àW×üÓ{)ÝÇËX™åi¸ŽV˜Gĉ³¤UçÝ……°éG“œ¥¸Ïî´ cÚâec¡AýŸŒVww%º‰›&í=8(—o€øùðó.^?¡Ü*`õ&šÄâ:Ùy?ÊγgÑ:×?‚4/0=»O:y®SnpÃZÜ¿\¹ÄÕ®ži3K.òN9˜×eøWžÒ¸NëŽJ\'ñ˜cØ©ä3Ĺ™uq]ÉMØbp,×1<.1[zÂíß'0?µÎå<žÿÓs¡Rº¾ì3~–×yÛ\ënëÎí’#Ų›`˜1ó`XJ:q1°¾3»¶xlÍxíÊø%Ö°sÓèÞ'ÙÃ< (ĊŃJ‘WÐcCùâ1 Ö”úy©ìÔùËËúÄŸ&m¯;µ\â}œVFú½¬“AÔÆ‡nFÝ ôÜ$Tä±0«È‡Ží6ÿÇÕÍ^I9ï•Îïý¿S‚ë¬h'aO“ óÖåm » ô¬‘Zfnþº‰/»{°úÕdöüŒ7Äx†&bþÆ9)œUuÿ!ÃuNèÞ‹]¼9‰PÐMXïªcJ¡H'Ðà›ý”~–À÷3üs€¸Òωֹ}p-s/®~xŠÕ}oB™püAp³ÞÞ¡¡ÎÅõóêìOl6æ9³¿ƒø¼¦ñÔøyßOñ¸t-Ç£¾í{ÊMŠÀuò£·Í—·:Eçö“üxö&8v^>ÆæHéý¾^Jh½aŒSæE(ϧé79hìlàûoÎKZ¤ð½0sú)=¿Vw£O,®“zJW)ˆ`moîß?¯Ó'gg¶úç}´c|iWÆáøAä&QήðýGÕó%^7fâ ¿“'ɪígß6ºvNæA†Xf\TÂøéÄA8öka.ÖMøþƒŸ«ò¼½êþCëPþÕIrõûS©ÝnWŒºoríó$nX\ám'9¼î2$À-Ý“ÿ4'ô÷4ëæ¼^ÊÏMhÜdü¥2cÍAä vž} Öܯ9HGÿ<ùþ€U×·ZŽð|ß/¯£ñg} Í?áçó<¿åu4º0zŽBëó\ÇâD ÒÝþ!‹~ðÏŸ~ õNöN’Ÿ'§êÞXo}ßtÎnî©ȸ½Ú"¯†ÿ=Ünùó“òùýÊö9¸ÎÖZµÃ**“rtz ÆiÕúÅlÁyr¼A«Z2Ü X(#5ô#ô¹aÊê¶–"Wˆÿ¼O¤Ù‚æ3tüÙþÓסû¼ã¤É÷×]nAHÛ›žÝÏ“±;‘.—0_ê?éÓ{ÂÏy½œ×Ïyÿ»8çÇxVñZIãe®ÓÕiËc.ÇȽ‚9KCsnÁ5· N{ž'­¾÷ïTbï &Ææ•±Ï]&r¶x>ÈýƒÛåÀËD~ŽÆOp gnÿ¸ ‘ÐzÛ-È ?ŸÑ1“˜Ž¬ÿ{‡ x™ Ÿ°eÖDÂù³üœkÍ?Œôº8°çòÆ;~#òžªÚ×I›mºðá±£$©Ÿ…ïòºEðül½K2ɇ}ÃW)Ýàò^çŠeã&/êŸr¸´H>('1 [N½³°&p.å*úÂòîò´ÎKGP¿Áu¬'/m[þöyT+kà2£"èó´oËÛ 3‰Ö˜w/½w»À‚”ƒFEòžú6ÝfÔ Éçš'šÊ¸Rž;Õ‚: HŒ{ ;t§þ3¨TöÇžË!Gȹ‘¶ö® ×à`&±QÞÈX3× ®› 'gãç$ÒzÉh°Ï]nql­3;‡¬ÍòŽï€Þ7¹ø<Òø®³É¸ÝÆ:óÈâÓ‰3o-)‚7O»Tûj&Qä{nšááÀòAoÂøÃ„ŸÓÒ|ƒ×ñ+¥ž¥‹gDvý$¥ûbnæÔp .Çü0¡q½¶ÕŽ˜þþu&iÿÜúSÒr9Ðs†¼A„l¯E† ³k{°<`»±¸¸BJ9€R~Îö‡×iÕðЩ]Ÿ’ÅÛü®IrŠ`ç9eÝú­²ˆÁ¡êÙ#G;G"ì¹Âî¯~Ûº’<­ç ö¸?О¾°ûS)çà|ÆaÇuF ;ƒ¬Ï¦^Ž ÞtÁ8‹šÅÕ­—ó> 2µÓZµÍ’>¨×øtkv>`¼ŽV½€þ~Ô®cq¦Ëg'-8@Öt¹~ÇQ«¶=œâ8Â#‹ø¿ Ÿ{=Ê2V´žZîK\Á8km¥3YŽÁZë®ðsÊÏT‹@Ú#cu ÆïÅuÔ=GIž4ßO4¿nËbhçR3Lº4‹8ZÍ4‹(tƒOú­»´ ¡ÙD¦Ý´Û„¾P’.4jù³<Èžpn6ÍcGŠY[½Ý!‡´~º÷xÇ'¶Ï@ÿÕ|¶¹k´áÏ1¾¯ÒØ)^gû…ñ£²Ì}Ø’7ST乫on‹äf?r/ªƒ°¾&²?»G~õ†ÚßlÍúƒìŸ¬±K¼^KÃ…ž³~ŸNŒgthñi– ’§”]˜p‘Äf¹÷_½ÐGÜ_°¼ž;nö8;ƒ1«šiA©ŒúŸ/iw<¥âìRä¿Û½¶þÁ‹DÝ:SgIO ýÜN$6~¢›£»5)MM¬Ýºî[YÌy‘ü+í3¦õK ^Ÿîwä`9ßxZq´ w>§xûø"Y&àƒ§Žaõ09auDÂÏe8Ï›ç«ûÅëÑ:Üt0T*hø¸ÓŒÇrÉz³G¦Mtz~;” ÎËÿmãôÂûŠyÝ÷'ñ~NÎYý,.ã:mâW-Ø>u¤ï?0iÅyøì/l^˜Kt~ÓúŒÓh–WÚ³üÀŸ<)îøÜ¹—Ë,aºÃ¾.ëÎ?•òs ÊC¶þ<>ã:%+ûÞ¼»$¶a”ëpN=Wž~ð.—l;xQ·àVi$obM¦wéÝóÎt?ñséøêF¯Þ `}/¤ü¾u7Ïî=øÈ1OÑø…¬TÖ°ÖF¯ô;› ¯gÿݯNªÀبpyQûK$Ëe“cM?pÒ4t '÷‡tWß°õ#Æeæ,ècÏê¶VÌn_Šüø€„;N|m>ï#’ñs«­à=²^¤BwÚ/‰Šr‰øXe·0ÛîÌî³áûn^?žò¢ü×Zٖв0Lz{ÝS)Ïéù -«_²}0®sØkÊÖ}ºÛ!F/»bXš rì.|?ã!¿¿8Äȃݟ±äîÞš.G„®¿¶ÝÍÅý@jôú¾G=ëÔ´nÁÎùðúšrîƒXH›;»Æë|@Ùã&ºDj—ç$  „c÷®dpYì0ÓŠq„û¯ƒr?áu{ *.nÙŽkg–´ZÓÏ2Õ‰}Ù~­ë×°'-'¿µµŸãöÞFkÖ= +¯¦ŸLÞRƵ+VoÖ)íç`ç²ô^ëÔ?)U?†w˜xä– ¶Y÷ÈøÕ/¬:ö¶k»±0ó¥wœÜ†÷[ÚWfÏÞ›°ÛÃg¶¼¼ñHJû¹ÞKó-7…–ŒüœßlY*»t£÷¦Õ¹ûàÂäq»n« elqJíydæ…W¯ìt‡Ó›ÞßÓ—9Êö"|¿Aû!MÅuî®|Þ©ýHVϧחàõ°ÐײÚw4ÈÕ{ ‚ ­m¯4Û˜G¶/¾kµ%Å <0ÛÊKp#”7ëÏŸ‡b]ãx[K˜¯>vž^7j½¦Tí]÷»ìË#•‰:²rG‹öºyá…Ê7­ƒˆÞ戱]ìdÀë|Aó~ú^^—öQõ«Í‰Òû*˜}ôõå)IyäaÑ®§# F3.°œ¬l{2f{ÛBŸî°vÍþuÞãÝÅ>m¾á<áªû±\§µæÆ†WÃÒTÌpàñÙ@›‘›'þ~?_¢×SKù>“×ñªžÇâ:WÇNPÏO€ÈÅÏ?4º©‚7£Zf—ä‘ÄjäM{äÄû{‰Û”«õ¯¼ !¼¿‰÷Sóu4öŽ×Û÷úaxŽìì,=<Ñ{cô:ŒyHåÊâÁâ½\öªqçñ„Ÿ»ò}±Æžñ:ºá{Ý£à5ëw§eE7ëé)65Î'O¶1»sÓƒõµ'þõ&ø?YD|Ât~­7½æ<ÕØíàR™ùcÇ5YwŽÂîÞB RÁþbeŸ«½òIJˆÐæÍú‹‡¹Vzæz³q¤õ'éÝÌ»\sU­'9û‰}jÜ.(‡Ú„'Òç½×IýÝáH¿Ã‰ðH"4¸©à‘Ëô¨¹öùäˆW7µ‰cùóƒ8›Ö×°W€È¡§~7}},öñVõ?^ÿôPc׋aÇÀ¡û€c5ðþ&´´¶{’O:•Ø·M\çl_NІN‘dàKx_Rnj½á{g üâ<÷K¯Y)®ctHaÚÓä8ØX÷›]+G}C¯®Ê'î9çnWÜóa}£f¤ø¨Õ©ÇÞÿÎ÷ɼ/Ž÷©ÒûÆêœxý;n­Næk€÷桹‘çÜ£»Ê'ï—Ÿ‹Îöv~Be§­VÔð#ôÜ\ ¼ÏŽ÷ùñsIþ„Æ®ñú´ŸéÈ3* ,TÁL§¨w6—òɳÛE¡ãm|€½ßžSAdlÝÜÖ£o„aq 8øýáýw‹Ö¸Û5ˆõ³>\'¸®—kŽöI¨Ü×óÝlÄêÌßèy>ÁÍP¯ù•cØóÍ–?N`ýf@ûnÌDÎ<ÏWùyNÕz†×¹´sVOõ¯'aOOô¬‚¾‹&üÜ·Ñe2Ô÷âÉŠÞ"×{¤¦A/„ðº<ÿÊã ?÷æçtßLï›–U©ìáÜð’. ¸öôI¯Æø¹œ›–7¥Çeò6¿E“^žbk´~›…~®ÅŸ÷¼ÿ‰óì©ýš²÷¨èç/Áu/Í‹T@›•{NVAÿ‡»õ®[]&/ÏÔ/9²ÆD+ôIaØæÂÏm9·ž×i?× ±¿®j•á:;^•îÔ> –äl°<®‚[ÞØ}òºLî–%T úÔ¨Üë´\ñí?n9÷kÃÜ'}ä~ðΧuۤ¾0¥·~à ?„ˆï¿°}0ðþº¿¯ Æî»:ìÌ–[Ž›¤ïåªqŸpáÍ›Ó`¡óÓÛ^*¨Nò³©¼LLWÔ}^§wï€l l¹£ ~ Y¾!?³Ôúœï¥kÑÃÎý¦ÓüŠº ˜Ï~’²ì”õcŠç­¿±.•Ñzæ,i‘üÉEáM'žTtºBè¹r «ŸŒ ´?i<áïÕÑx6ŠÙóVwùh~ã´ƒÚ™×éÝ[ѱ\yÏmèòKˆ ^.Yp~ϰ+$Cw·Ÿöî1®Ñl ƒÈ(ÁíZÊD;âq“Þ· ©SM¿¦}îYš#ÍÆ,r¤v Ãu~šYxxàYp|üÁfàdXí8âv…¼èºæ¶Ë?ÖÇ=„Ðþ ÂßáýŠü}÷_Kùû´þAíÀ× ˜±¿4îÍYÈútï¹j´ N0¹eã2¾qzŸ­ö>¼ï€˜{Ê‚-æŽ'?›(sf•;¯;\ÉVi[~ø í6ÜÏã¾cvæøY~ëÐzà9Ø?çí¶îý1ß¿²eeÀ±+äiÚg6ódý‡ýH÷Ôsë\Å8mü¡pms_Öî Ãâæëß9ב}ޤµ=’MJ cu3?ê?¸Îˆ›NÅ]¶¨½´Í’®*¨¿i{à÷¯„E¿;{]€õÏ‘•ý—šYnH6ûÏvµð #{Þçíétú:ðŵ&ÀßãyöÈ9`ºl0ìÕ¢ÿ¾ÍBú6 éŸ6 ‰Ïéþþ<-ÆC`3(µ¿07˜qTV‹J‰ÒGCŽBUÖ¡¬a&’>ã¨~-ߪŒÍX-匕Á¸€†Œ×’ÉxªUgG°y¼œ©ÊYW Õæ#ýÝ<ʪLUJ‚Îɘ-OUÉæ’D1–ª›Obø3‘øÌq%›Aù5|«êsáªÏ* f3‘´ßJÉf"É«Eਆ2¾•0ɱ§uû|>·÷Kl†/1®|¯¥*ZÂxª|ö8g¶Ø~ƒZ²Å@‘€ÒØ ¨2 ÊlQ°Y”ßbç·Ø¡õÏÚìï+Тs˃Ù<9avùŸÍÄ el@×àƒÊD¢!Ç0cf jCÆüZÆL9›ß+ð*Ù óHƺ2ĕ<Ƭ:Ç7’ÍÌäœ@ΞQTãQÿÝl¹ªœ@%J)ŠqF 0ÓÉ1çf;å1^×Ô|~o&›'÷w¬š‚?™ó¤FÙ¢ƒ& ôØLM›''Ì7Ïd j7ÆkØ€álÖ¦À öaÇ—sä_ÁU-CÉ1P( ½ø=T¹å6(Ù\9ÁF…ÿ ±ó?1n~‹™ÿ³˜©Ëþwí”où€²/ðm.`%ã=D¡*…؉œ‰2D#ŽªÂüWøÓ6WX`>è36 ‚±¹‚™ñÛV›/¬`Î ¯Æ¡.CÙ¢s$ ôþfÞF’0ÎM9ÊM˜3ŒÒgŒaæ¹0ƒ3“ñmb˜S£òP¦è\±(m þÞ¨ ?,ÌÂûî´öŸÌ9¯>w3–q§MÙœsk#CGD0®M<ãÚÈBà¨ÚVcÛ|‰ ø%öt ãCTe© óÎc«Ì'挈ˆ¿á©& ô }†ø=”„ÂBè㡌afžÛ·<ó[ž©õÏŽ™zì÷Wk±yë(J¯æŸÏ güBŒÊC™¢!Ç¢´'B`P›2–á×rr*ÙÜâføÂ<ö(Æ“1VDcV_Åæ„êWcæ(«ñ¨åè0 ”&UŽrCçQ¢ôõþ›s(0s2Q†èL1Ì¡fNÊ”±"vN(ª€q"¾Ä æs‹N„éW0t„Ùí¶è” (=tÌTJŽª@IØŒ!ðt"ØŒQ=Ɖxª’jsF¿4×ýKlPÆŠ¨ÊT5d E>¿˜ó"ܾ‚«ZŽr$ô5ã÷„9£¨J Ê‹æ §6ÿI³Þ¿ÅÍÿ[qS‚ŠGéÖ lÎR´ý['ž®À¬ˆaŒÊ˜hÈ1UXŠÿ ƒZŸÍS†Œ§¨¬Âæ½Ë«ÍUV2ÆŽ[5u9JŽ¢@IÐI"Pe(9:‹%A‡‰D•£ÜÐq”(}ÆØ©xcÂŒe”!ãëÎŒÊclaÖ»6:V(ª%C‹GéâÍ G©%tör s¸¯áOëþÉœw=tÊTJθ:匣Ë’[TJ͸: ÌymCàÍÊ«±u¾ÄRüƒ:–±0ª2g…yïñUæ3sFäßpg…wð{¨r”›…Ð#?+¥3š+¥”Cý-ßü7#´þ¹qSŸý~eÂçÌxŒJ”5U†’£Ñ*P6{¾€q2BQ(r'4ÒHTÊ5 U‰òA£Íü/öÞªÉlûûÇ2 vì±3ŽŽŒ³c#¶ QPƒXbDZa°2ŽŽ±ØQGÅu°žØc B5vìØ±ÿ¿Ï9¹qÖxß™{ç÷Þß{ÿºÖg­«wM${ïç´ìð@àÆpÇ¢Ø2œ”æ( ´ê8àŠÀŽv A€Ç7¹d-‚=;® Rݓ֞|=ˆÙÜ ›²¸{1 d÷ÂfqÿbpE‚Dpÿ¢³óÚ¬ÀI Hœp`ãÎká`Œv A2Å7$”dJ~X$VP"¹¢@ÐUú´[‰¤‹™@‹äKJ$`È:$¢¸#c@6Ð#)-À‰iâÉiVà‰$6àŽd’Ö®Ü,ÀÉk™Ü•mä>XO$sÈ’j'’:¡žäÄ¿, ûVrzI~*üȺ’cq£õwNX w8Ú… ¸¢„ÿ /¬E"ؼ¤žDø7àŠ‚¡’z‹àõHÎéØçúT½ü;k¤Øó’êáŸÙ÷úo©{¢Ö‰úöwÍˤºõw×,é}1»ÈÎjꓸ£>Å€l C}ŠGÀ¸!`Œ h8ñÀí/zªmÒw3 ËîW£T‡hJé|òwjάÀgâÁgVà‰ Œ b8°52¸"(#€hœñÀ j™@‹@MJkÈ:­¸#pc@&Ð"€ã[Õ?çžvG‰Ù@Zc~OÉ7/Ý{“îmHó/$ƒu# d-êF‚‡äpÄ¿¬Oø¤µHœl'÷«+’Ç<ÿ„ïÕ <½¤þuø7$W8°µúFá½®$û£ÿ'îú~žc}žcý³zõgj•Žÿ·R`j@ °O©‰ªX'Ö²€'7 dØ ÜÄY@‡@Žnf#ÈZuP"°£@Ð!ÀÍÜe¬RC°@<z=0ƒl FÄ€léûeH„x 4Hˆxà†¤0ûïÜÖáÀÔH–8àŠ„‰v AâÄ7$d-’((‘HQ èPfàŽ¤ŠÙ@_éÓîkw$[ È:$¸#ñb@6Ð#-ÀIhâ‰hVà‰„Œ $e8°5’ÓìÀI²¸ Û¬ÀS:#YÜ‹ì@$ŽÙÒ÷ËÌfàŽ„ŽÙ’? pGrÇ€ìú’ÿ<è1À<ð&žôZ`™ÒÿFˆn(Àæ)õ«Ç{ \Q"šI}˜ñ^£0¸¢0DxIý1ñwˆx঒úÒáµ€ö/Ì« ¹ü=çˆÎuñŸÍ­þëÜïçU¿?[ü£úõ©Úõ©sÅ¿k^%ýn =@õÀñùHô[—ûKûñ¾rŸ 5Æ è4Þ÷lôAZg;ïyéQ: ?™6¡Z³Kl×úKÆå³»SÞ\õGÞíÍdoK_&<²ÂW#<óöŸ9¿dà[•è£êìÝÖc¿9žÈ+GðNêîZû>à{||MÁF¯Bè;¿ºUgfz—«­£ûñ>³ éºgÖÀÝ 8¼–Â"÷ÛkðqŸ(ŒóÓÒS…g6;D]?(T9žN³»Í¢›#.±î|½æ~TOîGõe_ß7Þ›5€‰¾7ÂË%úU.Ü¡Dð­Wª_ª¹n»'úmñ~ÞGî?{ˆ–,O)»~W:Òsöé—˜nßêu^y{‰þ·,ì¢o•˜öƒYæÔ×?6íHrÿð®T¹†ùutɼ${JRNÖòÚú1›1NÇÉ·Ž=;DÑ—v̰>æ Ëx¸lû%öjÕ…¨%{rOK^&÷ÌúImi–ö£Ê«ÏW·Õk2§Ì?8ë0åtKÔ§Ó‚aÁ.ìÞ%¶dXƒS]Çsÿ]–÷ºý··8ú ‹þࢦ𼄷yùó ^ùž•çl—_–O¿p˜¶xLÜVÅ3ÞÜݤ®›ï2[^íê·O ݨ€¦ÅtÌ;G?€ÉùÒØá~ Ñ_Mô]“½ðr¼©1ΤæÑŠUì+A\¿Íßvr¥Ë¬Éî¯êµ®DwžJÂá² Þ¢LŽWÑ—”D¿páÇñ/i¨cä¾ázŒS¥ò³ÁÐSKåB^/Ó¨…¶Ç¶• /³ô(ik¹òÿ±Ê¼QÙˉ‘™Ïö bâ}ý:ß]ô×qîOnÄ8õ?Ù:Î|„~üñûqo¦Ñø'‹³5—™¡c‹ CQG…_¬F¯ú-ÄDEãÊáW՚ƴ±^ûæ7ÿØS$}î‹vv||„&7:|¥Ñª“•m ¹Ì®ïÌ5ïéÑçæâƒn¡¬§&2ÒcÒ@&|ˆ¢?èÓ-úŸ‹¸ø¨ï,Æ é^8z|£´bÊ÷‹mO£Éùµ+tÃ/³m %íX@?ÞÒ ÔµPöã™ º»`Â*ú݉¾W¢óñ= sóeq?^BߌM÷´GIÝÉä±tQUo=oKÿ¨ËìCƒ…ÕËNÓÑqûšÊ.NP+l‚‰~r6µ£Ï‘ðú/³Ñ¥ímõƒeo6Ÿx”"v¦-)03ŠM+ˆÒr™}Ýuõq:R=ÄR-!”=[÷•)<€ï¿# ]¢Å#NKΫqô£ñ%¼Îñ«Ä8GÆI‹ÒØLßdp…•쥹f¾Ìîx¹ JU=U œ<=ÔÜ›{>²A»”(tÛ—dÿ_GGÿ6Ñ?qù" [M-¹Tþ\Ô'OÁçÏŠe¥ÞûúÙ¼ÓèÈ÷?Ü®_fyŸ¤ (ß›4Ú·n#’zsÏÌ &æ ¥æøÞÍÛ°% ߊOx½uzŒÓìœmñ`¯cTd ÛY¢FMHˆ¸¨HdW¯F.Œ7†qïo¨£›ðH‰>Éâõ…GLô…—ó…ûñ0Nƒ2 ^‰sŽQUd]ùÂiôª`…rzD–¯Ëþ˜–0îÁèÁ Uírñ›õØåà<Á5t!ôn¯jD↚8sJvÁ ¯¹w+I]Ç*éB^9,¹_ã¨Æžx#ñ W |‘J™%oô¯”Èj”XŸQàçÞTçyÅ͉!lØÅ•ï4Å0Ù/Jû¤a*„Ñ·×ݪÛJðçëUý çú«÷w}ìä¼Á8&­d=NGˆÌ¸–Jãw>8:.‘œéíÖ/HO›[åÙ>ø–ž={OˆüÜWäKrÄöŽ~³~×\'?ÉÇŽ4äÆž¯ngvŒÓ«uxȱŽÇ)ÒÿÖ­òçSi}x§ÆõL‰lNûùgîïNÞÏÍú9ú ˜ð™Š>—røgŽz)Ï;x¿fÍmõŒÖRÇÓãt>ÞmÔ–ý©tksô¦¼ñ‰Lî«ÀëÔ ¶OYµÑƒÞÙ°ÞšN5‡6rô)y*ž9‰²MýòròãL=2ú¥Kúq’,»å—§’6§h"»r~çÍâï´$÷“Æv¿©P¹îY“=ÉMy?æF$¼Ór_²*ù}kñ‘Vq.-ó{ðä8µ½Ysà€¡©4*<`Ò ‰¬AAÏf+Êu¢Íù$£æ0þ¼40Ù@$úPŠ>²·æ‘JîoÜŠ{%ä~ÚzŒ“£«h¡Y£Ku™Ô%•Æ– Û´éj"[ÕzÆüºáèUÒ““Ïw `;¯Æ7ÌZ3Õ¾~¥ˆ§š„ÏMøÐDp¹ìÇ>S#Æ .^cx …Fxü[‹–©´Y¿íË¥™uLÈ«ó¥ü¸?:ŒuΙh fÂëž'GÚÒ¢¯¶è{'?÷äß'ãìJý¦aÒ ßv:xTÅT’ûÑ'±Š,ט»©þ4ï‹-—ø„±m’6µâ &ü”rŸxo>{¢}ÜDÿè– ¹QÑäx3cœ}gg¾m0ÍBáÖž½ž¥P·.í²›–Mb˜Ì¢À’ܰ÷˜ªGúv[Í}³7¶¼f°5ÍÏûñ=TEÆZyõŒ¯#îsòãxº.ø¢ƒÝB®¿lü.9…f/™çûÆ#‰…õ~:|HœŽÖÜTÝ9ŸÿöâO^Û—‰y²èg.ž;r_¿·*á‘×Wr??—v·ÕW¿üaãT¿Ô÷hø(—#)d:¼u+ß$vP×l\©-žV}üôìÛœ®}„ÇŒÿüß8üÜâù#|ZrŸgùóQbœ›_m¾3nõ ny±ø×kR(¿jóðÔ°$ÖÄ£§éqp7’½‡ÃؘU»;U«Û— ¬èK,<2¢nËϽƴ³‹âFñ¶²w@qF~)u>AïÒ¬˜©¥PÝc-q’XéS{ä)¦£´ØÁÍý¾cÉc¼³X_GÝù*æbÅ}p|Ý(¿ozŒóË“Sú9š“4¼yÃ:>=RHÛº`×? Ž‘ûFÕÿ!®Ì„¥#˜2Ô-þFí~Lö¹uáõ ÀáOëQœûn¥ÏgQõsÏ'ž$¿Ô™~®^)ôMÍ…TqI,­¢bò¨/ýéB×η*OÁ‚L’\+ôg?Ì:–r%¹'Ÿ§‡’è+üéb~ Ö]9ùƒqÞ|±74uõIÊÑŠ”J¡‹~ÙþÛî$ö¬`ÀøªK:Ó»ƒæ®~ÇÔ ŸÞXš{ã}+ùçÜÁáMã‰:!êmNþ`œ‚˜}•¿t’æJØ8åV2m]ùþ×[ǓؚÞ]ê?ªÕŽæ{öÉs§÷ööíKûÎöƒ¸¯Ã×.ÆÉɼ^P«1í–—:EÓ=Ìœ¼;™ÒÖÔn“Ø*­'N)Ú†.®yðí(V|pȇ­ç3±.–çݼO§ïmuÕðêV½ÿ)Êi÷¾ ™ZŸ¥èž–ÄØ°úúÉ––t©uÃ=ÓlcÙÆ(©¡ö`Ö(aK]¯z$ç_€£}N¼ãõ¤Ý€âSO‘O/©óh2í™Ôogß›I,Zœü,°5%×2öîsbk˜Zêµ¾ì Vã쬸hÕ0ÊÑ!WæèÇ,žK¯íì·Tcý–”FU¶œ¢ÑãnhšL÷ËV¼×"+‰=ìütåäÓ*¾²ùÁ÷÷&°I~Sß·šo`“*ûÛ:ú~Š~Ãrßó¶$û÷äqôçÅDOMÔ“StõRþcŠ$ӾæǾOb},^KçkIî“:ŽUzÓ­Ž÷“¾Lö©}f…çAø äu˜ _N¼cµ[ÄÃ!§iFÍè7Ú¨M²K±Èb6ö­eC±6µ(´ŒwÑ …F³©þ•5Mû°À¢¦È¯Ô|¾×Òá…þkáñ”½òþN,ÆiÚ»®ê óiúöa¾óÝÏÛ(kƉ®ÝmìÁ¬sþo訢W‰¥ÕÉä>ø}Xµ}-¹^µ¢*Rûîë­yþ¾PÉëòg*¹´áûòüÑŒqöÝ Xq9÷:mšÚèìzÕÛº¡Ð…V6&÷;íÎýŸcØÃªºåïϾß]?®±§ ?’ì{ª’ëoî3ë¯?¢ßÞV•뜡EžMb£|Ã/™½{ؘۓ°Zet£wõ’VaÏä~»&Íf }D-÷aˆ>ÆÂ~ôÔÅÝ«ÊóS—ö·ÕFßEîãÛŸ¡¸£öД&6ºä3ªYÌ(ÛÐ,z‹w| î1pú¤õ“X…EËâÖíîËdŸ]¾»ysîÚØ-P›ßðX%ž³ë$íÁ‰v$üs9yƒqvMŸ`ýµÛRlòÙ–ÏÝF\2r˜ecu̺nZÙ…û'±Euj­z6CÏdŸ{ÊÑ×»uvìSÉþ£ç*iõZþbkq’“7gDí².›ÎP­Õ½‚é]¥Ð¦}Kml2ݳ®¼ß{H'°¸ð¼K6ïÎr4 Šž${@Dß÷W*yžø\%<‹ÎýeõÇkFÈÆþ-ÏÒĵ©Åw'Ñà}µó½_oc²_§ ¥¦Ö=‘kï8ÖöÆÞ…{ú„°9®•†TØÞfÆ<Å Üàð’‹yˆsá©ÌÉŒãöàÑàùgéJ#IH’D×}R[­Úfãó·fäkð&(pÛ¹~ìñ¯šõfÅÍg®=¤%ÑoXø¢D<ÈýŒÕ$û¸¸ãl.°úaž˜³ô¡Çʦ÷ü“¨êño¼ØasìÓ~X´¨æºØñLîÜ—‰<—û£·rÌKD]uXþýxÿtŒSº¡SÏÒîÙÞ»&ѵá/FlK°1ÙïAÛ<øñ†q"Û¦ªôúa`?&¨ô&1ÿ}­…w×Ù÷aÇ8?å+´@µå,͸%_­±to½q¿•ȸô¢oåfôýc÷EYÓ'q¿v_&¯«IŽóG¿iù÷|¡J5Ì»÷TÙ{îÒëºmw¿ÏwŽ×ûDzÿt~Ù<ÇllÛý„ÈïÛз¿ž [Ø;=~ù€C/Â˜ì¥  È’’!È{^Þ9ú%;<ÈÜk–“7'±Q÷"Ï´ç¨Ð©•þü1‘¶Þ]·ñóòl0ûz{rÙÚç¸rÛô¬ÄÙ¡uø×ׇd©7‰ü—×÷ÏUźJ;W¾$ÏO¹ãLÉ+u¦>Gs½£°÷K¤ ï”i[¯ÚØþ9½w¯»¬¥åoK¯¨³s[˜ÑäÀ‹Ó½™èÃ/÷í&ÇsRìïÊû1í?ªzŒ³2HÚA>GÒldx³D?f”mÞSóMi½ýÔú.|Õèð ‰}OÂ_$<±²?¬ãGÞs£4N³À¯Z\¬Úa “=Na¬Ü«}×6¶mæèó.æqb¾(òÓyý‹qÖÕ›¾üå‡sÓÙr6Õv™êkúÍ’²ÉlWÛ’*ÌîBMšN¸w{“{ÜÕ³3BÙÓQeß_,ÜÐ1Ÿ—½ É*¹Ž^U‰ý2ùç’}´fŒÓ°^dú˜BV*0¤éâk›.“ë±g_ þ:™ùuZ9®€÷¯McçêùZ{e†²FïIï\›;|r<'©Ä¼^xÓœ÷­ìç~ý·Ê»[©ìÆ—];¿L ¯^|9ªy2Ë÷ãÇÍêt¢ãû_.Qxªãù¹UÒÝUò!±&ò_Ìe¿æãõ}GÌ–¨õ²¹•*æ­¸e`»Ë4(¨~1c»dvsí­Ð v¾4lº·÷òNSÙÝÌ„rÉ‹ú1ét¤t=’},*ó€!Užpæ¥Jìk)ÎlÿuqîïÆ8á=¾Å#ÞJGzºë›»]¦ÍsbNHf½{ݲ«Ú’üyLÿ'É—Äúú锋ÃïUÏå˜Ë+y~­Æë¿ÎªÓÒGm¥§Ç$¡è%ºY44è¡>™-ípê}½¹Þdó>Ÿ§ò´Ü Ú—IÕ²ø³¶ü\¤5÷+¼U‰ýy>£ýÈ¢Ç8çÌ–SKuVÊÖfµõ—K4Lž×x’ðr½|qÞù¬4•ðøˆó1ÙÃ+Ïí§†´û½•î§d mWäµXz÷ÄÛ5Él´Kå2i]iíÕ…»_ùLgFŸ;Xõ¾L>—Ts¯ºßÒxø«­³ÇÎ¥ÓmõÛŒõôãœzjøsúEj[±bPµ½É,£Ðƒ.ÉýºÑÔã¿)üz*+.UÞ>Žs9μû°ÂGXèv¹~¯–yA”§_ŽðÞJUr>¸‹d»XdrÇÄd¶Ä²7ùòÒÚvæ˜ïé_§²•wj«ÿ®7ûnؤÃm¦5äñÖ€D}.–4¾ùÅEwTb}öÑù ÆYpîëŸÆ­µÒxC寳‡\¤‹'G4Xð4™­ÕîÍÛaFrb“¢IÙÌ?X2ì„9öß„gBöq=äž“lÕÙ«Ý„Ökó‘—Zqüs„XVÊ]ìÚÉäæiáÈiAS\SØ•›FÍyL¿“Äp‘¬Qޏ±ã>K’}4]çoòû‡çéž"—öéH‹oÞCÅ”ãÚˆqæ—¹pµ âM>ŸºH?­NþúZí¶»ûÔÛM'ê(GÛ6Š¸ì…©a&æ·âù/×Ï×*yøF%üÎòüTÞ‡Å8›µxtlª•&Lr«â•ræ 9°«ZËÖ+®°é|²?åÔ‹d7¤m¦ù}Xe¯V_D¶uøÃu¥·º2õ±c=%$qN–“?çÔ^I¼h¥Yº#ñ‡MÈrÎgë².)|Þ܉ê̷߬g&ÛV³s·ŽIaLøy…oDœcˆzPìèÂO?öÔÛ1NK÷G× ó¬ÔêL 2ñ5ðiÔxF¯vãq·Öòúÿ‚Qž{|#ùº 7>á‰ç|â¹#üÊæJ³9^ö5¹t¾­>?«IýùV’Ïý.PdÅ*=ëHaroIÒjÚ­E$“¬¡¿÷vø@…7©Où¾¹ÇÞS‰ýeÙ÷%ü>Ü …qªÙÞ÷Xi¥NǸ|vžæçj¸N;<…õx˜Ž™8ÑШÇKƒFÍd-‹ÍvíÆ„gH쳊ü^"1–׽܈q »<*n½•ƸJ&åó´Ë‰>6…µ-r|ãœF­è×6ý—x»Ïdr^ôqÔkq>'êµÈSqžæì­ÕcœŒ¹ÒF«•*™«²IQçI1~èÛÓRØõšÝZ”þ¾-;v Ïõƒ3Yÿ>1{þ}˜87qPÓÕ\&ÜþH%ü†"®¹‡CÎŒ³iª_»F+¹ÿö`ü¥^繟;… ¨V?ÿr—NäǾ þuQ$çZ²Qí˜Ty7“ÉóÛ^ìúƶåÒ}ø¹h+zØ¿äÖ×OëQÇ÷ùÌG½ê'UîɈë…õ Ur§*¯jw¤°Y¥&×X¥ò'íðª¯wÎ`£*núêD³–³XJã¸o#æïbExüdoß/À8—¿¯¶b ž /Î]4¶¼`¥7ã§?O9žÂŸÓþtÿPѳæÍà÷ B„GžÞ›×oßߨ1o“Ÿw÷Uò~°š{×äý^-Ö£EŠ5Ù°ÀJûÎ=> uîé3—ÓƒRSXÝé0÷©þ”ó)ÅÄ=qDìë ¯¬˜_ /¥¼ÿ[]ÎŒÙQ[Y©¼±p”k•–×or%êa ›ò´Å¤“sºÐ†1'¥MŠfòü"ŒIöå²]›s^‡Vì·‰:íì¥Rcœ¼Eêþ²ÂJ/o„®Jlˆyâ—¿ôqIeus›Z~Ï!Š5ÞR2÷î#¡ìh¥”ã'µ§…ý{ÙûHã˜‡Š¸ç%[êœïÖï`Å͉Îàóy{t0žhVjvi]£}ESÙê"3õ½ÞèZÐÏW«F²3'fw0o eÜßHíéêýÄúíø|ç™JĈ{9®åõ•ãì¬uñë±V’¬s%vœ£·…7©[1•iv.é:)¬¯ï‘lÊBéƒíÍ„gFÞïjC¯.ÜÓã©c_â[jÒ°ïúh¿7ãL–µ?XiiÏÞu.Ö;G_ÞzÙö«TæÒóõ“ë|¨ø77Š¥i¢Ùƒåý¦ŒŽìÃDû†Â *çÿcG½–ªF¥lyÒŒq&JúÑVzeë»Ö|–öÖï¹±bÃTö.dÒãÀ m(sæúûϦÍbM[?¾ìm?v·ZVäÊ*¾q±_ êd;‹=Ô’Äý°œüÁ8Í›mõ½ÎJ;>ç?ò,-ûé`V°*•Y3{–ІF*ò¸hð,ÖéËžÏJ­ÀrÊtñ®TïÊëøŒ¸ê¸}Ç —KUâÅìz“kû“ëèYÓ<"ÙÑ£~»£»dòþtþ\nêØŸq–“/xÝ+ 'Ž6²RøDé†ßºy*þE…‰©,¤í—3¿2ê(¶ì‘ü£ßF2yŸ£/ßwmFr}órìG‰Ï_¬ßå} îÝÄ8æ¼CÓÏø[i×¾ïöYšŠ5ߺªž)•¸‰ÅüõT.g>šÅ] Ö5ÉèÁzÏV-XЉdo\7Ç|Pönf©„ÏÙù\)ãTj ,[IÊ‚²ž§éáüå«&¤²+a¯‡ ³èi¯Ï©)•Îfeöe~­iÈl—¤ƒn?Þoùüê…JøneO{[¾îæÏŒ£Ú1¿ÔÛÆVêVr´ß3ó)Q»¹.%•-p]6‰îé)»‚¾9, ¾K×á×÷äóW_Ï3ù¾ÐSî'ìDâ\.'_0NA:“ÔµRÇ—Í6ÖkŠÊ<öüÑëTVµæâyúô¤ÜîÒŽö¶ã‡ƒ~Oî†0Ÿ£r/U´'ù¾-«63br™ÇŽóßó^Ò†…–ûåüwér[ý…ôñT´Ò–Ùë&eœ$S¯2ó——Nc«‚_ùIG­7ÿРä˜9L7J2û†² ½¥±ŸÜÉQÏä{G/T{l;º(ntq¬‡ròãXZ_I?YÜÊýg'iÇÑ”éÊ:iÌþíâm57u!ùœu.“}À½™éд~IûèÙaÅÕóÇü¸×ñ-ÏËW*ùþ[7>/m/ç Æ¹Q4ßOã‹ZéöéAr’Æþfø­ƒW+P@_²Szׯ÷×Íó™¸ç¤›[ãç¹Ûý©zÁº÷Þo×òsÞ|ÿ+[µàÔd ›ØÑ1¯ÏÉŒ³ødj±N_Zéñôž“‹¯Æ Ëòx°ìÍ9z^{PÐÜ“O^«Äy•ózCqv4_ë¦<–rOš[½eÂ1ú©×ÂXfMcC³Î«¤£Ìº•2Fè£YÁ”YíjµâÏ`êÒZš™ê¾îŸž•Ýì¾»‰s>y½-¯£§[Ã+ Í;KóU›–æ}Œ÷í±`wf‹´}áþËø š˜ç‡òÞ‰³ø=³|ÝÙ–ìËÕïÀ´a$ßg.ëðFjÝh¸Û@£N®)êç'ç Æ¹»½nž³]Î’­öÁká·Ž’Ï´“[~Hc žF_ß6€ÊØÂª®Kž#<Õ¬²ô±–êਢ.Ëõ±%½¼ª¬r2è£y¦ãÈ÷qÎ’_± ? u”d”>_¾D:»Ò鸢ão¨‡yòŽx–=—í¸]¿Q =“Ï1Û’ðúŠûâ|:$Ó7ï¦ÉZòÙQÑL_î…Æ8%Ç.yæÆjíU¼^çÛG¨Wbå}竦3ËÝÀsíµ§ÉZNjüÝ\&×aÌÏsÎ7µ7Xçß­ }ѹJÐÝnÚÎU\ºÞVu÷¹ÛbÑŠq±ßò™GhãÔók×Kgcs-sk’âCŠ\¯Š×öœÃŠtL¾“T(‹²é¯V?Ó‹ç™Öñ>‰ùÅ OŸ_ÚÆµûø{§Íë÷ÌMÏЮiÅÞäÕ!Ïg¥‹>jšÎfûI^o’lševÎfòº²7«›³P%ñüïÿãè¥k½®§ió(é‹&‡©ç8ï\÷‡¤³M}w&úчêª1SßEò}“`G¿¼žIW ³ˆsçù¸ã”ËÙXó,¦îph†×Ô`Vûè¼c«&ñï›ÒÈ¿4?ÏOò}ölî= #1?ÈÉŸ€Ûjé–áȧ(gšUéõxUçÜ´½éÌCózǬ÷zŠ?ÞÆ÷×±sXë“7MlÖ“ùžn±c¾7‰sœ–?k~LO¦’ëÁ{{¢lá]Äï‡ËóL%ÆÙi®Tâ«3'éêØ6SÒAú*üíÒéìB÷:±›½©é’„×§¼®âžuŒ£Ø¼}úÎoOR躩³6õ:H«~>«|V5ƒýR«fÉã5{R‚zêµã¦ÙLÞ7 aò>vsǹ¤øž¸G/é#Æ«ùyÏŒsfLV‰üÖtwl“Ò%ó¤ú‡Œ'W¨2Xé{OoØ.õ–&:³Åý&îŠ{óâ>ÖÓ¼‡:m¨þè÷ˆÅ묾øC̤à·ïdÊz3íZÐ?nY@;ÞzzuÝÁ®ôÞýîéÕç0éöBFx/Ç~ Ûý™Cf×oîX÷‰}'qÏÕy_ØŒqy¾7¾Ñ ÊUúÕóÖƒÍTtÌÒáÌÁÊêŽ?n½ÎjúT[4‡É¯ÌJL.9êV¥Vüó%‡×]ÜËû Îß²cœüÕ+ždZ(ðQxó†uÌÔ|æà£kÆf°Ÿ¿Ž/v¡–NM{V²i›9üs×ñç³ÉçÔ­I|ïLìÈû^­?ºwîx[móñJ>3ÇBe.vÀʉQׯ?o¬3+ƒM= šžÛÅ6JÇòåæ°ûìlÇeû³]õbË< ÑQï5?_éÐ!ˆ¼6i8w5˱¯U®L‘é}rµþèþãÈyiáçÏŒõ®ÔºØò Üü…neúáçÃçöÌfå–\pún&öKÄ}'ëkßÄÅÁv•|>}G%ϯšòsxyþ¤Æ8ý5¥Žnþù8µ#Uú4ôð´ƒ1ñ,§\” ¤«ã¤ ØYì›{êÔlÜ‹ŸçKK\G´»<ºÉëf»ÊÙã­Çëæ| >ÇéšËë‰ní§¥®gsk,¬èðý¿5èѯ¿£™6úž[PH¿/æá¸Ÿ*ÎÑÄ|\^Oðù^œeaÑ–—Ñõ´Ž+Úî§ÆùoV½žÁ.xT™Ü$„^x· >».ŠŸ;11ÿû‡b^,î=ˆûò¾#¿Wƒq–¼R¯_ûct¢ydý®G÷‘ïËž¡.WX“×-UíI3kõwÅäúæÇÄ÷LÄy¹¸_%â8çºXIÕÇûd'éÕo§&¥¥º.… õØG?î¸V¾`å+¬æoM <ÚדJ„¾øußh¶aûþÒÏ24ŽyßÞÅon¸«ÛðïIf©Ä½lοã©:³â±µGiWÊážîú½ô²V‡3[\ao;¯i´ùz (“Uÿ°O4“¿?Ñ‚‰ú!ÎÄ:LÄ•øþ‘ó÷]‚n«µöÆ >Jcokgn2í¡…ݪOì~…Ü6®ü‹Ä`’vÿlŽbü±ãþ†øœå}·«*±ß,ÎÓ?ºÿq®¬y'ÅQº‡Umñ:{¨ÜIºózäv«}OÿÄݨTΟHÆó‹ßßhC¢Žˆ<Éɼޯµ¦œiq„¤]·öPùŸÛåúuÎV¡ø–SWFÒ£ÛÒ@"Å93ÏñÚ]›”™œêÏïS¦«ä{ìXñùª+ÌdÑ+ÃÚu¥7 ææ¾?“ÕÜ\5­O×vL^ß¶¦µ‡l­b¦µâç#*ñÜ’Ÿûüû—x}¯bGÆzN9L!§Ï›JÔ)ÒÒÎ+Lu+NS;» =ùõû{ëFò{ ALœ·Ëë܆üœçŠjEõÜãf–º®j³ávºNëùñóãh¢{¤¦Ö=Lim‡vÜSw7]õ¤Y®°Ð’/¿]Ñ…\ùœ¾ÿ4Šy\ ¬êÍ÷)½q$ö÷Ä÷~Å>«¼Ï/×u3Æé]}\Ù£‡(çëOKh¹GhïS¯0ùœÚŸÆ¯=8dÈÕh–4sÖð÷5ŹU'~îÞ‘ûè_ªÄ~h ßÿx2Õäy­<°cœT)š¢]3æ…wO Íš C ®°Ww¢³MÝ($çbY4Û˜Ö¦î™-z&îÿˆ{¥âþ˜§ˆ{&òóÞMÎ ÝmµurÑž{w¤1±ÆŽ¬–@#7F}“ÇÎö߫ڼF¢Žö7ô6^ÜÅòg•Öhßê˜Ø‡ï‹øüEÜÊϼ®•–óã\înš1ºÓAÚ k¤±÷7ª°* Â~W;[ô"ÔíËŸzÐäuu›¾Œb£~î2nýbûº gß,âÁÏå3T¢¾‹sTù¾VEùùq|{®S¿0ӱޅ®q²óíJ;“¿ŸJ)'ÛciÉÏw‚ù=”Æ$z"öEÄ=±Ã±Öœ8*Ï#ôgʶÌÄAãÌTËcEâío´¾›wõm ìL³qz­g¯Ã¨ª¿*oÀšHö`R)ûÞ3ÁL¼Ž-ßWóùþæ£ï¯1ŽWοµoñe«±•£“wV 6jììæ†Z×Âúõ¥œkÉ·¢˜*çà_Ǫ|_wÚéÃ×yÿ¸§#Î+Ò¦®Ûc\ÜÎñ}‰•.òŸÏ=1ÿßécòßЯIôAq῟ÕEîU.9±¹ÿØWÁû”K>¬x'·‚ða ·Bø_è)—É‚ ¼SïO®ã½˜„ËÅÌý Üåâ܇)‚÷arv®þÞƒõg«ÿN&Ï¿ÉÇàì]ç#ïo©ýÃ%‹÷^jg‹s?rg_à§ü-Fî¾rîltê»$<«ÿ¬ï’Ç_p~®}ŸkŸÑå?Wûüç·¹Èý~ã€ë'ªFÞëWêožàÔß\xeDóˆ¿Ð÷)«ì³2sçt ïñ+õ¡³8y,¼ç¹‘{œ}ÓFî›vvþÞCóg€ÿŽoZý7õ=wv&p'Cï?§ûArMëœ\©Î.çž¾ÿJO&%÷0Ç´pþý3Ç´ç_ðV}®}ŸkŸÑå?Wû¤Ø‰Š\²?Zêéþ„Uò:¸9õ8.?áŽ.¿¿Òã\YXvke–½4’ÿTê}Ãûoê¹ë!›;ü¤þ›nt#ÈZ|×>£Ë®ö)ApÍ%{œíR/öÜìO•ú+z¯ '¡p8 'á_é¿î^XvHd–½6’ÛËAmâmp•½R€K. D G, CÀ'¥ä[YŸð6ëfàŽdˆÙ@¤°$†‰'‡X€’ÄÄŬÀ  Hšp`j¼qÊx›ÿaPraï‰læBÉw/œánVÉûåì¡pî•ììýú#…Ž»š6:$°¸KÎ/ÝDö4'7$´d-;(¼Æÿõ¹ö}®}F—ÿ\ísçãgJï#‚0(?ád•Ü^ÙRŸw§Å©Ç»ðçˆïQ è´fàŽÀÙ@¶±‰²»'<¹‹U 6'ç„÷~áÎ gçt wN Çáùuþ¬ãð_õN ¿ÎßÑ ^x¥^ðî›0ñä3üÎ5¡@"œÜ«Îž ð@rš~çðú”gÂÄ׬Àƒ;&„¯ÚÊ<’ûðSÎjÍ_ðwIq*ýùWjßÿº÷Gµî¿©Îý•÷ªoWmûT]“båÏÔ3éYÜrÉŽéÌ\ìe•`æ¼²_:ÆÉQ(ÜÒÂQ˜”À(tD3pG0Æ€l GPZ¸›ÕÄÌ’CÇ <¨±@Á}„fàŽ€ÙÒÚëŽÀÙ%ÿØ­à€6ñ 6+ðDpÇܬÀ öp`j}pEàG;Ð â•ÿðGÿ»B3pçBÉϪGͲpÿ ‰'“ä³°JkVžTz'¯E Èúß9ÃþÈm¡çžhg'ŽX€’ÑÄÒ ”HÊ(tHNs3Ùqñg¼aŸçgÿûêÖÿŸægžüõ³¤÷ AhîŸp¯šx`€¸#@cœ -Àãn×X @`†ð@€š@6Ð#P-ÀÁjâkVà‰À o8°5‚8¸"#€h¸×Õ»ª3Á 2'‚<(Š쯎åþjáZT#øã€+ Øÿ‚kñ_õWg=’È dß Üü1 è‘àD0ñd0+ðDRÄ#Ø€ \‘$À4H–xà†ÚejÔ­8àŠºì@óÿ±÷PMfûú?vÆŠ±Æ6bÇߨ¨Ø£¢¢¢„j ˆ5vìØ±g¬xÆ‚b ;(HÀ{h‚cCGÛÿIö»spî¨ó»sïÿœs×°Ög±Î×û%É÷Ùï~÷Þyˆ)ØBPœ ¬Ó¾Ä¥€¸Â  @¡i5ÄæR¢ 6^0'0Ø@„AÀœ Æ` AÊ1Ni5„éRMg|!ÐÓ^DdjÈ.¬˜\ B‰ÿ=ÿïš}éÌqØWžý¿².*=‹Ÿ8î6a¥uÉV”ñêtì·Ÿc¨JˆwËE»º“{ZÝK5¯—|"»Ó§më~idÍ'¿?ÝoåKîkçmÉÁ\?l¾G#õÆs˜»K¹œƒ÷y’ükP'óf™Ã±gÏЙAÞtœ}œâ—­ëtp¢‘½ËöÕÉyRÄä}eÛ¸DòbÉ~/ÂBøo‰|þwJùϨÃ}DNSÙ÷êuhsœ^UQ-»ÀÈz6t±³Á¼õ‡_—L ‘r™‡Iþòö$ü…„ß›ðiþæÜÿÿ{þÎ ËVÌ™±Æ9Ÿ"s¼°ñïhr<3²nÞOÚàFõß–e¥± èçqû‡2î{kGüïn(½®›’Ÿì-áãÁ}SËqŸÔ1Û‰M9IŸ6ø]-xô­ªT}¶Ën#«{üT±œWZ>3ñü¼& Ù§‡ñ‹Mqf"[øàñ|Ï"HøxpŸÌ²Ü§u–1%ÌG“w}Ý:£ÑÆÇ“ý"ŒûP ïæ‡ßF-d<_u€”ÿÝPòåojñã㹄wD^Ï%¨À}:PG·~èÁ—¢¨Ù®s]méÅtÝÜdL¬‘ù|lü⎟+U8täxƒ‹÷?È„_©ÈI~#ÂFä¯r?°šÜ§uæo^Öð\Ô âØ÷—·Gé…[|¿ê#ã~=£È5 ÀyüŽ…ìgãim+9³!šçm&lq”ü){ðß>*" ¿¯œunô(=wÅqªá×½hôÒ£´nÂØÆ•FV}¤ÕäQ1*Rez÷mœ»@äH~@­Iø‡òÏýÅÇŠû]·–rgy_ëP'ØÑätsŒXð¨öGi蒦ћ_Y‰RqG6võ îëÜï–Ú±@Êoì'å–¶°øùŠÏEø% ?Ÿü¾üFÔéh6î>Jf›¾JG©o“—NcÅÍ<žtq“‹±ÑÉR®¬#>‰"Uøèpÿw£ƒðµËïd5<[ÑLçÐ~rV$ÉRº÷N‹$s®¸mKŸ[ûâΫžÔ»€ï§Bû0þ÷µbÂßSä?‰Ï_ø€q¿É–’¯V-®ÔWóåúŸjFÒ/ñ÷—6»I¥k&ÄkœÆ:ÙßÙWa½'}ìÞa|Þ½ùLòwŒ:y¹ÂçFôÈÍàþV|>Ýqã)÷ñm!ùHUãúAÄ5»ü~x°Ÿú¼{øñç€Hê´·LùsóÓ˜Ù¶¼ê(*,3%%Ì9dL¼îC_ßò¾q?ÃÛÒû×@êûÊ\?¨RzN9E¢®NÎnìI†í«<»¯IcÜçv [(¿?n>ã¾ô}%ßÑF–ñZ¼_bœ¯7¿“ÕˆlEëçòn=&o¬îmhI>Cm¢¯hÓØ¹Muj?œ71o×<¶mfVçéKúXr¯¸ŸaK‹/·ðUã7x®¹ u.|ZºAÑp*´va•u#iÚt«‡Ç¤1“ d…%èö¹†÷õç²iÅ‚’ÏzwcÂg˜ûÊu!áûÍ}ÖÓV™v»Xþ³~P‡{H7usÛ~Å#iÖžB×D(mÒ ÷N¢÷@ s˜ä_ÉD>”ð…:¾“'L6ÑÛ÷Ÿ“ü9QÇÿrHò}ãNi‰¤àÚ¿¤vЧ±¤ÆËìW¹RÀÐCµwš+üÈØ‹^«³sèHÂï[Œ£b¼>8Ú:¡ñŒR.=G5¨S­³Ùy• ,YU/°q$—õ3ôf ìÞ®ÓÚR*š9"¹rî\v¢¤¼wâÄ.Läsˆü]‘¯%üz¹¿jké>Øëuº¶»z°KÒvÊÉSœ·¤zšò€œ4Vþ镟ò  §ßüqÓ\Ëxmå2`ž¯“Å?™ûšþâ0.ódƒ¬c]Hø›uƒëû.ï\~éMäÜÞåS6®ŸTÏñÐëwi¬mJ;å‹D/Z3Ðð´Þò9’ŸWg)¿© U\Öëç­:[æ‰"‡Høx—þEygáîŸkDU¹gXçëÈ+_>’XÈØWʦ³õ9#­ã}Èé£ÊýFÃ$ß4&òJD¶ð¥p_2ÉwÐ5[ñh–ÉPz5Õß­½Ó»J$/xr‰¬A: ÙÚ`Ò(ÏÑ´¬¼ÛS»Ës˜4¯a"¿Føšþ>ßFø·æ÷#“¡N@!Zf³‚N•1%!FRú‚˜"uéìjÅãQ=}ȳäþŒ¼ç2þ~×aÂoÌwÿ‡­O¯ö&‘7#üÓ¹Ÿ£ežjÖ êtÜg÷vÿ…Tô×0'ß6‘´2:¸õ€ÁéR޶¹ïyê6Ìw.“úSêë>RÞíKî.¿¿å:ˆ|Y‘bÖ êpá©t¯nÑ9ëºFR±½ýOWP§3gŸB.¬w—|CçˆüÆý>{Jãg¹¡ÂÏϳûZ|oͺAž«=‚–¨·fP$U¿]6,,(qŸg7òXÔüÑóÝ&ù²I~{ KN›ÈO~n¶5nuz*¤ûïk-êTõ6%1(ÙvE½M$•WTîÄEéì“ÆƒÍ]éJ'ïnPpË,&|«…o.÷Kokñ¾Ý"Ÿè3_hÔ{ñqH×6þl託ôöT$­Ú»yúšu鬨‚7%·q¥3ûcæ=Ã2ž½¯Òs®G2ëwºIó©_,ù‘ü゙<Ñè*òï4ÌU¹_È„b–¼#ž'>”„¿£Y?#³ku«u,¿„ ¸çú~\ÿ£Ô¨ÆÄë"ÓYÇÍ¿eÉ]U¤{²­cã”éLºÏ[üzEަȯG÷¨ÒŽ®G)ùgãP§Ð­•ExÎð’ý,þÆ"ßQäë¹8ÇAø6.®yQ²×g>ÇÔ©´PÿèñÛMÌéÞ嘗²c”|¶N« ¶k¯¾VúC/²u;šP⑆-þAW{a_ú9‘ ë—´Ðý㹃ðß>™˜ô¿ÞTƒ÷›u¦²â¹÷neÇînünaà1º¹í£K“Ь‚|VßϽ¨‰âΘßÏ–ú­; _GÑç"Bø\›Ò‰›Ô¢ÏsØQççKáo¾Õ²n=v{Œ"¯+ú½jÁÎe]}z¾ÝŒ½ÌDÞˆÈí¾öâ~-úAäbóçž bD,Ï©sVÜ)ý½Çijç…•2Øù—yÅîlô¤1îºuzŽ*¥¬2Æ‘úž_Ööq#âë2â¹P·-óCñœÈŸ·e\?£²E|Ïïxôdk=»ßµÔãUܶü€ ÖûãÅøùÌbýǬÔ9h‡[ê–ZêN%ï6QT¯Ë¬UB3XNíI à Œ¤Aëk­˜0ƒñõ v–Ü á·+Æñ[äìñÜ )ŸuV5V<ñì “çõú‡*в7öm“²-ƒ¹Y0C·a$ œ5ðéµALøºòÜygêYÆ”ˆäl™‡˜u‚뵿’ºalÇÖ-ºÕ¬´uQTsÝþ”2Xÿʵ´%Ý(º’I‘S­%?_/ÐïèO¡/=-÷3áל?¯Áˆëÿjg 4‹`-ç\ð¼EkŽïÝT&&ƒup\]Æ~ŸŠæ—ó¹ýqÝd&ÖÉöÑÇ3kežþ´²«ôüÁ}óu¨Ãó ²;;.Ýèušb,,Ó¦{¦e\èÙåb½Á3$_e7Úí«lUFåNûÃc[•¬ ¿‰–už³Ó@ZGãëœFÔ™Vèbp1:ÆÜTCì’?œ¦ß½I:=8“Wø®€óEWid›êÔ\±ã;gŠyÓV}È¡Uí±?~¯•‹¥NþÜY+U¶¢hÏíC®Ö<ÎÎ÷ϸÿ~áZYgÙàvž™ÌÙ\Ú0qeÞýán£WS˜íŒf1ê•=,ãÏek'ݧ’Ìㆠ×3œ(‚)È i½5†j7ž4aå„LÖ«VëÕûê»PìàmŠÔS˜—Å:X÷åïC’äg|ÅaãËZáUÎ4”æs|G:§ðtYîä æ3¦ÜéZËchÝÅw=ÏÏÈdcfÞyØàB‹ô¾GS˜[±Ë߸ÝÛ²^(æÓ<ïš”W˜âÀsZì¥çá*\¨cW2©Õ&×(¶lçÄòÇZ1êV{V™ðL¶(ܼ0œVôý~ß›Il·¾ÞŠŽJËüIô½˜/ˆû¹áY¨‡Æ±¥”óÀ}­5¨ó|y-+ù¯Q¬þ·ôѼ¬˜¥Ög²ãZôä?’Žn*r»úDÖÁtÞÞ_Ê‘ìFË|÷à]o‹¯¼XGÖ9‰¿.>_ТŽÓØ©e 3£Ù¬‰dÅ}t”p¹žO±=™ìY`­wñeU´zÍâEšû³žÓ†vRõ#‘ _ëP±5é]ÿË:¶xŽÍŸ[¡C­¥ÞŽ.ú"š º\££ Í7åî9žÉŒm'ùiíN=V[8pFÛâöã G%ñ<öæ$Ö¯Äõ…/?Ï›nNÜO_Z_Føãõ‰£O²êS{{üIG²þsævÖg²¸Õ•ž¸ÓÔ£ûÕi2™ÙDn·÷À˺ ï·RþÚ5Ëú¥xžÉ¿kåž­xüñ°Mõ‚§Ø©.5{v¥Ö[–ç¸Éz†•î’U؃ÚÞ;Òõý¹)¬oÓóºö+ú[ÖÁy?T•î¯É¢Þ³ÊoȨ'oܯ]†:¯5+;aë)ö&q€Æ.,–¬¶û^«ý$“ lšcûxŠ;ÝnûfèÊwS¤üÊ^$öø<·ºåõˆùœXïãÏGU¹~P§ªi»¥ÊiV¦ŒûûVÏb©ùnêVóC&»Ô©nðó **ßó—K²¦ˆüé}oeñ±Ï'<óóü=®ï¯è=4bùiæ’Ô"îpÓ³t¹«ëó§%³D¾=_±ìúÃO“¥\€$üëyî’˜OgXrçË'W™×ϺÓgŸ¿u~ì[zû¤ÚgØ4Wç¥!gÉ<ÜVÍb²‹«wÏè2‚ֻƧ·]0™‰<7ñÜ!æ‹b>*îû(‹Exì-þt0ݺ*Oy~v ›»ï·‘ƒIä{ˆÜ 1yð¼¿?[U Ž¶mÅ}öÇuŒç²ÅQñkú%ŽÊb§ûŒ:¸Ò…^4ôÛœÀN[ZÛ;£‡¥ßĸßË(óÚâŸýÆó¨»~¶®¨B¼°r=Ñ1SŠI·Ùq´ÁSÝ(Ë/‹ñû‰+ݺüfPÆn?&î‹b]DäH‹~ë˜ü~ÔEz~áy&Ôdš¾ÖŠe¡o5ŠÑÉqä½·fJ·âשhóFv¼p£ÄU—uú cDþóÔ8–·óp§åÝýìnÉy6ùs…´¸¾“9Ð'–y¡ZzÏ!žÆõ-|ê§œê\Y\`—;­šê7qÝ—³äró~ê`ÙÏy:ù×yu¸þÇãõqK‹e|Ý:žÎÎ+Q£w\ .ÐbÒ­ •ѬÉÏRne7Ëz€˜oˆ}E‘ÿ-Ö)ÏÆluœ?›çQ§‹k»£ÆJ¹çéXóªßÝÚ–B M±Ë›=èã‹QÇZ¤Meë0-'±(~‹õ]‘ÿ#rKóçdZyf+z\|òÊ6+–™cÑ ç©¬Gõ™s§§ÐÜKk›o\àN|f:ûtùלRoÜIäˉœT±.Ê×eŸ8<\SñÜõJyÚf½ ÎŒyÍúì)~–‘qNùeèx½ÓýZ K¡ŸžgœëáFãÎû†4|?ñükwâ9€ƒiÞoëÛ¬4ˆD~±ØO˜ñ«ëžb{SÅ?n¬“¯ë(Pg×Ò¾(Î2Ó§\³[Õ÷ïnW¶c Ý/j³±ïá”g»_~}x{ÿþñXïRnn_iª»e_V¬¿Š¼iqß3ëÅT'îàÓ§ëϲ7Å_F®J ègÉëà}Û“´åÚÕ!R.ÎÖÈ<ð ó²U®Â’+->ñ¿ó¯¿kpýÇëüÒòõY)g7ŽTúíTr©zã]³ÊÅŠÎäšõÝü­“™ŸÄûÍç·­HìW‰ßb}’ÿæzÔ¢Î׌–·è+oöÔS¥ñozývï|_À¿Ç ºs|ÏÒ©“Ù‘”éìêŠÞÒó^+éœG Ëzx~óZ¾_Êïg:Ô‰¨V«Ð´)ç_Ï×Ó‘Èå™^£¶>³n÷]çLå*U hŸ8…ñçR%ý6kÌéIkäÄs¤ÛZÖÛĸ)ú˜Ÿo‘ÖÁPg~·n[ú9Çœ?ÉV×JÐÓ"¿c‹Î®¤Ü4”š-œ¸cä‚I¬ìÄûŸ¾D»+š;‘†çAYžÇù<¾Ãgï—•W¶bì㽟ö¿=ÇO¼º¦E"]ïw¿¬Û‰kt¯º§<Çu :˜Û´ò‹qlÎSUœCgK.½ØŸ´ä'Iï›ØËÿ~ÉPçâSPS;cõ.'e]"½o§:ÒgÓ5ÚœØöÎݰQä«\äÍf7­Ò¢Æî–Ü31Žðçã»â<˜Ÿ‹õ>³^PçúÈ[EæùűÁ‹œXû*‘ä t[§_£Ú…RšµSIóYoËs?7ÓÞ²ï!ö¯ÍºÀõ®Ý-îÞz} õi¼ér¿$ªprÏæšÃ¯Ñíæ,3´†ŠZwN™i5ÐOÊëE² ò— /wRÕK œéFbýQ<Ïð¿_znÁõqñˆ6çãXzÒ­;$ѤfK3¶¿F}îÙ}n,­‰úih€e¹~uVݵ1˜/Zï»sk¨‰ç^~¦Ågy?Z\¿ÒÝ2A*«x6qqŒÜhºòµ!£Ú5’Ç;¸^ZçJÍU‰ƒÏ d+§ºEô–r¶ì-ë(â„xßE>ޝÁõ`ê£:ÛžøtgÊÏtH ¹@‹¯O¯²´À5*lnÔáRÞV ”Ùßrn„¯G5¶ä”Šç±þÉuÉ÷¿¨“ý é»É{ãYZýÒÚo.Ðæ¾«–ÈI¦×OÕ‹Û»PÃÕ¬Ÿ4‰51?ðõ%‘SÆçuö–}v‘O.æ/bžoÖ…w¶âìü«ûÇÖ<Ï:¶Œõ?è~‘j¬œäþÉLwžÆýºkäâçî&³¦¸½éŽÒ|ÞY:7àDÙ}fo“\Ä~>@"/Ó¬ Ô±ýѳغó,sn˼wñ©KÙÎKGœJ¦‡âË?Lw¦MW¢ï¼NžÄøûÒ\Ê{RÐÞ+¥£†u&‘(òêùþeWr.èQ®i¦”ˆ:7Þçõ^úÛyvÄÇ+ô”õ%šÛnúûû{’É´ú¦æ«ÙãoödâsçC„ùºÔ]‡˜Õ›ï:’mé7¾oÉu®BžG—ÀV.?jî Kä^úbÕ€ÕÉÔ7xøÏñ¯)WY!§ü´‰LèZô“È5ó 1Ï5ù÷4¨s°œ)(-ýDgíU‰—hq½¬gÁÉ´9;$xÃÀ¡ô|ß–_×n d‰C6 *èÑ…vÛq)`|W)±«´¾úÂç<¿uûïwËö+1ê>ŸÐ¢NóÇñÊ~G¿_¦Óè™LSâjUuªœlúCɼ|SÎYç‹XöÝùx3œ¼jµ/¼¢0ÏwÕ¡Ž››é™À&í~QâÐejáÝ.·[2y_~¤.ãF™Û>sÈx^kwË|EŒ÷â&ßòF.V¼.S»§´$ÍÃPgåâµ>¯äz¶¸k›Ð¦m®P³fÞ ê$Ó²ÚÇ6±AääW²}­IþÒúÓ?÷o&òÜ~ÿŸç$Åx/r$ù<“Ÿ÷³òÉVÌ~×ÞÿrˆžÕ-¦;pê ELT¯zúþ*Uu´Yf·Ú‹Æ°cåëÕ ÖõÈ¢?>ÿ©+½o×-yÂbß@¬;˜õƒ:UË.›×;AÏÞŒè0 \9µìðpCµä«ôq±æm¹½*0çò®¾“™˜/Šó–â¹X¬‹ó­ü9Ÿïß)p}…}•îèY«2ªUnd ¯+Û.¤ìºJ—GgÚ›áC_œœ;>ˆ Ýñõ䎖¼è+°[õB:‡ùÄAÔ—:JãßÇU¡Î’ì±~|­g—Ï}×gïdíy²1©ÇÔ«¤IVî õò¡cß½nãëì=,Ïy|¿‡tŸiY§n3lgWûr½àúÁžÖV+:%²„f§WgFHqkm£Ó½¯Ò­wÛv´Êò¢ócÿaÝvóÙž`wåti¼niYwº9ò||èüÙ~§ufø©~*¼4‘-°s3t)x•Ú4P­¿Zç*ŒïôâÍCÒOûT³~Ÿ &öÍù:Q;2=ÅÛÈ»XÖûD?[òàòíëP§IÑ2×b¢Ù++SPíUzw¼<ý“òÚ|¸µ²‚;ívË!³ÉTfÞ†Wv—Æ%‰õ]¡>?.Dâ~!öÌzAÕ7G^JdI÷^®<}UøtÀ›ûµ2H]ú:⚦sòÞÉŒ¯£·#~?ð¢U — vÇ™êõñž5¨,‰¼j¯ˆtg?÷!–uG³^Fg+šf~“žÈnÕ5­ì_¥&Û®lÅ ”ùÃÑ_CÒÝèC_;G]­IŒŸw¬iÉ—ä9uIä×õMÛ2v”}!ãQþU¡Žyú2(‰ý8ãñÙÞÉTmÍÇëΪÞäÌ“å6^´è\²nå˜ é|J'v»°ízzÐ:iºÍú`ÓÁrÞŠŸ³(H"ÿOä'šõƒ:Ë5]‘Ô7‰yW.y0zg2ͬéÒ¬‘Š>^úêrorxä8wŸÏqŠ5~ÝØ:k¦«4N÷§Bñ·bV-±¡Ýõ§ÌÚ\æ­Ï™t‘ž{rý Î'Ó2i$6æ~À’™É´×ÞjÛÇW¨£Û‡GÊ >4¡×´óŸMdÒz$ë<üÜ•£tN郴îWHº?ôýüêŒ.8·M»$Vd‚i„¿Fü6Mݲã ñ~P1ö“}×o¦lZ¶¿T§„tŸoNµ –þ±×¥¦–yŸŸ½tø¡Úô¥‹Þtül>`D8ÓpV5‰•è\ºW˜/žÃ‚‡ÍÎí~…Öëû"*܇K¬Ù77@:Gúe}ïGÕ±Ì;Äó¥Øçëµfý¨³½"§U+ú*‘i–·3üÕ]ñ²â³ËT»W¼M¯}ÞdÚ­­5‘ñq¦¶”GÚÒ’-ÖIĺ9/R¾½”_‹:¶Œ|˜Èž§ˆúðòõ©–r«î¦ËôæÚí•zyÑÂ5­f÷ñ dÒzÏ—â\œxÏåüÜBËyS³~Pç¡Ø…TC"+»wÍØÉíR(e¶iw™Ì§ T ¶Z§Y™ô~Yò’EޱX/ï›ÈyÏÿý êDÕÌÕYM”ú5…¢Úµ´¼ÐeêÕï®ñ7wiœ™È$ý3±o.ÎãŠ×!~‹çÜÏæk¨3= ý§êñ‰ÌÓsTjGR(©àœ£t—è¸G]Ù…ú*š”ÛG½Úe¢%§—ûM,ó1ëÍâœiþ×£EºîúÕÆë™Õº˜õÜjù¶DzøœKÄÏñŒ¢€ŒJ¯»²×¯öàNÝ]ʶ·œÇ} ÎÇðý)×Ç›`Ó1&‘¥ že[ë:¥ý4z sK4§O­î1\Éô´~=k{\ã–kë+˜ÐX—óËù¿%^µ³<™uƒ:æcP™‰¬£yz¹NØ¼ÏæñºëZá$»ñ üãIR931oëoýÓmÇ{7Ͳœ8ï:8™r ëí‹4ûì­Þ-\içJÓÂæDæѶyÝúƒ˜8ÇËÇ––y¯Eýüûð2Ô9i:ÞP!‰MŽøxÅ=æ:9¸gêŠn¿HU‹$—iáFÞR»]-4¥¦Ls~ÿ`ç¶Äù±Î ú@¬3åσU Nô?žœª—ÈZ59Ü¢ÊÛë4ùBéeF\¤d}vÅþ<¨L¡žµ÷“ÎÁ dóüL†ôú,÷7ñzÄ÷\ÌzÁõ#ÏO»˜È&.óZ©lyƒL§ˆ‡–ºHAC=•¯ô¢ÊæƒV¾ŒïÏ `bÿâñk7BT®–ÏÅ2˜×…;[æÙf½ Nï€O«Ù¹Ó {ƒ*ÖܺõÄÚ¸þƒ[•F>T*-Ì»A?&Í#˜ø~¸‹ñYœ¿ó+³Np}ãü.…«uIdó|mjï¹AÃÍ Ihêæ»k7üèC)¥*Õß>^<X¾¿"΋‰ëósüº:\—ïGè¥ïWÝ ß&• ƒ _ mÿXz!Ñ›Ö.8thò¾&}߯¢±Þ'òË ­{ñ¼eSI¸î­/ºÍ8­g;‡;VXÓö&U³A³/"‰ºÍÚ14ü¶'-v;¼péÝ@6wÅc»vvdBWü9”,9Öüÿ—žóÇd+N5T.=§gÓfE¼[q“ú–º•eå“Dtx_??wJÿÙ*äÔôÉlnõOEæZõdb—Ÿ7ìaYÇQyœãCM†ƒÈÍæë¬¼Ž uÕËœ·í’žuø¾Z½C—oRüÆŠŠìºItée£eSFQ·×7tC¾›ÂÚÝjÓ>g ë;â·Øã¿/wøl_J:ó*Ï,â{^Ïb*.þnLÞMê¥xa]þq"m3æÉ‰Ä¿0Yô%ûx“‚Ê=Ãþ©g>3:ˆ}„þ9¯ë¿ˆæóêqR_ˆÖ³åÁ£^¤ÔI¥ñÇÂ…M¤”-§õ…ß'ÓªÉÂó¬7¥=¹ÑbãÏ ˆ?ßtà}ŽëTÔ?i<}¯žmmÚJ¥)ΦµDZ}Õµ`ïï\)~h‡%),õ¸]dç†YÖSÅ:úï÷ƒÄ9§üç~µ¨³ûUÚ°¹Ëð÷–v:³5•›'ÒØçKß Eç¼+¶)äê/Íã‡1±)ú†Ï3 EvYõ,»Õgç±t¸~@N“÷õìím²Ã÷R)}×ôàe…ɸþVs–©"óíúÚ¶¯Yï­7Œ`â¼8?%Έû(Ÿç¶þ,Þˆ:ÏM”žU(¼rÂó*·èí„èÁ~§õ½ûzhT‚‡åu *6­SX7Ëü]쟉ó8âù]Üøù3¾/lå—­Xúê#õÓ³ŒÊ£z¾t½Eéý’Çé©Tð»ŽS¼ˆo›ù3þè.ôLâ{-¢DßúvØó,©©\š‡òy uEÖútQ¦g‹ú]9uX{‹*fw_ñ½žj<)4ûàXoéœÑXÖæ@…‚Ñqîb\%¿›óž×²Ì§ø}Z.åzKç!qý¤s]øÞO`ƒƒ»L¹‹~¼z¶¸ë¹z—¶¬èº‘ÞÄ×I|Ï™wg5w\­wÙ™¸Þ‡Y¾?'öøúj§Ï¾¨B¥Kä[½~J`Ì´_ù6]KUbú„X»éîkn^T æÎé€j6~i‹ª^«Ý™XWŸ‹8/%î#LßÒŒèôÙ¹ êÔijµ~}mÉ£ùñüþÂÏÅZÍVX™¿°”À¬kö:šùâ6©rÊîÌ®tžb·7Ñú{7j\ê}øÂãÙ(ŸT»Çm=¥ýP‰ç ±%žçù9¾n–õr³nPgG)]QÇfZýÜøUýý<.5ž®\×⌯›4Çøzƒãßk$÷k1ž‰û¸ø~ƒØ‡6ëuôAk{uI`SïÙÍóŽtŸˆ§ÖÏ.MS«Èe‚é$ÄöÔ§;UéçË~+U¾ÔÀ\GIÝ-çPøóI7Ë9³npýcãçÝžðô<;îmêè;tüɱnÎñä:É´CçNnë§ŒiÚVÍøs²/ãÏ—¥ó%-ói1_ÏYùûYƒ:ü{*çYÛÅUY†;4úæÜ!ã©îljS³7yJçWG3K½ÁånÖÆr_äß+¹oÙû‚✋֊ÿü;û üíõ×j»ÿ1½Fƒé³B#j5šÑ¤š2Ø 1ƒ€8¡A#òå&~+?ç¯æ&j%/ã?òO ’¼Œí¾‘ë"ù@}+?Gø³ÿÙüSn¢¿ìó²S¢ 6¿óQ •òþ”ûµ, ‘–?7V䇩¾àiü5O•?òÀÓJxj)?Ìöwíáù2t~Ÿ¨”<>¿æ%<>µRþ=þ=þ«Æ@ké5¤š> 4b8°A3#pBSF[4¦ä%4*_†Ø·2*þj†˜É ÏMŒÀ ÍlÑðÉÔþù‰*)7û[ÂãøÏfT˜2ľ”kNU°…°‚€8I¾îy_ÉPüš¯»ÈãÉŸ¡(2yÔ_ðM 5Ø@¬AÀœ¾à+.ùJùK¹<²ßùGäË©ø}Ž˜‹ä‡÷µìlᇧµâ?ÿª1ÐÔ?Z`&ô©@f 6hÈ `NhÌ`‹æÔ€œ|Ùcßʳø+ÙcþR†â—¼@#¤¦7e(Fš?äˆ@—/;;ç™ù}ÿl®…)‹ìK²6T0§ßùê¥üì \ 6°û9Š"?;ä5þg~¶ýWrz¾æúG>ï©’Ï»Vò²SæóBÆ|ùóÉL&Ÿ½\“ç;Ä®v&;T¾>ŸÏž©÷L?ÿª1ÐVú¦÷lÑŒ”hÊ( Cc†€\à‚ÕåË*ûVÖÅ_Í*‹¶hr ÈJ4{¡áC$/Qù7òÕRŽö·².„'òŸÍº0e•})K6(!ª( ƒ°4 (%?x“Èþ(“Qý ?x‘ï#róçû˜üàÿÈ_ôk~žÀÂÕ€ ”üEm%Qƒ”¥ß9*Ÿ§çïóÊL¾Èúodi›Ä¯¦¿=áÿÇÀÿK㟠„4`0'4b°E3j@P¢)£€ r¥<³?“ñW2Í‚¤œF'4x°E“k@P¢Ù£¤œSN£Ø¡ñCAPAú|9Ú¹ßÈÂÈï§üg³0LÙf_Ê£µ…˜4 (!ª` ai¤¬FS–v(È3ƒšØ#«Qäh‡I"9Úò¯äüØ@œAÀœ Ò` ¡j¾ào”üàÃ%»äóVÖ€œ|Yù3Îì!îPg!r=°‡ÐÃ$±«˜vÛÃþžÿýGŽ}ÿ׿vÒßcz/шQ@†f ¹ÀM©vhÌPThP}¾¬³oebüÕ¬³( C“‡€\àbÊàvhøP`”¼ä¿–ëè/åq+Ã"Ñý?db˜²Î¾”U› \ *°ƒ°B@.p‘<å­¿ïèÿ _y‘$òóç#p‚8#€-ª9@ ¡FÄrËüÜ£€ "ÖHy@¦|îkå<£[äbüÞÓ] ßÈ궆øý‰÷®éç?y 4_ûþ§Ç¼?ïòubœ3q*«ïñ-ÿ˜ößɤã˜é>aú¬ÑH”h¨( CS…€\à‚æÒ;4Xh¾³?“ƒñWrÌ4R^£dhÖ \¤L=°Gã†IÍ«)gûk924uÈýÈÁø£ö÷|Ìê_?“K5rMïQìÐŒ¡ ¨Ð”z`Æ “šS ù2ʬѨþ (аáÀMŒÿe:`‡&y@…f×{4|ÈNßÈf ’r¶eDÈ.†ØA¡ ¨ =°ƒPBAPA0z`Ñ„IÂQCMžQö¥ìÙ< ‚¨ôÀ y@EH?”ÏŒÀ ‹¶Ÿäe¾œ‘Ï(r~l!J ÈJˆ3 È Ð \ T°ƒXCAPA´:`ᆂ< Ê—¯"åý˜r·CAPÉyî¶ „ô9e_ËØÙÛ6ýÿ“Sö¿=þýO{fÌûïŽwÿcݷƹßÏÍþʸfz£LŸ/+ä4˜Ø¡ÉBAP¡ÙôÀ –/§Ìäh>-°FúƒT @#†÷×2ÊB¤ÌE4­Ø¡qCAP¡u¦³&hb9šX ¬ÑÈþ UÊÐÖ‚< Ädhð \Ðè:`‡f.hx°CÓ‡‚< Bóë=VóËù²:`Q„‚<àqè€ä‰XC(þ (¾‘±˜?;;ØäËÎ6e,F[Kr€‹2Œ[! ¸`ÜÒ;/¸@|:`†‚\à"å+ê F;ˆ1(~—Q&‡0µÀâô©@‘†5¤þ÷|íïùšÕ¿~¾æ$]#Ïô^ õÀÍ&5¤€©ÖhN hÒp`ƒF Fà„†¶hZ ÈNhÞ`‹Ö€ D#Gš9ä4µØ¡±CAP¡ÁõÀM&5º€ ¯¹@ù•,F[A#eiÛA¡ ¨ =°‡8Â$¨ØC(a’XÔÀäXC8þ Èe_Δ5 J @a…IâRƒ(`û…LF ÈJ/ È ¾ \ Â( ˗˨„ £€ ¢ ¹ÀâÔ;4䄪ök˜$X5Ð{7L¯è½”‡˜#åi‡I‚VËyž¶-„­FàG[ˆ<èOdj›œ\5ÄûÑôóŸ<þÙñïßaìûO÷þ'Æ<Óû¦3}®hºPTh>=°G†IM¨ G3j5Ò¤3Ø 9ƒ€8¡I#€ 56Ø¢i5 (ѼQ@†¹À¬vhæPThj=°Gc‡IÍ­¶hp hôp`cÊœF)?;¸ ñuÀÍ ò€ "Ð{! ä¡öE˜$ 509¢•Dò¥ìY=°‡pÂ$ñ¨€ØKÙÙ )Ø@LAÀœ ª` ai@PB`Q@ö»ìì`›/;[ áEÄr Æ7°ƒCAPA”z`a†‚< ‚@õÀ" y@eZƒÖmÿkî«Äl à ` ŒmáÀ‚FàaG[ˆ[ŒÀ "ø{ž÷÷<Ïê_?Ïs‘þ© ÕÀähF-°FCúƒT @c†4g0'4i°E£j@P¢a£€ MrÍdhà \ÐÈ:`‡fy@…¦Ö{4v˜ÔÜj`r4¹X£ÑýA*P áÃApAãG[4¿ä%DdBˆ”»mA„I¢PCZ` øƒT ‡P´Àbñ©@Ñ„'B†¬!" S8°† üA*P@XZ` qùAd”[Ap! ¸@x:`ñ…‚< ‚uÀB ¹À‚Ô;ˆ2äÄ©öh˜$R509ĪÖ¬?09„«Ö¯?09Dr¥n-°† ýA*AØ! (!ð( ƒÈ5"“ÛäØB¼G¥öûÿÌø÷ï0îýÕ1ïk¼3uÿ[s:ÓkÕ{4X˜Ôdj`r4›X£áüA*P ñ š/š0Ø¢5 (ÑQÀM©9@‰æŒ24hÈ.hT°C³†‚< BÓê=7Lj^509šX ¬ÑÈþ@†fFà„¦Ž¶hl ÈJ4¸ ®öhò0©ÑÕÀähx­Ôôj`r4¿XCþ ( „p`-ÿ ‡(´ÀÂð ‡@´À"QC,9À ‚‰¶ä%ÄdPÈ.’ØAL! ×ôì QÉ ª \ .°ƒÀBAPaÓ{.L€â “¨ —„h!ªÈ!H-PB”Àc–ä'ŒYÀbÕ€ „h£€  9@ GŸŠ³Â¿?+òµ½ ñYñs—»GXiÇf+&O5%¿œgÂŒû6ß•üÜ ‚î}¶Ÿ=gÉ)Ù>!âîÔÃÞ–¼;žç6†‰¼CîæÃòçVëP'2,5صêyf¶Ot¹K}X‘<"ŽB‡íß°îîhJÕXd=Ǭ\ª·Kþ^Í„Ÿ‚ðo98¢ß Š^×-9öÂW)Îut[Žž™ÏlŠn›QfÃ]züÁð}ŽJ„–ê™|Û—?(ÿØùíxÖ1öãã»G}-9ö"oHøq ¿>þßy¾Õ¸lEṯÌ‹gÞÕêܪ“r—ŽL>ÙiàŠsþ´âÅ~¯ÇoÜE‡•Ƴ&;ç—>R¾¼£ä·ÕÉâŸÍsïnH¾-?Ë{‘¡ŽÂoÖûÝÝâYß¶¿…•¹GéuÎÚPûíý~Kr‰1ÔéÅ–&²£ãÙ™¬Ÿ~xë΄?²È±á>EWÄçÂýyn×?yW~®x< ¯¶&þC{ä“Ö½žáÔYš5`îÇ.+ÕTìØƒÛ‹Wû³=)õ2lGYò½¹?Låÿ’óYÎ2®ÿ>Í­WãØË&c{ô`Ѽùvng© Ÿ½:ø¤7>R¢èÍ»lKœ,b¢Ú]Ê¥³“|Id?^‘»-|Oø¿ã¯Cƒ:³ö&.;±.Ž ~|núÎ{”ù2ÎöC©³TùI££¶iT¢TÜ‘]'2“Û[ÍyÞ_1á§!ü5ÄçÎó=ZÒûqýCSŸqß_-êÜÕþ:¢Ã€8VbÆí´C‰÷èHŸ*›&ÄRír ï<¨’rÂXý²in…—û2‘7*üë„?‘ð“>©ùóqt¨SúLü¹¥ãØœ'¯Ÿ­|s"’×äT]Kåæ7Ò*û»Qzæ¬E;Øã€c¶Fe«¢[„·‘ £îéÕÿ3¿}#®—³åÖAÃ9Ö¨\ù”Ü&÷é–qÅœ…N±4tRhpÒ 7J^¹pÐÇ~ ÍY=|Ðx&ü…¿Žðù$§Wä×›u1>[ÁõsŽoNm[yß§"±wN¥îïÉì\Ó.>½ðû1lé2“!Ö8&rG„oŽð;þZ??Õ¶g€Žú™ìß‹zÑÝÕ÷ T³ÚvC!j‹ß˜‡Ù(«=‰ü á_Þ;B¾åíõŽÄ}q¤\KÔyö¤Q™äA¨|¼té_ïSŸ‹¾E:ÖÔQfØÕV—Œ^4¸…•s@†ã~jKÎé„¹Û ]èlñ=ù„"¿ˆ×ãï›uîž*:óÑÓX–\Õ”Dû€¢çÍ4^YǨ_õ×µ¢»{Óý©%W~hìÃZ7(roT²šq¿«®ÄýáºX|7¹ßR÷Ïr3µ¸þoõÒ¼šËBm²J @³æŒ¹¤}C‡d·Çöö¦Ì2¦y4?.mÚ¾T5~û^7ÚZæJ KÞ†ðùDÂÞ¬Ô)·½FnkðdGΜ•¨ÂùSú}]bhh­%ã=žzQº)æ°º/s˜¼àô=£ÏíjM¢ŸD³>p=¿äq‹]ýu,òè ÀèØôF—º¬ú¶3Ô¹YÛÀu¼èõÇ««û±e6¦Žða<‡DIî±4ÅŠ*-~`ÕÖmßÝÍ’ kÖÅ„lEÂhSГŽ9Ž«Ú½sî\uÏg¨Ä’ëׯ}ð ¹·ÚËSý˜qKÙÝgbG3áO]æÆ§v7ü3ßRäh ŸZ³pýEã-–4ޱØð]ýåÔ\}30düi:t¨Jî¶pwêüªÝÔS£Æ0î“7†qÿ æ–ñOø² ßN‘ÏjÖ®ßÌÏcΡœÖ`r¢¼{ #ÍwV„N|xŠÖ|xÕJ¥¢>óú´˜èÃÞ[U²ÝññLŒkÂ\èYü–òÁ?Ë‘U¡N#õpïV1lýåo]œŒ—>§\ÄèST±Åû>Çþ?öÎ,ª,íÖ˜Q[ÅŒ¶JaÄÐ-bÂ_ ¢(C!†T0Šb.3 (†VÌÕFÄ„¹Ä°KL˜P  ¶ŠmÃ]§ÎÙÕÓv÷?óÿwþ¹·yž÷á™ç|TÕ·öÙ{Ÿ]k ¥Ãû÷ttíí-廪Ï“âþÀ<Ïûér¿sS¿V5ê|ŠûiÌqÖõâÌ:£½ôtÎ+¿ìü TöÊÜ6[¼‡HyHC÷ÏÛÙqf¼ÇÎöÿà×ÊýŸxÞ¿ô€:îÏ»×óð1öàÍÛ©—Cô”Ö¬֔²)8©–|s½Y­Æ^õŸu bÅœžf]ì܉¸ÿ8÷±âþy€üõp¿cq>!å` NvñÕã²áWZ¾k°FOχ_ìÙ<òÆÁÄW‘u•$æø°9iÏ0¢…0î/Ês çmˆ¾ØmHÌ;“|U9òžþK}Ÿ'°p¿ÒvEãô´§ì³¼Ø:²bÆ}k':T±÷„–›†±íþ;?¯{Ìøýç6ñχûIòqŸÏï ºAà=—¼Bk'°›-çø®8®§f;<ý´Teåýªáë‡ÓâE;– åÉúm¿Ul—ŽçØóa¸/0ïkîÆó úA•;ª–õ9Ân$í>™¦§¤5l;r˜âïi[doIUöì;Ô¸Î@f{+,þL`ûÙÑ8oàúçï÷wý}ÅÏG‰:]½þœ¦e·ó+ÿT~¸8uçÜ/‡(Á¹¹ë†_ý$¿g+™ð®Ä›Äv¥ï¾µsRºRΩýÅkq1޼Äùw[i’|ÍPçåLÁ¨PËj/®ùUO-[­7Ïîxˆ—¼{k'÷§›þí’¼ØÇ'Ǭl3–ì£SZÇÞÏܧñæû,™õ…‚¹ÀÔÙ-L'«k™ùÃiµ.WÌ¢ñgnL«8ý %Žë¾¦Dk?Zjõ²é •·”«4†q¿p>Oåóîwišë¢Ãõ+êßü˜¹ø0^Æ «,ZÿîEÿy—P‰.ž<NMêT¿^w£?ãþ½ü:Üš3üõðœ%ÑPò#Gn³og$¾>ľ/ïVŒšdÑãùçZ-°=@<,÷Cj—Ðö*&¸—Vî4–ñiîûÉýæx?‹9¦í%¿Q)W<8GÞ^ñ5ÇvØ!6ÝÒºc†}•°Ê¼Rcç~êc&›U"b¨äÃÂí$M8ÄDÿôfÄóσ÷÷åçõÕEÝ ÎMUË} Ùr7Á©3‹To"ÓgµÛO[[~*z}ôw©X[ýëSæYÁÆõÏoãï=|¼1]gÉQ'âlƽ[u2g!¸yP…Vüé“cÊ>Z3«ËÇ/Õ=¥\ñìrÝ*^RCXï ›çµ1kjÌi/+ýë„J ä¯+qÝ-TX:`‡Ë^­9*‹< Ä}䦟RÿÂlOÚ²yþÏ×Ïb¹B|òºÆs™yÞ†Ñ\ò;×N>w5êx1 °-cÞ;.‹ÞžŒzñ¨ø>ú”xéÓ/êv«.VvþLœÇ³_l~>^¥ñ< žÏÌ󺸟¢ézAƒ:²g3ÔGïg•Ÿ7œššE?=­4ycô^ڴݹò娡Ò86йÓlh³ï¸ï};Ú~§ó—wñÐãëé+Ä Um_`üסNúzÏJš­ûXÇã}WX/Ê¢5Óý#,ö’™ùþá×+IÌÅTˆ™ßãƒÊ8þ‹ãûo9;Ü”ûšæiéQ§Œ“|s/k6-wÖÇYÔ£›,¨Xx<Ñnê6.ëˆíw”xçϾñy¤O‚ŠqŸsî÷Ê}&ÅuöGÑoß¾à}&$G¾f@™™ƒ³ã™á¶¾6‹*ú„µy»‡n/ÑìÞ|ћֽ9ñò ßH»}ĘiKTlMm!ùÔIÊq—KóÈWŽÜGµ}Q­ç1…+},,´EPê 6HíaÏʰ]—EžT»èxk7¹Í<º±ûloºö“y¿¯2f1¨Â°† UÆõ—¨Ckcn+Ï䟸¿"®ó䨓yvd׸;™k~æ=Íñ,)ÿ{ùk+–®T’”7ÎZ}¾Ëµ‡Êè7Éý$ ûêóxÓ|8%êùùmÞˆ8öêñÞŠ]É"‡¾-}>·“‚j.Él¸t(•èm=ð‰§’}tnÙ{wcãyšÜמß/ùøÌs=Å~üûQçÀ£sÃ¥mc«=úVí<¾*ûhæ²[VìÚó§^$æ {K¾³¿ÝŸwû™Ÿk6ÅNÊ=¾åØÂ¥Êó<»LGîGËßWƒ~Pçòå¶§ÜØÂ2j÷ý.:7‹êÄzÔ¹k¾ƒÄý!O²0cŽ·Î‡]X0¡jßé!Œß¯xŽÏ[s0ž9Šù ­ äèPGÌgÚÌ>©ðìSõë1²ŒSe9ì¼_I!åÁú°;öì.ó[ž˜¨ûÞ’Oë{£*{%sp)ßÇèmÐêìÂ+odG½…„ólböÛ\ÇÏÝN^á§_›×H™ý„„%{[þ°ó)ŸñìØ¤8çÀyŒùN<ß‹ûÁ ©rVåˆç8ô3óÁ ýGvºÙL<2¦t6­ô:xö×K±Ñ(ø»ÃvÉ0 ª¢”Ö‘ãØ˜&‚³þÄ÷_xðõ1Ÿ'Šy3âúX†: O½œZ2m ëº,¢E|ùlç»±”ÔBÙÀƒ ÚÓ°·gÏ›¾RŽ_ã9(Øè?.ŽcÍŒŸ ßøüJü-Îu¨òëÃeUO…²^>Šv­²é eÇ¿>ÚHκ ºzp¸”·7ˆ tšØmmÕ`Æ}‹ù:œÏ7øxÃsOýcÔébˆmRS¢›êI±nÙ´îLj—Ï­“|ׇ“UÓü€ð—£™¸ŽW1žsÄÇG^‡ç¢ˆŸ›Øgòñ|Þ1Ÿ’;§ç½éžMušíJî£XC=Z윟æéCášÑoF«CX×½ÍìÀ× ¥}˜Æ\7>.ˆënq¿U‰ëw¿ú¦’ïªEÔ°ÿížy=³©ŠE·û%íVQ]Y¯Ó‡soV5=zvå`Æïïü>"ÎÒùs®WÓ}]5ê온­]³Ú2ƒ¿6Å=›ê¹=Rdö_I3Uqƒ&ÆxÑ¢Ÿì,ë…0‹Gš¿?Ìø¼ˆÏ÷ø>»¸NK1~â¼VÌ£× N¹¨F³ì+¬¤é*ag*›6üÐB?aßO4uÓÞ>-{’å¥aÖãF¨Ï'ïcuŸ‡ØÇÉÆ×Ã÷#Ä}qœÑ¡Žc±'—c?¬&§2ëv÷˦/6O.9Ô_NVÏêŸsò¤ÓÉO|w&€ ™ÛmUšŠñ<"ž«ÆŸëðß|\0ÍÛңΰ«7›­ŒZ/åEdS`+õaù¬¥ÔvÛÒ"š…^ônêå[¹c™üÃщi1*ÆõÈ×Ï|ÝÄuÉ×ï¦9f¡˜G{V™Ðç†Z7Qí+78›RûŒ²š·7š*:½³½k¨ñyN§fkÚÕ?¨bü9˜»ØÒ8¯áóNžëg¾gH†J!î_ËP'»îûñ¡ç6ÒwŠªòI®¿“ðNEbþƒ’ž<ý°úá¾`†ÅR-Å">¿uòàåÆ×;Ž$Þ_¥|?ÔÙ¼-É:ûÑf)Ÿ%›z·YØgªõ ›!´´ƒ7…îYöqßq,ÓúŒÙ‚†*¶ãbã"-Jw%¾^çyÔ|^È÷ Dz±ŽuŽ®Û¢S¹m´°ý¸ ¥ñzЇ¼»úêzulOB…K£]oçc¡BWT1¾àùˆüsáói>®šæß¨QgYäÚ¹Ïb)§}Ô~C³iU-!MMW2OìÏÖ))ºÒÑC‡½B¤Ü#ãëJ¾Q8g‡çŽšæÓhPg^í³›ÙÊ8j_¬^ó1>Ù4tå¼y£_„Ñõ¦º”~qÃ¨Þæ@¿a“Tlå™Óîg¨ŒóÑÿ¿½±ßx>…á¶»A¼¾ןä}7lâNò]5}AؘlªÞ¡éþ¾}ƒéËà[qçóÓxçþ=¶±qîi_ ·A:EÞwœ‘»›bî²»M#w'Öz7œbÒB{>ÛëI—êO}¤b­'·–Š 6沈¹dÌÙâ÷é'õòæoøêX 'ÞlB޼‘! 4ž ù1¾Ùä4­á€Ÿïô¡ñª§Ýf “'fµò fõŽ9˜ù"åvt"žoÏ÷ ç¿ð¼LƒnPgEqa…´—NTr]0Ä+[º7aŠQkW¦ÕìG«?ißÏ#„ aʶc½Æ÷·¹~xŸñ}Óu€ןT¼mßC®ûȯÑìMúc^ðæÆˆuØ,ÏOEFŸéKùÙ<Ç6–ozçÕ’ñLÜ——Kóçßò: 랇ÚÞø|Û ÔٿʹoÙûiBvȱ¢}²©É5×öG~Îÿ”7g茾T»cåÕõWŒg:‡«ÚÄcþÉ¿I½èÜZ¿_ÜÚ8ŠzAa—|½õSöªÛ™.ÙdwSÖóKÛÓ== d •Ç2±‰e(»qÎùÞÝ !LìÓžÒzÝ]Zo|”ò^;ÖPœ{é¼¥ñ|ƒ^P's‚<}">ù¹q‡lJ®«–Wc<ëVzäËR±ýic'á1žYÏr ÂÄç‡Dß6›‚J-¾öp=íö½É ãûV©Gëý+І0žÇŸûðý&>®MXðd×Ì]NúL:BªOE‡iÜŒ·Ú ÖÙôë„J¿¸¨ÙÀbí.ok9˜´ñÇ:ØM`n˜=YœÇ¸)=W?ºò×6òVƼxžwÂs7MûÀlbŽü;CÃié‡giƒm³éP§¶.K¾Îfm-ŸÄ'þ0Œžµû\éåV´kÑrmB™8ÏúmÜÌøÒþÎ~\cî½i^‡ ×ÏÉ*¾55áI± ´I6Í0ÛÕoG™pwçã†}JzëG};Ï eÊ–¶’9–-8˜_Åì¾'¥Þ mó`ƒ'‰ÏÃsŒºäûgâsL)— uò·LÇR/VÔýÐ]Ö ›dâÜ"ÙH‡1É=½IÌWÏζvÒ¿_ñ¼ŽÂ9ˆ|Å4Q‰:»_œêä´ü(µíùº­u6}?ÖåÝÚÞÑlåÏRštö¦-Ûî·(Êšu[ÿôW•JÊùleÌ£åã>aš·«ÆõWw>®O]{ŒºuŸl9¨v6mÝìPåèØ¥lIG빟¦ñ}§IìÕîCì3qÿőļ5'âûŒ|ÿäÈØñWzw”ö¤}Ôù8ãú’zëŽS°³U=›Þ—LèÃF.gy²f5ÕJo‚V~NHÆ>^]m5cT0+Ñ"ù`sMWâ¹²šÓ§DU~ëÈsW7®ìÓìðîÒA/g…§(›MÖwkÎn¶‚Ýk²jWÉAÔæa™!g°Ke\6x¨˜˜·åDÕ?¸vŸìJÅ~ÞáëæòA:7òZš?÷’öi»ŠzAàœD׿£]üYx0Eû+m·Z¨]Év÷^µ }ågG¼ ³žÆRÌ2‹–^$åºu0æœpÝ‹û/£†–s¯9º3ñý)ƒ^Ârä%zºíŒì¢#í£E_V>Í¢+3<Ÿ^¯¸Š-ª×bíÙ>ýÈí¢ëñm!“™ƒ²ŽÝCgãçÄ|žæÆù™¸ÞxìhU¹ñåGÁöÖM2ÔÙ:Õ<®Ë¥ÍÄBå^U|{éNTïÕ,ú»ÌOM“=¤ó“Y Æ]0sj[×Mü>Ã÷ÄuU+)Xœ§ËQ'"ðøÖR%NR‰ÈƒÊßÌ"‡Åkš._±†éÜíPaYº*lÛ/™Âäsë¹nI6>Oã/ïgþŠï‰ëZi?uÎ[â²:ì$¥WØ{2ñ|U>xëóÕ’ë˜_ßQ#âôB1vÙ³“Y•†‘#Güª2惉ýÛÌx^‚ÿæëQqÞ#®Õ¨SåÚž]¾œ÷eQ—t·ÝÇãÖ3«ØÒKwxI9x“ØÝ²BÇIùØÍI|nÞ€øþ™˜o[@Ÿ\õ E¨©‰¤|<5l…&‹BkÇ5>ß^ÃrO•?Ñwù¢~~q5¡ËÅ™Æuó)MMçÏØçü7Ÿÿ™®Ót¨óãÒ ›H¤eí‹ôœ•EÏ6×x4¬áÏŒQ÷%Öc‡Ðì“B ÛT696à”y ãë0q~.3Žg|_H|®%>×ÐãúÍÖOÞ|2àufÞîÅŒ,ê/<æ.·‘ÍwV×î¶l0Ù¿©VáE;5{ß¡J‰ÏOÄýËêÆsD¼¿ø>‡¸ORIÔˤyÕØ3zïŒSTQeÞô„,ª–ÑîrÑgYšÃ@³ü^ô³­MªÈL–0Ç9èe^ [þ®ä°˜­Ï¯ø¼ÿâz¾•4þ‹ã² uÄùöiVuü³hÊ»Á[žÜÄZ²x ™ãIxó-çf²V=¶Ï>ÈxŽ#Ï5ãy]â:³c}m9®M.,¸ÎPŸÌs×jyf‘YŸëÛWÎÙÌ^Lò <ý|€´o5“‰9¨ÆÜS®;>/ãû™<½@Þ2ê”ûú¸d™c¨c×î`ËY´ìT£u®¶[ØþjýC]îy!Æ«ÍL&Ž'AŒÏOº¨í1qD[ãûÅs)Åûi;c¯A'¨sy1;Ûû, O…¶wÌ¢æocží9µ…Õ; î[ç>´/ã{’4“ùvÞ˜¼ïy”'ÞQ_äÄó©ù8Æß7¾ïeÐ ê ªž6þÉYÚ½öxËŒ&YtðuWŸe]¶² ­uGÝbzSíøç=ÌfcfW†rƒ˜xnÏÆ˜c%Ž//ùó(ÓÜS®/Üí'œ£wX-XÕÊ¢"»k¶ovj+{_="O¾» «—%6sØûåk+Oñb<ïOÌë´3®/íÃT'}ä:òs ¦¹ëzÔ‰ÿÚ¾mÀÙs´»Ù5Ï‘³ÈκSÚ´½^ñq˜ßgŒÏ£ ãž”—‰:_—öÛ3;‰â§\ž?ÖSÜþ¯[kÙDz"–6lwÔ„S.ÕÞÍa­^?X]tUãó 1ϯÛ?è’ïÏŠãƒøü\Ž:V3éÓ§÷ITÌyÀáÉWôä¼h—Ÿù¹XVyRÛ£ƒ–yP}úþ»¹lêÁMö]kHûæŽR¿º×ü7Ÿ—™æ9+Q'oSÍwS|ÎSJˆf~Æ!=m»Q?.x;k{¤qsçÒü¯Šþ²i6kã¹©ÎÆkÆ<_>_ï‹¥ý¦‡Žü\[“6BO[‹} Fòž?x==O›¿÷îSq›žßœ4ÂÆ>Žõ¨»wY|ƒAswÙü¹ƒf3÷ìqÒgHÏklçÇø:“?àëfÓd êÔ‰Lo¤ö¹@{·tõpX¥'õ¼¥õ}-v0¯ ?œ1Ó‹’»Ö.â?‡‰ÏOÇ2~ދ缋çmIç<Ÿ:VÐ˜éH-Ã+ü’ß\<ǦCëNç¤]¹@å?ø•|9SOG†Ô±>òt{›Uámp=/Úœµüȇ.sßG/Vµ¼b„«tßu3ΛøïZ%…“ˆÝŒÏW úAÕà³=˵ºH§1{nÔõm^ßÉVåñÞêêI†x÷³XÝís—žßÀ6 šÚ_3€º6 ÔÄIóø¶}¾q^+žoí/­§:‰ú™’#?wC8àr‘:ùî¯×_O e§MÝŦ_\ñm£dˆ?5›Ådk{×Þ±<‰ûs¤órc~pÇ8÷eW³òÅû…£1Û Ô‰}Xj÷«â—h´m¹S];ëi®—ãÒ¥uv³™ÕÏ-]¯/E|í°Ñ)t¶qÝÄû߸>y¿ñÜV>1èu¬ØáÇW§_¢¢#Àï;åv„Õ{D÷P§Õ̺ÕZür‰ÚV«RrDM=ÍZ´ÈÔ"{˜kg§-^:Ú÷nâ¡®AáÒ~cãy¤|_‹ßøzM|¾ïX`?H:GcF&Üìv™Zx·jz»”žnéÓ¶¤ÇæyuÐ÷ã=:ÓvmÑ÷Óç3UŒô¬m*c6Ÿròçø|]e:Ö ÎåqO¸m¿LMçÊ ~p&Ì(ùðÜö=ì†Ý‰´ñÙ]éC³«³îŸÇ ñ—KƒŒç]øù þ¹pòçŽâz^\GéPçLvÛ¹?•¹Beë OïÑð‰3—-ÏšÛg†¥ÔîMÍï}ŒÏŒË*7Ý´äjÓ@Æ÷}ùxÀ?>ÿÐü2ò¨xÞ­Gïoô«6wÈ:S'ã\™é÷èþ‡½ÏO÷‰—ò’=¨W§émBîÎa[§¬K[¹%€ñ}_~Ÿ÷Qž8z>2?³á3G1S^ð\óÔy¥*töÎÁ+ôÈjÚ²­îQ€½¨ÏJ«;¬n5p ­X±drï‡sØÎ~î}Þ6 `=Gû‹/ÒIÊ=w¤ñ³–¼z^ï•£¢ÚK§{³^Jëi'ŸãKûϨsmŸƻ›\¥öÖã“ÔºGu*íT–ÙË´m>¤¨a=°(òú£¯s™×ò[²e‘c™Ø¯]¤s”ÎT³l?‹žOÞJÏ9ó3w 7æNÆs7ý ÎhÃr•„SÅÕ^gÒ ;ÿ¨G{™Ïº« Nê”åãRn«w8³â3«Žei÷xZ&Mhîw¨é>¶5t„÷ª¤Ô¦}Î6ûò‘l€aÃ6€ñ|w~î[œ_åJëÁÎòo5¸þNŧ­ñ}“©‡a#/“ÒÞ‡v0gKš{d™£ošêkn¶È!‚m»Zw”NÄÏgóÏ›÷±¸þéEâùé~ƒëÛ-þtR7.™ÖF°g¥+eÒ½f*Þ¼±yöí–ßn•íqºÐ;nÚB&ê},{)^ã*wq5î­y½n»õÑ_ŒçÙùy(ƒ^P'"ÜçUɘdUÔ¥™Oú]::qÒ½uÖû™x®¦­ðœãÓÔ!+ÞqTÖ¾2cßÈ{qkiÿNÆû'׿xî²cÜS³i9rq‘LÖ+nµYy—V•_·~¢ï~³¾Ù%a½¨Î™vOrµ Y{/¹¿ÃÔQŒÏÇù¹uþ~ñy4?nz®R†:ÂnuÅ;Ét9ºÉžwé\³)•Ò×îg˜\麹Q^„[ÿŒì…Ò9ˆQÌÓ°w$ž ÌÏWñ¼`ñïUàü¶u>YŽ»YéU2=8ºô©Íé°Ë;²áOk$¶¸ÈÄçÿÝ¥qÖMº¼uëJ Û_læ´WÌ Ö¡Nêí ó"¯‘ïô@ÿ ½îЂï„úf>àúÅ­<©ió>ÝÎÓ0~Žš¯ßø}šïw‹ç³;8Ï­G]‰#Õe÷]£ÓCŽ/Œ,‡Êìí¼·¡ò ÛšQtÝæ~C(¤º÷ÂQ‘¬”ù£Ü—SÆ0þ¼„ç9óóhâsÜ<ãs;Óû€Ùô¹ðé®Í¸Fc½ZúõümÊéùDUãÎA&ÎS•Ò>G$ÛwlGÛ £¤ïÙ¹ÿ¾†¸yãX&­TÛk^;NøØiÈf÷.$ž_³õƒ:bÎù5š”ã>/.æ6Ý·n8‡˜Uÿb³%*i‡uðâ-¢˜U‚æÄˆ^~,ºç«zÓÏt'‡ ϯdt•ÎÁ~ö;^Jçù<ˆï#ôƒ:†¯É}—Bë$™ÇªnSmÝØ«b5—̰ꩤЎ#5v_¢XÂÆå;ãüÿž~Çç·ÏýüæïþøÊQx•Þt•Îá‰ã¨uÞôÝÛbO½J8zeÞ§–·©mç†MFøfc{U¹™#J¿ô ‹™ƒÚeúg‹1lX©+5údt žG¾òå–­•Y®#Ÿ”»pû›‚yÛjÔ ³)ófAÇz}áí¡UenÓƒÉ 26\?̆=¶fõ®¢ÃJúvÞÍœöЂ«Œ?OY>wÏžÐíŒÏø~ˆA/¸nΧ*çfL¡©êçW=Ë ÿ ¯ç¶n¯eâúq õµèxâø˜h6w¿×ÅáöŒ‹â÷d:ÇMþ:ÄsÐ |J‡:cuwªB†s;»2¨]÷#n,Ñ2»½ýB¶oéG†m‡ ‹Ø}a{¸v [6©ýÀåN$®{Ÿøóô.Îg7õKt.0oÒ£Nö3܉R(ÅC¸±gPT±~w“²´ÌÉC7p‚hWzKó9‘F]Šûiûâó­ûÆy:ÿÄù“xÁlF޼G¿ rcJ¡.ßuˆì–A1Ƕît³;ÂRz…4[þ²]uÓ%>8нì¾4ç£ãXé{aˆïËòõ:ÎÍïwâzÍJÔ êˆûù)dhÏZt©^ý‰Ó"ްÅËÃÝ¿H%Æ–ª÷®t4«5lÊÈnêÑŒÏù>Ÿ?óý¾NÏ»‰uä¨sûn±ñî¤Pj•"½^dߢÙŸµŸ_aâ<È‹6ɦN¹ÍOÜpÓ™QŒ_GÜ/u”ú+Ý‘ŸßáÏuL÷m•¨c~¾hCÕÓZèÙ|^ÈÆ[ô¹ÖäGòÑ lä²roFâûÍd)AÊ(Æ×5|?KÜ÷Ò9òs)|°À÷3QgÎŒŠ­FVJ¥à%S|oÑ…šo-¢ž'°fg÷”oÓÔ›*¾h·˜½Š™y!ÿý(ã÷@ùzLÜ'Lt4ÇjpÝ•÷½nÓ&•vnoVæ¨Í-¾õ´sòQ6ÅðàÁ›ÄóãQ,͹UÂìôQÆ}.>?÷SÆï¯ò} qÞ$îŸëPgøîïâÊõJ¥¯a{­ÊK§{½ê&2ócÌÿ†ù~¶Ñ›ê;ã³1’ñsãü>(ꯕqÿ‰ïÓó¾àßk6èuÌÚ_ZY]™J囇»ö<šNu •cì¡[±Õ¾iJšïñèÑ÷×¢Øþ)¯âÝÇŒfÛ›»uh½²ƒ±¿ø|‰ï§ˆç©¥y†tîY#_ò¢ï“¥Áxß쯊™›N%îŸV¯êqæÖ( Dlña4²ûӠҺŬtàæ±KŒ1>âû?|¾ÌǾfz¿”¡ŽïЮµöÍL%¥¡ö ÝÓéÑÎÞíFg:µtœuÍKú~q43¼l«ÆÇ+þ\–Ï3ù¸&žSp*°?(GLJ÷ˉL%¯kì—N§ÏËÇgZ¼9Î&­U¬X¿AAfg[úçnˆ6ŽgüûØü>Å_A¸Þák¥KR):¿êmÒM*kY¹OÑ>'˜Ó…7ye÷§*ñµ#ÓE³Û=æ†ÕÈDý’ñû…||¿/ìDÓ–v[·¥ôý\?t@Ù_Zàúå¶¼«›±è&5ý>D³ù»e‘Rzà;Zrxðçôú‹ûüœ×ß—åï ^Ë÷4fâϲ¿ÉßOÿÙOfÒkL6ûÍ%ÿw|Š¿• Ë}ž„fUJ¹Ø24m8È 4¯îOr±óMlÑØ1ÿ„¿]²”‡Èý׿•‡“.eÂþŸ'ž [Øç©°Ÿ§àóÄ3aM}ž4¿ãódš kêsgê_\8k¢p˜iÖÄ¿ê_,“2Á~/k‚{ë¤,ÄÂÞë¶’ÇSám'ÇM©¹ÀÍ©24¨äJY1R&¬ «6hÚh_ú3amÑÌ1RCûƒäÂÓÝÜÄãø÷2+b¥,lHö„˜C*äG,°€@€È!”X`±„òijá¿zà Å ˆ( ¤9Ä , ¨0 ®RöŽ©—»iöNá,ÄÂYØ< ñ¿#{ÇýYˆ<{GðrÏoóÇÜÛNl ìhßþs°ó€b×GÑß._ò9þ;ÏâïñïÃøg)ýzá³@#ÆóßñýV–"ÏĶ2È„LX[4mŒÔ¸þ ùO2aÍÑÐ*ähìX©¹ýA2°G“k€9]Ò  ,ÐôaR&¬«‰×ñ·2-r¥lÅ¿’‰Í³ gbÇKˆG r­ÄLlž¯hš‰_÷3±y΢ߓÜ!4m!oÐÂ^ï…3|L½ÞÿUÐ?òzDì’¥ü±Â>ÇBžOL¡ìÅ™>ßÊÄœ4Ôfÿþ1PbšPÒ%¯÷™>24f8È 4¨Ø IÃAž”q¡‘V ’€-7FjÞ?Ê“µGCk€9šZÒÿ ¿w Oäoå\ÄK™Øa È!ŒX`q„=p…Hâ%„¢¹À‚‰–è+Ä,ñªA.p‡â%Ĥzà QÅKK r»”écêõnšéS8ƒ±p&6Ï`üïÈôQ|#ƒ‘gúÄHB.ì•Ìó°“€­;+ ¼pv>PBðIÀ¢‘„/ø%ÿwñ÷ø¿a ´‘þ†\á½F#Æ 4£ ¤9š2ö2y¦¶¥”m&dÑÚ£i5À\Èké’Ek†zàŠÆŽæhnHr4y,°@£‡=pEÃÇK4½ZÊ¢²Í´@ö™yR~ã_ÉØæŽ…3¶µ@ñ„ƒ<«‚9ަ9ÛÚº³¶Ms£A>P@h: ƒØÂAÞïxÄÎüáñ ˆQl Èh”f°…8c$úƒd`¡j€9Īéà¯æ± ¤K¹f±À‚z)óGS(Û±pæÏïåp َ“x!ïBèWáçÏÆÀ?ÿþ»Æ½ÿ©1﯎uÿÌ8÷gcœÒì?w|û£±í÷Æ5>¦ ŸO<°DS…=Êð±A“Eƒ| D³%[4\tI1¯LÈžõÉÀã—˜—ùãüY93X 9À=TÌѤ*ä»bÆ®0 ®hàx)¯L rÿ ÛB+<Ë@s»¢¹ã%\ r;] dhöpàŽ†×š>äw4¿È €p‚È †\àAh ¢y@!eô„C$y…òy ç0ÎÍæ9Œÿù<Êoä0ò|”O¦é@1ÆJ™Ù”I!LJüA2°‡`5@Ab®…ÆLüù{žö÷<íß5O³—jä ï%Q ,ÑŒa@\Ñ”ñÇȳµeR6™A+GÓÆ 4nÐÿI­%Z r;[ ,ÐÜa@\ÑäñÀ®¹À ¯24}¸”A+d“é€ ò€BЈ!ZÊÙVü…œmÓ\ÆÂYÛ:`ñDƒ|«‚¹Œ”·mšË#‰L ’€ Ä ò¢Ó/úw²|¢A>PBŒIÀ‚Œ‘Dé’=Ä©æ¨ ¤9„ , Ö0 rˆVÌ!\Hr8X@Äa@/å’ÅK4ŒäJÙ>…³ gûü^·Í(œ&±o…a Úì¯ÌÏLǼÿêx÷knö¯Žsßãþ™ñm¦ãÚÿÔ˜¦6û÷Œg¤÷X+ü h&WŒ]±ÀMl1nÅH7W,¬5ÑhùRî˜#«é@Žñ*öOrd]ÑŒñÀ ©rŒS±ÀÍôÀM,Ѩj Ü1Ni¥¼±pÜÑÀZ C‡ƒ< @3ë€ ­¹À­24w8È 4¹ؠѣAP áuÀMŸh|°‘á߀| ƒl „p„Ø@Ñ _ȑŕl ’h €Pt æ&ÎÆæ™‰Ñ (!¢$` !ÅHbòÉÀ¢ÒsKÒ‹µs±//1È!ºX)o, è+/eb‡t ‡cE¡Ll!'ÌâTt ‡Hc9„ªû{^ö÷¼Ììß?/s•®‘/¼WhD¡Õ ¸£)µ©Ès²mЬáRf¬+š6X¢qÕ ÷O2cehèphl°Ds«A.pG“k ò€ ¯6húh)3V‰æO¶@4ÈJ! ØB 1’ ”!/Û4_±p^v°…xb$™æ+þQ^¶i¾¢˜Cdþ ØBl1’à” ØBx1 (!À$` ÆHBôÉÀ‚ÔsˆRÒâŒhÐW5XB¬j \!ÚX`á†=p…€ã%D¬¹ÀbÖò€âw2Ý!r-AèáßÈ̾ "8œÅØÂÏ¿²ö÷Ø÷ÿߨ÷ÏŒ{|ÌÞ7¡éÂA.pGóÅK4 Ø£ 5À¨éÀ ©î&ù±a@\1ÆÅÿI~l.pGój ôÀ,ÑÌj Ü1Æi Íò€M®6hôhhx°AÓGƒ| Dó' ä„ 6C4ÈJˆ" ØB1 (!$` ‘Dƒ| „X’€­ ÿ¢Q‚$` ñDƒ| „ˆ’€-„#‰É$[ˆ*ä kQˆ+ Ø@`Ñ _ó¾‘l!¼I|þ ØC„`!ª@:C±À¢ zà qÆ );È!ÔX`±†=p…hã%„«¹ÀÖJ¹Øzà !ÇËB™Øñ…ò`]!ðx „È“þ>+ü÷X÷×ǺçO!ý¡• Ø ÃAP !uÀMôÀÍ,Mr³mÑ¨Ñ ¸£aµ@†¦ y@æÕ4p4ÈJ4r°A3Gƒ| DS';ä\lÐäÑ (ÑìIÀ #5½?HöhþIþ ØC`1øÿ… msˆDÒ'C;ØC8+1C[ÒÜú3´U È!®X`©@:°‡Ð4ÀbóÉÀ¢ÓHÂóÉÀÔsˆPÒbŒdÐW3XBœj Ü!R-A¨á Oÿ XKV r;„«2ˆ7äD¬6r4ÈJZ du8È ˆ[l ðèodj YØra}Kb?J­W`ìû¯Œ{|Ìû{¼ûÏïÔfÿscÞÍí„×$|¾h¸h'P¢¹“€-Ƶ鯮IÀ #5½?H¶hþh/¬e!‚$` !ÄHbðÉÀ¢ÐHÂðÉÀ‰‘Dâ’½ ÿ˜C0þ ØC81’xüA2°‡ˆ4ÀBRt`Ai$Qùƒd` qÅHóIÀB‹‘Äæ’=D§æž ¤9 , Â0 ®c<°„ Õ ¸C˜Z` q†=p…Hã%„ª¹À‚ÕDò€âÕX r;„¬2ˆ9äDm Q«A.p‡¸µÂY`<™~›ïEKcÿ¹#ºA˜iÔ9rÁ=Õcq*‰>•íÈãq£µwÝ$žgú²ÒÚòW'˜˜?–ãød¢÷˜C¯ú}HÅÜðEFŸ¸R}½JŽe¦¾:ÔQ= \ÿ$:•î·©©ó’ݤAú¬f·}=(!ÿözÙÞ(Zâvx£££$»6’?ƒ½ÑŽûÁ¼.žØkïhy ½ðz þ¬©ÔxRè¢"·nPýÖ›®ÏˆdlwfGÛÛÛûQ§ôÆÊ%F²ÈQ‚žŸÑwFÌkp4æòzbžÜ˜+jxãfæÈÔÚ#³9•^í_òtëòÔ¼è‰60–â¤ÕNÍH+ö}¼3"‚…Ìï9üQQ?–YG0Ôs¡ukÚÜoÕÙÅèw)\N†ë w”%¯:J3¯=¼œ5èµ®V,㻆:¶ù½Ý» Þƒ©îo·Å[²”3[r’ù±é}f}qZ¬ ïÛ|¾µx¸1ß„çшþƒ’ï®ÿîŠÿŽ¥I©~L"»A/R:¶ÑúêØ¢}·~Ù´[Iû±”…¬çëEãëûIùf=ÉÅ`àÒ÷·jƒÑY'â>ÞÂõ•¸~Ë-o×§Ró“Û+Ôq®Ðv~§cÁi:—»Ç}¨õêÁ‘3C#YÑï.n*;ÒØ/b.Ÿ³ñýà}Å}ôxþ™PG:›÷©TiÔ÷36î»N¯Þwê–¯co.ÍL»£ó•üt³õN»Óöˆ3ÿ ½(6¢3ýrºƒnYþýîE'=5æMT †ÿ]%ß?É¿ u’žžÜœŸŸJ?~µþü.ô:Õ ÿ~ëØ®'Ùæ][n„øRëk9›"µÑLÕ`gÔ^_£ÏÓ¹nÞ™÷{8KùfyÆJÑ¿£s÷M‡:ÃÒTZÿbÛY·¶×éB·Ë§v/=Éò­/„ó¡·ýÍ‚–H~œ#÷{âþœbNÓGÑŸù©£è_ס€¶u×~Ëâi´ÈÚÌþÝ/i´Ù½Ú‰7N²óKef¤£H[—#­¦/a û®¾|ßÖŸíîv¸AÈÉvÆîËsæ¹o«©O’Ù¬ùÓ8ù%+¤ÑÍÄfÆ­N£˜òN‹WJdEöYwÔÁ‹\FœZ}ac4kç|nͧ‘Œç抹vF?`î—$ú4·#1ßWô•¡N·Œ(ÇGÅÒÈKí•F!?¯|Ò9‘µ³ÊÝ—=b hU)š­íÙÕ¬m„ýøÚ}Á ûÀŠùÕNruå¨ÓË`À”JãF¸¨ê¦‘®”`œ(ùÀö§$›«ÇjMXÌšúl”1_•çYp>~ÎêྭcB'©¯Å×£D"ö_A¿Õ·~oó[©Ôáý©ñg×&²§ <ú^z4€ò[5jnWd±1ïX=S&Ÿ·ÈAÊ1r0úØñ×ÃýM}†Ô¨cˆY•Jk7-Z´cI*]\+Ã%²ë®¼œÐxÙ¤¥vÕWXÌÎ͉q‹;4†ñ¼î›Çsùû7Çu^-¯«òþÓÔ¡^{Ù/x=%ÂgÕðH¥¶Ž4/yŠÝ­¸ýt²ËP)çb1ë¿i`±åóFK>CvRî¬1šçˆ9¹¤<'ÑgL‡:¢ßk*Mlq«¥RÞÕ_îèrŠå Ãboª}"†ÊELp5Í÷gb^ñÜX^Ç \o(;©×SiÞöB£o¦ÐÉüòõ/:Åž-{5&ʇú÷šÒýÊ‚(fgŽÉŒ÷Iƒy+cÿô0;G>øÂŸgRéöç&º‰kRhmˆÞ¶YÆ)¶âaô¤|I&ØÊ^‹d»tÑ×w­÷ebþ’¯Ô‡îÆ>åþâbÿv•rßDaêØ¬ ™;ñH*­ë¶&nÊð:äS_v±ÑiV|ºóʆ^¾4·Ñ›Ëò#Ùgû'¼™èŸç.ùnv3úîòßš§KMøÔ•¸o–A¨ÓÚJÃ~*ûÓŒRHp±³šõ2 \>p¡jæÃº‹¤œ,o6ìÀ¹²óäÆüªy ž—ç¨ýŠÅüŒ.|’”¨SâîÐË]©Ôf¯nàø§×(ÈÆúóšc§ÏÍ-V²Û†×31ÿÈ[Ê÷câºã>IÜ÷É4M:y-Áq©äÜö×b]£3õzìÌùzš±Rý6ûY)éûY÷íGG³_½ŠæõÞîà ˜]èæ¼…Á_v6únq_&±¯œ¨ü÷ÛóúHþ~¨óÄÑ SèÎ0]£‘´¥w9œ‘üä‡]‰1øŸ¢Ù¨à³¶ü}YV¹QýôÃݨgê>o»ºþƒïÞ‘ºœùý}u¨3{aܰÇSS©£Ý€5_^£O£\gÆM8ÃDÿN/j7=¶•ÏÈÅÌuût„/㾽ܿûòó¾ãùB¦y©zÔÉO§ì—Jq]kÞ¹QíÕZØ£ŒÃÎ3̽Òá=±? ¢ÇMîñs^Ä?-¸0¯õÆóÖxÞ#Ï1å><רÔOÚlNŽülµYµ–Û¦R»òÇ=¿”LkGù9XëϰÊíYÈO*ÛPH8ŒbdïzäÇxŽÁø©ì}cZó“ù¸õÜapÈÆR^–”/ƒ:¹nA3~¬™J[×­¾Xn^2½¾¨Œt­r–e„¹øÌD.7çZ­íÅ®Vüðã–G1~}>nñù Ïõ#ùþãú–˾ -S¯#Ù9àû.ÉÔ·XõÐ÷ÝÏ2ÏQïÝ»Žlœ]›xx_g¿QŒÏß ç&ó÷¢¦¼þ¾a{£¿½A7¨3MˆÉ)™JBõêæÉ”ðÄM½cÞYöK“nÎv †‘è{Á|}^Äú3чôG£)Ïeå¿y¾ ÷73èuDÿ´ê`Ÿ¾ÆréUšáR¯óúógY­ÙÛO.>ãMù Šå¾ÿÁT“«¿8÷ÌϘÇÃó™¹/žè#­wäã~Ø Y=M¢8ßÓ Îñ3^M|‘B?žé?â^Õ«deð¥>Ç|c*8ŸõÛ¸&úÃú11WÆ^òñë`ü\øü‚çOô‚ë÷4˥ЪÕŃ\7]¡Ê†ùs,8©$¦Ú#(ÆvõOÏÐÇBÊpå!~Òý½›äS×Ãè‹ÌóÿD=9HyLâç¯Gó÷Ä·SHÝ­‰Ç¥NWh²ß—lß“çØãz?]i¶{$‰y‹Ù}wVÖkÿÆs—y×?÷Á4ÿÍææÈsîg¸·¸’Bo7N`eò.S—ye›O¬•ÄnÞj}öõ‰‘T·õ3ŸÕIù¸¾FÿPÑ÷Pn¼òþó3ä|ñe¨lu¤õãC)„ÿ“E­•—ÉïÙ¡²—C“Ø×]¥Ë§]Aeæ+ײÕ"öËœ•ú´R>Òº§µÑWY|éÆ\nîû,æfK>ÿ¨s©Sòµ¸)Ô]ÝUUï2uÔW­uä|«óJoß>ß—BTc^GF±[¿¸¾]7l8ã~ø<ŸŒû`ò|nî+*οÅùuò>xÏÉóO!ÿ°ý%_ì¸D-C‚÷4ªzž…ùvòvßêMYÊG­êÅÂŽ„¿8½ÓñüKž»¬žÿùCÅšwOOþR¡lž£¸îm]`©FzðÃ@j“B÷¾ n5¼ã%Šl¶ÊwÒÀóìF•€– 6Ã({éý^•Q’ÿ¥?ku»êGe 7â:çù~õâú¨/ññÆ ÔÙ%ÄÕVJ!¯m3‚O]$!|ÈbÙyvncç­7ObiÈ“ˆb“Ïž~ÿäHã:Oô¥ìA“æ,xûÉê#÷­ïßîT9¥æì^æ¢nP硯`ìŸB7‰–.ÒX»7«>_<ÏD_×Ád=akò¤¨(6ËÃÊ5³Ýp&æ†t0ú.òy÷“繦9¦úÿÃÞ™G5ymï­"mqÆ¡5j­©cAªÂŽ3Î8ÕXƒ ¥çh«Ähµ*ÎÔ1Ž ÇœˆŠg@+8ãµÊïIÞ÷ä—à×Ûû[ýºÖgùǽÍ6É~ö»Ï>'çAœrºÅ=‡¼H¦ïfÏJüãµ?Ð/v ã)–+—d/çKwÆ]¹N¿ >þ¶ûÄy<~¿+_Ïóû˜¹?“U?ssä•êd:ïHM¦§НìzŠOÙ|¥Ã)–TÜÛ¸f€’ò¬ãÓtÃw¥G0î#Í}~øz…×î§hï—"Aœ¬U¦¯"7'SJVí…Nœ¤%ߟ¹öÍüSìÁg×TÏûQÓ÷Â÷7\À5ìѵmÜŒßÛìrwî˜Í[ÛÖa¼®qÿOáÞQñyƒ8s†ÎœL¯g[ô“tk•ï°FçN±®Ö„ð§ÇK~{"ÛÊ„{z‡³¦VƒúæâºÑÍöàë~aÜ’ÿ>a¢Dÿà žSÉ”ðYƒq_\7ÒØÓK®Ý•œföV»g§?]Ôö,µ>” ~É~LXï»R±­åjšRÜl~iܧûÃÚû€ª§ƒ¹j[ÿ¯“išÕÃH«oŸükÓØÓì^äªÜðpêToRñÕN ˜EUžJ_Û:"ª±ÿÙ|ÚyÀýF¹o´U?ˆ½|å_½Þ˜húà N¼H 6-æåŸ=xZôIF½¨ ˜“$fþ²WC¿¯”ß“ÌûN~o'÷ϰ꯿êé0iý‹&:VÇâh›@³‹o°Ï1‘µ*›¾¬_±aâóm!Kjá÷ê¤ËPöIt»%÷sZØ|RxÿÌûáÞt"¡_uƒ8^“«J¿Ñ™¨ÞW)îo:$Ðôáu'uOýw”4Öºp^Àæ”»qáÏAC÷%àu”÷<ϸ©àÓ ú3ÍË‘×yIVf–‰ju]ÜËx‚ÚŽü)óÇ_™p¯ïPrQÖZ·ÿÑ|Ö`›g½œc¾¢¯@3›÷åãñ„{LÝE_ ±?CœÌÝNu3‘õåž ´+žLd=Z©[ôÙ2˜6”üýàâMóÙ¡…ÃömISÚÖeKoYw›Ï(¯Ÿ¿ž Èoó©GŸQ9â ²´…Mbÿt‚.½V†8œa×¶í0É ïe7Ö﹯íφl[²âÔ¥¶ÄûqAŸOmë›ÇÆZåÚ<ïY R"Ž&C™õÕ™³TÒu«ò×èã´hOÊ•Iîg˜s¹ŸÝÖÔD#_ÞUß ÏÎU\¡’_°ùñû~…çÎ]¯5u窂«>ôâóEûú©FœJ–ÿü·³4w±æ›%ŽÓ‹ÓìÖ|ì´ƒnýSà¤ñs³ÍcšÕ[š;<Àæû"¬ÇZÚ|­yŸÃó\x^õ3q®½;üÒg©É¢S=RSމ>+gØjéºM·:ûÒЗšM0—¯òlïÑ¡þŒû.q?XÞòuîfWÙ£¨¯Ý |?ı¸è|Ùâ,íò ÆÐŸÑŽqõjsΰ‡[øU’°ÞžÇ’4)W3Ëú³ˆ–•·Ëbe¢ Lô}¸lëoy_*Ük-ÞÇŒ87ÃRÛ¼‘D§^ž96±ö1=ÚЬr£$¶ñ@0>y?qŽ:Ÿåì¼'á0›&ï;y½áïçé¬^Õq-Ðç8hräמõ]›D(Rµ¤qGiß‘ƒMIb!WFdº— ËíùÛ7jDÿ¥mîÃïýåó?¾ŽŠÉ/–¾cHûó? âsÌ$2š"gw|”¤^š=ý“ؽ=ÉFPš¯å šÇ„çâ`›ï ï§ù÷Ï×…Ü_¦€_3âT~³e÷I„ÅË—†:G)V²¥î)éY&¿42½Úµè«Û _Ëyâç5ˆ s½fâûin[çð9°ÐçºX¯)G˜ œ¡×ñ† ²ŽÐ›žãº†že«÷$:ÜH±?Žþ£ä¡y,`é’hݶ!L½a\–ÜÛxÝçúä÷5s_ØþæˆóS¥£¤1gè;@£ê·#´¿Æ†¯ôwÏ2÷ QžÇ|Åõ¹†•èç1«_蛿Ÿ›÷7¼ü'åÄß—U?ˆ³·X†ÚiÜòq8n\úíšVfìñ½õL¢Í (>ãׯÚjX)¿Ÿžß0XôÑ”Sn•ýfÞ&ºí6/¦ÝÆùöqßsžwVý Ž«uAy†öõиO9[Ïx|ÚÛÄÊu©ød`¥6^-[1 óƒ!¢?S{Û¾…àc’eóQæþÊB?$Λ'è̯›žO¤ë›eÆm§rGï–©3ÅÄN'k¼Žíô#G«±R¼KߢÜP[½æ>”ü¹Àëv#¿1 ×å5/0Ït˜þÐê—HÙmÖ&~O%wÞØ&ÜÄüÂv«Z ¥Uk/VÛ;‡­kúÌ÷—d_Ûz„ç5îðϯ§…ú#®{gÑÔ/°$L¤¯¿ôqÿ³L‡ þރź#'~x³Iì¹ø‘­]_nÑÒ9ÞÄ}¬úAaþsš^ÝÓ±¬É@Mw–=×'ÕÄNôëô‚ÓPz5!9ì¡4„ÅÝ¿¹ý³}ï×ø:Ï™ùþ†Ë*õк]äæQJÄY!ïz÷âÕS”Õ6úÎÕP9^î{ù‰‰­ïè}º¥½rüºeè®öºkÎÑè™uYܦ™m}Îï³çzåõÚÞH8nîhGÎ:E.èÞz75P´ls§jÉlñ)í¯›ø“þ‹êU6í aÂüXaó ü š0ïJóâþ­|ÿľ^G N|±ÆCâÝOÑã›A»Ÿé9mÚ4b`Ûd6säøÕ:¶êèE›?‹àkåZ`m@œô‰žÉ“²NRÈàí?´•2a×FOðŸv¤^¿Øgö}4_y½œÓy²C­Rdm»+ô£¸rÝNŽo'èq".Ç>茾}x¥Ów¦‡ ¥Ê¬Lf÷÷W¸¼iépªþãöƒ_dÍa>ÖAzo&ä“‚v 5}‰i„ØoT¦ëÍ+F6Ö>ñêò|B™GÑ~4lÓ®kÝ» >Ñ¡9òÖ…üIj{pÏP]ôªýóÞQñɬ¦KhȨüd>¬ŸÃ4&ä‘\ôM”÷犛%W†–ë§§¸.ú â«—°.%ÞH{wX÷pú!j™å»*îv2[ÖÒ8%ÏŸö?:9nÛV}d¥¥‰·{Øüu†¶þ€×·f?_˜|±z³sI9âXܬkùiRëW§f?9H•­)ì‘wÕgŸO&îkÏ÷ƒº0žÏ¼åuû𼶯JÄñ¨x.Dñ:¾ª_1®Ì°ƒä¸aúš)lÃø‡ƒç$)©ÄÍÚ}½J„°ß~ðýìÀoÆ×¼.sW¾æëÅóW´Y¿Bô›AœÎ·RØW˨á_Õ®îŠ?@á7ËýÖ/…uý³åŠøa¾ÔçÓWž‘=CXí¶Å†^ŒûLòç)ß?áë¾¾âþËVý ޶ZŸŽ$Påì%èðÅ=iÕ¸¶¥ÆÁ2s̃¨¯üb¾Ã¨Ñ¤ŸÍ„¯x¿c{>ˆúâþµVý Îþ&žÝ‚¾¤ú~Fæ~rlú²LÊÂöÃÉÆÏ+^ûžZ£Õº•<‡¥{¬YäðÓwŒÏ‹…zÖÀ–¼îp¿_û}¡ÌÐùùÆéOü¼ŸR.üT?v[ ›”ÿ:iMÓATÓzaä¾<‚í%/î«ÜÉj$Ì ˆcoN?JeS‡åÉîÅѦ»sb6c½º>ÿfúù´þ—i£O«g³ï†­_¾7»ãõ‰ï+óωÏUøçh¿¿’‰8ÓKÑ8‡×G¨^äæËÓÅ‘gßõMw%Ÿc;OS¬Yéè ×„ôm5GýÀ}ìÚ¶9=>‡àºä>«Ü‡Ùª“…9rÁ§}m‹´e/:ÇÑ+·M²oóϱIn_–7Ž ßg-[§Ïl¾Îø~*ŸÛòù4×%÷ ¶Ÿ³KGð{‹§yód3ª?¥)žórO4HeÅïÙ±Ãp²Ž¿+Î縞Œ?§xßÁ?7¾?Áë‚ýç&Gœ²V£žxZ}RŸ¡+bÉ\3ºó'}SYøØŒòÙõüɺ=„8¢O#ã~Ó‚/O[ž çC.zñÏKГpžB‰8ÁšO«ôL1Pÿщw4ícIb5TLe•ܲ+þ¥ö£ ß%×Z=uKn»2(·qOÆûqÞGó||"Ï{Ù×IõBîd ú[BçXúÍòñ¯Je-V_µ8FIúïë7Ih¨fó;ü>ü ub܇Gð1”‹~s©bý:/~^T`]8 jÄ…o¨a aÿ4†ÊF•‘{He?üv®ý?êûsù1 ©Ó™8Wg|>Ãó€÷ÂçCÖƒ¼þØrÅP2}é"7ûlŒ¡óòí»_Meõã'/¨Qa]ý³ÎáŠÑ3lëM>þ®Ä?¾¾åóûç~&⌯ö×ëa‡éð³Ä¥n1ÔúæÊš)Ÿ¤±7Ë[iÞËŸŽ4wŸMjq]“Ê)ÆW^p­ñ}ûà¸?c'çÙ|Mýw#Ýw÷V‡¥{zù5Gþí/)óKô=D{s𻕾º—Z5së;ªaû©ü¡fÛ;PÂzϕۻ¨ù|Pô³ëFþê¥Ãz‹ûùϼøü†ïWñu®U/ˆóäÚìóMÆ$—ß~l±o/Éê¬y:¡_û6±ï½3tØÇòd f]‡4_´än[›¿­Ðÿ´µõÂzà¾íü‘½?´qõ’A+þ8@ÖÇåö½4¥]¿¹Uf¤±é»»ýj:@º]-¾)ye†X[3>¯áó!þ¼ç >œßŠÏ7±/Cœ¤×i×§í§ôVã¾õ¹—ÚYP­Í¶4¦qꢛõM ûdÁ¢p=Û¾¯g\÷|.ÍÏóØûª§Ï¹&[WÝßG›öÄ<ßÞ`/éG42)Õí9kécÈ·sà¸j&è­Œ8hd››ñç™°ÞÈ׃ö#§^âÞ× î£Éî76T;M!É»F×z”ÆÔ µƒ‚’2+Þé÷býL&ìÓWcüóªùU‡³ª4·õeü9À}ªù¿ÇªÄArÔ¼‹çYÓ+_9Î\M.(µejùtV6)Ä}‹ßJ/æ<åiƒYâ<àÆußÃ|ÎÅçjü<ïß­úAœ/ïÝ1|G%û ;ÿlr4µówëq£tæ»ín/ÏÆßSÃ!“®_™)Î;ëÑÒÓÁÞlzcÛ\šÏøó€÷eösB‡E9ò73š ŠjKŽî>Úý9•´®d§t6"Ò÷»ñC´jx¯âk&¨më@a¬1ñïƒÿ-<—e$æ‹ ¼¾äÎö´bèÌø ¹k7DQ»ºú7CÒY—kùuކ  ¼¿6–x2ƒñyï—„ó)Mmß ß/âýGó6ˆsvSvþ"í^j¿ý&Ö?Šêo1r̤t¦_ÓºµÚȺíQc†m®Îý­ùœ‘÷ý\§Bßã^ðüâ,IÛ½~ÙÕhj9vm¥›žQÔ«ê„Ú ÓÙO»º  OûäU¿eãëJ!O[ÚüôxÀŸ;œKÜÿÄëWΟÒ!äv\2õÆçŽQ¤9ÛnÛÂÍé,vWd+iÕ4SþCÊÖÛ3ľ©¡xž²™íü× ×¿°ŸÔ¬€¯aâlH°È‹¢â3“6÷H‹¤Ê®?Ô)ÏÒ«½Þý‰r0U˜Ó0§W°è›êf› ýj âÏc¡<ðâëCû÷c@œ‹KE«"é—ŸÏG4=I‹#Ö©&\Ig™ÕŒŸ…4P’°®fâyÆýQ? ¼U&¶·x>ÐìÕi|Õ fq_²ž%©}J8‡š‰8õ—­¿NGËË9ô¿¬G¼¾Ýä§³ræo|æGcòOu]ÖGÍ×/LXGö¢›%ÆŒ7oî+ÎJŠçµŸyݽRëAÿ†}‰ç½U/‹sä÷K¼úc¹=t{¥&.‰EÒØî ¾®wžI{\t··?YVéK©™XWØ®63u}wµ¢½-G:Úæ .Pîâ|q]Ö^ôÃü:%ˆ³°¡e‡`ùZ¿èHê½ä…{t÷óìÂŒ_ƒ_6 ¡w™7zÎàëK&¬óÛRÒÑÒÝ·MêFÜw˜û sÁvâþŽØŸ!΀Éñ™«[ï 7í-'’6Ük”¦œtžÕ,Ý"#ujI¬ à/¬¤AåøékW&œkî-klþÐ|ßCxÿÂûQ"N«¨G§• µ¶Ïm|HM‡*ëϳi*:ßÇó¦»ÄzýfÂóêÆ}‡…üjj;OÉ÷¥„îÄYg9®›°œ¾ïÒððïy‘ô°Vˆâuö‡RµœÆÄó\ŒÞ¤lô:ÙC<—ßݶ¾áû`üyP`}ƒ8?[&KÈ=¬‰ûîJQÔsrÆ­]®“2@Ÿ7>Áœ—wJ¼ûXÅ¢fNŒüq‹„„sµíèMýà狪·'>¯åï‹ïëÚŸãV"Îü¯}ŽÕ™û+%ZŽÛ}EÇ«.õ0i¯Ó¡ûþš=ö'¥e ;Mì;\m}2¯?ü‘mŽ/®?…s€‚NÕˆ#¬Ã4ä¹*øIÕQ´¾Œ>çŲëäïðhlVèpê’ž•ÛàÊtÆoÀ÷¸7_pòs^ú5Ä ºýÉÉÖófÑÀªqÉW£hMôªõeg\§üž›;&ÔAîcÞ3~^£Mc Z^_NžëWÔÒŸj/Öë§â¼ÎìÅÏi õM<·†8¿OE5§'–*Ñ*šN'lžT&ð:õlÔíl¯Äá$,#ÕŒŸwjÙoáã–¿ûPÛ²¥!‰îâùËâ6zá÷-ýÄù–pž(q·î]¡Â£¨gL•¿¯‹¦~–qMŸëtáÕú-þ  êG×Fôø,˜ ó¹2ô©Å~ÝH¶¿×<^·½ö»^ç_¾¸«ÿ3/qJ1}œnTè,ÎÑ–äÈ…s=I{Ý2@‰¦¾ít;|½®Óʹ #ÙæOçmÚ9qÊtñs+eÛ'àû<ÏøþÇ<í‘ YgZôqFœÎUÉ”ìîÕÐc%ÜöR±³¥*hx¦\]ózÓa¢ïùt^Gó¼ãó<ãõ”ï¯ØÏ¡ä–÷#mw{ò³I쵬j︽”}å’×Ójˆ4%ý§Jº¿â†zHâœãÆa~^DÈW›_<ŸCóý^^—¬úAœ¯¾¹å8i ¸Õ¾jjÜ^ªqë»jçK_'U›”×í‡R…—«¦]©£æß«¼°ëí-ÚÚ^§Ê… ^YKîÙö'ùzGø}Žø»ÄYkÙyÜ¢Pöç¾ÃŸ—*CSv9Ý[ð2›4ÅþøñÁÚPÂ='s•ZœwUbü¼ÿžù~.ÿý?ï[àâ¼øñòšk²%L¡u;Óºg ]7úvÕßɦ٦_-è8„Ê÷îŽV9˜ ϵjŒÏø¼€Ï¡øóÁcÄ…e†›mÄþT'Šç“¼øþŽX7Ý Îç1c\JÆïdÛç¾^¯ØKorå?¯öÈÏsû‹}ç4&ìÏ:2>oêt]±o;+ö)âyf™xN¡² ›e9r§62uÞﻘÿƒãUï=Œ¥š5Ý/}ýu6¥NÖQnò£ ÔëÇ.üùöÒ6§êNÛ\’¯O¹ï»0_©)èq&o/J¼›¹.ð]²GåX¶ñ*gÓoÚñnŸþ9”j< héúr¦øÜùÜvî—ÿn¯øï<«¿8˜½½s«sP9⸸FüöôIyóѳqT÷r@¿jŽÙÔgô&ë5„J$ ¿œ*Ÿ-Î'ˉû¹>Ô«õ_n7õ´ù‹óyð~[¨kJınß¶Ò±KWè©Y™Y qϳ(ý¯uK]¶¦±]§îžs׳ŒŸ»scÿ7Æ´³ÍóùþÞØÏ%Ôˆ3ÏmyG¿ª‘lÏ“‹£wÓÓÜ­KZ—¿•E~7 ŽÚú’œ=¸vÐc¦¸_Ñ™ñמ+mlë~ŽVØú¼þ‹y=WߎdåMõ˜yKO;÷´w«w!‹öû©o ŒQ’é3ýžðò3ØËñ£Vºwg|ÿ”Ÿ÷âóUÛ¹ù7¿ ÒõkY`^h@œÒeå°RÑlå®Ð e½÷Q¾sòú™ Y4ûŽ|ተaâïoÑçZÏùvcü|ì(ïËþ·Ú·´}^ü< ?6Ñ×¥öª¸ï‰8‘cë—Ϻ—ÕŽ¼â)¾ÃóÚVÛ›EÁ+Ì÷ÜzPôƒP|ÂSYRŸ¨µsR:‹ûm®¶¾Cȳs¶óo*þÞà7¬Ü.ö8,†}ãy®ÛÂáû)ѹÎò(f²; \8œ–¬Érüe†Š óÁö<H8¯Ãçú©^|‚Ÿo¶¯gÄ {šz{çÎXÖ8²9R~?]ÙödÈ ùYdv=8pöóátíÈþ‡÷*ñwLžŒŸ«úÄ–¶õ¯Ó¼_°Ï9âtjbS¿#q,üåÔ¾KÊ Õž.œEMÇQëև𯰦‰ýM [>ó÷Åç…|h2QüñÐVâï]Äyâü¦Ék:넞½9¿ïGמH’ê½Õ7‹ø¼ Ìï³›d™~¶Í§ø¹+>àç±yà}ÿÜ68>Þåôñn“â>'ñý™ßk~÷[÷׿{mï{¨}¯ßç{X˜ïµÈÔáè{ø.ßë¢|}>ðõ·ïë,ê.'­x—S x—SQ÷u:ˆ÷uߺ¯óí»œì½_íïrúOÜ×ùw½_Mïñüz—„å¾â úxÝÇÚ÷ÏÖ>'ñߟá x¾òû,wwÚßñT˜ç«½×W èùú>¯¯Â<_MÀIñ^_ïò|-ÊëKñw¿}]Q¾×¿Cgñ»Œ÷Ücç$Þcgzë»·}¯íýí}¯ÿ÷Øý]¿ÃŒ"üs »«Ý¢WÑ¿ž½kßÿŒÚ÷¿©îYò&8‰>¯…ÝÑ^˜Ïk˜ÿD¸˜¨ïó+ÌçÕ’ÌÀôþ`oû¼å&ùÀ{‹ß¾ß.8A0A È%ˆœ!Èrˆ(8ý÷Ûy@hÀ b @Ñi-}„—äÿ»í$©äY¼^!VB°aÀ ”®È ÞpQÀÀ<ì¼ ó¸Î(ÄÛÐrw§öc¿÷?®æýoë÷\Ä_¦ƒàíÌ@d4)2¬oW{ϰ ÑÛõ}ža…y»f9’Zûžaïòv-Ê3Lù÷Ë!-p†`Tïñ·¶x$Zü­Uuk9¥Î• doˆK'Þ[¬ï-–ChZàük{¯D{kÈÞ£¸@j | L=@œ©H!Ô0`nþ÷¼Uâí…yóvg» D¯þXû>Ö>‡¶öI€8‹°…ÝÙ^˜l¸˜¤¯ŠÑö}¾c…ùÁ:!¡ƒ@Æú½í [”ÿ˜ôï4v‚P‚@C0Zà Ѩ@&ðƨ.äoI œ!¦ ä•8CX* ä˜8AdA È!6-p†àT xCx:à ñ©@&ð†µÀBTLà Aê€ D©¹ÀâÔ ªy@¡€b f‹_,Dk27\o 0ˆ88AÈA Èí¼ ó´Î,Ä+Ñ¢×}¬}kŸÃ?[û¤bü\Á+6BLD%02¼¯X{O2•èû>O²Â¼b37’Z÷ždïòŠ-Ê“ÌâWaEp‚0‚@C Zà ‘¨@&ð†XtÀ‚Q¿ÇÛâÁhñÆV×ü±½!(p¨Ô ø@\zà©A&ð†ÐtÀåÙö~ŒöÙj | F=@¦H!Î0`JˆÔdj¸(Ö¿ãÉÈïs/Ì«G_Èî–_hHÈMË^û>Ô«â¿RëŠò‰ýßRãÞWßþ¯Ö6Ëw¦.H0“eIf¶Ô6Ô3c!þ±ÜwL+úÇå9ö.ïXg$¨êßð{Û?¶(¯1‹'E¸˜èÀ<ðÀ I2€µK œ!ÈÞƒ¸@j |$ˆ$ˆäEgY¿B,™À‚шF r7Äã ñ¨@&ð†ˆtÀBRƒ\àAé D¥¹ÀâÒL r„¦ˆMò€_ H!¼0`JÐda¸(Ä@`dp‚(ƒ@CœZà ª@&ð¶óR,Ìã:·/EˆYÿ±oûØ·9ü³µÍC|ý<ÁKV œˆÀ<ExÉÚûŒ©E/Ù÷ùŒæ%› |Ôúô{——lQ>cA È! -p†0T xC :à‘¨A.ðXô@ÁhŠðÍæþŠöÞÙ>”H * È ˆË$˜äM$Ò‚Úoû+Úûhk@P@Œ … À(!L#Aœá¢@ x@¨Àéoú+j@^>< Ð5 ( x°Ü|F¢¿CÁÚ÷OÔ½§æýw«wÿ¿k݇:÷!Þb¼¾Y>k½ƒà«2$Z¸˜lÀTˆo,÷s*%ø‹qïØ¢<Æ óŽuAª?Ð_ìmÏØ¢¼Å<èÀ É2€I¯ÎH|ÈÞ€¸@j | =@ÄRˆ# ˜-û¨e.ŠäF$äˆG\ 5È>’H & È ˆÊ$–äYÖ¢˜Ó€< €Ð @ ±…3P¢Ž â LÀBŒNcÈrˆR œ!LÈÞ¨¸@¤j |DÏÄÂü°ß啨€ û·ý›Ã?Û¿y‹ÿ½ÙAð‰Õg$bÈr$¤¶ŸX5ÈHTè«@€IÌ@‰ä5Y>±y@¤6); ˜ n2$y¸˜èÀ< ñ‰N@ÈrA œ!ÈÞ…¸@j | =@$‹HßãÍ}íý±”H!ª0`JˆË¤XÈ Íðì·}íý±Ã€(!F#Aá¢( x@œÀ  @¡jEì¿ã£Ì@a€âf „È@jñJf „àÀrcj8 yhùc?wûк÷±Þ}¬wRï,ŸÁAðŠÍH¼à„T„W¬7’Rç(øÄJ˜HP"IÀ¹¯X= y5 (Ä E"‡3P¢¾ µ-\| ¿Ë+6˜€’>8!ñƒ@CZà ¨@&ð†tÀ‚Pƒ\àaèâЀ< €H @ ¡„3PJÈ špQ8 `Hò€B2)ÄÌ@QéÂÒ€< €À @ ‘…3PBlF …à€( <B|aÀ ”¡È ÄpQŒÀ< Êàa ‡@µÀ"ULà ±ê€ «¹ÀÂÕ Ä«y@Þã…·H½_X3PBèÆýÝÇzçðÏÖ;…ø¿[0è Q27R\”j |œz A‚j@#QÀÙò2$¬È´ábâð@‡‹IL@†df DR ‰.&w 0$ypB¢ GÂG'$}Èr$¿8C* ¼!pÔ ø@z 04 ( B$aÀ ”‹ÈÞã“2jôÉV#ATᢰ È °p`JÍøŸlð€ð"€Ó[>Ùᢠx@À ¢ @qj3ª™ÀBÕ‰>Ùj | Z=@¸°H!â0`JˆÙdt¸(j%0Ä. <˜€ BÅLÀrãf ù(¦ÞÇþÎá߯wÿ•Z÷½ÎYÞ»ÑòÝ!Ù2 §Ψm* ¼‘€:à‚$Tƒ\àcéé,¿EBJ‘aÀ ”HL#!9ÃÅU#!QÃ(‘° EÒ†3P"y@†ÞÀ÷¿àþµæþòj¼þï›M¶?Ãze^Ù=å,ý±ûÁfÙÄì§ÞZ§w§–s×êNœ g‚_¨3ã¾²¥QÃÙS[C²œ£Éw‰7Ü?Lð%*œ»+A'{?xÿâ[`Ùfû¼ 4s}úº…Þzpê‚ËboÆÕ0öÞ9ifÛµC÷qá9-\ŸBŸu#Á'Sô^Ï¿ÿ8¦=~)Á÷ÁŠ˜v`æa=Y|?è'ßòã郃âzDÊlfÛêñš;C×åã£Qº°1ýEß°ä‚ yÏŽ…rfä¨S¼ÿ!Ï›QçYãåÉq4<¨×½5]õÔÅom×öž”õ}RÙ^§ç0}‹Óo6ÕÆöÞ0#u#ýiÏÞ¡³dÄýßyÞ:÷34÷­T¢Î»Ç;ıíVŻ쎣2Už¼§–zº¶ÿÞ‹Õ»äíðÀ'¶ØSÎ ÷sâãÏYå¾{ü9eB£oÊeGy_d†lXæÕè"Õ¶Ýy¬i½ø¾ÇѼܻ_ g7TÛçÌ[:Ü4Þ¾Ì6Äó9¸ÏÎ}iþ¤Svv…òÇÔ¨óÏÂY7.2ý¹WD]¤è÷7ȧœžŽŒi\aå±qtøÙ³õ”á,¬ç€%fŽdBF#Sn÷ ãþ•‚ïV»Bù zÔY0o¤s‰½Цíú_/RýéîݨàÕœú»›ÏI952‡3£]lm9ã:çþ¬<¯‹ç¨™çÿXlÈ•¹ƒQ.1ãÇXûm²{ÚmHÚ=“/¦!îáÓpñ¹ìeÊËô_ϤKî·Ä}¥ÌóO%¨c´t‰ ΃Gï%²½¤*^ýð=ÚùîV7«ñ”§ëÖøW{Ò1úSºóxSn¼ð<©ÿ_rùo•ù÷"CZ;ÊÎYÔé2›º"`³âð%zuf[™ ïѸzÝ‹)-}(æpоJ¿Ïày¸ŒçØpÿ`îcÏ}íùü†ç:uƒ:É {ßhx™¹%nÝ(—^¦§–/V÷u½‡çc›4¿í(Ó`£W/”yÔ2$pzˆy Râþ—\—Â÷ÿÌ”çkž;£DFãÊìºtè2[RÁ;úÇË$ëÿñm­¦÷hµ¼ÔP_Ñÿ2”yõÜ¡=ú|÷C#ž®j<{Öò*oLyËBnDOâ¾xFÝ N¹RSJ7|{™U¾ LºL'·-{áñö.½>ýx}]_ê8Æ|Ê^œùuý9ãØ Ãô¦º M÷trn2­—èG÷JÌÈwlsï÷˜»Ñ½LþbFݠΖjƒ«„;^a†4Ð5®P¼ì}õ¶WïRBȽÜC·}¨÷Æòíâ53Ä=9üþú‘˜fòµžŸÏÅ|ÍÄóጺAÁë ÓXF+Jî»BÆ–Êí¾ù.±d;Ïï§È/ξ<“9>]Xüä|Æs%®+J8Ò2™èï÷Ðä[*ø™w/œ•+»tcNE÷3WX@…ŸÊ ¬|•Þušp—fô®dÙ|£'•s¨¨mÆZY> ¯s̓í®f0^ìJbžqOžhî+Áëz½­ýq…•­|;«DàU:’þ&æ¬Ë]*6®vç—ƒåä’ÐT7'œ ß·‡i>(¼ïa&?YÁÏôWGî;\(7uòv½[~¸ëUV¤ºä´¬«dpå­Ûî.5ÍÞzËîÞZÆž•©|;œ5ò:ñ~Nš‡)‡rõ(›[2ÿáWªúàïs¦;z^Êñú‚OèUVÏûR)Õ` mYYµ§ú] ÛzsÃî­£¨ý„ð„¶#ÃØ±=†|ߨ‹ñ|sáyÐÓ”oÈ¿¾àãªQ/¨sâÂ’¼[éWÙ©A+Š÷¼ ¡Ó?Jš…Ì¢’Ìõ|¨Ë(ª¤i¼¢‹Õ Véǃª‰Kþ‘ÍuΟû\ÿ<¯ÖçhÚÔìût¡ìÙ›ç+ßýì͸.÷ æýÌýj¯öõ¸{@B¹ *Ôr*âÙå ÍÚŠ×è}-u6î¸C9 Ï—j(›@ºù}_¼(3ƒµ¨2{ß¶NÞbn¹Œ¸o1÷[Æ9‡B}¬Æëg능c<{¿£«»ÓâkÔû{íÉMAwÈ8ü®ò&£M®^Š!™È‡ñƒ½ ª]©>¯‹ižÁßݤóýƒ;Ïô¨ÓìÆÙ:Á÷âÙ¼:kmSß^£ŸF Ÿ1à•|úG¹„•^ôdÙoKûŸÉŸ\œ¹s„/ã~dž®»µð¹ô|àÓZ1ã@M{Sî‰Q/›re3÷l°N|Ï,Kíö}è~z÷zWºqÓ;4úðŠˆë·<èÑ£ÚÉ]ŸÌdÛ Á/“÷©r`z÷­çãωž:¼rFé‘B±u:Ä lb±ÌÇ®_§3‡¹n,y‡¤‹'ŸX|NN#«ø8øæ 1oj2r*:‹ã•Ì”kÉ}Q/wi1ñr§žÄ??£^Pç„K;ûv löåZ­ýÝ ½Åר—Þ¿M½«u,1FNË·,ìú,?DœÿMe<‡‡ûós?q¾Âs3„\q?uŒ1÷£˜Ý CÍ"3nPƯ¥ƒVÅݦ M¬¯wH’“çÂfߟ|£eãëYþü5í‰ûÂú@jÒ•Q7¨³dVËð¼y l¬þ¥›];-e {>èÕ¶Ût¹ í‚OcOª^ÍÏ!~DK´}þ]ò(?Sž.Ï4ùÉ‹¹Â÷/-´ï BÁ':y|Úv1l†–äµ nϸMWmØq<ý¡,ý•c 3ÌꬒüÏýå9Ü×—ûâòñûV5»¸ÛóÅç ê 36T"+ø´+-5˜]1ÚiÐmÒ(µ¬3ćÆ?*¾áê&?¶yU·ç ø1ž§Æ×/|ÝÄ}W¹ªyŽŽuv”W×3"‘-´§í^<™ªùïþ©TýÛäÓ~ t÷z:8¶îÂ'~â®Sâh;â¼î·šKŒûó¼¾ïÃ÷IÝKÅõ¦°¿$GÁ':‘5XÚþE…üd°ºûñ#S3éÀ‡)KŠýâEe°êÛšÌæo¯ÐâÎK?vyeÛVõ‰x ×!+E_bžobÔêÈŒÆã‰ìTGCE ù8Óõ`ŸLʉø¼°ÍXOzÝ^ùzbkñáå¿ø³ðÕ§4°ëdÊÿãë1þ¼ç9ëB~“0«P§Ë.kIrÉ$¶¬mÌŠû‹S¨V³ÝGJ7ʤbýòRoÈ^~™_Ì×3æûýÔ1¸ð×Ý‘È}¼úqoY{I·÷ÐQñ1™§¶÷¦ì”¶¹5?ø‹û¼ÓŸWó~ããy.†ù¸/CŸsÖ K‚Ù¦&S¨æý4ÚÖ(öéd©Ž¼–œûÎnŒ=«x}ïŠ öéŽvíÖÙ ñ<ëS.™°›æ(ŒÿéŽÙå'ÓoQèýÈQg†±p"»÷¡6žˆé4é¸OÐÙ*:JøT¡×\Gê”u´ÇÇ‹Á¬Þ¹lGÿÉþ¦ç®°žoO|½Âߟšç%+Qg¡ûœÊÛ:'²ˆ—¥Z5’§SĺÚ#Š¿ºEa-uÛßô'Ž»ÁÌwÃtŒ˜¦œ/¾ö“Óù:‰çUñý`£~P§Ë‚©±ÙGö îíN§c]çͺy‹ O‹”î豫ƉAŒ¯ûù:ž÷?Wö’ù¾£ùù…u‚üå¬@Ó­?êçÓÞÇu3Æœ¸EéQ?浓Ód¯§ 6èÙ…øŽŠÒ™wPÇ4 çŒWMçXb®"ñ¿gÔê´ê~¤ZûV‰,̸`¼E=&ÜO龿–˜säI+ºÎj=4€=¸˜5™Èø<‘ÿ{ùúXÈÑ9ò}xaLX‡[ü”+KíW?²DæÄÂN¸EƒO>“Ôžz‹›K&«>ž´žÍV.éÏ"ßý¼® d|,¬O[ÏWà¹$æ¾ú¼¾…ëQ¯†V‰Ì¾¬U)»=·Ä¼“[$?²héTojÖÅ78k´+žñ$«ÞoA¦\yž¯ÆÏùxÀŸæ9b2Ôiú¾ó,ŸÈè@§I ½E+-¦UoSýí¾/¹Õå“5+³äøròc™Þ¹®ü,ž7·×?MMùD|_Éð¯>üDXÉñúS 1¬åÙÀ×ïm¶Ö‘÷ºˆ=;rÓÅüà ´?(Çÿlq?vºo¹â-3žk#|íLçI|LØïQ(W‰:¿ÙÇÔ©_"‘õøÙÆÝ{ªŽ&Uöð­u*Úzy¯ÜÿÔ‡.ÿqtÖËs®°-°Á…@ñܧ'å^rЛ†ß‰½üñŽ‚Ý~?{ÒÙ@þ<–ÑcÃc§zOS¾Ï%â¹bæóe5ê¼m²èÁø¬[]ë?lš¦£z%|ûm÷L§Ÿw•ZñdžÉX¯·½x8€ºíHÌã#>?òu˜Ö湯zÔ¹7­o]Zû {y}{¹ ¿{ý.éôñè¤+nc<éhù;sú1çn³;úgй1ÍMëI>áë#>n›çQXlË• Þö ÂfMKN8§>à’A÷7f»”©™N§ÊZW\܃†ªüYcÀaãûѼÍŸ“¼Ä7Cr4õ5†gÐÀ‰ŸZ+H£”ÁǨ*È)ÓAªÛlíÏj€ƒL9Â<ß—çÏðýv~¯¡Ðy%êäâÄ—%°ŠžÊn½•AÁoœƒuiôÈîñä½ïÜM:?btðÜ  1½/-w/ïRkROqœ~k:1~üÕœL9oF NVògÿV£XÝ!å"}1ï^•ÐêÄ©4ò\8Úqõjw!ÿê?3ílçvÁâü»'ñü,~NÁóDãÚ¼yµ§A/q}*œ(QçÈÄÝVšÆ ì÷ç#ÖçµÌ¤1c÷Åý¾)þH-_5´é81ÏÕÕ–ãp¥Z‹y¸îÉT»^â¸Ñ]\¿0åˆ û‡= ¿ªPgBZî¤oã™—™IÎüf]wV•›T¦Gëyr:9ðEltu?6ðÕŠ¡BØØ½«Ö'ÜLÝøËSÓþ?Oâ÷M åРNÅÌ¿]gëä~ÒWû3É÷qÐRµ[ÕϘêÑô;O:lˆ³n¯óM‚ÙÀÛìhÊ®ö¦¼X~.úe>²ùóK: 7èo–^Ï&{>ÙLzñðõþßڥѓ¦C\dñ^äœÖõè©IÓÝæíæyT¦óBþÜçÏeþy™ïïZ¨re?‡;Ôµs‰gêÉfv¸Mï¿S\ÚU&¢z5«ïæMåÖì›7¸ïTv¾Ú’2“ ‚ß'äç½Â¿[kÊÕâ9ÐÂ9€poA‚:ŽÓ)-ãÙ›Ó½ä¦ß¦Ïi۞ɺI™6Û.òs˜§±¿Ÿx“:'Øt߃ç }lº¿"ÔµçÂ= ê9G6èQ½ð5{nÓ)‡Ñ›ëÇÜ$¿¸fÖµ'ÐÓ%‡¾'ét–¹åâóg‚Lû!|?—÷_wð}@óóD9ê\[×íÄ…%Võú´Vk²oÓO¦'̽IS]T¯›O v5~0Ó¦*»¥ì b¯‚j¢åÛ‹93í‰Ç<U8ÏèA|ÿܨÔ M+ñ¨þP s9㾩Á²ºQy|Éa7ioÒµN;fùÐ%íãå.Oe.óûiæçâ*¼¾Ô°­^CîOqÍ›ï}‡V¦ÖŸëbs“Š5ûP°ÎÚ›Z…æ̸®,Þ/kfšWðç1y®Ï-3êuªlù#þU5ɧU©]wèH‡Ñ3â?§Òr™Ç›§î^Tlà‚òƒ²•.v»~Ì ây¿$ìc45åñsxžëÄ÷éºA·âÍŽLžu•e7ºV/5óHœzdÖ±vùÏöZexPçj— 5û&½ˆë~€¿¯zUš]{ä'-´´Øž+»¼ý”së¬Ø–î-ËeQÈf—4¿¤Òo +ëåÉéªaÚP%”•ñpt=¾_ÕÚ´ÏÎ÷cø~bWe¯ÙŸ¬:z?Ôy4Ö°Ã…Y5¨é?¥k¥Þ)ëÑa}*5¨Ü<«ø!¹ésããßÎè¿ì_ðç¿ù~¥ u<ö<7râ6õ`›Ê™þYô*ìl﹩ÔÉ+Gu½‰Y6™úós?ñ>^0ã9}%º?ÉNìÙÍt®ÈçeyÕOWžû˜½àõ§½óßV§Ôö䲋óñýYtjÌgÂàT*¶¯ý¹zž4Z›‰ËÒý×5¿÷ÂÏùyÏíÆaßB‰××äÇÈÓ[/³ðÃõ¥­ïg‘ìêíZ¦’´IZxQÉå{žÓ0¾^âß;ß?öÁ3Mû­<^Ø÷­%èuË Is—Ù€)އÊW»K§®•ýвT*eöØ/2Æ…Ú³3a†‹`S‹9‹ÄÏÁø:‰?/Í÷[Õ¨£K­0g`ú%v±|ÁGÛîw©¢6ÜþXV ÝûuÁ¢ãa^Tï´ê¼·sS¦¸ìŒÌøþ4¿çÁ×/|SØïk)öƒxŸ uâ_vsÖ%VÅ]¦ß¥™>kÊŸø%…ºî Èè1“¼ÊVJ!ìüY‹y7ƒL÷Jù¾®°ÞŒ7­_ø+ì³Û¾ƒ:!‹«–h´)Že55l|Þ£ŠOÚmn“B'Öæl\´aR4k愊ùs!Œë‚Ï[ üÚ÷1Û^Ð^ïÕ°ÅwÊuŠc'+ÎHL[y–<>8÷`å*wä÷†åJŒ UgvÜïÂø9詚ÓÔ¹‹»‰óF™éy%ì'8Š÷¡Åsaý¥Fþ¥ª g)Ø1÷r{ž¹GϧdL^ú1™RËŸ:|àÚhX ²—ôþLÈÍP¶ó¡!Ój§«¾pì01Gµ=Ÿ0añ¡ß_:Žþþ˜öúÄâç#ì»êQGž´þÀŠ ¬¦ý÷³2³ñ~Š6’’iÁÁ†n>£]©ü‚ˆ(ÆïM>eW¥\n?*Û¿‹jJB/Ò&x–vøüAœïYPûÛÕ~—×ìOûn¹ó›K{A;seÛ ×èª]`3?õ {^RO±¯Í\v-™ª¨m¶H«Ž¤դד”þÌeYË^3BL¹v|~òjâ¸Ͼ}ëÈßÏ’žÛ½³¨w¡ù¸u•>ô²dœšy¶uôdÓjÌcÉÔ4wæ*6i4Í©R¶’Â2ˆ%žªêº.œçêv0íóð¾_{l¿â3ž8òœožjÔêVe•¦¨ÙÊ»Šz§:èéJŸ4µj}²¸Žs'cÌæ´P6õЦ܈°ÆóŸ…}«ªâýøÓ|Ï…çB%A'¨3ÚxQDÍÜ[l?ÓÈu lge'S»mžKב“~n±Ïdä3bƒïþúFàu5ˆc–µ[»Ób°­—ž_bÂ64™NNÞZ%½¾'UþPý ÆÏYø¿—ÏçÍ×q*¼Þ³ïóv®qž=q6tuž¦ö™wzuëdºRÝpÒíE;Gf8>hb‡ø~¼Ð‡ML÷gù9:¿b>?T£Îa.ƒß´8ÇÎ/([¡M„ž6u ¼±t2áIz÷ô/Š6\¯ÝÌt½’KÔ›ÄR³‚:>Øfo:Gá÷@ù<´pî·°¾Ò£Ž°u–EU­·-r«ž†Ì}Þd¨^Kgj=¾ð|¡ :šöp­2”ù/8þQñqžhO|žÀ÷Œý¾+W&ÌSϰºÝsOèÉçÎMÙ«SZêzÎùq¢'IŒ|˜8Žc]h<—nkÊ æûY|þ$ì8Ê'– Ž£¶l¹‘Y§™§Ï¶«67ô”°ÎvÉÄuZ²eçŠû=èái ¥xŽbšÏ—V¦{Ÿü\ƒŸg™¯×d¨ãzÜþúÔ¸Slã)õÃ#ô¤žœ}±c€–Ê·iµ2s¢œ$¤/eãç²»??qØ7˜ ûþÍLã?Ï2=GÅóAóyšu¦Œ^›!YË*¦ž?Å~×Ó”-˸ ÕÒ Oˆ-çÝi÷D—öås™ñøaué~>Ÿoð{æ|ÝSè¯ßDúf¿°cÝš`È˦ýs&Ï+ÑAKÏOO]WýÁXšú9¡ßš!JöG¹ééúð Æïóç¾0ÿ»iú¼„u@»BûÍ*Ôwô…|iÖI6Ê*ჴV6…ósJu-MìÛûzœ;ÕÞújhÒ³ÅûåAÌ&}ßË® »¿§À÷éxžPgñݜ쮥OŠûâÙdYÍçIóç7èÃk‹¶Ï”SIöÈü~aì¾ û~ô± &œ‡ö&~?›ïŸñu¡p¤C¡y‡uÒ£û–>Á.ç:Ç|vȦŽ‚/çî»A½JO{þ}f<†³ cm{j?;;ˆe=èh_ÖÅ´ÿÀç7\‡|e¾°Ø+›§i0ðl‰ãŒ•(¾µo6厑Õñ¸AmKwë³”'µp>QÝg{8›¥ªf}ôtã:6>ˆÛ™æüÜ”çÕ›ß/“ ÎþØjU?Æ|Þ=ðØ>"›eë&޲¾AGNìoÛΓ&½Ÿ»¿Ë\¥iŸŽß3æçÀ¼Žù9© ¯ÛÙ0J²ç=îú¿ñʦy}ÊÖ–|rÚ„„²÷ ãöÚŒylÎâ²mš&Ì—ºÐúÕ­=/#þ}›·r¼®±Ý‡a-vuôóÏ&Ã.Úø•×é”üô(é¨qT1e(:`>ÛqbÇ5¿iŒßåëž«ÎÇ]~•ßc3êu ñ~{c˜ÿ¥Qae•ÙôB3qãˆa×éýÁecÖŽ&ããlÃ<¶Ôs\-PÌqïH­ú¦Û™~ïÃïá›ï«ðúº³?%?ÌÚuy{ÌíÇlšÐ]ú½¤þu Z±§ÞŒe®TuM'uRˆ’ÝìÕ=©qëw}I×ã+;ð<²3ÍŸŒý×{±¥•äxÝC¬aKà Ëlêññ×7×H9àʲþ1CéEúû_g‡3ùï-óƒLç¹üܘÞü÷jüþµùy¾u|K-:0íÍ6¦ÄèÆn[PçMù5ù¹ôÌ;âB{…­Ï½Î*ô‹šøÁ5˜ñõ?§àûº|½Ç×yæça{re‡6Á£~?›¤–üšús6YÛ…:ORÞ7ÝëúWXOJPçÆâ eÚÿ™µÉÖŽîrzn8ÿN¯¨k4¹Kƒüä–ýèLòÕ0™j>3¬Vª] fURjÍw¶ìjº§ÍÇÁçIBv#a>%ì#ÈPçV»š+rí¢Ùü©—Öd%eÓÎÍÇ^»Î¾FQ1O¥ík÷£¡¹ƒ_Ìé³€+»¹XkM°x?±ñþäû°üù*ä÷3­›Œú@ ÆÆßÃzͯÚeálšÒäÞ”¯k’s-(¨Áªq«`ÀÛóYÐUÏÍÃÚ3¾ÁÇwþ½ðý~ŸP¸W,Ìc•¨““tø‘Å…]lß¹ˆ“^³im·Ïλz]£Ñã×GÅÝD£xoµ€¥°;ߦuãëH~ÎÇïñûHü}˜ç´«Pçì±àÛ í`ÂùH6}v‹¨0·á5jiÂö¡½‡a×J{cS”QÓî~Œ?gùý6¾.æyÝw*9ïþºC¡s5ê°M%J¼V±ýº~ßý}6F­5“hËÈbÎ;†‰û¤ Yߊ=ö¯± 4íóñßðç9Ÿoò{9…ö­PgKÍO¾RïŸXß—cî-.‘C?XTÿ©KrÕ¼Ø-7óìÚVìÏͰ…Ì7Á™¢—2~^Å÷ãx8?'ç÷?Ìuj±7WæV¯bxH‹ÍlÓ³²á1århVH­zuv$Qÿ¸ñ[BÝ(îcµv®ß-dû1 ­\Èø|‡ÿNŒë|ŸŒßt&½©–óHêVi©u°õ|Ós·ÞeX™jè¬åеýï÷!>ðzü^H¡ý^ÔùTõê\×ÄålÙ©ÉeÞKrˆí9òã÷I´2¶’|ÇRWºþ }×íyl‚q"ÌÎ}:å« OÂï>½Lï‡Ï[÷µé߹Æ΅Ÿ/¨SÑýÌñì˜l_ÛúV7Ï¡OAcµ¿—H¿{Ïž3ŒŽ¼í8lÙ|Vúû¦ÿˆ ï+ #í¸¼J;sGü—ñšŸšßÇP¡Žð;¥P–¿¨}sE» ñ®QîЩD¢´v^µ‹¦îëKU™Su![òÄÚuL &Œ+â¾oâ¹éFàõÚR§öÞ{Ç1á¾jÍô²¯u)óÉá†nÔ.9TÌrâ­ëÝéóÓ=:Jé®®¥&µ0݇à÷ùï5ø}¾ïÊïšß•¡NuÃÏe~XFÆ{‘sÈx _#‘â·F´×ç¸R©KÆhF°«išCÇ—2~ßTXOu$~þÆÏ/ãÎoí¹`N7â÷&:Aá^ë*ªÒjF—V”CU>EN½‘“@ë>:¦¬o6†„{M,M’ôÌýyk~yÅ•í³ûPóaj̹ÝCÜG~'Þóyêh)9±d͇â:HÈæùÖf9Œ•Ÿa–Ÿc–ŸýµFóülk4¸ä4z,‘Ÿ]”2Ϲ °…0¢Dqø-B$*` ¡(€È ˜h` Ñ(ê}Ýë]+f1ò,Ÿ¢¼Þ¥˜J™/Ð)Ħ–;žåó5¯÷?ËÐ6Ï 5÷y/*ÇÇŸ­¶oä7ä—•sQð9´Ïÿ³Ç¾ÿWæ6â¿Coø¼Å<3CÞ…ä4f, 9#@>pûJÞ…!³Qý Y?r³¬Ÿ³¬Ÿ¯y›gýüYnãŸeýÄkA ò€  $fyRˆC,!Є ¬ –P NM °†p”@œ  h`%Áßzà1ň‚òZ …°TÀâRAdÑÀ B : ƒà¢¾P 2ˆO,!@Є ¬ FÐýEÞþ‹LZ $mÈ.o,@À _Ìüá™Ü¾ß˜kVTîEQ¹´†þ5üùÿ·ãà·Œ|üûïŒ}5îý¿¾ï÷Wó¼oßø¸ö?ÓŠÏ ßK °B3…ŠÙNhª`ÆR‚<à‚‹’/r-$f¹Œ•«Í3µcÍ2µ¿ÌdäyÚ4hÈn†¼Y`ó'YÚzà„æÖh`%È.bž…/šY ¤hh°DS+€È0^E+4y(Ð'4{ °BÇ¡ñ£š?TÌ`ä=¾@ ¤ƒ XB  2ŒUÑÀQA(ÑÀêO2z@d‘ªˆülž7«„°ò cU¹pC³ÇŠyd  24~4°Bó‡=p‚b€5„ yÀcš5D¡zà$ÁßÖˆÒ,gQ,!ÐD ¬ œP N ôÀ BŠÖ“è€ ¢ŠVV(Ð',ºˆülµ˜GÆóem!¾o× ÂX #@>pƒ ÕbÆb(Ðõ ™d¶n”(^_ RˆXõù²Ïëþž×)-þõó:'ñuò ŸšQ lБ Èј`‹æŒÔh€-5JlV_ ¶hÚ(±q}HÑÀ*`‰&V-ŠYŒ††öZ Ec«€%š[´@Š&W‘Ũ: CóG+ ¨ „ €‚Ð[ˆ"'ˆ#XC J\ ”X X"@>pƒhÔÀ‰ùÀ Š þÈn“XAP¡@œ ¬` q)ApÈbB‹yÀ‚‹ˆ.äˆ/XC€J\ ÄX • H!J°„0@dh4Èÿ"kV4À¢@ñj€-%ŠØÍ,k;h€-D% ÛhWKˆ\t@±G+>èÿ"o–ÏëþÓÆÂÅ>Ü¿Óh>þ}ëžÜ?s¬3|V±†ïòN lÐt‘ ÈÑ|`‹Œ@ŽFÔ[4cÔ3O[4fyÚ†¦õZ ý"O[lÑÈQb3û-þIžv>pC“« =9^m¸k‚¦×'4~ °Fó+ApbBˆùÀ ‚ˆˆ"ä þ? @"€È ”h`±„=p‚hb€5„£yÀŠÖ‘äˆ)H ¨P NV °†¸” ¸@d1À²ˆ,m+³ÜÙH°À0ÆA„j`!F‚ ‡ 5†;Ɔ»Å†;%¦ØBœQ¢@}H!T°„X@dm´(Ü¢rf }føóŸ6–ý§Ìëþ˜Ó¹‰¯ÀðY 5ÀÍ%6¤/Ð)S,Ñœ  R4© X¢Q@¤hX°DÓ*€ÈмÑÀ  t@†FVK4³è€ M ¬ÐØ¡@dhðh`‰&W¡Ù£>è?X£ù•@l!‚(Q¾@ ¤D”(  $GÈn‰Ø@(‘ È! °…h¢@C_ R4¡ X¢@dhH°DSú-¢9UÀ ª: C£ª€%šU´@ЦUK4®è€  ¬ÐÄ¡@œÐÌ@ކÖ[4u”ØØÃÝ4wžaÅî|@˜Z ÅØ¤–©èŒÎŸ;âWEçÊÂg{›¼o³è_`G'¢ù/˜cò]Ü<¤„ëI &_á¤V–²N+݉ûscÔ´Œûå/”?÷Ñ9€™û©QÇ–¨í©¢ëCŽnY0$‡¢v¬°¿u…g´hq| ={y°â“å?2ÁÿÁŸqfî‡Ç}Ù¸¿m)uèwå?Ù‘o"ø+éQ§uÝ»rû$ä:åÐìëRß²ñôéjÁ£gçG’û±Œò{Ýw¿¿è Òœx>ÏMáþÜÇÎ<÷Ób_®lýúU3=ÜE[ ö¨ÃsHµßruÊÕx:ò¨ãò|ÿjê…!Ë™õ‘A—5þ¢Ÿ~}Sž÷5ç>AÜ_ËüýHPDZûµÈæ?VXìœC/«œëžöS<%ÍÑÐyÞêbÕyhÞ2æ½»o³¼·þŒû?ð†J·fuN]—áÈý©„ÜßöÄó' ud¨³pãÉ9Þ»÷QuC,o¿zÝÑ8O+ß7^üZéL‚ïÌ2ÆsF¹›à ÜÁäÿÆ}®Ÿ{17R🗣Nä[R:±ŸæYÔtvÉ¡'3?íêàOªòe׺?ìGÆ8ŶËX\&CKíôgÜÇ‹÷'÷‡á¾³ÜÇ…û¹ê(Qg]ŸNq;¢°Ô×Å\sèØø_ÒöާYíåGô£¥Ë*8Å_Îæ]ý%¤üïþŒÏÜ'†ç ¾Œyþ<÷Á6êubWí•2-†z<—Øuú”s3«Eo»ãK/…þè%úµôós®lYµQ‹²ÑÚ¬¤¼>=rÈf«“mZ’†F§”{Õf(mêyNŸºeû¹é«•çûˆ9GÝiô¦Ñõ\é.úÜ¿}c^;r?¼B~±¨3iBTæÒ'¨ºoˆS[YÉb÷ºÌÛ©!‡æÏ.ÚŽ CŠñª†ËÙ6ý´ãòü˜õF帆}e&_Ú&²èG׺¼vt«þ¢û½y/L9mAŽ¡ÏRáý¬ê|òðÀ@ 2sÏ7štîžïÕò+YDŸœ‡¿ŽñcOm›|:WÕQôt4å&óÜ Á¯¶«˜.úӠΰ°îÁwO’ÓÎâMoãýÜßñùÇíÝð¹9¤Y}?y$õ×”îºyøJvùþŽQ=«0ž»¹Wæ3!¹sÓ¸Íýû¹Žàû%êuŽ;Ø÷ð»ú ¹Wú8½qçªûǦƒ+j¨ñÄgSfu£§oGzï9¾‚5?È[|4ù¥ÆŒx¶)2]jò#|‚{ò5U¾ÿ6™÷ÆRæêᕦµË¡ËA¹T÷®’fôÐËïe®ä:sjÇž+X§ÁýZ,bB޽ŒxniÙ]õ|åô›IŸ\7æß‹un&ûu8Úâ9{ìž÷Ø;¨&×- £X°cGE‰;va bA° XÐPŒØPÐ#öXPl;*ú…Q!v¬رcŸýçÿwœ{ŠgfÖÜ™YžµÞŹ÷dC²ßïÿZÞgYë{°_ž¿¸âá ˆÜµa^ôW˜™}Òá\p«ìP{yîLv=tÙôo-úºàÌ¢õú ¼÷gv”ïL<-½_ðõ÷¼ü²­Ì×ÀsßîÁ¢n=~¹´4úÞ)/:ºt8 ÝzòDÑ­&?\ú僆œ=·•Í÷­<ØGŸÛrwÙTòØð¹S>¯Þ/{óÅÛ^úû,Í: åތҌ«wön:¤šOœ*Ý ±S¹¡°~ÜŠàYæ«Ø!_oÅ)“ßò©ˆkK¹Ç”ßÈslJðìEXgdPˆ‡:ê¬Yo«­Xù¨m6ÛøöË€ûUºæ{:Aiå²ÇcV±ñzÝ Füøßs?‰`®ÿ…{8Çz¿`}ݾèñòÞ¶ Ê܃AÑÇýZeÀ³^:ÙÕ6ƒ`é‹Ñ×¥=léà4ÃÀ£¡%ʉ£ÜYÊ‘,žw/Á:6ݸ¤æÓ°,Jáx«(ÒVŽ=Ÿ]%n´”{zŸ åæÞ\¥`¢_,/FeÌ0ä€R®>q;(w–òÃÓöëƒwy¿`DQ“®Ï=Ï€q…e^/ò`{ÛN…Q/ÒÁÿÀ© [ Ž>£`©\|Sƒ™Œ8j|bKC¾-q;ø<¹NÂø-ä÷cl~l![Cðý0ŠÜ3ü]5é0ïü†FÓœ\Þ‚¥o{üÌ[6“Q.;ñîÈ÷”³©GÔ¶ÐÂ|Þò°‚o°Îج؄=Ï<¾~Íùû<˜~úxg÷uéÐnbì÷ FÃ×Ç$»Û+ß3Yëû¹ÇY‹ç¤‹…qí…!Ç­Ë`¿Ý‹íJæ2cO-œo›«޲(>Ÿ÷¬‰/ë™ë÷.—Xé¹;¸@`ãóêg0ʹ'Þ8åRÞ=q6ùÜ4!m_¾Ø¾ÚQ?³²IàÛÉ<{¢:z??>{[³tP^<´ô¾ 1ÆãÓø> VÇìÐÉ«3qÌ”Ýkï¶:fõ/œÊCãÇÕÚ¼°ÎÚ<¸#N9:KȃÌzËlî>HŸ¬ÇÒJg&Àìú“v'6Œd³¿.º£ò›nàó¼™–†:zŸàëµhPíXÎó$¸™ß1³óü4å.¥íLËï:y±mµŒï‡x|4ÿ£9þyÍÿ¾|}=6cX2}³¶þÖ°Ú'<šu}qaZŒTÆøœÉ~B.¶ƒ!W“~Ý6ð-‰{¥÷Ö‰uâ€?ÉÂÙG·kTôÛs˜Ÿ½¶©0—ȇ4ÚŸ/¶hßãÅꪩPÅJ¥ìAyÐc딓·¦‚õ¯Ižú …Ðø¾ÊOå#Ynø5•ø)Ä;¥üV÷OµØÑµÜ&q‰ç˜ëHf=pœ(Iåóíðï±jSëñÂT!s(Ô_¾óptR$ÃEMbz{œ ù©¿çBÑ:ðÕÀ¨üOv%9Àb¬³ª~MÏö»SaÉ¡ÂwGÛçÁÌ*ÏMǤ²ÚAÍ·Ú }\`B$+%]Ýþë Fë2Êåy*™†|UÊ/Î’` Ÿ*Ùz¾Mï¹£l%ò`j™°ÄeS…~r…Wý‡N|Ñ8’9½Ï¾,·ÿ7L¹±Ä¯¦õ-¿ŽéR‚Ï+Ç:ÚõA“†¦AdYSŸJy0°b[y+ãT¸%– ZÿÎ ®ßòmÚ4’Íð~¸6Él:£œËwÞß.vŸÔÓð¼¤ñ‹žÿÄÁÐûë¤lÉ»P?1 ²’7Ï.ÒAÛ‡^7°ÿ®Žå‚‘ÇÁç‡ÑµS®F² Ö'r·ZÊ?nuÊk% =—yþˆU‰|b5ÖÑcت§CН¼Ò¡‡:pnq6«[T $|ú|-¦ñà>œCý¢qo‰«Ãç[Æ5êš÷çœê°ŽÑ½øñýÒ!ªL^EË+:8þ½Fiãa)°rpƒIefKÀß°³Û½Hú–Ö)Ä‹ |JÃþ‡ŸÎ¿¿ü¼É(>_Ìç/¦ƒé£Ù>œÕÁ· —7zTK¸½øÄ-36E2>ox¦0t>ß¾‹ÌÿþO휹8ó:â9ý"¬Ó`cøB¿¯éðá„Î:;§fœO†7e’‡œ,Âeø/Ò0/ÿ^¡ýÈ+’°(bäÑÛŸÏþöw<â~@ý­÷ÖiýHT uÍ€Û¯Z°~»tê6hçÉp4»^y§R`rûîfOTQ†õ&?/Ïqék˜7sØõž— Í«ôþÁ:ê„ufë3`”­ƒòÖ8~uI†Ì¶+ì¬"ÇÁñ£Kòc=£YDÙ%öëf0â;P~3q.i^ïÃlòLõþÁ:¯Ýk±áA4žeó9s±ò†ÎôoV?øyÛÐù„®<´0šÍ8í·³w¯FëzÎñùÄìè'q—‰w¡÷ÖIîÔmæaлnj¾vºöY5yÿ( òš_Ÿ5~4M9ä`Û7šñÏ¡élGrNoÅâÞ¸c4¡ùÏÛê*p¾2Ö©õ¼õŽ/;p=¶úŒiÀxtnày8 ¼ÆÕIÈ3šuØ7šù×]ÿ´Ë¥F¼tâLP^-å»Ò¼Vï|ýšÜíù]ƒó_«_úÖÁúwf*/Àyȳíñ¦Ç{û>[ãh6nô¶Ãç›ÌdäsÊu¥q†üI¹â%æeòÅz|Ȉ³p쌣ô\üüC=ónBÒݘ;{„,×Þ5¦R´Àú§Iy·”ƒMã'åÈóãÏoaß åícgáþ«—Ó[è õÌæ?©akÙCùk=áýËOçGo‰bw&ؾ»ÿ·Ü]ÚO£çå{SÞvñÜ]1Ö©Ãmk4Í„K©6êè::ØØJ»h¯ø\Zoí¹Ov®#¿oÚÒЯÄëåsÞµþDqë„6(üè’ "ÅV¯ò:°5î26e¤ž|}üy§ íPÔ¥e‡(F|JZoðë––†çÏÙºnGã}~zß`N¦ƒofÂÞç-Šv>Î…*kº*Wg°}±¢i'|à0÷xíÅxË 9Ë´/GyõôþÑ8p&T—°b??TbÙ¸t§*]ÎÁ×3Aoî¥æÂᬢ3 #ÜyºãLÛçÞ0¡pÒñÞ8Þè÷7¦1ž7Øh<¦ç ñlhŸ³8gMuœô ‹s/;Úx,?tòÏ*ÎìXá]ê}·»Åv‡sAêSqN‰çMû¶Äõ¡u)?®ñý¦Ã: #_{ue/;hV.œtécŽu<•^yߪêähÖ£ÊÐ%2…}ŸÇ5¬ˆöQiß–æsÅ÷iŒTùâ›®Ž=ǯŸç‚‡Eˆ> õÆïØ›m%Uy·¤ [Íšì^uv·#î ñai¼¡y"͇‹ç"‹°Îâ­UÛÜ~}æê£saß›ÍÌÌNCB3ÏØ;}Üaɳĕ"V3û¶z4;*cüûa #_9<ŠkC^1íoñ¹Ùö~‚Þ?XçÓó§êMÏð6ƒs§˜äBÓÛµUâ!~¢—|Ëù1°PÀ‹f×üOiÇíšÎˆ ÆÏËú€ÔÉÖÜÿâKþÈŽ¾ö5ätëýƒu¶¶©÷Íþ¼³*¿úÖ¾‰8¿/š¡ö 9 2 s²£ØËÈ ®o̱Ž0Ï¥}bÊA§};â¼Bﬓ³~⃠óÐôºS«kîBòª´HÏ[§à»*¶ÿpÛÑPÆíÁõj#…ñSÆN,ãÀ]¡þÇÄ{»û÷4ì§Ð¾7í?òëlaÞ†u*uÅ)ÎyàhÆu½ï–ˆ©Ãófž‚¥#£¿é<¶G©¦µŒdžoo÷ÕDÆøù@/!ŸÜÞÀk¢}É⟋_?ñWEµyÝ.€W nd¹ '$¶¦™œ‚ŽY¹!½ËŒƒ”ª^—rE2ž["3äî‹Îh?€ÎŠ?tX§áýeË·\€ÖÆåÌ,{Úu\rVŸ„îrV†ÏÜkÇ¥é"ÏS‘1âlð܃N†:Ä—(Á‹=˜/.ÛQ{´ƒòôÒ î@$›Ü kí“å7A«v΋b^-Ë첓1zNê}€ÿ¾¥ãÔ9Õ´À¤—•¼pëÐ,õyüqé {¬ïüÌÞZV<1 û†ç”Oö­;ö¿©oè}è»+ÿŽ›‹uIÖqÙyñgå,0Š\ø¹§ì¼ªÁHÀÎëäïC^0ºîñ‹Fw£Xë]¶ÍóSý¿Ó:øo/•X¶“gNõh½FÏ3½°N×gR{ºfÁêw–>î}TצÌsI€[ÃÊϵñ†àÕúƒ$f‘ój‚ÈDfØï£ù"ý4Žß‹óFåXç»ø„_îö,8ÄmŒÜ —Ìöoßs~鿲BǵÞðdŽÇ”c¯£Øøà‚ë®Èí[Ò¼Ö—ô÷ôj²5-Å@½°Ît#î ’ím×?9ûå6Œúµ ía“ãÀ¯£½`œQýØAߣØðÙŸìÝwÈXÍ Z³5êm8/£Ï›xIü>ŠM‰}_5ÖáhÏuûgƒßÃæß£ÙmxÔDœÿ1H€ºUê­ò„!—¾tÜïÍÌ[möÜašÀ·üKFóHÚP>«ÞЛ6%αtXgGÝ£Ÿ]åÙÐñ˜éeÈm795gnîQ(ŸÚ5\dæó¬ƒñfúéóeáóퟚ¸LOš†mùnW’7~(_¼nô¾öeÃË€ê¹KG߆ª#~éttìQØ?Õ§Î@=gC÷!£.r;=샾X9ÿMƒøÜÄ¡ y+ù»8B„uô»p÷³!ô^D÷ínûA©cósÀÔ—ñUoM/<Ç¢˜"tø£G ¦3Þ¿í ûúÄ3#.$—zßàëç-T›h£Alÿ~ vO=\ù¼ÏˆJÛÑùõxaáx·ÇêHV«Å _ŸÏ2FûžÄŸ¦ç.»<§D˜wáë»wŠˆ|V] ÏÖÖºp ní²{:ëÑapdP=Dåç×ìÐ)’ñý)3Ì»©èý¡}šçÊ±Ž‘_õ´ÐWl¹#߆ùnwì>ß:0¸‚'ÄM[wÙ§c$;wݧȹ»ŒÑº‹žwt^EýÅs<úó%Þ—J¬3eýœÛšjArÖµíÂá· ¾ºùëÖé‡à˜zôÀ_½`ˆÜãã¿HömCÝ–gnNgĦõqsiÝJ|Þâ<5Ö9Þzé³Åmµ à“·À9o̱f‡ ÃÛvf¥NzƒM=óæ²"…óÑŒöqi_™ú‹Î1λ|-\Ò øç¦ÀÇ:ÚÞ¥Öv×ÂêÓ±;¼¿ ã6æÛ½üå ¬¼Õhƈf>P:at¢›Yãžîž­ ¼iÚg"n2q6è÷(±Þ?œ/¨%ªè;X ·:Î5;é&T¬h9·þUøŠËïrNô†1[?̨ˆóüI^³ Ó˜w!ìSÑ=ÚWø#¾­ëLi9°w?-p»Y7ýoBãÎýFuVÁ”ë!GÒ•øXî\Êjs$k_ÚkqôFçlü8l ´þ¢ùÔÖÜL²}‰ùªëd5h¹dî*-D÷äˆÌ7¡|Àzç‰Ö`n÷Sc¢kzBÝj/{¨Ì#™oXQX;ÛÉ~+/Šögùû6'TxÞ`™úA =¸í#í èWiÖ×–Íâ!¢:U¹KàÆøJó¦UŽdüwL<2…ñ¢^ž¿ž(4ðÇø:ƒJÜ3& öTÒ—»m'¾\q¬õ~¶}ïÌ9î ³º(*ëɦm :õ캟°neø{è9Cçs<¿©O‰},%Ö©1±ß•¾[´Ð!ÜÑéTÌ ˜Ý¯§“jÀ>¨,þÖnÜÁ1O.’©Ýß?2Çß°Næ¹7] ó.ÚG§ûÅûMu&}mWªh—^-¼½ð£÷ Øfqèùè_öÂ…ŠÊ5bÝ ó·éKŒ#ÙÒ.©MBý ü~ß´§C<ÎÕïËMˆÜU¸×Ãÿ=:¬“òfØÁŽ´0gP€:¾Å pÍŽþ¥[âè50sQûÐQ0õ—Ÿó')˜O ±B¦2þ9ßÕpþGûô“ÖÑÅß7£#¸»Ý:<8^ Õõ@X4}âܶÔX®©ÔwÏâQ¨âë«Ø×Sv3®íš*œ´5œ“ÐzŸ~笉ðõ¿Û7ë±_ 1If‘–Gr`˜ÇÔÆûÝpõÉÞ½¥ºÁÒÀ§_nù¬b/lÇÜnåLjgE÷bhNç14.~î'Æ:eâÛø†ׂåÉö_t¿ä@—Û·ÂÍŒž£;L&%X¿Šñó䩌øOÄÙá׫ç û/ü½–:¼_ðõÏoH9z3E í3§*8äÀµxï&FÑ»àì’ZN¸ƒu~ÓØ"œµ2Œ3Äë¦}‹£2n…н—NŽuÜV}ù4U •Lö‡u¨™»çÇ®Û Æ£ xá*O’z NÞXÅüïZ·:?ªáÜŠ_GþÆ##Žeñý%¾~Þ«çî‹ÏhÁ•Ã+뮃ů¾_—ÆBˆ3m”p¦ÎÛ»f¥lOÿú·¯Õñgü|Ì\¸/eüz.ÝŽÞ/êþ~ ž¥Æ:sÅ7V·IÔ‚ÅÛ©#Ä_‡h›Œ[±ÒÀWž0LRjp=k;ó¡»ô€?£õ¿Ž¬gàŽóãf¦qÉùýra_ë0ïûIuNiáíòoëλ7«%?¯o·&·\Ý+ÓR|jsèW¦Ç¶&Ne´ŸOümEá{Q™ë ÇC¾RÂ+Ï-FË<`ËØ‚eö¬bù+£“3&1ÚG¢y?~]´£çÓ_߉±ÎÂÝ[º×Þ­õÓÎd^ö{¶@Ón‡_ê+nl\ÅôÃwŸIŒÆI¾¿m ëš»V â-öxgt^«÷ Ö9ø9"ï ŽË‹Bž%®¼-üö¼ˆ¼° †‰•]>ºG¸á“SÁšäÆ^i~_ÊøùÙ ðáŽKê€Ý­üƒwWúj¸÷ãÛ^6w_½à\ð®å«<_[ŽuªwëuzدZâÙF:æHÖ_V?Ù®µ:Öj<”¾<éN¹£«Xç‡Ñl”˜q"É%¬Ðs‹Ÿÿ}²›¹(òõ‹¦¯÷œáïe)±N…ÚæãM#µ€c¨Ì½ã5>&hÑ·u†så…3ŸdNœÁj™Ø¤í±—2:¿ ù3Í3h?žÎ‹ÏŸÔX§vƒ7=ï)´Ðoàá¹æå®AA“5‚Ÿ¬…÷¹«Ã<<à׋šüÚÎ+YÓІé;ØDÃ} :Ïš2äDÃYÙWíègߥ¿öÚük‡÷2tXgðîA飴p¯©çÀf¯BAÁ®ò­k ìÉFW×F^ Ÿ–žXÁÞ<’í¯šÈèùNû­üßsÁÀ%¤y ñ·õþ9–/æN]‚ñ}+{+¼å©uXgðÍwšFÃ=¿—ÞÁjœwΨV!÷ór–ZŽÒI¿ßÞÜpŽDçsüü‰îgX ë7¾Žëlõ˜<1F ª4ÿ-®¾Wáã³ÌÀØÙ Ø<¨¥æL}_àçgËYàÅQ ß¶“ ûؼ?[Þ7ßü\kÜ(?º[‰û9b¬³q'7ÒÂÅ–•ãº_…f‰1I3.¥ÚæãÛ_H¾›ªØ8xãûu#þqZi|£ñ‡æ¿%îÍ`ž5ªžNÓÂÅ‚ªž+^… Û¿YœZlê«Ü€½¾ÀóRW°*uOìÚkãËJ…ŸšW»^C™î¤ØÑûÇŸm ã¬Þ?XçmêŒcrœ¬ôs(ßÌ#÷ ì.‹}^F.ÜÏò•븋€+˜b ×±^Œ¸¹ÝïZxXîÍÐçCûÜÅï)±Îï†ñ—´0ÿÝ+íœãW`d“õË6Ç΂•ÎÇ·-~àÃ^îÞø~õ ¶P36~áTOF¿/ÝC¦ýeþ9tÍŽÆ›â÷sÔLj‡§…&C¹Ñ+pû½í“£¤ð,d­îJyOènÔÀ´qü ö`sü ·©^Œ8æ|wšŸSï|]ý6P–vö­qz…çàçEƒa{¨|eW? ómÓáã–ìTábü |¿ÿÝx~bÃ> ÷Ð< 8?Ûèx¾¸îµ•sú§ka{™¯Ku¼g­Ÿ]9”ô›phåûÄ»§&²=S¶®ÚïÁ³Ãj7[Æ4VAKÎû0Ú§ù&ÿÒù,¿_ݳÄù…ëÌ9ù;L /Ê|>0ãòe¨¥Ýéå0ƒÕ®åòºËw8öîrе/á,IïšîÅèÞ-qºéó¦÷/¶6"´øµ­x¿`=^w›æÕ}™ñ|íe°th’Qæ ëà;hd¿p¶<샹[„„'”ž“ô¼¡~ üUÔT™lo8Ôûë ¯ÀÍx´Àó;/AcÛ~U«w_ËnrO:ˆ(Ý7iE«pö­eЇˆú^L}pb¶ÏäN†ójohü¤}0â%ëýƒu¾dôžib®…ã—¦>XãŠo`têÁ:6åâ®O=kû@èÈ–-_„3‹m¦fy Ïn@ó êk:/£s»âç³r¬£ÇˆbÊ£ï<éñø"Tùås«¾³72Íò_2_xÁEÛ§õÛ•_Æö«W÷oþm< õ!í?R?к­øsZ‰uôØÏ†ZxÚ æ¶å¡‹ Ú×¥UÙ;›Ù·=~WFH<Á±G¨_&Ükõ`toš_ÿµ:ǤŸt¯ŽÖ[zÿ`¯ïgé6VÇñ 1_ròKµ}ý4LÉ6í¿`šóH3p6½å^¸°5Ñý :ÿ£~£ï¡ðûKÞ£Á:Çþzà\a6´ÝT-»†÷E8=èMŽ.b+Ûn–V7ÊZ÷zmTÏÆ{ï~·p»»'Kürº?Kã=ßøu*¿Þ5:‘/ˆÝ\vkb6DžÜ´à¡ýEX¾pa+§ÕÛX¤·ÜJ Öã…C솲J)—e4nÑøFçÙ´žâÇ7aƒ¯ß«æúf¿®Î†…î¸ÆEPÛfoé¶g;S÷Ù7:¹«'8µûtyH(sÝ>ÊxuèöG¹ëŽ ÷Ïè}¢Ÿüýüž%Ïo°Îí+â7=æàûÕ>"Óé ®«m:**ßÝÁFgi0s¸7 ^©Í¾ò4”µ}×Öäá‚qŒÎShüBýÆï·÷,é¬Ã¸åÓ¨l°YydÄÝ-lÞÞ·Td—ÌÝøØÝL?_àN%Ú eÄ7æÇÅî†sÚ¤s3ž³Û«$×ë„\oïgÝ6l=––_í¤…ÅGç߉ÛÅœ¥ÛCöÜ›ìÎ?¶)”µ;2c}Ù›î†ûÀtžLß§¡{:MmMJ}ì 4žë}ƒu¸‡~Ÿiœ-ÜÛÔ‚«¤Fàô¶»¿?-…»{žº8 eQc,×m¼9šÝ,cö+ä:ÀÌç• –ØôZßÐ9ðHÏÍkŽÜTâ~“ë|©kRÚ» ”:Ï~Uvfƒýµá{Äïaoë¹Ls¸0Î8»n v eÓ¯Ò9dŽÖ!£ÀëÕ†äe]|¶{3i²Ä÷ïíx®ùèÈaœëóßÛÑa=9' ^7{¼þl÷l8ì¤[áÒd[èX¡’õ_Ðg§„ ã¶+ãÏFÀ=x|, ŸûÙÌì›ÝÛréö°âË€¡P-!>¦:¶…Þ7'óÅë6«R3³ ®›uõ¯7³àè”|eáÂý,<¡ÇšÊF>ð­M§öaìn#î‹\®Œ_ÿÙÉzþêü0{Øeù:h[•§v÷;Õ<Ø.î­?ž÷¨sÜ];Þ?XÇ-BÜëT̵Iž”´< rŽ|2=)ž•›íÛd‚ÔßV1(4Œ¸ÞÂùJ/ ýHºwÆ?ÚQŸ¿&Æ:ËÊ ¿°/6 >õãnXfAöv’¹O°Á®]7®,ô€ñ›¼Äïç…±&þCš‹]…õÝ og˜‡’è<ºø½J Öq >ŸµmNh+rO„ PÏþLnŠ\ź%÷Ÿt몾 Zþ©Y;9 ré6a#™:ºNû½ªV½I‘a‡ö;iT|þ!Ç:2ý_ô7ç¾Ér"+pö™•{;óýÕ$ðd~•¤éõÂØö‡âjGýÜïÓzÔ°¾î»ÓyUq·ë”õ¿saxÃ,à¿7sÊl± ,>ÈZQ¤;L€Gº61+Æœ×õ|×ßÙÑqL×> ‰C#l Në.½°ÎÅ2ËÜRU S|‚uëî ÃµÛ³Œ±ð/ßÝñš`ð©i³—æ™.®Ì[uoÄ {Ît†ætÆûJø~ ÖQ77ú¾ºõ°ÝÔf`…¤óP¾FæÁ½A‡Øw_³vï$éïûv{^ü ã¾Þ‡ ûÄ4^óýlkø¦Þ?§òůâêTO;_bû-ˆ²9M¥ç›=){˜=3ºÿ§ÑÀ²6Ë çÃÝwåïuö~Û¦ïÕÑsCï¬S.ù-= ÍŸ9U>{ >­»úëšÃ,íÊàýû#<¡ÊÔ5’éÖ‹™þšâÛŒîµÒº–æŸÔü~Tç÷ÃÄXgÞÒƒƒYßópýŒ||ÂŒsÂ÷5ްöׂO_Ž÷î›…°äïÞìlâÊh~IójÚ/¦ýc~>ÕBø÷ø} ÖÑ&ÈDÍ*áçÒô}¥Ù9ˆhÄŽ?Î>Â\bUž¯ð†5­¸‹¤KØ£v·îâÂè¾$Ý §¿ã}ƒ¯0ÜøBþís`6Ö$À%9t¯¿Îe³²Ã}{°º>Àí>š.ö#û3šGSÐ|ƒêн1šÏé}ƒuVÕ‹[´å(Ô ¿ÍÏ„×'üc;‰Ž±´1×S^÷ö)^Ü >„Ý^{R¾v£ïк—ö¡è~Ý«áÿ{þù¦Æ:×~k¾{ð9°Þœ-Ÿ×9¶“Ž‹>{ŒUÌj½ “©t=Ô¦]Ó%ÂýÃ|šîmÑçOŸ ýÿiÞ¨÷ Ö±®e7¶|ùsТ^Jóož{¿»w¦gꓺd™·7”«Q Òo/aü÷j‡3¾›ÆaêcZ_ñ÷Y:”8×5JÌw¹ý±n&<}8gýùÀ³ð©Ýšwª&°…ýü.&´ð‚œJe§–oʶÝvaºÿpFóJÚŸùýþ4ÏÅ÷‰DXg«/÷Í”L˜Öæt·mÎBëFu¯6Ù–ÀfYOˆ,êªéÜŽH(ã÷+Óð÷]jÆiZïc1¾~D~gåÝ™°d¾]°ùªl?ÌòK?"¯oRSM'z8Û|Y"|Ÿj°àK‘áå÷ûP39bíÓøçÿwH°NaÑûxû ™ð¹|dåÇ4°sþ¦+kcO°«ññêއÊQvû·A+ëÜxÔ“Ñ.Œ_×t0ÜO¡q™îSÒ=.º×³Åˆÿçÿ[Éÿ—,¦ÿï›FÂß÷O×Ä6$Ƶ5î×J¡‰ÿ.·$eŠÍøŒk3lz9ªåò7™é”­I\/â»þƵ•ÅŸ3®ÿ*3˜6Ä6”þÛPü7™é”­éòYLf#U`õ¯<›?ÊKçx6nůÛë?›EÇå › Ñe¨” W,ãäçØ÷sì“ýûÆ>á÷ÿ§\Wây×ÕUõƒ\×8!³I†ÊA‰±™ãP¦ØÐ(Ê[…2Ãæ–ÿ×U„MŽ*D¹ýM®0eЇ˜†Eÿ€ëjmñç\׿Ê&^ñ¼dÁórü›\aÊ¡sû ¾µÍŽ*´úWVÄe s¬I1®aÌ!Û‰Ëá4E£¢t(G4¼ eЦ„ßž½?Ç>~ìû9îýÏ{\ß(þ9Ë•Ø_Är Dé~åš#ä©ÿ]ö¦刿 êX®.Øð (6}øßä k~Dz– 4õœã*µøcŽkÎßä K‚x`Ê¿àý]ÎpB1޵åˆFT¡ÌÐŒrウL@‰~Ç“ø³¬a.ËNÁå wåÖÿ• uŽ#FƒÇ¡LÑä(]±ÌÍŸó½Ÿó=¹Ñ¿oÜ3~?Ñ?ã·Œø­.ب ?ÈoU¡L±‰Q:”#6³ e† -G \°±P"lîðà·ZbÓ+PE(Éßd+Q&Å:ÄAäÌñ£üV±ÅŸó[ÿ*s˜8Ä ü >Ç‘ø«ÜaªHàéüÚãéXrùê¨"«eHüQæ0g\i×ßøˆJ” šX†ÊA‰ÑÌq(S4t J‡rDc«Pfhn9ªå"dyš¡Ñ娔 >ÅdËÎ÷~Î÷Œþ}ãžgôÏ8¯ÅyaÄz•£ ~€õÊ1ÃtBÖzÊ›8¥C9b3«PfØÐrTÊ;ḯnØðj”%6½â/²‰¥(íï˜ÕR”¶þ³_eÌ~ÕýMN1q%ˆ÷ü°¿Ê*vCó©‹ñª P.hÄ”Í.psÜДj”eÇ’l‰?Ë.¶BÃÆ¦•þ3Ö9fŽ#\…2C“ËQ(G4»êç|ïç|Ïèß;îY õÿ)–¸aăuÃFUÿ 6e†M,G \°™P"lèpT!Ê [²ÄæVüÖ ›>Fh|)J‹²F(Q&h*%F3Ä¡LëýÆÔ!f"ǽþQ¬£ÅŸó`Eh¤pT!Ê ¥FY¢©ÅÄ “ÿ7Ì M§FY¢ñ¨"” ¨AY¡ c#Jÿ„­AYqyí‚AϘ0A³ÊP9(1š6N`LÈŠ±ãP¦hâ@”åˆfV¡ÌÐÐrTÊ€¡¹ÃQ…(74yJ„FG¢ÜÐðjwÓ/øÞäþù¯Ž}?:îýo︱îÿâ÷e|£±ûœTFÿŒ[œÆ1`ñ €눩B™asÊQ(lÒ”5UˆrûAþ«¥AYaCÇM-EiQÖØÜJ” 6¸ •SŒm-«ÿ÷Ü×ýhñÇì×” š#%Bƒ„£ Qnhu1ÎÄpÁÂQ…(74“e‰†R ŠP4–FàY¢ÜÐ`j”%šL!0r$ø!jökqî„eæS¢LЀ2TʨD™ë”M‡2Ec¢t(G4¨ e†&•£ P.hÖ” .ðq\и (š7å‚Nø9oû9o3ú÷ÎÛ¬…×ÿ§¼WbƒïU‚Òü ïUa‡£ QnØÌj”%6´U„’`ckPVØÜ1?À{µÆ¦W¢L°ñe¨” ‡2E¢t(G4ƒJ`]S‡XˆëúGy¯.Î{µD#)PE( Jƒ²BSÅ ŠP’bl°ð¿`ƒIÐt”/F0Ÿ¥EY£ • Qö'Ük-ÊÍ©D™ Ae¨”‡2E³¢t(G4­ e*0¯uoG…2CËQ(l˜” Ž*D¹¡±Õ(K4·U„’ ÉÕ(K4ºUÄqÑðw³X|?rÿügǾŸãÞ?÷¸1Obô¿g¼ûïëþnœãÞëÊz%^ÇyµÄfTüÎkÊ›5%† G¢Ü°qÕ(KÛ¨"”ä9¯R”eM®D™`£ËP9(16|Ê”c¼¢tÅxÖ¦Ü\•ƒ²üÞ«Zà½Ê-þ˜÷ZˆrCÓ¨Q–hª%AiP–;ìϸa–h*ª%AsiPVh°ÁdR”VàXÇ ŠP4e…Æ‹Ì'EiQÖhB%Ê(Cå ÄhÈ8”)š2%FcÆ¡L~µåˆ&U¡ÌШrTÊ ›€¡iÃQ…(74¯e‰.D¹¡‰Õ(K4²UˆrCC«Îç~ÎçŒþ½ó9Gáßÿ§¬×”¸ëUŠÒþ ëUƒ²Ä&V Š8¾56³e… #4µ¥EYcs+€õ*ƦC™bã¢t(G4€ e†&£ P.h†” Ž*,ÆHäØÖ?Êzu³øsÖ«)F0“¥EY£©”‚±¤( ÊJ`½¡$h4 Ê Í#NŠÒ¢¬ÑxJ” šO†ÊA‰Ñ„q(S4bàŸ°­sPb4gÊ ˆÒ¡Ѩ*”šUŽ*@¹ iPfÅø‰.hà”MŽ*D¹¡™Õ(K4´U„’ ±5(+4wŒ`p)Jƒ²B£Çf—¢8 —´óß0ŸûÏŒ}ÿÝãÞÿÔyëf¬û9Îý6Îqï›ÚèŸñ_•(“büW+lȘ俢ܰYÕ(KlXª%ÁÆÕ ¬°yc„–þûU†ƒcsÇ¡L±ÁQ:”#6º eÆq_Qf¿q¬Í°ñå(]ý¿ç¿j¸s‹?æ¿¡$h Ê Í#FвBÓ(PEÿU²D)PE( Iƒ²B3ņ’¢´(k4–eÂñ_Q9¿Z)MŠÒ¢¬ÑpJ” šN†ÊA‰q,‹C™¢ Q:”#šQ…2CCÊQ:”#S…2ØÕ(4iJ„F G¢Üаj”%šV*BIм”X*BIÐÈ”š9U„’ ©5?çs?çsFÿÞqÎMøï¹”¢´(klD%Ê›Q†ÊA‰±)ãP¦Ø˜(Ê4eŠM*Cå Äجq(SlØ@”别B™aóÊQZ”6qŒÐÈR”e ­D™`SËP9(16wÊ<¥C9b£«PfØìrTÊ›^…2ÃÆ—£ P.h€”MŽ*D¹¡Ô(K4„U„rCc¨æµ¥AY¡Ib£HQZ”5F)˜F‚Ò ¬Dø¿Cq’¢´(k4’e‚f’¡rPb4UÊ%CiQÖh°ÁdR”efS¢LÐp2TJŒÆ‹C™¢ùQ:”#šP…2C#Êÿ„ƒ­C9¢9U(34¨U€rA£& DhÖpT!Ê M«F‰Ð¸á¨B”X²D+PE( šYƒ²BCǦ–¢´(k4·e‚—¡´(k4ºe‚f—¡rPÖhz%ð½(´ÞÿÙùÜÿÔºÿmãÝÿűŽû»5Üç† #4¥EYcó)Q&Ø€2TJŒ‡2Åf”¡rPÖØ”J” 6¦ •ƒcƒÆ¡L±IQ:”#6kJ‚ «AYaÓÆ+EiQÖØÀJ” 6± •ƒc3Ç¡L±¡Q:”#6¶ eŠÍˆÒ¡±ÉU(3lt9ªå‚ Ÿ€aÓ‡£ QnØüj”ˆ›Ï¡ ¸ïÇ¢Џ±Ç7 Ê M#CвDs(PEÜ8'Âÿ Ê #˜EŠÒ¢¬Ñ4J” G†Ò¢¬Ñ@1‚‰$( Ê Í#JŠÒ¢¬ÑXJ” šK†ÊA‰Ñdq(S4Z J‡rDÙ ád¨”‡2Eó¢t(G4¡ e†F”£ P.hÈ”MŽ*@¹ 9P"4h8ªå†FU£,Ѭ TJ‚¦Õ ¬Ð¸1‚y¥(-ÊM#YŠÒ¢¬ÑÐJÁÔR”øÿ›¾›Ã'6èÿÙ!ügyb¾xXp´Ÿ®j¦;©˜œóré,{•yâĦ&'åNÔÕMN0pñÜýÒ¾š¿ˆñy8b©ºwlÖr8+ÁÀ:<ïø,¨"î‡ lªœq7koÙu’u¿¸©NÀXøžõ¾ Ê‡`6¿V|ÿ¼y#åk÷ò œQkËó­ù¼:5Ö1~1÷Û‘¡gχʋ^g+WìqŠ­Ø’~úc/ÐÇ×íZÄVޝâRòï›xˆ”‡E|‰]Mj§×YÔ«D.žëØhž&í(ÒÀñg¦ßU±à“á ý öÜz•·Ï¿˜?—¤Ú•Á6/xh?oÙ` aÊ“¤$>ß¡—!ß\ÿÎó¹Tè³Çr¼_ô]p[”Èj:_|V{ºÎ7-ýºyˆÀ'&älôÝ·û¶»°_làKóù |i>gXÈÄ:³¿=?4aŽîfÖØäS'j,åÕæ4;f3fyÙ™>ÐêȾϽCØ—/tG b”ƒÌ÷ ~î.E‹Ï=¶£ŸÜëŠñu‡Þ÷ÅÏY’}Ó$ÛÓ¡ÎA»+S¯œf+ÏkÒNoö†×FÔYâ"äàÙ³›³¦õó\4Ž/Xõéim!ï"_ÿz|½öΣæ´Õ@éIJ\‚ l²_¹Âbævzú°˜Ö^ ²ð\¼$:DàáØ 9ŽÃ€çR8x¨ÄŨœ_ßWÚËÞÀaÖûëÌÖ¢ë‘Rø:à—e>§ÃÄ·ßÖ—glrV¥¤S;=À&¦>¾"ð„‘ëpïÒ €t8kë~ç`N*,ñ m|á°šÍ1š§ÝÖÎôØö†!Œço 1ä or³ô}¯ß~W™Ôfé:£Jç.›S¡oÕ±©I/Õ¬Æ}…ÔýÛxpn’ÌLê/&îsš,5V•²rí ù¶”gIü¦â\K ÖÙ—^ãZzNä Úhå‘ òüÇô´Jb«&Ŀɔ€Oní¤ï#1aœdÄË%NåQ~qî(/ZﬣÇ?)Ò`‡5O…G+­+ œÄŽQZßêî c®´<‘Òb¸åŒÏÇl'䜷1äïPn·®ÍÉwëu)‘›¨Ä:<—# Ž;”¹¯»”Ãs>÷yœ”ÄÎíj" ZêíKí±t+«,WåkÆ¥©MŠ 9ƒþ…Àç[–ȃVcÚ¥†¼¼÷5>9?¯ãêÐp/ ±ôÅèEVÄÜ—ŠßUk2øÔûë\|æÆ£ôdØ&I¯µoz2ð\·T¶'ÔÍí\‘'L^ºæ¢& «Ü³ñLmóÁŒòy©å‰Q¾1å½QÞºÞ7X§Êô}ËoI“ÁZÛ&Ç!nÝøâÝqd*{Ý#ÉqîÀ7»vª8Œ}ªR³Ê°Â¾Þ ½”ÓK¹¶¡é5í7†uʹÒûëìé×·ã\ãdÈZ^sÂsËd¨`ÜzΧ©,§s®y…UXçDò©q©a”?nàwç˜úŒrO­ýüÀRÜÅ0Nè}ƒu${Ë,OY—OVþþ¸\2¸Fª¯ÞOeß{°'Ôð¶Ó²‡¡ÿnˆ!'‹Ï1îhàÝм‘x”Û¨7:_\­_°]dË$ø°oÊó‚$š=»`O‡46ÈwoðÆG^ÐØfš×á¦K.€+£ß³qéª[^ho˜SŽ.åØñã¨_†uê.”ÙÍÚ§†æQ>µÎ%ÁSãfï>.Ncý*¨éä '7œ,\¼˜ÝѮ޴Ðe£q‹Ø÷Yþ7W\v1pˆ×F¿?ù†žGŹ J¬órúÎ ·œÏ€®µ³èY­$˜±ðIU³ f¡èY(j; ª]ŽŸäb¶Î‹¼1ä>ç“ú˜Æµ +Z§$Ørõm†užm]°Owü4„u|ü2¶P C6WÑxnk3ê²Ë·aÛük]ÇT 8GƒQgÃ|‹æy”cÉsä¡D´ët¹ÎÆåNÃú¦mXåt5LÛYÿábãç½>°eÖÊ_mCŸ›íÌJí¬l®½Ôˆ£GÏ3êgâã‡G|ñ´EÓÖÛÙ$‚ìÙëÖ«ôÔM4lnJ“6Þý@?Ý­"p‚†3ÊÝ#®,ñu(_¸¶ü|½ï¬ÓUî­·OA€_œYþ45T•u·žŸ†~ø$£ÅGÐã;„°}¦glø2RàÇ5â,Ñ<€úž›<ÿ¯#Æ:·›*:l= •ŽÿÒ¬¹­b?¬<`{XÃ|*®Ø+ïèQk»T aÍŸÝзhãsQ›ÆGzPŽ!ñ—Š÷µë°—¹‰Ö¯NÀ€Pó±Ù¦jáù¯a v§¶ù.ûßš^Þzy1ãs¥Ç°ä3›ú†u4ä•RÎ åCÓxïçjòþÁ:{›7|(év(}ï>ƒÍÇ+G—éu–ñ<hµ¦0d|Ð"öhÔ ¹$b<£ÜgâwþžAó’ây÷J¬ó°ÅäIþK€sëžC n^é‘·è,#Náw“yóQÁìv§“í<˜j¦]¯A {®'ÍŸéyQ<çZujݯ×åíqÞ[µgüB¬£N~¡;–}>{ÚçÍO8Z¯¾s™`V1xÜ=±+ˆõôß0ÈMÏ*¼žšGSN<å_óëNÁ?Xg§CÁÄ'³Žƒc÷ämM†0èfùNݤ~&›\ÃCZÿ„pžõ³ƒÙå!–ëãnXü>WŸçÓ]µãç‹…<|!739_¬*Ã-ÌŽA³òk#z08».ø?Ø;ó¨&·ýãN©µX•Æ¥­.(âVÍD\ˆJª(¶.aBÅ= `DTÜ*îqiK—j\`‚ "\ˆ¢%uE­ŠZëïJžgÒà¯ß~[íûöýCÏùœœÓC3ç¾î™¹gr_öûBshÃU7%`_|ïÐôÍ­[c=õìš°eÎH³_ ëŸÊúf2Ÿ2.Tõb$5DØA²üÄ+úR²îUìä’ôj}3ºYÞÒñäëàYãF½¼¶õ:Re¿'ókãÞÿß×6KÄÖÁ\ßu®o»ø8ósû‰ôÍ=2»C?J†ÛÅÚåR™ÒDF½ÐÖzâO=¦Þ\•áK™ïóOaýfY¿cKD)ÞßáYφµ^ Ýl=©‡çÒ¢x^â¤\j¿|XÏÌsAdýÀµ;fÆÓ¶†§ËNÊÆPæ?µÚvÊÀóÓ»˜ûf²<ÍöÉUö9gý#ÃßáHöØ¿É÷¤“æm´v'ÏäÒ);/È7®"ó¬ôºýåq4îú©”£ßIiUŸSwÂê,,_³z‹e_c%ÆydÜNmÚOjy?«& O'[§í¹¿ßþ ­_)³û(}<¹ô…›n­ Ž(ú5pô÷3ûª³ù…ÕEX\ß¿í8UìN8}q>׌ó*C3åÁµ}$óãüm‹}ÒÉ!¡Õ•^Qghój®·´ÑdÇÚ…ôÞqô³Ü£·«=–²õ;aû*62?–§-çQ=ÆY³_t3¥Ã>²ùü鯉=Ó‰ë´ÈüI§ÏÐÍj%Ä5 ±_×mþCt—Vû40÷Ñ´Q”õÉf>>l_Àæ9ÖgÛ2ß1Žáò½¨ÄTròêÂN—n¤‘·ï î›GÓ’ü^J:þvÐA6ŸŽŸØ¶|aÚH³óSañÖiªâ¨ÇR½ˆùÉXú‹1NÒ`ŸK×›ì!£ì<õ>—FR»´kô|MõùöÕÐQ*)Ñ>ê¥î9a>î#~—7š²üÌêÌw“õÑæú»æSeÒÆYï²Ð+ìGR#µZýë9iD³3±Ñ¥«y´Cð mgù‘wþ¨7Þïk4œ 0û±>Él`óßòsS`œ"×& óv’uÃN[yH#yy˾¹ïOo:?–ÿä@B’ã¶%·ŸK‡Žú²¸Á˜`ÊúÚ²¼Âú¨3ÿMÖÿÚÒS‰q®Î¬»Èai$ëU?+ü<¢qñ‘ù4u˜·_}Ùx2ÀØ6Þ_G¦\ÝÍm³µ[íõbsz.¯•™ûu[Öy5ç«Ç>öU‘WÎ<2ÓȺ±‚ê¢|š¸²CÜ×ÁÁD²}ŽÃ//âiŒiHY=‘ùú°¾úì•[w¸“ˆ~Æïï„qžoî=V2 ï¾ÈúdIj”ìýÜ¥€îªlÿÁ“Õ2R0¤¶ÇéñÔ:o÷ÌöŸ˜ý~¹>Ã}y>í3`ÄçÒŸy¿þUêuVYåâû[£¶ÿøÛ¬/Ö¥í^¼²€Yä·}Æáü0â)÷¾”­3‡W÷ÿ¤cY’¾lM‡­û~÷yU¾œtÔ˜×u&ý`œ:x§+çn%:…õ¤›wÓÈ’&aýŸÐh«g×Nàs ³ œn—OR8}-€²}5›—Yxæ÷Ç't«ÒZŒqì/eŸ¼™<ZÛôlƒtb·¡WaG-½S!>žpûØxúJæ6þÛt?Êöm¬Éê*l^`>R–uH)Æ1•±“•D,ë=kµc:™Ÿ3íƒÞÞZz4á{×eGÈâZsû¬OÝÇE½œR(¥ì<…Ë£Îæ}<Ë \g±ÙwÚ¤Œ3ÕdT¸ž °ÒïòGéäô˜Òë½fhég'®:Oö'õ8¸¡e³xêx_¾¼ts€Ù‡“Õk˜Ï [‡pu§*õ%ÆY&]mnÔÒðóÁ·|¯¦‘5î,w]«¥öG>Ó&$ø‘ŒQÏ×·‰§œ_“Œ²ºÛ²÷guC¶¿nýq?»ë\]BƒqžµX]oý†Uäü»Ø½éiäzKãIŒ–Î/h7ò¬”¨†/ØwhE<Í}–ÔOVseó [°õ.«›[úIêñþBûéá­ C—í^=k¹fwÝãÎU-üdXQd_©ytéU;ÍŒ æ:û<ع—ŸKD¾ÝRíX-—*õ«È7·†»X _JL«ey¦¶wìúZKë\”=po+%¦6ìŸÎ1ûp²}3ó÷cë×Ìÿ¦JŸsŒ“—ó´åÄÉdÀÁǯÎÉÓÈÌÄ%ú…´qø ß ƒRâÙ,R¸OOC†M[¶;”Ï7 wþãbö-æö!z‘¥/¦ï?uÁ™Ñú8b¸¸ëÅèð4RXí~óþî…ô\Ù·š:?òèó˜M:ÆQο6”2gæGÉö»l=Àü—,ý‚¤gú°œ öSgŸ‹hë•idÌßz.¤MÕª£#¿ O¿›œ®›KÓçéÕIkCÍ~•ì93Ÿ öÊΫø9aî!€pþäiä“ÊÌ‚ÙK 髜Á/JÉ‚¨Œm="cièV‘³ [g²>úl]àý¤ýú \ªÎ7‡Ë»ÃèÓ”Nh­ÏÅ«ŽRíõ ÷eNA䨾k_Ž5ûHNövj•xº©yýÂêÒlÃü„-ý4çðoc6ö¸3…rõ“tr7=@~·¼.Ù~#“–ɃqtR±±†!”ùN1\V—fuIö|,}‹õ§†u‹AÊbifð½tb:YþKT»"º¼Nt¿mîÁ¤Ñ͢ʹ#ã¨ÚK&>åÿžö„ùŸ°|ÉÕá»T©{Z,;ŸØÃ:‘ŽõìråÎÖtâÙ.°ru¿"Z°& Ÿt /;ÒöæOqtÚt·AcÃ)Ó«k±ú«GWñEÇû;>^‚“©ç¶iMÏ¥“§Íå'­'Ñ„ÙÑ;û‘ú¥ FušG¹þúáæúóßey’½²us_Œ³Á}÷¹ýÂ4$ÑjÛŒ‡édMuQYÑú"Z\ú‰bö ñdWO»’­›cé¼jƒwû?œ@¹ó?ÂíßÛ™ó Ë“ìób¾&½`“}É'«¨»8}Ž ö7Ë|¿º/:UD_}éØOÓ2¬ë|æƒKe ÚaÝÖç_O ì¼’ûýÌy’=w¶¿±ôGV`œ˜E­±Å[C'­m"Qrg®gß ÷‹˜¿&ÉïîúƒC‡XŠÅü}âÎçGó:ÕkØþ–í;-}”'ËÆXHYOkþVñ¸4ž’Ï‹¿l»§ÑYjãž¹ËO@^¯hçýô»8Ú¦ëÀmgL4×Õ˜Ï+{þoúUró9wn­1ÆYÜIÏÍc•Ô{õˆó¹”x“ÇÂDgéÁq76¦ÛŒ'ŸWz>-ˆ§‹…+ý=nDPæ{v¹þÇþÒ°s`¶~&¦ƒ˜„Å‹I/g©ÃÀbÏôMüç¡!åÛUŒ =KŸ¶™Wx=ˆ?ÏŒ§OGÿÐ&£édÊêçÌ×ÕÕïÝ™»íW=_O©êeuª\ÌÍ»[øz£†xÛ^«9xÍYúèÇ›~BV?ïbŒãÚM;+ÿÊ4wýj©[±†”ølÙw¬ö9Úö¢òf§aÄÇn½´«Çˆ˜ÆI“Ì~ˆl½Çþn_xMÄî…XúÖK1Nί[&uUÑe½Œ“ 2î馌Ñ}ÎÑžá.C‡E„“£w‹×ßï˯Ï"(—çÝù{ÝÍçÄì¼{òom¶Ò‹Í>5&ý`œÇÆò@ÖvVXýÀA¤Y½ŽíyŽ®~¹f­ý¡0²etZ1(ŽÒGÂÞýëM¢[ëÜ“ãÝÍóš›<"#`Á¼âϱÌû“~0Žquh›³“rë£ ²É¿OíUûÏÑZ7&­£ñd+ŽNw%5|e÷W¸øt3Ÿ°û,\þîYå¼Mƒq†å^xÑjÓô@²¢ðf^Ñz4žTÍpŽ6Uv=çØ:„˜Ê­>q›dÛšÙ´·¢Ì+Ûžf¿Z–?Y°}®å9µã$m™Üà§®{hSaú8i¿Ë&³»àtû‹©”í¿Ø+‹/þ\™°ú¡I/xÿÜóK¾|ôù>j×Õi¯K“LbZEhÎÓK{Ãú ò$Þ¯ÓËY¡ ''Ú´o4|*Ï¥;_ßêNؽ%¶Þôm–ÿkh=±Ù—Ù¤—l¶?ÛG#îÜÛ/“,Û%Ü0œ§«º7û®vL i³ãP탃”»‡6ÕìSÊùúuç÷M·Ì÷—˜.Ù:Á¤ã8ó²¾ó|5M'3I®ßº@¯O.ÐÉ1ç"J¬Ç“K“;žð?›²õUô²ža­º°øuå×g·L÷74x?µÁhd¹Ÿ^·zýúV&iµó›Þ½/К…÷ï4}<ž„>ÛiUCKO¼œôòæ*¸;/|¡Ë`Ò¢AÛ¼[“$¤ßY«ÊJn™÷ÉÌߌśIGz»üËþíðë½,²õ«»²f¡èþÝz´lLŠØ›f÷K ­ùõ‘¸ð¬)ô¶öçHW»á„¿‡eÞ3ÿ`æ×Æ|£Lº8].6Ð:ˆ°ôV-ǶÝYäûÓ'¬¾@û̪Uï#g‘¶œ2Þ+\A©MÅ•[‹¦Ðs¦ã/¢iEc>`¾GÆí+*Ìó±e½QˆqêîíÏñü‰v-Ùúý£,RIÅ´Õ¨¦éúŽA¼ßc4]×M>nå‡ÓøzÓ02ñaņ¹Þ$°é˜AÕ#ïˆØë“Àß ]C¾0¯ÛLúÁ8'dË3ÑØ;‰¯Ý;AÂÃ’ÜÓ0GÛ¾ÙäIÇ«KJ:ͦk —<)šJÙ|Âæ}V/ãæÿ;"Î7ÐÕ¼~7éãœ1^c°USvßêÌÄëOv”SÝŽó¥³ $s(haT¥‹÷œ©”Í'ì\Ë÷wù:m…ˆ­oØùI?9åâûÚŽTSŸÁ_äd„$9e¶ÔÖQûäCÕúnª #÷çW¦«§Rvÿ’Ë =‰Í¹:®ék‹ì.N][z¯Ÿö%Üý/.®…9¬Þ¨¦Æ§^|ì$±mçœí¤£Sì=ý¿¾@}¸ÊGWWA‘¼>U÷šN¹ûýøû„¯£?­}¼~{Ë£wEÞAñž.“T9cœ±ÇZ` :LE{ä·—Ö>Eº$ís`„ŽÖ©_óóÄÖ㉩pCWuIË’S6¯su 7ó=nn±x´<ç’bœƒ[Q¦æëG§H÷®6÷wÅé蹡÷Ö>"å“ã7Õk?‹ž[w,»æ¯Q”;q'-E©7¨XLØ=\VGçt,"œï27ŽãŒ0]|:B3z|>hfü)’ÖôC›ø}:ª*[§ëûRF¼·¾ìQëy$›?áî'çgÒèË›ö¿0ˆ0ݳ}»_|bIçN- ï¿Î¯Ë0NÛ>³7y$¡ Ö½?Ê?E”¹‰«ÆÜÒѺ»oy}3”½2?lË: Un¹øæmxGi¯Æ¯E7²É–gŽ|“/Ò_˳"—F.L1®Lcèk2$ÅyÍ4zÓá¼è³%m «_±õ%[ÿ±ú‰¥´ãdÛ/îe;êݪo[6¨÷ibŸPsý×§.Òv‡Ï}•JR›ÕŒ¦Ïò^¿‘>Íü¹±û¬Èöƒ¬^ÌGñuŒS^f4†>Fƒ&D/Õ~wšŒ‘ kráùEZýz`a»ŸƒI´vÿ¨'‡£¨Mñ–ˆÞ63èî‡ì'gôàï…ü¾Oãꙫújâý‡ŽìèFÊ·¼ÛíÓ¤®¾è‚“Ã%ú`vwG/¬cŸ‰|æÖö¤Žj_ïø9e÷ÙógÏ;°¯²?WàýñÃßSitûFãî²~A¿_›{]¢áóß@<ÎÜ},¸IW­wôœ²z8Woê@Øþߤ ¼_BHéåq½Óù{õ9$æôÉx§é—èw²Ù#†®ó'^#ꎬ#žEcšoéõx²Üì7Éê ì~ «_°snËú¨ã 1% tº$Ëx-‡T[ä4îÑšK4ÁeOÌ©Áþ¤GŒª«Ðlªq¹þý Êî°sv„3²ù¾J} ã,ÿ¢Ú—s>¢ôá±áagîä¢Mfí¤ø|¦¿Þß'ÖŸô~ˆÕýY݇«Ã¸U97Ubœì³­UêoëÛã@ö"3þàÑš·9ãЋ—A¤áظa-$14ù—F¯6ER·ìü‡Í+lþâôÓ½J]^ƒqÍî4·Nž†îûäñ®­òÈÖ_Îý\B„ÖÈp "¿¼ü-oaƒÙÔª ÓØ¨Hóþ•ù3ݳº<[¯YÞwÔcœU§#zm¬–A·åúû?ž”GL¶é.Ó’Áº_çï)GS§ÛË/û5ˆ4×3Ù¾ŽÕ}XÍRŸVyX­ÉRÛï_Û¸‚Ì#¥5 }D—é«Ò°üf÷È«NÅ›kÉfÒýõ³ÛGòû=sŒ}Nì~(»'Âé—¯3cœüIß6¸¿2ƒ¶oøsÌ>ùÄ9òËQ«/Ó¬MáwÆLö'+g;í=q&?ÔXÑ4ßfqÆöáÜë#Ñ‹˜ÐcS—»U9Çcy¿¹·o]Ï uÆ¥x6JÊ'‡?~Õ:á2u ¸®ÌoãGZØ1B{ Ÿ|ÿ´Õ¥ÅÛ/SŸ<AÐH)ù²ãIÍ‹gSŸ-#k¬˜ÅŸ—t7×'¸8¸Á?ꡈ1ÿY“n0Α†#WF$‘’™WZ9_pUÐcÂVÝîùG™ëæ\«©ù"[—±u2Wàæ{%Æ1•åcŽÓíîEï°) ®âM]ï\¦hýÈ\’¹¶›|6m㚸«ÛŠ(s°svþÃÖ/ì—e}^ƒqZºqñ:dîÙ+Z¬ÿ §€bèêÁÆx&õËx¥¨S[Ìï÷E„Ý×cë1æßÍ|¢MúÁ8­W½NŽy‰x lª®1MKRšÕˆ:†Ïm»G“Ë‚HZ–ïãZÅÐ$¶¸eþž[³yšåQv~bé«-Å8ƪ|s—LúyG«EEßkIMm¼]øÏW¨ÛrÅõúm‚H"f‹É›£é¬~ž ZGEš×ìÜ’ÝÛãæ…B[²Ÿ3éã„Í}|ÖXr²ûvÙ®WÑ´Z§.a+žÌ¤Lܽ¯^|^»!bu!îP¯*ë!Æ™4uåýÝû3éäµ×jÏ*$Ü÷¯ÒëÁá²ycIãš#ê9íCœµí%›~e&åÖeùù ›y¿ÄÖkL7–û21ƶ¹æÞ<#“ú›.¦’\}¼‹¡ô*=Xèô.[ÆùZ×!6cÌyº3qí:~!—•O¿ééín>g`ù†ÕO¸{N\»ìt­ŠÈô_·ú—Ò^+?ëð£Ö¬ýí®Gb¶‚:Ô¿6®æ¢H~¿ÓÕ¼ngû36ï°ïõp:æò´ãоÞÊ¡e™4*±Y«o‡‘¸úý—…†”Òí×h¤ô'\="Ƽž”¿ ÷%ÝȆº‹—,w4ß§cñÀÖõ–ó¨ã´Ü{¥·øY&=&'ßÒYEÄåîÂÒ‰KJÍùàÈÆaÉÍ¢ß̲Q<¬Åߣéjþž;Ï`ë6ö=B˼f¥-¯o·Ig“EC^9U«ÜV„}¦Ç –Ò¤ÝÍ&$/ ΊŠMýÇÐÓµŒœ‘ü÷ˆºvÎÌþvþc™Ÿ…xÿ=„,êãEs2ª×Í-"ÄcMǨ+ˆ3ß2—ÓŸãïðü2Îuk, }qWq'1Ò¬O–'ÙógûóQ¦/â‰øu?ß`œõ#Ô³h¯MųʋȰȞÇCªéi‡½³¶5hâOÎÎ祥¸:žÆNŸß°Fë¨ÿ¯¾ÉγØóçæ#±¹®²ÑŠû÷¾'ÓûžLÿFŸ+þïÓŸ‚P ¬«sýç,}¾ô|_&ÖgÓÒßùݰ¾éÅïFËûº¾K_¦7ûl¾K_&)ß—ÉØS8ùOzÑùò^ÆžÂoÓNË÷¤Ká{Ò±¾%ïê‘ò=éRx¿ˆ¿Ó—Iý}™þ¬ÏæõeÒò}™Þç¾÷¹Oaõïå>kþ÷ן‚Plù>M–~8FC‰EO:K¯/æÁz ÿÏïqø.žÖoö¤{Okïií ¤üI¿&)ßOým{6éøžMJ^`2 ýúª+ÿ o“’çßñµÖ¼…¯õŸõ¤û#_kcO:ñûÜ÷>÷Yý»¹Ï;J`Œ: æ{«[zZ ”r‹u–^`Ùoôþo^J` ŽZà†`VktÐ1[lñ ÊHä©@€@W¼Ñ§N€ WðBð«HÀBÐGˆ!T)D‘ œ!ŒPÙä÷^œÿ©·:óÊyÛÞêÖ|ou-ß[õâ|WÃ?ê­®å=À`Ì©!ÔP|!X p„h“Að…x5ÀN•@ !ggˆ9…´ hÿ¤gXCè@ļʢGÝûÜ÷>÷)¬þ½Ü'à?½ñsF¦1â £¢ÑÛ:Þð cÞ¬ñ_ñ–Ðóžˆïâm­B|¨xGoëÞÛÚ Qò"‘-pƒX”ÀšïÓiì¿î á¤ðâ‘-pƒˆ”ÀBŠ: † TÀ¢’=C\*` EÝ?Ї]l!H9Ð ߝӨ‡ýïø[g¿…¿µ5„t@ «€-D.ÿþÖÆŽq’÷¹ï}î³úwsŸ¨€-Pô@Â÷`·ô¶"(@$þaÌo²7ñó›P[±è€Á¬¶h9Ð ;Ü `^r5"Ѐx!àÕ@ˆ OÀÁ¯Ž@2¨R!8C )¼ d@ Ü %/ÐþIvæ©ó¶=Ømùì:¾»X;¼»Gâõ`×ñ‰  Â˜!R p„P“A%B°ÙÀ¢M•@ ñfg8…± hĬÖtÐ7[ ¬!î bˆ\l!t9Ð Ÿ l!zùûÜ÷>÷Yý»¹Ï‘ß`ü„j D ÊßðÚ1z'ý¯Õ@ø†§ó ý_ô `þ‰ïâ…­ŽødPÙàí½°-ýň XC$@Ä‹ ØB0@ Ü %°â¿CD*` !ÉH ¨T €¨À$W*°…Àä@ÿôhOR À ÂLåû´ÿolí[xcÛBØr < rÅðÆ6ˆ8lc¬ÿýQîcyå»;×ýyîŸÈq,¿ýӹ͘×þ'sÚßÉg,—ŸO*  À¼ªÿîs€à2ÔüÝ'ŒyJˆl*`û<%R©z AÎJ¨€U „ÖP|´àˆÀ­¾^ pD'ƒJ E ggs Ð2 nl%°FpG#ÈUÀtÀ ¯Öú b>øe@ Ü %°†"€ˆ!°…(ä@$G*@ r †HTÿ€÷¡H£R€÷ÉIÆÃ«4æ*,8Cd)¼Ðd@ Ü 8%/:Ð7ˆO ¬!À bQl!F9C*` QÊH ÎT €@À¼ Tãí#ÅûuÙûu™Õ¿».sãß¿Âø9!5À¨xÃ/Ç”FOk p|Ã,¨ `l* hÀ¼¼j D'Xø ¾‹¯u6pFÀ§ðAÿ¶¾Ö–^ˆ$ØB$r ˆ% 9Ð1„£¶Bü7 ˆ( $0/J „U¨^FOk €ÀÀÜ 4%°†Ø"€ˆ!:°…ðä@$`*@„ `^£!ÈP|!L58ÿ=kÝ[x\ l0/\ „yÂñ¹6Ʀñß_Í}ÿ-ïýÕœ‡Gõ"ßý·\÷®yî+Çýüö¿™ÛŒŸ³`  ø"ÐT|°=¬³#‚®xYø€ÉH„©Æ='Q$ÆT @@*€x!—©j^R5"P@ðEÀj€#‚6T)‚78#€“A%"³3òX ?‘Ë€¸!¸•Àt@Œ@W[»èAo‹ —=#øUÀýgœ¿¡5òWб?l! 9Ð ’ ‰€Ä¢6î1!=@4©Àöð64/ˆL „š‚÷©Ná'Zàá)5Ät@ ZC„@Ä£ ØBr 3 NÐ Dš ª€«Bˆ6T_ˆW Œß&Lx¿n{¿n³úw×mþÿ¯4~ÂlàŒ@L: F@ªxŸC£wu6pFp*€x!HÕ@ˆ@MÀ «Bm¨¾^ pD'[x¾‹µ¸!à•ÀºáÛ{X[zzA j €HÀ¼ 5B0  '„øoÀ¼ "5BH  øBPàQ%ƒJà qi€K@ ¡©€-Ä&z èRÂSð‚Õ@&€ à 1j€#™ *ÂÔ¡óÿ¼ŸµÂNÀ×Ç¿àgmŒGã¿7sßÊyïóÝ?—ïþ§rÝÿÕÇÚÂÖ PBààöäX =)µÞÿúùÜßs¹ÿº1Nø Â}C£ÅJÍæ„fS#pGÓÅJÝD Ãx¦f @3ê ©f @cê+šS ,@‰&574ªX€cš¸¡qc¥æ )ÀM¬Žhd09ZÑÔ*`r4wþxfïÙþg÷¿ês÷GÇ¥R¥\¡¶d͇¬bbÜ?b{½º© “”6ßÇiÿ|(yäÆýŒ^^:é—¹9”qßA¡NêÜϘêãs–µ¾¹,û]ÓT\àõM¬ùWSWíAW‡ ŽCó­˜õ¬Ö§¡ŒçK‰ù†l¾ÜÜ_BôéêhË£êèP碋Ôr–}ÌšìñõI%ãÜš³[›Ø‹.Î{r‡’÷RCÂÒŽóm>#¢ÿRG*0ÌïçáÝl>M¼Þü•ƒoë"ùà‰þVzÔY ØÂWE«o]*ņ•œ˜áibÓ²-‰9kým¹© l<ú5×R5sONÄ}´ùõ¹oŸèsÙ™¸/¢PÇ„:[w•ÝòS‰³¬î׋ÃÏŧÒÓµƒƒõ1±ãzù¤¬Ã :v6¢Lýe Ù¢#ëûLõ›iËæù]Ü׈ûsðÜužïfm„r…ŠLóŠe!§Ú}–šJ}ï Ëlbý;ÈÏœõ'çý_:œ3Ìgg=Mð™jË'å¾5ÜŠûÝñœáò2\?m]ÿg~O“™®f«Ü†¥ÓhJü5¿±&¶qöÝn¡K‡Q©Mãkæ±êï7^ŸÊƸx”\YÒÖcÆsÎD«Ÿl9MÜoQ¨#G{áÛ=Of‹½<Æ=¢[%gOŸabï–½yPêõH*»£T«×åæ³¾­Ëtëµ}Z5u‘â]7jp²«c±W^’ŸüÏž)—F•ñøðFò7þœxž U7¨SwG¡wËÉìÍÕ³3\¥‘áìËJm™ØŒRa×M “žÓ3vÔ^À¼>­Ñé£Y¡¬CaŸ)s[~N'{¦mÂý†JSž[­½õö˜=Eª¾Ä}©¬ºAû­dVØrÉÁúÓÈïnëceV›Ø@UÍ çQ4xHÏ]£°Ê¾‚óf(Ïõ•| H~}w=GÕ¼íý&ô•ç¥&öéZÄ7_‡:¢¿S2zÿp–bI]šß2ü“&#Ø6Ý |\æÛ|­¸Ÿ,Ï»ãºä÷ŸûFŠ>=’êT0gÀ©É,<èû܈ýiÔ6eÒ–EÇM,ø§æn5 •tfÙéùíFÏc{Ç\è)ÂD×ÄsjyiSæ$÷XÐNÔ ®ë½ÍxÃ=-™õ· ¥‰¾ò—LLqr˶.%GÒ!—Wdža¬á¬…k]üfð|jâ9j\÷V=Ü,{ 1hø9]Ê4ºrè]-mVgÔ´,³ÚEÕfóKý•¦J9ôJ)÷x”ÍO˜ÿÍ}óíÇ+ê¼Z0¯ /9™E 6¼ Òigã®u²î™XýaG_ŽÜàO#;ÕjóYd½'Í2TSÙÈC>ºo–÷ÝæþKÜGÌÓô(â÷/GŽ~/ ÷v%³Ïš2|‘N?ÜúôV³g&Võ|©Ag7 ¦÷Î-Ù¼Uøp¿t¹“Ó¥üYÿˇûÜÏÖ>¿L‰:Õ§z\´:™ Ÿ|îýYé´¦üW;Þ›˜\þâ@ARß…3O¯«Ú¦KCØŠëc>t©ð‹/1ÏÏåþévï.~˜û%Š>Ùa¨sòú“w–'³»ÍæE§S›%SCËÞf-fviA*?z&ÄoT^Ì’^¥íÿüþL[®Ïcãþ¯Ü‡Gôì"ù©‹úÓ¡Ž˜“šÌb·Áéôì²r¹wµÛ¬ô۔㫢Rެ :¿˜ÝÖ¿ç0‡PÆýJy^ ÿ¼øx/öK7›®U¨ódሹU6%³‚Ú]ª®m˜A'ƒÞÿx Þmvpñž†ó“5.xå¶iÚ¿¬7Sòûu§É~U²Êøw°ùŠññ÷”ñÉ3¡N×q5 ¿ˆi? Î ×ÚËŽæ7»Í œæoš_ÁŸD_¤%l¬aÿ‹ò·Õ¶¼þûp6î3ÞôûüñÃÜåEüßRñÜ:W#y½.™å%Lî?nA•ryï9÷Û,¹s9—Öˆ6°8ZQú; Ò3™˜Sáe{ëÍ™µ¢ê Ï÷*»ö~á)æìʉçYXõƒ:k¯¨1d%31g.ƒfÈ´æQ=o³¯‹'u]TeM¯z¸ÝQ/ k²éû¸s3%ŸÊ$íìö{úM¿ÍªM6:¹:‚n~˜ÖbHX8{vOõÝÏ !Œ÷+÷äã ÷´ÚdÕmW$ŸI‡:ï,}–½n˜,å{eÒÒUoê,›…gþ_çúps5lâæ1Ó&ŽW3îïÏs„xÞ÷3ä>Ëöþ©zÔ±ÆUNfUZ}Y°t@&üiUœCÄm¶c[‰¸[†RäÁ„uÞ/eÛ2Çnto¦ÍG™ÏS¹#÷…åó'ÑÇUÌS6¡Îw[«çûX’X¥9B2H&}ܤӕ.o³Õ‘ò¼ÒNþtõþXã7Ë¥ÀPVÂëáíË=ºÑÎV}:·_ÝÙæoÌýì~ê:lÚ­&]‹ä¦8¤ȇ[ “ØåªŽÏíʤï¤,·ë6ÛÝÏ9ïç~ƒèƒ‹`P¸œ ìºfîó¾Ü÷í3âyCÜ—Ÿç õX"¡X·"932Ôéb Hbk6ÕZ{ 5“Þš»NÞxè6óË÷ý0«/%–>òy‚ûr&úɩ٬j{{Ýõ%ÃjŸ3·Kõ'ž3Às†"ï %ƒŠø&ÊQçAabͬè$¶dÔÓÒ±¯2©„²Ë‚ðÓ·¥Ü±ßÁ½ò»ì¥,vÇôÏš¸¨÷ãã~¹<7“ë–çZZuƒë—«¬rô›˜Ä.Ö+ßåe]#Å-ü²ÉXÃmv´ôЈ®iãÍ3¯iñ¶nºù‹ÀHµ­Ÿ¹ÿ8χç}ÀóŠäÛ¢Ž˜·“ÄfüŒ™²‘¬¶²Cn‘¶t±îº~Ô8¯ØÆCª%l³±ÜÚÅLÆóáżš¶|>^ûÚÙçiëPÇÒ1pŸgÝ$V¼çꎋ')êÝ^ƒ{ܢ“öí`šþäh×n£–1îsÍ}?ù|‹_ŸûÛòù¸½ÿ¸uœW½Q®R~îNÇEÑFºª,(¶¸Ù-ªté³ó‡Â†Ð7³2u­N­`ÅŸŸsæk5{›±}ÿ©õs鑱_´'îËÊßùóÚ>gÊ„:åK}U¦ÁÏg˜˜Ÿe¤Öi'=¯UºEü‡µ8ól½Ú|S³föJVAÕíHó‘!,3°SJ÷É}HÛW\ü\šþ’GÏs°í}‡Ò äM|åb8ìqf# È­2eàÓútͲ½gö§|6sêÝ+ØÌUù[Vm fb®]7ª<-ïÃ…²Ým>“|ËßC¬zÁõŸ–)qýÔâ3Lo²¨\9×™5Ós¨É¥ÏÝ× Fq÷QE-gW„iÂ`ÆÇEž3Æs9yž™}î—×ï÷é×ÕÏÊϰœ3šÙÅ]³èÓç=\ªÏ¡Š{ÖLZäOSð–X+v9«á>µ£&˜ñ\ ®?>¾ð¼!žsiÕ ®_÷íÙbÏßèYBDCÇÁò,ÚÕ÷eô¨-9´gÚæ§ûîúÑ«œ²ý¹+Øšy—,/k„0žWÍuÂÇžÿ!æÃ´-r¿ÃPçð1œÔ³™ÖÕx˜E+WÜ-Þee­n·åÍ‚´É°jë¤O¿eOF<ÜÂöŽw¼Ð|V}sDêÙüæù|†çNØçèPGÔ³žuW¬h´gEÝÇ[™ËìšÛVQ¿$ë_[ËÔÇ4Ïî ±åðœY~Ÿy=ž—Éý¡­:AŸ£ËÜo2MÏ֬Ϯ±ýH½Œ{ª¯üu}â>~ û‚:¸|׺p¡Ö6¯åþßÜŸ™÷/÷gæ9mö¾¼&Ô¸ÊTa‚¯ž5u8ß6°0‹Ö=ͬé Ì¡–ÅGÏ.±ª5,×âáûýZ5WÖ„0>ÿýö»þ_ùÿ¯Üd‡ŒyËЧ“žYc««e“AÞ¼ö€Êž¹,ãç>´ùø1KÎF-Û¡hï8ýDˆ-7‘ç0ñ¼TîÏsºy®»U/¨Sò÷_úvгŽÉ,oݲɱLÆ•ÃÝshø²[©é}©Ä•oÊÐT-k1¢yùˆÔ›?7÷›çïöï‘r\7tÐÍ¥S\õŒ´'§»vϦÅ>ÛÆUj—Cá7¯:¸Ñ‡çÆJײ¤jBiã>¡ÜŸšÿÜü¹ÂsàÅy””c‹:«¾ïw_ùޱm§›ÿ˜6,›ª;.6º~ÝYí×öüÔ$XHLÔ2`Ÿ%C÷]åã=¯ä÷ƒ¯gØÃa¨snZŸÝ.S[ºûÒéfó³éYZÖÜs(¾ÔÒƒ³ø‘5®Ç?‚M¸SÇÔû£V´¯êO¸Ÿ=ÏÑæº²êuo~¾Hûì4ó}²ÉeʈndɦZM&…¾D¥âwŽ4;‚¹ZBØ$k°qG["÷¶ùñJþæb>Š´>†:;f$¼¿pšM\8gûˆ£Ù´–\ß`ʦb§sNN  ïÛ¡˜ID°?~T¸¨sˆ-×^\g‘ÙÞky?‹¿gûPÒ ê<üzãíºSl㑞%¿Çõµû.;MÓÞÏ»ÕÚŸ ¦x¹¯ÁŽd|Rvr§ÆŸ·ŸæýgÕKf\"$ždÁ°Q-‡5©ðð›½ÙÒxäOÖ°–½ntç»gSBŸs½ˆÏÉKž<„¯›Ñ êöiÓépÛ“L®rx’W±®};EgSªrLÕ]w4l"ëE0!5ÓÜšç¨T³ÍSy¾^eŸ#Çõ›ù,;“|苊sèï6:‡„U˜ìo²©“~þƒô#˜W4­á°+1‚YãMÌj&Þ÷ª6]ðŸ_¼?×=ùzÆî!7ûWWUÔ êÔw.³ze§Ì·n®ã²¥9´DýðmÎØlª\†%ÇuD%® 8°~a¤í}‰ûþóü(žÃsaø{˜ß ú#‡¡Îšµ%§xÇguî—_~rO ß|è–§O6]Üéÿò•/ o+$ï’ÞÇÔLôÑnAÜçŸÏ_x>/ïwûuEê®â^¥³϶ׯ~>‡ôÞ¤³iÃ쥚2|©Ãý#=Zº¯b#Üï­|b{.«cd tI­móW¾ ŽrâëÜVÝ N±ä§9ó±‰K¢o r¨±átÍA³éÁÔ…û›Ž÷¥—õñËŽdÂêXf+µ-g–¯ûò÷$>nòu{qêÜN+Rñ{´pµ)­Ì-ê¾sÔõîU²éÔË[{»üèG² ;®\íÉN¿Vž:Ó6~róûÏ×kpü¡J¯–’ï´”3`,÷ÿªTß>{™Õæ½Þ-êܼ®k±7YtîrËéÕï)èBv~VÝSÒúh(ãy%<Aü}Îyò¾æ¾ââ|£Š¨Ô9×¥Ù„sÙ–ô‹5–v¾EqÙOT_˜²(²ß×%£ï6§è‘ÁnÍßvx‹÷ÿ¬´ŽvIÊͬ_$wZŽ:‡;wî(FûÁÃnÑÕF Qú3Y”=fãv“‡’v¨‡ù.ÓD2/ëB‚š‰÷YfËaáóržÎßgż3ñ=C‰:7üPþ\£lVõu´ o‘0{.«Ë¢ÇmWÜ›@½¦¾;Ṋó}]vüÑ`ÆýÉy.®ØgOž7ÀÇ=þ\²êu®<Ôtï =Â^ÍÛÝe^Â-šYµMÏìo²¨É®´ô¶c«~ß^ÅÎøm”›3þæã¨Ï³ž¼žø|m^ÄÇ\'|nµf_)Sò ßÙ"H›y‹¶ y®’E•¢V Ý¢¤AÇO\ »Šœ³ãâò£AḺld{îðëó÷'>¿ç§Ò< uÞ'¾{zqéaVož‘y‹^ßÎXµÏ#‹(»÷ÊEmFPÕyêõhŶWX¼m^½ Æç|þÊûŒ÷ϳÿ}L¨³noãã-ßb…º>îÏ?Î¥g݇.t®›E½ÄöëiB5d7J.UD±ÐÉÚÄxþ÷]Ç·$[ðÜ!1?¡²¨Ÿ¬¹uzÞ!–ÔúòÆ’r©kÛïW•Ì"ƒqLaÛ7ƒ©Â¹:Sžbá'™O7 a||ä¹óü~‰}è$ê×µsŸbGZ¶÷¢¹T*áè‚ ?iª¡ôÍsÛýHh]»(Û¸Éç<Ç‚N\Ÿ|Þf¯9êlr³ëÚƒìØäü¸÷¡¹ä÷ nÃF# ÞªîãÚÖ—&mH¬½OÅü¬ 3¥}¿¶·4É£ª‡³Þ]7R«Ty×  º:cߪ×¢˜˜{bù{ _áëçâº^ó"ë&ÔÙWÚµBûrûYÒ’ÂÌŒ/ó¨ïhÙŒ ¯p_¬9/C)¯óÄ/4Ç¢X£U;'§3žcÅ×§ø|ŒÿÍó-Ä\SqÝÏ!»@.ÞÏÆæ]\QRÅ;]¶é—FšÑRVÉè6‚ú¿,æ=ùN[U;a÷ÅÁŒ¯[ðý#>/·åóHÏmû0ê<÷É ïï”À¢› 3è<ªRê¾sO7#õø®^ÿÁ-F’pµj]¢Ùhk°d0Û°®Ã÷íz|F<φ¿gòzâs¬h.‹uÚhÖ¢Á‹½¬Ý¦éGnË£MŸŸ|Z©²‘LÇÖ$I·ÏÚ¶:šMâ«#‚Y¹>]t_]úŒø¾ÏÇàû ü÷áãœU7¨ãr-¹ì;¾c}üÃÝ3ÎåÑr¯ŽºÏŸfRÙüRËFнÒÝë JŒfu}ïxœ¯Âx®#ÏaúuŸÏòuS«nPÇ´ ³åd÷=¬é‡!#ŽååÑókƒçz¤eÒü‚¼äF†“8߉fgC…ÏÆ×õxî¿ÿ‡A‡ë—².4îbŸyç^;ý4 ÓŽjɤ1ÕôÁ?£ˆ [—õmÍÎx¬¬×t…Ú¶ÎÀß#~}®€ç(ÚïëQǺmù$žÝñŸ¿<¬\>9×ÿâçk3iWÊÝi®…C©ìáM0š5ülŒâ\­™Œ¯»ðõ)þ¾ÄóLy~M‘÷Ô¹¸{zyÈl³¥õ½½òiÄ·êÀŠs3i®áÈø'§†PPÝêBzDÛöxßãë%|Ý757¨Ã›:ÙrG¬ºÉ) «¤-;mg}¾žÖ-Ÿ:>È\ž4&“6=Þ©u¶?y;—n~li4KŒ±nE4æÍ>o§gzÑÑòî}.Nó"ñ=Ó,­/>óÔÅ¥èßƒŠ¬—¡Î<!8{»ûäÐ[½_>MxùbÅ«Ï3iÇõ S]øSâß‘Ñ,P¸-UÔ,?÷R• cÑ£Üzýš ¤V=bÝo÷Vê³BÏoRŸgËô£á;"b.åtuƒ:â9”8æÕÀ˜ø~x>µÀÛú×n™”¸dkãà9C©©6Ëÿy¿hfqÞãž>ä—߇ç®ò#±ÏŸHë˜Þ$®Šç”¨Óíèêv=êØÃ•ç:ì ̧TíŽu¡U3)Óýç^ÓÙJëä¡}ÅÄuê™¶sÎÒÇÒ¡y+¢™¸_ÌÄ÷ï¶ÄÇI®S^ϵW±-ÊóíŠä˘P§Ä0m‹Ë™Ñ¬ÆòÊK¼.çSAIC³IÁ4»zÓÞ…êqô4äq|k—f=æ03ض¾!æšµ'>žñ÷4¾.!>Ç¥ýÍ[ò™Â4T$ ,Y+“=̧×OVü¢7>·¥ Ý–ïK-§}98¤y ë˜{ ûÛä`)°+q=ò}g>Žò}Oq|’ö7QçEà®ÆÏ¾]ɾ{sÁk†£‰¶ùªÃGÊ2èÑ£ õFS¯•3J÷Ãnw.ƒmã4ÏåáÏk¾^ÇóÉí×kä¨s¤šögÒ/eµúWÞQÕD=–]Ð}ý&*¯ïq­|óªÚûy›¥wcXöîoŸ>Äx>+ŸŸ‹û€¿¼ò"q<󦔨32Å«xÏÕ‹Y÷óq¾I¨Óîò>·•9éäÚ µgÍyÃÉ+6çÔ­Æ«¥çgרr«øûÿäë8|^jÕê8:¬ì:«å&œz˜YÑDÂèë¤O§oF;UÜãOŸml8¼í«6§ÓÁá›+3q´‘m…_Ÿ÷ƒ}®™×ßÿõÉÎmÍb‰QÏÎå}d¢M_VæâŽtçჩlž¶g©~1¬Ý† ˜ÿ²¾Ås¸ø|€ïŸòó-<ŸÒªÔaO…ƒ SÙƒÐ-“*™È²,+Ò/"ÄœÓA´zÏRÕªþÑÒ¼_-ãë.åËm9§|~ÃóŽìχ˜P§v“¡ ì9’õmÕ:æþ'&ÒÎl¹A6+ú}âØâÄ Šè¥j0vYÛµ7¥Ìe5‹TÏôå%íÃzÚΙñßGœß¶'ûs€¹rqþàIW>(UÏD‹+^NX7:¶õùé»]!ådG±Ù“1#PÛòTÅõ€Æ¶>ãûZ|Ó>w\†:ÆJûÏ$õžHÙ§õ{äb¢µGæŽÝÖ;¦éÐ]9”*Ÿé>ï~Kîu6Ê{Kãóå™êëóî:6±í;‹çÎ2=y?±êuNäl¹Rz&Íé¼ÄñC}]/¿øsCótzÖ§âýb]GÐûÇšréW£ÙÉ}—]bÁŒï‹ò\1¾ÎYL×jT^žw…Xio/iün%êu¼í=lêÒüD»ú‡œ{ž©—ó»½ûÑsËá-W§~Ýøº›U?¨³wÁ‚SK\–¸nc¢ÐYçgdÒ¤õç x=¢»jX Û;7süµ×3˜ó£ðÉËÚt&>ŽŠïkwmûtâsÀ«H±u6dLöyóÝ ’³Çù'Ý¡Ó ©õmK£Œ«²5¸îMN:ÕfJ´Ý‹øú×?ïk~îÆ>Þ„:Òúf“Òº·;¾@ÍÄóàMlûD¼ßg)¢—üyÅ—CW}G¹ „ Hõ™¬ªšJ÷Ö¯Ÿäõøù)ûç¼u%ÞmÐäþN”¹ymds©z —·è& §È‡Ü °Í÷?¼waäŒ÷#_Oàu¬}ëÕ–Ã=v“ç%ÍGò&š1ëøñÑ7)¼uÛÙú¾Jھ槠#c˜¥Ö‰·C¦Ûæ'üsæ?/_ù¹Rûów¦y9–v}ÝÒ=T5ÍÜbO-|>?µótïq“¢ö~ù8V5‚¢o¶=uVÃÄqn:ãù |”ï'ñý8~”ç[[u€:_ª]¸ùÈwdyõøÌ$ÌÍ.«óéMrZ°ocïþÃIÌifŸ qÃ}§Kóú¶ÄÏÿò:£ª Š÷ ~žÀª\?6ù§ÎáöÒÊÂA+>6Ñ×÷_hWö&=RÓ0Ás8MêYúØ2·h&æ¨Ngü¹ÆÏ©ÛòÄ¥ï Xu€ëŠóý}dýuñsW=Zn”îá zîÔ¦ýÞ†#èö“‡/8ŬÇOf° Íœº6Yõ9ñy_ç7mçaÄuqœ Cmqµ¯|ªO ÷c–W:ˆ:Ñ“cæ»rƒ>ºç›ÙóöHªo]pbâ9lÞ§Düý”ë?ï’Noè±pnki[ït¨3¢¡L×uûé1>­9øœåîl”´û-xR§ç€ÚÔGQktã“Q,émõ6ƒK1>OÏÝü2nXû×+n=`s€V_]³±š‰^)OôÒÜ ¦CªÝ(7~9uK/Lwˆf¾ÓñYÄÄç?ÜÑv=þ|æç]Åç§ø¼1¡NvAëKm[¤Fà +›¨wxH5¯Q7èÐäS}úEަOz†ö?12šý¼¶nµ¨-A¶õ› MóÛVÙÜͶËûÈt|TOõ,¹ô\“Ö½nÈëwP¸÷~vÂÖ<65Æ{á½këqƒ2ŸV°yèj×§xZÚÌ7œƒ¾;õeãçÙøú&__åõøº«8’ô€:ËÚ†wê¶îšzvÈ7åLtì„sµO*ß Ónkš:G¡ñ?ùïM—öÏ™Á¢íÛ´³›t ›mÝÓÚÿ¸^éçI>jpX|î•0ÑûíãÎ}Z˜B?=TÇÐŒ9K¿×ïŽf«;ÔŽ)=g†t¾õK¾Dþ¢RýÞ¶u~¸.ø>¸U¨l=h|˜F,lriÛ“|:S<¶JŸBÍÔúfîÓFSÕ ÌÕ¦E³cj7Èj0ƒñç½ø½ƒ¦¶>åãvƒÑG¬ï ³–Ö·PçüävaGåGȼTØóé}ã¹/W®M¡aO’_š9ŠT*Üšçç~æÂCçuÓmç‚ùøÆçI|ˆç“ÛŸ§Ð¡Î­*ÇLåß!¸ohÉóù”óÞÜoMp Åå;®?¢  5vjú.Š#ŽÓ?Ä÷Ÿø¾ Ÿÿñý¾olÕ ê„÷;(?q”n¶_5åȱ|šôqÝ-|S¨m)—Jm(©}#ÿÅq»£Øèò{¾·lžÆø97¾Îüëýgþ~n¿OdúÊ×y¨ã´DÚÝõÑ¢âGò©KÚÐøÍmRȱðôú¬½Jºùnýü·_E±&Ñæ…#æNcü¾ð}f®G~Žƒ/…ï¿Xuró¦‰ìpÕFÇ(L¹².;šO6Äõ(V9…̃žlÞT2€<öö Þ66ŠÉÌz‡¤ÅÓÏçû ¼ßø}âß³ãçï¬:A“ç[ö?uŒXÅõm·ʧÝL‘-n]§Ãî§û‡—Eë§ä(v¿Çüã2¦Iç«ÜmÏ'þ¹ñÏËþ| ×ߎS¹ ™ÆF»ò©BäšqÕ#®ÛÖÏRVç½Ö.ŠÅÎûöÌÒéŒ?¯ùyG~ÿù>7ßç´ßïR¢Î“V‰Óã4oxßÖæSӤѽ<¯Ó+Kþ¶Vùcié¾àjñ«lã;?WÉ÷Kø>$ßdzêDø|¬ûË'¨u@»f9‹óI:¨E;רõã+#¿iH ×¾äS};ú¤qÓZ3¿Ï|¾ÇuÈçü\‹ý¹ êÄÉ…™'(‰«ðiù4KY¹ÂúÙ×hŶ÷n–(¬r»qU^D2}ɯs¯N·­ÿñï+òç+>ñýû÷#=êøvNN‹ñ8I'/eoWË'×®êÈÕ®QŸËÞ§vL›H.÷S›¾î¾ŠYåwªm¾Ïn>ßáß?àëó|ÝÆªÔñ¼Ö.àÍÅ“ä°âæ¶¹½ó)_yìñ¨ÝW©kx}÷%ë'ÐùWë|4a«Øñgg§ S1þÞÈû—Ÿ£ãçBÅyÿ~U'ßÈ¿Ÿ,|£à­¾®!‡=Æê}•N] 6½¼4ž.^)ÕòÛö«Ø¾Ã/w6m§’¾/êMâ¹àÒºö#Û9DqüêOõ¶Ü¨söZQ'¨Ó5µÏò±¯OÑÝ5«1Ê'ñû«Wh’éÝŽkÇRµè Ê#Yïߨ´*Û:=ÿ~¿ÿ|ÿIœG·“Þ;Äõ9ê§ÊSOÓñ±B |>m¯5mö„uWèüSK™œ5£¥ùy$óúfú ãïü| ×;¿äçöx?Xõ‚:Á+bðIŸ¦—”hù8ŸêŸsXò©Ïª_¾oƒú£¨LÀü~õ#Y·æë:5<¬²½_ŠýÖÎvg|]•ï«Yõƒ:â9FF©Ÿx]{ó!¾üêËì±e¯Pím2¼‡Ðäb›_˜ÇF2ñØ£Ê6/åßÓåó:>Ïãß;õ#íC¢ŽóuÝæ¹óµš4jÞ¾Â<¿7{™”~£ºßê@34ozgF$ÏïªØºƒëtºÙDÚÏniÛàóŠò/žýn¾{‘ïoéQg[V—Š ^1r;õîX !ÊøöÞ»gÎe žä9ÐwKËßìšÒ8’‰çUŒ¿wóñ™ß~^”¯ÛØïÛšPgJË÷G{ëéCŒÃÛ»òhðÞaÕît¸L?TéUWÓ|©Gw ðÙÁz ßLSÙ¾‡$>ç;Kãg¡m¿†ŸO¶_Ÿsø¡@¾wmæ›Õzjٷɾè< jܶ“Ó—¨^³ ‡ªëFQá—ïû7`ûûE?®±DeßøþjÑsh&Oq¸µm½ØªÔiéá?ñT¾žüâ—ˆ Ï#—ïædl¾DÑ=~îSj4…YXhÙIÿ/ýŽ©¤sÍlûÍüþós‚â{F[ۼǪÔÙå½hÿ"Ù"Utc§à<º¿1¥gY¿Kt}µëòˆÑô h™^‘¯eþ9Õ¾]6q*¿Úζ/Ç¿Gaû~¢u¾Ñ‘ᅳàÛ˜t⟿½ þö‚úwø¤lrÿ¿cŠp¯Ðˆ:Á÷®¸˜mïËî(ùA¤œÄßó÷üWøA¹I¾ì¿——óG9‰6Û>'‘çc뫊â7ò±õÀBÒ PBPàQÅJ ) ÉǶÏIüg}ÙíórLR>v,°´*š“hï£ÿ«< î ÅsbÿÈ›gæèþBV"ÏÌÑI=ø÷ø÷øï¥ßÁ(Ü 4bÈö™ÎRVl ps+šEfïjúU^¢è+Ä«ýüy¾…ñOf’Ùç[}'üùOûþžÿýÏÿœ¥ŸÑ$|ÖhÄà,eÆÚ{";—ó²R6™X€jnhÖX©aÿyÙrÉù÷2.þ(›ìÏfÆÚg“ñÌØàÁÄJ¢ üÌØà!é€#ĤF ‡¨â„¥&»ÌXûl²ÖÙ>ãÂ,eÆÆÇ_e“i€( ØÄ_yÂóÌlžÉøGþž<ç"á/ä“ñœ ƒøçï1ðï1ðß5Ê@ƒg›û¯òÍ bÕH¾ðöùŒJ`npì?à§Ìs1þlΙ}.†ÎAüó÷ø÷øï]¥Ÿ¡Pø,ш‰@&eÔ4e"•³·MRÖY¬Ô¤ ¸£YuÀñ_”¿íæŽÿƒlŒ?Ê:û³µöYg<£ÖäB¦#p„hT¿‘Qkr)8ALj`ÞUprA¡]F­}֙Ėd¸!` O\!>-°%Dhnb¬$ÆÀ_ecX¤ŒÚàô«¬3-°%«ÿ•§<ÏææYzà !k(!hpƒ¨cí|ÞÿBÞ÷yúVøóŽÿ cßï{ÿ?dcü³ãë„qÎ>ƒmaâ˜f?žýwŒeÂ=IÎh¦0P|ÐT À L‰374Y¬Ôh ¸£átÀM§F GóÅ'Œ]j`Þ»€šR LÀÍ™œÑ ê?ÈÁø½<³?“GkŸeÆóhÑè*Üëþv&­# F ‡âÄ &à Q$çFb.­}Ž™ " …ÀbI2FÌ@áè+Ä£ „ˆ ÀÍ.ÿBÈ03Ùå—é«]c Ä•Ü!03P`,ÒWˆM ,@ Ñìr.þl^™}Î…ÎAüó÷¼ìïyÙ¿k^æ.Õ0 ŸQ\¥ Z3P )õ%Ä|m!£±PÊ,ÓG4© ÍœÿñœmÍïdmû ¹€\ LÀžœÑìaÿ@fÙŸÍ µÏ,ã´&à ÁÄ'ˆFý´&à !%gˆ) ˆ*È , 0ÛeÐÚg–) 6=p…à´À”ž¸A|±’A p‡uÀbTàQê$a ´‰ÀùW™e±’XÈ Z 0KùÛö™à!ÇJb)ÀM£“„(åÐþÙÜ2ž·cÍËsø÷¸mÿqãŸýØ÷{ãÞÒ˜÷_5Îý+Æ8á3Nî5šLÌ@fK24œ˜—œÐ| ¸£ uÀ¨F GCÆ'4¥˜€7š38£AÃ@!ðA£:£QÃ@!ðAÃ&š6 ÍœÐÀj`Þhäø?È.û³9´öÙe<ƒÖ "0ùoäÏ:Aj`ÞHp†HÂ@!ðXí²gí3Ë\! 0D¤®’X€‚27ˆ*VV HîX¬$²@)·LîöK^™;kn˜í²…,m09„¨ „ À ¢Œ•„Ü ÎØ¿W&|óOH½Ðý=û{çðïŸÇyK×°ŸÑܤìY P¢) %Ĭm!ŸÑ ähÐxà„&UðF³&üɼmíïäm+Ð܉À  =ÈÐì`ÞhúàŒÆ…ÀHü Ù³ „H vÙ³…À‚IÎMØodÏ)È & 0D¥®–Xþ’=ë‘ÅJBSpƒàb%Ñ‚àñé€#¨F ‡ãĨF ‡(㣔=«24 U!VH®­X¤ìmûÌÆà!ë€#ĬF ‡¨ã#„­úƒEp„àUÀä~<œRÕq÷ß1þýÕõµÿŸÆ½ÿŽ1ï·Æ»?»ööÏŒqÂg¥î'šL ,@‰fÓW4œX€§¾Sæ3908¡ ÕÀ¼ÑŒ À  3ÈМ` Œk‰@†fÕ3P iõÀkÞhÞàŒ…Àì„FVðFC'g4uØ_ÈžÕW4¾ÚåÏ:Cj`Þ¿‘?›œ!Ž0P| ’D ƒP4À ŒÞ.Ö"dkcL3﨑EÛ $pƒ˜b%A‚àaé€#Ä¥F ‡ÈtÀc™ ¤Hù³P¼!¾à ¦wˆ0X€bÔ gG Hð–DR€;ĩލ ¤wÕ”«¸A°±’hA p‡xuÀV#CÈñÏãþcƳÿÍó8…ôßMR€;š1VjÈ@RBÌÙÖ ðFƒ&g4i(>hÖÄ?™µû;YÛJ 24¸˜®®hv-(>húD Cãk€( =A` ˆA\!-°%„anG¬$@\!-0“d˜âÑWþ;` I\!&-°%DenV¬$®@Ü!2p„ÐA p‡àtÀ¢S#C|ñÀ Tð†€3ÄLÀ¢LN¦ €+ªf €PãĪFàÑÆJ²· Ö#CÈñÀ bVð†¨€„­)À×Gˆ\Œ@±Ç'^ LÀÂO‚SW‰}*µ_‘1ðß±GúŸ0æýOïìǺ0‡ÿþù›ð»„û…投ÌÜÐd±R£ ßÃXfÞhºàŒÆ …À ˜dhB 0šQ\1Ži(1Žé+šT ,@‰f5{ hØBàƒ¦M24®˜ œœÑÄa ø ™ ­…Àdhn 0š\\ÑèZ`J4¼¸¡éc(Ðü24(>A"A` þA` ˆC\!-°%„bn¿b¥‰B HnO¬$ @Ü!$p„˜TÀäU]"AxF ‡øt@ „³a¡0†AˆŽ¢ ‚ŒN¥\g Hî©8B¨*`r68A´j`Þoýþ{ëoí?ðs!)Tt?Â(:.XÿDIÿ¬ü¡@¾°üYw‡ž±åiz”hÐjÒ¨<›¿‚w ÊaK©K6ŸÍ‰.G«ï÷ó«\¶›e|û«ûݘʸ¿¡P' uÚ?©hÞºè mÎÜt¶ _•j»]¹âàEz~´ßäãûÆÐOÝ «}"X3«±çT&ú´5µù1s_$î+ÂýçŠäÌ N½Þ½+uß}†ŽWþ¸úT<:v¤Øý=Ê‹ÔqÜœì¾Ñc(ÆÑ÷ƒ,2‚U¨ùºq¢b*ãþ'<7‡ûœzÔ™k ´:C÷|l]?2k9}3¾ÒEšØÈñÀ«ÑTeB­¹[D°Ù]žç~×|*ãù®ÜÇšûüpŸsžw$\ß„ëO°Æz%‘˜;•G´ìóoûœ1о];T­3š<|µö.®õªWþÖ *Æ}–yÞÿ¼øßÜ'Gôý®&vÀÝùÓw–ôK¢‘‡uð(È%ͦuOhþçÛ}Si¹¨ê²)‚;÷öVJÔd&ú‘6%ž7Ä}X¸/÷w³Ï‘¡Î…tÃÞCË“hDº‹„ ¹´deË0Vª 9¼aèŠr*1üMñЯ*¹³Z牌û¾óÑï5Ó“ûçsß:{q9êlìH3“è“Ô/^zìÌ¥>•¯2î_ ¶Óo-̨@V{ñ^R.øÆ}¹_Ÿè÷tÇóqǹ‰>ó¤|¦NE|敨SVh#—dz×vE¿2—Ê¥O+?yÛjµ #0ïãÚTðe­ôûZ¦0 «á[ë+&æ´t²ùÌÿ:Rô«õ²ùèXuƒ:­¸~4rd2Éz´ùÁšK· ásë+/жr'ß@õ§~»­Òp-ëàWwËɶ¼QîÈ?7žk|»Â_ӘδúîÃ6owˆ~/:Ô©8å Ã[“©Ø¤ô½e'äÒ«ÊlT…O.ÐÎ3íÊ׫›ðæEÆ7Õ5Ï9ã¾YÜï…û™û‹Ydd*}¼:ð·´¬Tû:Ó’ë7å‚Nrqèß`bk©?xnòÍJܽÑÙ»O§<9wJ´Ó¹\°µ¯u,¥-è•“Süí)q¸„Ò!–4½JÑ 5ù„«µówi™Ñ>ßcã¹<܇ûJsÿ,¯;_'6³§:Ëlé}IòD;'½Zöï:8–6¹¤Ê.¾ºKJc@C ]oâ^õ‘“§äg§eÍY=‰XèÅx>ï‡yÿÉ}æ¸_¨ù÷¦A;^S7ÜY8;–JÐò¼r—*]Y:½aX ­NÔ8Ý9áNý÷¿rê–¥Ô²Û~¡«šqß?Þqß7Þò¼#>^õƒvæÖ=é9z[¬” u—ŠŽí´bkûÒ…”¾÷²¬ ©¾7ÏjÙÝF5÷ýœªf<çjqß,þ¹ø÷Ê}âŒúA;=Ö–n‘`ˆ%c¬Øª»´¦xµ°.¯.P¥Eä3#]h¥ aS×kÙ·'6ÊŠÕŒçpðÜ,>~ò~›ûŒóœB£~ÐNØôÉÕK§Ç’BùééEß»$úÃ] ýã-ûw&ÑÖMË‚ e}ú¾Þ“ñºåzäíðÏaÔKf¦2§\óú˳b©î‡M‰^îÒowV¯s^[tf]?g ìûÙjphë3½€]Ëñnl†CsåÖâƒiæ³Í-÷õõ€çˆþ”¨Ó’B°ô]’ ÏYõö|ôôÙ(gZ×íLúµ !ì¤þé!73úKVs¥¿ÕNÕ=p¥¼y‚y?¿Ïo^¯a…ã%âhä¬V©+>ß!Ö¬V»¦×ΓÊ-:{J¼3}q±>îcä5’©¦–ÙT¼Ÿ =¼Ú<³êWÓøËs]¸±þñ|1¿3ŽFÕÙWþÆzØùÈ‹ûAç)tÈÊQe}\hÑ£°¶Ÿ›±u…@¡‘ì¨Ïð€Km‰ûyñçsŸ*c½ã¹/B„€È8\ù«GÝ¡èSgFl§óT¤«çã«]\iJlìÀK‚¤\zgÓ¼„ç"ñºàヹ¯sž² OüÏ8º3pö°°;$úŸ#»§7½½[¹Ñ·¥}¡¨`¶Ö¦ËsßîŒçp?.Þóù#÷5÷Õ£žÚÞ‡¶MŽ#ß]tô¼CâxŒù£ÓèN÷ìÜÉ{\7y™f¡Ò<Ó‹q?vîÛÈû9žáÜbˆ¼ñî–ÄóØuŽvž?X(3$ŽÚ¬êî\¥ûZÙlëò=ÏQXpY§û_Ü)å@—Ä{B™è'¨6ùƒóÜ5îÛÇ}»x~™y>¥EV¦rDöVŒhqÔvBœW`ã;TñÖ™¤¯¯õRnƒuIé¨éîÊîÏ(µô»­‡)—ûäòçsŸ8žÃ%Žóâ|N†væŽ^ôsZ¡x2~Ü whþ‚bU:®ÖS‰Ÿ*Yýt§Ë‘ŸN eÇÎ:¨“N¸1î?Îçùób¸ž¹ï¹íîµÓ«Å“ÃUú‰O·)³ÅíãHO+uš_½Üi 1@:ŒußÐÿpêS7Æ}îø|žûjòzãyBæã¸ í\nÓvgCÛxÚ«lZ´ìµÛÄnuÖxÞ`”h±×ßb¿ +šZ´áÖ¥,w×à%‡»™|5¹¯*ï¿y¾Ÿ¸®ig>¯A;–Þ>ß?ž¶”?êçÛ4íVסå[1ª}¼p@ú‹ Cç8æ²”‰¹T®¦|_¾Nàë#Ñ7¾u?÷<_Ì?Œ§1½G·[v›ZÇœ·æ,uí8!뜽+eÔ[yÖjJ;Qu¢>s±3ËܪçPÿvÄçÓ<‡„û÷qßužghÔ Ú™n lˆ§¦3;6%ŸÛTQîcWûç:QaYÑQß\è—I'¾êÆDŸDšO»šÖ£\ßÜß–÷›ü=HrQ'hGH±Y3Ð@­ú¥ù·ˆ»Eþ»7·­´û$%ö›YÕû„šzîyómkáP6ðC“e¯¸0îk(æÙÏ!å>¡|Ü2ÏuÖ ïiõèÑ0­Oܪ Þz‹†ƒNЮANý3í¼Èmù²#º]¡ÌåÖÅ:»\¤üè–$̾ܽ[˜Ö <_‹Ï«óøÑ¢yZé;•Ý ´¹Àësn‘8/=AŸÎuu¹¸KMú¿yô=:TÊós‘r‚:щ ØI%š´&îÛÊ}!EŸJežu¶íˆùyú¹èû&§[twÈqvNÑt-7`ØòŽž4{~Œï'‹PV®ªðM9KyÛƒiÓܧgõ%žÛ3mþ²·¯ë$ž`¾NHêMÈ…9f íΖÙÐòäúxºã85ÿºèÚŠ®îtߢåƒsBØàÝ· í׫XÂÁÚ. \©Xƒ¶AûZ»‘}§vƒ7–•|(s ÁþyÁ@âëJ£nžg*…ôQë+úåñÀÆ‹ß"—–ÕYŸÂÇ)®òü+å®t¡ÜöA/Õ!Ì0R+ùPruúÑÊìF;Jû¨øÕrØúH\Oô%ž_eÔÚ©ã4ÄŽ]3H9|it²ñ‡”þSŽÑâùg×všîL%U‰úMC™Ê¯~ÂÔ¹cMþ Ü–¯ܪî]pæ )_£ôy¤|´£48@§1Y'¤Q‰ãuÆ~q”Šõ¬çž1ZE7>ûÁ9”¯¾iL£$•ɇZôÑmnª7Sž”fž¦B;ÃVgÙ±L kî­XF 3g-cÞGi\½–Û~Œ¥_…x¶r!lËÇ‘–waµ+4º”1ÙžD=µ0åàñºæyæy•´•Ø×ë{Óš¶ÞçàÅ‘it.¡O±çQTyËÜ »«(w{…â;×-acK;Vó®è"­GÄçbß3åUòyy¾GÚ‰ïå|ÿqß:º£ÌÛYò4º[ 4ÐÚ=Šžž8[¶Xqg²–YU³]¾B"³Š‰yzƒ¥\Gây~|eÔ ž;°Ü®ã§'бŠ!EÓ¨¿Ð½¤¡]÷ÝÜ»×r¡%þ¾¿ßÀNŒŸvyÀäQ¬éÁQ“ÖïV‘PµË¸˜|íù¼‰çÝ™ç륣ßVe>œÒ%P]3rüïÞ¤I½_—ìz„wè•5Ä•†–VLy7@òÑÎlÖÓ qÜìeòkÍëK-'î#oÔË‹Le-ãD%lS‹œ¾¨»I%¯ŸÙ½í0¹<ê^)jžõ?qþÔ¨˜ÅLô/aÊqã¾Ç|Ÿ@œoȉçu‚çG=Ö[gŸHâþçM2´­ï}äë!&O¯þyŠ;(û"t„sÓü&C©6íCð}¾Náó}îן'‡ íT0þ…iÂÞážsÆÞ¤ñi;¾èwˆöd 1¬² þö \¥=Ál]‘Û5•R1žÁçI|>Æ}´ù>¥Qx¾#:3ŸÕ‰Taþo;ܤU{~ r\¯#G‹XÃòvԭ倴K˜˜» 2åžóu;Ÿ'ñ|>ß7ÏÙÕ ÇÃ7W_—Hýô—Õ³¼I…݋׹‘r¬º¬Ÿñi¤]ùÜ(¬ÐÁElSÊÐúËæªߟãý ßT÷ëTcbÊ;SNd} c\o@" ©¾ÏÞ ½ºÄñ9@Ÿ,î­iyÇfGT²>|2€ýœ>ñvÈ5õô+Ç›"½—ç 1Wí±É§Wüœbÿ¥G;TëËë¡øÞvýR ]ð ³ÓWe?%–-`»¦…;½¬¸·¨óÄÅLÜtf¿½RÅ( ¦#JaçhôyžIþóOâ8Ù†xNQ/hÇ{¡ÿÎ1ÇQg]÷ØŒq¿A‰.û4!‰{éóÓcOy»Iû3AÌÒµÍßœÙKyƒïg**L9|}Áç—|/ŽKb;/3• ž¾ž:'*‘JŸS¯~§T¡j)7Û%{è—Gƒ[X¸Qç´FCKŸa…ž¹\ë]GÅJÜ~’Ý«a ÓþŸ7ñöì&n+pºH‹<㥠íˆ>ü‰TªX—Úâ7hT3Û„>»Móñ7>ZÙöer64•Ea<7‘çsmžƒÊç3æ¹ÞJ´3p‡GÙ¨‚I´±ôgÏ¢9©te½ÝúÅ#©o“+—.»RË/)>‰wC˜õZÍØº½TŒ¯‹¹þļ¶ S~ÏÃ0ŸªÐÎØcõ(œDëëÁm©4£zPŽcâNR¡nôw#c|ð‡`ÖЮEÖÃ…ÕÝôÒ£õ¦‹®— ²iÞñ§†I¾ÙŠá–Ó~kðJÁóðø~”Q?h§UÀåq/Ë'ÑÚ²–ÓLH¥OZ(æì á6}ðu'§ŽÜ b—¯ÞO/éÊÄþ¤?iû½­ëÛ[ò×kÊï磉çõƒvŠt^Û#‰š^ìÒa×ðTú|úÑîžÕ¶S‘ õF\åAÎÕsÜÚ¶ bâßß™ñºåy â¾Å ÅÙåël·Î4ùƒ›ï7èÑNé˜Ãk»J’òûR©ÝÖK#÷n¥Ç †wª¼Ì“Üí¾uT;´(øîµÔѦüxq]\Ï´ÿ-®'*xN!Ÿ÷õƒvv%¹¸¼›œDŽÆ=•ZÞ{±ý¬íìù³_;5-ÏXê5¡Z°”C8Ò´ÉóIùúËF½¼ÊT>+rzß’$rtõBòóë´êç¼AÛzl¦G÷{TÜîª&aiDr°´nÅx^ÍPcàÜ”CÀs<²|R¾§BÔ ž¿&ô2zÄ$²y[ïÙº„ëôéÚí¥“zo ­'«ïÌò$176„©Ç”9õÆz ã>ãbnŸ½UŒ¬2¡[†æ±‚çkð} £NÐNÂð´keæ%QÍNBräøýŒ™°–êÒc½ÂƒWø«1Á¬ÙÔ“¶ ÚŒ•Îoþöýð\Hñ{ÊVð\sÿtÚÙ¸ù‘}¶w K©½næ”ë´¼ýÎåN¯&ú—_TÓÜHÜwf<¯”¯Ã+ßšªx¸ì•©nzÀó6x šDI¿Õò¾#¿NóVç¾jÝ%¥ žÑf )ôÛ½ƒÙ«7ã—Þ÷Ëø>5?_×oYOtoöTÁÏ;x}õ€v¾çö þR/‰zµl÷æç52 í¸n©Õrêpn[ oWgzîwQéþ-˜m¾%§›Šñq7ïùæmÅ¢“Ã/-ôÉPðs þßõ€v.§¿©r¤~¹†WˆJxxÞ\Ȭs)IãeßË]þwzâ¶êÎï¿ù~_Gò}žl¾’ŽvdQµÎö­Ýí<ê5þà5*·æê3»=ÁhbÿëM]:(†²ZAçþ2À…‰ç&•MëRžg’?7GìËŠãÉëLeÓ•}û¿*•D?3åcÇh®‘~ׂ¢û<Sm·áòœžÎTiÊŽME¶„°°}ßZX×uaaÞ:{h:Ÿû1qývÊÊ$ªr#tFϸ«$¾¯¬óšJv§.ºÓÕoÍ÷÷s fØÌÉöŽf¼Nyðù7_Wˆãó<÷ôhÇW‡U¶¿oTòÍ”r–x±5t{~õ ÎÝSNS„°ë¥+ú5Ü<æ?œñõ_ç‰ûÌöyòöÒÑ΄øë£oA=/¼3|ÁUÚ}ö]pù÷3ئ†›ŽYèA?ë|ÿ8==„íÛŸ6ÖwÀSðýþ}ññJÜWêBÍgßò½]M:Gy“©|usãëŽ9‰ô(“;8ú*½|¾9'lão,uŰÒ5=¨µJûÞ«R(k¼«Sý̘1Œç¯ðýUqù‰i>Á÷µøù¤Q?hÇíEÕóÍ“iU¯Ò,Ú\¥°¦'÷–.¼˜yÍ¢z-\Ü©Ñ/ß7„°Sól»-Ö;3~Ž Žg5(õ[—[;†½Wô;µÑâúkÓ~¢y^šíˆëD¬_jÖxÙ¯ÔUÚ5Üqj¿v+ô±í2æîþhJûIýÃåëÜMã/§Äõü{?çä뾞2êíôk:òEðBÌÇ©üþ~ ]Mõ´<¨e¢î\éÝc»¦ÏW„°âÂt¼œÚ”?ÎóZx‰ëü,Ó:ŸŸõƒvlJ%¶Z7*‘æÙ»t …²âû~¯²‚½?Ó¤a®TèõÌQC™xNîiÇx=óñ€ŸßòÜ)£nð|ûÞc6º*ÉNÞ¤â3ÿÚ¶¼}~ W±V5³^÷r¥‡xÛÕÔa¬—:éDÅaî¦çsýñ}^o‘ã~Þøy¡Q7h§‘e­÷['ÒôÚšë ú§1øËÖ³Šrã;7’Wèîpïrûzqøæ)}=˜¸QW:W®bêoø}Š ú¶˜Òƒxî‰Q7hÇѸ O¤gs„“¸š¨-Ü$|»TyDf£ÓîÔl𣧧„Jë}/Óçá9½†Q7hGÌïL$™Q ɺ!ýãØ.,˜”û{GÚ>5–°ÏrAØÞŒŸðû!|ÝÇë€ßçàõhÔ Úù\Ë-½©Æªõ‘;N$S¯ù>E*>ßÂ’û |ò³Û8êìyuçóZK؇w;±õ0íÇò{M|_¯“øù"ׯQ7h§Ë«ã¾[ë%’KÀHÅòåÉ4\9¬\½ÛX±'£®Û?GÂî›U|³yí·âÁVWÆû{žGËϵùþ2ß6ϯԠaWÞ¯d"Íöê?Æ-™Þ4¬úf®j;ì^«øBãÈ­¨p³!„•OÛ°À³‹+ãç¶|}Âó€øøÃÏ•ÌÏ_#Ðιi#Žw~•@výçÝfŸL‹>wÔ-°‹=\ÝêEó6^4ÔFHÒ aw›Ù)ÜMZW´”ö+Z˜rÈø>Ÿ—óû*Fý I=í›ú¥$Pùm™•O–O¦ÞÇdDJyÀä—]¡Xò›`¶¤Û÷{äÁÄû9Mˆßãçè\/ü^žQ7x~GfÓyÞ–ÒöýÙöÊÚ¦Ùvm÷ÃÝìr¶n]n”¥{ä¸8†0ÙÌLÇE{ÔLÌyªnÊeãçc¦{\Ò9­XÒ=±œLåÏþÞ§§%HûgW¨Ñ7×qñµö²7ëzôúQÙ•zYÐtéÊü§oÊ0Î4uXÝ´¿Äó{Åqºœ¨<ß»=4œj—;£ÉZr¼ÑÌäžûØú9e[tp¡ÆC¶¾lú4DÊŸÀx}ŠýgÓûàûeüÜ,O Ú™9¼BÉ9ÝÈPjp»r™ÚvØßf„×~vqJ]ßãßœ©‰ÝçÍßî³ù™÷/ÔŸ`š?ó÷ÌõÈ×I|ü6êÏÇ"¢ö‰Ö ´¾úµ «ý.SÂŒ…Í:`£XÎ/×Ý…·Ù·Û¶D0{XßþÃoO&˜æg<‘ÏËÄ{<Ù<§MƒvÚ夆=ª‘@¯J ‰¢—©Þ§+ÍÚGdbš+uyÙwË;¬W7w´½¾.cãóož[&Ž3oüϵãç´F õ#ŽÝ¨R<^½7½Í“KôòÚêefê˜8®»‘õá“©S¯³à2çÏN`¼ßçï…Ï3|É$=3ÝC4ÏOÓ£5ñë–u~m  ¯WÍÜ{à¥>v­a¨}ˆëá°¢óvwzzU8 aâ¼g<ãç8¼=q\ÎPd6ê›çÉ™LÇócêÇo¼zÎ åg_¢Ú%}Õ߯b3Ї—èpà ‡®6þyÛm×§Cë5㘘Ç^ßt.nïçsÎuI–iŒûæý±ÅÛLåÃãF{/4pÊYnÐ%Rî{u×;ä00¼ßÍ £=©ÐÈúXZ†°­kÚòf\\wüžžXÏâøÖÚ´¯eÔ Ú ; ¼ã _¢ã›3÷\ïy„ÍÁ¨QUãIO6 ¡æ:ë|ù²6ã×;Ïmäó>Ï4êÏmÝXH^§J?gt x~‘ºÿå\(Š žp·XìWÊ0ž`…°Ë5–°Y;Þ”—Éß§˜/ûTš'?S˜ç±ªð|!¾gh<¯n¼HË· A¬QìƒïªŸ·KzÐ5»r“6Îû›>ø:˜ßë÷)Þ)ø¾›øžZçÉÉÓ tyŠ¬È€xj;y ë:ð"-]S/@íz”5ÈOVÁº†Ü<½çH[^Ïr˜rËDÓý¾®äûâ:ൂç¯uçïtÎ /OÉe¢„—»Hº“1=g;Æm ²}œíJ©¾æ\{¿D:ÿšdÊçç©¥RØÇ®}ªÏõ3Åó<×¶YXb¿ëq¤ï<ûCNr]mtëë¦­ÇØØ&ߦ­©íJ§G¼¾XøÂ¶Ê¶á–÷>LÌmmIü¾ˆx®–ešoñs<ë´3°bV…™ËãèˆK,•’¨eVû® Ûg<×9­ì¡sç{±–×|™ùëdÆï+ò÷Éû[>oàã–1ŸxØZ¼ËT~Z6rÅÄ>q”Q{¨wË‹¬oâqöd{pä“Oèo½bÛVT3·Œ‚èþvï›ß{áã’8N]7~O2<·`©¤Í“JÆ‘ïù>>Ï %Ñ–zÇÍ‚G/ë_a¼ }<¶¶DŽC[2ëÜ®v3'3¾ï(æÈבömo˜îwòóñ^„X¯J´sµÅNN¥m‰ò×ESÉuê/ßF³×¾gZu׸RÕÉÔ+›…²]… ýdÆóÑùø'æ{Þ5Ý¿åçúæy»*´³aWû'ѱbü¶DJ‘³~¿òÛÔtö„eeÝiKÙÕ‡¶¬ežœlôôèÆû>ž›r=¥ò{.æ÷o5h'"°{©!±ž~½Øòø<µº–ÛÐí$ûQty!o¡¿þ—žíÊrÊ 7ߦ2žgο>ïåûœwß}óÚÖ¾SžsÛ´ó®Ð°™cikjBÕ ‰4ÉK{OUè뽩ÇÑÇÕ”:Rè CXOßîÛïO•Î º˜æ¥üžŸóû‹FàùvíIVÚÆÒý„]ñÇJ%Ò”¢­[ø'ŸbO\äéM“¢—Þ]7-˜} {xhsìSjüÏ—}Ú(þýu¾¾æý¾Q'h'Ùùìi‹¯14»ÿ¬¹·(ŒÞÒí<Í:ÔÖôÄ›—é9ìÙë 6H¸·w²to«•)Ÿ˜ÏGyñùªù9¡ÅûL¥ñúûÃ*cÛÓ Ü”@Oë =åvíeÒãYõ½©nóÎ¥œb=ÆÜˆ)8v²)ד¯×yq=ò{àæóEÚ lÚiÝó„šõ¨ÝH¥:&úÌ=bÓó,‹¿Ðü·rÏÕd9}Y—á¹A,¤ÂØW6—'3žËsèù~=?÷r8÷«aÕ¬6yï£aŸ€vÄóözóú¸m¥Œ9†Ø®uéIUk{ØÝéÌ:–·4…‰ý†’Äý¤¿å‡òþL¼§ÑÞ´/kÔ ÚÏb¨à‰˜eÅ~I Œj%&0Æø9ÞÙ¾ëLïÌ2 O˜’»}ª4ïJÂí¨U •¦uÏá÷ ÚS™÷Žw ÛÑ ÛµmŽ¿šC}FÅG?zd Ì©B g«aßݨ¦0nbâþÜtö­š°ëFâ|¢™õ£lÉlÓz›ŸKšïD ¤›î¹ÚÆÿaÅk ÏSUY¬gS[m-›ãíF+õ5:Vbez‡{}ækz?b^¬ ñûŽ\§³· ?{SžgžªG;b¿Nþúôù•81èÊ|Íyu_|šž­èQyV_7:U­ü'§ Ö´Èöïeu¾Œ¯×ø}3~ŽÇ÷Gy=ŠçÒýJ´Ó;ylV¹m(MøXŽzú¸Þý>MÎ1k›…^S?¹Ñ×ÕíFW®ÌÄõ²/ãó7~O“ßçuÍÇû<ã̇Leå3¿øÔñ‚tØ@7/qOsŽ=yThõqwZ°u¦Eð7Œg÷‡ÎšÐÆ×tODÜÿ®)½‡ Sÿ)öÓŠ<÷Þdh§ÇÌŒ§º¯ç©oX‡ >ÄS÷kþVožcñW^¼·)àa:Ï·þe×ÁÌq¾ŒçFóqS잘öùy½åÑÚ©ß¡MÁœ“çÉ»¹_øÇKñÔO9úõ=ùyÖÑ"¼N·,Óüâ B8`öe[-wß¹5LAü^9Ÿ¿ˆë—— Þ‰ë iÜA;GR„î<Œª~<<žâ\÷úø„g_¾³~ºÖ“²ôS–z²Dºg7ݯµP3§xkïwØÒñ¥ l–Fç(øïfø¾µùûÑ é5÷4JhžîôËîwÍ3žÆoºåÖõÕyVwQ͸íÌ“"ì5þضãö®ÇÍ Mg=V_Žò}>¾¾çë/þ»*£~ÐÎØõ¡«Â=ÏÑȺó'û<‹£QŸ")‹Ä°K+Æ•póõ O£v=ÙÈ,'|­õa†´_UÑô;/±¿9gZßó{Òü|蟙ʹG.…>+~ަþ–äzÉGЬ[û¾ŒŠan7÷=“»“ð«¬j‹ƒØ°z "Žô“ÆŸr¦}k¾¿Ã×|ŸD|µDý ?œÙ¦§27½ËÎÞG a¡ÊÓbXHЊ¡7¿¹Ò£¢sü­ë„°Š–b÷tö3­óø<†¯‹¸N»Öµ:ø¢mžý%Úy­ˆïÜRO£–òm=3ŽÚmmÞ«ú÷v³øÌþ1ã]M똇SŸ®Ôø™îðþòËøka)Ù/Mç>|ÿÇ| B;gíš ñ f$îçÇ‘õ±sO¶ôŽegÛø>Ù&s5OßzV #ëLÆï»ð}V~_…Ÿ¿Š÷nšæ9_Ò ígÒóêY*ÃÖO¼U'Ž^ìlW+mu,³ÿ±¹V›—.$ŒÎU®/f-öΈ®9‹‰ûë2SÿÅçSüóõ‚çÖ{;º÷èg©Î¡{”Ÿbée™Ë}SŲSVáJ•/¤°šÀ lKZáè,Ó}ñºdû4NñHÁûM£.ð¼¾»ûÄ5_~†Ä}¯X*ücUû -âØSIÏÏG¹&Q-û\ÈnœÝw{«Ù¦ý(ñ<§uÜã¸âÊÃl“Î:ÀsÅz=MÛj¶ý6ñd,å´©_öK`k»çÚoûvy}‹¥ßÎé°úÚò§ŽŸÍxÿÃ÷ø>´Såœ.æç˜æáÆúÿ”©”;šÓt gíËÍ»biÈúí›ödƱjc~ºu™¯¦Ùô|8k!›\«¿ªýþÙ¬Û’Õ7­¶“¾‡· ¾ŸÂÏÓÌï³ÉðüªIïûÄŒoâó ó}v%Úùa»ú—Ͻ£èÈ«¨Ö¿XÇÒ…÷g¥G'²Ó1ß_¾Lv§c^ã9³˜‰ç˳Ùò¤ylnSiaMüþ¿¿Ë< ëCÜây¨ íÈ®w/áþð¹]nù±~uÔq#e¶Z%±â«&¦ö É™çì¼³&÷F{vý1“ñû”üwÊüÞ3Ÿ×Žþ²f{‹ØŽ´ÅBüó—GÓ_Mÿ*¦ ñð9“…÷eæg¢ÉÀ…,:)×ðÏü6}$okÉW8ùO²]-óe»†Kž›ª¿#×ËÜ[Ø<×뿒朗¼Ó¹W÷N7÷Üü=ïô?òÜü{¼ÓUfÞé<Ó&¶¡y¶µ•™G½”íÅ3"¢¥l¯@ÉçÄÉÌçÄ<ÛPŸÏs3®×yÕqÏ“HIäŽR¾«¥ä]¢—²­Í=ëÂ'C⯾ð¯¾Pcñ¯ï -¥Ï‘&¼c°DAú€4 DaF+ÁÛIÊöú3Ï:!CB/ùß:óˆ?ñJRÆ«e{у]'e]ûH9²|>P¿ãÃ.Ô¤ðç_Ýþ»öƒÿÕ>ðÿ÷þïíû¬¤Z¢ð|Ìü:#€%ŠÐ¤%Š1RÊ–ø3»HɯÓQÊÏò­Õ Ø£p#€%Š×GÊ·Ž@[¢ˆ}@2£˜Ãÿ$Û:vŽy¶u¸Tô*`r¸$5HöB°„|@PJw¹Â\Pò&Îïq—ß›ø<îþÌ—˜ûÛqobž1‘?Ì<ÓÕÁÌoÝ'_ža¶”g¨—|:µf><+L+ùsŸ»ü™ÖæµÎ=:…íá’PÕÀäùòÃ„Ü {ˆ7XæË݉V³HµXCØ¥LW«ö¢g{´”mí'ùw ¹®~ 8HþŸù}Û…zþüÕþ{ö…ÿûAÞþYÿ'‘À ÅçÒ€E ¬Pˆ~ 8  uÀE©YÀÅ d(Ð@ œ¤LŠ\É«=ZÊ´öi@‰âV(`?)ÓZ‰BŽV(f?ìQÔ’i?sÇ<Ó:BÊ´Vƒd`DKÁ¤% ¬ ? ŽI j`|ŽA6p‚`ô¿ãs¬6ä„dò¿ÓçØ"‹0ó92*ò猙gÃ:šyµûåËN2­U’W» ¹@e–9. U ’'ÏúÏ|Ú­!f”k/å&f {€’¸Í=ÚÓ~'3ñ¯ùß¿wŸ÷¿eþg#ý=²„ïŨÖ(H ÈŽ(Ìh CqJ™cN(R=°A¡jA.P¡` @.eU$9ŠW+åòügÙŠ<—Ç<_QÈĶ’2±ÿ(›'ÈPø ;_>Ÿ”Ïóä+ú4³|ì\ ‚p @ñ„ƒ\ ‚ˆ @.ÿ‚˜Ô ØCTÀÂòÉÀ‹–™HJˆ-XAp~ (Íò*ò玙çÇÚ@ 8æózO–¼Þ#€%„ê’}¾Ü1!¯B ñF«Vy³ztÀbÖ€,àQG„²“”k-y¿ë¥ m”W!äÇj@p„øu¿ãÿ.Ô¨ðçŸÙ ýßg¿÷?±Ïû£þîÿǾNxo:`BÓ€tà€‚ÓkdG)CV† ÙÀ …¨6(F-È*ጙP”:`ÂÔH™Ø(P°F‘j@:P¢X#ÿ$;ÏÄŽ”ò°}@P¢È#ú2?Зé€5Š_²€#D ,! l -È*Ãl -È*þC(á’XÔ ØC4á’pÔ ØC@À"òi@ 1YBL>RnEþÌ1óÜX' dRöŽy†¢…”¡˜ ìñ#$ªÍ2Ç„Ü KˆÑ¤ýNv:p€HuÀBÕ€,àÁF™°—'eÈ*¥Šlàë¤ ' Ö´FÊÞÉŸ¡ø×¼îß«ûß:¯³—Úɾ/c4¡ A6pBaê ŠS+e©P¤ G¡†KŪÉÀE«iÀÅ.åöügy‹<·Ç (!ªH`aù4 „À"DæÒĦÖœ¤/XýN™yž¬‚Ô‚\¡_„0uÀâÔH>Jˆ4XA¨~ (ó奈W'åi›gùDIJD­6(-È*)KV‘ûHYŒB¦v H—ryA¶Ð/BüÑÀ€7vI¬Máï ÿý Ðþ+û¿ÿ‰}ß?»ßûwíó„ï>ÈPpYÀE dèçA6p’remPˆZ T(H£(Ã¥ÂTIÅ)d-Z£@5 8¢P£ Å(ef;¢h£LÈ%YÀ¬û“Ììlà„ÂÖ³Ìlôo:a ‹bOèÛtÀ…¯YÀˆ2ˆ d'ˆA¬ ?äF¸$5Hrˆ$\Š${&XB4> (!ž` ù€4 „"ÄäÒD ¬ ¬´ßÉ'ËŸ-«z`ÑæËY´”rÓ€B´„}ÌòÉ"•) Ò''; 8B¨Ñ@†>-d'Wl ^­”1ë å+æ ÷÷ æh`A«@4AØ ëw2ÿšçý{öuÿÛæyÒsr…ïŨ6(H-È*¦ÈQœáRªA2°G¡FK«HJm8HJoÈýƒ|FçËgrµeeÅŒÆlà„B×»äŠÞä(üp©ø€ØHÙÿ`Fc`¾|mKˆÆ¤%Ä ,! ”2ü;`1ùtàQé€5„¥éÀÓkˆL²€#Ä d\ ÈŽžXC|~ 8@„º|y³öd¸$Jˆ2ˆ3¤ˆT¬!T H¬XC´!Þh ƒ€A6p‚õÀbÖ‚\ ‚¨ @a‡KâVKY³6¹Ÿ”Ý(dnkA–”7«¹B¿ñë @ ÈN$Ö°ðçŸÙþw÷ÿŒþïŸyWïÿE¿'ôw¼Ÿûïìãx¿ög}™ð½è…w†B ÙÀ ¥6(*-È*—ÈQ`áR‘©A2°G±EKœ% N @†Â ÙÀ ¨6(B­”—í„bÔ¤dGfôŸäeç ÖPáoYÙŽè·¢5ŠX²€#Š9ÈPÐ 8¡°õÀÅ­¹ÂŠÜE®ö(ô`‰b÷iÀE,Qø> (!€H`øtà1D+¤C¬! ÈŽ‰XC(~ (!˜H`ÑøåË—5÷M G)Û¤)XAL~ 8 oÒk!KdµüyØÙÀ ‚ÓˆN r â39ÞN “ð» ˆÐ"Ô‚là1Fá×÷ÍËþš—Yü{ÌËœ¤ÿPˆj`rd¸T”j ìQœÀêÒ€… ¬P¬~ 8 h#@p@ñFJ¬ G!‡KŬÉÀE.¶ZÊͶAkA.P¡Ð @Žb— ^ ’= ?X¢øUÀää'ˆAl -È*ÃäG¸$5HöŠÖ,;[¬ ? °‚€ü@:páßkˆI²€#D dV ÈŽX4Ad 8Alz`ÁiA6p‚ð¢ âÓ€,àFkQ²€‚Œ–¥è Ä©YÀ"25dG6È Ú@ œ ^=°€µ ¨ dCÌá’ Õ ØCØÀâöiR&¶¤I™Úá 8äËÅ6tZ T$Ö¶T‚ÿÔ¾÷ƒÿ7}à¿b>öŸÍÅþÕýœy÷¯˜™ÏÅÌû-áó„w€‚Ñ‚\ Bá€Å.${R°D1ù€4 DQE+–X¡¸|€ L r…> …fr[¸Tp*`rž OlP|Z T(B£Ã¥bTƒdáœE™+¬Ñ/é ² UlÐ/iA.P¡p @Žâ — X†Jq$°B!û%Š9X¡ ý@:p@aë€5Š[²€#Š\¬Ñi@pD? d(þ@ œ ‚h ƒ4 8@:` Qh@:p€8tÂ]$È!’là¡XC(!°†h4 8¢ï‰2ˆ(d'ˆIl (-È*ËäW¸$05HöZP ¿ß‚Ðäš„¦B’ˆùï¿øoa-¤; ÉR_!›šÿî+òwîèH‰qßëøÕv3û ÷$ž[^ý7¿ÑLµXò±mG[š¯ˆ¬2gã~éB;hg¶Ñ˜ë]lÂ+ÇR‰âg¶KJb5éàµm…Õô#gÌÑ3…XÕš0‡qÿ"îÿÄ}Ó¸O‰øÏ6y|õhçÃéê%K,8LcK=ívªD,9{<ÜÕö"ûbu°GÊ5©»¬ˆY>e1+þëÊ>«ø›ò¸_ ÷Wá>ò+?Þ·µ)ÇJh'í ŸèÕºÞëC´öš{óCŸc¨EÛsʇ]d§ž$Ì[ÞWM…h3k¸ˆ}êP±È—nþŒû¼‰í56ù¹p=îçÈ}OŒ/ès¦rf¡‹sŠÑ!Úæ·êh¿œZٻ撎¯/²Ï[÷ Ëñ¤šǹVëžepý\&æ©T3ù;ñœ%Ñä©)ÿÎÜ·V†v®Ý-áÜzµŽRë´½P:7†úOí¨kÚïÓœ©Ùq⯞´,û ë<;€‰yCsLß÷Ïà~pÜRôm™'oM‰vVqUÕŒ?H­£ ¤i,c©ã4ùÀ©‘—Ø‹%¿º˜áa|û5-bý›°bs÷•å¾¢/Ó]Ïõá>ïæ¹A*´c´©{€–]4hv¹Xz9ëH•ê…/³îS²k/xP£å rYÌİ9&¿_ñï[Éä7Æó#y®¹ží4ÞÏê[üÜGe_¹a¥céd˜“Ç\fU£^´ëÖÖƒr4‡ ó[Ì~Ù°;IÌÕŠ¥˜aéÉ…’Ù÷˜Qû':z1f«V »1UèIüM9UÜ—7¯~’¢_0I~Ä’ß?Ú™îÛ¾÷˜äþÝÃå¤U,½¥ìêæÉ,¾‡lÌM;µä;º˜µ>ܤiÝþ&¿'î Çýþ¹O“èÔ.œ íìkóó`û)ÛÉjZÔžú5cé{\ §‹®É¬EPнÇ9^Ô{ýžÙnSØ-œÛç1Ѩñ þ}½ôx°ë[ºBÌioêÏúA;?N^ÛØpÓ6 +tpu‡–±ôµ°àšÌªÔ;2kñ8êU¶ëÞ-2Ë}5F¸u˜Ç¸¯8÷9ä¾^ý—»R£CãfŸª¿„íK?^'nœ¿iÞÇ?׎†¾þëË~–ü»Þ›úOÑŸOô×Õ£³Þîçí>¬ š…kî÷îKvž#§üæs•õÉ}Sòó!W2ÆßT dbΆ?óþæ;ÏýG¹o#Ì}þÒÑŽ0»ù&_NÛí{Ö¬‰÷cÌåYs•Ö•JMu¥ïgç½{³˜ ®t#Jù›êšç¡r?gî{Ëç9æyß2•®ÿ‡½óŠ*kú>f̘1£(&̘¦ÇŒ (ˆ#YV$¨ÅŒ®1¬\‰¢‚$1Š"¸ @W×·ïÜÛwŸÝõ©­¯jß÷+·ê_[ånÝvfºÏéîsnÿ@¹Øû¢Ï;ëDXi”<úËd}š<ûÐu)x~>`râ–'‹ë'³»ãÊ(">q¿É¿çÞøÛüvºµ¸òhgÞÓÇÜ/úC›%anÞ‰p,¡ñ Yi& ú¡ª¿ÕÎ¥p[+>êÓ‡í?ËU\§ù9½jÂ>šøsЕçøJÑN¥ZÒì'¾ðF¿Ù³s‰Ð°Ðø‹VYLûèl î¶;¶U¶úî=äï*Î!¥ü‚æNŠæ¸ñó[y®… íðó+w€ç‡ªº/$ É[n“Ånfø÷Ý»Á.ôþ©çã¥[ÏOre4‘ŸØKÈÛ> ëŽ\¢;D‡-†ÔâZ¸£±ŠD× 4²_öëåDpVe²˜Ÿ`l ˜D¶ï·=Šï9¯›«¸îÐü>šCJsi¾?ÍVÄÚ‰1~væj‹mðS¹áÌ©¹‰p±ÅçÔ»Y¬Ã/‰%Y×ñ÷QÌÙõdn­0ró]ª¿hN,Í£ù×Ê|Þx´ÓlƒÇð‘.ðÛ®šÇß'ÂøyñžæW³Xuõ’£ÚEKàC¸Ö£L1þ®³# Í©§¼€8'4¿•çêðó[åhgÃó8¸Ãá{]î¶M‚‹AF.Š²Ø±{*´/©¾WöXø°¤_ÔFºŽÝÂhîqmÎp¦„æúmt¹¨¿$tHíýçós)Ï+vŽ~ÒZ/ šOåÕ0›ÝêwðìT+8¾7­ùöìƒÚ|?‹æîŒæêÒÜhšKóh©©U÷ žµI˜GŸŸÖÔE;ͬ«:$X“#>ôåÍx|¢;£9ÍTgQ}%î BÞ¡Ì—¢ÏGÖfÕ^•Ú{*¶&Ar£â¢â™Ùl–¤øSÕK8<éò›ÏÙ^¬æ£iH§C[Xƒx‡†Í?¹ŒÄi!. ý~ЏÁçó|pcè97kÀñ°$ˆ î¥{Î.›Õ‹:n¼ÖÑ p@Qo¦i{»WYƒ-"gˆø#üç(“Œx­µ¸h‡_g±‘2ã]'Nãï¿~ ^ßÝÙìæš:-Œ¬ ¸‡ïÁ¤kÞìþq°æ&æµ¹sÙ’Ýã¦Ê%,G‰q¥ˆ´ÃsŠ×±_³*C¸•=JmL¿šÍYŒª¶[ƒ.«=ØêÍ®ÆoÖ³ÚÂh^.ÍYüz¾?å[4OT7h‡çe»°Qæ!' îªööèó$›•Œ\µ*~5ðs{½Y«93p©ÚÂhž1Õ47’8cÄ#SÄ >ÿá(ý¼}ꬃG—ÝzYIÐö`å§-rØ#o[]'8ûã×¼{x³¬‡C/ß÷pùiô»PžN\H>YkΦÊïÏ¥"–](Ã||±ù©Ìö÷“À¥K‡îQcrØ©7tµ–ëùêbŸ÷^Ì:Ñn“J‚«È5£õ*9xÇàøS¯%<—ä˜ßÖ9Þ¬KFæ0>^ÐÎS[ŽÄéÅîìy¨ÿó$øùá>ë+sØÏÓ½l=–AõO*…« ¼Xõ¹ÓCu¸2šsLþÊç5õ®fµøûÓ¼ME¼ Å8òƒ>¬_óO‘Û?' ¼Ö¶7opºÊÈmض´7ãy`X¿_ÎÊõ’Zï›ín+ƒ/’Ý:ÇšÝx/æŸÊ|Ú®þ#}ý¿N'Ä¿–x%å°õ/ýŸ¹µ߸q.z³ü;óº®âÂÀÏùMg­Qo„Øà÷¡J‘?T‹kŽvOî‘zº =}äÌ´žÉðó6fWó*‡mïíØøäð%p³êù#c;X¿wýTK\]>û@qÎ)ù3Í'¥ýS™ëvnæ®R #,÷Y颕 ½ÊN•lh“Ë^n7Dâa <Ô‡µ>8úÆïå.Œ8ÃÄù #> Ï-ĉUÄ ÚYÿ[É®v7÷°¢FÏç%ë&C·Á% ‡å²÷óú¹ÜC“›à–æË¶¾m;râcqÞ>ͧùÀÄ7Ž*ÙS¾zÈ$ n‰"~Ðζ›ç¼a•×DÄN†žLkåü\v÷Êì< °YUj³%—ñü@F\.êR]E\àßûl©ÞÙiO?_žK9LL`Óœùs½1Ë“¡ýz‡õ¹lÃòO¿ôjù{7šõìàË»qŸÌ™%ú²ïßDŽáýÏמ^ð^²´ó¢iuË%ÑUi2_ƒ‰@þ ˆ´ókL‘}Vÿ}Œ[‚v%Ã*£žï&æ²èSMGï[ :}ªz½ØÁ~Ù6Æ£³Ž3£|œö/þûú£söj‹œÝKgŠõƒ"~ÐΦ]êõZ„3~¾z2H›”¦§œÏe|H™k7ºëÍ–M†N³ÂœÅ¼êMÚ7i.9ŸHjå¹²/Ä-9ÀîØs »dÈ©²P{™•ËÆ¯Éˆlbl vŠ©7;ÿQ³Y½`Fë¯ØÏò[~Ëö·¯úlhGk2`ç "Û ž7©Vç²M>¤TçZ‚ã"&±Ê‡©ßÛ<#+£}˜úÚ´^SÞNõr2‚û}Zè44zˆÍkØÂöE“HëÜ~aÎ÷Yàý‚ºK'Xý㊸Á~lžs«ÕÉÙ®ŒòâsQÞÉÏõ)yU¶`éFéx¡¯+pÑŽßdÉ9‡²C¬Í–¶›žuKŒð¸˜R¸Ï†ëjõ~ãfq•¯RLVï×QêçÑ\p~^}°ÿTHš™¬oçS8xN½”´3M:]é0ól·æƒÁ´Ð^öã‰ò¥÷Y#Ãp“Jc+˜¦qù±íu¦Ÿµªtf<7U¨Žæ×› õ«i?ñqñ£R*}ai»iýöÃìꛄ —ÝSàQlŽzGïûÌRçñ˜îç,áíãñÆ= ýØÖ}¾º7cœÅ|šêò3âÑR‡ösEü ËÇ{M>Ââ´äK¢R ÇÛùÛ~ºÏöOié§VÎø?lõcüÜn±NþÆï£Yš{Nùœr_JŠv®8ÇÎÞY÷( âðUOSàœÆúƒ7Òî³sïmv¼vZ 9Ò7#íãv²³-ù£ÏF|KêGPž¥ü}Éðù³Sî>Ê;Êø>^*¸Î0=á^vŸí$¹mqyœ²É+}èÏn|1Â%ÀQú'TPžKüËZÅúκÔÞæP*´O™òì¥CÛÛ3þÅ¢Ÿƒ{Vœ¬ñgfŽ©_ž¾vêŽBŸV( õ†8oÊë™Úyq?wö—…?±bGx%UÍï§¹7uÞ8¯í°ÃS®£º `í«»‡µÿ£ïÇ=Åõ™òhâH+û™íœ;?}þÚæ§Ù(©ÞÿÂT°7î74éZÛÙ)&2×™Ó>’û¢Ÿ½Þï×lñÜ€ÎňOë4Íg¯ÅD; øÓüñ4+i±/åvƒ[0Ù°üe§'y,¿¸oZ7VPï…‡ÖeÍÎÆùRâÄøxì/r0(_§>ñøþ‹67h‡ï'œfÕ:ZëÞ‚÷»ä6iü€½ˆÛ½IÓ% ééµo^j€°¾81Êûi þ7ñ=ˆK¯üy"ÐÎÏi“\úØœa©Ÿ0à»wÔ¢Nðq+( ö?zÔ3µ›÷¢Ïþ|'6óÚšVêå}ENqˆ{Lç=Êu{<Ú›5Ý×úã–íÑÁµÁò[ÐksëËç=`®Gw<ÐÎf_ÐØ]È~KUïn3Йñùí . _·W‰ùù£2_NŽvøó»³l@½»s£÷ß׆ÙûælxÀ¢ûhµMÖ²€)ŠÆrS¸'gæ±ÓèÒÑ#c€ê]â¬SÞFçÄgQÄOÝRéüÄù/;ÇFrí¨Ø[°4ÅhÌ^ÿ÷ Lº=‘Ò#ˆUøL_ð°ØIä ?P_šúº´)ó]4ÐÎÄUQvAÏÎ1ÇnÃ/=º¥›œv«ô}Yu¾L ¾8Š™™fã¹ÑNŒ8Ä“##~åÙÄ;QÄÚ±ö¾;ØÇ0ŠÍ9fÓòbÝ4¸£]ÙöN·'pÄvàK§ ÆÍáÊÕƒXy‰ýÞÛÎBÞ0^¨«G ýéçªã©> Þ§"~ÐŽSBiì¶³QÌ·úÉû´>ip<>¦‰z›'ÀQ¶ÛenÝý‰¿²ÇËö„e~tfC¼Z¾¬hÄÁ <—¯Kª$ŠxÁç¾¹·á¤7Ql”ñîÖ Ò ³Íhv§Ñ¸:•.t›¦Á±¼@¦ÀðutêÜ @ç\Ô'æ÷éZè[oƒïu™Sp1ŒOè0bË"c˜'©¿àˆñ.6®icõE.ŒÎÛùsƒÿÁI¡õ”?ã9ÛñhgõèÕ~ªIçÙóy.ÜE=µ£T% 1å1Ó7Oö/ÛÅœ31_£~¿þ'‰û4Ý›Qök9ÚÑdn^ÍÛG³í*ò¼ ÏoƒiɼV1[ŠÁïÊ‹}~ Ào\Ÿ­-®îfyo#"Úžqa”¿P>Ny4ÕŸÄŸ%.•"~ê—JyŽi4›±Jr¶y»;°eÛí=ã6Ga4Ú;f÷ ]aÓȱ¡õ€öiêó|ä1/¤s Eü ×©7ÇŸYÍì,ö׬˜rš>g§ß­(÷Ôûç¶­˜3¯?x–ôv7k_hX³ª«‹x€î+W†êê{ñq¾%E;1Ï?dMÞÍ®ÿxPßÝí(xe²b¸§û¢Ã#Ó0`Þ.yóå»YWûiÊ¿PühŠÜ2þw)ꂾµ8|2|þ¿Ò›ü¢…zï¼Ø^çø²ÅPVüH6³‰ (¾®»…þŠ›Ð 6ìOw)žçÑþÌŸO 'ˆv:É=V-¼Íø<ù.$Çi.¹4²~¸æðN~t1̲XZ×Ío7û´Àýõðµ.ŒÖy:Ç¡¾ù1ÝóRÄ >úÈÅ/¯ÕD³gÜ7yÖ]:Ù®ŽN1Æ“dÞüÖ°yÍCß,µ=Œ??pyÛäWtn#ÖƒŠ>ëÐZq/G;'ž­ÿñP‹ Là,—KGîBL²ýݸöÅš07õX° ^7;;zc»9ß,ÐA¬éü”êY:‡ .9ÕÕŠ8iP*ýøÊ(¸´ß¦»àðËþ%w¡ô£¤K¯úÅ0qNÄì§S­€ç³w3¸›‡öŒ¸Wê/·¯ö³ëè—AÕ2Õ×EE°uåÔÃUMC‹ø†½ú†°ýÇÓ[Äœu`×2²`Žú&9ŸÆ?8füVà$÷ê@þsÈÐÎMÝ‚”1UXâúé?õX—éû÷ÚÍÏ*‚GWŽ^4èe;7+NÌíT“æ ÑŽŒÎ¯ˆëK÷2÷½ ?Ù#î¥X7÷N/hǨrò³@¸È¤µfþx8îè†I*–¾oÇ|àã5X¸÷âÌ´u¢TŽ7ë"æ+t.ÈûÛCñ¼SyŒ@;¸¦Ú—˜óv»ÛÑ}2 C³¼]Üá"0µ°èPg!”qÇûËC˜IŠyÇù]­ƒÄ×㟛:÷¢}Œïƒ´åã¦!ÖgÖÃÍÞx‰¥_¯Ò;):MÿlY±§65xäÕ'n!ðõNóªkÝÜE<'¢>ÕeüúV$¡{Et¯ˆ´“›”×xàKlv}Žd˜Á6'ªô*‚gÏ:ß c>¼Í ®Â6^)?¦S×E<_§þß·%r$éüV¹Î¢Yã_4íñü[ßqİ ´ó)̧±s´2È*{§¿¸U&4"˜oè“·%ܙѹmí~\¾ð½=ÖÚç2´SqceÈð.—YVþ¦áOf@Éø’Åi+‹`zJ£±û,†_¸ÿÌ.OáˆàNŒÎÍé¾åµÔ&>·rýìŽv†´+tp¼Ì†Ÿi[76!º'¾6¿kV½–]hj)ƒ;›·IL’‚YF¸ì˦5NŒö[>^Ûˆë'õiŸS>Çàü@[{RßêËlë»--Þ”dÀäÏÚ{z´s};fÍY„_­Zx Q{¸ií$+'±ÏMþD}ò7¾/þ~ŠøA;On¾´~´ü ;ì´RöSý{`ZõWŸQE0̽õÇ·CdÂ}Ò6ßÎaÜÄðDÉ¿éûâÏ K$"çX©?(G;çõ&ÎZœ~…™êÔ_ï­y†>²»¥[ñ‹Þ—ϲ·Íüt”ô`&1Ý?èvc'±ÏI|aê;Óù:¯H¿Ö}_•F¥Ò ¯º7½×#†Íµ\Õ#eÜ=(¾³iSÏ.EPe¸r¯½9Ä—V=³;ÌÔzœÝmˆ“È{¥ü‹î}Ó=\Úg•ó ´óF¥ý‡wb˜yIã­áV÷`ô€·™-Š EƒžNªa¦ã¬{blï±.¤<¿ö9ûC1N©ÿ¨|¯GŠvÒqàûf°š»yuš…Dn©Sü½Yàï{†Š÷¾é>/J]¼Kù qe•ïÈÐ΄zçW= ˆÎóïAàêôbçwr°Wuåž™1¸_‘z'‡²™Ss‹Juÿ87&2Ýã ó騉k›ôVë<ßíœôâòöôÇwN¥ßƒ)ªe¯UÊäЬTþÖÛôšßœ’°;”M½ÞÚÀ{‘³XÒsè<…ê(ºçI÷Ùñƒv¤[cÖ¿¿Æ5R}VZyôFç¬LÈ—ÃS«óUv'‚JdqRÿg!,µÁ²ŠjGFý+>ï í›7‡WuàÕ²ežg?VI¸ÝÃ\_ ÄkWÄÚÑRä1lPÆ¥2¡g¢Š·V†Ìv?ÐØåkŽŠqS¤†ŽbþLÑÕ³‡uw¹S-Ñ’F>»3æ­„Îy•ó)9Úýƒ£Œtb™"mÓÏ„·ÒNõ›ÝƒWž¬¨×Å [¼1èÌ€–?îåŒCoœÄþ:åå´Ðy!­oÊûœŠj©´Z²`[C«X& ê¶Ý(æ-U5›%S—sInq–0?rÍ€ß/‡°Ã.\eè*ðŸ{ˆ÷”¨ŸFuõUéüE?hg g¾}3ÿXvl¥V᪠™Ï]K —à Yê‚~nK ý¡¼&{ƒCÅ÷3è&åM´~R>¯³ƒkÝ“•¢nUó¹ËloÍ‚HŸLðžÔl´¯·§ÿÐÞüÚRpK®ÈK· cü¾ë*î?”gÒzMýh¾_7¼VŸX†vNÚžþþk,‹ÚÜöyä‘LèU\í¾c£C'ÝùÔÒ¶N­¬lü Œ-Ȳ–&9‹ßÈÌ<·ÁWÙo/`¥Û§L•dô|4RÛøî¶ïn)ä9a¬(½Â¦y=;ùxbÿ;g¤â{tÎAëß7’Öºï/G;}Žk®v•ñïeT3Ù÷¸Ž|k¬üä`ã†ÃXJ¦ç€ÈµŽŒÏ‹õòwªÏ)ÿx±8èVÛ R ûàŠxi\*}µxáò¹M®²ŽÉïU‡eAÏU]o î,‡÷š“–š$šÁ„'Þwo¢2£ô©¯ÙZ+ƒYZk†uÐç ïëÖÉpËÓyþ¯>r¯&ýËeüý,m¶¯GTs9˜Ü™Uxܦ锾J e×ô+svS¿nPÿîoÐ9>¿¿Œë_Eœ U»v$=,ŒeS«Ì =Ì‚_ø÷¶R‘CoyÜ~E¦p¢·Ñd̓!Ìл\ÝØóÛ3ýî-´t‘üŠò(ªç螯"NÐŽo ƒ_®Ç²Vú­m£NfÁæÃ©+—Âå÷G©Áf Ý{S&„M7Žòsˆvf'¸²*tœð~×ÞKy5Ö|ýc±Âý| 'h‡ËŠ:¡­Û»˜¥gÁíS—¬K-„ê„Â}ÓۛùãªvN!,:Û‰ÝÛéÌx.r?˜_תõ€§ƒD>/õ¨OO÷×ñ‚v>Ûê[¯A;†ûzÈ+³ ÎÕ(LçL!ØLÖNÿÙn}â öP–¼5tú©ËÎbÝNõåôo:ãóþ=Œx´c¥ÇušbG^Ø.Öu½Ö, ì /êf9Z@pY] ›¼PæQ÷õÓ¬%Üç>õ¡©n£¸¤þæ\xvf!hm×e˜%äÙ,éýla;[1 Ce¸£{T´/R\ò}Ï×’ä©–OfL¨Uj ëb£qÍ6Ʋ°«Üëlಾ‡º…ðöy໋,áêÛS­w„1.;nýÖ^¼‡OßGÌy²{íª~Õ~šüyŸGKÑΊÃéS:®‹e?Y¸HõÅÏsæV[ÕB¸ÛêÀ¦Áù2X×ÅáÒã°0öX§ëé/Äû´þÖ‹í>ÝiC=¡îý(áûÒ†À÷!ù÷Kdhgõé„1}—Ç2ÕÒëûœÍ†“÷Üõ|^ñÆß,_ «ì[f¸†±šuC§¹92ª3éþeÍJc¿[U%¿\ο:ç£dλ¾á-ÓÇCHIùàO'„>4ÚIÛzI˜[,ãCìÒ³aA«)@÷­d뤘NcúnÍ1ùʼn%¦eÅÞ­'¾?I÷Ÿ¨Cÿ£ö|ü 17~/|?O·Â‚a¥ÙÞ—»‰RaÏ– ­w|ŒrÐ:µ_ëÐZïêÏ#]ĺ€òº?ÊïŸO…úc ð÷àûÃñhÇÂc^wƒ‚X¦8®UÉ„+Mµú€ÓÙi®w™CûSJ´V„±OEÏY£s®ŒîÐyÅ Õ»âz¡ÔÇ‘£§žÓ4mÞãzÍ•Ñr`UÀ+_³u0Ô¤O×}YfàåÑq^TR [âhs¤ÁÍ·âyÝ“RÄÚ étèàÉû±ìö⬠s 8?¿mĈØ3»LË÷†¼zQ¶3q8Æé€õz¹ˆõÇc®ly;L¨Ûꀇ—ãoêê¿+Î ¥ø\M½Üz±±l®Ú˜ë×Væ@»u£R:€yuÄyiKø¼õ‚YÚÒPf‰Ùm÷,'ñ}É€ÇܼÀçËõ¡dz½½K²U€úùÊç2´Ãe÷‚bÙ®¹¿ööË£oƒžþ&¬J{¦CÓ%0bøè¹®ÏCXr_µ±:»œÿ{òóá°åJIÊÚÍ*Â~P#Qö+w|~¯ Ù¼çsbYÈnÁÍ*-6©OZ>¸=¹œÜÅnsm¸®¡¬`cZ‹Ê–ÎŒî·Ò{/Tÿñç6U⹃òçˆ@;ñwŠQ‹eŠ×… rò6<és0–Îqõµˆ¶Åvú&”-Z£—m¹ÍYì?S_€òX:àó€~µÞ÷‹G;é9‡ŠaÝ>¼¸A.,ç^ø²Ë‡2ë[cÛ.ƒ¢{I­VV†±ÆkŽ® ¼èÌÈ?i¤¼Ÿê :ϧs#Eœ ë.š4³cØ™­¯‚ôûæÂ¬¾òãóÁYñ‚‚ Zláîºq/«êõboêgÆ¿Ï8BüÞèýÊ;è^¢òýG•f¥ÒÛ•M&ξÃ,ÚdvúqV.|™9ÒN½}>èþÜ.+x«5œÊý¥ßÅ {Ùx­Ž#›:;1z’¯k‰÷7¨¿ºó|\J@¼? ˆ´óhÐ&ãÍ7bØîY܉F.ô”«Ê~ùÖ&ïȸ¹ºw¿üÖv/Ó±?øäH¢# z‘ñÂqÄp1oåŸ÷«¤l˜ç¥ñ‡Ë$ešž¿H€Ö™ï°ï³›ÜUþÙM*ÂgËà~£:ÿœm€N…R8ˆ¥6濘A§<%T‰}‚ÒCÇœûïf¡(³¯‰ø-1•Ù×_ÍJÿ3¬2ì¿™ÕôW,X¥šîJ34c„š_ÏjRfÁš(Í@Õp³4ÿ‚ø-þêWÜëP!pmQ(} à”*±ÒÜ“Hè °`‰¡®4ûD_`^g~_óþ·¬yªÂß=û êüsö«!:iL#®çɳ¿*öaü1÷É•ÒGgŽPâ¾f ôѱ#Pªèܶ¨ ”>:yJÝ•÷÷•¸_ßâC÷K™ûêþÕÜà?ã*3r¾Å¾þ;î¡Li>—Ò|ºxa>Ý×ükeö!7?=E˜ *¢í_0¿¾Å†PûŠùRÅÀµC塤#jÏz’£ 0˜£æ—»À=¤ùéJ¼×¿š÷Äù÷Ïÿ…5ïÿåz÷}­û÷Ö:Î_"Pªètvuþ9çÕUŠ2DQåú×<ãºâ¿˜‘®<‹3C‰ñÊýålQj?‡S™ïJ,°¿cDL™í¥4_¸eˆAƒÒÀÀðú®µ;ªeˆÁƒÒÀ€ñBU L0pâQÚ<¨” ƒ(¥‡ð'3ï¼P(C ®”®m^¨ ” [¼À5$þ 7=¥‡Áú,°o1"¤_±]3Pú¬(U X;TJЉRÃàu@ÉFD”À´vf¢*ñ]í¾ë×°ïyÝ÷µÎ]åßYëÔ…¿›œûŽëüs¦«I#îüÿL`qLk™0O]6Tp\[TJ8¥ŠNl‡ÊCIÑ™#•x®y():v$J Û•‡’¢“G¢ÔÐÑPò¯x®Äýú‚¸_ÊaJÑî/˜_ßbA¨ÅrD©aà: ä( à(”:±;ªeˆÁ#0¿¼þ¡bæ:J[‰ãj p\¿æ}}_ó¾¯yî*ÿΚ§ŠD©¡ó9ÔùçlW/TÊD•»Ë&0¬9ηf­G TÑ‘íPyJ¬WUtj;TJ;¥Šn‡ÊCIÑÑ#QjJ¬Wb€}‹!¡ÌSæ½Æ(Í4®@™`Ä£´1Pþ„_í…ª@™`ðÄ£´1€P5(R Jƒ)T([TJ+UÃÕ´`ñ(m ²T ʃ-¥€ªAÉ8Ž„À9$þ 7g=¥ñ'°ÿ†-aðó5%ÅÀD©að: ä( â(”:²;ªeˆ#°«½„ëʼW÷ú5ìûš÷}ÍsWùwÖTæçÈ4þžqýwìCŽqÒÇ ‚Ìö/8×_óíPy()a$J Ñá/˜_ßbKh|ÅvB©cຣJQ†À1( b/Tʃ9^`~üCÅœw”ž×ÕPàº~Íûú¿Ô¿ûÿmÍûß°Þýk÷{E¡Ôëp3+þ9ß5Uӈ绦 ôó,kÎQmQ(ý¦Ü}ôatZ;TJŠÎ‰RCv@É•¯jèÌ(9JŠN‰RCÇv@ÉQèàQ(u%Æ+1¾ŪPf„)s^ãQÚ¨” ƒ#¥‡ú'ëT J†A“‚ÒÃÀ ‚Ç•ÒÇ Š@©b Ù¡ò¸ºV([T J+T.*¥‡A*š-*C`CÇ•‡’bðEþ 7ì[ü ï¯r”jJƒÕUŠ2Ä Ai`àz¡*P&Àñ»šcÖŒ®Íwuø®_sľçußó:w•g­Óž]Á}Guþ9ÓÕ•ÒØaª‹'¯1wŸc¥†ŽëД»¯†¿':pJØUŠ2DgŽQâ¹–¢ ѱcPèÜî¨R”!:y Jƒã ¢*ÚÔæ¹7L†ÎŸ‚ÒÂàØ;óè¦Êü—µa5lÁ(‘5@‘ÈÒ7@)a*…ÉÈbÚB …´„Š™Ê¶öRàGT„bASJÛ°ÈÇ€,i›6RÖˆ ‘AˆSæ¹¹÷½¤ü”9gœ3þç|{¾$÷yÞûÞ[îóhÂzÃÂû\9ÑFàþ•^Äðž£òñÖëEä;­=@ ƒÙ€ &3ýJ¯ux/¢æ3ÐÁ„v €-¿Òz˜Ó T0¨Fu‰aá]® „q3@èa`'PA,V†—ߣÃz\õbë£}aOÖ¼'kž%â÷YóøÏÝÁç¸ý¶îWN)ß=æšzB‡µ ‚5O}þÿfó@ñšè b;P@ÈàëU@Ôà:ˆÛ¸øA„îÊæ;`Y™ ·‚ 0À. † 8Ñá]d¬Öc¸€æàDƒh`Û/ôWs€7ޏ²Ld …™r€†2ÐÂX9@s™€h`2ÁhFàÎd0 xÄžÄ ‡ÍÀt0¢ý:Ê@ cf€ÐàN ‚I­aeáݰ~ã:€æÍ ‡‰@#[Aï)ƒ¡]bw5'ÛØÿa_l†ØÞaf}²æ=Yó"~¿5O'þÝ ÿÔøÏ;bMÀÉ? Œ_;ÍõøgÝ0¢µEþYx ÄAÀ „ˆ3@è!fgX?lè!l'PAÜ ô¹¨øÞElV½–…on l@˜€ha=Òk2˜Ã<¿ÐÅè:ÆÊßý¸>F¾ÿÚt0X?¦ÿ:¼QóYøl:€FÌ ‡!@SZA`NPàœhR#põ#ݰN ‚q­  0° ¨abN4²¸†¶Lm  ëK4ˆ½°j ëKä5È{²æýgkÞ“õîÿ¯wüZ÷këÿy9ùcú{em@aš€GÆ?Ó-ô`Ë!Rs}þÙD7ˆÕ¬øA„ëJˆ7Â:f•r€8Ú”u=Äíªæ{f º ¨!vN¼¸·Äon  ë™57ÐÀ6 ƒ)LÀ´0GÎ#Ø6%þ Œb …ar€¦1ÐÁ ƒiì@ãX€ÄÁ@ „‰2@èa&'PÁPVËÔ0'ÌÜ@£ÙD³¹€† LçjÍgn  m@#š€haÈ ‡)Ý@ cr¢9À 40©ÏÀþÚó`vòø¯[°ëÙG×?v=ë&Õ×CŒú¶\ü±¶¡_» CÕµiNe9K{íŸpd^‰”’|­Ý…ñ_UÖ Wsy›Á…Ë]9žÕÒðìæl)§c©¼QÒ“åÔ6ò80GèuÐ&½O_ûhy ‰ª¼•ck¹4&bùT·¤,‰åÀ±lÚ¯÷ŒŸO¥þm1§ˆå>±žg!Ç·=a}¦ü æ,×qªê{^ºxÏßl%¤ ×È¿ìx·‚Ôù¼âøŠ•R\Ïøü s6}É?bÖÂné”åײüL–Ëò¦Øû ?6̱.Úníü“ƒVnä€KHÊ'cÇ$Ví—>{z‡XoOã£%é®IÔß/§S–£ËòÓYŽË›ï uâõWUûîÈMº±8Ëw¶„4újô7‡´ä~Ûºµ+Èò¦·[¦áhçi_öiý&e},R?¶˜ÇÄòŸŠ¯çæÖ,ÐIý1üæt[ylÔÙ3z¥È?yÇO%dWMù™OÛTÞþ@AÃs äâ•o'¼2œ“òSY(Ëáa},8«Ö€³… ñRBHhX^¯ƒþœ¼±õ7ÍJÉåá úÙKêþµÈ¿5‘Lë·íûcÝ8z˜O娓NY-럎ÿ1ÿëj´Ðg=FêâÇ(1gÚ+[.7Þè »Ê{?·«G)qDµSµûÆKnw¾¶éÛYI„Oën?£7“ùⳇ9I,KÈ{ñG³œ[±ï’´ëº¾¨Ý¦—Bs´˜óÎgþ’ééZâ{kl·øR2'²Ê¹%×KÆÕ:߬÷Å$"¯yë…xGùo…™²>–×Îò‹X¾ë_f}ã!ß`ŽáDE/cΚT;áóJIêˆ£Ž¾ë%“'ö‰H;šD„\1Ž*ç_‹[ò‰YìÛl.å²Ü–ÓyIHÙÛ«WµT æ<ˆ¾t諃 yU¥äù÷'ÅæMð’/N5ë´65‰Ì81nÒ-²FŸŒJ¨:jó˜šI=¬÷Šå2²þ¦—o0'2aæÝÙ[tǹÏj.(*%³Î”Íìø’—ôZ0Ö\©H"ÇØWž¸¦î|5}[–YÌn"æ.ÊÄœ¤Ѭ/ˆ}žB> \ðæ¼¶·%×b©ƒ~˜½fçáÓ¥dTëiµÓä^âZ='r\z"éà’å˜jo izô¿“oûÈšŠù_ ¥|hö¾X^— Ë‚0§ã{óWõå Ymø —R²b„}íØåäôÞÕFGQñ/vÌ‚rí­|‹f̧,ÿ“õŸ±uåÛ²TÍ{ëêÎ?núN„_orèàæ˜Å {H¹I,ŸÑ>îf¶µDS-çÚ‚9BwÍÉ7{Èó“S& ë_N¶ßÜ×´â£D"äm?ÌMr»ùÂ’¾„åä2 9£ç£õ+^ع"opµ¾Mægñ…E´ÆÝþ Ës<¤Ñ îkº<]NŽt-Þßmwé;^k°`½wﮯ`Ä›t¶¯xöÔ:ÂrÞ„\Ë+bî×åèS¶.Z6¶Zß»sš©›n\Dë,Õ±ömµî¯ï{³Œ¬µê¼ÁfSȱC¥¥ûÎr´åÞí¹ýž~“ zM„¼E­˜7xC:þ|Šež|”Ô“ò æ\p^Ž]]PH³Z7LÕWzÈs÷Ô×~QFÆM¹Ðâ‹ÉdÔÈ–‰? å¤Þ«¼r`îZ Yw§îëÜÈÞdsûšéKZ\Šf=Âù z¿oÄS~mo»?Ò|íþÁKk”‘Ó›Ÿž=~u™Ø31ì9Z¹vt{NÊŸfýg,G‹åY1Iýxaù©JÌÙ_X4~M­BÚ¿+1­ïXFºüSXFbÚ>34ra ±Ý;×/e$'õ+°ý Ëkö5®h–£ËÎká9sZÌ9™?QÐS”Ð.#Ÿ–ñEHeäZ«ª5+"§÷Æ¥ñŠñ½Q~~æ°Æfé<ÊÎs¬'‚éœõ•…|ƒ×oۻÑ ¯P¡§¡Œ,ÔÇ6ü–‡øO¹z|4-…,½Z£Ë­ï²èî¹mÕOͧ‚ûˆû4”§Îö7,/”ù7äÌy—“}±€æîù²wôÚ22‹Õœ9è!ƒçï5’.SZ]Ô·Ì¢÷ÿ h[5Ÿ²¼Oá}ô•ò­YÞäñ¤.弄åk…|ƒ9®¬¸/*ëP›/qH£meÄB>üÞ*‰=·oCZ2¹²€/FΤY÷Æ|ÃrsûI½l]fÇ…õb‡÷©81'>dÌ=Tè›)#¾Ì²t²‡$´yþh$7™ÄL²ñRE&M,YöÁ&ã<ÊÖ–ŸËÎg,–õ* Ç¿žàÌ És̃>#ïŸ*#Ø|Ü/èï!§WuΞ³>‰°¾Ü½OwÕzSÒ¤þKv¾æ‰ÇçP4; Ÿk}Á7r¿ö–aÙ¹èÜ=Ô{¡¬íç•eä༥ î´ð“wVÄ/Ö&‘Oµ¸toG¯å|t)eò<©ÇŸÙù“íŸÙ>!ä¼~ÉʋϤÎÝCwpÚïý\Fj}U¿]⥤*öTFW.‰ =öô°ý§³è_Ÿq®J1K9÷lËüòhnrxï‰slñUsû'ï¡¥Ý=¼X/{-øÇÁ™_•’ wßÛºçd’:àtjed–”k,ôu“rÒYŽ>Ó™GÚ³Ú¾Ó€9G#â¾\1~ÝÐíÈõUêrò‡Õç;¬ù¿Rò½É—¾÷öd L̤¹Üp\!¥‰ëf7ÂÎÃ1»Žd]ö^•öÕûÊ…¼^ æ¼?èë} »ì¡ã†®œ[7¶œ¤Ý®wýã9¥DsëÊ'm‹§ˆë:½r'“òíéMZ¥QÖßÂú\…<ËëR¿žwýrµ^_æ´,LîÜýç|z•¯›_N*s§¾?´”äŽVœ¿3:™Ìà>ø1FžE×ñW¦4©ç›å±³×gû[æÛð~J'æÔ•G¯3ͧÍ×öqŸWNTk©råJIšñŸg†¾—LzwìÞ³ͤCÞ‰^ÝqØ<Ê>wÖ÷ÎòzYo ÓØGÊ? ùst¡/Ÿšvy=«œÌ}j27óf ±h¶ŠÚLtç_^÷A§Lº¤öòn³Ï“®g™Ø>PÊUÒÁWªëY­ÿ,¢‰_{„Ìøöt|>-Kç CÊIÛ² &8Kȃ¯ïO‰O&K/w~gg=íóå¥_‹™+åβ>*vÝÉöiÏ6{ñÄÕ74$‰Í§ÓÆ>5ÿërÕÙrrغòÒòŽnšB4Sc¦Í ¬§urvL™þÖ”å²Ü^Ö«!ì£|ÑBßúñ:XìaÇý×›74ùT³¿`ä’Kå¤ËºLodJ 39©ù¾SHñ»;•I³'%vù¦ûÒ~åÃ3] ëõ•èá|úüñ„õ¾‡üƒ9BŸL>Ýݘoˆ,'Ñ'Ž.©-!Ó¯–'Lì0™û…LÚþ‡gŽÅµ˜#ö.–òôYÿ!ÓÛõ_|moGÂzlBþÁœÚ~xºÍ‘ÝtTóçRûU•“z¦bwB«òçâ>ÅÎ'Ë”‡½èϤ߭R7ÔÏN¥oϽ~lêüh©‚õ8²þ+–{[­sÊãϵۘ»›þзõ­S ½dk¶îÙ·‹IEÏ~W³žI$}Îí|ï0æ´æ*T»Ó¤ÞH–cÌöŸ,¿»Zž:ÿy©ù„ñÝ´waé¢ó­½¤ÿú6]v¹‹I¨.ö|iV¸eù ™4okÅ[+W¥Q–Éî§°u“ío„û=¤ëÆo0ÇŠ9Ï£?næ~JTy ßæQº³˜\Ü™‘íÿ{©W•Ö´Éýõ´wL‹û g¥QÖ;Éî±}ËÑöËÕ¯?#šúµw×üÍØflý¬ßxà%I›h½÷2ЉÅ9é@º)‘„j6­÷%itö¬·ǼEXßÛ²ï‹çÇß»Ðdˆ´_ ùsl»%w—¦*¿ÛQü'/`ÒqÄ®»ãÉ_sm©<1\ð æ\%[/ll§± Ü…Ù©^ÒøÈ!£û“9yù=;¿0…,ªõÚ¾EYG‹ÚGÞH¹5—²Þ¶b÷X¯«p>#æá|ƒ9ßêº=xҡk—õ°¯ô’ˆWµ cýÄ‹í>¯5•høÚ3ù:*ôíÍ¡ì~Š3oê©))=¥ý Û¯ ÷‘a}ã!ß`ιʺûîì¢1Ëf\«Úá%Þ(^xgIóÅ÷6¯1’üøwë¨Ç=‡²Þ!¶fy­ì¾»þ × s'ÿ‹½ó€nêHÛ?-`ª v@b0¡‰nš_QcºC˜. 2#cŠè¢ÓÝaš©6-˜.êXƒè‚ØÂÔ˜º¦‹þ=W÷ÎÉã°ÙÿÙìù>8çwv#’;*Ï3wæ¹ó¸kÞobK“Öîùòð/toÁ¶"á›.Ðð!ÍZú ¡®/ú;lÛðÌýtX)ϧ¨\gàý4o×»Ä~B<ÚŒvÞœ˜úÌ£Ñ&6!ol™Ö—~¡ç•%ǹ@…:Ù®Ü÷ ¡Ò‘Áug¬YÈÄ\ŠÆëŒ¼ŽÃûi~n3ŸßòÜ'§ÐÎ)ã”ú§×mdkº<Ж½÷‹Ó%ü.Ðηæ®ËÕZú¢Ó®ÉMü±9Õ„JܹNÄsÄ|ÊÛ²®y¾¯è#±\OðZÝôéæÌ§{õ ­ÊBŠ äŒÚ=2.¥7½X~«oÚ\£i­‘Œ÷g­öT=zÝ¡þ33¥~í†oö\ ®¯«<{Mz ¹õtV¢W i/ÅÎÓé‡ö­û.ˆ:„yôõ^À¦®,V-íI8Ë7g=WŸx>˜8?.Õíòïž=7Ѐvb»Ü.‰©N+T¬V½åšù¸ót·~õ·güƒÈO(§ÆÇ±ªgî˜KáŒçù‰ó ¥Ü÷köüø\Ük—·×Md%lK§j‘FeÊwÌÝí<=8}¼9[D[*wîÑñr,ë{UPÚ.ç¨ü~ÜÌuÀÇSNàúåõãÇ]NHdönc‡6è–F#×n©ýJuž×~_¡hj^|¶b”"–Uª $DG0~Î<¿Ÿgò|qœT›x®Óh§R«7ŽHd—KÏäyßNxdªÍ? _H"‹QöÎ=}luÖ®ž¶áÆ9ª;¨BqV`Ý‹û¶ááU1r?ÉóÓx½I¬?fÊõžGÆÇoNŸ !΢u¬i‰Z•Fg¿x^#bï9 ?#ÚãÛTck }b˜£ÌCÓõÑò}¹|žb+Û¥Ö”ë4¼_æýçGu´ã?ûБÃÖ±î=ÛÜk¶ *õ¶T4EŸ#1op~M3^=‰aÃÚÙ<íÑp9ˆç)ñq3_pú×ÝTZø%ײê=C¯­N£¤H}TËsdjÔøû¹yP|l©Å†~¤g™sž1\^×àý=Ïíãó&§pÝ’îK懶\ËÄò|g»ÕørósäZfTUÖ­?¹4äv¥±,þÞñ·«‡ güüý×Kn8¯—€ëmîàX;Ѿ†íÿ¢„K£s½ó´/XöÕ˜þ"¡ÛŒ ÿíÜ‚µűI5K×ñZ;\ÎÅà>ã9¹<¿Ì©{\÷õ؃÷ Yà †ß~°ìlÏð¬hyy–êí,þl¿9Î.U¸å­8Vݹp2œñóóÅóÁkQ@ÉÇ-®Myì›èýdÒª¢÷å±ò6ÐNV)Ꮉ†KWö¬v5ôWGžxá,5ï¶%宆¾»n‘âd[sØÖ"jêpÆçU¼_å9e|=„çȉº,!êþËLõ6×mÕŽÕLH±>{+âR ̳ã,=ןç.ñü½õ=ºùÐ^—êU?ž <2mÍ+-g^õö=Úì—NºáfÙO[ÉÑh±»ëÔ´)eÂÆ‘7bØJ#ýÜ#œñq,¿|Œoô H§+ÞmO_c%Q7Á42›¾—~ŽaYoWå{7=Œñz°ø÷_Ê÷ož‹Às_>ÊCC;SÊ]L¾æ¾„•¹Ìî×”N “뤷Ò)Í?žiF]Ÿ7_,+½ëq•oB™8ÿ,'¯W‹9Ï'äú$×AöÏ£F;"Ÿêòœ_Ì~([ oÑéäp¹¿s~{+ Û4v‹[‘t§¼üËZæÒ=¸"ø¸†×uùý¼sæó*÷Ô•×3þA;±ß+FO5²øEG5ù1¦k’G SZ©¶%±x«*ÁôÕÍY³WebþKã9‚¼^ÀדùýÏ}’ÇÈ;^-ˆ×åþA;ô¡™þá9Ìo_W,’wa:­N st†‚ߟmRzU\ѽés¿X–ÙA˜‰‡2žóÈë¶¼ÆÇ£sûõ/=¸•\'wúí<ÒÇ^[»€Õªw9dÍÚtÚúfÞõ­+ÎP¯ÔÆ7ßÜ"G¤±DÛg1,¸QÁµµ¦ a|^#®GT$»ªÌOå6eùò:H¿:G›:W:ýƒvâTç_lÙÇœ±»ÓiÇýF­v;CCêhÕëZ uʘ 1¬Û¾Êkêç*ÝwëËõ)ž¿Èë,b½ !ñþÇé´³Û¸¢Lü¶XvÇÅÓ²ýd:yõéUó¬ëzúþV\%´óåísŽéÝcØåC‡ì?G ‘óy^*¿¯óù<?fÿæ*•©ž_¾«Ç–Vhu+¾·ÒÉóçMûg;M¶ïfnDÁoßÌ)\ †µØf(˜¶7TÎëv²øz2ÿbN–8S¢Q†³·S£Ùž öUp¤S:)O~{š¼¦U:²ðHò^šÞ³ò‘hæ”õâÁŒçíðý Ü?üóðñ–Ó7¸þC¿çËúÅE±“«’g¯*`§i©íÖLô>Mµ‚îÎ]óh] Ûgí-å3ÿæÞßô*5´ÕÃM¹_¯ßè£ý´³DØ&`œÇF eZ;UPûÍçSiÂË ö ¦{÷_ÅßÞÍ^]~râù®ùþÆ÷ðu7ž“yj×ùK‘š¦Äë:Nß ÛEŽ~5ìÉlÖU«\˜ZÖN³n?>!•f¦Ìo±zz0Õp¹=¡,¾Ïï«Lz9o°<åýÏ«á÷ƒ÷ÆKë¶h oD% žŽ<´&G²äFÎL«l§M÷7æß_%•Luû,έ¦'êÑ•íÑl*fÇ ¡Œç¥ÿ¾ÊÇÃâ:S•æAf´seÛø>?~˜Æ®»t©:ªžºmª–\üÜ):1aÿÊÆƒ‚iÀñ²'÷Ý‹f«²®4ö±…2>?á¹d|\Àsù}<{ÿ™vŽvk4¡Ù$6kä†ñgüì4ƽN›«ãOÑ‹Z\*è,ë¹âË#S‚Bå_äã[§_™ê½µ›Î,0‚ éjñ½íôܶ0÷ ª§hZû Ý®6@o-›Ûî®Æ;Ö¸\7XÞGÀëlÜ/ü{¯_é£q”í8ËÎý™¢Bâ–_CíÔ×¹0r’ÚÔÓ½8д?‰¹<1¬‚OÍ›ëò–æ]uåº_¯æë¡|<}Ü©F;¢^>2+Wâh;X¾~üˆù'iþ¾.?%‘s{–{ Ûé]y˜!„ñÜ%ÞO‹:ãënâü×Mú°éÕãEݤq°¾˜"inë“tqz1ÝKï ê2éÀá)o¢Ù»v¿Ùž/„ñ| žKÅï/\¿<Ï'»® hg|õ ™S^¤4Ã׊Óìt)±lìÑw'¨F|U—1Æ@Ç¡Ñ̳ñ(ÿ™÷µLÌánCîu0BÝB®GŠýËUy}ŠïÓrúíœÖåõ¥9:JýxbÚD;½ãÞÐt‚Ö4ÎZn*K_~ˆfb>fˆœSÄ×!E¿Ÿ’öÅÙ|ù¾ìëÓf´“ß 8’Ö-­õ¢ïx;] _¹èÅðt×TúÊ‚…Ö¡sÀhöýÂòóF†‡Ê9R<—ç”òýq|½'{žpÚùNÕ¿Ð%ŸqT»Ð””ÝøÝ›•wÏs‚ê$ý2qùÆ :ÓAµ4ÔÍØò-=j–Êx½ƒûEüg³¼~Èçáå yfªç)}{Ó@î¹e”âkÝ®óuúq2ô*ÛøFýÔôÇ”‰%ÛG³_ßß ¿6x(ã~àúý~Pª0ß1ú3“o»Tý(¯P‰vnS’UÝ4=*}Ü$;Õ-z!¢ìÌã´/Ú«Ú²Á4sQbDëªÑ¬I„kÁkoB™è‹Fò<·óÏæj\à³Õ×ãö̤a®-Ô¡³mÇ­ƒ^Ô:N¯™O4wR'kåG³ÖÎ !ò<€¯O‰ýÊKùþÈïÓN¿àúëVÝëÞx>mÜÞS3¦.w/ªï|ÁBb¿=~y™ÄúF³êÕ:¶k¾{‹9)—Õsåuâ}OÏsÈë¹Ùë´ól]…/•Œ¡['Îî´ÐNS ÇÖc¡ ]žý6˜Vlž:õÀÌhf[Þ£DºòþN¾þýã¹ÔÛ—çúhM®Û¹ûèê»Ú. oç@ÌNû{úx•·PV«ž#ÞÓ¸kê-I‰fUóûÕ›\aàïòËȺåub~É>ï3£YÇËhî41ÒÝ`“56Û)²aùþkRSH=ؽÊË&¨ïYsŠùŰ~BÌãî`Æë°¯{Ÿòw õìâÁEHÔ&ž'èôÚy¥6ÆÅSžó!éùwÚ©Ù×å¼›˜BsÞWû1ÿ *¼«xçQucY©þš¯R\ƒåý‰¼ŽÉçâ:^.5óÞæÉ›[È9xN”ÎTwd­ö|¿ŒÄqºÒ=wß¨Þ EÚßHOºö¾]0Ž{þævl°¼O„çù8¯Á|mâì=¹¤þ³µœÃæôÚi(tSß%PÊUa£€4g]¢=9FoÛÍ)Ô|»†VŒœ»0o³8VúúÃæ.–AŒï ä:ãõæV ü“wn.Bi /z5ù®ÅG9kj´ãu}GFë~+I;væÖÌNo†˜|$ù…¸­¹¤†2ü­;Ë¿º°µÝÝ&ꪎœ ÇëÁ?µ¼z§~aâõ;>ÿpúíLî±ÒïeÄ*ÒŽñJ[†ßçá¹j…4ÃŽQX En†ièDg!Ñ;–Å™ï”mê>˜ñ:ï/ùxõzî-–Žï}Åß§Ž´¯Sº¿ ïÜ'øžˆ\Móî_{¾x»ŽmN?Û³ú1 }wqD³@ò–Õưa™Ó]ËÚ8oJùþÁÇ-OCúÍÚÿâ…|ãùÒNÿ åš#î®!íé×qý·á>ì9«‰ýÖQšÛ5®·«.ˆÊ\˜Z¥] 3W`#‹d¼ÿãã±?Ûã›rlxn¯Ï|ùøV¼¿»ŠþA;/›x|ñºÕ:úq„Þåf¢\ŸªY»ÅG)µö`[@Šª%, İóî õê+äº!_W?ÏN_±>ôÔ—û7û>È ´ópEbî©kIØÅ=v­&îklj”&¯®Ùÿ‡þýé—Yï‚_ÞŠfÚŠ—;VÝ,ïåïWìvH÷K‡”»^BšWäýS&S6ôú˜õ¶$rÆ+&Ù)_§^ËûŸ)i^×vgžQx÷-Vça¡~iýï¶ÄøþþyÄ~s›ïÍ:î[k$=“rÊKHy‰b;J´s»Ð?†¿¶ž¾Þq»÷¬v*p8ß×þۦå*ÿÅê RÐû¶a…áë­¼O•÷¿üóˆº{ìË÷ÿŠu qßíx諬ðúeµèuz­iƒÊ:š¦ÞS]¦oD§÷ò‰aâz™VÊ÷u“qû¥oöýH\w¾Dߨý)Ü9±·“>¼z#Ÿ ?Óë3ñå&…Rh`É«RcØ®Z ¥õÖÊû†ÅñV1Y_â|?´Ï¡ÔGï߀v2> ŒÜD‡ÛÅÛiòScÃfö#4sþ³b½gi(¹`Ã2à&ßÛZùùqTLZG{îËýÁ÷ù:}‚ë‹ã•Í4ñíÚÖbìôaöÖ>÷—¡®ù_YC–]E|ÚaÇ£O›-'3qß\qùsðßAüýûòÏ)ö/’OÐNBzù£¹fþDAWN•_?ÝNÞ¡³vyö9Bõ{æÛšÒŠ]QïµíˆaO’ýCãG‘uÅï_\W\¿bݤøÇûÄÐθU÷»žÞB®ÞK†OÇ8lû­&-Jy¡‘®tÌé'÷/Ë'Ün>vÖÆ÷Oóöøçï o|¹Þø>l§OÊfªïùæZßqi2 ø¾‡Æaß$ħ5²–ë˜ç6æn×rn ï_C÷ÿþøï¯íجlØÙ§òï#ÎkÄýhJ´ó£C·€­d˜Ôn÷8;õ,ýÓÏO––rZ5T{÷¸=çÅ°ÛÆê¨ÎPÆß/×1ß÷Æë1|ö‘OÐûöÝóB…·Ñ·Ñ;ì‚vÖb¶ßã0ÝôMýº5 ¤Ü açl ;©Ãoqß}¬ç'¾|^Áuâô Ú™¸qäáû·ÑÃVmýÖ µÓOT³d5×Ãbî¢þä5+Ï„ c˜³ÌÙAÇø>=^gáãpqóLžWfïÿ hgÎÕwߘGo§²[K®È£ÁxüêÑzç¢Àøºšw &{„¦Q‹üðÍÞ 6úq:Æ×»ø8€ïwswŸùòº¯;ýƒv–¹Ó–«¶ƒœË³]íôvmÌÓàCTùÞ¾â§f ¤‡z‡©.D3åÒÎ_mŒ c•Ÿ¬Þbk}ª¶:÷þ/êHuÙ_}¹O³ïK5ãúgzõ-1¨õ…Vvªýu÷Ý…‘½Ê»ˆyQqÏnÛ܉fâøj°œ·Îëd¢®³|ÏŸ´»4÷Î9ŸÌÀuÍùÂÒS¿ßIGC2¾­o§™AúèŸÌôáäNmî«iòé5/6‹aÆ«] Œi¦e¼~(þoi¿Ûc_±N]˜ø<#{*×W™ê;Í„'vR1aÛ¤7ÆùWÆŒúÁLU™ºúV0=Ùu.tûÖf5é”_ÖJ÷­òòó/|,­Tµ£w'å“÷щó@©†v®ÌÓíÜ.:Rs–—Ca§û Š»ùŽQÍïc3І  ý¾ÂTV®#ñu1¾?‚ךoò[˜žžOÎõÎ>¯P£Âýö7]¶›Äqƒæ¾¿î¸ßÑÂKO=±цË5òôŸËB6½[öðl¨<ÿâuþy²F,{{ʵ„¼þ™}ýBƒv.Û7XÕ&Ú5H¿=¿6 üu¢ñôA:îsßq¡[ ­›,Ðcåù$ÿ}¸ùºð´£‹hÁÌâÒý·9ÕBb½Ê€v’2²¦D^0‘¸ÿ$¶Œì0Ãï Ul°ýñ¾VíÝÖãØdej@½eCå¼kžkÎs|·Ü۬˼¼4 |ã|óò5&ž·ìô ÚQ.òy¢°‡ö<òúA:¥B ”8q€.vnûkÑæýH¬CDZJ#óÄ»” cܼÎ'îC}à+ÖóÊ9ÁÙçáf´“¯ÉûÂùrï¥C6×*q5 DoԲ㪽|éÛðnýȰlIƒ›õâØÖkî÷xÆøzŸoðzeÉ+¾×£‹ÊŸ#ûsjhçÖsM—ñã÷R‡k¡.óN¦ÓòºÓS÷S©M/› ¡'ý”.Q+cYà¡w†ùuLÏU“ç¯\\o¼>ëôW¦zí4M×o÷Òåößçó9N{ÛYÑj?ý¼+¡‹aj 5.\)†Uo»üþŽMٔح_ŸJò8™ïwÇé_ÈûøÄûšXßS¢=Ãvvê=o}hþuËy›Óiî˜Ã»»îÜGâý*ˆîè…wÜå÷e:ÆóºÅ}ˆåu?qXDÞ/’ýwQ£os¯ÒóØOºµÝvi:Ušt'è°×>ª¶´­ú±Kz×¾Ëâ b1RýP'ï Ç15‰×“,=¯ÅÃ<äwè{Ë‚±mè£ç_ÐÎò³Ý*EOØO‡æ¶ýcf: ñoÝÄ4}/Ý =S½ ú!OP‰š'³]1TÆîö‹9á¡’÷5óý§|\&îOë,í k*úí,pm~1óâ~Šx:7sùÈtjx¿EPø£=õnFçíú“ò›¹Æ+¿F3êÞk+{Æø¼‹×uRΪcÞðßBkÊi5ôÁ·¤Râ|sgë)MDß Õ|óWët€Š½¨üã­é4'ÕvýU·=4¥~ÄÃäšý©¥×¡¹q5bXëóëÜÈ£cu#]8j©ˆ¯WòºÏsÚÝÒÛó‡öÒ÷'>/`F;?~‘÷ ûÚuEèHÓ©õ¸ÀËK˜èä7Å;¶>D¥ë=ë¬C?]¬ã•»F†3Þ_qßðu_ñŸ_ûŠzQßßçô Úñ¥ˆ}ß÷ ÝÛÒ=@GéTýJ‘“¯½MärãúŽƒÈ­‚çð!Íb¥ïk8Ç)žòz_Çǹåzïgþ)—©>TnâûqY©±¾r½¶ÕÓ)8(ßò£KwSJDÊ&ïaÔûXÇ¢õVIJMÂ0­Åpib¹ÿçóô«}¥fŹåý‰=w‰vú>¾P±(~Þ=‘ÿøyS©tÚ]»zýÚî»iâim!‡† 9ƱÌ%óàÒ+?éßçÊë|DìÞøŠû[HŸG\'S£ÍíçZÏ\`Ô©• Ü4j¬‹8Óoî.zXvÏ×új(F?¦q÷¸v)BpnãõîW¾¯R\÷{í+ì’rïó-õV’›‰þA;í3kÎÜ^ÁL›9–FwÔº·Æey¬Û²Zs Èð3r^4¶lÒ¾—‡2¾ÞÇÇE šñÓë'¾O'Ÿ¿ÿunŸ ò§E·ï×y›(êÚ€v܃j®¿f¦e5gÏLMI#[ë³yËMÝIsv<ñÈ=FìØPé«ÛQRã&©¿çõ>ïëåy¤û§?‰ú–ü#|ž|&¹7S~×ó÷°4JÙJñožï ‰=|~iDÊ^;xD³ªÎ”Âåç|Åù Bº¿¥ûê—¾K«Ö)õÎl‡+ú_Ïrúí< Š¿Û·Ê!º1J0goÍýþàÖ­½wЩqKC¿@¿s{\› õbØÙ±Ž:ßM!?7Âë |¿‰¸)Ÿ4.êBb}]ôiÚ9$<@zˆFÍê:Uµ;,ÝünòvÊXõ¨oÛŽio'á‰ÝXv?QX0-ë×øþÐS¢Ÿ<ª˜Gú¾zßïíô2Sý®ÄãK¯v"Ï-cºÚ™F‰môü¸úëj-%„%˜›¼Šc®N_Zj¬T§QÉõ8¾ŽÝãŽËˆÉ•ßIëXƒhàŠï3û;Њ\âŸÏg3}>›é?}6?Lø|Váwú‹çšð³èþèl“?:‹îSÎÎ~]À¿x?g˜çH˜~—ËsÂܤ37…lD7D_F¨¡|ú9'9e^+a¨H`,3ðÎvÖI€t&pÞ‰dÎ$e^²{òWÏæYa– Ë³Âø9M9eÂ~ê9M<+ìŸeJ|îû>÷}†\_ßç"½›ð;@„ ÀBÔPCIÀ ¢Ôƒ àq&jÎtòƒP“4€LàÑšr8×éSÎáÌ~¶“æ_<Û‰ŸÃÉÏX7ÿ.#‘gè(¤ó鄼0E¡ÖŽ÷ü`”d øJ¨à5àÓ˜¼„ñ>®²Êåœ ë CEÐÀX ‚¹¢€h`2PÂh‘ Àpf)6dÿã￵¿4“\þâ9œnrç«þgù9¿?W=ê_ï¿§ÏS‚$àáé€ ¨!À$àêAðƒ“‚4€Làaš¤óÖ øC¤& „P#AÖœ·þgÙ9¿?kÝø/žµÎ³sxFb€BØ×Žë{þ–{í_Z<«XÈ ó‡!Le„½CÂ>|às˜¾Ö{ñ>A('¬[à:Êœó`5ÀT0’Q2“X€ ¦2‚,s™7 Þb&¬(a¶È¿˜£Ì–}íc&Ìi™À&5IÙבRöµ†Mn0­d?˜7(``È~0r2PÀÌ üÑÇ™€2[öõe$~ç}îó ¹þ¾>Ï[j?Sø!Âd € øC& „(#A€8ÍÀY B5oˆ5 8€¢µo7 8€¶DE„³zñPAÐFIÔZ`>n™jð®VàSB8ï¯Aì:`sÎu€_„¯ÿRxÞYxv¯e;£Ýú»¬Dž§#da¤Ü0o$ 8@@Yaÿ^ƒY¢€ã+aÞ+PÁ8FÉ<9åÃúÀP À¦ÒP 9ØÀÓ PÁhFÉlZ`•òaÀ!dcÃ|& „#A€ÍÀfŒ )-@c%sò\œò³çêð\ìœò?5Û’Ã9î‚&…?ÿú¾ÿ–~ïSú<¡¯ûwú¹ÿ¶>Nø’BÓƒ àÁ%Dg™Àâ3% ²@„hJˆ1dˆÒ ¼!Ì(à¨xC¤QÀñ‰9:‚€µÀ |Š çßårŠY ¬ÀÇ]8³>€°uÀæ!œé~ ¸eËÑr¥„çã ¹þ¾>ÎGº~–ð=A„& „#A€ ÍÀ¢Œ 8-@h T PA¬FI°Z`*×(‰W ¬À"6JBÖ+ð)&œ»½CÔ:`sÎFÜ p=°ÎzÄkÀ b×» gšA#}2P|)œõo? ¸Áz)k'{."ÏÚ2¯#¥¼0 b”L¢ ‚YŒ’a´À |Ê {üpeÎù¯j* ¸ÁTzü`®$àƒéøÀh ÀfÓ›”ÿš O ÌÀŒ -@3%CjøÀ˜ ÀEõ[ONùˆ‚iµRϽÎ)ñSs¯…<˜=¸Àð:`júÏd$~î÷>÷{ÿ¬ß¾{PB| ü!BPBˆ‘ @fà QFÐ@œà FÐ@¨ ‚X@ÁZ€ ¢5JÂÕ PAÀFIÄZ`>®ÂÙùÐ;­67á,hôÀâÖ[ á¼N¼Ü t½‡p®tÁ'D¯/)œ9$æ#&H7-°Ïßò®5 YÊÓ PÁ QÀ!ô}0Ѝ`£d-°eÎy°:`j* ¸ÁT:`j˜+I2˜XŒ–\`6-°L²@ÌgÞ0`p Œh*˜Ñ(R ¬RF¢y%ÌaaP3ð†I£€h`V‹”ym”2¯ýa\P¼‘ ÀÄfà #G,C›7L@s[€*‡Œ0ž öy¼÷¹ß3äúûú=?é¿wßDhÞbp i*ˆÒ( S ¬ÀMDªVà±&VlÀÂM.¯Ø ¹wB†^ƒuÀVTÈ$B_Ü j½«µ÷q'EqáìxáMøžÌÂoaE‚,™7D@±Y€ ‚3J¢Ó+PA|FI€Z`>b‚$F-°ˆ2¸@˜Z`>E…œ=¼‘ꀭ˜7Vï&ä¡à÷†p“Ä«/!œU× âd ðÎlFü!hÓ—ÂY¥Â¹›øw¤¬kˆ[l á+1ëZ ‘[„n”Ä®Vàã%<ljÿÂ×P+sΈՃ àc$Ì¡À&qItÀÔ0KpƒatÀ |`#p d*˜È(I ¬À†J.0•Øj‰¹Öa¾ ƒY€ &3JFÓ«”kÙ7L@óY€ ý”håœåʳ¬uÀÔ0iÒ’ôŸÇiŸÇi¹þÞqZ€ô÷‚µÀT¢Q£XD™\ L°µy \ R°5ÄšÜ X=Èj7 ¸A¼zü â$à!ë‹ y›x ‚N W!G}ð‡¸MnB.¾ ü!tS !»¯,à!œñ­BøQÀJ çï gÉâß™ÀfH Oál;¼æ)œA†Ï 2K ç)á}7˜Dle…31ðpƒaô^Â3Þ¸Œ“ J\d˜È”0R$Èþ0” ü{çÕTº½}°bÃØ£ãŒ¨¨ØÁŠ-;vì±Ç;öX€`±ÅŽmÄŽj¬o¤FlXÁŽc‹uTTÔï99ç=ƒógæÎÜï–ÿ·>Yë·îÈÕ³Sö³ÏÛÎ~œ ª´— 8A`! x@hÑ@ ±€h ºh €ðô ¸C€‘À"ôi@ 1F©éÀÂŒJˆÓ¤wˆ48@¨þ ¨!Ø( €hõ MòÇŽìR€;„  fÿ¿è‘­éÀbJÞ¬@Cb®J©÷O×¾ÿTÝû«5ï¯Ô;^ßþokÛ¿b\öŸ¨gÿ©Z¦á»ABA&Ð!±,ÀÉ!%˜HîH´Hà€dóîH¸Hà€¤ói@ä‹H@ÔHÄ( @2úƒ´Â‚o4~HL½£àƒŠï  ” Áßy4HXeqÁw $®©„à/‚Ïdm)¡ï>þŒD¶ ’9(Ðz¥Ð‹×FbG { Hì4an‰äv@rûƒ´ï…~$x=@D×Wž«Ç¿AÂG%’Þ¬@ƒä7 óJÀ 4 8A`BAèA:ð€0¢âЃ4 –~ ¸C0‘À¢ñi@ ñD…ð\HîR¤$&?Ü!ªHàaùƒ4 †À""³WÔ¤i€áR€;„!‰Ï¤wˆ08@ˆþ ¨!È( €(õ x@œÑ@è‚d \ÛRþäÜH$ýùü4šþ|ÌøGk{|/7…¾^ëK;Ø~b¤ÿvr²ªoªƒ§¼ztBò¡­O™FÜ~ƒöØûí 7šì^*Ïc—}²ÿMÅ’[Î>;Bò‹ÊRýhyôäýªpÖQèJ¨˜@^wo7Ðn³Ù=¼$öí@ÑÏ)–ľQ7(w—¦†=öÒâ vMŽNIewº_îÎ&”Øßà`+=ã>»Ü‡Õû»AsMý¨*ÖKèÔ‘ס¥Q¢o±×ï×m×ñÃ5b©âégƒŸ/½AKCÂ'MÉ-Ù–G’fkóÃÅJ‡³Å5?Öc²Ü×\ì3Ö’ºìYkwé¹Ê_³×iÖ©÷R?·¯úƒ'xšÐ9–ÚÜXÓ1ŸáùÛ¨ì!ÍÀGŠ¥§FP©Æcܨñ26ôåêØŽy'I¾aiöê…5âLÍdÿÀv»Õ+Ÿ\NP ë×Vµ!ð6›:.–LMí~Ù=ø1û)¯/ÝEŒ­ià×z8Mð=ÒªB­¥¬QXž;]&2îãÈûó¾£^åîx|œöÞv]3®;ô€wïÙ«cÉСFÏÓ-oP÷JŸèwÒ½~î_|v £ –‡¥ºµcãK¿‰2AîûÁ}9x_#þ¹×MÇu—ÕÛ¶¡ˆ9–,m…Þ7høü-ÏòDn§§)ª©éKcŽ»ùóîPÙ'‡¼¯Øí*÷†C;µ¯kW‘÷+¥ïÖ¾îY>ÿ òÚYåbßvÛhÃã IM>zSíöBú•¡¬ì½:±%ï—û¹‹¯[M-%½î=‘ü=¾¨xXÞÿÖ÷ˆó¡H‰"=2b)+¶f‡uO®“ǰ¼5_ÜJsž]tf±75¬á¿¯ÈPÖVñðNžŸ&0î÷)öl-åã•è«nGul TzÑ +6×Kû#©GÔEœÔ_í:Õ+zæòjEÙý ˆãØ¡ö²Î]ãè¬`·~‚&*‘Ue3½›ujâ{'ºö}ÂF¿06çüÎQ^MfÜÇ…ûWò>ûŽÏ'Tü˜`/ûf÷¡DœyÎÓÊ&Œ£ÞŸÖ¾?ã:=Þß xVòFZº~]È’•>TÇ>sk¯ya,´è´F;TSd-ÞWZìûvNÕåYŸôIí²ÿ´øzD-3âT¬Ôð™×ª8ªptòð8Ïë4oìøçGn  ãw ¶ÇúP»Uúüú aÌòlE·>…§HþH*YϼOýÆz1;\J’èÓÒIò3}{ÓgIRá^‰,Ž|ë•?7L}š—ÙP?¾p¤Ü‡q}ù¼!3Ë.eåŸW>Xõædæ¡ÉRNHmEb_.µìïñfq‡š*h¥bB‡Ë“{’ès*æ]%«úuRL£EÏâHô]¸NÚ”;?W¿–.¥Šjö¦ë¥}Yœ—² ‡kUmTa ëfkû§¦Û®_í«’ûå=õ}2gk–]]*4:nM¼ß»M?ˆã6£MçyÎñt*ó]Ù2Ž×©Âè)#æe¬¢Ê¥KÏ,ïMéë{TI¾Æò¯XÜä\ÚÆý&¹O÷ŸËÙwQò«*±/Rͯúª§K×g…{ˆ§åêìʼFÖí­{UA­·» ö=”Ž´Õí« c÷vŒß£—ûrsxî+Åû‹‹>#µ¤¾†bŸLâØäOSXÁŒ›®ÑùGS—.¹œJžèpï´ÙKòU e¢åT¹ÿ¯+â}Ä^îgõ•ß®¿8\xñt+¬|ôŽ“×è‰Eh$»”<.>~ãþ½m\þ ÃŽP6ìpj‡€~ÓïçÌû£ó>y¼+ï—˜Ýß0qF„ÔÈ*{+žž½y¾äÝökÔþÑæJÆðmK6xR¡½uÁ0¶öeÀ¸m§K¾} ˆûdrÞ‡K¬«®_éÓŒ8aù+-hð2^ê÷~,áÓJÏ~µ˜’?9¶ VyÒ »nýM;ØYŸ¯È§3÷Eà~ñÜ·O¼/ØË~¤Ùý Ó§À‡B-¼~§Ã¯ã'0\#ûû#âcæÓ-7à€›'©oíèS5+Lò³œÁÄú¨&î3+úd=R‰÷wGâþ´6½T¶ªöy”xa—@Ýö,ó zDÿÙé»ãs&ݽéIŸŠýhßiwëÐ7Ï—é’ÿR]¹_ê§ã×vö~+ù”ä•ÇM¼¯•M/ˆsr|ñŽC(Q©MzÙúî}åz¡qÁô:3ÿõ•=‡Ò([£ªPæhkÔ<‰÷ǪÄû‹sÿ»Ç]>®—’‡xõìõL8ǶBj$ÇͳÇ_9]£’ºŸ¾Ø©§Õþ½J<ªìC“?ͼ=:”év·-”r`šÜ/“û‘ˆßË%ÕÌÈg¿´oç@üûÏþ~tˆs¨Aàpç*<àçðYWÉz8<Ù Iõø­ÝšáKºd³}íA¡ìò¹Õó{)§ËãÞ‡{ÿàØ¾5¢ÈÆ‚A­ë×>ÍÂûÐü¸=¢cµ¯xrç­;W)õIµ]~nJ…‚®nÖ{8-wèõÅ),”µ ÚÐ~áºé²ïï·ÜýE×»³Z—$Þ?1{?ÙH\?ÿ¾uý&%Ðç6>q«’¯Òî©vµëË–„œM=sÅ;Û ep=^Ÿø¸HìKVZòlHüïÙt‚8£ò™œ,O O¾^‡;®R§7»<]¦gB—·R[GPê†Ua;ãBYZѽ'b;Lc¼>ñû¦˜W—%_§RR>4üªÏ_:â”ÍÓDZö¾ê~ËœÊêwÛ]Ýxu*ã}ÛZÚ~R4#îW(úï•”û&fïûjçŒqª—ਓ@¹Ú­h u›Àxýïþ¦æÚ¢çêI>—ÅHô‡P›½N˜GìÜHKû>T!N÷‘o\‹®a^{|{zõ¦qßïþ9”‰}eïÏÇïbÝþ,õéã}Ã¥q•ðùÔ¼>÷èýDr¬ÕÞ¢^—F•ö.®²jë+ÚRðª75ÚU2סøPæ“à?É.>@öŸä}b¹_<¯ß¶<¯bU[VhNÜÉ›D=ž]|œF¶aÙòH6¶ç«È%Vo:Ñìý¯iËñ¹_>Yv~³@ÆÇüE½^V¾¡À¡wâup]ú¥» «–D¿ÎkÞá°o=žü6<ÿ£ lØ ·Zñ¡5wòÍT†2›½`® ÆûዾTe¤þ¿7¤>Т‰×-—Ù½ÈÇIôöÀÊ‚/=ÒhàTçC}[nb]´ç/âCF³ÐÔÈn:]°íá Ù?Š÷áåù'Öñ~¢Ãu8}®H—$i<™FþV½Q®ÞÌj7©Ðz”mÞ^`ãóÜF&¶s7H~ܵå~Á|>v|]¯È#'Å×kÀu³>ŸYPbH n¼ÒèÎø|Æ'[X`×ñq˜G”›—âÐh÷¶tÞ€¼nY‡ü~Äý¸o÷yäùcËsÄ15¿²°ðø$i|’J]¶rLTme/ŽZ;f‰75^¬]ÐÍÞÈÞµ:±ä¾±ÜßPô!z©ûÌÛ}ÕÏÙŒë¿0qߥiI$ú+¤Òã“g¢X­[{R·¥É½¶¼`d­¿ä)Ss„ñëŠýàëçËûjòqöW¾—ˆ³½æúûSý“¨oÒsë³Tr¼ë<+Ì´5‰‹ñËE/š38¨øúf¡ìœbq¿·ÎÙ'™ß÷ø8˜÷Õäã—ìþ4vU­ê-lEì,ß$ºáÓ-×êTÊÔ9¼ð¼¿éó܈hýÎSZo“úF2îûÆýy¿øÜ‡*tš6!7]Ë£\N·Éã›Gô»L¢*yv<¾?%•– Ñd þ²ƒ]9ã´rxOšÿ¨ÕÎÏKÙjÿ=§L `mæ-o¾ny]Yÿ¼opÀº7·›tÌC5=˜_±•Ï'tòû‘ªGek°›D……a]ŸTª_àŒ÷sÅ.ViI3»~ž4lÝÀžN?/e[û¢™3ä¾ÎÜ熯ӈ}ã‹Ð¡=wI/Ú¹+mÈän]E Î…Ïùµ;&'ѨS†Ÿ—ÕJ¥!Êõ¦ w3ï^Ç <çI‹O™¶?XÊ~zÑ0O’Ï ¹?=÷§í~wÙØQµÞ©.©ê²Ä„:ýeYîo}i^› >7©¨Äé5îné×’èêÏë5,J{' ™}»ùVX½iâ£!^ô9õÐðú]—²ÈÒ³æ¿=ƒqÿ.>þëè+y|/Þï{QwßYëo'êq2ºm+î36‰ªWkXùâ •šú¬¶ºQ4ûb)[wæÈ¡r­Ñ™é¡ì̤r÷ `¼O=¯¯âºÅÉ—Ä^î¯Ë×õlúAœÜ•ëŽô N¢½jŸz…²ººë²®F³3+=â_ØûЂqŸ«–¬ddêùM'-Äxß{îÃû ‹óÉBtíA⯳«ÔùÊŸ6qÿZ·ƒÏü$º¼¼©åɉ+ôÂ#_òÁ½¬Î ëÜQÓ}É‹U»²íÕ"æÐ{Ä[uMƒìÿÍÇ{|}×îçú•ïe5«úܯŇ® L"a5ï—W¨X•ys«}¬âZjéG‰›zÅ–˜¶ˆyaô¼ÙŸ–ûò~±|½k²Ç{o—åF&ÑVç'þïPû¢{} üÌ6ÄϹyb8ÍÜU¼–ã°ÅlÓ2ÅÎí+‚$Ÿ ¹5_ç•vò8,ûûQ#ÎÖI‚CPµª²{èÀqW¨nŸºå¥þÌŽµÙÑá§÷ÃÉf“¹Dòõý£ùü‹ûk‹}‘ô¸rÆÜõ_T_ùEêçôAÕÎÆ#’hwÁ á • ñ©?ws óRSl8é•ÈΙŒL|}3XtŸg«ŒWÜÉì¤þqq â~„¹wù¤z ,-åJö¯³éqâŸ|È{Ä3‰6Voö)²òzuÛ[.~û%?âa4pwਰ ¡LßjÍ;Àøº÷ßæý‰»]ü¡K%]¼9©Ñ½õMd¿W›~ç×[Ê\†¡IZ ³aXž+”¹ÜÅíø؆a??ž°ß‡æÞ©ìÕÑ9”5»çk²_$×k~?æùV"«J„ÓÕâòç™}¾oFœ™¹^Ü»ˆ8‹Úøåù.õ2¥ ÿ2ØÇr€U)_áBÉŸ¼É}ÄœGÓ‹„²Îãz_ÒÖÀ¸_=¯Ç|¾Çdzb¿ûz²ß§M?ˆóleûø~F»×*’u™"ßnñvøAV¥ÈIç@'o2OJX˜ñÖÈfÏì3¾®q_H~_à>«¼ÿ:¯¼ï³M?.Vµø}%‘æ— ÕÒ‚.SíšQ¹cLìÞwæbK«zÓÖ~‚ƒŽ‘-üuãdVÐÀÄñsUâþ(¼ß¿˜ï™*1~}mlúAœÕLÚî³’Èt¿rõGž—iOUã«;L¬a³5sMò¦ØRB#z#ki›_oíS4bnç÷®Rñ[*q}Ὂûd÷ÅR#N\Ûç/N¢W)Sîks™ò·;´¤q¯C,³”âKï§Þ4µEDÉ ëìî¢Ó•ƒ\ Òø·™äSØ@^ÿ=yÛëqÏúv”£o9âLݨ׺$ )»iQåËTÚ¸sß–O‡XtÏîžÅü|¤×md—÷_ˆAîSÍÇkÜß9!ÜÑíÐÎ"’žªµNf@œI{–~Ø“D–ü4­Þ—KTëÒªßo=ÌVÎݺoÖò¼&sD{Œ_gÚ•m:ÙÀD?_gâëÿ¼^‹ëæâ÷‰ë:<ª>>Ä”D›& p—jYCß#lT‰›Ã+ô¡ëñ'?9¶ e='h9h³A¾ðy×¥¨SqžcÆuG\í?į·néG.‘åi­i[seûnF¼Þ؇<ÜÔ ” e{-|Ýxƒñu5~Ÿâþ³bÿ}iÿ×réy_ûã¨ó¿=v‰Î°­³òí<ÊŽ™~º{¨7 r«|´…C(kRímŠªAöWàûâ|ì¥jKµÉ«‹þVçm:¨nU7Uõ_ãv÷ùVÆYÅ}/Q|ÍÍ}w9ÆòÚÔ†Rway„BYøÊ ä2È~ÐÏÎk³¤!q?%^ÿ²ûN:áúc_'vð¼õ)Ü7¿úíØ±õã–ÇÇX‹Ø Æy¥¥=RžUYȨ̂§d”Èo`¢_„›ä/TMÖÙ/nîþAÅ×÷myëëñ¯R^$ѵû*\ù×TâB¹YÇÙȧ¿{´×Kž÷‰}ä Ò<¹²¼nÊ}'WWþQTÆžxݲå;®oûë%,4Òf¤r‘^}˜yzv)Ƭ¹„Åß|FO&×åV)˜µ4å*øæ×ŠÿÃ?IÌ÷O²?;ŸoÙò]øœjôðUÑB$Ø\ݸHaužVø°™±<³ºï{àIÎ)ºž»‡2¿ 53G‡Ëû\|‚¿©³ç½ÉªðVÅ}•¸Ïµ-ÿ§áÐó'׳ó#ù­Ç/ÒµLÓõUeÍì¡n³iû<©É³êH%#;Ø.ÏÝô Áòü¯ws¿TûȺ^mÚ¿SqM޿ߦÄ™~³’ë•ÆŠ{9#ߥÕééöÕô:3k°Îá—ækŒìƒ×‹„2Ï‚¥ùlciœð›Ï,Ÿò|ãóh›.p}wWyk[ÈÈb¢º¿H%š,~p«™åfYyò}ñ¤{v¼äedâþ`0 ¨xïè—%­%?’–’õ Õ2‡u ›½PHãã–_ùAÚÕ°ª«ÙÊ’…>´¿½y¸æ"=<»õØ—f–oXÆ»W½h~Å&+M½lfÔG»Í‚Yý÷¾«¿»ØšÚ42Öi´«<ßF› 8RIÕ€üù§÷¤Í±iP\+Q'ˆ³½m·©¹-t-¬w±1õ.’WÄ –÷T'ؼ3£²êó&VKp¶2²*- ïêzÖÀRßÝqª˜ÜX®ß|½ˆÏïøz›8¿«)êq:Ý›Y?ócÍÚÜm‡µèEZÓæláZ¡'ا‘—Ö=«åC×µÚ}égÜ_=~,?àœqq\PYöƒä¾ |\™}~§CœÄùå:Çe$‘°ªíõòE¿Ï?æÑ V¦kí›?ûPLrçáŸj‡2q=!˜q?ažO|ßF¼ny\ÄýªlºAœƒçjÔßò&‰´÷[Ýrê-XíýFÛ&–}˜ôÒ2|¥/µk™‘ùvw(˵Ígd@0KÞ¶ÖsD§Ä}ëù>Ôû—Cº¡„%nSjW$âë6Ý Î:ü«ïï%Q­ ãµµÛ/Ðñ ­Û¯‹eeãZ>¼vÔ—<Å¢>‡2›ÝZ®`æ^ââí§–Ri*éó•¼ë—Ü•¢´‘××mºAœãg¾w5‰Ö]¬…¡ÞŠm·©sÌ›X¶æÄíº¾|©k»pÏEÂØ«çíèÏAŒŸ[Çsh£jŒwÙÿ¸ØŒ€È~Ä6ý NQϬÏÏ$Q Ì"æé/Phíª¡iâXx9ã„îw½é¾Wáw3C٠獵J>A®²Ÿ•x¿z#ù4¾WYññž‹ºÁWëÒv5­êÊ3Çù?ŠH’üJ.Ф•oŽÝYÇžÚoË]4Ö‹†7t~¾÷×P6jЈÙ?2¾?Çï·ÜD\ÌR ³-…{=âû 6ý ŽC>…j™_}ßï½k» Tu`å›5îÆ±È´=é•ó{Ò¬îÖª†±KKÅ]¶7ÈësÜgŠƒ¹¿ ¯d÷‰R#Îõ5e¿¨’(¯máþmØyÖé×JñÌ»@bù!W†çOφôÆD_$ƒ¼žÏý{ø|U\×´§»î#Õ¯ÿÕ¸^‡8ßýºå •ÁüÁÙùB£Ïçi™gp¯ºÞñlâ·zÅŸ ¦>e›Æ&ž7¾t ÿm~Çó‰ï×mñôQƒ,•¶üÙ¬ŽjyŸÂ¦Ä)i»$ÒÐV§«ô¾~žöM1Z÷lˆgëµxw)r0Å6(\¡XÏ0v#¸éýó‹ l‡{ÅÛבýŒÄy]Æïü¹÷GÙÖ¾©¨ĉ ¸weóõDÉø<9»qJôxÆ}ÂÚ¾¬1rõ/¡¬öŒ€]'áÔÅC—¹Êó¾ÿ•’ì•¿é—Òø«¥ìGdÓâܨ»~§rw"U¬˜\&ÆxžÆìÖôS!­v?t{ƒ‹Ž¾<¹Sv+æ]õ87ÓÀDêF4ÖË£kÕ1 Iœ?’÷‹ÅyyÕ¯æ鈣«8ÁG3:‘Rýš¤´}žºå÷CW]›žt{ç”1::º¯P¾Ô¡Ì¾ôû7­Gñy ÷t•ý•¹?M/µ¬j1Ïi·§ €ó4ñÆí%¡ØìkÉ?\¥£úóï/ïÓ:Œiá<ãÅXƒ¼îÂ×ßøýS<ÿðYÅç-6àúëO /$‘¦_»³çd¹ó4ò´ñ\ʽöéFʲµ:ÒUî Ømcåêý°¥ø:ã>P|ýˆç_¿}â¥}A\âÓÈâ©n‰”Çx #éê¦S®Q•Diþ9„Ní-lE±aU•gž6HóìæòzÏ[^¿øùq\,Ž[uˆ3.¾ÿŒ‚‰4©I§ªwR¨b>®s¼Ùëíö:>DýÊïGž«µfóû¥’o¦]~¼cG®ýîòúãÄÛ*‡&=“$»~U·Ìˆ³«á²k ü¨Ì›Ï£ç¥ÐÔ1OL5ó&±×s Ýd¯%»Æ Êj½LÒ½ñþ|¾ñð¦ò¹¾~\£Uä£ëìë7é¸þY÷"ÝçPõ¶-¦PLP¯©/$±¤åu§vé×—*†ULºR;µv¥Î=ÕÀôË*GƺÉë[ü¼Ž8ßÅçdÛ¿ûú}ØÕ¶ªSZ ;‚ ”>g×ÕäN)T´ÀìVÛ¼’Øþg#¢òéK«ûu²þe)kô0}eŠs°ìcÏýØù~*ïó÷Ççï6 N{[M ¢mƒU¡ÕR(|aÌJF¼ŸÆÉÑêí}©¹b¯mW—²‚%÷†xÎ –|g›“°‹æ×ÜêN4iš®÷Ÿ?ªæamw·ÉWþÜjÄùÅÞ§çîŒx*a[˜L!·«‡»›“˜¾Ñªâ_|µ4¥TÀÏ–²cÊäy“KÍd/;†=ü j.]G%ûós[ÜK\wãèGØÕó½O}cšœ{޶Ìzäø:‰=ïPúÁkuÊý̱z>eLÛù:Û7“ñ׫|úãèõšÉç‚øúÀÙ¸·N’üÅpý+c«þTho<ýòØߨ9zÓ¸ï¬Õ-¬ÑCZíAéV³C-cS+…¼öÏ5‹¥×<üfÕ Hœ/¹Iûc¯eßç6[ÞÔjÜåùM/ˆãÝìv·E+â©Ì‘€>+«Ÿ£%wçv¬ìkaÍ2Nn8„ì>zýË2VÙUlóL–zÕ䫨K|ÞÇýßÄÏç³Jœï¹ËëG6½ ŽàX!0ž¢sÍ88õîY²Ùoµ°k»SòŸJÓQ³;UÜß/c·Ó -Ý63˜ñu ¾nÆ×9øúª¸ÏÞD>gcÓ âì‹tu¹èOYâõgvœ¥ú¯âzŒyja¢Ï±']­ZjÑ~¶”9¹|6ýRãçäÄ}×ßüÅøx|¼°Œ®–óݦ›:Võ´†êáQÝãÉ<èí“®SÎÒ¾E»~p;É&TõìµÑ“hñKrÝ¥¬ø¨_vŸ›Áø¾ßÏ9ÞV x8{Rˆó•x.¶½|®Â¦ĹÿÌakÞºñ°ô§–盟¥ûƒV%å›r’•s 7öÃ8ìqÿéë··ZÊn8YÐ=uãç´øü'ø¸rw‡ƒUÆŸè@ü\¤M7ˆ³äóŠ‘çsÅSÒ°}Aëò¥BÎ. dæéÛ͘9„.;Ùè²iK—Ìç3MWòù¹¸üXÅ×ﳯ[èp}ýÓa{KžŒ£ïŽîIž“|†2KO¿”çíI&Þÿ’µÄÔ°‰g–1›ëŒéL<ŸQUþþùú“K¿ˆs¹‡ÛÉ~}_Íûëðu»8*Ó·BÑ€3ôfy™¼¾5’Ùuï÷Cz.ÔRVŸ'ñ÷õálK©ÛšÌQŒ_ŸûNóõ‘r¯o»üp´½ ”×Kx]äùÌë&?gùÕýqŸ}_}qî8Z=)ñ]–ÓTmÂ/žÆíÉ,¹Ô­û•Ž÷¤Ô]ùM—3›Lï0>Žããq¾|O>‡Ê}iù9_›nêZÕ½zû9-?Kâ8î4e{•0ëB2+Ó6&vež^T¡N“çË—3»Þ7L Y3dÿa~NˆïÏßï”{ÕÐKväî+œtl.û¶Útƒ8óµñÑq±Ô¿ÆØöîµO“îÂßÒYÉlïöCË}×›ª;ãÙÊpf¬÷K®—ùü!×'ÿ~Ö:çšöc©Üô¼ÅÀñ7ª· ^ÿlºAœ w‹Ë•ðŠ¥QTönÜÓS”GRqVÕS,rã’uIgzS¬0ÍìÎvL{¿¶jý@y¿€×aqýç¼ä£ž›Æ¾ÌX—ûTKù‘M?ˆÓfS³µÕc‰9®©¿1æÝwþÖœ§Ø¤e³®æQö¡Æ |¶V¸ÎÄùvãûR|½‰¯g4][§‹É>“Ùç±Ä9W·cÊë m—·ëàStzJxÿe§˜:nuCýÞÔ»ž]¯qw—³Î|~<.HÖ)¯Ÿ|<ðãá~gfû—–ë÷õµéqf.91¿ç®TÚfœ~Š*¤oŽ/°õ«V½wûúGzRû‡Y%“‚W0cǽ›ÆEýs\¼ðuoþ½ðý›~çJ“|Þ{¦œ Ïr?. ©~ŠÞ6(ûZáG4®ïžJ›r÷ Þ¥„«L<¦aÏËp¿{q=ƒ©;s~åˆ,ßÍ®ÓtÄñÎýÈëbÇÔ1æÞ˘¬dJ.ìóªLžG4]Ç£¯Šv§6!5‡½X¸œ]ûnJññ‘øz‹Ëþób={§âçqøù>›~\­ê­ÆÏÚàx‚lv¹)ÉTúË…Oï>ZifËÚž–¶2úÖZu¶ërv~rç›áƒ ò:Âfƒ9ã JüóŸopßm›~gçXÝøM3µhëX¬ñÖdêæ“¿SÍ_­¤ûXâ§&q]i¡â‡Ñå÷.gÖ÷ÛMjj`\5z5.t?`V­ûµì—CŽÄ÷²njÄ1=:¸9ÄLuz_0rF2­¢Ÿöj%û¼gÖv»Ø…ZMó2eÊ y=›¯Ïñq¡8.ˆ—æg޲ÏîWëgˆSxYI­]O3­°V½_×dÚÓ83öž•ºÎؿɽ}J+"œ°Ž`¢ñoçløº¿/ð}q~À¦\IŸý›7•7Óœª°°’É­çæÂ¾7­Tª¤æUƒí]¨Ï%20‚yÞºÛ¹Ä y…Çù9á¯Ç7Õ¿Ö â”k(ìü3òtOum}÷$WÔlL¾l¥Eþ›ŽÖÐýÕ£ÇtîÁF=:߫ۃ éŸ3‰ûFNòùºÓ–yjåΔ×ϳŸ¿1#ŽGãØ•º2êSû‘KþŸOÒíõïFö=c%Ï’Û.g­íAg*\¼ÖàÀ öЖÆ¹NÿýIi]›žâÿ/æs:â?62eÅ­ã$®'Ÿ×⬔۵WŒËÆÞTpDfé+‹V°Jã–l)Š|æë¯|¿“×—eTßRøÄ[Ïïç6ݸYÕ?L¯ÞÜï8-3½îq¿“T·üЙ“Z©Xÿʕ׵´ñ\û²ãì#ØtE®WUzäñ'_·äûIüsãç,Åy¶Ç q Ø £Ôy§^ls’~ˆŒ<ôz›•¦Ž*åxöå²û¡û†Ã‡"Ø‹Þ5Kn ’Ï ó|-_8¥X/kªJ<'óE^È®O5â\ßz×-wø12õ;ªUV:I]»ßûÒfµ•zu[à[°Õ`²m¸®dÏ—ß3 êÈøþ¯'|]ƒ?øºFöóœ:Ä)¼äS­å•ŽÑæmëNÓ ÝWìË+õv¼YcÝ:»¸×í÷mW2ñ9„@ù¼0¯Ÿb¾ÅÉãÂìû¹\?nMdÇõGéÃá`³…Ä´Þ×h†•üÞŸrï{kÝX¿àÇ…íWÊ÷µß¿>äûÖofv›ðª’ø}Dâú¶ã«Š£ä>'¼`íÚUü|¯_GY©³¢œo‘Yƒé•pºA»’õo÷¸EøE>N«Aâºh%yüÄׯøzCöqq¿1Lµ,;B«ÔÅ('[hÈœŽÅ›¶ÒëîO6.² ¤ö_ÕÎ[ɼ޿}¢Gãë®ü\™¸òTÕçþÂñÓ\>ªÄºÝ‚øýզęìÝØÍùPª*ýÔÆB?¿<ÒF£±’v쥂ßõ§ø=Z W²Õ÷ÚgÍo8ƒñõKq½ÉMÞàó^¾ºÏÔÃ<ªfíÊoÖ¦Ú»£šæ²P³¹©«z´¶ÒÖ;_rÖRߪY=hì*¶£¢ Ì&~î•åù¯Ë|ÿ„?w!æ»t^qJö¿òihÅÃT¥ù†ÔõñI¤™{8æÇÆVz¶|^Òã½)&ôÉOËRW1q3HÞ§ã~ç|°­_M~?{iRó«ç8ÔˆÓôAó¡Cö¢­S“ª7 K"uÛioík[)%¾Æ²f•zÓ¡O¯Nο³Š9σ¡ãoû4üuóùçøàR/V±#¾ÎÉÇ6½ Ž_óbñSÝQ‰ƒ½"G&Ñ™ãÍw÷­d¥q–|~êMÍN²Š îåe lx3a&á.Ýgê‘xÎ\eË»éJ;Z6gÏžIÛZÊÿ¿M7ˆƒ?w=l¢Mö_¿9@©áY›éË¡¸à'÷Ò\·Åê¯×R¹ƒ~l¼šMìÖ¯2>Ïëf%yýN~Pñû]öy›â¼®paìü(×ßK3©d;ÇQWRúšb[ŽèKEkŒûþîüÕìÒÊé™·‚äq'Ÿ'òû€¸¾f'×›ì÷55âˆë9û©íÁÁgÍL¤rBá>ûv±ÒþB}iˆsÌýˆ£Iš¯ÆÝ ùùN>Ïáû4â¹s;ùù+q]ÄQÔâTß;rú‘*û©[•;Ï„DÚø(tǧ؇4ä£éiâ¸>t(®j½ Á«ÙÀQ'þí~ÀçÏ|½›¯wðuqÿ©ˆ¨Äi+<>²1†vÍ÷_ÚÝ'‘n,IÎÝÞô]ÿaB/çÞ4ƶ¡¸š úñËÄÜyßGåãiq|›¡ÇÓïTü¹dñ{,&êq Î^ëuÙ)†–ÜÙ»>¡G"©íòä/ºû!mß×+°'û̹RÖj&¸ÕŸ ŸSâyÍÏŽîÖ°ÂŒ3ïTâü¹³´)úÝ›G|žægJi=ú»v-©Òœ¾gm~H?žÎøP­Dêôý½7ºk}¾°Qu2€ñý1~n×¢&÷*ïõN•ÔA©¶–ô#ÖtĹ<½ÆÖU~¦’¶/ ‘vŸ¼:xõCºþe€³‡]zzbsfæì5¬aPç‘Óßçày ~/'ås^âþ§ûW÷9»Võ…FÂ>ûèͬÃj%’f­öeß°‡ÔàûŒ=—†÷¤Å tj¹†K¬ÓýXCã÷ßø9`¾Ný¸â¸åoÙ~Gÿ}TFѿ誉”’9±fÓ‡dJØPypëÞÔjÀÙ-¦ík˜·Ó¹:·?_××¹¥ñçyÜ.Žç»Ð鯄èšÓ;ñç[¨o= þ= ÖÛ‰Þ±vÒûä=TB$/ ÷¿Ù·Ó!‡¾Ùû´»K}Úï·!ù*zH}Yê å/y+:eëuÇ{ÿ^wB_•  •¼³sòVä½ÚyïΈlž;¿÷Vü»ÞÙ9y+º@l!Ùú­ÞÙÊj¹æÿÙsÅP]8…ßý·â_ñÎæž;Ô«=»çŽ%‡žP–zµÿ+zw~«…ßj¡Áî¿_ ¤÷‘"|_R?)«ä·øwzã)rè—½‡±Zêaü{ÏŠHÉ{L#õ•â=Ü-’·¶^òsÉÖ[Š÷ýü«ý¥\ #È|¶%ŸÙœüÇxcÞ/2›oÅïýÇþ®ÏlNþc®›d­ä3ëá…+Ð@€&á¬3~¬@óþcÅg–ûVüQãì¾)9xl§äÐÇø_Ñï[-üV vÿýZ¨þ—÷5KŒþ£·^PçÐ_÷>æýõR~çeÁ{ e©Ÿ;÷aü¶5RÿcáEj³õt×ÿÍžî¼W¨ Daü¯2?`ÊïÄÞ{)Ùü,²{•ý3>´9y•éªÏ"ÿÖ+Tð¡Õ@x¦z…š€²†pF;gï²äCë"ùYüYoäì~‚ý@ p‡ #%Qgï‘ü¯êÇ÷­~«…»ÿ~-TJ¯3Mø>ŒRxÁ¯18 1ýAP#A£€IªéÀÉ-ùpëA:ð@âFÿ®g²‡Ô3ù÷ÞQRRkAt¶^ð)’·Aò3sE²‡€ ©ç¨ 8!ñC@ÐBfàA&ÐA à ADH¢ð“|isò3ã}“3€‡Ô7™û[üÞÏìïúÒæägæ±E€L¡.J¾´.žd­‹ð\ ~AÐþŸÙ_ñ¥åþÔ79»¿EZžÜi9ôMNjˆ< ( t=H|4PBô`ˆßœPB@Ð~«…ßj¡ÝÿŽZ袀Ñ_ê/x:êA pGbF$§?Hj$iP Qõ x a£€I«éÙú.+Àz©ïrvO ÞwYð8 ‘úÇs_G!ÁµRïeÁçL—­‡¼áoö×Aà QDü‰Ï™?°'ˆE/õdæ^Ù}ÎþÛœ|Îü€¸@pFÉÇV[Mè <÷2€¶ºðl¨ð¼#~÷>gÿÈÓVð9‹ø=š¹g†Ð£ÙBöi@ AG‡ßõiޏ?Hj= ( v=H}4PBø`º„|«…ßj¡ÝÿŽZè"½ŽtáóF2FJýç¯Ç( @bêA:ð@‚F%’Ô¬@ƒd5I>Þ`$® (‘¼`$qt^ђ癘²õ¡O“¼¼C$ß3w$»d óe$½¸ ñ è p…"$!øàADˆÂ_ò¹ÍÉ÷ÌB‰™@ÁDgóÓø½ïÙßõ¹ÍÉ÷L]EèfgœŸäsë áE€ÌjB ¼'à F€ÌêÂóÒ9ûžýŸ[î§¡†h£€ÂÕ×ÎŽàõýÎO#=ït †°£€âÖƒtà‘G%„nV àMÀ ¢@ ñ› €Æu$æ¦ðó[ÿ¬~«}ÿ9ï³eÝûwÔ<á»S ÆéAŠäïhi@Œ $¡¤$c4P"! À*Ì…‘˜J$¦X4(糡é’šX³ù: >Þ:`’|Ðü€¸HžA@‹Úf.Hn#È:$¹¸"Ñ#¤d÷)À]òµÍÉ Mº¦)ÀµÍÒº¼èÑÝíŸñµÍÉMðµu…¨"$a逸@`Fé"ôN=m]$OÛœ¼Ñþ‘§-÷ãp€(ýAš›p6¯ ( P}6?Ž( €Xõ x@´Ñž„›ÔoP@Àz< äh „˜ À 4µ 8AØ! h!p3NÏé·ó ߯y¯Ö}ãýëê»Ç*|žHÄ()Èh DR€hœ&à„ @‹D5K~Þ! h‘´fà„Ä @‹6%’ج@ƒd6I~i~À ”Hlƒä5$øz%Ï45=BJv°W$}„”ø~ ¸C‘À"ði@ 1D¡—¼msòLÓ@$Q’P´À”Œþwži××6'¿4ˆ, 8@hþ’¯­{5¡ŸMt~ ¸Wú-ÙÙè÷~iÅ×Vï*ásJ$Ÿ¤I>‘! x £ iV Abš€’3d ’Ôœ¨! h°&à„¤5€tàäJ$°X%µ‘Í+RðôöfÉgÍX€+’Ü2…ˆd·W$|„”ô~ ¸#ù#àÒ€ZòÁÍÉoM a@p…@B€Dõ;ÿµÆ7'6Á7¸Wz÷о¸~ ¸Bl’àü$_\WÉ÷÷^lÿÈW aFÄ©wž#ÂkƒH£²¾p^8ÛßA°Ñ@ Ñ€h ^Ä«éÀ"ŽJÙ¬@A›€D2€â6Ü2B·¡»AÄ·õ½ÿs\Ý·±Ÿ‡t áóB2F‡\¢¿¤ 8!1C@Ð"AÍÀIj™@‡dµHþßF tH\ pAòA&Ð!‰ÍÀ ‰2€ mJ$µ?°'$w°Jà x Ù#Þ¤w$~$p@òûƒ4 †¢€BЃtàAD%Daür5‡ 8A ! h!”hà±è€8A4< žèÒ/W[YðVÁg‘A&Ð@lÑ@Áé%¿\5„ >æ"ô&ÅèÒjýãð; €(õÑ/׬@њ܄gñú@ÐBÀ¦úÂs!øÈÈÁ'<h lp‚¸C@ÐBäfà¡A&ÐAðàú'¾ÜçQÈeá‡×ÂÿDíãµîß]×þݵìêØÿO5LøìLÀI8ˌҠV™€jUÈZÔ*3pA­2‚L E¢™ ’Í2Ig.H¼`$  8! C„³ËHB“p6IèŽ$ŒÑ¤w$d$p@Rúƒ4 FrFTÒ‡ä«D²€h´&à$œMFâZÉ H`=Hÿ'|pP{B@Ð: >7¢nZÁs×’¼pý»‹Ð߯5Ç_òÁu¯!ô.Åï VSèÑ÷×¼p= ¨h tž{ÇëˆËTOxޝ XB3'ˆ-d-DgJϬ@š€D2€b4Ò2´ü?Ûoã°oã0ƒÝ¿†i¥¿—)|HFP !ý€¸ 1 è àŠ$Õ¤H^ßRÒúàŠäØX€ Ù2 mNHj=H.Hn#Èü¾#A&Ð Ù£€ ïÒ€‰H~=HA4PB`œ Š´‡¸@ F tŠ ( ?`.M° ÄcJȬ@!™*þÐø{ h+ ~©ø·–dV|q=à ‘EHBÓPVüjp=àáEħwúçãwa4P@ˆúBïcü‚ŒÊZBO¡%~qFe¡/®4ª©®Ð_¯d­›Ð?¯Â5‚ÌzÂsàÂ3ÍøDl™9xƒg-„m.·d]“?÷ߎ¾?Hj€( t$Ñ“˜ïÂÏ¿böWÇ`B=ûwÕ2¡vý«k–Îî·ZõGuêßY£þ[õIxßfà"œ öP‡ÌÀuÈ2µÈ\Q‡"¤›§X€+)èLàŠ„2‚  Eb™ ’Ë´Âùa¾‡Är@bùƒ4 F‚E’LÒ’-(QW À 4H>pB†€  E"š‹p®WXÓG"€x !£IiV qÂ5œßm\dm%ÁG×@ý0‚ÌÊ‚"ÞpŽõÃu#Z˜Ë¡n¤U¼Tðz5C_]è™?’^_SèýŒƒz¾¶ÐÇ¿ƒ¢²®Ð—¯h “«Ðg ¯dm=¡^„’´‹¸@0F tŽ8A‹æR_¦%¶ÓXîð ƒÄ‰àÄÚ­Ҡ㔸šëÆÝê?MΆµë{9Š^]]D¨/õi£>ŠÔè‹?cdѵ‚± Û9"~µÛifdîm¾5íS(MÇ ¹Ë71Fq“aÉòTß·FÑdQnùÙÛ ë7nÆùŠ˜òþù¬_Öל/Üm-¶Ã¤TÖŒƒ¸# ÷™½Oƒ}+\=³…÷¡¼}›ýc'C›wÃí|£ ›o@hþë£dÆû~Pÿ êeèßldS&Î}a^é×#0:è\òrüÞZÞ¼Ññ>,T*ªm–O‚†ŸkÏËÔF“§5ã6Ô $Ôç‹úÒ: ¹9z[¢–*ø‹°Ö¿ð0l1Ù()ÆÏ£ŸkqZ>Û‘Ðå® ä=®÷ûÝ7ÑÄrߪ-÷ö÷­Ïù ›ñ~·´Øœg>÷D¯lg‹xňè!‡ …@ñGú³4È~§~›ivÄâC.ñM~ƒ]ÃY êh²±ÝÁG,%Ô‘Ö÷ë™w×§¼Òq~u€æ?±~Šœo'¶£o2=¡·ÅlÍOƒ­× M%ÕïÃ}‡]…ùcáÒ†SaS·G“–¶¡µ&/å|‚[ò9ÁÔ÷ú¼ûÁ WÖáýZ ë@Ží4Ÿ¶dúPù~˜ÌÄZ’4`\Ž}*…ÊaÛl“rÇÀ•ýöNPF½±××ÜS6g Ÿÿx~]ó›ÚJ|nˆa^•Ûéº0äΊ¡û ý&;Ó »ÓÀ¶¶ÿ¥´—¥pºÎq'l,èbÉTפhÞ¯‘úüÓÜ?ÞOáÇÄ_7WæóóX«ú¬~°EzCÏXx=ƒq|Lƒßô_¥àâNò7ƒí‚‡"£}ÑÄjà8§?wÈ õ¤y{Ô¿Õ"ç”m®ÇçQéõƒí0â6š¿Úz9/< ŽxïS?¿Br#}&YŽå;ÆÐ'šdÚL¬ûJÎÿ>4Ï‚õÿ¹/ŠhälìZ¹>¯_½nlñ÷œòö·¶{ e²nFîö4è仾YF)Ø×0k8²ÈR­0·[EÂÓRK‹s¾æN°¾R=¸v>q>}õ+øP ðúå£n™¿a7쨺3qóipýü }Z) ïS¥çð½¡î¹´”%u£H½ÖÑ™sä„úJÚãÝ"áRO >Ç4O‡úR]éõ‚í|JRy?+Ü޲6žëñwß7£ñM§S¥b¶{ôcédhËĶŒ"vÃ'· BýiŽ4ͳ >`ÔWM¯¼¾ÞŽ·ÝN¨^{‹èЮ4x”êy9¶b=†Ù|j3*ý¹Ó¡õÅ(¢÷ªDX?¡î@}Úh¾.Í9Ðë¯;´þÊNJçm9£÷{EܺÑ("K¡q²`‡ƒé¨¼ìÚî·£‰ jd‹¿/å}‘¨&­WêóVÁ_¯Ï¤cýj —ëM.©±2 ޝ¯ûÄd^·Ï &U'ƒ“ͱm•3bÞT[ÄŽùêH}ižûïoDT÷†~|*l',ªÁi«í`q]¹3hy˜Oi¼T¶´´ÏßÅO˜~»>ôY³¤a¨kÃyKyŸ<ÖÕ‚÷a¤þÌ4·Œæµèõ€í<½R%åô긖Vð¦ÊÚ4H-~|êüR¨SëKÏßÛN€6«âË_Ÿˆ!ª‡Öîsr)¡÷Wz¤ŸÇ£Ù”a•Q8ª?½.z—‰±:džƒ³¾ƒHƒóVëk'z”BïcN Oít¿v½[Šɦ}탭꿴>q”fËìQÓZcMG<|-¢¹¯ÔÿO¯l‡Í! ƒªyS3,¥AqßÖ×׺”‚yçÔl÷¬ñÐñó³àšøû'l.&Ï‚õ[¥ã Öÿ÷ºˆí¿^‹FßÉù0¶aÇ >ÃblGßKm…#~C«JƒµåÝ–_p*…Y޹îއñác*»Õ!ò¡Ç\éOsßò~˜ô{£y²Ô—Þp\!ÁvZ½͘Çûö8?= Bd'ö5ì_ kŽ4xwo<Øé UcÈ´§[ÿ0%'´^©Ï­šgBóÙ*äb;çÌ[/ì·^›°Z˜™‚–]ó{•“éÐô× ï–Ðר(†f~ÖÆ4×Ä‚×õߢ9CÔÜ0GA‰í ÜlaJ³ †36ÏipÒ­­àr‡Rx÷k‡^»K&_B‹¦þˆáó¯iîÕÛÿÞáórhÎíôúÁv.}ª;x™h”©˜@¨4¨²øFüÐ&¥°-´Õ¶îÓ EÞnüQ]4q°ìµ|㣠Þç‹úUÒœRös½Ñ|5C`-¶óhóõÃÇùÃ,ñ¦e˜Å“Rfš#»\!À3ÿ#çhÒmäýu£Èù(êWÉŽ+ÊDŸcŽI쇾ýþ¦Ú4…“MEýq<¦|3©ß¨PØ¡®²àmŒÌ7›ÖûM ÌúPçbPowÐÛ·ˆ&—o×"çr~mùû9ëg\Æç SC:ŽÒëÛÑÛìµr…ÈI­^¨>·ñ+“•€=t ™œ¬·øòíÍѼøéÙ>×FÎïÌ/©ßðöZõCTãsç }`ÅBªÓÁ¤Û.sgØ\ç^Ç÷´¼VWÏÝÈŠUxÀ©„ L2©º¦ñ Ý{ ­#êëGõ³:ãêñc1_}M ïÇlg}ÏÕ}úoŸMÜÙr¤Êè 0cõ»ð¡§K òú黯3<À¢MýUÛî yWÇ´XÐ3€°ß;'Õâî‰"š F}œ ï rlg¤Èþ¤§j1©úáˆ÷Íé€I ˜¿»æÆŸîûÜ8<àÒRR;­Å¼Kø|ösÕå}Ç·¿ŒÞ×úlm>×ÓÐ?[‰í¬¿®gÙd9 Ð3^€U[æ–w )KŸN›.»ƒ°kñžj‘;ȧ³"ï̽¼ô·ùÈtü¥Ÿß•÷àóƒôúÁvØ|ÒÕd³lbù®# Áû‹É%`'˜ºŽ¸AïWºs#vº‡¨Å1œÏ}o ý%­kêN}oéûÑëÛ12î/¯Ýb=Ùbìk³øÒ`ókKà´UꜥuÜ ûbã+ÕwZ»‡fÌ^Êå# y_?šGBóDÙß­S…Ü£>eâÐÀϤÔÍDÖ®Ÿmåç`Ka‹U!.%°OzðSôWx·òqâÆ«1䥺Um»·K Í9 ¹Pl~l6?Ï Ÿ³Bî ¶žÚÿé²çÄŒÙGª¦C@hÓ¶›ðól *n=À V_›»;†4ÿeRÛ'A„úçRßJzÿ¡ã2Cÿ\1^ÿvÕÄ+q£¶‘ŒùÌÈ>Î 6WêUgdêîP<ªcas1D¢¿¡ñ9ÂtžBýúÙ?¿úútK°M‡mDF»s—Ï‹¥ÃÚ¼.A>‚èÂÄ(Z{ãj­»Ck-wÏõ "¬¯xnü!äçç‹Æ´k§Hû"²ˆOk3TÌozÝ`;ÛÓ]­ƒp<Ìæ¾§C®‰õ´Z%кß<÷cmÚÖî£Z¦Úÿút‘Õ.ÂŽÏÓÁiLá¹Ë÷`Hå:¶ [zÂc¿Zm­bHO}`h¡þù4g”ú\¿œ1m]â›7":¿¡¹zÝô-['”núþéûûÎŽ{2Ò¡çÑ#šŸº-®o»ÌÕ*í]ô|in aûÙ¾ž©9—±>§_x_xÃ\U¶³Öä‹¥ôÊn¢ë™8aÅÛtH_žŒwÚ{Tù壢Pwøh´Ûvþ‚ä¢ïùM»êÇIë–æVÓœ0šsk8Ÿc;©Oû­N_º‡Ë‹VÇL{iç{ðhûàykQŸ vMYõû²õóÔšõ"IîË3þèÛ…/Ñá¬oê ÑòàÅ,,>‹–ú<¼äµXT!ïR…íèËÔì ™2ÔáZ²澜òÔéÔËœøRô`t ßxíÝ/1Ä*²zœúH¡ã$ê/L}‡©ß,;nïQ¡ŸÖb;¾ûÌÞGø"&ÞÌʤ,L²ï`º)jhŒ¬$PÃöpЯ²&xû˜‹FrÂö—¾èøƒúÍÒûšá¸À¨ÖuÍÛWÓ sùô!Ï(pvnç{ÀæXK¸\¬r-ïpø9¡¹7t|Ms(Üš:~ð'¢>õ†ã¶ãÚ¤Ù™ìVqä¬bú™;C/‚ñø@u³{6ìæ‡Ö¡® ¹õ¨Ñ²£1äØÓVµn´–ó÷7:Ÿc×ïñŸ‡Î'Ù:áÆmØN«7*ÝMŠ#sGÞ¸Õج™Xãø|ÙÕð—|7ØóO‡²S„õc¿ k"{,›Ý¦l÷övìÒÇŸ»"g^4驪šHèøŒê€æk³ëÛoôë‚*¼îØ~)·¶õM ×Wœ=æ{÷2 ´v9ÛÛ´R¤Gç-¿äCÒo«".cS´ȯsQq:Ža×?Šè|ɰßÒb;…ý=Ê_—@nkªIŒ-¯À£æÆñw>Á˜qu¼ÚxÀͬ©µüçE‘ø.Æ×FÇðóZz_§ó3šC߇áüÙȾL\ÛeAÃõ äܨNN³®@3¿/3Ž—Á¯ý—Ú.Èó©ål?ÿ?£Hý` ÿyèïJó‡h¡^xÝ3žktkºœ&WžõŠx~ò t|´éžEšx­ðĈ7mQyHåhâf^eó¼gþ„æcçT±Ø¶¼?zƒ¬·v9[?ˆèýæ^éõ€í„Ýö±¿±ð4Y“·¨öæOW u«W³;W ÆÞ³ó£;d4{ÚæTûhòª{+Ó2'brdJŽÌ¥#¿ÏGsá¯\ûúc«7":`÷CÙñ«ÛÙnÌÌTOæWòëU˜ûæ é‡}E0tÅÝœä—n°¾‘rŽÍ£(råFñ[ÛO‹ùz¢÷ :_¢yìý¨C…¼9¶³ü^ôÀ&ûÏäÙ•k_Þqf oŒY^ƒþâ­u¯gNv\E<Ç6¹'ðçsÔhþ]?c÷o?ˆhÿD×#ôúÀvøæ_˜~–|š}ïÌ/%WÆ™TeuX ?èP+W¸·®wÍQ„I h¾ÅŸŸÿÑ<0š›L×éè}³BÞ-¶íõx¸mËDòþÉ#UŸ6×`Eåa1 ‹àÐÔÍ7[/“ÀTÓÐHßîÑäÊ”áêêöK]£ëèì}¤_o¢¹p†9]Zlç°rÆÚ°‰¤Ÿ~p Üöþ`ìYó~ëÙ:ªª2›{2øx4ÙX‡Ù \Bé7¾»ñy<4—æÒÜMº§×‹¨L¼;iΜÂÅçÈ3£‘^ƒV:‹ƒÂqE0çðk凫Ӡc³Ì±VE“¶ú@³%„¯ÐuÅ~@¯Oózèz#ÝÔëÛ±ê3ÎñÝ9²lÛaI³ç×`ÀüÙEkÁò›óµ5“@—IÖ¯<EÒZæ¤×\º„ß× 9Íô¾ÈÎ+ßóùVôƒíT~òK«åçÉ–‡ýˆUÿëЯ…Ú¦M¼}•>sß œÞÃ,˜nçÆ_KøœH:N¥ëM¹_Næõ^D×é ÷$ØŽP¿±Gˆþƪ«®ƒOá(Ÿ¹m‹ òJ‹]¾®à×Qsäv2ÉõýÔ²Bׯi=Ñ\¸}¿Ì]¶¯Ö'‘á|LŽ×ß×mx?›pBjî\ѵPsDæÚG×/‚¨¢+5•Þn8ÿSô‰·‡¥ŸL›u ô¾AÇóì¸5•ëªñëfì¾›«­ÄvžGuo©"ùÞ’>ªi@êyÔ8úK!´V->ÕÞÔqšk>a;9|»%Žp—ð÷_:n¡9z4ŠÕM+NWõXÝ`;úÕó9*Ò|É›É{ÚkàÐʧ[„ áC†í„Ïw<¸<àí¤ Ù@#ÙãÅüþ­#:£ã šç«× ^¿—2Cä©<4!dˆJj0#…ÐivµÁ¤é 96áõéœHò‡èb ÛÐÅdËå GØ…_·¦934ž~Ol½Õdõeâå¦F«ˆš‰ívӀè£KìÎÂ#ž/n³½ æ˜GG’¾“ÄRû€Å„îSÒœXÚÏмs:î¢ûz½`;ºusÏí©žDšíO˜¿^:nÕ8í)—C_m(…q«ç÷ÎŒ$¶›S$Ÿk.æçýlŽˆ_f×i¿î;TدÁvî{?õ=×+‰ÔÙ·Ë/ì„ެÜ]µå¦B8ZûlŸ=B)àͲóÀæÛ‰`ñ}çÕûýø}!:þ¢ã š—Bóg óh%ØÎœƒ©vf$‘áUŽ#øãø–£6¾…0{ÉÙv}$^píÓíâó·#IÞ‰•šc‹ ·Ò}9ªÿGGÝú,¢ß§á¾ÛÉÀQo`Haf£ŸiÀø^ùÓ9Ó ¡™¨gFTµépâ×É›Æ "‰>¦Ôs ŸoE÷ßÙ~ù®ˆîÓs@l¿É®g)±6G*‰ ”M¾0¢Î è·©xͰBë®ÐÚÒæ‰[dÉõ ‹Èx¿„ŸïÑÜY:¾°h´eùc3˜uºgƒÚ÷‡qëª\¶“6ÁÌæÆý$²H7+ÞÁþŒŒÚâfÛ»Î6}˜ôt•;´xP{CâÁr)•¹!úóó0ºÎ¿ðŒýèMÆÜ÷Tè¸Óp®ÅvôÛ M’ »ÿp˜cSvm ¹ñ¯;œ9ã~tDÇHRcíñ0j1ñ 8.tWôäÏYѼ>¶¨Ä_»óß«^?bw”¶Ø[æ˜L™W>zzÉ'¯©gViÅ»&2w‡Ž²ø:¹Ë"‰Ô7oR¨¿/Ä®›µåÇ74g­ë ëçlG&Zxp´2®6Òf»!i¡nw?i¡M¢½I¥wnQÏħýœHr¤MÝlñ?Bó2iÝÑÏÃŽ×jñëçt=Zÿq°ýÆT2Ño—Ü€®ï œõP ‰»: å U´;F·‹$›Tö(ó#mK¦¶î«/4赦 —w—ËåhÕà· ëfØÎiý¶O2©¯K¹T/V¬þ\£k¦Ö^ý±«™Ÿ ÖëM»fõNú:® ýÛò×K¾>E|ÀîOÙUÌSÃvúÔ4­ÚsO2™°gše~ß xÜ;¦$-$MZòÞý„”Ú_ëåú!‚,mù‡ÝË_zas‚‹EmO U~±y›b ûwzý`;Fê8O&©ýüÞMšÁîcÔšÑKÌæ¤{À ³`AÈ­ëÛ×õ#£^wŠ®w½ÿý :”~ïîÑrå“¢!&`×daĬnðú=Zôµv!™„7 Žù=6—W1Y¡…yƒ¢^êÜ< áþíëÉ“2¼Ô%\=?B×÷èþ9Í…c÷SjóûŽr;±­ZfÀ“LêŒë¶49œb>v¸¸R g˜˜³ wø`ô×°gdä&énÿ}Ñïš®ƒÒy@{qléU»WúëŠñºm·(íc™B6±?ÖV|¾Üù9:iaÈÙÇ×G\¡Á«Æþ©eäTàæ÷úV=æÌ±ë®Eì8†½®¯ûû0¿G …´;ì>yþü›0<2ršU?-øüRãÞ#Whµ#$úÔÑrºQñÝ™¾dË̓Žm]gì 5oUï}~»1Ðñ ý<†¹¢rlç²õÓj7&¦¦kÞOÉŠ¼ dÙÅ–ðûÞÔg†ª:oèáA®]våØ_î¼goþœÏÙЖ¢W…­Vu«\ƒ¿¿Óœa½°››ÄnJ!F­}g¤Ü„¹S–Äe6ÕBì>ÏY¡1åÓ$‚ }Õc]É6_îCO®>»{þR+Z{ööµõsMØúÇë6Ú5Pë¨L!çgz&w{}¬òœú=ª¡…çk5îÏ®0ÿ·A#^½SåÆÉ½ÿLô%4gÎëé~ Ýç§ù¸úºÇ븿ûíÆ#)ÄÛÔçøþv· ¼ÓÕ·Ñå°¸÷Ù‰[¸Á`åû꡵#È€2åpá+_~|BsûhÞwÿƒŽÛòò^ñçIè>Œ¾î”‰+95*ÿL!~·K[¹6®Ì©}¹®>e1ÂÏFuÝ8ͯ]¹°&ûòÉ›¾„ž{y¹°Éç›uÚòç–ØýЗ|î;Gêu€í¼gâ›CRˆ¾ÛX~ ÞoêgvÀ‰wm›·Êð¿Ç^GÍ#»~éKØsq½¹ÜKN·¥ÜºÄ Vx]ADÝè˜E)$£³2 ¼(•ò-qÆó¢º%ž`Yðç­vÅ 2)¾cò‡…¤y5&‘¼4*pÖÍnaÅ=í[è>·Î$ØÎ`ýÒiåm—äœ[p±tÿâMŽ €°µf~ÓaC}Fp ÒÁÜRkºß‡°çoí¸ù‚ˆÿ½çIۙĿ3æç)¬.ª°ºÀvê\´Ó3…¤õ^Vó6T1ëo³¨¢í|¼g%6¿¢ ñ/.K68úð9²ôÜ]G¥¹„zàuUÕ˜dpüžÒÄÊž·!·UŸâÁåùðpHËâ’AÓa`_»•Ïo*È¥†ù%–ç½ ­#ºÌîsÑutvß®wÞ²*« lçþèÉãÞLI!Î=ö^v» f‡dí+¥åCšyòŽ=a|uqîí ²mSpÞ„ÎÛé÷MÏ=Ðy/°ÿ]-VØÎ£âÏæâ÷´nü„»æ›oCæãÜz7#òáЙcÍ,xBOmçv—‡*È£ýâ.Õêyš?O¯G×=èù$ºo_á\åÀ2ñŧìòOáò´oÃć3í,eùpéùØ3:xÀ¬º¦xk'*ûë;l÷{:¾¤ç*Ùýè2nÿ¶:|»¨×¶Ã¬¦™lJ!Çï÷°©‘,­ÔMn9æƒîP÷¢îpmt|ÔÊŒpòÐøb¿5=|ŸÓýuö~V,¢ûtÇP‡blçãÚK«mÂRy~Âk[•L°#ª2®M>ˆJc;êÜ á^˜¾ ¢ïÎøðy”tüAÏ?Òù"ÝW5ìW$ØÎ<ÓC çàçÑÇwÈ„ ºíW~ÊEÞÖ5«&ºãýäpLJ¤ñÓ›ëÞ„®÷·®\wç°«]ùýuéûæso¼Ññ!;^`sIåØÎk3fÀ–BÄIiëš:eBà°•-ïäÁ€1ª5“ϸÃÀñ–ΟV(HXôRçÆ¿øðçRi=ÐùÝjßÜýš·+Ì{”ØNðŽ/-}SÈäRŸ N³ñ󨬙¸öHt>ó:²¥¹, ·9ª 'Ý[·ÿt·МkšGMõ_a߯ëôç™ï¬r Æ®§Æ!™°eÕ;»ÕyÐBu8¯³ØŽiÝrm8É~ìø:zšS¤ãÛ§^^k¿!¢ë:†û›Zlgo«[ƒk¢^ÖìyüjÅ™L¸X5yßÌ)yäÔnÄÕnÜs b _ìýž, ô¹ v}Ɔ{žã¹èÔæöV›ž‹hÎ/»~ÌΧŒ•‰Ã~)”ISÈÿî¿7x’ #†õ?Þ+\Ö„šñ€ ^Q=z(È—1Ì}çó¹·ìºvO`ocðYúâi›¢ª#[8AÌÃcÇ·lgùáÀJçd¨ïȪ9-îÀöûﬕ­\v$f[{@dµÁ/Eœ„Yˆu2~½›îs²ëô¥_ÏUp믆ûMbl§µïÍâ)ÄLùôÿãú®Œâ\è3²S¿c < ÿ­@£ÑÛd­ÒÞ>‹çóûMß®CQ]Òu$Ãó5l§Üaø¡r¹îóz“üïÀËÇ#¼–'æB∋+D»Üaʧ¬¶PNjÕI¸€Ðu9zî·àõB÷¿ û9¶c[iuÉõS)dÌåÌ÷–;ïÀ„½íŽ~ Ë…Æ;·(tƒƒ…–o}†s¹º ݇¥ûtìþóýiûzàõÕ'vºE‘27iôÕå;°ªÓ‡ɲ\x·&øMm;70JSoé³-œ8:´ð¾³€Ðó`ô¼<íÿ©é8¸Â9eæ÷¿ŸŸÒ.=…×|ýÊL‡¿y%Ù.§\ØöhNφE®=E¹[UYA:wÝtiÄ­„Þ_è÷Fs¶éùaÚUØoÄvrŒ©Bn§¶KVF¶Î‚™“Ÿ._d• 9}…YÛ-Ü@ù1¯ßL'±\;Õáhî~¾_ö9ž{"öþY“_ß7<k4¸L¬gÎO!fŽx¹iL¼éy K'ã\¸ƒ¿R—ûn°¬íã»C)¸ý”üz'='Æ>¿Q.:0}CZ»@S ãnÃqŒÛ©³‘6op¼7y¢* õ»rï‚»¢Áñ‹…î`Þ®uèÉçá„mw¡ãRz óœß>MÜ÷[Uþ\¢á~”ÛÑ/·4M%[Ç™Íí±? ¶‹¶Et9~ìÞ¶ìÚ穼²µí7:0œ°ëÞü9zîæº³Ï󼞕àõÙó©ÄmúŽt«ëYPº7oìеwápÖ4ß‘-§C– ë…£MöKºŽñæõÏž?h¯ߦ_XP©eõW"öþÙ¦Âz·Û =XÒ?•°ß[¼*jevxÒ]§<{ÑÜ ²[v¾yÙ,ô0~àæMèú íé~Ûî;^ÿ†¹×Jl'9?5$Ê)•k3ºßo’ &šMˆé|nõ={Á/ß ŠŒÞ~) #©q)ŠWoã /êô|ð³ÞA Î1OEì¹Ú{}^¿°öç¥Ý'¥’¤ñ>lì³átƒÐj“?æ@Øúû–>«¥°mñÃmˆbTçSÇ›.àÖoGÀoú‹ÄpÚ¯ë¯à`8¿Õâuöcfp©¤¡ÝªÉû¼²aýó+®^ÊáÎaJ¡µË¸näæïDgû){³ù|˜ÛGéÁíoÔ‚ø)¿µ‘Ôàóµ ŸÓ1r(ë’=öKƦm}yа°l¨œ0!ÑE‘o¢ÏŒh`6G~~Ú"Œ4ÒwW2¢¬Ç´¿_Fïðìîä^H]þ|°á>°Û98>ôF¥¡©dŽÌÕÙ0±Òõê §ç€çÄ7A3JÅÑ¡c³mäm?óªïÍ%t–Ö?íïwÕ]æY\›ï>$ÆvÎ5÷-èn—J¦Þ½Ûåb¥(9°æX›èö[·¦ïxÁïºÜo¦…“ÈšÏíæðÏŸ±ãòÚü8ñì†u¯=¯ÅïKUÈ9Çv9£«/¶O%Õ—4¸ GL¬ûÊùnµ¸Þ¹†ó˜ËÓÁQ[ïø 2#õŠh󨹄Ö%}~îÏWÔ»/ Çëÿy·þimíTrkyãÀª3rÀn[p¥É9Ùn ¹þ/¡ëþì>`m~^Á®ÿTçûEöÏê¬>°¦æW]î¥9cóÚlÏÇnõD}ŽdCQ íÐZážPcM~Q¡}8þ bª[goþùCú¼!íÙqY5VxÝ—ÂÚ¥Gp^ª<ãjÔQÚðÕŠl{ØÎ™ç Obì:ߊÜFÊs—àÈØ›Ïÿ¦ÏUÐ}¶ŸÅ›c¯¯Åë߸ÈLH!'šÖëò>ÆŸ]6%v|6ªÄl„&“jG%Þ…-$íÖÞéY i¿É vEIÇ·. '§ïŒ¾ÙYFrÍ~­5õ• ~Šö×V×gñ÷ Ã~[‰í0«èûŸ&‘êâå5ð>mcÛ®vŸ,¨r…ÞP¿Ÿ^׬õ4œÌ¾ò(x ­ŒÐç„Ù:íÚîáGÚï/âŸï¥ó+úü—^ØN¿9M.$%‘@ý7¹0Ä)Ò9£ND¥'>xžé󆻸Ý'gÛï¶©ýõû§óBvÜ–ËÝGùs7æ!ØÎô†¶Æ“C’È”žÌ“Þ¹à¾ëmÀŠ’;°¿ÃËÍ燺ƒ·\²©% '>á©ÌlæóçÑè¹@z΃½OWz>Ãp¾fäX&®ºûS½¸ß’H×K³C†:äÂùè—j«î@MGåô'“Ü@ ‰8¾ïL8úÖ»æ‹có {îÁ‚ÿÑóô|*=wSAØN÷,$2ñTÿ'§|s¡}ÏÑÕ£îÀÒáÃÖŠw…êÇ}ªÞ 'Ì®Kß’ù„Ž?èú Œú`2~ûÄJüsgtWiľ~z&ýôLúOy&qŸ‘æX+9ßJÙßä4Äd·j~àYþ­o¥úÙ­LfƒÒ «0‹ó¬Œåò¹Ï^ÆÇDŒ…‹˜b±û!ZÄ‹>±À—#eˆ3  ‚‘rÄÅ B¬P!ˆ‘p^&ŒG\°A~u,—G#ýNÍüÊ óhþ.§Ð‘ó«4ôIú^f«˜Ë£1Ì)´øÆóÄÐ7îŸd6ü(·Uó¹­ NÌR.·õ{ž•Ô³\ûM†µ”óðýQF—’«ÁŸ}àÏ>ð?ÕšpŸf¶Æržq~ã]gS˜õïÞo=ã4?È)d¼Ëc r¹´œ_\—Yã‡hG,ô8Ä‹]Ž”!ÎXô ˆ ?)G\P*Ä E‚è ŠAX£ œ(¤ˆšós 1Èjãòdßä5ü•g¯afÃßer9s^q†¹ÕßË'tä2 3¹(B9R†8ãûôOüË”Q˜õ…J.£PÆe~Ï/Žúö–}“×*ãü3”_CïÃ?ûÀŸ}à¿£üWû?¦v”F_sZ™ìñ?È%dòªÅX ±ˆ çQ÷­°¡O‚+^)¢A„XÄJ.—ñ-Ï2Èìbrü¸LB±W¦¢E±Ðã ,v9R†8cÑ' ,ü`¤qA¨+A¢C$(5b‚Pp^™Œg¹Ê ŸUËå6(¿Émø+¿`Ã܆¿Ëëb<ƒËgX"@‘s9ÕÎ(¶DÀùÝi¾ÉìrþÆ+ÓгüŸd*8¡J "4Ȫ–"DhÃx5áß1y]ˆÆ «šó÷¼ï\¸Ü.Ã|V%—Yó£¬j¦î˜×Ͼïgß÷ŸÿYpïQkÄf³2Ù \Vá_ù 'djà,ÄÂU"&X¼2.§ú{¹…ŽXÐq™]Lnƒ3—YÈäÖÈ‘2Ä =`±#åˆ ½ ±ÂÂAtˆ F¬Q NRDƒñ+… Ñ Ö(…A&k—Ùà÷MfÃ_yf6ü]fã¬ú&Ÿú{™…Î\fƒaf—Š0)G\PŒqˆ Rþ½Ó”[¨ý‹ÜÂXƒ &»ÐE­à„-E4~åßä²úqþŸ?ʱQ±¯Ÿ}àÏ>ð?Õ X£¯y­ZÄñä2ÙÔŽLN+bŠ…*ýƧ˜)Z)¢A„X¼JÄ X†d!b,äX.·ñL×dwY`a˹ÌB&·&±À"—#eˆ3{"À‚FÊ,|b…Å‚è Š@X£œ¤ˆ¢(”œ0¿tµAf+ÍŒˆý&3â¯|Ž 3#þ.»+)g|ÓQd*Ä …ÂeR» àTˆŠNÎeGfw¹ ˆ…ü_ú?É-T"&(V’…ˆ r©eHV/Æãÿ1Eû!Y¹Ô&̼Q#Ö(l'n —!ø&¿5–ü²©•Fìëgø³üOõVÜ{ ù­Lv„€Ë0ü+¯v•A†!“Y-ÆbU"&X°2$ cáÆ"¦X¼~‘aèŒ`ßÅäF¸pù…,ð`¤qÁBW!VXì!ˆ‘`Ñ«k,|WüRDƒQJÄ… C²1 "1EQø!YˆÅ¡4ÈmUq™òo2#(š`¤qAñ¨+¾AÅ̈¿Ëî’ ÀÔßdW/¿Ð…ËŒ0Ìî²F† :D‚bL@(Èàèÿþ£ò¿È1Œ3ÈÐa² …BÆCß [†d!¾Œ¿›ße˜ß*G²þ"G‡©Kæõ?µdú¾G¿÷ÿbŸ÷zÍù óZËçUÈäT;c1& X2Dƒ±0•ˆ § ÉBÄX¤±ˆ)ª¢E±`㸬B9RVçk˜ 8˜Ë)dòqs0Rޏ`Q«+,ìD‡H°ÀÕˆ5¹‚+t)¢A„XðJÄ‹^†d!bfÏà ›•ɰpárÁ ³,œQ ˆEŒ”#.(•àk–Å_e‚Y¡hB"Añ¨k‚‘Q#Ö(¦`DûM6˜Q!V(°¤ qF¡%üÃ\ÂXÄEè‡hG.—Ú¯ãùÿŽ¢ŒC,lkÆ·ùk&µ)ŠTŠhaÆëß VÊ傿³ÆqY8?ʤV±¯ÿ©}ÚÏqÝ~\'äÚ Ù¬*Ä ‹1Ñ1­X”jÄ SÁ§”Ëà¡9‡L6µ#k,bŠë‡hG,Ü8Ă˦þ^Îa9“Ń­2ÈÓ!,n5b…‚è º±ÆbWp/E4ˆ _‰˜`ñË,DŒ"ˆELQ~ˆqDAÄ!( 9¢EÄ(ŽXƒLV& ÌšË+G\P0*Ä E‚è ŠGX ð½ :D‚BRÿƒ,0éwr©e\ža.¢Iø& Lˆ"TpB”"*Ä ‚”!Î(ÌD€â FÊ© ±ú‹lÄò¿ÉF¤Ù>L6¢‹&1EaûõaüQ¯Oü;N䆹¬ÁvŒO×ó}˜zd^ÿŽ>ðßÝÿýìûþ}}ßG¿÷¿Ûç1ß;Ír f²Æ—ÈdQ»`Qªf"ÆâŒEL±@ý-∅‡X`±Ê‘2Ä‹6Ë? FÊë°d*Ä ‹8qÁBV!VXÌ!ˆ‘`?§F¬±¸\K "ÄBW"&Xì2$ cÑÇ"¦Xø~ˆqDÄ"&(¢A„ÍØìWÓç¡( #)G\P *Ä E‚è‰ß b…‚ ùyeÖ($'&)¢A„ØÇ)¹ìi)¢A„(²¤ì›ü2)¢F¬Qx ¤qAªþAîa Ó…)GÊg.{Ú¢“‡…‡8£Xl˜œüÿ2ƒìi ¯ É2>àøž˜9¬A~™aþkbúÙÓJ#öõsœ÷?£¯û¿qœçÈ]ƒf¿ªk,FWRDƒ±0•ˆ § Ñd-2yÖÎX¬qˆ¬)Cœ±p—gý½¼E"Á‚V#ÖXÔ ®°¥ˆ±ÆWpE.E4ˆ‹]‰˜`ÁË,DŒ…‹˜bñû!ZÄE‡X äH⌂H@(Š`¤ qDqÄd¾j! %Ñ!Œ±FÑ(8áH "à{AI bbRp‚’"DˆÂR"&(.Ùw²¯™ÜFí7¹RD…X øäH"F*¢ Q#Ö(HRޏ 0UˆŠ3Ñ!©±þA~c¢û› G+s—á舢ŽC,ú0Ùø¾GxbòMîbRfÇxâçBLEs™:f^ÿjhØÿýÿÐ÷Ñ~ïŸöyÿîþŽöq†ýíÛþ;ú5Ú§ýU_Fû±õ]Ì÷ 2bsaC"Á"R#ÖXH ®˜¤ˆrE%AÔÌd,.-∇X`‘É‘2Ä‹-`Á#åˆ ž ±Â¾*Ñ!,D5bŨàrª%X”jÄû*ws–"Dˆ…ªDL°XeH"Æ¢EL±pý-∇X`Ë‘2Ä‹91Å‚öC²˜ó'\a«+,îD‡H°ÈÕˆ5º‚+v)b¯@tˆ _Xcñ+8H "D!(ƒ ÉBÄ(ŠX.—Z†d1gQqÌþ DƒQ$:D‚B P‚‘rÄ£B¬P4!ˆŽË N@( `¤qá2¨(¦`¤q±a² ñÿEa•sùÓ~B&·ÛGÅ!¦(2?DÓ÷k†ì²¦•Fìëç¸ëç¸ë?5îráþ¦¥ˆb1*,H’…ˆ±0cS,N?$ b‘*¹BuAl0Rޏ`áª+.çZ‡H°ˆÕˆ5²‚+f)¢A„XÔJÄ [†d!B&1Á"—!Yˆ‹=1Å‚÷C´ˆ#~bÅ/GÊgA"@!#åˆ B…X¡(BrÄÅ‘€X @dH"F¡(8±H "DÑ(Ž ÉBÄ|/ˆ ŠH†d!B“1AAÉ,DŒÂŠELQ\~í*fe[ ÐäH∂‹C,Pt2DP|ÁH9âˆ"ŒELQˆ~ˆ¢ •ˆ‘ 0Õˆ5ŠSÁ TŠh! 5Ñ1Ï’¡`Õˆ5ŠVÁ W‚¨k°‚±Q#Ö(f¢CœQÔ B&ß ßRŽ8£ÀS¹ ÉêÇä°y×åˆ#Š>±1>ÏøYçÿ¸‹é™þïÿ‡~ï_sý»ú»oÇ\ÿêxë¿Ú—ýÓ~ìG}óYÔÌoÅ£à HŠh!’1Áb’!Yˆ‹JÉì0ç~±°Êg,®D€Œ”#.Xh*Ä ‹-Ñ!,:5b…§ànÀRDƒ±•ÜÍXŠh!öSJÄ T†d!b,ÔXÄ‹ÕÑ"ŽX´qˆ®)Cœ±€q0Rޏ`1' XÐrDËœõÀÂ6ÁÂV#ÖXÜ ®À¥ˆb¡+,v¢A„XôJ®ð¥ˆ¢”ˆ Š@†d!bC,bŠ‚ðC´ˆ# #Ž™#¢8´Ìù^Gsž"æ"E¬P$!ˆ‘ XÔˆ5 FÁ‰F…X¡pB"A©+Q¢cú"“±FA… :[&ÿ”Éòdòñ{BœQ` ˆE_&û ß"ìÇäëàgaÎîÚ3™ø¾Qtqã‚'vîø½g $ëgtì&åÎÿþh *Î#ã€}žŒž'6úfªà:‚o×âèž«*®Íe±Î úW.÷ÏbÇ2±YÓ¤ ¯Ÿ©Íqgeü±/&—÷GXïB„lñÞŸ3γlÓñš®°z^²7†è3/&ÄT'Ô_±ufoëÓó õcÚ‘`;A«{Ï*?¯"—9䑞 ~¢XëIãï€?“F].»žI²Ã®ÛÈsã¶‘âù¼_õ1¡~Åìûü"ªW†×ïýygJ¤Šèò|.×}ž êF„KmîÀ’øƒƒÇMq…[Ï’3g—„MfRR~ªŒÐ| ê#4–±!q1†Î“B2Ì<«pþ4óÄ•ØÎH“>5Ö¨HSÆ–¾qdxOoôÑì,ºñ1óeŽ+ïJ}[©ãñååñ²?êCϙ燉|;TðµÐb;©ÛZŽxí¡"•wvËr̓{V%'¤g‘„­Åûv¹Ã–+[Ì]³8';Ý%ñÔ·FÀç»Ñ<Ög¯ÐhCß+£¡eâ+Ç?uê4BEÌß¶dYøó:¡ŠÉ„ÙV¦7ª=  ^xóEÉ㾕^½÷æýñi=Qß—S‹âÅ’uÕÍ›èÎç0ͰbÆNµ£Š0©£õ£ò ¡¿ãüÞ™0l¿Ñ/tžàuûþÌNoÂÉ[Ÿ…énÛ}õSb}::óþ¬oTChd¾°´å€ŠyKØŽâ]Ý¥EF*âÞ{ͬyP»Û¢æ¯2á^nûÜN/§ƒÙèSÇŽØ)8ÿhÞ·—æù±~¤OE¬¯¸\:ĉÊùÈveõ‚íÄ«º23…—~ë¼oÝȃ¬ÙæÙ™Ð´ê¢ß’¦zA!cÏg¦ ǯXšOêïMh~ õ#§9)Í-Ö­šõÅœ÷Ábÿä|n°6o—3+Î{^ž÷wt,Ÿöð6¤d›´úsŽ„‡ú,9Nâs>’‡êù|Þ­êïd¿%÷€òX5Þ‹æ¦êuƒí´{$ó {zž4Z[c¦®Q><©iù¶IâmèºGdc\É ª|.Y°"œX\™ÖÚÛSÆçZSý±þëñ"öÏÊüß³u]™Õ¶óÐŽIâ>OÎ¥G´Ÿ ù=ïýŒ-›nC­óë™=)ô7?NTsûvÁ÷á ÆÑJAô¶:ãe¼Ï õ¯¦þw¬ŽÌ€ú(úˆ +Ï\vC}ÿIxØwì˜-ù`íš=_j{š¹%–Ž‹ö„¦{[ï/÷Róø—¶¶ýðþâÔO‡í‡ 8Ÿ½êP2¸½öŽc >Wzý`;¶ö4_”xŽh‚êM={<:ìQë6Œx7«Îÿbï< šZÓõb‰{Ž5v<6PTTÌÅ‚Ø"¢bÒõ¨X@ì 6쨔–Ø#¶¨¨Áöl(ŠQTk”£ÆŽýÿîìýíÎÌ9wÝùß¹wéZ¿5³ÖÌÚ/$ïóÕÍóØh|!œ±‡ÎV’÷•O¸ž›8‹P¿iêwÈú}ýÎûà²ýïT(WR‚u<Ì5fìÚr†Hƒo/sã>tФ^`^ ¤œŒîè}nܾl°%¾+»ºŽ &TŸÔ¿žõÓ1‹› ˜8êñЪÜ÷Ò¹‘ ë|×~{syårìæº!wȈ_ŽdÁNMš/ ~pmtpý„–±$Ñkå£VŠY„úÂÒ šcÊú(•êÃOs¢,úÁ:µÝ›ï Yv†ôðýúeu…<Ø™’Ó3bqäöXº¹ÂLþσWé”$íô°[ÛâfðþÔ/œúx³ß{ ·±S¡ÏM…uš•ý}Ò›Hü}>-º¹¾IÄL[à5Í3 "FëlN£’Œè9»ß¦šÓù\úêcF}®¨Ïl!A¬³µô¶Óëv`¿¾½þy·ñ"æùÖ9Îns†¬Ð :\”–¸‹øÐNeÊøä —gTs/ˆ%5§w½<A>4_öÈg¢.º¶œüøUTéÐûŠÌvŒ¼-~Ô&†°ÿ9›÷¦¾§¼ÿìò1'7ÆÖš—eƒ­Â:‰Ýçž[ðè8ÉvŽ_k3Õ׫_¿]2%Bf•ß/ï6?"ò!šìKkY±ÜlÞdzp¾[–8ýðŒ¥nTásN­óðtXgÝÃåîMüŽ“Œõc¾»Î5ÀƒÄn?26d@úS[_óð‡6¦3Ž¥lbÈs“¶Îí¨BsûØÏ­-çÛ-fçG·®`ýà øü{[m}|ŒlZé)n€…K§ßM÷΀v·ëô¨ïÉßóï&þCúØÛÝ,„ÐuÍ} ùGµnÏ×Wâr‰¸\ò&I×C‡û{N;Fn2q–Q¨s³oßÁ2àXóMNôƒRuo‘ü1$5²J›¦`~¿Bó/©¯ ›·X.Ô®)cn^H"¬sÚes+÷rÇHÃ¥ùC´[ð÷0Ý;Z®BJ®x+q“/„ ½2¹Ù¬n¾&4î[ØïƒÍ•àó¾î·ðü¡DâÙåEË.{ ÐFóÛª[F=ììRi¿øˆt¿Ù¨ó¹JJâb > !Ô‡™ú*²¾ï%ø}¯uN‚ Ÿïs°bB¥A‰äáš«Mi P©ZßséIzðúbhäøÄ^,n]¡ùw%y™Ø±±°ìlBs%è¹ûy¤ñº¦z¶Îé‘cˆíéw]SŽ’×'º÷˜xÊÉ«¼/'¯×Ãuçsç&=ð†U“ç7ÿò4–Xv™²P>g˜Ž{t=Åú³W‚š«ÝŸ—rêԯߢ¬e^2nQµ£dN©ÎÇS ¸»Aåz8¶*p÷¼Í>ð,{ò\©4ŽÄÅ2ÿæ:¿Q_MêSÉæ?€Ù}ÕVöæ÷á=`Ñ¡æÑ¯GHݳKƒÞ3š»UëašÞöüåí¾Àæ€Ä¹Ýåïü9„úÊÓŸ—æy¤‰Â»¯õ¢âÕ»’zÍÁ²èb Ík?Lfçôòkk6À0÷YÝTUõЧsE‡îûüÀ±Löô¯ÒX"qzû(®ä>¿€æ~ÑÜÖ³"¤œÝÜkÉ"G~]eÑÇ “¤wØ^¿l y:lóÍ’_ pnܵݩ¯CüÓðù1¨ïfGªi­T¶gšgYØ_óØ94(Ù{…*Z‚ÙùõEXGyîe·e—’àîÁƲFˆŽ·ßÚÿ:ürƧÍ`Ç8öU7l‹DÉåW„ê§KýÊ—ymIбӸR·š/_(Ç@‚ÏgófÄíZÕ0BmÛŽ•¥¯Ã°½NgV ϩۣŸ W’ÛÎY¯RþÈ÷¡þÕÔ7–ú?t6OJêСð<‚uœüª½¼Tí¼gC ‘‘›Óáìê33„-áû‰ õë*”œ?i(¡ú£ùÔ_þ­ÂôÉç€ÓyÓ¢¬s g÷;ö’Š×‚ë¶4B¯]U;|qIÖg?¾-9::ÍGIV+‚ëç\Þß•îãØºO¹\ÅŠpóù¾}%9ó9UÝ`&a}95V³N¸o#Ü{–Ž3É5xº1h¾ÛÐXÎÄÖQ’ºZÛà+;æñóýÜhŽZ·yö NWàçc:¯YtƒuêìêÿcƒçnR† # q?°iâÜk0¡jÀ0³ÜŸ÷Å­¹z§ú%æóó/ÏiÎÀÿ½eö})Á¯w­÷¬³ñÛ['YÄNr~^åÖ}ÝŒàøhArõ†×`üíï§ûåë´S’µ3ãƒ]Çç2Ðù•žlxu`GésïÄÔ¯ØÚ§Ûf°I’Ú£\‡)?¶“€È¶ßú{aìÓ®¦)—®Â{ðèµÌìJ¾iæ¤$gö¯(}vû<ÞßšžÛÐ\UêCM}d­×ï"¬Ã®G·‘àöÎîãŒP«U•ÙqÓ¯‚Þ÷BZÛ™¾Ð=~ØÖ}”äÅš†S׿ê3Mוt_5ò‰`–¢ù7>‚ú’[ôƒu¾ÝÕGn^¨"}×åå¾ó7ÂtïÀè“u®Âüïn—¸å É]×6üu’œ¸‘.«7ŸÐ¼oº£ãÍW¤ó4Õ“E?Xç—;ÊmßB\‡1I*FPè’‚_$§ý¡¼±¡ÓüÀÆ«~—Ì_”äàf§1ŸÐù„æÈ°>õ&1Ía¢ëëßGŽuÖu¹žóÛ&â2ÐïôYFè›1.ç½_ŒŒH~\Qäk]ìFî\ëŠÃw3'¹Íçs@hß±ãÏs±ªÙÂykª—æ}á­Ï]UXGQîîÉø=q¤lÜõ¤Ü`#TYBN.-—Íÿˆ2çùÃ"æk©C|»”ÛÕnÉ|BsDhßÑóЊ7;_ˆ-ÍçÜXtƒÏ;ìÍáS’XrÑ0áNçÙFhì~ÁÎ}ïh[¹þë: p`Pºôîëh2ã^ä›QåÚÇtFÏu¿o9*ëÞ¯4?ßYçµ°NmßN©M›Ä§ sÖ¦bÇq%c7÷¾r']õÂA9(¿“ú[4ׇ xßyú¹³ã@¦˜ýœJñë$zîgÑ÷½>O§üI¾<ƒÉ çA^zî›Ï÷.Cÿ±kú–OÿÖqéƒÊà:üaíqýÞÌç÷GTçt=IsÀèù‹uþµëظlïµ1°ù­/ÅóÖ“Iu;²Ÿ“ ŸË®ƒ–q9CF8'í>tS*°óß$Hßëc£~Mž<©›Ñ=.¡ûr:^ÑýPáükvý'Çç¯Szj™0òÕ²}6‚!çèÓöSá·àû“d@ˆ.øÂjs4?ÓÏ—î×Ùü‡g\G)>G×z]¬Â:.ÖÀ', ×}'U;½ÍííWäŽÈºNkûŽÿXÑòv|Ÿ»´t é}kiÃMƒW*ð¹£Ô?Ü¢ |nÝv÷š–Qï¹)Õª¨°ÈœŸá;ïø‡¹Ôê4ÎB›;õkÝ2†\ ÿõë/ ùszC÷sl^  «|nÞ¯<™L|ßWXf¿läÆ$Ù_‚Ô¸ß?ËfùÀTGèìäC.—l”?u!Ÿó@ó—麘îßé÷\(§lˆI2:nt£ý=HGýüô{ ;5É3úÖEx¸nÆ*¨ºzqÿK›bÈž²¥w<^HhßQŸq6#KlÉ ý­<^g}Þ,Â:#6o®¸v"tÚ8íxÒa#(w´Î˜%at¯iªúÂäVbîmŒ!-¯=™.¼ºˆ÷;§óͳds¹+ñ÷E…òc°Nb×.®Ó/̓õ•«Løšh„Þ“UžÃÛ^ÏžŸ\s|¡àý±ûóC#¼ä¼;GVP}ô“_x?uëqV†Ï’>ÀaÓäpv}yÚ5ŽØ}ó¼I=ïqy—´Ÿ¾OTæd4É~0Ôfµ¿œÐsºÎgçÕ{¼Î¨¯?ÍS°èë Ä*ÊmëÁuí\)áBߥs/ÀAŸK}Dãüá³XM´³+}ÎÌ]Dè}=_a×wøsszÞAïY,ºÀ:ÏKo}ò¨w4ô»R}þ #Àj»Sê]€‘5Ž&]ð‡˜ºƒ½ŒÑ$Ö{Έüþ®è¹ûùÙòûë<$Ö‰X~öTT³8h0HÖõ@®Þ¥l®ÐäÌy>«ã¹Kýa‰O‰êÚà¢e–­¡ ͉¤ã!=' ¹ôþÌúœÙ€uôó¼6k7ÃÍÃîÏbïajîG5wÚG÷òEàaçC>ÚÜ‹!·¶<¥_@‚z}Ü?9±5Äÿ~K~]Ìž7Wáóe Ý?z˜$òwGÞrWÁ‘­<šæ!¾Ô4·%ÏCÖÁ¡ölñƒ¥s? ¾c¸}ü"Bóöèù)½÷¦90ôžÓ:K„uêÅöý.3l;Ó€;ž÷ŒPÒ tì¶ÙHJUð&Ýád| Yf²s® çóŠèçÏÞÏ<Óýûý·çïW,ºÁ:Iu{Mî¿Ü_jV\½k„M7÷$ßö8â4e‹ù£}alç]§‡÷Œ!Ê‚êåü8@^š»Çö[m>Ïš®Ï-úÁ:ç&úîÏýupéd„þO Ž|OÙ¼·­ÞãŒzã¿F“»}§<ÙFÁ¯SèxBsÚé½½g§çëý`&ì^žµ›Í™Ç~[?=ûõÞ)PqÊ®ùÌÞåî=ön4¹ò­ro…XÁŸÑùž·Ð÷è÷f=®©°;«¡Çæ7¦>98®9µ,}o\ ”ñ8˜ê ýn 8{ûI4 ùvO3EÁŸ_Ðztü§óÍy²^ë°NŸ²M'ä J€£sê•y{ËúÏg÷*§€2U&òîç¸ T­Äõ]X7é—“ü>t>d×ùô¾®6¿o¶^¯°N/!¬™÷v”YD”mn¡eAŸ ¹o·ižù€ƒbôì·ž1äžD1ûÍ3¿?¢ß3{ö˜;®Íï› í[†š$)Ë«µ™ãr€Íu¼c„´£.ðK—2Á¹×q?q¢ÜÇ+§‡Å㜽9Ê[ ~> ûÊi÷=¾½ÃF¯{ ¸ó„¶@÷…ý`­%e.‹—„£M%Û`ØÖ:вJ2¼ìÜôǽ~ü{&û»ÕÊݹ]ÁŸ‡±y²Íùu^ÕW¯—˜ö½E7XÇ£Ò÷¸Ü­\iU†¾l="mT‰@×IÑ5³JL‚Â&Õ–FG&GÿRÎ/ôób×wÄôþœžoXßoê°ÎÄþëvÏmšu¯ˆ[#¬Ø1áéÉY8T9wzƒ½`9Æ«M®ïxðc­ZÎß§Óõ+;ä‰Ù<œ’ðyá¤Ó³6:ó}hÑ óý×ê¿~~³D0ßâÝóªÚ//} ä ,~ÐØ£­)˜Ñõ!Ž7U×¼…ì–š³Hç1ö÷y.fõè­;Ùs8O“dûüÊ Á¸€ƒ7ÜÒ°wöÝ|åèiPüðØç‰›ýu¶—_´,†¬ª3àœ¹ª‚?‡¡ï;Ñü`:¯Ê‘Åç_ò½›%y{ V)ïžqÅÓܳü>ž‚¸>/{í,ám¾Žý}k ô(òÌNaß+iÀ¯ÃÙñåºøãâ´YŸD¥ùqÇú¾@‚u’§¹®¾uú8Üu?tí"Ö)Õ¼ù8ϧ üïÙ·Æ×õÿñs"¶4Ž!µµ.Ù«+†ñûn:~Ðó z_Dó¦¬çMÖ©5…9Y9lž¤Z[|¼Ç†“ sM«Ùe«/XF-y4qÈš°t´8ŒÐþe?ÿ¯¼>¬×r|.›—¤…_.7Øæ—Šúpgnš“@X}HÉ!O|aiæä7št³¾\AÇ0>“Õaþüˆ¾wFï9Ø>`ïUXg°%5 ^FÍÙwðœƯްmV‡$HXñn½»¿Œ™>9J$6%³ÆVÿãç§ó»LâοŠé÷Ms–,úÀ:ï·‡òæ$øµK‹ú³F·§o9ÍB-°÷:~pý®kÊÇï¸ßޏµ¸ßk¿¦ërzOäÓ!ÃÑqOi~^³¾G0`ão¿e…>; é·ÏË>f„†=w_Œ:<Þ<¯g{ÄŸß×—íѧ&¿ÿ¢ûú•nßÊšwîÛò:/´.f’œ?Ü–IP†ÍÙS¤_¡Êíø™ýËœàó=b'V[MÎ7“ÚD«à÷ct|¤çüì=XyþüȺDXÇr<$<))ýâ`·fíÿ¨ëÐç8XÎG>úÃÍÆÛ•§KD“á7Ó^9Å*øsº¾ Ÿ{~PƒŸ'­ûX‚uò(Tk}¤,u ‹7ÂÝ‘6ã–ãßv{vo3‰"Ó×Ï;yÒ[Áß'ÐÏ®›Ù÷ ÊòyßÖ¹¨2¬s<»v¹)]|ɯYëî:#¼J{·{Az"l{t¾ÒÁi~ ªýrŒðY—ǧàÏ_éøÂ¾ÿaä÷ÇôœÙ¢|þ@·É»6ÒúqÙƒo–à8œs*.¦A"¸ÊSÖ‰úûÂ!qƒÏ/‡G“w[k]QANÏIpº¬.ÞÅæžÝ³ï9–äçgëùK…uš¯Éèš§¯iYåoÌÆý¬aœøûì£0bghû>P°÷W'»´hrê÷[›_º(øó·Ë“A<¯íÐ2#thլ歞G€9mJ¼a>m}~4iߥé1i?SÐû#š‹Hßß³ïm†ã8–¿çÇþOÉ8¢ÑãÃŒ0³äŠ»){ÃÛ•|?4ð†j¾¸ô™Í円ñë:Óûtv|³á×Öã½h8}ï4Ž1¯kºá´\l°ÝaÙpR§çó½Árþö5Š{e1¡÷†T÷T0ïÑ…ÐÅßù¼ekÝK°ÎíQã›&>N 0ø@w\OÆ|õŸ¡¼‚;ú—ôwµçž7%aÿú<Þ£âþ~•î“UN­1ñQ¾˜Þ÷YtÏebÎwŒ8-dK'µ3Âþe•Ó4ñ‡¸¾ô„Š§Úˆ"ŽÍ¿©Ñ` ¡yæ§Ö7hµ9±4Ðón‹ðy9öù«ß8Óª-Ö]id„A÷ôÂõ©]詤¡¾°¤šßïQQä„rkݸ#‹ù÷<é>ѯÞíþåf_ˆªì˜´ÿ›˜êŒæÓ[t€uvmý"kÌyxä;°µ¹¦ÜókH¾ÜÏ­Ï}a“pÃÆ££ÈÐÑÏE‡Ê.&O=Æ û0Ö‘Œ]¿_Š^Ù^—ðCLß/¢:±èëÄïR%Ãt‚mÎ=SÞY»Òk÷ÍúÛ‰’ý¸q&Š\”JÜFl£^¿êÐöý6=¯gçůEöYì:΀uj»YÂnöèÓ´|›ü靖8@T#Ï©lÿØUÚqn WMz7IFèþ†>‡­“ν×óZLuAß´èb„I²x[åVwß\€†Õ[^{òÚ»²UY¶«Ô°¾Üæù ðãzÐsÛËÝ–‡zÏIÏ7éy[בϴ蟟°p×[ãÈ‹@\‡¨?2ÀµíÉ'>٠ΞO÷'¼ àÏu†[> 0òlü†+5f:Í;§ï˰çµßÅì÷!áîm»±zÀ:N¦ì]|ú"8Ž©o3ý–ÎM¿¾·ÎÌ]±úù/þ¡0Ù7¥Ýû·Ñäðžúßãö+ÍQ¯ú°ã¡çû;ñçªtßãü^ñh³k?𜙔,]Þ›ÕÖé`Ù \qºÓ„/— ðmÕ—ç¥?Ç"Ä.ó6ÿ<†4³•ÔìׯË$iSv®zʹTØ>w²lßô÷ÔÿÜz¾—aMçþraÆe êU5Ü º~fÙás7À÷o–î>p±£xã··‘äÆvñåúdÖüDgoe°=Úsý˧Nü=^ žvŸ—fûŸ{µófÿeÜ{º¾ÑÉn»r2œ³ÖÁÇwÖMs÷×mk¿Þ¬Œ" §ÝØúÍ®‹;ðŸ7=ç½j¸Ÿ?ʱ$Ÿ·^h¿u*Z‚`¯@–¶3™ÐØ;•÷z寄g¢O‘ÀúTÐ Ž&5~'éãüy%Í·gÇç«ü=1ýûºÞ´ô9։߰ÿ\ư+0vÇcI,xöâzo×e0äÉÍW¡ý&¯Ö^ܶ0†üHÿ`ªôQÁ¿/Bç/ºd糘žÏÒõ‡¥ÿ±N{—G½V^Ç'L>{ßçAh´¨‰*E-Žˆî/Y=ÊÜ©ïQ1BI†´+pjÑ.Œ›ÞOÑûªSúyÒ¿±èa¤I‚ƒ|{Ï Wà²Ë›²¶×ó@²lm}ÏsA¨Î®x2]¦6 îÏ”Å6g<Œ°÷Àž‹´áÖßÄ^µ^÷Ì +!+žãо}¡÷,DX§r5f‡Ÿ¯Ê^uÝ/$”¯t~tíè21¶‰ l.mé³7–X¶«Â÷é½óèÚSz=‘?;Gç‹Pø]LÇQvÝÄí¿±Î‘_Sö‰Jƒ¯_?Žõσ£½:߯ßžTÒÍk£”þÍ(IÃÅ5÷Œª°˜’žwÑ}2›cmÓuE7øü=½o¦Ál÷²ù?êåA¹ã£ïFDxÕÓ?<z¿¶×ÇFIw`nVÃøóÃÂ÷zùbºï²~_L…Ïg÷ãWa¤e¹=Ïv]ð6#€¸öpíÄ»q|¾aïÍI1dã>¯¯»5aüy+½ï¦ïoÐÏÏúï½tøüA7»Ištt°Ë=òÌ}pzÿ¡ç˜YÄ¥ EµA¥NÁ jþˆ!ÒuS~ Šþãç§÷]tžaïÕ_‹ißZ߇°Îî…÷=w¦\…2K²ÏŬ¸ßd-¼h˜OØÜà ü{U÷»DnûuwïRø÷ɳºüÁ¿‡RèïIF™$•­_BûkÐ/ -©ÆˆûðK|Å~?ˆ)H i8ì®Nš\.–°ýÿÇüFÇúwr´éþÞú÷ac¯w“ó÷\ƒ}Múvµmr¢Fô2^NÒÒö-qyCwË@©ä×gô>„¾×J¿÷šåÆT‚—%ø¿c+tž‹uüÚ0IÐé0ÔöÚÓ»/îÁóËÛž•¯&‰SÎô´Á6¤ëRÓâ”dǘ:ÄìÆŸ³Ò{zB×éôïñ¬Ï§eXçts#’VÎüšrNîøñxTbIs;³gÆoðÐ6Xš?DIÆW’Ö™T#Œ¿¯¤çnôï"xœ:-Ä,f×oÍ ý›ëÔµ\ä¦ChÅußZG߃Ac·ÌÓ~#©ºqÉ3îc²~W —)É©ï7½;ûÇy=ç¡÷t£çtü±èëXþ¯Áuèèš`?Î÷ÌÖTùšt$Џo[ÞÑüݺLl#.­S’Ñó¶&ô´Uð÷‰ô=zÏKÇ:nÓõ¯E?Xçòˆ’-O¾þÇ[½KnZ•=ß1\¤$§æ ^ùÅÉ›»O‹%«ß1\rBçEzîAïÅØsåoâ*ڃʪ÷ÅpÁ¥Uà…νXý`©º§Ë«¦\‡7±¿oÝSâ¬qÓ¸fzi\q`“Ù'ÂÙÚã/Uˆ%­+}U/û¶Ï­gïAërûÀ÷bö<¿?÷žå0V7£M›á}×Î*£‡œ• ̾ çö}ø¼sÙZ}uäì†`‘ÿ¼w˾(ÉgÎ{í^Äïé÷K×Yôó¢9îÖç""¬óùE¾®K=3jœÌ;îÂуUñd ©Ú°Ã¾Ìé2PJ“¥«Æ’‰'íöAŠœÿ»Út`û๘í÷ÂïÙJ°3ÚäôÔ3û›r*—UÛë7){,ïΊ 2øô.'º„,ip>½F×C þÞšþ¼ô\Œž‡Ó÷÷­ÿÎG†uª96Û`òÕÃØUµTS:Þ…ìuß4Y¸°ãá˜t¯ VŒå×étÿLç)ú~]§ÑïË¢|~åÁ‡"}Ãõܹd.tÃÙÐ#j;¹RÑ÷Míó ØôîtÝ qä×Ôë}ƇªoúÛ·.…ö—*|nv±Ý~=´nÓäý–+¹ nÔmš÷ÑÄa[ع ØOíj-K}–K\ªµüte¡çôç§—Au(Œ•oÒOô¼ß¢¬ó‰9öÈÒÔNÃ'n‰Ê…3ù»5ËÞIz/YTÕÉo"<.ÿjFÕ¼Xîü3ŒÐyœþ]4»N}Ïßtk½ÿ7`º¯}:wø¬‡1mÒnùæ‚_å—3}ÙEn´eþc"ôtýQªv«8"¿¹¾Éf…‚ßÿÓûnvöTLÿŽˆ[ò犌1I6ŸN-õµN”èxì·íraûÌÔÚßëï!¥ûõÚ<.}œˆqꓯŽ#U‡6^1®‚¿×§ï;°ï›ÄƇ ©¿‹Ï.c^(åöåøüäýÒ_ºd@¹ßúÍymht)»ƒš ~w(^!ƒÃ«¶——‰'oƺm¿VAh_Òï~/‘‚-«»½²áÞoSèó’`ÒŸn§͇S-ªT8v‚«_”Ué¼—T[ÒJ%<ìå}.׋'‚Yͧ$¼T·%[äêÚðëIvùZü¸¿mœw– §Ëv…ÎKdXG¹¢ñΩþzãíˆ9w`¨¥HõÄ©ö¾††¹í ­›Å“ G˜ƒ0zŸÓ–ÿ»ºßbïïÚÁvößO/¨Ÿ^PÿS^P*d› ûù]©·Ê_õ´c¼UجA\¦¶¤Ïõ¢Y:zÎg¥¨qÑŒ U®ì¿ò]ÿ»y:Åå*Zçj› FØq¹Š5WÛ:W±hžNÑ\E&W[ƒQpòb¼î¨ïŠ—§SÔw½¨'ÍU´ö]/.O‡ÉÕ–qxvVÙ²ÖžœÅyCQONê½þ¯|Uì‹øë!rÄ„H‹ø´üމr›ÿŒ1QÀý.zæ;û~SjÄŽñøärfÝŠñ'.šCÁäÌ:ã÷YÔ›]k•³ø¯<ŠÿnEqcÖY³fÄ £A„\ÆØ_ÍšµÎ+šEQ4cŒÉšÕ""\x*!ŠOŽ˜¸¬Ym1ÅE½òhƘµGqqYLÖlçU%´ÊZ´öË+.o›úåQŸbÆ/¯¸¬ÅÎ#´¨×T*" 13™‹8hf…ÌÏ?ÇÄÿÝcâÿöñÐŽûïÔ7ô¯úµ3¾¡lT5—9[œ÷qÑœ &sVVL.OѼE&wÛ \Ã5ù?ó?þ»yEsÈÔErgE(”PÄ€¸q™d5w–f’—WQ4—ŒÉ5!R›¶ˆg»µw¨œË« ¾ÈÂV¬72õêS[e’Yû#—UÁdÎ*9¿v&oQ…:üáÛç€Vr"@ôˆ3ŠYÉùöyq¾}ö(준ɟíò>yÖ¹<ŒO^"EákŠ?ÜÊ7Tûs}øf,ü¿°>r?kó`Cª6e’ƒH°9Õˆ6h(b@ܰQ5ˆñ#åòg¥Åø(ͯ`òg‹ñ&-꯳ÊeüW^Ê7⸬2ë ÚDŠ‚Ñ"".«ì¯fÐZg•Ͱ(šUÆdÐê{\bB¤(<-"Bñ…#f{6ƒVWŒ—²¢(åHŽUV™µ—rqLm’ÊùÆÓ¬Fg± üIw¢·òS¶ù“¼Æ büLƒ8/R{"&»Â8FÀOOùÿ”1ñÿúxøÏÆB¢FØ„AÃSÞ€¸a“j¸|Úâ<š‹æb0ù´ÅdùÍld²¸¥ØÜZDð/|šÿn6FÑ,3 —eÀe™Ù£Häˆ ‘rYf5¯–f™—“Q4ˌɬ5#^E<å¥(8-"âòjÍ-ÿðn¡Câ†BÔpb¤9fÖ^ÎÅeg0yµ*ÎSžÉlT#v(ÚD8£xUˆ„ä ²Š3ã霊8 ¨•œ°½UwÑ,â…‚×!ö(úÄŒx¡øu?×…ÿ1càÏu!û=3?‹ù̱!Õˆ6e(b@ܰ95ˆTŽ˜)6ªa³†sùµL¾†±ÇÆ@ ŠÉ×`òk%ØÌJ®¡}1žõ©V™ŽD‚Í®Fì°áC↯ù/dl—mfaˈŠÑ!ö\¶Ù_ͰµÎ6+š±Q4یɰMEPpJÄŒx¡ðtˆ=~)H=›aËdlØ£#DŠ‚Ô""e8b°Ê6c€èÿ$cƒÉ° EôœÇ=Íw” ˆÕˆSñ9Þ¡HâŒâV!‚bòi¶‘ èÕˆãsä 8(¹A IE˜7z”Àe¼Øü{ÆÄ¿2þÙXøïÿlüûwŒ}ÿ¿×~ÿ•ñîß9Ö1ß—±Ã& Er 6›±Ã† E ˆ6žbóÉ"Å&Ôr¹µáˆ™ÉÂ†Ô þ1—ƒÉ­ *’#T4»‘Éèöbr„°is 6®±Ãæ E ÿ…<Ž¢hLG—æ€MŽ˜™ñíodÕÒ´âò8Šæ 0û]ǰpÄŒxá¦C칌ÚÄ ¥CìQTrÄ„HQ\ZDІÍDKEPhJNlÅåt¨9ñ1Dˆ" Br ŠQØ¡ Câ†Â 0=âŒU!&›ɱÊá.š¤æÄ+CR±)@d(æÔŸëºŸë:›ÿœu3WËÄ|¦ØDˆM)GLˆ›S‹ˆ°AÃ3â…ªCì±Y#¸¼Z6m*‫äšW†¤"ØÄJ.¯Ö ›Y…°¡ƒÄ[…°¹ƒ¸,"šÿhBܰÙ5ˆ^Ž˜)6¾bóË"Eh !1#^(b¢ˆø“ì4ëÌZ“Ó¤"\vÚ_ͬµÎN“ °ÔˆŠ+´˜ì4&³V8£àTH"Cá¥"(>%'@&³V8 •œ½b¢Œ@LVÙih’ƒ8£PUˆÅ„äp™µr$q°Ê‰tCkþIn·1 ·±sþÇÌHš_dBÜPôDˆÂ—#†b²‡ôˆ3sÖl¯2ÿ~މÿÞ1Ñz<üï ‹ÿ»ÇÀ¿2þY}6æ1Ÿ±b£É↠§A„ØtrÄ„H±ù´ˆ01#^؈:Ä›1)@dØ”©ˆ=6fRÀŒ{Ø ©\Öm(¢Gœ±YU’É4¯ Ñ!vØÄ¡ˆqÃfÖ Blh9bBܰ±5ˆ›[Ž˜)6¹a£‡#fÄ ^WLöšÎ*ï–É^sF!D ̸÷7ro­3Ø(˜ $‘É_Sr @tˆ= ))@d(¨TÄE¥ä„%CRX8bfÆ;š—ɦGœQp*fÿŠ¢Ó#Î(<"pd3p5ˆ =› ©ED(ÆPÄ€¸¡(5ˆ…)GLˆªfî4P¤9ˆ…ªFìP¬¡ˆÁ*;…k@Ü\þ<ûìçúîçúNn󟱾sãžef>3lH-"¦ G̈6§±Ç@ 6j*â€Íªä6Ñ#ÎØ¸*D€Í€èglb—‹+ÅfV#vØÐ¡ˆ‘`c«;lîP$Ç*kÒŒH±Ùµˆ>1#^Øø:D„ÍŽ˜/±G!D ˆ ‘Š8 (”H"Cq¤"(e‘l\;K¢GœQ4ª¿‘k‡‚ E ˆ KƒQ\rÄ€¸¡È4ˆËÆÍA$(85'ºD8£øTˆ€ËÆÍAœQˆ*D€b”!©ˆŠR‰˜ ŠSØ¡@C"A¡ª;k(bà²qÃâl•M)EkÿIx8bBÜPÜDèüù”"{8bF¤(z-"Bá‡#&D‚€±³Y’ÖÙ’L/3ÿþŒ‰ÿld¾Š÷ØGǽÿäñî¿òŸÜæ~'ó½b#…#&DŠ ¥EDØTáˆñÂæÒ!öØ`H"ÃFKE°Ù”\à zÄOÉ5_óî26 P° ƒDÂÜ=`3 ±åˆ ‘bSj6f8bF¤Ø ZD„MŽ˜/lVb  2lÜTÄ›7)@dVù¸Jnrøù¸9ˆ›^Øaã‡"Ä  FìpŒ E \>®  0‚TÄÇ(%·H@ôˆ3 F…P4ˆž¹g@ñ0ã HÇü½Š(‘ ÔˆÅ„ä •šy'™¹K@Q™) K‹ˆP\áˆñB‘i;Z(b@ÜPpDˆ¢“#&«œn! PŽ˜) QƒPŒAH"AQª­rhõ?×g?×g6ÿ9ë3/îÿ[À|&Ø:Ä›2)@dØœ©ˆ6¨’kÒD8c³ª6l’ƒH°qÕˆ6o’ƒH°‰Õ\#{!Dˆ -GLˆ6¶bsË"Á&W#ˆ6»±Ç†@ 6~*bÍ 2A*â€BPrb@ôˆ3ŠBÅ #Ñ#Î(R€x¡PtˆÅ„ä  p‚D‚R#v(¢PÄ€¸‰ðçG„((9bB¤(,-"Bq…#&DŠ"Ó""š1 n(8 "@Ñ!9ˆŧFìP€¡ˆ‘ ÕˆŠ1Ñ#Î(JR€¸¡85ˆ*GLˆ UƒQ¬rÄ„HP´ˆ ‘ x•œ€½Ý¿È7#R·¡ÀÃ3â…B×!ö(ö¤ñBÑë{~bFÜpÐ BäE²¾™>gþ1ãáÿĺÌzüûgcßß÷þ·¬ËŠ[“ýw¬Çþ»Ç.:n1¿“=ŽSfÄ F‡Øã 2l TÄ›HÉ5R¢Gœ±¡Tˆ›*ÉAœ±¹Tˆ,ˆy/›Ì ›LƒØa£…"Ä N˜ûPD„Ž˜/l@bMxa#ê{lƤ‘aS¦"ؘJ®9lP%פˆÇ¡pæý_ƒ8þ!zÄX…°‰ƒD‚ͬFì°¡C↭A„ØÜrÄ„H±É5ˆ]Ž˜7lx5b‡MŠègTˆE„ä ƒšÙ+¢ r˜{NÌ߃¡0 ˆŠCƒØáxŠ7‹†yÏ—9çG±˜™q£CìQ4HsÆâ¢xäˆ ‘¢ˆ´ˆ…Ž˜/”ᘎ˜/˜±C‘…"Ä ÅÆ¤†„{&F×]9¬Cƒåß]î¿Ëǘ$m®/>u4$¨ßÃQZØ ¸ÔKÜ;ðÊÞ‚Âæ½2(¹²¯tZ°gU’ dwUù4žó…rêKFsL˜:*¬3oÒû›ÃàZ§¦óí2wà¶sÙYiG÷‘G5Ž(? jD‡JnGÆ6NÁå¶„Ï2&ظÜýqüÞÉ!ŸyŸÿ¾3}Ç${q>ˆ},utXgtämÑÆÕÐç•§®ûõÛаÎÕš‘^ûIŸG²Ü¾Õ=á{o½²_V<éUP»‡ws¡¾±4’ú‹²~ ?įÝ7<ý,ÀúLNw¶Ô1`L:™2 >½JžsÔÖ’‘¯ö“ä·›Ä*…„Þ½çØn"{·‡F¨ Ô?œõ3mÇû¯³þŸÄ‘ÊŒWèXÈoÌf¬IÂú>bá~監nƒaL¥¸=KIÙvɆ2v U7ÆM¾Ï\ï EýWÙ:çŬ/ïK1Í-°Î!a§æ3$¶qiW³îm8y¨FõÇõ’W{7}ˆÜ4¢&ö}(%ñ$wÏCGÛ(ŸÛIóîX—ÇñJL}Z¬ó}$XçòÓšƒ»gf@œ¾éáws %¿×Æ›g’On¯t»Ãà|þçÒ§&Ä“‚¯=C„>Ÿú¦°þÅÏ;.?Ösûg1õq*”W‰u^WeŒg2`¢ãóQó¶æÀ¸ 5¶¯™rˆ´ª´íUõýÃaAôÓä{#ãINÖϯy BsèhîëÛeä}U©ÿE7ø|õ£M9®_2`Ä\ƹ%F:D¯½_SC’síGÔ¿ï¿éӽ׎xÒ¶Ûyøí™‚P¿Öʨ˜Eø¼N¶þr¾l&Dmª>/ a$—‘(4\~âhèßõJòo%7Cu&9JÁùäüáÏÂúS¿³yÒŸÄ47½P¾ Ö)š™â[3¶ËmëtëÔ`b¤ßkÈóÇ?j4¼o¸7Šè¿‰t•ûR n.O–ú´²ŸÃGñû7‹¶WÊ;λr§Žc!AÖyÇØˆ7Ï„·Ÿ• ^ J†×r Ÿt˜,2,lì>Xn"mæ/8М÷¤ý0°{½©o¹\½G–ÏÉfœI²”‰1t΄G¹¸ ¾ô¿Ô¸{˜Ì›9õ²lÞh±µAn{Ã&ÂØºô—êo_8ïú…˜í§‡–çŠð¹³^†œqê Œ½¡Ý-p[½e´Éý‘[ ïGðÉjÝ鲙̳õÉy¿=šÇæÉ¾ÓœgK¿ãsY_´L•ñîµ]Ÿ ©ÃB*L=v„ÄÛ-5”lŽÎfb|ÑC#çýÖ¨ ;½³> &ËseøÜS±ë– Í„R?F_^»&Â=²¯ä Ñœ§Òe ^ÐÜbä¹™Tyá]šTRðŸõWcs>çrF>ŠÙ¸¥þü<`és¬ÓäC9•pd&Lï2Naê— i¥*õÝr”¤ìn9vØàáÖ~ßlíf²´GlŸ«|Þ.õC¥?7ûù|;åÖÄ!½?ï»c鬃ÂX"˽6HÔ´B6L¬z®²wæQÒUUÝíÑp8½~Σ*e¶ãÕ›5Z\A¨_õ5k7K~ªïzç·ôYÌÎ]€~ž–þÇ:®žÆëf¿L¨Ñ(¨©þ&4÷ˆ»öÐ!‘,˜zdDà¦pi‘B$Y¶…Ô~#˜ºó·?æ1ªêEûˆæ°>œ¬_œëlkwæ[R@&Xlö¢nB´ç‰ó¹‘‰D`1š ‡ó,B&æóûk»vùÃwœæ¦QdꇴöpõqaCÛò×´o’°ùb™PÕ¦Þ¼cnrþ¬‰¤GPÿ¸÷ÂÑPuÈ€û6¶’‰}‡äôVð>Uԙ晰~^f1ë'؉Ϸ±èëà$[+Ç;J[ ùoBÎ¥OCÎM9F´{¤a;VŒÜ”·‡·’EÛLèûǸAs¦¨ëSõ™›ºñþÒ½`îI»%~þ™ ÷ÝÕ¯¥) .ˆ*´Î}pŒt[Ê»bÙ±à:ssŸQ¿n%iL,ß(ŸûJý°è¸AÇWêKkÓ!Ã:å« †MÊÖ§0 ÚUw94ü8é6³J¹¼/£Áâþ'ÝJ¾m/õmiïKJë±þê/Å)çç~¯RÁ,fûÌ©Ðï#Ç:¹Õ£*M΄ü‡Û^MõÍ‚ßKìµ­’rœTõ\3®’t\[GÕ[Éf&y[A¨õ‰¤¹&Ô·ŠöÍ%±èë Ÿ¿nÕ$J2¡ñ÷ ý×í΄™Sl3G&kÉÞŽöïuÝ!¿«çÐ Á*ÒE¾0ÌQš÷Bó¾èºÎ“tþ´Î›’cZÕã×MvÍ„L9ÝR°ŸCjZ⣖4\"Ÿ_®æøÔŸ TRñóÍ¡§>tÔÿ’ŽÏÔ'ߺ¿TXç÷+®#þ{çUòô}Ì(t V1‚QWwj̬®ŠÄ„CÆf”E1cÆ3 bÀ€DiA$È ¸*Žº(fPW1¿uçÞºŽ<ºï¾é÷ì{ŽœóýcwÏvÏÜ[UÝUÝSŸóVAÒ¢ÎW¡™»Ÿó&“8¦s.~Ñ­°1°ïð/:+X¯Aõn_ˆÿò^hM¼ÚïòyIO‘w®ñœ§ÚŽ5-ü:\Ûwôzœ Á’éæ³ãX“yA[ -!o™$+}VõnÕ*$”—}Õw発b±•¢)®Ÿ¹F‡¹0zoäù‰8f\ñâ×öã žw `Á3Ç^Æõ†·Ÿnºfë®]Ù0¯BØïÒûïõU_b»R©ù̳«]…È“«£{aÞ˜mmûÞÆ±† Š?_ª=®Ü:¾û®‚æðTϨß>Ù-å!<ŸÉ†I xaï/8~^ò–YEÏsyþŒY.4øxùÊÖQñle¢ò++øƒÃy´Q0®çC¹Øg“÷R ß§ôÀ[­j¾œ”>­?ï'8þl‡'«w«sáNnZÃÙå*pkå²nuXjðdÍÜ ¦DΉÆ_pžû…ùÔ‹êÆœTAT˜À4Û…–6 [š´ÿúqûß¾¬D.òR©'q³èy‘}}•‡à<® Ê>’“ ¦-_ã®ψÞãG:$0¾o?îß?˜™l¨dm‚:¦ë†ËEõ9&Ž qei½Öæè(p׎Ç*Ö\È…ó:NR7?½üs ,B1¢WI©5ì[4µ]³ýJ¦8˜\5ÐT.æï4Ïg{-ìg*Ä>•Ú}ý“qžËçdÊOçBžë‚^5 rà‡ÛkœÈ|GÞ<þ¸ ñž\ûZɪæÍ¼U3Ú[ìGM<~ßôLÌ´ù9j_ƒÇ:œ ×j…̘š“Tg.f¸&²‰»Ônm¹¿>nѽV{«Ï-Ðr‘oCŸ“8¿Ä)0ÿ)ÏÏæã àhèkòuûRéû óœ÷ç‚þv÷¶[ÔW`ÓîyV%'²—n‡ZøZ:GûJ¶àS§ÆíÔr±ï)­SÄu ùª ~|'sè h¢¡ âýçy·âóýÔu¹0¸G ½n jqŽ…Ž\µut¸5ø=ô¬w~¾R\'iÿH|âÛP>wk1¦¢ýâÆopÿßbF¸ç‚^ŽŒ‘ MÛíZµeé9¶7xÉ„ŽqqüZë׃•,«s•^µ›ù2ÚÓzH|˜E¾[_ý8É%-‰•îîvz:t×-YÑ*[É0h¯Z;Ó[ì¯Ü¢YýÕŽUÚÏ™|%ì/t€ï;DØGõæýçi0Xš´ºN.l2ò5o`”»ÜMÌ0vúÊëM‡±’_Ï:%+YrÖÒ²Ÿj}ñz?ƒŽZìºuë•Xçp+/ ª–9¬®ýý-ÿ}Ô8O…`£ß^©àÒêÀQGÎfBö¡I£";T}æì‡K¦‚Ê»mÂ%³¹[ÅiÂñ/|SòâQ}‚âƒv?z‡Riñöµ~SòTàXí¡}ÞÈLH;ÝdØäÚÉÌlÄÉ~oM3ÍÆ@ÉN6æ:KûˆñŸúœóû¥ õ§ºŽÆ_pü®?üã° Ròº:5¿›¯gfº­˜ÌôãrÔaëm€£9ﻫó%z/TÇ¢¼Œç”HhŸ¦g¤8ϺŽ%Ïz© {Ù¢e 3Àl?½“Ì‚ 7n™2 œ>¼ß¨‡qf‹ÆÌn&qøxy_ÂóL€xE?Áñ“ÆZ)}lT`4æÙRã:ࣶް'™5ysñö¾QÖÀ^nhôªYË®fóga1¾·Œü‘êÄñøŠÏŠó Ú^ÜOƒ£ŽØ®<|Ô¥‹fy\Lf!Ý û°‚™›­®"òY‰×Dù qF(Žñ|"3 ÷¦ñœ§iqI»¤ŸÐ¾~³+¾÷ûeðóûí°ãódÆ÷›¶©dªdI…ú­vûˆqŸâ3Çž |¢'7{‹1æ™Ù»Æ_p®Ëò™»9 ¿sxÖ“—é ýÔ}ÚÉ発ÊD#ëV:iÀú…ùJÆÛ£úíiý"ûÕæ¨qü§Á‡«¬:˜õ١ˤÃè§ÖêÅ#γ{#ZÜ,°†Š‰cbü¢u˜x´çíûëuØ特:ûôÉF9@œù땟 "γV«ª{Vm6 WŸISüÂÖ6®Öa¯”ö“­*ùa¼Àý| ¡¾ðÚû)ÎÓ­[l›²ä+°>2#©«/ÎÓ.aé²ÛçY猑æ{¦OƒѦwv¿S²õæTQþ%ß;õ»'~"å­´®PŸ¿à<­AɘÞû­ó¾–—`¬ï ÿDÏ3T2Ͷ²LΈ[NýûéûGˆÖOŸàø:>¼kÛ÷ ŒªšŸ*: tBjÔ ™rñ}Öep::ÉÂ5SÉÆ…8Z?—3>Ïj#öK'ŽÆšøÉÙ«Ýï‹ýáɾ5~‚ó”ÔMýÙíE68™M»hztÚ]Í1èË(¶ò£ ¶L¼ývXí¶Ü}iÁ¼¹ÈÍ£u˜žq­i¦_’qžæÕ­ëw?• ]×=YÕM/ —µ»wôÞöx°ýügNvЇks?#„åݬcg¶ëË{!¾¥»åIÃU™%Û2½-ØŠî@ç?Áño„ö»9b^6´nÑ4ôxt*Œ7ŸolÚ9…=©µæÜÞÑv°Ç…Ûp†0——æuïŸ3ʨÎO|Ñ¿fqĽR qp(ÎiüÅ©Tzx`|æM²¡ƒ×s7•m*èï}£Åœ69+éΫ?eлÉÝ­ËBØ3ÛgÛCu|qFÈŽ)Ÿ¤õ¤·ƒ'=M€üVã/8ÏË7|Ãâ³ ~Móc«§BìŒ=Ka»ÏÎë?ÚJotê]lÂ&ºÕ›³Sö¥~O~GunòûâÖ\Â;L¬ghüçÁ‡ù³¹Kd¢·|>vö›ö»íÿ,EXße0&wë´OCBļ•öq´ÎR¿xZï·m=ux:ðñ‚ßWÊpž%\šÛ÷-.WÞíp¸'õ,ßô¸ÈjÜqº3¼£ 4e«±!lô¯íšûŠ"Ú‡Ó~ßàçPºå³d ë’[S·ºBõ*¦‹ž(Æó~ƒóp§u33! ×ÒîmÛ\Ÿ±ç¼u¿ÈL{nRæ%ƒ—Çj™[‹©k>êò_FyÙ3oQ}™êZ4~ƒóœ_?á˜_¦À}N3Ëïîh|ú"kúbãןd0¥ÁŒNëBX﹟3FnÿÂ=!{æë¾)b†ê›¼ðçNÉ8”=¿hž WæÏûéÙÎhãáu. S;p|0wÎ’ƒ!,%&j^â_FÜe‡¾åcÿµ9.jœ‡Ã¿OSg€C~ôk‰U ^úkäHewý?ûõœnJUˆÈs#.3qoÈ®i}¶iuåìú&_q"tœK¥5‹F>î•­?í=:®A H”›YŸÊVo¿¾Ã5{(Ú{,ñOï¶³›‘ò•Ÿ\¬ÿ“½ññ¿P\—ù}’\ã?8ÏíwQÅáM2`ÄÏ?OjŸpšô‘=,Ley‘1‰Æ&p<&{¢ÿˆVžðp×Ïw½Õ-¨ÃsŸ_H ?&ýqÔêµäR|즚e}ÅzºÆpžyÖbÖÞÒñHm'Ÿ¾Ý!M¨_8€[Ð(„ÝÒ˪3,Þ[¬“Gƒ¯“éñ߃çßÉpüåÅ›jø º +lËóÛ×»/Šk¹¥±&K›­ºÛÎ’—˜Øë`¸¶ï¢¡\äPÝ—>?_-‘ðû³¯Þ‹œû §ôj¦Ãçݳs«ž9ç§z¾sˆNcs½nÎÍ®k.uu a>'J f/'NHWàÏ»B{èV’&¹+r#4~‚ãòûÕtçŽÇ‡Ñu~=êó&¹­ktïq[˜•…¶xíÂîMÐÙ袒‹ñ’ÎcDn¹ð¾5~ãòõÕtØ}Ù}`p•óPËï^·¾—Äõ7ã"÷¢BD.qJ鼂8`TÏÕøŽ»4Ávo»äKp«n?õË”dèÃöV«¾ð›ÙÞ´Ö~[°Þ?«{ÖàPV³¾ëÃ:g}ØÄªözüÙ ˆûÈÞB|ÏŸ3º”JÓ½¤ŠÞ3/oßþ^W×%眚5Fž¸ÄÆ?̺;«¶-L•mY·Ã9”ùÕÊ>îÙÕW8'2¾®jÄ+£{¼›ç›Â¹;ÎsìÐãuÇõ.úÆ+Ÿ ª™YL{|‰ÙGü9~Tg[xÝÃl0„²GÝ3GŒ÷eÝìænƒÕØÕÈ<ý'X±ÓëIËÖa_äýþí¼Å:Ù1ÏÕ.ëŽÚy§ ÇÏ,x×NÙ=R[îñI–ûÞüöùæ –v #½ýÓa©Ï¶ÖÞ5«Ã£ÞbýŒò6ÊÓ鼌ò:àøël‡Ÿ¼yõؤOk>±e"´´>—Ù'“%]¸uf‘̦[Þ6=œ‰y&‹ücºÂÇï«:7ûV^ ÀyF ¼ž½qåè^'Áx¹:A¨‹d²+Ž¿e-v€ú½l_¹ÂJY-á ŸŠêt^²tBÇŽ©Ÿ%tžJu<à<ò}©üÚ^€¢—f™N¡ ÀîÆ/\ Êx‡öPuÔqûò}!ây&ío¨^Ãs_Šü8ÚŸÒ}!à<qþÏ/=&šêLœŸšc¦VYl‡¡/´Õ0î€"„ÙµX³Å¿ó—s-:o¢uŽß÷TúïÚù´ÎÌRé§ÎV!OºŸ‡¡cfävK€Ñ“F&¸f±1#¦–}tu€Ì&‡¶F?™˜ÆrbåŒøÊäÄ[Ó už<ˆçTâ¸õ{ú[ŒNH†rçzÅ“JâÁwÌ1‡*±YìRŸKÏp„ßgçÙê…ˆuyò âÛ‘Ðy9ÕK)ïÕøÎs¹íÝûM“Áêý¢ÎŠxpkøtblÆç)NpyáŠ6õqßÔäçË%pÐ[|ï”ßÒùåQô½´×CÎSïâ©=CÆ3¸¸«õè¿ãÁì©ýÞ‡¶Ù¬ÏŽÍW:¦ÌÞ4„ï­•~Â[¼ŸA÷ZhÿLçfôµyçrœçð¢¨O9vIpôã§¹-ãÁñ€z\óèlVøÌµ{S•Œm;éÑd}Ì;Œ,æ,m 9žÿè¼”xndÇTÑø ξpXç¶6çàr€ÃãæâÀ")fsÓºWXÆäÞ›|pž·ñu??¬šjÊ‹rñ<“êN|œ¿!áï·”‰<¬¯êZ8ϡݙ¯F%Âêúñ«‡¸ÅAëˆæÇ>9\aSžÖ\¢´u‚çŠ?*Ù»0ßõG¼CõfâwRÞA<+íõ^óXV1 4$Zµ¿Þ>¿n6“µ9…™ÍóÌ·ÕQ<Ÿé.½1« ¹7#.-C\:Ÿåí£·\ã/³pÝ’Ÿ½v´w<Wïû@½'>o½{–^¡ûè¹Îð>uçPœç¶AU¹CÎÊÿÂ_çó´" qƒï™—ÍŠëÝŽ»è^êæ)ð\qžùSÊBNÆA3gå¼n±`Q[Ï|§uÓwðßkõ—ýúú’]ÖCî®ëÏøzöuñ>hן¤8O‚#÷Äbá˜Áñúòظj·¨çý9,D2Ï1ìÄz°#ZI¯Ì•ŒöAd×|>] Þ¢sà¯ÎOp~¿‰‹?==5#Fò5Ÿ¥Îa#å½bÞΟ¯*Ùâª{u[­÷cTßäϯJèü‰Ï£:}Ÿå8Ï~.Ò=7¾þü^BñBûžF2γk[I‡IgààÇQÖOÏBȘžtǪ˜aüû¥EÁ3 Ó„¡Û~Q²§Ïçl)v÷ã(íëøøyG<ß º¿Þèòþƒó¼Óýpç; J‡Z;cÏBwÓC#ÝT,þåÅ…gå2ðÒ,øJ¶ð/‡íü½Åº½~ž\‰¢ãJÏM?ý%Þ³åóÐZ¼ÿÌ.•â3.3M9%på΂æøp«Šy•l¨¶ÌìÿaP³›’¥ç$¶Zâ-Ö‰)ó¼ñT Ý?Ñø ŽÛ÷XãªqOÂö‹Û¼éÞÛÝŽ¸qFÅ~íë hkg2~Ÿù±»’%­ 6—¯ôÏ5h=¡ó_àx/ž÷îÑJ÷$ÔÛ˜xt]³puu¿è×UìÞ´eÞ 2ì!§áÛa=”ì¹UןVFx‹uYzî|~rWˆ#¯øû‘8îOŽÕ'šûFAë÷“» { ýŸí¨_ðYÅÞúDô‰r€QpçIé—çL÷áh¿Cß{îX³6^Ùo$´ïÖίä8Ïíæs­]?!ð £ÁuMPqQ.³v>½“# ±´‰3S²üz=Œ‚W2>þ˜Šë¯þO±®LûýãøŸ§÷<á8hŽ]ïDÃ[ÕX_ Ë\f3sÿîüŽÐ}ðÉ&}ðyG¶¿%¬Á öhРÎSâŒÄ{‚t€öñtž¤±{¿Nƒ½ÓßXÝVµûåGƒ¼÷ƒœ¹¹l¾Ì¸éñ 99fçóæJÆQÉoÆ{2:×!»ãóëbÝšîGiìÇ·=·~c}‹#0ÑÎgÇOGË÷Ë›+r™êãýÍ2 $.©A­ÚJöŠ»¾æî)žÒþœÎˆ7©½o×™ƒïy…$Ã_/Þüœâ¢aޝí¼ûi¹¬¶¦çe„ÕP²µ}º¸Ÿªç%ž·¿Ox-©{ùòÅc¾Æüú€ãqÔ ¿›ÉÕçÝÊž .ù^˜ ä²giYc"ì!Œ{ úøù›6xÞ?ÊK¼—j]>üþV0í“ß¿¿—Ð¿×Ø?Ž¿Lz}G×ÄP`A'&÷h åÚ4¼ÊŽ,­Ùg¡= r¹zèQk%+z¥P4>æÅè ÿ\ŒÄ:ûOEoþ±¯_ÈpÜ×¹]ƒK” Seöµãµ£á@s›v‘}¯²n=6gŒÎ·Ü›âŽFÉÆ¶ï¾3¦½£ß ˆ÷6ïÌsêQ"áíý­„üNûž½çyßšKغijVN½hØØÞÐ}Þô«ìçqëŒ8ì¹;¬´l¦dŸwîì¦ð÷…×¹‹<=…:þmI—ÁЇ7Ì+$´iÿn@ó,âÊ/öCŽÓ¬F‰m£!g먫ý®2'Ï{SBóÁªOÁ§+¯¬Ðµ¿jÈ\O‘“Jy>«‡Wcÿ8®‡^ã•÷€ƒª¶rUh¸V±kÕ£¨«ÌfA“ ·;þOOF¬OW°+)µ?¼ØSÌ/鼎ê |š”?èXÆ×¥pÜv— z¯vؽÍj Ççâ«.zë*ËX­ÏÔgxÖ>¦Ó­] ¦; õÈ @OÑ.iÝ ú,Ÿó¿Й[*½ÑÔ©oz‡íÂ}Òh¨Ñ(Îo‚^;v¦Àn×ZgФî ¶yá>÷Yžâ½Eú} ÿþÔâ{ÕØ9޳ò]ÎÞ6[`â¥.a.Ë1È6·fýòX茳nqNÂ÷Uç^Âú×CÈO{‹|cº©±o·bãP×ê-×ÂÀsãwµÝ ½ËmV0û<æ{`\Dz¡#˜š´=à `ç#K®ünâ%Ö©NÉï;*$tT».$Ãñënð÷Û °7ñöËÚ =œºÔ?¹!]uÛ~¶ª#w2m•ã¢`7]vî¹úÎS¼ÏCy"¿^?òöm>¯|.φìŸ?ö, ‰†¡Ó£½æÊc{]n´)îì ðtLýǺmžbÞNy5¿Ž^“øú/{o`ð‰¯³â¸+¶ ˜Ó®·ÛµÝòÝàôh%Ìš¸«0õ×€ËíáØz÷íãp\.ºŸ4].rë©^EöAyåS»Æñ•³^Í’_ugm's‘+ò­2tzŸÇg„¶´g;ÞŽ¸}p¦‚JŒlmÒ`£¼˜îcÒýªÓ¹ ¿>Õãã;ÎSenäd/of]wR-éÍh°›÷fAp«|V¯g÷-Ì”Á’Ít«:*˜ì²U·•<Äß Ñ÷àë&w$t¯Û ªgiì~^©T¦ö¯a³'Mþ(~lÚ®|`>óÍ›ÿ¸CC8ß‚¦!CBþä!®Sô;§¸€Î»ÎÞï·>Ïýpjôù}nĪD ïqžÚÍÓOgÆlf5¸pÿóYxVÃÙ¯Ýô|æ8e_Úë)2àóAÛ•ÝjÛãe"Ožâíø}çÉeëݪچ I†®öîÅÇ}œgÚ½Þ×aìvÖâmâ݈g¡o¦Sè•ù,¦Q“VŸd`ÓÿLú’¤`–]=%nMóåŒöŇé^øKîTºÿ¬Í1–ákÝjD-ïö°œûU…g0ëX=òQÉÒåŒöt¯ê›Ïeçî™õABçÚ¿Cã[|bû»Sg ïÎav͆³‡/Ç Ÿ¦³œÑ{៟Ìhî4â•×%±LþþU>€óp»çZ'°ÄZÜʳЭê±:­ËòÙü¸¨=Û:Ç‚™‹ÝØÌ–1º—Ãûw]±>˯Ï%ÚëL2Ž_Q|[qî®#·^kÛ/µ\°bfËk¬îÉwíëVs†NãzVô1 f|ÝŃÑÿO÷±é{äeëúøå~…ö}m5ÎS¤ßj÷ü ¡,çui½7[càþô¿äË,®±EÅѿփQ†Þ;åµtŸ–î#k?/Cœ‡·ŸÃÌRb¡W»×Çï‡]cUeìéè¯Î›ö]t:˜m[l?réR±îHñŒâ#ÿ>>K„¸(Ö§4þ‚󼯺üοæpÖÑjõ¾XØ3,ûCƒkרø¦‹z%Z¸@Œ]ãê[Ü‚Ùo¯L1U_"Þ¯ ïÃûždò}ÝE>>Jh¢]——á<̾^³¸Ã‘lb•7½ÿˆ…­ä®ƒª0½÷Ýü^+œAsŒÔ0˜ÕÒã~á°D¬—S<£ü™øßæ«»˜y*:~•oÊqž1ƒêµ}pŒñ÷ã VϨ1= Øú}µÞá?•¼É b-n4 Z*æ=tH¿× øLùËWçÞ8Ïœ‡¹ÇÞ?Á¶íxeræ—8(ÑÑ;2ijëÒ%×â\׃Ní=nÄæþR5vr¢‡˜§S]›î;SÝ–ÎÃ5þ‚ã;>Ò,?&Šé-w«[%9,:•WS®)`žõs7Y=w‚Köû&ö)bË#1브ÑïŠ(Ï¡sNZéýkß?Vãu,‡÷ÒÑþ‹ ˜‘—Î~ã ÎptaÝtÛçA̬s[¶W¿ø Õ;éþ¿/¬öœY> |œÿ…÷œ§êþâÔ÷óO3s×’Öïâ!&)´­D¯õ)Øé K5¯‚ØÊq¾Ÿoñ`d?tnÎ׳Ä{|]¥ŸxŸ_ã78‡-wbp†Ù½¿lÐÆ94ùaÿB¶wø³¡«¸ÀÕ_Žì5|ÄvÖçþÇ¥â:óõ½ÝóÂ}×/¿sÑ®£Ëpž_‡†ð?ÍNÇìx™Zœ…“ïµ;æRÈY/ ðÊp†ÌÓŽfWÒFfÞ¶„Ñ÷ ù¨ÞÀßC~$¡|„ž¯Æopž3uÕÎåg™Ù®¨¾icáa£™KÌw2Å.ñ­aù¥Î¿lË fAWš„­–-ï‡Òï˜è¾;ÕméžÝ‹Ôø ΦŸÞqóÀX¶öÝtîŠ|hµü~r![³ÿØ‚INNÀ«`~O7eo^$î7Èè~8¿ðõúd—»]˜;?ŽEfŽÜÜê,½snrÿG…,zÃÌCžÁóV;“‚~ fqÓ=k£ý(åE|Ýìp¾X蜓â\¨ÿ÷£·ÒÞJÿݽ•¨Ï÷}‰w]™gh^©g\”Àx•ýCvMåç\L™ÀtÐæxisˆãe#ôü5FƒøN“²oô2¡^r•{þZ /m¾q¼ ´zþVæ]gH¼k®· ×ó—ãxQ%õ7zœ«…çßc×PÌï±k´{œWî±Äñ¨¿œ‰Ðc©H詜V&0¯ µzœK–õXŠ˜†2¡Š¡À} žWøwzÏQ/àÊìêLì‡Ê½P¢PÜ*Åõ@ùÄF¹Î¿'6ê ߇X¯•™^ÒJ=bÆ¡ë?ä?Tî¬#0UZ¼›ÊýЉyÃõCOG™ Á¢*86~:Ê?U’¡¤£Lеú¶:¨U*ôlú[ }‹õn˜Jà†QßbK«C|ìdæŽR¡L„~îÄÖáú‹¢“û£ÊP6èìÉZýEµû¹k÷µú‹b ðG•¡l0 $£¸,ràí’ûû·ÅÆ¿ÄÃÿ¿â¡!*\ç /¶2SÌ£RwŽ¡hªø\ŠÊ½‘‹PæZ EbcWf(?‘3nW” e‚F(º+Ê=P0xW”JèÝ^™«ó-n¢¿ÀÕ±¸:†•¸±Ç#v¬¥!ÏÕáœÇ•Œ2¬Ô39e(ôLVkõL&^EJŠŽþ7¼ ê›,E Gés}“¶vßvŽ›˜Œ2ÀØ'z(K¶ç¨2T2Ê ;Pè­Ì±Å‚Ëv¢ÀN,ýNïvbëh³µÙ:r­c‰Î‹2Äàân8Ùß»ýÇþðG<”ëü{⡱ðyˆ)[™9Æ1e(]4Rw¡W<ÇYôø‡\ ”eÆ…Ò8‹j-fÏßõ/BIÑàÃQºhôî¨"”?¥‹àŽ*BIÑÂQºè î(ÊBÒúǧWê!Ïñ{ÒQ&è,¨Šÿ•)û=æ1e ñò,bkWpq,eŒN€ª@Ùü ×ÂOþ7\ tÈ(”Á7Û†è þ¨2¡¿<ÇØ.CÙ ÃÆ \ +ËqÇQe½xÎv²g;]àŽy ŠPæBïyâý$£ŒÑÉP(:{:Ê> RÿyctþT…Ð>eŒ UÁq·1 ¤£¸æþÀÛ*÷÷ÿClü?‰‹ÿ_ÇC.Êtþsñïßû¸÷¥ó…/[™OÆñeU(s4B…À[´Dc ÿœ )i8J Õƒãd ¤Z¼EâiWæ-ª´x‹ºhÌî(s4hJÚ¥B™£q+Pºhàî¨"”ù7X>ßâ,Žác\‰)ûwÜ2bÊÚò Ž¡-C¥£ŒÑaüQe(tœäö<7ƒxj”:PÔwxú×£U„’¢Ñ†£ôÑp=Pj”pÊXŽ*EY¢1Ç¢ Рå¨R-^·UвD#B飡{ Ô( 4ø(”>½J²@ãBé£x Ô( t„(”>:ƒª%E§Gé£c¸£T(stJW`©Pæ·Qp˜Ê¬Ùï±Ìˆ5kbˆÏÐç_›s.*e‚N(8š •Œ2F‡óG•¢,ÑñbQ†è|þ(5Ê0 e€Ž(G•¢,Ñ!cQ†•8ÛÇÑ4U²XÛ(:l2ÊPà™©ž™UÑ‹çm§kñ¶UÏLŽR£¤èØsH†žŽ2A'Ý¥B™ Ã¢*P2tüt” : d¨t” ‚@!¸¢T(޼rÿ“qñ?¥:ÿ÷ãág,¤8ø¿ÿÓñ{ö±:_Ø´•ùhE¨ÿÁÞy7uôíÞtÑE`EtQ¢›æ„"ºè¢ËØ€èc#ºlšèUËHÀ$š©+ÀØ 0 ÛÙ4Q£„ÓïstÎdOÈ÷¾÷¾÷~™ïâ™ß8™aÎZÒóüÏîžÕÿQB„±‚5 H H=p%„ ¤¨yà*5øC¬Fà*ŸG–³í›ãèôÉp”BÌàJˆ:H!lp% ¤¹¸€b· ¯ÎÏd8šh(¾ÉžYûw¹i,³V âe|¾¶(`š(ÅÕ=˜Çä¨s&àj˜)øÃPFàj+Ha.pLüa4#p5 d0IÈÖÖÀ|6 ‡@z€ZȬ•ÂŒzàò™Ú±‚A¹\5)Lª2åBfcдv €qÍ‚yµÀž#³Q 3ÛBÈlÌâjŒm ˜Û,LztÀöeþ÷eþç÷Ïšÿ©„ë±,[;P@˜fAœ\–mð‡HÀ)dDš€ ¨ Ú8àá¨!àx ƒˆMÀ4³ È hð5„d· x€"þº¸‚þ½¸âþ0€¸Fˆþ0ƒ¸€ ¦ˆþ0†8‰R˜Dœ@ÉeGÉ_dÙJapLü+}ʲ á=nÙ§ o Ì¥“YFÓ;PÀpQÀ40ž Èa¾(àj˜0È`Dð iò:Ùs¼í@ƒš“j„,oά:`r˜Ö$äÙª`ÞXÁÀ:àòF|Æ·(`hpŒmÌ­“[F×'€á­‚éuÀ`~+ è€ XÅ@œ€û&•™ðZç~þ•ùß¿[ÿ§ÕÀÿÓš÷Rïþ®Æ±úÆjÙUÇþ®†ý]ýâjw°ùñ¹¶Q h!;P@<. âÎ?C<à*ˆ(ø£>¨!ªx ƒ°L@Úd>yÛjÔ¦x óÉÛVA„qÀB4PAqÀ¢4PAœqÀ57PA¨±@ ±€ (!ÚX põÀé“·-ˆuÀ¾ÉžsëJˆ;H!ppU%>çV¡;@Än¯ €ð£@ÐÀñ@˜€h`˜ÁÜ@ SÄŒa Al@“D ÙÚZ˜ÅÎ}ïf‰Y@#dÛúÃ8 ²´ã`˜Ç!äf›é€ÀLVn­ C9€¦2 ÆÒ·Î ¦ø)ËÖþeÞõeÞå÷Ïšwi„Ï R ´ Ä©ñ@‘š€ @¬QÀ Ômé¤ ¡›Fí}º?LìŸÈ÷ª!öÇäó½Þ²þ1¾y¾6Œóýo½­œd£c—Õ[¢l$ôcÇÁФRõÉ®éô×áÄïÔ3†òýxÂÄ\/–ßÎòwø~'¹Ä¾»¬o”×/§D´€ˆ‡6:a|Æ´íNy=¡»ñhÃTZõܪýëf“àê÷tªC!\§š0Êúõ°þ§¬Ï ëGËú’øöµó›ìVVlönÂá'(ßïï!%ó ˜J5…š]LBšy Zhƒö‡ÏΣ¬/߯©0Î~±oëwÇþ×/§Ï‡ýG';A·„Åçœ ‘5ï\h9/•þzÓüzt³¤œ·Š…6?XðϳGÃ(ësÄþ^–‹Å÷ƒqÿe]%Æ9ÙóGå󜤅å)W:ºNËÌJ{5»Si¿±G/v]2‚Ì4×r¼…nÌõrdÊÌ0±ß {ÿø|G{ Ëycý}óê´‡KÙ=­>IÇ76Tž$®­ßïIK¥ë»žûPÙ';¯il±Ðw[ãzµiFY®ëÄþ-¦=99fá›Àv‹Ö¶ŠY[_¬C^ÿ`œK;¯¿°ZOÒš_WqIÆ8¹é»¼ùÓ艪Um~AÖM/ѽp -âm$9Eì«#ö7õ¾·˜Äúw1ý{ýƒqíÿúÁIj:\\9¦ä)’1„ N£=Nª•up):ñškVßú®\§H}¾)b=ö›õñb¹ë¬îx}ƒë_éúg‹íõOÑZÞ¨SD?k¯¼ãà4Ztý¶y“"FUÓþçCÛÅн?|׫põP±ßÓ-ëCÅr5›r¡Ç¤º¢¯¼¾Á8ƒWMèr7ò­qê÷™ùSN‘e¹—wß¶ Ê÷-“÷mi•µ(ûO -?+¹@Þ¡b_%Ö/šåðº¾'öÙöí—ëêV̯ο¿zŠ÷ü8ÿ\íR¾Â¦¸«»Ó¨_á1º¢!ätïãwZ(—Z|dêÊúþ°×Ãúl³~¶¬¯žo®˜ ãÜç>®ohI…)&jI†Ý¸žF_~xi›L^gÍ{rt™…vm¬:ÑÛFY½bù|Ÿ® 1×ÝRûv£¯65ÈÞ7ã *x³ú¤~ tyrÕ9 O™ÛÍÒñôÇ4Ú¾Rëê„™*®c…¶yÍÖ¯`¸ØÇ‘å7°¾Š¾¹9Z\wpàÊ•$Ðçb Luš< ý¸¹T­tÚd€jD}ãr9£òÒ‰ZîÝûáBÂÄþFüçÐPì«Íò¢Yý÷ÍS1bœyâ&½?’@/¦ænû8ã4Im_÷Ø÷}Ò©®Ø÷óÇŒ$e"ö8c¡[¦p§õ¹ Y—§uÊ Æb½d}èræ³y}‚qꟚs8"÷izI–¯Gåþ‰äþªR§®F¤Ór!7*^(:Š\Ì:ßç' õv…3N¡%î4ÙýhG Âú ³<9–¿æõ®ûtdR®ûiº¥âæKIßÂMé/»Ó鬹÷Ž/Ù9RÈc±ÐÀ¼}·ôÏ3…²þkL/|Ÿ)›Ø‡Þë\7·uñ7ͧi½¤:ÒÖµ’HS?[¿]7ÒéÇ‹¥\A˜òØIᯚ® ?AÎ-ˆêÿë0òg˯ó½A}ë ´ ¥\O~‹’ðy•J±N³zÀæ%^}ãú'ÇsDZx΂fc=vRã‘V3à@©siËó:“Ç;#v~U7†v‹löcψPÊu /0 ©Ø7—õ äû¯ 9\¸nÖÇI'ç]J¤yÌ õÊ.gÈ*õÆE}c3ȽÊYó¾Ï3ˆð¹¿ŸÞ–O‘3‡‹½O^=ãºÞتç‰B_Î3dM£‰Šú2ÈøêÕÍ~@Ö„TX¾ë²…òùW¡”­Xßi^w×…>ŠOy=ãºßMJÚð®t”ïëG«ž%ºÅ¯\žA~ó,»>¹ÂRáÍ©EVXh«±‡èv…Ò£öõíBë{ÎúdÏZ‘U¨ü^×anåÎîyŽ6ITÙíÛA—œ%”^÷""ƒ¬¾;±ÁËíÈø>Ûò èa¡WeÉO‡> ¥ûõæŸ÷kFX~›×-¸|~ßÞ?¾~ãºãwœiL¢U¶–äz}–Ô¼RôÐñ)$È3}úÍ ’n«Û»¢…žnUgôéfS(›°~ö¬/9[ñy/U³Ï{0NÅÄ麓I´„wBxŽTË«n:2ƒTš½åä¬ECHPé´æçs[莲eïÖ;9…²ûPö>œÅþ˜,¯Ã«o\¿xñ wŸ%ÑúKOÚŽ„ Ý{·_Yöä·ׯ#~}oÄŸ~M_4ÖF½ýé>Äòù>…WÄúêÕ5®ûê×§ûšT±Ó>MÚ¤ÌòK&ɼûºcé³~n¿Ò-µdZ²rÄ»·Ñ´K®Ñóì£lÄú³vðê×=Û`À9!v:!méißd±¬Éxi³ R?ÿéö?Ö’„3 ¦Å/>hzã0ñ~–sþÌ|ïÕ5®[bÙ3YøV;µïkŠ’›L6øõáÎDRDµge{-i}Ùuà€'šŽ ‰:"é=EÌ¡b}Bùûðl~qẕã&ë¾j§S_ÖnØBvžìîš8¦tñ«ÛjÚæ&ZòUðF[*áþ+}‘wk¨Øœõ7åÿîÌlyx~ánå´\\‡ö3Tºûq™' ΓqáëKçÏ |î¼–oMè<ÍB yo(¡”õ½gë«Fá†ã}—Ý dyÄü<©f¶>†2Œ³"³|Ø”ÁgèÛ–\âêyò{ØR]^¹H¿+/Û]Ÿ?Œ,k»rÀ±S:ãHõæÚþ¡bþ ß7·:aýÇùyÉÛÀFcŽw úKâúS¼7œ3”¯sÈ” /n¿ï"'6ÖÔ¤ì0´TõõÀkªM^»c±>”²ÜdÖ•õ#÷íË®Åuÿ¬<ñЛ=ghÆã±CÆÌ»@:=¹#ÿuù!xZÿÖÚ¡¤ÁÓ oI²ÐGyδ\ØpŠØW”õQgõåµ{uëŽ[µ81íöZ ©Î¨ß.cEg<.jw‘ò½OüÑ`ôòhéàj¡3¯¼HÍu=Lì‹Ïú’³ù4ËÝóê×Òpù÷OJœ¥%ƒ¸dñ‹äÔú‰[v‘¬Ì%¯ •‡òáòæ@ÌsŠm‘fΘJYŽësÍòpXîŠW׸îê—=l{–ÆW.}Ô}‘8O.ÿIÿ呲AÑ¡$o³^Ti:­RÇÐ{SżW6?^í©õS‘Ÿò$Y¾ß˜Ï+waœ7Ž|/¿ =KÏET“®p©=Û<Úà"®²öbókkÉ@Kô¦ø;jîþ¸iìûpñïç??…ØW“Ýïù÷«A¶>Ñ~SñzæµKKÜx–ºë-Ú[eŒƒ¼íÝcÉÈÅ.2¡ÂÔŸlC‚HooÛhè¾K+븳a”å%°û.ËCâëú qÞ컿!Ã8m ôrë,}ÚIZ,ÆA._b¸ÁEº=˜VãÑ‘ ²ºA`òК÷~åÞyÃhöü¾Ô@–«ÀÞÇlýˆq}åâ¡Å ž£¯ç÷_Ù꺃„-ùþñH œ}véW=‚HEýUZèÏÆ1dü17Œ­ÃY?bÖ¿—åÖúæÈk1ŽÁñäðÁ*çhÅ>™-K]"ÚE=+ëú¹HÆœ+«kÉ)cõþ{²,ô„~WЂÄP±_(ëÎ|ÀòQØ:“Õ'¯/0Žn¥¦×³Àst¾¶ï±wÝ/‘ –j¹§wp‘èåƒj.2%Ë®ÞÿØ·c ít5¼äÌPÊò'ؾ[7±÷-&¿ŽËM²õ#·bœÒ'>öû5øMºjßµoé%Râàë&ªf.ò¾Ü-ïôLÎ䮡:ëGËr³õ%Æ8½§µ<9úÄ9Ú ZÍ’ ]&¿ßÚQ~ñÏRflî¤!Üê¼^Ÿº&rBýÑKÃ)»±ßüç’ ¼ž™ãégp+UÛçÕ|ñæ­üðJí7ß]&Û¾_{6½„‹h†dV=–§?Á¢&¥F÷:sØàŸ/—ž*î±ßl}Ãê!Ëbý×½¾Á8w~\Q²F2m¦vYfN¿Lª–?³8¿‹¼ìUäqå°~¤»7P8†zÛ~Ÿ*®#ØovßgŸËòÍÃSb~ÞL‹=²/#î2é¨tÝ8üê69øÆZ¡†£iàØ_ßZ&†îk=ýÔ¬»á”å ³û ëÎò°XÎó—×?çRx×›k†$ÓÂ)Ã=Ч—IÅŒ}®ö·o“׆¾éVFC¼»AEbhõU½[žJ ûß³ë°u:›ï±œ^ß}'#Æ)Óñ^1£“éÃYwÆ^¬{…p©.çNßæï-’˜'ÜB2†–ô6(çKì~ÎÙmA©çLßÅ^¨Óôäsz¬¸>¿¯’LïvºÜdÕÄ+¤Vî6Õ{ÿ|›týáÞ»¡}‹ïWïxª˜»ÄtÍr…YÞ3[_3½{}ƒqU¿÷,tf2}Ð$|Ñ£WH }èÅaËnSî¤Ý£Bfí:0|NªEÈ_ž*æ”±} þ>ðG ¿^h$î£zý‚ë/ÐÈzki29¸'õûGWȸvK’¬n“ÔoŸuÅz¬µ7Øu“[FJ âþ<«W,÷‚í£³ÏÝwßÙoš[YwvoÍvk2ý¨1‹¨šB:tÚ2ÄÕí6©û( êo'´¤òª¥Ñó[[hý„G+E b_z6b¾d}ÃY¾œouÆ™æ—0äX2µÏ¸zvÞ°b™ÜòQLÛÄxõûª–ˆ ²©øÚ=›6X(·[_ªáTqÿŒý½¼?ϲ}nþ󨓭¿¶ã´ônÄ&So ÁÚ2³Ç†šË Ü&\j·49ˆŒ*ÖéÛÕ]cè¬ü)z>góf¹Øßží›±üEk³RÛ”„ݧ½~Á8½fÎl~+™JVw;tòH ™´öi̺{·È’É û›Ì "Ëâýw^Cß+8Ï2<Œòëda÷–—õÁ|mëžcÈWÃ/×óùF\D¹nõmÉteþƒûç?H!}ÖÎìè>y‹| ãVZÒ¼º¬FFÛ5÷«‘i„çJ Å}S^¿Š9kÓ .ŸÑžt¾°¨õ¾Mx¿`œ{—³pgN¦[’2EË^%׆¨š¸éÉ,q®|D=-)´e@jà] åVíÇkCdzDz*€°œlö¼‹³“\¹fÒ¶"¬nzý‚q~É÷ʬ‚ïÏn·ér•|(Ý{ãúˆ[äÔÖy¥í›‡ 9à1tÒÎWqe (Ûobó_–‡˜”89WÅ/„\ùfÙs1Nt©ž%g&Óúä_kF\%•O†ôØt‹ü~{ò/× ÃȺ¨âÓ›þCù܃ø<Í;Ø|ƒícyPn¤®UŽùÙt·2—w•LGÿ\óùŠãWÉü’ «¬j{‹°üÀÊCãoo‰r â¼›Í_X®Ó[¡´»žN5fË%–aœaÞÀ€dúm`>›!ÿ5ÒRõCÅ¡•o‘A gX]!H܇xñ÷NõÌç (ÿà¦øÜÁÿã°ª”B½ásc”‡ßW>GŸ–³ OÏk¤×¢MKZ}¼I.5ݲäIÃIÛ@wêŽ7Ñ9r6ê“ÏßÞÒâÓþ_»8Ä{ãmCØ>·×7ç{‡×ScÙ¥?¯¿F\ÝgÍh˜v“ü0»5üL*{o4Ñô­æÄÄï–N¥ìýgûZ|ý]È ¸#ÌŸÛð¾ÁõîÈA$Ó.{¶Lн}ô•Æn1í¹I¶~Pd=ú!„œ˜T¶YG4sþç‹WÃ)Ÿ÷\KØŸüVÌ dï[‡±ûª×7çip>Z´A2­|¯æÕÀ ×É‹ú=›Ü$íÚßOOn8‚„ìž:çh ò´°{~Ë©bŽ([°œW>wë•8oòÍq±aî©f».É4‰‹èwLðÏwâΠ›¤ÔØ!ÅÏA¾óÞ€,ôÌŽ[á:eûËl‘åB1]°çD^¿àúeÚŸY¤ô5sÖÎ.Ë®“Ê]÷³Þ$ü>m)t¨×ÅHu Å=Ò9nš˜ëÉê3Ûg`ûó¬þ°}¯_f¸•¯-5ÝÂ8ܧzýèur|ù³kîÂ7ÉÇ›]×þ8s8¿³þWi“c蜛/”¼8MÜßaù,_í§ñûx¥²ÍÿdGq­™âPOÜgÖrøë$ß!ÅÔËnä²F5MÕ’—ñ÷wCW”ÿ6M܇`ó%v?û©føì Å_ú®3”¸þÊ5Ãòc>ÖÙHå$B†éBo†Á6UÅÀaÄ[¶[ÄÐwú(é½ÂÓ)Ó?›§°uÆÈò©] Nåó´¸îÔÍÕª]nšLß7ô¯úÕ|'ysÛïaÏm7ˆGV·ìòCI·ÍC=£1o½Ûmö’îk§‹¹bÌ,Ÿ¯~;ó“‡ß‰ó/ßû½㌾7TÛOŽy˲U'&9IÇMáÍ‹oÿà#ך !Uz]©·õ‡Z®Wûßk!îsðûôuÅù ¿~~hÛ3êâˆ1 …< >ÆŠq¼2ÿ&™–¼\nnwI*y”ܺÚ̉7È£G*þîL,õ_ y)†’¥ÒŠãgŠ9Îl½Äòn|÷#m¸.Ÿ3›,ÌïSIîôòm=½ob¯Gåÿ=b Ã}Â1”ÿÿY”íðýYVÙ~¤×¸®±Øó{ùœçhÂÃÎçŠL%ùÕînrƒì87ïð–ÃHÀæך Œ¡íÅêUMY~9¿®'Þoùû”°/;Ó­|DÆÎ~÷Ó9êŠ.ñÓ±©DJc •¼A&XGD.Ø¢%»Œm[ÏÊCWû)ºþ+–›«ó×û¼›§Ù¬ÉCØó\>±a¶<Æ)0¸²dâ9zÁtù–«pÙ[Çtc”'¼|õ`så;A¤ö‡ßL…®Zè„SrIÅÛ³)Û'`óÒ¨g 6HÈMØ}¯_qó®¯b^—WÿçÏ»A›®5>G5_Sįo9аUÏñçÒIçjá_hLöôÞQnÞ! í×qù”üfSv‚åâ2²}|~~¥Èž“‚qÆÓ¡'ë,ý±ì’­{W¥‘ñ¦µw·¤“Ÿ†¬µý© !÷wU¿;7ÆBÕ™¡5sf‰>fë'–×ÍæqlŸ;›0ÌYÞ°ÿ,­¸¥ÕóÉÎ4òâÑå°uÓÓÉ‚«Ìý^‡}^J[l¡…¾}üat–X—Ø:Ž­Ù9ö¾úÖY+ƹ•÷vãYág)¿¾I'ß)4Gš©Óɰ͊[í» …¿»ûâåL ]< þ‚É›gQö<—åH±ólß‹ÍWØ¿óúã$N,T§TŸ³´ÄÏ)O"ú¥“ yÍîÌêédUƒ;_= A>ìëi)b¡ë×jÊ.›9[œg³ýnVgY^Û·ð]ǹ0NRÙÐëõÏ ¹\餦åiøð·idõ«üÃÌ]GÛÛ¹  =aóÔÚ®Ù9r檈y–¹v»ìÝ>²¼él¹¥³ÜÊ6Å:›G¿=C¥ß¯op>ÔÝмÚ~GiÒùƒæ7!äBæé“Òæ1šé•逿ù–gÄò4ÙýÃëŒÓyvÂnÍ©3´w\bꔼ7ÈKË®ÓnK#»›ü”ÿîàR&fÝâeÓ,tö¶3K®™-®KÙ9þó¾Ø!qŸ±œ$aç!|÷•‡îxF˜Ü »iꮘ“F¼Ç “©´çæ} ý}x)úølq=Ïë® ðœÍȯƒó–3çûz´go5îIÐZ2>¬Y°ä—µSVÏF^ ŠQ×Ùü¹¯9âóÞ§1×¾’[|þËÖ^ÿ`œ÷?¦m»Óà uQ5¯CÜ ²/±ýÀ‹MÒH×*ýÒZ§‘©Å³šïµÐš£äÏ «æPVøynqqŸøôšb íxx¯} ×uUÃìç;0NËÉÜ“K;2ì¸þîä­¤ÿ†%Òˆ§Íñ³ž‘ ?Žû~ß7ñõ°çÊlÞοžßÅsXüýX)ø˜_×Ù0—&uk›z§§²›äÕùÍå'=M%wž+xqÍp2£Ç½}ÛîF‹ºŽÜ±­ß°!ÕE½±×Áò=q³¹¸nÂë%¼0Îäñ±þ&Úé‘ßü+¾p“~¥så¨Ó4o¥¹¥¶ ¼M>¼.Q®¼ÑIë_kÕ{(±žØzaKæFÚÊûF)Ë“ãë}uñ»/ð×oDXΚ×7§M»1i§Ÿ¦mf‹®·ä6ù³_µ±ANbòêq÷©CɃ„ŠiI…bèéï TYÒ8B<_ÊrèÙ¹þ~pG\GúÎ?”§Á³ü—^J öaSjºM‚Ë^ªK±~¹\mT™“²a¤BëÙ¯l´Ð.go>ÈÒEPvþŠÝOÙ~õ 2ãÛÝ7Þuí»^Õbœ—‡ÛkwLL Â_­)ðð6©á^¼óm'IŽ^Ôî]E-i<ºwØŠq1ôÈ“ë–g­"Ä}ö\‚Ëeû¯¾ó#®Ø{;L ºŽ ÜUÔEŠîtbW.¬Ãü¾Í-IÐ’µ X²n¤Þm˧F1ßš¿j ù†¶Û™°în:Û·âïgV\é(‡ƒœ8E7Ë5ÿ¨æ"–—êpë:™µ 1Ÿ "½/p#5Eìš•ë˜Q<Ëö_ùó×Äýißyº ןԺÐõ-úS4¨qô…­\äXxÂÌ”Ã×IÏà_íÍLZÍûýJ|³Tv)ïbM‚QÌyfŸ‡µúìËJ¾ßV¯}Ïó¸0Îo¾¾;ªê)ʹ0¾‹Äwx÷êëäÔµq÷ª &?.éVg¾2†úÛ—Tø¡Q\×3¿ñûׄ}–çì9·ï¾®ŸÑ­l|uÕ뎷OÒÎ܃Ýq.r8ìé‰c¯“cïé솲6oìöc-tVÁRå‡J#ÄývV—Ùù,öüˆí÷ù>‘aœÎ3æÖïºõ$½ùtúèÅFIœ¼5duÛëdP“²K. !±ßÈ=Ý-ôâèÒº©ªñùÛgûRLÅŸdÕWdÛÏWâúxŽöw’;vúÔŒå.ÒõXIÍOe¯“5…[$Bøy¹…þÖ?×ñô£Ÿ|ÈöÁÙõÙ¾Ñþ^’»_u æSü}L‹q^Î=¬ÚT÷$ ý³yàÀh¹Q©ùöžkdEÇÃqsÜÁ„ßWŠ¡-o÷X¶nB¤˜gÊÖ-¼ŽßˆŸ‹¼c®ÍÚÄvÂ~ÿĈqRóÍûÌ}‚:ƒº¾™±ÝEdUÒyì×— ÝzM0ñ>_C3ó•Ø>ºÌ\ñœÛoéwoéäéò·/Ǥ/9õÂX‚;n¡Høyz[Þ/‡¯'è‚;¯‹Ät‘ oöVªµùY’¶yR·Áäl“| [.‰¡æ›«Î8—²œ_¶Þ`ëZö|Ê]úðWz~_®9’{Œ×ý= U­>ë"£¸ƒv3¯‘ü¯GºN^ÝÑèxýºëÛVõK}3—z·åVÔs1sê™ßhFØ~™×7çyyɳvðéLñnGk®‘««¹Öá¤N÷ý¥GnÂü|îóíÁk>}>ìy«ûìy5ÛwðÍçô‹p++ŽíÿÐÓcGï{o]¤VØ™€ ¯þ~L7ŒmPëNï¾p$eçÊøýÂÒâ<ƒéŽÝÿ³=wÁ8íSÚm\ÑÐFœüW–Ì ÉU«MUø 6—Üw&#˜Ðƒö®YM3w˜Ö»ŸGÐ3RÃÓÊ5ÎgU÷÷Åó9BÞ´o½QbœÊ†™3®[)M´µX^©vÙÐô÷„Ì«|^³=„ØÏëó’hÊ}À|=BÜeóL–ÏÊöuÙyK–íõÆ©·ôÅæpzœ®Ê]X{V™A¢N®qðàUR¤ñC¿ËªÄ^:šîÙVáÃúây4¶náϽuÍ뾡øz½þÁ8CzL’uâý¡3ÉxÒ/ƒðçâ¯ÓüWŽÞ !C‡´ê\=šZþä FˆçŽØ~ ;Ïêô† Û?_Ò0Û|ÆŠq‚–Ü ü%ù(ÝôÇî{kÆf{òÏSʇ\%ÕééE!¤ÜÛ•Ë Š¦ý®žû­ñŸê¿¯‘Ø#Óˆz÷Ùç–í¹ ®ßëjÞžæ:ÊŸˆÈ ]õÜI «dj©^³{= &ë–7¿èÄçÿªVRȤ”?÷þ)§ó`Ï)øý‚:ÙŸWbœ¹óW5žÓõ=æ÷Ö²&ƒªÔezhÉ«dÔ»==Û‡“ô›y°t‹¦7óï¿¶(’²úË~³s1ìËÑö½?ûEº•¿Î‰É_dåaZÛ{ (ƒ$´n9?—'…ŒÒw®/D6uÞéÐÛhzæáã×ëïEŠë@ö=)þœw–pÿÈêD¶ý4ŒóËcu·N×Q]« Óâ3H¿Íô¿BR4EtÌZ²aÙó¨êz¯ð@•Í)ý.þ|Çká<³pž×å}}ˆb²õí¹ rek{g—])dÛÕÇ“ZTÓ’±1UÿÒÙB‹¥*;÷}zŸ˜ïÙ÷TØ:†ùÑwŸA‹qV•K󧌧•ØòN¸™A*Õkþlõò2ØTi猰adq8' M š?(pe¤x®ƒ­/ù²ó÷¬žøžç±aœ‡#Ç'¬ÚOWÓ—Ì$ú=›ó_Q¤Žùºˆh©%•F­{M§÷µßVq.e÷av~“åK³}Ó¥2Ië´UßX‹§ïôË£% «U›×õŒ¦áåIŸr 抟Ûÿcç‰Ùóü­"âzïhAØyf¯?溕?½ª’¶|û^zôáÚ ™•2É¡‹ç^÷ä /’º¸ãó ’^ÆQxj½hÚ¦Äÿpv?.'Þ7Ø>&ûžJ”¿k ;‘#ëÞÞ•+óþÀ8_ãnTÒü+^öôä.u2Éà‹c;&']!µG™½éÉpr«ÅÇðÔݨ”|lmxIÙsZæg–û^©h×’ƒî¿ìá}[ÕdaìÉçùüo%ƉáMßC×W±=Ò*“l«m¯í·é )úñaþBGƒÉÌ`‰ßòÖø:}-’²ù#Û_˜6oÑËw•^‰ûKì[·{ý‚q¬±ã4½ÄÑðÌÉGs÷Ì$ßwh9Áp…t·Þù0ó˪N Ú/qsï®K‘â~ Ûÿaë"¾n½žƒe?gcÄ8NL¬[wQK§ ?ÏÉ$Wúü¼qe÷+$W×µ›­F¨£¡òïúFÓÊýæÌ ²EŠó2¶OžuÛ³Ïâ—ò,ÍûXÝöúã|wÿ¬5͵ƒ6¯Ý¬g—ÐLâßrÁ¸ÓU¯©mû,(={©6¬à »,´ïÛ)­FZ#)óÛ7açíÙ:iâpU÷šdû‘ ã èõ,0©ã/´À†+ÏÉ$•Fë ʺL2:TÛk {¸Ç†bhûƒC¯ÌŒ¤ì\%ÛfÏõyÞ äï£âzÊ댳çOî€èvz*òm®âK3Iɾµ__¸L¸Ó¤c„ëÝjí^ã°PI‹WW.¤ìûœì¹Ÿþ{ {NÍÎuø~_Íož[™kü/FÍÜFß´ò£zm&yw+¶ÔÖËäÛC'ÕL &¿»ã›ß–GSï]ÅIÙz›=bÏ ÙþÂð-;nwíÚ‘ðëPÁ?󘮢†¢o.§oÌ$ËFô½ÝgÎe’®¹ÛðLÕ`Âî{µšFÆ î-›K§.FÜ“Ô"ìy8¿ñ2¯  çÛþYSÞ?‡”½sêɈ-BÎ$}/§¼k;à2i]yþ»YÒ`rì¶.©(Ý@ý§ÎÕíÔÍ¥lßÍÏØù4v¿þuÞkAØs*¯0NÏ?›;“M´l’ê•d&É—rïVÑF—I§• L¦çáÉû®jª{9¡¥m®¸oÊž—ð÷e§0/ËÖOõÄïÝxýƒqÊùzeò# ývp¿ F¶L²©MŸ:C _&«Çl\žT$„Œ±¾Æl£pÞyžøý%ö9mÐO]@—!Ö7v‚=ðúã´=U{qÿèþ÷uêt;›Iú ŽÛíȼD¢FŒ,¼óyÙ{©99=ÉJ#ö®®[sÓ<Êöá—ï)94²w}¾'ÀÎù²s켉×?gqË[wºWÓEC;ì¹q9“´Y¡]åøK„ÿ>ñ² ´h£Æ1VʯóæQþÜKMáo]¯›ßˆë V'¼¾Áõ·~·íÁMM]JŸü*=“”Î|ÒàÉ’K¤íØòÍG©÷À¾•6¦ëóä GÙ}‹Í—ØûÄ|Ãö}çK~óÝÊ]!Ü¡…t}`K×áÌLòÍ©?؆]"‰ÑE¥ÞAjô¬ŸÕ¸æFúR57=íä\ÊöÙ>ûžÛ/cÏÙ¾éf?þçK/£/½Œþ»{ù ¯ÕáÇ÷ ù«>l¾¹‚¡‘õ3Ù ¾™«2!7Æ.ôfóÍO0 =$UB¶`—Á*ôñò´ìB?#ÖG\)äR{„>m¾Y ÎÏd)(ÿ¢O›JèÓ&õéÓöïôñíÓÆúêr}ÚX_].—šËár©]Bß¶œ½G”Õ>åR>““³ä¿ÚGÜ$ä ª„~F¾ÙZ*ŸnÆÙ‚2¡——«ÅõÖåz•È„\AOµô/ú–HavÃßäQ.WåQ©‰_j¢ÑïŸQ%ÂkqrŸÙ_ôR È‘¥ÅõÔU y2ÕWÜ7[P.d*8„K¾½Å­B7µ§Å‰[ìBßJ–+#÷é­«2W³„~K¾}Æ]Ÿé3®ú‹žKj$øûô]RÁ,qÀ†1PÁ8qÀæ17Pçè»ÄzWr½—¤2¼Ÿ¹Êe+p™«n „ÉbF3Pùd®?“­³ÇÛ¿Ú[7JÈÔR yÔ¾™3\Ïq®G“ æ5åÈÓ’ YÔ\挆¶¹¥•哵ꃀ ¨`ô8à³ÿ&kõsYZ,k•Ó$÷ó?¡&~©‡Ÿ¯‡ÿÔ|©ðß¡ÇeÎÞ⺹Z!oÚù™—j!W·Æ§¯AÈ[• =Æ}³sæ ²|™œýÅ}û\²þâÿNŸKßþâ\¾ ë/Îú\ú Y \æªÿgú\|2W?—·³'Ý¿ÚÏ×&än}2§ý…\·Ðß2>Gæ–FÈš– }-5BÖ–Ü'cUõ}-U0vÜßd¬~.k‹e¬~™þÿSÿþéóAáouqŸ iˆRœ@™#cË%dMs93Õ»Ü7_P!d,8Bè_îz˜Ç ¢Ö9[ˆ[BïM£9£ðéù«2V9ákrô2w¦—9׃3È` ð dxñFàj˜%È`#p5Œd0 x€&Š2É$ôáTÃPñÀ_†× ã3V¹¬.cÕT0Yð‡ÑŒ\æ Pûd¬š>“µ´0¢(þž¿f!kK#äOË…>ç¡Ï¹ ÈaÞ¨9[ !{š3³ØBÈØâŒÍ²Ue0¸¸F2˜Ýô7ÙªŸËØbÙª9çƒÿ?ÕC¥_öZø¥þ¿¯ƒ2 ¤Ñï\Ÿ#kK*dK»>“1±Z„ëq.ä7H ñýÎã…œ.gÐ EøÞÃ\ÞVmD­²¹Œi­Oÿa£»¥úžÿWyƒ,o&gßsp—ùÔóÜÜÜšñ‡AŒÀ Ô0J<Á,¦ýιœÖï\…7Ð*ãóV¹L.oU3€ ¨`ª8àc«ñ¹«êÏd:Ø€†‹Y@ûoô&¶ ¹[¦ÙÒ2!c†ëu®Qm9²·´B®´Bèsž´Bö–Â'{U 3ÇÚÜ@ cÇÿMöêç²·Xöê—ùà—ù ÑïŸQåÂßâæÞs2H!JpUŽÌ-·7Ÿï¯û¯ûæÙ. ô`Ïz°sÙƒî9‰»%…¸ À B/v—;²„ .{Uáksôc÷üE?vÐÀ6 ‡1¢@Р6 ‡ILÀ4\ö4Ã0&àÇä0OÈZ˜Èä0Rð e2^ƒŒÏ^å2 ¸ìÕ, †Éâ F3Ðød¯F}&‚ûptÀ`F³`Hp€Ó $0§8RÈ€ð2 ¸Lj…л˽Ѹv €yÍ9r·„Lj ̬®¿»¹Åe?°ÌU9 n Ñm@³GýMæêç2·Xæ*§G‰_êá¿W¿ÔÂÿL-ä>Ã8îõ@„N Dý‹RÒà“áÇeOäãs§ÝŸÉTB°±@ Ñê„ )Ä«²åBö  @ÌQB¶—¢Ž’b|N¼?­6 2)¸|¯>ê_È dY8 !l@SDPÃñ@ƒ˜€¨a”x ƒYLÀ40 Èaœ(à!Ç<@-Ãß/ã³X BÖ—¦27PÃ\ñ@æ“ǪùL^…(`<³`>°LhŒ¨CZ¦Ô ™ ˜3*Gö´\ÈÀÉZÖî“ýÅW'äNsù7fÁÄ:!,À'“USÇŒm Ám“Éú¹\0–Éúe^øe^hôûgÔÂa,÷žBqÀ¢4rù;@#Ì“—Ï£¶Äj hm92•°¸B6 bÖ Y‡RˆZ'ä‚ùCÜFày”ÑÃeS›Ák…LV)„¯6 ‡¢@ÐÀ6 ‡¢@ÐÂv €1Ì‚9tÀ0IÈZ˜Å0LÈⲪa;PÀ;Vƒi(`4³`6Ov¬H`<=p%  ¤0¡¸€ fŒRÒ\@cƘÓÜ@ “Æ)ŒªN „ac¦Õ'P¼±@ € ¨`ä8à3 ¨`ê8à›“ë€ÀìÖ¿ÉŒ5P¡Äcà§ÌXNûÜϪ&²zÈj«s¬¦ýÓj«_ÿ/kÖ²Ný§k”Õ¯OÜ{d2ÃÜ@ ÄDb Xl@žÏ³ÎÇäOPC@ñÜ™g(€;Ó ñÄsgW žî¼2÷<â±9êHÈâê Äd Ê,ÜduÀPG¬Â W ì@:bn¾Z` Ñ,ˆQ uÄ*Ü”uÀ¨#fádÜùdˆ7‹«#°(|2buÀP7¬@‚º¡N D݈RˆÞœ@ ñÇ)ꆸ€ u#øsÏrdMÀ$V QôÀ ”0L,Â4!gZóÄ) d®&ŸòduÀ0”Y0•8>“)«„Ñbf3Pòy²†/ó§/ó'¿ÎüI#ü[NŒZ`rˆ2 d-Äi Ô,ˆT@±šÁê€È!Ü(Ôp,Èj9H!f=°Dm ‡¸£€¨ r+È2°c‚×;Aøà0€H`p€˜Á $0„8ƈR˜Ãœ@ “XFÑ'PÂ0V iôÀ ”0O,ø_ìXiûî±cǶƵ+vì±ñĶÂÚ¢XQ"6ÖŠkÃJ@]cǺX( ¬5öˆe"R¢ lØ#¶¸ºŠû¹'3ïüë®gÏwÎÙÿ9ëuý®Ý¯\óšÉs?ó–É};A@ (!¤à1€  ã3€l †¸ À óf €ÐtÀb @ ÑÅ'/X€;¨2ˆP¬@1ê ‚Ô+PA˜ ‡8µ xA¤ ƒP¸C°zàÑ p‡xõ@k€¨ dCÌZ`*ˆÚäv °%®ŽyÈJˆ=8AðÀÜ!|=Aü`*4£h¨HЄX†ÿÓ=ñÏæN|ãûØßíY·_}©Wý'çHÿ7{ßþÓ}ˆÿÜ&à‚Ђ,à…B0C(Èj… ¸¢0€Åa®(0à…"1òïóïwðïq”ŠÂ˜+úC˜øÀôf @±è€#úCÈJôGô` ôpDoðf @é€#zCÈJôGô…` ôÿ^†Ÿƒ7W|àú3P è€#z@ÈJô€à„,À=@d(f p‡þõ@ýk€¨ Cÿ`Jè?8A (!„à1 p‡(ô@ah€¨ =A$`å÷Úù=vˆE±è€#2€‰NO °wˆHdмX ¢ÒÞµICßþŽ–ªß!á[ÆöÃøßÔòg¢l¾öùïhÙßñKsµ?ÛïâçkfÎFÙÞW†àh`ûsMüwù<«²ØÊßBzyÌ’ò©“ºmœõ Sòɪ·»kÇŽÍÓ$ßúÕÚ®^”.å^Þw¿IóÂ9–?(ø´Ïáì}”§ÙÕroÕFs×FÔ¯þ4“nœÚšt°X™¼yÜžGCi[𽉧„sƒuQƼa!óIdþrÌŽù2ß{¿V5Æ©Z¦Î™{t±UÁa»_gÒžÙ³ŽO½k¦˜ Oãý(+òûW3ü¹Lº® ž"åŒ3¿\æÍüA˜o3ó«áÇÑ`œo‚.,¯>–„<†LzZ|^êÅfzÛ{ÁûN»Õ¤¨_ü}Ì/á\Ã<Ù[û,‘r¦‹]ÙsÙØ R®'óŸø­û†kOTDÁOE‡qÒãxÛ™t_zìÆ‡Lês;z}¼ÎLµ»Þ¨¯/%skúï:.æä„Hùžì¾œ»Ý;k|=Gb>4Ý*M“ïu÷¤R?ßø”T¸£à×q~mQiMÁÙs‰ÿ´¥óߦ+KÜîå 1SP«åËLï–Wk²#3\ÌÎñ½b¾S«7.nóÄAò ê~l\)ÙÃz¹î›ãôOõxRÑw ~Æ·i}lÛû†afŠ-^²`Ç8ª3£k+ßGáï]%:DÊg>„ì~±¼X–×cïâð‹UYú§ŠA›÷.§±&jª-y›^˜¦'ìr7ÓÎÙ«¶î1ê¯Ê8p,œ3,¦èzŠù89ý/æ{ù¥\s9Æ)ܼã6—Á«)ÉŸ7ú½My¹M]W60“S±Í×WH¶ÿyx8·8"ñØ›âs¤ºf>áLW̘ù81_d›~0ÎŒ[[M[¾–òß_ìòGñÛ´xùÕ»¥J›©Eæ¸Ï7¤ŸÃÇ/ÎUÖo?åÙoÇòJ˜_î“–AÕÆn×>¼Ûë­Ÿî¼³ ±ÜC›~0¯Îg“6ÒŒ†ž+-çíän!×~O¥˜ûŠÒ'ò!ÞÝiâ… Ü¢Om6w˜<‡cþ‚ì{|M^Kþ0õ‡Œ]žÕDÐ ®5êHá㛨¦óì¸_%…O ß›J;\“Ú¶]=ˆB#–ø´ŒÞÀ ãíBÏᘣàßã(ùöy€ME¿2áï¯ÃõÝî»ÔÕ3Šl1/eoS¯R[WŽ™œJƒKì|¾“/}ÐúîùeÑn·ºâÓa-süA™ïÙýnAëG¥ç%æ×Èò*ìý§xó"#^n!!—ñ6Ƀ⋔n‘JW†mÜbi­¦~ÅÚÜÀ-Iò¨;GÊ—d¹.,ç˜ÕË;±é×齯wðoä¹wÃÜôò·©@Ñ ¯d¯ÎÒôUk÷VÓÒÝíöÕP†sm3ü¥âúÌßNð‹+(Õ• Ççâçþþó­Ê¹ë×=iØNƳÝ×Oøþ6%´<<×zଔ?»,FuÝç}8Ç‘î]Çò„ï9¿”+Ïò…ìsëåó™ÿÜNÊ;tV¾•oÓ‰†ç·—žr–žºñÆÖ¾Ô<µÔ›†Ñ¹|g{ã†pLwB=å“ü¬Y¿g9-¹rÑ0Îö‚Cƽþy7µ8»¥ºã¤9ÌñX£8KU«º½ ¸2ˆvô«õ¾7EpKw¼o"«Â1?AAù¤ü5æ7Êò3™_¬Mç‡N§åKÑ“®Ùy—nSaï–«B?ž!CAÞÑȇüÏ,ãöÇDp¿´ÞÜ…ù§:Iß{~±xÉÌî9©Á8C{wp³î¡Jœ}T+:Uߤ3TäàÁÕù2½É×EÝF¶'‚‹ÍØßÛñNÇr¢Ùx̧+—ÿ3®ÇÛzÚKвn ºMõ^çÝ×aùjçø«7mjØÛ¥¨o×ïÅü}æH~œÌ?™åG±þÁü§ìsTŒço;^a?ád\•ç6i~®ÙÍÏû õ;‘oð‰åio÷ýt# oÙ¸Fí9’ó_cÏ_–¿Éæöù Œã~ýìñgòÔÒv#3i}§Ý:Ÿ!ÇÈÈÚ¢›ÂÏÕ7oä.<ûvÕÐéó°þ-|ßçݘ,ó±²Ï‘uÐZ•¯ù~JtWvÀÃL*ÿªä¬ô;)Dýîá ¦Qc¿¿:º}WÝÐ,ùf }«±”+ÍòwYÛ÷]9Æ©»4ßî5mÑêsWLÌ̤E+÷„ŽN¡Âi£?¸5€Þ§vIõ<‚“Õ´6z¡ –rnŽG5öîÒ·©è›Ÿ%åÏÿ{ b¾t6½`œî/Þ¸Toh Fܹa‰W3i«±LüÂa)Ô$ÂCª©ôÛµÓ¯WÓqõ/8š¢–ò ˜?Ë©cyÖBR;1DôMÃ8­:wùùMÉÃttÎÕå\&å•ñIr)ô²hÅ"^¨IðqŽàäu—„]¾̱yóe9-‚ïøc·Z|¼Èë¶dŸ‹©Á8Nú®lþûa]4µkBb&9¬ú¾yìdºxüôÃt³š¶ Þ±µ_×¥ÿƒÇ­+†Hó\VO,¯…Õµ¾ßëB/*Ä:ò3tçŠ×‚iýÒc)ÿ„*=Ô±™”ry|C2)Úµê^UM­fÇ4óÁ…ä™[²¦wˆ”£ÂæGÌNx.¾sc¾y,¯Á¦Œ#䀡ãºyùïʤ쓩A%'ÓŽµR·g ¢[{‹¬ÏÓPÇÕô1U7,Ïñeó#¦Sæ£(Ì3åz~Y0·W“-J£ ;#·ÞœIo¯,Í?¹f2UÞ¤¿Ðó–×òüwºÆè¸ˆ~iU×M ‘|ÇY^<óku´õ6Í5/rX`Uö5¶Khqœ®,+»|ÈêLªÓQ_@öô4©¦¾iÜÞß‹Ftí×ÙpJÇíŸÈÏsæá쟬/ z|/}Ž\9Ïç§ ¿½îÓƒ£Uæjf.@=ïš¹æþ‰Ótày¿•?ô#“O…>7è¸b;{œU] –¾–›!ôab9Ó¹rmpýšS3:hh¤A®Y×fdRTõñãÇþzšâ½|¾¥Z_ ÚÔeñÆ:ndîÚÁ'Á’¿)ócó.Áîƒä3gŸ»§Æ8»žåë±ÇHýWnݱet&MÔ¸°}ìiZœÝ#ezã>”wÄñ9 w`Ë´¤:­C8–W!øš¾vüÆ?¸1_ö\Ï\ÿ Æ<=»É ŠÚ2¨[ÓA™´­rɤü?œ¦µN|¢³' Ë;{Iö:N«õb”&DòOeó:æÓÊtÂ|(í×_:Œóج—MÞyBôEÌ$jÙløÖª§©Qá!AÕ“=©èšßó¡ãœ«ëâJEå<™.Y~ «_ûÜm#®_!`ÍŠ»5âhöÉNsʶͤW.>Ëâ^ž¢¾>#ª,Зîh¾êÊ"—4§Á”w;B$?>¶þbþ¹B~Ný\¹”\?Ó”oyçuqô®`­– ëgR³ìw cN¢LÇ™Öyý¨ÚûEºÆ+u\ùÙn“w䬷˜ßóm=íQܨùoÝvyªùó‰VâúRÈ'uXhUz%=í]ê$ñ¶ÂU3é»V«Kn8Eyç|r¬ÒÚ‹ø”‰:?ë¸vK”j¹5Gç,çGvå Y—}}ž[‰¾–‚¿¬×·ŒôQ8­8IÍÕ¡/~*—Iæ.&äñ?E7¬CÝäæ$ä¢è¸1{®Ž\½6DÊ—ÊøÒù= >“ Ñ[ô±Å8ª-iwüŠÅ“æqòÚ?ŠdÒ MýeÓSô>èÞ±˽ém­îs6OÓqoñÔqX’ãÇÌê—ùo3¿üË®6ŠØáN,×Û¦Œs¿oø…¼3â©ëÔK—C?Ý¢nÝkÏnìpŠŠ~4Ÿ\Ò›2Ýòý~FÇU´ +DÊ}bù'‚ÎÍ’Ÿ±û OÓêé-ˆùËÛt‚qd9%ö¼OKö÷¹±4ë¥^ªÿë’³&º1ºwÅ{åP@Á .¬Á&®S/]Ï;9ëzæ/ÉrÆØ¼žéýÿl:Á8c?úN¾ãž@­w^Õ½ºz‹ÞÞX]&2ÜDÇ­[@áEúJÎù¦MÙÄñ»ÏKÏ-–W&<æä1‰¾½lžnÓ Æ¹{ßI ¤vq7¹Lg¢Êûºä;߯xykÕ&îb›k%Š/ æ˜_2›Ç°8æû*ø€»Jóg›n0Žõeí§‡Ë$RaÙ •vÝ¢1±o65è`¢V)mÿq¦¥vsÝ0:lç|ºüþPM°Ôï™'˳`¾ïB®Zci=`ÓÍ"«²Æ…©³{…$’mš°þµ.ZhŒÌD ËÕÜäæIO>Ýž[8r§åcy,ÁR]3_d¶Àþw<¼ÓÜ á9/Çõ§'',ÎJ¤ÅõKoÏ¿…ï5eûͧI4+ŸÂ/}noÚ9îå†Ç7qf÷h?»EŽ.Yþ— û"ÄrÖ…qäòçVbœÂ ö/¡^IäíQïø¦€[tÂqò£wÉIÔáâ®aÕ<©O^¿Ò ïlâJwk¾oMÞœzf¹1Ì¿4÷ü(÷úHqºT⿱$Û·ôåBnQPÀÚ†U£’èúu3³s_ªñxÏ‹ME7sG«òOÜ`©³¾É|ŒYÙt‚ëZ»ò^&Ú˜UzÃ[å-ª±bÉŽMÓ’hÜÄãM›ëOõü½ý†7ÛÌEGVJ©eÔpl=ÅtÀæCe2^·½²Rð‘×ẖ£Š§–šh,¿ær‹‡7®}²[õ­_}ÝÝþ^Ôáì»OŠ!›9hMS\«ï­Ïö 3¤\'›p]]ßyNÑÙp+nÑî‘Ç[•®’D^3–Îþ½„-çÉ7KùWLO,ÿ›åÙÚê׫×íæhÇ¥§È›Qü`![¼èãDjo»@?ªXò`Æû6‹y]9¹]ì¹,¬;^Iy ìyj?/pXlUº½^ú}™Ó´å‰^Ï~·Pç¤ø ýŒ‰4³ßíƒI•úŸà¶s3·#æD³bUƒ%Ÿ[– èù©”of«s\·Çþí¡ÏVâù_¼É)Óe ]>¬yÞ5‰”u¦fÑM=iÆ(>ym3GU‡ÇÐåèˆ]_˜Ÿ]wr¥ŸÛî‹×[¹Ò£îŒɨt’wL²Ð¢>²Ž?'Ò¢¡ã#^«¨˜2rÒßÍœP?!Ó«k¡žÏº 9¤¼´\yLÇ¿j½ì±ÚdÅÇâì±Pê©ö\tïD*4py»ûezPOŸ¥ýåÓ7sÓ|ú,Òú…Hþöìs|žŸ#ì¿Ö柶:Ç8Í2ÎOëó>™ºÞ?¹/ÿZ òKn›¯Y"mÎy-©+ÍtI[‘tn³äÎÖ1¬¯1¿q¶/ÊÖ¡öy&:ŒS3ïÒb61¢Rmˆ…܆?LmV>‘ª$´úÝjø‘†Tòi}¯m$÷êÉÛ”!ËÿöÁª‹ÏƒkÒçaëKû<#ƹ:ÿèÝß“R¨O@`ûN£,4³Å=m?&P´óÚø’s¤ÈÐaýžDrª¸nW¹½!RŽ•[V“„ý˜çR®Ë¿´ÿ~,§~©˜íkœ¡Õ‡ßü0©§…Þïz¨îj4I×®{7BQ`ˆH.õå,…»6„c9Ø¿·o_Çûpm)g„ù€³œSæ¯nÓÇ«’&8|8t†FoèÞÅ¡¥…º~ˆí›˜@» 9sèEÒ{ìüøÁfÎß°"õ ¶ßÀòÊYÞ Ë9`ù76½`œû­ªÝŒ>†ôò3^Í*[h·³¢áí- Ô D\Ë/ß2$[¶™[ÙÛüGÍ%ÁóygóBa½”ꦙÿáM©ïIçöó[%Ɖ/ÜmëäŠg)"¯ºíœ‚ʾsÔÛã—*8öÌžòÃ{ÑÁ²¡¯È¸™·¨IÅa+‚9¶îbû(,?íkõÖ(÷>/Æ™Z¯ë¤5Î&3ÊݺIÎÛVï*à—@QqBçô&ÙªŸš–Er÷îêß-½̱œ1¶À¾6ž/-ˆåvÙôƒqêüšØè芳´|FlìÐ=7) ÔÙAyÚ$PÖ8c‡ös=)öÈÙ_Þ7änLT·êP0„cû‰|Wœ.;Ñ•|_nX}{VqÐy§=t»ðûöíy(D?øZ‚~0Î’_Ç•žcã‚ÿx3š²üÙãêÏÄÚ7n,ïľ1ΰ*oæß?KÂ>óMâÝÑ+Yã©5Túýå¾´ìg×õ'7s/N:ÞJK æX.ëï,Wœ}_l]mÓ ®Ÿâ²f wñTê²ÓuÖnç›ÔêPá×§ÆÓ–lO7ßý¨kAMÝq×6sº“3§åy,åŽJ9=b^9ËËaë]›^–Z•ML?±µUª8¿»Aå*>oOí ¤E ÎÓ„|ÕH.´ô‘ƒ‡†p¬~™^X³ùë öó9Æé'n* ýæùß~îÚðxê¸LÖî¹/}UÂ)%-’‹19gÞë"åó°õ[²~Æî£ýçQbS¡vëûF¤RÔøôf.ënPïîQËú´Ž§Œ¨)õšô¥kXݤ¿äŒ›Žœu:gÞÉî;›¯³~ÆþÂùI A/§û‘°±—RÉïâ¢Müo#¥ïO’§®öîCßÊÛ?äšÞúõí4Ï`éü“Í{r穼rcû\ìÜÁ¦ŒSÞÐöââbfÊ(~õ»á-nБO—uÊ'iH‰”ŠJ÷¡ó‹ ¿õ0’›Þb»[ÿÁR®)ÛG*vaŽ"aíÝ\ù<:\w÷hcÛúf52ìJïB7¨«ß§z·¸“Ôø‘k­ÇúÐÀZ'ŸÎ,Åy¼žXäÙ¾`ŽùÔÿÊon­G,Wˆå%°¼¤\9ͧՌ‘ûûz˜éþ²ŸÆ~õ:µ›wÈï Ö¡ã^ípz÷[?jó±hþ4½_úÂñ.QÜ&W—sþ?çÜ'–cÆö•X=Ùô€ëî‰îâÙn­™ü­¿Î×i‰ªZ`“Çqÿb⛄ƒ¨MÅg©Å2#¹7?][Ó5˜cy ¬ï óZ«ÛóŸ|}%ÌC•¸®ÐWÍdØ7BSô:=3l6ö@Ë×n^i è¸øÒÑm‘Ò<œ=w’ÏÞ¼ÊUÊG¿ôáø•}…ëªq][¼²ÉL÷+˜ö%_£6Õ£›½ÒÄÑàÃÚ-<€Ö®ßéõŽHnIíEÉÚ©Ÿ çMhªgÍša Ÿ¤œ0¶nÌ•{‰q>[ñéÔM3Žçƒ@¯‰9¤q4ÁT0=aK_ê;ÿZ±––H1·3ˆc¹Æ¬.Šœ/Ôòøú|Är)Ùù©­îqý°È¤'cßši©>F{Îã­_ê0î»Fq4¨FäáükzS‹ùoÁ÷yO“ïÜ5ˇg÷å¬ ßó}7û|#ŸòM>Y1œe7rÊž\õP‹£• ¯Ü¼Üƒb>\ì±jnWuØ×§]‚9Aï ‰3±ó–sÑh\dž£šäÊ»µ`Sàª;¤‘qõ£ Çø«´{ù©wŸ ¼Ö¶¾\þGª<õÇB?Eqê‰%6î"}Ï,'TèÛ7¥~*<7ÄrälõjU6±Ϥ‘‡{ÔØ÷ ®Ò¶f/œ ­‡Ö™ƒFu¡6 'žO›ÍU³{„pL··ŠÿÔÇ2¬±};vn'|ÎŽ4ºî¢¸†  ÏŒ£ÌVm®¹ ÷mZò…eݯRÍùk“¶;A¶yO [߉j?óµÑRÓ;‡gû¶í¸˜.Ò9ˆM¸þÐâåoÝžFÇÆnéжôUjñªE—‘ÛNÑeƒ¢ìÚN4×_¡FsÞ#gúúlË™²ÏÁòY]½UW¨{¹Qî÷M0ÎÌ™¦ÜHL£+»Ì…’3®ÐèSÖ_[u‚¶<›2÷\HÚUî¦*;г廫rò¢X8Ë gû†,ÇÝþû×`œÊ§Uå<¤‘íT_w…â;ÊhÉŒ´ˆ”;½özP{M±Êmb¢¸äWÎã¿ æØóš­Gغ˜õößÛt‚ëO«þÜTµX:½XÐÖ#vÄRO¾ã>R}‚Ö>êôxiãnôcͶ›.EDq|zà›ó¬Ž«Hç(‚>.Iy—ö¹MF\¿„­Òén‡»¾Éõ®P Ìõ>ð}ÌØüÃÌžtbB…–ÍÍQ܆…ü‚1ç>±yŒ°ŸrEê'l̦þú¨úÒ]ÓÉ'øô2hÏo¤œ 5ã|œ®ÖîE!§–T]<šëZ}Ä«i‹Ù¹i5)ÇTø<ÝX.´MË­Jþi;zP: (StfçC—iÚÅ ~2Ò´2M~¸2óä5tó»úÑÜœ·kZ únÇöÙó˜íÓöº½jü˜ú¯…ç®ëµãVµ×“Ò©U=þÎ]¦Úg.t6^2’íxv†'U|}ˆÊGs²NüŽýiÉöؾ ›O°œ+ûsk%ƹ¹xÝîËÒ‰Ÿ4™;]¦«Ç‚Çßm¤Ä¹íV´îIµ#ª\mb‰’òRYݱyÓ3ÛÏdëA¶ž¶éã”ZòX>eK:ÉV~\¤äeŠY~¹ðB£xîçIÃt,þ¾_•VÓ1&„cû¬ŽØ¹{ïÂV÷¸îóûWC¢cÓIÈ'Ë =Æô¼2ÜH»#?VMéM—º¿|/1Š+¸éè²ÈV9ûñì>×;)õ=[½ãº|µÍ<›NëOîí»&ƒÊß ìÙ¨ƒ‘ª,Ì;«ZÏÞ4,ePWS¡hnvóBŽ÷¬ÁÒ{Hì=16g}ÛVç¸îéqü†j: 9ëáïß½]%#]Ó½ئWoê]ü]»==¢¹Õ†¥‚¥}1¶d9N·›”ÙÓ æ…°ß„ëŽŽÝhÍJ'îÔÉ ãﶨ÷ˆ£kí—¯l¸¹7¹62ºµž-í³K¶Ôxòû£=–=“ÖŸl߀Ý/[½¯°*?Õp›ò!ŠßŒ‹}úøµò}t,{3G{ÏW-èÛΓ íÝè= ãÌZ=óQCô3¶î`ùOÂüó¥ôþËw¶ß‡cá9wŽ[3sç%zQÎC-ëË‘¥Pã[·æ÷ßÓ‹æ–R¯Ëú-!RÞ{@èŸé¹î¿×í™\ÒçnásdJ«4àÍðKÔ4aê±eÓ¶/–ÿ8²Uu²v»Ò'Z:`ç¢l¿FØïN•ÎëÙûì|ÕVï§ÿký}Žç¨á„;Á¦j—¨p¾ºSßn8N• >ïoò@†Ö™»GsÛÖ/âþ(œ“£ÉîÛçbóu–CmŸ «Á8/ë…—L-}Ž~޹9oçå‹´ðxʉY Ó¨eg:%ÅûÐÛs?LnÍ9´ÝÜaòÄœý?¶?Äî{¾°ýAç……þq&>¡š_î´ë^¤1?‡?Ô:Fêªá±Ï ¦»ƒÕý\Ò£9!§-˜cï§±ýÖŸÙ~4ëßì}!›>0N ÿE–:G•áϬ?\¤k¿Ï™¦•ÎÙXnœý:×a¥U¹ôÌÒ>7ߤӆ÷ß…×?p\—”{XdÌQºÙÂӹʂþôû ~g} ÷ÊìÖ/ëªFÚ·Öõ¤œp6Ÿaç’ö9ÞrŒS \_.ã¤ù¨<<öM[3ãZé²Géùwû›' îC~ °6¨²…{sqþËvj¤}H¶ÅÎqŠDUõWÓi?Øþý%Æ);ýƒÇÌÇé””•‘êW罫?q`Ò;ò·‚Ûßõ¢F7ßêoÄDsåm/ˆh8ÖÙzZøþ»±÷ÃØs)×þ-ÆI[kAÍ»é¤uýýë{ç)Ö2òÐÓ#TiÁ/3*öRQù¦÷:âÍMË—2³Kç¸ì='¶o®˜[·ù ]Í\Ï= ®ŸüÜòxÇõtšU=vä Èó´nÖ4ŸÝŽÐ®ý+Ƽ¼s£H¯q?EKû‚þK‰ç±nìû±ÿt¸îEÓ®ý‹Ï§ÓÔày‹ªÏÓÖômO:B…ý~ÏžÞƒn'Ä9µšÍ­ï?¹_ÑGÁÒù ëÂ÷}HÚßfﯲ}›N0κ³= v9•Ná¥V8Ÿ§€ü=šL±´¨vJB¹=éCã݇ΈæÎ¾ç7R‚¥ï­¯„çÓ7–wËòí×_ŒS¦3ÿ&UºxÞ„¾ù.¤¶›6–â·þýàƒŠ†âŸTÑÜÁ25— –ê—½oËú¥ð\yïöi:?Qp#–‡nÓÉ*Ìwni×Nï-3÷Š>GqË_½l¢Œ¥‚wòTL\éI÷ª×y0Ä?šs-ѰòÄ9ûl¾ÍæBîí{1º½˜o(¼_*Ç8GÎ…×Þ¸/~âÙ×ÿ•ïÖ¿h—‡‡iËwkWèO^}âÔÍ ¸ZvÙ¢QÁR1{/—å ûYÒ9¤ýûÅJŒ3ð×ï7Eü†û¶¬™agƒsô°¸ãñ}óS±ˆ¡¯üÕÞ´èÍáMuÚDsãëkQ±^Ný²:cû´lŸ€íÓÚÏkÕ§æÞ’{뇥S£1gú ~–Nu¶TºÞ[v˜*ij2´ó¡ÕwísƺoàŒˆmòåœß±ç{”éQèÛrÕµãïa¦ÓdïCíÿ8”NõÖɮɣ ¤*ø¡ÍòïÑœͱBŠâÒÚò/îj8VG¬>9rþ®·Ï¤ç™°ÿíš+'V‡q<•W>9ŒN§;?¶+ÚaZ:µk_(oáF uF÷°Ø ª1xãÙrÑQ\íÈÓ® ~ž?ž“sÊòí÷…Œ¸þ4ïÙ–º=ÓIõiÀóÛÒÉä°®wŒá (²BÛú˜mtÌl»ÁÅ•üch®x0ÕÔõÙÞZµ¤óN¶Èö›YæÒ Æáfîõ3´Æ<éF,iÒ(ߦíC»v>$½'-w Ý4óÇú%Ó=ï8±þâ,í'³>¹*«Nt±¯ÜØþ”ýûG«­Ê¢¶~ÒéHZÒL¥. )ö†e¤]¯Œ¾×| ]¯u­Þó~Ñ\ü`þÅÐ`ižÁΗØ{ì<š­;Ù{£6Ý`œá]>UN§ë3ù†™FÖš¼°R›ðÉ—ªWñ&þíÆ^â¹6žÖ£‰ stÃöIY¿¹ð¾Ãåèþ9çiöó%ÆáÜý“—M§ûÓÚ=ü>,ôŸ×´ðA:Ä7oZ\íÐiýsæ3lÿY8o,.ês¯ËC¶ß?WãúÕ–NžP±x:͈:1vì-3öy«;@2'§N§¼éeÉj~ƒëΟ°,X:Wgû°ìwl½Åž76àúkÏ› ªóa=å”ßdÞc¦RÏù79@Þížxn„…–{ß">0š³ÎkƒGXN¿dOö»¶®µßgÖáú|zý¹§i”PåJR‘Ùfêºn¯Ë‘ýÒï=BËÔZ<"š³côb}¥¦”ÏÎÞobzgûnöç?FŒ³¯Ø„‹®¢ž 9Ë};˜iöau¬·b?}yè’bí;›}Ü€yržkõ×/Æžc5¤÷iØz­ …~ܘØïl:Á8…Ç—ì <žFuóÚ?©€™~9§X賺ŽÑ¶qùÙf)uMòŒæõ(¬õ0h8öžº wgé½iöÞÓk®s5V媹®$F¤Ñ°Ð#Žž«Riæ£skn–ØGEëOê1”žÕâ:×î͹.ÖÚy’†cóF6fçÁ,'œ­ãíÏiåÇvl67nœ.>ü»TZ±óÜËÞK×ç$æ-×i(%×ï°§\³h®[T³õIC4RŽ2{¿õGö~[O³ç‚M'熗J¶ê§4*šuýÞ’•géý›[[\ßC./úvÝзóùùZhN–âë?½ýþ,mÓ¦fãÞ{èô2ÏÔë…‡ÐrÚ«<¢¹F=ï/ìÝA#í°¾.œ<Ìõ¾…×ýx>ðA­Æi´^޵ÙogH9rêSÏèépÕ®Ó'–QŸÞ]on4'ôÏ Ž=çY..«c¶,|ιÞoÒaá÷i´_¾êõ„fg(;ÿ-C<õ´û}ÇN3 ¦¾ü\Ñ\枈„Þ5‚¤}1¶ÿ ŒsX¬ãl7ö{ûïÈqîåg|iä['Ù))6…ÌÏ~°ëènZøTuÚûÍ@Z–Yiò$ŸhÎoÞ@·+fKçrL÷lÞÇrÔÙ9„}¶`œqWdmзæÎuñ5wH!ãq7Vfî¢ÚñåUƒ½éGCh—€êÑÜ‘CÕ·/$3±ÏÃ~ïÁú›×Øtò«U9»ßÚ:;93 \›ÕõÃÑdÊØô²kÕr»èM ¯•yG  ;MÆÒ½‰â¾³µ=ôÞ{n±þ+¼7ùÄåKçZßcœ¨„ '7èÌ8»WÈÇÉ”jºøà•ûNZÀÃÞ@mN||x-Š{1J“®ŠÔHó¦·¤5 ›·=qc߇Øôë/0;¶Ød¦×•\uõ4>?®Àà©;hÝ¡Î/£¼©hÍ•žmNFqêû[Î(ðœgï±÷µ„ß½r¥Ùï±lºÀõÝV~xÞLm¦ø;º1á4­˜v§dÁÛépÓÄj#’nÑ/‹»üÅ5àåÞ ˜cç)¬Ÿ°¾ÅÎWغÒ>ÿ]ƒqZÚÍ”·XrÄø¢§©µöþÁ¥G¶Ñ­B‹×õ¹ìC#»l{ò~s÷üô˃k‹°y~MißžýîíÜ.Sª"a½Ü"×ïîtüýZÓ+1ôßÓüOÑ– ºž)¿Ñ¢m±›ŸœL[Ò{*ŽˆâŠ÷IäR ì<§’ô¾µp{ÑíqÏçA)ŽÒù»Ÿ6`œEå‹v¼ó"•š0çußStöAñÞ]ÏÆPãIž:µš„ßÑDqIÛýú›GÎz˜í[°ç «¶ŽÈ5ÿÂ8¦As뜎N¥¡ù7/Ldí³äpbÜVò˜îwÿÐ5%{¸Ìr¾ÅR5+©^$î Ö’~W'Ì¿n¸­-²f™Çòâs¿™tŽgÓK˜U9Ý2TŸÙ'•òM¿uvÞrÍþt/aÁî-W9_ñå ‡Pšµ„ßÈ"Ñܽþ5ꥳ9ö»:¦?¶nμøØ¿AAbïI°ç¶M/Ça{õ.­ó¥ÒŽOÃ52QË‘v¿ßMOšø/¹7vk³Ø#¨æyæßïK˜Å±õ)›g±çãúê¿•Ï#í—°~gÓ Æ)7i†ÇÐýg)¦ù™6=ҒȯQY}åÅQÔ÷a‰‰+ý†Ð”Ä× Ñu£¹hþØbÌ,é÷}ìy,ì£Æ» ×ýàÖtÔñݦÔε¨Æ8òGe·rVܯH¢yÁÂZL¤Ûž‹GšÕTÇvc£¸µTñÕ~™Å±u;O`ëpvîÌÖ·6Ýð÷+ïÅmƒÊœ¥m?ì.èR<‰²3òÞo<~3É‹¦9ÔPÓ÷žúÄË“¢8Ï¥Æ&Ÿ%퇰~ÆÞ+bïçÚÏ»u¸~ßyý.§œ!m½‘OL¤6õöõî=aù^Z¸\Sß—¼äÓ{×jűÙÏþ/¿‡eýþÈò*õÂ÷¿tc¿×±؈qö¥G•4ã m;ÿ(¸Ÿ:‘Z>­ZéÃ4É7ÏuìãÁÄi&Áz誱rŒçlŽÝö»N6ïž3ÏÜØ9–ý>•ã´¸oYk®q†>¼,Rôq¡DJm“¿ÓÔOi­ò`Z4îWû‹Ž•÷â*çÛ{éÍ–~§ÚýÖ†1ƒ]ŸKë ûõ–ÃZ«²úõÔ:¦ÔŠâY÷%P\gÅÔ6PɨöF'—!4‹šŽõˆâúÜJÍQ|¶ô=°ïS¨§ýâ~Èé÷wöï±Ê1Ž%Öï‡À)ÔÌv€ž@uoŒtp-\P¾¯ùãšq4´DIܧõ»˜”gI¿_`û[l>Áöó¼*ñ;'®¹×õgªíÅûº÷{R­7¥(ðÅ÷ß}FۻΌšjñ£‡KZüæÅu»]~°Ç³™Ò9ëìý¡÷¿=Mïîç*í+Ûtëú!ÿmKz2ý’\wLAC<>{°§A¾5¤ÚYaRø¦¡ÔwÔ+e=×(.å๋ZõLiÁž¿ìsëˆ7’þØþW¤ƒðç_¦=šþo{41¿0þóš„üì?ËKäó³Í@!z¤|ÉKÎjç''óyŸK/»\ˆP1;ÛKô@·÷¸T‰9a|&D`IÁgE)æ$:‰þL_ËSˆþçV;ï•ì/ä„e}Å÷×Þ“.ô+™öya|n,ïqÉgãðY‰¼:Ÿ£=Ðļ0ûÜXGÑãÒ$f% p}ê²ÅìX{ŸK–óW>èb~v˜(ÖÏ}ЙÏå—|ÐÍb^¢}~ö×ò]Dt–cøÆülÃg~-ö~(¼Ê¿½ñßÞ¨qøçôFGñód8Y²–ÆgÉf%ŠU'ìçRYv>R.bvïa§¶óE‹Z-úÿÚû×y‰y9¼'ºX€»˜&³´¿–•£½ùÌ0%#Šâó¼ÞýK~öÞTa_ñDgy9F1K‘÷¯ãó!øÜ0Þÿ—χpýebfŽ}–¢“è_gsÃ4À T¢o/@¯Ï<ìXFÄ_y[Ä,Y˜ñ¹0ó°û’p†˜fŸ%ûµì0WјeD¿1KÖähZ%úz2/+ÞÛ“¯QþÏ·öÆÿT_ä{â¿ýð?Óÿ»ôBÖùè$þ»cž¿Î sD1Øyxþ™º—˜æb—›¨ý;ù¬0ÞcÏäbV¢Uô<׋žbF˜û_äfˆÅr;ÏN×ϲ³YVâ—2qì=ÏM_ÈJ4ÙegóY‰îU_>> ‚Ï ã½ŠebV˜Å9';Û b  ½úxañYaz ƒÀ4¢ï9Ÿ—¨ý̯åAü•w±äo×0·‡±½_ß—<ŒŬ0–!ûgYajÑǘeAd}c†l–og èyóï<ðÿ¹¾÷ß}(ÿ¾!CöÏ²Âø Y pG±ÆG¬?0 W²l®bV˜ƒ˜È<Øub~¬¿ècìŠâÙÅ…ÜD>GŽB×+P‰ar1Gûk¹81ŸæAèEÒϳq¾âQ2€Rô(ý’o;ËÆ1‰Ù‰U…>'Œ÷1æs T¢±\ÌÆ±ÏNä3µEc>'L ²øÞñéE/w>?Ñä¢Ö. ⯼Œ­b†lŒ˜ñ¹—± ÂÕ|ÅËØ"f…ÙgÈ~-+L!z³,Ó7fÈš€ A(È=M €?‘â=MùÚäÿüzãç}ñïôÄûá—ûá?µÊA pú†œ°à„ @ñ¾îj­ ¸Úe&ò™8fà*æ„eókc³‹˜•˜%z¹ËPÔ1LõÙÙ¢?s ŠÞ"fàØgg³ŒÄ/eàØ{¸›¿‘h¶ËÎæ3ULŒ(>Œ÷k–‹Ù`VçœìlĤVàQňÞÍ|6˜È!0­èãÎç$†+PAt»,‹¿òpvóc3¾àálî_ñov³ÁXvìŸeƒñ¹f»\‹ìoÌŽÍ^½Qôq׈>î|Žþßyáÿ—}ðŸV!CöÏ2Âø Y+P¡XõÀ 2€…#¯?ŸÃbF˜£˜›ÈûÎ+QÐ1b~,Ÿ–(nXà|f¢ ¸ ÐCAð³Á\Ä,í¯eùðYÚ1¢TÀœ¾çÃ{×€  „Pb€Ä,À¢‰ù‚w}Œ]žYÌLÔTò-ø|°0¹oÁ烅LíÐÏ2ùLm °ˆù`¡ ›ï‘ŸAôµçsÀ_N¨]Æ…È!J-È^§¸@ ¡bÆ…»˜Æg\h€¨xOx ‡pµÀ T°È!b­˜qáþY†ì×2ÂøŒ‹»Œ ó7fÈš+A˜Ø ¼€ðΔZ³Vþ~oüRÎÅ÷žøß©þ¯ôÂoí|ÿû–¾÷ù> ëwü÷¤w2bÿ*sLd(> °ðg"(BGapE!†‰ÅèÌ@a—ƒÈçød T'©?0W1‘ÏðñB39ŠW ²€×_äck@†˜ «V1ÃÇ>#›e ~)Ã'8A_È?̰ËÇæ…áôÀQŽ:&¹Õ¡YÎ9ùØrˆF ²€ âÑ'1ÃÌ\ ¤P`3Ã@aÅì °DfrM ²€—˜a(fw¸Cxz ƒø4À Tørõ@!j€¨ø Œ&9y°Ÿç˜¹–¡øÆ X5Äkj'äôd‰9=†çwÿÎïþYó;…8^–ƒûgf|lðB±€ ,À…«Ž(Þ>C(Å 3'r °w´^ÌåóË,@‰âŽŽ(ð`®(ô0 Ôbv™«˜‡ýµL">[/f—y#}–Kœ @`îŠÈ  °D£NN °wHo—O”!æ5j¥ª_¦¼°øü2“³‰öY~#Ÿ‰­V1¿,Lž?0™˜áh®bÈ^uþ:ë'[Ì€å3Ìä«d/ˆÖ\ ÜP¼ `#pˆCA–˜afŸûµ 3wˆ\œP‹(ÄüF5ŠÙ\PС|>-PÿEîµXĬZ-pÿ,÷šå6Æ'ˆ X€;Ä ²¯d6Zì2¯ùÌF50'9ê˜+D ²…Ìkˆ'd/ˆÈdbN› ¸BTa KÌlÔlþÌ"39„¦YÀ ‚3ˆ.d5Äg¬/dµY "49„¨YÀËUÈ:“C”Z¼Äœ6ûìÚÏsÚT-„¬6'ô¸@»<4ÝÿD~-/n`.y(Èæ×´»ñßyà¿ó@‡Ö<Ð]¼^¶ƒek®(Ì0±8ý(Ä,Ûl F±«V Bည7X€;ŠXd(d ° Ú æØj€¸£¸õÀ 2€…®‹Ý˜E¯Ž(ü€8–2» À bPCÀÜ!=A `*Åä‹d/ˆÆdŽX 2'ˆ(XÄœÊP`J9ô!,` L÷Yv%Ÿã ²€;D§Ž^0¹˜_i Q²‚4Wˆ2L¦?0ªEÊgÙ Ä ²¢5W7 d5l®qÈ^ŸeÙf/Û\ n °Dn2],ߘekJ4‚à„fàÌ@Áïñ‘PïüŸ¿Ó¿–gûw{âß釬ÿý§zë{ßÒçÔBOc=ìŸÐ¿þWz×_õ-þ`t²oCA6P£hLÀ…&?0Q(pG!é ŤJT pBQ pGqé ¦VàŽBÓ'[ Ÿ‘ ”âÙ˜€+ 0L,Bÿ?ÉÚEaf•Ë­mî(R=¡P5À T(X£h5ÀÜQ¼z CkìrµPÄ@B‹™ÏÑ6Wu߃PØ ñ…à‚ÂÙ@7Wy˜XèþÀ$æãjAßcPøFà‚âÙ@  „ ²=ÅäŸåãòÙ³rô-ÈâÏ „£àþYíŸåäÚgкBda¢ÐÔÀôï|ëßù–Ã?k¾å%þÿù‚ôf @aê€#Š3d%ŠT'ª?0¬d/®ÈP¼`*±ÈQÈZÅçy£ ÀE­Y@…â6 \,@‰BŽ(ö”(úà„ „b€“˜n2ˆÁ˜ D¡V ‚8 @hAð‚PŒÀb Ù@ ÑÂÑ‚,à "Ò+PBLa ¸ËÑ7€„2€‹ŽYȨ.d…‡l ‚èb€„ÌÀÔ€  „cD1ú3P@”:àa€  „@c€#Dª&à ±†‰‚õf €pu¢xý¹qNþ6/d50:d5„m®·dñ¿MƒÈ@¡k¸Cðz ƒè5À T¿ÈÑ´À ÜÑô@†f2€MAGB­‹¥ø—½‘õÅBO´Ÿoýï˜k}KoS;ü¿5ßú–ÞÅfÿQ0abÑø3P xtÀd®(¢, B!€ŤàŽ¢Ò K¬@…^eršd ÎdèU`î(Ààˆ" f @1ê€# 2d% 3†_'¢83ø³þ}`§ Å©V B‘€…ªYÀ k.(Z-°Š×ä(`-pGëùu ŠX‡"vD3P ˜ubA«‘×…­äßíEQWv˜XÜþÀ (rpD¡3pAÁ‡‚l¾Ï ðMÀ}%Lœøp…ÂÄ ‚?0ô-È^è+Fà‚¾ ²ù½}ˆGŽž¢V BOÑ'*X€;„¥2ˆK¬@‘éÿ»` Nxg'`¦œÿÌÿö5Ãæ@ üY%þ»f­UùdÒý÷Ç$K>áGO_Ùè/ùdöý-p¬f¥äóØòÁ˜Ce®%¿ïo¹¿›þÆ­D“S¦‘’_ãͦ¥7•“ã‹À£Ã8AÇâBÞåI¦GžÈª¼;I?¾úøÀ»P: ÛP|㮡4-tcµùÚH.ßãi÷÷Êña>,×–åB0_{{_Q#ÿyzzvœ¦eóŽ_~’ÌÕ–;Rm >‰iì|Ï¢ËÞ²n@¤ä×ÂüQ˜_rˆvÚ;™ì£ËÕä¯kÁu^mØìÒO§É8£Ë’ÂOR oª¥Z¼_ž5„¦øXžyEr ËOmàÄ1_\æ÷#ø˜$æÇÅüù™ï í YgU ¾«§©ØŽÛÙ›Òã¨è¢õÃ^zÍ¥½–ËÓ¦ôQÓÈÇηfErONì°ó§ÙË-f¾øÌB¸ïsç~áúqoÎïùñÁ)juÁ;lÞ”8Ú‘¹$¥z†;즩ƒÈkIÍKöàslkvõØLŽåí2ÿ{–—D3î$Îù(å–åÊMÅ8[SKv…¢Ò=Š~Q.Žl1@w¦‰ßó@rõwΙÉu±rÎàrû0U•ò_„ûS@ò²÷5Qcœ2!}_>ksŠîìX¹Ÿá]¹–Y/Ùy"éëÏOéºß›Þ·lÔcæHîÕ¯×WΟ7]Êeb~Û‚ïO¼äÿ%øM6̕ˤÁ8ò:ÕNÖ½a¢F6Ó4!ì¤ÇËlò˜d¹·‘ó¦5¦WHø9’[±O¿µòÇéó'dŸ‹å¿>‘oÜ寒´M''òþw±¥ƒMt¿<ôl¤Ñî׆>èØŸN*vÝy mN×®U%’ãÝìv¾žÁ®‘W×Å5&–+ÍrÆßG)ÁއĈqT1[—«šè¸ëÚº²ÕFÖÙß÷¼GcÎõn“×çùÐÒÓÝϧ_ÛÌ•j5«ï¾™¢ÿy ©O0=†^<›ºã¼né¿Û­ Ÿ«½m ÆÉLîTª÷¡$z9/ôùÇÿÁÞy€5•µkÅ‚¬‘±Ä†QQb5+öرÒ%;RT4X±#6¬$±aGF Ôz@Ñ €Á¬qcÃþ¿;{¯màŒsιþïœ3ÿù‡ëz.g¾oÜ‹$ïóîUvž)‘ËýRŒ?Íy_2jö,Ô¯½ýÐWyr¼bòãK'­d9Ý$7’ð†hNƆg‹Æà›ƒ:á³R“ã²Ñùèxïó¯÷¿À¨­È“·t®úòœh‚e‡=*;v[ÎÖ5é+tž¾9›Lr숯H>•Á?0Ž!uP‡âe¿†ûc„vÛéVéÚ»»öñdxlGqg‡¯kEV²9©$_ŒÎëÊN$écÕø0NŽ VË.ãóPÚ |ŠQÃ}1³»IpÆiGlÎz"SŠøpߥ0©mV²ùitN“òŸ'kü¡."¹¢Æ¹Cb¸¾m¿_2•¹è`ûSo¶d eZ}¹ÏüäÃí©óDå⊮Eb—Ò¨íþü ÁœÍá$¹=tÞç[ÉW3ΗÀ8ý úZŒÌEâ!k7|kΔƛ‡¤n¹ÏV·TÇx"Šr¤êzëûevÆ¡˜äÒ9€õ™œ®óç'fáV•’§Gÿ>&´o`'ûŽGº©rÐÂ8¿¨';Ò‘KÆÒµív:áefBWOÔ6_ôÞlàQ,g_©3§Ö¯‡ß_óZ@òm¹YB¸þ™Îw|¦·ÍB6ºšŠ²›z_¿?~?ý®ïò—YèëÛ˜èô³éÛf5qXÎÜ÷›0}¤[g„§A|KçšÕ£}ã:º}û™™¨ §k˜Ë¨Tôéå̽:ëƒØYÛ+åÝY¬ÿçzNüe`(&uD¿ïqLï'ñ;íÛº´_àú[e‡|;e¢S÷,5=3SP½·Y‡u9Œ›)b´¦ciÜýGñ‡ÇW‚Ö BX.0ù\H}Ñ9ÉonQ}Ú'pý+»E6OU¢'Ô_wJA‚Œ€{ ö18ë˜}•ňöûQlÀ)n fù¤N ·€æ.½ÿßøóVÂ8N§¤òß•¨3…I¬—‚Ž æÄK±G¤àƒ‹'j6ÆñÙKû£¸V¿ÀÒæÁ,G—Ôá`ÐùyU,gÎx¾¤…q22ÊŽÖa†“—Œ^™FÞÊ)aÅôqƒ¥ž¨ãí~ZënG1Eko•Är<Èý…ðO…î;Ý«ñMLë„kOt›9º F4/8ÕÝÖ$ø‘Ù™°aê O´lçpçªOr¼ üFRä@kdÌááÂ8Y-¨þ ´öö­¬7S“‘gâùGªŽâ«÷2~{ £Šå-Ï©ãv/»@‡bû2©g’÷Lç¾fó-«ùÆîF‘ÚÒ™úJF , ѳOÇðƒG«6Ê…hþ½¥›·Ãë H±¶úµc0&÷r#ùÕ4×¾R@oüãÐÓ¦4d•ã65ᮬºT²åùq¬Ënš±eª;¢h&gžÉ1ÍÕ fóLÉ|œðSÇi¹ û½6ôG \·ÿñwIu´©èÍC›Þ¿ïR ñ‹uêxÝÁ­=³®HIáîJäØð±¸³œr$9“¤ß“5ãP)Œ“¢¹4ÕìQ ÚÃ[1ÞÎC>,¤@'1̓pAwÖšGÚÈä¸ÅØ·ý6?bù‚äó§ûc!Ûï ÏÂ8WZ ã„?ßܶ¢2Ñ9» ÔÞj™û´±øiŸ>Ÿ.p\ÐÔ”·6cgËqÓqÑó>;±œnÒ_ç†äã“\áj|IgРÝ­_+ÐìÓâéS»+PþÔ[Å/GŸÂ4·Ç™l»~|õXxß_wïùK{Ÿ$ãUÏGÿ, Ü—jë˜#:!Í)LBÒÜ©]=(P±È>yOËÓ8Ê…·ÿÐ-gôûÅÞ¦W§Êñ®vqg ¦1Ü’Fì|˜äÜn©cãuÆy=‡JND¯¶&öÜô" ÝT=f­;Iny3C@§_³nà0ír&> ù©„÷DÖ³äõçM aœcÓ)g& ²œÚÅç’ÐáAuÂ4™gð¾ËÆ$\Q/Ξ’¾rÜacæ—Vý‚Ùõ/Éa%¹Ï„gJæÓŸÀõé<ô„{­ù~Î' 5qgs7*ñÒó+]ÑuÕÓV“‡Êñå ÷Œò fó&IÞ6YǼ ¿±ôyW‹êV¸þÍ ›—~³º„&´ 寋’˜>v¯Î*?7»ŸÊ.Ùk¯z.Ç,}OcR7Ä/ôýK)°xõªsíœ,ظOJað˜Ÿ{ßßЯCy¼Q½’ÐꙓEyÇÎó>8ŒYè†Ê§ªÝNÊp¨ u¦zcÂÕ!ë½vœÍë|oÉôÅÕ¸J¸þ 6«o·èœò_8n‘„Ú¨ä1ã.`Y·ò&³ÝÑWåŒ#B oñµÏ¢}˜äI’>œŸç_«Cý–ˆ¬÷Hÿ4øÆQvš~ªÜô"úx§Á{§‰è …l‡²U›oîhlúIÿ[Óäx®þÖò¨eìçMrli.ö Æ÷-YÎ,食Äè„-)¬Æë ˆ¢EåžJDu[tu­½'s¼SËì<Ý‘rˆOáF©g—-|Üí{›ÿI¸.„kulü±òŠéæhoÔã®»3„ˆpW >q& Ö¨ÓÓs(íJÜ”²%‰h½>ûÐçzñµº(u[74]µgyëuÐ_Ê©–çÏî¿ú%¹õ4OÄ„Y7 gêcí§Y“-SnÞ:ƒ¾×]cÒÖ>M‰bQxG_ÞåiåŠú®9aSW9¦ÿ¾?›+OrËi~æmfþýÉ˶C$÷Ýà‡æîžB#,[Ù–×KDƒ:(h/â€ýhü—÷.ˆæ‹Ê°wt‹KìþËÉeו4ç»àéT·ïÝû²ùÀßÀ8£Æølöì$J‹oTïæ¤)ê››9.»H¼ÕãŠ^Ö—èeøÆBÊùËØsR·¤_Ü÷ú}ZD^/™|㬧â¾kŸ@ÑƸ0Ý»sªÐ,9Wœ7Ýz#Áí¸ûí\$Ç£/µŸ<;˜]'“z#þ¡çƒuÉÍ6®7%Œ³ïLǃÆCN;>Ï|ž“€ºñ¿÷ëñ~°h\‡·î(*$ÔÞq·÷«c’dh0Ë_bó†™y=/~/ Ü$ã¼t-ŒCsÊåH|qÖ;7P×½vµÝ¥¿á;•¥ú‡³çâd“\•×.~·ã«e,ß‹ðýÃ[½~ÚM/rÚa×µ ½ §Rªv ¡>yÒXïÛvõcÊJ¿™àÕõ>Ø’Y·SŒx‡EüÒ+r\yhNxÐÆ@–ïD¸gä~LÖÇÆ|/.\ZÑÙã’K1èvfDXm^"²ÃmZu?w w¼ÞòÄ¨ó7Ïñ;N@õ- ¼Àr*Èý‹ð¶Èûdð\7)QŸ6}á!D¥/ïtKD¾w†”€Õoµk¬öDcìM»Ø,c»ëm9¡Î,ÏŽü~¤ÏÓ<·Ú´ຓCL ãîG¡ƒ³æenIDtNt~ŸÚèUQ!\—ŠI(Ç6Ÿ^ß:*€Í÷%ë_²/e¨{¸^8úòåƒv Sÿæü.9I/>?kZ"¼X_;q-Ì«)<Å/rürÄ=ÿwÞ˜ÌÛÉ:„ôYÂÕ6|lp]®^i’µ1ŠÙMDÅ6®_’î$bÞ§~õ?$‹Ñ«(Û^r+9.Ii2'°óARÇô|ð`uÒcÕ’ =½.‡ëŽ=ÔjJ‹°í¨¶çI§fI¨Ñ¯øŒ[P°êJzš•¾ykS‘°sÍqõûAƧ¯4—GÏr¥Œóóµ0Í@•ŠŠà½“а íÑ…Ix¦­mþ¥;JãvXé%ǹCzÍËÈ~~ä:„“CßOõzßnHµ\x™N8´xYEý§kQU›aÞVS’ÐÚô>¾ycxHÂÈÜÃnì|0¥ïPÏMõÙuÙ·œü€³xvŸÇúÏw†÷‰ ×5`oú®B÷L=®.LBŸb÷OUà·] ¶Ûì憪î ZXGÎÌ{ü1© Ò8xwo“ÁðòàzËÏœ1°[(^´óÒ/aW’ÐÀÅ+®LÙ™‚wH‡¸»#‰âŒãÎb~;Ù^;_§ç¯Ý˜z+Œ>/Üÿ¼ÜŒÍk'÷wC]Ã8}ã}¶?v_Í:Ü¿7P—„œ—w95¸n*æh¯| øezxŒÿjé ¾•»Ð)€­kÑÑÚÝo+{£agE{ïÞ­ƒF6tÃu㞘d¶ºʼ•½–Ä÷p IÅ m´§wTÐÑ2¼>…Öéln7Y'-š<°ãÊ"úó7‘ë„§6ɉ߿ 7l™ @M§h«V¦âq÷]î ¼é\  2\ßjÐæ³1©SÒ/é:xC×)\¯íù¦ðîÃNg‚&Š(˜î4<.+мgw1Z6CÚzíFò¡C»—˜p.É|˜¬Éyˆ¡Náº]>.Ë÷:ˆ{æ}<è¡@ÝûP¤Ã4ÜZ6¥û(1zÙµó—-RžŸ¸‹S'Þ ¹oÓó†WÌ¼Žæ Šáº‘ëÂo2‹ÁO.Îñ‰LU …®Ïhµ,·IœÓËæ£’.ðÚÜ‘á ƒè3z3ûµÙØR’›”y "ç[†º…ëZVMiòy¨ ×Í|x;µ^2òïÞ¹‰¢*·+|çªÝç¶¾éÔ¾ƒÓë’`ÜâºåÚIfC‘Z2s7Û£9íÊÇ7þÊöaz}K×®Z8?åˆNŽa΢]³0u¾Þíú ¿ ,Y1bçÚ¨NÞ§zVƒä˜æ6cš?˜á1 Bd}Ö¸  çÜ^õ|{¸þ…àÕiŽçŽá\GŠŒ>Gm«ïú"»¿Mùý„Ý,=Å:é’%Œ²øâ‚exÞà/ ûØ"rnºnÓ»/ß3Üß̹®c¸¾‹·ºlí Ls²RP“ =gúbÜ{FÓëU=g!›ÖTÏ`]Cï³brŽDæµôûq_°Ä·›YüGSºÿÕ ¿×§*/4€HSP˜w†€c}=F¾cÊ’%…„\“á³mÛ>꓈ ‰ìƒþ=Ÿý, ü²ÿd¨s§¨Ó©õQ§°ƒåü–QWRlAÊGYo%ž¬hâ¼ËÝ-y­?bzùG“}dâsrnAxB¿ÖÃ%¹{õó'óôò‡­ÎàåÇ­G´KEc×÷¶^¬Ä#¯\'fûEù:' d¹kd_ŸÌc 'žð Œ×ãbg× ‹ÅýNŸÅ»-°º¿0µ ¿Ñº_¼œs#±ž«'ò´½ÉñP†_i8*%&€}ßHî?áÕN=ÕegþWvßÏø}“À8mÒt½DýÏ㫟v{Ç+RQóñ…wŸV)±ŸŒå‰î¤ßéK.ØÅºçƒ@¶n]_å çµ."c¾”zßv ×N<|[Âl¬E£4äfé3H#ÌÄGn]ã öB™§šu}ÕNŽã›Ü ·¼”=‡!û²„ƒNöýÈçcÌ÷VÂ82›ô¯É¾q¸[íRᓆ‚ù^1&›3ñýËš^—z¡ùJî³=åX¸¾‹è¸f)»ïKÖéôûS" |\cjáúwÌ© vVîu<ŸT”†îÈ7½}=“=Oº–2úr8ô‘áÏ+.´ðg×Íä¾E¸Ö®‘×-f×AÆçû&ÇtÂO×t\=ï"–N¡&&éh¤(æs›,l¿zqöÚL1ª¿ßç‘õ ÖÝËî–ØŸÝ_ ãþ–ºÐ«¾ý÷Ïô}®ÛhÕ/^iO.bÓ¸Z ÓQ§¹gîÌpÎÂI5›_Øy NÔtë€ ÷̰^À¾/d?Šç`{zQñþùô9Ž®{>•2R<^ïñûÓ‰£2P}±içœ=Yø}´é›Ñn†îÐ\$ÃÏýZ6)Ý7&|ª+ªùu¬M«t?¡yTb¸nvi-óàwñxi§ÚÑ…hæ‡.Òö—³ðÓm»²ÒòQñîëÚÈp–y'VdùJdý@Ï·> ‚väÏmbGÎYªó¨$0,ž¢ìöþ†×)üß_õÅhB­EÐ×,ÜþÜü±cìœÐG ìÕÆ¿¬–Mÿ…ì¾¹ïÒëÅ'‚¦/:}ε`¹7ä<ÔPÿ0N»‹­cj‹/áÍOjYÿñ£õo—ü‘Ø;gVPÀpGÔaÓ¾“£d¸­µóˆÉù=]/%úþÓäO_‡®_>«Ñò%ðôÇ+Fw Dž}8Í5çïµ èìˆî¬9ž"Ù'Ã[r—4ìÕ*åZ~©Kr®EöŒ×½ZDzc°ubnØbôoñaJ¤ ëýñÙ¦l\¹°ËÇã厨íàŠn¶ïd˜Þ' dÏm‰'Œö÷;¶Ÿó MŽë„ŽV_¦¢%‰xèëC½¹—”¨µs§ƒR²ñÀ‰Û_·™ç„xÕZcóÌAfŸìø£®HÝ’}uÚŸô¼ÖºïŽ ãì^¤mZÒ# ñ*å^©T¢Þ¼¹kO>ÏÆÒ½¥=ãТSkÓ:Lcë”w:´ dù£¤~éû“F@¯çk¡¨ËÔWïjûB'ùô‹ÔÎUIØeõx“o=2рdz,Ûçà]'<e9#jV“Ì—ãk-í/\Èî“ýƒ¤Ïo¿ ȹ=ùï ¾qWx~.P`› þr÷L4t’õøk“spE¤cß§]P†ò¬Ã±Ï2|£ÅÞaù×C>–ƒÅ<'@æƒô>VcÚ70N¡–ºl{26<†r0[­*½!?Ü÷Âv€+zÝdýµ²LþmU˜jüØÏ!œ6ºÎÔ̹Í»“ÂuWù –š‚§;}|;yýqæÞ†ô<ÉçIí}ù.¨îíˆî©ûe˜š|N„‰ÏȺ<@ßgk!²ž1ø®_¥MÿKÓTÜ,zã„æYh×^ɤú|\ež¸]ìŒ*ÇŠš‘áÇrÎWV±ë62? \Rׯ\n-\ÿ¸üâûàÄTÜøw×'ÎYhß›²†ºäâ.'S¿8¡ð]TÁɰv^¿‡‹ŽcrÎCú9o¦ŸŸ¨sÚ7ôüÛä„N¸‚:¾Zž†#c¶¹ :ž…"ú[Nš‘‹}®n¿½ÐýÙ–jrìþUÓ= ûÜ ØýÙWƒV+޼s òÜŽÁpý¨ó7LOǺ{/~š…^,Ö_°&[Íš>ìó˜H“´+Bo#ǯ,ßOBÁì>é[ä>Ds ? ÈçC¿ÎOôýÆy~ªÔѱnžãúô~ŸlDíî¶<›‹‹ÏÝ|+•NGÄ8ß]Ž{Ô ï„éúéÌž;Ðóœ\»éò]p5»Á„“˳í–çrÐe *ËÃ-ÆN~ñåWµÇ£ž÷H9n¹ïa«ˆ#˜ðD ÷•pfésï÷ìstdeðËI˜·ÙÄœåœWâ°ßж=k‹æyù­|‡Ï”Œ\pQŒÜ.<ƒ¥_Yqh^Öûët²> ¯‡žç¾<î^*h¿£Ë‹5øÆ9öµYÜÌJ%nqó]£ \TÒn~·Œ®ù¸¢@ñ¦i-O$êw&XÑ^Μs²û}äþGÖ;Òn«VlkñŽ}¾Ñ˜ÿ,„qJŠ—Œï•‰Ç€ö¹Èc—97ËÕñ/;6òdçÓEö??˜º “û¹Ÿ“s(ËFÓÍ'þþN@ÎÑŒ÷MÄ0ÎÖu‰‡Æ/ÍÄ­Fo¨è’‡Æµ—’y&Ïó˜³ý e­+>»°£«<ù«×_YÆîÏóAÒÏÈþyN´Ú:ÆÑßY¡x¤ÈÄÃÕŧ÷æ!§?æö±z‘{]γ“ºÎBG¢ßyñ ÔñßÔ¥ÙÈ ö¹²ÿEÎëfe9öœõY@Î'ª­C`œ%ã­é_•‰Ç,³|W’‡îfÕYÝÀJ…»gUˆ ]Ñ:ëFVßxrœXÖ¦Á"»`LîW¤o®9í›Ïl~§Á?0Ž}èÎU³°äH½ÆQ‚|Ô©ùµ.‘n*|kÙ’Q^k\зU}]ãa2ʰáÂÖ9‡&çQ¤èõj/v}gðŒ#ZD=i…Â5ò£óQÇ<›E©p—N·G4^ïŒ kcÛwáeÏ;ɺ<×Dx§¿Äê„#3·öpz˜…¬¤ “f>tmz:[…mo¦pcÂpù'fÝÅeî7Ìó~pý-FÞ­ÏÍÅêi›—…hºÇ„g“‹ q»´ÙC,£b—â'¶I2ì¸'̱Î÷ì¾ ñ#=N>ÓßßÒþ€ë_ñîµ:8Ÿ^m^é}íѯ›µºùe|©QÚØ¬@1jñ¶Íò¬GÏ}?³}%;_!ó ²Ofð\¯tجfV¹¹8ªNEC^ÉeôàÉô~&Ó/ãv=;ýÖT.F³ùu–ãwͶl¶ c×Ñô¼ËÜ´ìzÍPÿp]÷ ßMëæárÝ¡˜¶Wd®øãžËxsÖ õ™bd}Юk‚ƒ¶ý~c×µäý$óªŽÛ ÿ ¯+…ë6ò¸7î×<ü~ÿo‚ÇÑWrƈ³5—ñù‹÷,…ú ÏÕ¬¸=a}H×UìsudEŸßëwÞ|™wÔ¾wµû™®Ÿ?îÊoQ3óðª&ŸÂ¾_A=µGžNþå ݲcŒͱ¹=ɵ‹ßѧ>ÛÆöâ+zžöœ}Α<_aü<ÆisØ:áÞ’<ì4óÀî5‹‹Ð£]úYWpúV¯äã¥b”JlÈð/[׎Ïÿñ}R'¤ÒÏWÑïÉi°¯ç€^·7æáLÿu•¡ ów£Ç®àÚ¼ŠšŸ÷Dܞۢ˟Â:?3ó^NäJö÷'ëoÒß7=à]B?'Ë…ëºX ,~z4Ÿ’5QÖy•šnñÕ¾¸‚÷Œ|?lo]otRç,l&Ç yÒÃ뇱ûPdFÎméyL-z>ו4‰0w¼ ó¹(•@…Œõo8¤0½¸ðI¤Šå<]2¼“ŸOiZºÛ'Œ]÷’ù$á“ï{ê®k“½:%¼v>®LlT´ÌòºÚ­Ñ¤þ»ŠpO»îïÕ‚9Ƚßö/,`¼`$¿iŸ0L>2¦y¬Ïèz†ëù˜>óº1.§ŠdÖ+î¡‚±m¦¼*ƒº?>öÄs.š©xÜ¥Ç3¦NeæÝÿá;RÏô¿?g÷Fï½r1íL‡jës)Œ³St»ªÅž|<þâÑ¥±÷¯¡Â¼{Ot®âû¿NëÔaÓ\´lI#A‘ ¯X“ôÁ$ “}%z>³®z, ï‡µÙuPµõ4Œs/Šzð,^V'5Zu*fP«SWq¶íâ§ÛÍE®ou£\†Ïm´i1JÆò£M÷¼~Õ¿5»N4~~W ×ÝvàBÔŽ»ù8pTNN3Ô¨÷®Šé5|æ\Ø Óo³ÑÇ”ÆßŸÕûñ¾“÷§úsoɺ_ÓëZ“3:áºw«›¾yœ'Íò©½j›•ôšÝöÓ5<\°kÊ­2ö°ÄJŽß¯,ò/×ýøyýd>+Ùøõ£…%]'\¸îŒ»]žy’#z=ð{Iöe¬t]ë+V´lPË}vÙg)‹‘á'Îc{eÈ$ìù9'~ ÇkÆ>f|îdrV'̬ÕÛ=óW¾Üôu3×å×QxÜâYOÔxLáá}6>h*Ì–¸ÊpXrYîV [Çd®ú¾kCDžÿ ó7ƒ_`œÎmý¡ÂÊgîi“®£Yî)/§6+ÆŸßãEI‹}ÐŽ{~“Ê0MÞù, û=02¯%çN¦ÉÇ/0­v$„ë£ÁéZ…vIþ²ñíuÔ³g±ÈÞ®w‰ºi—VæƒèïÈðŒÖëÝ[•`²>!÷ÚçOôóÀ¦Ì<…Ï>Omð Œ7D4y'¼ŽéöNߟòo q昦½‹qÙÙþ6}f£H ‡ ³©2|zå5ѺÑö9=Ò§È÷A§|6s<èR Ñõȯþ½9gÈz·Ss‡©ðëÕc¦Ç.¾º5zÊÝVŒŸ|Û¡XØv6RšYöß´\†±ue‰Ûb &Ïg‘ý 2<Éûcµ¼ÉgÙ§®Æk‡q _Ë©Âs¸Mãþv=½É½Û6©÷ªÃûbïƒèï#ʰðcjpI´“y1Ù/!û dŸ|nÆÏ…*a+“ï»{NQa“éK¶~suép»Ÿ¶ËEû.ôA&KÂëäîáÕwV}ôÉýáÏêßÿÉbϳÉþÙ¯5øÆI%>»D¬Â F×KÞÂ/Aô¹þu¦Oú ß–o¼-ó”á çGJ?Õg¿KÎeiÿÜeÏEÈçgðË9X>½S¹|ž ›×þ£Ût¿4¶®Çƒr›ë¸Ó…ú»<ôF'wß§ë#Ãôs‘áì}–œ/’ïcÑ÷ÛºLŸëRíû_\gbø•«ò`V¨†ºß½X‚^è;ýqÒñ:¦V ›¼ÑŽ3;Ý絕am‹çÓ? g÷-ÈþýœÑSúAÙKßÞõØuzµyŒ3µòÆëèp§Ë¡Öiº´`íâÍW_ǹ¸íè-aÞèö³9CzÁ:Ð}]Âãá˜ì[“} r¾M¯¿ŒŸ;Ãõ]’†U&©°a¹dYŠN¡ÆËO\ǃy½“šù îù<˜)Ã-’µ¿­ g÷ßÉûAú ½þ{- \wz½I?Ç'qvOLÎJuSaï’„÷‚¥Èíaÿrtí:6…ô]ûÔ8dó~¬3ßyïúáÎ~/ƒ¬ïH}ÑÏý¼:'Ï×üãÄ,Û¶×t(|þ®Ë”î*E'.lïòé:NjV²NT1=Ìl¯‡>× ¯ñ|s#öû÷ø¿œïxÖ„ñ}jû$JÇp{k«ÂmÖ´ÛÍ¿QŠÒ_u¿Çýêûñ²í\”3‹'Ì‘á¡É'`fŽÉ|¼.²ïK¿ßVí–oÙøÁºÚþ˜ÆQ7lì|÷w˜Ç4{Qec^†> æ\œ5óó<‰/ê<(»IÕ)Ž)œ7¹À.œý¾Ù ¿÷õD0ñâ¥Ã&%ß´zÓ~9¯êïMñ~%Ç\AŸ¬&–¡ãVN½XOhk+=ÞËM»Ø÷B—¦2ÜÒy/¡Í:&ûUûµ¸Ø;ö­€~®Õõ]Qt˲/ûýÏã&ôÏ?Iÿd$ý2’¨Ì®c&ôõºÕ&?rÞþ*£—äŽDƒªêþ[F5aÐ6!Ũփœ ¨• ÅgU5¢ÕTöŠ<ˆQ­d4ÆlŸ1ªÙ„NL6%‡aµªŒ2ÊÙ„z&.šÉ§$|Â*&¯7šÉ¨1 )VµÒ(Nrc):ÐŒB*«œÊ¨¤2“¨¬rŠEcÌ(”Ô`r™¬r’Qk”Q©f]RÆ”T†œd 攂ÌÀ ~ HF™ƒYC˜L[&Óäg9rN «2³Ã($¬j5“kBeÉijä&90œBÊðN %ˆÆ4âU«ÍÏ8…„W]“SHòÊ©ìzå?½Rbò÷ë•fÌëÒ˜üÈgú«LL5È–b×0…[“ãJx„×Eq\«@b(jˆ…Í7ÅqU‚xPä‘FWÃs0æuýŒãjÌë3¹r×:„É•#9¾Æ¼®*&ÃIÊdËfe†gXÅp·‹â¹ªŒ²œô '0–’ávQy¾T¾œ—Îó¥ŒfÌ튨Áíâ1y¾$_.Î(_NÃ0mb»ÿÈ~Ò€„`ÎX94¤‰À¨q •mÒ0,×è¿È39çãZÌp»ÏUâ3PÚœk'†ÝeFñ­A*ŒmÄtU3l‡Ÿ±»Óµ&»‹dúRlª>©Ÿu¯ü§Oþè“·ùwíæÌÿnVëGÎùÏx7ÑF¹›jÿOØ®¶FŒ/ŠëʃŽUÄPÈ*#®+•¹éE­4b»V±ãëg|WcÆW$Hß’æÆ2“‡Öt–°1ã‹Çdœ«™|;Âùâ3¼5“q'aض ëµÊ(ëœ FŠé;м/%ˆË…¿Ã¥s†ùF¼/E Ö—“}ÇÃI@Z&/–a\û1™›$ãÜ ŒèÒ€„`ÈX9˜2¤‰ÀœqL榓¹ù³ŒsŠucËäëŒ8¯fL概aŠA*ŸÉ"¦¸_¶`îHPÕÁä*#Ö+a@üŒFX¯T °š™ÄÿÌÿ™?JLþ~ý‘ÃüÞZêóƒâ”þE¾§HBÑÆ‚Ìþ„÷J8„&e Ú¤ÙBaKAf ïUâC‘Gñ^Õ ˜ö3Þ«1ŒÊ%Vq5F¹ÄÆ<0êE;1\œ*#&˜™%ËÈɈ Fq_Õ *Tƒ±T ŒÊ'®êHó°©|b³\°È\0>“OLЉáasÀˆ†‡M1râ@æ`Ê$sÆ8`P Hr£*@\0kHË0_¥Œq}Aj-X 2còC• Û—á‚î«d æŽdrÛ¹Øb† f†÷©A¶`|©ûUÃ0!~Æ#ìךl0’QL=%LÕ(õó_Õ+ÿ}ò{üïè÷ÞÈÅ‚Ì2ÛÆÈ¡2ÛÍ @ý@m½ê|X Ü œ0Ўâf ؤ6âÃVÄ5ø°ÆÌ Þ_°aaÑ ª–41dpjMg"sÂøLV;ÅÀæ±Âl>ŽÄg²‘µ 3Lʇd¶óÀ@‘ ª43LâÁ›Òri>a†N¬17ŒÊMVø`¶ Ç0°)6ŽÖ(«Ýú`H ã@0¤¤9€1 s†«þIV{4ÿ2YÊz#F¬9Ø—Éj§‰¾ 5È–ÉS¦ØaB0v4cn_šaÆJ8?c‡v¬™;¬f¦ò?óÆ¿OOügÞø£7ò˜ßKG}>Pœ± 3(P?$„B™C±†€´ mÈüO8²„gAøaGÖ Ú¤ ¡°cAæ GV ²…"—qd5 Ϙö3ެ1?̤6â(jA|0GD ~˜Ḛz(Ƙ9Ų`˜Šf¿Ð\ £x² ͘ʤf8bRÆ`b‚Ks-Œ9bÑ58b×B 2Š–6ŒÁ°´À > Hrs*@\0hHr£*A<0k$HǰdcAf`\?$Ç‚ÌÁÄ~Lî<ÅÓöc8b„'« ÁÜÑLö¼1[Û—a‰qÀð~ HÆ5bÊj¶ÅÏXb„)[“%Ò2l ªn©ŸGìïÖ'Ö#©¾ø?Ù ÿïƒÔgâ@Ñù4Áõ‰™C1†ü G–ð1“Œpdm¡P¥ 3(V?ƈ#K®o ެ1ƒÿï°d —Ì ]Ê;ÅOTPBÑ+jpÊl94ŸL °D2œ Ã5£ÖÄ ˆ¦ˆfŒá Rƒø\ø; HhÄ*×`•QÆñ©A¶` HäÀð²)n$CÅ8`* Hrs)@\0XHrbرœÞ4?V²ÓIAf`¤‰ÀøqFlYŠuæð¬3–­É:“€t ¢ë”úùßÔ+ÿÕ}òßë‘ÿ¯öÇ?ëÿ}‘ú  .aH B1Ƃ̡ C@Z 3Ä─´¬ÕŒj„[+„â™C‡€´FÜZ3(f?#n-ÅP3‡Âi†Ú_±k GM…Û’æ=*Aæ`_²[M†ˆiA|†¯Fñl… ÷Q‚Q¢Aú_hž­9Ƥقq¤ 30È– ÿ-HuúÁ]ó­Á]3cùQÌ5  ÒSg+`4Ã׎éA`:ˆ Æ‹éAN`@%ˆ&ŒUÄ5¸ZŒ 2s†€´ !˜4–ák;€Yc®Šaà†€ÔÔŸÐÿ´ ˜XÊðØÀ̱ s0tˆ—b²qÀÜ’¿`²‰&LjÉ&óǨo…ü3_üg¾hò÷œ/Š˜ëVQï/§Ä•€t (Tˆ ÅÒƒœ h• îŸðpyPÄ‘ =ȉáár  % È [â2<\-HEgÄÃÕ àã@æPô!ÁÃâ@0ƒ¤ [ѼIŠñ-sDƒôÔy ˜D â€Qü@J9F R¸`?†?ÉùT ÃÅÕ„`¨X9ÅŸiA"0WÈœK3ÀU .-¤‰:Ó\\s0]H ùâ@0 _7š Î#Fƒª@b0¤ ÄSF‚ª@b0§ ăF3&õ©A¶`V)¨Êšfâ*@0®¤9€ .˜XÒðiV¸¤1ââVÀܱŒÁyá! 5ˆ†—€t 0¾Âˆ«9AP€¸Ð"@z4¥Wr€æ q¡AD€¨DoDû†ú¡zåEŸüY„í_ÞI/$}ïϘ·Ößþ#ýì_ÑǨöWý‹ô+Ò›þªýßô!êµ+AøÐu ôž8 @Ò  .CH÷'|[=È Dâ2|[JˆÅ"é¶­9Mˆ×6Ä’€´ á_pmÍ¡°B@Z ,dF=' Šdx¶UFüZ?(4 HÅ 2‡žÒPëM(<óÎ4ÇVBƂ̡C@ZêgꬊQÉp³#AU '(L%ˆÅ ª‰¡HU >j4S¬¾ ÓVAÑÆ8P¸$â›° [ ¯ŽZ+‚Ç9ào‰§Vâ‚¿u (pˆ EÒ3ŒZˆ Þ–€t ð¶D¥ŽIþ™ý32ù{·œ˜¿G¦H âBF€ô '(T%ˆÅ ª‰¡hU n$¨ $†VøPÄÑ *ŠY âBAG€ô '(l%ˆÅ Ò È .zHr‚‚W€8Pô$‚âqÀäFP€¸`†$SHAU ˜C ª‰Á$*ŒR8`_Äã„€T .(¤ñÁH‘ =H†ŠqÀTäæR€8\šÑ­ñÀh䆋qÀtäæS€¸`Àn4³ÛŒ(eÌè Rƒø`ÊhƘ¾ 5È *™Iý@ÌËÖ¤qÁ¸ =È ¬ñÀÄ -ŸæxG€´ !˜:–1¶(d6 :Ë[Ò€ø`øžúî_ âù#AU 14%ˆ TCCPxÐ"AU 'hJD$ˆJ€qB´¯˜’4ôÉÿ® ÛŸöÇÿL_¬9¢úÙ¿×Çþ#½ëg}ë?;÷ùWÌ{~ÖsH¿©Ùk¨>#©¨ëÇê}Eâ‡Òƒœ ¯(A<ø°#Az|èJ>øHPH  ñ "@:ƒÄ…‚ˆéANP( Hr€Q€¸P$"(’X9JH AÁÄ8P4äÅcN=ËKÍo¨ïËR{íÔ³P0ZŠ&ÄïK@ZŠ(ŠˆEÒ‚DPLq ”¤£ž¥`n¼¾ ˆ^‰¡ØT >\4St¾ 5ÈŠO 2ƒôñ¡  .bHr ¾£ÅèG=[ ©§Ö4àU.ø4¤9O• ø4¤9Aá*A<(ÞHxTI­_À£zøSI­]ýÌ™;©˜ïÉF0Ï`÷ŒHf¿Éx|2¯Š3:›üÙ^“³×dn´×TóY55úñïÔY¥†N20ü7M¬8´pßMÙp:z"yD|DòH>5ŽÆyÜã̳åò|üêìÝ Yr’ ·LYõäÖ¾à*û?Ÿƒ¨P»I!2Lç(†³ydÕóÊU:ÿù›kIxÔ8bgîËN+Vçc?õ¬°!oËP¨¾ 4¡y ¾)9—äp6¢Òf2OÊ0•ŠÔvk8›_JòÈINÉÿ$y7Æ\P Œ3Õ–ÌÇÝ‚5#çô¹‰ÒêœD)Á&¡ [6îƒúRXõ>>Ïa@3qø¿áÎ’ü$Â&9ŽÕr:Î~v>ž“vîí­…7‘ãêû–ý}JðÃÑÒ™ÀÚ2|pç°—'„³9äõÐ×»Ír H u}%\ÿôC»XXæã#üû-=su~8¥gŦ¼-{òõøc^èá õ{ö”ã€v—Cá,¯€äZ‘<ÁÝfG¶~EçåiáºÃºò³Ÿòðœc÷¦ÙUÞDKg£aœó%¸ÙަÎ/[{¡I=.ìQ»ËqøÕcK‡®as¸ÉûDçþÎæ*NL_lÁyÞ‹Í92ð°Dßûì/¯òð‰äVYó­5h‘´aA‹ë%øÄÐÂÞS‹Îü×cdXL¥U«×°1ò>‘ü-ò9Ð<˜Þ,wÐàgJkŠÄœ‡gln¿mÄW Zp)³ÍìR|×á©0·»7圞])Åýì¹ >­aódˆÿˆïlFF¿x6à ›óBò² ~€qFn˜‘¶34¿è?7¬°o9Z5æÈÀM[JqŸü^æC{x£¦Íå+‡5’áGÍÇtˆ°^ƒI~ÉM¤s}n2¯£ "\Hƒ¨z*£Äó°ÅÙÓûîÏ+Ga%)¾]âKqß?šh#Æz£a;Õ{d¸¼¾së _ÂYNá’>­øKùצèàwÇöv×í˜|j†[ã,+°?&ŸÜúö1y9êµÚ;𱦗×Zé¦ôóFþ'ã¾]“ák6ãÔºp–C@r–'‡Î÷©…6ÆfÅTÙWÏÓ xÑîy¸G×ý¥ƒï”#¡ìˆ ö÷R¼¯åöá•^Èj­•aCœ˜þGÿ ów:!š‡§f¹,ôûiËô†/ ãH^ýºäPË<Üõ€Ðb çòßíîl—2|v×ö¢gM¼ÐýÔ ›Ô.;íÞb &}ð…Hž"áî7[Ô¿Zž˜ÆÙ’½ß£¨VÞ^OßZäp 97úêþltÞ{§Í†Unžh¥¡ d¸ËûRŽó6ä ~ÃÕ‹þêÜhÆðmúVãØŠaœN¦¡AËr±áv²öZiÓ¶síye8u·Ç™SÅb4Ûowš}¾âh“Ö°y—5¹Â$wòpÏûý›Ëú"’Çjð ŒÓj©}ÁÌ}¹ ÇèR4isžVêÑpþ41jëèÖnÏA)6+:¿¢WóµlN,ý{wBôûõ‡€~}ßNf_Ú/pý‹9¦…'Ürq…ÕÑBþË[èÉç7«*O—aÏ̯’úõĈwÐ}p²7è¿è{ḵ,'“ÔánÞ kØ·_J ãܳxbJï\<ø¤³ôn§ÛH´õˆ«®° yž‹Œ…q~µ98³¡ ¯Híf'v\‹g@|6Ç”üþÀõtoÓ~iÔ0器“;õ6 æ]°oó¼ Oäþ*Ïõ£Ë|ª#Ëðöºë‡í½ÿöΪ©m{÷€ ;zP±bb‰-3vìX(ŠJì±cGQLਈ¢¨GE ;v¬+‚h,HèAE£pbåͽ×6ðôüßý¿wÇ»ã=ãçÞë¹{ÁÎüæ^kí•ïG¹`4ß‘æì±õS:À]ËÜ“Óz‰{Ò»HÿW7É,ÑÁ$ù𠨼v︽5RÉéøˆºNƒýý×Jo¯!{V1 †>„ö#Zÿ”S½tnHÕK+úôœ¤Yóºòóƒ?pœ§‘K{ÅÞ$ë}Š_= É€#×´¦’´–[ …Óa ‰{AöÅ_ @Ì›P¾ Ím¤óŸ¸Ýµº\:þMÌò}íÊp$8N»&ýÛÅm»IN~<Àõ^(únßç˜JZ|lxuBPj+oíB>}¸ ÊÝ›PÞ5{¼?:/—_ºÃ*œ°ãõ‹n@ëÂàgp‡éÚi#o’û;&ø9öïqiè¿6•ô¶³ÉíË XÒkªB?,„,È,=þIAŽŒ:YèЊˣîÂÍC^‰{¸Éµ§Ô€K~ à§;ŸËiðŽ#|×ôëMÓ›d©.eé›LpÊPQN%WE{š4Ù1vÝ~ø¶þ> EÈT­àsOi¾,›‡öVLsÞhýó”8N úÄ•ÕGc Ó,&eBã9ë¨SɆ‚«þ‰ÛfBØócÏf¬ !sÖ+3/*xž<­':?¦Ïiö~u)ÃõTã8Ž ŠºÆ‰%Ú>sìÊC®\q*™þ°pNÓÏ3à¾Ù¹³ƒC¸ÏÅ›Ðÿ?}®Ó:`säêðÜHãüuŽÓuUÇæÖïc“†VQ“ #k½4í×LKF-Ë-¾üe:GÞ8ßQ‚ãÜÑË[dÜ Êq­ïfË‚}­#¢>nÒ’¬o} ’¶K!iÓüQBIåGAcß9zš»Nçïìsì•ØsáŽÚ)…ìu¥xÝ–Ÿ† Z¯¼A®§1 Š,˜ðqaG‘JKìï.1ëÔR BC0n(a¹ÈÞ<Ö3åÈ%D7ùö¨&\ߨ‹ÞÔ® _CŽã×µØåÞü)(ž8+â\lÜ·àíp–¬y7ël7S)ô«–æÞ/”<ö_5ôÖ<>ב~."÷36ëÅMß»¶k}¹&Ï}¦ù•¿à8NÓìÔ•ÒÔ¤teÚ©>YY`´¬h¤^KúoÝ7$Õ Öõs;;¹G(IŸø~t¥LB¹¡”oCùè,Ÿ³j™ù¤¯? úï{Ôd\û‘9óͳÁ/tl¼À<°>vƒ5_³#à öÿøè#Šç>„>¯¨ïiž'ÍÅ.ã¼>ÛÔ$bf¯ªá³aKŽ¥S;Û4r†™XK¡ëËÂð"e™|kTÍn‡|xP¿ÓyñÎÂa•bß‹—O±W•Ý®ÌüÛä¬^4q„éÜRBŽ®ëÝÌÎ!:ÓȘ¼‰¯—Œ›ãBVŽiVÞ(Bùôç¥óbÊo2^ÿðúo×2•HÄ-Á™ ó›Î^-››F;Ô^Ù™Éñ¯³ðö£)?Ö”¿E?wšçŸòìÞ›•m«qõÔ®, ÇYÅ`®w]'+Ž[±7Ž%åÕÜ’FX>·êôè{mÜžRøT¶íøWo²Ÿvƒ|ºåÀ°Ÿsš8³ôBÖå±Õx_²÷³2ë§Úˆ¾Êw¯‘Ž“™ ˆ;KF^éw"ÔZ2ý»ºú4èmx‡ÐÂE³6ßóæù‘lÎb `óà/‹)ÏÚ8'RŽ×7»øúù‘Z×ȵ^“õ+È× )õÖ%¥‘ûGû\iÞuÏQqUáNŸMÞüþµÜè±¾Á1Íug×UX_àõŸì`êWIÖàÀÐÉr`aŸ\ù›÷)íÕÒá’‘¡dÜôÔâ•#èó·.ÏS¤uE9$t½gÌoPã8Îgf´´8u…0Tʵƒs ZuÅyÜéäð%·16K!¼îÁäü1¡\.¶7Ï- ýŠúƒæ„Ò¿gëŽÍoÕá8·®Ïd¢@Iôómß÷ÎÏûœÃsº¥“¡•F_Pô‘Û—CÉœÁÚîÍçS?ÐZšjÌK49§—TÖ<¹ö2iÞünƒs9ÂrhÛë‹—Üýb¹…¼nÞ…uÛ?ç_"W¼˜yôkyA:zE:ySƒHÎϖ“í¡÷2†’Û{õÄùËmí”7z{_WõÑB1»ž«Ë÷Û2\U‡¤ôÆ©Î%"Ýž“‘‹kDT†=édy۪ϚLƒ}µÍ—Û, %îoîhyŠ>/:ð\iš£›Ù }ÜËõù|ù2ù³8»~&n-²{—æÀ§ûGÚD§ÅJ†È9 ÌFœœþ&(”¼`¦) o"›È¬l…üú–]ÇéÄ켕åÜÊñºO*¯õ²jÍ~~µu°hUÜ€™é¤°Ã–‡“žL‡-¯©²Nýðå˜Ñ:bç yÞˆÁx]ÿóäl¿5‰E•ªc¯ tPÓÖoHÇ $òE–‹ƒÕL`÷=BÉ©Ä[ÃÎzóœe¶^šòü—?´û¦ïby j¼nÑ–ŽéO.†MZŸº×E^z;:tÈ #ž^=üX9“ç{mH4i03Þ›ç,Ó\é)>¢¸}yâu%Õš°Ü^÷òõÚø“^ ®ƒ,wõ”è UÌ ÏRÇ rÏûC+GùL(=mÑÔlh(™4g­Ûä£ÞW£Ï5bsõ ĬÏM¸yEÇ2\*“óz‰2¥>O®žV mÇëàîæ•õÆye4/ÿõŸÛÍ„†…R(Ùš¶Ó±Î"oBù0ôùÌö·WbÛn¥kUãž7=ø\tCýã8yÁ÷ZŸ'mº.SÄÏÐA3ų~$5èšV š© &jo(a¹:tßGÀ?‡éçJ÷ÅX_tçæ‡,G‚ãÌ)Üz¡ÝæsDˬVÜu0ç¶óÈ ñä{ÇɧELBU—æÏCÉÁ¢º>K¼ ]ïÓõûy'ò¼=êoÚÏ õã°|–³nežÿs­ú_­Ú°åß$±`ôç|7=Ô¤ç–a$øš]úÑV?úõ+åo°÷«XÌú¢k™yÇY/?ûW‡³äñøc§Núé`ÐÝ£3Šªgr’ɰêÐÓ°¸†aÄ€éÕ(HYžð壳û(]Êä¼+qœ©ÊÃj³À3¤zi'Íœª:׿›LÒèÓÕ'G†N‚[[64_ú9”äž]áÕSÁó躃ÍÖñ¹Øìº´KY~<ŽsIJO£MŸO†¢ë¬ƒ]«÷{çK2ÉÐe3óöÞps;ò#þJ %5'Ü"IÑrB¹`4ïŸý|þ[×ù‡ësî¹€×mbxðž& O„­ªƒÅo÷Tž˜IúØô_´¢ÐNmoq;”<̬6­ûž<úóÓì7‹C6Þ­Wv Ù½Ì~ŸÉ½dÖÍJʱ—£ˆõ;SŽÃ:Hü³Þ”D÷LÒcIƒÊ÷K¡ÔºÍH«ÄPr KBÕôgr~žF¯ÃîOë¸>Rh?0æ, pœ†}Š4¬/òRé ‚ÁÿnÎ$yGc?}äŸ|;úI(¹Ÿô(¿¾BÁÏûéuè~Ã_EmÃkÜ`ç3¼®î­‹]WÍ òíÓé‘“:XèÓê[•ÐLr¡mì”é÷'ÂÜÛc¥ßCIä¢þ ïŠÅôý/}n|‚ã|Ù}oÓœª8¿c𘖹°-úBDçËY¤Ó ~v«žŽ§à3Ï‘‡‰}IhUK¡Ÿ'ýyÙ>˜#fû×[Ãý1‰Æ¾Úž[/'†×ÀV¹°v½WïeIlPw7G07/m’ÛaÒ2uTÛS»å„Þoºž¢ûuÕUéy=ˆå/ ðºfï¿|OÜ2žúK“\ð{Ðk¿×3ôÁQ·—k=œ@>}ÆÔ¡‡ÉÎ]›]+uùÁE¡ïè¾åyê¯Ôãi·A³`؇`·¿¹y˱ñ¸/YÄæb昵óà@·š~ÏM鱊[b÷­'tÞNß+ÑýRf¶_gËm–âuCºìR5X»•#µÌ…N½¾;ÖÉ&_-hž­v„OŠc}QadFto“''½øù;õëã7bv>ÆÖŸ¯ËPï>ŸßªØj×¶Ê…ö.ûr¬[gÁÂ-¿öc„mÛF”‹«ïyeæÅï¿Ð}·‹Ûml·G¿׫:¹&¼~#¦óºÞ5Ô9Žs1?¼­ÙmèÝýžM.«°¹g6yvT6ÝÝÇ:ù»y´¾F„ò¢A#¼x~]?Q(åÛsFÕx}ö÷ß/üÆ ˆj‡>zß~€S¦®_™­’Œ„?§©ws9Lôõ/×U¼úqßé>${ý{<§—ò õ×7ØÍ"žWÂ…l§\pˆì{¹Îc$í¿‰+Ç!Ðl\™Y8™>jÖXí`¡ëúOÚÏd£ú5Ytÿ·¯ß² ïÝä’^ÒªhÓ¡Òfௌâ~¯:ç¢%zßÔdô¨lÑ[?vEô¿ßWN*šv]^ ôæ¹Æô=1å5óþ—(ßýTޏîÁëŽíÚë¼]‚ü¿ú¶v8¿õ‘g»Èp2Ê»çá± Byôþ°?®<ŸÅyø|—Yãõª·ÏÓ†@ãCGR;æÂq eQK<ܯ§J`QNØ÷5 CwÛéÃ÷{ú>‡rFés™¾o5Ô?^¿Øszp»œ0Æàbñó #¹cÆê  ˺бý@hÝêAïƒÂÉÑ÷Ü[Ôч¸úx|þ¹ü¾9ËI­ÌÃ>ïmÊòLqœÛ—FE;?‡õõGìXÛ:–ØO\QãºjO|]gÖØÀ€ëÂù÷* Ý«±Mo(]Ãluê¯F-5=­Lø÷-e8Ù8Î_¯…ßÞG;ýË…ƒ1&Ÿ/ê`ÉÎçÛç.&'>½Ù[?œ´MºÚdeŽ‚ã?‰ ·æÜ º™í¹uÎ#qïÕ1ó6}æß£ü€×w][Mþ¦ÆQPß´¤Q.$Þû& >­ƒ¶hf /Ûoø'…Φ×3®*øõÝ/§ë¨‚.S~Ïo·%¦“_'ž eðŽƒ».«+ÿh“nÃäÂìgO¦Ž9ª†^51l4Dx?ÚN ˜¸o B}K× tÅγ¿r<¿®e×—õ’ªîkÞ>içÆ™?«› !i+ÆõHŒòÍK§åÀþ}8Y·{mA'ož“D¹Kô9ðNñpI~+SH¯hµrzðï? >ÁqjÖÚÞßy ¾{¥‡ãóxÓ¾ÛÇ®íÓA®¶VHøÆ±Ð¯¨‹Ë’£ádxÑÆníÜéûí6@ïÿÃølóþßþæx•Õ€åÒˆ€úÝà‡Ý¯Š‚“7Ç¿©– –‡gXÝÐÛ·ÇÁâ˳6•lúñûÐ÷ÁtÎóܺ¤ujyÌ”ë#vü¼ÀàçﯷںÉùý-Z¿t¾B9]ƒ6ïé{pOÙ÷ÏjÇýþqùß.ƒ-ó"笚ß8¨ÞPYíÿ\shÌf)t°·«?jN$‰)™›”#ç߃P¿Q^é¨;¶:›Uå÷Œßƒèpœ—ÂS¢¯@§¥cœWÇúõ÷µšUkBª¯uƒþ>£‚‡ÆE’Ó–wœÍÚ*x~.Ëå³àß·t›Z1ÕÆ­¿/i¼OnrU/‰éÚcùY¸ »¥Î=®ƒ•=ǬÐ$æÀlO±e}›)ÐgñŒ³-S"É‚ÖË÷Þœ¨àÏoÐy;o| •ò¤dHÃúüý4æ¿ pœà´“ÉUâ¯BÐr× `½³¦÷™Èè½kG­y“ HøwЗc‘äú†O³v(øs†ôÜ"»~Í¿÷¢çžŒßëHpœ‡ûÜ„× â£þS·ë Ö¸rÕ¾í¯~15Ë>Ô)‰MZIî}WË"áÇ~­[º?O÷¡)ŸÕ¸¿Hqœ} 6¶éuؽïÁgWo¼oMö×ô÷Î=ãÄ“áN…ƒ÷{D’‚±ÑÍ6ä+xÎ(ý½è|ƒž?3ø¯g²ÙÆáæupØÚaߌÕ:èS˼áÂ0=þ2Åuê0Sú5¾Iöä/´«÷äÇ9=Êc¤~¡ç‰ þÀëžÚa@·C¦Ì_¬ƒV ÷Åôš˜Þã­í³{M€šÇ³ÏGösUãu Û½]Õ0Âú²òú,HM6úÕœd†GÁœÓ óJåu/ÿŽ$_ÌÖäÞÛ àÏÇÑçÝß ólú{su8ÎàÚkk8Pƒ»ti˜ûøÒOµ_^윖,x²9n8¿|Ý»ñÛH2:ÙfÓ˜Þ Bë„ú×)oëÒ5¶_øûBÏËÐþiðÃ5½dÝéO¶«A½ÇùäÅQ:¨û]¹¹zãè<ý¥ÿá×€ÙM±Jˆ$·g.)ÝñRΟ/§þ£ûþì¹Øïâ fÓëvzÆž[àõYþè X“í_iC*4»¶±Rô˜Ÿ9vœ|^WúÙ·ï7‰…ŽÌëþW9R²õPóUÙ 1‹¿àÑÔ Ø}ºHR½æÍ3ûzÚ÷è<‹~Cn“72¯,'µ ??6øäº^rï†]Ȱé±ÀÎsàC­‹c§gÃŒå~‡,FL€ä é¯"ò ¸-š{:ÿ(û¾ï.ÿþœ}v,ó~X€ã½¤ Šˆ·µ÷›u>œâ{Åœ 3§ô4Yyg<¤TÙ¹ªÖ,ùPü"´ùS¡÷‹òÇéy¯ó2¶Ä¾/ÓuR™óQ8Îeí¾/báãmÏ”»æ@`çÎjqïl6Y=+Ñk¹Y¬œÐ½ÿ£oY Ènû°Yž¸Ä5Þ7ô»ŠÛoòæ¿B÷O(OÝþ*fýÔ蹃_pœúÙë•™7=ž ÓÏi{öÏËö¹< üînì¾{ð²Ëª¢S­ŽÞ„Î{h?¦œyöÂ71{^½m™¾¢Ãq†šÕì±¢Y\¿jòEÿ(.9|J}{7 âßœòËô˜ ­±ZꉎÜ!wë»òã{5tÞFÏÿT;l-“B©x\jú— õÚñû¦¿½ÄJèšùÒ80×_?v2ª ;¬¿~" Žm”ù¸þTx¹ÿNÏŒLñZüðÐ7û}’žŸ¡û]ì~Z çŒÏ— pœ—3|ð ‹=ƒAöˆÑáŽO]·eÁ×a·<­]Ü B~¨KTdQIÖòøZt_úÇ÷Sè¹,ºÎ¦Ï/ãó«'^™®ô>ÌŠÆ©;+*w`NºfÁÛ¯8:Nv?_E¾ô²úyžœŸ×Ó>@}I×qôü=ÝÇ2øÇ°2ûÖì¡· ÔõÎ6I6„äúø‡ŒË‚8²¼ôÛ=WÈTôλï¯"sÞ‰j™Î[¦[Vܾ¸°7^×à§Vê¼Úž!·à¡íbûãýR»žYp!¹÷˜Ü.Щ÷Äy×rT¤Q×fáuÊù: ó•v”/3D%âëGkY>Ô™{®×ž»¯ÝØu ŽsÜ:èÙЯ· øáúÇ_K²`ªãñäâfY°qOrt|šló8Ôð›ŠPáô‚çtÞ’oÀ®»ßñßK2ÅIeòƒÁ°ró«Šìû55ŽsËÊåö›·aE¯Ö›Ü,Ošlª^- 7”¦ÔV:âz%Å%&]EÜD-•ý¨:`¿§”Îs}¿nQvÙ¥êÉs¸ þaê ö伪Þb±4»ôv8vžÙèíûL ï+‚âknì}\E$ZÚ‡kü¾·ÒùRÌÍ5ßkW7áxîíøçžÁ?j½¤’á ·Áw½öAØ‘,Ø›ð4%;“û¾ˆ#$ ˜I¹'<^ëGß¡÷=ïŸ;OeÂ÷SºŽ0øÇÉÚÏ,P4ý×»¸ì-Y¶x•çyM&tí4{÷Sü}:û5+±Âßgÿ«;_Ã{óßcçËÏùýú=>ú~Íà¼þ˜“mÒ&j@ [-š-Ì‚Gçƒ6<9› ÙA»Ã.Ä:ÂÛÕqã·wSæó§š>ü|ƒÎgé{5:/£ß‹q­~6ùÞ\ös‘â8ÇŸ÷j‘®$2¥8tZZ ΄ڙ™ïœ[:õ¼ a5ïHþ}7'ÑûξwÈãûíÓÆûzr§»ù²+{¿h`XíÇvÙeAùÔS2áÜÜ]£[¹@‡š_U¿Eúšw¸þ8ÏJçôýÛJÅl?ktið ŽÃœ‹~Vüå“VÑ* ª­]tzA&´ëÛÙáÄDhP³áöqU#ùÏ…î Ñ}Uö¾eŠCÅ‹fÆ‚)7kϯï e†ãÌ9°QsèXäûOšö9|2ÒcÞÃûöú^ú“`Öâ¶/¶\‹$=^èö%·RðïÙéùZÏŸÿºÒĺ d¾û:7¬wG~>b𠎳ò†nŸÏwàé„ùÓê§ã8'æÉE™0tØ÷úãMÃñ¦D^}¯£¶†œ¿Bû0»¯Ÿ$¦ï¡é>2Ýß3øæ†^2²q­à.,p“tμ” NÁ椦™6y…ÇŒ“`ø«IÁŽžV¯¢b:®ÿñ~…û~ý^û\­±Ê‘awâD`~jJº»K;Ö78N“³äËîB½[õ½›ü• ›Ï?“_!Ø}ùIÐnÜ´Í5GHÞº&‰;vÿØO¢Ïú~=Ïgìs´7¿ÿoðŽÃžË¹ â&|®:'ÖkÔÖ$?Î\eÞM„¿÷µ67ïÙˆ³º29^ŠžËe÷C ùº¦ó@ö}¡)ëg¹àõ9é.XVI.üÒ3’s-¤CdÀgÓ‰ûz§9C[›Åò}…*Òr^hÒІ ~VþyMÏ·Ðy;[ì¾¢ǹçû [W=\Û0ßôË„?•ßk)/gÀ•W½ši`‡M¶éÁs*²ôæ$¼“ B¿¯Kçt—~¯—þ=[ìï£Äq>¤/8~³o<4½ëPoüË ârÕá ä Ÿç7âüŒU‘ºMGœœNç·5~¿„Î è÷NÊ~ï='¬ÆqìümÌÆƒ,iÖ¼ºW3 ñ{^égÿ ˜R·ÆÖis½Æïz?Q‘óê~tù,çyòôû%t^@çÑô½<½ÿà8ÓÓ{~tÞ»;î\¶=f½Ð´~¹"® ÷­Ðuƒ ø½X`òAE&I«pBý£ès›õO7ŸúƒŸ·0‰ÑKÈåv[nÄýž¯Ë2`lÈåK%®°Û¬ÉÖç'Á€W+‹wW9B\=PáǾ}þÐsžì?óï+è|ÕàçY环veÆCÒÜú²Uöæ¹Uðå¤ù÷‹;§€ Î^Zá÷QÊ?躶]ç+n6oÌí›õã¿ïhðŽspÚ¼MŸÅƒiÎÂlQÛ ˜gÚÝÂFOM^tD Öi“»'Õ9B†1Ç•Î*øç]ϲçžrë¨zðǃF>£Íû±¾Áë÷ìÑgܺñðv_Á¡HÓ 8îå:ûö×tH:îún_èt`ç5*‚‹î F(ø÷Þô¾Ñófþ)ý/i+Ãì=žêÅ÷Ï0öÏïl¥ßÙJÿ ÙJ&Üï›lR–mè‚R£l’¿Kù ?ËߥŒ†@#lòÁh°(Çh,Ç€µå Eå°òr¹äŒ\ŒòÞÜ9¾¡„cÀ–ç4(¹Lr©£FÆñ_vƒ’ãb»s| ǧ¡\Ãh.'Ža70üW&SI.`9^ »AeÄn0æx•gÀ2ìšgœ¯Âñ_Éá¥,/Çòý„åeË1`™^)ǧ¡yä4O‰ayÙ¢©P%ÝX¦¡†ËRúǢÿú+Ž—1·áW9¼¿{ãïÞ(7ùÏéæÜï£5)Ëï’¢4(áOò/i6ùÏò/i69åR~Í?e“[•Ë&W–c ¹lò’rœCßr¼ ÃKŠŠær¢<8†—=Ç9,ŸO®âòweF¼wŽqÈd“«8æ«Çj°çX ”ݥ沦˜|r†q(°ŒC†kÃd”Ge”smÊ3²§’Q"4¡enû¯ekR¶;Ƕ‘ü„mÃð±9Ë8VÍÞ¥|l†k#DSrÆ–q¹»¢_0mŒ‡¿bÚg”ÿ*““©IæÏ?õÆß}ñw_üU_ü?Õ-¸ÿn^Žå€*1ûŸù5”gø3~ å&£DûŸx†4ܘgÈp°…å8ØŽƒí‹*B¹`ÑG—Ëfx^\'“I®âx åx†4œa¾J²¹zLv°=Eɱ ™,r-Ç{Uq¬ŽÕ@_E\9Ã2dØ–ehe”ENY†Æ l[4Z“/ÜŠejŒ²È³7Eÿ v e`S–ay¶”c »”ÍÐK6â_3Ü)šYƒrŒ/“_°¯“Q¢^¿æ^ó Æ«aòÇ£~Ï÷C“ÿ¬y¢÷óêLÊr½d¨d”è'Ù¡4'ýgÙ¡4'r)ÃæWYéÑ(A¹¼tU9ΡˆËKgŠÞ˜sP.›˜a{ÉPj”Bα½8Ö!ÍP/BÙsœW&—ØÝˆÓÀä3üC{†]Ã1^å£Ác4P¦—%°f³Öþ¡­€å2,4X´Qöº1˦<ÿÉ(5Gó¹£´( šP…²°ý×rJ)ËÆƒcÙØÿ„eCˆæãUk”ILyØ ËF„¦V¢Ì9ž—–caÿŒecÌ>ü§|QšÝ^>ç”I–óý_œ'þîÿýÞø¿Óÿꉔ eQŽéÈ¥ô'ìÊ:ü»†²µ( Ǿþ'Ö!Íj7f2ìkQ9öu2Ǿ@•0½‹^].[™áz¢Š¸¼ö(”šA^ŽuH³Ú®—=DÉ0&PhÇ9drÚu–ázY¡qä{‚r½J¸œvg&Ë9p9í”qh̼¢É9£É8f Íh7GÓ¹£´mþu^ å]S¾ayÞµŒãŠ8,ÍrÖ±®ËPÉ(Çõ2ÿçÚ¥EIzýšsmÌ5ü§†Éeþ=Oü=O4ùÏꉶÜÏ£7)ËórGiQ,ÐT Ó±P5Fó%()­%Ä 4ʘ§¬Cù‘1¯FÙ–Ë™*Ç:”pyóæåX‡ »å‚&PsL/w”%@CørL/ŽwHsèKPÿU€Fñ0âWÈ9Ö¡'šc¿úrì Ž]AY^É([k6»žaò,ó°å‚SåØ3zÊ3U( 4ŸJ‡²GF¡¬ðÑ¡’Q"4¤eަtGiQ4§ eõ@%s¼9Çìqø ³‡2-8ö+î°G#Gq²µ( šZ…²à8^:Ž‘ý3¾1óðWŒš…o‹ U‚’bCР„ØþóÄG_üÝÿßí‰Ìg…²2-ËS¢Ì±(e?aúPÆáϘ> ãP‡²çxØÿÄ7,B¹”ã2‡h”À”e³q|6Ê Ó¥A ±@¹"¥üG!k W°2T2ÇdxØz”ÇÃþ'þc JZŽÿÈð°íËñ°þ£ ^‰2Ç¢wG%£l±øP%¯MÅÁ¥fØmhˆ€rüG!šÃUÄôG4IÊ"EEsìG_TQc–÷¦FÙ¢yŒ˜oæh"©5Ë}d8Ø2Ë}¢©PEFÜGc¶¦BYpÜGÊM…²BãÉQz”=P‰2Gº£´( šQ…²@Cz t({4fʼã6å>–ç`{pÜG{4nÊŠáÞ¢ôF l 4²J‡²GCG¡¬~Á¿–£ô(‡^¿æ_ó¥Ø4(!6@®0ü6Íïùâïù¢ÉV_´ç®WÂÜ,J Ê SŽÒ£°@•(s,Rw”%ÂbU¢Ì±`ÝQZ” W…2Çâu7bW JPR,f JˆÈµ •Œaq JPR,r5ǯôEéQXðÑ(«rKg*e‹F£´(!"3… •Œ¡9”(s4ˆ”ãâ Ñ(¾(J„† @•0=£á˜¸œd(5J€FòEéP"4TJoÍ21U(s4— •Œ²E“ù¢ô(4[t9>¦/%@óù¢ŠP.hB5ÊèÒ¡ìÑQ(+4¥¥G9 9£Q4¨/J‡¡QP%LŸDêQ¶hÚT ÇÌŒF 8&nÊ¬æØ–r”åÀ°4Q4¶/ªå‚W£lÑ䨔Ԉ¯€*AIÑø”ÍÈ5*%ÂF D™c3pGiQl J`}Áü)ßvFñÿä^"íƒLÿûUï£=ïŸzíoÿÔÏh/£}‹éYÿÎ÷Á¿êKÿÔ“þoö#¦·«™Ï‹ Uœ‰Áb°ÂbHF‰° ”ÌÚûO2J„Å¡D™c¸£´(öž"” Çå¶Å¢ @• ¤X<”ûN ÷`U£l±˜Œ8Üz”‹J…²ÀÂba¢¤ØS4(!X Wd2T2ó΋­„Y‡b±Ùb± JPR,: Jˆ…§G90çW°ð´¨ÀæeyÙöØ¢PVØä(=Ê 3%Àžà‹*bΧ`¡Z`¡z t({,Ø(”­¥G9t`ùضØÊñ±åXÐz”u4JÀ0tQE]Y&¶z_ŽÒ£®-Jð ¶/ªåò”á‰ÕKÎG¬¾Ý¶wxK½4×.úÊ_Ýôƒt8Wýê°˜åR(PÖMírRE"fܳ+níIh^=½_”GIsi®E™ ‡Í¯JÁÙ³úàjé`Ùo·Gžu:t*VU-𜠎mm«"y5âš.~ëIh^>Í»¡œPšGiÌ“‘àõß1Ø£) ЮtÒÔKÙi0,¥ñ¤ªfé`mºóÐÀ¤É 2õ9<昊˜Z¶ñÜÅ‹ç~П—æw®ߺu`\)ó0‘âug[Xk߸%@îþ>ŸÝN¦Á™5róÒ Êæh]µíd8Æ`:ö¨HX¿©ö›6Éyn:Í;£y¿lŽË“ãuãâÆ^Ú<)&אּ²ûê48Ñ1wEè4˜±<ÏÅçÀdXû®±M£Ã*âZÐ1o­‚К/BóeØzesk”x]–ØM¼zf˜'X‡í=ž‹ƒ×_)Hn &벊´Þóו¤7 Bó°(gæ1±|@–Û Æëf7ó Úùù‰ËµÒÀnꑦçüñŸ›†MI}4ª2˜çÓ*R5²RçÏÕ¼ Í×bóDm8_‰Ù<,–× Ãëšø?_?,ì \ZètXæ¾0 Î x»kžxT»4.É[EÊDÝTÅÿ”Äò¶Š8ÞPe`ùr]ËæÇÞÔK¶E©|â867?Pi¡¾eåYF¥AiÉä‹ãJá{‡=->¡OƒvôÝbäÜ š¿Jù?4æ„s8ÎM1Sa àÜfŽík-,˜ð&`Y‡4ÞO[–|·±l¡"¶+ý}~äPÓQ6ß«[×x½/ -™ñLû®…•^äUOƒ~}w7ípB ù¥MžÇWWëI¯;×ó¹t,ï)U|¤ÿ™¶›$ÅëÕxåú|ùÄÛáâ¹FZ˜Û 3î•ÖU­×dªÅ4h<ôx\¤‰ŠÔÚôøÆ />¯æ©QÞ ËYasŒäxÝ ºœMš‘ÀñRSáï{ëÏÖhaQ“Ë«ßKáøòUÁ5?E’ÖuÆ·ØìÅçÓ~Õoìæ’ \Î0WÏx]ó[7Âñº'bfË«ŸI…÷ßy¼ŒÐ‚EÁø—ÞŽR¦½¸Ë·(’\Œ¾5çÌz/>÷r Ø]‹$ÒÞ'2”Åž„ò:hŽ ››.I¿¦usårÄné%;ö¯ì’˜Vçä÷óR€¡þÖmŸ çR‚ªÌî3 >›Í}A"‰"©vË?‡z’²Ïõ†|ý²}€å 𺟬øæ…?oË÷v.§@V·Ãe¦©à,œoõ~&„Ìݼû¾&’l‰l6×ÝÕ“ÏE§Ï5š÷Ìþïlÿ–0?ïË‹§*¼H€æ+#’Wû§@‰#“Œû ӇOÀyíµ§–MïäE’{±UGF®ðäç#4·šòhG>lòˆã×áu?:oÿâ”ý›¹}®)ð1}¶ÉéÇôjýâÍÓ¡±¡ªˆºà`ßÖð9g4—”rbi.}?å8ÎæL }|Yv?àm „6©.Ýÿ1¤5ÙºnQÍi`~bSç?«ÈCÃXÍç+R?›*;O4ô…˜Íí¯Áå Û–áÀ(qœîçLµróDÈ›^ãøÓ’G0p„,3oÁc0ëßzüÑ—np{ˆ`jjgù\íÁÖÁWsÏ#>§‘>?)§‰Î“Œs½Õ8α–C{Wh™,·à¼]Ú¢ú“Q¡B ›‰O&Oå}f˜¦ =y.#åÐgin<åuPnæÙÁJ™lŽ£™>! ïËA~“N'¦§À®Bó)–S ¾ßrˆD’æfµB†'Ò~.ä×”sbçݵëÂÃuøõTîŽ3¸¸ÿžJ$ÂΟÅMZ=‚ [·¤¥À´6IÇŠ¦@í MB¥ÿÁÞy@E•lm1aÓ&0˜Áлu 8&ŒdmÀ€˜È´‘6 bÁŒ™Qš¤(Õ‚ MP¢ êh›Û€ƒ‚Šâè·OŸª3?ïZó‡µþûÝßYë]Ü;álût½»ªvûù)†M(ž8q%a}NŠ÷ù3ÊÕ5êÜbÊÀ8¥¸J?ñ>¸I+ü]!Tå¾-~¾w9{¹_Ù0imQéËî1„£ØÙ²‚°÷Åò Û߸ÌH±k@¿ÿÕú;+0Ng}Cê(YîÒ¦[z!ŒÒ:æ´Ú{ŽéÓ‡ N•žîßÁ4†Ì9˜a™{q9©ÞwÒöw2\Òü¡Äèðá9ŽÃþ¢ü±ÕÖ)‘gC®¸ôΤ{·Öo /„³Áª—^‡-CÌý´B5“L‡Œ«Š&›¥ÍL\F‚qŽX¿À[ŸÝ95¥R¯§û û~½0΂ãµ/ ÁýgµM:;Âk™U§Ç뀃õGƒLµ—d—k£É°r›  3–Æ-bŸ‡ù‡õCçù’Õù0ZŒ3þÐŒ²yrà‹·R)„sŠ-_ ¼MÝ<[ŸkæU÷T¢ ßï[No†ßgšû-¾?q%ÝwYUï'®ÁüƵ{nšŽ‹æõïôª>=Ëj~2¤ºçÅ“Ýàe‰ßî=µbȹÅI\6ÆŸaß¿¾(p£Yó4Í0ÎÛ[¿IždÖ£–NºPwÞé²êM4œvãv7h¬ªƒK€âc¦•HXO¶/çû3_xl~÷-—b¾?t6l¨Ñt|dhì;Ua¿2·bÊwåÌíåš«Þ5%1¤“̉_r ÀÝ`y…¯ÔÖGšçðUo2Œ3¥sÖÝÉײiêxù`KaŸ„"Øl¶Ã}ôCØÅ5½¸k á¹A†}Oü|öJà².æ÷(0Î rê<Ÿ £þÐyH ,r81ØTEéÜÀñ6³~“=®ï£Éàþ»Ú4 æ{ö“ñ˜ô~Á窞®3I=œ 6£NŽŽmU Å|~âU¦-¶®v­éâMA¢I£i½w¬L]BØûaó%«ˆ¹˜*|îù_‚pHgCXÌãºÇÞäÃ‘ÝÆ× mqžÉ®]x=Õ ¾èöök´!š ?çbBÅRÂú±²üÄ8¹b>¨Ÿ«ûu0Žœl°kíõÃÖœ|ð¼atŠt(‚ŽVou½âçŽ_¹qB4ùùñÌìî7— ¾cÏgœTÆåÒÿLô÷µ»ÏÏÎËÝûnµŠÎ‡/+7ýÐùS!('H›¹¥y@“áÒ”µõ£…z—ì=°:Aj²jCe9¿.7ÃçzŸ~ro|6Ì_ùI9rE>(j5i7®¨üÚÖëW< >­Š{] $¿tÊ϶ܹLà°|ć—¶¿`ýkõãŸÿ¬°ÛÇŸûfÃÉ1+¯wȇÏÅÁ•Sã a\?ÛËS4³!¼²ùüd’\l¶¼°b‰Àscß#ÛgFÁóÙYCY¿ö ®KÊ0ÎdnÛÝ4Š,Î¥ué“-z¿´ê²¢Â×4sëól¨z¦ÿˆäÊRÎ(AB¿_ö“qtmwå4 l çø_›èÓ£ÚºDqLõ‰oø0 *¢ÆyyÖ̇WOÏu{;©ÎU*'Ìœ =Fk¤\£‰¿[TˤªÂ|Âòãf°~ÅŒ»(Îß‘gÀt÷;Ng]›ëϤ7ò Ô}×øÎ–… »ýÒÑütÍY¶o^j4©è' ¯˜ ŒO–·Ϋs¤ªð<°bëàúZðó>¿ÿ¨±^«³`ÂÙ—Æ_óàÄ «®Ñ5 …úgð¥ÔÕU5bÈHµ’e¾ŽÕ\ȧ|ßâ‡ÂççʨFWëÿ¯Å8Kj œ|Æ6 Žnnû¬á¦<¸éú¼÷í{`<ö܉°Z2*ŸºîNÃRâ݈E Ð™Íüzá¡Àµ¸ƒ«èÁ“&CJˆ6yÓÞA¼?²tR¾Ozt®)m>pQÔÕ¯ ÀÃÎeDVmW0wê~«íÓh2òí ÕôzB_~¶žã9Iw&?ÏuÖyz¿`œq›+׼̈́ô+OÞ²Ã8?Œ5Š.£‹ÿŠoê ãx€ ô\—ܺ$€°ï•íÿX¿gÆ­ãë– æÑJ1ÎÒŒ®ƒ¶fg‚ËñgÆu̓yÃîºen,€Â•Æ‹n–9Â(ç#Þ ‹&w/ý£ƒU PÇdþaëRž‹úNÂê…âý² ãtˆûukf\&l³á LyЬÑä²|  M»QïÝï:ª]­d}T’WÜò§naû>¶dùõaŸf'zÆVHØ:\¼þU`Ã7§zË3ÁÁæ´&0%Ö½z×Ä¡â?޶°¾L^øvÄM%yáÏMDA¾ù†}÷Ö÷m«–~øÀlþÐûãôš°<é°u&8Ÿ¹Ñ²ÞÂ\pY,-Û?´*_ô ·v‚±Ž!Ö7ÔJrqpŸé{¿‘Í'šÍX=¥—P¯P¬ûëƒIë;’çã«öùäÕ‚·³>çœ7Æ_Zdbú‚ïË®Â8ò×+ï¬ü ÑÓW¶Úd’ åËÝ~kܱB·,6=Î î>:«›§$Á•þ¼÷úôk0wéO•%¥ùðœ+§¸8CÎYÉÑ^JòhÍü7ëOÍúY ­Ï é pñØ:Šå¶dÜV_Óûã˜gµ<®È€&ûy Ã8óÎÊÖæC¿P/åüUÐú^Ë]ñçA;혻ãM0Þ;ÿ½h^ «ê}ƒÏ×tË®š<·ÎU¸uê ò¡cH»ô(b—//Å÷µ¿"åuÒÜ@Z¯± ýå;ÂõOÃK”ö”ïKyyøÜ­Ž—ÒJóÒáánÞ~×v0 ×þã·Vv±‡ÐroÃÂyJò4öðC¯YA„q=§‹¯ÿ– é:jzGY=á\ˆÍ«z`œ„’¬±ŸÕ0aÔ·;rà}ÒúW§óáꉖ³·¬²‡¶ü†85øÜ5@X¯³:Ï+H•0ÞÔsœ=œÎY„í/Y>fóËJÿçYs—HNºÞ/øü7+|â~q¾ïÇúªŽeÁ£njŸuʃ¬÷½7¾=é¡ç¢¼.Ô‹&]¼_,:ðÜ[àDzz;Ï4ºÐrBèâº4ŸØ;ÇÖûæªNÚsÇÙŽûZ\ØI¶}ŽeAXpâ~›þy0ºqFܦþîéX\Ø8XI:Þßõøoá’ñÔù}{Ž„ÿ‰ë²ssg{U¯—›aÿzëî>¸Ÿ?Þõ“Ù Ï‚II[?˜äÑs7°=lhq[¥$³žîÊØãCå}–‡Ÿto»¸”ò}ªs&1΀#{çi ÇÇ5Í‚D{oÐæ‚a'XwÀFFënÑä’Ïäˆ÷Ö~B=—Õó'Ei¸ro“~\ô«¾ŸÁ8 öÑ^÷MƒÚò4£û™àU'¦ÂðP.¬õéâ²r§+¼õŒ·(‹&ŸZYí]Û_xoÕï'Ü•Üùræó“>J¯˜Õ õ¾Á8»{:6¿ß%ò‰2aúŒ]!!^¹ð*Ý¢ÞOUN°ý½O¿ÑÓ£IÁTŽ0í'Խعãcð?k‚ÿ ]±w»U›#1ÎÂ'áEÃ3SᦧMÞÏ 3áüœ7W Ì…Â_~Ù?׳#÷M8y]I¼L%¢GøRže]aÉÖlýǸYÕ8z牜sB*HNLØñg«LÐõp˜8¹v.ÌŸšÒb¢,È7<0GIó¿a<¶¯ÿz}ÎêÓlŸ ÷Æ9ka» ¨I*xõy¸PySƒ÷Ž*ºFÏå`hÒÇçñR%Yó{ãîwÞøö9Øçâëç%õ‹êLÙ[.aûLqÕàšNš5Ÿ_3³¦­ÅõÙŠûƒæ»D]ƒÁ÷Çî:í»ŽnðÞ6IIrþì·ûõ_ÂxÇŒïÃlj—°ú>[çòŸ—¯[˜aœN7à }¦¶­;£g×ì4Þ÷ì®üìmë<×ZId9;1¢a¼cþ{©EÇÛ1Z—ÔIØúŸ¿|ÝEŠqÚÏr´z=ú2x¾ÐÊ»kàV“ÔÒÖ’k×ßò­ªƒl•r g%±[kQñÑrÅë ¼YvÈøIlÿÎêzzÿ`þ¼RÛZß2X ÕÀÑ}u/WÔ½WÌÛØ¶t†§mò‡¾h­$®›V´¡^¾gV×ÕûÐç+o$ì<„qiõþÁ8óq–h¹K ®^3 ÌÎ#Íæå_…½ú.ØþÖŠož/2ú+Š\˜Õ¶cIG'͸<ŒÓ6éáöÅ z¼|ÊÖozÿ`ž/©‚Ä„ƒŸO=ÉŸKgûºî¾ Ó%ÍWUuõoÎ9¹”$ßÜ»SžÌOàȳýãtÅX¾ >Ô¨J¨KŠÏwTçÕÒy bØëÁ©pb“çΙnW¡Ü^ãÒjª3¸(_xy²’ŸÚµ0&Á‡°|Â>;·ë5"âå³~Ÿ$ü9ÃOÕêŸZŒ£¿ð4ù¶tÛà›üç¿ Çoóø4Ò ¸ÓÁºÇ•ôœv½7ÒIxË£,°º˜wi«“Ž>^DzQÿØûød>ÎNà‘6æmeŒèôÁ`[}GZR’»:‰YÞ<ÂÖì'»¯Æûè#=ëQ}_ƒq‚¬Ül¸ÛÕ¿¶Ql±<}´*#æF¯´ilúåT—hº¾™+œ²zó'^aTm¿)Åçk oh“—Áqáç™ÒA×7òî¦=9`·æ”Ò!xåjF“=R“¦ž¤:ËX8wVïhÜûÜÑF¼&Þ?Ë0Ž<ø¢}¸‹ãù‡¹:šn±{ÖÂ;¶qËÃfÓ f©×[ÌÓ Ú›[@÷éu„¼Å×·I`›CBÌ #á/ªð¹VEn¿:KÎÃŬ[Ñr—txgñú\³—Ùð¢“I^ߣµhNÅáûQD"/-Ió%l^dy–ÿóž•ðÜÔ|8—յϑ½ô÷’t° ´cî¹lxijïÒv‡3èÝ”EÆ¿Ë-T óÖ+ì'Û3ŸéÇ{žNðtýÓ5ÎÔsèòÝ–é¾çö5#E6<~}ú“jÚ xmœ>ñáÝ(r¥“‹¦c²ÀSç÷%$âùÇ Ÿg]™:+^– ›¸²AËt©(¼ ‡løü{…vÌ\7˜Ö¨2Ь_z9Æf‰pß寽åûãÌ/|¢÷ÿšœHñ¼-Å8º¦/©ÓÎÂóNήê¥Ãò+t«?fAUyÇ~Önà—<}Yq¥°Þey‡íøúÖ;áÞË×âuˆ ãÔl‹šßå,Äý0¸õºjðQUžšw& ÜX=x}XS\UÙ Û(I倿JÂ~ðî¡ðu´’Êùö¿e½¡Üá<ÆÓ>%µ×Ì¥SCÇ¿˜uÌßÔµùGÌ„‰"¼æôT›Ò®6ï¼ [ײ}^¤–X:FäÖœGïgásy>oLŠœøhLz% ØÑ& ¿ßrfÜ}pü8y˜’̳ýºÿPŸ¯îMuÎSøûü}2>7˜DàŠ/ â“}6~8§†ß¥Ï¦«‘'üw¿½tߺ|ÙF–áüËŸOù¼Gþû²8™ì\A?žñ¹¥³oÏ›?â4¬ýûŰÃjXöjBhV^&Xµ™2džØCEœÇŽ‚¾J²bÇò—?Ùù un 7í/ëCóF½ЄÖÁªƒ|ôûcÃf.=q“Ôе¿ÙÓC™pò‰I©o{˜g={QЉ’üp²|À€Á¾Â9 {ßì^cãW~æUêÀÎ¥Åõ3Œ{dp‹ÛQ'a휛—š¯WÃqfþ™`oãØ¤ÖB{XèÃÝôŠ"'bÚ}ÞsÔG8×d^ö½²÷Ïçž)ÅçßH/®×ëò X0-²Å5Ä/ϵ];*N¼ùƒ1÷ S6ÁÏá8ß2©¥au[öÞÙyæbÏÎF'?ÔîC±º¶~¼cœ/ãÆõ®{vt¿˜=r¹ÚÔ­é_«y&='t„e}¸“.%á9}Þÿí<ŠåEv¿‹ñ·Ù¼¢÷Æ©}x+Î|‰P¦,‹, PC ·ƒÑ@nƒ¶­;ÁäFUCOLPžOú÷ûbçÛì˜÷YaÿϾ?½/0NÁ ø=fÏÁ`}AF §Æ”¬è¯¯ñŸ'Û9ÃÒ m;,Â8þz Ýç#œ²sþsý!áÏë› ÷_Äõjƹ_÷锌n p&dò2“…j0'šæ–~Øi¦y2Qå ±m,Ë&Hpžºåíк҇TŸGêÛ?t›àäZ>‘Ö©ð¹+§¦­œxô]÷¨AéÔW7H#¬ãZùÔðÜþ“’L×}…sRö¾Y=’Ýe\_öÿõ>)ÐIWÇ͹1Y–ÜjI¡]ÿˆºowŨg¤“’¸—ä˜Çýê+Ô«X]„­G"{[ÿÕ­©ÀÝeëp½O0ίcãÏì…kê{ïj¡O^Vö2®¥É ûsW¸RÿìòNq½_†ÊK} ÿßwî›ðß{®$)p8n¢÷sñ¹£Í3Þ½ ]w:;5Âç_w|¶nsømsßâ=ßöÔm{qˆ’ðõM?á~þ(ý´`&Œ'½ðymsçJ/µRB¢æx{wô[ìc÷¹³§g@›äš8¥8Ã]Ç ¥Ãý”ô÷üãܳñÁöŸSsö­ñ‰žkàsr–¤§Ì: *Þ.¿Šß'¿ÏÅ‘ÆÙ‰Î°½·$gÆ%Ñ/#¶ú“‡ý??ÚE˜·øüs[ÂןjñãŸ{!¸Çˆuªßáç©÷sËæ¨¡ÖCmAï§éÐûÚ½µÃk¹Ð}¾’r+}Ë—ì<ƒÝ›í(O™ö[-a>×U§ÇœµgëšGÂÖ3ƼqQÃöå.zO‡ç½â«Î´p–%×ÞOy¨$[_šx…Lõ!ì‰çnvêY,?ñ÷» bý¸Ç8òåËnFöÚCÏæí0šŠó·ÍJ‡)§¯ê-w÷&¥µ‰’ÞŸ÷!¬®ÌêYl_¾"dUeý¶õ«Õ uÒªí«j:íM«µ¯K]Õð>¤áÛÚ¸®òÒM›b«s…㥥;)”D?ö÷ê™lÞdù”?ÿù$píÅû03Œ3qÈÜF »à…ͳç«!%+¼ÕŒ/j¨;ºÓì'®2h8xÓ˜à^J¢í~þížý {lßÂαØ}ÆfœhýøÇ89×k·CãWø«¡´]ð©»8_On•tB3ÛìÚJÉŸ÷¢ÈäUÛjû û#VgâóÙg «Ç°z·Þø|žO¼–ï›—úçÑEÛl^€ë/E?¹4œ8‡H{ŸŒ"ú_ûàGø{Å„{Êì|„ñìÙùˆø³ã„}1¦hø\ßöaô½(5­Ë¨!H=ÜãNÚ,ȯý¶§ßù(r¦Äaý’é×™™ŸÙ}ÍöÆ5s~òA"®ûEâó}þº ñ 2ÎOVCUÛ+~ÎWà•·v鹊YP{^ú¡g/£ˆþšX_aýÅò«_%ºdMmëþ^9°yœÕ«êœlŒ³wÚÁ# ÅÁ°{LQÉU5ô¨¸Üg€ÿØ8|UÏÙ¹³`ö—!L1ŸÊü¨7ÞW¨—²ñÅΗÙ=”ŽúÂè€j÷˵§v?“C.ùBO™áÙWOð}©[Xx˜_{svƽ0 ´èþ¶Ãÿ®÷°÷Åò»Ç±sCUü‰ÿòÓÖ}«ñ¸ ŠtR>Lƒ_-¹ X:ôôY¯™–ŸŽ“ÇTÚìö€×cÿºg«$Üh_×ËôõJùEh!Ü/fõvÿ0i²Ñ£¦£ªÎ—Ç8ÆNMfvÙ #üðO‡iöCKÖ¤AqOC5=`e÷‹O¦)ɺo _ö#켊ÕÃX½–=RË×|î»ñ÷Øyî·”û<³jMµ^íOnmf²¨O:ü ¶+ÿh7›L7²«t =`\IçWþùÈOð%[Ï0>2¿_{/yšvªV«#÷1ŸA†qÔa½ƒzšn–þkl:¬[1.IVž u¾˜ ñ·rOœeúÏT’C¶žSò÷¼Äò;åëóï%k>*yì<‘Ö†ð¾Á8©ó.oLY½–¸6UkÝ0O¦iî¤'¦‚Òécˆü– ôËÿ‘J2Q÷w¤û“çwîñþù,aç‰,?èýƒqjßµ¸°ž”EýšÝmA:¼Lz¾öþÂTñcÁRøûŸŽÇùÛ©üa¯žÏýȧ¸×ãÝÍ„:3«còóL…„­£Å÷UçêµCAÝ´›É¨ygö¤ÃŠ?úºüÔ#nõ&”Á…F^$©™’DÌ«²Ÿ¾Ç_¸ïVý>âs »×)®¿hñù>ƒ2§ïš²•ÔìpeÇ)œWºkº˜—^I“1¡ãÉL0 š[z÷Å#ók¶_ Üe¿‡Äæž“þ§„qÚY<½o®ë¤ƒ:%Ùõ±ÙAçu¸ýae:Œ}Õàg8†qÞ¸_ÖDC ´gΔEËBOßÞu…}{l_ÀßÏÿøÍ{ÉfÇáþਨ]äÒ´›¿ïÙšÇ_´|²ð2~üxÚð¶2è¿"P Ž"öÔYn(Ô™ÿ÷ ;Ù/:]2$Þn[î}á\F|?PŠqêLX³æÒn’öæéíÕÊtx۪΃¿¢éÇŠ¾2àë ßK¦NÙ`}>´ðu6y¡—¥a›ëŒ•Á¼ice]Žîðþï ¬~Éÿ÷%ü¹®¬ÀçF=ø²9Ön?)ë_+cötxRU¾²4^§½Ì›æv”gí£‹ÞF‘Q–gVGw$,o°yçÔ?“¬;ĽІÂù\µû$§uÚ¾Èñ’ýçß8¨›a¥ó›ý1OíÂZÅn ž óÂWÇ„wW’ôu%Ùg  [³ý _÷)‘°z¼Þø\¿'z•È#IØ]ïöçúgÀ²‚Â- »©à„»¡¦5Ì€§ÑW­MR’¿R‚Ë\ úo篬ÎÊê+zàs·jÛùøµß‰Db>x±GüZr~ò4–4Oñp§^!¾‡\•änØí^Ê…û d·Çû¡2 t½1svÇô ¯X†ôY0Ts­”¤÷šLä¸eó6«°õ'[—°ú±Þ7q_ ¿°}Œ|ï¦\½A§L÷ƒùèùôý¡Q³»•6º’”®š.Ó<$ì}°y†ßG½ò5;Vð}ïô½7Ò¿Co$®WW”ÿ÷¹ó ø~$¬o›ƒ!ß»ÍHÄì÷IâúŽ+h’oõصñ®=Qy(kÚËMG¯ßê%)f¼‹˜ â»â~% T1íñ*b¼FPs°&ßêùf†‰DQŽa1í“ô5³K*ê#)£}’ß•3–g{žaÈØ4¢’Ö”Ù%æ1ü“þºZÚ_7–šÒöäØ4(Ê’öØÍ£}’´O’ÝWLÖc—±»ä”ÝeFù4bÞµˆcÈxו(]ƒ²B³GPÃ{¢òPÖ”w͙ߕ‡²¦=QXŸÝDÚ+I&b~ͼævóú{®üž+ÿ~¹Òˆ~®bîû£½›¸*3ä{8‹¸5b¶5e¾jÿEÏK©ˆùê*FIiO§2Ê8üV_91ãP+êG.îƒi‡&HF™¡BQZÚß)\Ä8ä6FhoTñ¿è÷$EÃÄ¢Œ)ËKKYØ_óklE=å<) ›ñ ¹~äÞíyŽã3‰úÉI)»FÜ‹üŸôÔäz‘ÛYòý¡¸^ä2ÚOŽã3ÈP”í­YLYØ¡”…íðU?rÖg“ñk”_cI bæ«‘ˆåŘ¯œÉ=Qy(k4{$Ê ï*FI)óÕÍï*FI1 DŠzn&S&¶§ˆçõ5÷•«rÜ×ÿÝž›ßóäÿû<ùŸ˜#éß7¢=75´çf8íI.¥ މm‹5‘öN¦=7¿fذžäŒÿ‰2ÂìM{’s¬Cù7zÞy£ŠQRÊÄf¬C1ÃÆô«ž›É(cÚ“\Ì:Ìûªçæ·z’{£ŠQRÊ÷2þ… ‹2îÀó±ÅÜÃʱḇ¡´O;Ç=Œ@•q?©e”cÃñ±=9fåc‡‹¸‡bŽNÄ±áøØ2ʰsa¥"ÎãÂZ£™#QFhhoT1JŠÆŽE‹¸°R̃±(c4»U,bÙpŒl[Ú—™±¾ÄlX๰ß×ÿórâÿëFKúçÒqïg,ʨ7JC{ÈË)kGÌͶ¥ŒØ2”-àD”)bJ‡²1b(Êv,Ü1eŠƒ\Ò¡ì¾â!²¾ò*”%þpT%J†&Р¬Ð¨2”-í/Ïxˆ‰(S4‡¥CÙ¢IQ¦hJ‡²CÃ$£Ì(ó«¬ÏÏþš×ñQ¦h&9åg3¢)KÑžç}1ž…)šLaγ³í¾êK_†r@ã©P–h¾pT%J†&Ô ¬Ðˆ´7½ ¿íOϱ5”gá*FIѤá”íÃñ³#¨a={þݯ>‚š×“r~,ÑÄá”õcM™bF¬©ˆùűÆhr9J‹²E³'¢LÑð ”eG±¦h~J‡²Ã$ˆ2ÂDàM™?C[.â~}͉µžûùó=W* ¾+ÿWòäÿ­É}—‰(SrT1JŠ3UI™ˆ±t: T(3ÊÚàx±fßàþ˜âVˆx±‰(SÌ T1å"†¢´([܉(Sà ”eG9ÚŒ‹(æþXâÀGU¢dh Ê M ÿŠ‹Èqlщ(c4†¥EÙ¢AQ¦hJ‡²C³$£ÌþC;¥EÙ¢‰bQÆ£ƒ2mÑP‰(S3žÛ¡EÙ¢¹9ÖYžŸ-æ!þSÞÇC G•Qb$ª’²Ï"QFhLOÊüáÚÞǃ2´#DLD1ó‡ch3æÇÐö¤l13Ö¶/ÏDc¼X);eŒæ–£´([4y"Ê”òbmÑì‰(S4¼¥ñ~8~¶&€XMÌ‹­DÉ€çÅ~_G~Ï ƒ¿u¤5[ƽ_ŽÑˆ2Å*Gå¡,q *(HÌÔ¶£ìØJîãNF™á E•¡DìØPTÊvâ±wÐMiÿ6˜"º€L¡‰.JÀ4è¦  ºhA¡EtÑeª¨ÝtË º¨–£ ¦FtÙÆFÔCÀ@Ñÿ¿«{çZöB6翻ߗï,œóœì²›;–îû¾wfîø}€$ïo´‚ÜÒÚÏáÈù‰¼@ƒÀO ¸ZàÁH3ðÂ*$…Ø Éaé@…$±ÅÒšs<¹àFó–àÚY=Fœ÷ÑdH&ƒŸûÑdH,c9Þ‹¦’áï€ If,Ï;µ9¿‘È‘p&à$^P ùÂ…Ô'F"š…dÔ‚ üN Grê„"IÃ×çÕ6 ^mp‚`$®H¼:Á}¤@‡ þ#%’9<‹;VæçFcîØ $¹x€ Én2$¼¤µàŽ•!ù ¨Qì@ŠB \Hz-p’?\(Zd(Fà<â&à*Bq0E‘°>Ÿ„ôÕÈ¿2÷ñ¯}·ùWÏþ'jWDzÎwþÙ\çß]‡þÙ¼‡û¹@:w&7×Åí@íðruµ#(P7Â…‡¡$wî—ÛwG $êD¸ðpÔ'F0 ꄸ€AcRŽž;ï‹Àñrk%O#€LÀËÕRP ˜Â…€Ò'F`™5A\Ü n}„Z G0/Ð ø€h^ A &‚1\H-p‚`¦Hœ:àJ©H¨z äÎñ"PeÜY]j(UŠ@Õ7EÀJ¸ýîL.r8xyœäÈaHj¶ÈÜ&àyP ÐÃhð @ 7wþA¯nŠà·®k†žï8àû³JøÏ²ke¶Qʹöq{Å>ðžái;Ä~Õ}½ÐRC˜—®fƒAó‡Öo ×Ç6å«)ö_Šþàç©“¨¿ÿV‰qªõÒ¯]ìÝK ,îz-éMY:·pϣǢ lj=†‘WœÎ«Y$]hGLÛ“Eo6ë¯È{oo‰ýo˜/>“—ãðýöÑ7å4{’¾>EÂcœÙ•&æ³ÏGåOB¶Ôõ;Ù)’j¹64Eô”ù“˜÷›õÕf}m¹ëp݆'noíÓÊF¸ó´ÜÅïO‘_d?mv̳“€Ý-ÕÐÁ„ïïIs§ªz«}g¬Ÿ/ëkÁ~~îºf\wÚ–-÷Û¨Éì­soï)2|¥vLµ—Qä÷u§Û¾ DÊÕ<t/G$­žÒxË÷S(ó[±þå¬_?ŽàOÁu>àï§Í|–S$û›Ím§‹"L­ñUm ÉÙà7÷:§…v^{âø½ÛS)ë÷Íî;ÿ=Ü IŸ°éíÙB¼_Êëò}öSsç)*Ô8M|måŸ!îèQ=e‰¬Ò¤ÑßGYè˜y¶’ÔÓ(ë÷ôhøð{_? ñï#àò(³'>H)÷x?½ ÿÊøLwš´ÜZR™gÆÑ‹=¤àìe=[èöSK¢V˜F™§šõó`~:²¥×jSìÓÃújúâãH+õyªïs€LTƸ&#K}],<ÇÒ>ÏO;§¾êOrJʵ?d¡çLÕõê=Mô£°þ$¬Ÿéõ wƒ³­â×ÛwzÞiz€šÇ˜M^&‘÷ß™Œ‡É†‹8ï «.Ç umZ±rá4±ë÷Ê<èþý5¸nl!刢UÒþ7û׺Õä 1|ÛpQîäQê¥Å&†,R·ýn¡=jÓKCONýíìûaý£¯½;š´»Ç‹Ö'õMõÅ5Æ1>o³G1ã ]z˜l6õ ùõæám— ‡ÈØQ·žøb™Ý Ñô‹ -ôB¿íËלž&úçù¾H_ýT…°û{³Àw߸‡VÏäñ6cœ<%ë-œúÓAÚoÂËSéÒåT£ý ^$»ºö8 qN£îÿ:ÓBù¾š¾hÖߊó!{ÛEU÷S£Ìþ\ÿ§÷(rˆ~'ÕÔ&çY²þ«aŠ#’éw玜2ˆØ~iÕþà [¶³¦ñžib-V÷˜/„ù F߉©z÷p Ñë䋌³&÷ýªãŒ‡èwÇφüÐõ,é¸åØ‘´H¼6Ñò0d)s繦Ût õi5¥ÓÅÏÁúkñýEo„¼ß9úò7VwZˆñìË‹D2¢Wàªù%Sæ%RÞ{·m¯äb6Ëòjw5äáO¯w®…v{¼cã‹UÓÅûÂúvºrËۦė„ùÃX¿_>àú)‡çNwíf\÷ââ¤wÕQôåÈŸ#ËFy«d¿·Tšv£ûªmýÈ£'Ç<³Ð¸åSîÊ5SüXŸ\þóÿ!öûd¾Gÿ>™ŒÓãXÑ©ïÚÙéæÃɵê_;Gr<^ߦÝû½„nÞ×»V©þä»w5³y·[h²£Œµ{Ùÿà·åû>ù¶tb‡<“߉~”L¾kŒsbgóðäx;•WIêúê¹ú`lãŠÒ½¤Âƒ˜l¶Hªi8ª¯…n,^åhR×éÿÐטïCæ ÙüG‰]òè÷BÿÇj™¼hIåñrM“ h:²õ†Û×+Ÿ'¹sž[¾ø2°áÏÞÂ?‡-TÙ~U|ätñó°ºÆú;ñ^®BßÌ*™ü2ŒSÎ×è-šî[5Ì8¤ÿy’òC¥øè<»‰)pXžòW‘b?,Q¸kÆýaý§XÿiÖ÷•ݧ’ŠΚ-s}UbœqÛ'ØÞÿMs%•é–ßtž‚®í¥É;IbÊÓ¶¹“†mkƒÎüÑÁBw˜·¦õj— Âútùòã\ëò¡÷³Û±”{z•xû+i^¤ëöµã-¤â¸|Û¿ì;˜ô‰.^l‘…jvåXüóºY”Å“è +öä·Jn_} Hö(C8­|™8ºtú€'—+8¿Y‘,ì}ùì yôkë–'#ðsûÂÞ zîÙ|ŒyåX=Ù„….»—pºöœ0±¯0ë÷ÊçyrÈóÉ‹þÃ’¹o&_WçêîzGk׊§Cžl8Ö>粨جJ?†l ¿”˜Rh~úò;×Þu²Ø’öi9êg|ÖõSç¿¿—!þóB7®?@º|ý¤:ñtàê|«gÕ¼@’oæ³´Í:òýø¿o?<ÔàÚè~a¡¯ó ZsÊ ®SY¿PV‡™ÇƒÅ…¿7à:ÖoǶ~Õ¹J7W—Kôf°>º¾¼Á8¼ï$žú´E/’ ½LßÖX¿ŒoÔCòx¸Döä~ä« +«,´ÅÀ©oÆ_0ˆ~ ¶O öE÷}OXç}rÙ¾„/_0ÎÀ KW‡§³Zº†­¼HŽg[×bÉô9$8´f£•É}ÉùQ½,ø½Ÿ…0¯­/opý&oNÏùã‹xz¶Jõù»/“'ßmxDO÷.ý´ÚÉ¡äþ¦¼öß^EЗÍÒöõRgÌcØú„õ+gó6ŸôßHõ(›úDñ´A3¹¼uõˤe¡‘îwÛÃèÐŠÓæ®/?ŒüÔ=aõÔ/-´ZÀÉzZÏ,Êû諉ë:¾Þ\ áŸ;9 óɳç·/o0Î챺ûáÏâ(ï­¹LrÇ/~y{>—íçN?vJòqú”fz VÙ.,§2ÿ*û\̃øÝ×§ê“ˆŠ£|¿úËDšOªy´‚Ö¼XîyÝ»HŠdñ¢àMš~|wñ–2æ—,¾˜G€­W|yë&˜Þþ‡8:héÁû÷e2féYõ_®¤?EJŠýdéO¸nÏeª(X«ÌøÍaÿà#gû'ÌëàË\÷ȃ7Ò<ñ´šúö¨ÆÙ¯÷y¯œ[MÓ#J¾˜6¸?™Œ“ÇæÛ¬Îúïߘqý±8c±~íþÞÆI9ô©¬ÆyûuWÉè%-šÛo¥ã¦my²¬?q^ä·ce}`iFyÏL©,û—I⺑õß÷÷¸1ÎÃÁU5°n¨Ö¨Ê gÈUÂY”7Õ a§ê~çíG¢žT©Vꪅ.5ämkj&¾7`ýÀ™'†õgŸ'“oÔíQžW¤ý@<74ï„×\%åZd3Hp¿î+ÕwB¯¾D}oOLÁ+zÃoåŽÙa¢·ý“¯§¿…0?/[ùû½dçhуòߣ»ú½XrÎU’ï7ºûyš…ÚöMÜQøuoÒeyËãçv`þxjqGǾ01?Ù}bû¨Sæ.|þ¶_•¸îÐÕëÅ£?J9aúUòk`Á^ñ"iî/Ìýò¬T“ïÆÕÉuu¬…ö’ìÚ?÷pÆÏϞ̣Á<*¾¼ÀuùŸ'Ž6ùùòšÆ WI³×®»¦8Wmš©êE/X5W¨…òëþ0ÊúŸ³u«'¼?€÷$p]~¿%Ž>ïX»ÎšûWIV9›-ªºNÞ{ãÕ"½ÈÍë¢Ò[hKõÒJ»—†‰ýðÙü‰y²øøÏÅÇ?®»#2ºØ±q´EÚ¤«s_#óƒ§åsk;ݱ~ÄäÞýÕÄÖ4ÉBÝÔjïå3—i¼ŒOèÀuµ¤[«ù=âhNŸ€ó)xB/¶ÇJ%*oá¡;úïk½?RßÃÆã2Û8m˜è¹du_祉>Z¶¯ÄÞ#úÂã´_³RõºE¹É^zŸòéš0·óÑvÐo.Nié\П¼ú"àv‰ú˜W´µ–èÖ:L|ïËæì9Áüîþ}ûnz”zßI-{ó»õÀk$uö‘{®Å;i½§mª!UsŒ[X¡…®~Ú'OJ¥0Ñþ'æÕå?OÞL?¿ ׯýòTWƒ<ŽŽx¶«iØ5"‹y3Ùµe|áíÕgIƒÈÌfZýAËò¼™T$Œ²øcëQVßÖæ)¹¬ÅïÙEÿ‘¿OP‰q®îJð*ÆÑQÚ4kq”*¼dN‡_vÓnò|>Ló>ÿ£°7‚º;E=”~0ˆû“ÌwÌæ«ÌÞo™öu1ï銣÷,ÿáì52¼zíW[Þî¡cVí*øý’!äí¸oc[”CþîÍ{ypº²:u„ïy›²ŸÇ—g^å…•î>Š¥O[F®÷æ×ÐxuQƒ}Ôš·¸uÚâè+kþdˆ…noSøËbõ”ÍØü«oñÑ­îE‡¿WÒŒqN^ç^<ÅÒ°ò»ú6«â"oëÆ·¥žZ޹Z˜æ &í¶®íZ# ùüòÃØcsÃÄç6›ÿ2O8¿ÎÏo¸nûér¼Ù‡Ÿz®ËººHûºƒÒ69l´U¯ytW4䀴Lö¶Ù#éÔ·©ÛÈ›Œù[§0OÐogmq1»ø>Êÿ{rsãT¼pF¾&––ÎSßuyŠ‹ ~Yæç!d?õl™·ãblâ[.׈¤Ë¦”›k¶èµe~p¾î'†ôýmîDcÅü~î-r®ÔTûÇÙ±´ßÉNêoq‘^…+Fþ°o?-0tA‹b¡}HÔåæ UޤÉÛo× \&½7ãëè3ßue¸®Ý3tÇó™±ô·%òß u‘UŽ{¥›=@—Ö:ýK»¸^$Ïë|Íÿa¡3ò+=@FÙ|޽çb^þ¾ò×UâºIeão†Œ‹¥í ëû¹ê"šÏ´]Æ †­h[¹'‰Zlj-tòˆZ ƒÒ â{oösóñx7dYü¹ ëFð ®;|H¥{½ÛÅÒá³Ì×ê˃tȘ=wçÒdøÅmie-T‹YJÙQó6:ucpZ÷zâs?wÀûh̸~ÚäA#<¡ÁÏv5R*IG«âµÇ¤[k\k ÛÔƒÌïs)îö‘ô—j‘ç. =Rl‘ÿž<…¯Cü½k\_v)©þ‘k1ôö—ÏÆ·ìžH®½¼)+ú -ÚåÂÃbc{ˆ>¡]’†%zÌÏϰücu“ícøâ×ýQ[Öñ¡‰ÊšßŒK$ÇNªvŒ.{ˆ®¬s»Èá=ˆùdü«E#Åù ó‰²}b6_ã÷aîóñ|Û£ìM×…×:CÝqÁO®,K$΄Æow“~X½i/éyåÌc ½Ó±è‘-KÂÄý<æÕbóYþ|e¸n™2½*Ä^Œ¡wg=üØšH7»lþùä!ZeÛ·#FOü†´ÞØùÀ•»ZþtñC&C˜øÞšíç°}O~ÈçŸ×í»¾oùÝ'c(N!‘ ¾ªðT-~˜lû¶öôûÝHåÒS/xi|–aâûv|ýó„F—ë0u<¿þÔົ›†ÙºïŽ¡Wçèq=‘´+sé”dÀaj¿Ü5Û=¹½hqÄ8|¿ü{ό瑸¿á[7xBøçÜ»ö¼eó1_|cÕíÔ·óÆÐ"N§üæM$+¸×–› ó‹NdþÒ3ëÕ‰ó‘=Ø?™ýØñ©ï åKÏWùï˜1N~eÄ„ûchg߆p™ÿ`÷ØåWÓк»&ÛË´'>Mm÷H¿dpt䕌ýz>߯ˆû޾¸Æõ–lÍ’«z ͧ9íÈV3‰É&u(žûí›óê—-SÛ’ )½‹æ›I¯´8uçòyƒøý°u[Ÿó÷Sˆk\w)qûç‡Ñtþφ6IdI1S×WuÐîÕ·‡§önC/¹<­ÚöHÊÙ—ÌÔ æ;›—³õ9{¿ä‹ë;eD™†oÇÄD óô$²r>=³ÐÚ|ÛÈë]»µ!­F$¨)’NšrÛb lˆù§Ø96–ᄌÆu‡äk^°}x4ûÊ{£ý¤$òu¡ØöMGè"ë’Á=CÉ…åýÞ·|I¿uôÆÍ@™w‰?‡—¯û…0ï©ÿ¾”ׯþ³½T«‘ÑBýI"K§Ö\šƒ¡’ß¿ÛY¤=Îk)šg-·¹R‚Äšñs³ý>=!ì¼Õ³‰%Þ_*P1Ó:Fƒq$;ÛìË%¦|$Òíòºi¡Þ#ô^Uã‡%:‘;ɱ¹fܤC~<ê‡ÔŒÏÁâ™}ï|úÂ|Wì¹á‹wŒÃï£Ûi¡(AI¤Ép[çÆ ¢h©Ýí²\ҙ̨3¼D$U„¾>´è½²u‹¶.ïù. ÏŽž9ÅùŠ¿ÏÛŒq"¿˜0­Ý;u]ŒØ±ùl’°~Œ¢]SúaJ¨"¾Y©.’Î)¶½O¾ƒÊæwlŸší²u9[²ó¾<À8³K¯R\zES”a“ŸÞO"·Î´*Ü-*жÛüàΤöG")ga›·j–èõæŸùÄ}ý ³—?}Tái‹7ö~ß—çîuÈŒ(º×Тٌɤü÷šomrç[{¶-]ȉe'œY|5„É”ó—Í瓬î³zϿǞ‡ìçðåÉ]²2÷šûÝÚC+[s®t2é´#[£â_Û雞º»#¹_àåò¾+·QÝä«cbÓÅù»O¼×ìjÿ\È–É ,Ãõ]mš&ž[r„–­{·¥êëdÒ6¹ÅûFØ©¾Áú"¾ %vw-tÁ³6º¼rj¶™â<Ý>®n ñ@êLKœ”T2ó{c%Æ)H7ŒI,„n)_úàÃvɤ׶û¼kíô‚âä…oïµ$·zÏ^bÈ»>Ùx¦p]ƒè™cÏVOùóŸOC.*»à§·­É‰¦Õ¿;ѰŸ7ç›nUÌ m‡éŽWÕ^¹ú'“»svík§5÷®qOéLÈ£C_”{»pþ)cÝÈžC¼Ÿûº¸oÂÏ››vžÇ—7ÇÓÙö¡qÃôm“1Þ” ɤU±÷ù¿¿j§6?ü¶ iL¾Ljt.ûïÛhÕ¦ÚI)}Ã(›·²xæ×w¿ˆó+>óE_Þ`œÉE ¿oí>ú'ƒx^–¯açùüyÂê{åËŒsï|GÅÆ‘‡èžŸÞììgM&ã__™œ˜7šÎ.þUÇ“&‹Sw¦m£¯[Öë²·ª²ù›o°}}>°:ÃÇÿwcœÛ)Ë÷'½=H¿û¸»Gl2™œçÙÏ{ªDÓußõ,|êAS² DÉ.9Æn£B&—W5üÃ9Oþ½Á!?³‹Ï?g_â=ò”¥ý·N=(x-“ɦ¦“nM›hKU¼O”D¯ ¸Ùrá?ÖiבU&ÍÜPèUˆc×¢bI©Â|U!®Ï}ùƒqNp¯…SÐç/~ÛZþv2¹êú¾áÅ!Ñ´Ô@Ùž«µZÕ‡;=±[·‰ïõYœ±÷kÌÛõ¤×†>ÙÈë™#â&¬ ÞSUçóã ?RýŸê wçß}ۋϳ;÷ì#‹£imíɶЩ- ~vmQiï~cÃÄóØl]Ïæ­ìþ°s*þo ƉŠê»"pÒ~:çõšFý¿¼Nnš[ó¡hjò|ãרy¸mÕµö¨µ&š®ÍiFÙ:›ScuMýå“7f? a>Ho½ã<æ²ëÊ!+ß=« X`×¥ëÑtD‘AÚ’Ñ-Ijà„çGm§»Zîu½w)BØûQ6¯eïE˜_5Ó>*Æáo«FpÓ£Î×É©TÊC#Wؾ¯Òœ”Sâ~têÛ–ìi lËâš­ÛìU®{è È^%ÙQ3óù'Œ3#7ðn/MÝSãBïo¯“)?žž:­| íû,äþ–&MÉĺƒúýVÊZÚdeë,þ¹RJœ7ò>Åœ¤ž±ÐCoíÌl7Æ1/y2èÅ=´uÔ€Ksf_'óÆ^??¤Q Åâc¦±@²»Ì‰Fi+­p}è7%NÎϰ¸eq½]þtÖÖoBZ-\ÓtóšÚâù_þüæQþÀ½Vè¶›Ægûöèœ-×IZß{zwÄçñ­·k“œ¾ƒÓVZºwÂp:eŸƒy;kO0Ķ]žò~óAM³vÏÅù6«¾üÁ8cÕ%½] 좷7Æ“ó:ÉW#ùÖDu }X­]˺ Ë‘Uœ¶ü;+Ýýªëóê3Äó7ì¿/|Iˆ³?øu®; Zƒ{ï ©Í”/ïž½N¾¸Þ;ç´þ1´ZŸ/.äž]H+]‰Iµ)~&eç°Ù>ÛGeç¥}ùëþúlûÒ´ VÊûᯓ+Ò|#K`Ïγ¼¨ò$ºèÃító…ž•–ÏȘÇdÞwɲŠëŽòmo§üúè:ÙD¢nþ¤‰¡üþ_qòÛ˜kºç°Šû’lÝ˾w¶?>0Û“':óûxf\÷DàémýnFÒîØIÑ2ût÷ýK´X÷Ô둦6™dy}¾v{+å÷Í2ÖMl_Н«ÏBôß]¯ÞYJØzÆ?ÇqZß]í¼Ê"<¿SHíq;¥i3bè·‘–[}B›ïß­´ÜÐÞŠ'mÃÄ÷ÎlÞÃæì=:›û¿ßtcœ[¦–Å"hyR*zLûÒ£Ï×Gïšcè½}»MkAn¿¹Õmõ+¶tª±ÍdƒøÍùý©Óâ¾›g:?ëñ(ólÏYûuÞ­Töð‹]¹¥§ÕëTËq.†Üؽḇ6¤þfÉ­¦­ô—ç_Ÿí=KÜ'fû>ìü,®ñM;—æÿ|—aœÇ$-¹d¦5§nÏþhJ 8´±qëw1tÆ’Öl%ÍÛ…åT ³RÎR]Ì8S\o²:Åž»ìœ.;Wé?/VbœC»LOWŽØB}ǧiMô×·ÕŠ¥ÙÊ4Ÿ73[(™ô¶¸$ûP+]¹±ö‹™â|•ý“Í#êL|×ù‡§âïAøò×WqÇ2ko¢+Î;άO!¯‡-þ°¦o,µæL›ßÞ¶"çV7?üÓB+å_ëþá\ ›w3/;?*‹çz|y‚q|𨠠ôíbs•VÄÙÑW-†Xºì@âȽJñþŸur¿Ãæ5Äzšù½K.ÂÎç²y¹/o0Ná„JËšJ×Ñ7¹—çot$…ÜýÕý…›bi…*öwk¥Ád=Vc©7¬´äâmWþFÙ{ægû÷ì|0ÿüû:Ó¹PÆ™wa÷¨rO×ЇòŸ:u<…ŒŸX8ýðþXºU²#9±W-òfÕöVšëǸ"Í=¿lÏöøú•ƒT¨¹:ªÂÆÂ}«Äç Æ1Îã6ÎVÑòjî7]Rȃi?×ëKO$]¾HF&·kå-¾ƒþø¢o“®Ef‹u–å';wÎÞ³³ÏÃê/o­z¾aáýô›^!gž¹SÈšÔдÎ?Å õ1ˆÌhúGÊž;Äs;üºA&ÎOؼ;oÒôvUêf:G'Ãõß\q»ó}=ßãYéåSÈ…ØáE¿<K‹žmvêØô²dã÷š¯¿ƒ&ú^/Äï‹ÍsøçÄeqŸcšïLâ{#_¾`œ Íœyó÷^J‡½1Eõ{—BŠ×T&¸KG·SKo^•ô _¥ãsä?¾@½úçë8Ú>dR’ª(µ·Üîôö{ |^*2?Ñ`œ„)ÊUéh¤7g:%©¤Ð±ƒÎÜÄ}9Øbù£ßê“ÙK“oJî òÙÁ…ª(fˆóvþ—ý^__žŠó:ÿý&ÆYP®Ì1‡N¼?pÅé/R‰~ÈÆ’?¿Œ¥'6\¸7 ž%ÎØú‹Ýö{1l~åÿ9Üg—•»RnõÞ.•¬Û;¶m»Þq™"åî7#«gôÊñ¡ïºqdøOZe¼—bómVÿÙ¼‘½ÿõßGHó(Ù¹ýa_÷ï2¡*©=àÖÌI“âh³1{U}ƲØ8øi®ðÔPh@ì¡›q^ÏÖw¬þóçC_†°¼÷?ï(Ã8‹Ê7Zgï¡!ËwK[16•”R»Êl\G{^zÞêÚ‚­¿øóáÙ{Ïã¿®3àú–y¶> 4ú›6å_¶"•È‹lTÉGú4 m6_¿_»“ò—Íxž±ùF×Û«¾Uã¥x¾Íÿ9½'œC¸ßn+½6•Ô;19þ‡÷q4hÁý:†mõÉ’:¶e·줷¸ixÅYâûxG켦ÿ~­×ígªyæÚ0#ñ•‹M©äPG;w‰§üºâkñÛ—1EÂvÒ¯TÇÏØÏaïãÙ>𬨻 ßOJç÷qÝ߯mzÔôÉÒÏ÷‹t©dì{ý òxáþ5$O/ÙÒA;)=2óõ¯ëg‰ëvü{~g¦÷<Ê^Ó[Þ<8ÑDî^>¯8‰ëž¼2úyóx}Þ“…Õ›‘+½kÚ~iµSÜbŸŸ=ØûF¶ë‹w\—ÿÄìܺÀ¶Tr6{Ã-‡ÄÓˆ©'K”íÙ’´÷”ÜIûpÛNm ”½7fßóà’7CßL}R2ß7ÒNiÏÅsÄþç-•gwŒN·Š¤Ý¯¾`K*Ñ-Ù[z¤)ž¦´=•«æ¬väì¡wÕ«wÚIu)­¤gѳ—+ê[–¬$Îߨû¶O’y?RxN`œËáÒñí®¬!¾òŒû:àØ[݃SñôMõ’åŸvì,¼ÚI¥ÙŽÕœ%ž³`ç{ÙïM“iwNèç°ßž¸þÊ_.= 쵎4{|rö1s*¹~Á#9JG¼¼ÖT­"/OÇõ¸öãNúÎ\«›<ß,Ê~ÿ€í#±ýY>Ÿ„d>?,ìgaþòFrö÷VF¤’¾ý;ç1¶;J·¬™:¶Ù0áÏ³í¤¦öû#ÆZgQVOÙ<Š­ûÙþ9»ì÷£>÷úÜ{Èð÷ë= |ngßÍ 8BFÇ*°Z¡G­BpÀ°ÞÝV!ˆ?Ö'Í¿H¸ÜZ¿ÞÝsÀ|Êè¡HB"w Jè=Ä9²ŒYzÔ²^jnÁ±júÌÇÜÌAíúÔ2ÇçÊÊêYå\Y¬ÿQð }Õ<‚/Ë*$炱 }Gü{Ö„~ŒŠ,>j»ÐÇÛ(8³ÔÕ2z’…Þê,άyXÿ5gŒRè[Â%·8ÿ‚‡Z œ Éo<ÔZàÁ‚‡šy`œYú0gñ:àçZù¹Vþ~µR"|.w…~‘nÁ'hÌâäúyë„’Á‚ç€õ«å|Y’ÜﵤN,xV%BŸ6Ö¯öcžƒOù²<@…$°×cRðe©5ç‚1eé;Éz1y§`øŸx>æËbžU.™Ô~=½õñ r>§ŒwR›g–RèÏ”.8alB¿6ÎwÀùV¥YúP…žmÁYœ«¡w­IðÂh¨ ¼Y\ßZM'ÌÇz{³žN®,®εj$·¸þ‚kU\@‰ä· ®Up¥àZe®W–^mÊ,άÁ™ÅÅ'÷çcµòsüŸ¯“ÿWjä¢>J…¿—},í‚OОÅ%ÈzysAª<Š\|¯]Γü‰^Þþ=,‚çÀœ/£ÿîÇ<Ÿòd!è ÀT~»àÉ2 þiµÐÇÛßÿÂúxsAЧîS®ƒy²˜oU!ôòf.AÎA-A2逫\†ƒšK,­Ð›—seé„~ÞœOóP»…Þuœ÷À#ô²d.»Ð»N›Å»Êù²Ô‚‡šóÀ˜üúXr®,yõÇ\‚þ½¼%u2;ôBË`$³Hþ‰sÕŒ$— ÉuÀ‚‘ìf AÂëß*óHüZàhÊ÷ÁÓ}•õyþø÷«‹Ÿç|Ìp?·›»N“Ðoœó š²ø¥X½Ð›S)øXŸ_Γ%Ek#˜Í@‚€ÖP ¾UÎw ÷ëóû1ßÁ§æÉb¾U ×ßׯ¹á#>AΠüÔá‚++ gz˜«§ TðpÞÕ ,=>9¯ Gpø»W„ž¿áB’jAP¾¬ÁOíï†ÉÚçÜ ¤Hd=pgqpÎU+r.Aàþ ÎU=pƒP$¿Mp®ê„ ÎUæ<àzý*QÂAºà<ðwe9WÖ¿ê[ý\'ÿ³uò¿­FÊ€HZ¡:çÔG— ë….|«Á¹ø>Æœ+Kù‰>èf A ëתmÍ—Ñ×øc‡O¹²d|#Hj$€Cpe™µFèîï‚a=Ð9‡ 8ÿÄëð1_s­ }ЙGóQK‘Pzà.—ᣖ ¹tBïcΛ¥z¡s.AÎIíJÁñÀ9WCý¼0œOЂÏs®rî,à¤æœ0áÀ 4‚7K‘ÅGý1 t)Xœux'çZu%Ú ¤õÿÜ·ªD¢[É®n DÒ[‰¯œ«œ#ÂÆy"Pt È„~Êsg}žGþýëã㤭duòßY#?×ÇÏõÑ¿>r÷Ô‚8¿HÈÆ{u Að rNÁPÁ£Ã¹V]@‰@ |_¡X3 huÀ”¨‡V Eë×j(‚Ù&<ø?æÄø”ÿKŽ€7/Ð ð€Á.$çÏqfñçhSð€ëO<s‚1תÉîçäÔAH$ð”ËpPK‘XzàÜ`à|‚œ‡:„ î λªòsépNÁ ü ~îU.!µ‚‹šs阅äÔ'Îâ¡þ˜OÐ Bq£m  l®:¼{ƒó®ºA(Úö¼«¡HtB²ïj(’Þ‚øÁ»Êù7윃E@œ@Žb`ø„sìóüñs}4üýêc°0n:÷ý#8ÍÀ›÷šªv ãˆÀT\;Bðêߘ A¬nŠ`¶ ´x€Jð®ÊÜFàJ¹Hè:àÊ?ñq  NŒD0 ¾1à¤V")¬@‚ÄÐP"A¬B’pþD YtÀ”H+~Â7Ƽ«AH&H Á1äïQdŽ!æ§¶ ɦ6 F‚Ô‚¨à¡X³àSS!p­@ŠàÕ7EÛ@Ù<@…€–  µÀ ‚Øf ApëþÄ­¦@À‡ A¯NŒà7 >jp`$‚H :à<ŒFàÁH3 AtÀ”ñ­)‘4á‚_6Écös1rNj’ÉÒËe8©ƒXàükFàþŠwR{ ÉfN l Hð1: hN?Ϭɨ¼ÔJ$¥H˜:Î= ”YœÔs1z€ ‰k2$¯¸A°à¥õ’Ùîç¦Mÿ„ŸV…$·Ý(øiUHx;!é ¨‘üî € (PŒŸð¹±ó¹ŸçŸçÿ®Úø¯ÎC…kz¹ïi‚S ¬@Š Õs^IÔZ!põÀ ä`ðÙdf#HjÁU+G`›€„"À­@Š ×7E°[¯nŠÀ· ‚_\@‰$°)AÜ  aR$…¸A(’Ã$Åy¥H‘(zà¡HBÒè„"yl ÈÏU+C"é#¡ÂËföS*dø;Y†ƒÛ$H4 °)N'8*åHdR‘¸¹5%‚IŠ`Ò7TÈw9ʼåx?µ¬|†‹ÚŽ–q¿ï!8f­@ŠàÒ ®éP™ H‘¯zÎ= B³x¦=@…´‚ÐÒÁèrîÌ2÷®éðsÑz?á¢U#X@Îå§à¢U#p@.¸h½@ƒ•qç“‘—*5÷Û‡†Ïû^Ÿç-Ï}/µðïq©6 A€j€ !Pu (°áÀ 4Ü Gð€ (ÄFÎý΂ÙähðrÎlvP ¸Ã¨äÿ½÷€nêèúõMw¨¦›.ºè¢Û4¢›.º ÑE¡™. SL„"ªe‚©1Í#7˜"lÀF0¢‹.úÿwtf2ä{¿{ï»î{×ÖzV²²È[gï}æÌŒö|èàj|ðEЀ¨ü‘À ì ‰|‘ àj$E$ðEb€¨‘ ‘ÀI¢à‹d1P#i" ‰c FE’(Ø€Éd©@…¤2±ÄÒð“á¿É>»µ#’M,ÀIRÉìÀI \ÂwÞŒÀ[8s"/Ó@2$ip5’5ȰAÀ 4H\ #yƒ h‘ÄV @"Y2ë€ø"©õÀ Hn#Kp°ÝÈ’]lÀIod‰¯6 @0²" 6à‡b`bA " ƒX€"ß×1§XHºëâ¿kîò­Ú÷¿Z÷þÕ:÷O5Ïgþ•¹Ì¿cóªö˜¼þk­Ñ+° çz…õtÔ £Ž§°–Ž.CÝN Aݰ9‚ ¸€Áà‹`v€ ˆ¾Œ@`à‹ša*Ô 3ðA½v€z|H`¨à‹ 2çuTÀe€‹¾2pç+P tÀ 8# :#ðTÂy œ\øn.|tàjŸ/òØ@@Œ2cp ‚ÒäÌ`àZ¨(„s´Âú5‚Ô T#Ð"X­@€5² Õ+P x,€µÀ ÈY#{ˆë€È…3³ÂZ5‚<]ºƒÈçïÚòµö/ÏiðïŠ)Ø÷h=>>çr~cÎõåÏ(̹<Æ`¶ÿèËΫ}m½ˆÿÙÄþÝòÀ¡²×}75¤ùV©ßB7·P%CòT\’ã7å²(Ê}9‰-×ëuºî%Îkòí×àt˜äyßå#4 2POŸœ]è7ÓI?Ç6’íUbh­­¤ÈCÚÅDÑn=õ傈ØÎ0ŒÎßç¸kQ¹ðvRüôü¾ ›3ÈlÃã„ ¢¨rôúªÎ­È…ÒBÇÉ0ºlñ벚ß?÷¹ãýýÄ>ª•»=ß:Ôɳ¿­ ãÏQe£ªð2ìÈðÞ¿ü‘Aº¥<¯²µ¥~/çßÞÜÚŸøÍ¿whÂÂ0jY®y¿ûslÞχ÷ÿû å&ê÷¾“¯ô`}©Dϧ ã]nç6ÚIDÏw¹õbclÿŠ”v¼ÚzÂî¤Ê¡;–†‡Iý”y¿Þ×˰øÃ›Â¥*y_|±?Q7¸NVÙS_ìÓq L›ïb§]d‚½`rd]ÜŽ-ÝêRZ³fíì{óÖ% ['æ[w?ŒÊÝ0ï·Ç½Ê•ÙçsMé½cÇÈ~-ù8Dò: ã0N͸ÊOŸMÙMΩF¼·<ƒ”Þ°rÙ¬øÜ~^'õC âsö߯Êá´­±§—šûþ*Jþ'îiûêzޟʳ¿‰ ã¸u›BÈ×/Oü–AÎßþcü„Δ.Þ~çNïVµ‰Âðá’‡ˆ{,¸G’÷9ä}Ø<ûXpý'}Ö9j4š×™ž‰ë×Þ´óͰþ”Þ9ѿâé÷ü︾áuîÓššói=um¶Î5$_ Ø¿ç–Ôgïè¸)ºMªMxß~wÞ`gƒØvýh(ô¢øÎRË2H‰Z…¦oœDé¬WvW‹nN·LžãÕ#œ ]@×øýo@¸WNô®¹¤~¼/ž;_9T?Ä÷±Š #¿?8íÚ¯¤Úƒjwg®¤´ë™µ+ÕS‘|G w›Ö0\ò»õyÚ.s%‘Þ•÷Õý˜¯”ú6¯÷Œ=T[êGéÎŒcúÛÏ9æh8É3¨Îô‘ 2ÈÀwT…Sú~G±R1—[’ýË„FðáT–/±¢¾Š®Záx¾5U¤~™ññ¯>•ø!Iòh‰}jK~w¾`e¥ÝmŠœÜC† j_öÀü ’©}°ñ*¥¢ç©5YiH¼sþfe}’(ÿ=x?-~ÿ}¯>!ŽŸ”üsôüÜ´ç͵-²ýé{I÷“#zã÷É6;[žÙ,tßæ…1ïrµ#E7(±-5Lú}xGÞ÷’÷ç>Þ7÷Çrç Æy5Wè8ÿ'éâ~fc´êN¾ºtùÁß»oK†˹bâ“0ºÈáãWD÷¹Ï9ïçÈýἯ“X‡äR½wç Æù(¿®E•r^è~cÈ å´›þL¯m¡×ËyÏ›š¯5‘ ºR/µ@8Ýš³É]ûƒäá}öxßQž—9Ö>}Ò°ní¬ý61Ιæ‹ôŠ ÇžÇM>‚qZ¯:4­ ±ÐÛ9žêŸînIú½µåzY'œ^ºžwHãuÊ}çbÿÁ†¬_Ï ¥èãð!¼Ÿ—gJ;Ɖêz?!©ë~2sEÃ.©sñ¹µ´øÈ»[è°½› -ÛŠä¬ tl§óg âûÖ'SfòflHþx¾9´âñ÷ʘHËR×ó"RpçÍc‡ªäüýQóÏì'WŠYÖž™ALˆøc˜…Ö(’ÝlôoM.ßYc0þN|;øÏ¶ù ”×AÞg÷ÿ•wÉøéa÷¢„÷SËâ©Ç8 uö.Škq€\ëúSå“2ÈÑ]3¯´f¡bŸÑ6$ÿ”Gù¿†Ó=þ*Eûó¤>S-¶¬«y¶²Ôo«ð»Ðu¥®‰}"U¸îå‰ÕvçÛ€ì¼âŸ{x¹¥xqk©…ýœmÉêIˇ¯P‡Ó×§g¥œýeå÷™ßwÞ§ÿzIAl,zx´¸n›®ƒ/^©}˜Ë}ܸ§{YõâÕÝíZ8]órPÅ)ϧºÛ‰(÷ò¾¼¾{ú} ¸î/íË´ßÌY½»eâ¤Qçñ¡ OXh`ÈÓèŠy;“ó•ŠýÔòR][»ú¶¿rRU©¯¥ØO>SùKüz²v‰èW2ẻ,Ù ô‡H¡½[ |¾Ž®»j¡øí­Z·;y¸Ý1EtíÖ"É~äˆAò² ]/÷.Þ7JìG)~¾\÷emï;sÊ&¿ø£TñùÔûar¶hª´'ü¾e\/šQ:µ&®»yËôa-‡|öOðþuÜç Ö¿l’ÿÙ³ÿ¹ã,é{¶AÜabŠïQeðäÄ‹;Ç;Ö‰¦UW÷l›Ü‡¸õ„çÂhÔ­~†¹ó¤yŸßq߬X—Þ)y?5wœ?ÁçUûQò€#ä±·ÐX6l‹ß›žØ?šºŸB޾déʪ“k£þÜ]Ú£UDØ\Êûîñ:Æû²qÿ;®qÝÜE盚y„¸?Vg:ÙP¤Ã‹K£ié›Zz[û’JãgîªN÷Íè=ê°…û« Hýºy_W±O"óÐếDþüê‚î/’ó–=©þÝt¢û;¦Ú•£Ñtƒ\0²õ&åÜ•pê×I0îÍ£üsàó;^/ÅyóẢgý/’§ÕÃ×ÓI×´6Ñwÿަ=Žu»wüH23vÎíË;Ãé“ÞµŠÎ 'õÍçýlŸ¾®û÷‰ñgÀuûlNÉ>«s$Qº/N~xö¡|îœ1ô©2uò­êÝHØÐš~9NK.\<úÆõyÒ<’Ï[y_C>ž;®qÝöžg›!’ -[pXR:ñmR,ÈQ1†ÌÝ÷ãõdw¡„K}yP>ßírsÓ¸AŠçì¹^BòöòqÝqŽqB*$·Í;â(ñ0¹ý§„t2üñáwþ1ôö„±#ƒ¢ð<~䯟V…Ó¢Z¡sÜç~uâ¼®>ç?¤ŸŸ÷Cç}­ÝqŽq–Ån|>Û1’t³âò­§/]Û9Kw‹¡³çövdUÅÓ—¢ÞоŒÏueÁž>ƒ¢îº=kÉÊn“nÎn[$ËóÂËéP5 »4OÈ1²®Åã.D§“^#×.CƇç©þ!€ˆ¾«pšÙ¯C­¨mŸû&ò¾Æâ<"NêgËë|ÿ"Æû'‚…±Íñt’cÄŸ£†bh§#Åûü:¤i5½Ã¦âÝéØõ³…ÏoyÝåól±Ÿ©"«ãÔrzºñ89]W˜¡¤“;—†ý”{S wâýó´Ý]H‡£Ójáôöó¥!åGÿWo/¯—Ïç_šô J6²>G‹ä#CÚÚCÆ/ßìýZŒS\о8NÞí›|iä¦t"«×¿Iã1ô÷ÂäpÄ@5iê?;iIÎpúƧ×oƒ (÷Óðþɼ_££ìúsù§$…"ÿ4ÎèÊê“ŘӵzÚýŸ;ž A¯ö/¸±(šVo“Ïúbøßø’=ÈäkQí?¼ £µ ¼7/ú0Wšoñç ï7¹dÇ»TÉᛜ;:’ EkœÏœä'æ ÆùëѬäÁûNöwª=!ØrŒöQ䌥â< IÞtšó}Íó(åtës¤þ–)ùýãùê0Î⛕‡v¬r’Üi·òð¢ÈÏeŒ×å±´G©Ãû­#zz ^S(œ¦W»^ëyŸ9ŸûÔ³ñøû8ïŸíÎ!Ž{¿¶f†$ó+ÏÏýG³trÛ~çBçXÚ¢úÐ#?lêNžŸKòº\8-X»½Uµeå~§ÜK˜79OÓ¨?Dß›×S‡*VÐó‰"Ÿ4AçWN's'Ï{91–.^ÿÔK»HMF|9ô˜O8Í{´ÇÅêÏý¾¿ô*Šý<_ŠÏ\7~’=åçQQ¤ˆ+öâ¼Bé¤ Y¼Å?8–¨;ÊëhÁndz±»æ·Âètfù±Ëséá‚Y¹®äàWþy»ã×;D0™D‘½bŠÎüpì–ã¸z_,½™mŸÝÚ¥+‰ÊøðtÐáÏýD[FfÏûòEE)Ÿ¹Vüû¥>£âóFô}j1N¯&{Bkç¥DìS}Œ¨ºé\Ý ±ÔodÚjKfgÒÕ=Q£G»ß»-y®äíáÏI½z¿laÂ=å_+ªÉWD&|ê[Î?©F¿œãÜÿóá‚èÆ”Dž-êÝ<þ:¹¡¯p´ñ½XújwÖåóu"W¶çÒýt>Œ6,õû݆¦¹Òú Ÿ/ð÷ >oæ^qwÜãúgöUºðWJŽŒZ—Óz´Œín˜=Žzu,{'Rš:;®ºFï<=ôÞÒ{.{ÿÏ#Å5ï<Ðkê (u>Âßw=û“[0ÎÖßOçïuŠ’YÑ!þ3V^'wÝ·1Ž.&fâÖ^£iï6hüÃ\æÏÈ.ùD?ƒû½ªgñ2ØqýÁC¢?òXȘ®ƒ†gŸ{2Ю&eÝqÃ{Ó‘àîdË  >§ÃhÝÒ Êï*2—='r>Ïâï1âï÷ZòÎxö¥÷zæPé÷oÏ}Ia!?ÜLYçƒSCæäX]¦íå0Z¾ÉÊ ùÌeï³9 ÷qHÍV¦{×ü\JO§ ×wkè·ZHø¬á“S~¼Nô9»þdµÆÑQ±êA=ã†õ¥šœ £ãrþ=õÂ9R_SWÜ#z}Þ(ùüÄó=V‹q¬Â]¿h!Ûå¶êϪ\'k¬/šsÄÓѦÖ5/D2sdßÜá0ú®Ò¶:w{Ì–ú…óúÁãWü|\Jî5ò|¾0Žð”*–3š¸y¯“Q2úÞiOÏÌkººjÁäš«èÚcÃèí´;?U¸<‹r_wáq<²lZ§¦Pö¸rõ]¯â¢?É„ëŸínëG“•„'Ò5RªeÔXC<-~ø~ ù»¤Éä€1Û†‡Ñ~ïš_ÛÑt¶Ôš÷µæýlÅþê”|ÍsÝÇ‚qÚ˜{ÌÓ?šO™Œÿõ™Yø¯^OŽÅÓ$EÅeiM·Õ¨¤ÝaÔ˜»À‡¢³¥ù÷p/÷sƒ§/ÂŽq†7›23ã·hRDúÜ|ô˜ÛãÑ¥§ñ´M«]?YŠ÷'ùÜ OáôY•{Ï4%y§øüŒÏ—¹_–÷÷÷ôu{=w¨ä›ÒûW‹&•k×ü{ʪkd|˃GT>Eç&ë¯y§ô%ÂäO1?œ–[Õ_¾aÓLÊ×wùû&÷ñ÷þ>êéë–aœ¾÷7+ó,šä/¿{Îäy×ȇÊåê=VŸ¢o—VQ,ëMNîY’+j{85lR iTkåžMžß÷/>Üjû}¥X—ß(…nÙÍšû³¾ÇÕżÁ8%‡'<›Z*†ŒÙ~±}©I×H‰Ú3;w˜uŠ¦Ï¹°$²yš\¹À„±áôE‰BOü#fKïâ<Λåå¥ÔŸ>Ï…Rݯ6—êš;o0ÎÏÂkxÃ2ëDpÁBC®‘Kwû}Ú¼ã”仺Зp}®—üùÂûµÿ<¿øÓ»UíJñ÷zËæ§wÞ`œRSZ9LbÈ’ŽçÝÛ]#/;<ŽXrîÞ·»û-z“÷ϼß11Œî±ÿUñÔØ¹Ò{$/ã:Iòóõ O?  ãÜîÔxõÄâô,WkÄg`¸q—ëí°hz±VCûÕONœý(”vúÑ4îlñ¹’o”¿§‰?w’äçáïù¼N¸óãrO€bˆ—ÓR×ÈÕ¶‹§å¬všZç]O;p¥)Ù±ZÈ´E¡´}‹6;šÍ‘<¡ü=\ôyÜUŠëôo•ü}Âó=ËŽq’–·½”v&†4J#Ýrä¾Fê /ýlJÏÓôâ´æû[ŒîKtkê~èÔ#”Öýýl—ä¤Ù’?€÷¡æ¾W1¾Åy…× ‡êÀ1!ñcÙ»4z~•LŸÕEþ…§iwíîU»3û’}÷‘|(Uè*fÛ‘0Kª3¼µø¹äbóù÷Jî=ð¬Ë2Œó´}÷Q+Æ’ó¶O¯i¿J"j/>×éÐiÚþU˜y4äh© –»‹Cië~®·ý×Ï”ê%ßïIyß*mWß’ÇÚ¸nŸMcêœkK¨ÏÏŸN_%yúÌ,2çÖiZÑècéK†¹hCéÙ±U§¬›!ýüü=†¿¯ñ>÷Ü·áÎ \_¨êwÇë '÷]%®ý×õ*b¥­âJ67°/i±É2¥ÿ_¡tq7¡²Î Ü_Çë,΋ÏÝJþÞ+ög=ߌC…6÷“b‰è‡¸JmnÕ³LK+ÔÖ÷å'm²¶Îªi;V„Ò‚ÇŸø–7CòØñxâu˜NÜKá9o1aœéOç^ŸKÜZ7ýU2ûê£g¥óó¨0.o"îS„ÒܶöI¬0Sò6ðzÌû˜óú(æe," Æ Ú–r¦ÔÒXòöYö‡;z^%}îV›µÖJ÷¶Øîº«éE&Ö:Ù¤L­P:ìqÿVJÇ )^ù{9÷|‹ëŠ?MÙ ïÇTHëÍî¼À8M¯Í·6–¼|w÷y¿«¤ÿßW7FYéÑq¥Š§WèEì©BÁ ¥ò6–нBgHïQü}MôKÞRêýZø÷Ú\ˆy5êeÙçñzéP]tEEöÚKÆ Ú‹ WÉ¥9»Œò»VÚÍñ²úÓ£=ÉÛ‹+Ìý9Ä8RHûVbý½£¼°ÿçò9/bù]-‹‡]†qÄëÅ’øåÎW²œˆçس&›*Í÷ùó…ûaø9ƒÂ·'«'Q…qD~²NqÄ|xIãʧ‘ÑuçïÊY0^T!¢ä¾AÄ´«Vj»CéÊB3›„+§S¾¿Â×=ÄyËå’ã)–MpI^5O°ãì8—{ÃðÛq¤ã`oÿ'ŸRÉÚ²ÑW*$Єâþ[×@R"n}Ð+”†;’›§Iþ>Ÿàë*â<æ£4?òœ'0ŽÂy}V$Æ™í°¦’8m¿¸|sè¹aò«­ýˆè ¥/,iqhÅT)x¾ò÷=q}°ó®ÔϲkÂ8]¢ÒPIâˆ{çp*¹jRÝëýgf»öûàºâ¨S>c²6”FöÍÜ{¬àTiž×Ñ{uC¿¶`ý£{ iKîï¿·AϨã”=¿ríàÜñäb§øS'~O%Ëöwé’ž@?Y통/yT°Fî€F¡Ì9òç0»UOöîÈ{[)Îc?ïÇgÙ'Ç8‡¬Ó¢¶–Ž'5¯VÒ¥’жãËæ?Gçê²bÀ£¾$ªäàC§ó…Re«óÁ5—¢0O[q¾wE)>ÿ?2nmâù¾ìõÚ¡zѳlžAuâÉÆÃÅ‚_‘TârÕhØñÇsÌ ¨!)ção¼ÊJÏÞ&zy@iŸ”×Q¾²åE©pùÑâ:®úê^à9>§cíæ—O%¡jA0}Žn}Ùìù‰òÒ0ú¦P(×u§Ó¬Þ›RÒ¾µ8ÿðbãÕʲÿªÂ8îY:žl>ºø—'®y6ÐÍ[ÏѪ£›{@C>U í\?O(ýÐÀ·r‘_§Içzx<‹óŒ‹Ê ßSÎ9¹ ?àù¼ÔbŸ*OÊžÅ8b_!æDGŽHhÄøÉ’w›×)¾¿ Æ=[ÿu9Tý_tÍuýy©¾pÇíÿ+ä}Ý#ý'?O;7 ˆîiíMÒ*'çÝn¦5Ü¢¾ÉÒ¾ßOྟJŽo“i¯+Ãu÷/ôãHMû–»ÝÊ\!…K už\=Ò8oC/ÒqÆÞ_¯2Ó÷¥;,ÐçšÂÖ+±çšB:ƽ˜î8Çuó¾Xòc‡cqäÇ®‰+|¼LÊØÓ«ž+t)x®Ì¤"½HãvÌc˜é±ú-†,É3…òuW¾Þ#Î×o+£Â »dÏ&ƵpÝ“«(§Ç‘Ážj—¥_&âzÌ:°ü©Oy{‘ŠgödÜœf¦G?Üêÿ`²”ÿüüÈð2;fŸñ@Yü¼4?Î&íð÷[w|cœ^wfµ«Ô(Žì ¼¿2÷_—ɱÅ7ÌúÁh3güÏ»‡÷"‰F—ÐM7ÓŠûò¬þûÖdÉkÍß—Å:öXÉ糞ï5&\¿EÕ¥¯zÅ‘A¿¼î29rTø .P»9Á7%µ¹¥Ïì;Ù`¦ûªuë×åÊdi_—ÿS7:Ýlyð9¾IŸ¡[z“jeKÅuÅ} É”ÿ\ü½‰û¶ø¹"w\ãºéƒ[`ŠK>Ì<=¸ÿe’Ðc›¾vîç¼Ê «÷%iÆ+…3Ó‰î€ù™òõþ¾ÎãšÏ/ÝqýÆ¡Y¼IŽÁ±dBÈÞÂÑ~—Iû×'×ç/|‘ «èËû“–O‡Ø ?o³=ú„ö?Këüù#λn²}]Ñ&Ãu½sû(×èb‰»Ü»LôaŸNVíx‘ÖJÜñ¼ÖÝä±cÞÑuÌT°§~<4IòDóÏWÜßÉ”ÎÓ¸ã×Mžwù½«S,)î9¦©ë+ýeÑE¶¯1ˆÝË>¼Œ™n¨µpÌ¢“¤sü}@œ7þ­÷7rHç.<Ïkj1N¾üýÒïûÇ’ÛucŠÝIJ!–µS½ã.Ò1KœK›û"Cð–“VÁLÝZÆÉz©¾ò÷qÞ.íy®kpýãÃöw©Y:–Ô¾¬_9ëX iytüñ¹ÒýðÛñdd:)±½µ™æêV±ïý~zÊ?oþ¼ãÏÑ5Þ[–7"~>&\÷ùÏ©sN;cÈ\s£¡#·¦Ôî˦7)m£ÅÛ¬ž™KÓŸœÉ)T’ÏŸ_áqÈß×=×3,¸nX!áC|gÆ9ŽþšB¬ºK§4´ÑA½Z¾k¿¶/;×g¦u›Ç‘q÷&Iëäü\÷Šóñºv\÷Ü“Fž‰!ÃÝÙ2Ûº«ù†6ºíþ¶Óþïz‘{Ï2õ{_q_ë$iãËs|ŸÖ×oªgyÛt;»3†¤8 K!kÖ„íØ0ÐFg·]¬iOò~bÕæM²‡ÒŸ6_§—æÅÜ.^÷¡²ð“'•²Çåö¿øù wœcœä|Êóg–ÅM‹^úU“BŠ¿ú¨ž©·Ñæ7ºý¶~B2¤é¸ŸÖ¤™©~À©.M”æù|ŠûÃù}äçÆ<= *Œ#ÞŸ²wq½¢mRH¯ØÊëüØh©7Cj4}Õœ½qrÇ©_p_Oâï‰Ò:5ÿ½ø~©8Þ ¥§ÿQ‹ë«½â­«ücHèI¥š6N!¿HXhžüaÊõ+º“1¨¶õÍ´eí?ü«ÖS¾/ÄçE|B\·x®äëñîxÇõ·™Ší½ž/†ÙÙã¡®l ñùkËì”]6ZUžÖ¶öÉîd´{ÃL›~âÌÄI’÷‘?7x¼‹yüT:ÿ˜ÅS‡qΖZÚ<ãv4iV¾¡ºÔ§d6_±Qm·_—¨ô ¯ïœ›ö«ÒLkNlïW'p’´OË×#ùwO^>k£Ñ‹Ö\=µ' Ïs®õîÙfêÚeÓõ'zi“õ½ëo¶otOi¯uìåÆòmÈ•´ày#}ê‰óŒsëíô9™{¢Iíùkð'“¿µÖ82l´kûŸœt=Éñj;çÞlfç,õ”¯ñu/ñü«C¹fÔ ‚ÇŸH>cwž¼s¨ÜÛÆh½Rµ°Ëæd²>qnÿwOmÔ¥Ér.éAòµ«:5ûF3ÏÅO>/þ}ô”üŸ³ŠýÙþæÌ6,îÄsÜ2Œ“¹§›µeÑä^¾Šw?ÌK&ʇ¿f?’#‘–š­Îý¡ywÒpl Ù£yfÚ¹Äë  Ž—ž|_<—~]Ú¯:î)ógòûþ¢ƒô?/ÆI}:XæM|Ï ®8yD2ùóIlËVÅi|þÖG]ëFút^ax?3="üô^ã¤õJî«å>cî»äûíYÖ_1N¹»MnÚM6Eߨ7²S2)Õ³wQ•éƒI7ÆlŽìLjx—¿‘ÑØL§üõ`Wìã¤:Ë÷Ïùz%÷çòç‡çº•ãœ3.h|aw4YÖu]ÿ'M“‰úäÕW+i¡.vÌ<ÕTŠî…ÎLç (SóÔ›±”^|”¿wós&|=Î/¸~ìKù€1ÑdóɆWê$“’e‹-œÖ"‘úΪ¥[ўدù¥–™ÚG>:$hœ4_áûß|"bÀÙ^e‡¾VŠç£*gyo´`ÿ‡QÃF÷ð>ÉdÀo;&$âyº6Ñz·-¹7[8‘ofûàã¥zÌï ÷e‹ó¢ìì½¢JÖs¯§Ðã WÇ—Œ!3?t˜ý8g2Y}òIŽ=é‰é OeoKžŸYzó¾—™ «ù¥·M”æéü<°_7•i%öÅþ=?;ɺÿ-Îó¼Þ;T zVÈ@½<öðx%×%Òd÷¤£ù%Ò°‰.?kCܯG—Bè1Ëý#‡ë¥s&ý™qv̱-ºD:kÙß+BÛ‘GI‹b“,!ô|‹*³ŸLÔK÷Ï3ø}ç©”ü=VŸÍ§0Ž»<©cˆxnüù±ËÈ{§$Ò–k’ÿüs_G’tëu“i!4‡{á}¢tÿ¹w™Ïø¼žç w¾`œÚO jô!Kj+l—HÁ¡Ë&.L¤ blí &¯ûÛf ó6ÓAG3šô¬8AÚgáu™Ÿàçòø: Ÿ¸óãÌy;sGŸÒÏ-¾DúöPvm"}ÖðöïÆö$‘ý«×=xnûøšcÃÇIÏGüü-Oãë>žñlÂ8Âi,UçRvÏÊÓ.‘ÎF/ÂBibÍ{%¯õïMš–ê½hRSäÿ aëÍc$_/Ÿ×ì_,1ï¨ìlžèTòógYÞ—1ÎúOÁsß5‹!Ÿ~¨Û'Y{‰_ÿî¶Ü’H{vSì]ïìM¶Ï[°¬8êÌ›+Ïξ<2Z:_È×ø<†Ÿí•}h‘º·ëgݯÀ89HØZ=†ŒíÐÞߨåY?@¶)ìr"Ýt`É‚‹ñ½ÉŒìCB4§èØKµFŒ¦|°ËÉ …}Ô’žÏw;ÏûcLRv"zlëgY÷úàP5ßýøl]ß™ûHÇ¿KÄKø¬ÍƒDzY°üM/RwJÕì)ÄÌÞËÇPþþœÕgoaÜ·ÊO3……‘ºY¾Ÿ Ã8’m¹µ9bHÃ놋…*_"ãÞ~ØÓúS"–û½íø¨ž¤[ö?&;;›éý³£f”¿>^:?ÀÿÉ÷õù9þéo*Œãl2qS±ÌhÒRø:O¾Kä”ð5"It`Béð…?w'ÇtÖÀ51ŽÛlÑKqÀëŸ7åÝYA§%Ÿ”üùíùý-Æ÷ £‰ªA¹f-ž$‘e¡/Š^xœIbsÝÏHîÞ”*[ußÌÏ’SÊ-ßc÷¹«ñœ|iœ?Äu±ÊYÎõ0ŽâØ ÿáÑäAù´/&‘àå ÓÏw3‰eñ€c«7t&‡&ÅõŸ×LG=÷ËŸ¹OOÅûP“Ü, ¼xÕ’ê§xÿÞŠëJ¸n cý\c7E“2ÃW­<‘DÆÍ:^ÕßžIvÛoôQØ;‘[až,µ‡ÐŽC;}Wv"åï9ü|_¯âëa|ßÛsÑ‚qŽßÏöÏÿ?7Þ š½>‰l´»„WZ&©\\XíLâPŒQ!ÔÙÞ2ïiÏÇùypþ}¾/ãÎ\úÕÒ…—/Œ&Gc«5˜:T»¤ÝË35šäêþ:[Àø$rüì¼G¹2É¡Ksþ¹¨©2äF×J1!ô‡ïÊêû¹óxäu‹ŸÇwç®ë~ ÐcþðaÄëÛC’ˆºî•‡«b2Éà`åëzý»“%ùT;¦Ü ¡Ë;õ¾úwé‰Òûÿ¾ÿÞ”;Þq½¿–¯?sB4Q¸Ü%‘•ƒoç]s4“”Òœ~ÚzWw’"h²‹›Ùù² Ò<Ž×ñü) ýâ7Üaõ5‡߸îË£W—~M”w'\éÔ3‰<^û"2É¿®ä>ø9÷»¢ó7SKÎ éç{Ž—æm⺨CÉ÷ûÝqŒë ~$¿Ð½}4‰Éˆ ÞÔ9‰ žP¡œ9“´?¬ü#_‘îdÈÎ’‡ßõFÝ è8Ó·ÿxéùÌ×ÅyÍ}å­E÷×1çübýZ\'0aœ©î€hÒ`_å‚iª$’<OHŸ­™¤mNæ_›ªÉÇÁSoŒ2³sã¥ïOð犘™RÝÄKë¢&^e²¬X0ΚFó:½d!/Ì.Z¹fÙWxzøæõ™d{ÁEkGÜêL~:<¶Uÿafz»Á„1¦7ã¤sÆü{ü½F\'ù¨üÚïcÇ8–žn{8ÈBòñæXþ$ÒMÐte&1ºs¯H—Î%‡¾lo¦¦¥eþ2n+ËåÏG^7y|óyç{³×'‡êå÷OÙ[ZHƒŠ™«Â%’r?¬XÙ~Y&ñ›ÛeÖ“bíÉϱØjxß|î?ýø 1”{ãùù{~žVœŸ=RòýlÏù³ ãär£dlþf§Øɓ³ìù5“LÌÿøäŸm‰ø:n¦=[l˜÷¢Ëé,?Åϵ?®¿ýcÜ3V×jf9w¥Â8® §P’ñî÷›ûIö´vëW2I¯÷¿h¶kZñ¶ši‘¢ÉÎ:{ÆR>Oâûz¼n>5jñŸoŸIÏ5Ïy¡ã\*>®ò–‡Qdý­âA[Ö$’’ÙÏ:ïÍÊ$Sr.Ý’ˆ·ÃL#dç56—Ö_Å}‚JÒú+_ß× êÏï½0N«j¥üóÍŠ"‰3~¸}f"Ù§-ótøtÔ?ïw8¢©w…"¿™.½³®Oë¿'Hëüÿ~¯x®7áïž÷Ç„qÊ…¯Ùr¯j¹Ñ $aèÐDrpç*çäŸ3Iñ×q7þèÔŽokЧ!´bö‚Û:žŸ “áç ø¹¸ÁõÓêV×,¸î¹§¢†ß:I*’2G'tL$ûþzaì¸L2teñ¡¡Ø÷BBh€áÓ#ÇS~žÇ_/ñܰãºî²rú$ÉñÀo®¹Q"±mzYãôðL²yïyŸÔL5Ñ_A™Cè„ ¿çf“¾_Íçu|_‡Ç“;¼î©žä¾wæ$Y6écµb•IáÌDÇèŸ2IE’ž÷$üï͹56„¦]xÝóÖ®±Òz ÿëwþ}f–uc®;¦~ ñÕù“$Lx½ôI$g¿/S¨{&yÕüh§ÅÏ{¹{bBw»ÐðUÕ±Ò¾-ŸñõbÏóð*\WÛot¼ÇNgTÍ’5?ÙH±;ÓIÑv™¤’nÒØé‡úi¾«?ÊB›c¶ÞäqÈïN~ïô½7’Áë?¯7’û½m^¢›ÐÄz# ΄TÖóÖÄ‚U—3«×Ú›õOeîVs·ª™OFfÞÛDÆÜ 6Ö×ÑàÑÜü•žàž}mÌmm`½oyŸ޳ͳ÷íçPðcnÂ/{‚s7¡à¶Ž2$Pp–Ý„ÞHܽù¾·B/”`æ“Ñ2w«‚y·w‚¦Êg§µà$´±ž)¡Px8 Õ¬7œ˜žŽÌ'#g½T<{ßzÅ¿õ5‡+ïdc½o,±uÀüà¦ðZë€õ. bþ-ë þ5·µ9e"˜Ç•û¬·£¹ ¿×ÊïµÒàõŸW+½Ùï•ê%ú·„¸>¬?¸ÐïIÅ®ÞVýWÖÿÖÎ…fÄæN®Z`e}  ÌS¨`þÞ7â+=p=ûÈ¥2k°ƒ$A„GO(Ïþ–ÿ]Ÿpóo}Ù—û·«È‘@ÁÀUFôoY=\×Ü5cùJ?L+PT®^¬‡œà(ôcž¡G¸ÖÃßÈü­ $aÐc øyx·4¬ß”Ð#\ëÑCÎÀÜ ‚Ã5è‹^š>_qÍ|ÍUÈ}ׂkF…¤6o$¶¤ÜüW=H~¬·¦àšœ×Áßð¸:˜?!’¹ y¯p?ÖGÎñ/ú·þÓëäÿ«5Rëõ½>~Y}Ø÷fþ­Ôl¢ãZè®gîV?§ÉÃÝÊý[fæ¶æNBëggþVÞ_Sü„ÞùÄy‘¬ß/ï“çÙï÷Ë>y‚?Aû•›ª/¼2ÜKèý/¡Ð\Ï\_öæ.Áßêæ’H 3—«lV·uÐ7œ2¼·¦•yŒ,¹tÌG(ôfÞV3s¶ =5#’NÇœ ‚w+ˆõ\„Á=ø"˜7AËújzúdT_ñZû!YM½îRóZ{³¾š6à‡D6o$³¤~ÃÝ*¸·LÀ›õÕ|2‚×ZÇú Íß*ø·‚ƒù#™P,¬ñwÿÖÿýÚø}þøõúèË~n»—èßú Žkp€ænõA°~ánõe=ˆÌQÁ…ZæOÜ­:` Ö]ðú1‡ïCù•>Ä‘@† bÁß @$ˆ¾HÃ=EytûWz¤Û…¾¢Ì¿õebîßü­V @YéXbî¹æŽ™¯õµ?ænü zæ)úYÒé<¼­æmõCPyx·´ ’õa<… g=ˆíÌÝüEßR߯xf¾æ*ä®kÁ3€¤6$v °ƒ$xÄ?¸[¨XŸPÁ3£b}ˆ¿æou2‚…¹ ƒ[ˆýÚW¡‘õ!ü[Bl ¾×ÊïµòÿD­êäÿN”3ða.{6ÑoÁ3ÌáªBš=<®Ü¿ÁÜÖÜMè‡à f¾Áãjd¬e~B´XX¿ep|ÑoÙ@`dîÁçj¾|p°^í_zf¸£Ðç+ŽB¡W{ óp}Ùƒ™{¸Ÿ«Kx×FâX‚y¸¼¾ð[Ã1#$–®¢èrõC‚™˜›PÏÜ„Bv#ó¸0—àqÕ ðEê«‹ž ÁÁÌz´ nB#p ë’5ÅÉ>ÌåjùÂ1𿵠‰k>HÞ@`*æ·3 ¨Ðfàƒ¤öo¸\—ø ÙõÌ1£ÿ†ÇUpW ½ž½„æ%Ô+ëûø}ù½6zýgÎ#åìçrx‰.®Èl¢ç:8š9\}¬†/®‚‡+ˆ¹®Ga$sê˜ËBp¸êA*ðcýèO¡Šù,œ@ƒ ·=8or}0óYW#p ’ÀdH„ àj$D¤GozÇWzÓ;€š¹¸dH– à—àqµ?$IèSÏ\\©Îk/Ö»ÞüX&à-Ãß©@Å®‚Ë"y ˜«ÇI§÷ð·1« IhNàáàÒ ë{/¸ ­@ f. ÁájN AÂZXÿû/>_ór絨‘ÔÀ‰m F‚GþƒÃÕ ‰obýæçµéWÁÇ%ø,¬ÌYhl!öÈzЛ€‹ù¸¾×ÊïµÒàõŸW+…{|˜àÈ&ú®#™÷ÇÀ|®_ø\ߘÉ\×ÜW¨B™·C𹚀7ZÇœ…¾l=°<8 dö ཅù;¯«ÈAÀ Ô_qÿpo¡ïW¼…¾Hp5’%È0AÌQÀ¼®Bòè€ ø!‰LÀû ßµñÞo$˜ž9]UH43ó2_¡’ÎÄO]Uôœ NW=°1°ºè¼gFàd¾Bp {7HP ó„^WëÞõW|ׂ‡#øâ†€0ßµàý1;@bG_$·8¾áu\hÀIȼ?2$ ss|Íí*øC‚ëGÑ[heÞÂ@`·ܿ{Ò¾×ÊÿÔZéÇÆuz‰ž4K6Ñ,¸Œ×+ó»Ê¬A_ø]GZ0s_kÀæjÔ+ó»;P!¨Ì×€à6—°Ÿƒ ·9=¸€o ½8™ãÕ\Â~’À äH„`à$„ÈAÀ ÔHŽH C‚'Ð0OšÉ \@ëáI¯©@…2æI³{8°'’¤Ë |dø{À*Š~W_$š8€ g>Hº@·k0s» MÀÔ~4=°óDÚ€ÔœÌïj.a_ kò¯x•¾æŠäl'Ð ©# ‰œ@ƒ·üƒß58AdfÉ/x°Íßð¼ AlÀ…Á\Ì'ifEBó/ìo¯•ÿ÷jå·êäÿ¤Fþ¿T…û dÆ àÌ&ú¯-@†À bîZõîZÁù¦A°Z˜ûšû#¸&àb.[3ðAë™CR†`6 GPÐ ¸-@Ž. E [Œymm@Ž . AðG 8=<’²¯x$eHŒ à$ˆÈ‘$Á̧.-:o½‘0z TH3ðùÂ-8$½‘Hz T2ü=àƒ¤ v€äŠ`þHóGª„ýmàdÓTÝr‚37Ø€Ég`ž9p1w¤™%£•yÛ® È‘˜ÁÀÅÜ‘žÞk55ȬAÀ ÔÌy-CâP## IôßxÝ" Ém©@Ž$7ÛW|º‚NÄ7²ä¼‘6æ4€T  ¢îûüñ?·&þÿyþÀ®ë>§( F¤Ú¢cWŽ` Ιձ«@àYðj•929v ÀÔ&æÉT#¸Í,ÀuÀt# v°?½ ¸˜g×Ì@l@D0Ð"!¬@ޤ. ArX€  \@‹D±’ÅÈF,@Î<»v€оH"p5’É |PÀXÀW†¿@]QtìÊhAÀ 4H¸à‹¤3Tùì×52¿®Ihf‰¨ CB3g*P!AMÀÅ»f–¬:` $m0p-’×ú /§àÜœ@‹¤¶9;¸€ nýÇn0p ?xû‹îñˆoxv½Qô ¨P̬8îNÁ)|ÃPKļþüwµò_©“¼&òú÷­Ú÷ï¨uBû§÷¿Zß'Р>X€õ!¸€d ‘³”è×õA]v€º|=üÛfàƒ€ v€À‹¾¨ àjb$!í ´Â^Ò!œoA~ë„3ÐNcí¬Þl'Ð P-@Ž` . aÞl978lñðÚº¾áÞµ¹ßõìڀܼäúßç3ßç3ÿ™ó ûÿ„ÀÔðC€š€7‚Tl@`5²€ÕðCàš€7‚Wl@Ž 6€T @0'P#¨Í,°5 x#Àõ ø!ÐMÀÁ®©@… 7³À×€àЃTà‡D0±dÐP )Œ,1´À H#K°?$‹ x#aôÀ Hœ àj$P$!‰‚€hLÀ e FbE™ 8Ifr$Z0p-.ÈtAÀTH>K@ ˆÞHD-°9ÒR‰ì jfIªÀɪ©ÀIkd‰«6 @Yë€M!zÆ,¡uÀ Hl#Kn°?$¹ x#Ñõ ¨ðF–ôZ |üE¯w$ðFЃT B10„@`( ÀÅA«]äBgyóŒ…äÿV­äó™¥öñZ÷eã5î2‡ùwÍ_x ûÖüå_­Gžµè:ùWjÍNösZ…Ï7ÑÈn¤Ø€7Ô\Âûn¬(psìë€MxWÎng4psMìë€ øáF›€7n¶^8Ï‹z`ÞupÓ­@Z`ZÔ«ðŽƒ:àjê€(PŒì¡©6à‡àÎ[ ï ÀÔÈûH CÞÛAr>ø"¨ ÀÔ®H C€'Ð Ð,@Ž`sµpNgÖÀpNárX/œ¹Eð™Xj(³FàZ¥(˜F EpZêZä«(¬F°:`~ÈUðF[…ïf!xMÀ¬©@…@6s pŽ–d=Çàñû­ç¸|žôOëDVöZ#;¡e{Œ ¶F$œ[óeãÛÙÙ‹VÅÎ#î?·Ù¿k½î©ª- >¶yÎI©ïmsbêÇG¶Ïœ9çòäl–Éú9•WåÛÂ\Sûã+Ë×Ú|(ûz†PÞßbÕÁˆrÇPO±ãŒO¨‘­þIòaìŒ;3¯ÙÈ£OIšýu3É•E ó:ö&q••;µ3„ v­\cÇI}‰¸¿¸2©}ç”2;á}·<ûš›p}w[ó›'ÈħÎ-9l¤”~ݪ;U2‰uÙº.gÿìXSY×ï±GA=v ëÄ–Q;"*vìØ£cAi±!¶Å‚=¢’€-Ø@Q7vlŠuÐlƒû];gïcðßï{¿û}ïýîø<ÿÇÔ½“sÖZg·óÿ ‚ΠÚÔXG.¯kµÀ¡Þ,Ñ—‡ûqÿ.îûÊ}½ q¹°Ÿ õ[Ì\{Nï›[úœ\¼+Œ¬ù¶¨Ùñ­Kê—gëH· ÃÎ^|®yLüwα㗖Ü+ÞvÁlÂ}B¹ÿ‰àCö‡è{$Üÿî`ƺÜ|6=±Ÿá¯¼‡û›NÃÌVðKÛ‰ð{uçÈ“X:ûF˜µó`õÅØ2µ¤áµ_ýê–›#úÃpŸ ÁÏ/ƒùÞ…0Iă{Cà·¡ÓôúmΉûQöª_ë}FWJ®±´¿”›ylMÏ£sR±ºKÃkÅoâ¯kÉýŽrãvÙÑÇß'Áw$U!üþF1ðm³å[ò»Ñ`?Ý®ìu¿Pé4Üñ¸p_ÖÉ øë¤æÀ¤³²ë+ç†-ë È9®%«i¦¬Ü4›pßwB\2e3'¶xËøÝmsXÍùƒýØ»L_PÞp Z†==ÝÌi%O¤ß¿–GÎÛÜ=¾c0šNA>ZÑŸ™çÏWîO&Ä[1¸\çþÕ2þ}Xœ´òû âÛ-ãØ)X\ïÀˆ. PæÔû*R’“—t®Ú~ô`XsjZé-é4(÷Ì•³EÞ ¯ƒœ 4©m¿6òý¥÷Ö –m_ÓôbL'!Šæ)Íe§@éÕÅw«=æÏï‚–Ï+ayÏ&¨ÜÁïÖœ{¹;´D2·ÑŒÈ³ ÷3âq äO¶Bð{,É|ï0®‚À¥µÅ~–Í{¢Å)èðJ9a}#¬îÖóL`d<•}‹ÀÚ90ʦeòíÉf}³Œói-iå|ª­Æ<±Þóøåy¹œhÉç°*ž§,òà|_Ñãð^1xyÉq‰ð:õ^À r9`?ñÐçµIƒaýíC»Bj‰!EÝ5Y;ï;œq9'Fˆ'‰ÈWã\sž`?Ol)@ätH®.[8,Êß»pDñHxü‹öùi7Ñ/:Цå€y¢/)Ï{¿œËlÎ l·ÛáÔc¶Áæ¤øÇU$ÂæÄWc>eóUs·y†¸Š~÷²Í^µù<Âýxy\ròŸ$C·gülwAŸ9q‡ƒÊ£ªù«‰yÞÓ%ôu6Ðè«­î}y<\^GB.6Þéâ8p?IîÛþvêƒÕßä*â|Âzø.a¾¯Ø®âæè“;O…n¯·K„¹ñs¢ƒŸeC¿èõC׌éç^¯®øFËÆ¥óÄç¯Bœ¿RTúÜ0Ôöã:b»v½}–¶ì{nóȾ[>ŽKSæ6ÏɆ ·<Š:ß*½êš8 Ÿ§íëLz0yžÈ­ç¾î|ü.ñý> ü1-I3lÜéﺀpÞ#>ž>ÿÅÍTjH[8ïÕØÏ­¼£¡F_=˜íç«$ÂùsN«+ΆU/]¯ÿÐÆí=˜Þ·¯– m­RöáÜ<;‘çÄý”ùóžÏ£,ýÕ5ØÏÖÔ(SžßS)Š¥NÚxü vµÐè(¢²·–ùíçšñq÷×çÜ<˸‰ÃöÓ6Ç—˜œ.:ÔíÕ ¯W/÷Ñ•ödƒÃÍÇ‹ÛJ\àIž]þŠÝZ²vÇò.Ïó>Ÿäõ–ûÄ&ß:~l—•è ιóæ¸Ç~^l0Äfè˜kC‰³ 0¤ÿb]·gƒ =਱Q•µ'pþjömõù¬Ü—žûÄ×°‹ï#³y^–>·V%ó”+Fo|}ù0LnzÉîå«h“Ôª•vS6¼÷{qzƦPÙ©Ô¨ {ðû$NøÖ¹¬·è+ÉùQÂx8ZOðq·åzŒ-ö³ÖïÚ•¥‡aÿ’U‘cò`‹V¶æäºl¨qûóÎ .°0ʱ{ÿ1Z"í|îì4ñŽòñ5÷ŸÕ4ô÷Y[é­‚Ï7,¹'Jì§ÛÁ¯’‹êC€ƒ¾!ù`è[ÝáÖ+³ÁÕæþÛZa}`–ÖÝêÊ×/Ü] òÿøõá\>¡Þ~Vpîߘó¦$Ï›ƒc;bó‘ °*.lúƒÅÙl¾<`'(0Ô[ô!çëÜvHΚ9‹ì9¯ÇQ|Ιóû‰2$±×·(VXuÚÐha6þÅnð°‹ò}ÎÍpÒÒÕͳHß…„óìøçåuJàyذùL+±Ž™óûQŒ(UʧÒðx•›“>2U«wYÍÂç\ORÂåÁ@g›NOÂI1}‘ ™×Šë%|ÞŸ›œwk9Þ‹Ãö‹J¼mÚ:_ö8ê[ôÖšñÙ`ˆQÙ6°ö;¯› ž”Ð’0ë'#Í](®“ð¸æþæB|7+_&l?Ýd±$^gµtx’ëR®\M’ ©uì¿Þu8~Û8ŸmœÒö”·8Ïç׋+øø‹?,Ÿ{V¥ò”çuûú++E€çûä/;à~•ÝÚð^Ù°ñtéñú#àoöGƒ–Ì;è¶(|‹·X_8†ûOߌŸZ¼y±6®l"ä ¶??½UçwÇtPß=â^±C pwm²U‡ÎÙÌ¿u\+¶ÞyÖ¯7‹_<¸PÇðñ˜P/Ÿ‰¾Óæ¼ÀvwöÞtVO¬9Cô 0éÎûÀÉ-²á]‰­¥›“_ÝjÿK¸–œ¯ëÿÕ'‘Èçã`Á÷÷‘BàT”}‡9—œØÏ¦IÔ¨^ ¿Þ­ˆ#ðk_­AýzÙP½ã|וO= £û›iOµZò íÉ»Kúˆ>À?ò‘Á'û²÷ÒrÀÇÉæ|ÀöÙ¼j_Ë}é `½8z{§*Ù R¿Ñ Z8Œ¬œ‰ãùî2XëóÚGä¬óù¿Ïœÿ‡9°ý«tÕG.nÉ OàÖ  1qe²¡èì²õ%=Ü¡_Ÿƒkz(´d÷ÐÎo]–úˆëÎü{ð:5wjm븷¥DŽ ¯Ëæ|À~|p¼l?tÞÒ¶ÇÓ¨Õ:µZU6=yW´Í Ô³ã„ùÐF×^^áË"Ây …¹IçMf.ްþ"Ö)ÎÓ2çöS5ݵ`zí}à«~qcës¬‡ekoû> ‚’š¬Á©íI '¾;S·ìß¹HŒ×}™ù8GXÏm*r•Ìy!ÉS" ­¾m ë€À_¦å'À 5].Ç>Ï‚Ž•¬óiXÕòy72<œ?œ4G³b‘8®çëîœ7À¹¸œ7ÅÿÜœØÿ¬”Ý_\ö@†aDç3 rÏ…]wgeÁõ™“ÞìÍp‡RÇJ;6'ôö¸ÚGü|Þ¸úÌ@ÆéOÄç!Ÿñusž`?S´æ,ݳ †26ÃÒ f@ØÑ±·² È¥˜ù_Ò†@‡ÛÃC—Ï'Åã QK{ûŠë¬üùÁ×qüv½Mïл¸ØnÝÞdz_ß/L~jJ€‰s^È‚R5üªnª3º5›üÇšp²¨VÉ×Cã}Ÿâ×™óVSn<”týòÅ\?4Ønã©Sf.×oútÞò0<ªwøýÉ,èÒfAVHñ¡Ô3s]X8iTkÑêï}E_k^G8/šsœÍy€íÊø w…Ý9 U î$ÀÒꇧt8“¢;I¿>væ…Èp2E>qæ¹ ~âu(ÌóÈQ¤d¹åÏj&ŒkLØnY¼Ù®³6CxjÃ,Ùµ||鈠ÝY`µdi©j‡€Ë¾ŸW^'Cëå4¾­ðŸÏ|ÜÁý½…ñ óÑ/§¼q®Ù’o‡6£¨I‚càì©Ò’÷ñ:œqZ”sÛ¦•Ýÿ®þýp2®yJË ³üD.çt ÏËl…Ð5ðuDK®†-öcÆ©)BÀÞ¹Ùû¢Ç Ñ¦t’«1o'ôºÞxÞ ŸáäFù‘9¥—ù‰Ü\¾>Êý¾sjÖ¼QÂÛFˆol·æ¹åó²ÒÖ¥©O$@¥õÂV.΂wáÆN¹Á†#ÅÝÆÝŸs—/q_å'rryæ÷Âø¡”È…*ߨÏí#æ…^PmSt2á¸å¨þe¤Ì; ªß6Vöã#ë¼RñN8¹éÔI;Lã'æîóq‹ð=>)¾xÃB\ 5ö³xÄ‚×î+ÖÀÁêÕ³[\H€°Ê㼜™{Õ{S"2Â¥CÉò)á¤zñ!åŽú‰œ#÷œÓY·lßJ#}Pë‡ö…x=ìçr‡'¹15V‘†tá;BÆv®1) \Rž¼•×vƒRº²N†Ÿ^2ãRˆŸÈmãœ1¡îÜVl)]ã7§çEÙsºy¡çOö£ÿ¥UƒÆ•V@Ñæ¯ž®0&À€™ —_™sª¾;~qÌ ¸ØcÙŠã0Šñøý¾ óè–âø‚Çíï¯?OÙÛÑ¡OÍ„ýxF®þ=e9;æ´þÖÑ[b#6¸e‰û/³ º³*œlïB ”ŸÈOàñ%¬·g)®YTóÝkqÓœ'eò”MÍ„e° f¸k‹%Båjëçtí™mÌ_ÀrROì'oÃæ“2ù~"ט_η«W.¾ò¸lkàˆBÜ!ìǼ(YN¡ÎþÞ8žýf›_³côžYÌ¡RÛ‘`&œ 'fÌÝNÂóÏ_ŸMzº\ûÙ¤X¢yžÙ³‡ÎšbÖlo*ä ¶ëìyp–çXòÕi¬K"ôoûµM‡fYà6éa‰ýGC#¬.uö„“è˜+“ø‹üz~ß9_V¨/ŸÄø²Ü7ðÄ~¾Ñæ£ÕA‡—cÁ¹³ÝmÍ,èviNþËÃcÀ¯«uiÙ¨pRo~¸aáZ?q_>à=þÜãû/æ|Á~Š-ÊHX¾Þl·g÷ü¼*ú¸–u¾a±W¼ÞÝ0*è¾vmÐ-œD¾Xkðk_q¾'Ä«8ßãë¢|(ü¹°¢Á~zÞ|öZ–³ôW»÷>–'ëû$4þœ ŸVä}ý4 :OÒ´þêNÊ¡'¾×uÎÃÖ+.ˆœ[Ëõ„8lŸÞÅʹ¿‚y%9Ü,óõy&ÜúšóíãÚàtâüÊ\¬cYÕF÷z52€ðy)¿/Âsú¾âóò‹ JÕ.-ö+Űž`Â~¬}îg¹6ÊÄÚ/2%ÂàG;~;iÊ„ž6¡ýŸ¶×} '¹RzÌ먹“|Ýï¿ óp©˜'–üe+ë<¥cyú/†ƒoÀÒwO²áSãJ/¥f‚ßKMÛ–™C Õ‹’IÓðû¬½ià¨×Ûù¸“ÏÄû_¡P¾Ûbû|4wj³w[Ò¡ãýÆío_Ë„“c²vŸ+3þz8Yï<ñü¥q\ÀŸK•Œï;ß±Î;ãÜ`sž`û×£ö¡¡É¦üe£I&<1ä-ü¥ª;H~»±8œ\??½¨Í1®øçÆcÂ:ì7qÜdÉõÄ~„ùØXr±éÃøÎ¯a÷óæ·=Od‚Àt‡5Ò:3jE…“ÎÇv¶V‹ó^ÏùsÅ~Xhb±)V"›s[Ìy‚ýHKîò)¿e:9¬vêâWÜö½Z—lu R;êÛ4 vi‰MâÃIªùò¨Åy^ ß'Kñ¬}±è^‘×Åëµ9O°aÜ:—ô6o@•å©þM“ ¿&ŽYëá8(rÖ¯m%î×v³Q‹ë"¼~ ûß9Šª÷~Ud¬/ ‡¨ûw…|Á~rûÞwO³ó!SÖUKΨg|UtÜ” 窬,=µ`Ü|Ô­}0ÖI3æI-îƒðó/|Ÿ8µêò¬2Íœa:Áè"ä ö3×¾Ûà¾ûÈüZà^£•¾Z·2Šiƒjß5 …±n¾¾N8®{þ‹ºTIµ¸Î'ÌÇš‹ÜQ¾îֶ¥glþ,œ‡°²ÉS¶ÈìÔêée5ii^°0Àà"‡ŽLðË„9‹U‘ÃxÀ‚å +á$teý}3'ΪÛjÑÌ=Íê±óIbß³øçæ¼Á~&Ø&¶Hÿ¨&Ãï)²†àåЊOÚÌÎ×-RZ»ý±#œÄŽÞV?.ïû󘯻òñKwíã4W¹øssÞ`ûUêîÞ½˜ìZD æ·ÛjÕú‰™Ð0dP§‹©C´¶PóT8¹*¥3(_‘3%Äk3q<&|Ÿ"àâúYöë]'qýÛœ7Ø'(YJpP™ua±^EÊ ¶Ïa½z°8üèÓ¤³×|?Ây‚6kLòêÜJ<“[kËM›ùåà°qÌüuzç(šóûùro]åõc—“ælœP\3aÆØ›}몱ïNÒeEÕãý ?GÁ×ùs¥Â§ˆÍÕpîyƒBç•4ØÏ²³+ëuØD†­PÄq—ª´ô¬”Õ#´‰åb‡„ÎUì Ã:ð26osíÌÂç“fª¼¾Èçâã>.àã6sÞ`?ÒCUfüöëj"ìë …“Mb¸"ÞºMÞ·ñJ˜é›è² ë¦ùqþ=oøzŒð¼¼ª¸–>îÉ 6Vâ÷±\·4a?>ú.÷nð9‘¡o›¿×Õ®¿ø—LhdßöÛ®r½¡¯ÛÖqå–„az¯×ùÄõ>v>AØ¿ùªa}Ì0¥Q¡u8«²yÊô}S\Û–&ýÇÒ•Xì™·ëˆV™ç[;íPROÆ; ''Vœu›¯Çÿ||ι 'F_Útô'…Pgš/Ûb?³oôtK¾L.5oc€^öõ½f7Ëdû–.â>ÈrŸ‰#ŸªÅç3Ÿ‡ñëÆ×78§Ù²N+±ŸK]6¶ôZOjÏŽ/™|ÙiÅ—ù”i˜ C#õçIû€[ô±#]´$d ]W‹ùßcw/“—J‰u†ï3›óûIé³fâdžÈØŒ·ê¦ c§sãŽÖ΄çL_÷P5:ï¹»ûÒãp’ÓˆN”¿ßÎ-ãÜtá:ID¤åþ‚ûi`SlóÈˈy7Þd€ÏŽF¯’ !]vóuƒÜÖíJ?Ä:ížµû%[5á|[>~áû |Órœ¡Áö››x!DûìîÎ8™â“ø¼L&¬y5»âûÃ…J”äNVmtÍJMøuáû¸쨂óúxÿ–ûqØOêÝ×ínL !·†ÒhøL\úÒû[¬y½ëîo`Æ4cÝt½:¾dӬ¹xüw¾#üž‚Ç—%×΄ý¸ÊcOô !Ÿ¯:Í•ÔJ‚™úF%n¾Ê€1Ó¥;ò*‡‚ˆ¦m¥7ÂÉìuûËråãÀŠâø…¯òs7œj9î°*—§ìÕ«|·!!$CâÖd~Û$ØPÄ9I—“v_¶ôbã÷aœáM>ë/Îûùù!¡Þ¼÷­…õ–F…Öûm±ÓÄÀµGýCÈ׿›ëè÷WïZý^9÷àÌl7ð´•ï¯%ÍË~Ö~ñ'|¾ÄשÇÕÈpù´è;S®žŽY[2¿Q¡ó‘JìgZãÞNõg„˜àž*»‰I |¨ÓÍ[èh7vÙ`îµw¶NKšZ]iã•ðç ùù!>^sÙ[´ñƒ8‡B¼VO쇮ÂÝ¿BTÏt¼ªÆûsyØô3àæâ…vý—¸‰çïúÎ|o³Z\7âçý„sd¯ÅóJœÓgÎl¿ïå+g~“o$æåËmI°[Uóh£Ø ø´ÜëÜÆ®PüF…ÖÁ“µlþªçÿ¼. ë›wB\¿RXžƒÒ`ûº/wúo\¶‘Ì?§+|î' ¡^œ[¶;ê¢ÍÚÆKBɵº6ß×I†%SÎ.;âž = ìgÄö†ì°áÝ+‡hÉמáÝ´ýDþ5“‡Ž5×=˜¯ˆ;°ªÊý‡B»VÒ<åå!†ã—®‡’aûV.P7N†gÊ'hûg@ˆ®šï¶G¹Î|ðëgwGJ(ö×ÏxÞ ë¹ ~®œÍñíÓïï9¨ËV2°eAÛÆ-“aE–û•½2àüÔ$íÇŽÎ0±šÍáS{µdaBUéðòb=àã5áóg)¼úu©53©˜È ·×(±s½• ¿óe|½ŽÉ°ëX‰ewºe@Ư7ë”íÛ „sUZR{]uÝš€ñ<>Ÿ ã´»â9ysÿëŸØF9N÷qS -yõ"ÞcÆ×‘ƒÌÇÅ|žÞõ Ëæ´´7B»i˜1ÄïðÉqXOrÏí¸wXMøº¯[œÛ)Ô-v^ÛmXrñ¦n«wnC¢õ_” ~¹èÕ2F•t“õþ½´ïboïÜLKª~xÛmº^ý×AÈ¿?•Å5¶»ÖwôËT»¤ì§.Qý7%ÃF߯´Ï€§ÓŠFUpèKjÒRáä~M_,Ejq”^¾NÌ×%øº‚9¾+àÁ½ú?q=Ž×}~ž÷ë®cž]z íª±ÝAc·&í&ïÖ,Ò˜ '•7ge¾5Apæ‚ íÓ•‡­AGœï.¯»c€Ÿ8àçs9¯Xxn ×Aƒííj3÷¹µ†¤×06=Ÿ™ Mt¯V蟚 ©b¸_:Ò´æ÷ªËýÄûÅ÷ý|v.|غoQ˜`>Tø>¯9ޱý耉û{5Ñí±ó_%CF@à†Y&¨y°W‘ckúÃ¥’Ö§|Õ‘w’vÝ"íý oòú)\çÀß×±ä9›°}a8®!{vGÜu(šQ/#eLðÆ>¼Vš› \ÅQÞ„bdêûVé3üÿáºð÷¶ø<„ŸŸ7ÇsÅ<åÙ+-žm§!o\”¦€¼Á·´¾wLÒ*«â³ Î`¿}Îò§t¤–!¹Êt;qÇóœï_ ßã­¢ÐûFØþøq¯gÏÐiȦ•|¼ê¦@Õ³æ”N2AÊœOÊ~AÝa˜öúû5Ýu$Ó£Zt’•¿x^‹?gù|—¿ÏÂ÷C,÷»”ØO˜óÉ×_R5¤ÁËWsÛ§À¸–•õµoš`L˜ãCçþζÃkh½-I¹OXø‹ë¬¼òñ¢°^ðF<¯g¹ÿè‰ýŒ ot¾f²†\HßK=¾n¾b‚½Ok_S~èªúO¼MÑ’_dOôÄsæ|þÎÇq<¯øsÚœؾùxKœ† Þ´­S 4ë|kwœ ”¦Þ©žÞbé´¥ªŽŒ®8Ù9µ{€¸ŸÆyáÂøá…buêåèóÛŠ?_ÊÇ_æ¼À~dø4®¶UCZ\ŸÜ«ÆÕòËýŽž6ð~š t‘Ò'¸Ž”0ívkxÝŸð¸äëøÂ{€™Š>ñ—Ú/%Ž7,÷;â°Ÿ)uâê qºÜtÕÐh>hÉñÇMÐøuÛw»@¬ÿù™Æ“:âYwçéWÃüÅç(??Â÷o„qB¾‚ß>o0ç ösèì€Á{kHóå+NHR³[.Š9lÅÓäó·õ€ÛNײSt¤CCÛFNþ¬ŽJÄs:ü| ðøžçßÌýXUÊS®:ØnãýÕâažx¦@{ùÉn^&vîÌ ®×x+]ûâ{¾óy»pÿ­Åýa½è¥‚;„¼*%ä öcÞV¯!q£éŠp x×rUÏÙoó6qÅ.lý1‚Œsô¥çj?qÏçÕ+4Õ/•šÿ¹q¡sW¶ØOZÒ·9Ã5¤Š»û—ÞS`s ìGx®¿;7ÿË’4ýŒb±d`¯ïç9ø>5ßáû Âø¨ðû@JìgÛ¨÷C4Ò6_*¾¼ó!ªì;—Ôû®» »Oj¡Š “v_µO<ã+ž«žsvÐýÐ¥-Ù¾Ÿ4ç ¶ënµf²Á°›è×”M{V>+5|1Þ%]ÆÆ;;€vKfc»)o»ãˆÈWgñõ>Î>‡x¾XžPc?æ×g›î&ö‹ouðn” ‹O>·ËnýËO,Ö½3dUoûföÓ5`OÏ5»|ÿþB®/ž«äïM ñçXøœöS¤²]ŸG»Èpó‹ ©?T;K±ÍCoælÜ ž¿têØyYÙÕ`T›“|Äq?$ÔátE¡õTlw½Ëƒ‚J›v’.6‡ú'¸¦‚—iý…’›Lðá²Uvúg(}ééDZd-½Œ}Äñ×ó:i9Î5a»‘ÖÞ[ÇîÝA‚v6™;z|*$…î[ë¿Î=‹–m?¯Ž3¬/3Þºk¹HSòdo½ÜW?óy2?×ò·WÒß^Ij«?¯$+ö½æk"/Fßü×ÙØændÜ.ã,¨,¸Ø¡,¸=[R¶°‡[ã¿Ê“†z?z2n—¥7®ç¾(–žn¶œåO°Ü+‰{‡s¶!gcÿ™_ ecç¢\ÿÄ3ÅÒãÍû'¬KŸ7iCmbÞázÆíR3ï7WÆ5´e¾œÃüqƒ‹Æ•1±ÿŒi(cLì8ÆXPýàû˜Ë¸Ø¡Ì'Î…q» “Fǒ܃±dŒËý’tŒ«b^¹JæþW8°”³`BÑ·’©OËßµòïZ©¶ú÷«•ö½ ôþ¡>ø3 PÊX”¾ޱÿ8°Ræ“kbL=ó÷¶`Àj˜§œ'cÀJð‹ fŒC%ã,X1Æ!eÒXú_R)J^‰Î[¬Ì Á}¤ì-|Ä]~Â8ä¼lî“Ëù]œ„ÊGy`"Å¡ì6åIƒ²ÅÄ Bå×-ì/¥þ‰—8÷˜ŠAÉ6—ùäÆ0&Móò`ì.{æ)Çù†”GcÏø¯ŒÛón—-ã¿Æ3?qï<åòVü¢\“†&·ãÀJÚ ¾šqŒK£¶àfëçriLŒß¥ÿ‹¬Cê)ž‹¢3ÃPøÎYø?õ”û﬑×Çÿõ‘ÆÒÏ<7U(ãÀåìWÊêÒ16¶%{FgÁ}¥l9c+Pî«òïpÊ4”1=ÊWpdLCKö ÷ç^›Ž?ø†sž!åbQJL JЉá2¢”˜ ¡Ì˜sº8÷õϼ6)÷Õ(èO¼6-½ÃõÂ4´ôϵà¾RN—šq±9w†ú†1F—&a Ê–1±)sƃñ^íŸ+÷'|.WÆ{-`,CKZî×g˘¯æ®f\lGÆY01¯Í`Æ4¤ll=Jú[Ǽ6)§Ë„rÁÄ×£d˜üjT.Ê‹@ Ê AÏzêÑ*þïñãßõÑêß³>ÊØç6ÒûƒÁ©CI1@½‹RÏ'¼'¨ú¿À•1_â\Æ¡‰a¾èj ö+e,Hc²_eä*”±,=ç&ø¥S¦¡ ã,HÓrh¸ÿ¨“@…2V¤{Ø&J‚ á…2 -üÓ]Â4äŒlîKÌ9]œÿŒ*@yb"Å£ÿµå‰I‡²ÇÄ F Ú²ŸK0U(cêˆù‚’b`z£ž_ÿœù*cÌWÊèÒ3.¶%FoÁz5ZÓwL†e½ºüà×Nù…¶àÞ(õcfüBKþ ÷k—`MT1¿vù~íœ_H¹Ø&” &†%ÃäP£L(L ójæŒ.Î{CÙc⣠PžŒ÷jIŒÊGy`2Å¡ìm {¶Çü ÃÐÒ³=¿ÁwÞ+et1.6çÏP¿ö`ÆèòÄDŒCÙ3&6eÏx2Ϋ#ctQΫ%Ÿ‹3*ô,Y)·P’`Òz¡ ŒSA9¯FæÕĘØræÁLY¯JÆŸ¡Lì”ì&6eÏȧ+—2 1ácP¶˜ôA¨|”&ÊžúA[p ©O;=¡K=™ ÿÇÕÅÿ)ãG{ö¹LôúcpêQ²¢ÔWcåŠó8¯”ÓĘØ¥éûõôqü™ã•2-dŒiA¯¶äÞeé»%øç̯žr ]×BÊ8†F)=ˆ?CI1 ¼+Òó9øo0ô()&„ eDÉ«|÷²÷ø Ç3±½Q N缆²$òBPrÆy¥ å…ŠG9bb…²äòDÅ£1É‚ÿÄß>U€òÄÄ‹GÙ3Î+åty2&¶#&b(KF/Æè’cR†²Ä¤ÜõÂg|W ãsÅÿ„ÏåÈø®Fæ‹Oy†&”YÇ’™2^õ(IÁ«ž2}¤ŒkA9¯2LróÉwd~õœ‹Ǹ†A¨|¹ÀéŠû‹LÃ`ußtÅ"¡!Fé¯ÿ›µò¿ªNþ»ÔÈVÿ®^é}Ô¡¤ˆÞE¨ç8^ H=JVŒzêbü¯¶Œñš‹rÅ aüë Æú‰±`»šP.ŒáAÙ®®Ð:”ƒÚ›± íËÓwL0ŸËÓw v¡%ëG‰¯CI1è½QFº7ƒÁ/Áà÷²àRîu.Ê“!e‹µ0•‹rÅäбñ¨!°É8Û5åˆ Ê’Æ‹±]1yBQ(OL¢x”£-ÞTÊóO¸…q({L®àß™®ö˜dÁŒwÍù>Ž˜p¡,é¼Pñ(Gƺ¦ èŘ®rÆ3ãLWK®çƒÄ0FeÆ ¤˜¨*”‘qB(ÛÕ„’3^!å]+/„r;\ã‡3¯ãP¶?0¯)ãÇ“<•OÙ…˜ìq({Lø`TÊ?åˆÉjÁ+ŒCÙc!P£Œÿ7Zý{ÖF9ë7—^_ Θ"”a€?Cå£<ŠQîïJyhÁŒí‰Á²Ç€¶`»ÆXS¼^Œíj_–¾¿ŸåŠÁ®cïÒ£dRúþ}wc@’U¤gáñß \1bP2LïÊôlÆ&F(KÏŸp9ÿZ2ZðÐ8ßUƒ’`©PF”’ñ]%˜P*”%ÇÄÒ $¶?(JŽIŠ*@yb²Å£1áBYÒy¡ (GÆw¥ èÅø×rLD e;b2ª M‰I©i"ð)?ɈR2®«”qÐ ?á É×Õ„’3d.ÊY’´Ø®1()&µcÉ0¹UŒïj‹Iî22¾Rèœ#Ê„ þÎÑĈ4¢±8„²áÒƒ§ô×ßµòv­´¬“?«‘ÿ]µ‘^=JV„²}0gP®Œ1E)³?*¿Ø?gÒÚ3&m>ÊCB=æu0cDz”¡~\ø3kꛄ} \1u,˜=Pz”¬}__`D:bp•§ïçb 0F¤Ý»} †¯GÉ*Ñwè¹}zÛDI1TŒHʼÎGy`BÄ¡ì1)‚Qù(ʲEIè~5*Æ‚kk@É1a4( &бmå˜<–@^(Jn‹×E“Éë'œÈx”#&X(ª€1n)7Γ-”%œÊ€’câiPL>Ê€’3ÞµQ…2¢”ÿ„1GÔ %elÈ8zž“Õ„’3.n.J‰‰ë‚‰«aL\WL`ç:eÿç:e‰Œ* |HLðx”#&y(Kt/”%Ç„×X0!ãQŽ˜üAÿ¿©ÿ¯]X»ùôú¡œ0Œ Ð`TAQÊ¿Á8.ö¯ós1ˆCY {¡ (G èP vnÊÞ†zd ì\G ò T>­‘ìzÆÅôDÅ”§øç¨\”+&@Lúž)þ •ò¨Dß›¢ïÑ÷[ðï¡\014( &‡×O˜™œó„2¡ä˜8Áü\JŠIä2¡\?WŠ å2¢”˜X:”ÔãeD)1É4,ѼP”Nƒ’`Ò©PF”œñs%˜€*”¥ÄDÔQ¶&&£7Ê„rÁ¤Ô5x›Þ(Ê…qse˜¤ÞŒ›«ÄdÕ $˜°*”¥dÜÜ\”’ñ7óQ®˜È1()cçÆ¡d˜Ô*”e‹ÉíÍø¹ö˜äj” ¥d¿–¥~S˜#8®8² ÔŸÿåŠS‘¾Ÿ‹•‹rÁ`Ò£dP”œñ¹ è܃+刊* <^#J‰¦CI1ؼQJ¬:”ë‡ eD)mñZ $X?T¨x”#f( N/”%Çú¡apÊå5 äX?4ŒÁ­BQJ fJŠí2¢”Œ¿-Å÷F™P.è:”ƒÝ e@É1è5( }Ÿƒ^ÝJ`kë-XÚŽŒ£MÚñ(GL€P–^(JŽÉ AI0!T(#J‰‰¡£û ˜Åß㣿ÇGÿžã#öï èõÁàŒG9b€†² õBPòâ”9†1Š«BKPÆ8JŠÁ«BQr eàßÃ@V¡Œ”^†úDãϬ©¯/örÄಡ~˜øçäÁ¨úî{ JŠïUžúEa\bà£òQ¨G þ “ UP‘¾‹m¢ì1!‚Pù(WL JŠÉ¡BPrL J‚‰¢²`Š£rQJLœPT>ÝÀÒ£d˜DjT.Ê“)%ÄR£L(L,=Jf‹÷eB¹`’éPL4ʈRbÂéPRL:o” ¥ÄäÓ¡¤˜€Þ(ÊQ’a2ªQ¹(WLJ=J†‰©Få¢\1AcP¶˜¤j” å‚ɪsøG6¸•rÁÖ  èÙLä8” “Y…ŠGÙbR{£ŒŒ®FQŽ˜äA¨\” &»ŽñÊU(#e–câkPL~ʈRbС¤X¼Q&” }g#®F墔Xt #î…Š!Ÿè¯ÿîñÑ_©ƒ´þýX÷þÙ˜ˆ×6˺ÆkÚf=ûY-ûWÆ?´ýgÕ•P{ègŽ£×on0ª eöbü¡ñF‡²›í…2 ä%(ë ï7Þxʈ’—¢ü!¼P”¼4e7á…2Ð5| Œ:ÃÀ°§gƒé=FŒ”úTaL£òQ©¯ þ ”`T>Ê&†žé¨"ŒÊ€’³À±ÅÚ`B¹`ðèQ2 5Ê„rÁº Åšà2Ñù“-þ?ÖJŽA¦AI0ÐT(#J‰µ@‡’`-P¡Œ(%ÖJеÀeB¹``êQ2 NÊT’aªQ¹t}U…2¢”°:””¾†DÏîÒ3˜¿”ƒVÃÜ^(J޹«AI0˜U(#J‰A­CI1°½Q&” ¸ž®‹cî'ó¯ßýG•<¥fUÍèÐÝÛEþixÜÉO楊ÑšgŽ\_¾ÊÃIæ.nô\‘þÇX¥Râ$úË’;ûñ_Dî¿ øû‘BþÃØÏïåÊ®xw1ó¾W¦ÂÊåÒaßý¦¶ ÎE‘ä Åõ}ûîûÃý¹ ¦ ]{ýÕGÑ·Š¶«ÄvÏG”oðG­mÄŒûÝ™ a.n~KM°ïdDïéí`Ž.}ù¡{‘D°ŸPî')ð ­àÂÁ’Õ2~/!~_K®'¶ÿ°Dï2ÍÕ[‰ÀÇM…ö{;|S› '¾òÙ¿µb>N‘D•ÜÖ~[)µè+¸ýõΈz±Ïs¦ì)}ê}‰B<5¶»î¡ªî©v[É=ß‘5›^I…{évïú›`åyˆ:³¼,—¿oùHb¶;×÷+äþ ó›ì·9_RüÜ´] ¶›D±Õ¶’ aõû7¾— KFûš@൴3ÞJIúõº“‘Ûô»O÷ïâ~Ò‚oZ1ÑÝ’/‡ýÜÖ§Wz꾕´6GS¡×¢q£š lËÀæÑÇ; \÷•‘¤ú[ßüæ+ò|¸ç Ä®¯Ólçñ· α´ô91ÑëŸ;^akØJ¶{PÐu*,éÕ) Á|¼vnÕr~À¦;éÕ‘¤¯,á+^'îÃ}ŹOçáYré­ªæ)å»Õ‹Úl#×fkç꿦BHÚì–·g› vï åЮ ŸQáãÖHräùóÍÙêï¾òÜÇGø^¹ÿàûjé_j‹ýt‹8trü6"ø¿ß†P› ‹ÛÎ0A²ÿ‰ãvw…_VvõÌ1D’¶¯³·Ýê'ú±rß=¿ÀÅej}ÏÁ'½-p?s^`?!ªzëL=¶‘v'¦jYñ6¸ôóLíe‚Ç·{·>³W utƒê¤I÷Ky Føîʯ?÷IîO ˆÝò)Û^Ùü6tš^¿Ð'öÓ¥ñÙY¾Ï·’m¥ôñGêÞø¶MŒ¯Þ¾ }êaûäWéÜã‘ ¿ûEñüüASÜï®ùØkvæ·Î0ç ö3­÷Ñr=‡n%o—žvÙÓü6DÍÝúöìXÌõxÓ“–¦ég œt€œ¸¢©äTÊOä¸s Î¥üÞ(¸¯¸%ÏLƒý´R†•®J`ÝÀ] ;܆-ݺ¦ßÍ}¶ZÀecã6 ó¶]ûå¯èÉýã¸ÿ?÷Ûp#À…ø9ˆ>„æüÁ~rbuôø¶…œ?»0²ÛmÐu­WvÖ‡£ºöP±Rj¾ÃÁäîùó/ûŠ÷‡û”q® ¯Ãœ+Ä?‡9°¯†‡ /l!¥ÛÌøv½÷m8”{ê4G™_¯N« îŸÓ+T‰<@Žlœ4~Ï)p.¿ÿæclU-OY× J %=×–nµÅý6Ìp:Q«ÛóKz|NÖøÜý;UVÚ/ý}7¼¬XOê5êü9¸bIÑïŒ÷~69Ê<Ö6<¸6ª›7ØÏ6e…Õ²ù¡ä ÅÖŽ¼ Æ¥û"'`?³6Κ޼™vwÙñ+š¤âîNç¿>åõÆxÜŸŽûí´¶·NXR?Xy¢ ²UV!o°Ÿ:CO%;fÞ«÷uìm(û–•ÀëuuóªÖq‘œ«8íe«+HZ£ß›½âG¸3¯/‚Ïa;x Œ÷€íË>¨:±}(9¿¦‰GÖôÛð¢gÎb;l·ß5ûå-‹–†‹w¦ç4üv€¤¯ÙvdÃ:?ÂyˆB»—ÿ{gãúÿÿ²F$K„h(É> EÌg%[‹(YBÈÂT–±g“cp0 §e"{¶îì9¶,Õ£uìe­c áÿ¹çyî§ÉÅwù]¿åüþ¿ãºÞ×÷{º¸ïæy>ŸÏ½Îû%ã}È EŸr}.³Û—>\°£ºJð N‡z/Èø|xŽws¨»ø™eïŽò¥ÞåÅî²úUSäÞ0-þ½}9|帤ØÏÊFË­‡n!ö ’ºg¬M‡µ•>V®…e½bÇÞtîfÖL®í¡!¼ÿøÂüù˜ï!óáäùµŽ"gB—ؾCî!çÒó¿ûé“ÕK‡æ¡F[ ÓÂX#·Æ>\(Ú}@rÝGCØ<ƒq—§ù²ògà¯ËóµØ~qm»› 6“¨ÚSG|ÛŸ•½l'˜{jÁÕÍrôÓþ½áý±­Õ‹]5"ߎqío“ÏwC‘ÀÏ›xßdóBy€’½ ó"Ić¸ÍÕH:TMö¹9@ 7#ŸO v…¬î&K?¶Ñ¸S‡Œ«dÎ}`Ùsb>û¼¯wQ9^€ۿؽͤ‹‘$å×aû_O‡uF¾ví«…¥¾ÃNåÔï—Èͤ ‰†Œü´eOÇ‹óÅ÷Ìû5V9ÄÉ›¶µÝsè©È Ðç,ɱŸË\â^—n"³—F™´ÉI‡Û×}?µ-TȈY÷¶T¨3PÞGCFµ›7aÉý2>æ÷ÈÆwžƒóRVžÏû úc?6^•£7uÛHFzÑN›A¿$uÄxJ°ò™àçÓº »s³†DÓ×Þ©ŒKÅ|Ù|ˆ÷7+–1ŸÑrþ–ØOóÏ’«KVm ¥%ý×|J‡u8e¯¸¿=áY³F¹=_hHÌ}?‡¼¢oxîÙsYõ»U’·¿‘ñãW«rÜ05öCvðmßxÉÝZmÅìšÐtÉ`ó®R-ô=ÒÄkÞxH ™ù ¾Iá9ˆ óÏd¾clÁ×ñŠÀxúþi)ØÝ˜Nm²W®%§,¬îYeÀÚ*ÝöŒm§íɱ}aR ®²#ë%Yç–ÝÒL]$΋ÊûÌÝ— õSŸìËsé°³Y{wVŽZKþ(žq»SÜœþtr-TÜ\üÚ¾}[˜—¼«Q‡\ ¹tδë‚e\6ïaõ‘ñü¶šÎî—>×¾<Ÿ®a¡|ôÝWà “× C5swË€gÚnjÜZ+úº5mÞ|·JC4 tS6f>ÝÌÇœùûò<»r| öã»ûðµæõÖo~WÖ¯“gÀ lÝÔ¶l~\¡OÛ±Ü Éó=õ²×ì„ùd²yóÁÎü’œ¥ñ~/c㮾 ûÉq½×|’ÃjbOñ }3À9Àeëyk-¼8QüK­'Nð¨%æjD>!ãð¾|ïeüx/ýuù‚í®³}Y3YºŠôºÖÂ;{`ÔzÝ5Ñ¢™¦<”Ùæ—ïG§!<Ÿn!aÏ™ùÎ1ÿxÆ}3‡Ø;‰ü]¾`?m’£Šîu '#º†M<â wü2ËÇÁ~Œå tÀK ©RwQ½9OÆ`¼ÚéK/n},ò|x>Ug`<]¾`?k'í Ûqw%)àz¶Ø?.:,KëÙ@ Ó8ƒc×wöjïa’@zÿÖNrd¡¸^aë~œ. ›¶±Vú뚂Ϩ³8Òå ö3ÙìKÔÖ–+Éæôà·æd@öÓ§ÝÖÑÂÎ;­b“ú‚ý:›ãüHhÌ´ó¶F Ež6›ï0Æu²ÿ8a{ã;Îbèòû‰\åWÙ.m99m}cu̪ pØ;à‹±¼sk~™ÞÞvòx;)$mjÒx±¢l~Ïò’÷¼/ã瑟d<¿µ;0NŽ._Êùñe Öº ˜€³·œ*Zø}€$»À³ûÿ‘bØ.8}›{ï€ÓÂæŸlfùÂø$ü|¹<‡\‚ýüIí‹—’Ù‹Þ%Í8š:®w­0?”Á×¾NÓK0ÿg–ù³ñ€ÿï۲ݲéç›ÊñäØÏ#.tEе%dPÀ“ [.g€¯uN£Ë_óîl¨`KÉ*Áo±lÜdóoÆÃây.¦ÀøDlœÐåöc>úÈeãB%y÷nþõY÷2r¾8¿­6£V/y²†¬ÍúÒ:e^™,óÿå¹YúÐÜš«+¼·åÆ%ö³»:/+‰½Ç²»/_d€Õ!_py—­s+ìOÓØÏÓÏ¿vY¿n˜è§ÌâmçÛ†ñ¶'¾ÊØ<ÝÇâFéd©¸ÞÔåöS@ª™¥$Û÷·<Ù¾4*-_c3¦(º5jÛóùŠŽP«¸–_h§Bm<›Å‡æ÷Íü¬ù}­°^1ƒõëŽZâÕAä'ëòû2£æÔÍþ‹ÉMÌÙN52á@»-+_äÃöõÓëwè†u“æ8 N Ù)MpeÉâÚZôëäù·e«¢Ç<!7Ö]žÌ¹Ð¬è«Ëì§ø éön1™Y)¶žS£L8ÛåÁ•éOò!kÒÑ[nSz€Ãt;pÀÏs5ðћտ/}MY}cuôqÿŠÛÆÝ5ø!Ï× q¡ü-7#“ë‰3l2¡×…‹ró¡ùôA-*Êû€qúõ‹ùø~’—îrT.\$rñØsaããDzqœ­Ótùƒý8žt›—yOI~›tîý/v™`ù»s…¨´|ç>¢÷•BRðµKoY³Ú_ô¿ˆ°u”ØOÇá.Þu{/|Áñ÷o~/µÑ¶|Èk8²OÄàQ2ž†Œ~q¦dw…„íKñùm-Ô›"ч_ŸBí/šä±ë‘I©~Âóæ÷LXéh·H»<ÖZpZûT/|-Ô.²ny”œÞmfײù{¯Ì–qcuyí®¡Û§CÉá ð΄篊ÖeÎÊÉì¹+¯{ïó› ÌÈöÍ«¸}Y#‡º@r½¦N½Jç#l¾Às:Jec+P"¡ÏØÏ) &ªüINË÷€í_;åÏÑë «Æ¬´+|@øùö1?Y\L›[%þ³!tè­zQØ©†ð<¬ËíÃøc?œêUþÔ{:Ù}¬f´D‘ ‡wIjl‘æÃ¬›#ýûumoz6XšRbç±~òyÎ8ÒïD_hÝ4#ÓšklŸŸ'Œ#ƒž ›­Ì„‘º»µÈ‡é[\\_}ê Û6¿tΛUV7Y½ä×ÿ"GˆùëâÛ¥nÖ¦ ÞäDÃé)OWf‚CýzUÆ7ʇÍG×{3Ö$þ Y›%Þ|¾è›ÍÖ3ÌÏšùfëâÛ½¸Án^»f@ZN´}¥ø%zšJœ/›`¿qö[æ ‰æÁ<ÔavÚÍyglý¯»¯Ëx^;ÿûj-˜ß»T´ z>}g&´Ù¡þ}Ã|pß¼wíôÙý`„eFã£ç4äö†^Ož„æ'ÍòùUë⸠γÞW­Ðúí“t%&B:½}›—¦^‰3rèñ²a~‰†„ž_ð(cOa¾×¬.0ÿx}ˆÛe~ëÜ® O%fB§y²ó ò å\«¿]vˆÁ©7²9 98ý´SÇ¡a„ùªóó¦–â¼—ñ_ºÜ|ûá×ç#€ç‹eB SÿÎ>¹y϶è°rUH'“w§;‡‰|æ§Ïø6~ ¦õ~¢|#ã÷ïmù¸Æö—+¬Gí¼1Æšút=’š ~gBüÝɃ?vÆÎÞÐL¯Ýzø¡Ky0»%–Ö0qŸ€Í/øösd nã¿þr/Îm¹s ùjX©j%œ<ìí>4f`‚PçÊöGW›;Ö=Ø.†ç©±ÝíA®ùÍ™ž ž6hQ 6ÛÇøZ^ÌƒÒ ž7íÔ&ÖÌö¸WÉ't]¶¿Ëæ™l>ËòPÏØîó‹îƒŽÄAIG:Ë„Qg7nïž’nYίîëË2ÛOsl›@Ì6Ž1›°PÜÿdûàlüíòâÜä•çÛ'¥dØ™ðÀp¼×þ¢L8c17ßît˜­˜& ‰éùãp‰– r?Øz‚qLøçð\ä·nÿ6¬I×Û­øùIS¬ÓÆÖiÿh&<ÿB'Ø™P½ß„w&ÇËž7öøÍÖö $>ôãû…„qÙ¾:ãIðÏ£lo›üfMÇrç+ìçè¾Ë­ºÍÝ6Œ!ÎW‘˜+-?E”v‡ºK¼ßýé”@ªœ|_ùí¹„ñòØ:Ž=÷½-ç.Ü^«t˜mxºrÇr¼F9ö£ÃcT †¨wEãwqаâsÇ…1y°ðIÕjšyÎÑñA…4Û²°ñØÓO¼q=Œ£Z“ ­Dþ<«WüøñMÆ×³våöyü±Ÿ«íV\è1¦<Ùn£1ºiÖŽöc°íë>“ó YÇ÷3bZyAÉïCV:®!ã‡4¼”+ ß;·có¼½µ*l™zßöO4ºÜ6¬¹ÈÕå öžÔus ƒ9Ц“éÕ øÜfEO‹J–ç}ýÎ=ïí]MNuMC~ëèþ*„°çÏž?>-[ï Ü ÆEÖåe¡œçúÍ…Xý³ëpàóê7¹OÎKO,ñ„jŸŒ{Œ}«!.\3è×PÂÆEv.Èó­îÊøsÜЬ‚I”Ûu™¸¥ËìÇÝàbꦮsžry5ãàë½g×+ÚçÁ¼Ãîó/Ýt‡™ÃéŽuá9…aâ9›ÿ±ýKÆãàߟƒ8žèòû©ÔóXÛÑóàz•­ÚrðpÛ›¤æyðò]¿~“KÂL݆z©Û÷ð¡©aÂy¾‰¸þeyÊø"SZ¯9×~uûrüsì'¨Ê"¢jrò:ÿ´#Ÿ–æ¨=Ìò`ËóÔ+íÝ€ßOH &õ-w5 ÏóØøÆž›yÖk(ØðMf„«Ó ŸÖåøRJì'PósÄ(¦¼ùgåÂãÉ×gŒò `É'\÷ç/ÍÛ?ÜW%TÜ_füKêšÕ#G=®.ÖOvŽ­ËìçW›½êìþ¡Âº–ƒã­í}ÿ%LœHx¸¿7Œöj{ëAhÞºÊË{†ˆã«ÓÂ¹È fû¾úûr)ØÏ·f_ÞÏÑ·½çìÏÁÒ‚oÆ'ßäÂ+ï4_càÏ4dBû MÑ«ZžçùHV1*~\ÿ>e{쥲±)ÇÔb?¿\ ÄUÏÁü9ÜÞºþÓ\¸ö¡yg×0Åøfÿ‹—4äHóÉ»oº„ˆ÷0¾Ÿ÷ñû2 óJ›rû¤ìÇ3íe‹u‹áD•Ùê`jé€ñ¹`ÜWÙ~Îswø=¡öYÇ$ °wWÐÜ)eŸ‡íS3þ5㣱s}ζûÉüÔ<ªÝÓ%‘b¹ðkQ󂧸 êMj°Ã †¾Q’Œý°y;{?ŒgÄö㯚Í3tyƒí÷Õ%ú2Hl²–k©ä ðJxâŠÄ\aï ‡–Kª¶Àz³Ûø™ß“à±°ó5þ=_ß ã¸3Þ‘.oèó:ßùþáå@w·ëÎç ¢%+æ‚j\Ó˜ …kÖÏ*ïz¢!š‡%Q·ÃC[dzçÅŸƒ?”9*‚ÎŽ[e$®óWS—7ØÏÊÉô |%”}~á±ëÀ½Õ»5ËraDZ†E˜ÇPþ6EU¬!Éí|âŒ!„ÍY<óüÉ7²-÷ìÈ£q_Q=®Æ~öÖ¶ÿܽï*°¨Ö‰»ÂÁç÷dÚñé¹Àõ¹UÑr©7 ÛMZ á÷£Ùû/‹#u‹…aëê¾ÎÙk ï«7°ûº¼Á~Î4ìŒ%~ ´N‰0„F|Ìþè› ·6´Ù6{³üæ9ªMÔ) Éh²fšç-a÷Øz†ŸŸ>”»úµ–±)–1..›'”›¯a?é›oþÙ=i ,¬ñzŒù¯;V®‘ å^ÕÍæ¹À‹ÓÖtl¬!]6œ÷ÿZ=DÜ—ùYÏjžW‹ª‹µÀ~r²›lnËr¶oÕ5êÒ›àì»e„iûÁјÁ³VÔýýŽ·˜u6D¬g,ÿ÷òÛ 1iÌ8Âö ¿Ÿ‚ýèŽ'šn€.’^#ºâó:þû¶ZÉOràöëUÞ„3ç–|6¬¥¸w!„ÕÃòœ±ë2þœÁP¸_'-w_A‹ýDeÒpüZeáó_Î+võÓdä@§ £¼úz‚¡O†YÛÄÜ÷SˆëÓïë3_G+[ëÏÍ åƒv¸\üÖd#Ĭ{»ÑRNÖ¬UÅù\tQã®ù¸¡`lÒ!Üu`<é=hôÍ̶óÄÏÃÞ/[OÞ7< MhŒçÌÆ#]¾`?û~ëð~ÔüP`÷‡}‡•,1|+6NK(qy8ÐÛ©Öñd«m¯gsßÏß‹kþý<–%ǙԻ£­-¾ÿr\.ìÇ7¹ýùE'7—\§ÉÂqÜ|9®2Y—ï?æm®=*œ¸°±jóxâC— éÁâù{ÏŒ/ËŸãÔçñúŸÇûÙŸýv"lû©‘ﶯâ yogS»9°)ªah¨ßhh©Û0'UmVk:Æ«cuš× e×´yχÛÕã@ Ä~tÛ±76Áù>¯ Ÿ­Ç:óvšE ÷høU1$Êf4”œ¿¹¨Ö¨xòˆnl&l?–íDZùí‹.÷S Hl·®ñü>ÇGýÝŒº OßÄAømaÑv9Â9ÜH0­l¿Ï]<¡tô黿ˆûü,/ù:“&‹4Ú¹Ö鵄EU%gJËqsS°Ÿ™%ÞßütxË6w/°êf–ÆYwZ1nUZís!1žüv6¿Ã„þsE>'‹3v?-í±U»}û,{ê9ÂûýH»ò¼Tìga£ODÂ5ùøÒÏkqf¸µ×ÚÒl¨½,íIî—¡à¹3¤ë‹äx2iè±Z!W爼l6?g¼TÆmfç×åòŪP¾)tðã#¿GB»[¾j¯æà·.²WÇždƒ—w͉Vó‡Àv’°3ñ$þðpÿ «‚Åõ;_bû^ü½Œ/2ů+õ9»òó2ìgABÿui7#¡j5dõrúr‰ix;ÎÕî·5¤Áh°(»®õ±xb6³Û•¡[f‹û1lœau™ï¥âù"û=tù‚ýì¾;ªw¶ÝfX.o?óÑbAßÕu’³áÏvUÓÇh‡À̦ƒü»%Ä“ˆ)ô‰Íç³,NÙ¾<ߟdlŸƒ?׿ë¦?ös´Á­¹r7CêÂúý7âü¬íÙ­}c³a^¯!+ê/ô†åÎ3Š‹vÆ“_Gîêú|¶¸Ëæìýlz]ùü[Ùã>6ÚL׎"OO—/ØOqfÀºÐð_¡«{©ùlÌŒ~]Øä—l¸þË”ês±žmm³tòÒxÂÏ‚ /Ù~/ãa§—öº·wØ[™ÚÁ,VzL l×åös¿Ú™¯Ö²-`m»pí‰)‡ÌN†qp) ~_Kx_sÀôz¾ñdK ,áÁâ<ƒÍùó¸s2~½öVÆÖOúç˜)ØOƒ6“¥5ÏoþÞ*;O%±ÙÚãÍõE>°>.ôãIèýË7Ž ïm³vG•Í;ùý{›r÷.µØÏͳ|wé@}´vÇýk¿l¨LÓó¼l˜c“Ó&^¸ï,ž£°õ»oÄÖl¦¿¾5°.”×ÐMÀU¼Çn¸‹7Î7M,’Ûeà ×m^ôòî7Ä“ o?½¾f¶8Ogí°sL¾}õ4zTÇ¥½xî Ëìçsä¸CIßTP©©¹¼Äƒ3*>\Þ0ÂK×wŸRg¼nªþÎÉÍ9ÙÃs}Eqßí«èòû‰m êÂÉ3ôqàÿÄ©dUÅl¨â×eYð9o˜éÔ©mß‘ñ„/f‰÷Ø<Š?y²¶~·k¯$ŒßvÂ|ßôÇ~Î>Ì>UeÁVh?ûd[›.tš~Ñwê«,a¿Ú>_1·œÐ!žøvÚ±£ÆúY"¯™å©x%œ°}DýÏ£Ä~ä)ÃNéµ ÂO?~vÙ׃–¥¡Á÷²@¦½º~çÔ!°fêÓϱÅq¤÷ÛÒ‰½f‹\Hvo§ií¶°‰ûSä²û"ºüÁ~Z74ˆKš¹Þè¶a8ˆ¾àÕ)ìbÜ¿m÷´ágO˜{¿êS¯ËqD³ŽnÌÇéò÷×ïÈØ÷"†ÖR­ðQ*®KtùƒýŒ¸œôàAÌoðÒ¸`¹S–­í² íá,ø³ëY×0¬Zý•ö$޼š{¦Sål‘CÌâÿ°ïðã£x>¬ËìgàGÓ!ëFí€nÍ‚ÓZX`¸öød•ÓcóælÜá Š-kJ]‹#$ÓÔbËÌÙâü†Çñ÷³Äûz‡ÆY™xî ò;uùÓ¢P>eqÏm; ®r‘Ûf[j>c™½. ’›9/X\k¸Ìéñînyiú-q¯Ëlñ^ÛÇ`qÍÞ¿ïÙ±Ü}| öcõÑMÒbî8?f’Ö¥3ÉwªÕœ’šwnÎiã >2k<G0Æ”} gGlcqÀ¿Ÿ/"¿»Ü¹9öóaÝPÙW浚sÅ‘ƒö½{ØÏ—µv>ðL3F©÷¤TPÅ‘õ{?'Y"¯•ÛlŸƒí§”w°ý€9ñO÷|Ø7\’.α½ è5 _Ôßàk+"F¯òhxœ[‚;ôÈüp_Ò,ŽœºÝÑóuìTÂöëØ¹=»_—'m¼ßRÃ~5¶;øæ©ƒj©jè (‹—/1jés¢zĤG= ·Í¢ãªÅz­~ûÌɽöæâ¹4»o ‹cl¯Ä\ã˜>\ »f ¨ÿÁ’ƒjA¡ ^»ÜƒýŸÿq®£X.‰»oGF>H­¸±Ïdñ¼›=O~èxŸ…Ý_`ûຸÆ~øù‘ºÒÙ£×é!¡O:c¼í)œ4× ^,Û¢½[5Žð÷­¦ˆó&¶ÀæÏì>¨.Ž[bžëö©£À"í¶ÙT+v´Î·¯ÓìtmqqêôboÀäh 2‹#“*Îù#zŠ8Þ³úÅÎo^¿ØîáЕÙQc¢ r£©Ïº·æ@þ{Xɺê÷àƒïø}G¾…w‹ípnGtÛKw§VgËŸk¦ ÷üŠtíʱÝÞY튂ï¾ÞÂú·2§†ƒö Žô†ÚP°Ò@qyt*òdÀTqÊÖÁì÷ØÂèÐÇŠ|½Ævß÷M.8%Ü“áÀ±·±Ó®°»t¹ ¬{l×ÚâXRB±äͦ¶dûlÿ†ëÊâþþ¾´û)®¹üfÆÙ(0<ôôœ+Ž×ƒ‡dÿy 㱫ۉ¡PÉé«q%ŒC~|ž*ž‡²¼iÝK]˜íX" °¿eg÷{e`çÌåÖÙØÏÙéÜ1ÛÃQpá—Ük8µZ—b⹃n•â¹Ã•Ù ,MêÄ~¾3Yœï²}vO”ÿ^“¡x>¦‹sl߯ç:mð=ðûßLß\ÿQ“_9¸QÁûå£@Þ¬©ó¶AY>±ºÍ~O¶ïÅçíŸ2¶¢‹w[œw.·rÝËEÿ¬èÔáÎ4¦_sY‰ëøœ=>Ö9nÒmX¤ÞïÏæyl>Èê?Ùù¸þ¼S‚ý,Ø<ÿE{÷Ýp°}¼,¢ F²Ï,ž~÷@?Xìãyó•ÿ^Öq¿“½Oü÷Š…ïiZ•»·+Ç~îÔN¹þàânÐ]Û„ñºö¾zö4šºÚ^Þÿµ 5­8òs…8²Æ~Eמۧv~ÂöƒÙ¾zõ=–þðMÆò[—ØþîÐ)þñ•¢¡ôËØc¸Žò\üÊÆËŸƒÇ;«Å\=Ó7öÌ×ó­Gïh§ði„=vÈÆi[_ÕÍŠ“ Äý4ýs5%ö?/©I£úÑPÉr©ÙïÃqÜŽšÚâ&®§ÌXÔ¾QÇAP#"°BŸ¶qd½ÏšÁ†Òé„ÕI¾Îµ¿çÀÿü­ŒŸ: ëmþÞ¶û±hAwN¢¡O{Ç¥d4Ö‰æáp|ÍéÝîz‚;lï¶aõ7§8_oï–]™&ÎkØ}FþžEŽx¯…_çvîžþ†øuT öScãÌ7£a\›ÀácÇsйÁô§:pà›îþ-Ï&tÈägGVöß?¶xЏ?À″l\äã»MùsBìÇjÉÌ BU4¬ºÑqûT6ÏjÓ ×Wb{Å|ø8DøÞ[,ñ­? Ôãî$Ââ”ÍgØ÷„ø~‹dl¿]þlЪP¾^~zÿèÄhÀ—¼£U0ßòŽOñªÂAz½:¡.™CaúšŽ6Å’îg¿>Ï92IüþÛÏdãCùzÀŸßI°}ÏW²Ë.k¢áጰ%ærPùCLÀÞW™Ð³‰ºÁbE^y8)–ìùR+qèËÉâ>!ŸÙþ0¿ïõQÆÏÛûÞ˜._°Ÿz2¿ªU≺Ispžn¶»À&#è­ÎÔ¾0¿÷€UÖ¡±du0ü.)â=Wv_—ÍX½dç\ìýéòû9l-ØÎ/Ž÷ÛT?ÏðÔ:k»&gBÕ¢ú®î¥>PR*í¥8K"ÞNÂ’>IÜ`õ˜÷´âzƒ}ÝKÝcÀÿùÛÃèo£¿Š‡‘ð™S xO5ÊKøwx­úF‚'¸Tð/X„‰ß±õ=ÁÃÞÖ÷Þ·GFßßñŸùIÿ¢=?ð"c­Ä_à&˜ IËZñOpÇÀ’ažàß{<ê{%êñ¶~Æme¼-}nëÏ<Œ·õGJ=në˜[)‡0Bð/bÌVÊNP Éì/xKõX[Ô—ù¹ù ¼Vê]Ä2”A$x»I¾c¶Æ|çïø#?p¹ž¿#e¶þ]#ÿ®‘Jƒ¿V4>Sªﻤü7y„Œim.ø¼¥ >o*›à#ðZõy[ú>¸WÆ¿f™e¸ÀjõùÎãí1­Ó·Áãyà–¬V5ÊHðjJ˜„A7Á\à&|ïƒ+ÿÜæƒû½Ï›RRƶNÒãËüŒIÈ3úLŸ±­“ðG>oá“ðgŒ™Tµ¥˜1 ©G¸Zàl þ·ŽzlLî¤Î¼/T À$¤LkÆK œ-…àeû—0ñ;·yàºêy¼Q.!Uú‡ÖHZÿ#µQ¿.þ«õðïZø×®…ÿÕuÐTøõ7º„âPr ”)Ö=J‹rÅ LD™c`*PÚ*å=ç«>K‹ùíú|j[=FL’À_¥,æC—‚²Å`Ž@• ü±Ö¥¢¤Ü*!À}P)õ¨Ïþ‚/åh)ô<-U[ÐÕœúDð<W‡`ŠI؈~Ï–÷é ú<æ×«ï]ç*ÁvPæÍè]·26u"ÊÜŠÞKÀŸ¡Ü1±’¬ËØÔ(wL²$º7K÷%ñg(wL¸$[ºþÆ¿‡*BùüÀÇβWQ’Ÿ°©)#¦¤ÏÓJEI©w¯¨Ôï;M`i©„¤ ÔcÃ0/K¹À£–ë1(G+FHjÊLD™cr+QÚï¼ð¾÷ú5ŤW~xrà™‚Ïÿ®Jƒ¿Ö|Ð\øÓè»|ÈÿÆ ãSSÆ Bðý• ¾¿4ýþª>GKß÷W%x`Í2oνJ™©()½êŸð©)cP*ð©Kô<ibPöj Ê$Hð,§¬A…ÀE\„ï}]ÿùþ Ü1±’,é÷ Ê3ªõy1?â êóbô™ƒ?cT3æ`8ªåƒ š‚²Õ”9ø3^LšÀÐR ŒjÆ”c"Çü¬ ÁïW®ÇñÇäNA™ è)£šñ(?K‰â?t}Þ åT›cP¢ ~âùK9Õ‰(zrAyƒ4NéV#ÿúÈjã´&þ]ÿwÔÃÿªZ(1àY«Ôÿœ2¨M1(-Êë_"ÊR‰*@¹c`&ѵ1geÃ`p †Ÿ±²˜±¿À–ê±`R¶jª„2a0 SQR j•0è¢ÒPŽ”©Š2Â`÷G¥¢lͨç+ïSLYJWŸú ò^ç”èŽÉ#°(c5 eމ„JmÄ{+~Âp`~Ɖ(sKúý:üÜ(w|pIú}|_ÍʘÓIÍé_üªåcM歹1§‹P>6ôþ “-U„ò±¥gXø3L¼T Ê0 %Á$ G¡|0SP¶?aN«„¥œ¬4”#õ<¸Ôçœ8YÔÙH`2LªHð8W ¬i•Д••(xœSf`J‚ Ž*@¹b¢'¢Ìà‘lމ¯DiQ®À3ÿžþ]•­y¡­ð;qôÝ þìÿG±§)GP‰Ò¢\1ˆcD ÀWÕçe¢ÒPŽÚ<•Š’b «„`§,ˆ4”#½úŸ°§)CбÏŸ¦ áƒJXY”­šˆ2ÇQžî”#¨x¶¢åÚˆú`ágxY?ãAH0¡ÂQE(Kú]lGRžA­Ï¹ùGPŸs£Ïü‹šq#P%(LÐT”T`Qü„s„âVVŒÀ¡f,AWLäD“E9Z'#$v *%ÁW¬)( l8ªåŠƒ2ÅàUüŠñ»¤Ð*!¨öô÷¬œT±ª‚=•†rÄ W£Œ0ðƒPJŽ ƒ2Å$D¥¡¤˜ áfÔŸç|…×§ÞÞø;brÄ âƒJ…rVS(ÿFJCÙ6¦>k?æa Ü1™’šRO lU„ò‘Ðïhã¿ÅäŠhVÆžNAÙb¢E J¬è÷Cð3Z—±§KZл¿ø3”0UBë$&b*JŠÉ¨2•‚²ÅÄŒ@• ü1ASQÒŸ°§)+Ç_tŠCÉ1ycŽ ¥E¹b"ÇPކÀdŒªå.0)sÚU`l9ò ±$”©ÀLAÙbâG ŠPîX’P,JTÊ‹AJ‚¡å˜ô)([Lüpýæ’ð,ſ牽Zø}žè*´Y@ÿ?¦úßä@26å@F ŠP>ÄI(s d…À•c@Ç L1¨(-ʃ;eŽ®Dq(9zŒÀœU ´(W úÄÂÊ.Òce›bBšQ~þ9›‚²Å o@ù@<;2œ²Oxî¬ZHT"ʨ1õE§Þßøï0‰"P%ÔsŸ JŠ ¥’*•†r””çisͨW~žŸ0&M1ñ-è÷–Ë3&ÆÓfœI5Ê“3Å¡äO›ò&(-Ê6eŽI«D Ü1y“¤eüÉ"”&r Ê“9U„òÁ¤NB™bb+PJŠ Žâ·U€’c«PE(×ïx•”Ç-Å"  ?*%Å‚ Šer§¢¨Sœ ø\ þ·×GVYd5ï¯Xãþ;ê«m´Ž±šÅjÕ¿R§è31źdÈs±m1@"P%( ”T”ƒE%L * %ÅÀQ ÁãƒJBI0ˆ”(-JŽÁƒ2€ Bq(9V ʃK!p±]1ÈÔ(# ´ ‡’cÀÅ L1è(-ʃ/eލD Ü1“èy£–Þ—¦wbèÝh Ä4zÆXD×¥Ô›?dª¤ õ:ÆÏ†’bpª„ D¥¡›QOº2¶uÊÑŠúáÏ0pƒPœuÛÚƒ8Å¡[Òï ãÏ0 ƒPœ-ý~þ=”)wÊ\2 Bq(ùO¸Ö‰(s ~%ªåŽI„’`m G¡|0)’PLŒp”늚~'„ÞeÁºáˆÉâÉ’J×–X'JhÍÀ:‘Š’bˆ@• ü±N¤ôÏÿïó©ÿɹÔ?ª1Ï¡øÚä#ü›"úL1c„€ D¥¡10Õ(# Î ‡’cÆ LõXÚr Xª囂’`ð*QZ”+q"ÊY‰*@¹c@'¡$Ôá(-ʃ;eŽ®D ÜÿÖv‰kÛ“ •jFy¶ø¿()&Dª eâïŠ*0§4ü,(£F”½„ÿeŠÉˆJEI1iTBâ¢ÒPŽM©ï9þL¢ gI}|ËøÜŠfÔŸ? &–)&–Šú‡ác‚%¢Ì[P?üoL´D”ùOØÜJTÊ0eŠI¨@iQ®˜Œ‰(sLH%ªåމ™„’`r†£ŠP>˜¤)([LÔT Ê6%Ť@•Ðï¼aò¦ Ì1•(-Ê9¥ßá¨"”+&¶U‚rÇÚƒ2ÅDWœoGLx5Ê“>•†rÄäW£Œd<ë; åH÷Ö€Ï >ìþSëß×\‰Ö8ZËþ/ÌXÍúÑœè?Zƒèç2Ǻ#ÅP ƒa * åˆõF2ÂÀBq(Gú zgŽÖT Ê–~—U€rÅ 1Å Q ´(W žD”9U€rÇ@ŠA™b0)PZ”+U"ÊK‰*@¹c€%¡$dá¨"”[ Ýã§÷…éîÓÓ5Y*JЦ‚-•†r´¤^ïø;cà¡8 õ0ÆßeŠA¨@qÍ©ß$þ eŠ©°¦¾iø{a`&¢L186ÔS‡úÉàßC™b *l©Ÿþ= ØD”9-‡’càÆ L1x(-ʃ8eެD Ü1 “P êpTʃ;e‹u"UB×Zì¶X#\±6ÄÐïzÑû˜ûrLµ0ТÒPRÌy•0è¢ÒPŽ˜ó*aˆJƒ²»mAÂ÷ØŸÞ½k`¡üô¯µB»D‹¾ªË#w ù̉~ÐÖ{7x³7Sð9;"«úþaÎÊa‚å+ÙÇ“5¾V‰#ŒSëAŒýožHô9Yjì'ùlŠz…Î[‹ÝžÂÁ–ëÃb×gÂ%ËϵvµöÞO3Žô¨õêFÖ´‰"W€÷©*ú‘0ÿ#ÞÏ…÷+JÁö £¯n?t7´Ôj8p]Öòmh&Xk/YœÜà)ø÷Ä‘wv}q­=Iô+å}fª‹>•¼¿ 'òjyŸÊ*¼¯ö#.½i7P7ñRw4Žo29¤Ç޶noïiÕkøæ>‹%‘)O,º×L˜ {¾Ì‡†žwe¼Ïœèc©{1­ åY>«B†ÞÞ «¯4öâÄÁ×N©ƒ}2áõÆëÞo,<`ÒåšdûôX2úõWÅá“óç`¼Þ鑌ùò>ŒmÊùˆJ°Ÿ»™o:_ Ôä-¢UÆ7ؾo&˜¼šÝìóE nÈ›«ÆdH a~3ÌçŽçR˘/&ãaês åØOåE•š_¢¡pÁÃ)7Ûr°Gùu­…}&¼Èµ|íÝÆ ά{boz ZÜ÷ÉF$ŒKÇû‡·އ浌ù 3¿šr~âØOïÞ§7†ì^¯šðhÏÁʘ&¾C,3~ÉØæZ’—CzQ{·‚ï‰-0æ‹Ê|X™/W9Þ6ös“ÚªTÞ Lç½kÝ‘ƒn5zNªhœ —S6»l ó†RŠ}±ˆ%®g½R7‡N ŒçÀüøùx»Áç¶çô ¢æž5{aë2ãJL8cþǪ÷pnýðÓ‡o 9ÍϱŸK¬Ì.Õ_b@/†ÅïOMÆühõýÜS°}c›¯¶Ï®í…D§Ü]90¦ÀàÖhá´*^ðdøƒiñ±‚/Ó8ÂûJDŸVÆÅañË|ëôùšZìÇqbïI3ŠöBF…°{:q°wæ(Ï)ibÜÊnîêw'–ÔßÓp;ÃãD¿hæGÈüØóaïGßÿÞ M¡|×eÛ›§-öAÑùž½"1n7E»å¹¥dÀêÇ¿u~0rÆ“WÁOcÉkê2“Rö¼Xüò¾ŠÏdCJ—ùìö©Œwθ¢ºüÀ~º¶ ¸}‘[Ù­:Æí³=F'«Ê“%ö;zyõ‡I­_|K­2¶:0^ä01ÿ>»9¥§m¨ |.#‘«Ï¡“c?v~‡ZŸ¸f(·¾Ö¶äàÌŒù/·îÍ€Ug…7øÔ¤Å._Åžo4Aô‹c>DÌg}ÍÝ‹ÇÏn«ôC|ìg£I­1¥ög\yjU+¬“7µGlÏ€'뫹Íí />n{|èÆAøxû•{Êâ–ùê¬9í‘¶iê3ÙÀƒGvÜå¹çJlw­Û³Jjïã푚p0¾ß-çü’Fmg|‰«ã‡–ä¯0?K¾ª2ö<30¾1ó»ãÛÍýstyíæÑòPwT_ÚqMVŒÊºZ{]Ô}t0±8ÎtXœ-±¤B«»fO ¬.±:ÈZÁ÷Gà‚µaÜ¿} x1ñ`=n„Ì·s[‘Ÿë*Ví»–.=³*–d{V é¡ 9w,/øøü*ÛZý× ýžWâãÛMy¯òÒ)û –sü/öØn©¯MÍE‚UwÐÙ°†Æ’^óúýfæQ–gÌ¿çdWƒž×_ss+‰þBú¾Hm 埭çq½'ìƒ q6á'kq è=¤™"ŠîŒ\8@=NìÃŒ‰%¼Ÿñ8Ñ‘åïúMöaéÕà’Ê"/¹œß$ösg°ã©£öAÿˆÚî÷8ˆzt¡æþ`¦Œ˜ûÒ¢7|3:¯l1,–<ùN’ ú®1þ7ïgŸ/ã9:ßž1ï»%Çö+ý±oÄý‘ûÀ¤¶Ãï› 9È·t«µ3 ¬ö\‘ÜþÃl{Þé¶g,Y?7§èTáÑŸŽùÖ2ß8ÆOáçmÊå±?ösz%ðìƒû ¼Ûùš £GÄÝ®?,z…7rÚ¢ÙbȨ1±¤ž‘ÓŸž¢¿óáåëÅ2Æç`ùUn\À~ޤÎMÞÕèwh@Z|Ë„ëõ¾oÜX@5u‹Üõo´bCx,¹}qßæ^’‰¢#«Œ7Ãü’y¿sÛrï_ýô®÷‹CʵßaWd¿šÓ 8XzÊçL`§ (ÞÔoاF^ø­›Ã´K±$|ùð;§ˆŸ§<ÿý–Œù£1_¶rþߨÏMÞý¹10Ú8tF |?•š­—dÀ‘lÅÙâK^ðÛê!æ~F8¯r IXöj¬øþY¼ñãh‘ŒqÏ^?δØOd§E¦Ü‰[O£×&áçù²îÖæq53`iSÉ ú žÐáã®Ò\'æ+;Fôßd2¾žVž›ýLÆøÂº|iW(_§¬îá µé¬ÛßÙfï9ûÒt0yëž½ÂÃÎ5ÿåë¨8"Ùâø§<`ŒèoÊxü|­’ÀK.üq¥åê¸ûÙtˆ>àX ÔM‹Êìè¹}à‹tØ»©hvÇ–ƒà¶}ÁqÄËï™ä@Õqb<³x²íi]p·ÛaÊ×A9¶[ïeë=¥{â@gó\“ÇñÓ“kç¦C±Û¦§Ÿd`Æqëª/&dz¡­cb>'ÌߌÓ<§4]Æ8¹º¼Àvû¤–u‡"³ÜÐXGv*í½‘²Í>ù7ìg Û<Û%Žä´j¢ù6;0Ÿ]ÆecœHæç«Ël7o“Ebü•xÁ7‘ƒ‰§zq)épµøÀêÅH¤¸eóWæÏ|yžàGÿß¼ÿ«ÛÕÄW8jÞ]#ŽÕ¿qYÓ÷ûuók¨ÛÍ·uÆMùœŒ?Ïøz|}å}jS°]>5ðÜðMD+Kœ])øªN‡’Ã&_®Üð€äÝ~ {Æ)úWòu®A5Y¶ê]©å{q~ªÏoÒb?Î:Ã| Ì?fbòÞ†ƒ´°qFëÓáp}ï9}ò½`©{Þå[8o<1do»ýÅ“D?»iÞuîUõílÜÍùv,÷¤Ç'Y9þPûBùHÏ£¹•ò5°Ó¹aÜØÖÞ IÇùð‚ÊyCd Ì‹%7kl¿·ý$Âêã‚ðu(WÆû ~Ð= ¶Û¯]ä€A/5‚U-Û}bWïl¿€t8ÔŸ’Á½á¾1Ç’Ü´È Ý'Šþˆl¾Ë|øØü=}¿R9ös÷ÖÌ·Ú$ÀMÓõ¾ï­9X0h±Çàt€½¯³Ü† ½ô¦Xò~[Óz‘»Çž“i'ú’2.ÏŸ}"Öi}N‹?ö³¾ªß‹÷¾ Àó 1ŽŒ'ï_å”–ƒm®Üèâ Ÿÿbv>=–ð¼«ñ„çØç³Z,cë`6¾óqÀû=*±µÇÉ·ú%€É¤!ڀƔÚyFµJ‡ÓÏh  †Ý·š\¸Q/Ž ò]~Ý`¢°þ±9š|‰ö­Í>6J‡›ÆsF»Á¼A‡å§œãHóü½w[< }¡ïפ¿[÷<½vX”ä¾óÿ±wÞQQ&Ùß'@Q1£ŒÚTT (¦¾m ¢ ¢`fŒˆ£` j›1cÆŒ8J·Š4 QôéQQTÔÝ`E•±Ç0¢~÷é§ê±íÙóîü±g÷]÷œïÙsfg«è§ëÞçÖ­êïç³îé¼Ú8Áyâë)§klã }yæ—mMKàÇQ,iÝP—ç¹É Œ„’F ¿^t>Éh·ñ-ýx.Ô…u¥i›t%d!ÝŸrûâº@ý9µq‚ó¼]Ó±­¸_ø<=‘kùî¶²çÕ2#äîsºüÒÐ*\YГLV–{ú†‰_ý˜)”û<‰BÊU¢ñ©ËQ6èY)bi¾/œâ`uµb룘d¹dl³÷Jàø;Ã`¹‘5–~2†ã~õ—¤qÇí»2…”Cë]mÎ3õõÉ›~+ãÀ*sˆ‰áûbh±vJ³!%¼³p_1ÝTÓ–Ö,.ˆ”1egK¦ õãýÙéûÖ9´?A9ßÄγeüŽm÷âÀIÚÊÃá÷b(º¼'´N…6ZÓ½à,À-èPƼ· »tÛÏ»4P?hê“Í}ÎÞßð®|pž½ãJ̆žY“Yþ]žƒƒcÝ?=P’>ÍpXõ¨÷¿h3céÅ&¬çñõ3}~œÿî3>PÞ•®_ªçY=ï·Šég Ç6;«Õw‹a|õÖÀ¥’ì§œ`îÉί·]1í·§üÞ¹ë<žoAãžî'(ǘóK¶ãýAµñƒóÜèa1yñÈx[ª*Eêbè9uˆ}åU%ɇn|nêþ2¾Ž¦û"êÓýböóµ±5BØöÚ²Ó@Þ^78þ ÿIVªxÀ—vÒ™‚b°Y$ìÖ.M ÌÎsK짹ÖÂÖìÇýý»%W§Ïã9é´žâêšÛBêC¿Èù¥«û~듌óT*Y šV³4îüb\sïè\ ‚zå.‰ ï„ãÖÖ™.c²ŸÏs~9_gôû§ñBý»i~ø¦?e[)2h}ÜõË9d² ”bˆïVƒíJÈŸ³6ãóXT™|Ë[Ƽ’‹çíþ™¯só‹œ.aÅZçÒ>åûhãçéWëꬻJ9¸NXg¯Î*æø½Ë”°ì—Â}Y*¸xáЈ5+dŒ§ˆûÕ¿šú½SÎå…r>Ä}xmmÜà<7Q³µ…bX·“-Hq‰šïèü‹:Ïew®2Æ~ö­Š'_}åé{çð›V§¬Ó? iŸÊí“XøKßo¸j>8Ï™"'×ÇUrX±6g÷Ðäb°­Êh2Y ý.H~ã }ßEjÎ32æaNüƒ_f(·‚Ö³3-§¸…ÖN”†ç›èúÿJp-þ둇jªÿ8S fm·9ŽQ±ǢFÉó@’vÊk;îó-o^ízõg~ŸOýqi?>?ú½é®ƒhœ‡ó%—×çw϶Š-†åÌ%O†+ÁÎŽU:ÀGa|È3s¹ªa‹çó>ÓÔšË3BêŸKû3ºýÎóÑò¬W»F Ðà£tݧÃÅp¡¢,Ãb€Ê—8 š¹8@`Å›LËú'IžYÀçiêŸKû­”sBßãô=§v]ož“Ÿ à䟶íîþbXò0Ó¹M%ü:óMÕ‚xGðWÖ+z‡ï´à ïøóù†>ÚÇàö ¥Bºu}û zUІ…¶¹xfLù ßW 3æý<+äG%ŒªU{ÀêÖÎPÖ¥Üe~ž–³*² àùõtF÷œ>bä;aëúžænÏÞ u9?÷Y›ë×%Àþß=¦í)†_û­7ÿØL ^‡ÿpiôÞ B¯})y_Æ î»;>\WR ‚½¦­·«j´ ûm¼àøïÚû§HH€ ¶ûºZì.†Îu}{-%øY<1Â8¿v£ «„ 5Ú5í’ûd¡=´~ýÀºm¦%Ù_"}Åîßì?}pž‚‰OìS`ÕfvW óZMóø{yäd»Ð\¯ß{Zí+c6g¨ïÇž `¬GÆø\éËçgÊy û)§cFï(¾ísHpžsBÛÞ=”çeNkÖCæõÛ'BîAâÍa£Š<¡kÅ$Öÿ~LrÓŽíò\jº¿àöí·x.”6>pÜV‡Vnß›÷'²Dƒbíü±¢mv¼XžÑÉ9|,¬ÌÒ©¯Œ!U˜ý‘ã‹aáBÏ1{Š€Énn½xˆ+\Þ7-×p•ŒûÔý÷#¿rÑè>ÂâÕŒÎ-/˜÷ž%uŽûÀ}ưœŽ 0Q»@Š¡Y?·Ê“‹ ëy’îõ!c”Ûªþ¿È˜7\_gÈÐ|Lßÿܾ¸6pÜ0#èÑèEuOÛoxD"œ§å Ÿ6W1ÿtIÚ©Õ€b_¸1^º¢j&iÆÈ†ÁñQ~¾Ær£ÅgOäß«ô¹ß·ˆ½¢YV ÓŽ9/4äûWÚuŽã? íߢYð›7¶abø²¾¶‰mP°T:ãC!oû†’1Íö>jqx C9{´~ãú+o…ºõºÇ5=±‚«a×OîX ÜùZL‰Ý¾çú¡Àí“q¾/õÙ@ÏÅüy}ŸÐ}—×>pëÇ­½ºyÔ,âM>§îhS CŸ(˜Yòþ½:tñSÙŠPFög Ê¡<0ʹiZònðíÜ9¡ÇÝy©ó!'Ûà¶kÅÀ°yb¼~á6gÕ`Gè»òǽ^H™Ö½Ûor8€´Þââ¦PŽ¢v]ã¸1I9?6›”Útݸ¦²¸ÑÑEð¶¬ÑÛ…V®p§W WЯR&lÎÙqÎ3ýùþ:GîýÑ ì#Ë¥;R¸<Þ§RZ7¤ð"ÆËöäua8nÌ Ep嬤µIS1p<#)s/qøÇK{0Y7ŠÒcvÙò¼¸yýú9µÊ_£}ØoÎépÎÇ?z©8зA1¼oªîУ.-c÷ ØqÊûã )Ãí+ýùs:šŸ8^@m³4&þ÷Y„ã7Þ=wCôluÕé|ï»6¯UøÅµ}²rù8Xqwùû™YR¦¿çæ×ýðë›þÿâ‰ðõÊ¢…Ï;pwâlvä .†iþ§U¾ÆEÆ\ž[˜:f848ñSµ”áÞ‹ø¾Ÿë‡©É{›[wÕ qìàŒ8Ø·ÁÆ'†Å°ÑábÜÂ…ðtŸ[?åýñ0ýíH‹:62æckçUµ—ðïi:>í[Q®§v]ã¸M¢Oì‰{›‰ªÅ…5jð=ÜäJmu!°ø>føîêñÉÕãkœÓuBŸCõ<¯-×_} œXÂÂqŸä­z'Û-O¨VCå­m~-„½ëº%„ÇŽ…ƒ—òEøó)Ÿåf£ ä¿?Z¿xj®—oÚî¶ì–U¹¢¿/ø†£ˆóhÛ‚I‰Àv‘&¼Wö.se)„ɳ&w.àv/íF.’1‹{d›õ ¡ýeÊû£çê1Â3/!‰+3²oæÎÁ úVŠÂ%¿ÝØW•SqóYúF µm¦x.„¶é+‚ ŽºÁgŸÒôw2æˆáÛÙÊð¯üV.ïõäë>ʃÓ=gàø]âwçOi’Ÿ»Œ‹y¡ceÏŸ­# a‘W½»ÍǹÀàûå©3e„ÿÈs'麡ý&ÊÏ£÷¸ónÝã<·|Ê:ä¶K‚+}|+Ž”ª!¹KÎ.ïê#G;\s‚‡O<{xʇƒ£U—ðü Êã¡û~Z÷Qކn]éƒó|ªvÝô¡Ch˹|5 ¼zïjP!¨5_³w­+ä(Ѽq‘1viqÝ÷ äûôý1©åüO$ø¾ÍºÜI Î3€ÅµH‚ÃIµ×¨/©!dì½Ë| !ªýˆŠñÜ´«µ‰“Œw+ØAÃ2´ û!š7)oŽë¯;åÞjãç9Ýî@ùȉpǶު«©jðœäp»ý„BP|Ž´–=BsÂëbýq|v£³FÃøsvú¹h¾£|¶;µÑЃœsôáâç©Ù±¥î¤‰àp®^©ÁŒm3:’÷½no6ˆ±–1™ñ××^g‚ø¾Ý—=òÐøw3Z?­ofÜa¿ÈÜßv;Ô(¯778׿H³öO?­8¨† G«†(„NÏ~&†tv»d(ca#·˜ö æÏíè|”ŸU;ãÀ¨• kóõmÜØUŠF´T¦æT&‚Ûšcâw©¡KóÃe-­ a”ãƒßþðs‡ÓY±yRFPµB’ìÌÐ÷0Íã´OǾÒ÷å¬kãçɳM>ÞðU"ÌÈv|ÔP4èÆÃ³- áHÓÍ»‚Û¹Cñ|qMÜ!)Ãî&,‚y¾7Ž%Ï¡§ým¼à¸«,»Lê€q?ÞºðÓÁUj˜yõùìçµ Á|Ÿök‹!RÑnùç0)£-ïžñõC.>«¸óoÏ‹“ËÏñ¹gO}ù9D ¯b[½+€mµK?Ôr…Û‹÷í¯å/eÌåã«öGñ}ç¶·ívèì[rÿ„{/Kp\·LãÁsË¡‡ççäÌEj°}:#"îYÔäL8²h”#h±^ ¥LlNnÿ˜° ¾ßB÷ÛCw)Ïœ‰¯fç¹ï™WŸþ\½E8Ö8Ïý!¢wsá]eHŽh–’2ë”Ä”@^¢}\ÇCáTÝœá'Â¥ÌÅUŸ,YÏû£}ëôƒ“ÅåšCLÃu»g=2Ê/ÖÝŸ(p•èõ€às‰ÐμbÔmO5Äfô«÷¥¤R{"k˜>6ž¶Ûu{“”¹Ýÿ×¶š f¯1 fìËs9>Ñ Ò3Ê/ÖÝo•â<óVIk ~I„«‡‡ì•9«aN8{RZ\Ÿa ”Ëjué ”9ð4"|¯G0Cÿ^º |š1-Ìêñ–?'Ó®ÿ~•"–æ|':¸>•N³Ç99püðɧÎÂAP»÷ M÷eRf~Y¨¬$ŠòÞ­ø¼Nϱ¸üú»öÛuï¡pž/í:²ÈM×]ç쩆°Š…7…YP¶æÐù­;‡€ ô©xÝI)ã,Ù?1EÌ×3´žþ6ŸW éþF÷\Y„ó¦÷,i{rØ1ÑZ O¼,®Ve@·ƒÎ¢ßM†“|"enMaA¢!´¾iþJ×+=? ëK÷¼R‚ãsõn|^ÎÞ@QƒM—à#Ž@ÖËÉ7'uƒóáç5RFtÇOÝêA0¿~éþÚªsÚ§½æ& 1~~UŸ$ïõ]¾Z4Î3üŒ2I°+ mɱZa«†{Ïî÷¬¿£>ÿñPµz,4Ôzý‡)RF‹ÞÌó•è}Ž[õÞŽ¹á×Ü®Y¯íidÊ×”+ªœ'u ðJ‚k9µ{l³SCÐϵ.+€Ø2ƒ#—7kŸA c¤Ìò¬Ö= æ¹d4_ÓóÆ¡/.ú­çêÍR7&*Ópö…$` ¶Z.ï¯û¸ëKÏ)ëYq·Œƒè÷ù’’ó¯`†Öƒ\— ¹zä9·î_)ÒÜ K+OK‚¸µ|ŸöÅñF^œQî^MO4Ì«;œ›¾43×§Û¹¨ÙÁ|=Hï#Ðñ¸þÌoÜ9Žk©}&Á†a72VبañÅÛ `Ã’òª¸*ˆ Z'ë(e~úØ÷·\ÿ¾G×%åXÞjéáJ.ŸŠpÜ©%ûã2Ë“ *5(¦C¬+l-š<±)€L *†åî«>Û&e†Î)<ñ¬m(ŠöÅè=+z¿E»¾q\,Þm^=O‚ÝŽ9/^[`¹à^îØ6ÀqGñï©cÂkmúm%çò½€òYŸŽZqÀ¯ÐÖÇ<~œ;¼Q¼ŠZy½ú3\ïçnÔa)³â^åͦy¡ü½4Ž‡Ýžï;p÷ë“:¦7Ð{eÚuŽóäŸoé8ô,xí^æUë‹ Œý£DFp~Ä)çïÛÿI™n÷¦Ìþ9”Ï4rýÝçB®Þãx‘ wë°cwž…Ò˜ÇÕ5*˜n5qp·ù0½agÕ££ “ÝYÉ”)]i¸¦QÇ¥ü¹½÷A÷;º}¤R·rÿ5üäø\&%íÞûQê{e–ñóáÞ6IÍ$1}lib4SÊ„xEζYÊß#¡ë›Ö#4i³}¥¨M°KÝç_Î÷ß*¿ö¢(TF‚½y€IÜúžM¤ŒZöjˆUã¥üýUš¿¦·.sªYú^xñt–ewksyÇvää0ãdˆú¥wÖµÔÀÕ}ù°¶xC§SËÆÇqÅ<²jËóoCù}=/£}ú䩽ºNåê%Ž;¥{2— ]Ît­9c¢†ºÓoå´—åÃï’Ä.^!^0¹v¯üäžRÆÂ§Ý¡ŒW¡|Š;èîvùÿlóŽðÍøÏ¡[—ùà<‹6Xa'C|ëvõÔäÜ$Š‹¼Í|ûxCÔmöÂÙ׿ŸÖ;4Néyw>_(/—ëƒpóHpž$¶ÝÐ6¾t”êUW }Yðt>¸åÕ†s[¼ø|Õ0ÂÜëæýÐèûPNðÕ+‹ ÛÖåúƒÑ8n}í— —VFÚø©a‹‹Ã…ÓóáÃÆ¶›OŽã}‡^zJ™—_­1=Êߣ¡yšÞïäú)å\ßÇåîË&Ã…æLýªU±èôÐÑ£óÁqø•cžÇ‚W‡NÒ²IR†ãñ…2ô\Šr¾iÿA»®q<-¾½k2´¸= רJE »‚ÎʇÝ\žú~p‡)ó^Žç+ez´Y|8¡g(Cï«ÑÏÏýä|Ÿ»çh0 Rôæ“2¤²S2ŒÚ<ëCLJ*hüÇŽyñ]ó¡`PFÊä˜1àâxóâá )ÓÿbÌ£;‡òý š·)¯•¾7éúÔ­+8yNÁ£wý’áX §ÝP¥ Îlþ(ße™!q7Ãâv»@KçsW±®¼UY7pû°ÐˆKîÞU)ášò}MÝ÷²hí‹%ÃTÕ„îò«*hu*÷Ë©†ùнðò ÅNGøÐÉmuL¨”¹V{ŽæÝ¦¯ë‘>'îüû®p‹jhzvT ÜCízÇñkyÚ¯òܘ =6¿‰Á*\æ¾0”çC㟠/ÏzŽÝï#í©’ùR¾~¡'='¤÷¹ÿÝ(W^»Þqžý-žÉ/&ƒ•­º¿mº b6˿΃k¹72å‘#`Ý^K3œgY~Ò„·éKùýíKÒº’žÓ÷HVÜ*ö$•Ëï8Ï’Ÿ~i™\“ Wo-©¤áÖM¼ïç|òuϦ„ý¢Æ›,ðûWÛMløFÆÐÏA×?å„Ò~-=÷Öå«*pž-Ë®]YÝ=¼gX™Çãç9Ðîö—ó`Än±<9nHÝzŸ –2²ž®øÑÂú<(ï–æOÅ©Íoß7&}tn_]Šã_¿w¬ÑôÀMÚx“S*Øÿþ²æê±<Ù#°Š¾è;ÝgY¯–2oϳ7Âø>+­[¯ÚÚµm ‘êóy{¸~ŠÁ@Œãv“˧À ³Óªâ{*«V¤Ýò<¸Ñg£H|Í“œßI™„™ƒzãß´¾1NoçºtqSò½˜‚U÷Ý©Vûñï1m|à<©JœÈKÊ—Ê}*{­ø9Ö#*¿ÔÏXÜÒ >ÍÉÝÆœ•2ƒÝfgƽYÊ×÷4Îi}ÃíKë÷;Œ~|œjãç©Uî*-Õ¤€j0[‘¨ ÷Äü)?æÁ´EU㣽¡í¥°é¸”iÌ^Ë/eèó§ûh®®|ùÍ=a7qöÌÈs&©€E¤k[Uðȧ£ªûï7!~ûµÑå¿yCíEz)“r®nÅ…ƒ¡|ÿœ®'z¾Èå®Ï(Áqg¹³7–R¡b݉oV«À=÷@¨µâ&´ÛÓòäéT/عÀµ|U)sáCHŹI¡|ߌ®š—§õºÕÃê7n4Žûþ®éÓž¦û b]s머öŽ›0$*}– ŒƒH¶ jòõ}¢¿î_ô3NuŽþ(¤÷šµëÇ=àÙWýùf øŠÎ·²›¦Ç6ÉçÞ„~6U}éîlùÕXÊ(^l£Ä:ô»øuxæs«—%¦¤ÿ\è¾Ww½”â<Ë:tb—ͦUYßtWAk£’6:Ý„€sÝZ´~45 óm×MÊDo´L:ÆŸÑ|Êñ–Ë„Ü{òÛóvƒA•¢‰3òM®N³l:ª‚‡«+&fÙÜ„Å-ÜôKp…¯Ý'cþ™lÙõÊû0þ¹ÓçCó(WWñëG»Þq|-Ö|n ìŸ:ݦ¨§ îý´ÀæÉM˜¾½y¢Læ ÁÚÆ–”yl–ÕÆÿUCûÂ4qû…;¤î©áïÇÒóízÇyºDÍ­ñJúÓ¦=÷°R*¨ixÑ›\è³ÞyJ±r4Ü ûÝ!,SÊÌq„Ö£÷…ñ¼sZoÒsPz.½ãÆ 'fY÷ož—γÉc˜üäÔÐ^ÛøA/gœëõºç)Ûû!t¬;ˆŽ­2fgÒ}y¼”ßOÑ}½EùÁKÂÏÚψêß›jãçY·°„zc ÍXf¼ÓR&#W´Ÿ Ì–û§‹<`ül—çþ¦RÆûÑÏÒBz?™æ;úûÊu^/½x¤,w ÐûÚøÀy–^¹0óѯ)U>çñóÎUµ>6îˆtºS=L/6KccköØvn¿ï¤ùˆöÝ»NlVPoŽpÏm¤·Z xº~(/8O‰ª¼Ë©Êò{¤ÝŸžSsas Ï˳Ï=áÑÕ¨s{"cù}4í·Ò~ñcWãý3”æ@÷ì.»YÅX°˜rÿ‡3ª©\¼à<ÏOŠº×i” §?ª*ÀÏ£±y²9·O.ÌÕ“é =›ÉÛl.‰ej¼õ¾ùkÿ„ï?~;íÏÛ[Sn-ò„õ­Z©µp47ƒ+Eþ+kemë• Yb&Žh¦‚®i /ûšå‡þ¡wö4 eÓÙ")SmqÚ^51„¡œsúû<ú¾æú•¯„± ç[¼¹ïDúÜ÷#Ày¬Žx ~ë” O*'»»˜`}voYe0¯CºƒÏ¶ »f¿–2«Z•_ùZçÐ~?½wE74hq#Ó5ƒø{,ÚøÁyΞŠ|µÓ/lr/¾]£„k¿t[¶åZ¹G1¶$Uš”Ζ1ÒóB/ ãë6ÚåúZeü½Rî|Æh­œ§|t¥Ï§#©PzÔ:¿ó+%XÖT9.=yî1'ødnisðsÔyºszM8nAóNÖÒOÒ÷à–¼™_7°ÿ†W-Áy,¦:¿šü êh/@+!sÍû2õÎØ{`éÎÝ0×£^Æyœg÷Ì6[Ï.ãïÐýÄ3»õÉÃbêñýTú{Ýß§Eãòû8m¼à¸«ýF.ÜšùŽgxLU‚=+ÓsŽÜ€q{m ÷wƒ„ËÆ×OL–1¹‘]7zMcè÷ÉG·ú^¦÷!µñ!¬igû«³±‡/¼4ÛW߀ë/ÖÔná ûSVÌ:î,cØÛÿ»-eè9]¿Ü9ëä\é×_Åq[œkønð9àúòJ[ÖjùJ¿álfÔm½#¨äš>÷”1…'÷,Ùµšö›‘}l=r~ÀõõD8Þ…;™ =¢ÎÛõ\ÔG Ç–^iÕvü °œ&ˆS÷AŸVݓϗ1Üソöiè~‘ëS׃eëVV×û¡ÄrÎÿï½¾{Iþ÷ø ±þ¦ÇQ' ¸ÿ°ŸŸå\Ûÿq}O¤ˆÂ5t"þ$ÔoW×·õ'ëq®+PbâÛÆ.toâ-I6ù“mHY×”acB|‘Jˆç.å†ÿq{Â}¥ž»izÜWkâ¹[J¸¯‘„_C9×&m9Ï7–ÑÀ²_u½%ÿʃ\×[’eØHô|ßÌu|ßDÄ÷M×ךøKæÿ ïZJ×›x%éz‘³ŒÃâëËûÒçÀVëù‘‡îµ5ñâ­ÐñC1'<›RÂk`½á,ã°‚x%éò¾ôY æÃ+!¾(l·’e~Ïßs§Äà?7wšÏÇ2aí‰'¯>#;òŸ°¿X&¬TÇóR×ÛIƒòÖcÂjPÞÄ߉õ+÷!žr”ßPò7ù_” Kù æ„›ÍòmœtØ_âÉ+"lDóVœ7¦BÈú•G¿rñ”Óè0a͉WëYÎòu}åþÊ—W×[Žå7DèùEYèøE9¿(]ÏL[â1Wò'lX9ñÎô!ül]^–Iü3uY7ú¬D6Àu=z%„kK<45(1ë¡I±ÂÑf}ÌYo)ေþ¼Þzœ}ó?óÜd=ÌK‰ÏËc×(ûŸï¹óߟ;ÿÕ¼ù¿”3ÍÉÿn@Øù({\ Ñ(\¤„íðg<0–í !œ”w£ëcÎzpFè±d¸°#ˆ9ËKŒ$žx"â‰Çúÿ«L0]¦,e<ˆG›åÝ„èðÀX¾ƒ9Ja݈ëFŸ•H=ÌYVbñÖèðdEÄÇœå%:µûÖ_ïÏ<†õýõÒô|Ìt|ÌCˆ¹.ïFN8ú<ÙR»a9Úz>Ã,LA˜7º,í(Ⱦ¨|”­ž×°œpe}ˆŸ¹@Ç—“åÊÊ K›z™‹ L ÇÑVÞ å%þï†å%²¿Ìc}ùÒ¾×™ßëLƒÿìœiAþ~–1+"¾ÅúÌí¨Âc³rÑl”-.èHT5ÊG1[òÁEžF¼ÕYŽ¢B‡ÿPú79a”3Kù„ÃÍrqÄ:Œ°â]ìDŠ­8ßÑl=†¢=RñdgŠÑ„ý@³\Ä—å(† JQN‚?÷/–£,0ø$¨ ˆDU ÄŒi(¤Uc`Êõ|IYFNa@è³fÓˆ?©/áqëú³¼°(âQªËÊÑg*²Ln]O㜵ǀDU£¼Y¿Rœ Lnïþì½EÌ#„Æúûè±rØÄ@}ÝÿÊÓ”õvg™Š"àxaßëÌÿÜùß’7Ùœ)@I 8†Ë¢(A‰pJQæ¸HC‹âÏb¶„;[ªÃËÑõz·ÆÅ©ÇµÆ…I¼ÞY®bJCxrâ½ü¯²ÄtÙ³”IáD¸Ü,/G¢ÃKCY` „VŽaåè3©×;ËT AåŽåÎ:¿w–«(Æà’¢Ìña†þÜǹå„A'GYž˜BÇ÷½%Öñ~—ïw]fŽ*ð,ôù³„›Ãò¹5zžÎ„›£ËçŽF™`0 JPö:žÎ>„kÁ2h}‰¼5zª‚0hÓŸ›ú¿{¶˜µ›;›0s([ñ¯˜9,[‘uOQ|¯3ÿ§råciMþ¾RŽ1&7üGVwô?aŒ±,Ú4”9.d_T>ÊtYÔ¾z,Zvû¢įžå/fë02*þ&gŒòh)#C@xÝ”·cŒedhPb š4”'„°~tù‹" ¤hKÎ÷žå/JIPQ­ƒ+„xß³ F ª%à߉²À€“ *Pb ¼4”ƒ/¥!¬Œ(”åÁ¨@Yc@F 4(o Ì4”~ATaEf†>“V² , –Û-ÀŽ@io,¥Ñãé3Yv·/*eOØ,›V„E‚ÞÇŽý-$Ǧ$ìnLÙ([Â3 ÜnÊ b½óMt¼óí³÷🱾ø¨’!ì9<çÏÞpŽ7F™Œ•7¿çËïùòß/Ùï–e… JQN¸8å(‹¿`œÙ6m…Cˆ}áû¢²Q¶˜£ôø´¶¸¨£PÂaŒFU¦GÊâopÎtµ,×£%&¼n–!Ñâ+ûL`HCHLBú ÆT>a0JP%„ Bµb () *o”e!ÀUŠr “£,0Ð$¨ ”. % Œ´l”ƒ/¥Ayc¦¡ˆ¨ =¦o–EÁ1BôyµÂb¹ÝÕ(1mZwÖ[•ã¥e¶.»[Š2Ç`éÅz3±>D8 lß>3„åÖ ²Q¶è‘(Ç®Uvw$ªº?ûû3ü÷P¶zÜî|¢<ƿ⠱(d”ù£ Ã 5ŠÚ”‰÷«F› ¤áüÍDH‹‚·@iP ßù øC¡T(&p@Á ¥AA0„Òz0ƒ`'¤…A,Ü$ÈùÁ,Vn˜ È iaœP( òƒÌP2#Ù U)™ãi‡ÔÌ_-Ì %•‘Yžf(õP¼NHÙ¡4(ÆsBZ˜Ï …q# äCZ¸) ÒÀœf( ¨Ê¸”øßÀvAšŒç$³Íµ0°…›8²q3ÖfÙþø»Àöd|sS–=‹óU—e‹Ê,à> RÃôFÈÅYÀVH…`dÜφ,“R£˜±ÌMÁy£1˜š°ïéâwh6È»ûÞ>o(Í"Œ7Œ@(Œdÿ±Ÿ¿êÖ7ÿ³}RôÄe?ôì…¬ï‰þ&úÙÔË‚¼þ¼‰Þ%ú”èIÕƒDÿýæ_ÑkX϶C>8y¡P*ˆ“è€48‘‰'3 Rㄚ $v‹“ªFï0A‰'8,¿W“„íd{mp²“¡ô;äƒ ¥BèHƒa†ÒŠÈ ©PH.ÈÅ”¡ œl_. ÊŸí@1¹ØZ5Š)Ý+  4((3Ûó€¢òf{ië±\m–%ã¡°ŒPb–¹ ¯Aj™Éeâ˜(6äݘe}áód÷(<ï¦,«ÿ €¯ìÍXVAú\ÄÈ¿ç*~îÊ)"^Aº}Â× >\9D‚Ë{hP© W*\Pò[’W>w±õÎaÈI#ª-8^ë;Âc¹ÊbðLÛx.—/}¿xÇý†¥ç)²qB0Žu˜ª×û‹k…/Д²k\np†>ï™;¨p禺þS™Ã“¶IrþdΫ©¥äaˆC×íKO 5s’/Ãוàyô8þ œÕîÜz˜®¨N>z›ãµ°\;òK•3ô$µN¯³zbi9'¦m“®u9¦šž»*òúÇ^ÎG’sV8î¨Ûw§ÏÛx˜:4ðë‘´ót‘ኜ!™«ÛŠÊŒÜumèÛ¤0cöm…›LQrID¾‰œs[WYvÿ\Ó—:‘·ï™›„qŠÕ¸˜ãðÙôìpã¨_ÏS…¡_uiò>†Æ¯¤ï_µ'F­Ýnß&¿öº¾¢îTIΑ,¢ä@ˆ|"‘Kå™#íE)ú‹-¢ï^ˆ=L®þß4}yý<µ;1§Æ›14+Ç‚2Ý:Sވα¿l“âû20Lz®—È¿‘s\>èÏ\Î/oN7"jÄ1DΧòÁ8æ…®î}˜¾l=Ãp?ú<…dm4î‰#†ê.ª\ìë N„71vÁ›mRÇï¾ïâŒú»Üy™k•‹ÊUnúÞ\('mÜ»ñÜØÑ-iky¾ï(çf`œs&˜;LE¢{g­ºëLQ3{ŸY»ò<ÍÉU¬éòÐêÔ±ØÀßÚúS`™Í[å¶KãV×Ã{ª’$ÆåÙ€Só)yrްo®KÆéXôuÙ•ùKûK{ž–įžC?¶zÕ|yŽ–´8{‰‘›n—jm»:zÖ˜)’8ß"¿Ep¼Åù‘sd|•É£;l»ÔöÓ™½†,S”×+ü,x7âóqû@Ÿ¢¯ÙçÑ‚9ûSÝùSŽmmtž¨ëô×u¿Š¡>9.my£ŠÔ¨êÙ©kp\9nŠ$ò…D>±¡S³R£ã?çùRÙxδ¯Â…wûã<«êuº®á0ÕȈ‚çéË–³Cå o‰}<¦štlaÕÀ;#·Kî¯:kšªäf‰üÔè›v¯«&9G?‡’ƒ)óËšÉ>À82á0¹1nêó´<{ضÁ#b¨_ÞÚ ‡j¤Iï¼]ÉñuamXd›vŸVɧùÖrÿj@rÎn#Ù§IÉçqyo¢[Óny™@UÖÌÚðóØÚÜ:ªl¯èrÒ“sc,óv%÷TÔȼOÁ‰yF+î=ªó~«ÌéÁ8SÛÇÎo¶çÃT^I žµ•x?ðœ-ÉÏ·b•Ï÷o—Š~ëLyôfŠROs\ï)œ”ËWÌ3†¨kgôÆyüÂûÞʇhÇÒº…ƒ'еUG“›bœë~jÖ.Õ–Î:}m»´hO‹S”÷#r-åü¬Jœà&zæÒ:0Nh¯€S‹Ñê²3†u]Ÿ@s«¿Ë}Ü£pšŒcIúÛ•¼ ÑÅçÖà{ÃÖ¼o§#£ z?ª®äQ¹ý‚q>{æ¸éÚC4ÓÖ=¼Äœjudn‰j¨ƒ­m\QKÒš+‘®\;¤ée65}1Τä ¾ÂÃNïÖŒue§Auã}}·ÊyÛ^ÍSôkkì½1ç#‡¤”@Û²8¾~Ùz$ø»Ï^Õ§¥ÅÇT?Ò`‡Ô°A“nß<VêXä≜êYÖÇ·Û¶Q)yêx…§e£øÈ1‡hié’3Mýè„-_å=c¨M¥‰YW©šPΚƒ&LÞ!¹±¹¾&Ip¥QÎMûÈóÍs*ÜMÙOò8zŒóé¬~sˆf×îøSž6 ´õSWýŒ61d;6Ô\åI3ª¾ùýæ«[wH94üÖ+É$‰×+rú俜Ã*÷« ÷>m¾y4ÿaªºûRÀðÚ ÔH¢æQêPòãü7)Çõþ Þ;¤¨õa#»OV®G"/6¼ÚÃY¶Üq ßÕí×ý·´‡I×wÜ/UK'л2¬ÅPž ¹]]—^4¾mηy‡ÔÛ¶È´{õd%X\O_Uî#÷Üǵâ¸l¶Ô×ï0Ù³¬/ÛF•@Õ*Ì?ð©B çET¦^—7¬Z¾CZÐyù—ONQòE.˜È]×w½ã¸Ç¿ÝÚ;¦Îaò;ìS¾þãx*uúIÇÝecèò½ö/¾©’—ò_Ýñý¼£;$™/?UÉYœ4ÿaÄ̈:ô¤Ë‹õ3Ϊx¾§œ+œ„ã.ìèØùCÅÃ䯂\ާÏ^´ÇqåÜÔ³GÝ®tìPúœÈ”ó}9G17çmüqzµHÑ_rO×(Ïa üþ]¯G'ãÉ¿æñúo4þ4éÊÎRÒÕùKÌþé×}ÁO*úìA%µC𬳓;¾ùQà ¹Š>'¦øwMnÜ=D?º;ž—›àªT*†®>1¬iZêW|²ëå†Jî­¸î‹þ Χà¹È×Ó† ÷Ã]ïg‚¨~ˆNw«8 ÷æx*â3¸¬T(†Œhܱõ„Fôàf­wQï…WCåOù]n¬àAË9Žo•œU‘k“eÔŽ/†N;D1·–v®h‰§íóNÔœ’5†bþjr›æ”ý˃³FÜ!É9uéód19î‚k!÷õÊr"C0N÷ =Dy¤ ˜yÅSóÍM3Góë}kZ¿düÀwH7K¶ô2þšž¿&rå|ã“:ÁmýÜ“ofÅ89Þ¶ZW±ß!ª¿:ê+íŒxºy0üü’Ähê¼*gë^mèÍ SƉ^á’3a^­°1“•ù¸x_rñi|ßt_'råD¾¯Û/ÇÓltˆ hV›OŸ>-Ÿõd4ç(·¤”µyìÞì|÷©ïZç¤÷ áoy>¯“ówßëD.žÛ78~¡ GöýtöïK=Ücd<-T—UjW4y]2«_¶æ´Qiñ>ru,›™mŠÒ?D.ðûµ 4èp^7¤Ô•¹ƒ?pN[å yõ^-SôsÚØÆ~8tBŸç@§Ž§û_´«~tC4ù=Ý\­½1Íê^ÖÿF£på>)sŽ«à€ ÎŽ÷iO[V¼ÿñûh^?©bÿu±E¶„Kîxþ“¥ÌýU¾?®•Ë„ãÎîÚnmHåƒÔ ™FÓºz<=j“?jÛÂhÚ¼[ô].ºZV³ÿñÔpéI¥Eñ·§Ï7Äu^\w+e»SÐ*þqûã˜0›ì“r€nLlpw}ñxª^òébŒ3Ù7󞪸”÷aŸû¾—PÜuß ™ªÌD=É×Ó GTÌݾÀñÛOñþríštôX±ÊÝsÄÓÉµÇ 3G þ«´tÜëFº/Ã¥î ÒT…¿!î»ä>–KÉ\ê¼ÌNA;mIÓ.ö¡f–´­Ñ´òþÐzÙ~*-}ÂìÀÿm¸tAÝ3­îáôºýYä¯Êë'oÜÇÕã¸)!½ÖÎNÅÎw|Ýx›‹6Wj]âÌöhêú´óÙ- Kkí¥vê_†K‹ûœË×)ý¾MÌ£Z~ý¹ä:Çqëvsáñ¯ûé›àê&,wÑ£Š]uã©ÛŸÐfóJË—ÿ0¥Ë½p©~U6#2)u.úšàA ŽŠÜgjdà”…`ë…«v.ÙOaaoŒç¢!ß-;¾#šç󢦯ËÔjô$\ŠL­UìÙZ“’,Ö¥äùãK]ŸOi}÷wË­ð Å|Ø]ï'b~Ž£ûì§Ñ¡ÏVõžè¢ðú +¾-šZý¼¡«qh%jo7·5Vˆô½».òIz>µ¸9å‚«(ûA^·ràøÇ²Zêõû)yöà‘ÉC\thØâ\û¶D“ìoúàÑéë©’¼Î4Yý]¬×ÉóëËÊú’<Ñ*\Bw½cœ^­:½|£ÙO?j¦vh4ÀEWf-[¬ÙMÁÝ+U²œ*@q³ý<éZ„D‹¦¾(YyŠ$øë¢Šú”×3^èZÍ_ÞtÝòÚ ¿Ä]ÿ­Sô{7ùµ-]z?%¢;z¹¨¯ôÍ×rIcBV>Mª)UnȈu鹫¢n—ZäùŠuÀ ó'Œ3¹|è cÖýô"ËÌØÍ\ÙfÒ–ÁðÙžÐs#ß×*'U™ÓîÓ'R²5¹>ùv£©Êûó(Á3c¨¤Úý&‰¾-òËÝ>Á8ŸŽVs¯n)9!¶ËØi 'WÌóåÏ=U'8 žõkÅñ‡ÌK›W³Ù>*U‰%»(º”1«ÉÍs’}hJبU¤Ôkàº÷Üž*‰ü~1?—ç}ù—nýz©Nñ}äžnuÑóñÉ+³ás—¹Õ¾4!÷¼·oE(×ñy‹ë™<ß—û]Ž·ýéûÙ?ì¥~ó†·ßßE[WŒŸ3tm4 \Wé˜ÃªV¨åµ(!Búü`‡Ûá¡éœ ñy˾{¥“¯+2ΫMŠ>i݃.%/î¥Ð›‡Êí¢B´×Ö×Ãó—-Ó 7¡¡ç :ÎÝŽÚ³xࢿççȹîYNŸ»¾qÜõžåOݼ—®Þf1ŽÞOXQöó9Ñ´®ÐˆŽwÆ5¤–%Šø]É),¬±òëÉÁJ޼èÏb½^\W5r¨*ªÆT¹/èÙë/@Ÿš™öÒÌ[.ù Ž:8wÑ:9šLÅ x QùR–ù•N–”šÏºe94Iᛈ÷±­ùmCb£\ttɪ›wçâ×éÞ4âòœ=§­-å:Ç8ßÞ‹ÛŸ£Ã^Ú{l~òåKq4¢L|çb㣩¥ÔçÔ¼éå©Ö¸.½ƒkDJ[¤Çg V®·bSøu÷ăͺ-ë©]ÉØêuøˆ=MåzÇ8ç²Þ½\`/9ÕúMLÍù1ûá±Ñüú’Gš“¸²Åƒú‘Rìâ£zó·ôþ#x}rîö'ü¹6SrëÝuq^Ž®¡É³‡®}ÛUÝôhÅjSeb4ç€U‘-+w×H©Ê'MQrÞEÿóFÁ-ø4…-\¦çr»}€q¶U_oòª¸7 N£½qÔýUÇ{›¦¢ÿÑœòkM)0¢cÚ–é‘қ÷·µ-‘žó.Þà'É|ûç:Áyöä‹&aœ,óª—sÏâè¾eÞÖݳ£‰êYªkvi¥æS숔z•T—‹œ¦ÜW î‡\o¯t7´%#ˆ{‘Ì ®šáºíÕ6E¿öŠ-kWýLn¬®9ŽîMÛõúö‚hš>áaÌÐɵ¤•ël'³ÅDJýÞ®Ø\çTzλàœñ¾Ee+9ÞÎ/œDθ¸¿rûã|ú²ÿ ~¦c‹–Õ\2)ŽÆ¿Š5ìXM%>­\Sÿ¼F*ùùÀZÛ®FJïÊo¨ù Û4…/"î{†ÔëT×oK.ºø¾Å•-½³ó~N wÆíŒSÀi)xc7}ÛìqêåAqTdlãè^˜¯Z³ø´--™ÜF”¶F/Üÿcÿ©’àÖŠõÊûï9ÇLJÕÉ÷YÙH>ž.¿&ã jr³Ë¢»ic¿¹ËÖ÷ˆ#Ãŵ×Õ+£•ûóI³F~q4RÒ;¾Ü;¢Eú¼RÔ“<|¬“¹ÎtÏÚ/yðV×4ï(ã¸?–Ñ»‰Qãš·£gÍ'D \­ð¿Ëê¾@I¸GĈ“•¼±î.x2Ÿ(•ßÇ7P¸-nÿ`œð,í[.j±›²µxtëL«8úzG‘ý%GS,ºO{ï†Ôèâ—–¹“"¥µlÙòŒI¯Sp©åy~ªîòë[>åbfÈÅwàøÃOXê}7yÝŽìߺMÉa*Mnœ_`6êéг'ÎÀQ‘R8Ã:·3)u&ÖùeŽ­ç>¦êØì Lš?Ÿ—éeß`œ×åÆx»kužÆf>qÔñYÖ¾£1Ý=Gý(¹%Íx½¸Äç#¥¡ ¯þ*XŸ“¸NÉ÷u•> ÷ÕŽô¸z„îwdßø§è±×õßEË+5>S£EmÝ8tX¿/¢iäçׇ•ÛßšFk/|5óã”bO£K⼋õ 1o×ùuø)Üw·o0ŽºYÕ¥%®Úhé˜}|›ÅQàwóÇåoME^Ÿ¼¹ºCúM½è‰ÎËg_Ö<{“2óx±^ ÖÿSð8ܾÁ8µ·f?Y±¯Jgm›5_ƒ8úmöAÿ 5¢©}WS‰)[ÑåW“:ŒsDJí®”Öð­Iékb'Ïó”:¼QOGÆ ?É&V;éGÿ+ºÖˆ#»Ý.MãψžöªMøñIäÏåvJuJÅ Õ™,‰ûÄŒÜ —Nø'ê }QÎÔʲ_püqó: ºŸu'Ý-ø‹ÿ•òq”½Î\ï79£iÁý¾÷5ÓÓ†'o¾Sš9‰š¦(×ñ¹É¯ß¥“ç+)œ_Q-?ÞŠq¤„7©5šGRÙÚ›O´ôŽãÏQ´»÷°5+.4&÷t¨ÙNéѧ :ôL¿_õ&øÛ‚+#÷¹êüýÉ럌ӣäšÐé£"øü2ŽÎýÚ9¡÷#'->±}A¥WiùÔ™_lðß)mÛtgø ÞéÏÇäû¾,ú¬ÅÍYY”ç;žë¬Iççoœ}"§‡Óàîì-ŽZH[ºm¼æ¤u¿ÝlÔ¾°žÊ4|?úàà’½Üéý/NVêY¬ÛÈ܇촣Eÿá7æ&ñ\ÄóýxµKÑ·¶G‡ÕY¸ƒn]ȵ$8ÕþµìÛÍÑNê•íÆçõo·$Vå_Íß)¹ñâ—ÒëYÌoÄó—,öMíÆOÌ¢pãÅûuûãôXz{ÕèíT|ÉÒù}rÄ‘¦uõ×YvRlÇ‹·ÓÚ´ãþØ©øSÔ³X7”Ÿ_<ÑÉùǺ_Z¸6µNÆõ\Œ1çÉ¿êÛ©\ß,s§¼¥æ#®ìõ^á¤o{Vjp²Ý3uÖÉI;¥cövî»8XéÏ‚)øBâù›xNâö Žšáç­Û¨}ÿµ_ë_ÅÒ÷¿®ù2ØI_G™#LõºP·Ë' ¯…óÒôÒ¼ã‚WÏr=Љû8y=±‘XW‘}ƒq8g…ƺoLcižÏ‘»z;i{òžÏc†v¦ã?Ånºý ó©Bµ ƒ·ßA~^ò‰?wû•?÷nN~sªÕŸj­/ûãÔoó(lø¬0ç›óÒØ÷±T7¹qË*q^Ú¤iFjOÃÜ4DåÛ)M}³¶rÝ“”õ±.)×JéŸ%ŠåŸ3(KKš³zaµö&²o0ÎÇgý÷Ö¹µ•jò«‘ãtÎùkFå¤ëw}Wbå֔Ėٛìü§Q¾Ï(H%*8_Oõö"™{Ýƒîø¥?P—óqü“{Æ<ÙYf+¹ñÕ^qt¼oóùsÕN*ýdºqe-¢?í»Ð¼ÿN…û%žˆy†xKžFlÊqâ¥ÎAjÕ“¯Ô‘ýÒ>Eï«9oçû-Vï€w¶8êÓÈ'²yv'ç}7 •OEU´Sº°ÿlÊn‹éwŸ—<ÿKÕÉ×ÏTÌéµe¿`œ…Öz—ÛBùnYwÉGÍOâ¬ï£¨_¡¼ ¿zQ™jÿ´òÉÄn;¥¸‚ojm©5Y¹¿“¯/wtòúÓ<mÅùr_ÖãøWv ½üÛ&š²5ë“Éci«au|ë7Q|Ý.MwŽÍ´;¥C_]Ûx6gúsWùõ–UÖ÷äu³|ßÙœÄsG·_0ÎÎJwg¯;·‘(b#Õ{K˜ì¯jò6Š6ÓÃÁ1Í>—â—Džm§d6dm]c IóXq]ÜdÁ©Z6wçΉۚ“àŸ»ý‚qÖGÌž}dþb”ÁZ·b©võµó~ˆ¢<¶+ׂ{•’Zšg‚ùì‚Q:Ùƒ%Á÷÷¯‚¿$î3×V»Y·ÐyߎÇïÓ¯sîÐvVê>å§ÞgbiÑÖ ×z~Œ¢N·ÖŒì¯Í&M~ôþÚ`)Rrß>· –Äç$ÖÓßQæ æ"ñœHœ7·O0Nb›¦WÎ-\G·ž=î7ûh,åo—põ λ\/jjZôÚæ»"ùç–~(žã‹õA1/üT·Opüe[.½[4i •,°/ññϱ”|sþžä´(þ\»]>±b~ùÍ‘RÏ:^=ÆÞ™¤ÜÇž°8ïòþ ÎÛê€>’Ðmr“ã+é«ë¦àAá±Tààœ–cžEQ¯§ Ƭ©GÉuêç¾aŽ”ò\Þdl–'½¿‹uoÁ}èbì‚Ñiº•êñí.N’_·ŽïUj꫾?-§ÈçÙwÞK+¿š8÷NÝüM÷öÌR==)wkêŒÙ‘ÒwÙ’_X¬¬›‰ÏGð@_ ðÝáW¯tE>lXYår½Œ×Œ³aðu*ºq)ÝïSöRɽ±Ôp~ó {®(:9¶Ú׋N¶¦ö;¦/ˆ”²¸ÁD&IÜÉë47ub>,xqžûü‚p|™ßø圽a·}±ô¶ûËøö(š³µÍº«qþtÆp÷Åw[#%»³:£‰óœª+óG™‹{OÙ$øwžûåB0NêñK#ïUúž"NÄ÷9RºñýµÚëÃM¿»ßûä>ùI'¯÷Õ}‚ãWmj˜t½ÏX:Õ„ÍâhÙÌ0k[ß(j¿¶è¸>ÍéÇ|lE:RÂdîµ×õ`…ÿ+žÓ‰>BK#¶îR‘<õSú½Û'GžwÈ~¿k×jâ~þFx©½¥£èûUƒsL iC§ßÿâZ¶6R2yÝj9?}þ#ž—Èϱ/)û>Û²F3ð\­çÃ!ÝøK[ûRÁ®Ñòâhíèï—ÝÉEÍ?ËíÝïFGÚÑïó„è_òzýDe>'æòóÓßtß,i2²|݆ö79p|Á#øºô‰¯)Ž亼¤Õ½Ó˜÷Õ=îЕ?Ÿ‰”½¶×ª Ê<^Ô¯|uE7¨d¿öY'?ÒÉœkß ëSIgÔ݃Uïím!í\úv÷žîq¤ê»{îTÇiÙÒçù³AÝhüË´#fDJSnÅÎý¡ÿeŸ‰ÜïïèÄz¶¼ÞQ;ƒÏ½:¥èg¿]Þ¨_ѯ¥»/›vn\™×•Ÿºú4Y}Ù“ònômtca¸>uß3®¶¶ú…¯ðùsÓš¿TïìE‚ççéCŒsFû$gü—c¥êoŽP-Ž£¢£ßø·Ÿrš>`•@ÏN?6:1RZ’ýVÍ…ñ Xô­Ï_7½ºô3’¯'9éÞãt¯s£[l°”oëÙs 7ÆÑ–£5_è{šJÚ³MŒÙÔ~þqÅû.ðaà¢Já‹vWúŠX7L*Î&¾*å>ã¦èS~ÖÙ‚0NÒóÀºuœßHÓ½º­ö;G—çŸy–§ÕizY/ÈürXk:Óác•¯#¥âÆåKîUœ øE\å}}¿éÄ}œ¼žÛPö Ž_ ¯Ï4Mü,éQšŒ#ÿ¥Í7ÿè{šØ.Èâ=šîlÿ}k÷EJɯ–œ6”ž¨Ü÷Šù¼¸o“÷e½àëòõÝŠãOÐw{BÑy’­w ‘’âø:ýiÊ?vàGÇg¾„?¼œÖ1RšÞèç~ LúÝ>Ñ·jžÏؘï‘N~^SCùÝ>Á8«*e>üÊwÒ˜ßÖ¼<ú,Žêõ.Ú»ìi¾¿¹"Ïûw¸!ïÉÈÈ“”ù•ð½Ø+ÖUDÿŸ£Û'§jdµw‘ªÅRt©3‡fq‘w°fgãb§éŽJµ¦ÏåZ´ëÝâ[»ÖGHrãN?¢¯ˆ}ÑâþZîŸòý¡WçýŠÛÛw?ýÎ,]4¥TöU¹È<5»ÿôü§é†÷ÖÓ©ß4 «>K®™!•soؘ¤ÌßÅ}ù曯Üê¡æ×ǜʺç>PŒ£q/Ð-‘¾ý€—梳ע©^§é´ùuŽ.Óô´¯Àä3¿äõÄIÊõJî³ÕéÉørïNT“êP±Î¡crñó1’îµ®œtÙ¿…ìŒS¦èðN//•6¼êÓ¤k!-q˜øø­¿>¢[Éû­IÕluÏõ;"¤OÔÙ¢]5QÙ7!Ö‹åùPVûpÐÓ’ëÃ÷ÅËÏ?‚0ÎÉÝ+[vûìGé’×– mʹȰÒzKsáÝ¿_2¾Ù£ö4èa䯳ŽEH¿ØO¡óOÄùu&®‡b~Tng®¥·ï´$q?áö Æ)Ý'N½ø‹åÒÏ©¿í­UßE_ù|Ý®ðÞS|½4€xüu)BªszÞ•3ûÆ+ϧD?“Ÿç\Uø³ò:a݌׌ÓbêoÏ\Á+$•{b좜îÜS$ ëùÓ‹¦ÝÉý8bM„t0qO7ÕÝqʼHÌãW]¬ˆç¯âýº}ƒqªeñÍ]ì¹EÐf×/ Ã]dj°ªÐ§!§¨ÅØ‘·çŸêA•oG¼³"Bb»Ü<§¬¯‹ûPyÞü<ªF†}9I8þ]­Ejq•4|ó–ç“æ¸H[U§+×ä º0bXÅ´ž´¦;Û(!ß´¨Â¥Ûã•ý ™÷ùËýó­²ßØí—.)ú‡rövÔªÞÁÙ‰+]Ô-©ûÓNÑ/õ/—-¶ '­=0oÎÓRH›”©âDeBpŒeNw:~tm«93|•õ ·OpüSßû×,·^ lx£tñ.ª”uÿ“û'iå®|3QÝéDb ÜFJ‹Nðy´m¢rýÏUø>M¾_ú­ò=·?pü3:] ›g•êÇ?Ø´Ðî¢ÎÕ“ÞXŸ$ݯs³î›Ý•ªõÊwü—'Ò}ó…Ñw'(ÏÓľùzqN'¯w&ë2snÝþÀ8o^&.Ï2t¾ä¢‹ó‹Ì¨´á$õ<7Ð{Hï:t~m•u?GHÕŸl[ÚŽÿÝþA±,æ¢o‰¿çöÆÉ¿.w§B6JSJuæLpѽ©mÊ×›u’~»Øsæ—y;S¹/ÙG_ÜËöºqÊûÿÏíÄz°çó-+Ž?ô3Uļڛ$õRc¹ï“\ôi\‚5ßà“Ô/x[ÔÈèÚ°­´ù#¥ÝWßKc•ýb¿ˆÜOn+óQon?àø‰µØBÖféx—Rzé©‹æ{Ðö$]ËÁ @­Ý!=«6bõí±c•눘ŸÊësO•õLù>T›qß:ƹßï·ÉÎÍÒW#ö=_òÉE«=>O©p’Æ_+žÅض;Ù|ÎÖ[!unÈfË2Vy$žÏè| ôĸ‹º-U&M_]àNø_îÏïäçô)úЋ›l™½EÚl/]¢hv*ÏÎ^`_œ¬Äÿ\Þ×ïƒq®§Ä~we»¼þΦSÅ(_å=«ãÆ'ùû¨®:e_†K¹;ã3ùQñ¿ð¹|=‰Qú±è—žû‡õ'>_CvH—ë_ü ne[¹¶õÓÇ©þOE½V.lÇ×=Ã¥Û>åbŠí1JâþCîÃ1:±~'ÖÕ<Ï{Ž_·Þ:Õí¦áRžÝâf$Pê­ÇkŽ5>Nã×Íl›÷Aòó.‰Nÿ²wÎ4£rÿ þ+Ö=ÄþyžÙDÙàö Æ™Vmk³JÂ¥qÇçćL Y7ó•9N=z|–ŸëH›o-;ð¦M¸$O£ŒÊ÷ÄyýR¬ãÈëM”¾íö ÆYÑpÞˆ½ó"¤ë}~°X¿M ‘Ö6x§uÆÿ2çRgê–—­h‡+}Fü{q½ë‘b_ˆ÷¯ßŽZP‡?/Áñ»ÞÎ2¸{¤”Öâ^{7' ëd¹˜tŒò$Û~qw=>¼ÆW£ð>Ü7\£”}ob~(ö³ˆý·òçÖŠÏ»åçI'×äf–ÂewJËØ×¼¢È^$oZ›£Çhf±¨%³+t£bî†óýi#ß‹û q÷+ò÷ðÚ[Í謒¿‡çÕ-EŸ7Ûò¾§nîîT#5¦/éz¾â]Q'äîýª;mÍQûmž„pé›lKK¶¾4\™?Šó/¯gßÕi¾°Äe&¯;ûà¸SÕYŸWêa“&²íNÏ“~áˆË7G£…EßUkÚ“Ê×-foz >Ͼãá½àá¿[¿þÛ¨=èe‘ý€ãæ k¸éþ›tµiþÙoªŸ§ˆ„*Ùº6?F×:V“våíENÛ©.%4øØjð‰áʾq_-ž¿ºëÇ“ýi“ ~¸ú6…ÎÓ<ÿçY*9FÇÜŽ©÷[O°ª¼#¥_¸´­v‡&õWŒPêRô%y=.»rÏóäõTù¹QÆù)7û·M +ýqUx×ó¤NÒNûÕAQí¾ºq§cwZÃË• —Ôý¶QóÕ#3ío¯N®˜¹z§ýtAÒŒêWvùf˜—X1N\#sì’ê¯|¶/aÄyZÐó}ã3ˆÙÚ’%©Â‘°ë;³†Kò÷wGIâûŸ¢þåï¡Ý׉ýòó¾ïÇ?ûó’^ÓZî’¾ú4qôÕ…ç‰}ë\msPÙÏ«ž»?Ö_Þ†["\ÊëþbÐHIÜ‹¾#îŸÄsc±O_œwÁ/ø={蟬ÄŸü!/þž^éYfžÙ²Þ<[6™ç™Yþ€ÇÊxi9Ó¹‚>üjž-›Æs‡Â<2“8ÏÀ–‰+èïÁ{üj­GÞH籦ý'˜/!œa­çÙgÉ<ÈÂÍâ™?dåù²FæKÏ2qžf ãüjÏGd¹CžŒ,O–AfF–`±²\ÙDHó…ñÜ#Ï.Ñp–Aæ\ÙPÎz à<—-™çnÛ8sÕ%q–óªÿŠ!È8/"Ÿ’en» ­oUp°0(”   Ø!ŸL™Qa¼)p^5Û1Br½²Ÿ¿kü£þø?Ýÿ«}ñÏzâß­²^˜¹þOô@5ÿ+:‘£íÉm àÙ”>K g[1Î@(” Àp6Îþ ᔌ—©9×*‰ó¥­Šó¥]Œiå|iÁeaÙAœTs&Fj” À¸vÈæ …Rÿ"sÒ3›±þ´:–G'3¥Ù·+lÿÌÿ™zýýæÞü5'²s‹Â´dÊØd¹ßf( àÙß™ÙVHåÁ®Òz°Qyö&ã°ñŒ^v(gÿ¢À™ØU,ÁFգ𭪠œÍÉØªBÿ1OÀÌù¨0Hg±ð¬^u&®4c±xs>ªà Ø9SšqX’¡˜ÉÎÙ¨Œ%ÊyÒž ÏŒñÌ Áý …’¡˜ÏyÀ!PÏù´p3ЬO-çV1Ž@gþ1–t(gþ¨vÎD ’9³ÊÎYÒÅûcì‘jbYåŸUðªP¤Ê’žYîÉWa0`(” éaD+¤ÂÉ1@.Èó¦˜1 œÓÇØÐfÎé ‚Qœm ¥Ö”YSÎ…þ+F_Ìl‡|`è–)é=¸¦‚3åÉ5 ‚œ†·pÓ ¤…ù-¼ANH‹F`†Ò  ž1Ï8}Aœ ÍÜÌ$×(ûù»õÈEüwíÿª¾ø¯è‰V¯Œý?Æ.U£ +׆1øœEÊ|~(N+¤â ¾DHŸ+Ÿeð`—š9׆1Ÿ-P*{Ž‚"vpŸJË›ÎÔÒp–…'·Ô%BzÎßK†ô< ßð,œœ]ÊyÏ¡PäCØW‹óž×&€sKã"J-)³ž{Ïf …R9³ÔiÊfä<{r÷2sžC¡Ô 2wÏù ÿ…B©PŒf‡¼ÿ„gc€\´pAÎÜK…`H;äS†B©œílƒ¼9Û9 òg Îv ÆÚ³A*×È¥~À(5Ci¬ÂÐNH S[¸±ƒ '¤Aß3Ci¬ÂìHÛ9gÏ̙Ό]ãøg~øoÕÿ]æ‡~|ÌdvnP˜a Åi„!?©R±gÈ=CÐ…kƒ¼=8_z&©rp†ã9;!- Û‹۹2q¾ Òz0IPø6ÈÅoä¬Ao˜À%Bz˜Á ©`#”é9ï‹™#²sÎc:Û!ŸLLg¤á\Ò4Æv†œœçlÒŸ”±€8“ÔÂMÅX΂d˜Á ëø@nV!LgÒó æsBÐ ¥Bþ0b¤†Œ[é9çKc«É,CÆq¶p“ 'g‘š¡´š2ãËÉÎÅ: „™†…’9÷^pHßË“CúWÜ{?˜ß ©Ð òC#°ðf`€œhàg?4 ¥ïóì‘ÿôÇúãÿ­þÈÎc«2Ö½òCQZ!g ν™3õŒ« ©91‰sî×ÌèÁZµ@©œ×l…ÒØ³µ“3-¼¸óÌiQäf(-ƒ•qî“ Î@L…üa+¤‚ Œ òƒ¬ †0rþã°2Ö}g6›¡d(F±C>œÙœ Â4H㘡´’2¯™q5œû ˜É iËfd5{23³šÍPZ™è¨˜‘qÈ÷>0  J„ü`D+¤ª*3î!?˜Ò ©[ rrþ¡J«‘‘oo†ÒjÉŒf;äÃÍÉP lçŒæP(•³íš³í=¸l™Ù°Å·7@.H ó[x0@NH‹F`áìC=‚…7äügþøOôúûõG¯t¦½? Ó©Qœ&( Ò£HòÉL{äÈÄ{ @áÚ9Ó>JåL{Á‰5BNȇ3©] Û ©PÜFδףÈà  Ý¹ ?N,cÚÛ!¿‰3"}`”ùà a†0AI?ŒÆy±Œkï€ÔœWí€4Ìê ˜Æ i9/–™Ç¹ ?˜ÈÊ$˜öŒk…T0•rA~>ø¤‚ÁŒP"ä£Y!Ìf|I˜ÎÊ'xöZγOƒ`Dä 3šoò‡)mÆ4U“”Œ…m…T0©rqF¬`Ù ¤­Î¨ ÒfäT¦AA0³ÒÂÐf(•sì6JÊćý+޽æƒÔhF(Ò£X!ãUBNΰìJ–¸j%¹ÖÙë‘™û£èÕE/üïö½ÿlŸû»öµ÷žfõÊØÇØçç`¯…“éÑ»ÂØ}0Š(‘í¥flZÈ…%CèUvö\„ójÙ×¹ ?ô$+¤B±¡DÈEgá…çÉ® …’Ùó¡Eh‚!=zP¤FQš DΫMeÏ8Ðs†³ªÓ  ô'¤Eϱð‹7ãT;!-ŠÙ ڹ ¿²Õ.ÈEnå…îɨ¶ð¢7@NH‹â·pAN¶FH‚ô0C¤†!LP¤‡1 5Ìa„\ú…O‚ '¤E¿±ðÉãR; çR§B0•ƒs©ÍPƒ9 o˜,¤žÌÌõ‡ÙÂþ€™k…T0ŸJ„ô0a¤F¿1B‰úR¡ß!äǾ'ÂYÔFÈõ'÷ªÿÌÇþu}럹Ø_ÏÅù¿IcŸ+ŠÒy£0C dÈjƒ¼Q¤FÈ ù XC¡T(Eë€4(\3” €íŠØ¹ çq'Bzu¤Fa› $ÈnƒÔ(r”éQì^ðAÒ ðC ¤B d(F°AÞ0C” À6H c!'äÍ™ÝNHëÎí6@.Ȧ±B*Ç%Bz( RÁDF(ÒÃLa†2B‰Þ¿ƒÔ0— J‚ô0Y¤†Ñ!=Ì©`8#”ùÁxVn¾@ÈùÀ„!P23Ú!o2Jæ,ð0H sš DH“Z!Œj„!?ÖÂMk€œæµp ä#[ 4(†¶C>0u(” ùÃÜ6È7AI?Œnƒ¼aö(ògüpÈÆ7AI?@¤F0A.Hƒf`Ò1N5þM!Œd±Ÿ¿ê‰ÿÝyØõ:ϹWæ~öªgýwçY¬_ýQýé?êK¢ýU/b}èÏzПõŸ?ê=™çK›ø{AoI‚üÑWl'Ú%±ýÆ8ÙvÈ'<J…qâìN~2Û;‚ža„!=zF¤FA˜ =û¤BaANö €­ÿ£8¼QI?z‚ òfû?Øš>¼ï„´ð½…_0 òƒï­ Åd„þ{ïÖTÖ®ýc;cabÇ cÅ–'VbÖ`;vì±cÇŽË( –¨±®`E,hl{PGcÇþ¿wö^ÛÀ̼ç|ÿïœï{Ïõ×õ»8×û¾g/Â~îg¯µöÊ}'U$P °B€MX¡À(°`jZP ØB€ ¨¡ûH @ñ…€Dà"ŒÖbÂù¢…h^йh q³°gâTCÛ‘@mƒDàmG {õжŠ7 èQÀ ÀzŽÎz DA;€Emž(ìP`ÖVб'4 ì@ ›…u4ljè7 xB¿6 ¾×Qnø6—ó€øï¡ôÛ;¤ª+]ð<{زOöÕ90HH¾*ûr®K *ñÓ:+‰¹œ·U<÷^ô_y¢:0«Àç¬Ú]Œç‘5ªxlä”?3wŽ©êÌG³,L1lÈœÔÉy㯒:¢|ë³­ôþÔìû¶SM©åë)ófíbíƒÚÞ(ÔsПò4y®3Ïaàþ{îy—JŒSÿÉNÿ|GcX±Ï# Þ¸J›f‡-¨8ÊJ¿— ܌Ôk:êìú]ÒõüÉVô{ú$ûkò|þûã¨1Îàú ~*Y>–=ÊsºÔÈ×W©JÞu¶t³Ò[Ÿ%ïvÐÐQÁå.|³5¿œåçYdBî3Á}¸¹o p]=®{\kï×7–}l ^£^“"÷"+mš {ñ­-Õwíbyö}*›'Ë@ÙÇMô}PÈ>ï<‡ûv×7àú.Û¹‘±ìÄ/6e­sFìÑvñ³•öÕëöaB ]̾¶ÿ께˜Û6ðO¹‡ÜováµÓã×e•>O¥ô~K§ÖqßêDz%1ÓjþÒóµ|>½ü—ŒVOïÒ~CšV÷nLÓ/ѬËdì‡Å}ÖŠäì‘—^¼Rñü1áºV\·ÐÁ\}#‡Æ2ßÛíº—yŠ\I|W3šQÿžu=ÆŸëHÛ­9}êq´ä“+ÜŸÊ¥\wË`;–ù¼]‘²õ5ÊëUnj7Ftþ©ôšŽ’_e4{ó8d÷{ó 9÷€çð”Í\åøe ìÏÆý˜²l‰î׺¹˜_¦Äõæ 1SÇbY/Wê5ÚÓªÌgå…ã’¯^zrBã;äC4sÙ;ß ë‡û̈>ÔoT‹“JˆÈ.×÷‹qÕ5ÆyÜ m~G,{?m[©º_®Ñøs+¾—[{œÊ<_!—*€|­ªúãE³Ê»Yyïýe^×<Ç—çщþ+•Óårè1Žø?cGÛà’ITvhp¯ÁcÓâ'¶ÇC4!¿àÒ™¶]ˆfONÆfý)ÓÙ?”ÿ¾of\õ¬œègcÀõÎGÕXt¤qkè[¤Ä›úITÂ7̳ëqÙ´òõÃ*åÝN_w4óMùdNŽ ç˜q_=1—þ²Jô»£âºýi¤Œc¿ývuˆcέ{•uI´®T»l3ê—s´þGö·™Í^W¬\âz§ŒûÇñúûÌ Ù‡˜ûB»ç“Z1Žñcå¶žqìcë…ŸÊMH¢Ë;?Õ/Rä8ÍxÓhÚùEM(õÆõö߃¢ÙænSÇuŽ(×#ÿÉóhÏ®^PÃjz©â9šî>‚vŒ§[ð°ÊŒ8æÙ}R­¤ITnbÇ:Îc´y`ÝÔa›’˜O;E\ß±ïØ@¹®xž×'ïÿîýØ£sªúéÓ%§ë㘘;œDÚ™·Ï8uŒjüºîÛ²%äŠõfM‘­ýß”}#åºrùéf‘}׸ώè‡#æ+1Îl§ðÁãØ~«w¡Jqÿ»×ü±ôõ]èŸVÊ»-Uøz¬°w­hVñcÖ7ï ¹óŸ<ßRô9%ùlæ’ü¥ÜTŒ³¸qÅÙùDZ£êõšáH¢½§®Ù­=FçŽÜLZ·AK«ÝÐÒ š‰>QÏçÏÑwí ŠçX¸ô€ëÆú©níDü6uw´Êz¶¼sت8FŸV'oÛßveÞïÕ°M4›]pà­o«°î¹c/ þ‘-þž‡$¿­ë*žÏìîƒhÀ8/bzO8ü&Ž )¼ü·§¥®S®·ó¶<|”¾ú¿»³Ñ³3å-™x¥È°hVnòìu¥;÷—ó ¹ÿºèvZÅ}ĸ.¹?–K'ggÃÊÙÏÎNLשqTãy{%sßù¢þèL=VŒhýpf4»v%Ùž»Ÿì£Íu"ú¯Ý•û4÷oJ§ŒÓlûŽžmjîgfOIŒ ºNÆkËä;J5ª\õ wgJÞ›yÊÁ‰ÑLôûî#û”ñ:ëòhÑèI>ŸU¢þ_©DŸüæ²/”K'ç\çªÓ:êö³¡Wm½rOºN9]Á‚G(§yZ™lÙ:Ql9uÛjÝ£Y'ãÍ,»­z¹/ò~%æy”|ô3IyE­¥\$1OÌ£KªzÚ˜éï,#÷³ A–+¯Så/ÇËÕ9B£ºî-³-KjÕøàšZ-¢™˜›¬—}®¹ÿϧlq&ÎP\‘…:eî[°úÃÆ4s×Î.½{–õ‚qF{ýÖ»ô˜ýì܃ÁgD_§Ko+T;B«Ú?­°(¾Y¢\Õ7 šŒÝÛPÓ¾œ‡ÉóQÅ|Ð+’æ)w¼Ž\ç.½`œ–…»]ÿÚo?‹N´õÙ‰ë¤mÐõå…·‡‰<‡$ÞÚ’Æí±øy«hÉw»œŸÀ}ïx~‚XÇÏ$ŸÂ þ{'³ËÀ{?{’ùêà»×iñ’wÞ¡gSæ¡~ÁS7§‰®à¤hvlQßC¿&ée]þy¸¯6ωpÏG4àú¶«ù¦·½Çü4Ÿâ|»N¹\¿¾ŸcsÁÒ,¡¹”—db׋·ú£y\OÙGŽûÞŠþfU|¾äî׉ëo÷R^κ Ž ^ZìʽÒ7(­øÌ5F¦eÞ‰Ÿ CŸo†Ç›˜boÏ[!ºnŒûXó4î£ÌsúÄy†øÜ²âúÙ…Y :Ž%”µ,‹i~ƒž[vñS½ÃÔF°M_ÒŠOV{æ!4É}KÔAi)ßó‰œgÀs,\úÀõ'u¨P;.S;T´šúö4{WÏsÍ<“Z­(X~h;²”ù¥qÞ“”ç$ûÚóºåuÔÓc\¯ãÚÜ’þ«¥Ó»G×TuÚݱò½Še*–÷§¥7hëÒ¦ùºŸ:Dwå˜y`a{úúvsmúf’æÅA²Þ¹Ÿ#ÏÝë* i¶e®xÛZ¸OªK§ÐTÕù°Üq¬ÓÎ,Aíwß G…òî6Î;D>¥Þ/Zò®UINhø:æƒ.øî²ŸÞ‹ë•$•ø¼}'ç{ºtëé_²ìͲqì±â§„Ø 7hOssÑf-Ñ–¦s–V iGm —×೉¹âìoö’ë‰#êá¶œÇÌs’Ýÿ^zŒ3½hëeSÊÇ1›jè¼A1=Ÿm0ç;Do7ßš³pF+*P<þÌ»—&vè§Ö'óúÈŸcîá ‹³C«f†MüìåõMÅówÜóõ ¸~VÁÖ´pÛ"Øö{Ù¨mϦ‡Sí2”ÑϾ¤9íŒ]øÚÄzxÿµõ÷¾Œû5ó¿ÏY½eðÃvÍr‰zÀu×N›úìK,›r¨ý’ÌÍlÔ¯L+åù£R¿I>¦ÉÛ„uöÌÒŸ¶3ënœÑOöëå÷¹r·Â—s *(ååu€ëŽ[’éæ³»±Ìïf¨ùôÙÏÎ[ªÚ`¡j‡µ– hHB jæhvûã´!GÇöc_†··½ð–s!»¼jñxyIùÆRž®{®~Éž¿‰e;ÏgÙa#ã©Ä曦[èÎÊ†Ž²õkÓoWÚ]|”=šM>R¾ž¾ëßWöEß•šÔ½Wa)g!¿\ÿî¹×ºTuä‚#6Dzl)µ¦Ž·ÚèbXÉaŸƒ-T<òfñWü¨ƒÁ'â—lѬî°î+o*ûÉó\®žÚ;Ó«Þ§Û‰%®;E°§^Ëæ”÷ÖÖyd£ák–%)tª¿íÆ:?꺲àÌÎïLlyèÄú]Wþ¸ü§˜ßdW=3:“wÑRëV¨Õ²j¥ XNŒ°oRDÅÒ[ï·´ÐúÈ5ë]þ…êÓéQö$ŸS}÷ïçÏ5>OuûVœ/ẠåÊêÓ>–ÙøÄ6®p“:ÞÉܱX} 5m8Üï_“¶]º­^Œ‰]<·°uoÆû<ÿ}žü\Ö§«žu<'=–o·výÈf7éÁ½F9V—µÐð3ƒÃ*©G7Šå^o‰åºõÐÙ²b_ù>òyÏ¿-ž»“gÛ§ÙÄzÆu×g‰Vƒu«Ë@þ&å]tt×ülÚö5îýƒõM¨~èFÏ瘘˜?Ò÷GÞ™”ßÄó8Nœšô-nOâóyÞ?]õqœ)Gý_%ŰyÅ甋t“^o.WhGòA}I›ÓÃwúS¦˜Ø‰b:fÛÖWÎãæ¹dü¹>oë£G›ä‘ö=ÊKõ(Ö‹ãŸk·,ZÃviP/â&eÿ´¹ëš=é²·P©¡ßßMõׄ™ØóÙkì×rô•ç‘\ŸÜ·W¼ßRÎVPªZ̯Œa‚+m ËM*ø«×äRÁBלÕvµ&ëÊYº6±Ï™…–>òþï[¢_ëïò:ÎU߸nŽÄ—Ÿëúư+þ×^ž¸~“æ¯ê½­áA)O¶-MúÚrÊ‹¬ÑLÌ[ê#ç¡ðy÷Mï«X/j\wñåú¦uÊÖ}â¹ï_Þ¤q-Z ~žé ”ÓÒ–D?Ôh&>îôòóˆÏo¸?öÁ B0©´„ëîížõ̧ì1¬â¶ó~/²ß¢G˯üéüÒ¼–ëê‡VTuÃöýºE31×£‡Ü§øOþ¿~ìðï«Ä× î¾ÈŒÓ­e•ã[œûXÿ‰]é½oÑ„Ç×fo[w€^$¯wö¨Ôœ†´’Í£YÀ˜þÖÄë¯GþSôþ,å’|VñN¾o䪌~Kóoö±õ¾,¨}‹ÆžÕwì*Øô([¨P“*¼vJ4u$û<óúæ~øâϧòzÀ}½iÅ8{®Å*W~ØÇz¹‚NnQÇבKÐÁ >›MTÑÂï ¶6ͺ\}×ìÆœÞrã÷…¯Ïy?Û«ßš¨äʲº«þ1Î|L=ìcûÜþ·¨ý–¬…ê îÛ¼°}&¿M£™˜[ÖW®'Þ×ø¼÷7±~+¥Ûoðè–ªÞ=BrÙÇFö÷þ8kÆ-Ês6B·Ôš÷óEø—†tØïÊÂ[_M,8fú¦ìyúÊë>Ÿïð\žûêÒ®?sa‘Ús÷±•¾Â†Ë-2½ü²5ë×ýôfòûƒ½×6¡ÜW?`&jbìµ²Qó|}äç ÿ)®›îÉû|âž×¥Æ8bÎó>öÇTS®¼§n‘µ×ÑI!WöÓëM½fÏž¥¡¤ ÓgbÞ6îü¶}ûÈþÛâú¶Œœ/Ο/Íæ¯n¸iµoºy´ã,Ë™V{yÖ}ìÛ†™_†=¼EMK÷<[hÃ~Z¾måÐmé`åùÏgUfM½…dð¾žÃÕ¨è«'å=­9¤œ’¼rî‘K/¸þ¶„nMÇÌìàdabr›ØøI/õÜOÞ±[6/Úž öÃÍ¢™¸^ë+ϧäür)Gïç¼m uœSö%vé×xxdåáþföX°—/q›l[õ¥/yï§Sm¿ä˜ÞŽNÌi8³D¥h6óþÙˆ#«¬3ù80÷«æþÃëßl4–>ò\žßºÏ£íçû²ÔÄÔ‰{Yb3›óm«Ût¹ÿþ½¯ÇÑÜ–™½û^lBE½b'1±eyêØTº‡¼.è^lx³Ç†R^ÈSé9];].”G÷TuµýÉ#ãìagßwKœÜï6}º¶¯UêÚ8º3%Áã]îTædeÌÔMìÓËø¡kêôóaxÿû{šŠçCùdÚª?ÓLº_~¢N0NhR–Ç¥;îa§šzÑâÉ·iú¯oîÍŒ#1—âêѦȢV&&æ{ô’×—|_£lEË×5ž éùš™Ö‡ìý­û¸¶$®ãÅõ¿ã,n²<èØÉÝluLžQ»Þ¦)õƒ­CÆÇñUöÖŸu­¿Mpö“skyqßqþž†çH¥Ë¡Ã8£Â=åîÅæ¿<µ«XÓ;”©w‰¯ãhðåÃ>-Qí^‡[æÁzkâž—'7éŸáy\EªG›¬Oq~“x^®K7çµÏ*çì^;™òyáèì}î_îåÞøÆQÜLgLȶ&XômÿÄÄr.z—·Íˆï-ø<›ç¯ðç™û?1dÞ'ÕÂÞƒCºG³fu«×ÙÝ_Αàºç9(|ö¯r'õ§äDeŒ¦Ðfv½ƒúÖw»”ygµþý§ÇRÿ"†ðñT%Kx@HÙÑLÌ"ç ðu(ÏAáýëΛ/ƒ·Õ¯–nþmÀ8 …m1ïL™KøËÞ¥9I±“Û‰%ÇÚ¨—-üˆ~žUdg·ÜÑ,çˆíÖŠ•ÿNüyÂsVø9¾Žté×ßã¬gŸšm*׳æ]òú”û-ÅÒa«zèÍýÚÏ\˹ÍÄn•|Rç^¯Áòú‹÷®?1—&U%îÛV–×g.}`œE¶Š†¾ýײ†sÏN+Úú.M¶åÎ6,G,=ºôj|¥âZP·æ§Ë!˜¿”èIãT§¹g 5Þ0¯¬¼/+Þ'éù\™xý¹ôqZú •Á:ä¿ìÈ×÷.u<½i^¹S1”i¾ E;ÐÚüб†›Ø¬¡/Ó ”ó’ù{q]òJ~/-Öµoº}^©ê gj|-øj5#×Ê]ÊÖ£E¤yRŒ´¿Û‘Jöž<°¥:,æ…å(ïc‹ó¾jòßçÇó÷X.}àúg[ ‰Õ«Øùùã‹t˜v—¤u=åêú¾Ðì{èPñ›«V/3±†¼«×{ñ£Ïóý;žcÌבË/L×°©ÕÒ}5Æ9m¼~¢jÜ 6õ·Y미KÇ=ºl x²æß¬ÒÂ'Ž$ö@Ç2±×;Ó³¿¼®çºáù¿¾ÚúLm*I} ¿¨\ß«Xh9ÛÿÙÔ#ê.EéÑ·ÄÎ}äSöj‹â3ÚÒŠhÝ—f\ß÷n¹ì3ÈýŠÞw¹Nøýp_w0έSð( g_ƒýŒ8~—VM¯¶·ÇÈ}´ðDõÞiŠ?«ÇG5±‡ù³ošœПr:ù|RÜwrª> Û¢+üåÜ—^0NPüȦ‹n,b/²~Þ;æê]ªQºq›R´¦typàlÉæ$ì*æzibeç–:³ ’uÏóYø¹ñº™ä|Q÷b.•ã,_ùÖ/®þ|VC8¾òä.]ë¸nV—¢ûh¯±QB¥š’saëηîcÝ2xÇä×~¼ç;sÙëe3Kç5>HÏEâ9ª.àúb~á&žÏ¸K~†Flye¦*Zª_)šRr½•[*ï0Ióº`ùüÿ<ü=_-ÖC]âya.ôNU¯•çç¬K5Ly’éLá@ËÉ fò,÷²äymsÚ7—ÃÏ‹ð÷ \/â¼H:/‚ë× Ëÿ<ÍWKXûyŒêšLGüô3’™Æ¬è»,dhš1~^á,å0_y·6Võ(˜ñs\7ü} ŸGˆ:ª˜N÷ŒóUˆÃžBóߨÿ>.™Ž}Œ Zça¦µÇξMIÖRñ@AQ&æº-kÉÏIñ}[ii]üT%æSe&žÃîÞÇ"1NðDï;÷O¦1­£KZšL-}ʺ—J±LžÞîHµ4ñV™Ø¥1f_ô§õäÔ¹3Òr•ü¤:±+{±{w²‘ØO[HûK•D½`œó¨¯GT³¨â`áG2U~¤øÓø=Ô­ÅÒÙefv¡k‡\¥˜ij}ïšÚçö¸nøü‹Ï‹Äý™@ižQOÔ Æy›2ëFõáa$œq8™*‹þ¶;çÒ®ŸÙâÓ·®¤ÅÃ7ýRì?CåûÃuÃûJõÜ%è¿sÈ¡šb޲‡>Uý-.pc¾þ‹I[\8ÁLCì[_ôj¹[ÊMÒQ±õ]rÍèab_ ŸÑõÂpy?Ÿ'àó{¾¯ÃßǺô‚믋¨~êéÒpŠ/ÕÑûn2/•íAƒE»¨Jj±ÛÝŠt¥‡¼ÃªšØ«#©«KÝ)çKñý0~ŒŸ3ãÏ}¾žtéãôúÐ¥\…¨eTòå’EI“Éá³ëãêäh wtrŒ®×‰º¦¾¨_ÂÄž.µ¨ÿÒyÿ…ï·=VŒQ᫜«(þ=Å÷ûz\_QjlÎyÉË¥ùF2 ZÆÖ³y4å¬×êP—ë4 ÓOgî*MÒ>Rˆ|ßùû(ž÷.®‹²_Gòu¾K/G(Jåå•tkÑUÏÎiÉÔxsÙ¶ÅŽ™hÍõÚvÖÒ‰;/©ê™X•¢¬Ò®N?òèø<†çÐUn™zÛ?M%®›Ê¦{^Fbœ¯iÂA¨5äýÕ÷ÆÖl)”Ÿ¾”ÚD»G‰.S·MìÑiaX_ëáh•Õ4=„ñy·8¿¨.¯‹ø>h£M«¶œ/+ê×yuóWMµô­Bá2vÏÚ×ë³åù#µyØéy;BíÒsž‰Ý.¼táQÊ1ççbx‰9åÒ{³cœï¿÷9~Ôc=]Ü3¹JÁB)}9:S«¦Fz®Ï÷[‰QÈšsŽÂ¶‰)VóFË}Ÿß>ÿŽ7åõ½¸¯$­ïû¤ª—Þ/9nl´5ö·2…Sh¬wïEË·DQÀtï ½¶Îô*ò渑ͱîªùqàúcäsWây›Ä?í›ÇºèDàú==òlXÂf*0¦eÒx|Ž ë[M{;érʬÙ‚¨ÖÌÅÏRÞÙÅ2…»7¾:Z¾>nðu=?wÁÏ#ºßw5ÆY@ êÉ¿…æ?Ý=cwÁz>Æó÷á¥vÒçÃ'FýnìFoäßÂÈ2ÕÞ?l·ïh¹Oòù¾x?þP54iWü~Ï)ÿ½ø{V—^0N oZ­Š¾[i˜«‘§Ð†,½«jµƒfÍ=èC7:P¨|éeŒÌ^èY§›FÉûüùÂÏ©ðÜlq>˜[ú¼Ä÷‹gÞ!0q¹óERè7C¾v›C¥¯²Z¥ ¢‘¯œ›²\0²®M&´ÜPd”ü|áûHâü8FÎÕÇÏ-ê×/ìz‘µÆ­œu3«W -oúó¼’¶ÓÁ©K?=+Ò…î]>S`è+#‹ë8æP|ˆ¼À÷+xÎ-¿?üù(ΗÒÄs¸§O‡)SšŒßA¿¸Ö¥ÐÈi'›Í*¼ú¬YsâA ¹>F»2²wŽK?bïf¶óºL!¾Ž•ó4ù|Sü=Äócv\?òøz|'íÿ8=iYÙÊýJs´áÆmTüãÑûÆ€V$樛Øó@‹÷œg?î¯3~>B쓨Œm7Üy©-—n¿Í£/ÖE3²ù °GQûKÚ;¯*¤ÐÏ=>5ÜF«âs HdMÉVuÍâK,[­u-?OøýÇ{¯ß÷}Uå9wîÔî™>ér;•çh#E¦M4½ØU|S¨šµZ¾R¯¶RŽ.“ N} &ñ\¯‘™Þº4¢Úüo¾O-®#?JçîÞÉçˆÜóÒÕçEÓäÑïúEÓ€./CÔJ¡3jùü•¥Îy5¿J#ÒgKñ挑µq-ÔÇÈëbþ^èàÒ >K-¯¤ùØ{ù}‘û9a=ÆqƒJÜE¥\/€ShÙönŸæ†n¥-‹>~£®O%… #«ŸíãÍ„qþ<¼ñó‰îóc®ß«À—‘åì!×ô»Y yVýZ¿Bã­ôÅÙhø¦Z*ÚÕ èíí[,IùÛ½^Œ‘×yéógmÒþÔ3yÁ=ç:㜯’ãéʽ4º°°“B_7Ÿœ21ÓV*ߦo·G“xžÃÈBrZé×Vcåõ^Šz¹¦×I÷Uâ<ðù}ŒK/'­¤nIø 3íh"lD¦PêÈ´OÌ[(o©Õ}êžmFå|¦-:4Ô(åAŽ‘ßSð}i>¯àû¢îýØŽëgupÞG³æ~ËY½K {?ôËÕ®[hÿ)Ó6Ú‘ÿbC#{ñ´sÿ±êÑr_I¿?xCîÇâî“îüŒG¿Tu«œñ¦I÷Qí?ú®Kí•B‘¥¯”¿ò<’|ÏÎ=·wRkÚ¹òF+Gu#kthž\£äúâúä×ß§Ï‘¥[çcœ¢Ÿ/¼8w2†Ǧ*ìSèü¸=³’ÇGÒ}ë»Ñ©ÆöÔ :Ûû‘åk{35ǸQò{I¾->Ÿ?Éó#÷ý5®?ó^“yÇR¶À™4ÃS(°é€¼>6S¶–Í6öºHýbË&M˜fd¬ºh»?ÿ½øyq¾➟«Çõ« ñË{2 Žê¶—p1$…b–¬ýä{fåÍvÖº* ý¾½mR£#›âZ0‘ßCòy1ϳçë<~?ø¼Ü¥Œ3Í^¹½òy]òzœuøèZÓ¶|ÔÜ­I»ªÕÕÏ¥)bDÁYÖóF5L×1h¬4¬ 柳ï ΨøççåÓ#‹Ä8A5k$ôœ½Ÿ&j:ºÆ¨*4ikxΨk ň–±í¨®ýÍIÅ=#óùºûؘ±Œ÷?¾žäóþœôŸ]¹öäÈòéö?­g¶aJÎ"%вvÛ?ó ÓÀ_òÄ®§½Šo——jM®ãõŒL÷x÷á|IcåyŸñóÏü{|ýå¾OiÇ8â¹¾$¾7L¡9³“/ï´Žº©cί~@Âi6ÝY#+ÖO_êlþqò~×µhH|¿ùi‘þ<¤GÿTuÀ6Xj$ñïžBÞe[›³Ê:ZåßðÒ´Í©ˆ°<Ùhd9b6u›:nœ¼OÁu¿ç$ÖÝ•xþö—tën%Æq œyçÀAäÚÈH¡zªï÷OZKW‡yÏ‹ÿÒœ„´\¿ÉFÖn]öæo<ÆË}…×±øþî–J¬‡·Rü/ò>“K/§ÝäV—æ7²P‘zÍîlh•B W¿öœr"‚Íëpý|І^Ž}2ÿÉ0£ÔÇËë;Þ/ùùñïõRÞ¯ré׿)lƒž³Ð½8{óÞS¨ò–l1Ž'k¨Óoæ7ûÒŽ6œRšGYboGmOÆËû|þÅç­üý×½û÷¶ §J±Êß»õ:DmFN¯^¼F Í:™»L‡««ièç¼ç¦×íHâþ’‘Õ¬›ö"h‚üÞƒŸãå凉ÓãÅõ…S–VÃÔ1g{Ç»Š)ôN1=ñÔœU´¬ÜîÛw¡¸Ñ¾~U ™×dßãÁK'È:á÷…Ÿ¿âßws_?Xqý|7Õ‡mq‡éIɆ…Ö•K!ñüåJZ;ùÕ°%:ZÖ§ÈÈþÞFðä VÀŸïòþÇÏÓóúr_cÇõS«hjm{„Úx]<ÿ¾4êöűœ¿¯ZN¶zÅÂs ÓQ™âw?ocdζWåè1ñ}þ9øó߈ÏûÄ}—/â÷£¤ª?)¾Ü{ÂŽ(§zœ3¥KR…e”¿÷…ÄRƒ;Ó©µ½/fšid£çµíÿ8óùùžqÌï_ÿ¸ßA‰q†wlXÝÔü(ÚýÞ\,W í½i8þàüRš[¥ËÉZ'©ÛÀ vGY¨Þã^Óù㿟|^ÌçÃüû—îuû£/ÿ~ÂWñý Ɖ?'L$ŽÒ›1M;Žÿ”L¾±?½Úsj1½/Û¼¿îtkÚ]Ô̯֯F¶cí‹qzg¼ïñ÷²â}¶ªø÷Pø{v÷ïUè1N×%W­:F¡ÎB9_&µð ºÐq»xá¨9¼9kœlÄŒ¬®éêŒ];ÇËçø:2ãy ~®ßýû4ŒsaÎy¶ºëqqjÅÝßÉWåÁª[ÒÁûUë|ØÒP:idcÿÈí˜Ó`ã}–×ñóÏæìüâT/›ða²—‡¼oÀßߺô‚q?Þx}þÝqj¾þÁ w’)ÁYs~Ò¤ôˆZVhs¬&MmÔ;¶G³o(ðë±ø òùþÞûì c?*³Ñ¯Çoíêœ]Ò£¸?aÅõÛÎ}ð1O£Nƒ•«/&Ó±Öí–w´Ï§àŽÅ¯ìQƒ> µ¤¾‰ÝÏœèÓóÓ:ãï£øùQÙˆÏÿÅç@NQ7gQZ»ß&ýb¥ÑÚmn³dXñ¢ß÷ó¥sþtòÚêú ÏŒlÖá`ÍyÉë™ïOp]òs.¢nÅq<¦ª#ç›ï¥®²’Ϻ愽ÉÒyä0:|sûsUcŠ4ïö–>Fö®ï/«S'üé{n÷)øß“ßÉ¥ŒÓ̵—Þs%S›Å‰¿_{6—ÆÅøÖªQ6€¾Sdîïˆb§&mëq|‚¼þâë+qþ ‡ß÷û¯Æ89ëþÔyî¨xIɬ>öSíÞsè͵›Ó;ækC·îí=Wü][ÿ$lÊšä÷,|~ÂÏ¡ˆ÷#U5uyƒaejVL÷~XqîM4Ú"nÄÓGá6­L¦Ýòxï˜:‹^Æ…ß^ kOõ³”õÚ÷‡nøþ0ïÿü½=ÿ~߯pïÿŒ31sŸºÚ'(VøÚkX25Õ-.¿kñ *6îC«!1ZJ9YþìÆ+F¶Ý#Я_®ñrÿL¾?Uõ¤ÍôõC®d¦7n}îT¤²¨\ÿÑ”}î/qÝñóç.ລ»¯Þ{Áy’æô0jì“L6½²U.9ŒnÌ]0ê[…ftklÖØP#‹XýòO”ç?üï ö“÷ª«’¿~Uýã%ùïç¹öÿº‘‡ô™…lk!«Æ=ÛÚ,yîêÜü$C%#÷ /÷lWî'éží*ä3¤å3 ¤¬š0)ÛZƒ7/ɇÄ.y‘DdÈhPHF·¼šD·/‡äGþWy5ÜoBÎab† ÷œCîóæžs!t°ä·ë'åy ¹¯Z¸(QäàɯÉÝ—ÜSÊÀ¶ºe6ØÜrlœ’çî_e6pÏÝ)ëPð%Äüø<¹û^&þï¥àK.ø^ºgÙð¬CžeóWY‡– Y‡Z)ëPȲ¼/ï'-iqó~²»ù? Þ—¡À.ù@EOˆ5Ø€ZòR@¸!ÀÔp$P@Ä! ø YØnžP ÀÏ-÷U/å¾úeÈ}µ/Þ@ á[€RÈ: E°e†ÜW‹ä·&ù¼ y Bž—P¿Â¿¿Ëkø»¾ø?¹'þW÷ÃÿÊ^èî#çÞÿªÿý«ÞgðûÞ¿CÏó”þ3…”ÁÀ3]OKÁoN#åºç[GH,ùÎù ·…ƒ4…˜Y(xŠûI]Bꥌ.!ÇÕ@+ùXzI>–‰2 …lëOÑ›NÈ^ˆ”²­C¤lk¥äåûWÙ ÜË7Rʤ vó±üW~âîy4‚(þUR)æZk!Ké™Ö …pÌÀ â1”3­5‘¹|úLk›—¸AòÏè!.xW†¸ùVFI¾•‰’ge$P@t! ø»ùUrÏpÁ«Rï–Ùî–ÉåžÙêø¯J-Äj‘¼* À´r[…L.-Dl‘<ï„\…r¹þ™çý;Îó¼¤ßÙî!æ*¸ç¶Z—”?cJ)«Ð–!Æ=«0B*b÷¬ÂH r¹lR®B„”ÛªC[Š< 8VȱÎà_î%eZ'¸e+ØÝògÒ$?ß¿ÊVà~¾¡RþŒB‰ |øü!šH €pB€ ¨3øyÚþÆÏSð7<=Ýógxf!ÏŸù«ÌBk†ÌB”Y(äÏ„§àí AZ¢  …8-À 5ÐyÖÀ b5;Ð@´QÀ v €£€'D l@ 1G ¿[nk°”ÛêŸ!·Õ ”|p„o>8pš€ødÈmµJ~¾Bv¡CÊUr¹„:þýŸî‘Õÿ_é‹z=ñB?üïê…Jå!f¶ ù ™~d¶:¥Œ™p༇¥ŒB÷ kh$P HC@ðC±FH,ùšûK9\ )«5 8ElÖ»’—¹{.¡[ Ò€Å%åV‡J¹Õ>(tÃßä9ØÅ%åÊ^æ‰Àÿ?áeîž+£ørexnµè $ké¹ÕN ƒ¨,@‰¾´˜¥¼˜Yíòd¸y˜›¹ù/|ÌÍn>æv ÍÀÓÍÇ\ aFOˆ3Ø€" 5$ƒ¹ Ú`·¬Ö·Ì-!¯UÈju-ÄlJ: 8¶%Äœ@—!«UÈÛÒAðV œœòþÉÛú¿ßÿþ™þ¹úH¿“×*d<¸çµ&e1³Æ |¤\B!ÃÚ=¿Æ=—0(2äFO)oË.e¤=„Ÿü þ&d[£ $¿ y­BÞ–Ÿ”Iè”r„¼-¡>…ÿ+=ò§?þOéÿäÞüŸë‰Â}4{ˆ­B.Eb¦9­iRîMp ï3äW«Q¤QÀSÈñ‰À)dTHùƒ6 FñFåó½x>k8Hz" Ú ”(ê0`Ï=(dWGI…®f)»Ú eWû¡ðÃþ"×Â@1˜¥¼›P`j#( Ž`jˆ$ xfȺñü²nxnuÐCP ¥äV§ kdˆËâ N ƒÈ¬åÅÌê0)³ZȺ±ˆ.8NʺQB€aÀ ´¢xAŒàZˆÒ¼ ÌP`Ô ¼ps À4kð„`C ¨!ÜH €xC2ä²òì/!—ÕOÊeM:Û | îpôyðÐÃAÐgÈeMzˆ?Î ¡$æ€ý3Oügžhðø÷ê‰þÒ˜B.«.Sú\ÖDà#åõ$?kDV1¿Ú=»Ç=ç0 x*~djPÌfà%e9€FÊrYƒA"ðG‘GH…®à‰‚‰Rþ¡_m~@˜”_­+(æýùÕz`Jˆ" 8â°%&d"-„b^‹Ø¢1/Ç@ ™Dd ˜ÌÀ ‚2Ð@XæŸÓgñ|Dž ôWùˆ‰òƒ¥|D?1Bc0H~eHzˆ3ø@ á è T+ðXÃè Z PB¸aÀ t°(!â0àZˆÙ¼ èP`·\ÖP)—U“!—5ø¡€"$уDàñGJ $ÿ ¹¬B˜¿JÌJ¾ ª%1ìŸùO4xü{õHá~X€E–)}î«P Z Ò„35Yÿœ]­Aáš—kl@"ŽžR^¢hPÐfà)ežñ|Ø©ÀƒAðA¡‡G†¼D!»Ú (~=°HÙÕaRvµ?Ä@ QX€ÂN …@,@ ‘€h –(à Á„;Ð@8fàñ„;Ð@Dfà!…;Ð@PfàQ…ûÏ?²«ƒÄÒ?²«±éAðƒè"@ÐC| åÅìêp)»Z!&?ˆ1¤=Di>f8H:Ô ”ipÄjJÖ@ áZ€â  …ˆÍÀ B6;Ð@ÐQÀ¢Í!Ë3Ö„ Y)CV»$?ˆ>B~0H~hRvÏ•B0H>h ó×þé‘ÿôHƒÇ¿WÔH×2eõ™ÒgÊÚ€ŠÔ?Š58Ek>2 ÍÀKñ#R‹b¶% : 8…m–2eC€ ¨Qä‘@BVà…‚6à'åhÛ?Ò„~ !X¤\í``>E8Hzˆ#ø@ á è  PB,aÀ´(!œ0à:È”Qp-ÄdJ* 8²O%î?°+dKzBh¡“-isË–T@€! øCˆ‘@1†ð‡(#%aƒDàFH"Õƒà±F€4!s¢µ7¤ ™Û°ø@ÄáÀ t³(!hp­[®¬AÊ•ÕfÈ•µ>( ú`jˆ? (ÐB€ ¨3äÊÚ€Z%æS AÌ$Ö¿ð÷HÞÿ£½ÅÿŠ>(ô>Þçþoõµ¿ëiÿúÙ¶—ýïö0áoeîW¦µB¡gMŸw­EO²%z’hÐÌÀ ýÈ@‹Â²/W¨[^m$P ØB@"ðÎ@ gb¤,k§ð~Eg>(¼p!§èP€Vàƒ"  E1š Ò@‹Â´%ŠÓ@‹"µ% Õ@‹‚µ%ŠÖðóÜjŠ7ØJ‹¹Õ q0Hþ(æHéÁ Ë‹¹ÕÒC>$|¤ôÀ À}%BzøëAðA_ iBŸA_I>Jpc>è+áÀ tè+ D_ áúŠ„eÈq둤Œ[µð.bKþ\$P@t!Àü!¾H €CÜsnb Qý3Ïúgžõï7ÏÒIÿ?BQ+P¢8Ä3ÎÀEl@bi@¢M~(Üp&|lJqpŠÙ |PÐá èPØà…âv A‘GOzHJ|(°~8p5!‰ X¥|îü ŠIÁ øA ’HôÀ | –pà:ˆÆ | œpôø@Dá è &+ð ÂAÐAXVà¥Äý ÈÌÀ B3;Ð@pQÀ¢ v †ø¢€' l@ !FOˆ1Ø¢Œ 3DÈôþh$P@¤Á øC¬‘’`ƒ…loàáFHâ  À"Ži@1': 8¶ˆ; 8"·%„nv †à£€§÷ ì@ñ›'@(° x¡€hÐÌ@Æ u"•ãÛ< ·ÇÕÿ³ó+ÞËþÿô°Œýë_Í¥þ;úÖßõ¬¿ëWÿ+½Š÷©ÿlú»þÄ{“ð€n¾hQ @ðA„ƒ4 C¯±ôТÏX€}& 8cJØ…wè3ž( ðÎ gFP@~Âyaa¿E”üPH@bJöøQP:”(QTaÀ t(.+ðA…'СЬº Åæ:œUX“¡?hPxfà‰â v A_ˆž(Æ`jeP /„P£/DúB°5 7 (ÐB@"ðG_ˆÖbè ‰À=!Bzˆ?z8Hz|ðC?Hzô+ðA/N E/°%„œÂú QB  H¢€B 6 FˆžN(PC}i·ÂȾM=3'Ké‰r.œô=­ÇÁ9iþ‘¤K G(ä\:îÿ$Œ‰q¦ۮl3ž¢·§“úÞºK ÍóXÝ:ˆ„Ð<ýs [ºËôÙFÖëèÏ{«&Ê9{ü:ܧ(ðÁʑêæ w_7+®¿wj¦c!ONÑýñÁºE‡ïÒc@ñ;×[Єë…2÷ÊÝžtÎ9×ÉÈD뉲_÷ký„J¾ö_T/ºï.´æAúÏaÇ8åÖG?®Wæ4M½8ú¦cÃ]2§X~¹¿³ëÑ{úpÝž”|¾àÆE¬ÚÓwúšÈ¸/÷Eý¯È~×ÔÞûtWÙgÄUÁ©ê&Óÿ4…<Ír®Á¼»tùmBÒ´ÒÝX¦€O–íB $8oG1Ñ×ò‡O÷uây _ì.K ˜ýìý46°ä´å«Ëw–}và œØ¶¸ì¿'î[Ã}ÏÅûóNÊ‹­™ÎG0㈾ìgh耾ïÞÜ¡SË‹©Î>ŸÁüjžmüG µÚßþl¿ì&ö"®v¯?|Xxpœÿ½ó€jb[û>¢(v,*ƒ ‚¢+¢flXÐØc•رAEcÇÕ±!% Vlì  A)±ƒ¢DEk¬`ÿžÉÌž»Þï»ï{×û-]ë¿Î]÷ž;É<ÿgöì™ü„§Ir—Mø|P'Áµþ½…í¯ Øé³ƒ·kóQPí‘›¿[1°Ý~Ž’ô·»÷çcغ{“õ_'Hؼ:’ËBçÈ]aóè ÷Ä8?Ru>ù¢a½×]AÇF޳ÏG©ø÷¶µøÜåfÓ=û¡]bŠ|} +ôímÞí•`’§IÎ3Ý×ÉlNñ/+T‰öÏÌbþÙa9«¯  ×Õœ°!½9Ÿ;+ýõ:Lç õBßý(R,޶˜å&‘°yr$ŸˆžilÎ#ù{ó8PÇÇþÖ ×½û{š›b•ÇN®‚ ÁÈ^èê`³©a,þ–ØyšRÂö9O„ŸH÷ øÆ$Ç êLY½vg§”c}xhd>ºRt5±j· ˜æBõBUß­löË"–;$w‘äU‘¼,Âë#9J&yþPgϰ‰mœ×  ]<žuÏG‘fânþ7â*ŸÍnoèƒ ì$§òá^f~¯òý?Dòøè9]“wµ>Æ=Í5É÷•BSiWÅ…4È7iJÒ ¢6ÚŒ_¯yÑoÌÔiÿ¢3/ÓCö$ïSaÓÜûl'™ $G–ô½Á?P'´å4NNû &ßê>ÚTinÕ÷¶àSÃ, ë÷ƒ“3ìDµ±+aá“9æIAlž$ékÂA¨u{µ{zÄS–?dÂñ†:Ó}Ú4™²®M.i–zUsÏ>3$wÝñ´Õ®djnuÃM©o.›æËiÙ\L‰0ÎãÒÁñÛ©®ôp"…78™ñhç}äûagº°Òv|¦–ûÀŒ…Ãд »kç_õGºí^~)ˆå\îͿճ<0ã\)³YÅüM©T°ßUTMrãÒôûh¨¶ë‰£íwà«á™—Š*Gþ×꼫;.oÜ6D“}_Âr"È!8šçñžwå\Ò–ªú¾è«¨qÓsytž$ê8€bWÑäîâºÝG}eŸ¬#܉ÓglïZcì0ärÇoûÒs±¸³vYöE { ó—äùÒ9øïymÒ·^>°|4Ãûó¤¯7PççäÛŽ,¹Š&±Üü©ö}ô}¤ôm—ù»ð2ñ’ÂYX€¾Öì9åc>᫲¹MÄ/$Wä†;©\X^Á/P‡J½ltî*z¤×ãÂó{È,aD\ ñnܼҎý½r!|/É?‹ßŸØþòÈ®@6×›åŽ2¹Œt½§l~É3øê>šwíÃUôÑöiDXê=ôcÈÏ7Í:‡âÓÝ©[TÕåìÆôù±xØòa¯o¾“°¹ÞÄwä:@ò+IÎ+ÉŸ3øê4Ú¸å\Có/tï0*ê:Ôýɨ=顸„F=ð\ ´g¹P‹ÝN[Ê×±Ÿ‡äˆ’œ4’Wkì{5¿Þ±™67]C±ÆgÑ=tîýÏÂ]a¸¥!ˆ»Ò—~VxNŠÅU/v]g¦“°}LŽCçmçñhžð[67Þøsè N«[µŽEK®¡Ö®*á¾÷PnÓÙï‰ÂpIð›ssw;¢~†`¹XÌ¿?ëNゲØm;,y~<4h–8 Ó×W|q«û´Êű8ÔóTʆ¢@6w™pÎç.óV+I¯&&÷e"8þ«öÔj6š/x«k‹6µL?ý­fnò¡Àé¯ ÝñÍ™ªž*ˆÅ>;4­{ZŸä÷“|DÒÇ„‹bð ÿû·Í5«ye£yÚÖ‹ùyíÝ6C¿5ω™²xã~>¾Õ{ÿ¶cñ§ÜÐJ¾ÎeëIr^Y>:Ã% ÷ OKun¶§’u³ÑÀqãŸtÌ»‹Þ4»7ghÍH<©ëé"E>ν]èßS—ñ…ûo$¬Éõ~¾ØÁòø—ʈæKä%—t+yƒÑ _ܵ í¨cÀdEe#Ã9~•ö¾Þ12Ë´:³Þ5=ðÔ·é6¯ß>XYþ#¹N:áë¼ðôt{¶µÉ}Ÿêè×ïÿõ×…l´ó{ȤÄuwÑi~² ׯÓ™8<)4`Vû®esŒ‡ø…pSéïÓÑ$×Ûln1\[uJ^6j̹^e£ð.?ç×üíœL¯mðÞY+§Þl;½ñc ÛÇäüë$¹ÞÓë1Ó}ÔI8d] (ÍF{ţퟶ¾‹fä(K\G`²ß²ÁSôTÛ"o,º{gȯ²õ>Éá¤×—ÏxòÆiÕ¾·fóL >ã§–×ö9È¥Öö›w?ÜA÷k¿òºØ&·éòÌ?;) ,1{Ö1 nq [.a?]ߎ˦¹ë­Mæ¾êôÛRÝ-lDâeel¤¾ƒ\íoÏGè¯nôtD¥on*ïtË©U»®,?_W‹ä&ÏK)Ô±6ßsШ[¢&ë¶ÝAáվ޼º,KñVÈÚ"Ùà _\râ°Õ>6–O@öMÈú¨ÞÛ·-ÌÓ ¯™ób¼_"‡:^ªØ‰Ëæ j[lòÔ¦Gª« !Óüpt>uÇÏæ<“ë$áÿ‘}:·ÚÑœ6>óyÜhß@ }>™šƒV¶häöŠ{ÍÙ¹ñò½‚|¨#÷ýqGGôsª½G•­ñxx¦§özlëO2…Þy¬zÇÎeú>ÙÍŸâ=Øq^gÚ7P§qWjEŸƒ¦9ù3Ô첿à’S%‡Uo²Õëu}d1<+þ°4°h‹09ÿä:C8„wH×÷@dŸÎà›yÅ|ÿÄ™‡­49¨ú‚§¯ö^¿PàƒÓ`Þ0¹ºxX-™Ø¼O<îúàx¯ï©,¿‘ø†ÌgÂ;#÷/ÆóŸuÝ9j·3=寶’¹¸Ö$QýUKš~¾Ó”ƒµÓ~õ¨mÇ…59°ÿXÙü'ë?r_I÷á3v`|?·:\±}¥ƒ×rД»Ÿ.¼–iwȈÀô\l‰ÍZóÍ,G-Òï(ÛÇ óšðCo…p£Œùz"¨“?µ¸—Í­ä>`âÞ©üÛèx›K>}wG`õ~íKWñ*þöm:"]—Í5²,]ÿãK½&¯x+Î<ÕÌ0c®7mLö¤PgáðÊYÏósÐÑg W¨s)ZýõiÙBð!ÚÇt=ø,®0ŽÝ—!þ!|V‹:î®ç°­•™uUG“ý,9Ôñþñ©FÍ79èÂñšUïæßB Ær6-_éï¡):ødœì}66ëv£±äo¹ßd¬ŸèyÓÉ„·£†:š0AÊ# -Šß>afãc·P—Á#¤ÝÒ"pô±¢1cGwAgnÞ ÙÅãë/‡<^ÔZÂòÉH—çêÒ÷mMøæ:¨sÂkû›ç µH°Ù%bjÐ-Ô3®Q‹w="ñì“ç sñBK6$½ã1Ü,Dm!aûšðCè>xÎ#|cãûK³ùÅüJÍ«ol®Eo/¯º$t m½eØðüHüëYúe@o”JaI¡ŸëG/ó¬Y½ìzCúšìc~&¼H’oð Ô9sÓìeŽZ¤h×ÃÕºé-T²ÿeâXošsvº=ñB¿8;ì¯Ü‰ÃöœI^ëËîËÉ>éc²e̵çÃñ· 7 ©ÄÕ"ÐÀÛ•ŸÝD%µÍ}ù…›¨öòWuÌj[Úµ8¼½î’.q¼²¼w²®%ë²L®ŸÀñáb7®§…H{Å ;qýª´ÁØ=˜>í‘õæ/ªÄc~Öiü@–?Lúоn~dÖydaì{)Ô æ] ©É×"zã&zÐiÎñÊ=øÔÄK£ÛL¬‰^nMïrL·{)ý:6îùä¼/\iýžGÏíÖ&|59ÔéPÅìÊ>ø<·n?•ì}5ÞíšzoÑ<"wÔq ìðÝAÉyÏ.Çãþîç–q1Èù'Ï3H‘u€1/N uN-Ü“ÛK‹¸r›7ãmo¢™Çž{p‰íÐSªs±ó%÷÷9>ã\Ùy'×y²OBæ2ù<Æß›ê¤&5í=»ì8Û™óúÊ*Ùë§.®OïΚթþËpHÀd¾aóƒÉ¼LcyÄÆÜ33¿bþüv;IxZ4€JSWß@KuÉ3G‰Â}Õw/o]Ö ý8Ø"ÿC:ô|Õ¢kŸíçŸýzM>Õ«¶þM$Þ¥w>\+åsrúS :³Y¹Ó&“}$ò}ÍÁ~ÂòG÷yäPGæØÎæc¿Œ¸Eµ4×ѼͥÇÖ¾ŽÄªñWGØM)áQOOÒG&`ç—]Ü\+a÷wMóå³xÉëtI›£Ú°ß§ÁpüÛÔØ¶Õ¢ùõl_6 ¿Žøß^Ý?ô#7wPÝа!âñKžf$à„‹CFX)a÷ˆÿÈszèÄî_|Ç/QÙÀ'×¢ÁmšÌ^á{á3Gá敇ÅO³G³Wöt™€ó?÷|¡]vFö ˆö›žGÏ3'Ÿ›-ÿ¹œ\ ×Ãäú<÷ëHïòlsVÇ(¬š7y®Ò¥,\S«0/áM²çœWr}'ûU„Ûg<·8Pçsëwg¼ÊA^h¸r\•ëhæ8§#}¢0Íõ°E‘'xOÃ]{‹¯mXÆ!×ozi†è}˜šÌ:¦'"|Rƒ_ u×ÒÖ_va `å¢E“J眅™¹šÜ*xlŸb¦h]CÊž[’ë½Þ~Äk[ÒãÞÎo<Ãå¡“'³ßCóPDPçʽ‚¼¿.æ áWg;,Ó"Ã6,ŠÂÚ¢1“Záû‡¢?*ðÃØ—Ÿþì>á ’û~ú<½`öE»±¼ƒ_ Nd uñ„ 9¨Þ¨©ÕgOÕ¢ÜvæSƒ+Ãç1€";áã°Š¸à¯Àôsévÿ•Ì}²n%Ï+ÈzÂdýuBîlìrål ~nãPäªEÙp×b'ÄAÅŽn–ñ=ðxÞŽ­Sà·¢5Zϲ}KÂ+!ë"Ò×ôç3åO«¡Î—•q=VªrÐË-¢'_sзAñ^g[FâËG}|ÜÜï^5Ïuæf>*ìl¹è| &÷§äïKö- ’\¿Lž[BXшa­å9ÈÙëEÀçÝ9¨û y»Í°>öè8ãLw>îYgõ—¶Ó˜ç?lÉ‘2¹“û>ÒwäúeðÍÂbþ¨›Ÿzß]›ƒÎêjý\î–ƒ*»øþêÁìëuÂupÔ¼<{¾×µdô¶o˜<_%ýLöG>Ìœ´ñÂçϼʻ߽íØÞÅ” uf}æ·åNËA“µÊj~3õm¹tOáxMBKáŒq6xñäC6§¾%à[Šöùo Àt¹°ß½Oý‰™—x»sO]|°ÃÅä>™uÖm qÞÞ;9kÿ]·,]~”©|f÷ãæSê·/´GU÷ZyÕWàS~cÖf™b²ïMú€|_ä¹%íÏv&sMu"VËŽl—ƒ–Œº±q¾S62`ÔÝ#ð‡>n®‰®¨ÿÌ.ÛRE f?¾lÞ”ž@|C?‡mÏ>§7øê9ƒlj7†óÞØÎA™…Bë–ôË–D`÷9§}-l‹¶„VîyKiîOÙó1òOò¼Ÿ¬óÉó$ãç#r¨Cïƒf£Ð±o»Þ[…øcžg›ŒÀùΗÞonÁA3= ¼Pw{¡¯£„½ž‘õ훇¼‡š‹Eè-Ëï4æá¨¡Ž$lSéàÌlNݾÔËB‚Ê“ÍëÇDàÀááé¿’‡®¹õúU[%~];™Ѧl½Oú™æÜ¾çeê¾ëfŽÈþ²ñ>¶ê”~y›2;,Ýñ eT\&j8ÙêÇŠ< ÏhèøÜ?x½dæF3%ö0OsAø÷û0zŸÌ‚Ùϰ`y¨ÆŸÇlQ1_¹kzÈÔ ÙÌóžLÔÅYz½%|o¯›«l”<œÝªÕ Þõ¨ê²åeû¥ä>«Ë6ñÑMgÍyO‚ KÓöÙ·¢/>œ¼u[#%Vz)»#ùÛs²®¡×QE¼Tù ƒéN&œJ)Ôñ{9oß YªýdžU‚õ5ÔiÅ»DÑúPlïÒø´Ý¾ÞøgÛ¶><•¸àÒ¹wo4eþ!÷ËÄ?d¿©"ž”êèrO<ïÐ9 ¿E¯¢Íùí·Éòwc×So£jå"|±E2Tâ¸VÇëw‘`rý$ó‡<—aÖ쾩ñUCÿms‘Ï3QPÕÚ?Œ¿Š®_8àá»/m¨è÷hI'|pL¯°G¬¼ZZÒ8“ûá&6uÖL«Ô’å¡Òß[)Ïçâ.»ñ÷Ï..ÝÞüö3kS%¶ßU² S¿L8¾dÎná.Ë}›»¿5Cäº`Üof‹‹ù5Î;-ÑMÍD£VsÏ,=›>Í[ÿhyÓÝí&M´ÄMR÷È}ê(ñµv ëÒz–q¥H?ÑÏn°ïM‘÷ÀŒ÷18P'»`W»LÔjyz“ö 3wæómiŽ»qå ]—ÙÙ4GÓ²;~vhªÄò-㻾ÀrÅÈ~ÙŸ#ï›Ñïq˜Îk>ÔùÞ_CýQÃ&¶Í@’v ^1t7&ϧ<^Ú*±ùôá }û@vŸ‰¬ÉçIÞérèøsžã¹ÀÅÔ?PgÜé;6Õç^Cž5«ÛNx¨Aæ ¸ÆÊáüî“:¢*E%?¢l”øÀ‡&ç{ `÷Èû,„‡Mž7‘ý9Â5øêLp£ž_C¹}{äemÖ 5û§V Å3“ÎÔ³(vGô>°­úÚmB&ß ½/ÃAd}CöOÉ}9ÿ@¿ÇeýŠ»Šf5z»iÏÛ¢Š³o(5OX=ó‹;Ê0ö]íµJ¬È0ä¯l¡–€:¢¥nî.—™oïØ÷@¶äP¡î&ëP5ÔyŸò-|ºÛñ°üþXzyöÙPüÉ‚'Hw@fþ•-f(Ùý`òyÒ¯Ý<½‹ËîŽ;=ŸÜM>ê¼XýòRÝ«(¢~ÿ6^AnS§o‹{ÊÜ·µF,‡ïøDÉ>Ÿ§ßû ‘õ™;r‡åK·4øÄS'úæLŸÕÁä==3ÿb¾°ÛIM@ròù4íçõ®WЩ-‘5™‡á»O|n] ]*ùYõõm%[Làÿþ|›ÜÇ‘uÙÏ1Y¿A9×ÍO&ÌÈ@»|'Ö9ÿö2‚{ ]ÿša˜p=ý3ÜûWâÜ6)ýž²÷qdN“õyÿÃøþÇP˼íúFÈ_áäP%î2j®}â™f†+'L¿;e|#öü×¢^Ÿñ*û¾Èu‡ìÓ}Sz8Ò~ã^¹¤A£Â†›O¹Œ a1ÏéßÓŽÞO­yn¨…_+­h¸§žŸ8%ßÍ“­˜aåÊú\ÏÈûä9…ÉþÔY߸É* 4è±a9yÝ}Ô«pˆ2 »‡¦Lù«—'Êñ¥€†J<¬ö·ž‰ƒýñÖÄW wEüÿ¼Y»"û>Nk]Í«3û¥mMžÓÊ¡ÎĺŽéWZkPÓú%¯Út´êô¦æ'‡ãE-j>öÑx#êí¬ú”øÙ„OÒ Íbvn’9Lïg^ãÑïO—²ë5“÷f ÎÉußdß»‚¾ Sæ÷ÚœŽ²Ý~\Ú7pw¢ÁÈqóŒéßdJÌOê,™äÏ®£È¼!ïåÑëÝbözcð ¿FÃÄÉë¯ Ñƒš±½ÒÑX—>ïwó#qµ‘'W ‚j?ýÔë©O’Òè|pÀßÖƒä¾<×&÷Æë³€b~F@òþ&®W_ëz&UIGü¸Öƒû»Ea‡{½íŒ„"-î5VK…?;®+œö8 ìù)s?H¯ÇÚ˜¼Çãrœ[¤¶yxÙÇ_®çr²hÞ-dó(ì±iµ¾v›¾È0ž¬TØîMË3ŽÊîÓÉó%Ò·„MÞ+2ÞwàC]}銻/#ñq=_­MC?7â4ݱoR…_:²'ª|zu½oUT¬?ˆÿÏ•ìû<Ú3g"÷»^2~ßOun[\ÈT ½Œ..¼8÷ˆWZª8Ü¥õÓ=ÌsÓ¶Èj×}j§ߣìѼìy6Ù¿$×z]hŽÈ~ñç‘BÔë‰V—QõoõëýLE;¨Ç%{0ó>|òœâë#†éßןòŠþÊÎ-ƒ?àøCåC ûßHGÊl7ÎÒ‹©è\Ú+õ®ü=8#o-WsÔŠ­ý¦² wv¶x0ñ†„Ý ûo„ONú–¼/Oûˆþ¾ÔP§ÃóÆßvlIGu†Pw©è×'uðÍØ=8YZù—™• ÑÕfÅý*lþîÜò”yeëX²ïDϯ ö=O2è}ú9™ê|ÌLË䥣sÔk}cS‘ç ™ƒí¢=˜éw¼øÌËÃÎæ*\3ô…ùÄöý8²ž%ÏIˆOÈûž&> ,æÓï{¥¡Y67•x¤¢ãC7?®Ùþ¾65°Z³Æ˜¢].ˆQâÓ®Ô†úßßÇ$÷„»Mž“’ßU|uæu¢ˆ˜i¨Û»Û[Û¥¢~I+šÚºîÁä9È_; TbÝÌOæüÛû ²7nÑ?ÓÞ³û'„cO¿ôþ꼌‹3?e›†¢Ì#ööy{ ÝÝã¤Èñ܃éç…¤­ÛrM?X÷ͽYg…Oû<›¼/MÞ!ë êmÇmû0Ï9é÷@DPgûÒsç¦&¦¢ºÓ§õ.!îÁ­ó×íÁÁ‡nD¤ßî€.¼¨ôAæ¬Ä;eÞ÷K”ñãÉ:œ¾¯|ÏìõCë.7ðܳ¾#í8¾á5ìA©ÈÚ/çÃÑ-—PûCâ‡_ïÁ͓ͫ3¶;Ò¾ï‘Ô}¶×0¼ð¹˜}^Bæ>yOÖu±ô|¿ízf_«Ëa6øê+íÝÛ?¹„¶VÕ7òÀ÷5bO;}/ÆþuáÒÇG¹ûÓžV)ñ™w­Û4½³˜é¿²çå¤ÏÈó2²Þ3Þ?QC°.v¡U—_Bé»®Ý]ãÊøsiDñ^¼!Ïѯ¯“'Ú+u¼´î¨ß_ûÌcËÉÅì:™¬'h¿±ÏcŒ×:8þ¨çìp í,ˆÞyøN ê`xAožb9 ©0·']õvÕÖÑJ<íÚ{pÜ"L®óä½ ²þ"¿—!óÅx^šIŠùã3sjû䥠€‡—gô‹MAs§‰×í{¸Xt6E°¾3ò<í2)ì¬ÃWQ`ùE˜Ü—û²î"Ïý þ€ãŽ®òk\ÆÖä0nÂàê!)È}×Ë3j×ýøŠâgã·¹Îèìê‡ *}*:kÁ¼Eì>3ýþö~’>þWù=ùÝ–ÁP§ZëèIãP Zeþ¶ðæÔäÚ/oÎç¡ûñºsc²Öø5B¥³÷í©Â^Ú뱡‹³ÏùÉy%Ï}Ⱦ½~hIûŽ?8,=íÙ5šä™(öHA%Qh¼Óý˜Þ'ø•\Whíµ6*¼.®2çÍ vn‘ýò|„üýM¸äpü^•î§„«Ñ÷ßžî´NA‹]7®óØ]{‡¿*îd‡[tº÷ÿ Ä§º÷kÖ,}?™Ì-2‰?VÅ5i‚ƒ)—ê,uMo³Q£‹…+Þ¾U3ûûÉ> n7‘zÓH‰Ï/{™ºä`û9LùðY¼Ûß½ò.d¯ó&ï_B0ó?« 1º±¹Ïͼ 5óv¥ïfÎ~7csºC°³ß¬Ä®_UcüÙë9/d_™ì““õžñœ×AKß«³“Ñé Ô…^ê¯i+ÀyyrªÒÖ¯;¢’ª“ÂY«ðí ß ê•­ëÈ&ïùÑóö}…ûŠfAÅ|•}å €‹H8æ~Ãm›ÔÈ˰p…ó¿§ZÊÇj½QøÑE}œ¡[7l¡³Š]ÌþþŽÜçë;½n©ŒÈý=ùÜ¿@=ƒß~ºërª#/(™¯F«~%L»ÒWŽ­¨×ìáÅ/®úý¥ÂôÜYŒÉ¾>ñc𺕥5ì¾òÚ½¬®Ê>ÿ1¹¿‡:>†—Σëgì?\NŒÜù®[Š‡äŠµÊ‚ Ítª«ÂÍz4ˆlº“÷TØu±áþü óœô;û<Ëøù¢êÁª o¶¶?yÖ?D´¾k:7uPÇ?3èrò´3hèçЅɹ5Ýf¹¬Ó¦hÜ K_¿¼ê\å§æ×0ø²Üu²¯Hž›Xž·2¿J>ä6¶ßÈŽ&ûŠfKŠùsÝ]j9ìjù …bÔÞ&´ÐáP4¾”¤ÞXú¡Ê?_5¸øµO {£Å¸¯Á>vD~A¿/[ ‘}ã¹Ã:qa°Ò:…–Õ ³ €ÑÚ3SîíPEcuÜFë{ ßà«ncò•8¸ê­¨¡üÙçä}/rC_7?ñèï—k²_·:bk=™guй_Àè»g›.g£ñ>[ïªóZ#‹ ¯ßT:ë$;U\Æðözcú;™,ö½âãß_‰ ÎÆf[zýh½›Bý’+ù§]ž|;=wY­è¸8º% ö)o´ZÉÎ72·H?“ßå’u¥Éû˜püÚ¶ÔØˆþÝh2z[wÀà|]4Þù­ÿ奮èÔpj Äôy-ó ñ)yŸÌQãßGËáøžºžŸx­¹urÓÌd”`_³}­ƒøá†Ÿþ ì=Ð Ãt•øóø#Ž)MËú‹Ì3rßB޼»Ö/Ý_Í<¿„ãϰîRy¼,…Uòu38µ‰æóËû Þ?§áÁR//ä30~s_žßéž_§ö¦ìþ.½ßAþ©gß'&ëãó­ƒ:..IÍõjÊî½ôNçdJý¼7â îÜ Šû ®3gŸÃþªÍI™t&û9ä:@îïɾ+Ù§4øci1ÿÉ•ðó¡2%ê\{ã³J.ÉèEãZøé·ƒÌ{%Ðõ…µÒ›)q;˧ÁvY Øûò=‘÷|Éóò~©ñz•uèuaºáw5YËd4ÇãèÏg³a¯øýêcƒÐíko;E¼SàI|×ü³Ü…{¿‹ì³Ñsì1;¯—|êólàd§VG{Èç\µNF?«\XPòð¾zlïäY}½ ÀlŸ»½h!ûyÈú¼MÖäþKnFÿù“ô'é?•dÆ|F­5nÄ8Ô2™GTþ.É)Ï74æØ¨˜|òßqlrŽ É'÷« Ÿ\Ì0^)vƒœipÃ6ä–Ë:Ò0܆p¦ñÅ -È /ǯæñkü~ûoøÕ2P©Q~‰FÆd˜g½Q9&ÆüjÂ5$üjÂ5,ϯ)ŸKþ»5ÅäCÓÇ€¬ ñ% È  *Ç`1¤ Áû7 V9ÈŒ"iAî`9cHâ‚yÂ+ak+ak•g"”ÏêÕ¼Ál*-NjĨ.Ï$Ü'†cÌ4æÆüŽQm̬ˆCøƒ„Q-©AN]iN5Åü'nLEùåîåøƒj†#CLf´Ù¿oþ»ç_E³ïÿeîýWÖpÿ¿ÍºÿÎ9÷O3Ž:Ç1 +h2¿rœ,+†#¨yCã©Ê1²Œ !ÿÀXP1Œ*#¸ä Mªbr‚ý˜¬`>•Ìð% ÷EÎp±ü@¹ >à ¤2ÈsA|hè4µ¤+ÇS•2 o†Á ©€¥ª¹Cã‡3Í/i›•压ÀšæeUcᨚUÀ_(Ÿ+²ÓH@:7Ì0ÈL$`¦¤ß°¤mÁ\Òr,i+0›(ÄÓÅ€,Áx~ \ ð¤ GZ²d8ÒL)éAB0§ú_p¤+↴Ì.æ˜.ê?ë¸?ë8³ÿü:Ήù;Qßu%š—J8WEæ4 0 ÄÆ ©€qeÌ^Ѐœþ½ gØ j9T@C«@¶ÐÔR†(€æNÙBƒK¾•w9†4Åô†¦Wl¡ñ¥ " TŽ‘*4b.„0ÌÁo©1 +0Š(ÄÃÄ€,Á4bäæ‘77e¤¶a¤¶Uyæ‚7L²“IAE ˜- ÄbÄ‘.Ïÿ#ì.Þ1æÿ³g~Ç‘6æÿ1ùêÆüÂÿ#i1HcÄ‘¦ ýOšŠ²ÖùåøGšK­áPÙó°?3ðÏ üWœ‡ÿŽùG;ÈPbÄÍRlÆ_ÅD…¦L*ÇÑ2æ@È@ú`A$1,ˆ$€&NÙB#K@:74´ŠáûIî š;d .é@ÞÐè1 ÛOÒ¼¡éU [h|)¨¨ÿ”bnéæV’s«<ÿÔ” âƒIä K0Š(ÄÄ3¦ƒ´ÍËø§ÆÜ-Â?µ¬€)aÆ’€t o0˜ d &“‚Š@0[ˆ† éAB0žú7|h1¤ÚL)é@Þp‚U +0¨¤ySÌ­ øÐ1 +†­¹ƒK©{Y0±æ_°¡+bú³¡¹`öp† M±f4ÖfŸÙ~ýçÎÔÐSß%Ã9%ü-=HM©9AcÊ*`o…3*iA\hX¨$‚ÆÕ€¸Ð¼á {KM¬9A#Ë@z: ĦéABŠ)â@ƒKî– º$€¦Oq ñC@z  .Ç6¥˜[—an•2Ì­ŠØ¦*-EÒ¼Á0*˜Æ” âƒybš›²M s‹°M sK²cIAE , Ä“…€ô !˜M rÃÉŒøÏåÙƒ„«ãÎpuŒÙƒÆlßñŸÙƒ_GXޝCøƒ„ÿìÒñŸÿ‰³£yƒÉU [0ºTİvŒ9„ÿÙšˆîCêÏŸø¯gàÿ–ù÷¿eöQß{ˆÍ'5b~%8 wPBCªË1ÀDМ4T B£ªANЬ2P)HÄpÀœ qe RX â@KAE 4sà ¼¡±U [hn)¨$€&W1¼A)¨$€†Oq éC@úr¼U¨”aˆ©båy«ä ‰YI$ w0Šd fñå6/ã­³ÄøF,1 Hò3©@¶`()¨$c%8`®$“©AN`4¨$Ãi~ÃsvÊÊñœ¥`Æ" ™²SJAE ˜3©–³ d˰œsA|0m8c\1Hû/XÎ1YÎî`r9ct1HûgÝ÷gÝgöŸŸ}Þfe|{!Ã]u‚f”JA"hJ ˆ Î4§¤¹C“ÊA–Ш~ \;4l8Ó´bäÍ+gX Ò‚¸ÐÈá RZ r‚¦–JA"Љr²¢¹öz°^_ZF1¦AÂ`Ú³VÅ -È ΘBôÖ*aÚKAEFL{[0¤yƒyTÍMY«0R’kµ$S%8`¬$ƒ©AN`2¨$³i@\0\¸sÚ˜¥h ô傸`D¹OQ rS†3ÆüsÚ˜«X iÕ '0®Œá* ¸ÓP.æøŠV`h Hòc«@¶`n)¨$“'8`ô$ëŒ8‹¹ >˜?ÑýHý¡fàŸù÷gþQóïß9û*šyÔw¦¦ŽM§ aΩANЀ2P)H¨q¡Ù†ƒ´ whL9Óœ"Ä…& gU Ò€¸Ð°áLÓŠ@4oHB«ANÐÈ2$€†Nq ©C@zš; ÄéABht5È š]*5âÄr¡ñÙæ4 '0¬^¬T€1T [0‡¤ñÁ$1 +0Фk^ÆŽµÓHAE ;V²IAE ) Ä3…€ô !˜J rcÉ@¥ Lâ‚É£‰AÚßp¥¹`¾ðr\i1¤§æR â€)C@zÌ©®€+â€Yu o0¬d ¦õåþ ¦´-˜Y * ÀÔI SšÆŽaXÒ~ Ü?ë¼?sÎì?¿Î2ÿÕ„"Ä…f gR Ò‚Ü¡1å KhN?P.ˆM²‚F•€t >4¬d MëÊñ¡yc@–ÐÀ~ \;4²œif1HâBS‡3-iA\hp¨”úÍY½2v8Åĉ é5 .4~8Óübä&ƒ,Á~ \ !Y‚)Ä ˆ æg "©AN#¤ Á0jL#`ž$‡ÿH‚‘Ô ˜)¤ ÁTjK*‰À`LÎM Ò‚ÜÁpr#fv.ˆæ‹Y% ÈŒ²3úrA|0¥déjÊÎç–ñ³5 .˜5œ1¬¤qÁ¸á R¨sC[ Ò1mÈ -`ì$ÌÒƒ„`r5È Œ.•‚„`ø$-˜^ Ò¼Áü*D÷+Ó~ÿ£3špÊþc³oVñÜû¿™yÿSóÎxÖU4çþ«k¹÷l#súŒêüASÉ@¥ÔlƒæÒ€¸Ð`áL“‰AZ;4›d çÊñ©ßT€,¡ùÄ -ÈšP²„FôiAîÐrêþš’ M)•‚DМ4T B£ªANЬ2P)HM«9AãÊ@¥ 4°Ä…&gY Ò‚Ü¡¡å Khj1H âBs—R÷­Ðàš<¤§îY¡Ù90»B@E oh|Èf—T€’@0CH†HqÀ! =HæPƒœÀ 2P)HFÑ€¸`–pÆ0b䯑ƒ,Á<~ \L²#ùÜÁLr%J rSÉ@¥Ôüs9¹d RL¦qÁh2P)H†SƒœÀtEÔ3 0žOÒ¼Á€*-˜P * ÀŒI 2¤ Á˜jÌ)é@Þ`R*BB'(þÄ3ÿY´´˜ï²|¸ð˜<–ÍG_yœƒÐ.™ÍãñT 9\sÔaLsÑðÌRÃÚÏtóa9éV;ýì·é˜pXwë×L\±›ä2Cù7÷ÿð®w E;wÿ!oŸŒx«®Gñ¯Æm+=_ú}WD¥¦Û¼RàƒÛ¾Hù¾ 1Éq%¹#$OvÇÛ„ƒ©+äÊ¡ÎÂ&Ñ5V¬;ŠÞð®ôÛÔ1,ts:‚Œ_þnÔÑÞèб}™è«ËêŸ?}fÜBLò¿ú*ø/óJî[ Ë•¥Ž«†ãÖÞ|!~ƒÅ´lÊÞ6pÜ<Ÿû¥‹Žà*Õê¶—¸x¢·ÛJbwWWbs k!›ÏCr4Hî›»¬0fÇsDòùr:¨3‹ºÎwU~é¾<¦S2zyáîØc‰Gð SaÍGTJcúJì4¥^j© ÙÜ)‹¦ó y¯ºT>Ó_þÍ2Î2[VÌw›4Q¼89~†æ·IF#§*õG°ü¤cæ,?OtË{àÓ×–Jü¤Àw¼»ÕB6÷›ää‘\\’ßGrKI> U†uîRx9Zøð×•ê ’Q¢PßùGñ «Ø¨7Šy|¹Ý³ç |愲‡÷˜ä‰0M6×ꉻ~ÖÙŽÉ»¡ŽÏ‡ã¯Ò´ð¹Pyú2#ªéM8¾Y5ïm»â-ƒZ^ñF¸¶Ûı ¼á͘»ân~ìç 9/t~åW6':»õxšð¤EPgZ=„¡á¶¼U2šÂI6__5[×ívåjÍ0üQ0ý4—Í—!ù)$gŠþ\•Ï ›)Ÿúy£¯Ëg]X¼Óö Ô9·’ fÛ‰êÌn³éø$¿]F¥ó‘1xQâ‰mF¢ºï—py ‹Í!yF$¿žþ|UÉ&çÍà¨SË0”¡„È¢C“Ñ~]µÍ‘#Žá¹™}½N¯¢[=<}xÍê‹ì»Íd¹œ$—ä1ÎÔm*}ž›I®¸ê¬S*ýÕÛˆš=¹ï”>®ç‹#Å]¸9|,j¶Uqã¿Óùd³þƯ£óZtl>Ég6ÎË×A;Ã㔊kP°$”ÓRžŒ2Å(áGi,^ÑÓèôL);Û!F­Ýóª¾áÏÅäÿOò†HÞ7Ë£ãuœ•<€КÍ4ø&¸˜ï½©ãºnžKÑúïW—ª“Ѧý.:‡=ÌÎ:l{:J¥ÀÏvZ§Þ®äÇæ&‘ÏEr­Hn¦¥rÂ=?aDòÇ ¾:Ç »>l6Ýi>ÙVU}Ð`ÅüöcâñÈ—uíœ27 (aºòvî×óÿ–?CøŸ$¦Æ½B}ÿÖØydðÔ¡ó¨ûáð¯A÷×ÀhÛÍËW܉Çû6ì?µý@Ô&­å»÷‹¸^zaml.#áÎÐùbÙfúûì„Lò˜¡Ž!ý^»W_ÿðñ£žõáÄLÐ9¦{MÍOíž¾;ù]=RcQ|.› GrE=ã½C<øÈòñÄcJ¶­-¢?‡Ž¿ª»àhs+±ÐÜ9qöRŒ,Ò_¼(^ž€mw<vÑ¥ŒÜÇÏuV`sùƦgÂç³9S¦œá,–eo^çÀ€¬ö¦¹€P‡7v[fõ¸­`SJêIŒÖ*G\è¢N`xï|D_'¸å]gån?6ÿ•Ì’/Lûô)>~{SŽÔ)~ÿÌ/á³ ·‰!ú £ýîZÍ©¤Àt®jëF%d'àÎo²æ‡‹ý0É÷"õH?ÉS4Œ“íMòàtPÇ·_ìÛïÑ»p£Aéœfjäz¸}ã+Þ œâ§˜¼îr/$|n+»ø0ïïár+òÙ\v~’ü­ò`’+m|]3[^Ì—¾«Õäkëpl^ßGÒG¦¼5q‰\WNLZ4ùP?dQÐ)8¾ÇÙg;6¨õ|Ö߸ò„oDç\}áÑ\g“<%Ô‰ˆšß»$> {[§ÇÅÏU£âz?åj*ñRýŠW‘}s¸›Åì= Xöq¦õ3Ù|i2ßHž1ýß—qÒ×3|¨³°`ÖÞ$»ý˜æ–ªQÈÛ7*q|µÖ?l³ˆ¢Â­&0ܱ™ln*¹~Ñ9W 7όͣ¿·*´o Îù³¥÷÷qàOï´gŽ«Ñä1i5ƒkªðNõ¦©)C‘ákóOÀR« ÙÔ—31™ód~Ñs-…Gçv>eypô÷Fsç¤P§“üáæHe4¾³ˆº«‘â’Ýý¶™ßdûCÇa¨©ëƒVUW&àEûí,BVÍdÏÉ£çÌ%ÉŸ#ÿ;=÷,hÿ@ëm›fÕz¿Œ¡€¢j4œsâDÑ^®êÛp¥|:øb´Gó} øàS~ÝSsÅìz€œgÂQ#ùZ$ïÖ$§ê\ØTX·êÜ~AáJM‹”íû´òÑ'*üåDZÇw:íZ%æX¶pÓžr•û²ë(« œ^òyv\[áƒÛ™ðÚtPgÿ³Â>Žº£¸J³„Yýû¥ À/Oœå$2<ûA¨µõ–SøD~Ûäãá3È—]}üç,§žÏ\SNóŠbþW«*í1ü:¡ҬY)¨§Ðªá†á‰ ¡/R£Ãv]LÀxìqÙÚ—ík’sK÷ÅW¹nOÚqµá"® g”ujÍÌê‡û$eÄtØœ‚Ö4®Þ2~Y"¦×õ^š9b|ž7';·°­æË® Hî%}½Ö³ù¶„ga|åC›Ÿ•ñœðûšÊVAŠ”#*mW°?{v÷윇'S .ì»=ášø²óšä]’¼nš3ðÅ$g^Çôu©s±‚É]NA{ΛÜÉxÜ´ÐðK3ïp•kõ:È|}Ù\kR‡ä’ÜA’o¼îBsïæÉ”øü”¾’¥V—Фý»].HÄ#³¦ØÎÍGYÊgf)v ˜ÎóeפÏL9gwYÞ¨qÞ©ê¬lõê~ÿÞ* ‹ñƒ.¡^ãwuºgq¯|tDÐúE_Ô©]b‡&_âq#ë£|1É#9d½Nþ{c^£Ž?sä@«)*<ªMLÌ×ݗЫ;n•v?Ž7=ÿtZOò½qäÅ_yñxoT—'zOÇ$ÿ™¬kè9¦ã~2ídœs¨ƒ:ã(ê(oìþ°ðLÑ%TM\ÏYwÛ®/v“Ì(¢ÙÿaïÌÚººýsԪѪE´%ˆâ„S‰;‚ˆbPP«QÑ:à€RÇÔ©8£VÅ:¥N5*HT`…)€cP qG,¿urÎ> ”ömï½ïí}®>Ï÷éÓÎ&9ë»÷Úkï¬O$ܘu^?î—‰üþŒö½c÷…ÏØ¾šeúZÈ‹$l^rû5NþNœD–ou¦à$Ä-y÷üÚhbÂËì¶Oÿ8Ž£eÇÇ'Ëeÿ•ã›Õ&C¯¬íµ¹WÞ‡ÇÙ“PÐ5ÀýØ,8¢ÿncIÏö¾ôÆù4ØÚäº|¶ÚÛoE›ÇùñßÍó(ÐÊ6íÍbK bÛy{Œíž¯+rqœÅ?ËŸÊ’OÁô.Þ·“HÖ:¹wfÄix>sYd¶iæ×ÞGB½Î?œ~Ã÷Q¥>¡ûv:årÓ¼Ôä§ÇzéŒZÃOCKwß–þí“É‘3™Æ7Š‚o6yb÷±Ñ¤å°¤âÆÝŽË šô»þ–”Eû[ÓïÕ|}–ã8ùsÖmº÷ëi(4 h{Ü?™Ü=+Økm¾1—n^dIƒ„9-AP]fF›Ì/ižÆö=~àÄÆE» }(8Îñâ._¼Øc}²þ)™È&. ˆˆÏ• '\Ô'3Ó#Ž-Žïƶ‘Oúóß÷é|Lû6š|‚Ï=”ïÕûi‡hH©>!ãXv2i~õQ—Ÿ ¢ \·äþíÒrOè²™ï#!i齇r'ñûÊå¢{ºîS`Ž&ŽS|ópƒGC›ç­.H›¥õ”ÂMQõ¢áÈ÷ÖYê2d¡°¯Wa$œ¸¬zéô¤ßÍÃÔ'l_ïÚ¬?¾/’Øô»)žÔù ´SµÎ›ìB²íz¥tvˆ†í©÷¾çîL®®æ§fDÂ"Ÿ©ÑÚ¯üùy‘~?”'¶>3%&á§š„öI§þ4ùÇ™ü¸Eb·‹g`ê`bå±+…üÐ<çʯhø13Ó[æLjÖXŠ„º=f~¼0ÔŸ_çé:Lûs6Ï™çt{Ë{'“ð¹ÌjÚQ 4µuà›ŸBfõhvgKH4ø/šê·ç†3‘ön·¾|s$t4m'Í«èóiŸwÚ¿ÜÿøÜØÇ3ÒH ”Þ}}±}GIxß*k4L°v®& &»ÎjïŸ ˆ„Ú ‹ê™tßP9fûôƒÓì}­CŽ–ÇY½#ø£oè9?¹ú²mR‰Oð ‚mýπ׺©VãÇxqÕV/*DÂ9æë™,ãûÝS®[zÇñbK¼}E;.#ì:Ô‹wgWg¿f·ÛYëü>¤’Þ½d.=IS˜ÆµRbèÂtFŽ„ùWÏ ¾øýxž{Ië”ìúù†ç4ßY~ÏNâMF¾jÿâ,ËU°XQ$y0éœPu6>b·S—J¦Æ Ž?#Lá+%‹ Ý:GÂǺ]FeÊÆðy;å7Ð|€òØut1瑈pœ]]’mvˆ…&õÇ¿l–FÎìøüèwg ëšcòõ5dî×Í íú\ìä|Òçwûw–³tç(±šnxP§ºÜÿ꾋±0£]ÁŒyþid8îì$Žú¥k>ZéNÀ?äÉT\O7E¾ëni;’ïLýJ¹*t@ûúš÷‹–á8­çôk¸2êís>žy:Œ´ÝµhÅ´`ó×!¤nYýþ“^FÂí=ùu›æ—´îÁúù©û9ì*ò”pœÓ¶Y —yÆCìÖ{g㥓’9MžÞÅÝE÷t&ž+>8o¶<iePÍ£Þ¼_h¼ÑýǫygÁ÷u7ß_+pœ)>«vo°ˆ‰^ýðð¤tòóÔÓæEÇ@º“íŒë=Ĥ± Ä{ ì4¾ž+§ùûžmø:­QN);ÏØpq `ýƒãˆ¿¿YVp«îë ¤“Dw¦‘s \=ñ¦S‹n$öØË›3"#¡þâ›·O¤ZG¥}ÃYÆóy(»³a}ƒÏg9½ZøÕ¹O¿U/ÒIvõn°6;>›^×¥Ë ÂoìÚFBjGaÿÛÆðï…òš(/œÎ_ì:Ôš›Ïê²¾YY$!_•=µC ‡ó†Ì~ܼ& °(ì’›¤ü$ïEfnè«àغ mžS|ù>Å4o¢ÜÚ›ò͹7"çi~ÀŨåæï dpNãþµ4ðT)zµqßò¬]é‘¢Ó*Èøªë¡$_~®ÌŸ¢Ÿ‡ýí+ì%8Nã_¶Íî#Bêz,]$þq‘÷â4çΕlpm:æÆ{·õåë^¬mø¼šrû¨oÌûÓËpœäÖ €:GiôÝEòÙ鎡yS5ðÕ鱃š ÒϾìÕ.îb•wcÏô=Sÿ?µ×"ó¿1ïO/Çqúß?ݯQ"¬ý&7·súE¢-d H˜~Öà·kûHŽÉåmÞ@y}´ÈñXùõ‹­õ#tÿfò ŽÓ©dÔ7?LOõ¦»? µ½Döu<œØãÜf)ç)î`þöÙ¨=Óqþ\·•ßxñ\EZfó¸—üçaç×~äÇ× €‚]´8Î…ænç3RaAÄ’«®«.Í¢£=F= É?Ï|üÍÜ‘d¾s¡Â] êûÛÏìáïotý¡Ü#Z7d÷Ý=¸<€åßqãŠ~ñ]’@ÓºÛ€—È©ÌE±é,Ü.Ïh×?qµ7«úâa¿½ìþó&ÿ}Q.y_z‹UEÕøe¿¤oH‚ݽÛDK»_&åq5~ÍÕ]ÑÍËÎ/܈…ãú/ê»D‚²ß’Žâ׺ÿ—ÿðþmc«Ëñ¹ÏÚû/ Kïùmv~ »BŒâ§‰ä°çˆ½HÃ7[Æn›¥âê'£y®]/é9ÇîúM¶H‹j“Édýa×øÖ|¨ÀqØ<.Ú麿oòâ i“fõÓès ¾lS“ÔêKZ·:ýÄM¦‚­cìví¹éÇs¡*æKyO·”ŸM¾Àç[†7[7Ÿ¤ÀÞ¯VuÆ_%ÊžuŽuZpV¥ì$Û× ö-,"4sT`J;ùñûú¾ézOyJì÷Ïò{Œøü6ö Á,äÕè-WIÏ÷iþ±{Îõž—þb—Á„Éâïâ<ß)·7Å×eh=‹Ö·Ç~1sàù]žb>/Z¬.’l—Í—¨R ÑóÞêV6z2ÿ¥×B­þœ\³>ïzÖp²/ædΖG*˜ÑvþÎd?OÞßô\€ÖKX^Ì#.¿´«ÀSá8¢k³'Ô¹’ÓR›:Õ“&Õ•á½??‡¥ZJ÷$éÁ^¾Åõ‘£¡@ÏÑè~õß®^ÿʉòuM¾Àç³~J8‹òÂÌíz2uE¿æ_?Å/Ž[×¹0’ȾLm´}p$ž¹uγÌéü–¯:îªEÌÿ~ùjÊKÓÁ¾ÇåAa ò¢•<©ìÕyhµôRš}¼È㯕ïoxDBÊæn ;ÛHŒß;pÕòn|½„ã:qùœ€çZVðŽÓûWÉä-ítÐ2‹9«Ë swžî|VËqMF«ƒÁ˜ñGr|L”ëFë&ì:ûÆiìÇÒq1#Ù¼D‹Ïh2¨ôK®Ü:¾3ƒ O±YPm],·ïóä¸Y¸î‰òà çV”_ÃîóËMû*#>ï{C6ZÎÑ k ³3ˆçŽo[†]‰…v%9ÄJ;V4à… æ/‰û‡»pup[~ßMy ”_aŠ÷5E’±Ëƻ頗Œ9y¸FFÞ˜ÝîHý8¸9{ëú¯ÝIÛªÍ?Ä«€åMkwôÛ·£+Ï·Ûùâð‘ÏÁàÄÖsžšž+Âç®õ¬VÀj(”3|½ü®‘é3­r¿±L¹s´‰bŽÕ·¨ ÑÑEÏú¸Ë™³%ô<ò‰?Þºz2{ Áçž èØõí~”×”nè´ëÙ¹Ð!Îw^¤Få^_4Ù•Ô]¿N¼w² æŸ/Ú1œç[Óófºîh¸fû”»\>„Ͻärd‰ BÌåyæ5â÷!»©í‘8°n0ìó±ÜÈÇ1ãÏæ­‚® Gµy>Œ÷#gÊa¿ßú|ý„Ö»MqãL.rù"3F'ÆÍÞ}Tx¼ÝeÝF‹èÞ(ƒ› à*(Íl”Ýù}+­‹±uš×N*çñÓ ëZÏ2¯ÿ+pœÜîÕ33uð¦oÓZe¯“’'ìOÚÄ÷ËÞ´t$Ïîjh ‚ÏúØÌ×·Ư»4¢õR6ý`z¯Z|nfqçÈ–ÏuÐäÖéãs¯“ûŸŸo<'Ò:+ß¼õ&³ÊN[wh­¶ž6‚çNÑý!C6¿ÎÆ7>×:{ͺ9tУŦ‡=×I]}>M‹™ËÅf½÷ûÓòꢂ—I‚Û:O`ë«"¾îMãîãLñýC‘Dþìâ®§õR¡Ï?Û¹vŒ¹àR\×`ïÌ·h†ÙNQÁϯÇöõl2’¯ƒÓüî§ØóÞ7æA>ÿå/Í¥BêÔSË÷ÕÎ$>ÃïZÐL¶ài+o²ep-3ß«€ÙEY½’ò\G?t]¢õVºî›âŸßÅÚÍ­‘K*Ÿåí¥‚¼}çq£&fÍ1GÖ•üó“~ùô!$Û’ÉhUÀÖG†ñû'šÐ|“Ö7XNUO6ÞñùmkHš9ÎJ…™d~ÖN‹Ñ ´ÐUœríKB6î Þ¸C+6Š>tp¿o¢çw¶¤Ó}Ó.^>òüÓ œ"gq\ùݸé©àêÑnV³äLâWZ6f§•†Þt9yñHwÒßÄSAUˆmIÚÞW4.Ù|!“Û–ð÷¬*œã8•ïNŸœ £É×é›K3ÉV lÝTDF_u{Þr‚Š›ÏhhÅïgØsâ޳úŠ|îœûOöfŒJ…Ê|+ƒ} öš}êTk-Ð{ß™&€ßö±´þC9tìzWàôcq‡ÃŸ%Ô®pÏÁ"´HÂrSÁ©0'²l\É8å–³Ÿ?öáªàÐ6ãkÄöŽž*`÷cãxÐ}%û½$ñûßVWÞMoèÀúŸlµ_àÞ©pÖ*gûŽ-YDÝÕ¶öE|>­C:=œ•í®âø–ãx4õ݇Ó{bôþÉøü¼Ýè³6 .ÃqVL&Ÿ _Y:où«,òv®ÏæGöZ¸žRmDôœ®¤Á &CTqs_>ϤuQvÿÌßשxO ë 'âÙ–Ë>%:¸Ø1;7ÀþI:X˜çÚS ­6Ç?kÑÅ‘\¼y$dœ· XîÝh¾ÞMÏ™ézÀîûê°>¥ÜT˜–õq7H|µVݦ9kaþ°KÓjõ#ä Ó‘Šã ÿGÔô~½oÀú±}î˜Çé:mà Ï,|Õ|úð’M7ˆBxäò-/-ÔÒùhÃCÉùgÇ\‡Iñ}“ÛOnt÷Êù¥¼t6ºÅÕ+«“¼’wÓöé\á> Ç‘ZMoºõ’æˆÚÔ×Þ ®²¸Ýš-Ø]x¾¬ÝgIÞ‹v0¨ýoõzÿ†òëÙ:`*o‚Ƶy}Çbm‘D3Ÿéé@¶yí%7HðPÏ«EZ˜ lž‘ãBŒö#DOp]f¿Ñ|¾V‘k›Àû?s¢ó;û9¹:Žs=ßXÛQ\ Èz8½c6ù6ëxÝiµ\¾æB›ú/OïÃç/4ï§ñÅž£°çøÜöM†õ:½C¥-¾h¸jr6QtÛ«Å~-´œÂT–%¤žéªÀ´Ì¶óæëmt}¦û/zŸ…Žkþ=ÉpœM}¯|µ\óÞôv³'›¸ovO€cZø°õí“¶ÝÅäaA—òà_Tܽ/~¿J÷G´ÎÆ®×…N4ï3¿Ï$Çq›@­:ØA šwÊÊ&×G%uÎj¹ü»;qëÔaáþ»¸n˜8©>@ëRì¾£ çîGó“Oðù¿æÃÞyê a“KÔ7…+nKÒ¨÷cŽŽêEzM`‘‘`³åÌ‹ööcAÿ,l’| -ϵ¥óí¥´é5;Õ(å¹°&àóûz{m ë`룾`7À@8÷^ðêªòžZ«¿8áDXÞ¯ LÓ~Û‰°Þ¸¼cÎIÂæÙí¹õ(Ù‰]ÿ~ubÏëÚVˆ'#ŽÓJ­Ù [„3ël]h Cäk»Œ¿£…EºøÉw“&Ÿ3› ¶|=`G-¾þA×#:ï²÷—ïñçãøÂëŠ$u©Õµ¬žØû)R]VÖõ÷Z0¶Hk¸Ú^J¼ï£>¡ç£tþ¥ë¸ù¼"ÂqL¸¹üPGöÄÊ@¦(–¬·M€ÓÛ—ïò&-îuIlz?ºÇžS~™ŒÛ…âÖ|•Þ‹¡¼d:™|‚ÏWZX^Uüœ÷Îu¸Ý$‡\ž»¬«½Gäo×Ù‹G“‹µí¤ÉÀÖÛÆrõ[Bß/}ï”_ÏÖÅÚUØWÊp§G'Fû¥€Íoæô’C.–oóÚ±$žõleÜæKÜkËígåE@«ÚL¦ïô>.½ÿËž³Üåï/š|Ïu3rS`È–è5#ç%ÔPzD&€|×í£cG÷-KÚ†5QÁÆ«“?ökà˯³ô}ÓúÊî’½GmοãëÝæuŽs süÀÜn)0 £Ãä`UÑ{ïš×çfœºâ<ìúIo2Õ¿í?7—oŽën‹fýÜцϛi½›Í?«zŽW!ŸÂqÖ'×쵤~ 8-bN¦sH÷ýqk×H„w›¶H¼áI¾m?Ô¹õL8~ÝwäÒ‡£ùø¥ûÊMdë¯øûwæç7Fçä­þ’7÷“á s½§ÖMÒ ÒéTÓ6‰ð"õ`½^o‡‘Éßä ®¢‚ãn1mç&øñÁç3·°„ó“Á¥f+çí?Ý$û¦Ìã>.ØßˆIØyטÏY±A^oˆ hª×{õÒ†¿?Äú.Û‰žËÓúŽù}ŽSSWV»c2˜0’×n’å‘;ìëLKãžÆ‡ã‰unwc§¶*Ø“,RÏ œÈ¿:¯ÐûŠ4`×Ýî-Èqœ¸ûRÛ½M‚;5\?× —<õ¥×ÂD`ïm;‘ÏM?\PA ³ûl¿¾Óu’Î_ìý›×\}²G…û* ‡¡oKI‚{|"—\¢)ººìPh"Ä4h¸{¦ ™×p_ÝáèÏS7ßÁ£´‰@ókš—Ðû*ôž­›Ñ81ùÇérüÅ®'û“ ­ßUósÉ(§ >³ö&Â¥+Câc6 &Þ›Òï|°UÁðé5ÔÕ&ésè~‡Öƒèýuóz…ÇÁÉöZéê$<ôJµ?œKõÕJ¶?”ÔøqÕê6*°=4kê„ïd|]„ú“Öéý>Zǧ÷=M¾ÙP$¹Ý`š·q2¾ÛICÛdä’vgD Ó'ÂJ¯EÕ<×#…^(TA܆IggÉøý Ý_Ѹ¦÷üè¼_¡îŠãÏ]sg“c¬<ðÅúw¹äüNEÇ3ÏÐ7ýyï»ïA¦vëPø:ÚøG¿]ž5‘¯3Ð÷Dë l}äWþžÉ7øüÉ šÇyUKL6š†´Ï#gç·ïÑ[˜Õ4Ýæ{’òÍ)ˆ€å]¬ºuxÏ4o¥u/úû“Oð¹ig>»§£/r_N%#óÈéIŸ¿œÛ# v5—Ö˜X}$Y;~ðɼkà5ö‘èDþ¼†Î_t¿ÉÖ³ÞV¹nÉqœ×»N;ÝO„·Ã^_½.Ï#ç×Ì¿žà“»ï7X)%Éa{†áº•~ÂzÒÊÕþüþ‡Æ Í»¨ïéïLþÀç¿) ¹$™’CL?äÈ#_v}&œßÝu[¸DäAæX¼_^¤¬=±`2Ð8¢ûº®Ð<•~“/ðù^Qs»:tL„ÎvÞ&·òÈתÚ’òzOndOp'ŸÕÎÎËøq–Ö.˜ÂŸkÒ{tÝ¥çfæù¯ŸïÿSÑøö¯àÖÀΗ o‘Ú»‘ª$x¤4B>„;—QË1àï‰ÑýŽMô¦“î=v ø&òÛ¨Nîo[l,’\¶šv46ÎÅ1Àæ[äýÜV]ì“`iË:5æ×DV–íèýMsÚ ±è9t*Ðû˜Ôw4~Øïç…›‡u®xÇÑôNü|Q¬š2£0`ù-òùÞ)Í6$Á[ç&u¬–&ãîöÈ!#TO÷o´>còó÷3?§˜­…=ì“4-óÉUï»>¸^®`^Ä(r°µGûœG°Ëÿ»Ñýe³øúÍ辊=ÍcïIás«1ifk-ôÿì˜Çi>YUöâ׫S“A:§UHtžÁÖªÇZôÙ–+Ú´‹Óùó]š'Љˆ„.µSÀËeAoyŽ/©y´i_«"àLWÅÏÆòõ6šÐü€îÓØùªâ÷$ÂqÊÎÈŽv1ÆÁf¦ Ùª€|lX^bÛ3öøU›ïqÀ›†œ®ý\gË[Yw¾ÈשïhüÐ÷AÏØ8cãT‚ãì’òñKÏ8p¾ÔÖ'wxñu\9ùˆ_ Äí<zHIÈÎõ¥—"¸sÒß~ßFïÍÒß3ÐßÑóº u,§aöôF‹Ž…ôãQ2Ïýk}ì¦@¸ú‰¸gË¡$2u©*øNñ¿ÒãuÛoùßgÐú ­÷QŸÓû¿lüqç×8Κ£ƒÃ²ÏÃ`Ó' Èøo1ÒS`úÚíiCÖ½ª¢˜•/îpiñ ~]¢¿÷¡÷ˆèºJÏ1Ø{Ml½Lã¼K™‘!8‡·1ƒ+ ?-çHïKú/»°Á™tê²éÂð̸s‰¹Ø>ƒÿ=ǧß­WS±GuÖ/8ÎÚð&n/»Ÿåf­õ²¤–ÈíPZt ô½ßiˆm™„û]HÜÜèô Fõ™üû¡õš¿Ñ{kô~–ù½%#ŽóÀ¶CÑÄÀ³`™±`Ø­fFÂÎ_)Àî÷IÝuÖþÏÆD€©üõh&P?Òº½çî‹/è½ó÷c±¹HR?Ã&¨^ÅOw—w4’÷zO-ËM}–®µgDâݺëºEÀƒÑóä²M³~wÄ~Žh'z/“®÷ìx¬pœÆ™O¾¥žoŠ㈑,^lØT’[³{ÇÞøq(y;Yñ#@Ícìɯ[ô2;ÏÄð¿7 ñn^7•à8ëïøÜø:%tL¿a†3A9ci"¶'ÃÚIãzÑ™³&fl)ׇSÍñv‚*ñ²K9Þ„¦7V‚†T¢„hÊ”±Š^œi•zqÊþ¤§Ýôâ¬Ü߉ö_7rý×Uô_«Ô“î_1'¤\O:ÊϦÜE&.™ÿÎÜøŸ˜ÿsâ§ùðf>rÿ/¨¢'ç_é½nÎÛÿI?N!oÊX‰+Kyc”++À ⸲”7ˆA®7ë¹^È1³ÃÿFb†9Aû3=ìÂ9c¢ôLžÈD†Jû“>vLb†9AYc¥"–µÃ0³K¹~væÜ =×SÎñn¶‚3Y JÏõ"å˜cæ¼Ùªzrþÿ2whïõª˜;•{¯»r½×Í™;•™‹ÚJ½îþŒAaÉõº+DIqР˜®@¡ŸòÃOù¡Å?k>´äþ^#óÞ0((fJc€*P Ò ”%Á`U¢„°!•zz 1xCPF”+±e‰,G¢¤К*´3­劮æ´”A&Á WrÈõö´ä¸ÛµO1åQÐ>ÅAU°·)‡QÌqK™yÒ¬Oq(ª˜ëSÌð((‡L/bû|2L†ÃmW‰I¡äÌæËõi·äxŒæ,nÆ€2ŽGfΣuECªQ–hJ9ª°ŠžŸúJ}?ÿ ÷gÊáú†s†D¥q½ÚC¹ L¿veýÚ.c1ÊM¯ý l ¦g±ÖŒËM¹ŒL¼2ÿ*s)þ“sâ?i>ü»sáÇ<ø_=þÝùï¿kÞ¡”ÌgÄ`ӣı'{eöÓ“]‰bp† Œ(W R5ÊUŽ*¬Äœ•›ñf…¼!o–2ÇÞlÊ`Ö½˜ãm+þFc ³>Æb ~J€BPb4‚‚Ù£ô(;4Dª%EchP"k¶—1á Ü1†¹£äßJ †7ëÀqwÞ"ÃÛV¢h¦ ”ëiƱÇ(s6 fD¹¢ÉÔ(˃»Ãôc¯Š¹S¹»”ëÅnYc1 %BÓ†¢Šÿƒ‚émŠ*Fù¢©µ(¦ëFا|ïS¾gñÏÊ÷츿§y/”J”3e@I0@•(!iʈrÅ`U£,1`å(#ÊW²Äà•£ QR b J„Š*Fùb@k«`Îj͘³…()º†cÎRö˜+½%ÀÀB¥q<†»­G‰Ñ ”Í„2 $h %JˆÆ1ãR(QB4IHÜmÊ^”pìEÆ<¨4”š( UŠòµf¹”?f@ÙqL†½íP‰MÁ°g “‚ëï.â8ŒæümǧH«ÄŸ•¢!5(š2UŒrEsªQ–hД%A£*QB4kÊ€r@Ó†sÆ DéQb4p8gâ@”%F3+P4tJÏõ{C£¤hpu}ßc)J†¦Oû Œ NifnÊcü47~šåÿœ¹‘y—j”2e@I00(gÊ€’`*QB Ô”åZ¯ÇƒW²Ä–£ QR d J„ÁŠ*®Ä¤ ­Ä¤µÄ —sLZÊ)c˜´!(#JŒ¯àx= [É™ ¥G‰Ñ ” „2 $h %ǹBP4‰%D£„ Œ( F‰ i‚P”š'UŠòEiQvh¤Pk–wAYeŒ©fÃä ¹d•¸ ŸVÌq{>#ÃåV£„h¼”%æØ¥fŒZšQŽ*DIÑ””èß`÷0æý#v%FC+8S3ŒF-Çîù#F£e‡†C•þ &†Na¨R” '„4ó«ÚðOsã§¹ÑâŸ57йñŠ™ïƒR²ÄÀ”£Œ(W P5ʃTŽ*DI1X5(l(ª%ÅÀÕ D¼¡¨b”/±e‡†*EÉ0 Óª`Ö¦™1k‹Q¾èZŽYKYfR z Jˆ‚Òs †Ùm@IÐJ”Í‚2¢\Ñj”%CŽ2¢\Ñ j”%šD^³›r]9Ž£Í„Ò£ÐDᜑd(­õo<3#Êã1ìn1, UÊÌ“¶,»Vˆf D¥q\ †çhÎ8®™9¿Ö¥EÙ¡)ÃP¥()šSƒ¡Aå(#ʪFY¢YCPF”M«@ иA(J‚V hâ ”%A3+QB4tÊ€r@c‡£J™y ®AY¢Éå¨B””ã:2†DéQvhü0T)J†@Ê'pn"DéÍ8Þ”ïHï|}šÿÙsãÿ…y‘y”%cʈrÅ T¢„˜!(#ÊT²Ä •£ QR V5ÊVŽ*DI1p5(o(ªå‹A¬EÙa ‡¡JëWdÛ†UbÛŠ0ÀC9¶-å¢1l[9ª%Á Wrϰ¼Õ( e@IÐJ”Í‚2¢\Ñj”‚2¢\Ñ j”%šDŽ*D¹¢YÔ(!&eD‰Ñ8 Î<2TÊM†*¶þ‘&@CÉlXž·ˆÒ¢ìÐ`a¶,ãV‚F çX Ó[ƒ²DÓÉQ…( šOÁrnCшÅÌ܈fÔ¢ìÐrT!JŠÆT£„hΔåŠ&U¢„hÔ@”%FÃ*P4m J£y(8e@IÐÈJ” ËzLCÙý ëÑ€r@£‡sf÷EiQvhú0T)J†æOC9àÎM(=Š¡4)>å‹ÿkæÄÿ+ù¢+÷¼ÿÇÞ{@7u¼[ߦ ` $¢ËÀtˆæ]@ Ut(‚ÁCˆ’ º0M”Ñel%4ʈ. ‹nª%ªL]ß>:gdÙ˜ûææý÷f}¬õ[I¼ÈKÚû9sfFÏörï+DiSÜ@ Z€"ÕPA¬V Áê¨ \+…xõÀ Ô± H!dƒ f pä‘}ëȾõ5„n²o ‚àUÀ ľd©ûí Á Ä0ƒ¸¦° Œ¡n „A,@“èòÈýf¹‘\î7— óÄ‚ ƒ‰Œ@#i€ „JðšÈ`,}Ÿý-‡Á ‚É4Õù \1Ì@*äHæ‹aÀX‘+W l@ Scª€„ :àJÕ$0«¸¦5`786`˜88f61 ­N ƒ±‚¹ÕÀ $0¹x€JÈ—Áð Ha|ƒ`~ pŠ€ˆP"@F@8˘ä´Îýù7ÕÆÿª.r5ñÿ­øwê«sï«m·®ýšöOëÙÿ©–±öOê÷>X¹Ï ‚Ñ7PB8f †x´À ”‘H $ðeˆJ<@qYA(¦^ †Ðl@ ±Áq9·!ëÖu ê…œ[5„hrnu@Á‘†c( J3C˜ZàJÔÄ©¸bµ « ä2mâÕ9l"ˆX@1€¨ j-åÎ> 7{.Û $·( pä׆BìzàåêDoãö8 |W{Pk,@ h(QkÌÜþjM£Î˜@0êLÈrÆ‚ašXà ˜ÇÌe@ é€(a& ÀP2ÊÔ0• Ha,ƒ`. pLf"-dù‡yׇyWпkÞ¥þ>'H°‚PS<@ZA(Dª^ †Xm@ Á€¨!\B¼AÀà2ÙDsÈrˆÚDvÈ2Ü(ˆ\@±ˆûް „¯N ²ºÝ@ #X€fÐPÁV cè¨`+…Iôyäuë¨`+Ãt=ð5>|+…ôÀ Ô‚ H!ƒ  p„a"ˆ#È £  °)Äb£n/ÂQrçƒ!-p%ÄcH<@!I $ðe¡•x â² ¦ n"ËàÖ³ 4D&ã¾÷qé¹snWB`áÆ«nM‚órkñx¸ç*n ~v¼lbx98â41ªn äÎlpg5 TPA¬V ÁÊ!X·ÎÑ:€ ~52€B6`ˆ98‚¼ûý°Páì¯)s æ\߉ø¯ö:%Âw"X`ÏfìÏaáß­\?‰Qïïžo÷÷Y™Ò¢Jcå ')d-\â¯ÆdT ŽRÖßÅX¤þÅSý¾ú.‘>¾†ƒ þ\ƒ[dCæþ’£iŽþ™gÖ¼Nñ3km§¢„ŽÉ…MNÒ³ä´SñGi·BÓZÌw%SÄ\2]µµË¼’Z&»ßß'¹ž¿¯ßán8ësØ_&HŸ%Ÿ7Ýùèþ mtÒ‘ž ›XœdG»ß›þxà(=Õ0áÕÎŽ$Êа¡5<. O¼3»?ë/Çú²¾?¬¯V`l Æ)Ø{}ßK~§ ýÔfÅQ')–øÍ‹•gŽÒWC3ã/5iCäÖþ;FµM ’ÕÝ+'.ùÞŸûÊúz°~ä¬ï6ßG',G¾¨ã<ìËu ú¶ÞåX*:ã$›Ýý¥â£”ÌZÓ\¿²%ñÅ˶N ©FçÿèøXÿëa}2X?,–È÷'” }ªø>‡jŒóiÆðj/˜i‡ R_^s’6IUÚ´|t”Ú¾?îÙͦdâ–òåo4H s{‰ˆÆGøûp°>,¯¬ýÖÃËo\º%ärJsômÔbœ:AGkÜÉ´×ÀÝã$­_ù먻$#ëj·üˈqø¼†q”õù`ýlX~ßÃíÏ]Ê‘Óq֮や“èä"\«3'Y~»Òò´ŽQ>÷VNÚû'%Ð|Ÿï½µáøwúų\–7ÀúÑö)³bœR .c urB1¹´Õzwäý¨ÇÒo{v$GŽ|µ{vÿZ Ö%keS¤¿ßeξ/G„÷íy޼I'®ŸÖêù6U]xHý¦Xy©üÒ^2«c´xÊ÷Å[†)Iìɾú‘õèvÕœu§Eù}Ãú˰¾Ì|žŒ;œõm ì´(K¾ñ‰«ßôy&úã2¦W-yž¹¤À¬¡Ç¨¾p¾vÆö½Éó‰ó§òƒ"ý¹2¬ï‡ÐoýføªÊñûŸÌm”#OP‚q¦î;8ýU¾Ít§&ßÅ>]dÆ]ù¼£úctÉ7ƒKîy¨"í玾ý*>žžúÓ€ð¸ñþüLöù³>û,?ÿyÓœ¾Á8J_0ÆFº `…ÑwZ¹HËWcS§:FÃvß0ÎìGf>:miFãi–…k¤>Îß§ŒÏ=çï Éê``ßN5®ßR–±J·žV];RQ®ƒ‹,n³aiØÓcÔ±ÿË‹ÉËú«îááG[t¬²¶Š6¨¹(£sFr{þâƒ{ÅÓÉ­ž^ÙZoœ¿¿1ëË÷aº¾cðÁ¾u¿úe5ÊÑÿÛˆq¦^Õbáœ_iåþ_·Ììîôa£Öl?^ÆÛ¤oøòlëßã饹ïq”ùŽõ³º*­˜Tu‹'œÏyÊïÏC ì{hÅ8ß¾>X·óšU4#˜ Øs‘eÝ>?|°¿nn1{ksG;¼ÍPå&謇ã~ùãüýA™Ž˜ÿËÅÍåÁ£p–ÿáó ÷¹Ìë<µáOËiÙMvµU¹Èº>׺>cò¼‘LêQóóíñ4ö¡),äÖx¿_˜Ž™Yëo™#¿(.Kþ*¤©JÖy1]rý«:C]dáÃ6û[L¶Ñ†öá3ZnA–ÝӸܵxZ&¬Fœ{D”?›Õ3–OÆîcõK‚ëóhØPÝêt4)ýÐhùõa~í~6q=ÞvoCsR|Ðlù³R ôåE.€$ê×Á÷ÁJ gyˆ¬_Q`ý’cœß¤í» IŸE'†T®r0ÖERFÄuª¹ÌFÓÌÏ+ýu¦5)ò#wgN KµK\Ü8Êß‹é˜ù‘/+œé/°¨ãLÕÜJù¼Ðtš}müÞé.2Þ»¡lÑM6zjÊŽí_ÿÚ†ôæn+Êú“º÷¾×Ý"ý~aúb}¤øã Âú}å諉qí3«ü}@蓆qøþÞmɰ'ãÆ˜ .2ÎÚsÌå")tÓ™×Å)ÝH£E§FW™O×þrºžãA„Öw‘õg9˜9ü²8KÎ¥úô ùž¬çnËk\äñ÷1;jÖJ¡]C/I¢$ ÇqwÊx*^¡R½s„ÿýbÿäûþôû’õŸÏ1/Ã8A)Qî§{ 7ÏœîØè"M³¶gÇÚ¼†¤¦«m7riÎ_#^܈§ÕÆ-ÜXjP„ÞËtÀòŸyÝÝ ÷µ§_òY޼=9Æ9Òªî·GšÍ$EÇ4_~-ÁELÂò§K¡ù>å:Îw&S>/"ºåާ}ÛN輺Ü8ß¿œ¹b—…œ´'þ¾ù9r0ÎÆïöÝÿí"{’Ø\nv>ÿ8…~*–÷,ÖðùfñÔ1¬v\rÁñþ>¨Ì?,Ç‚åL0òýÚø¼'-Æéi>z!ª žú­³Ó9_ûUÏîsSèæ_kf)[µ&—/|Òã·xÚ´ð/Á-ÜãýŸ?ëûÆt˜—`ä>w®d³ÅäUꌧïv‘Õ-{—YžBÏæ—,»Þˆ˜\­{\R<]t2_çŒ]‘þ¾x¬Ï0Ë ôùa1ËZFÆE´:OÛ}eL¡_—ZîÎÐdÖæ¾©ÇÓ=;K|åŸo±e–ŸÃúó¿/ß'݉ë¯;Yùð‰W’¯­½×X]dbf[M7S =Ÿ9¦ùãUõÉî׳ž^:O»µ™Òtü•¨w|ÍîO|}zÎúæè_¾$KÎ÷{û…ør‡¹ÈƒÎ I6§PÇ៾LЬO~¹X~ÓÎ3ñB>a´¿_(Ó)럚# ×Ñh~ÍO‡‰ué½ ÑaÉŸêÉzõG }SÎrwB3r-dÞ¯G÷ÅÓ§M¸ ²hiv_b}¤‹m¨ªQ“‡þ~z|Ýà}&Ç8÷^®¼ùûɵäåŒi·¯â÷ϲþ˪ÇSèÎý ÍñÝíIõ Ë›A¿ÆÓ*¯úÕëð8Ú¯Köû³~³üx÷™/ø¿ÇëRqîÎï?ôÏÁëI“uÛ›yºÈþÞ”¸‘B‡w™:'|R7rµP—bõ´˜/T2'¦ôŒñ÷½Ì™;|ÐßЧw\wɨb_Ç|²‘l¿ñhûk|Î=ÂãWI §ÒŽßt·”èדXn-x³|T¼Ð8ÆÿÂæ‰Lï¬?¨Oï¸îÚ'ö´¶‰øÜŽëJ‹Ç]Ò0•Þ´-lD’²•k|O áùêêòËöíFÝq¥R¾ÿp+rïòöò›CèéY LcWDúçì>ÂçËr‹®ûÞ5®»óËGÓ'¿‘É銙qݳjªu/UÈ¿nD)ö¶ú¥nݰÙríVe÷?6cýPûÿkqÝò {íîþºZ)ÉE~é7°þ'©”åfþµ~ì©&¡ ´Ú±sg¡¬o<[?àõÿ<œÍëï‡F\™»Þ6Ñ‘ßÉþ+qp¢‹Ôþyåùc/Si!SÁªWOÔ&jwéõ·?I ÷§õQÛîÄyÚ¥ý}¬Ùýõ3çï—urÜß­'½[áN)‰ÛÈ´´ë&ྻ§ï‹ e § åkqäùF¤QZÑ‹7<ñ´cèÎé›jļ3`ýøYÿ`ŸÞqÝ´ûƒ,^¿ì½“ï‰>ÞE6•[zèJ©ãtXÉZg¯oEæíi³½ž:¬îöûÙ›Ù÷¶ÂÖ øºùÖ¿^ا;hY–<´½5¤Wü²¿ÑسÑ&Ѷ*T¬pÈqº4¼¼®Ø¤öÄʵ£žïŸo3ý³:ÍôÉ×eO8¨AX¿eŸ0Ž/†mýNòÓŠSG`t±ÿWj6;N‡rÓëÅÉéú-“ÑñtbÒÃCmÚfÏëÙý—­ƒ±W–Äê Ï§E¿ïöejv‰zëÅÏ׺Hnù¦Çqº}ñ¯ßù‚ÌþÙ3WfßÙó<Ó{=üç~5¼ê³óuëYªæì;ŽqxÝYH³7íGZé"…^Wè<=â8=5#-óDz7â:y´ô¨Gñ4® «Xè™HÊîW¬>°~´¬~°¼àÀ¾ÄZŒS¥ÇGzMþÝDû°éØÕK\Ä3ðÄ€ºú㔨Bä®NJòU’uºNÝ7®‡á…,Ò_c~`ó-vf÷ÿùaÇ÷ÜèÚÍç2ǹH­âñ÷ ™S÷ ÓÃŽÒî$ð„gu%Ð"+Ów_z6ÞŸfó¦7–cÂr¡ó¬'Õµ¸Ûg†?H…C«_.pYë«+ëœ8Nó-04Y»¡+éÛîZ•®U(Ÿ‡é_?Ì™wÓŸ#Åž‡s›§‹/èc Ú¤î¥Y.²jËW¥NºSCãKåþTw&äóÁtþ(AÈô×V§Øz([?¬ÀÅk嫞3?ly–|:÷vï%êꃓ´.²"õ^ëMoÓ³6ÿ>cc’°j.½_4~Ý·Ç;­£ý:`u„õ f¯‹ÏQü,ÇüF‚qWô8*i/ùqZ¿µŠ ¨ï#WU<]6~ôu½Ùëž·!…¾¿bïY9òu/†²¼¶®ÆžWÙü,îøT\ßï/Ÿ0Ά9\£å}äc_à¡‹T¼ã|­i´ùqÍÛ„ ?ÓH–@3ñôTeöÊÖ§X^ëߘ[¢Æu7÷ç®÷‘§ùO;:ž»©ä—MÒh]Sí"‰Ü߇øÛk•‹O ¬Ï5›Ï°uÂ@¿kqݩݷô¼ùÃ~rýMõÓk{ºˆh묆e;¤ÑÒ㯾=V´ð¼ž@Çô.s¡H¿Ê|Áê=ó›|”’rxëôPjŸ?0ÎãϲV¦4£Ä]vb\Ô—.´®P‰uýÓ¨÷iµÏ*V=Ã:Æþœ@Sÿ´Ž ‰ñÏoXg÷­²/Z]\ü*œå=ÞÇ­Çžÿ~ÉÚ…­ÄÓ¸A%QgùzïjÛôØ4:hë÷Ê>ß(‰aí£öÁxŽ]­ñ§–VÙ¯‡ý“ÍcYî'Ë_ãç¡…y`œa1»FX‰O/\$=ŸlË_Óè²Ìmô¯Ô^„K¨p%ž¶Þ9ÂÚ¯K eó1¦+6å?a>eÈ’GN;>Ün³’ÚEgoŸO\¤U½‰®È´4êh9ä¯,e_RøÑ´Êo %Ðr}ê˜LFS¶_’û¹í÷AûTWgñϸî__ìû¾´ø‰äÅe.íÉi«4ЇïOå¿ô%Õ**>]Õ'Nô-LDû_VÙs ˵fó6ßõéãô­±h²~ÄR0b[ìoRÙ_¯ØžP;ÕT?ÿeíäÞ¤ÿ7?˜@G ¿7s¹3Êï3öüÌÎ÷Ãù|Åz9ôªÆõ&Ž_û8ù9Srê—çB]¤ËÚk†)í´µíõý×÷¾ò笰õóÜõ‚ÕW~?ˆÏÑÕâºÅ´>ªð碹f`O‰‹¼l³÷{;muàÍÝËÛ;“5;;¼îL }ÉÝñ£ü}òÙ>[/ ìïnÄuÍîs£&)_lÛ'.b.Ö°Òðévº EæÓÓŸµ&ת½ˆŠ>–@o7û¢‰â@¤ÿ¾Íê6Ë=vt|¾*E 6 \±bœ¿”üºõŽƒdzÊüj£J¸Hâà)›SæÙiÙ«gž¨Z‡ðù‰ TWïÔá–ÖÈwòXâû¡{Ãs®;óÏ)NŒƒ›Hï‘BŸX—L+à"sO÷¿Vf‘]Èé)M~SŽZ9au]{©k…õÑQïÌ ùç¡T!Gí¯ûYò-s/i‚Æ"Q'º«äuçÑJ,Œ³Sþïÿ.Š›Pr„)6ÌüÓ|ÕíŸ'³û»ô»%ŠšVó~8ËŸ ¼ßH0Ž/ëø!’¿mÙÍ»N²cÎGÚbŒóÛø*O—"-~{`îþÚÅ ™]ØïÏrY?¿iè}>À8Giº%Br˜49»øe§L'y&-õ4q‰öò¤Þ˜ÓæÂí.•8œ@[Ä(gßOð×;¦+ÖÏŸÿ\n óiŽû´ã<_Z$«öøÃ¤ÞŒïf”=é$£–¬¼ö‰ÁNGöXsæñ` 1ºìö&'КGýUæQŒ~ÃþÉòUØzÿ¨:s6˜Ó ǼC‹q¤ûþÚ­±&\L=à$3o$·ÆN‡Up)^MªM|Ë'KhÃJN_ãÏ)añ뽞p>§³FŽõQ#ÆÉפËò75ŽS™]]m¶9É˃tøévZ¿Ù° Ç«Ö'g[uº±ã4Jœ`©\!ÆŸcÈôÀê*˃ççiµ„¼«b¼_0ÎÎÝa­‡Î>B.œxÑóúF'ù±È]óí´zãñã’kÕ Ó[q3îúçÅÜ qÑþý1ö|Àß'Òýëñ¿ÔÉl\fm˜ß·>¿`œšKí̸„Ï ]á$ãRÆmŽ2ÛiCÍÑNÒIeI…±u÷5­˜Hÿ,Q¶DOöó«{,×—å±96õùge–|Þ´èJ¤×QRƒ‹ûžë$Óƒìç0ïßWû³Ší,Ý=&‘ÖH>pܸ(Ê>v€­'æ•k'ÁõgŒzàžºû( ©úttŸÉNRöÁÒ‰‰Iv:v—]ƒN¿)*oÛ–èßaúa÷¶îÇöáø†9Ÿ?0ŽoY·Ê1òkëåñ#œdMæ­v…Mva>Ó„ž¸yѲÔDº¨Îœ¾ý.e¯×±÷åm°œF–¯˜¥Æ8Y†'ó&#]£–õ›9ÌIf$ÔµÊN¿Ž[´Í¼¹5½˜»s$Ò™eF^|³4Úÿz˜®Y3LíUiØ‹p–GÆÏ—xi1Ná‘?U»’~Œ„2än'™u½×Ñ ³í”ßlGënx½áâæDºqd©íùÛÅP6ßdû,?ßW¼íß¿ðù×ß4løàN•l¤MFí>%Ú: ¿n§;žt¶¸cGúÜ ø¶éÂDñ ¹Ê‹­ÙóZözX=c9Q,2Ǿ8Æ µ¨¾šñ­\~¼hÉS©“ žìj1j ~ÙãiÅi§;Òj›“oZ–H··æ)³_«ÿ¹óùçºþý Ÿ_0Ιa¿½´ØÈÚWÈ'ÕPg|:§£’/véÕžvªÂÝÁi­—¥é°üû=ìõ°õ~¶»LRÝx0Ì¿æó˪,ùË=΂SH¿uÛÒª}ì$ÿ9ã›ÚiÔ¥¨?®ZÒoä»Çd&ÒSG6-m+™àÏ‘buž¸¾9ôñÔu%1œÛôÛ¾¦þúàó ÆqÿÔòÓŽ“RHé&#Ë<Ëï$ü>· û”_W|÷õ°õ%¶ãHV¤ÅÛWÂs{ã÷9Æ9ýq¾n¯¥"¾…¾LÒ¤Õ–šáõí”?Ê‘ÃíÄdþ‰ôÂIÙ™‡cüÏ…ìü ;WÀrqX^Uà:¢zÛWJ%Zõ‚*tW&™×·T·?ÃìÔÛÏ:¤–²!)·@²tX§D:ut\ÕGu³×kØïËòÄY]㟟jåXÒb.Å©üÜTré«Õë,È$u÷¯õ\ha§ËµÝ^x*~Nž6šssYŸDú›8zë¾îÙë®ìþÉžXdày#®ï˧¹™JøuŒLÒiÄÄævúèæÓć¿Nöp1·$ú×9ØùöœÉï—· ,×ç\w»„ ®=NÖ¬@=]2ɉS›†b~Z¨sû_Ÿ#ûÆï³©íj9>åJÖ‰HÊê"ÿ|y;¼O)ì/^Jý¹H>_຿Ûß%<æ8Q\m¾dmL2¨ÏÞ{úÚéëE“ïViR“LN ëwyêJ‹y®Üš韟²û<{ÎàÿûIxÍJ“æÎzQ/ÇókÐê,ù“V––£v'{vîW “”o]fލ¿]Èû3üó¶¤gò€-B~yöú«÷FÞýióë áý®“#S‚ëORr'9Ž“~£Ï9zñ*ùºOÇ)*øŽ[æ½Z¾ ½“Öú³-t4·œ¿ïÝu¶ŸÏÏ‹ž‡³sOëÆrŒ“ßÒo¯JœFVń٣’¯’o–½îi§ñhµ¯Ô ë“v¼ˆ¯³EÈõÎ~a÷_¶žËî'¼/ùõ85®µ×†ƒmÓȧO¯Šók¯’ã׫Žþ¦›Þ$k~±¯:Øjïmëø-T"Ò¯^$š²ßéœù€_çÍž§æüj1ŸšFÞQ%Æ|y•Ì.0?|nÔ÷ï~<Þ&ZL›FW)÷vÂ:g‚¶VýÙu—ÍWX.=Ó?ȵދqJôâf¬iä$9pdN…«dnÁ¢ò8… õzÿ‚¤SW¢›n¡·-º»iÉÿ<…í°}2¶ŽÉæwŸ¿㼊›_dÀ½4rÉÙþ騻WH‰ïGOïj§-~ÚÔtúšOIøÀñ—kWÞBùs üŸÎúq1œ¿o=ñç{ž³qb‘=釺eì¤i_{dûýWÈ×ý¤:}e§g=º 䵈kçNÏÞ^[üë0¬~°yËocÏì\GàónÐ/YòK>ž$µ¹W¹®ÆÂ+~='ª“‹¯|F|«Êæ-´âžöÏÎY³×ÉY]dÏ-l=ŽåÅñu¹ ïŒSý×¾­ž)ìä©îddñAWÈãEÍ[îf§MG ŠÛínA$gÍ™eïn¡%n\¸9 jÌ;zfõÏéß/ \×cK‹ kIƒí$räž¶Uë]!“×­ýüY„–j3©áðäVäûòËânn¡3;p+çÑï¬k°ù ?¿–gþºã$ßýôÞÏcìÄžozóI¯/“ÙãÛLE=Ëve›óÍ ·X)9¹…vÊ_Ïö{žgëËüëó„³|ÍÀ¼N-Æ![“®d'm‹6ó6õ2Q_k’5`®Î¿T%²×gŸ“ò n76^ÝBï65ýu®[öü›}N,GœÝù×Q?çúÆÙ¤Ø_F1ÛN.m¾V`éeòíÕONVÄs%_Gj’”*ÜÁ²­ôe·´IaK²ÏÛ±ùÄ‚}ö“+¾{íß'ôù×[f†5u-~ÿØíã÷¿L6npçÁR;åßßÇá”;ø]½¡[©o¹·g¤žÂö3øõ¹,Žtàþ‹×?iæn Ðï¾òŸ©q™4©«=ÑÏ©Ây :bLÏV ¶ÒƾÓì}AöúÙ¾¿}êß' ¬/Ak²ä¾õÅÓv­òå|‰I|UÚ±ÀNÅPÁÜFu)Ÿ#¹•žØëuféìýeö9°ºÏž·ø|ïº9ü(Á8­'|ãyb'´T‘¢_í¿DNßO]ô³V5%ïÞ¿1ÝÙ…;È´•VÐh[q—=UaF·¤Šk»³ÃK„Ï»³Ó¶º -—*Óö¿Î^ß¿•ºÚÍ^ÖjMÌ;ù€|Þò)¿ïÙº]àüQ‹q†ÕçŽFž ÍÄwÌë_"Í›T¬Ph{î®Ì¯ËÄn¥½"wPÎÊ~bûl˜­÷³õÙÀux#ÆÙuwcíü«O[­O4úê"ÙèŽÛS¾ÿAôôtÊðfÂû¿•f~5¼mZh.,{ÿX]îssÞøI¡¯„ÜËœ¹ªVŒãztÐŒý'Hï«#£S.m5õþÂÐ[¤|à Ñô6$³ÉäkôVzw¡ô#Õøhÿz›71ðuç­=)ðþìÄ8ù6Ox4åò ò¸è…‰W]$úÕu¾4Úé´ˆîk vjGj¶8^¯ío[iñ)‡í½ýÎs1Ûçeþñùå×,¹iAï.éOO‹‘+V{‘tô}Å×Ûì´¿Qv©Y6‚¶Ò[Y¿ê"Šñ×-vÞ€Cš×rtµÆµr¬OIpýb¾ƒ%éd2̬íx‘\mu6¤Ù!;Ý5Ê›¦ˆlA†×¸Õ¯sÝ$º°r¯k-ŽF¿sßâëÚ­ðO.D†»Ýò [vîÉçŒÓù‘íMÓÉ&K½=Õ.’×í²,N³Óƒ[ êº\ƒ”ªôÃóItÂÚÝݪ×~ç>Ìòï|>kGÛuw„u°œû#§@Û»®ãíÓɯáù§z_^ ý†¶¿7ÈŽù‹þ†)nW5zoÔ ïfNI¢¥ßtp:GQö~°ë0°ù1Ëo ‡Êž'ØzÁ‚ßÊžÞ³aŽõ-ÆáϦ çg2ˆ&NÕãA8æÇ½ž-Ÿ~ñ3Ššâ|Ú ‰::•—Oóîº!û «÷l~xß7bþû éäÇ:›[׸}žlòëÃAÍítXÍU{ËÄÕ¤G-oZiYmõ¢Jƒæ¢ÞY×çýrN8·ÆïKXq]îªC_¤“%—ž·¾“|ž<רWf4²ÓÉ“ŽÍ|R°>õ6áN'ÓÇ'Ö~Ô;,Ún›=מçsâzáŠù¡¬ƒTô½qçIˆ'&rs;mü¶]ï̓Ñû{çÛôJöÿž¬î²ûÛwà÷Ë^û×ÑØþ¾Ïk³ä} [RÝx°ô†çɳØQ ²ªvÚ1¬aÌoUeÔ÷5—™Ét’ëÄO‹G½óœÍÎ#<ùvÈœ½ÏŸ‡/¿y·ÑëÍusÌO$G¾f±’ ¤FÈ¢j'³çÞ[¿ŒŠ~9ºÅ¾#œ6 [–µ\—L Î_Ñ~Ÿ3Ò¿ÏÁîëã§•{t»†S˜o½ô½Or\wåŽõωƒ„'•(wýõ9V}okQ>\·‹u͈´vôj¨!'F'Ó«Üñ¤”H^1«ç¼n®‡ó÷‘9ï¸þÛßxlV:È M5T•F+L×M|%VÐM+ûuÍ÷m2¿kéàUóßÝ×bç)™}zÇuËÌ-4ÚA¬…qÌ9²nA†lËi4ñdá={—t ÏÛŸÿ©êêdZìüúˆÖÅ&øç¹ìú¬±÷‰}ï"ð|“ãÛÿíŸ÷´’XS÷G)ù9rïn±c­7§QYÿª’øtB-Kž¹:7™Šh¸_³p‚¿n眷ŸõŸóž›>í¦¨vŽó³NŒSyÑÃwâÄ·Pîq+]˯N£Â>0}I«×éÕ,™¶™~í˜aÏ»ÏÑLGlž°CXÿN½çôź,ù›/n ]ë =| ògI¢Pï[•–½N:°ÇŽ+“éù2Û¤Œöë•=W±ïy°}ê=Ë_Ý•7É¡/ Æá÷[ä§´¥mv8K4‡Ÿÿ¹!M¸ß5#o‡¥6n˜L/õ(2±µ!{>¾‡ÁæWüýöMøÍ5ç9¿%Ç8—gþ0âj‚ƒD>?¡IŒ;KòGص=êÇI¦.Ö–¸û¾AoÖù„ÖÞ’½ïÃîŸl]€«àç)¡9æ‹jŒótÔÝÍo·8ˆýÖ8qÚ³äËM#Þ.? ûÎõ´#ÝÚÓSþL¦¿hú†Ü¬•­cVÙ~ ËÁÎñ=B\Ÿ?wé Á1szÏž%¯"cÎO9ŸFãÜÿ¹ùõµ>m>2ûÏ'°×ÁÖ·øçœÇBýÎynÀˆëß‘Ÿë™˜ì ‹Šz?+x–df¨X¯§Ñ>óâ~ÞFln>qQQ3µ·¸þêZhÿý­÷òëšžðo[nzÚ@Fôôíùï[Zqý¥êq²'‰R©]PĽ gÈÎ'‰ÍJ£l’|1Ó?öqÂ|÷9€Í ùqœáŠõùk]²ÖÏ¡_'Æ 9q¨è›äB:÷3¤PÛµ½~»F›ŽjñE‡¨Útî¸75?®f¦±g Á‘£ßù^[wgã±õÀÀyHÐú,yúº uœkdüØ)‡ÚÏ8Cn\T†ÈL£‚ÞiÙ³ ¨a¦OòM;±áËhÿ96b÷)v^„}2pT‚qJÕåv”0N¹¹™÷9C<Ë?Ûq:î~U©jý2ÊíÍ,ì3f?§±Ï‡[`ßëdÏì¹ÔçŒóçä··ŽÌv¸¸*?÷©u†Tºöm»RÐoÊž& K<ÑÇ+ûF÷)n¦íŠ[ïrDûýÈæ»ìù–}Òç \W?ŠûMd\¢¤ðÏO“æ©#·-D/^¿qù¾0š1ýjÅvAfúËÓýv|CÙ¾;·ÃÎ 3¿ûü€ë¦¨Æ¼íÎeŸ&®îíŠõÚ‘F{bJ{ø)Ýæáü\wéî€q eõUõÉ£¶™Ó…÷yÔñÖ"šó¹×ÝóèM5ÆA~Ûpyò‚…§Iµca÷Ÿ£^Ì^?ôö@yI2cý³Í ›…u’ìóEl>Ëïƒdø÷o˜~Ø|Ú猳÷x‡kt9iU§IÖRmà®4Z¿÷6ñÏjW’–o™é7_ë÷ˆzf¯»±Ï•½ßÌlÞ¸îæÄ8WN¾_¿?|ýQókBN“§!ƒç¯Û—F /ó¨E½dyÔ’ šiÝWI¢7»Æû×ÇXýaû"üy©—á3?W.~ÎïëmÀôBúÐ Iôïé…$¼^Gлن,ë&w¶áû²¯ ‚x5Àd±ˆÞÓ«D+óÕÐÉwÃåF=K¸ük½Ð£—ëëf"!ßðïö-ÏÈ#ÿÚУ×)ä~±žHj¡%ËõäÊuàzô²^oV Ÿw£ú!©rõç岯Õ=˵نƀ¬G®”y徯 ¹¯R!÷5wöËsˆò³¿rç9xóÈ}eù_F¡RD®^H¬g¹%W˜Jèq"~Oîk„u“;ûúCmüPµAÿžÚ(^OFл9`,Ï!wØûr_@„ F€ ‡ˆM BŽ@A›@°Ðç20 1# ›:Ëtà²ÀbA†ýj¡¯“…ë{)dýݽÎ<²_m=0ÝBæ ËÆÖýâX."—yØ¿Ü ˜Šëe“ð™\¯8.›Ë»a½1 ‚Ù4ýyu9`¦€<‡Œ\}âòÊEÔ ¹ˆ2!1wÖ ë]+ô.̺ÉÝ»œ3rîlD–uc2±cseb³þ¼Ö\Y7j¡w¹ä=‰±BžCîÜWNÏÜŸÿNmüïÖżjâ?©…ÿ¤¾¯þý“Ú÷ß­{\½ûߪuyÕ¸Rßþ«Ú–»® ÿ-Ê•á˜Ç˜á˜íj ÈvuDg"/dÍ»ï¥b42„žt,ƒËí2 =/#„ž—\Ÿq7 ™]§‡oî,W¯G#2«m@ €¨„ÌêÀ C.‹†ë-Îõ÷Jø¼[ŸU˜Acr\ ‚1‚…ìV–¯Àõóe}îä0Š Ã,±ÀU(‚q4ydS³¬BÖCœ3U`6u`Va`O:›5“äB5g:5° ý|u¹ò¨YÎ —EmbÔ#-p¥ÐïCnë‡ùÚ¿y¾&~_gлù[,O!wþÖûr[M âN €ˆÍ@ !Ç'P@Ðf únæ:ò«E¥²3¸ .-p Ù­Fàåò`+׋SÈàú»½}Ýyd·:zsz„¼c@F¡- £0(Wßr#Ið3àð™ FÁ`\Ö ëãir[#úúêò·Ìy \__Œhâ÷dê„lB¹M˜;k†ëm.†aµBóÀ¬™ÜýÍEyd²¬³e­² Y¦5ëëkË•5£ú‡¾'›P+ä)äÎmåôÊýù7ׯÜuñŸÖÄC-üŸ¨ÿÓõïïÎç¸ÏÙÄg²ækf1ækåÎd5d²f9„iÁg,p9DjÁj,p…;èJׂ!ÞXà2!“Ë×2ƒ`!“•õ2÷…­õwzçÎa儯 Èä2ª@#¨…ŒêÀ¬A.;&°‡9g p„ðùÔ¹1!ƒ•Ën 9Z,{•å6£®Å'PÀTf †±´À- “Eä‘IͲYÏòÀ<êÀ\A.Z CSj„k5d„ðùÓ¹6BΪ  ™^!Ó‹å¬*a<3Ã|ZàJ˜Ð$\žðÔÉÎÔ±ydO³,A%ŒjÉ#{:0KËž–ÁÀF âò/„ìi.ÓF/äT(…ìi.«"HanC®Üi–gÃåN«`x+…éõÀ Ô0¿åCÎê‡zôïŸ'Ê„ñ#È*dPë…ìC–A-‚x"„ j– ÓÇ‚ !7(¯D=p瑳úŸœ'~¨ÿ½ÚøŸ¨‹ÿ_©‰Üg` âó[óɬ ¢ÔçÊ'Ëßjò[µÀ ”­H \ð%lˆX<@%d z¹óµ H lðn È)³‰ãš¤½A¾ XA(  ^ †l@ 3Ch€õ=Ù®Á0‰& ‘ËŸvLc".ÇLÈŸÌAŒN —àg †ŠÎ>{Ú‚a®Xà ˜ÌÌo²_C…ìWPÁt ñtÀT0 „ryhÀ[';ûP›GÞ4Ë=TÁ Ö<ò¦s¹¼i9ŒkÁ0o¬7­€‰ ÀÃmò¦ÅBžYÁØÆ\yÓr˜Ü$] l@ ÃÓk€5LXÓ‡yâ‡ybп«&*„ëy¹÷¢´)„iÄ©6 …H ‚Pß— k¡¯x"¶)„¬^ † m@ Që@F@v#˵æ²%z,p9oÞ`>Ö‚!þ¤0A0‚8€ †0L2€æ0 É ërÆDÜùg!óše;:…|G3ÃHZàJÊ$ü ¸æ²1 ¦n „Ñ,B.¬x€¦3 ÆÓ¹°RP¼@ #ÚÞ“)ƒ1Bf$—m¡0©xfµ) k „q-@óꀨ`b+äÊ´ L lBF¶AÈ”dÙÁ0z,p ob˜^ œ@þž|Iðä‘ Ëiûó¿Qñ±ùj"«‡¬rõÕ½ÿÛšÇj«qÿÆúöwjÛ?©k¬žýŸêØûjX^µ‹{Ÿ¬ÜçÁè¨!B<àjˆÈ–GÞ¬MÈ›ÕPA\V é¨ 4+…ØôÀ+2Õ€PˆNÏO†ðD^(„'ƒðŒÜ^ÄgRÐ ˆP@1ˆÛß¶<²f# Pwö"UB¤Ái¬M­@Ý11ꎸ61D¬î>—Ú Äµ¸â¶1êN°©Oëjß B!~=ð5j HQw ¤@ÃeÕ¢æèòÈ£Ö/Pç‘Em˜H'dQ+`&3ÃPZ!‹ÚË[áΫ™²r˜Ë”+{Z!äNk€È`8#å‘]kþ0ïú0ï úwÍ»TÂßç© ƒ0@qFA¤F ‚P5Àd¬Q­Ø€â5ÖAÈAÌà2ˆZœ@q›2³ B×7P@ðFAô`bˆ?8 &0Œ2€†0`˜"8æ0 ¢6 …Q À0ŒÃ4!S;æÑ7PÂD ‘tÀT0”„Jð3à*˜Ë $0˜x€ F³‚P˜M¼@Ó™ˆËä ƒ ‚ 5Àd0£ˆ`Èä0¦I0§Ø€&5FÕÁ°Fà*× Ba^=ð5Ll¡0²x€* [[ @s7 Ë[ £k(ax Àô:à ˜ß‚Qb(PŒÀ ”(ÀuØÖ^ï‚ß[ÿ«ºøŸœo±ÚX÷þ·ç]ÿ¤¶ý[æ[ÿ7{®yÕ-îuÚ€b1‚Ñ ÂÑB@AD …ôÀ Ô¨U6 …° ‚¸ÔÀ¤™AšB‹RˆÍÀ-†à‚Q“l@ á9â3qû È B#Aˆ È!H†(c„BœzàåêwŽ"ÍàÎsûªBÕ7P¢æX€5G<@ [€BÖ%êH jðÄmÔ›Xà2ÔƒpÓ·)„onþà2ÁD0CEÑ/Ww` Á$ AÑPÁ4VÊ}Ox€² L¤ž&¼‘4@3逆21L¥n „¹Ì ‹@£™@0Ì&†Ù´À ”0…¼ûÿ[„ïå>O,Î;…çA¶gÀþlþ]²!K>=ádb¾.߇ߧgþ,N>åïû3¢¡v©ûde};Y?m–3Q½t…GŸ%SÖ¿­kÖŠÁÃêEÒÀ¾‡rŒÓ¿¾ùX{Œ“8ÐôzÔÄS¤õ¸.s.\N£ÝGÝ(¸ëm#²ïúÇ•Sn&Ó ‡~Uzs¤¿ÿëÄú³œÖw&°o¨ãl˜g j‚q†yu†2O‘º?ÉŒ»‘F¯×ì×ú“EµH™.CDÍ&ÓZ\{¢ï"ßéŸÄreøþPO„¾¡- ˉâÆÑbœ fs?-ÞÎA4ÛîDî(wФæˆ¤QÍ—­+}²‘ i[hÖ§f¿=ÖW…õ;áûèœ÷÷‹æ_¯ÔßχLjqòz«š;È£=YË*_;I:öOßqæVíõz¦jª,=z臒õ:™éÝU×òÈÎ3äû¦|FX_7Ög“åvç賌q:û¨8H‰žîH’O’R!=¯w¹†qžo^ó‰ý3z¶¨b¦=·o(­åï;Ïúıœ8Öwõ£ ì«âÄ8e^6ØØ ¼ƒ´ë³kR·I'Ieu›gß]J£IÍ$ÞUÐÅã_4ïo¦—-G^_vDù_ë¯Â÷¡z_ûûiñÅÿ ß]þ{ëíYm„>› yeoÌ’·®½¸ÂÅ ¹’^Û–Þñ$ù£ZÛ1#N§Ñ¸|NšFHé£5Ž—ndö÷ f}‡rç°>A¬¿#ËóùãœYñƒÂ{5´æâ€?>IÚ,/×pÏÿÃÞy@5•íû{ìØ±ƒ;Vb  Š{lˆ=¶{ì(t,Œ5v$‚ëŽ1(%5–ÑØÁ‚Ø™±ý¿'çìc‚zï}÷½ûÞ}ï?®õ]Ìrg“s~ß}vË÷“@å7.EƆ:S–CTòUÉ!s[|Ë by`,Ï…¯C{­ ׯtïE{‰1™¬÷i¾õ}ù²¯¶W¥+ ”ì.íBåÈ×Zž³—|þ–ÿ–›ÁxŸŒëÅònló”h‡ÑN&›ODÇ;b"•›Hï¤&Ðò¯žÔvÔ»“„º Vhòé¨lB™ºÚ~Ë_a¹FÌ7¼OŸŠ÷Ë6L…vêÉÊ&„,I&ž[wÍ^a"ùo|*ð N¿§ð©^äã³råo—üVÏ,ïˆåȱ‡ñ¾'Ÿ““ãyU‘·š>ÇLÈóG;ƒ.Lí¸æzÑüâ-ŸXÌDB—%´ ìHKFG„”ºÓ“ì]´$¨\Kí³ø×É–3¿ËÙfõÌrÃùßÞ×äp0S¶{ð‚YýÕIäöÉÁu_J&%ÚWÿ%{X"]8pDtR¥.äôÙÖC5:EŸïj×p¦˜OÄÚcÜ5ÖO³|OÛãåÛüêeóÆ íòÔhg{¾ü3Þæ$¯q¿y¹žH"ERâND-Ló<ûŸW«CM]“Ãy㋱çÏò»‚³šîýûZä[ØåÈ¢òŸZÅ$&’RaéÏH"m7ÇLo³(QÈËC¶]%ùZõüúŠbü[>#Ë/âû±ûžñwG=íÛÜAÌ+²åá©ÑÎñ5éóêJ$=;oy»xñ¾^fX|ž©þµ%GþÈ'äEëhh¥s•Ô-¾õ7,ŒÏ-ºàY®0VÖ+‘ãÈçC üb´c=›‘HÖf9ÿrДH|¶8Žªº€ñG®ž¯ÿàRõãð.­fŠÏŸÕã6±ûgË7³àúI­ÜÖm˜(pIÆúê–¯¸>Ÿ_X†\Ðy™H"^çéy…|v]_æJj"m®Üžÿrž:d‡n¤YÙae~eíñã®óž,Ïgùºûb}þ¡™2o+˜ù2éöòÓÞüWˆéð¬†×áë_‹v"!“Ú™GŒ¤…hòg‘锽ºsñqW‹‰ùÊ,Ç—qmìøhg…ôÚÅÉ-.“ ËŽÏ5¦½ûo$R~|ðê|Ðêyxþ|ÞÞŒïxü{ÐìÉ~²qŒí|B†v&žÞ•q:;žlÚçÐÛmôerÇ“3.žóéL}ïmY¿$åëæ[þ.·0®ãK '»üDÚ‘xlï¿[Oæ-mÒã`‘ËDõzôS"í••zo¾W Úsä%«ÃàGU¡‚Žžßs'Ø}c¼46ž´ÍåT¡Œ¥c'eøÅ¯³³¾¼8O®-½ûJ"åŸo :Ê ŒŠ¹Ã¹Ç,—Ó6÷Xë®ùðàý•ºñ$l‘c|ðèxrzr›aÓéåȹ•ß_«LtÓý7¼˜IùñÍ41'å[³œO;>+®›ÙÙ·_l¦‘O2ÿäœûÝ"é™Ôf}^ž$ŽØû™q6¾ ßWÀðÐz¿qÝ{uK¨ï~¸D>}\S´P#™íÖ¸mõ$šwûŒìþ}È"õí/{GRãÓÉ]_NÇ,Wž¿s : ?_Ãn-C;Q‡ª~Ù¦½D¶Vó~7æË%ò k”ÏÐNI4¾Y ÒõJ²¥àÂ~†…‘4_Öœ/ÇzOþn|Ãsw žêÚ ç­-óNÈq­)Œã¾7Úé¹0¨ç–Á—ÈÙˆºÊ'.‘;ÃÓ}Ç%QžOçCNw›}ýFp$åRc“·Mý.g’qÄy^Ú#‘ódË‹T¡†§V_œZí¸¬-²cÒ%²:drÞqË’(÷Tvn”ëÄ=’ªºÖï›Ð^ùÝû•å ³çÁrmçmj´ão2‘ ãȪ­‡fxÕ»DÏ¥˜CC’è¼ÉJ^}éA´÷8k$ÍùsðÖJ{¾ÿ<ŒcÀø%%ÞúÜZÑÛÍn\¨G;tú³Öì8ÒÌåñưq¤XËÈõoö%Qëë©rc2òc¼Su¿HZ¼OvÙé¦|ײ÷øí7ŸÆïkÓHœ7ZýëÇÜtj{¯vyéÐkp´6Ž\2¼lÈá$ÊûՙƄ)úõ‰¤n7t§Lû?ö~`œ–WÊøŠv|½°LYKÖ>»{‘4ìºëÙGeqpëh O¢<kyQ²äK‡õN‘ÔŠÞ¬¤l]åÄò<ìûâúGÛržœÑÎáùÂõÁÉm«¯‘mâÈyŸÊnûŽ&ÑˇwŽœÐ½qñð*QÊ=’Ž8v©hf¶’²õ Æ'cõÌ«?xòŸ×Åþ½ve͘Lz‘Œ=xìˇ8Â¥S_:›DχÌ~Üij XØ)’ZéK””­Ç°<|Vό̿Ï]ÄþÞê´3b¸jÁ̧±„KM?™t‘,×Ñ â“hÝ[³´#«¼Šµ]Ó5’j ŽœòaúT‘œ›¯ÇÞSã$—ΫaÏsE;ý?Îlç§Ž% ¿Ì˾yà")®×¬.w3‰Vð:ó[þNäè‹c-k8E \†)b6[ç`ãÆÇfï{«_pýIs4ìKøõ‹¤FP‹W%²“Xî9‘Äç­£|ACï ñu¿3YäŲûÄøNüºCUûù®¿³qЪÄK‹s‘$-Ì*WW’LO4QWÈ*'cãnu‹LŒ ÅÚ¸Ì4ÕžDÙs`ãfÆ£eóþ÷·—XзÚëi ©—æËÔÍ/’~üªvrI¦ü8CN>Ÿ+[­Å™Ú¬Jò8Ù¹ñâsgë~Œ'ÎxB<‡½¢ðÜùñœƒ&SvuKã³ 1$ÜgÿÇÖ.’Ãíïû›['Óĵë|ÌO»õÚ¡îÎFÐî}ïô/éOísè+ 9ñg=§žý=?æyÔÎh§Ø¦²¾}cÈ×+ÇýóÜŒ%ÞÞKÜ÷÷N¦yÇöÈnÜ…<«rÛõHõX`BÓ1g›ùŽÍÙ¸‘­ãðõ‘—÷ ÚyUŠÁ^ y¼—ïKR®Ín;.™6Ø2iÅØȵ–·ÿ-‚Ö6~p°àñ}ÆüÀÆqlük;ŽWàúM–JŒ˜wp7Ó²$–8¼:ÜgüÜdºV¹Á¹b2²DZ²®ÛÎZ©×«ú'ŠëN¬öü/‡Õ‡íx^…v¼ºS¥à2Õ <‹%-îD5·¬L¦ä·–ãš{¸ï嵆¿W£FNט_XÿÂæqVpŪöÜc´óðC«Ï7ÖëÉÛƒ5ÊÅ•%¥‡¾oy{C2}”ôê—z•ÜȨåC<7nŒ Ý _›ûÇd‘óÄ89l¼Èrðùq™‹/õhç³÷w©¤'Aç×ùøÄ@º¼9x'$™ªË<ñ~ª¬G*ìΫh·4‚ÆŽMÍŠœ"Ö³X×¹üÉÆÙ¶þ´ {Ç»W.ÑÕ‰«Úæç äÁK×» v'Sö~ÍÊÊsºÉ¸*¥#÷¶/2Eä7°\zvߟšõ—¶gm¦Lk9é7ñ<93O•uå7 ºê†H¦—vŒ{Þ½Ucâ{¹YlTã1’Oúi=³uuö|øy|Þ7h§Ù†ÔIÕVž#íJÅΑ.6GF×tŒL¦A'f–/t¯-9Y{ú…ŠŸÂéÅ‹½O­ü­žÙ{„ñ¼Yf[Ï2\ÂGcíugIëô—1×&È­¦Ñ&™v™Ó~wMï.¿³>ñ~85ÎùZ½no¾Ìͽfó^Ûß_ë¿3SÆ3ÄìXeë´¾R°tYw;™†n+yþqQ²¶ÃÆAç á”çUû{Ö›÷2þ"_Ú®7ªÐÎf¿jÑibiüqÖ¡¶½®|±¾&ZrUÓÝ£‚ú zÛ§ùó4›ù\=JÜw`ã|ö~aó+¾îjÛ­;«ÑŽõ­|ЏÇì­Ñ³.Úé9ùtdY}ݨÐÕ‘–~äÌðm5ô™átWÊ€Ú(ÄýÆifÔˆzÅ ñ}Šœ§õ Ê[,®áGÙº+{®l}€÷ý‘‡jÇç>‡a4*ï)=aE¿Þgcȵ =;üšè¦å‘‘³7%íÏ~NWo –ßÊ-îc²ûÆxlÝî°7wgk‰ó(«_пÞp”Xq»cÈóí^­ï6ÑeôôòÂ_jò%u¯g|8í0»ëŽr½GÓí_VmZO¬#ö^tò‘qê/Ùâ|ÒvÿL…v”8âúrúÙ¼)¿Æðû¨CM”W Ã>æ{/)œvŸ•§IóIc„õ•Êbÿ¥­ÿt‰®ð]Ï´+w$í?×gíøhgÏ8nc3Š\¬îµóÔÊ¢œ0`Ò’á&:ÆËDzWRaqÔùÅøVô?;Rä"²ÏÁ8>ú¨qÉc'4³ã†Èp}çoýÃêj_G1dßóÀ]/—˜¨îÑæg“›{‘?Œ‰(NOëEùQPƵfë7l<Æx¢GÏŒÀÍnZv0HÐaöañs„˜xh­‰^.™§áÖf^¤QÊ ¿R«ÃiÎý ÷.Äy_]±ÿgó ÆÝe|<6αúíðûÚ‡ˆwÏÅú‰!Eî¿.qp«‰ž\ÕuJ¾Fɾ6Us'„Ówc¾¤¸,rظœq1σÍcìÆehgzËO·Yù½-òRãí6ÑS~[ËÆ>ò ËF5ð<*œV²¬‹ëßl€í;ðë wrÉY¶!_qºÄÕMcÂiÀv÷žEÞ ÷ƒXÿÌæ1**V>}£‰¸eõKd¦L¡ª˜¾OMÈÚyo*׉!½j…Lð 7ÑãÊAËêï®×îeÔ§‹ç.We¸B¼_Ì7l=†ù†ŸßºÚ=g´SdÅŒâÍ[ì"Öi@ƒòÚ5ûð‡Hô3ïê¾:U“¤M<Uú–æpÃê²#©=gçƒ÷=ùÞ¶ë}2´³î²wzêímäì³IFÒ<†tîž~AwÄDG” \ëÿÊ•xÎ5,xxMK –à€#Å~“ÍËÇ„Õ3[·´7)Ðλñ܉”­äqµÒç?àù/¨\úÌ ½¤ßÜeã¼–$£Ìœ3µ4¹I7ÓˆŒ‘ÔžÃWA/§‰û´?šgªÐwZæëƒMdUÖ ëþ­cH-Ç"VŸ3ÑZ+=K´žÔžh<‰¢Zê¼UúZ6f¤8Îd?Ù:){ïühþ§F;³ßo.”Yo#QíÜÞêA‹2­ÅóE&Êsr»’Ê^'íÑÒÑuÏæ?ä© ¬_fý&[Ç`û¶ŒWÍ׿ΨG;+û>~\9e-9V½^ Cý’4cu§ë&ºä¼ç²¼½ÉŸ©·v­ÐÒ¨A·Ó®ùŬXÿ¹âô ÄeÊÇ¢ol×ÿ,h‡çì¬ ãŠì¼n®CôEOL’dš¨ÙH-ýH××Cï®\ª¥¿·ÞcÏ›Áâ<ýdãÙœ‰×^~ý§ð¨c·~í Ë”M1¸JªÝ]H´}Íë\#†TìWyGàíÑxb!c»$vö( y´t‡w÷ ”ù‚£ùù_¶';·Ä·X}ƒë/àlç8¨¼~v¬C’G¬õu+ŸB?v£-8ôà–ÍZš~ý­ZÝï;~ã¶ óù5ùžƒàgaÜÎ÷g2´£¨?cA/ÂQë£v›«6l”BÃ=öæ<ñíG>U>6°zÉpZblèêÃoûŠóL~“*Œg^ˆû¶ý¥ׯðZ2eÿ¤‘´ÙÀ‚W Ö!g5ì´R–B7'^0?¹æCÆ×W+S/œòýpñ>±ñ«c¶ËÎ5ØÍgÐŽªsf*­5‹†Üä@m1dȤý'ªõJ¡5-Y5¥ qœy,¬vÕpzétôÚ‚Ù(ã³þ˜­+óïW;>¹×?¸£ÉûáóÓâ«N}÷XˆÙe_¨o Õ†›GüÒË“àå3–fi©¾]X|µ_§xà™^+ c¥ÚöëÈ:¶¿Š:ZØ­á¢Ò¼sËVËG¤Ð{ÝîxõlIâŒÿc±FKÝ/>Ø;¸S?qþž;gÄÖ÷7w{}¤N»ßß‚vjN؛ܥâú[v÷Ïg£^ëDœ/7&…Jó´ó=>¸Ñ ñ¨{nªVàz÷çaüzAe‘'ÅøUÞ禔rzÖÀn×!*SÆÏ‹7 çÈbH^õ£ôzRèŸ~ïÓ½Æzea=9ÐMKy°ÏUMXç¯&Ö-ÿ¼ßˆl«/pý½^±-„ÊC+öñz…ç°þë°?§§Ð–çW?¹tº#©ÊMÃ:kiÍ]Ïý:“Á⼂}6¯`çXX¿hËÙ–¡FGv¾<·z4Lßö¸ª,¬¥z¿pa mWÄ:¿+9Pvæ¼®£µ´e½¿Oõ¥lý‹Õ/«[þ¾eˆë ¶ü-ÚáfÝwÑÎGºàÈš?ÊÙ±&…N}_èòä=Išž¼}µV8/×_\e¿ï» ·‚ o3Ä÷¢í>‚ ×Ñ dj߇jºvdOS•ÒyEÑ&³w¤Ð ³>t›pć˜RËMª¹KKÏ=zz©Î½Åõ}¶nÉÖwØ9F6?¶õŸíl)Ìý¢{霧 žsïûݺ:¿¾RˆT*ÚÏÑûiÂs‡û~¿Ü÷Ú‰¸Zí}‡J·—Ä“#GÏ%øÅ¶M¥ÄõÎIÇm}ɨs½ô¸«}À_ÿ÷*üûV0è1º¤r½!µ^Ä…"ê^§^©ônæ²Çæ÷%®)/¥½Öû²bÿÃÞ+ÞQÇv:¤ñôØx[£>Z4v>Ví|~|}ãúo¦›\:F›ôœ|Ÿô .ÜÁ ™*ÌSz ãY-íf=×R\_dó0þ¾¾ö­ ~ýªµPÿü÷ôhç¢r–Cì°ã´ 7<]p‰ø_ºkú´TáÜKw±^j6Ú|²æŽV”ùƒ­°ù1û•Þœº1èÚûîdŠS nihFùÓ¥?í%îs°ú`ûìü‰í¾¶ÃñLYõ2õO;KOÜð]5gÀe’ÕëÍîE ©tÙæH¥{#oâhý¢ƒ†Íè–?l‘øžeuʾïÀSÞx ç…óÂøí K-=ç,å×?/“!§¦÷¼•JK¶Ý9ëzÍžäYßuñ÷¿hèÈ ŸU… ~2±u'6¿eãQ»ïW¡ŽÖ|Ž^V¶ÛçÝeRhÓ믽ž¦ÒÂÌWÉoó1lhè' Õ¶[¬ë«í&Ö+/²u ~ÇûXë~Üœ´r\áóteÙ|µ¶É®º¬wsRéÕ~ÏEÁ݉ä—O$yá¯úw›—ÞÓþ»ñ'[áû‰¼„{¶=g¦B;‰¿N,2æ—ó”ßw¼BömÕ>ªW0’ò]VOþÐ…ä·nxiéöª‡Ï¿ j&®30¿ñëKÉÂüà½'[g°ÝgV£·2^òß“ÎSm%®¯ž¯¢CË—JÖ_:-²›h©0>£lÊž {lbõ®›dyYáhm*¬&›¿å ª\1Ö?ú!äË­¶dVÕ°zñm´Âýo#ú€­_0nñöá-¸~Ð×¶{;Ì¢´ú¸mºÏcHõåÏzGWK£úÆs^ï®Õœl$NYZÊ¿§ºŠã¶ßÇÎÿ³ý>»sŠ'˜ß(íå‘j9q"dö•Q#ö2-–ÅÕ#¦­w¾ m¡¥·ÕíÝG\aó¶~Áú'vŽÈ¶_rF;ïp…ôôkíÃ=šJ$²üEË{ÔI£Ÿf4ì6fn#_µÕ†ßŠj…ñηy&ßïTÏu²ýž Ë¢7¢ì¿Ç"C;Êã=‡®¨§ÕZÖ‰Ú'‘”º{ËÕúiôÔ¢Ÿ^—‘’äÅ$a\Ò²ó<¬ÿcó6¿aóvNÏê´sëÏL2q¡ž.õ÷Ÿ±#‘ÜŠyÓ'ªi}Mvå-°TF:æí¼Õ}¥†vÖl݆ç½)‡ÛŸ÷z*œ+úä9bË/»Ë‰õ¸À3wÞh§Ïvé©»{ôôÞ¼EKß?M$­l-Õ&òßKìD*ì¸p·‰ŸFXêIÙþ$«_¶ßx¥›¶­ ›G[ývúÖQx¬ž6ôz½YÖ-‰T7 _ç4ZA×8Ú­JW²ç¦¥ÓÛ‰a_¡‡¸®ÌúG6_cßåŸ#»õ=Ú¹¡;xÄõ¹žÖs~±Hu<‰l®±kCxß4Êïö ó³òŒœ¡¡ü÷½s­Ç§zº”0–õð™pþ®©xnÎê\¿W*©tUêaÈ.•Lªí?dt…ûµad¹©cz#wlÓ‰¤º¯T6QC/V»y©ÈÂîb?Ìê€}O†¿¯=ùõžÆöëMhg¸÷®#Û".ÐB/5Ó÷¼N&×çŒu•®H£ÊÆ”œ‘Õ–LoÑeÀŠZjî•“‹ç1ØüŠ_cëMlÿ‚o‡?'¥àÚáŽ-^¾@ï¸\tXUÇDÚW+Z9ruõkáÝ\z .Ὰ㶙¦Ì9îÅçÃÆéì<;ïÃΗ؞ÇS¡Cº>í½®^ [ó¦~)äk"ʶ7¦§¥Q¶¾s7çfóîxÕ¿¸.nÏÂ.â>)[`ëAì=ÆÖûmûO5Úñ©|5SvíðçsÕ³Õ&R{lû ´#Ìé®›ÔÔ¤¡Õ—©æ.×CÜ_dãr6NáÇnâ:¹Õ/¸>?½@KµÉ(]✉”^_}yg\¿»Q»Ô5!ߦʰg4tЭ²ëƒ&ôûgö|ùûòГýŒ9¿³Ó²EMí¾olA;;­Ûj1ÔºMúÔDVg ¾…çÒûÁ¦©“~ð¬uܧYëãú¾ý–eõ£ìü«¶ÈÆí_çr¬±8ΰú&ãëD,†òóø2·Té£Ö¤Q§/U·7¾Z‡¸9æ7š¢4â:6;/ÁÞ÷l–í+°ïÚΛœ£Ù8/†æ”¿¶vv—bý߃ӨËNåXÒ^JôÉ»[…ihHÌR¿ëçúSvž˜õl\þÆ Ï{^ˆïÛõMÚIì×p[ uódò¤_RHÉM*+!itû¶Ó÷uîH¬«Ëj æéâ:Gîsòl½Îú5Œ•5Å~b¯ÿ篬¥¿²–þ§³–„Ïjâž R IP”JÈ ÉPœjHÂ1_!sþŸó¬mùˆÏZj“g›ûkˈˆ¶á#f |DµPä1ZàYÚä ësñÂl3 c,ØèdÉq™¿N0ŠJ`YËm2ÐþlK.=w¶¥óßɶô2…Ì_[6„-#Ö6céGÙ–¶K¶Ù–Ñ6,k‹ÀF ÈŇàÌËò-rñr8žuN žh´áåd 9è¡ÿ@ºì'9K¡?È û«Oü«OT9ü{ô‰᳘¹g‚‚ …Q”’£8C!GhdùÌX[†ÇŒ•Ùä9ÙfgÚf¡ëmb9C,T`Ær 1½ÀŒ ¶Éü5æbæØækÿ„™ÃŠúd=é!g%PàÅúØäýªþÜ9.ï7wîœëßÉSy›®BÞ¦mº-OÑ–›ý£Ü9[n¶mîœÞ†›!ðÃT¹òÐ9®"ËžsÎņP †ö²ç"GÈüÕý™¿òŸ°´u?ȉ²æ:üëúĦ?üöƒÿ•}àgÿ÷ßÉŠýŸäƒ9 ÿ-A‘)!3$C±…BŽ(¸È ÉPx¡‡kËs´É6·eà0Vb`.6˜«Àã˜`ÙV/dçâaÛroô?aa;£O ؈¹³Ì¹LMF,Çãr„uÿD¦]6äû7rí\vŽÀDÔ LDL 9ÂHJÈ ÉreÚ™!7˜+D0˜®\»l>¬“À¸±e!J…l;Ÿ\\Žë&p8c2®ƒ«;Ÿ)ü³0i´/œñƒÌó¿ÆmÛTÿã6WáwÉàî) 2rFQBÙ/Š3rFBÙƒÕjËàâX­>(àPÈEì™ ©M¾º¿Àje .I žÁ-°Z9—I`µr, ^)p ëF‚âWBfH&d¯ÿˆwø„\öºæPCŽu™ 7%D0 Çß2Bn"RÀµŠ>D(|ÈI(k0Œ#0'ô+  å@ ùÁ!P¤pÆßAn0Jˆ`d„Ü`šÁ8þ ’ÖæyÔ’p}`¨hȦRAÜær☫P$ƒÉB!GômJÈ É`ºPÁxŒ¿êЇ©X7¶ŒA9 ©†r¸½‡\ ŽÁ*ƒIC!GEa‚¤"òq£!gômP6äû7˜®0v Àô…Áõ÷mÈà¿Æ}ûþ}ú>©ÐV6wÏPzÈE å@ §rEC9ùÎ^µe€qìU_°rB+!3$C1«! Z)°WÌQ`€éö*Ç3 ìÕPÈ‹·ãˆâ€,&ý s‡qÍ æ…9Þd†¤0ŠZà®rü/$…iBãøC&H ©! L¤„ÌÔI`(%dváYÔjHs)!3$ƒÉB!GÃcd0\($é”)÷ЖGeC¾0¤rjhϤÖAN0¨ 2ÛpW9³rü¯(RÀ¸F p©¥0q0”)öª“ÀË€d0v(äsûCFÈÅ å@ ˜Ý¹Áð!PN›Ÿs©:‚¡3ð'|=rþïÿ¯÷‡ÿÎ}!wÿ£!ga ” ù¢õ+ 2ʆ|Q˜ú°\õË57_Œc¹@HŽâUC°Ò†‡¨¶áŒ™!™ÀuÍ€ä%yæÇwUBfH†B…$6 jŠ>rXˆ?bP› )Ì XˆJÈ É` 5·!0_Ý`Á$ ȹÁ,!‚aü!$…qÔ‚yü!$…‰ÔFò‡L†RC˜J ™!Ymž=íøb6ä Ãé!W˜.ʆ|`¾hÈ „²!9Œ¨ƒœ`ÆÈÉaJ$iÂ3` jÌÅ@ôYCÃúCzÈÆ 8°rX9ÁÄJÈ É`æ`(›Û»€©õ+Œ å@ \¹ÂäÁP¤€Ù ,00¾â¾}ò׸ð¯q¡Ã¿O_(®•ÃݤrCQ†…é!7hˆP¤?cÚ ) W-¯ІœQÄ’£˜C!Gt€À†•£°uŠ[ 6l dذ:È ¯‚2 9 _9¡øUPäè G!²@rBgÃb´@r˜C9Á *ÈÉ`”P «„Ì ¦QCG ™!  9ÂD’9ãï G*²¸ð<ìPÈæ €,&Ó NåÀ÷m¬_cýëËþýw_ôÜóCC9…d„ÜPLÁP¤@QÀ‹5 ÌØ¡Ðü¹sÆ(¶ nO爂3C2](äˆÂ €,Üw-P€>+6²@r¢r´aXËQ”:È …ð†µ’¡hC!Gndäè£B! Y)°c¥(h5$Aå™ ) \ IPäJÈ ÉP졯„Ì … 9¢ø•’¡ …ÑG@H^›çW;Á’¡RC9†1Bn0M0”ùÂ0v´ÀÒ€Ì&WC] ™!  I~ÂÓ€Ì A¨ç7&6WËBþKûĤ?´í ÿ/÷ƒ ‡Ÿ÷ÿì¸ìgc2Û~í_9.ã>“‘{.(¤¡˜ü!$EQ……å™ ) L IPdJÈIQljH‚‚SB&È …es{(@'`dä(Dä„bTAÜ9aewN…é„ÂTA 4rB‘ª  ÈÅ 9£`U’£puŠWY 9ŠX9¡UP䃾K9¢° $C‡BŽ(r%d†d(öPÈY 9 _9¢ø $‡ tŒY 9ú.äc¨  È‰ææ•0‰’£ß þ‰›S ÆQ@FÈ  |a"=ä #BÙ/w†2sßw…¡ÔÜÙ_˜Ê¦2AR+ò…¹ôÜþ –ÁLæ™ )̦†$0œ2ARO I`>%d†d0¡’ÀˆJÈ É`ÈPbß_åþ^W4ùþlÞÏöjs}·‚õuìO‚ðߊèLY‹÷µ+—<#æv¿is?¸øþ1—ÀñòGé¶}i”åûð9³r1÷­ŽôÝâ‡;5bŽñ.V¬y_j˵U¡ŽV›v3†v©þ&Ï⤲L–vrš.Ö¸a¬´m`7Ò²VVÔÛ=ÑWÌ#cœB¾ýçžë¸”¾MÄœ>îúj\_ÑÖ)ª—ƒ.ˆúp?è} i¿²ŸvÖ¹4ºMV*Èé99~¿r‘Ýáz±jïGíó=*ŠùŸŒk©ìôA;ñXCÂòì¹vôÑŒsi [ûl]3ºV*òeíJI|µÒ7B¼HÐýþ×Z]ÔеËûTšð-›å3Žã|0¾‘mn˜ít~-6ïuŸ6vìJ~yòP}75v )ÑáQþ¤må×ÉÅîkè—‡g¿®ïK¿…å.³yKÓ­‘uÂò–Ïi›‹íp*Sæ”òKß7hÔÞ‚inóSI©2sÜÏÜL£|¾N[2YYãÏãï44ªùò¤‰Ïûˆ¹Bì¾±œµMÙõ»ðÞ“å4Ùæ{;£zº¾OÐ \dNÝw8•œh½ÌrÈ’FÍs-lXsRñÑo[ Yú©R×%Êýļ‘_#Ü7–»Î]W†ë>\>3@òÀ@‹Öÿ%äZ*ñŽ{’¿æ£4!Ç­hîhÄüs–ŸnÏ÷xìÉç©Ùó¸¾5>ï(¯ß´8_YU³d§ò™i´ßrŸ—oê5%U½ø¹ËA åèÉúÇCEþûÉòíXþ6ã®ó96—í,*½»í…/:žÃ6N#O/]!»_¤ |¥–dL†¹}í =y4²¼×Èïø¯,ÿžå‡°|r[.¶íü²K›/–9}nÓÁidxÆ·¼oÓhSk`Yk²æ\…Ç.ih*sœÈG‹92,¯„qjØ}cùQv>A;¯3£¡žo˜F.Ô]èS=q9ïíû¦ö$ÿàÓK&Çj¨'·x´˜‹eŸÿû@È{-æÞÙr-h‡Ï¯1нœŸ—Õ¤'©×óè‚étU³"Ò6;»‘iqnuýeù[#…ÏSS̽áóʲú Ç’™‹ë¼ðd¹œVàúݪôu]µ9Åø¤\:™ó¡Tá캸þ±Õ_L } GqÛYGCgX䇉9R¹ù§,‰å€[}‚ëW¸žÓã}²Zc¦“[‹ï6Ñ.ÎYí¨éGb_ŠüÖ@CyÞÕpñ¹³¼3–ƒÄ8|RU»¼gÚé xdªqË@zs@×tR¸þÛ¢zŸt:ydBêʾ$}ѵO9Ý5Bý*D. ûÉ箥ˆèÜêSG’ìvü@‰×aô^jÓ'?~ãù13ãÞ²qmèÑÎÂ>/Ò^…`|tØ_ûyç5Rc\Á-ưt*¹ð¶PÙ^dÁcm¯Öè/'.ütÀkÁ(‘ãÌ~²:f9½Vàºüø×@wö.Ýýò5ß .²Ä‰tÊó\:žS©¡ýý·$VEYN'ûÉ縙DÞmþ¨ÃÙLYë¢ónÞ‹4Ð!µ^t®þæÙŸêYlô…tMcǧžìLúUŒûÝy°†æiy|Rx“ïo–Êsžz²ºµåÑ9£èøÐfkÎè—u³¦U¾N›Ý|•N·¯s˜R¾IgRb÷Ìúih½ÍÙˆ/%öÃ,—•õ[Œ¯Ç¸0ŒGeõ ÚÁÅʯ0èðs«×”_'š=˜|àz: J¿xò¶N¤ÍÜOw ½;iÆè¹1£Ä÷›_°|w«p½¥\j¬É@”ïVçÐ/×IÍÞ†Òî¥Ó6;+U‹JhO Lå!5ôùïÕ_öo0ZÌÑcù€,ïÔtyT¡6_?z²¼Y;Úqv=ùb^º®à†‡®÷¶á­gâþ¼ÙyØåL;r*ðe¬¶‚FȧþÆc??—å0³|;[~’íD¸Öίyj ×{nZÖéæuÒÚ¹oà†—é´Ìä]:ÎoEÖìŽ;÷Gq Õ­VÊW¿ïxã,O‘åZë×íÒÿÿ±÷PMe]ÛXAGìØ ö 6DT•;VbcÃQ¤#¨£±ŒbÇŽ:*Š…ÐØ0¢r¢XHl˜Ø ¢¢Äþï›{÷åá_ó½ÿûýÿ¼ß¯k=Ë5®÷½;¹g?ûì½ÏÉ~j¸ß·Ü¢ìܽäê—ŒGÉJºm#Pñ;Å<í°ëpŒÏGq0û¿ûÌî§r% ]-769K·ÏÚ}Ú×ìäÝþÅÚ"ƒË©”¿ ugRûÓ¥¼‹)JÚ¦ì„?Ê­›öÝûÁïñóx¡Þ…ì\f¶Í†giì·ø¯7éÈ€ß:œ³Ê ï=‚èöÊ=ˆ¸g˲‡•TÔãÂϾüœs\oÔëÀ:Ô\‡ÎİÃéñÒÄßµ‹Žœ×‘-žg_•©•Aç×úùê뾤o“ÉïÃW(ifì}/?¾>Äz×™×jtÃy„BýLØiMü7´°9K§ddÿÞò½Ž„Öþm˪f´ò-ËNÉ[‡.Kœ†žª¤ÓÖ¸~ Áç7îƒlÜKâç“c>Çò…Õå’ƒ¤?÷»GL¡¬®Ñmb—wõœk•o^ßâèIortoø…_\•¼./Æ;œ ËÖÕɼÿb}(\Ÿh°øå„[Pf U?Vq•Þ&kÚ¬™¼dHdÜy#bauù”tõÞYNßr¦QÜð½a<Ç} çt¢¿›xv‚o]èpøX§’ﯛn“¯…ý’§gPwϵúí”’)Û^ÿ €ý©ÊÔ¡ú‰Ó(ÎÆçàüxv^§ÛÓ²Ø|K=ão/ÞÚ>ÙœB­½-%÷2n阇MN-Ì ¾£^­]÷x(iqfñ>ï4%—gM§ø|äÖsø}Ø÷Õ´˜®¥Er®$/ô”sOy ]`÷û]ÇÊwˆ·…±ßÖôüõÉOk¤ $–vƒŒ\I½=i‘áæËÏÆüÓ\¯•ÝÏ›˃E`gÞ®CãûOJ¡IÕj× p½Cö›¸¼÷nXŸ–Sçô$^±¨3ñ’’Ž?_J$_ŠûîëøÞX½¹üMa,;~ ïø½tO¡W²ï½œ5õYÜôgçñ”ÕéLt91ç9ăʭŸ=hãËÏƸ€ñ ºÙÜÙ+k¼ãý͇X8Øéc]¶å’Ú°K|ºÎÙ|‡\ÛÝRÔ :íþøƒ[9‘^ÝG]Ý£VÒKbâ‚gúòñ ýë-œCjßnÖŒ-ëÈÁN÷i=·>:C-BÊUȺx‡t|ì.”˜AÙü°ö)¸Ëäh%½Þm“N뼿!Ð0/Æx ôƒh°3lÐÉ´;{Ïо¿Ž{qÊx‡Ôê6ø£1)ƒ>K³ÖÄ-ë@þ…g{-VÒŠ5æÕ y:ƒßÏÌëá\v <·ÉûJѶ#ÎÐ.¦€s—ÌoóeÞé j¡׋»‡öŒR(g0/Å÷…y=æX çuëÁΕø=ò#ßNÓ_[¼Ow~—¬”WöˆLËàôÞzÍÇž¹¢¤ì6îÿ]¾‚stQ? õˆ„ý š+I^ÒwUç=§iA§6e–Ü%¶'·oo™AûËL§ÞäèןÁNÂtû7ãü(ÆKôcä#Ö8\˜ç‰ÀÎêO¿ ;M” .»K./l˜ò(ƒ*óÖ@†Üì¯ú|åÈñJzrMøãªg|·¯aÃÖÁ¼¨P§[vLò05NS‡a»^´~r—œ©^U¼õMM‹ÑìË¢íÙ¢oBaQ¼Ä÷Ž~…ú&^ÀóâÏí³°½¦¡Ÿ>=YWëY½C›;¨l&•ÝsÎô"a¿„\t饤‰~êÕ÷~§Ø·ÄxÁòâ¿OšxÏeu5t ÷ÌV‰½ï‘ Ÿ™ ;“’Kë—…ÉæVyÄ©¡’ŽfÊ ©Ó¾ÛGp;«Ïô–÷W¡žl4Ø™¶MÝà€DCmÊ1JÈ÷Èϲ»/F4ʤƒTTν94éC’õ7}ì4ã÷èÓù¼çý¢òý~ëv/À«;Ai†[ÃÕucý‡rh›IÛyÇÕ_ŸÑ›ü|â•­Ý'ü,mJø Þ_Q—õMPOõáL|€çÿ2.½{Ù^”ªâ©pÿ);mÏy×LÚó]¦fx¥^Ä4¥ÚFIËF/«,ʯ£°€û-®êccÜ5ñA“+‰|wëY\\2MÜu%`Fûd¥CŠº¾{&õ:~3ûˆZBÌa2g%´rVD¯™þ|ßýç³ÿÇç_ÂýPvšöºÖ:¸Y2uÿ$=í×ã>YíîÔ7“.?Óf™Ñ…|þ´â'ËîJzzyòç |Aźë]ÔI(¦[ v êÌ:›sümÎD¬ûdó¡°á3eR«>ší“.·'ñŠV•Oˆ•tó¢ŸÂËw äÏ=ÐQßçq³ýò_øu3ñìxüÕ_>þ­ë=ºÁ†­÷IeÕí»3‡gÒ›Óí–œþÜüåÔéADž‚ÛŸy?Ã|õPolS9F๘N—ìôÿݧœªÌ)šýÐoö‚³÷IƒŠo½SGgÒÏù]}·;» wý—jÝ7*4bž.Ï#P'óbôc䓉/ð|æÔ¢Ö²“4ëüdå«û¤ZÒ¿ “2¹}£'QÊ¥výõF]ÛðA÷küõpÁs†b:ß`çíÅe†g');&>‹üÔ=ÜÞ#(“Î~¶Ë©ªr hvôÞf5É®tþ®ÞB=3ÔÅE?Æ]=ØÉi[q=è=Ò˜šÌ"[¼nTûg& ßÿ*¥[÷¡$:¢§uç Y¨ÜP‰ׯgóáó¼®ÎáÖu§s%ïçÆ4ìô9‰z7ÿ<˜øe‘ ‹/˜µ5“¶œüË×õ×¼‰TêOôó´UÒ»-v5ƒùõÇ8ñ]ØÁs«Îo2¿âÖ$*‰ η6‹TÙí¸^}8“<”ñd½\JBÙú&ñÆ´Ãù¼ù‚ýÔÓ))¯—€Éw§NswL¢ Ÿ^úI•E´n7Ë|Kˤ9t±<Œ®Ÿ¯6íµ‚žm::µ‰:€ÿüÈVŸü–›bÄp/K¯W/\Øqî[öÖ­CÇiªõÐóôZIÏ~Y÷æýL.žx‘;k‡U›á¤¤ì<úŠqÅÜ¿°ßˆõ¼p_—ƒ¿ymê9Ù§+=‹Zd‘›­;Ÿ%/2éýÌïÊ÷z’_G;O좤Uåê8ï5ßéH³ÏÃû±‰ðÜ••ÿš^n¦šîY²3ãbÝdpÚÑC« 2iä¤É?%¼u&¦4ö­½öuI àã òüY‡%G»ïzÆëca>*\ Ø ‹zåKÆ1Êt«Öv~@©Ú¡— Áo-}÷Ù¾6QQTû¨ µm%eòÀs_Õ+ØsŒ£å^=¾9aÔ’_©‰eGc&E}y‘ÃʨÛÙ š!ºürl^ÐwþÄê.ÞáóÛb:’gr%'ijôT‰t÷ô5Gêÿñ€T|ê4o#<ð%QDׯÖä·›kž(h½Ï_~KœÂç·Øÿ1?·@ý3aÝ&;Ã=VWL¤½û̲¹íé¥íú13éæÊW÷~Þ””¤¿Ñ¾B¾Ë×qÿ@eí›éÆ€ò¸µ7éx”vk]±êÍädþÎ/_~ͤ­‡¶]|»¹˜#oú⮂®Zó¢Úù} ûe¨ U’” žÿå^úúmsÐg¢–ë> e¿¿ÑUÐÒ2ß^=Jñ dš,®üò=08ªÐ%ˆ¢ž1®7®Û‡Õóç‰B½ 9Øé±&/»æãÃtÝ I¶3ËéÉ—µ=fIªjé™7œö÷ Ë*$ïu@A½¯fÕõÈë\ã¹=ömð¼ŠåI£bú7Ñ`gbÂÎ[cÝÓO#Zõ|SKOFlv½]§ž–þTy¡ÓÊæäô ]¢ø°‚¦¼žSñÖÖ>Þb¿Ï-°†û,¾W?ÀN9F¾#î|çë† Íõdî×Ùùwšiéœ ®“ŸQOÒÛaÈåné pÉcðÿïú]¸þ,O²xž+éÁÎÚ±·ò¼Ë¢ûÊæ…uÖß7{Ê\i§¥áŸNàIêv64sy§ !&÷õ§¬NÌ%7Ô‹é¦.[ù]Aq¿²HÉ•ìËñló­ÑAÚ¨LÌ¥­=à=U^áÚUKß¾qhôsª;éÙ¦cBM¨7&$'9øóû8¾ZZ/»ÙÑŠ{ìè±^LÄTУu˜ !ü{Ãû!˜a¾Íê׉[G°}=ØÍ›:hÇã½t|ý×;Ö“³½âzôÖr÷Þ%¿Ûž6uàEu}–£®w;„b½Ž~‹ûËÿûn‡ØõúÂö‰ÏæJZœò›óróªÈª§s8­'¡+Z¶Y߇õӤ׿*NS‹â?Æ]ü>gPo Ïÿ…º€"°cýlÔÓà»éÜsõÚ^Ó“M²qºZš²Å/·««+±y Spº¦!|žŠ|Å{7l^÷Œ¿!\ ØÙ5æÏõ;†Æp÷ ôdÞû&='Jµ´pá¥à¢ždƒzUÀ¹s :¿‚ã$½<”?7ÁuBý3¼wƒ}áúÈÀÎXÏ%Kçî¢7V/òô©žt|§Ò$-õÕìSköh*È4}\Nµ˜ìP¾ïßÏ9°ÏU\‰ëƒ÷åÓ³?)vR+û>G¿Ö“uìU'C´ôÃõ$ïÂÛƒIÁ´çû¾Å+èï'ƒ×Ù–åï¯à:a?ëV¼ß%ÔsŠ>‹÷Õ¢iVà» ë>ëIõ\߯¹Kµ46¾ÌžÏ߆ë‡ò캮 –Í;-‹ïÌóó ös?äõ¢ŠÃÃóÛ%µjÞqhÕ0¸’¥ŒVXGݦ¥“¦¨GK“‡£S¬WB\uÅõѧ‡üóQ—ìØêæâÕê×nmgÄ”9YÁ©Ø=5=<X„ÇÃ'¹Q“ m5Yµ!jJ³-µëÞ·J“ ¾DœÔæ³~Ž’Ö™ëïÈ÷Ap?Á~æo¬}‡bõ–Ź\ÉŽýyÒéë¶Ðú–Iïêˆ&wÌIûcZŽç„\ÞTGf¿MI/Ûëརº÷Ô‹ÃuÇúQØ9¦†}õˉÝÛÔ@vÜêo»ð”–Çm«{ønkbWëÛL÷?•Ôä¯)ö™Ño‡¿îõt Ë+$ðÜûëߌ¬t¶õaÀ ÖBÜGì÷Õhé»U•²+¹ŠÈ0ÛQV^PÇÛõÎ ¢Èsô'<'­U ¾TÞëût2°óë«oUZGûÕ‹ ÚÁ@Zݘº ÿ-§«×”\fä±aÙ?O;åêÇ`ŠüÂ} ûçXÏ£_ uå`gKcfVÓ/>.“f¸È_iÒ&ç´t¨¹|ÜÏ‘ôs)¨]õ•‚Îþ°­yû¬PŠû-Þ»Ä| ëëbu<Ã}¿X?ìLžž3y^ô*2¢fè ±Ó¦×YqWKÛùÙ—K)çNn¤fרUIYýð¢:ó><ǺïÙøq>Wòüœ×€#qþôÂŽØéÒ!òìÒWžhiÍ£«ý*z’÷Ö†¨I•4àQí·A=‚ø¾¾6ÏÁ뜙øÏµÎ®7Ù§Ë zìu ‡úà dã‘®sòµôþ™sz£žäUCFRI{{sá‚@>ÿÂþömØÏÿÍ uõ„ý ØmQoOßo¿æ–@õ‘â~¥×6¿ZÚoÇ”/3Wu'qmª¤;¹ÎêE}.|êç±ß‡Õƒ–ÁsÇ|Ñ5%g’Ч:-¶È ïÛe”Õq:Á] {n¦¤cö­Ù˜v7€â}¬§1?Á÷uP±ýì$|í{îëò´³qi9±jY^üÙJG“·>qÑ™0*i5(iÔ VÇŽÔ ¤X/b¼Åó>¬·Ø~cób÷D¢ÁÎäÓgí{n[J{÷œðÒÏ@&N_™`­£ì:5" é¸Ëã•táÇ¿Ž©DÍëEì?°ëÓ´XßAÏwÖÝ úy5§7o »—ß’\EGÙ|¦ Ý]ïýìß ÎîY¹±\×`~ð9l½žÆßwÃ:K˜oëÁN·ñ^{¯?^K<ºÜ¾²b®ø&úýêO:Ú¤þ×¥òôUl»FO×*©µ4°ÖòÁû 诸o°ñï9¯+¼fq!Wbß×>):yq÷{¿}¡8oÒ .k©£Ã ›D7¼Ô‘Æ?ž0s>ØYEîœ`ò]?óÌOQïœÍ“Ùõ£g N-ÖGÃÆÕ»w/1…®îmb¡£î¼TGºÐœ¬”f཭Ki±ÍÓ±¨žÇø]\gRÇÞ†ç>mœ\ãðØ¿Èx¦m¹Ô@Ú¼8+q2jéëÓç½¾ntýâz6=¬¤ÃöF-ŒŽ áû@¸oà=GìG˜xÏeˆhr/³ööMË äÌ»+»¼†¼Ýt¯¯3Hb—×LSÒˆjgbV6 åýµøç}ÀÝËçûÂsV9Ø©V—Ùwc‡µå `'dþüÌCϵÔÁ¡UÙ„Êmès{w§Ç:%m´¾0ÀÙ#”ß·ñý ïðž4Æ=¼aâØaº­•Ç2†ò˪ØwMži)›¿”!Û™AIc˜c¼þœ×óÔÑ4ñž«>P.{…x™ e”|þ§7“k†¿ÔR©MÍ¥C HÕ‰]~|¦¤ž^žu ù.¯Ír¬¿ß>>Ÿë»à÷ÌKM|;5›&ž°ÌÙKÞ¦½KÜ v|ÆÝê]­>fà™ËY¸+Übùg%×÷ æïâûÂuΛ2eÉþoøó„b÷~Ss%õ–uÎz|,–l²ñ¾–ê“~ë6[B™.…*ŽÎˆx½Å;$”Ï£ÑöO±ß˜7*¡Æ¦G͊뇃7kl “%nÏxKý äà•9§fj©fOTÄhA­Ýu—ï£a2 C¥¡ï}à{Á:ë<öƒ$`ÇÒrv‰¤[Ÿw²f@w7^u òÜkìZn;ÒŒžt|éÈ-qtŢĿú„|÷}°‚ºî%õOd`§§é¢æ1Ò}Ö²ÁNýê…ÒéXÔ¤~6 µ|WÅ}WϚ߯`ëÁþ^®0^ÊÁŽGΔg!ËÕÄõ\“¶Ó ä‘uÕ¸¥×´4pêÎJÇ +РÊo¯‰£19óWŸ^Ä÷µ±O‹ç¢¿Õ3x~šõ¯;‹¿ƒÑÖÇŸ—'ãîè:Å@ÖØw>žEK§v€¤.¦.ÕöRÞåT]ycϼÞEñýßÞÿE¿.–w˜læ>‰¬f®gÿf ´Þð}Ç/ié…ØF² ý[Ò}Ù÷¥^¶ñ|žq ãÆe<Çß…ë[=Øñé¾îìÚÀ¤ÞÞÄ[ÝÆÂ~V»|ôÖ ZÚÞkù´2i‰å‡º,ˆ§Ä~ÒÙ ÑEqÔòùÖ£‡oõîÍse ”žØÐÌõR<-×ý¹á’{ïØh.‰}z¥K·.®ûÃ&ÞÀóˇ¤ÅÔŠÔi-¹û;Ô%aaÁ©Ç´Ôe™å«Š­Ü¹¼1žn²ëÿnâ×¢çãûÂópŒŸC;Õ™w·=Áû·&Þ€Qãšy2:™˜ôšÃúOŸXåJœ–N_ä\×}dojüpfÑõxÚNvqX«¹A|þ…û2æx.ÎÖ÷í‹Ýg—íH'çI”¬;ï®ÄÖY>i‡–gl²ð®ÒŸnk-:b—Oï?ßÜêe0¯Žù ž'£Ž<»O8ñ}poÀξ ÝE›\4$¼|§Á‰ž⡺œØ&RK÷w¯Ú  Ï66|hŸOo‡©ÎÍØÂóßêGãùÛv(§£ÁNd_æWc·ëzCwˆŸs&L8.×r¿ëëO?»liÒ’ÆÓ“M&ý0/„¯1Þ`Ýnâ<Ïzõ—VŸ&5CžÚUw3õ¤¥ó3^Ü«z³7½árëÕ™ÌxzúbGËgß÷5¸/c\)ö;E°óäúJUlÄi2ïÏ˺u0¿þh{mý-í½bèÕ+/Ý©)}ʉ§Ï;Æ~ÉÌóï²ýå|þÜ〉—ðw>§Iðó]Åb±iëvÂi¬–6̛뿹 ¡Ìmù–wâ©jD›úÛÞñûöð\&2¯Ý®¯gßðu"æ&~€‡Û=?çu†0¿ê˜P×@ÞåWÎÝà­¥®5.÷©=¿zä³*>žf¼|Ѳê¢:ý÷ö^èW¶ç²ý 3d‡t_Z¡µtë×¢¥GC;’+šQ8í†ïÒxZ›Ù¶ñy6öØ|þšÛ¸v·Û4‰+ÃßC)ö»+°3b÷Ò™ò)dªáPå­e äíÛ ïOÒÒáŸlmíhhûû*ûâiaN•Â5£‚ø:íá}ö{ðû•p½å`§^»Ã×&¬O!2‚»^§'ú¼AMË{AÞ=óD¶¬a:N Çß_õ‰ ün¿Â|ùu¨ðw©Ñ`çÔ&1>Kªßó¼Ý8WOÚ»[Þ¢ì#ÝÆOêHÇ;·¼»äd ¿øíñ«¬~Ýñ9è¿Ø7Ãx[ìÜì$í¹cåYò©óݘN÷ôdÕÎÁw»¸C|Ÿ3,Ì`Ûƒj{®?òÏÑ…û» ìÜP´Üñ$ü3þ}(ì—c½Šëhâ ØqÍýãÑ´kçIë©a£ž4t˜3}þÛL::§Oyå< U,uƒ "–•O¸¶ýRQœÄû7#Jƒ³GŒ8}j)ú±b A¤™~¬-7‡çy–4‡Ý|ž§D0‹çy¢>OŽÙ¬;œ=,œu'Ô¤ø;³‡…³î¸Ywž‚Ywþ‚YwÑ!KÒ¤ðüšn;£Ç¨çtz˜YŸ¶fú±j³¹ÃB=íÒf±£¦v,§©]ÒÜaŸRæ3¾Êüùÿ9þ»bá¿1büû'žÒâ^i1¯´xÇøII3=œæbIÚ;Œæ"je›k.Ê9­l/ÁŒõ@~ 3Ö…ZÙù/NsQÄÍXÏHájbpæH€±ZÉú²ÌLO@c6Ÿ›±-ÐÞ)IsQ¨»ã/Ð[DÝÔɶ5›‡ÇÌ-ÎÃC½‰¿;³˜™‡—Óœ…gÃÍÁsáåHçæàý+}EU;Vuv<Ítfs³ŒE@Î@~GV»$]E ¬ )a–ñ9Æ?ò»z~gË}V³&à±pÊ0nö§c)³?£:;æ3ÙQo‘ÑÙ‰ päT€œYÈx™éì0šÚbNg‡™*GO8‚³GqïH/EgÖHe¦3+âæµãÐ’æµ›ÏõÌkÇ9 ¨µÃhk{‰Ô‘}Ñ,c/ü@d¦-ñwfKt€ˆ›eœÃi0Æl8m@Âél[µ,Y[ÂëohK¤r3ÜÆNo‡™*2Ó™Õ˜Í1êm—6Ûçzª8Ýí’æû—2ǘñQæÏÿ„˜ø;þ;ò;a ü¯Ä¿ÿÔØ'âþ¶‡óèp¼XÎùd¥èé0:‹¨¯m®³ÁékKóÛ#Ææ· õµ)§³(ææ·2pèT€#8uçØæ:³23Y#7»=V ¥SšÆ¢PK'L ±ˆZ:¨­-‚È9vìÜc[xyr@ލH›âïÌ<‘"¸™Ç*€-J©¢V@,€àÂiRºBM 5À¦m‘6… §£zŠêv¬Ž6êæx™iÌ g"‹¤‘cÇ" í’´m¼a=À³„ÙÈ.¥ÌFþ‘ïýÈ÷äÿïcÞNn™Ï¢gÞ98¤ ` N)è¸YïÑ+pP€ áf½£ù¬wÔUdf½GqNìHˆÁ™#ù©™&£¯íø3;óqr@:Àœ=`ïЕ¢/ë$ˆ6Ó—ssßs^¥Ì}Wl(r@ÀK0÷=  èó0zÛR ‘ "EòRü@l¦]¡ˆ€`€|€ˆ¦ˆl‘#@Æém‹xmEÀ–ÓÜÖ<XÝm›R´+¤C»‚ÑìqáôÝm)X›éË2ºŽ@è(ŽÔBÍíÒfÅ›kn‡ôO ¿ `ËéVàw€ù%™œ°¾Éü)-&þwÆCX¶ÿ˜<ïG ü>þŸÄ?fíb6àxa=ÀPU–ÕP,I³‡ÑPDmms ÅHN[›ÑPL8‚ÃFqN+¨"3]mƉeœ~"£×Å9´ àŽ]Š–¬™Ž,ãðRN;µzJÓNjõÈÚ‰¨ÕƒšÚ@’|€Ä95@$‚䋊ë\È9/ ‘ "EòR ” REò^@.5@ èž@´X€ -  H8½ †x¨y!2Ó¼ÐlÛi_H8=mÔMÔ´cõ´Q§Gj¦%kÈ€¸©GF¯Œ#0ji›k&Š€Ðr@À ˆ °r‡ôœ.F,ÀˆÐ< «‹ñ#ÿû‘ÿÉ-þùŸ g+‡y§àj€œ2 HÀ9c6à a=À5V õ£HÀic6œ#£õã °'öèŽàÌ‘#@f¦÷Ãèj»€ƒG¬ÀÉý:€œ=`З¢#+ÄšéÈ:!"ùL\b¨" G  ’h" J  ¨¶@9 G ûÃHH8‘"F€LÿpREòR — ‚EŒ-àd‹âçÃik;ñ"úŒj€ˆÓÖÎhkÛ!ý:€#3Š#'£]– p’Fòž@VÀëÐ$œf#£¯-§ÍtdÓ.@èh€•™¶v,À Ç H€è±%hkË9/ ¿Z ã“ð‚@ 0è"~ÄÄ1Ñ⟙uPlÁ倀8¥Z å¨HÀAc6à¤aœ–#jk›k9FqÎËh9¦\À‰£VLhbpè3mm+pnNËÑœ<`ŽîÐ$àð±¥èÕZüÍôj­€ 2NËQ¤ˆøßh9zQÔ%B å¨Ø6,ÒÖ"F€ˆ¤ˆEðo#À H¥ˆ€X€|€¦ˆd‘#@dK8á¢F€ˆ§ˆ|r@À H¨Øå€€'2`Åh8R9]´H€‘‰@ÒT€ˆÐ<9mmÔrLmÇjkGŒ™@Ï6Š#³ àÂh¦¬ºÚæ:ŽŒ®¶ÈÈHô*€-_ÈxBP• ¦"EwPÿ§ÆÄñð?#zrÏÊgÞ8£ ‡Œä<Á1U[pN9 àNªØ€£†ôOpXÀ–ÓƒÔ$ŒN7À8  ¸€#GqÎìPDàÔœn·œ;`Ð<ÁÑU[pv9 §m[O €J mëDˆ™¸„ÐÄ@ŠH€ r¤Ä@H€ ¢¨" K à ¤QqúÝ>€t€ (Š#‘ àdŠ2 U*ÀˆÅ‘Ëp’E¬€hþœ~· .J  ©ˆ9ýî|~·ˆÐ\€Ñ+ ¥ àäŒ^@R5@D èžœn$CZ@:ÀÅL×V‘c6‹t¾=!ª6@î0€à $W• õm®ó(òGòR€A$aýšùSR,üWâ`i1м'øïŒwëþNœÆ8&¾ýb×J‹iË0Žýwöøþ+±‹yOjf=Áq"ù)8 8P@ðGRlÁ™ä}…"Mnp®0€ž9ßàô¸ý:€œ-–¹Çç(Ðàö§Ó$àx±p¾0€à N¨*E·ÖâR˜™n­œT Nšþ/öμ©cÝÚ¦ `B¦ZtÑE7Í#ªE]tA(†Pbºè¢‹nºB †¸ˆ.ê0X‹& ÓD—!€L(¢ÿk{ïÙ– 9÷ÜzîýáyÞ'åäìQùÖ·gÏŒÖB¡šŪ. BÑšýÈ ”èE¦R|ζ …&ÜÈÕÀd(ì0¡¸UÀ ¤èAzàjô! ¡ðÄâV!¨È p„aˆC\Üùn¯"±„&ˆ%˜;s Á8kñyÚ¾µ6Ÿ¥&ˆ'Ø#³Ö¼!$ °9|=r´åè5áÀB 2ˆMÜÜžúŒÂÓPB€& uÀT„Ï¥ýö,úmî¥õúט{©„ÿÖÍ}&(H ¡(À (Qœ& Aê€ ¨P¨&à‡bÕ'P¢hM@‚  à2¸ŠX œ@Žb6o´˜…­ò¸(p#ðC‘k(Qì& AÁë€ ¨Pøf Eñë(!@:àr"LE0°Ä&$X B Ä Ì@ Ñè(!“Ë­v ‡ À[‚ì@Q…  V€7D¦v ‡ØÂ/*dsË!<ð†ø4ÀdUø|nw•ô|n)©N ‡0Ã/Ä©v ‡H᪀H!X-p%„¼!^ °9D|!äPà Úü¤çx+!nðƒÀµÀ ”º©QÖ,o=ps¿Eƒø-@† nî·hh C3#|}sþ™ù×?3ïÊn?ö?»ïý³ýî_möÏÌ¿þæ^Üû3sߊFÜ@âñCñh(QD& A!)ЧŒÀ¥NnO…å‹Â  @¹ó$(2+@¡¹€ ½É})8€Åg~(@-p% Ñ$(FpŠÒ üЗ´À ”(R÷{0ª …ªn FÁZ€ EÜ@âµú‘ ¨PÈfnßÅ€b6oô ``(nðæžÿ€ÈЃ„}0°‚¾x£ø5ÀäÜï!€7„ ¬ ‚Ð7PCf …8ôÜYnŸ±ƒˆÄ¼! wbqq{ŒÁ€7D£v ‡xÂ/ @!zŒ8€=Æü 0-€È¡™bÓ7PAtf …ðôÀ Ô ™¤ŸÉü\Èö عvF˜ý9 ü½!>EÞæÊõói¬èg0»A£i—:Äþ³gÕ\ƒŸÙ(óÓí_aû‘ÜkëPæQµgÓ7ŸGR.•󆹒fŒ6‘zúè˜1ÎÚ<3ºŸ™KVJúå˜7ÅA>wï½L±Ñ×ÓÆ¶®=:ìeBƒ†Q´]‹r£ïwž úÄ1_eæÃÉ|y=ý[¸þûl²Ç¯ny pⲕ Ûh½¹“* ,A{7îsme½®¾[!Á?$K^%óo`¹OÌw*íƒJH‘—ÑU¿ÓÄBò®[Ö(Qå œ]ÒCü×i• ë¯5¹E£6<ÖM[7^ôÅdy{,¿Š÷op‹~Äž9nŒSwô—¸v«,DÕØKE…ƒ,“¬tßFOÿ^¥.•iÕ^¡ë»£èíF««ú{ºïóÍa~ļßà_bN€§–ãXóý6§æ] ùذV§iõ¤Þ‰ÕïwltÛ±™ >½«MÓl›ŸDÑ>ç ì·'Ý?‹ù¹°zðô5T㺼¿ÿy2¿Lí)cÊ:È[FuN¶ÑøÓKÛ~mDÝ»«Öó½Ey_ÊñY|[˜ï&óµbùÜõµ¸~äȶAÂÎ.Mö‡B‘¸øü¥Ë6zt6¥÷A9½ß¥êÝ|'¢è“§ï6<Ü7Ž2¿*–ï²£®ìå¾J•2äÕpÝAËÊÐÃ)çÉq}ÁBƒ>Þ!«ŽÏí¾ñ¼ÞuÝj”§å\Áý“¢¨áÌ´É9ÞŒëSÌ¡Ló¡Oó4™¡g¾„ã,½âÛÃ]7ŽDWêÜ»cÒ²wÊ¥àã'mô°×Œ¤½}[ScÃÚ*‰¦–irCÝ!Y|¹™ß©§ï¦×]¼g`Ê´Ð8Ra`¾1QwçnX怚z¯ [ÑiÆ¿Ñ4éÎÚ™í&~%?æ¶èïÆ|=}L¼.¦ÈK8fê}2Žløsë®sÔûï±ÑÖ9VÔ#iImwUoõ¦©¯Úõž0)‹Î˜Ï4ÿ½¼ýL<}¦%G3]5öjþ ä9g·ÔíIذð¶ßltËë;Úý(§Eï­Ü8š.=¼fÀÆ¥³øWfÎwŸ0àçuá·«fЛãtÜZZÓÿùaç‰SÅ*Ý!KÚ÷H¾·ÁFÊ~!IuhâÍ–§ß~Ž¢Å5kW>¬Þ—XÞ ï—år3ë–+¦\?M_/~=Þô/üñ6Yñ×gÒ)ÌF5Íuß\ˆ¦ù½%G‰~’ìófß/ïO÷WàáéËß?-Ú@ø¾ø2-®o.Ggè Ä“×öõ·Ý&§íÝTe½~siçrDW«HkÅ­(ÚŸªŽê›þú™ÿë{¬n'‡&Îzè]%ƒã,ë×pçñžñÄ:³Ð€cn“ÑœL7ÙèÛ…á5çEÔ%óÚÐÿ-ú÷Æ=S‡„¤ûõ2ß5æ“Äëä±».ÍàcnÆ8|¹¤½xÒþ²ã–ß&Ênlï·ÙèÀ"º¥Á©Ä\žsž‹s˜ŸÛþÖiFÓbîË c¹}ž¾bŒ³yÎð«bâIü÷%‡~v›2æô&þhoÄž y“‰)ò\ÌÇxbPvöŸEn“©kÎMni£Ý]q5¯Dž ¨üzèˆhºì»yÍ×· ÉÒW˜¿#Ë}IÓ®›r¥êû–uˆžk£?Ü&fkù6‰¸îó²Îö¥cþ6xS4ÝqÚÞB?g|ÿXö}°þ˜¦\—×aIÚqã‡#Ž[ä·ðØÂç ïfeç}œî[—Î *q6ZÌc¯“õ9±ŽÒr«Þˆ}Õ[š.0ζCR'Úòmù~Ü{‹lxãŠÝn£]f¼­;È §MÛUlú[R4•Õx²êæ ,÷9ÞGÙøyË~u³¶¯¹tÛÒË«ˆõ–¦ŒÃçõ$Ó?>¼ìžw‹tûü¥íRè[ð‘§Aq?µ=v)švétàýË1—õ[Þ§îAà_#.:þ†÷y7àºoöÙTû"IŽÍ?£äà[$$µßÜ‹zå}f[ÐKF¿‰‘'¢é«p7-_u‚˜{Âêõõȋϼrò÷ë/]“’?t/Z5ƒîÌG]6d¨rôEÂçpß"Un´/Õ¡æÙk¿Ð·1uŒà×£éìnþŠÛ&ˆy}ðE¿-þû­•!_Éq&\¶i~øE¢^UÿIÙ·ˆ»lÜøÑslô±´‹¼ÂÒjtö].ø"šV˜Ê9³§ûS±ïõ[æcÎç+Õ&Ìw9MÖ¹×§“3ÿºw‘Èm¤Þ_7Éãü;¾™…~›V{NÎ#g6Ö¦\šmh¿ âü€Ý§˜ó=®¥ìªÎÑ¡I~tîÕÜŠeøûÆÙYçl©Ü¥Éå†#Jü˜p“ÔÌ?nã@¼–ŸÐkªÝç»QÑ4~ꦧߤëŽùŸñóÍç\ÚbñîMˆg~€×ÿ²oêK£2‘üõð;{Ю›¤ØîGÒ7ómtýú@G%EmÒÿôo‡Cñ>ÐtW_—„d¹?±úe>x­®mºem­Œ9ßGþfÊÛØ©‰7í5Ò©7Iô_qJÍUåo˜ûK=Òqþýw߇GSŸ1ûÖ·/ú©²:`ó3öy•ÍYð·v 5Eÿ°4`œ«CÎÝhH6š9tgÛ›äîí§}j¯´Ñ\GüÛO ©KüµW+uzM—øLzqe¾¹ó£î‹yì~ëYgŒ3mæœ7Oî' 97ÉäÏÌ/Ö°œ»J¤DLÞI‹éÙ^M…\'ú¨³¾ÊüðøûxªèSèé§iÆ8«–ßú®]~+yýæñ¶²÷o;½Ç©¥km4.zJÉ76zå·+©n¤B6|—\:½eö=fóvæ—¦\?ªæ®Ü1¬„Kݶ÷ùX1LrŸ—ߪí;רM*ë4:X×HÿÁæ§/ìûg9,O¸Ù–µþ¦¸òï—Rä[Z¿ènnf%žÚávϽAZ¼‹ß^}©ÞÔ\zëU3}ð-fÔFÊûõ…P–WÉ|àù¾›øÓ¥{T馄å¤é×Ïõ|òç]¬Ä9› @¸AªÕÝùúð\mg³ocg Úä³OîuŒ”{zë’%‡ùÚóút ù'uË…HÓ ÆÙ¾ôƒ×j+1êÔ opƒºròb£ÊªÕÖ%õmE7™ì_ÍHÛOÌQ«î¨ô¼ÌÌ9Õ,Ï…÷Û®™Ñã¬íó¢aò8+)šóHÌŠ¼7ÈÓå²ïUãmôÙ¦a³&-hEç|ÈQè{‰‘ŽZ{¡ÍãDUÞÿ·™0{ÅËçå_ ynùï£:a>õizÁ8Ku²VZ`%UÊ×ôZz9™¼ß×{ƒW°îÙ¸˜>ËׂŽëÍ%¢iüÕ ¡-KLÌ’#ÏúåÍ/‡níò^ô%Íð\‚qøù¢•|\T¿ßƒ-ÉäîO÷‚ž°Q©IÕeΈ:"íeiȵ¦AŽMÈòœÆæ,7¹vÝ5ÿAÒ ó!3ÆáÒ°•;­dm﹃«'“C+o-Ó×&øÏ¦ø.­—‹éâ³cóW+:!‹Ï1ósyø¡eì$W ªÔÅ# Ê2ø;0Nî¨jÃæ¶ÕÄž>ÖN&Ÿõc}_ÄuîÖÔºdõÈ­Ëb¿7Òˆ¥{‹ ˜î·ÈžØý%Í_ør• ~½^—Sä•OÏßÕ+ÎJ­úPzQÎd²xPXÿæClôi™ëýê'6'Oâ›U˜ö"šòù±!âý’Ý'Y?fóFÏû—×/T`q—¤d+g>¼Ëÿêu²1çúÍ­ÇÚ„² Ò¾o¿ûu¯GÓg¾_Œ;ƒÒ}bYd¹ÏìþÂr™ùþ%äcœÑcWÈû§•—:eçub±pâži6ZqijQ3ö)Hˆl£×™—Ñôà—¶=ýC2å¦ú‹¹£,§†Ï¨Lò_ÕyùháùãÜZW÷FÑ÷VR°ãõ”¼¯“G‹6%Æ-À8mæz[Z!å}£Ÿ¢Ž›}Ƨ\×,¾úìý°ÜÀìrï´§+÷XþÙJp³‹æ×ÉØ±£×ý¹ÌFS¾úãžúäQ™N¾mh¤í¸§ß,õÅòõx]>Í6ÇÏ€q Źúeòí1m ^'' >2_a£=óK0þä¶X÷Îig¤|Ÿ/æ ±yFæ<Òlóõ0Nr÷½–°’ÁÃóoN²ÛÉ¥Àž®˜ÿUíóã¥üÃËÑ%NÖÖH•ø°5kÇSv?aón¾/TÌð|íÀuû´[»J‰ïÃë\Ý`çV;y]*hpú}lçÀ°±öÚtÑù’êGMŒ´ª%±MÔÆñYæ̧åé±úbóÚ4\I‘?Z¹çY%·• i¬>:ÄNú·È¡Å¼r»³}7w­GG4ùýy\M#ý¸ìîÞ­gÇg™W²R–¯ô×Ä⟯¨a½C‚q¸I†þ•®ºðÏ9Õí¤pr±‰]q©ØapŸ‡ÝjÓãäö=+é¾aCôǼÓ×;Øëe9w̯›åtxæÊ1ÎÄ&âæ¼²’Ž#>ÕÈáN"γwÕÚhß¿S¶6)E97Ú•èû8›mEºîÙý…å²|Àì|vÕ§Üâz©]VÒå•9V}6‰¼ûgÚ(ÿÜó¹DN]T"]/™ë˜ù3ÝxúÀkqý/ ‹1ò’½;nN_¶<‰”Ûy½iAÌ[ ŽüÙìS›¼®¶¹PâFa}1½~Ù8,·ˆÝ—™Nø<Æïx`œµs­I}a%eîzI•Dêž»·2ßÿÅÄ3ôä’2››VùéçÅ*ŸLG™Ÿ.Ë•aëCžßƒ×MRެµ ¯¿û²ó÷>—O"×O7s\€Îý&vÍ}+W#‚AëÖ,e¤OVŽ[2d¹&KÞ{®f}žåÎeÈùÆ8~Äú¤`>ÁÅÍ<³‘S ø~\m£ÓKæÍ5!w}²kÕ¨‰*éƒvÍ|ZL+ö-özÙçÏüß—q·›nµ2ä{]M‘oŒèRè’ÓJ¼SªŒ×™l¤{Í-‹­ëm”÷-—‘áêC;b™Ó°¨äá°±bÎ [Ïb¾ïü÷ò:Ð=Žs’n$ô“Ÿx\M?ë.øåà©ð´è±r›m49·ßZr§y“ßçyÞ¦F:+ÚiûeІ²¼özY÷É•ªïØ÷80WÚ]°¼Æ4`Q=Ï*´Šw™;·ë£_Ž ¨:NÔ ¿*%êžõeþúå2èÞ€qZ>Š3$;¬di‹•½Oœ¹F.­/ôs ÔAÅó!Óý Ö¡£%ýGõ©b¤q 'í:#}]›½¦K–§Êòõ<ó-ͧþUi…õ×ðÜrb¾Ã´ä¹~iÕÒÁ¨ë·ß¯óiÿ²åیԿ-—Ä—~?fóXv}¾¹ÄuO:0Îû”ùšx+yË=u¹FÞûõjî_õº¶û¹¢í¦ÚmØö$šŽµ9¦÷¨9!Ëz1Ëïõ/СHßGïOr/wcÕ ë¸^×RäŠÎ+ZÆ$X‰T;xÈùR×ÈʼnË•B?à×irÑèrÛs­4FÓ_Ròä?žžßËúË!š±«çÀþ3ô ®ŸÿÍÀÇ¿ãúó.EŒòy•Lÿùç#^Ëm´V«°?SêU';Û­>±sžó_Ô[Ÿzh|–¼26¯d>ñl½Ê3wFŽq7{qnöi+Ñ%½ö™tæ*¡Šà Gð½ä˜}Û¼pi ©ovñVT4=Ú¡ãì†;Òs•Yn3ïÛ/#¼ß9¯G5®{§è©/=÷YIì5KÔ%W‰¹áˇ{ ÇމߑcKƒHçÕs[%Ÿ‹¦-NùrI#ÎóØzÛw`}™_¯¬—aA‹qV}9çÄv+iÕÕ§âªnW çn_$ÂF/p—.Ó‘°õ¶êm·<ý IÏŸf:d÷y^§U2äØpýÉ+'$<Øh%«Ouyù¤ÔUrþFßÙè:yܽ?:Šë÷$eãŠЈõÄæ lŸåƱ¼ –ß“¦Œsâ2²Bë­dÁü³á¿<»B¶÷+A]q6ÚøcòVYB[R)¤ú‹=yô÷MµÞ ˜–î§ÏžCXßbŸÛOôÌÁs`œS‰ëkÖZI“ÑK½ÏºBúiG)VÚl´Xë§×çnE$[|·óâJ»’±UÓó­YËý`ù2l~ï9_õ²¡¯D]Ý/Ym%|NÖròË“ø°[6JV©"wí Ýïï܃y ËÏb:fó#–·Èö#øç;þ9B‚ëqn¬W`‘•|Ô~ùïS®|Ža©ƒïÚèíª¯VIʑˋ]¾[ÖHWú§V³iÒë–é}^,Ç/C8®¿ÖØà\§P+‰ë~*|G§+¤åÓ¦óß³ ó…ä“›–T=c*i¤5Ó64b> «/¦;¶¾Í »¯¤éãÌܶoPÇ¡Vòfß³õË]!¤ÖÈ{ ïclpEï}ïrÑÝ?>û €‘Ú?Ì­fЈ9lÏtÈë§&aûiúÀõ=TtPZIZlʻˤë˜#—á}°~7x„#¨þŸÑ´ø—Àû§g¥×/Ó!Ëf>ýüsV• 9fŒS§÷Í™C[Éû‹>[ã/“µ /¹ØèÔJ­'Î--#£\ë]ƒë‡§ÆoX_#ŽÏtÂïÿ9…~•*¼¯ÊdwÇM7_(ùu3Æú¥i¿ V²iÇ»Ÿûl¼L–$F-éõÄFÏ,;8±Rùf¤àvIè´©ÑtTÊ¥î×§YÿeÏÃì}eÌáóß§éÎ9¯g´ò9#.“qi ¼6š4ѸϕZ‘ÇáÛïMý_'U«n+ööW–oÀôŸ¦‹¤ùæï†çIM$;}-—5½LêoŒ$ûb£uGžl8)ˆ”çnc£©:”»Í’«Éßo¯²¿²}dþ~Éç°I0Ž…Û&¡‰$Õµ%ׅ—IÅ<³Ö´ôI¢¡Gt/b"‚È n»¤ÎŒ¦S|4÷#æe^_矇ž:— ë/üç$Ç8:9´uI")º[vÈ÷Á%’wóSÝèóà eJ¿¯hºâûF‡¶”%Î#™™ÎÙú4Ë õœ©1Ϋù,‘mI`ü€C›]"…o©^(›D÷M<ڬ벖d°mño›‚£éœ›­Ã¶õI?îN½Üq°DüÞÙ<…í¿3Ý{Þ×µgvìá_ ¼¿H‚šµÚÞDw‰lèüý̦U“„þÑŒp»)«FFÓÎöcgÖ)î«°Ï‹­ðùF¥3Ì» ÜõsÉBÉÑ{­™Þë¹=ËÕë°,‰ž®õú¯ßËÕ'Ž#o5çÇá~•Và¿dÉ™a9bl~Ïžã3¬cœ­{ZäÚ;ê"9ønæµå/‘»®Ùº«õ’„ç¹Jdq·Èy½ñyU*5eñ‚·£Äœ$öy‰ëÑBŸáûK™ y6ŒózÎQÅoÕ1Θ9Écñ|4ø@ÝÔý ’(?ÊGrœÐ<>­ˆ¦æþožvúuŒ¨Gvžƒík²¾è¹íeO‘½î/=ü,\®¿jì¡#xÞÞ6çA7\HÉþírNö¡' ór/©MS¥¬-}o¬8ßfùHl½ˆíÿzîcKpý—ý"·ïI lÝÓ9¨Àñ/øœøüÆâôÏÚ+.*M‡™{·[»J“e߆ͯY>:›Ï±õª4`œ /'Û=§ŒnÐÓJf/ö=S7IØÈCßÌØ^ºáÇ(:W1¿TßD8ÿeõÅÎ1°#~T‘ðý‡Gq´›dƒêUK Ë"¹2V’üfÁ¯qx?¼ÎÊ“¾ÏãsŸ9E_^º·dP'8odï‡í›òë,ŸÄ\Ä û*ÇÑþnóýOãÉÏ©O·ûÎJN x¦t£$Ú½oëä²½›6>ÖC&FÑó9+ižŽ#î0=°õ(–‹ÈÖ÷3<Ÿ`œõ®öŸŽïŽ'{? YRh"Ù5t鵿Itš2ϧ&+ÚAï´Ôô‹¢U—™Ûºõ¨,ûÁ¬¿°:cû;žO0ŽåR©Þï†Æ“M~+W-ì›HŠ|8zz\ç$:lUƒ‡wv&¿ôZ6!ŠúLM¾}~¤¸þÁôÉr+Ùs [7òì/Œã×üçJ]jÅ“?.pì‰$zþãÔAItbéΚž)ÝH,×õ Q´rá»s/!îß±z`ë`ìy˜ÿ÷Õ2¬³z]O‘û×lô|uÁxB åÍ×åäEâœÚ«Ó’IÐMÝKµkïêA«çSv»EônrvOóà,ëlwïžÊ¼Áw…ó•Äý„4ý`¯=¦q‹ß] Õ·>œ¬ùõ"élÚ·U²$‰¦D¨‡Î˜Ùן¸ý)5ŠݶÚ7bÏpñ~Æ>¶ïÉú2¿~]>Ã989Æ÷eEŠ5åùóö¢˜Üõ/’´ÿûÖ$¡®”DUòçÊÇsGÓÖ~äÖ‹çôX?Éa¨5¸UÐcaÝðm gŽ ×½%nDçóHÕˆîÊ.¯Hû<7î;"“hÚú¡wâwðCí‹(Ú¬ò‰±Óžç“ü¼î'ñþÂçh¾dyfžW0N!­é^+.Úkfœ¾™@?Èy·ûÁ$ÚêÈ Ÿ/ëŠúÌÓvkÏKþ#Åõ<–[Å×mŒ0KŸë=÷‹ ç‚ìyžK}.¾èæþêžW=àÆ¡$Úåƒw¯}r»Oé?rn]*}Và¤ló”ϾùòS¾Ë?] ¼»â™«]ðûÞü¹A3®_:âKHX­ ¤L÷{ÏM ÏÝÿTäp}ÒñæqÖéÆq%¶åŸE_íñ+qã¹ï啜"¿´wN»~±q$åȼ¨V†xÒdpàÈþ’„ó9hÚ1©ÕQ455ßõÉ%É´/QHÜ'æçûoÄõiϾ)Á8+R¥k\sãHoYjP—áñ¤~¥˜~]ñ~rѹó|)KÂw-7ûψ¢=¶÷ʵzþHqÝ€ík‰Ï˃w->âE˜.=ÏÉ1ÎÀ?O¸·5‹#– +š?/OJs $Ñò5Ö.¿©÷¨WˆVE‡ÕÔLŽ(ž^ÏüzjQñþ¿ðص‹‹Ç¸Å<;Ïi5Æ9·¨D‡3®óäãò :%^ 6oU™9‰Öˆî7vãn9¹•X…[ѧÝë¿Ì“ÞgØ|“ïÇÏÄ|1ö¿§é×þUQ[¾í<áRk댻@ÞÖ¨Rã‡óIô×Nß×XÓ‚<8y­ñ¸FQ”;uX¤ÿpñ< »¯±zÕœúy¡S\?,âú¥B¾Ó¼. §TîDÄyòâa`£ü¾H¿e½$S.'Ñð­*¯iÜ‚¼;iê¾³F=ѳům7 ŸÃØ<œãMº®Ÿ9Ì·VÆ\d\ÿâ¢ð…þçI÷©wW÷Æ‘žl¹šßH¢; å\7ên ñ#K§þU2Šòõ?ø+ŸSB ÛŸ`ß«Ã4½`œ×¿ù<éûÈBø¼Ò8"¯|qãÕ»It¶áÙ½ 6dsaî@`”0?V‹uÌÖoøûòUñùž½ ùá7Rä­å¯Bv[H»A·ïw(Go0ùÌqâþÒšK,¯M¶«ÆŒ¨_!Šrw˯ÕâýŸ}nì¾Â/nqÍs+Á8[ݨ=±—…œ›?æMÞ¸ó„ß?L×KŒO¹®Wjâ~©;ZH>òç,ëy¬ŽYŽ0{Nò\‘cœJãz¶êø*–œZººÆÊIçÉŸ…•›‹qJ”·¼êçE¸”Áke¢h¥Æª·Ø;4ËùO6/g÷16_ö\WPcœ–¹KµX³!–$ÝV5¦üyRõ”)Ûžâ¹lÓ§›Õ:ùÒ¤mœÃŸDÒYßɆ:´ÃÄþÏÆË¼ƯƒgÔ¿ãÌ8[¢æøÎ±¤Ò˜¢1ò$ ‰’VÌýǼŸî¾Ÿ¼>¿Aå¨v4’&¾«²,Wô°,÷^7÷ùõÐ{¼¶>aó§4Ý`œh£c†9W,yþ«4ÿ«…ÒfÈþË0ÎÙ5k‰ø8øfm·EÒõ³âÜo‹Ÿ“YÝñýÌ&>7±óöž÷M3Ʊ·sÖ\¸ÿ¹ly\´s3 ZfG>7V7CO˜r{i$Å3£­Ïpñ¹ŒåE3Ýð}áeà–ï—­ìõ8ã9MÆá÷ ΑJ±›/ŸzK–Fß{·÷y X{jp™–µIôkÿ†GRË ÙÌyñC){üý¤aó2¾oçéžç¼n¦È«.>]sQ™s¤¾tC^£%–ì.} ÀÊWIô—]›·"d¯¹×„§õ#©sdµ7ëný,ÞØ|œÕ5;'ÈæÏžû¹ŒsäýÞ¶3bÎ’i&QãOÿø)‰ÎHî¸fµº ±´¼s+î‡Hzuм¾+‹÷OÖwX]³s»iÇ­·–ΰ~)Ç8ÝÏvŸoøYÂuÇ¢]bÉ„qC7¼Ïo§W.;¯ë@Òâ¿¿‹ÖÉg97ÂÖKغûÜ<û›ã\r<<ÿYRèE#c©²±¤ûŸe–'–°ÓÛZf¥’Ü_´xûø"‘4t­¤¼áô ñýðçnŠósvŽÏsÞ¤Åõõõ·•<C¾+Úƒ^~wŽÔÚö,ìh5;•„DßZÐ…,~wä·*M"i®´ ÚÀ,ó þó²‰÷¾ž*eØ7`œ’iø1äãÏOÆÝ9GL‡ÚOŠmf§Ï¸cá”$à]ƽGFÒ™˜%­ÓOüþÙú {.g9 lÞä¹`Æ8w«p;…1„??rŽøN¾pmyG;-5'÷ÔœÅÚ‘²“~·N†nÉß2£û‰Ïƒ|ßòó—ùþÿ.ÐsèÀõGtÍôÄŽ3$jÓ‰ºÉ{Α¹9½ WWÙ©Þ4ªxÑ[D8ïIù}È”­W³úbuÌ΃³:öÜo÷ºý’îo^é ÁÃ]qÇìsdSéÜ¡ÃúÛ);_šwß–>Ó'FÒ±/F¿ß?²ç6Ÿaósv.­Ïdø Æ M ˜>Mò\ÝØå¯çHGaRÔÔñ…ÛÞÍ?=;Ùïa¾¹›GRÚºrûÈ·}Åõ 6Ïc÷iöý³ßÛdÈÅ8üùæÓ$Uq¼éæjçÈ ]ß=¿âýj¼ØQõzçæÀfÏûFRÿ"US‰÷OþóJ dyâlþçyžGë»’wX°óÙmæ‚°Ï¿÷?-ñÂõùûã—Àyù ÖÒ)"iPȇëN©Äúbçœùõwâ¾Q†ç\?äxÎK«êœ"eÞÍ™õøöY²½ž¼à |NÒú#'œ(_…¸ï-~Z6’òç%ûˆû­ì¹n}þµËÛ>ÍMÖå+±¬Å³œ„ÝÇÒôëû¿Õsz”™ü2§È¥èȳ¤Yì ¿N?Ûiáa–_Ö'c:tVÝË)¬õÉ’ûÜóá’ñS¤„}—„­+zîS™1NBžõCT7“=5&ê“æœ%¾Ó&a§;‹r ›‘uç5M·bœŠÞhÛÊÝ+Ëü‚CcçÃØ9Ž4}àú¹Ê×úeð,JFÞáœ%‡]ȵmœž+¯ÊÂÍI©ÄáòÅ#i}Sd¦µzd™W°¾Èî÷ìyÏsÿÛëvŠ|¼òt‡ô$)R½àðõÏ’vÊ}‹USì”Ô «&Ý+'9mh:÷þ9¥›Ø¿¶®ÌΉñ÷Iaßã ¦•m»_ž W^]Ï‘Tô,RÊâ¿w¶¦ÆnÏ_ÿ]Sò󴰣Ы½d³««x¿gï‡Õ/û«¡!·r,#ì¾’¦Œ>vý•¡µO~~q–‹R¸¢³Sn•¥”W²°e}Í‘ô'%÷ÄÔ9Ëþ';·Çú>n³z†ó jŒSdHîÈÜCZ˜ê£_j§|­Nr8ìû׉¤Mg»E´£¬~ØsÓ9›÷=iÞ¼JŸ#•3ÎÇ0Τ¶9F~ Z²¥¯3†\îYqÅt½]œ?”ÄSTáÚ‘ôȨ ;ë >_°÷%ôŸÀéóg¹ó—z/žôÔ½ãl.8¤ÙÁ÷GI—ö!Õæ^‹!¯LX0ãÌ^]w¬¬VI꣎3ç¨I{]¼]|t®b_aúaïc—ôåÌm>Ný1*èî”VdßÏܹ†¼n0ΡYM”»š%ʘróKŸ‹!ë'Mí³|‰V’‡?JhZ&D?ò:U úæãœ×¤—Ø'™nØzÿßkfXgp`œ>_)oÌ?B7¸;`ýá²e±µøàùvO¶ŽN®Ý˜.ßõ0oÔËÚè€eÒÉ­}D}²óHìw@ìw,ž}ØëNŠ|¤¬À™ y±'o|Ëßc÷+¼ŽZ»°ŽØˆ¾Nµþzx_„xf÷-ö~Øz «³ýù¬®ñ ÆyWûQTŇIÚ6›>†É×þî£Éè—A9¶©ÏÕ£ù–¼.ÐaL v}•ùQ?ñþÅÞÛ_`ûÕl=ÛsŸDŽq6rÓúׇHÃ÷a…¼'ÄÝ»ŽÇL´Ó6ÛæW¼zºuÌIª9: BX÷ï'~ÿ¬žÙ~";—Ìúr†yÆq7 Ž,sˆX7.êî×7†øê¯>.4ÞNÏ_ ŸÔ´Tš¶¬P<‚N}Óï÷J§úŠýŸ½^öû~}3ýœ°ç9q-Æyþlímÿƒäîøl÷r`×ÂûC1ŽßO+g÷ÏU–”ü«ñ=}ÊïKöïc¬ÎØü5¸c³Rc.ý%ž·ñœ'0NÞAcÞŽßz€¤ÅÈw!íë=Ë{wúçOA‹F¿mHâ¯áV0馎A^ ÷ï7¬¿±:`ó%6/ɰ/ƒqöÞ¼c†ß‰aœ¨AÜI)#9ýaO¿ðgHçM+78l§+? /V:¹6Ñw¶\¼a çÚì oKùsÎÅÄ÷ÃÏÇÏò¿·qvŽÛ1¿WÉ óY-ÆY˜ÿZÂÙ;Qde¿Ï-ß?;C&¿š˜¨9bgçÕÈqî¿v@§Û{†LRÒÌëJìœ û]Oš^pÝÐÔ7g¼‹ cÒ&bgHóKËz™ÐÏ„zñÛ_ñr7#èÖ”Šçê|ê*öM¶~ÁÖþ¯{[~óXúßá±ä%¼O‹WFŸ¥Áü~3g±œ×ÌÙ_&Áy,eçÉcÉî‘ýåõ7~¿Ö¿ÉþbKžù_zü/O¿ßì¼ä˜— ó’Ó ~rž~¿œ§‰È! pÁ×Dì@.I÷• VÉ×Äòü~½<|M8_¹0A€œ¯‰È Ä0AŒÁBÞ¡Ì#ÿ&³ÿ¹W¦<0O¯_{&Ï“Ì^sœÕy`ž¹¯_Ë“zøÍ©!x q¹¯‚ð³Ë²æ¼O¾õÂo½Pëõ?ß ½…÷aå¾Oc˜P*ÁûœyffÎÄa9ˆ™3qÌ‚×/˼ÎÎëWž)óÚá‘‹ÃyŸÍKÓþ7¹8,óÚ3'Ì#‡yj~Í׉I°‡¯“NðvbÞš.8‚Ë@¾Q(p…$ÝãIì@a€7Ä¥<žþÎc“óxÒ»àñdÞ`0°‚Ñ d¿j„,°,ˆÌ^¿Þ™²q˜çf(p9„|³ñò|ÏY6Žg6bvÙ8VÁ{“y@+€è Àû+¹¯Á„¯c/¯{Ø¿§÷ý{û^v=ïkýî?â5÷Íûúg{ÛßõµOOûZ?ûïÊõâê»™zúeê<²l<³«³Ë¬vŠÍü²ñûÕ øì@î‘_¨ù7ä×d—UmÉ&§ÚR4cfÍ×<É™'&çIÎüÈY^ óà E¡;/Ìpà‹‚•¤{‘€7Š_ãá…ùw5˜œ¹UÊû_r V!«Õ L,ÙøþZ3åP{æÒp¾—žž—žžãB!ËŸ6)ĦÿJþ´ÚÃk¨®ãâ>O£ HP¡Àd(Ì0à*¨HQ¤:àJ« HP°:!£:À;=§Z ,@†" Y퓞W&µX€Å­n F‘[€ …Ü@…‚7)Š^Ü@-dWK p•Ÿ(B( #ðƒ0´À ”ˆ H p%Ä|! °9„¼!ž``2ˆ(LR00)¥n.[Â2)Ä¥. „ÈL@¡é€ ¨ 83Btzà*ˆÏ ¤ ¸€ B4 Ù®zà*ˆÒ$¦¸€ 5)Dªn †X-@Á† ¢U A¼zàjˆØ¤²82SÞ¢(!n@à:àÊÆ_ÏO”~%×UGøåþdî…ž}ðßÒYïûÿ¥ï}ëyÿ\Ïã>7 Ð´ÀÉ퇠àÜÜy–\ó«•èmÆlò«ƒQŒVnÿ=ÍÍõ7¦HQœzàæúŠÔdBfµ ¨P°f EÑê¨Q¼æ¿É¬ÖçßdV‡_ô³Pà ¾ø¡øµÀ ”øA`„xC`2ˆ# ¸¹ç]ˆÄd¼& ‚XÌ@ Áè€ ¨ ðƒx´À ”‘ H $peRˆJ\@ q™€ÓPAh& Ø´À ” H¸¬Xઙ1«ZÜ@ 1Z€‚Ô7×Û L3BœzàJˆÔä‘Uí Öø•¬j?X œ@ !›€bÖ×Wrd¿Íë¾Íë´^ÿóó:•ðß¹¹¿G1š©€Â47P£@-@†"ÕP¡XÍ@Š‚Õ 9Ørïô,ì``(bðöɘ‡mÞ(ê``2w˜PàÁÀ Pè¡ØÕÀd(ú0¡ðƒ…\l) n †Ì@ 1h(! @:à*Ä ¤‰¸€ b1?&8€Â1_ˆGì "2o þ°&ˆ*X€ â n ‚ÈÌ@ ¡é¨!8 Ata‚ðÔÀd ¸¹ß–Aˆ ƒÀ¨!J3B˜zàjÔdi˜ Ô``¬xC´ÁÀ  Þ0AÀÁÀ d²¸€ ‚6 D­. ‚¸Í@ ë¨ t3BìzàjˆÞd~˜ þ``Üo(<~GÁõBÖÿ«æsÿÙ½î?»ÏýWÏáþ;{[v}ëiÿýŒ{¯fîú("—PHÁ@ŠbÒs¿*ô+ _é€ ¨¸óÆ(4;·7!ÜlƒÈPxaBñ+÷Š"tsÏ¡(D ¡Ä‚  Eaê›ëW(P ¡?é€ ¨ÐŸÌ@ŠÂuŠ×üЛ´À ”(f  uÀTÜo'€Å @Ž"¾Üï'@ŠÝ |0°‚ ^ P£/Y€ "Ð7PC f tÀT†H!=p5Dbáž;!7PA,f …`ôÀ ÔŽH!pDdRIÜ@ AY€ ¢ „ ¬@… "³„Ü@ÁIЇœ@ ᙀâÓPA„f uÀT¤H!J=p5Äi24Œ‰4›ÿ.»y÷X3ùúó«D8ßÂÎ'û g[ØŸÂß›)òVÅ®ŽOÙ#ú´¬+ñÛÖÝIgD?íGêG݇ì”ù„Œ:Ô±Ãø’‚_È›ÀèÊGk~tDPæ«fCš¯;õôWp`œcƒnl‹Ï³›Ì ­0`ËÅ3dƒWmÎ#vš÷zð‹UdäB[éô²"hHͳ=†Þé!ú2Ÿ æøk·ŠÃÎ~ L{wY^ñ.ÒÿCÎ)wq].íúâI;mðKã­'4"‡S+W-i‹ ¼ÿn/ÊüL™O,ó‡àý.rˆ¹@žùuŒ£èÖlýÌW;IBBÃmSmgÈçYãoœŒµÓŸºŒi°·dsRLÛ&å2r&»gñÙby 9LÛÛ†LÌ‘!DŽë÷²ˆ‰ ÞAâF(óŸÿ".F貞®9²NTKÒºp×Ãû£#¨1¶U»ý“;ˆ¹Ì—ùw3ÿîºj\·Ô†}W =ÙFÜÝ;Mm‡ÏgIy‰fÌ-;íì|]9õHK²Ñæˆ.[#? "úP1ßæ Ãrd¹ëjqÝŠ¹äEŽù¼àb3Μ!}igŸÛ)ï[Ö‚´}Ùï΂9Â?×ýf˜óÿà®gÀõòí¬5wÓÛ­¤¼ÌÖPvä ©­ô¾ò蹆o(tò‘œ$¬X30ÏÏTÈ•ýX™_m—û«ÇŽªþ6ízf\K-üsþfr²nòž ûPϾ­î}c§ *^=]Rׄ$þØ8zoûÁϹN?ÞoÅ‹0Ê´:ÆuûzsbÉ”³Ý–×3!ŧv úd§«sžºýsbIÐä $BÕ§¬~Y½ñþôŸoËJFùGx‰¾ôžþ*^÷R䲟Ž[ÂõûKì2Ñ–g¹×uú>tiΔ¼%ó'ú%Í 32_sæÂ>Ï ¦‚çÞï/Áõ¿ßû¾ü÷¹Ö’£Im§÷N>C†˜ÖéŽë·‰’¯zÝ›V,?|Pç&´Y¡ç“G‰¹-ÌÇ…å€1ÿ;O?"9®Ÿkõüó*¬"‘Û÷äÛöü ±OÕpØg;Q¥Ð©ÊKJÑš’eNvˆ Ž£ƒÛ„NUŠõÌôÉ|œòïðV“|=ãºãþú£‘\OŽÿ?öÞª©m}÷FÝJìØQ,X±Ç– ;v,[Ñm‰=("öرcÇ{L@±Ö  zì`ÅŽý{VÖš‹ÙûþϹ÷|÷|ßpñûœ1ÎY3ÉzŸw–µxžÕÓÍMòòÆï/Ü/Š&°põàÊ¥¡”Lë%ÞWæsÃ|W˜o7ó‰cýÏZß'j¹»÷ÃqKÉ›­uðI£ÉÚ‡ÝÒ}0Κ‡ïê½2”!V›ín!ôѧ&ßãWõþ%ψåtàï³<‡Ü ݾ6¥i4iÚ÷L?I¾xºìÉ ÇÃ[)ˆ.uahü•fÎ-îI™ï ë[̯‹åû²<>[ŸC ƉYW´u\Ú¢´%?[E“Q{Ö]¬èOy_1%¡Ã —9±ß@Û·½¸»÷¹^¢Ï ó9çs¬ž ~͢ϡmN­ÃÃtÅ€3ç_šÂTdy­º+¯t&¤c¿TŒ§?Åz¯êëE~›ûÅnƒØ‡ÙÿŸ}/Ö'™¿-ó¶ÍG”bœ[¸à¢Î$zCÅ®†G“]uCïÏòˆ§JŽêë°­I¶ÚNh1Nžù³êŒùP±~Ïú&ó ²õŸR`œÔq“ÿš~®'Ý^cûѼ ¢ÉÀBÓ†d´‹§î…¸dÜÞdŒÕXÕ@O?Ëõ.Ƚ;eyeÌçåºÚù&Ẹhñ¢nc)ï£M|}ÓuÉ€xúá§Ï†ÚɽˆÕê®Zmôuë*»_«¯ W9c/;¿6 ÆI?ï½dš?Õœ(]G‘Mæ7Ìüza\<½öWlŒtjòý\B•Q½bÖOo/út±ûÂ|`ùœ’ç¢,Ës°êãtŽqlµ¥ÏÁ')š,rtS½š/øfv"›†r‰â!4ÏÑ6«_=m'οÌÿ‘ù€±Qæj›ÓbÄ8_ —(Ü3c8¥‚Þý9üúÎøÍñÔÜcÙÔ&›Û«#‹t¬³.„ÞWqqÔ7O±ŽÙú‡÷Íͳ[õñå,£ÝÜÚ+÷¾@ èŠy‚âé¼}W¶Þ¾Ù„4Úu¬iæ9ôó KzÚIüü,Gè\ôôE fÈùþœ)úþÚþNÒnmûtÙ»#ˆËeÖH|/ úä«´.ž–~^`Ü‚Fîä^—³ñO.†ÐSÕ÷4η-«ï2¿YÖ×?ŒI\vþ}šõóKqÝ‹¥ç•_ç±–´6¢ ¤Ê¬ŸË mŒ§õ¼zúäêâN^Ýð„²Chk@iOQl}Âò{yŸNÞKëN{¶:_ÄñõtLý€à±Èû¢—†üÜO/´¬5úBÓF¤n¯µ–£CèÜ—>Ñò\½ÅÏËò$˜/.ó÷å}'íýñ|0NÕ9I3‚éÍVÏËÕq¼HdŸ…ÑÆÓ S;9>ÿÙ’ä>YoTx­ºîc¾!Á]úˆ~¬o°œÞïò…œÏÇq·ëOŒS©VæøÀ7›i«þEò³{Qÿñ´R5ã—%%Û‘R!¾M*ä ¡_¯õß1©K_ñ>³y•å’ÖóÓœê°:«ßÚæ|h1ÎÖ/Š·×wn¥žRÇC^$ce.÷w‹§+./ö<<·#™ØùѼ†™Ñ/•õY6²y‰ïSoå,ÍÖO؈q–Ó—ù‹'n£Î¦‚Së.¿H®o%÷£âé´×yž¯mÚ™X—‘%Bèûó’û7/öÇa~u|==çY[Ÿw ®¿ò:g¬¾ƒ®kp`gaãERɧgåó7âé–™£“—w"=ê_O]Іù=v¡LÏl~bù̯ΪƒÇéŠw½'jª¥ÏbuŸk~¾H*o!¹&&ÅÓGÆŽ <Û^¬+¦7VOÌŸåÏZë×ãý6wR« mãK¤r‰æŸåOâiïk[—´û&'‡wå»í13„jæJ‹Vv¥Ì’å¾ò}¢Š]]*pÝÃêH˜°‹^ìZ¸ÑŽÉ—ˆÛ§KË_ÄÓa¹Ç½Ö¥ÉP°ªÛì,½²þ›½.Yž8ó=·ÍÓòÁ8oϹû-s7}P)ü½J‰ü¡)ÛfÁ«xÊ÷Å×òÖ@Ð!'½‡è×ÇúŸ;g}ôló15¸~¿?~¼¼r/õ³N8—ˆß„ªå^½Dø ÜžP‘v®ðèƒOÏëZr`ëÛ=ñf¹3ó§}uvþ!oµ}C¥È+Uìü4µ'c wÃöÓuªËG¾DÍ?:áY<%‹·7 Ú\‹VÈ•T{píÊç:ö×3,ÏJœ?ç­~ûªÊ[1çÄ6Ûˆq^ÿ84`“޾³¶ßr´fǸß[ª, ˜S¦>=snÞ×\ECh~ÎÆ¸X–ï4«#¶~ÝònÛʧ^Èû¾iÿd5q³Ë²`œKÊ’ÆmõTö¾tÑ×ÍbÈúÛÛ×L~€u~%}ók[3ÀmÓÖ„þâ:–å{2_àaåî+¿Nÿ,æ3Úî#µçk^îä%ŒºF¯?:µýeR)à¢äí÷xg.9{Hñ®dÐJÿ\¢øPÔµ¿è;Êú8îsUÈa~-öq[]1Î$ãÁêÝû¦¡ªÉ[\&ùVž\Þ£p}¿¢­wZJ7â7äú¸~i!G§¿˜£ÃöMì¾´Ô{­½q?C¼ÿÌÏÓªŒãppø¥öÒ#tvßMî¡ô2™ö¦Q÷áå_þ.ħyh¢ö£AÔeö¼\–ÏÀrNx¿êæ„å[õò4]±ß1ï^Õã#tçvyî9™—ɧQ¢Ýݨ÷ܱ2ÿÍí‰uÛ‰y¯ajd…r}ñmÏË„ó –/>œ,Û«<+èã¨bWÑcº£ôã±óCÖ6¾BŠ%œ>•Ú v®Ó‰6 jEœ¬ Î:iâìóíægíg˜¿6ËgåýG³òûøÏS‘× Æ)|¡ÂÄ·QiðîK¯Ç_!ÎÆÈh…W³Õ›êÖ#©ºRû^7¡…¼'•Z–Ú[œÇ™¿9«k–ÛÅü›ÙþɪŒÓκq>FgtüëØJýÁç<^94Ýåã]R7fÃãç B¨K‡ û¼Eý°\–CÄö¬Þló45gÈÆû$YqœØî8uÔË+¤Öùqá-(Ÿ[Pœ,<¼PêX ó™oê˜m‘Y~Ô,ç„ÍólÿÁÎGX¿°êãüàâô6§}36¾Jáþ/ËhiÎö¾Bmò «£ä¬¾Yî[oóõXß>W/ ûn;{7‚{ŸÔ¦Ÿë5Ræ¹1et¿j]Ž–êLêÙöz?æ£9¥;¯žYÍç—ý'û½X¾ŸßYÛ>' ãt?÷x&’ή¸»å»I×Hÿ›oŽL ²å²^÷¼ˆ×ùˆ° § 4t\¥· CDý°ó,þwJs[Ø~ÆÖ/^q8÷b¥OÐkž3kŒ!©Ã„®'ë ×}ÌϘåÿZõ€ëÕ*™>»[ë“ô¯‚­‹t Ž%ª¹µ¿|Þ‹þ›ðêNñûžÄ;fPÙÞ.!tSijæ½{ Ëæ3Ÿ*ÔgV.§m®Œ×Ÿ¶uÇIºÿˆÛ‹]i±ä{DÓáºC 4cúôpï- Ò~öw'¬ï´Ýu¼Õ8+ÇŠéšÝOvnÈÎìæŒSœ‹÷ª~Š–)îqòt³ë¤äЊ+¶‡'ÐM›ä–êÊúÄÿÂòŒÒZ©cÇ¢m ‰ë;–ÎüËYîÓÓÜÓ‹RQ§è„™ûµ×ÉLßw÷¦œHöyoä±}¿%Ÿ ”N2\¬Çkwª´-WMX7¾ÏÛø~TÍî\OŠq†,¤/Üí4ÝÚsp­§®“g_º+gF&ÐÝ>]ËQ§Ê·žÁ}¼ð-É4\¬¶Îaûf–'Íò8ì|¾1Nø˜{ Ž]ö=Ó“TpðíwÈÕ@¯ýÉ= +Ö-{®ÄrÕ6(q¸Žî½xžk—ã…qäÕ{nŽ}h¤‡6´h¥¾EÌ>‹Æè–H9”ƒ$z’·öÎéXÓ@×þ\ èjŸÛRR|>pp`·^¥†³}›>0N¾ÍŒ³ªwÂy‹ÈßœmH"m‘Ú}ÅÆ =IKëDl Ý ~_jOÖ¼ËÖ'ìÜç1¿¾z‘®·îÖ%éFZ´ÈÇê‹Ý"^'Iú&Òßd’¶y‘ŽEÌqÜ ôÃð7›Ž#ÞwÖ‡X?dçV=àºÖ˜ÄÜ“Å(áî6ñ­Ò}\™Hs?~³fz8rû ^˜_gß-ëPa¼Ø7ØçfϯXݹ®º$ïÙ]£è€áÜ/}›|°ì©t~S"=’©+ç|§ ùÆ-‡0¯ŽÌìÞÔË2^Üß±yœ­ŸÙy‚U¸îÑÙ ±u¢}â§zfÐÛdQEî@<‘ΘÖyþ‹|-H¯Ï¤‡Ð‡Ü¹eÃò â9%«?¶Ÿçßãù$gë]ÛßG‹qæÄ·šæ­‹¢yiáúƒßÝ&oصo¢—Ò„´íwùúŸw7Q¬O6?°<+V7ü:Zj÷œÆˆq[T£hãa#w\r»C.`•Qú`"]Pq†ã’ÍÉóKÕ?£zz ©]ØPµx¿Ù¼Éö[ìy ;§`óˆUçUÞ‘ ]“£¨tClù5îÓ¨KùðDê:]²©\¿6äÏñ~™¡§®ß?N±lõ¥öÏ.‹ùˆ’C&¨½k~\žŸ^¦+ ÖÔÏÞû.Š~ Ýæ–ÉRüvÍK‰ô`÷u©ß»(I9kP’žúU©]óß$qýyÔóÖ„÷õ+ ë¥da}þFÎÏšb®¥U'Oøüb_ÿ8GÛolºxìqü^>Çf;žH¯>þPúÂÐîĺ¼n¥ÞS˜,¾/ÂöÑ,_ŒO°ý€í{ ŒS}rí×zÇst£K¡)Þ÷ïw§GWµàþ_y=»z¡Þ¤ÊÇüZçþzÊ÷ÝÉâ¹;GfÏøùõ¡˜Ãi—Ë…qÚ›‚;Þ)xŽîM,~ÂR(Žôjíwè}"½Õ&2rfB?ru¸lû ž.¿¬ËðIâ{JŠq¸Õr•ÛQtƒëçNÒjwIøãâ߃Æ'Ñn!Û&mØÝšdZŽwv)b svÚu„¯¨vßùõÏ{ùåÔaÏz5tÏlskÇ+z>ŠŽñ5(bÐ]ÒjžÿƒØ)IôÛ2mýµº¦dkâ‚#RôÔÿû¼ä°ñ¾âûì9*Ûgòõ•‡°¼gÛ}´Æ)Ñþè‘Y1Q´ÁšVmbƒî’bQÉ?rÍH¢EFè–xßнˆ•°ž’žíõ9í+ö6¯°ç¡|ÿ(>ãû¿ÏÕ`œŸW:­íiŠ¢Ö§âÆ»$exåM}Óîx‰G[‘®ñéŽSVëiPñSá'‰ûì9 ¬¾ÞM)ûãv᪼Npý%G]ÇìºE·‹“^{y—˜|çöK“èåvÕ‡ÐöÄcÁÖO?}õT=Ë{â“Å~É~wÖ—Y?cçß¶¿—ã +úò¯¼4ŠÖ´lÚÝå±ÊhcíSnù¸6Ç»£—¼ÜOOã}îW­4Y<²_ŸÞ³çol?e»?°`œ‚[rÕÙEÆ®HÏßùYVy]ÏZû“¨®…þCü /Róññâ';ëi¡Éóš4›9IœïY=3ݳ¾ÉæÖW­ºy®ðv蕯ˆ*Šf–÷^4÷‰¯Y~ëÈ$êQû¤Wïdð™¥Ë‹(õôiÏA}>þé+öeÖÿ‹Ý›Ñâöúxñü=W°—¥§WãéQ4߉ež'ß'?8}9‰Öî¸ýùWµéúñÆmMk=åËX->GfëS–ÇÈÖ/¯†–Øø°šÝ{ Œ=ØÍ§…s-n}Àzœ[Ú+ta|=¼hYÒí¸®äp©Fu7ÐÓÉ£¿«Õq‚¸Î`ý™­OÙº†Ý»÷N0NÇzc>¢èúm³½Ê¸›I=G—^O’hPÿë“:[ê+I3«èé êßÏ”œðË9>#ç??ÿÞŒ×Ýw|ô¸Cy£¨ßõ¡kÍäXÛ~³òg$Ñõ·Ìyz:ÊIÉw¿ÿUYOŸ„}]yÿðDÊžCؽw‹ë´ñíëò‚Qô¡K¡óÍD{~æ´\“(®R› 1¾W˺zúzulŸwå}Åy#û>ŠÿþïÄgÛw#ÆñìttZù|Q”_ž›É¶Î¾}â?%Q¶~ŠôÝ\•è)'ý’ïËž;°÷6Ùùˆí}µ`œFó3+鈋‰Śɩîä¾Ï¬íR›uªJ™ä-v`t™É¿œ£³óö¼ÓZÿéŠ:«ÿp{f¤C¬0f²pêà3ß%ÑÇ×ßø»—ó Åëuº´§žip9¦¿y2µ؉°zäßûÍó(mÏŤgÓÖ„²ûÂԣ㈥Í$ñDº­Tȵ×ItÓ‹v¯VÖ¯-~þÇ5¹‰ÜOüülÝÁê…=¯Þ—¶ïãp)»ŽþFê8]7þ¼[s‡ýgÆ“?Úªéó&‰† Û_D÷²1ÑZ†yÞw€Vs|1æíº)bßfŸ—é‡ÿÏÅ÷&mó‡µg¾Š{‚M©ö~l¹Ó‡âɾü‰Ý/f& #«Êê–ÏÙq€³Oß/ß3ê”íÓXŸ³}ždÄ81›ª#e)ͧÛüaØþ†=GæÏøœ^ Æq›'+ZÃã ÕF?íö3:кO¦qËž»Ï?¦ ½š<.ðZOù\g?Q‡lžcóû>ì92ûʈq>w^ö¥êÔÓ4Zž«d•Ή¤˜q’{/ßdáý¥ÆdË”‹Ÿ.ìÕÓãenúwIÎÒ?Ó7»>Ÿþ=Ç÷Ë,§»uY|š®Þì_?Ö/‘ìÌ\¾£òÔdÚzå§÷®U[’ñ¹v~È¡§•ê6{µ®ˆ¿øÛ'±çüýy'gûhÛûãð6]1Au¥Ñ¢õוxYsO"™4kô–Ús’©Ggk“†¶%akV%çí¤§ÜS’¢íüÅ>o¿/ŒÏõù~\VXÿðï—Kß²÷ŽO 9ɉ¤[ËÃÁ®K“©üz£¡_/+I§àÑ_ûõÕÓ«uBE·ò×Óâ¼(|þ9g‚ø|Êö½cÆ)4µîò÷»NÒBùî%Ýü™HT‘ƒ¼×lL¦OÌñ¿¹³+1öï´a­—ž¾Ë5÷úž®SÄõû7{žÇžS%¹T{†«Ý:Ôã ЄDž4Äú{ÞÆž«³óÛ÷$ŒçK-î‰ñ áœ(‰\¶eøs2-ë–êúDÙ•¨k±hÖ'ÎuÌŸµOdõÍæ{þyÂa]Vü;)«~0N›ýG[z‚F_“¯ê±=‰L^PèQüãdá¹Wò²ÓUå™ýzz¨õé_'ûŠï?³zcó‚mŽ²Ã»t…ïãÛnö=Aå•[Lüëh ºUlDÉŒdÚBåR5(ˆÎТtâ=åÏ!'‹óÓ?{Ÿ­/Ø9šíþSŠqŠòÆ”r‚F¹ÜÒ/'‘½¥;Ußÿ1™ovðùéÈ`ícî`=m£Ž_µÜSÄúbçûlÝÅÿ~Õìsçqý~ŽŠ¤8C$µ.ã$IÃâí½?'Ó[¹ö¬®ùXJn~8s£ž¦Íû²¡ÙŸþâ~Í›lÿÄžÛ{ØøÐ³ûõ…Æé²§Ñ–KC#iåÃÉ­Ÿ’È¡#—¾þ’L/mXÚÀ¨}–{*Ó÷¡Sâ]õ÷ëì=Š<; uöü,¬cøçF\wóŸŸúV­I£ljÛ³`2‰×¯»ö5™~ð_ÿ3¡`iŠMS¹Ï§õ4úU‹E—fû‹ë mµÙ3V”ø çÿŽÁA˜Ï*ÛÍ÷Z\ÇnbŒ ó÷UëÛ¾J2i–*J𹕧ü‹x]†ªL&uAO;¬ôÃLéÿË|Ïú¿ÿ'·ç¸>7Ë_Ahó9Éqý„k3‡e&ÓW£F->øåíÙúÏÌØ¡×SÍ-¯ÝAÃýÅçSl¶žÈþ<ÕöÓ‚q¸¿RóèA;µ]ÑfMÿdòiXÊ&Šqø÷s]ÈÞ1§òŸ:²þA…¿°Ê/žW±ó~¶¿dçdvïY½OWX_£sŽ ƒZž~jœ”LRký1ÿ8îÿ<Õƒ$¬éSlö±OË·,±¹jÖ÷aûn¶.æu÷Xο×Ö’4s¶“Ü¿¯ Œc}]ìa8Ýá½ÿʧåÉdUľ¥ïr¥Ðrþ×ó>ÊÕ‚TNSõéÌõÝKw.øË_ÜÇf?÷cç õ&ìÎu:oáùE^Çz¬¿#œ>ð[Zw2i1{ðþùSh·zË»JWc>Äõ™«§ß~ʆžSý:_±çÃü9Óa]YWXŸóï]ú`œc涪Þ3œîŽÐöÀ™d’˜«Á½%R¨õu´ 6ÄuÉàö‡“ô´Ü·ïÇOùå½>¶?<ògû¾U|>ËÇ7jÚuG±®­zÁ8ÖÕYÆqZ-­ÞûÀ›Édþ¡í{THú^k²}“잇ò_“µÞcõÆö×|ýW±¯ØÎ‹ZŒc””k¸dúqzœ{ôY2y¨øXªjŠðº¹ûx&x.äNŸ«yNÌú.Û·°yÄvdÄ8K×~­°4÷qÊŸ7§“ŸÔ—}k¤Ðʵˆ—ß^U|OíZÏ“;z©Åºfó‡íßÿXp=þï™QþïESÈÌï?_ÝÆõF7¾Üˆì.KÓ·ˆ|úÙ@{çV¼î#_šý½JvÎg=Îõªj÷žµÃ‡tEá”~Û<>*<7M!ëÊѲZ íîu¶Í²útSg}ø‹ž^¾J|?q=Çîÿ»Ä‰ú`ÏylϤçtå¶Ž;J«t~âÒ'…ï6he?i ­—ú%,Eט“W…0PÇ#ÛÌš’µþaó«[Ö×ÙûõìóìqàÿùíÕôÛ«éÿ¦Wç¶ ìvàÿá¾³¸åâ}O,B&Y`6OÎLÁ““eöü“G{NÞMn6™‹¶™=\æ¢Yð'ÖþÄ9åU(…¼ ç¿É«ø;ÿ:ÎÅ6;Pð¯ó°É—µõå |Ú6¾œ¶þMÌ—S-ør2ÿ&ΧË]ÌɧËí±ÍÁvƒð‚@&ðùŸ.kVð±Ëî•Â|ì˜WJÈ>‚mö¬­7§W¶,lÎ/EAk$‡,l'!»‡yµçäÍɲ{Xö"—…ô?ôNÑØä˜ýî‘¿{¤Æá¿«GJ„ïÃÝ[fàå©rmýí¸bõ±É±ø'ßâœò¶=lrÍls,¸\3‹àñ(x|æäáî%x¸KÿÆÃ='ï¨ YÊ>—6Hð’Ùä9ÚzÜiïb¥Çm7ó¸ <îX7'..Û,'ïâÈlÙ´^° >ˆa° D0©ü¤| ÌàqÛøIÅ5X«Jð“²Íx´õ¹óΖOk Zœrȧuò,˜qN>w,Ï‚å›qù´Á ø Ä4ƒ`¡!¨€ È?w–ïÃÕ3÷ON=òßéÿJoäú⿚IûïöB…CÎ}ð_íÿNÿû§Þ÷¯ö½¿ëyÿ‰~ÇzÝÿ¤ÏÙö8®&´¼§àßÉåôp™ÛN‚ž x>xi‚gqdžÅN(À`‘ðyÛZ )À{Gf˦àrÊ"…\žì9šÿEFcNìÌ«Ó6—Ö(x°Çl>xœW1óÀS¢ØÃ€3 ^,@Â× Š_ Ì•øL²ì>ÅAB…Â&“ÌÄðçT žëÙ½9™ç:óæŒV0A\Z ©gïg›IËùrª(²eÒZl²'˜7qN^x\ö„NȈùøqrŒÜ©–æ÷:î÷:Îá¿oç,|fwïP˜Á‚ǧÒ&—QBÕåá3¸U6ùÿäSœS·Ì&Ì6‚Ë#K2Á«=óo¼Ú½¯v·¿ñj™À¢ˆF° Û|Ú`!dñ°lF<„ Š !ƒ"HËÛçsGÚäs[*fets^Å\&YN^ÅÆlµ2O $Ÿ˜€ "Ô „¨f € u‚(UÀd§V¨ ˜€ BÕ Ī1Ù2e¯H<ø ÛœZ PBÐaÀ9‡œZ©CÁüŠÀ b™ÀÇ&‡‚å’q9µZ¡ ¨€ ÈÐ ´@‚† f ü?Y.ÏßåP°ùOýñg ‡[&öÅÿ·zâ¿ÛÿõÜ¿ÚÿO­çþÛzë{R>‹–+4ˆ2w¸ Fg˜ Å,äQxçào쌂Ԁ4!‡[œŸcc¶L –9æ$ä.ºå»Èr¸ÿ)w1'?ö˜ògc¸£ðM@†â™\¯¼Ž¹< H^D$B@ a„'ˆ#X*ñYcÙ½Žƒ…, ¥3¦& ƒ€‚©€ È &-@PjÁ=X– ˜lü×9‘©€ È 6-P@p:àÑ©€ È„Œ1ÛÌY'1X€2[ælšMçw2€7„jnkAÁ2Ƹ 08X± ˜€ bÖ‰à¿Î¹Rþ^ïý^ï9üwô<Ûõž›ð™ÌܽAajA&ð²É]T¢PÃòð9Üj›\Š` |P¼1À 2sÎáVØäÙæRpyc@Q˜÷|çŠ='ÏwÁóÝão<߃A¨€ È -dËŸÕ‚L!›‡e/ª€ È„lŠL!›Â˜C·ño²¸¤|æ˜ È 2-@hj!›Â6ƒVáé€ÄÌ@ꀄ,@‰$¥˜âÔ ªf €PuÀ b ¦lŒ ˆWœ„| ÛÚ4àAGi9´nB>E&ðÐc€Ä,^e“OÁrǸZ  ¨(Ð tÀ !ÀÆ·=ØÆï=oûÝ#³zäïþø¯?r÷’ˤ•  ÕÀ$döpYŒ1@Š  @¡j…ü ¬¸¡hƒ@&ðAñF) 8dHølî0àŒbV ¹e¶Y,·ÌÙ&Ñ#‡p‚€(!Ä0à 1j€( Jp‚0€( Pp‚H€(!Ö0!ÿÑ ¢R7X€RÈZ³ÍÁ•BÌ xgËÁåòÖ| ðà‘ BWAðZi“»æñ P  è€A°%BpFSge{ÿxý»?þÿ³?*…k¦q¿-Š2 HP˜> L(Po`ÌÃçk€(P°: AѪ(P¼:¡€Y.¸,V6¸ ZœPÔÀ”(nPà^@œPèj` ¼8¡èÕÀÌe䢸%(~50D NB°%œ³å↠—‡k“;,@ Áh¢QSÙিÉ—Jq`Jˆ+ 8C``vµÏÆõ‚à"¢ iÀ â‹R0doÑœ!F H^e$p†05 xA ‘@Šž,Ùr(½ ÜH …x²åãfˆ9xä+ƒ¸µ@«( tp‚Ø€ È ú`Aø\>.—錠iÀ RÈ ÀMA¸¤5áµÀýÕëÿJßãz^N½Žõ¸ÿäõ¶§¯ý¯²oYûOö0Ö»Xß²íYÿ»ýʶWq}ê_yïûmœ¹¿û@ÿÉàÞ{AÏQ¡çh„˶1ÀƒË·&P#÷Î2ŠH>£Nè3j•™Û¯¢°$(,50ÛäoKPd*`2›HPpj` ž8¡ø€%‡l[ zK÷ŒE鄞¢&àž,LÊ*`2¬d®÷|(P¼:à„^,@éš•e«AQ§/ô0àŒ×€4à…BR{ °%Š> 8£ð5 (!€0à h@ð‚¼!#÷ ‚HãÞW±É¯uƒ0‚@&ðÉ–_Ë EL@Áh¢Q3Pâñ1Ü;*‘" Î’¤/ô‡H •gehkï1¯¡þûÖPÞÂÿ'ƒûÏ(ÌHà„âTH A‘ú€˜<|Nw °%Š6 8¡p€(QÀa@"ÉÊëVäÏÊìöaÀ…­iÀ $Ü»z 8£Ø€(QôaÀ…,@è€D,@ 1„gBÒ€„ ¤G ÈÞI$p‚P|€¸A0¼ p‚xÔÀœC~·ùo2¼Ý¤¸H^Y$BhÀdœV707ˆ/doˆÐܸ o | È …(Að†8@ ‚ à ¡ÄÒ€D,׬f €u‚˜UÀdµH l50®Ny°%Äœ!x 0„¯îo.€ñoò·ÝЂ@&ðAc“gepsšàþaý‘+®þŸ\Ký'û]öµÔ?õ¶œúÚzõ?]Cý§ÖOÿêßNpßSŠž¤A(/ô'ô` ôpBß & CßÑ Š'†{g=G‰" Îè5(( P ¨tÀ …,@Ó'™˜ŦN(¸`J^pFñi@ðBF)÷·°è-ÜY? Ò=Å dè'Z Aª(„BUà‚ JmpFÑ€4àŽŒBŽáÞ A1gp½ ¤(ê@¼QÜFà†iÀ … ¤(ö@à…‚R} ÈÞ(~ w†dpïúBjà BPAZ (ÔÀ ‡8A À”ŠBQ÷¾“¼ šH E_À}ÁÜÐÌ@Qé/°ìëjû¿ ²yÄHø}\NýG*<`ï‘°ÞÃþIþ³âCº¢÷ƒæK•9*ú*½ÙŒ\ðM}åyòºßííœ]…Ÿ þ ‚Ègù+ÿ3<5Á—È•, ¨:xûõ,ÿ-nŒ³³dhRÁkG茱>†?–¥>;…žÚfÞ~ìjqº~NCƒ&hȤo=ÎòÛ°Ï-ü{ÞÈ׈“WXånç®Á8× ¹Œø>ò0²ØÒPm ù¹½H¹0ŒÃçV!^–m3§3ÐM†¹ÕH˜"úÇ0?æƒÁ|JXž¢mþŽãt±6¦™=L/N¦×Ý:s«Šø¬ÿ»Æ¤ÝÂgOàû0¿+æëÂÆc¾ÎÌ_÷¯h$æÁsã1ÎË7mš·\p˜jK99—BÒ ôNªšBµK]"‚w´ ¼o'Ëò}d³çï°¼Ëźs;îÇ6'Ì—Ç‚qôo[U)Vî0mPùÉýËÒdAÙüUj§Ð¸>ÕwnºÖŠ;ÔRÙ}µò~ Y>{¼ßrœèëÊß÷ú„å±X ícºâlR¹v…Ñæ~ÓSVäM%¥ê,U-l˜Bï/>µ\¥–¤ZÞÒÆusQG•FDçÕúþâ·Â|P˜o¿¶i©á‚5Ÿ !Å8­­·)ŒŽmçQ¤n…TòºçªøÍSh_¿Æçû-lJvßë;Á@ùúó/æsÂûż’ÏZ47³@ù/òʹ‹ìì[—ðþ.ü8 Œ3ÍÿóÝÅ 9¿©äÆ¥±%•$…2Ÿ¯ç«7˜ŒïÓ·hðâ.ŸÕ¢¿3óma¿Û³Æ‹·ÙõẺ´õÁöÁ8¼ýj(m6cÔ±>SɃê?æïÆ8CêÇ×­b(FoÚ¿zÃ-b5Föóx˜¯ó÷åÇy&úQÚåéaœ8S>Ÿ<®!Ôýé²3e†¤’&=®Õ2…^޽z:,¨]qÛ©OfÃ*xykÑùIb=³|‘5¯Cwç=ÿHÎç”Ö²ËÑÐâúË~˜®­«§çT}ËNK%Wo½+1¹q •OôKqYAW5»aÞ8.„v+²ÃÏ5Ê2?_ö=X¾ó¯åýá>tü÷0bœõU¶–>­£]ëÕß¾1•\Ùùì›uüîÇ£uÕR[S«}ЀºÊ«áž¥¦ˆ¾mÌ›ùÜ1ÿ6^Gµír-'²ÿiog×ýô{ñ7w?G¤’#w½ÆÔsM¡et¬Ñ†Fæ ï& ¡b¦cÈ,ÿNöy™o ó9c9Iv¾ËŸÒþŠ}Ù4}/í[ðEýÕ·RÉΗ:´wN¡Ï·<›3qIÚ{U£ÈÐ:!4Ý}R`d³)¢2óoc9Þ¼¯L¦˜?hÛ7¥§èññÎy£vÓÉõÿ.:mêð&?Sɪâ>ßÛH¡¼YzäíUŸåÊ:³ÃŠüõ7ú‰óˆ}Nö-ÑÇ•ÿ y½àúϧ¬nÓ_KóŽs¬ò1¿…Ô?5/fV¾:qÐØu›T§Åãû—îB¾8½"v¥ß/9´ÌPíuX:ÿjºœ÷Uk$æ»[õ‚q^žŒp­ßz7p]¼´„…UžK#þH¡|ú$?bQÙrÝChïrõÞøËï—<@æ÷Ã|…øïWŸ°J«n0Î*ß zœØD»Ç>Î×¶‚…èWïÿØß§{R‹cñ»ª–ÛÜëØ$Ü9?Qÿ¬¯1ßu–gÍòŽí挳·£{ÚÇ5¨ÂkBâ'7 Q¯¯áäûråÀ¶¡c:×!ÃÓÛ–¹BÓÆÔú¸1y2eý’Õ+ó)gy,ÔÖWÑ‚q.¸{®ö±µô¸WƒfÇZHŸˆJ다Ðo ÏOu¬àN¾r±æ…B©Äae«u'SæwÆ|éØïö¸sžÍÝq Ì÷ɶß8d¦+¸ÔÏò‡‚èä3ã÷µii!SId—ES赘1ÔÎãLúnW˜Ýû†RÞïÚWì7l<æ{ÉrûXÞŠ]¾ƹVs\¾Hé2úÞùñ¦m-dvÂÛÄÊÇR¶J̧Î4’‹A\JÏ,vboœZôÙcúd~uü8|~¹×}Œ.R¼ËBªˆº°´\ yá"yY0…¶o•žÓ¤±Jy_Vµè«ÊúëËÌ'Öª\·Þ¾M¯¦ôœC7ÔÒµûXHIKÞMP·çÎnk·`NkZ}¼þÕêØPúùCÛqaêI”ùÏñ¿[¦˜÷c›« Áu4»=o:™~m‘¸»é 9w¨ÃÆ—Ÿ“iôØ®r5ë@Ç+Ÿ¶34­Ï( y8~q“©ÇžŠß—yÒÍ/ Ì [J¹TàÁ>þY9™‚ï"Ÿ#õI>µWµjÁ~Š:cue­Œsôpñw!{F½fÆv2÷µxÜÀ<‰ÉT6.¼k—I-hikp(äëÛ{Â.Ñ_šémo ÿÙ[Š~×ÁÃ׬>¶¿+a>¸Öúÿœ®üɰ{ý;OBÒWRÔŠK¦ÂÃ×çyP“î~Ö¯y¥í¡´½[ø¼}ÕüÅ߉ÝW¶Þýô…>èææ°¯Ÿ?#Å8ΜÝîúYäæYDþ©Òy_ïÛäV2mX®ÅÆà ôÎpΈ=”V;ø¾Ò¯}Íõü4§:¬¶ˆë Û|yƱÚ.÷œO—[Xõèt ñà‚LÉtàÏÌA=ËÒ ÌRc#cBiÅ¥¹g¹vŸ$öu¶ngþâl]¯n÷)dì±Úv>õ>ç@™™›×–YBB¿ a¶…|•–yr=™*c · ]]…ªZ·“JwçY6±FVß`ë–sÀòA¿xs«ë0»üQ ÆÙ;jú’Ã]V‘¯ÏôŠ:‹ Ãüû6÷¿šL-µN~Ø\±=5x³«1=”î2yU© /ås9œ‰÷™Ãz3Ÿá‡r–/Æß—ÚvyÌZŒ³¨ÂÅ=tãÒV=èb×RlýÎ…C.$ÓIž“N:"§|~åAêÞRåŸ<З²u!Ë©f¹@Ì÷•ådÛ­»0N{Ï­ÝŽÄ­'V[åµ²³ü©/£N'ÿ?ìwTSYÛöv¬`%ŠbKDTt4;bà :ê ŽvEÀöرÇë ŽŠˆXÇBˆe°€ãX±E £è{œ³C:ïû<ß³¾5ï»t­ëùcïœsîë>»ûG'\Ì_\˦;j,ØCÛG×´9})„2N{>ŒwÁ¸b|Ü5³à,êÐOô”•I=N*É‹Ý[ÜÙŽ¸vzÔU¥¥ /wÌ͉ïCGÖ\ogD ¯3r¨ioÞ»›Û ozçä;é¥1T|Û?´MÙ™%üÏê#³q*ÿž·ÌûrôSÛß8Ã%ü}בŒò+g×­¥C¯-îKµ'Vš¶(†Z÷_RÙ;v:µ/V'Œç8}Ð!§õI…¦º­æõy#ÑOö†úª#Éû_§WGÆÏy8|ß`-ݱÖ*¨vëžTYãDòƒ1Ô3¬÷ÎZ§™ò k‡qA>í>î×¥w¾‰seÁÃ@?ÛãnWÍm¼ŸøÙüúò ž‹¢p¹÷æ>ZZéØíüÁRÊUAýt<†nÊîðîZ·…åVϘñŽOÂ|ÝE‡~¼®ÛdÖr¥°×nuäÈËçËÔݵtŒmŸz¾é-éðž¹]6ߎ¡[ÂRK=i4Íôüùv™æ],Ïð¾on}óá©lö çv'¬£Hé£CÎ:#žï>¬³.ÚSKyî¤-µ>öz~V Í,˜pnz‘oXþdy™½×øºÜ,òŒýT¯ö©GŠòÀŸÓ‘vuº¯ÓMK ‹“ù­,G6;ýœül 'ñ=Øsâë~ÿ)å¯ó…”ŸŸËˆ9ŸX†~êõyÑãDƒ#Y³²;òÀ³´¹¢pßf)ºMªÙ¡±Îzü†!†.æÊ4מVbÜÍ®ƒqÂ*ýYw‚ç®Ä‚û~®&¸.ú}”Œ[ïœm½q=è(ÕôÔÒMz—ý•.¼“:½Q;VêKˆ·^i}.Ô4`ó·Öݕϟº”r Zx¨b¡‰ûÎønFÿ Ÿ ˦UnëCÖ®5uú™òvÎÑæ=´T”èÙu‰}Ù¸ßÀVkb©äÎè¥#¤¡¦ëaý1Þãíò<Ζy-ýh²ݰŸKöÙ4ËLÜ¥#Û¯?”(‰–n›a·TgÓ‰&?9šêñ[ðúÐñr'Ö™"æ-Ò‘ÂÈVƒÄµtÁ¯ÕSgêJ9µ³0–ÖÝ7—>ÝW,ï2.?{÷En—ý”^ßc{x¹ÄÈ1X #íÍ´æ®G/8î:“7lw,½s¦r=9ÍôžaχͻGÏè ´{DúÈcôi~yí•=W¶ª•ØQm‡÷ð”î];-kCv¥wûà{!Và°­¯±øbÏÕ6úí®íÔ)¦çÌ“d÷Í¡M×ÏÃ{DRÉ7´––:–›:ì÷á®äÅ¢ íÂÞÆR#~·a¨‰óá|aìŠÓ¼îKÏW‡öcÒšéÝð©ØíÑÛü¹ו‰ËùÓ¿»[Çþ'ë‘+ºœXWKE¯m9º2xc‘ïØxñhÙú†9OЪó‡2fž=Eøõ<ÉJÞ²c”ƒ– „šª«Ó›©¨Ýñ­SF­×²çÊÆÙw 2Žy'åëD·á}€ö9Úóˆ°_ ñ½²,h¦ŽT{.q ~ÿ›9ï~ýi[czgîæ¹Ï[©èÍͶƒ?‹BMõüÙ<šùçjMØz°y½}úáè[¤ñäÇÁ«"Æ`¾µîD…Ók`Þpá{õæÙíéÏÉŸfÕ•¨LãR6®-ÎûäÇ3Lu¦ùûÅó<üÐÏdiLåZãÉû܇{^éˆaîâÇ ðÁûêÓ/§ÍëJWx^=³ …Šºu•ÿ03ÔTïõÇø1|þÐãHŽvíû8œ¹ì4y}Ç©rP ŽˆmTo³Ö ãçnôUç×;[ŠT4'ûbÓ¤]Eëÿ¬>6Û_`ÜcÜ£Ý÷ßÕ,óW÷3¤N§™>+ÐîS¥òvC=28뢌îtvë¨(Ï{ŸfªÏâžÎz©!ð‡5š¼¿¤Î®¶ð<ÖNà÷ðëq‰ègS­‹©ÖvgÉ®>eåÍq_šŽ\wïyUù/Q·Õît¯¼ò‰µŠfµÕ¢Òúi&~Ë3ŒKpys•6§J?í°o–BʯίÜ/h0ïôsú7 ?K†ÕY3wTˆŽ<¼6~Ø“‡YtaA]¡])?RÑW‡v¾Û´³hœÃòËsŒ»˜Vç¾})§¯íåóÑ—÷Å'ÄÓ¤ì•kÏ—§£ýg žR¦—>Ÿ•E·}ʬçv§»“–_j«øïE|%æ¶ÞÏßJ ï¹ö„ñpþ@?F¨Ïyr²ÎÁF‡ƒudßÄI#‡¥gÑ”—܋ɉö©¸uHz%åùŽEï;v=l¿lLÝ^fHÙ¾!¿þaËûý¤6Þ«~æöñ`=’,jRQEg®È^jªƒÏò8['cãxVgßœCã‡~&|ºxOéÖ¹zkï‰:rå R[Ü7ž?ÑŽøÿèa·¡ŒŠ~jưŠâ­¯°ù[_~X—snsË}FôSwo…Ë:PÔÏÛwŒŽì>ŸCÿÈ¢wŒÍÈȃÛ;Ï©K«è©ñù†Å[¦i¼Îû½#î3ï´{Ùúí‡O×)‘¦T¨4l¤Ž´=r+Dñ,Kàf{÷%c·t)¯¢ –ï•ý¬žVâ>±uXöþáçñn–~A?š?>Ù*‘œ°ygÒ•Pë†åÞJùñfkÓ>ŠÑ/è§Ë8ÍòÈ€D²åéÖˆ_`žû¸›O»¿²hw#×\ •:¬¥ŠŽØ>¢ÑÑ+¡”µÏÖMëºìgÃÛØû§-,矟ʞV½ÞùôDÂs³t$ñ*·œEyŽ…=q?û2Ú«ŸŠF»»”ÑŽ -ñaó©YKVät|'¬w5±\ïE?üþh"yT#îMûöˆ¯ö‡ü¢/〷°¾‰çÓª®[Ãý¡%ø·|{×M| £?ÐnûSOnsTXj뱤¹Žmíåõ÷)Õ¶°ßù 64o×ãUt{·A›nœV‚KÂâ–íÃÕµ¯²dœµ“¯ËýŒ8•f_~Êrµú®ñµéH›„†6…xî¿þÙÀ9¥=m¶tÖº¶ýU´å‹;?IЏ`üûø’°¾_Êÿ®Fï'9Úç÷Ã.k.ÖmÊÛëˆ{‹ž#_eQç_³¼ç¶§Uj=üØ_¬¢Ý‹GŸR4ob÷™÷÷ _•ÍÓÍ×5"Ñ}|ç´Õ•~##æV¿®¤#ï’NåfÑE‘Ü™˜nî1Ú¾GU=ÿ87ɹ „²ýhöÞöþÃ!h|«Ç¦q4nz»9‘M-xê‰èçxÓ#³ÆýFVÑ$¸ÚèÈõ5k}ÒÑÏI­K¯R¯²Šzr8£›Á&Þ ãNóûHÒ³ëºî:‘oÚµXF?ëÅ'Ž~HúÈÜtêòê>¹¿`Æ„k/³è›‘ûkï¿ß‘,À¬y£ƒŠªêj©™lò#k‡_¾kšoò×ÛÊb~fe•+ã¹:I~þ\ Ýâjë¼Ç{¥¶O©Ñ6ždu¿ÄØõMT%¸çÌ—,~yŽûŸÆø¡ÝÑ…?_œ;ë"饖í™pŸx¶›úd•–.¡g––ÿäIV6XÓ­ã,~]!Ä”wÙ<€÷Cš´ÿ±»¬î¼ä}v÷Чž¿_$ ¿Ãˆç>yaµíÏ[åµ´^γnb+Ù~²¦âQÑ'Žó6èRb¼Ãö?Øz ïw‰EÞõC?ÆéÃ%riÛO×­Ý'®ú<®ÊÆ?mÉê»ç®¢÷u7ZTÄ…fãrþýôDúô§ ššÓ$ĹþìUËß·à}ö›-÷Fh\"/_ly$yŸ¬¸•­«ˆñáàK|÷ú–"})úÖŸU¯¬}÷Œ_ÈøélþmôÚßyà÷*ñ1—È´‰Î7ÚÝ'³¥+ïVÓÒ9Î=f,i ¡•}êÔÜ ¢¾Ö ™çBBK¬‹0¾8ã“>ôМnÛ–ÌÙ7T™pWÂûý8º¦D¼¾DŽ=xº9®Ò}’¹rf0ÆŸÓ“µ«É;B—øýì±Haܰ.'²ñ [OnTE]sÌ£R~^8˜\;u;-¯3ïôS*÷zTAóËÄfñ/ÞGr²I=#èHK¹œžµÍ¾'Õ%TÙÙv¯Ê´Þ_<2Þ›_^¾zûôÞM2ÂòºÑÖ¹²Úö½;\& ã ó’³ÉÈöeÖ ¶ìÔ¯ÇôÞt`Íœ³pß8JÖ›PφÙ¸„Íûw48”ðf•›Åº‚ýÄ7¨[{_Ìeâê-z^óH6 Ø3.4àÏ,öi_Ó}èå¤fãþœ¢¢M“§Ís¬JÙ:"[¯`çŠøñ¿&C»šz~O¾3\&-ç΋î»&›ô¿<·õôÌ,Zóäºà“‡zÐNܱŠÎ*JçGT®= è>Yæõ;¦<Ø}Ŗλ·´¶øý~è'nç’[öžWˆý®'³§f“Ž ªÛ.½•E—œ×´xÛ™Îä0Û5U&.wñøe\Q6_ã÷k],øBrôã¾Ó­Ãýˆ+d̾£÷ûõË&¿æ·êØûF½‘~vûÖ†tïï½ê„`|ýÎ%i\Èç’ë<ŒËÎò;çÄæéF¿ Ÿ??ª²C}…<:÷y] çl’0áôäéègGÁì±Ñ=]É÷ª+÷¦cüÿdí§­EÜcÖ[ŸbçžØ>´ù¾h"ú9#¹µ*£ð 1®þXe“ s8ÀQ–p¾#!Ö^·²GÚ~t`ÕñÅx…ÌÿŒgmÎcÕ¡Ÿ[Nëá’DÎÜí=oX†–T­^Øîƽïºn)ScAw¯C©h²¦õâ6CLyÏ'w„uq­é½a¾Ïge“+Ó¦lÚ5ß'‰TØís縖ø­8š‘ø­Ð·sädMÒ?2¢G¥ïTTžºÞi×¢ü%_^XP­n–”ññ„ý-Âæ·F ý±ÇRÃæ'‘¾¦v[­%¹a£OåeÑÂû“oÔÑäÜî9£“Š>kxïÇv¿­°sTl­‡õ4.òóÚ™¢r˜DÎî8ØéÑ-±]0£bxïMZ-ùYgRª«¼Rüþð-"§Èߊž7ÛŸbyž«ùñ^K‹s~Üux^k:$3‰$ìû´«–ôw k| yk[éÓ©çr&Í×-éÑCEKÏÐì«7:Äôç÷»ì ;wÂÖØ{„ñ>A?×}Ýwíª¤&üq1-9ü ÚÕú˜¯ ù>™Ùú}ò@•À« 6];_Âø—l™Ÿ‡¹Zì·E¢Ÿ_l:]ÐSM¼ Wx›Eš/}<ýSÍu¹ÞÕuUKzi“×Þ­è§ÁÕ³¹ÖoŠx_Ìoü|DcÚG6úí.9{|fæR5!Æs9ãüò7Æ»F,ê*wÊž÷Ô«¦ *z¯³ë`|<óõ0Ú=”]7½ù5q5n°f‘#oJŹågÑK_· =,¡ÆýÉ*aÝ.Ä´îÉÎð¿¿@ÊŸlc±ÿ`U*WöWå•éÕ„Û-]¯Ì"ò vŽé¯³è‘ «/7gG«Ø]»ù0–®Õ D]¶hšå?~?Ä åω¹YðâDh¿¥´LbxÙdrloÙÛ’¹YÄûà²^-Þd ûÜ.dÎÞg¿U½KsŸlR‚ÛËÎùðûÐü8 í¾î1眢J2y¶ýZøˆ,²²mÊ•†,š³ÌØQ½:“ÍÞxTzkâñ<ǯƒåKw8- _`o-Œ$„ñ3qö—¯ÊºÚ ™ü…_u³cÙþgË‹MQeVŸçÔƒ¸¶ÝŸÿ+ò)·ªñ(oj‰<Çîãܱq²9XÎÝcûÉ$6î”KCû,R§Lý]*kizÛNist½I¥ržqrÌ»i¼ÏëD—ñîØ~V­ò¸Y/_›ö™ñŽöÛ”M ùè“L¦\þ7'/“øååî©«¥h¬a[ŸÞäÇ!ïFVûK³Þ|œ´¯Óʸ¾lß‚ç>5½ßÌ9q‰h¿±q›Lê‹Õ,Õd’ÝK/ê㬥½ls_YîFtýúÙÉ¥]”5VL1ý~ÆIn1zÊê]z7‹ù¯íN8ëéØbl2¹±m^Ã}™Dš2þòÕVZªèU+ ¯cg‚\žr óù{Ä»TÙÞS)[Ÿa¾bã%–GùçË_­JçÊ õ^Ä?K&=¹iÕÜL’7ú]Ü w-mýìD“wÔ¸Ø6¼ŸÝNEí†WýÉyep þ#;ßÁöøó üùcÚçד‰¾ŸÿßA™Äçôí?Oxh_‰HAâÚÏ6]UÔ8¼½\r\9ùöÚ›úç¸ò™é¹šï#ËÐÏœ·Ë>¨£“Éâ“qÉ$I­ÏmxÔQK‡=±¾Ð¹PÚ»j·#Ûª(¿^P4ÿaþbûîì*ãôY¬Ó¢~ý"™40Ü3Ééï®§ !o%·9ðÞ¯{qgdÿ*EσÝ7vÞÝ76_1ç‰ËÑÏëª#f»§Â7C#—gd¨¸ÅûkuÒRö{~ó®/£¯b铦ÉýÖ~ÍMï~Üçlš}~dË òlž'“ª£Îžx Ê “ç.<1ü;- ßY˜å:@D¾Ÿ±nrÌöXÊà ®ó7)ó;Ob±Žvk7º“I3}ÿ8‘A¾¿a?§µTKÙ¸­î€m;¦v5£f¿›=¶nÃæìܘù8L‡~.ÿÑaéæ 2`Á;§ã2ˆ³måã˺j©øRõËÛåíHƺšëG×ÂïÏÛ”:½Kˆiÿ…íëóÏ=]ÊÆ3Ìßæóx«2¹²é•Ûºï¶ÕUÞÖ’Ý]3Hz¢}ÜKK›kmbRŽv%eŒCc鞀·ò[Á”å¶ßÀö ù~ž›Æ¯çGÐ~Ò€Qãl4¤ªñFd­{R©ÌAZZ,j1ä¸Ùaÿ¹¡ž±tÆZë{Ï´S)[`ï;vßø<òVÊÎÑ[p½ÑOâµ0}rònž‡WDÞ=2\Ùü|æhŒ3Þ†öð&)yã¿ Œ¥}Ê_8<»`Ši?Œùž­ç°¼hôÚ-°¼fTe 9§j7bVò=’žæýyØ4-}ô˪¨Gï#N|f,]y=éäž1“M瀙ïyÿÝ1ÇØ¾ºù¾¡ýðyOC:OzWaÇ=Riú‹Š9Kµtá&.Á "†©£vbé‚IÜŒ Ðô¾f¿ŸûØ<¬&»õ‹åyàHôÓdgís9®2³p‘V5å9™0~ÀV-]¯={îÕ ²"üÙÇÌñ±ôC—c6¿šTbÿÅãü2n¼Ñ'hÿ˜=RsÔ¢ï½î‘ÈfƒŸÐÒ‡¢¹‹W–éM|súN¥wó4ù§&™ö—Ø{‚­£²õgvŽƒ ëµèç‡Ùu¦ ÑçýåtìØ:èJKç½O?Ø’v!›~ÞHçÄÒV‡2‚M 0í³Æ-œ~lÒ~áÇ ¯Ž{Ñw æó/«²¹²jVõç¼ûQCøùÜ=ÒÉ-R\÷¤–¾™ôÓÊsïœÉžeSGkÃcé÷[­hÊ÷Åý8ðᦩ“[¼—ž›e$í ×ÉïçŠÐÏŒ«U"5ÄhË?Òɵ¨nñ5Ni)›¯cžÿo÷¢Ç»M6½·,×w4RÆY7ÿ>C†öÞ•t¦!ÕWNfL©ù鮃ÿàù̱tcèûŽÒá“MÏ}ÿSáG?òÊôûþ@»I‘5<ËÔ÷ÖÝós:×mÉ™øÝ¥N;ö=͉üpò—}õ¯ÅЊwÆè%/Mã@~Ü÷T:¡þ½¾åà Mç–-Îë¢ýÄ2÷âæ¢ý$—N®.H'­¿¹M~FËÎá“„Œ;÷­¡i6súýâ^Ô>{l>:Å»ãÜëïMßI™¯D¢þ{ y/²´ì˜t­ˆ*ëó›–zÎsº'«+±ž,[CC ¿Ô( Àt®œÅß³÷»y¼&¢ý~I;'>ï«!®´Ò•ˆîédùè› ¥®cÜp¹_¯ýó¼H©Æ—6‹¡‹³z(÷þ@Ùü‡ËØycvή–ß{ ›seª×$òº×À‰/¥“á;Û\+Ÿ¡¥ù™nß=ÙÚìØþªê¾hͳûù“Jœ;dùƒŸåJw|þ¡AÇ[.絬Êù|öA›—³>Ý%ãÄÏ÷æhéæ!I:ûþýIï·n+o‰¡y¤àlØÓýb~gçÙùP~ÞßÂbE„~†öç8qûöröªÖwIçmÉ ýßk骶Ë:vÝÑéªÜqYCGÝm^9s‚éܱ÷:›ol-ÕåΩÑîó%úÉ0ÂiQˆ†|ÞxÓæÄ]âòKO·üÒÙtþÀEŸ<×y—® öôZCgOi?tÌîñ¦ïJØwüshgq¾ßí6I CáwÞ%íuÊ [ªfS›yI›Gv%¸y'ž¦<ÕaB‰ïغ‹cÛë1s\«·%윭Ñ'èLjU_¢!Î]2/̵ïôÚÙ4ui•à÷b7ò`‡qá•+·ºêñ‰”½§Ù{„í—°øbßŘç‘Hôsýiœ2}®†ðãó»Ä. åA`lzÄsTÀýuH\ë{áªË1”ß'ö§ÅÏ/òû>oÞýÓw1æy%ýÔ²,¤C„†49eê¯Mî’&²¼i9èGñ²ÍÞO—ò¤Q?hÜ.‹¡Íª=ø©ôšI”Û˜ïØ8‘}ÇßOËï|tè§ûJëêý#5¤SòЭßÛÜ%?ôQ}(úÙý¶ÎñéO cxÄІ“eNÜ<Éô|˜oØüž­Gýa›+Ûß÷ó†Á4ÄaTï¼ï§‘dñÒÖ6h—÷sdÂÃ‡ÜÆ\ svŽüåÕI¦÷FñýQþ¾å ß_º ñÌÿ~úÙáÈ4ÄÇaÓ$÷ÓiäöòVQSÑ’¥×€4^ýó•óÑôøœ<•O`€)?²ñ{ÿ±óLó´ß9á|`ÊV Ù·qÚ‡5ide©œ÷…õ²éŠ¡å´!nê‘K\4ÑT¿ªïŒ?Lïs–OØw=ì¹±uAó<ï‡~âR9ð»†h¹Ï/ýÒÈûj1íO6ΦQöˆ®ÈH­ivÓOަ{ÚlŒ²Ÿ[ô>·<Ÿ¯3¾@»Cå¯ÚOÝ©æ}iÄ{G³5Ë[¢Ý7yK3ä½ÉÉÕwæ4?MßUœyñTI¦ñ4óûNˆ}/Áæûß{ ŸóÖã9,‹q¨Z¹»æ–UÇÎÙ”ûZxÇ!rxœUÔW¢ioùöá§Ò&šÖÙ~›ïóÏ%OÚ:hŸõ¹2nç Ñ’z>Ñ ^K¯›úªîÛTb8^¥0y@6µïqâ·m¥“FÇ´]dï£iUc˜`:ÇÌ~/›²ïx¼sò›½>ÝÖb}G‡~žž^Óý8Õº$»›JºTY\à:.›nÞºg˪]Cˆ$ÿÍÆµbè“‘ùòYêq%Îu0_°óUò…"Ù²µ]{Ÿ}R>Wvïúêùîx>®_­>•J¼Jݾ´eV6 +wÐûÜ`ÒþÑÏu.•Ã{äÔ­ÀãÇÆ˜ž;{NlÞÉÖÑËx7ú!w˜Œ°spFŸ Ÿ¶{£Û4äTÕYWS×¥ý±Ã5_¯Fþ+=~˜ý ’ªYòSîŸÑ´ó²¤ùµûŽ6­·±øbëlÏöOÌ×—dèGÆ¥EÄAþ¸O7;LJ% |2]•»²éçRwz¥ö%ù}†-óH‹¦g´è¾·ƒÉ‚¡§’,Y5$ñ”‚ùü¡H"Ô”ÒC¾0c"$êr2†ŽR¨KÇêû:˜ñÅ$fõ}ÍÛÅ:âb âüE/¡¾/ãCü_Œñ!ü:Õ…bõ}¿ÆãêÒq+\m(.–¹ÿ“ÜøïæÅRN,žÿ.~-þ]üRþó³*™ûþ»|÷?ÍuÿjžûZ~ûOå6;áÿŒƱd;‘«¹É¸±ŒÕ 6¯wÇê§—ãÖKð7ÈN`d§pìD䳜bõƒ¹ºw:wÃêlÊ¡œ¯ÔÙôBÀª ­Ê|¼ñQ¬^yqÞ ã%Æ ¬Vc“qn¾T§\mV£œÕÖä70ò…!±Y}rVSÓœk€ü`5$I”ÍJÖÐT ,DŽ}­v-ª!ì%ð¾8ù µ„Å0’2´¶d^3†9óZyÁd*ÈÁŒÍPœñ¥/ÆeP˜ÕÄc5…¿Ä÷⸠:È‹ðuñ¾Û¾ÛäVÿœq›ƒð{uV<Û‹ã28ìCM–q½Ìë ;”åöß,ë ‰Íê '~¡žp¼À¬‰€ô\]ay"$B G@zȉôòEð'Bb@ ?!Á _¨ñ.p%¸h…PïÓFIj~ u?e0Mdã„C:ÈK¨n CéÌ‘¯ýÉÊRCK)˜ËRA0Y8”îT’ ›yÀx‘-Ì ¥C0¡2@~0£’µ@³†cÌê ‹Ì¸^fõ„͹يbÌI1fMqö¡PO˜qþŽëŸ þB=a±Y=ϯq½â¥<;[NøØåþÏÿɼøÿ;'~)þ;¹ð?ý¬þõX<÷ý§Æoÿ/ùî_ÉuÜ3޲*bté¬y~¡])K¾k”YÝbä…T™Õ-ÖA^Fä °¯Ó9†!S_¬~±Ê©Ä3gâ!‚5ÒC>ÚxH„À•C98!ˆ# =ä‹`N„Äh…Y½ôâÌŽYè+ÔIçx3^à2Þ̗ꤧ˜ÕH·…‚yÖ W#] ?˜C IÌj¤Ë`”(ȶÏ•3?”yÀ<‘‚üÎŒFR@ÈOà̤«ml cù µ%0˜R0™9Óš1eÌ™Ö9̉`ÀðbŒˆâÌ-¥ÀÜbœ Lª„ôŠj‰·%‡¸“Ú>0±êÛ¸îÛ¸ÎêŸ5® ¿‡ñ¶8V„Hàæ˜ñZk+òA°Æ—áΚá·B9Ïßð¬mÈ~ÏZ„€Ž€ô/;#¸ãZ#ÈÕ®€ />#èc"øÕP &ð‡!1Ì €ô/L‘‰Ç"ô€A”ãEÀ(jH³„C:È ¦QA0ŽÊ|` (È& ‡tÌ ÙŠÐ7”yÀX‘-ÇÅâ!ÇÆtN%Y­é Æ‹‚ì`¾pHÉð`"#úC) ©0ãäD æôƒÔ'‚ñ¶d0k$d[Œi­4ãæ¤@ÅØ9ÅY„ÓZeƉø;ÞãDpLk5$ùáox[ŒiAøØäþýÓrã·¼øïåÅ7'þ«ùð?• ¹ç§²âù^›"ò¸ƒ¥,¹¬*È¡ wÞƒ| ñC9î þù Xãm¹3#<·Z‰¸ Èù €ã!‚8ÒWây9‰­€|Ô‰é!_x"$F+ ä‡`WC¼ÒC¾_aå¨#p¬µÀÊQ@9ÈX9áò‚ITƒÀäX92& ²ƒiÂyVŽD„¾!Î@þP ä#) È †RAvÅx¬‘-L ¥C2˜- ²…áüVŽÆS æóX9³:J‡Ä0£2¼0;˜2J<8^Ç¼àæ³ÅxÕŒ“ÃxÕzÈÆM„Ä0¯¼ÿâKì0Æ¿ð€±#!Ç4ãZ|‰!q'ü¸ê7ñ߯…ÿ'óßÿæq¡‡Ðcˆ%Bbf¤·)b°2~˜ò-Ã}·¿!`òýFµÙ_`T‹Ð Èù!°ÕÁ­ÜJ$t¥ì~’ è•BàûC)  ÙÂÁ’À JÈùÁjHÂ1} $ƒA"“øC)f‘C9L‰`œHùÂ@*È&’C9ÌÙ‰Ð7”É`¬(ÈŽcý@‰˜ãý@9_`°ê /O9À|r(ò‚ £ [1J‡d0¤Ò üŽŸh súC)ÄŒæ³FAvÅÕO‘1Ò!Y1.Pq¦"Ç¨Ž‡`nùË‚ìFu äó+…ð5~cT+¾åÆo¹Ñꟕ¹goÅ3Ì" =ä#°E¥ŠØ°×,¾4÷í"þé!ß²Ü7<Ü÷(ø¤‡|m¹ó؈!MIÈJ!˜}¡DHŒ V@ÈÁ­†$p%d€üèjH‚`W@.G"èÕ¯‚ßJ<`‚HÈùÁ ‰†P@ÈOà*r|j(’p{½‚Q8®¢ r€aäPäãÄC"«¨ƒ¼`"ä#É¡tÈC„¾![˜*J‡d0—ÒC>0Y<äÐÄ’+ÙÁtáò‚ùT  ¥@0b$d 3CjOé Ì© ʱÑTŒ¥C26 ²ƒiý‹ñ©maà`3>µ2@~0´’ÀÔò‚¹U_`¦E Ü´`(’ÁôQ‚ñý 5$ù ;­8í[nü–åVÿœÜè%´gàî ‚R I˜ È`SÄ• H¡4÷m4þI°JÈP–ûÞ­$?Û_`h; ƒ+ð,m Zù_ìt׺ýM P¡ˆ.jL¡‰Ñªè¢D‚¨ Ñ ºB ¢ [Ñ‚B;¢a -X6͘¦+ÔÿÏ9ƒLÈ}¹÷Ý÷Þý¯ký÷:ÉkfïoÎ93ú6µøZÈÒ2Ü@ ¡;€ b7?PCô ƒðM ´0€Èa3ð5Ìà`†0?PÃ6 ¢„\m ƒIL T0‹¤=Lã‘0Ž „€ò%Ldé@3¹B‰±ADÁXn €¹LÀT0™ ¤—ûsæl*ÐÁx „ù¬ è`B7ÈfQ0¤„€žåFÊaN5Ljé@³º¢Æ› nŒër˜× ‚ &vŒlzGž¤ÅíJ˜Û R&÷%ŒnAû¢˜ÉZ˜ßd(à*;+FÍ›ln;u(üùŸª¼&þOÖÿª…ïªÿJýû«ÚÇkÞ·Þñ:÷ß­q·¾ýÚ^×þQM[ÃÎW¸f‘ „€þ¡Ÿ~–U̯ ý‡Boü â²P6á;íÐ(ˆ„Ðl ”Cø~"~T¢sÏF <PA|v&@#ð5„è`b4?PC”v&L#ð5ê2ˆÔ@ËÄj> ‚híì¦.de›@¨!dÐCÌ D­²‚t ‡¸½ ·€T ƒÐ=@ ±‚w9DoAá}dὈ_Y>s¦­(` H:Â0…€æp9Ëö5Lâž÷ Y¶À”0ŒAŒã ˜ÇôV¶<,ÛÎ&Fàj˜ËR&SÀd t0›Èa83‚(Ô!jø¾É¬5ÀŒ> ‚!íÌ”¾÷ó³÷ó³ˆÿ¬ù™žýû‚ ÀÔ¦‰Óü@ ‘Ú™PÀÔÙ…þ¢5¾#—ÛIJ¹•²9§˜Ñ­† @Q›@h!n'CàfZÝ ä» €¢w9„oA¸&°€Ð N ƒ!L ´0†¤× ä0‰f±ð5˜ÆT0Ž™Ç¼ &²0ÀL Tbl t0–(a.3ð5Lf!`€Ù|@ÃÙA:ÐÃx^ óÙ@èaBPÀˆ t0¤“™Ò¼@sšAhaR ëX˜Õ”5Þd|ëa\7PÀ¼ t0±Èad3- ír–÷í‘0· ¤=Lî‘0º ¤ ïÊbîwDÁüN G0?P£8€ ÅÀ¤y“ýí ¢Þ™ÿåÚøïž›ý»æe¼îý»çcÿŽ÷öœŒ×´ÿÎ<쟭_wÆë–ðù|Â5€XìL0à*ÇÎÄc> ‚ˆìLHF ‚˜ìLPFàjK a9…ç—¨!0Ad&ZˆÍ dœ „µ$ê“ â3ÐB„N ‡Í ¢ Hi~ †0@†š¤@=2-„êÞ-†X#Qƒl  ®¨ ^+HzˆØ "!äT1»‚¶€Ô2¢¨ À "!nDîJÝ Ò‚÷ ß¡@ݱ€ ˆ‚Ü@DÂV …œÂó˜Á+JÓ*ü¹¾®>'^êwú´Ã´³wÎJýRŽ%d«>oçÊûåTë²]1ãB)GÌE¥@ê Ãû¼ñ>žo÷aãýÓ„a”gÅ¡õŽ'9wï[W³ÇY²bÀc}³+WèàŒ6­:1Ÿ¶,ýy”ñø/þÔ·…÷½ûZÞшý(+‹7âM« æƒj1N‹åû&µ‹'v[–­{–Ä­¹õû€ô+´cÑ[};’e‹ï4½2*–vh·#Æö ¿”×Àûõð>.bßþWš).!8¤B¦¼ÆÙÔ:®æ‚ÏâIFŒSþ³$ØmùÎø’èÉ\ölèБõ1ê'õßãýNx¿Pž ÊsÒÃû.Zp|Åâ³ËÆ“¬Í*ÝÿîâbÊ &ñV<Bì³Y)s?3Œ#æ}#ù~ÜÙ†dô,ŸD¿ÙÜê» ÛÃÂ:ieŠÅÒGëw‘"yºSž‡ÄsyŽ¢ØÇ%Es³£ÐQ¶f¦|8/ÆÙpÎæ9zúÙ´K›õgˆ)£!pÝáüÅ盓¶ƒöÆ>ºè¢ ë ;QžÿÈsUy¿:±ïömÍ™Ëcë^[U_ÊËÎð Æ3qÖUoÌ1²¥‰Ð0÷ ™õgY•úIôh…ZSWæhD¾R§üÝ­.*öíHy?Ažó"öWyÀry^±~Õï#šá›\iÚ’óïuL[pŒXï/ë6¶ë²ú£Þšµš$úñµ­îûÑ‘DHAZí¢Ê¾ ÷µÊ]%p‰ýLŸ²Ü¶Wšfo^ÖëÔ™ûˆcœi%¿š9ãóÏÇkN™¿kœDEÝç%mj]¾½n¿‹&çÒ98 §”ãÌûêðœ[žŸÂûGeøÇ?·cö ß#M„¶êÏõιî¬M’(ë/´×ýO*ÿÕEïï¹µ¤dJ)gû†ç׋?¿«É”Cão{œ;Ù>ðiœ'éàîû§É²žq:á÷gýa©'Ahæ¢Í«<ý`{“¾ª/¼Ÿ?ï§ÃóºÃsn-§ëŠ¡Õš#}g_ÖÄ$œ&Ó‡Ÿ£u0N×—=6uÍFƺ_%öÅyÚ®ÝÓtV–~Ñ7 õÅäy×á}‰gÜW眑#Í‚QŽN“iÏwM¾óU1²öëŸòV&Åf̳Všï¢Užo–½ÚÕïO9Œüóð>Qü>'^/e¦þÍAŒ³ìîØŽ}–øHFYýi’Ïy§PB»$*æÎ5&? ÿv„‹æ/*tní+鋇÷û^’êex~cDnžâ#/Ÿ•©«WŸ&%üÔç^÷$ZÄ]Ý£*ñ9é4¾áÁ!bè…Bð™áOýàx®Ï=ýóI¦~ŸJŒ3zÏÒç×"}Dˆg»­8Mfôqô¨eL¢ÎV¸ª8ÖŽTÛ¶òÞÆ¸š’°åFÄ^¬`Ù·ú7ÝÔ°¾n¬lµLýú´G¦ÜùýÂçGI¯×›W<>EŽ4©ÊDsSñêè)Èß>,^±êYÑUöÕí)õ‘åýàx¿AžSòeå٫Ϫž)ÀqrMh¾«÷™£dœzw«¯ÏŸ"¥•›0+‰Þý|OÕÉ÷:|U.=˜¿ÈEÅÜ ’øuâ9]ü¾#~ΚDœçˆõÙ‚q:Ð\=·/>J’[Äþ|Ï)rËèþ0vy-Q©gù;-Ú“2îQÆùw\T¼_u“üÃ}º²üßÌ(tUêŸøLh'µPÍò*ˆþÁ8Ÿ˜VÅ¡;Jvd›öë¡¥§ÈÎù- 96%ÑÑëÿØÿqÞ(¢½S•+–žXøeÎãºJõ€÷ ãý3Å\Œ æ•ý× [÷µ`õ[¬Ï^ŒãˆÛ÷Gž1/ë©þI·Á_nO¢Yö[²¾Æ*5m|½Â³ÇÒQÁs£WÔKýáyŸ-ž'È?®îõœ÷Fu`ýXÅúÄ8•Nvhyô\¹ÒUèøxŠ´í¾t@õÝIt\î ³Z>¬IÆ}Þ÷ÊUûVö’îü>Êë&ÏUsukfÒuDž4­ØŸ?ŽÌY;êãµO‘äÖžWØ—DKÍ>sÔËŠäF¶@Ë›]tLâîÇ'÷–ú’ñû´øy¼RV®³Lýe1ν’u*îG|M“.Ç8EÖ¯-‘PÑ›DG}«®×kYM"æÇ ^ŸŸ^zEû/hæ\C¹”-æù”dãg}ƒãoÈûàU¥82¢ÕøØiwýdÌ¢H傸$–‡¢! »äÿêSÜ7óµìvënƒžRŸ@ž» õkd}Ãó= 8þ©ù½^5}v„Ýü¤\#[LKT»m-õú–dü­Š5e.½q£ø©Ïn÷îÿ<÷‘ç:Œ™°CÝß^‹õ÷¯·ÇïÝìBÏø#¤F&¤Ó?Yœ«AÜîËItøŽÓ=fvoG2ÚíqÑ ýOÖzR¡§t~xž™8?N“êdx~¨Ç&›{ܳ雥F­a‹üdéÑeóßEÝÏÈ-Ò‘5O&Mkâ¢%²?ìæëÕýOýôø}…çÆð\³ð¼?/ƺ#.[x„ ß¼ì¦u‚Ÿœ/WjÙ¡×Iô“ü36ÌoØ”¼Ìýr€‹N8óèB–ó]¤~Õ|þ2{oÿ‚aišÈîöĬC"2ç·àøbßè#dKš«p ¿Ÿd9µx”,HãlO³µŸ #/÷hFÿºÑÅò;JýyÌÜÏô¦g®íþ“C*fúyáóõU-!ß/K}Ø¡…ŸŒûäVÌã|AÚrøÌ*¹ë¶"ek Éj.)ïþí¼GÞ™ûœ÷ÎðŽ_»ÆÌË_ç>B¾n=Ê»¹¢Ÿ”ÍòjZÞBAzÝWpßʹIÃÃdØ­S¨‹³Šï²¯j-é‰÷CæzâýûÃÏ“Ç/û}ï[/&ówÿ.!‡ŸT:;®NÑ ½ßUh0ZŸ<0Bv±ÜÕÒü‹¯ÄëpQÊ‘Ì}ìØáØ)™ó §Ü…rgso:LLæÆÍ†^L$Ë~Ê5q\ñ ]žÿ^«Kc*AW‹á¢g†•šyàEs©~ðãˆó–d)Jì(æXpü]¹Õ­:Lz·ýi۲͉DÖcr™_<¯i%²éÉ-³‹ª]Xè½ÑFºò><'†çXdøÇÝÿ´žq‹æ0)“p§÷Ý…‰äÛAŸ[– ²Üòr´ï–%¶MvQ!e$ÏËN´æÖs¥û*¤úÊçs¼?êþAÏœå™ó/½§²þê°&ª^Mƒ±‰dQõ—­;â:lûvéÓËEËR¡;ýÄ\Tœ§ë)_ßòuï_*Ö¿»ñþXYôŽ?­¥øuˆÌC6*I"i—mrƒYŠ ¹¤Ê(M•Ò´ZÆÄÙEG¯*‘Í:¥ûŸúFóëÌûïfè?_šöÓ›EŸ/øá™8w^áÁ¹I\`óØv8®?¾_ޝŸïïf^:;ÔÎE[Žp}é½Ô?–ïð~üáùJwJWƒ/­ë!"êó$YzçÇsdʰ>“.*ÞçõÒ}š÷wï7~iÿß7yÞA†þ1NÎßÚÇ…T‡H½è3“]O’Áæ}ãúþTàË6WGÕ#_ËÜs1T\ïu—ú6sýó¾ù<ßTœ_±õŽ?Àüûà­‘Så·®»ôÝIROXî– Ò‚ÍÔdoB°8iÿÁò*ÖÃnRþ ŸG‰}I#ן'ð|£ ýcœ'æ/î“ýÉÛÊ>äy·“äú‹¬Ë+• ÒÅ;ÛÞ÷8[>åw\ï5+†F mkã:Kùü|ˆç?‚å8>ÖH런y§ãô¿¿ü`«l‡X.ÞI²fþ˜QU*idv×´·"¦¦B²_ Ëí$åcð\pž×Ëûì†÷·öâøCêÎ;dxul&¬0N’iÉê>¨¤µõŸ”\~¦5i“â².K¡×žÖ}ya^g*ö³ÎÆæg¯¥>Á¼o~†þq\ñ~œìlm™rýY51û¬¼õ‚tcõÄúÕüœ¹–Gõ°¤‹Æ=ùüúÂ.ì~ÀÏû¬+ë›,OÓ(~Úé=v¤Ï¨]Ù´íÉ{®~Lt£ =ÿÑøv‡‡5%Kü_ÜÜEK^5{í¨7çAì›…ÝÇ>`¿÷=ÍÛ}ÿ3|€q×(æ$ñÖ\ÚµcNúÚA&jñ{ïZæŸ4´é<ªÝï«¿sÑöÛ:Ê_±¾e!¼Ÿ?ï÷>;8©Ê…­bÿ\-ŽÿkÝ#W×ô8H*˜›+¤>Aö}q>i”òÙKüÞ%ÝE—,¸^~Ñ~åç™ÏÛxN1ŸgèÇsI’ä³9|÷YRèl–Í‚tÁóÁEJ^,Cîœ_y·Ñ}Þ¥À…Ý»²uÃsMƒñ¿:ó™FÜo;£áó¯ðþ¿Tûãùz]?@†-z¼üû[ äçr7ÖþÚûgܺg'k´ò’BÎ#Á¾“9–-?”¤1M²nvCGļ6}ñâipgëÖ4³>ŸJ}·yÝ ßO±àøâåò’s‹4ò'c Ö¬»ß¤—&ø"çjM:eÜP]Tœ×´“êÏãù‹úÂ÷›$M¹/ù#S~Æ™¶|NåCJÚåêý¨Ðºãä;˜Ç2#HSçîÈÙaLS²¸ÿ6ÏkŸ‹¦æ_{³ðîŽRß§÷‡JŸ#|åÅñ¿l¦Ê[½$%ç øÜvœlüî¤Ú8+HO¶9—jQŸdÏ¿iH‘3.ZôX©Õƒ¢ºHùL¼µ¸/üXòIø¾sP¸Ñ£¦í'wî ›wÅtœLIîu(÷Aª/qòÅм*rpÈÙû§`6öp™âQoîËü|]2ëSoô=Íù—û/ºº<‘î—¢XÎc4íSý3¶ÇûH¡[¶¶î|œxºÝˆÝ=7H½»š}ÿ1Ÿ¿¹èûäøÐÓîÒõçñ²}‡,ç󡆯ƒxþr†O0Ž"»¬Û>²odGûSõqRaD…à„yAºRµ÷ÎʪrÒ}m©5§pÛ¼;ï¹E¾öOøyן5¢u “ßø„çeøã±´¸ušî%#?ÜT°a±ãdvµM“vbœrŸx^.•ËH\‘;÷›4pÑßÖ癹~rÊ}xõÓ·Vs>Òˆó¿ß5\wáϵ 8þµ4jUa/iV4º_å—ñ¤!=13·-HÅý‚œ¤Gõâ+vrÑBóû1 ·tŸæóõ®•9õ Ç8­ÍM¶ 8!½¾M0žd´ŸÏë1ÖIó:ü4¾¾‹žbç_ôî{¢ŽŸIó q=wGÃs*D=³ý&Œs§Úõ [Ÿî&§Sæômç'1Uºèû, Ò4ÏØ²Ôúäàóè^Î1TÌgí&ݧÄóô‚­w"¤< ¾/ÙxÙVcƒ†õE¿`œfáî&YÖ¯nQæX<Ù[£d¥üKƒT™î88³ Ÿưç[)¿óyÿ<Ž ¿ýáãÇRÝgyé¢o0NìWW<Úÿ ™ýƒÈOž7ümm½A*;ð(GÁçÍ¥ùŸxüNò¸øBò?ßï2 ·Ó,ïã4mò„4ýúG2"}ú±ÎâÉþ:H W¬ Z’½nÔØöÆÐ^Ý×lO(ÛYªÇÉY¶}m_iøs¾ŽáëÒ ¿àøâüÕCž&,ˆ¨³3žlÿþ²U½1Hô.Rûj¹(Òí3C©O¯ÇÐ_f‘³JÉ—bîÖÒ>0×—¨1·T‹ãû+nv‘&[ Õ®¶5ž¨×u˜¼06H-?.údçÞ–¤âèª÷¢s¸h“TGkõ£®ºü÷çó7¾ë°x4`œmµìùÎüLŒ åÓ²­Š'¯PvÞ¤gF=×¶µ6#YfêÖÊçbó}½tž¸ÎZlÖþxûBHÃÿæë§„³å1#ulÁ8qÚÊŸIÜ:ºôàÔxÚ(< Ò†£ó}”ô¼!Ñxr§|Y­¡ëhÈÒ¦»4¯à¾ä×Eô×SÍöæ7zÂóq3ü‚q e¯4x=n'ù~|×Ó³FÄ“Å[Ý4îÒýc>Ÿ}+T )VðDV½‹q½\yçTOéºðu ŸWð}­p?zqüÊ›>ö7 ì`¹uñdÂçû‡gÙ¤?$xÝ(O$Ɉ\å¢âö¡AZWòëÃÏÓøiß?~Qú‰ä{ž—–ጓSUuÒþj;H£ZL›ûÆÅÄãOoî Ò®s6EÏØ^“Èç/Wó„‹~ÓeðNo©þÒý‘×}±~…¤±i€›l:¸*ùD±xâùdm…´ Öã²?šoÖv'ù‹W]±îé÷羟´ëºoĸt ¿K9´ì9]†Opüs½›ýVsì’º*دyžxR]‘e÷Ù;AúSåw¦=èÎÆuQñ¹NoÉ'üoq_ì[§<”êKø{^Œ³rÅjÏÕ¶›ÉرÕ7~OHÛÓ«ÍÏøþH7²ù—µ¯¯c^,ξôÅïû|>,^çû®óLùF§×¿·:KæuNú£ùGñd¾îVaS¶dúzÈŽAc÷v"ϳíéûÛí7½§fÁ‚ÞR½äŸ‡_®cþ{dÚ¯-”¦nÞ¬æø¬."+;»öý¼ñ¤Eíã[Ty“©§ÔôÛ<íÈ“òîú9]´R#á‰G_IÇüx|ÁçMüþ•á?]'< ˆ&ß8‡ДœÖ9gÖ_Q0™Nèzõç£%š“ÛÇ^¬qÚE« ¶l3@ª“üxGãFe)•ã‘ôÜœ?4v’QYþ6Æ©S)Ûåާ䛎ëìÈO.<¢ÏR4™{˜YjoCRÕR)kŽ.Zö诵¦õ$/îo^Wø:ë÷™¡hñÝjˆ~Á8ç64䇯IÂw£ *sÆ“äø‰ST%’Y~r}Ò×y(ç® .j»´lLϲƒ%=sßðóÅç¯|šç¼døã,ú¾g¶šþõdLZüàñ¥â‰jGÒ$édšPM¦­7OCÎF–ÿñ\Cߤ{èYÁÒ:Oüû¾¤/^·ÂuìÀñ7ìçóÏ\Gžì8ÔgaÔÉb?œ/R>™Š9eMÉ‹bŸO1esÑñ¥…ÊÁ’_øçàû?¢®oHë»Ly`ç³\MpˆµDÌ»'ŸîQ7ì_)™žZ]rò÷ß·$)]çs'†Ži±èIö>Fé|ñ÷0xÇ¿&ù2|_(ˆq¾¨#Lé‰,ÛzãõÞñ¤ÐRMâ³ɴ̓Ṃ_·%û‡¿9$ž\Yì¹UYL?œ¦ì°íF’|F˜»h‹ž‹›=iß5¯bä<¸?iÁq^9l[Ñf‘-jûËÁ=ñd¼&kÚ c2Ë›îE6£ú.ï¢Ó ºøjñÀ?Íã6F>˜´&Ïs6y¨áÏOÂ×£ŒóÕ¥~¿2.#‹÷$Þ?•O®btŽK¦•ë +Ýžä~«7Ÿi\t€;¥ó—}Q~_âãñõ!÷ß߇ë ã([ÈæÕk'µ§Ÿüò÷ÇÉ«¬ORÎMM¦#¶¾5ï›îÒ}üƒ6§Wü§uß·çëÅ Ýã¸=gòNj»„Ù½½‡aÞq²lzÕöµÉ´Óµ»".èHJö e\T¶å‹‹&ýPÊ×›ü¼ð÷Ðøz:S~*ŽŸeã×÷'^šKÚ*•ž}œ;¾LŒI¦Öží÷§hO^û~™ÓEëçé0ú’òºÃ¯3¿¾|Ý‘¡{·dBE¯ûÅ÷DØÕmæ=N.iwß²=™–ŸÓ¥þÚíÈ÷»NÒ/žÄÐ+Ý÷Üi2z¸¤îcþž ïï—ˆ>Ïãlº5Ök›Nêÿ’´:ÒœLJ¿}jàždznöíJSwèȇ?üØl_0†flþJÒ ¿¾üýÌŠZçiøsŽðy€㌸ŸþSÖ㓉jyÄ¡7“ÑÒ&N¦I·¦ÍØ1¡q&M½CÅü°áRããñçªü¾–¡{w®vïæ>îoÉÂúK¾>—5(méý²'&ӹğ˜\£+ÙüpÄÒ—»c¨¸ö%[—…¤ýt>ÉÐ;Ž÷òѪ:äÕ(r"cú—@J;7Ÿo1™z.ûùb)=söÔÈ:ÛbØþÑÊë%_‰Ï§I> ŸwE(Ò´âsËþ„NØÖÏÓ ÙÞçôèÉ4úÒª䵞Lª1=ljÍ1TüïÞèž×M®w¾OÅëNø}Q‰q„Yh©šÔü]|Íî ä𘸹&S¿Ç¤,ŸKÏö+c¤¼e>/âõÏëø}>Cï8îÏ5EîöÒS÷Há‰~ùeÎòbÆWÉ´Ä×÷Úš7w&Ã>kªt!†æâ±uC¥ù¯ûü¹2×e¸¯ §FÒ3÷ç@š|ûË/†NK ¬®ì‘#…jj^‘½ë_ÕÛ¸ ‹cÖ—lþðHÃÇã¾åÏgù¾dxN¦ãäj¾{U'ÛúEFÀz9¸¢ù¢åyRèisàYàã¦äó|McÖrÑžÂS_÷pÊë$߇ÊÐ7ŽÓºÇý<ÓÇQÛç-Nü ºÉŽ;xþ:fÊüwË}Jÿv~òè.Ú÷ÀKKŽì&Iß|~ÈßsåÏøsÛLïÕbœCù×uüÝ8­vãæŒ $õPÞý³ ¤Ð#±S©‹‹„@F->ðå ~»MÒùçûâ{Îw5üߟq|qÞc¡Bšíâ døÍç›îãøü>z¯ûyc} ]zäð«MҼ놿ïŸ/ð÷v2ô_4M{¥þ¢Õ•7L¡e>È»ºÕ‰òéPY@ÿq ÝTé«É›rå!†„%®Y¦ºŸ6þöñ}¯×’_Åçû÷¤ü@¾OþX‰q Õûª&©7·hJ=»›@¢Ÿÿ\¸DÁÚ§æ…êåbÊé2õ´Êubè‚Ò÷«üjIù~?ß?ãï%ðõ:Ÿ†?/ÑbœASÛ|:ªð zX–|*.ÿ ²lã´‘“ áó”›´¤¥:Ims±óår1´~‹m-EGIŸ‡×kþ>NÆëßÊðÆ™à¹æéÔÀJöB³Æ¤~UãÎâŠöpC2TØ>¯Cså9¼íǦ£¤ÏÃë´¸t“ËׇY2½·iÁ8tÒÔÃÝfÓ¡/@œ ež½žr·D u÷Šï\¢_K÷Õª.ƒZÅв/ŸŒ ®)ÕS~_ãÏ=ùõáç­ôãóUªzØz㌜·>ߺy´vëΞÝv‚Ì?øUJ¹z6cûPGî•r<5(†¾¼ä_´r¢IªƒüþÌëvá £5Éóïhø¾Cø{Û^ŒszDŸ'‹Î§‰S÷l÷Λ¡ÛÂUS¨%qÇüO:’ãM_ÿúU míËñÙò.#(ßàïÑñýß àxu´Cœ¢Ð|M´û§åÜÐÛ‰´i¯OÉ20†j÷­Ó§ÂÉçü:ðùnͱ/ö¶›Ç×Í…_;Ã'ÅÒ´*úa']H·­˜vºH““ÄÜ2nèC’B}š—|~¼#9·w®z@ ÝõIÔ°¯ó½ñ ¿|ÈõÅ×µáuV‰qV(,ü¾çbš”äØ÷Ì|’üÜcäžä¨ºyþW‹G®Ô‘¹¶ÉöLˆ¡)Ê2ñEv˜¤ëÍÏ“8¾­áïidzÎ'_xM»áRJwÝJœ¸î$©[,i”.…^zýóåÝZ‘Vî~ø|K ýÍñ¤gÑÒsi~¾øz™Ï32ü€ãæ!U†i‹»·Òæ9I,‹SOýÑ%…Ì9'ÆR³19§^ Š¡‹¦ Á»#¥y¯ü}9î3ñŸ‹ë Ž_ç~ÞôuÓ—ÓdÏF{ož$¦/ûZŸBƒ»ûµ0«"}úÜîXîÑ_ó:(Þÿ¯I÷7þÞ ¿òú3s"É9me¿s†z~Ƭ‘¯*6#C«×S¤¹£éÔ¯>ªÞuŒt¿K«3sg“5iš2y}û]»ýÎçRJ¿ù ê¦ñ®Õ´Vᶃ?™šH¾ü¥Öǹ§ÐÚ[õÏÕ–l©pmêO'¢© jGí1Òú€×OËÌ—ä/ö»T÷2Ï€=ÇÆùz$GÕ)´^»-õ]…:’%¿d‡oGÓÈ»æ…IkFKë(~_åÇçúåû¸}*”Þ“k‘ø}Æ){¨ò_£5tÑå„Ô–‡ÉÓ~W~¤æúsÊ© ÓtÉ'm§®þ3Jz/‰Ï¯yÝæuIÜש"úÇ¿úÒóòÁ±5T›T¸ê¹l~²¥ƒò¥vj íuþþ‡ƒ»NYOܼt'šæÖ®s«Ï(éy”øžêM)¯×Âß3qàø¶'ÄÛcçZZýhùg•ü¤Ü€#…öþB±H9ûS'Ò©óJ±@4=í»Y¨ýgo|ǯ;¿bÎðéþþž½ã,Küå·Ç§×Ñ”ykN^jê'õó}¢Z¹$…ÞyüùçC_´%C¯Œœ5÷Z4íŸÛu5´zÍ|¿~¥áúâëÐðuUÇOâ—ÖÓè‰ë&w÷“Íû ];¸"…fý¥tëoF7%óº´J|T †ÒóòKGŽ–žÛòÏÃs’ùs)~½ÃëkD‰4m“l3‹4[· O½«ö“&3s–îéHaÏ7ë‘û«›µ.Õ>†î¿¸ÁÜkŒ´~ã×E|~›"í÷døǽ@„Àê4á¶µi]›ŸÌmÛh«}M õ¡"¿{ë—kõm ½a;Û䨵1RÝæuãz­yÛjo¸‘éýN-Ž›O¾~#ý){îšX?Ð3ûŠõë¸ßª‘ÜcîäJC/y޼¸ä#­KÄ÷ÁÓ¤ýf~? ŸWpü_¢ßS6´‘Ç`:á'Õï¾Ùº!…Šû?5Iú}§¥chÆ÷McþôÞŸ'ñ÷ÿx] ß´`œæ…ÙIeñß—zì'_/=7}È&ÌÃ7®ïÝ«}}òcµî…’+¢®Ö¬° uài_›ïsfzïÇ+tmp¹óm7ÑQICWzJœ"÷¶«Ý=6…NWµ§ítc²¯nñ*5£iåXZ!âõ›óÂuÉŸ_ñ÷Høs’ð÷ ¼çÙÛ¸rÑtƵ)]Ÿ"ß—ÿ¦è‘mЋrÿ3³G‘}„7n£é<]­u³RÆJõŽÿÞü¼ðçJü¼ñçrúÇ8{1Ûš~#š¾ê3öZÔàSäÇ^¿æµý‚ó3¯æ×ÕÊ´'â÷¢©æV³)›“æ«üý±.¥Jûòz/™¦]yvéú•Ëb¨øì)R·ïÖF¯¤ÐF–´Ã³hG"¾—MtùI¨4NºoJÏGÙý€/‚?gàï%gèãd¼é¢E?ìš·Ú¶Sä“N†^ÙRhöIÔ^­{b4oÏ~/&šþt²Ðúi†±Ò}óí}x~žø{—ü{™~À8®™b±‹}ïáiÒ!ñ“ó)tñü×^çïÆÖUÑôΊA“ÇÍ#í7ðß›ßÄuä#é>ÉGü~F,íоôdòâÙàQRR¨qY®]ç/w#Í26hb膯–D¼-]î7¾ŸÄŸÏ†ï³YpüggZŒm`‹¥‹ŠühéSî4éÐkÕTóÝ:¢×—‹–¥t%âs×*~y´4ŸçúåÏyùû$ü>~]§ChXÉ¿ÇÒ˜S1YZ5=M®·­ýªÖ)4GÅz³\u:“­…W}`hCËlɱ0åêh©nózÇï?üsñ}éð÷­¼Ç=÷êÌVå6Sý뛪>½O[áÁ¹Gp•>éT­ÖÏetdöÆRCL=cè‘FU†©÷Æßüx|Ÿ€ß‡2|ã.}¹ûÌÊO6Sñû⧉>è˜úÑUºwvã‘ù+µ”êÓwß ;nÙ>†òý~ãïýgòE©4í¨)sëýËÞË:MŠìÛ¼{HÞ«ì=R­ôûnŸX«æÖ±Ò<‰Ÿg~ßçþæóù ?àøÞ›3ó\KK,^î\ÿËiò,àé^¤ÀUúÙÓñ7cÈg¤c® ;5Œ¡%î–ÛUñò8iþÂ÷øzï[ñqEß(ÈúñÏûÞJï{+ý'ôVz}­‹ÿŸÛ‘¹_‰ñýJŒÀÏú•¼Ý7î¯z–8X¦ï©e=–qÂ2uxV¢qíf׿·zÈ¥‡e†÷YÒ²\T c9ײ°^rogŠ }‚• aT̹Žb}Mäa¹:•—è|G^"ÏÕ ïŸÎsuŒa}–Þ•kË󾕫£f¹:Bÿt!/Ñ”a9×Ú°\!çÚÍr®M,/QÈÕ±‚tÖ…÷œ³€T ƒ™=@Éú‡÷A ±l ž9fa}‚y¶„œeK„gŽ æ×³^˜¼‡:ÏÖ±³ÌÄðêáÙ:î°Þ(ïkåûZi‰øÏ«•2ö¹Âõƒ8@š€¨!TA¬&ê·z;ùvh e¹²ò°~tQ,;[Æúóœ ž?&äÊzX®¬…õzÒÊÅ~O¡°ÜÅðüì(–5‘ô,[VÖ÷éíü/ˆ„Y¬,[VÓ¸Y¶,Ïšø« 2÷;2ÈxÖDxÏ`ž5!ô üƒ Æðìß[YZ–5!gd^–-–5!dËzX¶¬™e Y6ú°~QVô0³DÂÐfQ0¶“™Û–Ãce=:yOuë©ÎsxxOuëKÇûó¼ Ë! ïž7áa=£L$sßà¿S'ÿ5ò]õñ_©o×Å¿SÃëá»jáß©ƒÿl–쿳öýº÷vÍ㵎׸ÿªGÝ?[×äìçÿUMLJbÍðþçÿ¨¿f ¬gâ3ƒ@.±o0Ï…àùa©,;ÖÍúž›Xßs!GÑRA„ê Ö·N ÁZYv¬– 7<ÛÊzj ù±–d¹aBÄ»2ÂïÈ 2 ÂûXV˜È ~S91[:å² ™3°yå>ð¾ÁBFX:г¼XyXîƒšåÆ¦²Œ0'Ë ó‚HÖ7“÷5÷% eé5ÿÜ33e#šYö ϸæYˆ6Ö+ØÍŒÇû y`á=ƒÃsRÃzc¾Ÿ¯½Ÿ¯Y"þóæk ö{…ëq:5ƒÐB¨N ‡XÍ ´­È \-ìrˆØ ‚ ŠåÅ ùÊ2…Œl9ëÌóx.˜ëey±Ö|Â÷ÈqßÉÄϳÃs²u,ã!ô±ð}13Vc˜Aà8> Â·±ÌX=Lãa™±<ã᯲Á<ïÈãá=„yƃÐC8ø²Ãóoüoea8BOõtÖS÷ú´„eàð¾êBv¢7¬0Ïyp²‚ÞG8<çAȾ‘(ôø´,üùß®•BÄåûÛ5òïÖG¡6¾«. 5ñŸ©‡áµð?¡þwæmÿuï¯jžp@™@h!4Al&ZˆÎ džé­žé2ˆÐ@ 1:‚4³\Ø(Ó ¹„^…bbžÁs¾¬ eÂzX¯t3ë•n…xÓö¾?)|'PÌùŠ„˜m,ÖñV&¶aÖ¤ÂÞžZTÌõòüƒ\/Ë[™^BDx/bž!äy9Æ0—ó¯µ0ˆÈaS…Ìùס°<%˃à=‰…<¯°gDz_,"•õ'N¯š9ËËT0™-¬ºDÂp6zÃx /Ȳ yæõÛÙ†©,Û0<óšgÚÃz{X6ïW,dxñžÅáÙ›ÃQ0³óý<ïý ‚áma™@ dù‹~ B°ƒT C1ð°¼13‚('£8˜5b¬ð +µ*üy_+ÿwjåÿDüG5ò_­ÿJ]üWj¢pî=@Z@*ÐAˆn €- è JP@˜Q¨( R H:ˆÕ”¬¤}N!K:‚x­ DAÄN ‡Ía™fö°\Y_>¡—-þ T¹ Ý|@ÁÛX¶™ZØKdâ×7¿•í2Âü@cØ@HX[à ¾yf{G晤½?‘0’¤ åJ˜Ê t0–(a.K…̹Ø2Í|@ÃYAºPYö™ æ3?Ë@³°33ò´H–?¨.Óü@ ƒ:€LX7/ˆ„Y­Âsc–·Èó°Ã³C,k1< ›g-:AHx¿ÆVÀØ ´0¸Èar-Ìîd†×yó^ê»jßûº÷~Žø1GŒbÇ çÂô‚Hˆ3è!P/ˆ„Hm ô«(Q­ è!^/ˆ„€m  ,‡V1Û™ ,c;¶‚T ƒÀÝ@Árh,‡ÖBù„ÙB_hüó°œÇðœmãÇB¿Bxf0³,ZLaé…„žGð!à æ"B¯|fÅɲhMÀϲh@ã¨KßñÂÏ`"ðÌdg†2P)ñ3 ˜Ë|@“ÙÊŠÜB¤ïY^ óÙX·&tŒh> ‚!­ ¢Â²hÀ"Ym€eÑÚ™aÀ 0®€v9LlA3Û™¡À”0¶A î2˜Üü@ ³ÛA`z/ˆd™‘ Fp€t G!ðŠ¤Š‚(P,1V%¼óGDþ¯ç‡ïª‘¼>¾«6 õ×Áÿj ¯}¼î õŽ×9^ßþ™gÄo×µ¦¦ õìí9Ý_Õ¯uìx…ë‘XA:ÐC, „`¬ è!/PBõ5 Ï0`œï2ÿÇÒ²sÇŽ,žç ©(Ä‹—¸Êr ¢È«f-‹¦ƒÏÝZåÉ×”÷›â}'xÞg‡÷] ïeÁ8U+}½êêÚXÚÅûY⪺gHާ¿Ø^–½J‡|gY§™¤c9fÑ´åÍNþZê/Çû‰}¯IýÒxÞWEÇqþ{çE¶õû6à &ÌFEAE§ ( :‘QQ̘É`nÔQÌÈеU@2 *bO;&ŒƒiC;Š˜0‚ùíêª]]Àxïwïwßzw­§kýwÄ{NwÕÞû„:õÿy®ò2í“F÷'Œ¹AN»ÙÏ*³ÑÑ£õyæú$¹¹äÔÙµIôíëø¶MÏ…ò>0è÷r2z§mlæ#Þ¯’iWíÓwÒ¨©·ó¡œÐDzlÉÇ{= Ý•Ň—îMH¤M.Ç$ÑIÍ],®kð}Aÿ ôÃúŽj¡ÝyìjH›¦Ó¢¡K× Ûvƒ|ñ(ﯣ+f?/^vt4Yw¿Dy<‰2.ß1ž!}™Ñ·}÷0N„¾""‹鳩þ[NôJ§õ ¤ÇÔ‡nÛ>®™?TG]|îd¨;Œ"MG<îøÛ$Þ7}eÐßýÑïãIÈC?Ú[wúäY¦SíîG?¶Ê»Anׯ·ÎkŒŽþ`Ë ‡“ž÷'ì8R’Dß3¶Ù[‚x.ô‡Byô{Ñç´û°ä|‡÷ Ó©ßXÆYù‰Ù`}ð·‰:šÕÜÖvFÎuó+Þ£…û©÷» âã¯;ϧç|Q0ë«Ïèçà³CÝÛš¥ÓÓË€ÚM2l¥ŸqÛé:ÊúQ»’EC˜x\K¢&úÄó®*ròîó~ÜèWÃÆ/çOý¼«­0û©k:m÷û©æn’’E~ ‹féèÛ®½nïCX^z]>ÂÂí®s0ïû>?˜è߉~&Œ«¿©ë§€~z;Úò™Ný}º´;Ñ÷&YòWœ´Î<­Ñ§¾Dz´+¹x†1JM¢»üÆX>èÂûš`¾¡ÿë÷§åý¡YoÖ7MýÌÎzýõB:µ”®Úh>®›ÿG{KiL.ï@&fíÊT3™~|ðKÓÓ!<×}¢ÐïAÇ<óM¬Ú=²lZœ»µ’Î[½êlÂì›ÄýUµMu”õ¹q öóëçÏjLKOÍÞÞ£u(ﻃqÅÆÁcÞ‡}£*ø‹K¤ARÏ?~VÒ̓Œ†eEÜ$‹&Æ÷|¤£ íîàän¤ýö¯QK?&Ñp_ÑýkBy?0¼ÿèK…| äl ¿Uß ôs¿ý‹%ݹƒùs“ü\/Éï‡0•ÄŸj§=ׇŒÈ}fµî{Ö­æµç:‡ñþoè#Œßó‘õýeÛ—Bû=Ç« šÜ÷éÏÕ³n’„>cO/ÒÑvY¾ÃBæ$7îjëü’˜D}>«Û“¿Âø¸ÂïÁúG}äó}lØ~Ù<÷…~^u ®‡dPÖ·ë&yÚ,"ãd„Ž6j‘‘t´§É:øûÜø$:Aoh^é{|qAn'Ö1}^@»wÇ)ttð¸ÒÏ~¿ #fcã}<“é ?í‚­ª‡Tñ-B>#ÎзPÑâÌ¡Ÿ8_cËé†%έ°Ë¤ÃëFùUwÍ#íº¤ÐÑ%µýór'¹“˜îª1ÁÉÔöØÛmšóó¬å³Çl¸øêï†þázLð@Î×úñlUH ’IåíÆž”G46®*OÑQ–ëHë›Ìq‚xîú‘"ÿý°ÑŸTŸÐ~§‡’IK?ö´¶8ç›Ú=]Gíî†_7· +åpgRhÇ&mµ¦IÁüø]Ù—”å¶s~­ÐîFeBä ÷LªÇpÄ䑱ö÷mšB»£ËÚ)Ì/u ÆÕÞ7ê䄜‹PÞç y,Çì)_7„y-‡ö×n™þÚ©n&Ç•É#§ºí²JÕQ›ÓËH+Gú˦;9)tÜö–{÷$†ñóƒŠ~õ¯y¿\v±$¬O5ÇË‚~†ÿ¶ãÆä ú5´ ½÷<âXê6iE’Ž>}\0yn£J=¾ )™ž$žoNÑ¿ë9~ôŸEŸaä%ëóúéê|¸›çÜ zÉsæg»j·H— â^‰:Êλ~ ŒØl2%™êqÇÂøùŽçè³…ß Ç¿O‰/¯ÌqM ½m· :pè›÷’v·È‘á'G—C?sݽ§•ö³&K=ÆO¦Mìô) ­Âû|ä¹ì×Y׫“Ný Ê]+)ôŸµ-‘ÑËûë#%õèwd»ã [¤þ+‹°,ˆ[I»ƒZFô%>ónNú9™.›Ã8r‡òþ†x¿Y¾@M‚ü$§¢/-s£KìØü€~>¦î’Ô^£¤ŒÏŠ›r‹”{ÕûØ7CG‹^þ4`XÍAäú½!÷ûL¦ogáW…TáM¡? ú÷Õ#ƒ™²›Ð~½ñ¯’†&6ü°cá-’7%‹Ö?¢£/¶]xù~0/–\‹>ŸLYn³¡žàüçå˜Ý"<-·gÛ÷…öó6·Û±9¦M ‹ˆ¾EZcÑàw7Òzn+v%ª­ŒQl2-þ¾1ÁüxŽ÷ýæX¾` ï×ÇŽ³¿ú±?½ìXDõtzöKæ¢WÊ[ÄaÙÏ—·žÒÑÝaƑϿô%–cJ~2½˜LÃܺH÷Õ®êï‰þÕÈ}ÄzÉ^OÎÏú9³À)âñ¡4:¯ùÎ'Ž×o‘>>ÉëÓ:šWtõe¨u'ò5<}ìŽÒdj®L¾0bt?ðû O¶ïB¿3??uòøTm‘+šF™UÁØ·ÈžñiVg!~ìn±øJÙÉt§Ô1…ú/ñžÓÄàˆ¾Ÿl¼V#x?p½&ôÍÔB?çZ0¤º4Êr¦òÉ™5ïkØŸÑÑÙ="Š 15ê¸ñˆúkh—+ÁéAU|¿qœÂqãMè¿.jW"uÞÙΆÖM£,o(Ÿ¬zß'ªÜ—!'^5høÌ™B°›;ÍH¡÷{Y+ƒ?'ú ²¾«ïøuû}X«Ú8KsÖñF*] .æ“X­u‘ÄW¬ëy‹ÑúÑLÇ;&L†ëõ<½MYjh%þÄK—ã›ÛØì:ô–Ïó œ,hßîÚOÓ®M¥A;éŸOj¼m=xr¶Ž† «ÿÁnkú¬á¬U#½Rh§³Ïí]ViÍ h»±]›»É4aÔ›ÕÓwç—ŸníKJÓÑ­ê®{gG÷Ú4LøŸ?Àr“vÐÅÐ*Ü:䀳yù„÷ÆqKŸÐÏy÷Iwu0¿É:$P•O^õ©ÖÆÅez0jcÍà•à{86ìÑç÷á†y{eÎ#Öœ79lZè×I.†ï¼¢Ë'Wýd£ ¯Ãt+4!kú_:¶Ì6….è5fK£å!|~à÷AŸwôó×çC{˜íÐ")‰:|w9Z¤&O>N£pú¯3yøÞŽ\”1+óºéÌ¥G÷Öåx½Xßé2Þ7[ŸÐî™’Mu½“(C[mh¦&¯®­1ƉŽ)¯¶w"í­Üi9*…¶w‹S_s æý]ÙuÒ×J>ÙFyú<€ö ×~žVV”H#Ú6ëòÔAM‰·”@|*ê™l™ð ™ÔMeT ×#íÅé~ý ¾â§8~£Ï'ÞázÉúYÔpîù›>‰Ô’´::ÏCM3;ßžã‘ǘQ7&u#ùöêz…ÍRé¼ýÕN5ªZÇq~Û²ÎHÓ¡%oÙu´«}ðjùÉÚÏ=ÂÈaššLH½Pûä±OûýGknkDRïyz6ŸœJ5çö$Ìñâýõ±nã~Ë(s^´?×!C&Ю½×ÝM”«IjlÓ{2h_Ò{pg÷ôŽÔK¿á’JõöÀ· >ß7¸Ï‚üQ¬Û¸TA?;ñÕ=@W—¯¶ë»SM¢F‰úkëhð£Î?¤;ÑÏMÎGŒ¹”JW¿Uýéþ >Ïп¹F8¾¡?¿>î¡ý•u‹ Œ;@*k«CjòØnÁö‡Gvï¸öI”n#ŒQz*}³þ-ûBøù2~Ì_äO"¯QÈAYáþÎÚñUÆ\T“†{³2C~ \³­Ïîm„:O|ú{ù¾Tj?{rDz1Ο^ò÷ÇkœWãþÔë_nÔcë’úY:¦æ×qâéÎÜö±·Õ¤ë¤’]*˜ß¾]xtúvG³B±T*%ú ¥a>ˆõãëD^ ´´™´pV<-¿|©þK59ýéRl˜oúˆB&œ”Õ¡ŒKpcëT:.³ÓC…ðë$¬¯ÈÅ¿¯às íw~4|ü¨wqÔI®- öt*Ÿ?¹ÿ„Y÷zZ“¸i±wG8§r<Èà*û§,§ì=?Þá}®ïåÐÓ^SõK^evhV@~v~x?æ³Ñsè9²¤;i÷ÞClšJã#,ò”÷‚ø}œàú÷]*ò—Xß]ôã¾o»—í‘XÚÿy[O¯Î$cYñ/`ÝÔiØXŸ×?v'%Û#\¥©Ô{ƒUʆ ôU~Ês°¾"RŸÐn×"õÂUþ±ÔõVKg®¤é¡e#@ÜölÙ{{ŒYXxrðçT~óÇi¬§8oÂuÔcWõ±|-ô3áy<,–D™>¨3¶€Ô™/ž“©£[Œw¯ïýBDî ´»’jšFëì°Y1k…Áï7îßáO¼ŽBŽ“¨C‰ô•R6{'ôãj¿ß{ÞÌÒ¥ñÇcÀ÷~ís¿ kl¨è‹õ¨}OSéhf¸o˜gbÝE>6ΰ® ×™bèg!ƒI–ÄÒë‰6{,( Ëö›÷üãÑý¥ª5Œ ½r¥ç¾E·Réö¤Cg»»î{¯Oÿ˜µúC%Ž\мh¿("§íÐûéuÑÝêµ×Ë®ïæ'@~|ªž·³_¾;ÿ=ôØŽEnæuþ瓚”Qï¸ø­Èëð…ö{ž ¿’Ük?•é'žd]À—Möéè¨+“ͦ‘ÑÚ#¶ Ñ5O£ViSÆóû·XŸøú¡ç˜u#‰'_¯kôf0›Ð>³kcºy9"dÓœ´âÝsÅÔø:Z}Éù­>^tò¾~&»û§ÑX‘—ÃÃ~rªqÜCŽÉã‰Ñ›±ë#´ÿäÜñvSï¥ q|N ¾·è‚éf ðY»ó&­y1~ü}Ÿ4Ú·Áó«š¹†ù%Öu\Gà|9uÂu‹ úY1÷Ì/w.+¨2uÏ—ƒ È­?®}­ÑÑ»FYõ83”{n’F#&dMŠ £óÚÀ•Ãñù&Âç=Zè§llî¢)Æ Êre5dã¡ôíÕ–ëh ÇÛ>Oô mîù:Ãõ‚b¼dã&Ã|÷ÏY¾ß~7·ç©žC÷°í‹:–H_?0R>°›6I|a ¹]oð'ûÅ:ºÃdÛ&÷'éð,7Í—4Zó¡å—š†õæûåœY5mk”s|»ìü Úµ³´x3gô.ªÇËXhˆ¨KšÏ²0µë99ì’E_Ú­GðA"K£ÍT‰ñóœGá<çQÇ;“B?µÕ7Œü;6%ch' ›ù§qa°Žj{ÏÌìÞ…>qZšà89:Ø•ür{’›‚ýá¾އÈ•X+Eñu¹ýZèG•y=¶Ið¯Tñc‘ûõîâÚª‡|nŽŽ~°>p¡¤Mdð™SÓ8~mÏ«Åñù&¸nÁû-|~"‡~ô˜rïÜúHCÅ¶î¨ „y;;Ù—Á·A?WG½n½ùEUîrípÿ}ë…ë0ô³›Ìy Æý”YpiȨþ§neA?ìüȈ6\gjöKež¿ äó×-X§pܨÀu‡ö=r'7Ü¿«·ÒaåAQ#h¿×®–m2.[RÓ'ÆNúF×YìzdBÕû‚ó|>‡ûŒÿ£‚~#™_¬§»Ç<¨!Ýò#̆êhÝû}Sn+ѻɣ;|žNKr/èÙÌ0ÎâõÁ¼Àï…Ï·„ó8-ôÃ\¦ÖÒýAK×êTPß#z=¸ý°¼2>~´&_ÇbÜbü`ž Ÿ·âsav*².‘NŸt½Y~$íë~lú«+pÝ®yùVó’ [© ûÉ«öw^H§ õ)ú¢a‰ãò#‘ëR‘[Îr7ÄÐÏáýNƒÍÍWS˜\ù‡ÝÒ[oNµ¥£ —ÆÝKIó¯EJÚôâùùÇ ë}œ—àþ=ÖI}^@»Ê[ŸÌíö3´oìêvGCfÏ`î:êuçÈa­Â‰26­¹’Ú¬nVpµ,ˆq½†û:B>²/´ËŽ+hî¡3ýŠ4ä׫‰ôèX}q#àI{KRíÞÜ»NÖÊJœ§wü¾ {ŸDŸຠy1úü€~&D»ï¸)§v¾Õ<¨!Ÿêòú‰ëºô×-h˲‡==•´Þ×ǵLNø-X×ñù>އXoºêhl~@? ù+%r!z¡õ¥ã%âqÄ*ðÔO:ÚÚlíϳ¿:Ò?ý’£s\”ôëókÒCƒùº>îÑÊÈö_y¾—> ½Ë/w¼Ì ¤z óK )÷kVï$ÜÏš?4ènÛ*>Ýé=ËSIõÛåMCøøÄ:ήïkòÏ…ë=-´žÁ36ö£¹Y;CΕiˆóz§ù— ufÌ­>€^yPk€ùh%¥_¸†ðûtø³â~Èkþùž£'êT"MSO ý±ÍhZ~Y:퓆HFù±lˆŽ&^Lýj]Ç•> µ/»à¥äyFXOñ{àóÃ/»úöug÷YÄÐ.;OLêõiÓô«†ì{|dŽÖùeØ…»e©ÏúˆÖDI´’׋4 ããï+Ö;œ7ëãÚ}´øÓ?2F«&5 Éñ ×°Ãýtôδ– œúÓ}7^ϳ•(©¦¦Ù6r/”_gãý¬ü>Æ#®ßqßUç6%ÒÔ¦ =Šˆzˆûw.$GMÌ{õ×ѳm4çM–ö&oÊi»NJÚµ—¸öÇÐ@þ~bG®Îïpÿ›ýwl>‰¡Ÿ¹EǬŽ&-‚û+†IpBi†+\ÿºö×óf8‘ýc6ù¥§’~Úx?cÏÙÀ*üWvýð˜ï‘û꯿°}Øùô³½Gëmµ–n!uKÆ= þ©¸\øcq7]\rqƂΤU§sï•)il«I^ UYÿ¬;á•=ÇÀùÃý«÷Ç>›dïÈæôsÆ›l#“dñ׊&f×¾™;¬SX¾ÞIMÐŽ5ç+i·á/{g¨›˜Ç8. ßœý>Í8.»þ•C?Ï\a¨ÚNê<í²ùúœB2,íê­éð}8Ž3õ®±eUËŸ•45N~èkµPþþàO\wá|׿,±;@?µµÓ_NžC$SÍZH¬VDg/û»zoÞg:¸xFIÈ:¸?_&ýáZe ëÖœg ×§*<ϲƒt9WÿÕÂB2z–ׇ]P§”çz\ЇNs®g¿RI·0§‘üC«Ì+‡Žû]úü€v7µl<¹sâNr"œÌ£‹ ÉÙÚÕ[ŽƒüX²*¢Ü¤u/ZúáòO{•üóf¼ï•ëîÏâúšý=÷\Û¶Dêãåa<:ïWrµ…—¦ôS;½ñÐѽq¾â@£·3?”ôèÚ³óMl õOó¥×íƒ#Šùý3¬¯ÂuœÚ?§êµÑ¢Ó.ò´i³Û Âá~¤YK¡N±ûÓµÈ!˯—Å0wñ½0Êv©¡þ±ñôçGâx/ä¶I¡ý Ïfe;v“…s¬C ‰ÃŸ/Kûè(Ù°èu«½Èè—ƒn†ñ'R1ëÍ,¹+õ × ¸ÆŸ7ì“ûB?§8/Ø\[A¢f©_¬~Žúå¥3ÜçBÏN4£î ²çJÒŸy0N/ŠfÚÛ°_€õŠ]çÔàã ïrŸõùý4R]ùëì þûÌ(iy}è`µhQkuàçAäí1WßÈCÇêçÖ‹ØžÃ}@Ì ç£ •qËnkî%½×ݼPHšºd©xêèÂqç{&õ#zìíZ%me§}­ˆžã†çcp×oÂý~´ïÿCÉ–Á=÷¿íó—xÏ/$Ÿ@À:…=oÑ•|Ìp_z&]IöˆêÜ#5˜_U<·ô?Œë!U ýÌ÷Xºòù>2ÈdÚý'³ ÉÞ3ýÛyé(>W‰ï47`ß0ƶX0ˆ_¼Ï™>ƒF·ó}ÌŸgÁy½ð9ŒÈ®DZÒwëÝÑû‰üºlÔÔBÂ<5oó´Ñë“VìBÓ`v°õš’Ž}­³·+1ì›âÏŠÜ[Ãs o ŸØµºÕ#Žœž|óð;¨göl—JutÓ×êýäu‡Ò¦m?OUÒ¾¾¼ˆ4 áŸ_a¼á:Ïiã9 ®´ùn]Ù¼~ºH’L˜Gš7r8vÆýúºö+¢tT^³¹çMOZ«ÞçÆãS”ôÈËŽZÝ æç·¸OŽëGÇ~5gv†Ùöоѧ–îËýãÈàôZ’zÝ ÉöÀ“êE]tô×åƒ>|iîNwåU_ä ã–gyÜÒ»†ñ¯×ö—qñi1wΡ̥_vu“·oº°ùí)VîÈZGæèµpjº?ÖÙàsдMó¼¶ûb”ŽDoyãp¨Ìß=Ы¨ƒŽrç¿O[UÏ#‘JzªhÐæÃ««ÇøýT§pÿSSÖóçqØsŽÄzkéÊ Ë†³yí7ý±ïum<ùóØ Km IÙgé„C¶†yêû²Íëüa³ëÎð«<_gדÕxN¸Ûþê Uv„=%eóú1×/Ûi-þòn^H²ŒÌqÐÑjFWvýx£/a9ÀJj,ÚØwQç *Ï×1ž0¾Vc¬íˆ0~ÐOh@«zZ»r`ó¶‹… I›à©R/GuÌs’æmèC¦ê´Jþ95Îq¿× ¸?e¡_Y’ü‚¨eÓMíÙ<~¤+³ß]M lx’Ååŧgö„õ”~Æ‘Œ—AŸî†ûߥï¤5?óçбbýÅu>îaú<~œ™‹D®S ÉÐ_›M²wÖQÜo¶Nëô1MIE-ã†|6pNqŸ ç¸oˆçý„ûÃ"û©B;nA"¹{±Ñ®iÍ IôóþWGC?s,JžšÕ’ö™Ó¶[ól%•z|¶7ÔGü>ø>Î_ðzVàC?V.aË?ç'’o ªå7…ùpø€–M¡>~]EÔoeC7ù-ÝãñæXI`Ï=ÇsYø|¼â:Æ„Íhß¼õÓ¡u/$Ò}Â1÷º…¤káÄÕ‹zèh¯«“Œ=ZÑ}õmn¿‚û~3Æ4È=¤J]aŸkÝæŸ7a=Ày’>o Ÿ=F{OlÚŸHâVúŽúý“†¼ü!¡éè‡=OÔ‚xì$ñ‡gÂ_ }ãËâ'‘Ä™Íox–ióÖÁ6˜·ÞÐ /oãFª»ÚNVORÒ7§ï_;ÀWÏ‘ÜáÖuEü¼ŸCéóúùabŒgÓõIdæºÙ‹;|Ô£ Ö–Ù ÔÑ´’wû_©¼ÈO]Ï¿J[¨¤owMÜ¢ þü¸…÷ƒ­:þ¾ã¾¸>O ýÓKŠnÅ&‘åæ_–‹ ‰¿dqVýa:jÞ§ñÎöÛF’Uç—6÷=kèËì„f|Ç:Ãþ,äî·áü¢>?J¤#j\ytûYi^7íèþ¯rVuïfŠ·ŽG’üS§îž‰RÒ&™¯{ôèmGpßÿ[ø¹ÅЮe­cïŒÞ$‘?5 õz£!Á·ïmÚcÜâ}ÆýCŒW|¨Ï è§ûŽ—Y×g'“æê)mã 4dÁk“§æëè¤SŸå?Ô’æ-ÃiñJΜ`3?+ä÷YðþTx¿ú©™“›±Â#…ìž`}Éô¼†HûT«Qˆë_îþ+éç7{º“/|žã}ÁsxØ/>ÏÔç´Íè­]б¢"q[~×#gÍz £‹Öý¥Ü”Ø:›˜uƒë5qÇ‘’^# ï±aœ óL í]Úëšõús é²íñöÈ#’ÒeÃ%·?Ùþ>*ïN¸Ï_:.+ÛØÒðüçqxýqÿï𺈺”HM/_Ó•õH%>å§ÞßÌÐÅ»ì½Ìà>Ï»JÕcµ˜&Ùš—@>K&7<]J`•óÈOXq]­Ï hÿËÔõ nO%¹ÖÌ ÔÖa/††ÏÓQnü?Ùýù•ù1~Jº1åSW³vUÎ °õû>¿¯ýâû ú¼€~NË­Æd”§’aGžš~UjÈð®/:¶€ï1ðÀ£;Þ2 ¿žöÔ'H`•}u<çŽçèðþã>¬>/ ÇUÎý~mFì×­¹rþ°†ì{j÷`1Üç}»:?ì/u&׫5= /,^ÚÜò¨2^`>ày\ç×mrègÝ|¦ð¥‘²ËÑ¢îÐOœÌi÷}ê[ÿr«€þäö áuÚ+éO?¦ž<ÖÉŸ/ðú¬:öÓ••þïø:ˆóá¹-ôcrËé×¢4Ò?£©£Üÿ´-éM—.ÔÑœOÏ>=Ý<˜è§Y%pñǤ%½æñë]¬«ìzú3ž¯—æá¹7+­¸õ;ôS}j±ºŸU9Ùu~^H‚†x¦<úª”ëhëZ¯ÇäŒw'7†N6õvVRf²Íí¹|<ã¼÷‡pÏï Ïh¡ŸÏC†OÜ–F˜]œú»4ä–ûù‡oVéèσc L¢ŸÄoÉ÷PÒ¿Š–®Þ'Ï_7ÜÃçd8ÿaïûž€¨k‰4Çõp¤¤C:¹“9àÓém2"…^´ò|ׂ»]=û“z-6 ¯ý£’Ú-^’:dƒ?­ø\¥ÁçÕ¸?Àök]a¾(†~^»l|˜s1œ¿ÕmåèG¿?+unÐÞäZûØÓ”T¿íÜ(€_¿a~³q\àûM¸O»1£ñ„å#Øù¯úî[ͳ…“’,ü£QɯÒãëȈ¯Ñ:Ê~ÿn¤ÖjöÝ”t_næs‹:üõÂñßGÀu.æ?»®f÷·}¡Û] þl4UIJ\D‰CÓýŸ5rت£ìû$br6ê7ÏÛ”t/³­vÝŸß¿Áxc×%ø}á:Tí/ö¨™´LIŽ[9ûŽùECV‡ö—G.øhföÅ…ÝQRö¹¡?żüôÒm™?ǶŒ_Oãï…ï=( Wæµö}J}¹Ä|Þj ÙU·[Aôƒù\¾!½ïAÈËÛƒç =f@+¾ç[ÏKÜOÃ|dã½=ùî±ôßã5òÝg‰õ+qß9WÄr¬£8_7Îß÷ï8aLÀúqœ0d¿ =-…žç2Žem íRƒ¤˺2QÁºçyŽÌœrç9ã³Éù,9 <Ͻ9Ïs1ç³Äp`…þ¾èm"ô÷õ¯äï[™ ñwLØpŽ ë&àÂ~ËÓ2—ó<2s\ÒùqrfœÏ¯–cÄÆü OLèó«âQ Ž+dŠ!ÂcBH8v,2Å !ç˜NBÈÙþGü¦80~QL<3þ•ùê#ÖÆÿT]üWk¢°V®…ÿ¯êß¿Zûþ·5ï[õ®r­cêÜÿgÊý²bs8îœãÞTæc;ØØ~ \ozGrŒX)aÈ1œc‚!Û™`jŽ †ÞÀÈv`üKM åèŸ)ô)÷ãX×Bò\Ž÷…¬/ôfØ BÆr”\B2þÁ9 ¨M1ÿ„mËy’Ç|ŸÇ}ŸÇ‰þûæqfÜgÖŠX6¬‚óæôæ¼9ÿŽeƒüC­€È¯(·’§°/LJ5ƒ€–ƒŠA2Ž[™ï¥äø^ᜧ0òŒžÂȲaøØnOa?ÎSØãc— <<Å‘ ÒJ>žòJ>ž•½Ò¿ÅAD– rÃAj Æ_ >ÈÔÒÀÍvªÄoP‚L!éÂA¹œ_zç—îÆ±³+3mÐ×ÓãÃJ 9£8>¬ ’4›ã{E‚ŠA21T ò†äÍ™90Ï¡àß |Õ¶?H ’BR'plX!Û Ù þ»ÁcÃ"Û Ù ‘»Aʱ„ íÄ·a|?ý ÓÌŸÊ5ò?YÿÕÚøjbåzø­Z(¬ƒRQÕøwõïïjß‚ÕõïÖ»ÿ[µîê5üïÔ7æž&ˆ l×Üj,·áÒTæ^kAR#÷Ú¤9@àÅTòFƃ¢dÁ(ç]Èw@F—–ct1Þùœ÷0Ãw(75°®Åœ±‚6TάKŒB†A#ƒ ÎnV‘?ówlB†K˜ C€G‚J™u¨¹i] ’AÀÃÅñ©ÅŒ÷|>)¿Ÿ€ï mÇø™Àï¸dòÃ9Ö ²¬…¬™.a¼AÙ 3æü:ô ’A™Ù1çá¿A2÷y$¨¸’÷¹¿€a-ô>WWâoùsžÄÈ~¨Ìß*x炜 &)þ ³ZÍy+¾Ïß¾ÏßDÿ}ó7 ÷™ŠE,›5 NÆ[=çd X„Ƽþ 5È ‚X2æ˜ ŸU  *ys|ÖÊü­lŽ¿Å0!ŠLSzP®€iÃp¬eJÇ„ÈåX„1\Rør@HŽ(P9HI’ C¢D‚ŠAn0 ãÃþ-!²nEÈp¬µ 71|™¥c-m[‘ ‘ 2kÏx$A?œ{ çÅ.ãXÖ¦ Ü' óÞ1|6HJŽÏêÉÃñY½m˜÷`Xþñ.y ˜„Q òÎÌÙ=æ<sö ¾7¨Tà×ÎpqÂAZ$µ’c³ Ù[È„ç˜N›Ù[È„ˆâ˜nBȱþ+G)ðhgb“ùó½F~¯‘ÿjü»úø¯ÖF¬‹Ì=TŠ |V5Ç#DNNefu1ÈÍÈÀ¬iAN° .hý@¹îD1HAœ sŒÖRwÂLÀ`_JŽ=Á0¾Ô 'ô.Ø‘Y- iAnü .|A9&!ÃÉñ†„P}ƒ“ówLBÇ$T$4Q r7$ÊÜÀ¬.yC"%€LÅðyÄŒ—2|H*%È Ë_À§``Å 7H4%ÈØª"—PRs¼äV y9 ŽÓêÛ‰yw–y?®%¨ä É™m˼Û*ywfβC3¬Q R nx¶se"³:d IÒVâ…!ÇB*àX + Þè¦èá 5H ŸðO8ÕÌÛ ßç‹ßk¡è¿o¾èÄõY*b9­J1§(÷,ä– ¸‡¦¼á -H Aœ2…@öçx­è(P9È—ãµVfˆ©8†X$¨$ƒ€W‚Ì èÃAj£‡IoP6H ‰R7fyˆ »Ú˜á!‚rA1\‚xƒT $J¨$ƒ„Q‚L!iÂAZô°‘ÛƒlD†]] ’‰áóˆ_v»Ú­-ã{ Ÿ’Í¿ãÁÊøÂïÛ3~‘Цãë'Ò' 7ǰ6ëh`øH!!N"$e8ÇkuêÄxˆô ê Êá8b úܖyOÐÀPŒá×”’@GÊA2HälŽï#ƒdÔÙ«UÈË™A’ËAZŽ!#`ˆå‚œ ñc@Å e%žõ·x?Ù æ Gù¿9_ü^¿×ÆTÿºÈ܇l‘«­Ærc¸ ¬Ì°–øÕrP1H Áš2†€õ©AN¸ P)ÈX’p,X†Qæ µPÅ1Ê"A¥ w6È <¤I!Ð c¿Ú‚^*É ø@Æ~ \S‘I_PH I *ej#$Gö7˜ŠÆS1äIÃ%Ž/(ÇœåW—·a˜@÷ 31Ã'Ï’ABe[2~üÐ.(äÄqÌV¬ ’,dZ‰«ÙñD„˜ð«ýAj ãáŸËšåÇúr@HÈ(P9Sm™÷æáï 9£@åvÌ{¢ðï@¨1 r·óþ ü;Ž©ˆìj%È X*®Ä;“ƒ´ 7Hê)Ç•EÞY —ä¾ l$»ä ¯ü'Ìj†¥è…@ù}¾ø}¾(úï›/ºqm–‹XÞl6ȂӤ9@ÆpêÊåØQ r¿Ñ ‚W*¹A+AfÈásÖ:† j?Ž9ëÁ*ùBç€$èQ r7|6H A/iAnüJŽ™í R$rVÀw4eØŽ 5È ’C2†ñå€ Qb@å oH˜l$T rûœGcH&ç‘ai—‚¼Å ó>¥¥-ƒDK™B²…ƒr@Hº(Pq{Æ3~2îÀ²UÿUø·LøJ™5ãÇrg¥œ wÖ” r²eyÛL²ú X 1$®?(ä Ã%±wæ]&æýˆ;P)È’ZÅ1g£@¥ oHpH I *¹qÌYcHx$…ÄW€JAÞP²îR7H!T ò†Â 1Ž8‘„ÍæÖH¬ÿ¨.25ñ[õð[µkàÿ¤þUæË ëÝ¿Sëþ'uî[5kSÓ|E†ì·ê׿S»°fý»õê[µêS§˜º¯±|Y' Eõª,ìR·ËÂŽ„ qƒZ¤™Bð„ƒ´ )Q9sn)äÀñd™€’@@E¼!¨T 1Ô9¨äA–2å˜×‘p¥ÌÙ8$…ÀS0Ï5 ørAÀ(P9Sw Î¨@bH9H rƒÀTr|ë\¨d AêÊ5gÙÖ1ÜàmÁ0ô oP)ÈÛ’aIAßm6 ü7s~‚9—97ÜÁÀ®—0^¢Ð¶’yNÁ r€á‚Ü”r€ºà ø~ \“=ó5üžy6!àTgƒÄ‘ R7$G6H  *ɘ³v=X~l.È ê„‚yN ‰ãCòÈAÅ $Qö?aSƒd\ÙÄð.Ïû|êû\êÿ¹”7÷ÿaÒ¤™A`†ƒ´ 'PȂԤ9@°Æpë ʉ!p#A¥ p6H A,©ANÌ 1´?H ’B`+¸àöå‚ Èc¸@÷©@‘ðÅ }6Èß”r€ˆƒdJ$ƒ¤I!)@¦~ \$ˆ‚K_ $†d‰•‚d4 SHœpä ”2…$ ©Û0Œ[–—]nÁð5áó€, ¼lodÖŽá7Aß H¶P)È ’N 2…Äóå€$Ovø> $b¶„ñ †Ï*¹Ab*A¦œþ µ ã™Çò´!QýAj;Æïþd IRƒœøwÌ{d 9 Tò…„Î9@RÇ€ÊA¾Ü9 $x¨$ƒDO™B²‡ƒ´ 7HúP9³æ„äW Ûå _(9 (Q ræÝ2( 9 æÍî(ÂÆ6ŠÿãyÖÃu.õï®+±þ§ëßb~…µÎWÄÖ8¬oÿÉyVåºöÿª¦}«ž1ß=‡¹GDR¢æ9Ô¯\Ô.¨ä Á•’@€EdP·²AflrP1È :?P.È ‚O2†ôå€ c@å _È„©Y R ‚S 2ƒº%gÞ÷‚@-gΘ@°šA°†ƒ´ 7Ú1ó.H r€Žát?Pó^t$¨$ƒÀΙAp«ARð)¹?HmÎðè¶:´kÁ0ŸáÿA*3 Th“9 à PÞŽáwÁg‚$P3g€!J™ó# fÆúÉ )²™gj“ ãÏ }@‚øƒrANvŒÇüÔ º3ã)Åø$± äÊež@ *eÖuP*’* Tò…äR$`Q R7$Z6È ’ͤI!é@¦xþ H  *yC"ª@HÆ(P9³¦ƒ¤”@RF':oHN1<`ÞqÍ!ìû\ÆÜû\¹Äp¾Ï–àŸ•ÜÿVu-‘r¾[¼¯Aÿ-œë,Òô3`ye:Š\ªO›— 4ø5aû賊q+üÜ*hŸ¡ÀìxžAÄ=Í®\, 6zûA]! ~]¼·éÞѾk5šFj¸õ¦óhE?ÂjýÚÑG }Ò„~PZègš9CÊ ǯM¾x²€Œ(‰X_¤£­FLw¦ƒIŽÉÒV“O¤Ñ•oš8¼=‡÷ñF9䯢_ú- yY¢î%R–g™AÒ ö¡¼í§•“Ÿèè{Øbñ0’,µ«ÕàF§³xÿ1ôC„>þ¡½e]cy¶ç@µ±Ä«n÷ôM¯u´þЂÇ?„xeÍá“óߥÑ%ѽç´í6£ OãóÇ !K ý´;~½ëðD}ÜZ@´!OGÿüIG{»k>œ”¯èW³ò¦Uñ±bùE¼/ŽBßf_臉¾Ý.™äÒ4†˜ýØC7*¢ «Ÿð½´JFîp~fíœN]ßÞR®=S¬·BŽ„ÚkzFšRžIÂü‡¼€DJ/Õ¬WDc’,KØ4„<¶qëÖlh:Ù;þùÅÎ~¼Ï¶‹¾ÉxÝqœrÐOϤ)2ÉÊ©Õg‡“–Œ£T}¥|›Ñ±Ã@²Ð1ï—÷ƒa^ÀàBFÎâýö#¼0ô‘b¹¡œ/,´òîç—g’‰ƒ2n_ŸU@j6Ýãù¬YÇ—q&ï&,¾Ö&ÆO¹ÚíÕl~\Àñ }Ê0>±n³ß“ Ÿ9µß+fÌ$¿Í+°ü2©€ìkûzîôE´Ù=Yùs[ò¢§&àô‹4:ãYV+!³øºßÇkäë¡£Ð7_Ô£DêôóV“NI™¤iÔ;¢[@æ\²ú`ÙªˆZÚ¶Èj½»=a9§itáÝ F?÷›YÅ7Çô¹Ây”ÐçX ýL\4Ý]¾3“,SÜÎzáU@ úY¥Mi]DM’>Y>½hGöŒ]2J‘Æq¬ühEnˬß[Õ‡¿Íå´éùÞtä†L’¹ëÅsC ˆtqÒÈmŠ8žeo’lô.Æmf òZ4³ —½ßE<·çUÂqÇúé\­üÀÈ5™$eÌækÕÜ¡.e/p™Ð®ˆ²¾îî„—Òh± ”¼YüuÂyrï0~‘ƒ%ä]Ë¡•Ï»'ÃÂ2Éó­ ’Ó“o'ýîeSDYŸðádA3ÃJ¥Ñ7Íœ;«Jgû-åx‡o]p¾~rú<~vý¶7[74“Xì+ÒpWúþãÝ‹èúù+º[ûŽ&3½Yõ1'•öïg=öhs?>¾pý´/ó.õzÊzt¸0Ò%)ú.nÖÑ»Ý]¨;åÃO-SAUã=Ú êÉŸè»tL`¢>y÷v®ç+|ÚïP]´ÿÝSK×hòù‚ó$­Éžkz0Ls÷pv±‡kÇmT½ÔÌ~K@=^¸”ûAû Z×Êð+p|uЪËvIð|n~ú «"‚*NXª%ûðÁ°jfC—.j¦#Ìwüì·¨3½Ï<8O¸þb>¡ç»}ÁÊÐPÒ4®Á²0ßö`ÿm«µLæ’koÜt„ªwÜ*¦&ܱ‚ÿ4õñ¤yÁó–/Ç]ñ¤­ïàv‰¶ôÍ÷Ú÷XØÝ*|Kì&-³¢A¥v;dÎ ÕRª™S²ö~wðù…'BûiÚ÷–å/òóDâ<Š–y䉰òèZp¾ËB§¨Fd»´Ì|èØîynÑ£zó˜™=õïøQ¿ôwt?Nãµ~Q`»êçÏäë|*ÎÓ`sô–¹‹áÌ„výÛâuê6ÏvèÊýZ&7e×qw‰ìme¯¿¾g3.W¹5Aá-äí'èùõí¦ü1Ç.çṉ½}®C1Þ÷€jõ®&Æk™‘ýº\Ûýa\-¹~!¤usíXÖí0ÅH»Eã‹æååñãמ_Ä×+=«BYr‹¦ö%$B{”…+ã=oLÔ26ÏbL‹ûC³zŸ=K20~/¿ *92Fð§u‹ÖwÊÉ¡>Úb?_‰ÕÏïãðýcš˜'Á壛&i™Ë}\ëÑâj=l¨V3fºì#äyYò—6´_¢qÀûíòuQ†óøÄvÉ·<> ÛQÕî= wöÉi}\ËØFÕhwýBGX™8”±=¥fVYé[Èù¹Óñø?Ÿ ~ðe×’/8S¥ªƒöºãþmá 7JX°È½虬eÂ?Œ\%o '³vï9¢fŒÃNÔ‘ŒýÅŸ–^7zŸø>‰ä ŽÏ¯‰àZeßFë w¡z\”ÿì-Öóhûy‹[AÞ\û6Ý« çx¬Ð‡ÑÏKyÍô\NÌù‰Äñê, ÌÚ]ûË„ªØO^°<<ã„–á×ø±ˆË«…>¢,/· | ¸¿úü‡|OFÿ^ÌÅIÅyø¸ÒÀGSŽDw¾@à‚SZfâá{³GYCeú¸”©‰/­°¢×‰ÞÚÑÏ!^sqÎýp¡¬[íiŸ¦–~²ÛÝଖá(ÚíaêÚ›~¥}ÔL³qßÇûžPý ýÜô\Žî#è/Ã{±.”mZ³kYßWhë­]a•»pä|¬D~YËT‘´>}òPà(½ÔL“R›§ç)~ù>Í2MSK7d°ðÜzíQ·GZf£ÛdžNCàGÇáÑ/;« ÿÅ[¨Ã´/¡¼IþÜî°îêòÇ_ìÚÊá‘•N®‰K /MŒûhµÌè-“²$ö0Ýëy f\Û_}ärÃCXÏižS^Ïeû$ø‹y[‘8EÆ~£Dºö7‡…îÉym ´ŒûÝ̾um@Û0lÏæAjfÎ¥3~OϺ ç‰4és º¯£ûoþü’¯‹©8O¨îÆk } Kæù ¼V]¶7y©e¶6Ì®0¢²9ÌÏ_\²ÕJMx~^BÜ–çÖP ÝŸ¼ŸÑäGVm²ŸÇyž]i5^£t8Û,“…Wya—Ú½Ò2¼¿tcXÑc‹Oc5Ó4€{r2JÈw¿´¢|kz_ʬ'½ eË^r$4pâΠù^¸Þ­¬p|ÎC÷…²0¿´Ä*f\õèוÂù8czþGŸ‹Ðø+ÃyÁy\vJSGiÀ£/·ò³ú"ªÍ{œg|÷a–Ò¿ªA׉kn;稘!•ã^ù™/Ô?½Â>‹®[tßH9” PfÁyKÚÖª´U‹^¬jòä Å›Ö|¶)Ò2=×û\bã¤wV/W1û*mÔL1öÖ+ÊI:¿yc×à /Hÿظ̹‡Çg²oî\妬†“ÚîÁïq;ßüDã÷Z&ÿ/ß±£öÏø™ÃU̵v'öº*„sºÎÓzHû0ñýŽÄñy¢ÆFÔ?zå Êã¯MþŒû L@³vÐè\@'Ÿ Ã÷壄¾‘ÎCyHÂù/¹Ïâs‰Tœgn‰þ·'ÏA-j²˜¶/+6¸û]ËÈnj|+¦ &Sñvܨú™#~á5P¾Ÿ?ÂõŠlr¡ÚÌoÆ|žà<•Ý :Þ}tvì˜Õf5Ó+ç1ê…aùó†Á´5~ëå–*&Š­±c«§—pßi>P~^ôL8g¡>êº<éS(ëTÔðáœa‡àÆ·Ãk, »ºiÄ£šyLé.á¡vGvU1_˜¶¦n½<„:IŸKP~=/_/ ŽßÐcË|ʇ`ûÍ…JWñ¾'øµÑ«ŸÇd 1 I ü9–ŠìKÜ…¾›Ö±òû]^à¸W>/öŸšý«[•â¸cš¸¬×4‘¾ÙoZ/y0\›W ¦ê©™l3Äå!ô¥B“ü¦ûÚ'î6}lY/Šçl)pžä½œf¤©aú¢ô±×ÓX(1ýÂŽ’ä1z~wTævàûå}ÐYÜ÷píw»9^7ŒÞÊ¿âã¬È†ÖÝŒ×á¾J[K>?pž¼ˆN¯ÁBMž²°¹yÍq}Úç1°ÉS}0Q ó?m¼ä¯fL{>›q¡µ÷/ý"=O¥ë.åOòÏÁxîl$ÎS÷ɘÊk·«`ùŒ§7$²pÌ·$Ý8é9ÿÖìüFí¡éþÜŽÚÁjfùˆ¬SOŒÎmé|4ÿ(a»áôA93ùã»,Ú”k ŽR ßÇBõµ_™ä‘s³÷6ótÀv5S2ÀÒ)¡£Bàо”î{èyå~”y¾Žó`s½öð‚xÐ\]v•ÙÊ‚™Ñ–ìê8³ßæ~Öênù*†¿Ÿ>¿<ÿìf×àu‘Åka_JÏQøÏAÖ‘¾…²¿Ø[Òì7qÐDw@ÀBí;ü£r ÂáK럴Ž~Œõ7Á@™ïáÔçƒÓu—ÿ\lø>Î ÄÜ Î3T÷@7®Œ{:Œ÷̮ǒârÀÉ5¸EØ-)„8%y½Š)ú]ùû²ÑLY®å;¾.&çªlx®q§2çÜ2œ§f{q öŸ¾ëþR¤kKzì˨ ðý¥2[¨ÃaÃ6«˜%ì"¢GŽ`hÜÒùÊrOÏ‘xîTæù´ç‘•t¶$:vô›§¸¼…9ß\~µ-ª‡,êÐB6zé53l b",ï7,Qx1´^QN¿~%¼é³oO\÷•8ω‰–ŽÜbA7Ýlmݱ2Ì•EQ¶Cad«Û͒ΩÿiCë.=6º÷ò×—ÚðñpƆò“ÅûàHœ§åÊm§ÛÅÂ…W£,N»þã츻çhz½bGÀ$¨?/KÅðÏ—½º®ÓëG÷Ãüü)’¯mùüÁy–oýÚ#LRÎw°˜Î‚áýkwšæþø…Èê}z¥b,þë„r›CŸƒÒx£ý¼Ã¡S;ÞÝ9aC¹”#¨Ëœgç†~¯Û ƒè¤èëSYð=íl6tL_9hr¥ÎÀØÉkØújæÀ»™K³{q@û;ºo,øç8ü7÷d7‘áBÝcnª7Á.[ŸUô­×Eë#|^ÚWñܰr¾Yw}SqÜŽ‰sO¶…[3žT{îÊÂ=ÿÁßvúäyŸæLaë¦û½T‘çÅ>B] ë*åÏðû¦K6Þ›¸ÏºsÙõçÆ.°¸´"ÚFž«»ßûMú/œgéÅm°ee] ëCèÎ^Ž5Þò•ÆúEˆ¾J»ß ‡ÀüÖÚS¥ëðñ …²žêS.ÄÂ¸Ç Ï–º³U£Å²˜G=ÇÚ˜’o ü{`åûÁ76”'JëhëôNý{@«wÌ:%óÏY%8¾A˜¡ÇG±°fqîÛWx_;¶2~;zDоÙÛ§dô€`ÃóŽ<…÷h<ò×ÿ3©C©6³Co.ÊÓïX¦¾Épž>C¿>ûQ 5™;ã3zÇFÕyç)nØñQxv5ò^®­Š ^ïn5/wá{”å³}³áŸv!} ßÏ*püVŸÕ·W¦ÇÂÚ#ú¹ãY8SÝw´CäÁ CO€˜i÷\§9ª˜g•ƒ¦ïwê4­;õÙÏ}îmú¹îðë@7òÜ›çÐ+qž §Nn=U±PýÉ­Kuqßã`Aß°ä¸È8Ì¢GMt­bØìÙnß; ߃Æoè®ïÌõ€ÖšÇ<Ÿœï.çÙxÓlÏœý±À6ĺ–èq¤ãán9°÷a K³gvÐày̾§*Æõè´®æfnB ß‡¿¡ мvzr]£fà]óHƉü<©8Ô<ÅÙO Ž ?µµócaf³ìÆ9ð(jÚ¨þN¶ÐýÝì‹®ØðÏ[G”«oWò›Ç´ï çkº|Áy¶Ú¾ûx,lèïU\2‚…V»O¼ój‰ùÞ{c«“2ørÏrHEÌ÷êÍ8B BØ×ìùÐ$Þ$å‡PŸCFÝóÈÄõDOV(3žø#âöXàÊ=AaA^õ{ï rÈóSkøî¸©É35£ªÀ\ù–ãµ½î ½ÿM,õ«` 4tù‚óì ñnÓxW,y_€Ǹ˗¥†˜ÖCíB¬Èû‹j¦åªŠóÛ8ù ÏAhÝÒŒ¼êÖÜ÷³ ÝðçJ|Ëpü íF¦µM޽¼7œ1Oš|핽¹:Ö•–s«­üa ]¦j¥µQ3½&yo¾++ì;è~œ>Þ–÷ÂâÛA³2ý¦ÇW^ ûã·'w¢ËBAñyS«Š9ÀsÀd0½óå±Ó#YݹO×qBH¯Ï+îÿ~eG²$ûqœgú8yPp›8*?\3™…—Y[|ɆÈVQg+„ÙCÓåëÃ:nP1fÕZÿ½Óx¡Ï ë?Ÿw%B\Ñ|¿/‰ó¬J[ÃÖ)¸·8 ±ŸYý#ãÚ¦ÙÀïSä êYzÈzšŠ9ý=Å?-ÔO¨Ãô{Ñ÷àè÷¡ûš2çW8O¼×„ycFÆÁÞ ÇgÏca‘¶ÿªl6^\év:j8¨Ö5/¨µFÅ€‹íòá§~®+´ÞÓsDúœ[ü^\.ŽßõËÞo{Ç›‹qäJæ/;9± 5f„]÷JÏ –œí1ë£bø}‚¡çÑ4^éú$î÷ôúÊ’ÎÙÔëîÅô¬”èÍ,Üzáô÷ôƒÙÐqÖÞ§û.z¨£¡û{s wŸ’Y>BFï}Ï•ÖwÚ‡—yîóèpì1±í[dþj'ÆíÐÚ :­Ë†QµºfæLð„vÛsz?0Pþ£/S–ûBx¿’ÎÆ÷Z—á¿ÉpžóoçUÍÞ )SWI‰Âë”øùïÕ3³A¢5¶Úp迵Jý… ÔÌ’(Pì#ÔEú}(/‘öãü¹@‡2×Mó,iÄçcák•“>÷£Y J±¿lX¼þéZrÀIfô^—”U§Ô0ë´ßÑåŽÿö]H‚I{œxa³ÆÍõ†oêεÈ&ÿ_¸Õæþ—¸n?{ÖìVßBü<ºpg@fEðƒÕ9œ±(ó~ ǵ.˜ÿ4ð¦ Rûø]]Ÿ;àdBåÏ&ÙðhKr©t ˜ßî…+¹Šù¾oJfw“¡þÐ?SãW5¼÷¨ðïeu!¼w².àøäœ úL|r¸æïˆ†}r6Ïú<ãÌ_»º~¾†?ïšðK͸ê[ͺôç~š®;âøTâ<³ 7T=ž¤†¬5|zlż:6&©v6ðhúÈ÷Ó¸‹IÅz]˜Q0»W#?áúÐ}­ºxÇñ<¯Z\Hì’YÝž7ùº‘…ŠÑ'®øžo÷TJ¯k!‡½z«6´Ÿ®bÓ öTæ+ô/³\Û·¸ˆý…è¹Q*ŽwÙw§[÷Û à¾p©O*Žwo²ñ1ãWY¨1šæ ›‡¥œ;9Rů%reš¯ÐGÒ¾¸ÑÛçí S«­—”÷É?g'ûœG’±ýhì‰C°Ê“#R³¾>!L~? Å}²µøÛÊ[æßWÅ4Úßdõ#cɹ¨Vèïøçžz@¯÷_Ç“{óÎN|¼Àz‰»“—þ¨õ±hÆcû'¢Ò²à禮†kL\ NÑþeé¦*¦î*uäÄ•ã„ûJÿ¤ë0{]œã¸üú¨õÙ¯q#cX;íè‡Ãdz`v߈­öÊaHaåîu{ª˜1[gtéåÏÐuÖºo ùºU‚[©nä\–ï·e8Ï3Y¤ £DȲ<}Áó ®ÇúéÅdÁfïý;ö±Càé$ëŠÉ^*†{éÖº@¡ uŸž‡Ó|ïK8þÍÓ+†¬ë(\ŸÍ˜ñëvfAÍMŽW}¶…¼9÷‡. U1§6ÌÖÖ©:™¡ÏÍé{¬ôºˆÏ¢ÄqÍ.»¶¥_"YXxç4fÙàYð Ø`íÃ}7WÍ/R1ígTÜ¡ßlò/ç,ü{®_l ûq/ÿ?‘8~ltè–¤a‰P+}˜Gl·@dÁµ´€Ê*I¡üû‡å*†ûTõØIÂõ§õŒ/£‚?ã»ÏV5±“¢—øóÜTœG¿ª¡ÍfÿD3çëô[á,œÝ=\sgY@e®“íLMï#[¶©˜nÆþ›Î ߃îÿé9Û¹3»m—.ìç#‡î»r‘ì p|îiLøÞD(™_úì"ö= kwöÐÎËׄ-3ö﵃˟=_~@Å<¬É=¹™"ä½Nü{¥Â¹øý^=ÛBÙ7£éwê½KÞRÔƒFN*’Ë3¯=²G5wlõl²Vż2«“éª ê&¾ÏªHÞË8E~ïÓ±L_-Áy^÷ ‘/K‹´Q8×=XÌ"Ïó< QÀ°÷ë\UÂï%hÓþ®öº¶Pâ÷d8¾Ô±Æû–ÀŠÜ/»Xè?!¬IF¿,°x¹úñ”:£`›+wP¢"çêA¿Ôgúü‰Þoñó3ŽÿÀì½û¨‡ W—…ÔŽÕ Î·É‚náµ}3SÀÚ,ÃáÅ–*Æ–9¶ äæÏu‹^/ëÙ/ϬøY'è÷ÐåŽÿب¢rìÍð¶Ö´Æ>«X°½OVR! ¶}~ؤޮ1ðªnÀr7\¿NtÞ«5ú:¿~î›é~™_ÏÈ~çR°}´o§#àÕbV³Pjö„éªÍ„ÕÇBU{â ÅßÌûc>WÚòöe—Iåxö¿îgw–z´°Ê,{¿SqÜÜ×mwn\|ü©2Æíò=n_Í„5/7{Žmê§ÔîMR©˜ˆý›WÖòç üs§‹ººš‹ãžôkÞönÛ£ _WÑöìNø( ¼’2áÌm&kf9Ã(—¤‡•«˜/Q™aÛçOú[zŸy®{‰ ýÝÏèòÁ®PVI÷àê(Äs¥1Ÿo€f»ÅL0ek5zïÕlÃÌ&¼Q1KZ_m|4ø—sZOé} ï ŠŸ+KpžÊ³+ú< ]TÍÞ¬Ãï±°aîÉë;3aísöY`ïþà^óe· ™t]ž&ä5[>Nß ñªË7°æÍ!/}«7¥O1ÏæÔ®Yj¹9ÞÞø¼Ä¦8LI^ÿ`‡Šáã`ºP¯éç矇~.³ž)pÜÝ%G[ž9 !í¶•†/`¡ñè×Òµ™ô~ ï&ûžÐ£?¸òV1ÃÏÆìw”Mêœð\á±o¡«åÏõøZv»ÐMÛ—Ù¯*qþó…Mœï5Ã}¤ë‰¸è.a™¤ëЫû¸ƒ­TÌŧÑ#lLÿeIÏ)iDûOº_ÒåÎóp$wçB•˜ØqóY8xÞÍvÛ¢L¨Påún§¬¾`ªûA]<ÓDÛå\ƒ¼Ÿ×Ÿ>¿¢õ›ö1e~§„ã7œöמ*QGÕ¸ î»ýs_Î ½2JV €oçÌíyÏl?^híæ:í—u¡|=¢çFbny.Îc;qJѲ+G{»f]0 Uê„V89Чvè¾`ôq¼µaäxƶ¸q¿±¦ yFó¾OEï7Ýwˆ×S=ûBþuZIÐøí,x4êñeL&´wž½Ìi©#LYpÞvIS;>´íû´)¿ìÏèï@h^èòǽ<¤×Ê~Š$¸nºÊË…Öo4ÚeN™ÐÓ¼Òã5œA÷³1#sýFô,ÓÜÉÂ>‰ï¯¾ ×Gü»\ŽÛ|ûÀŠÜ$È5¿%©âÄ‚é½ñ!Î6™0õÞîRÿAÎà”^gd^uÓ§dL‚]Í`á‡Þþýi}á|«Ö•+Ô‹ùuYãOÿP|zØÂcn˜Ws„ fG\\¦v΄º­,ã3§ÊáÚÜ]Ï}Šgø÷^§ qÊ׳*B®’ZµöwKè*çH÷ü>I‰ãÛÏ~–§ùz zëý0ÚûßKV‘Þ-3aÒ›ƒûSáR÷¬ÓŽ…ñÌÒìW/ÍêL+wÎõVX—ÅÏW"qÜHì&ÖŽ<+m£Æ=ì熜3]R'S,ë×z>BÆÜ˜ä‘ϬªÚ1´FÊ´_žsÒçuôó;4Û]ç¦Xœl3“?§KÅy¬|Îù»;‘%Õ6Ô²báMDðžÈJ™ÀÿÌþvïW+äU<£Ý“ðÚsÒô_Öû`y¢dIúÏü¢Ï9éso]üã<ø×ý*'CðÈKÃjwgÁüs»šï¿Ü‚«­#¼¬sd0‹{-ç¹³|ÕÔB„ç[4>éy#í_ø¿ïJö™|žé ,”µ¨¶¸‰k2ì´j—$·`aè®óßÞwwmëÝé6ð|ýÄ ¦÷ã™yÜköÛB„þ‚î¿Ï8Ñ×e]% Ïí¶p5®Ðsg]>à<}¸ŸEîI† 5Û¸duaA¯w¯ K· ýÙÉ¿ž’Å¥wÓÅ3º_ò!Úf²ßy¨ ôaÆ èôüüWå%¥Þ¿Ë_I|ç îÞ–¡˜…C½*çm.frÞæ”Im^ΣD#b²(v Ê0©Å,C1 GN¼*ÅÞæbŽBä±A’úøŠ™å}|ó LC||•"¿ ñ+ ýÏêãKýã8/ß4”¹ÿÅ%—?*ƒp žæÄ×<”pp8Ž¡’ð¨)Çè|*©¯¹Œøšë‹ü•¤„ƒ#ö5ó¨Å¾&ž"æC0ñª¤KÔÛ\C˜Ôþ„ImNüMŠËù›P&µLä?Gý|%ÄßDìAgH˜¯iį²¼¿9·ûS#ÿÔH¥Þ¿«Fê“ïÄr÷†ðÂÄüêA÷;?_1/ÌHÄ{åxaa¨"”œð^)/,倭AÞ«˜&æ?x:±Ÿ¯˜ÿà/b\G¯sê­)ö:/ï¯YD¸8ÉÄ_“ót*ByO' &Ž•‹rÀÒ Œ0‰BE›Ô×É•’JðïPú˜\ÁœÇ&a…‰½|Yâå«$ìŽFX¯”ý ù:êåë@¼|9:Ê¿–öƒØËWÌzuÀÄAé›ó¾œÔï<”øÐQ6õóM&¼×`Â{•b‚G$Wôä~ïÌýv÷'ïÕAäå/ò€ /çeD¸ŠÄ‹®¼§/·ûÑù!êýgjdùúøŸªÜ¥þ¹þ§ê ¸þ¿äUgHþ]ƒ,˜°À¤"¶õ¬£>Á\ðQŸ`1 ,BÄã<7•å`†œ¡¨\”aÀŠ`”ë )çYLXd‘÷±)Û†²Ƕ‘–u>aÛ$‹<6ó‰Ç¦æ<6)Û†z•GÄðGe ¤˜ ‘(}Âü¢^Â1$a8Oa…ˆ Ë18oa¹ñï½ì8a#ÓŸ~v2¬g1(ÃrþÂb&¬ØS3œ°9nu1ç­‰‰˜&òÎ/Ç„õ'žšæå<5)6TäM!ò&O-çMα#ûpïB—õæ~i÷§ÏûÓç)õþ]}žù̹ܵ'œ/1·A†Aó^ÃbΗDÄ…å8_á¨b”'áÂRÎW>JŽŒ’.¬˜ó%æ6p^Ãi(ß~ÃbnC°ˆ…Í1m EÞÔCýwÞÅ„i“J¼;ÃQÅœï0&M*Ê' •’sLl”“H)òïŒAbB£X”L‚‡2Ää åü; ãKì5œK¼†Ã³c|…&,e6p^Ãa¨"”'&c*ÊDä5,'^ÃbN¶a6ˆ½†ÅLX9&®eH<>9ÿusµó²©ßp*á†.¬¬ç-…ãsþë¨4”‰ˆ +ǤנŒˆ×gÊ @ªØšû!þñc%üléo<‡ÍgîŸÿªFŠ_ÿ§uñßê½þ«þSüïöxâú'®wJ½_kÝÿDû¯jwcP†T¡„Ù%#ì} °`‘±¢«Ëp])«+•x¨‡‰X]FˆJT>JN˜®bNå@pÄá¨â:¼ÿ0e@pŒ.)n$JŸ0®)³2h(³ðw ¸." Î;Ý„ø¦{b KFaÐ+Qù(9¿FÄŸ¡¾é‘(}¼XÁ(%äˆA>õ$Ö ôÛñ¾ÄŸ‹r\9bOLœd”“' UdÂûK0‘”¨\”&”eTΟ¸XÄn LBO’dþ¨ ‘'±˜ÓŒÊ@I1ù"PÅ(O«U)òEù¢§•óE!Iêß·¬'q1ñ$þÓÇýéã”zÿ®>΄|¦|îÚþ–˜á€AªAb †¢rQR ØÈrü-¯•ãoE@V^+åo¡<1°SQ&„×*æo‰™þ¨ ”9}8ª¨7Ḇ¦ìJT¾ÈƒÝ#•‹’b‚D’$QvsL–’0þ¨4”9&N8ªå‰ ”Š2Á$ Cå¢0™4(#L¨PT.ÊA‚‡2ÂäR¢ò {+%ÁD kË3¬¥„½UDØ[$ù(3“0UŒRàÍIC™cB†£ŠQž˜˜ÉåÖrÂŒ0ÄD Eå–cµz¢’QF˜¼ÁÄ«]Úgéˆ9Ö\BûnÇkU^«&x Ê“<•2ñZ=QÉRÎ/ çGe ¤Öœ _üQij]I˜Ö2, ¤0ø£2PRàã’ûç?]#ÿ¨jã¶6r÷Rƒ2Â`T¢òQ„CaˆŠbQ2 Д>©¿ˆEÁ1ÀŠ"†pÀÒP&¼á"Çl•` ‡¡ŠPž„ÙJy`b…9x rOT²ˆG‘‹’aÐÇp\0°¦LBÊÒ¡LÂß±tÌ Ãšcr,4”9&J8ª¥À„IEI0iÂPE(OLždK'—Ûãb"JðßQ¹(L( Ê“J‰ÊGÉ1¹’Q†˜`þ„×jÔžç‡Q^+Ǭ(渄˜x©(L¾pT1Ê“0e‚‰†ÊGÉ1!“Q®`Q2LÎ’ ”×j‚‰ªDå¢d„££G8:”>&o0ŠE™cG¾˜˜ÙŠb{p^ºœ/,ŸÜbfk*%',.áýQ(sLü’üÃZƒÒÇ"ŒJC™c1ˆ Añ§oüÓ7êýûj£”ÌYÄ];“`p*Qù(9i2ÊU‰ÊGÉ0`cʱÆÌEÜVŽ5‰ÒÇ@ö'ÜVÊ+F)0°ÓPæ„Û*fI1Ð#QúìÁ(%Å @sµƒ?eTŽiŒ’`2„¡ŠPrL ÊC‰ÊGÉ0AbPú„Ý“’b²D¢ô1a‚Q()&Nª¥ÀJC™c…£òQrL¦d”J‰ÊGÉ%øw( &Wª¨5ÏKE™`¢…·å™Ö2Â+&¬±HÂl Fe ¤˜„$ýQ()&dIJ*µÓÚ“Tƒ2ÂDU¢òË1[¨T”“7Å¢dÝxΘk­ ŒÊ ÜÖ0Âm•c‚kPF˜ä¡(%q[RΛÿ+Î;ÿ»çõ‡×¥E Å¢Ì ‘ã[;ôåüUð¿caF±6œÛÜ?jdÙù?U¹Úø»ºøO5Q\ Ë×AZiýû§s@®ÞqµŽ«q¿«oÿÝÚö¿«kÜ5Læî%Rª%Ç€Ò Œ0¨”¨\”—eˆŒÊ@™þX1Ê“°_û5eŽÁ!âqüW ÄpT1JQŽCÆæÿbï< ¢Ê²½J+fÌejËŒ3&v™°Ì˜1—m¶ …ÚŠµU´ hk[Ú­¢–™ ÜR[)Œ…0—1•mÂüý÷œª v÷¼™7ï}ß|K×ú­™åŒç­½÷=çÜSûç ,Àj.R05óÀ‚T e2 â.jáPŒj…CQ‹`6Ú¤p‡b(n_`òPèfà†`é@‡ 7?¤-ÀTH‚ ¼‘ Q@„6æQDb˜€ Éaâ~2æ‹õ@’„òD1w$K(O0³÷H$މUAòX d¬åpÅ#‘RëÈ^D_àŠdÒ×cŽ-\ƒ»È˜#ÖÊÖ´H,—ƙݰ6¶žE’ ͤf¬o þ®¹ì‘vEâéxxfv‹‰³¢_kÑÿ_óµÿÔ¹š–™Îî ‚Ò Ü˜ÁÀ| & F;Ëk-‚Õ\°¾À<^XppEûq/¬ÁÆÚX€÷º ¸ý@ Ð ÈÀ+]¬@ƒ€7ð ×3Pvx®}&à†DéÀ ÔHŠ``Z$‡¸"Aü@ Ð Q€+’ER€Icà‰ã ,À  lÀ‰dnH¦``>jüpCb…€t C‚™;’,”{µH6O8oƽ°z4H@pAú A2€ Ò˜³ø±u ¨‘¤ÁYœ°¾À Ü´AÀ ´HÞÐ,®lW$²¤p/l÷Âú ±£2ß s©0/s\8¼°¾À Ü‘ðÁMXßjŒÄ7W$¿¾ëAŠûnj̛íb\Qôž¬Çþ ɱÏþ(kã_ÕDV³ž½ûw½—ÍZûþ®î)ë«uÿ¬ö¿SÛ”uMçôg ä$׳ö]¬¨a¢v‰º¥¬YV¯Øý1±Ï AÂÞI PÔ”` ¼QŸ¢Øûvž„ó½ª@)ÀAd.$?àŽ` åYæzµ—¸ ù A ®:_`nÌó ¼Qƒ¢€š;ªmÀibïPƒ¼˜Qì]‚SÃÔ.P?4TpAÍñîÚP¸jn0°opP#ˆƒ ø ˜MÀ Òmfß•@p›š{^5rpA û ð@À€ ‚ÞX€;‚?¤³µ!’À ÜA x£ÆDeq¾º#AB€ hÙ9àŠdQ!Y‚ê1'sMâçå®WuCæ0Ãx@‹šb® ×k’*óEÑ7eý«1&÷KG-¥볈¿çžV¿¯ó«ÿõùÕ×½°¿Ÿ_ùðÃÒX€;3¤Ô ܤ!β³ÚÁ\°~ h¸! }? l*±XÁl.h?4ì0àŠàÖ+Ð"È@…@©@‹€.z_`n…k’À Ü‘¡<tÀÜ! x#9¢€ ¢V E¢ ɬ@‹¤ .H?4H H:$’¸#™B@:û~˜Ü‘X¡<¹|x É À‰\Øw€¨tAÀ ´H¾0àŠÔ+Ð"À+’ÑX²x®} ¸!AC€ h‘¨F B²ú pGÒƒTàä5ÔÍì»VÕgN_æ§ÅøHèPžÔº†Ìˆñ‘ÜÁ h‘äF B¢û ð@‡€TàĪf¬>þ®ëcŽ1¹óÚÅÀTž¬Ÿ+þwàMr|óPü·}ßáßUÿõPÔÂ¥þ«5Õ>Óÿš§¬wÿ•Z÷Ws/e]û»¹û=Íìó@…‚tàƒ@27S°• ¨XA x À ÀAfn4+Ð ØÂ€+N,Àg.>?4Â0àŠ@Ô+в3olOAiîÌP`>¨[&à†@ é@‡€57Ô­``>`ÛÏG[Ù^>Ù¬V E@‡Wµ°·¸ À}‚<¤» ¸!àC@:Ð!ðÍÀÁÊÀXØw³Z$ApE"ø Am ®H ?< ¡ý¼ÒZL‘ä¾ñ¼ÿ=®ÃºÕt*Mw ]Ó^®BC}ö=Ó\H¤kÕŽþñco²u›yøèìpiB¬eÀ¶åþvo„èÿ!û4ý'D_«L}›p$Ÿ£WTÍ¢©Êû£MêÚ£åf)‘Þ…YEïïM¬›á¡Éá…-.zòh€½Ï…è$ú­(=•ÖâÆÝƒ©Ã£ÉܦÃÞÀ2)Y¿äCÃÖD’û…õ§n±ƒ¿I.½«|;âÅ„{ÑwBx}Eùß•&¹OŠì4á:9*SÜ´2š,ÑW_/šB7’Wlü%8‘dü8:Þ·>\êpva‹}˦Øûàˆÿ^yqŸØ¸VŒÛÔ³ßúº§£‰Yå{çO¡ã…Õ=_ŒL¤Š=wzØuM{=`k•#áÒ³íë_¯\?‰÷iºí)¼<¢è›+ßÙê¤MÓ|zÑ@ò2šÊœY¾jpÎþrÅþæm©ÄÈM.ÔÔÑŒ e¸X!ø…_vGñóF|¶ëÄš™ú­©1îùß›™~*CÑN :¬ù”LÓª;Ô¯šHšNGç5ŸÝŸnçœ>CU~§4tëü<É~?Dÿ8Ñ_XÄ«Ofvq'Ù"÷ùÐà:GƒÈîæCmd^'ÓŠ õvÞΟH—J=«ah/êln›¯ÙNi9k‹su²$ú¢Øû ó>÷â>Mûµwh\²; ¿EFà:ÇF=²ê&ÄÐÒòN¯'“áüù*¡ï-ô¦Äb›&² •+øC@Ž;¥ùá9ÔOÓ¦ØûȈ~/¢žø|¿«¾øhíEµ3õãÂu*›ä:77†>­ŸýaìÝdòÈQ8G‘§Š(Ò¼ú¯¼h`†èz§Äº¼M®`÷‹¼£iwëç|òýNå¿ç.6~†<† +0ìüÍdú¿R“w-TaªËÚR}ZÑŒeï+Žñ;•úªÞÞ?7kÌŒ¸Çx[:_j±':†<+?øýÛ+É´qUÁõK¯Z(d¢zæïyZQ“gÓ)÷Â%íÖÖÛ\×Ûï‹È/¹`º§²©㾩peéöØÚ}uÔªµç“iÂÒjzŸ·P©•å¶ýÛšÆÔÙü$4&\Êy¸ñ|'«^Êì¥xkÿ”öiš‹Ñ›WºîŒ!¹ßi29O÷Øo¡oöÅ­xuÏ‹†|ž2þÊáÒ8ÖöñÞÞÿU܇Ì2“½/ ²ß×yýìÝé¾c(ìæ¼ˆË'“ij¡ƒ=ŸÅXH:ê1þÁîNô09©ë羨?¬¼ý¤ÿÂ%îw¦þdw¹eõ¤j‘1d]_hËá#ÉÔïDó[WwX¨à²}ŸïNÌî[¬Q¸4µ{•†û²éí}¤²öqSö9Õ±û2¢îî·Q1DÛÔT::™’ýžF­±P™ŸõCë¶íI’péG×oÇ•ÙíoïÓ/Æ›Ü}{ÅåñOìy+ú漚ÝuÒ¸ç×™z´pA·Ÿc¨e ÷áS“©Ö´Â)ëçXèSùÍ-r,ëIß4Ý^§c¸ôü磾)樗™ûô?æýëdò[0~éŒÆ_1”æé„ÐJ¦ƒ±C7Õo¡|“¯¯YÚ“*oô©Íˆp)W•Æ‹v6œ,É}ì_ÙûÉ}ªÈuÆ“Êg/°©Ã™ÚrüwHÓ ÏhXCÂï ˜•Lß½þùîæ†Ê;A=>tOg:÷:5ÿ›åáRµ»œ» °{6Äç¼ÐÑ>Õ½÷ š¥ÖÌ_Ú‚„7%#î1~FÛêÂ1Ôxˡޭ’©H“¥£VµÐÓ^–¾y}ÚQcÖ.ý·p)ž=‹8êBЂo •úà9¼|Sç¥ÎMIéÑ`Ü›ÏL_$†*G0qb2mÚ Ÿ4°¬…žlnÝ,{‘Ö´Ts(r°1\ ½Ò=W` ½½OUVß‹è‡/ú.fÄ?ÆŸ4=w±21ä~Yo<>>™vE´iÛ²˜…j¦yT|vDC;6úvìîp)£,øè¿ˆKá}q”ï·Ç ›b+Å¡çûÉÍG¢Þ|4¿…nÍœ¿¢Ò0 ÝíòP÷qc¸ä­Z9ºA´ÞÞOPx`•^5ÆÓÞ¾÷p@·üì“~W¿djZ Õ=g ¹ºo¹5ª ÙæÓÐqøýØ­=FJ™û>á~ÑWž¢®²® ã×ÎÆ:cÅШá!±.=’©Øà_Þûò÷»µ#¹ïa¸ô)©Ëʹm¦Úï³x‰¾²¢ÈÞ¬ú™ž«V\'ç°6›-{bè×\ž9ktI¦%¿ºÍ_—|Ž^å-§ÍKoªL·ûì¼µCº1ùTç§Ùû‰xÏ?QǬ5b^­ûVî+ëÔ1M#ç_LFU.¬M¦’%öž|¾ë«»ð÷.G{S5õ“™AûwHý]¾÷>Úišý¾Û}Tu+ Ÿ’˜¿fÄ9Æ—çM±´ç¹nñuÏdºÛ³üÅ÷ÓÏQÔÂßpúSÇÈ¡(a;$_õê3eVLµ÷'¿Gù.ª†å´Ï Dß`eßl ®Óahûè÷ebéc×OOË6L¦l«;®]Ôú­ ^¿#i e´©Ì.½ÜrJä´ç«ˆÏ:mB?là˜çÏ:³j¹zð>ŸNJ>¿–jÆÒÞu‘Ucj'ÓºSº­ÍuŽœë?¿}öA47êû×g}ùO) Kßr›½_®øÜ…çJÙ÷>×Ùûh¬™b)£]nd 8á±øÌYêÞ¥àˆmЋûÞ.ýþ´Ùüøþöyˆø½ú—׿~Ð {žÉñU‘¯w¸ß×ùàô[£‰ßÇÒ½5GTËÝ’)"!wBTèY*êÒìøŽ–>”ÿóÜy…KÑÅki®Žq<ϲÎwÄïs¯ê%ϲ˪ñç³Ç&\ç•vÎÕ+Gci× ¶^¹*%SžÎw–j¼tùt°;5êsfR›¸p)ož9õ–T ø"rl Ö±­c$懲—@®V\糞:¢Ú¥7¼èQ&™ºY£Ýñ, ëÝäS¯å©s½ï‹¿Þ.­ÓZ¬ò×ÛûVoexxÕ#ÝSxyDöŒ<锦‘û ¢Œ6©…qŸâS†§Ö?K×{Ì™ÚfµÉë‹piÈâ›RòLµ×1窱^á…ÌÈ\gYïý¿ýZæ•Ñï¿¶67žŸêš%”9KáÚf•¾;ÖŽ:<žûr`¸T°]Ÿ‡O›NËÒ‡9‰~­¢¡<æ~wŒZèÆ·‡èÊm& M¢9}f¹ÎÌq–òi>Õ°»Y¼"î>(\j4y/yŸfïw.âkC¥ìSç{!χ0žnÅÉ¢“\ÑE[­¥Ÿ%ѬFsr?¹†Ô¦”ºÃkt¡Ï{¦ýaô—úä.¾ ¾4õ ßð<‰ø‘}/I^wËë° \ÇœÖû\ûg±ôô›‘ó*\O¢ù+ßåâ -¢{/îéºQÆr½u¸4ìi¿Vž©ö8ñ#ê­ÈÑçQöðÊuÝ€ëÌ}±}ت󱄋œ«fNâýÈÏPøEâczл§½W§¢®ã¡XRhÏo±.¾öùÀõ˜nï´‰¥IÄÑÐQy6$ëíûâ~É>äìô´D‘5w*göîvNÓœcmR‰¥kg¾ì¼.‰n®ýmé3Ô¤ÅÊü®}¨í¥]ˆðpiÇóó‡ºêíÏ%ñù ÿ‡²¿ãúNù~þí¥±$ï$ÑÙÃ5W”N;Mo£½üÌá>ÜÛ.¥^eb¼»§TøöÄÏ«ìß©Á¸¼¼š<+–Ž,“òlaÍm~:¬uÔijuûØò¾>”‘nóÃ¥ÙË×4Üm øÂ¯${?\ìõFé Òaüv>ÇøEéĵýs“¨ù«' ¾ OS´nð¦ã.}¨Óç=·††KîºiŠžvÜÁu/×®îè§ùp0K(wŠ›oúñgî»Âø)í¶EN_KrÜ%Q ÿCã :M©yOÔZ\»'˜½•#\j÷ùÔ~ßlSíÏiqß(mÄÉ9ì÷)#Þ1n¹M#µÅŽÇÒ“µ•³“DÕïÜ5Àã4-6ÞeãoºÑ”‰õÂ¥l¿žúùØ~ÇóYÔMyÿ'÷ô44.¶Ýý€{æù®SœMP'îWˆ+²wPÝ;ûÜ¿Z©ÓܓЉ"_LXóëUÙs0Õþ|q){{²ÙûšÊ}þëeöéâ:žÆ'wìqˆ¾µ¶Ï»¦êÄwýÎvø|Šö?XòtZö´¥‹¦ÈcáÒÄ÷—7õ‹ü¾6‘_ñÞ%MÓ(aYz±ïQè|Öh7‰–kí™ÿÍUñunƒ–t»ŒÖ&¬—ضMó©_ô%þ¹â|ýÌÙHÔy^Q/S©q³*fF;DýwKižDÖƒ›»9EÏr<ú©qGšz쇻Iøœko¿2~öÇ}’וùs b¦þûŒÛ9¹ÈÀ3Ñ›¥¥Š ­Do.å}I»N‘ì÷¦ÒµrM .MɃMý"_Ë0z÷½§ðéˆëdÄ?Ƹ:ï~Ó%‡ˆÙ!WH¢ÑK†l_xŠûSzR¿K+–~ï.²¾¬}c™cYEvÿLsg9Î1ŽKsfô÷Gó¨fáRý°F…>:ž[kžoÙZDJ·÷[—÷‘ä~ë&ŒÛ:C wˆº'M¨²5/ž¯M.íï–~’Ö5møcZ_¢½YÃ5ˆó‘ÌXh¾Š¼^j²Çº,÷–È+®sµè²Åc ¦¢‰»"6~ºD./õ®ÏŸ¤S9ÚEÍ,íCo"[žú>\*uëIKs€}ž“çb®Æq?;ö=…/T\'#Þ»¦iJ2}»ö0­«š\Ôüø=uí7Fµö$…U+bnÓ“þ8Ó¨ñÖª;¥‰å—Y½NúÛ÷=Åx‰hyÕöÉNÒq©¤×âêÓ¹¢Mwíî¸S’=xŽº(ú$˜w, WÙÜö:"¼*b?&#ÞqûêTß^ã¸WååK׬„×IJëü~ýDK%ò^øHÕgÀNiæÑË#êM·?—ļRî“]ÐïêþÖŽï‹ÏûЦ‘ã×)jöBZG1†¸]Ö\¢­)5ÿðjy’ÚìÝÔÍoTe«Ÿº¥fëR†Æûó´,€÷ž;«§Í6æþlß—‘}(3Õ/®sÓt×kùþ8:ñ.g•Æ‹.‘W»3{f7=Iñ½Jw?Ì6ŠF\Ë»S’½Žû&ðzñŸ¸~6ni'ÑB[Ù':]¢ÛͪÑ/ÈxáÓá“!í©êk‹go[¸äÖÆT¾çöÀ/ücâ¹(~á“ï‘2ò×i¼¡ÚäAùåJ[Ù®ñ%¾ïá°µ—èXÙA'rµ'ö2Hw?\ª1÷’ÏÑ+zû~Ÿì{r±Ç­\ßÚÒ\íü2ýÏÉq„ñÞThì‰&Tï>dlùKä_Ó)y°)úSϳª–bê¶²0×NéFÖQÞ±ß?®kÃrÓÏ8ֲϴÝñ°‰®/{È ßwµsØö©÷îMÝçÍ)Ú|Þ€í $ïw¶¥?~˜¸½CÿÒ‡ÏCŽú:Ö¿ÂÏ çÅ3ûº·Ð†»Òvr¯'ÆÿaJ/Cñ9½(ûá÷l//R«ŸÞô_@×Ƕ­$ªÀ^—D5s÷aûç,æržÛìõZö¼WÊä¶â:íÛlþ“D»¼Þ=ûæEŠþ}y® ‹Hö«Ô$UÞÖw_¾Ú)í]ýü§\z{½6Tž1mI‘WöçºðæeäC7|¾EÖUZ½R")vU妧.RÍ^>ƒ‹'ÐzEv× s–tÍT»»:EH•Ú÷9^úËu—ð{ez`ÜÞ5ŠÌؾQ¢­Uk>Ús‘Ò×þ>¦Å’zá|´Ëî1îÒ7£Ol~øx§Ô»Ã¹—…‡9ÖA">ž“X/Êžy¡Áø%f^-Ré€D7oßj‘kõERo­xh]uøló M¤.-g4úþúN©#ÝzœTÏÿ"ëûëãz-yé)|™¼çÿîå{ýË%IT{GÛ6u/RÐ’+{7lI –†¥Nœh,åØÛjùÓ;%Ï€;õíë+QäùÔ#Çû» ?QÕLë– \'®øËªì&juö="ð"ÕÉ $ÐÀœÝU®U“J Ó•/! ^÷Ë®@{Ý^,‘_â=šò=ƒãg,³Š™èÎwçjæö¾HÍ3¯]Þ“@²?âžgÕ+½´oÛGH«^¿±¸ÎTûû_±ûIÊúcê&öLt,?ý\¤Ø1o¯ ÇÏ}c[×g¯Ô£ 1#¤/ˆÚXÔÄ¥éT)³w>¯Ý+ü-qŽqõ?ºa%eèp\¤k#NNþ%FzÝÕ]mךû#¤æG>=º¶ÏÓÆ-/xéY~ûþ—2^œº§iî4ž·*Ou;YìÆ½ ©Y­…wÍM°ï‹|hòÍÛËæÉólƒ!ïô_xÂä÷êìÏ{áQQî·©q¯]K×4Yge›[ð"•LjVståº1³TDßÎôSïé­oíä÷Vöy²ØO:¾ª@Ýè3íŸk0nÏ&Oª5©j¢wÑ›ª5ûxrßñú¦KÝ'2Gݤþùn»‹"¤+ΪÕtÓß'"¯Ä¼P¹îÑaÜ7ÊD^ÂçxïÀËÞ=º@ûûÏ>¡q÷}´¡´}ãç\™!µ80ÂÔ·ƒ¿ýçõK~~8<9¶ï„ëŒ3Sýà‚&*Z±ã}Ÿø<0VÞP÷Õö&47F»©æô)ßîwóåð·¿Çq#Ï«>{&¿¹¥.RÌKËqŽñnÈõà­DÅ·õCd] @*î­Ê&PÇ£çÖëÝè©Å¨š!u=s/gë²þ_xþÄþ üü­AnÕŒN[óÉ~?Æ·èz<èö\¢™óg‹»@ŸÌqS –Jà¾É?;š4ö^„4íí†*õo8î¿øù…_9>&jIN[•L^mkwá1QX¶9¿u ¿@K|WRõìnÆBZŠ jÕâçHîãÔáEZéòËÍž9ÙŸ·".3ÕûišŒp.b¢7ÌZ¾æ½~9vw£ò¸OæsÜœKHš…ç6è>GH½JØêó ´Þ"Ä>­˜7ˆç¢²>¨q&Ìê«6QPÐþ1És/PŽE›»R'ðý•üt<ÇÉ­nEH!Gõˆ˜çX'‰ùÃäÙËÿxZñ{¸ìxÅϧº}¿*#/p¶ª=æf¢ZïªI¨ÔÙGžJ㹸°ñô2%êУï;h:°¿Çq%~·–•R/6uø.Å~ž¸^Fžà:e·™™¨Ï¹öÏJ¾@ÅW̘£êÇÁzkòX›Óƒ[Î[/ÄDH§¢‹bŠíx/ KNþñΞ×bŸPéI4à:ûzïkrvœ‰rvœ’­Ný ´tlzÕì÷Ít½åãN›^t Œíô\‘’®»ÃŽõd§“Ê¿?îØ¿žDå~¡ ã)pøø±i&ºöóM÷Dõêïs­Rx¼™ží ¹ºÈ§+¥¤TÞq0g¤äþKËÖŸìóD±>çZ”óh+Æý­©‚4ÃDE܆:VàY.µ<¿ÍL£7åŽ~SÌ›FÕ`Fç©ÝÒÉ9½ùÛçòøÙíõZ^ÿu¤¡E—MxÖA·žiš3÷„¦L7³tŸ}wž~ ñsl¦ã#½ûïJóú{®Xñm¤´wFýºƒ§ØãGÜÿ ¾•]ö¼Ía¿?‘íVþþH{>Oô’ó×9¤§ñ®ÃVÍeRÏSÈô“IG›i–W›ëùw¦9ÖçOΉ”Ø®ÇÕQSìó7y½ô–¿­Kb]“ÿwñŽÁXi˜¨XØqë+çi‡îmè¾.fZÒÉ¿B@¼–"«…\îû2Rb6ëáþ_œ»*•·§kç4Ç9#Q—”û:\§a›bŸòM0Q®GŸËÜ?už¤é{†F55S… {Ý·ÞÖPtƒ·VUÚ%-Y?¯Å[€}}&âSøÓ”^Ð Œ»¼53šèÖÏ[ψ:OµZí.Ö –™{´Ý©ažö#_Ø%ýž3oÕÑŸô_¼7ûžb?Ué2`üö‡·}¥‡‰â®Um—móy¢Ë7¢’ÜÌôó‹ ÛËÇæ£õ Š„vy)M lݰò—õNÞÎaO®\™0þÐNï¦Ukn¢y-×zý´ø<”_,ìÙ¾Wù‘ßäUO#¥›Gcž?5;êXß‹y£ˆ1ÿWzÑ­¸NÍC§ÚNÇóƒ¡s“ÏÓ”í4UqáõëÖÝö ¥ÿØÆ×¹ô.iôøyÆRç ÄûQ¯Å9#ù>òý ^išs1^§fa°ÿs¶¤ÏÓ÷³Š=PÙLãú?[ñÓÝJ—¾¹Å í.i|Ѿ߹íwÔQßĺ[<”õAñ“¶ÿÑ¢b!ÍÞ‘ž­òlMÔÆ eÍt¯|úÜå9šQ¡í£K\è´KÚô¢Tl›WŽu’ð]ÊóÕýöú¬ÜÖ`üëiMþpG"Ö)^ÍÏÓ¾ÂΗ?ä6Ó°¡/&Ž ÓÒˆlE¢¦4ÞeŸ*ž›KŸI\;惧ØßÆ$åqz÷ÌžrŒ¯ w^|l­Då|œ¹Þý<õ¸¿xè Çñtiñ£jsöy“¦Xãñui—Ô«žSωwïkeÏ·£­¹÷¨Þ‡m5èÃvö℟ÿÄø·S«_8JâçÏÓÐîÉ ÷ÇÇ“ìõëN·WF¿õš¼Krmwø;K€ý9 ÎeÍÚ?ÇÙ;·õ<°2‰óÈññ \›Õô^âa±dj°W@"iRή_çO?„ªªDÆU¦r)Ï«]Œöu˜x~»õ =—c´k|¤qç-HœÛˈó>išÚUiÆ£‡iÌæsíJNL¤ {èÕ>ßx’ßëÕ¥;™v-¯d”2¶¡ËˆóïN$?‡pO¨|.Oñº®ˆ\;}ïa~þ,‘:tž˜·ÓÀxÝ0¡ýÚ„rb5®Ò¥-W¶Ý©›cJ–ùê#>ßø†Ä9cqn4#ž1þKëÓ×ýsi"å[ä¾Ì«k<ŸÏÍ|5a£´ªÛYŸò…wV>÷—›ÞéJ–޹\'ó¾?Ƙسëýr‡éH£Û ãG&’Ó£bñT)÷¨×{Õy?e[³®F‰ÍrK„:~~Q÷„YøßXÑll…ú|>ñ·t O-xæmÞ{ºBÑþ‰Ôö`ŽR…êÆÓâ&Ã^œ®ÒŒRF¤wmìm”^¯û¶èÊÍSìÏ91>RQÇÅ{åºÁ€ë Híà¼cæ!°´zj÷D:ò¹÷žaã)ßUÝÞ•Ðí¹VWn”ž¿ìÐw¾‡¿ý¼Jü‰ï³}›ËÙ~žKyžÜ„q‡LôxÞä¥OZÐ.‘Fthø±bÙx>ϨÌ÷R÷?ÒÊäØðÅ:÷ñÈGó¶}pâçš’¼?.ïÇZ1þüˆË';~Š¥ É{8{$ÒKcÎ3KÅÛŸ[/šuy(ÂhßÌ?òû3'û{Vùß¹gš—9ù¤i2^g_‰%÷N[óxUN¤57 ú•‰§h]L?~夵믔ÜzÀ(5Ü?6¢NaÇ{ ù\³ã<šØ_UžScüçÃNìhŽ¥o?­ÛÙ­`"­ï¹lÙD5>糩¬"m­r¤ôù0£t~ÇêÉ+ç8ö÷E}l³ÊÛ¸?" _®8W—é\®ã}ì qü¡XJ©¹fÉÙ·º|a´‡©j<H]=jBÓŠÒû¾5Ûþ±Ê(iº´\õ%ú/Î߈ù«8?-ê¥ò{":\§Ò›]*¥³ŸÃ—–yh!í&+7U'á­;¤A« Œ’üü Ȳzä¹½ÚøYÛó~”Ï{b<ù}y,ÍZÉÅB·¬G¾YV9žb—[cþ†ô ;¶d”2^G÷sÄÑ¿¼ºÙ¤ƒsfß8Æ»° vØ„µ±„‡äêìç-t8:·Ë›¢ñ4èz¾kG¶¥wç¯n\<ß(ÉÏuÿ,ß§x`¯;âsûƒÊ}_®³-áǃ«ÅRÃóÝ›µPý_ºOèóö-TOʳæ]*ºæN±à_Œ_ìóˆ÷gâ¹$ö]”ó`+ÆßPØõ'¿ò±Ô±q‰~…¼†^¿àž|‚Z˜?<ùð¸ùN]¸»“d”ª_zbÓ Çæ-{Ëä|ðEÜÇêÁÕ”Ë~¿ÅsOžoÈÏ= ®óiHÇe[§ÆP^ÛõûK~ÂçÚ¾lí9ÓNçý0mõôV4|²¦ðë'F)APh«›c¿T\ïÝÜvþNårÙãE~ÏQXŽkŒßmÎh•g…jéöìŽïR ©›Üië7îùçü`‰UŸ\Wy~üÂ(UÒMœåØw÷[¼W~qù—ÇÂø}ضÊÑhªö¨q‘ ó0þ¦2±ïF úÉ'bÛnšŸýÀœBïRö="mµ¿¼Ob_Ô3ù9 æç/ŠÈñëDŽÜðü‡‰Ñ”7Ôh!ót¡þèü¹[Rj·ãÙ‡Íλ¥y‹¶°Õuœû×çîÅúOyþ΄ñ³û!ÇO¥£©WǦ'ŒµP©Qµ½ÆŸ ;5½ÙT¦Št|IÿÓçòï– ™+/mîh_߈ü—÷—uFÔ³K.;Ïã×Y°~sÅè(šv(¤@Á!:œWóëäôXWàt鉤[s­(°[ªZèÖ`ç%v/±¨›òü,—Ýs­ô˜;õÃ:Öd>µÎ'Šž¶š´Á«Ÿ…ÜšQúð”öï æÞº®oÇl»¥|L‹¼ÕñY<³~Jì£ÉÏ{y=®ÆuîÓo7ã Dш›ÅðèµP³ q7î—‹Ëúþɵ)¦öëô4£´ñûNÅß”s¼·y'æÊóZŒ»kª©ùð“iPÜ«!‡»Yèñà__E<-¨±{ú¶"MéÆO æõ»`”7ŠîðE¼Šï‰õ¬¨s™¾×ˆëœImw¬Ú¤ƒ4çÊ„'mÚ[¨tùÎ9—÷>A/5¥œóÍjE­—Fà77JO^=]öfÇIì‹ú&žë=³-\ûn]90nSÎ6Á5Rír쥅ֶ~ÓmOË4x}J1G½¨ÓÍï\–ž2J[g•»d¼éø>|.Ìñaw—»…ÛÕæÏ)ùœ®ã#å¯;èÅz­ë>}z+ ­¸1«c¡ê'ø{¹v”±M•d”ËûU²è&e9çðˆûãq*α)÷{M¸Û•r ;@&öºÐÖBßÝu>ø¹ð Ð9•öçmG3¬¯×ø¦Q¢Æ Fl+7Ù~N@|â¹+žÊ÷rVŒ?8´S±;  om5[zYèè8óºÇïŽSþuÛæNœÕ–v_o¡ysÏ(lŠîR±–ãûtW>5½¶·G:¿_¥ø¸rp꟦¶îá ª¯öÓT½­m,”ü®Â¦ZŽûúX÷ë­èWýªýŸ¥b½«‡…½›òÅ÷Uå}›§˜—È¿‡<¾ã³YóË_÷Ó¥¾µŒñ¿ãøèéIÇ)‡Êzúí¤f”¶Àwr®ÝRáâÅ&6Mð·ß—Ìó7›ý{¤ò~‘|¾Jƒñû?ÜÙg?-~W) ãoÎ~º|—ÓÇid™ËsÔ"’¿x%±·H½ :ÖY¢ŽŠ8—ß?ÉßûÓa\kÐêH]éýÔ' æöø<;nyø1Ä|œïó¡n³?µZ¦Ú-MoÓia¥©_|¯EìÊu¬ü~TžŸaüÑÙ®ôÞ{výv‹mÔXh÷ ÷QOçó»óq#Þ‡ðÑ(±¨ ~hÿ¹E]³Ö]³«ÊŽì$oõ˜ýû.‹­3k\Þ-×O®Óú³s‰cöфڟTÕZ¨ËðûÙ×Ä'1ÿ*¹ôA}à £tgiã·µšÚ¿%êœ|¿©J™©‹¼©™éý« ãw±u¾0*×>ÚöøåœŒŸðCÎ:½~?N§\H ÖÕ¤r›Ëþ~Ö(å–·e_¾‡’óª=¿º¦¾ªú<º5_‡5“ã×±ç¹tæø^º×¶Š5×Úï^BQÇiÄ‚ gcµ¤Ý‡ÿXp¯ã|˜¸NEªyï„gvû¼B¬OÅ÷¸3ò`@šÆãÊÄc϶î¥ÅÆ¿Õv°;^Òpœf6)P¢è.´àSîÚ½/¥¥þ×l±ý%ñœõMìÿ‰8͈Œ•:|û«{©ðÜﮚ[2wÅÓóyaO:R6¬Ç·×’un+ç2­¦|±_-΋º/ÿ½<¾ã{÷Ú4˧Ñ^Bòô^ÓÃBWŽ7¸p¸ >ßucúô¡Äçn«lF©Eâ”[¹L²?¿äýìžòùÃfös!ñq}36höІvÇ?—ÅsåEïúæ¥SÈËÑÅ>nêK 欦ϳ[(éí?9Ëy·'öïˆyâÁ|¾oÅ÷1êÐ'ùÏ×ÞOÿõÖüOîÿÄúóo[Á6'Ù¶ÝIþÃî…ÅÉÑËN8$”±î æ~eŸtá×Q+ü:Þ']x<ØuŠžÂç’žX•±#z¥‹þ›Â±ãÇØÊ+¢¯°ð‹‰œÂ³£á¾Dï…§ì·"z xrù*ú §„–÷‰b 'úD±>œ¬g:s&º«1¾Zî'ÜÌ5æ—¥Ws‰þxîÜ™˜Ê{±yŠž,¬_^*ðæ=ól¼o^w’é¸WVôŒRzeUÜI¦ôÊ '™‰÷ŒÒ+z§‡(zF™¸[VÏ{§3wâךúµ¦9ýgÔTþ»¦89zT‰íJ/•{yB¸{BÙÏX¸'Üî -ïg,œeQ¼¨/ïWåþú´ W£ZáŸ=EÏ=áŸÐsÿ¬;%”'‹è%*=¢ïžpPh¹»Œ9hµH&#P!¡‚ýEÃxQÑÃÊ]ѳݛû¸™»Qø¸Yï=ÖÛ˜ùË<Ô_-÷²}ÛUÜߨtr3_¾’Ü×ʃûËl¬Ï15Š÷·Òó¾£îY|=QáæNç.Z#w˜1­™»hõŠÞ£Y=.ÜÙcn¼Ï• øð^WʾS*îê¶(\ÝJÏ£š;{”žGáì1sWw¢Ïq(/ :îìqã}ø¬ÜcÆò‰ýù³šúÏÖÓ¿ª¥ÿÕ:ú_©¡ÿJýTÖKQ+Yõ1kmügë!«…¬þýYíuŽÕ7VÛXMû»:¦¬a¬~±Úõ_­[ÿL½RÖ*WþÿsÉæè»Î9J?6ó*ú)z鱞ÆVÞKO¸$”‰î‘ánœP@º¿q*#¨lÀ[áýôS¬AÑûSé¾Iåî›Pî‰Pö1žw…'›÷1N/ïÊú²§ð> ×›]xÝ®Ñ˘y°ý®ˆ îƒõ@¢€KQG?PáÀ1w…/›»½Ø ðQ@„ Vô5òÞ zÂ{ƒŠÞí>܉Íü‡Â‰íQNîiÌü^5Æ, Ew5÷ *½ØÌ‡R¸–ù½ÒYc$ª ¨¬A¼o¨G7Žé/üØ,™½¹7BÅݰî† RôÎeOwæÎ1w$~H:#/¾¼‡¨šû³Sþl¥ ‘ù³C²¸…WÇÂýÙÁŠþÆîBôånæB æýÙ·ùXì³?¬¦Šzúwµôͧþ'ë'«V7YÍdõò£Vþ;ëäוóg=Y]üŸ¨‰Êz(já_Í×¶óÏ™ÕBÑ¿y •þkæ1dþk³³Üï8hr~éŸâþ æùJåNïÙîû7þBæ½él-©ðPèA Ð x ÜCÁÜ®¾À<¸‡y Ó‹É æöÒs§«<¤²úÆû´ÿÇœÅu­A"x2èÊÉn ?ܬµÜ§]8 ™ëÚ\‘$z`åѫݗ{]ÕHœ`Ê{µ»p_!ó\»!‘‚A*Ð"¡Â€+›ã”²›B8¿ ½–ìÀqQø­…§¹oÒÿ¤qV÷ s[€ Ó˜¹Û:¤7–ÝÖÌQ¨Êâµf}ˆ£˜£IlÜg¢ÜÆÜ®>ÀÜP! x#±¢€ ɬ@£è ¯ãþkæBþk /´œì Óª1>p)ïèïÆ}ˆJ¶‰ÌzÅs¿kOR05’5Xyßx¥Ï‡¹ÿ̃ÍÜ>Üm¡æŽ×îx ©@£pú({Çû ð@â‡òä÷eîDà‚"à,¼Ÿ;sa[.l¥S‘¹°C³x…ï'…»°C€ hQ<¸[Ñ»<}ݵ_kêךêôŸSSÙçl®V?¢ð.*ÙÌ»ÈÙ F Ð*|> §F°Â«ÁpEú w1ߤÒÛí†Ä ©•d·-ó¯¹ I}¸!YC@*Ðfñ)½“J—·+÷Ûš¹ƒùm­Üql@«pù Ù£€ ¯)@ƒÄ7$¿0W=HîY\”aY\”Ìýmà>J ETÿó‡‚tàâaä~J=w!1Ge(Hç6–·ìrŸ2ë»ß¿s®±Z)jäß½×5‘ÕCVÿª*÷ÿì½íŸ¹ÒD=û3ZÖ÷´¬f‰zÅjÕ?³G¨¬I¬þd­;VoVðóWõå^K²ÖVC4 èñáX¹«QéÁvÃÒ7êD;3‚ΜKö˜1£êoü‹v¾ªj*H/äpÇêhñ!€ òÛ ÜÛ!Èét–ßøðÕÈå` ¼‘ËFàÊ=ŠFö.•‡CP¤ ÷%ªÁÀ| *䦸#PBOUÙ5í  éÀÁÔÈÃ` B¸kŒ¹_}ATmÙyè‚ÀòàÓ À À¥¡ì6²÷l ØXvF3§¡ò(HáŒöeg(šËnhææfvv‚g&¿ÎC¾ÎCþ_š‡dƒhùuÒÙýF°FW¬0wn(^0±/°7sHZµ¸ °}¨àA ¸!Ѓ x#à@… ©ÀÁÔ g"óX‡€t¶ÆCB˜¸Ç:X’#”{_½‘$Q@DÑ+ð@„+Ð"q ÀÉã LÜýê ,ÀÉdé@‡¤27$V°oV¯€ IæÌÜ¥¨ô]‡ñÄó& BúàÁ}ŠJçµ;’2Ø*ÉØ(àŠõ)À‰ lÀ k*$­>‹cQé¾Vq¬¸sl*÷À†‚tà7ð$×pC²+Ð"éÀ+_Ì@…¬À#‹oÑÈ݇»(܇¬@ør÷¡š»S¹ÛÀ‹†wd3c÷xaþC÷ì²–þUUÖÐT;ÿ'ݯ¢^*kå_ÕÉ¥Fþ£úø¿Yÿª&þ+õPÔÁTÿþªöý£õ—¨{ìÞ™€Š½ÙÿÜqÜl¡ìÌ-Îç ,ÀR˜¼„Q@Ú¦V EP R\P×LÀ u-Ø€7×ȃWǽ°AâTö.@á³vE0û pGPs'l(ŸøpC ‡ðAÀG‚^¬ÀÁoÞH€( bïE¨ÕxBè€ ¸!1B@:Ð!A¢€šq)ÀÉbélßITnµIdà‰¤&àÆækÜ Ò¹#Öȱ&î°öfàÆÖ6 EÒ…WÔ,?4HB-’0Œ}uJż°À 4HÊ0àÂÕî¨OÁ _µ¸ YÍ@„ ©@Ë]Õ®H^_À¾üußéë|ÏéÿÍùÞŸí;ùð±X°úP!hõÀ<¼ÿ‡½ó€j*[û>c›X;öرÇ:±ñkì[ìEcuÔØ±ÇËhTT@±£¢; jìE£XbÏ`ìØÆïÙ9{‡̽ï}ï;ï·¾û­q­ÿš¹×{÷IÎyþÏÙgŸÿÏ„’`kQ±(o,d]n-ÂE¥¡Tô9åÅ­CÙQ¾Xä¡('J†Ån@¥£ÔXô±()~(* ¥FXQ¾h=ÊÅ8ÙFf-ÊÆXÙ” ¥@ƒ˜˜IÔ(+ÊÍ¢G¹P 4 åB©Ð<á(o4eCIÑH:”¥@C…3SiQv” ÍeD¥£Ôh²X”‚²£då3ó³-(‰ëeCIÑ„!('Jf41CºÚ(9Ó„JGiРV”š4åDÉѬ&T:J¦EIѸz”¥@›²pµ¥hfÊ’£©¨4” ÍmbW£ÂQ4ºeCÉÐð¡(J…Æ· |Ðüz”%Å&Šr¡Œw.â]K±1èQ.”„%a¼kã]ïšr·ÃQlšÛHhË ¾u¸ŸÀµÖdÙ÷_ÍûþÕ9ï›ÿ“¹Þ¿;ÏSxýïõÇÿÔùž¸þ»s¾ÿnÿ£çÆ’ba…¢X\FV`Z”%Þ'Çb3Ñ=ÁXp>Xp:”%ÇÂ3¢ÒP*,@5 •®¯cos¡TØ×bQRú{TJ‰ÅiAyc_³¡dØÓ t= ¥Æ¢•`ÑjQ6º÷‹7®§Ó55,`-Ê‹X‡r ät} åBɱ¨M( ö0ʆ’a3¢ÒQ,x+JJÀr¡ØÃŒ¨tºç`EIÑ !(;ÊWŠã£ÂÑ4†eCÉÐ Ff-ÊŠòE³„¢œ(š&œG‹²¢¤Ø»ô(J‰f GIÐPZ” %CcQr4—‰LƒŠEù`ß²¡¤h8ÊŽ’¡ñ ¨t” hAù`Ï A9QJ4¤ ¥BSZè殺WIÑœz” ¥D“ZPÞhÔ”{”•†R£qcQÞt/ÊŽòE‡¢ÒP*ºåC÷Ä¡ì(šZi€Œ~è#Z‡çÏÀÞ¬ZY? eýPÍÞmúŠ~×je¿¿âûDèó¯2ú#í‹6ž%ì·vÖí•ì÷!B¢ƒûÏ%öïú© išÕëô¢ýÀsZ,<7«D';𜻚ƒzæJž=âäÚ‹†I}<9¬óT÷Î%–ÝG…ŸÞöàr3óê.’o áùÖô8&<Φ-¶˜ ûáèÇáwÚØáGïõíd\0^:íÊÙêW×®‰¯¾´.£/ê‘ïÄó„’¿¶ºµ«Ï;OžÏ®?04¹B À¿±âq¾>xF~ŒÙµ«^þvØ6ævýÒ[@àu†‹µi0ò>ON(ÏEâù B¾‘o&¾½ǽÙpþõ-÷AŒ¦Ìë¡?ÛaËㇾ-L€¼ËÞì<¶ äéì¼q·ž‡{¤‘Á?ø€oâ×겡撌¼ÎéáyÊâ\SŽûâÞ’„\- Yk"öÊvpÇûN€|ÝKõKïÕô›ú¾Qr™=?кzoá9èBK}à9Át< Ž×/NíSiO ¼XäÐ<ÀÏy±éðÏ)=àÌž]ƒtk }Rûz_øbñðiùuøe<:ž§$æžêqüGíKß¹ Mß(†®ªf‡!wâ‹mn›Û?ôoÞ½H+˜:ÒôIz¿n÷tdäö-3°cŽi½²'vÈQ>èr´ Žßta‡…ŒïSigÎ ¿dÎÙÌà ò<ìGôte¼4<Ž:âæä–Ùáá6‰1fN<Ä <0&(Îî^×JºÁB^âDoø?yN(ï[‚õ¿»tÔ«öŠx˜Jã{ÖwɹtBöYHÅíï^£&{ú0¿>î:Çq6D-Ñ­é¾ J7±UAß7_ûõ‘O•xˆˆ-‘´PÛ¾ tl2ÇYÈü¶m·tÝŸ1Žçþ.?ÅŠã­írôôña]wO®ô½aE`®ÖâYn^;¨ºj¦!ø”…Äø•ÿüª÷O>2ÿ\Ç1—‡#ÃóùÜuŽã <Ú0¯{‡_õø½ãõUûìK? ùK´ÜÖ² Ø4g"nœ¶êŸ “ ‚ùˆœ;Êykbn•×àT…Cµf,Ô]Ú_ÝŽ?:èñix×Hcx7¢-<¼óÔ ?·0ÞÔluxde5ß•±?uã)ºß²ìÞo‚Çê ?Œ÷ñ~öà3OC»ZqU×v€&QµóÏ8Yëmï›Ù; ~ñÜ'9‡`¨›\.äû)ð8ï¾üqyiÑmÐ{ÓÚ¹cëáçÞ{åL3ËiÈ—«YÑAí^UÔßþ±åI:³{²‡§wÔ°£ÆúÃo³Õ™ÀS¸‹¿AdGc½…ùu¦|‰Â>ukÜꥧ!¨vÈ”­åzÁÃI«Íu÷X<<ˆ¬<cúÚ]zïI½¿Mß±ª(w9åv¸+³¾‹êwªíj[é–­¸§¿ZHá>Wî•“‘gÌsx¬p_æ &·ö ݪÇ6A?ÿª°ÃùY—NL©p¼_ôømn/ èI;#¶ZHÏÒõ\2É3n·‡>cƒëfðºœ[Øçy­L¼$+ŽÏ5ÄOjß+rS²ª§–¸èwëÈ®.èïÓ\GD[Ès­ïß½0Ás¿áç!+™çq„»¾ñ8‡N¿;±Ði„ÑÙÙËÚ†Hû¬<#ž Òôö ‚‘ógítÄBJøì?–a­…L‹þ=Þ¿UÆuà¹lüóóû›8çYã‡.èw-îÑZ– n‡Ÿýè7±‚jû`¯…KzC×|oËßd!§tÑ ÏòHõ =¾æ¤`l;ÈO­ÎSdœnœÛjTu[l!}\I?;É“›Æó>9ï)÷ Ç­å> aø«Šf̶Ãí_6ìiÁ7 b~õwŸ×XÈåf¾< Èè÷de¢+DeäT óa\'Ž+ð¯æƒ{Øyv˜7ì—\;¶‚-p©<½\WÐ_¯ÖõÕT ù8iò¹ Í“²p÷ëùu¾z‡:§çþÌçÿòù5Ï01îY`ªâîô.?åë8N.˜üèî*;LX\g>V8Zj¬õÙ¢î0sCÀ†eC,¤j%J˜-÷PàK¼öã÷c1çAŠãÏvƒöf‚W«åµvØáÊŠ£c~ôÂà™=!}t¹²/ºXÈ™>Í—L¼>>Ký?óÔ¿8_U㊫€a¨JÖX» ÆÓÍ÷÷ä^B`Èó“2­7Ôsƒâ,d@™šg?mÖ’QçâùƒÇkü|ÙïK;M+ìê¸ö„Š7É9ÀP‡ÀÅêöKòý} ç£]›¤òåÇUšK²òØøõ¥gÆå«…ãﯓóJÀþÑž\^^'’;‡õõÏè“îºÆq÷©ìó|8äŸU&(î©|GÕû¶;ÿIxÿvw¥âg{ƒ·8`!yŠÎ.6ùéØ,üÄOyÔN_àáBכǵùhi™Õa‡œÁ'½À|»qEs} YÙcR• ßužÏÍ뎇_OΑÈÄõ JUÄö«^÷\-4Y¯ù#_"ìÞ™äW`È ¸40­ÐºçÿÈ/Uª¶´íþÙ[ó˜+äÕ§¹Ï‹Ç›ôËAùck²²÷¡°e¡ÒÎpGËò'Àr|ÉÍAÝ@òç"5å"ä«Nðøeá±¾—çë>¸ÇQà8B>ä`BŸŽC«'‚%Ç/G¦=Šƒà­§üá›z©e ™Qï¤veÉ DàíxÁ¶IcßÕ¯â~¡Áñ´ºBøÒ¡$_J·³é²D8ó`ü‡‰1q0åբߖ5í£_ÎŒÌW0û÷äç“ç÷¶Y¼¾ÅÖõõX~©««ÇñkWD}m %åÌ#;´ošÛ^mˆüivü81wÕ­”P"%lǾ1¤Ë‡«×ôþÇ ß„<ÌÒ@¯ô†q}çÅ·2Ê“/›5¼ËHš8êÏrÌþƒÇ¿ß}H«KU'“8ó»Û££¡íÓ”K ’Â…6ýï'zGû=Ñ¿¬ÉÆÉí_rL›§ú·~îéHÈm ÉSð›0®Ç÷±×QÕ§©Ä/›U7ÊÞwÕ…Ö˾Ôê%McðC }žçõœ1žç¾NW¡`ç¢ýŸ~òôI!O¾n¦u§BºO”¥Xï54UÑexõy;/ uR×Ü ,š_ÖÕð¿Ý/F.^—h{Ö /›×éÜ–²à@ÿ‹Cåc³qôø¼›s „óUøsš»þñ8'ë¨#ó‡,&ç/Ô›W¿R ޳èÀ’³t+ÈÓós7hž!Žm¶Ÿ:¸w*¸1‘’:¢‰°¾<Æs¾x¿å\7^¯<—Z¼î¦ÇãôÞXÃLªH©I­\¦Nø=ºÍò?5ãßœ}µgTÔwƒ¢É å¢E²Y#=¾ã\Sþ=ÚÑÛRi&¾‚‰ŽÿfßqE‡U¤Â°z)]û'AúËÛkÔ=1ç~3(|{Âøžcw¬M¦)üG[»Öã;~]ZDªÖ\}ÆÖA+²y„nÅñë–¿7QÓt5©PääÇŸµx½·wôuÜ> æ–;ÒŸ©{‚0?fÜ%­çùÐ÷bëïü<ñçq]9ñ8£+Úüó®!M.|¾œ0) d›š5J\vŽŽ.Uün…žp¢Ù9ý‹÷ÑÄÇ}Þí9NXɸîÂõh¼¸}Œ÷.AÞê¦kIÈ¡;ó.L‚]oš~j~êÌ.õì—pùÊŽ©5Ñd÷ÆW“ËÆëúýçÙ±ª­¯<Ïíbžǯr¤ÚÝõ¶u$¤ÀÊoµ×'A÷Ã/¥e^‚˜Úµ‹Þ €´ûqò×ÉÑäÀ¾"o£Â‚³ñ4¸?øùâëŠb®¢c®ïšo÷ß@VW‘ôQlO‚¹M§½·ûÏçõœ»ìöɰTÅ»°Î8ƒ0‘ÉÍï*x .Õ‘(~^y.]iòÈŠv`{Q{úžÑäד[cúÖ•mý‰s8šÏ#Ý>Áñgö½qôމ<ïÓ¬ÂÖª×`è—Ûm¦ö;Û­ë÷¯Ù .ªÿÛïfÒÆ–Ïx;ÊSWÂû”çžëÀyµ[ ¬XÝç™°>§Àñ—ä>¹£ÌvrkõiÙ× lâ­Ö½‹d\µžp©j¯”.if’ÿzPšìå(ÏõæçI8?©žû­Û8nGãˆ/}zï K¤Wâóv¾«&øŸS9Æ>Ckø¨†^‹÷YûÖLò/;È{UF?øo<¼F1Qãp?hî ¥UãÚ]îs Žöwµój;¦EÃÎîÃvߟ‡gëK¼~8çGXŸa<>¿ÞŽ—Æcµv’Øæ£Ž Ô^ƒ9~«ªw¸¹^öŒ)ŒX/(üíØjú°ù±5,ôd;˜\®˜E“:I Í­k2Æ[›VcWSÖH¨<Î…€çßÚ5ßMŠÜQÞª4â:œ;^yÈ¡O1ðÔùh ê ß>:u*Mâƒé ›ÆS7ü>Ãý•™Û8‡Çí-gy¥Qwí&ë-MÎv ¹ ïôNø¬Šîg\Å boˆ[5íñOy¢IÌ´^ÃYyú_ÏáüÎ9sr¤8þ’–Wÿº›ª?Û¯žô¼]gFÁ±ú-ÿØZ¦ÕW4“zÅ˼möPCøs3_WúNk¸M~Býã¸/,‚„Êf•þøô:ÈóD·X}g/„UôËÿkƒha}¡ø=3úá`Ïçå\f¾¾½~õ“*kO* ô§¸‡í› uã»_ã`|ðë0)gd•>½÷ÂË ýG‡î Û;^.¯ÍMßöÌwz?Y6aºïO ÿ}Ðoÿ@¡dýÇk¨QandQÞò[ë’ÉPiêèÁ›]‘𓽒^Q Ä;Zæ‰&o6Èã_~=N 9Ùkù;O¿ä|7áy§°Pï#R;ÚU<u/‚¤ªw½ë\/Ú<{î»<"Ú“JCÏ´…}Ôy£ÉØ?Äå.2Ôã+Þ„ûÕ+aÇ[ðãåèµ"É÷’œ.® ß³2ªµïToFåp¸!ß¼Âkloð^»³ó†Ùs_æ}‰ïsÛá7vh<üàá/ñuñú†s)ñÑÇ&ß"Èh÷ƒ`2ü\ýIØÓÀ=0¢o@‡ô¦}aï†û#<2“²t»ÅÝažuDû… ›}ÿÂxìgü~¦,GÓkdªÂ¶h{rñšœ3÷½óÉpwë¯ñëwCÏÙW^Ýþ½ë“f" ô±<®9̳?Gxøâ÷¤áÊývŸõïS’â¸Ú«Á#‹ÄE†_Z´›ö4 ‡}lðáÂ.èSgÛæ½úAD\¹fÒjj‡-Å»ñÜøççœ91WKãVkRáæÂ%äÃõÔ½{sÜלFŸ»`Š=eÅàºj¸Ól5$›Ééøƒã^Å "œ»žÚxÑ¡V;>û ë62àϱîzÆq÷%ä¼°{@Ùþñ¬·WÙ°°uÕ¡7üÃØ>Åžžz¸Íj’uŸ‡Çì~~=ç'¬» ÏIzËèœSM]#È0Y»*ùêÜ€ÎÕ+‡,ß³¼6}|Õv¤ f×ï¿¿&Ö5}{]FÙÇS/üyãtTž’îäfŸ'!Ó{JŽŸl¹_ôyϲ`Ðì"ۚ߀ÈïA£o6Û =v¤ü°[±­XÐã/žû±0Ÿi‘i]ØŠÇñî?½Qòš2´înêvV4øîXêø£:}âl *7€ÒLæ6Wíiq,0O‘sñ<ïC‡ìÕa(À„q³âÛÌcó<Ž´àžK—Ž$¿—7}™0ìL3~ÇÈ¿ëɇ9›±Ø¯Bp‚™T{Ъü©åC=Ïe¼> :ݧæ /ìþ™æG^£R!ÓwÕn]v/éD·ür&¥uÖ®Vï€ORÏUûä•"¬>a&ừ<×M ζ„sdù}IXohÊ8iŒÃǹpÆG}îõ^"wlöY½æ(ÞÞ;¡,¸v¿™2ÿÚÜö0éJ·ñeCÌDXš¹øxò•¥c3οÿ ý˜ñ¸ñ8”b©ÞE_VUju”ºÊÁKöC»RÇWÌ}Ý^¿½?ïf]3éï­H­ÙžGxßä×ÊâTósàûÖÜ>ÁãPJæÆBfÒkþæe5ãoÀ½äÀæW*o‡’o$cÃFw‡ ã{ä¼ü,ŠTÌQo郲ì×ø6ä-½¢ÕËÐ%…ìO k…Þ©Rvögêq|ÿvŠ••f’Ö8×¹à;7À¼wàØ¥ËM÷§Ç©…ö€E3 9E¤­Ïª9~ §ßñ¾ZMþôr‹Œ}œ³&<Ÿ6ü‚ÇÙ; üë¨if2bé¨_ª}¹ï{$MkmßÊ6­võ·ö‚‹è‹©(RõÊ’ðÅ~ó>ïɺ>Âß ó}¬n¿àqŽÐÛï39@Ûjù› ¼Ü Kݳ·yo¶ïÏLÊ]<žúÃ[§¿òòõ~?ããáÙÁóüÌë9¿œ'ËyÓb®µÇÏÓiòõF‘^Ýã.ÜÞí€Ü »šÛiT=VÎt—xŸu÷cÞ/ùùÞƒ=òŸ=Žë~=\4Š|]ñ`ß¶3ÈñphbÍß«¤~û޶`O}\ûç½òóõ¿ä öÜç3ï“|â©GažQ:Ó{Ç^àaÎæì%á?ï|º÷±êÌKH´Ìƒ‡Ö÷~‹ ÷放J5“_77yÔ¨Mp–~ÿÒïÚÅ{ÿo/=þäû–Ä}ÆŠÇ9¿ºéú©É{‰»kz݂ø$9Þªèu·??|¤ÿêœÑ¤ÔùòÛ‡)3ž øs\Vžµð¼ÎöÓãø7ž¬Õçï%¾ñ[p?ôUß›ÚiðÄbJ«éï>¯I¿ƒÏŸ¿Õ˜Û48ËûÛç~ “.<°ÕËóÜ!Ìg}3û`lª¢`ÓÃ[+âühfËÁ4¹Óã÷ÚòÓ$Øs¾þ• ö€£^:nøÃL„º Î6Ÿž_sç >̼?DŠÇ9WË»e5‘D’«Ùš&½nAžƒ¥:kÒÒÕ¯æ‰TÁJUð%ÍD¸_˲ù‹g??ÿ½„°. Ü8~þ‹ïŠ$Írºe›| òç£O–Ãáî«'~Mó©àI­9k‡ÌdIN×Ço›‡gã¾g]ßpûÇŒ¿ôÓÑRâÃÁøÁknA`ëjW÷¦ „yó‚ìÑÏT°nNÎ~Çæâ|»ã³ø¹FxÆõR®¿{7—ÇÏâ}zz:nɆ›ó"HT‹9–Q· Ù‚ÝMænm3õ€g61Öékö¼ßâó¡Ž~ôü>…ï/¿§3áøãr$®iA:7¼û<ìä-¸g®Øw˜?QJéÎëÞЧâ“êÉ~f²g߉ïîi=~ãó~Ÿâë/üwîúÇñ›Î.š¯°.œ$ý<¢t±Ë· Hë8²T¢!ã®mû¦,Ü è› 31ÜÙ4©¥áÙöó ë?ß=óPwÝã¸K{˜ôÕî!Ý ·`^½×/6¼MNí;ê8Þ ·ÎÃyéÙ/ã¿_Ý4.Ûº$ÿ_!ž_xéRÝræé°­÷vr@q¼õ’R [Ý_Jë¹~uó’žJ(Ó9>­p}3‘æO¬¨«¢Ëv]øû(·p¼òeæ„ $&"<‡§À».›ò´}k sRίß[¸;N)§},œ×Hæ ðéx§/|Ým ðo‹Ï7t™Y“±Ï¿Çã}ƒï sû`|ªâD}q´™ûVS`Ù®SlÜHbJ¯{ÒØÚZ»Lðþ|*¡BÛ1žçùIsW½yUùgV¦ýù8î¿Ìò©ØbyH·©'¦ÀâI_š_n"=î9¾Mê+Ï4‰ÐF™Iê…áÓÊß“m=Ê׿Šëz³~¼Oð÷íâugŒ{ÁÉH†–ÿ4oγˆª‘#ìÅfòà c£ía Öî¤nòã|ýt¬§ñ÷0|ÝQü\¦ÁqÛÑŸá%¯#yU=.Þøœ³ŠÆôùvx é¿vÖpió6prqŦcÍdQèæç½2~ÉÏ7¿ž£j.=]wI]àó_·püçºIë^E¯!ü«•jšÿYZ¡T âWR°îp¯£…¶|Ù{næ^³gŸ:¿/óóÏß›òósr¡3vÙæšÀß‹»}€ÇÉ_3rÖ®·«ÈÁ‰›rß.w , |[âà¯dÿõé$qEsp ÷^möìßæç§@ò<ù™ï=ó¾Î.샞C¬8¾ßšooi ¤ó¸ÙuK7¸mÏ¿÷ª^u+~¿ÐF'æ8hf&—+ëï-cxVð÷Øâõ'ŽßÉý¢n‘ä¼zbQ§;нá÷Ö½öl%Ö}ïlG—Ð"àsøÌK?æj6ѳ?œ¯ÿeýÜQ^Ÿ¿3¦þΘú=cŠæ¡íõþÐsbGù²¼Ë\1°¬bã’ÑbV³¼)1;BÎò¦\,ç\Ìãs®m,ç<”圫D9ç”ÇhEù²L'ã”…²Ì)…ˆ'«f¬kžÅgeñYXÖ¨šez‹²FyÞ9e—q†„/Ëå£Y-œÉÈYלÉcaLž,Lκ¶ˆ˜<Æä¡Yç”ÉC³Î)“Ñ;KæÏ¥Lʺ–°Ì;ËéÓ±œ>c˜9›–çN©XÞ9çòØEY£âÜ)žÕGM®eLF_ÆäIc9£FÆdäL‰ˆÉ(±®UŒÉ#e9£<ëÜÀrF•,g”gÛDLž3jË’u®`™SÞŒag™S–9E3S8ç–rÎþî±÷X½×V•°ïì@ɰp ¨4”’åZ¥³<øX–éGsœ)£[œ Où–é§ÌÂ’3oí,ÇÙÀrœÕ¢gÊ<³¡dhË9•3dËõã,H ãÞòü«pQV,3ކeûùˆ²ýxž³Ë>µ±ìSš‡•V2ƒ{ƹ·œ'Ëxú,< νñ$œŒ'A³œ)O‚f9SîÍrs¾y®Ÿšqo½Ñ¨:ƽõeyõN–…j`Ù~”Éùßj–çÌ™Q¶g‚;DÙX4¹ŽqÏdŒ'A³œÕ,Ë9MÄ“ðqÏ|EÜ[5ãIø²\?žåld¹~*QŽUãžqžÏõ³gÉrV2ޏËIu0ޏ‘qÄinU¬ˆÑC=DÿЛËë3$þ«ìfqßÄËãî™â~ùÏzåŸõÉ¥GÒþøïöÆÖ³öDÞ³ö?qï£=ï¯èwâ>÷¯ô¸ÔßþÞ&ÎeæýÌ›ýïh.©/g5ŠøŒ4sÞŠ’b a, žÅLYbFQ&ŸT”ÁœŽÒ°¼QÊb´¢¤ŒÅí@)X&Ï—wˆòåeXˆ–ÁLy4›Ï‡eóq7e0ZY¾(g…©+ìÏ<ƒ™r0B'Œf0s&ÍãsTò—)#L‰n¡ó<ì;öJB†(åѪc‘rk(_Û _–%g™fîéPv”`bLEž/gZ ãѬe_Æý¢Ë”™Á™_bm(* ¥¦Ë”¥&²¡|³dîQCqŽÍÜ31s©Xæžš,„ò4;‘2i4h8Í„g¼Äð¿çgÏϼþóæg>ì;9éµÅÂ5¢ÒQ*,`+b ãõx³Ìe›(wž6å'R>w:Ë\s6ÄLZË\6²Ìe(s™²¾ì(9šÁÈrIŒ¡HYÝ*CQ˸´Ô(–Gï†Ñ±Ø—B(÷ %G£„fÉUöFÓ„ (š'œñy.¼‚1^i¦r(ËSöeyÊiŒëJ×Õ€fK§}‰f)£¼Ñxv” ÍŠJC©D Jf GIš ™ÊV”ûåbÌA šT‹’¢QCQ.Æü{~÷÷üNïõŸ5¿óeŸÙE¯®‰¯Ž’°\yÊòÁbÖåøØ<_^’G`3šX«³05Ä,WÊÔcá›XñkQV”/ã¨9P ÊmD¥³Üy3†ú§ –£Žñ\%h-e:¢|Ð0!,ƒÞ›q²íŒ­a`œl%)œ±‡BX½MebÆâ,5Îså\ ãj²p58ÏÕ&âj¤1®Fl«¡• ,5ߊ™Ù”7ãjPž«ªghr=c©)WCÂxDf|ÎÕŠXjrÏU˸œ‘†R2F¶„eCùb£e,5ÎÕðñ'eØ<Œ¨ô?É‘w1Fv8k*œKIwþU¬¢¿º¿þÝ[ÿî­Ö[éõ¶ ¼±Xµ(;]±Š8ŸÒ…R`Y!s>%åcÛQ¾XÔ¡"~‡‰qÛy® ]‹²¡dXðTcyP&®‹_'âcÛQ2ÆÇv¡TØK(JŽæ02V‘MŽr¡h*r‹Ð4V”/G'bd‹ù”v”Ì'ƒç¦ù'|_ßC.â{hÓò=ô(J&øi(5ÑŠ’¢õ('JÎØn.”Íi©"ðwµ">%eQF¶M«G9Q 4¯•†R¡‰-(4²åD)ÑÐƧ A9QJßòyí(Ý€r0Þ‡‘ñ>”Œ÷á-b÷ʰ Y#Тl(l:ÊïEɱ1PéôÙ™1ß8ûÉRb³° ¼±ahP6”/6ŽPTZó 6¥76Êþ'|ËßóÖ¿{«×^o•³Ï”F¯ n8JBù—( Ê‹XÇxHR,æÆÃT`Q‡£¼±°Õy¦¸äG+nAù`¡‡H2ó](~8J‚ůCÙP2Æ‚s¢”h†pf•ˆ©12CØ¢CYQRÊÇDÙQ>ŒKî@ÉÐ@Ff"ÊÂ8Jz”¥DS…£$"gKÐd:ƃ“35œe1€)NŽ42œhEyKñï¥NVQ`šs~f,Ê ª«,0€}Ѩ¡U° k@¥£TŒ§ÄšVÆÐÔ¡l(_4s(*­FvöyJ…EIÑ䡌GÀá(oÆTŠe<8ÊÎÀœ§1€u(‡ˆ“žŽR1Nº76-ÊŽ’a£00œ ,%±5åØ%îOâÞ”µ'‰ûQÖ^$îC¼ñþóz¸ïðžóg½æ×,½%ëš='±ô³â…qp~d.Ι‘”îÄ ¦`¬\ Ê{eGɱ¤ç8¹Þx1íŒ.Ç šF{úØF÷Ú‰XÞb®#ç Qæ­%gÝFß)°EgÊ9ó¥ïSQr,€t”}iCùb1¤a1¨Ë–3õ(_ô](ÊE÷ÆÑ}X(±()úÍ…RaÑÄÒçEôš ¥bü2úÌ’£Çœ(9»Áª³Œsjåè+J‚ž²£¤è©”ýdl"ðÊ|°(õ(JE÷ºÑõ},ÐôæK1‹T†EjDÑDé¿ç#ÏGô^ÿYó%;f:=÷X¸”7¯‹òÁ"A9r ln=ʉRbQ[P>XØš<™9ݱ()º> ›6 ¥Ä· ¼«ÛŽ’£ (J…f° $hµˆß¨1õŒOëƒF AÙP¾”áˆr ¤Ev·%G™ÓQŠEIÑL¡(J…¦² ¼ÑX!(§ˆOë& A9P 4› %AÃiQ6ŸÖR MÌ„Ú ÇÛGв£äž7g;Rž· Âø´24ªñi•hX#3­eñmŒïH™Þv” Íl@¥×ÈÎôNG©ÑàV”/šÜ€Jg|Z ãz‡ ¬(o4~ÊÁø´TJ)âÓ† œ"¾7m êÆœoÊ’c£0¢ÒPjlV”¯ˆù¨ÀæÎXßZÆ|”c#11#g>z3æ£%ÇæbÁ'ôÏÿ¤ÇòÞÊûª¸§þ;ý”÷RÞCyÿü³¾ùWöÌÖ/ÿ^É{$ï´'þ{ÜxÿûG½÷¼¥ßýOz=WV”O7¡dXDz”¥È)0¸ÓPJ,ªð\ƒ;å¤s*,0˜ ¥±hcé{,6JgdE§fLZ,¾”ƒ1·CPºG‹1¾7Å^¦À–†Raq*±8ÃÙÍ_‹²Ó½¸"¶¶åƒ=+å@)°€ÃEù·¥@Qi´¢bQÞh *僦Сì(9š#eGIÑ$z”%C³PN”oQÑ­@ã„£¼éï½PV”/šÈ€JC©ÑL±(4”åB)ÑX”šKr¢”h²p”7M‡²£|Ñpz”¥Dã…£$h>]Ñ-•âߣ(EEÕí†ÔUXݾhL=Ê’£A̤*” %¡kJ¨X”M«CÙQ¾¾¯Û’£‰ÌÈYyÝÔÔ” %Cs™ÁÕ¨XÆìÖ£l(4¼åDÉÑøFT:}Æ‹’bÐgávK°!hgð»CPN”{ª •N{,6 J† ÈJG)±qXD oJM$œ5uK±MÞ!('JÅ‚X©þ¯ÌçþY/å}ô¿šÇý£gàÿ›½ò¯˜ÏýÕ}ñ_߉{àÿöž½ÆXPz”%Ç E¹PJ,°pT:J……fAùе2”‹Îó°èäXti(^:JÅgEù`êQN”’î…CIèïPR,H=¥“îïe7}J‰½,¥ÆBMC©°X-( ö1ÊA÷òbñ¦ÑgT,àX”{˜åD)± -(o,ꔥÄâGycëP” ݈JC)±àÃQÞXô:”%Çâ7¢”hJ‚&С(9ö/]C)Ñ&f ß¡ìØ¿|±oÉéü•†R¢iLlâ¡AÙP24‘•ŽÒ ™l(_ìWT:í_t/JŠ&Ó£\(%šM‰}*¥AÓYé>4ž ¥ÄþdAù  CP¾hÄP”‹îß@CJÐZ”勯4 ÒQ4¨•îÍžä@ÉѬ&” «CÙPR4n(ʉR ÃQ4±eá·ªôwTtha^ÈÿÞÂÿOÉ~ÇâN þ„ñŸªhYÀÜõŠj™‡èƳ ¿<7!n°f߃6ÛHj—/[ÆÛsÁÉâ‹óŽLoçÉÏÛr·_µø×Q„s$6D<ÓØo"çFKñ8”–2ëËr"B¹`ß‚;P»ÐðY4‘÷Ué£Ëu†fÿ‡½ÿkªÛö¾aбaÇJ,(vĆT;**öر£¢ EbÇŽ;vA±1#‚†¦¡ˆÁŠØ°cÃo¬¬9WÜ÷Þûœçy¿ïÚïw{]ÿã>gÇJÖc¬Ùòÿ½ò~ºð~0q»¿4%~9a¼Ò¼žÁŸˆù÷2ß2î:b¼Ž…ñç-}‡l%cWý°z&Nži|ÃxC‰=?ôì«ß`1Xÿ”äN0éÔ'½Z&øP¾5—&žã­:;çLîØ-äe~…si×r êÐ,kŸ$‹]êu0 %ïšvoLÞM{·÷´Þ²2 ï‚Ÿ!ã~óþÔ7ãó~î›IíESþÈîç€õ®ÕiOCN’“x?ÀŒ4`ì`òy4Õá´K9?CæKÇ|-X|]_^§ Þ¨~©êMdÃÐ)w£žäÀ“ÃÙ>E U¯V÷¬ï5§]È—ŽÊÕè‹‹à›Á8IÌ—ƒù­èúZ)0~Òa'uûk©ŒÜ˜öXõþ‰éžb% zËc6¤“æ "ü¤ËÞö³ðù™ï‡¾ïuz yÿQ5Ư¶Ç&äl v?*ï®ö:¯¨÷Âóiòè£yhÛ×c`ý­înŸI0qìn›î­§å2.miÿ,«R<½…âEaÙóöZG$¯¼ÜˆCrõ¾!»ëœ!<hxŽzqþY0Éíµïd»sZNsB¨Gã¯U©M·R~¼"Œ{öt“¤Ö )9Ø5{%Ùõì£YêÒÇðæJãWa©gÉ8›kw} Þ(˜üúc=#Æy•ÐxNÑOÁÿ†ùWéúß(0¾Á‘Eד›£Æ\ûîm«3ý­Å9òâMñÖøUCÀ£×›èYîÁÄæÕÀuæ®"úQ§‡¬X©/<_ÛÃaνûô¢|ÞÏ[q~Ë5O˜G9 ÑcXв`õÚÓçH`­mö÷—9¬Û¨¾Û«-w#e}©Y¿äýeÊø©ºŠïå€0#IÒ€s^FÃøóW»9œ'‡ŠKfœ³Ÿ]ÞX¶þ}3˜èmûõ£y÷r¾}Ìß‘ù¹±ü/ÅÉÁ뼨°xyñ™±ÐêòõÊÑ¡ÿåͦÆ] Ïi›†é`0ùñÑàÍé±nÂýç}Oµ~­Œ{®ë¯&ÆøÞ†{Ûe,€§v:<~ ‡v¾·î}¬Ý6=&ü©"»_|a}0á\ÜG Z)ô7æÅx]Ì«”o0ÆOU_¾\tÃRöÜMûh˜ /¾Z:vÄE2fLjüoæ3à£dÛc› `²êÉò)+þã¦érƒ¥ïª#眶>º½ìÒ<>[m}~`|1s¿côñ÷4°rëØÜìs0ÙS!ߨ"}…àóÈÞÓ¼OåçR¾—·âÏÐis½`ÏÞ-“+vÉ….G&{ÍéLÖÏ©ÕùsG'h}~Õ¦ CÈ{®ú®åü4Ÿ3<<+]OÈKæ{V*ßñ:³c×>N\áƒú <Ý'œB½§¬zLïŽÜ4Æs4Ì.È´ !Þ•ÒŒþ´²œÏi­‡ž}Òö/WWº~^j¼NÝÖÛçÎù)…N ‹ü† Ê…%Ñ—[ÚN !óó&¬ž:6†}‰ìBË¿ôõr+Çácüp]¿J½•…â°ïQ=·¯‡Yk–8'ŒÌ…û5¦~1W„*Ò>‡»M 2Ÿ‡óîý&;''ݯ>bµ5£·96¿ºàûÊûÓqƵÿi& ÉÚó5F¹Pí®¿äqSÙo5½î§™ƒ!¯~‡oå´÷%î¡Âýg÷E? ó̃¿ ýŒç4.ÅCãu¾úÔøô¼¢/ˆZÏÎ…JૌÜy••ÜÿÃè8ùõ¶`Ê—õú1ó+g<]Þ”ãr¦˜–k·Õ³LM.¹ ®2Ó.üŒT? ¢Z›QðäÕþðjË‚I•'}*\=Êùáó÷[Ë bõÄûœSN ^§1g“ÜÈLWO¬SÕ+™¬ò`U(¹9óW¡äÒØ»ü[/›IÁ¤@±|Cµg«…Ï?0$öà³l-‡Š‡´*õ> Àøõ‡íö2÷ÛÖ-<Ý’ TI{ËÉ!㻆¼v„„@«í×mñ}õù‹WòòÕB?xø;:+xÜWÊ刴aœlæë®ÉŒ¯Áµ?Þâï×ÝÒýsaÝ„Äc‡$râX”ðl«íxèÝr¨çúÎÁDc—Ö½~ï꟦¦%6º>žjŒ«ð¼£J—ƒp„³¥?› ¦o=;.'?N}Rulç<ï>˜h윽µùÎüþ¬ýžî¹b øÜëú¸ë­Âüµ{@lò!¨ØõœdÇ¥\8ÕeHãßÍÂÈø¡÷?מ5Ž÷—¥_a¿Xø®Àçê*áù H:^c-ö|uß·"¼Îû‹•ñ•rnv—[— [îîêzcùòòì·l"`‘ו7 &Äžs¦v+7.a|e]s1Æ­sÅxfÀÂÑ·æ»{Y¹àóisÕç¯ÂˆrܪªK"àÇÁ3VqM‚Éšf§û~Zî^·q­XßäÇû-Jù-Jð:A+¹F~’<ºì«ó6šíõté8$œHf¸¹Ì½8†^1_~«a0©qý½i³Ÿ«Ëù¸3î”®­ãžæ°¢×½³P7ÌwÆæ.y ¯÷h…Mþ%"ÛÑ|Ò¦±`îWûúå+AÄáÉŠ6™>ÞeøsÏl&¾0r]Ûúm©¼aܽçƒÏ½Þrö¶ŒèÝ+&i&6¤]1×ûã“\¸é{9ˆìLjécYä%ô“úÜåw¿¥þ³´c<Þ¿þ<ë°£\¹]¯Rã¤Yʧ©zª °7ïÔÞ³ADƒ+xî%Ôá…‰ã*;î3óƒå¯GÇ+÷Øê™íQz݆æAZÛn‘µ>GÐyæXžÓ¶i🠢þèÔÕJé-|^ÎeúÀåO6“«^Rݛߺ‡GŠqÛéw©Òàc pô›y°¬ŸñÃÓ.‘ÄÇü¤M Ø^ËŒcLª¾é²;uÑš¿3ŸÖ»×¢vT*âýZ0þìýª,ºw6ßúUÏjB85ìÝÜ_‘$¦Oè¼è^3!¬[ÑéF_ƒ÷?=gj?7ë{œ;vÓ]mKqJ“bqÌÍ‹ ›}whZˆFÌSï¾LZÞèg¤ÿ}&¼éÄMЃIýMÊW¯¿{þ©,¿Ùºë{üu†¿~Ñ—Ïo¼Nó …M‚@1™ æÁ«6Ü É²hÚÏæ»g@ÁÙ™³¦ &'–s¤–ÕB±ïQ¼pÂŽ„?„÷ß?¬JûœºŠ+Õº0¿AZ07äŒÕóàˆÏE…Ï…+ÄšÌ8ek< xn^0¹—Ûpñ¸ÚÚ÷gYnû>Œ#¦;ïáulZÖjtëNøkë: ¿ÏÈIS?ªEò‡¯®ršN/B®ÕxLÄŠI‘ û{Ð>øXO3?~ÆWfÜM`|s{|\;ŒîÜ:‹ñ“æßßÏ"=, óÖú=ÉÅ™í~o¸ä)ðJÊò.w=M`|Ò~ÝŸÙ¡p|57˵׮V§£ÈÏžéûªÌõ–Ÿ=^ñd0Y°#¶{—»OŒgÏîÌ©?,Ÿ·R¼Ž"É­¨Ne9T}}çzK¼ŽAáò÷›EߢtåØŸC âé=½õG“Üç-¢ë\ÒÖÅÁgÏÕ!ÅÂx†ç µ*5 àâ‡îne4AÎãF1þ±¥?æï©q•ø ®·àc¯á0.ï»]ìói]oÆ:óî{±çཧϢ]K?ÆO¯YØbc¸J"F«ùdñí@²Û°«Dc+2 ZßþàU)û¿†åCF·Oª1Nrè ½[MÂàs®©?ç´½^Ýy•ô±‰w¦éØ^¯»ÿ¦ü ʯ_SŽ—ÇîëoºÜF½Õ…â‹Ç1mÂ`ü—}FÌ̓þޭǟ̹J†×?:·õMGˆùyqJ௠’RPc渲nŠLŸþzLËÏAdiÔ®œÃ®žåÆ×¼¯¯>ŸßÏgËl.†"ŽñÎ_Ú{kôÇkäç€FÍ= nb­î)/ƒH@É¥w§{”Â9 ý A÷9J1nø†È.«b.AaÄ’õYKó@ÕgúïW×IÐù€7nË'79–¼" ~¼‘¾Þªí/ìsòó:C`yÍߟ6¥Öð:sÏ_T‰#à[áÓ“ï—äå¯>=¦^¼NŽ÷Mid±˜k&2AdÏ$‹CG³V yÍú×Ý;Ëõ›U® ¼§Ø{K“×C÷Á"€åÁ§TÃ_~\'1QŠ­ÅŸÆ€çþ_DJf^ž=nƒv|žÏâÜ×—ÏQ“߯sq·6­"sý6Ãü»»ueÅýo6û\×OXëϺîÿð>ˆlÛ^Ã>&ZÛOWŸ§´.Õ?ô< Å=}3¶ö¸ šáÉä<¸Ÿy.]zƒT]*Zâ>ŽèwRxDv5HÍo¾Ý‡\ßݬý±ˆ/B_bñtß§"Œ[ñ¬óói’Ë9§xTOî}mvµû«Ë7HIͪ&’7öð¡&Œ "‡mêÖNZ®ÿ1Þ${_³þ¡ÉgŒŸ½Í¹Ï¹w—aOaba›Sçz/uqƒòìàÕÛûvîàóÓ¬GHËëU™RÞé yÂ8Ñ¥øx~~xæ%¶ÕÇqÝåñ ‚/™Ü$üº46i.;z3ˆðËÒR¡¿2æ/ ²·Ýþ\,Ü/6.Õõ—–âuøqóu:_!¶Uø¨7ííw“dvíá©»Sô‡dâøqÔ¾ ³îhû,û>ÌG\“ßï@hbщ%Q0áía¿ ÓˆL«=Ï.} ‰‹ûú§~`ï/Ý}5—'æ,ô¾ ß*lyûN?ô½ËïÙ.šŒ¬¸¶÷VÓÉð2¯ÓÏ•oà–}ð)Ç™fþÔì9ÖzÚ=´0¸50Þ—&Ï=qü¯YŸ¿ü¸+®¬_áv2š|“à~v•&9s"ˆØÝXYò6ÜG‡±yãÔ±ñ‘&Ï1îÔ¸'=7ƒÃùšõ§ ^bKBÜBVÄô8Üý¿&ÿÆ<ŸýºaL—Ä5„ñJó‚õ)òü_úý‹ñ:·$~0xxþG.„E´sW²ïÓó†¹3àB̉üäFÁD²·{aóFÞ‚7[·“OIpl2ó¯4ž§R“ÏsŒ_sWrØð¨¿ÕøA.$ß–íéÖJA~š÷›7÷ÖtðÚrî×ÕÁ„çJ{’Òã¸ï61±XÚE6ü÷¸Â÷sŒ;H„½Ò+âuOîæÂØîÛføOUñ›íÂÖN„-FZ8'“ÝÕ8°—ð|Ù}šÜ`ñÀRí8‚ç6âû8Æoü¼î:ï&¬Þ9âCT.86{l´m«‚˜Ÿ¨šõz,ÄNò2–~&gžp ´Ú¾È>7«O¶¯ÇÖ4ùÎÝ~!n[ôòç…\X7¢ÿÎ12©Ù]q}˜ÿ(ðjw¾ŸùË`2sãd›={Ö”{o2~9{ÏÙß«ÜïÑøçß…¯¼ÎË g³ÜÖFÃèO½õÏ…{¿Ô0IR“-Ûä ƒ×Ëò‹ &õšÆ?‡3Úú7>cæ,?´÷Gƒ™µ;=ããêyŠù}‘hXZñfÏM¹ÓRüqE‚tòq è7b8¼7ãÀÁäíœìù J…ýO~Þ_‘òK-½G5yqÏÖ©rîðDM­ç œ¿4föû³¹¢Ñ-RgT‡s¾1#`ÉÏf_¶&óïœzõf¶T˜ï†O4¾¥D;®ý+?|1ÆïÔëݾ"/\ÜcB.¸7q.7¿Ež¨³}ŸÛ(¨ôi‚rJC¼ïµnט•ªí ]Vþº1r×G¶>ZjãúÖñùº‡ð&ÕʱυžëgŸ›hw‹,u?ÏÄc4ôŒ{zjÒÀ`ÒhAÝ=I…ÞÄÚÝåÖ¬-ÅÂóãÞ²];u ÏÏ)Ƶ0YµuÜzÔxzjû¼[¸X²µ²ÀÅbû¨cfýt¬GyóÞ…â•A/Ï|Ûq ör˜5Ã\°›æYÕ¯z QGlÜþæýhø95ópÈ`]Û~ËÔlOa}‡Ý'žsc ¬¿ð×áùŠ"Œ¿í ºqº{U}ýÖüpÍîCl.+V' 8é¸<1˜Œm±eÚ 0mSýšqp²–Ÿ§Ë c\gQ1¦þ4ðÈ¿÷²Q·¶! ä¢,› ‘_Ò|2‚‰ªF”Ì¿Ö¡ŽØz¢&¯1Žìêé?Ï'ÅÀ¤›·ßªä¡s¬ÝÓScˆ|ÉŒu¤Â0½dÔ"'¸Ìûþ›MÉñK’~C¾ùݪæÀúóŸ4¥ïO~_Šñ¿¯«_ÛòZ TQÀÙ¡ûCË I_ÅÙò'Ž gô€ç‘µ¯ {Lú5ï{ò¡O9Ž»{òûÛñ*4yŽ×©Õlq“°ú·_]2tõc¸p{ŽËû•1¤µE·?Çk´ƒ>M¦ž½þ:˜4™ÒûEßY>åÖ1—õAÝý:ÆŸ,ÙµeßÜÛð^é´¸dúcØ=jí-ÏòàWÿGg'´‚†š Ã`bˆOcãî5åæ-üþ‚¾À‘ÿaЋÝ`måzçªñóE5^Çv­ÛÇWa·Á.£ÑзvÁ>*+}«4†85á:»%\>9ó(©B<ß¶0Ò¾7XÞðû<Ÿ„õb~\ŸŽ jðy¿¦Pìãü"¾{ÅXÊñ} ¯<®WuG coäÕ fq;”!¤Vܳꖟ´ëVjàm3vxÿÛ¹ËCH¥*lýPíz/{žl‹=O¶.Â_Ç„Ïw¼N„Ísÿïâ`X‹Ÿ¢Äu9p«C‰^äÌÛäðͲ1–C@¤€†3×YOÛÙßü÷ø ä#ÛŸÒí—z>…býî‘‹B:ß5M’w½*Íu»M¬_ÝuÙÔ¡/Ì$m2.| !lØy…—Ðç]ÂDë¿ ë;ü¸  °ñ°&ÿ1~~Ôö#œï@‹¢‘6ýrà57½ô¹MA[ëeå¶Í2´‘ŒÌ·ž³$º–w™}°g6Œ3-œKÐÔMàór¹ñ:Ãd3?¹KÎ,ÚÑ8ââ×°[{›,_[ïÃKs=RpB=Ó®ºŒ48~pëŽÕÞÂx¯lŸc߇ÿï-ižóï] ^çêèÙ!'Óï€ý… m>ÿÈòdúµ?kn“aÇôÒK¢ïpø¨ß!äc•G«ñêŽqÙ÷`瀮.â@Q||)Æ_P§Í·>uïŸUBû<Ά'cϦ~ÆøM?Ong~­)DßÐûYBøý@ïrùõn޼ͲÀæ ®€i;`둚zÁëÜ_ø¦öƒÉwaó¯®ë‹ÙÐæ¸ÓðZo¾¾ºÀ«Ù¾¬9Br'^Û…ö~±çÃÇ×¾Ø{N—g«Àëð¼î»PùJ䯗g³aušïç»oÃj­'>™bQ…½Çî !¶U«˜NÍõÆYìoM=`œzzOvû|æÖëa8Å/jÄV{ùômRëUíù«¬íÀ:gð’×BHµÊýÃ¥U¼žÏõý(ŒùyšY©ñ±žë­9GU‚wÛWgƒfÚpí6ùeî˜<Ó$}×o*©"#ﯿ~i•v¾ÆöÙsÝóŠ;ˆ×Ž4½ýiåÓcü¾7^*–oP £]=v:eÃ%‹ÁS&§Ý&éæ¾ÓÇ'ÛÃçÇ TFVO%’ž‹´ûü,?|:v¡ùõ_¸Öý€¨e@Lº/Ç_GŒ×¹©N;zC©n^d• µó¯ÝüSx›|lÓ®q†ã Ðà´öÈÏWó,×7øy§ž°ŽÃ÷§À8mš:Àë<šVÕciµx¨32lI6Œ½ É>%·Iãœ.*“Wýaçë¼/‡.ɈŠÃÝÉ<ËÌ_Ù\•\›d=épߨz›¯k ^ÇçkK»ÙNñð¾gÖ²Ûï³ Èõد¤š±$á´â|.±ƒvMlÛÅɈùÔ®ßç)¿ÎÊæ‹ì½Äƺy€×Yk<ØohX<˜hˆfÁ|¿¸7cɧ¥}Ú¯ëý®i¬ŒD.ævò=Ëõ¶ŽÎÖtÏI(0~—{yúWH€‚w:¾™æÌβoK6)zl6ùÕ ^7öϱ—‘ž]¤¸$x¶ÎÈž7[GÓ= Æ¸MÖô|> –)+¥ÆË‚•SÇz.jKžwÝÞí\%øY™[ ·š*ï^—´ýh©³¹QøwC(½oÔ­Ô~›ÞZœïžà6|àÚ‰±~'vdÁ–ae[1þÜ&†Uqûý'1ÒY? ?÷öuêoµã¡§VuÂ:VÆuüú^wØpd{»ÛQ}øºÀøw~å¨öKû…Õz7wÍ‚'KEñ£ÍcÉä—Vú¶ªIŒš‹¦÷¯Jû‚w¹ùß* çÊ /õßýîe7zÎÆ¯ ¼ŽG#Kû ^ìxÞpb8îŒR‚÷ŸÅq¶\!kJŽ ­$m·ÄK˜ð|ÙJÀ8éÍ jœšÌsT%÷Øúyý÷Æ&@“Ô@E|,X·Wyh-~~~žÕ~®<ßgTßP’2åüî ž„ñ×Ùy vNS“ïÏøÔ;C¿¬P$?‰‹1É‹üb¿s–±¤ó@ÿ7¯ºõÿ.J4eçIØøP“Çøï§\L­ÿ°8Úk@Ûà×–¥É1bÉ‰Ç Ç4~a™7.^š$ % íŽ<Íií%ä1ë—l¨wãÎXòmù‰&‰à~p[ñȤGðlU³ÄeScɾÜ=nó› ƒ'û®~äJ2v>i²ÒUÛÇGÿ4špd’v]˜½ïØ{I“Çß>ߣ®lp"¸¦Í=º)ðìˆ$—úyÄþ<ÂHøáýçEÜ–PÒmžwB—‰^åÆ÷üºª7ôÍ*S]þùë­Ãq¾=F¹$Âñ6Ç/UÜðVªeÔîæqý_»½_„–ôv¤ž %Ï=Ÿl–áIX^•^§è ü9K>®ãòû&‰ºoŽï¬©à·£KÚ˱¤rQ}{‡_ö0MêíZ˜J4ǃûiû ¿¬åSgU0=y= ë‚è¡6«èº<ÆìÁ=¡Dxj]´àj×G<ÅðrnB,1¹êÚ¦k/1 ½´ºI¥O¡$»~…_Ú}¡Âî›#ûŸú!ì[ñó:~Ü'Á¸‡[(^M½‘"#¿“³*?‚…gô‚_=ˆ%•;f¥Nìãç}½´Šœ´üæºòîLíüžÝ÷²û‘ìü¾îyu)^G³½–“=·wß—• od§¾œËŒ%ncÍÍýãþD2îëÔZr2»R»§yó´çQØ}âûØWáým:5ËÅ©]©sxiømTœ}×5+Ï—ÜL?–Ð~Fø¼“¦nC+¿þãUîýÊÎE±qþ¸=ÚÚ¶t• ¼N1wŒ·AL±Ù³§Ù¦L˜dÕ"¸yR,¹~,óxD¢iÚbÉHsC9áÇ^åö¹Øúû>Œóɯ·çë¯c: WЬXke‚Ãä5}žÝŠ%SC–8ŒŸgI6…uŒõéyÚö¥öÿDwÅ×{ÎA{’Àq›ïÌ•2!¹þ¤—m1.ÏK­’}Š·à}9³ÅMÚ¦£—ðâãi÷X\ÝótbŒÏ/'Áêø?ÏÞç>„Vfɧð~ŒOû2ðáÆÎô¼•œŒ=Ð|çÊežåös7T“ÿoº¦0“`îéáI—Bí[ÖQ®øùó=¡¨·À#Îw²üçïë/áùiòãÍù=wæ5“d ÆE_ìxç~Û¶æ~ÿÎ;®uñï %ÒÊ•LlääO•NãÓ%ì¼åGa=‡Ÿ?UÖkfüé•tÏ»ŒÿÑ}Òu»dàN³î›õvýÿ:öy,‰ü4xæÞA= öÆ+ÿ8ÉÉ©ƒ£;\‰ðÞ_¬°÷=»Ïÿî ,Ï5ù×ÑŒŠÄÉp“¦tvÇ®nÞð>–ðãšNw·Íì—‹å$öõŠ×ghï3ûÛyD¿&KR ù<Æxâûl¶rL†Û8ÒëCÖÙ³e…/±„?ÿY¸ÕÐ ^r²ëœ‡ÕŸÏrç~ù>c=ë]°¼l l_Q“Ï Åì»Ö1/õ¿õõÉÊ€ªíÖùKJö|cneJ¸Ót}åÄJŸ Ú#Òö¶þÁøÛü:=ÿaÜ#½ª¼2ïÊu1»š²4ûaÏ߯óøÞf5¬ÈêÝUŠ»ï‘“t‹V‡ôñž'ë=v9ŸßvUÏ_Œ· ¸~Æ·d¢9 žA×÷b ¿ßä8w,ç œðïíçlÞºï/¿Ú•ø¾qªi2Ú5Ô»µ,Z¬©h?–®û ¾^Ç`œÉ3~Là¢ý\ì¾òçl+óC¶®¬Ëm—âu>´[xäɲdȼ–nô{x ´ÍK:~-–49â>³‹Ý@b¥9¨ '³Ò†6÷æUŽŸË¯§êÑ÷§Œò˜-ñ½5yש_oYïøñÉprD®-2àÍkã»ýÎÇ’7Oæ_¶%Ün·ú˜œ\Æê3ÑÓÞö}øuUÃR¿×Q`ÜÔU§nÁ<Î]¶uç³`ï‰ã¾»ð}ær³û“¾ü‹èÅ»|¾¡Í;Öïøõ Ca’_ÇnWj?[×Á}˱k2²Ÿßc×íà!ê±7–ðûáµÈòÑs¯Ê‰QŸ¸‹¶þÚs ¬>´ÜäîÓ@_g :v#¨Y©sÐz Åíz¼XÛ<Þ¹s' ÀÀvךˆ%üºcPeܺ•«íeû ›§òãYÊÃÆ¸éiƒÕ5“aζ3 f=€Qͮ˳NÆ’Ö{eŸ»·(9<ºfŠœ vrŒ}ÕʳÌýÿ&ìW°qÌ6µOûGaÍé<‚?‡+ÆëøuêR× B,½C›[?€ñ™Õ\lqœtÿplÜ×?ƒ ¸i\¯Â91k_¼Ø÷ƒG¹}E¶îÇæ)‘cŒžÕLÇáÏÔù /|K‚Ð)K\0y¶žÞHKÒ&mžØlÒPXfTPAÍ02Úr–q†µ§°^ÏïKÿ;G+`çÏ4u€qcÎox“üŠ»¼H‡Ýû¹Ù¾Š% o۾̺1n6®j¼Þ*L8×ÔÚ‚¼žU„ùÈøšþ›‡·¶>¦É{Œ;]3POn—ËòJ:h¦q¿bÉžþa‹ƒÞpÓg›02²hDÚ¼ÊÚú2Íz»jŸ'¥ùÝ Œ{†û™š< ºTsZ^o[:$Jºì:iGªÝ~cÜâ[[H½ë%èF4?Sj¨7¸ýÓ~¤žðùù«9]7¢ÜkŒŸ·­sÖÓIp¶®«çYé²È#¨Jï4 ƒ ª÷XÙ,Œðç«´ã‡Oó§o½ñõk©s8z› Åýf~Îé?Ç }n§èu{;gÄç„‘¸­ùÕ8b‚>·ø¦=vªêEØïZØ{L“LJ_~JùöêßÔL‡˜A§‡Gà{&|ôö'U‡ô%Ÿ¾\>dFÚÇŒ´†á÷©„q{òû(tÜqeæ‚ ágßA«_7VN–%‚³f¡6 \®)ÄñSní„js>¶þœŒœðû”ÚþÀ¯›ëî9 5Æ[vDzó^œOŠk}Û* ôÔÞ?\ócÉ©[Sl·l´¾…‘í_ª_RW{î¯V>¯î>”ÞæBñ­üе.ÌO„5OÞïP% ¼=°åna,9°çy«}Ñ– |¿øÇ%³0â$ÐÝ|u™ü¯$äínÀÑ©Ôz—ãûwé-º¿yøU*Tû¹ÙfþÇX¯gt‚-ïcƒ #6“ŽvIªâQæíwá~³õëRç¼0þÔÇa™­ë%‚Ù0³kÑ©0Óµó‹cßcÉ•e6¥—X€f™hjÑü*æ£G™s¿m ¯š óXaºu,Á¸©¿®û•÷‡Åݹ±3Æ»÷MÝø[‡Á3ûy’gs„ýtv_ØùˆQOL—ÌéTQ˜ÿ–:€ñ'Ø$~R¿K Ÿ/–<½XýÆœ3¡Ý´ª„ß #{.m³‡g¹ß¡ ’‰½~¤=ŸËÞƒºûÒx¯^ÉË$Àê ý$ͬRáÀ·6ç;àý‰–þÑ3é@™»ŒFÆÌêŸd>γÜþ«+á|šÿçÿ­žNÿxuþ¿Ï׉ó— Ñãÿp÷F¥§õ9)ÐçÙÓŒ}ÃØ†:þNö”}£Gý””ÑàNÙ_ŒÑP¬ãkÇù3o;kFçm'§Þv.”ïj¤Ãi°Ða¼ZRÆ+ç…â@¹7Ì?\­ãwÇù¡8PΫ e4(Êø‡sÜi_ê‹Â|ïL(§!JÇC\—}£ËžfìeO»SoÎÿŽó(.B9ˆxö4çÂqÃ䔿NY öÔyÒÂåX œOŠ5eêzvrlXÿ¿áÃ2vX€Žg'ów§~)ºŒXõíÔe5pÀ¹‹–=-¥žy–Ôç‰ùvúëøˆPÆŸ6¡üiµšk N”×À±Åœ);–k(N”A]L™ Êlp¦ YŽÙ ¥~OKöŸžûOÏ•êýwö\#úÝ3Q˜À¾¨"}ž'˸ Œ7fIY‹EÔS/òÆ\(‡ó•R.çÍH“þ¯|¦˜Ÿ(çÓ.¡~¢"ê'ª¤žzÌOÔR‡ÃhMÙ8E('Êl`É:T&uy.Nek»S¶¶®G2Ç’õCèxQ™6ä}ß:>ɺÜ]ž,ã6()OVŠÊ¤~Tœçh1ÊIÄódMšó|œ(>ç;ê@=©˜ï(ÇÇá8Ü.¨LÊ“ (ã«gMý©þŠ×ÈØ8:¾zÌ'™ã©tø8JÊå–êxÊsþ¤FØ\tx²¾Ô³Êš2º™·^€ŽW2Ç+ê®eÊšR¦lSÖ¨ï_E9.”çhDù Q´±H¨•©'‡ó£×e;r5Èý)Ûsÿ®ßþOö_õÖ¿ê«ÕKÿÓ>ú¯zhÙ¾ùŸô˲}±l?d½ð_õAÖÿªïq½Žëo\oû»¾ö£—•íW\¯ú«>Å=¿=ž˼?uýý©÷„c!êðå”mí¢ãïYÖ³ñc,tüÚÅe˜1ËÚ™ú³s^ž¾Ôט󾋢¬æÍÎñ« tø†ÅÔߘó¿3Õñc·§~Æ4ñœ)cBDù[jêwÇ1`8Þ«¯ˆg¾4ç™ÔrÊ~QQ² ™'§ åjYӤ兙˜¸â2Üiγ˜cwâÙƒ&”=¨¢›]xŸtŽ7E=뤔/aI}ë2©·&ã¸Ú÷Ô²&ËÕ2[-)[Ð^‡)…âvC¤ÿŒ§þOéý÷ާLéwS£,1ýPÅú<¿•q'Ëš29vµåNp,Ž3˜Iý:9Πšú²ËQF˜ô”%Âä÷Eèøvr…àŒR ,¨w§Š²™‡§µkPŒ…€*FI(s‚ùsžžNXµï«îP†‘c©ã§î Ã™f|Ê—¶À¾äGý†”…Ãq¤‹êjy|\r>à ʑว"¿tÊ‘æ<†å”!áBóÔ—2O(ïÔ‰ú¤3î Ç9åüÑ}QEÍyntåÝ0tKÊŒæØ{”oš‰c¢ Œ(scܘbŸqG©QöeXÑ–:ÞÂN”µçO‹Ã…òlÄØOQFX(ÿsJ„½Ä•‰òÃ>b¡Ã.uÐáI0v©5eê‰)SÏšrô8nM€Cs ä~âûÏ8íŸqšÞï8Í‚~öîcûÓ$–è°$ŒcAÐÄ–P–ÇÓpQbLt?ÊÏá¼Õ£P&˜ôÎ(Ê“ßU„rÀ"¤þê.(%Ê —rt8N¡/ª%®É³t8f¡=J -gÊ‘°Æ¢ñG£$X< ”ˆ²u””íKyЖXPþ¨bÊ* @£œ°À( êÁÎñEXl¾exº¼RƓȤ¼R?TÊ 2e$ÂÏ ây¥"ÊÛQRÞçÅ^̱K±Xå(#,XgÊÛá¸ÐRTå•r|0Ó6Z&4çË.·(ÏMÔeñpìD,r” eMù`jêÍî®ÃQä¸ÐEÔ£]Ž2åüÙux¥þ¨bêÕÎq¡‹QÊ”0Á&áLaz:ÌR Ê,-Öa–šrœ”%ÂF"¥ÌSʕล&Ô¿=eA¹=Ì¿q%ÿôÜz®ÞoÏåž»\ç”9£”(‹2 ŽSf„ í‚R¢,(Âã”ÙS>4ã1rìSÊc,@YcÒûSvÓ_°{8cª˜²{tùÐŒÝÃñ£P¦”ͱ{,±XüiÁHPJnOe÷plèâºZ£“¥¤Ì Ž+«@Y`qù¢ŠPN”]„rÀb‹¢Ü wʽ`\3Ž{á@¹\JPr”‰™–ác)âùfE({,N?Tqsž­  _”š2|#šc2Ê)ŸÖ¥FÙc¢L(‘ã÷ˆ° ¥¨”C>t&ʋܟ:ÇÂӂ瘌(#,|'ʈ6¥LFŽác@Ž2ÁfàŒR ,(#Z‰²àƯ(5Ê›„?e£YRž-×0œt8Œk+Öá2ÚS.cåepœ´"Êò  Í…c3ê°•(î×~ÿôÜz®Þoϵ¦Ÿ­ˆ{†˜À(#žƒ«DYè0Ó’cm;£”™æ‹*@Ùc¢ûS†&¼eŠIï‚ÊDYbòû£ŠQNe‚…àŽR¡¬± üPjÊ‹ôC¡ìkò\!Ž逅"Ga±¸ 2Qb,šZ8Î(%Ê‚2†8†¤%’ey[cAТ⸴¸$(%Ê‹LJY’Xl~¨”¼ × ЗrÓ8®?ªå€ˆ2ágñ\ ÊRQæ?-TgTÊ Ö…2‡DX¸¾¨"sžƒËqÓDm´\p,æ(þcNêò‡8æ¤)¹;*“Û¿¡Ü´”e1î¤?ªå€M %ÂFà«ÃÁ  MÁ²Ã¹æàŒŠB™b“p¡ì4#®%eár ÆX¸"ŽK„R¡,°‘øRF‘Š;åášr|q”eI9E”7Äø”‡ˆ«Uî×sËöZÖgÿ®Çþ«5IÖWñ‘•Ú×eœ´²ýò_õJÖ'¹-ÖÓöƲk¬r½ë¬ÿ±ÞÇú]Ù^Çõ¹ÿ¤Çý]+ÛÛX?“êñ½ì¯ú×øþÅõ.Ö§Xâú×—Êö¤²}ˆëA¬ÿp÷% ¥Òç™´~¨"žUf‚V…²Ä‡ëK9e”¹Í±••yÞ¶/eÒp{øà(e1Ò$à˜Ú"LwF“ e„=ÃeÉ­¢ŠPö”›­G9‹&˜ Î( L%w{D±)ÏÉv¾ @™bâ¨)‡¬¸ Ï—B™bÈDYc2ùÓdâx‰–Xï~¨‚–<ûšã$FQ–¬U€rÀ:—£L)#ÑkÜU„rj§å]«Qb¬ï”QGž‰È™£|kŽ…X€r LYS¬e¥Ï´¶äÆL=xŽ,ÇUöäù±ßв ‹Qâ><ÓЈ2 åýxž¡%ðlêÆ?ÿŒ¤zÿã{zíbî?c¢L(3V…²ÄdöEèp96¶ J‰²À÷C¡0ÑPÅÜþ&¼%¤wG©QÖ˜ü´$¨(”)‚•É)Á‚ðG ,9–"ª˜›bÐ"qBE¡L°XÜQj”=M Ê Ç¥BYb¹SÆ¢5’?åh‹± QF xf¬e„ÅåŒR¡¬±È|)kÑ‹ÍU„r¢‹*õÆôCPnlªå„)G™Šð3ˆxn¬%§;e.бHPFX¨.(Ê Öã-¢,°pýPÅæ<7V²hÃ3¶9þ¢³e„í¬Ã_ô§ÅíLù‹s[ŠR£ì±ØPE(K,z_cmN(ÊŸ76eÄíÁR·åp+P"lî(%ÊD‡kMÙ±FÖZv¬6)*e‰Ä¥FY`C‘R~¬‹U€²Æã‡*¢å*æú&  åŽýH…²Ädô§ É1¬‹¹ß:`bŠ01¥(1e¾r ê„’Sæ«3JaÒº£Ô(±ˆç¾scÿ1ÂDvF©PÖ˜ÐâV<³Ú û‚ò^}QE('Lò(”û/Ê{ª˜›{épªýP({,„@” ƒ3*Š[ë¢0Á¢p¦Œj‡/ªˆë9”û*ÂBqA©8aÁXcÁ ÄX4â<ó•㽪zò¬×"”=ö‘Ê¢vÂ>bOY¯öXdrTeO«PÖÿŒÓþ§éý÷ŽÓœhl.yPr”)&±;*eÉì‡*B9aRGUàùÖî(ÊÜUŒrÂD¤ÉîŒR¡,0饨”“?e„àŒR DX¾(5Ê "U„²ÆÂ Åá„ DqgåP ”)‹U€rÀ¢‘£L°pÜQ™(k, )Jc!P¶=”e‚E%AE¡L°¸\P™(1™ªeÅ€*FI°è(S,XÑ»ýñ¥'á}y5^§Î€åVË‚àΖÐUu_¦À}þŠ…ù±¤°m²mûmý†‡œ‘ŸÖo„çWü/ù`j¾ˆo¡X‘xCÔòP*wßS¤@BÔ§ú)±ä鯆dÿn _fíýå‹ñÇ9‹$kýV˜OóQâ}‡ÛóSâ‹0þ‘gƒmíž’  ÛnJ£•çöÙKâmZ.JëjMêù}Ť0r¯ Gª*ïG\Ö߇‹+ƸvCìÏ,þ&qNµ)à±>}O׫±Ä6ÜÜaþü–„s5:Û‡øtÞˆŸ÷i·õJT°)xÉ—£ŸcIß«óW$„·vMǺ…‘C³õõÎ{”û¼ÌO‡Ý—üêóÕ³Û—òËVãuxîA<o¼vʬJÅxÙ”ºqäOñ”+c:ÀáCÜŸ0ò\:uÄñpÁß„çHiý±˜‹®½Þ–B±†ì½ªÞ·U0eɃFï»Æc½äeØpˆÞҼס¨0jTreOSÏr¾n?6 ^¥gV™ÏsŒ'ªÖ÷[³Nñp©Òû å'Už{`ûð8²»ÝÖ ³B^ƒƒ#âÂÈïÎOUtÖúY³ûÎ|üØsä}½ºóßÔä=^Çs]ìªozñékFƒ—u]{wJyy廟~aOX¥>ZëìÍ02D Ñ>Wv=ækÍüR¢7©£¶Jûï,ÁëÈÇsà-% álÖ$*8°ÛìÀòYq$þÑFKåùæpà e¯Õ…0âöMïñÁ®Z"ö¼y?²ï‚¿'ãéò³¤xÿz[]á¼’úo©`ßûYõ_ω#ÛnŒVíYôÃæjgXFb6ö]׸­§ÀÉd3hö}7G×5¯cójຠs•PÜÓ9Ô¦™ b ê‡:Çž3[Ömã‹å¿Ïž÷!§+Þþ,øØð¾œÍ©!õ]Çø;Rz_<,RÂë߃ú,)¾MüŸT\GdC8àq[pyåÿi»·Ö/‡õ!vÞïQëSÊx|Œ—®© ¼Î¯˜öCŽ¿¾ SO?׌¼ú f'~\G®,,N²_ÑV|ÚQp|eñ™?úijåó–ÿ>Êñ™O”¦>¶ŠíO´ÉVÜ….’øqÖ܇9ㇶ“mŒ#×Þz·q¬sê±Â~Awz‚á¾MB>—æ±Wü…t}$EÿC§Žùß…N+ý®rÖ-{þæXé6;ß/¼ÐúU7måï…z.o­X]ŽwÃ?·ß‚O ë#š:Áø33¶<ê|Þ{Ë·W¿YzÜ‹.ŽäÅo¿¤çphýg/ñF†½pÊWÓ]x?°>Åûúk}ìyþM[`kê¯sh}ÀéqïBÇ©·z<‰¿2ç{ãFfÄ‘›Vu›N K[Á:ŒŒŸ“WïÖŸUdËõ÷¶-)|üß•ñ/4uqyî×0õˆ-¸ºñùnv±÷—8òb ©dçHh“5Îþû0"Å‘FW ÷‡½ÿÃW^ë7f§!T‹ YgQšŒñ9úÃãKwࣹ£‹»í=8= A:wÈê÷†¯÷öëó6™†®#úV®oj» >„|ß.꺫oÍ7Å-ñp4õ€ñŸìÙÊÁàßÓɰöê²áîÖÝÞ-¿l>žÄ/™ûùtùêSãÓóŠîåÞ³»¦tér¾¢§q‰iWOí³¤ÜAK¾ð:<Æã¼ó‚îÓ®%ÂŲÚÞ!‹Nm¼VR§?LÖÓÃH²ûäW‘´Ï™qåxnuuá}Ä÷Ãv”ËÎûxém+[ÝÙü(ñr\J=S×Õ3Ò¢r*¾w¸C¶]v­_9¿´øýu¥úh™·:9²du9ßIæËÍÞKŒÿRŠÿŽ× é¾/k›sØo뺩—m2ü\—´¡ÞÔ;$ò~ÆÅ©]!§ïZö ^ÖÌ£\]ðãɯwA—Ï.Æø g?T m#íÚmïX!‚úíNÚ5ûOv„„¯–׿…‘E®Â°<q;ØxŒå¯nŸ’àu²ó5»™ s\öÝè}7 VßðyryÑ2~û…‹›.u¡þ¡adÚÉšÂNjû-Ïù«,<ž'Ö¥/KŠñoþÙU§õöX8c*J©°5 f|ºþ#f%ÆÿÖ2 ib1b]Ï3£ÃÈÜ:§/OÁÏoüóíË=,~ uÍû³¶Þ'š·`üÐéòÆ—;ÆBýº•æ4‘Õ\ßV-Øx‡TØ þÂ:¾7²öx5+=„÷,ÏßÓüñþЇ¨Àø¿v ß<òþm¸’–á+i’3‹ŸŽÞxøí§CáLeËÉ€0ÂÑ@Ž ò(çÓÛu•{ô¸ß¼éæf׫îkú¾×=ê5¤¼¼N®¡ëô{‹nƒQ³¼ÜطÖpÒ5öFCLדCfbù}zij7‹²þsom¨o¸MÙ÷…nÞêm/—¤ôœß¨îm¨?Á mX"Œþ¦o¿øÉ²rç9³ÕÛ'@³î­c§Œ x´¥ýä¿ÙÄí¯ÑåjðïRüEÆb–Ñ82&î|?âà+M„I Le›Þ% {Øõtr2ðâ0²®éŸŠëôV—ó×_µ¾úç/ÍyÿF1Æk¾bŽÃb—X=±NU/»D´œIFB_bþº¼¯õ›Äë\<6cÁ°¦1pûáÃkéF‰àmh=3uÃ]RòzíÏsã&Â—Ð¤Éøžö?¿Â®ms÷rã§GõCo?Y«å×ðþÜKûãuÖí>Ø=Lq ƪޚïHI€jÛg|ªq—dظÛoÿîÏ>ñJ^FOá2ܽœï.ïÓgTÊ_0ã~©Ù¢áòE·àGzØÐW‡`°hÚÃΙwÉéñq åˆ†}燑QÏ#Î?;ªí«¬Îxk£§ÛbñIÝ‚ˆña>(o†˜5¬´yùïAÐ7÷Ù•‚ÙaämõhËCíÜËñ?¼7­-6nòÆ'oìµk1ßçÔ¿ËW}¯(àçªÚµJì`âÁÙ®UR’S×}6ÿþn ·çJ«†Ï #¶5†úÏÿéVÎW›ñ–t}OõvŠ“Œí®«€µ"ñ¦M`ÜÍ_#÷6Tžge w‡õÜb‹ó*Íðl±ö¾0^»Ï8ü¼>?D×}àÆÂOøÜ˜2¼8¬9ù&µ½’¤+ [³íoç‚F#ÃÈÒEŸïŸµyÉâ³÷%Ëæ7Þe*GV¤m¼Îb³‚̼ŸøÐ¹—*~í)P$÷S_ƒE_¿³ƒ1ĨQ×0úu[{áN˜_)ÿy€qÖ³¢!mxþãUH87%j4´3[w1%(\ŒÛ×stT’æ÷]Š›&¥ÝxZ7ŒDþRŒ;.v/Ç¡g>ì¾%Ë<Û×Àç7ÆSTwùÙã7aV•…³Þlˆ‡6ýb̺-SɃõŒF‚û‡59k¾ËÉîþ\F¹ }˜ç|hë´G%ŽÐÖŸïï·z×nǞ܀ gFŠë̈×»ÁKw+‰ÏÞ)—ó€çšÈɃŠ7’ä£W ÷›ïO•…¾Ëxbº\ÆoYïNýuMnÐñm<ôðÏ?|à’’Œ{]cÅÞ™£á…_zÿøgr²×Ï>»¸ÎÊrœ•:™ßúfíý)Œ™+ÿ<ùë¨ñ:š¶>ì:gºSl;ýž¡$·=m¦?zLyv\öNNbNïh™ñd…pÿÙõØ8—½7tÇ z;ÙúÐ5˜íþf^XÝxØ1yYý#?•ÄúÀ­™ÍŒ†£[^õ.‘“wr—™µB¸Oì{0þëëŒO«ËMáuŽˆ mô.\… ó¤ ÿ¦“Ë]Ï·nOÔ}·ík2w$0_݆ý®¨ö¹ ãѳmV­9RSËceë<Ç“¯/1ÆXÂNÌzû~Ñ®\%p”ÐZÖñäy›6Mw ¡œ]9y½Ë²šÓò•Âø­ìûœçꘗâ3K0þ“篦Œ†sãÏßU‚_åE¦£FÆÓõ¨õGéZ ';¦ô<{cU¹~ÃüÏyN–9lºSÇöèf>¾㿚É¿_ VÛ¯ÛF(Á­EªtÛôx¸;×Ö-ŽÝûXNö¬´¾6ÔM;^gÏ™rÙ€çà… y«; Àë9#ÆÅ^nÚçœ:Þ3}Qaq<ñS©õVí 1Ü MNÆ:]8Uè&äÏOøFùMè<Ÿ_ÇT`\×gDþŒ„¶š†®„ÕÎ5þœXOÖÕ²Û³àUðñ­^dªœø9Øu˜éNX^òïï6>ãÇkÔãö€’ÔS6‘0•³!ߥ¤ÜšxòŽT\/†þÓ=~®H‘“úš…4÷r\•vý^e[kçcŒ?¢ëK­·«P¼`ħc]#ÀÝøê˜ûë”°#æÙ/Ã#ñÄy°dæ;;XrkÌTE¢œì¸?ûOßêÚ¾Ï÷ŸÂ{„­ë”âcüY3|F¿§ûøk몄KôìOjÆ:Wkûe7lø¬SŒœpôßÀß«ãx²¼gó/]c1ƽ1yâ©KIá°¨uÞ¢³”ÐÚô^÷$U<9?æ÷>×\h»ª[/×å¤zƒ«çƒú¬*Çô¹ò\¹t•žÀ)Å­ÄøË¸aLÓp0®yxê·ñJÙ;ûmì×x’¶ü§x„¯# ²¸¼îœù*!OxžúGƒÍÇ«Îç9ÆØu”¬­_Ô{žZ¼q‚$ûˆªe)ÞsrÍzÁsX¯éonÊI_‡ó}¯ÕZU®ßèrì0ÞÁx—¾'ôà li›ZùÓ•”Û‘@]Ï;=üŸ©uKNZžìørÌ”•Â÷gë&šüÅ8T± ÎrØp¥rsÆÙù¡Á|¿òç¢rÇݰI”ŽÏgþÏ ã» ÏŸý=èN„´‘‘¡ÐGXërWÔx˜ºÜ…Ìë„÷sLàTƒs1 ä×âÏsއó—mŒ—“Ó®ÑÐUX÷d_ aãí -ÉÔw-Ë8H°/ÆoZyï“§+Ê=gÖ·ÇT÷}*ÅøÒôÖ#ß¹¹#±"ùr%ì±ß½ð{ÏD¢Ù^Ù•Z÷Ü*'-ž¼p´Òs-7/`ãk–Gì=Çó>(‡¯ã~¤çHãOç Ø%~Ùyì'½ý{¥M$ÏaHëá7@·:Õ^6š+''³‡5:½ÒU¨#–ÿŒ»Íæ÷lÿHSöžÝ—äçÏBP؆È.«”Т×çeʉ¤¤fUÉ{¸sÛ³F‡Árr}v“–ZjëŸ|·Yí~ís£¶tþFùW|vè¾›gÏ}Öã”_•c^‰”3<Þlm˜ÿÖVN*¾5ûËòrùÉçO±ÐÇuçz» Åê›§ïlð? éW’^…û+¡Ï·Û®wŽ&’6û\×OXë›ü>•ÀH9áÜê¯Æ.úã¨ó܂ƥÞo"Œ»Xÿä—¢9§ànÇ/7á{sÕñ¦õÎD'’…Om,r¯8€8ÆR?NN²ëîÚ¶ Ö2ás7ªêh2¢ç$‰1¿®z8j¤MˆÖªûxº«Éï×­ûw«0šîGÉIOÇíŸzžt!ü>Ï£“à¿WLO:¼5Ko_QÂÝ;­g&‘U ûœ34ÃÞ¾r¯۹”w²u{Ýqã~¨}ô‡øãq¸8kj×›J¨a!¾Z»uy¼KºªÃd0wË8·“œ®˜ÕtL5—r\‚7“S-øU† ÚtçAx/sÓ½ö{…Å~CÝ‹QBî“Mö L"o×ÔS_OÖö¾+v”S.²‹pÙ÷àëYë;ß-»ÞIÃa¿C“¿x:ŸxÄ·¹ëò§ÜVÂËOGÒVMM"×F½º~¹¶ÜiðöCÿÞr²3ø—•iËååÞ‹üøUîÝ®2üüÊa¥8…jŒ¿Öó8,v<û|‹:¯»£„†Û†ý¹$‘Äù׈¡õÅ+•.•“¬;'9­(Ç×cDVçÓ+ßk8:«0î›&Ÿ÷Š÷Ký7T]½>î}“¥½Ÿ;·\“D®®©úëckepdEÑp9‰5›¹~ã á~ñëž¿(·iòã.ÉþH¦í†´z‹Z£„” 9?}“È#ã/£t‡3ݹ •œlíeÔ{Òƒå¸Jàíixô?„ç­»¾-ÆøƒvYm˜´ît·Ùûû“VÔ1y×v_9¾åDä@³-ÙXNžæÍ›bm²\Ø_È×U+G”ذq¬îº¶ã>J±N³ ÎEÎ_Z1öï´8-=Dzì7ÛÜv´95}2ü%4»eåøF†'ƒf ³ûnÿ»–^wÄøŽ&Ö²’‚gV~h|£x5øò—kø\?„nÍqN»Sô‡d†ûØ}ö§.+·~š¤\P¡ƒa±Ð¿·ÅtÚÚ¬Øø}+~^€×ùµ3?ìDÜrx³9S’ß*¸Õ—êÙI䳸Q…jkÇÀïhÀJ&Õøì½iåZý)ÅISpù9âCTàxx³EV¬ãÁléˆz/ “I£Ù9ÍîUŸóU“ûÞ%G¹×‘³¶ß1.-GèrèÕwiÚ‰ßöµ&’Ÿ-«˜©»ò«M´N&ü¾÷d°Z^ÿkÄíP’4+Y)r[VnÝ©³«ôúàÝEÂ}`óZ>x.·ÞÞBñ•K¡}íG-#ÇSoý€ÍñÐr¡ó´ù®Éä¢m¿öM‡0Ë#§ß%;Eûg~ê"í×—£¾ÕZäE9Bñ0«Æºã#“IÛf7Ë$`7xù÷š¯B Ïs]"ð‹Øóü±(mgJÑaÞÀ¸@üý§ëíxÿÇ{7oœ´ž¬6¼böóxHVÒÆç[2I¥&˜zL‡Ž¯,*_ª"'<§ua9ŽÔÃG~>sMJë%7çk¿ÂÐ ¾d>÷ÿV7…^4ŒëvTÞø­j‡S XÃMÄ>yïÛØ§g–{O²ýFVG|¿ï*ìiò¯³âhöÏ9;I~¸ñýN ð<ÖyÞ“y÷ÈÛ'Ι½&€Yæ‡é"#9iÆá‚¦,ú?ï¯lþÆxdüs¡ûPr¡GR{ˆ»÷‹àQ½ ÍÔæd×=Òxº($£Óȶ4^w÷J(q¸èÝ»YW—róf~]Q˜±<âççt¯£*š[=wÂ~¢×s[ƒªàCñhçù=’ë6ÏÞeÍH&2_µxi(i>ù@hbÑÒrëT<_—Ϫ1Þå«]úÍØr\oÚìçÄØúTôüÞ=¢'¾0úôë°Rs@$”HÌŽ]û8q‰ÿ|}¼æ×÷ŠC½¼¼õøÙ¸ð­wŒ=SmîÔ·÷„u(Æåσ,)7ÞLKÌ5²ýýÖ†qõt9`"Œ_}ë ýðµ®aóÏ&€kžg¼I­û¤Î8Ù¹“¿ÆÂÒGÍKf Ó~NV?üü쇺ýµ/‡›u~<Ès-Å÷œ?·ñyŒÄ¿¬7ª_j¸oNXY¥ß}’½ £a^ƒ ùsœô}PâÞ?¶Ü_,ä5»Ÿl]>~üÁ±3ûÏùã×ç$v·òpøq²!m]ç³rnÒ’ûdÎüáõ¬&Bb'Ó)+‡’µo4ßa¼¨\áÿ÷Êp¥šõ°øåýéº%_Šñ ÖÏYT0÷I8÷r5ëDXwú»ÑÉû$ N,Îê2 §µù2g>~þ” ~s;,î;¿ÎQY˜ÿðër}€ñ5yñGj@ÚÄ`ÐÁž›&‚¾¼èÙ–ŒûÄaY÷Ȝǒ”ׄ’/³¹“¤xá„ µ`¶žÈÏ#ø~¨À¸Wª&¯l4ÿ$IêßçüÄ€D¨3•u½ *2?z_¢ŸëxnÎml‡’Œ ûµ¬µ@XÇb÷G6yäØz³¿Ø0ž«.§Vñ7ÆœÈO>Ezæ6m”œµ¯„vl¤"Õzß¹65},¬ij=o Þÿ®ÜÀj~¹÷>ãu²u86Ï×äûþB±Ï—¨¥‘#Ï33g ÿ‘³¸…‡ö*"qç2`6åx¡¤]³Zœr.ÇK¿¾»Yûcá×¶€.{{ë+š|Çøü~ïYòzBo³ãæIàã–sb/©½çËóA`ÜdÒì>¡drËuË\^9‹‰þ÷ çëÁƒÂ  ƒHkhçØ³OvW>Ï1ž´º¯É„{çÈÇ”'ÛgŒL‚í‹úG6 "Îùœyײ^vIèÚ9”¨Tp+në‚r\Æ\œ5šëÁøƒ^ìk`ã MžcüQ}çU5 9Oî-é¸7ß= ì4€IyW8n¶«Ø 4£2q(áÞ.µ3Æobë‹Gu7óJþf£Œ.ÍŸ’büêË‚·e;’Ñ‹*ŽœÁ‘¦}‡«¿îei§ýf%Ç2;ü Y\®ŽîØj¥¸ø^O³õ ~>ȯÃàuZÿ|9_!º@¦‡l{œÞﶤŒT‘»Ùi³-`ñxoÙ噡ÄzŠ‹Êwùñè[a]ˆ_wåóRñ§¸†§{ $ñÛöÎ’¡qTàõ cT„ç¬v… -¯§Z %£ W&œ^Xn³äø%I¿!_J“ScÜ¥WZU~³à"ùpÌÿËL‹d°jtÜÐÅIEø<êãê5òÓ5”Ôߤ|õúû‚2㋯ô\“¾0Nä럇ë(óé ²¼®Èxîðd0[¹Ñ÷¹DE^×6{Õ4j t˜Óð‰SƒPÒOj·æ·ÉBaœÁÎ 0Þ»ÿü8¸+tv#ÑÎÏDxB÷$ñœ_AÄñn»³ó<’Á­cs³Ï‹T$uצ±/^ ƒŽ7»TÐ %Ç[Míú}îB!OÙý©õþ} ƒX-S“ÿ×êîG™‡"˜¸_õ}œ wFºWé­"6Öpùf1šOjŸÕ䥌ðûJ …õ¨ƒUíìÿÖ€~î‹6l~¤{O‚ñ[¥W»pÊ=„LŒÉ2퓟 úÛc¶ Ø­"¿­¿ä3G8fŒT©×dšÉîe8Ã/Î?Žü*#~Ÿç×û}r~9nŸqzåžÑG> ã%]þiÆ/ñÏ8vSFf¬ÙÑÊö¸áf’*òõp³ºûNƒê;Ú¬³®J¦Xµ SOgá<{ÎY%½s.-Æïü~!åLbüá.…†ñØ·ìëÅ/¾+NÏT©HÍ-]NÌÜ6¾V 0hJ¢9l`ñ\á9°þÌ~CØ“ècO¼;–ç©1þƒW⌱A¡ÄnÎíà §îÁ.ëÙ†¯ž¨ÈÂvÜ e$¸D<Ý”7"”%¶ÕïRe^9Þó§µiË^·Ò® ²s4š:8ˆóÉÓú7*Ö–ž|îÑq;ƵՀ@ÃÈ‘&܉ÄûPÏìÇ“*)¤¹Õ×¥mÀã6ƒõOId¤‡gÒŽ;øóxM…óC˜¤‡Úcœ‚Q1% šŸ÷~~í:/¾%ÂϹñ—·I‘pÊèBö£ 2’×clóf[æÑóš…6윟m”ñ—ÏüçR`¼±I¶ª”‹át>¯‚£ûTŸÙ%…|¾m”Ÿrg4(¦~}=Ò ¿÷ωì>Îê¯%tx~Çæ£ N¹}©s„jŒÛvr«·ƒÌ.·“WG¶ì¨‚ÓîEKýSÈêêã¶ü0ÚÔ/Þºë2¡^îóѳˆç±Õ¹Và €'Ó=7¨çã¶*¾C¢ü.‘1Õö ©‚‹«»Ö¬è”BöeNª°ÐÖÎ]Õâ|D“x3ʽ/ùsð…üЉ>6pƒOá„&ñ:»ÆGž9Ý$‚ü¶ìï±LÍ*¤™9,J!K—ÄôÛ8¢žEííJx·¤\?âÏEÙ‚óDnå ô÷cüÃoªÓŽF§Ë¸ª üC‚¢Ú¬I!cÌö,tüàWÞz¦O%§jµs†¿#r'¿ÝRÈ3v®ˆŸ?ðï5 Ưò£j¿™Ÿ#H÷ûµ¾w:«‚7Y¹K†ìL!;žœ;}$h¶ËZ…|Ùiœ6‹°q ¿pKÈ_Ý÷¥ã.^cð„HâyRúV«ã=¾½oM!í×ï1j(=g)#|¿+Œ?ùó¯hŽÎÏòó~¾`ü»WGDMxI·^ Ÿª Øèí¶ÞRÈùÞ[Bìäƒ@o÷šŸ½]d„{úûßÏú([bùȾ‡S“{¿Ô°ÖI4ùŽ×éRõíѹk/“ û½'Tø£‚3w9Pi ©ðÓñš¤Þ@xwà™têáVŽ7_$¼Ù9(ö=øý›~pukÜRãöÝù¼ÇøÏ®ï»6»É2pø–V Sàuó•"n¦Ûzi—4ïû¾-ë6x¼Œœó<–~ð,Žël–̾ út>KÏ¥u+5~Ö;T(^÷ܨ¡òÒòrÅ»U7»¥@ÌË~ÎßI!/ÇL÷uª-xÖßðÑf§ŒÈfp#ÙEB^²÷ÏsýjÃ票Թ7Æ÷ª6þ肎Q$Øé×9ù˜09Pmrû)ä}èj¹È¢?ìö2÷«}]FRÜ®„œ7¿Ü{Œ­»hòãiŽYì‹"½Úõ=lE $D¼ù“š™B¶ædvpíû ×û×d„ï?s…¥þÜòIð¶­ZÅtªŒt«Õ£ïÍ1s…qÁLƒÅ3’†×„_ÇzNï1ì¶Mø¬–&¡¯{Òq ]'Äø _.íßÜàɹ^ÉûÕÛðø=Äë]…T2[mþ¸Oͱ°íG+·Ì2òé…KÈWù<á~²q%¿~PŽ4½ýiWqW•¼ Ÿîv´9=¬Q*lÜû(îQÃTb¦ðvÿðhV<ñ♌t,Ü›3£Žv¼Ä¯·ýγuMvŽS“¿¿€[6P_#¦t›Ýwh*h†–©¤æß‘;M€Ùò'Ž gÈÈ«Ãñ=³sœ…÷7ãî²ß_ðëmí·iòøp¡xÓ˳3gM»NL §þ4ðH…¤›«Ìz N%O“Uö/z88l•‘k§ö™_œ'ä›_©» m}Ñ@¨Ýý5ÆÏ¿Ï-(]'^öŹy©Ó;x›J\¶Ëš,ô -7†}‰”ƒj‰'–V+|þaÇôÒKh½ÄÚù3¡i¯ÔÒŸ_Œñí2 }kwƒTÌëæ½J‘ ÷7îž–¾$•4¢iûH0X×ËãW®ŒœÍ:ÿ´‹ááóóÿ¡ë’q6f]<–œlߨ~˜&Ï1þ†"îÜ ¢t;ã9ù©ðê”×ÑXÏT"žÝcñB¹Øi& 22÷¦i–Us„õÖÙ9Z¾ŽîجÚR²6ÄŠþŽ„æ?^gÁ–5:Ý$‘½“*WHƒ;¯åîRÉÅE÷Ç/µéšƒ2²jlÂBs×y„_OùÉïßã¿çFý‡WÝ$J;î/ipÿÏ }ƒí©dæîzá.ô‡#÷m˜%# :гdÌî3û¼rW›¾ÃüôÝo~\mlÿI“ïxßšÝÀëôå†G#Óàèñ£½©DÝÓu~xw[˜þÅòõ'eĬ=÷˲Â{‡Sƒ{œùzWÔD¨WÝuN5Æ<×굋ëMÒ«=7bHKg¯kõŽ¥’!†[zz5 ޱ¯ZMÆ>°lÏ‹]ó/(³ÞüÙ¦ÒÅzwhVêü§Þ‘Bqº¤Ñ¦]¾wú››Å§Ò`ÿËK®SÉÃñ§N$Œ‚~íçÇõ¬+#[¦¸Tò!ÎÂçfy¢¹|ƒšBžkòãN˜Òdÿ‘À›äêÙ~QwÓ ¿Â°Fö7RÉ·9f’ê9Âç¥_K !ï}>{´}…}Þñ;TGƒ™µ;=ëï&‡Ô9ø”ޝ1þýõ×/­Ê¾IÖIHƒ„ig—MKM%ÊÉc㾋'€æ¸pd1²©Ô~dä\¡¿°ÏÏê“ý€?÷Nç•ÿüî Ùµ¢IÜÎ;'×4H¯nÛ*å¦Òûà]O›U?ŸB«ö[ã0OØ_²ö{¸çаõMc¼k×f…hM¤CÚá«$Üå ";TH#%K\2q F½ºŒØÜë6ãg¼3Yç»ú§©i‰ðguX§ha«*1tœq½fk—×5šÏ»r Þ%N~»c¢×$žgœwZ”Œïñ"ŽP¾@Èo~|öG‡ðï[q)Ž·ãG–Ðû56šÔÊŸ^aÇ¡tPowhán•Fº÷9jl°ÒŽ ×ü¢€|š œÒÐQ;gõÓ¤šª–cÁ7Ἂîû\ñùMTR® ¢Ó!¬w#ÓHªþ™Ýíž;À‡‡³wxøÊȱèã¡;-ÆËì~ ûtþ¥ûûQ½£…âÖOM㛌&¸åûçéP¯whØ0Ç4r"‘›° …f7m]&#^Ü0çØÂòš_ß2òƒcÐä7Æ}xûà–g¢Éô™Ò:×za÷^î‘HÒÿ9ìÀo.wÂ_F$2»ªªË „üxßÓ'Êáx‰ ¿®Óøstt]ãòçÛ¢ €4þaèÆi¤UÉr·—‡Àµ¹“{ô”eáøûCÞkשXÿæÇ#úÂüÁeà·à…|| Æÿ]k“þ0Y4‰Îä6À€‹jšw_×4RcáÆÝ¥ýáÃç¡7YË>C»žÇò†í³ó亿s‘bü7?*^Ÿ‘M¾¶^øŽ>í©éFF¿ùtý€0âÔ´¢ùx_>)ͪõý¶DÈþ÷ÀŸlؼódëÌš|Çøü~m4i{oôເßï>[|ÓÈ6‡8…w3ºn˜%#A•f,ù¶|)i-|‘Ü÷³pþU“×g|­%V«Ò½J®ß Š4ý•—û4¬Ž9ÀÁ/vý'ËHDßǽ – ýƒõëãŸY\-á×1^zÚ`uM ÄeÊÕ»eÀ)«ˆ¢ À4rSh·3z¬p_¹Sz/%ì÷Cü:h`¿Ö­o½c…b“z‹w® ¤uðް:Ó2 ùÇáÍ*F§»÷ß+%zOSØáù©±ŒìÝz'+ïÑ’2ç> ç[ùu˜N¥~&Âøú…_]pœ9Ï:¼™¿9®8ÎH#ò´’› ~Ágѳíeôœ¨vªó@ÿ7¯ºýâÇÑgóh“¾Ñ7 y¯­ó¼ËPôeÒʘ¢4ÒyèÊs+“àÒýYûæM“‘‰›O¹º-ÖgØùF>o»”šI0œéýÞâqѼéó Hmj滹j:‰Ì—w+:= SK*;ÉN|q¹ýréæßßk5ú%ì‹°u¶^ñçÓ?žOR½ÿNÏ'=zo8/Ž=-¥~(öÔ…ó³“`B+¨W°;õC±/ÃJd žÊz•Por‘ë•ñ D”¡ÖñÖõFaü5åïèø’GQ_rgÊý wÚ…úrZRþNå¿êúrRN¢å$šRþN¦ŽßWlº¾œNÔãApžx ”è/8‰wšã$2gN"ãNÛSþW¬œ§J -Z õÈQD&åÇúSor{ÊJdžOJoòÊž–—aðXRö´šz>ùSorÆJ̤¬ÄÚœ¨—1ç™'¥>+Œ=I™¾” ÁùçÐf!¡ìiŽÁüô8ï_Ê8S?=]&„‘+‘y“gRoN)eO[ØðìieBüÓsÿé¹R½ÿΞkD¿»JçÓú¢ 8Ï=Γ&³3JI=D9ª”CîãARÆ£3õCæ|öü(ƒ‡yµ[èxQq~¢r”)ƒ J¡Ã‚( ,ˆ@/dõBv¡lZ‹¿aÓºS=kÊ‚à˜ÛNe<öä”y&¡Ì3eA¨u8ÜR×ccžPßwλJ‰²ø æWóLŽ2¢,Æ-ǃSO÷LM¹gŸÖ¨ ïkÅù”Z`CðE©uø´jê%ïG=K9Ÿ«@ʃp¦|ZK¿+kêWU@=L©‡)óœ7Ñáž1?d5õÙó¥|ZKžO›I}è¹:ãþü»žû?é·ÿ®×â£üõX®¿–í­¬§²^ªÛGÅzïÌúçÚ3ÿ]¯ü«>©Û#ÿ·ý‘뉬r}O·ßý«>÷w=N··éú³>BŸ%÷ÿÃ%Ç£¢^¡RêÊyºSžœú„J)_žò%8ÿcwTfeÞC¯å„=KQ†×ÈqlÜÿÂÔ‰ú sÌ0wT&Ê’úè‰)§Ö©¶–UkO½ó”™­FYs¼ Êó£L0ŽQk‚‰èB9ÙÖ˜~y?v êÇÎØ´ Ê à|Œó÷ Ðaéòi9çí©FYSoO΃ã,r Ö”?«¦ó\}qþ§½öïÆ_Õ_ÿ§½•½¸žúïú)ó9ÖíŸ×;uûæ_õL®_²^©ëqÌõDz½‘õÅÿɘëßõA©^é¾§ÛóX¿û«>÷¯Æ_\o Ðûûž¦ÛÏX/ãžÇsU D<ÏUM=Ûýi¢8PÏve}9Pçc,E©¹õ;níHY†%¨ËÑ)@9Pvk1åQ()ûKŠRsÌlêcÌ1ÀþŠ×ê€É'G™`RrÜŽ=IM¹Å”ÍÊq)L11Ý) Û´eO}×-©ï:c²rL Æã¼ÖÅ4y9þ*Ç^-Òa¯Š1‘ÌyöǸv§žÅƒ¢å€‰-Géøû–ñ,ÖeP˜vâù[•c‚eZj7œwºiWž &¡~é"ê—®¦&àÌ#Ý…²lD:,›öD1ʉz›P¯b#,&gáÆeÿŒË¤zÿã2 úÙÕzïE:üDîI(/Œ1Õ(1v -n å…ér(ì)/•ãPpŒjŽáS¬Ã +êÌóÂ8^ª)6” e ÁUd¥å¥QøT1Ê EåP¸S^*Ç¡¢ PöØ<¨Ÿ»6‘(”õƒWP^*ã…Ybcñ£ Ê¡( ¾îœo{õˆÿ§çþÓs¥zÿ=×’þwFú<ÓL‰²0àY±(1&sʨÏþQ ,Ê0Í8&†&¹/ª%Æd¤ ïŒRýŸQ—ûS„r¢Œ3® 8F† e…á‹*าX þ¨"”åéQƬª gÖ ‹' eJY*”%’/ª%Æ‚ @Pnc¡IPQ(™”²©Å?UŒrÀ¢óC©)Ÿ1 )Ï£u.ÃÒ`\4TÊžò4ãÔZPNm1e¤q¬Ún?‹6®å3r|j)J…²¤l"”tʋڕI¹µ/­å„EU†¯!‚wG©t8 nše­Ãþ‘ ¢P"l R”’²8FµŠ²|Q”¥@ùóVŽ2ÅÆáNÙ?:ìƨ.ÒáppME‚R L±¹¸ ”(l2.(%åb¸üÓsÿé¹zÿ½=ך~¶=ž‹@“Ø¥@™b2KQj”%&µ?Mlç2¼5.É%(åIJQj¦¤ &¾;*%Æ E A)Q–X ¾¨L”‹"†„²¼M±@\(cÒ Å—2qÅÃÄõC£°ˆ)ß›cO*)ãÛr¾9Öš;e­qL\Ê dÌoŽCéŽÊDYRÖZʋϥF‰ÿ‚µÆ1q9Öš%ÂÂôÕa­1&®3JEYkÎ(%J„E+E©Qb,^Ê3b|J#,dgÊ[cŒÊ”=¶œòÖœ)oÍ‹ÜUDyk´à9vx-|Æ[+îÌóÖ8.®ˆã¡2Qbl¨b+-·e "6 JQÞÇŵçã¨"”&Y m Œ[d‚Ä¥¤\\Æ[³ÆÆâ*îË3È(מò‹ŠPöÀ×9÷çïz.ë·ÿª×²[¶¿þ«ÞªÛWËîùêöRÝ>ÚC¯|ÿëiû%×+¹>Éz![Säz ëºkЬç±ÞÆõ0®ý]ïÒí[lŸöïú×þn–ë=¬ßœ¡½%„~9w_õy–™ eiÀ³híñÁ¢$øp”(Ë2 3%·w=Ø>0T&e32.£eÑU\gÐqÜ2T&·OUŒ5€;+B¹eF”?«ËžUpgòðg¢¬±øqã/V™‚ÛhÄs›ò\2KL_3žG€r ,2Ž)k‰µëÏãhÍó«‹¹ù'w6“BMy±þôe+A‰0IÜQJî\'ž'V€c Œ0iœQ +ž;]D9bÖë( %ÂDÊ´Ör¥)ç‹ãs±³”ÿÿ6ùg òß=±§×.Ò㙯(#L`”%ÂDöE ¬1¡PF˜Ô.¨L”5&wÊÜ¥¤LD_TÑ“^ŠR£ì1ùQFXÎ(Ê Á¥FÙcAÈQFXÎ5µœkwÊG´Ä"ñ£¼Wû¿á½úÓâqBÉQ¦õyV¢Š²9þµ)”¥¦¬×Z\[I¹‰[²Æb“¢(S,:)ªeÅ'Ga:£f<ëÕ¥BY`Aú¡ P-´¬WT&ʋԥBY`±ú¢ PöX´´pOÑ Ø¥Òa*¡° £P&”÷š‰²ÆâöG£œ(ïÕ ]‚R Œ¸µ5”œ¾3å½Z`¢Ô({l´0Þ+×Pr”6g”eM—ò^8~6ªå„MCŽ2ÂÆáŒR¢L±¸ T”÷ê‡*@‰±¡ÐÉ Ç` ¤¼WÊ{-F9_3ÜÖkYŸÕí±ÿOöWÖWYOý¿=¯ëñ½ôßõÑÿ¤wþ»¾ù¿í—¬WêöI®GþŸöFݾÈzbÙñÐÒÿXï+»OÊÝ«(îYcòd¢¬1ŠPØßäܹ[L&Ê*€¾¼Q*”%&˜ª倉&G™`²¹£Ô({Ê´6ÁäsA©P–˜„þ4M0ÝQ–Üþ(Ê2UÌïÀÄŒB™ü óU‚R¢Dó¥F‰¹=ST1ÊØ8;ÓI¬DY`"û¡ŠPö˜Ð4©í1©å(Llw” e îgÆó_0Ñ‹PN˜ìr”i -ÿÕ{V7öÂ"(âÎÐrg×P&:|j , _T&Ê ÄŸ<$(S,)å¿ Œ°hÜo°p,°p|Q™(;J‰²¤ì×"”=T w¶§¡”( ,.Ê”²¨-±ÈüP”kŠÅ¦èÉ3§ý¸½Tæt1w‹ONù­»Õ‹Ð{;Šså–þ³ÎôÏOï¿wŒçDcsÿXŽ2Á$vG©P˜Ì~¨"”“:e‚‰íŽR£Ä˜à(Lr” eÉî‡*B9`ÒG¡D˜ø¾¨”€e‚Eà‚ÊD‰±üQ(,Š(”IÍÒ l)ªe…â*B9`ÁD¡L±h¤(5JŒÅ@9ØTJÄí(Û Ê—2±EXX¾¨”=X ʈ²±U(KÊÇ.@‰±è|QJ”‹ÏU„rÀ"ŒB™`!º ”( ÞT&Ê ÓU„r•£L±HÝQj”%«;*e‰Eë‡*B9`ñ¢Œ°€QJ”)²;*%Æ‚@£œ°°(S,nw”%Æ" ….AÉQ&Ü9”’;Ì­-¡¢PFØ\P*”%6_TÊ‚e„MÁ…2âöPQQ(l.(Ê›…ªå„Mß6 * e‚ Ä¥B‰z—fdû£ŠPöØXQFØ\œQrÚdœPQ´Ù8_;ÜÖs¹^ûÿ‹ñë¥ÿ7ÆtÿIïü»žùÿí1nOÔí‡ÿÓ^øŸô@ÝþÇzßÚ÷ÊŽñ$(÷<1qĘ8NØ×¢¸³¼Ø×2QbL¤@”ö4T&Êšû }‰;¡¢P¦˜hRTÊ{šeÊý•‰²Æ @ašbJQÖ˜ˆþܹ]šŒ¦˜ŒR”eIˆ2ÂÄtF©P˜ RîüÊ5€&«'LX9ʓ֥BYrgàPÅ(Lb9ʹå€É…2Å„–¢2¹s¹˜ØN˜ØÅ( &wJ„½É¥F‰±7rc:Lúbî,.weŠ=É¥BYb!ø¡Ô(1DÊû‘3JĉC‰±H¹õo,%ÊûP&Ê{·Þ†Å#E©PÖXDþ¨b”“eŠåŽR¡,±°DXXRT&Ê ÌUŒrÂB‹B‰°Ø”( ,8)Ê‹Î%ÆÂ  Å'AE¡L°]P*”%·^‡aŸ‘¢8·#_à÷è™: =S'¢¿}âÎvXÐßSqçEØ:ºno  ãG:†ÑßQ©é9‘@³y àÏ8ÐßP(?GÂþ{ðÿΞîsºóNš?Gé–+ë°(¡žBð+hñtt»ü*Aðýíµïd»Öé„ùèKíkyc‚àc¹õùñžÈÈlØvÖ>Ú ß­.‰½˜0_<î:x*Z8'µR/¡ùC˜0í#|ï“N ªÞ鸭“#ðœùãÁW-)ãÃóÌæåpŸ# RµÜÁR>__cGÙPAªí«ë¤7ö!œ]ôfñΑéäò2ä!p€ÃJäÊHX^׎ߗ>ìož_¢åò>ÇÀûdó¾"j¼NªiÈé‹UäP­Üf¤áæËƾMM'ó±_™ÍO'—zçn)Y948‘ã2ò¶}ÍÔ±Ï\ˆß».§Jb x ü÷/9{´ òåÓ¹õî<„œVAw[,O'›’Š~´©ÓüÕé•÷¸ÉˆxcKû³™Ëˆ~Ôé!+Vêkþ½ÿý”Š]T‘¤Ýøê19ïÂÅ.^'*z¥“N 6/ò'ÜfÙÎp‘æ-bjY.øz2ßWö|˜O’®ß˜ãŸÍ¤ç­ž#N4Í„ç–#­,¶¤“uk¬°¾0ä;ŸnÚRFîûÔd÷ûå‚OÐÀ؃ϲ¿ þÍÌŸ„÷›á¹SRŒ¿2ÞzPø`ù¶{òÞ%Ã2ÁiíBëU‡1 d=LrÆÀòMOvöüBªŸ›Qì뿜´›T7Åxžðü¼¶¥¸î¯ÍÆÕ»ºŽP_Aw½ƒVf‚ž[·ù2Y:Ii÷ªAö¤qPµu‰EaR17»±Œ˜™+~l©[xŸ­¿‡&O1^÷òwF*ˆÅ;÷½y§2¡¦}Lô±X¼¿×—5¯=R·T›Bü8»ãÆËÊñ˜w‡‹·+²æË©ÉOŒÿ*ƒ3„SP¿ÊL˜ñåÍ´½YédøÞ-·qдßLÕbØÿu~âÀe„qÔ™¬ò`U×R¾|z' ÅÎ{o·9f¯ }5€âG·øÑïÓ‰U·£–¾ —Âj >B²z[g1Õ>¯QOL—Ìé¤å0_AvŸ5ùŠñ[Ün—«ì« ·gÌWîþZÞ |jð€XŒ½^ÏýñhØ#;äu)9„\¸Õ­šY­åøÁŒÿR–k¨Ëãu¶ôO¼æÓAAºÝŒu:7ý8sx«ÚȽ›ö4.—Þ ‘¬¨!#z;ûyv«åË0? æGÅûsÕçóãnîÖÎ%¼º‚hp£[A;ÇÇš= -»._ÚÆôòl9ÐWFÖÿ8ÐkjýBßaþ|̧Ú(tj–‹Sißm)ÆwX:(yBÌHÈSÆÑÖõÓ (ßp‡%Ä+š¼=d®¿`A4ú”gѬ/æÏðOŠõ‚å.¯ü?m!}.tÖ§»–[É|y>ìW›§NÄ—¬nDýŽy_xÆOì²)¶_4©{ði=ßãY`x´—Ï’ÿ€âåæZ1§CÈBûœY¯¸ yÄîÓ‘OÇ.4¿þ¦wZŒq+ìZú¾Ñç›ä✅޻UYÐjQ§·[G= 67Žuuj3&Ùù8¿ˆ!§'>²yÖQ—÷{Óò 5ù plɬ#ŽÝ2J²`2Gg—< 9M|VV C2ò Ú=¡þÝ«¿±ïV4ÿ§å<2þS)sŒÿ^é´¸dúMòÕ¨û€‹ÙðcZÁåW®Hœã*󮌇Ƶ¿9ýP…_o‹A«ŽËKþ>ü²a~ºœßŒ¿¼öÐéF7Éh H:Þx6{¿ó¥ÚÛ1æN„â@2mÆ­’>heo¿/®‚)ã§>·k­~hoUŠs¤À¸'䱆 çn ¾Ö3ˆáˆý½Î? íó$‹&‚ùž‚9uCÈÈÈúsO[QŽÁßo“õWŸ_×ÙÊáÇÝ •9¬Ú©lÈ» õ=xX›oýªg5AFºöÌó}7ÑM—°úà9óŸ…ñóç[ÌDøüÆëô¼µ¥àá5ò€Ã…OË×­› ÿþ€\Éåµ°¾ L<ûyxç.2bå7ïàRo-‡œqiyóŽ¥y[w÷[/è>í ÞÙäUµí9囤ßF/ƒ¶M¶m¿­ä{sFí!¤q@·t‹V«Ëq øÏ_ú«Z ã M^cü¦J_\%Ûò õ|¯çÀ¯¸Eñ2Hæèmn=÷‡'Én„&„Ð÷jÂü KûkùÕŒ·Ís^©#^gã4ŸÚ'ú\%S/̳µxŸµ ý{ÕÉ æÝ¼×wéÈïßìÖŽ}!äv‡ˆ‡+zõÉúÊê [¾ü2û*ø 1n%ß×y®•¯siJÞ/ÏG‘¤v‹*E‰C·÷…7Ï .þÎ~ëÎ;€²õ÷Zdfét!kɺ¥åø0Þæ!äóåfþ‘ºœvÆmkcÓ¼ÏÒ+Dßô~ÀIŸÇЫy׆fñßZ4Mw?5ÄWy¹äùQî°Ðƒ|Y½íöçb:^éq”püïã*Æx)“ûÞx©¸Lä_Zé" KÚL^â1#ƒdXE§»>™Ýœ9§Î2èPu«xåjr ç$ô3¸ü{¢'åRN ÆÍžÏÙ^&“Ž=õô1<\á»$Ø#ƒôžÑ´E|e'ÊU!ËzM[[0du¹ñçä?ÅS®Œ©N%^“åŽ=çœóñ¥ÜýåÒp\$ÑLkLs¡éÐ’eÕöd-3G(Œ‡M€L»C³õ!Ä#wGŶ«…çÆò¿ßÀûÅ—/`üâŸ\bG³Ù-? Ωuɇ¤hà$×_3ƺ&üéçBÖ\4®¾ºœ¯=óÕd}qMu9‘ ¼Ní‘U§}®AºLŸ&õvÍ…gÊ«†íof{‡_¦+އ­ãßUœBÞy,šÔÞ£†Õ-ãljòãn|=:ªÙÆK$'óý—c¹Ð¤_ËË’{dhÞ¤œîGAO ˜ïOÏúu+Íñ(ç{Ü®p¼Ê{>ŸOãøs³åšFßÂÉÓ‡÷ßÎ…Jµëœ ê#<ú¿k1|t§¿ûžB™è!øP–íƒ|ÿæ98"Œûì‹dŒ—W8Y°eŠòe.TùÜi}FA± r‹jÚhHß%zkBv®8âšä)|æ³Êî/ã˜èòcÅÿTJÓØ{uÃÉõj^U2΃¬œ~Ïû}Ê kÔíF‰Þ „Žš‰U0yîßáXy ~¨ìýȸ1ºï ÆÝæ;óc%ÿ0rƒÃ·Îƒ/Õ6å}üA&ôšX³ÂâÁg0=%äa0Énxîrºíšr}ƒñ*™ÿ£®_§ãW:zvv¯*adBs.3óàCŸƒŸ2Œ~Ü1×Ú¾~ØÝ`ÂóæÖ}œÕÑä—Vú¶ú#Ì'ø:jÀç7Æß³êô®ïR9á(Ú¡ó W…®ÃÎ7zHª·ux02w4ôªqcÒ»¤`R»øö}ŸškÿNæÏî{©¾qc=N÷%²Ï|>ºåÁר„%·:>$Ó®®÷VßÑu.M«ö<˜X“§l׾ͷ÷íé.ûi£ëÇ«ÆxQò%7úXɈ×àUº̃QÝcc& zHâìÅFL~üLêN[7ÖÌ^{Ÿ¯ìjm±+J_¨k]£Þ™Bñö¹s~ú] &}o•¼Î‰ÈƒÏ{"ûÆI’7" ¾+§€ÝÑ‘áðsnÛ^gÎÞBÞõ^ý&fÁæB~0ÎÛ§• KÒªóëP"Œv!"USš»´RåAôBÕÁÜ5Iîg÷…­§CÁÍ|;¤ y]zýFÏ_Œ³ÛxVUÛÉóËÞ»~¼ÎƒÖ# gH°¸±Â§AüõWý ¦ãÁÕåÆ{ªC3NÖöžåý½©?Ưé3"#ps ybا¤j5Ô9ÜêÀ¾ëÉå„©ûǵ˜SžWÙp ûÿ‡GÜÄÛ]ç±>1úé¾¥‹:|£÷¡ õs­Çç1Æ_VáBÝ>ΓĹ5†t¬«†—“¬H}H†, \ðd,<8xöØa¯Ö|w/kq2|òïôó |7>ÏùyqÆwq¦AäϳdÒ 'Ómðó_¯µìÞÓ‡ÄLω÷‡Ãê•;õ½!SîF=yè%ŒxÿnžO¨À8ßçXje|–¼¨4 éxk5ÔZ½Ö~eÑCÓ™[ᲃ‚w:¾B†t^ð-Hϧï­[²º`óGÝy¯s§[ÚÍ‘…§‰öÍœ6ƒÕÐÒ°ó«Üï˜w;¬« ]e gëå9/ !šô߸ŸôoóŽ­?ðómžC«wß[¿4h}Šd5æV@Õ ËòË23Ì$ƒkܽ°½{àç¿8ÜᘻS*ä ã¡kòãÌÙù|êéIB¤Ó¢VÌPøù5·TË$ÝÃU+ä§AhËCíLCÈ{®={K…y(ËcÆçcótÝþ)Æø¿¿_–\è@JŒµ_¿@ g7ŸÄ&ÙvñÚ©÷ñ#áÓ*ŽdB†/÷ÈÍN*Ôã73n%?NâóM‚qgÖ|;«"9Fds}ð^¦†W»TYÞ>“|mYxñùб°r¯i…ñ5Bˆ"É­¨Ne©ðÙ}æ×;ÞÒïÁ¿§¤·‰yè½.¢#dé¬K-¸©!Pt8½½8“\/Zÿé¬qhÍ5Ì`bwceÉÛpŸrëŒßÊòC÷s`ü­îñ¶ö'^‡÷68$Uòã?o|—I¼Bb«›ÇxÖZŒýþiƒiC>Nñ!Œ"ðŽ)w†qD4yqkÖøÚzÓ³}ÑOwªá²þ^ûE™dÒÓCÙ¦ãAĆê£BƒÉäÕñž½_#Üæ{Í|šŸP“Ç÷‘­¹lÖ”Ý$‹Ã­žPCØ¡yæë2‰oë/Æònã Qn𭟃‰OVàÈÊák?çý£õÎйÕÌ~­¶“ÝžœS¼ò&.“XÈ$íšØ|,Xx.˜lôœ“{ñµ¶.xn»!Ÿ¯‡ñ`ò\Ì®v¥†QÃ3FÜË$?üô ô ï ?LìK$ê«ß¤çé¯ÞÿW%û÷÷jˆ›a6¶cf&Ù@®m¬RÒ†-záL¸j^_g­ð½Ù{å)›èr†?ó²Å%ÛÖóÁ¹â¦à%_Ôðû–bÅûüL²#ÿguèp57x¾ÁÄqiõEû%k˽‡|®§ªkóâ¯x¼"¼ŽÈÿôÝ÷‹·‚Þîä?>D¬êül«—Û¾eÕ#˜ÄN³ô1Õ^ÇuÝîïZ~æº}MŒq‡¾“oIšâ-5` |<#ùÊçfHý/ÍMÀjbŽÏìÞÁäÙ¦¼WÞH…¾Ã8@ºãV Æ[ÿX¾xôܽ0ñÌ7i›|hµsµßÖÈx1ízÍÉàwc…Å€qÁds~Ë™C[­êšÝ–Çl–çò¼{)Æ—ÜõY+Žº¹s>ü‚'ás‘¹ž6uë·ž ÆîUWŸLÎôX¶œ `yXEà*3~Œ.$ãN xÜôî+¿G¤Z·×ž ›Þ+Ÿ_ &m“_,3Mò!lÿ‘åGðÜþ=¯Æ¸s–D×z|é8ü\y¾Ï¨¾ùpûüû׫‚‘ ûvÜLN™–Ÿ}SVT ú:Ë–ßšü ,Ÿ6Ü#_Úæ$|Úó'>¯_>lèaæýÛGãZµÙo¯<ç¹zcŽX_õ)7Owªÿ¡Þ:}áþ²q &o1þniÊóäüSàwê^΀þù0òÞ—>‘5~¾=Ùu¸õuKÚúñyº;Í*$p8ÄNò2–~&Ú-<òd™6Ùüëa½®1Ùëõ(ïµ0δ&1þÈŽù÷‹ÎÁ…bÒ²c>ìºrnë'ý,‚/ÇfzI°=z]·Y)Ádþãk7ϬÖ=®7é»´²üz 7ãr³Ô†ÖPq{§¥5&äüž¯]©“E:ô¬ítÏV½?,îN0QNÝÐ6áìÚrûŒwÆòŒïÿ”'‹ñ_/9^èwäÄžºeR>tì]&´Ï"&Wï«Ïnu‚V5‰¯e“=oj-Øä¨í?,Oøý¬¯6l¼ª»/¡Æø÷|SsÕUƒ`o÷Âæ¦æC“#î3»Øe‘e]×Þ= v^Yöïûìž]¤hÇ­,Ù>‚&¯/ŠÍ5 ¬`8^µåÍ@Œ·mBľ͒,¢Ù698šº ­üúÎ÷öÄ¡ ´Üçeû6¹z¢0ŸÏW³M›) ‡ãºå6]óÌ"í'-Ýê5 ݺóåý`Âóp¤ÂüÖ4ë=ìúc3Õô•ÝÛ5æÀsêøõ`1Æí¿«¸ÞB9ÔTy÷º„q7Û|ë×þ,reÙƒMé%N0åbjý‡ÅÁäâ¢MÝÚißû,ŸÙz'Ûï(•×ßçTøŒsÂÀþÜ€ó/1¾Ï–´3KC²G¹jà?æœ7K·3!é‡<í‹s}Ê­g1Ξ&1Þ¾aÜH44Ç:¦åÃÅW.Qd‘P‡È6{¾†:í¬oˆZ„—ç¶ïüÝG_±Ïݼuß_~µ+ ûŒÚïø³¨š×xÝUï©ß7¸¬^g@F•fûS²ÈzÕ¶VÛìF€Wõ[Ëö !Kçl¹gµõåï”?_ÖíäÀ‘}…}gM>sÏÑã‡Ñ¯üK0ýp Å+¼ƒwºV”›Eøþm‡?ØßèB 6öi0Èc­ðùY~<ü<®å¼÷-Å©Vc| þõJô‰»hë?9YÞîÞñe¹Q”Ü$²Ül\Õx½UÀ-cw\'¡ÉkŒ?âÑ«Ê+w_ƒÙ­×áýÐ`Dëd“;’š=?˜ ‡xýë‡ûÎ !ÍDøI× ïEÆñ«ö²Ñ\ç¾]€çñ/Æ}ö5±MûMסޘ5cÞŽÍËé†w[f û&~zŠóÿ”¾@[Zn\ðfîëçéÑy =ïqk|¨9Ùã|{yªùÓÑù°ºpw¥+ݳ‰êßLéÀ±0w]þ”ÛÕBÈËío«GKËÍ×ÞÍãBÐqmW!5yT(¶\!k=ê&„š?[ß»P’zÊfH69WçÅGxÿܦ—±I»ê‡íÔ3RÂxfü{v ´ì¸ÿJË£=ø|ÆxG·:šN6І“ó·ìO±Ï‡Ó þøºLÍ&NóLoäïEõ !»vˆoȦ—¿|ÿ7~9ˆŽÿ­ù|ÆøûgäT? Cm¯ì6(f ©¹Ë%› Ž´9RµöhpÙ߯dÕÖz>KÛ¯ùquex5}OBÝ–pu‘ë½QË(¯ ãÖlw·ÍlÛzZíúe›r–X?ñÉ&õŸw / z%mÇzBp0–wÒBûôÞ´¶Ø¸É¶_Äö»4ùŒq{øuêRW¶ëžÜõï“vG|[FlÍ&«ëX ÊòêAßµî*#çüž;óš´\¿ãï÷Ga¿§ ãoj\ô}ƾþ~ø¹>¿äVvdw6‰·i¹(­«5hp{ýd¸ŸõiþÜÛ7aþÃê„ç­òóyÆ9wïô“? ðyÔoµ“E>h8|³ÉÀ«kªþúؾ<·vR5y:Voû<•”°÷7«GžCkÏ¿|ìä` E®öIšßŸç4Ýifêé ÍòÁiI ¯°£ÙÄõmU|ô„)AýÞl4)w<&_*Ô;ÿ¹¿ó[v¾F—G®ŒñM[œ}y \Sü>XÔÃz÷úc9öL6iµÙ¦F¯E¶°¸QöBÛ÷!deÂéÆ3F¯-wþˆ­¿³ù ¿oÏÇa|ž_7îHyR-æD[¼ˆ“eþþ†ïÙÇEaCÈ•AžªSµóÜÖÝþ¯aL×çÚ•Ú‡cÜ™×L‚ &Nïé­?ç·{²Ÿ×Ús=›|mi7Û)n„åÖ;‡ŸûøÔè/3njû «'þýøË†ñ#uÇñŒßy¤WäiëÛ i³?Ôðöz‹â¼„l’ŸûzR—ÚÀ¶ÖXÉÈò’Öu[¨µó~½µÈfgXiëÆv¦|Jz>ã†oZr8ø6äÊž3*RCБî’ìlòeg•—Uz;ÀˆŽ“_oÛ #J‹û7š¬*?¯9Òr“»Oƒ·¾Ôy=Œo¾Òà°QãXè•¶ám›§jè±àþª…Ù¤ÍKÝøò…B ^FÆrËvéÚý6^»ÐvÉÚ UÛ°÷°.ïQñœ5QšïŒîtKý 5T}—÷5›ˆžUÚqÑÅÏè~@.#ü8Å›ðœßj7›­/iòãqÔÕ›?b¡}=GÇß j˜¬òœe¤—CV?âæ%²¤缌.ʈÓm2}¼…þÍê§ ÉÁ¤j«jÐõìvÂüF“ß!…âѹwSšLŒ£çDÔÔáØߊ9äÈ9×qvS-At¿SÞü¼¢££ší[Cøõaca>ÃÏ›D´nøýyÆå9ÐqÐú÷ͺÍ"Ô¹¡y[£²)59âÒq °žûh¯BF"šÿIÙhë’ÍÇ4yŒq:i&˜w `Û¦íƒ/¨áW«ŽÃÚUÉ!ûŒŽoïó^¶;÷9÷.AFZ©$c_Ž^[n|SØ}sdÿS…6lž«É_Œ{âb2ÝÍvÜQ5Y¾Ñí*~¾uoŸ dDf¿nÓ%QF>Ü$¶ž_Ö ÜY~]éƒÐ÷XÝñ}—_÷‘b|§gk†Ýº•5] ý W}Ý_9‡TËïœ#¯MÂ;?r—ÇɈ³©Z[fÝù³ û~ìs{rÛs-Á{OŸE-ºÒñ^çÁý#øÆ¼ ·×úu¬SƒÑÝ”['*åz^ƒ(èpɯß]+œ›žÙ(ßþ§Çwá{°u]N´ãÿyi9}šô.Ì{ðrAûUjhÚaLƒ<ŒÏö%ø­¯=7KF$ÍWÌqX¬­sVÆgÌœ%ðdžաîûGñ¿œZEŒ‹îBA§-—Z,PÀ‘¦|Äç˯OZÁÏ‹⎕BI§&Fñ­¥Â>¿Þf$¬Gêî×èÉ Å¤]ƒv&)a¤íš˧ªáç… ³MrÈiéé´ ù¶ÐN³àJ¾s†´Ÿ{©³¹QøwCáܵî9ÆMPèwœzK =&žnvj´jÝ3°W6Î!cÍÞ¶ºÛÆ©gdõ %nùùÌ•–[—¼*¹6Éz’vŸš½r>ýšºwG¾ãuæf•ìßß:îsÃ;5xž²ªÙ.‡\}سäÄH¸h¿1l£(”üÎáÀ®Ra½!|åµ~cv j÷¢–1üûR‚qkŒ Ý7Ç7ˆWøÌ¨Þjˆ ˜z¿yïrèæÝÏy¹0fúU ð¾°uv^”½÷KŽ_’ôR [m>•·ôâóãÔ3 ¿ö ôš¾H¬j©†–gâE©CsH¸úÑêUŽ0¨ZŸíC|B‰ïZ™·þMíý1:}zîDÛß6üùÒôœT?>Ï1î(Í@1¦]Íí1¶¹ÖnýüÆpRù4úõ©Êá`9xW^öçò÷ƒ­sYd\øØ¯¥­0ïÓä7Æ}Xgjƒ5. àt÷À³µÔ°˜4ÿþ~n¦ ^oQa0t|›>e‰¥œ^^_ëgí~{ÏóuT…ÎzGhò㼪eTl3]eº·$¦Ê™W\–CZ¦w^d!裾6sœðëÓÚ÷ûüüß¿…y;×Îï›ðëÁz¡…âÛ^K7J„YseæÁÒ³V¢ŒU9¤ß™Œ·cË>Ñåäíî„vÿLw?U„q"/\ÜcB"ÈŽYTÙ‘y­÷Ï\CäKf¬#Z¨ŽÛ?…’ëòNZn~Æ8™lÝŸWòçÄÿò¼/$Â’mVgïɃÙw¤»ûzåöÞ«æ~hÆéàPÒ5ÿàÕcµëh{öº‘±ÏX8OÄö·4yq¥«*I “€£€f¸äŸï†¡W¤9dÇÛ«JÿÀ¯ƒ†’AÓ2b ¦¯-wÎ÷˘ygöÝ©!Ì+ù~ÞøýU¾.¥x¸Æ‡—H’ Ëäðv1#ò ™¢Yã›CzkÔ,,ÉŒÿlö%”Œ2¬4äÄxí¸ªÇ.çóÛ®šðùŒq^T–}¬“_:åîÊëL©²7‡ð<ùÁ 9o"''æŠïµ–ðã_Bá×¥j ûÖìþéöW^ÇñIï;õ$Õԅ—Âjç÷©RNä~Ü8l¦,ÏiÛTN:=éÓåuœ´Üyþï 0¹ê%Õ½ù­Kï‹cüwr—™• »;H ®¼Ë…‚w«…ä£ÈÞû~=µ×þìåäõŸ&Ø´ó¶ÞZ->>6dðÏ™_WÓ“ŠÙ•%Ñ &»bsA³¿•CêZ¯áù~ w¸}E¾DN4mÖćxÿ’×kháù±s±º¼lÆýšæ“ñ«8œÝ/Uz” VÛ¯ßÂ÷Y“{¿ÔCÝ׎ßï–“›Mó#Ôv>åÖúíÉ ¸TI87ÏÎsjòãO}¢4Ümw.Ï·áÈö\ÈœÿgÚeé¼ä´þŠV`¤Y(‘s·Ìs;iï7[‡a÷ƒÏ_[ý:_ú÷!Œ?©æôÖ[ýîÁ¡«Šçass¡ IÜ“CØï>Öo½8ý•—œŒÐ46i¹óal߃Ò]o”b|“½.Íw©ïÁ÷z £”ýr;U} >‡ìy¥zµºgkr"ØTVC*'ÉýZy½_ªí#,_ž7nœXѽ=·Ô¦Ôs Àø‡:N¬—ßú>t¸öåp³º¹p9ּń¸RÈ…¯ß\ÈôXk³œ<û´õ|³ùÚýšè=‡;œ ÿFùâÍ鼡&Ÿç—?Ïä^WV?} +œw¿õ"9¤I%ŽøÝ‹î£Ë‰Óý9ceھſßhúŸã¸ÍiPMvõ>LO[¿nLÄc0I LǼ›Ý¼w…z“{v.·<"'šã Õ× ïG6îäoõц³ùÿ;?.Ö +óçÙT`ëx´Ó¶Ç/=圞C><ÿô>¼;á÷•ðþr#Hí¸ýþ¬ôïAz—ú}ãÏpInú»³ Ü:\b7ë1¬ë¾â­\–Còë ª±¨¨-qzr­Æ9 ¹ÿ}Ã-ÚçÇïj×Õøýó.ÂybM~c|Ò6ØÑa´ ôâ”{z=‡K_;¶Åzg$ ºñ¾iÏñ½Ùß0¾So³ãæJŒ}#!ë\º«ßvTäKZÕ9m´ùÖ§nEŒ_ß!ðü.…v]žå ÿ»•Oü9&Œ·ýcRâŒo*hܱ²Gà⫱û™aRyœpåéÇX{ò¨Åüž?ß/%üyãï¹»Rû2oàˆÏß-Z¦€ÏâߟOtϼkÁi{2sȪw›_mï5\«)§½/‘“÷%î¡“iç_ì9GánVlM÷ÍùsÇzá…âÂÉ/\'OêšÕlH>PyàÓg9ÄÿƹàaVƒáhw÷éû«†n2h²W»®ÏŸÏùm£nÏ~·Rë8"Œ«ß`vâÇ•)¡”ElOÏ,â3–ïsˆ[ʯŒOY¶õ|²YFã02غ£{Ê)a¿‡`¿Ó]‡³uæ¸î=þPÛlHètó”Þ×b|¯·•IwH”^ÚסMéÛ‘[òÓ®;±ç¿åôŒ—SÄ5„óÒ|ÿoüº)¿Ï!ÁëNrŸü*2L4çÙ1e÷§‰ßs> 1Ì‹T4«Ó6Œ,³þÔK¬í÷ì}Â~÷ÃΡ³ß»èö )^DzŽýã{)àÑ]ö}Fj6ïpù¾¿íÆhÕžE…¹^~|Ó²Ô¹Þ¼Ži•Ž>I„³ª†¶Âëœ.>–ˆß‡_WÆyï JW·Y†‘ OVþ^÷XÛ?غ ›/ñç{øõ"Ʈ۬ÛõwøùG_Ÿ^±8 zÙüyŸ¿um›¿5>c¹“za¤¶"ùI\Œö>ñŸ¯‚0ÎáßC=JñáÕÿòîÅžÃ&n²ÎˆË‚]!1·z•à8„Ž÷ô}ø-_F,×Ý~jšv~À~ߨ®ZUjwàÏ1ðÏWïR¡8tÊR‚¤Bûo—úï΂‡3?{U|LfØÔ¾?ÿyÔÿ. pý…ÇÑÃæÉ'_uÄݧ®3ç^à~©yLË×ÞüéÃ/;È\0÷í¾üVÝ~Çãžøñ¢þ}‰üÜY¼|Dï>ðá„ø݈݈úÿ¶Eûf%Ä–p,Oˆ/z½Â„ðT{¼/œâ}Ð&ŽD骶x?On «nˆ˜7Bå~”ÜM±'QnèÉýa¹¡×aÿk™ý¯YÞ ÕxCJn•—ØÉ£î„гZÝ%Î);RÞ M xyJ¼šb/ô%y·Xg?YÄžˆ2oIÙŠ·:Ëž².oIUy3”6Ëåf(9åf¹3àL$wµÍÛ{jé®&g"¹« +›-ivE¨›-rƒ¯Á;|EvšeØ'ón¨£ø«ånyžwËS¼Ë×ä]>銰x—¯Ã{R´JþÙûguÞæk*û|Þè«òžT–])eOJãÝІ²[Þ`?;°'EE”S¶[JìL¤ý–Ñníf'±íts’_fB¸k˼gªñ®_Ä»~ª'MúkmÞ¿’Û¦¡²mÚæm?éã–Û~»Òä–Ü7•X]vBVØ ™ã}?ÝÜrÃÙa„ºïg)ÛÁÍÓ*;"‹¼ñ§ ¸$ÞøÓØ%!i%eÕaw¤ÍŸ;|¤Ë6Ç jŽ÷œiçöœåÎyÓäž³;àM#Ÿm–w´’ûô}¶ûlCFÈK â=-ÚGm¡¯*{Zoj•ØãC;©.»¾-v§I§­Üt.ð¦³Æ[oly¼qoóÆ–ôPÖØE™gOz+[[5v„çx_S<á:oýʦsÀN =•ÒžäMgòø¤y{«Í> Ê'}©ÝLL=Lýûi½;Rçn­kGêXê×ÏÛ­Ô«#uªôH¨½I});R:$Ô^ü´NÜZ~Vö u Ú}ƒ½7è©•}7R× öÜ`Ç©ý&»m°×d—©=–âÿþòÉ_kÎ òÔFŠã†\ŽÒqS@/… ­ìóÉ]yé¶‘»òäpL£‡\Ðf§vdñ 'q¨ :;+¤§VõÙD» _£ºÑG›Ë5Þ\Î)¾lòd»ìÆŽqË8ŒÝibƒ¯ª‹Må;ÀŠŠ,R¼´ÒÿEnÚî¾ý}Ð<{hÔ}PCÙ€¯²ƒ¦q ð}¥Ø÷e Êì¼¶y´ÀNÅo½§qðcö\{ì™É°ñN~™4ûeÈ£¨º­;Àf—m*ì´öxǽÆ{Ê¥Ñû±Ñû±Ä¶u?¦ñŸ7Jg-¹¼âí„‹Û#\Ü™Ÿ—ôÖfqèkÊiSÙ#í°G±ÂaÈ€·–ÉéÕº²Ij!$UJŽÝrtìRt”½e—»º»ìÔþÚÁÍÒûiƒ>àÍeéÕ›Ë1o.;ŠÛËQöK]v*fy“žœŠ9Åa›g9󼻬M»ËMÑ…ßKî.{~/ sÔ@JñØêì±m‚ BoÚY„¾R~„ Í›õ1ïœzì·Ùñ%]¶r{¹ÈÛË´aï‚X¼yÚáÍÓÆ^ÅïØgP$.ˆ…B©p©äyû4Á^Å€DÒN^Å;6äörÈŽ2ê §íå&È \ö{‘_ƒrL_j7SSvð§õ/åAú¼¨oñWµE׎ԯ²S?­?©;eovåH=9èóúS»‘zQv¢ìCõ^Lz¿òÞKv“è÷œÚoj·©½&;m¤.£«$Do©=õY¥'¶ô»VÇß…t¹ê¼÷.}> Å磡{šäôaÇí»KÜvÏà@xäc?vüØ8ÅI[zyÈ  ¥C~@¹wÌî é_%`^ñ]—Øu‘{•×,´…^¨ð7õ<¨ñ£Íã@ñ†9¼ÇB:Vk3„cÇáÍõ2o®×Ø«[‡]%Þ6¶f ‡9tRŠ¿ü9dÚmrUóÍÁàŽ:ùr2GôÔ䢠ÍâÜÓÈh öM«ûèúèýÓèýSbÛºJóŸ'N*yÈÚÛ µ;F8¨™ô¡æ@¤pðK¼çn!võØì¡&Ça„ Í>²¤ÄÀFHj¼éžgu’]Ô!»{ÈE 䱯ÂV\ÔYÅ‹š5"ÿ!û¨uÞ|AZqVÇ>ê4;+¤›Ì0§OŽ÷ß)˜yÅZPü>䤀ŽÀ–@ ]øÉb`°·Bõ“‘5@Sü¨iö£FÀ@È]ÐÂ^]Ã_dhäbMáø6°ÙËH^ê,;ʤ#µ4ÅKM›ñheQáÍøJ#p6’¨"` H<Ð6 ¥ ’(•ØK]Pö㥗šü@»+ÈKí°£LÝwôRGÀ@!yì'#o…üìl´›¿˜níåÿ»^Î$¶ôˆì‡ÌÒý#HŽíû5’ãú®Ù4»3àJ*þ (‚dtسQ‡P Äìªv@Kt'WutA.%üÝ{‚&H³W­ 2!rE¶Í¾ê.ÈÒû{ ‰°å@$é½>vr³¶À.¡»„¤³šœ‘MvV“32öU¨G^`#¨UÔñÿƒH"´²w­ÄÞ5òݺ  ¬}„­«ø×ȃ› R\B:‚î‚Øì¢ÐçØ©±³:ÝÛ²¿C#O.ˆR¨€îLáŒ$—Æ.!錔.!…áÈ¢8*ìʳ3RC‰”@ v ‘3Ò`g$9«m”KEqVWèÞd·G4AÅã‚6°ØY$¯ÙœÑûåÑûåĶÕËÿ~Û áí%§[g;áþöØii xݤ»7 áà; 6På¨nËh‚ »ÝBA8\ÐY„¤RJ4ØsI~ð&ûÈu*ƒÈNê»ÂsŠÃ7@C¸J» o89’\ЄÍ1ùú2è€ Âç)Ž7ÄÀR|Iy„²Á¾¤‚âò-*Þ$òއ À: –. #øˆ€ð@dÙ“Iec×›tú@Ÿ)|æäÌÌ $Ê ²(‹*F„ÎLr,•@ ,I™œçäöE¡Ô@ ¥R!ûÏ‹ IMö-Eì\rA4[8Ñ]ö!Y_>%ò.RIñ!•¹”ÈD]E_²›eËþ•ï3RçÊÏ}ÕÏ{ÕN¥.Ý?±ewRgRWÊ^´¢©÷¨ï¨ç¨Ûä½&õX>Ñ勵㾹b„.ù.w…|ßîV%÷ƒïÏ=À¦__:hsø7Æô½³.ri³k1%zùëôì^´é}7ùâ?4èÙŠIÂÛAn²ô,^¬üTáK²Óšü†û«³Ó„ÌÞG8ÀÒ8‹m:äd:^D´Až# ç»f 7`™=ÏI¼°EÑgÿs„»9bŸ!ù©ó ª8VCÅU%Q]CYÔ@…QÍ·¡q@Ø(’ è’k…ÒJ¥šì­.(ÄÀbuÌkÄÀfµ¦øÉeí%|ƒäT=ƒ”ú|îêÏíä­õñH]¼µþ<<øl–üŒzWö­ÚµJϪý*»UíÕ|bäNýßèÒ?§CGú³ºÙ·ïiM!ë ð,¨ðAÏ‚Ðpà‹  Ò8øè€Ð)„  # %zvXEDÀB8Ê  òIt¥B ³ë:6‚S`#@UQ“„ÿº #T‚4Âå²Û@ÈÊ ÂV)Â]åàY ºô:l^¶{ÆÞôHdî³dý‚és[þ1Ë–¾vêÂþµõùþ¼'ëþWŸýÙ7LJW~b?õ£¿9Å‹Þ`Žäe¶pý%‡øê5O›·ípåiÏ\™¥ƒ¯ˆ¾~tË¿ñ±ÎÔ ëöWì7q§Ÿ/«û´¢UœÓÿ}ï¾wøáåZª·ß!=/bgE\?ëÓÚĤ¯ËS×}o‚™Ý…¥æ;G¶üosuõŒ'føÉ ßÞó…›ê~rúõ‡½7ÁñÅÒ¤ž7PìcòŽ ®÷½ šMóéaóüEvfNÌé¿ÛÄÙ-Þ7èž{|×3¿_÷ç>¿ï鯞ì|Â7!vIS½}±»3^ìiàú/¯ ö°ùô^»ìÿ›kÍI÷Þráßãúbÿ|ª¹Ñ\zåG÷Ô}¡Õuö{ÿ‹÷S=ŽøûØYìiàúíÂé'ù«aóÅM·ž·¼±Ö|çw Ï(ÍiùÚ”›¯Z<ö³¹Ê9føÞº¿YkyÃPÏøbý¢½Æ­šhJߦºKãºbïwؼ¶•ùê?­5Ÿrñâî1-õÙcÏÞ77ÇÜ0ô»K~[­ûoüÇ•×ü³5ô ‘Ø_Kñ^–¾…÷%ñóVÛ ›»ìtÅq»ÖœýW'~kÒI-?¸ç³,:ÎW÷ÅþOŸ¨tÇ_;pAªçeTÏUâÑÖE³^s^š¸Ê|gþ]ïï¾bµ9Ñ8ã©ë[þó„óŸÚ…½ŠuÿŽ¥å»ÞSöDÅŽÕøÞn–ì5áOás‹ëOùõ†é¿Àë,v«W›Çÿ_¿{CËÿØ¡?Ø1>È\´÷³~ÿDÝ¿ñkK6{¬3°‹ºsÏ7²…7×½zÓm³OYe®¼ø[‡'®6=û5Ï»¹å_ú‡§ÎýÙÃ4ÎûÇÓÑ÷“7ýð²Öôþë±ûûëÓ{=¹SoR|ßbï7®K6é§®2ߨþØû¨(¶¥ïǘ#æ1#&Ä„j (fDÄ„3"ŠaÀ„3æ1c‚&ötȈ€" ˜fÌÿêé½{½÷yž÷ù½ïú¯u×q­ïòÜ{ÕMwUíÚ¡ëcrvNƒ,¨Ó’FxDøû|õpòv\‡:«Îêò<Ë›vw-ZT­—lµ 3ÞÑ®›œ“ mÉö¿Õ܃Ÿ7×<óˆŒì#ëîpjĵž¨jN6=Ýë4°HÇ]eóœhÞÑÞ8wÉÞô7é·¶#׿/ÕŠýyõ³êK·©r{Ø×·[´Gh8qÒ‚SØx÷S¸O¾_®‰w|ëX®o½¯ÃçïLPNô7ý俚;r”êYä]à—`< M'å7Áëlmsakx‡–½GþùêúWñ}­‡Ð>È|ß_ ^‡ï÷› -õäÕY½¨sî#Úk4ì‰Ü¶8>>œ Ò‚fVýÕ¯SÞf•ßÖ:ŸlúGV¬öéc×rýÛô®•HϧÎõ·n›I¹±™ÐæbÙú˜çÈêá§]«4 Š.(þNwù×}MAÆiz­þ‹k9L#™çlÉúM5¤uŸÿ¤h¿ÊÂG)cñ=lXøY?qA&øÌÞù3ø;ÖM¾ÌìMyô„×tt¼?Ux¡%ý9òßÿÞ í&Û®h7ë6ÚmöÆwW:¬N1‘ ùdðo5*½–ޯ䵳ÃIUmãç¿ù¿nZ¿E»æ7¿7ìò{ï>fž fÙtý±8ŸTiS€éFW¡¾2÷ž<}_È*²ñfVêæ…eB?;Öx™ošÿSþ9+Ñîç}vmX—y·;Qüm·Í6oÉ'×Þ<;[ëúxHë2L=¥8ü/¾ùÒ±mÚÇÿ¶áë"K›©õ_´Ûù×Õvvó3 JA÷•>J5hŸÛÉ|2Îi£»ùr'ÀânlýÒ°WõŠÔ•dó½økÑ* }_ËåÝë%Ò>vdÌ[=Ûïãz5ìjVht 2ŸÌíw·ç”‡cÀy`Q³áÍ#HûâÏ;︯ü‹Çúu²ñƒõKó[$x¾¿vp]Å F¨áÅÏ«Tùä^ÃÔ—6W‡CÜnŽ OfŽìXZo¥®ß®v~FyÞhgÍÉÑç‹2àÝÁ˜aUê©¡]ÿ£íîå¿dnã¶°rò»{­jFÛVï²¶­økÞ”õ$õO{7ƒÕbN­^§’á¦æÓߤÃóÂʧ3o¤Á‚ÓÏŽ>Âçè¸:"æZ>^p*;¶ºVíÿê]>©f;噯flÝ[ÉúÞÕp²ÞîÐnpßò•ÃQör†ÖS OÆa½UC@„Ìp…—¬‹]ªßÔPèÇ-î/E»g^\ªÚ!*é_ö®ò0«ü~Ñ¡€Ôܘøô“É˺>C»ÃÞ½3Ì]æ÷ÏbП}O~8"lžÊ¸ïZ¿ÆëXgµè[3NXO¶Û°!ìFº|p/ óÒÏ|ëSo4*h°ïÜåpÒ¹âô••vù ñ3þ™·Û×6ây íx·]pîöå€&NÒ›¤®( ƒ?É•Ös y óçö…kmmWüÅWaœ,–?~œ}—1rš¤|¿r¼ÎžZ\âVƒËsÓ ÛùIðÜlŒ´õÖ‚ÎÜ bÊ¡?(Ëì¹´•†Oâû+Ñî–ëó ¿ÊÕRÒtá†õIðè×éèUû È“ M®ŸÙÇo»ÊòÂÉA°ä KVþÕg·~Ó>°Êqq5hÿ[›¢‹©¡áâ î»;'Aø÷§ç/ üo oš}¹ˆãmYÑæÏ¾-VÒù{EaþÃúJkýú&ÎߟÏÛ›4X >íÊ}M­sC G£^›Ñ 2zìZtõz8:l¹©ëa]^b~È÷9®JùffåxU´ÏÑcøg.~Ùº‚0~²~ïåøÊh¸W‡µY.©p©½³ï¾Í*x]ôjË©_dÕ=‡YÎ06éþ·–ljõÌ“F{¯ü‹8IoÉä(ûêôU–”ãeëÝ*‘^±þñשà¡_²{H/¾ùl”S]Cº¾¿ôêäx¨ÑÒ!³óépòyEŠgnñ  ð;ÞOo× ÊPø}_ƒ_oæûfKð:S7yÎù“æÖÝ™\rVç;žŒ1Õ>?µHêvï[¿8N¸j¨‘R—Ÿy© ð¢ÄëR´»6Òósª{ ]½ ½2 ^IÚhHÂáÀîš"gxxïòæpâj[]}u¹oûÞ:]‡p_¡¢¿£ˆ[ç›Y¥À¹ö }šLº Cš¼Ye¡!¯úžTô]ŽšÐ*Ÿƒ¶.]þ7œñ¡¿¾\´jd–uÄõdˆ¨ýáÂÉzw¡iÁšKúhˆ×dƒü[ yÝÝÇ÷ø…Û÷{¤Ã^-ÞçÍÍ:¾\Eà4Šç#r´»¡Á “§'%ûÈú&ñw`ņžî Ð~ýÙ^n·¨áâNVÏsôI-?2«ûÈnV§ô…zÖ!ûÁwÇz´Ï3Úk=”3ð»Kœ´¯u?– _óªê Qül¾õƒ,ßÔ¤åÂQ8iyUËç/µ~ ÁŸ‹Ø?Сz<);–¸?¬²~çמ©!‰/—6È?8Î~~á›, '§« n³¤âr¸”ZÄŸ¿»¦“Ï÷ ‰°¸o÷Žƒ'ÅÃöÎ?óи_+uäë·F¬êÖ5œŒµÞ¿úãÈåÂ|€ß'¨$Ô7,^µþ‰v2éö½_"¬±ç@wñPrî©×®•RÙíý§yÇ\ár³¨u[„“Æü:}¹PNéšÛ¹Õù Â{á¹ÏŽÖ?Ѿß牧ÛF'@Ö€„'÷Rãà\ÊÉ÷7kˆf~j“×u&ÐùB8éðhÒì¿– ï=(jh¦Üý—ÙûÑú'Úý˜7À¹¥}¼¸’ë²qY”]^–á¬!¦ÈúºBï}qãM ËþÚGa| vÿ|Ÿýò|A%^gÔÛ¢Ÿ‘?UpÂuPÝ]½â ñàg=ןÒMž÷,œ<:Ç›Y€÷¿æË¸ëö_—þÕÿ|Âﲉ× ¡°æGÍŒåçh¼ˆ®mö1Žƒ‘F¥Ý ×VçN?˜ää s´ —px¸=Ît— ¿ã~jý3ªDúª^ý<#_lÈ«ÑKó!®Õz”;ÝxÞåú Ý~Q·ç©÷Åw;/Ž%Û·]]¡ð¼w'P¹ÅŸ½£ 烇adø~|ª$ðØ^ë§h¯}Ä}û¹]â@:Ÿ#LÄÀ÷MŽ/®YHzïmÒdÇîñ ¥‰§)ȃ^_œ·÷î“í“°÷ϸ—üz*Í»hŸ‹ŠÆ‘±°ôxëÖ=c@»Ù¨L_SÛÈlœ¾ÿ}«Ož‚œÜ©×}ØRa¼hRCmâXüņñ·Åów9Ú5ßV)loßXÐâÃjÇ€×Äìe3Í I·>~1 T®põðÕÁ•+Èú^óJ£Ì—þÅÕÏÜÜãÚáwÚøR¢½ÄúCo¦ßÌúÜ‚K4Ü}´ó¡…¤ìcËÖMâ\€çã*È­°Äu‰ÄGð1Uƒvb,{z_‚¸7°û59Ѱ³¤/1ë_Hî\ÛÚ%ÚT5ï|ž­Ý¼—yEúü5_ä9W?^«ÖO•XWØ“ê.EC°òâÍeç¢!zùNsé°B’’æ^uD_{¨hÆ‘d¤2lGkƒ¥¤Ï²W1s7|³×i´ãÚ1¢Á÷JÞ~ÃõÑßhåâQc IþÆ_Kê´¹KmKÉÿ°çÆÞ [Ôú#Ú;›ß(Çmv!Ñ«>úˆ{MGÈM›Ö~g˜‚tµ½Þ}Uá!ÿ1žóódz'ZIA‹¬9˜÷S´?%âÛæGQpjöòá#¢áùÓêW*x’à{KF¾ë›sÀ^á9]>ð«Ï™ëN™Ÿe¯>¯¢]~- ÒȤÏ/GED¾{tHVHd•šŽ¸78 ùͽ R1÷壿o—õó[>Bæ£%=ŸíM¹7|þÒ ý;\Z.½ ù¸šh¸W¯åÞ5Û Éqƒ³s'ÞŽíŽ}\§ Úm£%Âú¿Wk 7ŒÓ÷ùÁÏïõ¢K¤y÷ëÙ·é6ŒXwûæ¹Ñv|©¹æH!ñª¶ï[³“áöY˜ª N,qìÑß[X§öƒæ6­®ü¤/ì_ˆ×$h‚G ¾ ž¦ÉSZxEê…5Mò.’’ 7›½SOuÛ £ûìPívSŠ—ð>Ùs7!úŽ'gQþhG8ÙÍâ}D[>ŸIѾc#nçëhfr•n4´ h÷8õf!9R5<'i‚+´sßÛî°‚Ôµ™ ¯ïç%pµÊŸç¨lÞ]®Fû•juŠ8|ÚkAÄÑÐÿFÎeU!q²¿zä3»q½ìái<ìµíå¦^„Õ§æä/Z• Ü(þœH›rëÖ2´¿fÞ›âÕ×o€upÝæGƒ¢¡ÓäŽ5vd’EÓìFµ]8†-»¸öÍN)Ìèú¼áwOáù3^([÷ ¸pÆiÊ$~KŽvoNævz¯ÃÚvÇ:®ˆŸFsÎÞzTH¢T7ê½r€…›îÚ^ T¾V9Mwêž;‹SGŒÃÄxæZGû•JRB¾š_ó†zç"GÕë~‰~Ï ÉÞO[ÏE«b­Qw³C6{ ûåv™%Ÿ¬šÖÆiçÇ¿a¼Ÿ£ÝÅAÆO«»FÂÙo{R7ÌŽ†ï*öìú¦\¸˜3Ågô(ˆ þøÅÁWAvÙ=,«³DÈ_®­ÚEþÜg FCœ_¼é3xN“+ïç1%Òq#Nõð¹7ô›ÙcR4Xžì8òS!ÑŸºð‹çÑ0½ƒ»ë´™ ÒïK³Î½ßx ï‘­? •î™kæ=Ÿ-‰kÑAà§iýí§«1®ëž«0gQéºÇh0ʱ8rèG!ÑôÛ¼»É¬Q°¥Cçm‰#„Ûu¼¹Ô[àt±85Éöë›¹ç« ãŋץh¿ž—±÷åsW`ñöSF“죡lìãS+‘1® ›‡Ž^ Ç­_ÜKAìήm÷ñ›—·˜¿ðuL©ÍŸë3å¸lx˜èü¸ C—ᄾMÕ˜Ç>Կ±f È+ÉïRÝf¤Iº×ZAjçZ3{€—Pðû±mXü³}%­Ÿ£Ý­—7Œ÷¼‡ßž¹3<<'®P¾¯_DëeGذeÞ¹‚Xö‘~÷ñžËVAOBv^«H÷©,Ëñåhÿ¦­Û…En°$ñDã©c¢Ái»í“*-‹Hû¼Â™ýô\è< ÇùéW¾®ÎZü–ÕyÌïy>j—rÜw%^gm­k. ‡æÆÅ#à{6ñ6šܹˆ¤qÇ!\!jäêÏ­ldK)¯ÍR ~$ðñ´÷_ø÷ÜY8£õ´_©õ©Éfh,Ò%UFGCÜñe~E¤¬Ø]Qùâ¨õÑþáú1 ¢Ýþ<íIØ¼Š½o¶.¢õûØiؾÓËÃ8Î:º”uo×Å2&¸>SêTDô½¸Y˜{l†çÜÙ8ž6Îz!½ï%×=ë .\¢ö,"[Gø´\z×8š(ÈæùÏ¿Ÿ}ç)Ôì~·ÝNIß?÷‡ ã¡ñï…÷9Ú_ªc‡ÀäY9f/{EÃ!b¸ÎÀ·ˆøÏØÓéu—ÁP…;nW_Ax?óêb–'Ë?Ÿ >O¬DûC³GDå>; Õ¯šŒöé •9 ÞÊ"2*pQûÉÃ`ÇåÆ+“õ$yï…M» ü\Æc×·´gßôüî#/NWíaS zGCÕfíçÛ¯+")fïê¦LÚéZS‰"ýý>½óơݥíOÕˆþlÃ~~|æÏéÅ•HùyÜIØVe]ÿýƒ£aÜ&ßÁW·a¶ šh—î‘U¯SX)HJÂæʰÅÂ8ÁÏu|^4Ô¹ãfÔ+¼ˆ4ßÛàÜ…kΰxüº½ ²àÉöO¯x ÷Ïxyl]V{l"ŸŸÊÐî<ÛƒóÚ‚n?Æbqc“¤ýÊ[E¤~ãQ‹Æ—v⨠ZLaÁbáË#žþõÞ=o£'̛Ŝ?9Úÿ¶[ç&û¡¨©fhõ}X¿õ¯skÅ"œ/í0,ëá·ž+=×ÖPNÚÛ‹…ú‡Ë3º¥wíz¦ŠÀ“fë™ZŽcó´=Ìa×Ñ0ëûK´Ô"¢ˆžÔîÍHàW*È]|«ujêâ…?ùÆ‹ï/hÐnžüó„¾cv‚óåÞ© bpœ\\ûí©¬"b3¦•M´‘8nïy±“‚´Ù»ûfڻł]ޞ¸ÞÔ‹/‘j1¤Š­ôŽ“Ê">("AkMÜdßµYì4ëˆ÷“†M²Ü¶˜°uöû³}H­_£½O¼ntl»Œÿ8ªJ^4üâð™ùEdsjñÏF=á|Xhµ{Ӛό«"_,Ä_²âs“Ÿ÷jÂktn l?MëÏh·ÕoœBúAqýµýK¢¡vhÏ+-5E$Ôúg¸ë¹Ž°¾ØØª¶»‚¸yÕ:b8ÒSðg‡ì|îîÏU§°¥ëöyFûZüùÇY Ý6ý ‡özª,,"‰'”gòIgà¢¡Ž›‚Õ¬0K×ÕÅì»~|µ¡çÕùóC2´«Úg]Xe ɾeæ·61 os~ñÃÇEÄLl±Ïv¾<½;{˜‚$nô©ç°Jg·ûäÊÙm§TêV7‰ÏÈѾŸ¼žiÄ%¤»IÏ~·bà|§•n?+"w»¾þ|¹Yopñ~ÙÃÚLAü ?}ux1aûéì¹°}YÆSŸgP¢ýЋöý¼±†˜Çµz÷Þ;>TŽ^RDêF}’¬Ó'å ®„u·vÿÐã/ž-oYÈÎu°óŒZÿÆëœŒh [7“>}\³vÆ@©Œí®Ò"ÒÒéõ=“á°eÎi¿Ã÷Âï¿­ç°s‚ìy±ý$1—]ïN‰™;L¿ƒ¨Í»út-¶Oïq&¯ÖèYcyb¼×(ZšÙ©o\idÙìTí#gâÏwéx±›žú°Ù’÷´oç«.ÙM¸SamÇ€U­©Çü¾çBïÑú^ªãYý'µ#ñI™×ïöÞŸ «äüùŠW¬`ðå¦c—ÎäýHŠv/?»;ÅõÞ>²Þ5óÖ㺱°Ó­ÊX³g¹nþ¢ÁC×q0€3{7ŒtÒ‚p=þà¬~´ñ°²îíx؈֗ÝéùT¾žuCûÚåJ—'o+/…=¯§.QçBè„QcëÍ/µœ{<-ŒTÈëˆ3ÆÅB}Âòâ'‡Ù'wß©EçÍMÊŸ2´o•7dáȇÉ@î¤q,dô©â¸ëf.g³:} &‹¯¾ù*Œð|x]þbõŠÖßÑŽõý-5<#~U,,O™z1($®°¯ú³ïÐ.sÖQ¹äJ6-S¾óËrã¯팱˜^í¾Õ Ҳ虣¥^T—{Õ>œ wf½*]q~l|3>Û½·‚ôx“²(Ø]÷<ùúôðƒÖÑÞAkuµãO‘ øöLûÅA^Ÿ¡•?nÉ…%ýsÖÉBïvŸÕ6NXG;žŒÉà!Ô|þømïó´(¿}·DÚû~ü‚î²3D©Ïäã çƒ›öÕÖæ·«×XÙyþÈÅ^AÞîH÷¡‰î½”¯Ó¿¼j6ßÓú+ÚW*~|½êv–p«åãOÄAé¸õÉ¥ËsaýÀ63î÷“ºÏè7¬‚¨¹e°Êž„?'Sœžnñ\nöÝFçV¤Ù­ %“n¬î8(Îþ¸cé2"¶G½iعìÒëqeþÅ0ò½âòÂÔuº<ÊŸë/³aû?âu[9Úý¾«,Ïðs‘ì¯uøÈÒxø¼ cÕ•î¹°ßïµÍüm.Ãáœb\¹ïîüs¸®Nbçtùz´[¹u4%Úm©gõùÕuù^µm¯MâááÄ—ý¿4Î…!^3žî‹vÌn·ã\N‡‘é5.<.;æ)̳Øz ÿ]KOðÝ+i%áÏ«hÐîä!M¸KÎ÷9ù-5.^j4±r.ü´4mU{]/ #Û–5]¬ªª¯XÁÖ¯ùõßÀÎ hýY…ó w«­jAú°Äë]<Ô²YÓoñÛæ<î^•©c[iREAÚn¼¼tu4Ë3"O õZRž“4§ã-]ÿCûö ~ !¤tÁ·KÍÛßÝæ¶i˜Ÿ)‹¬Ãšv ÚòÖçŸÖol¿X8ïÁö‰f5Én¸´&páæb^î³íïÉ<²ÓkÚ%··ÙÈO3î€ßH£jÃÒs ®Õª‘~oANçVk*HÕZî/ª]ÕÍÇÙó)Åxãsá»¶rœn´yqœëŠj— N £ÏÝEL§æ 90z¬GÓÀt+¨tuÉ÷Ê rbb#R:ÌS˜²ûgçùóIĆ}_ȾÔú;^‡›uÄß¹L<»V½¿øÇ8¸Çî§}\VÓ¨Fb½3³^O×WwæFBO¡Nc³ù›Š¿÷’£}»¸ÝvÇ÷]!MµWïBÍZÒËòèàÏ×6‚®©kàïñóãÑðKç÷üþè'a½HüýŸíŽâŽ™Ì½J¼Ž½{ºç.„Ïö¾ðE™;³{ߺ¿»˜»<žß§¢‚p§Ì ޳yÍ{›.ƒ‚_½èþƒú‘R8¿Áò®ÖÿÑþÙèî5š›\#ÖéK õŸß…Ã;ù¡}~Ù¸¯HŸ–„‘o—7ýR'ëÖ1Y>`ßk°çÏö›Åë°z %Ò×oçoÏ÷¸F^í~øÙZÅ•#œƒ±á'’Âz€Çµã±._òóÁòßýHÐ~äÒšß2^#檴Á¡Upd샎!×s`C×m=»µ‚Iœûì #õIû ŽöÏsØy 6nòç“zÁȇ$"ãdw>ð:ãæ~–v°ˆ$ÑÍWýò+U߯U#r ËÂnU^‡»š/ #ïŽTJ2±Ô£ü÷úB};Ï.oú‹½ÀlH…ãnwxûnhŸóÒ[Û"ÉNéš‘‡‡$ÀxÛ„Ozgr zrƒî[ÙAðÆ–'Î#ó“_ìé!¬·3âÏ‹ ûôü<ˆ·/CûÁ?FÿzÓô:qoQáDÒÁx]¶)Ág˜µÊÜÈ$('Oþ) òWY^ês¸Q³ðda=Ÿ?oH×OÐþ¡ïçï®<îÃ$@v©ê9peDó 7GÜ1v–çÄy ;_ÀòFý\/›Â5ßü¹ú}Ú\ÿƒ×À±7ˆöØÒÐD¤¿ðͲ(Þòv°ÅhØ;=t@bI ÌÈ×T÷"l^?­Q¡Ý÷冼ߣÚÇLæOü}ƒðûj‰0èöºFæóÐ_Ž?}:nÀ°;•“n…õ÷¸è“£¤^ÂøÊç}ÁOØ>^¹s©‰˜×*qôM2óÔÐöÅŸA»/6)‡Ö¹C@VëÃÓ*9aÂ:Uùçü[8WÈü…Å­ø|€¯ÃŸ¯»Ißp^â”yƒC¢‚rà©m[M¶Ýܲ{ÀÖ—aäMɸÞROÂì3¿/›ç¼5ñý7~íÐ~Xv“TáŽ/ÜI‚Ù1OgçÍ©?âïXÃÓENýk(Hø™¦¿\ÐùÇ«Y/×ù¡—õø?ÿô¢ú§ÕB/*Ö[{fJ=]?µ¨ßx1í{*õ=åús¼i):¾UFû®(( Âö>5õô“Òž~e”5Ʊ ÊDl×RÊè‘‹‡ŒÑãB‡\?£‡1-hO eÓàb=Çãõ> ¦Œ2Æ‚0ñ¦Å|C3Êç)¦¼i1 ÂWÔ¯%ò˸ÞËA"D0e˜±žT\ïSŽÑÃzŸr=Ç)ãÐŒ2!Ô´'`0í hßZ×ÇŽŽ9ý"Æ!ë9.fôÈ)£‡ë9I{R¹ý Æ¡™ˆqÈ1§Q””ödÌiÆè õ—SFíȘ”3Ëõ 1!X¿@wÊ8´ ½O‹E½OE=©;­Œö`a=V8FÏ?9úŸ-ÓûÏÊÑô™¨ôt=±rD½©9&×30DÔ30„2híÐñC¨ó»ÑÞXÆ”Iy=¬G–í‘ÅÇë ¡ÂñzBhß@{Ê¡e5Æ…p£5®_–¯ˆ Á8jVPA¨bž£&§5ÖŸšqÔXß@9åõ¸Ð>ZƒVÌP³ LˆRÊ eýYÍ(¯§eGû³jhÖ`Ú#_Jûj•RÎ$Çôæúr\Ö7ëOD9j´okí³%§}¶\0À(c r‡ößqÔ”"ŽëO-æB„ФàF¹”Æ´oàŸ5 GãСŠQv´ïãÐ2.D¨?uåBXÑþ[¬Ÿ+ãöØÑž®À^ԃ˃rÔ¬hßÀRQß@®?5ã[2f—”ìD}¹¾Xœ/s¸üÌò²ïvÿ«üËr/¾.¡?àŸùõÏ\ÊåQ.‡²üù_åN–3ÿ«ÞöfŒÙ—ï¸<'ÎqÿU~c¹í_å3.—ýÙ»ï_å/.oq9‹ËQr½ŸŸÄ¹Iœ—þÌIÜ;âz˜rÌ0®?ŸsON%ž5›ƒ² =J‹)³‘c^ëó Ö ß—ö—Ñþ¤2Ú‹ãÎQþ×ï>ÄH×sTFY_A”ñÅ9†;å-Ò>öö”ëeL¹^wVÙPÇUd}D•”«Èõõ@çQ£Ì(WÖò¹þó¢¾ r:›R—åpÙÑ^Ì\ï<Q/fí›§åISæëÁìFy[¦´Ÿ¼elq7tV%í•çËõ_¦ìŽÕ"õ‹—ÓBÀ2¡Y?<;Ê‚ö ,h_QÏ» ê¤ê$½ÿ¼:É”þÎj”:vJSA×[™cxpLCeºÐÞÊœÓÛ£(t~w”’ö¦ç¸†JÚŸ>UÊq´0(´—(ן^Aû‰ºP®!(.”7ËøYŒãáNùY¦@2ǃñ³¤PÁ¨RžŸBùY¬¿2ãgåPÖv 8®ß¨e&bÍŠÙYV”áQFY³¾´÷(ëc_в§½G‹iïQ¹¨ÿhígϱ 9wYsžã‚â‚Øžòf9~–íGÊõ··ãúÒàvCE¢L1È}E¼ÙÇÏR‰øY¬¿²˜ã¡ wÛÐã_ð³¬Dü,Ž7Œ*EÙcòñfÇ#XÔ_YA9RL,A¢>¥\ßûR”=íUªG{•F¢L¹¢”Ÿ%¥lCŽî‚IHIû+3¶!×û>„² ¹¾¡ w"ëÊÅ+÷Gœ£ÿ]~æòñ•Åù•{Ä\^ýW9•˧\.çÑ—C¥z|þdyó_åÈ—Y^ç—Å9å¿ÿ/¹O¦W>ï‰sž8×ý™çÄ9NœÛX^û¯rš˜ö¯rËcjƒ{é‡È_¼íl† Ci*ñ V;ÊÂ.ÕçC¬÷»Ìg‰i('ƒëgL{º+h?cÆÇpÇ<#©Í³Å¬0¿S&†e€Îä!âóq½Û]D\ ùü= e1öžZÄÞóÀ|¡BYОÅÕ]ÄÙóõa/FÙ¡£†´áûôßñ/¸~Ŧ˜|EýŠi¿b+Ê¡–ҞŌa â_(1HÐé})ã”q/ÜQŒ{*§»Žáã!â÷˜Qvë©Bû{ $,({ åJûR®´†›ýS‡ýS‡éýçÕafôwÊAY¡c£ŠQvèà!ÔÉ9>P$åqléHÊ0uAE¢ŒÑù=P*ÚËc©P7UÆqÕ0("QÆ´§{$ʘöuçXA5xÎE¤ˆ§Æ8”§&Á q.OÍJŽ*3áyj ÊSó@©E<5 åK+hßww” e!b˜ŠYjRÊ¸à‚‘c˜ÊP9¢ðe( P9ío‚*㘎Oˆ-Çw ¡ÁËq.ž1íB9¦OMŠDûÂÛc`+PÜî(%J‚A.qLÿOM-â©•¢ìÿà\DRÎ…‡ˆ-éû/xjROã˜ÊQe(L Ç”q.8ÆtÊ¥;Ϥ䒊*Uв§ýâËP.˜h´g¼J‰’`Ò‘Qžšeq È ¥B™õã™–¥´g’1Š;å3^œeF¹Ä/Î (UÆåk/ÎBƒÊr‰9^œ/*GÄ‹+¦\óHŽmŽçR£¬DLb1+Î1„²Ï9&q JÃÕÔ˜Á48ÝP!¨2” ª‚« *eМç)qŒN þ3*RÂs:ÝZð\b.˜íPÁ¨R” v$ʃۥB™a¶Ñq‰ÿ/.GÄ‹+C¹`"A`2pG)Q¦˜|Q9”Ý)û¼8;/Žã‡ÐÄᆊq‰5(»n<ÓK&n”áiÀÕÎ(9ª å‚ &„&7T$ÊÏ¥B™aÒ ¤¼8{Ê^â¸ðî(5Ê‚²<9ö’‹ˆ½ä†R¢¸/8ÖwËÜ.7×àÝCÈ­Ü#¯I²5Hq®ç8–ÏÄ9Œå)./±Ü3Ÿæ/šGüi̳xß+Šå“ÿ&NY\r×vçÎ_pgß覵;7­É³Â\¸}J#ž3˱ÁèÓ¦èϲz<ûûE9¶¬ú­ºay¶!ãÊ$<—PÖŠgÀr¼.9wf€Û_D?ÐpkÖø^»ñì,¾73|_¦ø ,ðA£Ê¸9>0%·ÎŒÏ¿ÔæŸññŸññ?o|´£÷TÌý3:vªŒ[7G¬Ès¨=(/PR™çP«(KÕ¥BIÐùe¨”Ç Då ¤ !( w” %ÁÀð@©P ” eŠâAyªf02TÊ‚òT‹Q@Á4ˆ\P ”·Ç‡R  0¨<(OÕ ƒK†Ò ,0È‚P¥”Q­D™bÀù¢rPRKÕBƒÐ^ĬæXªA¨bn¬ÄÀ”£ 08ÝQ ¤n¨H”«J‰2Æ ukÎó9†µ›„çXsÜAwÊS5àÖ–PrTÊ [‰2ÅàöE©QäA"žjÊÞ ‰2ÅÀ÷EiPV˜‚ià˜Ö ”1eZ«PL 2”†²Q9(+LÁ¨R”=& Ê€òT(Lî(¥ˆ§ZŒ²§Œkʸ昄ÆÜ˜ˆ ¡ÉÅ ¥@`’qG)¹|‹ÉÆ¥FY`Ò B•öæYב”uíÊ¡¬k9MFnÖ:!ǺVÙð¬kðqÃýùŸäèÿI~þ¿—ÿ'9ù›ÿU.f9˜å_–wÿ'9÷ÿE~ýïrëÿͼúß1Vÿ·y”åP{úÏÆèlxµN†ÊAY¡ó¡¬Ðƒ(gÕ1UÌí=¢CÊQeÜ™tJ”e΄*FYaž B£¬ÐaƒP”å­–¢ìÐå¨2Ê\UpgIÑ™U( :´/J’ cû¢Ô(+tðR”:yªŒ;ëù1%Áü(bNËQ"«ƒ eŠá‹Êáò$†1†e!bKs\é@”e†"Cå Ì$øÿ£4ž#m†AˆÒ ¤<Á¨2” Q$e¯z Ô(3 (Jƒ’b`…ÐàrC©Pf˜ïrPVlÁ¨b”]ª eOƒÏ ‰2Å ôE©){Õ¥FY`P¢Š)U2ÅõE©Q˜ç‚¸|‡kÆ+CY`ÐR^´o0墺`G¢$Üù²?ب\P»S6ªe£r Ô ò²êÎêN½ÿ¼ºÓ…^³”{7èØ êÜn(eEžSí‹R£Ì*óœj5J‚NïR£ÌÐùQ”AJƒ²Ã`P Œ1 F¸?ÿ·sôÿ?êÏÿ/¹ùSƒþ»Üë¦ÇçÜÿ—uèÿëôÿuney•{V‘(St@_” e†ŽˆÒ ¤èÁ¨R”3UŒ²B F•rçwÑQC¨³º ŒÑa}Q9(+tÜ`T)JŠŒ*EIÑ‘ƒQÅ():t0ª eÏ­çQç¶GErçvÑÉÕ(3tt*e†/Cå ¤Ü7 ¨2”=€‚J‰2Ãü„*FÙa`„ Œ18|Qjîo 5J‚"Ci¸<ŠcŠ“ƒ²Â ±À  DY`à¡,0xQî\ÿ?T1*ƒI‚Á¤BY`@¡ŠQvXr\n(%J‚Aæ‹ÊAY`°¢ŠQvt ”ž;J²Àœ¨AI1KQöŒ .( Jw”%Áà”¡rP¤2TÊ ƒ5UŠ²Ç DI0pe¨”p0÷ͱe,Cå ¬0 ƒPÅ()¶œ·J‰2à DiPR ö”¼J²àêRT)÷ý(Mft/’«s™ß?j\fCE÷!é>¤”~×d@Ïë*¯}¹s¼ÜwN*Ð}›Êý{5ð?gG¿…òå;*hÿœ¤ÿì–X"­x£Ëì«n ý /kl§ôOú'´ØRuh@ð<—Š Ö­Ý2ÒWè§Ð¯xìi§ Œ›ÒøÝŒ^ݾ-&¬Ÿw^§þ±œjöÞ"G"„¶»‘ µ[íñòê›I•€ïg¬ëOÍú1Î g_Žöíµà§Ûä˶Fu¦uNh#Óg-s€ïËj GÞô{w¨“‚ð¼–%B¿ öwüžZ]¯_øi#æ›(Ñ®÷ªM•ço“îC;¶_z4V>»0ºwۨ?i$˜x Íò©£ ¯ÍRÇ ¹»DèËúêðýDô!V>âDB¼ðÜÚ¿í÷ëR¯ñ‡>Qdÿ=uU·J©¹§¡Ì¹^ }·×~–ÙXH[·cò½7aÄ÷^°±×P]i¡¯Îö¶fÛ#+Ð÷Õ¥WH/©DÚò×ÔáÛOGž‡” 1U¶·™¦‡÷:ñPfº|3øQøœ„‘:3*;Zx Ü ûpÉš¤¯Ú~´»òÉý“£ÈVH;©°;Âé×âlØpyE¯OœÁv†K|ãýa¤pòþk%}¼…¾î¬ÏúÛ°ûû&èúó}õ»ëÛÅ]GŠ×Éhhºl|Bû…¤ATDË.éÙàâ?ÏÊç€4ŸûТZ@QáÈwÞBöÜù~¦ºþýÝæF ³ñ)ï/nx)Üm:Òr\ݰˆ«iÐ}Ä‚³kneC^ÿ»:w€>á>‡ãuÜ´J¼…>Cìù0N$ãYò}«øþk2´_ípvN›s„ŒÕ_f\W ›-J~G_Ȇ³]†÷í±o8¬½ÕlĦíaÄÚü×ÛÀjKþê‡ÅúHkýívÛzé…’ÌoñnÈ5Øÿ¾é±lhÞFùmcÝAp[ï{ñ½=a¤³¶š®¯>ëÃ÷1ª!ô3Óú7ÚReÔUÿ¾Jr”k³¤†žjÿžœMûb÷…äWLŸâ{Ý_Th­ï#ô½bù¡­Y÷ßGjUú1®º˜7¯ÁëdÏ9yê½’$æˆ jذîwÃa;ñ:ë;„¯8Ó &ä2Cáû4ëxìyäÖ‹-ò¯¬ã8йózÉ%Ò™ £L]RË–cÜÇQƒßÖ:õܶe÷Usoyï2ƒé¯ÖîÓÜ £}/}ÿIMxV¥[¾‹í|ðÌYy·TIV\­UësÛtxù»É³¤ÍÙÀ÷ájWžiq®4ŒxhA‹:®ëËÇú8—믋vOO- ®M8ž©4~t:tôÜÿdÇÆlÈþR(i‘Ø7kØË0Â÷·Y*øëÅóG¢…~tâ¾nhßÒ‰ëpMŽ´í–ï´<´ýÖÖgÃÒñ Zé Q³¸NQa$w’ü¤²¢ï_}7ù>€•…>fb~Š íւТI‡“?N>8“W+›wZ Z5ƒj[{‚ß°ÔÖ—ÃÈ­¾ÜèxR|ßrãrþ'G{¯kŒP"‰&\WøÀûé0À¦8÷Âòlh :~tʰ0ü|‹ÅÛO…®‹ Ã´¥Âûb~è˜|hã µ„¾Vl|`¼O­Ÿãu¾Î´ܺZ4©¹µ]€•Q4šØþÆ“…Ù0ÿ»{ÀûÀá”›FÎZn¹Ùÿ²aüaö^½v¼Óª¢WXÿ8q_\ ^çÐfòÚßï»á?oµË€jk»œ™’ z¾¼±kM·7 Ù²:ŒL´Ù¹³ÙzaÜeï™ïKÿŦÿ"¥DzÍìRÿ¶ £ÉÂ=ª®ÎM‡)ýÌFfƒªÖ:ó îcÁÚèMêƒad›O^éÍ>Ï—Å—?mØsb}ðÄùW‚×áûžF“ð±­½žM²:m• ©Fd†;‚,ó¦/, #|^]úGÿ/=àù†”ÃÛ½DŠö¿x·&?b¹FxÀu§Ô*›öu„O&e±iÜóIü:ím¼ŽoÄúO±>{¬*ÿïiß/Îþ #­'u‹&Ž}\~?·È„CGšœK5ʆµäÆ:Ã_`3ô@*;àzõ¾îþY>ãû2@LÔáAkWwÆÖú?Ú^ç3ê’4šDV19;'¦ê¯ÝÒóç}è´9Óµ¨ö8½Òkõ§È0òëÝä+–…K…ñšOÌX_^1÷VŽö—žØþU†ö›Å”ÍÉ;˜ ­÷9ûê>Äü¡?dõ(˜¥©„‘c\9aª‹_Ƨ×ú;Ú™èZs£_Çh2ø€¯¾oF&<¾mtatþ}ˆo}ͯá0àè߇×Û±¯Õ _¡6‹/Ædã5ëó«õs´ßB ĉ&«¶m¯?»Æ=Ø|úÒ®èôû0û«Çê'[†ÀŒo*b~ô±º1liö²¿8;V¾ÑÓ7À7·†oäv)×K/µDº7‰(¢É/χQCîAËfOìÅ߇Q;'Åe ‡wÓ“' W…}ƒgÅïüüþMßf]¼²~˜å¸Fx•.‹îa^P·áz÷àr›ªy£®Þ‡>«Æ®‰¶‡qeÝn_F®Ü‘× ¿RÈó,ÿˆ9R´'oÔåQëªÑd§‹Ã›»÷àMçªñ¶'ïƒýÆ )Oª9Â’J }ÃÈŒ;.)άúαqš¿ßj¼?£½íc½[ïû­$Îýkx¿®žNiozœÝzŸòëÆÃhŸn'š× #N7ÛžìQu¥Ð—ŒÕ]<ßKÇIau˜_ Ãë\=ºåÝÔÏJ³±8ûþè,j¶Ä<Íó>å¯N„ýí/’6zaÄñGI½s~äÏú“õûr´ÛM[¸(ɾ­©_ÛoË‚IÁ}_tsº 7 zùþËDHPÏɬF¬ü_\^¸fù_¼!Æ›Õú5Ú»xºx¢$øKŸINÉ‚6+¯üz×û>ä­_»{ÐðË»æÚ®sÑâçÍ—ÿ5¾=^éÀô{zBiñø¬AûMÞ´ºÖö‘’¬3›¢Pñ>¬«Púñmóû`ÙòB‹d;¸fø%ñÖ¸0bX¯Édã:i¿sö»Ò õ„ú›?x»^Z‰ôκJ-Æ+•$dÑþÌ™]ïxÎËX`x¶ìQia€#¬ƒØƒ=|ÈæÁÆAv~Â}3îÁnï£G•…>bÞ»íçu9zÁ4TIª÷¯5,xÎ}¨ü0ãrÖç,8>m’ÿV{Ê #»_ÍÈ÷úòýlk ýÅùHŠvýõ«ÛìV’7Ë~]sø>ìþ˜õíîó,\ðìåÖ+à 1ß!µ_­m”¨ã’3æã¼Ì†ç(7¤Ï‡ïïì†öã\œ“¿XIš6unu3ã>¬Ýãx-îa4ˆ‰š5 wi<=Œ„Nå~sgwdøåÃz÷ÞØˆ9§2´W£dÂ3ïñJÂ×ËÙÐtÊýæ·Ò³à꺿›l93¶.Fy:+„xæŸïG¡ß¸ø>åh×BVzlÐp%éP÷ŪQý³!ÅïK˪,X³4dâ>õ@8Ìá¥[„‘Z_gW}ç¿ò¿ûiSÏpbMx£ëCÈó>Z—«ß”x&f¥£l¬•¤…7—‘²aæR£k·³`l‡3ÁùãƒèJæOš»vÕéUÂófãoÞï«nŒùfÃúë—ã ¢ý™Ž ï<’(I•À€†cÙPurÆíŠ,¸”z¸ÊàJ#aþ• ·í•¡„ï¹ú>Ïw“ õku‰4ʼn#räZxløÕ¬jåµò,้ ×„#¨†«á žµ‡­ž7‹GÞ®ŽÇÌúæ2.¤Ö¿ñ:Ó´ŽOȤü&¡YÍr@ã‘Õ¡ñ–,èz^Þ'ˆ²\”µ$$”ä­žÑÇ{ù*a\ÿ“+ýaIÃ_™5[Óù2å%£ýu—¹OH|ü˜ë]s`ðÌfŽÖÞYÀóOÇCËs¥³}/…’1õƾhwh¥ÿoPëÏhg™Ï×û>E‘›~²7IûsÀ)¤õ±Çã³àÙ³ÆéÖ/]¡_ý‡'‡‡-&©ÍJÂûñ/íÏËðçëì¿®|E®™qñؽHò>2û¸Mel‘o16œ¬?¬í™P¢-‹V ù’Õ|]ªOǹ®]ëÇh¿ðå<ôœ(bv/sˆÆ(Îèß h•÷”¶nït…3ëâ,_hØøÎW ë$<½¢ðžòLFUŸü±ä}ø1çDŸN¼ÿ¢ýÝ_wâEüÓŒZ­’ ¦qnfz|u—]ÀR›ðC Ïû]ù÷å·MÖ¹§œyŽ«í­2mÑïUéQz¢Ñg¿\X³6õùpx^—#LHfuÍ0Œ¬ÕUOO]ܱºš?úÀ¿÷îåø¾zéèW£8²[á-û\Hl·¤ÎÍ=xÙújýhW{ÊM #­´Ê‰üµR¨«Ùºó+Æ‘b}“µ~‹ö¯Õàô¥Ž?ϸ ZÿF»»7N¨ÒU}›ðÜ®»¬ñÁ¦gïA¦]i¿k£hÿöPÒwlñ­;ý…ûoµºN5]óÜ‹rü\9Ú¿Ðkîº~·I¼×U™zùS}‘da0¾OâµÛLâÛ^|Úq¼{]XØ’³þ‚ÿ±|Áê­?£½þ¡++ܦóݰéK§µÏÖÞƒçÒøvcoŽƒQ‘ÎÏ.Þ%.}7yÝK÷ÿ«ÞàŸ«Ž;ÈÖ£´þöï÷Œ|Üõyì8ojý`b§Y}ÌÜÃàj}ïßSù.q!”rŒü…ù›Ÿ.rocñµ’ÀÏ×õ2J¤ó‚(ânbçžt½îCH®’™3æt–pä·‰dÌ‘RC‰«hâ/pEYÜ¿êYéÚPù~]íÕØ³ºSØÄ›ä¨OÙ¼ˆÁÁèÑ„åÝïAg'xh4 n¾{£rYJžÚ!ê˜ì/®ÀÁVë}W7¨Ë|ÓüŸ´/ׯ_Šö½¹×VvƒØ÷|Zí­çCø’0Fffz¦ç¶{Ø8ÛøüJô¯`¥$øk>kj~üÔ¢2ð| ‹rqî†vÇw?|¸Æ¶dgkg鱇³r~¯Y¿2¡óé³aeÎî¥ýÈ¡¡¤CÞû»?Ê„8gÏaó½økÑ*ƒ“Qð†_-„ç­õk´o4øUiפçøÍާ?„§#sE?Ï„¯vo•§ ÆÁʯÓl†’ë *Ÿoðæý|tÞÑŽ¡Çòoï^'Æ/·ºNýöbUyw÷2ÁRú3f}Å1ðÙÍaÅŠ¡dë`›0ß¿ï7Sü{+ÑÞÀ% õ:™´Qúɨe´Û(;25>Ž/ûæºo”T9úì‰m(iŸv«‰O¿°~Í~o¶nÃò«?ÅïMƒ×I½¸‰ï•Hr}ÛbBó`ÿÆ•“ #3ïÏ=œ[Úÿ\cJÜ›w([è/<_ö7ë{¬õÛÌé® hI¸YOñº<ð17ïXñb&ŒØð¡ÞP7;h–ïåÖŸÃËÍk¯î/ŒÇ|ŸóO¼¿¢‡#KGfξF>ž²[NòàЧԚïfBfvð —ºÃa¦^HÑN_ÓST’¥þ‚?±õoÆ?*W/ ÝQ3žUÜw÷*Ùûz`¾ç§<[=ìå°#¦Z­'™9šò\C‰ê†v ~Mì>£ßUrpëžàÙmAö\#¿c²L¸xgÁöÞæ°þ|%É›’‹dÂ͖gã…çœc†×¿Tò‹xž+C»üzÎr_³r\gÇG`ÑíîH÷™`Ñ®æûûŽ0ÖÏdÁÝ{Iëö/¦º/ ÖØûYc|ÊZª‚‰ï[ŽöïüQÿpÇ+¤F3nä¼Ù7Ú©†s&{i79º€Ó’}‘̸ÐówXŸá9³<Æ®Ãú‹×Õ”hgªR•tà2ñ¿ü»Âýsàåƒ5Wî÷Ë„xâýûgêh* >q÷íE²Ã+üú;³ú^ë¯hO‹kt™tÚ>(÷¼ÊÐ#±y&œ|7®ÜŸS¸öíú¡¤pÙÙœàlR'çK¿»øy¨Þ½éÇhØ|‰|2çHù0>í÷Ô 3aü†ç½—N‚ØœAkj‡‹Z›zÑùÏÆ<Þ½h~G}ƒ%'%h?#D™ÐÓãizá·Wp—|˜òtÚì§pxJïç#VO³œ'„--Gê/¬WþÉfã%ãò÷A¹´x»ïž2év‰\ùµWïÇØ|!;¿¾VRô|cÖ¾ú àç=¡d†$­sÁ7™`¶®KE¾ÿ»þ¼£³MÒMqK©Ð{y>ì·_êøòRäíîWܪÏ8(¶ìa˜J²Î2k¥‹{v¿l½qùÊÕ h¿ÑOw«™ #h=!®³æ”gÀ¹ƒÜB«T4oÒß<>”ĵž¨jéO®»Ýpµrý-Ä-?åó”í…rÀøpr6æCI·ðjO·eÀ¡!F[­Bí¡]ý²M ±®‰+ÙnQÃ%€”÷Ï—63OVqÙwqþS¢]-aL8YkfЬ ¼zúÞµ*œv6]gýfÉ-­ %禒v÷Ï~½®"]/iS~ž†ö~l©®? BA_ØüÐ]¯Ž4~oægÀ÷üÒäzoÇ@CÏùÖ­B‰žÊ°ÎàKÂ:Ò˜ïÎ]+ùš:ãØký7«DÊQªÔ/ÃHÕ1‡«·*€5wl6>¦¼útý8¸ðóðëôÆ8làÖüð }6n±õ5æ_ƒ¾\˜w¹#0Öñ:-¿]ù”¹:”´ý½‹ø )€&9ë–ûFÜÈÈv3/.U íJ^¼üzài}<×DŠ?Ïs¢/’F?~N»:£6¾ûd\Ü.jý¤\Qs"´Ð¡ähnF»Fx_lübœ5ÆÛbü­ÿ¢}žržøÕkØ4hmœº/¿W©zŒré¸}µb¬±¸æw}t(Éu+lÒ| 1ëߺø^Ÿª` Fu£ã7oO†ö¬Fqd¼s¤qd¥%‰' ÀKV%ëüëtÐ{.ØR5öË©áÑ?”Ìôœ;;øî¹nÍê]̯#ÊÑNó܉=ÒLÎ’ìác+[Ý.€Ü€ÞYWÒÓaìsÇ;L€¸ê-2;‡’нJG¸ƒÕkï°Q•ò ÊÿžÊ,–OΟó–=]þ°ŽÕÎî™7ö]ÓÁÖ™òª±þéÁ‘átùŸí‹Ë{Õ;kqÕ¸j±j)Ÿ?4hwÉ»ÙnON‘Ž5„¬ÿY!3†õøy"´Û«c ïаª2óP­š¬ê äeÆèâ-»9d‡Í÷t=ŽÎÏî—H‡7}òÉÍá$Éâ°&¨:0æíîtHz¶)Ïw܉>5=µ[(±ñ}±>6€0%ã­iýíÔ•TÃuœ6xo°°µ¼ÛTÌ‚Àtè³yMiMóÁ`ô~¹E{›PrcžáWùì5ÂïÏÏŸ „xbû2bžªí¿;ºP®ì{ŒÜn\½ÚK $›fåÔ]•ó¯»ñ«Îà×BÉÞjæçVZCþ÷ø}ÌO6,_ñ\T~]Ë íŸ¿kÓGIñhP«s+¼·›,I§uaxü¸Nćž¡D{aÅ¡â×kJùõ´óµÞ¼H•õa²%* ûô1Hé&½Ôc~:Œ|¾¬mÉÍþà³ÈvZ@£PÒ/gÚµ4ó5¤›oÔ¸­…|'^o“£½78RË2òV¥~s¦j Z½¥ ÖLK‡Ñ3õ‡wðé;&þøí"9 ø9c‹Ñadû™,ß_²Õ°¼¿¢Ýk¯Œ+Níãy«‹5ð­îÕq¿ÒA{ŒÄe(t¿ÿ+õsñEb?KÏ5B±ß›ýo–ŸL÷+¹ÐÊícàuä›×orv7ñ®¡šüv•ôÍjö¨64´ÇÚ‚w&øù"ÑÛfí×y¬î½™»ÖM¯6›ßŸ×Ë.‘þnsvDWýäWäÏ÷ ›40áÌÃVO­ÒÁâêóÎÝÆ@Ïp¥³÷Ë‹dkÆ©ÕCÍuv_q`ؾ¶ÖoÑ®žÇ«\¯ý›ˆAßøsýƒ5 ‡Vw·´MÏ÷óåÕÆÂ¯!§žy~‘¹:¸òcM€À-bûÿ¬ÞàçG—mªÝÚ5zû‚†t\ä¹NR¼NÌ£ñ7_XCNc4}>¥™ƒ\›¤Ãô-㥉NðèºH¹Bº#›?Ë6üüjÒè• ¿Na#^GvCû;V]±Ê—¸,qªþ*\†;3îfUÃПçW–¹ãM˜w‚h`çÛ‹'ªÄ~î[ëÇh¯KÁ7E~ÈLÒxæÏYÓnh èÝ µ{¡êÌ–o½Vê CMb %U;µvó^¶†”?ó\àñqÖ²¿RŽöù|c “¾í;i¯ÁGV×?¨†9Åßögí¾Ï,^Jx¾ña‚ýÍü›ñîÙu´þö7¶µk¹~4uÑ· M»ƒ~Ì¿ª†ëžŸSÝÏ;ÀM=&>9J*W°ô~%×ì½²ù®ÖÑ^ë«7õ‹£V€ßÍ6½Ý²5P9üîÑJ§Õà@fÕmû|4œ—vªj”J:ìÝúŠýßuͼž¶=«ÌO´~S"ý•qÜ&¡ézXt¤$è`!ÆwÑûZ§÷©!i¹_‹ÎÆé•ïãdoC‰É…sû æ|;ƒ'fïÏöiÛ”Û— ÝÛž·œ°Úû ï=å•–åU(î¿Y =?Vy¼×vÏùy„ùlͱZò„uö|ùýCzþ£ ˆ¹ÙR´?pçøÛ±¯ƒÀêöæ/?k ¬Ë•aãV«¡ÕÉIF¢ Y¼­OŸÐP!Ù~EÇ A&3+—ãñ¹¡½áa)w†%ï‚ï°áëûŠ…0COä¶Åj¨å³¬}‡~£ 3wleo(á9ÏkþØüm3«ûÈnV§ô…zO<’¡ýVÃüÖt±6ížõÁªF! õ¯b1ÓM ]ß×Ô ½Îeú_8JòßÅ‚o°NÃê–çØüL\‡ÉÑþù”ßç·5Ù—ö-8sѤj­ïsÁc¸LÖªŸ=ú鮃K¬÷ t¾ÛÁغýÂ8OÌÄç”hïá·0oÕ!¹¶æè°º…PíÈ€Ð{ÝÔà6áÀÕÍ\ Ù¸ ¿‡’Mv¼u( øk¨çv÷3›¯ ÷­õc´û9âõå-åÀŸ+„Ú·Zl­ÖD ]r›~qhê 7òÜBb«…‘¬Ý3šn ÕùógžKk"<_1N/·DÊí~¸v8Ëç'É.á}¿]W§èa5,iày5qÒ8¨³"ócn…0bR°q|—õí‹;ýÔs=ëTE¨ø÷ )¿î‹×É9ÍØ¥÷IèU¿nÕ™ aMŽÍFï'iಫ⬆Ö°aƒÅªF_BI¥T‡ˆCkuu‘x½MŠvl^ 8;ë4ðçd Á&û¢cÕ¸4¨¦È}¸ÔilùÐë˜1Þïüð‡³÷ì×Å5[Çd~à3o»h>F»?ºs€Òèrüuð…0(äâüåçÓ`žãÞl½ ƒ Z—è³F­ÃH §Õk§*þâeóùØPxl½—ÿïèü ¯ãð:ó]°ÿ90ù±¨Mßž…0gW³B£iÀs›­Ûõ³t#Sç·Põ7\#¬?1Ž1_'v-7o•£Ý^Ü²é  ÐaÌ罞6…À¥ÑÑ[Ó Ó¸K¦ës{ÀõÁõ6y‡‘ýÆ\E´FØÇ`y‰½?>Î-€ÍµþöU¯:.?S1úÝ\ñr`! †ÒkÓè¾Tب6èº:Œœ»1¤§sÊÂÎ×0n8_7òõ¡ímÉ-…—ÝwÚBW¯¶µßù¥ÁT£“ý•Âýæ¶­·õ Y#Ì“ËÏ·Þ õ ã óœLþ:zJ¤3¹íÍ¥ ðÔ¼… Jd×§Á‚qµsõÇ÷„dyÞ5×0âYÿóåØ)k¿ã¹z_lÞL¸Xgßã6À¯ðûâ´Ë¯÷†C¿õwWÕŽùï‡m£¶3Òàƒªy~_l€çŸ†‘a\Õj¼æ¯uj¶~Âòª¸Ž”¢}äpFGŸÔ:¨Âû~‘]ÿ«}Ø·õh¸ÚŠž¬Úp\Fl‡]ZÖ¤êaíç‹Ç7´gñyòŠôf— ·¬=¦¢ôÂ'ܰJƒ›½–ÜÛðdðû‘adGõTÍÛkþâ53.±ÖÑ^‹>•·UntdÓÓŽ$9ÂWÚ$Ÿï´ —¯/F¤ùçÚþÐå¥òçEŒ…|:ºøS»w×ËóYåxzóª'W»Á–ÒáÎ…`ŸY˜ý+Ú¤n Ùh3qÇ—Ç…û˜ID€°Îî¿FÖ«øýU€Õal¿³Üz^'­Ë0õ”â+på,çÀ…`.?×;?úH ¿ûÔž1kÓ/Ì#V¹UßHã ¿̯ÓjÐÎ[½Ñ®‘®‚¯¬]§‰N…{´0¥Ñ­TèÀ)ìî {xüméÊgÂHõÌ/ëòÛ'âí*øzã!Ö1m¹škp1ð@ñ|o>Þ×O9” a«-[ô1žíª›UAAîÔh²Ûbðûÿ¹NÉ~6ÞòûE´þÀëðç¶#¡(¨æÉÍ# ¡oa«Oþ©s«Ê‚”ð!pnhéúîæ ÒïÔšO«kéæ-ìþÕ‰ÓôûüþnÃÏ›ÛÒ<ÅçR´¯÷3jõ‡¢HÐÖ èφ[>Õ±0ªMÊL1ZЪOçNj(WwÝ¡«Û™ÿ±<«õg´7véÌ5B¯Ïo-„ŸËÓvšLMž{ÚŽõùÖ’‚ôrdH]}Îþ.é±áÊ€ãÕ ÞÏcûÛe—ŸoÉÐ~O/»¹ÇfÜ€GiíUiƒ áHÃZ{‚ƧïOm ]Wƒ M\¤†W@ÏÞ+èüò‘àßü9äÊ‚³õrþ÷áy×r¼Î¶ Ðò&$—â„YZÒ ·„q©0§½Qt»-MÀ]›8¤šÜt¼å]ÝïÁê^þ»‚6åæ‹hwÖ·6E?Ü„æòïž³ú‚ìRo¯Ë£ñù|(0kv«<:à=¡e)69ñ¼þ ]|²çÌs!+ó0­£Ýá•Ï­6N¸ü¼¤æùMê08º¾¿ôêdoØç½{Mne ”Ïý8W@Zµ‹ü¹ÏØ@àÇ‹çszy%R~ÿã6t}o?ª{!|ZÛ¦™}ÏTp\íÕÃýÈðËõyÐÈDAŠà‘Ô©®nb¿ÿ‡9S6Ýú\U˜WˆëT Ú?Ú|°ÁöžQàPíÆí“] !ßÜæã.I*üªWœðréøà¸!¯F/Y;{À®¸¿G¶Ÿ¤õc´78Å]? ÛGMúÚ±®¬mÑ ¿A*Ìðûì¤H;vN_™£ “ÄÿŠðÓå vß|Üéê¼3ÒY³Óû–Ÿ¸áu&ìktìèYݸcm±.‹¿Õmõ»áЉ¯fxìVmZjðǹ”—6ÏG¬>87Cw>Ž8.exƒ}¶oúÝW nNf?›Â7gy»°‚°z{Ò¼vd¸ð¬wË‚S b©üIóš#êLxÆ!ÇŸoÔýãhp% ½wI‚õãÝ“ÆôÈJÓÊßo¸µÓm$êý Ò#¥ï¨tÿ¿Î»0>7[G+·Ïö 'u.êÛ5öFÔX|¡z!dlU„f¦Àœ Ýá„!‰on{øú9ëÎ͘ýÿšw²}Ê?×ýÙ–; ® Ü,>>Þÿ¯|òÎ~ѱõ‰ÕÊÍOõòK¤sdŽøÒ'´Û9w4lÏO…3ƒ¶˜t·9_(lùEA 'ÖzÓŸ4j¥úâgªÛ?§ï…únE‚v—ßœ0þx<àdテRcjUw}]5R'Ï7>ô¢;<±\8WþUAÀaÐúq·tû‰Âþ9ýî‰qÀÅó)Ú—/ª¾·¤â8éYçJ÷kø…ž7 «?·²áNŽ”Ö>ôMð×9ɧݶGt?]U87ÊÎjýíóuM"ä:Zd¥KýCzÍ.¤Ð}6;p6³û{ô¿í?ÎÝz5¾µ–l}Dë×h7£©þÇDYØù×ÕvXegXÝjO T|ÓÌv€ž-Ü‹ìE¦'\5üþ…¿0dÏÝwj¬áˆ3Kú u±Ö¿5%Rç¯AQÓ ñÇS× Ž_c.¦= HjíjÙ¶]gÜ®€‰2œhê¼tüzDwŽ‚}ɯٗ÷g´ÇÎz½/ ¸òñPE hŒà·µ/ús³OÊ–æÐ.ºqFڻȹ}”î~Ùßüzu°–Ù®úiÌÛ•¢Ýʽ®Ú=H‚¤š7@·ê¿TQKRX^ ~×W] 'Ç>Oè;¦¶.¯²<Èö;ùÿÝ¡Üy%7´ÏoÇ$7ZÖ‰,€gŸóÆ®YžõŸvŠ( 1'Í?TðO=N¼¸eù~º:ž=ç/k’¼¿Jª¿î×¾\œËÐþ½ŽÜMÉPÏ#íÙ­Ðëý¦wçפÐq¨;‘sŸ—D„“3{HöYéüO|žDŽv$ËžÛ¯?— ÝÝ‹j¦@¨êÈÃíègw·8±®iÚäÕÈ ád÷ðÇ &ÕͯšxøbŠ1°u1ñùq%÷ÞNuÄ7› ·ö-ïY6Õ[î Æûkz¹æÎ]ȬÂUáÄR»ðWÝÅž/?o±ηjÝ í5%-»c <Û- ^[½vÏ®4ÎîH è线Ð^B¬íFïN>Ýâ6ˆuu«gøß£"Ôúhÿpý ¸¢Çÿù§—Õ?½¬þSzY±þlÜsS¡$èÜ2ÊMäz°‰¸‰Œ/íNûY™ŠúYYÐ>ªÅ"„ñ¿è ˜Cû‹ØÒr«GÌLd¬e&²㥔™B{¨r=ƃ(3‘±¥õ([:’2[ZBû1k(Ó,ˆ2cí(7‘ãKÛQ^†r9¾tqC¾ÏxežY‰úŒKE¼;Ê—æx=\U¹ˆ×Ãñ¥‹›ëøÒ\¯;Úg¼”òz‚i`sÜD¹ˆ×#æ&ªþèÛb&êÛb%⣹ОV\¯q7QO+÷?zZ±^ãA”!¥½ÆYO«ÚK•ñ $´§•šöRe=­cš1{ä´×¸˜Áz¹°^ƒÅ”ñ'GK@¬§•1í5Θ=Œ1m!ê7ÈõT ¢LŽ«öO®þ'WËôþórµ}.ãÖŒëEOÙjèäÁ"¶cÜzPn×{±¹­hïAŽÛÃõÊR LÿE¿, í—%ñmCD<1Wñ )Wõ».£\5í;èBù¶e"¾­åÛ*)WñmÍh/×bÊí ¦ J{ÊVã·ö” QLÙjã¶´!ßóZAS*êym'bBØSÆ­í="bBpŒÛÒæ:Æm©„gÜ*([ÍŽö¼æáö”q˘b¶še†/Cih­ T)íÿÊØ=\ÿך ÜEüpʲd qÖ÷:˜& ;Ú÷šñÄ´ÿ ëkFÙ–9´ÿ ã‹3Î-ãB„о×\ß-Ö6¥õÝ*¥}aÿd÷ˆøã¦´ï5ãB0έ•¨÷×+˜r 8~«Üqžþ¯r4—Ÿ¹Üü_åå—ÿÌÅŒ…ûgf\.çþOs-˱,¿Šs+Ë«\Nåò)—G¹ÊåO–;YÞd¹ò_ñoÅ9’åÇ?s#Ë‹ÿSæíŸyðÏ(ÎâÜÇØ·,ß±<'Îq,¿ý«ÜÆr÷.ï–ã¹QÞ­íA­¡}¨å•yN7Ç莤\È@QßT֯ߘöéWÓ¾ÓŒ5L9c¬ç´ :’‘®ß´í7Í1Å)WÌãx¸.”Qc*b=J(CLFûJ{Pv˜Œ2i¸Þ}Œ &a\0tP+Ê“þÁã ÖÇÔö0å¸2*Êg ¢=ú8F¶œ²±ÝPÆèÐn”ÇÈqdT( ÌA( J*bÈ0¶ JiÉspU´_tå`‡PfŒ1í…/±­0@‚)ïšõ‡Ž¤ü[wQï{nNöOöO¦÷ŸY§™Òß›càZ sQv—:¹\Äîb \_ÚcŸã+Ê(_‘c Ñûn‘( ƒ ¥AI1(ä´×¾Gˆˆ«qIÄÜ.Æ% ¢Ü.Öš "{ÚÚ”ö‡–Ó bü[cÊ¿UQnãßZÐÞ¦¥¢žûe”.§ \Ê&)¥ì.Ž[ÖïÍõ9åXHv¢Ñö"6‰ eàrlŽ%®±I8nYs·LÂ3p#i@ÛÓÑcÜ…2p›DÌîÊAY`À¢ŠQVøÁ¨2Ú UN“× UAûD{ˆ˜ã¾”±È˜ã¬O´œöEµ§}¢{œëm*ê‹jA‹ßÉBÄ!g\Æ'QÐ>Ñ2QÔ T1JŠÉ&UFû£þٿߘ²ÉÕ”Oî+â“0®”ëŠ*³æ9¸rÚÇßøøâþüorõÿ‹<ýgŽþwùù¿ÊÍÿ*'Kõø|,ÎÅæà?ë9–sŒË¯,·²¼úßåTq>çQ–CYÏç•7ÿ»œù_Õtÿ]®äò¤\ïÿºÞø£[&âŒ0þ«&&9MNn´?>—¤ÜþÉÕÿäj½ÿÌ\mAŽcé¹S~ ÇÒó ,=Ž_"§ü;txEežÑí‚RTáÝ(e£J9Ž C$ÊTÄ‚â¤2”e'â—Øqu¬ˆ_¢@pµ,J‰2Æàñ@å ¬0ˆQÅ(;n­“2L8nq0eK1¸ä4À8¶c»S”erŒn ¼@Ê×3à BåP–‰/e[P)Ç‚²àê]”†rMQ9(+ÊÝËAI1`ƒQÅ(+ Ü ”ºyyöžrªeÆÕÅ( JJy|óÄ<äæ ÇUvC)Q¦Ü*J%âAåˆ8¤””[OE•¡ì1)(PƘ; ʘrûT”7åAyÑÆ"^´„ò¢s(·ñ¢¥lrpö(e¡ºSv€î¨Hˆ.”m€éŽRQþ” *%ÁuC)QÆ”ÝÇ1£M›ñüzŽ-ÁÀõhÎ3£ $:f´A ž­¢ì>7TdKžuϱû8f´xàì¾b”>U†²ÃÀApç›(£Ê“€/J‰2Åd 1T)«JÌPµÃ$¡@™RŽªBÄQUQŽjª%ñª¤˜HBPÆÝtÜhSŽU…R¢Ì0¹¡4()&9ª eÉ&eÀ홣BP˜xÜQ*W•ãVY`" Dsk!"n´=*e`Ãs£"®çÓÜΕYnçd.‹óp=þuks.—oñõy”åO.g²\ÉåH.ÿqyå0.GMå¢ùäš%zå×,ÿ\§Ü(Ê»D9a¿(Ž”Ƴ/e•a|Úc\Frâζà/®¦,Í2”/åfrìáRn/‚/w~ã¨Ô˜g3~0Ç ¶àÎp5 >;Ž­ÆÕ蟥ܾAkžá«F¿3C ¤¼H;:ðDrëÿ”ˆOÃÍ׸órøsPô“ÒîQ‘”ñȱÇ#)+—ã+)+ײr¹3"(5J‚é†R¢Ì0@ÝQ*”)ªeåJšñ\rŽ•ËqÉ}›ó¬\c‰Ž•kÜ‚gåªQ¦Ðî-u¬rV<+×<¥AI1Ðå¨R”¼œ½=J2æÎ¨¡"Q¦˜díy^¹“A ˆ „*ûƒ É1Ë#QʇŒñ!Õ”Œ*EÙa F•u-Ï-g¼\ &Ç-GY`r F£ì0É„ÐDã‚R Œ¹½O”eŒ‰Ç¥q"‹)ã1UÊÍ“D¼\”elÃór#)³ÑøØäþü™«ÅyZ|Våæÿ./‹s2¾Bm.þWy˜å_–{YÎýwù–åZqže9ö_åVñÞ&—OŹ”åQqeùSœ;ÿ]ÞüóìÚŸ{œÿU~d¹‘åÄ—ÿ«skâüÇrŸ\O—÷X¾ûŸä:–ç¸çÄøµ(5÷ :F17¯@çpAçP¢Ü0—)Qt””ENÆ ¥DI0ÉP˜Ã)§¶ŒûN‚ö.¨H”1:–÷m:—†;k‹VÆíURm©ˆCk âÐz ã©ëñï”åÏZ¡šQölÊŠ²»Ñ)KQA蘚&<¶e¹§%ż“Ó\ÇŸµB§ FY`® äê§–<¶Œ[?GGV  ЙÝ9¡C«¸ïЩsPÌ-v˜SŠQöÜ%ÊÝ¥D™b>ñE©Q2tþbNÜXÌ!eÜùWÌJ”)„JÉ}åê/Kž/[Ì­‡c”uç9ÛÆ((Ê&§Wy¶¶=æÊ(e*bÉšan¡rPVX96æœ2îûTtÈbîûTtÊ T)w–sMÊóŒe…ÎŒ’¢ÃÊQV\½†*CS6FöàÄ­¡$èÈ2®^CY CsgЩKQ.èØ ”1:·J…’põ÷­):{)'î<,-4Ü9¡ó«¸ó¯ÅÜ÷¤Üz·ÆJƒÁeÊÕh(5÷ )‡%ÃÑ ¤$rTÊsA$÷í(%ÁÀñEåpߌbbiPR ¤`à÷Ï‚è9YvÎqàæ†rzÍC´oÆ}sÊl…ˆÖa•À‹Ê}ßÄwP~–ý{5ð?gGç’¾|G íŸËìŸ K¤’wk™/NÖ÷¢Éþ!¿Ü4ùÀx^µ–T_!Ook‡øè5מ×Q,ô!>~½ÕýøpÒ-ÐèUY ¨öàIéÐvŒKÓ…ï›×iõ`ÛÙ›)à|)µ,*2‚:V:èp ¨ï7ñS¿=\ºeçžNÆÕéé ¡3ëÁúË”ë»vùþò) ë8hƒrG>x䘵ٕƒZÕÛU­7íŸN†%4ú$úG±¾¡¬?®˜ê†váipÇ7 »OÎk[0?”†ë ¬Ö¦@Yö>“53ûƒdÕ/¿Òá¤rÑ’ŸdðŒ^Ú¸´n²0½R¹~Ú2´ûòYÑ"¿€TØh[£ï–¡ù°Q¿÷îÁøü_šåï³aô“ÏO¬—NFÿ»©?áûª×ú÷ð}Ë[ÿ{ðÏAŽv#ºäú*âSaÈE‹•a-ò¡…ã(ÉŽ )Ðu΀¡½|m¡mÃÞÕýð~ýælÒ;³Ôÿþ¤_mÞŒþptu²ŽsÀxs¬Ÿw%^g´äUÝóUÓÀ–Ã,}¾í—$X NNC'„)“ê…ÄkÂɆ3Ή–qþõ/ä¯g(ô—)×ísÝ?‚‡¥þÞV3$i f¥wõ¬º§ÀcƒC²màåÏÁ}–…“Û¦‰}êéú(ñ}ôt}oMÌ?Ô+*‘†Ö‹WíLƒ'îNÁLÃ÷ÓÛ¥@£¦/­K·t…ó©sý­ÛF¾<’†8èú«²>#“ô–L޲¯N9H=õ×Óú9ÚŸô½âòÂÔ4¨V7c^h˜‹CÖ&÷ L†øKS2¼f †Ñ·9K–GLSÐS׿élÿ"÷œÞú°3iµYÙ øøâûáËÐîÚ#)çÒ²ÔÀs‡ÂÕj®óN†Áuòï}ž  j6ÜAÆ{?ßÂÿÚËrü9ÚKåÚë¿PCögŸážÊ‡°qæ6i÷d˜ÔúÄõÊ{-¡ERÀFØAù½8f­? ø`Þ³ÕËçW´ó2dn@Èw5 KûX{úþ‡PàÝV5}j2 \vºÜ;uA‘LšïÚ¾rÀ_}¶ÿ쫵^¹å`ŸrœL ^gV@áÄØéÀuçß¹ø!Ô’|Y494 ¹†…P:Â}§‹C9=múä!MþâBž2ª¸o~a-H¶]ÑnÖm;pi’úcn-¾’ÞÖç7N„^zuÈC±Òhäö‰ÉPéØùéÃm áõÍ–eÃ"È̆E. ®éƉ6#¦¹>k §×‚îÛñ~ŠöœX”Ñ=>u4xº²ÉC˜£Í\“!èþíi{Ma°~ë©£"H{×·‚î}±ç’[?,¶È¿6üX0:(ç í_Õ™÷c´Ÿõ^bm›ß­ÃGíyûzžóUõvJâåÛ»}õ? jHV˜ùÿÅÏàûÚVú²±>êZ?Fûµ×vÛN‡De…N“¢@µC&Ã*N†}†¶ xm æ½Û}VcÏÖ6pü³oØÚÇ”çÈОö?wJ‡gÎ^2·m@/'qÏæÉÐüûå§×ÚPP©¼$ñD㩺¸cyÝïð²|Ë<¿FŽöFµÛ£ž”~³†Ê¸>€·[†:X$Ó>ƶð$*«ÏâÞähâœÑ ½uœP£¨Í‡çÔú!йJ´ëèáÛÐÜt¨¥¬:(°ÃÐâîL“i¿Ýa0©HUi‡méq¹BŽÌÀÿ¯>ì»KÛŸª]¾­š{Ë{—U¹8Ö ý‘÷C6¸{§CÒˆ9?;Ux}½Œ ¾'ÁkÏ óO^þmcÖŸqŽ »‰ß­†þô/|!Ô#Z¿}Z"|þ5sð’t¸Ìr¡ßš¶ûò’ CH¥Ë{h£uôò¬ùÊ]§ûûÿÕ—=žëgÅû/ÚmbP±­G:îW·?™ 2çz.ßN‚øÚdzFÁÆ´Ãn¿—D¾nðÿ£ßø› 7—×k¨ã0ž›¸”âufn1º´oA:Œê°NÚyq.XÚ)ÚyK‚Æ ž¨7 ² Ý[Î÷ é/_œuØÿ¯ñ²Ö¯ßã+<ør\`´_÷yÈ‰Çø>“rÔ)¿¤¹ð¡(ιï¦$8¶ èäoé ˜U›#§GîCœV5Üâÿ×øÉxðZ?F{ÝëÔxÞhV:h‡ý:¹0ËpÅöAË’À!Ihý«Œ™µf„¥g±¾~#ÌŸx;œmµãîO›ÔB××S»´.ǵ£½Ñ•7å–Ãô_þnò,zïô¸“µ xNn78ê”ÞüÀ²¢.Ç踡¬^g}ÝÄ~¦D».çªû£w†î_q)*ç/|ã83 Þ,Ô¿Ïú®Õ9võ ÿR{}…á¡ÓtϵEÛ~?‚jWúߊãCƒvŒzwev:œlÀtr`Në²=·'$yü¶;ÇVõ‚y«~œ²]Až®~¿ômÈŸ}j¿Û7Ù—\ç´m²|ó†/!ËàçˆÛ ù~×zÏJ¤r «Ó¡aóY]ŽÊWúëo™Š Ö]\Õx~\ð²0i‰®2ëGÇ8*b΄ínÇ‘ñÒáW»Õ_¶5ÊÈ™>É]z'Á¬ðÉNžSì`Â¥=û~ŒŽúAþég|]øMëR´·°ÇË-o7§ƒÑ ë+ßJ²ah¥½V4ÁûÜ.óé8Ážòœ"ÈïÔÏÅ5¿øÆ‹sNÝÐNÓ®fû¥Ãn˳Çj*³a×Vý ¯>'ÂЗ×÷>8ä5v×uÑÃzfðäûq§èž'‹7¾¯u§Ëû­sѾۅEnž'Ò)g8F-LL²OJ„Êž[ rþ}F‚F9æÑE2Áÿÿ'xþk/˜Ó÷ô›ÄÎ|ž£ýÓêe['O‡äk6zÍÍq«¢2Ž$‚ÏJ‹>s÷Žƒ#~Fûš,Åñ?½ò&—8_9ßôÌÒ•U)o·åÿñv•h·’ÚlÒ·}éPùî̼{ÒlèsÚÊeè’Dè÷óûó¸ec!>ág-[ôßm’=Ó†<–~|¬ùÍÖÊVö.ªß·ìÆû-Ú³ì÷ù’˦t(â°n6¶ï¹ùÖ˜DãÝ:kéª1$¸ìDH·/2¡_oÔÎOFèÓ¾¤×m÷sJ›æ7«ïæûë=/‘>‘¯Ýuu:tº’¿èòãûp3>bôÕ^‰”§l ­ý«ìƒõ™+SÑŸ°¾çZÿÄŸßqÀ§k Ž ûž9œ¿C+¬Ñ¡m"ô¯nh:)¿¼Ÿ|Ųp_ùøéêþjþÍGìÂnxŸ}ÆÅŸù¾ÕR´¿¾ØØª¶{:8zŽzulå}ÙýÄò; á®õ™¹É=zBÛÍß·Ø€õ“áÃÑwÊddÂﲉ× )¡;0ž¢ÖoÑ1o`þÛ5 ¯æÆ´Á÷¡Êôm]^%ÂZrcá/K¸+¯3@RáfOïºçÉx§ìy²þ”k¯?JZéI96Û½·Î’Us+w¬d Œ\vñõ“òyöE‰ô*¹d½ '[„íܾR°ÏsCªÿ5îòu8åW£ý/;77RƒÇäCes‡d€ºp^íîB`¬ ÛŠ~µÊ '¸eÍ+ÿÊY%çÏW¼bW<ƯKÑëÅû1Ú]3’Û€Hƒ®+ØØ:œ=os¥ê]x5à¶ùÔkC¡oÃ&mÂRÃIÌç]]m’W ã Ë7BþºôìîjWògÝÈîûAeÓ½PÀç%ÚKí*ñ»ÕÍîeÑàüd|°ùí‡w`IÜ©Yñàø›JAâÂɵvvó—éÆGVŸñýû{?ߦ<´»ßÖìjÀi¼ï^]F­¸’¤Ó¥é«î@£?Ü̇  }ÕÉÛùÊ›c÷¯þ‹'2£[z×®gªÐõ”åÖãô^–H|Ì7­(Kƒ>úikÛnL‡ŸµßÝÿzí,zWz¤RRà¯Nº5Üö¼›|•?ì:ìþµ~ö9To³klp»;ǧì]5¯\;u‡ÖÙƒ¼îŸi²+œ”$Î^Ö,o¥Pϰu=-µÉʲ|Ý‹v»m\}¦w<\ÐàÀËîépsÒ½]G¶Ý‘±Ã“Nø YÖŽV‡ýɃåù¥‘+…ç;þ™·ÛŸB|ïÉÑ‚¼£Ý€%MÀ±Q+c’N÷ÅîÀ;üíM,íAÖ¾’þeoÝý²ú‘ßg|kô¦ëñ_qïmÄëÛ2´;Û<®Õ»÷©0ðyŸ%jØWavûñ£î@ΘÍK{p€ý7:Ütó'¼¿ëî—í‹ñ|2‡bn‰íùÚT*II…ˆ?H‰J V-¦ŸLiwœUÜ ÆÂ,íFU8Q׊ 6Y%ø»ï_G.¹Y­ßÜ6¾‘ÛغŽÖŸÑ¾vÚv2›t«}L 6.Í~ÇÄ ÖÙâ·¬L¸³&œè…θ;X²JXψ‰[þ˨ºž/Œ;#æÂhÐ~ÿ£ª]'ç¥BÒuL¡¦ë^ñpå[»Õ8HºYRáCP8éý^:cGÛU‚ŸÔ3œXÞð\½W%ÒÔÍu¦¼6K…F§¯Þë?ë*Òõ†ç…x¸q¹ƒÍè‘cažAŸûï '<‡F÷Øz0{¾q¼ìKÌú¯'.Žq/ZOXIøüóE¨ïù}hš‡ÑÞ˜Q—ϽOí¿öÍK¯ˆ¿ÿº™-'ŒˆÃå“5!Iv`✚ßpA8Y×îã·]e+„ßÿÊägóÉßùõ0´Óúú ¦hÁËipyµã²wÝãaË®Ê>ÛB}ß>)œ ¶ÜÔõð ¡.eöxîÛGl_Ž;-GûÚíõðP^í¹! V7[»¡y<]¿ Ý ÷}[66œ Æj¥qGÝ}2¿Š˜4Ø©•ÛW›ÐÙw;úQ?E»–œÊž•ãíMwÏéŽãƬÓF‘†ñ4þ¾=yCÀuÍ—q×í±Ž8]õ@Úu?Á—}75å9‡´ÃsS ÞØí=J…!·;ý,ƒïeî‡ú íôíÜs}£ë?áþøõÀÊÀê;1ç]ïu‰´Š\ž ·-Jß7üt%3R^Êp~9Ú´ÎèùkP8y' ¢j­ø#¼ÖÃØº[bç´þŠ×‰~2xÇ•õÉ ÅxÚ¦Â3Å÷m…áqP©_-3éuøÞÜíâƒádÙ‹¶] .¬ ,ðói>ž¤h‡çp$ƒ77½©˜ Ñçœ<6ÆùEÒFï·#Týs¸z«p2.pHÑÓ+¿ç÷*R>WWÁÿµ~ŠvÛÉ7* _$Á[g¸“³[­Ø}uR8CÏ„íeã 4û£\^7œLÏÖ"h¸î9Ä\¨Ú 0¯ ðžtÿ·7ŸgÑ.ÿ>’ lßiå)Ð.õà½BË8:®ƒE™GÚ™„“~#gݺøq9éÁnï£G•!ùjæý@·~ðÔ¶­&ÛÎ’÷S´·-ÌúRkiLm2±Ï³~)ðð|‰´kÍ8œ¼£Ïé]càƒÏÚÌ€á$/àÔ Ù¾eBÞcþ× Cü‹Õ•鹎žÂ> Ö_Ñ~Á¡µr²á™ 7¡J¯;Lª:¾‰…Ê-©G<†ÃgýÄß›…“›SO®ºì/.ãŒòuª¥oZ?Fû{÷ÌD(ž£”¼ÈL†%—Å—eÅÂÊÆú•¼+ÛÂQÏõ¿4'o?^kxb™°îÊöøú¯ô¬XíÓGºžû¦DêUyÌ— ‰à^6rê©d°õ,~ ý£ be2)X4n?¡u8áãg¹Pÿ9Nòûü] Úm>t¨ÑÀó ðª^ý<#ßd aÃ[^ލ¬˜˜èØ: ¯×½S8ÙæuÐ#,ÙOȇÖËÝ㯻%Ô×âû•¢Ý§‡rp&”/d­_=š Ö¶µLz‰EAd×¢3=ár"·qN¦Ü{ƒé'¼Göœ½ç6­®ü¤°ÕïCã¶}àŠƒÁ“ÚCø};7´¿«O…‘kk&À€þí]¯7H†.Ýæïþt06ÚÄs¹<Þ´ù„gp2w½ã˜[‰Ë ã…µÓßUôx pÔÙ ~]S†öR|ÖÙ¸ÜQAÍîÜÊBØŸ˜ÛbézWÀ¢ºyæéýëeÞåeBüòu¹øúd Dìê:do_ŽöÏ´¬w§~€ vìë®L‚ý79 M,´ ÿ½§´`(’80ª‚¼ê6{eb×e„?G` ŒãœU:ö-·ßªD»' w_ÿ:X›-¹…¤$,]ir,tþbøÊo½cÏ Z&øã?WÚóîm·ÎËqVõÞ–Hç]}¿ówÂ]ð_¦ÚmÔ? >Vy¼×¶A,|)®ùeÇ„±p¡i|ï’b¹Ñù‡fE‰¯—Ø9®J—ìxó¼{y®ÚmmîØ«Áê» ÅâZ&ÁHn[ûM h·YŒÆA¡þó±wÍÃÉ´.uM·ø’?÷ñØs_ßk^i”ùrë2R´ØÝ¹ÅÓvwA™é!i¯Ýæ¬Û‡Æ¶¾Sß vÝÍ|_ÉÇSéºVv§|…ñŠýÍ×{µ¡}U»îþ-àB?ÅØ }xÿFûÍGlÚ>Zu NüZ¾®Jì]\6Ôuo tZÖ¯xŒgnY`f8é?;ãtI³eo0yzŠJ²´¿p®BëßhïçÚK’fÜ+¦ýF|–?ü˜{Ÿ-Ûè4䘥:…“)A曜— ã4[÷aû-bþ“í†÷¼3Ê×ð¬¬YTF"Tœxs»] ¬›oÔµî,{xŸz¬Æ¸®á¤ÐlÙ’m–ýʼnåçº:[ë×h÷ ‡í=õc²³o$B°þÑCÎbàÜ·áýTS´ W¹œP¸kTk_aìwÎ~WZa)_W ¯·G®þÜ*¼Yâ0eo"„úvoÞ¢n ¨gÆ'uöÂy瀄'÷R„[]±ñYJøyý'ñz‰^i‰´ûç6®ÆÁe½çÇÏNóý#GCÑ‚·3ü•ƒ·ó/(ÈWÒ =oé_ë¹ô¼ðy¾#”ôç?¿’ }oíÁ 7g½OéÙ+²\f·8ý">G¼¾Ü£¥À—ùaÛXïÖK…ß‹áö/ª ëüúQWá:ZEûŸ²¸…Ö8(Õ2†$­™Öó¨£áRêá*ƒ+„ }ÎWù¬ uÃ"®¶o¶ô¯uzvžŠ½'Æ›Ôú+Ú/ZšÙ©o\,øŒ^° 0æ½ºÖøEx484ß9Ïñ=<øµgOÛS 2íþæc‡Ü— ù¾t|zǦ¢ol¼æÇo:ŸCûC°ºrð‹…žëV¼—5¯ž°5ÖßÔñqéXÐb4/)Èa·ñ¿TÈl¿‘åçÍšÕrÃ[Ðy>Ï”£}ƒsƒÃªšÅ‚kmùé½À3ºáŸÓ¢¡Ãüª‘’ N`Ò{¥õ´ÿÖkÅe«éº|ÁÆU6jýíádgò¬œ–ºÑúòöpÏœ¿LÚ=šßGÍå \”šœVÐ<ïKøs‚üþ¸>qóeØŽpl|(pÕ‚ˆ_¶«ÏĪÑà?cO§×]œ xwBLA ¹m__aŒ_Ÿ3ÆMÔúï»é•ÊqG¤1°xû)£Iö °{÷¹û)é9¨¢6*Èk'7U‰p7;7hõ,ã.åÎgIОb|çÆ‡?Dƒû³„U,àe“JÙ7• šºÙª¬é(8›7¨SÊEÙnßí䦢%Âzާ½wÏÛðy)ÚY/Ño“|9{m{¹i\©ð•+aqpìÐOev0¢Û£—'£p\Ù~òä†cK„qwBƒƒžÉ* ã.;×Sîü Ú.˜ƒS”hØÐcíÔ ÐÑsÿ“•`0èñ“\©-ì,ípGAk'ª>‚_òóˆJ/˜¯÷jóþˆv%“³»äÔŒ†k+Z·±®‘U[I÷ÝX¢„)¡%c&ÍA_Îí1$ âfÍÝÑRÁ_øó•x¿C;ùO[FÕ¹¤„Þß=øCš³GMSBí^Gx¿QW§—„*Èç?êÞõ_JøøÓ§ã3?J´³-bU·®“”à4ÝpÞôW*Pö34©7Z “f·¼7„qGMÄëFǶ=›ëî‡dëRâùíÆ|¯`TC ;Ã*;LËVÁmuíFK{+¡FÚéU½k “éÏOlQeK²*°¦0αñˆ­«µ6TN_‡ë½/‘^©?l/Õ–w6ä&© kº¤ÊèJ°ß®Ê(z> ¾µ¹æø2©ü¬ÅX›ÊK„÷¸Âmû$uÎo´g¶öÌEí ¬KÑk0í=X^Ø*+!,l9h =x6˜ºÉsŽ‚X\3ãâ1oÁÛJCž¥ô«L÷-Ê­³JÑ.ςۚÌC·TXÿX7Í=C {Ëì›" t¦e“´Ù ¢-¯r½ÿš?2>(Û7-w®í÷ëR¯ñ‡>Q¡¹zµô– F^Ÿ]±Ä“@ìØUn×áÕœ ¤í<©¹y VfÞ¸Ìâ€ß‡ú(¬O°ú^|^^†×ùyë¬ÝºðÛ0¬]í=.©à÷ Ù¯-MÆ«ÅêuÉã Mèô‰‹+H”Å~sÓ=Þ„_ŸùjÃÎå^²ÕN|y¿E{qÕ8Ï¿ ÷¦O¨zè” Vç­ú:#> t^5cm…qЧꌰ¥«äè’­{+Y{“ÌÇ¥‹:èΙ³y“x}F‰v·{¸–»wNsÀHD¶_¦âƒ®¯ªþãýX8³ÜòwñA¹Ê}6pÝ[Ùyíýõí+M­X[OØ|Š¿èú^çéÛü1ÓÜcíCÚ·lj‹öQÀŸ3}¬ßÞ ˆQò¶à–Õ»%Â{åý¸¦pþ[¼ß®÷¡Dz£þ^ÚMðòOت‚Ž£»z<½ ‡LM¹öqp§Ü¡PA†»¾«¹.ÍGˆC>Uò;"Þ¯– ýá*}ëƒãnÂTümêâ}ÛhÁ¡·aÔ¶é_ØA?-¸]A¾;à¼Äç¯õè‘…‡æO¶¨$páù}a~=VŠöM'å7 ͺm&ªZEîPAí]mw{¯¹ õÇ,ìÞ¸?0æÈóÑ—üÅË,›ç¼5ñý7¶Ï,¾7´Ÿ²\}ºÛEUíQA­›†}®}Œ~`\šØ³Ïó3 ’êסvoáù0~*ã}³s`ZÿF»¯ÍRÇ ¹{Z¤¸t?|X™ß80ümPØVÞv,¬ºb•‚Ì·h5+׋ôœ×g„­wC`ã6ï? ÜU¿óy?Ÿ—£]ÃGm; º«„ŒÒPʾr»Ýnö ?,M[ €Í?¾µè颻_»”šý.îh]më¾)µŒ²áN¹¹÷ëZnÝ_‰v÷\ku¨þ­HhW¿lÓÂÛ*(È«¼Ö¯ÚmøØ2aÚ˜»Cáð`WóEC¤ãOšÕõ&ü¸VÆa—ryJƒöú¤åì›ß/²å]nÿ¼®÷ãÇ^N¹§9õþ5n ÌžtÓ`¬‚|z˜íï5X÷\ù|am• <4ý/žÛÍ£÷±Dz¶Û— ÒÛ×`À¥—ù›âT`¿ußÂÚknÁë ×çä8Bzˇ_WÍUŒªä6K0ËÛ¡F­7ã}ÎJÆsæã¿ïßxÀÚÜ çtì~ÿWª ~5_œki~ ¬Vô{[×™®S(PÑG¨øó…Å6ì÷Ðú3Ú›ûå¼^Á‚« ð,Qþ@­F¿ž]#õ&Èb¿}úš?rÍêìãL3Ç¢>wêéêFÞÎ[­7´ãz#`AÜ®+°âÞ”0…F)‡Gg6]~fžÚ¾ø³<w8«¢Ÿ‚dÏã>¨ÐÕ×¶·5ÛYAx_âó32´ë埄3¶ËжÑIMû'*°¸zżs·›p¥‹íd¿êöÐøÞ|éZáãUWÿl»’¾îv¡ÜþÚíU”°pÖÇKPs£_Ç•Å*èRË÷ÜÖï7àÑÈÓÊÈjv0(ßóÓô] ²ñ¥©óÄ&ºüÆŸgáë%Ú9=¥e~Ÿß ¨¸âÚ²Ç*h¿hGüÌÎ|?U pÇ¡õÓXwkçÃK;GÅÞ3›'ˆó¼ížzXûº¦F8:sL9MºÆ}¿yê-¿tkꎰ§úžÕÂä\Ç /.!<ïù³ ;/Æß/þSïS‰tù¦&-Ž ‡ØÌ3©`¿eÒ×Û!7`ýè‚ì×.]¡Öš¯f`%˰?4C—wµ~‡?¿µ–]LÔax4¿Þã… ß_RÉmß 8lqëõáŽàiwÇvǰþ^÷ÉìVK‰Kýw Þñ~†?¢yÍ3É)aÀ¯sª z ~n@…ÈC½–t…q_ì0ت ÖFoR,X*äe¶?Æï[D 甲’]«Ù}§g´(º Ë¬á¡ð}÷ôˆÈß*Ø\{ÿd}¯p0`ð·_ zÃòÞ¯¢¦c\Ì^1eâ¹ R~ýú==¯óAˆ?þúíÊû#^g`¥ðùÏ‚.€ÅÇÀt¯ê ÐÌãÆãnÐzgÈ¿{÷›¥ —¸r3p‰0Oü6?s[z)N]ŽvŒŸV>y‚í£ «˜$Àþ^ÖùÌoß ´ëjp¡‰«‚ôîÀ݉·à×|>zG÷ïk-†ŸW¡]Ÿ˜á%•΂õý-5<±ž÷PO^ÙïãuØÿlv÷J§G‚4ÆÂ-aœ‚´ö[{ Å8oá<=»ÏjšèsYÆÀÿÿqôù  çNøs“¼ÎÝÚ÷ïäžþ{¼XݶFݘË×¡Þæµ猆g\Ù>YAb~Ô³t®ºDØa×ã낺pyWô˜÷%éyæÁ¼¿~.‘†¹î/þó$ÌÒNŒÀrûh¥×u˜0copLþh _ß›¸PAäÝ޿DX·]dºpfç§Â:?.Öãó'Ú}Ímó|9ûsŸwMì–ýÒ^ÄÆY\mÔØžrädÇÕwíÌûö¼›ÔP›8?§ß˜ƒË¯Ž|*E»ãHÆ×ÒŽÇÀpC~Q¡uœ¿²9nÊ›H˜;àdiÉ,{¸9¤§s Ú5Z¹´Ãpo£_mÌÚ+ôN×èHÏÕòçúÜÐÞÚOrŸN8 ¯ >í¿d“׺|W»…FBÕ  ^aòÑð ¹Ùµ×˜O·Ø®œè#Ôëlÿ€­'ŠÏ ÊÐîÂh‡IʤC0¸êõͶ 0w»çfG¯HHùõô÷·­ÃûªÈ?HAÒ:ÚMòüÃܼcÅ‹ÕjÑï6ã„sâsÆr´ÛãyŒ]—ð"ûþèßã fÇçO–ÒHá»$mÙ(Ǽ:èØÌGàó—ÿåý¾úèÆ˜jÂþ:¾–Ö¿hÿÓîÄÎ>z®/~깞uª †;3îfÑñ›ˆÕì¼®Öoñçùñw䨦Wj¾& =«½,½F¿GómBkÖ{¬ ýìÏô»aâ#|çÈî“?Ò¸Üú”Þ—é±€Øê-vÁŹɫÎ$Àà«£® I¼¡ãïߘ3~½OØTX‚ãŸö|˜nýÈܵnzµÙµ…xcë âù„í»yÕ:b8r' ÑN¬à“ç¶Ò »®A÷„UÉ·}Fî;­¤(ȃ&Ï{N^FX=Èê6ñº¤í• ¸ðË 6f:6¼óH’•¾¼G†_ƒµÅ<ÓŽ@ó=qã†7ŸeÂ{â¿·©!øÖ_ÑÞÎ*#4÷m9MJ–N—£Öe>R¿½ A‹ý¬zMtË Ùû­TëÉ,®vÙÒ÷­.ß°} ñ9Úzxk±CðØÇ0O„Ê=·´k·ú*|9°,¥êÇIP5@¯aá÷;|…|Àþ68qbÖøþ?é÷Ùñ6lRü}³¯ÃŸûõ>®¡öùOÝëWº Çn8õàdx¯°Ÿw`)Γ±(SòâŽ_ÿxÏça´cÞ¬AVËã¾09·Êšyo¡ñ“'Ý¿m¸G¾}žqyÐ$0ÊŒºN–+H]ƒ¾ñçúû>¿|±ù½œK´Ëå Ú3}µ~ÁfËyàöb…ï^IýÿØ;ûh'ê3GÀÊØJ‘ VSVqº 7®Ê/E±AQBA‰ïSuY\l|À—Q«f[íFÄ:õujwÛñ(:"j´v'‚Úy‹UÖ`µd=ÅF*6Õn ¬l÷ûð{~ágëöôœÝ³Üœó9Ü?trïÍó|37™|?Ën{¸`>!’§®YþÛ §‰O,ýýOoB¾ì¹î?÷_rå'öJ}Xýž?ÿóá7œœ<æcŸÓMt;wß2vYåDñ]ö>q¹V:öÿö“%âí³þÛ[›)Þ^ôÌ^k"'ÖÙ|ú•ÝDZû>Ú[WŽûõ¢aâŠÍ_=ëŸsÇóuTüy5ÿ¾tÙÏŸ6=^{Ç÷n›7m¹ØŸ>P\"¦ì¿eäC'LϽ=âèÏßÅ·Þsã1ÛWv÷O>ßåëGtVúð~SÄ3 yëïzêïzúÿÔõDfO‚§²×î鄼ÑﲚØÖsGžòN—µÎ”]Yöµ¶¹ËSu@e¸;»Ã=*eîëÍkþÖ÷y&µ>Oƒ½4uîóTýÙ½^òO×Ù?]æŽòøZTIë[QþiGëÊ+puzæ¸ÓSuh‡Ü¡c7 -h–݆ îôT]PªÓ“º Jìù²ÙwÐä^埦Îá@ëf!ßòOS¯^Ë’þiêÖk–þiŸ{Z”×0y ìØ«j-5ÍkØÔ¼4mÍkØb?X™;\r=.wh“›¦ÄnCrӔؖbçA]ëƒj³WÖç€Ñ®æ<ðØfkΛý4MöP{ÜûB¾Ùw{êPÊ=«:¡<­¿Ågǡ͎Ã&°ÙEÝá—²æ8ìÏôþL÷;G¦'ùwUÖ.²‹ºKÉQëSÇ:õûa"v×äØÃHýÌYv1’‹;ËýÌ´9öרNSåc,r·Ÿ¡uû™ìDhp·Ÿêhîu"«¶Á®Z´AÆÝYÊÍXmÓ\µêvç.­";¼ìðŽ´žæˆ{šóìE žæûÆ’ÜïWc§·ê÷#‡MYsØ”¹k+«¹j©#5ä¥ÎÔ6W-unµ-骥ޭÎhéª xé•kÌÀòØUk"\P×\c-͉@®Zåkk›÷rU€IÞÇ¿–þÇ{Êì#/B™=6ä /±ÇF¹Â;ì‚ Ø©:V­¿‘Ý]Ôuo—ý] vBd÷}šÝ-vÖ–@ƒ½yîù#øöüiîknrÏ_ÀÞ±4{ÇZ ÍÞZ 1ê×ò5ïeÝ(ËUŽ« §üîÍnÊm•?7¯UNSF«|Þ^÷æ.e,åko®~Z¦Ržöf¨“ÙÙ›™”—½žY½¿ò±7){³p{¸½ìû´¬SÙ¦çeåe˜ž[*³è±ÉñsAë%OKƒÝÙ.h°Âîq³èÎlrfA>wö‘K+IäOaˆìí£žeêXN•NXÕÚd?¹_ì¿.&AÎW ŒÏ~ë†&¾­ÓÝàN÷*wº»ì{UÝŸä·"ï«ÅÝíuîú,³ÏÊçŽO²;¬\öR(wU»ÙSÜÍN}ž.»^ÉUE® ‡{ØÉGí°O°ÎAå;¡ÞN害7ºøªÜÇW ·ú¤gºÍžé­Žiåšbo+ù¥jì–ê?¿ê?¿ò;Çù•É¿‹:HQ'hmǰ˞œ›!/XDì ÌaÙ^8Gsn’˾,, ·ôìH® Y`„ÅÞMòG˜¦ôÙ“ÌdGrƒÉ%Ð,nÀî,8òÚkÎMg¤tÙXj„£¶97C`çf4ZºìɹI.{Cs€‘ËÞeçf !Pú’tÙ+97•;"©9À( ÈÕ‚$‚¢j Ek²1Éþˆ€„ü{{2œô÷(OrÒ–ÞĈ>EP6‚¦ÌÝðޝ¹|"Ÿ,;$:ìÞ$§}‹]‹äJ®;-ý>uöû(÷"¹’}öH(Wr›ý›¡æµÙ–çÞwåSïí,™ÞŸç;gžÛ|,t‡]Žä"'k¤1øeÐd—#¹“[ìN.Hc!JìÑÈôx€ü2-K„ ‰¥Éƒˆ\®XT€%*€°È‡Æ¾{å{­³ Èe§£ÉåHaÙÊš+\°mv5A X-Æ"–A dÙwßbùî[ ƒ xIó –µêÀÔ<@©/J—Z“]÷äsl²¨4J:ÕÈÍQ²¤ˆ|´eö‘[MwÝûìW˰ë¾rì"×}‘=käí(±k¼å”t®YìO&Pš=@í±ÒuO H±Ãƒ|÷©C¥\@;Éd HŠìt$‡² šìPö9\v%ÙDNGƒÊ v(A XTØ”eÐé“^ÇC©ÀÞáTU`"¿=Pgˆîï(jÞ6—ÝmÊÒ~ÞŸç^bçÈó4ÿ,Mú_­]¤›7†¿j I$öX?î²$¯[•½nKâöø‘{J'·šìv+ –~nò[  ²X¦€*ÏŽÞ6;z#Ä‚åA…—y,[’X¸‚æèõ@¤èµö_šÃ¤³,,dÔ@Š=½5`aA ìw³Ø‰Ùd'f´A‹²g)‡y‰ÍÑ[U`b© µÍÑähéè­ƒ½`jn·–ßcG¯(ƒ–æv£@p@•½Êí–ü²t2EÀ@PAØ }š‚Ã!ûݲ£)‹0 ÆIWù5C`ØÒ±Y¹5A¤4>hƒ,'ÐüM$¯9„ÈÕ€6°• ¤ÓÒéÔèq:‘‡3ÍÅÙa_oÄá•c_/yÞiž7šsºéYN9NÙM™LY«¿fHyI9IùHHù§»Õ(ÏÔk”a瑞Csæl/g®çÜP™QÖ2BχûŸ|ŸôáÄ'f´ÓP`/£‰Ýmì*=ŒMü ú;{baGêÀfÇXûЦóÌ|ó‚ ]ÛÀŽWr¦GI§kó—ÇÜEÀÄÜ¥0o>½ßˆùÊ`®|Ð+]­äg% ‡9iÙÒhà[&û››ô¾"ýÆOHähNâ±¶Ž’¾.ò«ºìÙ"_Wÿórÿó²—Ø9ž—³ü½¶@ïƒö.ÒµZ&†ßu`` Šì)4~ÜUX5`a9Š”ÀÂ’x lr‚¦æ›n4–§/"^¤<ˆ€…*€ /–ªÀÄ‚@ $±hP&Î Æâ•A¤±€>¨ƒÑc¶…ô@¤é:]öhÛXP4LéÓ.ƒ6ȲO›–6* ‰åu@Xâ"¨ËìÒ5( …¥vAX¾U`bÉ=P–Ý5ÂÒ—@¤±üeЄ@:”˃ 0EP‚Áu`" P‚ÂMA`”éoröpA˜"¨päAÄ’`!XŠ lL ´@ArØäAÄ¡ã€0>¨!”‡Q¦o›¿;›–îíÈ! "$w7ˆ@aU5vp;  „—ª …+‚* Æ’»J·?÷¼²÷Z4•ÕŸv.ùá·ëa¶­N8^ ýë-×ÞÅógSCí¼n¯ƒú¾üvÑ.ûýDÕÇ!}V)¡úÃè~<ÜóÈäÁõ'çÅ×vã+Y.Žyñ cï_û¸Øôò3³>\;Iœ2ýÙ—^ÿQoXì7®:s~·wAõ;sY7,gâG÷ösĉËdŽ{¾µzܺÍ^<ŽjuÞZ.NÿÙŸüö’ÇÅwîßÎZ8IœýÀ¼ ‡¢xª½^;?¾ûƒ{ý“w»ß¯ÞÓVÅñ†/ýÊòûÜŸ}Þá/‡¯aæ9ë¸[w¤^uÍ{“ÅË‹¨ˆ?Š¿znÛÞ8¿ûø¼¶å__øå…}¾ë[Ó{Dš8¾À£xã;߉¯þáÛ}SWˆÃ{ë{Îz\ÜYx}èÒ'éq‹â…SOÞ8dÆüXù…¥×ÒæÞ`y¼Ä¦ ™ÓFÏÚpZ9>î{NØÝ[!ÞøóÙx\œ}É‚{v–«Ì·̹$ŠwŸB†Õy±Þojáÿ§ÁÅ“Äë¾pȚϮëÆL¹âÅâÌÄð9ñ_…ß4ožÅ{ŒùÞ¹­W]Íì7ÛKèKÇ=ã e¿»j÷…ñäû`:™OV oóØ™ ê‰¿úîFãôÙyaºïs&~¯­©¯Ï|s̶~|õsȾªÖDÝà¸w^¶ð†µüøêü7_ôÊJ±çõ{O^0û1aûçÿã¥sóÂ=›Q¢ø…‘¯ÿlÐ5ó»=hªïNöœľ,ÙSÅq§®»0ùÝå~¼U£9b•8îÀM‰Û=&dõ,15·ì©è¢(þÎGþ}ñŒù±ìÏý Ûc)WÙ÷Óܤ¼kßcïìÊ¥ßX%ºüæ§§<‰W—ØÑø±§éŽº?÷ƒ_¾èºo™¨ü‚ÒG&{Ÿ›7dö£Zóÿº+ÎLÛ £¸Jœ7áßOšsN$®ö¦Ü8DÌèövø$1ãÑùÝ9Sý1s¦>ꪕ{tçLõkÉœ÷cá~ö9óáð¹ ÷Ä×M¼íà)¯­Ùø€ó_‰1K‡/½å[ÓEvß»Ot]_¶UXtU··NåÁœ¾çúNºÿ‹Üû${38®÷Þòïoto|ùíæ€S÷Z-¾Ò¼bñú=*Þ|oýÄ#儱ø¿óÃ(–Ç¿*önÞ²iïáï~,¯ç.ÒL¿qo<ìWŒyí¤Õbá!Ïô¨˜xû–&891üˆ—6¯|>ŠÇßzоç9WwóVz}~Çý¦œ«8ÞÞÓ§"îgŽÊþòÈ…«Å±Wé­õ‡^üš=o†¨ ™ùb¼:Šeoè5±òo¨ž#ýq p¼[¾>=ôà Þtã¬G¿¶ZôýfÒ¹¿·Hˆ/|íÛs>œ%ÛZðÅÕ½zm·GJ> ¯¼yù¿ºÿÈ®×në|â¸G­ùÖ( â5sg~´nïºXú¹%¥F/nö–Mûœq¦¸Ë÷ü†Šâ>sC´ôÐkã›X¿þ”IC¸ïSúLš8Î[®3šóƒø§Ýÿð¿Ù{°¦¶®ß Š ˆ‚XˆbÁ6P”;X±ÇŽ;ˆJìØQ,QA) X°Î(ºE@±£X°c¿cf΂{¿_;çž{Ï÷íý<ÿGßwoÆ kñ_³eüìT`˜:³ö€ö'¡ù÷»½ÜÛI ;^´ëË"|¾Ž#–ë½ZóX$š;¦“‘¦oÔ¯à»Ç—ºUä|)~:Õàäã²#以A•=TP'È«ÃAQìY0Á0¿Õˆ ŒyºªuO÷å÷½ ë#ú‹çMû }EÏ@|lé‹ÉGÉçÞ{tëú¨`À±‰ECŒ#áæ¨†Ã¿O“@VÖïΗÚmÍâny] y£ÍGÓÏWsœ³²ø(a}¨T—d¾ŒyüølÞªr(«Sнœ´³2H鲿œc#Œ6\ÈÎØ´ ÌAðUás«óã×üÃõξcä†u+÷‰*xubÜÌyŽá°ëåàiW7èºÂæµèOS¿~˜eøjÂúèU×.|nÖß¼sÞ¨ã3¾\(yt§Oòô¸xw—yÐ l—uÍ^9šûX—xË ë‡¿FÃ_‘µXã½¥®®æ}ÀxÍ]5§óOã\`Œß¡p[~GM}±~ÌzÐ9°Î«²Žœßñ–ßør=¬Mï+™ .G&ë¬ßx\Ó;Hi±æ~Þ×3ògÏu*ççÊ(¦òi XJÛÑwÖpIÕyŒq‡§‹Ë~…qÞh&‰! Z‡Yv^w«4Bçm?Õ?ç:/›­‹+çœ \*¡oßõh_³â{¼Ü—b¯å_ï|:NF˜n1}l&(‹ž$™\ ƒå3.·w²€{Ý6ûÊIáó2÷E7¤ÿÞ‹‚ϨóãÍ05ˆ9wì¡t§ù™ðÖþK»~a0¡ãÞCß\‡Â§™wVí^%'õ¿~rœ§Åµ|U›k&ÆxêÝ(œä†˜íع!†ÿ 2O5f¸ çön3 ßêñŽ”¾"ä•ÐÿP›"Á¸‡)¾p_8a<¾L\}bÁm§P×£ ¾]¡Í¶þ}ûÊ /I5u!øÃ«nUÎýÐÔ…-#·6x—R¼ÎsÓÅ?†“ñûÇ7=y#Žé/œó=îL1‰Èþqp8H†t{ÜENú½©ìuËGóÜ„?Þ¾mVùï/ñЦÉœAöÞÜ¿½÷›L¸¼»ÛL˦ÇàúÌí¶5Æ ‡ÖzMv•“ÄÖK>l)^£É/§Ý.òÓÑ ô%ý»û¢ÄøãŽ8YR#’Œ·Úwà^ƒ,Ø»rQ¯AG¡¨VíùuGÀ²Æ‘­“{ÈÉÎ7î=È+çÔ±ñK™ƒÅ§œ¶íêhúŹ´ÀȬ¤­¦?µ:Ÿñ:·×'f)·E’[—ß‹zõÍ‚whß#óôm÷šwGÀY`_{¹†ÓZ‘»ñÙáåØÓ†ži~u>ÿ|)n´?îv—‘$"ì\½«s² ¹qѰb—#PxdñÄ>ÃF‚Uû £§t–“Úak-²¥šñ]gßNæ‡Uàê‰0ÞÔ/§A™¡dÁ]uCK8s­E³1}G¥·ìn%'´[ë«Yk5ã{;ɼ.ãkkîãÄ·Ƈàýè1þ¹†Nsû†D‘sh'Û,Œßæ^¨q¯F.À8ÛrÒ]ºæñZ«y? ³É•ÞM¾>´–†‡*pØÕyŽñ/;o˜˜EjÑ×zBÏziž5ä0D®˜8ýĵ`Eñ_ ädÿ¤©ínw\K„ç(ôÏTç1Æé¤nÔEFFÅÆÔ¸“)Öuí±ù, ]}ÉÀ°ìóíšqÜRNÿjiÒ¬HúG?êwC§êÜ©­éK-ð…¾µêüÆë|ݼeøÃ(r£+ádA£¾w;>ŒÿÛe…+“»É ã¼”û‡p_FêAÚ¹…†^†vI%Æ_ûZrÍ¡ÒIb-3}3Áì6œ½üëž|ÂA8×`òy@oðÐWBØ@9ip}ñ ¨¦åÏ“{ Yþbœ‰øVk'É©´f&ã{߆Âým2ªŸ?ËW[÷˜³§¯¦?÷س»6³¶¼åRé…þÛK„~âBý ãHuÿz)Þe}ûsìÉ“ÄSïñ‰E ocÝ óý"dßϼÐg¯h9?g@g²¿JU?Íý6Êñ¶¿½û«ÆGÙxˆõUaÜn;ž9gMÓ¶›ûnàaG6ºº:u˜¹ûñäQðí핹{ñþvþ:3¤ám?Íýž£ð§ð…>üÚ|1^Ç»^ƒÆAþÑä„[W½%nÃó5½êµì³²vO«µxñ8(èWųfs9 *®;%ÑOs׋qS$G¹÷ªßÌœhr{öýj§܆F‹s›Õ^³ £î¯9 ¾í ítëã…“oÄÌ»¾šçÅ>g%Mßv!XýqŽÆ7vy¨7¼u ™sq)ŽÔï@÷ðÚUNî†yi%ŽÝ$ nOßXN²ºÒ·Vã¬ÏþÍøZ{~%ø_–Z¬Œ‰!ïšjuNvx·½píNÐ}ûé÷…a“áê=3û‡-äÄ7ü»Nh·òºfï“÷úž+1žéŒnIÍ-cIƒä&Gf:ßÛ!Ÿ»?–m‡ ¶Ëî<Ûnft“wmæ†mgŽùc@‡ÉïïÀ—çG›>vÝIŸ §T6-^2i"'¡£Ú¿°ªî÷—ñãGj|^à~3î.oàuò‚sŒæÉÉ#]£ˆÙ¦Ù0ﲎbüù@`ㆰn¢I¯R9‘T›ætTå«‘ê#CgÕÑŒc…ñ=ãÀòqÆßÞf㘱ùrâ&Z9¼e×lHk©”ÿøº¤kEâõ[‡ÀNsÚx^Nlöï~íXèKB,×{ú˜ óÃöÆ-2Œ'ûJY r§Í7ÇÎòahÙøw‰_üáëwQËž?\ Ö|n¢>r’ ;hY¥Ž~šºƃ×Nà¨óãöõ3é¹n‚‚5s`ÉÂlð“hzÒ>[¾Œ|ºd¨‡½x¿£•AÙчÊ}Íç¿8둌[É9·Ñ$Ãíû—+È,t­º{²a²4·ýÄFRX–`ðæRÌ(عí¾îÀrRoQäÑ{ý4÷Wà4 ã/¢Í£ÐÑ)É]ÓéF€‚,ì;Õ×ür6yµÜVùðJ˜åv·ÿ;û1Óæ{ŒÞ¯XrýñÑqN&~š>»ýnœ’šëUa¦ÂúƽõÑxÚ¾5 b¿¨û¤µÅÙ;ÎÁ6/j)ìm2[îžNë^>{KèjÜ~/_ÍûYàV \u>c¼»/g÷X¤ óÞûœ«v{¹|R=ž¹; Ü6^ {´¤Un,¡”õsÖåy&ð‘/¨Ô¸¸sضù|qËpV3ã®VÖ]¨tYZå·ŽjþJº¼¬Î8Þ_:–¤lX^oø_OæÕM|´¶2„4ޏüaã‚J1^›†wGÖ_‡y ³q{‹%wÁëSQ¨Ebwˆ,8ü ~»Á„Å­ŸŒ%½ém0+ ¼ ásªóãáËÛèê1é´3ÜtÕþ»PØÆáãNÑp"¬gIÏdŸì|9–Xú.òxì÷> ó-› ó{%Æu_ál#>ª îÐÿà.ôÞȵ¤ÿt‚æ’Eš‚óí·×Ì(Š%ßNmü¥JóÓüÞ‚Ÿ[Ô\wü3}?üÆ«äü¼Î¶ëÝ"ÜO*ˆ´Î¤ §Þ…ÛÝ÷¯yb³€th¹=w@îp0¿{ïX‚“Øz·„ëTÁßߣË×J%âïq§F,9§ +Öú:«ZÔõ²½0nç"5Õ± ·+tW/ËÉ^âöM ý{Uƒçƒ}BædUÖð“´¹c"Œ»õåRQI„‚¼Xýxî­v9÷öI‡Æ+HtÚÀ×O»À–[Ó÷¬%'KÔý5ŸS¸K}·¿cYYãÃçW›G&ÆëH* n`'S¯9&šèù,«Yw±q¡žç}W¸÷¤t@+c91SÕ\Ña³¿fž#ð²µ×Q%R„*ÈÃÝÍo}ZOºÖµp—…¾…N«”‘`W/ÂúŒ!Î ÔÀ¤ušç¨™o ù~`‘ª*Ë_Œ§û ËêåJ™¤šå€ãšWO®Úú‘-Qù/ý°n7[0´E9¹ñ}Ñ÷§;וóå Ò_QKÃuÓæúÊ0îú¨*¢7/äz¥ß¥ç@û¼YþóW®'ƒÆÈ·xƇi¯ü÷áûoTÞŠ¾¥dÝ_Æ¿Î7­}›VÓÕä›°^«ÎgŒÿvëæìg¿¤ku½gÅïr`u’Χš 7’ß E½^âxjë®ö;–‹%»[?»¯ÓÔ‰yÍ‘†C^ê²|Å8þÒUúõÅ‘Í2¬¸\{tºÒÖ[Èö‚Oí3–L†›ÒWŸÞl‹%ç¿x$/:᯹Ÿ?Ö%®¨ÞX_3oø<Ìר¼H§r‰øæY¯u‹#E¾/;aŸ ÕG¯4^ý8ˆl÷›o1ÄetU­Ê¸KžØPr€Ÿ†o'\ÇËçÕ4÷AØ`\rî¿x¯Æmì.ŠâHâ… ×VLÍ…ikkpiYûúÂjÅð Ðm¼Wòï'±¤Äcéî71åï£ ¦'B%¨[:·¹þUž·ïD¯ϯ5‰#ãwå‰vnÎ…_·ê:gÉŸþuòˆÍn0Àš®øÉɃ²{Uö×ä…wKvïsIÁÕ4÷GøÜÚ\ ^gC°ñ€âÈ¡j;¢sáfeŸºi»Ió_‹W<ß?RÜ¥Öáщëv.¿ÿŸf÷ÞBñ¶: ì;1~"[דbüNj C©2¾¾Êsad=’ñþáRàQ{ËýC 4Rÿè›*åõ"äßéåÝ`ìf]`¼M`\TîÓ7Ë#¥`•eé{ð\€ÿÛ\¸•Po–âÜ^2>ðÚ½æýaÆÍ~¢I9±dv¶ë4ã-Ë[‚ñikþKAU=~û|­<øÕ§Ëä”ÁÄ*åíš–N`ôDæ¿ïL,ÙCÛ·?õ×ìÓäÊÂ~ÿJ ék¯¾ß¿5ýüÙuÛWXЩR"iä€S¼NÈüƒ‡åÁH3ií·$ç­³N:Ѫv·šrüu,Ù2©–‹ùͼIø½Oä[Ãx½/8n¬d¬ ‹ç‡›=_˜û…¬¾So?ÏGxÕ‚Û䄎>ì+ÏGÆ-û¦ÙŸTç7ÆkX«¨}LNR(¦58ª.êÔëâ~bxDG\µf?ðþ¦÷ãáóX>/÷eØé}B¡ÂûOÖàZõå?ؼ_‚qãêNòa!' Ô/ò<»¸Vîʲo™áº¢Ê¡w[ëéË¢bÉÑ;“œòmÊóBàF°y‚M…õM)Æ}>ÞòÐo1d‘Ú0ó8§üYy4Hÿàªa°§Úš‘‰kb‰#]N°^GîýêQ?B¯/B†qnÍÌ~>§m ãø¨É ‹{д’³qëʼnëÈí3­_öšäK–/¸·ù¶á:Mž±õóò¼ý&iÐð|^Ç ï'%Æ7z”’g,šlèk`¿yÀ=} •²Ž"×{¶}Ýv$¤øí+±„q€ËßOYð a\¡=/Âø—çªöþ:IDŽ7k·Yt¼”ëß=yr˜,»üâÄ€#ùóˆ%­îrþ: üó¿™5+ æÛ{ gH§UKÄŒ/x’¼H0ÏÛ½ç”=me]¶MFÚ)³FFͶ.[ÔÇ ;ÎÞ|×µüó²|/Ó|^6î´äë@Œ!Âøú_e³â_Fň“æþçîA§`ónŽ™3ŸÕMê龯WÕº²¨my>>ä}mï“üjšy¤ö¾ºã›k•àæELs§5 Ë»:†W|ûn;BªÏ·š»ñÌ86:ørÆoMZèüöÿËz–Àæ{¸x_¶AþðÅîHÒ¶S‘þ÷{>h®§Å«#$¾K%Õâ—Nð¥ÚäàÁõpÞ鶸ަþšù›wT­àsRŒ×dXj O#H;Ã^­wšçT¾§˜×û()1¶xÑ8Á Ô¯ùrÒB]å¾ÀÆ54ïíó2ŒZ8¢ûëÖäúöêÍ6uɇYï¦N n§ (|[í—õkªÑPPO_qü¾¤oë¦nåã#Æ+ûPaý£ãM´ »8ºÏ Ò³ó˜)ùðR4r¨hû1òôªmã*#€qõäD­ô(M5èü}åW–·º%b¶yœ¼¨IªäƒxNÝV_ìCɆ+q¦ßFÛçÅùþüRÖ¯#ƒ’NúYU­®Ù7Òæž‰0^Îc©ÏÀ0rì嘇òa›Ñ§V{tÂȤ¯†õj?nÌ”=¶½œ\ÝgÅ€ÂýÓV_@›»,Æx3FÓ“Pò¨éæÃ7.åè Q:}î…‘êO&ܱ{:Ú^>Rš×CN:›Y¯Û³Ž(£6Ö»WÈö$ôóÜï™ÝÔöi´¯ÿ/IQ>TQ⎓=ÇÎ$Ö«6¦ÔådÆÆîz=Æ•×M†bq“ª·ëhÖsÔy‡ñ<[/K¶ëw”tt~€A娔TÓ)Òïy^òØÚûÆ€oËIŒOάŒoþšxB½x9Î6±­«á` œ'íý^^')+ CøÂ#¤ÓÉG[Ò, ØÃñyPÇpÒÖSÙÖnñh¤¨¸²DNÔÓ#yy= û|{Ÿ–túq¢"¿S‰q·XF®®®lÕ¯Öżxp&œŽvù¥ñ0.ü5¡Ët9Y½r^ª4Þ_ÿdûñ?ø¹’¶öŠ0.ÃÝÈ;/R9«Ž‡;YF6žŸ{½Œ#'+OT~ãõËŸ°u"Î;i¯èT+ß÷\1ýd·Ã„.ÖùÀ˜‚v|"HÊŽ·z= ‰zÃSNX¾­Óðæ„ûVTX2Φ²†)øªö{@„×™å\ïzÔɃ$ÕˆŽ àû=§ã’"ˆQŸÏÖs¢@½\ÔXÎ××ýå\…ð¾8WÚë‰bŒ?nP¥Ù¿« h"—ÅbüÊ«F*'ç¨láéôG£{ÈÉÈèucÝŸ ãß]&UÍi9¹F…ü“`¼–¶OvݵŸLü^yåÃŒ¨6òÍÇîí#‰ÑH:ê½0Ûñ=³¨çáJŸfúaCøÜÂü^ðöhWqý ¯ãb¶kv—sÁäwú¦º“_ùð,uæìH²²°Ó`ãR1¨qF …õ5aY.ïØß.4®ºæs ûãìßóõ4ŒßkÕ>«>/÷E·C=õïÃõ¶;I&ÞWä¶ì [+;]ÙÜZNŽä2?¶L¨£Ÿ¯f–¬;ñC§ÇT‰ñT¥3kŽÙMvÍ[Ö½…è>ç7G‘ [?ßt©?<Œ«R©ƒœ”üÈŸAÞ”×eܲó½†o­¢Y?cãT6¯(¸[‚)hn' ¿^4åží}¨÷nó¼ƒ ¢H-£‚×òþ0vXôåómä$kI͉Äåy(p&…ù¬öy>ê8_Þ”¸orúvBie?†Ü‡OÊoE‘6}d/òíœAúÎ_Érò¥GDÇAöBÜêšç¦Íwa¼€Ïž5ÌÜB.¦Rá}ë_æ×²ÃI²Äª8$oD¨êï©°–“^m~½ ¬ñ×yòDe“.»ÔÔÜís2bŒÿuЦoÍW‘z¢r’›ö’Ýô;IZo«›šõÁ†¹Wî+'‡í¯ü*)ð'Bþ ïCöžmW¡Î%WX 6±8tð>8,\úÀ;ù$é^x{S‡ŒmZì(ž!' ¹´ŸÛŠ8±dcù}Ð~_H1Þ»wúy^ ½É£T'£ágïÃã+*ÕÔ&=|’W}6îu§Wiç+'ŸŽÔ|9þÙ_ý¼Ÿ\÷ôcû|2ŒWU½ðº€<Õ÷?85û>\¸ÖeõÑdØÇؽLkØÚ˻ÈrâÔ-¨C·èòùÂ[[Ÿ—Cµùù ë ù¯Ä¸Ì?Fê¦ KïC»Õ[’úF¥÷Ñ~«|-à¾Þ¦vå$Ìd©÷€iþšçeÑBùmƒIU Nð5u¾bܦjðáx0¨Þ'Nª_êc=.Ñ„í7þrXxéxuÝ09Y¶ìæÔ‘þš|ýæß¹ŽEu–Ÿz%bv.h)¬ªYrã‚e!ÌXtcL4¹YÛäv‘ô–4¶—kæwÂ}œÞpâÀÊ^55õÏÎót¬ðû‹0~êÇAׯ?÷1˯í·/„ûA%ƒò§ãx?}ÞFÍÁ®ãÐU§ÉIÀpo£ù7ý5ç„?Ö´ë@Œq'áì~äw «Ã‹‡ÂÝmCÞ[®‰&«Û²6=a í“+]Ø/'K²{ör¡ü½ù¸S]EûðÂ{YŸoù¯×q“Wl„åÇU^[&‚°MúµXVy¿ž=Œ»£•ɉûÞ…«Ý–ßáOUÊÔê=~×|nužbÜs[ª•ÖwýÍŸj ^P”ýãÝK£ÉÆàù•gù÷è+ß#'àýu:fx°uùçøÖÂù mß–aÜ'C_H~Þ jŒïšB(®u ·¸w É¿ev`Åð¾ààËïEWåĹ`Ú GÓòç/¬ßㆠÜFŒûîüš+ r÷@Ѭ v†; !yÆŽþ-÷Ħ>/ oí#+}éésß“{vŽ~‰øÍ®Úww?µ†—š,+„áSôO—ÆsžúÌ lÙËÂ7/‘“s¿&î^âG„ýa~ ¬Ç ãv~ƒŸGÃø»oÚ±dêa Sk™ž;Qo.äιþ:†\MPn,ûЌޘÙ?ò­\³>Vq]ú·†®ÎWŒ×¢Š¸ží‚#ð¢NzÏsÇ Á-hf»ýøy)¾®ñðö½¶ü‹œjâ§yÏ ÷ÃfÙ‹C·U† y‹q_¹Sã1§æÂáy 'ÌÝC~ºÛÍXp¹7Éì)í»æ§œ„Dn»–Z¾þ/ðÙü­ƒ&ÏÔy‹q›¿{¿4Æ* ~΢¤óBxbõtÜØþ1dü®5³DöNäZµš­fÿúóùýtH~0õåˆÎ†šóaÎaܹêßã°«=xVëz¸žuûMjY.i˜8Ü‘àÍŒoú[àÞ–û¬l}Þ´ëL‰q7-¢0ÂáBÛ ôBx 9÷vjT4 ‘í 9—Ù›qû¦ÒUÕêƒIåq¿±ºæ¾ >®½¿T„ñu;—l‰€ä^tàRËš]Ùyn4ù”ßÉþÙÞžäJxèPq]­²ÊÜqÓOs?þ§ |kíñ£NñãÄW3ògGÁ+—!î>,„éD*št\p¬ÒEÝNÄgn«}š)È »C·¤+ý«‡Ê Er~þ¯>„®â:*ˆêÔµ÷'åù?Z >ÖÕ<÷}†Kd/ï [tá·#ËSŒ¿°ý·žgA?¬#¾º }ÿ'ŠŒL;°Áé‡Ù³Óå[Ÿ$aç¯ü5û>qû¶”èÃÁ6:±Ñ¼ÏÔùŠq×G´_”s–׿4¢Æ ðê{¼“wÉ©r`—ÝÃ.D½ü{ZAè·›FlóÿË÷V}leu®|<&¬_³óÆælÜ€×aç³ ñò6=?Bä•m×·O"Q}&Íy`Û€„. °óAýí ©?‰?tD½él½¾¾øóŽîÏÁïŒÏŵðsö1#ã…{1«¿Ãwb•¦ÐÄc¢åEÙG£×ÆÉ_5ãá~ûiìß³ó_:%b:ë8«8Cfÿl_©¬&¶SäHÖGo‘û…²í6þp¼›->¿õBl:^~ÞE¸Ò›÷%FЋ¾N íA8ß©ÎgŒÏöGÎCñ¼JG>•‚Óá.^ Q|ݺ+,î´(JT-ŽÐÕ°z®~YÇh3tÜÄÃêÁ²”c §¸ºÂ+kzÇå5ÆÇÉNHãˆópùòݰ~èã#¶åýêQEž(=x|'X(Ý÷¶¨U1¨–SùÛ—°ïYÕ—nOk¼]ì l_‘Å“`¼>çê·_€ãÒS¿+Ý-„o¦_²k~Œ"FŸÒ ¶ kßúê«Ù+Žxz§Zq¶ü<‘ðýŸ¥ê/~ ú힂ֽXcÜÀi úU¹ž{D–²«…ðv¯nò»¯Q¤Ýø ,£&„VÑùþqäóÛoicûþeÜçÊ€j|½/ì!¬Þ—²yì:2¼=½äyú"dVÝèvM^kšÂòÅ£ˆ[ Û[7çv!‹¶öèÓ/Ns>N§*Ï:mx÷‰gTÒû°vÛ•#.A—Ã<G !¾ú¸µM£¢H§KµŸ)/ö&¿îÝåïGôÕµüþ2aëÕÆÀ¸ÏöÀxÕ]˜cüG'÷¸v~drÚžBx8åží—1QdD‹”Âáý5÷£Ÿãc#¯–ûó‹ÚÐf¤­©O~gÐþ>¢N­ñûI§;=Ü{F6¡3ÈBˆ~[ëÖ„w‘äA¦ááuCPÛ‚þ †ÄiæÂøW/焸®ŸÚ¬s‹íWŠ0~²ˆî쨣6èBx6f‰T²5’Ä[õŸ0þvb1ãš®Ì5ŽÄ·ˆjV0½ÜŸ-[%üÜk¨í¦Ìß|°´S…}P1Æíòé|_ÉI¢>º{Ï*„=›õæêuÖ¬úå:9Æí :Õäòàòy‹ÏÙ§I —‚°$¼÷ÔùŒq3Kè%8ôdäR¢ CÌÇG{E/z|Ý$ïåââÁqdÃdÅúMåãàË+[ö]æ_‹ÎfÎcH1î9³*%vk”p4;¹ÁF:=í¿JAN$ŽtÚ»¶ ™cæÐì8æÇVÝu½÷õ+?óf؇Ã>iz܇Î9hŸ7—a\Ͻ›Ê†¦)ÁqÉÁ~ãÚB•N»Ä»"¸¯µ&?¾o®‰S{2ôí£Ÿ ?}5ãÆ›¯£ù¼Úó"%ƵțÐõ–Ñè4šžˆÆçö¥ëNþü>šËÞ~ñ>4Û(Å¥^¹Ïíø>Ë´ñ½òºÎUø>Æß2"YÇå |5¤ ‘…0Û¦ñ¿U¤þ»ç- •Չϗ­æu§Æú-µ‹KËï3ãº×‚ÍêݨïÛS³ ÎçÚ%âé£>4Ú~6™Öt|òñ>¨N8%u\AØ>F¢œø¹dèŠ8BwAÖßæG4uèVÿ]Ÿ¾FšûÍÖ;{²|ÆøêclyW ‡Øºÿ¶÷á²éùù=&b~”Mlf•Ôšlðn·ºØ7N3_îwíGç¢ǘhî û>] ßc|¶y:³¨u"í>Ü­ïXhÚ3‚X©Òìâöv"l<G’ú,Õk¤_^/l_\W3Þª¡>àÓ î½:¿1¾Ï»áîW¡ÚôØ>ïƒúøU1Œ QÎöÍç&ڒʃb¦¾ ‰#gÚ~¼Ò©[ù{\XRç3ÆaÛ­W_ö]¢ïÜç/Wï+ 'õ©½6îI©7†ã4ïé?߃¾îäjîêìüwÊ0~ìêJ—<ž_…6I·úÅ„Üýåq_‡†“Ø´ñËz‘5+£žÆöý³òõŽBë†1' ù|Æ„uDu^c\ywúÅëD¸õiµsà}È™4ªúÆÙáÄnÞ™!ƒÛ“€ü#Sl^b\QÚëIoþú~-4;q£tu5¾ŽÒ©Âx¶㓵É[šÍMäß¾kìõ°[8™ÚÎsù¡Æ]IX;:à‰#ÆsßµÖ¹ñ×ñ°*Ô‹PŸêü®S"¶6¬š¤R$‚_Àì“ïûÖaWê‡î D¦´ÿú17ŽlŸN7ÂÊŸß«q6ÆAOëþe]ÛODÿM|ÊàÙ?aRÝ,s¿¡÷aÚŒmQ¯0>;7Öf~ª}ÖŸ«úØÊnÿ¿¬ ³ïë;/Ü®Â~’ãÓݰ¸þ×ÀË-¼kºý}h&ôW¶C8‘ù;ü40é©&K —žŠ#“ÞÃ× +Ê}[ø=‚ÞØýu­2üOã?üÓCêf)~ž¡'çZq«’3Y…R´ÿ°”÷¢ ˆ Îf¥½I=y)3ÞC*I«g¸ÐC*÷&µã½IsyÏð`-¦œsÇÜ8¯ÕŒ÷&MâýV<8Š÷[ÉEÙ1^t‘V¾bÞCJÆ‹ÏE‹-ð õ8ÏP΋Òó| y¿pïîÁyÑ”gèRqža çùýÂ)ÏGÂû’šýÑ/œò|„þQ´/©6BèãÄV¼_8åùèñ^-J΋öÐêÕÈ{µˆÑd¨R”3ƒŒó¢]8ϧŒ÷m‘ñžÎ´‡”L‹-ûƒgHû’ºñ¾¤Bÿ(9çù¸óþQ"Î3xÑZýþ<µ8eÍr„çEó¾¤B¬Åasû£”Àcóàý£h/À@Ί¶Fã âýÿèáò—ÿãåRÿþ^®Çï“Àf“r6›õŒÈ@Îó±ã<ÊÙë²~XE¼ ÀŒqžJ«Ï´ÀÜâýż`ï3-Óâ?< çHŠx?@Êšó|ryï¬`TJlÄØ´ÅZý³J9Ÿ›ò|ôLXÏUM+°Ù 9›²iõ8›6‰³Ùž¹·E«¿² e­Åâ½ódüåO™»Vèÿ^Ê™‰2> pムï«L‰žœ¿ò'º”7¬zpÒ>xî¨$ÞÛöU.â-Ù?ã¨ÆQ:ÿ3ÆQfü>é0†V ghÙý ±˜÷¥½è)wÛY—ž—þ+‘öUä\D¡¯²Àà¦}•Kõ1˜ó:ÄœÃ-ð:„^õ‘öª÷@å¢ìx¿zÚƒÔ L†*F91Æm)Ê .œ÷¬§Œn9Ê÷#·CËŒ3´(ãÖ3nUœ¡E{*ñžÊRθ¥ -)ªˆ3´‚yá =•õxOå\ÎêÐî©LYã[*b}Ki_{ª”2аÐe¼w©ï©LYfXøî(gÜJQ*”A0ª傆 ç¦à†’sspVeܺ¡ä¼—)e#ʵ·ò?Zzœ‹˜À¹ˆ”)®ä¬ÊEÌå=•)CK`ÜJQ*”šM ïo*Òb#Z£ùó>§vœqKY”‹(ôÄwÑb)is…Þø"ÎEÌEÙ¡as¾­K†*ëÅø¶ Zü,êôí~Ê‚Sï¦~M}º®Î¿ïÉÿ?þ·¼XÛ‡©ÿÿj÷R¦~K½–z¬à¯²7/ýW*x¦à“Ô#…±õ>©ó½åwzõ¶?ÇQúõ¬È?<*NË“?Â’Pûš!g…ó—/elSnŸ!ú‹œ2µñáÉQ†è%úô¼>;ÎÎV¢<ÐKr9û"sù(7;èn¶ïÓN¹ÙBvC£rƪgÿ¼lÊý älÕ?ùÁü¥Ž’pæå§&¡¬9ãB†ÒÃd’¡‚1¡Ê,¯Çƒ÷Ux=´¯:å¡7g æŽ2£ÜSÎ8õ伊@*L²\”“Ú“®%ÆÄ GéÙ°žçÎÊqç}†v5gש¹ÒXgΘŒáBï`Þ§ÒùŸ1>²â¿§À2 â,31çôäjqzJQμß:åF»`1„sf¸Z9·Ç‹#¨:cZc‘iq£e¨2Îð‘qî„3çF Ü ¡7»Àò¡½Ù=kÓ=Sº/Èz´£ÄX`á¨R” gŒ–¡\°àä¼è(7:e†Åç®ÅXf"Î2£ŒQ3ÎÍå,3)çFÛq–Yg™¢Š9ËLÆ™î(%gNxrn4eNHQ¹M7š2'´¹ÑÅ"Ö×=U†rÁBG•5cÜè Μaá{ r9c4•‹£ÈP¥(74„”>P *3FÝ9s‚2F%¨”a[ÆLÐbŒ&üÁ23ä| %çQntgNPn$åFÛq–™À D墬Ñl‚P*Þÿ]àIÚ¡ùÈPE(1gŒR#r±-ïO¹Ñ Ãú ì "” KÆMËÎÍKÂyÇì/ÿÇË¥:ÿý½Üšÿwf”Äyk†•?ˆò¢õ0ñÝ9/šòƒ(/:s)=8—ÒŒó¢PzXnœM D‚J@™a¡x¢’ôéyÆ‹NBbáxÖ¤gkc#ˆs))/:ø^´!r^4åRz T(3Îf£\ç©´xÑ?(H‹Óö'{ƒ2sÅœ¿¡Gm(9Ê ÔóÚ‡\‹×¦BÙaᡊQb,àp”!]/D…óbvAÉxQKDŒmÆ™”*-vŠ3)%(9/zÊé•ñâ§¼Þp”š€* %B3r–›5åtP†/Ê®5cvqžo çÙq^t.gwiñ;‚PE(±+ZŒfŒ*C9cÂÈQ†t’³Þ¨ÁHPò?øAeœÎyÑî(%çQî› %ÒbRÚi1))+:UŠrÖbE» QÉQ†hVîœß!p…U(k4¯ ΃£¼ù?^þ—ëüÏðr;þ{,¸`΂sæ ¤"-RÊ¥*í1‚ÿ=Ê ‹AÎyHÎÕÊ™HvXÁœ©i‡E¬ÅÔ çC9Iá¨2”KMÆ&×ÃrG©PVZÌ$»Úô|'ÆC9ca¡JQÎX`rTÊ͈1†i±¹¡Pzuß\‰añyh1†œgÁQưˆ3†‹8 .³7ÅœWÌYpA¨RsÆ‚ Gbáz ’PfXÀRÎM·ÃBD5aüt©ãpRÖRªå‚.çEób§LÎ`T1Ê ßUÔœ1†ƒPE(g4‚pTJ‚† D¢)¸£”œ1ìJâŒaw”e†f!A)µÃÊ?XpfœÇ”Ä™L”Û©BYqn'幋9 N` ¡ŠPvh6Á¨\”5gw£Äh>á¨b”3g 롹¡ä(3LF Jù7¼&)*W‹×TŒrFà ç|a”¥çÀøÂIZ8êCôŸ?½\ðpm¿¦þü§7ãcRû°¶Sÿ¥¾+¬KRŸ¥ûçZ$õSê›Ô/©G þH?“¶'R?ü;ü·ÖµýzšàgúW –?mýÓvs á>#¬-j{õ 1÷ ÞDwÎI£ rÃ*Œ éɹ¼©zxSÝõèw®é÷ñ^Ò}X”5Öu0IÊj1n8å…{â1N®T‹ ^\—1ÏÂñ$à1Ç‘ÀYf¹ô Ö›3Ö™%¡c'¬­p”>0wcxç⃳š’¢r›1Æm8Ê3m­ðAÒqÖKg•rŽ­k"¸clÓ—© Ê̦œS›@Ç0tÜBÏ8te,CÊÁvÃÜ4ã¬0Êœ¥±ÆÿŒ'¤:ÿýÇÎüs–ÑçU‰±ei» ‚8SÑ“?œ€*UV•öºÂ¼ \ªÑ;øÿW£½[hÆZsF·À[”£ô(£›³ºiá¸q^·!*e]‹~ç‰~χ~§ï5ªå‚…Œ*C¹`%ð"“1¶¬›¥DbÑIPI(+,>O-¶l ªˆ³e9[ÖŠ³e‹QvXœAœÏèŒEÌÙ².X¬Á¨2z¾‹VŽ2ãrT)Ê…³e ш$¨” É•„¡1IQ¹(k4¨@TÊ*UŠrAÃ’s®¬*eèÀ¸²*ýFT°z¥ÿÐÔÖöñÏ¿ÿ£Þ-ø6>Î ãÁ£þ;Ƭ¶'ÿWüXðbêÁÿÊ{ßüVðZmŸ<õ_yé¿:§&x¨àŸ‚wþyVMðIÁ#oüžO“é”{áÅÿ•Òû¤Dy`r$¡ÌÐïÌ0I†2¬M{ÐïÁc”}*eˆ²¦ã9Týþ*&[]3Co’¡Êèwž0ùä(7:®£ëhô;NèGIt=ŽåP"LÊ$”5&f1Ê“S†rÃUÒ1úŽ!&jÊ “5 e… ˆ²FŸ ¤gí0y•tlG×ÖÐSÜQ (&³e‡ ŒcR£ŠQÎè'2:¶Ã$/Ec¢—R¡‡8c—¢\0é]0éÃéwÐ+”( @Ê ‹À½Á¥‡Å A)QfXR” CŠ*BY£/¢Jéw°XJéY,˜p^4n(=,w”’žÅ*¢ßÅ"*B‰±d|0#ö½í§ýÝQ%°ï”Òï!Ñÿ6 ØÏ ÿ^ìçœùw”mÃÑò¾æ¯´ºÎmá¼ÏDM°y6bÞ¢Ný4ý6h÷éß±q¤e£•›¾´ƒQ¶E~DàûÑëHñ:‹NnÊw×¹u£vv6¹)¶ÝöÔ, 'ƒ>Ô qæ}ãH—™ÆŸ|‡ }O¾kúÈ-^¸&Ñɯ ÜW†q;?ÜûÍkÄu8§ÓeàÞ_ÐxÜtûÃ"H¯z©3¿ö…àÄ7öëoÆ‘zQÕ¦,øâ«éC?VDµ¨ï圿Óc×¥ëØVè«®Äø›ÏÍÕÿ*»¥Ý0yV¦ÄÆÍ#„ñ(zƒ¢óºŒ¹¯âÈû3Ys㾚¾æX5¹X“õÇÀ8Œ7wbÿŠv³ªªÓ&ínylW:ç\çÎP¶ÙɽjÃxb{¼U»wR_ ÿbþø·;v>1!ù$.+´ ïÏÂú9ë–ˆk-¼[´zÔ 8c|~У“ ]¸ÁøqIaýüôq»ã‰±ñì†>G}ÿÒŸLà }‚»¶o¶1¹-ç8qÞ ^G+º|ú\x•s°ÜÂöí¼ø ‚Ä Ûõàç`+“—2èW«xÒÛiνë]üþÂøBßÔﲆLeñÅ¿]¬O o‚âkB·‘›  ùÓK1ç#87½ÉÓ»VòM7žŒUV]p?]‹§Ãyi¬Ï™üÝç—`|5F{üM˜°£Êò®^°ü”$¸r`YúÐu邿=ȹˆµÏ–¼‰#ã[&¾[UÍ_Óo_à±0«©¦OsÜÚ¥ŠÙaÖPçîïîi{97¯ã±÷çùÛoBã#Fó&€™Û<»Ì„õu²#‹t~^öùGêR• {ä§éc.<Ö?©žæy|± y×éü½g?¯g7áýJëÖøû¨ùÑäËî©G$Z“âÖ¬OöÔh¹&ì¯|1ÖŸq3”ïøyéÞÝ’ òà”9ƒÀ½à-þóêDu»Ç ëOŽž>š¾hß_¸¬¯)ƒääkѾ¬^ÆQëËöñ;“à*m×û#ŠoD~©Áû>êãµÅ“•£fV6ñÓÔéÖKé™ûæhî»Ð‡}S‘OÛ<çE•ˆyÒ†ŠIð¥’óüGOòaHÓ~—+5Š 5’†$÷o íw úÚ ž{^ÿ|y?3Ö¦r°ãEÛû«z'À6#Òz«ò¡sqÇV="ˆóí—Ÿìw‚•?¬zS5^Ã×î‡À׫ÀgÀx¿¦ž™>Ê?JU‰Ë¹%þç¿A(ÝÎø£-d[ÖZ0·f<‰¯ö6jñ‘rŸò‰j;Êmr]ÍïÏ>o' wNß?¬¬ÌÿÕÅd0[­;ûÆÑ|ØÜïñ'Ý}äD‡[Ý;ìëMÕ ËãÉ«ûoGµ-ÏÖ±6µ¥ Œ.0դ궅o{°|ƸÕNxš ïÚõî'^—ƒL«÷̾A®ž¬fú° +tëeeÕ·m<¡Ý¡RÏ”?7!¾À[ÖærÈ0îy›^S6TO—úƒzÊæåÃéÄÉ;»¾Œ ¬?WG8rŒ‚3ãI÷à!Ò)_ËóÍjHáøW®u¹Xãr°þZJŒ»rtÖÆ…V)qìñœécòat†Ÿä[•HÂxŠ–Ÿe×/ï¿Ìú™ÿÒpIgî)ÆÿÞìHûçÃS ªeàù:â|xü€6zŽ$M[öüd\ ž\Øu~z£x¢TYö»Õ¾œ§¢ÎSãñmÿ×­¦­J‘´}Tû|˜pfóÆ7u"y¿Ájd%^ÇJÇì<¯¼ÔíÔB½Þ?òûÚ´"ŸãÞlK/)`ýéÃñfõòaîÊO{Lu# ëGjF.‡ÚŒë?*^Ó§R¸ŸE6{c[FƒàgÚýeÅwóÌ߃Φð¾’÷ ñÁÓ\믄õ¯2!zò¤X‹©øœÚR'þkêŒl™×@3SM}1Î’-ÿüv,ñ:­WRBy Ðî^]rïÁÀHÖßË"Ȧ‹®ªójÀñ>=;‹'Fì,ª5Û_ÃÉL»(2yeqÓ, cKlv kRÆâJ1näÏ»ÔMïAÝ'¿ºtüì£$±5#‰î§ƒ“2làJäʯ[â}Q7Ðõ'¬yCø»Ï)ÃxNß^—(»§B×[F_;„݇ŽöôŒ$Œ3'†WyKöíG_¨?‡$ýËù¼»:/´îØPÓO೩óã>*nsâ¬TðÝT¯kðú{àß"qOâÎHr½ùÙµ{ô‚úuò;Ž`òæ?Â8:Þ0Þ:!ž9+Óž÷å¸ë¾+=” <*ßž}ö»özlP'Ьm:¸N¬ ôjwgÿ3ô…7Ë/ué+-÷'$<7–ÇÍ+rÇê–ˆk¯ïqÒ#5¬N›f.|.ÔíïÕûpQtŸøe…+¬i°ÙhCŸx²7òÔõ®åïíŒägº+ÕÐô‰¾y>aKµRÖM„qçÎl¯ú3æ–^ncÚæ8¾×mßü$1+Jûº¤áPþ~'¿ÇM:WØ ?w—!íªsnw‡ üX1Æ‹¢¹s¬Ö‹ì[ítkÛ=gÓI²÷çdó¶wûÂm?ßá§Çkú ã$!Ͼͻ½5³Ô@Ã½ÓæùH0>ã¤ÁT÷e‹×?ÊÕCCIÿ¬“dfXè£qÎöÀúóÅ“-‹M§l,ïÛ9wܒ훟ƒ0~Öî{&Ÿ ëô_öOƒ¤ÓG¦ yP5?ëTö瓤äÝÆ=ïEà:wÿŠGãy?kß¿¼—›ÖN2™ú¤&PWñ–µ¨À —aüz‹ÃéIƒ¸ì§»¤Áyp;eIçNç Ëk×xkJô¶uÛê¶ ëÛàYlÐ9_¢zt÷{{Æ UâÏg¿Z;Z’”7T:;tÀaÕLJ'‰e÷amíã-IRsËYS°n¿7íæf7 œ÷ËÆi||ŒqÒVU‡E:é Æo8åÁWï%ɾ·OóÆ%½J77%'‡¯<¾Ç’´='7zøññq]hTíØ¤ Ý+ôÖ1)3.}:ìºzñæ¾–yÐôMß:9×O’‰Í«ºÇ˜4 Ñä¾^¼†»+Ü/¡?ºðøJ,ÛŒ´Ãù ¬ªãtÜÒgÇØt¸ã<èék½<˜Ö ³¾I75@ØV~|ìzƒx’zqTΑý¾Äÿú^ؽÁ é¯c»i |ø1ûXOŒñ/$,¤wZ}“ ë&…ç`þìz9¸DÚÕ êu«2!¨}]~wzÖž\˜´äç"…[4éxs}rìJ{ˆiß³c=|o7 ÔÈKT>>bãC ï[›ó Ä¸)žón:èÛ6µ~Q.Lͼ\eÈîhÒ%yMÚ¥åÄÙ=õœI<‘†\ß´®©/ç”swŽ«/Öo³ãnMß:òÁW¼w:*¦´Üq/;šÔ]÷®ÃâH1¨q:¦ñdMÒø˜5ó|5ó !Ocœm±øJw–¯õJÄuW;¤ÖÌà}RsA5ßÓÚÌ8†óH |~¢•^“x²o <|u·üsªó~ÌëýAwÍ2à#M«Æ¹pòº/C|÷¶¾x¾;üjE;‹Æ“ÒúÎ.?Ìüȹ £­÷œ© ÂxKèÛ­ÎGŒ“;yù°&¼¿x+¸Ù0rI yQpìõÐö60Ã5·of•x²¼Œ4·ùë|€õŸ¯M+×>20½C…¾°ŒßÊåZ³õ3àä¢n+䀕߯ƒÎ›bˆ|)í@Û>g™Tú6N3¸¹gc'eäo ÿOû='Åxjhdz†Ù]8ÃC'_†ÄÆŸ­Öýš×h_GR†E®îñZŠÃŸ¥÷¬ªñsíÏ'ÃxýTÁîàóð<Óa¶°s¬_»uT æÕ÷ß:—ÿ)NóþÉÑ÷zm^ù8Í{ƒÀ3Wç#ÆÕÞ°ó{:ä\ª·ANÜHu^Ö/[ÜmxúêÇt¸9±n–y(¦Í?˜ìKÎë—ú¶ų•¢·ãȴວ’–¿ß ²ýì®ï«§¹lÞo_aÞ"ÂøÕétç}:¬®v'ÄõÃ]˜’#®:óV,1:ª¤ö’`{?ÎñGb镹ìaõçå}C5ë |ýBð#aÜ®ÎcŒO?]›oé°Ÿ¦Cú]0Á¨ßO“ô­SíÝ`W¡íåÏiqœÿ%Üïjæ+ŒQßèú“Z ÆK‡Ý|ÙŸÚ)çóâñrËû}ÿÉ ×î3,ÅxèðÊ$RºìŸ°yí]°[v'à‰¹‚¬2vþÁ˜ñ §nÀG>nè9àüL_Òq©ôBÿíå樂¼ Ÿ™†+¬?È0î9õeÀE z§î‚¬Þ¥²þþ ò¶tKÎâÆc¡µªÕûæ/âÈýj§ÜÛPJiCÕr~°wÞò{æ6Бb^s~Ægüá 0ë=­¥kǻٰpj—‡ Âúô‚“f&Ç‘Ž]rf‡†ù¡/ª:?ñ綪-{ð%Þ¿íÜ¡‘Þ]Ø‘­çVÍ:Žà`ä×C®Ð¿qã1–âˆØÌóM/­÷œðbùª§¹ŒoÌòIÇëÖÓÛ°2æÓéùfºWgÃWÛDwÅÂ8òµù•“‡€ÅàÛ†%aþ'ÿ~ò¶p-xÏÂóf~hŒÌçW—Í/ÒÁ[VÏ,î|6 ¼T¹Ý°8¢¿tmËÆâp°ãçI«2ãȵæ’,Öjž¿pð¹î'{~lFŒ×±W°Ó¡Ù‰Øçs÷dô¼Vù sâÈÞwaÇëg¨é}ïa,ÞßÒÔˆÖjîÀsýó÷`ó_‚ñëç‡U|N‘S§'¹+³Á(óÐÚþ5âÉ­­5†?(º¾ FÈÑw‡ïkÿÒ‡¶ö›%M¿_/_7üP›w"ÅëøSÂY:PšoI6401ݾ¸w<_' kF¸EÈŽÆ‘†—×-{|­f½óô¤«cÚLª©yOY›¯#Ãø—ç̸Úñ¾{ìHõqΆ©¹SóŽ'µ:ÌÒ9W{Ô<0¯Ê Yßû¶qÛcå÷I˜÷Þu'&&¶œ¨—ãÝÖ¸³f®Îw¼Ž¡Ã.÷äÇé0åzÄÝ«í²Á|¸,òàÅx"³¥àw(I\yltpyPTsg„ïZÍ8RÈS'œÚÓ†óE0îê˜3S}òÒáç áû¦ÖΆaw‡•V9E*Ý*“‡@Ùœs¤qšû/¼§YWs„ûÏ®Çy8f%bÓœ²ÁŸo¥Ã¡©´£ó 44—Sä—´z5C‡‘ z—ùhó”8r³Mج•ÊïÀaZ¼¶Þ»ç-t4ùª®Œ+ø^FÒÁ$-Ø·kÆxl¿9Ôîè)òûÙõ ±ËGBȳÆ'ŠãÈû‰'vÚºVóþbïk]Ö/´ëKŒq[‘¾­E§Ãò /£×Fߺ#3ß¿?EŒêzÙ^7n:ÖëêG¶5ßìs¸ü>'ß_Þ³Q]Íý8Ñ,>›§J0þÆ*Å_~†¤óùõh>|ÑM=OóuW°nçs¹½[Éêºsá™sk5ë6l½ÏØ8Ð…:Ï1®^÷·¿ã¸])ÿñõŒäXuÞ~õiMvzÓâ‰_éñ¬ç´Éqk5ë2»·¡mŒ5~Ké…]÷Úóù;‹/Ãø‹ÍÒ&7]’Sö®q¨Ï|[±kù©Óœ8âãVã3ޏ&’/v_«y? õÄøoõ4\jÚÕ¾{Vwà\$–çxCuCûtp‰0ž±°õX¶.ðiÑãÓdΆݙIÏûÂdõïÿˆ¥Í÷þ–jÞÂóý4'SâG=`ëõ7Ø<¡K…qU^Çs%ížWNt÷Ú®ôg¬ïôµæÂæÁ} Šâáƒp\Õg›Y¯³RÒ3Òeç­‡:0¹ÁŒþWÝdëh pÞIñeiðmŠ©ûãÛ@)‹Ïn7ß9†õ†AKÚúgã󤔵]—¥„­[èiøÚýáEÏåF™µw^ü ¦€”Û°yz—½ÊágȱŸ§>?éV9&I¯ÚÅ‘|“m›æiÕ¹× ¿WÕ‚Ð/Ÿñàø8ãZ|îS)ÿJ¨‡1{nø˜¤¯'W!°¯ë¬Î½úCû’_ ÃÆq„qFÊóŽñ¡ë[¯u«Örã¬¿ã–æ>î›ó¯^ê´ð6´»þ»zür²ÍK_¹þ0¨õ$ïéx‹8²ïݘ*Ï;6Þ­ lßÉ‘å1Æs[ömÜÞ4B§ñCoƒAUÁ×3Ä´ï©«ûªŽ„Å·>yoQ^Â8Oø¼ŽMy>A\N=›o8‚ý’:ú¾Û³|Æë|šygÕîUi@WY·¿ÍçCgIÀ(ÓA¯ººÁôô$Ñ £8ÂxoåŸ[Èg»^ÝG¬£Ég£Íþ=çðáuÚΫ– ŸÒ·ïž\í6|ÍðÊyyñ,Y- uð Ï^Lp¨GÐÙ>ÐÓðœ´ÇÁ"ŒW] ÄMƒOÆÓö­Ù³3F ½[åù*¹<¥àØ|h|ñÀªqÄŸ.ƒu”’°VËׄÔчm'w”Èß ßÃbŒÛ÷ŽÓámÒ`‹Þ+aÓ² Ë¢);K&œ#ù’÷.-FB@§‘×G~S9_×Fõ\+ýËóæ±/Càªóã³×DÐÕÔ¾Yp8±©rkè9²4ùþfø< FÄ”v0}§ Š¡»ß6Ø %Œ?§Ã÷Iõ5ë£Âç×ÞG“büÛ—Ï‘•©°éƽyM²ÀB¹Úó]Þ92;ᬑî gxØÂîÓÚ' ’K'¦RMÞ 2­žÆÇ¾€öû^†×¹5´Zÿä¨TxÝþé*Å—LÈu,îbµóœ·ãtu'5GAâ.ˆ¬Õ•þeÿTà¨óãíßK7¶S¡…à ߟ9™à¸zm“ç Ûî êw¬¿>IAnÏ<°>üªæs ¾Íöéǧã ;$§îÄTˆµ¬'>Ÿ éI×/yžì<•ùVéoÇTºœãu¾tÍí¸f¼&ìÇÐQYݾ×I§a‰8:_öy¼}*\^_”°9$,º}Í’ž'-f¿ž·&Î|Ö¥íî}ZAÂÖZdË}4\>³÷ÓZ™^Öç<àö,1Þ¯Õ7ÖUiš K7¡j&ôma> ÕÙóäúîÚ6çN‡ÔEoN*HȨÃQѹ>ðÓ† Su= XÞbœô1ôÍ• mL]³ Î^L_^ôå<ç6»ðuR¹ÿæ©C÷åïáy0¾†f=||ÍxUÆlö¼%4~ÆÑmŠRÀnpç+#2!àË•C á¹ÞGÏÒ5q8¼Ã¬=¬ lü_ÚŒ3ɬ1«²&_…uZíqã^Yè¸9çb ä_ý0\a“ šÌ‚//åÕ~¨.ÌÛóËêîVq‡®ÿ¥ü~ Ïÿe×€Ó}Ž2®¦ ãÍUƒ†RÀ½ú„1iz™à6l¸®sÞbÙð˜ªVøpð<ç4kŸ‚L5J¬=-ËGãcB œUíýS%Æ]¹ê–³ ÜUcT°£õJûÆ–ɺ6õ^ä {­Yxïï”Âǃëž)Ï'K&¬§ûQx’?ãê¡åesSà`»Ó… O© vÀüà1Ó/vže(PŠa½PI²ºu±ÑrÍ:ÇÃJ±EICjkî³°~"Œ ÔyÛ¨DÜË zh†K ´šyÉ,eƒ ŽA—Oö_$®5Š˜ÖcÌÓÛšš¡ ÓK\µI]£Y'ÆC1Ž÷æ>ëÊÖ·Eï÷øä­[Ä)°{ÍÕ¼è*hXõปÉ‘ùA¡¿ÅN päf\÷X¦smµ¦þYÔâçl*Ôƒ㞟ù>½›m X~ìüé|_<¯2qôÅWI‘íÒÙq]{ƒÍëïQ7$oÜäæ§ž®Òÿd¾#Ãøí'ŒžÒY™ ”†ézïì4N{TÇå1ì@ÐuÙÜnß4aó_|ïu:Uee¢¹ßl¿Ý F× ü•Å-¸ǂ(À&FìiºuÙ¢[ð©Ë…³.\"‹DsÇtr…lùƒº%#dÄòo½'†Jÿ²ßº%»÷¹¤àjü=׃ŸãâyݸDüÖücØYH†Iî1·Ý‚EC»u Ö¿L¢ÏÇ7lVwlr¥€&Y[|wîJ)ÉþÑ'/lLUM½œ_íó;"Œ»6öXR'æ½)ö9Wÿ|m·4ÅyêeòüÒÝ[–z7¼Æ(«³òzÿâ—ºô«HW3ÎÖïYÞ4byŽñ/[:aË«$Ú{M·Å÷3àõìGžû.^&v•zº7Lîõf¼‚ôÙëQܾÉb56øV•Ù:šÏ­ÎgŒóÄ·sÙ÷ëI`—_|HžAo6_TŸ‡fº$µvhºäŽUó}Ùö«4y!|NuþbœÛ5¦tÝ“s†%wWd@ìnÕDãY„äçgÕÙ6{8ü0¢QõI ²]šù4ýáJ?¿·%´{zÃÁ˜ï+µ‡Îs.tXÎ×ñ0>ã;%ÁŸôàHìºÙáäòpŸ÷xx¶é¹]Ÿªúì^©©kåÎú¢äúü|Œ x8}99÷_ÏÀ¸”·sx”m‰íß<lÓÚvÚGȱ¥”çý&ݽVy²‚0Ÿ·æ|’À“êY¯o ÝÆhËž51¾ü%感¬öƒ±ÓÌ÷^(ê6qÓ·>Åû™uuF½nUVižûøßeÎ×aEÛwtš”ˆû¨A¡7A=<¸’†ÔžWSÉ×íA’¢¬Ôã.}™2Ë«É*Íýž›çüÒì°ùS#>Þ6ay‹×qÚ4ïù÷ˆ›P½ÆÅö¥C—üz˜¢JÒeøÀiuœ{­ò‡  CK‡ÜžU}•¦ÞX¾~pò‹ùQCøŽê¼ÅømÞYÝM0^_iPÌÔtHö±ÝÙ¢µ’8,\úÀ;Y 9skå×W›þÁƒ"ÏxóõÒ¯šÏ«}ÎC‚ñ¾µX>ÿ&¤ü¬Ýw­C:Œÿàðâ°½R3Ž r ×ñ»œx¶):ô|˜71¸TÇuÏÜšßaÜÑ6<.[‘bÜ…~÷¾vº Ÿr÷TšÕ:žO·«åª$©úÍ×<ÑRô=óá§œ”n4êÞ£•„qà«jø‡ÏL¿ïŽš uoB˜õÛEÇõÓ¡±ßä.ËÜ•äpÎÖàmãú;ï§ •Ž¥†$ž.¯Ÿ,¬Ç çÕù‹q/Ʀ¬K!7 ç‘œÃמ§A‡ÉCF¶*IÆð¸þYýÁ˜ï2VðuyoMý2Ž*_‡Æ8‰YÊmsn€©o£]Ö·Ó`_ãà!{•Ä`p‘%Iýa=q  -&vþ:3Ä›¨Óc>ûy‹ñÓ„=Ü~_‡A'7÷sÀyx¥!þµ†Å*Ɉ¶'‚ ÇöJ[þøUN¶.¡ä6oMÞãPö^qáûCb–×eke§+›¯Ã­oNöNƒê_TIü¾íé>±¾#È· ˆqûÄIõ Î{kÖ™…ý¶:„÷¨:1îõ½ö:b&õMƒBzc”œÛBƹkª¯ ·è1ŸíÞ„ñÒAZ°zë¶ÑQàÔ>=šÅ“`<6¿¿l* _œ­ò\I::\è4)¢xTó!Áíñýß\<¤½÷_Ö¥.Ì=t*Õ˜Ÿû …Ñí2ÇÎìÍòã÷ÙÕѽi¥ëÐägÇœ£ºià„¯dïJ2rɹ+.]á婘± Âòy%aç Í4~ªÙœ£·úv.±?ËKŒKiÆU_ƒd‡¤8É£T8Ô6ìjçJRãÌ™ÝUµ#¾¸XAj¾]lô`ƒ×_ÎËN4蘕=ËLó¾Rç%ƽÜiaö²ðk°í¦ÁÈ$¨"–µÓ¹BZZuù}¨¶9<»uVwÐ2Ùü©Öà&^Y?cõdÂýéš:nÆ]±vGåš’kpJ×ÿnâÞTX8;辤Ê2ßÑ;júshUXéÐ)aû4^š÷À· J/M»™hÖIÔy+*wUg»™ôxÚÒT([aTé‘þ²iÚ½Ã_¬!ªê¦Ä}èÿ?<‚ ŸÖ,¯Ë©•çOI\‡å)ƉUUOÍU%ÂÚceCRAev`ÅðzWȇ$ ƒž_ »Êq~Ã~ ²Í¼îÔ+‰pEøóIŠOƒA}ŒªC*|x ¢Z]!l½²/¤¸J­‚mDàúªóî»×ˆí5¦%Â#Î6M+»¨Éâ+¤ßÑõ-î\º×·Ù¬À¼ëV¼µõIp9ô†åÆùðVéÌZWáë'ÇyrPj¹uoò2µv«ìÇÓƒñ½Å®Rƒy1øä=¹ï4Ô¬ç?·r7ßÂötš–ˆÍmâoMÛuzæ³-èŸï.¼ØÓøƋذ/7§/ľèÝ»õ8)3;i—=ΓÎÆ|~Ó„óš TF#‹Ÿ³ýŒ{ë‚BfmuúG[¯ŽmšK†¬ëýñ ùQÚkþ¡.pjÔ€éÓDgÓoM»yò:iöÁ¤?¿™7‚ˆ±£]ª»³û*Ƹ÷ l‹>$*ᙳãwTI¸žgkýºBz˜·ë]²¾¬ ¾4Ï~¾‚ŒÝø¤íÚ]žDØ8Þ¯f–¬;ñÃïC<`ãMŒ[{`ðìïc”0í͸>ÅÉЯáöîEU¯r>}°ûÚ¾ÇØ9 BW™: ùÕR3¾ö<ð³ íPC`ç4±¼Å¸l^Càk^Ò²)É0xîíÜI5¯vÞ²$j·f„›‚ìOóÿâ´Æ'l^`;rº_¼»ËGü)Ë[Œ[íý«÷Ñu<¨wå÷è¸d€ÓÁè*aû­!kI͉Ä r´æËñÏ–z!¯·³ µÛi{ª°óþI,o1nÈ£Eø .CÒDÿÖ)aÉЫë­ïL¯’‰Ñ \FϲºF“ Ò·Öñ)ežDª-ok êá’¡ ÷÷8–·7£°ÁüQÆ— ÉÕ²Ù!ɰjȜċͮy^þŠÑì@}|ÙRAè*é¿D˜'²|hgµëöå[W×iV"6yäàívaâë;SÖ%ƒrk而WÉîa/[n¾â½geÙDAÖWÝÜaaíDÈWá\OP w X€À¿õò¼µö©ÞXÆ'ê<Æë¨qò=/@V¸2¹Î÷^k×û*±ˆÙ´=ÿio¾© [ãÔ|aëu"ø½’.dt€Ž£;šoÉiì\bËcŒ»Á’Òy¸ð&Úy°K2¼‰0žuKHé{²²5_Ç?ÍòãGWÙ°'ç`\öŽ­‹­“agAµq-ݯ’Z–K&w„çKè RœßQlñNO¾Ðžüc& ®ÞµáójËcŒ;øçÅçuçø¼16Úõ‰²[y•WŽ~ç|ûÀÊÂ-ºëzã{ ‰iv³£‚ï´æÇ­ãçs1^À<ÑÄyãÎA殃k\L“¡žQÅV[®’Ë­D¶µBú€óÕ&1ý1ÆxUžr“Xج\p¤­ œÅ·¿¹“5ߎq0ÜÝ/íÕ7(:`vé {nJŒ_jBLžƒþ•ku[Ö$j9ГKWIÛð*§ôDb8ý³mÛ!ñý³-Qò«†æ7ÂùÖl›3s²ý¡"Œ·ïœò©bf8 ÞÐ|eƒd误_%‚_½èÒfË¢/ S ³ëý<âE„õU–W­ùøÛ’ûÙ;‡êê^nó塨i ûÜ:–%âL·ïöùÇÎBý§Í.×M†‘#v,³;•œ¸´bcÍÏ]ÁjÊñדÇ*ˆi{qþœ»^|Æš¯Ë7_ÙëGýû™ñï}½b~ŒqßMxàò8ó%¢MÃd}¤À(âÚUÍ÷$m÷>~š‚¬Rÿ‚^DX¿bç6š‚0žb~˜Âòã6êÝæúÖ§Á«;¯z2l˜EBŸ§]%[æÜL´YkËš]Ùy®‚Ìš2ÌþGWaœÖQ³ßÈæ= ùøçËcŒ{²Aƒ'®ž‚É5êìŸø% ¢mQݺs•°Ÿë ‘ÙcÆè¢Os©¼ˆ0?ílý>®eKùÃßí¨Û’b¼gÏ×õ1¹:¯1îJÓ·7_ï·…wjÜÆ¯0î¨wcÛóÍø}~âp쩸Îéùn0×¹`Ú GîÛxç®ï|ú ‡Úôkh ’¡n\ðêĉd¼é|§gÒÎ`Úo¥ëœ÷Ì·x¿'Ê›ßßbû©¦|jZaÜX„q«xírh–˜û&ƒÿämò?$òïÉuŸ£qS†ÌPý¡ó¶Ÿò&¹a_GØçgûÉ,¯[”ˆUu,ýû7<Zt—ŒÙ™ o;¦˜õ;‘¼) )к+4_õv¡jŽïî=ì¬"Âó|•;²¨p¿E7rÆÜÕÛUÇAÔ¼f°2:Þï³ltÍkd¢ß4{ÿ==4ï›ÛŸœrÖY¬"‚ï ÷}þ.¼^"Y^cÜïÍWä:Í<ôÛ&URñ½›=¯c|ƒk¤þ í«ZõkõFæuÝ;¥íOza݈­«4€o£é&6çq÷;Ô_ðÕyàJ7~ÿÙs”àuØ÷|àVᘧ}ž&Ãéo/#Åí¯‘fî‹æ®8Õ¦¹Ûå-ÎרyÌ•„íÓµaütµ#ý‚?¿ƒñöžÜè±Ó5 nl4œXš 5³­|†;_#Grkìßã6>FLÛÕYA¼ªÚ?ã¼’ß_ö©Ùý©ÏÇ}oè©“«÷ÜÀÌ»ãe÷mÜ¿ñ:Öç\§G …ÔÏMžû– Æ­z'w˜z„5Ýw­Žÿ@¾_ñ6ç»bå¸Ósû¬äókͺ0ÛÿyÇ|ã%õ¡ß” …eê/z¦€]¬ÌâÇÊk$Š,ÜÒàýl~lÙHô'å«C=ÛÝYI¼¾œc ýÔÇEpοøôšÃõíÓÜaùŒq{ÖÝß|Ï®cpÖgFØ€Ö)0óþþ[&»¯‘•Æ^ts†Ó®kOï¥àãoÂâYƒ°/ðØ)²P5PTÁOuZ–ˆ=Õ ÀÇ îиÕËF¥À˜™:“F¼F\_,j]v¶/Ô9xhÅ4Ìú­·]Ÿ¼ù÷ZƒpŽñyöÀNYk|JÏwæã‹M·Û ÎÏHf³ Ä-/_#úuž¼4:å’+]ØßSÁó`>§0oòNØïaë¢nü\&{~b¼NÉ‚C/ƒBŽÀón'í;›?ÞègܺÆ÷3ЇÊ÷°U/±'»´Ö]ÍÇ#`¶O:Ùr€µæœ¡:1[¯’ñóÓ)pïjð>‡¢kdiþÒó‚íaËï6'ð>«j'Ä­!Â8ŒÝ÷†ü|‡1°u•‡…·ÿt6Ëç5ýáŸ^ ÿôƒ’êü÷ï¥Ãï£R§bß`+Þ[\èšÀûA¹óÞ)f¼'Tïç‰RqÞí£’Ä¹Òœ+Mû©¢Š8WZà!:s®4-1ç—åjõ„*å=¡9óÇŽ3’xÿð´(å`Èy¯¾¤å}?­´ú~Úiõ[öàýÝ=´ú{Zq>nª˜3rƒ´Ø0BOOgÞÓ³Œ÷ô”kõëKÐâ|%ðž}žœóeÅ9_Eœó%C•iõí£ý˜%Xë œgí‰R¡¬z²þ}Å(;γ.åýöd¼'3íË÷çëŸñÛÿ”ñ›¿*”5&»g%Ö¿ÔŽ÷f.£üF-f#ícšË{ÔS¶O.Ê "UÄ™RT.gïsö® %UÊÙ»ã˳w)“Ã…s€Šÿ†Ù̹Μˑ‹²â=OsµúÙiõ>-EÙqfPïkO{4q¾¸Œ£;çï ý™‹µú3S.‡ g6RΔ÷D¥ÌFÎ7ãý™‹9w(œ÷gviÂzÞÓ©”½›dÁú¤zˆXï{=Ú÷þþÌ”Kn…Åî‰Rq&G gr½™©½™©8£Â¹!¸ Âµ_Ê?_zhÎ(ªŒþM#œ‡gïRqæý™)×ÍÄ¥D™¡©¸ó~«”yäJB‰Ðd,íoˆþ’„²â½”i¿÷pÎöqãl=ô 9ews†Oç_¸qþ…&Š;g`ˆÐ#<9§›òi¿d9gÿQ&·”óy(‹¬ e‡IUÚ”žÂû‰*mÆú²S[sÖ3YÕ‚õI¦<2ÏVŒÅJû©[c¢r)ãB‹«ZʹªÁZ쪌÷PçƒTÊ×÷GöàLU+Þ9—÷G¸b¬Ëp>hpCÉùàÁ¥äll)*eM{$SÆJÌÙØe¼÷q8`$ü3Îúgœ¥ó?cœeÅÏ\”&»•‹cÒËxâ»k±€¤¨"Þ/ž²€ŠPvXA¨âjŒˆ*â,Ug©:c¡ÈPeúŒ¥*°Ï$œ¥JnœTÊûÉË8'H‚’q† ç`¡¬±À‚ iOàòþòÅ(;Ú[UfLû52~P1ï3¨ÅǦü =,FÎSµÃ¢ â g3Ú3CG] n GˆöD©8ÓQ`cSF`#Æwtá|GZÄ”M{Ñ›Y0–ªÊ‚žwÇŸ±žô†Xܨ$”íIߌ±±­-éy0|œÄ.-ʹØ”’30\Pr”>L7”\‹}–ôûÌÂÎ Ã%Gé¡qH8KU¯ëcÄyªfh&¨$”MÅ•„¡¹x¢T(+4)*e…fô£˜óÏ‚¹¹qþ™ÀSUržª•‹²æ Ê?£As“rãll=4+ gcS†”³±­Ð¼¤œƒA¿aBùgÿU–Ñ?^þ—ÿÿÍËÿ•[ó4É%Z,#” eIÌß…3,õt;œ³Œ(Ó-A‹é&×bãR¦›ˆJ‰a¡H9 ÄšóÝŠ9ãMŠR¢Ì°€œWb§Å+qF3’£ôèz'*¥‡ÆäJâLì@TÊ*U†ræ†å†’ÿÁ'ùg<þ‡Kuþ{xø¿5·ã¿GJŒÉˆ*B9cÒ‡£ô8›3—ó›QÅ(;Îo*F‰± ‚Q¥”KŒ…„*æ,âp^$.¨p^,-&;g‹°x$œíTFÇæµ·“òÜQἨ(—.UŒ²Ã F£ì°Ð‚P¥F´÷?íw¯£.:續ûDyž.X€Aœçé̹O†XŒžœG,Æ¢ F•¡\°8ÃQz hÏ2ƲÂB Båjñ=)gÝ 7UÆ™P”ñ©×„1וMèw£‹8e%¢ßÝDïD™aq{¢TMé÷ªðï̓݋=Ð’~÷€ž»ÇÏ„*C¹q(e³»£’P†hn¨”!‚• Å¤SýÁ¤3C£pCÉQzhn¨”!‡;g¢¸¡‚9X„fâ‰R¡¬ÐTcˆÅ½EF¹—è+†ø€äœ gVó}®/>¬ÜZŒõF9änFŒAN9”"|x (3Ú;ã`m{¢rëÑ^Ê´¿0^Ï”öEÅXX×*zÆ3Æ)kRŽrkDûãà¿oÂâ.X¯ÁX›.ü¬×œ~㢬±ö‚PÅ-ËYm”¿›Ëy»Áœ¹[¦Åg£IÎ_Ên6Œ½«‡I%A%Ð?1¹’P"¬OÎiS¡ì°N‚PÅôOL¼2ºW€5’€2Ä$tGb"z¢TœÙ„*F‰19ƒQ.˜ r”ÝÛE%p¶š0þÚ?ãÆ;Rÿþãgþ9‹éß1ÙƒPÅ(Lz9ʳ*‹PÖXA¨R” !UŠrÆ‚¡ÊèÙ,Œ`T)gðÊ9ƒ× %Géq¯eˆEãÁ¼VX<î(9/" çYša1y ä(½:”sˆG•Ö¡¬9ÊOÿR.^U†rÆ‚ §{4tŒS—²QçÒÍ„ñË)ç’òËPfõiwÆá¥ìr/LÊ/—£ ±@ÝQI(k,Ô`sÚc‘±/9ÏÜšóÌiñJ8Ó‹Ø•„²²` Þ"”µ•+*gœç¢¬šÑïPÓïÓïÌâ*F97§ß­ÓQ¾„32)ûÜ¥B™¡HPJ”‚;J‰²Bcä ^k4)*%B£ P†h”e†ÆáÁ¼fh ”Œsx­ÐL¤¨\”5šŠ•‹²Fs D¡ìÐd‚PÅ(;4ªŒî'¡é„s¯šO8Ê È¥Ôâðª8‡W`¨‹Ñ˜‚QÅœ¡Ž2Ôâmš¡Yyp†ºšVgkRŽzªå‚&&VÿôÁËÿ•ÿCýïÎçýG}ûïüZÛ«ÿäóþéÑÿ‘ñÔßqzµ=ù¿âÇÔ‡ÿ|÷?⹂×þ[>ûwþJ=QkQ¿£^G½úš0vöxéï@}ͦe†Ô•‹£G…£Üð' $øP†ø •(>ì”!=k§Çغ"|ðž(gë¢rQvœ¯[Jý “ ½ÈU„²ÃQ¡¬0IèY>”5&XgK0ÑÊP.˜lá<á$¨$ÎýD墬1ƒš0î·›ã};7¥½@ðçÐWôÐOä(CLLwTÊŠî#· ßÄß 5˜®‰a²¡1aKéÙÎÝ-C9c˨Ÿ`çrŽw ªåNÇj˜¼¹tŸî+ÐïN`çÒù =—AÏÑãÙžþ£&ÿ_ýŸ[ý[ãªÿ¯ÆTÿÆSnüs”Òg„ÉŒ*E¹a’' Ì0Ñ¥¨b”&|0ª 匉Œ*£ç„±ÂyHP2TÊ "eˆE!A%  ±8êq£^*| ¸Gùåù0{òÆ‹Ÿ»ÁŒn”ª ¬ÜMŸM?¾7-F'7­Ø¿¨¥À9«Îz=µN…Êe•kÿ¨r°þëöà={£Î‰ b2Éw„…ó"ðáY_ñöú‘a¼Ö¢×>ÒÓûv÷hèœ ”>´³îuRòÒgá†>0¬:í`¡ >÷‡V[CF¿ë÷l;Xq>K;0ÿj?AÁøÙ:­JÄã_œÎsÛ°÷ÿL…Ñ6¥¶]Û^'‹ô~5=0º˜Pbš‚| <Ç½Š”¼¤mϵÒôÝb}´šiøFÍ„ÝiñØ ØÇûÉáuh×îzæÁð4»{µé±©`Qgõжƒ®“ð­£Þú8ž–heÄä탋v ⡚´ºçÇÕDè&ðÿŽš‹õWc<Ñ¢V¿`3\rm;øÁÜ40×çq‡Y7HzÔƒw;B]@}9PœãôÁ¬!mFÚšúäw“RÓx»Ë‹–š~+óª[~Ö—Uìk£cU"~øflŽûˆŠ'Ù¶! êëäwƒ¸JŽï8þ¬?,qßþz^'4’‚¤š¾^ìyÕÆûä°®—‚Œ:úªýÓUkÿÒ¯—ÕKí }€%WAhÃ¥° ÇžOÓà|eå¬{§np.pO˜•Úº’‚l®;ùµUF9¯Køü¬¿z9×{d婯žØTàÜKñ:º *ÕÛ*š'ž-ö;R;º½Ýùë\â ²|ÛÍYµº÷†Wg­NÁë0n{9°"LWÓüêåƒNþ>6¸A2¼ë:/_ØÝ¢G:¤Ô¶?¸,çùV½ÛˆÓûÁëÚ­«9wQÕÔÖ;b« ¼Æ¯¬ªzø¤£Ä8—?l2þØŸ°þòé°ûF“ ¥7ÈŸg—¼·›ÛŸcObœ+Mê¶N¶VÃë8çQàÝ©óãfö¦ ÿ'‘_´M¾:¼j4iîÀZ7IëS|TÃ4}•‚/ç6Ú[Îúª }Ö¨É;€pÔùݺDìH1dªdÀ—%5Þǧƒ­î–?;Þ$ŸS¦˜î2VPl¯ ?Vý¹®ÉZ’6®†ó÷:PØÄ_ºJ¿/¼¸ãù7Ö_T„ñF£O›MJ»-<`ò,N•tw²}“8š=¹­wtô»—\d¥ G(þ/Kª¹iòÏ~Þ©¥y^Ù^#<0ê ¬¯‹/ÆøýÖü4lØnéø%ÙUj•Á¹<7ɳiY=tGŽ€^¾ [o® ì~–çãÞ×Ôä]Ð\ú_8Ââ…küXÿu Æï{vÒm?ßÅäÑΡ̓çd@À×÷•_»IlÛ qÒ‚ ûõ¦±‚ zëBùýf×ù©áRkç™ã-<èsáÕ rçlÚ‹¸à pßê꨼q“({tžyÖÞfœ°¸Ó·†‚Ð×ÇýYrW…†Û+øOź6©À—cÜ.GLpn©Þ÷ƒNý¯· {ÆsËã“ëÛÖj™k•ঠ,ïü4ÜJG4ìMÖÃU}ÞÝ&ev «ô¬Vë'ë·*Áøú¾7<5fInÜmû¾š*²¢X÷Å’$Mÿp#òÜ–‚›ÊùËWãöýeÝžî^¡.¤÷ýJëÖÛÉ·½¡®7RS¿3‰´s¼bãñÔ¶müm´‚ô´)¹î2Ô_ÃAcã€êP%¾Ïö7Ï»ðº`\FÆ­c Ze•¹ƒ8Òvo]Tà2ýK‡·’ÈÞCß\›´ þ>F]f+ÈëZ‰ý¯ùÿ…c/|N¡ßž:1î®9‡·Þ4ØEB7'èt¨‚ù×|™¤áGÌêmõö±œüŽvsò8ÍûOà ãQÆMî vþmºzËX_½"Œßz"½»É¯S´aœ žßøèߢI2™´hÉ¢ã ^òhÛÑ 9éU³—Eÿòû|3«Ý²®MŒ Ž*UY,žNÛqÍ)]÷È»í!ßOv1ÎG6ñêüœ§“ÚÙ¬K&›g÷>xg ŒsV¿&'ùU¾s§œë,üÚ²Í!¤–ü£‚ÀîÛ‡dÎ MCF£ïîž—ð;ÉŸ¬>ôéA÷åu+p³„¾ˆê¼Å¸³ZÚçEé±ÜeC‰Y& éFŸ¯Bìæ2xñ  ôè3ï3ŽŒ²Y§©ca\Õ9°Î«²Žœ·„ñÔxßÖH¾™ýÃv™àå¸,#²M ¹üÓ¤™}Ê`¸6Ž6•“ÕèŒã¥Øàô£6ËGüùÎ÷KB/·;H\;‡n|4*¬^ÇU½Ü?…|{\½ÌÇb¨ÿõk9çK¬Ó0Þ¶ãÞÛf²}J½Ã¤ôg­{QÛ2ÁNË'¥}5Hb$ ½2‰ª¦ N?÷4R~¢:ÿ0Îô]³&Õ¾p˜¬Õ/8r"Îö¸0ùB ôYÝÀÆm4$íu¹òPWAœ»¾úr­Ç:²áBvƦz ÌÎ:ÿ0^Ä‘ZÊjN22WB‰™`‘°pÜî)$gtÿí½Æ@?ËÃcz~’“>{tëú˜”ßOÇ’ý$ãÝòÖ5 Êîwo;whÃÕXÎeÄøó¯ÿŠó~/#ÎÇO<¿Ÿ #oÝ×wmJò'YIìÝÀ|vÄÅÆåÄþÌ{‘Yî§×¯þ]_¿œ£'Ì7´¹2Œ?j]µ©yiGÈæ÷i©S¾dÂÅ/-{Ž˜JÚ…Ošï½È ®¦à¬Ç/‘»õ‰?Ñæ?)ñç§ö(m´ S¤A8D„XWóN%├0ž«qr2Z}#ÖýÁWÆçn“×Á2ʨÂû¯ãn§¯S›c¤EÓíg޵ʂÙ?ÛW*;‘J¦íèö­×èQЮ]‚E©RNöŽÊ5ðè½þ/|îÓ´ªî:&š~“µ?ºä¯weu¤Ó¾DìtÌ>ð`ëPróC—…ý²à°õb‰ß­T2øð¬Ÿ+¶‡þ7³“bNÉÉæ»®ÙU]4¾ñfÖ¬€˜oÆðiú¯LÛÙ=@›».¸ÓÔ€„0’äâ?9 öZCÁÊ÷©¤Å ©‰eÃàAývÙºåÜW4ù·ì|¯á[ëò~ªöš:Pç-Æ•Œ=¼Æù0²ÔkIÂhï,è×½×®Z†iän˪S&¦„ö±†DÈÉ„”¯Sß^мGZ ž:îéæsŒs>ÿS–I¥ã,f\Ó•eAýŸ?†ï·J#UN6ÎQõ5†ð0æ«÷§wªáT©óãÌU7h?N\=ÿîMRôly U±8´! ƒúœ!ÏòŽAÄQ9ÚrA½kâòûWÜhošÁòÚÐKÚGè||ŠñƆ}ÜÑæ89–¾hAÝ7YPÙfºjú¸4«÷ëìŽÆƒ`—5ÉIûà²cáË¿¼Ÿ†¥Î©Çû±_t°l¿û¬ån ô³Wç)Æ÷’__pxÔq2½YX^ÏÚ·aÖë35Ó—¥‘a¥}EU†BúÒØßâäd¢mØÅÑ}4¼H‹Zƒëަ¯©K¡­:O1î¡V|{œÔPƒ³oƒÕãÖŸ–ïN#{Jæw®÷È‚5òa\|)o6X@_ ŸïWbùØçËj¾Ðq’Þãñ÷GÃoÃë¨)Ëö_H#ƒë˜ÙòÒHøX¿ÎÛîr9‘Tivmw|€†ç©]"ŒsqLôರãdÂŽ*Ë»z݆~ç¶ÙŽ|žFD>×jw3S.)ñ>îÊÿÜëelø Âçº÷ŒPjkúÖ²þà6p9Ôf\ÿQœçŒ×é¡×c\öŽãäÈŒûPÿèm(¡ø³tbìYÉgcá÷#"å$},püá{•Ák^½Úïjjž›°î"ôUVç+^çjÝ•?¬:Nî.žÕÒ,ã6æç»±ŽCÓÉ%‡Œmæ ÆCç†Ówl—JÝÍè¼^3òƒõ¹e¼)Æs l;ëíæã¤Á³ªó—݆+_Ž YéŸNnÕl´¬Á±q0!uºÝ¡[rò~WöÒ^™ë4yËê n…uÆË.ÎÖ ;‚÷áèË1=,î@HÛî÷ÝϤ“.5ÇúÚÜF×¼ìÖ¾“/T/¼l×kæ?¬¾+AuÇ»ù2…M]U;æÝsðzÑÒFïä`P¯fù‹×ñ>"}-¹vœ0Ûø³ší´GéärSJ¼ 9.s:Å÷ö¢‘ Žîy¶ž°øF ä [Ï3¡žu’—¼8ž×Á8–Ïx MOÞ0¾{œÐnÞ7§Ü ~·¨Z-ƒø¨X»€²Ï³Ù=9q:ùVN»~¿kÀ…þ÷D³}_¯ùäÐ0¡Ê²”cã!ÒàñÃèQ,ß;âüÜëÙSùwô!õÄòl[}ï}~Ó ÂÞ‡a£üá‹ÝX7ú‹ž¾:˜¹ž×{5ͼ‘q£K˜?Œçõ9ÕÆx0äì+ÃÄÈöÄŽJ²;0äQõ>µzdœ•ªŸÏêô‡Vî;[”“ù ûõ.-[¯áÚ ÷kudì×)F|qaÄwk¥¼’û'ˆeþ×5s.Þ—ŠöU2†g¦¢É}túÃÃÞñ%…åije—íZp®­)°ùAþgÝ ¼ Æm16àXÔÒ¤êÚ&sòžý2ñü Ò@yÎÖ|Œ¾#1_¿MN,Õ`©þm7×éHæé×ÀÒá–Ûo¾fë¿·p«Ç¸Ò£'Ȫµc8¹¯‹“ûÙlÌ Ñ‹D5³ªñƒ›˜»6Dà 2.ZSùp0¢éÆ»a\·Åõ6 wÔi• ’Êgß<‹È áö‘Ÿò&¸@Jý2oÊÉ’óíZv³ ǽžÒÑ„ÿþ–P÷‰Bþ.ÒŒ÷Ÿ~ÉÖ1.]=>õÙããØ5j·ZÙæ]J™rAtÓ{¥+txd#y9™â‘ÞøgÇ"=¼è¡Ø¹¾†cÏÞSBõ'lëuuD¿*8x:¿C—MŸiáóÚ¸fLÑØû‘:®Žu‰Ø<ñ€lHí$seY'WÿlH:zxòÀº·HlúiñÃ!Y¾Ñ¶>·mâjV Âþ’П^X·góIW„q'ǽ“lº¾2Ó¹Þõ¨l°Ú£sd¿í-2tˆ]䆮pÙ¿FíŽ8Nÿ-ÚÑôæzMŸm!/X?K`¼TÎw¸â/OÓ–¯;NV&®~r7Nλf×|Ü-b0Gß±ƒ¯ È-¦ú­Û)'·Ú黌H]O„¾ÿ¬yh7>(ËhFs8t´õž3YþbܘÜɡ59N\\6Ž~£{ªÔ™•÷Æë©Õa–ιÚø?ÈÉ5¨·<#†(xëÒ\³¯Å¸/XþbÜ6¿ÞÖÈ#µëÍúasoöys~þîúO†@Þ-ºà*'ÝÔàð’­÷sð¥MAØ?ú²!¼Ãº“–÷/0îÙ>n[Zœ #qÖ\‹u» ×.œ]ý2þÙßaåÄe·ƒø¨¾‰BNÖõv·hH„ç%Œ£ÛôLìèݺâþÆ-Ùf•FÖΙSÇû.4œuÛH™Žq»Œ1­4yfœvùHi^»@"pk„}Œ&Ã&ï\‘ï„q{÷¯³t#»üîÝ8|\÷ 9Pr‹tÐÛnk2Øÿ/'£.ÏjÞÇ2PÃøÙŒƒÓ TßÞÝ;¾Èau• ûÞN—™è<À|Ù¦DÌúÚ‡‘¾îUæ»P£ÙÉ€**2N¡¿òܾA°èfÈúè›ÓoJ½Ó<´l´rSÀ—všý®!g·Ùt«ÐO_Œq¸§ž3F|fy\ÿý.Ü7}h{«™Šl²wNͥát]òÈ+ár2¢äg?ûš}Eç²ë~«£ç*rž0îÌE]ãÚ†‘)fò'mêçÀ¤)3jwS‘VCn7  kãwµkuDNX¿ý@M] ãÄðü5‡¢†«ðy¥wô§÷öCf†’e#['÷È3;æU¶Þ87¢€_9™{DZËy¿@ÂÞ£-4Ü Ý£”Á4<ÃÀÏ ßëÝ@=­}ëÌÆÓx»{‹ß?F_iðãçÔ¸ k`qÅ]E² ßÙ–•‰ôF_êjéWÐ7)Öߊû½f}Kß÷Ùx‹» O%ÕO?¸·aG¬y=wY¯"žGLôö¬™ý¦˜ö­³JNê¨4ûsÌÿ›Cï¸.³g[ò}Ö7ûÅF›Ì–»q+ûüEx§Ëƒ"Ý8B¦ž7Œ‚«9Pï²Þtå1I_Fg¼áhí¶ï·ËÉ—±3ŽŸúµž\)a­«66朜N'í±_L3 *_Yñ¹T‰ùÝ¡áÁ#&€÷áÈ>U°îgÖ§ä‹õ®›°Î!p ´9M"Œ»:ã~ÌÞã2¢´¤#Ù\ˆ0<}0_E.o¾´Ø¬ÕXèõaqîj|v°ó#“ÓÖÁO÷=›Õ¥Êñ^ø”bŒg»h1E‘îÍO»tÊ…kje|§">UOMËó 7lvôê“.'!ÏWí@>ù[ò¾™¥fýª¥½t[Mç < ÆG‡ã_6Î…&mÞt¨–I¼ÎGØŽë ž½æÜÃ÷ëø½æGGï×–šzï'×=ýšhòBÈ?¶ÎÌžŸ¯³§Úš‘‰k“±ŠA#æÂÍK*?©—IÆÓQ€|Ïfwá´œ4Vƒ$8ï²…f}…q*Ù:‹ ã9FÏ.\´ñ9pÔòÜ€\({þóÞ(ËLB¡ZCÚ Wårë©èeŸ@aë¾" çƒí 4/ÿ Ÿ~X|Öðq´ëF‰×éPµ»Õ”ã ×çB×ålì­3ɱÓFµ~4jîßãÖëÓ&S¤;¬i€†'(pW'œ;0Þ3ÝÜþ'bÝ:£ r¡m Ý3»zeyzÓª ;ªÝk<Ü ¨|œ$ì³°qncèáõêêœsÈH~¦Û¹Ë7Î8ŸH$¶óBˆ4!jÌöÌ\h_eÉ>”I¾½½2wo·Áàã?E¹# ßãdâç’¡šú8DM;}^ÞÚR3ÿRç1ÆkølyÇ/ûÈ’ŽÖmM^ä‚ûÐðwÆg’ ûñŸï5l_ZÎ׉ˆpnBà@ŽÔƒ´s ùþ2Æ[•ð$aD`ÒÝ©àÀÀjy`g°ésc÷L²x‹û¯^C á%sY—;r2öš<1øcy3>ºîÞÇ-@×ymœÕؼ1Ëa{ÈÃj«Ö¸qî{oIð:_vÚ8¤MÚC²¾±<Û2 'F‡yf’M‘ç¾M n=,µHÂy8Îjx>ÂõŒHõ‘¡³:ò÷nókŒ;eÓ}J&+»Šg‡»æÁÂñaº^™œw2 ²ŠÎœ)½('UöL¸þ(€¬¿Q·÷ôiÎ=Z9­éÐ4 èé²óÖÃR§Ïﻹüá×x°¹-Ì[²“ìö¡#„<8Úxí† {3IíÏø<jÑôÁºdç4\Ha?nnó½c¿:Vä¤bÜÈ5aŽÝAÒ)>|CÌÂÑ—íñLâÙ¼ÊÓñ×Cl«ó~É9_1@sÞHÈ¿aw'}bjWcY„q{Î~W#d;¹3m|µay´£Ö¸_q™äôœU©½—9ƒÝåÏi;tÞdaœgã³*Ñ3r¦NèÇýéËë.%âúu<%:Û‰z)ªÉ׬¾œIž|éö3o›ÿ\ rÌ¢Ö‰´ôÎ-lÊ9“Í¡VÍЕMo»°y Æ;pu n½QÛˆúxŽ*ŽoéRÙ>)“xô*ì4¸°ý,þ{w…·¿ûëSOçîmü9CxÏ4׌ƒG¾|Ýæ÷iÛ ïÛ±òeÒö ¥ Øx.Ô\Òð,òÅÌÕgª¾54‹X·#9BA¬Ú½“Ƶ —TX׿wõó–8<ÜþÚ'ÙÁG'Œ• ®\ßÈשñ:c²›|îsÞxî|ptg>ì ª³²[T š÷p¤Æ^îès^Az©ÎÖ¡Î…ùôÜqK¶o~f¬áΪóãV›¹®Ùý[G@š›u,âP>Ôu\ÜiÆeóá÷MŽõÄ©‚”}¥†õ>¼p~Áüë&VXÇ‘`Üôi+‹ó¥~"òáÞž ¢‘YÂ92ÿÆìÀ6?$ü÷ɯïö–¯K|ø1ûXö ðPµë[Šq¯òxY%ù$Mw§Õ¹|¸1e¥,"‹TJ86`ɲ¦¤÷ŘÉò†qäÖ×Ö[«Ä®×ä‹À…½¼c»Ð8Û ¾'øӷ·,¬t(ú7 l—u-ŠZÅ?›w<‹¥TéŸàÓ–È{Ð…€821Ô¥þåËë5ãwaÝ·Í8“̳:iüßüëEt®qÀß·,ßñ:m?ソ3 fŸüyðuf>œÞOF˸ÚdüxÏImºÅiæÿ7Xxž-÷mй¢èÅòã%ØÏ=;Ñý8œo¿½fFQ>xê¾ùu0‹t^5Êó¡Yg‹wãLû8"¥xÊbá}XW3ïpN¯Õ3z»%÷Glž<Žï[³Ï­cW"®¢qa㣠72Ï0KÌçμnÒ˜Oc<ÛnöÃW??1>9³2¾åCŸ@sû½è#Â:é‰í{Ròâˆ~赘ׯËï·ð¾ecç œH1ÆuVo$‡Ãä)W~J«@¯µò'ñs<7ŸéÞÓô)®×4ެN«^µÇÎõäi«l‡ÆÛZkö½ÞØ6ÿ}ðè 몌[Ò$oB×[G‡#f`839«×‘,Re›I·ð£½`ðBŸæâÝíx`²^³~(ÜïšÌ%ÖÉÃ+Œ¤—­/E‚ô€õ”.m @]Ž˜Ï«ÜW>™CÄ 7:Žt¡Ç©âÖñó–m4ãIe£G×~¬µ-__vkÜ=«[…÷­ ¯³Ê`ô9íO·ýOã2»€$tJŸúñYdâÇó/ww€]Sg[G|ŽO:«·y†;.¼o7M[ wø‹˜å1ÆË´¤'’¢áR㇧Šú@ú-÷jƒ/da]:PF䯽§«¥o_GÔy¤S ÿ™?‹ð:êã¿ûbÀ³MÑ¡çà  $-øþe)ø}æþyWsˆšX7ËÜëåd÷[7óët/ï 7]µg,L2éUjãV>A »D^Í"n2~Ì©-"_n¤GèlÜvû9þÜñÁ÷Åýä`¡þâBØØí*9‹×³«ÞÀ¸÷³.d}ã¡doY9¿Û詇ØÏ‰ñçl;ËôH‘ãâ6÷'Î*€¾Ëß¾¿Œ÷ƒåu/ÒrÛ‰§ÕcâÈ®‹=nJ_±Ÿ“àÏ~¯¸  €ÅóÃÍž/,€AÇêŽè|:‹¬ÈÉ ò™)&}Ô†GnÒã€.ëÕ?'ÅŸ›o?‹Þ ŒvØcy¤ŸðpbLI ̽°¯÷©82gXrwEû9þÜ4õ‚~ØH’Gµ[S™ª§‹ñ}5rÖï!Ý=ìˆ:ãâ==ÖÓýœ’ÞÏ1ÏÍ‚.ÅÅÿô/€ ËÛ E\ñÓÎe5èí×qðñ8²ûmƒ *=ösEøs‡“v†Îm=;{¾±önP‚u3ðž£"õx3ò[|nþƒcq„înÔMàϯG‰8où=s£ÍñÐF ¶/€+!ÍWÇŸ ±\ïécjD&4ÖY4&6Ž4ÚvŽ??ü9:x újmŒÎ¼VT îì¶–VÛ*‘ !ï-×\#qF9Ÿj²Ï)ÆŸ{âÛ¹ìûõS0OokjBDìØ¶cPté2x~„ßÅÐܑޏãÉ&ÓšŽOpü/œ‡Î ënl~~ÝÍ—êðýõóÆëÄöž¼òû’Óð`ÎÁ„F±p®Ùæo>ò,Rº@Ù§·¿%T÷êlbO–Ýœ2²Ëz’¸kGǸ˜TaýPðu`¼S­›%¶9FOdþûξíâð>©¿R ïû®M<Ñ‹RøŸ¶YO„q¹0¿`ó|3æô>ä®\ï‘v* —pòjX>ÐÙ|*‹¼ìpºÏÑo‘ëf,hO†î¯Ö÷ƒÎz"Ì7ŸPç Æy{Ã÷ªlðYøœáµ#©îÅîÙó ý†ùumrk–øRƒ®å÷Sð3¶~gÌ÷§ï;÷™}N+¾~`Êü¯2êpTtîY «| ð:3÷½\à¨È"óǿݱóIsr;Ôd©÷€xrG­%õÖóó:@X¿ö…svÂ>•0_Tç£}‰¸éKˆ! °ß²-ùØ;('Ês…ËxU âÅh½@Ùù˜e ²dF¥´ê\-8ØÒ¦~´ÑÚš*sKÕîÞPüHµh<­m¬hÃõ£ñÛ€È ˆÜ—k\.5È¢Q*ä¨WÇ/ŒÖûxŸwwÈYާÖÚÞº{Îï°gÙL²“÷yòÎÌ;ÿßa»ô…]¿»  õ¶à°í?žùV³ÝùAóƒg]T°Ÿúö¶=—Ô!?Ï»›¥ïZ|®¼ÉëµîëüûÆ/žgK)xõàûŠú†ã¿^UìÒ—Ýu­y4>b3£¾øåIö̩߯ïœ_°‡.9ä²úÐŽn¿¶ô]§_kþÕGÞ ‹óyýX×e`ûËïŸõ]‡¯Ò¯8r±³éî.ýþE×,ÂücÏŒ_½ðÜŒVû”ïm/ýkÁnNdönù°û¥Ö}#ÏOŠùñÛa¹^ZÞŸ´oœãyþù'¯Ÿûò­«ôÓ~øòݨÃ#–<ÃÁ¼¾ß¢çœ%7·3KF.ÿþw ö8=¾¬ ǽâø`|·¿ZÔÑ;aq¿ã^^w4Yu ÖÕ'ñ<íê7˧´ZŸ¾s`ð‘%]ú;[f^ôÑÏpüô•àŽÚ¹GÚ|ðnõ‘3 vë3—.¿÷Ížý%¯{Šëßo…ûe'\8}æ»aqüÓºßû’Åó {qøoߎ­Ö:moú÷.ýœ—Vþú?ï´Ÿ\7xÖÿÖ¤¯ÈÒž‚}ïXú€îùü—ï¿8®Ü>õÈí;w¾žö‹Û‡7µè²Oì«<Ï—†\8~ų«õKç|¸¥½K_ì›ÿV ýk×Që¸êµ©|_QÁ¾í¸üýOž×s\,Ï[Éóxb´+Žë±]ý˜]¿zÉ£ú‡4LâóVî© ˜_BÓ¼c§ë´ªmåy»pmKó¼T{÷ùMù~ˆóUo„庩}uÑöŠñññá«}ø¨¾­üé»?èÒ_:ÝœôC|~Í[ö~¬iÊ }ÁÇVjÐõ{MzÐßúàöŠ×}¿¦Üâ<ä[b~ˆíÞ4(²~í]kô™·<¿ãm|žN#q=>‡Síl]³kº^>±ôÔŒ‡ öŠ!åé÷†îþ!Æé‰úàgNݺ콰X—[óCl÷”ózä¢Ùé{Îýúì½óºô¹çGG-²Ó^yèšYëçú£Û®B‹(ØW:o‰ÑÞ=.åxyNûÒCÃpÃbÞûž˜b»Ú¬K~‰í>ÿ‚˜uãZýµS•Òf— ýß©åN¾/ð0ýΙE«« ö1·œ{È9µv;{̆ >hêžß‹ûŽw‡Eß{Sï`»?^{:8ÀÖغí¶÷fvéã¾sïÇ?ÛÒißv๽gµ÷,}ìèo®,Ø/æ[Ý{Ží°•ßÌ{6nÑåç8>¯…Åú¤wÄy)lwæmK›ó7ÛúËý[OnëÒ´åèà‚Íö¸Ù…@Çöì3ÔkFüaMÁþãÑ‚¿{á=s2kŸÑtÙßåß/>¯Å~¨b»¯ÓaÜG¿nýö/™ˆ¾uËØ³6àsýÛ:þç#Õ>}ÊÓ¶©`¿6â……×-î°å}i«çn¹!¾7ì]ßâ›öŠñ–Õ2±4ÏÑéjSrL—>ô¢um§=mùUÛOŸ3ÒžñúWiOìeœõF1×a/ºyÎ#ËïiÓÏ©½Óôƪ]Þ×±ßù'lwö¾Àý¡Å‹[2¢Kp˦ÜMO`¿~ûÆ'N¸ÆÏ× öù.C‹íà㤠Ýë|d}‰ÏÕ]Ýë÷_l¿²uÐugýÞÑKw޾\?¦K?|±2»®Ó}Ù§ñÈ÷Öì•WÏøÚ²éöÍùÆ¢ó&ðzÍaú(}ìKÃ/†½×i¢Øîèaæä—þq¾¯Ýû»ôol<8y æËk¾ç>5çŠaúŠ–wÓîc^_ÙÞ}_ªœGˆ×½“×çÿ§¿Øîq[‡Üž0Öéƒél‡¯K_ºs×sÇñؾÜcõ{Ï6†|ëq|>ÿîk·¼@δîu9¢Ž·‡÷[O‰íŽ;aó,õšuúW®­,|c‡¾zAÁüÑÆN[^w~ÁìóŒg?ö}·Ý÷NÒÏ~åž'Ÿu^Ê×»Zxôe%ùú²(ÿž³(eæ*íKÇ'2ÞÈ¥P✷$ç¼iìW­°_5ÍJ&ç½Ù¥ 3ßâì&ï` T=Þhד]BÞèŠ'ËÙ%çoû<ùIÒ;˜cot„}8ÞüízCþv„ó·½Þè*{£“œ '½Ñ5v)È|^™mB½IO6\Š}b!Îà®5¸_#ž¥(gpËLʲ'“’r”Œ ÈQ"¬Éyqä¶FŠìÊ#ÿ˜Ãù'ä®xòO*œ—fo´éñZœGàüm™G)³”¼ÎAÓã4Ù…CÙÛ ÎÝöæPJwl½—Y‘ý qÎQª²CÁdg´Ãy”{d³œYgp®p•]²2[˜rXv¦<²½eR–8ƒÛë”NZUBîÁ¾žÞ×Ó“¾/FOWx_•|"w*ÖO¸d){*ÅÙS!ö4VÙÓ˜áb ?åP©ÿ 2ÐeU‚]²ä8KƒšÇ%KŽ3E“a—¬‰âÉqE9cXa7ŽãqœåÙ%k²SÁ›1L…æÍ69cØë’­±K–geKÖåÌu™õ™ Qæº'³*Í~ƒs†{óãHŸwŒs†e6^Å“G^oòã×›ü8çX‘K6 ŠœgE~œ²K¶ BhiÎ58ÔÇNé8‹r>^3†e>žt{ýf–Çof±Oò…“œ-ìÍÆ“~Jj*NøÑdì¯q.¼ÅÙ7ŸÐ$á«ÌqŽVÍ(ÃYñä¬ÌpcŠ{Î2 ¾ÊÞròÊœ3ìõœI¯ùdÉsF5K_ÔÓ?m?§Þ-{4õbê¿´+©ïNðõôÚ¿Å>úÿ¡‡¨ROüK÷Az³>‘³žá~“sAýìyt€¿¿ði—Øó˜âœurɬÏNzp?óš2ôâœG“€{¹‚ìW”>—\C&§Ì7wØcgµ×¯¨²Çº Î pAƒ4 ”á"7<Ö^¿"å›§8ß<Í™œ2Û¼LvÙú=¹œ~Î6/±“Kf{óô(¸ü5y\d&±æÉ$±×'ÜÕy L ûÕ° àg ùÝ-2‹“òÊ¥“EflÖC­Uû]™JçdñÿÀl¹}T¬û&¤_Kú&¤_«o>ø·ÓÇúæƒÙ>à}QA øx?á¼U1ðÓ ÆYîÎI&_b–}‰”é^Š"Ê@Cq$ÙyKŽ­ p=Î[*˜ÈráX vNÙù`ï·tlÙyk±s›‘LYïÞŒd‹3’½Î[—·äتxœ·uÎ~—ù¥IP!ì˜4¨ƒ3L]v†g{É€·<ñ8g%“/11LøC(êìpá§,xò%ÖA”2MÙy àc  TvÞÖ€áŒÓgœ*윎-rN”Ù9‘îÅ•èõkE=~­(û&(+9ÅYÉ!4’4¨y‰J/~ ´Ðý ÂWNn-µ•Ö ßm(tLϾDÊM®s2­Äï³3‘rV4¦8(³g+ÛàK ¢Y%@¨mtíCx'´Ï–ôNÐJòlQ]Ñ×gÙÓ©Ÿ½œz·ìÙŸ¶_žÙõ=ºqÎùiúìzl·S÷‰ú,ûh)‡Þázò–@ ¿ðŠ—Ùc˜æzËãõÉ ZÇã¡•²{hU à$¨pþ|TÁ.q÷p‘ùœÀ .ƒv¨ÜIÎ{²kVºËâ ®Yò—%<¾? !O¾|‰]á v…{Ý„»Â« ‚‚É‚:0Q89àGñĆ W8¹ ÉK¨²_–òå T¸ž|y*.«Á/[bOO‚==¡¦žlh½2Ó)ʆv@`L#œ=2:äɇ6Ø-['üàEàGÇ&Pî¶ÁnžòÜÇ…oÝ×{“¾/Æ|:ÄKhðÉ~ÂEÂÀÏ‚:]/cÇg=UäH â  E TâH³‹˜Üy9.é"&wžòì"ŽP@‰Ã„÷“ë)éYgw^‰]ÄäÎsØÿiù…ƒ=0XxØóÀ¤p±; .b*@‹Ýy5‹XAAZÀ* 3 \AfA˜(Ô«ô´+(Ú((ÿqÂqå°4éqƒ¦† 7hEJy  ÀãÀ!\Ä P* >ª Ä.â:0Ñr ,4‚<»ˆã^B%¯çñ‡Jÿ•¯Á¡÷8ôÈC\H¸ 22± ºv‡†’4•X3ezá{4—8¨M&Íîxj6!gq•=ò&HM¢¼l“ÜzhFynHÝI÷ú Ÿ^~Šð™&Ùiª¡Y¥¦Ò= x4­4¨µÑZèý}zd¤¢Uþ”èB>=ª_úòúôzsé5öp¯Kz6õjêÓÔ—e?VÅ®Ýï¼.õVÙO ß_·—6öÑõÐϺ~Rïòè[&ûÓ víU1€RèIÕÁ§'=Äû:]ê=è5E q¨è5IPaÿyŠýç^g§Áþs—®Ia@æ‚>ED¯I þsrv¦‚ÂÙB¯q9Rø‰ir‘Õã'&Çžô«ä%M^7ìƒ&òw¡¦€‰Ÿ~ô–(uLï\C1d€ "c)/£Ñ_öœ—ÆSÞ.^#(•ýœúJ¸ 2‘2Üð÷Ñu&šžL÷¸ãçè 9àG‘%¦Ð}¥øû6•©t¿öð£/ÄÚè>ì{c8äCFQÆ¥PR\R~¿¾yß_¿WE}}ó¾ÏcÞá×Z! øT?áL60ðs<ø-v“RÄú ¿{ÅUAQ¤A DPv&[(’ jÀ8^ø“©ø-ç&Eö''@¨*9íð8•f=ÞS ä‚F¥“ȉ‚Ç2PÙ\¡ñ”K× L4’7“(j”Å‹m€R3å–âq)§¿‚&“e ´R¶þýɵVᦷ@”¸Å@(hH&(žL Ø L4¨âáaM±‹5„f•µ©tß&ž¸ 2îÿÂ÷ ‚&–ãFFI{y]ô)Ÿ¯g¾'çx¾äOêÓ½õgÙ›eOîÍ“ìíÁ½ù‘{-õLê‘ÔåÜzõ=êsÔ㨧Q?£þE}ê_Ï|Ê;úSú õêÝä'ŽÓý>Øa5ºv‚:÷cÇå p£““8ˆúN48‰«‡q;9 j ‚ºv…A-gA;? TšáÍHƒÞrƒ87¦N5‹7Ç¡Z¥ã-º^A÷Òà ©ÓúšQ¬¡>jt|D×h4­'ÜàÞ”] 5-tÞ2åcáùØë5—x³ò 0…ò5ð3`Òyýpß<¡ožðÅ™'XüZ\``À§AD0ðó@¡sB A¼¿ð˜«(†$¨E‘.0QY.((? %DÁÄ€‚(œùÌŠJ »Í3ì6×PP)ö›h (ƒ ,J@,ç)<çe@áEAøQ€1uš3 K €‚Œ2¡0³\œÈEEàg÷¹(Ú8( Š7Áô;ÐkÀ&èTÐp@ ˆÇPàIP!z TAŸ.ˆ ðó@AñGA(h±Ñä÷ÆßÞD>gì ¡)¤€ "hy Ð5VP~4Š(u,yÄð8 #w^ó8rø`lq±@(h&q<xšJ”:‘2½ñ8j¡¼blÖv£ÙDZ)GÏ ÜVá`‚ ((>™2ñûhHÖÉ”—†ßCcJXS(‹?C“JƒÚ)”i‚mDÚ(Ûï'0Ѽr L4±„wôqeWð‘ƒw¾AQëóànü´òõ“ÓI«no]hÙšèÓ§1¾çú]„@ÑoOˆÛ¬Üät×Xä°Ð¸rŒÛ÷PöÁ„çq0¯ìrùÈy0âЇúWÒ¹ß"¡.Œ‹ºF‘÷2¤zо¿ÜÇyÌO£±'Ç”9܇HŸßŸ~šºÕâaRѬ‘§åAÏ&]¤?ãÒɦk©i»gÿ¸¾u[ð¸ò£HÍÕš9Æ¢?”bjsÓó…=aÞ¼U@I)þ™ãÚQÛÎ>ñ Çe:çÁÓSñ g¤“sÎ wk[ÀGŽ"™·ÞvÛý.€ðzâ>åÜ?‰û¯òõŒ P:X†‰‡VNž^ýæÁÃì×®5rÓ‰ÌG" ØÔÚu=öéb¹(Bl)¦Äךû± uRÄüRõŸ[‚q)5¼ö©xHªãÛp»eñ¯ð8|ó²Ì¨Éפ€ùŸ•¤õ×dJµ@‘ÿ#r õ} SÈcŒ÷É®Sç/âÁ6®úÐ ZÍ\´j[Q:9¼~WÌÁý#@Q]Ñ> ëÃ4bÌë=¡¤ýäyöuù<œã]ÊoãΓL˜;¶Mœ|¾‚Eµ<èþ-ðñùßéŒßa‡äèEz¿,ˆ©ŸãOô6¾…ƒ˜ïŽ•èß%äE†Ç·xþ‡¤¡“`ÐPÛ£ó~äBÜ…Í­,ªkÈø‚aåNyÛƒCNÄq…E9ûÐήîÆë)~o†½zÑí‡5÷±ã~?†þjr|ÎØ+¯mïO€>ɲWŸÞä «V–64$Ñüg5ï÷vpkÝÇZ½F‘öƒ'Ìy;ÚŸTîPø+Òtó×kÌ·¨”¨ ã.Î6{ßÿnÜß\¸[æˆÏ_m4ÄÖzzáÝnC s\Ùq-]£HbÊÖ^;—ûá=—ø^ >ô%þ~n”8͸ŽÿL“½O†üHÿT£ºÓnå‚Þ·ÙRCvO]áÒÏÝöX>µÿä(æÇëOô¶Ž¦Ä~ʹqÂó®‹‘´PúàvÁø6‰0øUMigE.L©dzjEW ©ß¨°_цÎ`Qõf·=ã£È˜ª.¥÷ü ç‹r¿sás±6äÅH0îþ!‰¿9$ÂþŒ]ÇöïÉ…½3ëžÃ¸—+¤î!­a¼•éÖòvQ¤ïº€_•:rÿÜ®âçúb™ÒþïW}öL„>†zwòÏ…&-ê:vÓ§Ï'ÔImG»WØoÚ;Š´îú`ÌÊyÌÿª«ëà·ã¼gϘϘàËîŽqÍÓ–ÙÝß‘ïÚÎÙ›¿0/—iÐt„Fô¿§”Úú–Q¤úšåí†/ óœÍr*ºõÎGü©ZŠ~ä†ã¬ ã;9ýö<>fË¿î»2!–×]—ßÌQC.…$.¨Ü®;èm÷;D‘×Wš?@žP` = Žj4zùôž¥¸rŒ·>~÷¤Tã$ÕðiH.ÔpûmóÒAC\dTÖ|é GÎE9Ù6ŠèñGUD%νü¡¾3_¢/Öcª‡Ú}e\gŒ¯·ÑtL¡ÏçÂåz7VŒuÓ¿ªOržŽø]öŒó•VQäKêÏÌÇ×ýEþ÷áúòkžú<Æ¸ç ®pi}„Ï‹·0iœ ÑÇ%O&kHRÅö ~ž²Ù³sÆNjEŠç½ÛãâO¿Äf¢/÷ã>Âú<î_(½ÖmLs% òÝ|7È*çÂõÓ+^øÌÖfïú–¹á?š>Ûzê5~Þµº¶£$¯üH´£É³!E߈cæ½îZ­Ôx"Á¸±ÂG7¾Ÿž«|–}ÉÉæ¶OkHP™¹Ÿ?ï^gŒŸy±{_n½ãüˆÀ ¼ óf>ØOÏ’¡æÐ]BžæÀÖ‰o\|4dßM׿ ÙÂj+ Qä*¥eêüȤ–M®TÙÞ ïì{`g'¸×ÈÄ{i•æp¦íK߈Jo­ù¼ƒûÀëóŸ#ø{&CÔ–ÂãÛ³s Ãò*7æ¯×ˆ~yÑᣎˆ"Oï^,?œsE¥ŒwdUÚãÝò †ÞÉ0ê…ûσñ9àBqÒ»4dn“]÷­îØ@ù#[{c¼ó^/·T¸è/òl¸oºàøÝzÞ¨îMV§~±|;}ão£Xûdð©90›D×lyHC>Üù‘˜ìÛƒû <Ò'JôSå~v£n èaÔ–mNžY­WSxÛÓ;ÆþÀ¡/Ӹɚ÷e]’!éÇ=õöý9`^Ø ì^¸†È«UÞ>ñioh3~sÜ'W*×ð4qž]âÉ}…ñ$OÈ_Œ·hö̰\Çd¨òvQ‡Á90ý¸Û ç5ä@…Hí­q½ás«€'Óòq^ÈøÜW›óTõùjƒõ{Ìöº™m2t<™;ßwAÌ*ìS?_©aóÌ>âßÛ¥œQò~DðGìÓšö.·©Ü06þ<òãµ:ÝìÞ4§dh2ÍÍòݸzìÙUÓ«òëÔ¼ 'w(—¢Ž\7,JäI >×]Ï_î°ˆ>[Uä: ÄnÀy¡úüÅç|)¨öe˸d8RvkÄ‚Ö90Qy<Þ)ACNt;óáR [ˆ¯VüÃÒ&Šœš>gÍuãÌf>î}ÀpÞãŽñ¯Ÿç˜æš U+Ú(d•ràÎÚS ?w×^sŽw¶‡ðù]hER·Í©­^[‹\š··}Qíû+G$ÛÕÐ}âe-<Ê}2¼lý 2eKmÅÉ“ã`4µ•¬E(¼mƒÂýy_[â»åý›æeJÏ1®æ°õF=’Aûþæ§ »µpdÞ!µK÷ òõÄy›–•&@‹¢Àƒ¿q½ÐNDó'ý÷DzôîÓ >µÛ_ý® {ÏB= (”>oÖ¯°~2|ÞI ò´`ßFká˜A¦~Ñtš?œÛ› ‰"]Í››ùïô}¯ìúþÄBÚùïÕyÜÏ–ó1ôùÏy¹¢gZ’a]çfnZðr±zìì Âù[í/ÚÓ¸V¹5sEã{ûý‰À²‚iùNs&.Åa‘b¼Iö_\.¼^¸ø‰ñÍóáé´ü1ý«F‘Sè “Ï7[3ŽBHÞÒEuê­õ«e/•ÿ°ÆUËRõâŽÏiöØ¡í£JÉ0#g›êYC-,¾k¸#ƒÄ8 [ÒGî Ÿ·ªEæî¼5øù?qÎý¹?-÷EÕç3ƽ|Ï=<¾r2,у&²Á¿x™ñÓcdç–&;M±§ŸÞ±ÃqéJ[ûõ±ñ~¤rî“¢¡­»ˆóØ+[·ÛõÉšûêóãº&¿p¬F2œýÒàuÌ£løPä´," ßËŽ¿:_:3–ã,iÆqyvöò_þ¢Ïíãút†Ô:/ýquäæ÷Ö|¥ÏgŒ»eríÓ'CZJ Ï‹ó½ëª2H½ƒ57l_> VGYM «%ú¤ |†~°¿íîf‡úCÕkÕvΩRjý«Ã¸Ë¾f~œ Ӟϛ»ìh6T»Ñb­$#ƒq%áf¢9þIJ2xÇä{Õû‹¾Ì|=VóAÆçéMÚŠó >/ç¼j}ž,”~8ÿtGá¼dx=$$%"$LTqüiÔ¤ßæ½íœ yj…¶*‰q͘¥=Gù3 ˆóNCî¦ãÕN<}f^H2ôüVÝdI6´ð¶,º÷=ƒ”Ù|iuwgÐÛôç)‰küÐS\XzwØÛˆ~º°¾QMœs?KÎÇÕç9>g¸ñ,œª%ƒµuÓ> ¦fCëaÏ=¾ÕÈ$µºÚövê8Ùaih¶’üJ?’ùòôé2ÑV"÷Äp^ïŽñÒÆSbB2xF=Æj64-,·ù¦E&9ZýÄý¯›Ç0Þ„’´Xå·§©³ãv/áñðeï|«Qn#²“!Ñ/®;Ú94ovV=üÞ—×v\ëøz™tdï"òEjW_ Þ4eãÇ+kJ+¬µ¸tŸÐásìB6J¹“ 5ŸT³ü‡g¹ÓsM&I3ÿb™8Ãbšï«sµ@I6´ë¸éæ?b¥o(}_ÓŠÍS …¾<¨Põä]ÔU2Än6²uN,µkÝÜkc&™H®=8ctt¯…ñòüŸõÞåÇü|{‚ßÞ mãcú°|ýf-pÍ ñ–æÒáíÂú@‚ñÇtxaQQ™ ßSν˜ߺ.ºí±'“$}Úެɨ¸kS¯»Z%‰½ÑóâK?b;:eÇÊlÿ²›¸þ¨ñöm³2 e…|Ÿ»6=x(úèsYÐ@Þ-Ãâh&ù¸¤Œ WWœ0?`x©ùãnþ±ë§ËÉàÒÌþç:Û,¸Ÿmk#‘gUlÍÿ}>.”î¯öuf…wɰmÓ"˽FYà=ôîn·¢Lr¡Þ„í›GCõµùFßÖ(‰àï'ö_º‹bjոϱÀÅ2òãnÜçßïuQ2œYLÒL˜ð{IÙòYäÁÇFŠžÏÇ@œÇ˜ ÕW(ɧ¿.ˆŸRRwm{<[šÐ´TÍ\g•¸û“5÷×ç1Æ­=¦mxø7ìCöܽ”— C†þª3ºIù4Øqyå‰n0µÒœ©¯ü”$’¼:з½¹yrÿäÙÃûÁˆkók˜ö>¯(µŒq}„;*+¦@Þý²K&ÝÉ„¿:ÙŽè“E®15S†»AìÓÈÓB•$dk¨m^ñ:Æ#Ìêz8ÌV<1êj­RüqÆíñä v€pÑo°fÂJŸ#§:e‘oÆn»{ç¸ÀóÄ®$ªÝUÝq»ÂÓA­tÙ¶]ØþÇSëúUœLG¼üT*ïäxï›±sˤ@—~“ƒ+nÏ„7v=<7‹DVjbÔú¦lœpxåw%fÓlÞãQ~„ûýß°¬>Šç¸l¼ùþ›µ°î>· ã çÉP!û^ÚïE™Pûý—þv^Y¤ÐfÊÂ7Ó@X?)É‚,ÝçŽ~Dàhöeùk[ßž=R>þ‰õòÑ-[†%þ¶æ{Ýljõg|Î#;«)åR iþAçÃ2¡«Ò?kEHö F€ÇVWÇ7ÖJòf݉X'æß×âûë†ëS£!…R¯÷/";”M¿ô ÞL87¨Šúž,Òe·.dåòa0a¶ßÚã•$}Sƒµ=ïû²}Ã~ pºçI”â?cܳ£zµZÿ=*gYíÝd” ™6/þ|2‹\hþìHÖ á ìc+É™ä5§—æûÇÞø%C¼m§Ù_Ng@íßËú¿Ì"™W?vÆÚ×ÿÕ¿…’¼ÚÚÌ®û:æËÞ[;Ե+hn=0éÿó5›ß_Gp~½>Ïñ9Ã&tÙ´åU2h;†/Øóß]²Ù$èǦ¾sÌ\àG£2kš)‰{/› ï_ù’뺘 {{‹ó}ηÐç3Æ£t·cY¸Üsöêï ‰õøþ£a6é”Óè‹c#èã`f6«’Ø·XY/Ñ—ðy†°ÕFå›ÏŸÞñ©µÀ5öñl ¥Â~`2(&:¶˜”ÒZ·Ã|»g“‡÷ošíŸ>ê^:qºO%é:ï÷ÍaÛ|‰ÀQè%ò+øz¤Ç ã6½_Éî®Kúí´Î€ÿ÷ì0®MµÝåNPTÇÖþ‡¹’TÈØëðÁÙ—íwö…)GÎ<´³Ñã\ÚNünmxÎ%Ÿs:ܶéƒqï<ô³)×0ªM.4Ùé–MügtY”ëÕßöŠhØãººùH/_Òy%õçŸB<#‘'ÀÏJ­ñ9åïOL7?› ›íš“V&NG¯O˜4?› rÚe“Ui$Äù÷õmÐFIÖWn‹uð%5'ÇkÚM*ö?÷ÞPôÕ×ç5ÆýEq¹»’!)øü²ZÏ5°\;pFGÏlò>Ìçfñ—¡p±mð«uí•¤Ž¹âræb_ÖÿÚC§ùGŒ¯–ïý[dô~f-ðŽŒŸ#ëóã ˜d°{8ÇdÓ- }nÐsðêlâÜÅÈiáãÁp.çæð_X75ΜÚõp–/¹ÑÂk@ýîâ¹%ç[ œaž§Â¸ßo½¹o“ e×4µõœ¾Ô|¸Ç?0›œN«påêöAÐA?±Æ¾—ò¼ö¨~¾$ôÞž%ãšõ„»Ÿy¦ðófá<ÙXèÓ·cƒýF7L†AÕèyb«Þ]xpÆÖlRo°e‚¼Ì`pß2 *(I«ް ­/Ö#½Äõ çf ÿ)pMŒ†JWèšIð»o5ók4 =ép¤p6iü¨Û¦Ã¶h¹¶þ1öØÞñçp_"pdø¾×p¶/ùÀÚ›.Á¸éVoã²’`=¾¥“¹š&§³I_«®Î뇤‡ŸË¿VƒcÒšìYá+î—Ýñîm׎íENŸÿsž¡ð|aÿDŠÏ±¼Òºz•è$è6sÍÍÎnˆô|}°Ì•l–l_®µ,¯õ<üÈc™0/ñ—b•/髿Ha½Cvîw§AÐc’LÛaBUæ#Bþ¹cÜ•æc÷ï;””ξv¤¾·«ßôýíl2­×¹Â«]œ =]®d(HëN]ŒÉ`_vNÐ]Ü·x6aד¼gÖü¼G˜ÿ´+µÿ#ÃçT&w÷†$A½å­‹ÖXpdüR¯ÇÙ¤ÇÇòwr¶úŸ‚œË €q½ÌçïÂþëKkaýQNÈoŒ+[û¬X˜ôÔÇ¡ƒ\:wâG6‰ïÿ<÷j70ZÖ©“JA^ÄÔÏÙ±ÓG.ðým¾.àû(Â>-ÛÇÃç w °ÊJL„¾Ÿl×åå¦C—µêY·ÇjÉ ՠû×! é¢o+¯+H›JÁQÁ‡ÌÉö‹J’€—t¹Î@‘ï(Ì_‰ÐÇ1nÛäi oI„+ø×š]N‡­UÚì™2KKfOŸ¹Ì÷ðH¶ÿ¯ Ë-§4 ñ!]ƒª¿*îd ,ºlIŸÛ80È©„€˜uZ2<îî}^¶°2άºÅ^¹UïDÓS2q¾¶ÕkEo—í6âyÀÿú-ÌO0nï;óâ®uÁÏûî]¥œéûpíôa¡ZRéË‚²ý‡ÂûʱdˆU¯4»Ô}o=Ø>ò{kSÅö§1nîcÿ%^&‰0١ƉmsÓA×îò§=´d|÷%®÷í@à­*H¦g;øÉˆÐ—†±ýÓ^ œËVƒos5›ÒŠî ùqã>oël};¦?¬û{L:”1nj»ýŒ–,ÝdœSx˜¶ûÞk¬‚Üñ¤ pã¹¾µòú5çW ý$Ýšóµ„÷ý—°žŽŸ?|dEÅÈ~pâ”Zš{RÚ]¹¦%eÉr~;@@ï3ž·†(Èó ¯«]—ÎËáÏž³Ýšç‡pnf"ä9Æ—eý(¾>Tj °L‡§ý›¤TkÉŶOŽ}^3^–?øìÉ ÉYõnЪ«2ÆËÛcÍyJ|>«ÏoŒ×»Â´ó˽ aËów:KÒA>Óµ³ñ-)³üeÞ¶4G0Ú˜~Ì{¨‚,I }g±CFJ¿íâ:RXo×g÷0„cÜÑ©gŽÉ¢âAàU¥Aç®Í*æYç­Ãúå=öØ€0¿WVÆôŸÞëâûåçÓ¼Dˆ+ŸK;u»=^[Üq’œŸõzH9H66ðü„Õ_W±Þ{<­üvQ oÂ÷ß ù”î§îò”Á^ñpµÜ ëЫiÔ¿|û‰9lz T-ðïSw°‚Ôß'ñ—7ù3¿„Ï—a-ð«:мe}cüª;¼;œÕ~­í;‡¼:ÛSR|g$ôè{ÍqgS)§p¼Åzæç„¼ø8ƹÍú¼Åø3³¥¹¿ã`›Óñ°uò4hRkûá«rHì²õ Ìáç‘éÝ,¤šË)#B^ÅZsN1¿'¢ÏWŒW¥õ¬_aYqÐnİþw¥Aש ÊlÙœCºÞ5JÍŠp†Kë-]ê¦`ûÞ„ó‡ùý ÃqÔhD¡ôí©Š·ƒ–_Í~¿= .+×êxñm~TÛàeL—jÛEAÌüÚÉíGy¾ÏÏÏ% ¿ ÆËzÒæô‹•qðäðØµ¶¥ÁP—ƒ“%×sÈäÝÞ¤.p…åŸwT|ßÏSº-î¼–p.Ÿ ãQ#6?bKŒ»ýÓÞà6qu«ÕÛœZNËÊ!?fÖm”[ÃèêªJ+éó{YÎù>kļâ÷by?/ŭĸÛ6V÷ês,ܺTËeÇš4(Ö„–q~CÌíØp6Ò ¾üÜ[·µ‚ܾ É r_-òT9GUè³ïU†ñnÓåÔÙX YÇÌLƒß¯èS&—øºý0žèͼš1äÝ6w^M„ó£ŸÖÂú/‹c­óÑõyŠq‹ŽÔÿ¼jJ,l}Ucv€SPxµiÍ\’UØëAQ#àlО‚ 2åë‡y±Žk?ßê4˚ϓ9×PŸŸWÕÒè÷ö¶±°dÀèeßú§ªÝÑÎ%Io/ÞYm ~~¸© 1ý bÖŠ÷œ…{%=@ØŸé(ä'Æ&x@^ÜÇÈÎØ÷o[åÙ!—”m±GZc½-|6é>àæ»eŽWDâü’|âÿÛ{S_ è ü|ZŸ§#±ï©_·Ü˜¦‚åéεJƒ. OK*ôÎ%ªÀñ—·í¶ƒsíÓÜfÔP+›uî_Í›û ™Öüü¹©uÄc"eç&ï»[ûAïw¨àÀ®+¥AÙM#O É%WŽD»J]àÌÍëí|G-¯WC÷µ„ÏsùýNaܳχõyŠqûSœ´« ÌŽ<¯sÙ ëóä¤.®¹äÎŦû^8&çýù‘äø*z#o áý.›²Ñã^`¸_ëŽñ ]èÅbè·õ«¤Á%åôóåóƒ1éM[¹½Zñf$ѽ§€Ã5„÷%>~òýeaߢ¯¸®Ñç+Æ×}ìø`s~m¶ÕÏ5Jƒ-‡®«*†ä’gwì,÷Íq…2™4-¥ä=ãÑgka]T,¾JåiÒÝãê,ä-Æot¹ºt6zZjöQ ¯7«TõH.I­,_lvÌéûa‘6’,-³Ç¤Áµ„¯+ŽÕ~h_<·ŸxΪÏWŒ—ÞûÔÉËëp3n «AÒil6Wr‰G£oÛö7wm¼¬Ë¾Œ$Ö-V¿] ^Kø= ž_|¿’ïéóã>(S)¤ÉÔë®Ú<[«¯!M[ÌHË%5Óɼ N ÇjŽ$AÞÚô#'×Î?äýEØßï!îèóvT¡tуßÉ•j^‡!Kî^|K ÍõI¹d»wú§FÃç6tÁIÏú×nèZ±oñïÏ_ø{å÷¹ôyŒñç_¯q_yîìûé³(ïºÎw¥;¶¹¤†×•K£w8ÀÕ-+žT¯  -lškc~­!|Àï Ž/RŒ—Úûñ÷|Çkp‹¦ÿY5ürºµažI),¨™1üùH¸>—‚Èäd­>õ¿•ä?ŸæýF˜77+ÅovÇøû°‹ŸùuÚgìyÖè„VºÍOxS'Üì2«øJÃáðñÛ¶â{•°?ÈŠ ¾Vœïóß °{ïBþb<º»Ý5ð*lN £j8òõìóèæyÄ.ðCí¡î¶vlÿž[Õpü>« Í<»–ð}Ÿþ1e*úØY¼¿¤ÏWŒ7®YÝ}±¯À{:<PÃmÇmvÊ#ѯg‡W¼2Òú¨¶ÕQE¦I£¯çƒ<¿œÊL1ëø¤3»÷$ÄUa\á\â ì²z/¶E ý^ïhü¾O©>¢Û‘•ICa½>ÜXAj8ØáâM„ýþl1¿x?äã¸>o1n“sǬ9wv_5žq}ta•wnšÇîÁ€ºÓn½_Zç§½šHñ¿/^Çü^Ý£j³œtÓÚ±~ÞXÈ_ûBéútræXD×M[¦†Wç:îšGÂ÷T¿þ¬Š=»§ «î7³Ìê)Ç>ŽîŸK0ÞÔIŠwîë/ËÊy=ÆLÁïÿùih4;HŠTFqŽPq÷´'í_E’¿ËÕm7[FJ÷±·â¾ç ëóãÖÌJÒVêt „{jˆ >ÚzÙZ̃nôÄÛN^mô(JI" ²æ¬´—~IwÙïÖë¼ÛåD6òã¶Òƒåc Q¹FggUC/§ýMBwä‘Ù×úVc\¡UbrëiÏ#IÝ 3Úuúê-Îg‚5Cqˆîü>®>_1ÞÚŸ¦ Úï‹AS'ܬ†ÝëÞð8—GLw ¾ýêƒ+|XLâHR8æîз x<­ØgyÞòý}Þbܳ]Fõ¿h¢¿×·OVJÎ#Âù¿ Œ[]Yö®ª‚69™—ãÂdz‡â=}a>gÉÎi…üRa\·„ˆø°aÙÑow: Sñõ~™¼#-å¹3˜ÞZˆùµ?kžý÷³kÄï‹ïC y|‘í÷•^wè0þ€1WŽ\yÒkóöj%ªå.ïóHó«ýLŒ¿:¥áÖl¡`÷Öþ;a»&;¯(/ä«C¡” ¼ºô×ë °ÀoР}#q¼q9ßìHÙ{dJ™y“oÛ‚o»Wb¦âú\?ßù³E±¾eVj='Á¸­ÎlÄ‘þlÖ ¾Y󠼇Q­”÷Hj`äN»ICaÉ+¹YvgÙäº~”±åZÂÏy:PO1.»×—}?Óe4ÖéœÑWÞ#Ý76Ÿ=q´ÓOääbk۹˫ËÄ>(|ïÖãª(Õwfµ*u¯T…q÷¤>¶ SB§åZŒWÃèÜ´7ƒïÓl‰¯û@µ4%Õ³¬‚¼lN?ÀZÂÏSù½àRçÌïþ땳BŒ”àw±bSÉ$5x(u°˜yøÙœœrw€£8®R*ù^Ç’q›ç?Ÿä÷ù~>O ¥íÌwdTòP€SýÎÊ»SÕPt>cÖ¶÷È´©µ.;;‚w-º¬ U›ÔÝ,x-Yâx²ù–äûâ}mÎ×ç'Æ;7ôbËE±‘ lI/ «á\£UgG¬7JzŸÌpŸeµÊbí3 ¿ñpoÂÂëÞp½!Åx²=c/dÕ#¯†ý±½_h÷Ý#/íßožj;>$>XßIAžm«Ÿi,#üœ†ß·Îý„þìŽñ {;Þº4ª% <„ñ>ž}ÔèÌ=’•| ÌÞw¡$y\gê TFøz×%?_6<Ï“a\¡>ÎC™Aí§h'ãßýæ§BréÉ:÷˹}¶âzXx]²¿|ýÎg&ÔA³Rõ Çç̷̘ì?îŒïý¬ïŢvª$Ü#í¬=Ÿ}jœõ{³Õ ëõݲ·jt‘‰ëšÝ׿VŸ­»ªùŠñ:.|â“Òì,ä÷ݧZ2V ‰ãlvº}ü^ILkðjycñš& xÍ^µ¬däë/þ»þ>„qVØ÷Ôaüµ7 {Yî êzÖtŒ6 Ú«¹GzÿÈ=h™Ú  õõ,_KAülŽ»+û*Ÿ¯ q+ ù:ºPª¸îscc³ÓPMžWÓ[ßÖõ̽G<“ŸwþÖštìõfû_¸®¿t½zÅJ>b=ðý'^¯B|!®ãÞíÓ¯•ã)ð¹[½¹Ž[—´J™úèyRéNÛ5]¤ ?nÇu©„žàø°¼ø(®¿ û¿ãílÙûV{›“wßíÊkì[ÒÙóu/î‘z™ÚZk'Ù@Ò¬ ¶?p¼R® Ì;4Ù‡”þ}Κç­á>¡;ƦÓAi8dž«4kcœÇÞ}-ùã=ÒãE›*—mÁòo|•f ÒàºÿÒÇ÷}ˆðw~±æû£|½ÀóŒï›Ž32|Îj›e’fœ€ë#¼?7¤††§´+Wö>±ÞöóC7w{Íëtð öÝâ¸i§Ü| ¿WÎóUÈoásË1ý™……â8œüâ4rŽßÒ·Ùçê×¾OüïÏÐõ¨äMõH"Ù}\_q}Ç??ßÿæûü{4Ì7>gTêÓ ‡A¾'gdáüËáA³õ›ÛÞ'“önq9n  Ý12'’´˜ýÕ;Ó‡ðósá|§;¬&ä/ÆÛÚ|¿Ï–]Ç pãØÉß&ªAø^ï“ÐoƒïupeçE‘6˜6ž×ÐGìçÂý[‰ø{"}þ:áü°­ÕUI³c°uIꓽóÔp9{è·±÷‰×Øæ­c~º@¹Í ÞÖÿIôטÏʈpŸì©µ0Î ë# Æî9…åú…¼¦Uܳiá}rªÀm¬K10çíhg æÁôŸ3¦\–~•σ„óÆl.ŒßRŒ»×¸cÊΧG`Auéu?5Œ=ßõû„Ìr>þ¡ïhHš×MvQª ëëÛÅÕ(É/a~ñÍúÕŒBÿ?tb¿äy,ì²ý.|NÝÉ!‹fý8 sô‰¬ïÕø­»î“‘_“’¹cË›$éKâ±¶ó|Äù¼pNÜ@b~ q¿Z÷Øìqbý%ÓRã£ãÞÞy&ÄsÛ!XW[ÿÃuØßhRçœs÷Iàõ¯w°QgF?]5×®{l®ëKd?¿Ö¨OœoÅ ÿüëáó¯‡Ïÿ©>F쪌æe¥¨óOV¦„ÆX)Òršú|Ptóþò@©˜ß‡'JÍü¿dÌÿ‹{~P/TÊ€EQþóþÐ16÷á·g>ü´\QáÌãÏyüQ~Ϋ¢Þ=¡ÌCÕ–ù‚ðŸ‹ÿYÎ|{ì x®Œ÷gÊJ1óS güV[ÖõT‰1ðlå^ýžÌ³•²Ÿ½P)^ýÔá€{õÿÛÃÿíá2£ÿ³{¸ {G)FïŠúókï*È€Û*gþü¶ånkq9ÛÃüš­AŒwekÀ»²e¼«bKÛŽ*FÙw¼.Mº >Ú̧Օy´R^«c\é™Ûžü®Ì“Ÿ6!wæ‡I›‘½çÊ¥B™bsò0ðÝöB¥0^« ¥6ðÝÖønÓ§ÿüöðû÷ÿ{ý›ölÚ›i¥ý”öMÚio¤=ö<ÚÛh_£}ìû~äFÕ•ùì›0TŒ•r ¼Õ‚ñP ˜Ç4ç¡RïHÊD51<¦ÿôŽ4e\Ô.j c‹x0iÎÑ1T(c@Ù2:#Æ¡œ>æ/Âü¥eÌ7RbÀ²`ü'Êš¶d^µ:æñÊøO¶Ì7ÒÐ_šûFþ7•óŸl <þ97•{üSï[ÎJAI$%ÿÔ OÎgr|ê}ËÙHrTqD=ò%Ì#_ÍØHÔ#¿%e¼ÎæÞ·žÿƒ »bæ‘/g¬Q{Æ-j+ø„rn‰!ÇNÊ8vÜÿ6”ñ‘ì øHöŒdÄx£¬©¸v¼FM» ~ã1ÌÿÖyßRΨ'c"™ÿáïμñM° y0ORʺs5à"y RPæØœ'g0Ùc"Ë ,Îç4Ťöh,øsçÚ‡¢ Pö˜ìá,á]Q(ó?ÊA(ÊŠ1”m±LZ ÌzÊoã,Móܦ,Ê3¦lyÊC e^ØÌ›&M{ôêÏ´7Ó^ügþÿGþß©ÿþSï¥}÷ÿ¤žkÉþ(OÓÃX`G˜—xR)\M]Y/ÄøšVŒ¯YŒre¬)Æš¢¬M“Ò¼w ã¼SΔ—¦ˆ1¦Šè™2cÅy0Ve{¢,̶TãK)W%Å^)gŒ)WÆk7Á"pGŠ̱<ÿ“3 UDÏŒÙ$‡ó8ͱh—;ãs™2Ž'å·›RJ…’`sñdüv c·§0~§Œ1¹ô fT Ê›'*…1Û½P)Œ—èaÀåòbÜv lN2”eÁ¸í:ÆïäÜvKlZA(êæ$ÅæB Òþíáÿßïáÿ½ÞMû6íÙ¼Oóýg¦½™÷eÚy¦½×°ïòËû)í¥|“÷Júîè<ÀÈ•‚’”˜f”õn‰ „*(+0ÎB÷TZ^`ŸÒ$sg¼3SÆ;S1þ»§‰À€çüS5Ê¢²Àƒ×1öå j¿rŽu(+LN9ª˜1Ð(µ˜Þ…Äd`ìBOÆ.¤Œc/”eÉø¨:”%&²¬¦ÀH³Â„e|c[Ll9ªe‹ Î’ÜqæM1Ù=P*”“Þ ¥CYaò‡¢ PR,9ªåŠÅ2Á‚pGÅ $X^(-ʲ‰À_ãœy9ªåŽƒ2Å¢ñ@©PðÃ(§ eKǔ׊2Ç‚òD¥ $”߆R3ŽeÌsޱ'Y Ê M…2ï(ð]- xòVXt–t_”ÎSé•1‰m±ø¬°ðB{–ðÝäô>"ž)ãÆX—Ü¡ý»÷üÛwþ;þ¯˜;Ú•0àm1ÉåÆÛÕ¾ŒÀ€çlWʼœÀ€§lWsÆvÕ¢¬*”0ॠx[,ŒpV®¨V$0àMè]B” eŠ…ã…RÿÁ§¼è TÊ JŽ*FÙרï&Œý®bìwÎt5gLׯ‘öB©Q,>J˘®¡¨”‹1 UŒ²ÿLW/”e……„Ò0]‹QöX¸(z–ݤ„ýî…R£¬°'†¢ PR,ì0VÜ®(ʼ…À°NAI°ØƒP(+,ú°V×Ò‹?e‚ ÀÝBà\šÒ=Qz‡1]=Ó•²ß])ÿe‚ Â2ÁFáÚAàbÒ†áʸ®&Ø8Cý®0ÑÃY²»¢"P¦˜ô¨”“ß•‚’`¡t()Cªe‹E!g…aŠ@™Ð=5T Ê Å¥BIèùJ…2¥wþP)(s, J‹²ÄB BiK;UŒ²Ç gÅåŠR¡L±È¼P)(s,6T J‚Eç‰R3Ö¶ ¥EY`¡t()cªe‹E)g…éŠR¡Ì±@=Q)( ª ¥EI±`CQ()n+^WT ʋإB™Kð‡Ò¢¤XÔa¨"”-·e‚îŽJAIZœo5Ê >U„’báËQÅ´ŸcˆA™bð@©PæØ ÜQ*”)]OÒû4(Sl)6 wT Ê›…;*e‚MÃ2Åæá‰R¡Ì±‰x¢Ô( 6J’`SñB©Q–Ø\d(-Ê›Œ ¥EYb³ E麗f‘[`ó‘¡´=æ}½ƒ²Àfä…R£¬°)1î½6§P”eŸ÷E(Wî½›Vk\ö¨pz ý‡öcÚwiýo͉h?ãóŸÿ_æ>ÿ{Œaq§ý„Þ3¡÷Ièof±®méï©°vUt½ƒõ*¥¿u¥g•tÿšÞ¡¥ûÒX&ô^,Ýs¦ëü¢bP&˜ï(Ês^†Ò¢,1çƒP:úŸ˜óZ”~±A(-Êó]†Ò¢,ðË–¡´( ÌwJ² ¿ @iQV˜ïA(Ê ó= U€²Å$ C ¤˜ïa¨b”-&ŽUŒ²Å|—£Šéï0ßÃÙ@k’£ŠP¶˜ïrTÊNŽ*BI1ßÃØ@lŠ`²+*œ ̨”sÝ¥¢g„˜ï)(ê0Fç_Vìw®ôN.½×aXsAì¹®ì®-]“ðý!ú[UþÏÁ5ÂHåT(íæÓ¬NçWrྠo qLW÷/ˆL¼ášrñ¾è)ðšû÷aH¶¥Ž— Â} fÊxp³/1ôQÐás²\\ÊÏî,û™/¤Yj˜óÃë@ÇØûdéõ'†öåG ?³a°‚Ps6ùw_Â}»8œû&ë?¸s¡´vïó‘Ã‚ËÆö=žªA»øqë&·ï“¬.×3–䯻÷²QžsÇmÏ‘¬#Ü_ŒûT>]œð¿À¸é'œ?Î8W*·“ŸÕ0þÉso´÷IqGD¹³Ã!;ȽáøÞ æ{áKJûŸüd¾9¢/ ç ”òÂçt°¦¤—ý0V~¿i¢QlÜ[ßÃúù}²-ôÈö9ÓG€À3UÁ}‡^žá+~~Îmá>>4ž;Æû­Xõ>Â~/$(vp¬’uV?‘tù|ŸÔ¼ÝïFÜê‘ÐТh¤u?ZÃþ‘‰¯è¯Á}´¹ß±Iê¹UíÌºŠ¼p_æÌ9²»áÃ2?oÝ4({a]ïåÏÍMsMÊ¢OÑ»+ˆQ™¬SjúwôÑZðûéÈü@Ÿ9Æ“=¸qü.Øúü›uÃiPåä«òfÈ¡q_—?` -›Râ¨‚Ü ^VÛq­¯èKË9ØÜ—ZŸÇOð§Ü 3'êó£{¸?©¼ýRãä}ë¶ ²œƒþÕŽmdT³8bRè§Â}~ ýxu/°ûŽA“ë¿›´¶Kƒ3[&Ìª×æÙ—»²|± ´x?aØ„. "prÖîÇýå ?ŸÑ˜Bi쳆}kîÙ}ºÑ}\‡ýÊkÐåq2Û—H¡¾Þ([AÞÛôîë÷náß ÷Oçþõ¼ß±À}Êõù‹ÏÙP<òöÊÎÛáÕõ©^¯f¦ÁŽÑïº_îõ€ ÜÛavÿ^}à|°:ˆÏáÜDî“Æ}7 }¤ïw?¯×éÛÀ«ÒÈ5’iàâ‘e¢„äFê­«¡ÝÀíØG»NägÍñgÂcýçãê:ï:ßêT gÃЇÓã»R87¶ÁÐÉQ=6¦u95ð9‘Y¸°w KxÜÓGå¶ b=á~¤¶•¿Xg܆óA8—’ûrÿ4}Þâs“®n²š¶ &­NkÜéh´ìxá÷EÛdEè€Yµzv©G¿Õ»-$Úaÿ_Óúù‹>Q÷¹'óaüNåïÐÁ“ÙÊlƒSSw¤w½†ß#µù€˜¿ŸÚºîõ^¢¯Ý­Cƒ.|øéGnÚšs 9§ZŸ·/dqFÚÂî[Ò‚·¤¦A‡¸‰‹^;? å‡Ü?ñN𞻵É;Œ'øØùî—Ê9¼¥ü21^T¨,íiê .5î¥AÓcÍoZ¸? zßÛÊC`þ±ìS[*Èïõ-m¿ùýͯ†ÿýܧ›s&?BÁ§ÜÈ¥P:±V¿¢Î®[ G¤ÊeIaØêA£È·Ÿ3M —ºÖ¬ú¼¾‚ êtÄuþ,ÑÇIèçKõ Æk­·å …³G©1n´¶­q}ßÚÄ}ñ_*p„¹»z4ÜYAA¾i/íZ’½W¼Š›øMôe¸CÌ7ãÒê¯ã¿´ÊL]S6Ô·­»¶> Ãß›9Îkå »(Nø]$Y•ûèüúþ¬/>·æ^C®ˆ;ƾeuËP³õ ´Ùòæy­txø# ¸ÂÉä¾zûþµö. ðÅ"IµÄF Þó}ä9çÛ°ÏÈ0Þ¥ï ›t¸ö'ͦ/h“nµ–9|T= ûß<êÙöž tŠ«õ4½8’<,Îí:¼ ¯× kîã)¼O!žãõ­“wôpdô«œ}ij_:ÌÖnW—Éy@.Ý÷Løç#õ€Z9wéÈï§cýX_Í}á8G@ŸŸ¯á#ÏW9ë ™g µûètXá½èæÖ·È›{‹Æ·q†uÇ.ËvÕS–Kv%¸ÕðÇžO܇“ÿ‚/XóR㹟#øgùÀO?å¸[ÓÒ!m+ª?$•ô¯ÑЮû*ymsk&?¾ó¬?áþ †þ‹F®…Òj\žÚ<õ†yçö<Zvß$ž7ê<$Srn7=éï.»F—™b¦ gÉ¢~ˆùÏó‰NÞW¸ß½àsÊú,>'²v·‘]VB—­ýlRCÓ¡ìÓq Û<$ƒ_öÛ¡9àk{)'ªŽuVÞר^ïr¬õ²µ{«µø;¥ý»¤ÏÄ­ŽÝ‡%0˱òåkGÓaüM¥}’¹õ.7?kãµÎ y´²–‚Ø}[Õ¦¯GáŸs„÷-øoºc<¡ïÎIkOÜØp1úÞû«Úz‡‡äÚ’7iËNŒ½M;Öç¤a·•[Ÿ·þ2Œ#ôóqOm§SÓa‚B׭묇dôá<ãA®0Z±Ï/½®‚¬ÐƒÞçÛ~ õJùžÉ1ã_Ÿæw凼}’Î.§lÏtƒcyf—tUdÁõø[øçúʾ|ñûá>úB¿|ÆTŸñÐI»Îw‡}—cdo{,Ø÷ôÙ¿4»yã±p¼òà–KË(È¡ÏÇ;ümži苨ÃxNzötòª#wiàE“MäWôCra„Ý¢nPéû2³¿"‰fnãÀØþ¢ßÏ'Îåß—r~ÂQšûìV(ý”éì3¶ê|Ò1ÑyúÃÚØûi±“÷‡Ä~¯ïào¿\`f»N_þˆ$Ó^ž{åëOÏC‘³b8ß–`¼”æ1[ƒ‘"óÆßÝÚk`Ÿ×µ—Ÿ>d¼°1¢oÄé£Çhü ç:þS>I1Þ‰qË‚¼µ‹HÆÚ9­‡Ùh`Wú‹N§¾?$sÙté8ÃjŸÚ»ž¼Ž$ÇfT*3 @Gy}ú-ºc¼=B;ö8»„èm×'j 6¤øƒ™‰Ž4ök¿3~4Lo´é\úýH²lãβý2DŸs>¯¸”BÝË0Þ´¾ÃZö=´œØè°ØßéóÄÕ5t¤úæ¿ÜÞÔq·Ù×z$G’™ôã óŠç=÷›äó6C_9ƧTèö«È“Ï·Z· ÐÀR¿F¦]êëÈЕSž_üj½ªÐ$’\É\œþ=)P¬OîƒhÈ9Qa¼ìœPï¦Þäšsö¡=[5P«¶û_ ›éÈõˆÂÔ‰sí¯#’<š2¨ÚñÉA¢¯'Ï+Î'åã!wH‡ñ«6}þÓ{ŸŒèqÇ5°MÚKåÛVGÞ­=H*¶¼…y[׿Î÷ßA"O@ïÿ˜¿ŽÅyÏçq}Ì|Ƀ¾™M{Æk`ã¡/MovÑ‘tã£[Ú>µg\•Hòš¶•‘Áâ¼RøÞ[çöèóã5÷]èù"l¡îvé4w.`eD){?|R×Ê£AàËD’ÔÓ½O—ÿ$r^øü‰ûõéóã=hì'[]ÉŸ|Xr΢e¹ ÈØßú€rŽì<¡Xw̺4¼;Sz-’„~ø#ƒçÐð¸²M)ßYwŒ+­Ýs~g$U*¯ë²±UÜk?±}U™hólÖ%uÛ|p&’Q,ß³@±þw½;v¼&ÑŠýŠû¥ó¾¥Ï[ŒŸ?εçƒFÁ›~ç±C2 yñÜÑt¤yƒ_Á²¯cáLý€NœÖMy_!HôßæëWC¿x9ÆüÿCH@¿×EÙÓ2À½bMüùýsqþÕ¡ œ“•¯SH$¹ì‘âµ}XI^ñq»ì¡ÓS‡újý²ÿ6c/µfýU˜§©0>MvL\O^T¡ – ÷wRGÖW’à’~"˜u ¹kÖ-Å‘Õ ÷ ¸¿½0î þã:Œ×aÇÅæûêl áúewœªÞâmÃñøþ>ÖDȾ.›³8’|~uÉn„o x ŸÄùOõy:®PªÇü|ZO^@Ü¿­“‡é¥a×s{^W^±!’ðï•ÿ||ÖS-K­×%72oæŽÝé!¤‚Y-Hº—·Þæ<\{LGê—_>&v¢ h‡®qË=IF<þZ5a8tÇxsõ †…¤]͵'öÌý²§â#ò3gs­-“¡Éð&—å×#ÉÑ£{½nü·u…°®úlÍ×g×AøþeO¤Gï>õæ’iåIµÎ³ÀV>ãõ8³G$xØÚ&³n9Ú»úÄ+8þMO‰üTõQÈeáû†¾²rŒç]üõm윙¤ÑSw¬¬,P¥Óò†ˆ¼Ý¸îõކÙ9c'µÀx_¼š6j$þý<ßKÍS1^ôoã¬S¦’9Ú·ÁKOeAŸ–YaI-1þ§icë\‰$‰½^ÄÔ/ÉwÎ[-5Þc¼:ó¿Ú[éJ®é(@< ªl}ü¡SGŒwýËÖ¦Íì¡Ãø1“»ªpÑ«Áâ:RÈO³Ró=£ …R!:é½ÌÓ)Y0ÊÛáþÅîÈÞûO6½bãv ÿy5’ìK\ïßtQˆ¸ïÃýì9§ó¨Jñ0þŠŠ^éqÓA†íð§¯M²árˆ¤Íš¾È¢¥&髆ÂÚS•«%("I‡Jö£oe…ˆ~ãÜ—Ù+(Åxr \©.]³u6ÜÖõ:–jóˆÄôøš.{f B½áüOVE1ýkÈß8?œÎÇCÿgwŒ¿Ýÿüù¥''ƒlhÛÑ·ûgƒÞ|è#²ÑnY³åɶ ›9ÞÊtk$)×wÖ#Eåõ„óŸ…ù~§RëjÆúÈxºO;à»k6„õX>jŨGÄëìUgw ƒ&N¿%¯èzÛºm‡þ>él.òZRûtrŒ·n~§Y²¥¡6ò̆=o–:Nóˆç¯ÿìÕt8ç,T„ãI—óÍÿÊ‘†ÎKæ\zÏ_Î̉j_j«Â¸É®uX M—öù~s]6lº“6áqXÓù  ߇æ8ïÍoºá`Òµ`ÆÉ|hí{æÄ˜IZ–â§é0^j‘_Õ'+€î7Ø ÝOvÔMD®´íîUôFÑ‘ÄÆý©ºY^ðßö©¹´Ð¯ŸŠõ`X¯F ¥«º.ª½Þº}º<ÈýL6{}òÙºHÍÅcïVtfóH"ŒÁâúWŸŸø¿×¤FѾpqwåw¶W³¡Åˆº×έ|DÜÜsxÞ`Ø7ªÑéí"IHAvÖ¨ßÁb>ñ:à\*þ~NB½RëK)>GÞ-âÅnˆV5®Ù&5ö,j{=àq4îswOW0ù6ÿgL$‘Ï™$ít/Xä( ó¶ÇÖç½A©ùº;Æ­·Ä¦@><jÌïrªêãlxß +aë#røHù[.ð¶lhn ì/¿9ºÌûÁvñórþˆ_ðõ—aÜ?¾è¢‡¯‡²Ñæ}í¾gƒûœnã<"ÂyÅØlßõhH~$ÑÛao f<¢—ÖœãÎû°[YÈ_Œpfþ§}7A™j=–6®­iŸlO>"™Þ/ÌÙ?2—Õ\­ùI¼ï¿¸Són0ÛW{fùÃ&ç˜Ëq(5À¸‚¿v(TÇ—#í¬…J;s²Ê+‘´>—/Œ?< °.ã]IúP|ƒUáã¾0~—ÔßÏ0ÜoÓaüÊ&º[`VüäYº!Z°ßÐ~÷ÔKÈ£•Ëë7±ƒ5zP<®lG\ ›"îƒòïO/úüT(5ÝyhïÏ-pxbѬ‘µÐhù°Š…äÙRµ×…Mí€Ò¼ç>$£Ê-l<Ò=Dœò:öoYszž†Ÿ_‚Ï)?ÿ~êèF[¡sTï^já†ôkÎÉHø´aÝ6iŠ7Mª¤E’WäΈ½ CÈ¥ÐÃmv^x,r6…y­À”b¼¼yu÷vÛ (–b­††ÏºƒýÂHâ[~ýHX0hŠo}×çmØ_d¶/DäAry^Ç¥ö 0îíÊÏÛ[¡ÍÎqc«kaڻϫ®e="ç£)HÖ*%~+ãå‡-Û?ZF…‘Qû2ÞX«;oà3Ùckþ9 ×á2Œ[ær§™Úm‡ËŸö4®µ] Ë{žêØçá#”:÷GG8SŸî4D’CÓ(y‰s…ì|#ßšó 9 rŒûƒnëšï„íj^8¨…Þ{_\Ò=D&uÎéØü´Ä ±‘¤®~#&DäIð>Âû(?ç0¬CÆ÷œÐ¹ùÕ~a p‰´ÐþåýY«‹‘›+%WŠ`9-—œ’ñùOÞ-Ÿïr;tWUÉßÄÊo˜é7t´Póٸد8Žd缾nº˜›”™V€ó|דòÃ/]BD.2Ï?Î)çõSšwËø±î8ß·×í_½rX췵̼­…›ÝöŒß`œO®ô(1³õº#OpýSPãÈó:!l~vÏš¯«8ïRŸÇoÐþK~o÷C«zt'óxМïÕ*å“E ~ª>vg¨+¸—Uˆã çœòñ…ï§ðqÛp*¥ñ?÷ßY¾æAˆ(³úâŠÇZHéõ½VêùäRÅì­Ÿg .é$yú½ é‡`q]ÍÏ«øøÂ9Âþ_ÓÒ|Ž€“Þ´Í£Ÿi¡ßEõ“Zù¤×ÍúPO)d¶ùf¹…‚ìtó›ÒÎ#äoGìU}4;rÄ¿ƒóÎ8]Ÿ÷ømÑÇanr˜ñõrÕß/´° Íóõ×êæ“NÖ_ªê m*Øvói¦ #w%&<{\’?¾F5ßä| #a$ä?ÆOztûü3£C0Øk•i™÷ZÐogÔÏ'+½Ov[Ó†èÿp™:ä±=©²žðÏÍyj†|gÆ‹±>Ós¶ÿ!˜Õcs¼û/-t<é]½q>ñ­ÛÌîó’Þðó`üêÆ RèØüc×O%yÉëÉXÞiÊÀ!ÏEþK©õÆ?öiŠEëÃPµ¢BV)Ò¿ϦùäÁše~TêwgVêRFAö´¤àáqžÃ÷_…ïSx¿F“ ¥—œMËNø~Ú…¸¸åÕʉM6lÚÓ2ŸLŠJ®ò¢H C)–ÆTA4³îWˆ~"žòý¡Rë;ŒwqÕ¥Q›Ê…úõiÇϲ¿9Ù6Ÿ|1º¿«kÞ X²éÉû& B©/_†– Î59Sdý¶§wŒýŒ7ܸ?EJã®nѲ_Õc0ÿÙý»mr`žjÖùdÐcئœ1 ª=Éy:ãf.ï1.5„qš^‹ã–>o1Î…a´Áº !ï–ÛãêúTµÂï§üúÆ£ÓG®›y5FVQ¶žŠj÷|Jò‰çí ‹÷Þ‡«²ý¸Œ×„}^ÆÁçtˆ~° êñq˜ÐóØÕ169Ð;á‰b™4ŸŒžœœÛ{›¤mÿkÀ·Hò+käv¿!„ïëðzÓç'ÆÙ֨ׄpܦ¯Y§Q90ìUîƒùCóIjü¹­ÝZŒaü$å먶û”ý{>q9çVñóLÃ÷«ÂçM~;ï›òà»2Zè’Ó—-\–ä˜O¶œõêÖ¤©“È)^¾¶3IþƒëûÅZ˜?å²sØOKݯÁçXͪ_+µl8¼ »ÜîŠ{#M­«LÈ'¡sèˆ?z.ØWëY?ÉŒxX³Ð)X\Oñu³áç6šR(]«—†ÃáÊÞ=çäÀݶÚBßù¤qµwnÛ YrVW¿© q}Êדüsrå%ïwË“v+ž=yQŽÀœŸOêÀµŒºÊrîG‹ç’¼_òya]H1®°þ8 Ù³Ž{¿,œêîXa¿4ŸÄ>*_ãä¬!вÜé—O1îº:f–—¯‹y÷'’W|~d8ž»ãsþ9\X{ ´êú`ÌÊ(GAò+óIýíMN¸26F9=Øä© o+Zû¤‹|/¾î3Q`ÛÑ;Ÿôøçæ‹ Ò£™yÅ]›‚ÅüãßϾž0\¯Ê1¾£Ï¶yº¿ÎÀÏû}fÛåÀ³‡^A¡~ù$ùrÌÆ Eƒ¡ÏÏ[C¤ý°J±§V+/ÏØ©Á%}]Ÿ¿çGüÎÆ#>ð“Óç¬Ù’Ëßäþ’O(Öòeú0ØT®þÜ—}äãœÂ¿Ï‹çi¼. çq:Œ8úÙ³igaFCÚÊ{r`çö+wßmÉç›$ýkQûþøoÁE°Øoøç2ü;¦J-¯´®^%úŒtï}6OŽýàZÿÙ7vç“ñÝ—¸Ü·ƒçVªúNSyz0V0áó)þù ÷¹$oÁÕ>]Æì=¾½Ÿ¦mŒÈ5ôàOžO´ÿFï=/ØænTa>ý÷y„0¿lÍy=|Uê< ŸÓ¬Þ¢¹ýDÀø&Y ¢ãràz‡ŸEaÇóIï21nW]máÞ:ÓÐN‡¤’þ¢ÏÛ’ú7ä-ºc¼#;Vù¸Š€‡õæ9›åäÀ\ëägòÉë}3|–„F[ÇZìÞ§ VW¢í—¬Wùü›Ÿ¯ñóºRë>Œ¯õÞ¿·Çãšmw=çYiÿ8"Ÿàb‡Ù¿S W*\ßÇ×íÙây²a¿’c¼Ù$ºf˦‘°ÒÞ>dÌ›ðJµ÷±ŠÊgû°ÝÁøK_ïÜp‰j6ûðÝ!Áâ9ï‡|ÝoÈ™RaÜi)ÏkŠ„2~Õ[Ž}Ÿ©ÛæTžv!ŸåÀEËÇ«Çä“s=%Åw7šÕý Dà_‹ãÏ3ε4Ü·1šV(;´ÝõCE‘@O×ÿæÀ‹/®Î!—óÿH0þøLJ[¶×N, ùÛøÀ×M¼ð}Ãó1 >gËî*ãÏQ@޽7‡üÎÊ¡.Y¯.æ“à#“Ÿ—þE†8…ÏïøKAÖ¨•nŸ.…ˆçcñr»#7-€Ïôù‹ñrËáâæ¡B(îØ$¾f¿¿ù ßsð•Ì;ë盓í׎íëôëÎízÇxï’ñžsù<¾ßMbn6¾>Õç3Æ¿XéËÍ«ÎJÐã†käBŽiz%Œo|æE渉µˆ3vÛ±E ò¢Ug“3 CÄüàó]á¾;Æx³ÎüÜÿ:M -ôO¹°÷¢ül/ŒÇÞÛõ 7¼›ŒŸ÷ÚÆÔMNùy@´xE¨—ÒûÌrŒ»§RÀâj]£ MÚ~o¯¹0Dª»wã¶Ÿ×Ú(º¢®n}rézu%rûÕó§%ãŸ?òýqÃúPaܽÖô›¤Œ‚zž;·>Ÿuç®u™„yñ¥}½Y–`²¥åâöJ¢Ÿ}•ô5>6ÜÓa¼ezð[4$T¦3¾\8B§s—ó‰bÂà1ÍÝ{Á Ìó¸âQa¿¤¯ñ}6Þy^î#M/”Úé²î÷ÍŒº+öãÒodå“]ã%ûNeõƒ Í]uP’ît¦^2žñuÏ7>_×S¿“Yê¾”ŸSMž»[·6Ó:T ó~Äæ“è‰q.m'ötùÕKIŠ<”_û$òµyÝëÈWâ{*µñŒtZåó ð¦ÀûR¹ ­z}e«Ä|²uÑ>Ù-“np·æÝœ26JêA‰œA<¬9¯ÖpåŽñvhl]<å"l´x]íºe.dÜ•¦=JÊ'Ìm+,øÕ¤óœN”uS’Ø%c/ö(žs yb$ÞËÖç1ÆÓí«qìZìE°:pW¶²k.8/‹ˆ²IÎ'ü>öÂmÓÍ—+Éœ¿Lo§=$—+¤î!ç±á:PŽñôרjÅÀÌ*&g;åÂÓDMß- sá⎲ùõJÈã›6Æ÷i~Æ*sl[§½󗟆ë)ÆmámYtoU \KŒ_õWû\ÈžÖ%¶öÒ\¸ñ¹áÏŒ¤bÔ’òyAJ’mOŽ¿ŸCó}+žüÁpþ§ÃçÔñ{o½éY L?åîäˆùп’V¹aE.4úfõ$bP{rÐ¥ï'ÛuJÂÏ[ù9G yF3 ¥[_Õ˜àt ÂN,Ô¦i.l9"ʪ\ömš›UŸÞ©ñýöI)Œ=Z$öc~ž—°òWõ*Eâ:†#†ó >Gß/_‚àBs—ñ sÁ\5ìcÁê\(y¯ÏÂò¤`Ýô¹3”dßñ»Åœ "û[”YPû±µá}7)ÆÑNmv,§ïeØykðóµraý¢„èîkráq+·~u¶Ô… w{\–ŽQá^TxïYŸŸø¿¿ë×*¸åÓË0Äø°{þï¦Þ¿`e.Œˆ~ëȲ¶õ¨ íý J²Ü¬Æ¯Aê Ò÷”ý¶»ŠÄyáû“Ñ÷—êü¡á–+`6²ÊĵsaÂÄ —aÿ®s>>ß§ ¼¸0°£~kŸdÍ3+Ùæun¸?'Çx³ús³ãUhÒéhüó\ØlÜí˜=æç•ú/cßøw…]Õù¥Ïyß*ÍU(æ+ï[†ûQ*|Ž&h÷šÆG®Â'Ÿ'ûÔË…¼Ÿþ+^ÎÍ…¦eþ:4,µ#Ll_u‹&[Iè©Üš1%ý‹ÿ|üá}’¥òŸ#{sk÷ëÊר>w.TÙ?÷Ò¬\€UO½Ö5ÕëJfy÷•¤RpÔFpþÛ¾ pÏü¥È[åû†um4³Pêj4ºÂ_×`¶GÙc?«Öîâ‘ N?ü\»Ö$û,'wk—§$8hEÛw &'þ™5Ÿ§îçH0ÞÆ=ç·n¾ 6ÿ.Ó_V5÷õéŒï§þ ¼¹a¢pÏǺø~sÝÇZÁâ½Wþ}žK1Þ!׋m¯C±.zxƒ¿r!îܽXkÏ\`÷²‰0~)‰ÅÚ —æT þo–¯×ø÷ÊëËðs»ãsú½Û×AuúÚŸè{ßCLP/Çä%¹Ð~\hzéÝHzm¨v¥¿à,þ΂ïŸñy°ázV†qOíû¼}_MVµOZ^0Í…iÞ›#&bŸø´zÑ¥»º‘ݾÝïoEäÇÚi÷ ÷çù:‹î:•c|ßskŒ¯yØÙçÞ_ÕrA¿k%Ë…¶îïBv¾·$At;ªm©;³êbßÁâ};Þwø½ÃúTaÜU#W>,Î%n~öÈ©J¹0#þÓºÚ¾¹0üèZ‹üëMHhçåš6‰"‚¥Ÿª7 þOšÏëyÖ«ã ÷éU>/x¼g…\È]JwüsAȯ:ÐlÓÒ… ªE‘Ù3Ãr+‹çî|¾Rj£PêÞdÿå÷n*X£˜ÓÛ(%Œ]]߃ð;‹.pcá‰%¿”äÞÕ…Ža_‚ÄyÛ?îC`¼¯#&ó QÁTz­®8,R×ÇÃ~ç»Ç´ÍÕ˽ ¯f \ÂDœ\Ì´xSr/Îp= Å8¶ú † šF,òØò:&Ÿpín²8æ´8gräÀ]ÓMnŸ[D‘¼à­¡¶yAìÜD#ΟJíûb¼»–ÑÇþz¯a+6<_Óá+ö­Ãúå=öØ@ë×³Ž ŽÇKÞø=~ˆŸîGÊ0þÍc煮o K”`??/vMX¿ÐÍ=êè/´Û@ä‚Ö5MŠ"t·¡×]>N\´6œÉ1Î’½žço‹…ÂÆ9ã»ßÍw‹7êB]ráЫO÷öKá‡3½pEzÜØ\\{NÐßú±Û3“%>­^‹û.¼n ûƒ Ÿ3}—Ó¶ÆB…ËŸËÄõqjR·ç#sÁuAFeÍ—Þ¸ø‚L½2Šì={õ‰÷û@ÂÏ»x>ò{"ú¼Äx ®ž¿é3òo ¬áx1²dŽ¿~Í…Nó_-ß*“Œ»{C¢È{å–ÂãÛÅý1ž¼ôy9«P*ÔO<þ¶|ͳ39ñ»wÏyƒsáÙͪÓß·†›=óî…ˆïƒñ~¿'Ã}M Æ›ôêZñá~qPü=qîS˜O–-n» ÌMvØj×Z `DàÈ»W"£ˆµ‰ñW³¶A„ÿÞï?ó<åûæ|ÿÊpþ(Åç,;®^±qB¬ïö£9P-ê]_68ßÓÏë‚ÀuŽ"åC\"âKîñþËï!ðsx~¿Áð<ןc¿q×|³uqp{W],á¸þ$÷ôKi.L©ÿÈöûÊ6ð’.ã£Èœ=Ë÷nÎûãëM‘5'úŽîTŠ'/ÃxJÅš”qçâÀj™t{g̼¸ó†gß\X¥¿ß .¾^•1é| q^gyqUÉý&~îÌ×›¼.x^êóã·|±çFϼ8è£ßÈË÷ª ÝæuÏ!_ûÁPú3ÇSQdÒøSéu²ƒØ¼å›¸?Éó˜?WŸÇwÿ´Ó·Úǃþgþ9཰\¹;¸®ÈcT#°?ìû&}çö‰ø‰î}xÝe‹ã‚á=Æs+þ6vWýxhyþNgÉ*¬‹çºÝj\®ž²¿íî6°·‰÷,‡ƒQ¤Á3‰'ŒJúÿûù¸Pj¾0»Pz­½áúãàE9 ´©\ø>®LѨ“6°iGØÌ–g£HÛõqC‰ã$Ÿ_ñóëRçWîpùc§¡ñð0è[¶G¤_vù’c– Võ"àIµHm¢É"n!OÚùl"¹ê{q|äû›†}\Šq—·¿0tÐÔxÐÿ¼gBtx³§ps•\îÙöù|¹ªÏ×(Bw÷Üïwðy+¿—§ÏWŒç_èÓØ?ꪾºê˜X\ôî\“йÀïƒêF\|eú;ŠŒ½ÿZñ÷õ ?æï™ï{î#Ëð9oÔæKÏÆCÙ°.žÒá9`÷óêIÛò¸n9c»óþýrp†^ÇÿEÂl»œ^D„õ϶Z·ô>/Æ£«ÑÂGñ°*hðrÓ>90äî¾ ãÕÉYlýhËÏë~ò#Î^D=¾~ ¿G}GüÞø¾‹a½©0nyº½S'–áh­ëFÑM?âxN¯•½9]—¬)?+é0ÆU¶qñÚµ>HÜïâ?ÿ]­a_Ða\eêÆ•†'€CÇ“¼Zæ€ôÅ·›p¢Úýáy¾äb‹Š¯f¿"éO¦T=ó8ˆðúåýÓõ×êqNuKÝ0šS(-£¿0ŸwúÙ”k˜6¾ý|ªà¼©ýÙ†]rh蔡—¾G‘žq‡›lü·û´Â~í+1ïø~O©{køÕË”-wT pr®ëh·Z9püBFÿ‰Õs¡Æãîç_ž©C̾¸~S—&c×}q¾d_r”ÏJå1Æ3×&°yQÐë4q>éðxû‚¹í¿XúT4ý°I4Ù7·ìrùÈà¿å¯¾Næõ]jŒÏöéA}±üð¥Æ9Ð4s†ãW|?×Ö?ëdÝ zíŽq6-M6»}·Úˆ`ñ¾ŸOŽÏ2Œ÷BÓöÛ€®‰Ð§QÊÊÙŸ´0/-¦Õ6üOöÏ÷Ðöê Âï ÿœÿ&ˆ÷Wù¹¸>1ž°›mô6ZÍx^}QÙ\øqò]úˆ)Rè?µ•C§â(òÈbÅÒMÆÁD8Ïý"Ž÷ü}èóãõ­¹§ÅÎí‰]l÷ù®F ÙÑçœýŠuÝípõw³BqÈükÇ+FÉ`“Í=6‰¿áã'ÿ¯y¿0œèð9­c¤‡«áþäš»/©´Ð±Ðmκ79¬o Ÿ¦§ÇõkMêþå7͸f8.ñ{b|É/Àû=ß'¼l$üó¯GÑ¿Eÿ»û‡ü'"êÃuÉHøÇèÄïÖ‹ùÝZ0¿ÛÆ[õ`¼UêWäÅ –ÌÓQÍ<¹¸%ãªQÆ‹V3^tJËx žŒë%a\/îyëÅ<‹,˜·£–y;rOp óW3f!eF«Q¾·ÿíeÈ^µ`þކ¾àœãeÀödÜhÊsýÏAÊx´ˆ)ßËéÃø^^ŒïE™¡Mn!åF‡KoW¾—-ã{Qopî]dnà naÀÀ¡ìBOæábÊ<èR˜÷ÁåtÌÇ…³£9»°€1p" ¼‹ =U̻ȋyÑæBÙ…rÆÀñ4à;Pï"ã;pÿ9{æáBý笘ÇcóxäþsV=K{©„3n¡;ãš0nt Êän¡çÜèæïH}ÁµÌüßžþoO—ý×èé&ì]©ŒoLJ²ÄÄ—1sÊiôüƒÓXÀ¸:A̧WjàÓkÅXfZ”cÐjƒ6¥c>æ^Œ³cÁ|Ìu(Kæc®fÜci[øõZ0¿^-ã™Q­eYCðëÔý/sõ¼FKæEgèÙËY2­cÑR?ó ð3·e~æÔCëO?sãôȧ‡ú™‡±§,Ú”iSÁÏœsz짇>gjK |{- x”kæ…JaÞæ2æ+jià+jÙFðæ¢Ë{*ß+£½öAÚÿø~Ù«ïýÙóþS¿£ýÍpO÷³ÿ¯Û°‡ý·Û°WýÓ^œa?âçd†ý†÷Ã>Ã9Þ†ýÅpßNŠ*F¹c‰)˘k&·¤’À˜¥|ÙÆ•¥\%ê'֔ι°þL±îL̶Ç?pÌ,Ǭ Ai†Y8=;fü2sü2½šükÊ–¤\WÊtµÅZŠ s%¬#Sü¢U(sƘŒ`ƒ«;c“q~«cKÊ ¸L–tîb‚H£Õ½“À0rÅZPu8¬¾˜º»À[õbü0)c(Ù2–e?êy×l@v5`>ªQ˜Ç¡ÿÎOþŸý×™ŸØ²ÏªCY1vcaËQE(iYK[LÙ´XrzvOç$¨pT1Ê #e‚Åአ7àaSÖc±‰Àzäôž/ªeo.°ÖMèY=*e‚E뎊A™bñz¢RP–XÄ2”e‰Å„*@I±¨ÃQÅôܧ‰ÀZ·àÿ]"°Ö­°ÐƒPZ”%¼J‡²h.0¦‹Q¶ØÂPE-æt8cNÛ3ÞzJŠ!UŒrÅΚ„+*eŽÍ•‚2Ǧá‰JA™cóðBéPRl"¡(Ê›Iª%ŦN÷=P–Ø\d–oÝ ›Œe‚ÆŽ2í*°SPÆZW£$ô3*eŽÈÞ¡B™cCò@Å $V%¬u+lP¡(]/µjÀZ—£ŠP¶Œµ^Lû;6°zÿ ›˜*Eiî BÐþŸîéÿ­~þÿµ^þ?ÛÇiÿþïõmÃýÎÿÔ¯ÿWõê?ûôÿÝþÌ{³a_þ_Ù“ÝÙÿ¯ &«'J…’`Òz¢T(SL^wºçŠ2Ç$öDiQ˜Ì^¨”%&µ ¥EY`r{UX÷æ˜ä2T Ê“]†RÓÞŒIŸ‚2ÇÄ—¡Ô( €'J‚Eà‰R£$X ž(ʋ •‚²ÀâðB¥ $X$ž(5J‚Å"C©Q,O” %ÁâñD©Q,"O” eŠÅä‰R¡$XTž(Êœî£Ôô÷Fô÷( ,4J‹²¢{Ĩb”-žULCJïOÑ{ÌX„E({ þßP´íQrT1*œÞ_ÆÂT¡L±8ÝQ1( öYO”šÎ—±`cP&X´ž(J‚Åë‰JAI°ˆ½P:”%s(J‡²Â¢EéPR,îp” ¸+*œÎ¡±ÐcP&Xìž([,ø0Vô(5JB ‚ÒÒû§ØBQ([la¨Úg±)èè1èP–Ø è(lrÖ$\Qá¬Y¸¢"XÓð@Å L±yx T( l"^(Ê›‰ ¥FYaS‘ƒð¿seçîFìg8¿¦waÝÙÜšÞ‰¥wÀè=ºwNÛBÇ+ƒ¹½áoVcؽ­ ¶gN?+ÿç°™[(ý6åmbÝ׉¢oÍØ™«'?¥îO°I²cÊÇ9Àý9ßLIm¶` p¿‚û+GT¯<,ZôOX£7h 9ô1|NðÜÑÚ4 ¦Véÿ×°0-œ©VîâÑœ|ÞyDÍþÖ5¯ö7÷hÒ³GÇ5ÏEþyÀe·T?Ïg"?áŸ|¤ÿ‰Ç¦3?º$'è|Œý´°}Sê‹jé9 øÑZÁÂ-«._žM¢n<Ú6²EèÌ}(øçå~o†~îJqP˜ÙÐ$8hDWµPà\¼ié혣7ÄéÝw5¨ºÔ5š,¼ýs…IA„Ç|‚nŠ~W‚ÿGiŸÆ~Â꯫c“ ýûÂ@­»–O¨ôÆáFÔ,SÖñÌ´¦ðñžK3ûh2¥S­ˆF‚Ä÷ó²{`´Íá—"ÿÆÐÏZŽq—¦>Ù;o~ø¨WwqÐBÅ=mLÎK®ºaÚR{?þžïM‡šp”à"î;)ð¿‰ÜRœ|¥hZ•3Vµ=ÑO ï¾µ®i–~¶t` ·«Dl9!Zô_á~¡Üï‚û®pfÁ®IiÞ>'÷ÖMVPœØ6wi¯–Z×çüÌë9PÐlI@˜q?ˆŸA_ÑdÚ¸ ûŽM y;œ/!øÆ5+Å¥2šW(ÕÛìnO‚y¾QU nÎ!^ƒ/äÀ¹Éíªz; :4~°󓺖ït-ùü‚ŸÎÑ'„ûò}%ÿóÛo·Ý&Á‡FÔÐ9:ùŸ©Ù÷T´Y³z'[hܽUÂxü¼‘¯‚D¾¯àgT§”ïãõÕ©àû¤É¹¡Ë†¦O[gZïÏXÇ ª[U‡YÓÈjæÀhrïs¿—ç]Jò…ûËò|¾×Ê¥üXÜ1~â/Ū÷I0±Ý¡+-îdC}ŸFGí6ãç}£±sü8œq¢ɄŸÚHJ×ÑwkÎkü*sEøR~¶øœž§4>gN$AüÁG©õ¯fÉx§»|r@à ƒ>¿—åœÇçœIèÛnVb áþß¼p?C¿79Æ´§ÎbI0öª«y³ÙÐyÒDÙš%øù—||$q(˜z¹=M¨ký×w¢¿$ÿ;8¿‹û"•â‘`üokg_]²- z›ô›¹5"F;L®á‘gv¶­8kõ ÈÙ±sËÊQÑdlÛC¬:‰œ#ÎA,åë…ñ¦Vβڻ) ÜS}{­Ì†1®—o›˜ë[ßNlã}òò.ѤµÿŠÍ]GýÁ/þÊüb"Ä÷ÎýÃJqIæJSwôŽ N‚¼/ádâälø¸öõ)·XS}Äæñ¯{BgÅ´MO±N‡ ¾w`YáýEô¯cymèÓ(Á¸O[Íž5ß? < ê¦ Ê†gSÓ{—wÊEqx}óŒ.ðlùžÛ^ã°~̲;Ÿ‹ yœÀytÜGÌÐWRŠñ+tÜܽùº$h|ot^Çlè7ã{¡ç¨(^®Éƒ;m@ðGŠ&”*]«Ìßy‘œÿÂýع߬¡¿“;>ÇX$O‚nt8ª— U÷o^eDÜ<¹òìáàÝpjØMôXÎ^ÁóÇãùÎûÿ ýÆdøœåz£ø$(÷¬éhërÙ œ5nÝYÛ¨_ÅÉtÄËîÐÀû°bòˆh²tQ@þ¦ž%¾O¥ù—Y"g€çi)>'ú~¹‡ÝÖ$Á£VGnZ¾É‚!é-fÖµÉQ1ÕܶMxá³96dt4©­Í•p+¹¿,÷åß—>ÿ1nÏó/OL‚àI‘ëïeAGÒè½Î*ªOº¥n4kŒáÛó¨C49¾öÓѸH^_Ü_‰û•þÓç×ás&éAµIPK Ì{§gN¶ÏSç>Z;úœ.ÿ9 ¿yzpB‰ßïÇœ`øþJ+Nžÿeöã/Ë/ Y“w£3žmˆó‘Åçï¦:ÀÍŸ ò±Æú-:|^’ZâÛ$Ô×Uñûå>Ò†>oŒ¯ÇG'ÁîA|gd碩M*åÀùتÙQûœ¡üJ?ŒñcRúM¸$ú ·’†?KíûQä›—ªŒ{x€ÎV›¿.Q€stiwsÜ´wZ¸œÔ¨åôþ.Ðdš›å»!Ѥú¾®‡£z‰ÜtaTlÍ}ñ y$îô}¼Ü>¤g\ß¿çVµ€,ˆ°-ž«£ßÛÛ:|‹TçZr‹&–Ó–ž~~4PäÑ ~¶Åÿøye÷-uQS%Á©¬¸öQ‹³àGA§Q6 Z|ÝœaÜ…¬º•æE“ Y™åJ¸é‚™±èÿnøýÉ1î©jÈ’êwiù&gÁ…†jí‹ÐÂŒéýmt„~Ó»Oµ$š˜”ë½­‡s Øo8·moó/ïºÆÀýÒJùAc|£Eq~ig’àé VºlÛ,°x²ýf˃ZÐØµ[uq¤ø¹Õ§„œ@ÂëQÈÿè/®Ã¸Í5ïN‚€žsŠ®·Í‚˜#C/Ý¢…… ­®XÛ‚÷çæƒ¦á<òÌ– ³ê,ñ!åómî+hÈÿ0ò,”NüXûh½õIPýõÔò¤ZLÒþÕ¬@-X$˜%î‘ ‚ƒËŠç(G“¡+s9dÿ¼ž{Æù³õm݃±ÍÄùP)ŸrŒ› ßZÀ¾»ôYc³ë_2¡á¼ÖWkaÝê?–°Sµw4Xk[OÕí@Ñ›û3[ùµí¾J^[äß¾g)Æ÷´^zÆqeP*Tï§™P®Õ­£h¡Aÿ»U. 36*Öv&‚/\ ÈOàþøB¼ q>ÃÇWCŸ=w|Î3/šI°sG«cò¼LXœS°ïà-H¥&f-猄¾O Ø9šlÜçßïuQ )ÍÉ‹ûÿ> ýîe_{iVב’`n½ã22¡Õ$Rçη³F•Ë¡skmñ­£Iõ…=K“Åù£ðŸoÄ~ûO>ÖrŒ/{Wµþ·ÖI@©ysoeB^€ƒi_k-,ˆ=[§k#g(k LG“ ým–LœZRŸœ£Ä×5†¾Ÿ*Œë3 ÜQY1 ôö¸$Ÿ|-¤^üظQµ1`çß×·A4±2ÝZÞNð·y¤ÀûËþg~Œ_¢ón—“ñ¶f9 /’ÊG®1ÓBþú½äÜ-gØ£ÐTÙ,š4ûýIã ¾>®ñy;ÏÃ>k´°P:KÄM„2¿ÿJ]Ÿ 9#‡}ió3ÎØyòM'° ž—××}§Z6zê úœóyŒ¡¤„Æû×nèDX™ßkœÔ#<žç8ô™ «Ÿ®¯xl…Tî¼Ëi^ÏhRæ`“Á&›ÄñŸÏK¹_©a}J1îé¨ÄîÖÛ!;¤Gò¥™ vÚ½¸wn6dí·ºYå›èÝ!¥¸;éólñ›q¼,ÍóŒ¿OÞ_¸ÿ»>Ïñ9Ú½æ[·'Âì¹çË_ë™ ãÄ~œ’š G;Œ¶j½wä÷Nª]Ç}ûê(Î+øü‹÷™â´Ê0þ–}v÷Ú%B긥NµÉëÎ?Þ…Çá|ÏgíŽ.“³&k¶ïëÔ¬õ‡öy’Ê{^ßûAäBÿ“/¹ãZT¯}|E"DlôRì­— ±Ó'Å7º” 5½kaë±iÍÉñšvø~D¿)ñNÒ­…ï3KçKñ×0n¹&”›?êSÐZ&tÊûp(""v¤ÆjŸgI!h€G“ÀŸ\OÜ $½W¼Š›øMü ÇIÆË3ïó¨¥U"œyÇþÞ» ¨)ó¶#<úÏýõù:ˆû­rÆ%WoŸÿ5»§ö™–mŠÎÍÚž -§š*ß·’f 9õ6Š´&ƒZ?øŸæ7ó‘þ$¾gÎe1\?Kñ9ó)ö5=l´Ùêv-¶Éþ1Ú?öU¿ú¸Q°wLeŸñùQäcÀ÷”³CKÆ aûÛZá³$rÖ1 ð÷£ÏgwV÷o"€þqF§2 uÁÓó—dCö†™íBŽÐ'æ¾H‹"Ž“v.›Ý1P¬þyù{çü%Îc×ç3ÆÔnæÛ  N¯=·ù xÜÔÆìŒ{6,­»èÂÍ Î`ïy¸‚&Š$¨Öíàâ’zêp¥è3o˜wrŒÛ5¿ŒÚbBLñg½ukä´–M™64Š®lº*Ô$mšÅ·}Pâ7^š#ö\¬>o1ô«VaüªzMLïUéX'¿ ˆ5#}xçl°7ß>«Û%Wèrë@¹Ï£ÈMj'<0ðù0Ï>çß·>¿1Ý2·}‡[½«>ne¬Ê\pë•9~Nò+7j¹Â }#™O£ÈŒ¡²=c/ˆë,>snKi÷bÑçTŸï‹ ¥+¤9ÛÛ]‡¤Ã/^MóÌ£ò‹žÜ3Ά»7G«:C;¿L׸Ü(²pSïÞç—ôY>ÿæë,¾ŸÀ9?ú¼ÇøÛí¿Ù¤¬ˆ‡Âc'›˜“#Ò,j¾Î‚J[Ó“³¿:Bý7¿¥&D‘¢Ø9»z4 Çûë[÷´?ªx.¾'þ\}žcÜFå=4<£<‘³Õu7äeÁ–5¡ÓcFMÅ q‡.F‘‘´\þVOò–kWm¬ùÉ:÷Wï{ÊÑZ‘d¸_êŽÏùKHÃç(½"-f@¿27ük¥fÁ.Üêq¯‡Á9[;ûx|Îá=[Ïħù‹ŸŸ÷¾_ËÇO}¾c\Íe¤Ù‡8¨×(ÔoMŸ XqrÍÃiª,Ð^°Pöo5ޤŒpòZ±n^£~l’¿øÞùøÀã—ÚoÁ¸¯×œª\-!&NŸ7ºoGü¼zîF®/­´æÛP9ÅÔµWT)îzÕmÝ‘÷ÊÇy>Žúâ«0®r{ûÖ‡â@aiœí.ºM>x, ²’„ÙÛÁ±ƒžËæìŽ"nÇ>Úuê îëòïç=/ y:Œ_u©ë£›ã`QPL¯U3à`ÝmGŽíÊ‚b7Õ¤Öö£`õÛê‰k¢ÈƒåU7ÿl@ø÷Èç?¼¿û®æ¥øîFK ¥ÐøÛ›1;ã`IÕ”‰oi ­ÖŽŸp}UÝìg÷¨ñl?9Š4ëæ»±ð¡¿XO|ÄëˆÏ³8ŸŽó·õùŽÏ©£&ÇÎήî”O¨}}j£®K³ eËâŠcV:ÂÍ®ï/"?ÍÈX]ò½ò¿‡ûTóuÌ`jëßD|_YŸÿøœgåµCNœ‹#©aóè,°É:7­™ÑhØ›<ÙÒÛ?ŠÌ õ=ÚÎ_ìkB¿¼lÍ9œïjØÝ—p^{D”}¾ÁâµFØ?•ž&Íg9]%¤mŒ"U]Õ^ÿÐOœr.±°_Ü8ßKŸ÷Wð玃w3½”Þj`JåUÞý²àú]–;@­B§¯¶D‘èõ³Ž¯Úï'r'9÷Žûëóã œ€8°Ü7'ìÐ; Ô«vרª}LT¯IÞ1 ~[?Žó "Q®ÔØÞO¬O~®À¹a¼pŽƒá¸ªÂçT±YÑdÈâ8¸p iCå+ l©ßbS@#ÌÿÑ9±é•G€°ŸEâ÷ÉGüuÐOä€q>¢!?M‡ñ:Ü1Vn^ܺxJu|¿7Z×;ºzx¦Æ ¸æ? N4«TÇ7Šì¤Û—OKÞ/—x]ý‡Öh)Î÷év‰kÉ燖“ÊfA¨Û™];î §$r7Æ3Šôz—¹)¿¡¿ÈÅàó·RóqŒ—NqŽýâ`Ú†·®¼ÔÀÙ -~-Âu¨¬áºr«Ê ëç󳇎"n<ɸcé/òÓøøÉ÷· ÷A¥WÌÑ0Öv¯hò¬@7øÙñU&ìüfàQã!°%&9=°cés¶CßNþâ|óŸø˜îïälßðïFqð¤S‡—Ûîi`篛osfBúŽ©Õ-ëTnW;ŠS¼\qÿ–¯ûï¼yõtcàß›>o1îòYñ“géb!Ó†þvºwÉ2=̪}éqX2èaŒ¬ZI½ññ–ÿý<¿„}ŸÐäSv»ö1M„<Æønµ–9|TÅÂŒƒÉw¯j ò¨ç÷ÄeBñØ€7¶§íá˜ÿ­¶s+DÓà'î£ðzvÌÎýîT»-聾··Å¤JGÎÅ‹•º©H~•¦ê™ sÆ.Þ²á™#ì^ìUøCI„~ï'ö >_æãç/ã¨DÈcŒÿòEó¢Àƒ±p=pø¦>Ç4Ðïiþ%“ý™`U±žYÿgNà–öQIæìix^ê'ò4y=ö5£e…Òwë¾Û 医,y%×Àæ™}/TÌ„Ó#Í|9Têx¯$ý¦Ý ”ÏöçÂ÷—*îòùw)®,Æ7 ÊþTeY,8['?¢ZÞ :ºyfBØÕãg†wq³Ã«ûW©EZ=²i»ÑOäñòq±·ãUÔX®h ݆¶o³ü ¾¶«ôi·k&$½½xgµ+ü<´»uv­(âT¿³òîÔ’÷+Œ#ÏÅñ„sÊ ÷íÜ1~*ÍFËXØíò:ín ´øy¦ 2aöš¼ ‘jW¸çûݸzyCqeeKúï¼®ÅqøÖÛ2|ÎÙ]Ì››Å‚ËùfGÊnÅñ±Wƒ¾ZcÆÿžùàŠ X¤mMÖ¼W’c#é páó¿ZgË1ÞÁ~»N-RíªnÖ@Ìx×­»L3a™Û‰–‘¿áЬài)J²Ë“žè­ß Ïkžw|ŸÔp}­Âøù^”Ôª‚!3_.]¯·ý.¶õ=wS7u†¬„2“ÒÎ*‰G¹Ù¤pÛ~,rçù>‰>1žõ²ºm"TÐB¶7q½¿®=®ÕèÆÓ €fÆ¿üþr®$[ÒŒ‡j/®÷G+_Ý6jó¼z¥Î–J#+,^VA‡–·†Z¬Ñ@ÒæŒ“:ÊT½upA•1Pó}ÿ»32•dòï¥ós7¬#|ÿŒ×ƒ!ßT‚ñºø©ûïRÁÞ…t£S—Ö š—“áËM‘'¹Â$åø½Ÿ+ÉÆ°œço®#<¯ø÷SŠ{ŒñÊzUia±V›B}ú^Y­.?ì¦DãºËréÚã§Œƒ¦eW,ûš¥$C«8½­«Ÿ8Oáû¿ü‡Ïÿù|®O ŸCi‡æn*XÞqÃÇÃË4`Ù»úýþ8/ÕçÏx˜ðµýÝuW”$‘+öô¹Õ|þix*Ãxô ÚßFF[Ö~ïí©‰kíLëÏÈ€¡ÃVšÝ?¦NÚwý€’ÐÍYÏ»%yÅçWB¿ ûñ?wr|ŽåÍó>h2sOÄÏi8ÕúÃæëC3 ìÇ11Òûã`ëŠ'Õ+às¶ZŸ= Ýøs‰õeø¾UoÜ®ú‡ž$l_YnÇÖÇùùŠšÛÝ I?‡pBIªW•¬¶Hóy¦ü|ßp¿M‡ñzŽZš’êI xïèFöPÌí?lH­ x¶àùÌÍW]áwÿ6aþŸšLZgô%ü<’×ßO2ÜŸ5ò*”:é7¦T‹’°ÝV…#>-ÿS^˜éÛw Dþœ¶¡:Ö¤HeèKø{ü'¾ ãõzyÆûÔëìœEç?†>Üò\›|wNÓ9Aýî“Z6Áï_!·´ÐxøŠý‹.þ¹ùüžï›rF¤øœ=©-ÃÈu¶ÃbÕð^h^îÓr£L 8d{ÛäÇ;@3Šÿ¸¦$KŠì<¶ºú’ÒçGßK­¿Ý1ÞÌýïÖ,´ÇWäà•æ—ëU9Æ+|R5±Ñ‚ëð¹r•71Þe2£Öq ¸Õ”ÛëgI—‚ÇîQ{lZž;}YSX—â¶`œõÇ•Ûb®ƒþëë¡A²Õ»4þhœkÏ#àDªU5%){ÇQ±ÏÏ—Íw3¬ù~9ÿõùŠñö_ëš{ªÅuÐØ}é}²Ž»ú‰Îîäœò²‡•/ÔµLIôÇ\ø¹ø>ÿÞùþŸa?0ZQ(µ_ØÐ+úÞ5 TßÝm±ÏÆg›.×Àø _&f‡8ÀªšuY¨$­ãN¸Üôç üsò÷É÷y+ÅÝÄç|Yª'þBj„CÖ‚Vh£Ìi`Þ“ËmžF;BZê/é¥yJ¿†U} Ï_~nÁçÿ¥æ7oTR±åªk`â<û³´¶¹gbé ª³ïïÚätv–7]Iò]-“¶­¤>x}¢HÌ3>®®ßÝñ9AK*Fv½c½‡ýj£€nw/æG@?ÛSÃ.8tLÇ*IÓÐá WH| ß?âóv}¾bœ{1‰?2ž®[2jÚ¶ÔÀ纖=ïŒ#ŽÜH4wU’ñ&Ò’|ÄqƒŸŸñ}sÞ/Jí×a|‰þàã*4w˰h¡‘åêLÏû ßsÔòCµLÆÀè—[n˜¦$­z4Éñaûi¯¬ù¸Sê\ã¬S#ñIµ«0­¹éùº¸n)¬ êø5jiþÍŠ¼Žy´\I„}ñýrÞŸ¿ó|4ìo:ŒÆóÖÇô+ài±úÂ_ø9=NNž6ÿq:”Ù|iuwgˆíÕ|تuJr[íÕ,背”>ÏÏÇ%Ãú3ZY(wnÏó ÕWàP0µƼ°Jé~7ì-M¦>ww¯–7¯Q’i’»~“‰¼M^/ÿ´¯(Á¸ô¯Õt¸G–¿÷áG: œºµÇ·kéàZ<° r¹=œò6½:UIо¬ÏÙêìCxü: tï7ô“øy ÷¤×§`èî õehÕpåúÀ/é07¦ù—»çÒ¡eÆÌ)µoŽývæ%×_cÖîˆ˯2À뎟G–:/ĸ”*zwòexÿêýÙê…éPáåƒ ‡ƒép«0]mÿz„,P’Ï;]×®*É7¾Îä÷Ùøþ÷JÍñ9{O?ëÕìá%èãy'(ýA:¬™:õ’Ñætðpíl’¸îåóYηã\ÙRœïU…Ò'9OÇ5ɺgúúDŒ>“qÃ=_–š~6'§Üà~¯_µS’¯LË¥¨}ÄþÍë‘óQùûúÇý |Žäµ·,zöEèÓûÜàeÇÒ¡‡qÀÓ»=ÓaF[ÖŽ„¶›ÊžßÙGIO¤+‘cZzÞQ£ôý?Œ[4ûRךU/BÙ´m]NÂ:ª¿?lO›t–×ÃàÃé^8—Tý6rSŸ?öéUâçæç-†ó~wŒ6'­LÔÙ °¢àÊ8·Ãé )JÞ(ªnwNÖ „m÷o`üÄ-›­ïVò~øü\X—Õ~ލÏwŒÛ°âÄËg\€ê>Í}*ìM‰Ç•â-5ÒaÑ3Eû‘ƒ!`sP›-qÜyæÔÅÈÉGä ãcÃÒûp¯ç‚}µžõ»Õ¾÷‹¹ûß‘VÎ=*¦ƒUHÅ·ÚÇ‹Êó}±~Îý%‹9íR’¼Ÿðy„ïQüóëóŸã´qb5ûú`ÍíŠåzoK‡±N+ÇWþ•.W4Å:)|oÚÃÕj¨’è7ª¤Ÿó|äã$ß÷4º²²v½FB¾cüO‡—‘ÊEÑàE·™w¦ÃñÁ¸Àü”™Î­í¾ÝúZÊŠá{éÿþΡªØùþ°ð¹oŠùR_šk.ÞßÕçûêB©ð÷DCí…½oŒÙ•5ËÎo=îMÌØýrþ€ÎÖLñç}qd/kéòçøiü^_wÑÙs•í-ظ×TÈw|Îf#®–í 6^ÒŸóË(zÊgi;3´õ›~p¤¼Ç¸ÔÞJBwËŽöðaó·íâýÃ:•b¼«g?æÎ=ßÏ™üº¸5öts©kü0  [X;‹H)ûJò²eR—Ÿf%ï¿>îñ÷óón|ÎmEkŸÔ^Q0dÛúΛÒaê+¿]ºŒ4ØcwëW§ƒ ¡¸ª$gÂc»UmâCÆÕ7ð™ì±8þr_eïx›2û$*!ü]¥CjîÅ£Q)iмiÞ€ªþCaÐçþ;Ë×Tý5¿é>„ïüÞ _/ñý Ãû&rŒïs·zs¿!JøfѼ£ÑÆtÐ;t(÷J, hßÐqÌ(øxµA•ÊJrxç³1&§}?ý§ûˆªÕüüFžÒá.ïí¡MZ«3ið8öŠq•ò0/9cBNy%Ñc6Oùüí~‚7Äõí?ÝãÔás¶\wâPÔN<}f^H:ã[§Á¸=ãšžIr„s™?Û+‰ü§mzc|þ8o=%r9 Ç%£58.e¯úî]$4V5#kƒÒaX-Ó¡ÒàKpxGÿ3£A¹6Sá÷ ²?Î%BD^²á=G Æmô¢ê†«g"áp5U…·]þz.N÷yŸ¬‰ ^§5xÓ\IòFŽkî»P&žk Ÿï…¸.3¼?,ŸÏîRg$Ì o–<N‡À†‚ÖMJcçnÀ®ëŠ#€6cIÎ+*q˱NÇ[’ ~¿ìàÙ·n~GUeºÈÄ>Âç‰üÜŠËú|Ƹiå?uX|9Ê®¾¸âñQœ¿åU>g™]ê¼?Õy8xì,~×#n]yiŒóÙ?ÏSx=òüãçdüœBŸçøœ±Õ‚Wµ_±óé‰g:·qg`³4h´|XÅÂßvÐy|#£….JRá/\úˆç·|Þ,ìk5ÎãÕç5Æ_¾³:ºS\ñn?0P•Ϩ÷Æ, ü«ò¨xÑÒn8þ¬$; çu­_Ò§ø~€pžÖŠ+4òzm¡txY85™œ‡ÎaÄæÙÍtpt£æYã4ðí¾øuDÇÑàûËf³y?%éVkùÕ Jú6Ÿ_ yÓ „¼©/ôiŒ+p`σ¼¶¹ârf:l9°Fúý•&›¼ü4ÀMè˜ß§³’|ï;xų§2ò纕ïw <åæxÖ×¼ WxRŒŸgçïÕbâ9°OžZ¡-®K’ «t©™¥†N_n8È,\añåö¸”R’&Z¦˜„Ëþ¶¾äùÈçÏ|ÿ¿Ôý|ŽsçkãrÎÂJ.J‡àâ~òf×Ô`ÿ±²óªfn°ÊoOSçZJrhöÇÙ²t™ø}ò¿çÏ{füýsN´>ïñ9óß è~Ê=½°fó·t¸kºÉíóQ5lªPn|âGÎköLJ³=ÊFûˆ÷tø<‘ç%_çîË1~ã ÚÖ²)g Áþ£VÔÀ§©VêlRçe;~çV[ª¯ìqÚZIO{º+Öœ÷ßÀïQÎßTïßDo³ƒ§a¡›ª®?_N—Ex©áÂu[[—œ`©¤ðd&æùÁ›³FÝèÅÇáñ~ŸÏêóã5özPT£ÓÐ1¹i?\ÇOþìÒÍfš‚ß}2-81¶ØP2¹’x¼Þ£ñ\Ÿ¿ÝGàûŠΗ ?·‘w¡Ôÿå³ü«NeÆdÿqÖ¨aö×”ªj ¿î2oîóN½Ù’ê¬$õ\“ß 8V2Ÿãûü~/¿¯Éç†û |ÎÕ±on—‹? —6-LLtÐ@³ˆÔ¦å@ ·§¦¦H–;@ÚÒGŸc¿¬í¸ÖñµÆG<§äçs†ó|)ÆÛ»¥ÿ›fv'Añãëw $L÷|»´ƒ òäÈ)s@¸ß¬$ÕtBåÅ%ã'?Âû*¹¯Á¿‡RûÚøœ®ÇÝ7*_„ÃÃnk–©–jàÔÏày Õpu }h×pps|cŒóòU®{l®ë+Ö-ÿ}HéuzåR¿C‘aü¢ãã–y‡®†nô Ô@W¥ÖŠÊjhÝ©‹1ì ršþš<óðÚ¼ã6}}Åßù ó”6,;_ÄxÍrÒMkUŠî?Û¸M¹õ†ÿœ\t"Þšä?Ì‚ÞíqY:ûmÆ^‡ξÜG4~ï‘Ï;žw/–7…|Ççœìú%´èÚ ˆ¤ºvÛ¯Ik;óÔ›wáãµv­÷ ر+I}‡Ü«'ûŠyÃ߯§}¤dÝ­âºzà‰ç÷]í­`ë-o[²¦ƒPø—K׫\O¾$mú ¬/Ø8´ûá»ìwÎÐÇùú̸þõþÞWô|Öøï‰ÎÍ4In¿ª ð¾¬Ï{Y¡tm¾Ñ·5¿ÃÙØï§Æ‡k`Ûsúû°ïÌ™”½]€þjƪ­’/ÈØq×÷oû Â÷«û¥Àuoü¼SŸ÷øùý¦‰FÁÇA8wÒ€ÝBçœåƒîÂü-³7MYà ;o ~þû¥bÆ´Ð+&ë?çå}‘߯Öç=Æ«è•7½öq0#SªÕ½¤%·W³«yœüO‡ÏXåô´bUu%1I=·ªÙ:ñûøô¯Åßã󣆥ï³büìct!t „}T _Ðbé ݸZfY.qqƒoÞ´¯­$雬íyŸïSòýÏLëõôúqdÓRçd2Œ»âå– £Á¸fu÷Å>Ô@Þ5ŸùçïÀöy}R¯rW·¤Ïm\ò=–¾÷t‰ÝÃo üœPŸï×oÿ”LÉícð}[ñ½JŸ5 žpñlºïØ¿·Çãnáæw«=ÍÛ)IôñÉmzûþíœHx?Å{QÂo^zƒÏi~ošS½¤cP¹Cá¯HÓ èT¯Y™Ycï€Ïv:ñq„ïKOô…ë¹J'ÊwúVÙWœ— }L-Îó<Üè/"%¥îê0~Ý»B6®8­ŽW‰üÖ<$Þ •ÍzÜ–vSÆ>í ÖSÁðÞë—ü¼ßð:ú§ïÕȧPJo•Œ7>‹6LÛlß5.m³žZû,®¼ë[ã=#¡uÐåêÒÙJ2sPÆÀƒ›}ÿøýSÉï“øzÀðž®ãç¿ Ga«t݈ýC2àþpuíq_RaQÊäõVÅ#`Ø»w•rV(ɘ3Œ/9¯ãó᪙ë¬w?÷„~Y¿Ô|XŠÏ¹ôkÂÁ^…GÀîh·½É“3 öz¿qµ©â¼ûͲkÝÉ”Äk•i™÷-ý¿·Ëç3†ó`wŒ—؇vÒ#p¬Î°V'–e°ý®Tè8¬þë2 ¡E[§žu½•dp¥oUúMñ#®ÏOŽØwï­}‹ÒóŒûuÄäc¾!‡¡F·fŸ|3@‡øoN…O®/Žž5ráþµ’Ðÿu­‚uâ9´†•YQ(ö¡ï´ø¿Ø;û(§ê;GE6hÝÍŠbtE. õª/Z4ÖÂïúR­b´#õ%¶«­äh¯u£ÈEW²Ú—´kk°U£ƒ5U!±* 0¸Œ/«Wií¥¨ r¢í>_î󛹌CÿìžjgÎùûGMæå÷dùuß}/žõ•—»ÔÔܧ6SYrûÐ¥ÕÇÖËš³?ñú©¾¿KçK¿Þ£ÿ{ûáÿ1°½5°½õYÞÞ ñû$~J“~J—{ûen"¦÷ð·³£tF7ÎèÝ”%nj9ôRjV›;ûeîiÉb…û-n!tEëýC’â~K„›Ü `ÑíÒ->J—ûúE)É DÙïv@“Žè·kãÜ® Ñ—R§‡2O¥ÞÕoqW_{¯´:D?t…~hñPVúñCË¢xüd»VÜW²¡e|)²¡•ãv­ÝgC+Ïmý·ke[?E_Šìº$¹‰鳉˜å&¢ìºè¡Ô»úQîÖÖè½ÊÑ•’ +Ev]’ÜC sS¿Nç•ÃMD«ƒ2膮ѓ’ ì!æþÉ2÷ôSÜÓ·»)6·ÆÛt¤ÈÆK”Nè {Ò£{R¶ôå*J6³ºx ‹³¡Ï~‡ù}7¡E7¡Çmñ ÷´2ܧ5è vžà6½„ez¬²tjç‰ ÅmCñX¥¹Kkp—V¶´Lúõžx™»†²I[ç––lÒ6AŒn`nà<ÝÀ ºNÂôWÕ¸G›.½ÀEî&¸gæmƒÂ„zC\'É€ãD;Ãt×èa­'°ìj‰»Jö ÅsâþޏìÑzw•K'C!à®*pGÜæž¡„:M?C„~†÷iƒlÅú"ƒ¯7Ä nÖé8‘-Cq7$énˆíûjô;Ü14énÐô}ÀunÓæ¹µe¡H ÷`…Ûáin‡ë Ã6½%ìfÕ€ApÐ;Øšà{ËÜÍ·»ô…|ô×ú{¥w¥sƒ]+=ìÏ`Wö׋»êDéC;ÔÛ…ÒÒqÚ#,Ý&½&=&&%}%ý$ݤ='ÒAÁîѽ£;'ú¤ÓäßÿJ·è^Ñ]òÓ@Ùýõ…îéù%¿äRÜÚ“íêìî¾)Á­SÙ¨vé$•mSñœÈVi…ûxqºŠÂ;Z6ïštxçé#*Ó1'›Ñ º»ÅÛ]à/Â47¢óôœ´îâr•é»<ë£ýM:‹> quʶ~š7Ùjným):~Ä¿){Ì%þRLò¤éh3Ùan¹w£S6— <ⵇfŽœ úKÄ·&q®\G \? \?„>×Q~âN‹ÑÖâF²ìqFq°îqšô{¯±ö$÷‘eÛ>Ggšv’„¹\ã®}†[œ&·8›À¢ÏXï"WèÀ”-Î0¸Åé›.ã]ƺŒ“t‘D¸g_çgxô—ú¸/#ÜàlÒ‘V¤#Mo!‡¸…¬$Úa¡Ã¸N‡±8Òêý8Œó#|w“eøÏð÷‹=ûÝ—²g_ ìÙ¹‡,ž´2÷3ÜèŒæïÛ×¹Ñé€0ö<ð€Ð—èHÓ[Èâ½ÌÒ‘£÷R¶íSܶrÛ¾N±ì 7éq“þâ -è/–ä ¤¼ñþþe1àF«q9à dí»”‚Ip¯3|¢¿k_&½ÅA/Z{‚ïE“ýc¹ƒJ\ôÒ!ò¡»Xw°ôoðÚ¡o×ãù—N• îKgê~<>ÔÛÁޓΓ¾“® öÛ®®úvÒÌPï5ƒÃnѽr û¤#ÔëÎè¯+‚× úé‡`/hÚ®ºÀà¿æ&oŽ^¢$Ý·²½+»»Þ`ß(¾[‡[»â@/Eßì=‡âªp'Wœ@²ƒÛO!Ý?aº#½w‹l5镇¬øcËø¤Ò#}`c´ïxp@sŒïõÿŽl€g¸E+¿àjôêˆó/ÏÍYÙð—™ÅMÙ=95Ùí¦—5ÇMYÙ“ ã\¦éÕnÒ‡#¾¾$]fÚ»šˆã`”® ® BŸë“_‡òÝ EºÄ‘]v4€EŸjkP¯O5<Ø÷•ոѧ«L;"ôc‹ïÆ@Б.ˆÑ£ ¸±Åuã€&0–,ð@œÕ6ªE:TSt%D¹ÏÝ&•-úSËtܤޏt“•¸}Øaú°µ#A»S£t§6èN7Y£wja„ï¹'¶x´»Ä½îxÀs#»Ý¥Àn·8Ûô“i'¶jÀàvw˜øfAX{´@¡/ÓM¦}ØâÌÑMfÓy#eæv·ÁíâÂvéGoªKojÐKô¦Š ÛFA Ø(’RÀIV&©.]’â» sÇ»"Üñn‹¾Ô ,D™8°Å1)lý^Å@tñßbš¶øÿ‹Ð?V&s~wßk-þš"½‘)îJÀ=Ö {,Zƒ}gd-àIh‚xôYk_d‚>k D Ô‰`ä€ lº"ƒÎšÂ’îã«áÉØôX·è«©Ð5æXV¸À¦«F jtf鯊ãy#8„Ð1ºÅÇ´|¯«ø©ÄãZÎŒx\é£*à¶å~%Ô(}ÚâÒ.ò‚ ðN‰oªä/R“8Àµë¤ë¤Ðçã:)ÎÏ£%?æ2Óï×v¸À¦o5ð­FqØ3teÇpè‹ ŒƒŸ¢×Ϡׯ ,!\`#ÐqzV#·Ÿ‰ä€bK´A’ŽÕ0«e:V3ôc›R¸ †@ª$~Q:ýêÀDÈr ’[D¸ÝØQº±ëÀ¸UMºU]ºUóÀíÇ­Zí½~líù«€°¼VÊ oz´ïÇ6â }a¹tŒïÇ6êh áÎÄò<ð€°—ø$¨ƒ®líû+ïK¾+» "(Ô…RÈ—^ÕýxqzU[ã|¯ªøò¢(ŒL¯ªxÿ(2K$*À@™d@ÄèTmÅ|ÿ_DQ0iPŠÆ.°éS•ÒIƒ ˆ |2{Ý…¿Ã× zøó×Ãò3çj‡Ø Ãa.îî{¯“8Ô%¦cµp¬º †ƒ^àaOƒ0qèóÀqþ"hñ}×%FRô]GèWm‚‚Q-@@Ê IÔ€A¿jƒ~U‡~Õ“-§çZ‚”u`ʵ£¼ŸŒPåA $® 0°,hSî;.°¸¼\O¼ª-zUKôª&ÄâûUkÀ@(3#|¿uÄÀ× ê ŠfA£|¿uXmx †ðæ@˜qxczýÖm§ßZ‚-~ë2ËûÚ  =<@àK ,÷¿2ˆ"üh[Þ÷m§Ûº-÷†¢* B÷j…îU¸ ÖǽZQ¹Æ z­³À1yŸœE’8M:­­€ÓÚ¦G5LjXr?h‚ЧÈòIú礃®…ÿ¾;8ÉçiËÿÆA®€³\á.Äq¸Ë Œž`à ;ô\Û8ð%Á¡Oƒ0qøsÀ1„ <GŠ  ED 4…€äA ØJaI ˆÈûæ ‚à8ô[[PxÀFJ Œ0¥@•ÀB¸ò -+…Õ@AKÓmmÐmÝ&‚瀰Àð@ A,ÄÈ2C™’{‚Ní»#¤IP“{ˆå5BPa¹—x´ï½6^Ô@!Nñ½×¦Üc š †Pç€l„»Z Ž—AXþ. ÔIïµâ~´¾ä{¯+ ŠÈ‚†x¡Q9àí;¢‹  (ˆhË{8(Š 0Ph¥‘-DyT@’5`¢Hà…’mù[)K(— h%“ˆ£lJ pYGQ<h[ùl9×òñYîàOsüY¹î¯{? ½ûÙ¹Á¾•ïY 8°YÐ6n‰‡7Ê ‚C쀰äžFà‡ºÂò7  ,ðhz‰‡= Ê ‚CŸ5Åáw€ l„ Ú )¯‚0‘u`"YÐ&’M`#(Ж{&˜2˽“ !÷-!<¶ü]h˽ÌQ ˜òz)p…@€âV´€€@[þ A+3l)P„Îu`"|¨ƒ¨¯4€0æ@˜e4@L^{-`#¤M`!¨Ð’÷gØ(ƒ°¼* ‚; L9Zòš]„: *À@¸³Àq„¼À '@™Oƒš¼f‹àg@ ˜(€,ðä5\A´@…PJ! ¹´€‚(°¼¶jÀD¯fb(Žh8zµ "(’ h€ %\`£Xd!4 Ê¿VÖÇ*÷8é'ý5‚qËûw‹KßK~sI—{ÖÆû¾ëÙ =ûr?½ªòâ´G»”ïåhM¬8Ïç¶wŽÒûïn½Ïô+;«Ú×^ÝK†~fWƒûQ6žgõÎàuS–¨óùÏmPåÿ~£ãÖt©»æ­|éµ'©Cozö ±WwV}ŸpïNŒÞ=Ð{áz÷@û–´7Xž'…çYûæÌh×%KÔü¶ì?ì• jËù?lF¾ß¥. ]sñŠÄ7Ô ï úð¡YÕõÍžÞëÅÐ;é³&<}ÝÖž*í' ú^²xžŽNZpðÄ%jûaïþfè[Ôeï^»íÂ.õÔÅ|yÓè¸ZëþôáxžÃÿxÛò.¹µÏŽáꞯCïêw°Šxü‡ìðÅü\VÎ>êÚ7(u¢ûøwNëR':ñÛß?U=²dÁG7ãûUý®óÖÔ‡oíÙyòw¯žëÙÓ?—à¾K ¿÷ájéöÍEõÐ…3,t«î3§ýÓ1]jå­î<þ²SÔícgüã WtV7Í»ígWÓ³[ïïRä^²¿ëêâñ&?vý¤ë_+ª£N:Íž3º[~êíÇ\;¼K]{£õ•+~ô5µp¡|#;«¾çþ“þ)¯ç½ž½½³ô…fo±|üÖéóêEµªkϱóÇw«··/;äÀÁ]êÌA{ž0û 3Ô ß]öÊ»³:7úÐuçMÓ³Û£÷£ô.GpÈÀãþöª«^¿niQ9‡_³*vZ·Š¿f¯?­Q_[Wº,òÌ$uú MîúqÕöF¯§«ïÞºÞÖ;,¾ÊÏsö”ʯ~{yQý<ý‡‹S“»ÕËoÞ0õŠúuSýÛÞô½sÕÍßÿóæ‡ê¬¾7U{jÇœž=#ÿççNÔž`¾Ròù¿$bÊ"w»ÕˆÍÃ+û-Y£>œµ.ÿ®yžÛðGtVe•oÑs½ßí¯ÑŸ·¿ë2z§]š,ÿÌßüøO3«ü~ÿyéþ3ºÕùï¤F´g­Q“¿zpÑ;ç«étŽ8|Tgõë—¾ºéÌ¡={Ì:WÚ¯¡Ï¥ÿuÚùÜãyN¾~ÞÁ£¦/R[æ[_H^Ý­^9ãÔö'­Qï&f,éX=E]-ÿþˆaÕ¯ÎîøxÈØŽžýqíw ú¨jx¼Ç_~oý~»-R7OùÂ!¿¸±[M~v¯«þgÈõÀÝß¹~ꪃvˆ³:«ü<»ì/»uôì°ê]0Ý‹úû£÷ovÚÇÃóL{¦kâüs©#Ï|íÊðÝê†øÆ4V«e}ýò_Þ|¡º¼in9~hgÕ{råØs–Ï©ê~wQ®{Ä¥æÎÞ¸[·Ø/üâáåö-Ro¬š~ù¶Ÿu«»O<ïÀ‘?^­6‡Ý·N]¨?i¯­‡tV÷9gŽzzÁœªÞ/Õ;@~¯¹³';õ±­Ê|u±ºFæÍWt«{eÎ;µZ•ºfé?p:eÖÂÓ¦àœ¼ôËùsW<1§Ç÷¤÷ºô®žÿõ[êÉëî?ez‡¿Wiãñ¿?cýø»«zÈÝÊÜvÞYWŽ]­m^~ûò«“jJ÷w\muVÇ9…ík?šSÕçOŸïŸDfÑ}í±;í §ð¸Þ01a,Vì3tŸs[Ýê¹Cž^Ñþh•ÚþÈÛËÆJª9a1MwV?Y¶ôñÓ{ÏŸÞÔ»ã¾î8õí½;ÏMó÷ײxüÞÿuj騢’µºÊÕ¾¯^7aëÚUjÙª×ïšthR‰õd‰Õƒr—;÷ÞŽž\jÞíõwðNP+:ÜÊí ŽðÏ5?tâñW­œVTsçÍüø‹ûmT™Ø„¾µp•zçì­‹ÿ­+Ù³«7íáî{ÏíÙýñÏ÷&.½`râÒ¯O¼÷XëÏ|ñ8uì+þ—½÷kªÛö½±cÇŽ•رcÇÆˆ=ö¨ØcÇŽ=ö(–¨Ø±Ç Š0Ö0ö ¡¨±£X°cÿÆÌš3¬à»ÏÙ÷ÞsÏ·÷=¯Ïó8Ï>{Ï™¬5ÆXcιòÿu÷˜][ˆsÿ늋ã2,hgh½Æ%êðºrÂÈ ‡³HõºždBJ´Êæ¯òúŹ^üúyPÓŽ+(ÁyJK aHí… }òN«Ò;ôQ=f_>o„GO­Ü+ƒ„]îÔ=‚þb2ûþ ¾QFvÝkï;¬ñŽã³<¹wvÛ—ƪ ‡I„6›.No½Ø¯V/ îáðHÍÄáø<š@±SϳŸ{¼¾ð:À}ß“— _àT탙óî¯Î·<ZV0 –´7BÁ‘/³æîWÞô°]1n,ÞþnúŸPîGÅý7}~-æYÛžˆóÜ÷¡äØýà_ºíò!‡aÉ—N«³òáN©w#ê¡nDÔý{O~ž³ÂækÈý¨8ïÀë]—gëÀUˆ{÷ûPó¶Ðsûa«îØ÷ˉPÄkÇ„×/ƒK©o  OJ…hÑ"‚|{ŸûÕ¾þœ‹þÆÆ}ã¼{kœãxo‡Ï9óa?ܤؓ‰ðûÃËøY[.÷ïóï5<[ûü~ŽõåZÏñ?äRÛü^ùsBð_´ï+,Ë9u?4=tmäȉð4ú¨Çc/Ë߅ÏÌ(ç þýtÓá¸Í7‡µˆë­¶]çÏg.©õÚ£ÑLÕÙ®ë,ì¾V·ó½wX‘!Íû¬j¼à†7Íì$ð¨\©õeè±§¯ß¸°{fµhϦ$ü󜨮SÕÿ N®Úê ÷®“p}$8Ͼùëõ>£«Hn[, $GZZïtJ úTÚ4}hŽ ”ZµcÚÖÇp¾ïOy<^ÒôÜw%ÖÕ.Ÿ¤8ÏâÆƒÃë^<{ö,^X& fžò"ž÷\¼}B|?˜ŸvjPí†dã¸aÅξUÛ|$9¯êaQj ZÏ®(pÜro/¿ÞÒç ,6äÉãè’ÕßF;ݿﶼûû~?HVPayDq@•WÙê$÷ßY#÷¼e>ØêGá9Ëâçùè—;aü½ƒ0ð›)ß§IÐ_‘¾3ü‚ŽWšÜ®š°/˜«¼µä©ã*’³¯ãHkœãx§}»µÍå<›9°ç$$ÈZ]èKÍÙq6¿]oéž]¹#ˆÛùŸ§}jÿˆ_ç‡ßÅ÷iïh qs­D“àq‡@Ó¯æÕûý’àKBÊÚ©Ýã@»àB¥úý¼aAý…éK¿…“Ï; ¾y@MøóNˆ——œ3`ǽÅq# çOJ»}†®©8& F|ïw¨|Ì”žj;?a ´´&N8Ù™|ÜTàšÚÆGúŠöüò•¸îzš¿ce/-¼2Pð ~ÎŽ—Ç?~ ÷‡‡ºùk0h× è~ëc8Ù´­©P‘U6þ% }ÿu[|ó¼·ã(â<…»Ôœ•{›*E5¸:)8 Þ¨WjÑáXˆ®VÁýû°aP6¥ÕÜ¯Ã‰áÆ£Ø‹N«luœ×)î[hçß‹ãÖÜTç™$] S à@ì=z!¯~B,„M*îå¨€Ü ›Ÿf„“Ê3vl”¬¶ù.r~çVrŽžØoXãÏ/AôÃÑ MKÏ9—çº]œéT7Ú,Ï•ùñípP´êÿý«pQÇ[¹%Pm‹káºgØøâõ® Ç´þÔ}Û”ïW7©ü2†õÕÃ`ãÇXaÃIÓK]’­jÂý¢¹¶?Ç›1F>Ù¯Ú8<®½ëÛÇIàõñ‘K‰ã1ào ¬Øy(<Ÿ?y}ã>ÑÜÿÑŽ£Î>~0nˆ»Óh§ê¼è§“âÚgå/S8ô…>ŸÑ ´ÅYÅú;öÀÖ!±|],æHp¼’ûû½ò­t¨î–Jf(Ÿü ú±¤K°²u³ ³bƒ2ÃI»3%Ê–™`«ÿÜTè»Ûž3ÜZÌÛ’â<åg;oø5ì( &áÚfpI4|ï³ï|ηµ`½3ƒ`ÏîÃI °EÌ&Ÿ@„uñK[ÿ$öÃTàxKÛž]qé¬mÚ˼¨‰äKî}û6ñtÏ(-ïõf¼ë<ÿ\0ÖIÇCSü|÷™sZT8NÐî¸ó_‹…GnNXÒÎ §64hyÞí¬i–»lë0üÉ֒élÏWá~½´}ož¿bÞ–ÇOÌ=¿çþfG¡I¾‰;zu5CýÞN&óû‹peqË 5ë ‡¾½º%>L'ÖÕJfvœñu‡=GIà4pÜš-î9ñ(¬}xrwl?3Œöàã"¼X:W¾Ã îïAÃNß' çÔë1só*[¼ñý^ß¹?-Ïkk<ãøGÛ½Zž;ê(dööh5Æ t5×\y.Ì«ÕyÖ²¡PìÞ«âSÃɌݕò©—®"öü’0ÎÇáÿ¹˜óç°*CšY.¢æÑjÇ ef^¿¥bå¾¹ÃE¨Ò¡GÑê3CµUúœLúITÙŸŸïkð}?ÎWàßÃŽ“ó,šN;ìc0pÕ\¯ø3lžX1øf¡‹pûÛµ…B} ôsX¹Bæpr ï~­~6?uþœæñ,öñ•â¸o÷X—[Ì`µ¿M†Íɉù¦ƒÄ+×±Õíp²ï*Ý` ÈQ—u¶¸áý®Øw_ãG· +ìcpºÒ éÇýfèÞzªã·°hX4ª|ýg=áuVµ?›Â‰iídmûlB˲Õ}î{.¬O˜ï.ŽoêZ~Z.ßc0¾ÿ¬µ“Žcù–¿VË€hxRuçµ*áýàó¼/—çß '^õÜó¡B áë1Þ'ñýq¿®Áq7©B–ž{ VH Ô¼®3ƒiÐìqc¢Áac…ægî÷…R;ǔ݃×;ßTپܵ ç²åôÛý±³åð={u¶6p¿fküã<3º+¹÷|xžºôÀ3LÜJr¼}4Ôsq¯|ªw?¸•ïCò>áy¿!>m £!ð€ì‚°^uÈ þø¡PË 0ø»Ï'Ôûlp½Êi[°â˜/a?²¸Á‰!SmëJÎWxìuíúS Ž›øsTÕÖyC|+ˆf1Ãúã[DÜ0€iL쵆3@ƒ|û '£æ],YÜ5ÐÖçñxö§ìû1)Ž{ºliê„ã`µuþh†±“ÒÇ.Ö M½tf‘ê>PQŸÃšpRv…ñEÆ×ÛõöÓÞxðþVœ? W³zHËçŽC¨|ÿ÷Vù’áû–ß-§`æÃ¾3§Ôõö­»UoW8éêãó¢F ­Ïã|a1wL…ãIË´œÒN€ÀýN†ÒLo¿·4€ñ¥×­noÈ{Ê9£…“"3_N_øÇz4ç:†sïžÚzÿ0¥äÌÑôž;³z2oùÎ¥ÒOÂüø‡BTéàÏ`'”Ê4ÿ|à\^ù¾ß§÷­œgDùŠg’]Âà“ÿÙžúÉPuä}ü¸ü§ôF·CA¨+X_‚G{½õËŽþ\òæ‹­$%/ëÔø>¹5ÞqžR ëÊíó™2¾y2\¼9ìò•bF¿û<ÿ¼×PüëÃIç÷›¤Ý_eß_Î[áëwá9Û^¼À?pÌ*v\µ9 ê]Ø“™Ü:6û.öê³ãX^<€Ú7Ãðaª…3_†“òA%Vu¸`ë¿ùþÿü¾OH+Ñ»ð°Í…¸Çñ‡\í¾ëV.¾µc2Lé;[º p«Ä׆„¢Í uû)œœ)õŠÜìÅóõ¹­Ï¸(íAðÝn&Ä=ŽÛqýÀó—^‡Ê ÐM†ÃzA¯øóP¤a‘®]Î{Aòæ8ÆS᪂A„Ÿo$Ü›ÕâÉîVð{ Pá:(p<¡/= ãNOšy³O2Ô»YàÌ1åyXÕiϘ{ø¼p÷=áQûW‡µZù½!Aø„óøäqãU³íØdèZ«Íº³£ÎAŸFÏoµ=ž¾v<”/‚œ>Hü@Û÷þÆØ¾_gŠ÷=,œç3döŒžž ;ç›÷þÛYh‘?ëʱVƒÁ«Ä”&GŠDÇóo»u:»Îóýίú\áú8a_Z±Îà¯ÃAµ˜N2,èR­ÙÒÕgá å—›e0èsívéâAͨm^¼3ûsç¼.BU±»þÿXnz# MEAkÉ@gëâr>/=RÌÌÁ¶ëßsÕ5³¦Úø9BYÅÎ÷]Šãí“î6nØúbï&ƒ§¥àÈÎág@w¾¶¤eÑÁ¶uˆòSëoh[ÿñ¸äü6ÞOVªÑéëÒ²ÂuVàø-ϽòûI=4pnÆý•Ì5dg …d0L…) =œX~îWÑ9~ÊÇÆ.vçT*ï$yµ«m}ÐÕp÷É0«–qԈǧR‰åxCù¶ôD(œì^ç¸Zñ8à^ ¿ü:óçžÝyÎS)òâÇó+t|#hQ³ÉÐiQ×]ÍW†¹õr=ŸÿÖºÜ {P*‚´êZ¹²wõ@Ûy­Ð¼²'ñ>ƒ?O¬ñãÓÓ¯M™:ˆÈÿöèô=É`ÅN¶> £veN0¹Ü´¼Å9‚h*ž Ù'œ;Ã??=]˜¯iÎâP¨ÿýÐîC›¬‰„‚Ëȵ‡’áÓ±Aê˜z¨ÿÒ½úÛh9ü¨{aµ[žòøæ\ž?Öíí¤fÐn×fýÕêBœ¯ÎF„˜ ©~[Æ4>™ 7fžØð-\Æû;71÷†)½kæ‘–‰ =^æmV¢EváuŒ~ᾸAëíú0 Ž_ píñ¿B¢@½äøÂ\ç“A.½’ ­‡ó‘¹¿wô‚J7Ömž?‚èóGusÊч]·õ×}®î_Ñ¡ÙyŽ_i[xBñ—Q  8ü±ÔÍdvkÁŒmõð¾`òÜŠÛ{SÆêA#°¿ë¾þù7 Ûs‰Ÿcñ¾‰Ÿï‰óJã›:äî²¥å)¨U¾Uáù)ÉÕ¾eçu¿OA®ßo_êÚvq¾‰5®qœ(YÊjg§À¢9ÿM™ž «ÂOa劂®sÛï®ÞËò>页dâs¹uÕ™¦šÙ÷Çß'ï»:¬É:U_ÖµbW=xéŸV¯ƒŸïÑ÷•[«‰‚¾÷NEZ4>Õ£b±y"Ƚý+OoôÇ{ü½“ëwj(;VhnwŽ'ÁñŸ¹¾.zAîç$Õš¿N†ˆ‡ß¢ 劂zËîú\Lñâo[…UªšÏ|½x±a@•,wxZû®Gåµu„øÅñ Ñ0­|gQÂY2¬7ï_·|W$¨¶n¬yÎ^,¦{™C1/ Ùu›ï#ðõ?7-û@ž5©r[ø„õ…8ÆyVÆ´Ûì1ó4\ˆ.W«¾˜T¿œeš4”ÖÆÎËÆo·Õ¿u4-¹§1ÜuüÙóüv®óŒM»#ýÐê,Ì÷oÔó`¡ ¹,Ï[;Žª°å¬¥œèؼæÀ•$¿5`mçHÂú«!¸Ö s8XDدPáxÓ®¦-¨~æ,¸&øNoœ?úv2=J|ÛNuþ·¿;œ­ÙJá½!‚<8tÄ$­Høó—÷B¸Ï¯øŽ¦{u-ÏA¡%Có¤€õ2Ÿ—òùWNÿÙŠ•VÖÞ]%pþ­°n®cÇÃ4à8ŽV€ó9Xáó³ÄŠ\)§bÕ^ùׅæ³uvjÖ¾îm7L†ßSØÇ ´íÃñxãë4ÎGâ縜GhkzßÆÒ•æ9ˆüüAÃïÉ`Ê·m¦Ãøp˜Õ5Òc{áp$©AîQ #ˆ%eÕú`Y íyÂó[|Þêœ!Mûðcü¾ÖçA5ÖÉÅü.Zf¦µî7/ìyhVÈ¿¼ýÖ.x=?x‡”÷Ìîßy<û@ïmõ?ÄO Îs¶Ó§Dƒ×yø’K6ù>__v–¹«‡Ã`ëÁe3øõšüFÂí‹uhë_ÅûbR§ûö#óGÏ8iÅŠöMJ†q6]~R%>ô(ö"W»†m¼¶ÍÇAŽŒ¬ûsYD á|N^ÄÏAŽ—1e×Ëàíç>ü•d¨úäÜïµ¥Â!|þ–/÷ÊWƒµç5ùAÆ&o0<«D÷í;°ýOgŠï߉ãJ…ã–[ròÂ’+ç¡ù‚Âqg“á饽fC‘pÛyîÑö!©çÓ"HÈý_Cš"–zg>m«Ò ø{S|ŸFSsÑüÕ¥>yðý/!.XóX· ]€é_‹¿8uû–Üë^Zò…Csï‹—xŸgVž|÷¯D÷ §î>8ȶÊëç•ñýþ^ŽÝ¹<ÎÓðê¤àn].@™N3uGð¹>"øHÚ·“¶çR1ë EäxÃCycjÙê?Þçq— Çwž“~zùèDËÁ¾dpx˜~«zÆI¸¨7d}hŸ>–ÀJAœ¤JÕþh«£< Ý)ÐòÂö¶ý~aýTÍ®ÏsX—!MúVmOƒç û­%GmM†åSNØpç$Ì}›'cCË6ð%wD‡uo"Èà›ôG^ñø¹l[—q^Ÿ˜ (Áq;7¸êäEàYî-—·áúìJ-Ïku'aç¡L¯žƒ;Àþ#÷¾É£#Ÿ¿V/){Ÿ™×;þ¹…uHM»s)Ž?gL¹"ÇOèHÛá•ÉPÖ¿~ؤÀ“pßùP\æÂ.ðTß¿µÏo\'x‡­V†ÞŸl̬s HtöûmBœÚ×/ŽßÇz¬i€6kºZ‚ëá¶¾³ï > 5ŸýZ«ŸÔ ªÞ|âZfé3ôký[þÙãóuçhŠûUŽ»%׸:{ ×ä£Ç-H†b+Ú¬¯u—¾X|Æ›àÖuíƒÔdW¡M¿Ž ´qïy¿ÉãRÓ²Ìa·(7¶¯*<48þ€¯»p…j€È•çå†ÙÉÀ÷›:m’‡E†ö„ãÖ¯©#[õœò ´Å?ÿü¼âûÙ}?ÕÛYüV;Ωç9Rœ’( Þè£úöŒdPôY¾J¿WÀÜb{ÀfÓÐ’ã:ëˆpžh{?ZàŽ~µí÷ û ï¿Zãǯý¥Mé|ß Ð»ÞriÃiÉÐÿ¨ç§-Kàý¶“¾­Ûtƒ{ ׬-;NGèkG»l׉ç/Îñõ¥xÈa=>7(Þ¼^4D?ÌWâðødøõëþ©‰ýÃ`Ý¡íó6lê …;Ìué:CGôÏ¿&t™`ãÞñým¾ïÃϬqãúnlø³G¿hXvª@UÉðd8›´¿Åõzaàµâzæ·ÚÁYù¦Ýé:’áÒ©ÉsöùÍ}·ŠÇ]ŽeÚ® _÷‰Ïý¤8þ689~R4$îÙáë ;<;†AþÀ¹E:ûu€•—â k'èˆð¾IvüðþНWÅçÌ W½ûw•s³£ÁÉ Mç/›‰9-ü7¹·#ÔÌ}Ô£udââqó?®È~þósTa?¥ªÝ>¿ Ç=]~ŠáùÊh8~ꆧºk2ì¹iÒ è¯ÕŸu.ÝÎn?ÔúÉX¹¤¯Øibçì~‡¯³y)ޯѬçÏ©h8àd¬¹¦m2´Ïßv^ÑÂ' S¯ u³]‡Ù1U+ÊÊeç§Pg2mõ—÷vï‹ãø“×\ÓކmWÌ(Ú4J™^TY´ï8èvÏ-˜»;t î~rß4qÈÓ^UûýœûÖü¹!~¿Å‚ãNTÍx)è©^©ɰQºqî§6Ç¡¤ËšÑ/Éà°9$©ÄJá}ß×çûlÂýdüè Ò÷Z·]ö.¬Ë²ɰúëÔÒÅï‡Â§¯þiš¾aûµ¢+ZӑнÐìÓ[=­83È}Ðo[=¡O!'÷jv÷O‚ãç›BW¦aëÝ6Øb$ÃÒ¹Õ{/] ½ÃçÕ¯ÖH ãj9ßl~]Grí»¶ýRdáïp¾»øý )ŽWaß,Ïæí/Bþ.Ãcr'CÁ—Bk…õŸ†®ëÝf,¹6ê†QGJTˆŽûô6À¶Éù¾?ï¯ø}ãÿk<ãWúy§2X_>§#GoÝõzÕ(Ø¿—k;gþ«slŽßvBOõéýáx‚¬ÇÓ×fx¸ÇÕ¶Ž¡ì¤i÷aºyáe©8¹ßmŸìõßçáÏOßâ} Ž_zv߆Ë¡ÖTÕÖ·3DL¥/І²sÌF$ÔØT癎´˜ò)ätðÞ:›£{Lnݲ×îv}‘Ç;M‡Ï/BÙ-b”7ÌP×ieTèœPÖ4%B~E’¶¥¶Õؼ1ðúÏÿòu¥]ÿM¯ó¾Š#ú–¸Õ”>e†K; W?¯ …Ñ >Î9P±%1¾ü-Â%’ìÍLnínæûÆOXý|ä1sÎ}THS»ç»ÃÆ iÑ‹iožz\ß9²ÆÒ½fÐÙ}áDj(LŒ}½¢ú¯–¤©u£%’{ ïããS'Õo ëŠîìU/’X—‹ ²¯Ç›qãVÿöžïd¯7¬ñã¾­LÀÅ><Ígîj†bõ»¥»B¡P«Ó¯Ôn _Ï÷Zü¹z$ ¤¸ßÁü:<´*7Kªk.6¶{ÀaS†ÔúZ¤2B—ô}Þ ¯¯õ÷;¡Ðî§×ž–×ÚýÕÉè*‘dàÞˆëÕJgï7ð~ƒ¿ŸÂ¯³P·ëÛ­ç$8O‹êþiCb òëâ»ëª›¡wlRú³ú¡ð0`Þœ .mÁg–WáW# ô?18o\öóLJ}¬e—?R뎔ò£b ѳìò¡¥Íœÿtçûc0s´´o³ ­ ÿbÒ``$9ß?×—¶‹lï'Ú¿O}ÇCxõ±¿NâsýÇ'»_Ïùý"¿îzž½öòâ1(t³îÂ&NÍAÛ/¢À %‘¤Û˜€VŽ­lõ¯OÄç]*ï³ÉÃ+35~v£¿K‚ž~^²í´ÞY¡ÊÉëa}õKÖm‰$Có¬ë@ì¹ÊF[ýÞ ¨k÷» ŽßƘ½?+„s»$ؑܿܦ¹Ç AãåçacCxv¼æÿ]‘d[Í\&$g÷¯Bܼ÷àëHñù‚ÇÝXæÒÝ\N±pø\å‡:K<¸’{IaÇ øjž®úÅõ åaßc?wF?]¯ø{ñÂïAjÙ=,8®õ˜«\,Ì»\§õúkIкáÖò¯Ú¯‚go&7;ú–dÄÞHÛz‡ß?~?ùþ)ç¹óü´[onÎò>7Ã5Î&쬽+" Æl¼XnI‘c¶x|Þ|öª—¡‘D73_ªºvöº¯;yÜ û©öïKpüc^µ~ôƒXHš,ÿº3‰/ÿ…ËËVÒ†Ç;‡î;I&¬ðì{îjö}úØw¶¼åçFv¿gÃñgͲúU÷X8Ö|cJ`ìÍ=Í7uÞQøÜ~s¾R‹;ÁënŠÅð¾Zº6õ%&Àv„çÏ3ÛûG<.Åï *p|ºêt ’­Åvîš“w†½¨Jª…}ï¾-½¾¬3tÌ[©Ã¦m‘¤ÆhÉ­†l¿'â×ýeûöu®ÍÞÇeëNwàÊ}GgΉ…gJúdJ‚:ÊB§ûÝ:›Ç‡69Þ: i5œ.’ûH$|h¯êŠüýY^_„uœPÇ48î´JÊÈ´­±`Ê[ô¾w =³ak%õ¸ÿQ9ñˆ;@Ĉ^cúš#Ijjƒ+¹Îf×aöÌÃ¥ñ¼){ê5¶;4à¸CªLì’ÿt,ìˆ~Ðhl$˜ý­ýÐýò#à³d¢ûìm- Í´VÖ¤G’Cyö ½@ìÏl×[èËŸzôJ%áñû›ÿ]Ž5îqž”<¡†à»±0±ë´5­['Aæõ3Íw×9K×xEîßW>T¬Ua¿û¢^cÏ…~ °Õa7Âs>ÕV'…ß)µdÿy=!î·dH…óŸXPŸ{úòr­$ÈÿÃtv\©#gÓ»·MV‚תŒ€ò#mï%ñzÀ¿o•?_„øtGÖøÇy­CS5Ÿca`¯¤RCË%ABó7®u Ƴ~œë½67T=y¯ôK$~?H„z–nûü|=d·ÏˆãÎpÛîpé},xoN¯á˜ÄöW€p®ûýBpj£ÝÇœ£HG—ú£:¶ $ü<™¿ȯ—5Îq<§Í'÷lÿ #'ô¨üäS"4=?~wÓbGÀ=ø‰vý©Ü¤Õ{éèuµ¢ˆ›5ÁqýÆÖÇü÷!üüÆß8^íÞo渊ƒa æŒ]ú0Ưn_{Ž'äÛ‡ Û „O¸D‘ò.c¥öξ¾<ùó‡¿Ç,î÷58~Aº_"kÓ:5¸‘N{¤yñûß|]zÄÜ.% ÉïôíÍŠFÙÞ?äß÷b̼_Å gzÜýÑ!ù€÷Öÿ´{ŸÔ€ã纷¥ij™8hÖsòaÿs‰pÚ¡Y÷-¿ÃñÓ¶®2ׂþƒ÷pøIb}¯v?{ß@èo“9»1;Ÿøþ6ïã…yàÿϪ¿=\ÿõüSþ;}«Ø529dó )÷I††rÌ#ð¤Å*¾Ì;ЉùV…‰˜#&Æœóe®‘‡«Läáª`^÷Ü»Ê̼«ÔŒýäÆ˜ÒÌ»ÊÈ9uÌ»Êù©80ž´žñ }Ò“R%âAjX‚Rï@ê]í*ò¤¼?æ¥"a^*fæ̘O Æ|â÷&Æ|òeÌ' ó¸71–´’±¤eŒéÄXÒbdN{ó¸wfÌ'#ó¸§,HcAª˜Ç½ŒyÜ‹YÒNÌãž²¤]±p¨QfÆ’æ¾*œ%íÈXÒæ™­bÞRê›Ê¢\HæH}U”Œ%Í}U¸w ÷Ò¦Þ×à¿k°Êáß»;²k@™¼œ½GƒYŽÒ£œ“WÌÞK±÷²ò LÞ0”ó²21/+5ó²’1/+'L_”žyYù1&¯TÄÞ“£´,AäÌËÊ ÅyY93ù·ú1/+Wæ£mb>ÚÜ?P.òôe>Ú®"î“;ó¤¼wÆå¥¼?æ(æ>QO+ Ê‘1y Œ½çÇØ{LJµˆ½G=´)ó‰úYQÿV7‘Ÿ÷o53ž@0c P?«–ľŒ3Âý³ÍŒ3âÇ8#®Ì?ÛÌx¼*Æã•3îž3ãñй{9ý³}™¶„qFLÌ?[Íx¼îŒÇ›É8#a9x¼ÎÌ?›òxݰp£,ŒÇŒÊj‘Íãub<^£{¶'õ¶’aqѲãü­œ±Ð¨WŠ'XämÅý[©¿ÍúO\ƒsòxi¥õ•ÖVZSy=ÍYGi å¬ZÝþdƒÐúÇk­{UóþQ­£uî?«q¼®q^ˆ¸žñZFë­]¼^q¦/­S¼FÑú¤rj­I´Ñ:Dk¯;¼æˆÙ¾[ÿAÙóµ…{âÑë«qX¾”¢Ê-°|ŒåK¹p–ü‚¿>åq*™§~:óÒ×0æå¾Q¾†r6®¦oœæ±åL9še²yÚԋΗ±ÚÜñ¦jØud~÷&Æ÷å|6Êf“‹¸lÔsÎì"°1©Ç=eqþã`ú1þšó–“3Ö‡Õ7™ñ=(Ûƒs=ôÌ™zÆ…0_dÆN£ÈÔÎÂ<9ØI䱩fþšæ¯)gþšJÌ5ó–§žšam/y·¿û¿û‡ÿ>Ä™}G‹C6óŒ²<|P”3c‹™g™"æ™cëQΔÎ<5Ý™|óÔ C9cøQ†8Ê“AÉÀ2ó̆r,&xjPΘ(J”‰ùÈû1þ¤+ó‘·0õ163ã`2 åTF`“’óˆ(2„ñ<¤ŒLyJÆŸtg^ò”O$ÃÔ¢œØÈ˜gJÆôáVUàÜP e*¸3ŸÙ,æ3K9 VoYÆMP1ne%D²”“`` „tæK©ÆÕ‡±d¨g¶¼­À;uúΊùû9ÿÿÞsÞ•}Ê6åÌ.Ê)P Œ( c›Š™]”mÊ™]ŽŒmj@I0àU( Jʼ³iðû ô( &eDI0TŒm*1»(=Ê D2¢$˜(*”™yg+gÐyg§3^ŒŠq¦Ýgš3ÅœiSf åj¯@Æø¦ÎŒ3Í™1!Œ#Ç C93¶©‰1»TŒÙå†I"bvQÆ4õÏV¢Ì()&j*å† «F¥3ÿl óÏ–ck§@É8n"¾´;ãu¥3^—†ñº(×4˜qMŒ×åʸ¦b^WåÆ`0 $Œ×ef¼.5ãuI¯+ñºBX œC®)å3®© ‡•ɸ¦”£ˆk*a\S3Ê ‹J*‹y`ëQNX`|Q”+š`Æ5•cÁÑ 2Qr,(-kZ (WLÊ‚’b…°$òAéQÎtoe1^)¯@†É¥E9b‚)PzÆ‘¦\ JZI`¿f1þkK>…ˆÍ¥BY\†4gÂ(r°‘ò`œ†r ¤˜¬–° ”嘃Uà‹20&—ãȺ2&W&ãÈr&e¸b‚«QéŒS q (ËÑíO—‘q ÔŒÇå†Å@Í8>ŒÇE¹³¾(ãqù¢L(·–ƒ6%â¡e…ƒ²` Œ£B™QnXH‚Q™to—qiÿÌß}îß}®Êá߻溳ÏHÙ´œÇåŒÁì‹2¡\›VÌãrñ¸œ›ÖˆrÅ€W£ÒQ2 | ʃ_2 \1 T(Ê“AÍØ´>"—/Ê€rÆñE™P®˜(j”åÆ1”‹èމ£Fe2FŒšqµ¥¥¶6ç"ŠÙÚæ¬ÊFÔ²D“3>­¤¼ÀÙæ¬ cÅø`êQƦ53&—š1¹Ü1)5"&å"J$øßEYP2LT *åŽ ŒÊDI1qµ¨,”&pÊ“X…2£Ü‘ò»¥ŒÇ•Éx\ZÆã¢\Ú–辌Ç寏´bM~_”åÊx\Æã f<.ãqe2åÒ:bðEspiݰ`„0.­ ‡•Õ\àÒjQN".­+ãÒZPîXT4¬°(P”3?”内&„”•Eÿo,êß§ú»‡Êî¡dì3P¦©ƒØ€’` û¡Ì(7Æ4ÍDÉ1°õŒiê‡2 œÓÔ„rÃ`Fe¢äôZ”¾/ʈrÃP£Ì(7L„`Æ4U`BèQL ?”%ÁäðC™Qn˜$Áô}”;&‹šqö¤˜4Á¨,”ì/ØÒœ³'fK[P2L, K.ÊÛ C9b’ù0¶)çKg¢ä˜tZT]ËbòP®ŒkjAI1ƒQ™()&¤åˆIéë"°ö\%øßE¥£ä˜¤ZTJŠÉ‚ÊBÉ0iÃXâ*Pz”X² ¤ô „q¥ƒ1¡³P2W)Åx¦Æ3õC™PîŒgš‰’cÂkQŽ˜ô~(Ê “_JGI±„ 2éz‹–ñL}ÏÔ ƒÊ”ƒgꎅBÃx¦r,a¬hPžiÊYÄ3uc<Ót”‹‰åˆÅeDI°°(Q&”; ã™*Pa¬Ø(Pýú¿xŸðríý_©»ÿlÍýÖ­ÿ“ë-½Þ”+=‹E¥£Ü1x5¨¬ÜÓTrÆ@V1¦©;´eaLÓTʇ¾ë…rÆ ÷C™Qnì!¨¬‚ÓTƒÊ¢|SÆ4u¥ï¡ÒQî˜Á¨t”;}' •EßÅÀÄcÉ!§ïˆ¡1I|P”3&‹¥G9aÒø¡L(wÊ3¥ï¡ä˜DZ”&’/ʈ’`B)Q&”„ñM3iÝÅÓ£œ1ÉüPÆ‘V£2ÛT‹r¤çÅ(GL>?” å†IŒÊtøÑa(gLH¿ª?š²£ ('LN%ÊX]`G«Pé(9&«–¾ k@9Ñ÷ÎP”&¯’ñL}ÏÔ9„%3å™êQΘÔ~(ʾ“†ÊBÉ1ÉÃPŽ˜è ”åŒ ïÇx¦Æ35£Ü±„äà™ÒbàËx¦ÎX”ô,›1M•(‹ˆišÕR`š†¡±`ø¡L(7,ÁôÌ%ÅŠˆ/cšºb1Q¢Œ(W,*jT:JŽÅEû÷>á¿eû÷>¡}ÍõasÐ öEQ®ÌJ”åŽA­Ae¡|0¸ (' p%ʈ’` «Pf”;|* 僆rÆà÷C™Pî˜Á( Ê“!„%„/Ê€rÅÄP¢L(WL%Ê‚rÇD ¡ïР¤˜0Á( J†‰Â’Gþ,i'L&%cI»•xÒé(9&—åˆ ¦@éQN˜h ”QÄ”ÎBù`â…±äóEQn˜„Á¨t” “1•…’aR†¡œ01ýP&”›ÿ»¨L”}—‡%« ¥aI+GéQŽ˜¼¾(Ê“8•Ž’a2‡±„–¡BXbËSš&¸¥E9a¢+Qf”>•Eë1&~Ê “_‰2£Ü±£2Q2,TÊ‹B+ TÊ „eF¹b¡P¡Ì() -* 僅CrÄâ¡@éQ,"*T:Ê‹I*%â†rÂÂâ‡2¡\±À¨Pf” –¾ƒ„ÅÆ¥G9bÑñEQ”ìüߨçÒø¿Q{ÿ®¹ÿój.½~F”m0*%ÅàÕ²öEP d5ÊŒ’b@Ó÷Q2ú[Ü ”%¡¿k@YPî,ص,à(#Êî…¢2QRº'ŠÊDI1´,(=Ê“‡¾³ˆr¢ï-¢Œ´ÇÅ$qÆ$Q¢Ì()&K* åCr¦{¥(ʕÌ(WL¤`T­µX[ ( &–eD¹am Fe¡|0ÙÂPN˜p ”儉§D™Q!¨,”?3J‚ɨDQN˜”¾(#Ê“SEߟD¹a’ªQ™(LÖ0”&¬eD9Ó÷)QF”3&° eD9c"û¡Ì()ý½Ê“Z2 $˜ÜJ”%Å$×°D÷AéQN˜ð¾(J‚‰¯DQ®X[•( JеUƒÊDɰ0hPŽt_e@I°H¨P&”+ *%âÆ ‡¥G9Ñßl Ì(w,$!¨L” ŠåH¿2£Ü°¸¨P&”™`T&ʇö¸ üF*çaìwª~¬Wæ¿Så¿‘rd¿2Aöo­2Ù»Oüß1ÁÀA’!=v”‚.âl>©^uµÚo›m~ ñG6ÏÜè¸ÅüS®(æ·…õoC÷å»ôÄc}»7‚ëF‘@ËâzÉ'«BÖÏíåj_ bÿS)ΓZþ`Ôöqð.iôêyêDÈ÷"åF‡Ãà\výÒ¡yÚÃ…/-}OxD‘‘¥ó®ú–óôžzôyä=w"uØd¹tÜ•~Ñ£V¹CÊ‹ëÜYQD¸_Ù~KÜ)—¦ÑÈN]Ÿ3Ÿûë#ÅñKçS¡WÃ8h^ÀñYú»»p¾äë[/W‚ågÛOk?Ž^^xÇßãßð¡ip¶ß;÷›á~_â¸Tà¸~ƒ+‡ÕÅxO®îBøzMÁa‡à[p®—åZt‚£'—E6žE¾Sûâ~A¶¸äãsÿPžWœ×#øÐâç©úlý‘×µâàÂX Pº Ïm8"³„^W\—7ÊÝ ü-ï^õ"« lìuúb¶o çqß/ÁHðÍÔÐë~ø}»ê%â@_eyF_ý]¸­¬¦þàwqÃz@7Ù7]@¡”Û›åþô“çœeŸœ›Ä}t­y€óT³‚&cAÀÜß…Y÷T‰øyžx}Y40¸'îDÄ¢ˆëN™ÛÝëÙyÀ}o¸/ð}jØÕ ŽŸ´Ï¯]¡¤XØ[”æÞ…†½O.t|¦Þ)”ð¥;ì¿hîŒ×gaž ;'òzsÛæ«,ösØ–!ýAíùÏÆÂ¢‹?é§Þ… Û·ÌQõTéoIëμ÷lõ†(Œû¬ñzÀù‚_&ó=Âq·<ÍhòãP,h´“|ú¼ UÅ•LŒÛÃß§ ?Ö †fE½s2Êæ»Ëã„ûˆö:?¥„sFà>WÖøÆqÇÿ¸X¯Û®XØøiûªî‘—jÏß»ºŸ{_¼=<4uþÒ˜(²wKßú§tÙ~6•Š˜Jx¦?g×õ‰ÇÂõm&UkÚ.ìo<¨ë€¦BœãøË·XîX mÍ#Oݪ{\ò6L¾Øn?ŒÜy«f´ÁÒÏ7Îëp9Š\zO„Ù÷+øTõ„ñm¾¹ÚÐ]ˆkwÝû v¯‰…ÜE[̪Ræ.l_’ZC^l?lÌbTÿ;ϱg£ˆwÁ²+›’lÿ9^yüq¿5ásw„Çî™N7>¿çÉŸ¾`èŠXÕE9ß)÷]¸ÑðæMŸ—û`c`Ðø\ÍÝ`ŬÇ÷Öáu?1;uù³ìç‡àÿÚãWHâÁ“ç[÷´Æ5Ž{°Kú¸—³b!¼XWïoîÀØ­‹M¿±Ê&l^6 #8ÖvIXÿcQdšO…¬¾E¹•ÙæƒÆ¹Íb.±Çu-<|xF¿X¨{¸”©“ùôØÚ(`îÉ}ШSÈ«ÍêCµ_#z¬=Eä?ýe+*2®Í /Uì«ä°=Cª£_«u,/ÔýöÕów`d«,eÒÚ}°ÕiF·»³›BÁè#ó¾îŒ"­ Wî‹õÐv]ùóm¨ó‹Î¯åâÇ=m)òkQãXÐ÷ÜÕ”ƒw ìl@âö‘û V5ïÎ¥üÛÀ­=E4ÞEºÛ9Jú9€p_HîÇÇ}ϸ#÷U?—¥8ÏÉÒW¼s׉…ÍŽž¿%ëï€óÖ·ªÕÜ‹O=5N-…eã:lˆYeó…ãüÝ—ÍWFvØûÒçüù)öÙVàø©}jùXX|àÃÃKïÀ£Œ¸²÷îî…Ê÷Bo—{Þ ºzœP¾\ExÅó’ÿúŠ;ì™Çðš.g o´ç‘«pžÕ5(É(ŠN/7"`úhlî…O£Ýn9¾;4Í]=tov⟛?Lo‚Gª:Ùsì58î{êÎæ ®§^Ï¿3ü|Õmê”Üøù?®[óL/8{‡~ñ(r5†Ëq®LV_lþþvþÒ8®iÄ…sßc yÓæuóúÜï¹ç=¼¹|ì¨ ó%|þ¤8nó¹W'œÙK(F6ïh“Q0׉廡®lá¾q1]àçùÒUša=<ÞíTÍéÑ«l׃×EÞoñº+ôÅöŸ_óP7l“2fF–ù~íMXqc‰»X>vëâvFE‘üþߤ/^eó-´çß³ùòŠý\U8~Dõ»sõqµý÷=¹›k¶ê™»Ñ.x•Ô¡ùÕÝ ½Ä¾çeÏD‘å/æž¶ÊæÓÎ}‘ÅþÞz]FÑN9ÊX¦p>Ž—¬_lÜ¢0¶ýæÓºB¬ÃªZr|¾uP<5UKýóºÏÏ7¶>…óÄ}–çIë’GY¸F <Ë;yzÖþpšÿ£•"| jXqç‡þ=™ev¿Îy"¼oæ\qÞ[p\¡¬ÅÀ+gï!•6%0NÙvȈ•÷Ö•CRïË:aÞ?¾å £_¶¯ÿËýŠyÁ93b„ÃÎ iû3f]?ÇH*»kKÀê(ºJ2£Ð–mÐR¿XÐFúΚ¾âQy»Þ½ÞÞZÙùÊûP¡¿¨Ç8X¯A‚ã^ŸwaôãK°bZKub@LõO™úºÓV¨ÕúZý'@ôŒ·×?Ž"ó¦=:Z9vÕ¾‚/j’Íç’û-sTküã<Çwº\¥»ïOž•v[’Gjx{AêÔeí²jK½ nÿëíM·£Èõ›I¾«÷›æœ€C®ïï-šááõ®Ë³uàjÇkPàøvåÛsní%M¤„´Ù5 ð:åfPuyOj €‡IÒeú(RìÓÙ°æƒWÙüœ¹Ï¤¸Váx×V4ßÔyÄ%È{­D“àq pclÌÙ’-7AÙëÔ î>îÝ>\ÖE‘–¾Ÿáq^øz—¯‹øzH쟭Áñõyf]ÝWñLª9sKÌÀ¨žÐ¥Â’ÊáUÀÌmŠ røjªÙoˆ"îUß~ôÏÓ"6n/çðõK1WÁ€óK-;¦…ñ"ŒÎóbdB÷ز±ìªÑÕ7€¤ãåbu§õ²ÅýüýÑ“'?\e«“üûðºÃûu¡O(Ãxܧ؂óœŽYW ZàEèë6ªP¢{Œ¹2gYöëag›°ø é=(Ь«Ý-©ç…U„ÿïy=ãœ>>¯5îweHóð}:Lq^,|<ñVýø¬º³¤ÀˆuPDú«Á“Ý!á|ï—×â£Èá{'r/8µŠðïÏýzÅþïàèV4ºç^l®üÈ%¼å#«.[ ÍF? )ƒÆ›_lQŸŠ"’ø©Ã Ü\eëÿùz”¯O¸¨5¾qÜ›÷h‚_Á§3%u.عzÝÊgWwµ­+úR,ôWÎ}w´ñøõå}¯ËÖøÆñ…ú ÷g(ZuÈŸV»û:kàèµ:¹ì ë%Cr-ŸEŠ¿¹™2¹÷ç´ØúQ‡¼?³Æ9Ž;j„zÇ…]ÑppØ)Ç OñPgâÓ©Aǃ@Uö`÷·³e°ûH‡<''áúâv嘛¥ïƒ8gPÜßjp¼Ñ'æ,>ç sJ?×î{Sž”yì] λ¼ìCÞ^Püvz±‘ãp½ßdÚQIþl?n~ßy?ÄëùÃïâ{”Øù~pž¡«Ÿkç k®<úU=!zy¬ì¨ª¼ŠùvÊ¡äÊI’¡ø¹o·Ë¨Ð @øÂñ¶ç'÷‡æÏ%kã¸Kù÷¸l€»e—œ¼Æg6æ}¿ÂÆ/­h®ýÈ/Šô+tæüþÆÙÏÞ'ò:Îׇv<ÆÝÒ~oïø¼Ñk¤çŽ‹‡ÄýªXAq}ó±¿þ þ˜š´dF»2keƒv¶:ÈŸËœsmÇÀq×_-=Ãi¦^œ^~¼“&¾•¿¡_`)tþ1ïÑFOÀg‰©ðœ(²auÁ¯>¯²qïy~»|JªW_ïb¿¿‚ã ¼šÜ´Ì£ux?‹ôèu=fœˆZ¨Þ¨³)sÙ°ÅQ¤Ù¹þ_d×AÑb»|¿RÜß*püž±qçÖ¸ ôËÕ3WÆÃ—OM¤> æB^åØ{Ûny‚üñý+VEº 4¨m¾³üºpÿYÞ×ñ}q?ªÂy誫úG[Kܯ²L…ú–”5`&ä2MYúSØ·á@â÷ÕQDýùäÒ+Ô6^_oðñy\ ùëÊê p58OÔéÆíF`ÙXïò®^¢_<œ+X »a <ùYêÔ˜Šýáòâ%éŠ(’\ýN‘Ã{Õ„s†Wœxc™ß3›S8?Úï8îþfÛ/p#0B~ðö“‘7Çš^hÔp À­‡×ÎîŒÆpEé£,Ø{¡d•g'\¼pí¾cûŸ¯mëá>··ãLZpž q)’/@µ¸‰Ã‡íŽ¥…•ô'Ï&¶ýêË|°£Hž—7´_ë®"üzð¿*úõÖ´εƽǵ~¾ Û Kv<´ ¶ÐgÜapåéo§,˜Ú'Šì‚Éž‡òd?Ÿ…úR×î~Jp<]úÎBúçç¡ËàM>·‡_Å ;)^ÉÈj¥·Oèô©¶?gé›Slx¨¶ùñóëÌû[»xÇq_wÖUî?çX+°ù/—ÿöbò~×°eËü} ŠõÁEFœ8êÕ²•„ï›q¾ ß×ÎAwǵâdΞ¥éÕ™SÕâGw)tIshH[o ”‡8îÅÒOã³–¯´Åïß8?‘ï“XãyO†T=v×þ’³pLݬˆK<< h_Ö'j9iu½í°×7¾¿">gRàøoûð•l>Íúžx8¾h–¾@r¢Xê´*‡»Á¢§íçTŠ"/¿ZÝ|%{ÞÇÛøÒbþ€ Ç‹›tåˆcËÓïbâA‘š6vùj’4óCÇÁËd ÿá<#©$æ5mª¬´íñ}-Þ§qN]‚ã7±žôPúxׇóðz”H{íVn- {åÞ¬bwèR£PƒŒ_‘äãŠïÆÐn+ ?'àÏ-q?iÀñzeXtßSñ1m ÊÆÃðQïGôLÆk:ÖM<ÓÊ%ßüÒÿq$±Œoòxò•„óùø¹çi‰ûI ½®AQuW½:¯Üjý<Ÿóû«Ôý?'¬#}>žØîx[Vš1’ëÄ•¶zÁŸ‹¼^ðþ•~ÞwZãx/~þ¿Ñž‚Ö’‚ßg—Œ‡çÆš/>-]OÞp_è\ò|ÐÈÓ‘$c¦$ã0æ7¾s¾?_x!åíâO‚ã~uºg¯¥§€Ò·WŒ‡¹»·–=znqP+¢÷^ö„ Ƀ†×ÐE±’ðs6~9ÂvNfåm¸Øñª¤8ÏÛé%¬x šxSrm<¤tª´g@¥MäIñü»æ÷†=ã"^ΈŒ$² i‰ew­´õU|¿L¸^ ¶}817\㻤6±Ô¯‰÷CݱEpÃxsÊâ¸u3Ù3øëì!»|`jç‘K+\ˆ$.½èŽßJÂï'÷•ç}?Wóx>I^EA~Mß3ÅC™)»Ö&¹†É€amo”» ö*Ùãj$éã²~üXiÛçãç<_…uC%öüeûˆ8þ‰7>“6DÁû›tã6ªº'Ôi¹•xGMhâQÛTú£ÞënG’Ò.»ƒw®$üúóëÂ??ï—íöqümÃoäZÚ* „ýœxXóöY^c½íÄŒBsР䱫_¿IÊ-­´Ñ-a…-Žø_ÎEæ}ç·ŠãÉ‚óŽ}5}žDœ^t¥ÏÎt¾nÉXëVÄgúP©ÜFI|¦— |Pzáù%äGQÛy(_÷óÿÜšû2¤Ç;6Em‹„S}ÛÄÃÄjæ¡3v’¯/Ve… sŠ›¶Šˆ$µ5«Â¾XN8ÿ…_¾¯Ëû,¾î²Û_ÇyÚ®¸¼¨lH(éo¸º§s< ª6¢NZÛÝìßýšŸ™r ’l®öµ»¤æ ¯ÿ>ÄÌȾ^|ÿLày™lϾ>µãià<.Ÿ;äJÖAŸÐ gêÊãáÈܦÅó™5ÄÕtÝ=|K?“Üði†ó¼©úpþbÿ„Ã?·óÆòœïïˆù:*œÇ3ŽÜÒûé`Êò° =|â! (bMŸÉ{Èþ]}&½êãìíå×[ú¬ üúó}6žß¼^YóÇíf>µAý¬@çxÐæîÖtZþ½äµ MÀþ@ŠÄ©;í‰$EN~«^$Ï ÂûFþÜáë=ñ¾”Çú•X[¡áô>ñà!1mÕÞKJ—þä~Ñgäú|ûp=ü¼µ&y³îÆrÂÏG8‚sby^pþ ߇±æÎÓ|äØÝ—]#`â®êï‡â}vîó¡SÜÀ}ä½1¦ß•^PaúÒo›#I¥MÛµN/'ºàŽßWßO‡ýÒ°cÍêä»eÜ)Q+\:äR9–ÝOVyÄí«ó†Ö/I$ÛŸtý°œðxá÷‹÷;ü?sD%8þÇI^ G…Ú¼&½l0`yþ‘Éû .þ¾ößêø%†6YI\‹6/ÔmìrÛþÏÎYåuÇ‘˜‡*ÅyÔ=¿:y®Z]wá<—šþÈ}wÛÒb“¦BÀfo˜»~æ'Û#Éõy[oh±œqQÐÆÿÊY¿ù÷³ã=â<—Ή™32¢êßòâáÇ¡Š®™½EÜé÷¿žx–6M£ýŽG’Ï®Nþ^åÏïÃùööûØEì8-*œÇmgâdù÷“p*¼Ó¸­X7ª¼›5*ðÍAòkŲýJ?ðZ2¨H•ƒ‘Äwè¾§ÒâËmyÆã‰×Ù¿:OÔàøýr¼¤: õ3ËÜ›×+öÝkòÎgá!òhg“×}§ô…Z-\’VD’I›¯uyþc™í{ðñx¼òs8¾^·æŽÙ—~á“`ÚNÁ±ñàZeÄÈv¿…²æ• û@±=ÝoTñźG¥ž.#œ'Ãëßoæû8œïÅŸ‡Ö|Àyúu½|×x<Œí߯ÃíŒ#ÛŸ)µDç”0³þ^°hjÂHBOOWë–ý¿œÃÉ÷µy¾‰ßr8!}º}ò”ž} hYÔŽÓâA¾¢Ïƒ¤×Z2ý¦GçÿžPð^Íi^"É{Ç)û'­[Føó†ßwá9—l«¯|¿ˆ÷µÖ|ÁyüæÑŠv ùV›¤œËö=xÄaB.ºOy~²'³ÿ~ÿV‘DYlŒ6àð2[ç߇óÑøú—¿Ï!æ£IqžÖBu.4¼´øÌ’x(ëô{À«;‡‰bQnÿߎ½ahó[%¾6Œ$¿º´™’uoáû¡|?J¸nwm|wñþˆÇáw£òÏF'ÀsÆéhùÊx8øzøÀñýŽŸµ:4ËÛ‰Žq‰$÷M/µœpοþBž¤°swàëmk~àø?ohìqý8lŸ‘ÙÓw}<[+ýé2{̈­Æ²ýáUj·NYå"I«ww×<ª”‡¼~ð<ç÷Càu†ÉJ&ØBx^à<Â{KÇaÅÚnÃuUíÒ ·ú%³.¼8Ô­µ'L_U#*’Œ«SýÝûe„ç3ï›y=_4óåÕqs{CìÚÆsT!OpüJX=<Ú‡;Å÷JÔÆÃÀuõÏž­sŒÔí=hè‡> `¿:³›‹$Oë–%uŽeç!ï ø÷˜²/×¹|%±þäiw'jD'!?pühì:¢>„¹>æ:Þ¯2 «›Zr·+ö·ý¯%~«I4eœÃÏÜõ·å¯ƒ¼.òú~vË÷'®ÒNàÚ5×^E\3!?pžç«žOʵ'âV&_‹Jˆ‡+[Óî×8w‚è;Dt ù9tÓ¹Õ+I 6üûjw[}ç÷‡ŸC Î7·æŽÿ$kO¼zk(x}÷rÇñPÐø¾`³¦ad䉱ýGò„¶ûB»Wû®#í;MH‰m–ý=x¿#¼·—îqñÂÎNË7†Ñx@vA؇ÒàøÃ|)‘8†5|Ô¦qF<Ô ÇÅKÃH¹WõtÎûÁ¸EôM=ñë”tîˆ?á}3çÍòûÍ놸?7àø»:–?2²n(”žïëR/+Þ¬›¾Cu-Œ¼Êu8Oñ‹Øgæ^pjîc‘¥zѱÜ2ÂóÚþ>Üòàïˆûr Žß.òÛË#ÒP ÿ#?ÇèÚþp²'‰¹AîQ óô‡gmG §#tû»Ì2"œ÷—´Õ=Gœƒ(>s8”!Í;­JoEëP°âq+'À†ÆK¾}’ôS G½™žàu¶Öþæù#IfÚ|ý}öýåןï«óëÄÏ[yßbÍ œçœ´Û’|n¡ð¢Îtµ¾U¼ü]à™“ä}íº=½àè¬GÓÏåÆõjñ…sêõÈ®ã¼Î œçu[<›SµžÝzRŠã^z(¸^(4;û&TÖ3ŠM¿‘0µL8I9~£‰›7œ2mrôü­#õÏ|ÚV¥ô2Â÷øû@üWÌýUà¸E6^Ú·[(Ü?ýýÈX巢͌p2Á__®Ýoxßñ@‰¦ŸÅx· ð·­#ùsŽ?ßx_(ÄÛ{·Ö<Àyê•ZtxwËPè¢86U1="ÈÞÕn…“pã˜õ]kyA†vÂR-ÎÓè[¡ø Îþ¶ç6¿ü¼‡ï³ðúËÏÇ­ù€óÜ Ð®òp …^æEMâV&ÀÖ×V~¯A¾5ÞQµðJOXZùw¾¥‘$bkÇ~…kú³sû¶sd¾ÿö—Ü>?9U …m[é¿! ‰PO°õƒ?»?¿‘û5ëÂÕŸðu/ç: ãeØø‹âsG ŽïíqíƒåÍ1prœ‰K”xœïrÅ>g"H\Ñ‚uVµè Æ…¤ô+Iò}ë´«ÆP²«ÈšõÞÏmœn¾~äûPüÜ×î=6m†ôA`£”ÇËÁY|š”<“ƒ“?z•úA®Ö:±l¾ÞPQVn»>è[†ÎC²ë_ßñóNþÜãï›ÙñYqžßʃ¶f…­;;¿õ4$À÷¨³#÷4Ö‘¯-|6äÛñ%±ÿ°¾gèOøs‚Ç?Ÿ÷ŸÂ÷uµ;¯‘â<ŸR&‹i{–½Kз$  9×ìeœBG Æž\U» ´³6 :²ûE͸&?—Úâ‹ÏÇ9Ú|>éËCIÕ«-Ù}öç8pî{öv˜5ãÝ©ØòèHøÛ9¶£@ôÇàMøp\ÙëHä¤ÐF%S–Úö+ø¾üËê™+wÿö€Z•æ®ü"¼wªÂq=]d÷[m< AcÇ|Æq{=+Ö·M¸Žxs)^éßÛv™û쩎L—_ì™J–~¿ù>9ç*ó< óz½-8Ñ>©ð^”çñ wµ‹D ­ÛÒ+•>:\Ÿ˜ª#†jd‘ºh˜Ñ`*ꮎ<>°=F¶”ï—³«òó~_„ëØÀþü ç©x±a@-èž¼ÓýÀû>&¿e@,ÆÓ«Ø£jÿ”Á¡š{UcuDxž-%üùÃ×óü=&þ¾2ñºÙ‚óô%…Gl:=›É¢û`k.倓0¿†gæôºJ¿uNÇž'þ„ïCð¿üûØsÞ3…<9œ!m|[’¯OÕCÐ#_™$>ÎìJ?“éI>T›røþ¬¾0¹uný@ÿý‚üw¶÷õ·=Ÿøçåë ^·þŠg*Áyê×}4s}èA˜ú!®Ûˆû àÕqåóHSeq±úýaÒpi£´Ó:Ò|뻨ø‰Ùõ‘¯3y_Å×Kü<Òš8þá/ž½çw?¿jÆE¼L¢ÝP-½Jiû¥JÃVo<ád¿öïžÐçNûíéoÛáÏCáùtË_¼®ˆë£ç‘iÐûí£ŸúŸ `êç[|¬(RåûÀúß€Ë ž=_qPGå+qx|9ÛzƒŸsð~Šç;žós.k¾à<-ªL:™:îTùµíXßâw }‡V³?ÝŠ"¡Á½zÝËô‚ºW¼+ŒßgĤªÆöý ßÿåçšü=Þ?ðs[!O¿ÏœgZî„ñ÷ò€é–»ÓÇÕº3j´íR¥Á)ò EÿªUVyƒ›ì®#Ï ÏœÇgMð’¶g÷Ã¥"«k/u¿·««ëN‘KWçK÷‚  %úö¼¯cç­KY_]™õÍ€×{¾ŽâëY1×ÖBãyD³z©+÷ÃÒß¡£/w¹¥[–R^zŠ$Z8¬ç/˜äq¼hìç®®š]¦ß¢¥¶ß9ñuï§ùuãçâûãp$Cúìª&ÅÒi?t¨6ùqÏ;P´ÜéCGÛèɸ|õ>|ßç E¦ ĺS£Î‹¾³–þû˜œyÃóÿ¯8·œçd³Ì}>Í;qv¦Æt“‡êIÈTs‰G5û¥Ý¯‹×'øÝNùz©m_×¾Œ´Åµýþ°"ÅyFK?Ïûry´ÿ¸¿çß;ÐLô¢¿YOÜ‹~¾v}Zpý°;Ù¹÷Æ´)þ¶ºÏû0¾ŽÎ¹ßÎß/³æΓ§vª¡²vôÉßõÊÑ×󷹫uš\¨Úqá’âžÐzS‡i:R[}¦¸t‚?á}6ÿ>MúŽõïÙdºX£ð¹fÞzîÉ⥄ÇßßàqÍ÷Ý5åc ÌþQÛŽWmÀyÚ7œöd‰q/´w}ûØwÍÐŽ‰,t¬ÁÒlM×á_ŠxÃÕ‹IIgîèÈÒ\ˊ״”ðºÉϵø¾2Ï~ÝĿ۲à<ò–rËÎ{áH¡¢1á[ïÀ†CÇf¬:Cx­ò­;Ï ¬Ë0|®5—Ž×ö•-%üþò>ƒÇ5ßW÷KG3¤Ö§¹Ó^°”¦¿T¼ƒ}iìÉ'–3Dà™{Âí“þ݇\Ö‘AwjŸ¾T+û{ðþLÿ´-Îx½³;Áy°™û¾f¨ ‡ùzæ”/ÙþYé†g‰dƉ{+oô…™Ž• 63ëÈÎóMSŽÔXjÛ/àý2†?wxÞŠ÷¥8Ïd‡’Cö@Ϧ9_¿'Wø¶tÚY2oopÁ úÀÜ™Õñѧ#ÒÞa«•Kmû¼Ïäë ûó¼öûÁ8>œ¿fÖߣgPÒôÐŦ,“;KÖ\:Xóswøºâ⸠‹ŽØvëtê§¥¶>“ç'¿/¼®ñû"îÏU8Ï«¬FNyœÜï¶Ï÷;°«põóÚ{gÉy¯sºíè …†½§³õÿüóòïÃ÷ƒí¯§p½48þ»]y®•h¢jkfM«Xô.T9–²'ÿ927¸ãøÒ-;‚ì`ÇCÏqüíÓj¥Õóg¿ŸLµÕ»÷Qq<¡ßÜ{ãÕ[V¹ šà,¿õΑ²7̸ÛÞ9ÅõyŒÏ•"ù“Ònÿξ.ü/ßGáë:ñþ€ÇOrëøx•d»Æ8§Î]¨ª%y´½Ï‘ài’Å1…:ÀÃ7\R°^}3þг¥¶u/_ñ}_^¯„ëaÿ»C‡cRëë0mvÂŽ ®whs2Z{ö_?ëù9dÑ;¯ClyѲՂøUyý‰°o%aý„‹íý þ¦¿’ká`ÿ{! ÎC‹OXå ¼BÑ£B3FÌÝsŽ„?+ñzfã®ð¾H…oµõ:"ô7K ¿.¼oú¤·¶8âý¹ø}d)Î󾯋mWZn‡™ÛýN\|Nox|xoÂ9ÒÀРXåwÝ¡véj§#:²²)Ù–'ovåÜÇþ«÷L8~áCèÙ¹Û âv+ˆv5mZô+~̳•óÌŒïÌøN2‘xã¨dž­î̳5+ßÉGÄw¢‹ñ©¿½SÆÈßµôïZªrø×¯¥Žì{R¯*gæÑoÊáÉʽú»ÉœÃ“Õy²f2OÖ0Æoû*C“ûf2­ÈÐÈ8"”ßdfžU!"Ï*êÉêÄXyCÄÂ"!Œ!"ù_‹Yy*æ (eÞ€4‰Ä …ˆ!B™¸&‘w•YäÉjfþ€!ŒáĽ«$˜x*”%ÃÔºL\ÊÊ3D²˜/k˜È—ÕÂx¸Z×,ò¾¦ü&_”‰±pƒ;$'»)=»‰û²º2nõeå~UÆÇËùR~€ •Ž’aâ‡16õ¼æLñ0æÇªbü[¹ÈP͘M>Ì£*§/”ž±o}sx]ÓÂ!g^×Î94>é?ZCyýüGµ“3ïĵ‘×EZÅ<Zy ×?ΫãuÖ4ZÃhý¢µ‰×¥T‹h ú¿Qhíù«ºóW5ç¿£ÞüU­ùGu†>ËdìaèËØDœÁIÙDæ«ï˘›ÔãN†7XKýóG[ÅüíBP2ÆÍ62| ó ¥^vÎÌ·NƼê¨Ocz(ÇšzÖSO%Êì"pÖ8¯ƒòª-"¶“ˆ­æÎØj”ÝA=“ÍŒ_Éý:Œ«&©ó'£ZÉØÔœåãØ•܇^Î<’%ŒG-ËáËiaÞÈZ”©’1*eÌ{ÞYä'ÇÔ3>¥šùÎË1õŒ¦f|œ~È*æï&ËÁ¤ü»Ÿù»ŸQ9üë÷3Îì{˜æš±(ÅÞ›Üc^ÂØC–Þ›Ræ½I=æÌcžrˆ(\ï˜Í§ó~(3cþ†0–GNRf÷ßtc>ó4¹”{Žre4êoÌY”Ôc^ÊDÉ1ñõŒF½9#]Ï|7ÕŒóKù”kK›N½å©ç¦åŠ"˜1~9û̉±ÏĞƎŒ}¦gÌ$±¯<Í5û~†ÖLZ/i×GîKLk ^ò?úqÝËYïh­£5ŽÖ6q]£5Ö.Z«hú?­Oÿ;µé¥&ý«Ô£ÿ¬ßá5ˆÞ'Êwd!Ê_¤Œq§œ¬Eº>*(pÃÍ(w JÎXá~Œ¡èΘµa̧ח±Í$Œ9‘Îxzº_TV`JhóÐsÚȘÞ!ŒÓ#gæÎ.ÙœYÊ+ãìê¯kª*ð¼ÓE|gư0ƬVä«ka|CMmÁK׵ΟìngÆî63v·–q»9Ïû”û0]WÌ_5cvS–¡%ÁüU1NõÏ C9c«ÇPÎüÉ)#Bʤ<Lc3orÆ‘ue|é_xæR¯ÛôvŸ›3 ÿîƒþîƒTÿú}+ûœfµÌØ‹2 Ú0”£ÈƒÜ•±vÒQ2 äT&ó ײ öe䔵C¹ÜÇl.7å2Êk‡{‘S>£ã3š÷³väÌ‹Ü “Áe@I×,SÄ| lX9crS惓ErÎÁ5£Ì‡,Æ| \n§Ì?ó²a-(7L.5*åŽI¸ŽRkÇeB¹aâ£2Q>˜€z K¹fbæårû1ÞŽSÊd\XÊ4ã\XÊ{׸ÜÔ‹\‰²0&¬†%oNÞeAŠy;*”…ñv¨¹#&·/ʈrc<3Æ„40ò`TÊßÀXfb7õwÅBÌ8°”ï`±¸iaðEQnX BX‘à 3gÆ0³ dX4ÂÛ2Ì Œ¯#ö§±JÿýÿUKñÖý[ÕÑ¿kèo ucÿ=ʆ¤|m-ʉqsLŒ ,bC†1nçkS6¤_6¤åŽA­FeÒóF n=Ê Üed|m JŠÁ®Ee¡|0èÃ[[ɸR’rÊôtï “Áño)§ÌeD¹br¨PÆsàœ2Ês00žƒ e¦{e"žƒ»ˆç@y¸Z–LrÆÌq¬(p!1±üP&ÆÕÖ0^Ž&š%qÉfãRNg9¨PæªöLmÎÊ‘0Žå”ÉÇÁ“S…J¯%pr´(GLT_” åVçO–¶„±´-Œ¥ÆX¾"d*«¡À)3¢Üðæ3Ž6å@P®˜èjÆncÂëQLz5c@R6Žq‚QY”‘ƒ…ÀÈø”‹“Eù ŒµëÆx 錷 e¼%ʘ Á¨ÌvÙìlÎ~ü»ý»U9üë×Pwö9, '„1å´z”®ʈrc,œL”YƒÊBÉ1 ÃPŽÔ~(#cáP棑1ƒóQΘ8Žð¾Œùè̘”î&bâø`"„¡œ1”(#Ê•±Ç²(û“CÃøº>Œ%îÀXâ”${,„%c‰;cù¢ (gL&%Ê„rg|Ýt”;&W0*%Å$Ó0æ£LÄÆQ¢Ì(wL¼TJ hpøº”=–…R`2êGɸ8®˜˜Á¨¬ê[—rÇ8[7åøLX*±uµ(Ç¿àá8äàá¨Q錇£E9arû¡L(wÆsdÌGÊÂqÄaI¯@kLÌ(7¢Ü°„°bà‹2‰xåŽT~(Ê „†±t9cLÂcé(9 =Ê™1ÆŒŒ‚¢¿ÈQ€cô­¥ÿ¨Žòý9Z?bÞZ3é-ÿ«Z)®“´FŠk£«ƒ}=×Aq üêŸÔá×>^÷þYÞí__*²k[Κ&®eâ:Fë¯]ÿYÍ×+Z«þwêTÎ%®KU‹¤ì/å|‡¡œ±þ¨r œowÆ?¤|oç|Û[Éx‡é´wà Ȣï4`1 œ1 LŒã­FÉ08zÆî¦LCÊ2¤|-ƒ“À•`ýP¢Ü0pÔ¨ôRÙ\-’%Áº¡FYè_ , JŠÁÂŒ²bóP_AàêQNX+”(3crÓàS  (WKZ”kU¿íƒ5ÁÀx°”«GI0@Õtžñ_ýPîu²ùÚ®¸éŒ©í'b jØCò¯L(wÆÐ¦lA#]{bpû`~P®äÁŒ'¨`LW7 øöà÷E™K&€/c¹R>eR9ÓsJúþU»l&6gò÷Å}Ñß=Ñß=Ñ¿RO$có¤ÓûÉØ€>¬”3¬eB¹c- Fe¡|0ˆµ,}Pz”´eB¹26 ‰±CXSvÊ ƒÝ1%ŒÈØTJI GI0T(Ê "„%å_kYrpþµ#ã_Q®˜,*”åŽI£A9bâ(Dük?”‘ñ¯U(3JÊX¬™()&V* %ÃÓ2V e`ëQΘl*”%ŤӰÄóE]kŠ&¡oUíŒÉ¨B™Pn˜”!,1)‡Õ(â°f¡ŒèŠÉªFeÖ¬a®[‰² d˜ÀZÆôEQ®˜ÌÁ¨L” “: 匉­D™QRLp-ʉ±M(wLv ÊÞeB¹åà_›PîX4Œ»ê‡2‹Ø×NX”(3JŠÅA˘«J”™1WÕ¨L” J‚EÃñݱxhXñýØ—û?­£×ÐÿõúïT?éõ Cqžµ%Á@UçxÖR Ø´œg-ÁàõË÷'ÏZÊxÖ™()u lÊHÏf1À•(3ãY£ÒQr ø0ô¾(ãY«Pé(ãYgÑwW1Œ( 㫺bR¨P&”;&G0*“¾¯†I¢a‰â‹2¡\1a‚Qé(wLœ`T:J† ¤A9–8«z”#&“e@9aRù¡ (gL.ÊÂXÖZ”#&š/ʈrÄ v8« þg(7L>5ÊRUàX‡ ²P LF#c¬‹«”+&g0*%Ç$Õ3¾ªeFIEüj?” å†ÉŒÊdüj½ˆ_mAI1¡±fú¡Ì()&wKp_” 冉‚Ê¢kJLx#Ê “>„%¾/㩺cР±ø¡Ì() Ê ‚c©J±0hUŽB’Ðó`T:ã †°‚ÁYÕnX8BþÞ—û[?ÿÝzP6N&½_¬TJAkDI0pU(3JŠ‚X C9ÒwãP”3µ eF¹ap«Pf”ƒ\ƒrÄ@W ô(g x%Ê„rÅÀ±ªµ, |Q”+&ƒeF¹cRhPŽŒS†rÄñ-)ðª¯Ú„rÄQ£,()&Žå„Éã+âU+Q&Æ«V£,(&••…’ariX‚ÉQa,Ñ(³Ú€’`©Qé(&žåˆÉç‡2¡Ü%ø¿G9b"úUxÕLH5ÊŒrÇÄÔ 19ýP&”&iKT_” å† ŒÊBù`âê]Nµ •Ž’c‡¡œ0‘ýP&”&t* %ÇÄÖ£$˜Ü*”%Ã$C9c¢«Pf”^‹r¤÷C™Qî9øÔf” å„Å@‰²ˆØÔÎXT( J†" åŒEB…² Ü°X£²èo©°hQ®X8”(3cJk=®´ßßµôïZêðïQKé5Ò£1PýP”+l0}÷%ÃÀÕ 1x(#Ê•î/¢L(W f5Ê‚’aPkPY(·åH2¡\éï P”>•‰òÁÀ×£1øýPF”&šîC¢ä˜ –¾(ÊC‰2¡Ü0AÔ(3JJ÷%QY(9&ŒåHG2£Ü0yBè{†()&Q*“þ–оwˆr„òEPNteD9c‚)QF”MJGÉ1áÂèï10éüP&”;&_K@_” 厉ŒJGÉ1!5,)}Q&”;&g* ¥À$5¢Ü0QCPY(LXJ‚I«BYP2LÞ0”3ý}ÊŒrÇDAeѳLhÊ“Z…JGÉ0¹µ('Lp%Ê‚’a¢kPŽ˜ì~(3Ê“^ÃßeB¹cР±ø¡Ì()-Ê ‚eAɰ0hQNX”( J†EB‹ÊBù`±0 \±`£2QR,”#?” E©ÄÎ’Ul½ï–ã=œ0~ëêÎΣ3Ùyt;öc¿ ¡}¯šý>DÊê4Ý/0ÁŸ¿óâÿö . ÒÐ ©æb‰ýý^…Ø|^æÎÐ{Í7ßµù±\°¶Ó\7B¸ïøšÂ#¿×ˆïkó]ucQm>®³ùMLZ¦‹Óø;¿ œgî¦È^ïô!@Ýç·XîBÛü³RoÍ dHz÷¼GËaÕ¿ü‹I¶O÷“àŸËÆug<î-æ>¨pÿÉ1î]N¿üÊÿú.œIà„´7`Óå'½!¤|‹«ßndûJpî»Âýš¸ÿ_ì§£ÁyªR›Ão!ðtÔœ%ës'BÛ\y¾¼GˆGÓøù½úÀ²¥[uJÓ‘ò~›×?­±ÌæûÀ¯+÷¯àþ=ÜïFì+bÀyºÉ]oeÙ­@©![*%‚ë"YSÁ@ª_¬p1hY_ˆœB™:²·}¡]ŽÿéOÎý.ù÷â~eb ÎÓÄ‹:Tm…ß'ZOßß4Ž•>ô»¯Ô@ZÆ>Þ;¨“'È7Sͯt6_=î[̹ü{pŸB;nâñ é'®ÒÒx_fU)ó{N§Dعû‘{æé~Ä¡Î÷¬6Ÿ#+F£ê2›¿ç‰ðÏÏyEbn«Çÿpl™ò¨Ò¼VÌ~‰Ð`[ǹ!²Ù+ÝõØW/˜M±Û):Òdƒ¶Ü‚mþ6ße~½¸ÿ ¿/ɇÆy&ϼÙgZ¥QŸºI†&B#«‘¦Œ ( ¿ê’…¶¿­#WÔ_äoóÁæãžëkZ?)Åö}¸/‘˜_©Ày¾úN‹s«?%íÍç›i%zöÑ@¾8ÜÛÒ4u‹Pò„Ž„‚ï†îò·ùys¿îWÃý|¸‰ØçJ…óôÉMÉCÀCúåéõÙ‰Ðwîò>˪D“7O”GÒuý¡ï—\²Éx_î o÷fph¶ï!¿n‚Ë%[üŠy?ˆÚ¨Î „ÔÝ+‚á²ì³cóŽÑ¤h£kSÆËaôLiÉÏ¿žžÔÖß¿|ž÷<¾8oPìGoÀyŽUŽmõ2=ü/|üü|o"|Yîû’ÑÄj#þ£;¤6¯Ú8]Gòð6)Ÿ'‚/ÎVìeÁqc´l(·•ù†'BÔº=åçÍ‹&—ÏèWçÏì‡òQG^)ô>b]ÆÁì¸øŽ˜OZS;%‡Òù)Ib+ô‘–q)!¬¶CÁÑäF¥–{"»v8üù…òºŽl–¥f•ZNøÿžûèpß7áz4¶ó×‘àø O~yø9ß< ?ò*J´|v_4©ÿdðøQ-:@pšÐ:Ò¨‘Á£õrÛõ°Ÿç¥ÍoYì$ÅñÓ׽lCB „õB$vÑ[Õ¨hòñ|½Ú]Út„‡¦ÁmÏ=בʹ»æ.Ú"ûóóëÃ} ùýåü8q]Rà<‚oOã(%A³2ízd\&÷§yJë e5“›gàuºT{§Ìm¹-x~ >>Ѷ¼æ~oâøQá6Áœ’%~u6éÈÓÌnµKö\nó•ãþG)¿Z§Eô7çðå´çÇjpž^û®Ä:ûl:ëOäí72 N96n»ý[4ÙvÒ·u›ò=a­ß Ì½'tDðù_n{>ñùT+~-Q!ÍæÆý4Å>yœGûûØ×w[6C¯¤RCË-J‚»NXþ"é~q¦SÝÚrð¤Þ’:bî|;‹ÿr›ï>q?i~(MþÓǪö~8O°ɱ)¿6AKj÷¹% TýöuIjs‘¬~n~6±M?X8Û0+6HG|«'õªsb¹Í§+§¯¯Sý’R¾{–©kÇÃqËö=ß/ø‰:xdø‹Ê¶>‹÷9œ3ΟO ×·™T­iK»ï¡Ây6GÌé2xÓFxf˜Pªö—6¥/Ágx”x8<ïê­:2§AU—“²ùJÜ/O¸n€?g­ùã–êZE]?~#.äßdu-3”Æèõó¸Djî.z"%£?d”¿ØøÚ.‰yÓfÅåE+l¾ÛÜŒÇ+ç}󼱿Žß¯îÂçm¹C¬q}+3|‘µêms‰$«ÞB©Í»VG:©ë{”}ýù}æ>]ÜLࣗ³«œÇÿǤ'gêl‚¼Uœ¥Yr3Ý^r$ðQúðÝ;ûÀƒÑ!OWîÿöWý™ÃI¼ÞVþÌF 4̲¾føýªk€1ìY-ñéÕ°þ€¹+m®#šÕCZ8—Ÿ9¹”ö}µðœ–àø{ç'6Tl#ì¤ØÏEf¨&9ó}Žù‰hF‰´ž@»e¤Ž¼Ñ5¯æ\`å>ˆ‚ßéE›ÏC1oGŠóTZh%AÔè˶™áÚ— N¾ycÈŒ+K®ï÷†ÙŸù=¬PñmCWÚâS¸¯‰¶þŸ×1gGã þøàrbÓe£6›¡•6¢[³–1dþ€‹Sšú †GJ—ðœ0ÚüG¹O±¸ÏÔàøKLó²šôÝÓf9Ä =o†Ý [f‡Çú©fù´UÀü/—çß½š]¸Ÿ }¼>°õMü9aÍšËéæ/¢6¦ѕ×7ê«õTûCÊÛô–›6äÒT<…ýµÏ]Aøs‡¯‹„~ÁÞÒ‚ã ~Ù¡mÏïÏ~½4CñwÅÏkKX?Àp8X+ºb<æWÍ ýÛ\º³ÂæCÉÿrŸyáþ¾·ùŠûc‡ð iùgy'OÏÚg£N ^Ÿ'\3×[9-– .‘ÑñF—¡ð^xÏ㨎4¼–4&«ÏJwƒ¯_¸¯&Ï îoÇgÇyÞg¬4+n„·´Ü”M†Õõ?Vz¥%£†”Õ?¬4ZEíªZ)ïëÎO·fçïû9·‚× îoÇEÅy¦XŠ¡Ç†~¦×5“aÈ § ×RbIƒ>‰Ã¾õòÁo¥­Žr¾€ð¼®fÇoTàxu.‹|m#”i9¥1´L†uºŠ˜yqdø^·û{°Õ9IDDúÎBj[žñºÁûîÏy®ÖøÇñ¿Ìœuyäö0dõ«î×dÉ}ôéÍžnq¤PVp9Múø®GÞþØ‹ë kþ¨m¾Å<xÿÇóY¨£µßokà<Í;@ÿƒ7BíÆŽÇ* J†JïëÆyˆ#¹ôûºÍ˜ÕFü0m2~rwN]®¶å¿܇—×WΗs> 8OµGÏ<›8l„¥¡%ë— #xä3(ãHçSÞ|=Ú¿ôÚ¦#ÊΙdÓ+õœ1îãÈóŽÇ³x‚ó>Ø"v?2¦ìz¼=Ž(?-Ï86­/,¼yïø–ƒ:²ÿèÌ9;‹®²ñß8÷±gµœ}^DdH[>º2eìÇ PoÏÙ7’AÓã^ëýçãÈçc‘í[vî ~~h¦À>æ”kDûZå³ùè¼þåäŒqÿgq•à< "c½Lá@÷çìHŸõm1DãȽGõ®UýÐêX0:rãhë£ù>«mqË}¸ùºž÷ãvþ¯8~õq#ú´ù± §ujp#4\žXð2 N÷LŸÞÊUgŒ‘OÆu×çáÏÞPÿÑWr÷±ûƒ*p|]ΖÚ[äÑÉPûæö;›\&BÓ%_=!æÕ˜ÔñuĹÀ–5­neÇ_çr¿r^_ù¾îYóçiÚkÔü!PàÕ„÷ï&Ch×Ñ{†\&w_ktŸ«Ã»œL‹Ÿ #U.œ±ø“ÚÖ‡sn0ßgãõ[\748~J«¹_Û «»xœP¾L¹ûÙÈž+.“ÜÓŠVsìâ îûû.Ù€×i÷ôže¿¸¬"ü~òzÇ÷oxZóÇÝ:öLR·…[AU¢ÙØ’Ÿðú·éڰۉˌãá ‹ªìkûaºŽ¬ßøÑM×z•íùÃ9¼® ×£Š]fÁñ»þ¾é›k´/Ö[Þd0J*ù4MºLîÛûÝ}8t•6:âf©mý ¯#œÃëªÀ—j`Ûdzæƒ.Cºéô×.3omƒŠïoy”'œ>M«^ñ×eò3Ú0ãíÃ~°›>ž½p”ù±ûÀj[?ÉÇáPþ}x?ÌóÏš8O¦ñm¹PÆÇ¡þb)°J#'¯n$]‹]>Ô¼|p(ûõSG<´wAu7µí>ðï#쯦Ûú×¢G•Õ?Ážã‚ótþ81ãÐïà~z®ê)¿ðõ]$hI…Q×s÷ƒÑ¹­®Ñ^GŠþЮø¹+ûûð¼«ZÌXzä“ ÇEÈ‹¶ö\<œg€h½ xïe«hpÓŸ¸F§;˜ù½>Á:bÅì4]e[óºËŸO|=,ÔÛf¶ü´æÎsóräž‘;vCuëFt œŽý‘fÚh$iåêžXì ¾Ð¯ÓŠ:ÒôQn“ëÐ[=ä}︯¾]…ã§\‹JHTk ßç9þ¾)ðiçðb$ñ²MÓÇzAï„q†xëˆqØÛ_Ê„÷y|ˆ÷µÜ_ï/ˆ9 œçhÍÊO-ö€¶_DAKð{8vðýðÆHÖß+;{¤d Ô£X³Cd†4ú²"öpâ^xù‚¢§@·ÛSóö|…Œè–gUËCàì[ç*ßêH©åæÅ¤¯Ê±îº”ÞqUq\ Ýæ=½Òí>æ|<æ×í¹,|Ór&@Rgá‚¡p ôÌùÝFéH+×__eãÃðëÃ÷Kùº—ïëXóÇ'£”¯ÆÜÏ|žS Š›îÁbÓ2¯ÞÍ›^Cáv%;°Ž7;7ÐÿË€ìÏÍã•?çx_ËóZx.~µ^wÎ㾬nóùš0žÚp›S 9ây“毒أÒ.• ÷}äÎÇ눵ü>^eÛ䟗s„¾*Ýö\µÛ‡Ây¬x‹þáQ®1ýg¦À†›³Ûœlw•hÎ ƒñ½| ¬bTƒ«“pž%/tSüþàIóûÁ¿çÙ‹ûZ ÎéL;ôƒð†â. ¥Âww˜ Ÿ|•J­þÔ=—7¬{Ûïåúi:RZR[ÃÛuã}4ï«.¬ßVøs›Ÿxy¬–È€óÜúCpà^ú(I*lZ}þÆí­WI›„MåUÞàÕø•¤ÞŸÄ©µ>‰yÁøbü: þá=€÷¹Ö<Àq-%ÊÙx.ô8Ü#®q*Ä'µ»ôôâU›ºk‘«Í¶ Ñ‘;ïl9°3ÀÖÿçäòõêÒ5^‘û÷µµ~Da_xtp»ÚçAžWûú{{¤²çìU2çÐfÇúþ°ôhÛ%aýu¶þ™?ŸÍTíºÎb{~ ׿¶Ýþ¸Çx‡ â˜:R– ¦búã!%®‘=™‹–Ä®è×ù7®ÖWG¤DÇ?š~ý…¼©fwý¥QœO­…¬è¯wNvO…¯i£OÄ»_#åŒ{wïÞžhã®#s¬ ¶>‡?Ï9§š?gù¾œ_çÙê¯Ù7 Áaèæ{ítiïT4Üx¨€ây6*¾u>ÏþÛ¶ÞøØ–:’ШÄÔ‹më$>Ÿ°OqÁVÿìùâ¿N…óLtz6»Ñ—Ã`}Ìú¦Â±šÊ] +¯‘5Õïˆëë ÄÚnèHÝ+­+ =hë ù_¾¿ÅûÎ%_7 Îól°KbÅÈ#0ri… Ëg¥ÂÑY+Ow;yÌ8Ѥ^Íл*K1É›{g·íG‰=_&ÛÏ}xM—³…7Ö°?¯Àñî÷Q(öu\þwKRÁ{b¹U$íñÕ¤ûホ—ëÏ/©#‹-/pãx -¾øuã\ ~½„çx}ûó œ§Dp£=K/…èÖk\ê®N…"±#ºNœÈ±mËzÃõÅ2²°²Žœ¸ÙX2ÿ|öõâë ûû^ÇîüÈáT†ôÇô±g;¸ƒ‘Ë{¬_Ÿ úAKŸ·ºNòN:6Þ_ï ›v.’—«£#Öm½˜À?ú~®ÌïçÊØñ¹qž•{¥»ޱ}æT(wð€öÅøëdj䇽óûÀ±ß3B5Ò_+>>ÐÖp>%¯'Bÿ^ÝÖwYóÇ/ùÅç›)_(Ôÿ+$ñ`*\®¶¥[•­×IŸ´6ºä½^°@ÿDß¿µŽ„ß™Gn¯ù3~…}MbûÕ'(pžàó÷¾ 17K·>q2Î;O5剹Nú(:®úO(;¡×‡5XOü}C}gì´õ <„ïsÄö}xÝǯ çù\³bñ(s(¤]»òÂÙTо:òywÆu2#µ|.¿®ý¡hlå©ïëȯµ´sœ·>´Õ-{.­Ð/hpüq[;»F-=ôtrðT¸Ñçìáõ%oó*mç5úƒûæ£#Éë»Ö9èOÎøµ;÷Æñ÷Í&…2÷½Ÿvxš ð¦„äk«$ŸËý›åõ‡‚ÍÌwæbŸs%, e“µA$'?†Ç­5p¼wŸ"6ïpú~MzõS*D^»wÔÈÄl$–'FOÈc0êȶNç- ;²Çã÷—× þ\ïŸ:è3¤àp¸×Ž´à¨ù>\;™¿U«Õ7ˆeï›aÝzyAJÅzØÿoL–l ²ÕY~=øzï»s¾“]à<ÇòHÞ¼åŠ-«Tì©töÛ¸s7ˆÃ݆CŠÜñ¾O‡)p=³àíTÓ°…A6®ï/ùþÝsÇ­mûñ5* Ú‹?ø²J´4?=p,ógy°Â §kc‹uk€ëíq§6_ñ ²­Ç„ø»aë+ùóA¼/¨Àño™Pò\Õ“0±³•ÌßgÜ~çz“Tx{êz·¡@©nÅkëÈ€‹¥æýìdë[yßÄï'_·Šëœ Ç÷Îû{ð•5'Á¿%§Á”àßúˆ17Iß*×ÓšlØc®9RKGÀ N´òºÊy¹öÏÓ’vïchpžrEŽŸÞ÷û$D%–+8¹U$†µ¾1ôàMòûNÂÞ­KðËõåõv5tDûcâܧóÿà‚ò}(G|ÿCÜpžC3Ã~ݧÂ;aª¥ÁÓ^É¢Ÿß$o‚ \¾=v–¢Àa웚*öQø——¯[7™#Ïß[_Éî{Xpüùöÿ,á²Roú¤AãÖ±}Ô5oËãÜ+¶w ’QouðБwˆ-.¹;û¹À×u¼Îñ}A;~êé iî‡|š;F€_¡ze<=Ó`CåŠK”Co‘Ò‹§6è7Ú¿u}h±âÆ«dÇ©0n’íúðý1GQ‚ã¿Þþýèå…ÐÝz`–'Ÿ ž·vÍ-{pS‰7¬z30É·•Žäû~bFÂØ [½æý8Nó|ãÏi1ßEŠóLíñdiÓ¬hVrO‰ICÒ _þˆã³Oß">"} {‚ÎaòÑãp}1náÕÆƒlï•ðzÊã—ÿ¾OC;Þœçi7úêJͼ±@ç>* F¬Žxáh¹Ešl]~»@­~ÞýÅÖa#udÄä ™%ƒlûüºñº'ÔY7÷Ôš8þ„f§ódè`ÕÃ;GLHC¥G1?r™lûyiY­#ñkWôö,ð¾%?GâçÖ|ÀqoWïr«ÁÌHhPC1sîŒ4øü¢r©›¥Ld¨¬þÍóõûAþÄi?ä¸.27œì^¿hmýÎ×½<ø:Ìÿ8®âäŠÀ´„HøœA¿HÄEyÅŽ©e"yÚs•žî'¤4¨#ÎÊ7í†L²}^ž×ü¹ÏëRSuñWYÜ„øÇñ½·ôÏ=²d4ŽuN®9?¿´Æ;Žw~ÝCíjž‚¬¯o£'nIøëóMdwÒšµƒ¼aíÓ»cûa¿XûLÖì:-Ü¿»¶ýá9SËvŸ­qŽã *±eò)8´Š[ñ­iÐgù*yÔzÉרÙHã ƒ#'v„×]àlýñÞ_wò÷µø}µ‹wœ§›jÛ ¨ÄS0ÜÔ!wü·´žO&1‘šƒïÆúzC¹ngo_^ #£§v J ²=øº¯C­qŽãUv×iüT=(‹¬ýYsíxÖD›È¢”^›6*¼ ÜÎú‘÷§êH—Nçæ¹žç9÷18—Q|_ 8~¾èÇ©góŸa?, ®ž˜Wñs¢‰4q:Ó:ÊÓŠ%M(>Ž44n~šÑ$ȶoÏë ¿.ü½ Îe_ Îcš?ÊÑaÍihi}Ñ/ rkžÞ©óÒDÚu.V¢å¡~à·ñ\ëË*¹æO_`ÌŽ{~}øùèàr“;=S=¶½ïgÇ>›!-Ø'ýSíw§áMÍÕ·[IƒÝ{›=õüa"•5I” é q½)QSG’¢£ïÇý±/ùÃÉ5à‘÷±4p^4¯AÑÛäªêgÕ®Ëä í+krtŽŽ¸Ò¶ÂmuŽ>(ÞÆMµÆ=ŽW½Á¦SÕwœåiõÛïÖØËK+ß&ëÚV]þc¡¬¯%ÎÔ‘È ;˜!«mõ†×~°³Fîy+Ê<¶½×g{»Ã¥÷ÏSÏÀò·?‹¦Mƒ›ÛÆYÖð6)rbɨå}á}˜|â6ü¼O²öÄ«·®¶]w~y_ί;_·ZãÇôÛÐwŒóY¸ji4Îd‚ö·‰çN—àM‡ûCþ†k›W÷Çú^ñMõSµþüü<îy=¿£Áñ§êצm›yΧ*iðÜkÔè^·Iãgý'Mkâeãœy>I^í úcŸ–Ÿßóó~¾k¿{Œß•ž…Š£è›6XÂ~·n9ù6‰›~pôƋް߯]¡¤}:rðmó¼—ÇÙ¸¡ü€ŸØñ|qÜ)‡O§)ÎA+…÷†Cx_3KO?°kùmò³~›ñ+¾zÞ3§³RwéH¥“ewçVd×3û÷,¶¾YˆO‰Ýþ Ã¹ 镵Ye&êÏA‰5?×KÖÕ;o“þZýYçÒÞ`}Û#HGfËWe8{g×ÞGózÆûþž£Ïç)PýsAóyÈwoX¼sḧžé§»M®5]ÓÎ ^ô?#Ã:ÑfFñ‚¾Úê?ïßxí×é˱‰ºúvûñRÿóæéÌóÏC'‹L}û„ÎKóJ®Ü&÷Ö›4bZ8óÅïÊ´C:’ëGÿ#’ ?äóðøçÏslNSWôucû ÂsFó\Û•·ÅsËyØÒjhÙRÛÓ`éÖpPêmöŒŽzÐHÒ‘ ön8Høù ߟçûk<ÅÏwŽ?±ë´5­[_€ÃÉyB ÁiwMÞ “^Þ&†B=¶ |ÐjK–ž1ëÈÖE«”?²¿Ï;~nϹ˜Ö<ÀqûuÛ©ªuñ¬ë’¯wÔ’4¨ÓºeÉ¢_n“BÆ^—.ë'æ,>çª#U+W¹˜5>û>ó¸âëH^׮ߩ¡ìX¡¦Ý~šç9‘QîÕŠÉ&&˜‡ÆþdJÏ>>rÅòª£ÿé¥ ¹ú"‡÷êÈŒîÒž—Ùú·¼¯âñ$¬3j Ï?uüÄNnÅ ðxŠSh™ÉiPvÑ™kk Æ“¬tß°¼¡m`M»ù ûoÐY¹í^…–d^7ø¾ ýûŵìÞ÷q8}œËÎ3ïàœ¤Zó×#Ó`c™Kws9Å“Èç«ßÌoÔ ¬¸ÙxIøÔ)i¹Köóžßg¾à×K¶/wíTCÛzÊš8OCohqe­ènÆ`Ÿ4ˆ5XÙx2ûÍÊA­ZÁbú¸Àyö¯š£ªÝ Èv¿ù~ÏoGü}ik^àø_Þ§ék¢2ìs_ªÓTξ^ÅVXârWG¼>½oÓ ×üóó¿ü¼MÈ÷FvýŠÇ/´÷MžàT¹çàž©eÇ´0Öˆ'½ší›WÞÆÓí T©pÿXô÷#ÙÏ~_Ä}¹ ÇëÜ=bn¥üÑðõ|¯ÅŸ«ãz~ÐøÐKõ㉹oàœÛ:À¥„zcÊ?Ò‘<ƒkúTºùg_Îï+ϯ‡EÇ{ZF׳»îœ§¶yñÎí-¢aÒ›ôŧ˦A¹ûÛ7íkO¶õ£…¯ùZlÎÓÞ\Ç«èÅìúÊÇÅÉL(f½ 8.Ýå;4#>/.öái¾4˜›9ÅС}ñDÙb[Éßce°áÉé Å D…ÃÃŽ«6g¯ssÖ}ñúG‚ã޻ѿòô¦A¯TÞZò4VŸS#ϘxrbËAG¿y]áj_•kHãHrbãõ¨¡AüΠDÒü6 ›^ؾ¿Ë§¤zõõ€ŸYãçù™òíL¼>m?ÉüSSÁ¥Ø©ô‚~ñdý Ó‹¹-»À¬«û*ŽèI¼FîÚ¤{Hø¾ ÿËë‹5Žq¼2ÆWõçº'SÇmÚŸ >²úOúß4kÿîÎPuCÐŽåí"mu÷Å|ÝÃû<ñû*7®©oúnËExî~ªà—«©0åÁ¾_ó–Ç“|íêt=Õ J5MÇ÷$æíÎë7¬ ´åÏ?¾äﳋãMƒã˵=S¢ì%À‡•SÛ ©P­ÕVý€àxò%ÿ™¾dl'èã=§~T·HrsU;ÝÚ2Ùù¯Ãðòcº~\½LJ^<Ö©ˆû4γūã£*=.AÁKªšÞ'SáEURýÖ¶x’0©ÊÊèÙïc"‰ð¾qö}åýŽ8þ,8žÞƒnh]‚͇<Ù• [õ|yæ`òç&¯Ö¸'ü}èØ<÷èñŽÍSáÒ“y¶×I ø0zܬSgð#k,Åë>ùiMÌ€ìþŽÇ)ï‰ï»óç¸>:2¤Ÿ ^ËÆBìÚÆsTM…qQõ>F7I œŒ5×´•BW£t×Ð ‘ÄáÊÌôç²ó€Ç)#þü°Æ=Žk=e–ÆÂ…rÃu— c}46÷ÚV dl³^MÝ´„=¥:z¯7Ü?êU+»_áÏ7¾žâyÆóÚn_ç¹}ÍuóàA±@w-ª}M‘!CÛ?ñH ±›Š5>}¬è‚U·ŸÞˆ$«¶¥èÛ%»oáqÄï+ßáû¨ÖøÇñçY_DˆÇg·|’×Z®ë³¨}¹xag§e‹«Âx¯¨âs¯E·†ÆN ²=?x]ãý0ñ{b*?÷.ßÀå±Ð¼]ñ¤@ƒ±­ë¾î˜@ïÄžŠÞV®Ê(שù¶!AKfeŸKˆûP ŽÓò°ï±Ÿ;c¡x¡î·¯žO‡…\ï$tM +7×›îQ¯xÌYú3é4ÞǵZù½!AÜGþ<¿gÀqC&µ5< Yò½5×J²^ËGTì™@,ÚkÎwÍÍà)ÌyVh$) qi‘Ë‚?¶å#ëƒxÿ.^Xpü.wô¿ Û×ó5\›ï+wéW©o ÆÕøàj-¡I¥[ã¤ç#m}:¾ßÏ÷QxÝ>ÿo¡ÎGc|<ߤwˆƒ˜ŽÎ°z~ ,Ûky'—ÍWFvØÛ†%Æä~;ûyÄŸCüùÊ×{šßûß™²÷EqÜÒZ®Ï‰ƒ‡Ç—Kè™3öê= LK ù®7™W£t{p¬DO$#ÉëNÝdû'gß!ÿ_Ù>·øú«p\á|&bŸ÷ûÝ:z†¸Li77Lï!÷ýÕ®Ôk|«û w‘$qÍ£J³ff^ž? ×î;¶ÿùÚCüž„Ç-Ö |çÛCqpjñ˜Ýê¤Àø6¹´íT ä×ú¯¯j6q‡€Ë#Õ®Q9ò=Ãö¼¶{¿ ÇëGËóÙ8è¾cY|¹È33o™oËÈÌÙI‹’Š4‡ï¡~ãxÎCïW:~7¼7§×pL›Wû¦> Àú}(Ó«çà&p°3}Ó2Š\^ÒãHTvýιïÃß û«ßi;\ÌîXY*¤wFä+3€ÄM†[ë£m[›@®[îg jÜtGè‹{QäÓù}Pvý~o•Ý¿üÕï³%8~6Í·T,r'.¨õýy2œ?]ÐñËÆâ³d¢ûìm- žaÎö–½£Èû—•ò„'e÷ÏB?zÕ|=ÈûÞ¿òLýÛgåoŸ•%Ÿv (×ÑùU92–¬‘±8¸ç÷X‘0s>Q­yÈ›™ßŸ–ù«ø11ŸÚ‡ñ©Åb¯h3c¿ådßæôIU3~÷Iuù¤r~ç8¿HÍ|U¤Ìï/“ñp¹W*e9jEÞ*—û¥rÿx…ˆOÍýU¸ï_ºˆ'fãº23c)=FµþÚwúïúw ýWª¡Žì;R&œTÄ3òc<#w‘O•/ÊÄ|ªT(K>‹«ùN[˜GUÊ [‰2ç`â*WÌ‚“2Ïiø>"¯¿ào'æOeFI—ó7ŒŒ¿¡ÊÁ¿äþùÔ/U.bÀùŠp!,‰|™Wª+&“šñ߸ϴ„ùLs?ÊËä¼pÊË”J?Ê“‹¸~ÌgšûûÑ$äÜ î3M¹”ƒ«a‰é“Ã+ÕÂ8F9ù™9=þ‚{ƒ{ü9‹<þ8{ƒ3àLŒ½Ap”ŒySe1Ž&÷ù£¸0”&¾’yós–&÷úãžÓ¾"&®ª•à!Å=ª¸‡”6SÓMä!E}¨TWBÐUCÿª~þ³u“Þn^/sÖJz‹þwjä?[ÿQmüÏêbΚHëáV ÿÙ:Hkà?SÿþOkŸ¸îýŸÖ¼Äß嵎Ö9'öŸs>Êývgä+òÔ§^¦ÔÃÔ‰ñÙ¸´¶€ÀùVŠüKÿ“ú왊fûëù0ž·~?”™ñ¼µÌ§Ôù=»‰|õÂPΨ*Tºˆ_éÌøkœûAÙkÔãÙȸ•”$gÜîí,öÑSb[PR t-íû$Ù¬µtÆþÈé—ÓËY¸¾ÜƒTÍØ@” DÝ”«ˆÑ-ÅdѰ„Qü…_^óËã|7‘³‚ñÕ¨÷¨’yà+E~y‘ï(åRÊ1áô"¿<îã"bûrïQ%ãQ6·;&¤†±¹ŒÍM½òÂ<þîßþîßþõû7gö,ô^ˆFJÆ0¢¾÷Z”®ÊŒrÃV£Òó <ß0‘_3åÉ1¨õ(g lÊ’ƒåëËX¾b†šŒy5;˜!F”ó»O±Ê)‹R…² dŒãËy!&Æ QçàPr¿{ê-ê#b§ù‰ØiæuïÇ|EÝ0™‚7û3»2fÎ;§ JÎ;§ JÊ;×Jnšˆ¢dþÌRL@-ógæœîÏL9!”ß«E9bb*røŠ¦3fRN%åŸ+ÿÜy4SVˆ&±y4«Qé"Vg§™+„²ÓÒQrLr-KtÊ¢Ô¢œš ü4=Ê_Å|ï92%y5û‰X¾Ü»SNKÌcT.òåLJwƤLgÜ5ó•ƒÿôßW ¥õ“ÖMqÍ×ËœµòÕɦFþ¯ôpâºøÕÄœuð?ªÿYýû¯¬}U÷þ+ú·œõŽÖ¹œ5Nâ <‹0ˆ”¹f¹ƒI“[`–û‰<é5,¸(‰óv•"å°¯œúÑ[P2 <­ˆµëƸmNÌ3ÙLåˆY´®a0:c0*QÆ$wbü63ãv„°Õ£$¤jTf™lþ#婯C×ú"›÷‘2Ž|£ƒû!gÒõ'Ö,g¬W*” < å„F)|]›ÃÞef<±ï±+ãäšQîŒc$qŒ(_܈rc|q&‰刉âË<%X“Ô¨L”“Gr±8ÜEžÇ¾ŒóæÌ¼ã)ÓQ…2£¤XÒQ2L´0–l>(Ê“N%ò<Öˆ¹z”„ùÇ»ŠØâRLJm[)Nùo”‹KßÖVþ½÷wçð¯ßǹ²ÏHùor¿HÅøEÔ/> 匫DYPîÀÁ¨Ì|—òß$ÌjÆ.òÁ 6 $ØjTz®cáŠÙorÆwbÌ ÊùÄgŠ˜â”[¤F¥£äŒƒËyfÆÛÎÁ±äñY”ÿ&b¾)EÌ7-ó‡W¢L(wL¦Æ{S`RQn˜XÁ"–8åYr–8eZÊ%øyP4á"Îå½YP2LÀ0”“ˆ³áŽÉ¨a Iù·a('LL_”åŠ7HÊdÌ"ξäÌ"ÊW1ž¸“WÃX Lb#Ê9•)bmpæ›…±6(ó-åƒI†rl*ð1ÃPÎŒûf@I0ñÕÌ'ž323iýÄB GIû³pƒQỸ]Ï „cqnf:óŠalq)óƒÏb~ð4Vé¿¿kèÌ×ýµ~þwÔÎÿºIï)å¾9c`ªr q¨6·À§ÜL J†ÁªE92^‘AÄ|KGÉ1€õ†¸•Ž’c0‡1®¯’±}ÿŠ÷æŒA®DY_#„¼/Ê€’`à«PéŒN9EÎŒ÷fal Ê‘®yQ”+&F0*«L6+Ó•±Þ2ëMËXo~(³ˆ“I“GrÅ FeQF&’%ÁdR£ÒQrL*=ÊY‚Ÿe–Œ7ÊÓÈdŒ7-Ê “M‰²ˆx4ñ|Q&ÆÓ |` J*bÉEl"Ê 7¡Ü3< %Ç$ C9Qn0ʈrÅ„ Fe¡|0q (‰ˆ©AYoZ”ã¼I0™U( cdªQ” “[ʤgÀ˜äzÆÉT Œ( &¼•Ž’3®ç P®XÔŒOĹá2,a('7ÜÉCà½QÞ0usVýÝoþÝo:üë÷›îì3PÖ›ˆO¤f|"9­%ÁÀU¡ÒQR àTV>7L9o®ÌÁŒM¤À 6¢\1°ƒQ™íYÃJÆ3Þ|ÝßeFI1´Œ“ÉYé”KŒÊ¤ÌaÆvÆÄP¡,(÷Ò7]ÌÉtÇdѰ„ñ±ÝT"¶å¦;c©Pf”“IÃÊeB¹cb…ˆøé”Iùéœé#ÁÏ#¸n”/lB¹2®[:JŽ ¨G9c*Qf”“Q‹r¬%ð…õ(gLL?” å† ŒÊb<"ÎÈä<"Ê\W3îº “WËØeB¹a"‡ ²PrLh½ˆí–Ž’1¶[=kÁ$×£œš ¬L=JÂønF”+&~0Ê"âefÑZŠ…À€reŒ7Îa…A2 )Û±‰83“²‰dŒMD™í2,!¬x(@È ú¡´~þ³õòùþ£:ùWõñŸ©‹ÿ 51g=ügjáTyýûjŸÊá¯÷ i­£u.gûÏê­k9k?Û嵌^WÊZ“`°¨s ìt9MXnN™•étýœW`ªe¢|0€ Fz0*åƒÁ¤g|_3c©‰9j¬S*T:Êë”åˆ5ʾ뇘é$ðÐõ( ¢ Q‹r¢k`”å†A“²'Ý+-‹±ÒÂ+M‰²ˆ˜“ޏ¾Œ“æ†Âø ”劌ÊDù``P n5Ê"8i!¨,ÆI C9cÀ«PR z ÊßåΘ½é(&Ak(kÒ€’`-2£¤ŒwNÄ¥G9c¢¸a¢„ ˜,F”+Ö*1ÑÂÜš+&Q:cJ£Òé_ú¾ cIú¢L(WL¬L”&W˜ˆËkD¹a} n-°Ìå˜pz”3ã˜;{^WL>5ñBÿýßèËþ+z²¿û±ÿ¹ý˜ŒÍ‘E¯¨åÆx‘™( VÊ•Ö0T&J†«aÁK¹»F”[]NÙå†Á‚Ê*hÏÛU1Þ®x* ¥`Ìr »eaÌrôœWî†Á‚Ê¢Ì]ÆÙ•`"¨Qé()ã•‹ÙR¬sZ”#&‰ÊŒrcŒÝt”¼¼À*—`â¨Q” H‹rÄ$òC™QRL&ˆSN™”SΙ ”åTUàëšQn˜lÁ¨L”&%ÁÄS¡,(&`ʉñu ( &£eF¹c½ a‹%¹ˆ ©BYr°Ê嘴a(GL\?”åŽ ¬aIìƒ2 $˜ÌjT&JŽIÆÛe@936¤劉®D™Pn˜ð!¨t’&¿/ʈrÃ" ±v5(G,¾(#Ê ƒ/Ê(bDf¡äX(´¬XÈQlŽ#úïÿ•µìÿŸu“ÖʶNÒú˜³6þïžïþ£zÈk!ïÏxí£÷\\ïhûgkœ¸¦Ñïn ÷ƒ'8—À÷Á ÒçXâjT&JŽ¥G9aPù¢ŒŒŒÊ¢µ ƒÌX@àˆ‡ ²P 8Cö­Œ±o³P BÊ•¾‡ÊDI1 µ(' J%ʈrÅà Fe9 qÊ•qoÓQr Ø0ú~1­ 厫A9bðú1v¸;=ËeìƒÒ£œ1 U¨t”[‹rÂàöC™Pîä”#º/Ê„rÀAeÑu&¾åŠÁ¯Áà÷Áà×£$˜jT:J†‰ E9a2(Qfú1}‡“‘¾“‡2Ò÷‡19,(ã‚+0I ( &ŠeB¹cÂhXÒø¢Lô½LžL”HrÆ$R¢Ü0‘Ôô·ôw˜PYôúû L*7Lª`TVKSk1dÍ(wÆûΤgô¼%i'pi©{NðpÖðïXoþ+{µ¿û´ÿÞ>-gæÃþ÷40}Q&ThJAjD¹a £²Pr¬cZÔÿÇÞy@7u¬ûÞtAQ(Á@ÑE]4èÂ4ÑZL€`º(!Nh¢ l@„æš(ÓM“DÆÈ¦ÉX¶e›"ºÁ˜þþ[{F’NyçÞûÖyï…µ~Ë+gÝ;#íýý¿ýÍìÑ÷— pÀ( ‰^Þ±ØÁld¬@†ÀÖ'P"À,ÈØ·Á®™ÌÇ;Hü|¼•€‘‰ Ø€b07P3o)„\@ Ä)D\@ ±€[ÈmeDÿn9„c™ ’B@áÀÔR´Ÿo·³‚èÛm™èÛmX8p%„²A(grˆN2â³€@P l@!ê€ ¨ H#¿- ƒ8õ 3—wwÄjR6¸€ŠywK„ß–CÄ B f @ÐZ`2[ l@ë€(!t#p5$½Øâ7€L AˆR$-°ƒ@$-°CK! Fø+œKB‡-µÁBò?¢>ûŸ®ÍTÿ½ëØ'7 ¹ðS5ÙßËwBžû¯Ö^B¾Ê«þÑzRø¬6áú 9ŠG(°9‚IJA‚Å 0Z` N ž0`J‘‘R°J2edÁl@!œjZ D°é€(tQ,ð€ (€à!D‹p.Áè*d4"(èœÑ§¸Aˆ“@ 58  ¤Z-p%‚×È8 Øl*s4 èP`rá÷À 4òˆ@×—°—/œA°Û…ó¾øLa½(¬ðN BÐG _+œÖƒ~+A: „ Â﯄ß^11„ ¿¹‚ ”DE Dá~Ÿ*ÔUF¶°¿qØ„3¹B-dÈZàJä‰("þ·ý·‘ˆ¿»Ò±ÿ{;ïáfç=bˆøÛ-^»ñßnñ³¿ZöûW¥ßo·T~ëF;GËÏ gOø¿Ýbw„Õé,Õö•©Á™õÎy}ä*7*mi•’æí“órжê§Ö&QÞ?^ìWÑÊÛ/£ó—eú=ëh¢¼Á­æUa­#©¿Ÿ[(æy9ôDÏÚ]Ï‘¿=œ—FFvýÚ‘¶5‰ž‹›”§B¡öDùòÑ‘®ÝLT»sÁ$E$å}¸ï/Âûåè†ñë^Ü#í7ì)ºcsøªÃid”*0i{L-qÝñrDÅ`2,Yváñµ×´]î¸××O’ûˆýNzûEsV¿ #æÙ¤'…M:GÃØukYþÅÏMwL¢MJßw¬~7²¼Ã¯á…ÂM´úû%+4Ž Üw‡÷}}".{ûJqÿQÿ¾ãVÌ#öÏ9Gbâ˾.I#«k—žo¹˜DÏèÇ~]Ód6lRøºÁDS<¸­pÍéõ羉þþ#.ŒŸv¡ÒŽù¿œ#Òæ‡lSÓˆ§=ìÕ$Ú±b”ºØŒ®¬o¯‰6Î(õ&´Lå}»x_Ñ¿!ËÛÕ¿ŸyÀ™,Uéý›ù·#«fùÓù,;+ÿÃ$º±ñX|I5ù)ë]Æj¢¿W}ðk|³ºw¤`xRÙÛwˆ÷æý1ü}yd?êôÜïSOœ#Ý«/«J#£Ÿo}ó"‰V¬f}³¨d{2´É꘦q&Ú°eÄõ:ÞÎçÆûÁñþ!¼ï–æ©]ÿõÆw×Α7íõØ[3ìøhÙ½I´àêqÏZÔ ")åŸþㄉŠþñ¾ûÀ¿‡V³_6÷üïýæýüû4…bž.†/57%6BÕ‚N™3Þ±¯L îß×Q`h#2ÇP'4¯ÙD=ísÔ\÷¼ýƒröƒgýe0®{Ü›ƒkÚÈÂë·n¶.”FJvÞùÛ‰ƒŠ}„«q†Î/6yûÖðû+~ÞXïõçzû¯¼ñŒoÄø== ól¤Q¯§;Ö½t’j¡—Þ+ì b¿Á/Ȭë}¶žvšè²æ—kÆúòÏ7Ü„Ç)ïã’ÃóŒ´äÙX±£ÜÉ8VpÆ')uv×îqEô§y‹^¼«ø2hÇ ½iØC­±¨ÚÝ'“#)ï'ů;¿ÿþ>.Œ;ÿ³Ÿò7ëe#ÜÏoÌMuÝ.ÆåýjzÉ?ûöÛ,=º±·aã__Þ‡ÇÛט}~î;íßW7 6KÑ«mÌÎ!629eû×+Î:ÉmC{·ýÌAK¾1>;¨Ûñ|­FÝ4ÑŸUºÿ~–¯¿>ü>p_QîOáÑÆWÌ[÷êãD™sWRÆvÐIÒ“]gøÜAóì~s“©è'ùÕ ´êÊ}Û|uÕœ1ÇŒ?ñ'4Þï’ûfrå~!˜g–ÇøÑÆúÐ:IÍ— ö·,á =FêR{JcÒ·a@Ÿ‰·MôÃw‚“t„·¯&ïsÌã—ûËð¸ò÷SÑaž¹ezÇÄ¥ÙHÓ©‹î™í$µŸ¿ÝR²Œƒ~,\¯Ÿ#´ñØW¼7Ñ—î›%;"þÔOŸ÷¯äù–÷¡öï»nÄ<®.7Û̲‘ùA!q Æ;ÉáÔ cšÊôÝO…ó» ´"‘ãN¼.j¦z ©}×ÏÇûòïÃûíúÇ™ón¦7 Ä“ ®ÎŸ­éë$kÞMZ_ÃA“R£¦‡”lIú}9¾áÎÏÍTæ¹ñ¾þ’¼o÷Qçq ~Ÿº„÷­÷èó¬»YpúÌÀx²Ö8¥­“Zô¡ì€zZiK·Šç/4%=ûþ‹CyÍ´z¿ŽU6ö÷Å3÷-ãýü®W˜§›^¸ Ó{Q/gQ/ Y|§v<™PûDÓrµä³RWì/9èɲfE×6$öa+G)f¦ûRÁ×7˜Ç™×½Ïó¯nh²Ç–oEŽŒr©ÇÄ:¢n0Ïúg3&îŽ'ƒòº{ì(î$û÷¼1WW:hã^ÁþP×%!ç×ÿ-Ti¦ïAº¼f€7^_ŒÎˆ8óG¦7Ï,:œ]2àvsÂ}><ºÁ<-×é8xL<Ézß±åøìT⎯ð®bKå}™oeÖº6x¤™v7Ín©ÙÎûä¾â×û1ó< ö%ìþ7uƒyn¶­pjÉÊxr£¬³Ö©[©¤š<­CÖú¨í‰ZCÍeHÜëµ½ÎìíOÍë<ñ>hbžhAxßHn0ØW1žÄNÿ)ÏËÄTbÝ0âB‰ ä ‘Ù>¯°m†™Jò¼.^Ké­ƒx\‹ýòz}}D?´F„ǵG7˜gã½;ª»âɺèߎ”:JSè?½ßuÖÚÑW*¡)gÀDóŸü ¸Ÿ¾»gŽöâŽá äoWD4ˆI%§>Ÿþ¶f{Ý\'µ©l}ò|ßÝUYãÌ´˜ñÆ« C|>#9û_º½õé³´JçZ5û̶õ‚yšvÕÈÅIi™ë6¦’¢1 Ùƒ4Av%!ðgBRK sÍÞþi¹û1óçïxuÎoGuk¾!b÷®¢nâ²T‹ÜõçÄuM –‡-úôþ%•ô)¢U,îã +%>Ý¿³=yÝýÂÏ VšiòªË¿·²øê`Þ/œ×…#_í:~8)}<³¶ºQ?Q/?`ùÌ·-´ $hÍŽÎˆ´@£m¡ºià멃6¨Éó· E·šiûùïÝšñ'?Õ†¶îüÂëc0¨eÏâÅG!uMþµ@ú`Q/˜'º·Ð ;L+¹·ÓÍŸS‰*rLêqVv!- /w™é÷%BC>L÷õUãëž'yßJÕ™µM¿AžßÓîyÓ_Ô æûæâûHi…q©„öø¬äÈiú¹êCÝAûƒÉûâÏR^›ÍôÞîÍ«GDx×%|>Þ'ޱOeI kno7®‹¨ÌÓt÷Ž:EŽ%k‰'ÕJ"Ä,½½ÐAËu[4«QdGR±Û‹áÍ4b{…QÚ”¯KD}_ñö?ÝQ_è8×’|üY¸°b~1bü±¿T|V;%nšŽv©däòfEüâ sgÌÿªÊ ¶Dôõ1ÓÆÅ^‹iAy}ÅëÞOÇ÷âõG/˜GúÀ¼/ßý³¬ûà×uRÉø{YKÿê`ýD[ÑOÎLWͪ»oî?_wðzƒ÷Cåë _RÆ/0*nóƒG $îäðÛ§¾J%Žï Ù„¼ïñåjFn÷Ze²Ä™éþÆî-e_úâ—ëçûOõ 8—¥r´ß¸¬á­’~«ìÆø)d{ó ÎÕÛ4ßÙ´“Ë+Dº{É~3]\PWk¼/o‰ýÍÞ¼Èï¿ß„ ã[.<8åL i¾­zèn Ù •4Þé _|Þß6¨&‰hzîH7‹™^é{ôØ¥¾z˜¯Gx?MÞ¯ÓßL…ñW6žÕuLRiEÛÞKH!+6Miôx·ƒ¶Ù­^}íZURîø¾„ù f*Ƴ/¿/788ïOYA¼/:÷ýñ¿¿¡ß³œJM ë¶].fÙ›BÞ´8:pË>}5÷ü”ײjD肯¸b¦ùmöýsƒ#½ëI³K=»_ÍåS±«ÈÖ½¢Ï¢ã÷ûìQƒåWˆ§Ë·!…D–2ô|}ÀAÉìS³òQ×{{x Y|ŸŸ_oîËàõíõø~—ÏÙ—óˆýþ˜m ™QÍPü˜ õöúyuÕ'Ý‹6Þ8ºî[Ÿ& ó­Óy¾âqÄ÷øþ†_P+æñ¬zí d§|Ȉq½SH‘ÞÁ'mGôÜêÅ ­;ëçÆØûÝcÌôÝ»W®Ã]|yŠçwî/’{ý“c½ŽyÆÝ38ÚÆ'”¼ÓºnmœBÊ.h|ÙDôÅÔUÓ?kJÒûª_w>l¦EoFè‘×çoÇ})y=÷)_é[–ê—=õ¶çM ÷Ï.Ú7µd ‰0¦¤5‰sP¡køÕš­Éº÷³'eœDÝ8$µ¾³h„·?W®‹íªïG&¶,å]ytñÃ~^´¿+M KÖÍoýØL:Yêò\rPëC•¶¶ räÍþÎ3c}ùç%±ïnš÷óó}‚þ _¬HäÉ9‡%&eÌ÷{˧8hhxµøÉ3é’ìJÍ´œ%ß ["(_wð<(Ö#¾~œ’}ƒÓµ!µXÝSQÔæ‰M„›@L'……z29q¿\W×Ôá ?ýGÍtתÂÔqÙ·¯Áã8w_qñ{ÊIú½¸?æU« êót¶‘¿âz2™ìQ?[­¹ç iи·ŠŒ/ùÍ9æ9šVQn~ÌŸ³¹ög2¼÷…××þ¾õFÌÓjcžß;ÈO÷îÆ¼]šLŽÎJ\\ò©ƒÞµ•<±~iry¶° 4SkxÁ¢ïKøú7ó¾Íü¾p½sÿA>0~ñÓ[–TÁs雫³†·˜’LÖxµúâ+m0-mjzÙ6ÞçRÑqMem}}—y=ÂuÁýZý÷]ÿRÏNç’m dR©ˆ%û%“­¥¿íLþyµl~¦Šð|p3mž4ïqa¾ñsöÓõÖo<ßú÷yˆÏR<Ô{ò‘S Äc'Ö,™œ§Ï¤• 'Sa©²-©r{l‹¼º{é×>ô­¯y½ÈûŒó|ò©ýæANv7<“@žÆ²Édzßð›%’é“{J¬¹ÝùV˜©ûÚ½%+NûÖoÜÿ+·9ÿ`üŠ]/ëëu#;½Q—¼Édü^Í€ñ“iù5NMèB öò»?¢n쇾ïÁÿŠ~½™Þõ;÷ÿòßW Å<7j– =Þ5ÍXö&ËAŠÝ:²çv½dÚpãw+ökÈùìWeJ³Ð:£nžçïxëÿýÆ ®Q¼k“ƒ dzÒiyRäVmm˜®M2=Vöá©'ó{“Ù2Õ‚¥å-tcyíàU|ëA?;§Ù1®;ÑwüQÚ²·…Þý\0öùð|ʯ ¿îþõž ãïÒû›’ $¿ö`ø~…ƒdXËG÷ÞLO~±»Çõ±j25z[Õ,­…~Ûæ×ýa-"þÔœ¯WøuÉáëŒñ»»»%,”@º,ŸŽ ÀA®Ä}9æÙŽdú¦h‰¢½Üíɤ3O· œj¡¤÷¾ùã{ï+þòý[®+þ|óÿŠyÖY8ïéûx²ñAµ¸†ï“HÕ/6^Î8˜L ´yûJÛväpŸëKµ,ôûnuÜ¥®-öæ!þ\àzâ:àõ†ÿõÒaž¢‹ïå©ó{(úvи³ø|9øõãëÕq%Ú¬[X…pß^0¾¹}=å\O:o^Ó³Ž9‰¼]Lz!1™Î}rõl‡Hºò»ºA,4Ü]¢ý©¯Îç¹ïÏÛbÈ}¡*‹ºÀ<眅ëŸÚOn>Ù}mê¦$ÒxâÐYW“iþ§¿vìü¡#9±lKó¿›iô2kÅ™|ºæ×‰“[ èÔ·™:æoŸŸ{^SÔÇù,•ÊÓø?žX Ï—(ç%‘ï{Þ¹)3™^³¯\?SÓ‰XàÔm3½ª]ÄØû¾ûÁÿŠû‘ϼëîõµn4*¾©Ë5D`žš+çïÛ÷C<·;¶UíQIDi)S¢Ð‹dZ}Wå«Ãût&o[flivÕL•éÑ”ûèðýþ<÷+2¾Ú$Â5«vÚþJ¢^0ÏS*ƒ‡Ä¡*ÎN"[ó‡åySèwÅj$ßÖ•éüû fº;ïáÀV]}û¡\ÜW—ïëðñsø]až?®¶í_YO”žÄšDÆõî³ùËšR6øq‡CÝHïÆÍÍÔÓnÿÕbšÓgÇìÍçܿٿ^Öaü<‹—W›\'žT›²&ö›/“È»eÛU§d)ô`—}r݉n$¶€±çÑ?Ìtaôé7/.öÆ/¿/Ü÷‚넯cü×cF̳4OZÖµŠñÄcç“}…t]4¸µ"…v7)OwÒÖ³¡o¦òõjEò…ÅÞuß/àºóZYQwyáì&¿ä'[·Û*ݺwy÷浌 Z¡m—¢U&w! .ݘ×6¿…Nø}à”ŽÅÞ¼›³>{ì­7ø{ßræ)¾â†llѯfë¤+¤ÕEË·tI¡;,_]YÖ™˜§é±uÍ–Å“‚‰¾ü(îü‘ë¹Êöo/d© ›†Ze±‘UEÇÔŠ8}…Ô/S>2ã¶¹ælÞ‰¬±é~e¦†Ô ¼ç>²9úîcüÚÝ‚Û˜×ØÈá[åŠlÜs…èï”íÛjx ó[ïÈê73­Ò MѧS^ñ¸üI°Õ’ˆ:Va5ςĸç~Ûµr¼/Öa|IåˆÆÏŠÙH5ÔtúáWȉ¢Ó²ŠêS襎ÙòqÇ‚I›ïŽMÓ=ñ­¹ nòýÑÏŒÛòìÎ6QçH σù é¼§ÍŽB«Sh£üu_.ëAâ'èNŠx}dSM_þ䟟ï¿*õ§†-Ê ÚÚHñûêÕ½ëlO¼cå¼ZM¦Ï‘¼Ç ‡ÊHt¿o—ŠN¡×nÕ>_éy/râvÉòñwÍÞçfN«ìýÞƒ Ö‚ÝPB•õ ã7ò|ç¥ÑçÈàÅ»NÖ¾B’L.?‘B7KºWY°±TY÷ÕñLßõï¸úÂþã»*¾®áù ÛþCëO‚’%ﻞ_)Gp1K•šñ}­Ø*ç˜ÞòC#õ÷¿&§ÐVç2~ S…v­gäÿàFžî^äy…‘‹½ûÿb~©êõÙãõ÷ ¾.ô¯¯e˜gĤÑ#£ÒãÈì¶Â€‰äùMZD>I¡õêÄwQþ†zºkÒ&<×î>–l/P1ÛO¬BøºWÜ=Ôíæº±CϽ~¯üý‰G˜Gâryà–Æõ¸Hnt¯|šJRizöàÊrÛ7$ÿ€£sÆaÝ>sg‘¢±Qþ=¸.?»ænVÃwÛk³ó¢ïT(Æ#½Ú/èGÝ„;“H¶}ópt«Ê©ôIƒNzêON6JßYõ€™Š~‹èŽnë®>ÕT%ü=+ÏÏâ}xãõ¹ó÷Õažüžƒq¤‰Çð&‘xìÊ•©tzZâŠ%ßõ!w“›¾ÏLwJ—ÅžOZÄÞ_W$ü>óüqõ£éÚÑžìý7ÆUH…°³ääÂ.K[þ–HÚ÷+ÞøT—TzmDÙèûÊ^dÄ„š÷#N˜i%RîÈøàE”¯žÿPæCRѪÞ<-æ7q½iŸWÞ5ØÓmØYRÝóB'‘(Z&‰œJ{UüeLŸgÒ¿ÈìA·°.ï4yøÝ5§ô”?Ÿ†“ˆßÔ'+{÷ãü÷y\·ðó/åË%…—„Þ~óc")¼5vïãq©´Ôò¡¥& ïNŽ|¼±y–™~_OûÓî2‹¼ûo<Ïñ:˜ûpúÇeÀ¥,UÔË‹ï“oÇE݇+®M$SfÞ]×hF*ÌÞ1ÿñœ®d’çEš™¾¿ÖrtWý"ï>4×ÏÏÜ7–¯Ÿ=qñ÷GëQáÅ’“¯…>‘˜ ¬Êûu$®‹`÷léFÓ·UÍÔóŸåÓœû´Ù>è[ïs]Üg­@xá‰{ÌÓwKÿ|+Ä’·,{­S%’7?ÛÓqm*2nÚĨîdx«àj­6™éïC7¼¹f±w”×!ü¹Èëyþ|÷ŸŠyžž{¼¦G¿XòØróǤº‰$c, ¼]©tcÓe¿؃t^K-5Óɵç%‡à9Æï?¯”Óõ«ë}ÆOl³F›Y7–¼YðÙìò‰ä+ËͯËK¥¦Ü˜/íIv¦ÖÍ;l†™&æ«8·ÔöÅÞ÷<¿òs¹ÏÉä8…y¤«÷oZûþ é`|Shùç‰D¤ßÛ÷B*uƸI»Î½ÈÓ„zƒ&›)’úY×ÐÅÞzžçO¾ϯß¿ôß¿·bž¬)²¬Égˆ¡îùÔÙv2¬ñÍnãn¤Òo M½®ïMÄ÷fZYYïö¶‚¾û¯ ÷©çuÿ¾9ü‰0pÈL}†L{#ywó¾4Ï4÷ þ#• î̓¾îKVxÚ,}¢™fo-QxÛ¯>ðçŽø}{ãL\Ç¿ âõ¾G/—³T•dß¶-°ð q´k|t®ÓNF?^iI'ýùa§ŸÖ…ô#Õw®SóG3ýîE§À‚uy¯÷kâïkxœûûRË0¾¸ŸŠï±9ëô—ì¤í€aö•œ´A‡#gÞìKÎ)Ùîÿ…Ók|Bï½^ü=¿|¿×¿~TaüØCžì«p†ôùpøø¤vrpïÔ#u›:éÔoçÝhчL;0}ð3]å(Vy¬žæ\'ä÷žáºççáxüyt‚yª ÇÄ2N•f|Æ«h;é+¼Àé줯ƼKê¿©©šr«w@¤™F¼{S©iˆï{ðùÄ|èöÞw¾_Êß³xô‚yf½m¡½¤?M†¼Ðýd[i'Rí¥#:i=Í«mKßhÈé’w¯dÏ7Ó’ï®Ôé½ë†q#wÜõ6w/Æï×9üÌzË×ûT§Ù{x;YÙa×ÖoÇ:éö䬉-ªjH§–㳯M1Sʼn÷GÂlzï¾.¿^âûÆgÞý1ª>ŸG'˜Gñ¼ü»Ø<§IÓ„7c§ØÉ‚è‘÷ÆOwÒ¸Cº²’b1tÒVa¦žËzUÏâ·ª·þãû¢ü÷÷ƒwaü‰ž…ð)ò~~…?·“"U 5ÍŽtÒ×oiŸ§'y½a¹dI¨™v½1F²ô¼Þ»Näzçëwþ<÷*çô_´g©ª7­˜º`ñ)’ÿü— #í¤F±ÕçopÒ–Ëò¨{‘Úã&.¹<ÜLjÚâþv½÷ý?^ñº“×å¼nÈqžóäk›uó|ûSdæ9·óòwv’ݳ(–N*¾/ïMJ¯;u£þ÷fZ3«Y‰¤ùzï9®w^òûÏë¶ÏÌÓZ.ïPû••¤­Î3²æ7vÒmÛˆkâœÔPuQÉ#ýÈàÑófnb¦ÒËŇçï£÷®ëxþâï[øúQü¾åˆ¿/a(æ)ÑnRÉ»¬$aÇú¡£»ØIÓ3‚ßpÒB›/Õ ·ý²궈îHe ½×Ç3?ÉëEÿç°ã˳é…{„•Ì2«øÆ–vbzòxÀ['Ýਠ²$¿†*SíÌtIákúŒ[è½ÿü/÷%äûÉŸÚß7bi¥½ëN4²’=éM*íi€ï1#ñ§Ì¯Òè³Q;G+’:íZ—«Ì´|¥|?M}½€æ|nX™Î3?y¾ÓŠñËžnp~C~+óS¶“Å£ÜKƒ4zúµcðƒoˆeÚò‡-Í4ùüÓÆ{¶àOþäüýÏóbÜ•ö¾¯ôèóÜ(uêc¿”¬uúå Ì£Þ:îÝ¢à4êl¸Cúí ¸nE›é¯‡víÔt¡w=ÌóŸ¯Kü׉Yª4#/%¢Ï¢hÒ­§Ÿ„¦ÑöM õšîéCzÎs<~TÛLŽüýÂù¡ ½û)|œ7c“–&º}~îbž¯œã9/Ã<â:à$ ·?:j®l'ß[¶ÝÝ=%&®’ôù(ëEnGín¯n¦í»{9µÎBïzßWþÞ(ç~õ÷]…yðÐ>ä$y¿9?RŒD··ÕŸ4?îmÐ.¨ÝŠäbáIJ™šÛùÎqØw_r×+k?ö/ßüJÍûñ¡r­2;-'ˆrŒûd­Òv¢Ø›7®ýÊ4z8#éçá]Ùù3}|¬röàÞu'×?ϯÏ+9ê/ÌóÊÀüýŠ â¶ºœ·—Ujs]zfgDµ—Á$`W•N-ò!O¾™V³UØïþ6ÿ>¢c½Ïy®wÿºÛˆylÿñÙÌãDYj‡Â$µ“)÷ëÕ{³/vºslåÑá]É®¨`r󑉾ýeI¡xã‹_w~îß—ç¤0~— ©¥å51mÚÉ•»òµO¦Ñ€G§\û­;qDI'wN6ѯõ¯øÅŒ…4ç9œTïþ&ß—ô?éÂøÞj һǘ¿$ê ƒ²ìÔKitžpì ”†¸Û¡º+UÝð},0þÅ aì6‘B56;ØIäÇg%åQéT{üÙÖ–A¢lPµF ³‰ùª®*côÊ÷½x|Š÷¡1øÙAû¥Qâ¹ÓPŒ+þ·‰$D7Œ<†Ï5hZßòÑé´Å–W©­B4$ö'ÁøÚDõ_ x¯·øy“¼ï¥ÄýÈFä‚£jx»²ÕÄøÇø/C{MŸÞÖD2&¿¼°“Œ”cwªI§#vU¿=yNOr»g­›…O@Çžmó)¯cyœˆçJ²õ…ø¾Ëˆq[Tšb¯öµ‰”îðÅôÏûÙIoYÈ›.¤Ó‘„“í}ˆøþßD'æMu­à|ïþ ÿ+Ö¼×½"ªûMµx÷A=qyæÜ¿~¦Ú¹Ãd™rx¾¨—õS®ÐÝJ§c‹ĬªÝŸ¬¼ÞìäË &J‡…?¹ž÷Ü4ÿÝ >s¿ -SîhZ}âÿÄ…yŽž^¼&¯ƒeզ޳cJ±-Þ¤ÓYg~ÞÒ/„,²6O=|Ÿ}æœjòã<ÊŸÿ\·WŸ¿µ¥E]ÂÏå‰sG–J÷Ù¯"EJtv ¯˜÷§øâû ¼~Ÿã5sì+è0Ï ëŽË bþ·“N8¾zL3`òòÈ{½ÈéÍ•»×À<žc¡ó(ÿœ¼Þçï;ù}ñ¯ûD³FoµȺù.õ:€zãéú[sfdЩOê(Êfjȳbî­óÏ›è?œûnmWy¼çqùýëÁÚ9î‡ãߎx^©üÒãAèûgì¤s/}SÓ’ º÷Pƒr…Óº“¹c„&:°ú™gÓ ú®“X¤yãJ\ÏÔÉyã²K±Øö“qÁû.ÚÉ´ê~˜·>ƒF•Yï|ÃäZáp&Z~ê­^ÎõŽÏÇáqu¿× ¾/7 |ýíÑGr–ê®»sâ]÷“ƒæ•ÏÏ^·“q‹ò˜WìÉ ¿¾zÒa´†¬ý!îÕÙßLtéyËŽÙ÷æR®k~Åý¢kA|ß\œ¿×yUE}`ž}?Î:ÞO ‰êZ*2ø¡8 ; 5;™AçV}_h³²'yU¡^ó'+MT¨v2ëûî?wÃï÷Õ*ûlec"ž“ó¬ ã¿*ø-†Ž!ù[¬hÚ÷•è[­è4ûrmÖ}_óÝ¥z‘13…7s&š.£›6ïOû¸<‹ã6!ü}¤G¿„ç@Ò>R§ØÈ™M %’Aw Ï[+ƒž¸Q´ØÚq½Ioá8ëH=³ÎˆŠë;5H|¯w3¨#ª´G4cÏiö|À¸‹ïÔž½Ò¹‡Ì*å:v±\"ùacÝ®—4Èu~醱}È»Þ;Û§ 4ÑN­Ò.FÎôÅOîs†b½­$¥Þoú[Tñº1~˛Ք/fï&ƒ]4­N"IjòD^ó³«tïÐÚE7=íMJ Û\ýMôa¾ø– Σü|2¿ŸÂiõf£Zþ>Æ÷wËžàÊoe»È]¡‚Ò D2¿Ý„gî Wé–~­ Œ ½‰#Eúõ }W¶ómyÞç=ÿü¹ÏÓ oë-®—ãùéÂ<‰ï)Í;Ƚ©õ_Å÷L$ì¶êJ£«ôËÅGMUö%çŒ%Úl¢…_G|è‹O±ÞºäÍs\¿þ×' %KUêö»nòöÑdlõc'K$Ç+ûê«ôε¬dp_R-äëKïF›¨±Tà£És){ñº?ò~~¾—ã÷zÿd©E…Ggo#±FdŒ“H>»ª-¶dðU*üŠråºÞDWküÕïfšhK¥smà/siîz‘×ïü¼°'Þ1î“É-&6ßF&»*t"®Ëƒ‘ÆLíUÚmxDçªS{‘7§†…Dšè3AÞMæzŸ—ü¾òs:9ÞS`ÜòÔo4våodúÓ ö!3‰ªñó;¿Î¹J¿®Ué`±ÍBlBo-3Ñ1íÅꕟKEÝ8‚øýä¿Ûö_·ê0îw¶QæVŸ_ˆˆD2à]žÑg¹Jû"ïš±7»“ÉÆ…éMñy+|5ºÛó¥s½úã1!ˆ×ŸüùÎ×…ž8®s·Y/«tØJÂ*ÖΧO$•÷Dnzµé*­þÕúÄ·cº“‹äMõnsM‹Ökw™÷§sY|Œ¯;ÄëU)ÇzÊy&×yº³Ð…-„”_V&:2‘¬t…U>·ÿ*]7&jÓ³ör~¸rÃeæÙœX>öÒ<ï>¾x}Nñû*ÎëûÝù_¾õÎÔüçôdâ½NØ÷öïqò©þr.æ Í‚–÷—û{=N¢üzœØ2—ï¡ÏLÿ~L‚ïƒù¾ò~LÏ÷•÷cRøù……æò}àža¼of˜Ÿ_Χú—‡³¾™*Ö7“÷dâ}3yÿòæ{(©(ö=zÌñ>Á™2Ÿo5÷ËÉd¾‡Qÿ ÷‰šùåæêÉÍ|Å´¬_0÷~0Ïjîyhôë]nËջܿSîž™ÜVðSåò;´2¿Ãpæw˜»o9ï—żÅB˜·X óz«¨Zúú«ø÷æ=VäA¢Wµ+Hôªþ+Wþ•+uÿy¹R¾—]¸Nãßé•É|rb˜Oï¥Dð¬N bý $f-pU.o1ÿÞuæ§hg½Ó£˜§"÷ÈÎí«ü¹G¶ÒÏ#',W/uî‘Ã{×iý¼>ÕcXÇz× =†cüü²yÿ:Þc8”y‹I!,mE±G”ÐSèå–ù9¼W'ï­.eþ°ÜW,Ú¯¿°=Waíܽëý|rÔ¹<ÅlÌSLÇ<År÷æ}ëŒLü¡Ì#GÆBÞʳ„|•;Wñ¾uþùH¸—þ=4s÷9gÞ†ÿbßL ónu2.¡„õ¡³%‚ÈȲaÌ›ZÎ<©£þ¥XȧZ™Ë»Pð v±žs¼×ï§úüú÷žs5ë;'ô÷UUû`r¿Y%ѳի¿¯àµeÿDÌ@æß*ø2h7…Þ¾À%÷ùÃh™?ŒŠy·ro­Üý}ÈQì/ô “#àõ@ͼ/˜pæ¥Å<´ÿ,Þã—÷¢ c^ÑÜ'ÚÂzX ý+Ÿîêû«>ú«> øÏ¬Ùçv ÷Á $Ð0` jÔ'z•‡'P!x£\@@ŽRs8pu./,P!À£™ßt8ó›V1o!àCÿއ …yrïi•Ÿ×Œ6W¯rî5c¬·/÷`øTo_ÁƒÁ 4Åχ:hüúû†1/¬@+8+Š=4£@¶Ìç½Ê=²™V4^(°9hz—W½W­¬¿¯yTsAÁw†÷Öä½Ì™ï*÷ÁŠñëíëÌÕÛ×ߣ:Š 9X™ žùÑhry`Ù™–žy`åîëë*æ!(a‚6æ!h`½35HÑ@ÒÊ׫Sî×;S™«ÿ¦ áß?Ë•‚)Á¤. BPDèÍ˼žý}÷g¡'¯àƒ` üso^‹°þBNÉ_ D†ûõå|¬{îç"xÛ˜wïÅ+øVq{7a=ÂeÌ‹TðAA[„=)q&óÑãþ-áÌ¿EÍ+óâã^ËjæçA„çê-Î=]¬@‘èü<¢@6…hl@Á<²Adõó[v3¿åÈ|¯*Á£^\@)õ‚ȸg(÷A'xU õ/ Ø€ŒÙÕDÏPÁAÁüù¿eîÏ'‡0õÀÍz[˜?½ÞϧJð@„`uÌA áÆi.¯e#@ÈaÀÆ<û„å7½¿G•“yT˜G•b·)\@Í<ü¤ÌÃÏÎ<ü„ãÙ-D_zÞc<Œõú”@&ë1.ø„ ¿(|Bù» ¿råÿÙ\ùWžüûyR¸Ÿ‚¸A\@…à4² c^~2æåçb^~‚WŒA\@à`puaŸ'½¿ïU "°Ã ¨àÑ@ʼüì@`77Ð è-@†À׃L †¢"g¾Ê¹ýü¸¯²d5$Bn6 ‡X „B4V ƒpôÀ B AD:àöƘ÷•à_*øÒG3aq¿Á—>ÊÏ“ÁÌ Kð¥7@tÙš³9ó0Íü•!B+Aˆzà–‹^~Ü'FÇ|b4ÌÃTðÅ|\@ ¡Æ)Äœ@ÑF)nt°lnÂü”eÌÞÍüèÌ+Kð7¸8 B<³ öp楬ü„—² ¸üüa?ÁÆ$B^ ýè¹ïé_õä_õ¤.à?/O*Ù¼™ÂõEpZ@ 48 Í‚5Ø€ A«™@ƒàµXÜ l2³¾°ÏƒÞßSKðtÖøy:ë™§³†ùÔH™§3÷©ñ÷´}ÂPð©‘A:àj#ÆÏ«Æä‰dÄbd‚ v „p¢˜xBsùµe Ñ?0ÀÏ?Pa@¶à¯Z™ÿ¼ÁÏSË d¬d „k¹¼Ÿ£´èÿlg^‚Q‚çMCÑ{ÞßOËÅü´¢˜ŸVÄn¼d ‚ȪÕ}ÿ¹¹íÿ‡ºOÍÆu ×Ái2¨¸€$Ö0`r­¸A‚× ä`È¡d+#˜ …}~÷6 G`˜/tˆŸ/´ùB‡ Ø-ÌŸPð…ve.Bû'ü ­@AèA&Ð@qè˜7´"17PC,Ñ@Áh¨ #@ü«fü¯åÅöÿ—-\¦ Èœz 4R "PµÀ Ø( B¸6 @ðF±6 @ G±`v @PG±Àõó³ŽÙ n2æií*½HøZàJ dƒPÁƒ¸ADa2CÏ|­•HÈ%H!–pàjˆ&H!m.k ó¸¶Ĥ™@ QEl ‘A—d D!4-°§. †ðb€â . †c€BÔ'PAF (À(!N#@ aÀjÈ¡¬ È닞ØÙÌÛâç‹í*9H f-pDmn †¸£™ÀÀÈ!tÈ¡¼ È™'µàm­†øc€ @ l@ŽD ™@„Í’BˆÂ/ɋ¿Ü9òÿÅÜ(äDUÀ¿ž…\(äÁÿäzðÿ†œ'\«p?dàÞ ÐtÀ ”8#ÈÞ ÇY9βA(‚ÑHÈ¡LP 8£@6EÚ€jÙBÎCÀZ\8 2Á $à0`J²‘s(°9‚Ú²A‚Ûdp=Ⱥ‘{¨p¯BÀG)‚^ œ@‰à7  v „²…³5Âo³ †@®p5D!Œpà~qD)œ@¡D)Äœ@ Ñ v €€¢˜ˆÂ€( ¦(-¬!*+CXà!˜Uxç!œj-H 6-°%Dgá]0°)¨6 €£€„@V Î7Ð@œ1@ †'P g ¨‘»b€¹K l¹Bˆ88 ùËHÄs5:ö[VE®óÇ1DÌ‘Jv6ÇÍ~ãÅÏæhÙÙ¥ßf[O ïQìD\›ëÙoC„3üßVâ_WJ–ê+¡-å°-Þ~'OÃÿDo—tÚ¿LÃ3W)ï7»7byÆÝ/{yý]ji½:ÛDyÿΘŠßÍ¿Â×ÇÈ3Qj–JY2h`¡B[HÑí.6ÛœHº•ºŸopÊU:áÔž¯•ïKŽv:™hÛ"ÚîuÌõöóá};ľxûjðþ{þýýd˜§ÄÑ.·vë7“™ï]´/‘Ìþp?ù·¬«TZ¡ø° ³ýH‡aŸŽ]f¢úï7 ê-›ëí'Ãç3¡¿GPkÓë&2·ôŒ£â‰ºÔèß›}¬2g¢öAiuòÄû-8Òý*"mŽ·ÿïGÊûyð¾”þþˆ:Œ¿áÊ©7dá&Ö·1‘Ô”=ž¥kr^_eyPKÙŸ´¼ýöV¯U&z/¨yi_ÿ Þߌ÷'jcÉ[äÅ•rôÅ3b|¡‹]iù&2F°õ¹H¬;žÞå Ü5©cóÖ}È¥¸à 1ц¹Þ4w_£AÜÇÇß?ÒŠqÅ~]Fò8uý“VÏI¡mwîrûžÊ7äTO2GùE ÅzôÕËCg¾õõMá}HÇõhRqúÅWÞ¾AÜŸ†ûSytyîü/öÎ,ªkûÛ¨¨ˆ16ìcÇ> Ö5ހШ+6@Ac ‚Š Š}ìÛX±nìØiÊЇ¢¢XÐD%Æò­=gïá@̽7É-É÷ç<ù=†ÙçÌ9k­½v™õ^ªÓuáN˜l~Ä@ïð@«¦Þ©$rÞîÞó— ‚Ð©1%OÑ’«=-aõ¼¥¿ªÿÍë´ñ:d_»O&º\™E÷7J÷Ðñå†o~‰ÜŠ×³¤’‡’fb`u°´¤á uG7Ï/ð ~¿8§Ž×Sã¼Aq½3 žgã) JÙÃr¯>R–Ž…ä‹}>½]™J¶õ™ÒûÐG˜qsÆ?–Ъ•—\—ëµñ:%ÂçøÉX?×CódxË]’ìÇnó%7NÏ© ]LºzÆlL%å-Ï-O8À.‹ò}Zbv÷è¼–ßÜ7^ÿ„ó¢Ä|G¶kÀÔÛ½i™rËXØò“s…Ü]©$þ”TÓ®…\\®ÚŠ÷iÅèÞÇS–ï÷o^'†×¿xÓ q‰TxïÊ–¹µC·Áƒy-tm ~]Ê?!‡SI ¿{Üù¬^`(«·MK¾ŸI‰0KŒöÅÏ'<ÿ›F S¿Àö Õí"·‚sxªizûX¨b­ê?ât*9ýA]·itoÐ{Þ¨Hðs1;p‘n )üœK÷;¡ÞUãB÷)ÛïÑ´fÇòó¶À®‡í_Ù;ÄÂå­IŸ_I%ß}z´·{ø|Æ3J¹AK¿ªsK^­À¯9çŒÇ%ÇÒx=ƒ_`û±7æËÔí6ˉ½¡–S,TØPÕÍ$.•èkF}ãßBÎìEKÆ—&­GøŸÀ¯I0Ö/¬œe{ìÙá¦àac ¨ þ˜+ësDºàXƒP¨·jºsŒ[,Ü¿¾Ï¿iv*q-9öÛ6Ùƒaík«¼eZV—{©1®¶õSë³¶€_ý¬{÷æîgšª¿.Áö¬+¡^Y{PŠcÀÔX8µyGù…ïR‰ìÇ´ ŽáÖŠÙÕœjÉÖÙÖwýŽ-%œKÀãÅõ€=7¯Y9ö»Çvs‡Œ÷“}»|¿y^,Øí–Ô½b–F–ßÉûЬŠ3tÿ±ãœs£µ¤cè@•ÇÏvÏŸ¯Ài½ß-úC¿‡›FèuMªµ»œ¼”õ xž#ò÷-LÛm×~µ&(Ží½=¸Cí4òÍ­gz´u©®brõ Zâ×#GÝß¾ ÎÀýJêÆï¯³/®»¬Âö…¸¹nO¢ ÓX8”iSåxë4r±™Ä®âVg4rÍ0ÉwZ²fíóÊS–ÿº^¯ÏïWáºv—@ç1ñ^lzíûµàÑ.²tâ‰Xð?æz¾ƒ,u>¡“Y'÷Ø?$;—Û5tY‘þ!ÉÈeãù˜sI?ÇGùä«wÖÀ¿§cÖÝŠ…Q¥ŸôØêœF޵jU%q¬3<=ã´—ZKÖzÿØnÀ2R˜[ßÛm!.¶»Þ¥ó•ø+A~=_:/1vD¶¸»m|9‘ô‘<‹ ?„KË¿Àø`ºzs¯ ú‚çÌíŸÛ'Ï~<ãS'åʾ•g˜97_é×~™ñË£Xxk±ú¥dv9}òXÇAƒaÊ/ÆnAÿ½ž¾l~ôRò[<<ž‰ë7J°ý’Ül͖٠팅 GÏN^F¬óŸm®ú`0Ô,Qmûý÷G»ùî—ÿåu/‹ú±ºæÕ²³?ö*±µ–:™uüÏÓ®öøukOªÀPžÿK,”ïÝdVÉ-iä^`lš¾¼ Ì˜áêµó£]Éýkí™U/òçÌë‘ó~­AÉovõ»ë›iº2»§àxž1Ï/äïîºv©«I)«¯ž=àq4DO¸v»ïÈØôa®ËZ-©Hñ”# ž3ç@¬SÎí4lƒ3T÷úÙ±ßwŽ‚`»CÁù௩Õß­v$ÚNí4àZi}çÉâöfnPfÙÃ+›VhÉyÓÝ‚ÏÄ!žñ~& j©Æ[dƒ!ë`ŸZ){ ý¶ªá”Ý÷ûÌ…‹o=. nËŸì;.-„?Yýr^ÛáPcgIE—¥y*Îܹ?ûý³ß<Ø0~`¡û‰çéb( èÏŽÒ©qàõ¼Q‰SïÓÈåùWÖXàyæ;œ‡çI¡Øª×ýçÒrúº÷ãµàƒ Ý¥Lè°ý‘µ[\ÿy«hžLÝx»w4©–Ð#É"È·.éýáó0¸mqãlÄj-1”®_P‡ûçšòz|BÖ^zGÁ/’seÊ3¯®žßu8ÔmX\õzš©j–Nbf¶ÚðÚú+Þ¡%ôÓW™_×s!ç™´èðxÖÕ-ó ~í uöÆÁ°&kOˆƒEÉUk;§“Šû=òCÁ/nâ¶åaZònÞØí-Ò—þÊ^…~âËç[ùNÀög¯xvdñ‘±pºâ‰r~ìñÚsw@:ùÆg›÷NÍ Hë¸aW‹ýZrnÓ/ÙV²‚¸Êë8òxÇë™ |c‰àôúÓ­Þä5œ·ÜÍ’†ÆÁÎJÛÖŒH'BÚþCËõb^,Ô¡]ö+ ÷Þ¿âgaû†Ç$ˆ„}ý6\ˆƒq*ìÛ:9üTçyصÎýaœ¢îJ7|wòß°³ ßáõL9¿Ž÷ûb¾‹ÛO=Ñóã•^ÐåÝI·•Ùq0¸zMeß9éÄ9æS÷›+úCyðXKÌIüý­+—ëýóÏQ”'¹%âúì‘xžnʱ˯̄yKÛØo³ämlöû§“²^ïgî\.ö;‚,3Öãçϙ翜óÁóHq> Çó8[•3&w&Œ›×:ç‚unãyϳyýDƒ?¤äÊÆœ0Xç0L:z\öî÷ÃãïM'½FZ$7l¬ó8'Ùÿq§ÕËŒõòùýž÷uc>/ÔynP¨þ²ÏcÀ\%xàãS,ÏŽ‡Ó»—f»hÓÉìœA³×Œt†±‰wô×’ì÷>%~¿ÌX¾0(Ñøy„øU§0¿ϳߜvS`Aƒìó_¾‡zëçù´¾™NŽô\tÂ7Ûî÷}U{Ì<-ycæµwÚÚ‚z§BÝÿtc½a,ä{ l7¦ %‚M€¦ÃÖˆ‡àˆi5«¥¦Ø÷*©Ÿ«+ÔÖµ¸”é£%Ï6Öͬ¯-¸Oüy ã†hc<çãªBÜ <¼Ã#óW3=XÝúxx³ëÃ…RoÒÉH—k?Ë6 iº–¨½Ô‘^ö+þÏËx¿ÊÇ£bN‚ÏãBÓ¡ªcàf×Q©Çuñ°åÉ!S=8ECaHÿN·.MÃþ»ÓÁ¶ý;ûãÿ<;»£õDàH …Ô2áéI˜æ,ù°±ã¨åÆüƒÏgñ:»<¾ŠÇ¥zl¿}å].8O„ÇÇ‚ÏØ„=€#ÇøÔ˜ª'¸|8Ì¡ÃQìÿÛ?põ_Nøó-<ž¨Qˆe’–+ó9<­þ›‰ ä©`6å§ËJ=ùöU¿¿j#áÑüãï3q¼~²×¤ÍVËÙ<ïC£ÝðºÅb¿•`»šG!¹ÓÛM€n÷wvø!îTÞReJ—=Iz¾ç¨Åñ‘0ÎÌdMWŒ×ukUßsÔŸõogŒußyÿ-ÿȰ]³=¼ûÍ=²ì%^¯ƒ¿ƒ:DOFÔˆúä;Ê?·^;­€‹ Œ›Ó<Þ_óçk°sl÷ά¿FO®æŒ¾ÓùÇaph!Ø!ϯy=jžñ~EÌ=TcûW"j÷šê0|Ne-Oø²íôN Îé‰mçmæ%g¹‚©X©%ÂWݧ挞~°hO]»^ pkËîé½kdk]ZN¾fç áûçÞhÞIK.ù¹Ÿîþ"€pî8Ï'øxAÿ$خˈg’ce}Îv|»$ÒÇ8Õ:Õ ƒ<EA—®0þp‡/ǰݔw]Ÿhôîß<¾r.œÁޱ]:»VÕ×v–¢€Æ¢øV9£U†‘/*7¹µ®#ŽsÚ;¨âÎíÏ+pç‚qΨÁžõœ33ªö(;êò®x×úžïN» r½f½¡Uv…ŽM$M3zhI¯éµ{wÏ $|Îû/¾~'®g¯Âv Øø©ÐÈÿDÞÛðè±y×€Ï=2ÈÖ)s†  '=˜³p°–,ª¡Ø²í Âçgù}0à/,ÌËVc»Ê˜aÁ[MɇÖEu‹J€|˜}w‹SiT®Âž)¥‡Aôë˜Ì -éiâó<Ñw…±àϱ(¿L\/?Û/GqÔ•=Á}›õrI Põìþ­õÝ3Ènÿ1»"‡Â?ÍçûØ>¥Á¥,\aôo^¿ž¯wˆ×MõØ®ÀeŸqSSÆ=M›&/vé&dRë&D“Õ±ûõÕ’ïíiÛ¤àº9Ï‚¯óõq¾`’‘+þn´5T`Œû\¯Þ33ˆ{§å§‡§±¤qHs-±oë4?|O ñº9G€?O>o](NcûBÞ?¾ ›~ÅÊLa»7Z°(ƒ|üø^Þ”é»shL}-ù)}iB›éÆþ‘οáãž÷ÊËñ<¯c¬-Oÿª††Üyßãôê bWïÆ§3£`’ùö]-nx2¿¿lŒ'<¯,ÄÕÁv£ÇäTÞód"TQ-êb¥iJÕg½¶g†3¾ßWi”"4^ç;ÛhÉzº|qºÀª'úvËXûÂØÿr;÷ï*lÿ¨]Y›«¶ßfc§ƒK‡:Šö®Þ´õLŒüôœB¥Zr}Hɦ‚ûÃùäüùŠÇ½jlWˆ3Á¾ÿÍ™ßöÓÁ Ÿ–¾#3È(퓞cátóz54Ô’=d•Âñ¾ó¸$Ì_>3r öí Ô-´¹0ÒŽ´Š>Q—ë·Ÿá—AÈóžKÏ, '.Tÿ)ÍRK1‹H_h´CÞ‰ýQíí¡ Ÿ‹ d_Ýéõh 7ÜXñ(ƒÔÛi›ÝKáLªDK„y¯@Â9|œ+ŽG&™¹²ˆÇk>ošºzh~½ð€bªèKx—AôŽ&'ƒý¯[¯ëŠy˜ ]ÀYa\—ç|Ežoð~»¯Û»Ó‡ÜK¡Ža€§ƒ…öŽoJg’^'?šgEŒ‚”*+×’Gt…¯ÀOx?Èû>¿&\ÿ'Cû2l¿f©\û…aË ,c„›]šîß;¹gIÕL2§Ì°•mÎŒ€j{¥lë§%³à€j¯ø•Ÿð¸ÍÇe_ãt+ð<ûãÎVœBü!¹CÐýkÌYnúäIƒL2æÕgå1÷ápíqV’ÜZKŽT×k¸o…Ñ.ŠÆAþ<¹½ìÛŸý¡û¨½òåpßf•|ZéDØðpÙðŠm2‰07ëWèò¾ž–¬=»}Á£î+‰pß òxq Æö&öUmq×.‡Zt9£F"¼i>bÑþŽ™äÒøŸ^x ¦VÜ÷íøe“Õ1~àöønÂý/’Oc{ÑMê|·* ,$=o|Ó"ÂFnŠ®âIl^î¿t(T-3¡ÖÀ6ZÒºœÜåöÕEâé㸺PÞíŽiNgÔAF^¥Ÿ·O„¹A›ª —g’¦§SÍŸ:·4ét»•–l|®3»š»Òh<Ÿæ¼]_·.ýƒ…ùJ“¬\Ùí¦žèW€/¡–»K¥€™$Ö1d¦ÙÄ¡þA“VMKòVõ’”¹’åšnœKÇ㥘ã%ÁvM£¢/í+îõš4&ºÞºž61“Ì?6õš£l(ÄM«péc89×W»’ðù<žÏ‰Ç2lOÀÓCNRèòs!¡õƒæ33INƘ1½+ …Ä·~û*œ OY4¾“ßJÂû=Þðñ‰8®+°]ó÷·ÎIXËæ±…뚟I¢¯¶ØÐ¹áØ:lÖÐòÏÃÉX§è¦ƒV¾þÂã®8Ua{«¦=ùåàëuzÉr­Õ©Dp \ëmI ø@ W¨Ø~§ïéÜp"äy+ù!ï'Äy½Û²÷²®Gðz(eèÈ!÷؉ÊYÁ™¤clM˹Ã]À7Ée¦^Ÿï¬$|^ƒÇmþ¼Åýf$¶›LNÄî­º>Ö0+9>'öûd’¹Öiê².àr4¯M×áDb˜­2æ…œ·Êí•çóâù7=¶O©}Þ§7À»…ôƒ&BÕ-ö~Ü›Iú€B®pμ9|N¼œcÜ~é¼Êh¯<çù!Ÿ_/´¯,;W¶Þwl¥ãB ýg‡rÊ'Áóº¶:–I”OoÏ qw…RŠ.K—'N-jM]4iÆÛGÏ_·˜,Áv£6Têþ 'T ?YÔN‚|û6Ýg2Éä— . ]A2jš{sS-é×8æ¶ÕÆU„÷øóãvVˆßŠí¾Zôàñ—X¾úÄÂvÖIPÖ4=ñÊ•LҲʃ;í†@³üýOO–Ò’×§»v{n•±Ÿçý¿||&^GQ`ûÓ¿Oó©&\ž ~½¨O¸Í[³ðÙ½L²v×ÅȲæÃàYCCÂKš{N{ºàyòëÏŸ«°½+n-· 0_‡æ Ü’ £Âç…ÖÉ™dãÁõ v­‹V(s?&‡Ÿ\¯Ï‚Wû kc´¾ÏC¼£Æö…õ¤¸eM=6 "Ž•z”“I¢ž ½ß÷ÕØ¢¤3áäÄ®­ŸÏ\eÜ×Ã9|…x¬Øžp6Bþµ}-¦ÏH‚éÕãGVyŸIœŸÞÉœRn4T7€&ÃIÊ.ëgîÜ.Ÿßo&ž×c»Àk#ì3{YÜ’$˜P9¾Ó¹2Y$-úÕÐ׻ưõ™p’ԙРîƒ0~zbŒg|<)æ©›<Ê•y÷±o­ŒÙ¾ž$=üdzãY¤j¿jóê­S@M;Ûèù÷ÂÉOW}µªè•„ÏWòë®·fa1¶{ërBÂÙøPØÔ¹Ý%Ÿ£I°‘. 6Ë"OfÕO^½y <}µ/ON†>ìp-k÷J¹}|<ÉÇO…øÜØnÂϧo/LÝ {¢Ü{¼å)n°Céö FE· ž7³ÂIäó]ZÅô¿¼_>íBóD l÷ÔÚÜý¶@;§ž®÷“ ­{Ÿ2U²Èþ€a˜z¸ƒ{ß–wå…“’#;Øw<¿ò’:…ò¶¿åžS™>7·BÒp:A’ƒn˜ <=8‹´©úá‡ý¶Ã`æòÌ5v?‡çq=î4²Ê˜¿q?äùCÒãë4cãª_„}-ؾ´²÷öEç¶2¯JÙèWI`à¹Ì"Ò{Ënß?ác<.}R•Õ’Å÷+5ZÖgÖwÏý„ç;â}'‘Ø®Im‹G·m‡;1Yï;|ÂçøvðÇ{²HõÁ^Ž× 7ƒÇåÖÔ’õœŽ½/™Æ~ )MKÞ·*¼¯ Û?òNSÃ\·$'Oæl7O†SÝ…¶òÊ"‹N?Šòž-JÿÖ˜hÉäß¶¿ˆYE8g“¯Û?0£SkàãVƒ]?Æ~ðÒ•SÞ/wœ·-l:I’¡ñ“É&ÍÎ2rãVÒé¹7ádšò»}­z9ƒÜ¾ vLÛqµa6S Ÿm“z´O†õ~V;¾Y”EŽ\Ÿþ}ÇÎXvxõ˜GØÜxsô»È –O¥÷ñìÛYmõ¢âEé.ð½-JŸdè»ûùÎÀ,Ò¶œÇ¢F·]@]{ˉ¸JZ2¢Y›--Vgø¸Ð`§ØÎ˜ŠòZSªî†8š«¯H†tŠ»]—E†˜ÖíhÅ!`±®ôýÃpR7û­Ây~q½’Û#ßWÆçï9ó€l⤘ÎÂ:¯ ϳCÛÛ4K¿îvû~ð޹É`2¶DÉ íY¤Ù——Ê—¡C ºåŽïö^ 'ѯ~±këDø~Þ¿ ãú…÷!b»þ¹ƒ#êùï;g­cÒêdHXõqHrX±=í«Ù3´ý¨…“Ì;Ç›\ 2ú`WoŒqÇqOÉfÉ‘­Y^Áö!bûº‡ƒ¾ ÿÛ?艓 _–¬oär*‹Ôs¥3qÀF‡Éؾ¥òeב3ƒŒýœ°îóÌ/…¼Åº0ÛïP·DJ«Ñ{áªòî¡N‡’¡b¹{÷¯,>ðuDØPXäùø¦mi-iµñQ®M›¿ye¼/û|‚ö m±âùÒ½p£íùuÙg’aåËSÁÉ·²ÈÏ×L²Ó/ì³Í…‹ýüé&3/Õ 2úŸ÷ày¥ÁN±½üAvrýö½ðvǭɃn&ÃëJ#¾kÿŸWé;6ß5v…Í­^‚M]-i>òûËo­‚~5>äûÛ öŠíM÷|Ù&ý^˜¶~åõ¤ôdéêòsNZÙv±ç»Ç)ÎàjÛ=~úeØ÷‘õ~^eÜ¿Ãû¾~Á÷¿8'$ýâZ­E¡y]ÅÎ{Ý•}̆Ly— /?×}÷$‹lÈ®ª\â6´ïº ñ/£%—=º7@dÜïÄç¥ù¾_ åÃØþ‚Fg'Ú³vÈt͇VLÓM»¶ÍË"þÓ*YW(>ÕLK¢š•9³À_øúï8×`ÇØnÕÑK\ê;î‡úÒJØU¥À£—¾óOåg‘Ò-C{X_‘C ër5Þ`»!åCµ>VpÝ<.òù<¿Ñã6…¥æGbûõÛî½ÒÓò45 ˜S@Ûû`s¯Ù¤~lÕýæ†÷¤áû¾Áû2ƒöÐvÌãÂüfaž²Ûw1Ëþ¶O½0xu}Éeõ€xã2qƒÒ<›¸=_é·EáöSó.¶¨¡%‹+VwŠå×_°®.Øa³Bãc“œ\Ùóä¾½òk'Š¿ŸÊ7½¦ª«f“›M]o[ŸÛ+ï»p Û]zÉ{ö‚¸ÁÇõœ·*^ç’`»Œb­Ÿ„Ö~s}S`ò¬»0D’M>W¢;¼Ýàîá}ªS_ÂÉNÍÕR·ö÷ÙßóÀ“p2¹æÁ÷®Nù"_áýh¡u@l·Ep"f.G ÷†weÆ„¦@½qö9#Gd“ʤ¬ëÞIc o²Óèñ% ÆÿEï?ß_ÌïÏt»KvwÖ-ôœ#ñ<w»œÙ/; ö‰e^ʶ§@—&,š’MnÜî1¥ÿ(˜qìΈYÇ|štîPòõJãú®|šø¾è±ÝnÏŽ só9çbèFòxš©:¤Ì&µ;¿üÔh–s¡–4“n8]^pßù¸‡ÇÛñ°jŸãņ…ž«ÉÓ\™a{˜«H„ë¾ÖGÑŸÖäŸ=îŸMú_¾s›Ò röE˜´×!ÿ\e¼?üþÏ\\íõ“&z£]ú~¶ßeg‰·ã5pNWœMÆ3Ô1“Öf“w5¤v÷Fÿ²ô‹ Z²¿yÉmi×Vçùýæë€|ÞÌ`ïØîƒ‘^ÒxãðŠö¢‘)ð%Âíp—mÙ¤Ii\Á¤äÃFUÑ’Ïcµã‡,[õ«u öíiw4¨sòùqö»á}м=Þl6ѵ.9nA) Ôíùád˜acÕª_­çâôb{=îýòÅÞãœ3çìWS k«¶®Ãe§ö?¬øi0<Ò.øþCn8ùÐ|M©c 칟^2u˜MŒ1Oäã5ñøGí»ï(;gÒ‹Ðk²wžÿÍXÕáÆ™Ù¤ñ¸ðŸ= 3BßÇqkÚz}X_÷¼ßäû vŒí:&„NÂî¼ÄNöº¸’œv³ä¥l²Ìqy÷å0`jœnty-ieUáVû-A„¯ZÁv¸yǛDŽ1£ª…]Ó§€dñ–pï›Ù¤OÕ½ã,/É¡S¿è&u´¤Ï‹Î.9ý ÷_~¼?àãañ8ÓäY®¬òg‡èо§ I®_ÈËXØ|à†{1Ùdŧc˾\”ÃÔW.C<%ZÒ5V¯ÕæçËø¾.ñ|€Û»5å쎜³§`Ͻ”ž—ß§€tߊ؎IØòì?i†tåKO+Û¨ )êBœ»jÌøº³x¾D†çI¾zóÓ7ápæÐ°µ1%R¡yÇö‹3³IÅò{¿k'‡-'é€BKžu398p[A?Éÿåë<<_ÿÚ~.žçÝÜ 7wn‡CF‰â+¤Â¡C~Ù÷,›\«ÐsˤäAÐdîÑWWºkÉ7êô÷Þ£ƒÈ+»EòiÆñŸ¯-´®í>z}êcä-< áÃ2•K²É'{ùF‹±ƒùü1#µd!ZyµêEó¶,ã:A¡ýIØn@Øåwµð1êHßÓMRaÍ£QoU?g“©]æÅ´ê µ$}zš}ó;ó1ü~m$¶»Ô}ûENÃû5µªŒm“ ]§~[#ùK6IxŸ!ip« o¿}{…5ZâîI-¢ Îï;Û‡Æï Ÿ7Ï‹êñ<µ#Jͺµç4ØÕ²|†]*,^ÕxUéGdÊŠ˜¨'°²ä‚†ƒÖiɼ^êÈ¿çó*ˆð|ŠóÊżv¶"ü¢cÜn}dùU¼ÎƒZY³æÕ‘KO2oµë`Üÿe>éôÆ›>ã>¾ãþÉùê{Æv/J7·° ‰‡Çp¤“ žNa¯ëÔ~DÆ7èdºÆ´ì?sʤP-y»ô¬ã®VA„_ÿ—óåÅó*l7ãõ‹QK/F@j£­‡wL…’g®®-Û𙓒wîéÆ ìÃÖìäqÀY0ÏÁã ·?¾.o°gl÷ÔÛrið¶÷i*œë}{q½ØîIùüëOéjZ™³Zrÿ]NÅ÷kWžgs;ãóT;Æöâú5î_ç ÐoÝ¥X¥B`ƒ¥Mãl‘þ‹Ïíî·t0Ô®¤Õ½8‰ÏŸNSûØ1þ<^dÕÚyóóÜZÆù˜0á(®¡S\{ñ¯TS‚ÖÝ:`"ôp.°˜¡NkŽñZ9Œ{Ƹwœ ,eµ&òD¬1oJÊj1Škp[~…}g&â¨ózy"6°„1]Äl`KÓÅ^ÄQwqÔYݱ¢¼‚0Æ+ð1ª”¬7çºèYý‰`ƪ’±šŒùŒGÙ.f"­É(®ÅMÁ´&…”±«ÄŒO³"|ÊÁÓ°zdn¬6#uRcáYŠj’Q†ŠÕgäœÊ ¶guÉòÇ ŒÕÜQ0fpžˆšÏê4F0G—‹ØVžŒÊkïèŠÔèv±ƒ9OÊØx:V£Œ×²åõwÄÜÊÇ£ü`ÊZ§ü`«_A¹*ÆÄ’²<Å1´8†ªLþÚ1ÔŒ}FγÕ#P4ÚÀR;”2±4Œ‰ÅÙ¡”­ÊêõðZÞbÞ‹=«M&®g+ù ËBÄW—¢ñ3¾:ç‡Z1Ž˜*q d"¾ºBÄWf5{d¬v#¯í­aµ½}DÌ«iËY”±.cõó 9Œ9˜ãXˆøX´F™¸®-åˆæH„:ß¡ŒÿGÙ/‘¬¶­˜i@Y¬Î‚Õ+3c\ÊÉ’ˆjýX1ŒNÄ6 LQ«÷“Ïê~k?Ù“qEóEl@êà Vë–²EÝDLÆäLe}‘z· _”s³ì7KÏêÿð:œµ,fX0Æh$cŒR»^TëQÏêƒSþ2õuzˆchѸ)Ž™<^Šc%“Ecä‰_‹_c¬‹Ù¡4öÑxÇãc<†Ñ¸Ec–˜ @}‘ǘ9,ž,ü1d¥I«“Ç ^ÏŸó9·²˜°“ùþvŸhlà|Nq BI¦“'«=È™wŽŒ %ªÇJëpiX~Îp’‰¸å¼Þ •ˆáä&bsæ°ý´«c˜‹æƲ“1Ž­¯J¹¾Ê:ƒ¤µ´$EgÕP¨íƸ»†Ú§Œù¡GùàC”2&f cêrV\¾T¨g*e<Ì0VÃÔ‚±;¢Úö2VŠÖ“Š(îï‹û{“¿~oÉ>ƒÞD``ŠYá‘(+4Ú`ÆÀ¤Ì¢Æ,â LÊ W3cæuš%¬Nscôª‹Ô"µú ·ÈRÄ ·GãeÀ9˜RV¿^ÌÁ´Õ¯wñÂ=E¼ðPT«Õ¬ÕjŽ`µš•"–G «GÊkØSf¸#«Ù—/âûÒš¤ VÇÞRÄ/’Õ/\“TÊ“5›ÕÌ =ëͲH-{Ê0¢ìpKÆöˆ`õI}LjòÃU(=«ãLùzQM{ÊÆ¤ q5sdc¿Q°ãcRÇæü7ÊödµJ)#S!â~(‹0àrŠÔ+õq29×HƸF”“i/ª÷ǹÁâ÷–Œ•ÅX™”)ž#ªùÇk;S–0ôø31”ÆM3©½óXY4NÒIã#ÿ(.Ò˜(îçiÌ㬡¯Å.¯ÄqŠÇ'‡hì™ÅbÈ"/D±Ç…µ,„˜îÓÅ>¿‡}&1·1%Å›ŒÊC¹1n£ã6Š™<”½(âÙzŠjlrþŽ„ñwg;Š1)sGÁ˜;œÇ˜Çêª[alS&­«‘Î992ÆÉ1«/Ô=§¼Ä(ô)> à†"cßPÆå8([ u+)ëÕͺ >e0cZ°ºã”¿à)ª5N™„œcMùZÅývq¿­2ùk÷ÛVì9kPÌ®ŽBIÑhC™áRæL$cÎpÖ eW‡¡ÌÊÔÕµbuuó—5 e†îƒÒ±âE¹3¿Z†Æ¯füjδg5ÄżA©¨†¸\įöñ«Õ¨|V[7LT[—2$è<*c²YsDuÄ)ÃZŽNÆ‹óY-&Šq9ÆNÍœÎM"pó%BýÝ0Æãòa<.I‘šâ”ACYÖÆ^ ¬VKVW\ÇxÖ¨V“—òrDµÅ)ƒ2­ÃPf¬.o$c¶*‡Ð¬µÀéŠ`ÜVT4czŠX ª"¼.ÊnuÄ AY°:ãœGȹ4ŽŒKCy„2 jT¾ˆß*®5.aLÂè"ute@BEµz)ÕÚ,=þJ1ô÷ÆÏ¿cìT˜üûãæ_9fþ«ñ’>O1ŸP‡²Gà EåÓùMÆ'”2>¡˜ECÖ>¨h£KÌfµD#öAE£ìјÕÌ 9ÆŠñh8§r¬=Ús MX ò(§²hx rί¶ñ«=kò¹|¾ÂeuqY(¨9åZ2Þe³Z¡ó¨Pº"<GÆ£±@‡òDE¡¬$BrÊ,¤ÜÊgFÙ££…¢ò(“.¯‘À¦¡ÌÂÆé FéYÍr5ãYËEìÊhÕ-§ìBKtRÆ_DgÍAÉXÝr=ãu©P:”=:p(«].c 1«•&E TÊ\‰Ò±桌gÈY5–ŒUÅØÖžŒÛEy­>¨(QsÊ«Q³ @ÙÖŒÃà)âµz¢"‹ç…þç1²8¿üçñ’³¬9ÓP̲ŽF٣ѪÓòi¢Ÿ†3 )ËZƒ²@cöDE3nC(ã6P®«e®DéQö_aÔX‰xÖŽhüaŒg͹†”QV„kH5jæn"žµRijcÎâ†Ò ÌÐiŒÿ`…X„õEy°r (K J÷släŒcC¹‡ŽÂX€à\Xjhž¨hƲ¡ìCcRæu>ã4¨Qù(9ãÄR{§¡<~~m-èŸÅË¢±’ÇIö˜þÐ:Ðÿ*6þ£¸ø[1ñÄC ÿWqPyüã±ï·âÞ¿óè}ç¬B=JÆ:gOÆ)´glÊ«V¢t"þ–˜ÙªDãÓ¡dh€a"M0cRNµÆ1ƪæ5*Ÿ1¬C‹°ZÍÐ!=Q‘(KtLJ’1åÜʬ‰¦ìÆÜR¢ÃF£¤tžñj“ò«#Q–Œ·Åù¬”%Uœççy&ý<Ï‘ƒsÅìjJ†FÆ8„JS]m/âRvuÊÙ¥CÙ£Q«™aSNkÊ \…ÊAÉÐÐÕ(34vOT4cr~µ_ÃøÕœE舎 )Â"”¡S„¡Ìª,BίV‰øÕ”:‹²@§ñAE£¤è<Á¨”#cµæ£ÜЙ"ÃZŠ@Yˆx­t0%Jǘ„Á¨<”:œ¦¾ÀkõDE Ìè>ITãx©ÇKŠÎ¨BéPRtÊÀÆËZŠÎ©j"0\­ÐIQ9Mžu(*åˆN†ÊGÉÑy5ŒQH™Ö(Kº"â{2N¡ekñÅx®*ºVÂX…JTJŠ\„õEß ‰’`P‰x…¡¨|?1(hX`£4(3»®« ”%e-=c†±à!§k/,ˆ¸1¶+µmzÇÐ ý=ñ“ÆÎgÌ,'ÿll,ºú{ã ½G‘&›rªsPŽhpj”cÊLV55@Î(¤¬jJ²GƒTá±JÐ8U(=Ê‘î‹B™¡¡z¢¢QR4ØPÆ)ä¼jK4^%ãUsN¡²²§ÐŒîBE3N!çU;ŠxÕJT4Ê ^õ&«›ˆÉꃊDIÐTŒ[h…N¡b\V{tŽ`TJ†N¢füB7t–”„®Ù¢t({‰À¬¶j °Yõ(Gº‹9’#JÍJŽÊ§¹":–KÓT`W+P‘(KÆeBY¢³)Óò«U(=J†ÎŠÊGÉ)Ã0‡îy@G kS˜ÅjAׇQQ( :f *å(b*P‘( :«’ V…kOçúPùŒ]ÆØ…”Q…’ +EüU%c çƒÅù Ê䯺±6¨‘z–(Ì¥Ö£Ñh5(K4\•©À¥–¡«™S.u$J‚ƬDéQ24ê0”Y9׉’ ¢òPŽhèa( 4v”e/bS»¡ñG06u *%GGˆ@Y 3(Qz”#:…eŽá)bSŠØÔ( tOT$Ê’òZQ:”=:O(*%güVêH T$ãS{¢"Q–"†«:˜ ¥GÙ££…¢òQ t¸ˆúÓÕ‰² {¾Q‘( :a *eΈÒÓ}-è”ÁVµ=:g`ó*E' Få5xÕjæ°r”†9®*eÁ˜Õ‘( :² ¥CÙÓ¸ˆÒ¡$èØž¨hÆ| Då ¤”÷ŠŠF٣Ç¢òPrt| c¿*PQ(+ (=ʃš*eF™¯¨”…]ÿÕ…¥GÙcÀî(ðYåŒam†ÁÃñYÍ0ˆ(–Ú>3Ïÿó1T?űó_›Eã$‘ÿ©yÀßÊùþHìû£qÆ;ç¨(ú,J°¨åhPa( 4*%Jr4˜Ôftß;*ª´À¤Då dhpa(34:T4Ê /•ƒ’£F ,Ð}P:”=¤š%çRKÐ8UŒK-G#Õ ,ÑPU(J††² kÁ(J*âRËE\jJ‡’¢A¢ô(¶š·²D#BYѽw(){ JO¿ã‚FŸ‡rDÃcƯ Œj”: ¥§ßDGÈ¡kè ”" åÖXàTSÇ£Ô¨|”:IDSU퉊¢ßcA‡‘ Ã¨Pºæ§:•ƒrDR3'r£k¾èHyt? :“¦àL ”%:”²¢{þPy(9ݳO÷þ¡“y¢¢PVèl*”„æs(J‚ŽˆÒ¡dè€aÌ Ý: ljKÆ¦Ž¦¢ßëë"|&šÍC†²ï!*ØžZ+¶ö,Î9ùwõEöÓúˆÆÓ|ÝÄ‘­›˜°uçhÑ÷Å{r¬ØX:‡¥5lÝÙG”Òïæ 1ˆ„±@â†Ò he%°>ßä×qõÿÇxJã¨Ìä_ÏCi,¥qô_É;ÿW¹æß!Ϥ÷#ÒDcó˜Ë¿“­cë+j¶ÆÂÇÖ4Ö³ïò}ŠR¶O‘®#+Ø÷ŽøÞä|Ñz ÝwÌÆÕ<ÎòïcçˆÆÕflïx\M™ï½±ÅY7Qœ dß%”³ïð=ŠJ¶n&Úo£cß¿V³5cž;Ú³ñ3]c‰d¹"Ý[£—û5lÞPÉæ Y\µ`ëÁ:¶—&ŒÅTþ@{¶Ì÷ÑD³¸ÊœÈS4?ʾ ¨`kÀ4 f{hÜØú¯ªÈœ Íùþj{ј™¯£Ð½Ö>lïŒTô}žÿñ}3yl ­ach%ûþ4ÿ.ßSHs? Œ]>¨(”X‰Ò¡(=S’âë*T4JН£òh¬Ãßi@ˆ‘â}‹Ž,¿´`k6tŒnï DéQ2|/­ü㉊aÿc ûŽÝ+΋xô.%Äɾ.ûzŸ@·Ûì}s !7í1Í{ÜÔ, У1þÐ#í|ãjSaÓ}ù{4ð†hнѧ ¯f †qh°ïÑXk£qöC£ôBc\‡†^M˜Ðùˆ†W ®ÚD4²Uh\ÇаbѰΣaMFƒª‡Æt 5 ©>FÚ;„/žTa_8FTg/ªO6j)Nc4š+¨±h0%ÑPö º³…·Åh$ Ð@® &²…¶ClcÀ6¦ª®h™¨4†–h±¨ÙhµðáŸEbÑy7ª>G¨ø0ZáCˆAÍÂAKœ\Ayc䮋‘û6j!FoŒÞOP»0‚»c¯‚ü.Ê£8`ÏC©1’÷ÅHþµ³ãnÑCŠ>{0Å#‹ÿÝìÌßóð8Á÷¯ƒ³Ë¿é>­Ç{øy´4Á{ü¿ÉãÚÒ—'xût´këç1vÚ„6“½&zÿ‘sHñèо=þkmmk#5ülkmgø×p´³5±¶é€/ÙZÛØZ›Hm¤¶¤†máÿùc–¯ŸÇL‰Äd¼Ï¿™sûï<¦M÷øíßþm×¹>$]%ôQ›»ÌË~4µosù4É^ì7åin]ÎðC¯ ¾ã„?%è`¡¼áœ'Œóž9žµB=Ä\xEôF:2)7ÔeLOg‡î®4U/Ïš)‹/ö*ü’9¾4í±ï §"è:\.þCÓýzQÃ)Í/ˆ~¦—caƒ–ØVŠÿuhkÓ®S{{ãkÖ6m¥¶m­­;µ£Crh6m¤6Ö4,ÜAâ0HÞSÚ~ŒÔVâà1ÓÏS2hæä ^~~“½½$½Ð{$gzO7¤S&&ë%ì|¶_¿+µ¾zWøEVdÿÖtç1ÍcfOïi³¦{Ñ¿ë2Þ{6%á­—> =,úz›9a:½¢i.~=¼~ã5³"VÐïëØâ«Hc:Ä©¯«è®B!›‰èÅzõ%”òÚý;®½ŒÇÌq¾Æý‘P²×ßý”àaïïúJöú»ÓñC\­ÿägø÷8±ï¹üÒ½þÞ×_jÀ ¿FýCWoÚëï}ùã従ÿäåÿ¯ýàïÿ!LÇ;øøþÍ?>ˆ¿î‡øòåËgQn]â7.Ç„µÌ»¢— Ù’ø!û(ô'sõ'E_á½ø5c¿Èâb¡÷öúÊkÌø‹´÷ë¹…ùÃÂ/6ccqºˆPª¯ £/õîTññgŽmü߯w¶WÛ?|:È·³µýíù©´ðøÿGjcغüŸ?þÿÇó÷› ôk=ÖÃwÂ﵄ßýüm¬ÛÙu(~þÿã=ÿ‚å@ú Ãkÿèô·ÿ]ë¶RÛvÅóÿ£xýïÿöñ'üŸ®‚ü[üÿ×ëøŠM±ÿÿµ£xýï÷û?þñ¿äþÿÌÿ¿²þ×ÞNZìÿÿ•£xý¯xý¯xý¯xýïoýŠ×ÿèQ¼þ÷‡¯¿xý¯xý¯xý¯xýï?ü!Š×ÿŠ×ÿþbÇŸÿO”þkÃÿ4þ·n#,õÿ·o×¾xüÿß8¨¯Ñá)ýö_Q†Ì*áë…âHŒqìËýJ‹ÿÄq „%ã3eûÍ{7™Ur%‰¨çŸ;8b¤6.³8¤çmà?O6[°S®†_Ç«€Yuh~³ÓH¸q6bu™¼¡PÒͧñø°pÊy¿õðÁ<6È!n®ñïéydxž3ø×ÕƒÉÙ©å~VOº .Ž’Í¬/M­þn# ÄÅäó3œ5ÿikf¿3¼OïKþÙÐ0Ùx¢ÂŒÃ«nC¿¶ë6SƒÊâfð¸Üá°J>­ôÀþpý˜^¹Úó ïSáû²®·~üdùFr÷檑ÇnCϵ/ŸTÍÞ nÞ¼zd‰löØs8}€b¿_îòø±ð>5¾¯KZöéœñ›Iƒ#Ö9Ë¢oƒMï¤ù߮٠ÖužÖ°Ý0Ø¿ÉòöOx¾gê\]x_$¾oÃÛ­+žöØFf',íûú6lXº°ÿ¾^;¡Và„v{‡ÂÇÁñ›ç9j ÞbH~»ós ïÓãû&ÅÏ™ÿ|¹é» þ7ßÞ§¼—©?ï€òæKmV7‹ÞFx‡;iàœGòî;e„÷™LÈ”Y‡’o©IÞ•î=6´½óªÎÜ·ÆýqÆô070•¼|6ŸK\ê¬Ù;…÷Ið}Ãr¯>R–ÞMÎõ\YâÛwàšÃÞ‘ÑÖ;àp—Å—ÃîpƲT®ýB ´ ë`_ù“ðdø¾×y6{HÀ•ØÈï§Ü»IO}ܱ47zõ;9íáAÔÑSAèì[©\ú/Â}Qàûâö-êÛÂe/i¢éXöþ²;0¢rðJ÷·ÛàèÙol? f™\ua¥~ÿ!·ZõùÂóÃ÷mpj:eâ>òºí’ëvßy¥Oº\è½ Ì[ç~>n1 ,/pwX¤³Î×~èÊž¾¯ýd—YßOÛOÚ˜~péù;ÐrïǽI¶‚²…~Ç“A#`a½=]~œ©ÔîÏìúQø|‘ø¾o+d„N8@^šþrÌ7îô?Pk¬Gë­pÇa~³‰Ü Ëz}ÅÉØØtŸ:¹¿ð>=¾o÷ ‡‘Õ¯=ÞxqšŽl”Ú"k Œ²|êðbá8îݬrÆ  ê´Vñ{~3e© ¥í¤IŸ^}ÜmzFßÑwÜww ¼ÿÞ?ÇÂÞ>éCæ0 Ô‰«²Q)cÏß÷ÞtÅ‹—%~ %Ç-(µ¾ö]¨Eòú­{²zo¼süü!'¸û¨LϺC5ð¼Ý¤·¬…÷Éð}Kk,(=ùú$8°ùÚ^mïBƒ¼Ù¾Zl…--º6_ß:Ô-‘Òj´L²—•Û3\xŸßg7hVÔ]ŸC¤_÷Ó›Ú÷¾ =#¯oé¸6ùmXšhê·ÔSþ{ÿÕT×î‹ßرcÇ;vìX¯ˆ A1J‹ Vì±c Š;6 ÖÐC¶zè±Glرc¿+ké~î=ö¿1Îûœ³ÏÙÞc|Æx ß;e­5¯¹Jæõa®šx¦\ë:“Ï){¹eÁŽÞÍÃØºCO“îNÎ%‡Ñ ‰;?£_‡{œ‘Œ¦ü÷çw=_öÏ÷©DnÊ©Õód¡a,òEü+réeŒ¡çç§}ÏôÏV M'>8Þ`šú»? ŒxíÍo?䦯 ¸<p8R/u•ÕÆ\šS«óÇæwŽS•^ú¸žÊ1~y‚SÐF5­¬_ïç(=Ÿ3 ·wÍ… Ó£ÂÙ“Š‡3ŽîÍ¥ Ó¶4z‚¶Å•74y8–*Ž-NØï§¦6aéõ‹ÒùœÉìqàËY£©Yk*Íx;âl.%‡…z,Üq‚Þbk|6Žº]lŸ‡ýºöwÕ–|N„\¨Gƒ‚f›#XÊØ£t..—–®|z´AÚ ú¾`‚ñk{ʾ°¡ÛÈ­jÚâÿþ'Ùó91rÕ?~yâjÓÕ|Vf–žKËç·lñò=›º/«áR Uí±§_»ÍjúÕ~ØÊM?VòÛ9õ€^í;5ˆd_eM›_¸™Kó?}:ѨZ ]¢µþ‡„úÌë,z…ãèÇ• ïKRùœ¹òjÏŒ‰dg&½ôjñ<—Â/¶z«oHµž6›å5d"I˜Nê¼ßç–àîËýùœ9Ë›Þê´…‘,¸áàf[¿æR[Ím’õ ¤×|^uš®¦Ñ±-VÎäÿ^‹¿çö"óC‘Ì¡j¯g5ò(våh·ƒ#©×š›+n5›HO'¾Ý0ÆQM}Oœ¨µ{Ÿ3 _oŠ>§#Ùφ8»æy”ÃÆeíwýÏU£îQÏ8UÔL¾$¶›W‰øü³˜J]óè½ùvCû%Ô=Ò}ѱ`ÉŸãtõ½]U|‡ Çþ¾³õóŸF²°ÐŠqæCòèÉ‘wk=w{':ªÎD:ëõxŠ ¯ã„Ñ1e´p¼!÷¸Ã¯ýlM$s>QXq͸¯Z]“Ì¿ž9Z6|]öñönJ\ï‡ó¨¡}‹íãjœ s×;<4ÏœB}MK†WS”åHû©ùÂvCnykÅõŽö¬}ìc÷í¡ytœq/|œŽV¹ÕrR-ÛÔ÷éªT“e—àú‘Âv›S"þ1ÿÑ…ÎÃYôͽϳ<²-?0i‹ì ~4KSáäTrlu×tŽ·–ásÇŽ(oÈ•}ÐÓ~m8{Ô#¹áã‚<êÿ ’aä‘£D? N Ëô ùá‡åj^Ç6`Î7þ}Š‘KþÞ¨·KÕpvò鄿…ÏòȺpÄž¥ŸŽ7[:jêFö>µ…úªR´³ÖáxCnÉ¢õ)#7‡1WåÝ6i&ùdé"™ÖÆç-1.™ÝQJ"k÷kf«©í׸×6Û¹õ79}|Êu~ºãr“|:`ëmIG¨êÐ}²9¼ø1zðB55ïÉÍЄí‡\»ÏË–gL e]J–í ï‘OeÙde­ÐÏN>ïnæ@£ãü,:.RÓ䦻ÖNy»Œß~Èù×n}Z{|´â‘£òéFË'ÓÚâõ¸Ã0~"-Ï}tl^¯õ©«-Sóøœ¹Û¸‘$„Y$¼Zs}j>í>óºá¥ÞGèëñ‹=ÇO¤ó+¸AMlº÷ËÙQ|Îdn‰øH+§³aâÏsVäSˈÂVŸ¬P¡éq—N¤™«Ÿí飦ú½:ì+ÉçDÈ¥Øôœû9TÅ6º—è*íͧ¥K ;;BK//8g=d­žŸ­ˆ9€ýŒ½ Òω‘Ã$-Õíéy–ê:(4ŸžíߨRŽ9BÍšÔñ™QÁ†¾Iß”Œý,xüñ;o$|N†Üì©«üÛžgÓfö}·*-ŸæLž4¶|à:·±u¡ú¾µûµÏñœš:5lk0 ás 亽 ©–3âãÿ.Ÿ¤W¼%G›!Éù!ê5v¢Ñbßv6gÕ”9xkoÇ4>§Dîíú;ë¿Ì8Ëž¨¡yú%Ÿû¼¶ûI­½öáf…N4Q»ÂrÚI5]þ‘è¥ó¶rñuê|ê¸å k|wõøº5ô´¸àùÑ !TyɶvuG:Ó^®|`œ˜Ú¡õÅšÂöCîuÞ­MŽžf3"Êz4ѓڣ_—ìyD-Úü²É™¶d¬ol·WMókÌXÑøòR~ûÍ+Ë­ÛT¬tŠ%÷üøþ\[=îY»¨e÷ê\Õ¦ïÆ¶ÎÄ¿5éÖŠ•}æð9rïâfª¬ b.Ï™ÜSO®'—Œküù0-šfcßq¡Ý«|¿ï:ÔÉwó¥7–ðÛ¹e#º|‹P²ªã Ew‡èéý {/›ìÃdü8黓âMìײ¥u«çs2äôG[o˜3ñ$ó7{\ÓÕFO#R—”½8L_ÉR‡Up¤Ãý[ªŠùíL‚;Íäs äÆ-zPÚå{ý²kÝ=ŽšØ«ó‘Ã$ÝÕ!lW”m*ɸxHM!ä-ìÎ>%rÙŸwŽôª|ŒÙe.©o;UOÙ®Ž…Ö¦æEŽ}=Éçò¶6¨I;4ÿdÿ>§å¾Ïv3wœ¹ÀŠv­“>WOKæÎ¸5é0écù.º£3mÞ2õÙZ5w[á·¡˜ŸäBâ,k¾:~ˆ-Òì¹st™ž^x,Ùw¼âa Duty*¥ÞV¶SN Î,šZ-¯éb~ûÍ/[Ìxº`þŠÌôáùÅ‹Öë©gƒç»ç®;D…n•Ü:HÝé¶½[»M‹QŸß8â­.â·r­oeÔXß|;U[[u¤Ÿž†<Û]75å ±øõ_óN¥øY]{~Áþ©ÍYYÖ šœß~ÈU¼6çnÕ¸=ìÌBîˆÒÓ¾*܆<@K~vÄ‘##E‚_©šì*‡l0Ëäs2äV~6¹{¸Ïׯ­yµÃzrNšÒ¤ïÃý4HTýÛŠúžd¶÷èŠ^¹jªdýâAöHþó)›ù0©ñÅu[Xú÷;ú'ô4­Àgß²}4lÿ÷}e2 twÅÄ`Ñpå¥ÌE|N‰œCþ&ɽŒìFÊámmÏè©Ü1¸ïåÖûhq¥a´ÓlÊŸñ¥ïçõMwò9-ríæÕ>û©íj61éA•zÁz:ZrñÞ˜ {‰›•*r¥oá',ªoSSBLä› |΀Ü/ë=æCäì&7½ÓSÌáçÃëí¥¼IÑÇ} \èÚžý°ŸF.6ÄŽÿ^L”ˆçÎiY§‡óô;~%0RO?Z…ÚÖË÷§âQW+µÞìDÉ ÷ÿqsÏ22O»Tœ¶ÞŸöàö j·ùΨ€S8?lƽ"ÿ÷ ü½©(nÛþoëþ¼¿eg½·Lõ§õ†.D/iÇŽÓKÄ©)EÓ|ä¼QüûS"—\”Ü-v3•Ÿã&tzjïz˜=íæÿgé ¹ÖÚq;Ž»úm»¦öÚI«4nXu¦+õùzUžuGM¹y§Vv1ðïS†\'6ª“]øaJz4zoÜ=ÝòÃ9hÀ’ôóÅØÉ¡œ³íàU5+Lq&Â/ äR#¥)£^!§S/»?^«§o;¿åº$ææôM”R³þY_sSÕp¦wZ‹Ê|N‰Ü˜ §dé SyÚÙ. ëéáË7‹Ïm£/¬]Ç.Ôlûà{Ôäslg—°ý«½¨È°Î)pòœ8q†ž¶¤–ÜÉn»ǯÛóõ…ùÛµX%ŠVSÕ±'¯¶æ÷3rsŒš”|aL=‰–Œ4½™âGÃ<îFwt¢º5l¯f]VÓ¬òUaMçðÛoQ‰ø°”›8 ×oôÔ}Ö}ùê%~öÏÙçž·º©¦ž’I² ãøœ9©ñ‚Ö)jRßòÂ¥zZÑôçµÚ ýÈ8¼Ýv¢e_<}°¿ôÝ´ëÅý|NŒ\Ói]~øœ¦"ù®õ4#ãŬ¾[ɧK¿5ÊFR:ãà2,û=æV££Ç¨øœ ¹Û“ª­p†RzloUn®§¶ýÄ åâªÜ¤nô¼M³»Ã_ªéj«žgRFxñÛ¹'m<+Ìkõ´ªNóæòq¾”œ»èuäšxÚ†ó5/‡ ŸÍo?ä¶d:&©Îœ#u¥§;-^åSÌÛ‹#%þ›ÿŒï-WÚV{ñKMYM·˜ƒ|N‹\G¼ZnÈyºvéaÖ™ùÔ{oÁüV[7 óNOj~§fN(?®É´cø÷i@®—øTõ†Q*Ê*ùÙîZP>=ÊíZZ­'CƒŽ_=éà šQd"m9° )Ÿ3‘—ˆ#sÜ–· ¦WǾ…f¬Ë§—f¯÷×®¦›kÞŽZsIFÇ{åT¿õè÷y)ÿ>EÈõ˜ÜèAÇÓ!ä÷ö¨Ërç|ªé=Leé¶’¶6m6¡òâ)´ÍôW[¯Ô½5cvUïÅçÄÈ…=Îgy<”ÚT©]Ï|ê”ýëè´Ôetnz^ŸOܨÂÙ Ñm2ÕÄÝèÆçdÈ-ëP±ü¨Ù§ú7«å“j·“mþ‡%Âù«”NæD5ÕŠúÚ®V¥YüöCî]ÔEñØá—üáòCåÚåp%j1µë~0¡Ýqgê½²{›ÖÔÔp˜[µjk¦óÛ¹îm·g6—EÇiÕ0$…FËéµ[xƒÃH¾fSêŠÏjÚ1Rùµš'¿ýûäyyb×q‘4fX¤÷ó½y´ÚlÜqÔ"’=š9¾[™9†ûNözª¦¤_ÎÑÓÛñ× È/‡5RSÉ~îÂ^}ð”U‹\@¦Ãªvµs¦¤–*‡VwÕ´æ´sÀ•|Îdq‰¸ƒë;oWÌ×Ö={•?jD¥®#ÿ&ÝçQð•÷;êp!ß53ï…¼Àyþª½ÕËûMã·r½³+÷Çû0§]kÞ7Ï£õÖÝ&†U÷"ßgkj'-žLï³>ÆÁ~Ö{¿ªÉÚ£ü÷"FîÃk—¶’(ª3Sµ=øC.e½u¼:§Ó šÖ°òžEo\©ÖžÝµ¢m/Ì]Ü[Ìä·re[úv‘Gãß×êlý@}.mOªm½Ýn*?Ž™;=͘êz½SuØï08庰ý{iÙñÇå†Ñ”»tä»à\ê§ ï>¤§Uó\øyÉIw2N—[GwuçÎ(~¿V"Wz«‰¾æÊhzÛm8N-ri¥µã–ÆëG‘Úêø—BwÚù±ö¸… ñ>Ýå8…äsZä¸*{§ šäñ‰½†zæRžŽ{4èžIŸûIÜ)ècÙÌS¦ÿ<Ž ÈM_\¿®E í_Äs)¤Õêã1†³¡Üå372qº£Iû®¦Ùk§º‡ðŸÏdI‰x‚Ç—nù›c(Á"fxǦ¹twÞgÏ¥©Æ_—žL7«¾Ÿx§¦{,kI—ðß§9~>Cý¢»vo÷1G8Î\?.H)vÒÄwÏÕÔT~hßãö3øí‡\Àù¥£:·‰¥y¾{§\Ï¡¬n;‡Yîug¾ÔcÓj»P]î4Ç{7Ï;O”ñû‹ 9Ïï}_ç.Š¥*~›š:¨s¨w¯óÊÖÊØI×uË”8/ °¥jZýýÞ9úÆ äø:KQgî¬Û½'‡7,íç8µr,”ÞÈ…ö™/³Æ|¢Š*xæ¼u|N‰œŸfàÁZ&q¤Ù=ª`!rU~Ú…OšÉ–‡MZ}î°T¸Ž¥¦a/}+ÆoŽ?ä†O$ãhFD­Úö9ÂüË%MòÐf×r£G»›4‹¢_yŸJkæsäøyb™eϪ3¶{-ª9¸—­…ìÖ“ô>¦Póq)eõzEÑû{ßLzÎ/&K1¬Õr°*5Ž.=Õ.ñ©•CSŒª:e ›ýÞªÖ“H j'é=¯÷$m[ä ~;ˆË ;«ˆýG)× ҽȦi‹.WHZ¹‚9VœV¿Ç#OìPz)½GU¬•}rQM~#çøxÍè¶}ãiÑÛ²ÀJÙÙôbR»}>®fßÒoNžFwßH–6ˆ¢ûw³êŸ˜ÉçdÈqWü½âiÈ«Žåç²I4N\±ß¼ ÌãüÞCY·=)›;Üߪ…yÿ>Èáà:RçD<=œòzð–lº¾¨Ñê˜K™™q%££ù‰·?bi5Á$MÇ/Jävz >÷:+^¨ÙôÜ¡Øù‡ÛfæöʳgCµ½Õ¨×KS“Ûðkõ»ž¶rn9Çä•ÇÓÜÍnÏžM+T}>p·3NÝ]©eF݃£QWøÏ/l?äfù$Tk#J ]›L—µÌ&qʱ~ÞS}ÙLã#:æ\c£{‰šl=zïÞû’Ï™,+o̯ÛÎgLÕRï±÷ø’E­†Þ´ýÖ—m4ó.9“[¾ÙîÉŸÔÔQþbaàs~Ü!÷d•|eÑÂê9ggÑÄÂ,º`ÄšÐ[Øž–ÜŽíD?¥ç³PʾŸªüÃw ¿ýäѸÁ±= ”}¥ë¦_áYtrKâëÏ…[XDñÔZ9Suûu¢U—wôÙ2pø1w~û!·[|)bª:f5Ìî¾%‹ænÖ4ê¾õÏqë?A—w»õ¶ò»¹üöCnëî$Ðõ"?Y ÷,Ê­øªNçª~¬ñÂ/6¶«¥TszgmÔ•ñ=ÒµƒvOå·rYM.=ÕeõÜÜ+‹¦9†êßöcÇ~¹`à›L&Æ Cj²žºúÛÒ«ü÷¢EŽ» ÛèW}¹Pë׳ªY4âèìÛ­ïmcüu7zµ¹[ÍŽ?ÿ¹Ý ÈU}ï¢soª¡ûM¹ŸI’Ñçvõ­¸ƒ…˰^fêN™é›»¯ø¦¦onË& Çßò±• w%XCF~˜÷â|&µ¢1DÜÉÇ]¾»Ï·ð8{õï9w{¤±pü!7E¤XCq…<“¦O~+Û±‹qgµ­¸Ó¨2vð%¾Oþ: ?;ñ®t´ÔQCüuÁLêòìR›Ò”ÝÌ$sYé‡Kÿ1iõmr·Qï„ú‡Ü–R3«ú^ê•oëú¶v&%|«ùþúÍ=ì㌟WÌq¥®?ZrEMëÊ-¬ùœ9þz¡†Bq7”t4nëûFceþì‡íÓ”˜Ê“ifjåÄ jÚ°õ”ø¤N˜¿ —Yù{³±›4ÔððÃF~:òV÷Ìp­?«[ôk`ÎaêÜ­ÆE5ý7ŒÛ¹çûGCU¸Û|³t´u[ÕÎÞ5ö²ß׿ª&î°•¨þ3¯4n?ä’“î¥úרkèÚ#:*Ÿ6С·í^öûz]ØÎÑÃ"ÕôLÓìæÁCÂñ·¢Düp‚#ÞŠ†ÚT™ÐÆåy]è9jÖ¶½lq›=†ÑYÎôa®¢@rç㒚;ÆçDÈ}h\÷Í@µ†Œ§Ia¤´ouQ}k/ãï»I©×¯ºàüÛ+zC`ÕZüç#Ç߯ÔPl¨ÿ»ýs3¨CñÂÅ#¬÷±}®GŽßšLK¾®¾2 Ÿ¯óÁ2Ÿ)„ú‡ÜÝZ ïS4TëñÈ‹5:gPYŸ-L3÷1LÚ sÝ„y™šjc¯¯q‰ß ä^]{°1KCw.;?èQ:r.±{·`?sió¸Sá0Zô¡Wk3Ô?_ïöSóøí®DîçѰ‰u¯j(Œ{›'Ó)ЧûÕØÖØÙF÷%åó=ÈsÀ|·8¯j·¥eú&œ? —VqêÕð*êŸöð”+^ÏEu«AæÖ+ï>ö,wâ¯â|ìsæD……0Aî‰ÿuëÌGjeœ8¦Ó¼—Þµ=Èòë}éq¶ÇdZxϪsÎ5u/H¬Ý/Ì_V–ˆ?»ìùæüBC3›k*-ÏJ£Z+RâG9È~_wØé“~˜³š­#®Çˆ„ã9ÓÖ¶±ÞâóíŠj0eS홵¢Ó³Ðƒl‡aC×›QI[©þDMÓnæ´ ö¶rù[¾÷Ù\®!{}Ç­¥Ñj—€¦{7dü}öqT GåG5õ6^¶r†ñ /Í~ièµè°Õ»T P7gE—ƒì w¹£|4ÅßîÑ÷ÆÌ_œfÇi[ Û9ãSUénlÓómBRéÀžýÞ=ðÑ#ÒO;& ‘[œ.í]¥¦úÅÇ7϶Ž?äÖ¶ï0´V"qwLK¥&êo|Ží×îÒPÓ _¬éôÞü®«Õ´íÚØø Ç…ã9Ý®r'Rú¿.ß›¦’Ó¨Š.){ö3ÿy#Ò¬6Œ ÓÅuã¨é÷<Á¸ý»Yãè!iÓDªsgã ÇWShŒuüVóûY¥뽯ŸŽ¤ª]ÞŸí®&šÄ½cáøó.?»ê8áIëDr²_c›·-…Ì]3=µ[öªf©ïà1ÂõÿÎëDÈWè˜HŠãV•BsVSûÞûXÄØ„K’ÆR›çÃP"Ô0±[Bl3aû!7!ëÌ–þ&)4²ñœ’–†½ŒÿïvÞÛÁuƒš:ô¶þ>É$ykýî‰ÔZùmɬ‹É4z{ýagìe|=´£f§—;öŽ:ËM{Vñ9rÜ…öDZy3rðÝUÉtÏ*¯~u»½Â|ÜŽž.ÙT§«š E9¯¦¼¶r={j‡ J$ã×e•Lò[Ö£?}öggÏl *ÌK[¹Ó˜ÚØnÆùh‘ þôÌ;GœH›Z°¼ö)‰26ºdŸ8ò{¼C«ZY:ãq¤ðpÜ!7=ñl¡¿&‰ò£ro¬hïÏ,Œ~FÒ“E+§EÒ¦–¿ªl2®·¬*˧/Ÿ;&‘;ö¹ç¼:‰nø/¤é²‡%6]¨}ºu8ÙU¢OIÍÇ„¥7á_G„\µN§¦ºQÑçž®{Kv±_«}«|®4ŒL–Wªr/3’¸YEêPaž‰¿ßØÁ%ª||"ɹ۾•“htë›:ëv2þù•Áäh²s¶^)Üwêrž;î ÍÑÒ›/U³×5Ù!ÌŸ‡Q'nZ–)ܯêþž;k9ì˜HᬃɯZÚ¸¡Îõm¬B¿¸ùá=­IÒ¤óßÈHÚXÅr¦AÁ¿?%rŒŒàu²?œ[—ç÷ç8ÎyùÞüñ‘Hºßß¡M«m¸ˆ¿WÚ¯ZÖ®›–Ц|ßÄÜ9O <[bGSõÖGޤ´ðM楷ø¿7àïG½7iüå#£ÕNYí ÙÊ*/˜M &ׯ}IfC®\ž§çÿÞdu‰øG÷ åçñ9ŒÃÿ~FŸVºíþ>k+ãF¿²ÛzÑ¢Rô‹‘´+Ư!µ…í‚\Ãjú7ß0êP~ZÕg+øâ›Ü_BnúŸ›nŒÿ{1þþ÷X…}"un¤pð+´á|ûÑ]žnaaáÜҞƩ¶z-‹¤Âç¡¡ãøœ ¹ÓÕ¸ ‰TÙøàÁÊ~´}xcéÆ_—K;äû'Î4¤Ÿo¹ÖüçR ·O¼yü ìo]vÜ|Úë µ=º=ðìr_ö~9wÁn$åK‰»•Am3ŠúøL¶rWéÔžHžV7,G<¼LnÖÏ öæÿCiwÙùXw‹ºüþ EF°D2N[\¦¼³š—71þzMÚz¦±mÇóÿ8Ÿ2 ×h§íóÊ}éÀ§ªSÆ]&y™aSFý,b¶iF·5½é‹ÏËK»r#ÈZ5äÐ2>g²¦D\%mO¯•_îxqÜeÚñþÎÓ‘ëÙ ÚqÖæJoòŠÖêëë⯠õ ¹‡ý›iÝD‰Ô÷Tì€òäK4`àßÖ0þúP?ŠºÏM"hKëãZ† çßÈ­{¼Ñĉd½V–±aã%²×™Ïy?Ö›÷´Üïð`jÛïÕ´£Ï"ˆ?Žøœ ¹QÜôõëh\CÿOt‰|êϺõóàrÆÝ¸"þ³úLz|ü“pþ†\}ã…1 ½{­“.øy‘šWÈ6©jµ”yöv²ì<‚>˜¯–ï‹ ¬…³>œ~Àç”ÈÆÖ<ìT¬!·7n-N¹Hs¾îÍð¸˜iÛ²õ~µGÓÍÎcͼ"ȃÉ0ásZäf,ès9UCÜ]â­~iêœüæw?/düýƒ1T2¸×‹4IÄ?æIäpÒ#Ù¢¡ÿÚgvŒ¿Hž9ogþHšÇ¦Z1·ÇrØ[czÍáÄ×]>g²ó2wîÉ ¹KÜkÙô"[Z6ÎkŸãç14œ+Í#hþ¡ìÑO¿ Û¹£âz;ÌWh(MznjÛ{H“Ônʈ™Œq4]¹dò­ôz8Ù©1ék+\¿Dîûiñ3œ4ôA›!K ¾@έ^÷}¸Þ“Õ’.i´ãþH:äžV2À7œæÉB+ïH®?#§jv¹™²¯†<’ú—d.¼@v+÷±;ïÊ^ÞmýÆ©ë:{9©IG‡p27>Æ_ÏW ç8 É†Û 4”žtvz^Ÿ ôáéÞÛ=&²ä+'FúlN[ç'T± §­ªä“rùëùJäÎvx{äe}êœ1cñ/ŒKüxϸYDƒÃéÅ€×|¥át>êrã÷„ë_È ç÷´šÒ¥ÿ“婉4ta„Äuakáþ³5múªìÆŒð\Ç2 w§ø¥ÍÇ ”ÑåììÕÛ©ìÅÕ™k†®³Ž&ñ­_&ó©¶ñÁPaþ¸ÇCN­H Ë® Ÿ­Çød{ßõN¿è|ÛFé7£Ü}&\% Yó:,®?#—àáqÍ&†oÔ%ÒÏ×e“ˆÿ^&ÒÃgoÌšNéó3CL÷³ækl–@‡†qèhÈtYÇ!¯'ÒLî4¬™#=↱á”üåz”í3aû!w)fÅmß'ñôz¥EÛ4Û«yõ›ö„IÓÐØ=NtíSdXßÎáäøÈlñÒ.|NÜhk7 añÄ]UœkƒýÆâ\‹»“l(ýÛâo÷;Ñ~›Ûå Â)zz;³ÈÂýäV„¬Í·ñ‰§‘©¹Á3L44ãÒqÝ&ïÂy¬#ÍÚôÀ=¥V8êøuízBCîÍâsÕoOˆ§ý3Çk¨å³Ù£zþÄönuÓ½_þDÊY[›„“CÎpŒ„ÂöCnÎ@ýˆÍã©@¥Íì/Oî¢öu¹™Úxêðìhæ€Ûaôû¾§qû­/ûÝê¿s“!ŽØ‡OOOµI Å?d¾&¾(E9îtfš ]ó;²®Õé0 ©õp¡Y¸pý9þ¼-Ž>žìG? âéǵh×*EÎÂó #ië"Ï»Þ+Ã( ï¦ÚsaüDnî©ü1MÇQW»e‡&ãûI~ Ëj~q*Õ¸õ¨ll'kêËŽVª¼4Œ ]/|<ÚJ?‘»vjXfËþq4±gyßN=ãiqˆëÊ€c3è|~MDÀpºìt#èè¾0âžš”ÎæÿÈ=Xû\zöC,u]z´Ê­8ŠöíÜN6‡öy¯ärÀšî7îVXåRÍ®i¾µ§0DÎvÝä[Á‰±4)à³Õ5Ÿ8z~Ø/!-æÓ#©Ñ #n„ÑÛºn«ûò9-r§âkŸyÇwÒ sUé`Ý+ENýW~`î;FWÍê‡ÑûõlùïÓ€\Mã G,í:2ò²áZ,Yͳó>ᵄÔίŽúÙe½E'6\ £ÕéWfºnt7á<9ïwÏ¢ºWЦQ'·ú¼‰¢_âÄ÷O¯'~^3œjOÈBÉc›øcݶÂù€¢DÌfpvD‘ë…M R÷GÑœ¾S¦úQóµ#oøá`ÿqeC(ùlæ´î¿"÷Èf¶ìÑÌ(ZtÃΡ²UÝ©úkþÇ'ñý(¦¶+¦£#&Õì°?”f>Ûõ“Pÿkþál5‰"Ÿ ޼£¦‹É‹óƒ›m&Ó܈5]ë )AuE…R'å6õƒgÂöCî³w›–­’Õ4!ºðñ…šnWè¶ðÜŸ ê¶v]x(=ñø¨X¥æ/È/ßÍWw´OÃùÚ©á5­#|è”iðí›.öÔâE fb¡Ô l^ûêÉÂöC®×ê…A]ë©…ç "©vcî‰Vz<ª£á†Í$úrS·Ü!+T¸O&ŒŸÈoC{GR–|ÈÉ #¨Ý‘Õ›æ\ö¡±uG„îïãHË#÷Ž ¥uƒZõ‘ðß§¹V³nî×FP#|+Çm#hĸkQVøÐøÛ,ºàŒ#¥9&ñS„Ò*ïüM…í·±Dl÷hSŸòoá„IxS+e8]¾ºîM‡>tåL/×1N´)äjhÛPáþ¶pÞ€œ£ÿÉ]îÂ)×…Û#ÃéÜÍÃm}è÷ù†©Éî¡kz„Ò[Etgoaû!§[º¼dI{Ó¯rÆÌ0*óånTûPôõÕìên;êcü'”–l?ãIEáþ+rúκüÑøžGÇD¯Ó…ÒÀ}òô¾Âó²£è³Ùe6<”î­äî ÷ï‹Ã=‰B§ºœ+˜`JN¦3»®ÚBVÜôãÇpÚ¾ŒÎ‹(”ê\±<ÒÅ\Ø~ȘÙ8¨¸FM:QgÆÐ¸º/2 dl+§ëmˆªÚÖè¦èJ{d!æŸÈ•¥†510˜Œ·k:„Ѓ7úÜo±MØ_†Q³E]/÷oJ×/ÔžËâ„ù'rqŽ÷vË]UÔ*5¯á È`zêÀÍd¶“Í–nùb’¶®»ne×Pò•9]þn/Ì?7•ˆ-ñ]™èwž~¿nÈ«ìa}vÐïùu—'¢R/§P²6NüùœhÓïçÔÎQ÷j«U RTT«qÀÔ&{v ¿£ógù×ó1r‹FMÛÔìÊYÊm±ï·vn×]öãvѲ)Ó«îÙÒPã H(é§–Ö;ýT¸ÿƒÿ¼Ïês¨bÁÏóÔçq`uU6rùFgo´§Xî1¬½¡äÉ}¼ýÂü¹x›[»†=9M¶Æáyru«Íäæ»é÷yê’… ^\…ãöIó«C_óOäÒwø¶Yòõ½ÂY×nÇóTOõsxû»ÿJéÄÍs„ç6—ˆwW™´³³’>z…tz¿ç ¼óÃÐÛc7__åøýþDzÝ‘{‚*”n§L± Ž?äʾOëH89zsÎíEí6üò²Ý4¿Ã²Ã©“'R×q÷ç™îýÇ}b1rüs™ÇiyÆ´cŽ}ÏQI+»Ö”»ijlFÍgeâF;އf^ 7Õ Çr¿†Ô6o¿î(õ]©iÙìU 9·ÏÞM¯¸Ç1Ih”m̪UC©[×ñ¶Ã„ówä¶ïZÕrq¹é×L759Gý{tïÛæón*û¼ãæ>§‰TÜû¥L(Ù=‘Þsª+Ü?GîØâf§jl8D­«¾{ùî,™* †¤*{¨r…ÞË^*'Ñ“CÎ#JZ…Rlæƒýöí…óä܇pwBy¬¿âêã³tqìì‹'ŠwSÎõöÞ#š9ÑÏ Ç-=û†Ò¹w+|®m®· ×¢Ú”î+gí£ë]¾ŽèƒíöûþÔâ‡ß/*üÞK¸Þâóûxõ'Ÿê¼Çëtw¬dñÄ]dÜ-šºÓ³E~M¥ðíÜ… a»!g¼,y|'ÝÌÏ’×Ó›‡?ßA1£ ~è5•nxqáPr©ÞxkÆï—bäæ]•ž4LUû©oGµí¹ú½Î]à%£Œ õMâg… ¿?˜Ïo7äfõŠú¢éïC5¸ÇÒ°Ýt–woyÏò£ íö®í £]]¸'÷BÉøxÙgá¼9ãÏ•Z)èvÓsñׇŸ£i“nô‹ËØB¹Ë›Í ¾äAnÜm÷Æ¡TÍôIéÛ5ÂsȵuVÙéôJjÕîçHÂý¬êˆ/Íh3¨òîʮԦê…OU>„ßBxn ¹͸'3ÒfÃÛW›ÏÑÏÊ—¾ç#ü®ÈYx¾#„Ö¹7ï’þ…Ïã¯÷NÎ'ÎÑ‘Çß§8÷žÏ™DÚ•ÇØ×@=)?Ó ú9þû4ñ- ó|v«²ù!ºŽ*m­j>.Ú‡”[Æc\Ùñ%1¨ó:î3>þ3y“ºóTaIËa#ú¹Ç½…öD»Ÿè²z†’[Í}ÞaÞ‰\Þ×Wž=—1œÔ¶insžæ¿1°›—/ÅM2}TŒ=}Ü4aé»¶¡4Äø@º0n"·eWôú>½Ö±â7Û–‡¬ýý\–¯0¿š@GºsB…’m§úãúÅu¹B×ßö›™"¾0¬Ï•óôÌÃsÖÈ:[„z"¡Ÿ7çô±¥ §˜……ç}Èyö¯€Sú­ÌÂø éyª¶¸çjM-#æ~‘8‰ö÷›µ%”vY¼ª}Å’ÿ´È¥ŒÜÜpˆïN6Ä×=xöp­/ó7ßyËŸß9$œl~4uÁXÇ…í‡Ü<ãƒþ¬ªñ=îzg¼]µ-ÄÝ=”Wq¤úóÞv6I¥y]v$÷Ø.<·¹¥D¼,^¡_]¾ñÏ=ª0b%[Êð½¬i1æÁjGx¨µó«&³‚N\ëÆ?§+BNë¾£±rÁA¶òc—ÞƒDÁTߦǯí¾t¸ÒÐëñžŽTsí¨„)×BIãÚ©GFWáyiäÞNÏñ°Ófa;n{™, ¦Á×6U¸øRV£{Û^q }î?G|}J/·EÔ$+þyOrCkÜ8-z”,¬¿Y›Lf êMóîïK?v=Q7w§Æadò`ĶCCøÏ§@n;f¯£3ãÏöš†PÚqgíûá¾Äÿžm"=¯ÄýR!ŒŒ‡S²ðÜ&rEÎ/{^ìt’3þp8äÏ~v£QŸäÛ›%ô«R˜ÓÅŽat´Æ[›KCøœ¹Ck…Ûç)Ùé,Ë×U¯†P­<Û&1¾T;Ô»Ý{ÝúP~yü†O¡$1>XÅ/äìüëI˜žbêÁwW• ¥Go'°¯¼…KTç÷h'ü™ç®ùä~®c¿ÝM¶–ˆ÷s·y%§Ùå°mU®œ ¥c[=:̱Ù"<Ÿ(!ããdØîR{SÇæü뉿ùÔ*“gÿ»‰0zZõËžÁn[èä³é½L¢!Ÿb¤Û…R¯N^ûS:ñŸOŒÜÒ¢ó-ö§eü|&Œz¿úÛªéjr©´«Mgâïó‡‘oŒ[ö +á¹[äŽtæî œgrã"ÃÈþzÁž¾ÔÎøC×?ßghUî0~»+ck£§i©X`^£³>²páz€°O¡,UÑËòžf•uü~¦DnõnÝÊÎ˃ÙÀº,O…Óªf…»·NßDÎu¶Žû"#»–>Ê&…ÑáVã?Îø)<òAn³K‘!ÌøøùäÊÝ2:é„“‚TýrÛ_õ¤«Y—µa’0J¨ee—¹Dø½rgÎyŒës7”wv®|'‚.…ióÕu-µýñi¹á¸']Ïý*Œ½XЧQ‰ð{¿qÖ—ioÒš„3ÛQÓ_½™I¤¦žíçzÓÌ]«ýF¯”‘Oz·!a4mü̉ţøœ9ãÏbGE0ãí ‘tUÜíØÀöËéän×>ñ ^ë›}~2 Œ…{ÛØÈçÄÈñÏñD²œ)ñ'âG«éó¼ï×\‚–tÑõ×>»ÿû¿0áþð{¿ß¿ûV³C1ÜÕdãvnã9O#kO&þ÷$aÔÌxâ,ü^9‰<êTÕkjÆ?w£&÷“nYHʦ©ÕV|w¡æ=–LpYFíŒ? S{a-ãÒg&»=ÿŸèö,3áÛ¡Onn n”Y0¼€[ýf.pkÅ·À­ZÃ5Q–ÃbXÜâ7\gÅå°V·ª·ÉjXka¬‡ &ÆU9L6Â&Ø >à [`+øÁ6Øn¯¾¹vÁnØþ°öÁ~8á†8Gá‡'A Ap NÃ8 çà<¨ B  " ÔѱñÀ-FªD¸á\na ZH‚dHTHƒtÈdBdCäB䃮B\ƒëPEpŠá&Ü‚ÛpîÂ=¸x%ðÁcxO¡žÁsx/á¼n!á2x ïà=|€ð >C9|¯ð ¾Ãø ¿€;ø+@E¨•¡ T…j` Õ¡Ô„ZPê@]0ƒzP@Ch¡ ˜CShÍ¡´„VÐDÐÚB;a<òö î»ãþý6  ”+€L1p‰ÁÔP " dRð0¨YT`s rP€Ê@„AO þ @+ƒ `ŽQ~ …r°ÄéJ(3 –6  ”O€L1ŠÁÔP " ¬Rð0ÈZT`s ºP€ÊÀƒ° @¦Åà j(h)øƒN¬­@*0€9o øÊÁƒ¹(¡Ì0°Û€4Pèez0Å /oPC)ˆP¤à:¡ XT`s øÊÁà ”P f(6  ”Љ @¦"¼>xƒJA„B#Ð EÇ ä ˜£I@( %øƒÊÁEJ ƒr°@Ñ’?è„frPÌQÐ$àZ(K8/PB1˜¡ØÙ€4P(~2=˜¢ŠÁÔP "F)øƒN(’V ÀES~ …òžÜºZx}PB1˜¡ Ú€4P(°2=˜¢ØŠÁÔP "_)øƒN(ÄV À…Y~ …r°D¡ö‚Ѓ)ж¼A ¥ B—‚?è„‚nrPÌQà%àZ(K|/PB1˜¡øÛ€4P˜ È ô`Љ¸_ü7ÿ?VàJ(3#ð5ÀÅÉ ˜¡XÙ€7¨¡D(^RðPȬ@*0€9 ›ü@ å`‰BçJ(3=P€ÊÀEP SD1xƒJA„)Ð ÅÒ ä ˜£xJÀ´P–(¦^ „â^\›t¼>(@e`B+ƒÐƒ)Š®¼A ¥ B–‚?è„‚lrPÌQ %  ”€LQ¼Åà j(йüA'v+ƒ `ŽB/?ÐB9X¢ð{ŠÁ “P€ÊÀ“nÅ¿€™'‰Mþ^Wú;Wú;WÒšü+ýwš+Ùû”NxoV ¸* øÊÁ—(¡Ì0ˆÙ€4PÔdz0Å'9¨Àfðl@( €2=˜b0ƒ7¨¡D¥à:a ´9¨Àæ8%àZ(K ¤^ „b0àj Ð@X`•AèÁ®ä ˜c–€h¡,1 {ŠÁ ƒ³ (@e`ÁZ S Übð5”‚¹üA' êV Àƒ¼ü@ å`‰Aß ”P f(6  ” ‚ @¦(bð5”‚ÅB þ  ‡ÈA0G!‘€h¡,Ex}PB1˜¡ÈØ€4P(:2=˜¢‰A*0€9 ’ R¡@IÀ4P æ(XP€ÊÀL S31xƒJA„â&Ð …Î ä ˜£ðIÀ´P–(„^ „b0CQ´h  ,P$ez0EÁƒ7¨¡¸ÅRðPL­@*0€9Š«ü@ å`‰bëJ(3^P€ÊÀ…X Se1xƒJA„"-?ÐB9X¢h{ŠÁ Ü 2°@A—AèÁÅ] Þ †R¡ØKÁtBá·9¨Àæ˜HÀ´P–˜p¿`VþË|‰«óçKçK “¿ó¥¿ó¥ÿ>ó%©°Ïpß+÷Úbð5”rJ þ -+ƒ `ŽAL~ …r°Ä æJ(3 p6à j(s xð-”ƒ%@/PB1˜a0´h  ,08Ê ô`ŠR Þ †Raà”‚?è„AÔ ä ˜cP•€h¡,1ÈzŠÁ ® xƒJA„X þ c+ƒ `ŽÁY~ …r°Ä`íJ(3 Ü6  ”r€L1¨‹ÁÔP " òRð0à[T`s øÊÁÁ ”P f(6  ”Š… @¦(bð5”‚…D þ ŠŠÈA0G‘‘€h¡,Qt¼@ Å`†dÞ †R¡ IÁ´P(PRð-”K ~ …r°Dó%ƒŠ™ (@e`â&ƒÐƒ) ¼A ¥ Bá“‚?è„"hrPÌQ%àZ(KI/PB1˜¡`Ú€4P( 2=˜¢˜ŠÁÔP "W)øƒN(´V À…W~ …r°D!ö%ƒв (@e`"-Ð Û ä ˜£€KÀ´P–(è^ „b0Cq·h  ,Pìez0Eáƒ7¨¡D˜HÁt¤À ¸Tÿ2_’™üïû'•_eÁDéW"NÚqeSßéQLÓÊ÷ÅDM °ÎWýþïݼ›?I™G߃ߌŸ&Ö{’RÆÍ®ªeÎTƒ]Ï?¶=LXÇÙø~ ÿãïñ¯ÃuK½þ2ŠÝ>ÿ°W¥ƒQTk€ï ‰ ^úçÝßÑóÖCß0ºòy€Wä0~} rK¨ÌŸ.Šfq)S÷÷{%ôSI·–s+…¹RÓ…‹y[ÂþÑÏd[‰¸–«¶ô]4s³{YÕÑÑô것»:RF~³«÷®ØÅÜ34%%ª0Ú-:8mÌCaýäÜŸÞïñmy ›ÄµVESÓ”áOo]r¦’Œ?éA‘.ôøn£)¦®¦ ëG w,üÒ£ ïb˜½qa©’´·¿k§E'z~š²öêòòàV¶§œ7}¼Ö@îÖÒ#G+/ŠÖ¹ˆ¡9C3“×¶bK’}®†ÍŸJ÷'N·ÎéNMn”û”Ï÷U 7© EÿüQ,«åRM|§0†6ù­úfnnϸîÉ’ SÉ£×Ù œìëœ\Ö6Iè7‹œœ[~Ò1Ž5ÕÅd' ‹¥›yŸžucÙ^Þo??EX/1\X÷œï3¬En—5ר0Žu(sÙ>–ÓØŽã.OgMoߵʃL­¼ëÈÈpuÜ>ºð±Ðo¹7+ÜhÔ'žÛ¸4‰£øaŸR}J¼˜î¹sþØ7nTÙØ7\XNè¼½D\Ñ.bÚÛcñ¬‰mÇó+¶ÄÑ3¿Þ5¬-`©ÜnÐÆ•".Ô)<0#œÎ?½+•˜ ý‚‘ûñ[p5žñ}õâè€õÜöäŒ_gIJÆ6ñ ÂI“½þî³<þû#÷%p¯é.Y›¶1²´h^<=ïÛ°?`1 Íî\¡Wu²6.pN5tÝ&,‘æ[»N×WßÈŒí ̦ѽ!…m¤„S˜ËÞ«Æò}¾ÅÈåp›©Éfþ¢yÀ‹ ôÆ´" ²Û,¬³?zÞÿª¾§ §õ­Ny¿Dè·ŽÜÁõÉ7Ãg^`\7ϳHtj«ðb_öt’»Ó'OZjÚfñžpÚ·Üê‚íJþ}*›zmó¦I±Ø¡•…•ž´¹HvsÆ\lî´•qݱ/ÜœJq}¬¥»ÂéÜ‘×Ë'Måû|+‘³°ÕõsÅ‹¬phtâÜÀ‹”2%÷œ.ÝM¿fÛÆßÎýÏ:™…U.å¨' ýÖ‘:3Í\z‘ÛI4¿DC>Ü ïvuã×cžL£ žá{ùiš¢è ô[G®qùö…—Ï]dÆåÏ/Ñš¹ 6nÿÓŸl&öÖ‚»á”2&õ€Í)>g²³DlÇ5Z»È&—u=Üì2}œÕ¿éö{½´ý¹OAƒVp+6 Û¹à ®Qð%ì»/3øÄe2¤Ž®Û{ÃƯ‹íLÞÆt"„õùÏ'F.¯ס拌ió+Gt…Ìï}²olº“5ŒŒŽïÜÊ…)޼1tŠÖ§ås2äTVu.¹¾¾ÄÌŒ ]_¡AËZwh»“Å÷T6yí.%ç7òÁ}»EßoGè·ŽÜ&Çí-w¸Ì¦¬ypCwF§÷ ª0ÞgãûjN¦¾þïRyü(nO%üçS"—p(S>ääe¶b»ÓfËFÆO»¸F±›0~Q®$¯yèyÅ)tü·ð2¿Ÿi‘;½ÊdÇ÷¯—Ùß¼Û´ÔÒ%ëÕ ìaÞ] O'¸Ñ¼‚ÛÜålëæþ°Ï*~û!·Û}ÀÙKÎWXƒø“;ßzjiׇ={»û3¾¯¯=[Ðoÿ¢ºW:}˜HÏçLv•ˆ·sm¯°‘eÒ9-y\Jýñò¥?3¶A•º‘qØ™A\›FçWóÛ9=×.¬9c\—QÃk-ôîSÑŠ½Ì².·±+íì^y …g¥¾øZå¢ç~û!wGÁ5cÌÏkYµ¨>IÄ­¦^ÿÝ^–ðúIpÄÉ´fÖXÅQײm5Û⟓!÷ìíšc?_2&þ´úsÆš$ ïÛ¹ÊÝ)û?Ÿ’q9d‡Ó^<¾;ŸS ǯC¨e‹Eíkh“È̲²­câ>vWàÄz‰ My»×r—²Ë¶¹|~ ÿù”ÈQjÕšæh™ÏúsSL“ɺ(bF[“ýì`Üø·•3YÛm²0‚ø>`ü÷©EŽï­eÆeÌ$É´8½pÁÇáûß/ûµŒ[a ûg•ÙeŸwÛ¹…)¦­îk·šr·ãÉÔò¨×è;÷³ÈÃçLå«](É¿0'CîÔè6™a÷’Xn¶çç–))Ô°,ýX’ô¾öÑhOšf&«‹ wí¹Ö…í‡ÜJcÃ’dƯ‡šJ™÷>7<Ûù0[Ö®[—’eÓhʞŵoGëM®å·r糸FkÉ,dýÙ÷&§’j]çw·Ü؃‚^O›~›F~7>Ö\‘Af&-Ö|âsZädÒŸkÝÔÉìÅ¢êZ:›JkD^Ë÷a¿ûušuÙ< Ûï'Áç Èùo:ïßõc2‹KÍ )•J²n>~”}«¸úAž¯'­ø:ÜãŒ$‚FòŸÏdO‰ø5õn9hh ›hl™F[˜¼üµòÛЧ—‡_áT*ÂV¿7 ‚¤C>Úl¾-Èϵª=Ñ7…qÝßìJ£ìòI×¶M=ÎJr"Ÿ˜$¹·Ê_ШúœuÉéF°ýÃÎs3BŸÂÜsòk¿™F3{'5úøú8SɹŽ(®T£žÜÔinmªøæÑµéÂñ‡Üh®]˜y*S¼­Õìk§tê-¼÷Ñ”¬wHɸ¼ù¡zk»ïé×aü÷¢@n낽›ÏHe¶£ó’W¤ÓÎtÿI,éã>]kO)U˜àª KÆv_·Cš°ýÛÛ¶wX*kplϼiéT÷¨ÇgçöŒët8IJ=çq3°:UvsU1ÿ>µÈm|ùñõžÏ©Œïÿ˜A7“‡²w²Ø¶ÜŠù“éXåE6§+FRZ+®a=Ÿ3 w?X±&ÒX…FÜL?ƒ®^­_ñ$Ë­œ’¸¥©+Y\¿6ÆP7’b ±8CØ~þ%âö΋ۚƲçLÈ•A1£º[ÌÞ|’ù×n}š+åpÓÇ‘įwË>r’´]n9ùiìÀ³%÷~eÐè›õ†Þ¯¢dƶ®ÜÈZ|ŧFHròSòøŸ#Áµóh’ÎŽ_¿Ã^GƒT¯Û*¿î°+i³k5Ÿù#‚ð&}o¼Žß~ÈÍ2k]üvj:ÛdÅ-è§£<9׸^ɺàVw¥8ó!ã¾=Áx6T_£Öd>§à>ß Ý‹¤3éŒ_ß]G[ú Úù:MÉøõô¥4ÞlykÅõºó´w¿êëùí‡×mcï³t¶k}ßã½dÒ²VFUy¯d¿ûv? x¿s]f™½Øåêù•Ïi‘;Á-Ø#ƒE^¬ºï›Iƒ’lV}®Ä6…wžêáDo¼d´§IÁƒ6;7ðÛ¹‘¾ÏŸ”,Ê`kÊ{Oô¹žI.ãÓŸVnÄŽ˜-[¸ÂŒe%>‚:ämWmÆçLö–ˆg”ï‘ÁFŽç:CgQ³’}gäÌb/8>‘ªO€"hIÿ–îtãß§9»Œã³_f°:§âNå.Îú©±ö5º¿ø%¡Ý­X³ü2¶G6á¿O1r6÷ê¢c½3ÞE¬Öf·JfÝšA,z㲨9g%4*°½GŸ/t¶ª¢ËB~»ËsY½ ¿ó4[ªq^S\3›Æï´¾—IãšB-žH/{q ²#éÁ}û¨3Âñ‡Ü,nùð£:6”[6Ù5›¾Ú]4­B[¶6Öjz€­MXõðŒe$}i4O£Ê¿O%r{×$ÏÓ1þ¼/›Žlñ²bà æBý3÷”;‘sÙm³‡ý"ÉXþÃøœ¹& WMîUÌd§Û%~ßú!›|JÖÕ $ô¯‘’ëÆ6¡nC#é|ÄÚCO“øœ9~×Lö¨aËÌÇ”C±#\ÖU—1…Y¦ÿô“©ÂeùÓd›H2¶«¿ÀçLö•ˆW÷æâ–ÉÂçÜ[¼}w}n¹.2ˆ½dyã-v¥­sm£ëŒ‰¤ìUõú·ùœ9¾@&ÛõJPÙͪ›ûàWŸ7Aì÷:¾wzÚë;F·*¹…)¿ÝÅÈ=»P¶ù}p&ÛÞ¢íBû¹”br-fn›SÌ¡CÖ½Iy.”V¹ßÚš/0~6»â»ü¡pü!—Vá÷Ÿ¹™,§ö–Aaò\ÊxÖokœõ)öÖë¡_\¹# Ü´E‡Èz’]S½Ç^8þÛ»qÈŵ/2wõAÂrih£ŽÖ7mO1¾ëDÚ^q]Û û"¨Ù½°¤o!Âø‰ÜªÜÐA¡U²X¯ö$Ôȣݣx½|Š6.¨lOn‡"³ËNFýe„ú‡\_®Ýj‹,ö<ª{¥¼IyB_ìSŒ_ÚŽê7n´xŽÛOks—Ü,êrüz÷Y¬«±¡_‰%ù¬xÄ~Uç|¶¥Ëƒ2/?FPÙéfŸÖL¶ßþq÷#ÓW¹ Íbüx‘GòÈŧ*æàxÖá7.ÃÞ4’úÚ Û¹ô”5uºÉb¦Î…Ùoúæ }±¿l‰·ìOKømï)ô ÆOänlçÂd±¤‡·/V]—O“N¾1x_ëEúξžøcüfì×Âzå[žž6}J$¹\çRù—0Aní… Ó£ðz;šr ™ëé„Wô-—UA¬ïçõMwN¤^î-M»DÒú=__4j,l?äÞ;{`×Íb³ Óõ4ÂDþòæÒÿ8ÞgO0"IS5ÞVm%l?ä\»zwPœÅF/xèÉlЮÉÇæ±ZO›Íò2‘^p—SšGRí&ýEÖÂöC.©Bw¤þYLôú¹ÓŒezòlÚujk3Lý‰%ÔÈøÁ"…~¨|Îä@‰øÝ uî˜Åº´ ûµ4@OÝë* b“l¸ÚÇß.âuL„œ±Hý,f­¨~炞Ú-»@Ýñ÷qkKîŽ;ü¦½‹ÖùÆOäò{‰Ö\þ–É\Ç85yWO­S®”ÿø¤dG6f•n:†Æ{qW<"¨à¼Ó‡­³øãV†\¯Q‰}×?ÈdÊÔ§öê_z¡_¬’íšR[ÒlîHÒä1O8nùþ|NÜÌ4ùr“ÔLöáü®çËDWéaªËàíK•B_4kú¶oW5·—t}#W „ñ¹#£,â7Ëd6ÆÆWɵväÄ®JævÔ­MXº5é®nìƒÏ¦+U´)Ì_»ü³ý°•›2Ùñ…7Ûüô¼JQKRley'…ú7Š÷v©ZX5’ŒË×ñó:r?6s jg2L&ߤoºJu«ûXO;É&ݸõͱ‘-yÊs[þèIñ\Û4-Ÿ39ˆ÷yói¯¬>?çŶ( ºJÞý|t ÃIáú„מyF$Õ3|èqoÿ>EÈñ×»2ÙªßoÏdWéîiû;ÍN²ßý:zc–½oW$å>‹(^+Èñ׿tÌ´å²ê[ï]êñIVÃnˆr~–­nò&ãÕáH¡/•pü!7Ó|åf¯p«à»ú«é÷«ô1z€µdÃÉ?×'¦WŸ7ý¥O$qÝUÊçõ¹Ù#~¾V'Ìã è©O³ü¨Ü“l q‚äL÷EVž4-’BVg×óùϧDîA nƧcÆe–ûPéóâI«*ÙªjÞÉ3Ȳó°amGþcž¬Ene w^c3«¾-vM, 1²i%£)ÙïõÇ­´9+Ë0,wËñn;aþ‰Üۙܞ—Áê¹äÝkº €‚ûL²^WÉžMÝ—Õp©„šÍÛ0{͇¡¿„°ýáxŸUo{¸2ƒ™w(íùÁ¯€6<¯c~ëÍIÖ  Ùf{Óñd°{0<ûõ:}Ìä‰ÂùrvYwŸ–{e0¾ÿt…íõ˜Ó4ø$ã×1¶% 6GŽâxçÏç…óä®ZÚ9¢KãûéÙUõO ý›m)¹Wv`eŒ»qÔ}ËN çÈ <\ý&Õ6»Æ‰”ØÊp&µúIöõMÒ¼ÃýÇQ«ß^˜ùóprÍú~˜ Jg“ÖOzuímú¨R¢d|ÿ“ 4°(mA_E$%:rÞøó[%r_õU>v_šÎå&´×H¿o~3Et öÒ·büæ‰dçmIÛ0Ÿ7žçïÈ)Œ“Ò™õ«„§Ú_£Å½;P2ýkÿiŠ‘ô£ÏæríîHâÏCøë6䊹6 ïÓ„ë¦×¨ÁÛ¢/ ÙÌØv Α~ÖµqØÞH ¿<Á)h#Ÿ39\"Ö,õ<Ó$.‰¹eã®Ñ{]ëZC>þéü|oÕ„8ßHu+ý¤j>Ÿ!wçHõ-KÓØË†ÜŠß×èÉ•'³2»Ÿd-SMt"ý»!šÁó"iÙh®¤pý¹:í ¾÷JcÆÝiË5š²5­RÙIÆ÷#u¢”!2ÇI_^¤¬>í,\ÿDnÖ„ŠÇ––¥²çÏÚ•m=yvެm§ò=Ézˆb[]çHÙ=ÌÝ—bžeÏ-Ó=ƒÏ)»â?ýyD*;{*êÓÊøkÔòlÂ#£'ß_ئERÒ Ý­»ìâsJä¸ÙM½¹©Œ_Ÿþ9^Ÿðºû1¼O¡oŠ}Ç…RÅ‘¤Ho*É®Ÿ!·=cTœŸE*ãû¾\£%\ûŽ-'ÿGfs7X"ùË;?…퇜ë‰ãAš‡)l‡ù óý?±*¹¬ªèy’ÛíZO Ö”WfÚGRñæ±oßV®¿”ˆ£‹$s{žJaƶM®Óá“3;åZždCßï.е§•E /êÝ1I=™¶rîtE‰OaÜYb³×éÄ÷ÅÎ'«žd {sâ$äzsØ£î˜Ô9-ò^»†¿®(FîsÔ¦û[ÌSXøš95F]§_Î|dܧm|aåTçÀEÒ“‰–Ók×?‘Kßz3;þZ2ë6F'ô¸NOT6]ÊëŸúX:“q˜”E’úñÁ úð×çÈ Þëxow2㺠ï[vîn]"ÕÏ=ÉVÌ;R+ãýd*iÿÁ+$’.;s}øœ¹šÓk¯c›Ìò•A6í¸.ôáU2þ~Œ]ð©üIùݪK²…ë×ÈM*¬<ñs…d¶}rÏ-KN]§o—_é©d»¹öØSiqø'u“jê{âD­Ýû„ë×ÈóSfHLb wæud 'vËV²Öotí¦™J‡[¬´­ö"’¦»?nœpýúH‰Ø÷ÙšÚI‹“Øœ ·œcò®ÓØ*SKnö bãmèѬ÷:¡è˜¼å|¤ÐוŸ"ärÝ–· éœÄÚw¨ëôáÅP»Ñ>AÌØþû´MˆšködE$•´ä:ˆòß‹¹sëMwOËš%¥|óõ:é·OùÞ.9ˆù}â6¬”Õª š‰y"×Ïɳ]îózŸ–ÕÓuØ=Ĭ’;FØ•þÇùØ[Q‡ "iÈçV=¾æïË(‹HÈuô£eý¸Û  ‰ï³Äb—ô´ìêH˜º\Iw4ißïè…ûÈe¥™K3Þ2öf‘~ʺ!…ÔüÄ{‡ÕN1þ¾–ù6\¯¶/’¶NZSoAŸÓ"7 Å¡ªëk›f²­£¤Þ^óX?Îìãû`8PLL鉚HÒ™rWœ„ûGȽ¾Úüu»„+Œ__¿¶Ü #þ¢SŒ¿ëD;T}þó½F áþßQ|/ž÷Žkp… ÅYRÊêB’nœÖlÈ)¶Ó¬Õ‚QÎôt!w£RMøða»¢øû\"äΕ)o._t™-2 o´`w!¼»É­û¬S¬R{®#ª”ô¹¨}MµÐ¯J¸ÿ‡\EÅôüÀìKlf“Z‰§ iùȦUZœ8Åž½ç.¸’ÈøÕB_'áþrç—ŽêÜæKsüZ}vB!í9[×Crÿ”Ð÷Û¸.^•ª©ékìöŸúáþrqW.²òà.}Ͳ éÜô¼>Ÿ:œfîƒ'Ö¯?ÇM8®Ô4ÉØ0ZØ~ȵ46¼Àºür’x¯®îØ0R³ð4ãûªºÑŒÖÜÅHš‘ýnyÓŸÂý?äN]m™š×ðKßÞl\JY!% ¼¤Yôi¡¾»Rq—¤ùŒ×òÏ-é|΀ÜÝ‚¹6w¦'²òû¶+æW.¢uuÇïqušÙу—E½'Ó»C¡­=Â~ÖmJ·Z{ùœÉ±q“ë 9Ï¢5lÖæq½—4."+Qy^«Vg˜w¢ß›Ô0ºÐ}oÍ<êtNÛ†nÅ퇗€9ßXaGû×+-ŠH²tüßágßÜ™äm¿Æ}¼ùûèbäÎ:?²šÀøþÊE4³ŸÇ„egþ\_j͵³¸IsO‘9[÷ß‘Ëkv)2Ë7ž53þ Šˆû\–ža¿û9qW½‹úà ý{ºkò¥Œ8–zküPDöÃÛ­ûµã ãûô9Qh΢ûõÕBŸ6þõ”Ƚ¨nѦR )tq©2ï31¾Â³°3ìøø1&v¸Ð͸Ëw÷µP×½ýÎ;¡ÿ÷½Œ*Œ\6,–oO¯.¢{›/9Ã:ͳÐWíJíG~ÙÔ¯j])jþþyr¶¨j7WŰÓC§ØlÝZDèH¿Ù}Î2|H ©Sh”]« ÙuÕB½žŸ8ŽñÚØ—.šm[WÑbô¡"êºîà³gYç!^+îºy’ÿÝ*¶5º©éí4O¿ãWøç5DÈíà=¬Òó(Vü¾_öÌÓES{Ààðþçþ\Ÿßó êdÚ$5-ø•e»’Ðÿ¹é·îßlÅúé׿Ý(¢KÜí›çÜL§%Æd8Þ GøïE†Ü¦Ð!Õj¶?,åªÓ…"º°ñ|¿%›Î3—N³-^{O§ÏsVøSž,l?ä®î˸ö®R$ëvãö¬.©EÔøÄÕoóZ©˜ñ4Y<ŒÃZ5mo3ðˆÆI蟂œcóã~ë„3c;˼"ZsÀíÌÑÓ*¶cëçÒÝ2âf»[Ë#©_t×îí>òß‹9“Ìe¥.…² ³’oÜ("Ó¢N‹ú× f»^”És=H*'ÃÆ jª—ö¨¶å{¾¯Œ9þü&„¿í³fæ½"šz±þ`&—ì;d“¹sí¸©)î w‡Ï™œ(¯Š>Öô‡—Š…Ÿ¢¾ïÑ«YAå;O³zŽÜ™±+ñuCM)õÎLzé%ô¿AŽîç{9‘ûOE×äêŠqwƒYÿR;TNW’rÓ` 5 ¹™»s}_>'Fn×¾¨ÚY¶,®Ñ·ì×E¤÷ÎÈS´a£;,¯xÔÔT {üŒï¤¦…÷¬:ç¬ú‡!÷ú…v`;ÛÓ,)+hTüû"ŠžÕµ`NSgŒ´YåNÜÕ³¼^jJ]9­hGŸS W+«ïQ÷A,zÆîÇ‹Èbß—½óYÛù.'Ûó³ýîãÂÿ{øœ¹n×zÖ[tóœÆ—*|."‡úºÛ&mCYõûߚǺL¥ZÏÝž,›¬&þ~ ŸÓ"'®ÿéUû¶Çÿ.”§ÿ e”PcšrÞT5µÐ4šßNxn _¯ßÕ§§wüyK<Üó#”íO÷jé4^öçusm“ÌøýË$°DlÿV£j\ï k~¬eð|if<]ÆFsíÛZËh•ëzC— jšýÞªÖ“Há¹%änV±)ž{Ùþ܉h}óOpÿQƼ¹ödï§]MM˜T\<$wÈMîgºôâált["WÜn•Cÿp¦­¼ðn®ƒmeç]¡¦»¯XƒÏÉ[f]ª´³òa_k©œ~RD7“ j´ô gjÏq4Ìp£jiçZ«©jƒ —?úNþÞŸW³&=BÕM EÔvÝ™‚#iáìÚÝåýœL'—ï:Ti¨ZxÞJè;…œé°ª]íãf1ë¸$îJïp©kÿ5œñýU¥4·±×Jì'–^m*œÎú¾!çeì5¥—=-R<-!¿ˆ†Õ[ý²eûÆ÷ûu¡ ÒçøuQÿ£Ÿ¹¹¯íz˜ìZB¾®ûõ$­ˆÚíüºÁÒ.‚]ï:³i‰Ô…¶½ž|Ãk š†åõõü–)ô[M“¹Ç«ÆÑײnÃG‹£ØØÏKk¼‹ñ¤…n•Ü0nF_ meYWØ~È=ýù|ñý¹géyÄËMIýŠÈ®ë²†Ek£ØøéÚA»§R® `c5ñ÷„~µÈõȾ1³|Ây y¯*gíŠÈÉM¹ÿ@Hãû ¹ Ï+bœà.Ï; ý2‘ëb¼1,ôó+"å©=¹QÆéN¾«öô¯úÓ }ûëøãrÃV}C©ú·õëý,¤säñ _D1~œLeöÆÆÏT“8åX?ï©B¿Zä*ø]\ݨi8Ý8;ºtöóBZ×áEvwÓhæ¶¶†âm-WÊøäª_3]MÆûÔž)ô«EnŽñD)’ ¬®¿I.*$ûf±Aa£?/q£ÝUËÛHÔäÝ%yüh¡ß·òwŸp5qOq¬H*¤˜Ô†Ÿ~J¢Ùe«·…»KÜé̃‰_0¾§¾¼%c½Ðï;¨D\wm-çãs£(øÎÈî¹á…ôà`ûüë¢ÙÀ¢Ž¬§ÐhyoœÔÄ÷éú}#·nxÍêæÑdlßPHîkµïŠ"£ÿ–ÞGr'æ…¢¾ÒѬy «TuìIç«SÉó{ß×¹‹ÔB¿i¡o&rsç´¬Ó#ެ>»ìùæ\HÞ Mè:!†=ç.ßüšBÙ·¹†ÅjâºÚVª(ŒŸÈ7S³xZTo3¼>8¬Ôê×Ç0~Üð ÷agÝÇvàï«u9Ýðê}üЧØÖÛ¦t)¤U÷­½ìU1ŒŸÏ`þae¶¯Ê8ìׇÚì^¾X8þë ßÝíE}™u¬ùµ…d{½æ°Ü̘?ÏÅäîzVÝN-<¿"ô‹>U"–/¬hhXDíF¿_§»Ï>[÷0†]2u8`÷Ð…˜÷‹š–‡MZ}î°Ð79OccºDŠº;TüçíÞg›7IýÃø¾òÎä[ýô䛘{ê·öð$áøCîxùÜ1‹w_ OíÕ«å_§”Ë>6­ûçü»vÒâ¦ú©ÉØvø¡Ðï9þ¾ÙE2¿mŸ&šë”Ô«Ù÷†±ŒÄ™æç¼ðÑãà†³ó¾ ý¾‘«X¼ÒoÔ%º\aÖ•Í'¯ÓÚò]5>wˆe&¿ÑÀÃ…6ßZôjäX5u_5¤;É…ã¹7é€{-/}ï:ÝTLï"ÅùÀïãö‡×Xõ£å¯ÊB¿oä¾ ²?øæ2©Óo.«,¿Nu[<ø¹@Ë̪®©{ؼ§téÿd¹šøëB¿väŠjÌ—tE¸uz”¯»'–Y­¦ÖEN%c»Âejºœq¤ã|¶ßéñµÛ'wlÙÉÈp³ÕåîÓÉ/vgƲòþ?nîièIsƒf,™;[Mü~ ŒŸÈ-üÖêã®Z:q¹Ï­ö×)8+üWçšqÂu]Oš³ªFØ?{ßZ¸i‘Pÿ{gºðÌü½Zjå°§îØ~ Ú9NÇØ;ÑÐQu<©WÁŽ[?0~òÏËõ¹×ÑSW^x¯¥“¥ÜßkT¶~cÚ–8&3> 8•æì˜·¶ã·HºÒ]RÓ[¨È]Ê\t5sRoܼF;hÕ.1qÌçØÎ.)·?×An|~ j“%ô=EÎÒ¬²N•D£N¹7c×è]‘ãâaú8f||¡È….IÍÛžŒúc Û¹ÓÎWn˜%S%ã ýkT3³Á$Ýý8v4?ñöÇGáùÄH:/å&rB¿aäö Ì/>S".|¹ÑY¦K¦'«¸#ñ%¹eû,ŽeŒå.È8±]j5][«b×­ü¸+BnÀ|·7E)Æ»†ß']£ ŸËš7zÇž®p©J}Gr¥ÉTõVÓ³ÎKü4ùýZŒÜÑ} &­J¡w¹{¯Q:µuOøÇŒc­p"ß*Ÿ+µ2WSî ‡ßJ& ÇrÒÁÛ—^¿šB?>âìš_£—³Ýœ«Æ3~ÿw¡²Õôo°ýø¾½BýCÎx›­S*}êÌÝ™. '»ûfšÇ³5Æ’wÕ³ÓûH:¼ìÀ曕…ù rµ–nê?pm*u1>xV@Ç:f”ïèÏš€•’ññ*ÿœj‘›Ò5èbû¼T2f©ôÝÆíœÜ>žÿ.—’2mRû©ÕÕö°<¨ÀO˜"W3vðsQi÷…J¿Ÿ+ m©öɇ¼âÿ<9n÷à³›?þ¾_(Ì_Ζˆ=æ»v®¼$>ÖŸ~dý¶TuFäÊ ñlã Ù'&_tO¤.{úŒ%œ÷!×)©yJ›F3¼˜ç1ŸoiÙ8¯}ñÌ&ÉAwpµ3µ=pùìñHÚudäeÃ5á¼9¾¯v:ñûGUòµ\ð:0ž^Æ]™w¤ž½=;ìIüsæÂyr7ÛúÕ#˜?W'w/ Ǿk0ug/ï¶~ãÔÕ¶‡f]éº)’¶ª’O>Èå?Ÿ9çnë¤Áéô°ïÈF?kТ·e•²'ÒátW¼1’z·ÈŸ-¾ÌJüýá–Ü•ìtr¨”ûôΫ«tr啯ׂâY/nÝl"a¶R¬¤_ê³c† óNäúÜOšÐBœAÝM¯k‘{•ÖÜw8pîx<[žuº¹çĉdHü,Ï\ùy ¹—óuÔÇ/ƒnâ¨t »JcÌÊk{gÆÓ¹“È}OòG ÷ß÷)„ó†s%â3_ózÚê3(iÏÜâ7Û®Ò1í•§Wñ=¾:>k㊭Ž4˜ŸX÷«Ž³=„q9ããvæ:Z–ŠñUÚ̸>þÏs°ØÙOMu‹¤:~f.y÷øœ¹ÒM_ ôÐw8côU²rñÕ iñÌøXHGWò®n¿N´*’"M&ìkÉ×urþc¹þt4wîMשí¯Ò¹¶Ýî?‹g?.[ZtÞf>êörÎÖH öã¬ÎבKZúæÁ¾‡:âï\¥¥ùí^4J`NƼ§Ð‡pßÉ^‘Ôé¶¶¥Ê?_T"—Z#amû™tz«QvWOïv—_ˆ•À6?1&í—pÑ6‰üG_d-rœ½1kz&eô¼´ïQ¢ž|»~«ž,Oøó{Ï»Þ+g„Eоö¦.â á¼¹JW÷÷ʤ祚f7êI¹!ÇÜr_ûý;¥ô¦)Ç•ã#h¬íjs×üç39qŒ{<ëN&ñÏé饸é†JgØüCÙ£Ÿ~w¤9Üt¡y]ÙbÐì<ÆïÏ"äv*{íW5É¢c{‡¿n;NOóºçX>ŸÀøþ¸É×mؾ}­"èÃO²°êrí”ÉõÎLÊ¢¯uM—uÔÓ7×ÃÍ‚N&°뵕*™N Û—·î-Aüuaaû!\p£ÞÖ,rUÞm“f¢§Ÿ ªTº²7]³Ï}\uÄxjr²¢lÈæºÝ7þƦ{¼9ͲœUéW²ˆëJÛ->Ÿ†‡nªviS‚ð<»=Ì»Z16<‚²¹Çs+ ýÚ‘«g¼!’E•³ëõöŸO-[ì=pde‚p=CBõö‹|ŽíÀÿ~E8þ«k¼Pš-ü¾ Ÿ’?æžj±8áÏñw>'wÀ©5$1?0§o¢0oAÎWÆ=Y”-<ïŸG_g{†|X˜À´Q³qF>‰¸§ûìúGÐÔ7?½#]…y‹ªDìtêe÷Çk³©â‰±ÇBÖäQRñΖ‹˜„{’})qöGÐð÷W^œ"l?ä.Œ¿iÀ™lR?êÒ˜uΣóóO–â}6kRÇgFÊ⦠/Â)~Ì"Íž;ÂöC®õçð¢mÙÙôtÏœÍnçÒ›,îFk³ïyPï1‰œz™B+¸vîM„í‡]8›Þ°-í±-—^½™¿çöë£S¦u»Ös"¬eâtG.<,l?äz;Yv nCoÎ§Û È¥ÕÿÞa^ ¬MÅ:A¶¹ö$?ØÉlÅöð^wAn‹w#>‡Æ Ø6\ö8‡^ºæXe2r{ãßvêbGÃJo†}u'¹{úøÚ}…ã¹IÆ ‰9Âïs„çå˜ññ¼ÁchZPQß7Vá´eÉ)ß|΀ÜÁëÕ½ÚÎÏ!Þå¶á94bjaì“¶x½ÈjûKŽ ‘,üV¿6áÂsìB¿öàq¨÷dDEÝS5:÷&›ì®vXÙçg<»ÞíêäYÃé÷ùkÐØic¿ u/ø÷s”9d|üðD6Ù¼|ø}Q<?׫’ºÂp2G­Âé ¹¥ŽP÷ã÷÷ª½p^Í|»lŠØ?Rœ¯Žgw”ŸÜO´¦ ó:ÙZ· § ÜÏ‹z u¹ݹë9Ôx‹îÙ‹/YäûòÒ®ÜÝñŒÿ}Ò(ЏñA©lN±û“&¾{.ŒŸÈ±‹; ÊΡ!sÇù%žÉ¢Ø0®¡x¼pþmK}»Gõnö%Œ¦/p Û9é“ð u sˆî/‹f>Ÿ1ÕkF<›ÓÒi¼Ùò ´MÖÂ}Г0:&Ìq[.ôkGnN¸×Òã·s(àKõ%&Ydóª¯_Ú”xÆ÷¿ž$<÷F÷&_|e½T¸^Üõ‘'÷ô.É¡ zóÝEdÒ·êñù²xö&øø§ÇhÓbù³€÷aÄßÂõêÔ1Õ÷y«°e||ó5gr&5Ýnh¿dV<‹ìÈaÞ9ý¥ÏaC§Wæüþ"BnùЫËTˤ°¶ZeÌg¿ûÞs£t“aYÀ]Xäÿ^òï¯otÙÇ8Ÿ7 fÚlÌÖVoÔbŠ„¦, £ØPÿwûçò9r­ìeƒÂñ}œý.[¦#Ñ€ÚÇê»Æ³eS¦Vݳ¥ßv­¸SJM+;×éÍ.ru¶žÝØß?OG¬àKY·áñÌÃüÙ¨WëG‘]dnºmN(ìÙâðä‰0n"·Ì}×K[lï•Æ’d|ì®e<ÃIY³Ù׆“…qâJß¿s`áz'r÷¯øöB•7å  šÝWõ2äSœð²˜’¿…¸«¾‡’ó´Àƒ±%Âù:rkÆNÝ’C­~ ›X7ƒÙHk ÈcV ®ùJ §,I#‡gÂèYÍ6Olê^h‰xì‡ÞÛÂqZÒÊ®õ…tÇžU¬Ç W9~¿_oÝ<ÐõRö¨0á:–pÜ!Ç×™š“~êÙËérØGÍöbÞßC¾*¬éX:ÌÝVF÷χèÅÝø÷)F®‡Ã~Cí9ØŸWŸz‘Œy§á|•í1ëâcEVç~QF¾Ö•[XÎ÷{Ñ_õ£È>‡¶~Ó…MH#¿vÙ,Šc}Ç-Þ|ÉžøçqCÿq}UÜa‡ŠÓê÷È¡ðûÜâÒè…σofÄ1þz¸„ä#vÞ¸Jü}[aû!7Â8 æ­liÀêi´tˆºûx·8ÖX¢:¿G;¸§×¬ ¥¹ŽõÂöCŽ?ßÊ&ë¨F8¤Si±ñ†r[–atöF{¢¦S^Î ¥ŸÓâg8ùÛ9îngyR63sJ¥Ÿ]»Ž·ÇzåÝ÷±®<ŽZÏêyÛÞ-”ŽFy ÜTØ~a%âò3 ªŸ;šMì«Üyÿ=…ú ë8¬{ëÚöΠDZt¼ñ¥Ò®6¡äöʳgC5¿_‹»WiÙÔ¼ùÙ4cˆm‡!A)”\m¦ž5ˆcüõÆÑ4Úx"J  ÇrÆÇ8‡e Ï}§Ð—ÑMÛ|Œec*œ’¥¤Ö]Ëø½ !QÍ«mäí…û È•LÞ´SQ#›ß˜[ñU2}ôˆ·þq-VxÞ}ù¼¶û‰.„‚•§ž» â_Oœ{›°ôúEYÔõsÅë½É乜*ÞŠŠeão³è‚3#éãóªÝCȫʖ°……ûDÈ©¸ÇL1/{4æûö~Éô™-HXx –½"ÿ¤C¿e¿ÎL ¡ÊCæ<ˆ®!l?äŽ$jGÍÊŽÏ$ªæ86"l},Û´Û9îÌéq´Duß7üfÉ"FÕÔÇóû™¹ÕÎÛYdÿ¼CEÔK²Ò,‹e‰MjŸnHS6_ùðéiÕ¼>­ÌòÕ<ú?±f÷ßµ•þ®­ôwm¥¿k+ýWk+qÿx û÷½qÿnP€ÊÀ• @¦´Äà j(1)øƒNЬ@*0€98É¿ôÂý¿©Üßþ&ÿ¾þ&ÿպݿסüÏë*éþ¥Üÿ„Þ&ÿŽ>pÿ«½Mþ]½àþöÇý;7R˜üýýŸý»æEÜ8#¶7÷pY øÊÁƒ”(¡Ì0`Ù€4PÀdz0Å`&oPC)ˆ0¸Iÿ¥?îÿM}ßþö3ù÷õ3ù¯Öçþ½Þd9X¢0É @èû»çÛÿ„^&ÿŽžoÿ+½LþÝýÞ¸yÍßùÑßù‘Âä¿ÿüèïÜè¿ÇÜèßy݈'¼…mÊ}nîÿ“‚?è„Ê ä ˜cÀ’€h¡,1€yŠÁ ƒ™ (@e`ÁMö/}qÿoê÷ö·É¿¯Éµ·?è„¢äJ¡îï>oÿú–ü;ú¼ý¯ô-ùßÑç »Îßù‘Éßù‘Âä¿ÿüèêõ£ÿIó#î8WÛ‹û\øßdzî½`€ƒ7¨¡D°¤à:að²9¨ÀæÌ$àZ(K n^ÿÒ÷ÿ¦þnû•üûú•(@e`B$ƒÐƒ)Š’ÈA%ôÂýÝÛíÿõ^%ÿ޾nÿ+}JþwôuãæŸ=ú;?R˜üýwýO»†d%ìoZá}[âó%s¯…AÊ 2°À %ƒÐƒ)01xƒJA„M þ 7«ÿÔ÷ÿ¦žn Ð@X`ð•AèÁ±¼A ¥ ÂÀ,Ð ƒ´ÈA0Ç -?ÐB9Xb÷%ƒtP€ÊÀ¼ @¦ìÅà j()øƒN(V À…A~ …r°D¡ð%ƒІ (@e`""ƒÐƒ©¯Þ †R¡ÀHÁtB±±9¨Àæ(>ð-”ƒ%Š‘(¡ÌP˜Äà j¡ÿíï~nþ Š–ÈA0G“€h¡,QÔ¼@ Å`†g Ð@X àÉ ô`Šâ'oPC)ˆP ¥à:¡0ZT`sJ øÊÁ…Ó ”P f(¢6  ”Šª þýÜ@¦8øÅà j( ·üA'q+ƒêS?7±ÉßyÒßyÒßy’Öäï<é¿Ë<ÉFØŸtÂû²9¨ÀÀý»0HIÀ´P–´¼@ Å`†Ì 2°À€&ƒÐƒ)7ñê}+Â`'Ð ŸÈA0Ç@(?ÐB9Xb`ô%ƒIP€ÊÀƒ¦ @¦@Åà j(T)øƒN\­@*0€9[ øÊÁƒ¯(¡Ì0Û€4P˜ez0Å -oPC)ˆ0hKÁtÂnrPÌ1 KÀ´P–à½@ Å`†ÁÞ 2°Àà/ƒÐƒ) ¼A ¥ Ba‚?è„"arPÌQ4$àZ(K/PB1˜‰ðú  ” Œ @¦(6bð5”‚ÅG þ  ‘ÈA0Ga²h„¾·(T2=˜¢h‰ÁÔP "1)øƒN(hV ÀN~ …r°DÁó%ƒŠŸ (@e`b(ƒÐƒ) £¼A ¥ B¡”‚?è„¢irPÌQD%àZ(KU/P‚LQ`Åà j( ®üA'_+ƒ `Žb,?ÐB9X¢8{ŠÁ …Ú 2°@á–AèÁE\ Þ ˜£¨KÀ´P–(ò^ „b0CÁ·h  ,0AèÁ“1p+e«ÿežÄÕ÷¿ó¤¿ó$…ÉßyÒßyÒy’TØ_¸ï”{]1xƒJ¹¿Å %Ð –ÈA0Ç&?ÐB9Xb@ó%ƒ7P€ÊÀƒ @¦øÄà j(B)øƒN­@*0€9I øÊÁƒ¦(¡Ì0€Ú€4PPez0Åà*oPC)ˆ0ØJÁtÂÀkrPÌ1KÀ´P–˜½@ Å`†AÚ 2°À -ƒÐƒ)p1xƒJA„] þ w+ƒ `ŽÁ^~ …r°ÄàïJ(3P€ÊÀ…A S 1xƒJA„¢!Ð Ä ä ˜‹ðúàZ(K/PB1˜¡ØØ€4P(>2=˜¢‰ÁÔP "& øÊÁ…Ê ”P f(Z6  ”Š˜ @¦(hbð5”‚N þ ŠÈA0Gñ“€h¡,Q ½@ Å`†Âh Ð@X PÊ ô`Š¢)oPC)ˆPD¥à:¡ ZTP f(°6  ” ® @¦(¾bð5”‚ÅX þ  ³ÈA0G¡–€h¡,Q¸½@ Å`†"n Ð@)ˆPÔ¥à:¡À[T`s| øÊÁ/PB1˜a2` ÐüËâðßý2…þ3ZjgõÈigp¬ÐÏÈ•Úõ^û~rmë(Im»…_ŸB†\˜*©o­Ö™tËÊø‚t¶v·—uòbÿ¬Ó¸Ÿ[VOBm%\‡-aä& ÑÑfßý}7ŒcrUbsãm,«¹æÖƒÈLW:ĵ“:Bž vž(«/¬Á}¾¯·OîÐQ8ë`òëÀÒ+ _Þ©ÇÞrmLIÉ;vqªëÚºg­,'¬¯ŠÜȳõú|¢£ôÞ?ê¿-ºLz›û¿ÚÆ1+Ÿ.ýÖ(H;ÙöÐ~IÈ?Ö3 ·Z"Ùîü:ƒîŸ½Þá¡ùebé´®Q—8ö.¾`^LÔ$Zf\°v›@ËzöÔ ùÇz&2ä¸ÕHæœN§\Û›Nèî©M«ÖW‹c^“?ïñ-•PÌšwjɼ›H8)¬s…Ü®m·}:=]¸ùÖ¢D²©®^ß¶JûæV±lBð$š¹GÒçÌöªÎ-+¬×‚\;ãBeiÄu;ȉÒP"kÝnTõ8V¿W‡}¥3ÉÞ¸ P ^Z·úýoÂzÈYõxx®êÑ4â×kO –‚âÆ4ŒcA³cž/s¢#³;„—o !ùÈÏaób…퇜åßœƒÃÓȸ õ¡?)¶_§.Í‹©‚qÁ²êj×xV°NRd‰xÔqûèÂÇ©ÔðÙzûáëãi¦®Vä†ÞqŒ_Ú™¾pËÙ !ã²ûÍ…¾ ȹ&6 h´=•º»;{öÑÆÑY÷®/l(îOŸ ãr@ÍC¨ÿäÓ­N]å_OŒÜ¢"Ã:§©är¨´[ŒiM’¾ï~yL›Pú±ÓÛDa]¢â׎?ä\dºçÎù)Ô1$¡j¼m,¥Õ½íÉØ8V¡_Üüðž“H^íù1Bˆë†S8LX/ ¹yëöê-M!‰±±y ‰ç­ÿ~vTK˾–xꀄ::ʽ‡ ¡˜—ceKëë"ǯ/›B«ZÝ9w;š2ÛŽ¿TiH3¶¡”Ù }?BèÆMÿ ³Ì„uÊãºTïK>G4è?ö íŽãoÖÍýÚ'㨱¯GáeÁ¤?Úz܉Â:eÈÍ7€É´dþÐV³¢Hâ>2«JË8æèä%:”;޲æuXv85øëš¨ñ>û\š¼ùs…/à:Z©éÌœ«×‰cóºp+‚ÚÓÊ‹S޶Õÿ£O‚9ßW¦¯ @nÂÀŽ;¾E’}¥Ð=ݰ_ÿîÿøÐqžgã[ÁįG%¬3‡Üü•uõë&Ñü*ãíÂvFÐS·Õ{vWûÓßëÉPnE­`¡¯›°^rü:HZzPí©CF—pZtûܵFqÖ±·šWv¥K“âº%eÆOî}ríz×hÉ¥ÆF÷](}ÓžìêÖ/Ž•rm4ǸÓÞiŠ‘ °"Wq„í‡\}nù¦ZÊáÚÏ !›¸#Žë§Ç ë”M%MeÃÉIB„u•ùu´È½s[Ƶ¥pÉ™o«S>_f‹c7qd4Ìõx¯œê!4ñ«µnÕ/aû!W3ß.-ýÒñ f¹\}ž”Ì£/Æ }ºdTwzÁ}»Áô±Ði£k-aûE•ˆÛ[·+Öü¼L·§¼9çv޶¿ï¸\S+žñë’M¥¹ÆÆSÁ´=7#.hš°ý{ïô.ê¢ø2Ý‘üùgÕ³Ô ÿ»žVã¿“;-^vðuDL0ñ}ó„uæ«•™™¾é-»~uq¿èÓÔc{ʰs®ñl¸18™'uRnSÓã€n¯©·pü!×aHГ©©›±áè)Ú>àFá6y¼ÐïÙYX<˜ÖÖá:‡ ýéý|oÕ‹ô¨Þ››mƒHšµk]{E<‹‰ªÿ>ìŒ%.Þñ%1(˜ºœÚáiª°ý«2&õ€Ír4Ù9[¯?ISë¹tØϸoÓ,܉ž±|³ø\0-0. *ôEA®cÿÖ7¶lO¤´¡\gÈ´iõîv½ŽÇ3¾¿¨³°nn0q«ž·\*¬Ó‰œ±@ކ.Ŭ¸íûä(y-ÔàQT˜‚¸å Z Û/ºD,m‘÷}n -ª=ÿ lq=Û³úÈ’üxÆ÷Gp¥õ‡¯ÞÞ"„>nXåëHaüD®“剢’2¶¡h|ˆ‚Þ7»8òc< \ÇÜéÖ>§zØwèóã Öx ë]!×óN¢eÁŽx⺧_ÜOvCkZ¯jÀšÞ¾k•Õû¾¨Ãàþ!Ä N‰ÂzWÈÙLØ;"57ŽÕ¹1·îš½dr8~áÀñ ŒŸ=èñ+ÓóUzb^`ìK ôµA®êñ³3V£Šc*Öî¿|79v|ÑñéêÖ¹ôÓ¾t/®ÉŒO]GïÏv·2ú!W×&ùʉ‘±ôãô¢‚¾Û© ñZºu&ÛHÿã¸ÝaØÐõf”З¹&î™WTÄÐg“»‡ûÜö¥Ç/nX÷ËJø3?köåRIð˜â×ãæsäZðEÓ;®ãñFº»ÉdÙûÒfõqã£#&Ó>‘{ßÕ!´óBѽó!B”˜qN§ÛãË¢ˆë†kûr5éz„­PUÐüé+u™[Ž÷H¹7ï’þå˜Ð%æ÷:²Qä¸;³äg»¥ÔsÀÒÓíj¿Žµ3I.¶ößBâ/W^ú£ wñy«o“ÕÄ÷÷šEª¸Mn§aüzðNd\Þm|Õ5î ÂöCnDsEm?³Hz{ñÙ¡–%è-»¯—†%¬ã;Ó~sn¡À2¶ÀçÈ­ ¸<ð‚pªµþ‡™Û{áö0 ó¿st™[[ÿ)ÿšb8æÛ#úLˆè,ô%B΂oMlº÷ËÙQ3Ø7ïÒ‹n“5úªOÛáÛfIq]Z·LÆO䮿_ØÍ¢V%ÄD±™°˜yºÒË›NÆåûbPÇ"÷Žúº!7dKÆúÆvAB¿Ž­ìǶbÕ“Dfáx}³édœþGιæË^{ù÷©@ŽŸ’±­ÝÊílë¯üK+{^`ü~îIÜ*vcüC(åíÚª×ñß‹¹ýMu½Üÿ-‰³¬ùj'ëÜ×UºqÞÆâ×Í?:•Þn~<ý`ñ}2…ã/ö÷:|‡…u´÷°ÏâF={Ç­I³³v¿„¾nÈq]P?/Ù@3­ó«÷³a/ªj{\ü3~V_Àu  !nvuOèë†ß÷h! þY³r…ÞØòÂw‰™S.2[Äúùû¦RñºŒ²âüÊåÚÏ]¶r…9®5l¾µfõRWYm|v€©ýªæ]9|‘é‹’’î¥Êhxâ‚û§†Ðó•žóâß ã'r=5?¨DÎÎ,ø¾-kËAù(ß}\ñEöŒ[Fú'9ÄTsÝØ&”*ríâw}ù{2™ëT¯`-¼¹Î¯‡Ø‚Þä-/ ë¬N£Í“¸†Ž¡´nõülEŒÐW¹G=’>.ðcü¼îëÙl×&3.1¾ßÜ4êcü'”FrÓW|¿I“øqß@Ó’!ØÞ|¿æCìÖ!wÑñK¬ø:·`§'ÛQZ…ÒŽ©ÏÖz }1‘‹1MsÖÇ`¿"òì|˜½³r^šõêëóàð×UžV1Î|ȸPjÌ•íÆ|NŒ\`­Ýû\ž°ˆ£ES³³®Nâ]/³UC¶>)£J+•ö«–…’÷õ³¥c…¾˜ÈÉö÷{Þ¦Ù öVÝÙÅû0ËÚO5fÆå?ýQ>4:ÓtǹPŠ¿bã•(ôÅD®OóûöÆ(Ùñ/…-D—…óÓ)tûN@ :5”zú)âï-NØXæœbM^«Ý¯sIÝÏmUæe6͸ðör|Ù/>”V?ÈóÝ;E臉¿nî¶?íǘs#³‘¹£O,úz™mÏ.8÷| q«Ë×ÛJfÙ³êŒí.l7äø>-çXí]6YÕ=̆–ŒVlëq…M06˜–‘McËn+C©ÞÏQú€±B?Ì„±ÝÓ”˜ÊMƒÙã‰É!:#¾™nÎæÙl˿ΞôÅ>gu¯¡”͵QûÁçDȉç¨&â܆MÉuÒ9Äž¤HíÎE^auk‰ÖZ\Fç+§¶wÇ<»zc^YØnÈ)«ÖÚ7,œÕM‘2íúAÆŸo0†“ÎþKîΠÀ„¨›{Ÿ‡Ò”=‹kßúÑ"÷ÀóÖ€Ï.‘l­±qÊAö¶ñéê‹æ2v«2×i-06p ¥6¢©ÖU¶òýZÈmðñÔî U³gÓ‡‰ôG0·‡’ï1¡o§—pÞú>ÄJäæü (:Åʱ—fìg—ëužÙCË~¯ï=Äþê^÷Ÿ¡Ô‚»Œ%úA#W>±ö·¡QÑì^‹ˆÂVŸö±_–åÏ4?3ÄtÿùdÈ­pàÎ\¿>ðÖ®°EÚ=ËS¬‚±Á§'Õ¨{Ôã³s˜ÐgÏ)ãûÙi؃×awWmcWÊÞ/ >Ÿ³¹idäÚƒë4FüüNèçÜ×.‹ÆX%²ƒ˜%Lüÿ±÷PQm[Û.Š3f̘1cÆØËŒ¡HŠ3æ2cFL˜K1¡Š"Œ"’ƒX$E0`ÆŒù¼³æ”o¯sï÷·{Ûÿí¿íÓŽ«µ§µ½×òµÂ˜£÷>Bõ·ÏA¶fSH÷]½„þÛv”Ñ|„À4?š$ÚßÅì¦à Ýž»•_Z·Š`±ËbŽDïÙÇò× Ëv¸éUåÃûÿùÑÊ:]>ו ~ÐÐ]ô›Ý8«ùê0WÈîfç>Ž-ñè{CèslWõ>?Þ £V ?èpä½U†µbî°Âéû]»ÚìdÛ]›×.êß!æØé¾ö”lµê—íF?±Hä¼O½ºáŽ5žÛçD³¼6\Â_ÌF/ùþÒ%qР߆bÒ•C'ý¨US“ˆÈáÛùñã¾—^kÛ—flÝšqöfƒ}º£r»Êg9ãËܲ›ý¨hé™óÙßø×SC·¼¹Gê‹öjñ¯usIg·vä6ãýn¬éÅYθÔ#‹·”çu%Эã–Ý›Ô,9éMõnµ–ÐÏå R³n³x[.a,$ÎvWžŸPOðß‹^D©húÃúÇSÔÌ«†‡rMµë±Ý½žŒÝˆjùñUœ½/÷µåcÐt ¯3‚NWf´aü¥Ó\dûRGÈØcçã~?Z_'ûçZk9Ÿ×‰ s~5mh‡•1¬oðú µ vñ¦|‹=ËeŒ÷—·¢ˆ=«âOûÑï%ysDÂøA—çrr[DD »Ùߥ/¿wS]Õ€ƒo¼dìl÷›ÒÂi éüÇ/s\3ý¨mŸK7¾:ð: t­ì¹NƱ¬¾êÙלIû©ßÁµz¼’±#ƒ {Ñš¬7°8XäG7¹ex]þû”Bg5wAóM³cÙ‹:OŽ[àF ?×Ë6ó®òyúÓW¾Ãìká•ÂøAjðXºï|,û}æ ÞÙú}=bÍŠoæø´ú¹¤ v¤²äÏËßNþy)îÙÎx<–9vøºw÷³ãTÙ©[Ÿ ƹŒÝê@‡œ™h×JN™!ý¥­Þð:½;ø|NC5]»Ä±ÎÃâVþp§£CÂ.7ÑóeÓ6Vë?h¥#yYÖ8} œÂlÇ,`½ƒ?èXoÎá$ŽÍû¾aÔ©í—un¸Ê—Ù)ã›êH¥þÉm>ÈiÚ(éÊ»-vòã]ƒÎsrúÝŠcíºsÎÀ§iNßãG­òc·Ÿ[™:Òèì’ÐÐ ?zÚõt´Á:^'†®÷~Q¿µãج)Ù3ÓÎïç)g tÆæö¤³Ãøé…:õŸ覕÷;Ô9žíQì¨årŽº—Ê?¬9'gùÛÞMÜ)¦õ}JBïùÑ.]æuRèo»&y-ŽgG?5œ¾ºùyâºQ_¬¦`Μ]‰!æßÀ#æ+kÉI4€s"åujènsÛûã?O.Ш‰A–ñóLÎÙ è[·ËgHrú)í7Ǹþ.~ü K,6ZØ» žq.ÑÍÆ^¢[A¦ú¥ 6Lw °ˆæŠ ~ë­ÓúO§~¸ñ:½ÈRÑââm÷ ìQ7] g7.åŽz¥`·;·Hl¹Çšz>W|ê œN挀ùïÓ:ξµyã}u®R™Xr\:οj¤åçà8ÛSrònÎ ñŸOÝ€VëZI`' G‡ež‘RØ1ónúWù͇ æ på”ôlàºÂøAg¾fRšeróþv&ýàÒkäâÒû¼¾’qîË£¦‰©qnÏ«÷É EâïÇoy:Þ<‘=^îh¹pÒušÙ¬É‚ßã”ìLŸ×>îSug¶ÓMN¼Ïÿ½H¡ëråÕ’I”È–qáÄЋdÏEíP².7V/µÝ"¦%-†Ö°v—SO¥Þ­í$üøA7¾ZaŒÛŽDvã×Öýµ¾xÑëf_t UVùÒø¿lõꀜ¶w6ÉÆëJ spLn—r'‘=\=¥‘öÞ * èÜÿ…Rð±¡áœMãJ9 y“¶ÆÓ™×éE•Šzè6ÄÙ£:ÝRƒoÒ4·÷0Îuc¦þbÒ•eVrâF³Ýi^g]FÔÁiÇG&±÷{ò+§nÑÅïgEŒ `*yßQý[,¢Ú»˜gßrJ®ù£Í”=¼NÝæ}9{ZíHbíO´–Ýu›ò•÷*^Pµï9±¿—ÕêerR-ìÑ/©7¯C×qZÇitÛzlñõ­+d4hyôÔ1›˜õݯöo,iùLÇê;夳‡>Ńº7¦£âаqiî½[zS¶Þƒêu°úõö<ÖÝ’޶;Tç´œJ9ûê®ÂøA'8wá®iöçœnZ¾¶°Ýåö¶ WAXÒîàßÕò|äÔFgHÁëÔÐ-Ñ 4ŒžÖ¨~yŠ/É;õ¨~$0€µ\ýÕlêV+bŽe1-ïÈé(·¬n#Ì¿¨?>µ.jÕ­q~4®Y«¡áélx°fSôÕ…´f¢ýž6Ñr šÈìó/ºTTk™WõÉì¶hÉÒ¬‘ròÙ¾áD“OÌ#e—Û±˜¼Úû±:BN׸énË댠K«;ýöÆd6iÑ™ ŸÇ*H/¶Ï‚ˆ‚_†˜>½9cI¤œ&:;êÊY^'‚ÎÓ0åã´ød¦_sÄ©¡óü)ö£}Úª¥,êÉ‹¤î_í(Á×oÕá$̇¤·«¾ñ:1t'ôV·ìßò.«gÒgWt_%…øHò,Y·Î?ŽH8P¯_oÝêÝ“SÎ’Kd±ÂøA÷Huôbç»l۴ᶯ¢””í¯¿`m­ Á7Æ p¼œ:jßÙéóÏ‹º;¹ú?§GÝe¯1 ÛÌ  Šsõ±Üˆ†ËYˆ5másñ“ÓdÓ¾®Y–Âüƒ®ƒEéˆÄ) Á¤£ÙƒÊOØ)Öú±ò&^ÏZFØS»zWå{÷ʉÛe©á!Ì?èºÕµ¶:…­Å-Li︙ÞÖïƒØÎ59WšÙ·»Þã³þJ@ÿ|Waþ±R‘ÛžÖs•‰)¬uyA«ÌúA´bT犬>ÁŒÿÞmÉš;ÞlŠ÷Ykfèî‘Âüƒn½“9–\©Ìïäâe­½ƒÈiç\åûiù¦Ö’ý‘oǯj;i¬0ÿ ãÜâMv¦²YÕ9'¸`ª>EvÊ-˜ñþ£‹i¡n!-§¢Ý#ždæt›£¯¶é_œÊøý¶`ÚWá\æÌxß%ºhôbŒž·œ.¦4<0Â×I {Qsp“¡£Ò˜Í4MÑCh^‘*áGQ0»{ãÀP½¶b?všÑ§B.ø± ãǽOÝŸOcM÷ª9q’f }mØ0„ÍÌì~pÖ;jûÉàØ#)®þ zš$Ì?è~w³yÏÏ4v¼óІ7?‡Rç.ó‡|™ÂLrí°ò·'Éd Ùê~ ãl]™0ÿ ûÎ…oÛtæX®ÛMFë¶Øýu ¤Êß~w ÔQAŽ /&~^(ŒŸºT´ºE¼è~tºP¯¨(¢ÑÖÂw)üèI£‹šÔZ Ÿ;Âê÷3aü ÃCج[§ 6°z¯vc{…Ó…1%ÝÍš„²¥õõåû;~¼rúø3×õy÷ÝüøAWÿ|ï½Ë÷f°’Ï5½~Ns×ogÊéÈM³h{š>˜sö“ÿ÷ó:1tuÆ·Ÿ¯z’Á´œ½TP­¼Ü,¯“G(ãýíÈ!,£× ›rêÂ-‹[ó: t»7l^£k&{ÇÙ%ï¿C ,Å]n$‡VíOü‰Ÿÿú>¥Ð횦÷«ç¼LƯ+#éBÅ­ÐþßC«âK[ûȧó.ËÉšÛ¶ëÈëÔÐ=pt²÷¾LVpòÅHfE—|òÚ?íÆt¶È[lé÷9A¥\>/6½/Ì?è¼óbûg²šº…s4¹ÌëRK<#Œ}½–ív~‡ ÕuÙÚê-â’ÉžÄé×m„ñ‹Áó’j4FY–ÉÈ7õtÞÃhÊé›9x]˜àËbC㸓 9ÅÎj'bo…ø Ýäù;[m’ÅL8ºhFc–/nr>Œñûµ¶t±FÍõ+åôv^ïf;½…ø 﫚Ţ¿6lÖpŽšü»nQ¼ îiˆÉš;–i§Î„ø Ýêƒd+²˜ã™ìAQñjÒm»~ cÜ.O•uÕmD+HgóÕ^˜ÐuÔ-L²˜Çé&Á#bhÑ"ÎVÅx_OÒŸ·ü³HȳRüy~¿=‹é¶Û”15·Ú—Q»T,srëµÕœ)fÞ‘¶§‚¾X³i(ÄMèô“«wwy™ÅtvCÝc©Òfà„šÙ*Æß+r¢[Þo5át}G,X%Ì;èVq/×4›é%r.¿KuëÖpÀ8œEÍçf}VYÜì« ¯Ó~>焼[*ÊnŸÞ|D6££Vµ‹£d•ç¼ã»ÂÙ‘ÙÜBÙ‰t]Wéllî ãÿüe3~?&ŽvŒì7ëhV8Û^œ²c#‰e\ÿ–Ó«?n·5â&tç›<è°O’ÍÜ÷—˜6§Î•ûNÖhQunU[\£s¼œê„¸ ]À­@ãW׳™îXúx<Í›Ãnw}é²áL*å_i¼ÿ”(f©<æxq ÐmD(hбî­ĸA¸‹Û ÏaO¹r¶I5<Ø:Š -Ÿ¶fW¿…Äûš*hðÒw,öË »®*›±óHëp£hÇñ4j³¤áÞ+ë¢Øë-Jî¤-"­Cç›ù£´?}Å«¦÷„ýèÒè[÷{s˜ãæÝÕë§Svó[§·ŸŒªÚgÓ…{+qne›û-ÐÛöé]ææ¶Ég{†Ù¾tÚó5Ógä(æ]4¡oš\LÃï-ôÜ¿I!øµ û- ¥"£×»$!ËsXŸ†?d~¦Óƒ ºGž¼‹ªÚïäféC7ï1mÜÃDØo‹éçE9¬lͶ=ñ›2ÈÒþë̃£™Îuµù¿±Zyê°‚ÖŸ~¿°®0ï »Ú©]Ы)xN®sF¯T0jÙ¨•;¢™òhÃâWIÒku‘ýNÝz;¤f’ÿ>ÅÐñ÷rX“ÙÓè ʤ%Çî ×D3ݵ¬|j¹’[a+(xÄðñk“„ýNè6%­øl”ü͋­¬Ê¤íÓNÚ6f,`÷ÍÛSÍu‡º4ž üÙ„ý2èî4øý¼v½¶ý ­²¸I¾^ÏFNc¬–ÌÛiÅÔ9Ü5²6X'%o(ÿ)Œt#ö_ó.›mϵõW–dÒ·uJ³ÅÛ{°öðñÇïmÉq£ï³_äÔ|‹Ç†´ÇÂ~'tu^-ú^6›Õ9–é·É¢! }M6^cL]suqÚÜÅôô$º¾‘Ó¼þ,Ç1‘×é%–ŠôtvÙÌ&ú“]Ôì,zhâ‚:šU=g+Ï mw¶¶‚>¾Ýa™ ¯3‚®NèT¥é¥lvaܳ!›eçž–¨eÂyÀ"Ú§=ÿ¢ {[ò:tAÙ7šoØ–Ítö¿,‹2 µ¯Ì>1öf´õº¢ž‹‰_Ÿ)Ȳn˃ƒ˜°ß N“ÍúsÓýS-ÙóÈ:®š  aƒnÛVÝ[›S/"êÆa¿3ñϹA6ÛRís–wïlj+œkÜõÏþ®ÉW·+¿†qx¼¯®×Ἲѽ—% kžÍÞ稆1»lšn:+s·H]U÷¯M^{{ƒRAü½+á¼:~ý‘Å\œøÙçl6U*×ÅM«lOÜ©^‡H}|<¤³pÞúå D€,ök‚SÜ…»ÙôÆAì*Ù£®ª«B¹mNkáœ(©T´hlNÓÞ^YLÂ]„˦Q 7Ô‚­=H¶ˆ‘ÝPP\No§Ö¥¼ÎºZ?÷w°5‹)G¬»1È$‡2 ÇF?ŒS³íö—{=dO#”í:ÕØ¢ ›ÈŽa¶c„sèø{Yl]Lë?ís(löÜÝSJԬƸ—R&ØÑBmN£]3ð¹êoŠ (œA4aéù‰ÆYL·Œ8“C éM£¯êªºcТGSåŽC;çDÐå^ŠÔÔü‘ɺ†u/>«É¡ iõJß7ŠaÝÒ˱¥ÀKû²[SЩzä×Â9tÏŽÿBEŸÉ:½šõ5‡ÖjZö²S Þͨû£q6d¢mXØÒIAþ]å‹"„qƒnˆÎ0;“Ù*÷Ê%×¶Ò…ƒbØÕQ}r/<]LÍ–ü:sFAü½FaÜ S/þürææLÖaèÉóõ­s©v‡ž+Í'ư^Ü]ºÅ†ì†œUMTC]Ι”?¯ÓÓ”ŠÚ×l/_>%“Mù<ôX.Ý 6ëÚvA »P« ýœbzˆ,9乂^¶õ,2Îס++á™l²Fteqt.UÝaÂöµ1ìæ2óÁÅvÔËÒõÜ‘JM2Ýs«›pN ÝÜmMV%åf°‰—Ãî{›K£‡[\îèSõ|è0ȼõo¹5á.Ø ç´Ð=¼c“zcÛÅ¥ùŽ÷hXó²k§Cb¿jGáž¶? ™ñN%ãuèÜ"‹–7Ë`>;¸¿ù5?úÖÄ£4†ñ~ÙKèãÜvulúúÓÅgnÛÏÍá¿)tºò; q§¡»îѬÃߟj+Ô×K‰Û@þôÝ*fÍø£¼N ~ n1uàÊxÿ{”0Û&ó™Y,Ó]7¼¾”žŒ{b›ÒÛŸ†ç¬ÅÏkç{ÜçÏçEÐÕÈþUÇÊ7• 2húª<*¹fœÙã=bý†î«|ÞØ‘ņ®ç~»+¨ÓÈ5A]„ûÐ5ë»eT_Je÷<ޝ3¹˜GžŸ lÇšªÓJbÅ”âüøÃáÛ â}fyºº‹y) EÞë »y4zOÛž‹ºÆ±ζÒQL¾¢¾µç(è7g7~×I¡+ßë´²|I Ûßãã·S•yÔlϼOïGÆUOh^Ÿ›5¿?},vÿ¸Œ×©¡K™ö«Ç®/wïS~ŸÔï :ÕWµ®Ê÷Lô§As8‡`ážtwFy%î»Ë.¿/ŸdeqŸÞµjô°ý†8Öi>wSÀžv¶7]:a™?uݶïB§yÂ=‰»¨ûoÏ·]Üâ.›xïÉiÉ}º¼âÃBdzqÂ}uZy2‡?õìd…”)ÜO‚®wÛáz2‹ïòîýÅ}–ü"À-2ŽañjÞf¹#m=ú>5ÅΟø}á~t;F},–÷If^)ãB îÓX¯ŒëÙeqŒ¿gâH]¦=µz0ÏŸì¿ß’ Ü/ƒî¾´ÔÏp ³8âfÿ¾¶–î®Я~<ãNõO r¤Ngì'—™ûÓÔqW•Íî·@ç}­¡ºö w~Ò©#´´êžãö™&ñLwsÐ&ÜÚ®ï3ÉŸ,ƒÒ+£UÂ=è>pö¨w“Ø«A5‹µ´õ´ÞËsâYËi'·ws·§‰º/Ÿ¯Û©¹#ã„{.Ð-[S±?Ù"‰5šåÚÉMKwó&(ïV>ìvÔû°å‚ÂæþôúÒ’Ý› ã]Pƒµ~G ÙwëU ¿µä3üp”Ý+…àû.&þž±?½¨‘<òà@aüRJE|þHdü½B-µ´9µkó(æ'×ÚnšeC]Ç—/Õ÷§픾ÉÂøA7qòº¯Ÿ'°-ŽN}Q3Ÿ®Žx3u·Õ:ãKÀž‡ ýi›Û¤Í#…ñƒnpóÍ‘Ék˜×‚ü1ûæSʆ+K¯Gû³Œ=æ’²¬‰»´µh´?âÊãKÂý2èv|)=ò9ž¥†8W+˜ŸO#³3Ûüðgüùãb??lëÌ­þä›ÎÄ ÷Ë KœéZwæŽx6@œ<¯ÏÎ|šPÞœ:)ŸÚ†vê͹hŽq_<ðøÉWÂý@èú¨:V¨õâYÜûg…{næÓÇõW<Ÿ¦d}ý­×\ô“ÙMm–i®?µ9r+èTŒp?ºó¤-÷ŒcŽ/[ÇHɧÔÝ[ºÌÜ£dºŸ)´³§ezkv×Lð§Nw[»K„û¹Ðññ/ŽiÎlm¹ï}>yÌ\Ýmœ’ñ÷×(ë—Ô@ú–Áý0B¸ß™Z*’ô™pP}2–ÝÄêæDËÚÔ¸M} ƒv²÷ 8Òƒ§ÇNÅFúS_ý';Ú¥ñ:#ètdž±l=w¬1ª€’¯3Œr `w*¸‹NÔ6Òÿîþ»þtpÕ‘m…ûÐõ´òô§‹1lV͵fŠ hŒaì6«ðÆÿNÆ‘¤C}rvûù“±}“¸FÂøAçšù*"¬s ë=mÃÙû 胺ɛ‡µ™ïÂìÙ–8P£i5Èó}•î€\¸_ ]ìªU¶x«Ù„3iÌ­ÒÄi‡çzÑYÿ” Áºi^keÃYÇ+Ý4ú²BºÚ¨òÂå Vø•û£­ûygÌú<šÑ/Q=â8¯“B÷{K³o*ö²=·ÃZH¶M۫ŸUÅëè%á+7¤ûÿã^6R±(hÜÉ7Ïš«Øû—µâG…´Õ®SOí`öûiÂ!ÿMŽtîäÖYO‚ñ|æy¿ÝE˜Ðñûûaìwô³Ì_YVœÜ̺èXépò/?/úÓ㦓;¸õáŸk½t¬k¿]|Ã<”éŽ=[Ñ'™b`Ý´`öøŠ+oº4c²Þ°#þd~bUk—³ÂýjèF^Ÿ›µ6„É †t’(¢&=ž6Ž4aŵC²§OM*ã2vùS{Ë.w² ó:Ýu]`ÖGw!¿ˆš;/i—?-„q«Ž¦×ìèWh³•›1¸c¶ÕüçCÇÝækÄòÏ­¥µÇëµêvlÓî¶îWwT\bâïAøSPðÜõá1Âýxè¸jï~a ;mÎý §ˆúˆ7G ÏKl»LS³Axý‰¿/'Ì?踧£F ›Y×mŠÊ½ˆªŸ9;4¦è;ê—Ì€½S­ýÿùû0èN6¼qdFoăgjÞ*¢ˆ·QŽnú¡Œß¿ÓÇ÷†×vô'îtµPÍÿþ¦º€8Ï_æ(Ù²ß5üæÝ)"Ÿ ß·Ú™†²mº NbÒ•§ýiÔÀù[ ¿ïË(5RoÊ’O vÑbpÞ¯ô"Šnì7ëÁÊP¶æ]>‘åD‡³­EþÂþ’ðûè \¬ÚTΖ³z•C*¢Åíæ¶ó eISì”Mw “íÄWSR‰›ûv~ŸÌïÖËC _ü\¿dÉ»"š÷~~õô/¡Uñºý]ósŸãóý8<Äú1ÿzbèêÞ®Õÿ[=v$î¼mZµbÙáxÝ£æaìòÇèw!K—PµÊÛ‡jG|®õ1Vø}ty¥sõŽ.•±µ5ÆÐQƒbÚvtÆ1Aaì鯸NmÍ–Òû “/oá{‰]ÞÇnÕQ^'….Á¯ÕøáY·˜åøÒÓ:í±GZ«ØÉq *¿-\*øÌûÓŸ{ߺñƒ®ûíM¼ûÞd·Â[à¯,¦D÷/µfmW±ÎIyƒö9,!î׿í¡žè” w¨»?¡k±vDòüs^¬ÆYë„ÒaÅ$¹9óbjŽŠMêÞo¨Ü‘öÎÔo¿ñš?‡æ¿O½ÌR®t]RoX6¶˜®ä×=¼µ]8[õ8¢ç“{âNmS¤+'¿ðŸÏº¬MÓ‹Ï,–²!/¾=2­˜Êjœ Û9/œ]»œÓ'ó˜ZíiwÚuùçíiëò˅߇A'û5%Ñefª;Ð+¦­–ž­Oî gü¹-]›fú±eca¿Zø}t<’½/Û]` Ú¯y¿¨˜¼‚zð ¯ú½ÁóÞÜDB^ÑýnH?èºÏî_9ø[Pà^$.¦-Í+s¿ß¯:¯Óm+;ûÓ‰ßÕÇJãݲaÜ à4˽Ïý€¸˜Žž)ýHõ"ªÖ·§+‹ê~¶õÿÇïœÕÐMNÙÝay¡;ã÷#‹©$ófrÞ„¦XªŸÔg›}Uüß×B7%º©Ù¡ÅG„¸WLO¯¯pYy0‚Eß°pò<úž¿¡Óü~/zY¿§æŽ©¹_Ø,¦3ÔŸÁv¼.ºªq faõì¥Èüù¹0~Ðu4(Ÿ^`±ƒ]x‘üÃkM1=œÔü†ƒáÖ’»¦ä@‰3¾ºÞ_ø]½ðûvè„üËùÜ"µ½Ã2߸ÛK&8wÜ`¡¿pïÀ…þöHúÛ#I¢÷·GÒßIÿ=’¸œ…g‚ûÞ¸¿Û H€ Tc*1ð™@AK\”#1+à4B@3.@J€!œ9pjP Lðœh‚Ÿ¨Æ†bà 2>£¸%(F”VÀh„ i \€ ”CQsàÔ ˜ ¨:)ÐX3 *PŒpÅÀdþí“ôoï“ôÿ­ŸäÿDŸ¤G?ÉwßíÿÄ~’ÿ®>IýÚþÖF½ÿwÔFë¢ÿ캈‹3.Âxsß §5n@ * ‚”3-0@À2 ÀL ‚»¸%(FöVÀh„Ào \€ ”C$sàÔ ˜ 18)Ð$ 3 *PŒ‘4ÄÀd}$pJPŒŒðúÀh—\L `ˆdcÜ€T$g Z`ðßô´î@#$)Sàd "i™7 •ÀIÌHÿM½#ÿÝ=¶ÿ{GVc$t1ð™@É]\”#${+à4Bâ7.@J€! sàFÿÕ7’«k:ý­þÿ®þÖFÿÏ«þîýg×G\ŒpÆ“ûÌÜ¿·î@#'Sàd "X™7 •ÀÁËH ™ P `ŒÀ&ž è#ȉ€+P‚r`„ gÜF€¦ÀÈ@ 0D@4n@ * ¤3-0@°4 ÀÁS lpJP ‘ÔÌPƒJ`‚$ç ¤@ ðÌ€¨@0FO ô‘ EÀÈ@ 0Dr4n@ * ’¥3-0@â4 À‰T ˜¸%(FhVÀh„àf \€ ”C;sàÔ ˜ ø9)ÐB3 *PŒÅÀd}IpJPŒ4­€;ÐÔ¸(†¨æÀ ¨A%0A€uR ¶f@T !øZw ±)p2P ˜ÍPƒJ`‚@í ¤@ ´Í€¨@0FO ôÐEÀ(A90B€·î@#{Sàd "ø›7 •ÀÉÀH 1˜ P `ŒD!ž è#iˆ€+P‚r`„$bÜFH(¦ÀÈ@ 0D‚1n@ * Ž3-0@ò1 ÀÉÈ ¸˜L `ˆDeÜ€T$.g Z`€$f$@Ê’šp!Á™ %À ϸ5¨&H€Î@ ´ÀÉÐ H€”#$G+à4B¢4.@J€!§9pjP LHh’ª¨ÆH²bà 2>®¸%(FHÀVÀh„dl \€ ”C$gsàÔ ˜ 8)Ð$n3 *PŒ‘ÈÅÀd}$upJPŒä­€;Р߸(†(ÌPƒJ`‚‚ÀxþK$Òû{Îö·NúÏ«“þŸR#ý­þçê#3á9Òï˸(áþ.'sàÔ ˜ X9)Ð.3 *PŒÈÄÀd}5pJPŒ䬀;Ðϸ(†€æÀ ¨A%0A@tR Žf@T #XŠ'Èúœ"à ” !Zw ‚ª)p2P dÍPƒ `Œ +ž è#‹€+P‚r`„€lÜFΦÀÈ@ 0D°6n@ * ‚·3-0@ 7 À] ’š¸%(FHrVÀh„„g \€ ”C$@s ÀÉP ‚«¸%(F¶VÀh@%0AðuR Äf@T #0‹'ÈúÒ"à ” !h[w ¸)p2P ÐÍPƒJ`‚ï ¤@ ìÍ€¨@0FðO ô‘DÀ(A90Bb°î@#$ Sàd "i˜7 •ÀIÄHáõ¨@0F‚O ô‘lDÀ(A90Bò±î@#$"à ¤@ ˜Ì€¨@0F¢O ô‘´DÀ(A90B³î@* ’š3-0@‚3 À O µ¸%(FHÜVÀh„$n \€ ”C$usàÔ ˜ É;)Ð$|3 *PŒQˆ'Èú(D€ë4(û—:I¬÷ÿù»6Õÿí¿ÿOý“KÂß™U*»rNT³ß—à å™^<®·º˜þüÑÅ%)5®ß©êëèáºe„åi{JŠP«]1Ÿ®ïlº¤·?}ãÚçœr#¾ŸÃZúóç¹×‘àuþôGjzp¥Ñâ•ÅôÄ·ß»“î°Þ×»Û~ÚŽ¾6~æ_ß^¼üzáI Ð÷ ºõýæ9=ÜHaõÓ6¶YVL]'|ÝÓ²id•¿sƒ…fêò÷ zݸN]¡ït¼ŸÂn⺖›8Ó¢öêʨHÆûNÛ¢§{þ‚ Úx¼ZþËbÁº)’ CóR‡GÁ%m‹i¢®aGdUÿ#¯ô¢ñ±_2GÿqÓÉ‚¯pv©hŒÎ0á8¨®ZiUL|Ÿ¯H–š“ç&¶¦º7â¯_+èÖ6ÎpGèݾ°:Œl=¨×H®Ss15yd[óØùHf~襡¥µ5Ì—ü8WAŠõ6|Ù¡{Aœ1úšÛlëÏ)Ťk·ÉÖlö鶘Nä:Å)èNöÀ9o½ù¾bèÌ5é…ÚWž$™ÒknêØbÚ¾Ó°Ó¨‚HÆÛÇÚÐQç‘·ÞÜUPßE/ì |½¡;–c0¯rÐ%j^'óí÷aÅôüÊÆcgkDUõý°é?£I ¾Ï±Ž'»?¨&øBgÿñç¾J|ïb2HÍ*û24ªÊ•ïëîO-Íe·O¨…¾;ÐõÑ5LºF fŽ{^¿S1 yk:qòº¨ª>*½ÿª ³äѽ„¾-ÐÙr¶o5½ÈTº¹¢Y1e-äkE± ÛƒM<è{®–‚ºžÒ§çf¡oKN©H×öéö ߦ…i~íbüØ©aÜ_ä@VœÃŠ‚xŸ¡ïts-i&¶ºE6º8RèR·×¡µzrê85øñ»à"²h0©ðÙ³hVœyúòNsKJkÿ³ÿýër¡ÿŽ0~Ðeô{øM© ¾¯}µ>?4[ÿU4[ex÷Цy¤³x*'Þß]˜Љ_<›1±—’ü†‹L&Ÿ(¢ýÝĽ ÓÙYw˜WÕW[gGV(Ì¿ÜRÑáSßÛ®@!9CÒÿz½í9ó«ÝŸGº6“0ß7 4j#Ì?è‚G5~“^@¼OXÙ6s;æü.šßÍ Z’÷Õa-¼Mô^i¾âÂf¡ïtiœkð $ν!jF­ ©™±þ[4.ÏkýÐ’6ØÝhò]N;uBßèäÎë/î ¢Í×»vÍZD¿àT‹±cÇö\nI¼¯•œú­èÓ}¨Ð· :m.pSå0gÿ1Š(õàüâü†ŒqÝÓº\šOWêÎh:í®œßy#7›.Œtï><Ü{¿_mIóá[«ˆê<ÿÔˆ±ù’·C×\² Ç{¯¤ùdÈéÑë±úšsBß2è²F¿lÓ·N(õ˜&ÿÒöu!›2̵…>cꀥNËçÐ6i ÃÀ9ùŽ«°ò©0ÿ {ÙqÂÀÇÚPâýi ÉÓÒ±WЗhÆ÷S›EÑùO?î“SÃ6ßz¨¬„¾÷JE-’-«÷ £kMïP¨ I_oÐ>ýâh} DuôâTŠZo<~Þt9ñ}¶…¾ÐõóU¶.Ù£¢ ¢Jr.RùG;{OU4“\]ûHd6‰òüE÷ï'§ýÛœø¼úÎA÷³/ס*œÔ6v1?%…Ö9ïà…cú”§n©Á?{÷–ÿ#?ˆ¡ke°°±m÷R¹OvéâTH?·Žf¼/úX2ÿ½àCY9­èu$¶ßaþù”@gò>°{ËÊzmð[ysr!ÕÒ¾z=²w´Ð×k,=ªólnR/9]šÕÞ÷´?¡›š[LZòêt2ô]^…Ô¢¹¯ïíïQìþÃkuŸ@+ß”ï o)'];5KaþA×ÚҺݙ‹‘´÷koGEƒBzÖöy@v{p¢°ÿU?3R캿4ý›Ÿà"Ì?èø>´Q¤k_üº€Hsõ#Þ¯Vè˜W*i1×c£i49ž]wezÍsl}ðê ÄùÐìAsèìY®ÑÍ1µBè]½û^.£ë1ª6c_ÃYþ4|œ¯éÖkQ¬gm³Á»;ϧ‘íß¼óÒ¾Ôàœ%„ñƒ®Ùû±Kî1ZÛ¶aI_÷ÒÖùeïŶ¸fì~¢¿€–»õúÑú9p¶õöBß9èŽ×®hif®&]ûØ5ä)©¹ÔqÞ¾šµiWdžd3AâZ½v4Û9{ϯq'¬È®ôÄõô"_š|2äÀœmBß@è¾5 ózCÃÙœ{‘O'_w:Û šm©ãšë4& é¶à —/M^=#¢¯0ÿî£^jóåé°é±Ôqúá³4ùÔnÑ˶úÑìb{ïèGæÐDU²làQ_Êð*ý}\&Œt÷N;¶?®ˆ¥ÁWׇ½¼™O³í·®ù-ŠùÉb7è8›NYÜòÜ+õ¥Ö¿Ç”ÅîÆº׃R;7#++®3d>»û±¢Þó(f>ôI½·ëf“îm¤ûßßTÈÐÍ›~óªË¦8Å]âj›O–wï×£â(¦Ÿ¦ØÖ»é\:²jN–Õwßô‹”@Ç÷±£ÔÀ»Ó†çÓ2uB;ŒßË& ¬ÖÍ£Ôœðë§ ü胦cƒQ_„ùÌ¥¦wó‘ñ¤³Yi‘O欷ÑÅ|SzVP×’2”ÖùË—Vé uø8¯†®]˜çÕ¶âÉ|ýûÎÍ*´ôåD¯þWžF1 {¶%5qïmOœ/ñ}Æù×+®•ÝáuË~Ä“ÿVõ(Ç»ZJ+»÷`ú»(Á_Ø’¦ßÞØ6w§ï?摞¶Tôt3WH$ШåÓÝÂohé@‹f¢¼Ê(vuáŽó¤óéLƒM‡çùþãù4‚nAt¿¸] tŸq굤}ÑêdÆ9¸¾Z`A•ÇËNõ¥Ü{íñÕò:t¯9SËDZ×âÈÃæóµãÖyJÈÇ(Æ÷ÉCº6ûc|)šÝöé0~ÐéìI7&R#]€ÔRø’sÍãŸD±Z³:Y¾X`N‹}]r±­/M«é³Ë Y?è~]àŒ{©é«o™µ´Tq){ÉØ´(Öò¡yåÊöÓÉLÖzÎÄw>ÔŽk«ž'ä?è¸n¥}’¨Ûó Éà ïÓtö¼z ùÅÁ”°¶äž59öÆhÓ-!ÿA7ýg¤·Ùþ$úq´~q÷)w|¾^“ƒQlèæÌúÈ$úݱÇtÃ4õÍV1±¾ÿ «Ç¥áâ$ÒÙ¹¸O‡j9‚›0~ÐÉdœ¡f²à”Gké±—‘Œï ?Ÿ~ wMóõ¡Ef­0~Ð}·n‘ŸLFã“õZ›GWgÛŽŒúɪµrLy¿q5ÝëÆ>dá_C®Æºø‚8ã»ÔyõÌn5DyôtNµK{D±Õû•m¦Y-¢§'‹½îCãl·~_Ÿ%ŒtâÍâ×ߥ9»O­*i”G]rÞöïUå‹:Ðcô¸4w*l~âÈò&Bü,(õLÝviYì]J1‹º½®àµü¼ Þë±QìQÃe%Ž‹¨ÈkÃs[ê9ÊySñ"aþAWº€k$šB|Ñ{ds`úË(6*æ×Ë¢à”V3.ü@kz×kÅÅÒµÂüƒîsXÍ’«sRèKŸqqí=š’]ÿÃ1ŠÍZ0ã~³Å–ä±Ðøü¥oz˵ n'ä?èx_Ùº~/¹õá‘÷èõöÇÛ^:G± á…Ÿ²-Hg[åMG, 靯ºù ,눊RèÔð³›ïÕ¸G/ÅË+¢ªÆ}Ûôƒ/&y“Äë™o„¾¹Ðýʾ>&¹}*í»lÏ(5—¼ÖÎèÞÌ:Š7X?åÞ¦¹”ná6¹ô‰·àã)Œt[j7üÙÌ:•‹þœê‘K¿ÇïëjÅîmᜫçïëãC¼o‘0~Ðél˜/¤ }sizQFz£ÅQUõµÆóÎY÷Æ>”½Æ¶Nzkaü KEºv¦Ù©¤4½ôõ^»\:k,w\Åšpöeæ éã-Ψև†Yý0ìšÿ k²îÁ鷺i´gÍ…1#KrÈçå§Syû¢Xƒ6dv³¦7†Ô¾làC¼ÿƒÿ ;çœ0dLõˆ.˜}É+‡ö^ Z» uOgs®#©5}0óÌ[oòùâÝk°0ÿ ;³‹Ëˆi¤¿èÕçN94É÷·áÃHÄÏë=œæŒ_D&K¦ÍŸ¨ò&ñ³[i¦ …ú:>§Q×I}7tË¡k;f÷˜˜ÅZØd~è¥qÕPÅ~orj]jÕ*L¨_ +XÒ§[J­nírÖãI6¹pÓVÅÂvœøö²Å|ªyjÖ‰UÞÄ÷óêèt6ŸÒ(qNWÛº7²iÆËG#óžwKøseÏÙ22v™7…´á"º0~Ðq«VƒöéÔᨶ‡Ä>›6œ66òHŒ|rfщã¾'Cw§;÷Å ù¯¨TTWg˜Nœ½oÇlJ:Ó¸_ûäø+kز©ô²ìÚÛÕNÞÿè?mïó—Në÷^kÔ»(‹ÜwÕ·ô;Ÿ]°–ý'¿áý<&‚ng‡°+ÆsígÏe‘É™.u—â¹–ÊVZÍ]0&îm>j¿µ7%ü Üö^)ŒtI»-S.ŸO§¦ýg=;<'‹23¶ ¼Ñ?Jð•›@ï³gØXyÿãy‘@W\sI§i#‡œkÛ ‹<íƒ4mkE±2ïÉmŠò&ÑÙœ : ½ÿ±n”B×X|Ò–âÓÉÒvq YB&¿1íFþ£H6©ÛÆêô§’Õ³JgoZ±·Y–¿\X?@7.$æPùýt²ß»ÿÔà]™´2yÖ¬ø¤HÖiŒ²Œ‰Ì©YxIƒ_;½ÿÑ?¼:‡ñþ×x’N\wÔöÃ2i½ê{ŒSÄå‡ýc9ãïª}BÝø—Šxß‹tÒÙp?È ÇÛñmrT‘Â8̧ðÖ«ÕÏzÓfû«z‡…ø Ý;Î^´2Æé6f2(ýAtP©:’Í7ééI²¢¢^n»;¼éÉ«ËYó» ñºj&WÆŽÿNSt édÑìy—3鑌[µu¨´¢v6'/lò¦©Yw£Ô~ÂøÿÙgÈ ~ý…qÌ[Ññ\q$ã÷?­È}Ùª6…+¼ÿ±o#î‰gÜ•AJ®-¼{êpÙvø³HaüæÓ†·<Êfzÿc#…ÎZ×`;B9[û©Þä΢_å‘ÂóbA­ÖMóíäM5u† Âü+þ㳓N‘›–ÅÙ-K¥u‘·[u¸‹×7ºRwÆz°Ž3è–_o ëèÎŽ3:gŠq8ÕõÙ)t°¶áôÀøCLsÎ<æQ­ÙT«Å<–ýUF9\X*ŒßƒRÑVúÕËÓ_ºk‘¨G‡XÅ—#ùófSK‘¼÷?úœAWxÈÃݬ0Z×xiºSv—Ú‡´W˜hÝçr¼Í\êÍ$ºMð&ÞW^?èx²qOï¦ÓY£.ÒØ&w©Ó›‰ï'¸±Uóšæ×Y0ŸæžžVÖÊÆ›ZFÜ[/ôý‡nA³úÛ'b¼+מ<g2üåøLÓí3Ów-¤ô˜AצØ{Si­&ÞËZñ>èø8NO¬T\x¦¡q÷ÆŸXÿù;ÔÝ<¾óÅt¸3gØíMúzÇGoëÇë¤Ð=æÂΡtÚfPý}7 uÊòƸçÆ×5¶´ºð‹Œá}òþ‚¼N ݵý5ö[“N{Nž N¢¿özщ£Uû­O.iÇGœà×=¼®ºðágèÍK§²RYÑ„¾I4rD]½çŸ±\®,o*¦CoãýZ÷&Ív‘tÐ2^§÷°Tä4ÿ­ËÈÁéôÜaŒQæùD:ÙçΞ¢Uûå¼_‚7õ±éÓà¤à»].7K§ÍfD×ë&ÒîU:Uöt¯òŸ(ýVçdo’Îz<%{ˆà»¡¸ãåˆ÷id÷ójÜö- ôèøîfçÜÙàÂøj‘nû¾‰7õÒdLR\ƺ h3gä#ÝtyOî›­ÞÇ>sgÝ};9Z, ¾ãZ îëM•±Ž>b Áwº¼Õw2­o§Qí^e—ZÇSnbçsS:œd9ãu+ÒÙÓÎ𦆷ì*Ý{ÓkãôÙ““x] tœ›ø¼«É4SgJ¹Î]Ö‘2Ñòf=¾Œt¤ÐÏcæí¯íC³ÒžÔßž÷Ó+-}7~yla2ñûI!4d©vï )ãý°œ¨³i¿²[ÐI²Í½Üß7èâö.¹Õ"™J7xÈûÝ&ŸfÃÚ[_c&†ÞùȉæÔXü½ºÏ?üâDÐÕûl‹ _C/Œ8ƒû *:à—¬h|yÆr¡9vQóxM:À¥‘F¼Ÿš:Ý´Ù¯¡œý@û *;å]\/òzÕsíôãûÑúu|hâî1'{LáuîóùpFFʾ=ïãÁ%Dþ9^5­¼е'£h­ÎèÞ‡:X”ŽHl!ø¾A¾üJ—÷IôãÑ3VÇ?€ŽœtÒí¡ãã—=Mþâ\Í‡ŽøØ>ßîÊ>5tk5µ³n%Q÷v[ü¢¤­Ï~š}£Êßñg[ØÔ‡øu©0~ÐÉÖsNVI´öñnMçJZ£ùtsUà Æ¯/Å$ùPo9Ò‡Ú¹(».ø¾••І¿ý]¶¯n=¹Pýüå‰þt»y¯7ýjßdA9Ã*[êPÚ1ô£³m<½7¿¦¡0~Ð=Êm´&’Î~ï§œÒÏ{í°Ÿ{“~rvþøRª¡Ûðó¡9çíí9.ŒtgvÆæËi¾¶ËØP?ºùûbÚy¯›U>ʼ/×?Ç] Ý•³Uw›%ç.Ÿ¹Í—öd]%ý¼É°8ëz:ZL}vεò–úP‰Oî«Ý‚otñ[9ã‘:WmiÏ3}¨kQ›¤_η?nö‚/§ÞÌ"^'…î…MONËÈcÔý/Œ0_JZ–N~y‹ñ>hä´nùRϬ‹9›¥ÛÂøA§³Åiž@þµ¢†ГÑ}»³Ï¸Í”œÑ 'j¥;¸ñ¡òÂ=7#$üóRþéá±wâiá•:›—¾¾Eo ŠŽ”±Gãue šV=7ׇ88_Á7óq©èfó Û¦8ÄÓùOöÆ= nRa]Ñëj5½™ÿ9Θo)½kTqcŠ |þöf…”÷£4‚.b—ùãék÷ß ÙcOØßÍòf“‚ÛÏÝì´”"q;Õx½âçéÍ2ßLè2fÖžœìG tF¬^Ty¡¿èÕYŸ*_‘sk8§=:ú©áôÕÍßLè¸ê¿‰yu{ß¡ é: k2Ù¹Åd_v #wðãH7ZnŠÁ÷’ÄÙ¹5ã?Ÿº”O¡¨b)¹\ÒõUá5ê¼këŽ/Z_Æç{:6Ù{_>ÔFw'Ì?ètÃëKÓ¬“T¥¥RºæúpœóL?fµq~ýWì¨0zòÏOõ|I·-uRðÍ„ŽOб<æ‰gŸ7WiÂGƒC~ìT¢sûy3Ĥ³·mëKªN‰+lîñŸ¯ºñ' G‡eÆÎ†¨åʨ¨o±¢¶¼jþm¯ÿ2ñN_:°±¬ødïçª÷¤T4ùI÷åËVÇÐØ‰œsÖ%r}1tÆG9ëߢ퇥bºXVÔ½¨·/Ù=êšÖ1‡×Aǯ‡c(ÅÑôJ†äiëûžÎ’3gA³=ØúãÁ-ò¥`EÖ:éA^'‚®ó/»i'PôÕ%vOÊ‹•é?GÁêZõûîTŠ3¾c´­/½X`p÷»©àW Ý•Š¦—¾‰ÔĹýîxŽ2ÇÝ>Úõ©‚¾O5s¤þûë "÷%~=!øÕr¯÷;ÕhŒ’Ñ>ûÞÎ íÏЋ †íógü¹—#뱯QÄ>_¡ÞàuR袣Vdž{-øž¢Óê§íF5S²æœøG²ªÞ3`^ï`á5»/xº›…M±B¦ÏÖ·ºÇ´õ Sš1š@±’ñu•! ¡â÷¥WÓ†vX ø Cwä+g8Eñœ}‘«;eÕ踷Åm%3 ˜¥— ±§C]·bÉéK»¸ð5Pð‹~Z*²3¿•õØ>’dY {pŒêç|i³§LYå‹Öµ—ŰV»|iCØË›=«ó:#è^O>¬Q¾Cïm¸“ûÃØôƒß즦ÛñQbry¹úÊ w_z¾+¼e_‘à7 ]Ü•M•+#¨Ycã‹ëö$ƒÎ­×­ÀœmÝykhC#Ü?å¾ðõ¥þEá&ÙG„ñƒÎ­ŸlÍùœpÌ.Ô¨¹~ÕÍÛcì½8€t`øØ‹ÖÔ­¼ÿG·,_áž’0~ÐÙì?}Õ⻊,Ö4\yF¼›Ê»o;“¸% j_x%_Àüãù”B—UìÑN%øÚl§\y¹Ñ×ÓUû|KŸZ Ôó#3Ýü¢¡»2‘Û°«Ê‹/—~¿VÀ~oÝ_ëK šj«”åKC7‘ùjaü s¸g”úÚ&”RëMŒ¸:ׅЇ Ì?TÀ÷°|f(¦œmzC?Z¼šs<ü¢Ÿ•ŠÔšZ¯$„Þ§_k0o€‰lc&7 d·3©žv¼ÑE9l€ñþÑ‚_4téjMÊ…`j_vøˆ×º¹”]±yøÐéŒ_ï9P-/ÕføÑ¡ åfOâÇ]¥Í{úz'ˆfD­nbø²7ûT8päÓsU¾}«æŽêç3Ñ"k5mN‰‚ß7t¼k ­õxzbÙª…ìë,ºsß²›9cæ%ÔÓúDì'c?RD4ºwÚQðk‡î€%—Á(A¾Ç°¼`)›ÐºV»Ë3ƒØ«™ÜAö:*pJÖÊL$×&Lãýá¥ÐM{T³uÞïï¿Ñ{-ÛÞD#™ìÄ~4ìÒtÿªýÁRcÝÚJ®&Ì麋?è~ûs†Jâ\Ç/þØÄ6ôÚÞ®^0»|qhÙà NÄûðùÑ>Tg‰×•@ç¶K›íå­ ìæ>}º‰íÚ…}° fºeÑ!*N›‹¥¨Ÿߓ_9Å¿O½òRÑÎ’^³Œ^ùÑrÓ¦ÎV㸷ø«g0“ I93ËŽŒÜ|ÁÚÆs)ïÛ×A÷l­aªm'_Ê>Þvç°â=,püµz]Ó‚YÎöÍCL:áS~4úÝ¥¾FÁ¼NÝÚ“Û""¼©ÃîñÖsö3󾫿3ÝòÚTL/×rFˆ~¤ÚÜð[v!ÿùÄÐ-}Ÿ6tØ­Û4øòåÇ=²ÞSuL{†°}œ W†˜Mh~j˜Úø¿GÂtïšp Å›ô­^öщ9‡Ø@—''Û„°—ò­‡dØÑ±±\õ££9y^'…nÛ€ÓÍ^÷ºA#¸ëD0Ã;­³.…0~ßÞAð½÷#ßÚv«¿¬ÆºZ³¿T3[u¶è.€c÷¬pÝãö£Í”=.µéŒgàèG|}(ŒtíDìíÃH)í]ɬž`É’ëιC«â¼ßÚ¡í«ùÑ4ÝBƒ×é=/Í`"7þºL‹<÷ošäÎû8Û»ì ­òSVß¶¥Ìx_t^gݯz¨Ä.І­ŽÕÚ’½‹û¼%;”¹ÔUÓÍ©Ž´¾ïBIè=?Ú¨+<øïEwKѰó9:n=ìfä|¶b‚I£~íÄsê¢3Dô#ޯ׉¡{6Œ3;ESöÄ~ð`OÞwÌ^cƼ¬Û°Š©öd«3Äõî ãݘS??`)$ìb[[½Mz}.Œ-g!ͺu²£gÛ£Âö£„î ^?ènÞ8xí^ò!Z“W²c^¿Óì×ù±ÛÅIa¬%wýÓ@Lç¹4•‰ïșVÆ=yºCçwtðµ—t×.Ïžf;Ì<»ðm³ß¿hŒ‡‡ Å/Ü^OòβÒ~‰ÂW ãÝßñ€!‹¶$ÿ>ÍnrÛÖÍTUq7‰+ äÿ˜ïz/JE(B6Z=ZBQ·êÔºé|†ýJùý©í0³ 0tn'ÄkÝ…!ù?ž3#èøs ›€*®éå3làÞq3½­Ul]ì¾,¿•¶”%êsqxW9¹iÅÈ,¼NïK¾š½?}oÃè¬3¬GŸg§R1þ|JLÜnâ©¶rÊ=¿Í¬ò0ÿ Ã`Û_úçò†_ìhžnFN X>÷„×I »q[Ó©ôé>Ö÷ÐOÿ}¿UÌ}ÅøÓ]4o…]Ë‚ár8Ϥ—w3aÜðçרN]Øp¤êýÝM|ð´|zx•¿øÜòÈÄ~³åtî·ûÎï#„qƒ.ñQªÿS½“Lû‰3œ?Ãê;feô¿ÎRúÊÄ^BÊÑA]E3ä”_~éj©J ë½Ü¤aÜäÓlvÔå¼=gØ×G¾¿¨Á7®}e[ã¥t¸C¥¡Ÿ©œôçq7‚yÞËRÑÉ÷§–_=~Ž¥ì³}ñlÆnå7ªhY„pÿo)ùY˜ÏþÔ[NÏm=î6_Ï댠ã\\«Ý¹ÀžºstO³ö>é5w#XnvQ=»!KÈÂtÅáörº^YxÅh7?nÐñ÷c¯°‚ŽÆa¯·fµîï6<ÖéNU|ÿ­Aeí'ì¯ð:1tsuÆžRv˜Û.¯<ÅR72ÿ¡+î°¸ónÚ· Ý>ÑæßKy;ø<¯“@çè7©è:Qëk¾fã)–J‘soÝa žµYâ<ÊŽªÝ¼6©S²]°±ï“ÓŸ×I¡ëÝÿëÕÅ^,CËtz0»©É­Ü¿SåÎûûÉI·íۜש¡³ÑmÜd_›ö24ò`5ZL8µµV$ãïÿˆ)‚³a´“ÓÊnÎÅ/àu%ЭéùìHT«Û¬ËŒÈ£–dÊ·[‚{ ‹dÜi„ÈÑŽxŒ•¹œ‚åK.¿ÛÁëô^•Š®¿öŒè}GÆê®pxµïœ;;9³Ùʦë"«|­±Hå¶(p2wA„×A·ÌeÑ"W¦[¶Î?Á2®| ‰¬ò7N~œ›n"'Ý5´–ÂøA·õÖ9Ô¾ì]Þ×°”ÇØ‹.¯þŽd—ŠÞš7/w ™a¯ ~+å4VWH ã|ù”ÉÃ=å,ðÚÅŸ»×a‡&6ytJ+¶ýf‘Ü2s$c³oËiÃâʘ¯¹ÂøA·áuýòý#ýW Mpˆq·'ºˆbµ—r'+öÔpö~Š»(§eOlÄó÷ðã]ŽÖ¦þÖ5J¶I½1áhŶ5©ç”¨ªûöi/8#[9­ž½dïô¼N Ý눰ÎÆ°^mVìZºm;U6ó¹øg³j—þcy#1 ïclã$§ù”°n¼®ºÚ— F”7E¶^<|\í=ìøŒQž£YÿïÆ]ßZ“ü`ÿfÍäTk5WÁð:½×ø>oößîe ðûƒlË×gA3FF3þü{- ì;c’œÝN±·ÿ Œt§î_˜ÄšÙÅåôvÚÂú Ðì{„Í]ç]eIÖOêî»l/'C]A/ŒtòeÜIr0³Ó¬õl›_Æ‘ä,ª{Âf†µû„–SÝ…#aü »ß”»p"œ›®`‡Û%æxûó¨…î⽜3'h…¼÷úϽÈPÖŠÛݲ˜ž/÷ô„.Czmמ#óh’® Æ<ú%. ÿ"ÄOèœ~.±0csî|·hыиÓÜ2¶t»­µOö|*¶kv>\-îñ óº]Ù–µ¥al\¹tšéÇùtóÍš7ëbg&¥¾ú`Exv™19­ÐfaþA÷³P±~sðˆ;SG™âþ¬…§ØÔwïêæoYDç/s ’¾žÞ›RQû“oç¼ðg§pNÈëiYò˜.+sN ¾Ó6´pâ.ç§Érú¬ä|fWaü ºÞlù5Çöånä¼û×¶ÒòëŽX¬?Íxÿd18R”sONü>¯0~ÐÙµ³ñtÔöÆæÍ)/½ÝdÛ7»ã§gX5]aaG#^”«ÚäË…sJ¡nî^W×ñmZD2þê>ºÿá¹ uβKñFÊuÎvÔ^³uù'¼?^?è~§ifû:’×lî¹1ÙŒ‡šÔxx–ñ߃élÓ³äôºwãì¹…ñƒ®çæ«e^ Q¬á ÁWôK’Õž¼†Ýnœ©¡÷u×3ˆ/˜Ü­&mÆ:þ>A4ã?âNõjNcǧx²…êö²¹6ÔcàúÝ)raÿS˜Ð…Ù5¯yb üš=¨ÙÅÈОl@úÃ}ãjZÓÒ^ñ]Þ½GäÞÄü‘¾0ÿÞ¢Ž¯ÅÝÜW3±Õ¯í‹”§Ij4¶ûyvP{õQÚBjÂmƒù‡…ñƒn‡.°¨…óë³4‡"Ô~×y6÷l§ã×."š·¿¶}¾ š9ÞâQ†PwB¶uæÖ‡•j6Z÷Oº·©¦ÞyÖfysÔ‹…ó29µüúiüJ¥0~ÐÅ¿üVëŽ] «ñÊk®å˜‹Ô»úœÙ¾W.Tsü>·"«z°œ¶ºlÎ[}G?責oŸ<{7†%/Ðæ4Úu™f¯­žrè"s}+Ðé©=M{¼gPåw9Ýp]ï¾—0~Ðñë¬X–]öeèÏü«$7ŠìS$»ÄŽVÎLÝ:À‘ta¹•âu®º:õ{,ûåËtÃØæ‰Ü×=møô2{ºø“d‹Æ‘·ÝV_!œÛ óº#†› OýŠe6Q‡62»NÚôZ!êy•½¾´d÷¦ƒŽ±W{~Ü3¹p~'Ì¿ŠRQãî I=ã˜ýž6Ñû7zQ«»ŽsûJ™Õ±n~LjûÕÆÌÛ ?.%~Ü Sþ1lUbËqã å4öÈ¢;^!Ò*¿ï§Šn÷^ùçs&‚ÎÏ»O½;Æñì+ªš€â›޻֛ý®±o—î,î?ÃŽø8''n0–\â't²¹Ü/˜â÷+'ãú·im“ž­6½Vµ/Ñ™ÛF\ §»?MÜ=FÈе»˜ÓøE<ëòqЧˆ‰2ÚÙKµlGᵪººr•Û» –ø|s7t=÷[?î}ú­:œ41…¸w_#ñ¦îïj\«Ýþ:ËØcþ )Ëš*¾ë?s;êx£N˜ÂÂøAwîÄ ¬8XÄ •åS¹Yµm¿rþuV~ÓÞÁf2òXìIý@¹p¿L¨?¡Û³ŽÛIM`%Ò¨o®å¾ä\cžÙ÷ë,Êôݽã¥Öä0.µÛ¼B9ñóM¨?ß•ŠžpÃ3/‘Õþ{¹>Ö ïwȹÎüäZÛM³lèÝÁÏßÊ…ßk õ'tŽ£C¾½ðIdžnaél±‚DQ­‡Øvób‡; ?¯š'&'óU.¿Éé…ÇÚ£Ž'„ùݘ¬Ûë%±èÂȵs<ýiÁ¡-ó³{1¾Þ´#îöW–ž‚2óbbÄ ó:]ؘ“ÄÖé.(+)­úëF=kߨZGËoJ‚WSPþ¶w·E óºƒ_ßWå•ÄîØ^O­@9\y|ö†ðûGZø)¼àðO9qÙ¾õ!aü ›=3Ø×ý}=Ák¤ÛåJ²wèé!7ÿ»GúÖhg©>·[X=¥‘VNŸëÕSg”0~Ðõ^µöX†c2Ël|¡Ž2˜²®ìž\¯­ŒÕÛm]ª©aGm~[¬½ §jº*Œt‹Œ®z'3îW0æ3BÈ\”œ#Û$«º‡¢gúùUøt9-?všÑG?èdÇ^l0z™ÌÆÝjú¾¡‹Wû$ÖÓÈÏÌ–GŒlÝNNSú/ÿâ«Ç>)tæÜ±pŸ»Œ?÷ %ý«_ ®´ñ®ÊïCTò¾£úˉ»]ìÁïÓ©¡«l³§làê» ‹OÛ”ÞaT»«ïÊe»¼«žÏ«Õ>-Éż­~çÂ(§!¼®ºHùÇ‚•~wY´”F­Þ®Üí—7ãϽ(fݾQd·å6yÞ‡R‘…|ÿçgwYY¼åÈÃëUtlÓîaS<}˜nÛ¯š5bWçwRж=ñ›¾èñ:#è^úµÔ:tNa‡œ™h×*œžXí½ti’/ÓÙ°kœÈl¨eÚú š^ÖÊfÊ{aߺ=XmØ.Na#WÓOŒ '~=çËøóN'zºaÁô•cäÿúõÙÇþõÄÐ uµ=SÿL »5q‡ÇÈ•´çøü^~ÌîHñßTGÚ{3Br®µ‚ZÄ‹îGKø}: t‘Jn##…-â~ÐòÍŸÔåªå(9§žõ;ìâ@|Þ•—…ñƒŽûÕ峟)¬qÈ*ÃZ1wèä2MÎŒ(9‹zò"©ûW;êk=ßnò ¿ŸÂ¿žºäN܆v*»dtúËÚHêæ`ô¾»¢j?qv‹¹Ï{\’¿~ƺ•Ý®\ïÊÌç¦ä}ëEC>¹ºa‡‚=Ú©®QCß–,ևǘ”Snûäªpîð±T´œ»î{:•µ˜³sÎëœ(š4Ãge£D«©[€/¦Eg&|{VN溃báÜ:îնƦ²W­^Xu$š`ö¬åÏ–ë6ÂÓ Ý^95¨3.PRW?è¾5lÖpNE*sÕý‡‘6nÆ:“ýÙ¢×vý›+ÓÕ$ãŒÈv ª³pw'_~Ÿ\ Ýš]ýÚ ìÆfNànÖ0êè±Ââ»?ËÖ{P½îa[ŠÜpʰæ|é¶“-x:ëc¯¦¦˜¥±‹Üö¹µš¶¨rŽ~ŸèEì½Ñè‰ìˆ›uW)èãaN ÷k”ö.iLwí厚övNõÊʹN±­ºÏ­åeO×ʼnÍýÖ*hâ´³RóŸO ]J“îKÏ¥1'ÝκÓ?vÍ•‰×©ô$÷ÃoG²¹T¹|2t{c†lþÈx] t&‘ãӘÝÜg³ß튡0­ÙªÑW®Ñ“P)îlñ¿ZA¯ï_~3ê0~ŸJE jœµN(Mc‹j»Ri Õúº§ÚC)a2œg´”tÛbN âï1 ãÝó)çs3k§³GõŒss&ÇRcσӿH©Ëö‰7ØRÒo,RPâìðC /çFЕZÇ58Ö#©Ëß?u‘ÇÒŵm®×Ûu…V7_°Â8d)%ûèk=OAl¸L œA7D´L6Û,MÄÓT«YE7 ²iðäéý>Ýköç%¦Ïô*ÈÙèlZ;áܺjºL¦³ÙKßtz´-Žòú…%LZ~‘þÜïè®;0SкÊÍêÞθÏÇM¿]é,\Ö²IÂã8z2´÷« æhÇÖ•)’ G Ô¬Á³6 0Î5xmüVúÛcéo%‰ÞßK{,ýgôXâþqž î{ãþn3 *PŒ¨ÄÀd}-pJPŒĬ€;Ð͸(†pæÀ ¨A%0AÀsR ~f@T #Š'ÈúŒ"à ” !PZw ‚¦)p2P DÍPƒJ`‚ ê ¤@ `Í€¨@0FÀOù×÷ßîƒûïð{ûßõ2ùïüÞþz™üÏx™p=ºÿÖFk#‰ÞßÚèomôŸQq±ÆEsî{á´æÀ ¨A%0A rR Zf@T #ˆ‰'Èúh"à ” !ÀYw ‚)p2P ñ†ÌPƒJ`‚`è ¤@ Í€¨@0F O ô4EÀ(A90Bµî@#TSàd "Àš7 •À×Hö¯î¿Ý ÷ßáóö¿ë_òßù¼ýõ/ùŸñ/ù×>Ý\}ógÝö·Nú['ý­“þû:éoô﯑¸8á*Œ'÷¹¹ÿfÜFP¦ÀÈ@ 0DÀ2n@ * ˜3-0@03 ÀÁM Ÿ¸%(F„VÀh„ h \€ ”CIsàÔ ˜ h:)ÐP3 *PŒPÅÀd}WpJPŒl­€;Ð׸(†ÄæÀ ¨A%0A`vR Òf@T #h‹'Èúà"à ” ! [w ‚»)p2P ìÍPƒJ`‚àï ¤@ Ì€¨@0FbO ô‘$DÀ(A90BÒ°î@#$Sàd áõPƒJ`‚ã ¤@ lÌ€¨@0FòO ô‘ˆDÀ(A90Bb²î@#$)Sàd "i™7 •ÀIÌH ¡™ P `Œ'ž è#Ù‰€+P‚r`„ägÜ€T$Cg Z`€Äh$@*€1¥x‚L ¤)®@ Ê’¨p!¡š %À Ö¸5¨&H¸Î@ ´ÀÉ× H€ Tc$c1ð™@‰Y\”#$j+à4BÒ6.@J€!’¸9pjP LÔh¼¨ÆHøbà 2>’¿¸%(F(¬€;ÐüK$ÖûÿýŸ`¾û‚žúS©¨®1pºÐï %¶Ž»$Oþ¿å íó\yÒïwÙ3ìÅ”Ñp9 q ¤Õ±Úó©§¢×w…¾‚xÿ+7Z—0;üÐÖª?ϽN ^Çe¿Áé,ûðãí¡ñ´ßÑ:µWÒ9Z×òspœ­'Õ^a  -§ÿ˜Ë÷“×û\*º~¾Çýæšt¶Ôv‹û•Î ô,5kL€÷YòïÎ9:P·´Ž9ƒÄûØñýë ÛÒ¡èrHaºÐg/,·Ý¿k峸8@Û݉–èþQPý[N šÁëDÐù­o ±y›Î7ú>»ñ%Šõ´Ýfý:EëÎp|Z})¥'NM òPÐñêbŽöäubèÒªí¾õG:û:%dV’C"}•<‘ÛÎó ðïí:öMw¦[Ûõ}&ù+hÙÈ[oîöúúC÷öÄŸ3u3Øñ,ìyF"m°8H»Åø¾ˉ뢭 È‘ç_ü-ôõ‡®ï‹SEvÍ2X×mQ’àwŒÚÙn[2E²œ†«6¤nITБ¡Iá3T¼N ×õchû vé»oÒß$:t|Öª‡¨wñâ¥ã-£m×$¯Åñ ¡†àËÝ,®QÒ_ÚJC|ÿåýtÈÚ¥ö.æLýKœ TÐçÀ×ÁC: ¾ _JE|¿ÿ æ­õ¼ßä †ø¾ü{èOUÎ&Í]AÆœMÍ2aü #ùuüACœËæìé;‰÷/v¢=ò¦}-ýç÷/Ÿÿx›kT”Áø¾Éä³cD‡Aæ[¨gRæ@Ï{qPA—fLÖvDðCŽï{œÁÞ…7{ÅÒ“i`ÞëëW>n ¾?Œ=qî¡-†)Hóúܬù ?è6O¹Ôb6¾Íòˆ»tÍ"àY§ºkiÄ–“u+‡ØÓà&CGEÍQP³)~"øi@÷ Ã>ÉvŒ·ÎÖùö]úP:üáùåÔlWóO;ØÓË8ΨCAÝNÍ—+øi@éÞ¨±ž¯Ó+ê9nj™B[žû8¶ÄžtmenÚÓ ·ÛW(ÈU¬÷hü!^W]Ó«#c~½Lg=EÍS=÷¤P¹Û}ó„r ÚÑéqäïtÔk]³ÁþÄû9 ~6•¥¢FůŸÆüiýäü¹¸7)Ô^¯å•QYm(§ÙYWQ¾#ù~búÁ™þT_gpÏû¨A7—³{ŒOgÝu†RI³´áð&V̰ómÿg+–Ð`]#;š¤kÌ#ø¡@ç©kß—Îîô2?—J“—¿|w4i “¶Ž¯³éÇRâ}±ü©{âÀŸMß ~(Ðñq'ùfÜ›ÿª¹–ÌÙwX¼ŽM£G¯ò.#Îovª©kl#Œtaö»òGoAÜí)MòL£Rí²…†Ú-Ì|m;×¢e¤³¬éO-to„yÝËÒ¡mÔ‹ÒY|@¿Õ^ÕÒ©OUÇ µ„ñ}ª— ýÞÿô³ŽïsœÎ²:sµÓÉzTä3õº},Íw„o­Ï΂߉‚t6¶î‚Ÿ tåÓ¸ÎAél¸cçö9éô}d¡×°¢ƒìÞÉ.—wŸ\J¯{ïñ9SA¼"ï·¤÷µTÄû¥1ƨ Ò¤úlh÷ø0ó[wí½ÿ'êÐÚ7í·¯‚vȧËÌàuFÐõv>Ñ*;Í™Ä9fPñés×Î9Æ|ƒŠOr$Îån æ«n8îó:ts7;µj Hc/½ïYZÖÊ$¯!¾e—ËO°?ù"ƒ³8£ ó— Zß ü¤ ã}dÒX¡áÈGÝL3)ÄD9¨W‹“l´ÎèÆzÝÝíâQâšz½[X~RÐqQÔÐ:]¾ZjZ±<“ö·_ñrú$–%¿ÿQ*u¤FÎÏë…bÜ~(ëß÷…àÇζr@Óµ»œIË[× HºzJð3]BÝ«-_ž¿ÐŸx_#ÁºµÕs–×Nc³¸6u™™ž:aeý^gØ• Zûòâ–R©ù¬Ž»ýɼí½ç¢<Á ºïîaÖ?µ©lÆrçÊjY”ymi£OÏ2‹Çk×÷ZF|_ ÁB¿o¥"Þ&Ux޳èhР³ßâ<Ÿ—QÅŒœ¥u¬ý ad­¦ÂøAg Œ½#•5Lt›pÍ)‹~ܯñe†ïÖBg<ºŒZFÍ­öe”?•ôŽøt¡ƒ0~ЉV>ϲ˜•Ê6d¹¿3>“Eg-lxhÛ%Á×Ò™¶…-ìÑÏŸxŸaü ãýRÙº[u g%fÑ}“ñe‡Œ®°?ýЇØ/¹šdìO|Kaü ›ûåWí×)ÌE×X-‹¼÷Fv˜~ø* ëZçÕò÷ŽdToÉô˜6þÿôsƒîe»÷ï¤ý³É ʦD…—xûÁkU}ó¸îv÷ ´Ó"nç,?ÁOñûŸ~³wY©ã:¯*˦VGõ¿È»Æ¼¥×_Xްò?™è ÁÏ ºjº†ÛwÙe® g“:2~rä^×ï#+¦)M÷õ–šûSãFÛ³?>è¶q6óï²™Û¦¦CƒG¯ ±u»Î ù'·ù$¦_\;Γþô¸éän}?EèVv­ÛÂü.‹åT¸lE=,è—ßþËuƹ)v±§¶Ž'ƒ”þÄûO~ÂÐiæyµí]æuÚÀÏç\½ôª¼œ²Å‹e[ÌzÚq‡õÉéßdÍeJ<Üfz\…àg ]Ͳ’ìÏ’™…ÅÏ©ÏârèÜ/¯˜¦7Xåm‹CaŽT°®_Â<'Ú©Y¤Ø¹RðS„n§O½†ñÉL×v³"‡ø~á7ر°369ÑÛ³›–ã¹~„¿½õwÁïG©ˆï˜Ìß9áØ.—®l½³jÕM¶ßµ«Í•t'j·Å(Ь™?m»Óm¸ØR?èŽ×ân“YVòœ—?'åÒØü·ûµ¿Å-Nz1Ú‰öÎÔo¿¡®?ñ>CÂøAÇûx&3vߠݹµ¹ÔcÐpƒ×Ñ·ØnTccª9’µç¨êaü ûj²-S†ù=IŸnr)—êy¯™5é6ëÑ`56Éž&½yþâxꉳ§ïd¼ü ¡keÜS©wKÃ8×ä^ ¹´0évèáàÛŒïû)¦øNmÍZ]TPGÇ&ï& ã]]¡•†½V°6îm.¥Xª.žm$«ò ¸Q´ãø …ПN?èöÌíhö`¸†ž^68Üðùlì£wßVƤß×-¹3Ζzé®)þáÓ[ï·¡a;ÆÜu«/ºG'“ÚK:Ëïo#¦Ö¦Ò›½µŠúÑþ,y(ÎoJKb¹{Zí¨µì­:Ø»ÁÐ6Þì§³©Óêh;ÁÏŸf^žœð»½à‡ ]ƒMq¡Ï'1]{W÷{4$ÀéJµ ÞUý“.gÇ|#Z7 vÞÚ‚4tF¿®¸ï˜Ä.ß×vó »Gœ iÀ@Ÿ*?:”qYì/ô©ü„¡k—5úe›¾Il…J3zqñ=z£<”j]ââ#õ¾—ç:Q>×~ßÞŸ*8ÛÖáÂøAwÖsi7ye"kð­KƒyäÝŸsÂòe1+Î m·„øþ„þ4åËúzñƒîàA{+Õ‰Œïã›GÖ[O¯=Í59,—.;„ùÐ',¸MSÒµ­[.ø™BÇו‰lÆu›Še3óèzDóÀ™~l‹âmÜØqޤJg‹?¿TP¿¤Þ£{ ùºs–а4‹Df™Õñ–uy4kÉ:£‚ræŸÆ-¤ìIé7¸g­b4Õ^4ôâç¯R‘Û­-Z·Odƒ8›³yô¹¨2¹Î9«ú3ÿDs;2O¬Ä“¯ ¬N.]3ÅÂüƒîÝçgÛ—&0¾¯h­î¶?®ÝL;ÀƒÛÒ´…ïîÏPP-]Î>t¼½_3þøíTeQ5Xùðõ‘3 ¶=ÄËtrûÅÄuùï© yºÂüƒ®b–wS§5 L×–õwíNÑo>?CÁ4/ægLy»ˆ>ª“Ä Þ :Ѝnœ.Œt‚8C¥ÆuÉNítŸ¦iüö×ôgºiÙÀšJ;Lë!Uï[+ŒtSu ãYþó:OŽ»Oo ãÏêéŒÓÓŒ&_ûÝTÐÅ“cßtž.Ì?è2%[-Žg‹BVŒ[èpŸ"®”GTìõgéƒí¾'ÚÒ\ýŸÓ£´·Åí…õƒ?oè”í9#öxæïÙK—ûÔ¾Clå²"ÿª8Ñ…ýnÿr­L}é6~¨àÇþ»Tt2âòŽ'cãY ζæÆ}ê5yòÞnJög}4K·ò§VK¸Þ‚;t¼a<ãÜ0kÆß§wwiBðR%{ë}éóéKÄEÝ·z¨_"ì'¹nætœ;x“Ø8fžäP»WÙ}Š2ÚñÆ6XÉþôKô-oœ6*\Aí8;Å4{EP€ÿ k2ß¡î ‡X?ÞŽmÓ’M›&CÙÌÌîg°£.‹t‰”ß×UˆŸze¢LÇߣƲqâ_õÎk‰÷5 d\×a¿-=®¼–ív^N6»Ë§œÏÆ:_Š5'Ç0·ÞKß ÕÒxáB «øq½æÏý6T‡{ܽå´KgÐ!Œt¥/ô0åc˜…¯¿¢^®–¸.Í£Ùã©£ëÛbK_ôŠÏ *”çòz¦‡0~й,­žiÃ:öæ:kéËtÑâ7Ŭ»Î8NLzó'ßP[A¼ÿàÇ]HKîÁS³—>¢¾µç“Luǰy« ¦k;ïb/ø*hõ@Ÿe«?vè* ôÚmS3¾ʧ«g_:¾(ˆñë ŠçÚ·PPŸ)W^~wáujèÎL{½³š)=NךšO'—¾7®ðªê /y;tÍ¥æ â}^„ùE²žyÂ1ÆÂÄÞýJœòéò¥I¿]~±›·ËÔp"][\¼žnÕL¿je¢UÈaÑloɻ׋÷æÓ&ÝFV0ë`ÝõÅûi|›¦ùø^^¼üzáI ÿ ãûÓG³°³É.£®æSRI«í/³/å ¿œ\ä@Ç.øãÈé™ïc‡Í»…ø ]hÛs(Š1Dz˜–wò©Õ°®7ºü—ÿ˜Î¶ä¶œxŸg>^‹¡S4‰1Umˆdºe}^>ÕjÅ9ù„TùEò}‚åÄû¤~ÐÐ=8î²°âú†$y&ã}>Õ× !ì®÷e»åÓÓ­ iþÆ5ršöü¼}aý]ðëþw#Xõ°7O½ÐÓ•ÓúÌ açßw’µ‰ZH_^”]{»ZNýu +aþAÇû€…³I9ùÉ%Ƥž=Êu˜8„^ÊZÑðÜ·±y+åÿÈï%Щ¥v­Ý0œñ¾ëtÑúÙˆòU!lÀb·{í¬(—³Ÿ±•“~yô¥|…?«—‰øç\ž~X3gq½¬ÞqÚÖa=fEᥜÁ»œŽ±Ÿ\&Ì?èJú}ßx{dm°àơ͔¾Wü­Æá6qI?—-~ (¾^Øö®ÝäÄ÷ ü¼¡[¿ìGlï)¡,’K»î”Òí[§Â+!ìðºmD‹èצº™œL#¦n¾Ÿ/Ì?èjÝøÙX9?DØÏ* wO'Eü×8èÚ÷w–SâÑãÒ„ù]Ö¦éÅg3~Ÿ±€j4ñÍ„°„6÷ídKßSÞ$Ç“ïS$ÄOèæêÚ±ÖǶۼË- Sfw}l½P†7å¾k‰˜ü¸¥œ3VNŸ>?»Þ©LÈÐÅŸí0ã“c ËXÏéÑK¼Ï½^ï e»7,»)¦ÙX½Ó?ÒÙwöâ't»®ÚÍp `ƒQ5ÿ]@!ÛWZ. eʶ¡}ﮓÍÔÅŸô£º·kõÿVOX?Ô(=<¸wŸ±­’õ>l¹ °y!åì˜ú0äL(ãº?YdK¿$ujŒñ#,rš è&øyC·«@6³N ‚ﻠţî…dq¦Éºáᡬϧ>úOvXSuû¦ý7ñû‡¸º­îu¾îéÇêd-ÌzjZH£—|é’ZåûÂn_â×'ÂüƒNg¾ËGðo+¤NY|?”%~_ûýÉ©y4çʖᯢ}é–ñ¹Äþ‘ÂúºŠ™cF‡:ÉØŒ™¯Ì]TH»&o=#'”ýñ<êô­[©ÜWð7Æ:~?÷×öªÉòB2:ÐàÂô”P¶âþ¾àDé\Zæ÷óòë,_}½³9×SˆŸÐõÓœ}òrà fÑ®ŽMßÍ…T¶OñüéÁÔ¬bV¢óÈ?`šÅš†~t¿g 'Ì?èhˆMÄ”יΆr_!}\Js& ]™Ôo]PôˆèçG}ý­×\ôæ_Í2QTYóöÉO®2½5»k&œ(¤õÓ|;­=±Ÿ;wc`‚5 *’d4îâG¼ßš0ÿ ûn½*áWàEf£3ø+¤E;G>ŽY»þø¹ÕÔ5Äö¥i5}v$ ùºÃ~¬Žp:ǸÕe‹›…ÔýYGíôÖ{)æ÷ü@‡.bÚTº.²:¾ÿ±¶[¿¯ç¿O1t‹”­*žb_ó~š$/¤D›ÙFwvÓÈÇKTÕ®ÚREÑ6Õc•/ÍàìÜÇ ãÝ ¹ wMÓ;Îê]§È *¤˜ó3¯y“Î>Èw1YâU ü}‰÷KçuRè†ì|t¦kÆ~ÖVgPTHîÞ-Wî´ÜEVkrëå|YHƒV/—~½„qoâõ¬e„?¡k<Ùòù›Û­ÌY!MŒ£Ú2f'Í7ééI²"‹µe-?¬÷¥ÉíÛ[v¹#Ì?èºHé3.À‰•nÎé;2¾_Wi—¾ƒ¢8»KóèöYù§‰ã| ‹Í&Ãn ó¯V™èÆ “÷ÝçÓ­žÕ/=H(¤¤@Ÿ5>¿wÐWξåô\®{!_òœÍ-x„ù]j.g´‘–šÔÛ“THºòïøNòàÚŸŸžCíºù§0ò%Îý®Y=!~Bw¬猱‡¬7¶÷é™\(ô…ßE#Î:vnõZ§3Öõ%ÞïDˆŸÐUsöÓá0uô yº#{ñ¾zs©ÓÉÐw=zùÒlQÄ~Üðçøwi”ïNžqoFÀû볪‡^H ÕÞÅ<û.˜GËt0¾ô{gÄ,Ô-Ðé–Ï^§i´Ù¬“ãñ=®wèXWµ‹œtÆk–´pêÙSæß|èaIýSÞ{„¸ ÝêÛrdhOÁG¦ì6·KØI6fšì\HÚà9ú}¨môþeźºËû²[K¿HÎXE6,¤O«>ÚÙvýY§Øìþøù™Ï?ò¬^í2ï‹v•ÚFäw4+$—Õ‹·)ó¶Òë·+OèäË[§oSÂ]çÇRø'rº6n9ýñ¥ÙÒ)Ó›ºÿ˜Ckz ó:uZiB¬7é¶™wRtׇ¡z;—RµVŽ)ï7.ö缩šóé~?§ óºi)fQ·×ùÐ$±ßñºBêüÒÖlô7'âýè,)/Q[·Œ7›4Æßõ…PwÖ)Ó_âÜ-n:Ò¸ei™ýœ¨€³¹}8Ÿšrå^ª7Ùµ9p­§PwBw襡¥u;9É8›I‹BÚ"<óJCGâýȬAÊèôoâN!·<â&tY©§VÔsTЯ¼™§÷M(¤—ÊöÏN^°£?¾WÓÊû êìC¹÷ÚY5Gˆ›Ð}éx¾Ñå+þtbÐ íÎÈ'm®&ÿÚ²þø¡HgnÙÐ¥9>­~.é‚°îƒn0×v‰’øóÐBJ ÍÉsO§÷‹¹BÀNðçö!ÎÏcž÷ »Ù¯u’Ùg%µë’ß%·A!5z³¾Ó÷„jl}Ôª[ãFÙ¿¿éýzG ]í½+Þ”ï  îí¶9ø¥€œ­Ts<6‹UU¯÷é£ÕtK|,Èûë·èø}¡@:?Í'ôbIioií°˜õxÒõt´˜Â[s+fo=ðßý‡0ÿôËDÖc<<:¤픾ÉT½Æ?Ç&ŽlºÎÀ†f7¹}jåFo;ayAÂ`aߺ‹5טyU"[cñHÀ*ÚÒ'iãå¥,ú@‰êèÅ…4ôî·´ø Þÿ˜·"è’kœœxÁ5ˆÚqÇg ¨ÛÓ_'T+—±úõö<ÖÝ’è %½…÷+Œtr/Ÿº×ßQmíäÛŠít}ËͬCÞËXÛ<‹–û[Ð¥•56Kgz¿_$Œt)…MfÖ· ¦ßuûÍÏÐûm¾õP93þuÑã¾–4Ô›x8#ì•?¡+o¶ÅcCZ0 :üäì|Ôñ^ òÇ<î»T‡ÙÂþ£7ýjÌh u tŸöuë`>4„¼¬V/Òµ€Î¼¸÷¦+’rÆZsèËSÎP×[ð;êèøõWíX|à÷†T±àXa‡õŽ‚oá\²i>ºb€•7õnÉzúYuKÝ2QèÎøÃÕB©çÖei>­O|z~ÆPGÆûpÌ£‰{›ÚoíM:›Þ>ÂüƒŽßï ¥&Oê/4SçÓy_“¤Q£œØ³ÁõƒGÞ°¢­C}ÇX.ò&­Æaü [÷Ã|Y|j(ÙHl6öR> (ÙøäÖRvßyxæøU‹¨w«^¿Úxù¦žÎ{(ì›A·XW ‡ç^9Ä5Ÿ¶X2é±xE•Ïîëóݪ-Ǹw‰­_í«?¡ãýªÂ¨“®È§ê«ìR§wtaå-#šî~aKó®¿êûd»79ݜҳü³?¡›€U½ÇÓ0!ÎæS á…ŸÖ1~.¦ÄYe~ð¦­ó–†¨;ç~Ð}=_ԛLì¯êåSåöWod]t¡b2ú¤ÞÛuÿœ%Ð=:ŸÙ5à†Št¶K÷µô£ï´ý±®¬Fí)WçgÙÒ C[cn÷¦€AûÓW¼æ_=ÔÛëÐZ½pâv äZ Ы–x¼Ó6¶w·Q½˜¢ÙØmŸÞÉ(yþ¹¹Õí…ü]˜-çìN_§s…ž–:®™ÑâY¬i³Üо~‹hnz«mý£eÿ8WA·Ì§Ç‡ÑáçùñË-=T:ÖÁq'“$»;¼\@]t|2ês¯VdªR˜ÐY³ihÞ&‚FÎÙñ¬U7-½{¸Î7ÏuÛÿ|[ص ¨o­²Uq2ójõнÂüƒÎ|˜yÉåíT­ÿ •§?Ý'õ;ƒNuçKØÓ§m³F¿\Hïϵü4åìß:?~ÐUç®×<ˆüNî“îšÊ˜Ý,k,wiCñ6\àñ¦óë¿pRÈÐýêÝ{ÆÔ±wHwÜrê>…F®CÄÝÃZ½×_}c¥Ù8Vßy¬+â˯ÅW‡¿Æºo÷ÚyzÝöÿîSn§¢ºoíc³Ê?õxî@“¶q72¼iýõÅûO_ÎmëãyÙÕèÓZ‘äoË­ïÓ÷èdÉÏN«|߇ »Õ£Æ/€½º2ªpn êdàÄìÕ‘4_gd“G[–“ƒËƒC¬Ý™‹²›áNÔ`­ß‘Bél¹7õ t Õm³ä÷#©¾îÂH$}‹ô<Êøõº¥ÿmj+#Þ×V¨_ Ë^_ñ!QÅŒ8Þ±×±<êa•-*mxBX‡9Ðõœs¥ŒøsáܺÃʽ£ˆ»meh™GyKûo!î´viŽÉ£fÍ»¶[Fc/¿/Ÿd%œÛBÇûFÓ™KͶ9wÌ£5{ Ö¼ž"Ô·¶T¯Ù¤ À2ªÓnôÎ5Âüƒ.º_Ü®ˆÝÑt@¯DôìžàO"ø/&·Ï{¿~ê芖p†—Ñ´Yg€|†y޲¨[+„þìgtX1©vø +ù§ÿH¨_ ÖžàÖ{)£ì_u¬|7Ý£pýqÎÞÓ'Ç_YÖ-$•»ŒÞ¼˜ç¸A$Ì?èŒá£ßÊ›“玾G­Î>îv£ ˜æyYÖ8}`!Ü 4~u]FòE(™…ø ]í«OO쮦>ã‡}¯qTk}¦\0q»óð>Ûîv]Ìdd0K/A#ÄOè._Ùì0ÖN-øÐçÒÔ¨ê}fÜ ¦;½ïˆ]ÒçÛò¾Œ]XÔÉ/QˆŸÐf?êtôªšzO¸Bÿx.Õh=íºlG0u£,c"1å°®gz>•ÑÒúúòƒý…}3èü³ÖI¨Igj‘K»¹ã„©Á”–|dºÚߎ>[Ÿˆý$#þ¼N8w‡Ž?_ˆ¡˜_/‹‚[çÒÞ¯½ ‚…¸gOþµ©çM3]ÝÐ9F8w‡î wÌmCKfô©hQœCò.?ýÊ‚èÏ>ë¤qôÛyÓÝF0ÿ–‰œS›[ž‰¡Cxu¸žC¦~š›½%ˆ2'·^[ÍÙ‘®göxù®Ñ—¹ÔôætnýdkÎçÄÓPM×.KsÈâúÉ ëzQ?£àÑÓILñµë÷ð&ݱü,áÞtœ»ha“XJÊMìzyÊǬ$ZÞh}äV]›Á}0oêû:×zµ‰poºÎ z‡º›Ç’tÕ—GßfÓ˜Põ–Ô‹¾îsº³¯=Fúv0A|áï— óº±ÆoËœÇR]D¯“ÁÙԸݣ_«Ä”ê¦1ÚlGÒØ&7æ¼’‘hy³_F ó:Þ'.–Ö­nöæÌ–lº{ËúÑâ~4äMÚOg1•ºŽ©ñ"MF/2ŸoÖR?è$oRο®'ÜɦéÍ2ò«ÒÉîN1ñ¶¤³IÎ’Ñ­Ýï) ñºï†Z™N‰|!³éëãVÍ#ïл+5Rš Äs†Õá÷ß2’Dì®þö±pîרLÔv× ­ñz·Þ©™”E]Ž©͹@›¹ô{ØŽRƒ<æoïMíG5»Ðõ¬0~Ð(oÚ(*!Žvݲ Ó?šE6;§´Y@|½ä@w°ê;åâMº2´“pï º¬g^GUzñägÅmfÑÙ†‰e?›Пû@îËVµ)\áMûOÚä¾î B¦\92žÆí¹0¼U5Q~÷W ÷;©7kè6uOµæ]¦=î{Bǯ3âÉw¬gaTQ&™Ø~éãÑJêo>G\mú2Úê«?¤›·Gy:þ\'ž´†¤8yeR`îV–uÜŸ®ië]8kµŒüm&NªƒçZ5*ïhƒuÂ}Oèø:6žøý¶Lò±c=ò¼äõDÔ8d•3q.É}¼ÿqß°„{½ÎÜÎZÙŠú…›dÒ y÷W ä4µGÓéC‚–P[éà\c¼ž©G­é%½ÁÆe¢jl’‡‘uñ÷3(õGðÅ^=å4â¤Øç|ˆ#¹9j>5ð¦m_/wô€×Ag°hëà{§è»ßÀ¹ wePIûy÷´ýñ½ìè›Ò³š7µz›ôúÜ,aü {çºxš&=?Ø ƒä~vêØÆ†Ê›W·£¹º oú|¡CóÓ×ù{ubè\/]ïþ#Nyï™=år:%g6%÷%Í9ó˜Gµìª¾~×I û•ëú¼û€DÚ´¶mÃ’¾é47÷ãà;S|©‹¶F”bq׸,¼IwŒc.Üû„Ž¿H%—šÜŒŠI£Ÿ1êõoùPXÿ(³YvtÔ„3„ó&={aü ›Ç]‹9•H†t·ž“Füù”eÑ·ømdO›Üvi³½¼…u¹p_º»êj}Ç$Ò!1g˜JíjXv˜òÆ[ˆëÔµ¾§Z~ëéñŠ×¯…ñ3(‰¶1úm"}<Òôc«­©´-Ðoâ¼ÅÞUñs•θқö]Ióɸ'Œtœûy·öI4î壔 MRiÖƒ&¶ÃBeÔÿá7å™õÕ²£]š÷?îaŠ ûµ›3ÎM"9g§y3…:-y2cð¯Û$ûí÷õݹ%´wuÿeGó¼){v£%ÍÄÂ}kèìu…’ˆ»ý1iT ¾6pÁ°ÿ‹½3Šâèú¸ŠÈ¸j"*(¸…¸D4&Áe\AQÁ}XqWŒ ¨„LLÄQQ1"Ž€:À@Fq¨ô-*Žâ£h\P‰M”‰QB\ðù×t›sÞï{N>ä$ç žóû$ÿ¾=]Õ÷VWÝ[5ì0Mú5Áw¥Óz»úÆ·߆ß5çI ùä±Ð™—1w•Qâà˜‰‹*ËiÞ5 dá‡HΖ—¬‡?ߦYß/: :5t_L‘Ž(8S&ž·XNëžÇ¿â³Ò’éoXÍ %!oïK+Ú)ܧº $ëcçî•ÑÃGs¦Œ—”ÓÚÍ…ŠW¯ÓÉ|ÊgÛêû¹Ç€ˆš 2ÞwtÕÐ\—ïÐŒ§À[mS§ž#îÓ»§–¦“_ï.O#ƒ(Óki|Sý|“+æ[wÀ8ä|ôrGžÜ÷ŸáúÉ9R>œèt?Ü­+?÷@o+êŒmË ?'vò ˜o ]ƶBÃÓˆ²Mßßä))1ÂóúÊ4z·Žzá2;;ƒ’]j——#æ]C·ê|»_ÚÏæÉc‚ŸnK$Ows&9<í˜FNæ‰>95e˶¶™âóÛ:·¸äßÞFðt±yq~¼OßG¼ŸáúG“¯h—ÒrR&™ÓQ~ý't£Û ¨WòÔ ëO–ÑîŒq¦øÁ©d>æÓ2ˆÌËáÛ3 /ûÎuÄöƒ.2ðÚW’y)ëä¶av™¸¾u€%ªAV!ÔóÑmïeRÔ¾«»îÎaÔC×cËÈÅ}šóûK©ßÓšÈL£šjÚ·HY×~eæß’kŠ3IËc~Ι¬†ÎËœÀÁ‹y¥d¿ùóEomÔT÷¤wP”‚¢#w9öTgÒøè™73òÅó0;>ðzû¨×ëb<ŒŽÇÉJIâ°ä¥h õûÑMu1' dÒ©+§ÖfˆçaBwÙê|•á"Oµ_ŸèóåÏ%$y©ÛT›š,ί+èMü—-\#3IÈ/Ï3…Žziw™'Ûˆáø¤,¡ª¯‹j^[ìçëBép$Ë<ˤMC*õŒÏ3…n„ù ^žÚÝzúç%4À?³uäž=br094µMpÜ™I–©½C¦z‰ç™Bgž®zt–n°r€ÅI”ùܺbiÿ È–U™´ýáVD\ñSüý±Óíq37×ôÝx–&&÷Š^±[Ì‹  Ç#ú4ÔâþÒÊÝž¶¸,¶ti+Íß^ÎÓú¥ŠòÉt–> Y§ÞE-»¤Ìs=/§Ö¦Û·ìÈüçnVCÒyÒ€R=O_Ä—h=)¦žSŠ;†ÝÛ)æ›Èi÷–˜œI/ƒfí-­tM¬ÑÞ__?™ËÓGì1í-¦ß×Ul|á´S\ç‘Óüy±Ñ+gRf`Ÿ7q¹Âïs„®GCÀ„­‡xª±U¦$b¼´Õcy÷Ѿ‰ÔãMݪêäšã?ôÎ{Yó"oÙñÉb»A'ôsž¶Ï]èñ[¯Oœ/ßA{ØÌYHÊ‚S;‡g í…¿·wŠž5:†'óôcFÅŹÌ7xn§S»_Õ¸x“ý¨ôCs'f‰ó…b{A7wQ­ïtO—-½»ûQÒž½ŽÕ©¨Õ„êÅå!ÄfÏü§d‘Á•¿4&Gl7è:{7x2O’ ^‡¢$EÔdÞt­¡.FMžéz¿4«¿®lÔø,:¿é¹Í°Qâù³Ð ´ØùKí žöŒmWçÒËêEMŽ|%Ö•(þø]c̓‹çCw§[íô¾Ö<XZ΢Brß9c˲aJêc^UP½;{ó²ˆeaî¬Î+nbóÀcóˆqß…>+£õ¬\Ä¡’¥w¨OÇ‘°>¯ ¯²v}"ÞË¢^ƒÇõs]#žß ]΋ў³+ÊHÈ'ÐSýõÝ6…l îŽó=-¿X@iÙã{¼rÌ¢Ò"é°èOté¬áp °Ý>JO맘Öp<ŠÌÙ1TÌcÈ"!_F¸O9tÑäMALEµ™‘Ö_OGº?UóãÖЉ>y‚éíÑuÏt>YtéûªŸe/„ç ݹ·5µw¦”Ñ4ÍÒ '8’ãôþ‹¨åôî;?à°ÿÉŠ,ðãÔ9Óëºi çî7ô,¿Û8Zg™ë{fÌbR{׌»2$ˆ6uõ‹ÜEçš±!žÿ Ý–¾ø¬”j^ȧFE1áX«)+ƒèå±Í † Ad>» ‹^/ñVU=tÕÐåó]‘ñŠÇç=e«ÎP~­Õ³Ô_J¾¡®3'{>Ê¢.uë÷õÏ_·}àÑ?ŠU0”R²ÍÃO*Ÿ¡iwü1¥§ÖüûóªPÛÔ|Ò19WlÖ|ù©Ø~ЭՕ,Ý?½”¸Ö³swî>MmïúÔ/žË *3“Û£ ÇϦö¢¥‰¯6<ŽÛºûºÁ¦´Î¥Ôeï{¿ž¢¥ç,‹2–pE¿_=2þÑBÚ¿jË.‹O´´wúþ¬ì*A'‡n‘´ÍÝŠìSò£÷)Rw;PØT¹šÛtktRêœ0úaw¡ý6- è*Á#Ï_‡®ïd–©TBWóS¥Úïȵ²¸oºßz®RêWqg!9%……ö·Ô’ÎxmÑg>‚N ]è)Ïný‚JÄqÇwtt¸‹CåFNˆŸ Z5\õâêã,òzÛÜ®o˜ ÓCÇ=g‰%ä¹¶ÛØÁùTÔ¹èë¸ñÜ2æn·…Re˜ìVÚAf> [ÐUC—P>éê•[g)ErD²>V¦´y4¡\É-_¶¾xÔ¦`’eÄõ~þ2‹–¦>ú9XÐ5éôÀcž:]ß,é,™—Õ»æQtîÅ-Z~ŽšÝÌäH?ߟ1²–Þ~;|yú Aç]“ƒŸ{Ÿ%V­×y’ÖúÝ?”Uý5WèW_3&€’˜[C»é1xã–ŸÄöƒn¬yé, õ'ˆçÓBž.JàR.ÏpÞ-§Ð;ô»jÉ\îq\l?èZ»&æt1]ËNÙqgè õ’õžscëyÈEñ‡ýÊjipÕÕµÓ^ ºXè’î.ø`I1oûûØ'§ºÀMÕ«¸ça±W|Òä$1thÉ൤˘‘bûAgî®ÝŠiúàk uÇhTTį×WmãÌÓÆkèä Óéi‹µôþ‡#¡âóÔCgùÑÕ{ Eâ|î1úö1;¨~;'¬’G¢%sYбý Ëf‰óEô~çNi9Çs©E'ùʵ;¸A¿‡îíRD A݇7OÐÒãgnoï*¶Ÿâù@~nœkìð•ýjû\’Ÿ,h¸©Kä,r=·=ý1˜Š ïœU%k…vÃßß[Ë* )g¤¬ßº£äºíÑ*7«]\ßâJÉ¡êQµÔ¾·Û¾kK;Ðż¼ôM·˜BqùU–4õ>±›[?,wîö¡4¬/ûrÒŠùW‚N]úßaO\ é  §o<ŽPÇ!ýtº“Ä%m ½~&„:…±/<lj“6Mß ´tû>¨›uY/޳teq[3Ð7Ü»ú·—µ,ÑZKON®NurÚ(´tÃu]»[¬ÕÓ0SXþ kí·7s]ð^n¢¹` ˆ¦mœ—Ý_K‰ØÍ'èôÐy_ü¡…×{zb½ÀTŸC£Ž~U87™+*Ø7*.&€.îºúî‡ZR¶í4ùÊká>«¡‹^5]ÝiGuÎ]ÚŸ¨Ê¦£lú·ë>®“æðVý|*˜S×s´–ÿp>u8»Ö}Ã#-½·­vêãí‚N&#dQô¶ÓTï`×..8ƒÚ,LY¹ŸÖÛh¼ùC!›¬FŽÍšuEl?èÌÓÈ•§hhÄrÎkІ¼VJº¶¬æ„õ¤@Êaeb6Ù¤ÿiЂèrA§†î?³ª²Ý)Ú=ƒàÓé±Û¤%j.¼¥žŽ¦ÏÞvë=Ñ>›äÙ7‡tÏtzè.¯\=Ð}üwôÛÇ6–/G¢q+:ûGà„:¹`bÙø[¡æKÅöƒN­d ùTxõxݧ)£Ûæ¼Z«Tn¾s·S­ƒé•dzŠm²É{׬ڡ7ÅösÀsÌ «òhôµÎ㟌N§^qþc÷«SÿÈ“þéõ÷!ÜS- st¬ÆAègŽÐ©^¯·°È£²qwLL£~—\štNㄼö@ºðK«QÞåZJéÉV´tËúï¸ùéIb³Kv©dzÓöfÖÖ4μ|¦’ÓˆÅ=Ù¡I†ÏúÏöA'‡.´ã‹Þ+NÐg ü×U(~úémkkÒ¸aÎŽ½îyΣD¥éƒ¥ZŠy5<¼B)èb¡[ZÐávnÎq¢÷_>±KMÙ὚–ôLçXÔ18Ï¡º¨‹ËoµôÛÖÏRwñýƒnMØ€¡ö‘Õ¿ ’eûÉ÷™z«qZ:hÓ|ë²ÚYTôÚv _‹l 3/T‰íÝ“žþ[œ‘¶3ûÀÚGßl¹ë¼qc:‡—uͳ©³CÍ€"ô—_ú,Ú{_|ª¡û$hþÑ_ä¹t†-+y&SƒóäÇÓ¹>o6Éâçï\ýù6]²i‰¿ïL›ÕbûuÆwl{6’;JËëº/Ëÿ†¬[N¸÷ð÷t®…¹x±j¼‡Ý²Åu° Ô¸RãH±M÷@jÜ韱û§û{nìÚ²¦,7íLÀ¥ËG¿jÜéÿÍ>HûEþ¹ý"ÿŽ}þ·ý"ÿ®}µÿêóGºÿɱQ㸨q\Ô8.úëÇEÿ¶1ó1áb{³g´>@Ù”Õâ·78(0‰«ÝBÿ:`´`5Dx߀ ð¢sá@cÉj+p]85 lÁrüq]à'§°b¹æh/ ÅÍÈ$,çý˜€KK–{‹þ @ÒŠå€Â>Ðc+–‹û@xñ#Ò„M–›ûpž>@Ù–åÁ>pƒ3U´c¹*°¤p¬²ö,gö ¸HÙÚ=ìt`kȰtÀØ­eÂ>P^tÈî h¬ÙZìÃAû¥ [s€}à‡­°esß°¤pÞ²Nlö ¸Ø±9AØ ±gsS°tÀhÏæH`¨/:}w4 Ø#øeã¾z_H ¼ÝA8Ѐj` è”@ꂤ¨A"`Ê@,È&à‚*IÀ$¦ÿÇž×ÞÙõ#ïöƒdcšwßisFÿ®±Qã¸èŸ=.ú7Î1?)¶)ûÝìÿü ð¢ƒráÍX?ž-ÂaÉ,X½9Ú˜€KsV÷Œ¾ @bÉêoqm FKVŠkàEGçÂÆŠÕÇá~àø|€RÂê´ð,¡¢%«‚} …S”µbu+°LÀ¥5«Ÿ€}`’6,öÛ°|rØ*À‹ÎÔ„M;–_ ûp®>@ÙžåyÂ>pƒ³UHY¾!ì)¯¬Ë{ƒ}`.YþìX³| Ø:`´fù)°T€¶;[¶^ûpà>@Ù‰­Ã>pƒCWرuLØR4šÌž­§Á>0¶®ûÀ$pü è€8"ø=¨n  U@Š!± ˜€ †$ xx€H Fàèˆëà ,î h@5°G ñJ õÀ GÔ  H„d äpAP’ƒ$`( tÀ°ü ðbðrá@ª=‚™P=¨nn  U@Š@'± ˜€ Ÿ$ z€H Fàˆ èT€¤;P ì0}€èA=pCU5¨RSˆ:`Ž®þ@ø¿qÏì¿úl‘wã#Ö}×Ôþã£Æ¹£Æ1Ò?qŒÄÞõX±ÍØïriÊöÈBv/ÍØ^MèÓ@ÓŒíO„ëÃiù¥Û'¿¸Á‰)š³ýZðüMfÉöÁµ ¸´`ûXàÚÀ$Vl?\è€ÑŠÕõãý*À‹ŽÐ„MKVç ûpŒ>@ÙŠÕÛÂ>pƒ£T´fuŸ°¤pš²6¬þö ¸´eup° @ÒŽÕcÁ>Ðc;Vû@xÑÙºƒp ‘²: ؇óõÊ,_öœ±¢#ˇ} …c–Y³|fØ&àbÃòja€Ä–åwÂ>У-Ë3„} ¼èÐÝA8ÐØ±ü+؇ƒ÷J{–ô_öÎ;¬©m[ûرcdžØc=Ö{ìØ±Ó VìØ±€ ±cGi¡…XPdR{hŠÅ;vìß;“©çìó×¹÷~åÞý¹Ÿç÷Ü}öå3ÉZkŒ±Öšs¼˜Hð•µùzÌ,üÀÄ‚B A2°j!ƒ ¸ È–H6@’@"q(AÈH" à bA!XclàôÀ F<€€5Ž-Pƒ ‘|dÀh@>°D2²*Š€ÉI @°@¢RO ‰Ëø=0lj-@ ÀIͨA†Hp2à4 X"áÙHE@Ѝ X *€'ˆ…@‚ähü˜#QÊаFâ´j!’¨ ¸ È–Hª6ÀÄ‚B A’µþ@/® ¸ È–HÀ6@’@"!+AÈHÎ à bA! YÛ æHÜràtÀ¬‘Èmÿ/x‹üª“øOó§NúS'yšý©“þÔIÿ=ê$™8ß’Äç–â¿)‹ñþžøù\RŠâ¼Ï$Ž;0ç½q5ÈLܦ$ï5‡Ï€€fT¥xï3Œ ¤pÊÒ¼ÆvŠ2¼ÎP$æ¼'Îa æeyo\S@ eyÌÔ CIpšò¼gæGдª ¼wæRQeE¾‡ó TE%¾—óƒB ©Ì÷4c~ æ|o-æ:`°à{<1?Pƒ ˆeÀhªò½o˜Ù¨ªñ½X˜H¨•Õùž Ì,´5øÞÌ ¤&ß#ù˜×âkö1?ÐC-¾vó5ÈÁ^ܦ6_K‹ùüm€ $" E2P æH ràtÀ¬‘(ldˆ¤!î@ò%’ˆ P$P¤Ö€<`£ž  Žðz`Žä#@ ÀÉȨA†HL2à4 X"QÙHE@ŠÄ¥ X ‰)€'ˆ…@‚¤fü˜#ÁÉаF³j!’Ÿ ¸ È–H†6@’@"9*AÈH” à bA! qÚ æH¢ràtÀ¬‘Tm $" E’U‚Ì‘påÀè€X#Û5ÈÉXÜäK$g I H” ä $nð± HÈí€dˆ¤.î@ò%’¼ P$P¤HúJò€ ð± HPpg3ÿª“äfê¤?uÒŸ:)ÉìOôߥNRˆó)C|.pšb¼79ÆB²* ¤ï ó èyIÞŸç=ÐCIÞ'×!Pƒ ÜdÀhJóþ¡ÁΨÊð>–øÝ€ÁOiÎû)â8 BEYÞ×óƒB )ÇûËa~ æåyŸ3ÌtÀPž÷ÛÂü@ 2D•w ©Èûa~T ªÄûà`~ E€UVæýX0?°@°UXð¾ ˜IÞŸó=0¯Êû$`~ †ª|ß>æj!‚´ ¸Mu¾Ÿó#hÛU ¾¯ó)‚¸²&ß߉ùº¢ßoˆùA!Xò}o˜èym¾ÿ ó0k[ "È€;yÀ‰A°D‚±*Š€ G @°@òQO ÉÈø=0Gb’ `De Ô C$-p,‘Äl€ $" ERS‚,àÀÄ‚B A³þ@Ì‘üäÀè€X#Ú5ȉQÜäK$J I H‘8• ä $Qð± HTí€dˆ+î@ò®x‚XP$HÀvÀè9’±x0k$g[ "QË€;Ѐ|`‰ÄmT )¹ø=0GR— `$o Ô C$|p,QØHE@Š‚@ þ©Nâùý?º^ûOô§NúS'ý¿­“þ_ÔHÿ§ë#[qžðß’Ï)@ üoœlº8÷Á÷R+e îeßX p)JrOsP$¥xoŒ ôÀ¼4ï5±€5‚œ-Pƒ ðdÀhÌyh|@ *Ë{c~ E@T–ã=q1?°@pT”ç½Y1?(’ ¼G(æz`^‘÷ªÄü@ yÏDÌÔ CUpšÊ¼‡æGµ* ÞË ó)‚®² ï©…ù°¢*ïí„ùA!Tã=‡0?Ðóê¼÷ æ:`¨Î{°`~ "pË€;ÐÔä=)0?¹ PÕâ=0?"°+-ù^}Ì,äµùžqÌ Aßø=0G ùÀ Á¨@(R$%yÀÉBJ~£ËŽ4|ØM}† Ú1*)ø·ÿA9/sÙú–Q¢¯›èÿ]ç¾4&z²ŽšXž·:¼‹;k|×?„E%©s£:Ò–è^Ǜȡëq»RÅ¢ÿtûg´¹Ô·G4á&­Ù&—dWåØƒrwCsôxázÔ‰âÎŒQäW[³iõ!ÑÿºîÆ(QôáÖõ5 î ¯M[»ZÊ ;ÓíUŸÒÝ£hª±ñ¯èÝ,eÁ­ûE Ÿí4õà‚):jزÙ]Æ;t¦“ëWèOŒ"ù‡ÊD?Ž|è’xðœp*Jq ·»x·g»®šßýÂy7új-~ùb˜tfu äÕìS³[9‡Ñþ4kÝ|åVêl3oI{ 3Ú•Op¢¥—äÎß¾FÒ¹umý? Ë®y½hØÇPšärâÇ.5õ›6ã^×Úö«³³Ñ "’ºv[‘åSréøA§àí?w„Pc#2?ªq¼úê OBÙ×âËî_ñ²'S¿ûHÚ8ëÉ×°7¢ÿtû6ïôwmL÷ŸÏœ:cýz!²ºDP(ãÝ.m·ØQŠ[òÆÄµ‘äÑbÑyÙ@Ñÿº™Ví‚Ri¦±ÉfÝ0õj“ñ¡ìW¾¢‘¼‘|$}Ü5?1o¹8~üwqhvsΕpáfˆÇ&2¶û³e¿úJž?küAhL϶áú‹ã©/ía’q[Ûô±™÷C§!¬[VmË¥)wÊiŽý¿ôo®÷³è ¶î‡¨A‘e¤,×—ž:½écv›ë ÑlŽ3mã6í»q~³Xò¡¥8~õ äý¶D¹šŸÛOÉ›ZØÌò¥»ïëëú$„}f[ŽíêJ6Ɔ¹˜ÏЌǺyÑ—&/ª¿—bꮼT¦¤/•ÞÞz^ÛÊ¡Œ»ö¬¨¤…7ƒ+nˆúK!9tÜE¼ö–ÝÔ¶ ¤ôÞ«>t@¢½Ú§g(k»µü•ü×®Ô˹óÔ‘ £èX€T’­ÇºÊ•ø/±ƒ4ázyë}>4«[Ï2ûP¶eñíÂø§.ô`[Áˆ§¸ŽÚuºî,Žt®Ü­={Ü6â]AA·s5wØ eO[ÌWÅvs¦ÈÈÙ¾çD‘×e³ZNEÿèüjñå~t×àØÛZïCG†nò± ej_ÙÎr¤L|ŠnM¢èíÓ£mJ\ý“øïbléKN¼M~—ÚÍÊz¨:‡²øÕ­ûoH²§íâ›W.E½=^fy§Šë:ÕÐ-=‚×yÑÞé¼Ã§/]ô¦Çîw!Œw¬¾ÀŽÚ_2éMÅ(ÒK¸ó—8~õ ä¿ú˜…è—nžzÄ—Îyܽ¾íP3ÚlœN6?'¾+h÷/ǺÃïêÄ÷ÿ°˜º/ûv7„6R$»0pS¿ß}_Ì'­i19ŠzÔæNS¢tYÚjæºÑ寫¥…·7Ò›*í›n3³þ>»zÜ5šäT;âEg?¥·œ-â'têÛ{Nn4ˆ*g'žfË6Ñž[–»öú3“o™U° Y¹`u-þ¾öŽNè<¡[þqJH³äÉìÈŠ>åËZn¦óåçc&ÿös\wÏÛ2zi5–^ë*=-®?èL¾Iî,~ÚÞFIO7Sòáb8Eƒ™ÉØ‘4!Mž»ã<›vÓݶN‘¸þ 3sqcÁže,Wþ®Û’ø-Tú邟Gj³ŸÓ}¢;Ñ$çNo1_Ý7N];~×ts†ñkÙAIYŸ›ý(*îxÝFÕ‚’Içåδ?pË–ˆ­Qô-èfhè¿cU 7/Ù}{—qØ¡†õŽ¿¬¦Bn/Õ ˜µšU:Öz² v=þlAL%#ú^~,ú_A‡ƒÛ` mb¯µÇþ¼¤&•W̦vƒYŸµÎùÇ»Ðú¹Ö—¢h‘úúºÁoÄñãóýÕÌh×b³•NvÕv2<˜¥ýRÖõ” =Û­:uyÇØî÷²8~V¿ú0ng&…­ÔzTdY[û`ö¤o€îBÛL+Wyo”ðÇǺcgCvò†­ÏÒñÛ(oË;Åee0ûº SýFâL1·÷”õ^EÝBw½]^Å[5ƒÙz§bÕbí¦Uç ó®:³#»¹aÝ ºÕeÓÚü7QÔ—ÛfоÐåN½QjÝÌÖ!j¸ãò6þä0j–]éÄ`á;ƒ®h>·üœE/[UÎóPô „®­Óþx(견ÃOJØŸ±Ö£Zkugªk¿nô¸t¿úãcÏJØ>¼^Y?è¼µý¾” cÆU>vr¹9W8SyòæþϹõ¬•t=¥õ‰8?Û/qü¬ ä¯ëó†æá¬G×ÙgÝT{) YÿrÑsB„O‘3Õmw§Ié5QdòÉ×tÏ$EÚE°Öûƒ>;NÚG² ?ÖW†0“?¢#q—?·_~£¢ tܽÎçH6pÕw‹º­÷“©ÿXûÕG½ŠÑ¸#ŠªÙq‡ Ñ?ºûÇ ײévã%Yß÷“­y陟J‡0S_};Ú^—w`‹úK_DOèx·ó­;£E<@¾û¤%ÿî¹nƒÛ½ÛÓ£¨âÝ ú>2/ÐåWÊiá7GÇ ï%ÈÞ䤦A«·ŸšÌ*½·¹å=ÊŽÒå^ˆP¿Ý>äK‚nýê*\ªeÃîšu¼—|ˆ¼BOÞ0ø·¿i鸥ާ ^+/5yVʤˇNb4ò<ÊÆÎ9²ëñøšÒy¡­÷ ¶­Á›V×Üí)´47JŠ¢Wm8"}WÈk&íXSbÒ1è“uɇC5A̺0É,eƒíååàí(jÒÿóÚšUÅõ]˜[­ìaŸŽ±úF#¿Ã´æÀÇ×㼃+Ç2;R«G«¨®G Oqü 3Ú=ì8ÎLýçSÕž ¥£±ˆã“ìü|œèêºøã‹ñ9MßW?è?ØŸÕê;“Qò[ÁGhËÍJ,g±'©Ü¨Ù™ºMš°ðy™úŠãݾ/ùfœ`»7¸t„*¶kãwÓ-èwüŒ»õ!«z1-õ²³ê𨟸þ ³º"ÀôÆ]DìGÒøo{ w b·cÓ¿ÝÖ;ѳeGž§TÖÒÞR7ë® ®?èü¾qC€f´GË ¤k]Ò ŽL b¡OxãP'òÝáòNVAK™—ÈOÏǺb–W¯>É|/Ÿ‹9ìDNý¿û4•1®î_õÒÖ)?ú}‰¢…Wã^\#ú7*'l{x:±ò)6`ÿˆc¹‚¨DrV´cÕ ß¾r6õ#v|…L÷²lÕ[¦¾ÀÖÐÕ7zvêÔäm%ÓÞºõërÙôçÊ?-´£AÖÓº¢hðµû†–wL:9t«zø˜ÿlËP\Îo_:„Æ Ÿ@fò×¶£Å*}ˆ×E‘±Ýä“Î:£ÝId,kžxsÔþÀÂÉ=²ž<™âž½¶^Œ,Jø‰¾ÇÐy¬X~= Ýi¶çÆ“ö:†R¥7ž].ÈfXönÒßZ¥ÆÖí?3Š;zwë#úVCglyšµævÚ™¡¤ež§ç=>òû¾ñÛý'¬Lt™â¢©¿|tÍ;÷ —Ä1Öš½h(Ze3ùÆû#ÌäÇáJ OwÕÐ@K]ë¶BI)úCgò‹c÷‚sšX†QØÎ1Ã^·dO6oOI8çFf[W}í¹±~¨äLŸÓ¬q\ËÛÖgóGNXÒúdÙWQŽ+ô d›.*¾×|&í›WçH¹ÕZ2Å;Ñ'ºÛ­§µ®°5ž5ß0’ßaP‰c×ãén 3Ú8¿œA¶ãÚ±l'-ií¹¡›è7¿ë]d–À¾;3§ŠemË5·-- bÎnâF'óüZ2ÚBM1éì ³ÿÖéÕå¹ ÌêlÛAE¿^ë¥ bõ+®ÜѺRâ¡:ˆôZò89_Ѥó„nu§ýºÞK`&ŠHÊ1{ÝÖ~UÐïûi¤ÍG«i)Èg‰gó6&]tƒ‰UgØ^Å´G~Ü2Z»sò{»%:’Õ˜%εpm;® ­ÿCô‹‡îÁ"^Ÿa&ÞyÖ ç½ß¡ic‚˜Ñ¦^í@Æ-Í´‹(¹1ÕtÜóÿê;žÈJHhéé ÞØ?ˆ]m7D?Ý`O¼‹­ÍW䇧emíkÒ™5)ß<Ÿ/Yº(‘}3 ê2o~4uØ^"óÕó@6ÖhðlOÅ+\<4·¼–¤ëÚ7¥4鬡»¶ ëëÙë‰lã¼ͪ7ÒÑžÄÓã™ÃK üÈîÜê5Ör-Œ/ƒŠÓ¤“Cgê+ÎXÏ„'Ió×ë(sxäù»Yo3¹Ò‘b·óFœZjpH}àÔQ“κþ ·.~Œ n³c؈—:ÚÔ¿âPW 3ù 9ÑÀ]—Ž&Dh)nÃú×ß+š~OOèî/_½îã3Æ\¿ªOM9J³&µ(9ß'uˆX[¿Ž _›~ÿR´ö/}ô siwkÄäÆIÌTG¥Ïµö×Ûs$mí;±èË$Wá§ þž¢oYµíöj‡–x×ùãKMº|èæÜ•µ¸´<‰-ÒJš–Œ8FÏêð3ý¹mlˆ’®œréø6-=¦ {‰•„OCÓùä':‡$1SŸ÷ãtÑ#x±¾K3ù©)©šËÍ;wjɧÝ#}#Ñﺗ-º5ÿ¨Ob†sgÑã“øRk‹ëa𱡮+má—ý-mÁ eÓï"‡îf×Oü¾&±sÕúìßPí„ð™ b^™‘³¼u¡…ÍóVا¥¢y_%ŽtbT’fɬ4O7ê4ÿáí5;1eãëÃ[D;Si©s¾ç.-•>ÓÕÛ,_øl@ÃÛ±Û$³¨î¶Vˆ¡ºé.[ö1î²Qmµå ìyãò&-ÝþØëYôáSÝ ­1Þ£—'³!W|zð‹¡³ûò b[3‹ Î;åHo®;m^¦ÒB½Òï„Ï t-8õ<8™íʪž¤V­§º¬œÄ \¹_„#]z®ê×E­¥Þ}/«[ú ŸèFµ7¬×'³g†Ø:7vž$£= ò毾ãuTÎ7i©ì¼G/d Ÿ›fòVjÞ¸ò)™ÖTØ|³Ÿ§q4¼nÚÃýæ­~÷ƒF(i‰E1†-]ôî¼s€½IgÖç‹SÏ!M{¦²6Ü@0žö\xÑ+äg3ù~»Ð|ã¿hi…ÑϤ³†Ž»þ.qLeÍP–H „óÕ-' ý]GžT»9þ¸–z®óþQ¶­ð™‚ÎØf{C*sIš8d×öêÀ]f^†°Ç%gÏ/ r$[ÙàwçNhé´oúÜr­ÄñƒîÓÅæ­¼#Rw©Ëi|†Žr{âÌV½÷ä2e–;Òþo5´ŽÑþÅÿκ쪭·^Me?wÏÌ,~â è²'´El5‚WþŽäsìÔŽwéZª¸qÔõ›_…Ït.½Ç¦²é/Îé•Hí7îýÜ <„©¼&e'8ÑŽ3ÁûÛ}ÔÒQËEQgFš>gtËo,¾Y§J3ùÈ$Ò ÷'E‡°ŠµN‡Fô@¼.¼eQÐ9šâ ×½ s4éò¡[ø" êõöiìÉ·êçÖL`Tð=öûÛó!ì^—1 ­|\©oã¼ØS£‰»§W]kÒ™I—®ÔîÆ£¸35£³½Îÿ‚ûMás£vÝ=w¥m4mÐë Ÿ)踻gÖ¬46µK÷'¡²$ê½âýý=BY­6ò[3®¹Q•Qü(ú/Ç]Ýõ™Ëll|ÓØf»‚/KV&Ñû>^7â|CY£‘é *w=нW¹hºþé¾uà Âç :vêéÕUAiLý)|gY–D_o×i{+”5>øÂe Í î®Z÷µ–Œ§ãa“κeîK®Í‰OcI ¹P2Õ-{ÃÊA¢a·×ÇyîvÏ—µtõs‹-%¢…Ot[”º3-+) ‡ò$ ¿ ‰*}‡§’._9²¤e¾–ªøF¸ùˆã]íéÀŸÒØ»izøB÷Ä/­Ùf “ìÓïgIWÔð|äÝûÈ›/¤Í¾Ÿ>‹Ð<´éýÇ4f´ßÌH¦‡Å½÷õ= a.g,/ø,v¦b¡KÞ¬º­%™ñ…ðYlQ ·Û:ÒJ§³ø ?Ÿ–.—BË —6Ý¥a¿üdª½¹öùÔEÔuË-Š¿m*|ú [Ëm«¥3Çì! ÕCSh{§&Û‹ÖhØ/ÃJ«¤F õ绹»¿Ç ŸSèw®$¾µNgwJsÎj_–;¸j˜ÉŸÔ‰Œ£2µô<\Þ¦teás Ýý2OÆœk™Î¾»v¯Ÿ‘B ±æ Ôü~>8ãÌ=幊Ñô؃; ŸLè–wøiØ×)ñ»øé¥S©›¯ƒféJ SÔ¹}­æAWrKïŠK8Zø° ŸaèJìš’þ k:;q‰ñ¤þòAcƒ.½xgùÈÊóÛu4™¾‡ð©…NÚ¢wï†=ÒYÈ~nïÞŒ±³hCŠëó|»h*»éCÅasÄñkY ÏÙ7êݸ·©4´Yqéç/a,´Q³5×΢Ü&±a49$&˜}5ˆã†¿¿†«ÒŸïìÌi¹ۤѻŠ=ú&8…³Ç+?6IÛÊ4ÚØ yÁø8ö­ðÇ„n¸Ñ85å–Ù¶¤’s9Wþá7õx8ëâ—j÷£œÙœ-’.¿¡%Ó}¨ðÇ„Îøz¦}:{˜^;uIWŽU¦ßgU„žEºPzd­~Ýp¼MqMÄMèZ»r¬tö–>Ç/ÉI£ÂÁ—lo„³roo}âD/F,9èqEKnßÛ+ q:ÓsÃt6%R“Ü©B:U¿ä¿¶ó•p¶¥”WŸ=©añJ‡‡\FÛ¾—½Oá ]M~[óÙô|$ÊFÇn/ g¿|e<_šÆ^òÇËoÎRä—b'FG…³8n·îJߌÞy2ç5OC×DÜ„®ê­;%NOc ŒÆ‡çèËtí€òúp†$Tû^-Wz9°Á»bk´Dê„’~"ïµF¾|µøL§iìΊÆq®SÏѵžG´}ά¾Nl=à­3¥ÆOI[â ¥¤]´§.‹ë:£}Só4öw×õvž£§#3]ÅyýË—b4·G´Õ’ɯEÄMèî¼zÔ»[¹4¶ÎÒp³–þålNÎÉ?þÛ_g{=]Äù1Zš|¹{Á×âøAWeîÕñ/RÙÖüEc c*ÜÙÎöNã΋öTݸp@K+w®xÑÖF?èâ—{¾º˜ÊLþbôh@³üëŠp6HÖÆ#s‚•7˜Zª®bsß\?è*åå>l:Éäo—Ae:yïàqi1|v´o‹Ùœší´ÂGÒäל]Üûvƒ}SÙ±uC¦œ‹Í žÖlö”˜°ß>9U4?ú4é§%S\5éò¡+ß§Ò·TfòÌ ›V»fy‡ý~Oul런HKžÔqQöþÐm äí¾”ËÚ4 • «¸hlçóTQêïä³ ì÷óÄÒŽýèQwV4£™tÖÐ9^fÒš†¨;ó_EÞY|žúföô°*Œ™ò¿ yô«SC†8hz/&üÙ¡[<«äƒEßSØþÐî]bÏ“ËÝRÁ©ÃXïE‘£—…à<3ÞOjÅ{wqü £q^¥n¤°C½‹¯.ú|ž÷hzÍÿl3¦¡=®„›­Å÷kiIÑ«F¿4/žÐ9VZ»e|L TjÄÉ5=.ɼ®}…±5¥ø¢ Mãvscÿ%~Bç}nUÍ¡[SØã ¤ tÝñÜs—çaìÖè2K{ù;Ñ÷Où1CëjÉ&´g\•š¢n®a}«”"·&=óý´2ã=·º1¥óÕ0få[|e£‘Ôq̤ÕCÍ´dzo,êNèž•âÆÙ)¬_]ÏŠ*‹‹dÛP~:Œ­6>°±#õºª¸~âúk‹óŒí›s£a û¦ñþ~Ðö"=?å¼øR»0vx°ÃàÓ_§ÐÛþOî—D¼ö\~f‹ëº°ê=êlø’Ìzí}vþ[àEºwjæïx 3ùN¦æE!O—ÐÒÂ'dŽþâúƒnþ÷øÞ ®%³“/|vx}‘Ї/[O¶²s0‰ö•(¹à}Q™Þ“‰ã]Å1“ŸYG'3£a÷K”ìΟȅ² *—½÷u2W<‹ú‹ß½'tí+ØÎ¯±1™=<üz޳Ï%*÷­çáCÙQï·³s§’Ñë^œ²e‚õ2qýAwÉþSýTÇd†`Y«\Þ%1"pµìGûÒ¯ãHm ;zÁO—»Q¹Þ“ Û1Ä£o4%³¬º¯Ÿjv™ò{>îw*„åšó'…ÿxŽüÏ×{>tÒ€Z¯¦X&3Ó{ëËdŸ°qËõ!¢¾s  Ôå¼~—y¢–ù¯®£‰ÕùÒ¶xS«¶[.\&Ç\ëK/§…°åñM»ÙMp¤ÛUF”Ÿö>Š wS›ž; òt¦÷I,ƒ?þ©w…âÆš=±îÂrâ*Î`1Ž´àZh½íéQô¤ë°NŠdqü ;µ»ÓÀçš$¶èòÃ}³ç\¡e¼>•oòû¹D÷è£CÇÎúKbÝ-þ„*‰|j÷ýPê:-I›½ªbkm?{ÓBânòuªG‰÷öÂ_ºÅÆIìðáÕk7Ö¸J÷Út~â•ÌL×…=ðêõ²ðz$Í嶸£LºèÈoÔÁ¥Ý’صÍKq•ŒöˆþñÞè¬W‰†“"Éßíë„ñ{EýÝ͇…ƒ›WMbº°wš"v•^]/ îÌèGÖ‘Þç§Ò;Mj¹S‘”Í—ìõ t_{Ü ìz›±bÆB\Oýö|+5/'ˆ}l<ÀÉ6}2šUjøÐÈHòÉ|2n¿8~Òy‡'µ¿nÛÌØ«ÞçmìÝû”)A,Ýêæ¹r«&Snïú~µ5‘¯VÄOèŒë06`]õž^Sôd?ìPÊÉgì‘ëWÔa*ÍkpºóÓ“‘dzî*â't¦ç‹‰¬î­6èɸ,jS 3|註­6jEÒžõå—–ì*®?褕÷¼m¨Id“_Ú·«®ÓÓ\£qÛ?|’ ¨Î>‰$íÞ'ª»Åõúøez"{¹Ú3fÆu=mä1Tøûù³btVªþY¤ð×tEolW%‘Õ¼]ÙÃÎ,“܃ Ûâk²¹==ŒG}æøbýîüH:3o´ÿ'™¨_ ãnGº”3l¹·û¥cÍ3)luLe«ª¬[¥„I¯.E/Þz$‰ã†¿wævz ϰ|)wôÌ$çQy2K2Óß9RBÔû›³pÜLë.ÄýBû9z6¾é¦­’,‹]˜I l»s„ýò~2î@nñ呉ÓÖЕÏlèÞDŸÀF”{gåº7“Žä/˜ç¼÷[Ý‘;lN¾I‘ôÉŒ‹çeÐõ]ÁŸd'°×Å›–ª™”Iµ–¹µÔék[Ü¡s2µu_Y»v$Ëj¦ûg;è<š²ºÕ!],YÙða&™|¤3ãr²~¶d|ü]ç—ñþO\wÐf´ú¸ûN<«b\ؘEQµ8\í03ù'ããÄê‘4L“_¸V%òt•FFïpVųSüR>H²hC£í}+Ï `¦<7Ž^xÏÞØ¡nä_êÿ$è:ÿ‰g}ŒæYTRî^˜¿ö;ûuÞ×GÛÇQ0/ç«ER޽×äÞÛDÜ„.p1SÇRÎíi6+‹dÏÔ}HYóÏU˜CÅ ô| _0Aw»í8Ü2D\w äç'õ ;£Šc£^½5pŠYìÁdn0;‘^|uW[<Š SýcúœÖÐÝ]RÁï{ë8ö ÁÉ÷Êð,ZYíóÃZÕ÷³‰£'¶L Ëð•ƒÄßíß â&t%¦¨Û\¼~šiý8þø\©†ïè'Ý÷;Ïîy·iåù³4€§Ûq¿Cžý°/ËO³‚qü hÍTNs[è°— ›Ëß”MõmMzWЮÍ3Q·@·äCËÝ­O³g 7ú™EJKUtÒá=¬eh¯¦OÒ¦Ñqþ¸ìn½º: ßÙ@qü ÛÄ_/¥Å2ãã£ÚÙëáquÍ#fzÎ4ÞÏN=5)‚6ŸâÆÞ¢nîp;^°Ä2÷)g‡Wì”M‰—3÷̨ç/ž¯N¥WýîÎÿàˆï÷áZÒø²¦ó3º×³'—Žeõ£åeS¿[9ºíf&_ÐÉdzNÆÛïQ·t,—,ÎO±ýµ¿+eÎÙ”Ým慎íw±'ÊŸèdKºEaU¾ì 'EçŸÒº‹ë:sÛˆÅÃ§Øø,ß¹’Ù´aÕ)ßÊßw0/&Ó^GÅß©[4XNfuøqßkÎ dú“l9/£·fSÿÎM'nÜÎF>4sz y^ûV4tc8m+y¿œ$G?èf¶Ü˜ÒÖ÷$뼬³ÜM“Mщî:·ÝƸ ÜÛFc¨§¸uyøœWƽ«·õµˆ›ÐÍõÜó:¿ùI¦¼ê<£jB6•RÜ8¦f¸È¶MKŸÑ܉'~5Ö!â&t¦¼#žWgÓ„µÖû¨˜{¨Üù­ƒ-ñÿú&3œ6nÝòÁBQ·@w4­Ä…)1ì‹ýúBeA6oÔ¹ôfÖ.9¬r“×Sˆ¿}Ûõ,œ–«.±è!â't1¸[°ýrBÜ7dÓ¶®ÎÕJÚÈŒ6áIvd´‹½N-Çv­µú–IgÖé—á ¶oÒÉkµÊæÐÜ«NÛÅù2“Ϲ%”èévÿX8™ÖY‰ëºÈŸ üÛµ;Áúói²¬vÍ<—Ú~M_f¨ø¤fœéêoÊkîNw·ñ9qýA÷ô%?Ñ3Ü|U]ß*‡nWÖÿŽƒ«g\á@åœùŠÜpêÔzàÔ™¯ÅñƒÎtÞg¾çëÚ=î‘C¦ó\Ŧ†” _m'Ö•†_¥íâúƒ®*ªÉÑ-ޱ6?¼à–C“¬&ÕѦ­e;c†¿‰ÕŒ§y?·>Õ? 37ü}©{V.N:ʪ9îYåã‘C®GF´òdŽoö¥ )5ŽÚïii¹3'Œ¥ü_DÞë\ rYVqÔQöùH¯iŠ 9d|M}{ïùºËÜýc©`ìLûš7ÃþRYC×tg‹ÇÖ3¾ÆÝ•C}æ¯ø‘¾r›jùtÀËUãhÁÍ1óG¼>¨âyKç_>Ý:ÖvbûÍsÈ|Y‰æ]–¹3Óû– t}TnÉQŸÂ¨ÝœÀb ¥D½Ý×ç5P:èØÈ'¾£ûêrèÐØ€øóÕ•ìÞˆF)Ì|­;\©Õí·adºï×t·ù2–ÑlÞ"³´©grÈ£ÜéÑW×Ú1—Cç$W¦ˆu“aä»M­¸Udú~Ðuî_ãG…¹ZfòÏÌ!ßÊGfÖ8>–½|ÍRN'¾ BŠß³_V^U7è¾ð²KÅL>°9Ô¡­Ë΂é˜i½–¥­XZìcf/'µ¸ßëüëyR$3½ˆÈ¡c‹âzÞÒ˜Ýj9ÑÅžææOÂ(Ã^ºÚë’ˆ›] äÝåÒA~÷"Ø–K|álíönutEhÇßþê&?Õ0ñ|A¼g€îçU{„àö\3c­ækŽØß  £MöT{jl2†¥ÐK—»Y.ÞA—6}{çg ÃÙ`¯£^ÖeréÐú‚~{¿~­·5í7£S|™[ Q·@çó:-²V¿0ö ú|¹´ÔnòÞ“7§nöꞘ0´_6çp«0z¬Îé{þ¡¸î {Ы̮ÆN6›ÛµÖË¥âß¾Íór¦_×]‰2'–º¥ùKž €.”ŸÖ^¡¬4¿n–K^§e^…©³h{í¹­Ît™LÊò7ð2¿¬]Þªª¨[ ›5uÆúU!!ìÐùKëHsIÑ'dæíQó©M¿zÏt™DeCöNZ,ŒŒË*ˆ¸ ]û—û¾Fœ f /*sÉåMM¡‹ÏWcÓg%;…ÑÜ-Tµq³kÜ·Ç݇§ A¬Ç¸D×&}sÉò­cóZ‰K©ÆVûs¦‘}ðZßðéaþ¾Ú•9mÄõÝÈ‹•§<*Ä,®„n7$—î×Ä½ó ±¿ÄžÚšmÎ ^FŽþÕNœ¿/â&tÕ'÷á¯ÞØ£üEv.=¾X^ç7b%µµ>a•8̉æ§MZQÎ3Œ&ÖæoôÄýt³ÃÖ%X ;Â,`91—êÛ—Y¿©ËJå²nX‡ù.d|m=!Œœ}»™wŸ$êNè:t‹é8löaV4çÝÙÁö¹Ôÿô´9ýWЯ÷g§ïò ûË{›èŒ-·°²]´«¯»æRàëóVß,§%ÍÎìåJ•ãÖ÷›ûFC*ó7¸â~º¥G¥ýGL?ÄÆ í~!yV.]ŒšT°Òe™¨¿É¸l6GC•ý7 ûl!žwvýåC~€]U¬¨›—KNOfÏZ´ì·¯û“M’—5bÝ­ˆŸ²ùüQmùžßÇêM³Øºwq.;ÝÙáîr±Âž¸Kð¼ Õ¸rýzœxÞÝ7^Ž–ßË‚’gϾ¿4—†w \v¶ö*RåÙÝory:M©Ûòìç}±OGÔ-ЙÞû³6Mù@.OYa¥XçIKøv…]Sź Í_îóí 3>žTïbî',ðÌ¥·W¥×i»–LïÙ§ÐÔÔ ››¯ÕP{»óãZ¯u't)Ãøåâý ¾_êÁÌ£w×QÏã÷ýœ0•’¦_Úé«!Ó{qýA7lVomÅÛÙÌœ~¨ˆriå§•¯âf{Ñ·Ù#Õy¯¦‘iý‰†Êðå—DÝÝOóTϦ¶²= Å\¡‹_¤X~9Á›~ù‰‡N ¸Ó0]CkÏZRñ‹¸o€Îô^Ô9.ivÃïôªR{ü›íit³Î'ŠåihV¯~2—âºëV ?4¡çź,§\ö§:ø^á9/ÖŒ·SÑ4—<Éó®ŽôlÑÆ$Û{ZZÆ#+ÅYÔ›ÐßU<ëG³˜ÈWfå’…[¹1CUôë=´ðöò؇Zv6Ñ© YÄMè,õå—´Ý´ž-óë8—¦ä¿5]Iü÷4.x ¦ë·3Îo“C÷ù<í¹…XÎÕ}¾c•ä½uZÎ+ÔWþoÕøb'ÖçBÇW_ÞúBYs§—¹‚ûçóGmÎŽy¶™êGÍ<¨›’ÖÚvY ˜Fƒo4rëúE\½ äÆÿk¦¡ÛJ³¹kJæbGäÒþ=6ÿ®Ë.¨Êˆтj1Nõ5鬡 àË™Kãþ¥š¾^a6ñ]-¥Ïo¢ÒyƒBµ+\Èô^Y¬KÂß[…m†Ñ«I}{ndSï¨AÞɇ7ђçG4nãL~["¶Nu £5¾ÞÓ\ä=èZ ûjÚóðõCiÙd| ”µ‰Œ¯C^9þ®7§ôÞ¶ÍÊ[7è4~|£Qµ3n¬Î¦Ý²Åу7Sêʇׂn!ñå‚£Âhei¾BDÄMè…¿þv¤d”x¿•Mw•UöÝ(ÚLŒO(ß‘/œ £á®Í×>×tí=ü?^þEO":.Y“M?t›w7h ÅåmþÐø®Ì˜0jô%æCöjñœ :]ÉÑ×?jIÙ‡ïÈëtü(@3ËvÌD'ºW·Ÿ™;‰u½ ä¦ý¬Ñ¥Úkx7*›œ·Oè›UÜ·Ùyªñ~jhLDáT©J1<^7èÒÏ5GI«£¶VwØu˦~kÚ8_»…®VùÜ6¸­y?ØÒõs›p*³b•eÞb].tqm¿å¯x¦£ÛûîI3­³)gJµ%ö›iŽ—®ÎPÛ™ô}èè=•Â鯕Oc ‚Më€í 3´á¥=«ßß[:›ªJãºMÙHOË7|ò}õ,±O×káp„ºô§·äŸžIžfz&ýOê™ôwî-ÉÿQŠs‚ÿn|lð± H¨ì€ÿ¿Û¿ßí/O·¿«ßm!HøÚoŒ ôÀ¼_ƒŒ±Zð5±¨A†Hx2à4­øAŒhT­ùZ5|7 EBT¶ák¦ðÝ€’£¢-_»ƒïö¿©RÈH²ŠŽüýÆþxÞZ"!ÛUþÌc)´²+öˆ±’µBÆŸalP$Ýø3Œ ôÀ¼;6ðŸ÷¼õ± Hzóº c=0GA ¼‚ûãsûÿwmäiö§.úSý÷¨‹xœqÇœÿ.\kT )‚”òÏí߯çö—‡ÛßÕç¶H‘À”¾G c $3E ¾W cƒB iÉ÷î`l æ­øŒ tÀЊïeÀØ@ 2D”w iÃ×vã»!)ÚU[¾ÆcI;¾®c=0—òõè€AÊ×bl "™Ê€;Ðtàë¯06’« Puäë€þ}Ÿ[k$b[ ")Ë€;ÐtåïH16’´ PÉø»:Œ ¤HÚÊnüÝÆHàŠÿ‚Ï­ª®†±É_ @°@! ¼«È¯¾¼®ùSýÿ]ýyvô÷­‘þ§ÕG°Dð²*Š€ÁL @°@`SO Îø=0GГ ` h Ô CDp, m€ $" EÀT‚,<ÀÄ‚B A0µþ@ÌXåÀè€X#ÐÚ5ÈAWÜäKa I H”• ä hð± H°í€?Ðso9ð:`Öæ¶@ 2D`—w ùÀÞ¨@(R~eîåù’€¢.÷”Æü X#)Ø5È Bܦ>÷•ÅØH6@eÅýM16"(pŸMŒ ,¬1¶5÷{ÄØ HrßA|7 æ¸ÿ¾Ð°Fâ±j!’ ¸MîK…ù‘”l€ª)÷GÂü@Š$¥lÆ}z0?°@ÂR4ç~1˜€5˜-Pƒ ‘ÌdÀhZpÏŒäfT-¹‡ÆR$;e+ÞKc $>EkÞÓcƒB iÃ{‹ã»=0oË{\ã»°D‚´ªv¼·0ÆR$L¥”÷¸Å¸ÀÉSÑž÷\Ÿ H:ðÞŸèyGÞƒã0tä½ñ½€dˆ¤+î@Ó™÷ˆÃØHŠ.¼/Æ…@Ò•÷çÂØ@Ìe¼OÆ:`ñ¾E¨A†HÞ2à4Ýy|7$s êÁû‰à»)’»²'ïkï,èÀ耡ï-€±dˆ"@܉w—øG}dgö5LÿŒÑÿ»ÿ$˜º%˜yö.—vñjtçêQеòz>*ö ñ.ô¥_eýþß‹v7¨¶ÞÛ‡¾…½Éî`G-ýæ¬0`Ûäާ©#N-JýñTd´õ,¶ü÷ßóy0O'çÐ9ŽwŸ¿šEeK–ªW´L¾Ï3¨Yínå—ß #“Ÿ ðU„μwéV#bŽQrZƒNgQê…có*¼©ºyôð>J2¶sÿFÉ5ãWŽ}? kºxöÜSMŽÓ*M'—CYÄä1.úEÞ¢? 3 úy1FYÌÔÇÁŒ~õ¥>NKnÖ©²i]Mô´ÍÒÝÞ´zÃù¡ G lX0ªåý0Ó‹wl0鬡{_tføêÇÉäk‘E÷&Ýî|½Á*Y¬ÃÂd´ È£ò¯çW¹ç#üø +6cÆIÓOЮfÁ·†fѧ¡¹(g¿{Lqâ] •ÃD!SŸ ;è¾Wã%'h‹íƑŤYTûÁ÷©ý=U¢Ÿ´í÷k9Ù<œ’ÊDO½é.üÜ ûÌ?fãJÍë«^W5‹v×ç}U¤[ßüýG*œýåxƒádê»iúœÐE˜w­=Î;†šA3iñÒ­zZùÐdc£!gÒmó»SjH8…Ìpš ü¡Ûõ|vÇbhbòÜ~›®gR×¼GÁ‘…*Ê;8±j@ˆ U‰ ß}Ï-œFìNO{\ ü0¡[s%hxn¯“´¹Oóõ•â2iLت!ÝçªhLÓ wG_q¥3cŠ}ê¹:ü/¾fò9ï–_fûIúÁÛçîͤWËÜ|ÍB7Ð¥+š»œQR¨Ç”±UáÄ4õIøñAgê·v’ì<šž_°2“äiöi{â¼i×åzÛ&IÜ(Û;5+É/\ô ~˜ÐM±è¶²×)ª`4¶Ï¤í¶x&ö¢©>ò•¹Q½Sþ‡êî §%•õ+»~¦ÐÜÝ´7zÛ)Záhn¶¥W&…^.ܤ_O¿üÆ}]ØÓ% œFÕó´ù~ág tŸYêÛ'§ÈÔ<“bµþUîZ­§·‹×g¯u¥rv§+.Ï¿a~˜Ð}yÊŽTÙ©WÌ—gá¤\juû@Œ8~Ð¥×+Y?jÆiš°¢ßýã‹ô4Êg`Öù¨õTrRÜÚÙiŽÔØØÐ1‚úó,{;Τ“C·*¼\Å´c§©÷“9ׇŽÑÓ•i³,ö?]O¦~°Î”y~ôóï#¨ß£é[]}[ 3Å8r˜9]ÞN/| Ö“©ÿ¶+Iâ÷dúyGо‡ƒ¾ùv~¦Ðlsa–zp-y7vÃí zªr#òPà—uäõtyÅäyn„QRv&‚ºí‰gQÂtÜ ›ËÛUn£.­Žd^¥[é~›¤kÉØ‡Í¤&çÌ,5‚2.î bò÷M‚N2øú°Äq´òâþ¶}®RêÏ MŸõ$£á¸ÙTãü„â-ŽFdo]F´ð†n×’ÜÆSƒJU¬ès•& ;÷ÉzúJrÚ¶õ¸.t6õŠ_ñ²ÔíwßÜâÉÕ³ü1çÏWè–¿¡×=»¥”¶ÄáÚÆÃ³(H6¨~ý ÔÇÅ5³G á ùöÕŽÅ“ömÉ”G¯üîÃZ-nèƒHÕ ÿ~ƒËÁ)ÂGNø±C—dý4»å—x2ÚK¹]¡íÁËûÖmºž‡þŒüüÆ•õ˜Stga½ËhP¡ç'ÓïbÑͨGUî_ÓíAý+Tg®÷â°Y ˆ»(¯ÝèLeÌÞ, „gÅÞ©[˜æó„ÎäG@ãOV^z1÷2M[ÔöÚš3 ~Ÿg£â§—*šA_ç»Ä÷~ÞЩ»{{1j¤GDÎö½Lñ-m6&§.~ô&§1Ž`™ú”‹ãl?3ÎÐÔ®Á ãû^¦IÇ»ßõù±èwߤ‚ºåE­‹ å·†yy41éò¡Û•™ñ¤ÆÈ3ôsi©âË%²/^U3ñëbê°]SkÅ^Gº½~¹óÝðšß¥~±Û­…t¿_}ÎÎÐÀÕäí]¢wí¶¿ ÚµDøE8ѽÙÜ(5‚æ~,saöW“κGÓx`¾ŸÙÙŽJƒ8~Ðõ;86¥Z"I¯M-ßè½ü_ì½XSÛ·î±aÇ;vìXGìØQQPDCØÀŠ="Ò,`” Á`#X¡IAìØQ,رcÿÞ•5ñ¸÷wÎíçÞsîÝ>Ïï_ö^¯)sÍ1Æœkf¼æýZ4¯¶Š&í,¼xõ… ½Nþp¨ÖóD²äÛæõe~ÞÐÝÊîw%uJ­jÈw(Ë#ÏW+Ÿù¸âwŸºoOÝpk$RÚ—: ëLg~ÞÐ-ÍΞvjk¥mZQrww¯þ8d^ÐrZZ|m©k')­¯ú³ìýýDR5^°l?óó†®fö¬ÂãYi`{ábÉy”ž°æKD§e´ó¢Ó¯¡u¤ô¶žíš~×)äÝœšw; >õZèÆDv°ëû%4•¶k˜G[æõßäwÏ“*|l…¾©‰ÔZoƒ%芡˻þµÝ¡ùßøXkeæyÚß~³×Ʀž”῾²éX«ÿâé|æH•Û=Ag0ºD<½Þ¥ÒºÙ¨&•Çl8OÜQ>ïß{Е†¡^â›N4GÊwŠO¤j·ý;ŸÙ+èDÐåÈ^~|½‹#õƒô–ÃÎÓ½aˆ'MâÛ5Z8ÒÝùÃ^Ûª)póŒ'OZ:1tïƒ~åÞOåȱÌìÕþo¹”Q©äbìÍ¥Tá{,Ä'Ì£Ù¾ŸgžbãµÞ0#¾‹ßìÔ\’š˜¨]N£²Í764²§«cøÎä‰$øE°ñƒn–q»o•µÔAßh3—šõþ1wÃÛ•¿ûð5ó zÜAICµ{šôLdãíåiu]ji~غ+Î¥R7{‘¢þ)—ì)×À2{§­’ü½“ÖWJe~ìÐéí'DZÒ…Y¦?¨–K.P’àwÎâ&tý7NZp¥(ØstÒ¼Y­/³ÖЮVVŸÃz#aÞ†¸\%ñn8Wk°ñƒ®ß>>0lDk¦wêŽjX?~ÚíÕjªðžÔ÷î‹è4%óëtãô~áiÔ¨ê®%ošŸ#müìäškV³þì.´Ø­‡×ÉJâ»Drt"è„õY*]zµCcpŽõ \Í|YÉìÔ4'Õ!%õÞòjÔ=A'†ŽwÁTlK¥€SIX¬¡ðcã]šïAsÙ†34ð[•Ç'×gS”cjÈÞ>¬¿ï\°¢uã_«”äÜì¡MÓ6ÿ,P_ù­²Õð U‹½rºŽ{6M’÷ر÷™/MYw"Ê|œq|ÛdŒßà'CçeóºiiòMUwœ¦+–QN³²Io× ÝDöé?d5ªKˆT‡©ß{%M°ë°û%›ÐéÂÏ„Ö;M¾½&Åͦ]6O —{o&»¸Ý¡çoÛÓ¤Åf/0UÑ£¼q¦ðù$ÐmåÛÚî9E7‡­¶QôϦº9ñ;ú7Øò{5i¸ׂQ*æ—Èâ'tƒÆŒ[ú¥Þ)2ã_®c65|à¾~ÀÖwÛ™|¬¶µÚ Ýèiò©Æ³ø ݳ”#UžîÐÐf~¹Ö(›Î=:•Vo óKr!¯ukoÈ{©(ëù.³Ú6lü,*úækˆ;p3©°F6ímXÏ×êñfêuÿ«úžÂ™¤vQÅõT´¿U|Úûílü û5ÌëÕåÍ)Ô8±ºýâÏYäÖMܱ¼lóï~G5¿tËK•ÔHoÐÄæßøñÁgÏõùq’x7˜éϳèÓ*Û€ï.[˜/¨]÷üné–¥¤1©éM; ÷§:Á¿î$]h±aàÝ,ÒÛ 9ûÓ#Þ`WBæú@©¤~. >úLtbè¢}?– ßEY{î^È¢«›Žô»±•ô·K+;Ú{àV³Ø“Jr¼|âƒlþA7ÊêÁÅ2—d¯7ÔÈ¢'KÂjÅn'ï‹õÚû³%¡¼’ÆûÞ¾•ñžt|Wÿ{%ÇI<í¦:öhù~ì0S¼“úñmj›ÙÒ·Ò«!ß)é§ýÄ]±kØøA7¾áƒ©sŽÓj¾½rLÝ5=zÝÒ}Ý3è{?}ê\Ú1~c¯M5T4çÅçeFlü +MãîYWsïížEGNLwŒØÈükçÑé>ŠæõU©‹¡ ï]mÁÉÇhÀªÜÜíY4#ç⋦»Éüô„U7nJ(d,ïYáÇÇÆoB‰Ø"zÑ÷­çR£g¦ŒØEoÝn±›¶ð¶WN$’¨nõo«¢äE¼‹ŸÐ%¥ä[ù;JSºm÷ôÌ¢Ý0»»»Iðr¤¶C–8k¯¢³^´˜[Çâ'tóºx²"+™ºë ²hqIBø»i=öŽ9Ñ̆£ïÖ©¨JÁô£üØüƒ®Ól³·ã’ɨÞ>»Ï³²¨ÑãUÔ°zUkV©q€È™J?“¶RÑн¦>ÝÆÆ:> /ÌSÓ3Y‡—·ÇgѹåÕ× ¢=¼JCg²2çg ŠF&7î×#™tÖ^aÛ˧¨©]f×{º¡Y$Ù:­­´IÍïØæL­`'É·3o¤¢Üg/¾ì{ÌÆoBEŸñ#T©ÿ‰…ª^Y¤8êÓxpE£š¨çH|ûåJ6ÏØüƒîãûØvÏ%ÑýXú¶»]Õé]e°ùú òâ×è’]×1W•̘ßDÄk}ã[š•›m’E¹ʵ͟‘(ÌüØÉž–~]“ærJÉü“ØøA§o—m¨¢i=ljeјJ/_{î¡ ¿Ö:ŠQO1Æòàlü S?Ò7ئyfš'?3ÉjuèÚ; ‚i,ªªÞm$´ÂæAè®h%]pÌ׉V±úº0ÞVÀ&‘²ªÉ§þI­¦'_ÿ¨&1oó.ùí[×Ï»]“Þ/ÙººgöÒK7'ÐÏq±£âžf²¸BFzãC œª+¸]ô·utoëlºx==žœ[$]¾›IÏf&U²]B/(~\ŸâD‚ï¯ê/¯§…îë©C]†üPШ]&ÃR 3éѶ's;…ÐÀ»GG}Ït¡éÎ&«|¥*šyøeÇëØüƒ®ý‡¾OQP‚“âá¹L:³°ëˆþBHÃÛŽ…HéÊÍÜbÓÕ*zòXý-àË“Jħ–œ˜27 Ž9­l’šImÞ…&¶ÙL³&\üÐÀÑjؾü4[ÅòËÐU5OÝþ¹$–ôaîX&9¬òY³`òiõ«š•™ðŽÄ*zÝ¡Ý÷ír–ÿ ³4ÈÖ Š%½mI|&•µþvüqÊZ¤7Bt¥øb[G²ñƒ®‹>ÀÄз9aÍ̤¶.9߃öPõ\xÙÎ4çÔjwo›Ùä¼ÃêOèB^óÆÑäÀo'„d’ÏcÃfºc{H0ŸÐ6'„O J:‘’|“ÕŸÐu¸rèÊûÅÑ”[͵ìóöL:˜vävr»`êÕ¸ÅûÁ%¤w3¹¢¤¼Om—œúÊêO蟸(êu{Šm{ŒCia€6+˜&ð¶u-ç“ëåØç­o¢þœÆ?!aãêîÊC§¦D‘àçIï×eè¼C(íó@é‘áóˆïî­üŠü÷’+˜ÌêdƒÉ¨ë,ï÷‰Ë;Lŧ>{äzfRrzÆø}JÙ»z¯êÑv>EŽj–àÐUEÎ3’Êz6eã]¯i©ýÕ3Ó¸]÷opͤ^ªëÕOCIoc!¡µŸ¬¢æ}ZÇ4ˆîO1t÷y[Ôˇ(É)g¬h^&Ež5¬šsUøZÞít§ÛûY*z“·÷•Ñz¶n‡®eõ÷Öº¹‡¨|‘ÿÛ}Ö™4m峯ÕÔa¤Ò^SE:R˜r›Çži*J¨×áMËólþAg¬Orš?ŠwÊˤvo,ªŸ£k«­¾ß¯ïL£¿g†¶ÆûÓísåc#ÙøA÷ ¤ÃÅë嬎Ë$›µ}³W¥†‘ÈÞ!ó¨Î·aÉST´ÊÌá Á66~Ð û‹r2n×léÂa™´àÙÊýw‚Ã(¬Em„ú̇ù™* x¢;ßË—túpºô ýš3ïÔ½™´bcb·™6aÔš_v4v¥NÖÉå“íUä?îáãgsÙøMA=ßÁÐZ|(’\ÌÆv0ê‘IñϲzU…ѽц ém*Ô´Þ›Aê–™¤ì½3Ï®,”îµö“­«éL†s¼Û&Úª(åÕÚ«ó°ñƒ®+oËr€6ðmÞdRÍïžr5Âè{óñ>Õœ˜_ê‚èÍ Z°ñƒÎJÙÜïÔÝý´¨UË—“kgÒ„Ò/yæaTá“]©¸èØÓ>*ê»÷õŠélߺôÊá}<Äûéê»*Ö«+gÒçšß.åù†ÑÊ­ÏUÞ*2¾p©äó݈MNmòí{B§ßFŽÂ wí¨*Ï †Ó*O{r-Œ(ñBðõûô³ 9UtåV¿“7|ؾ't¼‹¨ú>染AŠ÷Ç^¬NÏùí‘&¨?¥ƒ‡4k©¢¤Óu¯; ûëSKÄíbn­ë»—Gñ+ š<¬åâK éOâ*M«æ.ô®àP홽1c°Ÿ/‚ÎuCÿ†O©ã³}¹ogÐXյ̡…aTm›M–:SJ©?; _壢6ü°4fÏ »zõè‰4‹pªbÛѦeAY w~q±_M3s4ºn¾€föÙqSEz[QGöÜ:¿q-Æ)³ÃÈݬN測 ÒÛ^[‡’߇FƒFßYHŸÖ›[ø¿SQµâƒÓ;ž¾tz»Å‘a4þpØ´î)ÑÿЉ¡]5¼ÜÜwŠá"J¤ 3I®Š<6z³´>?èÎ9¿é<JFßÚHT´Ç?de½Á >›4_½€²Eê¥ÒÝ*â]†»,gã]—êH‰¡ôìÔ¦¤Ñò º÷öNïnç÷PCûÌ+ÝœÝÈs˜Ñ(̇Õ+*Ý|!|¾bènz©³ !ýmµ'ƒê-Ûã°Ûc Ï]ØzGEíï8Y5;ÇÆÏ²D¬Ÿµ-C¨Òâ&½šlΠM׎­ì¾‡B'껳SSG òôûGû*ït"èê?˜_uçÞ`º_žúÕ+ƒzäˆGEïa~«?¥ÆÑÈ9*êÝYº'³³ C—>C²¦I0¥Në6éþ|/üãO£`¶¿cÿ{þ¹,,uÙ(Æ]݉‡ü†=¾‰ÍÏu¶ôäÓ¾k‚ïÛTÓODÕÜvyaGöܺ¡E)»î¡É‡ç•¹MÁû”õ0jò0˜lø3f=òŸŽ/ U”ªÜZ-í°ðœJÝ« y©qAÔM_˜gÐð³}m:‡ÐIgíì ¡ŽÄ¯ [aÜßuào`A§…."µï­„Ad´yY¾ý2ØsÞßû6‘ü6Ù#Ok`±ÕŽ=÷ƒ.pÒ–)Ïì&I°œ+lŸAmfù!ÙAÏb zhðuý¯'*êÆÿmöÂç3˜V"6Yðí—ùnÚ{¨œîŸ®B ,î8>åJÃ×öœ±§XEsE¼S.?ètÇì|u"VYô®™Aû¯Ïíu*˜ÎΞXâúÛç<#óø’×G„ûS<­Â8f>S°ù{:Å®ðçL«ÛŒ[æôØ•ö<ð^å9ò‘#ïdÈžA·yéa»MÁ»h×wþA{:Ÿy™ îþîŸçèJ)—¦7ûŽ<í±Â ‹=·…Žw“ÐâÅ“¦Úð`žp>À`z‰¸éûe£f¬ÜF·ÍŒ|rRÒéâÏOÝføÓ§.9Nž¿ìi[°Ë{óÚIt§K+å¯eÌoºUE£]zzl¥<·©¹ƒ’ÓÉzxÞûâ×Á´ämYd•<{:Þ¦ –NITùíé 鋺©·Öt÷§Þ4°Ÿs\:ë9þHS«j2q÷ºŽ4¥cqãI¬nfç& {ªà(l¡uå÷îËÓ©}½gs?†üöÇYsÃűcñ»Er™Otã–W;o3éÃGD:}<÷ëJææßϩƚžô‰…na»¾M5C™OtŠecº´µÙD ¼‡uš¾/–ö?Ú­GûºWê8\TèFMŒçÔ›ŸôW_aèRæ]ñõ™îGÃô†Té´÷=<Šû³o¿HÇCúá–"‰âûì83â8óKNo{ÒÔ—ÞE/Z¸‡ªê¦‹hvÃZëÆ¤$1Ÿ"æo3ëþk®ïCk6iTÝ9ˆL}Ìëu6[D½Ìøˆ‘Dûöò„qáú¼ˆ½ó2rêÕñÎ Ùnš;dZƒn èí‡ ³7›'QÚW¯Ò3¶ì| ®?YêʾQ©m»þ¯ðù÷‡o]o÷ í[KigGåÎä†Iì9 'èÜŒ6«Ûs=õyö&¦ ãÔgܘééa¿ãˆ~{¼~óýaç[ k=èy©f Ôoc¾üô:2goàïý§à…¼ÓQYîè¾×q5ó#‚Nð÷¢«œ›ÖNJ'ï¾#·çR;‹~ŸàHMÏ–v³è›Äö‘™ÿýé?÷Jâwçžá¾O»v?å\íÝTb^æ~ª¯#­«Û}œNŒ×Óû€3?7èb¦ˆ‘ —Qñú1©˜gãœW×QÙM±Þm®©ï;’Kß-ÑM&$Ñî®Û¬gßf~RV%bý+OªÜmK“›é¤6Ôk d7õþ&þÀ''â_-Ò.‰2®žø4œùIAw½ù„WcŽ/¢OùÙt œqñnÍi»©ÛÂê‘­ óÎ~IñC7¼ Ÿý·\)™”]zœN'k-=×t7yÏÓ,³v¥CÆ•¶XÆ$‘…>!2?)è„ýV'4²ú»—ïÒ©¿~ƒ<ôeÒ)^}ÑÄ;óì”ÿ›,%›gÐU¬ãm ×:dP¥?+‰ÌILd¿™ê7æ’ÈC¿`dç“ û1ái汪³It¾Ñ2ã 5³?ž²‹øÕAÈ7æ–D‚/+;Ÿݯ‹oí¸bIF¼Í©(ƒ6L¶oiךRkÞ‡ÆÑn´î혵g“˜o;W]•‘/äAÇWx¨öΠ RçFy=v’,EŒŠ_Jå_ø7Iäÿ)Ùçþf6ßf–ˆÿë†Ü<×Q?÷"WïßgC¡ÛæåJý>LõH>œD¯÷ogRƒù¹A—›óºrÇjc9Ç.AGªNÏ £ê· &åÛéþÝó "œ]Ho÷›DËzfÏt¾ÏÆ:½]Ô +®N ç.´yÇË7õ¯oÿ½ßÕmªèe£Ä$úÊ?&/c>¦Ð ½|6q—RÛ|bîÒ Š»D-7þØþ»Žü•“ȶÿćùñA÷SV£ºñp'î,²j_ß ò}œ¶]5kÇïzc©~%ÑÒ&ŸŽgÎg>˜Ð±sVœ ê¾óýŸL<µƒù»;Rû»»è.âþ4þ¥ŽÇ|h¡ë©_Ø.æŒv/Ê µ:ý4}¾ƒPìµÊ©çL>-ºØvx•D̯Q?赿¶jƒ'w:ê×ã9Ç3¨šh|´îÄšü>`Æò®ts.ÿ %‰xWû/CƒYÏë–qœ3b-ƒÚWÏk7¯t;=ݹ'ãlŽ•÷ëÜ«O¥#ôñÓÓÃmK<…ñƒN8¸’s³nû¸óµ ú°¹¼ûòmäxeBÛÀ‰ iÃàÖ}-›aþu‚N Ë—\ÃñúlúdÆ‹c‡,ö§×fzÏ©½˜*mÛÝqY÷#ôÖ÷}¼có†®åüµ.ãek¸ÔÁü“ý Ú¢=°«ífÚÒ¬ùÔªž‹éúÏ‚O¥uŽÐºq;köcãÝÿùÉ›·¯ã2¾7îc]=“FTùöÅàµ/ÕÞï3mü"º&~?hÕ™$ŠÊ÷\Üð5óƒ®×äúÅznàôÅfÒÇ›•n4îëÃ|ìÝéõëJ§{¹&‘}Ó1õÖÕfñº™ò&¾[Ü6rÂý˜I~‹ÚL¶líýû<†ÁƒQ[C‡&QÇ/Ýß³ø ~[išŒó^õîY2ÖÛG| æhOËȯ˭Ÿ!ν}r·Mˆ»Š»âgñÓºD|(âJ÷Â×2.ªšÔ6p&ÛZ¹ÛÙïófEun7qN¢¤÷KÂ~œf~˜Ðõ7kC³Þÿ´ t\& ûÈÞ4èr3“Õ³IÁ7Â|XÝÊSWùaB§?.ùÁ›{ÒvÆðª33éÒ©ÝRMŠ7Õùõ¬ºÑYGzë`ï -‰Öè´{ã]dûwvì|8GÙ膦™T”½ÖÔ/Þt’/çN9‘Vl,•“ÄÎ:t‰Á‘Ï:žóáüêòF©™xzdÖÒ2oªüµ}í*.´³²\›ÄÖ)Â}&‡NSmâŠJ½|¹Vú”Iw>¡_áÞÔæ´<ÍyŠ+-›o‹×ënÏŸd>´Ð ÎïÐûr)ŽÏ“^údÒæOKã^ËÈýÓRwW¬Æk~'‰Íc6ÿ kì98wV˜/—È?ÆÝ‘I¢Ð¥Žmjʨ…¼ßUÓn4³•g òmœ®íÃ'lülJÄûãweåùr£qWD†f’Ýæ_Ë«TÛ@“bz¬\àF—j俣^Ô®3oèÚñöž~ÜÝcI—–Ê3i~ZâÕÃÖ2¿W·ßõÔŸ>¯bè–ê' /w)iäù© ™”ÑzñC2_C“'5uø8NÊž“à{9øxµÇ*–ÿ ãOõv8žI)ž×6_ýéEŸg;ÇÿéBÎúʸ¿6Þp-øÊæ®5¥¯/Wµê²å©™Tܬ½îóZ/šP°uØñ]ÎTŽê¾åÚ¤¿ø[Ë¡‹è¹}k~N&ù”;_Û‘ìEïW4ûy¥Žɺ.¾ã€úM¸ÿ„qÖâz!ûr»»´ñI¸”I—ôœí±o5émR/9o1o°žDŸyfø ºbè„×õå—Ýl^„ñšw£WQµtè“-*@:Í…e@·UV£•‚Î`v‰¸ýÙa†•¾øpCZ¼»Xûa&mîø!èÄ:æCoO5ŽÕ4ë¾1‰bÜ,ûÕ“°x PÇûp™¦*g<ΤÕû:÷)¼á÷yêÙþu½Û£^¹3nñäÓ=X¼„nÎÕΧ2;ùp½šð+âLj{ÏoÇîç­êfÎŲÐ$ÚÄ{Áætì| —²º$ÚìM&-ˆ;ðX:z#ÍŽù0©Wo2©–^rqOÿ8<šÍ7èFè7†¼9¾êJû˜IOûÜšº€6Лe¼A¢”ÒR†ÝK¢ëÖ«&îtrè~ä È8åòUu¾dÒ ûÇÝÖ‘Þî•Üéelð ¥IôfwþÌ÷-ÙøA÷îÒÃöSd\¿¢««­¾gÒñ™ãÚ8­¦#†?S‚Z- *ÕîåÆaž:ܼÐ6~ó}†.ÍR»ÒÌa#\}‘é‚m+iÒÏÎ?¸ÿ¾¯v”O¹°¦7›gsJÄ~W":oàÎá ¬3iQÈ™®‰£—S¦œ@$¥2þ8qË$2 ¥ûMº³:º‡¤ÿ|±Ž“/©ú¼ru½ÑñÃè¶Ëhàcv‡ê¹Ð»àkˇ]R1_KV§@÷râ€Ö “×rM¿yÛqYFiѽ猛éH¼›ð¥#ª¿ø±Jpý¹r³µ7W®áL¯H—ö®žEϪ޴Î|¾œlVð˜ö´úùîê)'Tä”÷3‚ùtCÜ?XâÅ%87Å«f•.ÊéÕtÒ*š£78O=Ž/ÛWí–ê/qN]½ë¿][Å» È©Eê#+âë]Mu½Ú¿×Íÿ½~¯87§/èJ}øƒ+¹–ßÝëgQD¥Gƒ×ù¯#á©„nøKZΜDÞGJ¯/X#ÜÅÐeôâŸä­àbø¯¡aUtvíí¸ $ä!{º`¸¤ÞHä7~õØi› 3°-»´âA§Ë8g~‘EÉŸZþ¸ÚJFõ…’}ì±ìt÷NIÔíp§¯ë‚Ù|ƒN8¿±”ÛÛý5õi•EKDnH”Ñe«©OÚ¬w¤±…áã¯ÖJ¢Ugs—\Êeù ºŸû›vN»åÉ%¼W”sí³H¼ç˜Kó*Þ¿ãP³§KF¶­œD nø?'g~ÏÐY4ðë&÷àNŸvLžÜ5‹DÅs¾ïMÆy.uÇ÷p¢{9—ZÎþ¢¢ÐBþ%‹“ÐÅ^íXb’»˜Ãb:¿Ä,‹â.ÖÕ$!Oí¾Ti|QŠ#ñ¿ ¿­¢a]¾ñ7bõ tÂsºE·î¨ƒfp…íI JPÑ|>¥°u8tâI›†ú,à G-j1vDÕÜ»ÂxS±USÄ#SØÓ÷Y/²{©ˆw‘^+gõ t–ž-½NÜqã„ýº,šÝS["«ìGm>ÞèÖ]3Ÿôe—•ŠÖ6J÷` ›wsKÄÌ–{ÂÛ@ÛdÑçù#%âm›HØÏ™K5:žoK*RÄÖnY(Œƒ:a}ãÂòùœ³(øÈé¬qk¶P¹'ÇÌ&î@Bõ—qCw©º¤J»,g®‹þiýâ¾}x–±•ò6÷coMÛZ—›(ÍU”rìÈP‹©‚N°^pâbÇñ¿„É¢úú ¢4²T>ÑüÃ,Üá„eŸA*⣢}ª “AWÜíôÇ}­8ËA¶Û•E•ÏJò6°zφ¥º;gôÂ8ˆ{Ý9eÆæt9üñ‰IŽÜ×®_ŠìäYÔÖjŠh·m -Ÿç¦¸7‡zz>òÖµSÑç]›JÍY}]’«aN÷µ\tË ´ê,zc‘hE%w“zùð¡í){Xèðå#TlŸŠå;»ñí¾+_½šE»r‡iB;Q…zÓ±k¦™¯"3ýÆ27\_gëÚîëK³(µsöú}ヨӃ‘­Ów:‘«ó·À”¹*:ñ«Òõ;aÞˆq}ŸÁ¢šßV:pû“:Ÿîù=‹VÆ®ÞiDK—lÈíëD÷§9޼ÐQEw4Ùßï²x ;ÇËÍt=¡mÝ0›¬Ü®•‡úÑí/ü¶ ‹¥±”tr‚ÚüÀá>–AçÌ«oîÄùŸ}ü<§S6U›pdi¢ ÊH‹í·Ñž‚c®Û¹RIír®÷õsdû^Ð u·7jŲ·)òiܾžkìV졜®õó¨yÔ «þ#”´"Ø÷fU¶€îVâ®-igœ9~w\m•M¦ªšÇšÏ fçQç°ß[(iÂþ„µNËØ|ƒîýÎÒÈ;]¸†)F¸$›ÜïJª¬¡7Í?Ĥ59Ïzã1¤Ÿ’noz2xçq6ßæ•ˆ…8"å –„K}²ix·6æ­RBÉwÁëÒ§fÑŒ§ÓÞn§¤ ªŒL¯²õtbýƒ7®þ߉9²‰?~óYÛw¶¦ú³k.pTÒtÓZóç¿`ñºÔýʢܹÎwžàŽeS¤÷KKE彤?6Ògé·CW+é‰WµÃÙzºÒÇyãrîz#ålªÙ'Î#þÍ^J5{-àá\âÿ³å %•Þãì :tÍÝ]x¾ˆëø¼ÚÁ'²iÑ£Ó]ŸØGa³F=l=q>Mlõè£dº’tëÄò¾n¬®„nWÓËÚîXÂe^_ø¸ã/ŒŸ_à°sö“ºÿ  ]OÔ•Ê:(éy•Ü![úß§º¯FêÂ<8ýcâfçÈOjØîÉ«ý¿ýÕ‡uÙÓü–’Ü~ô¨TÇüÕ¡ûzq_›nž\zâã‚Ifçh¬öƹ€u~?ÇÖ´?Ðäli"- Íûô;¿ù%âFGß0d)WUW˜ì;áùó'Æ"hÚבºÕ¿æ“PW'ÒÀi—uócu t‚¿ü2îðñåNç¨ó¶ËŒ/FPýìGuÌÞÛ‘í‹í~'$²:‡­ ûxû†÷²±Ë9»EÙ?®=GÇB†Žº«Œ$¯®Å‘O§ÚRûÏËWä8$²ýB¶€®nšÙÞ®&+8ùVõƒg!çèéöÔ¦ó¤6 »ä?ñ´¡å#ø_è$Róï?N:±ù]g½`g«?8pŽÊ{NX»BNF‡ÊžŸI;žKýR'‘¶ñ‘ØøA÷æi£G®íWqžyã¦_ÎΦ¿a"½~>Ói¹˜Õ—еUžkpýœ§?^ùöu°óÞx˜í»ZQ¯%uo¸×K$ývê¶Ÿ") óz ÷={an‚a55áGõQTYÐÁš^nª|Ò·~"ñ§¶ƒÍØüƒ®2ÞMÃËk¸” ü“C7GÍjÐ/=ŠôÇÂÚÚRÜ“¥Xb$²}'?¡k¡©²â|ÔZŽÿÕШ¾9ôª?0Ñ4ëÊÇÑ76ͧc#ø_ &Ò“ª‹––G³zºþûæîð^±Ž‹Þ¡1è7!‡¶}´¾éïMYæêÚkìIÿ³Ñ‰ì«7¡Ó?^?²žë}Ô)à±]ÝY'¤¿<šJëG=mrÚ¾7»§Lÿ–@[ùm—;lü kœ%¾‘&ÛÀ9ÕÑxæPƒ™éÑ¿Ïi¿º–3êþÝš9ÙxE›бçï\Ý/®ÕßzçUÓÕ–£‰?Å?7Ξ?^}ž˜˜@ÏçN›`8‹ÅOè:ŒþâÓ¤ŒÓ<l5#(‡ª½j3sÞÐÖÙó)ÓõE±dIÛGîkû±P·Ê¸1ÆOTÍ!‘ñ¨ê“cchÁ©¾ k?Cž[eJ`ûöl=]€(Äa\‰Œsœ;Ct,‡JùãÓýbéôøÚ•»m™E•5³ÏÚ˜$Еƒ?,ê7cóºý»G¼n7É›«ØëOf¶­ï4åA,û=Æ :½ÅïÍ: ìüµðù$Е7C&÷æv¾!eâ}~_³üfdûítZÃCiœ@åqV[¦°zºj‹ïæÏhåÃYú4ýnÝз·‚íïÏ åâŸ^uH …ÃF™»ÜdãÝöØc{Ò§ùp…Fµgß}žCç&Þ¹XP7ž^ðÇl—Î$ý±Žþ $ÕÿðígBÞTÒ&â´§?nþ!‡_.üØ71žNö’7}=q"¼W/íðJœÃÿ …í§ØWì+ûrÂyº:|û™¿Ý—û<‡ßpבÃ÷ykä ô®æÍÕ-öÛQz<'¾‰ù….‹ŸÐé>§õåùr«Ž¶j¸lN¥Dz€¨RÅp>Iç88÷{O#ºÌ9Õ4œ­ó  ‰Ø`Ù´‹'œ3ÔÑÜK#Þu±M$ƒÁšJæÓؽuúäêâIÚþÆä.GØøA·«èŸsý¸q3·ÓÑ~;ñd"±}jŠöTÜߤЧ„Ú%‹U,~Bç5‚?±îÇÅÖ}·Æ¬‹Žz½\s¬i åïsèãκÔf_<í²ì½í![/@'ünÎë¹tªõªî:òrùu»]JúÂ/;oÍ¡îj¦'¬‰§ó_ó³–·eõ tו}ÓzõÜÄí«É?8בþX[c ÖßÐ6TU_àÇÓæDþ<›Ð=lU<¾VØ&®Ý¢’©Vt$v°hZEÂ9ŠYt|)ÿD.žý.uuø§‡Ò?=” þé¡ôOÉÿ}”*¼“¤ì¾à¿»ÿÛú)ýÓoò¿Üoò?Z?¥rÖû…gÉÿŽžÜž%ÿš¯Û¿æYò§¯Û¿—÷­×}•L˜-?Ђr`†D-rPŒ‘´-€ h@0E—€pP ‘ÐÅÀ ¨A)!ÁÛ€@ cÉÞx(&Hþ–ÿÞ·mþ/¯‘þ©þ©þ½ê#¾6úRýg­‡øØâÁÆ™ÿNø¿ßÈ€”S& …ÀAJ ¼€”‚– :ÀÌP€b`‚€f ü”38)ƒ"`Œ7dd@Ê€)‚Ÿ„ƒB`ˆ@(^@ JÑ ’ÿô–ü/÷–4E"€pP ‘ÄÀ ¨A)!IØ€@ c Ãx(&"¼>ðZPÌP¤@Š€1’‹ (¦H6 Yâ1û_àMò¿£÷¶ÿ¿áÛö¯ù’üéÛöïám«¥@„„kŽmP˜ ÅÀ Ùø-(fHÐR EÀÉÚÈ€”S$o …À‰\ ¼€”»Íÿ€¯-?­þo¯ƒþÙ+úÏU ýg¨þ{÷‡*j ö…þÿu¼ØxòŸ×[ åÀ I ä #HYЀ2`Š %á "€‰PƒR B@³@Ç‚›9ð P Lì,?Ђr`†à'rPŒ-€ h@0E`”€pP $ÍP€b`‚ i ü”3Q)ƒ"`Œ€jd@Ê€)¬„ƒB`ˆ`+^@ JÁ× ÄæÀ(@10A`¶þ@ ʵÈA0Fж2 eÀA\ÂA!0Ä ‰PƒR B€·@Ç‚½9ð P Lü-?Ђr`†d rPŒ‘,€ h@0E¢€pP ‘4ÄÀ ¨A)‰ðú èŸPÌP€b`‚c ü”3$)·ÿŸ÷ùßÑ[»Â{äßògû»÷H…7Û¿‡o­% (¦H¸ !’¯x5("$ct,1› ÅÀ‰Úø-(fHÜR EÀIÜÈ€” ýïï]Qñ·Ë?ÏÌþ©ƒdÿqê ÿl{Bÿ<3û__ñó]ÆÆÿlü¿·@Ç•9ð P L¸,?Ђr`†@&rPŒÔ,€ h@0E“€pP ðÄÀ ¨A)!Ú€@ cÁÐx(&Ž–ÀhA90C°”9(ÆœbàÔ ˆHm@ б j<€YKà´ ˜!èJc` PL% Cg1ðjP DÖ6 èXà6@Š ¹%ðZPÌØ¥@Š€1‚¼ (¦ú !€x5("$t,9˜ ÅÀÉÂø-(fHR EÀ‰ÄÈ€”S^„ƒB`ˆ$#^@ JIÇK@æÀ(@0ü_äARLqsK@8(†HlbàÔ ˆèl@ б¤g<€$AKà´ ˜ýáAòoy´ý݇äO¶/[à´ ˜!1Kc$i PL‘´% C$p1ðjP DHè6 èXr7@Š ’½%ðZPÌü¥ÿ>¶üWóO½ôO½$3ø§^ú§^úS/™³{NËÞ»)þ™„ƒBþý P‰PƒR Bà²@Ç‚˜9ð P LÔ,?Ђr`† 'rPŒð,€ h@0E”€pP  ÅÀ ¨A)!8Ú€@ cÒx(&œ@4  ˜"J@8(†ªbàÔ ˆdm@ б€k<€`Kà´ ˜! Kcg PL¬% Cn1ðjP Dä6 èXP7@Š ‚¼%ðZPÌô¥@Š€1€ (¦H !’ƒx5("$ t,q˜ ÅÀ‰Äø-(f"¼>ƒ"`Œ$cd@Ê€)’Ž„ƒB`ˆ$^@ Š1’x5("$(t,Y™ ÅÀÉËø-(fHfR EÀ‰ÍÈ€”S$: …ÀIO ¼€”’  :–ÍÈÿ ϶R B´@÷7ß¶/O[ :–”ÍP€b`‚$m ü”3)ƒ"`Œnd@Ê€)º„ƒB`ˆä.^@ JÉÞKüæÀãÀ×Vlð/g +ê¥j¥j¥j¥j¥ÿSµ’»§øïÝ ÿ_ ä ˆ-( PLQI@8(†`bàÔ ˆÐl@ бàf<€;Kà´ ˜!øIcB PL% CI1ðjP Dš–ÀhA90C•9(ƨ@4  ˜"ÀJ@8(†¶bàÔ ˆ|m@ б@l<€fKà´ ˜!PKcm PLÄ% Ct1ðjP Dð6 èX°7@Š ‚¿%ðZPÌ ¤@Š€1ƒ (¦H !’†x5("$t,¡˜ ÅÀ Æø-(fH8R EÀÉÇÈ€”$# PL‘œ$ C$*1ðjP DH\6 èX3@Š ’š%ðZPÌä¤@Š€1ž (¦H€ !’¡x(ÆHŽ@4  ˜"YJ@8(d‰Óxü;úÛJA8(†HÈbàÔ ˆ m@ бdm<€$oKà´ ˜!™Kc$v PL‘è% C$}ñÿ ¿-ŸãÿÙWú§V’üS+ýS+ýÇ©•lØ=SÈÞ›9ð PÌÿ}T–ÀhA90Cà’9(Æb@4  ˜"¨I@8(†pbàÔ ˆðl@ бàg<€CKà´ ˜!8JcJ PL8m@ б j<€UKà´ ˜!ÈJc\ PL€% Cc1ðjP DÎ6 èX 6@Š ·%ðZPÌÈ¥@Š€1‚º (¦ò !¾x5("$t,˜ ÅÀÉÁø-(fHR EÀ‰ÃÈ€”S$ …ÀP„×^@ JIÆK8æÀ(@10A²þ@ Ê€ Éø-(çÏo#AIc$+ PL‘¼$ C$21ðjP DHl6 èX’3@Š ’ž%ðZPÌ¥@Š€1¢ðjP L -?Ђr`†„)rP ‘<ÅÀ (@10A2µþ@ Ê’«ÈA0F¢µ2 eÀ‰WÂA!0D EÀIÙÈ€”S$i …À [ ¼€”¸ :–ÌÍP€b`‚än ü”3${)ƒ"`ŒÄod@Ê€)  …Àx?x¼>PÿQ/I þëgœt»þ¿õÏbÿñDì¦74ÞÄñn6Ó4‘¬Ï¸Ž*þ«Ó»‚êÌI¢ïño/OvЯ'Ù[¬´¢œÓšÕËfïZÖç—‚¾òí¤öøßM2(gÙïëù—áuÞì¸öä×&îLØ·G¦b9:u¸>¬Î:ÒiêìÉ7f}†ï$žùS±¾IÐKq,è»™;˜wµƒ×(é´©õuíº=½ÆêaáVT·ƒêOWzò PXß$è&}]Ûe¨t3W¸&bõ½>júJY³þ­ñ´­z/£S¬_®Ÿ½ÀôDÓK›9¾‹/7VGC“ÃÛ­R3¿ [’÷ü1qz<µR'æÎ˜Åú}@w?êçšMÕ¶pê ÞBGÓßng¥PÓð=?Þ÷“HHß~Ô*ž¼_I²†WbýZ ;y½iÍEƒ¶pµk¨ Ï×!²çYjÊoÇ–;ЉݺMžÏ|•Y¿$è†Õ{]pkÑnÃù ¦^R³ïDë‡~¸«êOæåN ÖçéD<^¶oÎÉë[8›'ªÓu¯©éüb—Qœ¨ÝÀÌ:å߬_Ÿp½׋Ž+0òç„>ä:º¡²ªžuKÍÞ¿éíÏ+þÒ÷D ]Ü‹­IµÈŸ[”< ­á 5ëwæ@KRckT‹QÐéÞÃì·Ö`}Épý±¹?óçîïÛ´eM­dºûúñðAF O]8dÑNÅ_úãÉpýàÚ3ïøs;ûUvžLŸÚq²É¶¥+~¯:;®Sü¥_¬ׇ|Ÿ‘0z+Çw%iƒÏQ¹úëÇ%“éÞ®Û½*mèi|ÝSÍ+hcC£ú†¬ïtBÜ­œ¹<¦[îƒ/k ?\ÉM&þ.mhgM[2/kw¹+hJ݃ËÛ¥³q®k†˜÷¶r‚oèQ:êØÞøÈ kšÝ'ç]Ò ýÔØ¸8—ˆ¶èÖûâ„mÜ’×VÎ~s’SÛÁUªÎ![±tغ½Š¿ôÉáúæ\Ù„ §Û¸Zu²ŽîÅ<èSíÜLmøQšÈ·m»6—„¾¢ :;|Ùõ¸–l\ ëlq}‘åv®RÌ¡±msÒ“=3¯U’³GðÙÁ9 z:pR?‹tÖïן¾ÙÆ4åÕvÎ'ÞåútÌÓ¦ƒ¾Nñyu”Òmîkµg}Î̇‚õ[NßÞyÖnf“Mv†éH{ªy¿©5Qø´î)Ç›;Жؗ|O+XŸÖ/:½ÍéûÜõCÚ䈎1Ÿ9jQ§¸G \AîúFæB?t-®Ï*|þ¨vöNîÂÑÎÞùƒtTªòð>=ðØo?·GÅe> ’ÞúëkæõšEf9p£§òG:²y0äzïÇ(©.oTjOößû½Î_¢ “vqGž.`}Ž\JÄé³j6ÙÒw'ôõÒÑ2ÏÓ+Ñ=«èŒ¢‘êU¸TÁübYŸ#èµLnr°r 736ÜWuŒõµ#Á×QÁülY\¿f§Ýá5 ¹›íS+™éèLÈy™Ííc´§Ù’n©lIßžz€‚ù<±þ8ÐYÛ­Aëœ}vüõŒî:Š{#½åqZíuÑû±álê½fñ¡nõÌ_€õ§‚î—a¦¬£õnαß±ç8=Áw챦•|8h¤ È“c«–³>Œ¸¾Öš%µ+iwsEö|dÕÑÔ±¯¦«3jkʉ?^AM;ͨÅúŠAg[£ÆÚ†£‚8ËŸ¯[õ藍z=êX;|9N½,§K*MšMƒ¾Ë“ìPÐÃjõãÝš²¾FÐ]©¯Í˜ĵ_P'æS;]m-.la~‚~œáoð¹Äw3k”Œù»å°ø Nи–ˆÛ´úÑb×Hë;3ÑùÕl9¯Kž„¤_ƒ*h¾&‹w¸þ¤Å­ÃŸìáöÕP뎴ÑÑÚõÓƒšoøuœG“†Äø~Œ#¾ ráEÿ¤%bƒ|Çâ0Nè㯣]ÃG¤.}‚ )={ÛÚZKµüYÅòvÃmXÿ)è¦VÞ¿¬lR8§Q/>;¤ÏIæ'bCõ͵OãhÅoç}?¯#ÆõµºPü§gáïVáROGïëÕÕÌë$™ô~1j65¹}8ùSéÛô…°ü­>Aïå¼6íRëôIŠH‹<2»§-ÝÃZ+huÒ›Ì#Y~Âõ £onQ¶—[Ýþ½®Mm)ìkž(;IJUÑü•SçÑÁ¶-½Äüú#²ñîkaµ=–íã¦zûŒðožB:ípqþ&!Å4‹>‰«Ìo’®_v;mÜû¸«ïw7Åë|p}~©_Êo_5}ZÞ£ ‹þ/?g f}¦ Ó—W«÷s“GöÝ;dtÊo…‘GhëÅùEæ'\¼Æâž[‰øâÍÊ#_<ØÏxwn"ÞWXIcÿH'úõÜó¾;®ÿÓ—G„ëÇ=:|Úé×Ko(¬£·ó³§´²L!£wÇv¿ˆu¢š _ú…)Xßuæ•Þxðw=_´×mV eíæ–=Љ†ìÂõ‡;îŠ{\#’kPžyq#î—¢­ׇ¤ÐĪ s%ôY÷$a5ÞïŽ9æ« [¸ÆÒrÛ¬HîÆÄUÍSSHðMŸOS^{,SP?ÞN¢\¿úÅ÷ÛÎ\$·Ï–ï ¨£ß, ^¥ETåηµvT`Âw"Tü¥µ{‰x¤8ÍϨîA®=·Á¿Níïðvr϶Ê©ÛÕÓŽBk<ë²qÇÈeRzsWV?@ׯÁ¡ú çdý9ç’GxÄÎÐPåq•ë X1VœºB—ï/jgûëYýÞþ7ð ·kÖ‰è(Ä«S«2j6h~û®¾:Y+Eóø¿ÌS t~¼÷ýƒÜÁVv½Ûë(!üͦÅû5ä\t"õ®’ñeÿõ~/†Îq#ïä-çNÄ®Îé2XGGiß·VÖ¯Önoås*¨O kØ’õÌfêØ9ׂ–ʹøCu´ÕG먕uû3—ûhÈÒ$Ø­ß))éÛ•^SP±[Ÿ’E1̺¹¥pʹÓRW0êÙ¤ÐâÕSÚhHßöq¾ ùÉ£föx¦ Á/„õM„nýJíŠìrnÆžâ:nV:*X515îÞ5(ø€#å'N¬öIA›.(Kë*¼žºƒU<-Þ+ç^¦–6õ³ ïЮ¡-ªtØ'¶'ÅŽ=(XËæt3§=+VÎ=ÐìØß\ª£6 £ çkÈTŸ`çSLßù£kúC'̇ º•÷{ýtLÎ ÷£ŽNÆ^•Ø®¡–¼ËØùT·—¿Åä3 Jäí^_²¾{Ðí÷l~Øh£ŽYuN>¡aõ˜„ÖϾêÖ¼³"«q}÷îš6eZ9gRXkUÏ:½<-àõu µHÛ´¢ä®=åW±ytëÐ?ë9ƒ…%âAó¼KÇïÕ‘¼ênùŒšßýÑC;Îë>2žŽ[“¹þ«ÿp½¾w,ê$nfiþ;ÍoN©¾á}<ë,|~1®ü«u´$~P¯ªo4¿ý™L:–öúàOÉ[/Ì-~Çâ ®×>×í.Àçà»bGku4ÿÅg7ï‡Z4da»¾®ô9wšÌ4<žj¬Û`Òv¨à{$ƒNßvßs W¾ÂBœ®¶*yÍ%ÍoÿÞš ÒûŠ'ßþ±íeÌOºl›Øùíîa=øiåÄ¥Z|þìØ‘"W:úd–ab<½ytÅѶ:ó¡ÃõOž´¸4ì…Ž 6ok2渆†é²ºP\å׫'ŸÊ¼‚ùâúúúrî…ÂÝGñMG™ÆÁÔúá;ÓUã™å}ÏÆ“ÉžŸóŒê ŸÇ`Q‰¸ÎÀâ÷™†¹$øj¨®³b[üG:;çõ…ª™ñD•µŸø„å)\/ø‚ɹS¾ãk.µÝµi§u¶†¶ 2ÄÔt ‡3ke¯óâéÄwíÌH1óƒÎ¡W#u+Ì£³Ï+½ì’Kó?xØí¸£¡_¼Îg{J|a9yüõx:smÙåoçØ<‚N|(rxeÌÛ»^«œ”r‰ï^­\CÅ ym{Z?gÌFé“xêSÕ%4›GÐ ýåÜ÷ GëŽË¥‘³ƒ6:Å|œíiÒ‚+Eój%üÅ/J]ï¹­ <­siý†e·’ºúoõÃ:>œ†Nè8ô‹¸~÷¨¬üx'9'ø¢æÒ²c[ïú›Ÿ¢É¼}´Åy͵ÚîŸÀê,ÿ óàm§­åœòA»ÏËs©–¾aé)ö~\‰ÿ[  ôÕèòŽ1W˜_àâq½Ž«­‡ažG÷˜½)—<Û>ß{ªÅ)R–”ºìïFŠÄ!(ɘ/0Î"èjÕz]c¨™œÓ/ƒrɹlD³˜'5G/Ü}|é혆p_‰¡Û0d«á¯vrÎôµ×žû‡siÖÀü·Ú“zÁ—1š…´Ì‚w¬I «¦ö^Âü:¡›«jˆŠGÎMX÷ºê·#¹ôîÙ¬åë44vÃãÝ’ÞFjZM*i:oü;æ× Ýà6‘u†rîâôévÚ\ ÉyÔzîD egO;µuŽ;µ¸ºaAç Ô,1ÿWbó{„®©ï·ûwržú…m.a1}À½‡æ·?Øóš|ãç2“¶­•Çü:¡»âr`³"ã 'į\êì´ãø—È_xã$'â»î7 N ¾isÍ7è&÷<§p:éÛ“ŸÏs)}RÇÉù½44Ãö¹èH GÚÍÛiïN Þ¬\ºŒùu.)5¸}·ÊòƒÜ¼[6ÍËséÚìêœÑöÍ(`W×ÚËHèËÍür¡{³´þý­³rޏš› Ï“Uœâà[Ä…1uëŒs ã³Ž*X”@]«Ô8¾¼?èÜxÛÞŽ9½ÍBãó”ýàÍÒ…:ßøÞãviޤ»{Ho÷ùQ¸Ï$Ðujm9à±ÑA.V“ðòL»óä¶´7–lægêLÎÍùo Zt?ýÅ‘ÅGèôv>×#9ÞûF¯ó4noftÒÍoÿv¡l=!¾b`ãßXµç3‘Üè˜ú}¿ =O5/DÎ3[Cñiï·7øàJî÷<·… ã ‡Nè{Á™êÈÏSÌ£íÊÕ5¿}L_TžçRdšHfü/-«Åƺ#¯^aä"¸Ë~ŽvÎòÅ7Ag°´D|û›Uãæþy•r¥÷È+Xw; ¹?u§+Ý0Y>²TžHN¢‹=ïeó ׇç¢ó˜¿H ógt"Êm5`÷ÞD*ð¨2œv°ù…ëõíá¿à‡ñóóÈrMÈíõj;k£Ÿ½Öî7Nÿ5ëh"5ì±zhb~ðÐ ~Ây4äQºçò²ZeæpÐ`›=Åv©|à^v"ÉÆwqaóÇõñ ø¾<êRuéÖöõ44\ß ßžÚ÷Q>Üy!‘Jv^À$\/Çõ£ºv“ÏVQ²úÎ['\o§Íƒ"‚sÿâ8Ôû©ÆjûÈ*!ÝO:ç'%Rtßô,‘<­´‹ªâ#Œtò;'ßLÛÁ©/ X¶ÿͽÑZyqwç¹6GJVÊæ~§î&Ò|þ1P#A'ƒ®¬·g‰ tµ016°0…2Çe[v¥q+cœ£ï%R†ß%åÂ6Âõr\_{eæÉ1{#¸€Ö\ʳ‹èÖá&ÁÇÒS˜Ï =X»Ñ÷ÓóDz×~Cvóž‚N ÝÔ ¤½ëŽ!þ NØ'z~êÌKðßz6…NmË^bÔÍ™<+ñOΙ?¢ +†®±uÈzëªùÔ||ç” Œ¶ëHFÝe]ªÔPÒžé…¯:î¾?ƒå%býËäGp6¼vë|šku….§ÐØçÃB®D:PÊÚSS*+ÿ:Ÿ ;ÓíŒÄºOóŸÆæ›ç³Ï‘Bz;ÝKöt¯•¾#aýÁætÉ&+T©SóÉaã¿ê5P7Œ¸Ò [”=‰æ¬¶¥©’^d[N9žÈæ®ÿÐOøÁ-Ÿêï¿Yn×NÃÆÃœ’j'Ô™¢¤O…Ãg•ÝfóiyEÞÈ'σÇáþ¾¹Gû¤åPGV*ÿG举Dqgt|Žì•Ÿ î†åÓ ÃûrÔ W†z‰o:ÑÁ’¨ìf™J⣙¸\Ði¡“ïxkÿéh÷9¯s·Í‰ùäVue`ŸaæsãB­Úô(0y¢$“ùÇsj=cñ:;½ÑnwÔïDï•ùÄ»‘G`þn˜æósä.) qGIñ“ÜyÃâÝŠñÈKCec6Dp”`Ïu¾žOq­ë¦”¢¾HÏ;¤»Qì ·…Gª©(ªJzIga|EÐý<>-¢®Sçùk÷³Âg'×”·>%)´½EJøÁîäV<®ÿËÏJjô’+˜¼ŸÍ+è΄»œ¾1>‚‹¶Êh¸æG>Å^9]ÇK!Þ-vÆ07T—_ *IŸ&û±y]~¸GpSƆØß©S@Éêз1±)ì9±+­Ò)©W¿nÑ1l^Aw#úvƒSÅ¸Û F¼ÙÙ¶€:ÏPÃñ` Õ¶YÚxû}'2»>ÐìÔ4%ó+dó ºi «Mž¨<ÀñOÇÊzÐ\½Á$ê ½Ï® ½™¿cC?%ó¿bó º-MGGÇÚà„ç«”ésô~n óÿ”ЕÉÆ6ƒ”l]-|/ÅÐ¥fø|«Tï7íîÜÐÖ º“®¨ƒöU»Õjzm ]:ŸªUZ*)uzhÛ€lž­,·|ñp@sí~ŽŸ»ç•þ³†šý^’á-¡•ŠØ/<ðùVœjaïÍžÓÿþoဠ{þoI|¶ [,|þ~ã5‡[ïç\ô h]Ó˜ÁWkȪ²Cƒžìi–ìÍ€%”Ô¡P2ãé46ÎÐí¸¢ò>Ãýœÿ>Б®³¤k§jh¯ñ²ñ×V:ÒS󔚟Ï+ÉjI…!A'Ρ÷ó9kîãÖ½YR8o}uéã§joó/õ‹0žJ¢é£7Ï<+èdÐÍÔ»xpÀ>É۹Ñwî•¥³4ÄϾ¾·iÆšØ0“<%-ñ3æÀ6ÎÐUë+Ùyl/wd´ükÝdû±Qð)Êð__Ùt¬ W\[/LI3{ZýW…ïo´]C|tô”ÐÜ¡gŸj—*©ÂÏ^?~й#ʤ ånÍ:VPžV@·;u:U縆t¶IͧyïèË™UJº_\kO¼0îrèšßo–p<„{Ñjñ–ÍÙt¢í ªþ@óÛßÄÄëõ°¹x½½Ýx‡JA§…NoÚ;„ã]Ô¯é hÃ$ãæ.uNýžok½Lsôúk~*†îð£È‘IÁœ5¿½~ŸoZpˆGŸS¿÷Y…s.¸Ïº¥hÑÍS¯±P?s÷M*Ë/OÍ ÞNÑË»mÞÌìæH]Úev½§SRÂ5këjî,ÎBW¥ómm+ÅÎu¿ò( Žf=™~ê÷ýr®?rEIúÇi[ØüƒŽú¬ë°‡ÎЪEï-N1¿@W滨¢^·§Ø¶gß§:Þ¥öÄ¡ NÈSö³cü¤Þ§hñ¢«ã‹¤4H¶á>·šÍ;\?ä{ÿÙQ­ƒ¸¹«Þ[m¹S@æ5'¬¬ŠFx¼¼¹Ì~FÚÄG¨Ç?¶ßÍÆ :Åôc5æxïæݲì]p¿€®-ìu¬Ù[ Õ(kbaù]J÷¯[tMI6Cx6nÐy¶¾êÏ@Ž/_‡nÒö¯VièÀäq·»Ò!‹ÆÙ‰J2;ä“Y«›wÐõ>S÷Ýš@Ž_ ´*) œ7 FŒ(ÐÐÁJü‚Õ™îoïu«d“’ôÇq¬Ø¼[]"ΘÚR̽ÙÅY.Ý2Ùé âÊÎÆ×WJë|ÞmëHç?{鄸êÕeE®ùX6ï [ÆÛÀ< àGVI^Ý- ç¼ø—ùÓ1Éq®§§’íG²qƒîJ£JSÞ<ÜÉéÃÒ³¬’=õ5:E½ùmŒæöô2=º¼Üï¯õ–º¾_/yœ¿³ƒûôñÆæÇ]Oý~þ»++ïÊ©ÃJòQ|3ˆÀÆ ×׉»?ððvîLÍ4·¯/ h¿¼õ/ûúÇåYJJ~ð,ähm6nÐ¥m.ÖìØ¿krkP~åWTüzZ‡ª–§˜“­Ó/ð•d=rÕøÙ¸AÞ÷v㯒­\÷>!7×N:Eé3t!k\èóe¿.¨/&èo4–q½à3éÏä)”0kkN؇w¥ŸV¨¯úK}a°¦D¸>zfg¦5|¹R›`݇ÁRæO†ûõöÄæQ+”ä0ÙyZÑ6>¸Þ‚/ÿü¹™÷<æà}­}Ur´EާmÇ?Ht!a?HIS"ÆeÿjÅÆºýæ§î2•1,7®÷K³N?R±Œcÿ2žZ\oö™W#—­ç.¬éÜã9b»í™¨R®ây‰°Ÿ£¤ãOûô¯yÅAè¼.Yºt_ÃÙx/0_¹Ï…«ØtΪ&ŸvZIÂs3Vg®-k“øƒƒ«¸ÎUŸ[àu_*GÎÝdx»ØÑ„ ™‡ï9éàÏcOr„õž:ÛN™o×U_Î|:µÅ5ÌÎÏ9çÐM ÇüBÉM%]và¶’¾ ö@JöÄÐ}Œ˜,ç<¸&~ï†`þu7×öJS»ßŸëÞž-›æ\Q’îâØ¤ýÓ:‡FUw-yãÎyZ-> ]¦¦Åècl9á<‰3Åv]ä¹ó¢’¿X¶Ýú¶ÎþÚåÈ=+3>7ñŧ–´èž'œÿseçø”¤?Ö¥dû бsdœ=f»aquÛñ8ó‡=·eêàÝ’7ªª7¤W‘üÛR—3#Ùº:a7–+ê2«Nâîþ§þ릻r‚¿áâøã-T4ö*#¶î†nrêâú&/ºQbèò`_Ä÷ùÛ§y¹ˆsop¶íN£…ôÚh½zGU9é[ÿ[U6~ëÏRÅŽ:Úõmªz½€¾VÙöÃhøRNð-[@?ÇY\~WIuîY?ù˜t‘—fuܽ^B Îý•„¼÷ C~›+·–sûöòÜ(hÍÔÇÇã”Åo›‡ :1t>YüÂÉ•ø§RäYû÷g¾f¬XÉY‡§Ý0néJ5jNK“#^êÉ;w²}Hè:lSl¾„„óHdܺãðì•\Å>äk¼!&êèVŽt2è_âe41ÉáíþŒâŸBÙÜ[Áñ§Æ}(„‹A¨s÷'>ÔNÐÉ¡öSW¿K[÷,êo³åzöXÎÕ~ÚÜE:ÔžZ/yg»ü¨’ªœÞñ¼0îZè®ñáÂd-Õ±:Ç]ÔДªMœo×]Ê•øqsW#{šìrVõá–’^9VãêôfûÈÐY ¶ùõÔl#}ßQ«ÆÈ£ô’?dêÁémÆïÛ“uhi÷c†*Z_ÿëÞ5lr=Ö£Íú}˜êáÍÖÁT0xø‰™Ñ‹8Á/Ò‘V[“´î£"iµÍÊÅÙsèÚé7 |éÖÏN14fÎòïö! 9áÜŽª>?|’Šrx{òO‚N PŸl¢Ž†ÖâC‘4Koºˆ«xþíùøeÄ¥Y*Þ¨ÍÁ@¶ PŸl¡F#øŒY@Õc2#/%/梔÷'Mr¥òEþo÷Y«¨ñnûÆKœØüƒÎÏ7ukÛA[©Êš›PÇèZzTöòà¾v|¨z¿Ä•º-ØÿÐsšŠù ³ùÝ0¬~o§wG;5)ßV@ †Ý¯f°Þ“Kk¼µ¦{¹ -“î~µŽT¤¼äÏÆºŸm óŸí }9ë‡8apå˜{Û¥ÜúSjÍûàLtž0²ê/û¬ÅÐÍßÈ;¯P÷Ý c}#ïmþ&ºëRNg¨ð¨ïDåÛ§ÆÖPQ¿=WÆžãl(ç¶åO&R ½Ñ]½}`1«ßON>õÑøËý©Õª 5^üR2ÿi6~Ðý8\õǦֻÉy _! Ïç%ekº{pùFöx++÷G]ù»òO2ØøA§?Z¶›¬¬\hjW@uªåhCÆ9p›Lܽ®£„ÂnYpb‹Š^¹Œ¥æS„çFrèP„íY<1˜Ö¿È\5«€N-æûûîÑæû“S^JèÄŽ«k»Æ©Ø:Wxª….ß:7çuåú°Å%rXE›1ƒÓ&»^tvw pw—Õ¢UT¨ñu¨Åž£B'©4©™¹<„iyÜ:ó#»ÝPKN¨û¨ª•¹Õ6ɺÝL®ï/<·5ØX"˜sj²Æ:”lí¦Ôô_@K,g¹¯™Æíõ>_þ¹™+ñ§|UT6ùŠk¹ì t‡Þ7?3úc(ŸÔ;^yTÕž¤9´LgÍ5´Ï¼ÒÍÙ–4ðÕž?¤¢š?&!²³çßÐõ‹ˆ¨F­ª½q† Èúê™+åÅNû2rh÷«îtüÑÛãßµ*ºœÀ'ö:á¹]8Õú¹uÏAtw†ïšÑ¡.ÜÝ/'zïNö ù:?IEºyü“y6~н™Åžpº¯hû¦ÅGTO.Ê[ÀmH0ª“uÔº”~ :'U±8)èäÐÑ£Ö4`/Í£ÉUô) ÂîN5ϵ\Âm,ÿò&}+a0™T´ÿ—u«A—…÷©…nÒÌ[›ÜKæ¼²ïU@Ó4™y^\E*ø/ª(vò—t|èÑn{.\@7ÛF)І-æjóÇç;êËÛ°&*:ZùòÏì¬DœùîémŸ˜}dò¢EøSä£enÖë"Ý9ýöÙ í3zkqv¨ŠN-á´³ñƒ®Ð1Øu^Ýý¸%íLHG¬§"¿NkÝÙ…«8ŸÖ¹Ï2ïÂ{·+=yÆÝ&Q-#þD­ð=ãúéü±JMaq]»I]ŸÐçl”±wx£ÏöÆý¥t­Ýá𳕒(걸މEl¼¼KÄóNÝ0£m$PÒotãê«Mh#îæÌÍz;öÉn’Ò¹¼“<æÍÖã;išp_‰ KËo¸GÒáú9—H9,2bRÓ]©æ·• êÿTÑNSÞ‰˜‡Äõüék¯Î©á•M6?êc½æÚøj%w.ÛêkM×ZnÝôÄ7ñQ#¦;]§G¾‘ù ÙsiäUýcwÎjÙ©tË-Îä™(ª~ú“Šf9óO„ØxAw†ÿO9µÙf7 DîÎ=ÜÍ? AÞè{v¶ïg½ý0aöfsvÎ×ûÕ™z$ØYΞÃБ>N›oÇ•Ý}²Ó‘‚æ]}mï¯ðŠlÄå8v>ºîW÷=i'§kü1=CÔ{Bå:¶û¾]ÞÛ‘ ëj’Âë'Q›Ã—Ze°sÅÐù–k~U>DŠÇ5’ÞU- ¿è'[{gÎ `ØÚž3Pw¯ª½ëG÷$Ú`•¹aª’“ôA]:Ùg`ô´CÔÆ'áRb%ÄOê5=ïÈõŸß±Í™ZN´Ö…ÿeLÕ«ÍÿbI8¯.‚®ÉáQÅòCÖzòG§Ÿù¤·‘íçȹT¥¯3ñî°Ç·%‘†šÖi¶‹ýN ºEçÜü»~?DnÛ¬ëô-Ÿ®,ý&žìïÄ%ÿpÚQï˜+M¸ø¡ã^|¾Ô×.`¿S‚ŽJýÜú0=è\W~ÿs> jXc¡”›g±e‹Ùwªq¬¦Y÷Iìy §0nÐ çÓì,ufø‡|š«T¤÷«½„ãw§îtYD¯_óF¹Iô†ÿyEKA'‡Îò‡¯Åæ–Qdñã£Q­×ÐÎ*_c¸œ«yÿ[‹ãÖKH¿Í¿<‰ùF :-t¹|:ÙE‚¯|>5¹–SÚqÁj.æn©ãp‘¹íÑÙ/é÷ï¿ôãÝ5Ñ…Wó^G‘QÔì›ÃåÓü£o%Ûïnàìï•Ljxr µ²½h0;‰†ZÆ =]Ÿ·óE,âÆ£éËíHQòÝ|2[÷,¨ÉonûŽºi‹èy»MGË>ªˆ?Öø;wñî}+{çGS)oƒ~#Ÿ~ ¿Úå€/§ÿÙ‡ÁŠzn=¸M¤Š¹{¾ì;ÏÝÖQ{ˆ¡„ÓãXçç³:ÛÓÛö’2_iÕ_~/%.e¤ÍÎŽÊÚû~ÇúÜsù¤›½¾ù›¸ g…ÏVt!£÷­]÷©UÔO~oǾ#ì÷fÐe«Ú¬RãX2È]^úál>5¯r(Ñqâ&ÎG7k¾3uY;qÐü—*ö‘tó>4Žn¶=–Ò4=–OúÛ~Æ&®bß)‰?nÞ:‰ýîÁC?è8ëOÒ‘éhm[«ø|’ìx5ç©ß&®¿lÞãBï ܽ‡%‘°¾tÅÐíuzÔý¥[Õ89Am~ Ÿ:[».è[u3Wq®¶ï€åÇÈ2‰¾WF7¾/è üJı×59—ãhñö>-œ‚òéWÞ i¥[›¹û’¥ƒ‡¸Ññ„Ž­K’è}`LQáó‰ k­_È+(.߼δMù¤™Ø}pÔç-œ÷J~GÔ\^´u®GåyãÐ>6ÿ ë¦?© M”¹¿¿W>ºÚwÙÀá[9ó)ü¸Shµ†­H¢Gö|=zœýÞ :s÷MÏÖÖ‰'§äÉ]›/ȧÊú7´«®7ˆv§~n3VìZ˜D}§ó'fÙyèFßî½Âze<æÖÜI™“O!‰n¦¿¢·s ë·îkéF;zTdjŸÄž?³<PwÄÓ‚Èöïì&äSxȦ›7ìà<Ö4}“óÊ•ý$é/~ÖZè6? ø¥G7N’–Os>L©vçý®NÓSq‰C\HŸ6ñ}zÔÔRÌ?¡[ª?x–@MϬŸµ·K>Îv´Lù±ƒYx •š «ëû¥›SU)˜~ô€Ÿð}l*ÇÙ®ôßX”@º½R³|Js75hß.¹/Ýš,lùÒ%‰ý.Œtê½;tN¤ÖŠ­ïB§ËÛgS¼ jTùT–=ñ¿¾œ†¸4¢ûþAN÷‹ºÁÕ¾ÜÔ­H¤ÓÛèûñm>ÔÆºßÓmœðœÐž²Bù€œDú´9FÐI {˜7ºþô”D:õã]KÏõ‘ïoå*öÏ{»ˆŠ óÅ7ýøA÷éÊÆëßËé¡×ð*Ïó/·¯JÕeü¹sÛšOÊ,³'ç’ô&gÖ'QÙw~Á³X?è*Γi_÷¦øôú‘M¾z‚ÿïuoð­íÒ!±È+|:»'è´Ðu¸rèÊûÅJúà.»lu–Å|IkXן/Í;ÕÈÚ™*M£Qf$±çÂû,†îcÚÛ®¡Jjõ$¯–z×ÚxhÜŽH[î>ª‘Ö[]iŠÆú‰êt uƒ 3Ø\"^û9gíµóJrè1¼šÖë\Ÿ?~ÐV.×|ìÑq 7Zy×vw¸<‰Õ-‹„ñƒ®øiaçwX÷-Wÿ¼hbî7ÓÕÝ´+tËênû´î혵g“è­ƒ½ÿ4áó‰¡kŸÖðؼÚXOiW~ÂövpýG\]o0}ií>½˜²*‰ú5Zu6w‰p¿H ûïr¹/ê +üß Ô–Ó56]¶“›Î/÷»SØÓW":'‘zvÏïÙï× xnŠWÍ)Xo4÷bØ ßË ºMÙpq6ý —q¥Þ!2nªPçWÞ1æ û½ t6VYÏ:تh×ϰ—*_ ÖÂwî@¯ù!µœiÜΚ½Ã¬¯»µ[ðFx=-tû=Ž\°]ºë¡Ò_iñYÚdj×í[’áÏj¥?Ø£¢Ã]†ü÷æQ1t]Ä.„û¨èå—}^Ê£O~²[vrçm¾”û½<»SEúeœ†ÅÏ-%â [‡ߥ¢ø»G*¯KÉ£Ðéwöìà*Î!(ØÚ ¼§¢7ùB[wtúm±*rIÏj3&"v½9°JU°«8¤Oß­PßåÛôÆ] Ý×nüIñ¿†iâ›G³s&ýhê¸ýwœh¶”ÿÅo çzºÁ³ÝSïKóHjµ)Qá²Û0è˜Ý¡z.4ÕaÓ$z4aX­‘«Ù¼Ãõ-ô?0UÑç¡o)¦äQzý×÷«ŸÛÎuáËû±®´gPèªkU’hš[Ö…á»XÞƒNÿø«wù2nŽËNÞ,«ÆÊﮬQQàIS^Õfã…ë·|Ó©Æãu„÷›G›M¬´ïäZŒSfÇHɧ9`VÅ~Åê•-ç©ÎÓècßJ4;ï÷ð§}ï¨Øþ:û›?ÖÅ×ö|w×ïZö©K»®]Îõ¾~Ž.´rG·žçU´ìÚÐqΰßâú·E“úÙçÉjêv£‘\Ì­¸’ÞUœY=‚û`‡÷Š–Ä~¿†ëåõPmÎR‘wÍ;§÷ǧÝ/?ýk'×Ùÿt=±»#Ÿøø•aœŠÞ¼¼àXòRêöO¥z,üÓcéŸKÿqz,ñ}ß:‚N s%¡wwЕÅ*)»gøï•obàÔ¬SEÏJ/æ…ò§Oï—«ÅëÃô÷¾•ö÷þ»Ê¿æWá™ëÔôbªè]YÑãÛä¿8Þ7·ø~Lï_ùßÛ©¢¥ óEùÓ3Ž÷ÏõÚ£'Óÿé–åx£þá¡kúoôeú³åŸý¾ÿôG±`>º P Lp,?Ђr`†$rPŒEÿó},ù¾L¦ÌC7èXB3@ŠÉßú2Uô±¬è÷­ùÃG®ÂGWÊ€)¢„ƒB`ˆä(^@ JÉÒKœæÀ(@10A"µd^ºÿ­>rú£ü×ú}ÿéò§ŸÜŸ~ºf@ ä #y[ü'ñ“«ðÕ-Æ$ôfâ«r¾çwƒj«³¶â몊ZН¡þ^?ýŸ¬øš‰¯•þ^#UÔGµQE]ôO-ôO-ôïQ u`5_÷ðqƒ?ÿñ¯cd@Jÿè=)cž&zÀñþ¸jP L„,ÿ¿ÒòÏ~Ý÷5ù×|à*¹¥ÀÏò_éC)á "ŠPƒR Bp´¬e…ÿ[ ‚e90CÀ”ùÿá^“fÌÿV‚à á=%+úoWx”¨¸KÁÛ äæÀ(@10ýÏ÷,f¯ÛpP ‘ÄÀ ¨A)!YX ý£dE¿mí>n~·ZPÌ\¤@Š€1 (¦H< !’x5(¢?ünÿ[}Üþô%ù¯õÛþÓ—äO·?}oÍP€b`Â|oÿ£û¸ñý ‹‡ ýµù:Ä€å!¾¶° 1…¾ÿŸ _›TÔ#|RQƒüYTÔÖ¯9øzƒ¯5ø:£»0|úº‚¯%þÙ£ùk¦¢&áë¾ák¾þÐüç©=*ꎊš£¢ÞøÏZkü×ö]øyîÅÆ‘ÿ{-?ÿwüá¦" åÀìžÖúüÝ#÷“õe¼¯,ë]Í{¡‰Pø-(f¨ ¤@Š€1‚“ (¦Vtl!eÆ|=þô?ãýb% èØBËx(ƨ,€ h@0E”€pP ÅÀ ¨A)¡^°@ÇlæÀ(@10Aµþ@ ʾh)³^ÕÞÒ?ÈØ˜òß ¯µ@ ÊøkÈl˜¿ÇŸ~h¼o¬?Ђ2`Š@'@Ç‚ž99(úW<>þ5O´ ïX´ ˜"hJ@ Ð1Ÿ _4Þ?V Ê€)‚ª :`ÍP€b`‚€k ü”3`)ƒB`ˆ`lμ>þôFã=d¥@ !‚µx5(&Þ–ÀhA90C0—9(Æì@4  ˜"ÐK@8(†úbàÔ ˆl@ б„`<€ýá÷áñ‡¬!†9ð P L@,€ h@)!¡ØüÍóÔyÉú-(fH8R EÀÉÇÈ€”S4@ Ê“ÈA0F’²2 eÀIKÂA!0üÃGVŠ š%ðZPLùÚ „Kvf@Ê7ÿï$ è@90C “€À¿ù©ñþ³@Ê„ƒB`ˆ '@Š1‚ Í¿á©VáA+á@Ǧ‚pPÈh…¯ïC«åü?CP•€pP `ÅÀ ¨A)!àÚ€@ cÁ×x(ÆÆbàñ7o5Þ‹Ö(@0F°¶2 ¥@„àmŽrsà ˜ °[ åÀ ^ ä #è[Ѐ2`Š$ á "!ˆPƒb`ŒaÁüÕ*üh‘0ÄÀ ¨A)!X eÀ EŽ%3æIt,Ù˜ ÅÀÉÇø-(f"h€ðIÉx(&HR–ÀhA90CÒ’9(ÆøÑªA)ñ{T èXr3R …ÀÉÎxùkž´rPŒ‘ -€ h@0Er”€pP ‘(ÅÀ ¨A)!qÚ€@ cIÔüOÚÿV5c$\ PLùý+ !’±9ðŠ¿y¬ýéIk‚dm ü”óg{˜'ítµ OZ-(¦$ø¦ùíßê-1ˆþå<â?õÖ?õÖ†zëŸZëÿÝZË‚Ýk:ö¹ÌðϤ@ Y3Rt, ™3ïÚpPÈœ9ðrPŒð,€PƒR`‚h ü”SD …ø×JCKsàä "xšæa[È‚©99(Æ®@4  ˜"ØJ@8(†¼bàÔ ˜ [/ ÅÀ„ùØz5(&Ô–ÀhA0Eà–€pP ÄÅÀ ¨A)!¨Û€@ cÞx(&ø–ÀhA90C9(ÆH@4 ˜ 9XÙ^¶&H@4  ˜"yØ€@ åÀ ÉD ÂA!0Db1g~¶á "шPƒR Bâ±@Ç’9…À I ¼€”” :–¬ÌP€b`¼l5  ˜"‘I@8(†HjæÀ(@0F’/ EÀø/[(&H‚–ÀhA90CR”9(ÆH@4  ˜"aJ@8(†Hžâ?¼lKÉÔK¬æÀ(@10A¢µþ@ ʯÈA0F/ ÅÀIÙòo^¶"$it,a›3/[(&Hà–ÀhA90CB—9(ÆHî@4  ˜"ÙK@8(†HübàÔ ˆPØ€@ cE9ð P LP$X åÀì/[+ ÌïúZáOûgÅ×RÑ?ûZÿÔYÿ¹ê¬öµþß®µlØýTÈÞ·9ð PÄ¿W11ðrP ÔÄÀÈA0D/ ÅÀAÏÈ€”‚  :PΟÝFP”9(†bà #`ŠP€b`Œ*^@Š€!ªx(&°–ÀhA90CÀ•9(ƾ@4 ˆŒ- ¨A)!8[ЀR B°¶@Ê‚·ÈA0F ·2 eÀ]ÂA!0D/ ¥@„ oŽ%sà ˜ !X e@„aü”†%ðZPΟÍB‘€pPÈ’‰9ðrPŒ‘\ÄÀÈA0F²±2 eÀÉGÂA!0Aä #)YЀ2`Š$%á "a‰PƒR B³þ@ ÊšÈA0Fr/ ÅÀÉÎÈ€$? jP DH†6 èXb4@Š ¥%ðZPÌ8¥@Š€1’¨ (¦Hª !¬x5("$\t,ùš ÅÀÉØÈ€”’³ ðZPL‘¬% C$n1ðjP DHä6 èXR7@Š ’¼%ðZPÌô¥@Š€1 PLQH@8(†(ÄÀ ¨A)¡X°@Ç s rP QH˜)…$ÔTü‰Á¿ýç±kþè/PÑ¿ø«²N;ÏÿþÿÙG?|Ïš»“«èëo®oLfÿ»¿¥O—â»Týîó/ôõ`ÿ^"ô}Àë,]¤0yºä<9e%<¹'ÝÁ½êVïòŒGr]q5LE›l‡±>+¸^V»ÕÞW¥´Ûg=§Ÿ'íæÐnK‡oçöœ“¶š9YBŠŸ#:Œ P‘Þ ë³ݾ}y¿ó:Âxí÷­\ÛáêN,¡Ã)^^½Uô¼}Ù–ƒ¿„ë‹qýû¶Šæ©ÍÏÓ¹F|Ç?Nðéàk§"ÿ"Ƀù¬¯ÊÖñá}AÊÌK*ê|x¾-ýÈ¥¸z 3³®má*¾—çÇûÞZ¢¢:AÖOM„~"èêê gUtò]ЯÜû¹$ôÃßÂñÝ£v&;ÒުɎkUd{bÁÈ9ެ/t½Ü?'ÜWÑ‹¥Ä“2r©rÙ‡7náRgñ ±©¢OˆÞžò*ë‹Ý´ò…­Z¾TQ›÷•¼ ¢séÖ¥×c¯9oùÝ?¸â{o4{鉦¬¯tKõ†T$ôË¥!Ó×?mÚÑŸsÖ%¬ý@J¥½Ï÷íµEE£Ë›ŽpìÄús@g0k\ÀòêIT’ùÒù¶[.%ÞrÙ·óVNwyKOÅ7 L½÷ãí<M?ÈúŠA7jiÏDQ™ð6“rÉhPÌÒÜÎÛ¸kÏ+Ÿ’z§×ÑýÍTäË·EjÂút@çl xx®GeI~íí–K^aÛ˧\ØÆõ4÷åæ_p¡í¶Î›¬$³SÓœT‡X¿Žm¸ßçùÌhc‘D3’{i_7—úô-Z­}°»±y›çÏNNÄ›¥ÿ¡¤:ú†³¬oǶ ÿø$ÚÒûÙ›˜2õ}cr}Ô6î[e¾A¬=]Îpn< Š’Þ<Ôï-óÏ…n_¨M³ë’¨ï”QV.êHß¶¿ÑV®ÂXè×§¤®úÆÑ¬/tz»¹Ð$æ[®£y¡ÚÏÁ[8¡á<Š9ËßJ Ñxfg³ñƒn‡qëE-““h ß¾m·Ž²dï“5ÞÌ 4y®Îè1Ÿ”-ŸÕÞ¡¤}óº_éÅÆ:½½mQëÿ¦c¾2~\¯Æ-Þ~(¡'ÏÔ(MS’à ËúŠA'ôL¢©6+9ÏБ¾Ü_îò·s7"g;°~ËJúYO=ëÕ>ÖW :¡Oéú¶|¨‹¼ŽWÙ8㘃/Wáçñ­ÚûÛ‡•4ã¢å½á}l/»/j~{Áˆ#¤· ©¯£~ú>ÜØQ碬2éõK>°(iùºãæŽáÌÿº‚¦k{¥Iß]©ÍÛ2 ˜<49܇«ðy_„!õ¯§$ÿMs®œ-aýW ë›qÛ×8ð•ülåЕJßãù´Ue®Ì²QiÃÕÔ¿†á“Ò·‰ômÙ¥À·¦ÌÿºªIÝ\üRŽÐó|ãŸJn”k]¹‹7§ ³LPÍž_u*}–È|cX_1è¶=©ÔýÝ‹#jhõK”C«ftìž½‘z!ùlâ<*ÚÐçÜ–›‰”sM—t|ëkÝ*‹ÞâÃ5Õ4RoC-ú7¯:o·E‘qðAþZ-¾Üíl"5–RbÈüÄ¡Kˆ°wŸØJM¯Gâ­åPLÁ«%Í_®âŸ6k:ý©Ú‡ŒˆDÒ·›¾ÇúA´g«mµÞjúØóÞ®Û½r¨–óИ{IK¹ïÍÇûxT³¢·ÝGŒoJ¤.ú†˜lüv”ˆ³JOmJ­¦CÍ?4©—C35ÛÜO†¹q-;ŒþâÓd:}Ø×ÚÍ#‘šè¿x?¡û<ìùk5M–ÙL}Ž:¼ó>n÷túÜ™Ÿì¦‘õÅñoZÌO¤|›üú,~B×eOó[ Õô…o³”ަ>4Yìܳ1‹×Өŵgâë3i}ÍÆ-ç³¾TÐ5Ø:2ïôF5,å²Î‘Ëய2Íf‘ð=[у[Ô˜H•úŸX¨êÅúúA7úÞÒŽ{Ôlž£öõžÍýèÌüĬ)æÌ‹…:J$[½ñ· “C·²oT›:qj*®{µË®Åç(ãé0¿¸±‹Iè[hK› ÍýºöOd~ ‚N Ýü.yÆ9§ÕôôŸpÏQ×úï¶Ïõ^ÊúRÏ£N?Rµî÷·ñƒN–"öy˜£&Á/û™Ñ}Qgƒ¬_¤==~½ 3(‘:¼iyÞ²1뫹ùoÌÉ÷?®ªI~&4°Þ9ºn×ûxv%úd;dZò^õîYrDú²ùß]¸ÙC5-?Ñø[Þëlò‘¿z8n¬%ìßνªé@AÇÔq­~&Й°oLÅlþA'ôïRS}C¾lºôAwmCÛ54«^ø–I_$äñv½g|- Íûô;ëËÝÙÀºõì¿«É|³AqѱlTÞªu«£ëhÔù© ëÏ£Çc:ß°H Ó©Ñ½çŒæ­lgEçdÒÛ/‡f“~Úµ–‘á»[6sh–q»o•hï[ÍbO²ùàïœL‡6/±¿ë•MÅSÖ¯í3LJ¾pí»Z ´¦À¾ãß¾§ÚŒÆœ>ÈúTAÌ­·:³‡«Im³IÙ`å~ïÛ~¸Säî*ŸIe†‹¬VÙæ.ùöt1|ûkãdzu:¥]ïÙÔùN·÷³ì¶0?kJõxšaÑ+"OŽ­ZRÌúú”ˆÛYâx¬}6§g_?l%³z{ßµUÌ¡º‘5'7˜˜@[Vó÷iÆÆ ×O,ÿJ¦Ao~•øÕ̦ꭻ,´³xw‰KUæ“níµó~ó˜$Ë{Ð Ú«™i\%™>]æ H²èÕèñÑ‹vPE¾´¼ß'.ϯ÷¬ã¹>?XÞƒÎôò>7Þ}°ðR½ïápµ÷NŠ5 ;×ë¬í¼kÛ¯! ¸`T¶ùFÖº£ý»¹â>y:àÁ¼½)YTjW]uéìNj¹þBªƒ‰JžÇÓÍ—#æ³qƒnÿ,#ﹸ/·L¹x&YžEUÂröí@™ë]¾í@¥wm,M‚ã©OãÒI·XÜ„n.¸ÞˆkjÝçQÑšÍY4åp#Ç©ohçØáG¼žÛÓ±5[n²§Ãu»Ýy·›õc„NðQS¥&_>ŽZ˜E ‹k™—í"ñ—3«®†KHoÛ:=ž,âý:øÊæÝ®qãÖýμV©‰ïJ7cfëŸH¯ÍôžS{5æÛNŽ'û>3ͺƳñƒn o«‡xôdùìI ‡gÑD;Þo7Å:ôýÔÑö·yë&î“ß°ñƒŽwÃÚ½ZMÝÏæY×ã7mñn÷€ æÿfC½ÊûuîÕ'ž.´ ¾)bý4¡+;Ì7–S“'oSiœE–NŸ{¾9³‡BNL~«QÌ¢GõïXÜlO9eEº°¸ Ýt}#t5Õ×òfÒ¢cµ/8ÿ &¾[s«æ³È3É~ó¹†ñ´<,kvýþ¬nNo×ÖTM³"CŽ?̤°ãáoF„ÒÌ>Vž%³èo7T#žø»¥¡«;¡ëó1¥jñÚo¼++/“6î´xä‚0â£tÓ:³éÍ u˶Uâ©Æˆq‰s. ºbè?Ý#äfUìÔB“I¿Þ˜ì' g}òçþ~½í ùˆÌæ]`‰øyÒKŸôþG(èb·È5Ñ™å0½Ï›Î{ɱóÙªqÃ%´«“dž"脾©It£Idض™4b¥©•wô^ÒOËÉT3vß쉕âé _fïeã]£g¦ŒØDÑé‹=XIöŸ¬û|´—*úßzp$·ùG}:ѳÿHbã¾ìoœD|À-n™T´ÝºÞ”¯{Idxȱ†3ùÉ£föx¦ ^9ÞŠÍ?è\õ6I*ÒÛ\ÌÌd¾#{©¢¯â€ª9Îw®*ˆ»aÜ2ÌSÐÉ¡|‚Tôö#ÿF2iSÐâ²®Æû觬FuãáN$øí(¨oW`Àæt݋ƽ™¯¤qˆn—{gÒT½Qç¾ß~æÑÓ¼÷,RÐÏšÍ?èÜš7ʯb£¤ò‡Û?yµÍ¤µ¡½§–ŸÜÇúåJ(íPÙÍÁæ ¢ýœãÚ°uÃî1Šøyo¯&Ò}¿Lr‰¬´Oe¿Ÿ”*¾áî<ÚÙÙǼ^g¹LJoîÊÆº;çØ—HÑ¿Ï9nIôšÐj¾ ¶r. ~è úf[¹lj<ëg Ý~¾C|ùxz< ŸA“lV¶ÜW'‚œ‚v#SÚQÍb—·ö Z®7DgñºÐN1òÛˆã}-õŽ}ôuDåw³"H[ÍÐ¥·&ÒU ú¸³ùÝØ½uúäêâ©AFÔÎö×3¨òÙj QäoB_Þžb‚¢J½w¥ocãßÝs¿y<•õíÙÒ07ƒÆLžzlÿòHâ½^º&;Ñé÷YËNÊô,ÂHóô Ë{Ð]Œzø+@¡ .zä ª²¸’µ4’îß=ß ÂÙ…FY·³üá« ¯®Î]lÆÆ:;ÿkmìMdXšvàfR-)op£w$UøyT¹°®y*(¬Ê°«'íÙø•ˆ_.ä ü8z2{|·´Ç&‚îGl°t¥Ê.i¾1î·Ú˜¦¼ZËÖ}ÐÍÎóúÙóXÊæm¯‚3hõøéþNFI»¸#O¸yý·¾>‹£ƒ+v†VÆêèf<ë|àîœXü½3È1åb×¾1ÌGÀ™rŠjöJûK¼–@Çwï N¡;/_“Aó¯Ü’A¯Gñ…¥#óˆ£ó2÷S}Ùüƒ®Gh»/D14j椘ƒÔïñ—Œæ­"©›¾·'}›ó 8æ3ÊÆº}/ÆUò&¡oem¿¾ß¡MR$]»0ÇÈâÛ|JyiüKGwºÏë^{7?è–é°Qd[ónGÏYtüU½7ÁniÑÀô“ÚÑÝZüG}ö(š®ÛÇÖíÐ¥MjÔvÅ(ʸ—x`R}ú°0y@[9]³­bÛÑf.­Éé28(/ŽŽº8ž1dùoO‰¸[ÔnDÎÃ$·œÚÆ›2¨QüµïArʬ=ý¥t.)¶7:Ÿq#ŽÖ9Dt½ß—õs‡®êŸµªV:L·‚fÖ_Ü'ƒ†Ýí5³ÍO9Ù¾²ïÕHmGsÆÍl8ún?YÙës®çÅÐMÕœ‡HS•¯œ3èU¯&¼ç¢*Ï®Lh+¡Éõ‹?ô¼Ç|áØüƒŽïš?ó”œÚÜÊ1ÚÐ"ƒf$…¬ˆ>xˆR?ÏéôÅž"kµOUàõv6ÿ 3l·½ßÛºrú1qú^‡º´tNlãÌCtaí·ŒOŽÔÑøþ8j3~|½Q‰lü K_öæAPI$ ÔigPSïä4ïÜCÄßuigœiYÑz¤Ú821*ª;ö%?謫þ²Í ˆ Æ¼“O:-«S´¦[Ê!ê¸gÆÌ«.¤žé“ýàB¥{ð‰—t½“¿hX ÷žü†t³¾r7"϶»ŠÇžw¡ùÊÙñ8ªüáÛÏüílü‚KĵôË÷Óå’Ï~ÜL'÷¯/e/¶¢ùârw¤8ÓÁ*¼ñQ5n[£ÆZ?¡[ãù0±Uö^òÍOŠÊM§®Òè7w¢¯Ç·ý,¼àHŸƒ4\+£/SxÇ1–ÿ ³j}×pû¶p²ï×íö–³éÔêƒm׎§±~êö4¨Óöo»&Å‘à‡Èæt=ón8—O #¡¯{:m®Òv¶¶êaÖ?z>ÍZa~zB þ\lþAwÿ«úž¢q(½åm©¦S“:Ž-¹¦ÍüvB#;:ú±öƒpç8š¿7åù`+6~ÐñÝ»›Ü¦q{¶÷V¤“ÒnñöWSÂk~!6—ôí~wÇQƒ˜Ôô¦ØøA·Ç*6ÜW¾‡"iJ&oH§z㬟½E½>Í[wiämvSãȯÇÊoÊ>lü [TéÐÇ2ç zäqÈ‹Eédpzõ‡Bÿ(:ÿ£îïáö”­ò1)½G(¶FØÂÆ/¤D,øÙï¦Ýz_œ0'ÜBçÛ·L‰¢ ÿ³Ò&§x?£ÅQ•9Xü„îí™g¡­î"ÏAüe:Íx6[šE¾íؘGo*w¬ÖDËâ'tݦœhârh' ~.éTiïÓË5Fý¾?{ðG¾÷7›YÍö] »—P(î¾xWˆÐv¿ãmçlÞEM{ÎåJü·Ôâ]™^‘.í]ù`@§ßþyéOýÆ»|¬‹qÈzàùiY¬¼Svæ™ ¥·¶aÉ•¸¿øÈ¡[·×täó•›©Ê “¡“¾i©¨Å‚þ·$QÔY¾Uýà™3•é÷R‹^w;T÷fýÜ¡ã]ú ð£Âù¥õ£žj©Æ»qJ‘}½:àâ½r‹]ñóÕÑù‡6¦¶Jdù:yæºÕ•>y“Ç*Þ¡AK;Â7ôä¼¢È9f|—ÒO9†7h‹£AU™²üZ">žønûF:™f!Í;¥¥°ÑÊãkQÌWОVÏnXkݘ8âWOϳñƒ®Kaçwž­!_7ÞXOKWÇ9?xE!Ý;ú°IBÕ½CFm_GFzÃB6~ÐE|à—¯¤\õ¶}viI;¸¯KÊh²OÿÊNB3-§}ìG x‡#¶n‡ÎCoüëI|u]e…–fõm{ Z`4Uø6Í“­_þü|Ü_ûC—»»@«Ës§NÊÉ çi©õÉ`ÕíhZú³S£vÅçâèŸÎƱùà#à@ ÞÿJh?NK/×mÍðjC>‚¶—§Õui¨  âÕë±ùk-CÕ–^3ÈôF#ÝËîZªò±å8óâw—ï r¢J}ï§Om© ¡¯=›Ð >£ H÷ZÃSô¶‡3¯ˆ¡ëæû »0¿Xu°ˆ)ºdÎÆ/¬D˜6þŠ\:…«ÓËïÀgŽüDÓŽ>©CŽ|$–þžGÂ>„pŸ‰ ÓÛ¤EH8ß…³ÓzfrT%"ÚªU~4¹l)ßÒc˜; þ—q”¶µ-–ì̺Æ9ÞÖynï¦0?„£[R-µ+£©â{6\j™1év•¥/Ð’ùAׄ뢴²ôä†9òNµ¾4¥é²¶ÑTÑÏ}t£=µþ:daÏUVp)]·¾ôíÎѦ³««ý(Ž¢K­{EgŽ’Òš%µ+i“ã(§ç´Ôþjæ§]O>¼ÍXÃ8µè~ÔÏ4²UiîzdAW]R¥]–3Þ:424ŽùʱùÝýBa'ä­42™©´/Œ¢G‘I¯m:’‘{y“ë;ãþ²¯[ àWáÍ=/Õ4¿’F;¶í™uã{ÿ¼ÌèÝ1{ªÖ»ðD/yñ»{:ƒpŒ»~=áÇ­XQç‹k½™¾ëæÏÁÑTáoP'Ñ«ý{]e:\=ñi8?è¶ßŽ;f3÷«šA³Ái´&\).ñЦf69oGÅØ“×ÃíO}£m­ËM”ælü 3ݰãÔ‚š[9ê?ïôøÚiä½tñâ¸hÊ.9ÏêÊ ÿü,u3N½þ&ŠZ†ìWÄœr¦Üªü޹‚¥•Çtw`~kÐýÒ?Þ ãBŸWžçRt–ü¿L>;ïcÅ=å7¤œ(r5o(­`ûÊÂç“@×»:ï´»—›¡àq–ôË‹†ÑTá;9¯! ºÂݾY³ùÓù-r÷û˜¿íYÚ½ãI› Ѥ·yŸfOmÎÔ nd£ /.û[\iÈòt\-Ûc!a¸è¬¤W¯BÏ’âÅ#× ˆ×ÓGµÕñ—ü^ÿÿšŸµ¼-?èú¿üœ5xf$ç3`кË[ÏRAýÈ}îFÓâ>¼ó‰„ú}êØ¢ÞI=>¹~××lþA§·)m"çì™F)ëÎÒša;ÇP'‹#ŸKèíY«…^(èL™ïûxG6ÿö•ˆ¥ú0‡¸§ÇH”KÎÒÈ3‘£–;ưõ‚™^7¬}<é—gÙüƒ.ëˆ V€‡¸n7gÙ4ÞCÕ´^Õëüp¤3£?^×Ί§‡Ï üϬaón_Å÷x˜[Ò¬SM/ÿºzùŽ‘}2:»gê®Eñl}Ëâ%®8kDí寢¸êüô^y–Æ÷Þo»Î9†4!óöï ‘Ò¼FÃÊzÛijuª0Î2蚘yÛ®zÍå?;^„ïÁv÷°§ [ãóD\ ‹‰p§mKݾgt‹')okÛ@¸¯äÐuLH©˽^ejôß{Ý‹óÎDÓæJ¼Séz, ÷«O^cʸ—Ìßp_Åóº8Nð¯?KÂsøhjç›Æ i²»´ŠºR< û‚ŸY1tÆ«>ví3XÁÕÌ”u´N>K¯‚µÜYECšoùjWäNß—t2 r<é¿þú‚ŸÁ~ÄYU½;çÄsCL’§dŸ¥\—-¯Œ¢§ýj-¥=N¥Õ«O“õĘßt×uI|¶&ÛõmÖ‹¬Çx½ýßsÖG‘Ä—òL]ˆZ3àõZm_4ý›oÐÝŠ?uG¢HäJî}ß¼µz*E¶:ý|¢ uÕÏKÝš;‘þ±këxŠ™ø+È*–ù®AræâÛK•ÜÏ.3¿ì‘Jæ;£L7ï‹"¡>p¤-Úã¥ãIxþ!èdЩ{-Ø©Uçȇߙ©ô¢%o¬EÏ#ã‚oL(í/Œ®`kàgë#̇,•.˜˜m¹E£yÛ*•ÓïýßýËÊ&IƒØ¸Aç{kÉ«ÑãÕœ°ÎJ¥¢FícæEQ…oêÒ¬9ëŒdñtÅïUgÇuÌ×:Ï„fµ^©¹ýÖ+fÕz™JU+GŒßŸE9ü² ¡”Ê¿›ô:OÎßSæþ`qòêþqq`2×ǺúµêÓ¨ëi2äEóy{Ç9n4Çtï[ñTª]êWû7èþ?öî;¼ÉºíxY–„!DdDfB˜gPF˜†¡Df(-M™ƒD–A‚2²WSÀÐ ;PÚ+¥BYZK‚eˆÈˆ }¿Wò+wìëûÞÏ=žç¸ã©Çñùç¾9­ôÊu~×ÈïÌpçE÷Ûù#®X<6ƒ:¾ñÓïÛ)4ÿu¿”KY¹+Û{îl¦Ý=&¹–^Š|N”¨sÕmѥ•í|ÃŒ ý×¥dЕ9•¸Þ—­tà·Üm½n%ÒîïêÔÜñóf²¯(ƒ*›+ŠºñCÞ.9yáþä¼ ë_Ï aÕ¾j••„©Z]$’mÉí)Rü¼$çÁ9›"sL¨ójϦ;ySå¦Ó;6å©N™åóc­TzÕ×íNúè¹ä“s•lfóS#u겋<µåÜN^ɵJ8•§±Û‡w·RÁü¦ç¸æé/6ÓògŸ-™©s£nÇé U§ÌØÅ»WüìäIµ¢úû¿vµ²ç q´©l÷úS‹§Rx<¶<ò÷ó£.¡ìús¾ú»ùnB›*ƒõéýkgFµÒg$ìr×M‘û"©4mÜêò‡‡˜õ¸NŸ!ÜÞÍ·,±â—û­Ü´ÑøQ÷¥ó¬4¿ôñf4Ž¥·ªÇÕ+“J‘y¡ìø¡nëê?/˜°‡³’ðæ…›,Ó¤É5Ó¬ÔTt}fÍã£è¹0Ž¥~*…ÇÈŒaóQ·>mxó¾•\|ÂýÅ»-tÓ¤^Ó·Ì»g¥²®{6Œ¥'7­uòû§Ò…šÂ“6Wu‘ùQ.þ²V7 ÏU\¯Qão×A‘yV©Tƒk+«©3¢.7O8ñ÷òNgyo_s“ªsü“­ShɈ ê7ÆÆ‘*ôã.=•êÞ{ã5› ‹ºm? ƒH÷òoU&ðdÒêó7[ä´J¡‚9oOGY{èq*íûrÞý"ŸO7êÂ¥ïãëÜ:Óèé{¸N¿v»ÒÎ*)TUÔ!;µ‹ŽîwXpxV5;Õ=¸bǧÝÙñCÝ®EZµÒø•Cî·½ðq&µ,§úðüiëËuûÀ¡·¥[KÛ©Úuh|-6×÷»|eäyg_ëÛì=6eÒŒÏoî7ÕJí*ôŸOYã¨ÎëÒ[]H¥³_ÿTsê6×u?µªÔuÚÏOSìëõé¹LúmhŸ™c_±’0•pÒˆñôþÍEÞu¦RrÏØž{Ÿ±¹¾¨Ã"%Atx??¥B«Ö߉Ðè¾î²½W%¿üyýž©¤÷Ž˜Ùñ!›ËŒºokt;ûF¯tþi·2OËu:@ókÆTû®c2uj˜>é󻉔>°Ø“޳S©åê_›Ëæ2£.ßv©kÓãé¼qÖ~úÇ(¯É‚QÓJ%SÁ|×ëÞº›•J¬Óø²ÁÈÏãP'ÜUªò^~ç0[òù#Ž æïÜ^Þ£ík*ÍƇ_cçê½ò¶¡ìÞ >À—Þ:üÂjö|~Ö§¥“©à=•ŒJ—wüp*•æ|ýá® )‘:?ê¾ïlÞÿ‰ŒçŸÅ+gÑÒÝþg§$¿Ì…{΅džùS K¸fsµ“ò•¿ ·a–ñ¼/nÉg¦îYtú“rÃ*“é»wÈÝ!#í+£»Z½©$Lá4ŽÔIQ÷ÔÕ+Wž¿#ܩȢãW2£k2Í7Ôñ݉xJ®ºåR¹c©š­D݃ãmÚnjèæ­.سhÖÚ#›6M¦ŠgÿhwlU%--Ñæã<ª´xnïÑ:-êü&Ð7/L¹Ût7‹šOP4©ðœ£ãöööRu¤üÑþaƒç©šWmDÝ­]ç5 §»ùë]>[T³îAÊ)÷´fâ>ŽÝL¤×;öyvã÷Ô?ͧçP¾œçÜüÃË[šœúè Õ)×äâOS9¶®J¤“ò]_}J5OÛÜGÚDæÌ»Qç™éæ#×Q©Oö¡ý_+8öSGáqjTª¥Hèš8)RçGÝÄ,™¨ö7¿fúä¾_;Hm.6h°·G]û¿Ï~žu«Ï;·J&_eÎtȦ9 RÔi%9 ·Ëó£)¦DcùZvÊ_"ÀHuƒ®‰?þ¤Q&_§öÂUß’Mkχ†×•q”Ñ{sïC-bé—ž +÷yÇNÂSœÊ½Ø\{Ô5NÌn[µs&ÿÕ"Åú8{6ÝÛõÌÙ.–£ª ‰¦¥ÚáHv*#ܨÎÎ?Ôå ±ÌŸÖ/“¼‘Mšl÷•\Gïevø #¡Þ»v:ZqØõ2óØù‡º' O– Íä[„_H9D_Ÿ›\kõ îåóϪwÿ¬¿ýOŸ7ê$ŸÎÕmÑeòÊrí‘Ñ‚†ïï|¥W2¼?ÚôäÜ´Óì4ãb|ÔÙ\{Ô >õ&]&¿¹™?~Á’C´êî7Ú,H¦o­›R"–F‡ƒ×Ní5Ü”ŒÔÅ$#7-”žžÉ»rª C¢„ïóº7Þ’L[Üæ¼-ߦð¸ÍëvúÈßxߣ5‘:)ê‚ÏÚ6ï÷y&xLÔûÏÑgÙ—¶v%Ó¥r\:ÇS‡M÷ršIú\+Q'<]1;“?[|FŸ ­SÓ†MW7&SÁ<4ïªÖÚA? c©«FŽ»u›L›wfN&O“ 5þ0ÍðôšÓÀLO´õ숎}»5ZÜÔAþÈéõí€Huµ–¾n[ŒŸ×/|Cþ0$UÔ6™‚-³ºÄ'Òò±I_.ï !sËöˆÔq¨þý²•9SZê‹›ŸýÌQËš'”é‰ÔÜݹýTü>"ÇŸwøóÛžX3ùðë·bÕY);[Ÿ“‚ëšÃó,½SwÛiûS¶%ndçêÞ^>ëÖ©™|ß>ÕcõðPó“­Ãçs¹ß2†:ø÷Åv7ØÉùCÙÜØ ;ï¬øœÏy\gR&?,ЫdêlÍêÞJ{á Ž6÷]wé¾:ŽÚdwÏnvŠgîú~º¿Ïü~·´/"uFÔyï™c]3ùÈuÀú•M½]?™œ‹+\þ¹býøMM§ýˆ,-õÊÞƒÙqCÝVm_âÚfòÁƒÈ#¤}ѵlYY2/4iR¹1´Ä",dð÷;õÓâQýØñC]ËâjvA_‰ÜG;BÛ”©Õ[VH¦‚ùŠ÷?ÜÛéƒjó‡WíÄŽêÄS„É®™¼ßú-V²G(щÔ)Qç:Á|ÇÍh¼ßfÒºÊß¾¸ë#ŽZWjÓ1}À®n,ýìÔ5üG¤N‹ºùÓ—¶êësó῎7‡êÕ<(±Bþ<رìΦ8 •h§;_ÍÛ½®w¤Îˆº‹_î¿~û°›ž:-¥>ß¼»sÛŽÍŽ%aJæºñvZðCWîiéH‡º~±bM»nþØðÞžÒŽRÆ¥†=ŠY9Ò—c-FÑ #1êì%v ?ÏŽêjTð75#'®¬óõÔ҈Ϻûù.Çî/Žds²íìþ ;n¨»µgk‰›‹Ý|äyôQ •˜~j{Ïd ?8‚’ÓŸiv ¿ŽXƒ· ùÊ=±³ÏwBžÆ6~¼êòQªõý’¥iè—º¯¦Ý—ŒxùÞéoM…I‘‘ϳuÉç§Nêëfs>‘;ë÷ç¢ VR¦¿þÎÈú#iÂõú|ËÛIºµlMi–w¨›4¯[·uýܼ¯â¶Ì=Ñê¡GZi³°,Ü¢¥”£k³v]°“µììmDZ~‰ºG÷tv´uó7í×FúÅ1ʾ²¶wµuVj?}Y™Ð;±4þîÌÔ²y~Ëú%ê"ë.7ïÙÙfÉÝ]Çh\»»dX_>—}PÞÐÅAGׯÖ*αóuù ¥'²Ê¸ùsê±Í­wÑŒdã]íA+5^·ÚO‘ùÖè³*wàÁ vüP×ßZé÷×y‹¾Gë'.©_í4§•f~Óa|ÝV d*¬„Ð>úv±Èçź‹ò²sïáùñõ…7¤ŽÓ®ÀÀ™ëY)ü:NE «·u :^Bsí\ÅH]ÌÆ|¥³ö“-gò|Þ²·Ö±ì8 wIVâ:qÆüROJÔÖQªlDü„êüQ‹aµbØù‡:a5öx(ÏËù-ö~öqj\fÿz+ëËÜz­íÄÔÖA›Ê\|ÿPˆ?Ôewp[­ÏÓ´Ô†Çixø¼•„·[gpñ${Û³ ù3,ü¢6;ÿPy–ç§…t‚ôC‡F4²¾|þxeŠ·Ùi܃åyS:±óuõ\˶w;Á Ó”_›p‚–[¶m?üõåsàÈ};Üy¸Ü-–'ê~è>mcü† þAÙ®øÑ'(ËäK[³Êʾߢ¥ áj§ižžhÞ‹?Ôµ /2ø…eóŽg_9AŸãß¾õªõåzá$>b¶Óž®Â7"çƒu²z\®Êàb½úIzO×ÏöK꼀 /ÖÆÒ”ÒÂäå‚÷¢Ùù·)_iéßdÏÎ72øbU\SÛ¾’VTšÜnoûjpõÝÚ™Kâ¨sÉR—p°çu,÷PW×/NÍý9_]¹çÖ‹NÒ$_‡ëÙìAOË$ìC[ËÝ´ò›ëõ–g°ã‡º-…+…t^^ùÂäfÙ'i× Û[m^M¡æ_§~:šòn€ë­pé‹fKgçê¾>^«ÒùJX­¾ã¥aïö^vÎJϯɔUé(ÿj§Ò+ßrP£ÍU¼]}ìüC]Íð!žÂ®¬å¥$Éï†AÉVžvä ÑQ‡N–ÿÉN·…×H+³þ‰ºï•cNuHçmú’›«vðÒ¬3©}nN·’¶Ó½¡[ª$ðvÉÀåvúJ÷\)RçFÝâGúL¬šÎ'ûÊ®Y©ñÒU×âµoè¬dÛTïŽ~J<Õ;Ýæ÷®ñvšµ9©ík›YÿD]µy:}c?ßB8 ?õRÍAM<þÉVruùÖ‰Ñ$tÿ%Z; Ê߸ö ”¿ïó•ÛÏ/»ÝßÏ?_˜³à^úå§óWX)eØ|°W,©Â7zídîµ-åc럨ëÛ¼ÅÊ[«öó‹Óºìüp§—®ïèXñÞ +EžÓ"Iø†˜Vï/6&c.[· .üºª~?‹×*ŸŸñÒÚ»ÎH«¥Ð­‘ßäTýDK³þ=uϼr7ïð{lÝ‚ºí Z÷ÜÏŸåžrý/Õ«ºûi·¸Š<¿ÑÒÚbÍ<+±Œœ7ìzu‰K«Ÿ¾Zg?Ÿ¨ˆŸ˜Qé=(ÜKaç›–Ô¨¸Ûw×N½;rãs^‹Ôq¨+¾ñŸÆ§×(WvnËS4!þÌоö¾},m®?éí›vz,zç½TY¤Îºðã3iügï(mýOQïà 5Ëo <é±»#î¦ðkÀ·°Þ.ß.°ëÔ­Øí:”°=?!¹QrÂäSD㲨Òöv<%„Ð;¨öP⥵‘ßKŒ-_Ù"ü`,¯ûü+®Å·§èiõ'y妼|~õÍͧkÖÃç:æP+]€?ÔEæ÷¦ñߟðtÇ)Ú7.n{•½)tsÉ·öN¤+Âí¦wTMmû~©›?ÔnöLãC©Â ÍS”ôÓå6¡„¿ÝO=£i ½…ƒæ“ðä–?ÔM|ºãÍ·ë¦ñ}¾î°qî£S´HU¢Ù¬2)¤úªÕ‚v]ÆQý£=e3ë8èñ¨ôþû°u'êÞ¨þ꼸bi|ÒÇW•ªj§iè»çV²®ÏXz5=;kÆ«ªb‘magv½Ž:ñ·ú:Kýûø:º½Z©íiRõyT¦rZ·¼Ù‹Þ)r|í$rÔ×!RçFÝþ­9ósø}|»Ó¯K¦tš^tïa»¦V*øÞZÕéßL9~ÍN¾òú.»›°þ‰ºÁ¥Æ¶0XöñKvñ;:}všüy“Hþ–·õ*LWî¤"×õìzos¾²DøÆÛ>þª%þýâkOÓáë [<­a¥šE×G3ÛÔ óÏ×ÁRÔý¶¾A«?ÜÇŽ;RóhÚiêpû½ÉKGwÚØ^œíGí•ñ«±Sä=øÈõ¬u›öW½Þf¹}šºåvMZÚÒJá×ûÄÓj\­õÛ)°1vôˆì~ ê"ë¤}|ÙÆ¯ ôâ4õ^8±tëºVÚ°ØÓºWµž7ze§2Š{¥Qþ•v¿umÏñÿrw/¿ø±¡N­Úgè­ý›¼—Lg.Oms-)‘~þqÑÁ’øœU-õ´ëwõØõúæ‚ûl{yÿùÚéW;Ÿ¡R=¬-É$¤èןŒ£ñá S½¨*¼(À®×Q7ë÷Á ÷òúr“Ë3Ô/û\àF“dZtüð®äØñ4yÒ gSlî|¤Îº×ÕXgšµ—ÿ£cI½™gHã­8«Ó>ŽýÆÓì¾gm_ê4iý촟ϱûe©ùÊŠÞ™ív ß˾y†šï}œÚ^ÍQ–«F×qÝÆÑ¥Ë–?”;hv¿.³ÚLf÷ËPùþÛ^¾iig¨UÚ·óž&ÑšÕÂ?‰4¦ßªìƒ7°þìúÄ1n'»_†ºwvóE{y‰¨x\Àw†îöXäq.J¢·Ô:´O µÓZŸ²ÕN©±^ÌÛÁž7 Îýîî‚‹ól]ž¡z‡o޼48‰Â¯=ާùïMú%ø–O]?~5rÿ؈ºåò3·:\|øu”ʹd_Vܹ u‘ïçÆ‘ðí»·Ûé½BO‡¬ŠÔq¨{“ßsëä,/-ßñIíf¹´Èž“ÑxN n˜ »gM%—¬îšŽÏÙÈcz×°ûÕ¨³,X¹øÑ²†>PåÒ³úÆd&Ñ£9ïò îhšû¬.]”ÝïÂ!‘:?ê†m©²*ÿ5ŸÛD¸RÍ¥ #äU¸b]üMøâðhª]kÒƒ¡º4g#Îö¼Áž¯<;¥Ó©©W÷ðÕ{¦:üy.­>»i[z5ŽYoý§cïã:(ò}Pö\u§:|¶i¿rǧ݇®È¥5l®4 Gc®8å¹O·T/ê 푚ÌÈžó¡nÕÂñ¾ÿøR}{;r©ü¾OªW‘DsN{s9½ºç½¬X{ïœ=çC]»µj ~k¹¯Kqóǽ½ ‰ZÏYrçÊ£DÚWuðŠ™ƒdiuñµ§Zö¼u‘ã¹›˜ýãWÍ/äRêGqñéïèbëÝçæü8ŽÝ_ÇßOˆã‡ìyê|rí^½›¿}L¸Q˜K¹ýæj~¾¾ž½·>Ö,Ÿ3±y¢ƒ^?tY:¤1;~¨{3þ`)®ÿnþê™uû=%óh¾¾šòÝבSz\ÓzýÝX7$6ÞAá×Ïk²ã‡:n’pÂîæ»TúJ2M’GvVŸ–~~-}~h…¿NÜ×AÏ:më·â>;~¬ç…Û\[vñ[w.ÎÑ(–ô¾¸²†„··rRÆÑñWVÇ]kâ ncšé§;Øó"ÔÝi+|oùt[Noo辆ý¹DZx{Ë[*;hUÌó©]Ùó"Ô ‘”ún‰]üÞü¾³¾ê—G¹ ~m]êÜjšþ"sý:õõßÏà:nHzÖ]¯“?Ôé-:óœïwò ¿|¿ý2mMÝ’þþÉ«éÙ±’&÷C¿<˜òƒ¬¾ƒ6güúUå‡ì9;ê¦Ü›–ÞºÛNþ”¡®éW})¯™ÄÛ¿\M)Ç?žXå^üË룧¤¥Þ¯©ãPgè:ÿöŸvð{„eÅì<Ús¤ïô×Ç®¦¤!3§~À¡êzÞs.tÐ1íbUÕ!ì½Ôúm­ÚdÜÁçøeÓ§~Gý›-i¨¿š}^Gƒwœe¸”±?æY w*Õ+ÚK©h/¥˜¢½”ŠöRúÏÙKIøGóç=“¢÷§öL£Y©ØlÛè¹+Ú¨½)£÷KrB¤hlšBsm£çÐýÕÌ•‚=À ö¥,Ø+IÆB3m5lÞŠ'jOÊuŸ¤ ÈØ¼ºèY+ʨý(ÿj$¡!+@6ðƒ Z &pCähØ:àÀb4oÁA¡™kÁ^¡±+ÁN€^fðDͪÓGíû-ùì¤|Qsp£çÔÉØžßoJaÏïÂ3V´Ò¿Þ‹òŸÝ÷ûÿ·eôþHFÍÅ-˜±R°ç÷¿c/ÊdÏïuÆJôžßÑ{QFï$Gøê€ˆÄ*0‚ ‚ C0kÁ^!¤•`'@ŠÐÖ€<,À øA‚@Wƒ Ü9^ø@Œ°W\Â_ ð‚ %›‹[0ÏN˜¯"ì÷-ìCù&ëIÂúè?}]T´&*ZýO®‰ŠÖCÿýë!ûGÍò-˜-'Ì:v^ö„,ØRXËÔ-ºWT´.ŠùçÖEÿ[ÖCE÷ˆþgÖDBŸÐ³ã.ü¯8! Ô°Ù¼æBóK„=µmà ˜Là†È Íå-<'î¯f—ì§m'@ŠF¨)4“·`FÇöÒ£IªÀ.‚ MS ð‚ T pB¤h¨0ƒ‡5W›=³DØGÛnÍWø@ŒF¬#¸ 24f-XÀ "4i%À ¢ikÀ ÖÀ øA‚†®¸!r8ploÁl8cÔÚ24-XÀ " à,4‡7z.œ‚íŸm?Hj0Bµÿï%z°$5˜êüs{g›ÁÃIz°$(5˜À ¡›¿ËEÍ))Ø7ÛA!Ì´`/ˆlJ0€ EÐiÀ zÿè¾Ùÿ꜒è}³ƒ C¸jÁ^!h•`'@ŠàÕ€<,„ øA‚PVƒ Ü9BZø@ŒÀV\\ ð‚a®8!R„»ÌàaA¯=ØÀ’¨Ù»³à„%žÙ\ÔúŸ˜¢giEë#cLÑ}£¢uÒÎ:I8ß ì¸ ¿á߯#¸ (ü6S×Rhöˆœ)™ÌàaMMQhžnáo5wDŒ¨#¸ 24Dm¡YºóÝ„™#~ YªÁnÍSø@ŒFª#¸ 24V-XÀ "6G×PhÞˆÌàa Xz°$hÈj0B GƒÖ>£Y«À.‚ Í[ ð‚\ pB¤hì0ƒ‡5yèÁÆfèÌuf¸!r„€8ð #¸ ÍÏžé&Ì1€ Exh„µxXÈßüë9#pB¤ ˜Á !#xtÀÄ!ÁA!”´`/ˆPJ0€ E`iÀ ^ósmQsFÔ`7„@ŽpÓ>#èT`A&Ü; xA„T€là BQ &pCäIpÿ†9#j0B GØê€ˆ¼*0‚ ‚ CkÁ^!”•`'@ŠÖ€<,° øA‚Wƒ Ü9]ø@ŒpW\Â^ ð‚Á¯8!Ò¨Ù¹3Ý„#Ž궨õ’ð«)xÖ_´^*Z/­—þ}륢µÒ?·VÎU#;fÂß]¨Wƒ Ü9›‰+ÌióMKFpAdhbZ°€DhhÊBóp Ïhšô`?HÐüÔ`7„@Žf¨+4 ·`>› E£Ô€<¬i*@6ðƒMT &pCähª:àÀb6W˜Íæ‚ ÈÐpµ`/ˆÐ|•`'@Šf¬3xXcV€là µLà†ÈѸuÀÄhâ*0‚ ‚ CSׂ¼ BƒW‚œlnÁ\63xXóW€là Â@ &pš=“MFpAd-XÀ "„ˆtÀÄRÔ\BF ð°ÀQ€là H &pCä$pà1ÂIFpAd+-XÀ ¢¨¸ÂL¶Hd0ƒ‡…šô`?Hrj0B Gè逈€J0€ E jÀ Ž ÐƒÄJÁA!8µ`/ˆ¢J0€ E¨jÀ ° Ѓ ü AàªÁn¬| F«À.‚ ᬠxA„“_ pB¤n ˜ÁÃB\z°$u5˜À !#äuÀÄ|ÁAE;-˜Å¦ásgÔ:ISt_©hT´NrÇÝWúOZ+©ØgÎÍþnÂÿ¯3xX£R°™¶6ðƒK &pCähd:àÀb45U¡y¶249-XÀ "4<%À ¢jÀ Ö  /4ËVFpAdh–Z°€DhœJ0€ E#Õ€<¬©*@6ðƒ„ͱ5B GÓÕ>£«À.‚ Y ð‚ÍY pB¤hÖ0ƒ‡5nèÁ~ ‘«Án]ø@Œ&¯#¸Ø [š¾,à@ pB¤ ˜ÁSh~­ ü AX¨Áná¡| F(A6ðƒDŠ:0B GÐè€/ˆ:J0€ EiÀ H Ѓ ü A@©Án¥| Žš_ë‚ ÈfZ°€D6%À "è4` =èÁ~ U`A†PÔ‚¼ B@*Á6ðƒ©¸!r¨8ðaª#¸ 2„«,à‚V pB¤^ ˜ÁÃBXz°$e5˜À !#¤uÀÄlÁA!Àµ`/ˆæJ0€ E¸kÀ ô Ѓ ü Að«Án£1«À.‚ Z ð‚M[ pB¤hâ0ƒ‡5tèÁ~ Á«Án _ø@Œæ¯#¸ 2„,à‚A pB¤ ˜ÁÃBCz°$À JQfð€0 ЃÄÁA!|´`/ˆDJ0€ E0iÀ R Ѓ ü Ah©Án!¦| F ©À.‚ § xA„°S‚œ)ÂO &pCHx a¨| F0ªÀÎÿÃÞyEµmës€bE Lˆ(%ç$IÄ’Ih@hR#9Ëj‘Ø`@Q ˆY0nÔ7V¯)wŸ[ïTÝóNÕ=gŸ§UíÚÛþ6¬žsŽ9fXãGu¡$p¢ÔF¡jȤ©ˆ²G%£:Pb8‰ª£üQ<ÔÔœTÍQlTJ'XU”ªõ%®>*U†“/ åŠÊDu¡$p2ÖF¡jÈĬˆ²G%£:Pb8Q«£üQ<ÔÔœ¸ÍQlTJ'qU”ªõ%“º>*U†< åŠÊDu¡$pÂ×F¡jÈ䯈²G%£:Pb˜ ¨£üQ¼?åIúBÌž“"¹ËDßõ®ÿoÿ÷þ43Õ„:Ò;Y)å £ÄÇåPL=‘0R'¢~ýûÝÓ '³#à]Æ#T„ì_ÝqŒ´„>ÚnØP&·I&ÞI‡Õwœ>oë ¯¥ëvª\s&¯Ïü$n'뢺¿Çúälª!cÝ©-p‹Ÿyηþ<ì Ú¬ñùMÚec·SA¹ãb³”î*fS=J…«­ Z`ÌܱšëÊÃá¸S÷ ³Ã¶ð¬ëý‘ ?Òáü´QÎÚŽ…\”ëPׯŠ,j]ÎË•-0žófNWÀ9”G³ƒƒ›,Ú‡pa²ªzŸ˜#ñÏ@ÎúðS·{Û²¨Ø³rÖ·€üË#WåµÂà¦ù“žŽ-ñqä’úȤNrkÝhÇØLê»ý?hó[{×ç¯ … “òJñ6°ñ¢ZvËÓt°ý9Ô‰@nó„·B;t3)ÿ%ǧ~~ÖWo©¥Þ[Þ¿~ç°¼Ú×3µ$*ü ”ÞDêD ÷aäe©Ã—©Y{Ã/ß|×—êÍ gCž¿ô\;  «Šo N‡˜:ÑDo}⟜Æ{w•ÝÉT±]p² ³h­S†SIG¢›ÍÀ–.£w$ &ˆÿPF'+›O2äR»2«ï8 óa$´?½& ßåÄf÷1…3Á¯ÆYú¦3톟ç¸ÉýìºNM½z‘½} X÷[Òjº‚`›‚jù®Øôjkiv:H´d>œð’øF!÷´átf²•ó:w™¤(o“kqw‚ Î,§Û1ÏXþÆ5YÓañ®ÐŽÑÄ7 9©ù/ ÌS)Ó˜}»$ø`Ÿ1ä֮РX“oÂÓÙb õ&·_˦1ñBÎÈcÃiÃjVpþûy øp{+] :Þ{dÏ×r5ƒ:%¯×9r‰_4ñëFŽ©ßžLI}ÐÛ¢'LJ¢Cæ9‘zº mBWØäÂrîÄE•Äw9¦N‡új©rðÌ*>l‘¸QúmÆYèÚvwwûl+8Lw[.TÐ…éˆï,rjQC6ö%QuK"¨u|X±š»\×â4©Çn ÉӤߩáó ʃßÙˬŸ³¾rî¸D Mñܹ™³TÃÒo„w}ñÂß}lÁéå²5ÒÒ\â/I|÷ûcŸMÕìêÑH䃞1§rj“?œß³þñÌ­vm®5ëé<.Üž¸êrÖVÒ~ȉÍÝ58!$žÊ÷>ZŸ£Ã¿AGÇËïñ× +ÔFôØÂފ葳¹àb{÷T“ñBŽñˆ£®m]qRYŸ­·ÓóÓGyC…¥ŒÍ)›þq>åúÌ8SUâ;„\‚.í(Ŧ®§?xäb·À¥š-öË=¡j&mÀhÊmó÷Œ®H‡xå1ë2ˆï3rk°c¨»Ž‘QÂv|¨ÿeæÉÀÔÓ¶ÍCX:Ìè Lp ¾_È)¾ð ÔËU»w…8óÁ*Äâø&yM—#;`iÛ³ðtx]à/%Eüº‘[8yÁOÝý‘T]>é(º_Ì~çëa;ÎÚL±7…1÷&™,¯IÑS[º…˜vÊìdm!ã1ÐyjU–ß§,O>¤etÉêSýMäý.™íêœ1š ØXG>d2¾÷Èý0šµJøLµÕuÝe8ɇõûÂî>NêÛ˜UÌlì¹\â·Ãp,ä>ÞÐ**Bí…ïf·Îòaòø%Å¥+Â…ŸZ3V6ZÀW6=¹°F¦ÜÓõ §\¼ÁÒnÝ#g©(ÚV$œ/ã${lL“zmV¤þ9öŠ+ιá¢Ó÷‹PÏÑ#|°iÊ¿ã '$X¾glÀ·æÅ˯Q\¨Œèý¬áÊplä)êó ÚÀ ûKȆ§¢kÂÓ÷¹}é2yâ[Šœ‚ÊžãSNí¤B÷]ÿ(‡d~Z(ƒæ•>µP Ø.óN´ÝKü5ˆo)r#…5aôîjêv!Òt{fï¯Ûœ°jF›%ý¬òü®öCî™–£‡þ[8W0ûâ¤R>T¤™üè¾(%a –ùd)0õš¸ð¼º×{ÎLÒ~ÈIm4Ö®švt,4F_ჴNÄíA”Ǧ”” ÊíæÂW‘ݧ÷&í‡Ü;ÅE®'àqíågBå|xoÞéŸ÷e"õËÏfùLë¬{f\¸”:<þÍ †ã!çP>¥ï»¡/ŒÍ³Œ\È[nÂ૽e¿üÉ.ÔÔ^|– }6;‚ÚÞØ2í‡\ð¦Ájù'¡Éô¢or:å&d-Jî-{ýñÍÙÏ©&à¾toö‚ .D\’«š.lô_v'ëÜæÑ6B˃Àéh®¢QÄlJP޴׺%}²ß}ä NÈv€ÓnøùGo'çÌI éÿýj ¿¬¡˜üÉŸÐ 8ç¾èò>;†c!§*0Ä:‡_öÝ3¡ø /¼×cÜÙ¤~©%Ü=“R’=2tiÛÃ]ÖL»!vïÓšîËç!weM~ÿNÀ‘€½;`ZAD¬ÊãßãBÁº‚/ó\ÈÍŽyeº ¢À\jÝì6ì'k¦%¯ûàmLe^Û°%ç°5Ô7y(7r¸`è³wmHó=²‘ èä¥=­»£Sâ]Ïåñ¡têÆ#­VTÝtÚÙÇh7ÇÝ\HZO;•‘vC®õäÍ÷#6ÄBߣçÔÐË|Xy-e{à •ÝìF5œ±‚Á´ š=f/á¯XRDæ=ää“ôOç¼`ÃË©‹†º%c¿·9òðd'*½óK\£¿,œ@WbåBðF:ã#ã.§“¥óÕdÉ&©x¸»ËAíƇÅF_S:’œ)ÆÇÊ º¬Âonzž­q´ñ óó$=½¾QÔ:ìÆ‰½œÁ¥Èë'Ì?;SÒ*âõ«!gXÕžúÜ_õ<í˜öCNÞ®Å99éðŸýÔT9ÇúŒç"íÚΔäO¹WÀN»:>dª[E"퇜›ºzÀž7‰ Túœçà͇‚ýM^žN”fyò%5–äÇ^¤†sÁi¿Ñùä–Lû!GÛ°Ks`Úb‡Z‡øpxéöPGªîm` BŠŸ^mã‚U‘ü„QÏŽœœâ–ýÑFÉÀÔåƒröu GJÿ‰ †¸—°â¾Š-vjALÿäÑßgàŽKbR@P&Ró9Ë ó>¯v¢~ÕÍãÜ44ì9ÈkW·D™õÌóu çþ`÷¥Š¶T/f—™¨ñA¸áÍËi=ÎíRíbeA|¹ [ž2Vê-à åv²ÆÑö*Ò!!30~èZ>üÑÒrl“+{³YÊu½%|+9ÅÁš ª çÝã1ýZ¹Õj®ÃÕ¸°ltÀ³2|8EÛB·ºQÝ:"7þP´ìYéÐL¿f!§'(@™ô°™Æöêé…û£jý'ÛY@ß[p‰Ï‰›È<èÜ6!ÿ2hWûÚ~ÊJ“›É]Ç©i*éU\™mlõ¿¸ƒ J'q9 Ú©/véLtÙÙÛE=c4´»SêBU5!+MÁâªE‡ öŒðÛö•ÄMä˜:òY §‘×.ü°zeߤMîr§Æ/Òeb ÊãB.\ô?nó½—éŸ<ä_î, «Rî½ÙRf“+$Ü©åC¢EVuÂAac.ñ¿!í‡ÜC ÚH0›ÔÁDnÁ††íéǨÝ Ç/~b›eE.\[(²f~(ÓÏ„ò:YCÆ6•QÙðØq^›;®'·VY šþüe(7Í8ÄbÖOI5\ÀIÅÅICN9×1Ïwä€tIdÃYß°ýªºÅMÌ2ÝV>լɸĩaþ8¡xëãt†c!÷bãëã¢9¢Þ¾¯Ï¦Üoy Vv¥ØNE]6„¡£/.̬ZÙÝUÈpúÈ ­^aSm‘ t5|­ø8/\(.êuHu)+~¸1l¸”¤·Mó\Ëkæjñ‡œþÙ“a¦=¹ Xþ*µ@N}Å$óCÔ/ßm êN.¸­˜4qˆ È•wÞ+r,˜øÔ­Óìœ4Å•Ô_7íôG’Ÿ¸ÄƒŒ?ä˜ï)&µ»m;¢"Õí~™s˜r{pz°²tVUˆ¬<Æ¿„4§CÌxè@nžé±'óAH`„Ñ ‚eóÌ£TºÒ‰Ì]é–à"ªq\ƒÛŸÿÚ/¿“e&U§6¤®œ2,Jli†Ó"‡çžèNÉ\¼ôÕH× Ü'Œg?ŒKêü3œrùEKל, >ÃÍ ç_¸vè J.‹9Å Þ&OW•]W– ]#ã¹Oîczž.Çx=Ÿ°Øf(Ú®ã¸Ï“j^¯PìÕfÁ*ö³MÓáû}Ú‘Œ?ä^>{lwijԹn†”×ö>囼¨_õXÝNfm£6¤Ãš¢$–©ç=›B—yþRÁožO|bÖ ­Ëò®™Æ{QzbtO2ÕŽc‡?ü#|odü!§òt®¥…mÐåý·5Cà•ÒDe/êWï_õÈíÚ¹…>¬ÅŸAæ*‡KòKšA}¾É°|!/*µwBí"#ÀEÔn»Ñéäç2íÞÜæ+‡»»Š!ÅZCE3¨Ú<~rÜÏ“z?v¯›B‹LÞ8öè¨=éðÃ0ßXӛᄠ:Y¦‚B¦Å³v û—&˜k9&oüOŠñÅ4YΫÿ¸á•-{í£ÍŒw äMK։˔@T½TÖ¥ûMÐ~%$Jæ’'õ̹rÖ4U3XýhŽâÇ—/ÓEL?c!· ¦ªE^(^wMp¯ "ÚCý|t½¨ÁÚ•™WÍá@”$ï…^:¦×ÙÌ÷¢Üº#g=ÿ^½–ê M 0ìŠj¶¶75`²ñÍÎ8?$IŸ¯–M‡‘‹cw1ñ̹º…馥Ä/» š;¬çóøP±Û&}·õ¤†'†£~Õ½'í‡ÜVAþR˜5ƒ®äÝ­þúÓ÷­ò¥Î;…yÝ6‡ÏÊܰÝi¼©Iþ‚Ü+¿6ýGRW@uEE¼¤Z¼Ý>ãã¬"_ꗿ畳 +³?¤ÁÒ#w\îN%ù r]KéJ´W€®F®*×Rñ=ÎN¾Ô{º²¾1;¶(©z­<¬†­úÜÊæø{z À,öµ´î¡÷:á} Ü{i›¥y<‘³µd%órøÃ#ÆãA :]0¸bÕ{MƼq¤"k­ÑßJþÑóö§ÁWÌF#‘øY„ãAð=ñÀÛS#×A¶¦~Ù9ú5)ÏÇ×"JÂõA;u¤k¤A¬~bZ8 –i äz—Š‹tmãÁ³îks¿Žk“už«ŽÛR[ôäο2 ûiPu³©(>Œ¬Û‘S­JK·áÁƒé-3?5€Iéç¯[Q³K× ðÕf9ÕÏ™î†qiÉ›! ºL?ÓGîâz#Š¢M&ŵ5üò¢®_7ôò1‚]‰4P_þtÄ[?‘»ª*kù9+ †ã¼š“]”¦O0§˜¼Òv/OO‘‘ãy·WUñ‡Ü“©ÛWó€ÉGàȘ†ÓšoM(Ã;µ³R| !ãƒp…ZV*ñ{'ã¹Fz4wòÀÔ‚óÌÁ«Î~õ`™R¿|§ &¦T mÌ5®9Qz}çAÔ‹ýó>š4ÀÉ=¡Ûzäu)øÑ¿öº̶];öŸèÚÙÌpBů´tîM,‡¾Í‘GT`mó¸i~š*ýýì@žÜ£óßR VxùóŽH2þ³MZ§4^¶–6Þý¾ ÂÅ<‡,‡ARQ¬qÚ»¾Ü~hêߎ?äß”rØBm73‹üñZðå ½ ‡ÔrX%ëS¡âj®Ý›Ë$~"·7WÈ&M§ö{Ï¿‘ø¾ÞHÅm5‚+Šï[Î<Þfô{*´¬±±˜ÄOäºî¤ÛgS¼W1J2Íõ°€Ê5©ÇÖaôslл±ëXeÞ©p¨õN»)i?ä¦É¶K 9Q½Ö{Žeä×ã·CSÅ­©m™U¶±š¦©³?ÄCn»÷è—ÃÊÁXpPPOöUm€ña2&≯Nh{@bi?ä46íššXÕ »+&¸ÕÃÍc“÷r m`LÙ’Èb& ¦/]ü!N\îâ[¹‘ö+édõÑvlyå°Ü¡=ìƒn=Œ^evOܼ‹Úos0†AÇ$w„d¤À·‹´)Yÿ!7³†óåÄG¦˜}7;`ïx²¹q™ Ý¿è©g ¼ íÈÂɾ r5‡Fk,]ÒÌzP±ž¾GOæ ¬}å30ßËŽXq¥€ Lõd2þ+Í9¤õ~9ñ» ÷=»8³\ÍÍÆ ·ãMz»K ¬ ýÞ£ OÖȵ³Núð¢ÝŒw»ß™Ï÷Aá‚ì f)°T`<Ï´;9Æïµr¤_Åw¸ SÍ\µ·u‘ëAF/u`χ¬Ö或rïŽÉ_cüzÊÁT`üv¶gåF 5n€}èNm¨£m$eR`ÁY[ïIüDN`G!\›n/rš3ð6H”6H.Ï95‡ŠÏÛ¦ KØ“ßìKa—õîÚk“õCi'ëT/màY1Ã+]äÕAÌ$‡½ã^z“èÀôÙwf7Jf<’õCé/?Ù ø¤>x‚{ÐO§Ûí)²[W/;¿·ß7Ú»wâÊ ÷™ß“…œ N¬Ú51dMÔ.v}áGâú~qbßãšA) ³ƒ[V¼€äŸÈ1¾pXP܂׵jêÍþû\nÙðúà4ªfÿÛÉ q^ñ˘ÄOätž)Ü‚ûïô—HÕž$çg† ~úú>'C•ÖêÇfæólü¼àxbZÄ-‘n2w¸Æ›š¼vôû2ë‘d7eÏûMÏÈ~'r¯|mpÅU»F‡·ÀvXìg‘˜øµ^ÌhRÝúôu2Ó^øyãÙ"—_âóþñª6PùÇ¿Ýè7ŒÏU2€ Ð<ÉW®t²0yoSáÔ·©Š{ÄÆ ÑO%ËŒ@®Æ'ÿ#'ñó‚m>ü¾ÏIÙºò@-ˆŽwSi Áv€¦!,ª]·š£“ü7û†,äæ\w<&>¦‚ørׂÎM\!ŸŽ‹oÊ"g}ÖOÌ›˜ ûÇ›mlÞÀô{}äÓö§eÍÒR‘-7Ácš˜lí—Óà(0¬Óƒ,¿¥/8pj«æÝÇSIœDŽvÝ‚ý÷è&IÏÓ7áN7úÖY²ÞÕ…›½[+÷>çüM\f#ÇçL­Âñ"°åÜxô~¬ÿöúeñ9×…´Ú0þÃñÉÎm;ಃŒ3äÖþñÇç›ýoB„nBö޽ÇW? &û~û`]íÍ{Û“áÚÇ·ßju˜ß³¹K¯Ì§w—ò†ç § o€¤žä­1œ°®}é¿~¹>¬“¢z“ánFýЛm$N–u²FJ77©t”½Ëìlê ‹øÏ ÷ÅSåf&Â3ÙÏ×w&ÃÃú ˆÄIänÝZ„_â¾{æ™I߀›¨pv…’qg Ná:Þ† “ÁçˆÉƒÔ—¤ý“OÍ©Å8òeÇ õŽë`OÉÍQÝ ’¶js±L`•xÌœšaÉÄ÷†´rãES/RåÀµÜ¬²2â:ì©2™k ‚ô¹ÄL®m’Øßʳ‚ðëöJ¤ýÛª•yÚ5»ÃVý:L[Ÿ˜Ø?~í¯Iñï:ųª„NÎ%ór ®¯š®‡óÎyqÚ ô:ÒÛâ]¡Àø†€FAÎe%UdNË_tÚÌsÈy´šÕ}ÃyîApÖÝ>ª¼²WyÕ†Á0‰¼“¡èÁ½Š¬¥PÚ2G)®•´rü§aÞå0G8­û顸¶¿+ÿ…Ó90¦q„uÁrí–ª÷ÊalÑ[Þók°Õ5Ú<ûîyÒš°`UÈMw|¾-ÃËSݾ’urò˜õÞU{¯×àl¹lÔ*…Hˆº]tïcãnX¾c:‹Ñ|õ?¸ §œÐ)3zIÑѣ΄h]ƒö!y"þ<šPMo_¬áÀ§£·ît1qÙ¹ùCs†/‘)è81À{ì5X|ûÓÜš @»?^Ñï;ÂbáÀ»§×>4ùylä!Z~GÆÙ\«†íUGeî]„¯Kèš®`µj·Ýô1ÜO2þSúÚÛ>ŸOºñûEÏjØùâàü/Ñä¾Á~8֭ػԉ5Çur÷E:—]îæs®[ ¶»öûêÆ_GpÚ8–¶&‡ä9dŸš×ÉÂÿ(K5ñ †£Wö± nµläµVÇô?Qúxu7ÞÎX6·r9BÎ4öšôíRDL¢$ªÀùH¯ÁÐØþ¸Û“¶’µD…ÙØ »kHž‚ÜlÁ„ÉóW[—Ï´®‚ç÷æ½R‘…sÑÇÕ'Ï7†¨Ü”b•åh´;0´n ÉSl“ùó $d&Žø*¬kÿQ#俚^˜Öd§ ÔêçrˆŸ+ÉSû2Õ³SΖ†æÎ¾+AÞhÚÀà³±ýþ—7¦ìÇù›ó6r{wuÇÍ_ý] ß\ ´{c!cŒGaš–<š›pcÉ›$Púv cãH2þ3 íNåypÈcÞ¢}•p`Ý`¿ÉØ|Œ¾‰q?Î$Ø’[’ñ‡íj_!ʃ5ô¶ØÔJ8¿˾Ï&çzP8ÓçåÎÂ$Ø^›UšFÆ_y'ks[A¨ÿ; ÎcJrN^õ—w7R¢ ,·êÁ¶e9á“Èø'㹸qÖû~fPðºGìiäù«`[eÊÁ8â|¾³kHäÀÁAká”Ù'CŽž•}m( ]·è\…¦¬0—Ü’8 ¾š¥°Í¾{ÐõÕò¼$~"7tNmî÷… ÿQ2cÚUXüæã¥íqýñúíÔÞÄG‚íÅ-$OAN`ƒÔY/¢®¯¸w¿ž¯”|˜Ø×ï£×ÐýdTÕ `ÉÞ/ZBîG >œj¾}¡ n S‘ÏÌ%Å猟ƒ=…,ÀèÂnþ$`|…I¾‚\(mc»« 'š?ݯ_>îÖ¯ÛŒâúÏ=éÝœ ž$èÜEG²N@îúµ7ç .ÛÄVS£9 dr7á¾d˜Ôd}õÈ2ÓænÇþ²ìÏÍ#ó_Æ%ÇEºùW 5qS—ÎŸÏ M3{ØÐ¼ÐdÊcm3è X¶ïIL¼tsœÉ_cÎ'¯+)ÏÂúr9¼ ©ÝØÚÁ†lzÏÚ”UÒtw&Á úĈÌÈOx>©xü8E§í‡ÊAÝwÇÃÖ×l)07‚œ f‘¥“`Рæ-³Hž‰s^[ ïZ§W*•ÓÁΓþËÏÚó{k‘…|ìóÞp·šÌÈéY½Ý¥i^ ÏÖ®!2¸²—åË)íŒ#ûT°ÐzH¡Ä^äŸÖM"ã9Ƈ³äOÜ4ºÅåQS"ñ¿Ô¯Ýóîž>Ùn9ÌõN‚}ÓT½@Ærk6~ö4³ÒiøDîžšt7­{ùèæðYÉýnr8ü˜;Q²ƒ´rFtfSÑ1‡Œ” xP?¦0#b\<è;މ¾ÝŽ]çò : NópRIû]ídí)RÿÚú¡&›ÆE7É`œÉ•Ÿ³<üe+WóŒÀuýTQÅ;I0ÄShÊ*Ò~Èý캠0: Zÿ8írǯY¹[ÈV<ìv,*W÷3ç«Nçî~²¢ÓzV=&í‡Ü H5 æÀGÝBþ°Õ;Í–‰aìAffPvŪþ‰ŸÈ1÷Iò`¹Ö-Ç e%Ö0ÞyI:~íßÿP¥WlÕ†ûo.$ã9éÓø»'åÁVA"]QEÓb¥ Ù@ßRؾÉ$ ½|B83xÈ]k©ÉÈ=• úÁ rH Ì™.Þ81‰ Œ_¼,´9xú¶1æ§£Iþ‚sÞ• ÎÒ˜‚ƒ³LâŒ×¶ì~³ # ÙV2IþR…ëzã‘;å/<Ö,æÜ–Ýïo6LxUèrÌ{î„òžMW"÷˳fy:|Êu{ÉoEð¶D^JbÉ}kÐ[‘Xºg6´Å™“áXÈý!u¨mƒi6¸Ù,ßcSÅGƒF Óˆé÷,6ÓKxŠùgn­äÄ½Ê §œúÁé®y÷³`ãÂÏsÖÁô±\/¯hàÆŽ@KÐI: ù`æóõ¦×“ñ‡œ`y¹- ¬ÆÒÎ…i±gÜõ—a­à"©9lj$î%ʦÅI‘oœÉü‡œ m+^š 2üxÁ…ð zä™1ûïKê .¨$Áˆ»OÞmžGÆr/µèP&$ŠÔÌ9£T‹Ž?:wd}âõ|Œ½tއìÑ÷O¬zJÚ¹ýÅ›G \xNœ[hý¼\½®šÞ«¼Uñº&ÁJ}­PNzÜ–ÝR ‹Œ¿êNÖ/ÙmI#2 <{ÔÁôÀX_ï7ãèähØèí>NÁÔnöÐPIć˜äŸÈ*‹*Ûž¥áAcÝ–À“ez;œ0O®Ù›qÜÚ¦L+¾#.ÍÁõÚV†c!÷`ÕO—;—ÓÈ}Ý|81?onu4x} _©7ɇ}ÝäÄÆw”´r ¢ CSáÊϳæžÊÙY-¦_£a±Dî̲mÆàf7j/‹ ÿÈö£€´rŸ×jú 1LžÔ:Ëkòa2÷õ ©ŸÑ ºgArò7˜ðAù¶i ‡ì¿ö«î¿ß SÎj ßÑ•r#Ú=\†Ž‹ã¯”›Ù±Kuîs 4M»/)“´rÑãiçS ¾ç?¯$2Î*·êÙ O絬qÖÊL‹¬ê8p_f¿Ì¨`æù:‹Øîaðµ'”wó `Õ»ë%¡]™¾l _4\ë_sà³}CšÜ˽ÖÉÊ›DO|—@p /ÚÂêêô^€¹WßÒl®Eþo+Ó9P|åÒR]æÞ¤r·>RÎI ¾é¹`ðî,wÆ…þûzÚÂÏ^ºÄÖñôÄp,äövK\Io— _3É…!Î÷n;jFãÏm/Æšˆæ@Q@•݈…ä~'rQÊt䌃ײ3gä‚’»ûÛ{y‘P¶ˆ> ±€’9‚ 8é$À<Ÿr‚°s€Mömr`ÁÉ¡aÛ#AW°‘l‚c@Ì›H;Z“ûÈѧ‘1 ÷|Ê!§sÀÂG~¸ôÌH˜ù‡ŽÌÆ&N/Îr`¦0¹ïmRðXë"9ßÍ'IÓ ¼)‘°€KÍúi {ÏO‹MáÀVç²òÖ ×\YBµwÄÖ(è¡—…ãr ¤è˽‰HP8z¹óLxtûéèX!¤ýjp]¥qìùä9°U÷ýhŸÛÙ°ÇûÓÆè‰‘ ¸F¨a ¹×…ªI%CÇÂâQ3N¹ ¯ñ¦wœË?´öD…eÃhKã>vS4Ì”½tu½9„}UyxÉ"†ôhÕì›Bîç"·SM°òΊA2úÙ üÞ¨Vokn>Ÿm ‚iõB2TÛÑN³ä~5ru}–cDjƒvË^:'žÏ_â~*ò㬡QA:jhf2ÐÛ”|Ò~ÈÍ8;%ù”ûYhÝÙ"¼ós¬îŽY(t4J–èÛß²íÇLve$CÎqù¥zþLa#§¶`ª•»Y )þ4¸·" ž¨šé?19±J2ÍQÏl`—Ë7e½KÉps×·r ¹ÜmJïÓK5?V½÷e¹,øY?oûƒ½aZ¾óC·5d¼[<ù}t2( â®9u͸Ú˽@&ÆíR¹MªÌYèì ¿î-´„œqX‚ß ÓN 'tׂ÷ãp,lÜdMYpõt¢NwH¿¯¹àë IzvÐ8@Þk@N`?Òĺ3+‰gÁÑbÑR±!Ðsº+Æù´)¤ ­]Ÿt4Ø tÇ'ã9æÜÓçö‘ó,~d‚˜Ï?ÉÐþ}0f?>h×וoHû!·}ÛdÃ*Ú@ߢ¸Ôž 2‡2Çö…ôûÁÎØþÆÉ0ÐÃèvÌMÒ~×Ý—eQœƒvǯf‚\àÇì?B€>•ÙúÓ éá|*>ÔÅÒ\JÚ¹‰ç;EýcŒ)v ï)•”Lˆ¢¾hg®7M³<ÞL#hkC[úsÿ’áxÈù³-{-=(=» )"g3¡cHõHùã¡àòØ¡t`ƒȘ½=•¿ ,öä=|“¼×€\ȳ³6SP‚ns$tzü]Š#BÁèý…Š-ƒMÀçÅ‘ÑåSȽF†ºÑÉš¢}íýúÄT† }°‘ ñ£yC6ø‡Â­P«Æ.¦pqÇŒ´°˜`òw?‘câˆuªµ45G7Ì„MŒïª†mçóÍ€¾-81=Ö¾Øà™bJÞK¹ñk:€²¢·1·gBÈ”‰[ž†À÷-ϯæãºx¤ò˜-) ³tß ¡ƒä½äz¾JØ¥u†ú¹÷ú™Ó¬L¸Æž°n¨^9·±€êÏU‰ lR _–>Pa|O=»ÖÁÔ›°1üŸ+3¡3útR% •B—uϲ$ùP ÄÐÛrÏŽ\Ïñí£PªýÖ®ò™°;>ØÉaa0ñïµ€ÜÛ,¾/JÒ0zãƒñgå!ç¿8Ù.²éE¿…‘“DÎyÌA°m&›Bî×3ŸïÀÏ»Ðv­#(/NÐÂ2™ X2d¼]Œ=vháV'Sˆy˩޺"ÂÌè‹FÌï'tóÿ„+úl£¨‘‡SŽ=4‚{C¯‰0LÒô+צ@ð…GCŽ'~ÁøùãèÛ‹T¨íÖ'žø<³fݘœô*}Œ©ȪkèØ–¯GÓ‹Ä/¹à‚}8%ÄR¾¬“ ´š‚ú÷eéÇ4I¼>žf ‹øÌâç ªIVPlJ—ÿÝhÖªLP»Ô=š+ z”þ ë½Æ 8Æ H롳? g“vBÎOnwÕîoq”UtáôËØ¾©QÝk…‚Aò_ÞÛÈîߥ7ŽS±ê'_’vBŽë£cþ\!:µpñ™ØŸ:×îÏ u±0 …"s6}¸B[s }7 S1†ã!g~ÞÝ%JðÍîL¸wcÀÁÑ’A ¾ð‹ÿ{KòþI <¢mhöBî²kwð‚DÊY°pÌ„ ÿq¦ g 6¿‰ï¯o «®ï9¿k`*Y÷ŸàZg]w'×äPÌ9M&”œ­\y ^·F¿Qzo _‹Gý|1$ÚóèGˆO0rŒs2u]˜¾ œ i©Js?w¹W¶q Ç%ÿH!çÿŒŸ1 ¹ýŸ÷HÍMN¡²¾Í5(<Šwç°òdÿ{6Ívöh~H‹Ôëáã‰O7r×LURßö¥R;è ™Ðbå_óÍ®¤oX€\ípÌØq¼HéÉ5e8ä4Œ¹qÍûÓ©I‰ÞúšW2aœÂHÏ¥~ýï…¼èÙ¹iŸP*èÄçàÊ„áØÈµLˆ,âq)r»õ§Z3aÚ’„œy÷}aú¹ ɉE&ß0£²nb*ÄD|4”žG|ž‘ë›0ð˜äeJã¸Æë¦÷™ ÍRƒù 5H4†ÅÕ¼UgÄSác[ø³ù¤ýknÎÎ+SͤÎE­©1* \ì3W,•òƒbúÚÞPc(îˆÕ˜“ IÇé‹»d¼Ýêd1ý1‹jË—ÎQž›’O=ÏdÙúÁ©/jµnKÁ£çq¥ÖêTP6kLêžIÚ¹›T¸VFAõxUµèd•, ´Ê\Õ£ü@^ãmÊÅOư¨Nì™°M*r‘wž/½!ŸC1ïdÁªÂÛ>¯&ûÁ²ú£uíf°sûfþ£®TÈ9BßÄ&>ëÈy˜Šˆ·½Ï¡˜ýÀ,ª>°H͘絀³š[n÷ŽOƒ•\O±.Ⳏ\–†òƖ˹s¿/ ¼ú¬ŸÏ÷!ç8–ðIo\ŸÝœ48:_ÉÜ¥iwr-ä:mò(f%†ß2~#’çtÖÉq¶Ák=¢i °‰®eüç;;+¿½í¸\>5OVnµ)J?M’˜æ Oî<Ý+ηú­Æ¼‡©@ß‚‹ÃøÝ Õa|ñ£_0̧˜û ÙИ²0öéaþ{ƒ›º×œkŠI…ØÌÊA7’˜Ÿ'œà¬¤€êfñw¥]λ‘«—:Šjô-–@Þ»ƒ ßê Ê ÇB®üÌÏÊ…TÈPÉ@…÷Ù˜ï4iè£#$ï?B@Ìõ͆ÓG®èñãäûЍUôë r9pÈ8}ùÏËnuéþ±3gMÁd…ü·ûTPºsëÔq†ó@n¤ÞIÖDZÅTIJ,ž–SX”H\;âíkÓjáÊ.zç14¿ÑPFÆrƒ²¬Ÿ5SêW 2mKs@AJÆþtƒ»WLv¿gú‚ƒ T`î%“öCn}&¶„š*vXgÂÈ\8¬r´½-þxîW}°Ò˜œÿ¥‚àµÐK ל°Ä›nMãRªWþcñFý\Ð{s¤&ÿHÿxš£4öMþ¼|»Â³Ìó Ýîdí¾_¡¼ßÑ/Êä™O'·;cܪÓȾèÝhžJ%G_^M%çÙL»K §öæôˆÞ+”]SìwÕqy ôÔO(|ëaxÐe´V¢ÞüÔMÇæâ÷"xPÒ~ÈmZ··.±°Œ²:\î˜O N§-¿ä…ÜEJ²¢V Lw‡ŒT8,0’'í‡ÜúZ¢E™ÍóJxÒ’̓–\¶¹àH_jèÈÛj^ÔÚðôT°?Ä·-©'퇜ªÿ‡Á‹¤xÔ‚•ó>կ͇s“žÌ/µ‡3ÏåÙNY‚Ñ(úlw¹=~j1ÏÇFnƒÜ“67_EŸº­Êȇ¯+{kO4[÷Ÿ3xžò¡” ­Ûç_>WÏp<ä˜÷Qyÿ àœÏ‹®opÏ’?åád30ø1"r¡ÆÏͺØßí‡œŽÆ›µ×TÊ)«VïÜjvt¯6¶«[dÂ⌆š{é©dÿšá„ê;Y»ûŽM-§Réc¡…`yèGœ–°múa‰lU#Zçó<1¾O¼vBë&i?äjÖ9á^A}[ðµM]HîuY÷ïûÏwQ¤q;Ö¼v¡oñ[}rØOIó J×}«ÐùE`˜Vü=YÉBÄß/äÛÀ§Fú‚i*”§=­Û¶„´rµ®æÝ½ZAy®Vç(Á£˜uÃÏÌu ÷ÿ €^îþž s‚Æ—äù¹‚ãßnG‰_¥zöèùYn)†37 SN’§ðjP Œ‘L%@ €«)‚hPŒhÅÀd]$]prPôþ‡ùÝryþ¿»ûo­ô?¯V’êü­“þÖIÿ:u’¹pÍpß+wlð*PÎý<”'PƒJ`Œ`%2Pô¸LDƒ `„@&~ è"¨‰€Pr`€ g¼Fx&ÀÈA ÐGMd ¤ T#E1ðY@R<€ ”Lsà 4Bð4î@J€>‚©ðjP Œ\%@ €­)‚hPŒxÅÀd]að*P ”Í7ÐÚ¸9(úØfÀ¨A%0F—(zæ¦@ ¢A0Bp?tèEÀ¨@90@à7Þ@#$àd €{Ç IÁÈA ÐG‚0ž@ *1†È@ÐCò0R *€‘Îø, ‹Ä"@Ê9ð!é˜w %@IÈ x5¨ÆHJ @ ÊHA4¨FHXbಀ.’—x(HfæÀh„ÄfÜ”}$:3à Ô #ñI€ =$AS Ñ !)ŠÈºH"àT  ašo ’§ prPô‘LÍ€'PƒJ`Œä*2PôhMP Äk ¤ T#$b1ðY@IY<€ ”$isà 4BÂ6î@J€>¸ðjP Œ‘Ð%@ €’»)‚hPŒìÅÀd]$~ð*P P˜o ù/õ‘˜‹Eÿô÷ÿÝŸp~æn•ŸOÛÔòÊ”ÇQêSòKUÿ:èt³wÓ\ˆ÷¯5§ºSÏ/¼ÛÙ‰~¿ÏžimFcg´ì²V)ôÏÜ!øRlþ]Ì÷‡ÀqÊvžËPÜIb[;š8LtdTj¸xLkšÕpù§VþŽTý{ÙùJÚ(wM0Òåû'È »ÂÙctJf î×é©«ûÛ§=й”¼KB[½·vh¤¤Žë§Õ{õ‡ï+¡†Žï˜Ì$ãŒÞ•JÔt(µïÁ±ÆV”eYÞìÒs{ZðÐË}I…‚b¹vÍ¿…þ:Ðå¾T*kF$³yÝ-ëû'ª‰Ì»ˆO ~Äv„ÿ9~2[A“='^°úëd—Šê´ZÀ²¿%³d§ÝâHk7æ+®îÇÔ7ÏÝgÓ yzR3ËHèï]ÑŠn§Æ¦°F·¬ÞGŸ7äm{ndE“ÎÌ Í}jKGj­º!MAsn¥Ýôú³@Ç÷ OaRÛ‘k6>Œ£5_zuíT&®îc±¡«çG÷šJ êÚ*¹õ¡¿t}~¶}—Âø~¨ñ4ÄàD†…ÐOPBäõL“6@)øRóç)…nš¥îˆwR˜Ã{k+Ï3ñT'yÚÏs.eõÒÜ™|Å™Ú4K*kl¬$­ýÜ4aü ÛãÕqþ“‘ƹOûþˆ§ô[*ï–¯{SÛÚ ›ô u%m{Ò6Jgëcø°¯SCw¼÷œ/'ViØÀP[¯§ËȨ[ÎävÛg²*Ÿê§6ë·Áçû¯ý`J koÓóVí ¨¬ÜõúVUö êûÍKÌ–_8l^—´·è‚ÕÀ— ~ÜrJEmMd} 4ìÂÅ—‹FvN¤ï¯{ Ò×HØÁ{FŒ;íBÆ‘}>Å)Hk›ù@7è†ÒìZuSYes/ïí‰dì3²ëH7¶àÒ¢ZÇö8‘‹öBQCŸßÎÿú²@H>,iÛ“·H¥1æz-÷Í[Ïn_Ûæ;Ñ›Ó?”)[”Ô¼2áζ¦Â}ɧÖMßHc#ë|+Ô¬M¥i¢÷º-^m`M¯ÅÅOu¢Íokÿ¸ºZIçêÏl>=M¸ï +îvZù :¾ß`*¥LK;²p3[«m„ìHîâU—4K”dØaã½_…¾V¹¥¢ê1wÎã’Êÿja"Ï l›€£‘5w±¢»=º/_A×MzŸ3]IC´×‰ ›ñãÙï—+S™-þfRÆ©&óš-Ûø,étc OôÈPŽT >ÂøAWÙfœáœTV¾+kœ¯{&i,´†ß,p×øØ•®¹Ýä}S¥Ð·Oè ]‡»c^µë—ÊÊúÇ·|šIZƇ_ß´Ÿ5oq¯¢_ˆ39[µ¾?"C!ôýÆ:Ñþ‘«›ÖOe%aÏ ½C×Ë™µ$ë¹T$³Ù‘ø6Ì úx[|д¥Ð×:iÓå7Ãk˜ÖvíÔâýz2¾ÿ¡}]u¾CO]ÿÑ¡s¿L¾?c tGkÈö·Ò0®«rÇ_w(£Rw²[ÍC¬‘¶¦=ué&‹o毠uóÒœ{¬áûÊêäãº^DÃRk˜EY²Hô+~OÍf‡߿ێ*÷ï ›ª •]—LNúC7nêö:ÆvÖé`AO©uMúäü*èÏAÆ÷¶|ç<èàÍqáBÿ[èx$ ›:µéåÑ,Zfx¯cî÷ƒ¬î6æ×o±Ð÷]I¿í¾–Y]àubèv~ëc{¥‘†MéÄ9{dQaãO5êzˆ}àÚMÏ·§¹}õ•Ôõ{Äçœm¼N ݉W®ƒ[=Ia“>ê´þö9‹´v>‡ØËqãz-¹î@•&9»ÞôTR®3¯“A—œß7<…iÛÃõ¼K¯ c #¬¼ØUÀ”yc©b…ݾÌAJòxíp­e*ÿ}ª¡33¹1cO [±[Õnºù]ºWY¢WÇë0‹x¬Rqɉžqê—>^IÃ,ÊÎ]y+Œt6Zcö沄s½K¬4íðOo6¢ñœÝ”àLüïS’RÕ¶d‡‹pÿ”ŠÞD?^ŸÓ/…9 ¸øÆïÆ]r_3rèhkÆûB»P­qº’Ú¹.h^XOèË _/'³ì‘\gÏ»ôñë™:¯¦aSÊn»aëB)¶¶‰´WÒÅm;´*ôÃ…n€ãÁ¼9¹ÉL[¶·Í¦‘ÛÏ÷œox”qîë73œiå¨!}'/SÒÃZk,3]„þÅÐiÛ *’Ù¤íc}zNͦµg»/|q”}M»µ ÿ‚uäl*©»x͆ÕÑÂøA·`¦ÞÚÎÒd¶Íß³‘Ò#›Ò÷h½xó1ÆÝäu¾HÈßdJÇŽ‹”B?¡ÿ4tÝòJ¶,HfÏ®r‰0›ì6•.¹tïÛ3ܹ"¶·¾Më¯o¡¤_…‡[úX ý§¡3°ÒW•õNf]B’›çeÓ„Ÿ’båcÌrkPêÁ(;Á×ZIMµBÿiènFF-=R+™Ý=’’ó¡VλUT÷Ï1–»ëügG^uv;9YIsî·ßl5Hè?]ˆ88ªèÒðIŒ÷Ï¡?÷¯¥|?ƪæÖçuöûô@~ÿæó*Pèÿ¯ÏÆÙOÓ˜aÈ¡k-–çQùéÂym޳÷Å»3tèIfÁލ«ùþÞ‚t|?ü$–ï¼ÑÌl¡(øÚ|Íq6#à¼û:gGú³„«”•B¿EÞ×@ ÝYë{“Xâ“í/ÂshÄœÏôzž`U~ÐÖ;ÚÅîÆ<°ß±³Þ4â'…Ž÷?Lb—º”Îéý8‡& ì4mqßê8ï>ë qß´ëT—Bÿ~èÆ?6@Ò%‰y¸°#¡á=j8¼÷ö‡v~ìë±3-6IÜè¢gøŽûÞê©ÂøAÇU¿¿ÙõÆ“®¹G†o~úøŸd×q2+¨2ðEX­+Jú4DìýÉQ?è87”9…‰B=}’Æëv›“pŠ=õëû–¹’tÛ´ãF›”´ï•þ"‹ÂøÝGžÖáœLÙ¯âQN3<ïÑ—ÎA_Ïf5óËZd;Ñïm\aª$¾º0~Ðý¾.ÑxKd˜LÅž»zîŸÎ¬½¡ïÆ÷­–Ðó_Íö vÖJaü Spaiu"Kô«ùû¼{ô{æÎ_ƒVŸaÃÖbìiøˆÍÙûjãx¡‡ %Œt«Ø„9²Ù‰Ì<©ýÉ)¿ïQëû\î;ÃøëߎB¾Ï:Þ¾ù²ÆGÁ¿ ºNÎFõOdßbfnûÒ-—ú7XyÚrçvegthKHò–Ø*éÀ|ý¥º«ÿènW~mÛ¦I";¹Í.`j.™¸véöcÓÆ÷‰·ú¬+É^½xÚ‰£‚ tü|"imïÜr©[úïý“=ϰµœ=»¾=M~%wÚ!Wþƒ_N tQ§Ö&MJ`®úls4—ä²ÇGGŸaÑ£ó6Zå@‰†œ“•’šzk°.Pð*BܺÓ{p@³X=åÏíˆ\z¶Ìläë¶gÙ+Ï›[9Ò"­‘±’ê·?·¼×mÁ? ºß¸“ÀLnL[Ÿ_˜K×O¶ïxAu–yÏüÐmk’‰ö~ûPó5ê—) ·¶=(øAǯ$°­Ã‹C'üÌ¥p«ZÃ6cU¾¹ºZÃF¥ÐW?謧Ü8(e·Û9K·cm¯øù+Æí<[ËM·—»RöaÎ@^I&çG.ì5F¸ÿ ã}ä˜áo£—écòÈ ‹Ûòq$c¼ÿ¦+%-n6ôîs%e.àœ§ÿèü¾p ïãÙÆÇ™»}–çÑŒ.¯jN“±¼vÓÞL w¥ç²^Í~¢$m;ó‚ÿ t|ßñx6\kØ’G]ovP»Ì¸ÀÆ5¬¯¿ì¡ å_rÓ _I•ø+Oð¯áÆ¡AžÉi¯x–ÒwSó‚3yäfÔå×é[Øïs]÷z:Ó NÍÏeá~çÚîÆï®¿}nŠãÙÑ„» nä‘}Òíþ«[]d|¿m'ßàÜø+÷”t5âkpï!ÂøAçÑó|§¢AñŒsaÉÉÏ£²#Ÿ6Ž[p‘ñë\š².ÀÎÿ¡’Òâ\j6º-øtC×y‡â®²Ž÷¤´EèÇ<êÓp³¢Y×ú¾µÐzæ…’22/®ï]"øtC×åò@”žqÌR;±Ï§_ó¸ÄE&k›XoÝOªÕàó§f•JjòÍ¡îûí¼O ºÓk–vms&Ž]äÒqŸ|êQÒ%¯ïã‹lºv"êH–Qº?7!®úó;ïÌtü}ÇšïT§]˜”OMOͪ=à«s±§ÝÜ Î4/|Õã>!”S¼vXÙyG~ü ëêâ±1 o[x^y¹À2ŸêõÊIèã‰]ÛsàAN®+½v]45„zwj“Ûõ¢„?è¸<ÿü^ÍrëKð+òi@…aýœqþlÐãÞÁ-ÜIÛÞWBZ;ÖÓüñtŠKE¼¸šÍ_ÓÝ÷w>Ý-´õ—?«ò÷;y]ýôš}ÍîÖïxT7ÞÔº#^ÇÚx¨™Nʹ1¾ÁùôcÓ®Ï÷SØŒáûƉŸºÓ†ºÚ™5ž[ÑrU€àŸ]ö*ÙÞûÃÔìæ6Θ)Ÿ©Íî^ô dUþ“{ÊlŸ,BžâÇÝ3ÿDèæ™Ë.¾dÂ÷O¼Ï{[sçÆäÛÛWPýô¨uUå'íøAÇûÂ1&ç–^åSÝïçù^ bš— ïL}çLÁÍ9'S%=îaòy{™à_ ݃&ÌÉeé›ëÑJz·»Å“¢:òêùƒ±¤KKˆg»NìÍ»ºÍ7nØ\›Ë´öÈ- Hk›3JÎx¿Túð²C­PÜ—ƦvæÃC tßò?¤}ŽŒa{JŽ&ô, ðˬ\,gÓz6Ÿ14Ìž¯õ_q©Fíñþø›fñã®ó°T7°s/ƒæc»ðû¨ÊxhóÝÍMΨCwLÑí E(*Þ~Üðó´Ê9Ûuß-ö§ž¹rÝÌâû7ËYÕºì诒Ìf…~üqDÐ…hÊqåÝd®ÊÅ›- ¨Ç¦GóŽÊYþ×Ç]Ò()¥§ísתŠÛÚqƒ®mgûE³n²6gûFÄÚþ|EØžoù"˜/ˆˆ)>âBÚié©zÍ•Çùñ..´_­Ì¹¡Ñ̶¼`\+¤µ¶Q0¾Ÿ· 5͉½Î6†â­OÆ‚üý£ó¨T4eA‹‰Åõ¢Ùz½QóÊoЧCÌË*˜®6»ÐnZdBâ‚›v'ÜwÐíX(ƥŖ™=t»€V5 j½t®’qÙéO¹3i§™sC(|äˆ +SøóA§µ ˆdÛ8[‚z¦úáõøš’9¼‰l˜±ÖIèB· º}cÍû¹‹¡]‘1‚eÎ÷œòäiõI=ž{ç‹’lxn¶G!¡Œº'mËú†Ðýχ=^ΟRè6_ŸíUsbë3ðδ%ï ˆ V—öìÂvsË)ìÉ^kH÷Oã]ÀÇÇ‹w g39ûßäøÍðCꀦhTºBï²EV¶Ô)mBUñ^;~ÐiíísÃçRý¦~!ýy}EÀ êúª—ÅáøÏF¿YœÑ¦=?~ÐMHiÒ{eÇ06”+G[’¯,ë禑!¬Í­ò>¦ƒíɺÒÓ¯9â-7[mãgÇ_I©ÈµÆ…Ïv¡ìÈhî(¤™‘†;Š„°‰ë¾ê;h»ê[¬ |ªlùñƒnÏÝÒ kŒ÷Ñ)¤cí†Þx趯ÛÛ²Vw'Ò.ø†Tû‘jÇ:Õ•÷¬+Tlßι½¢’&;½zÂx_rzÚ»5ëBËí Œ^ çubèZlÙyâ 3[ØhQ=Q!ÉZ<ŸùÒý2;áûsöï·®”Zêøm»2„–|,Ðï%¯“B·¨|§KùÆç›BÚzçÒ˜ë5®TÇÛ\ûú“!ô‡fùŸâ¿t˜”0s¹Ìv(GoWÍ+¤'îŸ9vô “?^j>ü¡+…má êBhìZnÅ@?èÌVèÚ¢BɦÆ67Ý·¬z™?ý‘ÞUVëøûwƒû»Ð<­‘NÝ´*º˜^Wˆ›Ðm_|Áôëjë4üçŠv…ôÝÁJñiÅUÖÄSoQæCGÚlË93…ïÓ!ÄÍÇ¥¢#œ=ÀK9‹²jYû°[!ÖprMÀUVå#êu%™9¤Ú‡V;~Ð%úîëê¿"ˆñþ±…ôŽluµzý’{še1ñŒ[^lÁ/"è$WrL§? `^£v~ÞVH ?=éÜìÊUÆû>ÚÒÑvÜL&„.F\ÌX¹B?èø<êÏ8NCÏBêjúÎîõñ«¬êúÈ Ø6µ÷¼j_íøA§|}³k壋̤ŸÇÝEÞ…4±¥ÉÖ™›®²Öþm†Ù6\!¾ÜôýQf$â¯Ot7'~ÎS/¼ÀZáÊ/¤OŽoó”\eã³î*N¬±¥¥Vß—OpÇ÷ÙøÇ˜k³øÏ§†Ž_/;Ïf,¸ÿ¤ÝùBº´»Uô›«L÷é–>v4²®íÕõÛBè„EÒ“á»y] t%öÚZÌBkÀPHzÞ÷ž7]u•iËó{k ¾B(ý^w í„ûïI©¨FÛÛÖžd®Z£˜Bª{qG§¤cWYû)!IA::fœóåjÈ?ŽƒtõŒm0õSьՄb”êûÖš«¬êùÛÆÄò뻯„º+ÛêÙ˜"èzl;{zØQvkñί ®Ò[I¶Å²Ú*V•Çw¿¾u(Ã+„Ê‹8ƒ&þzCWGk¸áÍêkØÒëœQ†gzª˜vyá¸#ÏPÜÉ]B—yæv¶òt3´ö±vÃÒ¾g$Rÿç—[i¦¨XÕu¬ôèöQÓqBkX%ÄOèiàïdw¶?Õm«)¤¼WéOX©X•obmÎÆñ»’ºíé˜ìÏ„û:aÝ•uð]9¯VF!¥^²{ë¼BU½~ɯkc>|Ck7‹×•@7<è2·3óÿž9`ZV!éf®i0ÓUÅDN-z~eK#Ë›7‰Iªò›ä¿OÒRQÈÞáŸvPHE‡ë5³T1Þך¶l¨ñån°’¸§8Ûñ:èRZŒ;³·…„Jƒ§´{WH¡G>jdªb:Å:ÖÏšÆ/±Éª¡j@¸ÿJ«ü@=ȹqÀ—®÷ iîŒ7ŒUì6·üuÊš´ez¤²úy±vü «Ù_sâé«4€åØ&’ˬ–ùÏ T¬÷X·ƒ-}âì î+Éɸq”D^'…nóÄûºoÜ'økRÄ»Ó :«˜ÖÚÀžþx\]r²BI;B‚Z.â'tþŠúßÖò¦.‹ÕµWÒ“;Ó ¤TŒ÷ÔTÏk«ò¡vü «?ØõOÚ´£ÕÇ›ÐçYÞ’Y*vejTUqŽÕugÁ$Î0U¸ÿ s»™e •‰÷\«b‡·nY×Ή¸§u§1n(‚ïÿ˜/Üwe¨“›ç%œ¤äbƒ%}ð=’ÏŽšÓΩX•/?TÒaî±s²pßA·@»P–jöýðjoA!UZXœ ŒU1Ï/×v<Ú#ÖY”Ûj_}§Jᾃ®xÓKE2Ú¨9Öt\n!=ˆNúù KŪüᯎ6í£¤Ž—¦NÁßbèxŸ¨ ĹÑ¿[HCœwZÞU±ÐÜ‚¿mõz™Q´ùœŽÂ}ïëp‰²¹ò÷aŘæOãT,ûGrþ¹ÅÖÕë²^ëTÜ|ÁŸ§ ºÝ9‡èÚݵøN/ÜwÛú·Ã”YÅÊö<šõZLŽ+“{J”Õ>dÚqƒnúoy{?ÒZí4³Ú_øn=mªR& ù@I´ÆÉÖü¸Aǹ‰”“ÏfÎ ¤ÆÄ…/_¤bû»Œ8½@L½ÆŽí2ÊMYíß­¿§¥"/ ÎØW!øAR#í­Ši|Íâ×±"­ý£’8·/÷:BÝÝ“+Ë'M®B!\ZG¼u½ãÚ;­TŒ÷£´¦×’/×[ýãý*‚nX=OïD]¦æÍÛo»XHî#Wœí^SÅNêqFÅ64"»­þ†ÅJŠÌã  q:~w•‚îpF.…Äeù®2fíøÛRïgå’JúÞãÉån¼Nú´ª~TÑÔ)áÃRÏ>êÜŠ«ÌdWï¡›dv´Iktª¤sauvå%ð:tYg>÷J±½F¯[o¿»׈–•÷~\eU~W—í>v~¾’øý)üõ¢†NõL'®Ã˜PªtO]´¦”œc¦ŠöÒYÑz€=ñ¾–JŠð42t“ u t©šÅ9MÂ(ï 7Ñ.¤‡ {&F5T1Ñ·ûŽ¿fGü|^AçWqNRÂ|ýY©ˆ«–õŸ‡QÅÅ«æ…4?Ñ˹º3w5ž}ÕVðUP;í¡n®SûíËX¸/p]w|*VCŪ|s¯M{qr¹‚z7*áž0~н9{waŸòZü¥»jD!u:¼ÆêOÖUÆû…[Q4gÏG Z䛯×A?èêJ{¯x`I‘Ÿ$Šž÷’Î:;v^e!ò¸!,Éùô“•sú)Ho4çØ.Œt¼¯v…6ÿâz\ZqaâôNWYå“_<º,ö?)þ!É 1ñÁ™iu£i[LüŽ5 iÏ‘î6zaWE3æ-#¾¾VP'.,µòt•VÆïºMÜjü·ôÕóÉy›9WØ6Î5×’?*úä€Ï×òjhd¯NBÞƒN§½^—+g®Ó®©ÜŽÌo†rÐWØÆŸé‡±þô`ü"ñ¾ŸBÝùqz·r{ƒ¸§ºmã h|¯ˆUû_ ^|«,ýf´:8MANß_K_íçÇÝ:Î%³Ùà›4Z}´ueÉ¢ûù•_fšq;¸ìHõd>"¬‚¬on’¾½ÍëDбI=§_þz“b¸4w¤€,W‡•øq™ýöË ¼cOŽ5'õµ.&­}WWaÞ]ÔÉïMoÝ¢±¯Úõ«·±€®Nmþ¨¬ùvul§ïoÚ¿nLÏ2gŸqâ'tç ?\¸1†~på€eÍïî@VÛ+ÌåÄíÉÏÚÒ½èáÌêb0-|?ù™ u t3Ú7™^‹b©Ë‹œÞß'М& —¼©{…ñÏ#mèû¶\ŸngƒÿXáþƒ®ëãMÛv~‰ü® hèé®í/ea~säi÷c±bâ\ÆcöS÷I¶æIí…û‡þ.>áŒf^ÙîbŒß­Ê×i—™t»h×2:öB·Ä>¸ÚT;~奢 nÛÊ05Å:r”O†{ö)Ýê\f|\\BqæÔe“ƒéã3÷Ë_TÂýÝeLJ+÷{©É€süÍ'ÇQ5äc¤!Œ÷ã\LׇÍ?øqx0•ì_»Ãx¡nNïEÔÕZÏÕ¤;æô‚óÊ|º´cÃÖzŸ•Œ÷Ë\LV³GýLÏ'Ç|k,Ì [-ñy³™â(|ñçë÷÷çSÝGº+ÙŒß=·}õZBÇý’Îç)~^>_hÔ±8J yøx$ŸSÈ‘n• ¶ì»¯ÿ $ Š”fm¬\íçª7è$Z¾|GoìŠ'æÓ¨ñ·l³+ØÂœÏów[R­—òo½ƒéÝêÍá&6¸Aǯ‹ÅçúgiOßî-ôYgª`VíööìeEÚíí‚©WVÏÝ_ðë%Ð>J/žÜ ^ûžG7­'{lÒS0­í¥5òó-/ÛZÁÔ±S|¥ã~ýCçE©hº6 ÅÓ¯VƒÕÍÍ£©÷}Ìú§ôÑÓˆ úôî\!§5Q¯zÕÖ[ k¹t\Nó> äç|·føå_¯ õ&tþí¾l²>›HßöÄ;¼*É¥n^}ÊtTrÆïw´  gÌD»´6ÙB½ Ý€Ví?Ž|’H#¬â% #sÉkvs‹/Cåìümn‚·„FOë1úB~9›>°y1?O—¥¨„¸Zy^ÈMm»?—tµ‚X›~¢"§¼Å´"ñhqzyÐ?̇  Ó­«7ö˜$‰ò—÷.^–K_¦wnµd`ëÁÙ‡ž^JÍRnDª+§ªýÚñƒ“ADÈ$êÑõçÙÀ\ú:Í)Ô;-q.Ûš«–t÷Õì'«{Ê…} BÝ ÝÅ3’E]ž&Q±ß9Ub­\*Mî÷ìùž@¶(óa[×ÖÄË©Oñ2‡ ¿…õ2èâþ, µé–L¼oß=2¸ß5±Â%Í,b¡Ùþ¶”Þ³Ïe-yµ¯¥vü 3ÿóÜØry2}ˆp8Q[~mùâÈV÷OZ`÷ÈŽ¾ëþ|üœ‘å»ß˜qëÐiÿz2™¢tDep¶rδßȸ§Âç2íès3ncZ Æ9¤ yºÅÎFmî&“{}5L»GáãçFodUu-ïÿD˜ 3½(Œß«RÑ£K¿7BÞ­MìÒþÍÛïÐnù’@VúÈÁÂDÏššj CƒèðPnÇ0~ÐÙ?¸'ú8"…~Æ÷™zîU5þr;}e‹@vëeÞ½,i gGÞ1ˆf‡æ>=&ƺ)O W¤û~sèw?‹kí˜îÕe÷ÝÍ—Píò¯¿N· ¢z¾^#îu t“nœŸç}>…>„¶®Üãp¤oëÆï¿YHš7¾³6 "™zÔ·O|$…Ž÷SO¡OÅ—ûÞ]œCÓç÷óróg§î\/úœ=Ÿ6ιiY§26ç|*¬‘/ÄOèŒÏ湚ýH!ícƒÞ9ôô\}ùí˜KŒ>4ŸVÎ_qñij@ªº´ã]G®MziH^¿eS»®Ëºf4¹$ìO›O[®v1é_HUqJ;~н}ݧiö< ½® W[“M3£öåx‘9l¶´Pd/$Z´ô{HcÎèÆët^—Šæù´é¾ICo»EŸÈ¦è.ÉÎËs/~´‹ÉíK½4×4¤Ù°Ñ1s…ù:tQãÞD­»¨¡ŠYcÇDÚeÓöíê¬_xEʾ&ëéX]QRÒœëû–ð×µºwæ.G÷'k¨¬÷ =›Æ¯Õ7™ôZ&ì?^NYQu¦¯­D¶G|ÂTAüs1tœ«û‰—z6–›!eÓ‰°õ“——±£É’Ž fŠéÇ‘Cõ–âóyn+ȾÌ?—’BôlÕÎ MR©ÜQmð"ç.¹K¤qݧ˄çËb*ù˜ ûøn íxþ0¡GŠðœºÌaÃ{öM%ý-u“/ޥϳVÚב±ð烆Ö(¦öÚµ@:ºê눱Køü †®k»âq¯g¤R;ùÁmçWÞ%Ã!SûöZžÕ™ÝeÑËÅbÒ[·Ï¬ˆ@R¿>7ºï=!ÿA÷ædNN©Ô!§Å Ñ] ~á¸îhæ9öÓu¶wÁÛå´¨ŽÓ@¿À¨“tÞ”ŠFê•Év¥R§ß§Bæ4½Kï÷.}Rë;·åé¸û-¨r·#:üâwÚçÇóvèîpQÏ¥R³„&6Ù²èfc'Ñâ,ã¯×%´?e¦6Tµ®¨?èö/å6´¥R‹·Ç7(¯dQñ…6þw~f¹Ç|fæ4ÿñ ûÆtæÁ;³–åBÝÝÄ–G‡«ÓS©UÛŽÞ»¶d‘MaÏ¢öù§ï뾈V_3ÛÛ*JÍXÃ¥aüyJ¡û“n€3Æa¢à²YtÐbû ü°“ÌPÙõíüÅôköï·‡þcþƒîà‡ôÛV_SiÊ¢oG¶Ï"«±MýÇ©ýؤüÝÏÌ^FûÕa„ï¥jýL;~Ð +;ß6±^©ý¸Ñ½;ä\t ᓾ«ÝðlEüó·@ ˜þçÈü@á9-t%¹« õÓèÄ’wÃﯼC[ï¹éæú2¾®±%Ó¸yšã)°Áäkkòã®ó¶TôÔ7NßÇ(>K7hŽ5½CfÍn²à¹/«š¯MmÖ%u eÔN¸¾§-¯3€®žáðý!CÓˆ[e˜|9“ÞêÄ´êïWý¼ü¤Ô²ÛT½@ÒN[ ñº^á!?RƧÑï¾'º~›–I;ú¼,vÜìÇÕæžø:¸ÌnfߊÚ:"lÙ…¦üý.†.þa¢÷™iTs§ÿley†ðœÚU­·oþÕü{V=*Nk~ÖNˆŸÐEšïÛ°0MXŸÊ ³á­dÍq¼¥§–v I¶¦kÜãªó„béôþù¼ºÙµWvš%N£§ß5È ¿Šãûóëú1­=ð<1µNÈ­¡·>@ð½òtâÛ}ò‹ìÓˆ{{`ôÍtšl¡Ó.`º/3Ñ>˜² Å{/)×@·¡ó”Õ¶O…üÝÊ#Ï;º¦Ñ=é¸ì é®=}Ÿ`ü¾Þ%¤† ø‡uw¥¢ùÉìN´{)&¬ØÓ¾â69ìÝzØòãL»­|Ðj¥5 '/¹¨Âý݃^Cþ¬N#Ãaó÷ì¿M·‹¸ ß17w™úv£¥$éÆ9ëPÕ  ¦o3ï»¶êüüðï~MuפQ~Àär‡—©TÜrÒïÙá>Õ뿉KŸ?êÿ#€øùoÕÐ]=1 %u¥ 9eqp{*ùf¬Ÿž±Á‡ÙøµO}lCW—r;ÙißÛÅù’Âþ$èvÔZrcÆÍ›{LÓ*•Ú§»Ï¼½Ø‡N¼sý¢- žÇ9õÒö«åyÎ…ý-ïÑçk|ÆuÂû&k(hzD‘S}Ø»š=ê´VÛRšÛÝÔ¹¯ˆ_×à÷@'^[fê€ë’s›^#ÒÐÚ“–é óa¯†ÉåͲ%½#uf”äPÙ£¯nË…yt]Û›¶9û üYŠå’{)´·÷ä Êú≯ßË£¬lèýõ¯YfÍsrÛ§™ÂsZèöU Ø‘Œû®sóدÃ%)4©Ã…b¼Y‹m-×>ëdM†®Üèêú(à^Ra¾Ý»¤6oÞã>ß?&üp«ŸÉ´Ønú“¼Ù¦K ýbóÅÄ­f}Š ÓvòZŸâ&t™Ã/nÊC\ù,Qôüx8™L™ºµ˜z˜íjÄ9¡[ óÕjV:ôêËþúRC·­õtŸÍ=ÒˆsO?bL{ç={Öþî!¦µ¿œ~Ûlæ@ç7— Ïi¡Ó>AÜÔn#O¢»Î¿Æö3;ȶ‹8Ãûåä:ÑádÀ?<¯Óy_*ê£5èM#é§™§êNJ¢ôMgÛÕ;ÀVý6lÙµDLFÚÂ$€~¿_1è±0_‡n;w{J¥W¹‰ä<µbÏýL6»ljöPZröÌ…èÒ²<±Î©ÿp!nBǯۧ ëʉdö(ÿy³ýŒß×jOW¤ãÇl©H 9;{a½ºwµ¼ï'¦¦RéûFí¾'Ðñ+ïïgmG®3Û÷JB^^ó} ÞÛáç‹Rè”Ïý¿ºšJo¦ìרö'ÐPíÄúãV£íœ(¤Ý®ëÅ·¨Î««sìùëLÝ .-ù¥’ÖV½}]™¤j=qê!æÙd{·íué;·Ýñèö׃%µ…ý-Ðñû0S©±|ø¥gÊxšQ}Gñ/†Ú¥ôR’cõ¸×«hmjöSÈ{ÐEü©‘§X–J9ýF%R<¥d§éo<Ì\¯œzî¹ÙŒoÜaÞñÉé¯? uˇRÑ‹m×[÷¥Ò’ÓÉ_–dÅÑ‹î:þEQ‡«ßÃhz¯×Ứj¿§vü «p9üнs*ù æ"R-k˜øÓpùaöÈeµÍÆxkÚ"’ vœ@[,Ú÷Nþ&ì“€®¥!÷—†jYÞ½œÿIMf¶ 9êÅru͈YaEµÞnø>'€?ö³›]S˜·C7ü‘'"ކVh "5­rá÷ñà!válN߬·bšë÷Õ$çÉŸ0ïƒ.{ˆvFIÚ圶jª9v_Û>›²FÏÛÙKF[ÑWn`lÝiö­@^'ƒ.uÉ„àO õ=ãÿÍf Ãõ-Ÿ·áÄÖà~YÅÔžÖ´]ëø@üû9ü÷¢†Žß÷«¡š˜=uÞK«‹b§üú¼Ÿ)—dÏiboCñÜ 4Tµ];~ÐÙsË;Æz¡gÒ\¢Š¡ Ëît¹qmõûÚÇNÍié«-U|¼ÖùX*JÈsyÚãO ­Ñ¾˜t‹j, ^Ø*kõ{s;~qFáôzß•†d"ÄOè’j|úñ󩥆 ï7×½E~»×Í `¸8ôŸžt ÆQ›®Ï¤']ÒÚ„óñZ¿1…ÆMtºŸ4ä& Kò|ñüöåUÂÆK %ÕõÜTîqB¿ÿQ ]´÷÷nv)4¢Wú¦3Ž7èh£wF»²ZÝO‰šîÒ×_ÛÒïÓmzÆÞçuRèê$¸¾_ å>wêóÅ÷:ì®× `ÿ!VõœÒ„ “Kék­Nú¢Ja.t®I¿C7}H¦ŒtîO4…ö4l™bèÅ6Ø9è¡}Õ²Î÷‰4¾÷f.ÿ½¨¡›e­g>"<™r=¸|¢ÉÔúèäÖ½Xì-å÷ìˆ[}uH؆•O”ü8”@×ÿO—__Ö&÷¶ññ QtÌ%ëƒïJ/Vµ?åéIß„·£ið!Ã66baü>•ŠëÎíµnH2i—I·ER—”M›5÷bç_ôHôËšîõþ>a0ÆáWË”í‹nó:è®=”· |—Dáeïê#(ºˆÄ–b~súF…·³&~üÉ„Y]×@ØÝöp.P$QÆ¢Ô”·5#è¶üФþGV¯<Ž>xº$³^–5Jæ}ÐuûºfmŠu]Ä%òpý%Ì|Ù¦›qeSŸæ6t@aùb³G qOSN6öwBÇïÓO¢|î2=F}5%«êz`;í’n®oG?÷µ(;HÛâ /Û â'tú×fë$i©ÙŠAŠF¥¡taMÁà‘yØnZr×gBîss©å†#k2Ê„ø Ýl ¯Ei†÷„0”Ú7;¸szÊAÆ×•Nt)óÁ„ø¯tøOÍqÒFÂøAg¾æÕÐ1F‰4³`ë ä½×hTké«b/–¸Þ:ïÀ²˜ÀÓúòŠvÝ•¿^t>—ж˜»Ýk“@ÁÜ‹Ø*²èºjÁÞ,cm;Çà[®$mÌm” $LJ2‡¦ ûs¡»4ú㪂- Ô:66/`²Šjè:ægŽóaWn4É=fëJcÓ—Gž ¤ NŸœ¤ÙÂý(¦íPË T¯·Sø* ›ÒèýïþGØ—nܼ3¹zOœH¹í…ú¼N J±|kP*ò ÷¸Dÿ ­²ï ¹„5¸utöaWGáy õ_}£¯á0a'tclÓöÊœâɱdÊÐ×_Cèv·Ô˜#²“ÜkrmèЄ_ý]2ð}¶á*W^'ƒŽÛµq²a<í î·Ö;_)¼Çr”å?jŽgG çÖU:‡b^;8¾h§ž;UùiÿíùŸÙÿh›ÎßHj¿=þUz q$Â5Á}oÜï6R *€Ñ?ù×rA˸9(úbfÀ¨A%0FP“(zp¦@ ¢A0BÀ?tüDÀ¨@90øô¯urPôÔÍ€'PƒJ`Œ /2PôðMDƒ `„ ~ èr}€Çñ°5øÿÀ‡ä_Í«í²­·àCÂy¶ý»ûüwûBþ­þÖFk£ÝÚèB]ÄÅwa¼¹ï„ÓšO •ÀøŸüku°DÀ¨@90@3Þ@#3àä è#¸™O •ÀÁNd èá„LDƒ `Tÿ?ÏÃÖ¨@90@@7Þ@#wàä è#Ø›O •ÀÁ_d è!˜þ“‡­Ñÿ~#ÿjþlÿS=l«üÙtþxTõäêš¿õÑßúHªó·>ú[ý]7úRqqÂCSîssÿf¼FP&ÿä_«‡€e ¤ T#01ðY@ÁL<€ ”7sà 4B 3î@J€>ŸðjP©ûŸé_+Ñ ! ‹Èº4ð*P ìÍ7Ð߸9(úHfÿä_küÙ_Dmð¯åÅö?Õ»öŸ½ØþݽEªê#\::Ÿ­ý­¤:×þÖHÿ5w¯K…1ã>—þ›ø,î\¤DÿäY« e<Tc1  ‡€f ¤ T#81ðY@ÁN<€ ”?sà 4B üOô¬õjP ŒÔ%@ €¼)‚hPŒðÅÀd]ð*P ÌÿɳÖäÿ²ÇÈ¿¢ÛÿTÏÚö`ûw÷©ª“¸úâoô·N’êü­“þÖIÿu’‰p½©…ó6Æ“(àŽ… eúO^µZæÀh„fÜ”}43à Ô #ÀI€ =;S Ñ !ø‰Èºÿ¡^µÞ@#tàä è#À›O •À_d è!ø›)ˆÀÉ@üO^µœ›Pr`€Da¼FH&ÀÈA ÐG1ž@ *±Žoð¯éÅö?Õ«öŸ½ØÌ€'PƒJ`Œ„,2PôœMDƒ `„ ~ è"q‹€Pr`€Dn¼FHê&ÀÈA ÐG’7ž@ *1’¾È@ÐC` ¤ T#œÃ™ß©“D:Ÿ·ý­“þç×Ik¤ÉT¸–4Ây™w %ÜïB€2û'¯Z#,1ðY@ÁK<€ ”3sà 4B`3î@J€>ðjP Œø$@ €Þ¨W­Èºæ"àT  ¸›o ½ prPôøÍ€'PƒJ`ŒD ù/^µzH ¦@ ¢A0B’?t‘0DÀ¨@90@1Þ@#$àd è!¹˜)ˆÀÉF ü@ÐEâ åÀ‰Èx”L€;ƒ $e<Tc$-  ‡fú?Ø«V ¢A90@â5Þ@#$aàä è#)›O •ÀIZd è!a›)ˆÀ \ ü@ÐE2 åÀÉÝxèM€;ƒ Äo<TcܛԲÿRqy½Ëÿƒúèomôÿmmô·.ú»~ôŸV™ × ÷rÇ åÜÏ"8™o ÆT @AËHA4¨Fbbಀ.šx(pæÀh„`gÜ”}?3à Ô #J€ =FS Ñ ¡?t8EÀ¨@90@ 5Þ@#Uàä è#ÈšO •ÀAWd è!›)ˆÀY ü@ÐEp åÀÁÚx¸M€ =qS Ñ !¨‹Èºð"àT  à›o ‚¿ prPô‘ Ì€'PƒJ`Œä 2Pô(LDƒ `„Ä!~ èàøÀÈA ÐGR1ž@ *1’ŒÈ@ÐCÂ1R *€ø, ‹d$@Ê’“9ð!Q™w %@‰Ë HA4¨FHdbಀ.’šx(HræÀh„„gÜ”}$@3à Ô #!J€ =$GS Ñ !YŠÈºHœ"àT  ‘šo ’ª prPô‘dÍ€'Pƒ `„¤+~ è"‹€Pr`€„l¼FHÎ&ÀÈA ÐG²6ž@ *1’·È@ÐC"7R *€»ø, ‹$/@Ê’¾9ð¡0\çù©‹Ä:ÿçÏæ4ÿ¤Ïú§¿ÿïþÜâ»3è¨?—Š®uü}*$Ž6µ¼2åñÆc4p¼GøÊDEõßwJæ^Õ™~TðE3§˜_œq¡-ý ~Ÿ=ÓڌƼ?ÓÏ 3§Å‘OÏ©ù3bƒIb4uPÝGÙÅ&}|ð±%Þo.x¡Ä—R‘qŃMÑejúШÝ÷žÑr*Ÿq~q·£ìØ—º–~3ìèÜ›»íßv ú‡~Ð…<9”Þm›š¸nX¥Ù™Þh8â(û5H¿[óÝöĹJŸ^D|?X¡tßÇ-ó7k­þ_ý3¤÷kén<Ê>Ï0ðÄ Z!ÊÃ#ˆêNBŸÚ ª0kYÞbƒàÏ_ëÏSߪbˆ÷3ó§¸TŸ^G|aœiYÀÍW.š J*½¸dbKÞP ] ­î-š,q¯ºD+Þ~S6kx‚5¼g]aüÆ™æõH{873ˆé R¦òºè^œmýüÛMšÕ(55ñòE7 }Òàá óH’Ò˜9ïÛD}o|>Õ©%ïs©óµTt¥ßh|Å7ÉcòÒã¿\ î¯¥Þw}YôeîœiÖõø›‰AtzÝÀŒ5Wùã@×k¹ë¼Ñ7¨qüƒ·OÇ^ ö·ëxt0ó«öÕ™«5”"»€©½Ê¿ðß‹ºbÃ}>.¼N‹÷—õÙ~LFœë£M†3×6¦”¶qx×xtŸà¯ÝÅÈÆ— <¢É4 à®É½ó´³‰åí¬Ž'«ûx¸Ý5háé J7ääûHaü Ó¶‹;E¸XúžóåØaü ãûãFЕþAµ»Ÿ¡fÖ·ìØx²úºÖÚ'ü¢Ô«­wîÆº‘ãNwû"\è§ušÞÄÆ´¼~²ºOЩe_v7” þ¡¼N§²Tô¶~ó¢âZáT®^µ«QÙIZ`µe¦Á°Sìåb½´&Nä1ËÂk‘œN½LýyÉÿ^  3×ÞxadЫkBï‡~ôÓã\ÿq?O1½ºç65õu¡¹ÚK{ÊÉR4àÁucaü k¤mèJc>Ô«u'Æ—ÎË›/©w†ÝkÜÒ£çù4ow‡ÊÉì°k[÷ÂøAw÷¶Ñ‰¥K®ÑÙü‚Ѝ”'ûûÏÔ³Ì?åë÷07J^ôÀïÎsÂ܆=Ž þ˜ÐÊ9è(—:| 9Ùi ùÏ'…nËRÕü6IÛ¦ëÞ*ïºf_ là¤ëC¶>¶#¾ŸšœžyߟZ&Œtõ6µ˜°jÐz~èhü­”}tùFXû®-.°*ÿ—ç\ûÞÁä{Øßïaü ³›Ãu=Oý}f–¼ÛK"öîÑ-“ ,<õñÑYÝ%T¼qfÓÓ‚‰Ë–úµyëè87ã—N“¶—þ²¨0ïZ×ýã\ÏNp$éÄFÖÁÄûÜò:異×ûÛ>~3ΜÏFw¸*ÚE®ÆÞ¾ÀpvŸíè×îN7®&Þ'Dðg‡n³4Ùý’cdRX÷­èì¡ÏÕE¶àÒ¢ZÇö8Q‰ çØLÇw¬àxPðg‡îÔ¸Íâ”m>´íÁÖo¶IR²Øl7ÂvùÅêþPM>ßT ]Lœ»ß韂?;t¼ßÜA¡oØV:4]…\dU~~E1zN &¾¿ï .…n¦¶áøn:¦B%²‘ºgx×úÍEö¾p$® %»¤*t‡ ~7‚?;tµ/«½s/o¥¾¨*ÆE­§~;–8äë_b©ë›9Д+§ç.ïL•Ÿ[{ˆ×©¡ãý1V’¶}qáêR±nuPïK,fáøõSÏØÑή¡K0›Íaü ãýÎæQú‰ýîGWÒsîíC /1[ól©MÓw#T‚i^ú¸¬» aü~”Š:ôZÚýÍdK¦µ½‚.~ß|¬Ù%Vl9æíÒË64ìyÉɬîÁ¤ ‡zü÷bÝÝ•ƒæìZêúŠ[Môñ_òý"㺯¯€Ž~g_Lžg{­YnÃëDÐýnªZøæ””­áìülÈWr¿i<¾Ï«ïz¿7²£W¥Þ­° ¦ÀëvåìüÙ¡_.›nòi{Ù뼺œÖúŒ_\‰ãŸ7áOmjWä<îÝ¡`ÁïF?èjpv›m1Þ×p!õ\á¾%ÌèãÇÍ‘<{ÿlû0$˜r*w/:2š×É +áì¸Ïù>Cfô¾Ë‡ üõ—XU_Ãî ýÔ—oÓžÚû»5áujè´_×µcŒ÷%š@V2½ÀŒâKl`æ£]ãk»"zåo׃©È)¯í£6¼®:Îýèh‚/+5©pº>x0ÅLˆl­(VÂu8 &¾^Æ ºÙæOjØÍ úeÞ‹ÍYõC4ÓÓŸ]i"V.²"Î¥­eMœey'¿¯0^Ðý,q²‹b±þ—LY0˜.û´xðã5¿¦uóþeVô­yo“[ êñºhêÄJa¼~•ŠÒ~5™´}l0óvžd²mù«ƒ²Nþ¬¯•ëÁ³ÖToóVý.£ÔLÓÃk´Þ~¼ k`ú£³ø²‚ñ~£s¨õÞÁìT-VÒ‡+hm©µúØöZKtúÏ" 4¯A•“ç)¨½áª=qýŸ&?.U÷qó:ÜÚ¡Ñjé‹;Ÿ½ñ׉¡³± }/>p™™´ÈÙmþËŠÒ5NµûâxµWíëÖt¢=íM’;¿Ù¢ SéK_Dð:)tþÁçÒéûVåÿ¥µhçÏfʺsï•=‰¦sËÐäþð:tòv1ídCTŒï+íJº<šöî_]ßÖP_s¸c§ Ù\›|5¯SC·µ¤÷lƒ×*ƹŸ¨¹Šêç¶™÷}ž?ë mÄoOsö¶ZñÍTAÛŠ_d¶¸ÃëJ [è·Svéó‰|ß³wûuts±ÑØRöÍþtûœöôÄ"¡Ñ¡ž 2Þ9°ë ¯Óù]*²­=ßdÇüPæÖרQÚtâý´72WV忸.!rÒÉÆ Á¯W¸ß ©5êcoO”I—ÍÜJ_\×'·ñgMìäûƒ?ÙÐÉ{Yuŵ‚¿ƒp¿A×Ì{À… aL[>œÞNªïš¹Þý™©ÙOýÕùÖÄn…àÏ!ÜoÐåy=é°vM8;‘µ¬¹Ã¤]4´aø(}¸!¥×HkúÒ+ÅvåŸ`Á_V¸ï Ó¶…ìÁô#~,¾ÛC+ºÍ·œñâ[Ó­oï'k¬éÚþáƒÿ &ÛãÙƒcy ºô ɱ¶¥lezhÏíû(L'ÊdMâ%vï×߆¦{73{¬« À­œÁ.¯SCwÄcÃÈEÇ"™Y®R9HÓžýê^s‰uú±¸ï¤˜?û}¶6Â8p³Òæ_y] t¡gß%ObœkPÙ‡ÃtvæÅëÁ.±Î-ze<[)¡Ó/ºC'üÓøý)yœ¹ØíúÏ(ÆuAnåC c;»fwógâ+“fE:‘óÇ?ŠnSÔù¼÷Ù¨kÂý÷§ª¯h4+ú¦]h öVŸ¶ïógif­æ½èé"ÌTsÚ„Cã÷t-¬rúØ]ÖŽÓ+¹Óù&5òXÇÙ…†ÜWtmª ÕÙ=N¸ÿ ²gfD7Xu®nQÝ}éuÇîß%ìø¶~W-Üœ‰÷¬ò¥î?èÌZÛþÞ ¦µãh}’¦8J;<Œ ¨öÓ|ö”36&Å–‘› ÷tœ qãÝ7™UâØ-»¦¢aw”¼`­[­™ºÐÞ^íôõr^0Uú·¨xJ¸ÿ ;>µ±«Î°[¬¬•ç¹cSÏR¿\ñZªÈTO¿rlGk–UÆ}»‡º±N‘gÏ›Âý]ÄŠ÷ÝÞÜbc_^]dî~ž~[óPïSËž?ûYç-6ÔO\3êí³`âÜ×­· ã§S&Úè:l¡õ¹Æû$ÊÈù]åéÏ™Ì?¾`¼÷Nkº5F·Æ·æ âûB ÷tsûï² –=µnRZy~]|©ÓÅöíó•»5¹ï¼Õiâ¼ÖA¸ÿ ûXRb]Æ8pÞlx‰ØAý¾<˜¶þkZ_xuTñÍhk" è#ÜÐ [”±zb,ü‡ýéì±£§ö. `C-{t¾ÙЖzXhºEû(H%Úñ$E¸ÿ ûÝ[½þôpµà³@î ú´š??€ Ó }y÷=}ñyÝ>W¥™0~ÐY)NF´ôV³À{=JõSiÁÁYï[X°ª>á76¢¤V÷Ìݶ&mäǺ÷·æ»¤¿R³Å_ï.דÓèÐZ•ÑÒ¶}yôj+ ~Ø® wœ‚87Ž y] tóF÷WLšÇ.48uÂ<˜ïŒ q¸ÀŠ–‰GI(:­çÈI»9»®ã¼N§F™(òf½òØ3qLÈg4íc×Áªû7Ó6œWܽvpËQ¼Îº¶£8ú8ÆûO…ÐÇn‡ÔM™¶­k] EÞZ㨠~ÞÁëDÐuy9V'xf<›*¹}½å¢Ë$ÔïÈø|î@º#—äñRÐä“¥j„ûºFÚ°ñìžÝÀkߢ¯ýà½þ­§²E4,õp¥ ŸÿÐË]As¦¯î³+W?èVÌà*ŠvõÚôùnUôÌÚÁn!te_‡ý*s¾œX«¢>õO•<Èê^Þ;À†|nœÝòtœ‚êû'^yóF˜/@W¸q{zh«˜ÜÜÎí¸|êàúì—¥£æ4·¦-c,Ã,†)¨ÕDZ¡~nôwKD¼D–ܪ͔§†¡tbûòýî0š;qÏ‚[V4¦ÇóÄN ²sëõü@ŒP¿Ô,Û0âu¬M";S´k“ÝÃPŠþ¼ïÃÍólÒ®m͆Ø[S¿£=Æ"ý0s›pP˜/@hä›<àV"ë5þåº/ÇÃèd÷QE¦[Øc¬öÕ³¥íäsÃê)ß a¾vÚÙ,‰éÝiŽÔN7M/ôݼ%€ñýŠíiÔüyGÖš(È^k€"Ì÷ +MxmWä˜Äxß®šmt~ñԩÝv¬t!Öƒôhؼ.ö óèøõÂ$–Ñ%x÷‘ÔZòÒit׊Á¿Î‰–iS+¨5ªŠi…üݘ>ŽIÃ[&3§Ù©#®í¤ûï_\93= Rd‰úb¾ ]RP¼+·Ò*ä?èÚh˜’Ùœï Ù¤(Ú¨20jÈn_Ûæ;Ñú/Çë½è¥ þ¾òtZWèØdfÂÙ°ÖŠ¦=–²%ƒËÙÝNü&HȳqëYÙÈÓnS8‡kaüj•‰VjìSسeœSJ4é8úOQ;ˆ}9թ屋vT6€3Rþ‡ïź{iÃP»–ñ²ãн{®SÌÈéëÏÌ]5|`÷ž64ªm‡W3ƒ…ù£0߃®•6‘¦0mZ™~ƒtëå¥G$2óµ ¾hEÝm"¾mË &×Í ë-Æ:‡ü˜Vûêk˜òv¯ëß$Ñëý›x2ÞÿWL‹W8íþ6˜Â®qF˜üú‡ºÜ¥µ–ö0×0­Í@ÆMj|qÉ‹%¬¾vAÌ’Ús¶­-T§mV^¼N]Ï–]Kô6‡[Æ>r‹$~CmL `¼ï˜¸ÚÇÓhJ‹âd~F ¤ÎžŸ5B|‰¡ ¼k:ÛØÓל1¨](\ë6ÉZA].¢ ëeР¬¡ÅÄT6îÖKUûX’}ø¶JePíÓÇ­Ö¾]« 1½¿ólÀŸ§Ní2‘þ¦˜Ê¤²?ë'î~ù,–¸nÙs Øä É—æÇÛç¶ô^ª >Nñ:èsvQ÷SÙ×ÛœQ£ìkkÝÑ`ß·h<·Â–H*~rX!ø ÷tÍ¿ŸÜXÜ5I?>I\4JMÞ=Ú:´¨î§þ53ˆVÐZƒWÁ¹‹„ù:t(Zê½vJcù®f?.ŸUSÔ‘x«&=‘§5´§ÇïÚ„õP*þaÝD ìqF»[WÓØÒMçãkÅQljɿ `¼ŸŽµÒ¼î»1HA×7:ü«¯°Þ]VÃõý~Jc‹zølñ¶‹#Yí׋“G0[­á·íçÚç#oʆpÂztÙñv­†ÝfEƒJúö¸G®gSMëáxU¾ï8Û _q.—mkðºè’4GFœX›ÝÐSR¼q¼àø©L?–÷ÈšºNlsPA¹éo–¿֫딉žöÙ~¬ â6#Î&ôD<ý2uï“þLµfìèéÞVd6ܬäìf-röl#/a½ºÍ}š·¸Wq›M?¸qПxÚâÖ±Ÿ~ ?›zîÕwo1,<² âÙñh¯•IIüst-ú\ò¹Ó'ñþ½ $ù•=e÷Bf÷KZ¯.®ëÃN­A\ŠÑOÛ·®•°^ Ýì%Ñ!ñöéìêFõhÛ´â}Bü™¶û 1}.¬‘ßj°‚Úlu»6DxÞ’é*ãÓéìÖ+ N$É.×u.±¨[=±"ÎER­£|c…çEÐYܶ59w'i,ÚÎo&‘¹NKyþã}ôl© ¯ï¡ÌoÁdèþjŹ—üó05tîXáRÌ`ò‘eöÑ5’H¼èhPHà%Vå¸h]\É©Q Z§5`ün ûå?¨t`£5ÞIªž? Ùqèգώ´,éÉðÝǺ'ÿ}êÔ-­jý%<Á2CðÛJ¢±-Ì|7ažªÖ†:—õòo)h“çäõz£„õjè,»‡?µØŸÁx_×dzZ;vÊE–}xϼgÏœ‰ûvKp}þ×õqtZ›Åklò‰ôk·”ÉÔöÈÑ}Kë°‹¾Üƒ:'2{4(è6â‹«{×õjè&O:3+47ƒÍ«·aŒ_Ë:]QÃýâ äéÆœƒ¥„|¬¹¦ø‡ç>RèºjR3XyÑŽ€ÒRÿJ;fàÀzÕ5²½«=½ø8g²Æ÷kƺ#£\ºn“É ¤º+Ÿ¾N¡’ÉnËò˜Þmû&SûÙÒ¤©¦þ®?ÿiü [~3ü±jH&ûí1ËBCö?^¹ß™À†»,=Vh`CvóÛ&(hBÚlLYx„è.äŸO|>+“8´¡ãJ†~]žwúmŸ€j?ÜÊx[…x¾‚D3âwÞ!øœÖÃuæÝïv¾]&“'•XÝžJ^OânÖhÀøçX6´î÷›PËõ jiÐÀ~Fœà—Ý`m•ɺd&ÔŸ”Jí8£.æ¹{IέR[Ú¿ðmûÝ Z±[Õnº¹à—½Só[]e²dGn¥1&7»ìnX#€ñë·öt¦ÒiÊJ®þÜúK¯}_Þ?A ÝÞʽýÆœÊdeÜrè¾4 gÜ@0~ORícͯo ~ Ðué&‹oæŸÉx¿¢4Šþöê†EÛ;{¤XáH:šú-&‡)A¿åªþû”Ag¹eô§âËø>ƒ?µÈ\q›>{öí·õJã܇Ƽr¢[K_ KWÐõCu+Z› ãûį!Îá™ÂóŒÛtòQç^ KÙå7]éL2—´VŸâ:Ûë±7F¸ÿ¸ãM,¸vQ:ý®y qϬÊbûëÏoU&%ž† >Sº¨#ÖlšjƒãˆUí#û¥¥&Gç? dÓ8{é Î4ëÝ“_Ñ¿ô9wÁö%øÏeïߕɼ®Ž ë.Ê Ÿ¾7oö±ášu±ç(wýÖ9;~+ˆseêœ'Œtüú}&Ó¦çÈ Úð$¢†WPµßÂ1Ç!×õk)ÿÁ—Q 3f÷:øþ_]Þ~¹yßLªóñg“—)A¬%7'¡):ôi¨¤cË›Ü|'øLAÇûFf²5[:7i~1“l&^Ì }Īüøúuž:µ©’ªüO´ã]Æñqqû2ŸîÐ ùâ…fõäÕ×—Áòü•Ô`úh …àO ]7ÿ–¸žsvÌ>w(âäü­6måìì§Ø÷ö´ñ×ÔÍok+©ÃñÓò€ë‚?-tBO·ý%ÉdÝÞWœ«•Ey­ tÉÙí=CO²r ñˆñu?¼VPEœ³ï°‚¿[ý2ÁG7“é.]876‹’Mî§o¶–³?ª€)óÆ8V__|žüÝ 3¾gµ{éØLf£«ã5fSÅ{ÜÚö$Rδi°Ü™,ÖvTôJUPÕüC;~Ðq®šÝ»g²õ|ˆÌ¢Ð­ë_ fU¾–Þo;É~(èÁ”3oô³âǺٷ›Z<­ŸÉ´ÍÞgQoÅÖ€ƒÙ Ë#i-W»S·ü-RRÕº–vü ktµû†+ïßß¹Ò÷.yä.?Ì_Á¼ Ž[O)u'­½Ø%]z*já*øóA§]6ÊÎ`½ÆäL?hw—Âgd¹·tR27»}™ƒ¸S ­¡”’ú×´ÙRë¨à“ÝëéÜŠ@ã²ó^Ù]Êê*5jÂÎÒ¾év\SïCíšØMÊëJ Kþ±òÇÓ£,Ž{\^x—æ¨×[ŸaÑu#§©L\È}|—šMPÏU®2eáVÁ×»òž~ºe—Õì˜úY‡Ñ-²É äÏ–Í•!,ÂÓÈÐMêHq¸Š"ö((¬Áéý5‚¿)t'îjž·šÁŽÍȦó¬Ý5qßˬiÞŸé¾Ä߇ J™jõ°t†à Ýø·]gÌéŸÁFZ(ǼÞMî:þt4½ÌäB¾½÷µ§§Bsš¾TÐþ§'Nx²œ?èä_rÓ ƒ­ÓRfÓ‰S~û¶,»Ì<ÄÜ.œ×0ƒñ#”ôÁ[X^Ìtý½e•Ÿ¥³¸´ “"?fÓôܾ{¬Ö]®^'Èðî½Ñb%ñÏ—ðãÝÜ©g¥†ñéìŽ=çp”Cª‡3-Ÿ»Ìª|ÏZi/%Uü¼Xû×nþ<ÕÐáÏœJgSFøÍ”ZåPò _Íßç]fw×sFmޤ¼“»ðõ%MêóµfØxþú,AÔL½.e®\Ñâíñš+Sœ½Õí ËÊ¢¸¤ýNÄû¯)©j½V;~ ËDsŠ-~ºÎNgó —¦åÐ÷ï‰Ó·_aóNtñZ»Ò™nßô°˜¯¤™;†ûÏÙ΃tkN,Þeú±õÀׯ†¿rho¬øAhù¶§Æô+Öï©s쌖]Ö*ÉüÙåMryÿ]tí·¤×«ÎêÊæÜø4àEÕçf WÙ»²›¥u]èYÈ솔t‚+´øû] ÙÊn3á·fÌú‰jî{•U]ÇÚrþ¸’ªü”µãÝ«ä›ÝlPWó×÷=ÚÒaUA×&*vŸ›~íp¢¬»ŠkŽ)i=®výü÷)ƒ®)÷˜Çç6Óé|kcÂ=ze½/¦î`›N_ç rü•Äûú,ãǺÙu|üéx›9ôNìöþÃ=ºre¾Õ–™*v.¬Î®¼š*tPI;Lò»hÁ_/%н\ÂMln³ 3›œ_Ó5—|Ó†ÝV[¨Ø©jöÙkO¯¹íø|£ã~¿zÎ_Ÿ:ÊDÚ²¢ýmÖšõ ™o–Kµj]¶Ë·V±*ÿð–ºÞ¯ë~\¤±àuÐ%{ô×Ç4öÙþÞæã›s©¼`\+6*vöô°Ò!íÈäþÊ„wJâëRs~ü ³:PSÿ\n>?~ËD'¹Ç˜·Ön(È£ñ{&zØ}z¨’V¸Ñ] Î!NIžõÕìmÁtfmzÛ}5•Éí¾\˜–G³Ï}4!竪‡ùy…’>š4zv•ßÏ(‚Φ¾³Íë]©¬ñþg5ú~È£.†£z7e•ûj•ýåLÿ¤ TÒ õÓkö¶¼Nܸj2•­âÂÛ|j¸öWÏîcC«÷ñ89r®’Ò#¹‹üyJ¡ ¼Þ*Þ©o*«b÷àž(ŸÞ¥pFà¡ìòÀò]Yãèݨ=)[[+Ikonºˆ?èŒM¿‡ïÿ­aÌ—sV¦Q÷tÞõ· ­~tâòçI㕤µÅôæÇ] Ýͼ‡AŠ, ãï³|²1 ~TbÊ.epÑ®ú>â×Ñgòã]L ûØç5ì÷äZ>½½T?­ÛŠP6lý'fqÀžÂ,Ü<á±’^†úl¶˜Î_“2‘­ÅÊ•óWhX¸Ï«Àcùù”Ï^ vØZ}¿Ï™®ôþ€ø’_è½ÍÞŒ?芻V>¡a öw<4áW>ùØ0s±o(«ò+¼³ý©n[’â|þØ•¿DÐ-z•øÔ£Ž†}Þ1{õ‡®4÷ZÞgÕ-ŒÃïžÛ¾z9Ñvn€¯’.p>„¿oÅÐÝ }Û¹áÝvbÓöÅL hSí7‰§_…²öѵ֦]r¦N­f~ôR’A¶›e½L!ÿA÷èÉã1õN¤0nÕåšK-â¦3]ÃØú’Ë’Õ.Ä=͘¬ü“…ü]Ödn#K ]Zƒ©€J¾Éâ]–…±W+9v!õñ׺‰Jâ÷S.åÇ:nW…žA {|:ðN“èÞìbO¿0Ö÷s_ݧ[\hÚ½†c3R•„“l;ç>¯+n˜jÁޤÇÉŒ»›;=( ÆbéàçwÂXýþ.>áÎÔ^¯Ë•3¸ßûMèðêÉ0þ^H®3N>ý¹<œÙ·æžÐØÓ¯½ŸŠò·+…ý½Bü„®éû¦K7Ibóº}üùF!éÛ†”;…3?ÇzÏùbOOïRÉ=•¤]h<?è´Ë OÙ§¯JG…dvBϺã–p6ý™ùÃM%”¶ÿñKèøýü÷R·ýÇ;‘8½@¯Ö}2uãV ÂY›~¢"§97‚Åšq³Éžpu”4F: 3w>Nˆ¡kÛþFag£¦ýzŽÝ'‡ÆFTï×äVÕ}ƒ”¤ ¯ùïE ]Ç•šºÙIñ,¼tÏ£™Q÷ɶõ+»_#Ø#ýšR›;+(¨‹¢ÂÁCIy&§½tVðùV]×”¼Á»lâ™Wžݧ‡=­9:’»fÜ–Û.dýµc‚ )éÉRóá;ò÷»º»¯L”}cyAŽ&ýºOó¦Åjn¬ˆdwyÛ÷u¢Ã 8^%ÉÚ&Ö[÷“×%Ð]nYóz¢O{7šÛX_Dæ‹‹Z>ÉV´\ìlá ì“SPƒðN±3„ü׬L¤/ª4»Ø#ŽÕËN <>¾ˆ‚ÆõýòáD$ã÷ Û‘v; 曡Û×\s â't²á­‚#ÕLûXÍ®ˆB£ý›Öô¬^wáŸ?ücœA÷ý´™§t‚šÉýÞ’¼»ˆRögÅÊF²V¿.œì™oKÛf8ç,W’sïñý÷óù] ïˆe­[œfìS+ÿ¶‹È/üãkèE_~úÍ};6e êÖ0ÑÍ ûkð÷‘º‘Ú ±l×”öSB’Šh]êÑ?ÝOF²íuŒíJ¤ö4,=Ë£+®kε³Gaü Óº¿ßŠauµFÔETãþoKƒ"ÿüÅ,OF½‰º<&夡 ãÝISÇa‡n±»GRr>Ôz@‡7{5Œ‰dü> ÍÒ.4(iéfÎ1Y¨?¡këjÚî´ªçüÈaõ3Éù‚Hv®÷‚‚„v¤D­oü]A ²—R“ø¼¢Ó¼L´é‚ô8ñ“1ÇWð€Æ8q#ɪÖ1Rì§(ÞýTÐUß@]÷üõbÝ0ísY}_>ÁÝâ9~yº2¦yãV]Æ–І®žÝk*i?wŸæu"èöÜîíR7:š´žf´îàOÅ^Ž×kÉuz¹ÞÊ9òCÕs8!ÿA—ûB”7OÅBÊlÖo?ò€¼¾½d<"êÕmù™ rø6­?¯“Bçæ`¹Áû\$ãî’¶ÊtY,iLkc#î˜ÒÔŽNžªífzIA¼Oµ0~Ð]޽ÑÛì@;3/UÇ,é¥îß3ÐkXTõþI§K#;j6*¨ŽÚ£nã_Bü„®h÷$2œ-l´¨žè¾íÅ¢™LO]eM;jìjÚc‰BØ_ÏëJ ãŸc…±fIe?> [\„4Œbò¡£fݵ¢2'‚6ÔãžlðyL§E™h{¦ÿÌÜ1¡lhx©n15ˆ«3Äå]$Óns)´¢{[{N¯ áÃFÍÝò\?èÎùî?´¡ã5Ö÷¦Ø=£c1 þcí’ÉÊ›]zÞú†5-Ù°‰©‚Þ¦s…}Š Kç3ÕR±åz>§Ö ,&/Íú^kS#«÷£Ví·ðß{!7•׉¡»6sä¾ÌÒøB¬˜œz»3:-’}èÙ»}Þ|{òÀo1Áçwêšdä(¡~n›¿g#¥‡’ùê–÷d^1=¿?Ðü~dõºÛ‹­³Æm¦ þºãÇ]¯œÛàÌfmãX‹é@—w ÏU"Npv½gIz:éÀî. zÓ8Öødo^§†N2¤Wâ@6„ [«‹I[ŽwŠbgí¸„ëDkË?ÝjßPAµÆì¼®ºen§ƒõû óÙb·(\4>ŠÝïð|Øã厤-Ÿ? ë/Âü¯e™híê÷QcÆ]dþ ôéP1QàÕ·eQŒß_ì@Ž5'õµ.¦£mÝúÄ ãë%è:¼ó:˜ûì<Ó–¾Åd3tF·ŽQìÕaãFæ«ìÈþÓ¥ÇÇ®Óu—5™³Wòõ¼º™_G˜~šåë¯_.+&?Ù7«(Æ¿¯cCSÏ/¼ÛùT0ZÝ—$<1t›u“¯ž`…ÚŸî\LÇ;Äåw1b\6=ÒŠF7KÜ`²=˜´KN ãÝ1Ij©ã7våCíøY׊éz›1mºE1íòvG1MŽüøëžG0ñë?Âýݮ̸¹_Xç+¦ðszK0~|}dI\õÑlO0ñ~Åüý †îÍûñ#GïÚÎ8WÛn7‹i{ùÔ“÷²"™»ŸÄ{G˜ê»dÆ ¾L‰o¹‰ ¼èDßn®¿ççÎ~ILìVÄÓÖÎ-v¨"™ýŒ¸v9VÄ=Lü~¡þlU&â÷çŽ!î§š©‹id‡š¯MOE²f«¸™˜ ݪy÷è à`ªzN­?èzÏÞf[ÑjòT^?’s(’åïÙ¿ò·¡-YXÍø¾)˜ÂŸZÿ¡0ïÃÏs«>=vÒ;d™Ö8Κ5sÉ3’Å¥s¯÷Ò–„ý§ÂÅèέb:Sv1â×äõŸûÒö µ#ËFO'Þl,¬¯óëXRèu‹‘_=BG9Íð¼^La>Þoº@À=^«eGGžÙ=朌z©tñ×— :®ÊnÕÆ—| Ö8^LïKŠ¥»wD²ä‹/^ÛºÛÒ—¬± +Šä)ûš¬§ÃëÔÐEsÛ¥üN÷ö_Ë+Åôå·±;’-êé`ôÖÆ¬µdå´ì»¯ÿ $^WvtdT˜ùu^i@1»„5J·‹düú²5åÕ³¢Å÷ƒ¯?Ë…çLÂ})ÛÒÑÄÁŸ‚¸í–GŠi£åô­»F2íÓéDKJíòäÙüArª80}Áý'üu"‚nø\n!Öïkž¼§˜'ªü>E°o²=OnYNÞ»ËõLšËÉ1ä×Ù7w…ûº—þ<].§_5ÏŠI2þí–:‰ÌŸîÈ‘åô4zÞHó?AôãŠîï¨#ü:–ºÑŸMwÝWÿ¼¾˜úrÜóÍ7‚½?Wëv³AbjißgÀ· Šî´ûÕœh~ÜeÐÕÿÞpŒõ§úx¹é«CKŠI³÷ðØ3ë"˜âôö¦¾5qUPŽœø÷QøëY “vƒÍUšzkôÙ>ÓŠéÔ‹å=?ÛE0÷Ö»—ÙÒ*ÏèÇÉ©( ñÞ€í¼®ºßÎùèRQ‘>·¬˜Z-N~ÜÓ1‚9l¶´PdÛÓþ±íëȉ[í"æ¯k6e¢Ï-2Wô;zä ¢6w/&W—ö¯ön‰`MxšÎ¼)¡3ÆVCú Ï%yt#µÃBé‘¿£Ù¦‡—Mw^=Á¼?9¢„t¤‚)AW6Ÿ¢¬¼¸¸‡‰Âýfé•­.Gˆñ-Ö¾ !ì“s¤{}¸§A´m0—øÏ'†nÑÕ®—j §þO7_ûúäÕú´0ZTÁø÷xè- ê8rLm”»&é ÷ty£4i| ‚øçôhULó7w^F0§}ÇïjžÛQÈBßs)èÖ/eÐ5p<4®ç®HÒnϼö€:ÌšÝdÁóˆê}$ˆÜZ8>ˆº7ØÚñ‰°î ççÉ—·D‘v»êÉÔjîÖ¹or"Ø¡Éc¯z¼´¢}oÞÖ¸1 Hðoçϳ„‡“Ä!nѺÉz¥;ÊÀ6<‚Õony¯Ûb⯋ º0ÕzêõÂý§_&Ò]²½‹réuŠ;»cˆÍrËì×Öåp;°÷ks/1mŽlÒäK u;÷Ú~2ñ÷‘tk;KïκA_r;²Ц·ìÆ8G°ú.ïÛÈŠ\ÛOWQHüs|¡î„.»cbfË‘7‰{[èA¯ĽÕv`æÿº®—íušÚ$ˆøõ}¡î„®ÝÔš†·¨·\ÐèEêûÖYaÁ¼¸¯i² Ñ“E!»Ñ÷w܃1aÝ :ãOžwW7Œ¡¸5K¢Æ½)¢­ß×<éÁìYuqÙn[šùÑkÞšî8^xJÃüu&Ó¯ÚC'”î3È,"éŠ'a}"Xâñ°õ“—ÚÑ®^iï·Û$¦˜ †.Åúôü!y±t¡™‹ÅŸ+EįCE° ‡õÇDeÙ‘öqaQ UícÑŽt5s‹1m¦™ý“Õ#½Šh¸J5Êdp{£}\kGƒÛz=, ü‡õu¶e¢ >5SM=?\¸Ñµˆ3Šç=„Ž[­¶©gGsìwδ*r¯r`„û:Ù! \jÊ™mr3bFÙHëZk@„ðÞ‰-qÕ®E ñïOò÷‘º¡Ö\‹£m.G:¿ïSD…âÇÝ3:G°¼” ŠÓ¬ÉP»°H¾ '<é4]¸ÿ ëè÷áXc›¦ª_DóÎ:vTá»ôX㛸؊fõ¨%j5<^ØÞzIˆŸÐÝ(úœÝ²FÎ^ˆ.¹õ>=yàî8Ù,œ­¹scòííˉ«º‹oÐÒ7VZªøÏ§Ó®L”•#—”@ê%Îã—ܧyBüoôgÁ&ö˸,&íö´Üºÿ,ùÓ®ËéoîÍþI{Kþí¡ôŸØC‰û#® î{ã~·i n¯>ÆT£šÜžq\W ëßÄï¶òß¼—’;·æj{AÝ x¶ájL#ÈKô¹ZÇzø¦m¹œ‹ãƒ `„ ~ è"ˆ€Pr`€ä`¼FH&ÀÈA ÐGâ0ž@ *1‰È@Aç짤åÀIÆxpL€;ƒ d<Tc$$  ‡äd ¤ T#$+1ðY@‰K<€ ”$2óÿ¿ÛÿO·eÏÛ¿}·ÿÖFRÿ÷µÑߺèo]ôÿ¶.â⌻0æÜ÷ÂiÍ€g î½B|`Œ %©É½ß†ïößÄç– ˜Æ@ÒˆÛÐC5mÌíS‚T£&Ü~è@ÐmÊíÛÀy(oÊíÀyo ¯ pòfÜóTœ7±ðlÎ=×ÃycfI îùŽô¤M[rÏ9p|PŒZqëî8>Ⱥ­¹õ_¨@yknÇÞ@#wàäúܺ þ‚½ðlË­àøÀÁ_d è!˜)ˆÀ‰A ü@ÐE’ åÀIÃx@L€;=ü^ Ñ !¹ˆÈºH4"àT  ñ˜o ’ prPô‘”Ì€'PƒJ`Œ$%2Pô°LDƒ `„&þð¹ý?ñoûwð¹ån/ƒÿ¡õÑߺèïšÑßÚèÕFÿÊu<„ñä>3÷oæÀh„Àdܼ&÷.þ¿¾·­ ‚¤;7âö°ãç0Í€gcn/5~#€Jšp{zq¾@ÁÔ´)··ç *€‘·Çç ²€n3n¯Î¨@y3nÏÎx„M€;·àöÀàøÊfÀ³%·ÇÆÒ’VÜÞè!`›¶æžQãø µáž•âø èêsÏìp| åúܳ#xèM€;ƒ Ào<Tc$  ‡¤` ¤ T#$ 1ðY@ C<€ ”}ünà Ô #¡H€ =$S Ñ !وȺH<"àT  ™o ’’ prPô‘¤Ì€'PƒJ`Œ¤%ùð´ýïzµý;øÙâ²ùëg«ó·.’êü­‹þSê¢õ5#î^— cÆ}.£\¯,\C ‹;—š\Ï&\Ó@Êÿ ül €n#îGè€ ”7âÞ½ƒxPM€;7áÞEÂy#ÀšϦÜ;18o`Œ€+ÑãÞÍÀïz¾¦Í¸wpÞ 5çöªã¼AÐmÁí™Æñ ”·àöîâøÀh„ mܼ·§ÇG7ž­¹½u8>0FP—´áöxáø@ÞTŸÛk„ム`Ô–Ûó‚ãƒ, ‹à/@Ê’9ð!1˜w %@‰Â x5¨ÆH @IÄHA4(øÝÀh—`L€;ƒ „c<Tc$  ‡dd ¤ T#$'1ðY@‰J<€ ”$.sà 4B3îÿ~¶ÿ]¿¶?[®¾ø['ý­“¤:뤿uÒ¿Fd"\ojá¼ñß$5¸>Ÿø¹c!H™ÖäúMb\AÅ¿§m ÐCÐ4mÄõk€T£Æ\ßè@Ðm½¿Pò&Ü{Ô8oà 4B°5î@®Ç½WŠóFð5žÍ¸÷qÞÀÁXÒœ{Ïç ô˜M[pï{áø µäÞ;ÂñAÐmŽƒã(oŽão º pò6ÜþtÞ xêsû¤q|`Œ€/iËí×Åñ‚¿)‚hPŒ ÄÀd]$ð*P (Ì7ÐIø9(úH"fÀ¨A02Àï~ è"Áˆ€Pr`€„c¼FH>&ÀÈA ÐG22ž@ *1’“È@ÐC¢2R *€—ø, ‹$&ÿ¾¶ÿ]ÿ¶_[‘Îß:éoô·NRëü­“þUê$SázÒçeܼ×£¿ AÊ xÖäzeã³ýxÜ–}M3àÙˆë5·' ATÒ˜ëyÐC@5mÂõÞT£¦\œ7Ⱥz\/œ7Pr=®'ÎxˆM€;7çzàøÌfÀ³÷®:ŽŒ¨%-¹w¦q| ‡ mÚŠ{‡ÇÀ¨5÷.)޲€nîF¨@yîÝ:xìM€;·åÞ5ÂñüÍ€'PƒJ`Œd 2PôLDƒ `„D!~ è"iˆ€Pr`€$b¼Tcün @ ÆHA4¨FH8bಀ.’x(HFæÀh„ÄdÜ”}$*3à Ô #qI€ =$1ÓÿŸÛJ`Œ,2PôŒMDƒŠ#Ÿ[.¿ÿ­“þÖIR¿uÒß:é_£N2®î;åŽ+@ʹŸE2Þ@#,àäµ8¿ Ì xÖæ|ðù1š¤×ãôÜLër}àq}€ `TëGŽãƒ, «ËõÇÆñ ”ër}šq|à 4BP4î@Þ€ë[‹ã#Hšφ\ÿTüî†\Rè€7ÐÔ¸yc®_#t¨fÀ³ ×7ç Œ`%M¹þu8o ‡`kªÇõQÃyƒ `ÔŒëç…óY@·9×W ç T ¼9×ßÇÞ@#iàä-¹~/8>‚¶ðlÅõÁñ1‚¸¤5×Çzè¦m¸~ 8>¨Fú\_dݶÜûé8>Pr`€ào¼FH&ÀÈA ÐGb0ž@ *1…È@ÐCÒ0R *€’ˆø,!¡˜w %@ Æ x5¨ÆH8 @ÉÇHA4¨FHFbಀ.“x(HTæÀh„¤eÜ”}$13à Ô #©I€ =$8S Ñ !á‰ÈºH~"àT  šo £ prPô‘(Í€'PƒJ`ŒÄ)2PôDMDƒ `„¤*~ è"ÁŠ€Pr`€„k¼FH¾&ÀÈA ÐG26ž@ *’³ø, ‹D-@Ê·9ð!‰›w %@IÝ x5¨ÆHò @ ßHA4¨F(ÄÀd]"À9©þK$Öùÿ¿—IóO?ŸõOÿßý)ã»àþ(smôiSK®ãÏ1*kúqذûÕRÚM?¦~8û®µå3'¹׸К~¿ÏžimF>}¸†¢‚ßÓZÔåiÏܱ˄ú¶à8Vs7o¿.‘t3®lêÓü>ñ}BÃÙ2ý“Þlµ£mN(û@m´ ÑøþbèDÆS?*Jüˆ Ie6Á®q8;Þ„kpî@Ã9½7Ä÷äûMH¡}ßÄºßØ$Š^;|öZM!í¦ Ùi„ãiýüiŠFtnYlmº´Ð/6_èûÉ®ÞC7É’HKÇä‹…4z¤íõ¥¦áìüª­¿vv$¾O{ñ}yºz§GÞÿ'‰¸.©í·’•YàÝ2ëp–97ôÌ®lº%šº½Žq½yçrø¡»Ðwº3wû,O¦Úk,3]RÙËfá-ÜÂÙ€GßUåvôäjj»Ïzdz©fÏ"µÐ7¢}™èmO›Í~1Éd³©_yÌÀBººcõ…™ŽálDv[ý ‹mè˜sÛu­„>+¼ÎºB®MNÛú“•~Ô¹A!­¼f27œI4P­’X×}¤sï€è#‚îÄ’wÃï¯L¡”&½Wv,- 9*‡ôÎø¾çbšs¿ýf«AÄ÷—åubè¾jÖÎKK!ÛÔ·oP²î‡_ê†W÷›¡ml@¼ÏßOC ]Îç‰ù»;kè÷u‰ÆãX´þföëa+Üô~Ò¦[bÒ[®èÑ1€ò‚?ŒéÖLè— ]ý'£Ï¨×h¨ædßá{ ¨‰_ߣ{XXµÏ*ç†åÓ-€êU´65û)ô›ƒn)×Ö:YC…/ê­õ_@¼Sã}Êm(~oó~FЕ©Q=VÅñ}ÕJ  ™»1ÐW?•´íÖÚиuFó·û‡±¡Ú†¼v4€oØý}Mt:”‰Öš=Ú!•¶ÝØ^ó]>¹ínx/&8Œ]ó°Åë°=iÛ:ïWÍ÷{1€®¹Ö@>•j/¹±Ã51Ÿ.é|YWÆø~Ô”ö`s·‚–Ð/ºÛ_º¸]ÿžJ­güœsïd>uóq±mœÆ^q ßh¹äJŽéôªò)ÓŽt»vYfO#»ƒMÃ|]ó髸–qi«çë5âNMî9ýò×öthyc³vNB¿xè¯Ü]%Û›Fáé][.—OqÏŸ¤ ùƶhl¨›ý— ?øÓ ‘õ¬ãûàÈ ‹šÄ¦‘ gßÛ"Ÿ,S¿¯jX3œ­û>n™¿™=Y•~_¿å™?±c&5á¿5tyò½’5õnÓ—»Á}Î?Í#iJàôˆ:áÕ>™%Œ²zâxÅoŸŽÑ€¿oK ãûÝ&©Ø«‹Ê£3C¸ŽÞáÌ‚³ËèjK¹ÑûäO•O|ñè"Œ_Ç2‘Ø£Gêê-·…þoy´ƒ³÷Ãçã\4úϳ¡AõÊgÜ÷'ÞŸTèßÇõ6qî™ú‹òhó˜Úß_”…±ÇÙŸ·ýaMœ‹ÌÉšÞqÞz;¡otŸg xâÅmòؤWóCÇì)‰>èL‘S'Ù¼yçOâ‚›v'ôƒŽ÷'J'¯Ÿƒô»å’mÍ­‡º g¹á7¹R‡‰Î“N—úSc¥G·>îêt*ÅûWVîzN+Æ ßÜ£õ†[ó&ï g|ŸĹñ}»áO÷sXè›]kΆµqÙ4á:ÙÝ£hëûGTiáìTƒ÷¦·F»ÒU®m?é-iji¸_ÈÐ=®÷|^Jï šÏ¹to¹G—õ.ê[+‚uÑ$;Ó‰¥KïÛäO_i±I"ôM‚Núi橺“2èJ#EãYïÑn‹`‡qFÌçô㺛·JÈqÕÀºy+ýiÎ÷ñš „ü]‚Þˆ-c,3¨(vÊ¯Ï îÑõü£ Ö `=eûT_ØQUÿ¼»£¹ËBþƒnkjòÎ~ë2h"‹ÜúýNÕ1ÑŸä44‚õåÚí·´¥—aµ®ô±÷'¯YÜã:Âýг¯Ý—A—Y?Çrh‰uƒMÛpžã×O=ÓjŽ Íó=hÓŸOûkO ùºˆ’I–ãNe¶=¨yU¬ÕÍÞÔ6¢Ú§ÖÏñÇ¢…§üÿ¡¿šNç2‘e¶þåKŠ êURûZtÈm„¼IÓÚp.²%Þ'ÞŸlø„©‚„ü]åOãñá”zÔßÙðQ6 ©ýls£Á×ÂŽÎér ÄŸÊ[ßh¾ý¥Ð÷ :Îý|Fl!éë•ɲIüc…eHË–eÉujµ'û¯eVòü©Ž<ØÎy‹àwݯØmŸ$fPTÄîçÖÙ¤òK1«Ý3‚ øš:Gj$!înjýÅŸxßcÁïº&{F†¸ßΠ™¤“ºk6íO–µÁBo);7u¤eÅ× [ÐÖaË.4åý`dÐxáë•™!ô=¼K7}Vö¶‰`\UÖñ°#Õ (šk·|œ÷ÉQC÷þz‹× º–¯c*/Þ¥Çfu6ÜÁ83ßlOG´XVÜ%€VŸïPÇsï[SãÂȦpž ¹vÖ6wÉõµfʸÀ&ò±ìTMGªÜ¿"&°^çÚ ·á§cP&*ù`>x×õÎ1ûºÜ¥¼ä‚úâ"˜áÜS¥Æ ?7çÃK¡Ï®àW]VŸsýã2(m×Y5‹v»)ŽÞ‹¨öqÚ4ûixP™?…œ0·aÁï:Ÿ»5¦DeÐl­1Qÿ>Sñ(¢Ú/qüØòÂïþ4mÙ /Ÿ×ÂøAWøW|¥Œ:¾<‹ZXÌy7«ãÀÙϺؑ“ÖÀ>€:Þ¾ù²ÆGþ>’BÇ÷uÍ Zã¤:f‘àìq3¡kµ¹Õ·«?¯k-–ï˜Ç_g2è"öÿFe–A&Zã¤;”ޮO"XÝm̯ßb;Ò¿6['€´6éû…¾ÐÙ;=[µ3ƒ&tîk3aøê5~’±ãóÆû~ÚÓ­q”?´Ù°hŒXèÝ­õ÷üôVgÐGŸcŸŒÃ3)¨ƒI£_"˜¶ n%¡ é"»Ÿ?üÉîÐFÏÉë…¾]ÊDû»rWÔG²ÄÚnH&ê0xL›Èjß/m`ýjô¼½d´P¿@ÇûŠgÐ0åØEK¯eм1'·}šÉöš%vÝãJnZC\×Zc^'‚N{{ É ·íµ“Ì qkc¶fŸŠdßôæ£ôp'Ç+Ç’)4.ºfƒÏŸ„ñƒnLß{§žuÌ ù¢´A‰×ÒÉ}D-ÍÚÊHæ›”ø¬ô¾;íïÄuô Ö\;ò0áþƒî[ÓQWkePI?θ(öL¶/{²<Šž·¦»ïwzÆÙLáóñ>¯|Ý#ƒNÛ~÷E:­¹wwåÐÐÛtòì¤wóÕQìÑÞ¨¤ÜIÛ†× ïÂO Ýä‰7ÔJO§…ï#ÇA·©sõ÷}-£™z×u…0¯  *níøA·1±üúî+éäZv£×Óˆ4Ú}ܨùÑlfÌŠfú¯œI¼nv§Ö¨# rÆRüñtº–‰ø<•Ný;=\-‘F¶6ì•F3Þ÷ÚQ¸ïhþêëqf{…ûºÈëâ§»§Ó4£-]Ên¥Ò®U>ûÉ¢Yú¤Í=íc$4ksÄ%ŒC¿ìÄQê£|\Ag­mPœN ­eΖ¢T:°£÷6ݛѬ*åÖžóµ†iñ>îBý ÝîzÜ„1æÿ18Ò%EC9 Z­WÍøºÍ‘îŠ<3}eq.x]c…ñƒîà§‹ëXƒtªtá* íÿ1ähË_Ñ,lÓ•™³ý½¼AÓST埨?èzNßÕµô6opnü•{)¤-“û\gá#9‡gÒszÁye-m–•éÈ>5tÓk‘Šݦ 7OŠZžB5µ†Ñ×_¯¸PaVÆoÑõúYf$j™ÎÇÁè´v~·éiÃ%¦êòd::½_€Gàu¶ãùÄ)®díÐàl~æcÊÛlæ}ðtº•‰lýf›ä~›š‡¡ÒN¦u‹înœ÷ú:[~ýá°y]VPÞ/›.#k’æåÂ;SßñþdÐiíÓ&ß&—qïuÑI¦ºŠ¡;¯ ¾Q]÷Ìñ¦×ˆžB¾å}EÐH¾ÿ¨°ÓmJèíºòPY©µnè†l=g/{•Õ:þþÝà@J, ùÅëÄÐîpUô)´mU;%Qÿü×GvDß`¡ýjajìB#3j¾iÒ+æ/œÈàý¥ÐílÃMÀÓhùÅÎbe"­«ù9îлÕõàh³ Ñ7šþƒ?  ºõÙ½;§ÓH“á^k,%Òä§·U_:ÜdU?·››ÎÔ $î§öÏòt~—RÞ¹¦‘ò8÷ (¯qÚËFão²7ö“©Ý,G mæ™ÿ¹a éŽqÎâýK «ûµV'}Q­ôÙtã†Mñqñ&»RÑ¿Íû³%O\{°O UõI׎_÷2ßg8l îôô=žŠÍƾá&;%jv@¤Êªê<íøAwDåÖ³ÙãTâûÙÆSÝ ñg&»Y]_ó~Mäwn½ cxº4¿ÝÀ°TʹóqEßxÒÉ~Sô'ò&[öDSËg’iÛdšR•ÿ¥vü C1u¬ùŽTRØêÈŸ$Ǒˋ»¨Ìn2ÞGÉ…ìø—ÛÒȚыo™ ~‹Ðm¶{¨xe–J†‹®UÎ´Š£›º»¥Þbƒž]éQ¶sqnª9Û‰_Wüú ÛʵoîœJ½µ…¤šNœ-¬¿ã-fêÜhd—5ît¤;çpHc3‡XýHƺ¯‹ÿXøJCÚvÊçÕ4¹¶gOqÉ-fàkòAd»’xŸÊ@:ýg> P¿@§µ7¿¡¡sÍÏ|©I¼åƒ}‚Y {Õ©Ðbè•ôsÿP‹²sB?hÁç´G™(“sQÞ­!;­ñ£Ð]ˆŒ1ìèܬ7=­¤ºÚÆÔ ß«ß×ôøûÁ:Ó'O_XÌÑþŒdï¢?\êËtËcÏ^q§»Ý¹€@ªò]ÑŽtÇZ÷WªÚjhÛW¯v-¬cÉ×­ëùŸ;bÙÐÐ>ýº}^A ïLOJ¾HSÒ_ÔÊ>1t.z:þi ݨ,:gp-†þ4ê159–qî¯Gºã“Ž%SQªÉäÐ)r^'…îϸî¼.§P¸Ò¢›uchl­—òo±¬õÍ‹WßœhýôUê+†AÔ±S|¥ãþvrp¢ ÓM>µnD;gž’ô‡ÿ^Ô=ªÖëRÈæz@®wôM]ã×ÚŒUû£¾ìV±÷üŸ@’pvú‚O-t|ôb¦œAøM:”ºwÒµí¬Ú·†÷ç "'ç‰ÆMúóÇÓ1D}­¿&SßóO7¸¯¿Auܾý¾ÅØÚuƒL¦-w¥ݸ;3èâ tÙÊÓ&J¦ÙÚ±ëÄ­‚ ª¡f½Æm»0åà ê´7îg«AATå©?èBM[¼Ó™L¼ïÂuj_¿°“µ‘šM×ÔCFq#½uûì4¢®+4]Æû²Š¡ãýÔ“iÚÝ´uH4y‡¶Ü7GÍ87Ç‘ntLƲºM¢°ðyÈÔ¼N ÝêÃFlÎN¢Âˆ˜â#¢©``ýUÊMjVù}‰o» nôèáÐòénA¤<ó̃w¼NÝ•w ãÆK¢ŽÚÀEm› öW >ÇnÔÈ`³ÑÝ#Aä?lå*6÷mVCW¯—Gƒës“èÖ³gskFÑEyx‹4uu>êâ±yS¾,ˆVÔÞö¯+ÎÒºo΀fUù$’Æ™ç‡&¿TWûëh§w'ƒ(ú™×o_gÁ'ºg™¨¿ÉNf™žHê¸Ôëö%‚6ôu¹Õ»në{Æÿ›Íg¡¿yM¯­Ø¦—Ê>èsévo"YÍitÔ%‚¬TëÜdíãØ˜ž1n›ß ÎnôØÏ.ˆ¤S{ÏKÇëDÐõ ù‘2>‘ºò¾IE¸Ðÿ8®ºÏ¹·ƒ¯Ûó aÝ[?èÜ ^ûž@Ù¿ÎìøéNcƯÊ2‰c›8Û»N*6 Í3s ¢N¿O…ÌiÊ>iϪu¾¤å¨gE½©Û¾üÕ„8ö%ôMøÐ®jüc̵YǃèZPG(y :Þß24/*®µ&Œ™^;2É,Ží³pÇBB­ÍäA‡ÕA¤÷l¦ ÂøAçÍÙd·O ͯ—4úJ©¸ûjYÅ1~}Õ‰xû Ú·1.hÄaü ›}ü]Û}Yñt¹…oi+ÏPšV{vòÙõqìÍVÎIÊY˜GçbäÔL?#Ìÿô÷^o§¡.—4¥aæÛŽÇ1©ÖgÈ•xߤ Š9hˆ Œtmn•÷1Oõ8;Õ3×( ä8x#Ží ì±pr77Zǵ¯n)§ÍyC—4ù$ŒtIµÒ-ÇQA¤QØ8Ãk´Çw”ׯÒ8¦µ]/q£&ŽóKlÛËÉ4ñ˜éE_þ<ÅЭž®ì²òpÝÜ·¢ RE¿7UÜhÏZ¾|ˆäFµ"w6ûQ[Nûr¸†Ô‚O;tÿ(ºMGóžÏy¿mŠŠFX˜íÚ0$žùþº‘s¶ç z|§Â¾ñà *u¾Ó·¾ï.ƒ®mYÿø–OÕ4M»°p•^/˘Þt~<«1`°Ë±ÏÎT³ÏÞÖ…™Ad¡Ó.`úþxjèH*~rXMMÅ>–”x…FxüÕÞ)ž ä–+v9Rä'‰¢çÇ z=‡sƺámìY9\MÖ¬'†ú2Ϩ‘½.^ð'w ^º*ääs}|˜‰0~½ÊDÁƒ¿zWÄ0ê9{ñÌü—)y–GýY[âÙ£â´ægí0ŸæÒ‡ƒœˆ³ ñ:è²]¹+‘Ñë‡ûk ¡-æ-Ûûm¯ž§ß/ݽÆCþ¾÷"è~µ7ª˜KÙ?^ži£¤5Éãæ©÷ÆW¯';}Û®½].¬£>íÐi§åÞ1ÔÃBÓ-ÚGA› $7+}âÙƒ^Cþ¬–P6·,pTNæ‡z„º&ø´C§†ì[Ôp£[£ê`²žÕ‡³Þc§Nr‰‹:õerÁ§X?è´öW-oÑßÖŸ§¾•Ó‹ ,.„ÇWû¤:p¶‹'ä4(yoáíH^§†®Î¬Èí£Ìn ñZNµæŒ;lÏøu'j_ÇE´KN‘vêÅÓNðºèN¥]T¸A}ZÍŸÿkZ•¯iÊæ<Žgߌ7®»ï$ô/—ÿÃu¦Ó»L”>ùùÏ–)×ipœû«=´ûd³_âï§êDõ"ÝßwDN ή¼¯3€.¡ÏÔs¯~DS»Gm|á4M¼ºÉ¹ú Œ_ÿq¤g‹§ö‰½ §=G‡l›áÌëDй_»X7Ç8š¼Ò»m3®ð§O¶—ô“Ú$0Þ'J"ø’ɩɠTÍâaü [„ÙK{Ë(âý“ý)ïBY烦µÙbö‚¿²œ–&¼ß\w ?~ÐÕˆF’:4Û¿åšKäé¾£Gãî ¬ß†ÑýÈÝŽZ¾šÿ휜rß¼îÓ4[?èº7ôS_¾A/_Ûº»u‘zê÷z7¶kÓ¦I3[:û…[ •ÿ\H?èxÿäÉÿ½óŽj*Ûâ?ö¨¨±‚±AP¢;–ÑèX+v¬„j쨣u4Š%v,XÐ$ê°aÁƒÔXÐØÅ‚#jìØFì¿ïɽ²Þ{ýÖï½ß[³ÖcÖú¬Yk†ïÝ÷æÜ³÷¹çìsö£³íÊì¤)/Œ˜Q?•õ·½oþúøx±¨„ºUbûA·Ñ…”?LÆŽôÝAã²å67ü.13=²¦O}v{ÅdŽ5P—Fx>‡–ùÊÐYU¬ó¼Ñ»`í¿]±tõé{Eýj©ìÇ:̵1Íyó ôaúŒÌq1‚κ®?WŒM í$iÜ×Û©î×ûzWM-ªÇ«íNÿÃ@óæ„ž×tJè’¾5ébÆ”^DM7¸=r±è©õÉìµÄöƒÎÌ›wãAÚÉ?Wd[¨Žü…²«w*³õ¹5èNãúâ$)9:|ÌŽ y%¶tÂùüh`©¬Ç·_üN™JÛÓœ©Ìãâ½Å]K‡Òªµ³[žA¬+ØK†®rRzêÜÊûiÊ…„æ‘Y›éNÛÛ+„§²ßÏB=TY$Mi£ ËƒnV?^©ÎD—ãõ™M5Z¹¼^’ʦ¿Ép7˜Ö•ˆ]^÷˜*o}w™!´_«|e‰ðZîµ–è圠å{7QÂxËßßS‹Ö™_}‹88|³Äi/1Sh?è®+ßzÏ:¹‡„õ’´~îùG'ãð{òŸ·R m,÷ÄmªÎ@C©ýÙÕ…‚=%t®ä=Š~w6ÿÚEù‘ ”å?§±1µhò…êýÖ1ë 9Û²¾JAPWa…=Ò_ëzv=š>.xO*k\3£ÖÂzHX5µgí)%ÔbûAWùýIs»;ˆWŽú°Ž˜c†®û¼/Ù¡›~OK=ž¼Š+0Ÿe9ä"èb¡K_í1«u£íT-.éŒS³u4ãhî²½ö2¡^ò:{Î}‘‡«‘*¼9´æÙžébÝœUEÿôc(óDâʲ=Ä<±qý ŽSöGÅPaÊSÀ µ”³÷J™}±Æ«>ÖrŸDöáa#¶Ì…ß!º&Ç¿,}—»‰\¿þ5#oËr1ù¼_+5±½þí$ÓN’ÓIøîP#ñÞ¨Ÿ ´—CkøÙÑG·í±žŸµë¡ž®¿p}½iÖ-‚(YRÇsÙ£8_&¶3t[#×lj¯'½”Ï éIûfÖ+ƒÇæªhó`OÙ`ê—©Ö²òÁ…µºÛª›®A–Ó笴醬¦k·VOî}íö½|›!ׂ©Ý¦ºŽ3ü$ÔÛºù«V× t\D#mØô¥ÿ*Š”6éíkfO‡IÏ}Và»—'ìl¤ïÒ—œ)¶3tûš$TIhA!Û¿Õ{%=PbË"Çx6ßT¡RZB5ô˜¾£¥‘:Ø ›‰í Ýú}¿œ:wkOÄ Šøp´ÿ¼ìxv¶1GM_y9÷FúµçÊò›ÄöƒN¬wÊ„ºÚQôK°ûÎÑ lâd·ÇQIt´ïëDC-#ÙË~û)>{á ©,NþjÊžòQ4ïþ½ø'1Iç²-û™D"žÆ×0Òy^žþ«ègÛä+ò'öm¥egV¸ù?]NÃí…Ÿ3{¹·©ãçs‹ÞA7Dûªýä-¢Ÿ…N¨G³” uÚ—ÓAóoé}³&×áê'ÒãÉ]•¬l¤‹÷‡¿ë.úYè„|š•Ì`²*[Å,£òëKµ2Œ>ÂÅW{»÷DòÝu ·ëgŒS‹›¿û$ÆIèî\½^?*l­XW}¥â…­×òYð½ð—pŽ/F¿Ç9Ð ~K+[±yÐ7-^7ß1Ðr´¨ÎìosùDŽ6ã=ÑÏBצþ´mñî›™0£#·òùakޱõ|š¹z0Åß²!ÁÑH¡< %IŒ“ЭËþ¼rævtóÁÙÁ‰lyVæ‘ãBiû¹ þg½ñû«’öN½%ÆGü½ë¾ø4ÛÎ^\}yNG§K½ÉÍ|œí”so §E¹·RÞ0RÎñ Ï~ÝÄvsÏW¦ñô¢…;Øý€ÉãTýN°£UûÏôÜ…ñz×g÷Ïw7RŦëvLÛ O:ïbÓ-£_áwx»3¹OÓ¾'™.‡¸™LF§__çd¤™á·V\•Š~ºGeøDÏnö©û¶&£<—Ñ¢z¯žü¤:ÅRݦ½]i §-ÊßÊ7í¯µêúbƒ.êS“Y9Ý÷09¼Ù/j´×¹J-#½“¯z¿Ë'Œìa:Å@‰Cy¡wA§…nîÎg)U.îe¾ïîhuyåN?ñgtÝÓ,»ä\ B(Èxªþýò—ùrt±Ð…ôN¨Üs¨‘õš¸Ü[â³\¬wš±š ª}pöàÀ#פñ)>±¿A—ÅÓ7®šXû-§Æs/6÷M`Eu–½Çµî\&Ù@“+`*ö7èb|'¶Õ?‹þm&"dÍî—Z]‘ÌNì\/Ýo ¤±BG¬¿i {¹¨5â¸Tþc=ñã³x_îDÑË…³ #7%3ï-¯×sQ“×Mê_ª¬‘Ú—Ý*õ± ß.ÐÕ´/üd§sx@ZAQ/g û”Ìê•};Ô22ˆN>¿±õ¥¯‘Ú]Mê÷ô¼ø]]þ¥§4ègf%ì I+)Öx£uÉñgØî¯UÌC^Ó>sí¼…ðëCÞ-´m¤Øß Ëë{ì¹ô»™Õ ®±öÂÓ•Ô+±Ýª¯—ñý*‹ö(J#ž¹é¿ÌH“<â?&¶ý%tq}x!ëxæÕ‡Wè\ESÜç$¶é‘ÂÞ”çÃÈvÇßÏy½‘Jô]\©ÿAÑ_Bdz$°E¡ÃN·I]M9¦êKŽ%¥°zÖ+5C‡Sm⾡kŒ4Šà—Ûº9ÆœèU±5ÝÒ²ŒôÔ·Cþ§+íSÙàŽº…Õ‹ìf’ž&ö;èÜï[Gøž:Äš¼èÑðm‰5$¼ç©Eóý½j Ïþ:ÞH¯½5Žß üžø¾û¥ò¤ê‡Ùô9¼‚Ù’)•Y¡qK›5.;jG(µÈïÝ©bW#é«r8JU-MWùJ”5£\”¸®]]û„Ý ¶ù OTú†Ÿáצ­E¬×NOÍ)­ö2Ò‚[†~å{ЭìÉà Vr\µ6ùU·PT$3‹·°îŽTh¨Š,fêo³Œ´qíÃ&ëO‹ó¢Ð©RNoí¾ø$[¢áý­ÔYÝʯ‹×Y5Èy„dª†øjgÍp# ëŽB»ÇBwâÎùySkœbžö‰ˆmt`ùù>iGÏ2!ŸLCgx†à?¿ŸÉеü˜3*v÷)VuÀ±C}c)ãLFÔçØþ9cfö§•?ñÛ æˆó2Ðm©ñ¨ÓE¯$f/{Ywmë¼7ý7Ã9¶¤ÄÏŒ{Rä¯ÕÜ;¼â!áù<ó•þ|$-‰]y˜…‘ÅJ_ügNZõó¬m½Kʤ *ððŸb‚ζ…vwîøƒ¾ó£úfø11„ÜI>îØkÖy65ïúÔÀfj:þ¹^ÃÖ 4¿î¸S‹í]mEl\ËœÓl¿æ|ÏWv‘ó©mÛZgŸg\Æt-³4°hÜ1dŸÖ  K{ö©ÌɱLœçÝMcmŸñºÀ~¬¯ ½Ó(ÝÁHƒw -µ~‰°¾§…®©²oëÏóì¬Oº'΃^`oˆŽUÓâ–±~ýÑßœ©urÞa=#:¡î[2óiàéWûûrêÞëÅÓOŠÖ¿.®tïãOFž”úÂjו ›ö®0©ï‚dFR"õ­ð}Ôo€iÕ¤¬¢õ`‡e+«´°þ÷2 Äu%èTýš…×LKfÂ|¬‘:&¹äd±Ë¶Êã+„Ÿí?6qÌöóämÄu]¯|åìˆK‘%g؇s§ߨa¢icWç÷½È¶ûòD†0Ú'á3vFº¡ö¶v ×u¡ãY`_üÎ0Úß!ø·'û¨zÌênéYŽV2åáó0ÚÕùlýöˆ«”§þcו ›öÁ»óð-gØ’• ó==P ÍtŸv¾—XJp«±a+ÂèéºÛc«oÆ8|ëµMq[…û €n˜³ëÞƒÏ0s½F¥fÿA_G=É(‰¡q¢ ¥±ƒ“¾ô[g$áyÄu%èªî‘mÊpOa»VÚ{!ë XRf@’ÔÊÚ”?¯Ôº*7ºõ¬I ô#ÿÄÞ~Ð ùc)ÌžvºÉL.í:îae—‚#;5Lõì…°a¯aÏi ë³ÉЙ9´MMaÕì/v<í6n»@s¬L¨wDñxb§‘bë~Ý4@\×…îþüäR¥$©ìã ÇïOÊ&зtW_ý>+ëÛÇiÜûžjŠ©×ùIwÜç×›«k¬+ä+8´ËWnvÌ|[Û/•åõl÷üCZEÇÔˆZœkeÛ•YœHk¶ô¹ÝBg¤Ò%ÚN+äq¸@×åjµ–»Ö¤²Ìk£n–Ytˆ|»•ªx¹(Ïví¬Ê Ëâz†˜WSkenpv*›a_À;L=V^Ðñ2{ìUñpÇÝjÚ~€'XéÎü.Ö#†Ž½ªöÎ)ÍÏÏÞ[íÙË/†_f“–.mÝ)˜žud². þ¹þ±:^e²ý4¶äûôReî¡ N$í¾Ì*ÇÞû0yt(usÐ<¿9ÍH?ê3ÚÛ:!;ÅÞϪsêàQr·­¸{ï2 Ó÷êq1%œ;óýaØ®;/‹yMÐù<™÷ äR›<¡ÁÇE‘ÇèP‰y™^aámMŽÂ5ÔÏžo$êà5q¯˜WÐÏÒ™®Ç,ilj´4󿥤qWÄú¤S¨éÆõ'/½6ÒïP{ûµÇ¸nq×Òõº¦³C¯–ç-hyœ¦Œ*ç¼÷ Z‘ýËõÒSèÃþÃõlFòq…÷źŠ/¶LŠœ™ÎÖyoœu½Ô ªåÔóa³§WØòFÞ›k¨qNâ·QÛ²ê­.úƒºµM§µzeJg|ýé×ì\Üßí*ÓŸîu5VNiOšŒÕÏH‡*Ä”hcòM sÝëÍÈ{é¬p@„õù‰“´cAó+;Ç_-Š-V´.íqÏK·ï™ºZž)¹‹¤Ì3µÇ0¶ùÍõ’÷ Ý|•y-\ùìÞû Zhøì°»=ü®}|(Ö#†N¨ãœ!~¿&ÑcüÅ®®2×{qך>Pÿ±{2t þlxô:ƒi*n|Zrôi*­¿•vöëU6«ZÕo?YÕ$1Çë:ü„PŸxÐ~Ðñ¯³k3ؾ¹¦]÷…[ë1qv%ùµ¢yÃh>½nF¿­o<ý6j€Ð~ò•…_cœšŸÎ`|ö»NÕdºz"ÁÜgÂ5Vå¤>g‚és_(Å8‹¢âT§ t%íŽ2ƒ½»?lá m29ÎmãsÝù}úW§Pº&\èyÊHBýÞBûA7iáý‘©Ž™ì÷y vù¾M¦ÀÈËÊ¥×®±šŠ›e_*ÃÈ^†4Ã(æã Ï݉‰¼£e2ªý õùÄ3tcŽõë£*×™‚ÝÙ¥B8yöÿÃMÓHB~¢P] //GüK&SþÖX—s†Jø¥¯ÑçzÑû²¿¼Êæ7Fâ¶Ð±ÐéÙaCÿ©™l4ŸP¥PÆ”ßt׋¾ jÞ™Ó·Š‰nóeŸMB¾z2t#èk`ÖêLVzXÿ§O¤PÜ»×3N§]g1>ÁF‹[UlöMf¢»«sÝ·ïìåAלýÔüç™LX?H%‹ÍKúv½èûè¯Ã©cÖµ3Qí³ vLR uãùÊ*왙Ʉy¶Tº Ÿî4µ}6£G¥JníB;윛 }·à¡ý `OlÎd–è“õUÒ¨‰lþŠã!Ù,«têñ%µƒéÉü~]æ·7Ñ‘’üj=û í]%{‚_&{huÍý8?vzôªûu[6Ë=k’t@0NÊ 4‘0ŸÛ[h?è;Vªüò†ã_¦ÑÍŽªw7¬Ù oA^ÇÕÔ¡nKK½Mb»wÚºíõ5£<[XéçvÕ›NµîGŽìTò+³Ü?Íœª&þUØ ‰„õa…Ð~ÐõªÒmß:O ÛÜëZÅÎYéTNû[óþ^7ØêUûÖŒ ¢]#ë°ØK^ÇEÜ„öƒî­C­ï»Y_]Úë›A÷ÜÚä Uß`¹¹­Ï–8L›=õ4ÐDžº*Ï ÝåBûAdzàú²0w{‚d…ñíUÛo°‹Ü}m¥‘>|Ç1Ñ„F>¥W•öÚÏßcæ?…Œµ°´Ñ²€ŽÎ™tPîéZ.÷SÙiÂiÀ‰wî½&šè4OãŠt.Ð}½õé …XØ0϶–Q‹3éÕŠSjÕÍa#ë¶Èø£¡ÔÀgy“M„N\'ðª‡Ð~Ðm,Ã?,ìe9_¹¶ “Æÿ“zLë¹æÈ’s§P›…{-3Q‹Aœäz í°>baw÷Ÿùli¡Ö+e”{rØ¢pw¸ú)TiÏX|Ršè±gÛÙ=„öƒîa³à ðß,ìYýð¥KÒ-¤8åâÚîE^ù_î’)4©Úû…ý§™(äÆâñ‚Ÿˆ….|yæOGtfOßis–ºJ³ì÷¸Éþ™‰7ICÕθãb¢µOøBˆÐß“¡ûëE×/Qx_2·uÚt–*üa˜µwÊMVú@²þúpz€_»Ö-# ³/° ý!:Á/Z؃;kâo}9KãÏo:µgÿM¶ÞñÖ/ø06´‘Å(®{vÚÏ'_©æ?ó¸sÔõä¶nÓÜdS wW/¿'˜J?È»âñã³bËÇë­„vÃßW?ñóŸûñ\xy¤¥-ç(üwå“Ó-6â× Ú׎Áô±Z üD&ZùâHì‡ )³·tBým ;ŸýÉuGëó±)ª°_Ÿ[Eù™öéäQ&2ó²Õz».º†ìØ“Kh·Líó÷/WŸ§së†ß­¸ð›˜§ÝøG@ˆXÿýõ×ÊñÑžvºÊöÄ ³O/¼9OãÆ U¼<~‹íuå ¡T†§Ÿ†™Hð£ ìºXèfdåÇ„…[Xžîò˪^-íÚë··˜°O1Œ¾|^Q±\W}XÍú\…vƒNX°°Uˆª›] Ö{ÌÕ»ç²çòf_“j„ÓöiǞŹ™ˆ²·´Ú º1Ýs=f µ0ûð°Z9Æ)ÍïBrźÕáô1¸ç”U>&ªi_höÚ­c¾²“}aíPc_Ù±áYu¯Æ†\&Ô—£¥a<ñÆDg3_–lZ¦½Ð~Ðñl¬WÞÆ{Û¹,*wïé«r™GsõºÔæat§,_6Ñõ§ûö•<"ø#%te·?Êÿ©™…Õß·~Û“¦Éà·pײz·™ÌÍì°Ç1”nf­˜ïµÅDÍ|ηêßNèwд'ŠX˜dÕùDcäE :Û¹qèÀÛ¬Aº÷S[b0U‰lY6ÆDrì áB¿ƒnÇ»ßôo¿e²Jq¹ÞºHÚ ÎòµKo3a¼ ?½B[¡§ÞD“r‰â&î¾;ðømƳ¾¯Wù‡Å1+Z¤&v´ë’¡»œð²aÅË™lÅm>±~‰÷=UÊ÷Ém¦‚7¨µ-jj.½Ý»Ò$æï“]—ÝÊ¥:];šÉ&öïà—w‰‚ëWL~ït‡ÙÓ$F’ãùí“+n4 íæ›¯ì=ÇyøÖ-™Ì><©c¥˜ûûR*ö¼Ãò¶TK:Höí:Lt¼vxòã¥]ìv\ kbwh™LX´RŸu–̺S”‡HN•j¯6Q§m&žë&ô;èüM#6ÍdFN™2(ÜJojÿþ%Àt§hÝÍÖcñ7טhA›:mÄuút¥3'Þ¾¦Ìd+¼zo²Ò í³åµïßaN?UùÕqH(¥¾yœ»0ÎD“î©Ó»¼ªîüW¨DËV£&uÊc÷+ñ­ê¹Öed‰ßLT¦£¡O‡)…öƒAÇ\=ÒÙY> ¾B<;µÿ°<6Ñ»|œûâPú1Ÿ27ððà^„þêÐ9_™Â§û>¦±ûš\z?ï mõhžÚgf«ü1°ìëÈ0Úõ˜'°™hæ²§"´útB¾eûÔ‚Oð]¡Yú•µYŸÇªã+þRËp:ý³ñç ã•×§íí]½#)ï’–¤±jÉY¦§\¡…³»NLÈc¿VnÕÓ¢ §™!|ÁH ßßhÙ*±¡à7¡Ûéß~š* íРíÃ+ùµQÏÅÖ<¿$ *Œ”w”‘³0î¯^Ò¤|JM*>c©øŒ%­CñKÅg,ý=ÎXâÿ¨Åw‚ÿnüÚ* ‰ Èà¨@4° œ–D3°81 Ñ¡)€@p†ƒó: O bAÂù©€$‚ ƒ3 ÑÀ $pŒJÌÀ\à(ýXD§Y\ÿí¿Sÿí_k›ü'êáÆ‚œâ3–þ£g,ÉœÕø¼#ìé¿yÆÒú,Êþ¨xlT<6*ýïþNã"îg4b{óß„ký€$ƒB ‡“RƒX¤pX* ‰ ÈàÀ@4° œ™D3°87 ÑÑ)€@pÆ ùH…@G¨± HáU@ AÁI€h`-®ùö_­ùö¯µLþ5q Møþhè”TMùž`è@5ã{S¡V iÎ÷HBÌÀÖœïÕó=°ˆÁL4ÀàÆ÷.áYÜü€®ßCƒûr;uK¾—÷ ¤|ªV|Oìƒ kÍsÖaX¤ φ}`¶6<§öXÄ€©`óGØGõ:žkû@Ž€ªnËs¾`H\Už<÷öAyñØV iÇs1`˜­Ï €} 1(+€:ð5R\WÁ×q]PdÞ|m ×V ñák%¸.0›Ÿ³Çuÿ?œ;ÉÇAüÇøØ†× lTúß™;â>"BlSþÜüÿù=°ˆÎI4Àò€3œ•ÐdPäp^j r€ŽL´ [ˆV “S‚`6à§çôÀ":@ÐÈÎpˆ~@’A!ÃAªA¬X·¸ÞÛ¯ÞÛ¿Ö1ùwkâš3‘Ð5åg˜àïIÝŒŸ¥¿R)Us~¦ž™ŒŸ-€gV qã{ÜñŒÀ ln|¯5žèE n  †–|ï)ì#Øù]+¾·öÁOݚﱃ} E Tµá{½`™;߃ûÀ $r¾öØä|Oì=°ˆT4ÀЖç¨Ã>ªÐyò\iØrXµÏÙ…} E°Uµã¹£° €¬=Ïa„}`’<—öØ€3³Ð)x® äÔjožOƒk)‚¶Ê‡çwàÚ È:ò<Ü7°ˆ]4ÀàË×zqmx? ëÄ×qm GÀWƒX¤þ* ‰ÿ2>⯯X¼ÆV<>Ò:üýÆGÅóHÿ›ã$Þ×µb›ñç’á¿€h`å÷'¥À lÀNËèEt`  œáÐü€$ƒB ‡ƒSƒX¤pv* ‰ Èàü@4° ¡D3°8F ÑI*€ĺ¸ÅõÞþ{õÞx@ùÇ:&ÿnM\-H¶&üü;è€XÄ ¥`hÆÏƒAËèšós©ð¬@Ž[-ãç#áYMåÆÏéÁµAµàçÅàYHZòsKpßÀ l-ùù°ôÀ"BÐCk~Nì#0ú]¾_öRíÎ÷QÃ>"hªä|?/ìƒ óàûJaX¤-ßßûÀ lmù>;Øz`ƒ­h€Á‹ï;‚}_? kÇ÷¿À>#«Ûó}°¤|î¨ßû  ÏǵXÄ ­`ðæ9Á¸6‚¸ÐùðU\ÈÔÕ XÄ—ç â¾Ø|y¾® ôÀ"ÐÈÎ øHþ—ñcðºÅã¥âñ’Ö¡x¼T<^ú{Œ—âû–,Þ·ÿM bA·'¥Z € N+D+À)A0pCóz`›h€äg8;? É ÈáüÔ ä)¡ hA"(28Æ ¬@'©À,ÖÇ-®ûöß«û&qáõ`˜]þýÚ¸º&ü" ªÚò3`™'?+öH¼øžuØf`óâ{§aèE Ä  †ö|/)ì#0û]¾§öLÁ÷ñáÚÀ $Þ|?® ÌÀæÍ÷7áÚ@,b@WMG¾·÷ ¤ð*_¾×÷ €¬ϹǵµÓÿ]·/)„úÅã¥âñ’Ö¡x¼T<^ú{Œ—TâûdïK4Àòøµà¤ü€$ƒB ‡ÓRƒX¤p`* ‰ ÈàÐ@4° œ›D3°8; Ññ)€@p†#ô: ŽQ bAÂIª€$Šurepš XT "€Ø€ ª?Ћè\@ 8ÃÙúH…@竱 HáˆU@ AÁ1€h`8i%ˆf`.pÚþ@,¢W 0€<à ‡ît 9¼Ä‚ …³W-H@V‡×Ú„}`’º¼æ#ì3°ÕåµaèE   †ú¼ì#hø]^ öADÝצ‚} u}^# ö]þý¹z`…@ŽÀ¤nÊë@¤Rªfü<|è@5çç²ãYHdü|p<+0›ŒŸSgz`ƒ›h€¡?·÷`çt-ùù±°ä~êVüSØRBUk~N&ìƒ kÃÏk„}`w~Ž ì3°¹óóì`èE    ~¾ì# ú][~Îì9¬Ú“Ÿwû@Š`«òâçîÀ>(²vüüØV iÏÏ!}`¶öü< Øz`ƒ´¨ü,\H´UÞ|O<® €Ì‡ïÑÆµH:ò½Â¸60täûcqßð~@çË÷kâ¾_݉ïĵÁ_´ ü4™hêÜþãx‰Çx›Kq^wñx©x¼”ìP<^ú/ý§ÇJþâ»ÂOnS "€ØøßÂAùÿöþª©µÝ¾±cÇŽ=vìØ±^±cÇŽ="%tìØcÇ;öH Á©¡ÎІ¬Q@cÇŽýûÏ̉g¯÷ßs¾³Ïg?ç=®1~cïg-ÿ&äμ¯{î ¤ á'++ðèÁ“— ø€*À“™d 3LlÖ ”ƒ&:ø‚L1é Á”`&A[‚†Ÿ­À  sL6àj¨KL˜bÌ0yZƒTP˜LEà Z0ÅÄ*oP‚˜hmA ~Òµ/P€Ì1 Û€¨¡,1)‹A:0Ãm PA9X`Â/hÁ“·¼A `2·)hø‰Ý ¼@z0ÇDo> † °ÄÄ/nÅö Ç냊€uk¶g5^ÊÁ¢ Û;¯Z0mËöðÅëƒ @Á°)høâa^ hÏöÖÄë ðúà#`{<"ƒÂb RÐðEÆ ¼@Ñ‘í'‡ ŠŽ øtbûšá=ƒ%Š|AË$+ðE¶r(P6àÓ•íçƒX¢`‰»±}e𳂊—µÛß?+”ƒEw¶ÏþnЂi¶ß~VP‚¡Ûw鸞ዞx¢»¿:^EÐ|z³û|ãõÁEQ܇Ýo¯f(Ö}Ù}ñúP–ì~¼x}Ђi?v_X¼>(ÁÐÝŸ¯RÐð…Õ ¼@1€Ý¯¯Bk>Ù}ñú`‰Â+Äî_‡×3aëÁì>jx}(‹!ì~^x}Ђ) ´xŠÝK 7 ¶ ø e÷ôÁß –(àâaì3ø»Á ÅÜ”`Îîñ÷ RÐðEÞ ¼@1’Ýó/о ø€*€Ý)‚Ý5Q:âÖBì?"“ÿöO>qÿW2ªT¸£Ë=Ë:ÉLåþù^¹-ÎÖÊû³Ÿ~ÛÖ]™¯õLå¾ú\U·?ûë·ùiuåÏ>ûïÄ%>áù£¸>Hüþx»óÛi’ø¾qyÔküû“Â:™9?5jvÅ•î4¶¯>ǪrŸ ½q¿5rý溵ۛÄÈ~<î25ÒöÜݽËcæV³½oõw!îÿVî—UaÌé‘ :uýÓø1ILÍèÚÜóèþÊ vG=fÖ¶‹éF¯œŽ®äÒÊÇBë®Ü~T*\çVýÉÚŸ‰Ì°%Í›œ;’GÝ/_©X;ï1£ÞÄnDïDÆv\/‚‰Ûç†ÛwD€\üß'2Üþšy¤þ™qBàþ˜¹ÁnóÚΑï+LÖ² ŠÆsBä®H6]$÷D¦¡±±VµKª~wÇcF7>§ZûdqþÁ®IÁô«ÛùÛÇE„ۥâK"Ó͸1N½›ðô(ù>þ³¿<·Z0iµ”²·ŸrÆ(˜M»Îv˜Û4ŸÄïâZ}¿þ˜™~¶æø&ŽÄveϼÌ·ˆ ¹§×f íz aw‘m9(ŸR×›OZ™ü˜)mXó⦆Nä*ºZýÞ§µÕn÷Ô½¹ýb*ð»ü.ÿžOœ±'Ç'öOªÜï“;žJû¸Hãö1‰g†uX£íÒ¦€¾¶Ÿ±}Çö'ú[VîoÏíÃhÁȱ£ºüKÃíƒ^@ÕÚì~ö ÃõoÓ °*ø‘ñyÆŸqÄß?9v7SÏ qÌ”v3n7\\@›Ÿkžwë SÕ¸‘‘# "ïÖë‚jøus˜5ö»ñ}ê‘kÛáPó±Ì¥*Ÿó7ÐȺc6´O{Âpýííé°wȹ–?4áTæ­Ø«Ü~[&£K…\ßÚ†í}æB=+«_¿÷äO_›CÚ;ù/›W~žÍ¸ñC.·mrvÓa1ÌZå¯;Ëã hðë¸Ù‹ËŸüé·|•Ý>}p0¿ïO]ãÏ'DŽí.~bG4Ãõ;Ðð¼a¢ú5K˜›£Ú}{3Ï‘Nn™_ý÷¢Êý"¹ý½DÈu9fphz;Ša»ÓMüU@ë&šUÔo[ÂÔè§ ï+s¢n«\ ó ¦yÆ“ r¢ãƒ^thÅpý éÁ¢Áz*ùÓªc«£_M &ñvG1nßrù½´o¤v*&ÐŒÝH»Ft’4­„‘D wÏ–Uš8Ðæs¿^MܯøÇ÷SˆÜÕcšQš0Ư¼x˜•®ŠNG¤¿gJ˜§ÕÝWU¬ ml›Üµ ò66Zâö !—\C63úc(ãîÅv"-¤j‚ãýJrK˜fe¹»çÛ»kó™ñŠÊy‰?äºé¶]878”é¹þ‚{M‹hšS·þ¥%Œq;Ç™ËéûËfÍï7TPî÷Ô¢‹ ¸ï‹ 9Ë]ç¿ü^Â|®-3_пˆ2'í|âS ÃíO.¢XÓÙ'¦”Q‡QÊFhÂr“¾¬®ó>ô“¸ÏPT8£ˆÌ‹w¹ ­YÊ´vøéh-¢—~…>M "v×AÌDÜø!7Ó_j?ï­’a»§Ìq/"e÷ S©E)s>Y \%^NÜþbA•ó<7~cK…þ7¿÷P2‰ Û˜¦ˆ·hã9¢{)ÃífGéO¯l8¬ "vW¨+Gpã‡Üüðÿ6™7˜Ã4³X)/¢.éæÉ×F”2^µÕ8ÙžïHܸáÏ;“^gNÖwíq ±ˆâw¯-y0³”y¸¾Þ‘Ÿ½) í­û·Ð ŠÝpe¬Çžáܸ!÷â$ÛÀç*“‘4æºóÃ"Ú=ÛrÍà>E”=+äü®\'2¶y9DÛí’l®Íí;'AîÛë—ê¡®0Ü~ŸEäRË}g矅ô10’ZÔw¦˜÷ .ÖZDôÛzßâÆ[†ÛüÙAc\Þ4Õѵ5¹ó$…¤ÉÝÛGáéL½¬4Ÿæô}üÔc+û‡1¶lÛñ>:ZØ©›êçi[g½Î{'&§†aUÇÎ ¢qAÏØÚpûêé‘Ûìç6Ü=ÑdßÓ½²ÖQÖÎ+{Û9Ò(ãÂʼn¿ϕ¼Ä×!îó4W*tµÛ~ÓPèÏLê;M²\Geƒ–ZHÃ^T­Š¨ðWögCý ªbl¸8†7äb~ »«m“:Š=Vß°fN»*}¿‚®úN¦Ç¯äÓ5`PÍ Bnü›ôÙtÐØ+2f|ƒFC‚Žé¨Cüá†ç$(W›$îµ#v÷Óåäj}Åó±C¸ñCŽëx‘é}s±ç¹`éâïµè™RÀᅵƹŽ?Wr_NîC†L»Ô›?äÖ³ âÛYÆàôbíµŽ~¬×]j¹€†m8Z»bÍÚ¼yÌ:¼Þ÷RÝ›>Üø!7»KÆÃYÙ§Û€TGÍš^½´©€F³m–ÍWâIjï§Ïä|½äÆAÜ‚Á«­].Ÿ`Ò² !t4²ùÑ>s èÓ½¢í«'ØÓ<‡GÍ~Ëéáuv!9š?ä>\oøòУŒqÚ1)¦çãÜíQ@ФHÄn7Ý$ˆª…Ž9úæ·/¬ÉøRáå}kd¸í»ŠIYxöˤßùüþ¿ŽÔÞØNNynl‡ÙñÜø!·øêÈW»«îa–6ìš’Ö­˜_›œy–›O•ëÎŽWʼCåüþsÜë ‘Óžm¿Íyæ6&~jÓk‡ÓŠØÑ Í‚òé²Sè‹ÕáÄõ›Sðýq½³®sã.Bn‹‘'ó.®bz7 .¦Ó-ï™ùlÎ'ã´ŸhO–Ÿ>È;6«ì³<”?ä><õºþY9Ÿ)|lèñ`I1}Ìž¿mØœ|êol€±‚Ž-Í3¿Šœ’dSýÓSø}o‘x¯Ù7QËE$É‹ñ&bòÍ8ºk@Ÿ|~ßZ;’y²KË©þUïN4Üñ F.8a`½öV“íõ©[‹éö´üÜûuò©Ÿqcèåtå-»q_ -ëÒ>¦î îsÑ#×UóÏaIÛˆëXLÛ¿®}§yžG7Ùiq ˆy/í1øieÿwn_X“ ¥B®Ë•Èš{hÙ&ÇI’³ÅuÌ;ÿCz­JÜ•sÍmïÕÞ!9VynM·“{=r¶þ4kÝë ÝxwæÕ¥ bÚ–ª=~%æå}W´{Ò…4¾Ì}Ï„ÈU5ž0%í¶†KcŠéÑýê»6íÏã÷ ‘ì~ÄÛ™Ûðó…¥Õ}^Îr—²ŸÜ ê½xÞòêbêZ/ÊtŒ8z›–mi“µœälûfÏ@¾Ÿ)7OHãúlž¦Ô=ŸkeSiQÃ[ còþìŸ>’m÷³&ÄÓIq€ûùdȽ³Žq¡çY:tf\œ>¯˜æÜ~;°e=b>2Rë@­û>è\s{ %öe¿8Ü÷ZÜ¡ÍKßåwº@]“Ï PLUîÔjæø2—^ý¾öõÅX_îµyK Õ³]ÕìÀ#~üû¾ÿñ œ±|1aÃÓ²bz,3{•K\_gJ>º~¢@~ÿTþø›X*¬{z®®ž×eú*~…÷+|_šf§?ÝK—nçwöëB«Œ¶)Hèè”3œ?þ3n#ŸãG“C7´©ù¡˜Æ9ˆÏŸ™KÛ×ímZ­³ =xßëu7nøóŽuH+@ÆmÏ¿Sê¢ ³š¶Ë¥Ÿ“Ÿ%…Vw¦j‹ºØ¶Éàû‰qïO„ܵ#{ãcN’∺ýÖ_ÅTdÃ6>Ë¡ú^£#z-s"ŸÞ#ϾH jïšõrûùJÛ.tÊ•¿SG¬ÆÝªÝ¥‹¦OFœWç»{¾ÙuRèËwøäðûs9r/nηõ"§šw)gЃÚS9ôr°âgát{JS=y¢¸@ê[Nw\¸ï‰zbåùz0yøz:Ïô.=ÿüzÛ=þï_AvÄ‘@\ÿ®Qܸ!·qÕ´í™ÙWhÀœ”9ßjߥ}³ónNÌáû«® îÆÆ@üºŸ«ë&Ö¥ÂzµÆ„Hj_£Îon}¼\÷.½_µ%yNû7XÚgðu{2¶=Œ à÷iíËrýNœÛdÜuêªmžšùQK•ç3‹Û¶O à÷óåê¹~°q¾A¢;÷üÂûë_Ãõü´4-å÷thùÄÖ‰Žö`„|ÿ‚‘ܸ!Ķ%¾t“ÒW5ž¼ ŸÇ‡u»òvœÖòçbz²l}ô‡jåã0`o¿Ï4rã ®•4«tówiä§ñOµd¡²¹ÓYL“Ưxý6€êO§rã†÷ï•uuþÑœ*w©áçí4°öÏqcž„ÚqxVl\N·Ÿñ-r;u{³ÅÆØîª¥¥&“>öß_æH\ß¶:Øg» knÜk›ÙU­üq‹¦9ÿì]¥¢˜,ŽŽ ¿Cö_—9ïs 3}’_± øÇñm2 ó‰`Ã3›=!4ÂoÌÚÕïŠÉsÜùvwˆí²ÚÔ°‚nÞš2dz~À?Žor\ñPrê¾`ú˜çÅt•m0w‡ßÿÜŽŒË°Ætz͉ÅÕ¹÷)Dîûµþ³n ¥cJÏn£¾â,»6›öÜ'ûVk9Í92Pu½wÀ?÷yG®¼I-íÛï¡ÄvsÓþ3º¢f‡³él»mgÕÑcÃôêtñ•ãâräÚ7éžõteû³[V1-]uì|ÕÙÔØØ f ÆYàƒÑÈõ>Ùé<7î2ä¦5ž’ñàY¹…ï³Áü<6–9`j–MïbžŸjûdmyÖ¢‹¡ogíqÇ«9ÁËà‚ùóÃiD¯—÷Qìj=œEVѓ׋¨{f@l·§ö ¸ãUܨNZ%¤†Ó¼í 뵓Së±|²ˆë—hGÇ<¨1¹N¿>ãêÉäRá”qçûFqú9]LlWԦóø> öt£pßíwÈqçO\N€Üü.ý¼}#(uFÉÃ{Š)ÿmb¡[Y&ÕdÛ‹mv$c;`óÚä?Ï7¾ˆ_o"תEƒ]öU"i÷‰Ks¾¯)¦Å‹òZ\Ø›IUâïÅ®œ%¦'57o5ï€y%Ü/k¥÷¹ˆkžð{^È ¬ñß]ðýdWLÎö-ëß9“š,OÊëéàLUöÚ:6 à÷æ¾/仾ën’IeçÜ=¦Î(¦•ùêñânSÅÊçmuæ÷“÷'ú•ë7*}7~Èýœf1NÝAE!FŽóǺ*íTß Óܦ­3wüsDL©ìeš³þ´ÀØ€–ëC Fîbμ.G·¨èy7ö‚êùîYÛo4ÕxÁÑŒ§s^þüùÐnüÛvg\*êë٠ȥa1í~s̪§_í{i>qš;5ð’×:bwƒoüq"7~SJ…kN'/h4(ŠÖDH´+tT½¹°Â&ƒ–÷ŸkÙ#xy×ÿ–{ï’?qûùsßOrÕçX혳?Š:†èÈ9{îôÂjô8·ß³–ßí(¼Ù÷ÛoÒý‰ë;Éå„È=ò +Ùó(ŠŒm£Stäàíw÷Td:Éf”NÊ´‚”U=É(öç÷¯åæ rŠäµ{ö‰¦ó߯¦m¹ª£ë'æ,®ëœÎwö4à«ã¹ÖyþÄ}?øº‡ÜNý»×KvFÓÉÞÇÖùÑÑ砋ͳš¦ÓvÃbÒ[ËäÙþü:‹[7Ê«ZxeI“ÜhŠ™8x~ÖjõýØÛ¼J´†öW3|ùyÎ‰î Œ(ÚñÐŸŠŠ¥ÛÍøõ&r™=Üjª1d\Ìב϶--ûÙj¨µl`¾Eggš“½ç‡?=_¯ wr{&C†¯yÖÓÑÆï«s¤ïÒ¨²¦q¹Ø4€ï×5ƒ¿©¥Âéõ^ZC¶šÅ-ç´ÖQÇ‘²´>iôÉþWÎg2¶SoÀ¯Ç¸¾#䲪¾nнf,}ÞêßvÈ"rL\½cû4:Ð:Ò÷Rk:³uŸ÷K¼Ï¥E}uõïq}„Èí‘~øEÓcÉq»ò/¢c–‰óU©TÙ'cÊͬÔÉ™þdÜ–:Šëç!BŽØËh§b‰­ªãŠè„ï­´¹©TÙ/í"åzÊŸ¢ua³LK¹~,äŒËö‡±¤dÙ¼PD!ÊwWÌ+Rhn“9+KéeÙú³™ÞþT»õÅ¥ÝoÛp㇜ãÝ_'Ov#¶@æÆ"2kåXçÅ o‘³nêr[ؽúª}þ4!×ãc¿ö\N\{q=×<âhœbVh­…EôêãÎhë² ݨ¹±ß†qñþÔPu÷ÑCî¸Õ#§ûTw]RDõl²5øÒ"ZÚwZ#ýÇd~}iOƒj™>5¼ó'¿Ó3{E†qõÁdÖýëô~ù+ŽF³ËÉfE”´oìÑI—“ù:é@uzIºW«ðÏõ&r¿o™µ­:1žìÛÛ/°|WHÁóši¿MN¦ñŸ Õój;R³k+·­@î¥ÑÝ˹y^ˆ·|asÒá$þ:œ3 y’îáøÑÿõV‚Ü£ï­Ãæ·g(¡tÂÑð=…´%Ý5#ºsuY±®^[Ïp9r¾û:x81$062(¤7C箸q+‘¸}ËÝù¾·þ´æu]ÃîáÜ|¦FΦE÷»o24.°Ñ€ï# isý„•-‡$Òcã%O:hÍ6(ò§¾6³DU¦NâÆ9c›š/ 圸°Õ¦E!-½SK•@™ ëX÷$öê¸o…?ß_Š?þ¦c–ãöj²<ïê{ù]5zÞØyU‰­â¿Ó“‚³‰ýé‘Ûêù>CÈu8ñDMRÿ¡!ïo#wFqâPœšR‹¯ýt§cÕ×±È÷'öj¯dßghze5½½R+s¬¼€êTŸÂž¤¦-Æ)®TÛØXÐŸÊÆwÕYs¯'B.nnÑå³ÇÔÄv¯=#) Ÿfé!1 ]ph~YWÇ…NFÛ·éÄλl»Åvüñ‡Ü™ów[Ê#ÔÄvO›° €š}þe³±<žuÚ±Ò빘®i ’ίüéÕƒöoçöäûœ ÷kÂpŠjZoœh ¨ëYäÙ#žï$¦‘Ý·ºk@òíí ”¸œ¹ïÎ%N®‘@§É|Æ×+ ù¸Æqíâþôñ«œ§¶Ù ±ærzäΚ²@ÝÛÿÓáKi>µLo~ýX,ß_Æ…Æj6ünß-€¿îÅ÷‰šã(„m4’@ëŒBóiˆh‚Ó U µg/6s£cdîýfdy¾+¿Xí6ßg9û%÷'TóN ¶kÁ¬Sùtáõ:»M¨êê²f»ó}¬ÈÚæ‡ùê"îõ„Èqý¿èîê3g«{æÓžÓÃo,‰¢Êýâÿ®:Z@£»/ŒjáËÏŸÈùìh9[™š@·ZÔÑ5˜On8V\)WÑÞ–lçZj×¢ £ŸoIÌÒ¥+^r}›$Èqûù'±Íl»|Z’óžá¯HÚ¨pO²0õ ‰.×?øbÍþcоÏr³Šî~ŸÓ,‘ŒËÂOyôäfz«Of‘tmŽÍÌO=Ýi¯Ÿð’æx±ÝZ¯«¹û»jä¸þ–‰dqÁÚ² 3vI{~êÁ×KWâú÷P´ºì–£=—Ó#÷Æ7ºgŒ(‘~ £ÜùçQöÿ-v³Ã©²ÌØÙë¾Æçé˶A­Îõ…3±Áû4öWH¤ÂÒîWŸoÄû ~%rÞF1ËïùeÖt&®ÿpõÜU`›x—ëK%@.òõ¦üe7éºÏYÇ™yt¨ËÌüQ‰¡4dððY[ž9ÑËÃ)81 (_ÿ{SýûÍÕy‰t«iúüªÝóèÙ¢G× ¥ª³o”÷q¢KÊäjòÊó~ü»=…½s›È÷cΣÏÏv~7-„6÷ÚbØñÍ‘n=)üRÀ¯»¸ïµ¹ cUdû['ѲÅWr›åRìåaNsOÞ"Û_›)ç8ñŸG Å•½Hëú•û~ÊcÏ6çH"{ƒnt—¹ô¬””–þøøôÎbjÔæôÊÙÕÉ—-G­¸ï§¹‡º¶X!J¢á'më»;—¸þiJz“É6Ðr¦_-:õ1 ¤SÝFݨÏý|zä¸>ªI4}ëé§æÒ Û}Ú|ƒ’T­Ç¹Žw¥¾-Ó¬?›×—„“™¿ò¦«/¢tdï(åÒÃûÁ¦Qר‚mÇØÁ®³mwÒ{ßí_¸ïµ¹*ƒW®b’¨ûßo˜æÒלèù_НÐ;»å>çã=(MÌ6¾$î>?÷zBäÆ '‘ɬsVQr¨¡{„¢F™‚0y¡r£>°?v­@â¯Sr㇜™Ð¥I·/IÄÕù·îÄЩ/ä4­OªzØaOÊšÏvÔ ¤—&l# þøCŽ{¾ ™îoV–s(‡Žm¼r±Û×Êëtñåw/)Þg7¶”r!7~È9—-ͳH¦Ð.W;Þ·Ï¡þמÊìäO²WnÜè\YHÎP ¤Cš7­éÀåÔÈqçŸÉ´?½µèéð:¸+âü”•—ùþ\.Ô¦Û 2^‚Ëé‘›÷¡ÍÑ·³’ùóæÒœÙiTÂ%ªìs¹Ö8à´ÚlMØ•.üñ7«TXbÎ6nK¦8úü^ji\›5ÓÏSõÛúKœøu[ •îy4-òw¼ 3^†ZŸLUf,T]KÔÒñõÎíZß?CI“OXû9Q;ã s - :z*ãÞrnüòÃ#Úao2±Ý‚žÓ’E-[›¸»§h¼±Q—˜ä¾;eþsùëÕ|ŸKäì/í¹ýD2 1ÞøÔÒ‘â_Ã‡†£Zgâï·P»ÁGÏÔ]Ì̓ä &X™X}~5UK­û‡½v‘Rÿµó×%è]èºË¤‰C}ùy˜Ÿ?‘óhòæä†«É$¢äšu»iiصf LöÿY÷ø&ît,Š äû¬ðó'r\ãdb&° N´ÔüaYÇø&;éõÖfú˜,WÚÆ.ËïÒ³«¥+Öoçú9ê‘{¹ò‘ËU2ùºÐÙEÞ¡ÝMß=Õ›6H/vÜëãJwØÛHŸiñB·=7~³K…?¬ÎvêÉ$ÓüMºº5ÜîÐûKvT88èÆøºÚr‰]sܼºœãÌ3ö­ãú› 3ÞÆLL¦‹¯sZ¿ét‡:ôÿì©è>–™¯<ärΙ¸þ—rºpËá#\¿J!reËn÷,º—M¾ÆæÅ»3Ʋ¤Ðê-«pïO‚ÜüÚÍ÷ÀÏu§é°›·¦dSAÔÏi;îøó¼‚—÷èq.w©SìHÓ*_¹ÏQ†\v êÊ«ü\/¤ç×̦6—:«^¹úü·çi.ÿ1ðM éò7Ìùñˆû^ª‘›0f¢ÉÄvgj”˜Ev _¯¨Áb*ûÂ6`ÎywÓ2öñ…¦ü¸!'Â.d’©CF‹0©$‹ïÏ{”¹{•½Ðë@l7˜‹Käô>"×5ô—3™ƒïÉ®åêcø~m¨–¹¹eÑÛ®7ZÞ™w‚¹óµûáj7‰ë«%§Å&­§üæûË"÷tÁ¤žñø>猌ßú3“Š VïwšIk2úüÞ&bšaÚvMí½rþú ×Uˆœ‚½lx:™vMP®ü“I®µ/>û¸ü,ÃÝßq¦‹ÝØŠä|¿zîû%BnÏJö‰™dr0.à2ÉãZûÏ¢/0•ßçÁë?2‹ȉë;Í»9c»ùíÉ×fÝ£~#2)¡Áõeß2¦çôðæŽ—ÝéAÈØI§ät˜}Œç9—“!—‰Ÿúéêdºµw×ÛŸ·©áñfâ6Gü®¯'uXÏv–Sy‚ëéÁm¸œ¹lYÇ<¶|ÿ*牷‰}ãÙ.ü6œX¿œ…ôHœ6ûžé‘[T›}b(™ÆtÛÕ z×mªZÛ²×69ÃÝWó¤3l{Èršœ½odؾ?ðÜR!·ŽK¦ŽÆ·©ÁT¦ \ÌÊj£è ™•|[¿ENý¯®WµmŽOrµºÙmP2H®>hsÝÛ4>q–jè+ÌC“f¸Ñˆæ÷ünÉé7M÷µ<˽ž9Uuý¥Y]’)µß±‘c²2¨GÝf¹ÚOW™k#¶+g_s!c[ɦAÔã:ÓÅä7?~È5hR%:™úö¯‚))ƒŽíXR­‘ïu¦ûúK%þ)Δ5"J.t ¢G2_pàÇ9®Þ$“™ñ¾ r±»=¢ÚÀ›Lâ×ü[“Ÿ;Óé¢ÖïìƒhÞä;¯àûs#wè<{c ‰ò.ý´nÔ2ƒ†'60mé®dšïÑ<ùÕ…^×›½è… ˆ\£4©÷ŒëC­FÎxÿA…hø&ûn:Å~é:bþä[ »z<»ÞÞ,foÈùó>¾?7rï;??›žDmGnõÌ»”Nýíæ®öÂà =–ùÂL~Ÿè1ó³œN†O{§Rp?ŸÉ¼Rá§óãß…'ñ}ŸÓIÅ>`ÊÐüE·˜Wô€m£Ö*ˆüq5yÃÏ›È/¯ù%ÑЉmÛÎï”Nù+'<ÜÙ!Œ –ù½˜?Ì“_GÑ·.O®ðäú ‘{•Ø0;çÛìÝÕ¦e9{WYËpfæå訊{ô•éÔcÎ Zöö—÷Í…ÜÏ'BŽ]MÿÚ˜Dß$ì Õ²¯Óï}ýf´ýÑ®«xЇ«C…–ƒ¨Kõ«/ÊÖóýÕ‘û6óBû$b¯nKź_RÔükãx)ÍâN¬]ôÎnn¶ß—ÁO×&wàúˆËëfy¡ÐÝ&‰šj(þSÍêŠ#™ ôƒ‘'—ºÒ û´ ‚¥Aÿè¯FÎm}Cí–¡Iä‘|üA¦!šÝ‹}CÅr=—ÕÊv¦­)­ú¬šD†@»K'ºqã‡\HüöôC±î\¸cÿ•4*É^}Üú(Æå²ý*'1};Ë.”‚‹èHßKÜë™Ì/kjDU\ nÔvN£Z»¿Ôí58š©\·°O¾êD³GžÙöq÷¹k¿~üÀ:~AÀ¾õ’ni$6> ÍÜ­n~Š9Òé_]‚§ö âû¯òý‘sÐÜúTïq"uߨ_¡|’J“¶”Ä0•ýœ¹çz‚Èxz´‹ï¯ŽÜ•†ß¶ÉH¤í¡'zu»œJsºûß[Ëp÷ahB=v¥Dý²íSû\$ÈMîÖxê ÐDººÀió²Å©tþ×Ä*?[Æ1µNóh$¦E§[]¾D¯ÙÇßq9rµêvsþå›H›úÆ‹´H¥Ö÷R¿-÷§¿ìü¥ïék ÆÏø ˆ;7~ÈMuÆ®ÁŽDšR£Ù\&7…¦ºÔŸ¢žì“-\N„\R/ö/N¤_uÙNïÉÄvû¬z>I^oWxವ}6øñÒ3Ad¼­üɃ?ä¸þâ Ä=L®Ú]ÖMdPT.•6p¥ôjGÇŸõ¢ñ—ŽôRÕ‹?ä°ˆÈ)ËJ CåŸÕK“hFrZæ•5‰Ìîç›ê'¬t¦ÏM;’בț½äÉrÏͬ‹• ”oWnùú\ML¸0WY”ÈÔiçýñbâú“Q ÎÒ½²¸ŸOœIŠæØÐS ´ÎØP3‰Lý¬Ž^’ÄTžG‹•ÁA4}s¸¿ÕD~ü” ¯=îøeÍÚŠoÁ>™DçmO]¼ä›ô§_ýÙ%_æuÊùÍ8~È=^~wÈ—ù ÄõIN$×”×{:ýJb*¯[[û¼¯Ñ»³‚$“zÌÎÍ}.Bäš=9ê½aX%û~ü2Ë;‘ ï|›æœÌpϺ’ñqå¶ jáØ³ïWîs!ׯø s9Þsvg™HÝo”~½—ÌTžŸº4¯·î§ ï17ißîç“ §y™PQ¡¦ëÓØÆô ÄõëNa [M~=>ÌýÏs…KcNDÛóÇrkLÛÔ¨SÓ–NÑNKü¨sd×§4) wåNí|zå&WГÖfnp95rŽžøh¦¦öŽ}ïM_”@ûæÿêà72•áž·p£aëc–ží¨ çöÁŸ¶úóã‡\ó)G7w‘ªÉQgñrH“:®r½‘Êœ.{ÙÿG+Û¶TPÚy§WSsã`²°TÈV÷Vnj|c[‘S¶š §\8:«Usªk ìÞšxÝrËÍ ªìïi?举ãjª¹«0éô>5©V¡’¤1•×CÙv«È±O—/냹9%ç’j²ìÜD;NM7÷lTnzœÆDû0»vʼn¸yMÁ_Ÿåǹ‡+÷.}ÏpïÛDM{Æf\YQþœØ§ œ"iÌóºžý ⟠àräªÏkÐ;äCÊU⣯73ÄŽú×âçÔ"ÖÐÓz€#^?1ÕåC™<»ïÔîç“!7ܸfHdl$O3–|íu'é9}Ï\piÕTGþù z¸ æõ˜Õüü‰\ ¦~¿¥â)µÂrSñºxn½rý9µyÓ ßG2^æøD•ó”qü3^6¼O{û³LÄQÓ9_ÇêN>'YËäZë~8‘q¹ƒñãîSq?ŸÉ¢R¡P½0ÜuL‹¡jK¾WÝø8šÞÒ¡ÂkÙ*;à,ßäNGUiì“¢ä¨^0ùÔqn^Ò#·›’Î òŽ!öÝ_ôŽ&··7Ü»eøSÏ_þnóôv]½ëázîÉÊ•Üø-Æ:R1»ÝÓJfÑ4ª&ÎŒNèã/váïB£ºž9’>V°o8’´žÓï­-{F^Œ2 žæF{§ß‰¹%Sñrn.g²¤T()¿£'UµK¾9ÑE¬6Ï÷VP3«âšo„üø!g¢ú­YFÞY5ˆ ñ›3¯MžøŒdêá_?ê©âÐÍ‘¡ÿ9îjäÞŸºÚþ\i(Œ`+i»ãÃËgä²ïdŽæ™móòÉàV ª¬»ÆñCnb‰ SwQ(uðpÞF÷úññyâ‚ÖÏþ\纱ÃÜp·…âÏüd¿¥¥Â´±8Õ ¡¼V1áS#è¡Õ³w5ž‘BÞù¥×‡?õ+ÿ|¬¦úþ¼¹µ«ßEŽBÛÜØßpˆ z ʾõ.JkÙÛ¥æŽÄýþ‚â{³¹ùóäªÛ²OXÞ¢ÆÐTùKÓSÅOÿ{€)ˆ}JA³˜{= rÆvå”´yÛÎÏ/JÂI|nÝÁ=AO)ýh¶ZsÛ…fT=·º|ª‚4Ÿúcf>ÞŽœï÷R aS%—]ÚpZb\à>åŸ/s£Mâö=+Ü4²[œçæ×\NœªÊ¥öLoÒÃì/²„ÓÚ¬ÒsîOI¼´AÌ[s*¶rèÒí º}±úàgzþ¼¹/¶ß´5>]'ö·ìZG„SÓ§Oš&ÎxJ­’Î˦5ð$îú²âÏó Æñ[V*¬œ»#åñ5ºú,àË¡›áTVköÐc–O©Kö~žQžêIåña?äNd6Wiÿ‚¾{Vù…Ó̬ÏýÏ5zÊ_‡ò k¿Wûöí« _vöswqç™BäF÷YYº]s…„ߺNÛ‰Ü3•½£òCåMÏ*«9̮֚ٚÙ@A W˲§Zrß3rÛvgžL}¸øt.œöLvÿ`VTF–âUüo»w$ˆôŽìþ¼9ö*p›› rÎ]¼¤zl])LĈ õúæDƒÂ jþȦ­-¿ÞÄŸo—Xá|ÿ\U‹Ùõõrm½R§~rˆ3‘â@ÓŒÄ BñôÚŽ¿Î‚?oü5‚“r*Ë[±¨æùpòöÛö`œOì½\îLµ[ Ø·1!èÏõãx!Ç]ÿ ¤ÏgÛ5=Ï!)aèÖjeÔÔØðÞ…$ >”ÕÐQñ+ëO–ñ×§E8ï ](:²/€jÙgv§Záîî›UF6ƛܨ‡m‰Û°ª þ÷.øëœÈU>~`ªã܃½q9¨ŒŒ¿¾Ò؃öX¦¸TРçnï¢øëœÈEô[—8ÅËJ'Ô˜ŽïUÚäÌÐcÍËø~ážÔ¹[“È:v þú=w]G„\ó˺º\&“ö±ëœñ=¾ëyì@áçRê°èÔÍÛåž~߉û=îs‘ ·îG Óªö2êp½ŸaŽ;;_Õ·ÂRzP7³ÎøhOjù{TI"¾ÿ•÷ã†wö"}½wQpëA8íÖWmYJÜýÚxh‰ßFW‰;Më~“{ŸjäÆoО'îúV8×jž£>SÊ÷_w'ññ¤n¬üsmÜç¢GŽû= ³4¢b_5æÇU‚»[¶”Re?ö¾_ÒgJ,p¾ö©ç…†wøëÔËK…;÷_Yö|³/-Ùó{Mµd—6èc¥”n5!d¢Â™TUë|úˆu;÷{¯üø!×Z×#á‰×)šÀÞ.4 ‹™e.­&—ò¯#¦å ?%µj*øß‡åÇ9ß=;ÙM>ANu.éºDМÛýÂNö/¥«·»WéWÛ‰Œ— k)þü~Šqü[0s§³ù¨ctvYV•C#èù}ë÷[—Òö´ÞÉ‘ÜÙòÜFA^¾béŽ î~9®NHiÇÜOï‡O‹ “ã>>U£”®WÁ×õÅܸáÏs×½PØö#¨Ç¼ú‰÷ß”P·¯»úNtäû‹Ã êø(0¿K wßR\Ñ—Ç‚{éÙ﹯A}\öT?ª¨„œJЉû=>±W‘-¹û–zärçÌxÚ~ËNj9—½Aiõ¾Ö›_B7&EvY•àLV3·GîÐûß#êó÷eíP<>¤NZ¾•7vn½Í/‚:…òNõ/!½äÔ ‘+Y»²O+h…݇•î þ¾:r¯-²gNL[KãqvkAÉŠð¾{Khå•p˺¯Ý(¬:û‹| Z>7îÇôãÜ}R!r×J*.çú8ÓúØtÏœôª°piåRB¡¯&‰V7ð ö.êþë Úuî`$Õ"nÜã~Ȇn,‚Þmñ¥`J Iã'åÉÄôDÀÞYQð¿Äß×CnÇp¶ ÎdBOžþ1ãW•diûœì]BRJë[~p'I¯q{ÕGô2ÅfzØUþ÷בãדÌSöv;ÖEo·ÙÞǬ„*ϳÙY½í<qÏqò÷õãî;­cºoD’Dzúöá ->±ëŸëÜs¤ÜýU=r7&°WV¶2ظ՘IÇÂÖ ¦â'”ÅÞŽoéBOµWe4Qw}nuþ»÷Ñß½Lþî}ôw"+÷®óß ö³cÿ~k€ ÊÁ“•|A ¦˜¸„à J0€™-HAÃOjVà Ѓ9&9ð5T€%&=1È@f˜­ÿ öAúïíùwí½Ç6»×ßÿª½LQ „à J0€Ë¤ á‹—xô`Žbf> † °Dqƒ t`†Bg PA9X ð‰À´`Š"(oP‚(ж  _ ­À  sLð5T€% ¨d 3Sk€ ÊÁÅU¾ SZ!xƒ @áµý_´’¼A  €Û‚4ü^HÿjßÈÿÞ~Hÿ#ûFþÝOûïIbòï¹Fú»>ú¿o}ÄÎ5^ü¸³Ÿ ›·PCXb¢ƒ t`†IË$ ‚r°À$&_Ђ)&4!xƒ Àg RÐð“xô`Ž7dPA9X`2/hÁ£¼A `¢´)høIÓ ¼@z0Ç$j> † °Ä¤*èÀ ¬õÿûDþÝSû_ï©](2"ð-˜¢àÁ”` -HAÃ#+ÌP˜¬A*( *ø‚LQ´„à J0€E̤ á šxô`Žg> † °DÁƒ t`†âg PA9X ŠÀ´`ŠÂ(oP‚(”¶  _4­À  sQð5T€%Šªd 3Xk€ ÊÁWRÐðÅ× ¼@z0G1¶PCX¢8‹A:0C¡¶ ¨ ,P¸Eà Z¾ˆÿÿÚ'R*( yø‚LQð…ÿƒ{EVîÉbþ®“þS뤿k¤¿×‘þoX'ýïZ±s„7?žìÏÌþw[‚†Ÿœ¬À  sLV6àj¨KL^bÌ0‘YƒTP˜ØDà Z0Å$'oP‚˜ôlÁÔP–˜Å ˜aB´ ¨ ,0AŠÀ´`ŠÉRÞ 0yÚ‚4üDj^ =˜cbµ ¨ ,0ÑŠÀ´f÷Ðþïí¡]–(,bÌPd¬A*( ø‚LQ€„à Ѓ9 ’ ø€*ÀJ 2Њ•5H@å`â%_Ђ) ™¼A  °Ù‚4|‘³/P€ÌQôlÀÔP–(‚bÌP­A*( Hø‚LQ,…à J0€ÅÓ¤ á ©xô`ŽÂj> † °D¡ƒ/hÁEWÞ P„mA ¾ [(@æ(Ð6àj¨`{ `‹AÆö¶EáöèÁEÜ|@ `‰¢.èÀ Þ¼A  àÛ‚4|ñgwŸcŸÜRü‡u¾6&ï±ý½~$1ù»6ú»6ú÷¹†Äï~ÜØŸÍÿN¾ eß&*!xƒ ÀÄe RÐð“˜xô`ŽIÍ|@ `‰IN 2Ð& † °Dƒ t`†‚d Þ P lA ¾XY(@æ(^6àj¨K|¹Å ˜¡°YƒTP(t"ð-˜¢è Á”`Š -HAÃD+ðèÁÒ|@ `‰‚)èÀ ÅÓ$ ‚r°@1/hÁ…UÞ PhmA ¾èZÈ@f(ÂÖ ”ƒв|A ¦(ÐBð%@€‚m RÐðÅÛ ¼@:0E1‚7(Áw[‚†/ôVà Ѓ9 ¿ H@å`…€|A ¦Äö±Àß Êÿ°^b×û±ý]/ILþ®—þO]/ý»¯•þGÖIVüwMÍ¿oKü;1È@Ǿ&(k€ ÊÁ–|A ¦˜¼„à J0€“™-HAÃOlVà Ѓ9&:ð5T€%&>1ø‚L1 Á”`&E[‚†Ÿ ­À  sL˜6àj¨KL bÌ0™ZƒTP˜\E  ?ÑZ(@f˜x­A*( LÄ"ð-˜bR‚7(ÁLÒ¶  ?a[(@æ˜ÀmÀÔP–˜ÐÅ ˜ar· ¨ ,0Ù‹À´`Љ_Þ PlA ¾(X(@æ(6àj¨K 1È@f( Ö ”ƒ…¯¾ S!xƒ @±±)høÂc^ =˜£Ù€TP(L"ð-˜¢H Á”`Š–-HAÃ0+ðèÁÍ|@ `‰'èÀ ÅÎ$ ‚r°@ñ/hÁ…PÞ PmA ¾HZ(@æ(š6àj¨KQ1È@f(¨Ö ”ƒ ¬|A ¦(¶BP€ÌQxmÀÔP–(ÄbÌP”­A*( ü"ð-˜¢` Á” 3pk€ ÊÁ]¾ Sw!xƒ @±÷5T€% ¿xÛó ḭ̈`w·”€ê?¬„&ÿ¾Ï#ý]ý]ý]ýßw-ÉšÿNiø÷f^ =û÷a’²PCXbÒƒ t`† Ì$ ‚r°À„&_Ђ)&7!xƒ Àdg RÐðŸÈ@f˜­A*( LŠ"ð-˜b‚‚7(ÁL˜¶  ?yZ(@æ˜LmÀÔP–˜\Åà Z0ÅD+oP‚Ì1ñÚ€¨¡‚}6 ±d 3LÊÖ ”ƒ&iø‚L1a Á”`&p[‚†ŸÌ­À  sLî6àj¨KLöbÌ0ñ[ƒTP("ð-˜¢(Á”`Š„-HAà +ðèÁÄ|@ `)Àëƒ t`†âb PA9X ØˆÀ´`ŠÂ#oP‚(D¶àj¨K¬‰Ä ˜¡PYƒTP(\"ð-˜¢ˆ Á”`Šš-HAÃ8+ðèÁÏ|@ `‰(èÀ ÅÐ$ ‚r°@q/hÁ…RÞ P8mA ¾ˆZ(@æ(ª6àj¨KY1È@f(¸Öà J0€Ø¤ á‹±xô`Žâl> † °D±ƒ t`†Âm PÌQÈmÀÔPÁ>Ÿ„Â.èÀ EÞ$ ‚r°@Ñ·)hø€xbÛ¯7ìnì>ÄîÖòßÖIlÿw]'ýW_Gú»Nú»Nú»Núß¿N²å¿3ìçʾ¶¼A öÏc’²)hø Ë ¼@z0Çf> † °Ä„&èÀ “›5H@å`ÉN¾ SL|BðèÁ¡ ø€*À£d 3L’Ö ”ƒ&Mø‚L1 Á”`&T[‚†Ÿ\­@:0ÃDk PA90ñÚ‚4ü$l^ =˜cR¶PCXb’ƒ t`† Û$ ‚r°À._Ђ)&s!xƒ Àän RÐð½xô`މß|@ `‰B èÀ EÁ$ ‚r°@‘/hÁCÞ P@lA ¾˜X(@æ(.6àj¨K1È@f(<Ö ”ƒ ‘¤ á‹’xô`Ž"e> † °Dу t`†f PA9X  ‰À´`Šâ&oP‚(v¶  _ø¬À  sBð5T€% £d 3Ik€ ÊÁES¾ SP!xƒ @Aµ)høâj^ =˜£ØÚ€TP(¾"ð-˜¢ Á”` ³-HAÃi+ðèÁEÛ|@ å @·)hø‚n^ =˜£ÀÛ€¨¡,QðEà Z0Eñ‚7(Á,Ø]¤ ùë$‘Éÿ÷3ݺÿǯüç>ñÿÏŠRáâÔiõ^ÚɨÚí~9Su˜Ö Ÿ1Â)’*ÿ÷‘Ùk:ŸfžPÊ‘~ë{wXö§GZ´êPÍò Tí¹]Þä Údl»†Z²ÛÂ[Ï¥Áïr§Ùñû¾ãuìœõ½ÚËD%uí¿z{$¹=Ï™3CþäϾR=J9-¶RÍà²:oWqý „Ƚ7É:Àý S?(3kˆ_$i–[nÛ}ðÉŸýYÙÝó[MQÐïÚ}æå‹ø¾mÈu\y$°á)Ó¢§‹eý¤Hºò¼ýsï•O¨ŠqcWâþ»‚2Wdië¹þdäuc¤;1® Ì2sJ"é°ç‰åÛç<¡ßw×øR̓¾N9ð­ózÿósý²eÈukÚQovåÃõ5RѪͿR¶ zÂ÷mó¤£lû$_Mk;{½×WÜ×-Gµ§˜A+r֬믢²+I­Kš>¡.’\/z;W¨‚F7LáûÌ"×Èüekßû¾ÌŒݥŠT4¶w‹á=?<¦ËòzißxQÕüüðx߯Œëwcb_*<á<0ʼÚ9&Ì–Ý_E;&Öi='ç1í²ÞÓfÑ/ZþhzÇDFAA›¥6ßÁõ ×£~¦û¦GF໺7U$Oúº¿³ò1Oe.xÑP»Þ£j¨|ÿV¾ïrÛÜ~¼4èãè¿zëþÝ{²Öv„ôñŸ}—ýÛdvEîÛù˜%}§q?Ÿ¹íË:M2»!c^Ý›4®¢E9U\)O\ù˜.ëº_íN± v~™¥àûapŸ§9ÁÆY]…]fV7Š¢“ºùEÏyLßÞ²»Ñ㽊ô‡–!§>±½ÚÂh?¦NÓ[>Ë÷FÑÙõçý: }Lº¬­zÖÔ•6š/¼pþ²‚ïÇË÷WGî#»šÚŸ™64Û=1Š~²Û ¶{L•ûaÞñòû°BÁ÷eåúlëÙqð)ï»#5€á¾WÑtðâ"Ãäê):zÅ­i=\(ûûo«å‰ Š\6jA¿ÅFÇÏ¡T8À¿=…@&Òôà§úS£)$wÒ¢*/ô´Ôzï^Ë­.Ôåd÷§ƒ‚§&´rÊëÇǹ¼¸é/nçÊ/cÃñhšíÒñŽžÎßb2Ë•Bb¯¶³lÌ÷ÍfÌ ‘«uúðÐ;º æQ²ôüÔûÑô¤¸ïó¡¡zjy5ë÷ÕÃn|¿`’Þ?»fQÇ!Æœ¹ [qÍ?>T0Š–³Æ¿ëCnA_.Þ;‰÷õpðì¤×…>ë?(˜ïwniÌIë2jýŽŸEÁÌÎAÙòN’òj“8æË:=U|[xºÕeOê[1°[ßþÁÔµÍÆ{¿ô2ædÈ-h™còð Óäýè;Ž1;ñUzÈ<=­ÛÜ{­qýî‚ùϳ·1§Fîk¿§7º”^e~å{?ïÚ/–šö­±Ðkž^Uô5«®ñ¢žL½TŸqÁÄíOknÌé‘[©Uß×®?x,}zÚUp¿‰žF> ºâE6NkXgr0ßWЂ;þqû\gÊ–_.ø6–:·´xÔñé#~¿U­öñéLìîl-çp}ÏÈuï|¦`øýŒbõøîlãèMq÷z’ GtoênïÎK=èíãc%ÓŸ+¨QÉ ›/®qßO!rAÊY£ÇÜdÂJß…ýPÇѰÓ÷œq{D­ºgnr£E¾»×MÇüÂv1éRãs¼qüãöR2 s/ uÝãéî\믓="»¦Õx¾u¡ùµ„÷ pqûæŒäƹÞu»þ²x¡dNôù9eÖ™x2c7¤3yDž½,êe t¡£[¿óÊVPÔ÷6í{g[s㇜[Nհ뎷˜G{iŒ)CiU½~ÖÈ|ÈÏë.Ô‡mã÷UÁ÷YšÆrWO?rî_r‹Ñ;ì9²•!‡zq gžzH·1%”é~íáãuj:u®Ú²^ÏÐ@cƒ&šËä~-ï\Ù¿‹;þc¢{ØHeF÷:7´s¸š‚xîü€_S$ ¬çI /\\¿btpe<îøCîuÔÔi;†„1¢#ûN8~PSk¡ý`w×4òÝùÞ‚0ŠÐ½͘L™y…>¢©Üø!7wflÆ]y³¢ëLj t\þ´EFŸ”¬}QZ/ÅúÕ,\ùÃ&˜ï[Ïr;m¦å´ gvî£ýÃV'P‹7U–­ûxŸŒÛìns£Øöì‘üqW#Çvû~0œé‰Ÿ2),äž>AõãîSå¾ÿ}žgò0/}"¾9j:7~N•û†3\ÿzü@²{‡Ï}êaV*ÛåLÆmà\ƒ‰Ûç˜û“ÏÞ£Rñák?ú‹éìèÍ¢´mÁüþý‹¸ñWö³P1N¾wgÕÚD&Ç4ˆïÑkÇ Ôjº3M|žËt>L“ß½«]¼Ë©‘›ðª‰°_ˆŠáêrIOouoà=*“,™v1Ä…„ïÚ¬ ¦wÅìtç¸÷©GÎÅøBQÌþcRë{IäYÿ×Ý?ûü'¤~zû-3˜N|®¹Ìw*÷¹˜8— ¹õbÃîfÝvH2Õ_µªÕϤ»”µ¶•sp¬;É·±;QÓV}‚Wüø!gé8eÞxU“nëþkÙÚd Q‡ô^º÷.ßÏуØÝááøž5ÛWÛ¥b7~ÈmÅîXÍÛ†&Óý’<‹Iwéô¼±OÚMñ¤¤w›kæŸ æû0ÌçÆ¹úž…ú-s£™oSZ¼~—Lo×ÿPç.®ÎýžZäAôuïÀëô[8qîlnü+J:½¯c@4ÃvÍ4ôJ¡¡aîqS²‹ÿeçuc¿Ûÿc”!'3_Ð?í}4ÓŠ-·®)ÔFÝèx×Å;oÌúIn4f̉¾âÁÔ׸¡/w¼«‘ó¯!^”5,†©×qV^y ÕÚç¿üÙââ?}Ž:Iª_ñCAMG-ªUk÷>õÈ->î1¥tG ãß¡dfÇ)$hþ¢»EñŸ¾¥ìnŠ/ªWîOÆŸK©°úÂèîÉ1L»<0O¥9+Kšø £ Ì2¾[¹ÐÐ öWo£þÔ…Ç=8¶˜?ä® jF®ËLŠ Zuwv*½zôéLh¼Ž SïÎyÐÉõϼ´ga^lIÓ%Üø!wažc™Ðîó½OH%uæúò&{uôc.Û`Ý*Z²cƒÉ¬æÅM Os¯'BnЄ— —±LBÊþVS“Réíп§êþì'kl;ã½OÕ[ªçŽ r\Ÿ³XF»<>Öä{*|Ù}g˜™Ž¦Œ ‰rñ Zy–›ÇaÞý>òÖô“o¹ÏE†Üõ€fl*b™aXEÆõO£í¯/=’WDÍ¿´?Óà‚5n$[9ïrÇ‘¹ÆóqŒµ±ñEu0ŸÙ9øTÑŸ>Ê£Mç ƒù}c¹ï§¹èf‰.½–Ç1wüØ ¬ÓhÔ‹ÉýÙ¯¯²>pë îx0q- Ùö2'㘇£®fž(L£¨Ã™[­:ýÙ7y‘qÌ÷ÙåŽ[rÜþ˜q 3ñç§:u54pωoõßRä›§Á ¢œi{—ù·*¦“Õ®ƒ6Éøù¹=ó(úÇŒ—äÅx“†ª=ëç¹1¶ïgìDCZ© PÿnïtrürþøCîe•Òîíã®/†† µ9\È÷3v¤}®Œ×/˜T×{èÛŒ?äD«\¬=-žÉ8Ôѵ~ †üÓN„íµ+¤ ç— ç@cÛÎS•u æ÷]Êr‚qýKu㙫§ØF¨*øxdM¬U!5=]ÒÌç¢=iÞº mL>:ÑãÎY˸ñCŽë×Ï»¾Þr¥N:}þbú2¼a!>mO \˜ð&]‚ù}ܹœ¹â«ü·‰ñL߀¤±æ”Nª,µýiùìf{ò¯Æv:®ìOÆŸ[©pï½ËËû½ˆg|&©¤½ÒÉ´îƒ{%1”SST­c²Mg·ånÌïÎåÈÝ‹gŽaö´cüé|?ëÒ.34òæHƒÙŽ8Á•}Ô¸ñC®¯ßkßèž ³ëCðŠ“¹é4ùL¾¶¦¨€ß'Süçs)-.[Ô¾r«ãu›Þg˜šŽÓb«eÐ4cÂjƶ½àLÓµ]÷ÎV¹.çër&Ã2O·1ŒñtÚ*ƒæˆ]:¾æSøcåÀrj{£ Ýç1Á|¿Z.'Cnì¼ÈÓ72Ìrùëe œ3¨ë§›Ÿ’òùu¸+ý¶õi°ãÎ÷ÝãÆ¹µÆ …fÙ&ÇI’³ÔúÄFQ£CùÝä“=ÍõϺþšyÚ‰ú×p=?m¢‚ˆ=¬ñëOä&ŒMõŸ“È0êuêëjw›òfSFIr}8d¸¸ö#%²Û¿“‚Ìd‡òëä¤~Ù÷Ç"w\ÑbóÙã·iyÛŽéµ|ó¨cZá€]+ð}1¿fU°PAý–ø´_Îr­¿mª¹M57wZvÃ%æûÆ™µq¢üe7•ú­ŠÊ>‹Ü¸áÏsýnÓJŸ%’ŽÃó¨^zzòõbŠb/Ãø*þñýPãÏsýa†Û<“¬¢ßÆÙ›æÑ¢Íu$ïê¹Pϯº%²¼¸zµj8¿^ANqMþr>wv×Ù&v™”}kU»êy¹4Ó¸A»+Õc—¹çÿø^™x`þ2/ SÓ’ÝA5“šN‘ØdË¥ÚnGÃ\©}“îYO1^l[¹Fü|‰Üœt›|¯ŠÙ^3)§çœ×ûìs©²ŸR½§7¥Qý´à^Ó#\¸ó!rÖh»´Á÷¸ñåFn‹gÒÒäÞ1}r©Oð]ž.öÁ­VsÒ¥y9Üù9 ccT†ùȦ²hL×–Cë~Ï¡K«¦¢9Ó-=ÛHMA«~umÚQÏgHËÏ›¨oH SuÂé!{]³h]À·ì¾9dnl$äD5Œ ôæÅ\û5Bî{%ó¨ÜŸaöme/eÑv»ðó94ýlÍñL)-¦–ÀA•}t¹ñCn[õ'kÆ3ï_´©R”Ew,ìÊáûœÛ“hýtÊ â÷1æ×+È5Nô?Ô©0žÙÀ¶ó©—MÏÞ¥X´œœCÎ+S-»‰W±]ÑÙ êÔûdd§ó|½ó,ž·a;#Ä3{™®®ùc³éiܦ£­:çÜâtjßX;d,LAÔÀÇl~öCî{)@®«±a´ZšØdèaçÈ;ÔôfHD÷vN4<¸ï”ჂèÓŽ«ßwärä¸ë[±ŒÅö¬¡Þ]µdî60cÜ¢;´´¨¯®þ=GÚ~›½pDlWÏÖy\Nˆœ¨mZÓb®Ï–Òãê s­r‡î7š^wéGJœ~ËÅ쩜Šöì_ù«+÷ó‰SQ·ßú+†áúié™ù ênײihO¶ã¥=õõ±žã+¯ìÛËr‡ý^W|3†á®/ji^ˆ©Ë…%ÙtkÀîl×WvćÈÉÚØèˆ?_@Žë×ðgUüµD-}êllšMz<Ðøòr:Ú€mx)'®OÒnüÛÕ½Ãt6^(ÖRƒ·?ßÉ¢i.âjÊ*Ë)àÀ´ž»…rªolœÅ_oAîJýé㘈hÆ{ÜîOŸhÉÍP7µ÷,*ÿr øØÜå”|ªÝ´OörêøóóZýyþzËJÔƒ™w[o^Í|mÈ^iË!c»Å±Y´mÏpÞŽŒ—a$òÌKäúzøW‰­ÍìÍväÉ¡¬«Ã®Öøœùç{]¯}£Ù÷ɉë¿Ç½O!rã¸=ûÅ<è\sûɱ9tþ›ð}öåLª3e„Ì-ÃŒíÎË)Ÿ}ùÆüõälƒ²†Obú™^k³Ð>‡êÚfRe_N›’‡?öàõ:×éýò×-~þD.²s­W.ïULh^ÃwçPúâ5õ¿¾M &û:Ÿ/§@ö´À7üùØÍZ¶•ª˜Z‘ởæÐê&foºŸ¸Móipú‘ ê›Ø´,·"Ø]ç}êOàÆ 9µ¶Ó„;½UÌ¢³‹:\KÍ¡õÍ®Iu›ú½²ìú3Ξ|]{×$öìôâ‚1ܸ!—ÌÞ®¹ÉÜÜR%ÎërýWËy“AK£VÐózc¯í ¤ÌüÎÞc[çÆmæÛûÖÅ#ËIû‡šæRdé÷[r÷óB³œ~Ùlz:ÜS}zü˜ÄrÍûϯYP3’Qo¼¹°[.5vq÷‡ed¶½Â ¢c.l?Š×÷’;"×ÊÇaÀÞ€¦éÒ³Û[ç’T:䦬}}9q¾É&ñ2ªH ìá¾2–|;Ð?…ˉÜ|ÆbL“³ž=bs©aâ±å·§W®I Êhb:<Ì—­;ùû®†¶"ØQÄe»óLí@ª"¯×F›ËßêR!wÝ Œa»E·úšK­6_e<iˆýtµ§[ƒmHÿÀ_çä¾/ä‚;xvÊ ÿž±ócÓ<w®·Ëè¡jRîÚ¹v¢q}8ø:Ľž¹ëâÕçïí e¶±åQèÚf_’¥ÑƒlŠÕGGiœÈÖ3¿NÞ—ÉÜø!÷£íþª[:†2o[³+ð<šsïýà{ÛÓ({VÈù]¹NTk¹Ç—U—øqáÇ9“5¾}ûªC=­Ë¿.Ï£7yKŸwH£¶ÆÆÎbšáÚeÍé Ÿëñ±7?È«ž!_üxIÓ‚??÷Î£Õ ÆÚÜU§òûè‹©[lõ QÒj’ÛjçtSþ:5r³¶Å%îø~‹i¤ÿØçá‘<{ps5+»TÊM‘Ÿ#p¢¶]Ëʾ$w<è‘›WpûíÀ3·Óg³Óz摱 •i*±W ù”oÁÞà¯òã·¦T¸°çÝ6Ïßb†Øõ.O•GiÅkª{…¦ÐëóŽÛ×íµ§Á—ë«kЮ¨··¬ÇržÆB¡d܆µÞ’™Ç÷çJ¡ª¿ÿÊ:°‚~{ß\x¦ÜŸ6±·­Úq×q…ÈÅýŒk¼•Œ±ðÃ?§YŒSw w“=û6Š¿N†ÜÌ ·¶m^]g抧²ÚäÓ‘Ã5§h£“þô¥˜a\8МR³•«{ðã·¦²Ïufý瓵žwϧŸ)i;Ú.LúÓ_}ð±êëXw݈ûl=ÖÄ듟{¿ÎòAùÄôÏ8løšHû«¾ü<çDOn,?¡V½bÛ‡esó¼¹ö“&5{õ*c<Ü…ùâÎ6^N¤ ‹ØŽôjÊàvn·üùúÉ·Bäº}rpzz…©H`õæ“KÇŽ/—Y'ò}Ûì©øåƒöoçúóý7„Üø!gyyGRÝŽW˜c¯¹ì™“OóNm¾£Í K¾ÎæÆ9«i—Úz- fœ¾K#çS«ãa9oo%PÆÏã·ZNÚ7R;É8Ê~¼ðõò¾Ü÷S†\~dæó_ãœê÷ü•}>¥/úÎÅ3¯Ç"bϦíìüùó)îxP#Çv_i‘ÄpýQòéÌ¡]n û%ЄS™·b¯.¥EÆ%þüºŒ{==rIÛ¥½DUƒ˜sÌ™®Ê§o-¾Ôý¨&?Óà{Åó—RçßÕ®ásy¡}¾aHs.g²®Tx»÷õ=É#åL‘s@àûuùd=¶èε5¿ÞXF“?5ˆëÏ÷‡æÞ§9îú{ 3ÓéM‡Ç›òéÐÕ{/v®SÓccRùõçΰòçï×qã.DnzÄöá6AÌÅÍÝnKòiø¥);¶÷SÓä%ý}µœŒw?*>Î^ Ëߺʾ$þÌ‹£5±¢È§S=[ìVe1”~³½ÝÎÝ+hjò¢gúø“û„!Ó.ñ÷ùË5¯üž™?#{Ð!Åd_>m»6ûJ†j°m>$õé~t\_~>ÊrÅ/VMNõcºÞvyâÁ|š±áÎý‹›âùó.Gšë|ÀusWâî·ŒâÆ95Û†fãefí¼º¯úͧ7Ó}2´‰q®_l Í:ô¼¶?…¼¿-:hÍrò® ­s2æ;=ȧäŸG,­GK-UOÿrâ¯sûÑH¶}´ûDnüÖ— Ç'ˆKLX ¶ae>]ß2eòq±Ä]Ÿp¢³Y%–¾Œ•Olu¿?þk8F¿«ÎEf„óã:çòé­uÍŒHŸZµwšýÓªNÔjàÇ^·üèêÂÜ™ ¹q"÷`Ëá#ÍÎ3É™£ŽÌ¼˜O~O?­ÜšMí¾/è5þ½]¬YïØ¨ë~üu@âÆ¹+Q÷EФ³Ì4Ëó®¾—óÉÇt’rG÷hª:æè›göt˜îä~üýžáÜø!ÇןaªmŽÜPO}¾4ytvw™Ývl0©·=Û ]üçëÉs¿qö™ÏæÓŒ±Í³"ŸÎ÷ˬ}·TÅ7öôÉ<{óX$ýìoÞ©ñnGÊÊøÜÁ3ÊïÃßçó.F%Æ,N^ŒiÐò€<4ŸÂ޾”Ÿ(Š =RöA1w¹Y3ÂŒm'Ûñ÷i‘Ûû‹½á,eVDHUùdlÿÚ ‚.84¿¬«ãBÓúÕ=ñªŸŠ_"×aÄ]+»Þ‡™›Ùý›âò©ÝÓû­Â©ÃшwÝz¸’Ø;´æ[ä¸y?þÛ³¶äÁÑ[û™î™›Î;'æó}4ÈžV«za’+mleiý- ß³V—ÒmàžË 'x½Mî²— é[ì­LÉ''ÿîï<÷„Rã…H²lÈv2ó£¨ý)žuzâÆ¹½&,q}»“ù5Î!élæÁïgÏu‰ !]„Eèè®ÎôhŸ±Ñ?ïäÆ9®ï„áú5åS?ùŒå‹>Ý¢£cT|[èD¿|(éÛÛ_Gr¯§GnšnkÿÔ½ö®ZóÂ|0jGÎ9á-âÚð9ÐHcã*?þ¾†7~J…Üq¾Š)l÷yL•{ù$y<åt˜¯’†¤”ø-gO³ØËÍühôÙ[âaùùE€w>ïÄt¸ãÐËGùt{é¢Ò±ŸnRcÝùNcVÐôœ£‹õ££Gw_Å=¯"DN¼àË‘ÝfyÉ·õ[žæÓÂ-­Î÷N¹Aöt Ð:ÞŽÖtêÕãÉ?:öœèsã‡Ü›E×›œ.O×+ö$:½Ì§¥¯jí‰;{Þ/ ïÿø´u,Sc/÷:üù”=S†ØÓ·úMêÏ*ϧÃwÌ7w»FsÓ…+h”@{&,Ø΂or†ï™µi%MètiþˆOùtmJÑ’ý®’ñc3·§¼‡ì E?z3rñªûݹyZÜÖvþ#>¬ò¦Ý¹-Ædϧ¹[W´ìõ4˜6ÔòÎMtp ‰ZœóãŸ÷Árv’¬ÈÝF9ÏüªL ¨þøÛ·¯îRP«Ì²LG2¶!=àGæ¯ö¸èÏÍ+&K…Æv¾¾;©ÝϾE~5 èWË]둉mÛ¡¹-Å4lñÕ‘¯vûñçoÜë ;µiû‚ËÖ{É¥ql‡Cu hæÒµ} ·RkÙ@,qéΧ-VÖ>~üó\]"Ç^µT±ŸÆ 3”ÿtRÉò—þ$ýèÜìçegò‘¹|t‘øñ}¡¸œ¹k–®8­8Lî«Wà#( ëϺ¬žåGXt½*^íL'üMfZ®ð#å¼×g¥…Ü÷R‚\öà!òn½ŽÒ§Bõ¼ÚÍ (úÆô]I 22¶¥½ &ö©¦Ãýh{ÚО{®I†Ü²ˆ ª'ãéK«Z8áÈ®Ž;.Ò¶Š¯o\hr}w“Á?þ¹­Üø!gÚåjÇû'hàž¡£Ïµ) )ûÒÕòsd7¹sÎm GŠìnlÊ?ׂ?ärn^¿ô+ôŲ¶/ Ñ*놆w¾4«>{çѸ¾”~4##`Ï`“ÖÜøm*î¿6èÄݾ$9—r`w‡J-+»4ý¾æ@¡›Þ+m\ýøugWnükÝë|À×gi•±¡y¹y¥ ǧ•šš8Åq ïÕÊ"¶ñ£Už[“Æíäæ!rÜùËyZ¡½ñÌ씯»_óܑخ¡§ü(dûš[Îüz~rí„+¿^ TxÁ*ã¯"Ûß:RLÂïG;ö¿æ÷ó þ¼¯Û]ä%2.ð¾4iVR²Ûýgž¬¬;ûÆ]vx@\½’!§hçx)MFÆöôøfÿú=éP«-dlƒ÷Ó•Ž_µý!Wúñý£¹ú¨F®$ÀòíJùejÝxû |î±å¯µ wç¯;¸Ñloí«èH?~^éÈríšý^?Ç…ñ:8Æ9ïöCÓÑ?Ûòç¯nd< õ£ׂæ-[Ò…Œãµ¹T`5±mÛùþ4ð^³o¢–”øáË—ø%®Le¿ÈîÆBáÇ÷;`Ì {¤h†3­z–²ï溦TrZùÊjà&ÆxÙiž ªÍ>ãG >ÚÜÛ3“{nQˆ\Å~8y­@*ü•ýÙ€ã­óÝçv}ªïbÞÓטõùb2Ï”©õö£uÜmÆœ¹FîiùKŠ)eû$A g]"MýýŒáÞŽÀh‰¥Ï;=»ª±W;O4úb|M‚ÜiiïÊé‰-û€_•z¢9ÂÔÔM º±Ù‘v·-q«ÌÕâÆ¹#¥ïÛçzQ}}o©¬"Ÿ"­Ö$»*Ž1á-rÖM}à@Ÿß~Ë\pɯòùJãë©‘S¤N²o´‚Ö„7û~ûM>ª¯^.º…¿~ÁÝ?õûÇs™zäê-nç:¡f0™®f¯\çSµý~3o |™Êã´Î½©8⮯U厷-¥Â'^J &î¾q>Yože*ƒa×ïÉ>/ª\ï7çŽ7äî®õo·ã M‹î}´n6ÖoGuÚyÿS¼D ®*¦Î¹ƒQâýiØQÑ•3áM¹ã 9åðž ½Jåž:ŽõÆI‰ï®ºdLXG¿;écÿýe§æùóëÓjÜq‡Üû1ì•„«ôcHßé›ÃóIeË6Nôc¾dÄÎ-ºìB1/Ý4äOïÄ%>áչ㹠ýÎ]£æÆ/ê£dËšþÌ”ùÊCÞ!®¤ßá¶ çÜ|)àÆ¹-Æ×ɸLÇú{ô–Ûçûdöi}áÃl7šÔ@WPÚ½òü¢;wü!×ÏÛ÷sÖÏë4Å{ÌMÂy ÷|kÓb|ÃÍõæ¹Ñö±±Þþüu9nžÕo©ì³|ƒŽ WÙ½¯3v•ÆÁ ×wЕ.žYaü¸y¯9wüm-rý•o’¡Ï¾ÐŽ.ù4»ó²ÚÉW˜ÊûmÜs<þ¹åÈ·—͸ç2Èq×ÁoÒ¨‘êùô=»ïdí²kLeŸÞÄÝ#v´ÆÏÇ­‡‰;þãî‹(é×öU÷â'æ“A½jW½Òę̈µ×fm”;Q¹‡Ã¾ìþþüu!wü!·¦KÕòQÒé¥C‹‡ä³íÓäù7™7aƒ:š×r"MçNNËgøÓ¡×á²/©\N‚ܨI[J •TöêBμ.ùÄÍóJ†{Þ׉ž×‰h4c?ýËÉû\+Ãý{»[´~Òùf3›äÓ·E'Š·˜ÊuwŸÆŸþ—Ë©‘›ãYßí¤è 9l{`FŒÃõP׃Ïn1Ý=^Vý3q÷¿ý©†_7‡Yc¹çcõÈÕÿÙdñ5Å-ª>Ôb¹üu­+TY¿Œ aŒ«µv¥â®Í…3þüõ®Üøm+†'-;>èÅ-âúüæÑÇ:s7u<ʰUîìz7þ¹Z·ïÅõí×ûsã‡Ü’ýW3â{†ÅûÎÏϦçQï•}–l c*ûû}?„#ØŸ²“jO Z˽O!r1ïÞhlÝC¨ÓîòOáyÔþâL²K8³&òe`÷ªüó¸qߟõnÛÄ9Šþù…<šñµƒ¤lVSùœj­sÃŽìÿíOý¯Ô+ñ°áÆ ¹çåf©3JBhÇAI‰Ò¾Ñ.”ÆäŒŒßšG—GÏ鹤®Šùéï™;ÐÂ…&²m)úÓûˆ\×Ð[³¸qCÎøxâ¬Pb²ó.y”âqFŸ"3¨{KsÅ»˜m Ï¿û‰ÎàÆ ¹±ÆÇ¡„“«³§lóhÞººá¾QÌ­Ÿì 'j-ọ̃ȟïWÍÿƒ¤T˜>¬Í’À˜P0¨öC鄞½¦³U$›ï@)íî¦ÕÙ:…?äÞ}T/FÍÙÛsfy5Æ9jK,3dððY[ž9‘ȸp ¨¼ŸÁrŽâ’7„w_0—Ö”û¸qtÃ=ÿàL)U—å\/  9Æ ’\N†Ü±3Ê‚¨瞫Õori=©¦žnÏpÏ­¸ÐBã {@åuXnüûÜY9´Ö0Š{Â^ÐÈ¥ú=b®ü|Ï,3^€u¥ÌwuÆa­HwØiÊÀý~Ž9·S·'<ûFMÞ~¼KãÂsÃ…™ SæÛë õw§"ERÈþøá¾/&ÛqžÍ¶ëíN§ž%0B•ýh㇜¶çÅ ᤶ›à½)—n\~ {l"ÃÝp§lvMõl²5øÒ¹Üø!×¹á¸æÎOÂéÛÆxÇ(·\JzQ×5<‘Ùôy±¼k‚+EßÝÿÓáKM’œ]QÈõûU#çàS;‚š oµ÷Û’\JžÃxtLb*ûÑ'ÌàÚ]È?ÃõíÕ#÷zE ¦~¿êû}mÐð¹dzïÌì&‡“˜îÚn˜Ù\ˆÇ@²ú´½ôÂX®o¯ÉŽR¡éÍ%w½l#h½ÿ‘¯a.-éšß¶à[»áÊX=®Ä]÷$§×u³Ö.䯹ÅǪ­´!‚æ6cïØåÒ„«ËÜ\“™Î1µ ñn4ýJѼQ ­<öôˆ³;—"§aŸ±WAžkòιt bMóZ“™6ÚÜfn_wrã C.ð>{!/’¬šäí¶ý™C1e玞—Ê(†±¿€àI­‹˜—œ+ŸáƹÚ{>y<2’šï^ÒtdyÝ\9qÒm*SÙ—¸©¡É†ckÿÙ'¹AgÞa&ŽäûFçP§“«WŸÆT>ßÚÝðùXª8‚ïëuû}<“¥Â³cgÕír<’ª`Ê¡›×m·…¦1γ×q»áB÷·éè1=ðÏýZãø!÷ëuȲõÑ‘ô(1•,‡Þ›4ÿ[+ Ãýž‹˜žw_å£H—¤"os9!rk–e»Í7DRÜ@ö™šiÚG¶|“†iÞlå°ôyNd5éCZØà@êÛ¬õ‡aO¸÷)BîÒÊÇBëæ*J?Ì6ÈΡã’'ºj˜gmF49ÛÙ‘œê?œ_6&&ÛMŠúÞf7~È5+ù1ÍbœŠ¸çìrè벊Ü.½Ó™Ö}Ù;ûtäI›µkò¿·Á}ž2ä¸ëü*Ú³ÊoÉî9´c{?·‡;Òî¼ ç cªÜKð $‡™ºñ9Õ¸×S#ÇÞµüzIEÇ2_´õØ›C‰cí2ƒté̮ƎwtàŸß¤Ô~ÇFŽÉâ~>=r½tFg©È˸€Ãû¬Ó¦•È2ƒØhðˆ¸YŽÄþVÅ‚¯ÔÙø Üçi²«Txyû«h†ò÷°!î÷õs%2˜Êß+h7{½C‹zrþ|ƒï¯¾«ò÷ø¢ˆ}ª£µ(‡ªýœü"ƒáž'Óä*¿ÿ $ö.c•¯\NˆÜìSJZ,¢µþ­—ÏÌ¡ù-‹jo˜~›ážïv&îþX %º•uùÍõe!—^éüË(Â"e¸ß˜òÉ”»×¸ÍÜqÙ>²ë,rÊk¤ÎzH÷fÕÚ0Òמ?äfÆQO5rUöuµIîM«ø‰R›æPáû¦;s>g2Üõê´kbë‰×éìR»^y}¹œ¹ußF/ °‰¦ ï4¶¯žC,’ª/ÍbV_jSÃg‡3÷ëðôØ•@²+Îì¼›ûrã{~©M!vªa&O´45ªÞ¤õ²îú­YÐ"ü{ =j,Ù6Ù?ä.FL¨^¢¦qÆÝ´ÔòÓÂVµ·f3á^ vg™8Ñ­„ÄIgIþq!×Ä‚½C]öÌ™«¥Y«|7ÄÞÍf*Ÿïë6àá¼îrþy-~üÛwf †(†RwùN¹¢¥$ÛòW#†ßaÖ7nôk¼VLSÚGËâäÄþvîÄd.'C.ìªôýq—ºTÒ¯ÚI-5Uœù}ùÃïºÐï;LZ“Ó‘äÛyQ~\N\ã/ÚÄbVh­…Ûµô]ºØ--£y1ïΤ·®”ñ-+yM9ýêÆ>ÁÉ}_ôÈI«7'•‰¡)왇–~|nÕ­‡PË'Ÿˆ t£i“GGž(',¦ªoòÇßžRatq{‹È×1ÔyÓ®³æjé]Wûƒa´Ìì…:72º=rW_”­?›KIl9²½C'D½(OÍa²}rêë® ý^&6”“~׬ü¸ãÁdo©ð¸¾¾ó},žÅ>q}‡¶ÑKí¥W9 Þäɼ‹vÄ>=Ú¤³œê¼=úRÎÈ¥uüç‡XÚ±âÅWÙtõŒú/å2Y›.gGŠ6ÇS~N”ó÷]øã¹gI¡Õ[V‰£ª[u­Ê&µåŒ ?‡ä2Üu–´‰]î­‘ÓèOÒ|cøñCŽy/9¾AÿûÃÙ¤:¶¦bY.ÃÞå?>Àž$;4ä+§×õã-ÏôÓß%÷•”˜üÝ/éï~Iÿ>û%±ÿˆùïûÙ±¿5H@å`ÉJ¾ SL\Bð%@€‰Ì¤ á'5+ðèÁ“œ ø€*À“žd 3L€Öÿ}nÿU·ÿ^õqŸ[%@€"` RÐðÁ ¼@z0G°PCX¢`ˆA:0Cñ° ¨ ,PLDà Z0àgoP‚(4¶  _t¬À  s!ð5T€%Š’ø?ÙëÖ  s3ð5T€%Š›d 3:kðû&ýWî/ùŸÙûÇþ’ì¾I(è"ð-˜¢¸ Á”`н-HAÃ~«ÿÁ^·û’ü¯_#ý]ý]ý]ý¯ØyÆ‹wö³aó6àj¨KLRbÌ0aYƒTP˜ÀDà Z0Åd&oP‚˜ÜlA ~¢³/P€Ìñ†lþ zÜþ«þmÿª/Ée[”ƒ&}ø‚LQ„à J0€Á¤ á‹ƒxô`Žba> † °Dñƒ t`&ÀÏPA9X °ˆÀ´`Š"#oP‚(:¶  _€¬þ'úÛzƒ @ñ²)høBf^ =˜£°Ù€¨þ‹÷’üÏì¹ý¿c/É °Dƒ t`†bn PA9X ¸‹À´`ŠB/üOö¹e×5×G¯!ILþ®“ØuÒßõÑíúˆ'¼ùñdvö¿Û‚4üe^ =˜c²PCXbƒ t`†ÉÌ$ ‚r°Àä&_Ђ)&:!xƒ ÀÄgûoÐßö_õmûW}I|ÿ úÛª¡,1ù‹A:0C!° ¨ ,PDà Z0E‘‚7(Á [‚†/ Và Ѓ¹?ø€*ÀF 2Њ5H@å`â#_Ђ) ‘𢿭TP(b"ð-˜¢  Á”` œ-”ƒ |A ¦(zBð%@€"h RÐðÑ ¼@z0G´PCX¢`ŠA:0Cñ´Ér¯mPCX¢ðŠA:0C¶ ¨ ,P”Eà Z0E‚7(Ál[‚†/ÞVà Ѓ9й ø€*ÀÅ] 2Ð ½õ²¯-¾6&ï±ý]ILþ®þ^Gú÷Y'±Ç»„7ög³À¿/hÙ÷ƒ‰JÞ 0qÙ‚4ü$f^ =˜cR³PCXb’ƒ t`† Ï$ ‚r°À(ú7èoû¯ú·ý«¾$²ƒþ¶¾X(@æ(6àj¨K1È@f(Ö ”ƒЇ|A ¦($Bð%@ ÀÏRÐ[d¬À  sð5T€%Šd 3$ëÿ‰þ¶> † °Ä—[ 2Ð ›5H@å`B')¨¡,QøÄ ˜¡ZƒTP(Š"ð-˜¢@ Á”` ¦-HAÃO+ðèÁÅÔ|@å`â*_Ђ) ­¼A  ðÚ‚4|¶/P€ÌQ”mÀÔP–(ÒbÌP°­A*( pø‚LQÌ…à J0€Åݤ á ½xô`ŽÂoóŸìsË®3:ü]/ý¿n½ôëZéÿÔuÒß5wŒúðc¾wKü;1È@Ǿ&'k€ ÊÁ“•|A ¦˜¸„à J0€™-HAÃOjVà Ѓ9&9ð5T€%&=ñ¿AÛջ͓®|A ¦˜€…àÍ÷nû¯ìq«SLøBð%@€` RÐðÅÀ ¼@z0Gq°PCX¢XˆA:0Cá° ¨ ,ø¹À´`Š¢"oP‚(2¶  _p¬À  s ›ÿd[)hø¢e^ =˜£ˆÙ€¨¡,QÔÄà ¾ÀY(@æ(x6àj¨K@1È@f(†Ö ”ƒŠ£|A ¦(”Bð%@€Âi j¨KQ1È@f(¨Ö ”ƒ ¬|A ¦(¶Bð%@€âk RÐð…Ø ¼@z0ÇAo> † °D¡ƒ t`†¢m PA9X ˆ‹À´`Š‚.oP‚ÿ}m…&¯#ý¿q]ô÷:Òÿ™ë£¿×‘¸5’5ÿÒðïÍ ¼@zöïÃDe> † °ÄÄ%èÀ “˜5H@å`IM¾ SLpBð%@€ Ϥ á'?«ƒÞ¶^ =˜cµPCXbƒ t`†ÉØ$ ü/îo+˜¡XƒTP("ð-˜¢8Á”`Š…-HAÃ+ðèÁ…Ä|@ `)ÀÏ2ЊŒ5H@å`¢#_Ђ) ¼A   Ùþ'ûÛŠÀ´`ŠB&oP‚(l¶  _ä¬À d S=!xƒ @´)hø‚h^ =˜£@Ú€¨¡,Q0Å ˜¡xZƒTP(¦"‚†/¬Và Ѓ9 ­ ø€*À…W 2Ðа5H@å`¢,_Ђ) ´¼A  `Û‚4|ñ¶/P€ÌQÌmÀÔP–(îbÌPè­A*(‹ÿ‰þ¶lÿ»^ú»^’˜ü]/ý]/ýû¬—lùï û¹²¯-oP‚ýó˜¨lA ~Ò²/P€Ì1‰Ù€¨¡,1©‰A:0Ãg PA9X`Â/hÁ“Ÿ¼A `2´)hø‰Ñ ¼@z0ÇDi> † °ÄÄ)èÀ “¨5H@å`IU¾ SL°Bð%@€ פ á'_+ðèÁ“± ø€ Àäl RÐðµxô`މÛ|@ `‰‰\ 2Ð&uk€ ÊÁ“¼|A ¦˜ð…à Ѓ9 € ø€*ÀA 2Њƒ5H@å`b!_Ђ) ‡¼A  Ø‚4|Q±/P€ÌQdlÀÔP–(:bÌP€¬A*( $ø‚LQœ„à J0€Åʤ á —ˆA:0C!³ ¨ ,PØDà Z0E‘‚7(@f(zÖ ”ƒŠ |A ¦(ˆBð%@€i RÐðÅÒ ¼@z0Gñ´PCX¢˜ŠÁ´`ŠÂ*oP‚(´¶  _t­À  sað5T€%вd 3hk€ ÊÁ[¾ So!xƒ @1·)høÂn^ =˜£ÐÛ€¨¡,QøÅ ˜a` PA9°¥ÙÝd|AûÖK"v.ÿüïʸ]L${K…Ú¨{Ÿr›Æ‘ªÝî—3U‡I³áwûnS³ÿüïo—Ôܗˤé·¾w‡e´þrÔôN½(-Zu¨fù:ƶ—¼,çû®áûŠéGð»ÜivÜ>72¼Ž^rꆨua·]n‘M]•o7„åþÙ“ÝÕdÌQ9å´c;й}!ø)²º>‹º­Zðq.SÙiõÇŠ¸iÛäúÚ²^?ÏõÿŒ#Ó‘çæ^ºšE!­Ýd7æ1ÂÚÎ^ïàDvMz·YNg…˜¯ãr&>¥ÂµÇÍ«ÏkG_eN¡/Vg‘ÝŠj‡ƒEOé[—'×?x:ýé3TwEÝÑ &s9rö™K¦hjÅÑjñÑ×›)‹‰ã,Mzú§ŸÄ8Ë}Ú®–ÿ釯æ„Èe(¾ö-–¸|õ\aØâÓÿ)]øÿ.ÜÉ‘ºšü>Ñc¦œÖ‘p9r©ìv­Ïbi¨}µçvy™t'<ÿi@›§TsãÛ{ÍúºûÐnrÒ::ªŒ{ŸäN Ü6Õ5/–ØÝy?]̤æåEµók=å÷/±§Ë“³Ú‰kÈɸýñnܳ1?á<0*–z}˜·d¯K& w¾/ÉÿPF\Ÿ‰4`öÂmSLä´ä‰¦ÚÑñ\N\ÒyÙ´—bÉäÀog’~|uŸnú2Jðº±|Oê 2¶î.'³ñq®ZîçÓ#çõíÖ¤­±´qV×AaU2©W^þü·Ë¨Ê´]õgÜ´'cq9i;çɰT~?}¥Â: Ï.ù2/–6o¹>åö6%:ÑûXD¿_·#-ÊvJ•Óæ¼ÅUЏœ¹ˆžúŽ¥ÏãŠv·?›y†•ì¹TFå›.ä;QÇ+åNÞ¡ò?ý Œã‡Ü—…ÚM+Lci¶ðîo×Û”9áÙ¦{˨ç®ÛÄ»bJë{¬4JN,߇tå÷ó@nûó0wch®ëòæw‡Þ¦;÷üªíQF!±WÛY6t¦Ãí˜Èçwäd4"ºQsî}J«ç}f¹ÿµšÊÐ+Ôô6]g·£š[FËN­sé3DLã'Ôê¼ü‘üÏþÎÆñCîTÍ­s’¶ÆÐ‚ϱ‹2(Ú2÷ÀÝ‘eÄõKr$I}v#29n]o­íc~?äR=.ÍuœÃïGžAl7†veä¿æù²cödÜn憜^ý¾öõ?~ÈÛP5‹¡ôaó7eÐ…÷† ¶MËÈsò†ë»ÞØÑ«†Ý,/Èù~­\Îd©P1تÑÏ»Ñt»x¸ÿõÉdmv•ª–QdèÍÖ3–Ó$ãÆ´r:c1æÅºÏüø!—VtqAcY4)­Î-h“A_†ŽZxþ])¹e¾ô;XDÎkjݰ[NŽýóûé g‰Y¤¹C4%õx¨ñ>ëð¸ôÏ>b”\³n7gùŸ~aÆñCîŒnóâÖ=¢iº±Á@:•åV잟[J=‚Fvy–¼”|ÎÇ_¼¹@N{R›Œ>¿—ÛgH‚\Uc•(]­~ éL:É߯ە—XJø·º[ö§ÿÙölãcn¿ rf¡§Ýƒ®GÑ„:_º¦S9»mqHéŸýÜð/—¸ì’»‹ð×úÜë©‘3¶XE ~È•³F§Sñ–ŽW¦\.¥T¶qùrj;êù¸Árj]8§ùî%ÜϧGŽÝºc¿(rc·ÃkžN“Íj ?\J&‡Gnê3{…Ö9W¥FN¾‰;‹âøãï@©Ð8í|VQ­q>=Þj¨ê#Íé«Kÿô;v³ú,»"9¹ì;™£yÆrÓ 47ÂT´¶æ„¥…Éê˜im¾Ü¡ôO¿¼:SƒÖ¶Î—ÓÓ%Ÿ$4üø!§L79tƒŠÌ—ÕÞuACÏ-£[|˜^J_8ÈÃ~9’焬ùéirbê°;óû‘!×Rz;r”Š®ÞÚÞo†îŽy9b÷RªÑOÞWæD~cõÖ²9]ùØ$Û£7?~ÈY9·jšUME=†ÿ”õ™¥!—–Õo¥µ/¥^l›ÃH'-Ìï•$ÿÓ'É8~È]vfàHJ‘žŸz¿‡†ÚæVÛ8´”Ö¼®kØ=܉ºq#“¾gô§³íøý¬«Ã¶K8IÏ'± s5t¤ÊÀ@›w%ä£]tÔ‘¸mÐåúýǹUÆÆê‘T¯Ú©Å)OÒHvýáË{%ú×ÙÎíËäá{VÙ/Î8~K…~¹>g¶´‹äût¥‘¡…ä²_J Zòà–®«ùm¹¦ì §3ޏ·ôâæ rmΆä5|AC‚Å×~^H£-Ñ^…§”%|ÿÚܳq“üò@zËî^­æÇ¹“Ëï×÷  ’”D³¡[Ò(ºÝð¥CÎ•ÐØ#æ##µôÉo†.ï+Ìí»%B®a÷šÖ·GP‹ gÆ,K#—#YãÒv•Ðõ~†]ÚÑNôqÖ­~7;É©9Û6¯íbnü££Ý&M vlZþ•FU«t°>áVB¿W·9ØË™ŒÛ޺ʩ=»Í}3.'C.½}½_ÚEÐlv[±iô%ú{îÑY%´¥ÚñÖã ]H6ˆÝ‘QÎïcÌ­wÔÈÍ66§½˜³Õª§QàÚ:Š®CJȯ¼x˜•Ε–:¥œ*§P!»Ñ4w¼ë‘;ñ Ó01)œ.=›Ñºày*…»ã9½u ™¤¯1|Œu¥îìvù÷åÄõ¡â÷“;T*lçvëžÓÉpÊsªµx~f*Eú¿‰ªjRBíR†¾0¨\h¬tgcÇ»rR®5bŠ”ßϹào'³÷:…“tJ› ‚TÚðþRç&OŸðûß:Sô× kîDËù~Ëü~ŽÈ Ø·1!hh8;¼Êò\*m?<ª<ë m96Ü­ã'2n/æ+'c[º ü~ŽÈíÚÖh cãpz=¡ý‡*ÛSÉùSû’s‘Oh‰ùóñ¯·:ðßo9YÄ.对† >wÝSFl7£F.©¤;±vÒS¿'úÕ$whmÝ✜Òzšì~œÛ7T†Ü™ów[Ê#ÂèíJyí{3RiÀ¦~ý<¡1Æ©WÐ0Óa ŽÉÉ;Êçmò5~?Uä:÷dê¥ú„Q\›uúH¥º?Þ71ßü„‡çN¾óÑŽÚ¾‘û`­œÚœ<§ŒâöoÕ#g¿ð\êç…a´d ýˆÉ]RiFü‹ÆíŸÐ+¶}VÏôª‰°_Ž?Õí­žgsû.›.§ÛÞaä·©pЩt¶uÛËʹOȧWnòpõ l>¹kÐ:9¿¯(·ÿ®¹ ëí¯ þJ^]åuo}K¡Ë™6]·~Â÷¶§'íØöþôÕ䯹(¹ÐÑ)'”|/¿gö,…¶<°¸O¯'Äõ3q ûv[=Är¾÷>EÈ ðp‘}=Ê÷©H¡ílü2š=¡«·»WéWÛ‰yµôüŸŸ‹¹¸Ó·ô ¥_¤åqL ]³Àâ×c¾_²˜¶>1ù¶å·œËÍ•¥=¸}XeÈ Iöκ:,”ŒÛ2^K¡ûùeCÓÊS’lªzŠ˜®³m™ëчµ-åÕŸÃró5KøBÆmõÎ¥ÐÑ×ö$f>¦`¶u gz{äË•“µƒø>éü~ÆÈÝéUÛföí2=2ø°íZý¦uõÄÐÇÔrØ:›}/Å|}‘ó}¸ý“MŽ >ôöΙ/ ¡¥V?oL¡´s——ô8÷øÏù‚Åæˆ >cÜvë“Ö“ßϹ˽ŸÍZ<7„¸}1~9‰uï|Lù=Z>±u¢?‚Z[”Ë)èNÕ _~ü3¶ÕmBo^Ÿ*•,I!§üÍ'7»=¦÷Ýz ¤;’•Ù±Sõòì»,BŽí^´0÷e.ë°ÚÁ&…Ü6ÚØìŸ÷˜>Ÿm×ô„Ÿíé°êÛÆx9u?Y¾ké6.'AnÙÆï«s¤·èjÎÕ*“ǦÐì£uð“=&…¼óK¯5¶m &§oÊF ­Ãý|2äÜçU[0ã•6 ù0xp -õÛè*êù˜ïgàH®,{¾Ù[NõlW5;ðh7~È…§ÊšŒ©u‹¦<î49¤ÁÄù86ùÏó/âûh —9®Ñ,¬mÉÎØp7…¶®ßSýYž6*Ü“,L=èî/ó|¯<¯æ>rCÏ6ñh~“îͪúºA M›¹oBî>=)/²òКOp‚øóknük<àûˆ nðÇK µ›ùcµ›ž¦În4ûæ¢ê©ßäþ»Já•%c¹ñCnÉ÷ªg_§Œ²³KfÙèéÇ/úð)®Žsn¹¾jüù |¾ý®Ó :l‡»úÐÑ#øá =å=X;¸ô’3}ôªšçü@ÎïwÞ†7äŒÛAï½Frªµß‰Ïñ§O´Ë×6z2¶Ã)¦êVq¾”Èù¾“¦Ü¸żyëãåº/®’q›yóQ­ø²M =õ¯n’vq¤qû8ËÿÑWG€Ü°?Êé*%öýôAŽïÉiÚÛY÷Q£’A7_\s$ûv_wnV9Þ=Œ9!rrí†CKü®Ðb¶­7¾—§¿†&{Þ|ôçü°ß›š9 sp¾_G×`«Æœ¹&ݾ oZã m1ìøvjh ݼêq¶dû£?}áRïM¾™%'n¿g¾Ïr¯âWx¿r ¦olžBw,ªŽî2ûy4]àjîDÎûM‚Ö¿“ó}¨¹¾b2ä4{ÜÕ?CAXD gãsq¸ë¿ã#2¶‰8-¦Á#-,Æ÷ "Ç8óŒ}ëø>‰È¾æMX;LAÓ{´rÝæ”Bƒ—ÏÛy ü!¥[M™¨p¦Cù¼YD¦Y76õl̽ž¹„£ÂÓ.ÑìWâ6/Ö§P@Ç`ý”؇4ո᪠-è·¸­ÉÊ Z`\èr}˜LŽ• g¬àß¾~í}fÂqÌ·…/j:¬Ýó¢›¼b²§¹R÷æMAÄõáàúL ûm87°þ~9 ¿u¶Ó/…:¼U–îžñ¼^z\|!u¥„¡&oÚÄ÷oåúó‘;œuxΣ¯ô=$löꨚ±ÙlÉUó‡|? W:¸çàÄà]AT6¾«¾Èšë)BîÊö§«ß¬ ä¯W¤Ð¡ÉããåO»{è¦].TO³ôí/ï ÚÒ¡4ö÷îs‘ ·iGòº/&4Û–íü˜Bå›m–GÜx@lwÖÇKiNÊœoµ‚¸ã ¾×§^¦e[øº˜J¯G ïa½åÁÿ‡½ÿk¢ÝöþqìØ±c;ëŠ;v¬„P{ìÁб€(  ¢¨¨Q =ôÐ5**vìØßÉL8>ûÎûž}êÞïß}]Ÿëœgïù>3Ì=³Öšû¾³¾þ²×Ö9ì/>•j踺UNwX»¾¶É$¹?R<ç©jzŒî;SR‹^+¦Î û‹_¨ºÉ“ëö?B’MOÎN„ú/(mý†vw+æ-¸ûÎù.ZÏ ò´BÑÇsL™“ÖwÝù±˜ôíËk8SÇÎl$ û‹±ºQ%>nÌ ¦Ÿ—cVÜpO¦Wg¾¹®)&ãáÕ»O¹ìLÖýë‹Ù†‘¾¼o?›/èGF¸Þ[L'ƒ;\û¾+™D‡-ž·ó/¦N/‹Æ.w!o&J9uE°ŸÝ½æsãÝÕ£{•k“.¶ÞÉ~ÁÉ´p†àÒ%q1q>Ï®Ä}_…Q“CvM–9.âÆ º×ÉbÑõ êPôe³[ êÚÎ÷*ö/&½N‚q>+a䓾Ŋٴ{ß «ìoÜ2pQ9.± º™L‹¬ví2ÿYDo°†‹éIä·.„Ñ[¶ü?aÃt Ö}Ë$ˆ6êØ£ÎT=ý’—\D¾_ôý¶˜8Ê0¾îä| uÐõa+e4pÐÆÜÝø¾ˆ_eÒ­‹wÝñrRÐ{7b]´»‡Ñ‚Ò UOoáüoŽ ܽt.g…Œ8_”êžž¬Î¾Cúéc?'²Üi¤+¼Æç¯ÜøAS£4öäí@jÚlÜãÎn)t\x%G!¿C†þÃò”7î_¡Nbv¸óÉ ý£×’ÌÙä³Úòú„µ)äq¼Ýú;dèßn¸Ÿ×¯;\˜Üm7~Ð}Xæûãz^ u©m± ã°}Íû/“ïÐý»i œœéÁË›}›ã}«9:ùÃ_”Ö´ˆÖ8’BK?Ž2Ip‡j.vx¹Ý…Üô/Dóý>õçkî93:Z"ô]>£JæÓ“ô¹íñz)´¬¬›ÉÃ7·+æ¹ùˆ0 Ì™ÝéÐ&7~еuñüáx’^´èYc½2…8Ô>™s› ~_ÅÑÕ7={F;Ï–}­aÇtÙ·+|ñà}ð”©q>…Vû¶m´}çm:Ê;-ææ!›(iú¨–Òº^ÜùDб³Í…‹NPÝð`ÉÑË)ĨkÔj6ù6a§Y§ºÓöñn©$ÿöêg cl¹ñƒŽH.ò§6¬][L ¹LÛucƒÛûy øüð¥´ùÑb”èJI:¥®ÜÄédÐ]jÑð³?U’X¾‚I¡½ž¿>š_H^[ sC—òó|xÿ¢YCÀ,¦ÓÎ7§ ï„ü¼¹ñ‚nï4ÖÀó(Õ1ÿ¹Ï•ãZOŸÊµoÑÚmâñÊ%·jÞÕx¹ú—Ó©¡»¼—˜?B;Þü¨{çL uÖÎÝä}1Ü©ýËñïà ó…ܸAw?éÛòoÓ*³Q³&)Rèѳ‰Þ¾ÉûÂ/%ïžé·œÊÃH:–]™pâÆÍ·DÀÚ¥7>L±}Ø‚2…õ«2ÑqÖMÂÚyœZFƒFŸœP]I«õ†÷ÜùÐmßnf«éCz;y¿j”ä/mÒô&M|`­ýÀ,ØÛTÉûòq÷SëÂÖ|ù!jÌNH¡y–¿œ"nð¾—KÉ¢vÔ¹©’ óÙ 6~ü ÓÿY§¼iÑøw î#ö{³cé‰úb5a½éûkŠN'ƒî>kÃ{p? ‹Þø"a} -0=°o}Ï‚Š:Ñï³eÞvŒç›jÏt¼F_¹º—B¼g¿ñH¡WÇv§<ÿ–O²æ‰5Ö|w¡ã7¾®ú-Ì0¯Ãt­Žû)Òí¦WÇ;UrC\ç|xòÉàÛäp’©¹ÃXÉûÝq:#¿!÷ß{ÑÝ\7«b‡š¼ç¹ñY>q¾êÎĺö¯¦¤vújnбnhC¾ï ŒÚ‘§,Äø5LÌ»2Ÿ†êfœé«jÀÌ}xÎö b'¸¿O›Ûíy¶·SôhÖ!4…„eº­ãòé¦%ûáæL‹ë™dä< #+ëï¦+oq:twزÀt+UÓy§P½„àM5ZäSv™«‰ ?Ÿf˜'æÆ:nÞQJv~ÓDŒO!ÝKºß‹<ºèÐÁäü ºòA|º ®óÆlvÆ‹{®eÐ%4O)ÞHK:¾tF^>wä|“Í×óHzU¸õaŠ˜ÌO5=WOÉûÖ9sãÝùõn ¯KèJÖa>…ÞJ䵿íÊ#·¯/¥/ö¸R¯VÆ©•;+ óÜøAg~mšcDÐ*ê•Z)Ú(t×ã³gæûvUéèF‚Ú9í<:*ÉnïÝág2øñ;^"ŒeFløøÖƒZWW¹îä×%•뤷Í#ƒŸUЇÞï†ñ¾ƒüû¡.?ø¬S2êö«´Ú‹\ê9ªÕ‹‡ÜèÌ3Ö Œ–Ù[Mé¼”»N!ts²î5wŸeOútÞ;…Ú S¯ÁÕ\âæW]I× Éé{Ãøù î|"èXwÓ¯/f‘òqsï¶ç’êè¢ûŠiYJê ¨ïyß nÜp¼þ³?hShà†Ù^?fB7ôæ¾:+\¨FWI­kÓñè׸÷\Á·x‚¾àË­ðÁ®tÃãi¼U­¼á:r(ÿ¾áø££?8Vms¡qêœÊ]ShkgÉ—}_s*êûÇÓ¢™ÓÿguÐq~e"f°¤sÿñ¸¾.n ²rH"2z0j·3mÛM{¯ 3¬Wsãå_"Äÿxlh 3c<0öD#YïìB•Y›VÔ•õ7­í>qw¼Çóó$ÌQ«`ßi8OlnàÀëËr(ì¦î‰é[ä=e“Ð¿Ž¯Ðßà絜ÑÛ~`œ¦_¸ù1rhYõ=³VÕÚ_W 3¬[qã]øä“Åo¬W1¡õZßÇs1Û®°úá9T¶wâ¬;ݨOÔàA£ðºË}oß–ŽÜ}”BÇÎ4I^ÃôZy½Gg<‡’ÞÓ·%æhIo#|Ïv>e ÁÃóª&§“A÷èÀÀ/=¯cük½µŠÁsŸ4|AæÑ“ZJdmÛ¹—§Ã¨z=ñ³ZW¸qVC×Ûb$Í8¿áæÛR(dãÉÄ ®Z2ø§degp ó—œN~z6zíç|ýâÁÕµ‘®ƒ´dðkÜ:[¤y>;ŒÆ¾øæáÍÅs£% ^ÓÍjÛna¸yA<÷ÍžLùZGKÍSÛ9[9ñ~]aä´½×ص|] Ýõ÷‰+¯H¥ŒzÞåÅ#ç¥Ðà‚ËÓÊS²é®í°×ó#H?L P׌[:ùzO¾®„n [µödX÷™fŽ)´±Ï› ÛÙäf:¼}èh{úåCá±JaTùÄʲIb>NB÷`lZÓñÑžÌûó¾pO!ýçVÿlþ{ÓŽNÌìóg( Êmnºn.ÿ¾A7üâ5·À[™§§×Р¾¿2~ŒÃ«7Y´Òñ±oœ©)¶‹fÝøjXàÆºÄçÍëØleŒ½ùõh{ }ÀNHgÑ›C™³Þ·²#vªZy(-Þü]1fÿÞA7±ƒó§uû¶2Á¬“O ]¼´¿Ä×9‹ÖšÛŸ2ÚcG;rÎ.iû.”†:–VÄt“ð¶8•Béu»{j›Eµî””ï‚ûtëTâÓPÕ†ÝÙÀÇèd‰°‰~z+£_Ö<›B¶ä:Ù¯ “BÍ|“{ÇØ“[ßGîŠ[¡¤·MoÌç7èŒWuv?ýÚ“áâ âN³§wg’ÁdkCœJÇMVŽ/XÃ×'ÐqþOžÌ‘~ׯ¤¢þu[½íâàLª:ïúV÷DZy«á»Ù¡ô#N½òÍ~Ü ë×ÒÑçÐ%)sü£½Y—;)Ô¼èrVÍäùJ”8¼’#Õ\Î:Œ†þ^nܠ뻢駨„-̨§kv?Gã:zý‰ x¬­*Íö™´qou!”ŠeŸæ™Æ¿wнª²øê&æU¡q⋯)4ߥoîˆÉ¼ß¥ù¢»nJ®\?ÙЄûûÔÐé—ùÎl`ÚöôúH= 9ÜžW)ƒúÔü¶Æ¾bŸÌïù[Ý÷3aã Ó±lש_m4£µ®àZ:qqÊŽ>š-8¯  €¼Ú×ü{€|:¼Í×׳×0³Ç¨R•}5ô½–ׯnkÒ ßoÄÞµÈ0Y…TîR¤æ¿  ã®{%³Û¿ôý´±Úú|ý¹E:•6e§m‰u5‰PÐüWv½Gr:!t\\\ÆD¿1móm®†œžj:=û˜Fìn¢ íDô£qŠçœt}ÒŸ]VÄÏA×yºæ#s1cÕ$éÌYw ¹¾ÒâᥴŠõÙjn¬ó®‚vçÇ•ñãÝjìÌž³ÁícÁ,O ÊsI==Sâ°ÖÓž¯/B©Kユ˜±üøA÷bù}·Õf@ÖáHCeìgXÛ4:°ÙºYWGZqïWJÍF¡¼?5wjèê"ªÿ¶þüõý¼=7O! 4ø”9Sœy9O,Siļˆ…âûiï'I»Ö‹*|%ùusnü ÓOß,[Lñ»KoÝœšJÆÎǦX$¦ÐhSÚ¿áý|ª]™tÿ“œì’Ÿ]>€{>ÕÐå²Óv{–‘>Œ8§Ò­Õ‹š×BüìâæºtGä<Š<½hsXªœÚèj¹ï]t'‘+Äˉó÷K¥9…ñ;ÏMáëÄyôNÄ–rªR}ü©Ù9Ü÷¼Ñ©!çµ’ žºuÿä›JƒBïÞìBc;­®ìo<Ÿô¶ŸArzX%½A_>ïAÇù¶®¢Uù9Ë-.¦Ò0סK6½L&®®^@oN×È*ÿëw9tz[Öç«©×~[I§ÔTZP+$ÊçB²a’ZÜ;÷í´œÌºF…ÖákË.OYp`-±«äMt©Tº|åŽÜÉ´fy˺ºž"Ú¸Ú×Ñrj0sÿ¢ºÖüøA÷²ŸË¦´>먇}¡Ý¤¯©´éi³N¥½“éÊdÖhÓŽb 7¼³Aþ—ù)tÜþ†õ4òŃôÑ Òˆ[gI¢eoËp'ìHLÓGïœ%'·˜»Nõ)þœÄù§Ñdó“‹ý‚’hðºC5Ë-ìéâ»tÑ>+9?Ä»:îyÚL½¿œú~wH]Ͻå·Ñ&‰B´`Ê&Ø“®èƒ M—ÿe¾ÏHV"dÝz£æl!{ý…4ZÕî[R“¤ ÿIûr/¿†ãåü< §@—8Ì7|¼”Þ¬d¨ÓèÅΆ¹Lj"ï nGuj°Žlò¿Ì ¡Ë¯ÿ¼ýŽ‹R:î4¥IYÕkïðkgbÅ8Œ¶è4wWH-8«Œë_‡{ÎDÐͳR—¾{"%®^J£=MŸj•HÁÆáE·ç,¢ü-7¿—O ¡ò‡ìàî‹:ï3ž'v¥7„<….¹¡Ï¹û/€nO'¡òIf<}.­ûùÐ|JZóÙè®o¿.ÉÍç qܳɲG?7ÇÓ÷ã·zTs$nÿAˆÁÿ–7dFiLr¯xâæ‡È÷tT’Åðšæ¼mRß\Ü•â8ý2(®×·Dºp2®—Û/g¨ˆóÙ æçsfpã]W½1[\…odê  »>]¦îú„í¨q\³¼·Ã4sâ Ï E±³ô»‚i¾ÿüvg“ùywÆx`óY;=é]Ôwõ,\GZ_×òèVqû–æÕ·í¼Ç;˜¾0ºÍÈå£àajØ’ÇŸ¨éfø»aÌ£šîƒ|ž¦N Ù•Cî¾ p\ éœ&Ý£Ô9û•¿÷M ßù#Ð&,˜ÔU—ÞÍœÁ×8®ýØæÑ¶ªiœ¾€¶!/™Û7i0õŽ ¯ßñ ·pÜ<}‚ö¤É;$ázåÛ¨nWk5)仂 RçòóÏÁüz0?ÓBŸk¥-ÔÞ{â ßùT0·'BK0Õ\*_r(ŠŸ·Çq¾ßv/ËŒg(º{´È#s!ë¬M ¦®kO= Iâò´Çe½jl·n,Cç/ïÖ_Džãw†Í ¦ª+vw¨?šgޫո‚—Ý.ORÎú°Ë×ûÝwžâ4K™ÁZ;jØ{êÓ=Óƒ©õ£={CVpãlR",k_9±‘y,hû~¯=;_ݬ®E0¿.Ë'ÀqÍÏZÌó»A³â”ò)Bªµ5åêÚºÁÛdwM·r>Ïã¸kÍ—ªŸâ:ªtfW4Ó¨ðÖõ|ã1ô&üä§#'¨‹>‘QfÕ„k;›sï¯:÷3s]6ÚÆÍþNg÷_p ÷dW¯n߃øõMî¾IqÜ—ŽqN¶ ÑT³Ÿû¯´ 4Tn*È©ÌíƒmÇß_WT÷åÈÝ¢éÚŠOYâ3öä¿ñRæþgAôü¢_áÆÜqj'nÛ½ÜÝË“}z1e-®7ߦޏŸßujÐ|öÛ±Oì¨_â…^KC‚ˆ:CšóuW»ÞæÿZ[û:ï+kGçêIUgæÑp}eÏÇyy‰Ð¨ÚŠ’bÏkïÑ«zìJaêDýýÅq+ÓÄïŸT4ýdÈgì(ª=;£ÄÏëóqÇ™" ~Àõ^Kv¹¸×Û~ÇœÛU´À¨…bâ/{*šš\n¾!ˆª3&Åóu0tMvŸ‹Ü‘t•ŠUIß‹µŽtvHÓ"yp±_/³;qßqR§2ܤ~³«´>9ÖñQœ3½/ZY/0ˆøßQp÷Ç¥tS¸¬ßíI}5ñÂul>$Ÿw…Š™‰†ˆ©Û²q–=%Aü>>>NC×&}ù§G/W샚¦7 ¢%5:|ª)ãâ›Çm˜\¿Ö„œ( ÞÂEŠi‚ïÏNá“‚x¿Hî8#E‰ðþWv¢%ŠŽ^f gbg¼{Ñ·Œ¹§VLâžKŽã¾_=i™‹í:o\oÑëÞ3\.ÑÊ‚¡ãJ¢©kçeÒãodüóÍ=÷Bèâbt‘zÑ0_JÜï d$/齋o"÷+4nóñAéе¾Ê–v´¬lGêÌ8ÿ=3‹»¿8néìB—®Q¨Û£û. ,E4»¾ß®I_dô _C Œø<ˆã:h{k'®7m`Q±®·¿õ*Õ…þ ëG$Ñox¢É7–60}Á?ÏÐõj¾äà¶«‘d48÷™È– X›ø6Aüú*¿Ž‡ãBL“šùXF’µ;­§×Ñ£{ßwî®Dν<ÖmÎ'„–o)®Ww¯'ub.ƒëèuê“B³ø<µtúál]DW>æ¡ñû:øïè:Fí i6ï=Sµ¸}ô˜­ ¨ûÅÿþ¿¬“㸲­=[ŽQñjÇ>A4vTrÈÌxnD8n]‡÷š¶uÎÿ]Æï‹áòÿ;·Ó¤ì²®s÷¢±ŠsOóqÌšéÖƒx_inÜdб³×>‡Ã+ò-ç.£‡ÏÙQܸ©qÜ\ÿã¦+ùý1´jÁþ—Òeüú%ÿÜâ¸þãfon¾Ï“î(§Ô¸ˆë(³I™ô£YÍéâböZâ@c6vq¾!ûKü3 +NN~ZµÃcEE^îÑCÕ¶L-£‰ú•Xn<8nUÿ‡9ÅN Ÿ˜ÜÇžÂæÊî¶K’½~àׯq\gßËUC*î¿ÛöÍ¡‹®Êhà€!Ó7=åŽḒIìŽ1O²X—æv×uÓîò·OÛ·ÝozØÑÔÇQa%'eÔùLûbÇ™ÜuH¡‹-î2®Rpõ ëíGºѱîýš<´£víÒšEyˈÓebÄg>ãø±Â¬¨sÏ~V÷+3¬ÇQ›Ú«‘këY“MVóë›8>;„]hN#“ØI[ëÒ˜“S.J5›´ZdrȆ¯i†O!éN6P܈ãǺcbδ‘Rï+_K£¶?­Û¼xˆ¾˜o¸½æŽeäðöDü„jÜó'‚®ÑÕZö²Å[ˆ~¸ddÒèì÷[‹"ÕûøçȰEdô‚ö[ÁÇè¾æ_˜ðìøfºÝEjF÷B¬Œeö;+öS¡ˆn1)AFú2'‹ÓÉ Ó½³é×W³‰¾^f_ð4ZáØ»Sñj)é·ý\u¦Ù=6³[ƒx¿ç™Ü¸A×üLæ¯36Ò®¢ »>ÏÓhbçÊæ_¾®&±wC 'ê4ïdÞf©/¸d;¹ñõ!tïçh4Ÿ¹ûÏ4*?áßä„Ì…Ö<\S9ÇæöMywn½ŒœÎ(¼D¨>Ü´×™ÈuôrGå+Û¤SpÜ‚»w©S{v…õþ?_Ç]§:n_’„†{Ǭ4K§³9v«z?YÈè—uk8ÐÖ¤çñ¼°«7«Ûp÷Eݵ¦=…EnkHog>2F¼³AåŒáw&¯ÅÖ¯Q3ˆ_WåÎ'‚Ž­Â-ZEF%Ûk†ÌMçÇ{=SÏI¹'üƒIýç]¹Ù,ˆz ¢ÚÄNâtRèz I¤%Ï–Ó(ýÜÒéRû!·•1äñ?ò%Ï:Ño«¢r¾~‡ŽÛŸ°Œß‘Núí\ww1†ß‘Õêã;Ó}`?ÉéÔÐU¾yza£\7ŠXÒöݱ3é4pHÄ€y®û™ ]N²¸äLç®Øo¹=,ˆbå}æ›ÅétÐ~÷%yér¡qj:õüòlw¹ß!fXý×YwÜ]ø¼D× £¦—ðãwºDhøÞŠ1žqdâ£tr‹Ÿ6Y~ðcX°?cèD·«Ÿ´Ê×£Ðq¿™ImØÈžA³Û}?èË~×å3ôÖç‚ Êz0ï•]oî~ ¡ãâëHÞ;ƒî%Z<º½ÇŸ±Ðn̺{Î…Öë7£Î8É. óã·oª'³ÎFi‘9$ƒšzήV_À÷L+ÿÜÜ…Z§G?¯ôþ¯õƒº³쉦0c Ë>L˜›A'Ö[w‘1†uUî÷2~Ÿ-·ßIÝÛ >O¿_À¹¦Ÿgöî«gëNì.ð^{ƒèóÁ¥&–Ü>Ot«Ïþz1jÃîÚ˜,ͤðêg¾5ÐF2ÜwÆún17¤ êý Ù_¢ŒàÆïl‰?oýçì`lµ#+õͤþS],Ï_`ê>ðsšZy1¹žîòþ`luœÞcän¿­:Ňy'’w2Á/{>Þx!“DÆ›œ“^d’—¤ž6èJõÌß·þžÄ×%ã¸ñƒîÖ öƒÑ‹Y¨ˆ~±D“Iå‹/Ž6úfca¼2Ú…®¾~^ïZíät—¸ýž"èòGõ¿¾­p7³²WÒ,§û™twäœöÖ?.1§æmZ=KæLì¯$@WÉâò’ˆÞÜ}‘Bç™]¿Ãöq{Ŷ[ê½Ï¤ÕïnžbôË­-œi­~ã¹á}àö/Ë ë9ÿÅÞíWö1"ý]³hÅê+F‰.3úr²‘3Õ^¸[ø±~09¶\õ@ÕÁÜøAwçIò‡í0úåÖYTMšåÚà cÚ>ìüÓÅÎ$éº:Õrl0¿O’Û¬cïË’Ç~>Ȱ›ôeý³HØÀܶJʆû½º3Ýß=·÷ÎÁ4•ÝÛTÈ_D‰ð§íê+o¦hÙ«Ñã­²ÈBeüôœô*sXÉþ`Ä…:-`Ÿ¨`¾îåî‹:î»àS7©õ²wó³Hž™Ñe'©~½„Æ>v4/0˜ßÚ•,ãö ¡#k7Œ9ÄP‹)ÇO,Ë¢5{_»U¹Æpu­˜J&oÞ;åX0¿ßc4ÓûOï£?½Œþô>úÓûè§÷Û­è ºTâ‰|J¼~ë™dÂ{»‰€ßg²ô7ÿ[ßúr³o‚ßúM*y\C%ÞëMÅ{á²}” }ºY¿7Õo½'…¿yâjø~Ý_\ßzv³Þo‚ßúPFò~&ÿlþoU«½þÔ]ê®ÿͺë¹æbë-6·³µ[O±5[;±õ[#±µ[±5[û°õ[ç°µÍ?[=cÌ?WlÍÈ^‡(cµ¨_DÀh1‡H@$(à 4|P±@ tÀAÆx5(æ:b …Àc¤@Ê€’ø-0Fp ˆ¥@€`e¼†\–À(˜"Y/ åÀM d ˜ ÈY)P2`öwö}Tƒr`Ž)~@ Œÿ›û>údú>þOôÉ6ø‰üî·öõ¥Õð Áx%ÐS$kàÔ ˜#aˆ $«ÿ„'­÷ÿÅODÊ{ˆ¨XÞw–í}mÍ'$Ö+Í’ïÑèÁ{Dzý«­ÿ =ÏøAŸë«ýV/ü™«ù·k†ôzáºV`ëCðÿú¼Ì´Nøgž1xÙ³ã¡3âÎe ¼€”s 1B`‚àa¤@Ê€‚‰ø-0F` ˆ¥@€@c¼†:–À(˜"Y/ åÀAI d ˜ @Y)P2`†€%~@ Œ¼„@"A) ˜Ùo á›%ðJ ¦tÖÀ ¨A9ëÙŠÀç´ÀP$ ”¢ ðþW|ìe ˜ X D‚R @ð´Þ@ÃRKà”@LX­Pƒr`ŽA(4ý—>Ñ‘ ô O4ë©aðûz²þWy×ÿG=Yýþ/^ß1ÖCCÝ“óªð}ŸmˆŒy? ¶¯sY_Î?UÀ÷n¶á{6«þI=Á u;¬ÖxþÌ5HþqæþYj‰?k<ÿõÁƒ>’¿Oì¿Ëx Ô,P0E³^@ Ê9‚žÈ@!0A´R eÀ Qü€#8 D‚R @°´Þ@ÃNKà”@LH­Pƒr`ŽÀ*2PLd­€¨@0CÐ? ÆÀB ‘ m€7ÐðÁÙˆ k+ *P̼EÀï_ñ W0E`·R eÀ ^ü€#è D‚R @°Þ@Ã'Kà”@L ¬€¨@ aØo á“‡%ðJ ¦H&ÖÀ ¨A90Gr(&H4V@ T  ˜µý?ŒßýÃD‚ÿ¸Ïê•ýÔkUöñÃ0ø‡±>ôºß<1X¿Uo e¼˜Á^Æ{c|W­Dò~blò5"Þ#ƒõ_-&ì< ï+æÇû°–$i+Þ[Œõ¨WƒrÞ/ÃàǪZÞcŒõdµÉûŒ±ÉÝ ˆ€7ïñÏè5f¨ÃØúåOö§“ý©ÃþÔaÿœu˜Áç^ÅßöXðZöÚÔ„@"A) ÈÙo áž%ðJ ¦€ÖÀ ¨A90G@(&ŽV@ T  ˜!XŠ€ÐcN!€HP ¤6Àhø j <€è€)‚¬5ðjPÌtÅ@ °(fì¼ðZ`Œàl <€è€)‚µ5ðjP̼Å@ö¯øÜG‚R @`·^@ ÊÙ½7ôb …ÀAß H ”3$ðZ`ÌÎ- ˆ¥À Âx5(fH"à´ÀÉC$ ”’‰ ð>±X :`ŠDc ¼€”s$ðkûW_²ÿŒë…ßýÆÇ•õ%ÓS$IkàÔ ˜ÿæKÆzÞ—$Q+ÞËÕhA9ïOf¤@ ´¿yºÚ)Pñ>eÆìž Þ¼·«˜²sX¼_™Œ÷w-¦HÒÖ¼g™ hø¤mö›Ïk$(ä9ëõj¤@Åû—³~e@ ü€æŸÔÃÌP‡ ¸˜Æ‡â?u˜Ñö?Uƒý©¿þÿ»þú¹ö²âŸ=5ÿwšã¿(d¯ Ì H ”36ðZ`Œ ' JAÏx -P0E@´^@ Ê9¤È@!0A°´R eÀ ÁSü€# D‚R @`µÞ@ÃYKà”@Lt­Pƒr`Ž ,2PL…@"A) @Ûo áƒµ%ðJ ¦ÞÖÀ ¨@ë‹`î 4|P·@ tÀAÞx5(æúb …À À H ”‚ ðv¿3’ƒÈ@!0A¢°R eÀ ‰Cü€#‰D‚R @R±Þ@Ã'K 2 Æèÿ9/ØHP HV6ÀhøÄe <€è€)™5ðjPÌÿ“^°‘ m€7ÐðÉш h@0E´æý`e O fÀxHPø›/¬x5(&H´–Àøñþ°¥@€äk<€’÷ˆ-$d J ÆHÐæ¿yŪ€®ç+^@ J ’¸%ð2 å“º%ðJ ¦HòÖÀ ¨A90GÒ(&(¬€¨@0CA ~@ ŒQD‚R @±`¼†/,P0E!a ¼€”Ö…†u•ýVo±u‚áwê­ÿ¾zëϜןšëÏœ×}ÝeÃ?_þï°@ tì¹ЬPƒr`Ž'2PL쬀¨@0Cð? Æ„B ‘ m€7ÐðAÒx%ÐSMkàÔ ˜#ˆŠ T+ *PÌ`EÀh1‚­H@$(_à 4| ¶@ tÀÙ H ”3jðZ`Œ - J€­³€”3tðZ`Œà. JÁÞx ø-P0E"°^@ ʃø-Ÿ$,P0EÒ°^@ Ê9’ˆÈ@!0AB±R eÀ Fü€#ÙP‚B`"€H@$($#à 4|b²@ tÀ‰Êx(fH\"à´ÀIL$ ”’š ð>ÁY :`Š„g ¼€”s$@1B`‚dh¤@Ê€’£ø-0F¢ ZPHž6@ ”@ŒÙy-`¼ è€1¬ˆ7Ѐ2`Ф+ PÆ®E"[ ˆ…|b6"à"A!0aë, ~@ J1¸ˆ7Ѐ2`Ф. …ÀI^$ ”’¾ ð¾°@ tÀ5ðjPÌQ ˆ  V@ T  ˜¡x? Æ($„@"A) °°Þ@ÖÀ(«¿DFÿù¹3íßüû ÿæŸ ÿ)&þ¿‹(²]’Lçú0ª6;^LS Lb Õ²ÈðÏê}+ÿH½Æð}ëèÇ„§ —ªº×Oa,M•÷ÖÏ3˜÷XE+–mN½m4ó=ümîd{Î÷UŠó|‰^›ïgr˜a]í?Ë¢®l{rŸëÌ:I¶çccWrØïkŽG0‰Ó¦r/çk&ƒŽëz˜áúPfÑ„QûGúÌfN†²†˜bj 7¤ æûs}ÔÐYí œ_:á3àlxZÑYTÉçÔ¨ìV1L kËÝHL¶e®S9óý@-¹þÐY´‹èSz„Ñl\¿$=‹š ì(ïx'†1ø5o­´½~§yÁ|ÿÓÖ\ÿˆs%Â¥÷C~®ßq”iôþ¬¼Éý,2çâ}£¢_EBý¬°³‚)fÝéQKw¶Ôßt•vw¶NlŒ¹Q²åÝÚ7YÔ«ohµ|a,S)´N+m®‰Ò»ß*ræûü™éuBèÖ¾|XqŒ‰Í yø+‹òûF([½Že¸þ"NÔ×gØÈLï`¾ßy½N]aöŠGú2KôƯÙêR0²c øÏjx»Æ\g:}­X¤LæûˆÕçÆºSOJÆtÖù2M÷¬¢0A6e6ÿ$ï¦f þîË®v¬ñ2˜÷iÈÕt}½]|—mòcÔÆí¯Ù'›æŽû:7g¯š1ô :~«gå^W•?èNû ž7:Î Õ´d“eCqdø{5S{ÃçS݈u»íP'„÷ó¨ÅtÃF‡ ñ 8Î'03÷zÙ¿«.æ}Cø¾ý#õ:5tiû®]t"€áüK²iü³5·g&Vôa)•Þ~Xï-Ôëtеh1µêò6LøÝó•7^ͦ' 6CÏ'VôAzÒnÆðª³B q†‹d‘%B×°'+¶2i‹;­òM̦ÙÎ^,«™ÄXÅÍÐ]ïL3—?jú~eݳNt¹ñƒ®SÖåî᧘XçkKVeeSNÆáŵ“˜Y} q&Ÿu%õ«†ð}æ9_[!t«õÆv§݇^÷eÓ$´·NTÑ/Æ{ÊÂ/=²C(Ë£ÊpÚÇéDбoßœö2f][¶sn6]4?U#WÌDìñ8<ÍIL¬Û} ¡n}2žOæÆ:uó ïxÈ÷E‹G¼É¦SW¾¯ÑlKf~E*ÆÍæJGÜNH©#§Ë"û—…pã.ƒNœõõˆÃEsPi}wÁ÷lй1ONÏ“™ôÀªžê\I¼€u –Ó¹ñW;­ˆÏt&GÇf¼|/cØ.¦U´”",}^8=…‰´·xeïFÛ'¦œtyÂ÷çΧƒN5ß§Ê‹ æÁfu•*ÆZ2)êrè@L sgõ²1ö[Ý*îËšÅÇ뤼çÎgt¡DØdë¸ÄÌßXKú¶Ž–¦¿¬ë°¶³†™1ìø–“ÝhÄ­Xé"UöéwÎZ Ýx+¹û÷ÝĄֳ́U;à~~º¼ûuT*3DßàØ…–79˜Bú6]Ml¸ñƒîm½2ùŽô`Æ»ïÃÊZ3-óûÑd¿IZE¹KW¼Oº‡ëL:´™÷/‡N2.Ùíý¯`†uƒYØ[Kk.´5ýä–Æ\ªu¢R/3üé»8§²œÏÛÜuê ‹Êhßxþˆ¦Åç''õ×Rá™ÍóSÒƒï²õÏ×­-:ËyŸÎÚèb‰pÁá¥K¶†0ÝcƒÊnÖ’Ôg’ßú.錃ýûåîJ1©ª_™i)çý!8º‡é¬r3tÇ‚p—Z:õ剕d_:Ãõqu£v>íRnö“óï;wBèÄU[Þb^„0“'5³ÿ8NK³ý“>¥3\n<Ÿ7|w·—“Þ.¹7ç'.‚N¾{­´KO9“2ÞîÞ£IZª1üÌò"‡ Æàóöô®µ©œÞ´ø ¸JÜý”B×=~òØ£vrædü„jMfiéåÿÇs2ƒo¬Þf£µœæÝHx¥äÇ:6KU9&g"vW‹ ž¯¥îgF÷o2,“±œRë}7ÚÅÚÍv‘ú.sã]ÆÛZ£§¦É™–›•ýíµt#5mÏPy&3à`‚èg-Wú1Ùl´ºœ°íÈrrãÝônjÞø)g¾ÿ²´‹kÉG¬ºÚ Zcð•ºÞg˜ÝîrÞ×–¿K%ÂBû«ÙÝú)˜I{¯÷ZzjZr8–øó–¶¢ãÂf›=†¯–WøØèǺü¼qºúÆŒn|x :CK®}½¢úVÏa6ö`±éù¥*çº;Ëiøáïû‹øïèæ×iºieÓ|ÿÆEoóµTý´Å¶sýr*ò{Áü*ó;Ù µu`ï?~ÐÍX}pÉ9ÿ0Fwjz§´{Z:ë0qLsQ3ê é°«Z'Þ¯ùïz½‚#ŽÜ{¤†îN„SßVÙa̰ÇÍF>×’Ç&›eù{röë§y%'êÞ°Q~YO9íjÎ,ÜùtБMòÎ¥ŸÂ˜[¡¬™–f>Þ0¶}T³uÛPÑ‘ÖÏÎݳÌLÎ÷¹äþ>£Ë%Â$ï““Š»)œÔé/~h©5Siíó¢&ÿz]7æ²å®íº3@N/·ûêòkØsã]ì—ºêNW2ߎfír©™Cº³¹]ªä2f¯%øBp ¶ë^¯ÙrÞŸ†?è:È—ºØ®S2GØv rhpÝrÎÍ‘ö‰–yÊ }عñƒn×”ìè 2%óödOAT›Zëð$ìîÌ\f½û€ÙöN”úÈõ‹ç9e7øÒKÑk.7~Ðq¾‚Jf£]ßYæÝr¨éÖ¸;7•Qÿ ¨—>Ÿ>Z“‘ó~áó¸ñƒî1kïõFÉÜÆv Í¡„:}—Ö{RáSX´ãÉàýQrêR¤n­œÁƒº u*wßÕ4œØB=_0"‡dÁSö?¡)µ}h"w¥ø¸{‰Þ'åt´G— ;¸û¢ƒn¿Û=œÑ·'’C~“¥v_V>áû)ºÒöߪZï“SãÎûœ¾ñãw¥D¸óТü×s™GKWVÍÍ!Û7?%çç=áû`‹É¸¤á¸6^rj²oÂóªý¹÷V]j[x…3Õ¾_™çœC‹{ùä,ù„¦1µç_:êB+'°NÅÈ›ëã­ãtBèê¤õ÷_°/œ±¿õiÍÄ9´ºî³ÐmÝŸÐÖv²¦3UŠßÕ°ç:yEÿiýøA×ËætmÉñpf»Ó’Rç-9Ôq@½3½š>¡Zï.zêH¶ú†·rŠzÚ×¢æ=þýƒŽëëÎäÚ»s_µv)fZù ]qRÏpÌ®Ç Zpf˜œ¦}©Y÷‹Ë+2èªõ í¿Î|tmò#èxíý!(~ý˜:Ä 3®ôÅž.0/‡öSí—}å.áǺ.q-s•êp¦iùž¥7Bs(üƒªñ÷¢Ç”°©ä¦¼Èž&â.6ÀûÀûpsã]ô–£wA×Uðj‹ô2î˲çÊú©éãÖ©+ßµw ‰­K>ЦËéÛ9ãŸW}8ÑÕáÄsöoOÄçP¥Þý–¹ü˜B=ÙEzÓ–5¦”Ó²O5ÒÜ¿ñyÇËd‡3}^WÏ™—“CîCûµêô˜ª\yèõSGR=ËÞ,Çx/œRÓk¼Šï “8Œ°³Æ}œ¾qãÈ5÷rhªÖS¿÷1ÅÆ}+Íw"|ôâ”Ó‡ã9Éü¸A÷üp±]#Œ›³üÞŒA¯rh@ÀŒ-+W?¦˜ói;Ògb»N7A|OfÛP—q:)takßn.ÞΠ8ëWþ-‡†í=ÒÊyÑcÕzÆZ'ÚÛoç 'ä”<<µõN'ƒÎq~õ“ Gü}»4ºZ+—6T»4ãÆØÇÔâÁ«ÆÒÛî’Óuõã ÎŽ|ÞƒŽû gf\<¹=·Y.µ›Ý1ýu¯Ç|ßd*úúŒoþës¢ƒNߎ´ þ¾;?íœKY!»Æ¶jö˜÷7þ—ó 4}ß“¯[T%Bå¾-§–?P2“Z„¬ži‘K™gÒ¨_%d¨«ûýtÉfžçÃqÓÞ=ç☺ö3ÃoW‰P2}ç›Õht.iŽÔQPZBÎMƒ kù;Ó™ùúÜX&§z^&s²îqqLùG;VI”̘­áÎ7§çÒ”z_{Í-!C~®ã3<"˜äþ)úñƒÎ«‘ß”” ç7”KQoSB&ß´J³v¢Í¬ ZS¹Á_€?èbco*Æ–†1—G>/Ï¥aò,Ÿ+a%ô¬ë /Õ 'rѨÊi'ÛÆ¹1÷<Ë ãÞ·0†óÊ¥ƒ[ß¶ëSB[³†²NԻña5åôäIËœa/¸ó©¡ÓÛoOc|X{ÃC¹díø¹×› %TSžxîÕ+g»ù‡IK¼¯|_unü û´9¤õÀ¤î×Ü¢SÐí(©ÙÕ±„÷»óõŸœ÷WšÆßµaí“Kª¬•…2íe†GæRûžƒFN(¡ï­^äF|ýÌÏcÍàÆºŸÝÔkO e2ÊN-•©s©d|®ÅáÞ%´áÓ‚ÐÎq‹©y¶r–Ó×Ín1«sã „.¬ºöµ"sß´²Ô!;—º—(>mjTB'›u‰½3m yŸÚ¿` Þ#Þ‚?èü¾uo…‚Ù]‹5.Ë¥!íÛŽûüˆZ·‰/w-^L_»})Ä' Á·‹?è¸ï@9ó9âæîô·¹T´àňÏEx?7ÚÔ~ªÏ¹ãrjwèÊÛ.ݸóÉ {ÿ¨wÏç‡åL6²zßÊyÔbMVµ’¸Gt,³•Ï<3WÚ:`ÐÆÜÝrÞG”?èôÓL­åÌ:¶MtÃ<2‰Û:æ`ø£ Öõƒ^Æ:HäTÿæ¯A¾Ü÷:®nóÕ€Ç<çå;éºÏ#ÞÿÙ¯Ÿ[­\´âÆÁè:¾Ãž¤ZTëÂÌRË>5 Æžùezã#:Ué£sþF':j¶aâ [9yÇŽÏ“‰§pã]–Buúet0óv"k…¿)›àãò¨Â§/cÑ•€+cåÔâKÌÃðqÜü‚ºÍ¦í†Þ± f¸üšG[½{Ýaú#šµ{XÔA'êù*ÁRs9qóZÜ÷¾ºÞ{wg¦\bîb³4Š·ßl3mè#²ŸÐ1'ÝÌ™ïo.çý«9ºû?Þ.ºÜ7ˆùõÔÜv‘4T²v¹ñþÎt¡kdºŒÉš|b9?ï]‡Þ‹í=ÏËÛNm£kÉ£ û5Õ{T÷mc'Pä¼7Ï£†./(ïýÒ2Æg=ë‘GM'½­üé!í^àQ} #¦5§ÅÊP¼ê^?wF 7/¡ƒŽó >Å$foÁ'@}>ÀÌ}Sü*5sL·Ú•,·1¶Ç ùÿ¢K„“–méÕ¢o S{òö9m…ÍìõetÂC2ô¿XðøˆÔOþ—ù/tggZOûØ=€iÑ8³ŠMI]yÿ#_þ¹Ëlkd¹ÿüúrsã’Ì¡ãµO2 Zö8)ÿ’Çù>|HGOÚÝq½+ű娿œ÷ÑÅt\œògFy,Hž\7ŸŠ~–M9¾æ!]ûÖªmÏ,1]˜>bLÁy9Õ=#éð^CÜøA×£g‡iǽ]Mû|j‘ÒoÑCrÑþ¹ÐwKÿÝQw&El5-½3?èÆ2¶uÚñ ¿>n@>™õíú!rìC~~ԅίÝ3'BNì,Ð Ü®ÜøAç̼^õt·/ãÍ6âϧa¿vU36H>Ï´ÏÖ t¦Ý¿^¹QNÛ¯ÝMß´¢17~Ð±î¿ ¦cÊÊ?a›O÷o½²iÐâ!æó7‘Å¢ëãåTÉ+z}“æ­¹ñ‹)ޤ —KÏW^v¢ }Ž4zÕMÎû?uçÆºVz#„Ã̬9UŽìÌ§Õøwý€PT ½1Ý™Læ×·—óýÑÛsãݧ7_|˜]å»zóϧïkÜÓËr É[ʧyÐÒ{–]36èˆ÷K§.ÛëLNˉ󫘤?ºê[š^ãAÖ]Ümo-XÔØ/ÜQG÷Ìh×f· •ܱî“u_Îûãpþ"èÛµ´jvb =cmй¢oç³&é*|ÅÎÏ;^6ñ‡œ¸ï}n½U 7ϼ‰¶ZÖïbP@|#_Zö×Ñð¬þvßRÅÿ½Iß9ÕÔà‘Åùçg9_tœµ'©ŸîjR@*ÓÉ’-uÄ­#ˆißø-½wÔPÞÖ6“»jèâ{³Îê;ˆ{_ È®ûŧ+ë*òÝÜIK†Ÿ« Ýù‡>â}R 3ÓOÜ{ÑÃÕ?¶Þ, îÙ™VYy÷‰ÉÏ>±g¦˜ôi­‚Œ‡Wï>å2çCaÄ”cÎÙFî%®( /\íþûd24öÆb­ J-ÉÏ2W‘ ÂzsÎJ]×—jš÷8Há^ìvµX¼Åeêû4uîä[ºÐ–µkmë« òå¬sçï$„®–㚦7f¢>ú¦€š'=sŸŠÜn6¿ß̅ƲöN-†ú?褣™Ù78Lצ¨<:®€2Y„7»O‹­Šžr¡ØÅZß{?å´%þv„S_Þ7ºO#ÕÈ>Bœq½oÖ¶öšÒ{dðqé–Øáí;9-¼|ã®ï× Ýh¯î¥Çøuˆª¬®ÓnäÕ{dùѳ$`”+­«üzΗ¨}^R'‰÷ë…îºX#92Á>ŒÙWZºõù°v ?ÝÈVµÒNÞLAúéÛ–œO”Ç›wÚ3ÈØŸ¸yCÜ”åo+M¼G7,ßx¸„v¹_¿.ÈûK©K„qštÝ ºc™ÿ:./Ü£ãëæ -¥ûüËŸ†+ˆóàýB¡ÈÚŽõÒ¢ÏJñgFƒ…ÕsïRÖ6¼é2ʾ±kâ! ºw°¨÷©³Üx ¡ãÞëS¤©µ¹¥}LµµÉ³þ‘»üüðR:œÐ%À Ï —¯x)èfM‹I»*# PËkù=ðÇ«9wy¿[w Õü´}7…a‡7µÁ_4ˆë§w¶ˆ* ¾óë–ôo{—T-G/³˜’KÂÄ®RN]Ît6•b‹b–·ÎZ@Ó¦G_}^LÛ·ÝØÝn+ Dw,Ú)hƒKÔ¬ñ޼/t—UºyzaýzQÕ<¬€†•vÜäz1Ö u³Œ´WçFÖ‰‹?èÞ,Ó.Ú4yZ¿\@›l겯˜ÞuéÖòæLgÞÏYA}X[¦œ¯˜Q\‰ðX»«—ã9ßú0Å/úXm{ÒÕn¨˜4væ[vd8ñu¿‚f‰øÄá|ÀнoË”¡ázoùžÔ#¥EIIÿbò‹ßæ|ë†[wæÜ( =Èíó´ù7N'„®—·¬¼Ï“P²Þï»´á¶J³²ý˸˜ ~%—^ÜÛ“XUa˜¿âƺ‚‹êwN #Áñzk Ȥ[ÆÍYÅE4#c„6ç´3 iÅ:®È >äÜøAgr±‡_yˆ’bRŽw^R@¡±»BÇ+¢¶×e±NS\¨©Wà‘ñˆcœÏ#§“A×õÈŽóçW‡SP¯éfµm èybïê‚-E”5@q!õfg(H;Ž5òåtjèF®¹—ì<î4E¿Ž°šd÷èÚÎæ-¦‘ßé5Ö C¼-Î+h½ãÝih÷÷é Óàê¨ùªâ›âhDÕdÓa›"²™Ã:¸‹)¨þ± A'´ò†{èÈ¡Üý4Š/>ذeÛ§çg¨õœѹ}‘2µ½Ž¾ºC†uëöÓÄs?Ty¾ÝŽùÃùñƒîÛÂBÿˆ˜³´ÒÉÚÝ£=òëÄÏ÷ßÄÜáë'1kØ£žËfݵ†W‰»N!tï.D Ç{FPµø)ÜL È»`úcÁþ;|žs¡ 'Nop\© Oþm vâÆ:vµú…å9òR :ZǨ€*%ªÖüXx§"n6.m´Îg•‚\¶Ö¯íÂt=ê~WîüqŽf;Ô\ìð2Ÿl,7Øüuèyôj‡“ÎT8&§JÛm ’\óz“x–ÓÉ «œçz·úåóÔ±ÞmáõÂ|ÞÇåÕmv-ìÌgêÇÚ§3ø±‹¹ñƒ®½û£©3DR[<õM’òI}þPGã[·©ÛÊ÷ûK¤:ºèÄþ£Üñ:ÿÃËöÂν‘ôr"ë@•O»ïùÍìpúv…¿hÿ÷%þ•+H|¤×‰Ó]¹qK(Þ’5¼¦‹¤O£Ù”Ož³ü¥n¸M—ªm¿™àBBÖÞþ¨‚¸u7nÜ » ž³ê…Åú5Lò*ßñ·§Ÿe&ܦGqM£7Ív¥rëàNÆú–7èú\1)‘m¿@zÛÍåùÔb@§(aÓÛ¤Šè9´w“Ť_ÖÊPÐáæËºß°˜7è¾ÖÊÝ7&ïmp/õŸ—O7ÆG¬¹WHΧR̲c–­~JA£æ–çËO 7ÿq‘vØ>:y ¾ÆMì½!¤Œ—wþæN³v_¼z佂Ú6êšùd9w?eÐíšyï€ÇEJ ]ð`a¯|º»‰ d…4¨“ óƒ‘KiHÒé~E ²Ý–ºï*÷|©¡ûþpúÑ+ª‹4Œ “ÍòiŠÚ©j­î…t¤ÏðŒEWÜi}ÒŒƒýU ZȈ.™Ï½¯:è.{ÌÝ‘it‰¶¶þUm«Q>-8¡JIÙ-~Ýj që' úqû *Gî|F‰¨«*bf¼D)ŽËz–Gm¶]õôÚ-Úï«pãëM½âñ)’{.Ð¥;…¬Ü¼ç >Ô-47~º˜ï»óö7è?;0î|~çÆº•=XçúK¤ßfw#ìÆÌé·qÖ­ŠxdåÑÁi¯\Áûir:t-ß~è]7ŠvçGíu Í£ÀA”oñû—\Ⱦþ+‡j Þ»!¡¯Ózqã …Ž‹[Q”ղˎuó(µÊ¡1þå7ɰh´~F¶‚ÖÌ`wöðïtœ?f 'ΉÜ/É£%M.5Þ’v³â|E;§¡¤S櫹ñƒN%˜ì|zíôa þ<š)÷Lð*(ýó¾ÑâªÜsm”T"Ôo£°ºLù¹Åµì,pŽ]r“ždM2?¹XLÚ ÆFüüènü ½K}H¸í2ÕŽ"7äѓɯ†ÖºY1Ÿjøû¦ë ¡9:î¿¿LM"ŠkgÔÊ£je¢·Ûd؇Áº¥÷ÃsÝRUeuZÿþA·bçÿ\&v•uËÇ\j¬Ÿ®.¨¸ÎS<ç¤+h¹Ï“ƒ®îüûÝyÛÈ–Wz^¡Y»Šë ÔåÒF]óäÍN/‹Æ.w¡HA¦Mÿ?ÎÝOtcõôWh‘tÓªçi¹ÔùFã„õV8ßjöËÅ…àèæî ºÐ¤Ï }¹ó©¡»»(×4"ä ‰×µ)¸œKV‡®V·( ƒà:ý‡‚šîÔ<{ñ…Óé sðUÞkQx…,:¾¾ð!(—2¶„HÊ^çS¶,hËÖ½NtâñÅœA„ºìľn *~ü’K„‘õ+ ´¤Úä‰g÷åÒº†í»&æSȪg¶>iŽ$É÷3Y9^AWònz‰øñƒn GçGÆ]¥Á‘­ÚUY—Ka>•d{Näây7ê¼ÏYAšQ÷ï¦5tçÆº¬e~bï­WÉxþËOsri[P½îÅ+òiÞ0Þ‘Ž-Hz8p‡‚n¶˜ðjL§AWk¼óÇzW¯RTéQÓrI¸Ú>Év|>ü$õ¶Í‡´ï]FºÝgN'…îçÐè/®’Öo|~íá¹t/¹öm 3JÒø r&ûíeâG^ ¾îçt2èÌ<3I:«øz6—êîXÕzjYYµ(¾Ù4Ð…ÖnÏÛÚl“‚ØêùÉ,N§†Ž‘.Â—Š–¼.Ýr­i.ùŒÞ³ É£é›ÙÄø.}õêX‰Ïgv†”»Ÿ:è¾Íí1æÝQ¿Þ‘KM»Þk˜¶/"Q<5øêJ}Ùm-G†ùnüRJ„b ¯´**¿»*½ÞÛb] 'Úä‘a_Ù¦×]†(¨ê#*Hîù@×uÏj_£d/Ö@/‡jéíóh¾fM»VÔI{ª”~þq‚‹/Bèâlî«KÆ^#›Ç#Û¦çÚyÙý¹osé{‰™°q†˜Öàj¯WÐj6l˜:sã]u{f³×5š¡7¤Î¡¼ñó+u‹Ë¥ˆ>¥Ûµ#\È}fX•¹SÄïÓåÆº+Ì¥aë®Ñc|µ^VäP#µÙIË#¹†ºˆ"z7¼³¢—‚dÍk¬ùÎÇOèîüµªJµëtqK`õ:>9d\{kûån¹äQSMŠ ŽôÕ¯¾ñªÎ ºìeÖy™”»/jè¢_\0ö:­Hœ·±–4‡RžYìº<2—Žã-(Xã@{Ú”›žµTP»œxˆŸÐ9D`¿Ø¨j»¹êªKsèvæ¾Íý[åÒúY.—Õmhv °°ël•ˆœýÞ—ËFša«‘Gý‡i¯SNºÙ±ùórhÜVÛÛÑsÈà;jæƒì2¼GìêD« Ü}@WÀN˜F“ƒY—;³¬r¨hhâÇ{Y9$oi7­A˜­:áq>c¾‚¨UÇÑ_¶r:!t£~±QM\}–CeÒ9%¡94#jEoóîŽ&tvÉÁ÷¢~y¨%w"èêê o£)*õÁá)sèÄ¥ãµ7çÆXéQ5Ü‘6{;Å%¶ý›úºdÝŸ£iëª ® \gþéÌ™9t¯ÍvéÆšNü|¯‚,üìó\ͽ2èZtØV<&†çœ8õSKO»û¨Ù}K¿ïNÛiáDVw³bß tløª›Ò9ºúÝ‹ß:CyM–t|©¥{­Y§amÅùLVEîÔuO¬æÓîpgcmf&>‹!§nHuZJXz k½›ZÚ‰¯ÚÖgI¶T¦òß…ì6êÓÜßg”Šü®ÿ.¿AŸ²Äg|4ZÚÒpôÖ)Z y.ÿ’À‘úQr~?(ÿþA7Êf§³ûoÐ;»O_Eié\ç§îìÐò~¹tZº!Üå”wwõ€’S|ý’jðù½AÛb?|z¬¥íWªkŠ´4·@@ö¿3lNZ_9}ª½&áʾ~NoË><–šè -™èw}ˆ¶b¾!·q„¯ß8:½­òáXâÖÙµtŒ-ÛMµ´„݆<׎ô¶–-䔳öêÅÑ.|þƒn%ž¦ÆÏb)¸Ö”,н,4N|‘MÖ×z³ÂŽN25w[ÊiP”fMì)>ÿAÇf¡±Ãêï²)­Ï\-ÙžÞ¾—"›z/ ©S;b½-jð QËSøñƒNo›~˜¡ä„ õzŒÓReïŽÓKæeWø;·ìµbꜵrz¸²Ká–>~¦áûf›|ê™R†ôÛ;ûá|‹ɃkgW<×W³;&äÄï·åƺԶ¬á°šf\ª1ϳ–’“›Y¹œEßOÃ~~ÿ7~Ð]é™¶Ä{¼š‹nõ.¬«¥J•’´›ŸE†}eIûÊ> ªÊ‰›7ç¿û kYØ-š¸}~Ùt®»Ã#“ÜÏù?õÚèÂï硳͛—ôŠç¿Û¡s¸â8kû 5Í;‘üiž6›L½í;í•eRª`mƒP3ÚÍ~v‡P”Íž’îžüw;tœ¸š>•9‡gÓ åÛ^œ“I‹ß̘%8Ó«;û‡? 1ÌsãÿÝ~L8âæm²iöž¼_f² ÒÛ wu {ƒ­¹}>Äð~pã—^"ìüÊïz÷8jÏ·è—M»ªllØovõ4~¼©U¦…Ž-uy¾:„LÒëïÉ]§ºoíØXŽ£Øf¬Ó{6å Ÿ]Vd’AŸ´ìÿ#¢.æ7Ý­CèâÄ™Ëê.áÆAÝÔjÅï¿»ÆÑ\wÖQ5‹LÚn²|••ÎûÁ‹hÁ£~·ijéÓ]]>ÿA§ßæ»=Ž–Ø {_Ë¢f‹^[>ߟÎï¿ÑëQ÷V|t¡™+¯ÅYïâÎ'…μ^¯Ö+ãèv»Ÿvâ9©ü¨›hR:5ÿ5üQ¼§ˆz|ˆë;`U=<Ä'óãÝÊnÍN«âèe#aŸ‹ŽYtâÚßšé4»¾ß®I_D4C?aò—y 5t¡ì6ÑŒ8b£@óÁY4¿Î:Û²Ä4ê:T¼æî|;Z>¤± ÿz>tý¯VÕÅ‘»†úyÕÏ¢jIÏŸ?ÛœFºF/f~ ´£O—{YŒ¤šâ_}Ì{#î¾e”õ¯ãèó쎕2I¿½Ú22S÷NRŸ·£ÑÁÚ‹¯Û†žsnü 3yYÞÛ¤jÿþA§_fjOGoM(íµ;“¢Ûkvúl*i|­ãT³#ów;7-¦ÊN3Εõât"è:­®ìoÜ2žÞåN¶7±É¤kÚÕyçšZ±S?Mr?ø/ùV ]쩽ïvŒ'n_`&i¦Ÿö°k*é—Lm©Ãíì›Nü>Onü Ëvfpã©FYS+ëï¶~¿jþ+ åMÉ|\}ÔBj'Üy õÌà¿Ôjèt=Ù‰ÁxªÙ2pQ×ô *ÿn>R¥!ývγó©MX©U¯_A„Ïcƒr:t v®ºqñüþ  r Ùâúf q¿k˜GN~¾Ë ¢ýìÏ^öóßï™%Bv÷~·%t®JŸ£&hH¿lÕzùwƒŽ­-H'͇û÷¼ƒRH3ÿÜæ%>¶üü_0ñ¿—ãÆ º5سxJ¿¡>k-O§9Ùãß´´M¡²Ï{oû̲ãçƒùïîï’A·JØðÓ+Œsf鸄®+Óéø‘.—cš¥Ð—£–DzØÓÇÏS:Áý;ü¼ä97o¬†Ža·´‰§ é+Qâðtj{¹×¯ŒdZ6a]Äö×ö”fdŠo8ªÞhKãÕO¸ytú廯ñ$?³jm@Ýt:ùªe¸±g2}›_¹lj¸=é—Õ¬ƒ(g„¯GiONg”U"Ü÷!x S+žT.ô¾FiÕ½,÷Y&ÓÒ½}[:úØÑ…Æ©s*w b_b¼aܼ¸º÷©{<7Ч¶ÓèkÁÚÛµ>&ÑXv›}[íYáú=¾{=Sµ¸}ô÷÷ ¡ëQçPÞ-¼ß,Ø›4Ú‰§¥º*‰6^±×z!Jsš:Èà‹ÎÍ«Š «­ÿ°B<ùñᔥQµìη$Z?°iãêNx¾Ù÷Èëô—<"…îÙva#Žž&¶¹“R+ʶWI>59‰ŸW°¡›Ê]âU5‚þ’·dÐuè{öá~ĽÑêv3Ão§Ò†ÎcVooÄχÎ!ù·AվܖÑlé›ËNòùºuÂÛGºÇ è—YR©TX4øu" 0dú¦§shð°7É[ãeü:÷¼è ›sÑ¡ƒÉù88uµ&Ó#•¹jò&ßH$îwƒsénº÷ 2êÁt2úÅÏ(»D¡ö.ˆ@>¸ÐôTeÑÐT îVi_üîD2¯ü];åŽd{Z^õ;•Bë¿™š¶‘Å“ò;ð Éãò”æÈ ëFÜøAW°næ÷û â¨Åô1o»-N¡^ãÏ7›éO–η«Ÿ, §é£ZJëʈ]¥Ýß™ÏwÐu÷ò<·©RµÚ”Q£êàêv¬Aæ;Ä7n~ õÞ§ÈIÿƒ>ßAçy율å[5ýiıj)Ô¨abþ[¼Ç_ßÄ-ö°€ª,dWô ë¼ÜøA×wõœ5q:5½#öÑÉTãfÆå„ëqÄýÎt!­~|ihý׆õnü 3yú jhžš–Ô`±LǪož™°9®â÷[dÅWÞL;õ—üª†ŽÍþ£Ô´´ïé:–&SîNqó)Ãã*ꜞÕëçÅ^;EúiãÜuê k–5ãÙE5¥÷ŒØ™8,™zØ|ðóù¤¦ ývd-~iO– ~Üùúì¿Óå”Ý–lÞ¬&vÖ/ N2i²À¾e˜š_ÿv¤–ú ˧þR7  K(k€'HMÙÛ¢/­)J¢JFó¯­˜¦&ö×k>9ÑìáSäÝ0úÊÕùü:t\§æ÷$Ñ­ySÊç¿e*Ö#åÓ<»ë û”¹ñƒn»]l•š®Ö H6&Qa½±(jT¶¸cÍx'ïÊþÒ*lZe}w«Çé¤Ð…O]±k²£š®Yv8mbµØÔôhßXÃúÿÞð뜜Nݯ…l€QS´Ã…ÉÝZ$QÛ¥Éòë âöñ =½—Ð)%€"W :Ñ›ÏÐe/«?R¨¦1ïŽ '¼L¤n¡k%¹Þ¨¨7w×t+oz3€¶±åJS¦ãŸNz—ýéãô§Ó?N'ö?bþ¹øã—ûoûåþ­÷Ýßã—û»÷Ý—/Ì¿Öüïíù{?ò/Œ¡ùUoÌO¦ÿÍÞ˜†þLºßúbzñ¾xlr¶/“˜÷‚aûaš aZýÖœíÇdð€aû`jyÿ<áo}˜Jëéý[¿q¶ÿ’ïù¢ãýx­ë{YÞÿ_zŒËøžK&HÊV@ T  ˜!I‹øžKÿˆ=/Ûý©‘þC5ÒŸúèO}ôÏZý#ÖElŒñ0úãüòþ[¿¼¿Çøw¯¼ÿ.–¿íþéYù{¿ðG‹¡_øUÏJ5(æHBb …À É Hÿ—{VJ@$ïÏbèWiðÈc¢e/Î3XÉ÷©4å½ñ ýÁË{ÿ‹‹ì7O<¶/¸¨xC_J¿ßú ûq~Ñ|?Jï½bèEiô[p¶¥˜" [/ åÀü¼ÿ$[ϾÕþÔEæŽþ+j£?uÑ?~]ô¡Y :`Šg ¼€”s$<1B`‚äg¤¼0ë¥b†d(úÍ»Îø7ÿ`ÖC¥xÏ:Co£ß¼S”¿yÕ±½»½x¿`Ö3ÅIUÌ{Ôzv|‚Y¯”2Þ#˜õJñZÞ—ÎЧ;”’± ð>1[1ðZ`ŒD- J‰Ûx ŸÄ-P0ER·^@ Ê9’¼È@!0A·R eÀ €ø-0F1Àv¥•€Èßê$çKÀ‡Ë?u’ÑŸ:éÏÒŸZé»Vbßw©ÑÏßÿ“çïßúÎý½ž¿ß¹ÿNs}1B`‚`¤@Ê€‚ø-0Fr ˆ:`Šdaýïô@‘€HP è€Pƒr`ŽD#2PLt¬€¨@0C? ÆHHB ‘  l€7Pƒr`Ž„%2PL¼¬€¨@0ÃÃ-~@ Œ‘Ø„@"A) ÑÙo á“ž%ðJ ¦H‚ÖÀ‹÷ûeýȎſùΙüæóËúž”±>t¼ßœÐòþ¾¿“Èß|æl€7ïëË&VKàÁûËé€éo~¾¬¿Iy?ÎË—õ6‘BÞWÎ H ”3$eðZ`Œ-@ ¶(fHà"à´ÀÉ\$ ”’» ð>Ñ[ :`ŠÄo ¼€”sb …ÀEÛ•] T¿ÕKlÑþO½ô§^2âê¥ôZéOôÿvdÉ?oj£?þ¼ÿ–?ïßúÄý½þ¼¯8si1B`‚€m¤@Ê€¸ø-0F0 ˆ¥@€àn¼†ô–À(˜"ð[/ åÀ‰@ d ˜ )X)($àÔ ˜#aˆ $+ *PÌÐo lb±@ tÀ‰Æx5(æHéO}ôg>Imô§NúGª“¬øg꟮ѿé§k ¼€”ÿ~ºÞ@ÃjKà”@L¸­Pƒr`Ž@.2PLÔ­€¨@0C? ÆøB ‘ l€7ÐðÉÀx%ÐS$kàT  ˜!Yˆ€7Ðð‰Ãx%ÐS$kàÔ ˜  ~@ Œ‘d„@"A) éØo á%ðJ ¦HHÖÀ ¨A90G‚? ÆHVB ‘ ¼l€7Ðð‰Ìx%ÐS$6kàÔ ˜#щ $=+ *PÌEÀ÷Ó5FB ˆ¥@𛮆O––À(Ž÷ϵ^@ Ê9’©Èxß\$V+ *PÆúèþæ—«ýÍ+W"A) Ûo á²%ðJ ¦HÐÖ@ "A) aÛo á“·%ðJ ¦HæÖÀ ¨A90Gr(&HôV@ T  ˜!ñ‹€ÐcB ‘ P°® Þ@ó[½Äæù?û”þÔKR£ÿßzéO­ô§Vúߨ•løg†½¯ì¹…@"A){<‚” ð>`Y :`Šf ¼€”s41B`‚àf¤@Ê€‚ø-0Fà ˆ¥@€@h¼†Š–À(˜"HZ/ åÀAS d ˜ €Z)P2`†€*~@ Œ\…@"A) ØÚo á¯9(&ÄV@ T  ˜!0‹€Ðci!€HP Ú6Àhøn <€è€)º5ðjPÌàÅ@ ‚½(fþ"à´À‰@$ ”ƒ ðjPÌ‘(ü€#aD‚R @±Þ@Ã'K 2PL\¬€¨@0C²? ÆHZ :`Š„j ¼€”s$X1B`‚dk¤@Ê€’¯ø-0F" ˆ¥@€Äl¼€ ”3$jðZ`Œ¤- JIÜx ŸÐ-P0E‚·^@ Ê9¾È@!0Aò·R e€u„gÝ×ü€ö·:IÄÆñ¿ùgÃЉû¿ºœᦧÍ:•öV“ªÍŽÓT¨û´OÇVÄ&Vüóà MÆ?Š¡¤ƒ}ÖölgKl·×´%"â|¼ÇÒß•íò>«(¯Ñ1‰ð¶#ó=ümîd{Þ1·D˜÷‘m@­¦Ë÷ïìöñN¤w}×]nCo£ŸkýЖê¿[oÞux 5™ÝM©üÊ÷…€.HÖ8¢¸¶šh)³ñ¢}"IFïxþäatEÿ‘k{ÍÇ\ëH•뤟ZV›ï‹ë.º¶Œ¡N™móîôO¤†]F¤ö²¦jSÛÍy>WDú¶‘uÿÚG:šÙ¢Ï¥l†Îϯšüµz"½¿è™íV|ï cÇû¸ë"ö1—ï#]Úßò‚ü¼TÕâv®ÝZ÷zÚu’tÅÛý¥¯Š Ç;YfK×3”(ac(úÞˤêq×hU‡Ý®²'ί;€æûÏow6™ëw¡†Ná²~÷…I õ²÷^¸4ÔÛÕÒÆÝ¯Ñ£Љ¿ì)tÑUã}hŠ¶ó®©ƒ¹>:èÌ?-Ú˜Ó†¡˜Ôe9©Ó¨è;ó\sHEªíq|¬ß—;ÀЯK¯3Ê+†ß` ob‰uý47K _×1=>_¥†…'·¹Œt O#+ÅyÛ%îYß ºþ“Ü÷ÅÄëíû#žòºËç,ºjèÓOãö×ìã;3€oþ®³‰ïç‘gèKÖ‚«çœϓþ‘_5Wh—;ۀɞ&1™¹Q­µê# ®ÉÝOtQ?ºwŸKzû@E<õ¹{Gâly…¸öúvÄûÑ“nûȪ­Fòýã ³¸¼$¢wÃXzUvËqÿúxš\ÜÍËóÜeb»Ùï(ñ}v }à¹ñƒnælö‰¾Aó/õý>5žšEoš}¼ëå ŸÜÕkúZNÀujÖýjÛeß÷º³wÅF½AK·œ:ÅS«ûf •ãHo»cËûQ$®:ß?ºª©Cvõyƒj~[Ó°ÁÏ8Z¿²‘Éë®Qä ogKbÖyÖIƒŸ7~ù%‰C,|[Ö¹A­^©¬Í‹£Ãâ¥Ý>‡]¢uÃü·=%"×}ªß\~’:O÷Ï|dÎ]§:«)—6IŒ¡=ç×4~ªŒ£÷Ý÷=ZÓæq>/v”õëÌVÏNRúN‹£cìø~:Ðy«Ûnþ¹!†‚T&oŽ£nÉ_NX{]¬è»ºgM“é›§ü'¹ñƒ.yñ¢‚¹=cÈòóœƒßfÇQ@åÓ©.Ð/bÔ;ëöÝÏ%€æøÅÞ2iÅ÷ÓîåT¶ce4M|v|‘}8êÒA²?¬÷ZõªvéŽ!.´&ùs’¢[ß_ïG]JþÂÛÕ¶EÓˆú—ÝM«ÅÑœ£uWDò¾bz=oäðÒÛ'ÉÁ¯QTê¾ÿtúÛØ3šŠg6O¾«¦JW2›ØŸ7ô×¥žó_ìÝ~å$ɦ²Ž„|ÿFè*ß<½°Qîuz3ûXiKj óô,‚£Îôbù}·ÕIŠaí[ñý J„™‡×r\so¼ðùá^5q¾Hg)sð£o§üšNò>]¼ït9ÏÖ lÚø:éí’Õô°óÏm!t†÷Usà}cNò}«¸÷V]y9j.\£VfeS†S“䇥õ1“Ó4fû–ýí©v¾}™ù«tÌçqÇ#±|?èn=vhýÔktukd©¦ÄoÑ^Fo•ÔÜ&åí(…Må^ ”©âwñý¬ Ûšô ãü-®g’‘óˆ!£€ÃfI…a~·3Úï^4öÂI*ù<àÇíƒ|ü„nÍL‹ù›Ttm þÍ µ¬âaÄVôkµÏ:ÃÐî¦s´âû÷CÇõóQQ!Þv¯± þ¤Jås1Né±Áž˜œ-7 àýù~VÐ )qVU:u•ô¶ÿ{ÿEÛ¥ûØ1cÆÜf̘1î6cÆŒ¹%6AÅŒ¹Í˜Á@McÀŒ ØÄjrmr†V@1cÆü]ÕUx^Ï:sþ3g¾™3óŸµ~kæy^/«»vÕÞû¾«z_?£(ôÎûd#»Z?DSRê“2KÏ«Ö×–‹_.êÞÜÏã®}@]žï\oE‚è©ÚŒò#õÀ°îÝÌHûÆŒ» ¡ÓŒmjÏǺ+ór&††ÙêÍ›—lŒ¢-{.T§¿ð¥ Ÿ» ûÚì÷ç´f‚ÛôéÁÏ“ƒnçü aøÔº™n}pbß(ZâðRo««”ïš0˜ìÌiSÞœÅõ ¼hi~óƒA÷óC“œ´`~ÎV$ù_;‡;Ùû÷<$}…®úÕíW¤àü$ЩPEK—“fì©s$-Ê:ˆzÐÜ‘ìÄFsºþðmÆcOÚ¿íy²å.nΞ:§'—¯(ºÏ×Hr½{Ðôh+7ÒŒ%u2'÷ž²^¡žµóF¹øA7Ùë]Õ ãûtîWÝÉä5펾[8ó>næäÅÚRGy’ѹ 휹ó©†N¾“5& "¯‚;ªF)4«ý©qéÊ ¿ç؆þRn«úàIã ò=tÏó÷_^…Pü|û)…q}lmê¶ÿDîý£‘ÌÀ‰ߥˆNšÑÛ÷e‡ó†xÕÆ“‹tºkJ»ÜÉ ¤ys;˜|œAulrî4¶:Cמ–éšQùcÿâiƒ½jãÉÅ:åÃw<ÒvldÃj~4±×_.[9ÍŒ4cÈßxRe{£ñ} t]»¼œ×,é­Ûö>.œïë¾pâa*–Ç/V™ÑcÜeZÑžt‚gNüüMè"Y»ºG]DOÆ×œ§Ã¹âÒö{Èñpk‹ÂŸætª½tèžµqáâÝê­3¥Ð67÷ú› ÃiÆÖƃúØÙÕÎ#KóoŽV{‘8y>ùŸâçÉAÇù=Ð…quæiNý™e‹ý|èVyo¦ƒ-ù±%Ò‹¢íî¬?–ÀçOè$†Ì¾®wéÛ8»täÑ:šÂmÂ\:0øîêM6äXÓÎF®ôªõ‡æâ—_!ôbíb:Ü!ÍxRï0zíýÎ;tâN†½š×”Úþî_Vµ=wÊš»Ð…žüaþù•Öm|²»ií-»3¯ãëCÌ̉ÓüÆ;làçìãxæíF×[ÍçOèØîR'ù&µ°p*fòö󵯆'nžã’ŸïÚù ½½ˆ7šÿ&Ÿ?¡ëz~¥ž›ç B‘_ò5”n§ÎžìÈ É;TjC klb/2ÕƒqŸSnZò§›®Sëhù¶ÔPÒ”Õöªn;ÞšŸoé]Û·sñƒîÝ.֨ğØéݽC)"J0býtæNõo½ÄÄN¿ïñÔ‹5·Z¢6ã|ÉЈïNêöõ*ïÿboJw¾Ì]xb†;“ãÔËë “%±S'[mòâ}–sñƒ®u»§˜+d‰lQ9%”Ž~Øô.d°SѲ¡÷ž––`ÇKÔöKœ/–Vê{ôåšš#—©®é¾z:‡ÒX?ÏnÉ ¥LíÐçÿ:5tso„x¨=œÉüDúðSFr’yü8¸¥èÃC·¢ãeó¼Ôñ¡«ïvÉ:ÄÅ[«°B8úîó[íó/дoÆÑ›¦Êi„lÅþ£›o1µþÅú3z7ü›6k ,9>t5£58”ß¼¨½ùh9]QÅv²ÿu›ÑŒ»sS#ç^f‚‡ÞµþÅ\Ü ãü‰OSâ vr¸œn yëTzð.³¸Oré¢tK*¼õuõ†xoª×Û]Øêw}‰ ãæ£̯Ìê/'æcÄŽ¸½ ?'’vŠO‡åzÿq]J ÓLMl1à ­^! Jí)§®ÂUº#î1÷.ï;{΂æT¾Ò¾ÖÀ‡²šw|lÌ}N)t‘¯ÏìT¼ƒ6ÖØ®ÄñžyŒl~òÉ=†÷u #õuɇŸ¯ÌéÐÕÎA<Òe¶ŸÜ-i¹oçÀ9Û,èªûŠ9u¬|ø:Ã}>5þ|ÐtÍàYæìÌ’í8¦ÞØ$-b‹Ý·­êiInu,¢ûõ×—’'¹xkU¹¹¬–ÌÒöG×´('§¹Mi~ŸijÚtr‹ÙbÒso ¼ëCÓ-†ØíºÅéÐqs¤w2Çm‚Gâ:ùºlÔ"­Üû ïûB#ó³w-ùîCí7~1œ½›÷Ùƒî§k¼}áæ3ÊéÔI/³›ÁÌì·oì²!AÓŒv½¥üÚRÇ.}òëçE\Ü 3Ð}a*3kŽ.?/'¯ÆïŒóí0°J ËÚ@&Ч,ï)%»iŸoÙÜç|8¥Ðm¿¹`©¯36Ä»G— 9Í*Ž’…ˆåŒÏ„AÙîO677[JŸÍ„†œ/¦ºô¸m=–¸Èœq›©F>é\dñlI(Só˜5Ý@K£,{Oé%%ΊóïTCÇùJº0.]wAþº±Yr£óØ0¦Öß¼ÆVJK4†yÜçÔ*®ryßivîÇ çž¡š)£®:á̦AzÍ’GZÓ¾Šíñ§¥ü|í\ü ã|&¼˜Íc׬šJª 6ì-gØ)É'^ˆ)Ý®n1é>­Bè:>nUVìÃpy#”Ÿ×Ápþ™bÚ}fl·”=-_Î=‰‹t©MÎÍ_ãËô:2³óÌ[¡”¬¼ï4CÉ5S-‰oh)åý6x?`èØÕ™n Œ ;œï6åi(õvÊýK÷(†_¿Ò*¡xâ^7)ßÚ²qÙ·ñ\ü SØ7lþ£Íef2kÿÙ9Œ¾®gm¢Ï«[Èïˆ)º«ÿân%RšWÄf^æüPÐE^mÔàŠø ãÝëÝšÙkÂ8¿¯› ó+àÊÌÅ­¨ºí–+Þ |©ïÍžXÎââ]‡C\eF³6®a$èpêÒññ ¦zxìŒ úœÓïÇøêøRÔ1µü´Çl.~%Ÿ·øtiàϼ8‘ºZFá]Nù&S0eóÙ þÖ¤±!iëËçiîxèBò_%ϽÎ8ïÌ©÷¤G8Ù¨&ô9Ý4úwÜC潕û·ÇñÚ±C¹ëSÝózIãß`Úo8»ÕÃ.œ_ ý2c[ôïã±Óy/¶ô%vŠn— S¹øA‡"d›}“IÿzÑ4PNì´V‡¢h¦kåeEY¦¤û%¥Þ-§µ·z<‘‹tŽ-Z®ÿ~‹q]°¬ÙòFôzVû'ï…1ÌàçŠ×·±"ƽ^ý­¤ü¼áÑ\ü ‹^ÊÄÜa¶Ødn8±:‚FKßÈßùÅ0œï²˜ß’Ò çÔ{7‡pñƒneÁ¤ŠÁËï2ïD§J&ÝŒ ˜7sdõb®^YRÅ1¶€Ié«gøš¡ó8¿j5tC<=¹hJS25æóφ‘$þ9±ëHQ,³t¸Ö’ÍåômÂŒ]O*¥¼Ïu.~¥èo‡ÝíÕ¢ €á|Ý#ÉioÇÖá±ÌÁúæj‰M~à2rÆ )y,ýœÀîà"~Ðm¸7a Õ=ƸÜv\]y$]*é' kǬj¤J·² ˆ ^g7öåýÇ?s~ÎЕ&ftYñ壱9nE?*J¿lŒc4öE¤Îz:|”/央lbø­;?èÖßp nëÈÝ¥~:;¢(K{fa^JSëãm3;°ÅÌå¾´m-ëœ5€‹tÑ?YÃÐ f|§ã_×äGѵk#ÝÏpyAL¬Ûn{ _¾ÿÈź}š#ˆyefw1bCÒÒÓîwÏÇ3 gù,ËènM´iÀ¢õ¶¾ÔýcÞÀArîs* {}±*ý}æY Ë&^ ÝšPl•ð#žùþý³:xŽ E.b|ééHÖ)M[Ó—©¡iÞûÌ7:þå]]MþéЮ·m3M—ÎìyoC?° :_:–À¸çý¸Ë*„œÏP0sОuêRAÃ;Î'0~ËØ'[Z*{9¸roí|ôiº½¬=fy0ó¢¢Y|×M º+Øâ='‘i¤ý¤êíZ°ª×¡Ív¾ä½¯ròî“s5:!t¯Üsu·…0›»–Ÿ<å§ škKN>HüÝ¿¿Œ2µiéËçÏù\ß Ýº¯õ0’Ðöƒ…E zø=>ñPW%3s«Y¥K´ø÷ñ8ŸMCN]v\ð™Ì–½pgFSSÙåÛ÷)™ZŸ)¯Ï¿6Çñ¥“Ê¡P£“BÇý=ræ`Ÿå÷jæE“|Cÿaó•Ìå¬#„Eÿò!ßÙ—Ÿ›=A£S@W\°¼ÛåkrF·OÕÐÑÔgÔ Có¡ILí¾ÞHŸ­^\ñå}ѧrñƒnÞG³ŸcB™»½wÝyM–Ó¬6UMbjýµ¥’ýeŒ/mY8¸çɤ9\üÔèý™µë£CßGßÿŒ¦â«Yí‹“ΧלÜ÷¥yæK%¯+'mbÄź?Z«3'Œ1øÐ¾å›±14³EâõÓ£’¯Qoƒ--h×®qË/¶–Q­ï·&~Ð….4»í›Æpþ©1ô¬^›$ßsÉ ××ZÐãÕ±ÍÎô“Ñ4ÍŸ‹ƒºNWC²'¯ gØÕúÖúPØêýÂÉŒ¾÷ä©¿ê[R•-×õ•Q¹Aµuèî¼H »~Ùީ™w¯ÖŽúCw¤-q˜™ÂdÆ_½4E`Iëc³ÐqÊÈk@ÙˆÖ¾“¹øAW_ðúùR³¦Õ¹¥Z#bÉ“µ}¿œÂt[òx\B;K2N8¶ñS#Ý\™¹°…w<tKn]!~Á„vpmwr[,%Ÿ­ú2D+•qéÜl»ñ# ²â õx_²\ü +É´6,6dFΩ›K#œ}šl\™Ê\u{½}Ñ: 2i㪨ôå}—¹ó¢õ¨BØÅmæO‘:’)nÅ®0b)ïE¿ÛƒRë—2”OÍéDpM[­r_Þwc6?èV­ë}¿rus„m FÅѶ” 1^Ó˜Ú}ÒáÁïwÎd?èØÝÅâÌ(fOå.»qd-ý‚JÆ?¹Ö"ÇŒ4íÞK_¾ÿá®t{mC†yàÖä­aD©—NôHcš¼ rzqÕŒº-Hi¹ßïE·‚Õ£.äâ—ßfh™^æ8šþsÁýõõÒî¾7£7ö_yÿÈ—¯KÜñ¤ÐµStXGÁœ;ùk¼lJ< û±zÿÛeéŒdƳL¦7î‡Ø½È°¾duë‡×« î~P@çiãêûvš‚³pÎÖG⩲Ê9Fïv:3ýc®bYc ÒŒÓÈhrÿ•ñt.~Ð)³ÞÕ[¾KÁÄÌa;„x*jÓ;$¼ÑCfüõ¡sƲ¤w{<~¾œ)£šÆŠ»^´Wüm^í»¡`Þoïø3«y¼4èÐJˇÌ¶íqÓæqIË\ÿy? ãꉂé%iuyQMH“¯ûò9¾`œ“è†õaÓÀdqû° ¸øA·LcÍŒù9Í<Ö=„> ‡uR1l×%[mM í:̬Ä}´Ö"_ïŘÅ\ü ã|Þ£-VYm·Ø\Šr½pã¡MÖôájÏv íeü>ÛR.~ÐéçɈWE3‚âýii͈ˆ‡UŒIøÉë«ßy¢YRRÜíCÆ\ü »t°ÞʰCÑLÁTvAœHûJ½‡•«®n‰©ñµC¿6‘ýö{ÑÄ:ŸUU³ë߈f^ú~Hí6?W©bøç ôóýH‘#î¿ç®ù{W¯ââÝÙ‹˜–éÑÌ•¡G%ÃÒ¶Ý5%û«.MÃuÝüêú×E\ü «‰1»!ZÃp¾AI´üÈÂ_-û>­}ÎJ—´—üÂuÍ=?YÆÅº¸)¬“x 3#äýlû$únÙ¡ka«§ôÊÓâàŽãftèºEî"¡Œ‚Œ¬öãâ …îÖÙ.ÏšŽaïÌ<>.‰eÒ“Úçtpªÿ¢ ÔMnÿ˜;žºakßßH·Žek^$H¥ÔíÌÝÑŸPvXsk&Ø”Æj[_*³Ýjº;†»®µ*q]wfŸ0Æ2'<'i¥Q^‡ÆÆvxB‹ònaFÍÛÏÏüîK³× ?ëô’;ž:YsEÃi±Œéƒ‡F\I£öK;¶­ûä÷sÂÚïÇù“ðý't±£ì×]Še&fÍ9mþ5LÕ›¬—½¬¤³;Š«ÃŸY^^[åËA2þù×«ìóÙnâ÷&ñ•Ë‚tŠÊÈ÷wÍ®$Á®§FÇnˆ©ggÃËþì'$бî|gîÆ2Ü:;f÷j:±Qx%iß]ShglM5•ß(#½þZW›qŸS ]ÚöNV×#b™~ŸÇ·mð5n¾ ïYãSI¬ëK»6tÑOk¡¾©Œ¸÷>¸ëE…tøÏ} ±L6òíc<¤5Êý›ë©äïS[’¯þ‘ß›dÔC°nJƒãÜ}¤†Ncÿ£ŠeÌÎ;\{HëŽ?­²¬$fz¿9·?Ûг¾Ã´ou‘ñþ¯üý÷¤B}¢*/7–qÛÂþÅ÷‡äà´IkæÜJ0šÝ·¡7È®›ÉˆëGùû:Îg6–—V÷U‹þ*r•tVYëKK×I£ípÅc•?†¿ÿ Ë[ðkÅûrœ—"EWÿÅ*šÜê”îÝJªÝ×èÔÞïNðg_2£SW £øü ç³ËìëÞ¢µl¯ŠžìJÑŸ¥UI׌Gio ·¤›/ŒæÍB}£Ù¨àî tšmüW±ŒÍàÔ)㯩h[ÔÙ×¹U¿ý‡ž:‘–Œ~)¶ñÜkÛ¹>D Wó/– ߯2O¬j0L¥¢­wKާeTë¦ò¤®% Êi‘àË¿gÁõ‘ èún^6mÞ¬Ÿ]:ŽNþª"û^®-o‡Uü~¾_4œ}òêËŸO¾.EZ¨ž]^‘Å€¸^ÔP¼*mœ_Åoßû³=´ >¡¯[”WømI;®ÔzZ!dÿkèÜ y"gw§Stû¤Ý……æbºŒÕõîµë"~Ý€??uâ¾ú?«c™þšM2(Ùusã[*hà‘ã˜B1E±6iß|‰ízêšpç_çgËHò3ý®{gÐwë+*j}Çi²¼n“¨ëœ/wŸ‹ Ë»Â6Æ2‡FÔ|‹Ï Éáç …´æ„ðcËžV¤½­ï†¯}ù}n".¡ôÔÐB\'ÍËbÂ޾Π&…Õ³úUPϲ+Ù}Êñý¾_.¼†¾úÌC³_šs}™ºà;æâºäÖ‹™ÔaØÌç®-þÇy©í#6T„õ¯ žÂÅ ºY÷•!¸êk |3‰.½~îS9=ŸÌvš–ĺò´Èö¥öeF5¶]¹õ¢:¶»x”Šüž {öÒ,“,'&ôZVNSª¤s >XPÀrc;RûR^ã ¡\ܪ*„ç›-I`b®OϤŒå£n‡*ËéNþº ºYð¾r¾ü~Í[Íþ‰ºžì/–Ù¨YÈdÒ=vÛ-°¼Ö¿Ž îŽ/ÙõØ—æi ¹ý !tÕ…Wš¿ËL˜Ýg‚o^&¹uhþW9½K÷m¶t˜9ß÷ûrqßÈÚ¨»ÆòëÒLê•úóä ‡òÚ÷‘ˆ[çúòû^ ¹ýèú¬Z3¿1òú”WvÈzgÑö=Úçn)ÿ]—Ç8äžX«kÏÅ :×Q÷Ë·Å2Üû Y|XN…ìã…2 êéðÞ®nVíþ@æ|( 3WKœïˆb™—mo6\¿1‹,UÙ¼VN݆^Ž*¦ûU^MäO}ù÷†sûeÐ8edÛ`^,s¦yÉË–³èåµ]ÎÀr2ÚÜÅ>¸ØŠX×öe4lCN÷õÜ~Ù3ôG™1æíF#ÞKÔfåYÔFkðµù­ÊI³ýßÙ†:ûú\Ï,ã÷zqûÐqïwÄ2[5ƈYÔ©Õ°äüéS¯éfÆñ6t}N°ó±>‘õ3_4u wßAçï8ðã ô+ ’/­•Ínuî[ø˜Nþ ØiC~õí6LFK*t6o0–‹tfÃfP+ÿŸ“{gÓ¯àƒô™Ç¤Ù¦°úëî¾ÆÝwб.hLV £m|sÇ<Ãlê8õÍÔ‹WSí>;@ù8ô9ü¾žf?P ÝaÖ =†™Ï¾bMÖžo+;ó¸Ö¿ì·ä?^— 輸ҿ®g 3 Ëäñg³éÁ³mñÃv>þÝ7¢ ¼Ôë`®ÞŽãâÝK›5ÖGöÇ0Sîµ9ø^69I2*ÓÖ?&ÎWÍœr»±OÊkëÈ.~Ï+„ñÙ{ ã0Ä“[V6Ílp}Ó‡¹ÉoÛ³uç“͈ÑÙRú ×çë¨Ù“vpÏ7Ðm6xs¬0†_çgÓŽŒf- “­sÊŒ§ßÍHžä?üt¸/uúñøúLn\]×¥ót¶wa¸óC;Ó>&Æ÷|\û~%9š¾èãK\|çpûÐ%m¾¶-àg4cxRprL9½êq,¦ÙcÊ5ð8«µÑ‚íºžïš‡<–`å0à;·¯.®sÔÑíå%ÑLÒÒAûç{ªýªgÑðúZ‰Þ-©‘›YÅ Ôµà¡RîûI¡K,,+èͰOAtwåй§gW,,}ôû=1';†¢.sý4ÿ¼:Í6¸[4sÛzÖ̱®9¤®Ë|¯Ÿðˆ¸ý%1´É:jŒ:Ô©C‹#fu¸ýx5tΣæÛG3=ÛV>È¡c‡ÓƒwÝ~ô»Û¦7.M]üÔpëÜé\ü^ ¹=x>nI4ó5øcÖÜr[ãéuïâ#Ú2|óMAÃß}7üù¯¯ÙØ¢™“Omëø~Ì¡ïuZ6ì}Äß×bò2g Je4|œ ñ·Üs7!tÝØÇ+ÚÑŒÆ>±M.µzCúÃô]zªõÔñv_E;Ëè§kîÕ{‘üs>èž8fOIªP0 4/ æR©õÓécæ>¢Úý{Îÿô÷º™‹t‡žl}½#RÁtZPs·ù¹4+øCó·#ý¾|RØ…›Œ7bß„äžÿK¡ë:¡{ogó$Mع´ýl‚]‘¦\¿² E_›ì©#e4áXâþösxŸ6èäNÓ37*n™KkOm^¡nøˆú}iŘ4·¤±ìãùŽ2J5MS vòïåBÇîÆîœ©`z.žT©_.y¼R¼¹ôFÍ÷•–”ڿΰÆd4F÷y@Ì`îsj½¬^´Ý>¶@Áhl™\jñ"ïéž<5¿Ÿ&¦ -ÏÅ:oðÔ./æÎ‹:._0̰7›¯6.Ê¥Ù+—´ŽR>tÛz½­©ÞÔevÏ—ñÿœº­¾ïîV^b˜mMÙN#—b:ô]ÜÀOÍ¿hC–ay³ö­ÕúèrñƒNc(d˜ëÑ#›uo•GK&KÏö<¦¦mÙE¨ÓÂÂÎ{×£®t^6õ1ÿ~tÚIuûÚ½ˆb^–t³t`]Ø7#ÿª¥š.UY{ õ&²hýñЂ­2rvù¾àçkî}4)tCX»Bç(†Í.ú3óèS¹pÅC5Õ|]éÒÉw¹œ6í½çˆŒïû¸ó¢€îLöÉKcðx‰Íú<:o ª ÒSó~œ›¨UÙ‰CÉø:È?g‡N6£GÒ­ÒHæZjÚÙž<Òl£6QÓf¶ ÑÙH?ŽhØß^F/™ôy›¹ãi½Bž6}~çå¡HFóºø¥U]3‰ê¶¡Œ™›Ö5J·¢2öµ 2jØBü¬Iw}Š kßuÓ»UŒæuå'yÔ¤8ÌãÚ¸2âž[‘µî¤žW§Éj߃äâû”¬ýûpæöÉý_uòéE¬ÑDí2ºßÓZöp¦m:2}º'®3vwÏ-˜¯º÷ã–?ÎT³ËÔ.ù”Ñù‚õ´œRÞå¡¥0ÒŠ¦ì(M°˜Y›Çø÷:¡–!h° G8ÓÇ~j§vùÔÚö¼‡o)=d_ƒ\dMì[Yß d´SóÂ(ï3 fú^3hÆ›7‹óI©ô3mSJÆ›²›d}¶¦W-/}B1bћ랟xŸÒ×Xÿº¹Ä¾ÆŒl»3"iS>ml»ÂFoT)±o#ð²!,~›\-ãŸçðïBÇ¿§ÅpïÅå“{ɬ¯Ö?Jhý†Ó^Õ­miÌÕ~ƒÞJd¼ß.„.ë+k\ÊØ¹Š]˧¼™s?6Ž/¡è›•ÈT¶ôë Ÿœeµ¿àâçË+gÆëÞ[ ŸOv‡’ß;]BÆÊÕ—tÆñ4û¦2~/º%eÎO–É™©O’±’Ê'á:ðbI %qs#Ć¿RF½»Ë“yŸYèp1u\Xø€™0?ÃiõÏ|ZÛ"ün·êáò¶ßJ~ta~oWíø:yÍe#Þ§º_{Þ=`4éªS,i;»ª˜¶Ô\nÓøª5ïÛ,#K3R_¾çÞSWCw·OÅaï´¦¥çÙý1´ñs‡'׃Šéj“}¶×µ&¯Ã–S.ÄÉ(dæ&ù¹bk.~o*„š75„io=ïýÙÅ´'nê֚ſßCT°¯)f×î—Ørñƒ®_»3ÁLP0Ó2s1:ÄrÈ=ê=¯˜Ê£Û‡ï[fEû²ŽÆ2J¾îµÞzÎ.~Ð9ãÔ+˜)Zèéwöl]Üþz~§âÚ÷ü)ñðàßnÉèÛözp:tÓXÛû ÷ö-ñºv;¤Q“"ʽþnb¯VÖ´º«Öæåwe4³"üb˜÷9%Ð5Óü0!ˆ):d=(ì¦ëq¯ˆLwÇ´n©gC“TMš­(‘QË×é…:pçE »kQdÄ”/Í_Žó’aQowåEG—Æ9Úë^Þ©½÷ïzë×Vþw Ð}Jß<¯ d8?Úò¨ ÌKE4¤ÓðnWZo¤×Ø ?25y¿yƒ??èTVÌÈpý~!­xš^ׯNi~^qtÿyý(«dûè þw ÕÂNE6“ßœ¹ÇôÕ¼°\H_Kýü¢ iÇæÎÍÕƒíhÒªFö´ñ£qšB8ºãÃXìÆ*.uÒ¹……dÕkx#OI!eï½´÷å;y¸Fqö—Œ–½ñĉ¸Ï)„®BÁÞhÌ-õƒ 6…TüüðWa!‰ìû$mÝ·‰>~^d¯z)«Ý_àâçË{—©þ.ÃÊ¢4y¿n!-Ö¼h¹f Œò­.@¾VÞûØìï³]ê¾-mMî0Üû™…t¼fжdeÕ»ôö͈!¶thBøÞ±2¾áãi öMÈ[Lç°‚îz iÕ éMíó¿ï£¾ç®U6º##ÑéMU!œN]çÁvûo¸Ép¾ô…Ô6î¤YÁšª}Ï}ºvò¦¬¶ÎpñƒnÆÞ~‘º7î=~èVMÎj=°€´—±;ÊV´z{×ý“d¤|¾ìá¬76\üÞV9ÿnfâþMY>? ‰9­;èTM>%Ø&ÝÐcEu‡(+_È(ÆzR#w? Ûê5cå€Mט‘¬ që"h¥÷y{ÍŠ*›®4TTÉÈäèªIçÏoäâ]“!˲E®2šÑå­S—ϧoïÍ¿ôÆŠ2VMˆxªQ}8§A7JóÂÔe&¡Ã«·SÆ‘¼ˆDC׿ÿÞ¯:2ÏêòMÓo1¤ëVN'.µù±q·ìü˜‘á¯oÎ-¢ˆº†÷ϧ½^Ù.W¼¬i1®–°2bß:óêÇé¤Ð™x_ºÿ¸³Œ‘­­¶š¿¶ˆF4<]ñè}½îs&cÜ kò™Q¿\-£;a-r.šqçEí:áÐâP)³Ûàà³û‹¨xµïýâ¨<êSÿæóÊÖÔV5-¿úCm?Ïå 5t Ô>‹ú${3½,Óá@íé½)¹ïÉ<2ÎÞé¸^:]V÷¯@ž—LßÿC‡ß» áð:ÌŒóOfºf¡VD­nd¿<¸,ïw>ã|§ñý*_ ÿ~Ó  ã~ŸäÆXΠNóÝŠ(¼èæô¯½ò¨ö=3«ÅÛÏÙâú´¨Y0ÆHÍçOè¶h^4pfB"¶Dn¸ZD56ËÏ$¿Ë¥Ý[9+Åtl;ûàCF¶©/¦Ž¶ãâ4~Qïu/2Ü묈¾v¿1îãùÜßûWî}êX[ã¾e»fwáf.~Ðq¿¯8ÃhŸÅ‘“ö‘i.5r9;öa¾% ©hUlmŸm.q+8:¶Ëx~÷8£x1Âr_ru )Ÿ°hD.… •vx½ZL¤y #£Ñ•MÞlátjèÞtÕ7nõ!¦YÅ=ˇEÔ Õü€Ëuri¨Ñ"Q¹¨cÙ2ªþ|ªàüRN§õyPó\u³¢ýÜï ³‹ˆ½ÝKÒrøþØš6vp12SF]\‹õs¹ó"€.ÂiWEˆîwžE”Òj¸£¥KNíuElÕu×ö£ nG… àtÂ÷µ¿ŸYÀ¸ÄÇ=)/,¢ICSÎä¯É!#Môd®f.0ð£—'î4%ƒM\ü kº§ðÑݤ•´zçû%Ç‹‹h‰o_ŸØ9µ÷7ÏšVÓa²ß'ó÷tN] º½™–në«,Ï&adÇQëúl¢1¶«.ühc©AÿÔ=Ü} ÅŸxȨ41c/XwïØ)g‹[…Ó‰+Ù4*pàà^7’óË|í8ä#·w=ü;Eòyó}íïLSï‹oLÃù8ªšìb'Î&f½lr“tÝ“PYm¾àâ]Ô`ãMíO½ë§´9EdóUö^58›N86v²‡íïþjJ ¤qqß}¨rõõ,Ut{’YDzz†×ý”õ»®ŒèŒ%®Œöw6‰x²”Ï›Ð=>'K/žzžš-󴜊x—æD|PdñûVä;삇½2’åï;èjNnŒ¼Úè}?ûèžO|yë5>qÿLMÕ²{Y°ÕŠ<º²+xô“˜˜%qçS] 6ý¥¸Ð‹Ä¾XQêµ}ý̪߭ý}âïóÒh=ûÆ#ß·@7I r»ÝM½49×óफuô²~ç®þÈhÕ«õCÛpŸS ËöÍc/b]îunQzƒ†þ2 ‹õ°Ãù¨_f‚‡CÊdôq»²å®/töý|º —ÒA»ùúò¿{*¢ç÷_v8IiõcCu´¦'öìΣŒîmÜ®ËZN§õ±BÈåQ?R¬Ku¾u²ˆºwJmwÑ8“fj~8cEgW¹±LF©M¦‡ù,æï;èØ_cü8p™¼3–õqÚ‡|ûtB”nŸLâ®#+ªr"K+º2øÎ[·—Ü÷B÷ Ï–èŽß¯·?QDÁû Ž7|ŸA-†:Î Ófögdt¾·ör¡/_÷ ;Jì“ÁkÄ>½k#*".ŸeЮîìÎ¥%Zù/4DßÉý†‹tÜû›þ´ÌðˬàE´ªKßU2~ïß+]Ã[þyI¡;sIéY»tkYßï‹Ð7ÎØâ½l‡Euû¶bÐôwæ4ð¨pÈæ _j)¿ãÚª”ûœ èV¥_‘ßxy“_¾ˆnæêØÏ Ú÷bæªsK&äøÒŽË_Ó‡ÎæãÇž—«Î×Ûq›4¯‹w)"Ǿzçä­2jgHy_¤ì/ñ¥#†Çº¬zÈÇïS…ð꺞¥ã~ݡŚ…reê°¯Á3Õï糡KØøR›÷·.·+ãã]ÊámÇN  ÷J& ?Ò¨4õŵ  -IÙ·ÅŒLgØïÑ©+£_SÎéN|ÀçMèbóò²µïñÏ· ©nkÿßœU´_=`à¥íÉ“üáK^MÖVºØ1gIþ$Ñú;Kòï|¤ÿ:ó‘ØÄüuÁž;öï7ü'ž‘ôo'ù/ÍÝþgœ'ù3’Ä@ òЇ!9¨z(&"à T@[€ã þã¼n…Àþ_áu+RtPÌ ÈA5ÐCqW Ú(tB`@ ðG ä‹à?Ûœ$ǧç­6Š»؃P(öÆÀ(ùÂoì€?P]4FÀ(@ ÐGc Rú;?òoôߣGúÛý¿Ñ±¹ÆŽ9{nX½9¨zHT"à T@IKìA¨$1cà”|B3vÀ¨.œp Pô‘ðÄ@ ò>!9¨zH†"à T@‰QìA¨$Jcà”|Ò4vÀ¨îÿÁ ÉiÞö?ã I%_( €ðj ‹Âa€Ô}1‚| #Àñÿ1^·†(N’…Ï­ðj ‹"f€Ô}51‚| ƒg$@ª ž¸ÐFñ3vÀ¨.Š¡p PôQÅ@ ò ¥!9¨z(œ"à T@ETìA¨Ucà”|5vÀ¨. ®p PôQ€Å@ òб!9¨z(΢ÿ?øÜê ¨ ƒj ‡"/®@´Qð…À€* @` ’o €ð§ÿ1/’íoþú¶ýïû¤¿=Òÿýéï>Ò­>é?ªGbó„=Sö{³ÿ»1p Pô‘¤Ä@ ò–!9¨zH`"à T@ÉLìA¨$7cà”|¢3vÀ¨.Ÿp Pô‘Å@ ò’¢!9¨zH’"à T@ SìA¨$P#à è#¡Šä$WC ùßÌØ6vÀ¨.’°p Pô‘”Å@ ò´!9¨zHØ"à T@É[ìA¨$scà”|b7vÀ¨.½p Pô‘øÅ@ òŠ€!9¨z( "à T@BìA¨ cà”|ñ0vÀ¨®à?ÎÛVEÉ8ü+¼míA¨0cà”|13vÀ¨.Š›p PôQìÄ@ ò Ÿ؃P(„ÆÀ(ù¢hì€?P]I#à è£hŠäPC rP ôPPEÀ¨€6Š«؃P(¶ÆÀ(ùÂkì€?P]b#à è£0‹ÿÞ¶º(æFÀ(@ ÐGq)È:(ô†@ä è¡ð‹€+Pv¢;ÃüC„Kç¯o›Öß}$‰Ößéoô_g/‰½ß%|ÜØï¦‡ÿ&Ž@É'*`üè"q 5@‰L ¤ è © ƒj ‡$'®@´‘ð„À€* @4Ž@É'C`üè"9 5@ÉR ¤ è q ƒj ‡Dj ’OªÀø5ÐE’5@ª’®¸ÐF{ª€ Ù8%Ÿœ €ðj ‹dm€Ô}$o1‚| ƒDn$@ª»¸ÐF’{ª€Iß8%_ €ðj ‹‚`€Ô}1‚| ƒba$@ªЇ¸ÐF!{ª€@ðçq+@2Žÿ Ÿ[ ƒj ‡‹[\ h£° =U@€Bg ’/zÀø5ÐE4 Õ@EQ\ h£@ =U@€‚i ’/žÀø5ÐE15@j€>Š«HA>ÐA¡5 Õ@…W\ h£ =U@€¢l ’/ÐÀîßés[(îÆÀ(ùBoì€?P]~#à è£)È:h Ø_K€üú%¶ÏøûÜío¿$ÑúÛ/ýõK{¥ÿœ^É€¿Þüg×ÇW b?’”؃PHZÆÀ(ùfì€?P]$4#à è#Á‰ä$;C rP ôüDÀ¨€6¡؃PHŒÆÀ(ù$iì€?P]$M#à è#‰Š€+Pm$T!°  `#P€ „+Rt| ÈA5ÐC2W ÚHÌB`@ QG ä“¶°þ@ t‘Ä€P€ ¤.Rtà ÈA5ÐCÂW ÚHþB`@ G ä ƒ°þ@ tQ(Œ€P€ Â!RtPD ÈA5ÐüÇyÜê¡8‰þ•·@j€> ™HA>ÐAQ3 Õ@EN\ h£à =U¬—  P€ö%C1‚| ƒÂh$@ª ¥¸ÐFÑ{ª€EÔ8%_P €ðj ‹k€Ô}\1‚| ƒâk$@ªб¸ÐFaû§¿m5ÐCQW Ú(ðB`@ àG ä‹¿°þ@ tÑ °S„€âú$¡Ö¿Ü'ýí‘þöH{¤¿{JÿÙ}’!M)ùÏf¤ Ÿ=”!9¨zHX"à T@ÉKìA¨ôDÆÀ(ù °þ@ t‘쌀P€ ä'Rt ÈA5ÐCbW ÚH’B`@ iG ä¨)È:H¨†@ä è!ÁŠ€+PòÉÖØ ºH¾FÀ(@ ëw‹d,Rt˜ ÈA5ÐC¢W ÚHÚB`@ ‰G äº°þ@ t‘à€P€ „/Rtü ÈA5ÐC1W Ú( B`@ PG ä‹†°þ@ tQDŒ€P€ /øó·Õ⥿­#PòEÌØ º(jFÀ(@ ÐG‘)È:(x†@ä P#PòÅÐØ º(ŽFÀ(@ ÐG±)È:(œ†@ä 衊€+PmU!°  Pd#Pòר º(ÀFÀ(@ ÐGA)È:(Ά@òïô·­ú(ìb ù@EÞH€T=}p* @ìA¨4ìeG ü‡>‰­ñÿ¯½Ÿô·Oúçí“þöHÿ½{$cþzaÏ+{l!°þ@Íþ}HPFÀ(@ ÐGÂ)È:H^†@ä è!™‰€+Pm$6!°  èŒ#PòIÏØ ºH‚FÀ(@ ÐGR)È:H†@ä è!aŠ€+Pm$O;àÔ@‰Ô8¨úH¬b * $+ö T’®1pJ>;àÔ@ Ù8¨úHÐb ù@ÉÚH€T=$op* D.ö T»1pJ>É;àÔ@Iß8¨ú(b ù@ÁH€T=p* b!ö TЇ1pJ¾;àÔ@…Å8¨ú(4b ù@EÇH€T=!p* ‚dì€?P](#à è£`¹ÐFá{ª€…Ì8%_Ô €ðj Ëî 5@EO\ h£ =U@€‚h ’/ŽÀø5ÐE±4@j€>ЧHA>ÐA!5 Õ@…U\ h£È =U@€¢k ’/ÀÀø5ÐEA6@j€> ´HA>ÐA±6 Õ@Å[\Šßô0vÀ¨.Š»p PôQìÅ@ ò ¿!9`Ý£ôЈ€+PýC$bsøÿôïµÿDÿß>U7§ö;˜väÝŽ¾X(?K}&6»=?½ð÷¿‹ Ùb«¢øsÃv\c[äEšó¾Õ3¨ë¶ÆÇKûÒž¶wf>Ú½(¡8øÈ^;æûõ·™óLDš#IpÍR×@:úvÛº¡…¼­Š>¾g¹ZÐ ·æÃ“”µóµ¹ùRöó9hÆš 2:o°_!¹%Õï×JE¼O!qþ•¾tl‹lÍÑ‹ü\èv/Ë<¹Iï>MÖ{S.>[HÛ¾¯¿Ô%ú!é^´*¦"Ö޵·v>7:vº{çW÷éþòî-÷í,$=›“!×<¤ÞìXáÎV4 ®giü7_êûvoÃl~.Ëç ¡¾ÅœeÓåÁôÅ›øSH‡ÙÖâKúïùÚv•_ÖãK7»é·äç‘A§×t8„v̹ðef!uýzÁ«—K:ÕúÎu½lîËûdróò„Ð}žp Ðþê˜kd=´ž%ßk8vl:íNˆ2+¶ –z[Žîô%Þ?L£AÇùòÉy?‡B ?½ð̼Gi´*p@̼æ´6¢ûƒu“|éUó(}·ü<2öx7µÊ6”Êi`ÉË©? hgM‚MèÙ42¼~¤ß‡¯¦ôÆ)méû.¾dù*¤iÚv~{¼Dï‰.×CéÖv€hÕO4/ΦÑì0ÏÖ:¦dfÿÒò^[_º·1büðeÜùT@Çiš±-Œœ'm›¼/¥€Â_= ð1•÷s4¡§‰ëVf÷óåçjòsu  Ée ©CöƒÔg´ç¹lxË©ô¾‹Ó›EÏM(ôìæøø…¾ô°Õ—!W†ðóäjp¼·Á–Îõ#H#s- ¡qÓgù­I%—÷ãGM2¥…­®]°ÝîK£ÏÅŠ~6áã݃éì…ANîÝ_@!Ÿ¯©“ÊûŠ™’øåœÑÝl}iñD·æqŸSÙ¸m»KÏDÒƒà£O¯˜ÎÖ×ëǤfü˜·)]»˜7»jˆ/ï›ÎÏÕŽuëÙ½(Š÷]Æ÷[wuÌ«­)Tôe¿uÄ6Sò–Ú¸°BúÇ| t›§ iìâІì/W}ú¦ÐiŸ„È/ÍMIs¹„Jéð<¯™ñ¿ø¹,Ðqþ ]`íÞu È^?ÒxkQ2=½Yaºó  ]/ž68í¶”V÷»¹m'? ºúëTÍUЩM§V|ȧqÚãVæœOæç,¯§‹G¦&øH)Ôv[ú‚Íü<2èÄöA ßÜTБYìDÀ|Ú³êë£aó’iYK×ãs¿ˆèLOÛz;¥¤±!ráï¿/¢¨™?>6‰¦½š(ù`^uö~“d>ŸˆÈdî×=ý'H‰›oÎÏ#ƒ®ç–¯»£,¢©´|n›Ÿ|º­ì»$%‰÷Ç\GÏf¹e«J)²LœØœátBè.ÉÙ )šÚ˜Õ_bp(ŸVŒôòjv6‰Äš—kiêØŒ;S’}¨ÇóIZ×çññƒNâêhjêü¼îZ‹|ZrubòàEI¼ïúZœvëŠä¾]wÈšÆÏE‚Îfÿ÷+Ó÷ÅÐ^³­9fæS¦ùžImÛ'‘›é®åEkI6mN·)>ä;‹ŒÎϵúRëGC¥©U3cûçÓÅï庽ó•t`;±y](ŸÿLôÇ2ªXƒ=>~е½ç°þø°Xj=Oê0½Y>qsu•ÔäàêÇÊzëibN1±Ík}RùøA·SßÄGëd,êÜUïWy´(eÏä€J_¶àŒËFš{vü•Ã}èæ¥ÆLöC>~_+„#%„‡ÇòóhóHt|ÏŽ&õ”µóEé\‡ÌG=NûJEÑñ'¹ûHË/Çý߯ő£´fØ“;y´²Ë‘MF÷ik»üËŒ°ã˜:ùÐ+ vàwß ¡ûÊÞ.âHkÓÁúñçòhéèF¦>«iiÁÎéÕŒ99 g¿,,õ&Þ÷–‹tÙîOº^«Š£[§ŠÄZ›òHçeÍPú‰ä—¶yc›×æô¬Z'aA¹w­??èØ*ùhb<ÍžÒsCù‚<Šÿhp;f§Ÿ˜xÿòîÐy­Ô¼ù9?¼Ï:t²•ÓÚ^ˆ§Ok÷ftšG-¯¿Ømœ@ÖúÍcgÆ™ÑÏjրޛث¢"œ÷Y‡.**÷ÊŒªx’íɵ²Em¬hW¾¼QÂïó¹É4¨WÎNïÚ¿‡‹t®…‹횘 q'ºô*—œxõºNXU5øèE§n¬{¶×ž«+Zß*„ÓM_½±=—@z^†ú9©¹4ÁKåè¼)ž÷½0!Ö ­ñ¯?æ·  ã|ä(;TvQçV.Ù-Üy÷rßxê7”0hBƒz çyÑR¿åõ.ãó'tϲ|:"‘$¹ßkæœÊ¥__j]GÙëÙÁp&TV¼nâëU^Äû(rñƒ®kCr0‘.õ"¨çÒ“º.‰îNqtãC›ôƒM)1wÄSg¯Ú¹é\ü ±cá²iò áÑ^†¹´ÛÖ.Q85Žjý­=-í“èE)ìØÀõ\Ü¥Ðõoh8ò`O%½2q¶¶_.}x]p$þ],±î’¶«ÌhÆ«ýíÔá^´áŽûS‡½œNµ¦à*)'-¾ìSý\êP§¾‰RKÜ|r3íXЭ½µ=sOSTÌÏS…®aè)ýé¡JºËÚ>WäÐØ6C(e~,iì4ìÌ(QG«ËžOžÔi­ï™7Þ|—ÕO¢UoÆnŽÍ!Ëgw¿{ÿˆ!sÖÞ “]ÊŽ,9ïIö¬}Õ>BÇù%Ñ”•¦ªÆ¾9tdüú¬‹wbhy?ÖYÍ”ægY6Z½Ü“Nn£kâó'tž_rº\“¨Ëº=³$9d×nwPÄú˜ßõýeéɸú£<‰ó…ããÝÒãÅÍÆ¨“¨~òÕÕÖäЉCÛšõÒ¡¡ýêD40¡ò‚ôØÆžÔ-~ìó*9§“@wôÞQA£>ÉðD+ºËÄâæýEós×Ó‡ Ñ‹Ö(<èò–Ë^ñý tƒí3–;Z$“ëÂAîwÊ¡,7Á¹£g¢Éù<[EĺÙW®ó ‡{lÈ÷/Ð…na'â&ÓíËíÊŒj²éLY²²åœhúÉþõî"ºwJÖhRCZÞ¸ýñ ß¿@—ŸSÑÿæ³dºÔrrNUN65}ç öÿ8^úœò¨÷?ú­Âƒ¬#¯ú¥Ð¾Ýl…Ϧ)A/p¼¯ízz´›mÐÝùùš|þ„®ƒgtÙP‹bÝ(t²I9Áe¾ã>µÓd›PÒ¶ª=¨×çmÛMøøAǺÿŽðM!nÞh6­98Gëé0{wq#UåÃwøû:¯!¬U?Ÿ7“&?ÓÜજüVwbªg›Æ¾¾£ñ~ê\ü {ÛjXŸóUiÔ0æµ;{3éõÖßâ[É)­';ÙØ„_¹ÑZÃãÇõ÷ó÷ß/ÜG&gê6L'ÍâLê="m@Ï]¨Ë¾ÔFõÇ™Òq[ÁÛ•n”óüæÍºÁ|ýƒn¥f_:­¸|b§¤_&ųC¨­f€µ)¿^q£²Ñ‹{t;ÁÇ:o÷”æÇÆ¥ó~)Tú‘5:!«Í úýĦ”|åmÓ»½Ýx_3>~Ð/^ÑvÇÂt g/SU]Lrk‡0å®kº¯ž)Ùõ2?u¹ÄµÖσ‹tÜ<Ëtóëî¸-—3(èÓàþƒ[c½ûqªm€ Ù ç,8ãJ®#X'®Ï’B§›>*UeŸNÜ<É Z¡I”÷é”úÀÀ‚{ëÉ`îHÃèÅ®tªóWŸÎü\UèÆ­¾9ñåÑtÊ>Àø Z) LvþD]SŸ×y/¢ììÀà(CWj+hb17šÏŸÐ­z.¸ÛèB:?w>ƒv\®µ%ˆ”—Y®6‘ÊãäÝU®Tþyqù¾ÿÔªæ¶>2PšNí®i;±ZESRÖ%„¾ ¤Ï=Û쯣aXþýE®ô#|ÒÖÜkü½½ˆO ¸±v¿ y÷^3â‹÷ý$ЙoŒjU”N¬[ÊîQ*r˜~ï`Çšê>l÷Fßëi`͇·î.üÜ{þþû‡ãÝ4 ûÔ@Eþýå-âļÿ‡ ­aíóÿy<tK®Õ[±ß¯üëÎ}On=¤Bºaï~÷wßó„ ÝÙµÖ¿€‹týÚöTëÜH§”#ëž?÷ÖL>qTgÆzÚ…m|,(¦Pwü£>®®ÿêT Ï>kk•¸Œ(âtè ».jæ(N§ÕN®Òc›ÒhGpß~½Ë¯Ö~.S8õ{¬s­?èôˆlœN¬‹ì4Ê×sÜ/=xå·¿Ù4›éåÅÎT!>{ëûp~ž?tª©:ϘœN37Ÿ7îN*9´èþ–¡—ÉýahÑÇL3ªÏÚÓtpù³ÿ„®Ý©²¶Ëà¾eÛó©¤hóÉ£BF®Vß–/s7£é'9õ›åR;·–‹_ÝJaæFvÇ DæóU§Ðø¡ëü›»úþö}<56rá}[øþºs‹/¨ú˜F¯¼ØÁ¸)ô!Êùû‡iRÒ®Ü×%ÍÉœ&¶×?¸Ê…‚X{ ~ý]7¿ ï·ä§Qv¿ÐؾÃSˆ~fÊ&%yQÜëñÇ÷[{W -w¶ýÄ÷ŸÐ-îõaÄǰ4Zÿ¨wZ÷¬dŠR†mÔzéNo%ý—Û[’}ãùû»\ÈàãÁ ¯©Ü÷“@Çù½âsjüÞ“)ÚÉc‚âµ ïƒ,¦K#R¦ïu!Î7t7Wû·Ù•F»5F—É4¯Ñ÷Û /þÞWüZxò‡¹ …–ÏÛj>ï§Ývy³4ŸWžDìY¼êD>[æ¶ÿÜÝŠ½2£¿âúä÷ý¸øA7ϧ«Ýšaiôkm“–îk’hÇû¤ËÏ;¥ß°•½LœÞ¢âÞ‹ƒ|,߿ԫ²]bÓ&iĺO^ø©¤5ªÓçDŽÑ$ÍFŠ˜h ·3­¸òaîÐa|ÿ]Y§üÑSé°k$£¤Ã' cKè˜ãûŸ4_LÙobœéý»ÛŸøúÝn»¹ÃSÉõØ£^&³•t½ ZÏí¡½øâœrKªj·`b¦Ú™"ÝúÚtgLB¯ä8¦Ò¨FÚOªÞ&RöÜE:4¥÷».ÞinIÓæ¯coM’¹° ?ßB§'7^xØ*•²Ž³Fg‰jáÒ6®râoæˆí?_®s!ëÖ=Î4á÷_ sÞÝþÈ»I©”’¯Jû)ĺÓ.¯NÂ3ëß~ío:}¸òq8ã6-RÅéбÝTëV©ô6b‰mê‹2æŒÄ˜Zß«ÛóÓŠß:ÿáû¡†nÄ· 3v=I¡³÷ Y|!Ö5ÍÑ;pˆ)Š]«'/¦)ƒRç?ÖZõ+…QÈŠnòšptõuËÉ ôýEŠCt sôÙžæÑ›­HÓîtr¡ðõE²Ô†|þ„ίêà¹è“)9–-Øñ”8©µpcž<éœ1ñ…5iìcw»üÒZ3—x? èï´8ôhu ½¯p¯ëæO‡¿:]ÓþÃ×;2~ä|îòeê{³g±Ù>~ÐÉÌÙ^gh ùÚ»ïöœxº˜UÕý,ãÌÔú-õ=maþÍÑ…šõ\”5ä*ÿA×®Îü7$ÓÿÑ­~ÄQ³æÝm›]rgjý¤zùÕ;° ñó5Ûbmɯ ë 1&Mæ×Mq¤jd×}í^o¦ÖjÜ ëÈ2± ´m(¬âï?è|ÃX£¦dÒ3iÛÂ4ŽJÌîf õex :¿Ý löNzT¿cþþcÏg¥°eð†dzÊ^îâhëÛÕGÒuünßb‰§\ˆ;¿Å¥vN5¿•B¶ 8OH&Íð‡±Tÿpðžz%—™Ûß§lÓ¶%vêû”ñ.¤“bÑbÖ`ÞºÝþbõ´“IÕ§ËîSÇci𰣑tñ*³¯Þ…ÎÓs­i¥w£–¯œk}‹¹øA·7ëCA¼$Òum‡¥},9^Íy±yœ?S6¿g £mM¦gÞžÓ‰ ûäÞ­íEY=Ý`»ãòתÊ4³é™é¿Ó§Ü/ÞŠRr¿öôìRë‹ÉźÅÎ=ÎnßœD­Û²O˜bèêÓ~O¬·ßdvظ5K|oEË×¾£/á.|ßÎÅA ]êš9ÊF“Hcó³-†Êê_ºfÒñ6ï/iå€M3 »’æôyóñƒŽó1H¢˜c×–'¡º¶ÆÝaºk%k2µfÁ¶5®Ô¦Ú¦wã>~ÐuŽì$™­ä}Ù£©y‡Ðk7Ç0yâ±(©6|_íJǺ³Î.|üV ¹ýp%•:Îh0?$šõr¯\®{i¼ñ²­Ó}Ú­¸îJÝgÍj9õ&w>ЫÒ1h-Vòõ4šÜ ØæöñÓY^o{²Ÿ q¾H®üœ~ÎßIÝι%—Öè+©ßÄ:£iÿyÓ3Î'Ô†4öõº®ô èîüts4Ƴ‰ôâ'k@MvSOçEÜbø¾‘×Ué­ùêB‡'º†;sþcè&.pš—HK~ Î÷HTЛÞ6ÞŸA±@¥±¦þ£:vgšôk#îxRè®Fµ6<‘HÜþ¢‚–Ü ‰4˜ÌÜØ7®Û#+ê.Éî;ut¿rÕý¶Ü÷S@÷noÃl…‰tÞ…5BUš‰Œ‚6!Ì€nrzÊÄôà€ù•Yý]‰³=áΧºUû¶/•¶O¤ÃßXcCïÂhló:‹i\à»ÑiWzÂn(ùø5ªÆî«È½\”@_Ž.??!¡ ª) §;?`>¢Ú9?³$“Ö¬ó‡+}`/ +îz@ǺJ^—&PѼêyY– =Îú¼}ü29ÃïcЙéYIj×Ú9ø\ü ¿P‹6™$ÐQÖ~ýWÝzÚ[W7”i¯y°'¦–iç˜û®ìkˆ 3tx×`pï:Á>^ðŒ¢9÷ü6û—…2Ëf?üÐÚÔŠ_ŸºR» ìÊ}? t\|âi¯‰×€²Qd8´å¤±wÃý–¬#†5>sË—–nÄ=§àâ …îàZùÖõ—ãI"¿¹Ü)#’®šÿrI8ÎÌØÿC§ó [º1x»cÞa7òõʤzÍ]/ èFŠ«|Ô&ñ4îî½9K6EÒÉË 6G0=¯=Ÿt}=¼AÌùŠ ãü5ãø:NÙ¦«z^Q0ÏÌ®Üï·~5fŽÜéÆköìj.~ÐUäµ¼Kã5 §pjl—£Zß1š©ºbbºv¦-M½ébêN¼ß ?è~†ô3´ÝK†°g Œî½ÉÞàîÍjŒJlÈâréⱯÜhßùñ¶=G,çâ]d|잃biBë¡ žž £g ¦ Þò!šÉÍÎYS@—õvíp£ûÎ}}ÑŽóûTC÷Á®n–UI ™| |uT}ÿ™vªÍº¦R²fžw 5EÞZ):çFë—F~Ÿa¿Æèw5~·1äP8úô!u(õºœ$ÈLŽaØ]Ý”f6¤±µîNû·=O¶ÜÅû)Bîò­BOCší$§P’<ûùÍkLìï|]h|bײLwrßÝ®cWÞO:k͆hº™q³Îì©¡äoѱðJ,Ãû‘Æ>OÛƒ÷äŽ'‚N·_TáBÏhz´÷¹ñ•rÒØ¶cxŸëòøFàwrœ÷®×þxÞϺÔ9tãìhÞ—SNO\_ <Ç a·m.Ø_²þë†îüshΟR ÝŒ^>Ë'|TPŸº9ä`&§Þc²Ç½cÊ®ÝP ÙÐÐb‘¬;ï ÍÅAm£^ŸK´}<ÛENÛV¼ LYÏðÏßh†æFq§î²Œ®qéK¸øAרa–Üq¦‚âQ=ª Ð[¹ûVññÌŸ>bîÄùWrþ°ZM*…mwß–VÁðýõ²ª+­ÌîŸÀð~qôÀeäŒþòB.~ÐMb fh‚ƒWÿmkЦ†IsƒN&0yŸ z$[ÒæíZqk"Ý)7qjYI2ïG ýýÍq+÷FÑÅè…ïžwy@“Î}ªhX•Àì´¯êÐW šþî’p6Öu3¦&ø-‰YÌÅ:ÙëzŽ…q‘ô#Z±õÍ£ÒØéLId®º½Þ¾hµ]oµã`*a?Ýætè%éiƒ|4hÀãmço‡êXàU±G"³lÐß–tDÎ>0ð rƒjëМN Ƕӈ «ìvÞ®ªéŸï˜ô>‘¬× ¨`AìêuëAÞ›Ó) »Ï–m§pjùn·~ÿI!4jצ{3ç)™÷gª¼·Ÿ± ¡G¥ÝÙãÁûoòñƒîplStÎa´ Çòç+tB(pkŸ¿’±1,6}6Õ’ú;¯ZÙü„YÛܨ“5§ÓjZ)<µIooH‹0Jª’ô~YLÇ'ú™F6JbêDEl^$¦&¾‡‡/õä÷¡Çpñƒ®M¿ÏãÛ6$eš2ä}Ò¹µ ÔV7©õI[¦—ùÃó').´GåéÏźgûÊmºGf¬}[‡ûÔ;»Ùu™}Úïõí™'^ö${gA/iÌ0.~ÐÝiÞ®üû¼ÒZãÐA4¸ÍÈ.G Ó˜ïÈþmS±.žTUpë+®—ŽOOæâçC|‡"VÇí4É ¢ìmÇ×:NJgjýÕZ³ög›¼h¢Æ0ŠóÿV@÷³ÿRÙËÁ·iè#Õª AtxnžÜïr:ãîÆþcE}î¦ìñâ×Üõ©†Î"|J÷A¦7©ˆµ£÷¢ê[úm›>d’ Ø' VtêK¨oÿñ^ÔýcÞÀArââ×¼Røµ«v½N.¨Dx0ˆî~üv_¸ù!Óf}lÖ@s+þý/:ÿLõl×îú@7¦´+–Ü׈}šÕØ2ˆ6{‹xŸó©õãÛoÀvŒ^Ôìi' ñî| ¡kvïk¯fõ®Râ¬õ¥åsƒ¨[Ыº¨˜Z¿:vuÛÌëÏøA·øþ–¡ú/ÓÖîtxjQ¿õÚ¹*ưSqn{oK:÷«îd t¥Z#Ê¢táâƦ|±ŒæOϨ×=ˆ>å¿ +Ø­b¸÷,HcCܺöúÔââÝ\Mc&%öf*©Dû¶kõñšŠ)ÝÙìÜA´UgÛý}¼hI…Îæ­’&iâ]ùcÿâiƒ½È/½xjÌç@jZÔ`li–Šá×{4S³p¬ýœß5:5tƒÓnë,3u#öéRÇŠ@*XQÔð—Šiuò¶Ôê„9ß/x‘ÅÜèN–Y­H¿¨G§Yº]¢/FûÿÈ ¤þBZ È`J}:…ãi<Ìe‰½œ¯Ùè×èÐ-×¾yïH°•ÿÿx—Hw{”l³[šÁ,§ÑIçjÌéÆAÖÝ‹ömð¹^·ÍçBy‹}wЏ~,âÜÖ¥Õ9”Áø¬d@ H3éõgDÐ¥F^ÛRx˜îù³/(Ò|»oC{d0üs&j¹®ïIÇ0/Þ‹»ºûI¬ñ¯=©æ$CÕ ïKc[öÓ÷Êõ¢ís#7öàâ†?E¹}ÆrYh6Dé†Ë‘¦U&#¾8äÇœEVÑ£o^|Ýk£9 è†l Ôwô:&gòÚ–}§g2µþžÌRÞmïèMßOI‡]ð£ùójüù𜭙ßv2;uÆ/®Š¤Ë&·_¬ß•Ép¾éÖtc¦æÂ¬½O¹xµÄºô`÷œ€²ÃÌ$ûW™Çpþ´§4·÷v&s"¸¦­V¹}”í`šT{Ñȹ®Ž˜ÏÅ º®Ýbj¬ŠO1¢†¦Ódª@Z­vñQ¨3ÖU½eO+*:¶PgB”±«¬Ö¾ 5:!tgwuݬlxž9x‘}#ÞV{×Ki•ÅXæ<µøÉ’R§Œ¿¶BêEXÕ8ö_ Ñ‰ KJ|]·Ogæþ3mµÅÛ@Z+XÔ5B˜Å¨Ö±Î–´×°¦´ :ã3}n¹7U£“@·ùnêªí]ÝÏ2ý Aƒ ÚÖÒÌuã†,fúÇ\ŲÆĭüHõÚÑD2m„F'…®ºã •öho¦ì[çûËqŸ¶hÐs·¶[ƒŸÕœ o™ÿÒ׫¶nqqƒ®=Ûžwòe4W›0ˆ2ÍⲘ¨­oUÙæ¼/²ÿ>Ÿ‹tK­NÙìíëDZĊ8ˆÎû¯o\Åðûá¤yk,Á‹O`?¬g×|u¹<ü #k®h8Í!ˆOÅ'ÇvÍf†fÖ¥ZІäv¥•=ÿŒ»º™õËՙî1ìnŸóÝ Ú>ocòœl†Û7¶¤8çnó>šyóÏÕ&sñƒÎüfJÿ:î3{ûOï( "ÖEzâîl¦ö}‚[·ó×íXàMã·¶l\öm<?è |Æ-ë?ñ&£i{[ݧ2¯„FÇý³™Ï+̯ÞÿiAMr <ÎjySjHV®ƒh?è–iÿÛ W‡™Ì¸O£oØ+Çæfó~ótfÊù‘±È –÷—Î2#.~бO/­‡ÜerDZï÷i¹ÏzATÝÆñpk‹ÂŸæÄù_{Q‹F¸T¹8(ØÏÉž6çfH»§1÷Is{Îa5gï@sªÈ^¹^4ŒÆŒ4¿ÆO ÷þÝ=¦XÌþÁäÙâNòÃå9Líó¢{‹ou:êEŽQ³²¤âI\üZU |g—îÈpÏׂ髓-“@•¿ßGÎq¹âåžR{ÿq:t?Ž(Ê;Ĭe·=‚©±Ï¢dA•´¯GEįs¦”¼)#iÑ /z¶î|rÛ­Ü÷Bwÿ»‘zŸyÝh‚¾¤:˜ú9åœn%õsk)´6¥IšËÖ\srN.ââ.‚Ž{¿4˜QöbChþéΚ6¨äצjíÝë½~¾Ôl›p ?èÎù2xÜŠ†}v‹oÝÞ³ÝgðÛ :]3?u÷03bߦ¾„Ï9a0Ù]êÇ}N)t/FMÔÓ›þ€¹ Þ¯¨÷€’£TÊÓÅTÚíˆdocsÒ|¼VÞüz˃ èÎ_8±ªÁ09³%æHÆ-[¬Û";}{•PAFZñÊóc-(m<ë˜îMš[-Q› äâ÷Þw(ýOú€Ž,½iÞ# ‚v,N¶é³Í’oÔ›®Ïó,~cÔ›‹_ëJá8Í‹a ëºùvÖ¥ã%皺VÆÛKLľöíéMší¡\ü ;¦¥ÎzÆ$[‡yW…ÉÉÐÛqÊÏýTÛßzÍ-àpЛŽxœ+çî#!tMU!îÛ™»M؆=”<Úß¼â·ÿé° ‡¦-½éÔBö#w¿‹ Ó¼îw%‚y­ ÐÝ~;”¾– ›SAï\&×|¶"ÛÝFF'—yÓP‘ä\.~Ð `/ÜHf’æE‘0*ÌKÑZ?¬‚‚ôún’XQ³ Ÿ» ëM:A.®Ýæò¼ºòMÁóWG1K³OÂh²×»ªºµ¾éÔ~ØKý¾?¼ˆn¦^Ì-[ÄÅ:Éá£F2Lðµ Ïü©áÚuûÃvZ„bi£²¤ƒ÷ÕɽáEºn’u½fqñƒîÊÑ”¶  ÷{†p’ ¿_}³ªœDìåÙÆ’¶ïn0{­¹Ô›˜²~¿6•ÂÀO;CfnR0ׯ„¶‹±Ž ñ¨{R3Êé§kîÕ{‘”´#ʧÓÐÚ<¡ÏÅ:SÿÒNù æ‘Þ®ígëDÒ»‡Í× /§æ6ûœTÇ.¯Ðqëü\ü ³Ó^jýIÍlCµÎóޤÛQúüʉ}û²ÍKR¹ÎÊn:É‹ßÇÍźí.w´uyíû«Ä®’óf¡ŸØucêÆcÜý.€®ïæeÓæ}ˆeªæ.)éMï¦^i5âÛc:Ø€]q¢®ì]û6»WíõÙ“‹_ÛÚþ=ŽÉÊîxFÓ«-ZAÃ*S·Íß*/˜óûµ^”×nDLÑa.~Ð5ÿ¹تÀ8fæŽ+æ—K£©}v•õUù} 3*û2½ñצµëŒÆ\¿ û¶rëñŒ¦\MŒ!ŸÂÓ¤úì›Äfôcþ…Ž›Ööá|ß Ã{v2žáöÇcˆa_£¾ö˜Æä{èž7%Íë뽸¸áÏ¿Ø2[87&žáöáchóÁ°1Û/>þýþ«éºÀ·¢SµënÝ|v{¯S#é7xõ²õ±´öâó¹/$©ù¯g ›D˜Ò·;Ú?œ÷"ö­«ES¿EiâÖýŽïAãÑ[QƒšïbɹۻñïlÓ˜ørÙÊifäÿsrï©gkëe7n~äP±2±ÕáÆ‘ÞÐ3aÃŒSªÈ†æt}X'çoÔÞç#¹utMض«k"óöðû릗âÈ©ãÚ_fSÓÈV£'D.² g3cûo}ïE3œSïEܜĭ ;Ñ{wÇø-‰Œq|g·™?ãÈw vÛÀÇ¿ëÂåSózSï–ÓÚ[=æö!$лzÄ%2¯ÜXƒõx¼vP3§6iß‹ØÝ~ËĤIÓk½éŽ¥vâ =ܾ‡:öW2ží•ÌáC­NçÆSÁúkA¯¿>¢'ãbÅT}Í{²7­Ñ}6ýÕþvÜ:ºÏÏìS…æJFWóƒz9~»GßÇ~ßwÝ4SÞüõüŽ[絫}ŸJÉ8Îé²K˜@âøâ•)IÈeÛÅÃõÅä4ðã íJ/z+.w®ÉÖè´ÚWòþÁJæÎaöE¤D’lŒøˆjû¹ã‹Ù'ç^Ä­?špñƒNóó¹ÙIŒæ±…c"åÛä4ŽõxDnzSžïødNÜvÖQ©+›~ëÎźþìÿ|)‰Ù¼ünO¿zJê1¯¡Ó²cèJáµòaõÌ)çþ)««{¼ø÷„¹õ½ºc¿¶±o>1[bß\]µCIƒ»¿>ç¹ùmÙ´?vÚa3Z«ãä¾cúG¿e®Qyü>tú³Ø’iL«Ë‹^*©ŸøBl¿5øßšÑ“ª·{<~zÒù•znž…³¸ø±ßoqâ€+–ÉÌŒµ¹qu×%Q—ž×m ñçÏŒ>|Ñë5DË‹>æ,=¸²Ù|.~Мíò³Ïõdæáàm}êæ$QôgËF<úý;Ž Ÿã¯ ð¢N_"_Ÿ9›»ÿ +Ö)u÷y2óssÏ/³“I>êìŒ.èRð¼·r *é[<ðý2/~¿’ÛÒêP)ü¶5Ãñ­^ sÿ\»ï£ã’Éãîùs% Ñ“íq=:ZÒ"s݇Å^ľ}{±·ß%€®sób„e S~–mìRÈz™í¡µ¯Õ;3̒ܽhî`ŠïgÆ€q\ü [Þ0§a?Y ó!zøèmA)”+7|𫦖¹¿Æ¦ºX’öʃ=n®ò⟣Ìàâ…LûzQA s³w`ËÀA©t¾2©p£¦/L¯KÆXûë¬Â%^´q8ë˜nÄźí“&Ìql•ʘöìÍ•TZðÂi½¿šVoÞ¼d£Ì‚^…=è9l²×û¿Rö¼ìhÝêçôTF÷y@Ìàîi4ïÖ”Ð^çÕ¿ý»Óî²/Þ{·¿À?g€Î{OK—.;S™ìv€k]nž1Üs¯šÞ¥û6[:Ìœî®t«žóÓnŸ´»°Ðœ{®¡†®í«—¿_Ne¬ØË£m:iúBK5Í+b3/ÿë,øQÀÈj?î9Š–.òÒÚ÷Š3SÁ ís£Ï¦“dÖÑf´XMšm—ùf”Ùùu¯}½h׳¾Ã´oqÏÏбUtÃTfØ®z©{=¤M§›ÖLRS£¾cNÞeFÇ&¾ªÎ3ó¢„ÁÊ…œNçCžÆTº&yH«–z\ö þí3®ùù¦Ì‹ÿ½÷9EÐUVÏê×znã3©îš/iYHË])íÔ4°Íþë>cÌÉ(þ̪ԇ^Äí{rçEÝØì71¹¶iÌM´cU´}rþ‘–Zêß}|øZ÷žŠg^üû~üs"è†?íøíü™4f„uÔìI;T4«Ã§–û2ËÈ´±éË#–„æÿçÔ¯¸^Žtšc¼Š‹t?,LÂtn¦1)V ’ÆÞS‘¥Vh‹î²2x$Ç8¦Pü{ÿ„¯•‹t“5?4Lcúvn’ÿJEüºŽù¾±ŒÂÂLïÍ`MÚõFŸãMzî”w9VG\gã‹üƧ1SÓe;dÐn›ïœÆ–‘üöà CÛÙÐð'wúTöæß›åãݪޯftŸÆpïyePÊ¢GMê•Õ¾·JšÇßnÞüûjÜyB× ½ââÁzé ûkåö² ú¥°HvM-¥ÚçŠ[:Ž¥ÚëM[Ô9[,ûr:tÜïÒM¡È ×‡vú¯v)¥ÑUs6bM×?/™¿g¶7ÿ»î|J {µ~hÛ€®é »ÖJ7“¬3»žY)½ÐbŸ@Z‘˰,¯~Þľ QjÇ)tÇOÏÄÊ71þ¹wUÀ’LZ78³ûÇá¥ÄÝÿbrdêz“5ܦOîý t쯳ZIgª4/¼y.“_xd?©^)ÕîÇV¶ðP¦6@6 þr ‡Ó©Ùãýð6¾–œIá5N­f—ðï+[ÒpÁžÈoå^dRÚãúQî=­N• ‡ýlŒãh^çoEû³Y}¹„Æj~$¦#^&9‚T¯Ú÷Þ¹¸Aw¿{ÿž±Ò™ÇûË„YtFKî7kk Šr²[˜4??äEñZÅáÞWBçg¼ÑjTït&xø#—¯»²ˆí²FKhÕÞ&’·Í¬‰ûßq]®¥öät"èÎ;\ÃùŸ7|KûO÷³hʇ•Ì‚¦%tÀj¡OE òìøCl`îMsÙ²oÈ}N tí5mk:ÓªStÂÇ7YÔ<©÷~AvqíïÄÉT[ëìÄ=ÞT×ckõ\1ÿ~t¶“ߜ顕ÎTXöÊ›×?›Rø™žÅ”lÔnñ³~¶Ô‰=ÜFoj&ôÛöl ów†öß¹G­ž¹GçCþ÷ž}Äþ#æ¯ ö¼±·a^ïÚ‹/Û_¿‘ÿ~#ÿ–Úÿ§ó!kghÿK~#ÿæhÿgùü{çhÿK~#ÿ¯ÍƒüÛýíþöFÿw{£©/bóŒoöœ°:£:ÿ¼~µÿ¶¿Þ"ÿ5¼Eþ-s³ÿOçBÖÎÍþ_y‹üw™›ýŸå/òï™›ý/y‹ÔÎdûšû£¿ý‘ÖßþˆíþöEÿ÷öŒØaÏÇ’ýÞìÿf\çŸ×£ößâ»ö×SDë¿„§È¿v^¶HA>ÐAñ2 Õ@ÅL\ h£° ùyÙÿ+/‘ÿ.³²ÿ³¼Dþ=³²ÿw^"µ}.›¿¾´Zû"‰Öß¾èï¾Ñþˆ½×%|ÌØï¥W‡öÏëKûoñ\ûë'ò_ÃODìA¨*cà”|Ñ2vÀ¨..l#à 裨‰ä8C`þ7ž"ú(€b ù@ÅÐH€T=Gp* B)ö T §1pJ¾ˆ;àÔ@EÕ8¨ú(²b ù@×H€T½ÿDOmu!°  Pä#Pòߨ ºÿž"µ}{jþöIû$‰Öß>éoŸô_£O2à¯7ÿ¹õñßÄuþy½iÿ-¾kù@ÉÖP‡ý=0Žª^+öw©8>PíÖìï#q|ªZ³¿ÓÃñ#PòIÚØÿ¶ìï—p|$m#àÐŽý Žô‘ÄÅíÙßsàø@ ݰû»T=]öýv¨€vGö=k€ªŽì{¿8>pJ¾;àÔ@…Á8¨ú(b ù@EÃH€T=p* -Àñ=U@€c ’/6Àø5ÐEñ1@j€>Š‘HA>ÐAa2 Õ@…J\ h£h =U@€"f ’/hÀø5ÐE3 U@€‚g ’/~Àø5ÐE14@j€>Š£HA>ÐA¡4 Õ@…S\ h£ˆ =U@€¢j ’/°Àø5ÐEÁ5@j€> °¸ÐF1{ª€ÅÙ8%_¨ €ðj ‹Âm€Ô}r1‚| ƒ¢n$@ªм¸ÐFÁ{ª€ €p ÀNÒ`Õ*Òè“„Zû¤¿}Òß>I¡õ·Oú¯Ò'òד’ÿ\uþy½i…À€ªæìŒ$8%Ÿ\ €ðoÉÎÁñ‘l€ƒ;ËÇúH¾âVìL è ¶fg;àø èµag àø@´Û²¿uÇñA¨jËþöÇŽ@É'p`üÛ³¿EÅñ‘ЀCö7‘8>ÐG‚벿ÍÃñ’½aGö7b8>¨zØß,áø@´Q„À€* @a0Ž@É `üè¢h 5@ED ¤ èp| rP ôP`DÀ¨€6Š؃P(>ÆÀ(ùBdì€?P]&#à è£P‰ä-C rP ôPÄDÀ¨€6 š؃P(pÆÀ(@5ÐCÁW Ú(~B`@ G ä £°þ@ tQ(€P€ Â)RtPD ÈA5ÐCQW Ú(°B`@ àG ä‹¯)È:(Ɔ@ä è¡8‹€+Pmj!°  P¸#PòEÜØ º(êFÀ(@ ÐG‘)È:(ø†@ä è¡0Ž@É7ÀøÿCŸÄÖ÷¿ïkÿí“$Zû¤†>é¿{dÌ_+ìùd+ö T± Ê8%Ÿ¬ €ð¯ÇzoàxH^FÀ¡>ëïô‘ÌÄ X/Äè ±6dgâãÚÕ@¯;›Ç* ­ÍÎÇñA¨ÒfgUãøÀ(ù„hì€vv/Žiš²3dq| „)nÆÎ2Åñ’§asv¦&Žª^ v¶#ŽT@»%;sÇ ª%;ûÇŽ@É']`ü[±³Àp|$a#àКI…ã}$eqv6Žt  Û²3zp|P ôÚ±3cp| ÚíÙÙ%8>UíÙ8>pJ>±;à¯ËÎÀB¢7Ùß¶ãø@‰_܉ý­5ŽtP ÈA5ÐCQW Ú(B`@ `G ä‹‡°þ@ t8>p PôQ\Ä@ ò !9¨z(<"à T@EHìA¨%cà”|2vÀ¨. –p PôQÀÄ@ òŠ™!9¨z(n"à” è£Ø‰ä>C rP ôPEÀ¨€6Š¢؃P(’ÆÀ(ù‚iì€?P]P#à 裠ŠäWC rP ôPlEÀ¨€6 ¯ðj ‹"l€Ô}e1‚| ƒm$@ª ¶¸ÐFñ{ª€ÅÜ8%_Ø €ðj ‹Bo€Ô}~p* &€Þgþ¡?iý¯ßiúß=«SýOúüÿéßkÿ¹OÜÿUtªÆ,è"dÞ¤1ònG_,”Ÿåýȳ©öß½w}eZLñç†íÜce}œ–w´»-%†ÉÏ4¬žñ{Þ%7ßgÛïù ߯¿Íœg"ÒIãÜJh›ŸÆ~Љ^í¥Q©Ì¬!¿züø”K'3ƒÄÕĺ0tÛ`K ®f…5·ö¡òN>I?wñ¾nÐ=6îð C+•i|uWbÿ<Ú4̽újHM¾Ìã×ò 4T3à߇î;us"ï«]VDyÛ®I)Lã“ÝM_¯Ì£«K'µ;¸§€:Ö_Öbpà*Ü2$~©¹ñ~J\ü ›¹È¹ÇÙ¦Ñ:×¹íNçѡ빋„Tº"üÕ”­¨d;iΧvŽ?èbNTåå.Ha¸ë/œ\ ?ÏkX@Þû*'ï>iK3š?=ë€O­!?è¸yT)Œ-u,}™GÓ:6è╞O#Y»ÎŽ6Ô…µkŸáCûEnÚûŠ÷åëZ)üºîÎô¦ªdæ*kÒ%ŸrMm‡p˯½þéÛóÂÁ }øù³¼/&tŸ•Onì:›Ì4ßhÓôáœ|2¿äpUf™OêHOv~ æý1½Ég l÷'¼/&t‰Þ]®ÏJf—Ó½»+Ÿê®Ÿë3:Ÿ8_r1iÆé•xSð"íŠÖ37rñƒ®E«1×Î×IfFúlEŠÉ§AFOvn˜Oµsá]?¥ýÈ)÷&«¸ÔIçr: tËë?ØšÄT®-ÓȨ̈1‘Ã7åäý¾ÿ>×1Üð¸Â›k>½˜¿“ÓI¡›ÒçŽéêÍIL\æý¬~æÓ¸Ü=êŽ~y¿ç{²SñÎTySvó¶öý|8º:šeI 7—¬€šD7i»)ï÷ýÀNßk[߇V¨†}tçtjèSž$K •Œivð§IK èü¨‘wGŽË«½®ˆFÇQ>Ôôå0§L[N§Õ­RøéUïžßO)™Óú¬Pu.Ô²ÖÇujš>âSb§‹­[åÃû”p:t+±J†u«r­€æ,=Ðß?-—FMÎÞ§µÈ†¬¥_<Ã×øÐ̉ÓüÆ;ðñƒÎ1ïð¬·o™¦=ìz« ¨÷‡³w»ä’îËcN ·¡È®î«§ûÐÞ`?ƒ™]9:ên×@šÈhÆ@) ä–W·ô^ŸK"öò ±&Ùþ!}èNX dN'®Ó×~rã…‰LóPn'(¤£ó£Î­„ü¤xÒeBkÒŒsÒõáó!?蔓Øð+h­ß]̦ƒ[ö:2Ó†‚š6Ì+Îð!ã.éß­[p:)t‡K;OÕ²‹cBŽ-ÚÓjC­Ëì~Ð}ÛàK2–i㪨¼WDÑû²nÌ}šU›O¨Ê‴8ć 逸äs:-A¥›¿Ë r6y^v\ª·sÚ,jÿ¹»[ ¯4üÜ÷hÅVÚd~"}ø)N'€î¡9ë$ËH4ÆÙE$ûÜ#yˆ,ÚÛbÐL¥p#lÍ:vøÐ®:Ÿ2®侟º³^œHa\¯jÛíîPL»°NYTüþ»•߸ ô«á«œÄ©>ĺo|ÆçOè¸üÃtÜb;qªA1Íœ¾(Ú¥&“˜éýæÜþŒû=²LœØÜ‡Îu`'ªsÇ“@ÇV.=c˜ë~åÖfË‹éò°_gGgòþEÖT¦[WbúЛ~mßXx:‹tMûþÔ{žÍü8v¢aûb:»y€é™LJñ®?ú©ÚŠ®øÎè‘tË›È×Ï3ç®3tþW{¿°ÛÍœ‘k!“Óàû[ݬ̤Ê~9“ºž³¢£QoÒØúÙs:5t/–=œõ¦s43;sÔ…M!ÅT*9zH»_&Õúj\kÔàŠ¸Ò›&½ƒŒ6w±.¶"¦zöù§_½éû¨~Ýdüý×ÖXXÁ\9üñ@‹÷Å4éXþLÅ­»0êykòúiíÞ ozÆŽcwæï?è–On¶íUSãý⛣N Í:wi~áÉ ò·«½íxk:ê¼ý—±ƒ7EŸŠ:4Ò”¿ÿ Küøækê †±¶™¦ßbH 5¼¼bï² ÒØ’t°¦Fê¤Xx“É<ó…ùÓ9:5¢ÊxD1ì´÷ªY%T"]ØîNÏ ª·¯ç‚ów¬hÒd“ð=oš>ðsÝ )œN ]U[vbY$³«‘}fŒy ]\pnCÇjý˜Í:ZZëj–çåM5ì˜Ã¶|ü ÓØÄ·Ždž5íñôÇ\×{WF©(±ÍdÏãmÄäSW4áð1ojuæµ`ÇUþþƒnxÏy3#˜AzÍ’Gº—ÐÚé/æÝ;§¢ZÿÕ ?>øŒ"o*;~øˆÞ:þþëY)ÔíÕ§,œ±›öù–Íýº’vÈÝÆDE&³{g¤èYЋ3Æo¬ñ¢Ç‚Éîó÷t¿„¡Êü˜7w‚züJ-¡IšŸ*º&´°ÌoN=V(êo,ñ¢šÆŠtÝ?ßÎ=‘ÊxÔ¢t®,¡õkGW¯h®¢¡Ç¶ÈÖ5£ÏÆ_U >z‘ß¹/¡å.~Ð}˜çÞpú{9³,,<ýØ÷*ë±gëÆ¤‡”wìäæŸ}ÍÈÈÿÚ9EwošÙµëò^áœN]XIʾ-måL«­³rv´)¥ƒ!“D»’n“ü3^šÒiY£I zëöÐ>ŒÓI{Öú¨>`F™Xø$ê•Ò±Nl„ý7Úì÷œqÝ{ ´â•œN»ºk35„a§íµTJuÏ”gä¥SWÖžÛٌҶw²ºáE{»çœNÍžOíXIŸåÁÌÚ¨ë#–RiË!Y•‡ÓI§÷›.ÉFæäü4ºdE¸×ŸS«W¥Ðij\Úu³ûŒcÖñ®”.è²u:uˆ¨h8‚Þ<¹Þ"´£7%F~iÞ¦9§@·¦}s6AÌ ŸÊ]v;KéÎÚç§/f¦ý¾^v±Š>åMÇÛÖëí.äã]Ù‰U ã>åé¨'Ji¨êWDض4úùvmððG–¤±oóó¦Í)3eÆr׋:£Ñ•MÞl¹ÇLv8ß­”Þ–LžÙ°mÚo・ÙÉšÞü¼U¾þAÐ9dp²m3`ÛZv4:=põéì˜úÛÚrχcß”^Ôtz˜ÏbG.ïJ¡ VtkÓ?í£±µ•—RÃäc¥’wPƒ#¹±–t³þ©X·u^ĺÝu+æú%t;N)ŒË·ny) »º`ýª)´¹Ç9õŒd ZSr/¿o;/~î;§SC÷~ìÎðµî7í.Gæg—Rû±>—Zz¦˜[xלúhŒÀ=ù¼Æ÷Ÿ½+… ®MrŒØêÏ\P³Æ¥dÿôcꘙ)¿}½?´oùfl@­ßÿAwhéÇwãç]e‚7.Üôª”Úl>²ló‡äß÷Q£õ?oññ¤]]Ûv÷átBèJ·®ðéx™a§¡Oû\JÍžYð@–ü{Þê‹ÍeÖ^rÏ?ò„ºqÎ;sê=ñenn¼%¢n±U÷Ђdºs Ï2ý«9ºæ8ð£'¥6aÁÇ:ôkg—ÎöaX7ìKÍÊhDø…9_“h–8%´írsj^¾Qç6Îç?æ3)tGÞM:ûDéÎ̬ÃNŒ/#ÇåOš\I¢ÎCKz7ö©Jt’M_œ`lª£tPFûg—_òW’÷”;ÙA3ªìóÙn™½ˆ7šÿ&?è¸|*a¸õɾǦ¾f`r=‹èQÛBI»º&Tü`e¿!‰Ä]f¼l÷…ÄNù/4(£Ôúb­¶ÊDÞ‡e=é™îu´õ ÝÔu=¶šóñƒNcÔ}'MM^pc߸2Úúâ@ò”£‰´csçæêÁ"ÊöŒPÖÿîN—õóýp”t‘Öæ1Cç+PFOŸ>Z·nF" ¸6±ÏÓ¸µt­ÞŠ·£Üÿ¬ÐikŸ¥”Á¬1FÙNYiªjœÈÏA_CØð&¸“!;Ú…ï_ Ö÷GdÛnéK;¹ºðgãÍ6¥'ЕveF5¶k(°ÅÌåÏ^»“Ù“º.‰î|ÿÒ·Rxl;ˆÞ•FšM˜ÝŸóã–³Õ·.$ð~:kh@½F÷·5ð ê¸[¦Žåûè\­-7¸ìEvÍ‹⼤×5Tz,O n£Üš®^Kù9ˆ¼;mÖ0wów>~Ðqó­}iáQŠõUFšåTÇrÚçh·ŽJÖfêÞös§Ê—^Ëúðý tͯ¥¦‘ùQRÇO– /£˜©&©×òãiðÝÕ›<®‹ˆ›îN¥·e¬°àûè¸:u…äìe1°ŒÎØzm©ôµï¼xʸ¤½ä—À„²Ceun¹“]ž¹ò÷tu:Ú;èiÕcw¤ÊhUÔ›#õâ©~áÛ^JMˆíò3‹Ý‰÷çâ×·Öà6 <¹|Eî÷Ã͆Xj…ÆÑÄéìÎ… mdöšÈq½ð׿&~ý*…WÝWÌ©cu—œÖQ\æet"<'ýÔÆ8*–~Z5~¡ }¤:Aîµëy.~ÐÍL9ØÍº(€F®QœýUJ]ö|Z}µoßo®çû^72ë1®þÙúܾ”ºj«ùkÍê’°Dxpç»RÚº[¯³àq,ÜvÏꊈDìÇèçö‡¼:n~mu[Òrue)õÈ›ðc™o,ïS·Ž\zÓñ„Ê•¸þ”_¿÷«Ýï¹Ïû€¡¹ÛdhÓXJW¼?ym-ýzÎ&lWÞ§ˆ¯Љv,èÖÞ:˜"°Žàè{æèOìËï[¯¥©Ë{ý8ìJšrq‘_ÿAÇ͇á}ÄK©÷h+3ß§14¾ÂB^Çgµ•ñÔï´+eLv±«Ìï¿@§ÙÎóz@—í&6Éó+¥ÛÌ<š\‹¡Q¯Ó6¹ŠE”:}o?‹H×Úu?½JaüÏÀ=ïä¤I'Žøœ;´8bCCÛu~?Î}[ö"®Ò•÷Mãt½Z¿ìPÚpÇý©ÃÞRº5úò§DA ͳ× ¨³žM_7Ùýž+å²öeyÜçB§±É £ׯ/˜`YJY]wOþZMG uYõPD}4¾+q>?ÜyAçÝ´W¤I8…¿¾m8ר”ÊnÌh¢pæ÷?Dô&yˆîê­.Ôwä¬Aýwòë?èæ¿j¶xÕóÒ-˜²zl)éçɈWESKù×V¥kéù—‡¢G¹PÛçÏzUçãÝeÿŸ«#é.Û^ JiÚTa›ºGSÑîÚËŽ…ŸíBù¢G½ÓøøA7âûµÎzÕQ¤?ËüäXíR:;`×À:O”Ûxîµí«¨°S«Ó‡ç¸»[4ô ÿéÕ>ç`èÕ Ý×ê¾Æºãåàʽ÷¼Ï Š=è8HT×…h%ŒWq:­þ•Â=Q¡¶Û”W3÷Óìú²nñi×ý jTÝÞÐè»1•žipt²›3½ æÜõ1§@7œµÓx¥ Õ€W>>(¡÷2ÅÜ>ó¼ÿ»1uº2ç×ù%Î4A³ÁÊçOè6?1jMÊÕ¬sK å%z»uQÐ~á”1 ½2¤c¢¡3=Møp¤O7¾þAçó¤bz_u4ý(V]ôÚ_B5F, ¿.^AŸ/ ›”ºÖùï'îkk½"†ÖMŸÑ¨÷ú"Ý{– C ’/­µŠjt´ºìq&»Õ óšäãÝòàË~]Rcèa›‡u§às†íZpò[õÕ4&«©ãÒc›Ç88“ÃüL¿ëüþ'tߨ“X¢ÇËo=êYBË[”÷>EÒo[,§¬#›Ì 'VÛ9S}v¹}›¿ÿ ãâKœ¯U 2+(™W'Ц¬Ûýmk†ˆ¶¶b;wgê%Níý¼¿•ÂÎuÐyÅçËWL†Æw÷­ÞIƒµ+÷uI[OÊ‹-'çT]â}óøøA§±tŒ£Q™‹°T(&»|[׊2Â2×ÎÙ„šÕQܳ|x‰÷=áï?èÒê»û{u¸S–O1%/‰ö¿<Ú·tÈ’Ÿ—jç‰sñƒM–£$#ž:};¦Ñªbê¼68¯_Ÿpb§ûëö2¡%ËØ'v—¨ÕÖ±þ]žƒ¨Ëêq T×"ê°A1UÜšÔ<Ý9Œî´`Ú×SÝ.cŽÛ\úãy€ºUÎwSª}ø¥ˆâŸ^·Úr:”J^WNÛDD¿žÄŸ¸»ã"?Uz]²‡_? ¬2YãäDÞ¿¹ˆêºß^?°y(±.k"Öщ)¬cíEþºçûO膋ìÒiÔÃV_†\)¢ò.Û·­>#§íç8íí#"íß;h×½H‡'º†;ó÷tC¶,X¾s’.å–µ–(¢IߊîÎm,çûiï/w¸yô|þ„îèˈ3ig•´¶íÄêaÆEd$ý0 Oò€lûls‰[±žjû7¿»¬±&_ÿ ³{øþÚ™çJúbUìQ¦_D¿]èµøCï;²žNd7/ÒuÖn3Œ{¾"…®¢ƒ÷ÅYIt‘Ù3óLã"zTïÎ@‹:¹ýÆÞ‡†é"{»‡ðë>üùI㺬ÁúbÍüÞô¸8‡´‘“_½ó›ú­§Ä–—f¤¾¼Àçmþ¾ƒî´aÛ•¹?’h¤ï,“Y¡…ÖïþÂ)Kƒ‰óm‘ôW·ˆVÈeÙÔÇÝæð÷Ý Já›ÄW. –%óëÖB²ø\±Þ7÷>=YÎn¯¥Ø÷ tøjŸe3zñ÷t‹f&æ(ï$Óãª%k, éNÀ€fE¢û4Ê}õéƒÛWÓ•ü ƒì7çiTàÀÁ½>ò÷tšrÑ(…zÆ(UN($6‹Y¾âŸó­¤¼¯=}?=ÏûÉñqƒîy kþÞÕ)4*ØööÐÖ…ä¸ã¼©ïé ÒaOÇ{cº3»'Z¬ó´¶µåôìi|Þ„n‚̓5â;)dxíÎ^ç§¼Ïtiw›ÙјNHK ö=Ï?Wãó&tl×ä^/•ßï( éǶ×O äýÒ—Óå˜ü)އÏÓsTKŸ_|Þ„.¶%»ÐL¥6:ìzœ+ Ÿ3uö· ¤ö¿ÎÞmLYÛßçk÷•¹øAwÚìœÑˆË©´ÞèjF…I½™z±²°a -8c豂X÷Øa;Î“Ææx#ß Æ:ºËœnŸRéeéɸú£ hGÖNe©ç=ê,¯·=Ùo±nØ ‡ÎÓå–ëv9òÏ «7åÅ£”ii$n´zyªv¹<_Ó¡ ÿ=rvù¾àçë5ÔmC—{í}ÎÓÇY3ÒcøøA7mÄ‚;ýÓHc»^˜O±×Þ¼Øq+€ß‡QÒØ{Ç?Ý;O%S¿Ç:óûÖеpÐYž^šF¡¬}úÍ|ê=ƒu à}œ×óùúüû èìÚÖkЀtšZ§(Úa_>M î2uUÿ»ÔÿRõ‘µLÈ6=z„ï¬óið6çìcþ¾ƒÎ$ïÓŽ9[ÒiLÂ|ûÆóóéÜ,ÇU%ßnÓ̓SºwrÌðsßxßrN§€ÎO¹rêõÈtú!«ÿãh·|Š{n茤[Ô§'ëHkJšeV¥ïÿÉçMèXÛ Eƒ‡{ ì`Ý7yTçqc]¹ËM´~Ãi¯j¬78_XbŸæüªâtZC*…g$Mf:Î~Hñ7&»Eæ‘BGoa¥õ ª6j[Õf— i쉇àû±Ûú ¿n€NZzÚýîù‡ô1ò˜Z~:~ô \A3¯S—ÞÓ¾joBVš¥çIV]0Î ŸÓ ¡3ÔܘésUóÏN«òH–õ~ã =òqôzp¯`==:[µ®Ê‰>íMÛRPÅtO[Ž4SÑ—6ì“þ< í¦¾×øŽc ×LHó8ÆÑ‰î•µy±ä ÿܺIA½…ó«H¯_áRÃ/¹ô(açÖºWøýJ²}éD†6ÍÆõØÆ¯Û¡K«ðذq®Š,¤ÃîKÈ¥mO‡ ùz÷2±O=, LiÓÀÈÑ:Ñêξxðë¾!µû‰*Òlë\Ê%§×OÛVXú‘ѵ a­Ú›Ó«ýŽt¢ºìãmW¾ï„îû¸ð¤Š®NkyÚ$—F¬½Þõ~s]¡ÿ.°¯sZ›ýz¹±«ãîÓùuÃÐJá“»Ž¡ÃýU4Oc|•KâØ›Ÿ¾^–Òëû£zê6²¤ñ£ë¾ ÛïDnsn„x¨ùøA÷jß&ÍãTtjâ ão9tzpý±z뽩ÿ:Õ”º3,i~0|êI'ºÍÚ™jsqB÷qNܪ§e*j0joÓ 9âäÛq÷nJìluUÏ’œÜÞ‰ֱÉðñƒî{ÇÒ[ÑßT4•ݺ˜C¹w~¾ãéJ›ÂU«¯9YP/kÙÙhØè1ó|øûºë¶Æ‹W´Í ¾·ÎÜk³6‡œÕÝ.ÝʾD•!¬‘•9Îï»±]œ#ÅX³W:§“B§—×VùrPió÷Ï¡%g“ÿìuá·÷¼Û‘–÷`;_¾þAwmrÆÉô ê8þQƒÙô¤â¹í†cŽTľVàjF-4†t޵ï{pñƒ.EÿuÃŒ•ôÝÀ½×@&›.¯ßèèçS4Kó׌ 5F’Ž¼Ç ¥_)d¿mW» bݧ;žÈ¦¬v¶½¼_£^íÚêbF¶ç»¿˜ëÈï§ðùº§÷%%Î Íc¯…Ù$êì|3îÑA |—":mhF—/Ÿ”òÞñ÷„Ðõ}|ûý&— 2Ö,²i¶Ùw‘ÃÑ•Yåf¤Øºú“#}` gñ}'t?Â'mͽ–A;Û>õ÷+Ï¢›ŽKÛ@µ~Ík~Nýúê…ãëS tÿ?öî<º©r{øxQÑ0(ADËæ0§Œ¡vœ b˜¤ BU†P@£^±h@†  ¡ÐI+ PƒP8a(„9-ƒDª‚FqˆxÑ‚¿ïÓsÊë½kýÞ{×zÿ¹ï]u­ÏÞë>;éi÷~Î9ÏyžWÝiÏÍwšÚ¡ö˜œþF]ô‘£Wš½sëÚQâøüÄêpÔÿórˆÓ÷AÌ—Œæ}Ë|á˜<ÕµguËÈáÚä=~;;Zª–lôéÿ‡ëýq9Õã×o=‘oܧ:&¾¾c/ÛªüMÓ¯ÃÜòdBÃm]Msdãjÿ¥¹©Æßqú}û|©Ãèàâù¥²æød”Wûî×¾Ý‡Æ¥ÊÆ+{'Ü1GÒX+3Žç/á‚£d8q1_¦¾¼Å÷óžYÿçϱ«Ó´w¾mG‰L•-ý“o|ë7öo2þþˆ³õx÷Ü™¿çËO/[+þ}Fhc§UÜóÁl­BõÚÃÌsR¥äöB®_VML¬ÛÎ¥«Ä=÷ÛÆÿA¾”ÛÿìS õ:ûf]«•®•îWtìóŠOuÈô˧OYrèvcÞqonß5ùrÒlmFäÉ» Äþ¹ÜtW†V2ìm=ƸŸë/ÝçV?Ä*èsøÂíR‘«æZÇóeZù~Ûýc²µÒù2úþº~ãþ§qÝ@܀ޭlY±@JnKðûÖkÐÀãåŸZ¨ýâ]ßlPÚhyÇ’ñt¯ýÆ>aƼ3⺗lÐZ ÛêmÞm0¿§“ÌoÞÚó=íLêÉçî-‡"i |ü^æîyp¼Ñÿˆ;UqAfr|©Ð§ÁèÛ3s´º¾JÜ[}´|y"i?}~ƒÑ÷ÚRÇ.«‘æÍ[ÞòqÅ%š1¯Lôûa~1ß®vœ3æ)ñßÓTÞ›c*QÓæG¤Í‹Õ¿Éž¾T3æOÉùiçÝüƒ_Ôn“wõ’¸g~Y¸«WùybcÜøÕƒ#r?UµÜÝj5'¸f\tK…ë›ÓëÔš#ÖfÁ¸å•zIܽŸWIK‰+ ?ªB‘üoÎ^Ëüp¹V®dƒú1¢®¯9çHÖ¬g½6Ř'H\ù굂+ùroŸ‚?>>*©õ7W_×- ;«xŒ‘u{n=°|èœÒù†úù"nØ”f>ü%_æ·\ýË&G¥‘÷·7®Ÿ\© ÿùzÚÚ!cnöŸqw4ü­BŽ1ÏŒ¸¼ÁêNX¾ôÌÙ¹üÈÒ#R©Ñá=5Ü«5ã¿“{*¨;“säeµí_ĘgF\‹6WÞÿól¾èûÇ‘©µ?ÚââGÚŠä¦?-íçsÛìÿø9ãÚ]pé·~Ñ”‚|)y<—uX^Þ]õF¥ák´¯Wª äFÉ}ê1Qí9b¿<éüâõ8 q§?º:t|^¾ÌM,÷è”;‹¾¯ÜZ­tßÁWûÏ©øL¥9¢?ß6þÞˆûû¸Ç'®Ù”/[{V¾¥ÅôCÒ¨hoí­ïýÄGJ«ÖUoßjŽ1N6þÞˆëõÚ[mz/Ï—!—·œ~ûÚA±^5¨i_§•>GÙvô—ü¯fÍ‘¼¼¾[f 1Οú~Ï?[í§Œ|ùü¸z”vP6Í-˜ØëÜ:íêÕîì!ÍoYôEÞôÉz—2W×3æy7üȸAQúÁëÞŸÎÿñ€Ìß{¡uÚôõÚwÍÔ§‘2Zm¿'ŒoK®cŒ¿7âZ½¢vÏ—Z--»òÌõÐ]›&¶ß -=¬>}²ò—Ù#K—f‘¦—}gÌó$®ã gç]¢ß=ØeÅàœ³ûå™w„v|»AÓçûŽ’]í+׫Ú?]LkŸ<íIÖãâÚ_pŒïŸÔzýµëæH†iÀ~1¯o™]¼t£Vúsû_oO™nìo©ÇYˆÛ°`MÓ­­óeäÞ3½ÖËÅì•7ž÷‰–´óúÅÏ7Ž‘-»>º‡ëš’Û µŒñ&qíÕðéÞ|™}â›{„eÖκÏ~%›´Ò}ùÚ–L4H—­Õ~ÐŽ<ªÇ¥÷I^‡ns¯Edãy5‘wŸŒìÕáZÃ:›µÇªdÇɕˎ zÒÅRðÜð;Žõ’¸÷ç˜f§|‘N{Ò¯NÜ'ÃS™Y.W3ê‚ëÌ*lêM—ѯºª`”~þˆËÞýS—iû"2üØ[“ûmÜ+ùuÆT_ÍÕlÎ }3N«»Ü{fYú?ìC"Îùø÷«WG¤J÷ª9öÊé>Ù¢éûMÛèM-þ¾3]Æ<¿×ÖÔ­ïCWD\ëjýüάˆÔœ1u\“'5·ÝwiÍV­Â³ËÆÍÙ8Vz©]CéÒ¹d₞/®×5-ÕÎÌé7_ÖÏq‘\¥Q%Æá\Í-þd·$¼õí¼Ž•vh¥ûó]ÚT0v׉{26¼Üý =ÎK\Ét ü£ÒlÃýMjtÞ-½/Wxxø7;´wÛÖ Ž•yUïóvÿ.]/ìøuû‡ô¸â6mIèúÔŒ£ruƒÚˆp—äõþábÅ}šö¶º¼zœ$68÷áñÆse®ßy¦¸š"îz‹öºÿ¨<Ô7ç±ó=wÉ Õgž»çñ¦f%¿T4θNš[ú\Z?Ľ»âÂk.‘+S¥'}¶SÎümx³&BZö²¶yµo/jVÎü×çJ4µÅoYgõó×ñ‚£Ï¦I]\+ŽHÿïÓŸŸ5b§ôäK¾«ÛNmÀ@·%óð8|öûÎßMœ+_Ûc©[Úû+÷çµ§7xĸŸLjŽãÇwjíÚ¿gú*iœ<¸Ê:läø¹’žöJâ yú¾…âôëÎ#ò¢ÚþzjHfWÛº|a½]šñ÷&±+OM‰¹çÊ[ÜÕâóKú¾‘)Ämxòƒ*™ëKɯí4É8—|ûÌ]ÚÑÔI]›ôK•’i,|¿E{,ÁÜzœ—¸S÷« ·‡EíÒ~¯[“Šw޹ÒäÒ.íÕ’ðc¤÷ô˳çŠõäÊK]êq9Äéã»Ã²à§—øÕÞ!j–âÅ»µr;Îl{¾Ÿ[æMWä¹²`˜ú 0öÅ$îw®òCkIr—·_<ž¿]y¨þ†»µú–ᔟ>ZJ†qqó¤dï1öÅ$nÞïÏ·ïñø!IùvùaûÛeŠ¥ïúoªî¹9Ι|ë­“ÇÏ+½.ÖÏ_'®‡îéRsúÕƒòÊ7Šš?¶MvõZõÄWã÷hj¶ùÂ?Gɱj™iŽSóÄ÷§›6?¡Ÿ q;n‹Û÷ÞAI?<ð×Ús>•_\Ï}0íÀmÙ,5An´HѤrSªdHû’ {ãü׺^O.aÊäçtëR´Uzmp@*.ùéVÿé\©<ô•F[íÕæ_ª¨¹=UÂë.ÓÉ2deÎ’ï%êû¡†ˆ[u—º´_F%¬»’Û1WÞ~+cÀæ={µ) g5ß;V oOeœ¨Ýw3_Î’rñµ¾O¬…¸o±vÏ Ë”g2»V·IîNþ¹Éšaí™c½êû¡Îß­Þ ÈßîÏ,_íM}¿eq·y6¤­³…å–IG–=zâ©÷ÆÒ]g„5ý¾ õL=Ö¶dÊ•>‡^M˜×_?Ä_W@÷÷>‘í‰û¼?\k//«Vayªt‰ûjͰ‡3%ý»Èw¯tÒ÷iöwqö§®Û'¶ªÏ-~óÓRirƒç¿¶_Ó燌‘F UgÆgïû ×ùÇf›þ¶W¦>©nŒo”zôÉÛ¯ã*ɨ7ðóÜ éñ∠Y;õ¸q­^Ÿøñ#³÷ûÊn—œžþTËÚšž›¿°sŒ”üY>Ÿ!¹I'gU~a~þˆË^ñâÃÍêï©ñƒÛnÄÆ¯ë?ï€6i‚šé•*±?:µéóz†|â<õpœ×ù‚ãÉ¥U>É“¡uâž´v½Üxëzq¹ë´¦©c¸”I•^oìY›¼;ÃØ×[³>r¦ðgž¨Yym×K÷:»Ç¼Ù7·¨iª×ù{(>ᱺúÏÓAÜêLµQüùyªDë¤ò ;¸d>¨•¾Qò¸Ü”i\÷ëç=…8}ßí=²3ù\è|÷uYÒjqÞý‡´>%?R%Ðù•9Š3Äì”Ш©~Þ½Äü­þs[®îçÕo_eó×Y•–¯>¤Éì×~­Õ$U†µþªKÂÅ c_[=.‡¸ÆO<Ù§‚o·´Øõh÷Œ§‚2³ðÐÞŸï>|óúá—ª Ó£²cYÂõ¸qojÙ\qì6úØZã¹ÛamâÅݯ.}Ü-·œºx¶ÞÏÒæÙ¥å¶•ï§Ÿ?âN5<^yå’]²£úŒ ©Åkä®oFÞúþ‰ÃZéþê•×]mXùÖLIIk¼ÿʼnéç/ñ‚ãöË­^ÜÚr—˜_ªç=Þdœí¢{§íˆÖ¾jǤíýFIa¥ò\ºd÷WÑÏq…jzï¦râ¶¾¿—s~,]:©+å#ÚìÍÃÎ)‹vžk3ê‘Lã= }ÿxq+û.íc§lkP|®×„$Xéòª)ghåk”«þŽe¤¬šÜ·çbo¦L¨ çz\ qúy É]“ßyü“e«¥áÜÅá³ Gµq™»ûçIr¼é–ݙƾåzœ—¸¬r£› î’ûZ¯Ö(Z%I½Ln=í¨6bÐîßwÉP¿‹×2¥ü’¦#û=¨¿âB]¾ßq•&×é7ÿé»VIJ£*ï=sTk’x°åëFJɯIµ,YèQlêç¸þi‘¶nÞ!募ýuoÏ•’´:em¥šmæªáß½ž6JæUß}¢œ9ËxnªŸ‡"âfκ˹kÇvyå«gzO HË/µ6ç{D´…êvA¿ÑrcÍÑE—›eIµ‚šoõ1éùâº\pÜ©Ê@Þ6™<'«ÃºÐ I-™è¹Ù§‡~{®õ/e‰~=¨ïso!îÕ'ÿTVlV.¡Â é´ïž;Z¬ŒÜüý´Dæo\¹5ËxŽàÐÏq7ê5íx«œ(¿íP°ïr¹Vµ…³ð󈦮úÖµM•”[0„Ì’6®~)åzwÖÏqÚ\Qïø«†+ >”’×.ªæki›Þ¸z4U=˜îþ[–LÙ¢&æ¶×Ïq¢Ødßñ\ÙÔóág~üy™<{´k‡@|íÌÎOÞ[m¬¼‘W³õ eɕԧ‰~þˆÓŸão–¢·>k=Þ¾LªUk³*gb¾f\·‰åÇ7½Ÿ¤f‰¬>4ïä¹K;JÎqŽÓ7âÆžØ$[j=4öá…KeׅѧznÈ¿yÿJßß;Köïûé–Æåo×Ïqë¦Íüüýä¶óð‘—ˆ¥ùììSßæßü¹œzušçÐú, ¶ ?Ÿ Ÿ¿¤ Žíî£o”¹ánáõ)Kdúµ+UkÖ.ÐJ绿z¾ãxÑ÷•Ï’óìoV«¨ÿ\rˆy_å5[–®Û©´`Þ³ïËøìA#š·;¦(Ùh|¼LÍ*:~Gz–<4#3é½Ì6úßqNOÑ3—},ÇN«\ïÉÔnÉ{§={L+¹ ÿÁx¹}Rƃ3Çe‰GOuE§Ÿ¿¤Ò缫ekñ™÷,ëKjÍç¦MXyL³N¶Wij/k7Î:þZóÒ¸ë%¿/q]/8*7èw¬õò•’sï[ÓÇœ[$¦g{n¨ûõ1ÍkÍN(?vœ””§{²Äûþó_:œ÷êç¸!Û’ã¬X!~þ¢ï3,’¤1_®¯Xë¸fÜÇ“ ç’¬¾-³DŸÞÜ\?Ä%UWO¾?”*ÍŸ¯óõÛ %ùïÙ鿹ŽkÆýi¢†í÷g ͰÖÖSúÏ%…¸ºÏNŸ–X*Ú¢Sk"w,”}O;îœr\«öÔîc-FŽ‘©÷æT{ KžŒÿîáßh¬Ÿ?â ¼gíúMÈsUp옲@†nµ­Üt\Û™øN½æ³Ý27ïZåfó0ïÔÏqç¾è}ä¹÷eb×á†v\ —®žúû7ǵÒùàÉ×_"8 KôûÚAýï¸o>z¬s“™ åùCë›N:<_,ì,ç»÷„ÖW«ôĆŒÑÒ°äÁ{iœz“[­Ç̸`Ø+öÏâïD½N6¾|¾ Æ’µŸÐz÷¿Í¾}f–Œ©3ðQóK7Jþû¸no¯pÏ·¹òæÕ£ ê½9_øo'´š%7~GËèK‡;vZ^Z¾*‰³× dâà»’ôÅùÍÑó%ñÌ{~[vB«ôL¥ûïêå–7ÏTk´éÓ,™0v~å}¿ÖÐÏq YÆwÚ9]ö\¨Ó»ÍÉÚÌ]­ß®[ì–ƒ‹ZÏœq8K>«Þn×™·lúùâ¿ß;v؉Á­¼7?ß};×›NÞ¼oùú‰S†Ÿ%‰sRVÍÿĨ—ÄU¨õÞ°f=²`~É?òXûÁ?®N:yó:Q½%^½f¶|©¦•h¡Ÿ/âZ=XûâW噟†<Ð-:_ÚÍÛ?ܼï¼\±½vjÂéT¹ý÷[ëÆ;²Ÿ{¤äç"nøGjô==VÓãÈIÍÿêßWŸ—hïÓÎ6+jAM[¶äÔØsÇ„?›JÉù"nšz¼rÏD‹ñ¹Ó§.q³FGüçåÜŠUG˱R2\þþZgåŽ_g¶-‰‹“ ŽëKºí¯3M«è_÷¬i¡ ˜0ð£/—ýsŽ„ÂSe±·É®i+²D=žè$ekJ–­•ä+[+é¿a­¤ÿ†5%Õ?nãwBýÜÔ±ð"1X)T)ÈF&Š–i" E,~„‚f‡!žç‚!ÃFÁs#…0Süœð"1X)†)ÈF& £i" …2~„¢i‡!ž"ê‚!ÃFQu#…0S`ð"1X)¸)ÈF&Нi" Å8~„Âl‡!žBí‚!ÃFáv#…0SÄð"1X)ê)ÈF& ¼i" ?~„âo‡šjíòÓ \ðÕRkˆ6šƒ»¶ZË‚ü0Ó(œuÔš äÿ/Z3)ò/Öß.]Wòÿç5“HCÑê¹ùáGØhÎvxè¤î“’Ÿfí‚Ï®îב6š·»³ºoD~˜iäÎDuÿ‚üˆÁÚE]G“˜’ÔõùD4I]Wþsݤ²}mËÆFÞ¸ÿþ±QÙ˜è?{L¤jŒÇ8×êç¢b]ð!„bØ(Pnä fŠ•^ä"+Å+ÙˆÀD!s ADa¡°%ðQäìð €"Äó\ð!„bØ(‚nä f ¢^ä"+2ÙˆÀD±t ADa¡x&ðQHíð €"ÄSX]ð!„bØ(´nä fŠ®^ä"+E8ÙˆÀDAv ADa¡@'ðQ¬íð €"ÄS¼]ð!„bØ(ænä f »^ä"+…>ÙˆÀDÑw ADkª5æÈ?ÂFC°Ãƒ@mµæùi.øê¨µŸ86¬uÕzG˜ê©uw86‚ˆÖSë¿plø†j,vx¨¯ÖÃàØ4| Ôº |7Øh<î†j}¾Ì4!g#õž:ß 1X«÷¥ÉLMÔ{»äGÑ&êýQòðѼìðü‹5¶K×T Î-Ô{$|^ž ¾–ê}>/l4@w+5¯žÏ 3ÍÐÙZÍïæó"k5ϘϋL65ß•ü"jSó.É?ÂFµÃƒ@[5/ü4U|íÔü(òÃF“u·WótÈ3 ×ÙAÍ!?b°vTóÈLÔósò#ˆh'õ—üð#l4j;<tVϵÈOãvÁ—¨ž¯6¹»‹ºÏO~˜iêÎ$u¿™üˆÁÚUÝ÷$?ÂF÷ÃÝMÝï"fNxåÿ¬÷¨Æ3eëk—‹¼qÿý㢲{Fÿ9ã£ÿml¤êDšq>ÕwVÿ_2üÊ(B<ËB(†æF a¦˜9áE.b°RÜRL:ÒD _2üEÐ(B7üЭÔû€|n£ ¾Öê½4>7l4Jwõ~ùa¦i:mê=ò#k‚z_„üˆÀÔV½Ç@~m«æÓ“~„fk‡öj~1ùi¾.ø:¨y®ä‡fìî¨æ[’f³³“š÷G~Ä`µ«ùgäG¦ÎjùD´³šC~ø6ºº¨ù ä§Á»àKRÏÉÉŸôëdG`¢ù{覞ÑÃ@Àß_ÆG–¸²}ÚÊÆGeã£P\Ùøè?åþ‘ú[÷çL}/+ÿ[ ²QŸ…"å@‚ˆÂBÑJ†a£€ÙáAEˆ§ ¹àCŰQàÜÈA!Ì;'¼ÈE VŠ_ ²‰Bè@‚ˆÂBaL†a£HÚáAEˆ§hºàCŰQDÝÈA!ÌT'¼ÈE V l ²‰bë@‚ˆÂBñM†a£ÛáAEˆ§0»àCŰQ¨ÝÈA!Ìm'¼ÈE VŠx ²‰‚î@‚ˆÂBO†a£ØÛáAEˆ§ø»à«©ö\ ?l4w-µ–?ùa¦18k«5åɬuÔÚæäGÄhvx¨«Ö•æØ4|õÔúÆ6 Ƕ¨uv96Ì4g}µÞ+ÇF ÖjÝQŽL Õú—|7m¨Öaä»Á°Ñ˜ìð ÐX­KG~• ¾&j}4òÃFãr7Uët‘f~±Vµ^Ƕªõ˜ˆ£©¹àk¦Ö"6šœ»¹ZŸ†8˜ixÎj>7b°¶Tëv𹩕Z?‚Ï ¢­Ô:|nø6¥Ú¨÷ºÉOãtÁgSï“6©;A½çJ~˜iªÎ¶êýKò#k;õ ù©½zü"Ú^½E~ø6š±:ª÷DÈOsvÁ×I½¯@~ØhÖn»š7O~˜iÜÎÎjþ6ùƒ5QÍ#&?"0uQóYÉ ¢]Ô¼JòðÑðíð ð—½DHCQX$Ãÿ—q’_”=_+'yãþ÷qRÙ©lŒôÿ:FúwÆGvãw-d|fÿ›9(Ty(NNx‘‹¬«d#…Ë4……B– ?ÂFQ³ÃƒŠO‘sÁ‡Ša£è¹‘ƒB˜)€Nx‘‹¬Äd#ÅÑ4……b™ ?ÂFá´ÃƒŠO!uÁ‡Ša£°º‘ƒB˜)²Nx‘‹¬Ýd#Ø4……‚œ ?ÂFq¶ÃƒŠO±vÁ‡Ša£x»‘ƒB˜)äNx‘‹¬öd#EÞ4……¢Ÿ ?ÂF°Ãƒ@-µÿùi.øj«}pÈ Â]GíÇÂqëªýG8.‚ˆÖUû`p\ø6šˆ,j_ŽKSqÁW_­OÏqa£É¸¨uÒù^0Ópœ ÕzÝ|/Ä`m¤Öæ{!Scµ~1ùD´±ZG—üð#l4*;<4U늒ŸÆå‚¹ˆZÕú‘ÄÁ°ÑÔìð Ð\­§GMÎ_ µ¾Ÿ6šž»¥ZgŒÏ 3 ÐÙJ­wűƒµµZw‰ÏLmÔú?|nm£Ö¡!?üÓ j]òÓH]ðµUëE6«»Z·€ü0ÓdíÕûóäG Öê=nò#SGõ>1ùD´£z¯•üð#l4g;<ØÕ{~ä§Y»àë¬Þ7#?l4ow¢zï‰ü0ÓÈ]Ôû7äG Ö$õù©«zü]Õü{Žû/öX+9âÊî•‹Êî…âÊÆFÿ)÷œÆïSØø\vx@‘:EÊB(†¢åF a¦€9áE.b°RÐRL7ÒDŠ]2ü…Ï(B<…ÐB(†ÂèF a¦H:áE.b°R4SLPÒD j2üÅÕ(B<ÅÖB(†âëF a¦;áE.b°R˜SLiÒDŠv2üÜ(B<ÝB(†ïF a¦Ø;áE.b°RüSLµÔ^•äGÑZjÏDòðÑ$ìð €B˜iκjß4ެõÔþ]˜,j)Ž ¢µŸdža£ÙØáA Úß…ïFóqÁ×Pí3Âwƒfän¤ö» ?Ì4&gcµïùƒµ‰ZÿŸüˆÀÔT­CO~mªÖC'?üVµ8q°6Së^‡LÍÕúËÄ!ˆhsµ0qð#l4?;<´Të£ò¹i†.øZ©u:ùܰÑÝ­Õz‘|n˜i”Î6jÝB>7b°ÚÔúyäG¦µŽùD4A­/F~ø6¬Ú©õ–ÈOÃuÁ×^­ûC~ØhÀîjýòÃL3vvTë 1X;©õ8ÈLvµ.ùDÔ®Ö' ?üM܉ê}mòÓÔ]ðuQï “6š¼;I½¿J~˜iøÎ®ê=JŽhWõ!qðuSïÏ7rþ2NRý½lœT6NòÆ•“ÊÆIÿã¤dã÷EýLU^ÒDTý·©dø6 –P„x ˜ >„P ÍÂLqs‹\Ä`¥Ø¥ ˜(|¤!ˆ(,Âdø6Š¢P„xФ >„P EÓÂLu‹\Ä`¥ ¦ ˜(®¤!ˆ(,Ûdø6 ¯P„x ± >„P …ÙÂL‘v‹\Ä`¥h§ ˜(à¤!ˆ(,ôdø6Š»P„xн >„P ÅßÂL#pÖRûl“1Xk«ýžÉLuÔ¾ÃäG°ŽÚk—cÓ4\ðÕU{¾rlØh"îzjïQŽ ³…c[Ô˜1X뫽96"05P{rl……æ“ ?ÂF#²Ãƒ@#µWùiL.ø«=£ÈÊÝDí]D~˜iZΦjò#«UíåB~„Q MÍÝLíÙAÌ48gsµwqˆÁÚBía@"0µTkêó¹D´¥ZÛÏ ?ÂFc´Ãƒ@kµÖ5Ÿ›F邯Zs™ü°Ñ8Ý6µö/ùa¦‰:Ô´äG Ö¶jmTò#S;µF'ùD´Z+’üð#l4_;<tPk瑟f삯£ZÃü°ÑœÝÔZb䇙Fí´«5­È¬ÕÚJäG¦DµÆùD4Q­5C~ø6¼’ÔÚä§á»àC.b°2H†ac0`‡¿Œ“RTù§ÿ뼦Ògvÿ·{S‘Š/ü§/ýç¾ AœE.8Æ\Ï>¹|ÝlMß7éy¶ÃÅY?Ï\(¥ÿþA£g‡÷}â¼ä½›ðr«úÃåûÁæØSE_´»ÔÞ8¨^•‰YòÚ=kz|ùêߤ«÷á7®™»ÈŸ+)xôé”’Lò|?¯G§]KÒµrçÆao¶HªÞ×xö„ûÏßÜWØ6鉗eì£Ð¡$.…¸pÓߎÿöÞ*ªnÙ÷ÅŒÅŒŠØFPQQQ1V›Q ˜17¹AQLØDQhÅÐ&DA%'Ùä Mn¢MR̘1¿Z½Þí÷¼{ßxgß·ß9î1~ãœooþ_‡Z«ªæ\³ë €Ûü*¼R³”lfæP˜5EÉÉ+*ä¬K¶+Ó‰P·yÒœè.!ùV˜qxìök0‡K\ÛÜ9ü%1Éõ¾ÊôÜüûÔƒËÏW%Ý ô¼2!Ük˜¯œÚÜ9ÿFh oO•0s÷™¹ìVöå¯ìwE×o‘§š}£Þ‚s½¤·›¡sŽ%=ç=f»xÙ1CC¦c¡î¨l€L0Ù)Ôwd6]g›¡s.èÍEïI0ޝwߨ<‘Žê6+.JI¶!y”‘Éuˆ›ñM̱jþíÞC6x2™ƒBÏÅà N´sm€Ÿî=’CºÝËUISm¶4Cç\Ð[w¯Àׯïi7Ô%J¶ ^F¢&ä߀ñ7æï˜ß +M¦[ 3ÙxÓŠ€Îù.2uÔTó+ªÈ›»—?]¸|¦;—;ŠÕüÛo~G›³ñ¶ø‡Û@N„:ç›-ó{†Åñ.«G­¾ÌíÔ¾ôj†ñn£³nSP;Û¿ÿ§¸»þrÝÝ ²xKQ×EãêÒå¿Â‰ÌÎïç-h¾Úð9/i[ÕusX7Üàãj¥ç»¹ŽŽêŠk,‡_z!"¢IåË5#!Ù®ÁbZ#ÚDe xy{r×Ë þ©R“ÝÇž¿ÇFÝËA»»íN%¿^RF ‘Àþ‘æÖu`#ô™öâg„¢%ôueôÄŸ™ËK_/Ô9=mHŸ˜JhŸ“(0úÒné{)d_6}©3× \$—=À\çZtüP—ôâ@ÌN#ôu’esò®•KáªtÓ¨>Áõµ¯ÖÔÌܤ…tüPGû\¦gÊæV=dã5c¤°MvA„ÛêT  [Ô2Ÿ×OWÐñCíG˜NÒ,-݆ sû‡Nvú,ή· ®ÅðýûgiŒý½HQ·†²•2L'ƒ¾u×=¯vß>Ë£Rf.™ø;YÍ0;s)[õfNàŠVö³WÅ+—g¥µö KÇÆÀYµIy;¥NÙj]±„›ô7Á» çàÓCìžl ã‡º½2Ô bŠšÌ G¼x°z‘èëîÀï9´O½.?Ô•É%ƒTS¶Si±`Á÷+k#…<Ý¡[ž©ÿk[¿^/ëǼÙ6•™³Šº¤½3Ö”fù“<¾y¯‹ƒÑ¶ûØM :²Á§0H·Q~óäðØDM²ÚFÇuéý€!w-N1sªQwuÿ!ç™­)+Š#µîOœ^Ç| Ù¯IÀñÕÐÓs±…¨ÃàŽyÔ7<‹]1]Ë9•Çv;ö©:}v]íuÔôUÓgÑ:êl¼,x°*°gFyµ¦Bdù˜žû“kaû£I·æô´\©ëØéKÇu²¯Ã!d,é2d¼NŒZ§rÏÅ©z&~êñ!Íz•ì*y‚ýã‡KÇou+»§áŠâÈ<²fnz\x¬>)oU-Ðן>àŒzkÈÌ×¥¿OêfQáyšGdc›G¤Ã½¦“Âü>µ¾@ ³8”jLfÎ8ê4„Ã_ïQÊ'É?',9ꔬªÆå-Å5PÒÖßÀ´%tÕ¶ljqÀûÈÐþ¥i=ŸƒºÛg[´*ŸPS)/¾N‡¡ƒŽ¯® ¨S2c. ðØrßu'7f…=’öà¡n”óùчóÉÓ;Zý“ve@Ïäynrœšß}Ï<ë^¸ ¤× ø÷2›Ñ«ù„ƒÝø´¼ P-½Ñ<`j ã k;7,{Öw,^_þA!·˜ùþ¨»T´¡çêÜ|rªÍékÀüLÿ3©{׎jØ{–ýqÀ83裴hÝ·¸¨¶ìù‘O´NŠº£C7;l~•Odùòn&ô4¹~zV5¼·± á‚:§kÜk\÷½7©žþ\rÚ­ì-{¶}Ú;°€,•-² NÅvëÿjè—››q߉ §);õQ ½<ðvrª)7Ô•—RÆA¨S{ÓÌõÊ‚Ï˳͚ «á{‹{HNÙms'z ÛÐ>.lÔ 7OÌkØ\@¬U6îïž †#ԟ̬†Îyû¦O)86†šÜM¿u´k¹:ivÃöãÙðàì# ÷Ÿèô1²–5 `ºOß"éZÇCÝV•zysDUfxŸ #Î\»eœ+Uíº^’·U×cÞ³× àAûôáo¯ÐïSˆºþUæN\/ zÔÂ6âlCgÎò•@ý¥Ã»Ç ?¦ñ]®Y%`|pSÔåzS •2zÌ´"¥'90^¶@’=ÏÑÞ~buoÑí/H뤨£¿·RN¥¿ý¹°däûÇj* û5KøVDÝ @«µtão³¦•­#K`ÄJ©Gjsm.¨Ýâ“MUà¥ç±±‹†P€©±àÝéÏÇBÝ ƒÎ>+ s–Á–‡»óÀÄO!&îvØt[ç­@-^o“³™–Ø…m>LŸlÔy³P¸ý©€–Wî­)Ƀ†8ÅKƒÍ«`õâ7º[šÁoä6î@<5æÚŸ~=êT×;ß8&WHh´|ø¬Y`GfTÁµæ›™#Ò@Øj#€ƒêjýò4ÔÕ/Oûü³g!yŠUfdm>„¼;>õs%ô-7h×xeÁÌ€ÿiÊÑñ·A5MyeŸB2wt—:õ}pH7m]-©ìôãÍËGñøÞŠP÷ºÏ©ðó …Äíž`-4Yæ1púÙÊN?. vQ‚ZÁ­‹Îƒ·ŒŸêŸ 2S>Ly—Q!øù©}`s%Lïjˆ¥ëÐgj@~ ¬nyt!шöm’[ÛÊîýEhõ¼|©ñœ#+a•ÃÅQê˜y  ³GýÆøJáß÷©ºi½?WÓ3ÿÈ~6Eðêqh¿‹ÌÜkK çZ þðCd£îË€gq»jW ìk¼<¿veJpÓ[‚lL|¨†£v_ÔyQiùG9òK;½bȸ}ø«‡y¼háí]oO){è*ÔÖNËíòˆ‰êNE|nòÀëäÅÝŠ;zˆÁéµc¿²i@¹4{o²‚%Ešúßrë#:n¨£}± Ȩr Õµb(ÝpHýÂÛrðämZ_ZÂæ ­„Ç×Ðq¶[ÛçLÜP—ÖAÝEÙÂE « Vß÷ˆ-‡u2ãRKø8Èð¢ÃYl‘ÝèŒêV]²ïe_Z@f~)T%†#º7Ù*‡Xáç,E¹0šïrj!ê>Noð®ÁøÐê´²'ôë°'³€È@%àâÕOíܪr3µÃÒý-®gmËL.» è}2üûã#5´¿FÇ-çn7((œ°’ ,‡ÇÆ¥ysXY“u-ä€X—7޽Àøï¡ÎN¯1ÀûV¡Ümî•@¯à–'Æ e=Ž2@4z®® s}CÇ uË»ExÂÇÏUл¦¥½ö>³X·$¬ :}ÄŒº:xNX*€ó:ÛjšF2þ³¨S|À[¶øT™Ò¸[o^)xßlè8}¼Œñ—àBö§]↘/)[ÙáŒÿ%êè<‰q“ (/…á Dõ7µË  ÿ- £Í ûŽc]õï@V0þ—¨Ë)^õ ëíK_ ÏÛâGV-ƒNÿާÃ'¶Íø €9¯  ¸´NŠ:zŸ©€PSæãú”A}kv÷ë¥`ú*¶o¡9ܹPµ¶múExWÒt^­“[×ÊÎûJm¤âç“ûuaʦ2X¢`[ÿ&´”é›-@!­îuë’‹pç5Xœñ}Fl¼sŸB7/ƒnG…Ž.ÊÛuÌøNÙ^*_„šâ^ùÆ÷u6Ûª®lÏ'ö¯ï9TW6kŒ8µ°ØóëAñ¸‘<ìCƒ^ï{íwSΆŽê‚q]šOB”ž\6¶Š5÷w)…º÷ßÍn.°„I­¯mOF ñ}†m,~=êèõC>ê£?ô Q9Ôé]ºý «.õy«´È†—Ç<‹À±.ŸJîNeüƒQ'›¯”O¼½øòÚrèiü%ñ| 3¯ü}û¥¥Ÿ&€’¥ÖmÓÿ`Ôµ¹.¾êx>iNuöå ÷ôêÈ­%þ¡ð$ÂĈÿHÕ°±[Ï5Œ"ê”{Rßx>IÓàänS¯Þ¬±K×.aÖæ°ó\ËTÇ 8Y9WŠLüÖc}·>©Û¢|¼ò”ï pö„ú©61]ÐÝ«»94¬Tր̎´Ë!:~¨)3`Í'qËô<'†U@·µ_~©ÆˆáäŠug'7ƒ½Òwz³g `þªÈÕ!#hu‹ÕÔVNýœGªn¯j3}^î·ë$«ÄÐé«G¹°(÷€îÜÖ>o1ñCݳNwM*óȬ=+zªVBñWÜ(†¸×OîöO0ƒkO¨Ä÷Lx£œÇÄu ²l¡ýq*Aýà{ƒF‰¡1ZºrÿRsHØ\ì¤Û²å…sÿ¡®’²+óÊ#^Kžtëz¥êNÛ™ý»ñ™æ°‡Ú~ŠùO¶àfî?ÔÙ,ºÖå£I¹R3"8¶¼\˵ƒ¯rŠ!ذhö§‰ êE² ànÝŠi…÷™ûuýdly$ôÈzm‹~Uð:rÿÑÄ÷E¿ëdïtÞÄ ýV˜ømheÓû`ydü*ÓTÁ–çÖÉgŠ ÿóùÜî°mSR^M°ŽÊ6z˜ûumº\ÿØžKžÇõб«‚šÊ×·)ÁyG;eØj½¥&o 0î»c,–í2dî?Ô9§QÆ1¹$dûþ½CCª J>s»8ºR¦éÝëko²mJe3_œÖqP@mk]Ë%ÁS%«UWÁ·9?Ÿ…êuuu¸srŸµé"Ó÷Zýž¨©æÃ›‘nÇ)Œ7›÷U0@oFÏ™”±‡¹ÿP'˜eÍÖÙ‘C°X῱òËsSÄ9yÌ:Á–ѱ೒*ä´Žƒº¸É*Ã+Æåe Šë‹‹Jަ3Mó€ÞgàÂù…”…5¨Î˜Õ…¬¢u<Ôi¼]½Éôu6=ô™ ¿£tŸ¶öé’þ@_t<¾N8*™ù@Z'ÜØéM®|ºpyð‰jXÿ*âb› ÷÷þï¶ëŽzsm¬”wöÈPZ'BÝài¿ŸÙäØbÁ1תañV£”Bõ\H§ 6,@öXï[¯°ïZ'¥¾×#¢ØÙ„$NÑõH­Êív É&[UÉé+ kC.BÆ„=9ãã™øé¶²»¸µâ”Mª6þÚù¾¹œä¨Ž "Ë“/ 6ÔfæÎzW#–¹ÿP·“\LÏÈ"Sgßþ×½ÎLº´iqsöïçÁË š±1¨§p#¯ÓyžºˆŽõú·²Èéa:>''ÖÀÉWÍì³áZ~ùûåÿcÝÜ`sΫå­ã nÒOµç‹³È" ^ûõ5ðv–Š\b6Øb¶uw2ƒ´¡'|¡Ú> úóñP÷©ìtå÷ŽLÂLí\ÖÀ«¨ƒ~ŸB³à‚é¾þÞpr;øÜ±Õzgmg⇺ūŽ=i Ï${ü¬tZœj °&îV´vÖïõ샫~ óRà¾æäõû˜ø¡îºËÅØÓÆ™d„Ìx¬zº^ޜВÙéC #ÝgŸ¹§eÆËÌý‡ºª…Úa™¤Eý¥ÙRaMžãOgB®U€Ì ƒ²wt€gêTÆbòç¦V6íœAÂè7RwÜGW‰‡gþ^SÝMèÍÎ9’BŸ,Ô…î^¬š|0ƒœT§vjÀpø7Gý‡Ðé£rãT˜4nl 5™:ã ?Ô9¸ÒÀi$¾^ÓV ýaµ°fàˆíoWeü~Ž¢ž”¿ò¤j,ñûñ^“ÃäOÔÑ~Žé$>Ü*iá¬Z¸^Þ̓+MÿÝ·6{o»|Kèt¿wZ1—ÉŸ¨£ýÚÒ qpW¶¡zΓŸ<ýT:túG\Øèm9Â:èçô÷)DÝ®OÂç÷J'49üfµ0®«Ù®d•t ?¯ ðMÔ/m€ÐeTcÌäÏM~èiä[×ãE®µ0SîKœufÌxü5¼!ÄvÀ\\ÒûƒÌþЛ©¨3¸±´ÏÕeiÆgôÖÂ7¤¶6i`Ý[·×1ÏýÁòWÞZ¿ÍLü6ãzö»]½ëS‰ÓBÊá½~]êÕ×i\˜ö•¿f†!· «íúdÕŸœdî?ÔýŒUÕ>p4•Ðù«h¿™TÚZÚáºÃà·Üö‚3ë¾0õufM£¥kú¦’¹”æëZøzØí}¢w*DÖ|'Ïsôaà¹ûB³³þ ³Ñ0cúÔ•+ ±W½&"Çì‹[åë@Åï„õ´5©Ð}õ³^vú .ã--½ãaç¬ý˜ûu0¿bÙ"’„«\åñu7¿ßR³n©û¡P7]³Êì–?|ŸC-<™ø¡ÎSƒxÒB¦]4<¶cq,eæ&‰`®ÌI)ìkëß¶_í–OëD¨Ûüöôê­!)$¬ëõómø>÷Ÿ÷½~XÄÔA(µí»÷,ÛF½5š7û+sÿ¡ŽÞÏL&oÆô-k]ç¢Û:Ò§ˆÀ1}‹!ìÓÉéµ8È £ÆWu`êê(»ÁŽG¤ÁËzWû:píÇ"Pž¨`Nb áÅ!ÊðÒ.Nu6wLëØ¨³´5<ž6èÙûE½ØùQ¬©µ«·S œó¥ŒA !9¸WÛ\8qs;fDæþCíW–Hê Ÿ-^^#æ÷=QÓ˜ ~ó¨rCx*½(žq>Ì£Œ½™ûu®\=j¸4œ5|þàel©·TËZž ·FéoxÇÔ?^RrJåºö>ǬPyu×)»mñD^fdYÚÕÙ3B’ ±tæÓß `æò泬"?(´iv7‰^‹PgbZ²p¨u9CÙC®‡Ø.^ï_ N ¦Ña¶¼žÔü,ʯó9?Ôõ1ó\ªêKz~h›Í­‡”ã“VÚ¹<‚J«Gâ=wôAö_{û}м }ÞÓë¹­­lyŸ~óc¯ÆHmj£ªÂÇú=ÝÖ‘¼ ŸñWõ!.Æõém?0Ú”õisÿ¡.Ô—2 Š&3£Š /˜ÖÃê‹é·X$l^á¶-I2üê ÚVûý±Þd£Ž³~˜F‘—{Í]êaæ›àÞµ þ—P·fEÇð¥~@uSµÌúuûœS>|zI(×:{ÿzÐU±/Z‘Î_)c/Cè;pã‘Ù~Ðíõ±ŸÑ›èï…‡ºAëæDt$[ŽRQõ°ýùÏí;îŃE•Kt–Ð6¼iúÿø=ܬ>ÒûCBÔÉì¦FGþïGϨ•ÈyOúÅCÞІÖq)†pa:åôæ gD>lçõLüPçq«¾ÍpI8é>os¬vM=ì™%Qéu0^/o8ôÑÐ.ºô=Ö}ž/L‹¶½Ô£†‰ê_LµF< ¯ª®¼^ô¶~β­s©Œý}ÿî[­ì ØÔ»_Naâ·­•}%^ù!ûCinøîv¶gLÛ`qVgE,|H—o,ÉÒ‡ :ÍÃ÷­ñ…ÛÓ¨•,SÿPGuEAsCIMëî1•£`ÍúG“ĦÏáÀêÝÆÏ°ðê4D½?Ôñ©mG‹»„r­7³–/²lKÕŠ¦ÑÛÖíYû‚Ÿöcî?ÔQ§Ö†<¸C4¦Ü,^Ñ»TËR¢a÷+ýCÂ÷‚æg~{2ñüRFoîÇܨN?X%†§«Ók€miƒÿX s.íÁ¥ÎhÿìQí»Íhû$fý€ºë3Ÿ<˜x‹ø—÷æŽ;€ß˶(%·ê(h©¦>ðnÈôé5ÎCÓ掽?³Í…©Ô÷©­QQ°ë™I-À£);Ô" (÷ÝEN»¿UŸ?Ö+RÔõ¤Þ6_H®uË8‹ßa»M²¿G‚\îá¶I»¡ra]=nºÂÔ¿í­lÙöɹ+dÍÇ+û/àk®Aï|äïÏg qÚµÀßžf}p™¨Âܨ»ÿåmà°Éû'Ö÷?…7@šuïQ_†GB÷­ZN[Ïízýã«Þl-.fúÔÊ6Hž¨Ë´½© `(ÒVYr-‚Ë'6+åîƒ#T:í,ª L`⇺·Îïïúû’p¹à~Êâ0špÂåÒØ¸EÙËvãíÿëV³îõk¶b⇺£uížx’R¯Qóê`Hô„ žÂpP_sõÅ7k4FÙY‡Ïó©ÃÈä°­LüPGûOº‘ø"²÷Ó‹˜mÖñHyT8 6e¦qÀ3꙼ÔÄòRtí—O>êšy…èÖŸ$Ëü2|}n·‘S,Ø“ÂÖ»ÕÝî‹8€‹ZEþ PWøâöƒ¾¤¨c|ðˆð`߀ç]C¿I;›ö¨<j5¬çÅ{$Ïsã×ë¥îO1÷ß¼o©ã*ËßÇÇðMg©eŸ‘÷A?õ¯WO̵ßïß×ßûû…:r}×ÝKœ,!ðÜ,ѽ1a–}ðõÃ}°*  ")Ô«óü ?ÔñݶÛêV nìIø@ûˆ?†ñË,Ëvß…ÉmŸ|³¸{aˆì`œ'¤èŠŽh0õuW\¿j¯ºs K™õâbÛ“¶é|N 2ÛcWOæü³†ºýýZW<êsí÷μÇ`rWWkØÁ;`¨šÔýÎ,6ž³wãaÏ?ëêÆú¿ìºø ¬œr~Z÷ùáeô釳Œ‚!výÛøaú@¢Râ{2çC™ç z­ìwýF~UBŸºÄ ; ðõ.O}³qÃmXòÒµk¬³>´ÍÌ›=ãŒçß' u)룔Ü`|êƒdã®Á&sn1û^úí—ºéÝóó@_ÿLüP·ÔpÒ¦·ÀvÐÚýòø>WÕmN~1î&Ó¯p 'î¨Â×RØ1–Ú‰`⇺ ê²P¹©ÇÆø½LþÜÙÊvÞÜ6íY8¼ J[ÛcècX™qy»(d6Öz@ûØ™íÝ&¢NÓÉóÅã gs/F£ïc0òþxlæüKPñ<4´kŒ”,~1rZ¯sð0æóÝ)šLýC]«áQGß®Q0¯YsÅП °½0¹D"Úgy'œj…—æ9Ùsõþ‚÷ÅÉGÃÐm¤ôK{ŒçLxÞ#ÙßÛ $¯‹;ë§g‰x1ÏPW3¿°ë«þ1°÷‡d45€Ïøe9*«üÚíÌ.ÙÃ4€þ¸ ÷[Fœƒ˜àcÙ“0ÏÎÁ•Ÿö,¡u,Ô)qÆ\I|—ïuå,rnת³“BO¹Â‘³Ïï;Þ7`ü#ÏfW__æ¹êh_ÌDØóäðÎuØÔ½ù¸Ë.êú–篴€ZÕœ—êçþxÆAÝ™Wõîä=‚>FG†%oi€ÍJ$ƒíög7€"«Á¯ýÏBÃÎG¯–Ù2}'êèszIàñ%áúä… `^ýëv„%tïB=Y3€­ÑwZ.»CÙ†Â֞˙ûu?ꚯsOíú¢”w¬ðMðÚýØ7?ÛoÆ'?õH>;ÏÜ =|”‰êTÏl\àÃI¥xÕ+õh‘ãi9ùþˆ(Â´ØØÜfQöõ‹Ïý¼©{¨£Ï¯ \—=«‡j“‚üïf$áЧ"n¨ô°¸¼~µÜp—p'2ÏûvcªÞùaCÝUôÎȯ‡'‘?¥žGÈEEÛ5G ™ó¤n0vIx3a3÷ê†< ¹Ùl.ñdêIW=!ƒn.ãnÞñ07›yî•çܘs1Lß‚: ýaxKˆÀJÚ¿|²w=|ºžºgéYW²Ÿ²ñŒ3ú÷ n Û6ä3ñC½ ƒå­ë¡ÂSÙkË²ÇÆf«Õ `Sa®¹Á±g“fʇ1yuµ›.ßôòJ…sùoû¬ÀuÑÏýv-Ú¦|2¹MvÐwðkjùÞ èç÷LüP—¥Y–¼áy*ÌmÓ9xz:®ß2&ŽÛ±Ò¬U¥V"&0ÑoËÂôr7Xjä3©¡ “7Q×O¶±œTvÕ¯n_ôKz¬@>ËÕή5†ŒÝTÆuý㾕¢î éþcü«iðy‡÷·í/ê˜þ]@:îl=gÎëföÉzÉS'èéçÃr{ZÙô¸ÞŸÓ`ÔÀóÎ:Ùu¨óayìâ rød´–¡ÀN®öì=3Ð¥ó<2?Ôõ*Ó8¹b]:hÙ…>½U’ÝÙíW®|j[÷’|U¬°¹Ý(·à/&ôë±Q×ø:¬þÈõtØ:úÐì:^T= µ‡Z®‘Î}—5\êä¹ëŸ} êõ™ w?¥ƒìxîî:ðjJ}Ô¥ïu2y÷Hýn}8ÅÎ6Ûâ qÔ1êÓLß‚:…ôÕ´3 ª~f¨:Xó Ì!vÌ Bû¹ãz¸Wžå7×?žc Q7«ÍE¼40Œ sXGÖÁ¯Ëõ1çÜ$G5 ®ÉÓÞªg¥d‚+È-(ÎaúÎ=u(ÒË伨eö¹n›ÇÝìfTÖ=nî ¦q¹Ö‹˜º‡ºõ2#ÞL_g´uDV-”%¹ï¾wô6±”%N ºiÅû®pº&dC¯H&îÅ~îINÞ çLpjžee.¬…ͺìܲ`Òõ÷Ÿ…† Ô“DW }™¾ut]ȱ|åcµàÝöeº\Û"kç·AEÂy®Ï9éø¡îõ˜utÜSÛgl¹¥ú—U|›u—\žC5¸Æ ©Óµ¼<ÒõsÔÝ?gí·É8‹9ÇQ ÏëÍNZ»ß#£fÔOèéh &²®í"йKëx¨£ÏÍeAÿ¤]¯ º×Âö[£}cC ý}ÃòÜ–ò" WYâßw©ÙUÔ‘w~½x¼¾Z̾8†.ºO¶Ú&¤êž1†`…3·Ǹ§ñ+ô2™s¨K4Ý‹­B6ðbÌ«\¢kàÍ›k&¾µ9p˜1P«ÝM®ü˜›­@˜ó-¨³ýhè÷Ý=”e?¸¨°#Ÿ$>~HÍœèÛfl>¸š]zÂ&R·Asßíkeÿ|ãÞ§¢0XZïØF5 kkùá¤sßKö³0Wf½IëX¨›>r–ÊíA9ðè%µAP/I­+¦Fú<»!PÕÁ7Ï…Yß2÷êö¯¨i·#ü¨í±Á5 ¤ã‘AŽo3©Âù©Ó½òÖ»üq>†ƒ:o+—•+/瀹f‚R·ÕVÁuÞ»/’„î*ÝÔßÄ:Ycó†»tž_¡ã†:ß뤕õ9p¯Â+Q5°ÜþI¶Dš¡1Õ@fìî•¶¥ß²ª˜¾u‘Ü G(ç‚cääöÕ°k‚¯gØõ(ò®èz¿m3Au|ó¤9. ù¾åR׋LÝCÝ®n‡nìÍ… ·,&=>P §æ&™?ÜM”ÆÝyøÔÂê'Úl_Ÿ>ÏÄu²ãRA¹ ;V³¬lº–™Õ÷Œ!¢Tâ3…„ jƒÀnß:s½"—9_¶ëãöÊ\xJ-»‡WÕyÝOIRcHü¢Êóý™Â¢Ï*Óç¿v©õ{M—ÿ¤u,ÔyP?·š_ Þd%©óº?9Kn—Ó\k i3>¾ç ¢—W©—Óù–º‹ú7ïËí›]UkE(–ç-¼¤G$Ww›íwï®Ëº+/ó§uÔŸîÕ|&2óËn\@+ùäÔãI¡û c˜öª|•† (ú¯*xùž>—ÆCÝ¥Âf ɃÁýžŽ4áJ æ±ƒñZ~³Ï‰:å½·½8PÛ%ý¬—ÆV‚û}ïÅ3¯§W&«`ä3ˆ©ïþXó”X¿°ºúœÏäMÔÕU9Ú®ºP?Î|¨­r¬„Œm}&Ýï²±"Ò髨"t¹èÌýDÇM¿•=:·n=.uãMzÃ+ÁhÁáã ž"B}så, eM鿃nLý¢Ï³PgæQË•+j÷quc ˜¦°Ãà‹ˆÜܤm Íë<6¸ÁÙÆ2­c£ŽÅ¹_3gl!P]sàÝ ðrÃÓÒ0•tþ.—8i–vƒU»ýW|ZJŸæ îå㣖´c‹ð’¨€ê÷§öÍOeΘ1¿Óqê´Î¸Ù̹\ÔÉËï)\¼+?[vûRtGi¤æþï»"ûö<²Ç g˜ûuÛÖݾf}¤b2ç,ñûQ:ËžV~i¤óœsôâãé§ZÜþ¸_E¨Sì1;˜ãY*Y·H`Z9Dðlϧ4Òù|÷Òk»ÍûÜ`L¢0Åx­“¢.?Y¦{«ܲ/½|¦n5›íØ•N®Kú\ Ð3ƒí¥çªtƒÄÃî1ñ3ÀuÍ ·•_â aêЭ[¬-Aé ŸÔtœo…­¿X¾c³Ý`OÞÌ̹jÔu­w£œBf­>Nøš5V=ƒÐ¿¶€éùUÆÏÀt-g²¿€ŽuaYƒ*³$…ÐÐf¸„%.ƒgÔÿ¹˜A˜ó­àß¿ò×ü‚3°òò†ÈŠVæ<<ꊶR™¥R\íšë}ÊàÊÿ+Õ½3‰l™8è4Ïyø’Ã$K-Ä A¤ˆ&O]Ä!ˆ&S."D$ÿɳ'ÿWó¹ÿÝýKþ«ÌçÎa ™b„ RD ›.⎈D "D‹ž6ÂCâ‘vD ‹  ⣹“ÿªùÜÿo=Lþq>÷?ÎügÿêÖú»wôwïˆ'÷·7úÛýûìQùž‰+õÙ©ÿ]qGDH¢ÉŠ‹ ¢ˆ‰Ká!ñH;¢†‰Œƒ1"IØ#áHÂÂ$§‡ð‘&ái!ÖH"E”0ê"îˆé@40!r!"A19j#<$iGÔ0Yr"Fä1q²{$iCX˜Hõ>’Ã$U-Ä A¤ˆ"&Ym„‡Ä#íˆ&]"@Ĉ<&`6b„#m ²ÂGr˜äü¿3ŸûßÝÇä¿Ê|n1"…Ø#áHÂÂB§‡ð‘¦èi!ÖH"E”°ê"îˆé@4°(r!"Aä±@²{$iCXX0õ>’ÃO-Ä A¤ˆS]Ä!ˆW."D$ˆ"Zm„‡Ä#íˆ^"@Ĉ<aö¿p>÷ÿ[“œÏÍAˆ˜i þg>&,¹¿ýÒß~éo¿$’ûÛ/ý;õKÔýÎcâF}65üï8Éa•b„ RD —.⎈D"D“š6ÂCâ‘vD “ bD±G‘6„… Pá#9L2ÔB¬‘DŠ(arÔEÜÒh`²ä"BD‚(bâÔFxH<ÒŽ¨a"å DŒÈcRe#öH8Ò†(a’ÕEÜÒh`Òå"BD‚(bÖFxH<ÒŽ¨Q{Hˆ#ò˜œµk$‘"J˜¬uwD„tüÿÀϤ aañÐCøHSH´k$‘"J,|}Ä!ˆ."D$ˆ"m„‡Ä#íˆ!"@Ĉ<$6b„#m ”ÂGr˜b¥…X#!ˆQÂ⥋¸#"¤ÑÀ‹›‹ ¢ˆ…Má!ñH;¢†…Žƒ1"EØ#áHÂÂ"¨‡ð‘¦ j!ÖH"E±@j#<$iGÔ°`r"Fä±x²{$iCXXLõ>’ÃV-Ä A¤ˆZ]Ä!ˆ^."D$ˆ"amÄ GÚe=„ä0Z ±FB)¢„[qGDH¢œ‹ ¢øŸàg"B: l¸ˆ‘ òð?÷3¡úŒ¿ýÒß~‰'÷·_úÛ/ýûôKZÌ5'bÞ»þw\D€ˆ©÷ƒ‰ŠØ#áHÂÂÄ¥‡ð‘&‰i!ÖH"E”0©é"îˆé@40Éq!"A1ái#<$iGÔ0r"Fä1²{$iCX˜õ>’Ã$J-Ä A¤ˆ&N]Ä!ˆ&R."D$ˆ"&Um„‡Ä#í “¬ÂGr˜„«…X#!ˆQ¬‹¸#"¤ÑÀ„ÌE„ˆQÄäÌFì‘p¤ aa²ÖCøH“¸5."D$ˆ"&rm„‡Ä#íˆ&v"@Ĉ<&y6b„#m “¾ÂGr˜ …X#!ˆQ‚ ‹¸#"¤ÑÀÁE„ˆQÄb¡ðx¤QÃâÁAˆ‘ÇBÂFì‘p¤ a±ðõ>’ƒPEF ±FB)¢„EGqGDH¢Eˆ‹ ¢ˆIá!ñH;¢†Šƒ1"ÅŠØ#áHÂÂ⥇ð‘¦i!ÖH"E”°°é"îˆé@4°Ðq!"A±èi#<$iGÔ°r"Fä± ²{$iC”°@ê"îˆé@4°`r!"A±xj#<$iGÔ°˜r"Fä±°²{$iCXXhõ>’Ã]-Ä A¤ˆa]„‡Ä#íˆe"@Ĉ<h6b„#m €ÂGr˜â­…X#!ˆQÂb®ðx¤QÃâÎAˆ‘ÇBÏFì‘p¤ aaá×CøHÓh!ÖH"EòCCüé÷Æ–û}rÿöJ{¥¿½Òß^éÿt¯¤Í\S9Ì{ÓB„ˆ„z-LPÚ‰GÚ5LXD€ˆyL^lÄ GÚöEzÉa‚Zˆ5‚H%Lvºˆ;"B: L~\DˆHEL„Ú‰GÚ5LŒD€ˆyL’lÄ GÚ&M=„ä0 T ±FB)¢„ UqGDH¢† –ƒ1"É–Ø#áHÂÂ䫇ð‘&k!ÖH"E”01k#<$iGÔ0Qs"Fä1ik!ÖH"E”0‰ë"îˆé@40©s!"A1Ák#<$iGÔ0ás"Fä1ù³{$iCXX ô>’Ã-Ä A¤ˆ ]Ä!ˆ."D$ˆ"m„‡Ä#íˆ _ bD ±G‘6„…Gá#9LñÑB¬‘DŠ(a1ÒEÜÒh`qâ"BD‚(b¡ÒFxH<ÒŽ¨aáâ DŒÈcc#öH8Ò†°°¨é!|$‡)pZˆ5‚H%,xºˆ;"B: ,€\DˆHE,†Ú‰GÚG=„ä0…R ±FB)¢„…SqGDH¢…”‹ ¢ˆEUá!ñH;¢†E–ƒ1"—Ø#áH¬‡¸#"¤ÑÀ‚ÌE„ˆQÄâ¬ðx¤QÃbÍAˆ‘ÇÂÍFì‘p¤ aa!×EÜÒA_ÂÂÎE„ˆQÄ"¯ðx¤QâÏAˆ‘Ç€Ø#áH¢„ 6P^®¨û‡>‰ªñcþî+ýÑ+ýŸì“8r{¤ÿ¿÷HÿýÑ¿¢7ú?Õé1× õ]R¯ËF¬‘DJýû0)é"îˆé@40Iq!"A1ai#<$iGÔ0q"Fä1™±{$iCX˜Üô>’Ã$:-Ä A¤ˆ&>]Ä!ˆ&B."D$ˆ"&Em„‡Ä#íˆ&I"@Ĉ<&L6b„#m ¨ÂGr L¦\DˆHEL¬Ú‰GÚ5L´D€ˆyLºlÄ GÚ&a]Ä!Ôy%LÊ\DˆH¨3ݘ í‘p¤ aa²ÖCøH“¸µk$‘"J˜ÈuwD„t ˜Ø¹ˆ‘ Š˜äµ´#j˜ô9ˆ#òX؈=Ž´!,,zÉaŠƒb„ RD ‹….⎈Dƒ…¯ ¢ˆ…Dá!ñH;¢†……ƒ1"E†Ø#áH¢£‡ð‘¦i!ÖH"E”° é"îˆé@4°@q!"A±Xi#<$iGÔ°xq"Fä±±{$iCXXØô>’Ã9-Ä A¤ˆ=]Ä!jX9ˆ#òX Ùˆ=Ž´!,,ŽzÉa ¥b„ RD §.⎈D )"D‹ª6ÂCâ‘vD ‹,ÉaŠ­b„ RD ‹¯.⎈D‹1"D ³6ÂCâ‘vê7j>’Ãl-Ä A¤ˆp]Ä!ˆt."D$ˆ"wm„‡Ä#í ‹½.â”Ïüÿèƒ8rôÿNýέól·øŸþwÉ?ýsç¢þ¿BÃVöÀQê—o})dæŸzÌ–{TÉïÜ“õ­ ‡dzÏ<:mì~˜ÜS[Óqœ d'Æ{öl_Ïí ØÆß=à5ÆäøahOµœ«lB¾ß¥ F8²WáëÈmµiö¾†;Ÿ1{\'†ÐiS»%OÊ%Ì|  Ü5Gœgüç¿DÔÑóÿ áQIöI¶P «ìW:9ç’MÔ8SÏ^±6êØù?æ¤Èµ²…ûÙ3ê ™ùÝb¨>ÿÙ©æ\BÏýâ‚êÙ‰­¯mÏÃpCÎèìÌ\Ô®V4¸¸™*† [>Y°*\;D9”™A¥e}¬nvIê1ˆž_ÁF]Å‹aŽ)…p_ÇS\\^ Ïç¾;÷64tú‰?ë;öéÓç!dNá %ô\ êÜò(#êB ¦«4î+ωažƒó =Ô fêkN­=s:š<>ÙÝKω@]ä¥öÇ+…€Á6óeÄn7ú؉|Òé[yõTëÒãç΃҉)\ï2uw“TÖ+¯Ö½yÇŠ`L7îÚëMùDfóèi2›ßó°H6hŒñ FÝÑéç?Ü8R6Ôx©>Eà•Þc¥`uéÕ>L[÷;•ÎÚüæîy ¦( WØIÇŠÃݪi] áÁʾâØK…à·m Õ¬{äÔ‹ôã7·sáÁ‡šaç¡ÿÓžoéÏ'gÜÊnm_£:h]!Œ°é½0½¶ù˜Ö§lõp7xד yÃÖà%qdãÕo2>¥¨›¢•Ä7§\.þe“V> tj®šÚ'‡ËøzŸgæ“Ò¾¨lÔ=EMV-„£Suì,júQ@~!)sy¥jx’ %ãj¿8˜Ÿÿ׃º3³É¥nÝ aÃë£j}>äCŸõ–ÝÏ("\Ù v3ÐÝò‘³ù<¬ <¡ýŒy¨[!3ø*€<©Ú1;¯|ðûp|é¶€""³í^m?Ü÷G¸yœgæñ0ñC]—omåþŰª¬:Wª–±ûìót)&©›÷ŠòûYí‹è …~}ŒŽ0ñC5UY·¿î Êʃ~½m‡m²*&/•vìQö·€WWJ¶Oôñ„+As›5WÐ~ÔRÔ­Ÿ13àY`ÜÝxèÌz£<€ýfţꋉlœë= f> 'ÿ,™g6r+?“V65ÌâxÌéÝÀ_Õ#Æ,x¯6QL:ýr´­Ç{Üò„±{ù55Ó¾Ù,ÔÅΠ c -mÍ%Î…”AÚg÷ÝCŸ·5‡Šh³àžð겉ã‘3ôë±Q—¬|äñÌEø>×Íìµtu.€Ù»o1 kî¸^ên)3©A£ž ¶ºË N­ã îíåi¬h•˜ÛóŠâ‚¶8`+Ÿº1UL:}U?VlsÜÕÏ‹™ËO¿Oê®$œqyó#tÆ›|:v>¢9‚®îoÅ仇p¦_ˆ)¬¹Z'Eß|hY»¸ï²cÙ¹KË‚“%„Šæ“®¦pâ«ü÷ƧÞpÒàʔdz7Óñ3ÅëÚŠšD™²Ûrt6Ü|óùî½òWzÜÉŒ^ùPåvÎæç$úûd¡îÔ…Ãy«òÁl›Î‚¼Ô,puè¢4¥¦„$[ Tz1Š 1ó:ÒŒîñáRqBíÇR&~¨Szž6mL>8Âêc’+œœõ-%>P{ò´®ó™y9ŒÏ:ê¦Ö½ó¹ð!(wd¾Bd¥)Î?µ¸”ÐsÍàpô½‰£[ù°£¨a„å6úóñPGÏoÎ Åî9âˆLx¡ SÛ”’½M9Ý|VšCÐÕÄéßùpèç¤!ã¤ô÷)D]ÿŸùµ<þÐ8b{&\|v?¸”t­¼·wp©9\1¢œÀ|@s%¦ZÚ^„º¹c¨o*;,»ö#ߤRPWJ´=f»Í_jÁø;óÿà5tüP§µvßCvã§™Ï>ûì\F:ýÛ*3-5yq|~;dòhÐñã¶²ýWê_9 Ö¾¬i°Z“vE÷¦x¯)#¾QáwFÿ4‡E"¿aÓCù›ýºëÄ“èø¡Žr TiÈÚÇ-*×}wºŒtú5v°pv*žjRNeòtüP7Pk7ün.¸RéR©V”néô‡TÔ©EVò¡æ e0Žê<~-¼±Ì.ÆYm˜Ø»º÷ú¤Œtú2=ªl¸sOÌzîè2:~¨ÛØõì\¸9nƒjõó4h®å¬ï£TNhŸF.PSl¯FóöÅ¡¿O!êt¿Ý¿¢Ö;rz–fû§iZ«iõšr2¿9Ê ¾íT_ùΟo¹Íî1+èø¡nÙþãßlKr¿“4x} ÚÂãd9á0úãºãïÈü‡‹ªUštüP7;}ÕNr1†Ww]ö"<åœ/'[_<ýa½9Œ¿eEMÊ…¹G?==éø™µ²?ûÍ\R°/FSmÁÅT¸lŸü<®µœtú––ï.uà3s°e} uʾ||äOWÚc»:è¹üä1~‹lu Øö媼çï×“éØ¨Ë¹0`iE[6;ò¥òÌG¼Ù·fý°ÍDï`yŸ²Ï˜w}½iñ™¹ÉrtüP×½ë•5A÷²¡Ï»(ŸÁ"÷éÁ“Ûg*HÔ‰wáºæàJOEw'åôâã‡:ÊåHï@6,½ò®m•žŒÞ«J=U™²A{fŒß b6Ë· ZM_/BÔ­¿6ÚzïÌl óR~ÖO»ç¿ª ´Ÿ.ª”Ÿñ'™EÇuÓQwXãwM`Hï.[ ”+ç]÷«Ö(Xòî[5èø¡®qåþ¥—"² í+œùò.žŽêÿcM%£œ¯L@f'äÇjjèÕãèø™·²S›¬Ÿì°ÍÝdzîä¤@Å>jài%¡ë«1Øä¯Þ\šÎz¾ÞYüP7Byââ™Yð8}bö•Òdûüb¨k•dlP¼Ä^‰]Õ½™ r³§nü¾D?Ô}Ÿ»õüûL¥íd¶îº@?§’„FíâxŸ5jJÜå¯|€Ð‚ •ßÉtÔÝO3áõÌ„òGö`E’`5mÄÍ·•$G>ĺû]#˜+3 òùã}òP×ãûÈ5NÖ™à2½Q¼{QX­ 0gTaÉó¯ö2-Y |àÆ¤¯'±$ÐñCÝÞ9Å¿LÏM™aò#X¿ÿ°JâÊ*’õÍæ[«Ÿ1P.º·Îø€ò„_œ†Ñß§u2»‡ö ¸}7qõÜG°U!"<À¦Šø>?;6ÏvR6ÚW}˜ÏG뤨k¯Ulžó0v¿.èžžESÛz‘«UdÕ¦¿0#2Ãt@Ý ¤ß§œE+›žOžÊjí–,N„õßÃ<ûVjêø<Sè™rc÷Ãg>`6zÛzE»7tüP‡E3]ÔëSÎ9 `u9A=ÿ{¡ý¹`ǯr^óÖN¸öøÜM¥ƒŽêò?èdì~šFÝž”­Mê.\5Mò»O&ñ[oO{àÃ\g]e÷-uã§Ëy–ÞN‡¹Ý³ëÊãÁã½ãô©û$$ïqòÍ,.P®D1ë}Àr^ê< ‰LÇCÝûn”Ño:È÷¸ÍmÝ?ÇÞXÜÍ[B:”´*vqÁeªPwã(jÜõJ†¦L'DÕFŒI‡êίÛâ@ÅW1ÈHˆœÞèù¥#¸°úAÐæ}S}`U©Õ‡™cØôúuGšë}"jÒà5–þXHZT-}#!½½æK°ŸhdkÓð™- ïËtRÔ­LÐthÄ<½unØ]õ>qðhê°‘Í*ÕäÐ'Éo£·ådze{ •=ÔÛÃ|à¦4˜÷s…qú¥Xx§q³ã†j"¨ÙÜëØb.h%®¥¬KaÚråMs¿ÒñC]\ e˜•Å&”qe,ÈæòªÉ 9÷GLJšÚ{ï”5eØlš6î\n_:¢îÌy³2R!ý£Úï´è2æÐ³ “šáÃOØ Ð0‡!”Íá7>sͣㇺU±ï”Û§B¬ËIqÔΨ>ò¢Oßf(ØK”YüöZ1—?}îýtüPç©öJ!E#(×"I{4ü¸ÆMïÙÞû{ØTcn>ûÊ_ïÀøm޼ìRºšŽêîP6d­"Ùª»Gƒ|)‹#_ÞÔé“kN¼»Î,´)G2Z'B]}äòïé"Ø~qò}21¶Tñ­Ä7óœ¢àñ<‹ß×KÂ9j ðJ:~¨²ÏiËmã¿VkU> ¼Òa‹÷„™Ã‹s§ü^|['\H¡¯9ËV¶‹NöeÓ—68xlز1¾NM`þõ%ïÅ93p8Ÿ`Ñû d6«ºôõÉB+v郗±hÝ·'?#¡ŸúÜËmfM̼J.4u[ø³o÷ßy‚Žêd¶¯)0»Ž‹—#ar†¥ƒÂæ&ðY¶³ãë.SޤƒÿyqP§¼²räÚWÉ0ëáøþÕìHx¹îúû‘ ›@;uKŽÿqè㸇ê(ž³J_Ÿ<Ô}C rMÍk¶q/" {ðåm_'4AJ’®ðŒa”öð í}|`kWƒAÓ[fÒñCÝÄ]ïìw9$Á¸MTE€Ð¹koд½ª1”¤®0Y—:Ò´ŒÖ‰P·ñ‚ËŠš¬G =çåçŒp'{÷–Ì/ /¸i †âÞ×§û0óœ§ÑñC·×žò€ž³wk«w¨´4Â:Mêc ¾´SÇfènætYÇÜV­ìI[­í—®H ™‘E8ä<³H«¨‚¼ä¬†Í0lö†Î³ò§°;Û÷ïHÇuTQ:–׫®e<݆‡¢?DÆ55…zh– Ðóî} ó¾ÌØœŽõz2Ÿíxx¶æb¹¸g8¨;lÑ»+l„Î9Ö¶Ÿç/ÙuÙ<_Å?gIe÷;u2{ø¦8pì´ÀûÜC{ÑlûÀ³$¿å‚N3®ã>ÛR­ Tìê£ýmÝ¿ înªf¿1ãàJâ»W{=„{Ÿeõ°i„ÎüÍç•´büZml§ô¦ëê¼×~0…XX?m÷ —pu·§^#Èl|ps˜rxõù£¡îȧúpË3}þë ý€Û’¨Ó_ (—€ka\ÆÒ‡é¯Uèú‡º®MÊOúGƒÃÏí5·ïéës9«Tuì©®Û=ÔQ6 ?øÌûœL׿ƒ­ì*-ݵ’£ÀƳØè×¢ûàÿDèŸÙ¿‘ñ¯áÂò<ǧ¨³¤û:êîß F¡G‚Û=ÁZh ÐËr³ú$…Ÿs7ñÔ¸pjgÍÝ„:>¼Üpôª}Ñ0:~¨sþ2ÕèA¿HH¦ì|ÃÀáyÔ>Ó)tîÛ|íM9ðA'\ëò— uúþCÝ€·v׌júðða0¬,Àe[–:ë˜ÏòŒÂ»F|hy-÷籑ôý‡ºrÊs[8ÄHØèÕ5 >.]¸Nx_ ‘I¡*Ì`ìùíÞðõò£½3Ö¥ã‡:»Å%voÚ'1Nxš÷ÍO ‰‰†맘CÃ*±xwÞïtüPÇzq·bÇŽû ;}1v ò>¾ä˜´Ó‚ñ#äƒKB}þ©CÝéø¡N«Æ&ýMp(D^úáx(Îݽ¸k÷^)ÓÿÃWox7ð™ëšEßÖØŸ ]pìv¬\ÃGêè…Òý[ bõjK95¢Ì_ÞyM¢ï?ÔQSôsB`ýçwgè„ÂÅÆ'MBÅîn»'ꀕS?wZæÃìÿÒ:6ê¦íMÛ” ÷¢3ç, þÅmÏ´ä¥@¹ øï³€M7ùFÛßða½l#`*?Ô)«ºóž}F¸t³xRó®».éc)uª«»ÜàÃÕ~”C–7üû¾oµ“]¹ ZSæmÂ÷ws­³ÏúÇp ×øO½…\Ïo¿f%ìü>D²þJˆºQØ­örj²®…Àï#¿÷é©öÿS¸m½Óµ­+'I«´™uê¬Yú „ùæœôt(ħ^¢¡óHöÙ¨bƒß‡Ýú˜3eôú[ŠºÊû”ñUPÜK­B¡š²©ó˜ñ+5†;;…õc3ù°vï,/Ÿ—KéûΦ•½àø÷†`‚ý"f»âõeþþÖÛð à3åÜŽµF0Af¼ÆÊ…fLz=ÌB]|ëøÉÏôà>€2 ƒªï[züÌoÛŠE«[Üýô̾€ò…NL´œ^·£îæó Æ\õÊy ›Â@‰2&¼ÑÀ¬~ûç­¯%‘¥·èõ0užo*z¾fE€‘á—®|«…¹Þ霟}Ì€_ßcmu>”P9“úÞ_g£.¿ãóˆáý/Ú7,Ö®š|r`N-PO1^˜ƒ¶E¿cóaÒ‚|õe´Žƒº]”­×•2^ñá‹á/#aÏÍE“}kaÏÂMƒ™YµZ-çÃøwÐ:ê}ë®{^ý"y2vË’îÛ¢ Néí–¾»kaÐàòöia¿û2–¾RxËúùuד¿Ú·= "ï}å>ƾ`ÓÀu5_Æ×Â.ÙÆRÆDñ…+ºôó êl/׺œ0¾J~u ÛöhR4Èu»<ÿôëšß~æ÷­\Õk‚úõ¤¨FÙÕ’²º>ús¢Á«‡õ£÷øvùÄf.ÌÞ°|kc1ŸÙ7¤?ŸÜáVöÞ‡÷v¤^'íCݾÚ#æõÜâ<Æ£d¶Á>¦Ï¢ ;,· ªîµ“~=ê²RŒšS‡Ý$´ov øÌø¾pîÞæ9Š 8wˆ¼~uõåÔr ~þÃF]áãÏ÷Ý"S]ÙÓmZb ãàÖø3k s^ûì™{Ý+p]t+ûå×(ú9uýФo†GÝ&¿ö^›ÿbK,L^Ûqasϸð©ç~Á:c¨ 9Ã=Œy÷YüÈjÿúy!u[îîžõ5˜(ôtØš ·Î*gG×VÃîÈ)iëWåÊ5ô+8¶ý¯ö^OÏ£¢.YeÝ9ï!d­¿Ú ùq@¹Fü¯þÝÿÏèÐT1Ëf-`õþv„Ö‰P×6š ÜBoųháDf?ª•ï7&.I6”€l{ÊÍ ÔNö9ØîyW½™ø¡nEÈæ¨^»"ˆcoÊ4FùúDiH@¹çû9{Ìàþõò}+j}ßB&~¨›i/øTø#‚È–ûŠà®Ê˜föÏ*pý«‡“œl|2æ”_°,;J=±¢}¤¨«h«»}=’PÉÂðØ#ØøT9° ê÷:¥×¾iGMœpý†«û†ùôsp¹#¸^ôÕÛüzI±’m?ÙªÇHPŒ¾o¾fõ|.ÊØu²ϧ³¿ ãw¤sEŽÏa›…lJs¿c—œ,ª :·ÑoÃ.Œê¯Ó îù0÷;㇂:ú¹z4YŒ]Î…Ä$(­]Q:xaÈn;}.Ó_ù@ÏÓD0m'­ã îdÌM­Õ£cÈÚxþjëñÉ úfÚ§ÞUйÏÓék½­úèÊvBëx¨ûhQÒ5:†˜Ê ž“awïú‰6••Ðé/“ðøºšXÕÒÜOuU[Eû~Q—_½ðæýµ±dJ×Ë ™ß’a“‰šVÂOƒX£m.ëb:nø÷듺-2kŒ%´ßu „®¶ó_í„ÈiÝŠ6G€úñA¡OæûåBT~’9/ºâ«ùåìãˆñ!sSAM xTŒ™^ í7G~:a` Wǿۻv¯/.hþÖ´™9/q´•½TfOœî•„vYKàœó‚ ï+:ýlÁdÐG§¶¾ñz¡[¶ý>Y¨“Ù_'Û5p)E`}Ð0ý  {¬tŽ‹yâë1ë?:n¨³’=ˆI ´¯–Ž®”s¬€ç‘ÉÉ=ð*îÈ |ŸUZÇAÝc y¯üøRͦްˆÎç:ÜgÕD»®æÐjÐ/¬¹Ãç×㡎-Úc±,‘X³uv„{Šàé°ý;û¨€ËÁÅýãpá4÷I =’r¾f|lP÷mu‘{i"ya3ô[¾ÚÜ«t3ÛÊáùRê›Âèã•Îò…KìJGèסŽÚ½ Úöˆô©ºi½¸O*Lú°bìàÄrPý2(`ÿH-1¯x=-l8Oû@IQ¦¼ËháãG$òÊ›;Y:©0buÐÅçËažlÎØÜÅ'/ªùv^tüì[Ù«zí»Ø ‰ÄhÆ-ÓóL…_ʃ.î+¯#”ѯ Èì~Gc¿Î“·i}Éøî¡îÎÞu³ë_$‘Ö}œíj¥©ð tp£µF9ÈŽi›BÆso~z˜VS_ ã[Šº'Ôrƒ›L†ímÞ1, ¢õεLíRÓ}úbe3…µeB®ŠÈÞõ®>6*ˆñMDÝÜÑŽÃ/’ɪ¨ÈS9»Ó`¥ÏñAâ2‡ˆäBú´1kÖ ðcÖùŒêHåíUm¦)d×À9%Oo¦Áø‘Zßö]-ƒ.ÃòßÙ™AaéÞsÆû1ëKÚJˆºŸ—¾hI!A]/^Yù& rj—™—ã‘3CºM0‡a ­ãRûó\Žê¨SQ†»ÙÊ¿æ¹g^:nˆYðfNpdYÌASÖà}D¥íLüP·+(ëÓ.1!£[9s»¸¥7ÃÆUY®¬Óï teÞ}aQêÏuь۱V¶þˆQ‰ÕcD$!˜mbZ’S‹ lÇä•Bù夜îßÍö?ó…ݘ=ßöc|/Qg³a×qþÖe@?VÈ–JAcÀÅwcCðõ¼-GXøBÛºš­õãÿ=Ô9lrú¹Ì[D*¢X>Ûd9ñ²,`)ü¸y°TSÍ*[&‡>;î Š”½W 㿇º{ uœtqZôèä‹ô ¸g2³_ÔÔRè<ç¤,+ ¾?Ko,Ée|gQâµmmñ)ûPÝ¥jh&´¿Ýbr¡£&…Ž«3ÚjƬ»}¡v«ý„}Œÿê‚ ‹fš˜J&ÞïpK3Í„uâ¡»?g–@§¯“EP“ͦi¾p¸8qU¾#ã[ŠºQÏØ•[R ísŸ /§F+5ú–€]ZwÕGfà9Y¯ùÖgß ò;Ø×ßYÔ=¤ÞßÔ!•Üß4 ¤­LvÏ_(ØWŒ;\4¥Þ‰ïþ¿rÇ[ÙA”mðÝTrÆÒcÖ(£,H™u°ÂN­¦w¥œí-`×Ú?ݯ¾@nêÎø£NfÇ)N%úg{]XŸÒ˱/Ö¶‹;ýÀaà¶ŽÙI;ý€oœš1f%ã߆:™ÍéçT²è¶óÇÓý³aÔw=åɱâßy~A/Õ9v~©B¬c|/Q÷äŬïwF¥ê)¿-'¾'Žžºì„2¿×‰/X‚¬l¯öû#/ñP· Xx`QI§ŽåÄdƒ3ëS‡-[ äêÃÓGYBѶ÷Ê>o|aüçÃvÙÌý‡:¯Hª1K#C»RÎ…9±f|ߎÄÐeÆì>ZÀˆZSÿ‹¾ï›Žê~êëxO#iç¶ÜwÝ™gƒ8Ù^/†m7wt»àfÍU/BÌ}ÿði—¢n©ÃÜCõÒÈàå‡fÙ„æ€QØÜ_3çÌ`m»›æk_øn¹‘/yÍÄïD+›rò0Ðýs|í²óâ‚ê"pÜo«Ëdǹð}b‘¹$?Љêrbûiéä¦zÿ;"»do ?Y[ –ŠKî™@Ís%û´hÛK=j˜ø¡îÚB*¤‘-vÞ\Ê…Þ6ì`ÅñEpk¶Æ»ÈI&°z4UYþ|ŸÔíú°¡GÝû4âoò^«ß“\ÆÇ­øÎƒLj~à ÍDg ¾O¥‚ýcmicêί9=õW:Ù÷(º1\3~Õ5Š>*„/&A£Ê›@TÙ€çã\}¡õ%ŭŽêjÍ+G<žNdOqyyð¹(ݨÇÈBè)Y}çÁI¸ý1®»ôš/³ÿLëD¨sÝs×t©Z:YhÕQ8?ly/Rð»þ9^]à0Æh§ÆÛÕ´NŠº;ÏUª÷ÌI'2»ØÁù ÛÔ/¿á'/ù™B´rÛõX?0“‹Ñ:¹“û kí—¥\4_z¶/<Æíj\×µæcQfqzj;¼¯$ítþ¼-ñ}F÷\/=édNë+ù;ùPä=ÒjÔõ|Ùm_áÂô–uÚÕ¾ðíÈ ?W2~먓-v¦“®Õ/êǼɇg¾ÃÊÞ.·£ÔŸ‰¹ðÖùý]C_pž¸}ÕxÚW—ƒºT'MÃt’hò®pî¼Øgp»WM„ou‰_ÉÛ©.XoéçLLþD]éæc ÓÌ҉퇎äõ§ `²£ÚûçGóૃyÒa?.LެÔ5Ÿá “”{œùÌø(¢NñþPK/Ûtrfó‰–Ùp»î”—÷°<Ȧlòsa—Eµ¹;oªÜ(aî?ÔÑ~Ù餎]\‚Ý·OÄ2.ºçœÅ75…‚É]föî ôs%Æ7u÷¨ãž§Ò‰uÊQ¯Žì²Œ.Ý•Ëì›ÀxÙ™/sþþ>åNµ²«Ìœ¸ÎK'ÎÜû\ÛË…°õ¨Hìð+ä—P;_&—¾ì¾YƒÌ—c⇺¶™y³gœI'‹>EaR×ÒÇŠ¼nåüî{döA>@ŸGd⇺ΉÚ×Õ‹à…£ðUÓê8²%ÏbâaS¸3¦|ec Vù#¯3qÿ§\4œK'¦jÅ.Õ&ùûMmÙ:!r@¤:îUìØÑÃÜv­z¾Ø¿ŒñEÝÐÌÐ0KÔÍï÷ä!?¡ö,ñõUqËþîÅÛx|I𮫻*̵cêꮪõ>í™NŽÿXsòu÷b¨V1P+`eCY½ÝÜ–k¸Þsyí«5Õì—=„³ÅLÜP7—ç=Öç`É™P¬',ÙA3xÑÒ/sôA_˜'7JqìúúþÃû£Ï ƒ 7ÿãÀ-Y¿ûª³í3œ²Öù²H®<¿`‡Vvâ‡kŒQ÷ëHõÃ…õÅ êºøØ×™àÔØó¤Ê²øÀ+‡¡ÒG…Œ_0êfù”PÁx±¢URÖ Ãné¢'ë½3;}'áÝqÉK–øüáûËFݯcçêi‰Au䯋—çdBTÄ ÷a·Œam %K©“kLžÄ¿ß¸÷‹z±s:ɯ¥6ŰYUx6¼1~kQcA‹Ûãõq/ùð­—O¿ù±L¼P—õ%HׯC‹òå˜IÅæ{¸P öF½ÚmºdÀׇíîêÿ~NS®0Ä^õSçP'{ mNb?p奄CßÈ>líGé@­îfyéC۱͛¦øÀaµåÛÖݦ¯c¹Ó­ìpkß'Þ˜GFɇ•@¯- ’‡Óa÷Ò²ASoêC—:õ}êý|ÀHÖÈ2÷ÙéÎsÜé„þþJ j¡¯ã»iéÌ~­þïçñ=_³Ž3÷꼩ãò{ÒÉþ´ü*ãHwض¥5 ꄟv/Üdðû\’óÐ;»úFÑ:êÆë<ÑkØ–N¦ØŒn>çQGe …4èôŸ÷1öbØßÓpÅ ¦Î¡î«(½÷ºtâ=Ê—L%y§Ìš·1yîas¢»àÈ掽?³Í…Ö Q÷öcÌô9X?f }^RO"RGwKƒ{YM3Gß®}9y|8PðÂ}ù\Z':Ýy]¦“´à¢›M¿Jà€¬@¥Âô쩊‹'‚_wô¶õ|x>¾ý̵_LüPG?oO'²§ÙKÁüUö¼‰æ©ÐX:óéˆo0î+ßé¬7dÕ³vM­aÖy¼Vö²efpǦ“ì5ú ÍëJ¡·üåÝU*©pZfÄm´­Üú1K ³N@Ý]ª|bç.óËð=T §e“"èSÓÒ¾FÕà÷ï=öiŸ9£áÀø<£îÖ¶iÏÔ°oøZ±öÙÅRX¹*Ìü5®Ÿ7_¾éåªóãËò‚Roê !Óg¢Î);î¨Â×4B¯[JÁSóŽcÅjQç~ôAí¬{AãD­Ž-LC¤‚Zx¤‘…YC‡¯n-…ÃÌCºü×Ã`ø5Ú{Å´éç˜ûutN#Bë#û•AÛŒî%¶ú±)rèM=ÎîØÔ˜öaò%ê®§oÌL#V›Lœ×Í*ƒŒK÷Œ¯ON?ñѲ»žPcwp¥³ÎC•Ùœ ¯#ÒHÿµ³o;ÊÀa“êJñâ˜?•:ñfRõ‰ùkÔ=M„sC~Tn0b|¨=ÿ¸>…¨{ ;ΑF¦½†Y£”C©ûšÁk!êâòÍ}'2ç“<`¬Oì[Õ)Œß3êvÉ~˜“J ŸØ`Ë[½Õ'Zóc˜óƒ0Y¬ún³søÝKQ×3© |Se*¹7:‘WS_¼rWK`ú_}Xœ8pØP›s@=}Odî?§Vöd»\­U‘©dí¤;GÜî–ÃÊžÝFè܈Å]öO:ÇÀó†N¸œëŸÃ,¢ŸnÔÑy9•l•5\å0]ž=Ï[%Ò‡Îí¶‡æn[7%僙ª\¿tUf€ºaxL2O%ÖSÆÜåðŽ{ñÛ¥ 8°šu¯_³–&X>¾ùó8Œ2Hz²Ùg¡Þ'Õ>@*)¡Ç ¯€6¡ŽÖ‡aqûß@ûøz@Ýûïf70÷êdÇ|†¤’Úô}jœ…06­w€&?ÂB¨/úÐ÷DMãÃÜs0¸t¤óyZ'DµúÛü\Dâú-öWÀÕÂà {Ä‚ìv¸ A¾aé%ÛÎõ+ª‹ý™ûu¡vM‡’DÄo“±ÒQç xì·¸ŽÄ0Ï· `¬ìÛYX4k{Я´NŠ:^å÷±pùNw* `^IGqKtçsXô¡þ¾zÉYØýJS:­“sne«Î”SÞ%"ÔnÞëÜ P’ý&†éøœœÈ7ª›9Öt¨ê®RÇì“¡îzÇÌ'&ŠˆïžŸË¿¾ª€ë'Nœ|šOC[ :ÀÉëÎN8~®TZê~»ÏÄuš¼8¶S!÷¨cÅŠ•ÐsmuÞä(¸÷|ÁÖ-°ü°¼roMwÐ2©ö=aöYP7\[÷»’-!inÔÁÂJ(×ëÇ-ˆì|^UÇuuϹcñ€ñ.«é뚇ºß¶_íFHA ·KÍöJPíwð„jßH80ñp`ÆN}ø Éá0sºÐyBˆº¡ÖÅïïx¦ÖÅþW®­„'5=oE9E0ç 8 ‡©»Â?ú|‹PWy÷ÝâñSˆÖôæàž—*a»ñã¡©¿Â!~—*–@„Ø®œ‡þ|Ô½<³ÝY‡*˜¼áÖs…ûa@ß'@Ãsé\ïÓñC‡Ò%¿Ÿ‰Äcó²ð{ûª ÁÒùtìñPhÖj7O˜më:Ö}*vî|ÞNÇu#pµŒ­Éã9m:OWÁŽqº?œµï½ïfYÜ”/h8ÿá_/B]Hãn½y ä õ+„«U°Z¿0îƒÊ]X—Þ>pæD(K¥¾;ý±/Eõ¾Ëæ«·ØØ|Pê>mNÛÛ+„éÇ9pZögX]ðò½R+?×VöëÔ/âÉg9j§¥ üUc’†w¿ÃôGû@Sö Ár}ŠD9ùLüP‡E=ûýˆx’Λ¸#¢£ âŸMÑ _ßP?8݇cyâãNPÅ/^nÉìs¢Žª†OÆ‘Ž¹?ª½‡H`F7—Ì@¸ÍÄy´ Z­â®îT—m“Íô/¨;ݰõVš$–PîÝ ³$pL9t~ë‘[Œ_º,–mÔ:B7ÿ·ofOgö©QWk9üÒ ÍXÒ«LS‚Æ|¬šª³ów˜ÌùLG°¹£Ñ÷³NGÝ<êíùÄþ×תp%puêí´ÙßoÀªèÑ[Žï€.±—uÿÈ/"ÔLxÞãÚ“hrúöûÆN0íöÕk*Ü`öáwÀxÙÁÇ?öᥨSzõvÙ‚EÑ$-µ!ƒY‰S9w~œ¸Î<_׃oþEgL{;ß!êà$SÿÜð>z½${µGYª4BŒ¶Éï^.dÎQó#Î|r„VÞÞõW#™ø¹užŠ$¢€â %px~žIÔ5Ø0ÖËÎf7œ=žzgþ1G-»$Lÿ‚ºAºò›'G’y7Ÿ„¶H€ÌÜŸ0¿ý S_÷0¿+áõk…! ™þu ÏÙ–—ØDkKºžîø"ŒÞëîØº ÔcÓð;{á¸õÑJ«GfÝÈû£ß¢N¶¼éxHdÇÕ'TÃ…æ»7Ê6`@üÁÀ†}0\½¢GRÜM{Ïê:…þ>E¨KL­ð—gô­j0Ú4®ì'à÷ýÐgPm}·Ã§auLGY·£îõñÓåî•Ç xÇWƒ&õ3TÛ ¿_oŒ 3âI8ñiOð¤TfÝ~¦•Mí²r %ç¤o_í­õÞ/ê¹úB>·åý¹;û`h¶ãŽü+Ç@,†ÔÌsLüP—ÿiìÁ„¯w‰šìÀC5<˜õ`ð»Ë| Ï…ïú<ú1Pô_…w<óœuµvw² !¥¿MÛÅ«†Ðîé÷{B£ƒ¨[7ùý0{ãƒÉüêcÀüÎŽê^_¨ZÛLâa¸ÂïjØ™RÖ[Áì,ÈnËeæ9ÅqæwÓôõÂC?¹áÇÛ}·I>U¾¯TÃ'9Õ‰´\¯;è|tìü"Dp¨RdbÅMòêãkïÏ÷ªáE/ñÚ˜{<8¸öØ}—×L_} D/¯.R/gž¡.Â’Z8Ü ý]Œº ޝ†)™^Y׎ ™}ÛŒ`Ì5þ•¸ˆcã®6é ~ŸRÔá—;r]º9gTëTû8…ŸfÐù;θ†¿Žæ÷êä?š ùßuæÑßùgýùÿÝ|Hê?\æš ¾7êß­Ý忯—í¦OÛÿŽïÈÿÝíÿ*¾#ÿ}ÚþW~¶D‹¥6ÂCâ‘vD ‹' bD )±G‘6„……Uá#9L‘ÕB¬‘DŠ(aÑÕEÜÒhü ýlÿU>m”ïHçmÖßÞèoo$÷·7úÚýG3!ÿöCÿÏû!*¿X3q¦¾J«Û忯oí¦ÛÿޝÈÿݼìÿ*¾"ÿÕ|Øþѳöýj¥ˆE]Ä!ˆI."D$ˆ"Lm„‡Ä#íˆP"@Ĉ<S6b„#m ‹«ÂGr˜B«õ/ôªýWy¯QS:gdwÎy¤ú˜ÎõÙß~è߯úÛ ý{öB÷‰þ5}•'왘RŸ›úßôºü÷õ§ýÏô[ûßñùffÿWñù¯æµöÏÞ´’ò¦ GÚI=„ä0S ±FB)¢„TqGDH¢•‹ ¢ˆÅUá!ñH;¢†Å–ƒ1"…—ý/ô¦ýWy­Q"³³;û#¼|þCŸµ¿ýÑÿ÷ýÑßý¢¿=Ò·‰º×yL̨ϥ†ÿ§Ë_OÚÿL¯µÿ•§ˆ"&mm„‡Ä#íˆ&qÎ1_‘ÿj^kÿìI+ý'OÚx¤QÃbÉAˆ‘ÇÂÉFì‘p¤ aa!ÕCøHSTµk$‘"JXduwD„t Xt¹ˆ‘ ŠX€µÿ…ž´ÿ*¯5jjœÂÿ‡>‰ê/þöIû$žÜß>éoŸôïÑ'i1×›ˆyßøßq»ü÷õ¤ýÏô^ÓCøH“¤µk$‘"J˜´uwD„t ˜Ä¹ˆ#ò˜ÐÙˆ=Ž´!,LðzÉa’½b„ RD “¿.⎈D‹"D ƒ6ÂCâ‘vD  bD‹±G‘6„…EDá#9LAÑB¬‘DŠ(aÑEÜÇþ×ó_ûgOÚ¶ò¤!ˆK."D$ˆ"Nm„‡Ä#íˆR"@Ĉ<U6b„#m ‹¬ÂGr˜‚«…X#!ˆQ¬û/ô¤ýWy±ý£×ZgŸÄ–ûû¼íoŸô¯ï“þöH{¤ÿI›¹–r˜÷¥Õ忯7-•Xµk$‘"J˜huwD„t ˜x¹ˆ‘ Š˜„µ´#j˜”9ˆ#ò”-b„#m ¶ÂGr˜ä­…X#BD‚(b2×FxH<ÒŽ¨arç DŒÈc¢g#öH8Ò†°0ñë!|$‡)Zˆ5‚H%, ºˆ;"B: ,\DˆHE,Ú‰GÚ5, D€ˆy¾>b„#m ‹‹"B: ,4\DˆHE,:Ú‰GÚ5,BD€ˆy,HlÄ GÚ(=„ä0ÅJ ±FB)¢„ÅKqGDH¢ÅŒ‹ ¢ˆ…Má!ñH;¢†…Žƒ1"EØ#áHÛÿÄŸ¶ýŸ’Ã$^-Ä A¤ˆ&b]Ä!ˆ&f."D$ˆ"&im„‡Ä#íˆ&m"@Ĉ<&p6b„ RD º.⎈D<"D“½6ÂCâ‘vD “? bD ±G‘6„……Aá#9L‘ÐB¬‘DŠ(aÑÐEÜÒh`á"BD‚(²ðõ´#jX`8ÉaŠb„ RD ‹.⎈D‹"D “6ÂCâ‘vD  bD‹±G‘6„…ELá#9LAÓB¬‘DŠ(aÓEÜÒh`Áã"BD‚(bñÓFxH<ÒŽ°°ê!|$é@4°8r!"Fä±P²{$iCXX8õ>’ÃQ-Ä A¤ˆU]Ä!ˆY."D$ˆ"\m„‡Ä#íˆ`"@Ĉ<c6b„#m ‹³ÂGr˜B­…X#!ˆQ­‹¸#"¤ÑÀBÎE„ˆQÄ¢®ðx¤QÃ"ÏAˆ‘Ç‚ÏFì‘p¤ aa ‡ð‘¦ÐB¬‘è“8rÿó='Ö?œaýÓßÿóž”øŸþwÉ?ýsç’€ùÜ[ÙñwŸØ^%óßh¿ÝêßóàúÞnýäx‰ ™Þ3N»ÿ÷œYzžç*ø¢ãñuÂQ{81äÁêÆã‡áÆi'¡s¸„öQåÈ^‰…¯ó’«üü¨~ÙÚåó¢Ó5Õм¤pÂJ¢O¨)VŠ÷a­ê us¢ŽtÎû§çG Na„÷æÞäÕÔ¥[ZªáŠÜ1ãB[ò¦ëÄÃDFðàt•iÑ×Ãþ,ôüÔÙÈyù·íª¦j¯«Á¤,¶çnŸS„ö5üý~Õ§®_»4ŽÖñP—8Èñù˜¾äl\Ùû©¦Y§Ï×<âLh}(²LKžuЮÓ?žºX×§· ½ÈE‡³ö/¾WƒýKÓˆ!¹gHklÞðh°7vë¹æÚfî3¿ u*7½?äN¬>º¦Ü£²¿|êý­Äƒpì© XûÁ?fýÛø#ÐKŸú fþê"(C0'r?øÅÙ}k?o¢2×çbß=ûà¸åÜíWíàöÐǺ˜ùWg[ÙI”Ç#”«¶@±®4&½6|äCäY1gý¾íú{=üçü+ÔEX%-œµÝ‚ôqžå9ix ¬§Æ‰ù›nKà¼â>°[ø-ÏùÃax©1éGòfN<Ꜽ¶Çܺ¹ˆì¤T)*×Àƒ–~­§ýÉÃcÔ@¥}@ûSØkyvÿ)6Ìü2ÔÅüêRyo¯!(´T·îS3=Z´ìP Y?=K´Àk?6Påð{‘Òöö3¿uKöÖGH&ÙÁÎ/Æ«&àëeîµýas‘Ðs9@¹$ÅÁQ¨Ò5Ÿqã} Q—wËm®Ü(4íIïç©Z¡[fôÖ?Dز/Š™[)º#°qçúªÁ{™ùI¨;)hî ÓmÜqT½î‡å,_x…ˆ+SS28pQmÙó#Ÿì:ýgèø¡nGñš7£ö{@K²¹qÚŒÈÏ “qÐól8Ðé#•ðMyÌ´"f~à9¼^²\»Ýɇcs–öž]g9Ê{<Ú?“Ô´ñQ+€ÌƲ†¾>Y¨»ºéÝse?˜Yöéa˜f dZVg­xs<Ûï›7Ä–NZT5®‘_’šî®fæ_¡nÉø#S³üá‰Ê ”Ïój §JQdýûäÖ½Þ7^w〸Œ2Ò³ƒÁ‰:MaîÌüÔ)v}7q«µ:¼´da ¼xæp÷kûMrWxãùŽûš.<ø¤-ÈÞn%3?uBÊõñ×%˜­yU¾iQ Üv:woÿ³[¿¯Ùøö‹6@¹Ä¯Keæ· nÆ™ú£ý¼¯ÀçÖ‚#®Kj€×ãø»¯õ·IZzôÁ×÷Á¬ñZSæÙ€’ßÏ}}0ó[P'3:@Ô4PñÒXpÕõýþÊ`âÒºðö3óÖl ešÞ½¾öÌüÔùe—½ë¶ã:Ø4{o»¼¨¡©eUwÈ´‡{Ýå0óÝÁSO¿´¤lfþ•G+;²óñ¹seí5°7)ãÇË—!„ñ{Ð"sÇÅ“ 5¦Õ’™¿ƒºÇ– Z“ nBBùæs¦#ïwõÒŒ…"CØ{Ç' ¯ÖÀóÄûQ“˜¸áßËl¾ÜÕ³ºU50¯Õ8¤Õ ”xlªªù¶Õhÿ/Øì°ùUÙ[fîê¶Ê,CŸµ%yÉ¢0ò­`çµCëL€š¾7zþ!Ù+Œb|5ðïõ~žÜ¾õT´½=2ù¡ýM_ŒC`JÙè~bü4ðï©©‰ÓìB`ÛðöYéø¾V¿Ó‰šáýоXÆpJ6¸ÏVlØ_\¥ÎÜg¨;w_hvÖÿ.3¿¦e^{÷åP8Ù øn¬ËG‡^°‚#º õb1~(¨ÓÙ¾2>7äØ(Œ“_5$‚Ô~¡fÂh•´³: `þ™ŽÏy¬7¿žõì“ Q¾ÛOb\ÏY~þänéœ÷&Ÿ­~bäÀ¾7,Ô-xvªÙ¢8 l´Þ‡Îg×@Ý9½1æs#IçÜÄ]Nçy}V[3þXôçb£®¦Û}¿â><+÷/~‡×{ó¦)½“#Ɉ_KšÓ9ðž?yŒÓ=x=ѳdÁ=æþBßDýRÑЗ½` ´Þ¢È¦ú=ß-7r@6n*ÜF„þ õbæ“¡®R™%¸™ý$ µÃŒçÖ@7jlsZ‘&¬²?ÁÊãÛ’µÞVx51óÉPDZkÑ6å„C‚´ßO‡™5 RñõÀtíhrâævÌìØd–Q°ÄÛ.WM©ÙÄÌ·B×áž«öU†Ã|^ÔÌóÎ.å´Š£ í¦ ‡>qCÁÊnd83ŸuA­‘%ó!ötÏúÚsR ¥Ã@ý1ä:56xìu·ª/´¶æû–K]™û˳•m•2°>êAÈÊ'Ö Q¿±ËâÞÅ óR>€xõ›.Üà4îÎçŒ êβ;Z%¸”í“R  \žD~tü¼0§Þ0áM4Œâ.yjUU y‚MhL$¤O{ýOC˜-3,°…Çõyƒ®3ñCÝT™H ${|nþ”_ ]µ-›Z‘`Ç1á AÎkñ‰é[l;} èø¡n؆Òï3ïÇça¸ÔAT Î NƒúŽM"Þùe 7Œ _AŸ•‰×láʇ”·1¦´Žƒ:Úo(4©èS ®xƒó’ˆíôÌmÆ™¸&ÒñCã$j¢g,”5HûúÝ­†KMVŸN&sõï¯ë¸íÐp¸s>/?Ô­=á§\]ö <ŠE_/"Ô ¡ÆS~ˆï4ʘ¼¾WkîVQ$„©/Ì©DÁzi¬ú~SHѸ8EÉÿ0ãcµ‡ŽêÔ¯ (dÏøeÒÌ`ÿe,Sf®ø©?â.B} Gdž‚DXZ~Jnsž|гpK&‰¡ì†åL!h±¸O¿§@-^o“³™?Ôm‰êµËqì#Ø}¡šåw^3æ,ƒ-3 ½1aürN1þ”´¥œöëK/=õŽlƒ/)­öSƒ§õÎ";oX7c¦ ¬_SÙØ6åÐsÁÿKÔµKUû ?‚/kYXJàîJ¯?N¡ýt;狟ÊýÕ/„ö÷d£îû¦ò‹'´“˜u‘š®‹É"a#F´LO3‚áWÏy;Åøëî¤ã‡º©›>JI‚Û¦ÇÏF¬“åÒÑØ3›l‰>4CcªÐñ=Îu+7öÐ~›<ÔiŸÝ[[;-æe؆.€A7ÉÜÛ³çyãLk;¹ŒSp#p“z\4­¢N}òÑkÍ7“aÖŽž=U%°ñÌþoƒƒ³‰ã+NÆ’.Fðe¨E|ÎâSÌÜKZ'BÅ›-Û¸¬ëŠËÑÁXzoѤÏÙdô¢Á—&ýŽ;íÿG뤨»ôÚnóþ€è-3Ž©ýÖ©uëurȧK*C.Ü0fæRŸbü¯èïEÎ×w^ÞÃLû`×U„æ´UA}²ï%õ[9D¼¿màͧ&°ûX7àlñºít?Ô…yF ÞçDÀu¼6¶Upuœz±Z×\²ÕÃÝà]O.¬¼æ=«©+Y/ÐþºlÔåe*ée¿%Ð3ÁCceB~îÑ4Ê%V–ÇîJ¸@ûñxÚ™ƒ:ÚwKé—…ëû_«‚Ò5&º9¹„™+ «J­>ÌÃÚŸ˜ñF]»*…·O”ËK­K¬³Ø;hjÙYÑzÇëRMµK½xŒ£6?Ô-%8˜¯sO¸e^ª®Õ¸zä‘Nd=å¢ïæýy S¬Dû#‹PwuÏVT”®À×Ië« ÷Ù‹/—Zóˆ÷hÊhÙ "’BUxŒO㌺n½¢÷¨AÙ:ʽ $ÃÉc•ù¤ú¥öÇ+û`ÙÅU~3yØJjéÐñóÃú·ƒºãE0'æÀýƒª@×EåD¯³ùDüšoÀ[a'—7FÙYóà¢c^Çç«éø¡ÎÒÖðxÚ TÙy½¯„Gàü®¹<Ÿ9ûü¾ã}8÷ô@—ëyŒÿ 6êÔÔßò"'§ÂPÙB®.­™‘®R@>ÞèheÀ¬—¿T:îÔÑsöSáÍö€6õ¨Jðˆ´ìUÍ- 2ûš«°«\5!}’ÈliTÿ`Ôm%‚T¨¢¶7ø• 0Ðp7ª€ŒNM@5„)9Å«9ùYÿ’œ§´Nˆº§Á…Z ›R¨¿*ßcU 3Ç'-–ïRH,ª\¢³„†`'Ûˆp†/ ŽÏ¼@û*‹PGû¥Bp RþJXzvÊŽ2ÝB¬¿à}SÆŽ…çœöëf⇺üš`û=¨KzÞ—P•°úÃØ}ž7 ‰Úê.78YFÌzÝ~õž¾½œ³–Žß…V6탚 oþ<îÚ£Þž`¯<þ©ðw¶´÷5æ£3¸$ÔçŸ:D_×,Ô%~IŠÖ§µ‹5º¹®«ŸS¦¢.«è i©à\[“ö~s(F¤¦­¹TLüæ‰ Ž¶Â'Ê>œ¡BþǺd«±2u´ÿU*”Ÿô?ùrzLÙïó´­˜¨º'`›‚Q¢0sŸ3Ú4mܹÜQtüP÷ü„BªÍˆTÐt1 XÜ»ž%ã‹GŠI‘{)vΆp«Þ°dgE˜b%ŸEÇÏ¿•]½Y¾ï£‹9*³Z˲‘}Áëðy3'¨BpôϹïÎ`·µSh u9¯7nï—ÊÌK.‡1Q?ìâMŤë}÷H…‚Àáœ1WÊafÒ«+ê|1±­X´ºå‘™Yxø¡3“?Ÿ¥È⇺s~ßFŸëš íºCÚ+‡£3WŠÅ$ìeb ôþN§n?ÔuѸºtù/ î3ÐZ~[9sŸ‰I§¯Ú¥Þn¶ ³] QÁl«Ôh*?Ô};¼ÈD8«z÷»iÞcx <|Á¹º;j]Ÿ,ðŒvé¼.é¸áßï‘y[_‡î÷Ê!RSg¯çê’ßyzaxVâÂøÃ/“館똵ɥüUØMS|d_B:}x§oñ“*˜¹0¾¿+è'­ìVÃ£Ž¾ø=H¯œ<~ ¿ ’G 5ËCJHâ Ša÷Láô™ìk9ÎPdMm4³éç ¨Ë ÔMmÄïýS»ñ ù»eðÎc㬪2ÒÝxö™[&Œ‹3¤¥\Yárš¾ŽÙ¨“ÙÎöN… ¢ž+ÜËà°Ý»jÝJ ½2þ}ýÓû°,™Žƒºê}}ÄëcÔ‘§MËÀ!ÊY®X½”0}7Ðý‡3´ªV,í=™ŽêôÓ¸Ûc¤Â×U”CeÐûe¥$ÖX´sm€!d¤œ 3W^‹Žê ©eã°Tü‘XvEµ äNÙÎ=QÊøþÂν‹1.0¶+51~ 7Ô5PvAÊ© _°<ø¤|qVøðñz)qålKþ¾ÁöSv†Ã]aÊ\Êáu!7Ô½y:¤ÅïSûKö»Ÿ•ÂàÛ¶š?rJIýþůwßÇü,3lt-¸Pùx¿ÀVö¯Å—?býiãnÓYW ?žéV¶—Êvd²Ðý“ðSÖ” ¹Óéø¡ÎbÕq¤½ÍX çoß<ê:¢Œ¬¯%‘¥·Œàà#ñž;>n@ûÐͦㇺî6*8X·hÿÒRèÅ_uïÂò2ÞêÿÂr¶1°T×)æ¹ÁbÞJ‡Šôçã îJÐÜfÍ©U22mJrU1³*#k‹Î.ŽöÆ|‰«Q±Ó÷Ó×u#¿ªbgŽõuÁí¥ÐöòÁˈ ÍÙ¤*ÙLÖð.íŠuc|Gèû@ˆºEîW&Þ— ü96Z¥ðlÆÆe+2ËH§ïmPã¼k6líUtüP·wØà o‹TÙ®*—B ?}˜M{‰ÔÙzPá€1ØemÕtƒ_X>wÛ@Çu§o¹÷ µO…_Åú)Ir¥ óYNn×ÜižÙÍ8nue®ð¤»å¡Ž[[èø ZÙ.ø)†žM…gAš çž”Àà­Wϯ('Ý—ÇêdeI·1ήŒßÄv:~¨›Nm‡_ĺ|zÍ”-%ppe¯Ü;ÊI|¾Cý³"CÈíòèÒ"cW(¹_õA(ÜJÇuœû5sÆÞOCÊ^;²æoœº0ʯœ¬S>ÆŠÔ6„²^W ÷ß7ÒñC]Ÿk÷“SaÌj[£ÖÀP•´•“N®qšNž/»2ûIô÷ÉC]`ÆÎsJR¡Ï`ÊqßçŽ/“¥åÄ-‹2@4„m²CnÌ>—„¨[*[Pb=ÿ´K|°^ïzêb'_ALøä·›Í<÷ÊsnLžÕ¡ã‡ºw~…}y› Úç¯în[[Ë:zí;«‚tú;Q®¹ÇoºAÐè»)ï=6ÓñC]»Ù†}F]Ó`–å Q¸(‘µ·‚0ûN°ÇÆf«Õ 7 ¯×mtü.býéH/>= ÞP¶ÜÃJ@gFßS$—ut`°š)ÜÊrèÜsƒ07×»ƒÖ±P'[vŒHë’ÜÍ/~ˆ!>Ìô¾BbY<àuQ¥)lôqpj²[gÿGÇu+ÖSÎi0-¦á`t³4šÞÞ·Uõï½¶ž` Dٜ̻n°fÀòP¿ÙtÜ9¨ o™2ŒLNƒÚY”3–JÖ‡]94¬’0û°0e¾ê'ñ7Ư„þ^x¨ÓÒv×cZ†}\ó:\ §¦Z¬ZVù{_A¾õ”r¡^/ÚnÊ»‹uéø¡nD˜VÅ.l<‡ž·¨ü½?Ê’ç_7ìåÊøG¬£ãöï¯Ò{ý»ñbÖïpøVY›úÅ’øýèwíY¡GãG¯¥ã†º¯¯^ˆæã÷!ÚÍZúÖP *£#ë¦ÝjùíŸBûd»BdùqRâ¥ÏÅ;ÿ/í›±ôgmÓ˜rwüo?OY|/aÝÙd£Ê°48sö%®C·õ xš¾-Yó<ÏчÝöû0•ÿy¿°P7Ãêf—¤ipÄf”‚tšl' z{‚×/({Õb}°ÞM)ݘý»Mt|Q·ãÙë£ÞaH¥ýÁb˜jUgà`Õ%þò[± €ösúy<ý}sPwYC_sjm*D4„ ~S б³ïLÚÓø/8ù0ë\k€n_Þ¤¢C¿OêdÛT"¬ÇËìl߯Ø Öà÷Ç䃗ׯ–;{¿Þš•IÇKˆº3Å13„©à¼´d¯ÌO÷KsœÓÂôÙ† 1ò<î¾ê ãÌÜŸ¨ë§|Êö4ö5g}ùÚÅðôÇéËc[`eË|ë•vá³åg@ï`yŸ²ÏLœQ§Qu£ww*¬©íì¯P »‚kuûµ€îE‰‡3ë¹3\rA­lÚ?¿Ê渴ؽ¿:äs3ô®“AéMèb )¦À¸;ý“®…|Ê#lÿ öuñøß#™eùŸ Ô«Öù)Ï®]£‹u'Ÿ<9¦˜ÂR¼Ób‡RÇÄœ1Âs‘TáøÀ ϲõƒ‡¸…"Òîb à?l«¼F#ßÞ{~¾W1¹Úõm £Žs*ëyS£èÚåíÂR¸ñCœÔµuÎ=ø]üÐkô,¾“—ºU±ûœ ·MĦƒö±ã AÜÔ™mN”L»@õÚ½íS¿Ò5 k~äMXµbÒo™±cÝ)5cÚ¤l‹&¦»úÎL6Ÿ+÷Êt¢Çº§)\ßtéeHÞþ¢ˆl¯·ï_"%WûñÕÑ47 îÍ*“ÙqÐ ŽÍ“)ÔÕg룟”éä:'a-úÔ79Ži[âÍõ[eÇ݈8ý¤‡ÎÖJ!µóÉN§'÷ÃW¨ŒEܺb8YwN®«IDÝÙp2jÜ2öùÙ·£ÅÊYcw'××é4$ñ«eë¹÷WÈTºæËæþQ´1}¥Ÿ!b;~ñ%¢V'tû¦ýybºÒ¯*L£ÖC.,·¹ˆœû'Ä;÷çë<ã΍ÿÌtª9÷ùC\¥˜§ÏÊ=GösS'ï9žÆþ=½xO=ø3ßPS)£½ùU!5(ÉqFN ¢,Ãô7GGsë8¾ìø!®ÍIÿ¯z6rŸO™²³Ê’ð§˜Gš8¾t$ûùÛ]":XëL£yÆ#äÚ~šH;÷2ß³¹¶ ~3`k¥`úîl§¶=ZDÓé™ý'ûLÅ®‡".Áw‡)Pp„"§À7Ñïmµ}T·‘ü¹Ö§åƒ ®Ÿa4- ?0<„Ýo!®[£vÓ¦Û³óÝkh¢dŸuéÓ÷¨SàܵñŽ zrëî¼áµ”´ÄU Øõz âŠã˜8‡élD{¹š‰f´Ly9;íÞ§ÇYzou~—¹JbÏŲ¿O¸²GêWkó©xîa¢&ß\~Â=ê9‡Ù9 ¦5)1-{mSÒëÉ”;Ù×Eƒ¸umèËW;DE“’ [½½JW ã2"îQxuÏCÑ]ƒ‰7Ôs}•Úß1"®iU_kîÒdÊV½àoq\¥¦§w::é¹×¥Ù>vJ:É,CÍŽâ’ž¶u&>J¢‹©=dU~½J[&9Û}î‘{ǵ}ROIÛguºÞ•ݧñ؃yëî O…%ѦoÍ®’2Ä|ôu{ŸúÇ®•¸,>ß;˜v±ùãØñC\€kâ U¿þ¶ñªâ«ô`Õ£Ù…ö»T§³ÒH½ò®ÌõUDÓºÌÁ¶ ìø!îRÁ3|ó8H½ûD͹r—›‡rãM»¦D, аûŽ’=îºp€&ŸnX´çî§>ÁW£o¦ŸºÅ­Ó±ûÚ üû}ƒg{¡¥°½ß­ˆÅã*7<­¸KÕ~˜Vd®HE}6.œÍå36Nƒ¸¯\_µô¬rö”l¼ICÞíq—®ïéŸÚ¬G ±ýå•Äî;³ÏLj¸©ô>½%"«õ˃§+'RQ¹Ðñ‡ñþøcî•õÙnß „"¾d*J4÷ùžÊŽâ⥓Z–´ÛGß„^êúúÕU:šžXwÂå;ôA—x4%Œ>¼˜qò«ÂhRZ%…m2f²ã…¸ §R]wx-ïa_õî*•äÏží»ùÍÞ²`v¸”Z×ܺ裒\Z“<‡“ n~ßNC§ï%Y»:…3+šèëyùƒîpë׳ˆ=ßKÝ·O[ûÃ"îü âê÷søˆçãÚ/ö­…|’œø8¦ËbNsN6Ì¢ì~w®K*æëÁnÿqúþÉ=gGî&¶¯‰N ÷í¹å÷zÌ7k0‹j¬ßik«rm³çpûï{Ýû¾bºßii¢ºÝ&¾Ñ§ЖZy{] §[Ì4³…’ëƒÌ¾ž6ÄÉô/xÑvé+}½¨\W ¯ÓsùÏÜy‰0Ê[Èk™M¥_Ô~ÞKÇ>?„Ѹ“w*Þó§ ·Í.“‰üŒö—dÄ®‡Òžc£BÇ*¹ýbö|qÛô^¦ï Õ‘›|W"?×躤éë¡ÄÎ_Cȵ=Ó0†<3/ëX—=(B\ü®"¡cö6r}=™f¢ûÆÊ»4/ .¼Í/Œ ¡º{õOˆ!nÿ‘?Äû²—dÒ¦Ÿ“Õn#çšhÁòW"^ߦ–ìƒ[†žTÓ–bh¤¯ßÅñf6N¸¡C~}ìÆ*è üáp„‰Æ2?àÚmîü~µõÎì~Í¢$n½™?Ä­õ«?%ïý&ªÍ|Ù€ºUd©sd÷mÐ~Ê™†êPJ1mk;‡b(m^XéÞBîüâ˜Ýͤ)©þÀ*Ó/í6ÑèWj<\t›Ò;Šú¥_•cÚ7ˆ%n¿€?æùµMè^9^ECêýÍI%ÙʱùݦãÌ×swþ;–ÛbÏ÷xì+Õ¹r¿¦àÕ:îú ¹Ž—7¾M¿Zw˜Ð3œ[׉¥¦]´wþ qÉ}ÐOŽ¥nû^Ÿ®h3ѼC‡ñŸÜ¢ô¨î[††“×ʉ×cé¤Q“³7¹Ïâ^Ÿ¹û>4š.¦ír ó£ò]Zû‹Sn¹ÏùRׂ3‚œ¸X³d§<ó îüâTÌå4-ÖëX_53¥|ýÊjûñ¹¶÷*†SX´3ºs¿Xz¾!#àUSöq*×ÁÕpZA‡­Ì ™x¹Cª…ÞrŸg¥ù7<²<Š¡AL{íÎìë¢AœëØm«e$Ýðt9u7Sýss: è~‹„k:t_¦ ¥äbçîe }h·òíÙ÷§qì9—…TeÅ„Ë+Ƙ©ûî„&ç«Ý"×…!´ˆöó¨I µY¶f{Ënüç:ì5‡.ö(Jf&ùûMU«Ýü´>×{K €%1«Œ?³ï3Ä‘}_PðŒaÓhùóo-3"ÌT{fÛXÕÙ›ä^¯Ò-<"Í'†ê¹¹óŸˆcçƒôg×ËÌ´þDµ± 7ݤò¥¿Ȉ ¦Ôôíb[ÇX÷ú ;~ˆ›Öglݺ³¦nliöãa3Íâ۹Ɯ›´ÄÏG´§j1«,uºÅqç¹Ïâf쮽õèîo ÞGÇx\1›iלú{oRð‹—FT ¥m²Óù[ã¨c½võä>ˆÛt¹]¼Ÿ`‘ajFïâß‹ÌÂÔhx“6cÞHFÏ›k~_ç¾ÎŠ?ı縗ÞÙݾÏ{3mÕ,Tõ‘•¾|r{ø`g8¹–ñ‡Æqë'ìó3".² “pV®ôbžy*µ¯©º|î´•<'ÞHî;‹¶?öÍ·4Žrs ªvgãlˆcûsGØ—JÞÝ®W)Xc¥«ÕüåØ,ê¹c9Mlw.¿ý%¢³¥]‡‡ÆÆö­ÛuL*Ux¶ôɱVwŸ^êà:X÷§¼ÄCÜñ¶_8cç­3°u*•VTy]\ÒÂJ2G ~þFJñ¡_ì¶V‹¥×¯[5¸Êâ&vZ1^|@eè?0CÕ!6•Î~•æïÈçêa})Ô¸0†ZœÕ`ÆÎ]†¸Ú®ƒˆ ?Ì`6DSé§3Õ¾»v9ß=Îd=Å?> m ½û’9 É]†8®Ï²aÿáå[^L¥çÔ:³-Ÿ[G¡öøjú‡1ø‡ï³Œo¹ë‹÷ô\+ç½[¹óÁ©tB¢.¯œŸOGÚ2'ç‚i°áÔŠwY±ê›mD\¢þà“s­ÔSo^´÷H£ãµk>¨6"Ÿ6d—n=LçÄß(GOóãŸõ}Á]†¸¦Û†}ض\í®›¥ÑúÐêãz·Ê§Ng_oo^?„Ò|&¬ ‰¥_,¿Þ¯q…»>L["z_÷EÞo§w ¹qdaÿ4®ÿ{·BÌ®WS UëüøÃQ/îú0­{}<Þ°…¿ìë^3Ó(¶Ã±µ—³òˆ½+”¦ ïxa·#†ST-8ËõgG\ðŽ ¾yvUoùðýÊ4jö´ÞŽ_ò¨ØßP}êñPÚ_aò˜CbÉuüw"wýâf>Û´×c¬Æ 6 |¦I£GåW]šG®ãáõ¸¾âqÔ½}¥;3r¸ëU×ýD9«Âs·asYÂ/iÄ\Õ±qdõsmȇQÖ{Í·â¸õ|îºÄ1»ÿ医 c](Ó(wB¥É•xyÜþv½U9R âÜ›?ıçSö:o9úitâ3Ko{ß¹ V͸±ù±Üù{îü<â6ú}ãº*{ ¡IéíËù¤ÓÇ÷ßO½A§ÛTy2û%æU¥­æÈc©ùï“; yÉ}þ”ˆ¾¿q?/a¯!5€ù`¤Ó”0fGõ†»¾RŽ,­`yëØ?=?âÒüŒÔ.ÁÐÅc]ξ•é”ë?¹Âˆ¥7È}nàsÌâv Ù0èõ0îúLÄõÓª[C}‚¡ú0Õˆ£{Ó©ÖªnñÇß _ÍݵÛQ!óõx7æ!ûvÉÃ^§!A܇ö{žtÞgH»8§|ôtº#TÒå5-ÑjL±ó¢2uôê×~w}Ø÷9Î}†ýž²ï>O§Mû1­zƒz/ÝPÕÙ=ˆ\ÇÝGÄpù‰ëÏŽ¸^Û[w4ÔH4¬óÝËVõ®‘Ϙø÷=Krie—Æ_5ßD#Ÿ ZCë¿ÿꣻ>q·Rmü¥‹ ƒÒÆŒè}ÔÒ†\jV·wA0U?™¸ÔÔ>–[Wà®_Aܕ䆃ze'ZÔß*fÆ5:Ó¾ë Î?åÒDÅóßþBuGi”CjÄý©ÏºÇÁ‘nýèé¿uÚoÀ›}ûæU×Èßu0&—\ÛJÃÈu|ãdœ{ÞÌŽâR<~·çnÙo`óÿ5Ö®ïNÿ\rŸgïžËo³íF— [WÕ‡»þq}]oû êF=ÒÞe\£&Éî·­K·/ÏàKúH©÷¡Ûš7SãèÛ ¿Ñmçqõqí"—®ï6JkxüÇíPóktzdáÏsî^§Þåõ“Ï‹¥4ÆuP#–^>^±Q9®þ!®®Twà•Vk0b®àÈ ú•C:|ú/J÷}bø§¼+—xŠaó¼qççS¿Õb‹òzdP]{ùQ½×¹}”°Oߟ™nó²GìûÓˆ8f7§ÉІ‚Ý>¿NÉ  ³Ǫ\çökBˆÙ]ìMÌêíÌì8ØW¸u}BBôƒÞràõŠ J÷þ¾ÅžN×éùŸßlþ9˜v¯´&ïr4c¶ãpã—„ys<9õ€½n$ƒræÆL“U¾þiôÀšv¥ï6)é·ü—i¯Oqㇸ+E=#·T;hTÃ˼œA§‰šë sÉekFS¼?Gd•Ö Ž¡ð§§ªg,â®óCÜÈ&µ¾®@ Ì*óÆûô2énTJ©™ãσˆLÃâºEÅOæ½5+²õA‚8fw- ì aaMæJËLÊoºlìÛr¨@Ã\`D—¯w mT¤üSžW ®ç‡Á¡—·4¸–ÛÚgR^×V;^—Cîë«\ǘ¼•¾~ q‚ðSK/4,Nx—ÙuD&mú²ý®ÜQ9t!Ágʰ€`ÚpãÐÎMw£)g}Ôø¸Ïâ6×i¨úè !-Z3»tv&ÍíéFÛJh8¶Îþ`jvhöða½”thù¾ºÏâØë›“ Í]2éܨzu&ÌæöÙƒ?Ãç׃z$ãó` :Õ!ɰ`ô“ÝÉ™4©ñÀ{í¬ÙÜ9¤`14óÒÎÅJZ*Ïú¡Ä“?ÄÕ?>£FÉà${+“ö§TÙy4›\Ëyo‚(+rÃŒÜgÑî÷9;~Éî뀓 ‹JŽ÷­ý,“_èp^¶6›žý²PÄÍ[¢É·N¾)ã e÷R*»—’£ì^Je÷Rúϸ—óGʽ'˜×ùÙ~ =8€D%5XÀIKrÐxHbbP™KhBö³û)‰þå¾ÜeýKþ}ÿ’ÿÛ}¹ÝýKþ©>oîÞ·ÿ?÷SRƒÄä7æÌëÂÄúƒŒà’“4`/$*?P€ÀGâ’€,à‰$&9èÀÞÜý%?¿wYŸ’ß§äß݃›÷õqs÷ºÕ‚ ¼yxì #8A€b" XÁ …Å ðQh$  x¢èˆ@:°EH *0sI2Ђ ¼Q üA Fp‚K °‚Š—(@ࣘI@ ðDat` T`þ‹>·îþmLŸ[yYŸÛ¿Ýç–™÷0˜ºÆÌeä-Ø\Å¥ÉÿÑy3'rÏ…ðÖ-›y”­ýoŸ•Í…þþ\ˆÉ rn,™çÍü?1¨ÀÌ%#!È@ 6ðFrò%Á $+)hÀ ^H\~ {¥ÿº¿vYÿ‘ßäßÝ[ÛÝäŸêÑöy/[ØÇÃã˜)$BlàÂâJ0‚(4RЀ¼Ptü@zpEHj°€' ’ä ;ðP Ä 3W¬„ -ØÀÅË”`'P̤ +x¡°ùôà> Ô`ù‹>¶L6EYÿÚ¿Ý¿öóùóÖQ´(ë?R6*›=ÊÖˆþSæEÌg]Áó¼øø; ¨ÁÂ<$)ÈAvà!i‰Af. AZ°7š?(Aà!ÁùƒŒe=HþŸ=Ht`’ºT`þ¬É?Õ§íó^¶zpŸ‡Çj°€' Œä ;ðPpÄ 3W|„ -ØÀÅÈ”`'Pœ¤ +x¡Pùôà> —Ô`O¼±E ؇¢&˜¹'hÁÞ(xþ #8A€( Xÿ¢­ƒëcëʲ>¶»íçó%fŽ¡,›/•Í—<ÊæKF²ùÒÊ|IȽߌÜãà鸞+ó»¤ü@zpIKj°€'˜ä ;ðÐÄ #8'˜Á $<)hÀ ^H~~ =8€d(5XÀ‰QrÐxH”bP™KšBlà$êJ0‚HªRЀ¼`ý@zp Wj°€'’¯ä ;ðŒÅ 3—˜… -ØÀ‰Ú”`'¸¥ +x!‰ûôà>’ºÔ`á¼ðìÙöy_[#8AÀÃã XÁ Æ ðQp$  x¢øˆ@:°ÅH *0s…I2Ђ ¼Q¨üA Fp‚…K °‚Š˜(@ࣨI@ ðDt` žT`抟d Û_ôµur}mÅ *ëkû·ûÚ~>_ªl¾T6_ò(›/=ÊæKÿ)ó%?îýdæ—d ó³¤üA Fp‚IK °‚˜(@à#¡I@ f.¹ @j°pÉN2Ђ ¼‘üüA Fp‚ÉP °‚£(@à#QJ@ ðDÒt`’¨T`æªd x#ÁúƒŒà®4`/$_?P€ÀG2–€,à‰Ä,9èÀ<$j1¨ÀÌ%m!È@ 6ðF÷%Á $u)hÀ žHð"là„ïJ0‚(RЀ¼P ü@zpÅAj°€' …ä ;ðP8Ä ó¿ô¸5sE2Ђ ¼Q`üA Fp‚G °‚Š(@à£I@ ðDat` •T`抖d x£ˆùƒŒàŠš4`/8?P€ÀGÁ“€,à‰â'9èÀþým™ÂÈô·•€º¬¿íßîoûù|‰©ñê²ùRÙ|É£l¾dô(›/ý§Ì—ÄÜû…yM™ß+9èÀÎü[$)1¨ÀÌ%,!È@ 6ðFó%Á $4)hÀžHnB‚¬à‰d'9èÀ<$?1¨ÀÌ%B!È@ 6ðFbô%Á $J)hÀ ^Hš~ =8€$*5XÀ UrÐxH°bP™K¶BlàäëJ0‚HÆRЀ¼˜ý@zp‰Zj°€'’¶ä ;ðÄÅ 3—Ð… -ØÀ Þä ;ððÅ 3—ü… -ØÀÅÀ”`'P¤ +x¡Pøôà> ‡Ô`ኈd`OžÈAvà¡ÀˆAf®ØAZ°7Š?(ÁN IAVðBaòèÁ|* ¨Áž(Z"ƒìÀCƒ Ì\A‚ ´`o8P‚œ @Á“‚¬à…âç Ѓø(†PƒÕ¡Û¾Hâú™²÷@œëvì_$ ˜¶ﲨ÷ÊÔåo6Zȇzú†îŸIlŸƒHR“édÁõ÷FÜÍè¥ ['Ôûⷧ״ЩöDþ M¬­Žù›„†wùØòý›Hz=’¹³;wß+Ä^ÆÜ±(Ùð­ë†9Z YÅc!wßr¦‹œç•(:;¼FùŽÑÜø!NVÕHûF$ 9¿9:YhÕýøA:X(¶—gï)7‚hû‰g‡õˆ&vŽxüdî¾ˆÛøS÷£ÆIÉ×mþûYèZÔã*+Zèø¶AãªLšuÓzî;ïî»Ãõ­Eܰûç6Ÿ I6äÇö0e¡ÖË{½ÿõ\Ÿú+ïH•`bû°DRJɯ¦¶¿q}k—ˆf-ð©œ7?Ùp>ig¡dùŠä>ó³Èu{ŸÞAt Zº°ÊÑÕ4tÐÕ½.±÷Çã!.Öcÿ’+’ aç•Þšc¡Ð€5;ÖvÈ¢úm׆…þ.¡üðÌw›ƒWqýŒ¸û–!.³éÆ)ümɯÊý",40­ÔQíQ&õ¹¦/·k&%ü‘pkñTоYòÇï¸ñCÜêYÞý[%&Úä÷X‹qxÞ‘÷!)“î–Î/JšNºÞ º V‘òÍÑU÷¢¸û^1¿ÏtrwÐÏâí‰V˜ŸIïÏõÿ.oÿ4ò¢ýäOWQlûÛ÷;¸û]áßï{»îH¿ãÉùåó_’-tlG£÷Ra&…í2ñ³ÎO£á®/«ÿܧqW#™Œ'L‹MJ»€ñL»4ôCu[×¶a°d:±÷W_M­£š]M0°q6Ä-ω©8>#ÙÒ£IG|nÞM˜¹Ï¥ î>º3H¸«÷ÄöýVSpЫùsµìóò8R"rµ³+´Ð²…7wÖŽÌ ª÷~orbÒLÚ51»Åö¥«þ|Ÿ$üû’‹æÝJ6lh¾·ï+ ) Âã†dPÍÿÃÞ{@E±nk»˜1cÆÜf̘1Î6cÆŒ"ÚdDT‚B̘1·#ME¿&7Amr[ÅŒÅ|ßêªæ_ksÇ?Îý÷¾wß}Üc<㌵ïêîšUsÎ﫪ùî=°ð¹¾„|Võ_¾;Ç‹âk×ï±ü—p½AÇûj_g_o5øý¢v:™ š’¾ºæ½ª9æÒìý]Nyyýí:•@§·ÿøúø]¼¯x:ÕOÌH¿›FkÆÎ rPKÖóú</>Oâ)õï“NõÚ÷Úäè•F{¢¡þ¦çðŸ¥ÔrÕWã©nÂ\9üý»êÝjµÄñNÓ]Ýxœ8úï·Ý>wlMØqdÔé#Ë„9¥R|jù8A'¾8õЋ×Yù—]ùæ#Ÿ^·çH4ªÿzÀþ Ç¥´ŠýÈC;'‰t?}—!5]g]fD×µ—S^*æçå_g­çµ=éë‘Nu–ojñ`_*Õ:×ÃfÎx3Zû2Ånc‡M"¾=~g5^'‚n~l3·ŸSp˜¹ Î>˜Nß÷[°8•v„U6×)YD›4É4cÏ yº-9qGwt¾ÎO=ú«Û•têµ»oÍá©t9èNËOŦô~êç߯l|Ý_aè~z“Þöí5Æ×tò<ÓÒàþçZ|'îJnZå,ø¸ò×t rÏ?pý+­»å”ev:}œ\§ 3>…ªµ²Ný°nujß!¶rù&ê»qT_ræ:B7îmös‡kìþÕÙ ^§Ó‡©od+ýRˆ›¾{\׌ÌWqN©€î¸nÛÓK{^cñAýV¯–AC޶m°Î4…*Ÿpƒ´ÍÉÝ¡"{¾—ö}y°È‚ÏWjèT!‹*¢ ®2O®\ëgP÷A>§ê´K¡%šÁÍËHcÿÔÊúi¯ñ: Rq©ªsáW«Œ÷Ê 3E:›‹’ioãוý%ô{TCý®›Ýˆ÷Ïò#t¼ïÅUv\b”k8>ƒ/¤Îz"¹êºÑØŠµt'ÁŸ‘tç“9cÚ+,-̾ZÁè^ŒÛsq2-ÖÐeœ`7Šrþ|ßþw tGM[ïÙ´ô ‹‹æÒfÐLù¯ø¶Ét÷Ë0ûÀ1K)8úZÃÆµó¸øøA§)—Œs÷ÌôÌ û ï<÷?VR¸ìK¢žÎÁo`= ¾÷|ü k2$ýùùÝlþ¬g7Ì lãj¦™×”¤S—°ˆŠúpN£.ósW@W2$ðåõ–lû…–S»_Î ]·=†º)I}²ÉÅ;1 ©šý¡~?§9“gå×w1+„< ]N|õeé7.³ŠÉúµûDePÎÊŠ±#§+)èBÑæ½ûPïï7uE8ÓžÖ‰'ƒKÅK›ÚMÌšp™íÏÌý8$5ƒ^8ºgÛQ)øÏ- /‘xÛÞöké%w˜£„¹€ÐÍ>{+ª²ð™pu¬a5ßò¸éÐIôz&7Ðvp>¯£mÃV”ßí%̵‚n‚a£~í].±+öמz“A­]§|¹—D»¼œ…”éØa{Ìu¤9móuQÆFºÕ%ÖªÁͨó¿3Hós$ þ”‹¨7þðÐ:’)F~ý”Çë¤Ðuèþðˆò"ã}“2©ñRÉ÷UË’h·×ºv4o1™3É0G³µ‚?‰ÐŸ@·ØÒfð‡™ùqEãΙ´Ò³ýÛüžI´j«¼Í4S3jåá4fÝugÁKÈŸÐeõÜ·jËÄ‹¬Ž{³ñÎ3é¡ÏŠ)ç>$V/Ûw±œÚu ÍæÚáÃBü Stšw%¿ÆEV<ïBlÞ¸LZt·_œç­Dºñ=iÜZÝ%Äÿ{'íü=>~!¥â/6—¬xpͨ¼èQw™hmš`FáMf­t~µà›ÌëDÐi|_`¦.=e•IqçL$Óé’ÕýAŸ»™Ñîi©_V’Æ®ÐJˆt³jON¾ærñýG&=(û9¤qËD¢=îÛv_LWsbû„º8hçbóñƒÎ£Í—gæ_`å³=ïÄzgRƒG–$论àÈ÷m2èræ^ ¼Yﻞ´I,ˤmM &ç¿)MÜâÙd°­¥à{ÍëÐñ¾µçËLtxðf’@Ê¿;ö˜nªÍcä8z¼‘m¾?¡‹æô²FòyÖ'Çi¿û­LŠ9l׫c‚ÖÇ›´þ€›kZ*öìÏMâ=Ï464 ™TÖ’3‰¯:Ï´~±‚ß?è4ã—gÏ÷þ:º"=“LûÙôjO·{ß–8Ý3×Ö/­?è4v@nçÙ³YK'f’Ëå^]ž:Ç ¾ÜÒúÎ ¸0SÌÿ> tSe©jKγúm-£ŸeÒg‹.<5ŒüÞ$ÚãH–ùi®lâ]Í/Þ<˜xž]ôů<“^¼w?ñëu- Mªÿ¢\BÅkvî}úaí­µuì±IBþ„.^ò«Þ±ÞçÙFM̤äDƒµ5®ÆQœM‹¡5–HèÞº6˯D›S÷Çã:Äìêt-jp+ªóL<øãÓãÕ³¨®w“‰â¨üǹš?·.%nš~öó¿Í1WC—þÌ(¢î—s¬Å¢úiõ²èƒ^§º ãH¶³m„ÿ3ªqøý»Aý–ЦÌOùÕr…ø…¡Ž¥ÌÈÊ(:Çnrcp›gÑ‘ñ»Ÿ¥Î.fu+1¥ —Ÿ?451ÕΣäãÝ´›–ïOÄžc>&ÅIéí²hÆ©×õ÷¤ÅÒÐ ŸØ’] ÿâ9žÓªîÊáÂõÝI_éø€slŠÆ@,‹& =»xïÙØ*?F¾ŸšNç<½wµ"ô›Ð-|êTƒ½çXÉž´.ž†YT¯»tgC,_Ë­´ç ¾ó3Hïð¤´×…õt-õƒoe»œc¼ŸK•—ÄMî83V˜ß;_X÷Ì¥Ïõ×Ç…OäûitûXæYL?ÇÆ§Þòì3!‹\lLV:uŽ|¹ÐÌúK?µ¸°ˆø8ñ:tÕõ6TôxŽÉ.Wó¹0+‹ Ëî›/ùSUÿjÎ3òž·ÓüouL ?úûéÃ>f‘‡dóÛ¸òTØâ›dM×,P—ÒÇ=eèhù8è„—Šn‡MßVr–™Iöí8d›EáwŒ¦ï‹Ñú ÐÇ…Ê%­ç™ÑÈ}GõæRCgè4>Ëžu=tWÏ9‹¶X(\3æTšQµK Ú©2æk¯_>~áÚù¡gYº†}<³èdÅý†:ƾ“Khؼ݇5¡¡ƒ í„ü ÙÒ¨â¡sϲ7n4~µ'‹úT7dõ3EU͹§zÍüÛï“B—Ý­D?¹ÃYÖØ«‹WíYäpàžüº‚êX¬úâ|f á¶%|fÓÚ7õ˶ŽÖyÐÕÁ9‹ÈXç+[$_É"ã FtZ« ãAö#F¶^B Œ¦%;Ï¡0§E[ïéõ:jwœžÇÊØøaßk”†gÑÜä‹GæŒRPÓfYå}¯›ÑfÍàý©B®?èZ/\‚VHÆøþ(‹œ3å=ÏÖRPãÉœó¤Ɇµ¸bnHJݧšWøã©Q*vøêum”—Œ¹¾ÉØ—‘E}_Ý9ÅèÇ/ê°ifä}ýò‚eæÝØÚM¡FVþBÿ ÿ{d츻qeñ£,j__QÑÊÑêÛ÷̪ü€š1«SÇ]è?¡ã}€eìvݻ˿½Î¢êYYÁawï’ÿ… íjš ë–úLð‘ããÝSŵÒûÓeLckð-‹Œžt5ÈŸx—Úõ4ëúfÒ2Ÿ`äÙ¬^“¿Íi—Bçê½ðtöKŒôÔʦm;[N ½Cû£ 5² ‰šÄM9Û‹£Bü ã}£eŒÿÏfSŸv_N^p‡>¶ÛÿnÎKKš•raÛPÞ‚‘0þ/Ÿ·Ôêh@q›lZ´óio¯CÑteÆÉ¢w&Öd;=¦]æí¼~>~Ðk80Y‰ã2¨Hú q—lZ»`þÆéooSZVW×ñml}WmÝåã‰~bqäõX[ûövÁ‘²>Ùtèɘ{]'Þ&­_=çúx¨Iö°vØ£‚Âúº5e};»ÈØ;ñÖC²©NÇwßÖÈn‘&Ýê[ ójÇ1a>+?èBk¶®Öb¯Œ©û}_wyd6a1ÚÔºæ­*·Ì‡ë†>=3Eë ËǺå³Ïg¾•×I¡Ûõ¨ù‚^2öqùv‡©ÙôdüÓYï"éýíGÚ?Y&øÄL`s~4SXÿAW³ƒ¾¸Ò$›šž¬r‰¤¤“v¯§ ]JfÇÍ:]OÇþ7üý•;“&†Éïc—M];¥ßúA;º›ÄwÞf.Ì¡ŸÀN<÷ÝttŽÐwB7¶Ï‰á]¡›{hZI«¥Ù4ä}£ò [#„þÏœ &W;'IœÀ_>nQ¥bƒ†CêM±Í¦;‘§×ü[—ÑÐEç;œKÍx¿ca½€¿ïxº.]žõ.6ÇǯhÊ™ át¸O³Ÿ¶J(;mq=ãïŠ~é1¡ÞAWä5¢4}ŒM=qÕÝÚ%›¬Ž+v«NÏç,™ÿÙÜBð£ëE5wtiK&ã ä%¶Ë¸ôr)?èšYóØáz‚‰4FEÙÔöþ¤Jƒ•—4{¡?š¨í øøA—] àqöiÆñÚ?"ǫ̀xqQ{ßxÿ19ÏîÛyg2¯“A÷èñz{ÓÝÇÙ—›c—¹}Ïî] ·Ä»Ö%1¶´VSÐzÑo®áæ}Ðí›÷èëĺÇYZÞÛ®ÕÈ¡é7ï\mÔü|•?¿Ò’-î×öÔǹ¼N ??ý;+kŽC™/Žû.yp–6wzý{Ÿ%Üà “{°–§‡>á¿§N4ú¸Ìè’æí1û—.a-rhû»F#Êd´ð~që•ó-‰®¥Êyôä®°/ÅǺúš?V­¸zÝsÈæC«øÙ5ÎT×íºNøêÝr eëþœ~g•?è~e¹¾è>ÀŸñû‚9¤ÙÆÊ9I'ãErg{ áüIG¯†& #\н·J3Ÿ¦<Êiþ—C'Mþé8uþùyú¤ñ¾H#èëÌ4·‡„뺔¹ 2+&eü÷Ï¡“ÆN ^î/|?Kò»;%Sf?Fø‚twž\™Ü¦è[ØÙ-|3’ÛÞl„:-ðDkm)ìoS_ᦰ^‡Ž÷>ÂÂ~Ñù17‡J.Õ>þ ê ½_ì\aeE9Iœ¡À²ê]óò!B×^éæP‘}˜åìnàÜÊ"‡vÌÜÚïèøý´ó€Ÿqa¥UÕ~Ô^qôÍer!~wJÅŸ#jªÏÌ9̸)ósH|ôÖ:c÷=Ú}XZ™¸Ü·×EZj>~ÐY5òÞ» ìóy´M?pc½ýâvc„…o•? _ÇÓʧ·z–†ñ~sbèTÜ2Gÿ»°’»ó˜CÓ–V¶ ™ìSµŸp¥ÿ´‘CŽš þ¿¼NÑéR·ƒ¬ÿÀjlÒš­¹µ‰j)\k7üiUuŸ”÷%šÏǺŒ½m=†=<Àf+ÖZžÉ¡Z›¹¿<çLÉ-}¶ZQéÄîê\cKÒØÔ]ÈǺÊS"ºæãçÐέæ«~YþÂVjÊ5’Ö‚/¯S@gß±wåJßýìP‘E³cQ9´bèÄ¡“ζeZŸÞ‡ÐZðká}ÿÔÐÝÎ)¾|UåÇJæLÌ¡fõ7MŒXjÅøu³%ñ>÷–Ôbpß ¼oœÎ]ôázk\zµöcbWø|®ÏÀrgvÀu㈅‡,éÍ;Ç}ÅNË?0^'‚®Ý‘Úóâö2Ë;³{O”CSº=~n=s‹æn—µ³öÙ–ÑMç˜E|ü û<®ZaŒïÆŽù÷‹™C1OŽ™ÞÀ‡©{s†;Öäs¶Q¿\$Ðñë¬ÝlÛõU'?åЀ²ž­Lôeý}“ØP—‰-½ÛYSÝÝ §¯â}¥Ð½í¶'}ÄÕ¬cî¶k~åPùÐÕ'›?ÛÃ9Ü:f½µíÿ°km/áþ,¾È Sflï°z‹¬v¦ã$Ý\2 mZ3ÿÇ~6OsÀ–Wù« užtŽcßíé¤ãË<›¯{Ö¡i.Y”ÌP>ì0KH˜µc±5ÕûÙ“~½¼F“^çã kÖ­Óþmlž&À¹tiιÂj{üÙò¹ëö9Þt¨ò 7mwÿ‡C#1?†ºÒË(ZÔy+ÓØ4uÎ¥vãuœ^çŸ`ZßY­/Ô_?OÝé%sE!>ì—dzà^¹Ô2óÈ–ùµÏ°^Zew>gO;Nv±.µÓú®óñƒ®~Ãøàcã½Ù¨~W'NK¾‹66Ûó[ÆTËÊšœn«}¾@ØçžÌǺ¥^eSŽeIÙŒ k¬$ré·ýÛܹçXüá “Ìl¨‡ÆøÏNXGããÇ´þ’litG´Œ¹d¾¯}à‰ç™æ¶ê kaÒžzÍÖʳÿ}2î{fY–¾qg«} V¿™KÆs2âT//°¹[BeV‚/Žë­FÙ‡¬y?Wt“Ä[»_te7Î]ì9-—2úMS»w½Ä´uŒ÷_r|mæòñƒîÁýÙ““Ö±»_¹Æ#—ÚvŽ,$Ée&ì‹’Íðºûoq¤„Q½—' |0ZßgV¯ñqó/ réÍÜ £”gÿ<€5í¼ÇÝv¤í͹<ÞïTÝCÎ~´ž›r·©ñó\ªL¾>üÄ‹+lÂP¿~CoX“›ÆÀv•à 4•tºæN¦ìpæé.–¹4å–í‡{C¯±â[¤›êÚЧagõªmw"Ó=Ý®ï â§D¡õo¶bOÊz=4·Ë¥²Í}¿¾ØqÕâ–"Ág| U®~е|dRéØ~ ûXzøÕÊ\*Ò?·~á vÓ“»ãoMÛF\wJì,äëÎ|ü¸ï) Ûqðû ¶òFÿ¦ι›tÛ£A ã}ö¬©˜Û©sêhSÒNè ž%r÷ÌCs£4—:¯,™5o¨œiëߟ;ÓG®½mØU£SC7ÛÖgú@çI´wäEŸ Ï\:òÑ}öš 9ëÖùÇ.Ù+Zû”Þˆ2g¡Ïj«ÑéÄ”Š?øsQ‹èLuÉ(Ÿm¹”züg÷(û ¦íwyßaa½óFãK.‚®mμ–[Í­‰ëªZìÉ¥V•¥= +ƒXkSÎIÏB¨K.Â:°1¿­¿#½u=øèÜÁ\JS¿xÏ;˜±¢ÑYPþkãŠSËÖÒž7a²/‰">~Ðý¾Ïv­¡ß4Óßðx.]YñfóÕz!ÌùW÷æÕâÏ·µÚ}>~нË7ݱq=~—è+Ë¥ËyÛ¯EaZÿÚ2‡ÞŸ>t¡`¯µAË/òñƒŽ[5V~s£™á^#M.çRƒ$ÉÃö¡LX‘f›Jîò·ÏS@×°xaé¸ROZ¶yÔ§‡7r©}ÐÓuA¡ÌçÛ‘áæ-­èeä‘Û®ý›ºº¹\Ù³óö siœþŽ6GLÂØ¶Ž'gµ¿f-ø ®£Ëb[»ô‘ÝøøÅ–г7Îûñ¨ÉVá~^.}šY«èã0&Ü_ ‹¶C«_Oš´µ|„F'‚î²é]—ÛÛé›îÇÏY.õ*^éãδ>ƒÝþlú~½Ð÷LÒèÄй kÙ¼¶ÍNª¯ ?¾.1—&eö™ëÁæxÌy“ùÞžî¶ØQסr=il#›ÏÔè$ÐeμWZ{ü2ÅÑY–šKç.7 ü1>’i}aù}äõ¾Ã,N Ý€Œ]?{ùQÆ“Ý3U¸n?E·­_/Ši}oçŠ ~ë¬XG{ËÆ :‘ÛœîCB÷SÇJýëFÙ¹tjÏ´ú3”Ql!gWÙÎŽ87¶‚ïk)agÐIüõ^çÖÎŽ£Òe³õ¾žy¨›,κ»ßbgí¸߆r®ºÛ¿ß:âýGGó×t¹Óïæ?K}2?è>§_é}¦ôñÿ7—ú¯ê¨ÿ9šiý°[jŒO7’EÌOiÚb>~ÐUk=b½ÉŽt½=ç0™K'·Í¨Û×ïÓîk sD¦s£ûqu§_^7’tƒ¾ºp·Ï)rzðñòž—¹ôøÓŽ£ÕûÞe-/´Þu)ÄŠv–åæÌú½‘^?ìøn~ïñ|ü »rÇ7ÜêåiBSÐrÀkÔÍ÷‚z¤ÞeÂþm¬9lN¸±›Ö·›tiÁ=¼î —QåÀÙ[²Þ ŸÐ\ØŒñ×9òçØ×ÓÏ~t£‚ÇÉm*LøøA7'gu÷KõÏÒË´Ñ]7½Ë%Ï«6‰Ck*„û†VĹN.\ä^µþÐÄ/¾T<¥×Ü´±ª³tÓÃñ@Ç÷¹õfÀä‰sUÇEzÒÐbpow­_5?èÆz u~x蹞ЉûK× lúê´‚­žºñÆ–·–ä°mZ|7 OÖƒ­\걺pŽåÀ¦½ÏǯWÜhx¨rýÝ3‹ùøA÷æ“Ï-㳪>ïHã/“ï»Æ0í~OÑDÿsKÜ(Ñ1ùªî0þódÐIO×np`ÌEZTï€ïˆ;1LëûÍûäºQ·xt7ü½¬ÕÛ%ú/.RÝYe=pÍöþ°­S-–µO½ý²ÚG å YÜ蓉;-Yt.$­3ÕÐ Ò<¨q‰&žŠÚ¾q žÐzÎÄX¦ÝÇýÞùlßçs6 Ï) qK(':|ü}µËeº3|ɵÑ8¿ò|ogßßËìƒ=ñÍ-‰Ûýè¿Ixވ׉ [Ô|ýìOŠËäÔU%™û<—~L˜wt\N,Ë?¨xÖn”i.çðMtÉ«c¶üÑ\>nЙþ~n¸li환™ŸŒë¼þµXyÃîq¬ùÑ’¾§­éôæÒ±n;7QmOæßwÑ|>nÐ5¦þ§{…¸ÝÄG…¹ta–IÿSîqLd¡/ÚË–ìkq+ŠMT³!ïÇŸ'Rèdjˉ /]¡æö73s),f®´86)´ýÜ 6vä:.v<ج]ßðqƒŽ»[|Ðä*Åwèâ…<=ÎâÚ² Cã™ûÖZ_jt°'—å?b{Oñ j\ÚnmÎÇ/A[/¯Rï­‹¯Ç o>ñ|<íh<î[ç^ôâf­¯9?è¦-”ïq ¾&øœæRãÇWö_þÏ?Vº®,“v}íN3ú%*Fìåu:‰¥bþ9«ë4¸âÖDÉuäM§³˜Ö”Û}?ün#¹^r©{Œ?è¶—W„õ»A5« \ûýw—ïÇ¥„ª<ý=hŠG| +q®ím…ë:3×¥½†>»AOœ£«§£o±meg×§"yÊ »óÐ’ëtù\WæJ'öê¬jÙŸ?¯%ÐqÙ!ýêMú9P¿KÓ­ø¼mGz;I¬ºîÎs¶öû7V=7¡‰tÍkî[Hu÷HJ¾mÈ¥×–O×oÜ’È´û<ïß×ÍßØÖše´ñ™©ËÇA]gŸÜLNüs{¹W÷ý›ê)‰Lxž‚Š÷.ØTu¿]?èbï \½NN͸Ž)—N¿úîä§—$ìgZPµæ]¦=3õ \{Îi‘?žjènÊ–ï8œ.§Ë[Úo0Î¥ +SœŽš&1¾¿µ¤&³§ë ò¤³yõŽ1åŸNR©xVWÛ¾AÔª©á­èá¹týxÎ’®ç“ªŽg3g ëù·<&‚®§f8ˆVŠÌ÷Ì¥©WÏþ.©ªÏ=ÒÅZô Ÿ'¡ÝzV.áã݇CÙkG§ÑvuäîXŸ.¿Ö(d”’i÷gG¿?ÙWêQõ\&~Ði÷hLü¾`.-ëZºd§R{Qè6WÝ÷Óĺº;;Z½]L/Œ¹­s¨_÷ýySò”ì g;kfEã×$øÑrsÕ}#Mü “í?~q8˜’nEî©]žC7÷Ømß-™Ý÷Í(V×·"ãV'ÔóÚDׯÕr|Æ?w¯€®Gì¶Ë S‚ébîþJ½™ÙÇ)™ ûÔ„âýJá&šÙ3ð°ÊÜ’ts²Ž¹WÓz£[S7äæÐÊ}ÅN£’ÙžIc]_Z¿Oº‰’¦X—L·âã§ÄùéñíÁñŽ!TÔÛéÏ!÷øñ.•¿“™ÉÐÒzïœ-ÈdAƒ…uÄ›ªöû4ñƒ®¾¥lÅ2qñþ—9ôkHã–5'¦0Í‚²ñµ2ßDu¹íÍTþ÷‰¡›É•µÅ!t5rÍ®¯Q9´>ýl{¯)¬hòª·úZäà—Úl&‡Üiüy-nòÈU•׆PuƒIGÒ‚rh¦ëÍCõ¤0½£+/ß° ð­Ï/ZZy}P÷ C±/l¾/„ú.Y}â ~_ÿ»öûZ¥2>oXÒÛÎÓg÷Ûãù·ü"ƒîÎõµîž ¡EC]ŒÎæ,Q?DjÊ%|_ó½ÔŠj„û4ù^SJó›‡÷äÏ3tÅ#~¯Ï ¡-wvt~,‡ìÄí ÂS™z˸šíÆÙÐÁ’™/$?=éÂÕºçÞÖà‹ºð厵î„ÏæP£¥µÆýh˜Æ´>ñÌŽ¦–{R/Í ^§“Œ~õËcQ§”â\º }shW“~;L—§±dц&— ìhÔ·e7'Ö—’&Í·çu"è¾n]x`Tn ¹«øŠ4‡ŠŸWÚ¯ILc‚ï6}?XYT÷³'\áöý!~Ð)ÞÔmZøqÐ<Ø’Cþñ¡)—;ßc%Ü6qŽ-}Tvl0ê‹é-æœày:Uh|ŠýÓZÞîå‹9dø4if¿M÷ßOÛuh3E.îÑ/©·ð tÝ=}>¿ ¡É{×rÐt7¸ÝÀþé÷Xµ[Î ´¦@îqÃöîd­1Îò'tƵå}½ ê[ãþœjòдbi§ûUy‚{º»Ã÷´ÔxûvCþ|Q@ÅÙwW†ÏÍäyò>ãŸÓ±¤.+^üÜÙB;;œ{0™Ïgjè’×\^+ÿBzié%_†æPN7†^á÷Y½‚§åSzX ϸӹ£œÑ+ÿy:)¥â#˾cj… Ï;çÐÂO£ó}~ßî[R÷øÒMUï³hâÝ«ß힥Ö¥ ÓwtukC‹ï¿›˜0åÓÞ¶Þ[ºY¨Bþ„.˜Û†mJÊqkuÛÕÍ!µJÇgÊ‘LëÌ??êAgR¹øï).®ßΕú¡ô¤ºÊÀü[6q»ŽGJ°F6;¯|²"ÍíõCžÂs'Bÿ]×vö ¥µËî;.,˦ä¾_š=j£b|ÿgM£¦vu6דôº¾k—bÂ÷K2èn|2ÜLJ•ó£L¾æfÓ•’ÝÑuÆ«˜vŸ¼¤ÕÒ)–xR"w9äu èøçC‰n/›òÚô½ê bÂýê?£‰úS?â÷Yøú®†îH=}Cé‘âé¤ýaÙôéhš]ÿý*¦}.ãȘµc7§nªzŽV¿ÔRqÍ&”Öh`6í?Ô¯_¸Š¬^¯â“¥/á÷¿t‹¸Çžß‡’¡åû³©âHô´y*–8àÀèq÷$tØ“ËXîô¦á]Ãc½LùøAÇ¿FÛœ¸R6uïôzµõWÓÖ­~Õ­6×8èNëVO´ôâÿ^UéßKùÜiu6QüµÙ¡­Ò™öýª·AŸÎÖéN»m>ö¾H¡Ã¢§p{t­*üÀ–f“¦œÎ¾½ã^üZB‹¾,t®=×%M ' ôØl:¦lyÐØ1ô”ë\j°¤êy~Þ‡˜ïËÕÐ ˆpšµ7œþ\é58›ÒrA±¾éLûÜåÛÑKœ‹zn&®zD_ã÷3tÒJÅ{;°ˆÂé6VyÍzdSÞ„úë~^NgsÆ·•6ô•йìäÖ;GzÏ£ó:tÝ;p2BðeϦÆ-Ž­”šÎR£ççž=n)¼ä)øóë1t×tÿ1‡"h·½×(›üXhÀ¬éì÷³„ë­éÒxîI)u‘Úg\°~€nà¾1 —:ç¶Br­f6íIZðXÝ.ƒ•†sèÚÐ1›YÕO¸Hÿ¶“B7£`ÞÃ.'"ÈyÛ“½Ã¾f‘ñ³×•;§d0®«jdK¹½ûDv”ÒŒBœq_É ;{ç›kÙíÊ7Kùjù.‹"Ö®¾!ƒiïÛîž6¿àIOá~øl>~Ðe6í}~ÿƒ!ÿdÑ,ï7-‡]È`|?mK¹3¸Æe3¯#WòûCjèžæ—šủ ËÜmÂÂ,âÞª³{Áø}1r Ÿ¼:rß&!Ž3øøÝ+ïáqmhÚä×DU˜½äŒOeFUžxâÒ#Ïó”»àÛ.ì»@·péúŠß׆³QÏ"§“«ÏÈ;f2·ùvaŠV´Qóº;}|ætã³\Øw®ù¸:æ±g#(z‘Ï—ùQY´¬,üÅÚ‰™,Ëb«Ù˜–t1zÁ¸ S6Ñî‰2~HÝúߦ¾¼"h· ·"Ê¢w[âû8d²;«oî<Ë’¼Ûp7ø7 û‚¼N ]“ù•ƒ¢E¿ ‹ºéÙ,ý¹7“iŸ“þrèd3w{OúÑfŠ·S­ |ü »~õè£å#„÷¾²ˆÛÅëšÉ´¾æùèδ“ ï5ñûC è† çÞìŠ «º+¬^oɢщÝÇŸÉÏdÚç°Œ>µlün¸”¸· C÷ áãÝ{ÓÝ·rÂéZЖ°ë³è`™âÞèß™ì^çæfc3-©®ï”H¿ÉRJNz[½[­î|üî#Ÿ}åYN kÔÙÎvY4ÆiĪS]³w§å-KÚŠÿZì4)½·/ñ «lÎÇ:÷»¶QŽkÃéÑ€QŸCL³(ºñç¢Ê)YÌ„{-õˆ%ñÛñRáy=>~ÐUlõûø‹ÂéîùÄ-þÓ²èËË’³ïVe1\<åÓí-)¸éÇëZx ωðûùèø}±pª=]ópTÕÛ83Þñ`›WÞd@7KrR-Ý<êÓf>nø{U½‹¾ £§wlbûgщQj»ˆ,özËQuVK:1šû‹ÍÚ}u>nÐqW÷®¸0á}„,ú½Æ.uiaVU=©ñvã¯ÐÙ›iyûù3ôÖñ:tÅO#ʬ¯„Ñ‘ŽµÊ¢µ+|Õ«~g1­ýâ[Þ+ãjϯ|Ü ;¡L«Õo_}Õãîøe‘^pÿÊ.Ù,yÁѹÕ--IsÛ¼µGÕ󌚸=(swe›z‡ÑÉÁ w>«–Eõ¾ºœ’]µî›ÒßáË5Oúvò¶yÿø¸A·Üådá÷0ª_Ó¤2“~´/½½:›iŸo×,cŠ=…ç­kóqƒ®cóCçCw4éøþ±o3)ìã¹¶µý³™Å®‡c®¥YÓ\ybþZôãõ¢ÎÚ·²'¿ºþ…3ͺà{rg¹Þ³L²¬V}wû¸lÆ?Ÿ`KÏäß÷>ò$ýcÒe]¦ð÷3¥ÐYznM;F=»ËY”IšÛáﳫöOv:·žÕ“j¯sþ~t§;Ø×‡Ñ<î5‡¬Lº²©uÎé.9ÌdM;×°¢å´I³&¥í¯Üý¸‹¿¿¨€.PU'5OFeöó§Hɤ«Š®åOLsÿ¾Á âžÆ”Ö÷&þþ× þ~t5BoØžzF³ÆŒÆ!̤¦á2=ür˜)÷xt[GâÏkoº¶8cv#Û.üý>U©˜¿õ™»ÝžI£:Ö¿¥ÌaüsÒ+Hóz\œ—ðœ?E÷Ûœ9âpâïgd­Ñùy÷wkøØh½J0™zMJ¸­:Û{,¿ºÊ6Þ%W…¿™I'úEÏœË,æßù1óàrê¨)Dž¤yM`ŸWÝÊݧʛž §TÛFSúʤƽ‡]¯i›Ë®u nÜÇž®Èν\8“:r· [ð÷¿¥Ð}Šw —ªÂ‰î/“¾6˜áax8— ï÷4LîVOâvùïÍåï»Ê ãïχ“÷ž{& õßû.&—Õ›6Jæ˜bCYSÂoD^iãÐü@ÚKá~-t‡5=™.8~È{U&…ŸîÔ.äu.ûî’î÷ÞÀ†¶µ-ÿj±EJ×oä-[?k&ëúg¦ÒŸ™J:f*ý™©ôï1SI;ãÍ^8/¸cÇý÷D‚r`€d%þ@t‘¸ÄÀÈAÙ1[鯳(ÿ:»Ûðf+é!é)ˆåÀIPü è"!Š+ƒ2 B‚4~@)$K#à€è#yš_ •ÀÉÔÈ@ÐCb5R Ê­øÐEÒW e@„$l ü€RHÈFÀ 5ÐG‚6¾@*!¶=< ‡äm ¤ÿ0[é7‹R;[I "A90@â— º(bà ä  ˆPLP Â8 ú(&À(@%0D±2÷_\λë?ÅëäÿÔNô?tv÷?Óë¤ã?©Gúkôþ·ú¢ÿû¾HÛýé‡þôCÿû!.·8 qçŽ §7¾@*!“=< ‡$e ¤ ˆ°Lß?̘ÔÎãææK: úHj&À(@%0ı2ôðŒD‚r`€(þ@t‘ ÅÀÈA!9š? ¥p@ ô‘8M€/P€J`ˆDjd è!©)ˆåÀ}øÐEÒW e@„$l ü€RHÈFÀ 5ÐGÏc"Ay‹ÿý\I}$rà  ¢ß±2ôèD‚r`€Ä/þ@tQÄÀÈA¡(˜?  „pÿà…Ëùpý§ø–üŸú¼üÃýÏô-ÑÎäú—?ó·ÿ}z¢?{EÿßöFú¢ÿïû".W¸ 1å~;÷ÿ7~@)$)#à€è#i™_ åÀILüRHhFÀIð,ÑÎàW e@„„g ü€RH~FÀ 5ÐG24¾@*!’£=< ‡Di ¤ ”$N ð* ‹$*®@Ê€IÕø¥`€j „k|TC$`{ y@ÉØHA$(HÎàT@‰Z \”×_ •À‰\ü è"©‹+ƒ2 BàLP ß8 ú(&À(@%0DA°2ôPŒD‚r`€b!þ@tQ8ÄÀÈÿÁ—óàúOð,ù?õw3ü:ûŸéW¢í“pêèü¹§ö§O’êüé“þì!ýûôJÜõ.âÆý6ü; ð*îû Q‰+ƒ2 Bâ2~@ *!™=ÐERWÁ¯$è!É)ˆåÀIOü è"Š+ƒ2 BB4~@)$G#à€è#Yš_ •ÀÉÓÈ@ÐC"5R Ê«øÐE’W e@„¤k ü€RHÀFÀ 5ÐGB6¾@*!´=< ‡dm ¤ ”$o ðJ!‘{ y@‰ÝHA$(HôàT@I_ \”Š€)ðJ¡ 'Ô@Âø¨†(ö@ò€Ї1‚ÈðÂåü·þüJþþnœ_‰j ‚i|TCP{ y@ÅÔHA$((®àT@…V \” ¯)ðJ¡'Ô@EÙø¨†(Òö@ò€ ¶1‚HP PÀ%À¨€.й¸9("wSà÷/ò+áúŒ?ýÒŸ~Iªó§_úÓ/ýûôKFÂ9§¾»!þ=<îó¨ŒD‚r`€Ä%þ@%$1#à@ÐCR3RÔ@IÎø¨äüq‘ôì ä=$@c ‘  !J€?P]$G1prPDH–¦À(…Äiœ@P}$Rà  "±ÚÈzH²Æ@ "A90@Ò• ºHÀbà ä  ˆMP ÉÙ8 úHÖ&À(@%0Dò¶þ@t‘È€j Än|TC$z{ y@IßHA$((àT@A \” „)ðJ¡X'Ô@ÅÃøÅ?xáÊ@ÐáóD‚r`€B#þ@tQtÄÀÈA¡™?  ’p@ ôQ L€/P€J`ˆ‚ed è¡x)ˆåÀÅLü 袰‰+ƒ2 B¡3ý'ù»‰+ƒ2 BÁ4~@)O#à€裘š_ •ÀÅÕÈ@ÐC¡5R Ê ¯øÐEW e@„¢l ü€R(ÐFÀ 5ÐG0¾@*! ¸=< ‡bn ¤ ”w ðJ¡Ð'Ô@…ßø¨†hì äîMnúŒDþ¥_ëüé—þôKú%…Ο~éß©_2Î)¥ðÝŒ€jDe|TC$.{ y@IL \¨>’š ðrPDHr¦À(…„gœ@P}$@à  "!ÚÈzHŽÆ@ "A90@²” ºHœbà ä  ˆHMP IÕ8 úH²&À(@%0DÒµ2ô€D‚r`€„,þ@t‘œÅÀÈA!Y›? ·p2ôÈÅÀÈA!±›? ’¼p@ ô‘ôM€/P€J`ˆ"`d è¡ )ˆåÀBü è¢Xˆ+ƒ2 Bñ0~@ù^¸@ ôEø|à  ¢ÐØÈz(:Æ@ "A90@’ º(Hbà ä  ˆP LP ÅÊ8 ú(^&À(@%0D1³2ôPØŒD‚r`€B'~@)=#à€è£š_ •ÀEÑÈ@ÐC4R Ê ¦øÐEñW e@„bj ü€R(¬FÀ 5ÐG¡5¾@*! ¯=< ‡"l ¤ ”e ð* ‹-®@Ê€Ûø¥P¼€j bn|TCw{ * ‹B/®@Ê€…ßø¥Ð'Ô@Ÿþk¿[®Îwþ¡_úÓ+ýé•þôJÿ™½Ò?³O2Îî˜rŸ+®@ʸ¿G‚2~@)$+#à€è!y)ˆe@„df "A90@b“ ºHrbà ä  ˆôLP Ð8 úHˆ&À(@%0D‚´2ô,D‚r`€ä)þ@t‘HÅÀÈA!±š? ’¬p@ ô‘tM€/P€J`ˆ$ld è!!)ˆåÀ Zü è"Y‹+j äm ¤ ”$s ð* ‹žH \”’½)ðJ!ñ'Ô@…Àø¨†( ö@ò€Š„1‚HP P4$À¨€. ˆ¸9(">ø%àŠ‹p@ ôQlL€/P€J`ˆâcd è¡)ˆåÀ…Iü è¢H‰+ƒ2 BÑ2~@)0#à€裠™_ •ÀÎøÐE±W e@„âg ü€R(„FÀ 5ÐGa4¾@*! ¥=< ‡¢i ¤ ”Q ð* ‹‚*®@Ê€Öø¥Pl€j âk|TCc{ y@…ÙHA$((ÔàT@E[ \”Џ)ðJ¡ 'ôPÜD‚r`€b/þ@tQøÅÀÈA¡à¦ùå_ú# —‹þáŸóþ៵ÿ{JüÿU«JÅm½\Í™$‚~\yŸ1ÃRBÎ-?‡Æ-ˬúçc9—‚î´Ìc}¹ñCGTùÏX œoØëJ jž¼°zO/úÆA;hO²ïζ·ÇÍbZ½æƒÒKÅ Oz9úpiìTçdRå”ßEÅyUsÖ¼Z9¨Å/íü]~>tÅÚɯ%G¿áñÃoÆgRù³šÊÞ+ò?Õ†´ó±oå…ÎÑ}*øi@7·&7Ù6‚6œëÚ5ch&=›ë¸fà‘<¦±ÙoCü`ЗH2jj/¿ò1ƒ§ï~ÙÚ.Ÿ ~N”Ôë¢Û/Á‰ŸË¯“Q*ޝ±©k·(šà(w:ð,ƒúw¹ÖD¾;¿Ê‡ƒ›úè¹Ü‹úXp6øß'‚Î@3/Š:kfP½ìŽËóï—nA¼_³¹7¿9ù±1t§»*^àEÕLö˜¥eÄáðÕ_ùŒŸ"¡ŽÖ‹ ßOöÒΧâã]7òl5¼Úpæ–A.3жŽý”ÏÄ_ooÈò—~Y^Ô 99þ†7ï3"…®áü‡=b£èU[ÿ"ƒà úµáEꆬ­ÍO[Ë[êUýdqÂw/­ß'?è,8[ë’(JÙÓyEËT«Ûmߋà ˜è¨Ñ±µq.¡WÞ{Ó°¡#çl~.ø¡@wagl%ÌeÎ ÓQÆ>f¬å´ý›ºùY?GÔ‡¸©.M?ñ:5t§8{ÌV·ˆ›Z}p{™ ¾™lí]Àêt¶óúkêô"³×·ñ>´"jP³Ï?”ÌR±Æf¼÷-šþñg–k­a{Šo]/`&: ÊÃmiû v¼FMêT½ÑÙ©÷?"èxßë[‚P]2œÚ»uaãçªÛ“kXѱºÛ|È9vKúuGÞïL ]SEÊÙ‰·¨o§ŽŸdÐ~Mš?mPÈ4v—‡—Säþà‰«¶òå‚SÞñ¾;èò_:OO¿EA­8c ú”æ•U$.ò¹pcÏl!ÙÞ¿Æíâ?—ñyÆæß°˜AñoTrýu…¬‘¯ÞÂûÅËi¢ÞóÇ5/ù>¼o‹ ºé+¯øDwÈ ûm§‹s.²ì—×®U³§} ?Ù‘7mÛU”™mÎÇ ¿o—C“Ùø~“u†íjU?ƒöퟜZ”[Ȫáʳ£÷½Vœx²Æ‹4cSó"è<âMãpnY¥bó¡#ž_Æñç\Ç:=I§ãw:ßœcXĬ5 Õ†Zwø¾¨ÏDo­7èz=®{çW×[Â|ÁtâÎŽC‹‹ïdCÜô»v*oº^mêø=ãxº›}ÒÙ6¹EÜÑʼ•N /dÖ<±¥ˆ ¾¨4¡UVDÚ or¹³òÒ¸Q‚ï:t‹§ô¾{¶<ŠÂvg¹÷ºœN˼oæÝ ,bã÷éŽPÙЋAŠZùÞ¤»¶ûÊ«oùß'…®¶íÖÎD‘÷° ³½¦SNÑúI:EEŒŸ×dK+B‚šúPÀ{#g¦ >|Ð{sKQ¤ãä•NÁ]KÞªûi}ZžÕ\é\yÁ‡xÁG º×ÍÙE™yKë»­N'ÄúyñF™ÍêžÏwݱ£¹¡Îý {o!Á¯‚t{똽þ¼(Џ¿jµ,æØèoð±Èì¸Ëê³)jÔÐí8u‹vž*¿ìRqOóoG/ Œ"ÏŠÈÕa3ÓiD+¿:Žþ™Ö‡†÷¥ÜBÙf5̺™ò¾c"èÜæî¯gU?ŠvÍâ ùÒiËà»;Ÿ'=d'gpgžµX3"yÁQš¨1F|ˆ ãÜSõžF񴮾&¦SÁv‹>®Ÿ²‚šúGè‘-± çhzñqö¤%:|~@'ÿõÀânt$j9îG:™ï2uëRÌnrã…:ØÒTnL‘žýÕM ÝtÎ^ùp$½â.ã¶éŸ>ÿÖí™ÅUççoKV&üò¦Æ‘7ý›óyEÝÍ&1F‘k#é¶å$Ww½tr¡÷7¹³£5Fg…[XÓ±©ôøuŽ79ùÛûy_æ§:þó#©³Q¿’KµÓi¨Ç¨YGeÅ,°û¬E3r­¨­ÆhË[˜“(øðAW{ÚºjýE’oÖ33~¨hPá-ñÛ¤bÆê•?|¶ÇŠ 5÷¼ÉøÊ–Ÿ¾ñ>g:9¥bÍX§Ž‘zˆ3°TѨ1z[½+f'Ö¸·6Њš6]ÞÖóœ7 Ð[ð~ "èŽm°öMýHÁŸGEΪæåZ=b…¹^.“¬i]àÁoÁ¡>™êñðÅ}ÁGº!!GªgüŠ À·úýP«Èëú™!âGLcK1φæLNÊVÞÜBÂX>~Ðñþ4{šKï-Ù*Jk»¦é!ÇGUsGÆ“ã‹ô-ôÁ|ªùÀ½Âõ]J‚>Îôz¦ÛZ’ª¢{u‡œ¶:ñ¨j.¾o!a~,?è¦ktªhúÖö+^¥=b¼ÿùr:=îfVˆhËßüÝø{4!zóñýÞU»Õß.\E«ï:鮣f¼ÚrJ^77eE·-$Ì—æãÝ«¹{“ŸàxèæÒçÝU->9 ­n5ãý[—ÓÔMok~ô!¾®»\ä‡Ûâ)^µ"ÉrÀËÅîgTTýþ—ÊžƒÔì|©¸qØJ{Ú`~“UÞ‚ÿÿy"è⼿WkÜ ’jÎ?¿°Æ!íP¿màT5k¼yCïikmiWnBŸm²K½¾7èø9^‘tÙuɼ]¾*ÊŸVwC{‰š9¯öˆ›àc-Ì/– >^‚ßl®Ö§,’8—Êf›UTé¾:ÖEÍÚ´j´ÅºšÅ/5ŒÔ÷¢zBö¿º$øÍæj}Ã"©MÀnÏ3kTtàxúKÚ¦f#ŒòNè[ÒÝiW¦%ð|gy ºk®]>*q>W¶æ>@E…s8ƒ85sûQ|‰¾[P¼<ÎÿÓo:²$áɰ­¼O­ºh*¾¶ {¤0gPE±óæ\U³c/GÌ›kI6ÎvþÞ´†&ï«üž¡sæÆ0B·bÊäáþ3p>»Øú5ZÍμè–8ð§%Yo\8ZÒÁ›lïè§ìX/ø­ç•Š}.ûõ®èI Wê SQ«XÑ™I*5x0 Õ¦ãVô¶+7ØÝ‹Æ©Ò¯Y+\wн.Þ_sˆŠ"3»>U3>Ÿ[“e³cQŠR/ÁO@ˆþ~oõ 1»{ªH—×[©fZÿ:~žž÷ßâ,Áßû~¨Õ7’Ž/Ùíµ®Šü²ç”Š>fü¼S몺úxã•<ÿ\á:ƒÎÀëÞpW‡÷[âTP‘^ÂŽyw:=fÂù@«#ºÖyíàM‹Ï8Ooù…ï£dÐ-Ÿ==¦ºŠ¾µ[~«lÈc6dY·Ž·ë[“Më'¦­"¼´>÷|œð÷­8ÛYÄw…&? ûþF!S3­_ìî½·ŠûyÑTOc¶™×©¡ÓŒ9Äùô-tç/UÚV#bŠlÉãªù³amš~1ý&|Ú„ë,ça­‚ösp½ô2Žæ> g6ä¬zÌÎøŠÊ· 4•kgßRš¼?lÛwá:ƒn瘎6ñ¸>_ës†àhz »!‘>f¦ëÔ=À‚ÊX@ó)­ðøqqâf¡?Ž›ÙçC¹/ß©syÃ2|Õ¸Ø~ÿcötÛ£¯%´ÚfÇý»¤´°‡Á[W!nнæl J#¨Ç¶§ÖO< «/™C¦œ{ÌÆ-sûî’.¡~ít“«w— s…ë º€G[oä§DYí“­‡? >#NLyÌ r®|ÝÅ‚¦Ö?:?¯”‚§q…Pðu†Î&îxÊ9yùqãÛ> ï*ÉMYücßu‰²K¤å¶[èƒãùìÆ­FÙüç)  0ÚbGuéy³×÷ût•ƒžó˜ñ>b–4WcPçE3h3ñs(y:ûÃÇ2¾™…S;aƒÇß§öòkÉs<©šó™?Îϧ©­ÅÙ´Zc ÿûdÐmªÓV¯S8]»ïà5ºû}º6¬qÚz‡'l~LÀ…™b+ÒÙõã[§¡®”Tž÷À²§Pç ÓùÕsþ¹×aôhfçX¦{Ÿ¦tÛÖïæ'lõûòÓ5R-¨Ë¶ö‰ØÁ/HˆtËùç\ £æò«{u®TÞjÈÞ'Uëݼpƒ±Ý7jý”ùø–ŠKbõ†o£ôà·ë§ß£´“;&ü8ý„ñs3%TG÷YÙ{ww2ËMR®?èšö{Ú¤È8ŒÖ¤mêrë]ÌK7Ê |ÂÒGI'zü”Kç B7“0§žtštÕ<ŒúÝ|ìõ™{”Ÿ–ø®iÌ&ôt­Óš}oþ[_#®öaÛtÔ¡äRauð‡ï=*ì—ßþKÆÆ×)K\Øâ›¤µ;u ¬sðI‰à3 çqàZ(EŽâ\ïQ¿;“'û”>a¼ª%íŠåɻҡâaw?§ ý tŠÑnq›CéqD§ÄKïQ%gÃZù¤ªîlŠ|9w„ëßÖ[ 覎ãœõB©ûïƒÌ}ò=šÿhÞo÷ú%UsÛ߉ÃÊÒ•„<ÅǺlUmIΡTò$ hBß{tгqïPRå³ñâxò°Â"WRåÄÄÇ ù³ýºNIëÁŸBˆ÷»GÁ¡•Q“ KØ”/.(ùd{N÷Jaþš°ãȨÓGøëH|¾wÂã´seùG-\iüfߨ’ª¸× «¬ÇzrÑ[zµŸçÅÐMÓ(„~Æ(\Þ=N£feæµoÌ.aZŸXÆq“ã×ß_ð}¾º/IîÙ)[BhÀ8×Ð5ñi$'Ý')ašñÍò%´+au½Þ-ÖR©Ÿ·4ÿ<)t‹4|Mù¬;düÕ4ª½­Žýª6~Qå·ÅG ~H.ÔtKo™É,¡îAWQàx=~Tm^¯X—°;¸]žî%Ì÷sV~¦Ô*·rúç.‚ÿ³°>€îì—D=v!üyCøäÕi‘¶÷k·í%,uÛÃ-Ò…¶œ£¯‹à#(¬Ï¡«¦?jú÷`;™›l›FS÷nxú@ [ÓiŸzRÊâýÓ\H¨3|ü–ŠÓeÛ ‚éU-Öp@]vHÒòt ûʺôš7l!m_¹k`[kgR5‰y\K¸þ 3{›V3.*˜4ö›ÍÒÈîGÐïò+%l7®ÝÄ”‚ ~°—Ê5ä~~ÿÝ\þóÄÐÕ©ß©"˜`uÓôs*-Jî°.¼„­_q¬AÒÇEÄû­¡ý’«Çšó: tË•®‡¦ºSièå§'óR©‡#µ.‰+auW]pÜjFüüogú~“3Àâ]ÍúâJ“`¡oJ¥™Ã¯·È(a›ÂÎMnoN=—oYêéLÓõðü²—_OÊ ;ãñêbÏ`Jäìa§Ò)Šx£.afo,ú7—›Ó¬µ§œY°æoû[ èFhEÓ·t'd¦T2?´ï ë»VôñÇòó#̉ë¢ýn­|pø}?5t»g$Dœ ²hI*Íú_~õg ã}¯Íè½4¸çBW;ŠúÞ®cßûü¾‘Nq©¸G‹=a,$ˆ†Ò¯ŒscR©éå·ÝÊê=üpi×#4hO÷VVþ¸ˆ ‹uàv:ƒè‰™é°âö©Ô’[·|ÊxŸõ”Ófꛉ¡æÂ¼c!~Ðu5ðص"ˆÖ\ßUh¯“J'¼¤+FçÒÓ/\ažGš¯‘lNœËÌ.þ|‘@Ç6·7²›Dí“Ké£PZv$6 ·ÊOLë¯óWÿk)t+Žo8y®KidwS¨Â°ñ§k‡r?êy¤'üõl†ï±ÍÂúº:å-M~Èi]uÎQ<…ZÔVd2Ï\aNû|º³ª‰þ«¶öt!éÝÊo!üu«€nãÚ.}z=‘Ó'Dµ•[ =|¬¨p^ž[åW±—>Óþoþåjèºíßìg#'Y€£éÜE)ä)+ 7;—tLÛÏhmJiœ}H:¿¤ +Ÿ*Ô¿G¥â'kÜèwYNÓ£÷¥PÓ]öžKÚ<±Ø{·´Þdô…|ýåãÝÓË)_vË©’³»i•BÎÆËÞ‹r‰e=8±sž)™ïW4>«Æ,(/â]Ó‡!7ÓåÄ_oÉ–Ú´²CÝ\Ú¬”¾vNW>ŸOKʦּê)\Ð=Úî³Å`™œÐ ;UÏL¦Ñ¹sNüCW”¬Ò»1ŸnýþÔ@<R3£Îâï×+§…ºÜ$üdÚ¿þM»æÅ9Ĺ+­Y5—87ÈfõfÓ ßÆ¯+û ý t<Ú|y6LNK+¤•‡’iá=— wSshAcÿíÓ¿šÐˆê‘‹¢M…¹ÓÂúº_œÓ¡œ¬ãv0“) ¢bLJÛ9Z?aºØ‚3¾M[¯î}g(¿o«†îvƒß/jדSRÅ»oi‹’iÌš‹ã§ßÌ!~_` æÆ¸èC¼O¨°/­.7Y}Êóöë@ú¾îòÈY£’)5r¦Òå\ñûU“ùF$øuðñƒ.û…8gîµ@â}¢“éU­ogÍ!­ß-?ß?ënd‡­¯fGò絺¾]*N§,$Ó5Wà ë'S—es<Ú•SåÊûÕW§oãͺÙSˆtÖ÷}îÖ6NE¶ RRd™õ• ZVç~ëÙÓHzfÍc±qKaþ>=H¡;¾Ñy†WÚMâÜŽª•tÙÎðÇ‘Õ9B^0êzÌÚ·&¥z ×t_§„ÍJ²º)ô5J²+Ú’·$G{Ÿ„øýð^Œ‹?èÞäéÆ¿úvƒ,vÔ94#JI–õŸÍË”Cs»¥Ϲ¿x?#–rå”…Ã4aýÝ©ikæço¸AGOz ¤ìœ’Öûúæ>Ë‹éùàú¡#/±NifëxÎãRqÔoåÚ²O×É~ýC³ýþJ*·‹u›æh÷3¿ßAL³ š ĺÉOoºe}în©×¨¿¯’:ÞÞ¼SE6íû]}¬´9]å%Ÿ{}ë5”sÜãÏ31t¡#†_“tš¦HÛ ¤ç=ž™—MqM.Ìym¿DðEšÆî¡ºnk-ìoB×iøû½® þøžÏ–yŽÎ|zͪüê‡]>PM¶“ÿ<)tyÇoD?õ¼JJ°¢V’õ“ïÛË&ÞŸvÉÛ†÷Mq”°Â‹ ·_ôúèªg“¯µò }{´eÅ%”}1¿3›:‰–«µ}aÕþùmÏ>¶+„þ:Þó ñ÷e””¶&R¦ë’M¼ùš©ê¾}Ö ö×ý85tÍúnÕ—èR£n†=•ÌÙï˜eÓ¥ë·dz/¤³S,§D}_¢½/ÅÇï Ö9m¶v q»L ß÷ËÑWÒvÖ}EÖøl’/àþ…) ýsL{å;~¨°þƒn¥QŸ†?.¿¡¤âñ®O{fkï;‘?gKÕfûk"†n Ò|KÏ”‹´kåœtÓïIäxí׺²É{ï‚° ç—h¯ &øØññƒnkOßV^ Ím”·IÜ,jdbyV•_aW÷-Ç;͟Ǹ»;­w û.ÐMål,[_ óJf÷zœD1w6ýx—‘E>3NMNømNstÚ»n[—r¡pºÆ“¾x;â`NHd5¿§e–°^\D9{ðf6ÌûI’ÿí#Bü k¿¯5·sHïæsÒIôãËÖyS²htã·÷ V.|=lØcîΆ°~‡îòÍMGžÇœ¢â½N‹ËÏ%Ѹðíúõe‘ä©ÍŒ>åóéÞÁõ¬×Û² ú´Çý£p_ºº³ïʶÖnŽÞ¨»wV8±¥GŒÂ?ÒR1ÿÀ•‚.Nž;úX9×5öº2ƒ®Ì8YôÎdÍ^Ÿ6f߯ûëuè&Ì–Íz:Å™ÚöXy&‰^%Þîb–A¼?óêväÐíï×° š 3!_B×u¢µiBÛå4F³±—D4V¿ŸÅù zÐ*JÂláþÅíºŸt^wÙ2ÙŒò*8ãAœ¼ÍkìÏ ~ÿcŽà›»š]ÔSvÛ;Jè7¡ãŸ@ÞÏ‹ãº%á¼bûwÜœAü}ãùå¸öþ¬5ŽÌKÌ9! qƒî€+çà3‡ýš¨òŸ‚|9væÇ ì2h\™lšÑ§4Õù÷… ´ë?>~Ðñþ×Ö,`Øùgמ&‘Ìxlµn³2(m⦶wL©E’×ÂÔSËÙ4¹ÑɯÙüù¥†Ž÷Ÿtbü}¥$ªv>õDÜ ÚVÙ\§dݺe4£—Û+޾¹LΟÇ:ϰ¾|YÙ&Ûâqii„®’|™[r±m8Õ¼Ò|äb2´ï„ÿÔ Ôbpß |AwÌc‡ë«îL³,h­¤=s*;ûUË V}Å…9‹h¬õþîÅÕV1Á¯—t—r¾ïY_$eƒ4…OIß‚×)K'ïö¿kyë,¢úo™ë·š ~Ë|ü ûõ&xÙ†[>ìa—מ¡øXûc°×ƒtíz™ÄwZ£ô®f—¬¸ÆTèW ãŸ‡ÙÆž”¾X2{ª’,R·ùÍŒL§.}Gt9¹€Þäžz;ê½£àËÃëdС©@*ÜÁjjŒ•4mǪ:ƒÏ¥ÓjKã™ÝWͧ–Û”/^}u`“ºq+B^§€NmûÞÒÂw7»i3°Ý;%œ£zÓmO:q®kó(Í|š²Îè;.jèX8g»™.¬å0ÀUI¡§õÊ»¦³r}m4¢~©­d‘¦³}– ëõç¥bWi¾Kìgæ­çµ=‰¾ñp?—ÙVéd®ÿbâùÜMÎy³žýýxŠ »:sž;Àøýôï‰ ¾6˜‘^u½—}Íœ´n„|1ùøA÷Pw×N£S‡XЯá’+JÊü±ÛV18½*î*î±§Ù«ïŸÅ_ïèÜm§H/>Âøz£$ŸIß~µjNs·v<9ËœzÏ ki{v%ëvgÀŽ1Âzº›½¸Œw”•5Ûx`í=%-z•Ý$ý«ŠºÖëûêW„ø:çÈ\{©O?Ÿ%Ä:»>çŸfcjq†bJBrë°-GE±­ºÏ­uÞR¨'+ØÎNîä|>~Üï«# Ûqð›q7ÿY"Ö5— •½U—ãXÚí·ÅÌ1W¤6XÁò²8ã<þy05tÕO‡bEŠ”l]몛LŸGïûüt›Š”ºÜ•d]õ=ùç™øçÁtʰN<>Êfˆùv˜³™ë˜Lœky¨¹J¨çÖtôç­ÌS=V±ø·#·%yðû;"èŠòv¸pYÆøz™Lg,jú Q‘öþ=šñþÞÂõÝbÛ&;oÈβúXeÏH¦Í±]FJ«HûÜÚÏ"îÁ'ÆÝÍ£yÂzº½4;_~é»òíðýívÉÔôÑÓ¦çrPDHà(ãY‚/•SnüݱÇtá9$èú.–†g_?Ï^·ô ºë•LÒÞ,}{ð}rf˜œ—PHè\—¨'Ö³¶ñ`¯Îüï“A÷®z·Z-XÿAއ*N$S†ï˜øk³Ðá¶„~ã¤qbcÌåuâ݇‚„Á™ÙÃ÷¨dšÔîG£}¿Ž°öœ˜ÉåQ·š´äujèzœý´Õïã%ÖIó@L2-iinpUyŸŒšen5ýiA»?¤¥Z|YÉ:¯šÙ­†˜×é¼Àú$¯Ñ¤×ÍØï˜!>%Óù!ŸzܧÃ>=RËŠ,9{òwL^zøÕÊA¼NÝ›FÜ/¾ÂÆjìâR($âÝu#Ãû4Fs£ÝZûü(Û³´¡Iá9@è¶HÆJõ*+o½C¥;4…Þë%Î*)¾GíG5;Þõˆ5•z_¼%µc¥»«sùóLÝòä’å_½®± ?û9Þ[BŸßp7ŒïÑþ^œƒ³5xî»éè;6tÑùçÒyß-)t+÷º`‰{ù«³êØBm%»¼·ÝÓÞ§^ {ÏzØ1Ëó×MŸ>™tšÇÜ`¯âÜÎ/ðO¡¢Ú¿+ž¥‘ͯôaËÛXÓªæ‹V„Ù±!ãö¿}>t¼/÷M¦Ù¦‹J!…CÛ!-üÓˆë2†XÑ ã"«ãíïËÌžºø ~«ÎW“³·‹Ç)ËO¡ ¿Gûíf¤Q5± %ñÇ߆ñ>ÈüïÓyY*nN‰Ea[äŒS]ÿ–Bw‹M¯¥×N£ãK-ûdö· 9'ÏïÝ{ÍBèC„çp¡;e}-µgµ ÁÏ2UðsK¥Z³:-|¹HRµo|ä@i×Cwyº4«{Jц vߨáì­”Jc^o­î“J|þ_F¢ñIz­1g^›ê¶h·”×I {q‚3 bKV^̵ÅçÖëÑhb*Må–±í–Ѿ¹k»ý-aüùÍë¤Ð}nÀdzƽ8ÝT:ÿÙÑîJíT’¸vKvÙ¼ŒËŠw´düsZ¼N]ïY¢×ͯ³fdN¥-—¸D˜B6{Ü|'mÐÐÍ«GÅ[0~g?è°˜™1Y'„Eׯ[”JÙËn<î¢}Άÿwd)ÓÜÆY8“têmú燰]{4‹¨—F;ÊwŽžBý4.VTß{z1 {,\~~*¿W¥bî¿ÒáB»|x–òitâúìÆéeÉ4hλ+'?[Óé{,|nÊToý,¥ÆóñƒîξóÃ?„0ßÓ‡¦4\™F¯¹Ç­÷'µãœ-miÛ°åw{-d ž·±µ5€t6 Þ9Êå:4v?›Fu›/xðvòLhû¹lì´~ÉŒßÇRÝÕĺöO+$s6…²ö.§ƒúç§Q‡ ›Ö|ÌURC§±á}–ÙѦÌOùÕrç uóF'…®q½©é)wBç2:¨ù=Rž*vW’«Dçñø¶ÄUÕ𜅌÷ÓÈǺÕaÝy¿CÙ¥q£šöŸuÎGô{ÚUIÅ8#?r9Ó®–¯÷"Öb÷Ô—5ó>‚ è–}LÅal̸{~½vÞ£¥qv“Žg&ѶÄfcOn·¢".½]Äø}SÞÏQ ]¥}7Øz‡±ì±KwO¸G);¿ÔÁºOØŸ¦÷ù܃8¦l„f9˜ßkäOï_›µNyÏ©Æ}j—Ù숫8‰´¾ó?vÉ X(ÜïïÌǺà&¾¹õÃÙjÍÂþ>Ź´Xþ5‘¼Gr™Ý‚ê×ó¸§ûBÆï³¶ããÝŠ3{“Ì gµó&cé~Ÿâ(|ýë DâvYôŒ,…ø/`¼$ïç(ŽïÃY·‘C«¿¿uŸzEeÍÙi—HKtÚ\œöÛRاŸÇøóÍ€¿þ ëÞæ‚ºçÓp¦_üÓO÷i_½9ÊZ' Ï•X‘EÌOiÚ&Â}™Þ|ü +_Å=¸Áº_^¿íJßTwÏí“+“HóØž#òµÆïwãžvi¿÷ãT@ðcÅÆR·vçRZíÐÀ¢ºß×'ë|ká}bÂþ,?讽p;÷*6‚ \Ô¯í©4¬•ga³® UýËhî6¸ÞHVá=ËåCç.¤‰ß›Rñ:¬ÂýšD²Óö¨d= ‡ãíJúgÄ“÷ÜŽÆÅíi{@ì™Ç÷F° k.ñÐèDÐõM7òò¢HÖ&v@êéš*Ê{¦ç}Æ;žfh|µ"þy;#ÆïçÕèÄÐ=[Ä=XÉŽq·UÛ«hOǵmB†Æ“e~Z§+[-…õX–ž™ã+¥ÑI Ëó~oKÅ Ñ‹Ì^·˜æñÝ*òr–ÖʾK'WøŸ}?AB347¶FÝvåãÆ>Úæ{ê=¨ò{‚ŠÞ9Ôˆé»<–öG jd!ø¨ŽÖK¼±º'¦u¼%»Åîuñ4,/RѸ³ó‚žwŠž/³¤ÚÍ<›¯{6Sxžs?è6vàžÌ¹Å¦ïyѧBEáëºwiŸCÁR%»Ñ÷hüŒçОÖ¿G54æãÎÂ`«.z·Yjå—Ö­¥“µÊzñgÏÒúz¿~ØñÝüÞséôæÒ±n;§óñƒnÛ>ßžû'Üf[ƒ¶ŠêtK§!ãL÷t‹!…ûä=uØißw!+îµKÁŸ:þ9«Ûl`M¤Ó£ÓéФ5ƒ¼’Ä÷+vÄ÷;ó„÷†x?p5tiþÞCî_ºÍÖ‰^]É^˜Nu?õóɱWÏyØUí 6+_ѵn,ï[®ó®T<`|ÉÑýÛìÓÎË–;¥ÓÕço·4PÐ¥co×ÍY†ú )ó¨fµk_ËxŸttGÞŒ/v®¸Í¾›Æ¬¿;Úè%~êéÈHûÿ~Ï|zÙ¥|û™ß¼/»º.ˇ}óoÍͶx\‚nêÝcÓî ÇÑŠ¶¾po³f‘öý>~Ðikï͆i€tæÚB÷ý°»ô¹g’õšß±lÌ¢K̉‰ÿ}Rèü ¸2£Y óïÕݧ¿.¿C -Ïèì´ †š8— ÷«xŸ{tuZ~­Íë¹?Ì >Ü6`û;¤nöjÞ×ÓÔG³hï›óñƒ®¦Rä35ši¶ÿ:fPâû=ûøES“Ö ÞOzf¡½¿Qµ.×Ä:.KlgÐ ŸU;;ÿ¾M ï·^9ß’þ\é5X¢ÝçãV^*ævçXdPì¯IGz¬¸-¼GeIÚ÷k´÷•5ñÂßÏìÁ½ Íx_è Ò½úªâ`Î-’¸4:]w†%鎩%4ÿ;ÄøûÑ’Kñû<è}ÚíBÅ4Øô½ç„[Uý°çösâ3J+RŸä^ YÈÇ :•–Û”¤*~ðhDXñûð4«;¿V$ÕKö³zµˆþžëêö¿Î i/Ž¡cŽ"í{EMœ‹'Õµ¢ö¦Öyõ{ ü=?%šùÄ'ÿl”IS5†‘Úç]„çC,IØgäãc'åØºƒ2I6QÇK`$Ÿ¯Þ£PaN]¢GëVûjIGŽþ˜õë­9ü½ÿ6Îà;šMAÕk¾0“~^°X}'B¨K„çz¬Iÿõ6|¢—÷ˆ¿f¿'šÍ=ºÛª«{&¹¥è°zF%t(HªçaF’ÑoÍn4³žsàƒºnš\Ñl…æ…¯Lê°ÝvüyuxÕ¾uëëMv¼²'þ}þ<CÇï÷D3î퇣1™4"êpóF.á$ÛÙ6ÂÿŒíÛ{m¿ùòåtÈŽ{×I ã^_)ØÍ–é÷»$“ÞìÑËzT-œ*ŸìúìÚÉœÅMZÄŽÙ ŸËë¤ÐµÒÜ8Žf·¦ör¬EšåõŽ0:Š£ÝaÚ2:Ü(ç÷ð4ô£¦ïèêÆÿ>tf¸;ŒÑìNÙà,ƒ,:e60¯C0šÁÙqW³¨zŽÁýó’KÝcøã©€î´„{ã.ZدϢ¾mŸKWm %Q¹B'v»%}ÍýR¾œ„û*|ü kÓÂ(¿öÛhÖ¶o·€•Yäó8b“gq-Yt.$­³µpÖAû|¿¥bþûÜašÇÅöCg»©æùa!´¥©mÁ¯Ã6į´ÏññƒnUåõ©î0>¯gQµ>G¶ØL÷”^·Ç–$7'ÖW…;ÐÍFÒÈk -øøA§IW-î0yµ&ORò³(½IàÙéAôàkϽ5m©¤Í™ä_Hû|ž&~ДšuÌi{‡Ý2ÌØUð3‹†î?VI‡ òb~¶±-¡©<0ï’=MŸx~^¬?è^]o™gÕù;²!»Æ³NÙÔE“ää’=jòÓÛÖ¤}¿èèÕЄ!cxì/Ÿçr7ÏýýÄlành{)–Úæ¼fEûâ¹iM[ëÜ»éÞ›ÿž è’;œµ5ÆïâÌR¶"›Ftìÿ{ÓMZ³0°óù–Âû†¶´~ÇË^7xºïõk§{‡µ–Z_¾/›Bò'›w²¼A;Üb.ßhAWsbû„º8T}oMü>–ŠUw£u¾G³»Å?ß/ ˦šËõ kN½®}^“ §ouíºt•p͆t{&fæ'«£ÙñnÕò³iïÌËÛ&÷¹V•~»;Ñ󸚭«ñ:1t>ܶ ‹füû‡ÙT>½a~©ÎÕªüµþ§÷CùÊ5UëMü ë_¼¯°ÿ™h¦¹Ó)‡Þd¾÷÷J Ð>OOßNÞ6ï?cð^°?èRõßþpC43¢¬±b½í<}v¿=—‰»[Ò¼ÌJ8/ÈûúåËÌùï)ƒn‚¦PG³á=ÓÜO.Ï¡öi^»Ì¼¤}®T»&Ô3[>~ÐévÞ5ø}£hæÉmïïΡq ‡~5¿H_Ê~ÙofEkR'ÏɈs"ÏʯïbVØññƒNbF?íîÝîOäÐëÅšú•ž'î­åÌÓ–Ôè“Iá¶ÙNä{ªçÚ¥V¼NçS©8òÐÇ„â]·™‹÷Ðá›2r¨Zù±¡}cÏѽä]ÓÄßßZEH¾‡ŒÏñ:t¿k¿ÉN›íjÅ-”r¨Þ¡¥¥mägµï; ÏQ®öûy:i“Á¶M+n±tîuÃV¹tbÇÞÃoÈèÍ;Ç}ÅNË„¾b%ÍfõÍBó: t+"jªÏÜbÝ\Ç#ÕäÒ·nOn|\}†æ¼¶!ÒœôÍ‹ÛÝÌ^EÖò'óVXðÇS æu‚ ·Øè†ú]7/Î¥¶–ÑÏæŸ:EN¾\_º¤ê¸Äö¯øx©³?èt5^#¼·ÂëÔÐismÅzÏþ|Äùn.Ííà=K¸?Ýý2Ì>pÌR¡?qþûõWúǽ¶É<Ÿ8ìQ.Ťüøe0ñi i+!~ŸÁ¹jßP?èp’7SMˆä×õ:ytô°×Òf}Qñ îJ ÒÜVlåLÒI/2XWáúƒ®Ù‡±l³#ؼ’‹'âEyôeƵ§žï'c“ú.¹–ÔOó¢Öšçc²×I +ýÐ1cõ²ö´9÷`i^x¿—4¯}6µ¦'×}—}\Cý}“ØP·?³%ÿÌKÒù3/éϼ¤ŸÙ’Úy·ö¹Á?î3ŒD‚r`€„%þ@t‘¼ÄÀÈAÙ?øàj½Þ´^¸ÿ*ÿ’ÿ®×—L =ýÎO’ƒ2 jùŸí‹k üþ ç'É@ÐCñ3R ÊŠ¡øÐEaW e@„Bi ü€R(šFÀ 5ÐG5¾@*!Šª=< ‡k ¤ ”\ ð* ‹â+®@Ê€ÅØø¥P˜„ùIþ™ÉmøÏ·–7î?Óó­í¿°WúWöIÿIÛýé‹þôE ÿì¾HÛq}пcÄå'!Îܱàþ&À(@%0D"²2ô”ŒDþ~·œ‡›Öëö_áGòßñn3Bbty@ ÒHA$(H˜àT@ÉS \”’©)ðJ!±'Ô@‰Öø¨†H¼ö@ò€’°1‚HPþ?ÀÛVüR((FÀ 5ÐG1¾@*! Ž=< ‡âc ¤ ”# ð* ‹Â$®@Ê€…Êø¥P´Œ€j "f|TC5{ y@ÎHA$(èq$À¨€.  ¸9("DSà”Bq4N ¨>Š¥ ð P Q<í ä=Rc ‘  °J€?P]aN$7KÛþŸègû¯ðgãú“.ö„þô>:ÿïö>\ßó§çù³ô_õA\®pbËý~îoLP IÊ8 úHZ&À(þ /Û¼ð²ýWxüw½Ù8/[]$Q1p@ ô‘TM€/P€J`ˆ$kd è!á)ˆåÀ Xü è"‹+ƒ2 Br6~@)$j#à€è#q›_ •-þ³½lí è¢ðˆ+ƒ2 B!2~@)%#à€è£H™_ •ÀEËÈ@ÐC3R Ê šøÐEqW e@„bg ü€R(|FÀ 5ÐG!4¾@*! £=< ‡"i ¤ ”M ð* ‹*®@Ê€Õø¥P\€j bk|TC_{ y@…X œ@€0C[÷/¾lÿLÛ…/›ˆ ýŸ>éOŸ¤ógèO¿ôïÕ/q×¼Tˆ÷û ðï$À¨¸ï„d%®@Ê€ÉËøåáe«þ/Û…ïÈ×›ó²ÕC25R e@„äj ü€RH´FÀ 5ÐGâ5¾@*!±=< ‡¤l ¤ ”$i ð* ‹„-®@Ê€ Üø¥Ìÿ“½l€< ‡Bd ¤ ”& ð* ‹"%®@Ê€EËø¥PÀŒ€j “Ûø¨†(pö@ò€Š1‚HP Pü$À¨€. ¡¸9("FSà”B‘4N ¨>Ц ð P QDí ä=Tc ‘  ÀJ€?P][1prPD(¾¦À(…Blœ@P}fcà ä@-xhýÙþ™~¶ÿ 6®ßøãgû§o’êüé›þ'ôMÿ陌„sN!|wCü;{ yÜg"Q)ˆåÀ‰Küê¿ð³-û?[¨äž?B’³2ôðŒD‚r`€(þ@t‘ ÅÀÈAý?ðjãülõ‘HM€/ˆåÀ‰Uü è"ÉŠ+ƒ2 BÒ5~@)$`#à€è#!›_ •À ÚÈ@ÐC²6R Ê’·øÐý÷³WÔ@EÈø¨†(Jö@ò€ ”1‚HP P°$À¨€.Š—¸9("3Sà”Ba3N ¨> ð P Qøì ä=Ac ‘  (J€?P]H1prPD(˜¦À(…âiœ@P}Sà  ¢¸ÚÈz(´Æ@ "A90@á• º(Âbà ä  ˆP”M€”ý¿xµý3ýlÿ~mb?ûLú¥?ý’BçF¿ôÿ§}&cá¼R ßÏ8 æþ›HV&À(@%0Dò²2÷_xÛ–ÿÅÛÖ(…$gœ@P}$=à  " ÚÈzHˆÆ@ "A90@‚4~@)$K#à€è#yš_ ümEH¦¦À(@%÷Ì6’«=< ‡Dk ¤ ”$^ ð* ‹$,®@Ê€IÙø¥ €j „m|TC$p{ y@ï?ÜßÖH” ‘)ðJ¡('Ô@EÊø¨†(Zö@ò€ ˜1‚HP PÐ$À¨€.Š›¸9(";Sà”Bá3N ¨> ¡ ð P Qí ä=Ic ‘  hJ€?P]P1prPD(¨¦À(…âjœ@P}[à  ¢øÚÈz(ÄÆ@ "A90@a6¾ ” µ ðý'zÜ5ÐGÁ7¾@*!{ y@Í€1‚HP Ðpo+ûÓß}m¹zÿ§oúÓ7IuþôMú¦¯¾ÉT8o¸cË}¾¸9(ã4HV¦À(…Äeœ@P}$2à   ±I€?P]$91prPDHz¦À(…hœ@P}$Dà  "AJ€?P]$K1prPDHž¦À(A%0@2• «p@ ô‘hM€/P€J`ˆÄkd è! )ˆåÀIYü è"A‹+ƒ2 BÂ6~@)$o#à€è#™›_ •ÀÉÝÈ@ÐC¢7R Ê¿øÐEW e@„¢` ü€R(FÀ 5ÐGÁ0¾@*! ˆ=< ‡bb ¤ ”>øÐE¡W e@„Âc|A$((DàT@EI \”Š”)ðJ¡`'Ô@Ìø¨†(hö@ò€Š›1‚HP Pì$À¨€. Ÿ¸9("BSà”BQ4N ¨>Ф ð P Q4í ä=Pc ‘   J€?P]W1prPD(¶¦À(…Âkœ@P}bà  ¢0K€P€J`€Bm ü€TrﺡpÛÈz(âÆ@ "A90@Q— º(ðbà ä  ˆPðMP Åß8 úhL€/P€JÀMr³2â¦ü¯¾I¢ó¿þ÷”Ÿ~ #­(kìÀ_„³WÞg̰”ÐÚI‡>×^–GÚ^“ÜÃëÞaŽÚ:9ÄuÙáú6œ.¤_ÖÁY;¿•ÎT«°ÍÚdS¥ç>G†Ï‰ÙÝÓ´Ä1œ $Ïïã‘Gn½Þ´…Äq'ð_´¡ö »ÜÎèLö]rgô äç( óNœ~niyûñ¨I‹«'òèÙÊðl¡‰týÎZÕ±¡C[×]YC‡Ãf¼ àçV¨¡8ä‘÷—7ò~ƒ¨ÑŒüuÖYPÝ ñ7ß¼±¥=zµÍ™ÇO1t§#‚ò÷¿ a ‚m¾ÞjOz>§§Ì6g}—¬>qEB ó¾Õµ‹°¥™ÇkOü¨Ãë$ÐÍìÕf…§]›uߤè}÷|Z–»s¿´ÏjV‡«u@BgR9Ã$ZšÛ?¯a¡0Ï:ã;‡,y̾‹R½wP>¥N_þ³o5Wönÿ½ùÛiç‹­®šÓ©‰tu‡Þô̵ fn»¶igšO æú~/“2Á_›Ê¸qñ§œit¸bÑTa tQ-›$< bC&´øÕ`u>-ïœ4àÍç-L˜Ã*Ìw\K•—çíŽæy@ǹ*[±òçjþÜšOžOw·Š ÛÁœäÃtía%Ì_+Ìéæy|)kÆ€”ÊY7æüd>MË9j¦'ÝË´óÚ¼Dâm{Û¯¥3‹7¯›/ã¿§:ÍVK¹0ÿ5Ÿ¾^p¸óéý,Êùó}ûk–”Prnñ„æ.4æþ`‹ïÉö|ü K‰q¬Þ 5%\»¾rgR>E\>Þøî³ƒÌvzL»L :ûÙläì¦ÎÄÏ­\ÎǺêÞÃÝ~ßd¼/`>5Ù«ôjx¤Ê¿o«»MñÕWNœx^'…nK…g£¥7˜ú¬ªÇ‡|ºÊ]fzþlõç:)+¿/¥îƒ§ôé¹a5qÓÛöÿ=eÐõø2²y­o×Ù‰Åá9­ê.¦]ìÍ1öóö—œËKˆŸO»Š*¦÷päwtüÁël~³ ëˆ hÎóÇ/œÐúts´V > Â<îxNQ£KÿklLè¾?†På㇅cN1þø™ sßœhõmÕ’Ëûù8èT–ŠëÙ<~µÂü*«tlßîõŒªîxbtïøÓìsð›Ð!M©‹aÎ0è5Ôè®á±^úÂ<$è|jjuÌã éÆNÐØ5á«/t–1­Ÿï#ëLÙ"n 7?ŸH ]®¬ÿŸQl ŸÝÑÕ› 訪ÙÖée2ÖÌ".³·Í"Á/Ô…´~ßšøA7góóVÝÊ.³GO®s¤€2çd}|û,ãýĄÍùuó†Œ]K¿¤ujëáÏO)t‹{ÖtÞÑå2[:ãtðñ›´éV‹ö]ÏœcZ?~>ÍZòP÷š%zÍëdÐm±:2º®ã%¦_»OÔ΄ªá>ýÂàç™v>ï[¼¶Ê'M?è:Ý«;ýòE–`ziYçââç^^`Ÿât§'ZÐJ§ÎßÂ*\ªæikâÝTÈÆÏ* »gØ:‡Ì‹¬ÓΑӒ<æÅy̺îB³Ê*z¼â‹Î×R1ï·|Ð\…ô±ÅÛKk\fÝ8»æVÔq 7¡Ñ…†9šÊñ:tf’};Ùžg+;~8r­c!¥7S×2À´s©X®^»£k\ªæwiâæp:Çî-Q0TH ®ì[Àê ûZ1ÞQîd)Ì]s¦§›Û…øA½˜3`<ËFê<¹¹tb!UŸ3ûÚiï«LðÝ£e¡Iõ_”;UÍ'×Ä:nÒÀ§2våSzŒ™Ço°ÌÙUH]ÌOéndÝ^­:ýRBÔõ^ñ^C­)‰õæÉAwò§—sáÝ£LS¾Ò¨w+’gÍ’3íÜ´ããçÔïvñ‹*:·?çP ]ξºxf•³†™¨ORüþëöú%rörƒÅŠð–„“}u½Þë¨É~Ðéuníì8ú 㦋7¹TH'K†©gnb=|o5;XÑSŸÓ÷®>X÷·ùƒRèâÝõ™¬ôcçÆÖ;=îf!…8ô£D?˜ñ~zV‚_ì::w”3°â?OøâÔCw.îfÏ}Þ^H&V»‰ƒƒ™v2 ls¹ãZŸ2ëêæÂGè.›Ñu¹½¹µå&¾R¯Âô):Æ!Lðÿ¢ ±yãü|œ‰÷秺™Ü¿ðfÍ&…oV’Ånãæ‹sB˜v¾TûÔÛ/«}tæðòsu¾—Šù9n¬ìîÉü›ªBÊsšüöŒy(ÓŒU^'¡QS»:›»š¬ì‘_æçGŠ þ=ãýL IäûdÂÕâP¦S)øjTÍ“ÔÄ:­¯À–©K’"ŸÒþnú.›…±šKi&—ÖÚ¯ùÛœD t¼jzÜý|ŠáÛBê~Iþe{FÓž^õ­qßYècø8H¡ã\ só(`øÆýu+ éMžÕæ¥Ó뮇—5’Gn¸V˜#ËÏù”AÇû=¹S­–ŠC^5Šhp‹ÑÓ^¥„3­ÑÅ‚Ë%j¬#ÎÅ ™'?·TÝW¯k£¼ä>tZœ×sAÃ"Z¥ ¨·pvÓÎQîVçµÃ‡CëhxFký‹æòñƒîš¼µÚÛqýàìFõ‹¨Cy½½EO#„¹ìV”Ú÷ƶøÑë«|R4ñûQ*Þv}UÅÉO{i;7γk…7jô¹û¶HÖÊJÒ>©±à‹¸žsc+ò¿OÝÉ•«¦Ï2=@Oï8ØÄö/¢®§¼öÅxß8[Jæ.ƒ ”è˜|Uw?çR ]0góÛî0ÕþQ銭gÞ~ˆbËG^z›ÒÏžœ·Ï°~V}ƒ0ŸZ˜Ç Ý“ê*óoGI÷ÆöþÍ&Q¦qyo÷ˆ[ŒŸÛiOš±º‡Öÿmʟ'éŽÓÙÜ3ñÏgQݳëVyÞf¼ßœ=Mˆýò«ö›uÄ×'~ΰ ºF_íj¿÷:EÜtOý…E¤x7býµqÑÌfuÏç»îØ óï× ë~¯:Íøà2š–ìÜtê2|Ïâ‘ µ¾G3íÜÉš‹oy¯Œw|6xº„v5Ûßp8K[Që"êxhìï™î04ï-·š[ÓÌ;3_¦:“àwÀÇïg©¸ßîOçÖ³sÄ÷çE4ãhã9ÕÆÞe®ïtzfI‹í6-[rÕYð#˜ÅǺõ+WGt­sÒÍ›ÚMÌºËø9³´"wKh¢Ì…Xž>úd7üýIÅÚÅc/×e­°d¬v#ûõÂ-èÞˆ’ïOæ¬|±ù¹Ðî{Í_¾kŦKôñ¦›b~IFð…åŒñý©%¥HÊüPcù_kÖy2/èæ¬ê÷+ü²0_¹ˆo}=Ûwœ‚Í®è}ªñKš×®ë¸.®Â<ø |¼¸ï·ãŸë¿èpÆ ;ñ¦E$U«cjíS°‘Fy'ôX ~o®´rX̰gFóñ‚nÖõ¹¥î“®ÒÛ±ÜÁ"Ò›ÜpIÖC+sèýùèC+ê½%Û4¶ÀõosËÕÐÝsÿkϽ×h~«òq“Ѝ»qxÍ^}c˜vîïiZ9ïr‚ïl'>^¿JÅVMi”—}¾Œ¸ÒÎÿÙºýdî1,Ù®ãf+rXοv¥&%C_^ïÎÇ :ãâá‡Îöº)øf‘¿×jÚªŒaAƒ¶Þ_ñÚ’îž-ÏaäJêÞ·*ŽwÌÇí—Öw/.E^}}»se4Þ·¼i,+àìô-éhÛëL¯'•Šbvòóã%ÐÍKd"äÂÜç"Ê?RÍ®ç¢XÖq€Ûª³½-¨Nði¬hÖçº×¶;¯“BW÷WÄömƒ¨ÇÌ· ê!ïÖ}2êd,ãç6J¨pë³{Bµ}#?·\ÝýôÒ‚ÿÎBJL‰;e[ËÜíBçO±^F£{ýzç[ou¬ÈíÝ'²??覜l1»Ùæ`jÒ&&±â]![þtæ„6qLð7æK®'Ó_›Ìäózðñƒ®ñ eZ­~!ÊÙ¶?-¤§¢ÜQ?Ä1µ/Sì6.¥·âSÊ&¹ïËÌÏ×ù]*Î:æn\YB ,)jåÒÑq9u;Žc7¹FruYÑðâçÎnÂ\ÌŸŸ tä¼"cåŽPê¾òêÛý÷ )#¿Ùªñ9qŒŸã-¡ŸýsÏÕ²wæ’vcšøA÷éŠNé0šýI‘$I(¤eô¯?lÏ6Yžêõh%µï[¹¼ÈÐ úå½í§ÑI ;Úæì™+¹a™0p÷íBZó­c­OvñŒ÷o¶¢åמz“î.\‚Ÿt#w_‘º‡Óåêo7þ -¤Ê9®ª×·âÙošéox܆®+ˤ]_»çZÞȶ†F'ƒNs™èGŽsì–ôëøžœ­Nó6ããÞ¹k»Ú ~©›„ãÒžt“ޤEGл«uÒÆ£¯úúäYà+ç¶Ô0òÙÞ_vôëmû!Ýã7 ןˆt·ßéwø¾(’Îv÷Zt¶v]ëæz:3 ?ù5»Èž¾ëÛÑ›(·Å ØB=Í÷ÔÑy&žyìÄê _"éÀ}…2õx!þyåòÄA‰Lðõ¡c:´¹ã&ø¦ÓèDÐqQÒÛE.g?–¢ß<›ÔÒh_"ãýmIïð¤´×]é¯>(bèêþîlŸÖõY û²p_!¹”¤Ù,z–È>Ü?Û`þz9–›¿A˜›;‘t»8{Û·ÇBÒÉ]¦(2JbÚý¤Aw§ŽY¿Að•ÇǺ8ë«’ysnÓªÁÒ±w!m©ÓjÔß$–Ò¢¸´ó]+ºÖ5¸qpWaîü>~ÐiþüÉm:vûˆ_c·BÚÐ=_TnÓØÞ–cuÆïTDÐFâ}|Gkt ègÕµïìMñãn,/^SHÛÚôZ!î©d¼¿”5M[(ßãì.ô¤Ñ©¡ãý¾£©·>÷_ÀñÄÑ8ì¡dڹÚ2yp3õ× åŸNµgâö¥’¡Õ¶Ý!–=bÍð¥…ä7Ky¿0OÉø:‚ºÜzïóA2ÁO”?ž"èV:W^hV÷.Y~ý¸2fN!]Êiç?,™ñùÍžzŽõ<;y·qî†MvóñƒîÎï}ͺï¾KbÍ‚¾êù^XSãX2[w}ŽÛ¥£ö‚ÿåf­ß?èîéámÔ˜ÑâÞíž-¤¢Äùmç|Of.?hjbO~¹Žf³à{ËžºêŸ¾ÿº·‹Ñ¹Ì«úRîá­6ÍRo;j7aÅÄ%›þöûdЉÌ±ÔWþâ…غ—Þöð6ÉŒsʲ¥%ýû¾<¸IXï ~4ÐE.ù™×•´sLG›øZ…´é·áÜ RÙ3sÎÈÛšžæsƒ­Ý…y˼N ÝÕ>õn¸)hkúud¾º|}ÚÀVæ©Ì¹åçиeV”Ïo¸ýízЩþL<¶Ï‰á]Ã4gÓ¦që‹ (lê|Ïž©,³ÓY¿Îu#㘹ÊÃn“øøA—‡¬ß±\A¢Óõï¥ù.•Õ÷êqê ÊǪ¦©­YwQsoM>bèž'q²1ÔSôÆSV@ÕÔy!Ϧ1Þ÷WB½jÔ ][Ëú†º¯UÀûVI Ë?ê8*Ï2†$Õ¦·6’PTÍ‘‹Ö§1í<öTŸO͇OØDõŸ’Bç¤1”‹!‡o¯¥¯vР½¯/n¹Æ´þLîšÛ”¸³Íô¸rÞßN]£{‡?È‹¡À±Ëܾ»P¿g¿Ý©q¥FÏÏ={Ü’žl—]MJE2®â}ÇÐil@šÅRänc¡€Îät‰S›ÜcÚ}Î%v°D*Ì#æujèülû¿?3–zkõP‹»ºÖŠó÷X÷©}ÆÙÐ\ybþÚšRùhÖž£«?½ÏÄ=lïè§ìˆ%§ sµ3 ¨zµMÕêüºÇ3âvF}ñ ñíñ;«ñ:tÌfæÑ„øXÒlêa—Ú©—Þg7ËûµzÊžZõú½xi”‡¶Ÿæã]гɿcI†î]þ;Ÿô\Þ¶­{Ÿ•Ä´¼½yÁrê<äåñ”9ó^iíñBü 3òúKüˆ8ꤹóÉ¢oÉ´=°¦ßkšì ž%~ÚÒ̓z]Ýíy<ï“%…îÑàÍëëâèÜ;Î (ŸúbõÛd烪õIu˦ýž6ÙDüu:—të7ª¸-£e™>ÞsBóiºf@òƒªõ–iNnÁ÷y-x¿@5tïF½?ÙWOãÒGI'zäS¿üØÑêi*Á¯Ï†v–åæÌú½‘Ò;ô¿7^ð³¬ùLÌ]íOÆ }|>-³6¦³^Åx¿UZÅÙdt§u\3bm?=è¸.ñȾx4¤n±ß¤|êí7¸™LÅ~æïk¾ß†ֵ¿Ú3yêt`ƒOJøã"†îÍæ«õÆÇÓan|xÏ|ZtûÀ4㦀·ocK{Æ<«Q}3ånÛ¹æWwÞ‡OÝÖÅ×c¾Ç ~3ù4ùBÓñ­¾ª˜ÎïC½f¶¥Çݸ#³™^í3l`êÌO)t>s¸ Áú…î¦íÛ™@ ºÜ x˜G—~,ž±g[:«™Úd ŸiÚ›k´cÂY›‡Äž:Þç;"©UÃÖûòÈ!f×]ïÐt¶ë*g0dKƒ?>=^ýØfâ×=ÂõWë™X]Ò&ÝD²^²fͼUyTëå×G^÷ûí,68¶‰ÒÇu*ëËŸ×"èÖ}¼6\œH£-vÔ94#|ÞM>ñI7ƒÕ?ÖÛÇÁÇŠJ$´_ýÁ]ðWžÆÇºJ§ä5—×&’Eα'ÞêXÝ´_k{w뺒‡d¾Ê0Ëb«;•p¶íùÏ“@çqå̰WiŠÆà'²—Õs˜k’¡õÿ¥Ù†VõrŒÜ‰[•vlÄÿ>)t¼Ï@"9Ž¹Ù°EI.]‰³qZÁøülN¾¼ºxÕv§ÒwCWŸäÏtÛÈItç‘}RC–Kšeãî Æ&ö˜vã‹%ïç>72«¢º¿œƒºOÛ¾+oLI¢öχ>^z,—î-¨1üÞå öãÇuØ43×½õðúînT·Ô@2’¿ŽÔÐi.¯$Úô¶æ÷@—\Šzèÿ;6ƒg¹±ô½fĹ êíw£gOZ¢Ã_·:µŸ‰K{{Ê K"î*ª53—f˜¦g7x”¡õ¥-'v÷Š‹t'ÞÏKð†n…õÂÅ“^&Ñ…A†‚»çRpµ¤yòZ_äªóEð1æãÝrîö@[%‰w<8%ùC:5ÆJ´Ï¬ªcZ_jÞg‚?.èÞ­¹T·p–’ ‹¹kÍ쾪E¼8“å$ô0Å’Þç|HõØü·óZ q« êy)é‘éÈv9t9…3”ÏdÚ¹û§Þo^seª»Ð_óב ºI¯›‰+É]1Ê:e{9{Ò¤2Ù›Š·û¾\µ¡ç{ÆF'm¤ZTaýeÂõÝÍ^%ͱ¾q6kiœVpÚán&k± W@À7 y·SíÙ{£Ð/ ~¤ÐµÐ4zÉ´ ¼P¯dH}èç|Uïe&Ë›˜^££ ­¹ñYÞªž«Ö¿Š_gâk鲈’©ï¡é3ß4È¡‰¿]ë5Ï|D­é‹Îム×ýíüAç;%Òo²S2Õ4º³ëKI6ý¾6ÈjýÈ,–×·ºÕæVôq]ë_™ ×҈ꑋ¢M…øA·náúõñd¡>f“~»” 3I›§)Däê®WýC·µt¤ûEYá4ÁOºIÇLV&S;îv_6eÉ·,võÉb¢£FÄÖĹì-YGæƒiÞ¿V Ýíz=éÊçdú¸èt vÙ4ø× á+.e±Þ7 â÷—7PVo›ÖOL…øA7@ch“Bµ9;àQÙ”ñªÖüE)YlâÏ&ƒm-ÉâÇà·÷V»RóÀàðž„øA×agõÍg¥ÐCΞ£Y6ÍÿlÞäÇ»,¦õWéöºpÊ„Ê ‚¿¯SC÷.õØ›z›S¨}ŠI‹¹/²(·UÝ•Ã[eW­oùóuƒP?„ú§ûLðõM!ÃèO£îfQpþþ—#)›ñÇßš:lùÑbàâÜ–[?â?OÝìi.½·d§Ðø›Y!¢CYt³¶óñYvÙUûD¼ßêÁ¯•׉¡ë¼§ÞIÇ©Är.N*³Ë¢¸g}íWìÍf!ǸVd8aæ²h”fc[¸þ ã÷'R©a«¨Ë×FfQ¾‘nWB²ÙÞë?êw±¤i‹ß7Üú`=ÝÊ £ûTÈŸÐmiÀuì©äñÔñÔ†YôúŠa“”œlƹ{¶©Ð`n»¨é:͵¡|¾–A÷móïg ;RéY©üûÞǙԴßÓå_²™Öß®¨ÏÒ> ö»/“T»’Ó3¦å0Î \|g‘pŸÏEð±â]ïæ/gŒ¤9Œïï‘Nõœ«æÍœ…ûѼN]ÞI»qÓèÚ¡Ó/º%fPÃK•¾þ9̰1·Ã°˜ª7n Úd°Fë¯ÎǺВmfD¤WÝÏÉ ÅöÜMr|Én¯uK¨Ã¯ã×g7^#ì×tVGŠÛ䥑ÍPe×.v4iîöîÇÓsØàé+¯øD/%~w },ñû´\ðs†îrÈÁ˜Ùp<›q;ƒJ2GíŸÃ´>c1O'íÛæ,A×éùOÏ“†÷ÈiüîÜè«é;'r㘥øO7Sݸ]^,L¨_9‰Ž}ýaܱIbHáû°âç¿ÔTyºmH&Åà)Ùc&-üiba¡?ÔH{åE¥ë»¼j»ÓÑ÷ªÊß;&˜údÒËìùMCÌT—vîaa÷û=±÷Õ¤²ºOâ¾”ÐoÐÙðÖÖëËLêþ¡½„Ÿ&nc)‹9[0rÙúQ¥u …~ƒnt«¨Y3©¡½ ™v¬¯º¸U…•îw–î‹$9­­:«X7¡ëùùûa¸¿ÆyÑï{zIÕ°ï,LXïš"Þ*âU®~¼&¾7@÷‚—oI#Ž ¿ÜeËuz–6?혅 ã䔲:söòsEÂ÷¯‡.ó¸Ôïj&=i7O—ðÕuªp´ÞŸ®ZX;ÛÛ-—Õ>d/wÔl’¸n/ÜÏF莵ØùpÈo´úhÔ0Ê»F>Çç*=[Xx{ëGO¦C~׺½m=™ö;n1µtVè*v2E>ú9ƒZ}Áj\Ö]£•Ïï]´¼ ´3õhWùþ”,O:ܽF AçP£PÞ[sM—õ ƒš.á‘t×húÊH¶a~ñèÚ=(oáKÔí1$ÌÏÅ:ÜÐm^ö¡—æZuù¡½ó÷73éÁûM‘õgà³ãh»cŸ›ñ¾IØçîg9tSÛ¥KÓÎeÐ`{À_&µ¾|{€~jýÜ<Ç»Çõ1Ô¢^»ÌÂ`Ù?=ç*褒mæÍ ¸ý²!ÍšeÒ²‘‹_ÔŸX@Þ¶a•Ž.W–Ö­¥Ù‘éƒÿ&ø‚:¡îu]5Vè蓜AŸZîëã8º ôºèyïy÷Úõ¡•ÏmËÏŠï}Ðu³jdÐÄEszzLË Ê=–Tÿyp=™ºåjýùJêM›šÏïuY7¡ —Ê ÙÛ{6ü,ƒøjľb=û1$ì§w¥÷£2uÙ&>Ðí]Ù)Ï<9ƒÆµl¾vûátw¿Rn÷±nûxq=^"ÖKßûjÊ ¯êïXfõÎÚ-ŠtJJU¬sm_ ÆÛyPㆵVùWpeá‘.®úq܄õnuÄuò2„O®ÒùH]ýæ¥u¦içï›}•ÕŽÕ5fæ_J×]  öú6Ó¡a½é{pź«ù¼êœmÒÊžì8¹µ§7- ¿þÍ#I;¦µ¸‰ë.Ðéj6•õ[: ë%WiTÞ»Öÿ¨P@IC1$N!¡ÎsO&ÔíÖé´Ð]ø.sãøÜtZêt³Ú+$YV­§ÿ¯ùeñ4 5Ê–:nm2è– ÓCÇgA·’ÓiKß©¿Bu·wÜŸŸO‰O+¼Šh7µ´Î'³O?¤Âu¡+.™´½ñÞtâ³ï­M®PŸ‡a½näÓ¸ü²ý>¥ÿd|¯µBw a܇ Útjh/´e"ïwúù¥|²O²&‘ý5®é4æe߈tµ0ßáßw:v˜ó“× íw¾ÔpKB~é¼V¬wêÏôxkvŸ?èüÚŒé\Ü÷‰}ÁÀD9ùì‘Çñ|jd/8 ”¼ðÚt±~·p¿È¡óÁß Ó)«ÇÖ ø³iÔ(°Õ Éþ|’Ϫ×ö]ïñÔßü£‘ üÙʒȯ|÷§ º7{«?\x•4ñg»ôñMöS£òËöÕÏ/ï0pÑ—5òL{9à pŸi¡“ly6åüUú~GsµiÄw벿˧¼lõÏTqHÅš=C¦°¾Ï¾­¿R\÷„nËç]å‡yµ½pñeÚ|n÷ÒGßä‹û)žÄ£­Îù{³?®¡ ûfKÅꪫt1²ùÈ7þ—ir»©_ü¶ ¿Ì÷beÍ}Ýe*ëöšy¯×] «( {Ó¾ëU²‡yÕ»L½b&­˜‘Oéü5µÒdŠû½zÒ q@ÂóîP»P¾áÇÉ®’K›¼þÍ“/QlÚiR~Y¼ˆ¸¾ÄòéÛ3±ÿ êQ_¡v}û¶ìt‰þÞñ̬‘ùÔ`î{ŰEžts@÷s+-3ÙËa[—ôæÕrè¾}ñ±æŸ®Ðío[ìÝìó÷«®_¾ø>à!։ɞ¹·ùx¡¾¸î]íeù%K¯P­.S^½v‘tŸ˜¾÷—ù¥ërâzÄ,æsxsäÕ»bÿA÷¹¦•Y5î %†Ó\¶ä"ÕjõÕчó‰ï2ÖÝ;†6ÊOL5²ÂJ|'E\÷„NØÇ¸B#_¿wsít‘Ö~—zt}ë|²oÎMMÍYN³]粓mF{¼-èŒÐíkÕ*«ç'­âÓéÜTŠœó²W‡æùb¼Ïêbp ú§~·B× `ïî&ñ=,•vȦ´éÜ0Ÿ„õšábýï ñ=^ì?)ž?{u-ôªW}É Tº’¿–Jš_çåð bÕuAlÑå$ÿ‚dá~q.©Sêòsߘh«Œ¿§ÐÕÄ’f«æSãYõ·d<)Öÿb',SCG7trè&­šö…z’‰®Ì|ÿÍO)ä¶=0th…|ïšÂÃ.1E²m—øüA7;ô@ɵÎ&ÒýZ¹c+U Õ_¬nñEqý ?4;tñ*Á[g‰ïsâ¾tc³+yWÁDûþÑìâµú)¤YÔðEÚ/y´å‰ùÉÂ/=hnç¸+&ÄÏŠóNèø[RÛi”³Ä»IûËÉ´þãôwóòÊž¿mï‡ä˜©aÂ{¥ 3B7ã×Ìž_J£íë‹ápÉ”ûKí7ó¨û]'tÁdº¾?ÿ÷Ñæ›üQ[å3ñ½:ëÙwš+Áiä±cÛŠ¹“©Õî{ê¥å‘=ÌÆÛ[¬k­aB? í9ÔA¿Ù]Ë¿OqhôÐH²”w yb]qoêsö<`F°—%®{Bw(næì“•Ó¨³N1ò|”‘2ê%?šGMC®îŸL‡C5ššç±?®—Ê¡«où%öêe -éçs@i¤§ß¾\ûÅî¼ÒuøÒ8†jO] “4î¶ö2Eäx½UÙHÜ7ß‘'ú¡§8ß eñüø@ñùƒ®ñûÄü#C.ÓÉí‡$šEŒŽM_räɪ<1þʃª?z¶û¡ÿt¿è¡ãQ‹[~¿D}–üàÖÿi=ï1j¼vam6ŸÓ4ƃv%çvʲŽÿ-·Ž8îÈý“—œ¹D*è—DÇÍc<_ÍÉ£ª<\¥Ž'Ù—û„°ˆÀ—dËÅù t©‰i?´™}‰Zïb[e¾@CFi>töÍ+?sž)Þ잺€‰Ï¿ÐuñþÀé0Ž%{æ¾@žï«…Ý›Gï÷fé~X:…„}æùLˆCüÝ:÷zƒ÷1Ž uTé×ôÊ7²ûç‘ùVròƒ‹**9½î“9c>æiâø ÝƒImR_bâ)±°¤`Z—<²ocçøR˜¢‹|_Õlì€&Úš:áþTA7¥v›Kim/R”súëáÏ q\-ò¨ÿë10Éÿ¾†CˆÏ">ЭzÅ5RÉkìó¾iCÎÓýÖÁkæ‰ó]_º¸H©\Ê’V[6ìç/нÍS©ý¸Œ~朣½|;胕„¸CuäË.7ÂØ½:£ªOy-ì÷¡ÛÇÃñ[¥Rêyï‹aÓÎQÓ3¯<7þl¥Òõ1ˆM ÕéKaÙ yû$<#…®äýgéȸ…‘‹ïYËêZK®ÎùÐüMãoO{‰ýW¯PŒJ¡ê}ÝuÝYŠ™—:LuÍJ Ç;~ÝÙi2Û·MzìhꟋû~Ð û})´aï.õÄ–g©Â‘è £“­tã~Hχ{¼¨ûîÝxs cB]dqþ]Ž9ó“ül2 ŽÚçݘ%P“üZëv¶–®WÒÇ¿ÕOû&L\Çß {þKäC­O2 {nX›á@—Ìú‰¿¶’qkƒN?¼è#/@Æòcéš¡ÊÐ CsÅsWSIˆ— ú ?ŸÙ\]yõ±xz~íΜ†;béÔzÏE®ír©ÇóÌ (µŠ¾‡kÏÜÊjxÎsZŸ+ô· º-'~X›Gƒ%›znôŒ¥–Ë[\\U%—´Ù›]wãKBüXˆ¸+ÄUi¡«™›rîåóÓÄß®ÆR­—§÷V)xPV<ÑÕ/îýò&Ä­´ú :{xdƒÓt¬É‹²OQŸÊÓÙÖóh†ÄyÉ8š6¨ˆ…0a½ÀYè7è¶j}dD—X²oÏí8EŠ×.tØò€„ÏãO›”ݬËaÂ>[¡ß óÞ8ÑeÑØS$œ§8EQCöž£~@¥qN<úäΧq½®ƒÐo å·“´SæÇÐõºþ•ÆËNÑ¥¬A= Ô[³µþÝ>O»Õm•_(êŠ í¹@g?ö¶Ñ@“ö*ÞͯvŠê¸æ:lt~@ŸÍ(z·>gEú¹gôÇP1¾ÛUè?è–ëïÅ¿s’Þ¼}¼¯eA ½lX+·ÙëûT9úÈôÀ¥¾Äww›(ÂØ?Å BgëÂ+§§½õúWñI‰¡Ëymgzeݧ­—ÕxWÑå59éñ7BEî%ôt‰•÷Hü‰êÛ/(††™Æ\;uŸÝl]à|e 5ý~gôÁ³!ìÔ7 bf” ý½ öÃ#d^ì'qØC{'mÜ{|Û} »WtþI¤-®bHÞ¢LˆÓâÔŒÐÙ—)EÓÉ_ø@CŽ©Žõ»‡Ý'!^Û§tŸBôËBÿA×üÁ|ÕWýÑø´ög,Š¡%_¶•OñºO#‚–wjÜu }j»üÝÆÆó™O×U迆…rûñ°YÈ‹¿ôºO:‹*¯UæT⻸îÏcöm±:bœ ~>s^Žmמ}´Ö­M¶Ñ}⣸GkßÒutV/«ñÊQ!~NŽŸ¯Ê#;õ4}ã#Ÿ7ø<§F°‰oîÑM_¾1;Vîå§ÁL˜w ß» º!ÁMjZwÃÈ–øþNZfmý=åÓýþÔ-o{ÉÂqÁ쥺@WÜJè/è„õ¿4e¸©JŸ14Ø-~Å¡ƒ÷Ä8Šé$ø¢†]Ë›ô‹oçVdï/èì")B_Ü¥ðD Rf¸XxOŒÓ wŸ‰i˜·ÜÓ®3B÷z¶ÇÒñ[èFbAýfWpµš4ÑŒ¸GuÓá(ä´aØÓJ݃™p®«›]g….öÙPXÚFÊXÈ_ÈbèÎ ²mjq„ÕØô±C žrëbÅ©Áâû}»ÎÁ¹P~ý}»Ž'W“wвvd¿õúnÙù]á|l0skgŸù ýÝÍšõÃÛîYF äußþÒêm9Ò6êVú]*8|½V‰¨`ñܲ‹ÐÐÝص::%ˆ–]<é™:è]4}©u=t—JãÙ.Mì½nþÍ`&Ä–ôµ÷tÕî<,Ú¶+Ì•wšsŠ´‹¬.Yq—f±¸z­[úÒ©š÷¾éõ(Xœ×v°>-t²QÕ^5Ÿ1‹)WPo;E™\·ó½K¿‡Ì½³áæÃ×w´X>3˜=öcø5¡ôЉñìxê?&œ;ŠÎMû9IBw©t?ï-’ñžg?è-ôt-{ùź~ËÒjµnVpŠ| ¡Aú&w©ô<‘ðÜÍaBœ\è?èì¯37°>ÚA˜"ÅÒ–ë«'þòæMl;Ãíy¸ŸÿÈœ>îý¡ím!Žß¡Q¡üXïwìÛÌöµ?”5ZKsN¼Ñȼ#Žþb\Å,&¬“ ç \ ‹úmô§ç;g í/<ð¹^}ã&¸CÂy¡é$ìÛ²;!Aƒ¦­f×É¡›Ùœ;yÖbé\›ç).‹î”÷Üï•t[:»Ôÿí:tuRkùeåîdÂ~x,yÕPw‹RÞ¡Ò8€û<µál&ÆÓýݹ>ûÉ÷0aß:–’kŸZâv§ìœ¡ýødH Ë«9s¼Õ_Ðé¡;Ð3x°—Ù·5ž¦Ó~9Rèx‡>ªeÓç&ùÒñ}<d›¼¤šöe á:ÐÝ.ù|oÇÇûXÒÈåo]¦UŽ…wòr(£Ú s{Æ©è÷ª·§˜É$ôÿDÍ7^W sW-HEÀ f¯QÀ $0~9`.˜x‚`'2 ÑT^·|Žô¿gŽT>?úÿ~ÄÇØçü»áz%ÐUàgñy€;*uE~F ß/bÐR8ò³RèoPÜ*ñ3;¸÷€ù/¨‡ûgë»Ùþ‹¼ÛåµJþúZ%¥y·ÿlþÈòœÛ>ç6ÏÌb=Üÿ‰ºn¼®3L] tÀŠ;L^ ôÀ¤0|ЂPÜ0P(`Løja80Ðæä˜ËÿÑyRùüèÏü¨| éï1GúwÍøø.ö'ÿÌüÿ{‚`&ЀèŠüL=4¨”@çÈÏvã³w \êJüŒ1¾û¿ öퟭãVô‡ÜÚåµHþúZ$¥9µ ¸ÃlÔ@,@ ãQ-HEÀ­<§ö;§¶èE¬wû¯Öosy{‚`\4 X3æ?J FP Ü1R=°)ÌŸïfjAÂæCüÖ)ß[+ŸiÊçEåó¢¿ÏÚÞµb¿ñÏæVç½Â}Ìüz*òüK¸¯Ø*ò<@xÎ@0‰ƒ˜ h@t%žåß_çöÏÖlãµGÜ1ªËkü?­=â "€I4 Ѐh`Î0$%Ð#(î0(ˆf YÉA80pyy‚`L4 X3ŒM tÀŠ;ŒN ôÀ¤0=ЂPÜ`‚*Ì@C”ƒp`6àƒôÀ$š¥ h@4°g˜§è€w˜©èHa¬    ¸ÁhU ˜¦+áÀlÀ&ì "€I4dЀh`ëÜþ«õÚxÛ"àsW(`½„°¿'ˆ&q  ¬À“žWŒ˜/ñyFù|©|¾¤u(Ÿ/•Ï—þ>ó%™xÏÅkwÇSWà¹;ñ]òö0P)*ò’è[PÜy.CÜkÀ $•xN=ÜûÀlAÛ?[«¢2 ©ÉÏçC$µø™tè€Øjñ³ÑÐ`\Ѐh)?+ŠëƬº:üÌ"®¸c@V×ågçpÝ@ŠÁYQŸáÂuƒ"àVŸŸ%Âu38ñ³-h€Í‰Ÿ±@û ˜ÄA]4 º!AGûä•@çÌc¡Ñ>pÇ ¯nÄcrÑ>Â@ @pƒ!¨@0 ÌAÂØ€ ÌÂD“h2 ÑÀ œa$J FP Ü]Ð>Ð Âd@ @pƒé¨@0 HÂØ€ ÉD“hN2 z`R˜•hA(n0/ˆf ‘ÉA80p±y‚`MN4 X3LO tÀŠ;LP ôÀ¤0DЂPÜ`*Ì@³”ƒp`6àóôÀ$© h@4°g«è€w­èHaº    ¸Á„U ˜†,áÀlb}ÛµN¯o[ Üaîj  …Ñ+€$€"àãW(`Lä € ðÌÙ¼šB0ýa¾$w(Ÿ/•Ï—ÊçKF‡òùÒßi¾¤ï)“xm2 Ñxþqü> TJ «Èó`ãów \jGžß7bSTâyÑÿ è/¨m«®Æó¹¡} Å@©¨Îó‹¡}PÜjðíƒ"àÖ€ŸEûÀ $ ùùE´ ÀÖŸ£Cû ˜Ä_4 º?W„öaJ FP Üaj  …9(€$€"à³P(`‡„°‰'ˆ&ÑTd@¢8Ãd”@Œ ¸ÃtÔ@,@ R-HEÀ †¤QÀ $0'9Ѐh`Î0+%Ð#(î0/5Ð ÂÈ@ @pƒ±©@0 LNÂØ€ LÏD“h€2 ÑÀ œaˆJ FP Üaj  …Y*€$€"àóT(`©„°«'ˆ&Ñde@¢8Ãt•@Œ ¸Ã„Õ@,@ CV-HEb][ˆ&Ѭe@¢8ü•@Œ ¸ÃÌÕ@/ÖµåÆ. ¬ÀF¯:`ÅÀƯz`RL@ @pä€WG‹æ?Ì—¸Ï—Çq—Ï—´åó¥ÿkó¥¿ë\ÉS¼_øwÊÛ–ƒp`6þó¤ˆ&qP” ˆ®ÆóÒ¢} ’J «Îó£¢}àŽAS]ƒçéDû@ŠTQ“ç‹Äï®Éó1B‡U tµx^@è€;XumžŸ: Å`«ò´A[ tN<¿ÚîÄÕ xž ´¤Ð yþ´Š€›3Ï€öHñóèh€ ¸`ð÷À$ h@4°gƒè€w…èHa    ¸ÁDT ˜Äíƒp`6àƒñÀ$š h@4°g˜è€w˜‘èHaL  À\`Tž ˜DÓ’ ˆVà S0‚bàSS=°) N´ 7ž D3Àüä € ¸À =A0‰Æ( ¬ÀF©:`ÅÀÆ©z`R˜¨hA(n0Uˆf ÁÊA80páz‚`ÍW4 X3ÌX tÀŠ;ÌY Ì@“–ƒp`6àÓöÀ$¸ h@4° ]ÂØ€ ÞD“hö2 ÑÀ œaþJ FP xED^õ[,˜'©þóÏ=ÿÒ¸Pþ>`g“õ‰y4Ttާ<|šJÿýëY#tgäˆõ´zQÝùEN|ˆg-iĵ´Þ;+Y6+qÁV5 uú•éy3.hgO×yô0{TuÕîiÙ§©Öá ¯×äô³×Þî-ÖuðcB›AB>è ›[I&ʰÙý'ù™«ÆÑÝâzß× Ì¡‰-yEuZ}b ¾¤Š/êsˆù x{_ýã\bcmq4³µ_àìG1ß·ŸX7Þ‹­iÔxt¥àQB>èl«Ìý¶k ì5O?æO{nŽt^yÁB¥u=6müi³ÏL/–;{¾ß¢”±B7)”·—%º|Þ#†Mûæ¤íV`<­:×±ãV ÅÆÔ}uìÀtòƒLf_öì=véc±¾,tŸoØsùÂûÖÄžˆ1ž.¬½¼¼Í ‹Xo$€f‡/:Øa€7{àÐ-7y´X˜·g¯çtнißµ—KÕ3äÛùìÛ£½,t%íyÅÖ•(kÓêq……“Y÷¯ûn¡PXÅÛûà’¾bm,3>±sì”3´:¥F×¹µ-eõÍxñŒÁ“˜PP¬ ]HHÚ´ãO³à«÷–¸ž;CWÏþøCámòú‰õÝ/ɲøeò–$ÐT­¥£Ï®Ûä_Xq{Úß²ºTÖ]u^H¾O+tOŸùk¶%Ƴ¯ß5ïôÕóªÝ±æÄi o‹õIq¿lW&çUÃ|J¶èzIø>šÊ¯_X3|ã6©óêyû|ÎÒ+ï^ýÞN¸MÏVm·Þ¬2F÷þ­‡×þQìdÜ»#í»‹Ït5žN.\à•Àt¢ƒ~¸q–| Ëv¿Mì£ýèÕD“w£ñÃÙ¥ôg÷mtrèêW.øc«³,íõªÖÍ•ç(.½nqóª·ËêÅ ùL³ö²Ž‚NÝ {⫳,q²×¾ØŒsôæ`Ý÷kÜ"!Ôt1Ÿ,‰y¯„zÙZè„z]çX'EÒË£ÏÓü:Eq1·Ä¼›eu ÷IŽÜÍ™(èôÐ V RÍ;Ïf÷ È9OãgfG®¼E¥u…<ü½X`{^W¬Ï ]Ó6XÛ%²ÌÍßOýÌ/‘]«Ñaó„[Ôô¹ë™6÷ˆg£j~°?êu¶­Ð}=<¢Ž2/‘½¹ã³A¯)&Nrk…Û-šH=¯l*žNŸ¢nй0˜Umòã”véb}õf…òºu> 2G]`aîÓö8¬»@«G5šñ1›â“êô³þT§?ÍÄÖ.J>üÕB¡ž» t–Ú1É)C“˜m8O•D®Ã =dg‹ùýý¨ËâœÐ;û±¨¿trèÏál“˜ËõN¹%†$Ò]PC ÙTZ÷WÈ×Ô“-o0|ó’ÖbÿA'ÔOel?/w>„QÛ÷uØ´ï²i©=á/ñ,¤õªµdîÚ¢½‡‹ý]—¬õw>¶7²ÖÛ’¤ó0S<»YÓÙÙeyW_®×o×W*­nï?è¶Ž™î¶ÒÈîýès¿‘ží7±ïðlzU)eTÌ,•8.õ¦nºÚÏŠ; ×i„n§Cê¯ïY¸Ê!oÀZ#̘Ò¬}6}÷KœþÝe=ë=á­O?:{òkÅhá{±B÷iù.wßîÉìì^¨ÈHŽ Cßߪ’Ms×wmâ¿Å—¦žN«þ¤HNB^fAçмPîkOì”ÌJú›þÞ"™v|Û!9ªà&ýŸ3MÌߟ޶Kóþ]ì?èR—ñÊÆÉ¬w÷ƒ}“ɵ×9‡§‰7é·ÆCWh*û‹yôeyœìýP'<…YÞTMO¦CÑOf†n½I=›Ï޹;cºèÇ#©´³½ÿ ëÄe‘)¬Ñã þ-+¦PÛ¯'ûvÆM±®T€˜—{µ:´ö€Ÿü„þƒnsUÅ3ÏSX~ï.?_R¦Ð˜M®&|u“üªú=[5ƒ’t™4dÂ1ß¿¿ÐÐEݽp¯íT&äßL!çÙ9Ò¬ª7i¨vǤø[3¨Î¥‡5Ý_õ¡Òzqöþk^Z¯-•)Æf¥šŸ¦P§u©}ݹA÷ÍÛv/SÎ ¡^J_±î³Zè?è²Cë-¹ñ:•Õ9¼uvÈW©”´ràòšÇoÐÎst Ê£[N|ê%ëšÏú¯æ[6ݯ<ì"ó›zê¥j}*izÍÝÝjÅ Zºhvº6ÖŸÖ[—‘Ó¯ìºíýÝa^>~çE&µ¸3þ~*¥ªöi3éFi~ræ? úØÕÙµî·Âç“CWÜu̪›¿\dB]ú‹”»rÝî7ÄqeZi½ Š¿Å &ý{çEºµ{Ä„̘Ñ1`@  àn1an3*h“I‚˜1cÆŒ£(f0˜p oMn‚’¡ÛÐ`@1ã˜îS]…ßœûç·Î]ëœuq­ß:kœóÌîî·jï]Uoí‡'nò+¯&2JdÕ¿æNÜóJén¥¢e.Y‹¿ëûå;Ðŵ#»™ŠÐ#›‹ÍùïÝ®’ÜG]w&²IC¬‡d¹¢ï8çÊ:0$cy”#u¼”ñëÒ[â}}ÜùõƒÎWc’È ›-YòjV"Íye£u•åüöá\’ç~°%~ÞÿR~ý Ó¤ÓnIŒ³iHº›H·óKì9˜#ÔzåÃ9Ù’»äRƒ üçTB·è¦;:º$6›³D6V9Úº®9¿óõR÷qÆ-ÙQd‡5è ¬ŸZÄûõ@ǵKaIl\˜ëf–Cµþ~»Þ§§Ù±«‡Å¯tžõ»ô"‰iltô’É‹å»?j’C)+Z7t¥í}Ä{nµæÙò:t5f¹›«ú&³èn_®<ÞžLOµ/•dSÔèn¿™çBÛ"âOª2’ßÉ. ƒ7ò: tüܳdööY®ã¢FrzW3Sz#:›zjŒñœ‰ŸS8_ð ãÏ èÒ\Wv+ Kf­Ê·/¼UNÊîZ7m˦Zß_an4µP…:ÏÐÖº¢úWd!’™½Gù˜&)aĺ¾—dïCà@žs[6^0r†ÿçœ ¬t‹M[6‘³k_c‡ÏÙ•BOß»-Ùa‘M);§Ê¢ì©|zÏx¦³²WÜŽç*¬t‹[}÷ê=JÎêmŸsÑM¥Sû;ÕțК3¦_$TëÛíú¨bé€Ïüq­ÕC-ª×þë§±rvù™ãŠ ûS©Ÿ²rùÒ¹~0k®Ž’PmýÚ°8ÖÏþ¬pþA§)LJåŒ÷µN£ÇU^m{*%t»¹Ù”_*é×õò/?>Oˆ ›Ùaõàr–çPm\u,®ô9ªv½¡ Þ÷ýÙ1¿ê©R,ž%©7UX?èì"¾»¯|.gëeõëëtO§'â&GýCTq‰ûàTë»åÅêq>ÂùùÛ_O77Iü“Òiÿ¬N«EKÔþlÇç¯; ó¶íió¦ûÛ{˜óºpèzYŸÑî›Â¶DwšbÓ9ƒŽÌ-hî=FAW×ç»fþíDížçÔl™ï øò:tü¼Ñvƒ+oû2HcGÐ^ñ;²¦Õ¥êÝŽÿò9•Ðñþ´)Ìr}QÄôÆ™¤ߘEo;}”ŸWϯƒtç­´¶ÞžÂßF é¡Î$ÎEºuÛ,:q½áæÇ ®´Î+÷äk{Á/˜?>EЄ½±xwÉǾ·çu"螘/I-S™¦ŒwË&çÆS,o›NºšÁè®ä™äÜÿ»|z&±c‚°~Ðõ™7á“óSÙd«žžOgd“¥Ø.êä4ú°­Ùóõ¤¿ë?Ÿ–ÊxßËlY“¢=7´ÖœŸ¶3Ú•^]à Wj¼Ò2´mw/~ý ú~%.ÝSYæ Ÿ.7³Ékʳ¦5©á)õ:”îB{¸@.ä¿§^á«R^'ƒ®ëèã6F¦24qm_¾È¦ËœÄÉTÚ}ûÐâc»é°mâ“[œÉ¸00:q¯SB§7-KeM¸Ë˜n9ôVG›š’JWµ“Æ9‘±ÑË%öŽŸpÃëMœiõR‹šN½àß9/•u¯»¦ù¼ºà»þÏ_ßSˆ÷q¤ïu–õ°§ä㮯§ çuÐ-ðü¹ÄÿY*›“¢%NÜC+O ó=&…Þ¬ùó¢™MWôÙ6c¤=M´wfT°°~ÐÕ_’}%ÿc*ëÛÁQÒ59‡Z^>QêžBÒ÷<À^ðws¤æ#{,WôÖºãÛ_X]þ™Ê¾:%zûkåR¬cxœÞ)T3ž3³梻ù yÀƒ“Býƒ®WÕ„îꥱ¹í·ØµµÌ¥•KÉÑ»LN}¾Ï"¯þ¸¾yôü`”vv¾z²3¯ ‡.¥KÚݗЕq6 +s)é›Ï·çät{;wƒÃQ˜oïF·&zÅî-êtcîx–ŸÁç4·—λ•KsûO{»mœœço«2Û‹¼›¼a~Z˜æÆ÷çtÜ4êö5©lÁÃè„й4Ëämߎo“çÝÚþcië{=v7οÞj‘®“åÍ¿_¦²À¾'»›äQ¯/?îO&«\Ïgힺ ~÷Rqa½ð|<è6ŒÞ×wR~*Kôr©‡Opríœ28™z«îõp£ Û¢O’¥ÿÒ׉ ›ñBòãdB*‹Ü&]ÞøZuÜh$ NN¢)æ L§\XJE]8£]7r›í¿×ã*ÿ»H kžáßÉ Çõîã[,«ªqÝÐÍÍ{Ñ¢$Ê 6 mçþ»ß‰ÿšwmò ¡þA·LÙ2¯ΣG%fùÆèÚþñâW¯©Gxó“¢Üé3w3Þý±ÑÇû_¡;-Ij{Ù'•Ýÿ>ý@G¯GôuÉì]¡ë©õ9ýÕ;V¸ÓnêŒÝ¨qu{´À¼NÝôµ++Ä©¬t‰å›EWÑõÏFýŒZ'RÔ× M¿YJŸcªn ë)¥î¸Iôü÷SB÷÷ÑU¥=SÙ÷ø“N¼zD“Ö.(Š|Hõu] ߬t£¿^4jz×Õ}¥úòçƒV\ÇmüVO·y*Ûbé:×ûšiýÝsÂCòÞîy|1úMkµ@è—ùu7€N=ªf{ýÊaŽìcj*ûsñýç ´P“zý‘ŽÁÍí¾M£0çö§ š õºûŽ »&KaÌç1y-ÓŽ·Ü’@ëŸ&Ÿ\(¥Ð™oßè4Kðq® ;¸©°þáƹ¨t+{LØ ´ÿÎnûÈínT{½/Ü¿å×:þþS ³µQÖµc>ùßY‘à/øÈ¸ç.d÷·+ùôØ«œÊë¡«ü{t—^èCøûHùd÷úrúäIñ4®ï¡‘­WzЩI“î|óø]·5ëÆ~¥} Û1AqÛ½ù´Þtˆ]ð£8êT¥íµõ§Ùë!ûr<–Q¬Åã]Í}yºþ—^¬:ýJÎvî^ÙÕGžOº«OUIâèäÌ%£îñ ?½å7.öö¤ZyÍúõU‹µ¸N_Î¥Ô+ ¬-û网QË_ãdkZàó-u[¶%z)->¥{øÚ)áüƒî‰ï=íìrV>Û¼ªŸyÅ_³ 2Ò÷òŸ<3p)í;¦j´f”\N&fÝã×]ÝF±‹ä¬zFdkg¯Òò_ü@ÜYFa…WÓ¤t/*uK*s¡{+/Ž]¶•'îÇ4Ãq²r¶5eN\ÄÙ >ù«Û½F«µm½j¢+Õ›¶¹ÅŒ('ÊjõuйAÂùÝÇšûÓÖNfoZ®,, ½ÝWîˆ{@/¿™(åBñúÌnxÆAð æÏpèzÝù¾ícq2Óšãø‡^!y­K·©íš·wü³†=)ç[Rþ‰ÄáúºŒ‘[ö§$3î®\‡q…Ô³+XvŸ†kÉæ]µu¢Ü6‡E…øœá§ÖoÜé̯tN£ Go$³Ê¢Šf+ éÔ7ö;2îÑICGúV{ŸVúû{jÖÏP-jv¬Þ ùádÖ‰³£¿XH‹¶N¹mtVІN?Ñâ÷œçZ?5~ý ›Óùxð:Ïd¤»øî U!=þóxÈ®wiÿ†÷F{œOÚ9“ìÎNtŸofEÉì87†»s=˜k²¨Å³¿haU£€S‹)«¸àµõ'Oƒìz—ÿ]$ÐkcèÐ*™m¶—í¿dSDÒ}UkhØ_Tï¦CŠ‹ }ô:òã¯\Ïß÷U4ëï_”ĸ,ðçá"z³`MTÒº;‚o‰+•-¸[eåçIk÷òèi*¬tú ãžßMbüÜñ"­tËÛK Œ›I©*Ÿk¤=~Ïk׬t‹5F´IŒ÷e.¦¨ÊÕ1Ý&‘Íš¯³J‰÷çðø}@³~ÐñþIÌ¥”ÚŸ[LãŽ-c~›Ò‚lŠï<’ÒÝ“Yo#=¨œ³yÎß_Òê§õìèëa96‰-~O¸/¦÷ãZ~·¨·[•Ǻ)ö rpJñ ˆ=s'g}äïg@ǹJué™Ä¦ÔsûUÿr1嫯>›u“FüþT¿—ôwÿ_{|kÖ:Þ_-‰»¤6ïY^Lq¥?ë­~wƒvÏš]¢=ÛUð§w§‹Ûd.3â?§:Þß>‘5ÓÜ(! Ÿƒ>šß ×Ê»†Žºþ‡n”÷W‹¥ì¦pÿ ºCßµ¬>›È&‹¦Æo±(!UAËSç¶^'“ަƒâ¦»¦Òu¦0ÎŽø!ÿ9ᛟ5émç%‰lê ›'õœKHc·œCg×:Ì{èBSª[ éí@8½îÂýOè4¶‚í™ÿ¥Š³_v—P›ÔæÎï;ÄÊòrItk)%>=½p\[GrhÛ`¯×[þ9‰:;q¹É…´‡,µ]ÙóžJ(lû¸ïÝÄ׈÷u|]éη.Ý2møõëߥÏðîù[2}I÷°¿Þ—PiE–g`4µHwþ9ð;¥éÚ>o²Ù“~L掘üúA'ÂQ¢´~ÈFÛ•^+èSJ—¹ôÙ+Š’»ÏKiµÆ“’†ì·´Êðþ]?5ëg«Üá!K™;pÝl›Rz²f|ký W¨muRéê6Ëè›ö*UæŸßþhšõƒnÃîc›;}õäÌí¥äuE4ÚÄõ=ó°Ãˆõž„b³si+jkÐÔejœðü¨­ïekâ¥k%zP*ô«‘ÔLsÁúk%™J+½èÁ—Ò¨ÑÂýkèzq¶(˜é,ά”dó;š$¼º@'nma34Ì“ÔÚ%<ª'¬tcŸn7ÈLH`õEíF,RFÍ6öôy¹ø<Ý)õ~ø+Þ•Ì[ÌÜB î´¨Ê~pÛhþù˜:Þ¿%ݶ¡ÛÒ2šUÚÚköû³ÔxO» Ëœ_G×úkÖo®‹üscvãî>¶>QF¼ïæúÒ诙Ìʼn¸njëEôKã:þ9—tü}ÛxÖÙ5·•,£ŒnöéÛë©ÃiÚœ©g¾É‘V•ín¸e ú䛜!øl~ý ã®¢OmŠg«fÞ]Ò°¦Œä±–Ç;:E½ì èã$Çßç»àkƯtë̯ÛÒgžÜcˆîåÔ=(¯Ïô7'é¤Ëˆ·k}1~ûðiÖ:4î\ˆc]|u6žPNléïFûîeªnžw¢WEFtý„ç¼.ºëœó{‹Ý3>gY9½k¨¥÷'™÷NôXöΙZ:Gìˆüè÷ÛwS³~ÐÍ:1Z;Žý͵ÁG˩ۄY]f¾:BËü˜sr¥ûC¬‹kü(ñÊFýÊ"áùtë«KVÇʘQLØÛ IådÞkáVq"TáIR2ѼøÑT |j ÄuÜV³Ç‰ž2fz!ÍÁáC9åéÍ­1½·æ-è}:²ÒöšqŽ×~¿ýß4ëÇéÎtÊʘ¯iIP–®’êuÒjèûliy¿.ô;º”T9C*:~󥲽ŃO^æÑÀZ¿"ÆŠÛsNæJzú´Ṁá[IϸÁä9w–R?Âã[õ%ÉõÐÂ󿋺n‰æ/+c0Ûnî™+©¯nô7SÃõ¤vkzµþ¥¾ôãÒ _ªoõJ•6n&¿~kÏ¿ûlî–F…S•ô9Õþ¸“Ø—tëk[…º }ˆ/ñÏ•ùçááÐ5y`øÜ/÷˜é¹O·H”¼?x_:óø¥}·JWºÓ¬Q~‰Ïï>[³~ÐehWµì×èsíÿðw¾Jš°ÖxäÒy¬CÚè²XWÓaŒcâë¯SB·nÏÞö®Íï²F…1kì¶*©bwâͲÖè»§|t&þ¹Ÿ·àÓÉûFh©E¶óÓu¼tÿb?u¹GIYW’äÖ—W3SË{V«ž;ÓßÇïÚ žæ#¿~Ðý1ØÝaCT lÂ]‘«(ýÂÚ~ï‹.°Ä)ÚÛÞwžwùR/\í¹=iÁ¯tM"Oµg‡ÆÜŒÛ®¢`»ÞnÖ}"Ùž_¶µ¸ëD{ö½nµtërº rqÍÕŽ_?è|g…~1Ë=Ëú1eÖQ yÐMûÇò‹l`Á©OSº;Ó´{f/﵂xߤ¶üú V‹ºvÿÇÝœÓìæ®¼Õý/¨ÈònþÙáé—Ø„©tdÉ=gÚ³(=«Å´•BžÑæ×ºmç_ÜôW8{w¢~Z«g§|Ž\7ºÂ’Ô£<!M:ê¶ŠèRúÁÇå¼N]«.G|f×c¦ J’ý¥"½½‚„]ec~}x™ãïJ)fb&F¬¬õ ç׺š0”=ºÚÄmwœŠhL4멘Ñw¯”f<¿qáÙñ@ªÝjߛСüúA×úÓÆ~ï÷3]²Š&i”F³‘ëSÖ|níFßw†9±‚þé+]؃Q íök¨è‹½~³a×XìøA<ܨ›Æn…pÝ1‚_?è~YíÕ·¼ÄµŠtë«¢ÀÙÅGÖJc˜q»€ËWLÝj}ÊHº€{2nÀ¯t|][Ê‚7sÆÊ*z¶øâS«Á×™éÜO;;K Å£Sá¡a? ï3§e¬=6ã2È<êÎî©T—ÛÉ<®ê:KrßþaáJ†¹Rß!H¿iAË ¯M5:èÞVpF‚+È¥ÕŽ+á/T¤­uË÷ùéìbå‚…óÛ×öåþ$ø4it"è > Ks>³‰2,{­yûVE•S‡µßdÓ^¶“¸õw&Þy9½YÄmâýÍ$Ði‡tØIOÞù:ᣊògr†À7™þ¶C‚ЇhlÚ».'›ŸkEÏü÷ ÛÙ‰ëô÷—ýÛVѲWv¾ûßb9Ê[·ªï9‘þ‹ñUëü…ë2 .:僖ÇMO¤©{FÛôIE¢æVõ›Íñ»'Þæ,<‡[A«5J¼ï› ºY±Ý¶¼šJñÓ¶¯ïUTº{FkÛÏ·ÙMíºýÓ]èIke·Š"KÆe\áuJèz¼­9í8íIÿ­ŒŠ*OtÊû#&–qW+{Jéä|‹OÖ›V×güú Áï’õi­™u8%&÷uª¨RÑEåùOž+î°ý¥í Üèê£nŸ­ê­þ—ßÓº²«œïiê~_5Ú÷•ŠÚº~›Ðßê/Æû3ºQß²z'nx¯¤#õ-ónÙå׺Ì.û=KšÇ€•ˆ<¨~?ÔíÛ>¶æý”Òa.íT®žWóë.ntúâ[a·ÎÓ‹cC[ìP«(A+ðï/YwYÜý5ßßæ¸Òlƒ†3z¬ Þ'×Agë7ñWÚÍc/>Ÿý ñw,Ö ¾ÇüŽ¥é´u!ãcÆþj°‚øüÌÎpè’"Ü«Ö^¼H:¡ÁSÍió«Âø>[ÄÙæ6r¢>I&?Z¿ òï‡'ƒ.7øèÚng.çzvçƒëê[¿Éï³ÜÝ;õr  ›”öö  †²ÀF-~ð烺sÏ̯ð*Ý*JXl¨T{[4yÎ&r]Qµ"CBŠÜ \Aù ×¹æüú™¨E:iyˆ¢é½á¡êÍe*êòåøÅ!ù؉Oåæ“ÛJˆ÷'ó'îif±Œ÷Õ3€îyèÀ7drüXy·XECWmý<±I{M/j*¡»Õ›>D:ú ×·üq&‚nŽÆÀ+†Î®Y¯¢™sç´,d,Û¨hÅ uœ»ÕgDpÝé¸LV±m ¿~Ð]W¸(õk =_,™g˜£î—ËXû™Ë†_ëlOÜ]¾NSÉWs£‹÷Í â¾_ùéç_§GGÎ…ý™¦¢é^4¯ð—±ÄÄÏ¿Ú7qüÚWÑ´©>Mäý@ápÎuÕök7h·Õþ÷T´h\Û#d2f³.ëççŽôÖÆãÀޤUÂ>ZÁ·ºu³ªrß…Þ¤— †¶~OEöŽyÃuãØœNC®g9:‘þŸ‹›ê®¢‘Ú± îÙ¾¥ÐãlêÞ¢ðÏ&åÜTÑ‚ä©?:8Ʊuýí¢›îp¦K½btcÒ¥…93[ºŒå×ÏT-âûÑÛÄ=%RÑ5望ÚwãØüÎWn<¶w¡(ÎŽvdñû8ùüb?1–¸§Ca‘*Ê=Ô1h~»x6«q·Í½\ˆÛ×~ ?¹k Ñø|&2­õþC[Þì7pZE'’o3ôŽgԥ׸¯¨›q)ý¿\.ÜŸü¡‹y°!ewÏ»Èm;@}wг1¿!g³¯ò?{Ò™ŠgqOð– ÏŸøïÝÁOǶ¿°ºGÁœ]g~—ça'rº$°ÑÊ´='<œ„û5~Bß*ø>CwÎMX•'£¯'¿—ŽZª¢”NÎïö­>ÿ>Ûr‚ErHüiIãÌŽ3‹x:öh¤ùâ8â÷먨‘ßE÷3ÕÙÈ–¾¹^Îäö¤«rR³Äû¬ò>¾èò”¿Š£¥ŸÍݰPE«¿ôfÈ:V¤~;0Û…>M<äð‹¤ÿz«çNÁ7º][zøþ½*^د§"“FCxNdc­_ÝqùÝ¿ð÷¥„óºz£ÑâÜèY©h^Ç5âF?Ù­Ô&ëÿç¸ör°žÞg™àÛ w÷:2,.í—–›¨¨~]ò²¹IlÑ*‹§×œé°Â®µëøåÂuŸ_”Ðqw«Bú?¤t«Q¨hø‘€ÄŠÈ$&wmaÞÊÆ‰VÜKñÊNñ%~_:\k S‹:äÝNóML¹ä×}ÝÑÙ±y_’ئW¢]I!ô||e¾µ/µ©vïÕ$^¨ÐmŒø¦uvx"ýUÄmPR@ß=ï7ŽNf3_ÇD6x$¡æ¢3Ë_,ñ¥ðŽ|Î?èø}ƒ‰”ËÝðW’¶µç“gë’ÙÛ‚ŽÕcß.¦!ÇL¼´Ð—lºd~_Ú’Ï»èè7˜×Ò(‰ž_·Ð}“©$ý‰"eÉ_ÉìLw#ËŽ~e~®lñŇbÞ§IvY ùºwÜcÒcI”üþê*Ù=%Ýüƒ{À™Ììüð å¶B?çCÿôµ‡®tlü—Ÿ’)êó‹JêaQdæ`$gcš$Ùï+îCŸ>œïÙ.‰×É »êön­O2iìÀ(iС֥-¥r6³Wäá=Ó)î6CŽ7ýÓ¿Y úyô·=ªd’^k›2£’ê­ùi^΄÷¨føÂ½m}Èw¦QÏ)üñ©5\-ÊN½/»,–Óùñ\'¬$¯˜˜žº/älù‘‡ Z ³§Ê :*ú ïÞÜâ Ÿ'  ãŸ ÉiÉÑÛ/GÎQ’u€V÷Æ)¬I»¸³aä@ï¸òwÈ—2‡ÚKÑçó„:ÍFùòÖ*éë\¯„²U)̱³ö¾½}©õÔa×kûÒÌòÍV „ó:Þ'5…š÷¨¿Â@IÆ]Œª”§°g«z|äH»Ù¹^ôAí}ås4ï÷UÓVWóRèi’‘º¢‘’Ü‹w&|ÔOe4öœ~?/öòë]ïîw›l›*ì›.§•½Î½”¦²ý TM óïW=×®\\ãÁ÷2è œkfŒ§Rò&£€o—Ëið¤¦ÙI·RY­o÷†*ÉÃÑõœþå|PBwv™ë’•!©t?¢4J{M9ÍŒMH ÔJc·RµKž(ì‰ßgä@‚ÿ-¿~#Ô¢D›óKz–¥’÷âã5K'–Sá0÷‘Sǧ1ó> µ–e0¤Œ>zk纕¦3s•W¢n¹#u~<§ý;§闔Ѕäošôî]q»j‡¶€.¦$¸Ô(ƒ¥ ²å>9Qœ÷Uû­IŽtOgöÁ)OùxZfjQ§sS~ퟓ)øñ–RPÿe%ë2ØÍ–6›Æýuòf>ÌdóÞMPï#º÷²Þ‡~3hä>ÉÅ£7ùëtz=®¿oª ÐvܳúÙh—¹ï3Yð™Fƒ^¾™+Ô1ÕvÓãÊ`þwQB7&ì}åe.3:  ,¡)ï.¬yÐ%‹µ=ò´]ð‰9d˽îµ^,샙įŸ¹ZÔðÇ–nCV)èEP¯×Å“Jè[‡Y{×Ïb#4ªæü¾¯Ë¿¯ÁŸGÐM+šSúÇ1i^KêPBéO?{/ËbÃJ†[}JœSÛ ×Óüñ"‚n°J±Èâž‚Ö=n2¢¼˜F/µ»Ñêh;åzý¥ß͹Â>©Ñ‚ß<_Ç$Ðå½¶®¿ ³R§†ÓÃÁ—\v%f±‘:‡ÚX?›Gù…!ë]ôSرáO‡Žã³ èÆ=̈tÒʦ6¦&r»bZ³mø¢<íJr·úikC!&å†KÑó[k÷þýŠ×…Cg8ð]PL¿lºüöáåøœ_6|û©Bا³PØÇn.<ïàó® :þ|Íêtõi~GǪ²‚¯ßåUöfQíû¤¹áÀç]%tÁS¸Ù$k²EÇls±§½'õ(ª ‡–}=u´¥—c¸o}¾óý„ÖHµè‰ÿ¥ÑÙôîãä[ÍŠ(wMy“Æé4¦ÊöþàÜEÂ{bÆß4ã׺{gÈ3‹³É¤‡zÿŪBš}‡2Ö?¨ ¿.ƒ ã-Ò´ûËZé¿ZÄjßïѬtSöž_eò+›z->‘ÙîtŸTºŸ®UÐ[»&eókß+a|ÆŸGèîw¶¤w÷âv-..¤sYÛ¼ó´ÿí•3 æÐÝõÇm“Ù³žGAнõR,^k‘Ca÷ä ¾w*¤§;?öƒî¸ö ½³jó ãûPþs†CÇçÊŸ~pó¸¢òl`9Øû`…POĵû·¿¿@è?G־ǗCVQ´=+¬€Žv¸[ »«‚ö&£“œYûþ ›ÐÛ_ûO¾QB׿¯)O.çPòç…ŠÕŽdÖ³ŸéÛt·Ó˸7[f × vLv ý KÑÂù7 }Öõ«Ù¾á9´¯ÅÙÓÐÒ¢@—À º:×çèö‚yÂ~̵þÓüúAçß°8¸ïÝd1¸]çùtÛÊfwo *ÛþÓ¿M…ô$=J­gËø:Í?t™_û-È¡ŒÔÏ=¼îäÓ$טõ‹+èrþèAw}l)zùh‹)!ö¬Ë¡cçîð: tËíÏv¸ù-‡|;ަX“OÛÍNÑWPõ™NŸW;Ø‘æ9ÁN'Æß7®ÿ ;ØáhÐ’?réS‘Çå‡ùô­·¥«Ë˜ ªØP8ý\´-m+{¢²lì"¼?6š_?è&ùÙ;rj.MkÛÃÔ·ÇT9.}øÓ J÷¾¹ÿõ…µû^Ø?ïgÉFÕî‡Î¥„÷·*cSëÀö~I}*„zgCÍšn2ÙÝÇ•ñõ§„.ýHI÷°\2ÊÚt÷zÀcz¶}õ¶á+hoWîFë\êÛJµ¤Án7Öÿi¹«­ß—kY¨EæF£Êri@›u‘'G<¦VÇÖÑ+½ ÚgìÚê¹ËìÚ}l¤A“o­…þºÑЦÍ”æRF½æ«¾?¢µIý'_m\AW&ÙzüòšIí¿t?Ú2lãïŸñyPÝgCÛ½ñŸr‰ßGøˆv;Ï-ŸóKM;Õ¶Ï,Åt,eDqIè26#õìÖáZüñ)NÕïÙdËfyôÍ&Îkì®G48"uÖ„/jÊ<¸kVMO1Å/hï¹Ë“w=õnœ˜_?è4Ûy»æQ¶¿ªqÅìGÔæqRA“·jzíòjË…ïâÚ}dÌðÏÆÑò(¾O‡në“=#¾呟Óó#qúèGç†j5õmùºaô,á~”g­O=¿~Ðõ÷{wÛ2Öo|õ½8zíU4®DMž‹Þî?ðlÉ]]ç±ß‹ýËõtcŽok:=Ox–G1Ãn™Xd«ÉQªðÕt>ñÏ÷¼Yú­ÜÇÁþ>Š–¥ZôÔó\¾‹cõìrýõ$ þ·Û3q§D55½w`Æ^ÏÔj÷ƒ€óÞŒßÆgÐñïùäÑ‹ì93ÔÝóèÙÙÏn«)ÀùÞ¬QÒE•Ãmpõbº™§ò't}v~Û;u[iç·3/Î¥cKsZ+"ÕôæxÜȶôzûU\{1þ¾›1¿~йåˆÏ„äÑÄ9¬×¡\JÕïî2ø˜šÌ#ZÖ¿lG]¹í¶¼ß÷ù:ÅšÌÒ«Gòèyõ¤¾­q>=+XµÕ{—š,Læû5ߎø÷3ý˜ð<Ž_?輸v¿£{«™SË´riŸE-ßת©g”ÃÔ¹ÃlÉûMT·/WØ îö|{áþ5t»t¹Ž<ü5r¨¸‹Uõlo5]»A·í …ÔæÑ¢ú‹z2Í6§×ÂúA·ÄP2JÿZ•¿Y/Í¡¾&~ÒÕDí'îðü2ŸÜóÆýkS ãó¯ÓºùxÇðä;yTô|Q÷ÇsèbUÚè¡6jÚýhÌyèOØ—¾â_ŽOt;G˜ìý—GZq],×e“¹ÁÕ1 ƨIþYo¬}£tèæ´w±Œe«›…T7 )H«nÒÿo³þSgGr¤Â1ÁýnÜÛXP ‘¨$ (€’–‚hP Äl@ Í xƒ úHpb d #áIA8(zH~Ö Ä‚j`ˆd(¡@tE DƒJ`€DiB€\HšfÀD%ÐGƒ`ÁӶοí¿Ç¿­Î—äßëKò¿³­ø8©®7ªë‚´êz£ºÞè?£7âr·°æÜïÂiÅ È@ 0F¢’‚pPô´¬AˆÕÀILBè ¡‰@ ˆ•À ΄¹ìÌ€7ˆJ $Á@j€1’¡„ƒ ‡Äh ‚@,¨†H” @ISA4¨H¢6 ¤Î³í¿Î³­ÎƒäßëAò¿©ÍÍŠÔCQ·ÅÝëF|P -¸{®ˆ@Ç’»÷‡ø TZr÷ „¹Ð p»Ó½AýÏœH®¿©ë“êú¤ ­º>©®OúÏ蓸\(¬)÷½¹gB€\HRfÀD%ÐGÒƒ` 5ÀIL ÂAÐCB³A TC$8   ƒd' T$?äB"4Þ (>£¨ÆH”R €’¦5± "‰JoÛ:¯¶ÿ.¯¶:÷Èÿv¦¶裨‹Að(î9=âcy©÷¼ñ ¾µ%÷ÜñA50D ¡@tÐ poS‚èôI8|êú$­º>)H«®Oªë“þ3ú$î\ÖŒû^†ø;  î³ I‰@ ˆ•ÀIË„¹ÀÌ€7ˆJ „&Á@j€1œ„ƒ ‡dg ‚@,¨†H~ @‰PA4¨HŒ6 È…$i¼AP}$M12PŒ‘D¥‚§mGÛ—G[ïÈ¿Ïw$=jk3n9âƒj`hÎíeF| :#¹=µˆ¢AåHno'⃠ ¼ðÜ^7ÄGÁƒ`KnÏâc4R €škbÿÑ'qýE]ŸT×'iÕõIu}ÒFŸd&o2ásãï¤ p±¤¬AˆÕÀIKBè ‰@ ˆ•À Í„¹ÜÌ€7ˆJ d'Á@j€1’Ÿ„ƒ ‡Dh ‚@,¨†HŒ @IRA4¨Hš6 È…j¼/Û:¶ÿ.¶  ªan®+âÐéËÍE| *ûrs.„¹PÐÌ€7ˆèÇÍýC|81îÏÍŸC|`Œ‚'ÀÍAC| ‡âg=›Ç…ø qs¡(€Î n>âƒhP9ˆ›“ƒø È…¢i¼A„177ñQDÅ x7¿ñ1ŠªÔ„›£€ø@ÖÚ”{ŸñA50ʽWŽø@t†qï7#>ˆ•ø÷l„¹P˜Í€7ˆÁ½wˆø8ùÅ ØŒ{ÿ ñ1 ·Ôœ{ ñЏõHî} ÄÕÀp÷^ âбàÞ@| *-¸}úˆB€\(þfÀD%à&&‰A0ý£OiÕõIu}R]Ÿ$Óªë“þSú$káx’ ŸË xƒ äþ[HRb d #iIA8(zH`Ö Ä‚j`ˆ„&¡@tÜD DƒJ`€dgB€\H|fÀD%ÐG"ƒ` 5À‰Q ÂAÐC’´A TC$M   ƒ*‚m%0@Bµ!@.$W3à "€è#ÙŠA0`Œä+á è![ƒ  ª!³„ÐA’@ *’¶ r!›o”@ ] ‚ Ôc$x)@ÉÞXP ;q™ˆ@§3çÕˆø Tvæ<„¹P$Ì€7ˆèÊy¨!>Іwã¼¼£ˆH»sžRˆô ߀ó6B|P {p;ˆ@§'çõ‚ø Töä “÷æ¼£PIûp3éè¡hY÷åf£#>¨††ÜŒnÄ  Ó›ø Töãf#>r¡Ø™o1€›áŠø(~b<›%ŠøÀÅPjÄÍ´D| ‡Âh=ˆ›­ˆø æfü!>PcnÖâƒhPiÌÍPܬ ÄÑ r73ñA EÛ xƒsîrÄGƒà‘ܻ̈ŒQÔ¥£¸wjè¡À[[pïv">¨†–Ü;†ˆ@g4÷®âƒhP ¸IÜÚ ÿGŸÄÕ÷º>©®O Òªë“êú¤ÿŒ>ÉF8^¸ß”‹+ Trÿ_$)äBÂ2Þ (>˜¨ÆHhR €’›5± "ÙI@(P$>Ñ  Ú€ ’¢ð@ ô‘$Å È@ 0FÒ”‚pPô@­AˆÕÀ UBè ¹Š@ ˆ•ÀÉÖ„¹xÍ€7ˆJ D,Á@j€1³„ƒ ‡$m ‚@,¨†HÚ @ \A4¨Hè6 È…än¼AP}${12PŒ‘ü¥8oÄz(Ö9ŸiÄÕÀ° çwŒø@tºr¾»ˆ¢A%0@Ѱ!@.3à "ºs~˜ˆo€ø Ø€óeD|`Œ#íÁù">ÐC±±îÉùÔ!>¨†p~iˆ@§çÛ…ø T&äB‘2Þ ¢秃ø(ZbÜ—óuA|`Œ"&5äüEè¡ Y÷ã|.TÃþœßâÐÀÍýG| *ŠŸ r¡šoaÄÍãF|F1ÄÍ…F|`ŒB)ÌÍ'F| ‡¢imÌÍÉE|P ‡póZ(€Ž 77ñA4¨4áæW">r¡Øšo1”›ç‡ø(¾b<Œ›+‡øÀÅX:œ›o†ø@…Ùz7g ñA504ãæ=!>PsnîâƒhPiÎÍ¿A|äBA7Þ b7ñQàÅ Ø‚›KøÀ_jÉÍG@| ‡âo ‚@,¨†h87‰P øGŸ$árøÿõϵÒIø»ÑjQ\âŽNSòè{仜i2ÕüÉþýÏ_b¬—E˜ª…¹f#)Kuó|^-Ù›Ì5îùµÒ ú3ÞÿVJÊ}ú³Û8V«çâ!Žþ"_q|ž0/XA~e 7¬é¥¦ n,J7 -Šé?m‚?së:wšž?ñs! sÑáæQËA]ýN\SPÊîäk¶Q«·âeñ{:ç¢{C{lüšù¹ЙtÉrÝÏ£·©ƒômýdúµÙ“ˆzj:¶¦å6­Qt¨%7(c㦄U s­  7 ZWÎòˆsí}=@A’V-ޝ}ûœ>ü|v°w¹=-yû30jáJÆÏâ?§©Eܘ’ÁqÐi ³Hoðè»&eÏIU5FG~DB®U·šeø¯bü¼Ra.5tVvgÅíäQJzÃA{‡e‘k›»­|2ŸÓ¹[+ƒ_Ý·#ÉÕñÍ·V1Ù5×,ç¥üÜ_ts4+y´î—:q{T& žìÞ_öœ®¯~-v_Hƒ"‹–môZÅZ~o)Ìu„Ž›2ðTUžÙ«54“¶¶÷æúsZÚkö ã+ÿ‡½÷€ŠrÙö}1c#&l3f̘z6(Š€Ðä&7IDA$ZL˜1cn3bBEÅX€"‚Ds£ˆ˜1c@߬®êÞ®uß9çÞ÷îyïî1–cü·kí½« ú›s~•úÿsέˆ!ÌÇ‘ùPȱ]Pf®pM D.wZh|6Âò¢ÎìIzDµ¬ùž©NðòdbéXîwÂüèØNü$¼Wéü¸÷|PÎy0¤UÇ:.;ŸA䦟>wq5&Öp9صõÕ6q¬Û=˜GÉ%@ŸN‹í¹p¿d¹ÜqÃ30Nþq¦MGGH¸ëúɦÞb•b²ýÛ-ÖN…íÊÎv¹:Õ«&“ñgýºÇÖ$¾l¹üDMy¸5¿•Ü®k½oh,a¼'î«*ªew£äˆ½Ðc¤ÔåLj-è<ÿ8 Öq {ê ›\ùö•ÅÆ!羪Øn¢+Ù’8 ª–¬:™Öýu· ïªVÝ™ÙÉUãßKþôía»ÔvÌ?Ïæ¾qísàÃϸê€gpbúÅуÝá@#êøC˜ï=÷Çv?©í©A ¤Rlêæëð:X>ƒõ {7L ð„‰¹¯?<‹&Ì_®'{~Øîµ÷kYžC¾MÙÖá:¬ýÑÄ+Ùâ ;Ù··Ï^ÐI ’&j»Éz¬Û-*Žk;·ÆË£Œ×vdCA婤O&ÏàÙ«;fÃr¼¹k÷éÍž¶óü5îû›WÅÐh€s‰´}6üzãr nïg`O¦´êùÜ"&løRß;Š0¿¶^ìùa»ï«vÖ¹Q jûã × Hý>ƒfz,’Lôæ¾…QùýtL+DÞs î+†Ï}4¼¤uù• Ÿ¥Âoãη‡g=ÝC®\Þ1~Ñ|æ&Àv×ë¬5ßU j;žð«p´*j_ãå°#-ýsÞðü¾yßà¬9dt8%–2?¶ jwè«£M1 Žx­hq' žûv*ý¦¼,–.5žçfjðÞèY SÂj.³À¡zî¤U9tȼTYv^-ûî]›ßwù‹¯1¶sÖ«ãù£v1l™°~Å ”LxpgAø„’r(~j_5£¯HáêóFÌ%Œ÷Ìê™Û¹w¥$ê"PØDGtë— ùœ+¾^+‡¬ƒû½¶›¡oðªCb!0¿S%{ô~',‡ÎQWu?ÔxeÃôä˜osIÜ‘ƒÎÞž=Ôí”Øn×ᣥޑ…°óe¶²úJh5ÆWãrä®Ûkæ™/Ë›dÍåþFúìùa» U‹¯;B)†×†/—!ºý­ÕK»•ƒðÃ¥Y©R ,U®-œ<—P×ÒÎ>ÌßOg\…ˆñ9 Á$p¸5ÄŸSÍd㉰ݗXß}ÀLÕ­™=/BiêW›°Z˜G“—ͲÒè[pëì9¤ÏðŠY™]˜?•Ûiæe>÷F|u¹•I¿¶?üü”ûŒºÀÙÈ‚qÓb ói4dù‡í.v£€¯(0ænÞÊó0(ÍÔ"ìÅSh¯‡3 WH¾}¥_jx a¾±ìóT`»…÷f¼?©„³ãjîœ;¯œó'½»ÿΚ®ûUÇÎE3¼ÜŒ&õöô °ט=?l§.GÍ  NƒÔˆz÷Óàä¸Ýºç=…Y¡Nó.yÁ¥k[z†@4a,×*l·õªW3E>œÍïCQHЩïsSãKO!ñÅäÛäÀæiQR»3oÛž‚p^Îʶ>11sƒÅžÙü=ÍÆa»/^´`ß„i×V‘§aEòù=ï–=…» 5Ññ+­(ð|6|ÙRÉò]ŠíZͪ0lq9V¶øÔ6&3Æì=Å<ú)Œ–uèþ¤0­,úPiâl²èÜÃsgr. ¶›¯xpæ]Œš}Ákk×T¸ãåÔ`yÐSèSï›á^`U!yäÔvÉÜ÷?žÀ³ÏY~ùChC%ì·Œ ŒßìÄž¶»úhÅÀ{O¯ÃhçÞc‹­Ž€²¤Sͳ'ÐáÂøÏ·•Sàs Ø_2‹ø8Q‚0ã*°Ýĵ§—ØÇ^‡'Mj>î0ìxºnKúÍ'0g4%Â…9¯2böFÕvýý—ÒŸM‰íŽþlЭÅuèt\ôiŸu24ºz¢÷Ù'ÐïØè^ j¦€b•Ljý#Éíkã?̱gÏÛ•m)è~b_6d¾èîîis¨`ÇÝO I`ö•.0ä‘sÌ´á‘d³ó¸'†V¶ìùM¨Å.½¿ÛgP6lœÔtšÎð$h0ø}MÆÊ'ðÊàçžCãü zÈE×…_gi|ÙóÃvÎßíltö«A–á·¿Y\bÌPc¥Úù@Î÷¼Ìˆ.‘äcÝ+6'‚Åìùa;Æ©¿³I£ª‡û¡¨GÑëÐà'P„nïs½`– I”u§?Ìs`Ü3)¶smÙxŽùÙ« ©8z¾Ù­}pòÓîÆ/ÝŸ@ñçñwwö€Só¨¡õ,þyNdÏÛÕ½ú½~ÏWá·`]—k·÷BÆzm:Ø<‰ÉaòdwÈÖM ­{(‚Ü«KIŒ{¦Àv’§!£j§eAxâr¤lħܨDôKkÜ[zÀ¶ÈAyÇÃIèø¯G¦¦²8Sb;æ—žîj ønÑèwé½AO Ó¶âû6yÀ‹NwvoIÔ¸UCþü°Ý¨çMš]Ì5¦¸ínð;é9Á¹Û8»£dó~O˜mì»KgyI»1ïá‹›Œ¨3±BÄæ™™0´e“çí§(@ž%=Z=¼cwä Vu“çë]%§׺ìÉÚ &j|U3àMÑ’Œ"å.(¬/­Óµ.þœïÆÿ>¿L ¾M|¼/(”¨§ç®œ«ˆí‚ÓW\ŽšVµKJNžÞ u ß¾êð± †Ntž×n¥çÀ…Ÿ¨úùa»Ýzµ–Š÷_Y‘ƒM,½v@Nó3»—•A§¬GÆ‘¾Àâkù1ö„ÍÆw,䨮r—Ê×¼é°õüÖ/á6h,²¸y³ jÒ•áïÊ|ÁI½ šA|ü´wãT*°Ý°w&æg¦CKJ Ø)¹g$\(ƒ­WE•/K}áÍ»5Bg?ù–Jl·8zÍÉ¥JXòœœo†Sm•–Ÿ’ÊàûùÀ4‰/ÌÍ›y·rû ûÅã@ÏtÆ9Ta»#µ,Ç­2SÂÖ¨QEW6Bý5=|u6—ñçæÒjaüröûéXà{s=5Ž%p7ß·÷ºã EsúÍâ2HY¶wÚn)ŸgM#¹æszM¹Äøˆl·Âf“Û»—¡{bð”þõ6ÀÃó_¤Gg•[ñÞÆ…]Ûö³Í4ò¬×-a§5œkŠíæ™XĨw Fã*Û`ÝzpÚ4ø|^@ØRûà«Þð¢q—ç5óCHÂäÝæe1þ£Ûù ýuè*âAßõÐ顽Á~§20i—åñR8®ûëìºNSI–á½kæ±ÏEŽíìZ´ê0ÿ<ŒS“ÖÅp—¼ðñeàñdš¥á'Íû=¼·\÷ü»Ðƒ=?l÷úñç-§„çÀxÑö¯¿ÃÖÁ}÷nqƒÊÀ쓱mìßS—ÿ*Èõ#ßvÅo™Ëx¨Jlç}Vwåç¦i°òKT—N†ëà»IyŠyÇ2ÎWœ—VúžÛKJ–ŤíÞ¶3þp²g›ê3P)s²•³š»²ËuË ÷«¸’’Á›¾Í‹Ê¥¤ÝfjŹГp^wááÓ ¶Ÿ¾O˜õJõs—:? ‚ñN/ó#ê×k"ã °¾œ—gÖ= ÍßøÕ#M×ÂÕnÜ UP³wFÑP£`èzíöE~SȹåY3õå\hl·b¡j¥ÂÃÔv»$'€l^‚2æ¬ 6F¦^°›¾{<¶¶$EÔ®u.ç c»s5®//; ~½.Ö=(L€­“f¥ìTÁ±ðNŸÎ< „¶+ûZ ™B¼›ŠÛ·b|`9¶3fWÎ XE-G“¼¥*XÛÚ¥jxN¯Ÿþ$³;]±±ÏSíæÂ0¯ó“R n8%ƒ¯»£ýõÂUãÉ×eŸôqÑvþ}?RZB þY;%¶SYF†Ô}ræ2"n·\c_Œð”ª`KÒ†U—ò| •нAƒX?2üh«Úç2ùóÃvmŒ¸Ï> ׳]K‹›­^¿î[ÀÐľF'¤ðüp¹ßì¾äƒ'udeít,+DÕ€‰C}³ž[ó5p'`ǦL¡Šq×fHÁ~\yÓx)IÞ¶‚¼iÈ¹ÐØîYßëIìA8Ø Þ~Ù³ÕpûÈ˨Ày÷ýC™Rh2ªKDAÂ8Hì9ˆ°Ýs£3qöÃñh§ÀÓÊÕ°mðƒm:áçò&þ[Ò, ¯•„ß3±ü‹3)¶Û@×{aS7Aþ€5ð²wži߯øûå;ZéË9ÙNÄïý¶+–õX¼È±ÇÈØÀT§Ýpp}Ȭ‘= }mêTûÒ<3'lõƒo6¹1ƒ68O"âÎÆS`»îÞ ÷eÛ»žÛv¸õ"¾ÍÚÔüëµÇЫUW•^²?¼Mð•w N†úùa»—Ú+†–$´ÑÓVé^] -,ëÚ^Ýñ>^bp<:è,¬U¥Ã_âZ…í>­ ë ðq$(¯c<€™axȾåO8?lj°ýSÆ×±ª%(;Ïû›õûw—FD¯‡ˆ F‚uC ×©Zs¯@Ÿ¬ÕWwÏs! [wôÒ[Ìž¶Û5ÓºÍ×ÎË`¬ÎREð˜ím¶tx }T;ñ7€«!דuG¸’%µ¬Žù¾ŸÊž¶3í·md÷Órè^ÒäО¨ÐîÝ\éÜÀãUÅØÞoý4~Êäàõ•g7z±vRl×êå‹nUKg@ŸŽ¦ø#m‚ŸåF¢V¹`ç½|ƒí³}!½¬žþ¡ ’ð)¨uÍî öü°Ýøá †OF8t1\¶y3è44•˜ô$ ¶m´¨‘¯“ ÑÞæ ±ç‡íwÖ«´¾Aff¼;à™Ë·çç,}Äýš½!úEÏAºG¬É“ÚFžßÙç©ÄvêåÔò…_ŽÏ Û·Nž¾l!{Ò¨×Ãçzþ+¥f ÍÈtõ‚‡§Âv]—yM8±’xŸ\¶ùû1Ò+gñÎ+Ÿ˜âùgïáôÅs‡uŒ:ý€ý~:Ö8Ÿx¶|uù:¢|­ˆÀ1«Êef=ÁþF¯¦Ö>áÌWºX&ýpqæÏÛ™ª~3ùÒÍÜ_’µ r3¶®õÚ)÷u·ñ6ŸÎ÷¿Y;¶toœ¨<ºôîšÑçQöv =ö›w½ûJ³‰ª<ÛÞ¾tò™ÁRеËcí¤ØîÅ/ß~Å»ÈÀேuï€Ù§ž{KÆZ$[ž €F÷Ê«&õ2æŸÍž»Û= o#žï&n_º§Œl° dz·,ïÄ=“Ï’ opNèP˜Õ)¹÷õQ!ìùa»=Ut#|/êÖúÖ¢âvØÊvyÙå†V-8'­ é÷¹Ÿî³¹¬ÛýYïÛÝìýÄÍBYù¡Bý'vû®7ð!…,Ö~½×OËÿ[žwíôn_ÖN…íŠVÜ«é£{~Øn"Ýž]}Š´±[¤:se—’Ý=ñó úy¥ï$/¾Ÿl ~Å–]¬øó³©E{—aj–JL(¾åÔA˜6m‰rxƒRy{jŒX C­§ZxÑDÖW‰c¿ŸÛu¬ú注*•Ütè4sȃ$ØW«-Yôì>¨—~>°ÈbIG÷|1¨ñMŽüùa» ûFç/í;MÏ7:1«u?ý>lZ©;Uwˆ/IJÚ¤³ Ûê±rÁ,ösJ±]HÏÇ!á~gH¤­a›àɇaø«çI¿¶ß‡²/ç­õÍzqZ¤ 7¯LgÏÛùÅ\iÑÜè,J¶Ö©~^y1½rö}˜§6Ø÷^_ÕõPöü°Ý€µoªÞ%GF/~{FÙw°ßá>øô~0¦óq^FAÞõÖÊãaìùa;ÒœšÒˆýœ9f‘Ç`û‡Á%3݇ѷooY!Õr=®Ý”sŸµSa»æò´Ã.kÏ5¾5ö8Ô¼fø ýûPÓ´÷£úyq>Ð`8Cm×Û°v:¶"#_ýŒf~ç‰÷Ûõ{uìR {Ž‘ªòÓ=è°!FªßÔØüf$L]زðøÑìùa»fÊúããû^ Çæß ¼ù=|¯æºÜ½ý¶ïûæçæÔ`³‰°pòމY¿§±ç‡í® ³OJŸ\ ¯Ú÷o“tÞöò›“x霞3ý¤K¨±™ÁŽ`,¯Ú=ÞŠ=)¶»wƒÐ]$eRú&? ¾ ɾﺌä ÕO(ðÄö÷§ˆýœrlWÔ‚½.‘_Ûã~†”Ÿ„9ŸÊRGÏ¿3žl}bîkÏ NnâzîV~Ø~í^¿D(½`‹Ï)èö#vÑg·{0Ô4:s•ŽçÈùp¾,k§ÄvKÛµ·­v™jß*ï\(ÞÑkç{°d\ÿÛ¦þ€ÉÙ¢ï^XáHO–Y;¶«—qnI»ö„$µ§ Thha~4¦Ñ=Î=òçü[)d´^ǃµÓWˆ:ïžbÑ:‹¼lÁlý©°h_²¿{Ù]ø|Ÿ´øÃŽtÇÒ > +hÖN€í–‡—† Sõì|ài¾Ï{îKÊ_ïæúj€©üž5ýÞÊbÖN„íjLèÝ9NI°¨n›~÷4¨'7Þ…ÞÉÝ^Ë|8¯Ü |Ú/YߛřÛõ÷Lþ互ÜK²iprçXYYY;)ü.È« ½ä 9­=ëzÙAËÙS??lǸéD6kæ’'«ÏBZGÇ¥ î;õg_VÐiLË­Ý7ùÂzÆ*ù&hlÝybø˜js®Ó¬üz»Zt!Ù­aÏf»~gS-áX©7–¾_ƒãÈÖIìß ÓIÈñz—F,Iƒöã,kê¸ ·|T­NtIrëUù%¯¦8w¯{u9|\»á“qêVí8ê8Àþ$ÇÎæ9Ƨ“n“ôŽÉÍÎÁ3á#q§»0cp=|uH`¬y3ýqÞ­æ&ÈÀzßÐm×|äp×S±OY;‘Åö“'ºcG“+d›y­Uλ\MçzwÁâBd³Fï\8O%zÏÞõtoVLœÜäét)¨Ëñó8'v"½“ý«÷^!gŒsdɹ#;½)…í9’naæz>'ö7´Åný ÂøÏ`Þå¢#ΗBl´ÕÂ×õÝ9WÐ[[Ö¬>¼Ö3(¶ æi»HÛß’¢¼ÔS;uà,ÅSµfÏUŠýSjÌ£$Z œ¸ô6€ÝÎRèÚ/bÆšwœîµ'Ön:|ÖtÎ_õöâ*?4ssùý/B#×Äü:A:¼Þ;jë¢:Nqzú½íwq[øÕéœølÜ m3_^ [ ÊlÑu/Îi³‡(50iÌo–v,цWZ͘?ÀŒL»W–Œú"dÜ«/Âú6½>9Á­—‡×>mÂâÇ‘½RaÌ$ËNW·Òyz ¦go}ý]Z oûüÈÒ—ã:€µŒ3 6,>~|Ö!;žÏÿG§Ø_MK|ef’xëozŽ«.CêâACæŠJAÍaŸ(…|ýoöp…z?އO Ѭ£àáÛg‘ؾ¬ ûñ¢'úYdqêUEK3Áµ†éõ”BÒÙÏË>\ð†{eǯ·ÿ,…C­FÛ<„Ÿï×sø ¾ŽáëÖ,^í+Dly;Ë>‹Ppb5ˆÐï™VµJá÷ÎfíS¶z‚ùçÛJç†SÀÆ¿¢öækÁ l@ÁkQ0´%˜.€ƒÖ‚û•ö- 7;¸n¿:p•^ªòv× ±b²}EŽ­^¸f‘œ‡Ï«eaJˆKRLôìܱ\5«¯xŒ¦;PSáZ_½±½×âÛ~Ȳåš{1Àî9ôà|4CX,¥ù~Àxœ¼nâ81& ^¤NÏ"×ZÐ7é°:|[èñÜ;îÿéÍôãÎÐ¥v³Ý–yÓatû¥ß=KƒaØ«•ïVX-†~ 6”ž¾4W;ÎÔ-M®}lÇú•b¿LzçÆnÏ"uM◧â‡{ež½!Û“tÂãÀ¥ ÝÁ›žO²ë¬5†U„Ç£^.€åGAË6.`ñ‹ýlPVtÓò*a÷ÒáÝë%Ùã÷ß³ÆéÞq íÁ¬Raeòi ?o’ACºmÓjçP/bñ‰ý˜–ÌÕ±ßv•œK~}¡ku:¨ïo¬¿ËGêŽr»eE:j7\î ´Jé €‹ú*Ķk Þ’#Ó?ogý(±7ËMëÅ߯õvœõhh&ûøvÞ`¼-'xd¸H>§¡\r6›=i{,.jkvóÇÿ3c“áäÅ ›w,¶o‹n,>±¿šï]†KL®‘òô„î ô-Üû±oà¨n‘?oR¾+0î›”ËVù9Xo><Û²y=$/8rÐy üJ±\­z%€uO?|{¸-Xž9¿½…žÌ ¹!?Å÷ó*Då_nôê»äÉšiu¸KXL÷]w_duÎÆßxr>g-p»µnõLã©Ðmð‘'«r×Úäžñç›'@£’#.oû(œ¸¯“Ä»%´±Z;§G‚=°û^ƒYÜâ8âi¡]¿_#ÝÔ ÑL¨´(6?Þ÷ŒÒ?5YµRªåH²ó…°)ú9èèäõu«NE‡ÕÚqÔñ‰ýÕÉÓ²ɦã7ªvMÏ„ªúKóÎ4¼ÑçpuõÑ®÷Íï,î¼Ýv°x› ‡LûcéçöôÞtæ£p]Tô(— öÀøfìÇÙ†èÊÆ‘ã8l^Môž—Õ=Pœ y7xÆd݆«©òöº-¥pJtaÜòZöз é}Ä1¾ê<Ü<侓†{ ¶O ¦ ø,dïåj!ßoo5(–Ë*pœu'îý$/³I×§v}ÊfbçS‹&oköí`}‚Åýê–`[§þ¤]Îaé½~ØË.n°r×ÕKßš:@º,^[#<»¦§Ñš´Zà>§‘ü}7øó¼Y‰ãL\ÕpÐfÇë$®Ì#£Éª,h¾zïBXuZ¾\"ê6èo lµ@aN{]êlX ò[k»íXã’¼¦…C6=Fl1 ›y?ÑÞ.=ö½6œmbbu}¦/ wÝk¸§Ãé8b½¥áÑ"‡M}/ÄʯÂË‚ñC’oC›âM‹œêÛBqËMQ¢»þðy‡÷ åœK–LîWås—,¨nÔñ»=÷:@oolé#eW2/¾){ï pÆgË!ì¼ý*´©Ýj^]×Û WglóÍÖü¼Èú5ý™´¤f:´,j¿ÐFw2Ÿýkœ/'ߤë*v¿Œõ/ÂþwöÚyªÞ¢Rû~ÿ‘ ¯Á„óÛ°¼ ÇÎ7»µÁ2\þ:Bv\èÅùÆ!œ<^­1n"™Éϵ°¶–CعÖ5x/:fh:ú6?É!‹oN}í ßî|Èù|&Rg4îëê Ç©°¿Wä¼mͲøÇþtuVpƒ¬¯ÝXš£¼ë-°Òïs~Ì_Q·¡È¾P¬¶y\ð¹¿'·~4ؼzd~élpïú" ê©76ZÀ3«:[ýJô õZŸÖ3ü½À Qi³ ¯Ç±øÇqª³ö÷™vƒÜvq©<(^K,ÛÚæ6¼ÃO©Ò4û•ì¾@ç£zi×?ì~K0øÕiÛ€[møù–7Lwu¯ã6ŽÇ©º·¿éÒý7È«k=¿é“lhtÛdÛjÛpðìÖ‚ùÁ.°B5¿ïÝa°{Ðú¤¶s‚aáÔ·•óÏ9Ãùó~'&÷qÓŽ£ŽsìÏZ t¾AJ/&Ÿr“^‡¤'} }ºn8Zî ¡Ý ¤Ï1p¶ßþR°–ÿæ¨ÍKá÷ˆnÖõ§ã„ëüÐè#íÚåÁ¼_±U÷®ÃýöúµónA×âWÒf¹®h(l“ÐÆÁ<Ž`ž]Ü/³52m=EIycZ±øÅ~eØh–Kž™Æ,ïØ5v‘[RnÁ³¯kÏX—yÀî=/]Fužlÿr*Ý/Oý]+?Ö¨ÄWÎþFðqV»_ÅM[qŽ §vŸQÇ8N×v3CÆŽË%1e7¯õÊSÛ¯X¾þ´húuø'Ÿ7]]´Ëíâxù¸¤™àÝàf;»{S!R=!iC½êÞééÝüÕhO8}o­‹õØ8R‡ÝÛÍ%§•†-{çåÀý_U6["oÁú–ëÂö°0Çé{¦Àï.5_f©B´ûE2/zBæ¥GçØß³‰?—óÈ%Þ1? n@»ã^Lq»Ù“.nvÞék2oŸÛãjül»¨yP°aǼP §Ü²1üœ û!~tâšKš}鹤Ü? ~/™ ·`G@›Ý¥Ü µsŸ¤¤ï ^~åM…F;Ûg1ä<¾´÷ê¢(°8~që‡;íînwnÆ~_%ökИžXæzKÈîS.¤]‘îµëy Ô¸ØPcÅ7Á&a„éÜ!P9uÓ ÏW­fÁ­Ÿfw÷»tç¼åá•ewn™[0¸ÞoµfE°‹gçRÆ›‚ƒ<²ûöÐw&æyp¼ãÏfÍoÁaƒG_lœ¡zYʯ5S¡íÎÍËWEOãç³@=}zÝßuÌ›Ãgÿ_…#‚FÁç[N ÜšLö¹8²8w®‰»~|:0,Þ¬*i°.N=«Uhò­Ä Oí—Ìw‚ùWî Âï¡M…2:óœÓ¿YXÆÄÀ}zLaã|[ø@agÎâû$»q®•Ky{*Ç:¨&r£¯^öZûš|øm3ÌVƒ5§Aw¥þÞçSør çáš}ø/'â/‘ÁÅœ{¢<"À¼ï×Ú§Ì ÞùmÖ ÂzÀ£&oìïNi§å¦«ãÛ¥BÔP5彯ÏMÒm€Îª¢ýpÃ˽|Üçb˜wÓDÖ¨Z šûnšsöÕvMŒ]^=­m0ÄÅGÿ00ø%ŒŽÊ_ðL·|#Ýú8Žp—øÜõ›Ý?à8ކuW,¿I”_—êf€=ž¼U ?<ö._ê'KbHáj+˜Y½¯eÃÁÚý!Í÷R¶}Üq¨Ë…ŸÂC½§/8Ô¸FHg_;œ9ÏßsÃqèifxÚM2ôÖúoÀÅýõ.ÃúsÇ4svŸÍÆÛxçßé°*öc‡žnl ìz`¼=È—Ö|ÓoÿSزôë˜{ë¯ üüñ@W'¾~ÍòÇqxÜ:ý÷M²9‚^¼*„k+Õ?X ù½§µ)ñp€ªºeaœÃî ‚qךõ ó{‹Á‡g§M†¿Ï<¸ÿø¼Çyyi¨¹<ŸÌ8°´zi!\I9²}a1˜=Xaxl…˜o·9yëY¨¿ÆR׶査ÿ¹È¦jÞýµvÞ80B~aâZУ˜×£v`4±ÖéU6ŽÇy_’º"Ÿl¿¿(6àQ!¤¾ÐUM +†ëÅI‘c::À‰Ç-_9~›š{;,îÜ {좭]œáAÛ¾Y/æ×åó H¤áßޗχLYÞà8w·˜=™Oš¿oî3´ªëÙÎÑóĸö3Ó퀴^iù².®¿§ÛÇÒ7èpÛ±ÍbOGuÙiU]íxšç§ÞFdãèH*Dt× _-É'ùç'ÜXPóGCl¹y1´-9›ûâ¤êtß*Ò_a³V׺ûê¡7Ÿ·¸Cʳ¯¦ qÒŽ£ÎìOQc¡ßÎ9ŸTœ, YEàºiç®Ä¾Åpg|Ê…å·Ýáç4Û„Ò·†pQ~û`G?»1Ѿ(#^Þõ–¦ýyg.hýþy¸VUšïÛ›å»ûÿÞ‹nLæ“…ÕÊÕ¿kƒË~á½IÍŠakÝVkŒOxAaÊS"2†Ä+ §Ü¹ƒéôrk¸æžµi^[Çβ۲´Ló†…µ­ï£o<;C7n`ªôpÝAüž‡L œ)šzê„/>Má½xÆî%9 AU ñO ‚C²q8N¾Mý‰×çLZýø;%Ðë´×ÚÞEp>¡æ‰®®ÔSFÕoZÎ÷q}ø÷<¡2îû¦‘žÎÚqÔqýMjåv»Æ/Ÿ¬=ó¾WŸ· õYeÉâfEðÒU/燉Ðo¼:Ù›Åéeõ|`m“‘gvvñЮ{Õq‹ý̸Ûå—U>aßË»Òa£¿f|*—”UQ'·¹‚¸Ã­¢ÛáÚu»Oο§Þí&~šÓV\°Ü°šÝ;”c¿Þ¯ì»}’O~œLu?‡?_ÂPÞ+„‹?CsÝ Öƒ~^ýš„AÿÔð­õîy›öÌ»{æø ^ —“›µ*VÕöý«üýjó—úªÀq’Ê·—Žûq“øÏ:ü|ß×[`uqòõ…— alÄŠFß Ë)౟ûBõT—U9Á!Wgq™>œØ÷`îjœ_û¤×ÈÔgó@· Q‹¶Ï—Ûß$1™•ç߆Ôáƒ‡Ž³-„i.ö +àçAp¼§­ëä;À¾?¤¹Ï êkÇÒvpóÄLúÅÍaÂ˱‹wúòûì¼@€ã|Ôióí󸛤㛴²ÙÅ·aóäIKºb|]œøúúÉ)°©ç~Å}«``÷Ñœ@¶a@•}4|¼–:|Õ›ÅPôãê®-€Õßæü\Û°øY¹l¿I„ãlWm;ßì&Yöza¿Æ=ïÀÐnyAÒÏ`áhÿÁ| 4Э¨|;4û–¥æô‹NNð»!ý ¬¸hW°.ä»ðsðýŸtajÑ6[B'ôÃX¾à8êã÷Â<²í*]xÜÝÍGWÈ)€~#\–Üô‡š ôE¢½qÖ[è:Èc2Ôo&{ÑèŒvM]fûÛ¬9ößïÎý)}2óÈLûį&Åw kØÓ»Ë÷À=EU7\7°ï¯†€Éçå;ÆM‚vwîýpt‚û^FÒÑ,ÿØÏÊÖ×”å‘%Š¿¶/…“ßÎ7™_‘ò°ó‘§¤ð6·nƹ%A ÑúñLµxà÷b\ ¨—ó-pž3>ñõ‹¡?Ùùö'è³*ñîó?‡¾Í+…Ô _.L1*€'¥An¥®×éw½8Iüý?‚°yÔ1{Uvc¼vuœcW ÜúÞË%ê㈖wáÍÈ]UÙÍ `QwIz«å®àßeTÝÕuGñ:ÞŸ|ß~Ásàd;íýuc?Ÿ¦÷3j’“K¬:•–Úß…¸Ðz­^¾ÈÃW~í¶uƒöñC–ïﵡÓN!`|7*%k:»!Å~ÛÒ…^.Qé·NÞ¶â.¸5¯Î;•Sºž©·±¶..ÚõN­¶þ7>ÌŠ† ƒ„¹^gV±xÄ~<Û9vØŸK²OïöÝNîÂ×Cù1;¢óuJrðâç=¾À¾¿!‚áu<úßX å—4 J€—:ìÏ?^Hÿx!ýã…ô¶ý#ãŸí“zY ä¨4TÊ“XŠJD t1¡E¨(T ª%À— PÙ<ÙMP¡¨$” e€É/FÅ£”¨j”1J*Eéaa°@ÉQi¨*” )*U€ÒÅ¢!BE¡RP•( *•Í Š *•„R¡ °ÀˆQñ(%ªeŒG†R JQzX|,PrTª e„ÅHŠJD t±0‰PQ¨T%J€…J‚J@eó¢e‚ E%¡T(,bbTA óœ¦A”óJý¦8ß•rï-¸w"åºZp/ ]î—˜ÈeÆÜOZ‡{ÿ¤pÖ†œ3[ÅÜçGÀ¹ÙœÑ*ç¾>&ÜûòÆ,8«^‡û*8‡5”{Apßêm(惄{ 8»Šò=)]É}óâ¹gž€3Ï«ú2>UgS‰9ã\—{ã%qfg÷ ¦Lóxîƒ'ásÊSp~¹÷¼£¾ÀRΗ2æœrÊK°à|rÎ’Já/÷²p.e‘qÿ:êñ+å¬(cΧ^u"Î= l()÷ñ­2v&eÐ:¯óOMüÿ¤&Ò¹‡@À¼$)çVÀ}$Sø„‚òm³QÆœ×Fýµ 0–åÜ/RÄ=µ 8ÇVÆ™#&Ü’²FÄœY+ÀXŽç^Î^£1*{?†rlʤ•£*û3ÆšœshEËI|Ò"æÞŽÎ)åµxTÕ`æã¨ä<Ε•b,gsNZJ5œù4&ræ‡1÷g¤1ç¡UdžŒJÎöHàŒX)Ær6÷¨å¼3Ê„Màüê·Ho §ý3Oügž¨óÏeÌ¥œgJ™ïÕ}Ÿ+‘³¹$œñNYÎã2à\÷RÎtO@U3¯ä^ð¥œßnŒ…?‘çk™pN;åEˆ9Ÿ]ÀYZiœËŠ*åœÑPÎb7ÆE"YÈ8+Ë„3×u8o=‘¿@dÜǘ¾Hè ú„æ‰ÿŸÖD a#üãüR.K(*3Y(ß·€2ì8¯Žú‹ 0–ãQUÝ£ŽzŠ—rŽo(g®P.]g­H8³×c9UÝ›ù…+¹W¸¥Dq.o6gòÆ£ªú3Æ\<çðZ`,§p^Š¥äþß”‘¢â¹T5mîõmÌ™qtR#CpNœUI¹ºœ¡K™'&Ë îá-á<¸jº¯È}»9ÿN‚d¨îÑÅyo”‰›Èù%Ôa/ ãíŸyâ?óD¹Î?óćšhÄŸý}(£Ä“VŠJDP®&°…JAU¢˜ÐT*›'· *•„R¡ 0ÙŨx”U2Æä—¡¨R” ”•†ªBaa¢Q(],"T*U‰`Ñ PÙ¼€˜ BQI(Ê ŠR¢ªQÆX`d(ª¥‡ÅÆ%G¥¡ªPFX|¤¨DTJ ‘…JAU¢X˜$¨T6/R&¨PTJ…2À¢%FÅ£”¨j”11J*EéaA³@ÉQi¨*”8)*U€ÒÅb'BE¡RP•(? *•Í ¡ *•„R¡ °0ŠQñ(%ªeŒ…R†R JQzX4-PrTª e„ETŠJD t± ŠPQ¨T%J@÷Q ¨l^leÆ;£E×¢ cªº0ÎeÁêqnGgÀJ± gsV‡ •ÂyfX S8ŸC†Êæ\ŽD^°e¨Êyå ç¹&ò"Nùd Î&“rÆ=emH9LÀ¹ö*δOä…^6ˆqìu9›•òëM°ð+PºœAùb"Ω§¼ çÓq–˜’sé£P*ÎY¥\ Ê¢7Á…¥Ë9”&âÌy]ΛWp6å§*QºÀØ©ÔÕ¾‹txMü³þijž¦¾ýGuMSËþ¬aÖ­?kÖÿL­ú³NijÓV45ˆ~ÜÆÆv¡œàRÊtÁ8H  Îf¡,)gúã³Nä/j)>ãlTçó&ôg^1>Ë4ʘÃγQƃ;.‘¿¼e¨‚!Œ¢à,ÝPÎ…‹GU¡Dø¼’èKŸ‘ U0Š1Kœ}:†±Û(ßöŸùÍ?ó¹Î?ó›‡ù ô祌5cLZJ*Eéa[ ä¨4TÊZŠJD t1¹E¨(T ª%Àd— PÙ<ñMP¡¨$” e€…@ŒŠG)QÕ(c, 2”UŠÒÃ"a’£ÒPU(#,RT"ª¥‹D„ŠB¥ *Q,(T*›T(* ¥B`±£âQJT5Ê‹ ¥@•¢ô°Y ä¨4TÊ “•ˆ*@éb‘¡¢P)¨J”‹–•€ÊæÌŠJB©PXÐĨx”U2Æ'C)P¥(=,v(9* U…2Ââ'E%¢ PºXE¨(T ª%ÀÂ(A% ²y‘4A…¢’P*”M1*¥DU£Œ±ˆÊP T)J ªJŽJCU¡Œè¾*U€Ò`¿Æ)ÓÅ¢+îÂX®•]ŸŒ2\)ç^†JáŘòí PFX”C{0®=-ÎbTJ€E:U€2áÌ1Êe¥ìúRÊgÅ­@UöaVʪ×å\±$^ÌeœM/À¢.ã1#Σ¯ÈXô ”.úÐAŒ?¯Ç™ª”;/Ÿ„ÒÃâŹ`œ/¯Ã™©”+oÌ`Ùœ'/GUš0>ª|$cÈ‹ðE‘„Ò×Eg|YpV¼çÄ'q¦åžf£ô€1OÀÞ:ÔÄÿ»:ø÷ø¿Z÷þgjÞw½û{­ûŸ©s¯q"Z×0iLb p–°Šr…1þJQÆ{‰¨jƒorTeOÆNäç›`¼)Pºk2”ÆšŒÎ³PÆkrT)gý&¢ªéÜ‹³}%gJΪ“¡ P&œOW9ˆ±é(]Œ³PT)JÄ9t”Õ…*åì¹T5JÂYsc)(=ÎàÕÅø E•¢Dœ%Gy»Q¨RÊÜåÜ8ÊÖUü3Oûgž¦óÏŒ›„Ò£{O¨”.ç¾–¢Œ°¨‡¢²9ë5U…a‘OBéa¡Be£ 8Ó•rï-°ð§  °øËQ•C»•òíu9³•ríMð…*à<ûxT• ã³Æd { |Q¤  ðe!GUŽfVʪ7àœú”ç®R¢›0æj°÷¯‰ÿÑÔU×4õìïûPÿ;ö þgꑦÑw›HÀøÂ•ôÜã@AÏûð¹WÑ{øÌuñy‡r°ŸwJŸu(>ëÒ¾Œí«èϾRú|–¥(>G üÀ’Pzøü¢P*”>¿Îà•£LðÙ%òºŸWš céêásŠB©PøœR87WŽRQe|Üæ9ÿÌsä:ÿÌsþæ9þYÒñt1aE¨(T ª%À– PÙ<™MP¡¨$” e€É-FÅ£”¨j”1&» ¥@•¢ô0ñ-PrTª e„…@ŠJD t±(ˆPQ¨T%J€EB‚J@eó‚a‚ E%¡T(, bT½¾B9—^„/„T)çÑ' ªQø‚HÉôb|Q¤¡ø²ˆGU¡$œ5/àœù4”_ ñ¨R!å“ãJùÛ<çÿî¼m—ÎÿóùΟ5ìÏÚõ_Õ«ÿ•ó6ÕuHвÀX`\Ä£ªè>ÆBI'Ð37z?Ÿ½>÷(” eÏ=e€Ï<Š Ÿ¹Š~gŸqÝ â/oýŽ>Wʂ޷ÃÏŸ£U‰ãsLC ð9ÆÓ{vø uñÙIð™)éwñ9ÉQ•(1>§4”ŸS<ª’Šžòï,ñ¹(=-6çÑá¿céßþ]óg°¿î¢ZjC\r5³âé=ñZp¨“÷üÁ›»0VÏuß²Ù ]¯ÉˆÅ£òµ~ î§§š¹ùIaL²x}~™Ôº|ÿb˜½ vlþtèx'îfý?%ŽónÌûíý¹„ùkݧ¡·Ýürœ^5 _ïëù(!°¹ÎØ’3>Ž`­ˆ®…µ‘X šõó¢ÍšÚ025;òò.7Èõ£¤îÏ…ý»f¦d$~ºA2ü“¥Žö÷àƒQÕ¡¯Ço‚~;ç÷*þÅ¥PÛO­wÑp¡AmÇÜ1FÛ¿úƒñ¨­O~Ó³úÀ ²á®`ýÊí÷ ö¶ð*kÙMH-úW‘ë3,£.z;ÔvøIÎý»s/ë(bÿîÐö/Üçû‰v+5|ƒ<ó¡Î’÷àˆÃ³Ø ]or¾Š÷e™Åýií¹oD ÷u÷V÷#Â~6<qùKn¹]Koöç>÷¡:ÛÔÿõã<ø,° ›RO ÌG7’ûƒXk?'ÆMcþR쇺u<•CÌåÅ¢à>Hß¾õß“-­¾1ÌŒú½—Ÿì=›sQL¡‰hoÄ o_ðOyâ8ÕÇYÝûñè¤ær<‡èº¿þâpÞ-Êìœûjš§8¿qƒN]êDG~‹äþ©}`bäþ€}¡Ö& Šàû¢‰‘:h}‚ÔñˆýZí=F–C¦L’ou;sNoßøkÆð<8vkIÉ/ |tkø°GX$ìéù}Îý¶„ºgµ=ºÔ¶^ S‡›Ÿ°»1÷«×ÆÉ êº×ú*óVâ8ïê$Ü˼~Ä]_ÕujÓ°îZÔȵõò ˦“ø~_ L¿ÕeV$0.s_¢ñ/½Ðc¤ÔŦt¼kÕpvSØXzúÒÃuM€q­C¹Ÿ 㞪pœÈöA‡.vºN`´ê¼ï„0dé$Ï;%¹Ü'XíÜn³s¶–ϤynÌ/ÄU;Ž:.=+Dkå…ÏòʲɃÑÇ‘³Àö1Ê·ùŠ\Ð;ÚzÚêpw“þëÕƒÔh­om̈6­ê¸r}›‡S\q|›±øÄþ\¼=['ee}“²”C cÒŽÃ_r¡cÎgwÕfoÙÔn1dÄr¾’€û/…rŸ–0è©§lªjŸ¦ulRÐŒnú0¶[÷«b¾Ø"§IÅñ„sƒ³‰ ÓÛàȧî߯vÉ…OE^«eÖ¾1~a«1‹ç€§Á ó7óêACŠ/ÑJõó’FB'5@²笷bA OñsÛSØ)ó&÷Åq¨ËZ×%׈AJyŸ6aêYOÙ±âX¯ïÇ{ýá¡ékëÝc¸¤ñ2N«Xý+¿´¶(Ä ž‹²z9\h ʳ㗽ÿ,§ô¤}6"?ÎO²bñ㬿Vü¡ŽË5²aÿí«"‚Îö¼-{çÞKƒ‹;wöŸuoèNœ &-‹KjL‰Ø`CÐÐs8eN CmaÕ-ÓsÙ‰õ¡Î²s:¶m6ÿ(79Ÿ|mŽcizvóÐ ×ÈYó-M_m¨íP·`Ø(åîýÙSàuÐRÒsj„Æ‹0Î#÷¡´“„ò¤ugkƒsŽÛ!çzœC#þ‹ÿ¤ÇYøååÓÝï®öÜBm¹_þÎ9q1m\t¼žTsÛ&Ûˆ2¶ ‹òý¬ò¦Æ=¡så÷e­êºwG÷ÖËhÅ}‹í9_†ùë©pœÁ«×¾Ö¾J¢bö÷×ñì¼çõËku¸ºÞû÷EBÓ>Õñ‰ö„q´Cµþì‹nD|´À…µ†}w¤Öµý¼8Œç)ãêxUˆln7<ç{™ÿ9mÆi›GPßt­É¬9ðàtï O|߄֟OûGÃÏŸ_U§­¬ãørß'W­/Úfx¯åÖløRß;Ñšûîâ8Â.£gøÊ"yð½çä…àœ%%Q\‡çÕMÓ{ÁÒŒ"åšà(øCÁBÒééò{g:h9"Ìßí«pþÙgÙ3"u¸?¥%¼—=?]ÍüÑD8N—¾ŸŠ³ˆ:’AúôÒ3F§®C]·ÖcT­= ^ü)X>R"„c¬ô9'Óž×KØÑ½vÌ’Ö…¬ßOÂ:jãÏñüù0þ¸ÇIk7bXA&¹òeý aî#˜Û¹Y‹=s®ÃC…]ëc{ܸo]Ö7Lãã¥ñ¥ŒKóɉŒø dþ…„>ƒŒûÇë3ã•Ëqœ‡îM›‘Iž¹ ¶uÃõ]çS&^‡ˆKÙÞ§W¹uû/ìÌýÒ»/dʵñ–§¢­!áí =¿2?h}U›$-?ôi\^¢J[¹£ÀqôÎè1zx&©¿ûâš½#ƒäÚûqûõ¯ó÷¿;lšMAá?A’²²éÃ×ÍýAP¥Ô¹²ÔbwD?l][ë×UoPÁé —¿Ì?”8Îd2þâ¬_D«v} ‹¯uó,͆µõ=~¶Øä /æŸkÓ_ ÌÿÕœ4ð™þuæ.­?«kµ97¡÷ß•À3óžª;Ì[…ãd7»ÍiWÉLüôÕ>ê1Ômt8ðëÖlIÕò_åÍýmåpÚ^·¼ÅD!)Z³Ä¡¢Â̧P‡uWèÖ+­f³ž.ô±qóühÛ.¾¬õ1¡·7çÕ²÷–Ž7ÆMÃ6¤gùÂyÉÁ¦óÜ6nÙð»¤xÏ–8)6«s"¤BëÎyÑA0(önä½öaPsF²MÌ¿UÚÎû}xu÷£eóŽSfJ ¼Wˆ"„’«ƒ#Ô6*æ=q?U%åÜù°­Ó¡ËW ›9§÷šLô‡±[æšÌýãZó!kN=ÐFËéÒpHÔùƒã|qªÎ±¾Wˆ]Pf®°¹ n ýÜúwÑ58ôjë=¿!>ÐsÄò#Ã6ÄðÏ{°öóg èc¦xqßD^¯sv’¢ <¤6èbÎib>ûR§|Pïߥ“'3ÑKË·”Þi½ú„¶Ž9uÑÇœîÎ6¯"³x}iK4>a,žì´>nŒW[G[Gÿô»”ã8¡oÆO²Ø—NŽß$ˆ«‚ ? ò¸öž–ˆ\|àcǵïì_εdñ8¸Š.üýlº{÷Nq5­fml6èÜ‘aÔ&A7Å;Pü˜9å‚Gã詆ÓIvLðç[N*HKü^Ò¤î5Î5‚ùY¯â…q3Á£Ò²nò|1™—ܨiæÉ hý¬¨z±‹/÷%nÄòû;ãï´hÛJ%àøëôÅ™*{>$é•«P9j\¯Q ¼aGóüþu#àºóf‡Ú¾ö„ÒBZI@wH\ʹ-@ÿÇ¡Míîë†cÊ|ߘ_¡ lj¿øeÅBªjšÞ;¼Fo§7µä*Œ‘e†-ö€fji¤Æ÷’L¯~q£ÙZ0èQ9ðSüîÃßôŸ;þòHÐ9w¬÷©9àܼ\¶ÒM(œöºâÒÇë‡Õ1=­¯(óõä>Ö8Žß»¬¶oÞŸ'½]mÌ^¨à¡Ã˜ñ›²àA7чðJWÎÍ ‡qY&ó[62%C†îÔ}2&Úˆ“®Qzóùð7!ã€~i×®|ÀÇ¿òipœ‡dGjvœ#Ÿõ«3ò«À ­D¾W,‰šßV_àÝZ_m×1‚¿wMIµq«;¬¿*ëä4Y¬GÿØã—¦‘äí_6loY~¼{ S÷[–íäñúWö®ê Œ?’0Þ··v}¨Žo짬xûÅìºi¤u§ëÏ {ŒìºèµOI& ‹,yØÑÔø´o³ùïß…h8=ìó÷ˆ-ÏŠZÈüu|*Dù-óïÖ6;KlξÖûm\SÓ+f^œ ®j §ì„iŽëDƒc¹^XxŸv¼î…Ãk»4ÃůÂ@Ãß¹’ó«ycþÑ1 RŽŒbqã0>Ä2'öŽbàXçGÓëóGdÂåá‘å{~œÍyt=8·M'–¬xP|Ë&—mñ2®Ã絡éá¨n³m¹o'ãýˆpœ9Ûƒp sšL[›Úa®uÌËË[çþ6ö ›IÆù@ò\ 6Ûœ}K†ëtàë‰ÖX3Ž:®±¿Þ1*¿”'©äñÒ…‹Œ¼Ë`惛~#eÀ³ˆ½w—ló„/;\¥åRõ]tKråž„Ïû¼XÜb?Ï=Þ%•dR]4iôèÕïÙð½s½Oå o¹ŸfƒfþÔhIxÓ!C§C¯½9Æoë‡ñçÉ|œØ_7Š©mqŠt¹~äQYd¬ìb™¥g™fd¥öðq‚ <®äÏíÛe¶_sÒÊÓFE¦þkösw LÕÀ¾ƒãlx˜[91ãQ/c£Ê`‘Á¸ Ë»dÀÒ–-—û9@35pcçÏõ"õlŠv?ǵB7bAÏ![ÏÖ»¨•­þêOŠãÈn~ßàw2…T¬oqkAÌŒý´äÇ(º36ãÙ{XW·¬‘QI8Ì´ëßuùõÑ$nµóé}{= ÛÎ×S&€¥vu\ûVˆøl¸` ©{øå³Ù[Ë@ÙP•ž|ë €u`îuc¸éDß”3aÐkãž5—ÆßfiI´ãÏÇŒ"À~6Å.pÝmqœô¯Gw8Ê â@Û«½Ž]¤ø¨„qAŽ ^3­]覙°!‚V,ˆÙIyÃç§j9ÊšÏYŸØŸª_“ŒŽ’ôÌÎæ;ΕÁã+7%-¼µ&tÈèæ¬õžå^ǽ‡Äš¤Õ?c™b2¶RüõÀ0Ðø¯FØê¶öZq˜lÁ•âq¶2~ÇÁÉèE‰A29duuкKeàcî2dŽÓÖ¶~áoèEíÔƒÃ9ÇBLØx! ÑsZò[(«u\•=ù—Pó7Ûçÿež Çqô §u<Ñæ ©ëv>nZf<ºå3úf·+¼Ž¹‚éøà{YCù_¶aëU/ 7Å3ö£—[øôëðýDñóáèàÜ2pnÖÿäŽwé0Þ°ãÄóÝ´\SÎ"&ç-gß¹+Çðséâ¥Ìw[‰ý¨ñµ÷úvé\T›z›Þs;Ÿ;Zé‘#nZ#ó7%l=æ÷¿T]še·ò-ÿ¡~^*ì¯dÞÔ^–f{Èã铚•Þ*›W÷ß._œ} FuØÜÆÇ_ßw&“Ô€òù°~„2wvU8è߉]¼ñ›Å-¾ßغ¦Q¼ôi6¿×ñ«Å9K³_:ï"KCž!÷ÊÀŽb‚ìÓ¡EçÕþæî×ïÅù]øzó¼xW…þ$-ŸùGú¶ª»fÆ;Ë¿ÌS8Ž“²n ‰öp\ÿ¤ š¾½t¬u·tÐ3lá'Ìræ7`œ‡ö ÙW o£Î>ÚqÔñŒýý®ÈZvmZÙ·2xº[¦ E /î?ôê_d«õó÷Þ<`„aŸóCÅÜÿŸå›Ç©ZaåtïI Ù߯²çÁ/el’9@´V ?ú¶ïòÁÚNÇõœ!ÓÆ <YÆ9óä|Lw¾ñCèülåÌ£BÍþ-ß?`ñãï^ï4܇ì:|´ÔÇßé¾lÆL%|—¶ëpþ®-¼&7'o †É®*G­¶%ÎòwÃglw¶®pÐŽ£ŽoìOP4ûÁMWXrônŽÕ¯2x•¹œ¬+áÄ™ðÃÛC f‰þ÷ øR t®º/&lßÓƒÏãØþ­Ž…(fÚpgß³àG혲›õžÅì5í­„'/u0œ@oã„Ü×epåòŽñ‹æÛ‘'î’:_é~¯Âátÿ ¨¥õ_WÇ+ö›K_è áÝŽÇ~/àRš€ù‹Ž“áƒ|ˆ\TåKO>ûZLôí¬é ™Œã[mZ3éU]­ï=ãí8s^ÛŸá8t7û@êjhòrLüú„ѵV'ݽϺ »ýp_çÉD“l[wø8ꉢ-ûü³s}89\WÖƒ—¦¦½Ýι€É”»ë•l~"ÅqòïMk»õÕø±¾úAC|~{«ÍZ4hOàç—ö½ú(=´<¾¯B¾í.Šß2× ÌÍ×:Å^;Ž:¾±¿è. ¯To†ßõýTw/lÝl˜tbt‹¹ëιƮ‡«ioOs¢áXüp¯]e{ˆÍ+ØÏÐ ‡YkBvòÏ· zÇøP8ö2´Tm7iå µ?ýø•·ÂóC͉æüd¾z-T;ÿQÇ)öÇüæwÃçóæÒ#ïÊ ´ÿ$G¯²K0#ýh›!œàâ‹ûh;{M]!#|¼ñކGkîÜu$J›wÞƒîèv¸¸ouïräªý_øŽ*gût ZØ }‹„•—ÁÚƒÛbÖo¼Cêui>䥘Ÿ;8ñùž©Üïëç5ÑF8®ü8b·XëÏö3¾ {o¬Zä5Ÿ}.:¢º×G/ì¸&Û¼iâp¿ êj's½Ä9/–0·Îúæ·Ý€ÍG¬ Û‡²„ÍÎãžZÙjûWÇ3ö—çºk¦u›ƒŽ0_üPHsë5àZßPhOq.:Z^•†Áö÷Y}à8mïT[É?‡öDmÑÒ+ÎÍ'E8ŽÃæ•~ÝcSAÍ©ºXcöµìúã<Ä÷üÜ(e¨Ü,s{ã3p<±½3êfÙ7ÄøÃÉžÎÒ Û—† ‰vu\c?±jµô> rzPŠó75Àý<Èrl i…#¼¿Kn$áÁÖ÷®#ãè8A5xsW°Ÿ™t{|ÎhÑ ®>8]V§“å·]ÎCÓ¸Ò§ùµ@ãêdI–O:s~û`òãyRð"Þ|~»ækò,ÐpŽX¼±ùªû-:Òù泌›|² òßGön¯w®}9½ìmª˜Ï{ÌÈ4§w¸'¯÷]î÷¦dìöŸ˜£å¦ÍvèÑ#1ë·p²h¥å¨ãÇ92êp½/‰i0Sukfà2èy åëÒ¢s°-rå’ç:Ö|ذ<ÍúZÃÓ¬7ºSŒÊ“!‹C+Pm×ß)Ÿ{â8Cw…Ÿ}µÿ옲 ré†2ØÑtç±õ»ÎÁôÍ-Þ~ŸÀ÷ÑD|½8‘¼ë1óAïÉÚs6Í8êø¬íZ«»Júô<Ìhn&º¼¨ 2)®qÖ9М/ø.v®[g£­sKÛµ·­fÃÏg­Yüb?+wºWZ^„‹yz-Ž.ƒO¤VÑ^›süœg6Ö=ºt ƒæ½D‚Fx›3@ Ÿ‚Z×ìÒÖqö^g¿¯ûõÈî–¶öä%è¨{½vÏÐ2¸Ü¿¦*±ç9-Rñ­üÜëý9ry°Î§í†~²•k¹Qšù%çDóßóŸq–Ç—aºzCã/öQåå¯iàYßÞÀòÁ$hd5F’ãi7æ=|qÓ0®”³–Ÿ¦©—ªzïo’þEèè$lʳþ gZŽãÔ8$dίڙ(ö÷•”A{÷¶ôrô?¶Imc 1N§•†pûÛëŽÑ‚“Úç Gߨ_²ROíºŽþXûÄþ2/a”&šÞ&>nM©QÁ£aYýÏÂîi û~‹Æƒ ¿¾¦:€°s#-?gË‚œê¯íXžH±Ÿ§í:º0ov*mR¥‚²’ëf7; l?h4ô1l{«ëaü•P=M5ô…±æÍôGôýËëÜ zcpÞßM~faû¥vdB~ÿˆµâ8ûÔ™å·öÃÛnƒAæª&¹Ciãz8uX½‚ƒ¦/N fuÇY¾Æ6ûæý«pôk‡7i>*è°FwÎÐg ×íF`-3‘Dsà.Ç#m½àt¨ëâ<þÞ® úïÞu­Ù –SŒð6¾áüOçó…”aîÑ×à·`]—kÎ* f©L%gÀ?qú´1C¬í¯D±;7uNËG4u›­k̵ûç¤çÝLÜ~ 5Ü#‡¼®ã8ÃËwµËl ö“vÈ{š© à¢ýhY‡3j2v¤ã[PŸ® ãóÎAd!ÅãµqàëW\\¯¨7¤Öw-7µ…2ïIÖ10Ž<GãÔþ˜Ð»s\6P:Yˆ± ò»ÿôõÞiÈv?6/d-¸¨7 ¦v«¨±´M¼ax¥ÕŒùÜ´ó$ 7®lž²N][èã8¢íüûl%ްRïÊ/ÙkÜ[(좿ÚÙíaçiÀE’žá4kÎ ›Nxýæ|N+-?nší°Îsò@ë†Mám-øþ.}êæáÖ ÞF5dç@*çìY÷uu"¯ƒhOÃV'tU{VxdDðih¨Xu¶j"ùêg4ó›I(Í´]-sͽÍ>(/o}ïQ˜!ë¡{ò[`ë0 Ι2fù\!ê|»Ãé+Ÿ®Ã—è•ro<†qëR#‡Ãi~¿f0:y[^SJ^`ó9Ž#ó0Ñ[W/N^jƒ¿ÒcÐÿZ#òJMõñôS ÷ž4º¸ÞvÍ4WÂÞ'ÁPhº9´²¿?À2±ûÝOÎC iµ“eþ'_È|;zɵyì^„ÇQ³ÄæÂú•Û}ó^œ=^çùÊT¸r¤~Û²æ nÙëëèÂøž„÷û‚òD`~@°=ÿ¼ZÀ  ³I#¢Zƒ²+™ßÔù/÷ò”8΢yÉù·rÒ [\z±ê‹—©ép7½¨ÑdøÞ´eSûªrnyÖŒF}ÍIŒz"à í¿]|rh¢¥–ÿ9ùºÑⵂ;®ÊZ¥L†¿¼pœOñ…á=óàK¢EÐð5`Y·Ôå{Û¦Bt¸ Vøù^»•},ueÑœWö98¶ÇóLH˜ü¡Û¼,Kðm_fñ#æ›0j{̓¾6:ü=aÁ9…ìþ’ÎÔ ÑÈæ½ŒwÜ΃… ôÚ4ë}xuú)XkXðÔ4Ó’ÂÍ{w‘Ì"ì^ÑG¡õª‚ü’WÎÀΫœaóûýZ’ja®êÑ+·Aµùûm¨—¹aýXþà8?¬lÜÀì&´Ò•lú²ÃÞ׺á~Šó³qž†Qb­Šä爆_ær¼ëÞ:¾pyÝÖ~ûN6àüÂá«,±Mêa)°¼f÷ŠD8N¯Ë÷ì¶ï½ n»-¾†7z×~ŽXÞàÝI˜d·«¼™;LHíä0;`6ÉÓ7(kÄxrÓþäöEE.|=+Ñžªm>Þ3¹6¬Y}x­g+ìJ®ù~$ŽÓa{ü¼i57!¼ß»ä¹!fïGVKNB‡ÌK•eç%0Á}ãø/¦‘„óÁI×ÇûKz<•ð{ší¹½æž”…ø§Aø{~‚ŸÛã8l¿;\Ü(Ñò!è:õ= ¥½zz?ÛnÕ»˜›bIø=ZBéï.‹ùºtÜ©¹|ïˆÓá„c¢-¯îV í>÷ÝÑ<ߌŽ,8NÕðÛ[UäCI\Û¹õ‚BñD}¿§'`ÜDŸ¼³Ÿl À±ÝÕ‡‚ÂÖ›£ ã×NÔÞëÒ¬_غh°õ4«ŸJìßdg¾_j ãfn.¿ÿEÈÞ_…éÙc&ÀÅèäqÓ—°qT8NÀ0OÛÏa¢_sïûHý>ÔÄ}ù 86hœpÜzzJ·6oa?ßhÍù¿Ç0ž¿ß¾j¹š}/vŽ6ˆåMH…èý d,£íÝzf<€œÁ™'L?ÓÆÅö?3 àÓ6¸îEø¾/¿¯b 5Ó^OyµøàO~þ¯MÛž;xxô_÷×8NÙÖÑß½€úúüº0ö›íº†' Ë ¦éüc Qæhåú Àøåf0²/ý„üaYLúÁ‘Ñ>|žÓ:,_<õw+¸:hÝX³¼P¸m²mµÎtV?E8ÎaÓÄû—À@»KÃRœÀÜ!±/÷ >Òðf;N6ÓΛØ<¬/¿_5ƒÏÛÁGØ|Ÿ©RÀ÷óô@ç÷†>v_"áCûѶy²¼Áq&+¯í®[kO/±Õâío:Ò=¶_ug"ìœûÌ4f¹¿—Û|1ÝT¯å|¨ûTU4蹫vÅæ‡-aõ‘Ÿƒ ºýÛŽã¬2¹tÞàBx–z°|{é} ùžg¥€ç§ó/Œ´Ý÷­ÚïAØ{{(9F§¡†Î ™'²ûkÿZ7^]ÞÞ:£Êø~ˇÝo(„s׊–Hº}ï¥÷Ï\“2‡ö·¿yØÃÝaã’æ¶ÿÞXzÒ½v0$ø²¥0r¢vuž`ì^L!œšã¹äwÄ}¸=ÿ¹‘(E»Ró€^¤“óþšÇöƒNåûùCíÛÉž-‹ÜþÂÓVavŸ”פY…~iÚ³1÷au«ûoæëFO>4;àÂÏÉæñýÇ–™®Âˆ›v¤±û©³¡É­…&Y[> 5ù§YW°ýD6ßÓ™V!Öy™—ô­’F•OI«uÜZN–¦{&Ýí4â»Ü.e¼)H‰"ìýÐ[˱¿6ÉçÑSk8ænãÐÚÿ³Í‡> ÙúÓ‰s¨ÙüU€ãì¸4ä^r÷"HœÞb¡2çÔ¾X¯E+8ïªVÝ™ÙÉÜæ[éüê=‹hÞ§¬n[ó÷ùí8ê¸Çþ*m)PºŠñéÍØrZ•U|‹: áû¿]nÙL¿IØý:3èLñ¸­­5ç9,®±gõ„¢f¶õY>3è¼Ûë¿p9/Í^Þø‹ xnïáôe.áûÊÿÃÏ¥©ÏêøÅþâ-&_HœRôÒcÕà{`٦⣨ýHœQªÿ¤‡¬ øÞãÉÑ…„½ÏFÀÛÔa] B‹Òí Íü´ët͹3Û§uÍÿ_¿8Žaëß³Ç/.§G«CݪîÂàx×è–«ƒþ¼ÄðÇ1\<ž³8‡,!ì~Ía»ß§WHù½÷ÿaÎîźÀ¯e‚.9l%Žãó0j¶ÿç@âBÅÞ»°ÜÁ>·¢Ía8:ïLçQ6Vü{ K»ÿ2”h~^;ٟ÷TTØ_›ï†Õ½Vï:Qð]x?ªÐÿôñdhóªQÈ¢¡“ ©ú‹; ;ÿ2#|ŸØ9ƒh¸ö†ê ´.ðs…bÐú$öóêL¯IÝ¡&0¯Fu‰(èÑñ.t9aòõ¤E2˜êWVYÁŒÞÏW\j;ŸðyQOû;I9ŸÚO÷½Ý»ÿ,dßñõÛßà8êk6¹EpÑ#s¶ïíRXã^­Áù‡ nƒæ¢úÙÁ„Š‚-澟Lξ­8ÔìÜ$g˜¾jC_`ŸûkaÁwËۛݫ„ìç¥¹ÇÆê;ŽÓ¸ç/£—8Î&¯m«6&–‚× »Ž_‚>ŸLwæßŸšO:·ìWfAØÏiÃÒŽö3Ð &ŸHÝ¡SòVÈîy}²ùݾÏוåŽOËaAŒm¶ð[_ÿRx?uþ•¡IÝ4Å<ÝÌ 7ê:þÄï7ÛÙÝM4çsšõÛ«‰Œºì´ê“PS'ÿœ¯ÈqœÀ¢/ ïAís™kt-º»¢v^¹l,Õ›-…>ôëÛâx]B4çpCÂýb®ØC¨ø„`áoÚßG³ŸJo#Àæ­ #Íêx]5tû´ôTmÞÿŠ×ðúõ-¤xµ¼Z±èÌv«„ŸWͽþÙwî&ÌŸ"æ÷g_ ôy—ÒðÝ÷6vÞÁ>7%Žs+Šn`C+ÓíK[&Þµqµ-wNØ^žÞÃTc}àóï "×¬Ë ûÞÃ(íú‘Õµ[ÂnÐïÙUá!{Ž&šï°ùŽ“A—3ÅP˜»~j#gÁæˆwcöÁ ÍÖœò†`5è9Žð{„íÇCäÔ-M®}ìÍ¿¿”/dóþRÎÍÊ÷CXþ̨9ÐiuÛb ³Œ6ï@;åþ£î6{aámÙ£6…îkZP˜¼i™|Ÿœ,Ú§É÷üÜm0?W{ ü¹c„÷p«baaÊS"ñ}=6à8W[ Þ©F&N–û|» Zäœ8<\‰SXl?ïK®¶ÄOt!a÷$GÖ1<øø3hï¨þüüõ¶pªÍ¹N³òo of4´>8kô_Ö/"gGËXYç¾Åðô`þÆ·á]V솑¶}GŸjéåÊÃÏnZËù¼eÿžG‘íÇ vד厱9ÓUX1<¬úñ½e·aÅÖo“Gí†Þcd‘ÝíÀåm½)‹»Î%ê¯'´›D4¿‡æ½Êî” ù¼æ/ûérì?\¶öÍ(zJÜÁâ6´›ö]2åŒ-—Ý´uÚØÏ#¬¹öœÐ¦÷ñžöüž×+!»gðJÈî_NäûFü^ŽcÖuÚS[ÇbpÙ8×¥îï[p¢É…‘LP¤ó¨vÃå¶Ðòã‘}­˵q¦9ßcçv`÷tÃŒ~_…ì{¦„ŠåÎ&×;¿sêËî))qœúýèJ»Vê/3»qþçŽï‚~/Mº½KCSUÿEõ¢¹§íãté§ÍzWpH9и¯£ö= O'Øïû8º .†G8»?çèÜSˆ^8íòzÜÂsqö°µû¦ ò¶jLöTÑäTxdH'9ÏÑ ­ÝíVÒäОb`ûÝ·@öäp§i°Ç±ÉnGØöëõÄåÙšy]G¾ÞöƒøRiY÷þÔ72Ï)£(‘3õ„Mã¾ý¡œ[IÙæIÜ-Šûó[p>%e˜Ë¹ÿ™˜ûð 8‡’²Ê%Üç̈óÉUM˜·™‚ûšI¸¿¾÷2Sr_ý(Ε¤>­QœdÌ™ã•ܳLÎYãbΤ¾ùÆÜ£L‡³Å) ¨ª ó%Srž8åÿ¤p/2ê·ZÀ™?2î³j̽Dz¹¾œs ©¯ªœ³}(ÿQÁ=ï©Çåùüù(æ|<絤q–õSqŸñ(Îò¡ª‰œ÷ø§*eãýGLg‚ÿéqÊýÆ5>SzÜOLõ;™úÿÅsŠ{¦RvOJ€E!žû þÆïIàœÇ?ù=ÔO¬%Å‘ÍÙ=‰¼€ÈþÆÃ«âìÅìÊx¬¢>÷œÝ£ñ¶ÿ§ÖþSkå:ÿ~µV·£+æ<8êÃmÄ™ÁÙÜ›2R*ë0þeëb`‡rJg¡èq°Šò޸פgžPžeþ¦¡ÜS’úfS¶I÷‘Œâ\_g™PN›„{cW5g|6ÊðÕãÜ’$Îî•bÂdsõÀNApOÈlÎ_“rÏkÊ%‘s6¯€û\S‰ˆóx©ï£çèpo"O8)÷²®îÀxj”¹kÀ9#)<e¨ÎN£žÕ”¯ûw©œ3EÄœ­[ÉyIÜÃQÊ=û(ÓAÊ=)‡TãW,ù/¸º‰<Á5lÎJûÓ£šòG“PzÔǘótÿîO­â¾Æ‰óm¤œ4ñß[JÎÕp¶$œ“ö§o£ôo,Ýj” I6÷ åìQ“±Œñý‡o#õ¤îQñÿÔÚj­Î¿g­Õ°Ú©Ç°ç¤ñ ¦l*J—{+9ç Šs6)‹=eÀ½~)Ï@Ìyš”¹ªÒež¾JΖJàlu)&@6÷î¥<õJÊÌäü¨ê¦Ì«7›3£äœ›nÀYQ”ƒI}yåœeÂé”9`¹—”.á¼KÊ0ÁDRpß] g@USÞ%÷Ú5àܧ4”€ûë–rÖS(÷Õ¥Œ§xÎ9p¶åVRÝxÎt¢¼Ê$Îuf§¿3*)€²sŒ8;GÉNrTe7æ/ç 'ꙫà|Ê?ýrÿ3ŽN(g˜ÿÉ ¼gÊ>…s)åÃPÃz6À GU¢ÄœI)àÌ&%Ê‹B÷‘7ú·)‘s)ÿä6%ò¢!Cpf“â–ΟœÃjÎlJúƒÙD™”Õ” À™MÀ?µöŸZ+×ù÷«µþßSïu çRßucÎ8.¨ÍüÖ)S¦ªãþQ¦±vJUùª§p¾eWRÎ{÷Oç?Ê(V¢Œ0ø¸O:eÁ¤  ¸7z)÷EOà|>)÷B¯nθ|”9lÀ9/)e@½ª9ÿ÷ï~ä”W&æ¼²*”‹‚’óñ$c–es~¬†Y&å|L&9ª%æü=&V<ª %Áº¥D 0Ñâ9cX‚ §DaÒ%йJÂÙ´tB#Ce£¤°Ú(›¶€ói5̶PÎÝaÍJBéaÂFýÁNBéaòF¡T(cÎp+Åze‚É¬à øÏ¾Ö?óBÏy¡·J‡ñ[ãQµ£^ŠJC 0˜ãë0.½uç´R}6Ê<‘¹ŒóX)s^ÒÅ€E•¢Dœ»JÙòQ(Ê‚óäi"ÈPJ”&DJ…²ÀÄHäÜxcLDž$bT"ªRŸñS)#^—Þ=äÜTʆå¼TšDbTJÀYði(=Lª(”ŠóߣP”‘ŠI¦àÌwL¶xT=OÀ¤Spλ &Ÿ‚sO%(ªªãR¦»½çJéü?2NCQ¥(&jªeŒ ›È“VŒJDUÒÆNã|Ó(” eÉœ‚2À„–£*QbLì4”“;¾c¸K0É•(#LôT5JŠ ŸÍ¹¦æ%å|g£Œ±$ò" ãLS,ñ¨R”‹Bª%Â‪FI±H¤p®iJ…²À‚‘‚2À¢!GU¢ÄX<ÒP, ñcdêa1‘r¦©•PÎ4ÕÃâ…R¡¨ÓR°¥jéüµ®þwímÑÇòÿdëÏøíqýY×4µìÏöß½×%×ù_ÛïúS›þ¾çEë=eaÇc©¤ûèX_$uÚëJª%ÅÚ’2æ¼gúÀe¨” >xJ—sKQ"¬+I(= ˆl”1EJ„‘„ÒÃàr&3å/Ëé$ˆ ½O‡RIçV It^ŃDFÏ1Pô,‘3)ß8žÎ:3–1åÒ*0htyÐHQÙô\sŠE”K‹Òà Šú/øÄi(Z<ª %Á€S¢Œ0wPÕ()æo6Ês7s‰¥”Ù(c ÌDþ2×Å| ¥ç˜«F¬¥(lJs4 ¥BY`ަP†-æ¨üöp Ê€î…fÛ*z·ù˜ôÏ~Ø?óÏy„÷[MŸoª´cÖËPJ”sBÆ©7Æ NäMÙô( pJƒ<UÚ€1è“PzðQ(Ê?¥cÍËQ•(qÆ—×ÅDEe£ 0!ä¨J”CÁ9ò&˜ ºOI"A)PUôŸ93^ÞMC%qV|J‰ÒÅ$’ ”(#ΆW¢ 0©ä¨Ê¶Œ/G•¢Dt¿cÀ‹0ÙPÕ( &]羋0ù’èÞ?& •„ª¦ÿlÈïô;¨4”@€ÏUŠ`‚F¡T( LÔD” e‚ « g˜´”UEÿX‰2À$–£*QbLæ4”:U…’`b+QF˜Ü }Ó]ŠIž2ÆDOäÉ.C L0é(]Îú.@™`P t±„¢JQ", (Ê‚ž#ðÂ`JäB†JC`¡£*Qb,i(xTJ‚ÅC‰2Â’€ªBI°(QXLd¨lzÆ€E% ¥D`q‘£*QÔ‰O,WéŸÿÓÎóþ¬ƒÎwþ£ó¼?ë×ßç9ÿÖ©ÿŽ¹ÎŸõGŠR¢ªèž;Ö)Ö“l”1Ö’Dþ”¡ P&XK(]|С¨R”xJkIJ…²ÀZ’‚2À@(@™`0X`0¤  èþDÝW€ˆ§ûI"z¿ ƒ¢Šî#a`X``¤Ðï½b`„Ò}#zG£”îa`$Ðï°Ò}nÌcHJƒD†y[@÷0XâQ*”M ÊGŽªD‰1OÓPÌÓxTJ‚yªDap% ªQR ²l”1æi"AËP(cÌÓDþ²–¡ P&Œ ”.æ¨ægÝ÷ÁÜ4ÆU¡,0HSP˜rT%JŒù˜†`>Æ£ªP ä4”€ÎoèwP1«é¹ýî)wŠ}וžç‹ÿØS¢ßsÕì%i¾×ªÙóþsmCïýÇó½#ÍwX«ø>·”ïËk'¥ûwÍŸ•Àþ…Uˆ¾:wï™TV £ D¶ƒúçV›Ÿ+‚øÅnÅŸúà ß7Nu\–*ÁT—ÁµàKŽÏv±=÷oÕáþ{r¢á[ÌÐ~°á~3hrýzæÑ8æÿ)Åql:&íð¾›Ewž^']‚¶o.YvŽk:γŌƛ^ÖžG¦Z<ð{1n<Û²9£wØyîÉØ8Ø}5<äãi Ï6†hüÜuB_ß ß¬õ_ÑøoÓþTØŸ¡}“YíXàñ$»ÎÚBXeJ0æó÷éLzçÆÎ!ßælݤк‡ü¹ßˆ¯ÖÇYÃ`QË¿øÞè̬µVR•@³Iý7XÛÂ3ãQ§G„ƒÂ¶|RÑ0?p Y¿üê½ù„ù5ÚÃÔsCZ6yn©áìh¹g*ƵŸŒ×Âý^pœ»º™¯¾×Ãßg\’ý©… ëý±^üC_¸¿ĺó~ðXÕxý¡¸$v¯sâå;b-'[Ã7ÓŒ£ŽkìÒèváçÓ©ãëÉM®@¿@Ë ½çè“Å‹ž~æ £Û®Ú<=Ž\7™prbÒ$ˆýâq gºh¸ê¸Å~~ÀÒo°Ÿ¦û¿t½·ºì—†ö ÙêKüïyaÜÚjÒ•áïÊâÈÜu£Cº™ÛžÇÏÙlç÷lÐ;’¨õãQÇ'ö×÷Qö˜øyþºs.hˆMü0¸äßß6ŒL=½ëS¾'¬§TñB¢ñ ìipsX®œs€æ‚¢Ç¼ØU-?ký}4<æ3Ì8› ç­·k}£X¸·üÖÈú±æÌèÓã£H·»o]áBáµ9"ÅB¢ñ YÔÎ ÚÕöÖY—2£—縚±Øôðá'!ãQÙAñÃYÃËwq.$ŽC«†þ¯bèáîiÓ0>¶ß™K-SH‡œ­{¦Mp = Î]H˜OµN–ÄÂÕVÀ}Ù´ã¨ãû b3Îë‘Ú¾­s>c—5©G¶;t?¥3tPâã[€„©ÔΖ¶¢NeŒ×¨^!úõ–éÃ1 kqÆÙ›0¢Í9ëÙ‹ˆÏbÅMA÷؇ºWlJ·m±{§`(o^gló@Îáf¾Šìo¦Ú8ª\Ô†’7áÌÉÕŸNíYJî8O|Ñ?Ú¾}&ÙHN4þ®¹›Ž,]Æùá ñÍûµó”tì¤ÏBƒ×K¦­lÅù˜œóŽãœ^YÛç`1˜·¸´Ò÷\Øönd;îøròy‚ýìF^®Üt.aþe“@WpzÙúžÜoÎCë{¤ñW-A㟥Žkg»ÍÉ[Ï6Ã.Á‰‡cEy`]+3-²fIÈ©31m¾;χX­ÏPe›ó-¼´…ê'+¾Du™¬Gר¥µo.†»…&%ï®äÂ@ÅÅ¡/¯&‚ÌÅ“…{‚_Ì•Íb‰†Sù1»s“1_mÀ¨wŠÎ&ŒÃ¡À~Ú¤ÿv>éW oôƒ—8ÚåBÃfÑ­ý’ב'ãÊm¿ó‚Ý—¾GU^ˆ%œ# Ô.iª Î볬ÿ_yMØ_£ó—ö r+†®+w]½ôíl{ø;D¬Ø@nž˜iX·ØFètÐërl™ñ¾jgvp`;ˆ‡qžW°Ö·KãÅü¦'sn!÷·ÆqÔ娼¶nÎx;zÉ X˜ª7`À®Mdâæu¢Gõ¥ ÆmuœK4¼±í“'êŒXá:×#*?]t×Ö ÍßÃ]÷î)œÌy¸œ¯Q!2XÿË«Qób(Rý#+Ö¿ªec¹=‘H£z\Ÿë wÒÓe&Ì!šÏ•qÅœCÃ}аŸvO3^Ü/‚0jW¨—-ï¬ ì»|+¹:œ=¡Í’쯾Å8j ¬3 Êš9ªü­@P¥Ô¹²”qÑDØÏñîÑÇÞeÁ“Y5qS®ÃûZ¡{¬]·µ}V¢;Ì:bs`sÑp}îÆ¾7½(õãhê§õÛVÇ%ö×yg|UAÓPÓ3ý¼¯Càòw1kvX/9zð„–>zR6¶A$ X?a¶˜û OƒÉÖm}?O”†Ÿó9øþŠŒOÕ¿¾˜”ÇéNíË¢Šàå¬JÉãlHs{e“™»ÈÊõM,Ÿ|q;›Ôà ‰?Ū_¶‡¼ºç–´sƒv’kïÇí·ƒ—Öž6Ûó]¨á‘hòŒñÊØ8 G³s-‚Qjk64ïXökšTAŽ_{‘ 2r„YÎ_Z;“XÐùVÊc¬¿/¼×å8B–á½kæYiÇQÇ3ö÷RW5å½oÌ|лӑß× ¢I¶×»_ â\çQËaOà`í·Ñ¿RÃ4Îéö5ž/󣱯++‚ív>œ¸ç6©IÛ³›è/?ªZæÇšÉÓ»„/ýáî'›Ÿì'ƒÙª.¼Øäù—÷šÎ¬ Q=5P«JnuZ1Íþþ~àûc÷=äÁù#¸ÂNÏËŸ}.…i9ô]œç/òQÎÑânûKÃ@ãÇ®ñÃ.ð®Ôßû|›t¶ÇÌtÿ‡ùšA6 ‰oþú*z»Ñzä^ró¬mé//ˆ»vvvÓï3 ›g¸Â¤æã¯âÈùT.àÞvÚø ùGṄ=½7ù(dþä8§›#ÂqÜ,7­/„5!Ƕ>¿ >o[öÂl)ðí½îx]_ðYñPx87‚¤\oy*Ú.c•™è44þnšqÔqýµYôA¸º¢.R^ç« Üu6Ðzö~²x^-ƒ>nþ°:’~°‘$îÉµÄ ›¤0ðñ÷”GIæü};‰Å-ö³µG­àà»…fh#u4 ú‰ä'ü²)Ûü–/ ùYvE7 T_h1¨ÇºÊåJ«ãûëpaüçÛÊB8¢óxÚ#“,¨4û²¢› ‰¬ÏèµÃÂ8¾ ×÷&/ÒÚßݸɾÝbU»$¾o‰yØÕ8˜?ç!ó›ÿLu¼bÿ¦%suì·Â>ÓÂVågÂñO ×"y»èDÆîgxIGG»ïfÙÑ¿½û¾k¹ŒÓòA¨GÃÝúÓ¯Y…ã@èÆ^z‘…pª×‚¼‘Q™¼Jñ¼ÞêdÒïêñfÃûú@µŒ$å–cq âÍ}<'€æ=¬GÏ‘"õ2Á½ÚNÙ½£¸_&˜Ä9.ï´ê0±¨-{÷|†'˜yŒŒ LÅ9zRX6~wÀC˜š:^±ŸváwZ|0-„>¿ÞÅ7º•þ/ý½eGHø®Žõâã\¡ó€‘o74‹$Vu“çë]—Bß–óíáÃz×{èU$F% F\ÞöQø'_M„ý~²›à¡Ó¾Î,šSpÊ5 [|ѵÏQòþqÛ~½\@Lñ¾##‰&ÿ5ñ·¹´i瑚<Ð̃ÿäùHqœg&Ô}ª*€Äiê§_×~Éõ¶=>JµO=¿ë S.ä,‹œENì{0wõgÎßr„VÇOžémèšùŽf>¥™×>m¿ëú¯h6_•ã8χE.{y´>/Nøø ®@Ë)÷~mÜxŒÜªŠX-r—u(€3B[×8¯OËiÔŒ£Žkìo]>n(€èÙþG†ÿNóþyGõœ“íƒ'þ-áÜ™„ùxK@Ãóm%toÐ Öá/¾ÚJìOR»÷‰©±ÀÞwéàâTRϧe YÓ;¨yÖw˜ÔÇ!×´ Œx{4Ü¡‹÷ÕöývÔ!ÓG۟潤Ž[ì÷û‡Ú¯÷:àç;ä~ëïÒtعÄkØâE)äÔ äñä\ÙP¸¶R`ëC9Ìn^0w䩹Ü÷§–÷ÌüííÿÂSÔ™ëØÃÇÆ ëQÔ]¶Ö%Ž‘›Ï«I!_>Öz°´­70ìP’¼myÓÐò»çÇ­Í¿k8ßCŠWVýj¸@ŒÈ×q8NÆÒe­8ÛÈW1Õ[ û^¼a>ç‘þ¼;ÔÝP ¿ÍÖŒ=Ê9”~Z?fƱӎ£Žsì¯ïÀo»~>ÌÓ&o+!mwxvÛ_'ÈàŒW»¬-¤°§àäÛÎÈ¿( ]Ï·»n¸{Š›ïH±ŸöÃ(!®É_~»†ã»ž$ »ˆ–?ô†êá5w×´ #+’½_̉šUÔ1’û|†ißÃŒ+Íy0ØïLƒ\ï.áù°áшË_r/Ç=ëOþ8I‚º n°]Ç ¬G,3•> %ò]ae"‹)pmݸˆ†U^MÅíƒ# c“}ÇʯÚyç«+"6ÀqêOðºY;ìÔ ïK@«tèôSĦނQË Ü9/4”0ng\þ:Bv\hâöŸ—ož¤åÞ1îÈW!ãRˆa毞­ºªØ8Jgöb¡äê’›à?ñɳ!~•ì½AÉ)²úã›–_öº@ÝŽf·Ž %ßS—ÿ*ÈõÓæŸ&5ã¨ãûcö¯7!¿Õ¨ã'¬.Âm’ó©o*yë³<¯ë G\q¬GùÂé„­“üøóŸ OPÀãÔèDUˆvSŒPv<ýÕ­xwñh|’/F¥’œv›dŽ?ë·NžJÖÖšTzÖOë¯ñ1ßß+rÞ¶æl@€ýÝJ]t 6×ÿ PÖlàÓZÊTq }ƒ$ŒCö•‘·Û,öô‡-}/l¹êºD×ö9£¢__ ^ú]ȸ$ß„œ[È÷8¿ÇQö äˆÈf|–Ûå:Íõ4Ü衼Ïu'pJOÚg#ò#Nwg›W‘XÔ¤üî3w[Îï¶ÔŽ£ŽkìoÛõ÷$æBÌòŽ]§ÛœƒCÛŒëǦ&Ó®Å÷ùéÊ9^ÞdǧËïONÑr7󤱟íÓ¦[ÛJr!eUÔÉmíÎÁ¨ «šö9CÎlëð­ÒKÃM KFL­ºÜ'ïCúÀšßµMåMCæ.é ‰9>Üäét½£N¤±_cÓf–2`ë<Î?ñ¸4ŸœÈˆBͺ‘q?Í8ÏŒù2«pœ°™dœâXÊI©‡ß¾^’=þ,«Óý4ï;Âò-ˆût;À§¡Ò„OAN•õåw›†ÿšÇ°ŸÃT;oTÇyt…hh3J‚»KÖÄ÷^;þ,,;]ÝJçéYÒí`£ÝžûÁjÑÅcÞ)îdµî -}2ŠûLÒr>4ã¨ãûkúøÊù÷osàû©Î½»fœþaš?ŸFØ2ѵ½ì ËÝB/ÎÅ ‚—'KçxXi×çê8Æ~˜ïwt=xüùÔ3°Kwì—f-Îç”Á,}`÷$ßIç~x^iá>ûa¶±ï.å>`¿}ïêÕ‡}8ˆý\R쯮¡¨ZœM(vïËiãÔ®ËæõçÈŠþ‡æŸnîÇ7Ð qáûÁ°êìF¯m«x^øóQÿ(Lx;hϯÌBÆ©Ïyj¬žÉqœ¯y5·ž^¾v®vt= ÖÕ–YSëœ'{û9õ~âªñ'ïÂ礚øÉ`×úÞ‰ÖæÀø)€ùíjþÖüïY7ŠÏíÙÀë3Ž3¾º­©_Ïëpa^úôÒ3©p¤oŠ~‘ÿyRbû¶ÿ¶wΜÿeÃ}Ûƒø¾®9ç}2î†ûùB—G²am°¤B\EøÛÈKç ó·v¾."¼>ý?¯:^±Vo³á2Å‹œ<ÓŠºý4my|œ1f¼Eœ“fßœ°uv t×,´XâÁ믧öóUÇeL…H|ͯ~Ÿ§× ]·?¿ƒ\ÖžzÔ 6èÖb±3aVêÍjN4ûgÏ;Ži¹µ{$XÍ¢$ÙÐp Ší¥µ¬'ñ:É}ï±…C®U×À¾Ù”–RÉIÿ(¢göbÒ¸O= ç(¶àç 2ÎÕ±æ\kmÿl½8é/\=öŸ–ÓRwtÖU8ò*Í[ynjàÞNÝr‘ÔW“ù6œkmLm[Ù'#m*ç‚‹@³¯¿£{í˜%­Ùç*Çþ¾t3÷—deAzýÀR`IÝ•f4»DÚÄ4‹,yâ©ásþ{ˆÖß\ÃéÓô§©cêxÄ~]ò'½ëà×OÎßY?’’¾o¼¹ô¡¤æÉ^°`ÀŠÉ&a„éÜ!025;òò®`hûAwú¾î»ÿ^[§WŽñP•Ø¿ÓÐÛ¿n~É„Ö#Ç?Øny >UܘTû2©=±vÓá³¼øzÈ™_»¥±ÇtíûASÏ®éi´&í½Pó÷££ý ]§h8lŽóÁƒ‰2áË™-Þ[z÷s™ÌW-Ö]?Ö~7à\"u#ßôqJ ë^P°èÎ!`ù [!zæÿÄÙ´I&ÔLñ=¯ẇ¶kõê2¹ÙuF«Ôîî Æuuò$Âõ5‡JCµëÐ?9ÏìgÆ”O{Ë6dðÏ+¼æD\´'DÕŽÔ%0ãKƒœi?¼È¬Õµî¾z8¤eý|Pàôíï¼Õ_û¾aï>OÀ~×ú´žáo˜§hùí †™71ƒ Ñðœ9“°:2&Ëê¤Ô ƒ¹ê¨0müÓŸ¶m¢9hΣÔq‹ý³Ÿ÷ ¼[›çô±ã!8ð¦®â‹Ž’„ŸMx}&ÅXºs®hˆ¶>1^Ðm=ÐŒ£áühöÏÕñ«ÙŸ¼#ujw8ï ÄöJ±ÒKI¦•Ÿïýì´=\ýöãÙz'2a^^‡~!pòà éJ‹ñð@ߦ±×'ó¿Ôö·©nÒ¡€©éÐe¶þ£ÍÀ|EW·2k%¹( ¿}°£¸ªA6äðÆ†¤$*ßOgé1`kÎ=Ç~¶Pì`†²Bgédî‡Ò9µ|ÏF(ÉÛ±µÎ•:µói«êWM M_[ïþ Œç¢å.Ë&í8½ý\*ì¯^û—cú(a¾úƒÛϹJât\öá.°E/|Ò­ÈqD¶a@•}Ôo&{ÑèL˜Ø(²|=EIyc> 5²Uùþ¿Ç4eûÉ:s*DÇš$7µO îúª®S›îì¬åœb%åäwìD•tþ|§o¿4 ]Þfõ–§ ݲ«vœÃì·~šÝÝïòI¨ù›qYÿìßqä›Þ#{]»‹9÷DíƒMßjì–7I'ÍGï˜u§›ßBؾ÷8|£w­A 'jù¨ê¸Å~¶(–ÌðyxvI® ŒÚ I×Î41±J'Á%•1K9gÕh¸j[ü¢]ÆJmøù›'J±ŸÅãh¥¹™fGƒ…íÓ·¾(KW¦IõøÊ³¥ÀxØå—÷QÑQù žéº‚ùÅ·Ûzr^0;G’c2S£wOeçáç0º‘¾:~t+./\+#}½5ý {ԥĢ`Y_Y<&n*L£Û}sµÏ[Ã˼WqõÓ¢œ‚ýO¯~q£Ù90<07|þgXÖ:µÒ°á²yÉ’àwÕîZîoàho³e*xä|ó}—åÂ?GÐì»hÆéy¸ëÇI 9'SÇ-Žc ^ˆŸ…×ËÛ•½1U@ÐþÓÍN¹B¨î.бûøoqmºÂxXûq*´™þÍÂ2Æ‚ï7[hÇQÇ-ö~Ïa¦ÍëÓ`®ÑíWã÷í¯Ágõ6ÈAË«QÿÕ"„¿·Ä°_/»Çê1b´yoö8î½ö=Ξ/?/ž[! ™ÓóÇó TH¯‘7¨¯·¢ ”›-ÿ¿Ø;ð϶ï‡Z†¢Ó¢ÆÒDÅšØ*¶œSkì‘“B2 aÄ6–2¶Š*ÆR[M¬£ ÆŠ{,‘ID JOEÄ>j-úž×\ç=nùú¼Ïs|Ï÷½ÇñöãøWÛ#î+™û<ÿ÷u×uŸ¿£çTô^'oöï0±ÛHë|}A×{Òîû°¥§¯¹Cx=©ÕõðøÅqr”ÌvÁø¥Šý+¬ø«ºÉ)–£BøIejû×=Åü€¸sÝk{Œ$Nh{š§ñxî…ðùÎýS®ü™]¦#ļ¿Ë‘ŸÈù5*'WÁûî¤ýƒU[¸÷ö¸¬£BDùþÏë˜{q7QUJý4‚ž›_@ƒíeÅ^tðãs¼^ùWÑÊw€éÐ馃 LÐ%¹QÊŠG…×0xÙòÿÞÐÒ[8ì)ÿáÇ‘ rW½ñ×áüz+p>Y¬þºŸ"FvLøÇáu{µ#`i+[ÞDw„/®ýÚä¥UÑŒ†ïŸTÌÕx,”>7¿å¾5¸ÏâõÆ”ØR©mµíðÕ­Cã§Á•§ÞP6;&t,×@óèëþ ºœt¡êÕð›5lÄʉ#áZæ7«ªiBôÆ·ÂÎñÕ$$ÌÝöûo!"_™ó9iÞ€ã|™žöM\´·«Îù4âá–ÝÝôé–(Š[5Ô/|¶ä„6ô?™ÐIç@@óÕ磧ùê%â~ÜÝO/lyº/¼6]Ø´ãçºpœñÆŸgu{´ê¶Kÿyíñ4ø8ðǼHÓ1¡Ö¿-½ŠÏ!¨ŠÑÓxg2œ_-qËÁA>ޤ8Ž7®§¨<-—”¸VÖu'$ÞçŸífÚFž=&\`6õÉ`X9xh£s}sÍ’áÁÈaãTá¾ýnoÜâu´yþwJ®ÝµßóôÜôå`yo·L)?.œÊ_7±k°¯^Óvþ•-†)£(¿Ã}:¿”Ljª7-‡j)ÙÏöÌ;.dOc;Ƀ@]#ÿeR/ÉÆn]òŽ‚é#—Ô|Ôp,s×ßXîÈ3ßþ¥è?ü¼Ÿ‡pÙ8F,M‡ý,|BÒ Þ•ê=.üùLªP=ÆÇGxX»¨J í³ªi>Ú7çÜvçÛÜ—ñúÖÈ™Y×òÖB ï) ½h^¥â³ãB^ç©õâ©Aä°²ßFž7D¾™X—öÆ)^çP L®†]“ØŠÍ£6þW7K°ü™ñâQZØ¥ú±ã×ÅÂáI’álØzÈ÷ã{X¯ðÆ#^of››gXMàÝî:c‚š¥JÌVöÍ*_TÌ¥Š~þ&*{'‚£@¬‡ð}þDßõÄõ®7.§¨f_8–6¯ö2€ì®ó›µšVŠ7Ž™’%È:]¿q¯»ùt…Ìm¦!0ýèŭÛ„i­w Jÿ fÏ:4¯VëD¾¥Îâìû>.Õ+whÛ´íê×Sc¬ý8?P‰ã$x ± à«_¶zÑx%ômþgÇÈÍY‚p)#òc~;>zœ±a8 w–oÓŒ î\;¹Iœwxßç·|Üí­ó[*'«xì™­?φÍJ¨¾ÔJH©öêØ£Ÿ³„uÕTe¾ü=Š|(v·iÝqLöˆóÀÛûÆñÆ5^×K¦@Á{Å×t[µZ°åð'„Ö³ç<¸ÿhqÃèÀHè²x]1™øóaºwãU“fÏ{ú²&3^öy„Œ±l÷;0ù (ÿÉòÂÓ‹ÄV‘Cs±AÚ«çÖÔëVÜÝS`ëeV šYßVhº?ãUˆÈýz‰1ô²'ø|8•8Nü\ÏÜÆí' úÎnáÛ{f¸òÝ…:Ç\'„¯[ËÚDŸ I Fþ‡ï¾Ýýkõ²£}¿G9/(r ï¼çúúù¸­œÇÇqá8ë¾(û]‡m³…‹6Mnö§²Ìi˜-LÜ:öh›NC}ëV/­²¶ôbäòa˜ñjÍý31¾q¼qm(Pu«øÐ¯OtªpùèãðM×Bƒª~ßgŽÉ:×H¬qHCóõÏ`þÜç5Ô uT'Kô­_½q‹×™3ñq¿¹ß5ç+ J¬]k•åÒ¬ÙBË*£¶ÿ8ˆæÀß[È֑ߦÐú/8_ÏÇ'^/M]uÁÔÁ‹…ÒŽ‡´ üúœ9xTA¶ÐÿÔ¡3N‹ÚWwðn ×›)¢¿„‹…«×þlðq´RfT~t«®bÓ Õ‘¾øõÆ-Ž3{õó?Ç]&,¸_þpP:¼ˆ_Uý\E»ðÍxÍÃÝwûùö…§$ìŽì6l´oÛÈ ÐM;Wzànö:D<7Èó3ò­y¬Ç1Tx|³¤3M`ÕQSâz¸Þ3óe£¶vßz-øéŒk:F­Ä¨_NE$ÃÞûLh¾~4¬½]÷D³WC}ãxã¯'ßZ9yáØU‚Ò»AºvŒ®÷áµX»Ày½áu}]t§Jq°á›ð$Ñy•‘t5žë¥×mþ±+Â"ž;*æÝÐâÜXŽ“Sü3ÝÝQÛ„F«7¼ˆ‹þzM ú¶N™\as‹ŒÇûýհᨳƒqV8x§ØccýÑäë=¡ø“?^Ÿšá‡×qúÂg5&c*rN¨ ¯_îNLÁ¸(«PâF‹Ëý=Ü=ÙX¦j•+|à/ûð@$¬˜‘ëy^µ+\ÞX~îÆ£éœa?úÜbx^Ì,P…ïù¥ÄÕ;„çµ/-Üòã÷ðЮN~›+ÄMNˆZ}!\¬kŸ‚±çÛu½ñã0XZŒ‘2µØÉtïv‹—!Òß_‰×¹»FaúÞÂ’I¬òüô^«¯ï˜™+ÿ¢nÄ÷·ûøêâQs×ÿ0nb2<Ïžr>wötX–´vav¹qð°Õô̰ï^‡,¥âVx¶uŠCmξ2jîµi= ±í¦¹MB}ëýæ×Ò~Ÿ1„æ¿â¥þ¹ÃèÿÞ}×0Ú_¦:Žóã£øÉìŽc ÛmP/wYÖ®¹BÀÎ)iÏéFë‹^пûé'Å%‚¹Uå-A{Ãh??Ì7Ž7Þñzus¯„çWÙ%4hÃVÀÛáÏ÷ú®=Ÿ+lý#»Ã8YOø³ kÞö ý0ßÿ,¯‘à;ß\ñÜõ+~žÖŒ×Ád­®)Ø%Œ_²µÉæVèúdj¿'¹œoW½x_˜á%Î%KcàTÎüž¶í¸^èÓ²æÔSÏCž9Bú»/Óþ^WfL+ýínáüÀîŽX+´Þ˜’S¯ÚI¡ÿ+¿è-ý#Ï·ãáÜÈOçy9º7ÖŸ ü‡1¾Ñd˜¾ï¦}ôwˆ¸ï,Ö%Ú·¼ff.笻pœÏÓ¼.»G¸rË£s «:_ Ðv;)üfbr¶m‘6¼v¹"ü?èôqâ¯íAä(óù›¸½îqZÊs÷›U JÎ8Þ®aâaÒ{ySKÃ(—‘ 3N ­>ÍNê½L­Ð¨«]5 øÏ“談ðó]|ãx㯧¾¥0º²G`oA|’³Ú4ÿµýðIA¯è6¦ÓÖX˜qúƒ:³»Ž s‰ r”“#?ºX:Š?—Tx°Va®5S÷ +CÚº Ý )BaZã×'…ÌUË+|“Kó‹xX´ð‡Åƒ}>ÁÏ €+AÕ·ÕÌà?—¯×&?ùè¡fû„.uÇ_)Ûó_ìO¯ß6OHܤ¼1¤Ë ZMeÙw$A‹F]x8ÔÞ¦ÞÇçÏwˆÂ»àë É­Ž´êµ–¸Ø8›%V*Ü'DœÊØhؽ ‚®œ4=%Ï·ç=>R96ç|³ïÛÁ# æþÀJÖ^°£9[цûêU[¢ú‡•Ö>òñ·¥>gÆqØiŒJK2…‹÷BŸ®‰Ý m?¾¼a]žP0@‘}¿]Û$+røÕHðßûcéÂøŽâœ_¸ìNÚß¼£oo\ãõ¦âݨÚj¿PuÉÒy1%÷Àš´ÀŸJåå ׯæ}Z¾g°ÓN3¶†ƒ²cv…c’Á0C©úja{ÚÿàŸíã[—ŠŠu¾¯Ax§¢ }PÈÉ+ÙdQË}°æÎ¥€—q§„>*]íËþÐÌ{ `è›ùñï}öêP%>Öàu*Ÿ-ú0¬]÷ºíÒ÷AÍö‹V5\vJ˜Ûb¼v]x6÷›·sß0xýhðžf×`ÙœíÛÇoù‚öaø>ޝs)«Å¹C½ üüW&,7×›ÜâüÒ]ÁúÑì†æLøÇœ/žüY2_¿Rrã±Á¾sÕ^üuK×$ßù>Î…N„ìå_7³}ÿ0DäݧݼÛìåæÀNC½¿ÌŸû0Ž“”t1:Öß&Lì:¢\›ýúm•>;å 0¾t¹ûÏcéù¦…š =É©’|ŸŸ¸¾çõ‡!âŸ|ÝÝ*¿J_Qïg>Ž Ç9(iö´M6a çœýÐbIïQuçä zÄ%ÓÀ íÂŒ—Í´ðÍŒñ5 _åi;â›óõ‘ßœU þ‚‚Ðl{ U`ñ§Žë_Ï¢fœxTxw°ïý›êæ?ø'?Oßþ­û®œ#r›?ñ3`û8“z½lk¿Ó¦×ów º;Ð÷>Ègá+O]ÒB@¦ºï¬D5°S eÌjZÿ¼ù\½q‰×õ.w?<*„E¦ÏPþ#ÔZ]ÒhluZ˜ÐUî)?(Æ/›u±„"wˆòü˃>Jè<ÖŽ_°ü½öãè\ß½1ßÄxãçwyžipÛ gw{O<*LÅYMìASeÑê剧ï4è‡ÐÊ»àŸ^°õ/O÷£“oïŸß ÿä<ú/€Ÿ{çãp×cf8G…Û—·m~ä xÚŽ>j_ÝøSÝëjîë8N]ÿ³Ÿ¿î”-t¹WQÕtçQ˜Â¹ò¡}¿a‡O5ŠƒŸ/§ÇË}ûÓ1²”°£=WÁ‰JìDòb˜Ú©ç<ÿÉÝàÖ¡ ÝŒïBçÇÐü›ã7·@•Ý`cÂäyÙ‚7ʃ#¼n}F8ôû'ÝÊã?VŽ‹©ÝÊÇe—Ýü²Æ©ÅßB=ó<ëµÛã!bÝåb RC!¾ÆÅe&¶‡ÕÇ•Öíø5FÝêÊ'ü}]%Ž#ß•–¼yk¶03csÿØAÇà`¼EU#úŒ0ãXäö‘Aƒ}û–½ÍHx6e(.¡gÁó܃‘?§'A¸_é³[5¯ÛZƒÉ³ÞÒü¹øs‚¯‹U8NÕV¬b•-ÔL­œu †ä)¯O8#,;P&Î:7Æç®¯>¤ïøõŽ®|#iv([¡så%!ØxòdßG´¿>ØçóÞüÁqdô ëû4[ˆ™w7µãq<§jŸ½øŒÀÏÑFÃñRï×K|-ž'‹„šM'JoØÊ\ÚgQr7_ýZ\/‹çåƒg7h9ÅÌëg';&ô‘]ødŒ½ÔÙ¬ã0äýÄag„'ê•iòG4¤:5×üO†ñmÿÈõ¤ì9.¿Ï˜žÀÏ¿vö™0«ü“§µž™_ò}D3Ž3¥é²Š÷ØÕˆi/7v΂¦=~ÙÐ~FX8X[fÁ¡Q“…¹½~Jöösv4OéMu—.¾s’â{:ü}„îog±á8鳚\sÄØ…o*ÆÞÈÏ‚î¥ïþY£àŒP¸Âò°KP,¨ ¶¨p~\óõ¿õ¡sZàë–8Ÿ_‹çÅuƒ†°Ç½ë}ô®÷Ñÿ¦ÞG¬g×6Ôv?ÎØÚ‰Ú…ÚíÇ¿Øç&eSgRo$Ö«ÍI=!uÔRE=!Eú¶…R¿p)ŸÚC=’¤Ì+q ¤}’´Ôׄ%Œ†ú¸1ΔØ;\ä§:‰Um ^IaÔ+©Ø'ꥫ‘°O4ÔO—ñS5Ô+)“ÎH}uÅ^Ibo£¤wx*q¦‚ˆUí¤‘fêDL?Ö#2ŒzDЬjõJRQß8Æšb¬jÆ>q×äýUÓOI="3©—œŽzD²Þá:‘­€‰®#¦Ÿ‚XS6 kÊE¼jiŸ9õ—2«Ãˆ¥ú¯úˆ+©‹MÒoÎE½Ä¥Œ?3±§‚‰=ÅzÏ©%ýÄ{ÊFLU‘‡"öÏ$Ó‘rUuÔ¯¥(£¡hoÞP …qUÅ>+™ÄCyçÕï¼Úà÷÷ój}.RÞ*ãR1¶µ‘ú›—ÊE\ñV¥}§Â¨‡¯”¹Ê.Z„ïI "å\3ÆC¦¤ß¹ƒX3b?_‘ è"þj*±®Õĺvç1Ä9Œ è & –X×A˜t&êÓ)²® QaÄ«ûù2& ›úù¦§Šõµ³P?ß`âT¹‰ic‘ðWÍ”¸¡ÔÿŠõFgüUÆyð ÔJΩ  ¾v6ꉥ§¾v¬Ÿ¯žú¤3Þµž8UJbâØ%\œBb°Jûe™©¯¯”ê&>à¿êí˘:ê *öÍ*¤þ¾Rn•…X9*êÎzhi$=~Yÿt;qEöƒØã×F}¯¤¬@=õÀ*ÚG]Ú?´°-çW‰üÆ t3>+š”zb½óêw^mðûûyµœ®+嵺‹qþŽøjbQˆ\l;ñZ¥½Ö ÔYÊle}&E!± ¨BæÙÄ¥($ž Øwq²M(7J d{$£2%Í SÐâÏÙe¬÷zQV6ëk¨)ÒÝAý›¥WÖƒÝñoôpVS?RÆçû°3n¶¦ÓÌ)ag‹½Ø;ÛIìl±»ŠúJ{<»‰3hDy˜gïÂý¬A)«‡õ&5PßÂ0êÇ®DƒJ¥þ…êw^ýΫýþž^­ ßÛå÷†÷ÊxiŒ­m¢~õAÄK+$n™x¯Áüf”¬g™‹0_YO×°"| õ°—r¶#ÃF}ìu('q„Ä~Ñ"˱ø¯Fbm³ÞöVâ§…OHIŒG‘“Á˜BN‘÷H¬í`L:3õ…YÛn”šøBb¿h‘1¤"þ+ã§…gÈCüWÆOóTç¬!«„ÿj!Ö6ã§™X_ÙO9ÿÕJ‰¬Qr~cmˆÿª þk!õ‹6ˆ}õ1Ñ ÄO ‘C r6åF©‰Cä.ÂÕwò_õŽfÌ =õ£ Fã0(¸OÍJL¢P4+JAL"±‡´å .¥ÈÎ{HÛ©'¿”QÉøö¿èË/íYën˹j"?Èò0>,š”Øï¼úWüþ~^­¤¿Ç˜n”å)ÆùEvâZjPÖ÷Þp¹ÄÖ•öòO¥ÞÛR¾.ãаFÜĸLE¹™goÄMœK±¯?ãt›QV·Æ²‹=¹Q6 çÒ… À¤2HxFfT!*XÂ3R£Ì”pZ”ƒxFZâ)1S‰Ó­ N·8Ý:âÁ)$œîbÂÙ%œnqáô('*øSΆ{û3>cá‡nDy”œÓm­ÅÙ¿¬·¿¥¨Ãûƒ[%¬K+±âXoÿ¢¬n%±¥ýýÔ3\Êf=þÿFßpÆF±iˆ}þ•Ô;\ÊŽsIØÝb¯Æîv»[ìõŠ2é+î!þ¥‰ŒGKüÏ_00ÿª8㨨©ß”ź‹iÞyõ;¯öû{zuý^R1ãÍc›‹s6B0ñæÜă²‡X…ÁoAÉKq&”¥‹X.ã§›¸Ä&J&ÆRȤ¤b*€8"‡Er«“qXœ(&å!f§åaõbL¹Q*ÅÖT(q‰.¬çN± %þKTÆ Ê”p‰­Ÿp;ãÏ™QîO9—˜qXdJü~%çÏ1žgj-Î%V—ØRa‚§Š<LôTâÏ»Ê)áWyˆMlDyX=„Vž"|b-1>™9hQT0š„%C£Ð¡œÄd1 œ(‡…XVª"<ºL2“0T&JI\«L” ÍE‡r›Ad³(ÑlRQâ@H™ŸŒÏâø „ À8(O[Î¥ù,&2(-ÊAlæ%ì‹yµèÏ¢'‹>,z¯è·¢Çо*z©ÔCEß½Rê‘¢/2/”úŸèy¢Ï‰Þ&zšècÌ·¤^%ú“èIÿ‰Þ#úè1¢¯ˆ^"úÇêAôÿýˆÍ”ùç“ëðÃv–|Ãå)Füð=e9[ÒŠ˜ÇZÆÇ›a'Žd*1”TÄM²/IW™3’ª'o˜’Øàâ0×\(eMÎ~3a^±eæQfmÎo”²¼m˜#oØ·A˜&z`jQT0ÞtscÎ%Ò¾a¸1&wa3Îá6£ Ù¾9cØbPÈþ‚©(e ´{Ã2Ñ`ÐØQA³&zÀjßÍ3ÞÍ3üþžóŒ`ú¹¥\[ÆwSa[P”Šønb1Y‰kŠÁoE)ˆÇd-¶UÈ8ÿÛŠR`bP”¢<å@)1a ¨Bâ3PN ÒCœ[Æ{“a2éäœÎxo”DœJ;JIf@ŠÌJ” ŠIg¥Ä E™(µÄwò B1- Îz #έ‡8áŒû$«Î9·b…3”M¹Í$^8ã½YPžO9çÖ†’+ñû•œ÷Æ8QFâÜç–±ÃC1Á('*ÝH¼·à¿`H±äg¬[™€–8RÌ ¤¼[ñ2eh:”¥B“° ähz” ¥BÃHE¹P¡hV2Ð"ü71¥S܆ ¶” %GsÑ£\ÄÒÔ¡l¨4#ʉRajQNTšåAiÐŒì¨ 4$™ãÀ9PÁhNf” J‡r¢Ø[,Ù—èÕE}º¨Gõg©7õä¢~̼Xôáǃ‹úoQï-ê»*¿7žË¼–ù¬è±Ì_™¯ŠžÊü”y)óÐæŸE½Sê™å—E½²¨Oþ;YÔ‹zcQ_”z¢Ô ¥øÿÂûØg˸º +J†Á¢C9Øü ½Îöçx§¢”@z” ¥Â@rJ8»©èo.vˆ¸sŒßm¢Ó <ìŒ&š½Ì‚’±9ÊRbàéQâW2nw¡å!v¥ŠAi%n¥%ÇàÔ£œ•9§ÛŽ BßR›[Aë¨Ê™Üzâp›PAÀ…l/˜­¬ÉyuÁÈf” =HK 5ʆRÒDƒ1'3Qšºos¶lî†oGaðQFo(&‚ ŒÉ`FÉ0!t('J…‰aas9L+JŽ ¢G9ÙÜN®c|Þ âk»Ùº†sŒ­maµ'ô7;{C"ǘ]G…×:³Úá9ãs„Û¦¬z}/®‘rVØáXë|üPêÓ–Býd"  DrŠgCäë8Yó7Tƒ×+ÕØ_3nRõ‘³C™rë“J¦N³ö†…C¨ÿz äV¾r³öá‘›EýÇ£ ì†šZ < ûSÿç­>‘gŠpfaõi9B«ùUÞïxÃ_:vE=ÝV1ûbìÙ˜›<¿Yõa)ÀºÆ|| ÜLJçý½[ûúŒñ~‘CˆWòçÇŒãœÜ_iÀ·_æên‹8&¢¯×ÞzëÒY¡÷i§ñÕX(½òôþËOS¨/[_êß×f.ì¿gÃúvoõ3³áõÊ´ªùÕ˜að%ºš'vj”öâ¬0uq5ÿ…_ ‚Þ{>ŽO_3–¸IaÀ9‘©õÅÃë¤Ï^±wúðA3ñÀããcs!°Ý͹~Ÿ¢ä£;œ; ôñ8ûzp^MÅÈ®ý V{¥¡7îz.¿yªéžŒÈî†×yÒüi.tVsXTÐ9áR[Ÿ_ÅzÍ.ßgû8ÏéÈ7ó{¤¾°Ã€óÓn‡[¶$pç¶»¾>Fb?YoÜâ8ŒbY8'GHÚ°ñ· ³OB ùÉø ÝÎ w~½¬éUV ©]¿ ÎÐF=_4§0Hä?Ø×ò¿Ø{¨¨²mßÄ€3æ2cÆŒ©f)Y‹œD EDDÅŒ3f̘1cTV‰€DD’„€˜1cþ段Önì{OŸóÞ;ï½ïŒ×Žñ¿}nvM¨šsî•öÿGyÙåŒ#Z.¥þî£Ï„qä1ÎáçŽÃÚïO&—n×}V¡“-¬û@qŒ­óð]ƒcŽÌ·7ˆùªõ†Œ ¸™°vp.£ÎkoŒÚ€(™ôh3ªGÂÆ4˜vZö(èÙ–x+çö~'8zdõÁûÉóD~ÿÞèçÂøÅ8Ž@-u;˜LZïŸÐ=U'Š•}²Om¹GŽî?ùÔ\ê Ôm,Ú2Ü·ã€nŒ+é$òT÷¼ßw¢Ã5ÊÓÇñÔ6Eç“Éüéƒ<ö§ƒIHi~ö¹{dîܳC­ÇO„ókÒÆ«Þ1?2 Ñ{cZÆ Eókªrßxê_f$ú©óãœ/Ìò1+L&¡«c¯mïr*ÍŒ[‘‰¿¿{×=×{ÀèôÐkçqßQâ9¤_çn¶¸¶ÕØ[ÖÀy&Ô‡ªBJý÷Œ`Z0Cý´T‡rSRH£!Ç·h†ß I{[Ýú|dî_jZ§µ'ÜõY:²«Í|æÕ†p~ç1ñ8ê¼^[&Ó°ÎÝSÈ“ÑO&¦ö¼ ›Ï lß!›|“½»s°žó¥šÔ÷­44u|öz˜Tõ³“à8k^.ïU·k ¹ßÞx_ô]¸roÁÝWc²ÉviË:ó=™÷\ Ü³á0R°£Õq…/ú‚a—‹è÷©ÎO¯ƒÚ˜,…Lša8½uv>u»ã¬l’Ûîôå²EÐøÆzè£Ì~«9L˜œ§÷bÈü0îsÊ}ŸÔùŠãï‹^½âÍd‚Í_g¸m´ßðeƶlBù/Áx¯Õ…ûOf3¿âÎ`yï‘î‹áÐãQÉ”ñ2‘ÆãP"øºØçúì­”WŒq¬Âôwo•L—/Ýtsmx-2l{>›ÔQÙºÀ¾=ƒ 4 ä$q\uþâßo‘ç³í£d²ÅÉæµôv|ë0ØÉ 9›dŸµýt|¿PžW ó™jiWîå„(Füæ«§Äq² Æ,ÏK&¾;·ÙQ3úú|>¥Q’MvMýÙwƒ=¸,±ÐøÙ=ù¡Õ ´_ØÚ¾©çëÑç˜ Ç‹Ïxþ¸^B2qÉõÆo(šfåU|È&˧½._íK_)⥚³Ùx]Hå‘&µížÅsëmúÑkŽÈç~Nê¶7Ü”ñ<çj]™ìB/Á+™Üî.gBŽÌêý³z÷I`wÏ}*kg¸µèqΑ³Ø< i.8¾I9&ª›\äßqî ÷ Wç-Žn׋é’‰Fûës¦ÞÊÍ¡×<èvŸ|·~3xÆxæK7 Zµh°ÂS³á~îÜϹ*¯O†ãÅ  ¥d"W¯fÁÏ…ŸKï‹\)þœ`|â®.h9›/PßcŽCù6ÉÄb­;-­³ÀÈ7ÒËäûäÄÃþŒC5—åMÒ(áq}ý÷n0æíÛÚùó\ÿ¾|¾ ÎG7ÿ…`X˜L”„ÈY𾳑݈5÷ÉPŸáãÝ#ÝÁdWýþÉI¼Ou"slS¦u™=B,í´´æ4 ¾æåâ¼€ò5,‡•úä…cœÚ¦ÿJ“LŠ.µ<Þádd~=º©Î™û$Û¦ïÙãõ<Øsw>{.uêÛgÍ8hÖŒ¯óTº`eÏZíÊ¥”añ›Ožã¬ös¡i2q;¾yGʃ,ЭW; yæ}rJq®îî·îû5¨üšëiw`Ðc#…;Œ.·0ø0GÌ/Z£óåÕX_&û˜·CsJ÷d¢ÓÅå]Ë=ðØµÆ6Ï!OïÍ^=A7~D{'Í9ÔãæîºÓ\ßÎjÿ¼ºÅñ¬žÓù,Ž×a“Ê$¥y2©ãSÙÕ&-ÒÚ: 8Wûwr_@êc6 î§¹Ô1ûFŸc2ŒCý“Éðé´oÞƒ£s2›Ùä²²Ö™#_¸Ààv¾çL „=þçÒ\-a@Hו}e"ÏŠ?'›çHK7¿’j~Qšj4ê7ã„x&ÿX/™¬7_Òwe­l¸^°§{ì´ò.îiØÈVN ÆÙ}˜'rݨOº èóÜxþQI¦9ö+¥óæÑâsJï‡>§“I¥®`(š …mÏ$,Ë!ã†\Xfí™é?eÑÓ—€KÜØŽpî å9Œe~‘ïE>ýK+á‹25V°O…cœ&zn5H&µZã·gCêÕGïâÃrõ%·‚”ã¿ü„i÷w¢žÖõö„gQ­ò·ïp‡ÊiŽRÞ}•r~À“°^¯¡¿%P¾-ãµ`œƒ˜}Í&“+S}ÏÕ¸‘ Æ6x.‡Ð鉦#Hv¼“y.…cæYñÚƒX580»ãÍÙðàÖ=ÅpoðhUjömþ)_/y6 [¬€róhßUaœ1¦ó­n¶G…þ«lÈøÆÀøvñk°ðüÞîΰ(}V~ùÞ%PBæÍ|xÊ\þ¥™sÒÍ®Ö3°Hž5(þ¥”s÷’ L.˜F˜‹~Àê:ÙP&[ç¿ÕÚK7™l¬fts}÷û/_àúµ4‡L»èàì:(˜gó5´!t^&}šyœªœ9 Ž[û{éSR+™lûbZrdê}î«R‚F.1ÿ!_ xªÙëÝ‹ÕAœ“BhŸ1cõb*úöªëÇÛ¹i•mY9ãÛþÝŽS÷Á!gpÂ£Ž¹Dï õ—ŠCç$éAܧ°Ï®„NÔÑϸro¤Ô^†4¬]òÍFôÿíÝ?ãôYÿáÐ’D&Õž6éå}ð¸ö«y.™?ní·[=áI°›åþ óùü™˜ zù9~X ãÌ͆‰šo'&XµÎQOWtf±y¯cœ¼w)¯ìJ"æ“.m<Ù5¦d®²šK¶U™×Ýé~6™N߆/à¾ÊäÕßMÅþÙ|t¬¹vÿÎ:?mhV{|}xݽNŸ†OšªÏ×pŒ³ã\jÅ¿$b`y ­¿[Ô°ârÉ¥)ªÍq5½€öÉyp¨ë×…Û !ôó3Î’,Zµ´²N›¯lž¥ ´Ï[ƒa‚Á’&u¨¯¶ãDzvŨOQcööäÀÅSKÎÞÎ%ŽEok=ð€ú-¢Ÿ>‡q§zÊ«±9Á­êÚéX>ÿ(áÝEûÂ-ÑW—ò7h^©0ΰm{˺MŽõÌ3‘ŸËMì£Ö>Ï%_ÈâG‰!r¦g³ïûW¬m­y#ÚÎ`ë_¯_gÛ„'u/°.øv™½3ÞÙý·ù´ÆFÌ»Ÿ—¯ÏºMv¶ÈĹp¸x_b­Zyäî«­^îtÆüŒ¸¸4ùGV'±«TQë÷Ì9Ø—ç çõÍàĨ‡ÞyCkÁøò1ÕO.Q°yÍ7 ÆyLd:ÃÛäÎC råÂ¥,—ÕÎíòÈ“+‚¡²= Ü·¯ÞÆ-syŸ'•éâÀyöcFÚlÔ‡'ëgÍ×«ËølÖ0[Àe‡Qî ã”ÍÝäz›hÕí¬·8`Ü< tήwâP=î»ü ÏÀ¹lÁ†èí®™tn›‡ç¯Óõó7)Ïç‚êº; „rýgýçGŸR»Ý&³_;jÆæÂ÷Ï+¿ç™ç'·‡nh9B£µg§® dÂýùù¾_ïqÞ刚³|cÏŒÓ"gÃ\ÓÄDr`K€GÃW¹°@kÙ›—îy„Îsœ`S¼ŸÍŸÇ„s’¹ß/çœqÿO¾Ÿ5{¤Ål…q®xÚ㯚HÔøá¶y ¿/gº|Aù´¡Ú³Z.0v àX­s욯t³#;g ÆáNPwWÏå>Ëmsh8¯rlE¾Œº~0ÎN¬Bg«DÒÞ+¾F¸uÄ|­§õv{Ù¼©ÛVÏ)®p¶¢O‹·û‚ äR»Ø±väǘ§·.Vw†6B‚Û1ÿd h€û.}”Òù½ ´Ù¾'âh4å—«0Žöœï-´«%’-sxE¬Íƒ÷GÞF~<ŸGÊ®_®ö%i<4³}ÖmoÑ| þºÖÄmìcÃŽæÐþPfÛø;FâºÎ7¿Jç|åvDnÂö†ÒúÙT&;ZY¹âåõB¿§ãÈÈ£¦­ sLXÞо¬|=NŸÃ¦¿í£H0N ?¼(|ð?òàÞì^GÛ¾Ê#ËOWÓÉ:ëN³š­+i:¨?¶›÷ú¼1}ܹ/>çFÐçãŒbœ5VŽÚe’@†îó<•Ú=ìÏ O³¬“O‡>iâ=y<˜Lȉ¯6q&ð¾Æçe4ÏF²þúTäëQNº[oR^‚ã4É®è}ºu™Ô}˹ê6ù°citÚ.ùäã­I»t1ßšn¿«ÁtÆ51÷s9—óYOõN?#ûmŸ7ÇzѯCY<ñ9<¬mÒü|¸[û…޹4ŸP¿qGpðHsÓ¸o6á>ô”ŸdÌž7oÅï…Ïë«rDÃ1Άz³Z¸¯' ß·îÚêH>¤¾ÃÿÐ6ŸT›Y¿£¶‰ã$M‡¹fýd‡jÛ¾þäÜH‡î‰>ÞêzÁññ—j\?žPîE>t´âì³Éù¤GQµ³§­A²w\ÛSÛüàí˜-O¿JÇÚÇ&Á 3«®~^Ày#êºÀñ^wkÝgÃ-óàcVSÍØãf£³sn>ùPÇ~AÇ]ãàó¶½MxÏÊyO\LžÜ~Ï\GÝkÜóðLѯžîûŒ…˸jmlJ÷4BËdj|M›[¤TÛ¦ûœ°. ïºÇ«òÉ mK²>>³„K§Bßmõ™Á¸ ŽÄAXþî5û/ß7âÿ<Óàþ6OK >錷„qæm¿lù6*Ž<×VM~ëQ±s×Öý´=ŸPŸx+Æ?÷ƒœÛ†%E)6dŒ[ÿ›_Žb~ÒŒwŽã¼¹ýjç8‡8’·GwËÖ5pÐñÝ”>ÇòÉ¢“çl­Ý­¡Wýï«~øA”K·>·{ÊI_õ~4ðõ†:Ÿqœ·S‚.Ö|s“l{ðiäósð"ãÀË6Ñù„Ÿ_´Ï{;Q¢í; ¶³G&½Ý7¦†=xýÌ2µ•8o£ucIóÇ}雄5v“T¨º5/)€6£·ï™ã.ê8nËYg85Zë¼o™?¨Ó$DNÚ¦^{®ùÞ‚êDÛÜ]æ/öÿç*Vø%e¼ ¶žÀñÛàj«U8¥ñ>¯æ`JöÂí _æ‹XºŽ˜Éx«ÖäI·ûÒ¶›Ì¡ p†±Ç²1â:Œÿ“s-¨_9õãVbòzöÓ5O•$)hÛ˜ Ñ ¾yÚ‰³õ ÈRí™O^îsP?frÿjB׋ ÂãmG¨pœ¶Ûôï}:§$3‡ $Ÿ°ghçËòþ¤q²INÆuøèW°þž?ó7&ü<ˆrzos™¬q¿.[ʽ”¤óý¹‹­—=€Þ·3R ÈÑÕß7Ž˜æÁ¸:~ì<¤;¡ûëv`b˜xØ.Îö7~¡Çs9hö9 Ž’´È¾šöìÂ0iÕճ߂Ò ãvbzŒ¦EhRï©ßï`ç-¾p§Í½]¾â÷D÷¿Çðýš§8þú>3äúݼ·ìÁ0ÝåýbøîbúøÚ¶Ïñl?Ë[ìOÉ;m«yØ@f»¾Gn:‰ã¯é*ï¸ê÷zRàø‡öüX:ëA,éßÑÚÛùóÈ'%öÞ’ém aI™G\»äR}±áή®¶@óvªè;N÷cFî©óÇÙ1>áá•7Hó¦5½ZYžƒÕfz?( ¯"$7ì·Ê{šÆ8—$K£¸ZíµÙóÛSä]SÞ6­'%Ž›y~ù˜ñ·¯“úm‡GÄ‚QÜçŸ5_̓RýßYCÈ”Úý«õðåëGÒâÊäž}¿Ì€Mç^Ož1ÌÎ]ßý.÷¹”ŸÛPN· ÛgcÜgŒó`Ú¨7:\'ÃÜš7Ù³©šÕTÞ#¿ ÈÎU=Ï/ÐÚëÇd9áûžM²Z-·Ò¦ Žc½"ûÕËž×H”Ä—R…ð%³Y#Ÿžˆqè6iìGÈîéÕò¡Ó ¸ BŸkôïËðï÷ÛñlgÈÕòl_¨§_ ¡¨ÞÕ{ÃDb?aDzgÆðdþù£ ßÇæ}„ﳨóÇ«ÑÊ÷ùˆ1$®¯@)«”×M¬Ý ;Tî ûGŸÍ¾(ñäýŒ¨œ¶þðJçWäQÓ?¸âœ;T9s ©ÃbsvŽE× ÁÊMχD“M‚W,:ÿùáº9È8[ÿ¶!™N°SkdöwOÆí@†—ŒÛ°ÓÏFäWóýjþ§| sˆ=ÒÏÅÔžÆ Ç8­n&~|ó5Š4'ÝOÛÉ‹ ûÑóµŽl|ÀæŽÐyÁŠÝì=Ù<¦.\x—ªXofÔ@4ÃßöÅ•8Þp;¼'ŠÐ¼,‚Æ¿êNŒ9ò€x^! >¿´aÖ{âÃ$‘_¾7ñó$JÙ>;Ž·b™Í¥Y}£ˆzÛm{\è¼¶À4êÑRìñeœ ;'æüÏnŒóæ ¥¼y_q<ÎãRççÖ2™›€ Þr•tSF~ÿr¥‚ÝÞ=†ä$ö­ËÙNÇÁ‚k]†*'1nI»ÏhÅw¶¾sëØhÍŽûw(·6ïs*’îËJpüõMSârs¯g¯‡µ~w·àܽÃÕóAm-¦š^³€Þjð†;¬1:èU&IJO¢rØÆqâ>0¯c~ž—°©ßÜÞ€ÍóiÆÑ8ëyÛDr…˜YOyÝ¡´¶·¯æÔçÉrns²Õã×flý>èy«5Éxêld”‹b$>'c·ìîuäÂSÆ›“}žÒ8 ŒÓoÂû“w|.“Õ«"¦½*‚yõí×[½}@x”d͘XëŽ3H*”q«mˆÃ½F¹+AäJ}ß7dâ`‹GŒËóPJ÷ Áá­IÙfУyq(ö©}l·³…f1ô¹Ô½þ‹ïH|0ßúÙ„qÇs&áÏ zo¡'ãlÝ•6Ê]0üÞö|)?÷2¶ËÚKhÞcœ&&/,JºH.†å-ߺ6}l”¨]H–?ìhÓ§ÜŒ­œCèú¡›?tÊMbç7YRÎß ë²V´¯cœK³k<év‘\¯–¹µ1ìš[ÖWÚ¼lÏÊÓ²©eô¹4·ÊÈž¶'b߯ë4ßú±{$ÙÒûßGçu|,å\XznθaG9Â3euø"œÎÇÃ¥fÆÎµ»’¬ ¼¯yMlÙù¸3ç–>¯×l™ò{ˤ‘¯v‡æ˜±}G:ïרV&›1ý–žö2ÑèA¿@ÇbHª³¸µÇˆB²bÍnÃ3ñŽŒwêÈy1„îÁäº)kæ‹ç’œ¸¬í¯Ë4ÌA;ý삞i_’`œïÇ"mFŸ'?úæªá] ͯDF™;Çnq¥ŠW‘¯óÑS˜¨ººnqd|M{‘{©®ϱs׈R×H"ì®éÌ)†ÍâïÌ($”æ&òXe>Mº}nGl±ëè÷œ t^í!®ëøþßﭺϣÀ8G¿LrÙ“x–`1.X²¼†ŒßÝhÛæB¥‘ÚùŒDøW?Ñt¸ Œ+ÿØím´!¡ü¶± ~\®¶ù|ÿ•~.†¿?0Îîn¹M“^ž&OôdMÓŠÁe¦B¯ùÙB2¢ä¤I¥ Ì«”çeÏæ!±4¯Fƒú¸rº…G]8ÞÝ&wó«>EԸѨbP_cH,$_4—ÄÙ3Þ§¥¸ïOëÖ &5Ñó T%Ž#œŠ´=y‚P®R1(”…ù…äy¸æ©üèqâþ8åÀý¹làÌZa#ÜIÌCu^ãxvŽî‰—Dwƒ\}ÃG8^H™EôúR˜='wqn=c¨Sð¸Â¼[ñ|Íü›Ç‡Îs‡Ÿ>{7ç5¿ósÒh}Óóíe²@—«£^]=FÎ.öÝÒþm1Ôu0 ¤ž÷=ùíJó¬ïæKé9xOq¾M96¶â9Itè¡î;®¼—zF>´›ænòÛù’ãXöªhV4ÿ(™yzÝoH‹ª>Üye)ôS͹ðäØö9÷&´¾›?7 y>FŒ£Îk¯¹åû¶GHß–·Í>i—€–d¬lY)ÛÎ÷ ë„ï³Q¾¢™ÈeSç/Ž·åbäñ¶?5¾©q ,?þâñ”Å¥°9ºÄ`ƒQü|šðù =˜Ý|¦ú­ŒtþûóÏý{+óeþ5Œ~ã‰cœÓNB…$_ Œ;Û½Zê¶ ŸWÊúš[¿Kù¼ðõö:kHf,îƒÑçÊ{)ß¿¤\ë.´Ïcœ}u¶úw8@Ê’Õè?¼¶í é4»T<ç`PB÷»ºÿ:šŽ€Ð×ýýŒ'òäèy@Ü_CÓvv£yq1¦j5«Û?j<ÖƒzÁn#>·ë3ôµ'[w·'œZmO@ÅXoq~Î9¹¬¯²ûŒ4ßdgü¹g£Fu_Kzúz,ö+|ÏòßKáŠåÛ¨ˆæ£ ßÇá+¦~OíÐNë^W/¹Ñüžçý-R_Ð1dœvº¿§À8ê.öd)‘¯ïµkÒ¼ضxŠd¸])¸Õ´ÑS86ýª6*¸ÞtBëX Ã?¹·-öÓª÷ƒq¼M-²J;¬ŸAJB^;çz—@ݹ÷²–Z—Â÷ÃM[Æåôc\T?B÷3õØ<Å”q<­hà8|}5¢øñÕrÏØÚcÏòóãJ¡N’å­c+ºÂG‡ZÞò%á¾N¶ÎFįÿÉzüäàbëì~{^(q¼"aZÕÂæoLšÛ=°‚Vô±Öª躶-»ï6•0®$i¸hnO‹Ù“aR·ëÕK⼓Çá÷vÔyŽã7÷ûb6f~ØDß”¯.ÐM¦„[–ÂH¿³r¿öðÀ'§eI‹)dAÓ³¦¥óíçüòõçâÒu«58µ¹óݧÝ¯ÑØY&µxð¬¢mËàü—¨ÁvëKÀðƒ8«±¥lÝÛ F^ñR:™L¶}µ£†-¡Ÿƒ‘Øø|€×«ñŠ%N¶f«Ú¸ÞeçKg]©¦[ ƒÓ‡[en.7Z¡ñæ¥p¨é¤qoý†»oG<·lÆÎkIøó‰óÞsÄœ¶ÿ$…‚–ÿ”R~µ 㬚ÑüÆ8anv3ÝÌx…%ðòtÚsãRØ´Û«F`° .·ÀLñ!´?“wÝz´Î±s`÷ä°§nãÍòg5Å{˹«ÖÎüÙÕŽÏKi~c“®ª\³ÆÛÀ¡÷3½ZK`P’_äRi)Hb¾ÍÍ;0ji—•¿]0Ÿ÷ˆçKü^ï§ÑŠ—_ì|i(Ü)uyåÞ—Îgƒ1΀›þgÝWíO5à°>žOó=8¨êMw·ù4[èµ÷䦱ûj@¾*Z¶ŽÉØòLذ,îó{uü^/¿Ï¦®‹|¾² Κ•ߺŠu±ºÇAEÏRh·Ø¬v]{ ÷>|Ø:gáçÏ&nIi ùÁœ#MÏ¡Ä{£êz~eü ÷AõA 4Y±Êù–¤ô»Õ/çØåO#ô~OK2pìô˯ÿÁÏæ÷˜ø¹Lû&ÝÓËfšÁ…¥³ÏO=Êž§Ÿzã ZTËξPÃ~–4/…ë7Ý/N÷°oµ6×zÅ—ö€²Àø­ÍáÛÈóVÛß8ÃÑnsïiøEJï´‡…¶q€Íï÷ÒÂÊdò’þÇS=ALåƒý’ó%pÍ-{ëþ¥ð­ßç‚EycÙùÕLBùˆCáéÏç3K|¦Ã¼ö¦žO¦€öáÓGõÞ¯^<7Âlœ;ㆲûiaü¹zân]šñú\ d>Ú»h­v)D¾˜uôK¬!l¢L›[@èó||ºðêÒ ŽNÐTêZ«Ö[¸2Œs¶q¯S…Û+Â,N^)Ïò—NTÇ8ãSìÚx ýwš]¡$œãM÷F±óˆì~ÿ')åLVï5°{s´~0ŽK¾ôqoÇã §ž”0þ{)4R_`í B7Ô9HØ:’ðóC¾¯ßUQ–>âƒ4¼Ëâš|dûˆ#ÙýQzŽŒqΚ8ÿ[À ˜üápé¶èذ}ëG |Õë›uŠ·[íûz/½¡÷0íEÎ8çKòþv´™ð‹Ò}µp?ë{¿3–“N‹·°óWÂn®î/ã«÷‡Xý]=t·Ï&l^A†ø „ïIÐ÷扆ߌçëq½6aFžÆ°=ïò¢-ô¹­Ä87¿lä³ê4¬Jíá[3ªDìktáOù]e²a[ÛN: õ:݈(¼m³ÂpÆ[p±b¤Ð‰ç’k;¿=Ö“ $|zOb¬x¯®ïbwÇ ë"aµ¥@€ÆÏÁg¼ùˆç*ØÑ´A€Á +غ¡–ëËOó[±zpdûû.âx— x×QK\Ò>ÂêãTO´²æ<øÕIê²±v.ŸfþP“mög¿› ‡õ“½¾…^]HèþÝB9Å.PbÕ1Žh»°{C €ßS ÿÞî»j¹vqKëãèÕù°f„ùX”V«ú°­%páM_ì„*hž=¾É\w9ë+‹Hä“í/¦0&Gè¿»ÐÕ„­ïÅ{Ó¬¢ÛÞ­>ºÒŽÇŒ<Ø<›ÞC Æ86òÿë¨?ÂJ ½âk·&Y*öü¶‚:j€éÖwÍIß’¯‘ÅÆâ}4þÜ­þH•Õï©3¨ñÂ-é9{8Ž/É6®ãUzšî’åo+{7í¿|IQAË««n5º:—°ýñœ•χù½~¿£g“Å' ±góhÆ)Æ8OµMúüho¼Ñi~^¦-mT°ü뎡nÍ ÙýËY„=ÈÚÙp\®ÐøÐÂQuk;‰ç¹üÜ•ßã«:OVaœÉGŠm‡¾º vIÛç7_QŸ¢Rünªš›®þyÝwõ|þLB߃è³f,¾e´Ü¿͚|m4€¿ü¼dyê3)?7¤q³yÝÓØ]&›ÐóàµÎw®Àáïgå-*^ÒœÑD}ú¶ÚÛ î$ŽI»¸e;è ”ßø N,€žÑs¢`Œ£~LA <©›6±C 4øá׬}ŒŠ3´´Û¹ôx)ùU»C¶b áûúl^&ÎWøóƒ¾_5æ·õR8ÆÙî2É­0†ÝG(êƒ:Ø]Ƽ.ÿU7&ÀÖ“Wµ?XÄû#áûZœüçuY°Nrè¤æÐc°0³¡q”ÇK¼ã¬â|ѱÛ0¡~ ¨ïÎbœågŸÊÁ~‹7·_Íe÷ïd¤Ñ£A瞟6üå­|Œ~[—©p¼Ñ¯®Î9Ôù:X£ë?‹!Àæ}ä©#*°7(ªÿÃϞݚMžÚŒ·ÿäÖÄ6[SÛ§Òø{-ê:؃?שÔîšýnÀlÅÐÑ5ßÃJepÓž»TX7jôE'ˆtîÓzßûBïC·"Âõ&ÝÉP’ðmæ·'“~[—Ip¼ý÷~•ÅÂì:ï.n~Q g|;wg½ è~®XO^>¶ÿ¬9ì¾ge¬"mÇéµþì\g¶x«:é¸ß΋d8þFMá=›EO[t)/†XÿÑ ¯«`Ht÷¾†˜1µŽØ«œ=Ÿ½?ñ!–Þ30ÞWùøE¯ŸH‡Ö‘ÿvXã¿mè:à}õÔ‡Š¡ÆÎ¼Äg*0^|È1¢ÀŠ.~¿µc1¡ç2ÍÈcaA2ø~ Ÿ@Ç8^|yôʳFJH¹é[­^j1tÍn{ÿ«¯ Οíh6fô,|·yÛ‡`ÂÎÄ}qþ½ó>Ã?:_eçg8¾K~J ¤¾pBR –Kg6ÓŸ¬‚‰{m;ÇÄ™ÀEÙ5õšËÄõƒ¿ÑçÓÓ.ÚGäÀßSãç·|SõG󀞿¨0ÎýÈ’&/ìnÂz}á‚e1\y%iýÊM 3:Ëê á´µÅÕ¥|¾Í×_â:…߯çõÏû)= ¿Æ^œo%¥Õè³é&œ¢}»×‚b°{ÙnÓ],Û6`†~ßá`!ÓB¯{0¡÷ÅzˆÏÿëÚ¶Û,™‚eé^ß úï¥Åú­Ï¶?]!åû't>Ìîã`œÈõõ‹^6ŒƒwzÛ+VLÀ:pñ±ƒ º˜¬ wN ê×K ºŸ×‚Ð}a9»/*~»Nv­!±{ދ絿æ »q*¿%ø&ŸŒƒ± ‰×7úVÇZ«`íçÞ+ÊVŒ·Áp7—Õ6±S_DÏöwaYȼoºº?¥ü‹ºpܘ†2Ÿ&·àÇgÕe‹ÖÅßA¹ñˆ9~ º4ÞÔ¯—fÍfûIïcéù…'d» Û=ØÏ_‡h@™eºåŽì¼”î÷cœÓ]¿ÛÀ-n5¶Ô,†mýO¬/SÁé–BâÙA–šÍ6J Ÿ·Óù®„]~ùa…¸0oÅšßÛ×d÷â\Xß¡óËpŒ³ïÀCÌ”[ð>ÑܽøQÔN÷|­3H›†kŒôÎtÝZ;7½Äâ(¥¿Ny»Ó#7]Øã =F‡?{` ÍúH}vŸy<Ðy(ÝWTbœO]Z7¼’w ÒÞÖ1—R›µû÷ﮂÆ]¦µË´rõ1V»%,ÛzŽ;2·kÛý’xˆ÷ºçù6kpçmk ÷Á<Ÿû¨ëã¼–í{Wn{æôKŸ}®ôÏMßó¾­ l{+vv€3_ÇOOø¹ŒÐyë 2·°âÚ³Ó!§Õ˜WÆ—¦..šPuaç†u rækÒ€9@ï!8ÐzÙ‡} Wé-öă|ˆ\µoaXxÞÏ‘6QÁðËÏbmÆÚ±s¼•„½§G^ Žø‘cå !½²â‡+'ÁÄ~ù}:Ò„V¿vítO—ÝëÇÞK¦÷z%'±äSõ5¯â¿ìí¦Eà`=k¤²¶ f-02~·5TÆyžT¬`ëÓ„îsXÂÍ_&u’³}ñÏRú^Âw);ßæ÷•h½`·R·>‡'€gµÅ:*í©Q'4U0o²ÝèÉ–l½»”ð{Cü> ]ÏŒa÷¦>IŸ Øtaà±OÒ‘ _ß)˜n*ž3œÖ þö:úÛëè?ÅëHƒ}&‚¯,÷2ŠBI˜·,÷2òGå1Yîe¤‡‰ÊFœ±Ä}Œ¯î»Á<«rK#ÿä£-0K/6î‹"0ð䌫¤Ë<´“W:ˆñJï¢ Æ•Öc¼R%ã•3®’cuÞEm);@ÉX¯TàJ Þ*G@à•Võ¬õgþÙUY¥AŒ}Ç=«2:¸‡"gß žEœ=Êøÿȯ(è/|³Í˜-÷†XÒrÆ'å^Eå(ƒ*^¶NÌ/»ªO‘ÓŸ¼²~çG«=ä?IàGû3ÛÄ5øg\Ò¿{ìß=6Xã?«Çj³ßY©ñ#Z‰Òc”œ„R1?JΈÖÇÄcÞ”œ­B™1–]ó¯<½ËË.˜ù× ¾ÞŒ™ÂýkooDc#…±Âð®Â‡ü½¹¿T$ó‰«Ê°‹ú“g­À¯S¢ô°ˆBIð‡S2v?c× ŒÑ`Æ®˜ÐÁŒ1ªÏØuIŒ]Òš2Yt§@`BËÚRÌ$Æ)ØucÔ =õØuU}3ƒ˜WmUn]0ã q_¸ª|î Ç9HŠ*áa¬ ÿ:ø/ó,åìå`T9ó/åìeLìpæeÊÙËå(9gù1ãPmêq*c¼Áߨ ’÷7ŠÁ•Á[áUx4œ»Œ*˜ Œã)iôЉ1<«z+°€’PúXDa¬ŒI£ÇüŒó»3„ñiÖrcwpn J .”ù¸K_A`-›µ¥«Œ¯Ò޲;eí©Ç»À±©ê¹̼Œ9kP¸OK†ñ'®B%JQ…§%0–¹'|8c*ü#¾rÈ_x;aÑ+QzXø¡Œ×©Ð§ÜBÎV®D™õÿÃËÕ›yWå*{ÿÉ·X» £S†?óUüZƒ™çë?ò˜ÿg¬C¾õ÷|õïþúŸÐ_%ìß ,cÙp®}$c ,ε÷f C!¡9×^†ËF\‰Òfœd%ãØø£¢jSN2÷8ÉAŒ“,øÆ‡±Bðþ#™³ «zÆ—£äX$Q( ã×TTñžÖc^ñ•(öÕ$”~.²ã" >ñþÌ'^ƒqk¢Uð‰d…&øÄ+¯PŽŠ`Üš Æ+ PÎ|âf«{Ê+ü3·F`Øëbq£Ê;RfMJ Õ•‡ÒÂõG)QzX¸¡(JÿOœÂ ”s8J›1 •ŒQ脊ú~ØÆ(¬ê ¯‡…Šª<âŸ3ë9§F`ÖW¢Ø’£&Œ5oÆ?6ò7–ûÁW0†l$kÿˆK¨Déa3 EU¢ØT’PÂíÞ°¿ç¯÷Wÿ¼þªÇ~æ ÊJôÖüƒEÂx‰­?Xô2ÆLÌ«þ‹¾’qCQ('ÆNøú˜ða¨JÆPøå(9@ç¡ÂQÚX þ¨<Æ ‹¨Ââ<ÅT…Àè`|X½*,¸TÊ@à^¢*Q ,ÀT9Ê 1¼eÍ:¡"XQ*PI(},Î0V œiÀ8³s¶3esëwQ(,Þ TJ‹8UŽ’c1G¡$Ýÿ`w;aaG *zPŽw8+roTÊ‹=¥ïÊCɰð#_V`z«Ó;¢ ë>¥;àÆ%ç|ë2®l9J.°7Q’*LY6ŒTcÊ Œûr”6H”.6‘`T9Jþ/09…ºÓÐø×÷·þOïm }P¦ñÇ|÷¾ª=¯j¯ãýí×^×ÿj¿úŸÝïúwô&áó‰À^¤ƒ=Hý'I8ÃÄ/=X¸o‡½F¿e[çiSžuhʰ6Ãþ)ðm±WáW+0A’PúØ7ÂØƒÖ•2À¾ŽÒÆòGå¡d˜H(ìA(JÆXÕÚØ3¢PL4aN…ÊαOè`ðα7(Qz“0C˜_IðçDU¢Ø’Pú˜œa,A½QLÒá,U†‰j†‰‰ÒÅd F•£äXëQ(‰pç 8 ¥IÆÙ•2À„GicRû£òP2Lî”&¸?*¥ÂD7ÃDDé`²¡T(3LúH”.&~0cFˇPÆ®P TJ !¥B™aAD¢t':%Ãâˆ@é`¡T(Áý)òïyÓßó&ÿ¼y“³¥‰Œ*GÉ1ÃQå(LäpaÏ“Ù Žªþ·°ÒÎQ(m¡—¡"Q•(3LøH”Ž0WBE°ä÷ö÷Q,oTJ‚Å‚ª@9aQ(QzX¡¨r”H8J‹Ä•‡’a±D t°`‚P*”N$J‹'UŽ’cE¡$XHÁ¨r”TªRX[baE *PfX`(áž0*U!Ì¡„sT%J†…ÆŠÏÉØá2,Ä”£‰Ò–àƒÊ@`q†£´±@ýQy(jª¥èLùâºÂ]6T$J‹7•’`‡ *PNXÌJ”^÷?øã ,ìHTeÊ"@ic‘û£òP2,ö”|J…2ÃÂDébñ£Ê—<¥‹À…’`CAUü7lð ”6 %JE¨p΀2ÆŠR¡dØ8‡\þ'yÊ ›I$JJðÈ?øáQ(Á±&h= þ•yÓ¿2gzáÿ®ySÕsA¡×ýÿy®ôï˜'ý£>ôÏzð»FbÏÑö©°ÏdûSøå†÷º°§{R»²øE‡ ûOÂ]ìQ( öˆTÊ “ Rxo !e€ý!¥ýÁ•‡’aˆ@é`¢¡T(3L˜H”.ö‡`T9ÊL8Dé`oP¢ô„ûYØò„w °þƒ„w^±þõ…w0Éò„w[±¾ÃPÂØ•2Àä Gicú£ô0 C…}&áÜQމ…’`2† *PN˜”J”žp— “3e€ ŽÒÆ$õGå¡d˜¬(LØ ” e†‰)¬„¹ð®ªp® …ÒÅ$F•£ä˜ÌQ( &tª儉­îLab{£t1¹ƒQå(9&yJ‚‰®B™a²G¢t1áƒQ[p{‰ú.•ð¾*¯ß v÷_ÁîOe#i°ï>ïOÿ?ÿ“ ìßí+“E=fg»%n vc a€ªW—Tó"à>ÊEGÛ]üñ±Dä~™~²‰s«e&úzQ®äVøØÃÉ fð¾Ìÿ̧HSÒì„þêÛŒqZ/íbð1lêÄÜ8Ò¯Lnh4xQ»m"¢®é¯â3‡pè[ŽØ¼ôËøyÖÀùœÜ§„q™¿ ãBaœÕÓ.¯¾!O„ʵ~7ŽÕ*«['¬--÷ >]¸k4ž-8[Î&œ“ç5£ûÓu7æ2ÿ;/à>ÚܧŠóªr-•ç§€wÝ–%­ 5ü_‚Þµk«å•@ÎÍÉ]lÀ(‡ÔÇØ¨¿å'iì/“I´GôÛÒsÊ›õW a:ÌÞ;8¶ÎnöÛ>sŸ¶/é}nüŒ„þ=k"}¹²Ú•åÖÀ9ªœgJ}$?‹¾úÜ¿S#Á83¶O­oƒ@³n¶¶º¤]úÑóT ¬h<¹àçv{¾®øDð‚ÅdhVKÝyζD#yvù‡ë®"§iãôÌ]>ߥԿø‹”ú.š2£õÝÀ8¦Â׸ý6D…šúwò*„ê;;ÚUßZ=¶[H33\€bÓƒÉñŽÍ›/K¾,=5bidãM}ý4Ãûz™~}7¸¬ºn0N’‹æ(oÃÜÊiLF‚ñ{æ_K ¡³yFõ¾?1Xô+¬Xga_ðШ_¸èSý~êĵ×?}’2?3 "êóŒqж½s©]tîßÑ¿|´A!^2œg_z_û×ú­€ø°Ÿm‚‚ õ½éƸÂc8wCô©æñ8ŸN]/8þ ÚÅ¡&5’`gbßë[?€÷ºM¨3°žNÙt]w†ÔöûÁÄèøÓ"'¹È3áŸ?_]8^ù—{&Ã’ {Ú‚½Sã@û.ʯkš–À®²)µŽ¹AíÒÌÄFÓ–2¿£ÁŒg2|ôëß2÷ý?Õu€ã8Ý,Î' Ö­I¿}ùàøz{½f§/Å0äûôÍN`7hTö"eÌOG>DT’N=f3ÿÒ™¢¿0÷•¥¾°c~ã h(“Ïú8ikÔ5|üáãÂÐtýý-‹¬ÖÔì`fÃOû§šÚ,c>ê™Ï)óI²ë€û¶x4­¾iÆ›1@y$Œû€q¨?ft{ laûŒ›lQ ÕÊOæ½—ƒÚF±Ù2Bý~ÄRŸ1#à~Ý<Ž:߈¼`øúVïÜÙ™žU]Y .Ý»Žà¼•¥„ñ£ÈýyvßKÃ"­­­sÆÐ|ÆqZì½YÒwr2L-½Pgæ4§O¿á‘ÅнÝõÖ‘r9¼p¸kþ¦õÂ8Í„û.R®ËD8ëjeÛÌó£úç Æñ:lK†Í‹B½nÆÀ×øŠÛ‡×öÝeY5à†­æçKæÆ•!Îc}¥gëÛçÂqÎ÷¿b>´¿ãø‚+ºNëh­6ô.€AV_gòÝ:ݯÇ8Cå­òÏ?Êé@8ˆú™ÿÆ1PáxZoß›äšn·êm趬ôšfmïX ®þòzÇåÎm²¤i`Y;Æç°ýÀÔù^&‹°»á)°í³`ØUcÚírê1´˜ñ÷&ÀIwÒ-ç摚GawºKÖȸÊÅççïp1Ê5`ùŠqzõÙ˜b™yWô.ŽêZŠSÕ×ÝjY ½žÍÚ–>y"óo\Î|]Dßaê3>‚qK×õóÉ ÔŠqÉ1Îó­ŠF½LWËnšû!µÚÛ;ák<+Ñv›j<Ò®eä|³½Ï÷wî—I9ËÃáwîÎ#i?·ûíÝ àû‰·Y–ŒKŽq¶>|ÔäÂû(ñ ˜4?.Îmó ™TT‡û7<ÙnþxÈ >™Å„s?8Fãa´@Æ8µe"O(x©D¶jãHö=ÑyH0Æ)Nñ®–V*,rÄzéÖ|0²?>¡q\¬_Øì\à¤N:ç^Ìå¾Y"ÿÒJ_æôÇô̓æƒùô(‚ãço4ÿP즿R/{kÎ'”·Ö”Ý«¿wš7,/Y¥{n^ ÄÅÏÿÙ°n…4!áÓ¯æµßI¹ßåtûÆùµœ™¯*Æ©û²ßæ,ß4æSœ;D›4kX»WWóýôÉ ìÖ…x¼«ÄçÅRÊS2çûÜožór)ÊšTLë\;Žr-Ã1Îì.ÕîCHTì:ßäB¯úŽz]!œ3õÝ'ÛÌÍ%ÞÎDŸs€E®£ÎoO¦^¤Aƒ•wsnÞ̧ÎI…ðÃRÏHÙÁ-Y¶®Ù  ²cË“ÎÛbe"—Ï·Õù‹ãÐ>•© Ç?©½"LÖ-jb·¿=½ ñ·êòõ½æÊ0êŸ: Ò²;¶2þK¯q¨L×¢«mÃiP·~ü…]†¹0°ëÊå…P‘³ÿ§So/h­6¤/òcN]tQlZã ©Þ߯=>¨OÝK)çÚUíÿ¹,ÇöÔ¹48»ôLã^ r!Xëˆé¾1…б¦ÿÔ;&Á‹'Ø{ó7…´IéI’¹ÖÐlý˜çÕZ‹}Jä¸1>@U__ÆÉ¾~$ ¤]mv§?Ê)uöåæuÁùy­†}‚z¹ƒ±E»q© Ý]Á;™ËÄõ(OÇ8ÎëêßÎÜKµýÿåè™ïàÔ¬f!œLÑŽÞ;Þ „®ÖÈw!iu6¬Q±øüéžo»Õ7¿ÿæ'ŒãÙ9 •™z:sÖÚ/Ï%Ãæ½Œ{þÞ]¯Sr~£3¬Õ8>÷íâ…„û¼Z4ï×Dð™f¤ß ›ÈMKò©ÞKK¸'íç,_1ÎϽ˾û>Nƒ: w»}vȯç?˜d=€FškëF9Â9íŸW·´ýãsÞ5ižãH…Ðy·78Õr÷ö´¦@Ÿ;Ÿ¥$ûîžµvÞ¢/¡:1NäYÁ89 Ê+½g&êçÀ÷§ñó¶Æ<€:qþµ[q„Í ç>%K8‡ˆ®;þàšé¼òM¿¤¡97bîîІÁ5÷é +·a|SÖ§1Níï¥OI­tÙãç›:9pôk÷g×q~îd­yô¥3|üôôP‡G3 çßòõ åØMÍ€óù|–óò Vô´ œ~n‡Ëd%‡â ;NÊ*±(½³%¡«À¸piÔÛ¢ñ@}^9of€è?LçéF"gòò¾Hé¾çÑçµãX[šç”–§ùç€:ï.Þ‡íÓ¼Ú>›òzv~¥¥ËþíÌtBùH}™¿¼+ø­Œleáä ‡¤~ž·@(W¾1ó¶c|EÊG—aœŽk&˜œ/Lƒ”»Ç¯_zÞNp™çgò.} yx`’;Ÿ³êDï@òzäøY…ÝG²uJãôøÃ¸çöhgJؼ䛴¹Åæ…]BgAØÁèÚ™Ö ÆùðE¯St(s.=qÌ}¸{Úõý®Ž *Óug ‹ãˆÍ$”§"‡®§:zÚ™3޹pþ‚}óŠûmÌ|Am¡SïíW;í¥<ß`Œsíeî¾×#Ò¡÷ó­…îMîC—½3]ÀYíÃ?Û§yÀûÐîí—œI*ÇþãaÛÊsçOŒbóQÀçQ|ßålÞÄ9ãÚ™0ëá´~0NƒƒcÒÛy§Ã½×A[K²Ak|hïÔÜèÔúpFýÆ›œIöÆK"gy»‹ópÞg9Ï’òØ¿KÍúŸšÕv óS¦û.JŒ3\=ÑKÉÝ>ø„ˆ¹a2WÝ‹0iÓneìSwÈðÀ鳈ŽKÉ]×*àþ¹ÙÒ ÆuÏ|;kŠ@¾¯À÷yÔõƒqúoú~S=¿ì‘‡dƒÚ¾6´t‡µÞÙ|·Nx Fä3‰ÁÇ¥÷:ƒú¶¾~£ÜÆÒýZžÒ‹þ)>G«új)“-˜rÉÞÜ3„ÿÛÞ34§Ü9ðÀ¿œ'µÚyMå”_4“üð6ðò‹µ¹<Ôg_&rùz†Ö ç‡Q_} ÆqØ/Ëëî',ûm™ EC‡–_µ+€ZχnŒ÷wa}rᾬ”cÁæÁ¦Àç…|_ŒïïÑç"í2Œ3ËgJXM:4sÒ°­Ù ›ÕW|ô2ìÈø¾¤»`‡¼ÄŠ& [ÎíyyÔ÷^“ñ)­ Õ—ëO˜Ò}Æy²»Ú®}ÆéP§rЖê¥÷ ðå¥ï.ð½©oÒãyöŒóåÃ|ºí!äÓùe%«¼A½ hìÉ|¯µÙ>OG8ÒÚݺÑñIp:âæÀzí)O0ã¼ÝöÑC/¶­^érïú=˜+”¿Nj 8wÛÁƧ‹×ûÎ+úîªó¤î|¾Ï2gƒ0ËkR§Lléeúaa-˜ú°­Ê¼îj˜ž|R{HË@Z?gº€±kŸ:¤´¸zfº^é—ù-¢Ÿõ0hQ`^Ckí»Â—”mmv뾦\äEòõå¿Ô·Î‡£«ïè(r=ŸÖðû¼/YžÛ'Rõ_×Ìt¹/zlÍ[–^Å:âæBÕ„ÆLgÜsXݲոê3­€ó×w¾=z¬ ©¿úûY÷AWׯñ³Étú6<¾†5ÔžÝõÌûvc©;ù°ÆàéÛ=ghf_óµlº¸/M¹FVÌ'¼RjäsÒšJ)ÏÉg˜í‹-“-¬sfË!é t¯Ê,0­mŸpîb>™mÔ²F7P/çkú’çÀªPÞÔdx¸9hÞ0GO±2þ3­·ÖÝ]×tI‡.#뱺“‰.ýymˇ†ZÇCÚæ*àôˆ¥‘¶§}ÈõçšØ:ÇÁ.½ÑÏç| dÉYŒÿðVÊü祌gƸë¬N0ΊûNqºél˜5NN;ìŸ79Š+ó`~åS×ÀVä©Ñþl-îƒsž3]_‚ž©æ!E"ó³Ç8Û÷6YàyUTórIÁš,ÐØut‰y>Ø,Ý:]ÕÀ(ÿt*ùÔÉØÓ)ÁNX $=#àóG]8Þû_';™K‡õ} ­œ>ì¾åÒ!–¬>$;4 è>ö4òá–vi&Îó)×’qîh]…ã8ÕÛ/ovÜ%V^Ùk1Ó> ŽLiŸ!ÅõäË;UÙµ<àZÏk ÿôé¤Ú‡o?Ó×MÓÍ—WÙ,póý/¾ù‡*€ïÿ«óÇÓŽL:×Þ#$m=2­k*Øq5?¼"G6(¾0êX³?¢~<šk‡js¹ï:³/ì2]Ì×ÓÊ‚ßK9Ÿ’ùÙÓ¼Æ8”‡Ÿ˜…¼Ë„µw6ݺ‘Û ïÑtv† ½µîØ\ðy?ÆŽæÎP§÷‹ŸçuÜÄ}¸Ÿû/*Fš”ò}zú|¢ü9ce2ÁÍ{ë×4(·snfBz¤uÎŒcy ïlUd‘a=‹Ü¦þœJ~âÓQGê Ü7ž®ïÇý¶ß'ÁñÔÓš¬4¸äü1º`m& ò[f½9~|,ܧc v_kO¹:™Ðó˜o?å²²-ãÓïM†ã4xÐÜkpRLQƒ[2a•ûœ›äÁÊÌÓ¾íß9ÀLÇskyÔÏ뼫»BÁi™Vµé0gfëúªÞ àëSu~âxv'òµÎ(Ó@Sx\µÉ„W+Ýf|òȃ¦KfôqÆçå´»š—v -¹¼fë·é0´‹¤kéè "Ï?—è>ª!D ýPúœ Æ8åSž®Ã8G„éêç X±±žÞZ“’“ºé°Ëô§B3"—ÆJ«çÁÂ'ëj‡ëëß']~M"¬ÝWô¹=hž?’îë\mþªfؾ¸ ðõ¸:ß1Γ°^¯¡:¼¼y¤²rE˜ä¦j”æBïÝݵ煹3þÏ$R>VÝLùÔœƒúg6Où©i¾/“y ³Òá¹kÙlç±P-°i¿Á±¹3òsvx;w œVÛÇôy>ôùÓá¿ð°9×£ ,ñÊ.íhÇ8‡²'=è—k^.ïU·k¨—){sÁæP°Y«! HùÑÀx©ÔO^uû„i@¹®½Ù8}X]Ý—R^Îc)Í¿‘@y9”c!Ã8ÛZ e\©7ª3`ÚäÞ5Ž,Ê…Õ#OºQk˜V«?8°©ng°ÌnítÆéý`È@¯ããÄù)7<•r~õº¸>kÛURžã´ûïÍÓáÛ3˜¶øû](l¤ÐpÏ…47‹¤Z#]aÑ‹[ó;xšw~ìü×r Bý|z^—”wiÌxÔ3Œqüí(sÐN‡+äâÈù·î‚°k4Ô4žnõ_hfë:ÛMÒ^¾÷&¹7oLJú‚¹wjtSG/±oóóhuàxŸôÆoŠû˜Æ¸wÁàý©¡2ý\ȹ¶fZâE{Æ ™,æï¥äÒ­V½ÁmìcÃŽSÁ£U©Ù·ù_¤üüè›kµŠq'lD®¼ºN0μÙzõx˜Æ~Ÿ»Ð¼ ¸Ihë\H–Çìš“c ò¦åMæyž¾Ñdà<ái¹+.%†Ob<‘¯ÒöõÇ6q-ûÂòÊ YÕð®U' ·ñ»QwÓ`ƒñ½üdÕ8UÿÄ~Ú¹0ÇË}WRs[Æ¿t!ÝÔ@IИ¤ö·96À×<Žº"pýÞ2ébêÕ4žÚ[BïÀ=ÿ.;Þ|ÈûKú]™¶Ï–Íÿå„ÎWÝÅ>=qÇŸ>Cœ ðו¢k:žÇ;CºhüÚ–Eöœ*zT¿2t…*> T„~˜jÏ÷·åÛL€¦ÏŸuªX=šn¨Ýoçlq<~~ÆÏ}Ÿ\€¡lýŒq†Þ[ñªÛ¤4˜xqüàaOÓ¡¼ööä˜ä˜m"Ì€œ€q% í+nì\=–-\0Wäsò›(™î(0Îa'¿©ƒ:§ÁÇ©Í~Ü• –M½±â\¸¾—>;0|<礑ìúMƒº˜æ Om`”÷i.ž¯Ðý€wÒÞ¦b«‰È÷Rç=ÆQ×Si*¸^ègi’)½ÖKõ7çÀPÝæ¯†+ ÞÓV“½Gô#÷Ýë•ñZ!Ö/ÍŸ?ÎùÕyãÑsÙTÐN®ÖÕÿEÊ ”q~9à¿e]ΧÖ¬/V# ¹5ÙV­±øþ‹:¯qã¨äˆþëSaã³Ú#ÂÓ üd¶épó¶s¤g—Q“ؾQ7ˆ YTMÏd2|ÚÝ®é¶C^@Œ»Yœùì*öuþâx-§zã=„"ÙÒ¹ið½íÚj‹:æ@Ýžþõo[MbÏÙ"©•€ÿ»í×ênkê¤ÀúÖ ‘SF÷Á`rþVe›Ïœ(“©5;¹g]*¨íÓÀ¢çì¦9ßïÃûKÒtõ= ÷¥€Ý5 ,Iéb¥––ö þv›¶ðñý±ŽÍÄzcç…´Ÿã¸n6¿¶ÈîÓ!»Ëߧ‹+¯z܇EíÌ–wP@KaáIŠX€—?ð{ã„öÒ\.~?|þGù!¦¿Ý÷aœ-µ:®ø6 êh®–§ÂæéMW\¿ž/9jå .ËÖ×1õ'GNÖ>ôZËvé$4[ ?ÐW}frЦˆ\ŸRÍsª$ËŸÒÆM²+zŸvýŽqŽß9üðׯ¨¥Àô›Ÿ[ο¶yß ŸÝ´eç+>$sÔNÿòÞ~P=µQÿÐ)S VEs3ùw'ñ^åŸý”š®†2K¶?ÅæCç•׃©ÓŒR ©›çÓé¾)ðm„ɼ2¯û°µß£Æ/=í×ÞôúØKûÉ"_8*4<h$àØäcÄù?Ç÷sªÎ#UgCæ°“»%)0^såü¯Ú)à<ùJưqø¹Å= ÙÊ (ïy9ò£a¤Ã+x0ðJî²bWÆ­³ã¨ëâd™¬­c§kYýS þ© Nï“’¡i«Þ§•ƒîÃõïa’i. t·n¹¡u”V`ÊáiàîŸÞöGß¹°5¢ÅÂÝ[§_¯¨ëÇ›ýzÎÆ)0ÚeRFíƒÉ1uf‡ç-ï³uÉxVÇmÙ}?¸3=îFÿ°¥ýÛž9þ3Åõ+__ñõyÕóSƹ{½Íœ’~)ðóJ73ß¹ÉÐ)±CfÌ÷lxñºbCî,7 ÷†Êi÷ÏOùü‚¯+yªÊ“Vàø‡îš¶œ©™Qçó7?ž ûvÕXâY’ ëþèRÝ Xý°{%þŒû:Ùüs†“‹†µ w& ÂK>Ϙà \{£ÛnHºÕMOY%öuâxƒŸªvetNõ±þm˜Ý{°k6Ð{cP”±mßb¹#ã8ù€îÖŸê4\ Ý;ÌŸ¾J<˜ž85¤Çwóßî—Èp|Ú’ ²¸$üÆ×Dh¹åõh«lh£¼4ÆŒí·Ø“?Á*Lßö í|YÞ ÜðR5Ã?ÆWçŽ7hÑe`BL=µ%Iš”ž/’v ;g ÀC3xÕ³a–íc9¡ûÞÓàVTk£iÆ @çL³éоŠã{[÷\çyIsÀ6ôÀ†D]íEQü lq¿›­É¥­7­ß=÷…O^ôÿ~ÜUìê<Ãqúk“-’$Øèâþu‚a"Ô¸ýy7̹Ú!¯Z²óR2òíÞÞ’K~p}ÞIC¿Ul¿‚îc+qœ¨ÔÅEÏîÜ>„ÝÛ_%@íi“^®h‘ÍÖé6@9“ƒáªPÆ ýEþòûêqVç}辘ê~ßÇš÷kv;”ÊÂÊ=Væ®ézj‘=,êðøú¯M†Púj”vÒNÎù†.Õvk·öÎ}UçÛé2™‰ {v¸¼R03n„µé}ãå=¨üбs›x'qŸÛ¾0*á{¡?DA‹ú-7Í‚U§´$¯ŸŠÏm^çü~^Õu²ãÌi5õÄõ¶·á>…"ä påîl%ɺ Ff7rÊ?6‚1™)7”§ýA[Z³§ÕeH9±ÏÝÇÂRÜ÷æñmîÛqÀïûø2Œ3êêÎ&/!®¯0cI€˜ÉïÒ_½>û¼¼ê sÖÇŠqÔùˆã TäÁ¦ë KšyñpõÅÑîÕöÞcë6¿@º—Ú’è=>kíðÀê ë0;£ åã8ÉNÓN L„Ê6NC—Æ3®Û=¨õé½fájGÐÛ]+2éÜ8v¯×G|.v:â7eâ<œîñ|Ydí_tþŠãmyúUÚ¦s"¨¯]IãáÌÊcFÓñ{ëa89è‡- U/HÈZáúÕž)à?ao¥éR¸=Ÿdn\ ò’󛟻õpi5ñÞ\ÕúVbœˆ’•gòS …gê»À–ñ ›•¸Når’vÊo–Ö°ë;Ƽ^µž«'3îí$ ÷мÙyN]°H:½\¯z-qÞWõÜ[…q¦u—¼ZœFº°aÁû[°v~÷Ócî±ß{ÛÏ0&[5Ã×¶¾êÍžÓãØþ¬¹È¤œÝšâýNzÿ…Ý_6k¹Ü‚sŽ÷¸÷I“Û¶ž¿/Ý®û¬Â*>¯Ëßb? Æ•ìö6zLn“oQ{î)½GóSzUXÞ5ãÜk¶q:_¹V«<6>X›Œ×hu >ŒÍžÛI÷œ÷5ÆÔŽí{X°õÑLñ{ã÷ž¬Ÿ5_ï›´M½ŒFv埥ôœ†Ž¯Àñçªrã!ßebçKOâ`Æ­âùZÕîAëNοžêÍý„'¢ëWþìÞˆ­xψïkÐý“ÏÒï߀¥Åo÷Í‚1NV¾ÏGôÀ<¾ùíäøˆ8p:¸eÅÆ§Yâ}3~/á{¿ç. f¿oüvŒP cÁÛrd¿Ì÷Rš×Ù}3KÚ!éÇö1Žf¿Ú-ÞiÇCõ”cãKÝâÀ»à’ö‚ä,(Úó´ŽÆDØ©52ûŠ»,=Wž3m¾¿¸ÿ~$.otèòÑ uðÔ$ ã/⼌çaÕÏM‰qT>ñõ-Ȧ¡â`àæ­“YÐêÖÞpË!!õ^ô¡m2ð[׿µç–™@ÏËA\_óõ÷ýï£ó:~Òû±†â>˜ºn0Ά—cRÍnÜ‚„S§§¯½}EVh[÷çùL–|æ'd;7Ï7™°Ì¶/«¯7R~o’Þ7¨—œfÝGÓ8[&{ÙôTMw¿[°°¢àhýÕ7Á<ïêÖÿ,º£M›ÍÛœî÷Ú’7•oJg‰û ôþ¡)D‡ê¾ãÊ{q‹Þ4ºŽ`œVŒ£>¦o| ê·úÚ-Êé&„›ŸÎ¹ç”§4>õq ;Ot!ôþï,>0³TffÂx#`Àœ Xû Ø~ê{)¿ïOï‹Ò82Œ³®¤©CÌ3K‹ÓëM¤7áÝÌ«ƒeÁ>y^é‹mù}qÊ8·lžÐø~Åë)SVŸýúN:À'vŒtŽ)Ðû¡ôsS`œÔž¹&÷ˆƒ”£Â„¿Ÿ—kj'ôË‚sçZUì°æë²Z¹Y¶Ür:ë C€qˆÿËü\}]0̘3v¥õƒqj 8¦Øpñ&˜Üí=»Kµ›`8«ÿÌS’,h:¦Ù‚v[ä ~vAÏÆàë››Óvö…Ūã$/í€?¯Gœ”o½[Z!å÷×øý:/¡qÂ1ÎtÁ’Ñão©—×:V–(¡ºÙh®“Òw×G5”‹ûÍ”kë ·÷Nyi1ØJ<áû÷œ Lï][ÿ¶ß£Ä8×Ë{š h~¶…Tô]–¨„Ÿ_µj£‘o÷káÊTÎ~ÿO±üBØEmÞ׿LÛUïö{s1Ž^Ήw#;Y³sK¶~Äñ>”PÑÏiæÉËJèw.Énò›L°¬«5ü°5»3ŸW±ç¦èº·9{ߊÖŹ2Y‹}½.ÏPBì2Ž(¡çLaÅŸ _/­ý™‘fËøÑv ¼Õ³ôñt ÷Plx½ˆç&‡µ¶DÎèFÏw$8nÂÆÄƒ‹[(Áyà¾}õ6*a®ÓÎÚ·3¡v‡œÌnŽ°æž°!åê㟙¾ðí¬p1Ê òŒ3µÚ/÷ïo5Ï–n~%}U?VW'qž©®ŒÓ}‡«Ký5 •Õ®=ø{äÙºwÜv&®õ kõÝiZÜ·â¼uÞ§ž*ÀèýzöüÀ8÷úÛõ:0Bó?XÕPBB‡9šk}3Á´¿<Ö8ÍÝ ’ÃeíÇMýÄû×ü~5Ï{~Ÿ’ß—¦ûFÝhþcœqÏS³¬ÒoÀÉ:ÂEC—+£MüÍ2a¨z"íü}„Ug¿֚Éxö#Ù¹|àœêŒ¯crvºVH9™Î‡:ÑüÇ8Oj¯Øçqÿ:¼z㻩؟ÀÆa%îu΄s£&ÎÿàÎæ±£É¤z§Uœü܈÷A~_™ßK¡÷G†=ÇêLëãlÐtîõäkÐyqB«>³Ü5.¯×O+œÇ‰é¡€qΖ¹MÜL‰›úÁ”{=\q”¦‘#aÖÒfoŸv©òu¯­ê¾Fd™ìÝ®—Žk^ƒ¨ÇQ¶ÃœÔع¨þÜÇ`j”ýì >§ÈË=3DZ·*õë2‹=·‹ó{þyñßçõs{ÏٲьËÞ“Ö Æqi°´ÓÒ°SOìt­^óˆSr>oû4+sbùfCŠí„'º?Û§èÏî?v¿þûÐç {Žž“É0NbÿßæDƒ}ÁÃV’cáZï—ñ² 0ÚòQzF{¸Ò7¼ÅëñN¤Åþk7Ì›³f,¾e´| Ûßï"ö[þO~¼ýÇÜž½¢ÚÓúÁ8›Ž?©uö]˜¶v¨b6/«6f\]cî§ÕÛ–ß?aïiù²<³\”ë䎆—“_¬<þý{ët=ˆíÓ8Á§§zã0 üwK‡«bbÁêk¿½Öe@ßÚîK:¥Ú²û†öÄíëÎ#ý|ÁD½ ´÷Wùý‡¯{¯¹õµ|+åïyTåׇcœ÷Æ~vâ*¬˜2zkü–Xp.}kÖbN,Ÿöº|I´ï»dI\þ¯þgGÚù매ls1Œù'­ÄñHΔ]…ÊGŸR»õŒ3Óå]¼Ý2 Ìxé®1»]ؽ#;ª&+d*{>IÅ÷ô~ÛÇñýˆúñ.ù ”y3žºæ­¶˜—e€}@Ó·òÜÀì¦mÒöù“Áù臱}ûMfó*Þ§ø>Ÿ'ðûttÿ‚æ­Æù2Ù³ÅV£¾Âî³ß€‡ñß—¶è˜µì/-w ™å?–›­ò…¹£­:õöïƒÑõƒ ø{[|ÞÈ÷Sù|O]ÇL½Au¾„O¹ø<àX K¹é[- Û7û˱‰PûÇØ~¼Åõ®Òóh™8Ïæó_þþ]Ÿ°ç Æ™8þdVóÜK°yAL̤ó7 Í¨øH» Dz»<ÒMž:ß´I‘ÛÜAóR|büØüÄP\ñy"_Ó{ž–¿½£À8=†X[ô¼kÏ„O]³ýXv~ø9xí]¶oàÆÖ=£Ø}ŠYâ¹ßçäûe|žEŸWVâüK]G¸­vòGÎsŠtvšxö¹õ]V®Ðaó•·Ýz¸’!¾®Ûò%0ZhÓ™ Èڴʶ¬ÌU¬C~?bE½ÇùO\Ç1Þ{Zçº4 çx›‹°çl·˜>߯ƒÇóÊyûÚÝ…Õ—6ùòØhý'dqHýæV³à‚…ÝŒú¾^@î ⺮¯älÿ”î+qüRCá¼Âm©íµnÀ¬Ì‚ņïî@Ók¾=F ²ãëCrË«Ù`­ñþÀÏãèóÞxÓýØwR¿þ'ë=ò“ÿ¾žÇ8Âif³çAX­6xŠ÷ ë;0ÜhKCýt?Køc"¼>Öˬ'/Û–‰xï‰ÇQÿGÊdKZ\™Ü³ïyØ‘™ô´Ù¸`2ª¢òÓÙ;°%s|5‹ÚcÙ½K óoàïç·~ZjÒó Ž3{w‚¶6‘ ¼Õ×jÌ ÐërâíÜmw`Ûãý>ž0góÜNxCÁ 46Ž\ÐÇv[·»ÿ¶¿*ÃñÖ ¯«¶:ûßO´r~º[l:6ÿÜ­Û&°åa^ì¹ë !ÂõÅG³!åáÏN÷ˆó'žtßÊø·ý3ÆnÙ´»yž-z4ín¯Pï‚òBï w VÞ!E‡;V°ÂlU×»rx18âGŽ•'ðþÈ×ÿ|>Èãñ{íU÷Ï‚1Î=KáêÓÐ1Žh·p®t\p§Ûˆ;sÛ°¤(Åöíüh ‘1¼»s°ž}?/ñ¾;ßw¬úÞ@8Ž·hû—}ä'¡ë¯­dé h!l75¾z±ƒê6üèü½°ê³Ötjh4Y|ÏÇRýâ6Û?Åq°i´;89bšÅùôÂþxÿÊõô9ªtȽ½?LÞÆU|NÐï} {Wenƒç%yŠï±üí‘ô·GR°Æ–G’ûLvŠÀTå,oÆMáþHÜWRðGùÌWÒ© —Ê›1U9£Z`þ ž’û€û¿ |j3Ƨ®êw"päŒùÇÕ‘¬0þ¾'r,¢(”.cTsæg¦èUa¦Tõ7cÞHºâ¥DUaý )ëOð’<ÃÿÌúË`L*oæÎyUý$õëOÅø aŒMmÆxªyÌKÅ›yJ ÞáÞ̧·’qÿB÷OÆü‘ÿpÙ_xöþ#÷ì5ø7%ƒy­pÏÞ¿b1è0nŠêOlÕĦ⌆`Æý«ê¿"܆¼%ÿî±÷Ø`ÿ¬«Í~gÓ «â[îÏ œYÍ=è"ªx—'1:YÍy2þŒÿǹª—JÁ|̹”cR LÕ Ó€E d^æNŒKŹªQ(m, TJ†ÒÁ" B©PfX,‘(],˜`T9JŽ……’`ñ„ *PNXDJ”„qU9—Šóô«ðªúöʳZò'6ƒ² JÅxT‘¬ÿÌ£ÊcÌæÙË™TU½çôªœy ‡³¢•3îŸÀfÐc<%óíõg>žBA›1¶ªŠyÐE²7û _Ïä‹Î}=eb4l*IÏ¿òE×eŒ†ò?ñÿþ;NUôƦ’`S a>VÂM¸¿{ìß=Vã?¯Çê°¿£Í88œÿ'ø|jcû£ò´(»úŸ±ÿäŒ#x£s¾ªãþUõEتÁŒ­ZÕßSÆ|ûòªðUÿŸ!¨ ”œñU¾W*‰ù+CBŸq«“ZQ_¿`Æ¿‘WáJ„0oOμ=¶„ª=õüû3Lðö”3ß?¡0c‚{ÿ ÌjÁÛ3‰y{z3oO}扞Ä8‚¿g8ó”3¾ªÀÀÑg¼ %cN¡TÌ79è/88ÿÿ¯*Çÿ¿áNÈ«ppþŠÿ'ø|F¢t»Zõìêð*ì¿(ÖPªú| ü¿¨¿{ìß=Vã?¯Çê²ßI¥Aù;Ü÷=ˆ1(8ÃZYQ2¬¹¼À°Öe ë¤*,ž¤*|U!Ù½Q(LúpÆVublÕJCEÄ|ãŒ3ÆùªJ”FJ…2ÉDéb‘£ÊQr,–(” &UrÂÂQ¢ô°xBQ•(QJñU9gŒó' ªð'ªú(;1†µÞŸØIUøbåí(_,ª=õOþ3_LÅ8>AÌC™3Æ„õFe  _¬‚ùÏG0®ªc2 ì }ÆóIb>ÊAÌ{UàXËcU`ÉÇZðR–ÿ…ë?ò§ç~¬fbP¬1½*¾¬åS/a Š Æø‰ú ÎOU¿úPÆÓ抪džõ¡ðÇþÖÿK=öïþúŸÛ_%ìßé0Æg9F t0yƒP*-ʯþgG'Æ÷©¬ÂŸÕg Ǫõ˜ô!u)V“?UÉŽ¡Œy&c̳ ” ‹"¥ƒ…„R¡Ì°@"QºX$Á¨r”‹% %Á‚ AU œ°p”(=,žP”S ʧ¸gÁ¨ ”nN‡ãVg $X\!ŒëãT…ÙŠªlûM‹.UŽ2üW>šNGʬŽ`¾Õ#- ¥…ÆŠÓ›1Ò$ŒÝ˜ÇX·‚7}c|(ô(·º’yZG0¶r0* ¥‹Œ*g×ÁÁ÷ùï8ŽUù>Aýÿ+ÿé ßç¯8ŽrlQ( cW—ÿ»:¢ ÃQÉjN(%ó¹8ŽÊ¿ç¯÷Wÿ¼þªÇ~f"×ü×?˜±@8¿[Ù›1$¹W¿À”Ô¤É Æ5 FeTáä ü4TJ†IÁ¹ ÆÈ À•Á¼ý½?sr“PºXÁ¨r” $ %Á" AU œ°X”(=,˜PT%J…“„ÒÇâ cäÊ@é3N.ç§qˆ¬ DŠ@U ÿ[ðþgŒÜª Œ*Ü´Šv”›¦DéHþ+7­œñ’‚Qª*ì4m,P™„’1nZ%cD2>®‚±(ˆc)e ô'7©;e‹;1V®ÀPsblJ1ŽŠ@é`¡¡T(3,øÈÀÐÅ,°Pò?±@†š>6…TÅ?á è1ˆÀxKÊ¿à-Wá „1†š>6•0ÖX„,K@¨Máï±ÿ¨¿ò¾Ê{é?ë¡øU©û&ï•ÿîÉû#ï‰ÿJä=°jïúÝ?êu¼Ç ½Œ÷¬GŸzÓ¿Ú—þY?â=Hø f­nnc$J{M0ª\ëVcx•/—3j “Qû‰?*OØ{Ä/=¬å¦ LZ3ì‘(]ìÁ¨r”" %Á¤AU œ°_(QzØ/BQ•(&LJ“& UÙünç‚èaò„2>ÀLã|0ö@Øiì!íg§Ev¤¼´ ”Ö}xgÊÇöïBy‰*–€L¾žÿ˜ãóßñ«r|®¬Èø×¼C'LZ%Jï/XÖ‘Œq¨ÀdN’þ=ßù{¾óŸ7ß1`?S…ðÙbâF¡$˜¼!¨Šj¿ó²ýQJ”&t(J…ÒÃÄBå1NQ*¯ /Vàž¡T(3LúHÆŠõf¬Xm,TJ ÁŸqÏ8/6%ÁÂAUS0–¤®pŸ‰ÒÅBF•£äXðQ(],ú`T9JŽÅ…’`AU œ°(QzØ BûÌ›B(ªRX_bsP¢ô°A„¢*Q lI(}L®0Ö0žRÒ_0•83IθÜ;É›J8J‹*hMþg{¬Ð_ÿG{ëŸûjgúýé¥î£ÿÎ*ôÏÿ‘¾)ôI¡Gþ¹'þ;ûáÿŽ^ÈûàŸ{à)öG ßïŸØQ( &WªeVò²9÷QàeËïQÁxÚÚpleŒõ¨ƒI$ðlQ2LÆ<”&d8KÊ0Æ´­Dɱ¿E¡$˜¤!¨ ”ö7%J“6U‰R`ò&¡ô1ÃX{£2P˜Ìá,¡UØm!( &wJ…’a’ç¡ô1ÑÃX²{£2P˜ôá(ív”í¦æÝ s6TJ&ùó&°o)ß-%ÉèLÙ×A](ÿV1ËQ2¡Ÿ ûcBïÂâÑÃâ EUó;,"•pW )¥‹ÅŒ*ÿœF9XJ‚E‚ª@91Þ› Nù/0XˆI(},ÆPÆgüG\k]Æ´ø·Þ¨ ”ð`Òßû]Ïÿ4þóæf,f¥ðÙaâ*Qz˜¼¡¨Êjðºõ0‘ƒPI(}Lè0T9J;¥BÉ0ÁCQª*Ü[]Lö`T9JŽIŘ·þŒy«ƒ„R¡ô±‚PIU¸·y(=,ŒPT%J’„ÒÇ" c…âÊ@`Á„£´±hüQy(OJ (¥BÉ÷V(&oT+*¹ÀýFI°¸ü[Q¸6c€ç1æmJ .¥B™aáE²âóFe $ü;¨<” ‹1U‰’aQ†¢*PfXœ‘(],Ð`T9JŽ……ÒÆbuB)ïÖŸ12…â5C…¢T(ãÞæugüpƾ Û›q3%XàÞ¨(” =Ur‚W¢$Xô!¨ ”¿¥‡ U‰R`#HBéc3CU¢dØÂXcP ’PúØ ÂX“ðFe  °Y„Pž¦?*%ÁÆ‚ª@9aQ¢ô°‰„¢*PNŒ/^’aS‰@éegF­CáÏ?ë±ÿÛïªÚ#ÿ{^ÕóþÕ}¯`ÿ3s³uLø}•(=ürCP('ü’•(=ü¢CQ•½‘”µ­ƒ_¶Ž6eãªPfØc"QºØc‚N®0çÂDˆ¨÷× {ˆ¥‡=$U‰R`’$¡ô1QÂØƒ×•2ÀŽÒÆòGå¡d˜H(mL&oTJ_8ŸÄ¤*Î$1©ÂQÚ˜Xþ¨<” {EJ{EJûD*OÏ_¸;‡}A{BJ…2ÞÙ™²°ƒQ!˜˜Âá&f˜pn(ÜÇÀäŒBI0ACPrLÒ(”5Ur„U¢ô0iCQ•(&¯å œ„ÒÃ$EU uÉœ„ÒÇ„cIíÊ@`r‡ýÃ: %Î1áóþÞû{^¤ñŸ7/rbc IëJBécò†±æœn}Lä`TÊ@Ø›GU  0±CPå Lð0T9Jމ…’`²‡ *PN˜ôJ”&~J‰ÒÅöîQXÁ¨ ”>DJ…ÒÇÂcÅáÊ@`‘„£´±PüQy(LJ‹&¥B™añD¢t±€‚Qå(3,¤p”¶°§R¢´±¨œPJa‹+¥Dé0¸ e†Å‰ÒÅ‚ F•£äXxQ(m,>TJO‚¥B™ ûþ¬ ÍPa¨JáÞ0gJ‚‚ª@9a¡*Q:X¬ TJ‹6¥Di ÷ƒQa¨raŽ„…†Rug qTJ»'å‰ç¡ô°ÀýQJ”z(ª¥À‚OBéaч¢*Q ,þ$”>6€0Ö¼Q(lá¬!˜¡ÂQÚØ¼Q(lá(mlþ¨<” ›EJF*¥‡#U‰R`IBéc CU¢Œ7^‰2æ‰ÒÅÆâŠZ‹,]ÿi­:7úg=´jïüO8 ügó¢÷Y Ð«þwΉ„ß!Iø\ñ‹ EU¢ø'¡ôñKæDZô‹öîy ïØâŒ*GÉñ BI°¿„ *„½(ì/f˜‘Â=.áŽ,öö‘$”>ö0ö°õFe  °‡„£´±‡ø£òP2ì!(L  ” e†‰‰ÒÁdÊ@ûN˜PÂ~&TJ“*¥B™aŸˆDébŸFó!áýXL8L¸ á>ö]ìÁ¨r”ûAJ"̇P¡ÂáN&c¸°‡$œïcB*Qz˜”¡¨ ”&§¥‡ ŠªD)0Q“Pú˜¬a,a½QIÂ;¬˜¸ú˜¸a,y½Q(Lâp”6&²?*%ÄG9aR+Qz˜Ø¡¨J”\‰Òö0ÑUžÐ:Õ`ßoÞŸþþ§¹T¨.”Éì~I¶t¸} J³ú=mùÍäºå-¹Ç«½ž÷óÒLx¬õÖÿí±tÑwi ÿ§#l'÷“Òý¢4ÕhxvV}cõ1 PŠ˜— xבùS^,“µV …˜±–ˆ¹ 4êŽÛïÓÏ~ ÿ–>‘ù… ã<  Üt[Æ£p}F¸¯÷Sø¯qL§g(f†»G,ï¼pü÷ÌžØ=]ä²SŸ{-Pcz3Ÿà>À<Ž0ž Çñ¡èL¯Ìƒ¿kbºæ²ps¶ËÕQ¯Ò á¨ù}'÷>å®B}Õ½™/–%PŽ%ãÅã8›Ž-áp*ó”æÃ°íMì]·«iÌ·ÅN„zî8l¡üߩ̗Ä(—Íý7Ÿÿ`O9¿ðªK·ýðóþ£ØûÃn€Ãâ–ë­Iƒ”†š½vö·êËkKžt»/m»i*pîN~·`Ïäñ¢ß ÷ûK3^Ømò ³ß8RágMçù-fíFj Ì èzeæüt÷40»÷ü£A[æiCÌÛüsû©"ßóV9÷—óã(ÇÄ fx˜Yuõc¾’çÀiݳ ‚à ªm«æ‡ÏÞ×/8CD}»é³PFƒCû >cD¨o׿›>Jä°rÞ¯:¯q<ý%+Ó¶Úýg¿ oœ{|l¬|Ú¥‰>-Ü’ó8OûËóŸ[¿—Êde2‡BÂfh¹É¦ö¸ò0+`eÖ¬o©ÐØu¢wˆè¯Â¹€wB²ŠUu'd§Á;™çïߟÇ›»ÜûŒwÀz]ÞxrÁÏðeA@ò²{©ðmœÙBƒ-ö@ýýÜ™?Ÿ+¤šÝ8>« zªÛfŠ~^܇”q…Ÿ‘ùaœË-2çŒ-Z -‹$.=c¡çáu¦Âò‡mú”;å#)˜o“+ly–ñlÞXÐô¬ié|#‘ÿÇ}øûvf¿ùí)0Ž§ËžÄO.Á°í°†µþ¤XØ#¶R!óõ‹Öï·»ÂôÇ1ÝŸ\¶ùZÜ–qŒÅ8êüÆñZ©žç‰>*¯UbágåÜ=CRÁä[ƒöeãYiB:g×;q(È iŸxïh%r9ŸYmïÕÌ 8w\×8þóèomf€YË™šÞÛbaMêëÌ) a²ªnß¹ã9——œlð[L½î‘Çêg¼c ŸC¥È9•Ú]³_mSæÛFy^JŒ3ûnŒIêR+0¿yìÎáXYÖÒ621üßZ8ÍiãÂ|G@v-îw<ôGßÛ¿èɨùkÇB_£°—Ï~—r> çS_/ÊÏQaœ/¹ïR>^™Jvg ÆL±pmæÞ.w¤À SÒÑ3Á–n`¢_y'iÜÀZô‹Úx#=s—Ïw‘iÐäÞJ§£7›õñËe²!:Ø æÿEY›€sÀƒ–šþ)`Qýädλð`¾¹ h;wL­¿ÆrŸcà| ʇÿ.åþØUý™%‡æùRâ-Ç_-ÿ²[úË¿Ÿ»Ö‡‚ÍÆ0W'î‡/rp‡U‹r¾îdÚ‡OvõCê\¦={i×RÁ}¹u×1Ì_“ÕƉnÞ[öÀg™³a‡ÖÈìXX÷%ú`÷á)ÐBmÔiƒ^§ÏóV0?fgÈÎ*¬ã>h2ãIоˆRê§øYÚ$«Õr+mK¸µ¡f«Œ“²¶ô¹FÈ:2ì…íÆä‡±0¾zâך]S QsÜå™í Ç&¿ÆÆ¾àZ÷bÆ©ðØ[èØÞñëô—·;'‹¾zï§N\{ýÓ')ó/ý<Õuƒq:¸î8—Z±‰œþ©}+¸ Í{}:4N¦R×ZµBî׎{?Ï…2on'úq.6ï+ÜO}Õi±=L™0ŽqÔÀðÍä×f¡ax%à®~&õË4‚ ßÉó¤™"—s[h\#Ñ?Žû»qEÊ£`ÜyŒãcÑöñGÅrûÝw#¢ÌyÝK¿Uy2ð~·5ÔìAe/¸csaïŠ,GÆí’‹þÉÔ?»RêÙÚmLµyߤÜ'¿jÿRaœ—«¦¯ëßzÙ=¿¨£~a\ødèæ”%{X{~Úˆ|›ë’Žƒ^yøåÂO`¾¹ÕÀ_~^²<õ‹ôD_‹áƒvZˆ~œêú¹‚yð5=~v‡Äåý£¾½Ÿ˜u8ÉÅðD2¿eg´s©$®m…3%kPV÷+J·uÏÎ VìîjœiãpyBœc ߤü÷¢>xc€û,ªëãœ×7²šxw'y!ØI6V·Çé«ÎO†ŽùI­v;ºÀà3M«EÇ»õ­veŸ»óé6„'6]x¬LôÃãuʹ?êúÁ8e’ro{‹ÝÄI=QÂŇtÆË’ÁÕsGX\ñDÐ>çVàïäÃ|ýEÎ&çÑQÞg¹”û£Q¿a:¾Ç™ZR8qä~ÒFFÞ”\W‚ïº S¾'A*]š¢r‡ë'á'äÃ~~;à>Ügͯv™uÓŽë½[r¨þ i«NIŸèj0ž¶óáb>xgŽe-Õ8HŸMÜ’Òô&”ß“Ûñb´Ñ¸ö8y‰-8þ¨Ÿ–×>±W·OسÁ ÖWZ¥Íïç 6Ügu€‚–ÿ”6=wáJ÷vSàmþ0ƒ¼=”;Žq¼ì„'ñ!¢¶=lsL.k¯ï“íe­ λ³y‰óé³€.'öI°zœàl6š¿}ÚEGY‹õÙjÌ¿P!úÙ©ë㌊ž^røç!"[;, aí›0°Ì3,¹kdiW«½v"´Y”V«ú°IÀ}Ù8@øš–ÛŠ¾‹´h2ŸiËßòL…qú¹ìù`õ2×ÒlZ½a7a†[ÎTó—·aVå‘&µ¹ÀÁO®Ã­{@?5`Ôøó˜ûø1¿c)}^W‡Yq+2OûŽc}Ö§ÆÕ2Y¼T³i'‹cdiÍ=Ã6­½ %W§Ù®ºv>ÎÙþ« ®ô©6i‘ÖVW‘/þP"€n0ÀæÍ‰½Ÿ<G¨Dõ=%¤6¯XÒhàdPímtôÆMSZ7gɸeß4LÖ/WÝÏ€ù˳·Ü†•+Š3/?±ìž^-:9çwÒyÅèx;gÀŠI“aO‹´êÞM™ÿ|]¸g•þ¤¦¡ß’Žå¼È0ÎhYìŠ: Žêƒ{îµ*®µÀï6”oÛ_øË6/ õº?Qä‡OT8èeýðeë0pÞüà‰Áy½ÝêALÞ%íÇ6l^Âø}g†©Aï Ì㤶ßßÍ—nBôIÓÀíæ·ÁýÙ6ÓBmÙúc*l°µÛF³çžãSÚÃàMÞÇ×Ek0?\ h(³mü;æÃGý7ƒ1ÎÔ+“íN~ Æ®é꯱ým˜3×6¸¸Ž#–7íý˜¯ª¡Ègç~ý|~iýhÛ ß^Ÿ¥¼~)‡Žú!‡c·„‡CVn?IÞÌžÒz³ùÍU#ªLöu”Ûyó³$Ü7¥Yñ“8øørÂÖýDx¹vönÅVóuuäP +¡qâçÈ9|žN9Ñ Î ¡õƒqb¾˜à”÷,Y3Jñ$£ã-ØËÄmÚ›ø¬k½Ä£¶‚qP§@Ú•{9!Šñ¢¿á4³ÂIÏ ‡0nv¹¸Î§¾Ù6¢±º~0Ž‰ÚØûyéónÛýÙ· ÄQöÝñ|H‡žsØRêÆúÎ,q]S(à?­G3ŸûáÀùšü9ÇýÜïk F±hý`œùéëŒ$ºÌß/¹÷k{wô J€ÈôÕ¡†+ô3ޏ¸4½,{U؃@•ic¹ÞC3 §[°uécÆ¡x*åœkš—Z?Çq¡aéÅÀH"w¨çXKé}·¹Í7N`| '˜·µäÐÖ£sÄï…sì,5ûðõŸsԧߌ­+(Ÿ=Ç?]6´cÉÑH2nüFGÉüxxì½f˜n]…ŸÂÇÃù+ïL{ie× ×jÎbýS—lqÑÛµ·À¨°‹èg¯Îso¥«tË–vˆóšyYkàT­4Ãc ãA/åÍâ®õìàݳó½µîÌâܲv‹0QÏ¢Zåoßá–ç/íÓÈ~-úØS®Ä¨ß|ŸeÇÒêU=[× $ü¡A…Otl{µaÚ›xÐ]Z/èC+˜wöÍ­Q£gCq;|?ˆ”Î;‘–kÍòÝFä`ðòϹªµãtó™ê·2ò¸lË’ p$DøÀãá€[kë Öj°b «×>„¯?8ÏPÇ8ŽYÉ‘©ò‰õêf~_ÌaaðëÔ]¯n±ußxXóB×q|›@þ}îLyŒ”?ŽãœlÝÒOy‘\Ë|óŽDøpÕjz̹[Ð}ýÜͶ+åXv&¦A ãRõ$¿ÊÖœ›Ãy¡öâþ–:/q¼ZwZâ¯p‰tpX²Â]™3ŸÆ™õõ¹ç¯œê×߯ é¶sùg,å=xAüÄ­ƒžw˜Ïž·•âz…Ï_(ï„ñŒ1ÎjÒuZ¶á%²±¹úìn"4K{?<¢=öj’­ýy€UW¿fñ²¹Ð\xµ××o fñÛÌíÄõ’›‰C'ŶOV) ¸1ýØè6ŒCÌ|çcÊd©1Kz­¾DV.¾FûW"Ìv«¼ù%;”†§ãy@T¤ßõáýçå ZÂûÀ–?ïÕ7‚Æýºl)÷òã¨óÇ“ž·Úþ¦åe"Wí[8ß÷6øÄY[ÙSZÄI˜(àð¦/Á²)³Äu6ß­³tüÃ$-wš¯8ŽìkWËå‡.“¬GŸÿÈ¿ ÉnŸßm!Ï?7Hšá Wpö¢£á ž‘í¦¹›°y€PÞýDàû)t¾dCóÇÕÕ®æYžw™¨·SÚ%A“Ô¹ÃvˆƒG'L[æ8€åû¶³;O}ï[…x X}d2Hï tÿ–ì §\²¬L~/ÕÕ¹<{Ø'éž¶ÂNõh¶_ÄÖ'eZ—Ù;ã/“ÉEÐüa8tühØèóM¨Û|ä±##í@½lhí-î#sþíNÀù,|ÝÄ÷YžÒüÆ8‚»´Î™Ëä†uϱ%Ó’@c¯žmó»7áêñIúv°ìiñ­.·§³Ÿ» óÁ û·_zh!ÆQç·ðù\ Î|r™<éÑœt?ñWµM}ÞI0íc—‰¹åµ°É1ɰêˆ"WVý&øxM™³ìИ®n”s9ÿ’D¹tës»§è~ùP‘oorV¶ëE~¥”Ïß9_HïgdÜÃþ#¯í¦CâuL™å!¹ò%¬ª?#Þ\Óý;~½ü1€q,ºʹ´d‡¶Õ¸>Ó&,Êýú×H´ç=~ßפ¬[R—˜‘E# ;ÏÏqnmÛÒ÷ÂY:“áx_|/îE>Žq^e“ uef׿4ˆ‹£FWµZYC·ð5‘¥Ï¡Ã¹Z[>2$”›êo6§cr}Ôù<®[FÝ7Ióã<œ½åLŸãQD5o•Wú°žžz®Þß²Áv9ìÜfGÍųÙ9’1Ñ3Õ<¤H´ƒ$íÿê'þà÷ðy›0›xºz„›5ùÚhÊwÆ8 —âS¢Èù–ªe¾Îi°¡ýÝ{]Bn@á±{ÍnÊÁ/¶QÑų3ù:œp®帘ˆ<&Z_ÏÅ~DŸtýŽqZGû™w&ŠPý4˜¹Âaæ‡Ñ7àÁ²1š L¬rÄýø>áëqgu!KA3¼¯‡‘éS)åº?'"œ›KyU£Xß{"Îóù:²*F…qvþr iMâ,× í›G?o87òâu‘›2¾uÄ/{üÙü¾¡çY£Ä¼âçÇœ—Å÷ª®÷5n”É2ïf¿h¾4š¨±à»ÓA²Á®ûjÅuèYÿà›&§DŽÕ½¢ÀÁ %Œÿ.rèõor¾wÄišªø…K¿jpüüæŠÍ€öG¶_Šq°yÙ¼ºM×è?|]q:œª'Õ[þóôx,Pé ï}È×ÌõÆ}#t=j}K¾FGØÃ“Ö­SkÕƒéSNÔ<õ­¬—–êþÙ „S’{ŸÇÐçÆ)ÆÙNSI iÓµÕU÷;ìÙmÖÜõ×`ÈבÛ;NÛ6Ît…^kO|0$Ž‘‚.,ÎɆa=õÎëC“OfÅÛ,;3>Ç4X!#n÷¢õƒqZ©ŽcÈãƒoü¼ÖÜw7ÞÚ¨s ΄FÔ”žÈx vŒ?ÜîN~œ}'ÀÑõ­á&Ý‘W3Ÿ4úz<Ûpäughò½K˜$¿#¼«?¯õžéYS¡Õ1žþ>ág½ç&ù€#1$¼UߢÎ5ïÂE£)»ŒõbàÔÁ[ý_–9ÁÒ…µ›µ™ €²ëg'FZÀíž:#»oí…eQ³±ìüDNŒzè7´ÿ–²üƒãÑú¹ÁÏëbÈÅW—uÔ½ µWŒ>áq7Æ8Úg×p·z.9(G®+ã~õ¶q€L<çm_lײ/"ïÞ3`ç gk’4é‚"†|\Ñ¥|ð]ØÕwí¼óë¢aí‹»Ó-Ù¹¶?Û_4!|ÿŠŸ«sŽ ?ÿ -Ü=Ûµ£éoç[±e2ý^Kb{;Å5ðú.¯ßÐ7Ô!&y¼Ÿ9= Fù$ àŸiðAþ`•µL<ŸÚÖÞR'ð ]'àxû>ľ½<%†¼­¿ònÎÍ»pGõ¦ÅÅ.Ñ`bàöÍa±Ý­ÅãNÀÊg êßœéLè9²'Пo ð>Éëžï›q^˜ºN0ÎÓS'ÍÅïáêÂÎ]FÖË€ÓYiæÅo¢`P ¿s×úZÂË;UÙµfAöÞëIÕ¿»ÍÑý#XÃݾc2&–[3Nês)Ÿ'ðù=_ qç±åâuV;bH«ÏeCÆÌ€~Ë:Ôlëª×–m1³†—kÎÖƒPçúÖq›¦;Æ'ƒGÂr|€¡G]Âç=©NŽÁž2·§ÅìÎ0$tÍ›ö“£ÀzÖH¥ùœoµ·ãTûi@÷l ]¿˜ð¾BóÇÌŽ‡›bˆR»Õ€5ó3@oE‹ž™­‰²ƒDßä“ÚC¦Â²¶¿j,Ó0gÏ7p¿ù#¸VMðùŠÉ¥¶¶s½èï«ÄqåC„^ éxüÜÓi;2€´|‘Y'î*Ä벫wÐZÇ¡9S9Ðs,Wð}–i7®l¢8O¡|•ß÷T8~ѽûm×MÇŸ[ýï3 h„p |êw h}ËÆ–­ƒ¦1îVBÏéFˆó{>>NÐýi:¾)“%uó|:Ý7†dÖJÍËHÏ€­ç&Ö½¯wt›oYæ¦e-®/iþË×Ù4és_‚ãtÌsA1äpÐöË–o3 PG8¼/>¤›Núië—¶;vϤlŽÏ·Œ`|1:Ž ÇQ?ÖWÄÎÆžN ­3!=åS‡ÑW *á`§ †Æpnç1mÿùSؾ½*–ó)õ÷2üUÝQäªóÇk¨¾HCRó‡>3&‚ÎÜYpfû¶Ž M…ÇÝ~O‘'FyŒ0RÑ®ÿC‘óÅë«Õ—ëO˜Žùmß9ã¼x\/¡íŒ2~‹ÖœAó2!l{a‹U‹¯ÀìÁw/Mí'žÒsRì?GV¼ŸlW_—hm.ò¤g/Ûüîu§wR^vÀâ7¾O¸Ç`qÄ@2ýáãÅ«eBLê@ùÝ™øûXhíž”-Ïïï»j¹vqKFôÿíÝd⽞ªÜj%Ž—uÜþÃêÉ1ÄL½‘’ 'â¾î–ù^aë:pkÑãœ#l€ÍÓ ÝÇ1†:#Â}S(/I…ã·ÕZai]ȽÅ™°<$ûÒº©WàêÜ 2ÅÚZìóÅÏSO(`¦–°› óRY&{ïà¶ÚgL YúòãëMŸ3ÁÂ}è>ây…+´cß¿{^´%®–ÍôæÍö+ÝïKðù í7æ¿ÏS0ÎÍvMº§—EõõÝ,Ø›ÖçΧ+@ùtýØçYå{Cp MeÎâ>‹Ñ™ø|’Òùäg©¦€mþÛù¾ ãÔŸÕÂ}í¬h2Öÿ¹Vòð,è°Ïß F]>¿º?Ïî{I#cv§“¸Ÿ Õy·¬Ñ:3(ø9¬ð¢m¥”öǯRÊÿ”ÏDã(0Ž´<ÿô×ñѤ‡p™+WlÖj}üvšš½þj±«TQë÷¸0S6ÿvïÒýÓRº®ÀªÏÛ,Y¡ëç`Œcã¥;w¹w4±R_àÈ‚‹¾{µbž\WõÅ0+È<“û!<Ü“¯É„ÆSŒ³f@wõÁ» ËkM¶?ýMªUÓü€Cæ8vˆÝ+Â8tžM½mPqde8zm9vè2Deºî t±’¢”Æû¼&õ®GÕÜ'#·?¾ùšæ<¦¼ºR7=ЭZZY§ÍW)¿÷Sc\ÇçÎr‘Ó«N7Œ¨ïvcm93}ímãËYðsýÍyÝì/CM÷&R +;h¬L˜§ûlÑÄ‘X ¶Ð\q|“rŒ:m¶£¨èƒxσsè8GQ]'G}|\r•?î^”¦ÎÎí¬u™Ý³ù‚¼/3î½/3(Ÿ±TJó¡\J9ªÆ¿­[4n–Éú ˜S›«¤ùª¤g/¾dÁ°Ùí»´¾t ÂÞ%¾>>͆ÕOd,çkmûf@Ÿ^Œ+Ü è½¥4v'WÊ>_pÓ}füjqZ?'¶ÙšÚ>•WÈ×ÑIó~µ¿º1Ó‡¹Í¼—žìÛŸ5–ß?!t=Ôïw]wug÷"Ù~@Š”¯ããhbœ ;ý/W^!탳»Z݃·õ–]‚~3ÚkÝÒ2âóE6ßÒ'ô¹-Ê—ʸҤe‰Vt‘ÒzÁq•¬qî»ê ªÞƒ&Kš–µ»‹~\v3‚Ÿ£Æk$CìÖ¿rPÕuw@‰‰x¿•Ÿ{ÑõËïçŸÁG:Álõjý+äPƒž…ï6߃/ì윪_ÏýÝ’b‡Á`çÃíeŽ$ì'áõ`}S¾Úø}OÞ¿ùýʧûgáçqnÃó7ã.2)èå”ó÷`âÇý‡—]„lÃ|F¸^çß½WÛ–Ð}cà÷êxu]àxšÄd‹düeÒ#"âëö;÷Àe˜~Ê¢ëÁüÇ©E¡fâ~*íK­È6a¬‰{nx@x—Å 64ùHÏŸp¼ÏçNì^ã2ɹ5ú>£ËEÒvxD|àúlhtÐõËœñ ÁLŸÊºÖü¼ðûˆç.Ú¸i2дP@å4Ç )ï¾Òy8Ž7¨[ßþšä¹[ew4>±êÑöx.ìx,°~H8¿x@±Ãüéƒç°s‰ÉìùùKJ÷¿¾IG¾ÝÛ[r‰ò±Ãqüãf­¶ô¼@¦,ø°ê[R6ÔŒ=äzîÙy˜§¹GwH \.XqØ…ÔPÕ¬ÿc€xžÊï×ê9‡ÝÕšªü^~ªóÇß$àI›_ 3•ÿ|Ȇ)‰/çÔŠ;‘Ÿ{µl”#õc´©‚LìÒþZÝmÅs8~¯‚öW ÚÇq¼îúhlÈ:O:„Z´™'¹=ô¾í+ÌÎvËy¢}fuß&Æ÷!¹eßeÓ¶ž‡5'¤ËS{oÚ‹ÃÁ Âú6áy͹ʜ¿IïTJéùÌp>o¢yŒqŠí…¿xž´[Ÿ×-Øã>lP6°9¶ú<ë›øyaû}„ò‡ûŠûKü¾¿ÿNï‡c÷|ÛÐ>ŽqvÝóêwþK${<°uöâû0=õo­ÅçAëâèͯŸvfy2‘íë&ôçï/Þ‹àûª~;Ïu=ùNúk¾0¡”²ï‡žÿ+0ŽpÛ¿é–HBïEß·³ù]jÌ<ÏöÑõÙû »_Lø}V~^ÏÏ]øç·:"î@iú˜ßÎ ƒ1N¨°½Ø8’”vNo¯à>º˜Ö±©ëy(˜±e]ΧáôÜá®ùWBï?÷#ôùi‹gÜ;ð×î-¦•¿—òxt¾9–q»iŸ Ç8{Kô3%5ο/fcæëæ@— ÃVÍ ÎC¯¼ƒ-Ús~(¡û³½ ½æ éÉëÆ*ϹC«ºv:–Ï?Jë ë0;£ËXÆ…g¼W¾Æºï_;œ%ƒ·»·0ÎIçÚ$š×< ü’ ÆÂ¯@¿‚õ÷ä„ÝO tž: >uj®âütÞŠ5¿·Ç¾ÉžKôœB—Ö ÆYÔyÂþ;gÈÚÔ·uŒÆåÀ©÷¦MŒëoÚŽ{\¬qb ½§Ò‘Ðs Cö<•²û•Rz¨RJÏMúð{´nâËdÎ>7J¼oŸ!÷«[Ö4ËúY££¢F‚úzµ©-;Çt"­Z4Xá©ÙI¼'Kï»ê‹qÔõã­ïµkÒ<Ç3diÆüÊþÖ9ÐnQÑ€ñ}"ÙóÇs+:Ÿ×Õ‹ÐyËhq~Ióµ?ðy»ºp¼ûz].žqš¬ zñýWlÞhª2á¬q0ØüК‹Í`çãØ¾„3l¢L›[a Mò>(ØúMšs#æîm1/Ùy­Œ£HÛqz­ÿ)²¼¸µ¡†x¾‘dN4<÷óš.žØØ’qg±ÏÁúœ(ð[6Ã7¬¹AÃ)àPûZºrzcà÷ا,ˆÍlÝBûw0Æñùæè°{ÛIv(x—P…€‡¾õ1Ýóã4’fUŒ‡IÐ9ã3cñmÄ{ßôy[“õ-6eû8§À±Ý‘ãI'ÈK÷£ËÖæ@/õE¯³0ðéÝéAúÐCCˆ<ƒìØò¤ó¶X?7ϳøþÛÏý#Í?Jése Lrsˆåz^¢Ä8…žÂÅÒòeé©K#s WÑóÂÓ…g!÷E·3µ/öÆ#&ôù®!®¯ù½ ¾E?·—RzËô÷÷ 0ްJh{ú8ñéÿhúÑÜÐv}ùÉÙë,Ô8[š7ÕEzÕÿ±ê‡á÷hh^™²}ê?ÎÓø¹‰pû´Y¢ Ü~êTµËìžtB™L'}Dô1Ù12oΗœÕsàÑéG¶®Æg¡Ã‰ùNƒ`áëêßÎ:ê÷Ÿ}íä=Î.¿f‹\\~ÞDÏ¡-~_ ãw; èŲéĵ ukæBèLÉ’ø:gašKÀæõe£án¯ÚrÛÔ9„Ÿ ®ûn³¾ÿϹÉüyBï5Yî«ëãô–ñMõu˜:¹Ð/häH×Óg a¼w½î-`ïÍ’¾“-æ~ó³FÑÎŒ¡F¿ŒË}ÃMÙsí½”÷Eþþ=Wfï`œW‹›©®¥&ûeyÝêç‚=ÉúRÑë ¼ïèw¢8Ðn†º>Ï…>/ãeýƒN]¯$Õ¬šrØœ©ižw5€õùÑl]9è½ø0`NP¬ý†Çì}“WR~Oºê¼E…qêÖ}]k„~8¹5å…J1#&«_L<iqÆ„Ï5‡ßmOÍ#ü>äœi»êÝ~?&÷ñŸwºåP¶¿_Æî±”IéþÈ0vnHן‰e²‡ç$Øt>@ú‡NÙ9cQ.x|þþðÌI¶^ ^Ckí»b!ù>}\hÞëvlŸÉø=~_‘ÿ3mRz’dî(~ïƒÖÆÙ#y.Õ8±´*Ìi¾g.ì0ݲ Ùì“Ðìs|É‹Á¬֯ƴ…„Î3»Â‘“µ½ÖR@¶»pƒÎC¼;j[öÙ³çꀉaâa»8Cq½­®Œ£Ói…ikÓ½dʾ·‹fžÈ…·SO´Èµ= ³¦¬ý¹ Ðûzó}¾J¡“pý…Lº_àÅúi 1¿é¹¤ËoónÆq¹¿eã,ý=äS…×!mŒswa@ç&'Ù9o=nÑÆÆÎ!_ë7©oSaÄö¥lDNù¢ýK†Ž©.îÃñûxwnÕÆ)$}/,ãÐùÄnò´¤Ï·Àã¹pñ›‘ÝÎÑ'Ùs¥HÊÎå ?G¦û8Æ0LRûۜƣÄy%Ÿ¿¯guÞGöÛûágüÐS.Ùï"&oì”#ïæ‚íæ:“êŽ:)Þ ¤ïÿÎ!ýÜBî·w×#šƒ.ûžé;º÷ƒ`šUtÛÀ»O¤üÝïÇÎu[ÑúÁ8qºÃK»„‘H­§ëõ^åÂa97ü$8üÐp9áÐ LÓ^¾×}2°õ$áïÐ}A@çÇ¥t_õ±”¾oЂÌ,•™5§õƒqÚÎLª™•°ƒLzí2ZZž 3Jœg*ôNBñWû.ƒã{½?»Ðó`sßy|R§(w>dÏé‡býÐúìÍÖA4ŽÆí2Yi­§¶·{l'{W7 ³z‘ ÿ{çíTyæá +Ž ñJ”„FñEÎQ$ 26T”à5^`EQˆJ1^´eÂT$ƒ·-‚nÄqb©atÜÁŠ”Ú´µt#:¤Œ—èà4VÚ‰×ÎóýîC<Míè,X뜵žåù÷>—÷÷fŸdç÷ÿãwÏù?Y£œ¿{ÚÔy#\|ÓoZÎý«}z2p×O¹N{nÈmgvε3o“Üû3Üpü‰ð¯C§þÐòô¸üWím«ÚÇïsÙckÔ„y¾±|}Te/Ù{ô^#ZÎãÿ`ëøÞ÷úNªS}âWtǶÓ:ï¾ßÅÝGyáøx"~ûëG.µô_¶ÕÇwÏþ'û„5ªýÖØoß?â,uÍÒq3^'{3Øyß‹ûwCG8ΆŸì;þÆ%Ö€Ákë[½pÂÖWó÷™êðotkò™våÜW{­åî'y?pçûHšïÉ诫#WwXnŸm«+§oY0¾þ°úãÓ³žº¬ýlåÎ×½6¿º1­íaå_½ìûÿðøt¹ mUfé;¶OWÛ¦ yÖò~G­øÙº­¿ÿE»ºzêš¡KÿíÝÎûsÝ÷õ5ß÷Sâ<;æsÐö¥YëÃ÷–üû‰¶Ú¶cCïõÿÜŸ7M ëµö7\gu<¼:IÕVŸÁy&+÷ýnîy:æ›ãí}å;îýùmÖ''ç¿vŽ­åçm øR??eyª6rйšt[8\§”)~˜æ>¹¸ôc6»ût×n\º üâF­ˆÃO;¨«]\ Ú?]ƒ˜ôfÖZ¸šýÓÑÇ?áˈWJ{ q÷ÅÅ+ÕПKw”¦v-ä% Iq-è Æ¤k7 ž…º¸ûJÒ%—kò+¥_Ww–Û]zä¢Ò=åziŒ»ÍŽ…ª8¥ÜŽÝ/Ó%Wý‚îr·—3#>)íCu;©ôÿùî]Ú½K={Æ.õÉ¿óöp¼5F—¾`íBÕÝåqõ¹nw¹G<ÓñÖ-ü ¶øÌ.åQñK»~¾Z ƒëÙ²Å/Ýܧ—gM€°d¡.]ÃYé-Š U(*ž>_“Ó!*Î?¡Êˆ³Æÿ^"Îì8úºvsúµ— *âçËŠ£+(Ýœº³<$އ2„h^!‚šÇ´îæ,B@|5ÚªÝ|¥&÷ƒöŸÆ¤§\{¥›;üÒÒUî·t¥E·±ë€(‰5!ýÆ_¦ÏÏláæs]5qAÄ›ºÊµÿ´Ü½K»w©gÏØ¥~ù>ª§g4Ý¥ïX{OÃM.œ8Cl‚—AN‰÷4Ò{W'{º…Âu€e 1†¾(¾Ólß]]í¥î×ýU/N”€À ã;5»¸ u×qB{ ! ÎS[†Ú-]óÖ¡½ÒuˆKwi½…¢Ù+ p¾¬ø¿´ÿÁ”Ž÷„ø¿t Pç¡v@â€H‰ÂKPãÒuÿCCü9Úu"¼ù&ïCIúsâÒ 輄Z{¿òM>³EÇq³û¡&î/·ã86DX&øXi¨B”ÅP?Ë!#>éÏë‹wûQ³âýÒžÓ ÔÚϩѽK»w©gÏØ¥ù¯¯‡ãÓ1»t6û¥;¾(NÅ,ØMÝñ^ñGÛâÓ1[8.´W,*.ÅæÎø˜x£]b½…ûÂõ‹Uņ*DÅ«]:AÂ’ƒ†t=ç¤7>ÖßqÁêÞø˜øývù0bâÒ ª¬¸t_àÆ¨B”À;.Å Ô ¦Ю1°Å£˜ïXˆ@æ¤3>,ÎŒ „ ¨!îŒ0AÍŠ;:N`KNãÇ¡X±Ë¯¡dqé‰×¾è”!$^²²8µ3ÚnÑ/í:7t¿´ÿXÇ»¡{¦Ã,¼,‚Øa!˜àc)¤¡ Q–C¡…CÑuèÁßæ¸iÝ®øŒrº§»wi÷.Íxvÿ]”¯³¦Ÿ=?c ­!~ÚˆëÒï3Äð1ÈiñÓF{K'>CiáàpÝdY¨Cœ¡/‰—6×wWW~¹…{Ãu’%1(B@¼´….ÎÆ¡IB ‚⦭6yëþÏ:8rЀÁ*ˆoû‹®s[»ãŽA—/™öo¤g?)^2/LBY<ŽÚÁaŠƒ#-AM@BâßСMŠ“6Lx&ï†öцrþ(Ç[&ÐxG9>2CºúcâyÔ!OB„Ý/OuqoÔÅIf‚%†*DYð³2Pƒ‹¡–CV\Ÿ×Û_†K#'>2í£Í‚n¿Ö>Zí#ÓYÓz—ºûÓݙɮü*÷¤»Ý}èîÀ/Ú{é®s÷œÞmî.ûs÷—»»ÜÕÊOæî§¯s/¹»H?Îô/˜]“ÜËqakbD;ÈÀ'þëªx~´öóüÚGïa€½’Õ^2ˆ‹óÚu6Zø8´§,à մ–AÊëç õk.ý—¡ö¼ºÞ¸øz‚?ß»QƒÃVì¸ ³P‡¸öoèר¾ª~ðpÇåaørâ£N0€e‰sÇ#BíuÕNŽÆ‘Ž:ÉPVF:¾Aí›ñƒÍ˃x*nrp¤È² rl‚§¡ Qº~†:5ˆ‘ãb ¿ ëÑ)’ß@÷uP÷ugϸ Ë×Q׿¯ŽцCkЧ5*. =ÀI(‚ŸAΈ§U»ª‹`¨³P‡8Ã]‚ žƒ$š\d9h@‚¡/‹Ÿ5/ß„ „ ^‚"Âlr•ćÅÏZìâU š”!$ŽÖš¸sЀa*Cˆ@å%TI(J¸bP„!ËBâ„­tØ.‡uŽ!——Ɔ8ÆÀPÇe­=d>™‚ŠxP¿ø¬+à'¨I¨@˜ÀAÇј7k„ðšà#Àiñ²† ²q”ã+Šh|£ÿ˜ qi£—§À†a7ÁGàÓ`C„à›âfÕ²øY¨AŒeP„ ! uˆ3T%Šóº –DB,м,‹$T ÌÒÈ‹L{Ys ßŤ½¬Ú?ÖáÊóìÚ¥{Âuwhóþìº7»î̈§õ¾ü*w¥Þ“ ÏŸîÆ¯r/îŽ;Qïî»Pÿì‹àgÈ àeÐRP»/ÕÓqUk'cªÕN4ð‹§Zûc d eêg8Klò¤ÅÅÅdXsÚ• ñS»F=¼I¨@˜!6ÀË §Ä›¦}²!: õ÷b˜Á6d¸óÐЯK0ä%ˆ‹w1Ȱç  †¾ !?A†?5½ïA! uˆˆÒ`Ç·˜ƒ$G‚$µ!ŽƒZ{fó‡;.µª¾/†À˜!4y N*&@xÅ©hCD¼kñL—ÁÏ.K aBVŸ¢öLW!Ì3ÀKðR`C„àÕ×~Ú¿öXªÚ­È+€Ÿ€f 1‚Z„aÍBâ„¶Ô§¨Ã\‚$.Aý•í¾ì¾ô컯Õõ`TÎÓП‹² Q†¶ ƒ¼ p J`³åøiã t ‚ u`¸ËbÀó2äI(CˆaÏËÀ'¡"^Z¼  lˆ|! UˆˆøEÊà# (ë¿aÅK[êâ“ š4T ,nÚºx%ó¤$T´s›@à%T)(‰;%²4 AØÊ‡írpëÐ%¡aÂg€—Ƈ8þÉàPÇÉ]?L뿃ÅA™„"†;~n56D¬t•iqÒF oü8#>ÚA6¡Q]?¡NBA‡¥§¡ QÂ^?Ï@¢¿ áA,,Ô!Î2(A…ƒ$X e‰Û[/ˆ$T Ì¢0À˲H –†!‹Cûhó²@´¶¨œ,éÿíy±Ýe_v}~¬yF<_ÿód Ï—®¬y§ý¹Ï—5ﮯ{g¹»Jÿ,Jú÷Ë>Jï帲Ãì Äˆ"Ä‘]‡8û§AöO`•!Äàä  ¨ !öO^|“âÆ3Tx¬ØaÀLð1di°!¢_ëdØú¾;†ÍÔ×c2pIH0te1xy¾$T ¬_Ãdëz·0ˆ%2Œ9h@‚¡,CˆÁÌËp&¡¢ïaHëú5K†ÔÐ÷Ïéû=ôk“â°N ÖC›†*D^¼â­N3ÄUý:¤xª#ì|ì‚4T!Ê€›àcÈÓaÔôN`!ÀÈBâ„ A‚ƒ$Øe±òrÁ„ „ Š^}e ; Ô}Õ}åÙ3®£âr=¨1ÈCMÎÐÁ«ïu| pÊdsЀ]†C—ÁNB ¸^†<3ìxøØaðMð1üi¨B”ÀO2Pƒ(‚_?ÿðŽ$T LH2P?aÉ@ „&6D ˆ"¼)¶öt(|úõ(‹³;e²¼- •Ãvy»½Ž6DŸ >˜€2„†:ï2dª$˜)(Ap¸ãô®B ¦¡ Q[?¡Í@ b„·œ…*D rAƒ¢~ÍP§ ^„2ø yj#ìEø,Ô Fð‹à%üq(A%ƒ$Xe±ò²’P°8Á½,ˆØaQ˜àcY¤¡ Q–† ^}¯àe¤ ¤œ\Ɉî1×Qÿ_×PÍ»îÿúšcƳû\;é溺gì  ØìŸ{§q† A! H°wÊbçäå5 3,y˜$T ÌÎ1ÀË¥À†ƒd‚aJC¢ Uü VªÕÏiÉ€E°‚~ï,–Ò×N Y š^†-6DôsW ]Cï¯ !†//˜„ „D¼ c l}ŸCÙÐÏY1˜QÓÔ÷xé{ôsU §áLC¢ iü jjc`}úšI¿ï•¡­é÷g‘ùøÉ|jc à××Jú}­ä½®³OÖK$ë9h@‚a/CˆÏËÅ@*&ëxÉz lˆ |# Y×MJ¶ròí‘ß½ûq’OÊoF–Ý»(Öÿˆ[¬¡÷í¸øtu™šmþ䯟\h‹¿w¶ú±wóMÁ5«;{ –=5bíiÇŽWw¿ïšo<µ£mUJp2–ÛàzÙ›{œÇñ d¬=ZXf«›]9ÿÂÕjèÀOg>8Iþ¿-ÇëT#ß}yæÜÐÕêÅÛŽ[6~ö%=®GÀù¯ÓKáø[·ŽÜØã©ë¬}Á}¶öëž÷Ü^­v$öÝ4ðÊÑêo§]}øò?f,§Od¸åô¢OP®·Û=¾ô†ˆÕékIpüc¾7º}ÃÕÖ]³z_pÚJ¾þ·{νéÀÕÊ8òœãÜŒrzZI¯èI–ÓÓsºxœÞé Çq¼‰)ë•5ë^M˜¶zóè×O|}•ª Z¾©Ïü6é½¹Ör|¬c-דUb-Êö=½Ók¢gp¼K·Z¸Ï%–ã˱Õͧd³|•zäæþö3['¨mg]0nÓ°k,ñH?Îy=L•'m^~N½mê‹ìØOû¨¯ÖŸzû®3;ûÁôyJœÇéÕý¶õ£‘=_šú#[ÝÐ! \¥fÖ&õz䆘0öÉÊ2ïÕ–Ó<Øê5hܲcSª_ñ±ü~ÿ>W¼«äûû Íùw³T~Õè ƒzÛqž*çqûèžžqóO_g«Í³ÊÈü݃êòôàëÞ›¦ÎðÑm“'/p{ÝLµóà~¿=±0Súm=2¯}ÕÀÍíßzn»ô£8¿ÏÆ7#1ÿ—».©†Ó¿xòf[=~M¯¯øÏ•ê¡ëÇ Ö>]-?ûÔí‡qƒåúAÞ´˜rú§tú¸]óˆ Kž`Ñ„Î>›Ž¹ç<Ž'b¾šôĵƒz¿o«íWÍžxÞ¸•Ò3×®N|ïWK¶ºÕñðØao=7E]¤k²iWö˺`û,åöI=ûÜuŸöÛ§Þ–|aŠ2ï8­ÓsÖ1ÿœgÔ¼9jøñ7¨&j±½­.Šíÿä?š¨sNùåþG>x–bØû÷yl±åúƽt»¹¸m–Z?õÜÒ‹}Îéœ+·g쎳´àu¼rûQ;rÀyfÎБ·¨÷dÍùÄVkOè·iþˆ”Û¯6ôèSún{övËõ6½¦µý¯Psž¿4;âãÊõi9ýb;Û¹ûëݽ§~¶Ÿžó8=8YéÙ¢fu迹ÉPÌPeýÛ§ªs/»eÑCç}Ïr<´#,§÷x¶W3Îï<[­þæüEw÷û íÍ^s®j¬šö™ïÃàøûøÝ±³žø¾ZyĘOŒQ[Ôwüö€e·â×£Žê÷‹i¯ûn´œ~ÓS¬?M¾¸®]zž§ªž¬½àŒñ´mX¶ïÑëý¤Íí±oöK”8OWÎ~â¥ÆõÞýsÒ˜-ê¢Í÷ütçLCÍ==4bÍ1ê¸Ê—^{lôyžd¹~]·×weÛÜ ¢z¨wfü³oñ[û©—.[¸cTJ9ýmŽ/±Êy=kÏoÌÈ)§·y‹º¤Ï¼›Ž?ÑTH½uÂäc£ëçYNïâ~–¹dú¤Ÿí<_¼¸çªg®>þš[U‡¼¿-xØÓû¨OvÞœúô&õüGW~ôƧ;¹yáÍHõ½¿û_KÕü//ºü›[ÔÌËï\±ý`CÅ~oܶ›ŽV¡ý_¹jÔ†k,çç1HúÏW®Gbã/Íù':@úz|jÌ´ÚÓϺRéöÓMïœéä†óœ¡µƒîTšäomQK8`Ûýêã¯(¿~íqêw‡¿½bã ×K?éA=y®¿Êí3tû‡¯òê…íÅ'trÃy—œvéÿ°÷PQnÙº6f̘1cÆŒSÍ`€"g(’""bBÌ…3fŒZæ2®BEI*H°ÀT&ÄŒó?¿Zk}¢w÷îÝ}ι·Ï?tŒwèé>ýÍ sÎë}ƯàÓ×›åCu ùû/Sÿ€ï 2{ùx{²ã)¹‚pÿwÊ´} ¸çmp¾Çtuƒqê}Ÿl1ïÉZ¸WèÔ]Ï)Ì­ü.»¥Æœ_bÎ|é¢õ¯k œ3•¼¼ÛäÎ-@Œ£«|ÞèY÷×AîÁ!×ûäÃÛ`»0…d \[P}üø!0Z•2ÐûVaæ{Àxî?qVãðyÂèæôi=”7ˆqùÐ~BlΤá›a_BRÏjÍíaäÓj…„ûÄ-š»Œ`TªÚnԷرÐïý™„¿nÁ}µY õuTcœmëÛ]¯›² 6ÿ!MóáÅÌðõ]šo‚³[œâN¥:A€ªŸÞ½ý3 åxÊ´‡‰>ën¹©t{!ÑÙÆÖ•ˆ~«ºzÀ8UŸu[q-t <, ôùp¤ ú3³3@û9bLZ¸+”oá®.6ƒùÜb>¤CE.=£Ëûtÿ˶ ¶²MÀu>¬2xßçЀõ°+ײcâ$7Hë%€£fÊ•ì ‹:Æþöà{äRÕÇŌdžÏ9·°èzžÝPytÀ³¹ëò᪪Þȃ'ÖÁØüéÇs6¹2f¡þÀœ£#r²uy‹Ïù:òòrr$è|*r­ú§”­…„ ‹Ïhå 6snœ{3›ù>›gºq™^Øè¿øönÅ´0Ηb\=êS)Ççjlf¸ìÞ Âl)ff>¤mØ6ƲÁÈ:±"XuܶÞÚhû¤¹„ù¨çäÒù‰ óÅ´`|ú\>×ÿý¡çGzmcœô|p>l8?¿ÞjHüzÎîa@yƒs å^ô…}‰ß;[ÖO™Ÿ*>ÇK¯Ñޡ߷Y+ê›w>´xœÝá“ù*81³ê—×u¬òdçΫç>dÔ˜ú¿ªñ9oúÝ‹©¿%l¿‚ù~Ž •2GÕ` —|7@Uø=è¸<®s²z9Îu:ÖÞGœ7=:¸|Ã2Œ+‚ÏhÌ«ßm‡)‡66üœ§=^d”?¿ž Іçv ¢VkŽž™A¦v“RvÀZeï‹¶‘ùP|./ôa›åà7Oo^óA.àtÇÆ F Â} ¹/âìþ²]Ny4ܯðP@+ƒO­~öGÅ8êŠÑGî‹[•ŒŽb}íˆi”šSg ,ŽM9•Ÿë“߯©ô¸ýlB_§áùAy–Ày«”kð^BýzAä¾ëòãÄÚ  à¯8çCø™~æQÑ Ú:!¥Á7oðÏÏh±{Þl2{™ËÑøíHímÓU­ìô{vîÏybÖ²/†®›äÙ¼²ÇæØÓ|Ç8£žÞöó.ØuÙ´ºý¼|¸_¼êH§ÝsA_â÷@]ÑŠ ¥(B9JÝçØ†Vjõ¾r\0ó‡Ö‡ÕÅíwTKz/ 1”´ÜiáÀü@Ýi=`÷]i–X$åÈb¾9h÷'\SÀ—Í&§Ÿ{CM…j¯ëŠ(Âýìùz‡çm½Ê^ÕáÅ+6¿ÔÍ´¡ÝOóK5ÆÙPqð½ú»¡wJëV#ýp¾ä5Ú4b*¼mò,!¹¿'Ü’FM~ý8Šù½v…ÓúŽ«‡Þ·óŽûñòyó§—I£×õþÙ·O‹q:©š«õöÀuÙØîáùðØ©h|ߺ¡|è¾QsT®ÐÿŽÝÒua³ å¶õ„M£c·¾²AÁ¤±ƒýgãèêä2ÖI5d³î'Ÿ3è;#Îä/®>»ÇX¿tnhÍnNÐgg»N¯³EßÝ©}ê×­ä!rýø:—òF†ÑºÀçF®[\b›±J?WÝÅñe±ë‘Õ ´pßߥï7g{Hví¿hBNÿ­mÝ–Z)P>בŸÇÇsî·Ì×yWî:^H1Nwß /Ìo‡}3én…Z»G¹BoÇ‹õ-퀮G„rY­ÏØ׿†Nž¢ß2ÿ›ûúS.'å¾Ê1Î=—AÕ&>ß M=ñ#·÷Ž{k!…Ÿ1ÓÆÅÚ‚ýÖ“'JnÌ$Ü/˜ó¶éムïÊ|µ/s.Í>Ø^Q)óYIH kia.c«„ûdbqz߯ü…éç‡Ï±é¼z˜íó}PóõT“ö’|ˆþüàú"7ò¼MwÃ7¨wçÃXŸé$¹YÁ¥*3‡‚õâóû ÎÛ]Ë­ž"ræhÓ÷©Æçv<¯jl1:êz 3›|¸ñüÂûxï¤ÊØä#müœ™?ãtB?¿¡0,A[<;z"Üvœ-ÃÏov¿žµ­úS çKQ ígZŒ³d[%IÅŽû!Lö9q³q>ÔÛ¶tLÀ×prµÖÇ.;º¸Ãûé—ÇçM'tiœÃÂ9œljÜå²~Z4™« ÔŒô‡Ö÷öm9‹NºÐ¢±µ+ì/îÒàÕæH¸/#U=Oý¹y_ø43äôÄU?ó•'¢ÇöæÕw)A·j‘æC¿~ŽKË9-%©ùóLRvùAÞa›)®Ï(§L&r‚9G‰òŽ^I8Ç™ò> ã »ßÀÇæµú]Õ¬ Zš\ˆ! 7ÏíœuÄNo‰+èY$k€bð̯2àù=°æ‹+c¬Ä8º|Æç˜¿ôÐÌ!ù‘­ò{¿|¬<µ(oãJò1«^­Žž`+ØÎï™E¸ídënÒm•€û‡óù†.oñyMû^kh8å t´'øúÂ<îD¸vZMè8ëtù¨çç«ßWôúÛŽÙóÂO|žbÁ×µ=“ÐýÆ£¼Šù1b^Ë[Wª +Çè›e¢&<-˜³†œñjÞ-·ã Î"Ïn5éÜÑz@Já°ýò¸°:/‚ ZîÓäõ%|ܧï«Íc|þ É%«Å=A݆C>„L«pØñÌZR’ØÍ\bît ‰™„ÏË©Ÿ®ëËÃDþ-ÿ›ûÃêð.w£q¤W9Ÿñdá·[¿n>tªõDóaq¾ãô}ÚP'È[v¯É¤‰ÓÉQýµ­$â<¥ô:EŽÏyÞ¾o»÷™‡`òæêGVÌÉÌ´%µíÖ“bÉŒ“á8H¬¯ h¹c áëí¦Z;Î$YAQý“µ£ž0ŽñUÞÁâ°3;+UȇÃ÷´¨Ÿ·Ônxpω>ð¾íü÷"ç*6V•›”¶}4ô õ\o úŠëòŸgñéùSußþM¹x[¥|È÷(?zÐêMäqàîw3·{3?ãI„ÏÏu˜M\Ý~ïû²„pæë^(1}õ`³¹ óc§û;j|~Y0ð0¸>½ð0_oÄžéW­çn!™K–Ëß¶•3Þ‰"—s ½w­X›vc[WRž¸Ÿ×@ø8švÛ|=᫼_­¶`Hq[§ùŽ9Ô;ê³kÑ Î*×|޳ÈY ë„b^EÌ©þö]‹úþ÷‡Öåof¡tŠ€‹T«åwn¼ÕÀªà:ãæºl%7*ô½½X* âH’jjyÈ*Á¸?;Ý÷ì •²÷:¾ù®„û]SßmSà]cóI^x>¸}ʬðNí»ÏMlå¶|Pµ1îùÝ0i$.ÅÓHȧgЧ‹ÜEÞÛf}ôŽ˜Ç|¿‰q´hcœv‘ï(ø`v7*ÑÀÒ¹|/g¿É:×fè虤ÇÇg;£vwè):¯ÁþçméÒJþXÜåóîÒ>ärŒsOÀV4:² ¯[Ö)ÖÀÂr¿nÈ Ú– É÷˜EìGÌÖ}¼%Ä_z9æÓa_H~ðfÑ.ÑW~~¯$|û¹2|ã8êW­À8õ.H¯ŸUýfwn÷*ÒÀD¯¥Ï†¤ÇwãûíßE8Ñæí[žï0“ؽk÷êDæ‡ï”Û-c<…÷Τ\Æm§ã\Æ©ÿ9ýEêùc0fìñÖ•žiàñºÎß<Ãv“Î'™mq„â³tø>•Ðý¯îð~ÐÚ uf9ÁÖ÷žýík;çñ~ÎçCviñó{ëÑs5ÆÉ®Új˳c`¾ÒýÌùç¸óÒO*ÕßIµmnõ¯é§¤ÍK#äl|7+çë;Ê¡ȸ0E¾?ÃûHFNëHóF”‹§Å8U®oXå8¸ dX T˜ÑcÚ“m;I覽 ÑŽÖ`ÃÓž×ÆÊG´ÇOžW¾ ƒ¬ÞNĸMEÎ]e¼2Z?Y…RÊG9Ö;Íw=º¥:N[Yì"׆ΚáQŠòFO 'íaî,{‘ Â9‹<߸/·[“+_BjHE³®~0ÎÎ×s³g‡ìàñÝ*æiàþÑ2ËÞ<ßE×qÿ-Î`º`ôÑg _ïðñµ_Y•ûi7k‘Åùr”S%ûi_ŠqÎ,ßÞ÷ÐëãóQ!yM“VeÄ%¸M‡ô7sUúÌ[¯L ôõ[Ý?wÊrf|íÏëK&³[T¬†ÂN°'ã‚R†ã¬~·qác3¨M{÷*¿™@øÙMö’±K¾v‡{e3½?M&œøäP¬fº×hh6ÒwJ8”KÎ?»b‘ÜlÐ1ùñ¬ºðõfæêÍ3ÃAX¥×ªO÷wç¸OöœÙ*|Úð\·t |ž%÷rËëöNØæ ‹w5î9pžåŽçQ.ò”'lþÚ‚Íç«Ázc³'ïççÕéêãà$-ö¿ tÓ-¢²µî¥å÷ßKNú)»•‹|Š¥>Ë&œ³Ól€Œøã’ñÕÓ^Ъêë:} ¼²ºPöø‘v,,iý`œœS‘FT°¼ñÌ>·i`SÄ#ðpØG”õ„òƒjûOlÿþp*9†]=¡¾=Û?2×#”UÂöIÊ@õ½‘­Þ¤ø2žÝç×bœæî&¯¬NÀ‹L¥á¤D Û·K$mŸY¡‰÷ƒ–Ž’òÎÛ'ÎC§<*sqL9 ï$™iþ•ú}ÿ,¡ûö?­+ô®J[WU'ž:5t(| Tì'owëqÐ}ùê,p‰þºÕÌd‘{DÏdpB~ÒÃÔ㻄ŽÏ¡õ´¹Z8;ÁÜK°óÓ¾c„qÎ*H°­t:\ ÷}…b­¶ï|€t0ì×x]}tõ°|2p¡œuxwãzÔË@XòÇÅ3«0s-è:QqÊj…´:=P¿ÌG‘'¨«ŒsòlÍJ•íO•†ö§ãçöªËø=JR}úçöN°ny|ü‚­“Ý¿w¹£”‡àÀæÃe ÖË—-Ë^(<_8@W?ÇVåZ˜xò$(f L¦æ§œŽ¹+‰Q£vW+î‘1žÖDB¹0îÀ¹Ô¼óùQ•œJ}În|#áœ:žÒùãôµjÚÔµÕ)¨õmpf¬Z¾©Øv½’¬×.š:¹‘¼6ë7`î« äú‡»F-Ò\_‰Sº~È’ÐóË[~nÅ9-ºúÁ8Þ§·ë8ÿ$í“Åî«//'ßžž¯$ûW„­·Y½uìÜê]Û'qbùÚŽñw[³ýú+ìÜ¢@Â÷a(Ÿ›ò¸Ô§Ç×Ú¯ò>ž‚]OºÜ_F\bt êU·s×[²=¡Ç9¡üàüææ¦xœ©8ß¡Ð{’¸†*E|éÃö‰[ÓúÁ8óô›{¶{XÔ]ÕGeŒný:ö ¶’ûßì Í’û>)Ryvkÿê®ÀûqħAÞñ2K‘ AÏ©I8¯¨CoaeØ‘ÖOv¡ôåCIß*g`smƒUá-4`ßùR¦KÚAâ4òûð¾án`äg¨|ÐaÉÌKJº}AÎ8(ÿ纜ó«„Ó’‰R3öý°õ4Æ9Ñ0LýhÁèY°§õ¡šxµ¥\z­î‡H·Îƒ‡Ûö„c Ì6ŽTopb×Þþ#€s°GPŒ³2rJ?×Õfâ½ ]Ýàó7¨jþàíèTcäÌ^•4p¢ «Å›‡HÝâ‹·¦ÕñUçÛm¶6™DêøÏî4ŠÕkPί7ð×O¹‹lœÁçÎN¬< ñNçêLýz¼B¿]at˜”+ÚdÑþ¢ì]¾àì©IÄÿÔ4Å‹ôQ°«ì‹)ߎŒƒŒi›F{ÿƒÏA¹£fâ|TWøüsïWu“dœ…ô?ÆV]ûä:Ôéé¶“ ף߽–{ƒíÑú#¶nž@è8 ›7ö¾ßÓâ÷†û<畾‡q[wùÞâëYXRÇ÷¹ñ•ëÓuëìóU÷ÖWîxÒè¾F8iöÙ½Óà×Aâyçðò8º:Àç…êà³åÛÊÆ`ÿuXú>»`ùØ#d^Ö¾Ðæ¯]Ø|k y2Ùoô±×þp}þ¢qßÚZütB‹ÏÉ•¾é;ùÔY ¼ÇëÐÿóØ´¨óGÈ•èk·µUq>·bʃšÈñ†JÊ”¾°3àJ÷m¯yÇ’1ѯ΂ºõšö…FøþN˜ç,ëtŒ¸÷ܼ¹Ú2¶ßDÊ_ت¦… œ*žófw@<Úû `r”¿¸¯C÷ÓŠ%ü<ûÒIÕÒŠÅtÿDqÚ Ÿ³mŠý–‹{¾ªqŠL|}3Ž‘1G&o­«ï-e_çXÏ÷"&[™/ï ÓÞ{íl›ä U×wœ2ÇQ\?ñs œ…»É¬!æ¦pQƒÍg0Žï±ÍÇ,Ë(‡«ÒZ%yà•w8¯â­cÄ'}RàûóîðàCï¯ùË]y_rΣË×ÜBéÒ©Ñ–“ ŒÌ®¥¾|/,¿Bl›¡Ç‰Ù¾ív²®Þ°_8ÎiæBÆ Ûß>PyÉ»êÜ`Ô¾¯›Ÿg ¥ù‰Ï±z§¤ mVî;Ÿ•W¬¾5N>N:¶¶³n1Ëè=_²%˥͊rhXÞ¥FçCcÀªlõÞ“š‰}U——ø<›³µ­zãóbO—q6¦.<8ŒX¨H¥÷¶§ló}ÁvÐÌÞãoþ=U¶1jý'z8ò‰|,¾/Ì9¿¥ûžã¬úí{p>SG-ÒÛ•YžN?R«ÈÝå ûEÈ> &­_6I“±y&»&îKñ} Ê ·a.Ö_1NGÙâ$l'0ݲeÏÙKó`áóeNv=A š<ê}×Ç×¶X6iÜh2½ÓŒ¢ÙŸFˆûQDzó¢åÄ8º¼Æç=¬–ÜtìkºãÙ‰yÐ|@ë‹Ó ãWUÒ|mèsz]ÙÙJ1š­«‚ß~âªñ9cÒêÝ~ØR «‡Ÿ8wÊ+v¨ìzØd cbúˆ³ƒo¯|Žv¿;’”Ý8¡xX°?ç3‹ëeþ9ëòŸW1g£ýg5?d1rýà<¸ŸqºêIâùFòøþÃ@>¡Æ–ÊÃý ]ÿÈ!$~®'@Šçþ™¡+ÇŠûS|^Çï×%'Î6,* u —W(Ý·¬ÉãjKÔ´÷á•a&y`æ¾yPÑà“$rcÛ*olùy0éZ¯ñ›~÷äð¤Uñ‚?¾ÛÂP¸û,¯» pÎz™¸®þVD:§Ð8FǰBÒý§ÔÐymËCŒò EóªdÑI²®r£efÏqÞ|ÀkìÆÝrBï…ùçÑý©Œg[$Y|Ú>seèÉì}»\|½ÿ܇1Î!á‚…t×Úêå‹ÛÉŒO·OÍ÷Úq;]àðÁÚoöÅ‘àC³¶T¬æÏ>+8Õ6¾WÅÍRà‹Lü¼:yÆ\«T&ë¾Ë:Ö»^(M<7BQõP´Ö]4É…«k5PXž!· w],žáÂöÕû{á¸fÍH°Õ4€É&þè-ò9¡ôÜ_øüêjWaBAÏÛ0%AÒ’ön÷Csaö³½~“6œ!µ5›æŒ4sƳ%܃vù6‚­GýÖ±¨÷.ªWp»LÛ<åv÷ae¡×‹ËccƒåüGëãèp‰yIp>pÜÉ!†Žm=i—ö ¡ëj;`÷-ˆL/9eeßÀ¹¾”Ó6€í?–̘UR¥É'Ɉ3†i #3ž/]GÊ1ÎØ?œG yšGwÔx=Õ$¦?—„.3i9ïPñ»$Ø]_˜å‚IzÑ ™ÅYBï}š³}õ`²ÆBر Ê?–2.:íçqø“ÍycdŸ“ ¶n##Zøn>û1ø,1vôüAOS(UO½yÜ£Ý9¨ÙýøZ”04A[|–V¾ØÄ7ÏŽäp•eº¤Œ`ó%sØ=\¸¡Dç·FøœE³NÔC/¥óìä86ÙuÁsSB–žoØÇ[ºîiJèú:8žŸïñýQ]¾âó–÷j5çæàs°ú¦_õ'rààŠYI©+¸äû«ºÆnŒkÝú7ZðÉ[ÂöMlÄûvô^D‘¸¯{üEáî'~¾G)Ç85Û q ¾ëÎÝŸn38þ‘ºŸàÉÖ&pÛýÔs³ cؾÉ@ ÷Àøøxvå†Nñ‡Iø}Žšyßûf¬£ç Œ#P3ï/<t¾‘ÕŠW¹Oè­&YAFó¶^ðæûdek}WéÖ0 |Èâ|ŠÇÑå1>Ïçm½ø†‹ÏA3ÝÍ8ñÉ%¦ê5éwñ¤wÎ*~ŸT<ù¾ÂÛsc€î§X°s¶ÏˆÏÙÚMØÙ;®™G.¤ç€ç²­‰«¨ÉìG·Ï·¹ä ÚÚ¹¿îHVÿÐWâ #Û^é•¡áöù’j³ÅüÕå«ðº6•„X;±7åÉrùú…EjÒÈað«£=aRÓ=íSûy‘cq.è…Âiï®ÃkiWÃÔoGì7×X-®_éý¿§zoÞ û®L«;Þ·ÑË/”¦ºùæ;é¬jT *Í9¯šZ:4I"±}UWç=s…©Î#ª›¶bã¯/$ÜõtësÛ[äÈòó~^J×t>`„qÄÖºÝlî9ð}êÐêm˜\ß°úDë$RTõbçÅ]œ€Þ³ &žÓ«(^U ~ŽÄ÷Ùx]^ãóè9þ9ØZÒ­p›Ø’hþ|LÙ1JÖ³¦Üè¹Ö(2hö½K±§F°ó0~«Ë[|΋3ÚìMç ª~â‚®urØx˜DS\lX.6O&3µ쌞Š÷‹uùˆÿ{¯qãœÂ¶ƒZØmêåÀõλvW9”D†/®Õ­?_%ºr¯3ø½º>"ÞS‰\kÔ*îý¼âð¹tü?w³.Öý*Ōݱ4?‰¼ÞcX²qƒ3Øf¶Å·èGôÜ„û`x²rÜ’ÀåáàÚB˜ÁŒç¼_ýÌÛ¥ç>jŒsÿó=‡5ÇÎÁ1ë‚¥’Âl8}ëæî4ýs䯙¨°œ¸Î›gøñgrÂï¥ÑßõÈ€Þó²çÑ<?7¥óOv¾$|ÿS„D:½K’>ædƒ<˜|ƒs¤ÏÖ£Vu¿{Á™%þ'väÊIÊ”ïÍÛ †÷ïŒô2•ŠyÀãèò¶ PZþÀÚþ=’ÎÁžê¶äX6,÷óý2sÚ9R»R£õ×ø€0«j¸Ä4Žë™cÜz”ø;~?U——øœÊckšIÏžƒ©Ø6b¶TZ£:GžŽ.{°VgvO1€ŒiþzíÞæ£Ù}%/ }Ðø~£./ñyZ,Û}ꌨ›Þ9q~6䙜lðæi?Ñ¢a/ÆA:,›;xp(Ì3~)Âöw‚€÷?zÏÄâ§}R9>æ÷Âä…ÎAHIý¼¥“³‹¥Ñ§vçÉæÑo<׺=÷I. ésl4;W¶‡oe7æÛËþ®·±U™mò‹ôóPàóçŽ4[uaå9XÒ¹|_c¿lØ}LÚ®½óyR¿EÇýÚ1Nìõ²ý•fŸÍk˜±ù;Åç<µ€´}š¬Jþj• '%gÎM9O¦úµh¯é%ƒç/C—ß÷%Ǭƪ–ß aŸ‹%Û‡§¯GϹ«ïÐ>¢ç9˜ðY/¾w6˜mœüÁsíyÒÇÄ'ö•§ »§è@pQT«ÞžÑ0¼ËEu¿e¾ì>‡ð{(º<Äç¿Ú¡Çœÿ¤¦opÓv̆9ÍŒlë'ž'M Íý]¼·Jj×É)î¼o´xï¤qÐ×þ'åÀ‡Ã÷èyª-›ßÑù†ÞBéþwƒÍ<¯$®í6Ȇ«·JZ''ýóÇgWýqm»#îÆÐX7qõœüòÖìûÄÎMŠ%|ŸÉ®ÿ—^îÛmÿîI—¿gø©Ø'¯'±8=éÛók0 ÖùžÎ=O2ƒ’Ó»LÌúžéqÞÒ¬áxý®–ì÷0fâ>V•øæ8Û})¡ýwpþ¶.¯1άàÂÔ^ÎÁÇÎýÜCÎ\—s©Ïî=;Oª/{nàb m›L]¼àƒé²¢êíË‘ê²Î±¬¿™Xo|<âãD“Ög×7ûiž&Ç8­…åC3¬GWþÜévwÀ JHW‹Øg{Ë'×doñ'¹]PžÇaÎGæß7¿·ùó~£À8ôÞÎ9(ùœšºçÌXðìò‰6È—!Kª :,ƒïWÞUÿàG¿±·ôÒ a÷½Øþ–§øû ž|\(ý~â0Î ½žCÖ}K‚ëc„‹€×à‚BaÐzÈr#"fé{÷VTë{lK /¶evVk’yÍ™ýþp¨øû/þùé6ÒdPíQ£ÁØ}ŒCï_%Aó³Ã궘t Úë/ Õ­¶FTvzŸÊ‘®úæS¥f(¢Œ¤ó— ÷©ù½»#G.Øÿ鵄Þ_±a÷"Øý\Œ³R9¶]­»I +#ì0^ƒGY’ƒ»/Ó›{¸µkêÉα¬ˆ«riä¡£€ß[äûôžÀ ×}ÐçKÙùIZ?7 ¥Â¯•S’À¯ÑüåÑí¯Áñ°ÕSî^ ÅóÂ6Úºz±{™z>7øï-9¿žßwæ¿7äûútCσŒ0N˯ï'i7%î:lík0eiÙÇ•%åîØ—óÂ<½ÐõI(›ÿØÁè=êT{ÄÇïOºÿR"á¿')}/MŠq†f^é2= –«›Ïü–í»5ÔL´M&—bHÒe7‘s߯[XŒ~ŸÞýFÝå‹Cìß+¤¿wz'á¼ïÒçrŒ³ Ñ¼Ö‡§&Áƒîa!q³`wÃÄoÓ“‰°*!­Ý$lßNœÓýK±Ü6i¼¿ù¾bI-'áDu;ß`眧ҡ-3&%ÁÁ[†Hô|k~p[2W°ù{°=ÐûÝ^$ì¶iûŒi¡0P¸ÎhÐ_Ûš}††ø£$™´/z¿òbðp©›X¯C„€QpsöŽ“Šu6ìÞä`¶/TÌîY½ÇÃÎûoá<ðAnߊI0ØàÑÝò;³@w»áE2S·‘éÀ¾?컦sˆoÆqÞD·ÒG¼Éïùû¶i~ªêj6ÿÁçÃ8½¯gg%Aš0ýÙ’ÃæZ3Àü"É;ºï`?cW~oƒ\¿S[1kHðó[ž”sÿFì|¿ºôï¤gUÓÆQ‘ÞI üˆêI ~>nÖU".’;Öƒõ{ÎðÝöðYGB÷C‚ØïûᨉGí0•/·W8ÿVüÝhÝEcv¾Ï×]«©›G–íi+Ä­©¼H4ldW~œ;ï²'cöox=}$ûÁå•eá ÀqwŸõÚ[·Þ²ûo%ô\¾ßO¿»Q`·ï¬çÜPƒ™î‚nÔ^r»|¿Iƒzoú'øÂŒN_þ£ÏžyøäRÛöâ}yÏc, oÄ÷‘•xým\œ“XljzôÏo£ßGÿÉG‚×Ô^Ô>=úGø\‘?cP ^âòR>º±¨”;e‚ÉË<˜ùè–æ¦ ^âáŒy`Àü#•Ì»íï°´¿xŠËÿ 4CUð—¡â˜¯x8ó7`¾â™ÌcWQŠûù‹¯¸P\ÿ̯$yý;Üó>âœ*ã_<ŶŠUÒš²¨¹§¸À¢V²æžâF̃÷Ÿ1T9k!¥ó¯˜yðF—bþÅ0^î"4ë_¸*Ö¤Œý§ÇxU*Ö$dÌWà2¸1.õ¿ë#ͼJ{J Þ¼ÜOîw¯ýÝkzÿûz­>{ß™zÎUÕc,@îÇ’:•‰2ÅäŽCéc‚‡3ÌÒ,@Á§7’ù–29+€¿ã]^ô‹_¯ó//Í<{ÝP ̳7’yö2Ï^ (e¼jβRüâÙ+pÃQ”‹,e€…‰Ò¢¬ÇÊðßô55«šsjL~ñëø *V°²R~½_UÅøªÜ¯×˜ùpþ3. ÷;Ï,ű*AI±àcJq¬bQE(6ó<—ý²R3ßskƳüæÂ™ßœÀYuc~‚÷¹œ±Vÿ]ªÆ®.í;.ùáEõ»×þîµ ½ÿ}½Ö€ýï„Äý3îªÉ/|œL” &u,Kì`T&Ê<Žñq¤˜è (ƒÊÔ‹8±­1ñãÏËäoò ñ$ŽfÌ@c,”Ʋ|‰£Q” 'y[3–5gå¨X1ÉPÊR¾Å™ÌýŸyzjÿ ¼ÀbÆ ü•c]̼ùt¾Æ(“6”¿*z£Š•û»1VN±ðo,h5Ê‹:U‚’ÿ ŠœsÄŒ™§§ºË¦Ø¯žž¦Ì۳žD1ãF2ÆçY3k4cæX3¯?ÍÁ']àZbc‰Di?0®”Oúï^û»×*ôþ÷õZCö¾4zÎeÕg,î[š€ÒǤGiPRLî”&x$ó.-ÍL|“Ìû]à&F2&™~Õ¿çÿ^ü‹²œyÀ—f( Êr”’y(+˜‡²óPÖ2Ó˜Rl²è_<” ðƒˆDiQÖXdJ”!šU„’1.™Àºþw<ã^Q)^é/þɇBÒoCù¬Ü?Y೪Ÿ•û'›0Ô¿b5Æ•ò—ה⒠ÅnŠ-Å%‹C£Ü°¨™ï¼Û/l²æ=/c|2ÆóIaœV9óU|胫UhÁ¨L”)68”>6p”%ÅF’€2Àf‰Ò¢¬×G`_˰¹(QØ`"Q”pÚÍ÷¾~÷Úß½öïôÚÿ„>kÄþs}LÚ`Æ<ãZyfú ãGƒ2Å„ŽCécR‡£4()&wcüXc’+Q†Ì#Zɸ2LúÆ=3ý›ìG£_¼¢cÿÑ‹$–ŠàƒÒ ÿÆ¢Q²Â‘16çý¨ia‹RÕÿá)­a>õ (,®H”eE¦Db¡)PEÿ}ö£ÀÀØ¿2°KP2î72mC9i¢÷4ª˜±Ò¸µœñ~J„c1§ L° cYQÿÂÔ/ÅJ3ÁBD¥”â¥e¢Œ°ð£QÅŒÿ]Š™&ð=JºS¿jM)v cçÆ0æÀ€ŒeüÜׯ^àaaCQ ´Œ™Pʯþ÷|öwUèýï›Ï³×­Õûs¾®&r$J‹²Æ„V¢ 0©#QZ”5&·eˆ ®@eþ–”bÂG3?~/©`L5ƒ¿éÉ/ðÀeX*”H0óåçœIid„ŒR¡ŒoRÃX»Ñ¨"¡ç2V8g«Å ´(k,,%Ê‹K*BɰÈT(#,´hT1ÊqÕþä¿ãåŸÉø“" 4•‰2f<”AÊÙMA™0În ãì*PEŒ³«ø'\Ê„R¾ÿÚR\5}aoWŠ«–€*þ …qä¿°Õ2ÀñÕ {R¦R&ãí£R' œ1wõ±i„£4()6”6H”eD‰2Äf¢@¡dŒ­$ðÊÝ^9ÊŒ¥xk@ëVøSš ògýõõVÞSÿYÏä½’÷GÞK÷B¡ÿýßê}¼çñžÆûؿڻx¿â}Š÷&Þ‹xÿùwzÎÎÐg„±E©÷ƒ[«AIá IñËL@à‰Ò¢¬ñ‹•á«Òÿç F~ÑE‹¿è8T‘pV.°·o[àÉñ‹7ÀzDiQÖXïJ”!Ö»U„’a½«PFï UÜøßc) E©ƒ U‚²ÆÚU¶£‰ŒÊD™b‚Å¡ô±^Ãá!t¦¬3!+°ÎŒñCŒ1ùÁ7Ó2æu,cò˜bbÆ¡ô19ÃQ”“4e€u‰Ò¢¬±Î”(C¬1cL࢔c¨D`2GþžÏüžÏèýïœÏ˜²×U¤÷ç|XCLdªHàÄbB«P†˜Ô TJ†É­Ba‚G£4¿p­1ácPÆfŒfl3C,€p”%ÅBH@`1D¢´(k, %+ 7”eŒŽR•b4 œ!c,˜p”e\‡òµŒƒ*F¹1Ž6gœ ,í"á\ K…2ÂâŠF£Ü°ÈÔ(c,´T JÎøf&Xt±¬ð‚Q™(S,À8”>a8Jƒ’á{B`AF26‘IË|"§ƒÒ L°P£Q™(Ã6”›‰2eœØLƉF3Nlô?á?*Q†ÃUÔéßÌ@Ø“C%”â›)Yñ£2QFØ‚aœiPFØäŒs&ð¶摆ñbÃQ™(l‘Œk€M#¥EYc¢)Q†Ø@¨"” ‰ e„Í$UŒrcì#¡±È{ÛL4ªHàžÍWáÐgÿ»{ì¿ÓKK÷ÐÔ;…¾)¼­ÿÛ}’÷È_{á¯}ðßíBÿz߯}ïמ÷k¯úœÐãþ«½íõ5á36À>¦*óƒ+«EYcS¢ 1q(-ÊH‰2Ä$R ŠP2L&%K(7”e„‰%ýLFmmŒÉ*®I¹ÖRLºa¯ ¯X¸'ƒÉ§BécÊQF˜„˜„†˜„ TJ†½H…2Â^*F¹a/R£ŒFª¤ñ¿Ç[ŒcÉ-0«0Á¥˜Ü±,Á娔 ]…2dM7”5&CÊ"¥EYcb(Q†ÂÞÊ$U"œ)b’(…»qÂ] á7ØCŒ1a‚1aÔ(#ìѨb”ö 5Ê{F ª%ǤJA™`bŲÁ:•‰2Åž‡ÒÇ„ GiPRL¼”&_$J‹²nA9Öœa-$¢›pÏ “Q“Ñ {Z8ûÄŒDiQÖ˜˜J”!Ö¿U„’aý«PFXÿÑŒA«Áä•bòšb½Ç ç{ áŽZÊ›ŽÃZÖǶÆÚU¢ 1¹¨"” “\…2ÂÚF£Ü°vÕ(c¬],€T Ê A2ÖY¿çI¿çIzÿ;çInì¹%¿1yãPÅ(7Lb5Ê9U‚’cB§ Œ1©cP%(9&w Ê<U„2ÅDCéc²»¡âPEšq³µ(c,ª%ÃBP¡Œ°¢QÅ(7, 5cg£2…ýw,*e„…*F™bÁ(P™(S,œ8T1JŠÇŠ(˜qµKPÖXP ¨” +e‚ÅË ,•‰2ÅB‹Céc±…£4()]Ê /¥EYc*Q†X„ TJf„ï e„*BI±0ãPúXœn¨¸V”¿-ÅBEiQÆX°‘(­°¯…‹Ò¢L°€cYËP±¨"áßXÐ*”u4ªå†Å­FcÇ JPr,ô”{0J…2Ä¢D©QXü‘ÂÊ›@$*e‚Í U„2Á¦ŽJA™ç¨"” 6 J‹’b³ˆFe¢Œ°iD£ŠQnØ<Ô(cl 1¨”I Ê›I,k(Á(Õ/Lol0±¨”h] þ»çI÷Ü®tÏü}v÷k?”ëý÷Ì“„žöëü¨t¿zÔ¿Ó“J÷#¡ñ>$¼nµðùã—j„_j4ªå†}G2ƾƒ*F¹á®Fã—ƒ*AÉ…ßÎâ—Ÿ)ôüòåÂod1 1¨"” A…2Âdˆ~¯Å¢HøM,&…JH ìÁ‚°o¤ L„ß`’cˆA• äØ'RP&Ø#bÙ€ŒÊD™b"Å¡ô±G„£4()öˆ”öˆH”eɦDbÂ)PE(&ž¥ÉŒÊz& \¸/…Ih ì9aHöš0¨"” “R…2šF£Ü°æÕ(cLÖaI˜am'ûJÂ9¼°þÁä-öª…ûMÂ}&LbÊ9UŒrÄV£Œ±NcP%(9Öj ÊëTƒ2Å„ö¡±>W¼ ¿(½o%œñ ÷©¢ÙïÜØ=Ucö›€÷·W ¿ô þ›+ Ðþ Çr‹ÿÙDÝ%ôânJÏ[w ù°W »ZÖ»Xö0P´ëìåâ—œÃ3wÍð>/‘áw7…ú˜¼‘<·½æú´¹/p¿s÷’Oë{p®O9ØÐRýØû´/Ña©F0ßµþAJ¯4§&þ•Øß$”GäðïCŠq ö+ÌâçÔtÙþk·2!ÃÒìØÃâm[9ÚF%c¾:þ¢*÷o✠ʕþ(1v½Zn”8M8‘$[ð‹¯)Æ™|c^a¿¥jæ “‡[®Ö/…tœ;õ»…20~¹êζB¹'"ïút‹¾Àœ#O9}XüöÔ§ãÙT+T ‚»R“Ù`lkÓm£g ™Z³[Ý2Ðáå ýåWúˆ>ëÔç´ó½,‘LvlÓ&6ù»„ó@JûÆaÓ·õk¾ì«†»ƒ}ñ+Ì„ kJVNO!–*wj.ƒÏîŒæBz ;«‹ãQ e~Ƀ€sX¸¿ õ¿ú7ZqÌ›%-]ÝY =ß,ß— Ùv«&oI!Ÿº¬i¹Åžùè!} Ÿ(Ïuö}f(èŸûÓ<“ýä[¦Å8‡•ožÜ˜ ÃBï¿Ýp>Eä2yZß=yñ[|K²7#h^Zоz÷»×9Ø9á­äMùs¶Cd?ùméÝ)”®ì…ê«áH¹ýGÌÅ×ß\ö̵8…P^£ã·»Š>9”m·;e¹cìz5Ÿ~ùƒ„úÛ~–L÷:†”;l¶âÅ#Æ/À8WŒ¬Ü½¬’î…ºNÈ„°ò±E÷Ú¤WÅâ»Ï½`}TZɇ†®ät€½Ý·7Û–5\ô¹¥\žrŒY†ûv‚¾¤bGÛ£Œ[ƒq•ó'–Ǻ¬t÷éèL0;µÅ|b@*I~éuų£œùs "6ù-Gõù Ü·–ó„9—þìžu³µeøç †ÏæYÜÖ¥ãÌùØ1p554¹Aù50RN[ìµÙ™JF®Î6Ï×óûF›UŽýÏ猀í¥5Žq}¡©“ÀÞŒÕywÊB­û½<Ù×›å1åÍ)0NÝ[««+ª!tªL¶È%¶Ÿ_ºÃýI*ù1_ÓÏ’á7È¡kñ=™/¥­Œ“ºl©ôô~fæßË7è˜ ×{½4"Áí†&~ð¯þöµkò$÷ÆŸ.›œ'L}¦Z÷'ã}`à–µÍUi=~âðh1N½†McæÎ  ûÚg†cÍ 7¦‘—u<ëxÄ *ù…}ÿ‡©ÜcÌ÷´!ÐüÝõŽTvâx`<¨uQN¿÷’¹'n¥Ïÿ–ù3õdÿÍiýh±.mf^8àF@‡§©Ÿ ƒG¾4RgÌ +óé.̇Ñèðâ·üË ¸o ÷ãü"îkMýÝ©¿®Æ©Z£k´õpnãö5©š v!Sºê¿O#œ?Bý˜¤Dg3eà/æç³q?+þ7çÐÑ×C},¥§AqwÁ‘’š œ96û*Ì¿pïfzËtæcå1£…iN¨¯¸8oŒûªq_.îkÅ}š,×f<½—~?rŒ$à*›\=Ò§Æ©«°k˜Ñ"‡tâòtT§ŠGÜáù¦Q œX?ð€äåÝ&wnÌ—¸«8Ïáóê§Ý‹ùBµ¤õƒq,_<~²,ù,D,ržcrü*Ôì0®éýEé$H9°ÆíC>ÌŸÎ…ø\睊~ÃLçvè5-n0p~÷ukk n®})hÃ1sÌŸúâÅaÖU÷ÀY¸a¿iû²eWÁ­ÛÎ=®¤ÊGóg¾¬] õmóî[Fýš­Yý<ýÝ’¾» h%cãåç©1å=Ÿ·„^—ûÛ^…{—'MjÙ$ƒ=ÐðÚº@hygGN›ûn l|¬sZ¨†xw_¶âÙ ‘ûÎy€œ?(¸”5àÈü=÷ 㬻´aÅ gAg×Zë*Ø5ŒŸ>.ƒ|O¬\#çJ«Ã‘ù2¡[‹B‘çÎý£y礄x[©#­—»8þ‡N˜zî,ÔŸûZ²¬ð ušò2%ƒœX߸éVe,_¶w…÷¨Q°"«ŒæxpÞ å°[ÿ|øëÖÕ>·’€÷Œ= »­hvù è°EM/“[7mk­Ú:O>nñƒ¯Iê /ïúÑ~}ÍÇ]š!&ÕÏ[]dœÑwÎÄßàÀS+ |q:^J1NÔÁ³Q©KÏBÖÄ?¾¾5†…Õu}™ÌZ:ëS'‘ßÎ?ÿÊw>7>âêË}ÌÅñ’óã-4uœdÅ|:™ß&ÆÉ¹v³Š_¯³ðÙÕeÃêÙW`‹ÃX×.“v¹÷š¤ør¿k2 éÛÓ›X/oÏëßͺh”ïä ÆÑÕ>oÜÒ«ßœÿ÷‡žéu–õô~{òÉeRcç郟{À%¿Û÷‡9î¿v5në¬Ù‹áÀºúáS]ižãs„Q Þ¢ÓpêÈ]eÏâËP,Ø 6¹B¨Ï° çCïÛMöçÚÂùZñÏ‚½ÀOä7êòŸwƾã°;£OCÙY%_&]†ÇÒV--¯¶çמ_5Û‘ùbK¯×VäϘM¶ÙTÏÞQôå=¾¼­ñrÕ+‰gpÃ<«μ2ŠÖö._眆—»7½_½é2ü±´ÌÌÚ§ìYÿ4ùÑÔÇݚ˖b>›F†',,’PN©9ó‡¤qôîJ/½Þ?U=à4¤–9µa@ÐeˆÕoíµeWȲ}_º¶rû2å\ÉDŸ^Î1å|Σ>ÇæÌß§7¾ªúšSpt»©UÓ¦—¡ÎÃ&óãŽ\!‚š7ʨçÜWšóS¹¿ãÄéGLbÍÅq‡¯§¸¯ñ'yÃÆ'ó)ÇCŠqúÜnÚpïåS TCƒg°ö[ÚËü;Wȉ2Ÿ4*wWè?¡&¦xÖgìA˜•ÕñvÛöÖdz;°qá“èßǹ ¥9+rŒÓÿ’âÙ»§`kÝÄ›U32Àg…mÐÚWÉ)MÍjÓzy²y¿1ã;м'Ê™s7£|~ÿ^BçS¯%Ü¿ŸöwÊ×P`œ-Ob6Þ­xÆë€”`¿âcïÃî’nçÚÔêøÜGw¿æ/¯»ÂÏ™q~pN9—†£Ü·œr~é:'ãØ˜#ÕiØ=ß<ØáÒ¡®Ê¥W‰æñ¾SÍ^ÉE.õyvbïφ\Y8ðÈò^bšô¹j|îå²Ïk´Ç×oî*¥2@¹7ÕÑ%û*Ñ Y~ŒÃ9 LëdÏsûjÏø#¦?ñεøœ£É½$«¾ž‚eËë¬6!Z¿i•?³z&i·l]jú;?à󸇱^@÷ál>éöï:n®yÕ,/¬{pƒù~ß/”ÞÐZ¼ýôlkñaßè HjrÃ$“èÍ»° •»¿íX~ÛÀ.\u¼½ÌxÂcÅúâ¾Ýž:ãW3æ?Íø±geù»UŒsNA§7.Þ B2 ÍgÇ8‡Lb?vëü´Frñó[éa¼~S ,´Øt ,Á¢wL—Þ‰¢Ï6ÿ8ïS—ïøüq£×W»t ¼b:§_Ê€Ek×¹V—IšNRééw/Ö'Í õ¿·9 þuË/û²Ÿ8Žs_|Ê)e<|~–€!=s ÂÚz-s̀ųç™N[‚¯¿·ô‚ßwhÔcA¼ ¡¾óöb¿ ¼˜>¢¯'Ÿ×/ÖÎ꘮{ø|ݶ@Ì)0h5ת±U”c¹©üŽLBçë®Àæ9ÄõÊí†cœÄýÎÅÒ6l•òaÚ~+ï{=®Ù×A?§8ŒÓYg$~ ÎÝ=ôb· ˜‘ÛFríT&1*{øÑõi.0«K£îÍv˜‘šªý±µnÛ‚nø© '>7iÞùŠÄ¾ÔÜ%µÐþ[j Ï\i’l™ñpÐÔE^4ß1ŽÓÄÖë¾Çœ[Ë5~7«gÀ7Öì:s-“äd|—åîv…äôìÛVKÎÌî·¸°}O–§ï%Ôß³8•õ¯Ýå΃Ø|^Wg¢_|ƒ£ŸOÀáã«ß$ßN‡‚”IŽiO3Éïg•æŸñ`¼GÑ”óLÊ—é>ñYœpžçsqi‡ÞƒBiæ¦wí/ž€½pÎDžš9òFó—ëg‘¾ÛºÙ4þêÃ|iÙú¬PŽëàùÀûç,ÒÏWÂÆ¶?…qÞ{-?÷N…Í›º¨Ò!¨Æª“ûÛg‘‘ÛÛ¿;ßþxÜæb÷¯l¾Û 8™ò‹¬áÒÚEÝÕ{^2žê ÷ §Ü:ï•b]ÃQAm( LÞÞÐÁ6‹4÷™tÚÏU«çî?-ÎCù8Í9Üœrq%p>nØöÔdV7‡ÎëTp¤ÂܼóëÒaÁÍj}´²ˆ4¼X;û’¿Èïy£aCs·éç6H¬KšçO$tœƧ~Ð ŒÓIѾ\¥#*hÿ$c`ëééð‡Ïܹsb³H;£›ŽÆI~â÷ùÂ3±ÎºûƒEîH9³§wÓ-‰üþ7Ýw‘ŠûWºúÁ8÷ p“ ^^œ}.nX:,ïj™*;™EÆÙöî[YÎæ×}í¦PÍm|½Åw, 4TÏ¡Ü;(ûVº:·m:Œ*;¸“¿&‹D Û-¼¿Ï’Ð÷Ý(Äë6ÝEÎ…®ðy­j5Jºøî8¸5¯9crÇt˜Ô{aáÎ×Y$b\ÄE‡Yn⼓¾.}’ßNá˜ê_KëfÃùÕ+»ÚÿTìߺvú†Ž_z ¥:<¬ Ôû… œtð¾žÓlcåkäà|“V~sÙþR{Â}Ï9G–Ö•¹èïÎãÐùª9ãŒÒ8F‡rÉT ÃÌ[§Ã £»ÉÕš]#¾~~taŠ#PŽù à_ž—îºN"ÆÑå9>ÏíîÚåññÇacù±¸tN‡ÖÃö=únrMäHîMo_¦[eWàóµV×oµÉ’í“Ò¾)ÇçÜÞëÒö‹Ãq8±#7Fu) >×ÖîÙj~´ŽòsJÔ:@LíSÇŽ{ú‹ã—ÎF½± –IOï÷Ur> ý¾ø¼áo–9Nl}jܨÔ;% RŒš¸õp½&r!(—w$ðy×ÛÇ¡Auw8?ãA^ü àóY^vÂp[_ =BΑDÐùfÆ™º£F>:LÌ»4q:ñmAè5Ò¼aÅ㿺Ýï)®—8§‰r§Eÿ›×—h5Æi£Käc`µ/y—ža:\í¾X:ïñ8^Øcp˜lÝMº­r {}}Hñ‡Åù+Ýoú1¾óù ç ý´À85ÈÆ°üÇ`d§ Wl+¦Cà€!ml½Fª^™×{‡Ÿ7T‹]qÄŽ ËœíCÝî/ú¥s#çNyÄÙ&~ÚÐ+,”uŸoš—|\%éo´/ÒÀãòÜðúêkDW åÌÛ‘í÷#”SÙ躰Ëûû’‘g—>ð}Êõ ûjFG ÎOq= 'M®-.øšEkû»˜ï†Õ*O¨ïÇ×wlß ¡ý¸Ðùÿ}‰Ý=ð .%tŸ‚rt¤øÜ»™ÂÀ8<¨mþUÓÁËþ¥íýrÙdTððMÂüØ<Þ(Ÿ¯=>Pf_‘C¹éƒø>­|þhÅ5Ùö˜#ݵÎ`ë[i<ÊÛÝ­m6ù¦²LûTSt@Dg –âçϹ6ü{¦ûæR¶?Ç8„øüvó¦,ï1üô*ÞÞèý´4¸ë•úØ2›$ô{0BUÆWäš wŸwYÏ æZÏoâyU*òcøß?soèóã„ÏgÚ¬99&ǧ°Kµ§Ñ WÙd]pAÍsÏ=Ø~˜¡û_]ø¼ˆñŇˆëWþw7èÓ3h0N£Æ8Ó_”ÿ|`ÂaÐ#)|¿_¬¬þÝB==›Ð÷ïÂNõôعa¾êŒ·é Öç†}k7ëòF挿ËÎé0Žó™/¶«†=9Ï¢\äià³»é‘ê+³Ió6êO ë:Á»\ç(j¶ŒÿdKø¼s¸ù¼š÷•‰Ï«ÍëoŸ ìÜáQ¡t¯´sÅšÙ‡ÀÛ *[̰Ÿ4™¹ríŽl’ñm‘eôGpô|bt RÔV_¾—|NFø>çypîYi >·ZóZŽ-‚kž©}Xîoy"›öÝÿôtw'˜û¶n_‹›¡@9ÉCå뺊~ð|ýÍÏe8AWøü Jtx¼íM;Û1 ž'WLZ%›¼s¨ö´Å$ÍxmÞ;">IŒJ4#ƒj-6Œ0 €y•.ïŸÖÑqQÞK8‡—n?ñV0ÎÁE}º/ÿr>×ßU¡k¬°î}h~a61ë?,®ów ãn(ÐýosB×õŽ"—;÷‹Yþ×·ÎçÖÕ>wNX×QKò‚£i˜ÕªÚ$í/“Cºµ ^u¾7é ¤¥ ¸´i䳡½m Ý—7kÙVõy;&Î^ñúE«×â>Yéü‰Ãç¯èø®“þÃpµv`y'Ó4:¿4Ê!ÁC¶Þ{á/ó4»=3úù@Ù·Ÿ¿]^ìHèù€”½ÿâøÆ×OÆy»_l5è'¾¨ã\n±{ÞÊÔƒà2X•šÐ= îËËâÌrÈŽ*!Ên½€òrèã´äMŸ­2Âyð<ŸŸñq¯t_Òâó§[ [ØzêAˆKvhí[9 öX™½tDù|»8£ÞK?x2H8Éæµ}W„]‡ºEŽâþŠ.ß‹ ¥ûômz¾µ­ÛRkôfßV/Ì!öÈV]½+gó÷p‘kBç÷c€ó¼éxóNÜW×m Ïatùq[7ØèRå È¿¼dfÏ40îôJqhwv!Ê®÷†mG·]6óF`ÙÁ:>³íD7íw7ì½dnµù=í€ÏCuu€q\œk²æy×XÜ- ´™;Ró.äîßZV¯sÈ tÓáZãÙ|®/dÛ^~XÑ|8;°eãôÉŽv37Öü(¹=nѲ¯í °E¿òËÊ3>3Æ Ü_mOu[%Ì:íš8¬$²j7šÜ÷Fi: Î†Ök ºFT«¨Š£9Žìˆ_°57Õ¬Vï0Í\.?Õø³„óQ9oNWøüJG&V¸}R%)‡ä÷RAÕýõy‡9„ž£;pÎ4,̶9vr“¡çg–"O˜óBù9>ÿ¼æ_¬3hÓÊÙˆÃ8k½MT…ËöÃôÜ´¹¾OR¡ù ÓçW¾çÓ—$c>>—ç¡ç†[Û/'ufÕTØÌL<Ïåûttœ~'¡û'Ö?Ÿ`œU.ÓÍïN„±V¦#³RáqÒªŒ8ƒ\â&s)c<«`6/·!|Æ÷G8çœÿÍÿsÊßdÜ8ŒSçZ£9¶ú‰°tÕËo¥ÂÃmNÕZä—•ókƒ 0ëÚ»FŽdu=ŒðsÞÇké€)%½‡”òTÂy‚t¿í§>.”Ö?ÒÉoÌ>8ßávʀשáVcz·\Òàµ>ÎÐìÁ`ÀÙ3£3G¢‰°Ë< ßä÷(ø>]û|ñúw3Æ—¥ü$9ÆÉ®ÝqûŠ«{¡ý³mEQËS¡úø~‹Æç’ºm5× s‡îûU¦úŒƒÝ7-:_N”1¬ÛÇ´÷SéøþFRküíï—*›}_tŸAqZÚ­Ü¿~ú^059a˜˜ 'ï^[·$—LoTxòŽ«'Ðzû……¨-1}õ`³¹ d‡6[ôe°xžÃûÛºrsŽùYÝ_gõƒq̼}7ýò¨éÛvQÌIü~¦ˆÜ„\rÊÎbRû@oVÿ>5½r½&>2Âæí"GF7]Íz#™÷üž•¥>ä>Ù»·ìQk‘›£«ŒÓ÷ÅêyßwC‡æ³÷díM…4·”ã×.æ “]šNðÑ5Ÿ•t•ñóbr£ç±ë³o{²ó’  ó¤ºl·tê8|È ã#!l{™Ój;ÑúÁ8>eg.m½F$]h>xs*¬Nß«úÃ\rõ»_™²K¼Ù~­%»G`C?_2›¨ïŸԣXý6ûû«Ç†vª­å§LPEɹºq‘“­Ÿ'…Ò!/” 3¼ œ€ÃÞ˜ §S뺖É#3üKîÛÏóø•;Gø9ߟæuCë·:è]¸êópø‚U2ÜŸí7aœñ;¶TØzzXB#ÛõÇSX¾6Ì#ç‡oµÍéìôžÍ~o„p¾==oùqîñ²Ï,•lË ]‡Ûþ´¯%Å8“‡ŽWïo» tX÷˜T€…<ïœG^ØœêõÒž½Î7#|ÝÀÏùü…¾¯‰üAÐðNÅÎl’ŽorŒÓ%-4ÆÆr'ØXõ®H…C+ã*ûHòH•v5·gËîqy«ü~¦šÖDwÝHé:ìs]9tð¨›UedYƉû*‰=7gÄõ3Ab>êê㠜ݸ½gë°:~tÛ;¡©°Ð®¸œåð<Òÿ‰ùø¥lá¤æv¾?G ö._pöTÜÑV]µ{ö8 ¼¢ò"ï,føëV3“=Øz—Î[ã0ŽÄküÍöMãáVÛ›߸¤Â¹Õ}"À;Äæ4Z=UnS/ž ¼Ÿ4XýÊ»,Ž?tžÂùŠ_$±|£Á >8òjPå0©1λ Q» Ço‡×™“²Àúi>òݨð<Â9åVº ^ÁœcLh¿”Š÷®xœÜ)9r0ÐþFŸ¯ÅçÏÍu;W`¸.†¦îÑïƒùÜÜkgú‚<Ò®Ö]ßòKÝsjùý®ù}FŸí`%î?êêâi¡48òpÅ—{·Aÿ]îq·Z¤‚Åð··ç‘åßËRTóƒÄzc–M ™yII·/È Ý×q÷3øþ”.ÿñy}³ö›¥Ùmƒ0'hœ åÛ¶õqJÊ#œËÛ\¸ŽX/„ÓÿF!äÞ ac?:ë?œÑä²ðý\º¯a%ÞßÑå=>@W!ã·Cáä9Á©`Zr.p6ÔŸØí´µ«/ãM‡ÀiÉ„¼]MÈÅÏã>?\å «ú¨3&;?OäÓy#ý<äø|ûøÏ}+|Ü ÃovˆŽÚŸ ­jÏ[;é{©wôI{ãÏÞàPýóÀƒ¶A0|XÿwVn„ß#ä\Y¾˹‹|¿’Λ§ ã ÉJ;£Þ8)hb› C…ë{M®“ =]”ñõ„Yõ‡®˜ÞFÎö‡{kÙà ×ÍD~$]G>“pþì÷©Â‚–ÝÃÀçŸtÄåHß-p÷x‹‹£}Ra£¹þÌ*½¯3þ¥+˜(Š·Z õa÷SŠûÝ|¿Šïûñxë>íþeW–¯tŸX-|ßÙsŸ· ØSÜëT>8woH6vlÿp}€›ܪxôNÁBvŽl ŽGÆw5éè$ögΤÜÃþìü€ŽßZ|þ£å£Æ4º±¦×\×dòTXr7nŸë$ÎsË™ÇÃdph¨ÓØê¡A|œ$|¿[w¬ÙÖ87”ç=ßïÑåÿ³Bég¯1Éßm„‹e¡†§Âøƒ;¹N.GåÜTg÷WQœò}6žÿ<_ Ê®…;ô|ÑŸ¿ùì–î]6½w€óCÉNå”ëdSš[«qƒ­Ùù‹?'&ü>!å´™‹çüo~ÞÂÏ+tuqFn~5cÜî }öÔš6ýRÁ2¿ÖÀ;ó®sßÜ#…-mØ<¼®ªw>·ŒŒðù½×eü^?4¿­Ù¾­?9Ɖj{nþ.× °ßÿÕÆsø}ïÖ©W·Uø} ÛNÃø}e¢¼d1äð”aâzp‡AJ›eäâç•~[Ð×ç’ñÚÜñ#ÛZ³ý=:ÏQ`ÉêàÔûë¡u¦Üñ‘}*Té`—Ü}ÛuRÉùÈÏh;Ö'»¾>_=ïÀI»íÅó!ž·v·Zq鹄¥÷“â0Îùcƒ×Wï¾ö´{³ü¬M*D¶û£ÙC×IFÀå£Éö"篂] ×'î2ÂÏkèù»ùOü^5>ïîšÖWß͈Ó¡©ãkãç„›äë$¡kqÔ¹ÁŽœ#È×™dÔ¾¯›Ÿg eóÖ¡ÀëZ—ÿø¯bÿÖå=>ïv¹‰¾WB×Á‰ó¼ŠþØNŒ®üÑHC®†û|8xË èù‡aÜGqŸœ¯[øxÃïKðû.ô:í×rŒ³Þê›\{bHø¹5ݶù)çê¶É´[ÖëU›—}Sáýˆ®½õm5dãÇæ{ú½s€‡Ç„…·3¡çnmØûò~ÎÄùµ<žÂ 5&à© Ûß`yqve\î³mÚZ袷ôÚŽY©PuvËqO|4$Hwal8–ž2_TÆÐ}ºÞ„ó…Ù½GñýðõÄŸ½5ÆùcåÿšÏW'|y×_)ÂÊ!TCR¾<ÿòl…¿/GØ:„Ð{Éö°1ü@†ç¤áâ:œ¯¿ø~ ¿×¯«Œc˜áÛbBP ¬õxÙ§óJíýþ©íd ybÕìþC Kq}CÏ'í ' açðÖ0üà‘Íz9/Ä>Ëó—îÓHh¼Àù°n²V 8{Y*ö½Ô¦Ç ¹cÛòÑ"îg¿Ï”¸ß¾~¡÷'‹qøù®®.ð¹Ø„RSÜ£¡œEÒ’ön©ÐãÒðàç+4„íÛ1>º”Íë†*CÄ…¦9C€Ð†+8‹ãhé×+ÅçÎh#œ¤-€~>æ§LJmËAecâ5„Ž_N¼>ùx@†à,¥¶Áð<:ÚÌ# \ìG§õW½oõSÉñùå*ÜNÝui>Dãê¶á"ìOŽ~— Njˆ]¯ ç<.º»oOè=Â>„Þ3wǽ_ç3ôÞ‘ÕOçë Œãö´Þ碹°ïá•a&›RáuT¤*2[CÜrê/ÝÝíÿ:úÜÖ¤›|n!ž÷ñ¾GëžÍ“ð¹ýu fC'ÝÅäTˆ†œKKŸiX>ºAEÝ€Ï÷÷Ë_wg“»ÍÙ|ßLì¯ô¾—ÕÏ¿+Âç[úä](ëu…h*ŒPÆ^’•Ï'o–m™´Ô…í[Zñ×ÍïÕßïåϧ¿ °úéw Z|þÅs}g œË›:Ýëw1¦ž‰ë0´a>Ùä{»sAgGÎi&t¾ÐFœßñß_ð~Íó‡ß/ÕåûËB)íS`ÃÔ[-MòRÁOSqÕ‚ùÄ«ëº-ŸìmñaYŸ¨Èî9³~ä|ËïOð{¡¥yÍF'Îî͵^Ø}ã4h¾¶Áž}}òÉ1ÕèýÐÇmÝF½anÖ÷†ÿçÞóõ3_¯ñ{ÒºzÀ8ôüx4ôÿVµ|™îiP6iòûbi>Ù2Y?úÅ·`SÓ|ïªN„Þ[Lø÷ÊÏø9!ÿ›Ï“JÏïåg^Ö¾Ðæ¯GÀaÛ§ahÎÿRÉ*Ÿ|ÙÜÇ·÷Ðâ¹ ­ká¿{ ÷õz²qé2{?ùz>Ó‡åݯS`:ÞûÁêºnzŽÓ à”¬Jù¡ù$ã´2¦î³<ÿ Ý7s |Ÿï“ÑsÛdv¯ûªD9Q2`hL7¶.hJëãÜoôGê·)È»ü2×ëõHƒo×OŒêa›On~?vë¤}veÇ÷‹ çDÓ{¸ÝÙ9@–„þ~íž„ï‡Óõ3ÝOUcœƒ=æ]ýÌŸÜŒ{ïÙß> º_p™ôÙ>ŸÌ5ÛíÕ¼;Ì´ŸýÍl¹¡û·Ö„÷E¾/ÇóŒŽO$ü>›/ÒºÁ8Ãt!ÄH÷ƒ…48µ¸ðø×|¢Ò£ÝÒ¢¤#¯í|âCØ}Âë^¸MZxï~ÊÜ…ï¾4/©–šz!q¶5ÛOe|ìâBiË…>–oŽ'>5Û&_j—VM\SÊÈó 7Íaíº/vß^xú{»gi¾ÚAWÝF™ìnµ»êW ½ô‰ÍS€ïßêêãÐ~IöŸØþý¡Gè§–m>:ŸLy§o~Þ†í£¹Ê£ïHø=F:otaßOXværÖú/÷o‡uí6Î~è|@bKëãø}éùâòØH2¿qñG¿¹ið©íð9Û¦äõ·˜Vf)¶ìÜ{8)ª²vÔ ”7ÿã>6ÿ}íK_Øxå#þ¾IW?§LƒÀôד¦>¶óê4xývGí óÉÊ™.oï5·gûdôõ¹G.Æ™zŸÙÌ+ªGËÄýaþ;<>¯ø‰—qöç6Zý!Š\Ž·âÓ ëë Ô…óI“9»“–'Ûã”v®.þ>¢wÅÍýŠ QU'ƒáOÞ‰y@÷éd?ͯã0ÎäÎ-š¿ O6_×´Ùs< ÆtÔ«nb>‰O9ú¥qM{6oè@úé6ÎzUbgœixÂàwyj—Ê.âïãø}i]½às—ŒÚ9msÎò´iØ‚ùÉi`·ªáØŽgòÉ‹“cÖÔ0œñ×zÞ­Oè¾ ?Ü^~£ëûÜÄó~NÇï?—¾§¥Å8ǯvè±ãÝrcÁé‡O.¥ÁÂrK%‹ÓòÉà—+¦Ï°:_r#|‚Ÿ£Ñs;q_“Ÿ7Òü³º¯@×ûz¯ ¥ÏG;§ ÉDÝ…¥4¸“²nïÔœ|’Ò¶ì*›!S =6‡}á¹và÷ʽN—/±¶Ï3ù=D~Ï€ÿ.GW/'£ø°8õbrä±¾vÄ«4hñné–Z7ò‰‹âeï±›zB«ùM/Æ“Éìs+9Ë÷çø¹/ùú’¾Ÿ.Å8/SÏ›%ŽZF¬,0™Ù(Fôq'Ÿ\9n§YîÓFÔ‘ã`aû¤íÞ–7l Agé$Ç8ºeÈÚr3XolTùt¨7$¼çŠûù¤Šëû:sïvÿ¼Å[7Oæç\Ä õË&i2gð*R~Ï,9l“„ž‡2мú°:ž…%§§ì1›?è¹?»7Žqúß±[º.l%y\lpÑî~x×ÚzìÐã|ÂïýÌ›t{ÏÓpòfRÃoÙÕ[“­B¯íÀÎ[¡Œj»Í„Ie ß”gçB|’Ðßÿ¸[ŸÑzÁ8K_ù½?ô|5ôåó’ªéç;¤ÎŽ7ùd蹫©›"­á£µcÿÝ-¿·Bçƒÿ»ÜÖ½C”ðõué{ýjŒ3»G³u$nW™9ñvéP÷ƒÏe HùE_«HÆË`nÎógkŽ'üw)=>ŽØØ8Ûœõ33àçóô^I‰¸ŸXú>¥ãhß­?,yK¦>¨YqË´t°Øcòý:„Î{AÑ1ÿ`­èÉ„ž/Öƒ·=å1oG9³ó{Ðý’¯’±Ámô}¬ô~Œð:ÐÕÏëBiµ ¡•Z½_Oü ú|p]ž}•I#cÚþù㳫Þwó%×Oï9<•Ð|­ê–dftõñ܈ŽÏ-avô”φ†ß$ôùŽÐÍ;:·¹Ÿ)ÄëÑ?¿=”~{(ý§x(é±÷,ð¦M™?Ò?b pê_ñ¢J³¦­+ÊñK4Œ}Êy2Æãû3ïÇÇý¾9OÉü##¿DðEŠCéc!„3v‰”qO9#JË8|‘¥8|1Œõ¼#£‹O`L'  °ˆ"QZæǽuÝJ±O_¤_ù%ÁŒ_bZÊ)œy}K#Êà?$å/ÌÁ IeLýPäÌ3Ò ³„3ø„B•ÿâ‰"°¥¹g¤óøþ• åö‹_¤àƒ$-Å”–3¦´ó@*b\(Õ¿àô«¯·šñ¸×œà«’‚NhyNþù»?þîÿÓýQŸ½'[*pžþ8géý#‹ò^©à¯«b¬ÒhægZÊSܱЅc‚«QƘä1ŒíĽu9ÛIÅ<㌅ p¡PX‘Œƒ`ÍøyÏEÁÓIQŠéËø.ÿÈ/.†qN©eˆ¤@1(î)/Åип²Â AðŒã\èHæ«+0¡•ŒPš­ú… ° Õ(#Æ…Q1?]Î?à<'OŒJA™`±Æ²‚å>qúÌOWSŠ#x“Ëñˆ8ÐÖ¥¸¤ÁŒKjÌÐ3Ï1þ.ÿùWÝæÃËšB°ä‡ç“—ŸßóÇýQè¿{âÿÝžhÀþ31þÜ;ã÷O8ÎÚ_ø¢ æ¥)°EU»‹f «?óÒ4.å,Ã[ÊûNð6`| JŠÉŸ€2Àˆ,Ån<4 ëSÞ•²ó*…±›ÿÌOà0h±X¬±X”(Cæ1Ι0³¹X˜/b©šÿ9ƒAðÄ Giã.eÀxÍ‚o¦5™eÈØvE¥¸ FŒSÌü2c™ožu)6gd ÑØ_<3M§¹ˆ1☯8ç4 ¾â§ûê¥0Ld)~h,㇠¾w*Æg|2‹QnXØj”1w ª%Ç"OA™`¡Ç²b/íEÃXvræ%Î=2…& ÔûUøó»þž+þ§ôECöšµz”¡¥ü>膌Û÷WÌÕ/úŒÝ¥AI± XQ»•â¬F2Ϊi/Ê® =˜±(þ.ÃÚAJ›A8ó·—bSH@`cˆ”üàÅ u üùŸ>‹á}ñ¯zá¿ÓÿxßûW{ïs¥{ÜÿTû³ž&ô³_{YiÎêŸõ/Þ·„žõïö«¥W Ÿ½R²¾ÁTþNtq¥\Ucƈ.©Lùœ3h‚IË+•‰2Å‹öú„³aL25ʈqr8¿^àªaÒE—b?«ëýà€…cjPRL”öšpƹ0þ†½‚ñpâØÜüÏù†˜Ä TQKÊ T¡ŒÛ¹X`ab«QÆŒXRŠ{aÂØ7%Ø[Ü÷‚3Á®ª>@¸ÀCI±¬·™3›M± âPúXá¥xª ÂyG c3Dz" Fe¢L±_Ä¡ô±_„£4()P‚pVE”‰2Å^ÇBƒ’b¯H@`‘EþžWýžWéýçÍ«LYL•*0ÀÔ(cLÎT JŽIšÂ<Ñÿ„Á“ò '5•É©q¨” “Z…2blhý¥ ŽÒ ¤˜è Œý)p¡K±¿2Q¦Z`¢Ü~áÙ—Ô¡œhÎî‰e…aÍXöœù¥d,k,%Ê F*Bɰp÷+øOxön(ʈñ|8çPàG£ÜŒð5¡Œû«åÖ’2¤[Qž}I+ÊõIA™´ùÁ”nK9ö¦Xˆq(},Æpcʰ7eÌŸL” g ª¤ïKࢠ{-Ê Vɸ¨[:e„Å*îúƒdÀX_Z”5cõè3~=ç¡*UÚë?1¥ù˜ÓRl (l‘(-Ê›‚eˆA!ùÁ­rXøóÿªGâW÷Oûã¿Û…¾È{â_õÃÿj/üïìƒBü³þ÷ßÕû„¾÷³ß Ÿ·ÀaXcÑÁ;”aÒ©þ º¤“Õ„1¡…¤ F¥04çZ3>«)&jJ“5¥AI1iPú˜¸ ,yƒQ)(cLâhT1ÊM`¶¢Œ1¡cJ1 SJqÊ~«&x$J‹²ÆDW¢ 1Ù#»5U"¬1ñSPƘüѨ"Æ3¸ÏúØËÂQ)(##üïQÅ(‡G4ª¸%eªQÆŒ÷\"0˰`RP&Œa(N0*eŠÇ& ½=,&u)~™ÀvMÀÂ2ÀŠØe(k,°T JÆØÎ†¥ØÎR,º”^$JÃx…÷U 0•É8Îq(},Æp”%Åž•€2ÀâŒDiQÖØ³”(ƒÀ‹DiQÖXÄJ”!²â÷üî÷üNï?o~gÍž)0^嘘)(LÎX– Á¨L” c,– ä˜°)(LÚX–¸Á¨Ì_ø®á( c»&°„vC©QÆŒI­F`‚G¢´(kLt%Ê“]*BI1é£Q”“?Ž€•‚2ÁBˆeÅ °3Q¦Xq(}á¬Ç $¥b…"C©PFX0Ѩb”ŽeÀ¸®™(S,¢8”>’¥FcA)JñŽu Jn„¯ e‚Eƒ*iñƒem‚ËŠ.•‰2móƒmÞ–rï¥Xˆ ã‹1’1ï¥X”Ñ( Ê‹3–¨•Âx®÷¾%ÂU1žktʽ6ÆâA• ¬±ˆ•(Ãn?³U(Æ»ç×hÆqµîE9Xè‘(í¿ÀɶÆF Db3P ŠP2l *”6†hÉνëŸÿ=òïì¡ýOîýÚï„^÷?ÙãþÕÞöw÷Ñþ_ô1á³R£Œ1qbP%(9&P Ê“(–%’J]þ¯9ÕB‚q^¬)cT ÜØpT&ãSG£Š…óQL¼”&_$J‹²Æ$T ó5LÂpaŸ±DèW˜Œ)(LÈØRìéLaކÉi"œ{br ½ T…2Â$U 4ŒË6•‰2ÁÄA£dØ‹ °á†*A¹aR«QƘØ1¨”<eÒš²¥…DFe¢L1áãPú˜ôá( JŠý'¥Ï&Á¨”1E cÊʰ8”(Cì; TJ†}Ç1¤9?Ú G‰2ÄâQ”âÌʰˆ”(,¤H”†ñ¢PXT‘(-Ê{Œeˆ=F*BɰàT(C,ºH”e=F‰2Ä£@¡dØcT(Áµ;ú÷|ì÷|Lï?o>æÆþ7BR£2Q¦˜œq(}á·( Ê5–%k0*eŠI‡ÒÇÄ GiPRLà”&q$J‹²ø×(}Lh9*E8'`,ì”!&¸U„’a¢«PF˜ìѨb”5&} J‹²ÆäO@éc£2Q¦Xq(},†p”%Å¢H@g—¨”¾pŽ€R£ô±PÜPj”1L ª%ÇÂQ¡ ±x"Q”‹(e€…ŒJA™`AE£ŠP²æ”Ÿ-W0*eŠEË ó³M±àâZQŽv8Jƒ’¶ùÁÓŽDiQÖXˆJ«Å¨@¡¬±(cPZ”‹3¥ŒÊD™`¡Æ ŠQnX°j”1mLÊÛ6Áâe,C©PFXÈѨb”´š±±ÃQ™(S,îÆÈ–a‘+Q†Xè TQï¿Ïè–a#P¡Œ°D£ŠQnØÔ(cl 1¨”hÞ³tüï‘g&ôC¡þweþ£^÷ë “ÏÃþ»æ`ÕÃþ§Ï5…÷›"|˜,±,a‚Q™(SLœ8”¾°~D¥ L0‰bY"£2Q¦˜Pq(}Lªp”%ÅäJ@çšÂ^L²aÏ “L‰2ÄDS ŠP2L8ʓΓ.e‚‰Ë’/•‰2Å$ŒCé {c(0Ä4öÈ0!‹…~„I©F ¿Q@I±÷Ä¡ô1QÃQá.…p{Ž!ö›H”‰Æ2¢I,G¥ L0™cYB£2Q¦˜Øq(}Lîp”%Å$O@`¢G¢´(kLx%ÊûŒ>ö˜L” @,ªå†ýE…2‚ˆF£Ü°0äX)Â] ,Ž"¡Ç`¨PF¹&J‹2Åb‰eã†R¡ ±p´(k,%Ê H*BɰŸ¨PFØO¢QÅ(7,.µ°—V„’a/Q¡Œ°—D£ŠQnØKÔ(&?¯9•¬Ç ÷Ù¢Yosû/Ücû³ý¸pvÿL´Ÿé±œåâ©›žÑëBéîù_·¸íÚÀü„ÁµbnÅvÛÒ¡ó¯±wË åYÕǧý H·Áu_wÿ&i¢wêAê,Pï]T¯àvx‘Qþü‰ùSóc9•”CÏü31ÎK·ÐU‹.n ó#lKO¥ÃŒí#/ÔV@z,°ñ¾žc˸«„ræZp®…È¡â>‚ƒöY¯½uë­äSõ:ÕŠ-~â¤È1Ž6ªÌÜšm6*[çt¹›™Fša W¹ad߆n–°?𒥑ÏdÂ8=„ú¦ôýS¹¯÷Sá¼_êOÆ|¡^s•õ¤ý´¡}}Ÿ¥CÃØô/îþdÛæ.…fÒ¾Ðñ–÷HóoSˆÅ®G·Üd¦äÄ¢ä±U:öbÆžŒ÷þ@²ø´}æÊÐ'Ì?§›ÈâÄaœÛ΂úzÒ\óÊ×H?4’î…( 7[=4-³®¬fû¼šã4î×B¸‰Îž­®DôQá>”ŸÓ—ù´2Æ 6Z{¹ÉÊ ¤ï†VIµ øcã3çF# å«”œ=X¯gçƒÝ§2ÿÀn„/Է¸?÷ßÓÙ¡Õ³êëC}µµGz§~§Ü ›H¼ž½I@• xsøY|C|?ùUÞÙ¥‡Ô##¶éム?™ù\™‘Smã{UÜ,e¾xã£ê½zÔ¦XÂý›=z r È×%ö›B©õâóûÚHJ™ß° ˆ0¯üøN`ñ|4wRtëšÄáõ“&åM&”CaE¨KæûÒ[̃]Ưgm«þTÂý†Jó¥0N¸¾sÈ{éFr´WKÃJë2 ©[øÃ‡¾N=8þ¢pwSç9¯&8—š‰þ˜œëÄý@¨?› óqjLëãì|Ù«ü¥ MäCÆJ½^G3À3Ò§Co!ªŽ³¿Üêgä¡i„ù˜3>'ó¥å\,}à>*YÀ‘ì'Ž‘ãÜþÄmÇÛMdÔ÷rûœOeÀCƒŒN›C ÷wäùÆyëU§Ü=œçË9ó”¨=Ôz¾ã 1~$Æ©8tR™®=6‘¦ãR*^KΠ܅ñ$sÜ,°\dùÞqñ겑„ú:ö‚ËB›ièÜŒûU60’†z¢ÿÕO¾PçYp“'“ý6’mñìèü~îß8UqF¡}ÁšùmN ¼. XÇú~ÌÏ̸/妗c¾˜î"ÇRW?ÇCµïÜÅFÒóú¨ø¯3`Κ_íÛ>t6>QE6ës¹¿óë €nWîÌ5+ï#šä­<¹:œZѬãæ#ï$Ý'¹F$iCÀûÓºøîÉÃhý`œ G¾­ÕÛDžê Fù—a‡Tùöili°eÝ¢¥S†Ã· Ú-G¨?s[æ?ãÂ|eíE¿#ÊõÒ}Ks*ôÞJu¶nõ7‘rO.'|ìpÖ/i¸N¾«€ üê²µOúPhùéè»ìYŒŸ÷]Âûè©uŸK{2Ù›"ŠúÐõy“ºúÁ8Y-o|œ²‰ñ.ƒ²Ó‚Œ¡G ˆóZ“÷n¶‚Uf(—TŸAañë³Ý™ßY7Ñ?‹Ž ·˜¿Ö] w¨Ï#Å8wÈ”q÷ön&ÚÂSÇ]†æC›ŸŒ;[@ž6Ë÷êuuää6]<Æ!Šû2JSÆÙë!òѸŸ?çÐþgHëã<]êá÷i )poûlëePoð9ó0¹€Àú^#{ ì•*M«c>~6‘<›WöØ{r¤eȶ«VîÜ7¬/™ÌnQ±Ôzù²eÙ u Z#¿Ûú²¯È#ÓÕÆ¡þD ù²fä2ÆßuL+ N·{´¥T¸ÝîÔú(6î8ÊGñ‚Ãk¿Ùï cÃÊž8¯%|ÛrX>Цó•ž Û' ÎÀ´~0NˆãCšÇ‘ç9^a&9—aƒ¦ûU½Ël¼i”?“P>¤›è{ÅßÏшÞྤ›Ôƒ´¯5GIüDί®~0NÜ9àG\WíÚ·óéehêØÿÎÕ¢ˆ2’Î_Ö¾¨nØzÆT2æâ¨è_ÜE~8å Îâ¼ZÎ¥\·Á´~0ÎñÌ5úNßãÈ’×é~.Ã.¥Ã Á¹¤¨åÄù±e‚dà± õBë•Ðï× îNÙ­‰½nÜǘó‘8/œsuõó®PjëoàÖ÷H‘íÌzà_í ¼^à<¾üòÌzÃgfÖ8¾6fátB}»e„ù_‹ü >/àþžÔ—×”ñr[ÓúÁ8½÷íîTåT¡<Ä+0ÿȇ Ï È  ..=–Ugaã$á'îkÅyÈÜ×ú•÷ü‰û'Å8™•qFµ•¸ÛÏe(¹a]kdèÝ oêéuÉÇ j|YñUÔ ÆŸkÆç=Ì7¶ò¶ti%,ò¶¸ïç)ÇPŽqZlï½y+Ñ»kŽ3»+°UÀÔ»A2ì{oœ‘ã ‰õ5-wLaãö£³Ü?”û­¿»)ÔÇä ë×o$Ÿ^ àøa?ÍCgŠDèl[‰0ª4²»¯>Ù´ã 2ïsíþu!!¼üîºý§æ£x–ú :Âþ“5rWÇ…ÈM_ov´5ÇAÊ¥¾Éq§Z¢íÙÍ­äí]÷ÙKW`|å¬Ñ_%7H`@Ý“e0?u6t g±ùNKr »³^(ý^&ow€8 Ü§PVpuKúDqÔÕðý`õ6Òl#qK½úì8}Úo’fwƒìøõ ÇžaPØ|ƪƒ¢8·ˆèÂÞ Ò§&çÄÊùɱq¼xç¶-‚ù@2ž Ʊ n¹x2çý“û[_^‚qç_îô¼Aºivv˜œ>ÎòQdÚ{¯m“†Î£ü¸Ù còãYåá¶á®‹Å3êC^Ø©L¯]~P_–°k¹ÚŽÖÏûB©Ó7žÔ1ö¯u¾ÆU[¶é‡7ålõ?7Æ»'”›4ŒñA­€r˜ßH¸ß&_Ïpþ“®~0Núõ>Yüvr;bë ÛVW¡e¯çþFÝ ôóï%ao.ÚøÍ$Ì'Ž´üú~’v“½ÈKæ¾þœ?O}‡2~/ã˜aœ‚ûó&Fêo'ÃÿhîÝí*”˜a7ˆáªí;Öéá±Á1³w)õ¹5#Br2†-ͼšóÔEœOu³¿â§?¤1<w'd³ÊKôçÕÕÆ¡ßßvÒ´|ÓÄ›«°bMùáÅãoZ#Z/Q¾î:ìEÌl6ßíHnÜèœZæTÐ÷5NäËÏØòîNß!å¡úÅh‹­A3 {H‹˜¡¡´~0Îì¥Oï¼[¿lÞªº?|æUèWóÖ¶ÙSnÛo#Gï1Øqêih Ì!l^A(—ÍÂlû/6_¯[ôršI3çÊóu¤®~0Nµ•’ÄmOÖø[Ý—‘«Q>=ôéìÄÇ£ÙÑl#KØýY/¾÷¸Ù„ùúÆ_€úa‚ƒ¡5DÌ©þö]‹·Ì÷»da[Ù…–ó‡3Î õ]Tcœ²'»Ž<Ö1žäW˜3úEÑUس²vƽå7HÛõ‹÷'‡¬: Ž"~I_•*Jɹ®‚Sí`V7"_ŠóÓ8ï“ö)º.ÑbœÅ÷œóz'o'»úÛ Y5zDÚŽ:wƒ<,T7H_ï#u€Ö(qýÃýéüÛ ¨¿ñÉâœäãIʳùåOÜY½…R·²íŽž¶¬º°rü‡¾™0c]¢ìû‘döp³eûah«ï§,™-¾ê:Pî+WVîò'}È81ր̓ËÀž‹ÉóÊþ?ñÙ0ŽëäNÇlo'¯fY9%„e‚ŸÅ3ïËé7ØüÍti¼vŽèsOùNÜǞʽ²8LZ´ð%¦vE¨{àбöÍFrn­ŒSMgˆ¹th¨·G5.ÎûÕh—{ÿI™ò½y»an°Ü4°Ücÿ9ÌOÔ˜pŽç¶óùÁ¬ãSÆFè±útcëmæ‹‹q¦xyŸÐíddÍ#eÍ]2a½ZÒ³Ï7ˆ¿ÑÙ² *º‹_u…üÙŒ·ÓU\ŸR¾™Tä¦óqˆúÐÛ‚nYëJûŽã$à,gáþí¤Ö¾=ëîŒÊ»«õG×¹IÖºïûèÏgì©RýÂlÆìM(—`°È‘áœÀ» Üm”ö½Ã8w,¿¾Dt7â³9nÞN-Õþ&I ñvû]'0ßcì4fã0õ"´Î½`âÕ“–éQ>âþçw‹u¥û~è8§Æ8g»vqµx;ùÐbì‰O3¡øJŒöË€›Äàðº1»íá¤0íÞ5—íït#Ái¶°Ø‚ût3?áB‘“Éýß)¯˜ùäbœøó憰t; ­ü1näáL œš›$üÒÃ…Ý> …Yù§¸%Ì¿æ“ÌýŒÅ8œ3¯«—’Bé˱™>3Äj´*9¬Ã¾Ú¹ß$Ãsï•X6´„¤å!š— 线՛pßëëÁ}3ÍÇ ¥õ€ÏqJœçü(žœ­m½ÐûF&Ü»mY7>à&¡œT 8šÜK²êëÎÑyOtŸÉYäÇëòŸGy©;ˆù£^ ŸdÂýºçjN»I.ùþª®±5ëÛQ„ú¡!†ÏæYÜ}4Ìxz~êv—`àþ¹|>É9=?í›aœ¼}=ð+ÜA6HßOý Is÷[0ý&¼ÿáÈhìÇý2Ö5Ï"ŒoF6ŽÝúÊB&úAóy*Ý'|!ySžø^iõ“¬ãœi\µÊœî;ÈÛøa!Áå²À°à%-¿I:†VTyº€¤õtü*f²ý†v¢ï-ç…ó8ºüÆçEµØë9?U™?š[êgA¯39 wß$ž R¾º]ó„¶áOö߲îáÅ›d_úÜ“Û=|!_X†5ŸC8çªäÞâ÷‘-¼Å~É絺<Åç¹¶vÚv–¸Aô–?M¼Yx“\<¢h¤_G3‡âÈ>—Džˆ~yaŸ+äV8¡´€9[kt¼ùzÜú¦ü¶J}µõ>JƒÓOÔu]³ƒXfç§j³àø¼Í†Öo‘èÛ7*Ý|@·­²s¡óy'Ñ/z¿€Glf 3'>I9Å66ˆGô¹FøÜ !Ý"cßï UÕ²‹è‘‹T/+½ny‹œI:ª=ìdž Éy„ó68÷‡ú~›‰ßÏ»C9SIÖ²Ÿ}¥§ç ÃrOMw’µSöî73áfÓûß"%['_mÛɪüÔªZ¹ù„Î:ú:ÍDÞçgq~]çþÙ·ãX|ízÙe'ÑvúæYpmãFE¸Ý-2~ši¯ öÐøÁÒFóÉ}áëljK¨/¼P?þë0¾È÷¥ÚÆ8§¦ ·“Œ[1íäÉ€,¦|QÃ÷z¬žË<¿¡0èåÒz¦óI®¾°P¶'í>Ö"þÕ]A ‡÷ôyļÏòϳ4 㸶:u­»Ã.â¡3æÏ‚.ÒÞ­mFß"O«™u]ÙɬR¤[¼ÏÎ#ÌGŸX•­Þ{R3g‘WÈßÇùÕ+»ÚÿTÂûbiަㄅFĺ²‹\¸¼;P/! *¶’®;9é9>'ôÈä­°6Ó»öÈÁs ãewß²úŒq ¡nŽîâ~=çOSžÎ/œ=Œ“ÙF0~ÞEîÌKÌOË‚Ë:× RÜ"ú¹ÔÕ BŸîú¾ïc¡Ï«N(ßÝžÍ#‡AµÜ9¦Éëß±qð»„òmöAº_¢÷©PZ¿Þ¸~©.»È¾q½›–¹™ÑÃ>8-½E æØbc?w:¬Ÿì¢`œ@#B¿¶.ñ÷OSïø?qì¡íBF…ÍSz@òòn“;· ë#ŒS/!YëW°‹è0wùYPhä_~Ë-B–Þ>¹/Ûªœ2žª PÆ #ÕöÝ/Ù:.§.¦>0޽Ÿ Œ·¡”¿ëÆúÌpZ?§fp—@œ»­™yãym¼Þòð-R!¦×Ìv®Ð앺ýD1îLYhÖ{Åúª^>ŒWé,rrô·oá>諤óµ ýÕ«,ŸSèêãÐ|ÞMfMº[éQ”“<¸¶9ãi÷hê â ít ÆÙ|~§Ÿ”yÓÞNä}ò8ô¿§Ü>7¯ÿÍÕï&» äð,pª¥"ß"ña#}§Äø@â'7›œh¦~´`hrP£=PnÈoÔÕ>ÏòØ›¯9‘»Éªëœ5Õ®ÁÝÖãGÈ+ß&Ú†)5æuÃð÷W³ƒf>ßVëÞÉ!Ó·¨ÖöG.,/_‹¼[¾oÔªóšã­6±uÆ)ô8¢7fïnò8ËÉ®°ù5ég¾h›Œª™Üeã9ü³ùøÁü(Â9Ÿ”o眣Ì×¥<_GÐuåæi1Î…'ËMª¹í!ï«Fœ?6øè=[Ùî»ÍmràýˆE/§úÂ2°ÏWîœÍÏc€Ïÿ¸:~+èôð¢äµXÏl…”¾½Ï…ÒO¾¯ðv¹4l›Oñ¨kkµ}טàÛ„Ž_ÞiÕp\™à¹„rXØþ“…x>Ç¿‡¶Ò„ÂËÞ2îˆ ã2NÆ1=m„Kë=¤•m»ü'ã¯Áèåõ{›léÐïùÜ×îPöø‘vó‡¸3›Ç[‹œÁÔK/ʶ©ð^ÂyÞ|^MyñìœãlJØq¢Þ¹=D[«.Y®Áfÿ©#Ý7Ý&õ\:$$|rÊùY@Û @ˆÁ„óšNRéé÷aÀ÷)wå“„òÜ€~~¬.0NÏïN){í%ßÌ?=ª¾ö¹»×ÍUÞ&ß™‡*àëhÿ¨EóÉøss³ö…Ú‘cVcUËo†ÀW¯*çOñ˜ö 9rÎ7‘Ÿ±ëj ÕþX{ö>ã”{^£}Eë}Dºàãë²Ï®Áœ‹»<³’nëüK.$È ±ÿéBçÍóˆûºË‡ØΫ˻d~çVšļè¶íÛ…×"߀q]€òèh¾Åaœ’EagvVJ$M[”›ññì½:wùµÛäÅ‹AýæÛC¢:&7qËB×¶lÝ(÷Ax¾NÕÕ >W óî<¶h:­[zŸ“µsùÃÛ¤A€¼é¥šÎ O,èÕ"1Šó7 㩲} ZøœBˆ¿s¶Æ>²~ÖÛá*fCìËG¿Þ&M[ÿqèÝH°ÙòôsxŒ‚Œ–Ôhdvⶴ;¥çNÎÎÙÞéú…Þ—Bi²{­^Yöç³#[›µÊ†D×g‹&6ºCfg{Úº–œ­ d¥€O]m­®o`M8PÎ@0ðs~îÉ8M¬Žéû7Â8_MÌ"ŒÛGÆ™¾ÙÛWš ' ¯2ðÉy“úpƒ·?d¬Ý·(|U™¬[°È€öáÐeÂÉNm{û²}Ó"¿žÖƒ ì·9Þf|[cº¿’H*/<²ì³áŠ~›ÙíîoåÞßËe¶GöÆÌ×cWzú}N5ôÊ¿uãèòŸ·¹Ëâ…—/%‰¤Eÿ±Ù0»YÈ “EwÈÖµYÀ½ µvj€ëz¶_=âKÆWO{AÓû‹oïHóŸãÝeçú‰¤ ¹ññçÓ²Á£AíÅÜ!eÜ%Q#.ú]Íaçñ½ÁY·e«¥ÿ1¶êq?‹ gTKš¯øÜ@Yk?¹û,¯{ÃeÙÐm¹¥ÝÌ;¤¼­gJÊ7øöüïä“s§1IñÜ?3tåXhóì†EÉHqÅ9CMÖlÄÎ1˜í2N7Ɖ¿è걟TªÚnÔ·Øl0Ñãî=Ÿ†SÅÒº/95h.¹r¾ò°]“úÊÙ‘±ýk‘ËÂ90œgüS¿Ç8£g'|Ö‹ßOzMí%• Ç"Ïym*«%©mÒ»q†ÚÝÚ¬, šK¿˜p>v£è  â-Å8ºüþZ(ÝÞLÍì'Wv¶RlLΆ!ã¦o¯¦%&JMø¶]àý±ÓÕ9§æð{ „ŸcP¾*§ð9<œ>9o?Ñ ¯³Aÿ¥ßÃùõ´äbõÊíö¶‡c<W ÄPÒr§…?¸øoYsäž'PÞúCºžÅç ÖÇ&ö>²‘4n¦%—&\Úg<Öœoª’¿ÜTΛº7¡fÖæÑŒ t>yWbwÏ0,¨ËC ßo§|`ÖŸ1ÎòKÕœ.’¤w¿G»LkäÀ‹eNvm§%ý½ü”YÆŽP«Ù˜&ë‹ëY±n/T-­XlÉx@w%<?ßäçĺüÆ8oœܬÖGI¦ÍÝйnœ¼_îUx7-Ñ–qjçòÅtËææ3Èìe.Gã·ן”;.ß§ïI”.Ï7Ääý87Ñå;ÆÑëßgÌÅQJâÿö¦™k˸w.½©Õ@-q­ùFJ†¸ÃÜK:œWM#ô|µŒ8o¢çôR¶ox_ÂÿæóÎ+Óå;ÆYÕl¾K»‘JBço9 ¹Ü-9iˆ– ÍIR õ‚i!ïr£¦’óqö§&“¸ÏãGœ2Æîü{¡ó؇~~ÿwã îb:‡ø*ɾ½eÈÄëÅ»kɰg1öNÏ| äÓ3ÅÓES_ÿÐ~×—qêû?Wæ÷42ŽeçEËüÄOÔûV(]Ô¢ïz•³’ŒÙóbÅeçès`ðˆ…£´$÷€p@,‡k{Öâˆ8…p8?÷Ý7 J鸯Ÿxnuvå†Nñ‡Iø>]'0.=Ʊ&ìÔ*‰á•^™‘9î8Ü£ìT-©X|jÙ­=rX‰³÷¦ß¦Ê/œÏF×5Ås8Þ'&N?bkþÇOŠqˆp|ÑLIʵf9pæáà¨õ‹´dÅ€ó¾ÌƒÁÊ Ã´y3Èä›Å§¯.~nœÿÅû]ÏI(×Ùœ­w)OGŽq Lý;K*`œ©w¯Ì[‘s Òvß %ÊS‹ò6úû‚pû aSiôu„ÿIÙûSŒçÎûÏÎGÖÕ >?gs»-‡ñùtß4îÚŒ¥ìÖ’ “’’ôm'œ(ݧs€/‹ãº­JèÃÆ-;°H¼°îÁBÆm{"¹[}”“6°?»X>¯ˆÝŸÞ‡° Ø\I¾ÖñÚ—”Ù9®_†×’œ‹-×Ù4s‡ÄÐæ¯×î%ömÿºå—}ÙOä°rn&_7ÓýËA?å³ãÔëÖ °TX|~½ïåpª^-¤òy-)Ø»|ÁÙSÎà9%õûƒ—3‰ïÚˆ.}¬ÙsÀïîËV<ôp›éy†ã`Ó~£Å8w}ÖÒOI–¾\<Ô¹ ïÛÏñ²–L÷R‡;&8‚p˨αYì^°û# L¯£¡‰]ÍDÞ5Ý×ø$®ÿKs¨õ¾J%G–×ûÒ[IjÖˆt9àu§SZ­ëZ¢?D½%(êµxôuÖ&áë#Îk£œZwƇ* Ï<ºÕŽyX:$/»¸u¦+pþ¯®n0Nõ1z½Ì”$ºFT«¨Š¹PÎûsÙ©wµäEŸÖßoá¶b¶4˜BëãD[N6èï¨$CümN|n’ ÞË›Øø\Kº¯×.š:Ùls:2“\ /'%Ràûí³›~¯0[φW}Ðõ[ðŸç)Y¹Ò8¯\W7ÇO7ÑSUá‚S.4ýx¹FÙ/Z’fáy'ËÀò„k&Y‘ŒGiÉÎïú±ób¿¡œ¨×¾MσWãPÞér}aú«*¹0Y¿s¯%Õî’MŒ”ãƒýà­]øÁm' CÄ;T\ÇÒ璘XŸ<Þ«!+}’ €•é³¬É Úoâ0N#ÛõÇZ ƒþHY?:fù™GE7»K¼^ÿpdŠ?|ŸV°$;œpî)ß?à<+þ|~ož“öû™sqw´îQø²)þc€ÇÆ\8÷îò¶&Ýî’¨¶M¥vûƒ|ù|h*òVùy?ßdm†ü¬¤+»ˆq2s]žu=u€ôj{ÁËáh.ìlÑmŠù]’1¾UÄñ/~°EhKs#ˆn["†w¹¨î·ÌL:?YuÓÏø½¾¿eZ'{žÛ×AŒlÂnÖ>’í—–ZVIÞÖ¯ù²¯2î› ¬˜ét— ¼’¿+:R±»& nß"’ìMo_¦[eWq<`ü8q¼f÷$ôÌ@¶ïEÏCŒ0Î$Ï– 6% ºk&ûra‘ÏÖ»À»äZµØ#ì£Ó³ßæ—¹>I|?´Ov¹­=""Ï:/Íaû7DþíO4Žã´µïZÒ³Ýâm[9ÚF• 7l=[Íw—hb§»Õmì­„k8M&Îãæû°ôœ¨=/3_óš$3’’õcãÍ79ÆY|K‚ÿûÉ¢e^7¿– c[|Ý8ý.IÑnd8¥ñ±Ù;ÛDˆ÷iiµå|GvGÒ¡wᤠ-ú3Î*}¾ŸƒS½ÐVûÉÚçæ·Ç¿ËYǵ¡óï’¢ç ³½[9ÂVM• kݦ:ŽH¡·»0“ȸ¹Rñ^h׉ŠSV+´Ú¬~ªÏ8Œ³d¤ðÉã¶Ýô÷5ɃžÉ×"ÊÆÜ%î6%}×;çoÒú¶ï·Ò|•½w[$Ö'ßo§÷néýF5Æ)3:wåQ‰dî\cßL³?ìË´ù½I¤ؘ«–7Ù¨¾Kf?º}¾Í%oè:ü6ïyì|Ï„,005ƒúÂu¿¦ÃDž¶{¡þĨ¶ÏÙ½íAlŸÖ¿ã,Ùº)صE"™u£Nëc§ò Ä]íÛ.ó.yïØ¹Ç±r± dAç!Œƒì |}!¼Ú&ÌÅ8ººÀç­Š±¾QR'‘ôƒäqÚÜûšãHħAÞñ2¶ßoœË¨Ë|NŸÅ ªš?ØGâ„›±yÐÌ {ÁÛWwɤ´íýìýÀ,3 gÄɸ˵–i.l¿Ï ^ ô³½·ØÿtyŽÏ«XÞÉtö>]½¾íµ/y ¶ýü¡C¹{ÄyÈÄþqµü ;´Ù‚¤/“È3&9ǹÀ¬sù‰AÝCaîÛº}-n†Š÷]9O’Ïè}KzÏ@qt_o£}D·íXû:„ï Y•Sçù8³f¹²frHí¿ »SòD26û¯ÖµÜ!¶@H”`°¿¿zlh§’ù'Ý/Ï /¡¿W°Êݦû'Z|¾µýÈ-îî%Âê·~§ëÐ|¸¬Ù¸V÷ÈÃz’ZSŸyƒï‘KU‡¯´þ/“]¡çzت‡²{µ~b]—}$í¬ÛèßËöo¯C¾õ˜[ºÝ#¯ë­zÞý¬®7eŒ$í'ÿq{²;8úaw‡ž2Ð8£ÉåA¬¾×=ÏŸ—VïöÖg÷’©½÷J\=¯ÃÙØÉ…]%÷Èu«Ç§¸‚î¸n»/ ›§l4ÔÍèýn9è–K CÅýÓKÇ—iVé­x>«Ã47¢Ÿ³ã\ü¸Q­ØK.b¯ƒl¬åeW›{¤[Ê®ZíœÙ= /ÒÆ[ØiöÏ%2„ ™Aâ~0¿ŸÍ¹®¥Ïå§±A‹ý›Îì%•|c‡Õ[rÚ;[õ8åx\>“¥Iˆu‚¦êlh½6˜»—|òXçœÎïÝòû6´Kšw›¶µã Xo0Á&7‚®›Gw]ãÐ>òîUæä㇮ÖÛÓÂ\}îÉ´…Õ*vw¸xóèÜéá¤a¿Ù§nÀ÷Gù½ŽÌOCòÖyKøß¼Ÿõ9;DÁöO1N¨×÷ýW7%á]õ(¸C>~o×+äI¶–®µ‹sg÷"Èö‡ÒšGǸ@¯µØšÝ·3ùê|žÄ×™|^¦«ŒÓ¶éCyï2û‰îøüÜJŽ×«yõèôt{‰7 aGl yF® ß8΃݇÷ƒ‹m™‡^øŠ}…ó¶ùï'tu€Ï§ãë~¢Ûn1ÔÀôýž»«Eß#Ê ¢eGªøAû§}pÆIÞ¤4¯6àƒ|{ås´ûÝ‘p"ùËÍÌÕcÄu9?Ÿå÷VKóZõÊ=’ ·kŽOÝO5ÝUdÝES†œÐ¬¿Gf«üÒ"&úÃÜÁƒ7ÙšDèþ»¿x‡î¿X=xÄîcI(§ÖTœèêãx/r¨Æ÷©ÒŠj}5`yëÌÊ ûî‘Zå[-jëÒÛ{]Ú~Ožö@ØÀ_/=G”ˆóW>žðý&Îw×Õ Æ‰»½dÕHÝCoz÷î¯/¦Âiî§^ƒrfèùC¬}§ãG'n…‰'käB‡ûwFz™Jsšù½!¾^oÉ'å{ùÁ8/¦ŽZ¤·ëqÕý°Bù7Œw˼G>™÷°Ûß^Îöe';ÉŸÇ}~À~¯`ÃîÙ?o ÷ÞJ¦D^z¨o#Ž?ººÁ8ã–ï¨é-;@¶Léûìl€W=žïuçéÙøážóžðDÀןHFÏü²cð ˜Ù÷°÷Öš#€ß+c׫ùôË$ü¾4½·mÃÆiú»‰8Œó¾íü÷ö“kÂqÚ ÌÔv°3zvDµ?1e½+\nY×sPöx¹Ö¨UÜ9? ûŽ 9f|xP[w‘¯ÍÏ»ØþèOýFqN„ ™’8w?¹¸£Ã˜qK5°ªLÜ¢Æî‘YÖýN¬q‚f·ô/2CÚ¯)žë3ëÇ=7~ÞÈãÐógzÏ[‹ÏÍ®74ÞO4›_ xµI7.©‹õî“vUw?« t€v]»—!–þÄ·MóSUWŠã]‡ÙÀÆVó#g5x%¾n]}”Çñ(¼UÐâøDÒèNƒu{Žh äðç?ªW¹O„Õñ•yöp( •Á§®¤g­ÞÎ8Œ`÷<@38«\ó9Îlßõ„Ÿ·í—ÞÉìà`¶¿ÉÎå0ÎŽ øÛŽ}ɇC›æ^Ó@ß{ú©}Ÿ×F×°‡KÛ6{“ï`k²!jZ¹>~ÑÏV] nê<\wËЦ ÿ&áã8?ç¥ûIô{–bzÿkÙ]ëÓú©·4 qÕ+Öø>É|;KÙ¦wšQ4ûÓBÏOýØ>œðûÝœÏÇqþýðõ¤®>0ŽvÄ+¿è}dÑŠ6:½Ô@«/…îñ­ï“¾vû®ãã\´Ü^Οè0ðuå°âD÷„Þ¦ölÿ´¿xo’Ðß› ‚ñcgž·˜Ãö}1­Ÿ}$³úðüÇ•ò¡^ÿIÒ1]ï“„ég›trpå÷bˆ•içÈ,W9[WÇ žO|þÍ¿z~Å~¿„qÞ·è–¼—ì°è óAø¡ËËþ÷Iý‡•$xˆ¿ƒ¡ë?¸å;ð…gb¨<Úu¹ÔQö¬óyþÒ{(lqúë&Þ{HŽÛW\í˜O>®ñÕæ>éé0$ ¦µ7Ð{ɵ¡—.‘ö— ävGýÀrœ‡gt]FóL‹Ï齃y‡LÙM>GÔ®õmp>ÐiÚ}²w¡ÝÐ3*Ø·í‰k?;B?·ì{"òíùëçû—ô^±9ÄÜÜ0ѳ%‡õ*<’Nß°ªÁúb¾ûLô±€|Ø"Ù•é|­ù»î}àVÅ£w ú“1û7<Šž>ø:ï¾iqȉæÀ÷ù>œçÏû.š§Óó#ŒóPåØÏíû.bµâè|‡iù UΜqvò}räà˜}Íß{ƒáªo>Uj†°ùøàë7ºOf<¿´ÝÖh»çµ„ÏŸJ÷{)ÆÙV¹îÁh¿]¤ ɹºqQ>Üž ïk6ÿ>V2$yt9/˜©;Cèy©?ÐuÕpà÷¥2RBÊw*W")íº4íõ'q>FßA×+rŒsTÝ gÞ;É©iŠéëóÁÃòÉÀ5«î“fkâ[¸ƒƒu÷½“UcÄ{+ €%ãÂ÷~ÏÄ­þ+³;³Ë€u’cÊš©–?ÏÇ0޳î—¤G¸t¨«2œ¥O;ºÿqŸÐúv†µ’‰ƒf¤‡²ó4Oàûd£­o<6ïCSöÍ1._‰}žØ9ÓПîiÄaœ —=ê·“4mòlxµÔ|¨¯š°k÷}²èXCïÕËÁ ±Þ˜eF“€v§Ëï’ÈÙ9‰Lü =/ׇC“NtXVžÝjþÒ¹££8/ÐÕ Æ©9crÇ¡w’€A~²Yù Ÿü¹¾ÙaŒSoV›­Gô¦ïäS>!ì\Ú†Ÿ «eøt(ûœðß/6o£þ´°nyv.d -Œ|Í*,`çƒg³ªÉéÛxbè5Áê{z>¸–ßærã,9¼ÌnŒ3 lwfìôç£È„À‡ë’ ý šÛøz‹ïX=§1ï5ÐùY‰$ÕÔòU‚ÍOß^ÅGÒ»Ù›N§”'o^ ±¾™¹¤ RÍÔû$æí¨z_·º=OMÀÁb¾ói?àçãºmZe_qËû5½o4˜Íߨy ÆI4Ü_C¡ÚNžd |¨­h½¢åõûÄ®ÿ—^îÛ} «¨†ÿÈ*c]Ÿ°{+–Àëžÿ^‰ï'ð{)ô÷Gt?AŠq*µÉ8òµãv’]ÿzɰ÷ù°Z10=öÑ}âÒìEÏû3ý€þl 1~¹êζö½»½—ã,öÞ¯ù}ºßLãÈ1ŽÃÉ·]m‚¶‘KµF¿j¯Ws]ê÷ÏüxŸ¼ø¸·VÕ‹þP#î·±>¡äK#›ÙáÁóP‡sÃ-ƒX½û‹û0ô¿·€ß\®ßžM ½ÿÏ&=ö~3…ï­Œ°÷ƒù‡‰ŽÒ”Ö@zz (ƒÿn¡à^Ú¯)öo0g¤Ì3XðB‰d|kÎ+Ô¯CY¥½šÔÌÅq¹¿¦ÈRŒk%crý™O]$cr™ü‰Oé/|ë„RL.ÁCX`AÞ™œ9#x£k˜—ðße»þ•_“Œ1®J1g¸7ºñ_x53»„_¼ÑæŒÛ/¾ÂZƸV2Æu$óF·þ¯&é?arÆõ¿ÂœùÝ÷F…ÞNoÔgïG#|/˜” (LÌȲž~ã¦eºþW™\z¿°®ãþ7Bð±S¢ 1áŒåÊY\u¨ŸziεÀ0¨G=ÕÕŒu­(ÅsU1oõ?óR06ŽéŸxHIa¹*K1r2›SuÁÇŽs#_a-óôü»Ì¿â^»1ž«q)n÷6ù æu ó™Rþâ+,p#ä¿ø|1ž«Šñ\ÌWXö'ÌkëÂãâ<×…ñ»7þî ½ÿœÞhÀþo}LÈp”¦Œp΄ùŒ2ÀäŒ,'ì£Ržë…&p&L0™cYB ¬ÂÌ™0`œBîï©dWΓ2N¡%Å"H`¬ )c ¬k©£,År8×å·®Çx:¿ú­‡ÿÂqüÖÎu,+(S(øí ì îu,°\¦NÊ -¥EYcÁ)Q†Xt TJ†Å§Db*PE(¢ eÄ<׋OL]ÊóXðÞ“c¦ Œ±HcP%(9ãr¿usmlBý9‹"æ¦÷¿+báͰP3§ˆ¥qO­_xj™7#L×ðxÆc&{joÆ›ÁòϾ~»QÞ‡ì×óK÷ßÏí=y×Éûíÿc§]oŸÉ;ŒíŠê»ëF{ëzûêfw{Ìñ°û¯ûì² ‰·&{o.ÎÑÄ_ï* ‚åJá[dŽÙy3TµF„Ï'³ÌaVÎ^/nʽŠÌáãNYGsîË6!~ A("˜Jáí‰=êI©Mød™›¹YPÍÀ‹°ªV;ˆ#Bëj×bÀ„û€!v€0!Ì~ A U Æîp€0a@ü¯ï%ó'Ù/ucüq=õÇõ”-á·s=¥çEÙï­û¼îÓb5Ùûî‘ÿDî†ýµ.²¤*.lBí*Û¢Àˆ€û€!w€0!ì~ Aà"ô²ƒLð[ªù°#@a° ßCá¬â… -Ä †Å ÊCã* ŽD±šÖ/|@6P´*|‚@‡ár's/¬î&œŒ×ód3'£Exa™‡Ì ”L+ˆ½ð2V÷e{€’y…'H‰Á5?Pc€­À4d§f³ð1/¬S ¶ùîlÓÏxÈd/¬ ÀÔX&,?Ð`!8ÅR0Ï+ûú½îÆê{±SÂfV߃Õwà/ÝÿÉÝ÷[ß{ì÷îM`Ÿ‹Å¹ Œ5ÙçÌp#xKüõ³Ð#¨$ü‹åÀ€Ðz Áµƒ(0"À> îÅ0!Ìþ*~3æ˜eì({ï Âíö~•fWý²Nt3-ï „Þ |µhåÀ€!ðÁ^Å3ÞkæYTªðs†ÃÉܱèj ŠÄ€ ã S Žh0s-†ÎÁ®ç„ÿÌbì½)B?ÐÿóÏš°Ïü@ƒÁtŠ 3 v™ý4Ù?«ÂÛA1Ì> Æ@;@ ˜0Ø~ÀÞííøãzï뽄ßÖÞÓ‹Û‹±ß BéÓ)ÂiDîžu‚jÁÚì3˜C Dh­ ô¯$!À6P ²¨ª8´µ¨lˆî„Ü)‚n Eà]Â9kA Aø­ÕüÚå@ƒa°Ðb(\U¼³A Ã€¸Cb Ç°¸@14> Æà8˜ï˜ª9g@…a²ƒ(Щð=ˆ=†Ë#LÈë9¸™Ò*¼³z ¥$a0m \x¸½×ðp{AóA‚HÂàšAhR¹“1´dPïlPxg]@Á¶\ÃÍm> Æ°;@ ˜0ô~ ©â5aøKÀ)Y¸µX. ÀR°Ï4ûú½îƪϡÝìü%;PÞ}UwÞ{ÏÝèyµÿÖNc¿#»¯$ˆÕ`= È!Ð TN,3mmö99d !³€ Ð!ln Dଠôž$!|6P ¡¨D;ˆ#éj„ÒbÀ„púuŠš…«V‹°ºD`cì½$M¹7›…ÖÜ쪣Ö%œÙ:Ù ”³¨h;ˆ#‚íj„ÛqwÖ…'ÛË®át%‚ne¯ƒ"ì„Ý)o Eð]@ð›Ah1. À X@è0n ÄPXAè1„±ÐcP< Ãbå@Ï^ûÄ>Šiøð˜A@xj]ââ @‹ä ˜ƒæQöœ>ÎÔ:ˆUqÕª1€& ¢h0ŒN1æô«®X']ýœÏïuýqöï]§ý¯ÑŒâÿga4ƒÐÖd]+È ‚iÁDÖ-€9J„ÔZ›}V÷ÂêI¬ ”‚ë*„×¢Àˆû€ºÁU×¶ ö BíÁ6ƒÐ"à. @È- t»(x+ˆ-‚o«æßŽ-ÁÊáJ …D€ÃáI( ŠÄ€ ã S Ž€äA Æ 9@ èUø”{Ý(Øûဠ(0d: ›(1pVú¸º“0ˆ6P H/Pa(í *œÝ¾k8»}@Å^‡A ÂÐZ€ƒkA Ã»Cl Ã0»m½†¿Ûò3îl§z3-†ßÂ:,7PbX‰ç]Äðw·ÿÝk³ßâkœ×Úg¿öZìÿrg±ŸÓ4ŠS„Å @›ÈºnÇ‚µX·r”‘D€aò€$ÊÊÁòÂeQ`DÈ|@ 9@ ˜8?Ð tN<3-è „Ð‚@‡0ºoãa4³÷¨! Ò‚@‡`ºá´‚Ð#¤„ ªTˆë„Ö)‚kDp}ìó®®=W†ðj^P À:Ù ”³¡v%‚m GÀ= !·r`@ؽ@…ÀÛA90 ø^ Âޱ{ŽL\(0 :ì7P`ŸX@è°OÜ@‰±²×;ÙûmÙóü?Ð`xœb€LÀ4$§&3-†Ê, f˜“?ÿp£÷öš~f‡Ýèsñ7¦|M$¾·D>å¯ÏxÛC‚­Î]emÁó«¥Îë›ÖÜò¿Pn¥Ð+D£Cc‹ØljýF¯Û>ºå $÷^ÙR<ûø¡±4¾evÿšOOßš~ý³:%îÕèK‘¹=jÝÙ#Wô'‰þrœóÆÌýYk?vI¾ÛØ©uRˆ¾­QåC­J+ÃÓix,‡x?ežtªÅ™¢Í4êϛְCÇz2sçä¶O÷GÊý<Ǻw¿wÄ–¾Ä}‚¼—LJs‚sö·ì™à’*µæ­Btα´Ö ÍA©¤²¾ÆDéó§‹b¹ÒG;V´›B…/%Sô=gÆÏa·ÁíuíQçô‰Ó…Rçô—ü¾cˆÞÖè{”–|PoœgþHšûÌGÏ%w(ñ~ïIñþ¸ª=ú uèèpbÍÂ~«¤¡–Oç„hšwØŒà€ƒÒºð݇´5†S‹âW\ƒn}LRÖyuF£åSD/æ`’ýµìfT¸ýµû×O±­ÖôÈÐÕ³C´ïÎØØ®™{©tVê¦É…™Ô{î¬Æó¦ x>Mk¿}hî×C‰÷‰gþÄ ÃíÕÍþxõf«Sz³}$wÞ !úðÜÖ<·n/ÖÖêÑúChñîý‚›§I§=†É+§O!÷•w~¨X>Lô¿àßSë×Ô.>ïÉá=¶÷ò^ vû'u…§Ë—IýÛìÞ¥^¢ñ}W¯m¿—F=£˜Ó¸÷òRó†w,š*)—öùìÄ÷f’=®¼·s è§=˜.{„UüüÎamÙMË_–ffÏ|äÜÕrµÜKŸ¿ÖvæŒ2iIjúg£7å‹>ùq¢—²·èEì#ü ‡â=IÜ÷ÛSôðž7ÎÉYÛüý‹C’¿R†HÑ­êî¥ÃÙgmOù³è ûžý‘ã$Ù‡ñþ7žŽÑ5ýE¯xÑr2îå‘û¥ïËêÒ|V˜Ï‹ç|7óöÈÖÏHïl_—ôeˆÆ\ôžø¤¢ŒÊ¶>7ù“ C‰Y‹š}0DzePß„. †Sè‰Çz3‚ØT4wýR?\õ ‹¥Yí[¤ý©ˆ÷UGpγwë‹‚»íÒ…ú{öþ*DGLJZ.#î .z(ûH¼iqÏ׃¢‡­C¼_ˆ÷ÕÆÒe/dUt‚âˆní'sÞš'Ù¾Á¡ ŸúÁî)Ået~ÐÛi…G•çOjt‹êiõî±¢·05Þ³ÎûñÏÇ}9¬…è®E]©t>8çÔïܯš+íWç¾_‡hù¡ãi—Ö•‘®õ¨ÍçÇdÓÖY)½æûrDŸÍDÑW¨ÿ<¼Çæ‡x_’Ü÷¸N—7a÷Cm+ÏÑáÞ§3SJ|oïVÚ¢´e½2.ÚËèÓKúMcŒ¡z Ï6X0^âž°I´£kfïIr¯ŸÜW.{¾„î§½L8gp»‚Ûÿ¡›.Ýq øDn8D;Þˆ^|É\FoœÞÝ­Oɽ҆ú«j´÷çÅ÷…ܹ–Žåîì–(zs‰.ïy=ýÓA´ä\1μÏʆsŠBë¤&>!­?ßò;ï7!j~lc¿2úZzê/ß¾M¼_Ñï”=ȇœ)')m]˜Û÷É„VuãýO²ß•{ExOŽ çܸçt›£“¤£¯¼ö­6¢ù²ƒ+ï/#þßGÐåFÌ€a’¸wj¼˜ó~4¯Ëäè¶ûú _䕸ïíûáþQwd ½jܳëÃ9¼ÿjˆ”RY¦F=ß~¹CÓ2:5-B#–å.®Ï—Z}àÚ–;xB¼ï_ž¹Qö&ÉžCÞ—È÷NçhçÞ×i†«­tù¯Ÿ<›˜¦ŽFÞºt©Tô!£9;6Ooxaªt’=ìÕ@EkY‘[?â^×>$ûñxŸÕÙt¹o°joyB½#:ÞÛ׎")mwõS‡idî'*Ž”÷eÒVãßtª”qöÏ…¾K“·thrË‘þ$ûxoÚ…t¹opç›…9“ ¢‰½qrg{î©VáîɤäÅ›*î¹/Lû’'õmVZJcGm½\°+ƒ6¾ü÷ŒÓǦHüþ͎﹟_îeå½åçÓå^k¾?x/§çðÇÇ<²ŒisÞì1(}~q)™Ë¤×3Doy®4zËþΙɣãÞÞ'™Fr­Ü7Ë=±ÇûÀ+çç¼ôòs#k§ZèØßNÌù{§0}<»Ó´ï<¥ÔØñüˆ³M³¨n».Ï¿Ói¼ôý‹O 966îÿàýÒ]â9x|ÎâÓ'ï¾ÚÓ&÷šWÎΩ?àa×”Oˆ¹0åL|öÅW–’µóÊÛ®ä©[£“_„ò'HÜ+—+úâuqÿƒÜ/+ûçx/WOÑ»'zÅqÎã›Ý[ói:ÿb‹&cÛ‡)yòð´Æ¶R|eíòÂQ"ßf©æ™‹—?_0Žd¯ß—ÈÝ¢vêEÞŠx_fJNþÂÂh¸W¥r~pNÏÊ`ͦœ–ã»àçùk…«ãcK鶺-þÚléhÚr`Ð̃ó„Oa ÉÞš^ëŽì3´Ä}·gây“·«Îiçx-=žßáYb- Ãº‡éoë.ø¢g)-[~é‘Ë'³…Ÿ/Oâ=îÙäêrû›šMšxß6÷9GÓeÆÿºÎ|(£‡ðY´áóSÿˆ®Gå…ŽŽ³«Wã!aª“=¡¼×=¥ôÊÊÛW¹–¤‹7>vò݉÷{'>GiñÜU÷s_[wÑךÏÎÙÞ5ؽ@ìê·‰)LÙßxd5,¥Š>¨ ®2 À4)iÆÛÌ‹&Ä{e‰Üû_a>`?v0ÞËû“ùüàœog¥%d-¦‚ùó¶»'‡iÓk +rΕС®S¦øR¯ö¾sßT.é~Ø:½Äi  º­=Ÿ¯1„¸§ü@ºìQ‘{ç»{kÖ?{†ŸcÂ9ö”Ê‘W3w>¦¢V4ZB/LÚQœ:;ƒ¸·mªTñjâ®Æi¦x_£ìa—ý©²oFÞU{zm8§ß€Cß)Ö-%îq Óª>ý.7ûª„º¿xþLë6…ŸÓ"qßæÚþðŸ'nïÒ‹ä~9¹ŸWη¼ªzZ\8‡ûv—“ýÕ%ýæ‡iaó¼KÃ>)!f h©ïK5+…©»'¼Ãò0]#ƒdß[rºç€¤Kˆ?nTõÛúpNÈ=¸î{¯®¤¥³îwÔcaZ3^½oàÖÚá[Ú÷¥½©Á‰ÔÅ{¦HÎ_óöãÓó…‡iŒÈÛxJ\ýö¸½ë‰>û[©tdâȶÆâþ὜óel›7«¨²ó·_~oF˜µZ=%´ªÁm‹ GûPÃ!ѦS‹ ¤µ;Nå_Øð-Ê|¼Íò+&šy`ò—)õ,¢ï.Iôý_IO¥.s×=BÜ¿-|à Žè¦Ü·4[ã¢euÞ;Õ¦zÞÔù'×”Pó{Æ%ÝýØtÇìá»ò¥¯ÎöÚûl«)Ÿ2Tôö ‹÷jî|÷Zž+k@Ü § CL5ò=ªÂ9¦,9»ê¹ÕÔñôSÛ3…©²§uI •¦ÌËy²v­2²‚ÄIk£¼«ÅÕžÐ}'¥w­oˆ÷ªÊÿÊq›šÏΙ51㵃·®{1Líž.úöýÙ%ôƒþ”¯H1”¶}˜p±¼$W*+ص.'îe{e?ƒì…ÊÈ{f`ÚÔ>?y\0áœÂûUÿ´m-Õº2òÓ_Ó¤ö-k'•Ðü=ê‚vQ#í{¯ç¥âe£%î/4‘ì»–û!ùß'‡Ó¹¿þp:ßK=~Ò?hÃ9¦ƒ¹ƒR¢E¤gšÉ•a­iâªa(!Eÿ–Æà]£èp­ü©±µ™Òý•ó9q²ÜË\ýï¬Ôÿ|2Ô¢; _;Ÿœó£«ýuƒuôÚÈòþµÞ Óê–†¶[ÒJèJKU·cY£eß@Ü¿Èýƒ„?Ãÿ{Qþyä~ݪ~kÎÉz¨¸dÙƒnŸ® ¬ØˆßÛÆ¼'>lRBc×Ô]!Ê7z©WgGûÎëÇS“=-ž¬è&zp»Çïù:¨ª¿3‚Ûwtï>uÛ¼I¦w=‘™ÿbï< šÌº~¬ ¨Q,±ŒÆ†ØcCDÍŽÒiF ذGgtPg4vƱ`ìqlØÁŠŠ>±£ˆbÁ ãX¢¢bÿöÉ9'\;ó­÷Þëw—¬õ_úò:ÏNÙ{?ç쳟ýÓà|ÂŽ™^dÂèf{-7 ›°”ÌÖÃÜ…ñdlô“p(»òÙÓNízŸ3ϯ¯µRæk'ÖùЇÅ÷´svÀæ³²'.àÕU—a ¯fçˆÿþî!ì³üxdÙwA°½ §ôãë_s–ô>ÏÜx½›Ñ}Ê÷=Œ×;·6åP®Žë«YO;„דîÑceô$Ûƒ}…>Í'•YcéÉ⸻ŸxÂËQ†_S Ì÷-þ½Sÿ¢uÚùŽLõÚ ¦Äd€ÜÕEë¯Î„Gk]ºÝÉôsö8ç¾n/ ÜÆ>ÀyÒœcÍç¨ç—©ÐΧýÓžÇùì¿f¡V[N ªujò¡™Ð>°}½EÙ8oYHm˜{ºÒtO(߯ñ€‡@׫=Íu'>ÇžßOh¾§¼ Ú1 +é-wCáøß>Ë4@p÷þ5jŒÌ„Õó·ïŸ½5ÎÞ:±9mN„ð‹­Ï©&óB]”++Î禦‰=--Þ8•¬ ÊQÜnÛzãÊï'M­ü2!~ž¬iØœ0ÝÞk©…øNs/Ž~©{fI r]Íû€ì'sw¼bÑ @÷l.<«k¡—ÜŸÖÝ£+_òLM3@ôs‡ø6=3Á=¸zŸ „¦&p²Zp úîûË*¨)ªXqÚ¨óÜrÜ–1aÒlóÑr«j°<\ $«5¡MÝÕfžš).ÐN q‹š»¡“ Le€Û»ÛÞžÔ$ž[åL©¿6vþìØ°“ÏH3göûc·9õgqZRn±©yuÉ`[KåDÆ¥ŽÏ_} žò\EUò»w]pÚePnª¦VÕXn‘ ãÖD¥¿­›ÜÉÂ2Xàë&>טÎéö4ó¸èûôƒâsÙ¥x}>Òf/¨ðt×ÄMXÿÇx?ïÜ+ðBõ`Uüû¡@çÌöe<¾à\hÊAW˜9é7Põt)§ó…û]—±¸A;± vÚ\Ü ‹{Nkç·¿—^qÃ÷^ÊïÚÌ}¥Î÷õ°þd2{(›cì ”Sâœ?Ê×±”ãêÍ8O”û B;AO»æNHÙóêß„ÍÁûU—ƒzͨögÇMOb‡3>ÞüþÑöà÷kÊçz›¹ÓyRÜ1<3skJð†ÑŽÆ.¦CùÑqÐxR÷wgg Ë¢®½û_¢Ã;íÝ{cu…!åW(!qÊÎÞãæugœ:gàü-Ι£ùÀ‘ÍÙ¦ûY-Ú¡üò8XÓÛ·rsüÜæ¥zïŸÚæ Ì\A€òápXû:M,#ÜÈX±~ºÏóþ’Ï|=é¶/qÍóì‡fþãÌ8öhGf¼>-á^Ðõ¶îmz:n˜Õ¸2gÙ8c+t›Û9|øxaæcÕ)¹…?ã%tƒi›cNf+€ó·x~ëþ}5«[ï(O'¯ß9ú•­/¹¨vã:/=ob…è?/ÕÕ"6þ>Dº>RàëÎ]j¼¯âò;w{›ùèü{)ºCÆ8×Û/Uñó2-tâÁÃ8¯s+µ$~oý /_†œí]%P~°J(¯‹¬PåC'ùEñ²ZwH©NH?^°ÂØr«uÒ+ó¼qîÅçKÑå‡ÄîÁÓ·ŸYˆëðŸªÎ¿ £ZɬaDeË=óÛû ¼A÷'a¬žhžÓÏç›ÜýaýãËžàü˪VÑõ±íÌýcZ•¤ ñ°dl]õªeH\ÝÆýÍ–ËpóGë%Ú2n—·Àù¡¼Çù‹œ—Iç™Íë":g›í_Ðή?}¼Ý¯ÅÃÙ·ŠpÝÚ&eÜâ–‹/Cò,­ÛºcØ\j¡¬Ùö‚í=»wù­;кRw³^ß6Å ^wÁîõÛ&p æ—OU¢Nº ›”]1o$Åné«ð8¯D;Fé7ÐÙ¼_0Å^çÇåÂc×ö3ÏbP^†ÙÞë]S?…°9çB¢¥ß Ï»á\ 7ʯ5@aFÔÝJÝ.ó=#c¨àÕ~r£N¾î±OÞ—ÝÇÇ@íqoÜ<¦F˜ù |Ý@9Î%îãyhÇ„•lp:¬°yÜj‹È*[Rû2ˆÊà˲ ×{ÇWÌæwûÁ½y·¼<òaë§þÀ× üOÊÇp.ÁUËWêgöɃð ¼Þuû^4èG» k¸âËõp¨r7ÂB_=¸$8¿ŒÏ¹7ù5^gPëÜùö‡@2hýºM  t>ësаbMm°Õ¨¢îu¸Mèß93PóÔ~ü:gê·xü@Ë]ñs¡L¬;ioGMgß­:c¬ro8„¥ÕÜ=Á~¬@÷§þìu¨Øû3óãLþ‰×[ôD:y[Æ!8sŸÎ 0¶tLÖd€ßñZ‘7 6)‹Õå ö‡wAeŒýv„³}B(«³Ü3óbéçGëá¼~'û€Ïa˜øvêÉáG 0íÍzÛNƒ3ÀjÏ‚¹«T`Û`ê¯ó_èúÉ xýžrÆíaòì*…/ß“óu;çnÐz µ£E;¹Kj. «u”w,†ùá÷ä”8ôÊJyLhqÚÏe{ø=”î«8Dàë\ÎçãüÎ æ\^/â\i“£wË‹®[½:#ß'·vß`€íí«Ýh’Cn•[¹=| ãÒ8 œk̹œ£Éó8çĽ(—Ü7~”‚íh}?í,»¨K?·&ö=QŽY¾À¶?.¨œñ;v­W?zU¶’„Üô2ï[¨?3”ßoyÝóž‹ó–Eâ|Eû-)½%pžWˆySuºbz¯‹ã%p‰µ´}ü¾?\nHþ…ÒÌs£¼ÐAÐzN–29Wi^?¶wŽyôGç÷rÎ}-~ÿ¢3×ÔðP‡îV\Ÿ¶ûÂ%HˆÌŒ*´ï=Ú‚ze‹¿øßýèQñÏO^¬¾­Õ¼\iD°Ñ¿î‘»ü|K²Þ)z¶o î¥íüDðÁèNMÆ@ójúÇk;.õà~—jöcõ[AÄ~8w›ò"Y=OAùs&E5û$&ÛÌ>@ë-ì\í¼6rûƒ‰Ç!½YÓaý pùN‡XÃôK°¤KÓÙ×]”pïµý‡œ%þÂÐÆŽå—ó¸TjûçÇx±·ù|Œ¿Z‡p/‘4h§á‡öÙ¿—OdÜ:PîÒ%8\§Lh7<ãÖ½É fu©~¯×ñu>­¾2sæ)W»‹jG‹vž%ÄÖ®žšö=íì\Zà蕟nYU¼–Ã2›‘al"h6N¸­p³xÝsVx¾æëÎËR~ü)(Ο‹ Ý9ëúÃNÀƒ†5ð``÷‹0Ïq·úœk8œn-îÙrù óÔLJ»õµ×…÷Ú›×]œïÅëØôuѺ{Úé;dZÛ‚'àÖÇàÎC{À"ëÞÅg“/™œ¹²ôíað§ˆ,è œ{j¹/$W­tdù¨‡y]´¾Y™©ój½0çz.Iߨz¾â‡GÚÙNÂRF®…÷áû)–vº‘GïÆvÅ}ü˜ŠM_Yi•=Oo( »ã?:¬8¼œyo}owó¹HÕ'ß7~—jyëªo=‘äZ’ïv÷{V8 û«\Ÿéx?,\Ûwéõ5¡ñÃÕGƒ€qÝØ}ÈE™ÀþôóªÌöIïä[Ç võ*qŸR :ÃìÑ¥ »^ñÙ\85¡ÕE§.À¶ç“çdÎÔzîÙñGB¶„¼5RWVípt÷ù¼+Ø bç3%ø^*´3ÌT``(üºÕíd.ˆª·QÆ\€à¿8ßÄnOו-kÙO8ìM<³¿À×ñ´>ÀëDôþP(Ÿã6¯AÐ%d<‰×83Î7ÚIêÖÔcÚln:~šœ³/6ÔíuwâÈ¿46R&ñÆؾGèsð;¿‡u5×[¸Sœàõö]¹1Éþž”·– ï+\ß1¾ß¸2ýÐÁ¡û³ó[+æe'ó¡8¿Õxáâß$Øëàžû•.ËÇç‹ìz"Uë ðÝð)#|Ù÷’'§ç\mÙ9î ˜;6ÅÎr0ðú•ÉßñzæýR¡e¤üÇý¾êA`.´šÔìyBÙ ððÉÊ«C°úquæ kPkêWÝë‘ÐQfn¯ÇÓx÷fõW¢œ£¥ƒ'êð“Å÷ïìÿ›Óµóàó㛽"”ŒÛÛ€Ýÿ ü{)½oâöªìŠlú"½ÏËÔßÑ΀ÐZ±©:pïÕ8õÌ…z=ÊŒ;¿é<„üné÷ìˆ"7˜1ÀyBYSüßߪîRâ> Àë{ ^?DñJúvz{YçmNîÙé}–qž™ó&ÿ– ²[½.×õ™õ6_ßäÏx½mGHT\³O½ûû °ëüiCÕzgaEï¬7‚µcã2š{ óc“7Þ¾à(ÐóSW3wžô~íBý¯{ÞŠlì“ ‡as×ë®9°¸Õ”Öùg`ÁÔa»Ïò~›Þ=‡ÊwÈ8Ô^ë œ›Î¯Ëý‹×+Š×W´hgê½j6LKç-ÛB¼:åÀŒiççÇŸÕž;¯Í b}'6‚Ô¨%Ïx½¼º©@×ÛÌÛãuûâüy^¿‡`×kƦ$¸‰YcÝð4a÷¡P õŽÞÑ4Û»å¾aë‹âuvó¹Í¿ŒÛˆ×u÷>3üX´*³îfê;=´z¤ Ññ %Ý©ò‡!g»ðþÀ¸ ï×àŸ?ß§ðs&^WŸµ{{`h=gÕÌW8×\ÞUw?ŸÑ.kï^×C#éŽKî¥Ã8©Í-8?”Õƒú²ÏU%ð~¤Lr›ÛØ (¿ñ¾œïv\wn{a¢dÝíÄvÝü`×½$¨´qÏìÙ'ô0hh÷[ý¥ƒËª E›÷u$[×( PN_'àRîêó>¯ì§¥Oò;—¸/(ÐÎéo0²“œö­_­‡ý?î•uJ‡Žæ¾Îö¹x}B`ùØœ÷èÿ¾+§uâ—¬¾Ð¹ÄúQ…vÞ¿[X¹¢S2|¬\΢ãzh¾½þûë§Ù¹þPÖŸáÊøÜ¾ÂÆ?š§uüà ä_?Òú‚ÝÀ˜KeGŠ€÷ÙQ~³ðû”)NÐÎIý´g.Ó’a‹ƒëwß ÐCÖ|¸zzÑi¸¨¿ý&°K8¤ÿæ“t»|öyû |ŸÒÕ¾»ïÏù˜½5÷ ñ×A×›”§E;·Æ|?djr2Ô1vL飇fõö~áq²ƒg· l®;ñ¾>z~ïÌúù\Ìý!­œ´Šä–ò ­ûê4²s3´czYuR (ø.F£ò‡ŸiûÚæ4ÄN¾®MS‰ûOÛ¾é/ˆ¯ñb÷–ö­¸[·²>È—ò»mâÛÆÊ9w™òWÙ¹3Ú2}¾ÄHž·}ÀÙŽzP§V»•ù š x77â¤?ãJîÑÿ$°ó4žßÙ~¥ÈÜŸHÿ'ÛOÑsQ­|E5»µçþ˜)³‡gŸ¨¥‡šeÏä]Ò`dáÁ*÷ø;Ÿ(OW%ðû"çÅñ~z¾Y(§ç<}YÝ›­wÐN™–ñ£§OOSêÉÜÞfC·GCv–_›ïfüZÎJáËÞaJϘš6ªÌ÷ÎsåÜf[±®Ž:臭O²¾9vîŒv¬ÔSë<=„Vîv>¬~üxRš™/H9ÉŽàÝ.Mç¸8”qo{±>G0çeΧdëÜŸ› íLûýÏäjSàrþæ… "=œítÄ;õ10μl»Úü®äÌ`¯Ûø9*¯“ðü?ñ£mÍ&yŠ}´³¢¦RäWáãfÊs]Žo‘¦A½Û{Y¦€ù5ÉIA`}BBMi¥á^IƒX¸ŸyýB÷åÌç·<˜âíÜYJ@ƒ§ÀÞ|̆³õ^Š=I…šT4È™ëkÕûÎM h¿ÄøQ¾Q´ Œõ¥‰Ùúø½œ÷YP?¤ñƒvúZfú¢S`s4Ïúãôlx%y•úôH*”³_آŌ`àœiº¯ P»¾ðFFË p;-›Õ¸By3§Ò©@ëéPÈê\t›‡vÜ ;.¸¿êì÷œ'eÃ{aIµF?§B—êK_¶ß2˜ñs=wÓ[à}Ê´ä˸„oä÷=Ë®rUÄú=Jð•EµóºÐó«v/8ÏÖ9­É†.y=û7•§Â̪}7F¶T<äF¼Þ6(§´@÷ß^>åS£^JÖ‡òFN9•b°/ð?£p¶)~ÐNŠóìš=æžÓíîÇl8?±éä#ïOA Û£Uz©X}-~¸t¬Ï¹™}Ú×?‚ÅïDx²µöÖ[uaþï÷ï8YåÓ`ÏÐïGvÖùDizÇžÇ.¸S̆‰[7”ß”x ¦¨‚ÖÎlî“ièÇ1­ÖáíÁ3ÎÞ/ÒçØ[ë²Ïj±:“M &C¡Ã#™í‡´Î«B;¤ óºL*‚Õ¸-c– bç !ï5cí¨+5`ƒÄ­ÂøµØ}~°:´Cº©m§Â,uùšÿ¸©›7ôk—Âx­~P{KÝ_·Âö¥ý…³>µüþh1ZâjÒuá8V¯m [[Lž¾¶štÜ›àЪë8æn]¦õ|êo¢:ùŠauï(ë`ü/ŠÉÉïpö¬|â÷vïžH/sîpäwàwpb{Yk¡µÍô»q§=Lñâû 3oý¡÷»u2jÂÄä9—wéÇê¸4N¥hGÔ(qòÈ”TX¾ó±mѶk°Ïél¿?§ÀÒ•å¼Ïš×ÝËëŽo}Â>H ý?rs)?·áüx^ÿã|hSü ]¯i/Ÿe¤Â¹jÁ÷­æ\ƒ‡Ûnæ’õúüú³ÿ`hGÚ•¼üaûäy;ÚNR±¾ËžæúIi*ï‹¢ý&´/B…v?_öéÌ­Tˆ¶±]8|Ø5°I·?¶¢\ Œ|»ôtd·0öüEÌ­xaï´ÖáÝÏø²þH? œoZ_Ñàõª*iø.Úkݘê} ĆK'&ƒ}Ìí5«„3´+üNî áÂë{9÷ƒÆÃËvääu<«Çß•ó>N¾¿¥ÏÑ}¿íhžY×{Û" ´o+.µîv >ZH"ÕÉд՛Ÿ·´ÂÏW„YwNÇ_¥ø>‘ž—ôZ¯¼+ç}œ¼þT¢?íÄt$ä40¹ýw×@åÛ8åR³d€K;–L‡'†þ p²cbëN{^w0Û1Å^¬6$?¤ÁÁmZl*ÌiË&)­n&ÁXC/ûÜã*`u5! £ÈÂÝ@ŸûÓzjWs?¨Éï%ù ÿ‰}mú9 Rw‘F,=oïÆ$¨;ßùÏ篃Yþo ûŸŸS-tóø9?§ççF¼ÎD×´ž Åëׄ´ë‡æ¤ÁhýÓ_&íÌ7ŸZt•Mä•×w\¦dë“Þð癵rb€pÜvK— ë@×yÅ꬞ósã{‰Ÿ–ô.QV u}÷gÝ_‘í;gܲ5 ‚â…Gz$Á­“ ²0÷õ]Ÿµõ˜æ·¯“ñsDs_)Û§ò~zš÷èºN…vrš^µÞñ{;¯Ê‚v³Âòj&Á°rûžnåõD1{þ"Äœ_9§›_Ÿ÷ÏÓ¾hÌ»äX_ÌÖ[h'hEŽtùB\/ÖžrfXŒ¸¸Ñ`ÔAûgKoÎôì¼ïžåS®'=ÌÏ!ñs~.Åû¸hÞ§ûV-Ú©ì4¥‘ë÷i±v¿$( žôXu÷ØEÔ‹¨wÔ†Ÿß¾•—»›w¥Cþ@Æ·îo~.„î[_Êù¾…ž³ù°û-ëkB;³;.²­3$ ^;’PD™VûÖ.È®†Í ’û²s‘° lÁëk¼Îá(µz7¹F/s?ïkà}Zô¹šòÐN^¢Ã³¬Åi0ÉtPósÔTsuг~¬Óó9þ¬¿¨-°¾îx ¤3NUªa?8§š^/T Ï­ù±sèµÛmÕ…r¾~àë{Óã'µÙ~íô²{z7ß43·óáì«Ð.bôâv:PÄ—³ÌJü¹¿ŠdÇ*ðçÂŽÖ§ËŸß è¾»ÿ|Ùûè[¢N®@;´>ŸÆòäUзJº£~ @ì¨O£ö±>nì6¾lú áÓƒÔ_öMö7?çÔØ¶Çûè€>§!2÷‡óÿß?hg«{Ë‚WËÒ èí ßêmº .)ï9I€f‡W,8ÌúÕ@ëþ‚—iƒ9:já%ƒXð[yàý…§Ú½“óç èóTôýhÐ=·LƒgmzõQ̽ ­ó{É>X¬.ÌúGª³~_a¯û‘æ“ÜÍ}•ü¼ÖKžËy>ú5¹Ý‚†Et¤E;ôÍàæ÷…úšð¡W¡B̆€ÊKOÂ…÷?½]ÄÏg…. {Úöh Ðþ€½#,O·™æd¶Cû–Ùú ¯ûÇOdG˜V7šOt¾ ÙóLøh{T{¡<²þ²®=_ fçÀê$ì¹?¼NŒ·&ìÍ‹TXN ]W¡Õ íÃM§O@ýÝO\ÝyŸ¹@÷á7¾ßåÏKšü¾^¾")íåÓ·çq}Yù·½õU8õ8#N2éTu}ßþ§?|ùþ”õó ’ëØú•ßÎ׿¾ÞïÌϬϜ9µgͳR´³gL£ç«v¥Â¨[×C{>É„ô‹ö' E‡ßÄþ<…@×Áþߧç‹Àûµy_íc²/±P  ø0âÂ’TÖ'’ qç<Þ~–h~Þ' —´›fÌúu˜ùœ…Ÿ_r;&ÿÆëÕ)¼))£I…Á¯›ÙÆfBùž ZºI„ ä6#î Ï<–å¿•{ 4ï…›ë?|?Ìó¿ÏÒóev^×=tP4÷+Ë^:1?úoü%ÀîÇDд=?JÝ\—šç;­¿@Ÿß ŽéúZÞó…>«ÎÇ'îêËîÿ/Ì~Í÷ÓÅŸ7Т@·7î‡ú¥íÌ„ÇUNÊV·JDZÙ+óð:†`j‹úS)Ð<ìi~Þ–®·ßÈ©½óç“ès*ôþ£C;t› y†Âà› ë64Øyñ8{>U ¬Î&ÐýVÀÖKæõ?—ŒðîÙ`ÜårÞyrK‡A®ô¼ í¬½{ÝözëTÙK‚»e‚ðG™²{†‡±º…7¨ƒxß’Àž›øú‹¯Çø:„Ÿ»ó|S¼/BT?_áiÛÙ½M*ɼ¥j ËË_~|y ÒTÕº>k û¯N./öd}CCúÜ­½ù¼€¯Cx?4ßlÑŸÿ‰óI¾Ílúÿo¶&™õºEDÈûÎÑùÂŒoHœôk›KWÄ8^¥gm*Jq±õŒýJæÔ‰18"k“ZÄçg¥8°NœS£esÕelæð—8>ŒoX|Î&axé¤1±ÉLuÎï"3Õ‹3±#J±_óßPÍfÙÉÿ•ÏÚÔþ ÆáçX5|¶z^1.¶„ÍÙüÃKYjŽáÔHŠÍ\qûßpjŠÏUcóíŠs¼"¾0[ý[®ü–+5¢¯/WZ²÷¥ÑùjÆô²ü gHçW~fæ[),aPÞ!™+%©MjÝøZPn$q(‰„Ô3ðwl¦gT1E,›IìÀf{~i>»’1½ŠÏ»SIKr`I ‘í ,àŠs`Õ¥x‡ŒéÉæP90æ!Ÿyû/¸^ŸãPð¹Ä„yÈY°R6óîs,>¯½8'›0(¤ìTcÁþƒ¢øLâ–ŠóyøÌ¨Òs‰ù=ü[®ü–+¿–\)f¿³´ œ-cŠ9|…³ÛeŒ™]|Öž˜Ín/Î…3N™Û’|a&(áô$0†…šñ²ÉüãÆêùû0Šq,øL>ÂË&3A‹3aelf{›‹\œ Kæ¶ |0øP[Ê&‹e³Û «'¡Ø¼>ý?ä“}‰kAæ#DZÙíÆ*óù^6çæ¡Ü0ÀãXû› ªù®EéyÉlv{qf¶ö33“¿­'¿åHèëÊ‘ä¾-a¯;ODç‘F2¦™TÒ£謱(1:l$*冎‡’T ³!Ñ÷Q>èÄ Él5|/(#JiEf¡o¡SG£Š*‘Y˜GP2tðæä¨ ”CUò¬#Æ:¼¥¯FžBûl&)áûÄ¡$5Hï5ÚEù”bÄ0ÆOB-Òo‚¿CQ>$ uÈ™"þedsJ£‹1.âØüd›Yú¥Yñ*Æ4“a`Å°àŠ–äÃ’ÙÉjÆü!³“‹óa#K1Œi¦Aå±¹¦„ãX€òa3”ÿ)×ìsœ >CÙØþ/F,ašEÿÃçÊçgëÙüÑ(”Ñž2bÿŽqQ|†2ašYö(ÉŠüÂåo¹ò[®Ôˆ¾¾\)EŢĔË8hНpÖ¼ãiç¡Ü0âP6kž3k MÂX@dμGBmÒcDúhðw( ” 9G¦, ãiD2–6™í¬f< /ñɬù"” +½eidz1åÛ6š›1od³Ÿ‹³nÉœy#J‰¨CIo#ŽÍš'< J‚A©a3 ÿ +íKÌ 2:Íšb¬4åß°´9û±åƒAžÀJTJÊXi_bp”žmdóæ‹ó´c?3#Zû-W~Ë•¢¯3WÚ±×U@¾? Êô&ì4 :i$*å†Î‡’ ÃjP(tÜ”7 eD)+’9ÇxMtähT‘%™×‰×CÉЩc˜cG 2PÖdÞú=:¹¥¯Bæˆ` ¡Äèð‘ÕÈsûh?%f¬¡„êäyF´‹2¢”¥X¹„Ë¡¬Ez¿ñwѨ"”²éÓÃßa D£ŠP>Œ÷͹ ,xÜP13 ?‚±Ó0°´(K)¾iIN®-² 剛–ääjJñ$‹;- U€rc\I#J‰_R¿à§}ŽÍa‡*jÿ+—°ÓbX@—æñúŹá„Í!ûoε'ì4q’Ì" *å† "%Á$¡ä‡äʯ17þ§r"σÿ‰ÜÇóÏs$¯ñ|ö;—ñ<ößÉ_td):rÔ?d}޽aD)Ññu(;ƨ%Ì3Õß0·б1T:”ã}‰ÅÊ@9tÿ‹¯-Æ ‰Då¡Ü0OÄ~[W}[W‰¾Îu•³k$ßeqÞ™Tƒ*@ù ³& ¤è°Q(#J‰Ž«CÙ¡óF£Š*æÆJ†ŽÜ9•r¨Df ££c«QúÊdö'ÆJŒNY…Ì´C@gCIª‘Rh僎Ÿ€’0†e‡AÍxܪRLÚ¢šä9DüJ†ÁÃD…JGÉ0PbX°(›ÛHþÎØÜ–äÌ¥eAÊ@9`0iQ–PjÆ;S``Å6¢|îHiI­MÓ„ò‡$¥x´Q¥˜$ ï,eDù0îcJ…©ûÌ3; ÚhTJ…Á›Ž’î# bΤu`¼îϱ‰ÄÜ‘¥xÝ( öð„I›r@'Ò¢,1øÕ(=JI %ÆDɘg’Rœ¢(TÊDŠLÚŠêÓä‡çJ’#¿¦üøOrã¿É‹Ås"É…ÿ‰Ãü+õ뿚{IßcðGm2GÔ¦X‚QŸ2€3jSœx¹†#p),ùªK¦0e+yWÏÀ^Tòâ÷Óûâ’7 ñ 46} Ƀï äÿR’ã#øbA®„ÀP`.e¥~°ÁŒÀ¥¼Ô»‡À YÒzj‹ YÒœ A'}–ªB Æ;k805‚Vì¸ hÀz @Ç€\à/í!˜íÌvæp%í‹!¨Ô.ê8àÀ6Gw,ÈÁ¨)Àõ$NLB¸ øµÀ ²€ ‰ì áÀÔH °GbD€ A‚ØJs,éž-’D…$‰vH”p`j$ŒØ£~D€ AÅKHç˜H"pD"Å‚<Œºa ¾î“öÈäÿŸüY.ùó YÄ늈#ùŸ{¼+¢J¶ªXðC¾Fétr^ån©¹Þ´íàÇ6ï3-}OGžëÒñМ#–>vÎ=|“úX<‹¼OŸ;³xºÜÏdu3ë>Á*ŒsíÆiµÓ© »<èäLÚÑÒñ¥¡ÖrýôüAæ„Þ$ú OCã~“Ñ”ýñÁ˜;ÃGZúÉýÇäþ ¼_ðB`œq%×é^&êUÈ›7êx&Mqsr=ŸDg[ïìžÄý®¾,|Mk¯¢Ïû‰>¸n¢_©›Åó*÷™ ÏwºÑþYátÊ(ÜaM¯ ™¤­5ïèü$šUºó’á÷=iѧü®Ń؅žû×ÎÎìÍxÿ‹WX~¾ô<-žW9u­¶{Ét’ìtUfàëõù-õ‡¾IdÚŸ<ÈÌí@– dæöàí|™Ü‡Sö&JÏ1à9gn÷¼P1¦ÙáØ7“\î&õlÐ>‰*ÜÑä¨Öž>F.d§ÄvWªt¯qŠ/{²cí«ek}h@¯ãï½–X|<ÒóLxÞˆ’(2=¦fÕÚfÒ ³Ð#‰¾{<çþ‚6mh|í† ~?q¯qoÆ}G!4úUá3#ßõ#Ù¯"÷_–ýIÖqaS5[ÕwÇPWÇtâãeRÇß½Z^8‰–xrˆJmO¿þZåR‡‡}ØãsS“¢X•«¾¢‚|©÷…Û•FöêI»<˜¥+òÄÒUööX÷)U`œÂ—ú\úÕ%JëƒGeÒåi[ã³õ–~×ÂÇÀêOÜðóæôái–û9w&ùùÜøP¹²@‡+‡tú¬ïª ãD<›ø$¾)âãßÅ®O.Ñà¾çœbzr)\©Œë¯,ý܅׎.îâyz¬Ü¿õ÷¾1r9.…gœÇ9Æ™tN5øý»4ºU¼µéyê%jõ`êåKõô2Lß¹G„»ì_e›ÇK¦d_&{¤¹W¡!ñ¾ô?*¹ü®’÷qj+ú%qßNÆ9ûÜôx÷­4jU˾ðÊï/Ñ­N1ÑŠzúuXѽšzããð¥w6…0Þ͇ÉýÊäþ4¼ÏÎ5¥ÜÇš÷Mm-‣Å8ÚQZC»7it8xGcÓàKT»ƒ_ÛaNz*x­nÿÊÆn4å¨C›àÞ!ìçî•o^õcß^›}ð¤ÖÍâk¬p}œòîâGJ¹÷˜u%î3ýð1NÉ2›¦ºK'›Öó+s»D>¹£ìsÓȃ™}æxQ§[…‰sC÷€ôñíN²ŸJî'Üd|ÄQ÷ŹJî›èJcGOOíÉûš™0NÏ«£ënÃ8ÙUÛ—]]ç=ð8RuðÃ4b캇ÚÃÝ©¬ÓæÅF±ÛäÅÄõd |[WœqÃä~_rßj¹3÷`wýzD¿±j¿çϰV‹Rƒ?éüûR½ÖM>Lõ_è ÝžáAyÍD¾žÀx¹îLö‚þÆ.t_3ƃÎe /ذ@žRö©óþyî¢)kÆ™há¦S­¹ýº$Ü4R×*ªïò8L•¿ïø¨ŒsgÚlWuçR“XÀr‹æ÷fÍ¥¶nÁA$ê ¥¯4ï¯õT){›­}š*ŒóÛˆ j“NŽÏêÜ_}ÚH‹™Þ]ª{Xôgë úî|ÇÌéPÞŸÉÏ‘û\É¿¹žìӵq~¹Xhm:½HnÖjü#gÊi»Ã¤Í§Q¸_hD{§X2šñqƒØ€f½œìp#Ù+míñŠÀóÒÎ)y¯O'»ošo ^h¤ÄÙ5+ºÚ}• Ý•#·ïÀ¸Ç4ˆ=îÐwìÍúÝÉw\R²fïÛ¯ÅsJ6tÏPá9§6N¯xhˆ‘ìG\·Ë,rXø—Ê0îkéÉÌm«õ‘ûs‰¼î+Œ×¿Î¯"ÙW$û)¹÷„ˆû?ù8 ŒÓ¼B÷¡õ"Ó)þNÔžëg.R¿Jû®”üñåÏqh| ÎÞvÞ¥ÄÆóÑÉý˜ä>©rÜñ>´,ÞBî“ó!Œóóȭ׆„¤Óµ¿8|Zz‘Î,¬õm‰­‡h£[Ô¢Fa^¢_òÆûl0ÙêÔìûÅ¿¹’Ü9±_JïýŠÑ»sÆvó¾rÞï.ãx¸^Ù¢K:•)ý±³1î"}·®wô¬CtæCÉÎ3•ÞtðôÝ¥^uÂÙžyaK½û3¹o:µè§YQø\lE?¬`‹wÁÿÇ{ö•G¿9¥ÓʽÁUžºH—[§]Ž®~ˆÚΕ™¬}ï'±b«œ"‡Gú0s›è\’½Å<m(ÈfB¿šbÿi÷›§+ý,¿?s~`œ•ßÖÓû§Ó¥Ø§ŽËs/PÏ¢ý 63%Òä¦múL¾î)ú§O^ßîln§ƒoQJ9±®Óì®–>úÜßðZÉçGÝD_mÑGã„-¯g÷ݼtJþU*àèx…UÛt‰´ø@¯Ç;ww$wß´ûu¦0Þ§½‡¥ïü¬ïý·lnoyñþŠ¹Ê½Yý¿ëQ½ËgñfÂ8÷ujì‚xÛ·lpLHÐÚº|Áž„å‰táQ¹“º4¥eÔsƒnL±¼'dȶºåjé;/{Æäþ˜Ö>d›ÙªC³¶9ø!¼Ì ­/ÐÒžÆG )ðS^ßÃ=‹Põ[¶óç¹L³ü~¸·Ã‡Ö~·ñi'ø~>(o~:tëˆ÷[%Ÿ7y‹y ïÛ¦À8js¥“búÇ)¹?ž§Ûv«â—aœ&}‹_±)Ä®V–:wOc½©ÕéEy½˜ü|·ñÞËO¼þ'á÷¶ô½5çÆqL¬xé»nétܯãÄ®kÏÓÍóëv.ˆMýŸË²„·µ‹X1•I]€k\ dr<Ëžž5µ£ÃgTÌGržòþ¯]id¯2× ðù~0ÆYh¼xåa…t*rç]•ƒ½ÏS÷ŸÏã÷ýP%Xõô6rô“Y‘*ëûÕ?Ì6X¢]Ï›~¼»ïtå—Ñϳ qt~Rþ•ÿP¤÷g~ˆŒÃçåibýpž¼6z]i´"‘žÏ¼<æaÒ4ÑœÀálôÓÜõÎûëÉë&êrò`DeÛÄû?ç·ô_·¦ÕÏ-:‰÷ Æ9üÓ¢MÒ轤y>wŽ2¶m>?‘æ-ö*;¢™ø9Od½ÊíÛ¨~?f×§Tÿºó‚E¿NoQßlHö]|¸.‰{Yú—šóãô^‘Óð€m:±b–¯Öcq·%ÑÜ9ÚÖXš˜%ÒןB³Ú¸“dQ{ç2™q]Æçkm„¯ÀÉÒ÷Xö«ð¯§¹øýTçù£ÈVÝô¸@ìiôݘ*%LΑ1éÆËÌr‰Ä}ôʶ¥ÛNÇéŒ÷Kt•½PÂséôYežgh;Vƒß·×㉎E_œ¥×ÝTAo¤££_'¯÷õy&ãý”Õ¬”~o\éÛ^—Ü×Ï›ïο^žoÖ°f¦ÑÄRÆimœ¥ÜSjï”íéC§¢E'yа]K2”³X!sãm/1¯(üÞâ}öÁÒ'xqR³øV.ÞŸù«‚1ÎñVUœš^L£«­“¼%cÏÐRÃ'¤õ‹¦Ž®ÖHM-†”A)‰dÚÖåw8Ò0—!Rgçn¦\Oä>ée çJOÑX|ƒæüÀ8«†:ìÉ‹N£zoJ³%0NÞx§¶êƒ4¨ù¥¦M·w¦=Â6Šý›½YM—ª¥]è$ò´»åç%ÏwGžÓà}WáYà}ص‡×£4*9gëÌ?œ!·ƒ #w×8H ÍÂ±Ž”tg££±Þ,ö¢Epì‹a½˜ð&ˆõ³]\ÝiÝÁ—J>y¥ä>‹WÜœçaï¶5Ö;¤ï|†²û”:rçýrÔÞæÛNI÷º•=´aÁLVsñ¡§õ2Ù—$÷•ûýó>¾/”´ëܲ«w:Šùšè{ŒqªÕååPë·qêá¡tE±†7~:@.ó ?)Ô°…è3>‹ñyc€¥_¬ð§‘ì_ä~úWJ±~>;ž‡65³UÇú¦Mx5•l*¬,ätFø®P¾mÅ«3è]ö0ƒâ~$“ýX²IžÏÈÙ»m=_WàùNoÝš÷Ø›JËš—›xìôi kY|ò™¢/r-Z0ø­ÃO{"Ù²¡ýJ}¢f3WnV}kG’}µr?P¾ŸóÊÒwÔº¿¶ ã8P•o=*•6Áð4ýW0é‡ãˆo¶[Ýœj¼ÞsuîÙ™ŒÏK4lï‘’?,Ô]ÔMw‹×FîC,{µ¸ŸšÏ/‚1N5»~mS+UxüNÓÃí[žGkл^­ÒªuÞ߱ыI¶uûïüéZh£ÛHOËû‹ÏËò” Ò¿?¹qºûgïãŒó6icývRhȬ»}S‹Ÿ¦½Œqq€xüÄ÷Ÿf0Å{ݽ~l¥ŸÛOÕ={Дr{ÝïNî$ö#^+Ÿë?ïØ«WJ¾þî"úß Æé_ü—NG‹¦RÒ»ª5]È †¥ã?ºÖ9@ÝŽ—õߺʇ6w:DG0þûö¶ô;–ë—ÜWžÏÈ}\­û¹0Nœ¤.˜Jþæ…fßýsÞÆÌýbÅWø»f±¦îÌîX°»YÚ«X¿-E~6 Þ§õ®R®Çrßf¾¾¯Ãóãür8gÐŽ—)t¼z·y‹zdÐ7û6v˜¹ŸÖm,»Îpчb×ך³&’ÉûF5ý;~3§5›´ÓmT´“è×{DznâžµV¢Ï-ßDZ©•­ºZmþÈž—RÈüš¨’AÉlO°¡õ~š±´ïÁCw0ožs³xkS¤Xgh˜ìÑ–×½òz#L“ ˆ<ûFÉûw>>_R`œ‰‘¡{BÇ¥PÓÎI-¦ß=EQM³R»¿O ~g' z•Ú•¼+ Ù¸.’‰yûpT9îêö¾¢_®X¿cÉ£" ^–è6Ê—„O€çÆq3‹cRhéÇ~EK­>E7êmSùRmx’?âDŒ+¥¹Ù£$FŠ÷¤7»;iGVÜ5oÚÝ~¦Îg·•ºú©Í¹•¯•²'•÷ýÖ¼þ1çÆI_ûJQ0…æ®ñ9m£9E••Yö èù³Xêµ¾ŽH¦kÝ´N=KÿêÒé÷J8?ï`ñmžZ1¯™aç¥ZóÞ~ÜµŽŸùš"0Ίsú=N¦ ëÍr)uŠŠ§.öÞ·5AxkSÅu ož-ÖM~L^Gðúà,æç&¥ìk˜½fAƒT};±~®Êó㬾àUÈýt2yŠh0êæIª×«Uá bß»qŸâl¶-äBóWLî/Ì÷uQÄœoJW¾)¼Ö·-qÍ=ö<0΃Ņ'F%SÞ»ô§wž¤ú‰cçÝ_— æ§Ï”0ió3Îf§ÖýͳU?&ÿ\x¿rW’½Ö}ÉMx.}ÿkÆ™&ÉÔ±ŽÛ÷{†ž¤¤*’ñ.xí TÏ<Ñ™Í.´ðî´}V9fpó9[º¿}g‹—Söt>Фœ®ƒø½‹uLílÕøCÆÉyš|jÆLÅIZ¤ÉzPqq‚X/4¡[«ŠD+ɸנ/“½ÛòºLö$sïqŽð$tó±€q+—yíÿÖ@¥J xßâq:Í[™¿‘~rÙnÞ<$ÀµÍ9¿Y•A³Xç—W ~EüDßæN§ÛÑ2Ž9/ð¼Ü *6ød ,æn4:NïF½Òú%ÐäskªîÞ™žTkY7­ï,ÆãÓÉ^ÀÒr®|·Ï|ªÁxނ᥽»Ý6О Y!µ¶¦Sæ±F›Z×M ™LEÏ{Ðéªg>È)¼h*6¤qؤݕHiþø[ê­œ'ÊÏ-2<¯‹Ø/þ^Œ³»í®o^Å(ßÈ]C§¦ÓÕ%÷6ÿ¦£Oö–¼1Æ‹ž´º¶‘b¶ØÏê`é—/{ä}2y<ž_jñ^æãh1ÎÔAã~hïn êÒ¡gºð»éHÕÖÉÕTïÇ^ê7]£„OÐÍâA•=HÖûq<ïLº½ÿ©§ŒŠJçtz:ÃÝ7~”Ž攽â™íEÓÛÚX*šN-ñ´¸ãÞÉ–}s\ã9¡+ ÆïÌE±t…òé”—HV7בC4Ãñ¤Ô£ÒÄ'Zþ}±¼1-Üý¦ÐÅÒoom@r?{süÖÉVÕ7Ö{Vçþ ª{²Ù‡2OÓȾþe­|:jÝÞ¾x‘q]¨™ò\¿Cë¢Å¹”{µºz¹e›S¾–‰#ö4H²Ÿ‘ϧUbžVŸÇ-ž¯./[NP‡uNE’Óèq”ÞqØÁ}Ä&”ª\ÌWE©3cçŸÃ:mϾå¯ñ_·’äs*ùùïw<Íì>P%|Êüù*<ßN¹,ôôÏÇI²Ö&ÍO#SZ—RÍfì£ïljTt¾Ù‚\ÕœgÎaÇ%ÝÆO}E¶'¹Ÿ¹9~ñœrE¶­ðˆÉù+ûÎÍñ‰çŒl›_p쵌þX¤quksd«³Ç>K}>r38>µèæ\J2Dôa|?FC OOCŽIÌ»ÇÏãð¼ m¤FíÇèG—”ߤѩæ©Ë4ÛG öL;^Ü™"¿}œ3#)šñùVoÖïZ“¬7†ˆ÷ê`‹¯ZÞoâõ{ xþ¸¬i§r³Ž’~b‰·™7R)¢MÚÜ7ö‘ç–éŽ? º±eS«‰Ñ¬Òéꇨ{‰sDwX®à¢ÑO<,Ïß~±$V„®âç!¼ëudøQʼãy×õ@*Ü~ëí½{ɬ™(ïI§¤U­&š½Hµ½{餷Å#eíGµqÀ{|l³1»Gèà¾öê‹S©Q¯’™y öÒ´ˆ®QÅɇ>4³¯]&*š9êý½#‡©Å¹­ÉuÄÚߤÀó¦ã»¾80‰.®?‹P*}ª¯ ì?cé×´ëU©foêÙÅwÃÏaÂ[ÅÆ÷ YÛƒ–EíÛ7a‡·%¿åyŽ­¤*ãFü=$üŠG[¹É­:…’(·ûå¡…û¦RÆú¹†Ý´¬ëþ ïõ¦øùåΤ\›Ã–H:ÁeY£ƒãVócwšošát=Á›Æf–\Ðrþâc>Xí-ö'5<~1ŽÇ´€w$é©I^‹zMš¥Òû"ϊ즶OüÝ‹FJÛoUc„7ÝCxv»kW‹¬lÖëö?.}§T½9:ñJœÆr¾`ŽoŒsñXÕïî4ÕS×&Ã_ï²I¥ZÜ욎ÞEŸ¼I¿ÜÞ›$‹û¢1LxUÄŸD¹¯ç__Òëw/¯·ÏÅz±}Û@š!ðs$-ÆY_qêꥇé2«³¼þ¯)4´¦§ó¯y;É~BÏ‚· t¥‰Ýn-rŽaÜ·ìÃdŸ‚ÿB‡Ý Ü,û÷ã{øÔ#˹%_Oòõ•ãt,Ò|ä§3‡Èùh½RÅS¨·]ÒEÓÖ´uáuuÆ+µk¹²Jñ 1ìÕþG[Öòg²Ï•×åîbŸã±ÅÇÄýS>[_™0Î7’nÈ|"‰MN¡âžÏÊô¹“FöhYcêùÖtóØ˜žq¯c,þ¾ú…Ô-fÖò³øc‹n©LO,çÙ© VÊ×YøíùºÇ¦n¶êJpåèE1‰TÞ,rO!§q;l.¸î¤Ió†ì‹ÙÕ‚ú?ù¾¯Ï\6ÎnüÁALŽÛÒöF…N–ókùO¾~÷ëq~qVNøäS2‘-TÛÛ?…NLœWìUù´ld»Œ.SZSÅÕ[döœËøú0q_mG’=S²èû„²ýfùˆõ.ž{Ò+¼ˆ×´ƒ4xþ–[9!)téÇénÏîì €Ê+š:P¼·tÒø÷±“Ï36%n:?f”¥¤MþXªX.Ÿ—ày uíòè%I³>5Ö5¶ÕŽX³ƒäõ8¯WÑìBj‘nÛ'ŠuSo’×·òz†ïG¼û ŸÝˆÀ834ë/‹:@=&dœ+BWS.½µéºƒÚKË—Ò]hžš.5n6>ykC·¾Lz›´¾${¢Å9n>QO_*åüül]‹qìUyšM¨êò5ñ[“’)䯇¨Iâ©Ââ#f·èJ’õѾo$ãuÏr²ëP©ñý`Å“ÕvÒo)Ãç¼Uòï¿Ëg^ÆéP2òÓ ý4žËºA»’)¬å{½{DoêNuÛžmØ1Ax¯ëe«Jõ¸t渎ڪœÝÝI&¦5ÿ8c;-¯ë‘úö÷º%ï“ó}‹^$ÎhYný­Å“ ‰úõ^ìsúˆõ3ßgV`œVçÛy]Z¬£½š.Ÿž’L—þpâÌÃmt©Lå‰mnô¤…“¶þÃä:%ÍÎ*-ðÞ~äýζ÷š>ùÄ:ꃒï¯{‹y0?ßRaœ{†]¿\è¦£Òæ ÍdZÝü7מ½¶Që']ÇlÒ“NÜ Êvíõ]EÍ3­™¼ïÎ÷=iþ1oã’,û²§ŒÏ³Äý%ŒShý”R+«î£Úz·©N¦ë«ÖiãOmµœ“&Þ*x§Å´h6XJ‹Ê¾Œ?¿Éóayy¿Òœ/xnNÓ3Í›ÌÙC:ߊ¹Í’i¡r¾Ç"Ï­T¥eù¸‚ýºÑÚä;M†xF[æIò:Y^÷Éç²ò>£µßF‹çG%D) ;ì&i·ª|«dz[ã›Cïm>*wªnÊÚa)ѲïS¬Ë¼,ç>òý2ùœÖ¬c-§üìýnÀ8¼>ï$³ž·i2­ÒkåºE[ˆŸ[¹Ñìõçw^ü!š‰ópËþ¬KÙËQþ\-ž=ù½È÷£ÛæS2aœíË®yä4ÞAEâà î(—LÒÖ<|Ði ÕL¼×mÔèŽäx9tlÓBÑl÷é`¯ã®N$ç¥ü|Ù$ϳ¹§¬ÏGÔõÅ“{ürp;­ø-Ë6í¡~¼Ð³„ßëÍäž¿D« Õ;ÓìÎ×zíb²?¹U¡uvms:’éÈÀ.áSTÔ=áà:›+•%ÇÏÛñÂíó}¿ç^M±nÀ8÷„N½‰Ž^½½}'¾Ÿ¥Ž ÚDg? ûø|Q-k1£Û·—cXˆ¤5ˆùí·né.3|ȼ ®êeÙç÷åsYs8Vç÷w"0ŽmΉµ×÷j)_>úÝ):¿®Çåj“7Ò¯Í|»ýu?²IÏXÒfÅÆ÷YÔ–÷­|>+χd¸ü~i¢éœ¯_Oi1Îd© ê×Q •v(£]ÃIwÇi‰=Stè\rÝÞùPÓ½+Ö—|¾ÀøÏ«µÅ¿,Ãß—ü¹<—¿‡V“ºÒ˜|¡Ë TäÑÊ’דnpÎ÷‹ Ž}ÛLz0Љ÷“ã‰ûä…'Ï©è4ܹDêJR¬ž4¶ûL)l[æä&®¥‰ñ;Þö£à{ƒ»7ÌbßKÓ'“lߥçûSBùšý~OÔïõ³U?æïøðîÙe´6}~TͱòÙR|HУՔռíÕ)¦z0xÛÁÑòy-‹-sôÐáÀl¾Ø9в÷Uå{Üo'üˆ'ô7ÏVÕG,¦J;^ûzM1P\×I=ÏNYEG2¯ÅMõ÷%_ówŒ¸ÿ¢²¬³fUûôÍ,›®"¾³•òŸÜ#ìJ|]Îç*ŒS²\x½ ÕçÓèÒö«ÄèñÎòQ}ãhoS7¥ÛÒTdîÁ…ä=—‰÷4ã>ãÖäº:!´m;îÏÆs žRÖmì,2"gÈ ­>÷ü6|%åXÕözEº¤ÝbÈäh.[Ô[Ü#ê@ü\Pœ#ã9b×N¸>†Ì:ç%z°aÅ t+ľšúE-Ûàûî÷yÑЩýûîÌô#yÓiOÚÊ{7ÄýT<ï·Ôf¾Ë'W˜m >wª¬Oúi9¹Œ8Ô½ÛXOê¯jr3Éyãë—`Ù/Güç5ÀrŽ+ûyüŠý|<Ÿû‰G°øí‹ 5¦ÈvzÑVƒž-£^£>ôˆ×ÐÚ4…nlèLqo-É^@¾_×ÁRoùïIÔoéçY·õ¼Ý-dzX<õ#~ÿƒ‚ZÛ|wz)5‰»)(Ê—–Ýn}âÕ¹YbêÉøySk’÷åß»ü>•óÃú\ʦò÷ä‰A?'Çî½ î9uªŠ«Ì7.iHB?¿±ýýiïŒkC/¼Í¸7´´8o"ËùŠ|OV×÷ŒoÕ¯-÷7jæ/¹Ñã<_Ÿ)0Ž×€­³æíœÁ,³`Ð"åŸP®i«±4*JWÙÓ? ï_ßgÚ„(&¯ûxýð¦Ë·&´º·!€ú7½Þ¸ö®|Ä÷»?)eϲ|ÿÔϧL¤áÌÆÎÑì±ÃÂKmwèå¦ïXÑÜ…4T508¥DU¯0¼ûóï#™ì™—×K}ϼø$½·ØW·µœOżJ˜u'ÚŸ¸·ÐÇ;Æ)R¾j?»ÅóØ¥ÀöDz úÉù§nAnó¨yãd/¿„ º+½Þg‰Ÿ[MÙh¹‡ ßjµ(tûü$Ë{fyVâñ[KÄúã¼ûeiùÔ3Å~uÙ'6É´rò˜ƒc£iÝÕ‘šw{‚¨‰AÙvÂ/‘Lžÿñûá¿ïãÊ^Yù\ÿþÝ©ÆËkN õ¯Žq®mí’3ôÁr&ýÛŠŽÉ¤ÝÛµÌ{³¨Æ¦†×Z)‚èl£=Ñi¢Ùû‘=b³WgòzŒŸ—©…5WÙtÂûc^‹ž)å{ÇÖûߌ³1«èêþ«Ø¹ ό’©~^ß¾«¶M§]Ë‹°+iú|¯}žÌabŸLìïô“½¶–÷¬®“ßÈGÍ«ÑøGÅr¢Ú¹¶N7aœ¾}JÌÒp+›ØâpGÿdjW×uÔ„'iAÁAo ® ~¯zM\ÄŸ’ÍOÛHï+wö/ kòÛä¨òó;ŽÕU¡˜noì|N§+ß÷øøx$ϧlU¶ýŽ¿mÒ2~$™†·?Õªÿ1ô´ÀÆBÕNøQý‡­1s‹^úÎ,h®êe©ZÄï›û¶lv÷aõééÀBë¬KwUÒäf¾9ŠçÆI}–}cÖÖM,_ÅAgŸMH¦Y5Æv²FçU†ÄQ·}ÈÆ¯ÓJ1¬{·Š_º«¿ЇB>32ÞŸ~Vž¯Ó™Õ¤ÛeÎü¬i{ÜëšÙrœðT‡ðüÁ8±¯wâ7±…};cè”ÑÉtng¥1]ûӖΧjøö¦íKòiçU™Ã„ž-ìWBSyø ±BUËì9{g\]Z~î]דSjÐ¥êM¶¤º…“«Y8>„çÆ™nínc{Ÿ¤ºv\–L ¼ŽûxÓúî[ï^oÚïå+S£ä{5lÖ©ÃK¼KæÅÏUöóù*â=™fæ›]Êa2ñ}ü ž?ǰoqÛÞñ,²¥´Ç¼~È«+7fµ«l6–ˆïI7ØÌ[ìÉâR"‡\;Þ‹ñûØ^ ½‘5–ûÒ 4Ì¿§hIw|ä¶Ží{ Ï-_k1δ‰NžãWì`~—_vº•LTÁ}ÞÈ×½Y'çãwÖô#ï·3&}ša¹_,ï¯ÊñÛü»ð½ÞSÊûò9(Ÿ?ŠýWŒSÁ°lf>;ٻŵšíþ)™Î¶ª;ùbëPÆÏëÉü±‚2“Ä}¡Vb>£¶x­åõO¾RVüaÈÀ#ªÑtò¨N|=oÂ8oß¼l´‹™§åeSh{Öœ]IKƱVží¤ ¦>ÏnÒèÁ8‹×¼Ò®óŸv}ß—Ú'|xóà0:¼¨®ã"}>ái.G›fÌš_¾e(5IÞQªÎ“n<f«:7v‰dýw³[Ò´¿bЏ·=•¹±Àô9ÓÒ­B‰w~œ;ÖrÎz³~µÝŸÆ…ÓiÖm8ñÏԢ߆<ŒÚþÞŽ&±àÖ#¦P“òUž·ý)ŒçÆáû{˜q¥tÑ*…œê=œ©}4‹ñ}Åêr±Ñx‡üX3óAkOÚÜ·2ËõKbžAÆ«U§xW¨Dk*ž+jSŽÎ”¿ýK­ãˆÏ?x\«0?ŸØÇ¤Û•A)ôàSrÀ#sXÅéaÊ »Ò¢ïw-6IìCºQtëosO4p'ùž¾üù^wŠY>×sXZN”ëŒÃÏõu¬À仢§Ðñ£wöû:.`#GFZÍ ¦‚¾.³|çMcå?l\UïZqÑr*)3óYÎ äu1¿‡ÒQü½XG`³F}P«Õ¼¢¾ýÕ2nÊü¹gî"fóèÃû=>t°æ§s e„¨™üùyßE^wÉ÷îø¹¸§Å‡mÎŒƒIKµxŸý¬ìÔË/®çK¥Þíš±˜µqH1êi/:]©r‚3÷»³Â¹Ôš÷þ±a J¦ñyXqª«Šÿõ|û‚âûDn’‘»ÏŒ#EO\“ìnálŸS R©lgõ­ '–°Ò/ÏÝ\Ø£'½kßeÒ¯¿D0±ÿÀf^,U{¶{ åÌz»¢MŸØ²³ÌC\¥m?ûá9öæùƒqÌ×íËdÍï®|;É'•:fTï²ç‡¥ìp™òßôú¨!îžÁÊfVŽô²í`©ò|¦Ãäд1Q7Åç ò‰{ƒMžj ž?²Ué{=Zqï +zliE#S©­ëšªÊûËØÐÒ¿ ){Ô[œÓOeü¾ss±>nFÝo°ý™[ZˆûV7Ä8w•üüÂYìWWãùƒqFÝÙüqrT"S¬¹çþ~^*•/aò¶Ü öqKlåÓW|ˆ{Œ§0þõ•bò=«V›«oºÔ,÷;TòsÖ6ŸÝQaœSƒo^Q=Od‹§ÅNNK¥ §49º’Ív.Ó²áF?q4…ÉÿßOèIr<”|<®æ»ô|b^úQÉ}õ¾–<2çƙ߳£ng¿Cl§t¨B}ôžW|ÀÃ8Ö¸áiOuby ú5ÿÊS“™¼_.ö¿éÚ23<‹ó[qoá½Rþ:äósþ`œ£! ÝT>Ìú¯®e¸”Fî]?VðÙ°šÅ§îûPär_¹žY¼Ì󽥃çÎâÜ­‹x/¼P®Qi—cÒGËþ…užj1ÎŽ” wÏWÖ³o#25›cÓè¶n¤÷ȵìPéß5ßÜŸJ˜<žÉ÷Såû¨ò=ù|»|‘¾%èñS%Ÿßµ#þy'±ÞÁ8#Vœí’ý^Ï|êô/²%-\F¿>ï¿h=»òvôèŒ`zd÷I·Õ}k»8xçªD_’÷÷>M–&ÖJËù‡|oEþ| ßàëƹ0fTÙÇ˓؃óño¼I#¯fŽsoôÖ²³¾«Ì{L!Oפx|3Aœ_ú‘ü9yIösËûK¤ S•¸OÂ÷ãlg«Ìû*{„™f8Ñ82êJ47²G{?ì­Ô ˜6íÐ}°w8´dñÝöî–ÏkÉ÷òäs¾^xú/ï*0Î(6uÿ@ý¶ãš3ǦS¶:|Aû>›ØÆ“ãFà35ŠÕæ5=IÅVöÊ*¾…¥ºfÿx¬Bµ T…v˜:‰ÉïþO»Ÿ«õ¿å^ßÏúI)¿­ïýG`œ 1ë—u-qœ­ì½÷ðyß“TÝ­ôÚN¶°£¶µ©žåGæcÖÊáòþ„å܉ã$îû\çm·Äù‚‹˜ñú¦Å8)ŽNó[uœ™£þbøIºyöÔ§ÆÓ¶²Ã‘#NÜèKæëú·&°ý!µíö=lm¹'¯§ùóÎ+y^^ç•ÍEžUàùƒqÌû+{‚}ê¸È¾Ãá“ô±\­vgªncu>Ž˜½Ú‡<'äkÒ|Äx&ß³z^P:T‘¼ŸÌ÷#® ÿüÏJ£‘’Óçµûüóˆ&Œs×Mº x‚]Y¿dÜÀR§è檽çlŽmc‰oêT­‘é+>'=Ž8Ðqñãl¬ãÍü~N"¿·åspù¼ï³Ï55ÉVñ{½ŒåŒyºº÷)Òíˆ{5j;Ëg>Pð§ÛKªêvÇv4ñl×r¥'Éç·|>Ö™>JÔ¾>™kù<"¯×,÷ÂÌùƒqŒ«kÌæÍØ©«Íg‡¬8Eù^«R´S<3ýœ?zMÇ Êj”?dZqÌ>èvÕ½?x‰u¶ë: °ìg›óÏÃä0¶ÄÆWû~oæ­StÙëü/…Üv°±’=Ë7@•üO=uÛ:†ñóÆ´æ{›QšøQÛ9úÊ×[î3Éùh½ÏŒçÿPkSܱ|6­Cÿ}[eËž3ã×ÝÉú Ér|Ø:Dܯ c|>Öƒ¦¼ê»­n2~>üþñÏ÷ü~¿‰Ç­¸×‡ç›®©ËÌv2°û!J…qUMNvïê–]¬ÜÑ \[¢­vß·û)Þ–ÏÛÈûòóÍñç}ÛÕ½M\wã÷¼2ˆ=Ÿ=7{7«½½èÆÛÙ!´É¸ÿqbcúˆyz{±¯Á÷+ xN¯×¿îœd`N#Ç,¼8è4=)ðpië*{Y•ó7jM(î;eò~ŸohDýÖXî]l³áÿ|íÁôµÓ?©“ÜLúÞ6ÜOhí‘ùOû«EO`Ùåúwôb ÿÂQh'zcÊÞ­?r¹þ‘wKö6X»\C…ËUî,»\%ï–Qô ŽB;ÑÎ(ú1ýŸŒ”tR:½èiíÞ²vJÎkµðnI·+ߵΪßy–èMgíÝ’zžEáˆÿŸ«ÜG8C¸dþJÏs©“…Aì…G&Gø®ãD?É!õÆüZ3¿ÖÌ›fÍ´ß[– woY;"þÓ^ÂÑ—Svþþëˆ/ü[ö¢ìºù#Wá¹nä~êÖ®Â0á*”{tÊ®B©·z–è×!ü[ö¢‡T–p`ÿW„ävõnW©‡ìÄ þ¿%y]¥~ÂzÑË3ÆÊ骷ê%l}¦¬9R?á,ÑÓ3æ¿ÁW(÷õ4 OÄ_é',ù¯5( z Žˆ\átÕŠB!Ý„ûZ3¿ÖL›nÍ´ÿÎÁ ô"HÿJãáÕ±G0GP#¨uÀr€® y ÈþvpDÀÇ‚<,ÜÖ^0õlP#!tÀIño<ØÖ¾Â#|…_zu¾ì¿.yut¦=Ø%·ŽæOz)\DvÉñjí–z°Ë.± áw¾BÉ«£·r»æXõ?–cþ_x°¥ȶ ¹[GrÚ!yÃIûœøš‘Ä:`ßDZ×ãY@ƒ„Ö’:ä$·8"ÁcAþ­c#\ÿi?dðêD€ ±ê,¹]¢ß§ä+ÌøZ3¿ÖL›nÍ´_»É†»È¬ÿidуTv)þNì˜/|d’·"ÆÊÙóGNÅ?òöÈ=àe§¢£pbKî¹7ip®"õ”z#ò>¥1ÂG¦¨&õÿâ½Jÿ¤»Br½ ׫³•Ï'ô ™äy•ú$DÓX+Ç«ÁªG²ä"sþÂï#õI6‰ž¦±³ŸQkÝ×Tx+þJŸdɉíÂ`ŽÂY‘'¯ñÀ–xOyíךùµfÚüsk¦Ä;g0Ûü­gsŒðü(Ì1 hÔz @`Ç€\à7Gy,ÈÁö àŒ€A*œ²ãLrfh¾ðbç BHŠ˜ãŶö7Æ€\{îoüÒóóe_yÉó£ ¥½å%×ÿŸôi¨Dù¼ZŸ»±ã¬h’_ÃQx±s…çÇ`å±Íµêÿ,ùЂ¿ðbK= ír×äp´o$ÝAÀø@ƒ$Ö7–ÎÒð, ü‘Ðàˆ¤Žy Éœ‘àq"ÉC¿pýضâÞÿ´'´^x~b@.ð·ê -¹n³DßgÉßhüZ3¿ÖL›nÍt_[Ž w£Y»8âD°†#pAÐj-7 d8ØæÎÛx+/äßáìŽýÂ&ù8b­\CÿÊ ùï\Crß{Ù)¹†"€¸ y´ ¸ ‰b@P §·äGsDRÅ€áõþ3NÉ}*Ü·.µ÷…}áG“’1¸.ï•Œ³rÞJn4g$hœp£¹|á'Ò‚à"zçÿ.Éx`‡d&áãÐ{$~È=P Ä€\àb`Ž’ä`† à,\Rœ·:`G¼§¾ä—bRúG®™ÿiü»jãÕÄ¿Rÿ• MªsÿUmû¯jšu-û²ŽÉµKªWÿ[j•\§¤Ÿ±Î†;jƒÀu(N¼@C¸ i-‚$ dIŽZJ,ÈþpDÐÄ‚<ŒàÉΠ8D¡À\LZ`kå8“<þV¾í\àzcލ7±ÿƵ-;cˆyö6ï^Ú¬ö_{5âD`Z;³µV>³XáS ¾YÉe–ç(}žíw/¶äéQ xc@.ðo,ÝãÂÿµ ä`üÀ3€3;Nw(0¹Ø"ÐþpôHnÙp`j¿Ø#"@Ð ô@dˆ¹ÀIa~žXמ»e3:p¯¬IùuÞôuÞô?_‹´6ÿzÞä"ÆÎµá3-È.T-°E°†, BÐÆ;n805Xì…SVgågü;|Úq_xÌœ…S[vý+G㹂TH”x`gåhtAÒÄ€, Bòă¼Ê’c?s 4³-yÌœ‘T± W¸¶c@®´î¬!õ)F)ðïAžBêˉ¯¯&wˆ ·¬äÛ–½Bá_xÌ$¯l(0—zR‹SVr˜¹Ô—>CËfª/|Cñ ×IúÌ‹ôù¿×í¨öHö4Hz=P ñc@.ðG €#Š@,ÈÁ(À!N…P`.(Z`«äNY=º †wpK1-ýc]3ÿj½üÿ¡VJuRª‘Rm”ê⟩‰w=´®…R üßZû¶ŠŸ±(`áÀA ŒÀÁ¦¶¨ua ¨|ñÀL@%œÎÆ8‚”À'‚3 ‚T l¨a ¨°ñÀAö…-[9´cAF­ËÎì¸ãЖ’C;N}°p6Ú"øÃ„³1I`/¼´új’çV—úïãwr¿Bê3¯ ‰ Ôµ¤>£øojîÊŽÿ—æ,<ÙRâ„‚ á©I 2¤?­ÙöH(G$T,Èk$}¾ÿàŒäŠ  ŒÀ‰¦¶H¶0THºx`‡Ä & Fê€=’0ä ’QHÈ ü‘˜àˆäŒy Išœ‘¨q"YC…ÍYÉ}µR'MéÓsY_÷ɾÎ÷lþy5Ožï©Å³ó¤Ÿ!4ä5Ø!Xà ¨´:`À9@ƒÖ…pÖê­œ‡[+‚? ’@ l‘aà†Ì*$G<°C‚„P#QtÀÞÊ©BÒÄP#yt"Ô äIkYáéÎ.Hª8'\ݱ ¯ºäò@MÎ ü{ %[(0ÖäîÚðZÜ]«Fòé€}©¿(ž 4HD½ðÖ†¬ºRO0|ýVÎÚ,G© þN$©ĉd :ÔHÜø¿Ù1© ${ ÈþHzpDâÇ‚<ŒœQâD!Fà"9½-ŠBÈ*‡x`§äÎZ>%}"\r|[{kÿê>Ùÿm]üﮉÖûeRÊ5ïÏÔº¿«ÎYï£ýoªgÒÏÈ ýޤ3QHP¡FÅ;Ô¨p`jÔ(°GE€éTQ(0“Ø" Â@P!°â‚+˜€A¦ö´ àŒ`‹jåÒŽ/éP`.¨GÚãÒ&áÒÖ[¬6\ºSWEòKJN9É·&¹£ð=#pcA^ É‘‚¯8Kûi5¹«6ÁlWûsG¶Ø;H=_¥þ¥øš„Û5& …ÃV lôaõwb‡!ð3€3‚?N$@(0—&ÒçÓðß Â@P!)â#˜€ ¢öH’4H=P ab@.ðGâ€#’'ä`$QpF"ʼnd Fà‚¤Ò[á§Íêðu¾õu¾õ?_Ÿ´6ÿz¾å/þ[)8ƒä5Uì¬ h´z @àÆ€\à6Hçâp`væð¿ÉÁlüa ¨ñÀ‰²€ ìáÀÔH°G‚D€ A¢èÉL@¤‰9@#[iO­Šä+¶1'R°ðtçV•œ’·ÒÆœX’«;N$X(0…äUÂ3la 8×’Üh|úÚRïz|Ÿ ø;H½¢ñµ"ÃëJ}kñl$¥Ø!1Ã¥Þ„ø;$¨ØJwÞH}¸ð¿¥zô"i5@l‘¼a «‘ô9|ýÀ‰L@„Ö{$uÈ$·(à1 ø#Ñ ÀÉ ò@0’>8#ñãDò‡#pAÐ[‚0TÂïm‡¢L@â ö(¡ HŸV'îú–òA„åÿØ™äGMü1×ú³õíë\K®cÒ÷›!}½¨S& FÒ{Ô¨4¨Qz @Å€\i_ ¤¶¦0Tªx`‡À & F€é€=‚,ä ‚M8#pAÐi-/ dj’Ø¢&…, BMŠvÎp`j©Ø#P#@Ž´ÿ€µCÀÚ#`#¤»eZGés Ú ®37No(0—š’— ù tfûÚ’Ïϵ¾Žä%ÀÏÀAꃎq€êK8Ȫ'õ´Åß;|80J ð]¤Ï$ ðÀ¥‘Ô;ß üöުɬëûÆ2 VìØcìØ²ƒ ¢†š†" 6 :–ر£cÁ6bÇŽ‚±œPADŒìQQ±û✓ýÆïwÍû­ûy¿q­ÿ➇³!Ù{_§åÿ+è)x#àÏ¡ô±ÂQj”-E,Ê C*EI±@”(IJƒ’a±¨P†X0‘¨r” 'e„ÅÅ (•ƒ2ÅBŠFéb1…¢ P,ª”>ö’p”Z¸W‹EFÚé¿@¿ª{qtV2^‹Ox ¼=beè¯g9³×QÂý®Ä¯üÎZÖ—k}ЬÛÑuïd2þ~B—'1¿ÇoxZ:F%’Æ¿|°þ£c"ÑŒÿ×¶ËE0¸:Û«v½XÒ&0¿Ý-=/øœ¨ {qg2¡|;­¿<ã½h¹$ü+õ-Ô/‹Æaœ„?J4 ‰Îš9ÍB/¹G/¯¹LŠ%]mgï JqêF^? =ü6vp?“V5ÇêÇ*Áq(?*‘|•œ{×E8þÌcÖÑ´XrsZäÊ·=] Ö«¸5OöNá<-—‘ûÍãÈqœ ëçš,ÕK"# Y0öùE8²~êîíMŽÊ“s†™ãÝüþ˜J¸?õƒ¨îÏ¡Ï0æ÷3¼b޳¢[ÏU‡%‘}+N­™ ÅÃǸ1ú™ž:ÈÿV² \ŽÞ1wþò©„û@SFk8ñµÊµ>VZ<Ê1ƒ¿^·^Í|ˆqü"oÁ™2‰Ì{?aÁLX6ñR’Õ¹cäúÌœÏë»ÃŒ £Á)„ú„9hý’¹¿Ÿó+û©pü¦Æ:IÄJ´Ñô•$ ~ÿKLõ¶Ç‰(o⨚Ù@ý“'“›N5gXD¹õ]ëÇÞÿ¾põÓ Â=î÷Å”?v_Lù fßúvcœ¬<Áˆ$™TØh¬ÊƒICßY~œ´}9T6Í̉éç8‘pßTî£Å}œ¸/ ÿšuòʵ¹ù7¼"Þ%’»kï $O&' Ì>>Ìbþwqdú‡­M›í ïÜæO2Šù[1NÅ àœ+ΡS0¹o|0NØ|ã³ò’Ix~”~˜Ý%(i]u癹q$´ðzèç£`ü‘Í%³‚å&ÊûñS¾õ-w”ûâq_wêWFãH0Å<§ÂðØÔ Û/AÁüâ–V:ñ¤ëÔŽ¯”ÕäöqÒÇ¿&e!‚A– ‡TÙ)O³æG œ§Eýƒ€ñw'Çß8eÝ‚Âê)Dp»‹½w ,Råñ³çÄ“Ö u9¥Žâþ4èXñ>“دÎ(àþ=ÉÑ»2RmµãS_~:®Ç}ø öãª;ÉÄGýJÖ·O6䄘ÄÖ~O¦Èµrö† Ì‘A¡ï‡§Ö·«Žlr“å·­iþã8GòãDëÞ%“ãW¬SDeÃÆ©ž ?Ž:AdùMWŽíãÁ8…þ„ú‹¹íúø»C!ñÎ/ öqg>fÔG…ãMm} K†Y ™¼dXÀê—!îÀÄ_Ï ”;åÍüå­/ÔML\V¼6Ù!2ø×¡‡ßÊ d>L/ÄÔÇî©xÿù×Ë– aýŽú–©1ÎÀåÅû¿¥ÙÚá›|/ƒè€Ù›µú'Ið¡÷g*œaë^Ì|`‡ã‡‰c7;hùÑÜOŒò«ì`c5‹ü“¾4ŽNŸIäö•Þ&{RÈÛ7o$]†Œ«­êy$§–ÏÌ»âÂÇÎd>­_"÷åäå‘ÙAå÷S„q&je¸$¦Ê©ÎÛ:«¢úí8I\í§ Œnà”- ×o˜D¼jÇådqc~æ@y’½´9ž/Yþ—ÒEÓ-¿ña“`œag—ÝH!X\K«;ç0~äI¢]4_nílމi>™4ë!¹r̓åyoVÇ æ•åýOm»#æÏE:¾ ów¢¾XrŒS·-»À7)Ämö‘“~ssÀIçZÙã§H5óz†’ÓN0bÝBëi ÷£ãþØœSÉy{ÔÏΘùSn Ç¿:ÃåÓí©äAצ¤Ë¡¸×qÁZåìSd|ÎÉK¸Â£Í&7o3.…»–ÛÎùaü÷ç|Bêwh´¿RNA4Æ9ÿ6k­NÿT2kî‚·ïåÀÀÀÇÄàæ)b<ÖÌað8÷ùtpzxÙî9{ªk´›–×Íû+ï§{šÀuÊWSḔŸJäñ>\l†}¢$&;N iüÕ6 ¼ñeýhæïç®ÍKê×ù3ßsßòw/6ÖÈüg™Ÿ—çIæ¥2¾m.Üÿ"`žJI|&eÕ½óz$܉WeB„w»ùmw \xsÆ“7Óæ÷ì”6»m½A@ý¢Ù<§o‰äzãô§Ýg¦’E'·äš -nêGÌ2?Mš¾k»©Þ¶‘Ì×{9¶ûÖìU«Ý˜¾+¼Í»inJaºs§NQ©_Ų¦/ÝžÿRLyà>À¹¼u‚qB"NïI%»~ßsíãÊ\Ð,î×5ôøib-à5_{A¿3ÏÛ:„’=ÖŸê°ÕùoÙh}R©Ú[æOöV<Çqþ—A«eŒ_:Ö ÆºX+ŸTbpl„Njz.4K^>åMóÒgÏæ/kV¹Cé¹ÞÕu.L þxhžZþ,çPp?<Î÷åó.êçN9ÅrŒ#¸îÕ–¤’ü-ޝ]_åÂ@ÅêÚHÌñâ‘Ë7#„\=*< å@ùæZž'÷?ã~bÇóg’ÜUC™7õV`œ4 h•J}ÑMVtʃ™öüåhiýÁô~ì`hۨ˥‡“‚‰k°hÃ¥V~ZŸµsBÎNùÝVË…âýÞ®¾ÕÁßûº€`³¹šú›Gcœ«ÞŒò}SI¿àÒíj¿<˜qr§a·; dv“®v¥á6Ÿqç÷რå|ûjùÒ9£Jì*±Ñr÷_rbÐÎÇbîC¦{ÔçF¨ÌžÖÆ¡œ‹Tò~WLßw‘y`9úÓÇµÏæuçt_e­ˆ¬.Ôë:ɇ½ŽfÌïÕ\ë#Èçó”‹>8½¢~0NÕ .Ã¥’qzâò@¯±ÛåçýÏÍ·Ú… ±ÊÏ $W{ø¢¨YC_Àxj˜×­…'£$Ä;éÞwêokÇçM96fW„ãVØqL!Íê6_í¤wyúçèí8C ¿ÜnoYU >ûÖl¸xÓ—pý½B`’–@}çé¸7Æ´ÞYÏç)DȲ†¶W@æ¹cWìÃ3d\ÛE¦ÙN@ýÞG’°í­~‰˜ï_õzºåË=aÈý3ë¼µ>x|þW™c Çñ§T,4RØ|ö ŒÛußø,Q/T½Õ Wæ{çEVÌ›Ú \½SŸêÅø•ƒØs”®T8N˜ƒÐSˆF¥“´¤a>LyqgÖçõ»–A‚êÁ±À9QUŸPtÉÙä57˜áá ì|‹ï98ãçÆñL¯YÝ[šBz¦oxð¤O>ZŽæä‰s’­ÐyžÇ¡óÓâø®Šíø»ùpÛ¤y,Ü?Oz$H]ÖŒv€W €>‚7Ñj 5wÑÚ­ùûÝ{Î@ùŒ”÷¢ÀñŒ½fd|½ŸL.ŸûG«jcØÀ¼9! Û® œl ûì`³1äìã*¯#»Œ‚‚“†q–ÇõW¤Wgg<ü¥o•ÇbÎ;¡ý’ùÇcœ–Áæ R’Éʼ=síº^…:¯ÆTϵ#¤ËìYÛz9ÚB§§7í¬ËƒåoŒb}ÔL»¿Àç¥Ô—ñ©8uuïé=ÚlÂU×Õi´ª0N.ic2y5«>ª®Â£ÞÞ³ ¹1qíòko‡‚T#|©ýÉ;Øä Œ¦åOñ8ùŠãM=ÙÕ=™Œi£jOæ\…ƒýÂÖ¾;CHÏKºÓãÙ:Úƒ>ekOí¼rY©O²Žq‰äÍüa¯Ú'“Á œNŽ ï?-ÿøøq8æqå|7Ʊ#ê­ öœKt‡V3DÇm¡Â¦y³¯–‡]‘‡8Þ“åÍj'“!ÓöŒÞ]|ô»­=ÝDEš;pÚ¤…[Y‘¥Ö;FøVv;õëöÕ®o)?þÕãÉм5ÖòéÉ‹ûWü½j\ƒ:öЄªÈÆÏ W¶ý:V·v¹k–æJø<‰¯Ûèè¸î¼¼6{µ;”6ŠwÏõ´„{}ëS&Þ)ž €ïsóŠzÁ8AÖc&j%’9.ÉsF¿g‡Äº~»$â<ÂèðFì'§ûŽ=ùÊ4Ü9˲¶žLËU£\7SíºOË婨߰dœ4úüQaœ¾þN~üªýH£Ÿv²‡I”û¤I¤ÿÞ¦:›Vx@Ì*WûËe~äãÄ㾉Ÿq&+¾õŸ–÷’ÀÜSÒ'NÓˆ#Ÿ÷Þù%啘óä¨ß0ãŠ`œ¸Ž’a=¼ÉÊ--‚Å%×!Å7eSÂâ$bÒqwÇÖÞlÕ8U<0|´~Ñ´0l€ú’¿Ó¿«\îX4²Þ™¶ßøy+0΂1âö{U¤Ï´²R—‘µÒíÉd²ʈãܵë1>ç¯ÛÃêã'—ïvf\>=-÷£Î±êT ÔrÑ+êãÐõ•Š 8Ñ×aüþøÜøÂ<÷Ìd2öQý²ƒ.°Ý¼{þæ‡ãÉP¸óôZ˜7Ræ»ÛXMº°å“,=s5{ùà¼ØÖ±3œÝÂ@xÚnø…r“TgÌäÞ5®MR‘ ck_šZ;Ú¼J&KpÕÑ2À pRcn;bñp'>R×lÓ×Ï´a}Ô(Ÿ«”ñXjkÏ?*sÔÇzOƒ¾ÍU¤Ã™¼>N/  ¼lvѪ&)dµó”Ž¿Já䰗ʘ¦“ˆáæš±éGGhç[§›OP•,±Ô®§8§”®“­¾ÙïÖP"éTÚ«,"—Š¶Ôª>™ïèãa’BâKúô×+–£€ýoæìϹVÚ} ¾.æùö<(hÉ‘¯Ä‡ÌçÅ:²gÏYš×"ŒÓ¬â Ñ=¼¤W£Á…°dœÈgœg ã”8ÁÞWÓ^™D输˜íƒÑö:|/†ß/ø"¦\NWƧ\A Æq›Ÿz'ëèy’Ô°¾á–É…p³z¶n§ù)¤')sÜá ¦ öÓ¯ʵçXtïÁæ{ÎŒ+XCË)®“‘‘rx¾ pÑòp ØÒúÁ8WÞµ˜¯Ïy¢—ÔD³¯|ÊßçÃõêýòº>žà·ëÐmwRˆû£çfœ“ Úù`« LS7=ç\ô«iöõ£˜®ã%lŸ™ÍÓ0ŽNXOÅÉs¤Yû=……°ñ7“¬Å¸În7·mÊÂ%£ v­}Vvv'úqÇï;ì”Ë×_ûþðó.¾B¹UÀö›èü ãdä\ú"9G<_WïÚ×BçŒNÍì™J^Ô»Þãº/4yW¾ÈÝŒ^93ÂfºTË;å\`¾/ÿòù íëtßQ…qââÃN'ž%®¬‹jŠnÀæñÙÎᩤpaÈ€Å'ýØú},«S;àQÎ#àóz.ôRLãK¾ág©1ÎûÆ:÷š÷;K¶‹ŽYHn€QÚ´ÃaI©Ä­‡õÝÕý ÀsKÚ[ÏPÆ/±†»ŸŠ·‡Ù +Öʵ¼‚.ë5 GÎub|XSZ?f%’Óò–ôŒ9CZ^siºÈç¸,ð{U#¨Üˆ¼1 è¹I¨–ÇÂ8¬Z>tt§9¿­lôFÌy¯Ô¿ßñ~§ã,k%aϱ³×äl»Cu­[¦‘Nþúq¯:{²ý« ìù3oˆñ M´ó7ÎIá¨Êë Æ9©?zá¦B9@7`­o¨áõ‰i„r ] ÇØhŒ¶Néû` |=ÃÏñ8ˆ×'}Ÿè>·ãlžwiå£Ólß÷” Ç_ëÒ.Ö[;Õ‘Âùݽ=‡¸ú› YÇÎl£}^Ó~êü¼¯§x_ºšåYËö#å&)0Nîºms¤ÍN“£Ù½E¿»Îí—Ž´9–FºÔK ÕÊ8eÞ„ò|º»'š!¾þ漤ùJߋӦÜÓó`ûnôï‰Æ8ɧp•’ÙöîÁ ðó>óeBfickxáÈ;Æ—vgŽ~Znåì¾þ¨| Âq£Æ™ø:EVlO;÷¾îM°p2Ó´ûÀŒ‹J?8 Ç~MÌ´û&|ýÁÏUù¼½òúCq(ÿêÉÿõtrç>7Á;JÝ+±ú²{èîÂ;.RxÛap€,Õ‹ÿ4&ô÷4Òî›óýR~nBû&ãï ,‘WDždçÙ7aÕƒªæõ /_Yu|¯ã /öo}»n«?»§Ðˆðó~>Ïç·|®Œž£ÐýyƱ89Zåa’ùýüs§Ü#ƒSݤÈéš××Z?p…zç6u5͸½ºZ^ ÿ{xÞòç'åóõú•­s0ΖjÕÃÊÊO“@:½ :Õ¶˜{œ¨Ý¬Zÿ4 ÛHuü}n˜²}[K-WˆÿüžH£¹§>l3è›õ§ãÐuÞ Ò`•ÇÛŽ±7!¤å ¯NqȨGíH‡Ë8_ê;þëE{ÂÏù~9ß?ç÷øßÅ9?ÆÓˈ÷rÚ/§ïŠ3–ñnñä~ÁÌÅ¡Y7áª,¨ÝÞGH³_ýÛݶ÷ãN³K‚Øû.Ñr¶ø|×Ï7Ê—hù9u‚q‚ž·»óÛÜ8B÷ÛnBNø…lEÛtb:¼Öë6Ažð*V:vóôq„ógù9תCŸútpbÏ廌wüNË{ªœ*Œ“2ÃtÞ£øã$¡·…ïÒš·àÅ9½ËëÒÉçý+Tq2ÈÛçZ¶$p¡ý Öi§Ëó¥s€rC¡°éÑä»óªç²Q®¢/,í,Mi¿x­Œc=aqKÍûcäqµŒKúÜ‚žÏz5½3/èŒüðÊgÌM*<<ºÏ8-yo-›NS«†À„ó ŠâŽLb\©ú@ϪA¹$ª‡ÇhvéAëǼDòio¬“åàcdÞ,H{ ö­Xûp:±Q]O[5Ë®™ 'g„sé~‰#Øg/µˆ_íÊÎ!«³yÇ/@_7©öyTQ?g£q« 5æÄ’…gâ¦Ý\t ~Û4ùrõüt¢ÌõÚ8ÕÓ‰Í}ã~NKç|¿\ìU²pjDǯbº.æy`FëãTàrÌŽÚ×oÁ¶êŠ)ߦ“Ö/¬¿&,•=gÆ÷#ˆ0Ûk’fÃòÚ,Ùn(**S`™˜Ÿ³}ÃaÇ8Íê9½ëëa²p›ßUQÖ-Øy^U³V³ ÒãÈõŒá#€#ö\a¯¯lYNžé9iïx<Ô2¯ó31çà|ÃaÇ8Ž}„•ÇavÏæ¼ ÞxÑ8ƒ Ø]³,\ÊïaIíV«m.º‘TóncRM¬Ùù€ð}´ïïÐßæu4Æi¸tPfÂÜCdU‡kwuŠ`Û£‰ÎÃ<3ˆÿ»ðY×"!mY ‡d/qãŒÕå®d)6k{€ŸûS~¦Zˤ÷q$l_‚ñ{1ŽºëÑÓÆIů۴Z¹U /Î ÎVÓ( ePûiï5yã&–_äVƒáµG–õמgñç3ïtEüÍ9šãøùMñ·Ç)êXcÞz«]¤lÙ¾6cB‹`a¡pšI$º ;mã ·S…‹ZþldO87›Îc‡kyìyã%ì9£w~¡û±Eð\¸æ8'“4ê­êuÊLÆy­+ùºoèÈž‡ŽŒ;9D»>Q º<:d;W¡Ï%Žoç¿ê@çhjµâúÙEpÍ;ã‘M\&9h;°ãØdw¶æÇ÷]غußD;>¿7GŸÃ´žÔ8~Ÿé=Úµ-ÛJúŽ8Ò%²°^¹¿®ÓíL2û7·Ø»€Þí-ãÝG‘§${Ø–IžœëûMýëˆK$3Mš6®1z3±Zëq.ùY<¨·%=ë—,Q¯sH½Nì~¨9tXØIøz¥27[„㘎iÑøRµ(²9ïƒWUMLìvθe·,Rw~Á½ËUœAØ=lÐÔ•ŸC‹—[{ˆâ‡ÃõÅË&}él­]¿Vä#Ž×õóÂ8¯Ìu¤ì½a‡ž:ÅðöTuõv§,ÒüÙ¾mo»°uÖoÅóÓ‡å½ ã :iשG¼†;7 x æ÷2*óIå§‹yð´"¯5Äb~Ë.^‹!õ–g¦ÞÜ,Ò«Z7ÝfºPµq&GÌòö"ìü^š™l±ÕÞÏäñø>_åûP Œ3ëÄ.Ó!­W’9-Þ=4q(:oÊ"^›OÞhcàÅæ«Îä~°03‘οäü5§"q¼Ù"ž9¹„SçNÔèb¨qóžºÇ“,ò{ݧö´ÉÖí®dmø 3÷uN„?Çøºª"OqœíÇŒÈ0Ö‹¾ýnb1$xíê•Ýå¹Ñ›Ül#v¯‰ÌìR¥·ÞBï7[³ûAöÚ¾\‘—8^S£y^Ó_O!ÆSÛ4ù:½'–挽D¢3<ú®œ'×®/ؼž;~f`=»ÚùXåù—”Hhýù’V'’ÊÎ-.†Ü{V×:|‰¨›§×[ÔUô>· ‰Ž'sö°&%ÉqÕ›WÁu+›s^$ÿJïÓýKŽO×;R°œc<¹h]1Ôo^ùþÉ%²DÀOÉöä„í#~.ÃyÞ|¾Z‘¿8݇›Fʇ«¾l,†jíÇÜ’tÎ&”WîÅö!†ðó_ReoV9yýYßëþÞÕ>¿ùýz¿€î‡Ê1N›eUg·1ôš.éK6ƒ¬ MŠÇ:µ|ØÄØó˜¬É­bWpÊŸ£4‹ |?Ÿß·¤ó©Çbz¯ÙŠ­#é=fÆ¡<ð óðb®“=pÿ²l2 mÔnßAn,Ÿ- ݇Mø¾;}nZå8kÄ<ï°{¨8~óIU‚×õ\ 5F·Ö3­ê'ç¬~Ó—1N‹˜s·OZ©_v¡ä f“Úíß5?ëâÈæ•öl~àOžµ}áÚÍŠÍ,aŠÓþk.<ós ÊC¶þ¶?cœÛË{ݸ·hlÃ.׿|1<òZ~æá‡l²íð%ý‚‡RX9²®´Eˆ5™Ò¡{×»Sü´ïKÛ7×»uW»ùRÌ_·Îf™Ý¬§TÔ…¤DR§ÚïÔ»!§kß=oNƒqŸÂ¥·Z_&n«ú9KÅ….ò`pgõu[?b\:tâÜžölßÖŠåí+-?> ö®ËX_›oïIø¹Õð®a§,†»­EF ¾LäV™Mlwe¯³'áën¾<ñ¥æj™–д0L|gÍ31ŸÿÒóA[¶ÉÖÁç¨÷Ä-ûõ·C”AfÙДbȰ»øëÔË„sipOöúŒ"ŸqõÖpQ nýµìl¦]$¯[Ûëø‘'Ú}jºoÁÎùpüŠí܇Ñ2kF•·¹Å¥ýǹLêj²ú‚6±{w2¨4z¨iY áuÇ÷Ayð}<ºa •9ž ŒÓ2ÎØ_íw´•ªYäCÛ,“§º//“ŽoÞ/¸íË÷o‰pŠÔ|…/ãmZÝŸ³¾úšñg?‹§\N°Éœ‡ó˜Š÷‘ÝÿÀ8Ë¥F»Á­b] ôþ^¹YÐmÉ$?öœl”ÃêB" Ë.ì^’¬ÓtÙS'±†–{ýbÍ%××­|Ù~å^«0ŽbˆKÌ„ž{!r÷î%;®bŸYÔlUo˶OìËÖkÍØ} {ÒtÂ{[û™2°÷é³jÍSGXžŸz*qsulU8Toúg1½ÏÀÎeé9¼ãÔœ3>Ù0ŒÛŒ;v³¶YwIûÃ/‡¬ˆß±Õ(˜öÁÒg·Ô†ß·&ô^™=ûÜ„5Ø=»ùÕõÇbzŸë£8×rchiáßò›-K$—¯w߸2{?\œX¶ëN1$*Jª>7‡L»ø%ÿÊN8³ñã}C‰ ¡\aoÂ×ô>¤©6νåoÃÛµÎöóéø"ÿI CÃÁ¡uÛÙ‹!hKË+6äí ïYmNrOœmåÄÊåÍúóç¡vŸ½¢pœ›‹š­Œ?Ï®õi¾ª¤Z»)tØŸCÊãê}ÎÈvÔæë¦yËß5"›£:ØI€ïòõ÷ÓÏÕÈq\zê¨ßlŠ?(†ÇßæMLÈ!níz6¼‡#ãKÉò–§¢¶· !ôyà«W\ã3ÆC{O›¯c8O¸òzLqšW¼0GáÍЯ’ÛÅpK#7ꘕC<ß«)§:ñþ@‚l ÅðMc ÿ|?_¢ã©Å|É÷ñ*ŸGcœüQGcÕsb!bá‹ÏuoÃ;Çã–™·sH\¿*9“»ðû½D61¿Ö•w!„ßoâ÷©yœŠ|Çñö¿}ž%9;5ÂD¯žêï~×çm=íH±;냉ÏRÉ›úíÇ~îÊ×ÅùŒãô€Ðõ¿êïé¯]–ܾ©g ÜX?—<Ý2xÀÝžì^»ñ×ëÿta‘‡ÕûCo˜û˜3ðùkEÞ*‘˜=q^•q÷8ìé.4¢b8X¤ê™ß-—$…7Ã|Øýâ¡Dª“š¾v@ iþU|/ ç]îÙÅÍÇ»úiï©ñ¼ jvžHŸ÷"Œ“üÚéXï£qðX$\p+†ÇnS"gÙç’cÞê­ŽÅŸÄ9~r/£nZ=­»ìëí=ÞÊõ'ÁñÏ 1v¿NûÇWÁ×7¶©µÝ³\Òî¶}˸5r`ërrkÈ„a =| ¿—”¬ç°oêÀÿWŸçuIû5»GŠqúQšv596Ö½gTË*‹^¡ù£Wä¬ówÊîËٽѤè¸Õ§ä # ¿ÿÎ×Éü^¿§J_7¶Ï‰ãß•5;•«s>š…fG`ÿUž÷X×áH.ù¸(øüºL9°ó'9cµ¬Š¡çæbà÷ìø=?~.É?Q‘×8>½Ït¤iåF¿Ã4—È6—sÉó;·BÇØÈ}¾…=§‚ȨšÙÍo „aõq 8øëÃïßÍ_åvb÷.sv˜ÝãÀ8Á5½Ý³tOAùþ®ýô3‹!ºÞÔ1u_ä\ u›S>’=ßlÙùãXvÿaÐ{7´œy>_åç9•÷3ÔçòÎé]Õœ‚½]Ðs1ôš?ö÷^uóÈßK§ÊÚøh¹ÞÃ+.è…¾/Ï¿ò~ÃϽùù]7Ó×MǪDòhVøQ%\}ö´[}|_΄MΙØ%¼ÏmÒ ¤›—ö~ë:¡ý6 !ü\‹?ïùý'γ§ùkÊ>GE߯Q>º ½ÏõR{¿®r•`œo4£wꞆ¹‹²Ö[ž(†ë›ßÙ}õÎ#uöHb˞Ƞü®ð Fv.&…ú9G;6_äŸS+_H›\¥MÍ21=Ÿ²Ó>'*ê㤠ÇOƒÅ×íqžßtÖ}QŸiydöƒ‡ªf™@?w3˜|Ü0À§i£±„ìÎUµjº#À¯ÅÛ3ß‹é~Æ+íó·ò¹ºãœvÙÓãÈËÓPq++¦NŸ1hÜlMétúÕWoV7´çtd¡=ç8µº³ájåK±i ð5góBºãÓõGÌ4ðܶuG1˜öïfly4´j™ùKx+¹v~cåQþÁscˆö¹ÉÏøçvšèy×…ç/Å+/|5¯k ô>[O`œ`¯Kf÷>&@Ï*åû\–CLë/›åâëµ~ÖÓžR?ø oÞ2¡°LìnXçb¿íç_Ø:øýº¾¯Æ»ÚìÌ–›› ¤ŸËUcy¸ðÉ›3`QoÁûnÅ0PàgSžGL—Õ|Q£{ÿÜÙ6ºéŽ‚Z!$déúÜôWèy¡›¾E8 ^ã+ê`6ãiRÈ’bvS{ÞZQ7Ö%ºŸyFßn’øÕ­ÂŽ;¥lw…ÐsåÑlÿd¡÷“Æþ¹:ÚÏF°|þÌö]~:¿qzƒæ™ãtï®l«Q…ú³ê¸m )†W‹æ^Ø;ô IÓßã§»'@Û×FV, ƒÈ¡ìšJ´yÄû&}ÝÊÄ.Uýö¼o «Ž59ß™æã ü:­ðèÀsàüä³ÍÀ Å`µãhˆgØò²ãª;nýØ=îÁ„Þß "üs ü¾"ÿ< íûoÅüótÿƒæãL=X²ûÝ9ÈøzÿE±c1Ìk:mó†+dLýÔž[ìåüÞ1ó’[ÌC~7QeM×8ßw¸’Y¬kùù³¸“ƒŸççì<Ìù›ù±ãÐýÀóppæûmûâ|ÿÊæåñWȳ*ÕÏÍkäÅîö&“_Ϊ‘}ÚøsáêÆ¾ì¸; Ý=Çðîù¶ì}ü,®î™0|JÛ7ó£õƒq†Ýp)ê°…@µu‹[,êX µ6nýë¥+$ö¶²÷Ý}nÀîÏ‘å}°Ü2ŽlòŸán!û]Ÿãã©ëëõ>}è3ëk €Žçùc×€)’A°O‡þû× é_/¤ÿ6/$îÓ%üý9:Œ‡À<(uà¿Ì8ª«E†R¡ 1‘#Qå5(«EðD2dÕŸå[•2ÏqÕ¢a¬ã1^K:ã©VöW0?^ÎT嬫Øïü‘þβ2SU‰a1E0f‹ÀSU1_’HÆR•3£¿ðDâžã*æAù3|«ï}á¾÷* fžH:Œo¥bžHRÆj8ª¡Œo%x"É{Z¿ç·¾½?b3üˆq%g¼–Êüiã©rïqÎl±ý µe‹"e °P¥”Ù¢d^”ÿöÎ{§Bç¿·w겿¯@‡ú–3?9Á»üÏ<1CPà5ÈQé(#Lä(–ÌÁŒAmÄØ€?˘Ñ0ÿ^×PÎ<Ì#ëÊ”1r#°²oóÌäœ@ΞQ~Ç£þ;o¹Êœ@Ê‹)’qF àédĘ Bq ÞN9Œ×ð#5÷ïMg~rǪ)øŸ'5Ê 4eÀ<5•ÌONð7Og jã5lÀpæµ)0¨åŒ§jðßæ<Ïĵ f̆ÊLUCÆä>¾œÛ ý ®j)JŠBi.ÜÀï¡4”Û b¾rBŽ ÿþ'ôÎÿ‰}óßžù¿×3õÙÿ]· åÛp. ä| XÎx‘¨r¡wb§£Œ0‰#+qÿþ´ˆù ÌCÆT26W0K~Ûïü…•¬¤ßq¨KQ¶X±(ƒ¿ñ»“bÑ(Q"ƹѠd‚Ï0Ê1nÏsÁƒ3ñm¢XQ£rP¦X\Ñ(]þÞ¨õ¼ð~†;­û'>çßûnF3î´)ó9¸6,ÔTãÚÄ0®„±!Žªíwl›1ÄžŽb|ˆÊ,UÁï<º’?1gD(þ†§‹20îâ÷PRlJ áeDžy²ç™ÿÎ3uþ»{¦ûýÕ:Ìo¥DTýs/ÐpÆ/8Á¨”)&r4J—q"µ)cþ,'§œùDzÄüØ#LÂXŒmXÙ¿8’ù„~ÇÌQ}Ç£–bÁ(Q",š”%ÃâQ¡ þçP`椣Œ°˜¢XA Ìœ”)cEìœPTãDüˆAÍ}‹N„éO0tïv[,ÊX”¦UŠ’b*Q"æ'*0¨õ±XC»R_QA-gœ§£`£Œ!ðTEßùŒþÈ×ýGlPÆŠ¨ÌT5b Eî_Ìy²ŸàªjP2sá^3~OðE•[P^„à_,œÚüOòzÿ·oþÿ«oŠP1(ý*”­ÃYж?`ëİĘQ,ƒQ9³9ªKñ?aP2?eaaÄxŠªJŒ1Áï]ú¯²Š1vdß±¨5()ˆ%Â"Q JQR,%J„Ò dX8*”!cì” ¼1ÁceÄø:B1£r[Gðz×Å E $X`1(}|1ÃQjõ^Žb÷3üiý?ñy7À¢T JQRÆÕÑ0Žb4›äØ¢"QjÆÕ‰eÅkË8oVú[çG,Å1¨£ £2sVð{©äÏÌyÃU> „ßCiP2 áŽ<þ¬˜z4—‹)‡úßùæ¿}S¡óßÛ7 ÙïW*¼ÏŒÇ¨B‰0Y¨R”“V‰1ïùÆÉE $˜È1(}ÆÉ¸ŒÆeüYöð2ÆÉЭG½è£›Ì–±2ԌώR kvÆ£U3FcePúwLmŒ eˆE‰*GɱxÒQF•Á¨”)S4ãe„¢ PÆÊÐÇâ G©[SNÆØÛúXtጓ!iÿ÷¬ R”‹R‰aaF 4(¾I*”!©‚1 °XÃQœÍÍ8",ÞTcu‡2­!s8J²ý _ûq„Â+£2‹Ö”ñsP¦•xòŸàÑ–>{ˆßCa³ˆb Càeä ÷¡~„BïúåŸõÊ¿ê?Û;°:à=ï¯úÜÿ×=îŸîoBOã=Œ÷­ÿÝžÅûÔ÷½éŸèKÂ3'V‡òÿŒÕ ¡‹='UÀx°ÑŒ‡ø³ì눺”/ƒ ¤ $ÿŽi]Ž’a2©„u+&”%þ¡BbrE¢ÊQrL²t”ãíÉÌø®¦Œµ£‹‰Š*`œ”>&a8J-ðv0cQ˜ ”„1/tÛÿ¿Zàë(P¥()ö %J„IÒR†ai7ÊЉfl×ÊÜœq ÿ”KÍx®BQWbZüÃU…2Ä¢‰D•› Ÿ£ÄŸc ×(VDÁÿΗþ/éüwÏ—LY|ð¿1I¨t”!&kJƒ’aÒªP†˜¸Œhˆ ŽR kNLäX”&³¥FÙþ‡Üc-G©PúO ÍØbË5Uʸ‰ T©°îd<×RÆN¬ÌñÉùŽi-G¥£Œ°h¢Xá£rÓš³CQ( S J *¥¸®XX±(,.ª%móc¾µ¥nG×ÇúÑ dX”*”!f$ª%ÇMGa‘F0Þ‹UR3εÀS¡ ±x#QjƽgW#,fª%Å¢ŽE üüJŠ®ü À]LÿŽå*aìÅcÁRð4Ä×ñÅþŽç*4‰`TÊÔBø¼7þ,6ŒPT˜ò¯…Üþýˆ+&ôɲG ýQèBOúá÷}ð¿±þŸèBïúÞ÷ýNèuÿDŸû¾Ç}ßßþ©Þ&¼æJáµÃ¦D‰°‡E 4Õ(‹ZàÅ ô1¹ÂQj”-&Y ã"þ'j#ÆKöÈ0!e˜±(LÊpÆ@ þŽ9-$©•Ž2ÄdD• ½ “6e„‰Å’7•ƒ2Å$ŽFéb"‡¢ PLè”>&u8J²ÅäŽE`‚+P¥()&º%Âd@Ùb_ŠAé·ÿ9Æ´ûRJƒ’a‘¨P†Ø—"Qå(M¹°&œ»b/Ò÷S°ˆ”ë’ ‹2À‚RüßðÏxÒ¶ŒEÆÙ¯X|±(],ÀPTŽÉßs_ÓQFXœQ¬@ƒQ9(S±ð™} (GúßyÛ¿ó6…Îï¼Í–_.¼O˜¤¨”&k$ª%ǤMGaâF2Æ¢&°UŠ’b"+Q"LæT)JŠIƒÒÇÄG©Q¶˜à±(Lrª%ÅdW2Æu°Ð÷„µ'&¾ƒ*gLX%JÃØ‹( JƸ°Æ_ŒAéc„3þbeæu0*eŠEÒÅ E0æ5g0†£Ô([,¦X””U*ða±°”(WJƒ’µù1[„E*EI±ø”(`Jƒ’a!ªP†XŒ‘¨r”‹2e„…ÅŠ3•ƒ2Å"D  ±X#„µ+ãc‡£ÒQFX¼Q¨RÆÊV0¬)sJ#ôN,je/%ˆßCiP²ÞÓKàSá÷P”¬¯À_ÁXŒß˜óÖ–1Õ([l1(}l¡?Á…ÕÅ&Š*0<‰ð{(}lábÁ[ÇZ#Â?¾Ïõ£~ùOöH¾ç%ôßÙ÷ú¿¥ïñ^ÇûÛ?5/úÖ?ݳ„×E¥C™Õ2ìO*”!ö§HT9J†ý)ÆF*EI1qbQÿ!§º@ølF]Ê~U}M$œO~Ç¡ÖÅ„ Få Œ0ñ¢Xò£rP¦˜„Ñ(]LÄPTJ‚ ƒÒǤ G©Q¶˜œ±(LPª%ÅDU¢D˜¬( J†I«BbâF¢JQRLàX”AûŸcObŸ‰D•£äØkÒQF˜üQ¬Þt¬pïM¸·!Ì¿°DØ7"P¥()ö ¥‘ÀpÄï¡4?àIK±pÊ+±_õ±x P¦?Á{ÍA™š þuø=,®PT…à…¯J(?úÿÄ]ßçXÿαþª_ýL¯’±ŸÓ‰*@™b’F±D Få L1a£P”)&nJƒ’a«P†˜Ä” 9e€É¬@•¢¤˜ÔJ”;¥AÉ0ÁUŒeŠÊú&{0*–%½¥B•£$X‘¨ráóeX±¨r”-D,Ê‹BRǶE $X,1(},˜p”e‹…‹2ÀâQ JQR,"%J„…Ò dXP*”!U$ª%oócöµ![$Jƒ’aÑ©P†Xx‘¨r” 0e„EÅ 1•ƒ2Å‚ŒFébQ†¢ P,Î(”e„E‰Ò0¶•ƒ2ÎPÆÅŽ@©Q,âHT¹ðù2,fÊ :Un$°añwAbqG¢Êû<@üÊ =U€2‚bE/EE¡J…ÿ e€M U`*øÕãkÒdžn&ø0ãkAC¸¹à‰ÿ "e |ép,”ô?˜WÕÑùgÎ+÷Å¿š[ýßØç¾ŸW}¶øgýëG½ëGçŠÿÔ¼JøÛÒQÂTŽJGa_ŠbS9J)“F„IÒ d˜ˆû‚j}¦öü:mΖúïÅÔwã‹xÛã{ý¬3n;õ aœ·q²e÷»%BtªSÇQzŘSúE”O&WµDtÎÞÏšÙ{?yöæùêwFê#îêÜ› Ø•ùÄ×î·Ný¥™oõ¹`×á¿Ùg/M„=¹¦ù/Š`ÒÅ[³:˜å““{ó[—{Bõ*}¦<ö#”Û2špŽ,çÕpƪs—r7…|sÕÊÜm9Æqt¨pxó Àw ïqr÷×|òò®Zýß{ÃdÇí—ex‘p¹Î«¥Ìg¶Ü3Õ„œîÛW˵äüê·×÷[Ÿ(ŒóÇæÌº‹Í’Àå«hm» E°Üc<Ë'¿?ê¶ëiÄHÆGµ'!ž*ž,C¸ï çrqŸ ¶uy=|/>îßAÿèî·Åü¼1õŸM‚M[o4ß{²¦Œ¹‘ݹǼú(îKü¯Ø·‹:Ž”Îÿ°a€PÿphÛIõaiãê@9¡Â†µ¥ô?fÆv¾ðaZY,Í_>fo¬˜Xü|˱|ò~G^ĦÙ#§¥:¡>àãH `K³9ÚîÌm’À8–ía£^‹UƒžU…»¢v›Å‡rî)­Œs~§×ÑG–ÉàÒ¦Hwù²"˜“Ú¢çäËùĹ÷íÚEÆ>0ùKçÆíÕòÔÀÝ»Õú±Ì¿Ë¨_ŒTë·ŸzÚ|Å×—¡èùñ€ZÒoüˆtlJ$»Jç­N\– n‰ò"X;ÑK‡<É'›&öÍt™îÅøwN¤ú=u^ï’1ZdîÎý39ç%ÔúÝ¡±ñæßðžE'ÛéøÖ…yÉpØhöÑv¦EðññIÿ{çÖd¶µ},£±cÇŽG쨤Q¤ hhŠFQlˆXEÅŽmŒ;¢`Û± EbƒPÄ`;**öo=Ù{çĹÆóÍÌ9ï{Þïýôºî¿æš,’¬{?»åþÝ"›Ûuä Õ„6­ëAlµ øq„ú¥Ž'Îù<_ç®Q.<í7 Ö #úÚæP^ öõ§ªƒæµ¼Eú¦vêfÝÕ½€Ã#Iãêø'´_íx.)ð¼pÎÇáý˜(`¨ãhn¸ ë´nU6Ñmîx­lUÓâ]X:úÞÚë9/r5uçu­—‡†xƒKÓJÊNM üsáy¿ÏAçù:úùäX§Gú«C³`ݺ•³†?(€Ù¯\×—Ko‘ Ëæ“qå|±ö>=ßÙ4ðň­!EiØÛmÈ€Þë|Ë)¾÷°µÉ//À¼žBÂWlËhuî÷-r/¹Âò×ÏœajÀƒõg|É(iT”YøxÂyˆ<ˆçtóüsÞßäÎbbf›¦Á–ù+×÷>Róª:nñ¹EŸ­™st³+¬{2©gû¾d]fóU#MÆÎ åyw<÷Šç7§ŸHYV¥”ñ‰ðõçî{☒añf×@;ëåÇFß"_{®i×dü”°7pÂ\_rc᳎þs‚Ïk¤ùl]Îçúp.>Ñ`P±äÙ¦ûÓç¦AXrÁ†j‹  î‚ê8´Ü"]\V©^šzÃ…vÞʶ)¾¤l×qh\k™=ö¨ëà€õÓÖ,È­,ÕåQòþâ\ýþ5Æ:f ÁÅi0Ó‡ÈúN,ÿ£¥w·È£3ïVµn#ƒÖnóú*üçc<™Ú³~Íb{ ü?]~ÏOÜ“U;åP¼ãÒïE‚u*USV·< üÔ¶paå £{·HåWù]›ùÔñ“ÑÔ?Æ™™@ø|¡a¬ýãʽ¬€óVx=ÎÔçÖɰNÿ,õú‰¡öx’\¿}D†¥„Ýe“¢¢¨5‰þŒûë«Ëoã)ž“Ì_ŸsÄx.<õ ããažS,²c/Bt]³Zð¾zó¦2³lReÄé¸qJÆÁð!5ÛŒ¸ñóîqä–W%¯öÞðù¤xjöo˜»x~yõæw« ©c­j m%ãKbñ¬ósïg_„ºÁ"×ñoó¡¤Áý±mݳIûú» «ðÓ7¦¢s½É”[?KëŽ#”/ç §„2Íýaͧ{FíÔõÙóõ£¸G󬱒Ӟ<ÇŽúëÄ; DÑth1­ZTáÝ|˜ü,ÍwV6qÍô3ã.ƒý+™øPFʾvþxÆ+²šƒ8D—7ëtoÍ”‰¦Ur`Òý$Óç™댶ö¾èQÎ6»–»ƒ‡õéŸMb}žV]|z$ؾÉV¸U£Ë?ä1Î3å9—4¾L7^ÒyËk–KY ‰§ép-Ñ(ôàé|x¸?f_åÄlBsõ\Ù85œ2nÓû™ßx2ÅO:¬ÃäÞºœRîSþÐ>&šôÓååiýƒu"/Lgp;Ên³ÍùਠÍ&w®%?¨÷ÙhžÜ’ú±y«®Wƒå$÷cy̽s§i.Ù31ýÜ,¿áÀJ°ÎÍMN¯]_¥Ã Æ›œ¡Á®ág®g“žÕÍûoi: öWˆšSØó2ˆPnÏ¡ä9”[óBLó2®ÍÓ–a-~¬…–Lo8"|D>Ìlè¿o_Q6Ùf½hE×àað>çUÆ›cãHrQb¯ÒãIç{wÆz›K€óÜ8çƒÓüØoy¦XÇ«Úíö!nJúò¸¥U>ì—®±ñe6QÍð~­¡ãGû“áÚ‰ÖD¹ÀP+]ð\mž{GŸ{ôýȱαüŸ{åŒSBÈá+^¡-òæÑç¤ÂŒÇùΰü§EVìüÉa›Úbá|JšoËæc¯Ä<ÇçG[¥TÄö›뜺ºøSÏJVškQ–ž#—÷k’Cp2‹¼Ð<Â1Œ;DÄ>·¨;¸ÏÍÞku7Hݯ*Ëã{.ŽJ8·µ(Ó^×÷Zÿ`sÃU? Õ(áú½w}>çæÁÒ Ëí?šå¿×!“<`ÇIäIøý©÷úÀÎ@ÂçÉ<Ïœ?wh®ß'1ç‹ÐõÍó3\,)ª±zo¤Ó%L 5¸êÉ!ÖísÈYþ³Üã=a}¬»Ù Ç“îÚ×Î1cÿÏ:>7þpžÍy¦ß1ÖyÐiÿ£YÛ/—Õz]väAUñþ|ÿÒ×lTüK/O ÜÃ)dƶÔam»Αå¹Äœ#ÃÇmúÜëÉ#D÷ë ¢Ü Ö™VCH¾Ÿ T8S˃®«+æäF«"}*Õõ€ù[¯N¿×ܶ¥$P7îp¿òùŸG1[7ÒÏM†u~{uY+Í€½Lí|òÀѺºKòj샋3¦V¼ï =V'4ž³q*1ö5J¼ßy ¡<·lÁ×s8cðæ†—aÁ„ç%óRs¡`WÄèF9ÓÚzîü:6pcb˨³ŸBI½‰Þ_eM$|]LçÝ,§Ó¾XÒ&¸Jæ|´qï«rÁ²ÞÑÈ‚B¦ôÍSZÁMë^'¨g’½ÑB öDÒ;å`W‹n®@ýçªË±×ö;¾ž°P/ò2Ø’GsáDø˜äÀ9$ÆÑ+·ÌÍr;Fø\šCzå7ü k2´¿º$!F<´8äVStyÌü¹ÄùÚú|K Ö‘ÌëÝúàeè3ëìž~¹ð´I‹'–¥9äùð×[ç]‘B½­Î~y2‡„;E~¸"ˆpN*åÒå~ò¼aš{>(Ö‘a·sͥѯ.CÑͪ«fÔÎ…GSÊ¿ä¥›ÅÆŽ@sRg‘–=Mm_Ê ërf9çó è:Ì8¿OÛïXGböÜÒû ,êÓþÁs5ØäÔª«&Ý•{êÚtt߯¶uæÔœN"[I û·:ñQCßKØ|ÏJÇ…åükÎñ¤Üº¿#Ç:ýüºŠR\îÏ«\yM ¥‹.¹œ5Q“gK²œoÜ÷€õ7¶[7ÐüÒö”¥¨ÂûÐZˆï¾gÍüûVL×åeb:ذý:T`S]·Üª˜ Wâ#{_Ý­†n‡öÔ¼>PMhÞéHÆÿœAžíÝÆ¨êX²2µGBs'à|$Ê{-¦ã¯ ãÐñNƒ¯?uÌÉ­L3Á²Îó«y“ÔP%ä¦ÂÖGMŒ^ùwl|Æ>wËÙþSÐlBóvƒˆ0›­ù‹;èx´Œ‡ÁsŒ9ÿ<íòÔíkèüÔ`H±$Â~­Éì!™¦ñÍ뫆›v¡ýãBÕdOÿ˜ƒ¶‰n0ÑgüÂðÝá¤ùÚM »R åñ9Á­Çû÷WL–Â^O7ǪA/Åü9»KÀ\ œ?§õ Ö9¶pŽ*É3DûìW1QÃuƒÂŠÕ–¨‰©âºË¾­#Ÿ1œ¬5í¸­l‘ŒPžûÐâë†ëö©(ÿèXX½6»a ¼O´¾Á:S;71Ø—’ ·ö‚Ï9ûN…mT“yðDµõéPÆ!C‚+ïm0`$ÑbD£€r@xîû{1'¾s΢~¾¬ ëX,òÞ;Öê*ÜÏÞ™_/5&žê\åËn5¡|ÈÏïz©ÂÉYdÐý“kNx“XÖ“š‹ã^ã í×µk;ì’Ï&48pŸÓ|ôºy ×ø8LßËOÇ:qÆÖœyR—šÙ¦öÊ»!o§NQʃ7ƒÃÏž­»1—·üðÜm á8—©©g£lÏy®5çîêó>4Xç×*5W‰^…å3[„(«äÀåHë½§Õ¤~áÍ·­úÃÊ—&kK†3¾v ¡ëj7 }îªË›¦ïó­8/tŠ­_$åž ÅuÝá°Ç+«d±ñ>¾¼^ѤÒE59¼C6'j¥ tOÊò_36œ\™½yܹ·þ„ri\!ª@rbœ—Ϻ¼d™qÍ´¾Á:Ù½GÖ.sÌ‚š—·N©±.OOíÚ纚X˜÷t]zo H·3'ûÊê_ÜÕŸq}í€òKmûŸ®ï߈ëº;Wö@ç§Œ ‰uæW’©³`YÒ#‘fL6ìùl\p¨HMNÇú¥îºå›?5ÚbšNÖö=óöŠá9ü4·tÏI¾¿K÷c†|3ȰÎVwa9 „ÙHHÿl˜=#T½üµšØçY¹¼{ÛGÐq…ø>ï'Î/âœXÊsø†{!ÔéïÖÉòF\1Yç5²V6´<™˜·­J.¡c¯\2s†M+­ž·:ŸPŽ“?iúþÔݽƒúërÞù<ŽÏ¹?õ×r¬³«ÛÂÍï¾fAÜpåÕ|õ-è–Õïç MrɱA ¾6_:úö›s=¦òò±â¬¢«‹|ÉëÐ&_nÔ꥛ÏS®B®˜Ž£Eb¾_Fÿ.Ê£U`^Ý¢nϨ©‚j“ú­¿»ï^,ë4±K.q9¹UB5'Æ_[@²ºÙ«F—øËÛðφt<ÚÏ9b>¯çÜ4ý}+ ÖyÚãÓžf&*h²÷˰ٷ`MÑw¡rI•u“^ö7é§ßת_+R÷ü<$àîZÚßãþçóEÊ·‘~»¾wÀùÀøúß PA‹Ê-Ž| &¸÷¨18—<ØùÐ×}°=LYhk»yX$y\’Ò4wí"œŽ4êf”Ç">˜4¼w«9™ïÄ|_K”yhv—zŒßu‚}ºã#^F™ÈÝ‚ý±q—/¹æ’ê¶£jă€~ ÇI²¾¾~=ÿFÈ“vtóaʱ¢ók ¾þ‡RS+;‰ ^_€¢7áA_÷ç²\²qèå/Ý–Ù‚ÚöZ¥V 1.h FËzeƒØ¹ˆ5ã+|óý:Ÿqü†"Ã:Y åå*¨âo³]õÛM©\ùêÁ ¹¤õÞιn¶PõéøWkn-$‡æåŒ½ú!€Œ9m”3ÝV—ßÿå×$™åà7ºçÚùº;G<  œ×£õ Ö ð)°«¦‚ó¶Ï=^~úÚ}ºynz.Ñ­s?ììIj}}T¥ú)B÷íèø–œŸÁ}Êß'ߟÕúë|¾=`üÐhd<›5î&¼;Wíå³È\²þ‰òiTO{¸ðx…YMHBÏ¡üuÜ Êcè§ã+ó}Kº®´ÊK¢ßëPÞ‹ : œälq‰ _™KºIw–Ë¡¥ö YHVv^âî™@è¼Æ8—ëÃÄ˯•ˆ9LJŸQ/j°N{a;w¥ žæN\û&Xn||éÓŽ\2Ý Uc³ØY´&õ½ÝBa÷è:iH蹤„qÕíØçVÀrà‹tZ}ŽÁ°bɧÂÝðë\~­y~àö Ô¢…{Û“¹¤°æ³¹c³ÐíÃraÍâ¦c‚,¬¾á‚c1ZའZk¿¸ ¾Q{žCv.Ù <™{k£7μh%)’l}ÔþbÏ~ä—)áçmôbýÖøø\7gö€k‰ùúì›ó¬³*«Ë¯³vª`vP«.å“nÀŒ©=W½Î%;OVºÈ&]Ú'êÛdqö;þºý7Ι <®çŒsR.¾Z4ò™o7›o¸Ô2¬ã¬b© bÝ»¹nÀši Üç摟Zõ‹Žýìdz0\é­7ƳÊ£¡;£Ÿ>OOÔ¾µ&ÀÖ?x‚#&í묳¢ñõ¢ÖØoô|êüº=·ËÝÎy$uddq¿¹ ÅÆŠ&SoYàÔ0€ðù-þÓñ󃘮?Š9ß™ÎOé>¬ë´èoùâb¤ 愵¶È»Ë'9ÖÖ*ŒN¨-×´Ç0M£È}a›iEimåçiúÜZÖ)\&l´ª ¥¢ ¾¢Ù“?Ý_Gîuð´l´r̺x&àÞÙÅdl@ÜI‘sáçÆ¼:*k^ˆ9ß÷5ãpPÿ`}‘Nƒ·D¨Àäø³Ù7G_c|î<2®mª› †©á•´6Šðs-Ê_”èæ/úÎKqüõ¹˜÷÷ì‡ͲÊ[fûXgyÜ|‹“sTpXÖìe@ßkd}X±-ä:ûÔå°<’þüµõçÅ„ÎoG“{{5-ȶcç¢áùرQ‡>¼Ò­Cø8®¿Ï§À:’m¿Š+Îþ^Ó-¨u…kÐúe‘ÔíhYÒp^ûmbgpœÜæCò"Úb_§Ký½‰v±¡Tw߆Ïßùþ çøQnÛ/À:·V¶Ý2Ÿ o³nDX]WÁÇÙ ßä¥ç±ç´3<=Wçêžå‹Ø=oΑ‡/ñÙ»œî£›·ÑçÝS1Ý–0îÝï5pÄõhíº}÷¬RÁ©¬—×îâ8÷ºÌàÊ„ü<ÒÓ㊿I¤3h#£ ¿GÃï‘ð}}ΕåókÎ¥¤û¿í¨°N”ƒ¶RA³ˆZцî*ØÜ£ïèçydþkËðŒØ°gÆËð‚ðBçþD /7qÀxx=u\X¾ßÆÇi}.•ëTv«Ýõ·-*xwßw[v/œ'Ö˜[ÃÎ ŸtÕ6Ù=‡hÒç`ƒŠ©|IZ˼ôêáC`ÍØQµO¾ê桼¯ùyIáAÓkžcØ~Ö=˜Û<¿ŸOiñ‰¦‚þ7wõ>U'Ÿl¯½xmཡwݵ‰"™—–Uö%ŒßC èivÁl¾S&æ}Çûžö5]_E`äEë?ÌT@«4 ÖîÛµE>‘&op ÷ÌÆ÷(2ðÅúΙ¡û]6àP´yâ(³×º}‰îзWàøf¿WŽuæËÚÕ*Ø8ÊÏôF·,¨áöðÝ NùÄ`Ô‡W>»ì ÞÏ÷ëHcȳÍcæO |œäû†œ JýÿR7^ £FËrº?©À:süè^¼WûÈw*®ÂÉ£ö¶è•O>{‡¿tÛc%‹w?-[°„ô³Ißôi yܶ4jëW1pÞ8ß/àã@;“Ÿ³~?Lë¬3 ÿ!»é»T0ÞáõrçiWaÓ¯gK½ÄùDU’”P–b¡—k¿¬ãµ„ «1ª¬áÎqD;L×snw>$&¸‚Ñ£[ n>óó@Êó´ú–åT,yÞ® ~8õ^qîÉU¸µ¯âÈуó‰Ëãg¿&õ‚Á/_V‹!ªN“vë7öuÓÍßtó*¶þÖç câ»Ýªú¯*˜P³ëiªL8cïÔN·|²oÂŽÒàù¬qK öDÊí›H8ßߣú=¯›¯ïèþ?ã­aÜ+­÷.Âñ jòÛª“2! åûóÇæ“*¢¥ÝæuvÃéK\˜E‘´4§Ô˜‘ã ÝŸîËžËýtûS¼Ï´~Á×½ÓkîtE¨ ‚ç 7ü2áÁåÄ·ÍçæïA5wŠðy“ U§Š"tŸ#í»ö:¾Yèö£ø÷Ï×ïtß‚q7±Ž¢òäÛ™Î*8vê—Ó“v_ºmëŸOª=ÀÅü=hªÝ€! E^} }ˆß"/ñªUÀrãä“6Ö'W M„íXrtõY§W½‰]ûЊEC€žÃ†Mm‡ÍküRwþ{ÍBذpdüDêƒÅ’Ÿ„¯§… -ß^˜ñ£¯ØÜ¨€lóz?ÝûW°Þ¿ºgƒ±Ä#T ûú’9~ÂÎ ßO¦Ïè½£·âê£#D÷GèÖCZ¿`¥õÛõTŒ–GÓò›M÷õ‡;ìôœu¡<`?nÁ˜œÓ®Pv^Ttí¢ã:~b¾|/¦÷ß<Ù¼tõ Ö¹_§Ê¯³ë¨ ø‚ð É€™ÇƒŽµ( õ]«É  ŸÇø0Ü¿‚ð{NËÚXvÄÚUïúäËGvÎû•í•‹W]ž'%stóz­°ÎúŒüºÃj¨àåÂQóêm½M·N¶­:¸€tž=êƒÃ 82aöÉöýâÝ×öåWé¼Fƒ¯?iÀ¤e¢‹Y°¿Cô‰:%œ­}pô¬²ý¬·UÌ"G\ÿ…Áð‘Ñdû¼È¥ {zuV ÔÝoâïƒò1{~3.8Kºâì£î’,†ãâÂtÐ^wŠ- ³®‡©?¨Á(3ãmë)1l_:<·ôþ¥ “¥î ß÷äëÎõÖú_?{ÙŒAÇg‡G0h槃&û¶Å­mäô ç ÜÜ ÁšíkgÏ!ÙMíŸÙõ!9 {žË_ a÷ýúç…ò{3ôÞ݇’àëø":Ñ> |îD-Xh’V}æU,L) /ÞŽ_ù%ÁÚÙx¤5‹!QMš¯2Œ4¯"ÌÃcsW|xb«ó}ù÷e¯>ˆùy•þzC†uŽØid|í*T _ÖÎ*å"ü:zœ¨ ÈäÒ,·)-= ¤kË©²R=ï~éàŽÇÓ FX 3S™Ž×ýkY“ý&©µŸóÑõ6]GG`Ï^[¶Ô\~Vôn» Àö"ô ôY•ZR@¢Ô?™ü6ÛæVZÝÌ6{ »g6”­;ÇÀ†SÆœY0è}æ&:n¤£Ñšq½Rƒ 4cG3_''ê¬óøH×JWG\uç³wƒ¦Ý‚ŒC㿞¯cŠV r…Æjÿ6»rc9§š´¾Ö†Cuã—éøXrÞ·Îpÿfž©À:ô>ÎUpª»gõÄÐ4WØèZ³ú·Éaé"‡ã8Vª<µ¬|9ZÜ£wµB¡ç˜ƒ€s}ùý~>í]b_yßvËübÌfÞç—e„ŽÃ8?מo:‚êö×¼­2:~h ì•oìÞ8õ ÖÑ^÷ò¹ ×DNî6îX$^_]²é6YsÔto»OÞ@ï3/!’¡çYDz‘ÎiË/n wg¿7qƒiã[ÔP¼© ô>{9ãžúŸhýãZ,nN«v´Ó¬–çÀç½iÖ‚“·‰™ôÃÑ%_d˜ncŸ43–Xg ß7·ÿ(b5Æòè [àç8V¤ënß.Óñà‹˜¼2¶´­íÄöÃé<Óë$+ZÖï”™E3÷ªãsÎB§àOßÞ&×GšÊ÷‹ü ß†WÃJ˽g6šÈ?þ2æä@`÷źû6|ÿ‘žÚ~³O#Á:gòœ6ïXžå;ëWÛ½ñ,<ñùeտЅäBÃ*Í–õð…‘?7ÛòÚ9–HN7Áor4áœk¾Â×IœÏ÷9麊qÖ±Žhÿ‘…ÉÝ3ÀwWä’}£Ï¶WËÚ’ß:vhÞa¤H"ï¦Ç/%tßÌ›Ð}ìºsIþ;~^À‡Í–°ó"欓9£´~UÕ%x<³o£UÎBs[Ä…¤Ñ“êöò„›~ÂDg)¿ÿ@ø=B~ožßÇz]ùܰ#ã%ß¼9¾þ´vë¿Æ…_W§Sy»plÕØ„M®…$Ýza;³.ðÅäñËv±D¸½PØg¯÷Òˆéùô#1_õcçðtþ$Á:c¥ ÓöH‡!3„‘þ L>¿àl\b!ÑMÝ h–°»„ü<õ„i‡>£Ùy~wØ`8uð­é=®›5b}Ž· _Wû5Ø¥Ã]ƒs¿><  ¯V”* IÓÇ{úx²õw qŒybäîíÍî‹™éî§òs4>§ë 6Ãן¥\SÇêÖEX%Yà°eÐièãVõA›{…äºY륹}½á­í@¯«»¢Ù¹£;áó_¾ÈçÅüÞ¿@÷Ù½¬³ùìnc†\„K¢z¸¤û3Ê“ î¾,ÝÖ¶‹Ý:Ž5yMèøæDøïLøy9¿_ÅûX{]¬øÛ}2¬“óþøåðÛiÐõÙMïÉf§`Ýѻͪ·ºC:ï[íÅ©QPß÷ü.1dÏ‘ÓÊ ¥ºyßÉõï›HlØï$KÅü^6ïý÷£Á:Ú,nqqgË;?ÊDvÞu:u±åòiøŽÞûïù€kãÒçíbýý„%áã?Wàë0ÞWü÷Gú¿4p/–8j¶Ì™51 f;.Þ,kµ›;òÉ8<«ÙÛl/vÿWí&ì±îþÿžé¾[‘˜ï7óóôoîÿc;;¾à“" žàª¶žé hš>L»CåœÝÞj/ÆEæ/vÃø8Â}¢õ¾^RÇù™ýÂ.€°ëÖ¾ù hv`p…¤Ø;¤y½ƒ—ïLuƒÅÂ@¢ø93áÏñÎ.}ÏËwf÷)o‹é=ž>ßœ—Êðõ_Äëóô<\[+rùj|äÏZ¼Ùv‡Ä+eÆþƒ]àãªeU½ž.&ö·)pLèúÖvžSŒ[0Šùs‹>÷Ùï/ñõ-ê^˜i>ÿOá÷LèóÞˆú£X¢šWgÔÉ£ga†|ØÌimSÀ!joôÏ•4äô“6Úg{Àé^¶7NF“ª¥¤ŽŸ<ß‡äŸ ÿþyßÒçGO6®5¢¾À:·FÆ/š>ì,ìñè-šzò84ßæÚü´¡†¬}ëkTãW˜w¦«Ç¾Ñ$ôÀˆY»×{¾ÿàÑüê§ñµÍع|¡˜ïü•Þ×jAŸXÇ~ÔÉ[\t¨ÕkëT¬S^5Bc¬!ô÷ɾ—1—¦Qì|Ç‹ÝCéü>Oø¾¿'v^>tÇ¥4:aù‡K²'ÌR@G³-Ù“ÃnOÛv‡{jˆtïÂŽeü¡³¸²ëŽ(ò,¼¡æd¦á¯CûÀR÷{> ÷¤ßü~-ëXhøM`ˆe3[‡ŒGÛ&FH5äÁžŽwýÇ‚öZòÃh"Öü{‘¸J—aÉvl÷{:ü¼¢ r׉ˆõƒu¿—Øj@ÿýÈÄü'ÇäC^ÏA1`ïOe@³Ê&–¨âóªÃXN¹ÀÃJÔc+pg+ÿ…L¹ÆLayLÑ,ŸÜƒe1q–‹‚ñÂËE?‡)Œå0é3WÏÁú³ÌÕ%‡ÉüßÄcÐç®&2ŽKË·tüÃ¥”ås.µ>¿E?\Ÿø=~Kc_égGèå.qÎê?Ë]2û ¬Àcß±/Âà?7ö‰Øß¯6 y¿ (Ãï0T#XÖ¯ož¢—oι2<ß<ì/ä>•Ö¤<+cNDZŒ_!‡N©ÇeP²ÌóÆeÐçMG0Þ´>ð÷š?ËüWxÓ’Sî¹>0…1¢YþœÇïx kÚC•ªÏbÐÏôý;™LÆŒÃÀÓœù÷ÏÓæ[õcìû1öEüçÆ>¡wä(QÊr8%ßá  \#½ŒsÎòãìhÎòû+çÆµ([«´åÒüS!û.ŽåoÊ로1ü„üM#lôT Ê>e„M*ù/Ú‚2F3D£JQh ʇ*Gy A(4Iª%C³(Qfh˜xfš ” enŒŸŸñ?xÑÿ~_"ʈñûJXæp c÷E뱌C­Ç<Õç?èçësºþˆÿàÈÑú !o3eŒ&ŽF•2>t"Ê †Ò ¤hìD”‘âÏð»~Œ}?ƾƒÿÜØgÄþ>Í!ND}‡eÍ2ˆ=°9z9êœkÃsÔ#P%(GlÚ”16n4ªå ¬@™ùé¨òš”Ë¥düçxÖÔA(•×AÅòÕ£×AŸýÍØÏœ=øG¬›?ËüWØÏÒSÎ:g ¼c:¹Ãå(Ùïx‚ezÌR}–ƒ~Þ°>ƒë{,‡8–ÇγC¿—ÅùÏXÏ’¿Àßú1öýû" þscŸ1*eXrœ5B{Å?æ§ ùÆÆzÙëœIÈΜIøWò×MjQ†Dy-ʵØ^fØÔñ¬±ƒ )WBhpE˜‚2ÆFF•¢<°áSPÆoUún³A2A3Ä¡ÊQ24…e†Æˆgæ¡”(34I<3JJ…2GÃÈQ"4M0J’à˜`ünó¿ƒC˜‚2fÂR–‰¬` BwÏ™JƱ‰clVû¥Ï¡ÐÏJÖç~ý‡Âƒ±šõ6h`ÊD`~¡ÊûRNs Ê *A9¢±SPÆz\Š?Ãÿú1öýû" þscŸ «_"|ŽØ„)(ãï0Y¶W¹óŽÍ©ÔËxçüžñ*Ey`Ó*P&ظq¨r” X‰2Ã&ŽgÄØæŒÅ*¦F©õ˜j–ýǘúÌé8ÆœæŒÃ?âëüYÆáßåNs¾Î¿# žs…,x%ãMÄ3óýŽ5!B#é±Wõ9J”š3þw ¯ïq&â™qƒP*”cLp^µŠ1yöá÷˜ÕÒ¿ÀïúTø÷wƾÿŽqïÆºÿMãÜ_ãþoãÛ¿klûÞ¸&ôÊŸÏ„gQ"ʨeL—Tøc.«ÀST¦|é8=F!gKsFa Ê0UŠòÀFT L°ãPå(6¥’±YãÙƒY`è¨PæØ¨r”ˆñ(lØ8T¹°vÅÆ5ÁÆC•7øc~´ ¥D™aCdz¦B©PæØÜr”<¥B™c£ËQ"lö`”%Á¦O@b㇡4() Ñøüè•C¨@™0¡Àg•ᘥdüÁxf&g¡Ö¬ÌT2=®Eª%û3ìØ2ƉÖgâÈPJ”š1žR2FSF£JQhNEʸø3ܰó³ÿyãÖÿOó3söú¥Âç„M¨@™|‡½Ï3¥B™`ƒÆé1x(lÖ8T9J†M«D™aãÆ³æ B©PæØÄr”ˆ±ÁÔ( c®Ö¡ìi JŠÍҠ̰ÉãY£ë3¨ãYÓsþá±zþ,ÿðï2¨9«G2A#Å¡ÊQ24”e†¦ŠgÆ B©Pæh09J„& F©Q=þaJ…2GãÉQ"4_0J’Ü”!1X±ÏL„R¡ÌÑœòßqÀÌШñ̬A(ÊM+G‰Ð¸Á(5Ê ,×ãR«ÛG`ä|MíˆJü“0-—ÎàïÏÏþ«Ç¼ÿŽñîãX÷Wǹ¿3ÆýÙñMø¬SPÆ([ºô;ÌV-¦¬L¹ÒñŒyÈyÒœy¨@™`3Æ¡ÊQ2lJ%Ê 3ž5gJÅØbr”dz`”%Á¦M@âXŒÕ¥œC%Ê 9ž5³ ¥D™aSdzÆþ#~tJ…2Çf—£DØðÁ(5J‚Ÿ ¬;±ùÕ(  eˆ&CiPR4C"Ê *A9ãçeüvô¿Ê7ØÑJ”ã ¦ B©ÛPŽ¡Á‚QjÓoy_A(%Ê MÏŒ¤Ç$+GÉЄJ”1ž™1ˆ±¢ÍÑ”ra½‰ÆT¡ÌÑœr” „R¢LШq¨r” «D™ü>ÙùÛù[„Ánþ&eÿ¹ð9`*Qfßa»ÊQ"lÌ`”e† *GɰQ•(3lÖxÖ°A(ÊWŽaó£Ô( 6qÊ9 ¥AI×Õˆ±ªKPŽØÜrT Ê›\ŽÕû–_-güjÎZ”`ó'  Ña(Í_`-þ]~u9J†&R¢ÌÐHñÌLA(ÊM%G‰ÐXÁ(5J‚K@¢ÉÂP”TµŒR£$h¼”!š/ ¥AIÑ„‰(#4b˜ÇUŽ¡)ƒQj”Í™À „R¡ÌѨr”ÍŒR£$hÚ”!7 ¥AIÐÀ }(ïš³¥ŒÍø=Æ¡*e„&@• Ñì)(c4|4ãzíEáßÿä±ïÇx÷?{¼û+cð¹)P&(Ǻ¼ÂsbƒPªÊ”a-G•£dz ë8T9J†Í©D™aƒÆ³& B©PæØ¬r”6¥FI°qP†Ø¼a( JŠMœˆ2ÎQ±‘U(slf9J„ „R¡Ì±±å(ÑwxÕÁ(5J‚ Ÿ€2ĦCiPRlþD”! ¥AIщ(#4Cª刦HA£1¢Q¥(c|߯ÿàU—£dh%Ê ÏL„R¡ÌÑL‹9<Ý…p6ÏÁ£|Ïl1çñš“Y—æt`%? „ùTì£l×vl2lxyìú„$ ¡9T^p3°û‘÷)‹ å«:1þwg–Ëÿ³.r Äœ×E¹ hNÖQ¬s?t<3ºíàÜ šoEÇ ÖécqzĺֿAspiÚ=) :6?e‘›Áù¢[ýaWƒi³û/d9GÍæ™wf|µn,¯._,=|j㫜<1Ï{£ýÐúëÈ?Kë6q;omrµÚœ.L ¯5¢ˆœüd9öì,?–Æßðñ€óh.kŽ˜sÉøû¤9Rõ©°Nz§þ«.ÏK„Q?'LÙK‹‘mÅ?-?ë ñ;Ò_Lú°€å½µfü‘vŒ‡Ü‰ñÕ²uãåCwgù\ôýȱÎb!`ð¼veV÷5IÐãlÃÐ"’(Äù8ÊàR¨sÆ„ö ËC#œ÷Âóýèø”/æã)ÍñíÁr¤šSÿ`K«wNèpç ýøðËã$°ÜS§þù…ED[ÞlT6H 9‡Œð÷Asè;ê>7šg˜Ç>¿N¬ïSÿ`èÚóÛί²¬/Ÿ˜gj“ª­+ýíVšs랋ͳÓ&-$4—ÞåŽvÑ×üóâã¿úùM^Å’Þ/Ìmý²f¼3¸½¾gŒq7LÍ’‘ó§<²ì?VžôKùÎä×¹¬f-ªã^Ñ<Þº\nž«ÄÇ%:nP®¹1Öɬø¬v§* PiÍ⦋Ú%ÁÌY“!²ÁOhs¾s¡Ò"’̬vý\ -á9Ã4Wn ðÜoš³~W¼*L ìÔýZÿ`ú=ìÅŒM}‡UO‚ðÝ•¾¬=}àhXTyÈH°LÝN˜OX~%á|(ž Ë}Ês' 1Ñ ûÍŸcùœX'øjôõBÍ6Ž$Áœ6eêÊ"’a+6[é !î‡Ûì¨ÉóÈÈKûUÅÄÀó¾ù8ÊÇëCcE馳{0.=G#°Ns+mò*TX²²ýTÓ$8fl?Þ=§ˆLµëg¹¦– æz%º4.$Çkš¹ôË@Âùœ¿ËùZ<¯—æ«öfÏÁÎÔ?XǺߵC3¶BIyPÈE³$hádRRDê?Ëzãì—O¹ælÛ©¯MR<œŒ“êò“i®i™xÒý$ž7®õ ¾þ¸eVÖÞ.ý=¾ãëg´·9üæcé{³ŸãËK°z„êYûeóYž—ã7 „†±ö+÷²ÒÍ9‡ˆçx×.sÌ_ìDós5Xgeéib5{-h±òõ“€DOÌΪ{—¬+1M¥é™&õ]a¹i„óJ8›çÒñÏÏ h.Ëô.–< ¥WAÇ]òü!M“àbÅKŒ;Ý%Ñ[:Må?bë~fru>aóÂù5<×ô÷|žßªŸGfŒuB*‰!Öp9œ¬#“àî¢3?µ•Ü%×KL<ük¸W¾-’ÐÏ»-áycã|ÞòìÚ༞ŸNsøltóT­o°ŽµëÐ][ƒC•·ñÒq}’`EêœÞN®wG;|w?í9.’°þd}=”ñntÜ]ú|+s¾,ç…h}ƒuhŽð ¸Ý®ÊüµÖIPuÏðS ‚î¿çÿ‡½ó€Š*[ú=f̘ccdÆ„Õ*HP°„&Š‚ETÔÅŒ˜PGÅŽ£b@aDvƒ "Ä€Áô*8^ÞgŸiç2ß7÷½Ö¸Ö±æÞµNu¨ª]»öîúÕßÜãÀÏ ]Ãø„Îû4æó§!0n(›çGëlSaî­2nÐåjÛ€¾yË~;-.B{íŠ ólކ=}yBFø¹lü¼=±Àicü6ÏMsŸÌ¾±˜_ߨ_‡£n.‰ABîŒmê|néE:ÇTùl(&u2G´Ý®<;ñmÃV6·šÍÍ¥óÒÇóGÙÜnÆ'úl.4ÚñH!åuLülî¦í¼ès¥ÿƒ=a$³57p>òïÍnü¬˜L[Ò`ègø”WŸ7n y“7kÍÜVfÂ|cÆwÔ ðJpÚX¡Çæ6Û¶U·2ùlα ít^¯xúìÝ~bô #þ•(*ûŒW+!Ç#ZÅOœAÓþRJ˧2²±¿äFïõ¦Àâƒq"Söl.ÿñ¥›¿ÉædbÑÿfOêoáhg)iQõàÉA}_óõ¾Ñp÷Ð'ëÁJHGÝU¦+^:Ã`qáÂ;߬æým*°¹ŽÌÏ‚͹æèăµàs;ÚùåVä»ïÞ…c£ã‹>DÃÅ<±ÙëoKÈõ²ÄиuÎp7!áá@Âx#ŒÛÉæÚ³õšùãbÓýå‚¡2§¥kBã_ïeX:Ùr}g½’üª¶Yá>'Xøè¾ýħv­%]êƒéµÅí4Ÿ Ú?åBÝêC¶O¤ûm»rq“ÉGŸV'£VOwÏÉ¿ 1-4;Ì,!Ó>¥'­›è¡ ðbb áh‹æBܲ§,^?ŸÿÝEà ³þã·Ñú„Ö»"´9®Ô5¦Ay~àý锕WàšnuÞ6Ç7gmͬX)èkr+Û B뤩B=ÆöO¬þdŸ›®\7ê3žŠí”é_mñ-œ"º×6×”¼½›¢?ÝíSBjWû=±ƒÎ$µ= «é|úqB>¦ý‰'¿qQ•óý?Ÿß.E;áu=ãüœ&JR¯0ôu.Û»¦„\¡¿ä[çy°t­Û7ß@ò]É£ù¶ºbgÄ^÷ïù0l3ëÿ(ãí\óÕÆ%õ Éo]ØÙet ô›¼jÇÀRÑÛo}Xƒy`±GkÛï„öƒÆ Ü 6o—åö—qö(·‚碃¤ ¯¼8Ktk”Æ@ù>Óѹ‡Jˆí…ïWÈ÷ÎóUæÏsª›ëJ¹ó–`Ü–#"Y uˆ2Nðyãnçïõ˜E bG®z¼;zíþ)÷ã™2£‹¯Vx+{ˆíÌE¤?¡yPŸçë 3Í.yå$¬gl^³*¯¡ŸÿV›šE‘á¾kn:ÝŠ—#ö·/!ãõCÛ꜒ºö®÷>í^BXŸì¬ñ•~> c…º™qGÙ~Töåâwµë*ã¶ž'/*VÇvŽ…âÝ«ƒÝn—©…åå¾q„wyMl±”dxqÕoß+{û^YßÍÍV­ûEh'd-÷Ï“Hó‹Í殉…¡º½W\B('Ä d©!NÏ–ºo²ÆË¡þ;XžbsÁ_Žæ{š¯Äh'aË·Ö%ˆÓÔ\:~†L§í¼-!]“Êrâ!¢w§äÎAÄÄÑ8ö}w{ \]}`¼?6œqæhÜÛ×*ã퀕ÍyRy¼ÞZqhÉÖŸ¡f‡ÍÎÅ-K G±m¯ãAå¯÷KYIÆsøŒP'àùfÀxŒÉêr\KN1ަ|°Šç?¡3mó½H(¿ð*ís¡eÒ›A4±—ÎÖÎþWš×¤ÅÍ*%›7°L·åû$ËÈR£aâ£Í-!¾fŒÛ9=3èføSR„šµ`G•;«&-75><;«×e’<£äá‡õ×`{Ÿ-³Æ:•w¢qk¯Ï(½ßÿþÀ×þDsÅÐx·í†B~£\¶±ü:•¦Ì"|^æ•&X‚\áû­ñÐ{ŸçvÏRb¢5*ôÔkH˜uHœÿ­?ay™õyXß—~iü<ãÛzû^iEv½ö_ÏÑ>í\ÅÝeûŸ¯×…íã´¶ÆÃîô÷ÆÉ+JÉ•…óÎgZÃFý#.ÀŸØ7Ëøfæ½iB¿ÕÓ”‹—Ãó sõ(§E‡ßw¥qv´[¥ÜoC¶óé=’€AïUm#ƒKɆH¼0Bç-øæT9¡¡è·m‚D¨Ÿ˜ß³z­ç™/Beú#xÎk-C;/·j©é¾!ŽÚÛÀGkËâ7ŸÙSJÆ/>ÃÂk\òÝßä^2ž[†Ïà9’0Þ·móGï§ såYïs}_´^G;FKÛf®Œ%«|:ŠZ¸Ê!%£Ÿk³“¥ä…¯Öû¤vRݹѦÉ0/²Ïi™ÕD©0.¼Ö¹f;‹K¦ü_}l¶UåVÈÑÎÁÖïæ7­Ž%]Ðsäà9lÕÉË¥¤hŒß‚ £ÀpGôzóÞä÷°#Õú <öaÀúWìùl.?åM:OŸï/£$ï¢<Ÿù?“KMš=«“ƒhÆš IŠRr#´‹^J¥,=kÖgð¢qq33…¾ õ·ž<-Gè_²ýŒj?VÍ¡\üìÓy ¯’«“{ rš’£~ØZ5ó^)1k3¹¬±#ŒypaʇëþÄtH²|ܶBœúC7~}ÍÖcö^؜鰷¤Ÿßè¼vÚ”3´çÁ«¤&u¦L;,Ô/Èé]YJ̇Th>ów€{cj¬¶¿÷çù•&ÀÎhÛCx?¬žcý>º?êFãítãŽ[ºÆ‘¶m>Œ|‘ÃN€A¯¥äÖľ/=¥ÐÁøÃ7•ù3þÿ¹æØ³ý åc~Îß“âó½ÄÓ¬¢¶Æë´á7ÎI„Œ)¶/Ÿ·*c|x¹mKÞ“º%<ÀØüzÊ]bõt‰ÀïÝu­™úÄϾÚ9bÚæ°_ïkd™­åæ`ÇDP¦ÛneD”zbÅdØc›T<æû%„ñÜØ¾ƒÕ‹¬eë>åçá÷•´ïŽvDßm +(¿F¤ÅÛ&BmŸ÷w&j—‘Á‹‚ḇ†þÉÃ?¶¯ö8Y,?²¾«ÙºÅÎ!)'Ƨ픪\°ÄßÂxÃoáªáh«[#ÊÈó=S~y?‹ço-#•z½œ5ЦÚìÖ;ÉÜ.¬>Ô´•Àå`œ1%ž~—>°}Š2nÐNn‹œš®A„tË{*¾cq^xO“¸é•‘Ê»“G¥¶·e=ïŒ^Awœã®m2w\%±‚ÛA’‡)Y¶ü~ô­À™¦çcÓ€ò.)w^ͱ\¬lãv’“Ùwìýé:ŒlÓ(¨Œl(%RC-ÁàÕn±Iår6‚kÔÌŽbÔ}Âh"hÚúã`Ïgý$ÚŸ5æíóÜ'´ó¡N×!ÑMN’‹0D®C³`Ý-ºe$Ê1¢MäóYP¥›û2ÑŸý1Û~Þ,`|ÆÍ`u<ãÁSÿÖÿ¬?*F;ác:Ò¹,'”ËvZ´Üi–jWF¼ã¦ÛÝn Õß¹(öö&q껦• þÆò¾I‘ÈÝjøoþFyÔS>ë+JÑNmpX{ãÈ G11X}ö:¹ ,s/#t=±…‚Œ‹’î„­‹¬/Â8ÒÌßX“®G“ùý å™ÈÐŽW¾j%w2ñüìàÑ+× E46Ðóhµ=¤îÈ+n.dü â$Óï íè[§ê øÅAà0ž*W(Ÿo¤ú$ ÕÒÍô’`‘iã«§+raR·ìÇ`ÇÊ—„ž„Æ}{ËMýi¼pžÉx:ª}^9>ÿÓ帤%Ú·N‚ĵ-{N»‘  ÷« w„¹¯K†þÅ—çVýVo°sEÆÿf}ÊÄøƒúëVSnQÚ™l;vÅüK <·"¢‡uk^p(¾ã°ËáSµ]ôðÇKÉžþ\#x.°óCö—õwÿ‡qKU9™jNåbÃôÊךe D‰EËL†vŽ=V-Ï… [»†íûÞhŸf9©Ëx[ѺÆ_ŽqRY_”öe+õžììt=¯O[/hgÅÚ¡ÓO¶H$P´¦Áº¶)p¹_œÙÈ9¹Ð îeÉuC{X”¼ ø»Ëå_;å΂µ¿î;¯³0~1;OXñÖödÿ„iÐéã‘}îÒ¾Žíßl:ð{q"á¾å^)0Àkªv» ¹ð°©Æ>Ó¹P«ù“nÞÜòáCMQô4ž›kÊŸOMÎeYÿ•ñ¦Ùº§ŒÎγϟïI$5¾-^]Ü‘±ÛÒWéöÁÏídÚ9Y³y.Ž?¨L¼3AÙ¶ª \iöý³ÿVí¿ËðùÏÌûücÄ›Dž³›:ÿz5»u.Ô¸ôêšÞÉlËš¯;踄°üÄ>oZߎv^Åþ²þ$ýKã1íØØ–Œ(€ë¤ƒ쩀΋ß™üš¾…ß4ð2´€Â‡‹Ûä/!r—“¬mÓøýÞHþžÇp¡_Àö¯¬®¥ç¥t=“£¨îZ–ù_'´Ÿ¯€ ·–î}’c\WÝ3Ým í;wò—êOè¾T¿®Zç·S(GzŒÐocy“ù1½ßÂ÷ÁÐÎ:ƒÌ.\'–u¢P­lpÞp-3ˆU|€d¿ ]ïstÞ÷~¤Ïú”æp¢·Až2ʃöã´ŽÿÙç¥æ\.öxQ÷Ó»ëÄ7®aÖÎá©gö°ý•xÐÃI·ÂÖ,ÎV éR½ˆ¬y.½¡×ÀRàÒ³óIŸÄnì\Lõó¡ô ¨é¹¦ö¾"ww*|+½0}HSx?Ì,}c$\Èê!]‡÷«ÏY¿O/h'o^A“µî7Ȭ®ìz º)žòƒËs w£Ü¡ÚR¾žuöôÞÌ8á܃_+㟗s¿…è=7Hˆë ýfiÐñç“zÍÍ{ÃHiHO)Œš”»RÍÜçÇ™€h¯î+±³Hú¥ú®´Ödûúúù} >5:ù)N+ìLø Ý\º~\˜»>lr⺜m#‹9må-Ô‘{BËúîŠÇzQýTa•°}/½3ü3ÞO8>¿óý¶Rµ$â³±fî›°\ò&³¤{è&éÙÞÚm 䩳­ò%ÛÄqgí£¦ñœ-¡ÂîA°ÏñÉè>¾'ÎúªtšDfvX©—|6æ-ﺹA4V:ê\ž·åËs#g÷Fh?jÀ)eûÖÿ¤qIÏ¿‹ÐNù£!ï—D$‘ÇÚ„?ª¹ LwlY‘ ož§ºmg “3³~ÜãçG+7|¦À8e´®ÓÎÙŸœÕ/¬ÎWÆ…K¹8q]ÖO½’É„ ^gÒ¡çv?‡ºÌl(|~ãíñy³Þ»[BVp¸½åú|=oÉß0‚òé«,Ìn켃îÏfãe*ãí¨‡qj¶;™”¨}Ÿ”“ÛMÚls5ŽžKêð¤Øöߎ-|“íGèç2Œç=‰!âv›˜³a“€ñ¯žž_NˆŽí‡”òÜ@´sçCí´Í¿&“ ®Î!WÕoAÐØåžÌî4´f<Ö«zå‹ïNó%ì{e÷CXÒ¾Ô}½øÐýƒŽ_(üž[Ò8—¢Ê£K!ÛÎw° ²¸mÒ»y‡fƒiàÜ_’ÞXB•¤cE‡e>„Å5ó'Æ5cõ«#Ù¾Fõ|@†vζç@i)ä4$êHSoÁÆ~e/|³á@ypà^s+xyꇷ»~ð%©³÷Z4tœ G£Þò^<…ç Náû«Õz”óüN¿ßogÖÒî´žG;Þ%IÌ.¥ºgÀeç¢Í²Áÿ†V7£.6ü~ØO¨WŸ,áþ+P¶oÚ[òy¾‰pîNóÍ\pÖ×x[cÊw•£{{n…L!ߥ¨ny.†ÛÇŽ­2È—Œ§n-ãí¡ôÐÙÖ¾„òZ§ õ Ë÷ì&ýÜjõæm¿iÛÛ˜?Gâë0´³}ã.×׺ ²qÊè!£oÃС.ß§ôɆ-½£÷ G0ro5NËÏ‹ï?ýv~ãgç´7òáo÷$Y¾gIZgÒû~j®åâÕïÇye+Hß–aò3WoC”ÛŽç² ›¾ÆíPgXH¢;ôÓòæûz Ä­úòŸ[žÀfç¬ï Œ´Ó­Ý–µÓR¤ÆfüÌöí3aÄø'{»ggÁ§²w]u]áuƒ5ÇM—V/²û–l_ÌúÇì~+ÝçÓó;1>_¬c¸ýQ¡‚Œl+Ýa™à|ûÐÍÜãY1¿¬QD‰+4¬þyUÂâÂâŽö“'¼è+Ûûko©æïaVê¹¥™Aäf}>OÐs\)ÚÙTîÑàÈɸÞ|zÄ’L8Y¹/ÍpiȲ%ÇBœ]¡§ÇùÂù»—Úg7öyô|ß_ç_ }êÑsŽõ<šeJãŸ褮¶mb*IZ› â‚]ã¦eAÁûCGG–9C²Gêêc–×Ã)Ú·ã¦óùz„ÐwgqÉ8ò4?Lúì¼3í¬p—žn¼9•|¯mŸ9¹aŒþVº'«OÌKšX]óÄËêz ˜@ع9ín¯¡;Yè÷1xp*çÄr´3¸iÛœøØTòZÕfÕýźÅu™P;úcÁöŽplN^éà¥Dy /™Êç °þ.‹Z7¶^°óe¼ Ð³û/ÞJ%iÍ3º¬@¿jç]ó0춸µ©CÈ‘MÊŽXBh},ÐõÀv|·ÉjN¡%ô›î8·Ì¢0^µsT±¥»Ãl¡ï¨Œ—ùåâ!eçkŠSIA_®³ŸƒåÍI2¡´ÿ¥·ÁÅöðÑT[_®åGè}Ç^_’rê&ã×™>þÁÃN§°|¤Ê¡&T#ÌÚp¿Õ˜¢,øx´ñÇïe‰¹¿®¸'_¬&wöã¹›} [Gؾ‘Õ¬ÃÎùU爫ÑÎÆý¯gNM#FCê´²á›S5–f+2! È|Ý&©#¼#}ÐÅ–²{å„Õ¬¿ÌÎÓØù »OBû´~•¢eùb‘FެÛwyµK6tß¹P?Ï2z ¾V¹UÃ6\Ï–o_ÀßO™Hî5ÖܦÂD™Áªã…ûVôžEC`ü?ÆOTÆÚÙ2pȶ4Ó4âÒ¥ÕÙØcÙ°²×Eë¡3¡éóåm²¶º€ÞSý S®þìôfzÙJ[>OÏ€FIñ;6iÀ‰þ«´}§G9“Öü>јÆÚ©ãÚ¤†idáCïMÛJ³!BGíЧ·a‚ýǧ’½®ài²ì̺>„ïGÖç¡÷®ôù{Jù¾_#~}0ýüÚ™ßÊ7hôØ4ÒÄ“Ëð90Ó}ÿÒŽÞ†Ù.:%Ô¹Bs³•¢eå^¼–ü:? ´¶9brkˆPGÑúì•^ÿîË7o¨™ðY=P„vnpé¬[i9©IØ܇ÎY]5õ6ìŽ6­Ž‰t…GE-wž òæï‘6ú ô<ªPw°ý%;çeýZeü¸•‹M..ëÞôu*¹0(¬öXdôÝõªÓ‹ èm’¤arʸÓÚŽe>„æ™Þ„ÂÎÍÙ}\ö>Ø_¶Ïý¬^C;˽‹O÷HJ%NNv†Ý/äBZÃ5ßÚÉoÁeǾ¢›¤àW5Ý-ÔÚGàôÒ¼?X¨ XÀúÍìž©êû G;}™õÆ÷³jT3õ'¹0â]ËâÈ5·€Þã±ï’ί}§ø’7¯OâJ=•ç?ë÷±™°û1ô|ç£ãóñCИŸJŠ›®ÒÔʃǧç›[Þ‚5Óµ¦Æ7°n·žWæGžõ,°u{&aqÇúò¬îî?¸ïk•òz¬°RÆ ÚQ^ƒ*M%”hœ³õôJåM§C·&ÙmGFÙƒËÌ|ƒ¬Fž$?w™å‡G3 »·Åî°>óÖgRåÁŠÑNì•W{צ’‘ƒÏïú.–ÜlSÝÖ&²åfLt„¶†VO_ŒsçïÁ™“µîÜE‡ÙÀ¿?a}cï‡ýÎE/øü¨YAIÓS‰Ïçí’w€»ElÕ:¬6<×Ýî ]”­z>7“°óýôË9w‚¥¶Â÷"äe_x’Pg+ãí|—âì]·#•\KÙ×ßãtêu8äà•›°oÏGû®]¡õã0— Ý _Göû¶³üÌî_³úJ'øü¢u“wŸœJÖ.ÐÔë}òÌU6’nÂÒ÷wí=â ¹­; 8¼˜í„߯°ûbìùô}®ŸKÏ#üï«îÀ‚Á3g5¾ ‡~Ür3Õv}îÜ’SÞ„ÿ½¬ßÇøåvW¿1„|nÁŠjƒq rl®~ÇcîBÇ…{e§¢ÒÀ`ÕQ«È{N°ÑþüúÍ÷}IжÙÑÇM ,®è>Ž5ýßù}þÂrñÕï$›®+Ȳ'eQï·ÝÓÖej®içO™9º;@ñ/jÁW—/!A=êš©vŽKï }<›.‹ôŸÈJô7›öY©Ú9ׯtí¡[ 2þ›îýÎeÜ…¤}Äå}ÓàÖ«[JüíÀàÍùìæþdtÓƒã*Ì ëï°¿ì<‚åº.ÿì\JŒvÖvYÙdA²‚ÄwÚØ|aí]0W«wx– ‡“jV^±ú»€%Ì/ ;Çó ¼¤ëö[<Ó:¬H#̨x3 :–ÖR´s9ÞÈíf¬‚l ´«Îí“‹£ÃžF^J…Üâ?Ì®k²>Ù—LƒÇ•w†Ï!tŸ0èþf<õs|N'Eå å ’8Š;ÊK®£– ¡Y¶ §5·…$«ñ›|s½Iþeí‹“úÏú©¬þûó vÏIõÞo8Ú9ñúñœ -øzÛ%Æ̇YÊ‹À©àñòl›Bo;¸îÒit#[/¾ŽŸCØ9&óZ'–ê5º8yÇ‹ò‘ŸÝÇ’ãó½+÷|è« ï ‰Î?ȇâãË·4J…¢=ÃH©”ËuŽ'95tÚøQ{m»oÃîO±{l¥uî¨ÏxôEhç¥/÷E)HÇÆÛ=_v-€wž±³Üã{"/$&ÅQxÍ–M ëh/ÔïìüŒÝÇaûw¶.Ðûgô\Xͽ\¼>äõ'0S’.vƯl  XÏÇ={‘Z¾ÿVßßè±™¡û@ÏÀ~×Âüˆùí‚ñ'_¤ ÑåëPZˆÐι‹Zué"Ù`vûêùð0¯žºíô¬l´ú¬‡ ÏȃŒ>Ó±aì –WÁýîºKÉáB=E×i]žëÍ߇Äç§]Ÿ|fÁÃb8ÙÿaÉJla{=Þ?ÞÒt÷< }’„ræH¯£Y=ndX÷9ÂïçØ¹í¯Nüì÷€R´³y“îAçÓ)„pÇñ]îAN¾]Ëåž)`Þ{ȉ{gh_çmîFoÞÍ9Ô°¾û^Ø})¶>Øp¿ÒŒšøÙ½Úé3Dmkö‰"Ÿc²g§ä̸›zûƒ(B´Ú9›=v„«ÙÃÍ_žò ´+%ì\ƒÅ/ËWô÷§åz´~ü|Žv®®}÷2!…h4Vdž_{î ³¨KÊO†Ó3çØ5òp¯¾™R‹ro¾Ÿk/Ü—§÷ GC„ö«ÕG[?ãïQ<×£÷Ø}$eÜ &F®ô—§÷ È÷jÇï¢ivÒÉÝɰ&°y§îvRX¥°9»ÊÇt–DFl—ÛÚ7ž"ü¾€ÝgûyÆ‘g÷•qƒvÏ•]Îû)…<ó1OO¼­형 nÖÃ8oµçûêÿ´ÀÇ¿!°ï®£o=ãÇÓõ…Þ‹Uó(«)°”BÔ{™\*­¾ÒŠvÇÊ;'CÂá6£¾±‡A­?D®ÿ¸˜Ø¹æk?ãÄŸ‡Ší7X?Šíçé=>¡_®Œ´s´µ¼©~p ẟ³B·þä—EùIp;ïýðk ìù:f¡ýWB×ÀÖk–ÏØ:Î~ßÀΡ•ñƒv»L&§¥ƒ.8òëDŒzqË3ÌM ÖžÜMˆ…ä¹ëTèj¶€üÚºCkó*}>>¦ ÷PèþÄ@¸§¢Œ|~ôâµ÷<Ÿ'“Ë.œGÂåÊh=Ë$°õãNèÀ~ÿÂ!cÜÝ'/ t9‰¿_2I¨§Y½ÆöYªþ,C;ôw*ÉdÌÆIÒ²ÌB˜÷®IÐ÷“ÏÒòýNüýÕùD°7«}A³9£…u‘þ®ä¡pÂÎÙ=—p5úïßyÊß3 þ{g@V£ÿ¸÷˜É}Wèˆá(utF/T>JŒN‰Ò@Ç @¡ŒÐA£T¸‰õñsþ*71œŸeüGóSøYÆÚõ0c­ù9PõñsØ|ö?ËÏḉ^¢?æeç£ÄT‘(ßÍQ áùa_âÆ~EÁøaªÜXÆ“~a¦ñ×fªüÑ ¼p~žÏÓüÝŒöH†ÎïÙ‰~Æç×fA±Ÿá¼þÿÎÿª¨Î¿‡|î»@GŒDi 3 ŠPFè”Q(MtLª%AQaˆÕǨø« 1nž:yªe„Î…ÒD‡—ñ3@uêá'Jynv}Œ 6ãøÏ2*8†Ø—رE(# ª(”&VªeÄÏu¯ý CñksÝG•¡È˜ǸàæìUq3ß1Øå(mnŽª%ÅÀW¨ÌÙã|û÷wü;þ«r &ÿ‹¸Ï1 ¥‰Î(CU $è”1(:f0ª e*Wa•ÕǺø«¬²(”&:¹ U’ ³Ç DèðÁü,QÝzxŒnÖ›‰ügY«ìK,Ù ”ƒ*%ÂÀ’¡*P~<dÄdt«g<ãû0.£*߇›ÿGóE¿6Ï3 ¥‰+CU $ü|QM~¾h&ÏÒV‹£2Óó÷¼2n.²¢–6ünð÷LøÿÄøß”ÿD¨H”:`ªe„Ž…ÒDg”¡*PtÊ”3UÅóÌþ ÿâ¯0ÍxN£:xJ\†ª@IÐÙcxÎÇi”£´ÑñCPµ()€B…£]U CužòŸeapl³/ñh51˜d¨ ”ƒ* ¥‰%ãYK;UËåA 4J§V#ãh‡ñAÈ8Úº_áüh`p ŠPF¤Q(M TÙæÁñóà#ù¶V™­,CU¨°0Tg:Ü!¨Z.b+P:èa|°»¡2QÜi{Øßõßdîûo«ÿ´ù×PÁ}–èˆ1(:c0ª eN)Gi£c† jQRtP… ë¬>&Æ_eÅ DèäÁ¨*”5ÇàFi£Ã‡ ŠøYò_ã:zñ<îú˜Ö$òÿƒc}‰U[…²Æ ’£´1°‚QU(k~¦¼úøŽ^õÌ•g< ÆwTå ŠPFœQ(M Pª%Á@A‰0XƒQU(ë/ÌsA‰0ˆe<ˆãs£ª¸|¨KÝŒ‹ñû™în¨ÌzXÝêü^@}—û÷Ÿœ¹ü÷µÜ÷ÏÎy”ïTsËs\Ž“ªý{ç7Õœö¿aÒ²<Æ­CQÜwŽ$CU $èP1(:U0ª eÎ%Gi£ƒ…¨pÌþ ã¯pÌd<¯Q‚Žƒ¡³£ªPÖ<ÓGÒAÇ ã× •És¶¿ÆÁ¡S£ªþŒ?âÑŠÐùƒQUÜžƒ@Ä3Ãø@pCe¢tëá1ªò´ÃQê*J\cQ•‰ÒPagsŒÅ(”&– U’`€Å D˜·þ{çžäÙ¾ý¨UQ«ÆÑ[ÎâhÅhWnŒ wbÄ8p£1 ‰+nâģЛ:qãÆ8nܸ1.\ ‰ µ*΢u|çÃóܔ䭶ÿw|ý¿ß§Çñ;r¼o=¸ œçõÜ‹ëÔ7P¢nYÆs%ÌgPÜ@)ä+ZaF ̨ò,e2ÓD0§8€&5@58€†5}ž¯}ž¯üýó5…ðî½€m@ 1AªÈ L#AœàrˆÔ!ÔDà Ö ‚ Z-pÄkA°¸@„lbˆYÜ@ Q[ÂÖPAà6 …È ‚ÐÕÀd¼¸AØ'²ƒ`­¥-!ôÀT0† Haƒ`5°)ŒbÌ¢v ƒiŒ@ãh€ÈÄÏ”å ¥v ƒ± ‚¹ÔÀ‚>’ɨ.ãY€æÓ7P„ öËe ƒ!-@ Sê€(aN+À zà*Õ¤0«A0¬Ø€Æ5æU yˆ.!OÛ Z-ãó´ƒ`l-p nA0yâ_ÈÔæ:¹j ¯GîÏs ü«õïCíûo¯{ÿŽšÇ½oVîs…èôÀTŸ H!@ƒ B5°Äh"R@aš@ Ä™œ@‘šA „šœ@ÁšAD«.ñZ€Ö7PBÈV ˜õÀTµ H!lƒ n5‚À5Àäº r™³À)äg›€· į ‚ l@ #€¨`ÂÁj`2Ä(˜äcÙ³6 …q ‚yTÀ¤Bv¶F2@˜)8¦2ƒ K \ ³q–ìl3òË΃ñ,@ óé€(Q߬@3ê¨`J˜zà*Ô¤0©x€ŠÛƒ¢Úÿ˜ûª€yM NN Gm3@:8Æ6ƒ ˜[ œ@“›?Ïó>ÏóþþyžRø;œÕÀd£ˆ H p9„ig"pDjAª¸@kbˆVÜ âµ1¬n „­@1ë¨ jBØAÜj`2ˆÜDº8€‚7PBøfñk „Á †tB0¦P;ÁF ‚A4Àd0Šˆ` p9Lc0N"p¹ˆ`" p9Ìd"J@cæÒ+ÃdZàa0›ˆa8p%Œg˜O<@ZFÔ7PÂV )õÀT0§ HaPƒ`R5°Ìj"Vì@ãæÕ;ÁÄàr¸@Ck€ˆalp0ÜÄ0¹ö/drs[u„ר ¿ÿÚøWêßÿ†º÷¯Ö¼ÿT½ãjÝjNÇý®6 …À ‚ÈÔÀd›ˆ 8 p9„g_"pDhA¢¸@iA¥¸@ÄibTÜ@ ¡ZbÕPA´6 …p ‚xÕÀd±ˆ d C̉À µAØZàa¸ ·)Dn„®v ƒà‚èÕÀd¿ˆ` p9Œ`"1þ°La"Cì@ƒ&Q;Á,. €aÌ ¦Ñƒy,@ é€(a$+ÀL:àæÖ®0•¦Ò7PÂ\V ÁôÀT¨c6 …á ‚éÔÀd0ŸA0 ØL0b Œ¨v ƒ! ¦4ƒ Ô,-pj–Á¬Zàa0­ˆa\p0ØBøy »+œõ®È§Î&ØwfÙŸ|÷ˆãÀ{ò!øä—c”õ#ãû6ßú¹W$Ù{´Z~h×_NÉò8óa[ûøòîø<·”åòýÑúRÿÜj+ÆÙnpŒêòí1êmŸ¨¼AZoÚ43%êÑGn\ðÓ~Ä¡Åßù%†(KÖI)®¦¬Ÿëß²9ªMÄW½/ùrìY_%ÿœ!'Ʊ>¬ÑoôÉ£40ײ‘Ü ßq߇I>}þæ)×ú“­iE¶Kë|ÿðÆŽþ¾{–7Äúq³~}üçó…bîÉ¿xï~ž6þ(íS¢ÜÕromC’´›y˜˜}uºÍˤÿ‘Ó!³ÂcéD®ó³¾B¾|c¡ßV_ÿl>÷î²Ð×£F¦¼1Æ‘Gy»ºÉQZ®í7†‚7IF¹ý9”=LÖ_’’O7€4xºä{ñŽXºÿ΃ã_÷ ¬?2˱áûaŸ ßÿ‘Ï­‘ãõ“o¨L‡ó¥¦s¾kv“ôMoZÁ¾÷>î}è,5ɽ3íÚ”9ºæb…[A'ºùò½ùþ0Åþ!'SÎ2^?>Í¥çÐgQ\c›$mòø ’î‡Höh©zTr²o[¾\WnÄÓ%GÄæu!—N"ô%ûúñ²ÜmÖ÷„ÿ{üï¡Å8cÖž˜¾û§#t輻‡G¬¼In?;ô.ÿ!Rì—*;‚Ò{’|ùl[Ø(rÝÞJïãë+Æúi°þìsçó=j·1mõŽÇ|ß_#ƹa|U/üÍ7òZú–7ɶVß¼ÿáøAR¶på›Ù7«„œ°xú]¡ôî_ÌèOYÞ(ë_Çú±~r¬Oª>ŽãØôðÈGhÒ//Ïúõ&1§Ìu};ù )<¡Š1¬mw’q{Ìäò8ú0>mÀRË@:{O°©–,œŒì·£CóÞm3õÛwâõ\–\Ýl?L«.rÑý}*¹êœ™4Iqt¬uòrw’2kRÄÝ»Ñâ,iŠˆ¥¬Ÿ"ë¯Ãúa²\Ö§—å×{}{OÎûç0­Nj×ì“Jr¼u}o®ƒ¤bêšÛ K÷ g¾8¼gRñtÚt®!V e¹#¬oëwÂúkñù±?fêË&Æ8÷ŸÝÕlzuˆÞ·|sõ§ù©„,[Û2ZÉ¥y½KÎÜÜSè§Ú_È%‰öåD³Ÿ¬¿ËCbý•øœ!W㔊)±õëå‡(ßß%•ä=6,¨y¼•´áÚ¿çêMnT*¹ñà 5-›¶Qûúõô6ʪKXþë_ÞÒ,[òúR}Â÷År-1Îã_ªL‰À8£v(ð*•´:Ý?gýÒVrÛp¾ægoÒ!8 }ü­¾”ïשöåœÆ=u/Ëqª¡¯ïË'dùEüxüû¦Å87öæ}ÿÑAšò-—D›FöŒí<÷%mJ¾,³§i’:ìËYïªö¥?VÊy³[Ššòý®¾?\¨¯ï&ßo©i¦ÜL#^ÿM…ŒMÏãÒx}à|ÓȘ¤gŒ-â„å[ö!· r¹I¾Î¡¦¬ß~øË*K ž öåm°þ¡,Ÿˆõ…÷úã^^•ÛJ+ý²Â•4+=¶×¶.ôéTfjlÏG½IsX²? ²±Ýˆ5ý(ŸÛõ#azbãxý׋N‰™ÒEc¥ÛwD Ús0üjuL/¹l?iX­ö •åz“-¢÷»ç”Œ¦Ó9Eô¥|Iéqð6w®0_?°?-6­ÞÓÄ— ëõEÜ=ùñ~\Г•6Žù¶iCwé:êÛͳí'ù¦^ºtá]O²Â}µ®ÌMK ­Þ°eý© ^þPçô‚ßó-YŽ&ëSëõ^rì«Ü'c(=hZÕF^ÄIª«¯ ÒÅî#[¶|ã^fêA>¯3lo·”ï“7€òýƒªûêëËÆúv²|V¯ðúÕ¢{&mq •†œ5 v’ íåú„»{IÈÜwÏkªT$¼ïøVÁ }éo¹gYgW,euõ#g~f?…|ðL9²*ŒSEݹgŸšèü¹aoBNr$#©°¹ß^òUðÛV=wv#»·o©¯hÛCÈwÕP–'Åú³<ÖO—õ;÷ïתÅ8o×w1½ÞO›J*5 ÊIŽGyòM~’LòXku®BR7Êúçm¬ŸdŽØX÷úµ²þO,ï=¿¼~À8;nMJk½{½ýâå¨3 Nâ9Pbd2‘õå:©wúæö %*E•Ô4Žæ}˜~ªqCÂú³>V¬Ë ñï7gcýT÷ÑBS7ûOq’‹Q¦Ÿ·åJ&itx|Ɔ®Bߺ^âåèÊú¥±çëÈ~ÖO9'Î2ëÆ¢Á{iï³5^UXì${Ÿj]múÔÁCϦ—U>÷ 'pñ*ZeýEYÎBÖ¼ ¾/v-Âç }5÷ä­Õs”í'S]¿<ÁÙ×;É–pñ;yŽ=dÏü±·Ê„ö$» µZcewºN½ñÝÒGñ”=Xnû|X?IV÷ÙüÎ댿åtÔ’ÉôJ ½æïw’1"ûYHÑ·¾Ò-ëMfÍÜ0»kÿHÚ~ÝÕ›¬,Ÿ Ø—Ãú3]³>`,¯Ð댳`ãTÍÜð=ô²móŽéÄvà›Žk÷ì&æ4KõŒµ}IÑ-ÛvU*Õ‰J¯&šÆÆQ^Ï!¾yó?{ßXg¾¿/ÿù¨0Ί&»ž¿»h¡×=E~ÊÿÐI¾?5jãÄ÷»Hr£jŠå¿õú=+i®äW9_Š¡gÛm[2!¥¹wxûų5ñÕ¦~þ][¨CB_3Œó4‰kTh¡%g7Mÿà$5j.eÔßEÍ~õ2X®&WÔuìb¢è›'£ÔH³qí£S~ôÕ¦gÖ§ñʯéâ2'3ç1Îfn:YÌBEwF—8S( >zyt¡1;É¡A-çü±™Súi•ËšB®R4eýÂÙ<•ÍWX¿Kÿ\+^¿óÅ©³vSî×[:,{õ¤Ã¤Ó;HÎ M–?xØ›T.UìRÙjÊú÷²×aý¡Ya¿ËYâû ýÈ1Nóñׯz¾‹~[ eR9ÜŸ|¼æéÂòÀ\¯/4RWC¹î¥E¤,Gšõýdý昞ùÓºB¿Q!W<þž¼®òÃ=i÷]tLP™ú×dé$géÔ³Å7n'áâq9§uúð%Pe¶¡â(ß?½*a?ÙçÁtÆúòñóúb¼o0ÎHãWAÛ’wÒy-¹NéDóbºc\ídõ¡o³_ê,ü»4´¶óùaQz¼o}ÅòÛØûÆ~Voü×YrŒ3íØµ´«¥vÒF\psçt2¤ÐOoCR¶‘Åãš¾y_,RÈ5HÏ”-ÕðBm{rÕ¤ZU|ù#uÅy~Z¸a¦üu^wõr –Ž;èî|g†|Ó?DxˆÛHKçÈò'ÇG’Õ«&ÿ|éDêââ“—&P–ËÌò6|ýÀ…~çüz"4Óç®Å8\LB¡tutÅ´ƒÒÉ˃3žÜýbyëˆ=ývGi~µ,VvjÊÏãâé/ÒŠïömCXËgfy]¬Ÿ¢ÿzÁˆqÄÆjwØNï«Þ-?<*üô°ðˆú­dåºFEΘº u¬?Ív£j·ª_²¾÷uȺ¿?³‰å€Þóý>í¸˜¡¯êfªÿVŒãXYظf­¿¿Ýü23ÓÉâØ1êi[I€h{ïûËT„ÏîO;4LnõZã«ÿ|}ÿ=g‡õe}Hýó´œ'/“|e+­:Ú5îÍütÒª¹8.‡ÎL²YV64ëˆ u7ä|¥¦ßöy×·g²†²>ç¬ß+ë3ɯ³„ðýöe™Ÿ3 ÷ä‹;æMê’a¦ÞÇú’tS¨gb­—[ÈõÙÆÍ«Nõ K_xº³__jZ×'zôl ]\’K> rÜåÂ<òYë£Z7»%rŸRA>ŒàÚ|P1Æéâ ÚBå? ]¸>|÷àëS!W7“–I{W´߃œÿIÔþƒ¸' ì\°{Å©ßú‹÷a_n+ËdŸ¿¿Â¯óä'õXßfëŸl¤ Ojšqºÿ½‰¨-]”s¨ˆ7Nkî}¼IÑJãë7ÉúIfí«ÏràýóáT'&ÛÏ/Ý}ÖÓg÷·~Ÿãl:iЮFçwÇ7’¸of§VœÓäl[¦ÓƒH}Ó¨FÛÍ•4”åi²¾öìyÉê3Ëõäõ ôïÇ8;îïÞùâZº¨B¶®¦“ûfìMÝ@‚çæ8ÿøaáó…{}g>oî':^ud°{|5¤z“¢ÝÁ©!¬-{_½þÁ8gÎÔ^1òòjz­d»/õ®tRÊQê¦há÷‡"I™Žc'ô°ö¤'§ ýªÝ˜ÊžW,Çå­ñ9Bøü†3åX1ŸÏ´Š¾ÍVðKñÛtÒ¾í´¾yCדôo˜ +…<ØžôrĆ-›óþž'Æû¾­Ð§õW_ÿTúLÜ Ip_¿h¯0Ž·]x‘to.á<ƒPÙZÅà‰ëH”îÈsQùN$µ=—° ¢/ ìnt¸ç`ºoøúF±“êùòX¾ë˥ʕ.JXŽ£×?ƒ0\Þ¡o‹‡Ë©dÌô=Ñy2È‚¨Ç~;m"Ó¾‹ÿrwp'âU ëÈA4º2×YÿÂö_˜Øú˜Íù¼~},Æ8?•ëâbÚlî´ê感­ÃÐ|±;•dKŶ‘­¯ôrü(Ë`¹KYûB³>ØþyrŒ³&´~ájmÐN\¿O7›†{àgEÆû]Â×Þ îîä‡AÉU+ÖŠ£xXôq:æKÓœc«\ÝZÝ7OgëW6æ×ù%xß`œú“ƒÛm?•N=Þd§N’Anuʽø'Åj²íýÄ‹+»÷ ët\ÁHï&r; ”Ÿû>ö¼aó@>7¬n¦u§ãðuwýа|£™ßgø²âÔV‘FyfVL(Ñ‹;¿NKñ}ü¼–Ï£7bœü3¾'+¸€ŒÑp;Sdù÷ÕC·ýDF­Ü^½R$ :ݽ̠>Êò‰øçXYßçÁëØîû}Ø~¿Â×+Æ ÉñàŒéõ"šwYèæöä½äÁéå瑈ÒÊ$Gìny4†vKr5_xQCYËUcç:ì'« þy[NŒÓýÜ•ª f,ò"2HlMínù¸9¤öÚ9ÙŒS£È«Qg®ºRùë½Ã.4”ù‘­ŸÙº‰ù’­ßýs,†`Ythø #ù±²f[þ.äBxÿÒ“¶êI¡ÐWÒ›ºùÎsV]\§üN eç|îb ß¼†Í;Y®ŸhK×k%¿-Æ8e<äø ò¥2á«iÝ3ˆÜy#ù•jáóTäÁÃ׋îl‹§X,•PÎdóÛB¼Ü—‡ÈæÑüz'„ðÏW!ßã¬Zk+“qw•Ï’AÚÖš>ªÌ’8ùä< z![æ¾Ù¶cM-s4`JE ÝpªR¶êyš¶^gyÔl^Èö øþôü8*Œ³wwÙê ó¯%Së*˜¿Ï‰}o.:7<Û·¥Á8Sçè×=ƒèNÆ…4”­X>"û\Ø|šÕUÿü-Æ™;}ÉÄLä^Ý;ÚwË KpAhZr6õÀö «Šè ïݵ;*AÈ=ÒP¶®dûYsvXî¨>ãL*yl]°žÔÍQ®ZtÏ ÒmÁ¤Iž$’KU¬)í×w'åVÅöë>\C=r÷Ö5o^Ã÷ÿ¯ëÓ˧ð>v—ó¯oÅëOŽëq3qØFÒká˜)‰Ñ¤X½*ÛÛµ‹'ï»\]ÂÓ… –4êÐjuÔû΂ƒAñB.ˆœ$¼¯X´¬Sî;W`û×üùHÝL¹ NŒ“í×úc¯™6CZÿà[1¤ïæC%ž»{ÃÅ!­m$g”õ$NCñ£¼¿)Þ—ËÂç_Î{N?(çž¼üCH¦œø€¡÷äßyJÍÄ›Ó+ƒ„Ž®Øñçád°æÍ‘–IÉÁiÆÕìOËík Êö:AÈíhHX¾=ÛÌšÿÂò2½¾Á8ó¿àVH[ÉŠ)]£2„çqeªì¿dÁÅoÚ“…»Ôm‘@»RUíQƒ}ûÛÌ?Lglßà Çëÿ¢v»]Šm¤ßwãWÞî€yYÌ‹Ë}–v¤ã"ßfp´éô]?ÉãÄAtE*7žÍLù}y¹0_iô{^‡wÀòPëúη½~Á8Û6j—¯Âv24#a_öð Rù¼¢îžŸzÓJ?¹'tÛŽ”¬_dQùùƒ©µÁ¹åµÖ¢,gÏAjG®¼;pmc‡W!,Ï}Ìà'û oCŽ/é÷KËZ!¼_0·K¾¬Ìï\Ë£M3Hðebë÷1tK GL®ØRËÄÊACèåãÒnžL ¼N[ ëõ0a½ñFÈÿyR\yüi£Õá„åCxý‚qR‡rÁÓ;É´×{~®T/ƒØËjå_L›çéû4·©YÑ{@ ¦¥÷§‡$D'Pþü–³™*ývséî–'*Y”ÛlÛÒÖWW½¾Á8« /³?l»‹<+щÚ$.÷¬ëï §F_ïºÕ®ô½o…[ý¸}~öÊòâØ¹Ûobumè”›’6…fÒ™ãp©>Ågî&ƒÆ¾´Äý˜A~Z¸Ðû&ZÚ)G3kkt!sì¾zÁCiKÌžO n çê{üv["¯éË‹gy',wÓ_ÃîÉ¿ô ÎB¾t±K¬4ƒìjX»Éìãií æCßw'¿+üt(ÍÞ,{þZCb)?Ïú½n^{_÷Æö—/÷Þ?¯CŒ×¿—þÅš É{H´ãÉ”!•3ÈØ€Mí7äÕQýúo–oS‘—ýH»Æ“†Ð†ùòuMH§ìô ¸I.ÜRëöòHŸ‡ßóù’íŸñç˜B.Æñ¬ƒ¥^2™_öu q… "Žmðz}Ëé´oƒè{ë„ÏWLÕâvÒc({^±¼Ž¬9ˆlÅ?Q…q6?9Ü0tÞ^R»ýôçµËdo6yµ¤­ž.ø¹^JåÆ=ÈêkkoUÏ1„Vm¾ìáoóYÓ—GËê>;ðÏÛÕâõ5Þï¼°diÞbDPç’dͪE÷œCg×/3ñíh¶ï4œ>ëÚ¢kðÌxÊï¿„>o-”°}F¶²gàà³mãë ûÂ>Æy3öÒìrK÷“x.f«Xù5Wr8í;ºÅU‹ï*Ѽˆ[ð.ùÂhúæÜ¢ÒcûÇÓœÕí;«›–+k¬0fäŒ"/CXîêŠáUwïh!Ì3øó4+Æ)¶<»ªþøäé8ÝÅù2H™›ÅwgTOÓ*/Ü”+¡3©u'ï“„Bcéé¼M’—Gh(Ÿ·J¾Ž}­h1BArü¼¡WË&¯…{#Ï…ùsaŸ¶ïŒï¢ÚJNýÌ ¤“í…וžjY@7wˆ_8ÅÑ‘x2¦½J,3š¦¤fÏ35NÈu«çË9a¾ç÷;Ÿ†Ìè–?ì› ÛŸòú%ñž¼Th!Y®ú’cáíIËSŠýkFЪRÁwi(»?ÀçóTóÍÏøõÆýÒE*¹/Ë´ncœ5£Dë›n±’‹IX¨¤¥“B/OߘÑvÕ™ú¶Š=B¸0‚æônØÅS>§¶ºoÝÄž3lß_WÕò€ùyºãL‹Ý¿&w΃$çôâ \I' f-®2oþbê¬p³^Á¹È9nÛ~öH*ŸXN±Úï;Ocÿ^¦gvÅö¿øu­°€qNä˜ÝdQâAâ(¸õà¡é¤ÈΫïÎåZJûµ{=£YIž(Îzl-Zqzß>¿i|ù`¼~«úîK°Ÿl=ÊÏ{øu ã=¿eÓò÷ùý…]餩£åæýë—ÑÒ¦<³mˆrð†Ó›ù8ÅÅ ùØÕn^°ý3>^šÉŸF¼þ¢ùÊâ3F"ªû£çÓÉ’ë+¨k¤®ÃLkוôÜ;RûøÔp:ø\rÓSI±¾uóac«•'ŽJ|ó?ö“Íÿü×iVŒóÜ¡+g½>DæÖÍÖzÂŒtòhUñ»Ý+þL)i1»ÌÀ®düA.ÐmaŠ9,ÅR¶ãççb_=cûBü¹®áÄëW]6bÕÁ˜Ã¤EwQ'cÓIî˜;ÿ :¹‘¶dó¹]ˆìÅןÔÑÒ_ëÍù¦q¬ïü„ß¿,æ»GÄôÅö9ø}’¼_†ß“e:êìqí0)¤u04|}­Î™ìVЋ :xZE‘Ÿ¥’ ê„$š<¡QÜSw,÷*WwC«}çWlÞÇê?¿ž¯)Ô¾.‹1?ß>B¸ÕA)u:ùªËšŠWÒê»o'D¼ùAÊãI´f«˜uã÷ÅR–ãÈrÍX^¿Î¬Ÿi_[Ž×?/ç\GIxêñó%"ÓI@ø¥u &¬¢O†GÆyÜQØ·J¢|j¬/÷”ùŽÍËØ~&ËCÏ”·Œqò¸Ÿ+ï>Œ\ggVédîáï–*¤«éö¯; i’A¼1^µ’(_Oâ(›Ÿ4ÝQ2bXŸÚ¾÷‹åRòÏÓ:¾^¯O0ΙYt‡©í1 ­«ŸNª½4<Úrx5-9°cÙ—Âɶkoé[íÕx…}Ûã8!O¼¾P_ä„åS³:ÆÞ7¶ïåõ ÆéÞM;zðƒcdó’ý5®UN';Ÿ7ë9·éZðGëÞ–†¶¤¤yɈŽãiôø"pnåïíI|9V|}yÂΣüsO­x}îi?4æ8y…ÕBéé$ÛæoêV=¼†þZlš[¾¹ áV/³%è¯ó–©Ž£,ïÏë ö­/e‰šƒ½¦¸BؽÿÜu'Æ1¨[;æØq²¹êùȾ…ÒIp™†­J’µôéÁcaëbÚŸ®´pý0e<-ýòJ•ª–8ÊÎßùÿ]Í—·ÍÖ±|VíÌy™#°.ãÊú76"ú¶˜({:æ Ä^Kãµm{;Ù†ôìSóÙð£ã)¿Š¥l]Ïê«Ãì9ã;òÖ=!/ã|˜Ó~a¼˜•‡›<¾ï$ë·XSBf¢ÙêÍ©Xgoán¹|ýj­ùüö¢ì c(›_ðy~ÍÿÁ—l–¯üù¹ã”N"oßþj#9uÜ=⬓4š¹©Ÿè¸‰^{oç¹$›Ó±ý^ðD:jçJY³’1¾yˆ W…oýÇ~²y™ž³ ã¸W~ójdÏ$%Á8ùÚ.'Y;p…sPü:Z{O¥j¾ïD&¿~–ý—•ãi­È•¥Vœñåù²ù ÿ\¼/ì7Ý a÷Ú*׺;äH^ZŒSAÞúû¨‡'Ȫo{„Zë$]® ï#‘­§­Ênk®Ð™nÎ<±óx–1è;ÇØá¼Fê»?ÆÖ™ì€­›ýs§ÔtÇwÚž'ÉÖÕÍ",tí¤9å{n QC¿?Ö')ŠØ›Ϧž@ùóÓ”Ý÷b9ïü}Û»Â=χ!-› …RCH ]Á_<Õø{lVŒs)ôÄí‹gO’¯ûåzšä${º–*³çáú2½àËørQdUú¼=¯›N l=GùEòBÓÂs·¥oÞÄ~–ÈÅÝDlî;_ñúãhºk¿æ)r³çjj'¹°¬vò“ýéBÏûk‘Äï~y-»nâœëbèòΣ‡t0v$ͼJR9ÔxÿºÌã›×ò÷[;멆¼FÞ“¿Ì]p9EêÅõÚ^®ƒ“$OW=j=xV¡—ßu$ÞøÓ€qT¼¤mÉ ób)¿?×P¸¯!÷å×_6÷\º;„^„øò¸½þÁ8¦;¹7?ûâ4 ͸Yc'™2gN©Í4©Øñ9ã˵#Ó>Ô[:d¼oÝÄôƞ̟Lo,·•ÍC¼þÁ8¥éîûçÆœ&ÙûDlvÿà$…wÇ-ß,ì/¶$_ÈöOûõÖ$:¯zÈén»àΆϞ칓Cb¹ç6’y?ãÔL*ûuõ_N“Ú_ÍÕç'Û-{¶QÙ¶PEãÐÕQÖ&dÛ«a»šÅé„ýÆ8ÊòHÙ¾{°õ¾’i?H‹qöú&_i~†TïQ³ÊõÜNrguxí\[hä¹ÎߎhLFÒó3¿3™j jý¸µ_6›2Ÿ²s|¶®òŸ?1ΙA´\w†T™(ÿ!þv:6×ãë¶ÐËÁ.ÎhF^W=7~ïöIÔ9'Îwß…Ýß`Ÿ ó);wä×óü:ÊŠqŽfÔžøSÞ³$_yîd0ô–4'{>3­&KML)Ù–TK{cN5M¤Eª¬œ}®J,eû¾¬°Ï…Í?Œ¿%ôÝJ2w;1η—Û=±ëYr´ÔµãyǤ‘[¯·>>nò’#H›†cj%Üœ@׌\zqÁêÊö}Ùs†ßGyyW48©â£>Sžù^ó¨{òÂEɱ;Ï’»¥GÏ]Ó0ÄȸDM3Í£­·¨f§NdþüÙ#ÚÞ™@7¶ Y%†¶ ÎaÎÖPÈ=!ƒÇÍ~ö¸Ü³å×OCÓÆ=ÖÓ¡„?ÇöŸ1Îùùƒ*m®|ŽÔ-3Ø^¡D)Zx£*ïVj©õ:E{ë™Ó/Ýý0‘FÍ»*ž;} åõÚT¸GÙˆ|“¯}`ë/…sNwHê&îÁÜÐwïÆëŒ3Àû†œ#ܭ⯟§’'Áêwc¶ÒžKÏU8hU’ôžMò¯é¡£¥¹øÌ¯R–Îï£4&ÇçO ¶®Âßçs ß“hDØ9‘×?ççe!ÙÇzΑÉa«ûÜ‘JÆŽ©ÔzÞÙ­´µÆ8tsj'²vþ¦—MB§Qïº~é@ß¾ ÛÏf÷*ʰíyûaÈWÓ[<ø¢fÃLçêZŒc­ða^e;‰¼ù ÎýÑ©${rµ~»ªl£k†ôé±ÐÖ‘Ôª{o­¬ÀtÚÑ»aCY¾;»÷Íϯ\Âz°q¦ü[#^£òís;;iåÝÈK%R¯ã„mÔ6qÏÜ^Ȩ^¢€™ ¦Ñµç à‰Ò°ûÙìóf:æ×?m?FxÞàõƒg½=hd'K¦ÑGy §’´ª' ]¹¼F¶k0‚l =Ùvýè©”÷û@ú”».¼X!ÜwQøöƒ?_º®ÌÞ_|÷ÙÙ}(¯_0Î4]Ïg¹ vÒ?{“ª=7ÉÞaÃÓ––ÙNù{5ád~䄞UÔSéõû§oËMÙ>€û×iWçthè{~2ÿó÷.ëgÊ= }Oί3ì¤Ì°Bk$ n’…–.Ök;5,ëovbRêh.ËTZ7J®n0ª?eóqvo½_lÍîŸûß«cn·ºÐ ;9£¯<µSäMr¼êÈÂŽ%Û)&WÎ-[÷´–®eLîAô§‘Þ…xa¹Àì~Ë æÿ{›L÷·åçmР+…ŸÙÉí½ó’{—¸)œOm§3¯ŸVáU 2½èÉCW®L¥—×=kP®ÐaÞÑ„4\´U]·^sòUž.ùÉãß÷QXž8»ïàõ Æ È~y}×"çÉþ:]64øå™ÿeT¶ywÐvŠà Ã,­‰÷ÚÞò©ôl‰9%  ÷wš^Ï RQnº{¦þ áû8ÏCøû—­„}T~-ÆiÆ,œ'î#-·-7HÒüÔœ;èݲ<-††“·µŽ$žÙ0•ö~XüPõS±”?ÿo!ÔÙ–ÂsàeÈñc ÙJå~!ÜKnữîõ Æù!b®3ÿóâõ'Þ Ñ—‹§[°ƒözºøP‹œHçKsf&H§ ûªqÔûµ‘µmIjüÔ™·Ÿµ%êÖ JÄžî›wð:kF*Ö=U5t+ŸlÅ8®/Ÿ6iúyÒkL¬úd›dÊ—Ü}u¼têIÍHR¥ÚëåooNîÓÄPvš­ßØsšíwó÷³ëgºÏíÄ8›õÕæÛvžéºêô7HÞ­·VTí¤k®e_ºª}W’P¬ÇÔ„þÓinÑ]×Ó‘Ñ”—°¦tkR¿¯1øý š¼b^àÆõý(ûž;¿cóÛÇýúMÞüæY÷[~ÑL¸‡Ç×QÆyÑnkõ-åRHòÞ³“ÞÖ¸Nj7®X¹O¯Ýt`›¢W‘_Úr Y´¶É˜wÑ´{î³ÅïÕ#,|ÁÓÕkŠPW›ècšºîEæ¼m-ÆI”ä}1¥~ y~òå®…y¯“Û#*\[~i7í~¿ -w®3™ß=W¯Æ+ô41t ™r.†²ó”y·l²®¡ï|€í‡xý‚×½÷¶èñ¤N)d”öñ©…®õÉç¬k¡üú±iXÿÀþh=¸=êToY,eu‘ÿžL=_Ýd¿ºq¦ïOY1Nôw-BËÆ¤ï½M×H{:^žm¡Á[Û'¬[Ýžx·–Ϥ·¸íá’±tNâðºæ…~øûûÄÎÓ›6:¶²ý¡F™æMNŒ“ñ O¢’Á=د‘9Úß´¥[hh„ur—äÒ§NžÕÕ&L÷ù’ßO«ïÛçàÏ·nùæéìýãçOü=„€±÷ä­ÚÇåøS ™ÒäËzÓ›_#†}k6¶ ÞCSÚ$T÷´=9§˜ÐÔ?ƒ>m1çÞ›Â÷Âê¶/ËÖë윛=ïøõZiÞ/‡ßÏO!^y–¸FN—+?lô´=tÖ<]Ëû/:‘œs—{•GOKtÙ·¹veóc¶ÀæÏl?‚­Óøûnü8rŒsýfŽÁÝϦ E³µy’q•<Éxgy÷låçAQd¥8qÔÈ+zZiØò[+ö§ìuøýÒA_Žv‡ëøïÛª0ŽèDöŠš‡)djdµI +®’w%FÜ•H¦}çæß¹{uw¿¯z*N‰ƒUúS¶®aûYü¾—5„ÝKaûƒ™¾Ÿ‰q&Œ-T³oá $~öÈää^WÉÉo^ÎxœL«ÛR V•¤÷‹v³è3CÒIϯý}ßeë1~ŸðPˆÿ<ֈ׽]dÛóZµ.ëªæÝ+¹J¸o=m±—Žô<ô üýñôb£šÉãý}û\lÁî‡}ß_eûü¼‰ß?·bœÞ›¿\Ÿ¿Íò!qKç…nIkSöí£êË¢ítERnü&†Ó)»7Ξƒ¼ÿjúöŸØ>=Óû^³×/' îéÅTHj:Eë½RÖ»PÙGï´Ì±¨×E™q÷î·çgÐí#Ÿ™Ã¢ÐuÕZÖûqA=Ÿ¾Ø|‰í§ð÷©B„y†pïY{O>ûI»sâñ¾É.u6Lt\S·.÷Õ~Úò»˜œ¦/º“¾-Æå±Î¢ybW œ½#ÚwþÄöØ|™Õ¶æÿ¼cœ^Ýš•Ø–tDÌ"Kná w7¶­SqÚ~:´aq磄ïë©÷×.CY½bç²lžÉêO!4Óþ ãÜß½%ǽéHÔù»²ÝyäݼÁ©/öÓáK”ó—-W’€c5Ô®åz_=cßÇfÏ)öûx}×ÛÃ}­tö¢÷|m±]!ù‚Š„g?@CO¾pç½ß5—œîøNO¯·š˜X¾[,åýK|ß/dõÿ¾p(=§ÞÀ²5„ïÏàõ‡tÌ÷Ku¼~þÕ¯Ê^›y…Tyíèj\u€^ LÉÓéU™½»Ë;GùY¾ý vO‚ùí˲÷…ײ}cÿ翹¿ÉçOÿÝ=ž„ßÑð{Ïô)þX&,ëóĉU%äb‹!Zp%Äký“\lPAÔ6 …° ÿD;»‡Èú¯,Ç!dÂþ•>O,6kŸ§¬ý<¹>O,Ö¿Ï“ñú<ùgÂú÷¹óï_œ5k"k˜ÖĿڿX,d‚ýQÖëal²³ö^— =ž²æÁúç}¬Ç×{ËÂ1 ü\?×À¿«Š„ßÁÁ}V¢QcÖþžËDd¹Ø"!ŒË„•àEõÀT¯íO2a9A«È lã?ÑÊ!d‚±žÆË p ™ˆ%›e"fÍÅÎÚëŽËÅf™ˆþ¹Ø\¯»¬¹Øþ™ˆþ½¡ü{}fíáž5oÇ¿‡û¿ÚëS"äíüQwÖëÓ&äeígÌeïèÿ Ñ?çc™ØÜʋ˟`Ïáÿ×jàçú÷ßQÿ8íȯϧôòw‚ J-p0ˆÓĨ¸„¬ ƒ «„`­@Ñê'ϧ3a¥³A´Øÿ‰žî"¿Ç”Ya²°5Àd0„ˆ` p9Ìa0H"p9Œb0Kb–žxbüÀ 0 ÂD‰Àä0“ ÂP‰À BöŽ/wÿì¬YˆY³°Yâ¿#{'ì#Yˆ,{‡ëåî©õ=ŽYo;+ÀØzà©û9Øn „Ù­@Â÷·ó}Ž?çY|®ÿê_ðotrŸ„h¢?èú±,E–‰(dq™°RˆÖ W ì’ +‚ 5Àä¶I·Ø "7„® ‡àM ¢O2a~½Ž?–iá²ÿJ&6ËVÌš‰mA0¸Jó™Ø,_Ñ?Û\ö3±YÎ"—ßãa0š%KoЬ½Þ³føø÷zÿWûƒ~ª×;gb5° ùcYûsy>†,Ù‹Y3}>–‰ÍuÒÐ~®Ÿk`Àß_ÅÀ!B p½Þ³fúˆ!Lp%jˆTÜBÆ…Q¬ Ø€Â5âýTž¬ ‚6D­Ž¢ß{ _Oäå\˜…LìDàrÃaŽDà ˜Ä ‚`-p cA08æ1ƒ ¼Zàa0’ÁLZà ˜Ê ‚`,-p0!Óǿ׻¦OÖ Æ¬™Ø,ƒñß‘é£üH#Ëô1FÎÚ+™åaÛ€”Ë ž5ÛT0¼ Hazƒ`|®_ò缋Ï5ðC ”ÿ÷^Cˆf1j€È!JÓ'2Y¦vmÆeÑÊ Z#qyÀñ'Y´t"p„m"ˆ[@‘›@ „žœ@Á›AD¯²h¹l3 "óÂ-ä7þ•Œm–á˜5cÛÄ0¸KgÎqôÏÙ¶”ÍœµíŸã¨ „Ѭ@ ³é€ûzÄgÍüa=â•0£H`H=ðŒiR˜Ó T ì@£fÕÇ'úÄ&Ö‡kf0t"p ™?Æ,ÙŽY3þ(‡›ËväNâ¹¼ N¯ÜŸ?«Ÿªÿ®º÷ŸªyµÖý3uîÏjœ*à¿·¾}ª¶ýQ]c5û|Ì ¢JN Ï’á#ÈôÀT› H!8}.>¯ŒËžU;¡~(ï§ógå¦ Bœ‰@"Õ£v™@ jW"plòÊ´Àõ‰l w–q+ n3‚ÀµÀ t Cì:Á[€¢×ƒø-@ è€(a à .CX€¦Ð7P =:˜Ä%Ÿ'kcÖÜl–ÃøïÈçQ}$‡‘åó…|2 p9Ìh2³?•IÁMJÔÀd0¬( Ÿka àÿ|ž§}ž§ý]ó4™0†›{/!D ‚( Jó'òY¶¶XÈ&ã2hå­ B¸‰Àù'´A´¸@„mw"pDnAº¸@obˆ^'dÐrÙdV tÀ ”0‚H`½³­ü 9Ûþ¹ŒY³¶­@óè§tæ\ÆOåmûç2“©€ H`6=ð%LgOÿY>zà*˜Ñ¤0¤A0¥Ø æ4 ª ‡QM fMN ‡i@ãj€Èa`„‰SÈ%3ƒ F \B¶OÖlƬÙ>”ÁÍe3r7Àõ„×-÷‡«œÌþÊüÌ¿æýOëÝÿ­¹Ù¿Zç>Vãþ™úÆj›]ûOÕ4mÀßSÏ– ï±…û7@L Ô.„¨uË <\ÕÀέ5!4;ÆåÈj€ÈQ¯L’#«€Í ‚Ô9ê” Bœ‰À ©A¨Zàa¨S!oLÜ ¶1D¬n „˜­@ Ak „AØ †¸uÀ ”¹H t=p%oˆÞ ”¾HÄø;À¬0‚FÐ7PÂV )ôÀÃåÈ¢FÙ€&Ñ%Œbý.snbÖll–™¨ ‚‰l@ #3©È`*#ÁXàrÌÌçbÿQ^¢Èa:“7–œ@š…LìDàr˜Ñ³dbs9a"˜S@“š€Fµ~ž—}ž—üýó2…ð­@ 1j „A”–Od*²œl Ī2c­A¸Zàú“ÌX1­n „°­ âÖƒÈ-@ ¡ë€(!x+@ôz!3VñÛ€ÐPÁ6 … ‚!T!/Û?_1k^¶ Haƒ` ÿ|ÅOåeûç+&S;ÂlÁp*`RÏ<@Ú€&4FT;ÁF ‚)5Àä0§  ‰À 0ªÁ¬Zà ˜ÖaÜDà Ø ‚`b-p0˜ÙÄ0´¸ò2Ã`r Ãèºdfsßá:œ¯GîÏ¿²ö¹öýÿWûþ™ºÇj÷¾Y¢ÓƒøÌ ÔDh"Q@AA˜_~l"pjœùOòc] âµ1¬N €Í bÖC³1Ä­n „È­@¡ë(!x+@ôzà*ˆß$0€¸F° Ì  ‚)l@ c€¨`Â$zà*˜Å¤bü=À™Fl@ óè¨`"ÂHÁLj`R˜Ê<ÜZæ² ¦®æ}$Û¤0žA0ŸØ &4Œ¨ ‡!M ¦LN €9Í PÈÆv9Œj0k"pLkA0®¸@ lr±@#›AP–Lls–¡­ ‡¹L ÓÁhF ‚ÙÔÀd0Q0žØ 4L¨ ‡M †LN €1Í æÔƒI-@ £ê€›«0l «.ãZ€æÕ7PÂÄV ‘õÀT0´ˆajp%Ìm\ÿ‘Lm. [έo ¯GAz™jßÿ¤î±š÷¹Þý÷Ô;mÀ®æ}jnÇýÞ6îó…àôÀÍ5@xbO@š@ ê["p9wn ”¥…;c€0] â´1ªn „P­@±ê(!Z+@¸zàa°ˆ!bp%êšH j=ðÄmRÔ5ƒð`WBðAôj`Rˆ_<ÜZ&°)Œ`Ì v ƒ)Œ‚1ÔÀd0ˆA0‰ØLŒ¿D0ŒØ Æ1æQ;ÁDF ‚‘4Àd0”Q0•Øæ2SÂhÁlj`2˜ÎD0ž8€4@˜08f4ƒ ÿÃÞy‡5•¶[;–™ÁŽeÆØ±#b;X±ÇŽ B v¬DEEÁŽ;vŠë%*b¨b6°cý[ÉÞÏðŒÎñ¼ç»ÞóO¯ëwùÇûNn“ÜëÙOËZ¤ ä„ Ì!N? Ni$0‡PU ( ØX ƒhƒ@p†xÕÀV\ €c byÀ¢6‡¨U ( îXÃ]`\KÍ÷BÅ1ÿ¹.¸A˜„«rä÷Ô¾!i$øT¶¢¾꯿1ø2ñ<ÓçåÖ¿—¿8Å„ü±‡‡SÜþ<ü¢äC*ä†/‘|âžÝMóR|ËïÛ¨FßGc6> M£;¶UÕCd—i°.»ñ5{ÆßǾtLm£,j1›TìZPýã#D;[ÑŸÁNòƒã~0/‹žî5R^ÀßBgx?FÖ4j0uÒ’BW2©ŽÍÖ ÿ`ÆößlkymW?rÌj0à—ÓÁ,x„Áo¸ä;#ä58Hù¼žg'—rEÜìùÑê䯶¥Ñ‹˜¥v¬È¤f…OÙN¾ËXj»ØØWÒªè2ãö.ZÄÆÏïáy¿ðpvóƒ¡^GÚ°ÎöN‹%¿KÃËÉðzž2횃i4;åÞÅìÁ™dS©ÈÕ2õÔlÛ[«7ç݆R­ÏnÝBv,d©gv¬l'ÎfõøÜ.ęýt%ÄSÊ7áy4‚ÿ èû‡×“ì³g™&‚N‚È2éYj[ÛX5[}åñÖýJ2Ú¥.d=^.é;±Îp1߬u4¸ôù+‡ÚhtæHÜÇÛðúJ¼¾õö×GŠêÒ¨YÜ®ßê<Ë C«b;ÜÝ­fãÒÕoœt'›µCƒgO f…Ë\Ø4¶´·Ô/B._{éóà}Å}ôxþ™¡Ž uŒ6oӨ܈jþ[¢3èÅ[Çî~z5{•4;ýºÚCôÓ aÛíO‘y2Ÿó=)bQj’šÐF½¼yVÖµðÔGRÞÄ!_ƒágÑ÷Oô/CÍ£¸mz}5ýRóÓ›IT=¨ÚŽQãØ¶}Û¯ïA6)9[ƒcC™óâº{GyH>O‰]ÜnÞéÞ^Ì7Ë“r(ÿŽ>75êô5–¦ÑÆg;Ïvk™Aç»\Œß¿,Žékž?ºˆ;í]tÍÇdìRÑÓ›q¿'îÏ)ä4=tü™9þum øhëPÇàÚo^4–Ô4±{ó8¶)*:•ÇÎ/qójš+[v<ÚbÖRV¯ÏÚ‹w,}Øþ.GêŽk%åðpXž3Ï}[óû$™äÈí–7)þ[:]>Ýøà„µéök»{EËf…¢kvÜfuôŠ_{~K(kÕ~\âºÞŒçæ ¹V’0÷K|š[‘ï+øŒÊP§ËÕÅ÷‹¤“1–zH:ß¼úÍø§Y«¹Ñ·½ 6QåBÙúMZ.Î?¾V’/ø×>°B~u»¹ºrÔéi4`J£ ^ŠÑ¾µÒI]Â`|ZôíO‹K'ªOaöõOŽñd„”¯Êó,¸¿ ?Ú(v¶=æ(öµð~”¨SèꀘdôÛï6F5»’FmÞÆO<»þ4{´ oŸ¤ûHߢ~3«B!RÞ±j¶L>o‰½˜cd/ùØñ÷Ãýóû ©Pdzþ"Öo]²dÏÒ4º°Þ wšeÜM~>¹Á`²HOë¬û-„%Î ë¶ûðŸŒçåpß<žCÈ?¿¹Nóª¹$/à?Ž:4pH{Œ÷S,( Jß4j9àä@ÓâñìFÙ] ÚŽ.bÎEë¿u`‘óFŠ>CVb”GÍóG„œ\G1ÏIðS£Žà÷šF³ ¶¸•Ò(ïÒ‡C{:Å3½aX¬àFu2¦`¨\ ®¦ú &äõXÏåuŒ:ÁëEÍ[t=-#æíj2)ôr*Åé­Ý`I<{²üÉâ?»SÿžÓ»&/X̬ŒÁqÞLzN}Ì[HýkÔÜùÐóïÜŸI£kŸª§¬K¥õãu–¯Æ³U÷B§.àA2ƒ­lJ0Û§ÍØ·Ñƒ ùKb*¤>åþâBÿvsßaêX¬8åhmè²n÷tÏT:ì^Gv¡~+:«ýêzC<(°þ«÷ËõÁì“טSnLðÏSˆ¾›]$ß]þwx•„“?v&î›eÔêØ ÓÈueé•þMRÉàbg61õ4\î4ú|Å›÷j-s²Ü˜ëÁÄÒòäR~Õ¼cƒ.Îõ½/ù ù ø$)Q§Ø —Tó}id¥8ñQ µ¨ùi݉Æss‹ï²i@JòÜÄ|9q?&®;î“Ä}Ÿò籩PÇ>¯¹ó¸ÝiÔ¾å‡"÷§Ð™ÚÝ÷æ|I`¬D¿mÃk(©ZÀ«1#CÙ‡!…ózírg÷ ˜;Òåy Ç}®×AòÝâ¾LB_µ£__)®Íë-úû¡ÎCLU ;ãx”BÞ÷cKî³?#úÉ#«bâ e#Ƶ¬ïãÁ²ÑOçÙzt¯ìþº³ÓðÝ;ZeŒ:g~A_5êÌY¸ÛõÁŒ4jk5`Ý—)ôq„ÓìÝ“Ï0Á¿sµšÑÂÝ;„9íš‹Žð`Ü·—û·q_~Þw<_(^ªuô§=w+û¥ÑîÎU¯gVJ¡ê »—²ß{†)Ê9Ñd0=hxcØðöKؘ Îϳñb‚ñùÛ×¹Éüs£ÅÓ_V«×Zò·7êufbrЧ‘¡zeS-{ØMµgÞYö¸a—öV \Ið½\Ä<Ü_ŽáÃÒ¦’)Ïeåó|îofÔ êþi©ÔÆ.kù²Käß±v‡çβêsvÅ…œq#ý‚"¹o?-b¾Ó*?K|2\ÊãáùÌÜOð‘Ö9ðqßo•¬vøia¾Ž:'ïú¿˜ò,•šžéïu«â%ªaô¥NdaåžËþk\üa‡3!WÆNôñk#}/|~Áó'zÁë÷0Ë¥ÒšµEÇ:mM¦òÆùD6NSSm/ ³\»ò úØ2\~ØpñùÞEô©ë.ù"óü?AOöb“ðýëPÇt¼ât÷k©¤êÒ°o’c2Mþù¶G\"{P{erãýÞ$ä5†°; VzHŒã¹Ë<ëŸû`æÿMsä9w®*š'§Òë-“Y©¼‹Ôi^éfSªkØå+6g_žò¦Z6OÜ×>X"æãzHþ¡‚ï¡\zòþò3ä|ñe¨3®ÆQ›‡S ÿ‘YõÕiø“Ã¥/NÒ°/ûJþšžìE¥æMøÅºÅöxîj]z wqÝc#ù* ï#KÊåæ¾ÏBn¶èó:IŽÚ”Ý«R©klhgßÚ©­®bõ£ç4ì:»Özïû ìeðbvå±Óë ®žŒûáó|2îƒÉ󹹯¨0ÿæwJÔÉ{ç67Ï'•|übŠ?Û“DÖãǨ_ñóóptSìp£l×¢‹×tXÌüŽ=KØ;œñüKž»¬šÿé]Ùª×N'Lûü[é<aÝkS`©BºÛd Ù¦Ò­ÏC[x¶M¢àÆk<¦<Ç2+Œ¶>fáJ·—Ýéù@¹Xô¿ôa-®U|¯¬Ò¸Î…yþ{ɯ^Xõ!>Þõ‚:û qµåRiȳ–WÇÅ_ CøÙòs,qK‡—ã°Ž4æI,fÓΞò¼ç-­ó_Êî4uî‚×k¼qà¾ÕÂó[AåS«Îéij/èuîyŒýS)ÓùôUó6h”Õ«5Ÿ.œc‚¯ëPª9y‡vêâÅ, o §›­<™ÒFò]äó$î'Ïsóç˜êP§LdHÏaïRh@À™ì¤ç©ýñ~‡?Ïrå²ì3e\èѸ[#7Ä3!ÒCòçõ¸¿+_Ïs?fžÏdÔϼy…Z:³Ýé)ôúJ¡Ë­ÏSÈ”-þ7:œgÉ…4ë*IÈ!_Ä:¾ÎT(éÍxŽ4Ïùáë>ð<Åüy)2ÔÉ^£­µ-…R³ko:{Ž–¾x«Á‚óìÙ¯·üš\v£f[ž„k´ˆ5nÔ£«ã‘áŒû6›?ž7z‘UiÆÇ5žÿ)øŽŠÏÔ™ë5*×Û?…>Í1LÐÏу5.îÓγ®Æ†ð —KW¼²<¸ >½^¬™1 ÞJ\7ÚHϾîÖÉ-IÈïöA”¨ãáÅ~ªs %þÚpÜïw44öÂÒ[eX¹ƒåÇîßãAW#z–ˆÞ¸ yÉnLXï[S¡eªkSm¤¼4žÓÇóaó瀪P§ƒ¾²£G½šf äÐÐÚ‡ç>n{=‰Z“æAêN*¼Öt3¨Ê^é"­“!¢jÇ®”ÓÎçß‚Yr ·çÌ]Y‘˜vKŸæ´rRøü™Ïkßt"a¾(êu&W¶h©¥ºuRm?wH¤™‹ÂjOêž$æï(i¬qἈÍ-s÷ʽ!®Œçðq”ÏyŸñS!§AÌgšŸ#¯1òše©-Õè¾0¤—æ,9Ž£±8‰ ¾¾®d®¬±áØ‹¬áNûº9 .b®@s)çòñz‚©­˜k!ÎÏPG¸ïÊùnZ2¾Ü ³´e`ÄÕòç’XÖª}¶¥MÅ6ŸÙº€ v?º=C)­Ë–=0Lüm¥œQ>~.¾äù¥í/vrFå¨3Ä0-,¯çOgéªï'e ÉEvkçn­¼ñÑox!»»qÿSçQlØÎ¥«Î_s$>ôùZZß¼ÔÔ(ÓömÏó'%êe)³ë\¼DŬw(Çœ¡%ûSoL²½ÈÌÊL·YWk|ÿXõhá–V~•ŸüЧ”gÄý~…çÎc‡uµçùùW~îÀ÷óŸ*Ô©`øÏW\¢y!A –v8Cdž¤Ù‹Ìjì´6ý‡’Ϥñón/™ÏÊ5¯»,×ËSÊ}Öc-¥\k>Ïá}.™0HÕ°8OGOš0%™ÞðÖÙ–ô$ƒ{þ®-AbþRÚ÷᾿|ÿ¯£})”¹{Xûû2Ôö1“I£šÓuhÍ¿¾.äù2òšQ§êÛû-»·H&,^þP׊§Ã²íµÏ[\bòk#3«ÜNïXí†ýZÎ?¯!LØ×k.¾+iÃ÷…y®mõšu„}‹ô)N=áYöiú<È~\×…—ØÚýI&'vûÐá£;9Ÿy.[¹sSm—-w²%>îs}r¿fž [ ßuÆTô§Å¡‹ôù¨ÆoÅi:VmSØÇ—˜m¹hû…¸>bEûÙô[8LÊ/àþÜ|~ÃçïBþ¤œøû2êuÊR™Ž»H “3še­NÓ´RcϬ«sl¼É³ð¬ÅuƒX ·1oÇo*æhÊ)·Ò±r³=´™¨Ý–¿rûxî9ï;£~PÇÚ¸ ¼H‰G{ļG9;.ÚýÒ[ËÊt©îój'¥7òªrÛy>ö†‰ùLí¥s !Ç$[ÊQæùÊÂ|HÜoFß‹¿jv9‰îl³|6nG•‰\ªÖ-»ä°ÇŠƒ•Ùнq-ʸJã5Ï¡äÏ>n7v¼!ϪÀ~¦ÉÌùwIt»ízõÄÁqTìTç-môÌ#1tŸ_ WZ³þj•‡ç² ÍÞ¸ÌHq‘Ö#¼¯ùs‡~|=-Œ?âºu–LýKÂ$ª÷‡Âö^©8êžU{jßÃZV±‚âE‹ÝÃÈÛ½r.ò½‡ŠãŽœ¸xóIOô y!ÍC7–Y²l`ŽñÜ#£~PGØÿ¹@ï]÷w,­US³=¥Óú¤kÙÙ~ý㯘ºÒ‡ )¡Ï-Ù‘§÷wýztãó5¾NçûÌü|Ã|ʵvyý(%ꬒw}|õæyÊvŒyts¡šŠ_ïyøú+-ÛØÓÇéBK7úP¼^Ë…{Ù§®9ñ1E1㲸msi}Îýì¹^ùx?H…:6EŒ 8O昽õn¦¦·õª¤°ó‹;6õ Øß«VÚº? ûÇÎRN¨gД„ý® žßÊÏOò×á¨W¨É°8Ûóôò¾ï¾7‘ŒL·nõä˜ÂfŸãwÜ“VÆLé4$U_¹.bûÑþÒ<”ï ãèU)ŸEȵ².°ÎV£NæDû”IÙç(pè®áŽŒ†Î?x9éÏÖªKÏÙõƒ¼¨²‡ò÷ÄßÙ­Q<¦îÄ×ï#Î3û‰9šÞÏí<Ù¤F 2N»Ëõ£#eìºßNÐê„_?ü¬·?æí^.4ÙwжœR—(µ:…==VîúÖe^TuÄ®¿gÏe ãFzo&ô“3íöúsæR­·8ߨHw¬ÊG5‰xåÐåí„R/bÜÈ}ëÞ[Ý» 9Ñ& sä ùsäxb¿kdµS´¦ýÛÞÑq)¬ºùÂÀ?¿xÒÀ˜dý©Ø¹LÈGS0¡äbn¢œx>ב)ÑråÂâøi/®‹„ù uê&nHÓÐÁ}ÞžÏ-ÐùS"Õ©_þH)÷dæ³i溩lÓøçCç&+©èýš}вÃ]~=þ̉ñu'—yŽ+_óõb‡«Ún\%æÍ N穬ÎÊDjô±ÊͽqÇ)ì~™é¡ýRY×{-WŹ»PŸ_>ØGõ d•;CԽϙäÏS~~Â×%|}Åó—úAˆ*}:>o˜Ho/Å ï8Ò“ÖŒKeÛ«(5W?„úʯ~1ù3PÌé'åðõŸïHÏQ_<¿Ö¨Ô9ö®ÓÄKÇÎÒà~ƒgéŽQñfïK¥§²áçš¼-k0µÁTëAÊ\–i·n‰É˜Œï ãYC©ø¸Ãó~óŸ éþ•ç{$öìðhÿc”zeLýÃ;SÙ¤/Ÿ’×5BÕØÐA[b’jõc<ç§÷ëF~»d%=çøyÏÌ®n²(G^ûxªUŸggH=ÙÒ}“É1ê8>÷Nh|*Û÷úâ–¡÷e@´Gm•˜OÜKÚŸˆ¼·òÑh뎒~ø|dfÍ»'¾„´—ö•ú1ÔiÜðöÄeg¨z©MûæÌ9J3wèÛ;;• 㤠zÛÒç€Ã,&žgKû-ü|…çñþ®±%å÷„ä‚nðú¢‚Üæ7?CïNöðSû(ÕþRÿjd¡4öºtµR^)I±{fë?¬U¬ìø›_K¶gü¼€çùòük!ʦÀºZ‰×—Õïn~ñ|ygðú)ËÓ^ÄÓ—Î{&÷ˆ¥À5e[eMNcõÃDf?ð¢»Í ‰KsÄÏ¿ãy3|?çò9´×x®r'c °_¨FãöæÌx*îžgùäm}·/çÐÖ4Ö«ëÛ3/{ÓÆÓF]PÍaÜ7®¦ºQÛ®Ï\r„ìûnl¶7%í¹Ð Pó’Ãé„ÄÌÕQGõÌvìúÒ>=¾ÁuÉsVy³Q'Á9r!§óÚËßu>Bl¶Z¶ú’Æ&ÙüQVïãM›âK×ê3‡ŸŸ3~žÊ÷mùþ4×%Ï Î¿Ï.C!ï-ŽæÏ·œUõíašb??÷lÃtVøéÛ™x‘qû»ü\qמñçŸwðÏŸOðq!ÿç&GÒÆ ž8ZûtR×U‡I_=¦s‘¾é,llVÙÛu=Èx<„:bN#ãyÓB.OS©Ï„û!Wøç%èI¸O¡Dÿ _*õLUSÿQI‚Ú&™1P1U°¹]þ£Ê6 H©±vjKq\í›Û¤'ãóq>æý&äD^vÈ?Nª‚yšêb ÍÓ ÃÇ¿&µXíµ&ä’b×ošØHÅtØìuƒ:1žÃ#äÊż¹tqüº,~^T`]Ž:‹ª ÛTMMÂùé!*]Jît< _‘ÖþЉõ^vtbúL&î«3¾?Ãû€Ï…χ ¬Õxý±e aÈdô‡¹\¯Ørˆ.Ëwíz3Õ›¼¨Z9wºy¯Ö©ò1³¤õ&ßþÖÄ?¾¾åû9ùŸû:ÔWåã'÷StêMÒ2›CÔæþêê©E2Øç•­G[õò ÓV¶cH%®£«SçñÝê@üÜÞÿÈ=ÍØÉyR®© ÿn9àÉÚÐL;A/‹sä­f¤.(Ú÷$̱²)yó µnnÓ÷ÏFlLÙ“Íwuò¤Äö«wuQñýA1Ï®y¨:”·pï-žç¿qàû7ü¼Š¯szAW·æ\n:ú)ÌWŒhqô YÖZ÷zB¿ Ö*©­Ë“‹žtJax²ù³®Ã¬–,}ì(åÛ óGi~!¬žJ÷òçCËQ§x¬lȪÇÉø¸Üu¦´ë7¯Ò¬ 6s_·ÅÚKž¹·Eƒb7f‰ãqÆ÷køþÞó¿…ÎVâóMœ—¡Nò§Œ;§2ŽQfëq­\f$ÇÓ‹ª´Ý™Á‚L»D4ð$áœÌ_Ì®+ññŒëžïKóû<ùsaU¨Ó'­éŽ5OÒÖý‡ÞîjxbG229ƒÕî°lXú-Gï3 z+%î 4–öÍøóLXoèÄõ ]sãpÔ©›tðS£FGi²íÝMUb(0eï¨/2˜ÊYe⑨$]ùGýÞmœÍ„sú*Œ^ÕëtxPÉJš—ñçÏ©æÿ£~PÍQý1žgÍnÔ)>{e Ý[TbûÔ²™¬tr ív·a”YÈlÊë†â~@ÆußÃ|Ÿ‹ï«ñût|þnÔêtxÿ䑺Õ*ÖÇýò›É1ÔÎi܃—3™ËÎÇ½ì› ¦j§'ݹ1[Üï¬KË.ø;±™M¤}i¾/ÀŸ|^–ŸÐdIŽüó¬æC¢¦â/¿Ø÷¼¡X§Læå2`¼«3­ñêUxÝ•´ÎÁšÿ>øßÂsÙ’Ä~tƒ×—=Ú•1pà!º8þJîúMÑÔ®vVìça™¬Ë­/µâRÞÇ-E?Îb|?Ï—„û)ͤñùGû6¨siëí/K"Rû]'ƒ{DSýÞ#GOÊd±ëÚô¯Rs =ªÍ’öÕy¾5ßgäó~®SaÞc[ðþê,ÍØ·qùÍj9v}…ûöÑÔ«ò„šáÁ™lÌ„SÖ6žé—>yÆoŸÅøºRèÓ–RžŸð现Ï%žâõ+~™Ò!ða4X:õîoÅ£)èR»ÁÛ2Ùá½Q­-*¢Ùòá©;ÎçMÄû”Í¥û\/\ÿÂyRó¹†á¨³)Ñp!/š ÏNÞÖ##Š*Z¯U–e2Vs£í+åP*7·Q¸¢—¿˜›j#í óÕÄŸÇÂ8ð̯ó¿5ê\XÔ7Æ/ŠfL¿Þìd…„oð›p#“éªh~ l¨$áü\ÅÄûŒç£ÿÕçA©Ã½Åûz‡Nûåk]Ñ‹ç’=èòÛlYÍóÂ=TêÔ÷Y_C$­,cÒÿz,êõÝb{üK&+ãÙxÁ–7n4úËù®Ëû¨øú… ëÈ^t¿èèñúm}Å}€bâ}í7oÔxÖ¿Q_â}oÔKHŽüiÑ&¤í§‡«ƒŽ$³(Û¶a½º—™E›C÷ö Ã*½| Ƕ·íìȾ{[Ó¢Þ†‹#¥}ó«Ï(7䋸.k/æa y2Ô nd8!ØK.Æ/:Šz/}gÓý2»2k±ÿû†žäúø¤~‹ý,¾¾dÂ:ß‘’ãKvß9©ñÜažs,ì ¶ÏwÄùê œ§[Ûf7}nop¢hÓ“ÆÊI—Yõ’-²Ò§z’Ì8œÁŠ©ýŠÿòÉš ÷š{‹ç¨ÅçÚ )šŸ{ï_x?JÔiýâ‚28BúÜÆV7©´ñ2›6¡¼ÙSîxpõïu·Kj—Íg.³´=GNXXzÐ"ëy­×Í`Âë~qàãÏKäûŽ|ŸŸ[ó{YFý Î…ke{–vÙFÁ/’.¸½¢A×*„,zx™m/õèÏÂQntnÉbù‰ýþŒç‹ ùЭ‰Ÿ£ð{¨|_ƒŸ؇FÄ_غ1W6“pÏ%šb_Ætv)“Åö.¬xõfy%UÕ÷þåƒýlÆïý~½OÃÏõùø)ä»¶•þFý Žù°›Õ÷gl¤CŠž}q§|Òí߲ءÉKõn #ÃÛYv¶8ü5àëO>àã5¿¯ÌÿFý„æÈ3êõ|:Åb-Í,ž¾®÷Ë(ª·½c­+NY¬ö˜]7'-F©;û¿šïÍ×ç-_÷ñù8_? ývÃáºákxeS Ç]†: ×uWÒà.Nm΋¢çý³=’îpìJ¾Æ‰à4&Þçbô9u‹Ã¹â½üîÒú†ŸƒñçAõ ê ÷7ì@.%ÛЦ¶û*DSÏÉYö¿CJÏØ¼ñ‰nd¶²SÒã—~,zöĨÛe$Ü«mGŸëû¿]Rµ=ñýZþ¾ø¹nþ{ÜJÔYPO‘PkÞbJ2\·Mg*/³ÓFÜ¡“O=Ž7éAJÃ6øáiâ¼ÃZš'óñ‡ß#’öñÅõ§pPЩ u„uXÙ¯ñÕcm4m,›ónùò0y16{¡uÉÌÎmxc&ã¿7àç<Ç›¯ ¸Nù=¯ó5Ôñ}Xä\›ù4¨ò‘“›Ñ´.fÍÆÒ³îЗžÛ:&Öð¦O5Σ?û3~_£mò]Y_NöWÕˆ=ß^¯_‹ûuz~OKßÄ{k¨#üûü¨ṳ́E[ÇÐ…Äm“JùÜ¡ž»]ê•äEÂ2RÅø}§–ý‚_¶Ü¬ ÇÒ%!‰îâýËÂR>½ðû–~âþ–pŸH‡:CÛô.WnÄŸÔóP%ïÍb¨Ÿa»¦Ïºòaã–÷<©jüúð¿ú3a®ýbˆ_×ô÷º—vÕ<þØáò§SW÷öã î‡Ò¡>¦wËu÷Ñ–æÈ…{=)âŽa%†ú¶‹Üíâp‡VÏk5c§ š¿uÏÄ)3ÅÏ­„tNÀÏ%xŸñóù§7e_l]0Çu:Û5ñK¨do.L(js ]*Qѻњ6rmõ;ÍÜÅÜó™|=ÅûŽÿÍûŒ§ü|%ÿ>”Üð~,Ú=œüfûdÙÎïฃtûÆ5‡×UPÇwJæ˜ãJzºê®jXYâ>ÇÝSü¾ˆÐÖR^<߇æç½|\2êuê4xàæ3)€y>h_9ýÈAªö`@•Ë%ï_ÛÔÀOí]©Üû5ÓnÔRñï‡U îú°h Géu*]™à½ô‰t>É×;ÂïsÄßÝ ÎzýÈÎã–,d÷Žžú­DÉC4e¯é“EïoSPáÄ#ž £MEmstkTâ~WÆï‹ðçòßðû¾î Î»××ݲ\Êœ#l.¶éyˆîh\ºÆ>ºMs´‹ê,ê8ŒÊö²?žkUßâû|Š?켯,Wßo+ÎOÅ{¨“î1¤øúí+Xt7CÐõ!:°ËÞ¶üÛ”·µê›éîÃÈG¶êbõeÓ˜°¿]…ù7­jõÇövâ8é(­Cøó”ß+οÑ¡Nˆ1!š çO‡(c¬¯u½äÛd¸%X1È…„ßsMçÕÅXÆ"Cê:w§€ß¿ 0é"Ž£¥sêÖ²’&—s”~—cÔϲy™í¢U%׳Sïýr£Îê?z¼ö;Žòõ_¯8ÇA‡CîOÔ}{¶$8ãþ—CÔçH̶A·iä¶í/&Ïõ eÉjÍ…µ~|ßSÜÇoFüž9_/ðçjí&+Ô^o[ ßT¨Sþדgâ§ï`|ŸÍä}­N'Þ¦²¡ ¿®àIGž×oX-s"ÏXy·ø´F^](2±Cט©Ý¥ýBá÷‰éÒs›¯¿ŒúAJ›³J­]Ášõ(«{Õô0MÕ¹‡ò6¹Î;Ǽ½'Í÷¢ðã­ø¾7ãûBÂ}Œnâ8zÑ¿Oá}Ttƒ×ïܺHífîf)IËÿ,åy˜z:¾·i÷Û”³½ÄîG¯=èä€vSº¬ŸÄ„}Î?˜p¯»–´_'ôs’xÿ7ÙŸïˆã† ÔùíÐhóbq{Ø®yŸ6:ï?_ãÏ›üû*Ô™o³²£[å(¶ÿÕÕQ{biÞŽ¥mÊ>È&·»êâ›]HΞÝ:a7[<¯èÌøëÏ•¶Ò:ߣÎã…ya8^ÿÝü&ökF±² Síf?ˆ¥=ûÛÛÔ½’MÇÜTR’öרýaeg±÷Ïâþ\mÛñóS~ß‹ï¯J÷æ?ÏÙ¯eýB5ê”ü˜ÃJİÕ{ú.ï}”¾˜¥lœ˜MsɃφº‹¿¿Å<×xÏ·ã÷cÿtºîñ }Kéóâ÷aø=°‰.«#nŠçž¨5¶~Ùl׃¬fÔ {ùÛ£äì•çXå`6ù¯Ò?±ééI1ÏâžÊ’ûD¯Ÿ›ÚYÁï7çÏd¨ú:ýáž=‡Y“(+´ü1º±óÕ°! ²Io}bМ·^tëô±çO5~âï˜ì¿W%Ì[Jë7>NóùBþ>£N§ög·ö;}„…½ŸÚwi©ã´Ö§Óëà ÙÔlµi3Ñ‹0i õnðyÿÜ6™~z9ýô6ùwø9™ˆïOk"ä^sÿ:§¯ü뾕{?÷P)æ^ÿSîá·r¯5ÀMöƒ¹‡—{ý½ÜCÅú¨í×ù=/§ÑËÉGôrúž_§‰èשùʯók/§üÙ¯ù½œþ;ü:ÿÕìWí?d~ý]Ž„Á¯Ø—~úØýûþ½cŸ©øïÏ22_¹¿“Á»3¿ÇÓ·2_óg}ùˆ™¯ÿ”õõ­ÌW-°CS‡ÿ`Ö×ße¾~/ëËù½Š¿ö±û^îµ!ïÐLô±Ëú;SÑÇNû•Ý×¹×ùóóç^ÿwøØý«y‡YßÉÏù–W»DïG={Ž}ÿoŒ}ÿ›Æ=Cß„S1çõ[íßÊy Í—?&6ê?eƒ}+çÕÐÌ>@ûƒù`_ç¼~/Löƒ¾Å_ûÛ…SÆd¹ õÄãt@…Óÿ‚¿„L!6_ä]„aÞáeùƒ· " y†¬WˆU , ØP JW,!Þ0QÀ>@ ìòe~+ã:ëÙ†ïΈŸó½ÿçÆ¼ÿmó=sñß§3²]À8£ÕÀ úl×ü™a¾b¶ë?e†}+Û5 ÈÑÔ?˜öwÙ®ßË Sþ o±b‰fŒß?ä[2 ùÖ~µ„|k9Ì *? NW¤è[ì'úË!´`ö7ùÖù³óç[ûp‚#9©¹@aÆÄò€3Dªj(Ð[ýkY‰~¢gû·²y¾åÙnÑ«~Ž}?Ç>“ïØ'ÀLÌ€ý–gû·r`ÃÄ&5dU„‹y°ÿ”;ö­@ ì âp` !û‚, Ï—•ø­LkÝ7² úÈŸcßϱÏäß;öYˆõsM„¬Øp±•@,ÑaßÉŠÍŸIæ'fÅþS&Ù·²buÀ Mùƒ™d—û½L2C^…ØAáÀÂðY@D3ˆÄè€Ä Ì!Õ?dc2 ÙØªZB>¶ Ì!*È ˆ+˜C`* NZ$0ÿ›ŒìüyŒù3²U ( ÆX ƒ ƒ@p†0ÕÀâ z „H5ÀB Åú¯d2r?÷oeõÄ~ÃÓÝð˃ zÓð‡}?šUñŸë¾—û¿eŒû§ñíÿ×±ÍðEs4˜Öp†&ÓÆ6ŒgšoäÇòܱ1?ö{™c—k†õû/d}û½¬1C&E˜Øè>@ ìÐðáÀMï ²€cW0ƒü€8A ‘À‚P\ ¡>A A ( ”HÃúbÑ'&˜C4* œ 3ˆÇè€D Ì!$È *˜CT* W$0‡ÀT ( ´X ƒØ‚@pƨ^(Ð%¨–a˜(D vd80…(}ACœÀ õ:à”/Kñ[×¹ßÈRT@̱?çm?çm&ÿÞ±ÍN|ý<!K6˜¢}€Ø¡!ÿ“%›?gL%fÉþSÎØ·²dsMûƒ9c—%û½œ1_äE0ƒ0ü€8A ‘À"Q\ €Xb ‚ úNn6ÏWÌŸ­€ b ¢ yÀâRrB‹2‹‚Ú_ç+æÏÑyÀbT 2èÂÔKˆ3L¨Ð;5˜þ‹ùŠA ï;9°€8BÞp±ÌBQ\ €`b ¢ y@ñDsHrBŠ2ˆ)ägˆJ dVÈ3¬E!0ò€3„¦[(Ð%Æ1 °„øÂDú-°ƒÃ)Äè ²€¢Œf¦Ð'4˜C¤* bfâ·ò°ÿ.+ÑBVÿœ¿ýœ¿™ü{çoNâ¯7rb#Ñd92â;9±* ìШAbN¬3V ,д¡@”h^ °üNNlpFS«;è ®–hò0±Ñ}€Ø}#'6˜B¾ È!„`1øp‚("9„¡¹@ÄDò€3Ä¢ÿÍsóçc;CPj`Q…=PB\`…‚<à ¡©ÿ&ûëÅüùØ¡@”£XBa¢(}€ØAœáÀõY@¡FˆùØÿJŽb(Ðg[ , îP Jˆ\, Y‰@”¼SÃHèCßüûn?:îýï~Žw?2Þ>;µ‰›ìÐxáÀcœïw²bД‘Å…œX3äg4¨X ICþ;Y±±@†æ yÀM¬häP JŒo`‰±-L| ÿ]V¬Ð;4}80Eãû‚, ‡"€DàtÀ bˆæ„ ä„ dGÈΉX@(¡@”2Ô–M˜(g 2(ägI , ¦P ÎU,AXA 8C`j`‘…=PBl`Á…=p†ðÔÀâ z „5ÀB Åè´À¢ ¦¦/Èr4˜A¤~@œ ÖH`Áª@.P@¸±@ñ<à «ÿ! ;8CÜj`á äÂêB×üœßýïLþ½ã³ø¿ÐÄs4¢Ð'4d$0GSª@.P 9c ò€ ô†ß¡a5ÀM&6®Ð;4p˜ØÄ>@ ,ÑÌa@”hj °Dc‡‰Íí´ÀMLÑè¾ ÈÑðáÀMï ²€ÍÌ ? NB$0‡T ( ŠX ƒ0‚@p†@ÔÀ" z „X4Àòr²}AVÍ‚9ÙJ –U˜(, –XÐ%„¦ù›œl-°ƒðÂéW9Ùa¢}€ØAáÀ¢ôY@qF3Ô耄)æd«@.P@´±@á<à «D ô@ 1k€%&ŠZ 4Àâî´ÀBÅî´Àà¸NB?Š­÷s~gò_ïþ3cÝÿïãœá½k ßšMäh¸`†±Íè€0˜£ U ( s:ÃïcÑhÈP J4¦X¢9ÃÄU °D£†=P¢aÕÀM ô@‰æÕK4p˜øðöZ`‡1-˜¢±}€Ø¡ÁÃ)šÜd9š=˜¡áý€8¡ñ#9š_r"ˆ2!ägB , ŠP JˆC,!0Q$>@ ì –p „`4À¢ zÃøñh€%”‘X@H¡@””XBTa¢°|€XB`a¢È”@,!¶0Qp>@ ì ¼p` ñù‚, ‡#€„ètÀ ‚Œæ¥ äÄ dhÈΪX@¬¡@”­X@¸¡@oÛ ` °„ˆÃ€(!f °„ ÃDQû-ýuw8TÜÇãî .&ª9rÁÇç(ã>IƘˆ¥Ç‰çn? êÙ½[¶”£ùáI§i{'ºK>Æà3÷[ÿ¹^…Z:9÷Ó|6PgÿqƒáÌ1ÖÔÞÜvœ&ÜÛbv¤U6»Ð¹Oj¼’® v­sðÞ v¯~†Ãï!]$YîƒÏý/¸m~y5^ÿýÚ{Ñ)­Ž³ŽWÏnŠu‚ž‹o·¯A6±V£/¯Ô¹PãÙ+²œÉ¿ÐžŒûÊòœî«Áý‘¸O…àç"ú9¡N|Ûš¶/޳ù=/ ?AÝü6øDWͦAV¥ìZ£k—W^£òg‚Ÿp&ägÚJ¾¬‚ÏtŽÏmâyÅÜOÆøÅ¬Ì‘w7ØR­<Ážø«¼|‚Rž¨:u(“M·6_¬t(|(ýqZ?âú:1'®s¯P4dì³®¢/^Wâ¹`Œ2Ôif ?É&M¶²ëêr’ÖQ¡±×?é芦êÚU‡Ñø+^ .9“ þ}Ù®ð-¶îE7Ç-\r÷E/ÉnÂ÷ÕŽc —zýJ𓣎Éçý·<>ÉÖ4~JV¿Ÿ"íGÙì×Ou´úJsv7Ô…N¿Kêú`*3¸´”{5q¿!O³.q¿ î[Èý¤ ø ¢Næƒãè)vúÍòæI§èÊâ  ÜÒÑÔK×7N©¤Fs3œO_õc³z|nâÌxq_îÄ}ЏïdþÜPêÔ,2uò»LÆf>tÞþª;£ë¯S+JÖѱc¦ú«YnÔìúQËÔEÓDªþŒçÈp¿jÁä¼äƒÄýn_¦j‚nPÇàz­‡šµ´mÓgf#ßwŸO¸×Q¡¸êö³ÆzЂµ¹/{wšÁnÝ8_nƒ×ÆóyàSö—’!ÀºicÑØ\Ðê\™ZmÝï»ÔÌîÍã£Ý{¨É¶[y“&;utãѶSŸxÒúÊõO]í=ƒ%uœQßû¤3ãù8ÜÇ”û¿ yÛ—¸ß½à‡SYÐêß̲Q…86i¿EÝ¢{ÔÔüJÓÚ{–ê¨ø¹çï*¿õ¢©¡kÍšÎîú,ÙûÑÊ™°ºoa÷v¢Ï¯ƒä»Æý½¸ß}þ¼U“U9rÓVϾܙ'æÀÇÑÖ,u™ÓuTåà£VZzS Óû¹Ï§Og†O©rXöØÒ,ç ù.ññ†û‡ ¾Dswe¨óhç—½ïžÇ±¹f¡Í6ÄÑ´U™+½tÔoÂÑ8Å|/ÆÕì]ϤiÍWôeÜÇ…ç´p} }æH‚O¦èÿ¼Šÿ§™ö`ÂyŸ»q4fï´ ™”îµÑçOúÐÚ79(u³lòpùu·¾ŒëòatXÖŒ¡ÝDß°¿rÁ„¼g‡93JÔ)Üm¿ûóu¨ó¤îâ”Ö§©ÿ¤Ž·–Ûë¨í¸•_µr§¥“Ju<æÏt޽^ûG?¶ó’ÁÉ‘tÇÜ;ùM—÷çyëÜÏ0¿o¥ uÖ[o9ØRšm6+4_±ý4•,ÿè5ÖÑÅ=·ž/Û¦¤ˆÖw½c ùK93Üω[;wìòFµ¶.?¦FâÞµn\Šgº“[ÏÎ ‹§ˆw—È»ŒŽ¢†Öý5$Æ•—=¤¼Aÿ5$]r¿%î+•?ÿT†:Fðû ¬÷¡xô&eBxáJnÑÖ·—Û:›yRn–cÝý3Ø#ÛˆO™==¥ÜxáyRó?ä†sߪüß‹uªn)å?¯å6zÉ„u¾èåñM%‹Þ"×í ©L½)òÀ¤]eßOåy¸ŒçØpÿ`îcÏ}íùü†ç:uƒ:)µ;]j2ñ s¾°aÒî =6}¾¬ËÀ[x>6Ë·y8]5ØèÕðcnU œnbˆqÿK®Káû"åùæÏQ¡N×’ÛöŸa˦üê±ð É»}|Sµþ-Z¦,Ö×ÂÛGô¿ôc¶h£Ÿºr?4âyááugM_\þµ”·,äFt î‹gÔ ê”)6ªDí7gX¹8»Ø‰Igèð¦àçnonÒ«cw´úÇl‡’OýØóãVý~Û•õ2Lo*)h¬»SÏzc:Š~t/Å€<‡f·ÞGÞŒè(ù‹uƒ:ë+ö.?Óá,3¤.·9Kçäï*5O¼Iç§ÜÊÙÍ›:­ùÅêœfª˜£§d‚ß_WóÁ$_{áùùTÌ×lO<ΨÔü·Î2i„oÑ]g©å°b9íÖÝ$–bí^ú¾'…þöÎ3*ª¬Ùû`1cBPGÚŒ©6‡QDEAQl@’ 6¡P@(£ŽÓˆ3˜݈‰¨ H 0æ8â˜nuŸSçi¼Š>ïûá¹÷®™µþk¾¸NÑÝ»jWÕÞ§~^v ¾ºY<^^ël¨#®Ä im ˆóóýî sK¹yæÃªs "+ÄWn.njþóiò{ý ͯÃÛ“c<£}nCÀ¨fZ=¶:B#³¾Š.¬·Öý vl+åàÅ!À󔀿{/P}>®Ÿÿñõ®ðékм¨¤¶ïuˆÍ}“`u4g·ür’¬Ê}ºå/bÜïí äƒÜç¶æÉróLÿ´ ¹ÃÕ¸Ñh§rßÛ_O ¹Î:LO¬ãYr”SyÛ÷¿ ÝÊvæÝ™ ìIýæEA¬³Ó™w‹såú[wþk^iÔ{o—óàÚ~)ÁçssB¯3}ç+u£&%îéÍ£è܆À·¶ìß9Œ]ƒRûMd§¶ŸÜ°‹#¾9·Œø†ô{P=@qUå/hçÌ¥ðʼÜë,~âšZ#.%ùU¢îAJ ³¹èo5š%wYc®Àš­:åþ/þ5ù9íûäÿÄ«UçÍE¡•·IfJ:×±>)Pè6îÃö²žn°.ÁÇ`¸ŒÛ®Œæ“Ò~Bóâhž0—ôåyµÓ¦Œ/†:?5J]ë"ž­þeêBôèò½ÓÜÍ;V®º¼åÿâÒSàÒGpTÏTàž¨üe[…xá-ºi¯S˜VÝýn÷íoÀ¨‘oëuéV v'Ö„ÝÈs€Úfy´íJS‚_æ2šSÍq`FÍ­§8@œh©Í êMç8Ä"´3@U§2wÍÂi§nÜ€£ ­m¶Ö)“•óά¼ é:ã?LºÀó¦æ1ŽS1˜WbkIsQ¯š÷t¿:hÐ÷§ò´sƪ¿é£T|µMï‰7á`­ ‰«ïÁeçVkÏ”À¯;–yòÜÏÿ<qxh>?ͧ> q38. ß@;*̽]*3šx¼ûº‚›Pðg=ÙoIEàÚU÷Æ€t p<?|wÞÍ^õ¥ŒêYÚ…~ßçàêÁ¯T~ƒvÂõ ª\šÊf•¾´5ꯀë§_í*‚«U9—\º8‚N+/³”i~,ÍðéO™3¼ž.ñ…yò<7ƒûýMªõ¢Ð7':•9|Üu90@’ûŠŽEEàsíÁÖ çÀ§z?i[ø2eV§îňûKœ šëKsq)~çµ2J* å÷´c­ZPi¬êãv¬üÐ1¸iô˜‰E,]Ñ«Ýd˜ó Ö–ëÛ¼Øö߆>í8Þ‹Oꪛhî*ÍCUç蔢=–Æ–ÌV «• ­¼÷ÿ^·C¸O02Ùï 'ŵ_þÈ‹Ï3¤ŒêÚhÎ3—×ü~><ç?Û+Ä\šÆÂŸ]9Úzx&8êÉ}Æ¿(¬"´]‹Ž™^^¾<'Ú“×”Û' „¾õkhÎ/ÏÝâüíèÖëEÈÉ4Öî´~Ó Léím³=©š«€ ôç¸äD¥œçvzòÜ^ƒjþ.Æç¬¯ì0¦±n­~=ÃNeBÖ•Á‰6‚ÝÉIF9Ãä—ÛÕ>éÇb-7=Ó —2šL¼êûPŸ„ó{¾ÞäúK´Ã͉NcW¿hò<Ưv:Ö£޾Ÿ®ù‡ÔǪog¦œ…înÒ³ø¥»º¶Ÿ_ïÄA!?¤ý8„ŸKL|• ±jðx‹¨Qd÷øóCŽ.„ò°ÏËûÎr„ׯ’u¯Ýe¬çûãZŸþðfAëÏïh4HàÿQ=Fû=qÖ9~£ÐŽù>]QftÑ/fÍÝ•YЦûþØz As\eöM±,œðkÆ_v¶À6<`š·PÇRÝGó¼é{£ùÛê\ÆD´s<7<íEƒtVõÙ+iYf<\0eÂŒZ…°®þ°ËG$ Â`éú²•eÇuöb[j¹uÖÁXà¾ÓþHñŸx êó¯KÑÎj½²'CµÒ™ã+/hÝlHÍè*ïT^E.7ÄÎií‚[ |Ødmó‹æIõyÞ¹lo›=\àÜS\‰Šžo;eúˆjÜ<bwÕ˜÷t¦5cI‡#vÙལKóÍI°t鎡¿FÌŽáÅ"«öF½•ò\^K¾~'ô™èœ@KÚÎù>ßo|ɦY†é,-ajÞîmøü܇Ží)€ØcÿÑÕD3F=²)Û“±`%ðÒ[àÿqû¾¡Ày¡8Iõ$ç—?s~ƒv:l|ëe<:…-{ö±qa6„Ù?z뾤d3+÷_hçÁmLõ`‘%V.Ÿá#pD(NRCåø<Ën½Ç¥3MUÙ nOö±×²/€€ýrÅÀ1ŽPy¡_랬¥…2C÷aÄ]¦¾­WÚ߉WWmÿ@;ŠámG Mg*ü•ý-pµ|<Ȥv?^ru…4OÌ(¿š$e.ùµða{´ØXÅïì´ÛZC?>áû"Ï-¸Ï%úç*ÿ@;[—5 ¨3(5êPñqñŽ[ †a¡µµ @äúÆ]zv·ôKHYàÅ~r}þvu7×Gñ/<&ÐÌx+Ô÷/d,PÿZåhÇT™UŠÒ™ï)°ò,ºsôVîÚw?b^?Ьï ×Ë‹­ú©»ƒxoöwã'?wnSœ; |—þöòi§—Ä¥hæl´rß(aÿWùÚ96Ñ´ëê÷iÌç\¯®õsàóKÁ[ãóã͸€Ÿ d e‘“zýqº7#>q*‰'KÜ*îûOóCb‡š+îcœLHS6Às@ãèšvFäÃÝëOÌ]àB„cüþ)ëÛªí+³r)¿˜ÅË/ùÅTϨ÷ûEhG9…¿ýž4–ævïÕªƒ9 ët>×Ô!jÍ,+6î –òçY? \2®›cÁÅÿ\‹²ÆîÖ¥szVû<´ 2œÆî¼o‹;b.Ì=í"Kh‘©›Œ\báƒJNÿpYÎô/”YxÏóö]®ž7ªWèsQ¨ÎKA;Ëí7ß58…½¬Û»³$Â6µVëUöÊßýæ—Ù|Ü•3·- 0bú œ/ªÇ¹~r®ÕIÄ«¢~°ÊÐŽù²ÙqƒÒØÑ¦ïìÏ…3³l–.º•ÊÝâç{ðuØ×úÌ{£ºŸêxZOt®Äõ2-¨ï¨~~‘ˆvdÞ+Ê× s}>”>Ì…ƒÛÌ<“¹¾‘«*ûK`žÓãe[J}Ù¥”Òz}Ç;h'ÄCîœñºpŽÅsþÊÐNïa±­Œ{§±@UÁ˜Ã]ïf ÛÇsŽAcÍE}¦ø°Çî+Y×y¾ŒòDú{©>æ¸1ùÔ‡çúc\®ñ{…8{\‡u¿´KccXÐÏ&®y0éìQ[óeTsõi/ ¾qIÔçê‹ðù6':i§1ÓÚuäñ¼“<Ä®X]œí ÝÍÝä%v^¬VÁ£ýg2+O|5:ÿ¥x@û:GLŒvº½kƧ18:hîò?ó`­†§N_<ØW”gþѺ×?ý+x±ÂAomÖ¾—óçÍ}øú§›À'¢¾’ò¯>ñˆ«‹$ø|%†µQ›ðúA§>ùà¼)ìÀžŠ\žì GdåÞ µ¼Ø¹±jõ\)gĵá¾þÂyõǸ¾øðj<Þ´óÌ4¦]‡Úiløa{g|˜ÛÜÁ­M|.ôsr^{ä± \ýtrÑË)³l²Ë·ã%_þÜgT\>UGOs¤À¢zRyÊÕÎ|xuž“òó¨”Ê”§‡;öæƒhèæÃÓ#rAçØúàØcÎ0µ8îê‡b)+z<7Á—öc1m™‡õм_†ï æ«tÏãÊ8¾Î=¡ŽPç¾–¢;žc›ä礲÷â—7v7*€))û7O6Ï…'ç^³é'/1»ïÅ,‡ô.ñå¹1=„z’òª(n«ó(4vUˆ'íº×d{r*ËL½xÔªîn-³ª¯— ñ t[LªåSU€*oV_8”1êGÓúUß'Eø½ÏÄç€ãr;‹õëí9þÕ'/ã™0¸¿œÏ¿Gñ³èœ‚x¢I}ß¼:Ðq$_Ÿrç!h'Ö}¿vr—Tö÷Ói›+{ÂÌY‡’þÞ–Ÿ²·ôï6›ç¹z±öÖåf×Zù±˜û›yäãÆ0¾~!pD¹þáðjç¯QhÇ5§bnÏ¿RÇË,„&çŸé¶_”æÖÞg©ÎNx­ãÅ&¼Z3Å·³›uð·Í©E“àÞŠ;þxl%ôè<‰î›TãРúÒ…­Ÿ]Oa›$^&¯Ž‚ÛCÙêDÛèPàáÐí'G8¡ÄYKy¾‰œM(b'³ö ¼X:ý’¬¾•¢å[JoÕ[ŸÂæ9+¿ÙBxqÿõ‘gýsàQ·ÉVâ'°Ìr2~®'âÛÚsœxTÂy!íû´/Ó÷¥ÞßÕˆª2kod•ÂÞëKŽ(‚w?I¯ì«ŸÑ>#»w°u†F-4Öƒ]l^n•œQŸÎ{¹¿[!pµˆÍp÷DhgÀiÍü­öæÜHÉÑEð9gk¿ó%· Ð`÷á*™ Ïaödÿ>ó&{±\¸ïA¼1nd ÷W8»†|~ÁÝãŽs”Ì&>ÐÚp âÍì¶wˆ¹^IÝuÍÚºÂãðã Ád+<²våÅó2¡Bý\ZTwPPýD÷n(.Óý4õsñ(|¾‰²­Þ:™Ý˜oSê\ k³;,±2¸šÝßWmÒu†ÞJ¼”©Êárþ~Yw!¯ ý˜â1q}ˆ[¦ò´ÓbÇßñ¯³È¹.½ëî+†Øv)Ÿ³áW±Ã›ÇöN 9aYã‰'|ÙZ+£}«ÊeÄû®ÑMàÑ9Py`!Ö1ÏY:£:‰öKõ~k"ÚÉÏn²xBîv¹qÕÃa·¡©"ÈôTIÜùsÙŠÓN .ꢳ¥ ɲڻnŽœQšîyPýB}L®ß׋_ü}2´“rütÄ­EWXc4nµà6,tÙÐøÌY0dŸOÁðiŽàT_Y)ù±‹ ï+oÉ„{¥Ô×åêÍ¡~¡<ŠË/¹¸¬±§Bœg8ün¸è Û\ŸÝº¹ý6¨Ú%³`ÿc‹f9û%pyô•cöø1.±ecV´³»)¸±Ô£8Ãùéjçb"´Ó̬²y“ —ÙÀÚ3×õN» :^µ, Œú\²œ; ”§ÙÝý…¼œî]pù½)pûËc¡¿OçóÜïÅ×1hgÑù.¦›Ëlì²Q1^oç7ÎW$Ó³ P¶`¤ãÒð0#ú]wþìÅö¤qu[É—LŽ>^ȉßGñYå/ø|å®§S•Ä~>úÙ'²ï°Q^<gAã'“ \ôlÁ0´_ÇIn ƒcë8óåôûPÿ•òY®ÏnZý^ Úñ[Ù²vçmI¬¤›²ñyš>ê¿û©AœÙX¾uÅ–i ¤hê•ûóü9?F~AyK•—ñèiÁ¦œàó^Y¯,n4(‰m–³ö„?<¶äXó,hûw§Fµ§ÁoMo û×Oª²àïCóç\ý•ˆv~©Ûj*˺ÄNÙ7º?âüx:¿`Þê™Ý8þâé Ó`¯¯²òe/áÝy¿[þlï}%ÓÚæZë,ŸeÍsTëÂSWוÇÿ~ia×ð”â†û4þûáú®¥hG’¾ùè*é%¦gÚpQa~Me#)–ëdëbg8T…Ez3º?4/Þ¨E£ŠqÐàó¨ù©#A‘êXÏìó{>ßÓã¢VKô~Cv?³2æüco…x·ò]«KláDZOë”B\—Œ…™Ð"Ñ`‡IËé5÷õÜofÑk«S€ŸÀµ£üä•ûìU ýeAŸ'|ÄnçU-¡õŽ¿¬“”È•…m»R0è=sšÃ©LèV±ð76×·hÐLª%ciñ-m6Ww€Ðç¡u;§í¬qµY相¡*ÿ@;ʪ¬ÙüD¶ö¶T?~@)\“µ9“¯ãìA…ÙôôgÇ·U„ú1â?s}«–üýø,!ߣ|Ûšq~‚vìTE™}ÏÝç;[¢*ÃEòLè¿Ëqõ†X ”.Ñ\Ö´ËBÖYâà'g´þéù*?ÀçLì˜Ä´Ú0¶q¯Æ$C§RH{‰ Û”L8;og‹ÜŽÐü}üê³ÐßKù¼z…Ï{Ò°rùàÖÙ#3ë)ëe¥à1zé¹õ}2ᚎò¤Û öN/°¸×ÛOˆCÔçÖaWáþ,£Ó}õü0íµ¶šô¦çvqYƒ&}ÃJaÛÙ¤­õ2wÒÛçŽ:A´òzí9Ë™Y[?TƲKdïí2ÎQè(å¡Õ¹ß\}UŠv¸þU‹l©¿kÝÎR˜¼äi×)¥ 8ßæá¥§Ë`âÉœûCü™÷Ê sÔòãóDS <úªõ¾¯BÌå©çÙ²ü­Ã*Δ‚Kñ-ñ«x Ⱦ`ù0ÍDªÈÇq¬ UçÒýn0õ³(âúfÕøÄ"´c¡hÐhzÉ9æè²ëºÁÍRHÝdî¾I“wì]³æˆW^#mŸ£û y·¿ôî}Ò¹g©×kb´csÚô†GR<ÛŸx?öA)$Î+»<ÐGûö^[è.Ñ“—â9KØíì&Œ•3®ïß]ˆktž%ì£üù zž&A;óí6ˆ6ı¦ÙãÙߥ0ÿf¯úVSðÂÑ!lÇE{ØïneÜT²„©ŽÖË„ûù”oÐ=sª{ª£à󻚼YroçìÔЮòÊàÈâyKkPÀÓs›tîÍÏ©ã6LaŸ-È- ’1ºWLû>—ÿݾ/®è_­ß…vfŸ|!Y]r–ÍÐN}oÒ¦ gÏ<œ¥£ucGÝH²‡¶;_MiW/˜¿_.c¹‡^é4èžõéˆ'O眔ÿ¨üí¬¼]^6¤ÞY¾/^Z­\õxzÞßËX±{¡ê$+bCDz»V¬¡Ý)ãÎCGÝϦþÕ…Ü}ÕòŽR´“½ÒÍ·ÞvµÂ2æ³Y Ü"¿Zqè&Œ¬çùô®3Õ1œv ë72Þ8¸LÆJžÞ·0m`%ô(¿!?¤:J½ÐØ_!^šÜqBBíÓ Ô®µslTÌ·q¸ ýê }d†#ô´<£ã²;ˆ-Šj¥{òœŒ‘ËUq!/ sSâÕ«ß/¡#~q?·Ñ9Å\ÞÞsØ=­ Ž—å»Ïн ±g}Žôëïsß-9b¾$DèÓÑ=c:&;êç¤b|î`e849Éž¿íýÆ© –ŽnÐÖ:ó”÷õó;nêªöZÀR¶xåñ®dÆåKæ°yýýÎ/Š~oõx+Á窖ûÔXöAcß@/ï2PvÑæ¬½ñ’s3Lf̆¦YSp„²=gödxyú0º7Jõ qÕ)îÒ=VºÇ¦ò´S%Mñ:èü¯ÌlR/’Ý·N³¾ïŽE|ˆÙhªílËR¶Z‰9nåËsÜÂVmŸ±9r#á}º‡¯ÞÂçç'>5Cr‚õ7ÿë”íª2pfÒPÔáÈÖЈ°–%¦û…°;“œ†¥w‘±q7‡œ^;¸ýÈHÈŸTëŸ÷bGoÑéöÇY§^Ê–e080ìßo2 düµˆ_b¦À‹Üw¤1É=ç ½žË„ó\:7¦ï›ÞW£û×êçù¥hǭžo޲™µíºØî@;W`þŸ™`[5¢2ÖÏ Ü\q)ˆ5éþÞFΨ¡s êëR½Gužúy˜Æ ññí“q«?Âæ&ŠþÌ>\ºFþ–î§2`îÓþoùŒ,"'åÔYÂ\T w9£:’ö ê»Ùµöñ ä®p¯ƒ[¿\=)B;7WºÖï_ë0ë[¦°3O@îZ<22æ™w|žÙkœÏ¼(Ž eÊj¥Õ59k‘Õ&ÔRkˆpO›Þã <‰[ŸC˧¸>‚íäõ×[SaÍB=®l(I/ƒ½ÛO½¶ ΀ȘÇ&ÆmÇÁ”ŠI/^ÆN5Ø®Ù'YÎßO ´>©Kû+Ç'ÔM*ÿ@;MT ÿÚÒ|yqÌïzg¾Sø•gÈdÇC뼪ñÝ e²ëŽÛ­åŒúßéw¡~Ý'äîsylÚ)O?ñ@ãÒ>vèBØY§‡e°qègË}#3ÀnÎæÈ¤Û¡iäÊñï´—±Ü£Fûö‘1ª#éœîÑ}$úêœö(´“pJ^´üÁÆ”ÁgÛ°&K:e€ñ×ÝSFMe×Jqs“ÖO„ýã|í³t¿êbâu7³lhÿz@µs„D´Ã¶Õ®ãó:ŠÝ7¤á°we ŒZ>¤ÃŽéš¾–{¬ù>ér6¶éð#Œ|…>½?Bû9å›t/§Zß íìÐûèfâü;ûr敵Ëág ßÍ3ÓAïòЊ„i°KóË­ÀåÌ-Õ¢Wû2:¯¢~ñÀ霜î¨û©ÆÁ ±­~Ó ¿žÛÙ¶' ‚b•Ã"¿6úíö¤Ã/I7SvøÛBÒ‡Vým~ZÎŽ`ZîË(ß¡÷Ä(®SŸŒîs~Æí»"´sÄ>ø`JÄÖýÄ&Ŭæå0ݧHOSš. *æ¼Z:>´»TZw™°p|ùa@çW”×Ѻ£{ÐÕêp´c‡åF— LÕ¦Ó-‡îº<Ë2O‡'.£ åtÚlµ®\7TØwõ—…Öo5´¦lüåîh 8@öè^Hµ~/ÚùØòú›´_YDü¼úïDåÀ\ð[Õ0ÖÆ5“ìYm—ƒîåî+ZÊ\U‰Œœ]øï–ì?¸÷>„ÏCy롾¿ °epõýí4µ?º,f;ԯÃõ=Êá£lÖqã;iðçQ‰sðbkˆ}7Ð:"”ÕkØÍýS¤œ¿¯d ŠÙ•ÍöVLûoñšÎGÕïcD¡î=%ö|…qiÿrðsnÝèx|@N§¶µ&Á°Íu[,n¹œ…?Òµ™ÙNƸ¸bÆ÷}͸é*?ÁçõƒAÆÎg3î¾j9,tšükô¶4²PsÒêñ¨¼Þù÷ –rBß1t¹£¼Ðº–có>÷ú ßå%T?¨¿ŸZŠvÌûOÃÐ7 ]ñÏ8bVÚòYœï”e_á8ð,ŒÈÖžƦ^ŠÞg)öa£6§Ç&é#Ü×¢¼”Ö/Ïñï1óç"ÑbŸ>W§:ß‘Áú¯ O£ùN›KÒ`–êEK0ˆ³êÆŽêéÝë“äËè=gò7ºßAþ"äÇj}>ÚYöêÓ¦¬P0»™¿e¾y9hj¹çÝšŸï>¸Öȵ±©;·_˜p‚î'Òût†ú®tTýž¨íè(_—ù9T÷"—ƒê¾u¤ì 3.-·ºW:ÏLîÆ®ç$?áËè¾)WO :£óˤ‹;G,[<èÞ¤ÊOÐw¯õ7hÑ;À¼7”C‹ëùÖL»Jž5÷3íjšbð•9Å_›iW+,ù .ö—sí¾5¯ØPm¶“Õ²kbæÔÄÆþ'þ C4þó±P‹ÿ åïÅsô~`~ñ—ü‰1üŒöï±v”3Ú‰·CÌDbî|9§]¹S3ñkÌšæÂcª %A'IF¢£DòÎâ†R LÐi¢PZè8R”e¢ÿí9PJîÍ÷¬i”ÒÑÜPÉ(Ct¸HÞéÜP ~Ô—ó=¿6ª&þŽâ fì—3¡¾5ãÓD™mûƒü°š8ÿpcÿçͼûŸC4þó1P›ÿ?ÍøTüà\cuN…¶7ñ{¼ì5^v´/ûkÜDu^¶6.pT)j .ô”n ¼ìšæ¯Â•ˆ2@ÇX‡ªBIÐA’Q†è$‘¼£¸¡(t˜(µŸ_›á.RãïÔ4¿ý{ó=¿ÆßùÚüöšxÙJÇTçÆªÏpÿGÉËNDðóð~„?ö-vÅóïpcÿÉÿÉC4þó1P—ÿ;ó•¿Ï!Óþ9Ç_r,”ìì¸`õت±zBÔX=_›ñ®Î멉½ø5fOMsމcaˆÎÉ;ˆJ2AG‰Bi¡³HQù(1:M4JÇ•£E¡´DøïPù(1:S4Ïí¡™ †èX‘¼s¹¡(t²(”:š¥@™ ÃE¡´Ð館|”ÉW悚 #F¡´ÐÝPŠïð{ò¿`ÈŽA‡Aé¢Ó†Ô0T¬ÆÐ–ü ¬&žEMÙòÀÿÛ±ðz ¡¢QZ¸¥<ßâGæ «ó-tÕ8Œßãg‡©ñ³cÔøÙ_ã0ªó³uq‡ *QV¸ÐãP¢øÙ5ÍC&Î…•Œ2DLjäà ¥@™ “D¡´ÐQ¤¨|”&¥…N#Õÿú¬wÏb$–OM³ÞMÐÁ¢x'sC)P&èlQ(­î_gù|mÖû·Úê Zõ9ï5q|”üìd”!:ïºà—ÕŨú‡öŸ<ðÿvìûß’ðG©òûæyfJÞEªe… 3%Âņz޲ý ïBÉlLüÖDõ¦ÆúùÚLxuÖÏ·¸ßbýÄ tÑBP•(+tˆ8”Hwa‚Î…ÒB‘¢òQbt”h”6:‹?ª5&¥‹Ž‚*EAŠFi‹ðß¡JQcЙbx‡rC)P&èXQ(-t.)*%F'‹Fi££ù£òQbt¸h”6þ þ¨R”/ ¥…(Eå£ÄèˆÑ(mtF)*ÿ;¼ŸÒ/˜´V¨8”6 U‰²BçC‰ÐÃPÏyæ1¹Ý~kV÷¢&.­rý*ÿûwcáÿkü‘Hñï߉}ß‹{ÿÛû~ßËó~$¾Q\ûÿ‰i5Å3åïƒÒÆÅäϳ-Æà¢ŠAéâ AU¢¬pÅ¡D_p-Dj\Æïqµ‰©§ÆÔþ’ÉHJŒ±*¥…"Eå£Äè(Ñ(ío0z¤¨|”(ª~6ñfCб*•±ª.c“ɰÈjbWÔÄ™ý'?û'? ÑøÏçg&¼Jå÷ÉóÈD¸ ÃPÏQ¶¸0Q¸8סªP\¤‰(\¨ëPU( .Øä`ö¸©1{Ö©1{’Q†¸°#ùÅíö³ç[,Æo1{âP"t„0Ôs”-:D"Ê" õ%FçˆFi£ƒø£JQcÐQbPºè,!¨J”:MJ„ކªDY¡Å tEøïP•(+t¦8”:”•£cE£´Ñ¹üQ¥¨1èd1(]t´Ô±w.P.{w+A‘ö”—CEŠCP„!ÿ] qŸ¡ˆ{8.¡hÌ BµB‹3&3¨mcê—2F‡¸îsɸUAUfJ'Z4.ÕT«Î³³÷›ߪö[_­sV¿c­ßêeY^3óüÞì0ëyü@á2€ÒY@è ŸÈ! ø"fd4ÿoì÷ÛœÕ7PAZ=äu¶´áö¶M¿s—̼@Ù@áMÀ÷»³BF…Ä=ø{ïÀ§uÿ=­»ï¼÷žÆ3ÚÊ}÷¤»Nøœ+p¿Y@Ä p. Dè¬ ôŸ¨@+½°GTÒæâïÝÓv—ØÒ~|k±ä–¶¨`‚ìê_ÙҀܔ¹awI{d&à‚ïr„ß ü@ 2€"X@ÄàNS@ ð?( ˆ¥Ä΢È ‹ øiœ@qÌÀtHÌÀt)( “ ø©œ@±ÌÀtÌù„ýl·´GÆöeÕÏ ‚½ ]@ ­ ôÒ-m,špÔþ;6ÉÔ×.Ék^ ÄŽßØ—ýïsÝŸë,¥þýÏu:é× Ÿ/„Ñ T¤ „€Áô5Âi—j FPíRXÀ Ô­] ®xvBl^ ‘¶…@hl!Ü&à„Üñ„-Fðáw907PA„ð5¤°ÐAŽ  € 1ŔŠ‚@iÜ@ql ôÈ”Jü<zÈäre~ ƒX@¹, b ™ (!š@ „s%¤³‚ ˆ|@- b ¢ (!£€R:€ bš€pÔ ‚mÍ€¨!­„€òz€Û%‰õ%¶¶ÍÀÔÚ.‰m^ à ƒä&àdw9„7ÿoìͲçº?Û]øïøs¸ÿ¤;°äý÷{ÿLîiÞuÂçÊ%|­4+=ç*„ÎBÀ€ðy€´0 ˆ FíÿË=mð”ØÓBk^ ylOÛÔ²] ³xæWö´ƒ@» A·0 ðná{Mz?Ð!ø@ð[@Ä@PB+=„p%¤°‚ˆQâÿ%±?à ŠÈ!‹øÒdı€ˆ@@‰, b “ (!”øbeä²€ˆd@ö„-my‰ÝY w$tD´0@Hð=ÆÂ÷ ßS1=@ 9í’ Fàˆê2Èj>ÀAZ§$î“vf…œ ?þlwÙŸå¹îÿÃ3^úy!ás z€a´K4/Ð ˜ C8MÀ 4©ÈTð ë2„Ö|€Cx@Ž›p²Èfð¡v9‚m>À!àN CÈMÀ8„Ý ä¼øÁÏ „ß<@ ì’Fàa—¤ˆ. „VzHâ*ˆb!`€0 †4vÈã*%~Dòd²€ˆT. „XVzæ*HfA ‡ln ‚p6î=ˆ§„xVzè*HhAÀAF'CH3ðÄÌ! ƒ @IÀ 4Õ! k^ ¸ ƒ¼†[ÛàÈì2m>ÀAl'Cn3ð$Ï ˆn ð ƒô&àäw’˜YáÇŸíüµûïÿú,÷´þ>õIÏoÔߣ>~·=­?“>F·ðuC¨l  —¨0»2#ð Âf—g^ AðRøŒÀ 4¡ÈDðtBi^ A8@†€š€pªÈVð Bë2×|€C€@Ž›èæ0 РF¨íR°=Â÷† Üá>CÀ]@‰[Aèv7P!ð6ß Tè•øo ‚6 ƒ@, b … (!†‚¸€’XAè!‹[øû1Æ”Ç ‚Â{Ráû@ pÈ äÉ,™üÂ÷~HB¨!–]’Ë(ü]*PᎲ€ÐIÂh žÈ Ÿ ø @ÍÀt¸Ÿ2„ïù€˜^ ÁÝä2Hj¾pó€øãséßÎbîÍY¥ïÚ´Zê/hMYÎ~q‹úEzW÷/×|Ò‘H¯ð±ȸv)#‰õs„gÔ¼Vžõå/0 <ðSßx¾d‘çc‰Þ®:Ñ?sÍ;ý‹ÈÙ mÕ-ÍЛç›õ)õÉpºqûãjß$'òbÿCÏú™Yëecý¶Ï¸Íå«ýú‘=ÖkÎz‚X¿VÉG‰s´Ûš$n¤%?¦ô^Ô·ˆnÿå@ç³бy³ôߟºÏ”G $ñãÖ÷x)p/Žgýl‡á¹soDYq^Ëú©ÄÝß—‰íO çp8gÁ{»æŽ[¿‰j³¼=‹èn[ap¾€R~h¸è®¥/‰½3I<Ûe}lb/p›Hÿë¹{>ÚK»‘bÿ¼çØî‘{hÖš_ªf‡1EôÍìëÚŒ. G•JËG^íIá9Å–I|ö_ xæ£8žõx±|²~Ö;Ëz\XŸ»pŽç¬èÞ.;ýïÛhΙ»çK.¢c¯}àíV@oüÔÞÙ»'-IªªË>˜ÌÏÏß=³Êý8ž}YO Û{¿Ö²ýyÖƒöç¸R3£OOÍ .WGmŠsºWþ饦”52{p“‘½¨Ë Ý³ûÎNáSO•îáÛϳ~V1¿]huƒ…æ¹½¥eûŒl/óØ?{”3_ýÁ9BkbÒšâ[l ëéã«P{gÕ²­}¨M´åÌ>s ßkzé­''ðe_\Å=·D'õé÷”úƒÚ–ÓÚß7å¶6óöQC’®+±^¢°?8§Ýæ3ó¶nÌ$eãÞŠãÈÁ‡VnêôÐ =«¿Q7-†ôÅ Û/’ùó×uß½?*AêW&Ö;(ö]ÝÑÚn¶Lÿ9ç¶VÌG´ÔÔJôgs1—T½}aáNZ~éX {—"R½¯SŸ=æ¡¡3N?{§ÅZÕõ€ÿÌš$~sã;){ÄK;Giتaõ·æu–zîïJ½1wµ¬Xœ3i‚ýBÿ YTÃ8S×’+"ε1fþGß¡ÉÃêA$¬§6HæO¶èé˜Æ+Þ³ŒjЃ‹ôÒ6âœ_ïxW«¯q«óåù·";ml?(ìÎi+Ì6Ê"­ùÆé…øxR£vmï“à¡é[ûÏÞ°RO“\6æWIá­Ý‹®^>¿®nôàÀ Z©wRÙMf»b_m'i'\ê§Á9ýçÌé<ã‹]¤û¨Lã‹øx¾L˜¸ö|Þ:œ•W~mõòTè´z` ŸûeúЮ/Äólws#7~©¨6‘{›õ÷³±÷Kòç|Ò¡}—iù»iäs?½Þ0ªˆêþ¼jkl55œxcò[™zº~oȸ Ÿ,å›|Ì7,õ0!Ò—š1èÆ*ÛgšH¡ØÜõ‘^S‡ðõoqáË .º6𹩭Š(wzñÇå|ò û7˜ÏžÒvИ¥|»Ø^ñMߙ΋;ö±ÝÒJëê ômÄOæMɯ‹ç|zjZ›Ì¦{¨ïèõó›ÑÇ–â·+í̧ԫg/[1NîívtÞRþY}\õ%—øs §ýܨ+½7ïHèûšÝ¥½÷ëZÖïÌö´Â¾à×ßüíOéåì!q÷­ˆæ·m?çôâ|êz©‚2kñŠ]»wOèâRÞ²w^™o¯ÄGzöôÉ ·&ïè"ÝÏ×´™#º j`¸ùº³~Þ°/[йôo§Ž[|b/•¿3Ø3¼fmYÛ·Ú¸|ÚW¦vý>åciÕð¤yÓë¤ð™ãÇÚöÉ~é§b»¶¬÷˜õ7Š»GQìÙ+qΠ¹ïŒv§í£«:y+=[DÆGçÓ—U¯,Ó‡Ê8kï¶§ð#ÃCtñ<Û|÷“íÔ ÿ†ÛGvŽÃ¾àœ­mnï·Ÿ¶Y:wz³\­›»l÷”—òézG¿élÓ^´øæsÆö6~ñ(aÀ)>²GÃz”XOëe=’%ûî 8'ª­ÐÔ|€Ólº‹¡BÊMvìd•|:ߨ2flAoªuùÂâ4¯œ£:•–ée½úl·ƒõβþðÜÃÅ»¢/8g¿òïmnŒ9He+&ÖûçÍBú¨Y«`ÚÍ<šº}_‹¿½KÂúTªÝÆçõMµx¶£&ö!6Žô۲ݱO®•tKýý8áG„xZû^°¯RhDþÔž<š}luÝ×û ”öîl|^úµëcM <ëeg{wÌ{Ö³¾GܨQÙK}/yƒs†XïÚ|§kç>ë÷ð|!M;°»õˆ÷òèÖ?\}|=øa—aSs/æ1oòåå Ã5‰;éœt¯ÝŒô¸½Ü{ʦ·÷kíeÆ9÷õy;}–›„•EîX!Õß¼bÛ3còhÕ–%–íÉ#èò:¡ØÆ‹}õñ<ë¹g{㬟õݳM±7MêCÛZ̽R-kŠâ™C4¾U“Ü…ÔùÆîé/æ‘ãܵØ2[GÑÒ¡£ïìbãk(2÷žãÙŽ™£]õMê]êÿ±ÃúÐÄ{µºèÎYYH—¸y‡È" ¹ éHÍĨ/®äÒ¸׌•Ž¢µ&nÚÿ·T~ƃù—2¦L‹ì‹{3#ç„=Á¯×¨vµ]¾‡èBqË#­·ãëà):wC.©öh5úëûüókÏMãsžÙ“¾Üij½¶ÇÇžÿXœøz-þ~ øõóý³)tèÊZk IW=wËÖ)¹T>/cVå C©j¿íËÇY—ñmnÝn4ñbÏd´Ô‹Ý-Ò«)¾~}Ù·d»Wa?pÎú>ÂàO¶ôœ_HqႸ\z;ð°òÞøÁt!ïCçdý2þFS¡éÖÄ‹÷RG)/D¬Žõw²ÞFöq…ýÀ9“Zv.ov˜.'æ”k3§PzNÊ¥ ãÒG,x•î½”?vÚÃT>ºé÷evvžÆ³×ßFŽ6ë¼#:²Ÿ öèÞÖ²ööç¼vv[ʼnɇi÷®àþW'#O¯½üú’@Õ)µïJÁÜþôþ¼Ô•mvØøµBíýi/ö÷u#ÖÓßaÖõìI‹îKÏÿפ÷ÝÙÑôãœ!îrS/?LqIé†R‹5omËÉ!ÕÌ®MZ&Ä’ãÕ:Â÷Ž÷Gm‹®lâÅ×§(Öíê†~yŸ¿nk… L¹Gú!K}\ÌÕkÞþæòª9Teêk•Oö*¤ök_ÛûÃÚÒ¼{hLÝ.±´p[WÇý ©| áú•›"û)lï”õ·²{_£umÊ¿Ï=ò:¦Ä9†éWt 9b²ºé ×ÞÊ‘ú7c©Ö’ ;—Jåñ¦f^sºÌ᜔ZÓ|S-È ~—Õ¼ª,½!šC‰Õç6\«@áº@W*_Ú¸¼ùƒ^q<{_Æz@Å=•#‘~UÖ7^rÈ€s*Þ¯ÜiÌÝ;kp'CÝBš\nÑþÄ–9RžÒ­î±nÖOåûÜ;yÆòÊ/{ì7–íW³÷·âû˜—Ùçµàïªzs'ÆæRjwSƒq• ©g¥f–—ÊæÐEÎÔkÕwz:wq|“œ©|üØ«+)¦ñ¬çò»±?Ÿj7±Cäõ’Ý_ìõŸí`„ýÁ9‡?,<^k.u;‘ýÁŒŸšùºu<ü&#§¯.«~øl*¿Z³çòZ•‰ï­vÄúZÙ {]÷GÔô»qNx†í¹<:<ÞR9óªŸú5*8Ñ6í0¹îÿø™½þ(¾8™Ñi<Û½e»:bß±*r¯±°çþ’;§~œUªhÛÈè¤ÛÚ¾7Dw´!>ÿذÎÓ>Úì:7æËnZ œ\|uï÷$ ßý4ù}·ül^?ù=òbý›äˆãŽšh¾¸ýý´Bq{DEUµ³LÁÇ6Z–o³;ó›0zxûqZ¯¥ñìÒ€¦·úløýÞ]>OãÏ~¿7¿oÛ÷Þ]â|$Œ5ʧ҉ƒm ¹hiLeÇ×ël$ÞKA±Ö붉gÜGœ›Vòæ+÷ëïywxý'|ýè´ˆ[:ëQè”T’û›–…çwQÓ«™]·RÍxÆý)ùzC\·Tò>ßDŸ­³Á¼îñÏϣĩàîp>•Öݬ˜½ê—+Tx~=󼌒lϾ3’¶×zñLÔ·Þ{Ï2ŸËñûêùûÇëÀÞX—uÖ·bhF}ßà ×=JÏöN¼÷ãÁ+´%={¯µ£K7Vì­v3‚d êÔ?õÆ3ßÎD¿Á:Äë1Þp?>çôõY³!NGÑÅQÉ/û U w7nÞK™GL{WÌ §:ê’/‚ŠgkŒÂEêC÷9å~Þ|nË}}øºT¬kb¾¹çã¸Û]®Ç¥k;òµÿâ íRw‹‹,ƒ8¡›ßýS(½ï)È ¬aáϦÒÁìÅX!aƒ‰ûñ9*ŸÛò~ÎwNãgÉTïò¨í¶£âú©Ãê(Ø"$|O%û¯X—¡ÐÒÜ6-=Ôe+·fjü‘5C÷äþ°¼Þð>‘÷þ÷"Ëgò²÷«^¼{”Æx.Œ¾Bëïí¬ “}OÖ a+/µèGSÝ3ûØœy¬YµoVئgâû¡¤wZ_‹#¥÷¾b>ßïÍnæõOðèqß¼akX>ºTípe°ÿ*_:ÉQ8y¥ 7,MëM<x ḭ̀ݎ¾«G0î &öe-H×±i™aÇïI¾äׂÅçkKï=Ýý βªE’Ÿ7K“îÙ¿L…î<¯QNµ‡Ê>Ìi‹èEÎñÏrfijÛqǺß+ƒ8RŸËçÄüt>·ã>ïÜ£Äq.Ú°çç‰iTþlÇÊç_¦ýsÅ…]ØM/,+ÛtmÚ‹òj~>ûÁ¦8©~êÙ΂ñF=*õhÏkÚ4òÎSøÜ›ÏÅu¶Ô·!NÁìzhqÒHp3.q™–ÎÒõ‡Q»iz„÷êô¦¤<ñ–á•âXØý‹Í{–Ó3±h"ÝOÞÌë×Äç’¾Ÿ‹ ¯¿g郱õQxE¡²\¦ÚËùï¦ZéW¦4ÏÛ—¼~âJû8&ú–è½÷îsÿ-¾?Àç|À÷yàBœš13)úUÉ“ë¨|—©z­©ßÓ¼]Ôà[çlãÈþ4 _ñäC®8&ú©è÷Ù}j{ãp‰~±›2Uùj9¶Õ4£&C‚KÇ>/]¯ø.¯åóÐösWgá•öä]¬gü9éÑþû !£?p#ÿ& CÖ²KdŸùË£é;Iº¨Î¯Í¨rLÁ]7¢Oùpin]Û;ÿæyÃ߇–«3/iÔÊœ~Lˆ£^uüç°Béä7áI#ý%ºST0 µRpø®Á›Ã©W‰Çý.dz*«›~šypç8Õ©æ°¤wöä«íõ3çñøz?Ï<:@œz¿þv°Q÷tš÷àÛé¿4¿D÷™ÁcÕVºÐ%ÿ˜¦‰4ižg#‰:ï ûë½ó>Þ/ò߃×®w_¿Qâ¼Píz%)Ò& ƒ‘K4à„lCÒÚôU›Ùj}A×G‡Þ~7žõŸän»ð”žñ¹%ïøú’ÿ>Mª“~~%qg„ŸðI§M]?òô"õ\ஶʼnëèpêëWjeûñ¬ë—›õ[¡gN,öŵOš{÷ËøçÍý’Ä9Jãs_ânÏ%ÚdÐЫŸ¾H`éÎÄ6Ý’‡m'+•(\rnu:ñ´Ö†Ž ¬Lå>n¶.ù[þîKÆûH>?½ägççØÇr!ΊÛžt7dP­í?›§\$C¹Ý£Ç\ÙFùÖ3Êe¡4V9闭ا}>9Lú|TÄç'Üšû2]/Ÿ5mé‹àœ~ã›3U ¿Þò·õt;ºÈ•é½.Òûݾª½­Ï6Ú0$­ó²þ¶A§‚ ‹ÜÚW[@³ÇÝ+]±qnîCÁûV®o_9âx¦p?ePìs<ª~‘´?Ø'óÊVr;åý #úKϱxfŠízíZéLÔo ï\Ÿû™q_H^/=ºÁëÿ0Á–'¿ƒ7ˆ¤hÍ-…Ò"·Rü¡åsêÜíOÝC.7œÇŠUœùDÏøÜ“ûOóç.¯»¢O‰ÔwáõûÕž÷kÝœPܵûغ°:øÆ×¶º½,ºÈ-å÷a‹èÚqLÌO½·ïæyÄß>÷áý¸¯ÿ qü4(_%ÔÒcØrzÜŸµ¤ï ~rac‡a”<|áÉÈZqìèÙÈìÎ ôŒ¯»øóŽïWñü}Ù­B$½kíµG#‹gÂÓ=¬r´×ošÏ™¸o2÷Ùà?GŽõþ–LUt1yÁ¨ºP닞_î;O )uÚBQªü«;ÞËB~Y}¾SûC…cÙ×ïBšSñs|®ðgþ¶rÄ\©]órC$L³Î;Oeë„¶êYÇBƒÏNÙzØŒçÀÌÎï(–ıï†Ï“0˜ñ}6±_ñþap¡“¬‘£_U!NzéJSÇÌuPB#Á‘ù<å^Ôy r#i°»w‡aTâƒÛ -eâXÔ´ìiÕ›~îõïáûŠÜ/ŠÏgÅó%ŸPéyƒ8£<ÁA …ñ‘ãµzï‹g•*¤Ðœ"´ÍÒOKçú¿7vx¡8V`ØŠ!q[3ч¨‰×ÏN\OdyýÇÄ8ísœ30lá>€Z[¸SK¶ýUx{Öö*¨×´¤u£F÷#—â¸<_ç86ü»‰»=;TšŸ+¼¿Îðý9Ñ¿©EŽ9–qŠluªåRÕ4†tÜx޾lÕ¨£¥íz*¤z^½ï¦Þ’Ÿ\³õ{x£ÓèaÞu²è{S×Ûwñ9:?à›o6Äô¬ú;Ù«tgÂÅ "ÎÑòÀÍ7{}µŽŽ4,ºRCuž?<5O›^Q}°\ìP¯ÿ‰87mäõá~œóþm@b‡zÒ¹ñ÷q!Î{]6ÕÚè Ñí£m)ÏQ÷Œ„¯êïYKMÚ¥~]#¶' ùªâ“ÌA&Ù°ÀÊšS†0ñ9_Ï»ÿÇçüo¾Žö}ßü¶bv±ŠqRŠƒŠx>'–¿Þ¹Ú;ki¦ý½–k'÷¤=Á`}.{¶;xä™ÕC¤ý€jÞ}¾Þçûú¬Éñú/šUh1gƒƒ÷Éâ‚¶:©Kè²öfkèôõuëÞݦ¡é17ž^ˆœËn5í}±òPÆý¬ø¹¾çû1¼öÝ÷S!NÞ”ªQSv8(hW§®¯œT÷âã×£’IôÑíM›öío»h.ûä!Œû?qŸq½zÔ;ϵ|$ꯟöímç8¨FêSÛÖN:“QÎ/a5™Z¬ã¦­ýÈc•6—y¶áº –öÇ*{ë ÷ëæs‹mza…Ð ‡/q4sŸô¸qÐAïùo˜VóC'­—â—µpåéùIÛ[ݵôX[²ô®ssÙ°ËÊÊiã†x÷­Äuäï~dÜÇÒwþbÆëÿpçf¿É{Ô]°Wv¥ÀKá-ŸM_IÖníF56‡ÒÞî_–½kbkÛ”ºxæ£aLìÇÊHç¥d$®çó÷‹çƒx¾DÜϲ!ÎÕ¹yU÷8(ðþãSÎRBã¿_X©[Ab½ £.Úw:”TšØÞßè6c|} ®#Kz}Çź™Ì}ÉÅy¹4WFñÓ¾v;èþÌ¢÷KŒ=Kç?سTp}^i^F“Ô0:Y\°~eÛÖ=CŸçsÿ<þ~‰Ï{°øþÖ”Ö]’ßÙ¶LÕÚ°*ϦlA¿¡Ÿ·§ÑY*;õéø€—Óî§MîF—Ëò›^ÑÄ—šö¸Ÿósïs†÷ßÜߜϓ¸ÿ’oý—#NÆ£Êsò@÷‡fe=”ç=K{È;f­[ê7#”–öq·Ë»v.Ëœ°Ïß1>Gâ}™X¿Žóçß§÷]ß©gš¥ НqíFãSÏHóž¥T¾þ–;»[jiü±èsîÅs™§|·Äxó»±w]sYQ:%p}–×ïŒï×ztƒ8›žÌùaêò£ì)¿î™}†*]{+îØwô™Ÿüë|3ûSƒ9<9M¬Ü••§>ýIÇÄþ¬=E Û%¥ºÑšÊÃ&­yï™÷ÜOT ý˜õ%ÛRg÷ƒJwvŠþÚÄ)R¿É÷]8¨÷”°ªºÞgH×zÑIÛõo©{±›·WîOïžtéoÛæ²:Z÷b u¬ÛÈûÔÓZn‰ýßãàQ_ÇݽUþîž3â¹,3â(^¦@œƒPCõýj¡®½'¶÷{¾Ð»¯¬]…ã§Éí^}˰l>M»¾¸{÷OÂÉÓ–îœÅî]ÓoxhÈøóÏ[Åßç˜×—÷5ÜÛ£Ÿí™*a×eÞ·|Œ•v/Dœç»]*Ÿ@?½1Ɇ¾sä®<™ÉþM0¤Ó1qÞþ©w‰ïωý?Ÿ¡Öob9â,‹ˆþ|`¢ƒ,‡†-íušýš³òK-i_ɾ·T‰ýÙLs¼§)ªÚçÞ9¶¨ÏÊÞ÷×·¡Ý‹žËß«~Žó9*ÄY¼Jh„ÔÍ´tvß§©ÂžÄ}#ÛÍ$³­ñ£ûÎ(Úù iq‡YLÌ×AŒûÏqŸV^ßxýáýoŽs3ˆÓÈ]ôýï9è¸ûý°OS¤çiñS‰ ¹s%z]‰~©³Xá;W¯kÅÞ1î[¼äÇ^?fñ¹s ˜¿bý¬ê­³ý Îýƒ#·Ь´j¿Bè•S´B7cåͼé|V$Í^(œÅLƒ…Œ gÜ7·V?ãéÀÐ ï¹þùð9·ïy#3âÌøxNÊ {pÇ1zÇ)êQnÑŒ%+¿ Ùw,Ÿüs8u¹½fñÃy³Ø{Ÿ” CÂÿyù9d>_ŸCg‚y½ñ=ŸcÛÎýðTî3aGô]|ØôúÆž:úuÊ7®Sùè_進)³ØÏKRni†„3îc.æwâý÷ßôè¯ë¥;hUË¢ßÏ ;Eb_Ô’b ³ë ÕÒö¨ª5-ÅvgMÆOÉÄùwKýÛxç4|¿‡÷¾þÙ~;2U%ÎÌÝæ°ƒ’ò>›úI­StDy#ûTÏXt«‘)Ñ›ûÓwSN”hžŽ<ÖúýÐbz”w“Ïaù>&_G?žðùžQ 9ç[rÄY™T&­¢ÍAþžƒ§hÑ/ïÈÖNÊš»¡MúrZ±<f0{¨bâÔ´HÆçâ¼ßäû¿|VœW7ʱ¡BœÑ»î!Ãt+ï“#Ož¤bŽ¥qá­G²âÅÔwë®íGÛœœxæ©‘íÓ§„ÆgüÜ-÷éæŸ7ÿVŒ›Jþµ•E½ ŽÇ^w¹ƒÆ–¸ý÷›ßœ¤fí7òKË6åùîEaSz²©í„ƒÒ¼)ŒÕw·>±F;Ng•iÒBê—ÜÞý2±Ïm#­Ÿ$ZÄñؾ£ž5¹üówÄIêU¡Á©†êIlÆ–ñå‹—Õ’ðôøPkdcŸ^^EOBÿù¹ež_|+êHIbJû˜BÞðyÛ6 Ì#ÏÅáÞþ–ûÊóþœNü<¿x®NìÓ]ˆãYžE9hïÞ3+[»OС;§¸·ÍaU®[vaU8yÚß«FéÜyãë~Î??ù¾&÷%ÍáOkÍTµúzMÔ™.ª 5f¤õ-ëTÕÇf|°|pñ-ôsýÅ®ž0²¾B;5QËÄs"õH<—£ðÎx~óþÆw>$GœÃûŽoê BÛl8AMžG}?ròÊ¿Œƒvœ¼eSÑ´9¥´ßÁÇW?nT<’b{TtËÈ—ÿø`z„ô¨O¼¿àyÍ÷Ëø¾ïþ¬q<6¢ˆS¨×¥ë 9N…¿zR¹å—‹™}悎©·ÂéxÓ¥ªçŸÁ6ØL§7,ù½ðõ!Ÿ?ò|àë6ßç´q<¶Ÿ;èFéÄ‹A›“e}ÝÊù.-aÏ×=ÕMF!µ‡úÒ¹ÖPÆÏM‹ë¿ŠÄ÷1ùßü\_oyôƒ8Ï~áZ\õ`O¦-zÊqÊ|§ÚÝÓÌì» Çœ×´4ÝôÒÒjãç/øþÏ7þ=q^Q+ç9Ä)ÛgÁÆ£YTí»2ŠF§ïÛßsºæ,cI²C%â•Zú±ÉbÛ¨ÞÓØ/kLHêçõ“åþåüü,¯üù&®SÅõ®ßÎLUôÊ%ù–íÉ ¸]ß¿Úì8Íœ0¡rÇyË™uÐø ý6i©Ø/:5›Ë¬ŽÇ<£ãu‹×7¾ŸÍ×Sb}“Ö9xý&.ª°`^M쎋'[ÓŒ¥õ×&1[‹õ½ö× £ŽÕûܘ9%–uOê™g^lovUîlHmïù3þ>ñ¿Åóùrîß ÎÅSª{ Gãýª1'µã)¬«×2º¼‚õÊè]zT×ê0Û‘qêF,«ö šÿÕñ}ßOás@®žoâ¼½QNÝ –O=3¨ñì­Ý.ÏqÐ’¤–ïÄÕ]ÅúåÙ~9uh »5žÇ2îo,ÖÅÞ}>äûf¢Ïn“œ¾Îˆ3ål¡ÊjÔ4tzþy4yÛ¸<—’W³Îº¤)kHµƒÓúoÿ.–Uß:rQ¾óý¼çù~2ÿ> ?§S~OSÿw5'^Ï=ºAœñýb_ŒÊ“!ÛtPwmјÕÖ0q>­£Ëko¨;¶eñ½ƒ.>ß‹Ï+[@WZÓ¨›ï¹§6nC|}Ã÷{„-™¿õÇö9Î7Ùçi ÿw#Üédv…µ*¼*ƒšéºncÊZv¿¤zxëcioçîË&ibÙˆ²s]­S{Hëž~çÛýíòu—ôñ$øÞ 3ö<|,úšw£Z‚s)ñ{;.ÄñØ";Óén…_iA[:ºf©Ë­gB ¼§œEžíì±RÝîÎÄýŸnÔÉc<Þ…¾6Žy"“=æç¶Ôõ¯¼ý}`MI,‚´ðèfW¦jáËÁ<©é”\_YäÙùtÚ68Óœ5a3ZÎ/äIÏ«VíØ®Ù4vùá‹\Ý™¸þkF;K³eNkF«ƒîN\^øFðOµ?ÜT=ù~°XÏ[PüQaÄÑÌ1Mj²;Æ4Þ?hßÌtrný­Óˆ})ìo_¶^Yn@8E—Z^pbì4îë-í¯4!>äçÎÄçÏÕ`ž'¾çÂTˆ3#o×cëW¦ÓãV ËtjX#X;æÆFÖ¡{½Å³³B©ÿw᪇c§±rÃ:}šGÕ]ZïðsáÕ½}(×ßö=W©EœŽ“ÒÒ—N'GAá‰pŒJ6Û{å€ÁÂêïo3èÂi-=Ën?óq…ilWÛBïVÖƒÙ>ª±ÎRY:7)÷Îqø¼“÷A¾ý‡qôž ¾tjSFø&Ë1Š+ lØobŠ~ÕËlø@K×ÇÞ7¢ä4–tUõÁ¶¡Æ÷÷ùzÔ»¾‘λóý*_?n3âävéX×ÓIüÞÌ1Ê»T­¼‰UÞk:8©õºµµ^9Yþi¬c‡aÚhØÞµz·éÞ‚ “äªØ9M½ï¯£|Î×]ý Îñ¼34-ǨvŠUY¥Á1ªyæâ—­ý63ãÓ_\ àÕi@…ÛeRÕÝY„åÇnƒC[?çÌÏÏðþƒïƒ‰º’¾_ƒ8¶Oý^Ì«rŒš~Wµ]}i”¿hê¦u7³Q²ê;j)uXÔý¤¦0I/Ì[÷=:¬åóz-æsSï÷0=úÙ©ºcMþ¨È¡4zº²ÕøøÆiT^—Váz¾-ìjï^m÷ %¡Ê6ž1YÚ÷èÂøyWñQ-iðû<š¯Ž?7<úAŠ|bèô4ú¸Ì¯ 9JîÇ O/˜¿…:ÕaÆ9aTxÈ|íådæ9¦x¿ãçZùº–÷Ÿ<ÄyTçÃTˆ3vú¦¬eÝkèoyTú¾ÆVVã̤ïO¦„Kçͦ°ý5Ü[U®;ãý%ï«ù¼˜ÏÅ~ª¢ô߉s-â8¬zy…÷ð¹”XÀ,;Js>a;~ÉØÊÔ‰× 3EÐüÊÂAÒ©lã¶ßÖT©«fü¼$?Î?Õ ^?ºkžc™’¬´z*¹î>Ã¾ÜÆJwjÈJD’0},3UšG¶a¼æyÀû ‡ŸãýœG7ˆ3·dò¬‰K’É8áù¸Tº»sØÊÚòíìPï³î6¤Áá ~ »øõÊ]†oÚ2þý¾îås(~>ŒŸ«ÿ¹ø|³!N»ožº¦ÃQR.É0Œ­“JIÛu}ŽlgÓ«Œ¯IÑõ6W­^~ªt¾Eíí§ù¹-þùóÏ…ÿÿ¼oôèq”Å‚ûäÏ”*–løÞ¸óG¨™ÿÐË—†ï`¶]®ýúˆú[ÑbtøâT&~¯¶+ó·‚·ó<æë+ñ¿è¥÷­lB«¡Ç­ÃÉù^¾!ùËDzåÛ–1¬+ã}%ŸÏüq>Íë³ïœHŽ8Ë¢„o¦¤Òðªß×/]õUù¤ÄérË­ì 倸¼±¡d!LDb™8¯ìÌø>xÞ¥˜·Nóõ¡o«ðús2ë˜/ÏJ¥©ã"QºìÔø™¹F— ìðVC)ÿµT~`hçÆO§Jß§ê éRîÝGùãjÔW[•á‰uH|^ˆ¿‡q²²¦4JOòÇj¸ÝN«Æ}wꛕ;Ùé””¥²úS¡øà Ëi Ë×¹lÏë½ÔL\×ÔôžOáu™Ÿ§äç¸ø¹ž¥~âŸÿo÷ü¹‹éÿû›~Òï÷¦×ÜÛ{\«¨É¹ô¸6KIüº{K’A’;&×2$½¸ú5w¦ó»5¹¯÷wÍzkEàË=®_ug:÷´áÞ†ºWxª^sg:¿[SýŠ»˜d‚Gp+þÑÏæÏîKül4>¯‚·×?{p¿°?„®N ‚à“}î8y[ûÞÖ>ƒß_Wûü¥ŸÿM}]¹Ÿ÷u A¢Zréëš,ÝÙ¤N B2'ƒ$t p$¶È܆\øºÊ‘ôF4¯¹W˜ßAÇ}q¸§aöøº*_îëúª{…¹_÷óÒ¿ÂÏ+ä5÷ ó{è4¯ð·–CœF¥øG¯ˆ?»SXðŠÐúø&þ w; ÷p@è1ÀB x €ècè÷gïÛÚ'Ö¾·uï?W÷„¼1û½¹—+÷þâ^®1À•K/W§tŸúëîÞtü€–\x¹ª‘ðV GÒ_sϰý^ÖZ`/•{W]àŸû¸:_sÏ0÷’à~`æWø½îža«µ „@ˆ ƒ ’?Ž¢´ùü$^v×°p—I¸o¸žèaý¯Ü¡.x㨠ðd‘Ç—Ï›oû½·ýžÁﯫ{2éçsù½™+÷ãþ­j$ª5—þ­€$Ž.‚d¶ÚÜ@Ķ9’Û˜ ÿÖ $½ díkî6îƒ(ˆ#·þ­ªÀ—û·¾êÎaî#ÁýÁb^á&øH¼êÞaÈ–ütþÌÃZðÓ îWÙŠôø³;‡áêêýîhþ±8 bNt pÛd·¸ZºËS¡€¨!x+v² oû½·ýžß_W÷ä ÙïÍ|^}ý¸׫¸sáõ*x†¹¤»Ö“A’8¸@’ÙdHhp5Ûš ßW Þ‚ô¦WÜM¬Ž?xV뀣Tî½_õîýêzÍ=ÅÜW‚{‡%¿Â?ìUwk >›_µ¨!D+CŒFÉ7GQÚ@P­œÞ/»»XÁ&J¢Õý‹w¬ ž9!¸È rpƒˆÝò¶ß{Ûïùýµu/HŠÿ¦~°Ü7ŒûÁj¨¶\úÁZ Iln F2[ mY@ƒÄ¶ $·)~° $}¢”ø:àJÀ ü!=pÄ Jþî©Ã=ßëÜúÁ†¾ÜV!AÐ@P6Q™|<&¸o˜á¾aˆÎ‚ <ÈZÐa¢$DÝKü¯í@!Ü×. ôþ«8 ¢M–<&ô>Þ‰É "Ž.1[€ ‚67PCØV ‡¸ h r+CèF4¼ 'ýŒ$æ¦ðç_­}¹­{ÿ-õN¨uÿ‹5ñÚ&|N¿7ó€õõ <`H¬\xÀ† 1-@†ä47P#I­@ŽD5‚, É¥ÿ«Ø (%µ8€ÉmþHp=púx[ëK½Þ÷Õ*y¿Æþ¹÷«¨!+C F4ŠÍÇg"7¾`F4“ AP& ´–]ò³ÎÌ‚ 2“䑣Ňh—¼_}}'@ ñ™?¨N „ÍÀ_ò±vD™  Ìà!¨È Rp5ÄjrÖ(ùã¨!\+C¼F †€­oû¶·}›ß_Û·)¥×S¿Wî Æý^µÀžK¿W#‰ hÌ6„„6l EbÛɘ ¿W%’Þ ü‘øzà* @1ÀB ‹äuÍ=u¸¢àu[¿WuàËý^ƒ $ÈZÊU"ÈZo0ã+¼Á´( ¼DI|:àJˆÐ,y!ê_â{íJˆÓ ü!P=p„š  Öà!­Hž×.ÉoÇd±¸ crÚ²€¶ ˆÛ²"· ݲŸDÞ„“Å&óQøóÏÖ¾·uïÍëžPó´~ÿ=õîßUë^Wç„÷úM½^¹_˜àó„d4½Ï«¨‘¬V GÂAÐ qm µÍ²6—>¯:àJ$¹ø#ÑõÀ THød x¼—Ÿu€ÐË'Ê…ß«Mò{5þ¹ßkÐ@46á˜@6ÐB@v$y‡½Ì7,¢2l …¸ì@%J"Ó‡äc²¢³„—(‰O@ š?„¨N ‚ “ADTf2ü«] "µ„jn †`­@ÑAÐ@¼6g DlA² d m{ÛϽíçüþÚ~.DúïßÔëÕ T>^¯:àȥ׫!‰M [ð·F2Û (%µ8€Ém΅׫ IŸ ø1ÀB AàjˆÁ ä„dùx$ ÞÖ¹õzÕ¾ÜëU!%JbÒPBTfIX:` Éë5h!4;P@l‰’àtÀ”žøC|zà*ˆ0@ˆ1/ñ¶vÄ™  Ðà!ªÈ Vp5Dk2ÿD5lrˆØ²€b¶ Ú²¶Ä( \ì@¡'Jb×ÁE¸i7ñßÐÏý3µïß]÷þSû­ÿL­{[ç~¯sÂûfó{3ÿW3ð÷ñU !séÿš4HVBš@6Ð"qí@äM”X— ïW=Û THîd€.‚D·™àû ܲß}¬eH|p•z½ÿ«]Øgüsÿ×l …Pì@±$J‚ÑDcÙ’ÿ« A@& ´’( ¦DIP:àJË üÿWà”ü«Í’ÐtÀ”œøCtzà*Ô²dÆ-@A€ „@˜ “¼«Ý@ ‘ZB5‚, `m ¢5l …xí@›@6ÐBÈv €˜A6ÐBÔö·ýÜÛ~ÎﯭséŸ ¨ D"š?’Qœ@…¤LHÌà!HÐd€$Õ'P!Y“A6¸@×dH^p’8QJdp%Ú ü‘Ôzà*$w2@‚ÇA¢[€ Énn‚¤·ßÜ@ X"0‚, l ‚0l 0l’çµØ"I”„¢ „`Ì’h´Àrü{@8€B2ˆIœ@Q%K@ %J"ÓPBlfàÁé¨ ¼dñÅ-@!^âƒí!§È Pp5„jrˆÕ²€¢µ9„kY@Û@DlÙ@ 1Û‚N”D­ „¸ÍÀ×PBèfà±ë(!z3‰¹(¥Þÿl?÷Ÿ:C÷ßVïþkð{Û…Ï —(%8€ÉgþH@=p1 õÀ ”HJ3ðGbê¨ É I\ Éš ´HX;P i¥ÄÕP"ÍÀI¬N B2'ƒ$t p$¶ ¹c€ „ É-@†D77P#á­@ޤ7‚, AòÛ€\èç€[ø~,„-Ô:Ô7;P@‰’0t â0l¡ÎÉñï„’(‰E@ ј?„£ „€%i( ¦DIP:àJË ü!.=pD–  ´à!œ?§N ‚ð’AÄ\ "´„hn † ­@Q¨!N+C F4ª A¬& ´­( ÜDI¼:àJˆ8Q²8€‚6K¢Ö‰ÿ›7Ö)ÞØàù³âÿØ;¨(¯µûcG£E ö±cA ¨ÈP;¨&–¡Š(Š}@Å¡¨ˆ [ÄŽK –(*ð ÕÛ€¨c+cGŠõ¿gÞ÷L ß-¹Iî—o­¿®õ[ÜëõΣ3Ï>ïi³·øŸ)…Ò!+ƒµÕs¹“qAšç §«Œ¹§+rÛØä8q߉o F“cŒ¹x£‚³?œI‚Ž”…$ÙuhÖr(•Ê@!ïøKZzwA¿¦*¦yµöæ]ÇÉ.o£Å”ï¼ÙçsotÕÞFЬZûûÜFÜ_ƒç¾s¿BcΨ˜k+ä[ ~uJÔ)÷,ôÓa÷SLð‡:É9œªZ¥ë Z¼9'õ]5_f°¯ÛIKFWs«4̘÷Íó¹Ï—ØÕ¤vŽE¤C)_<-êtW=Nß^¬bGŸ˜}NÚq’ùŸt‘Œ¾|‚äž žÚ/óýù£(â©,˱Ì0Ú4ç¾SXìÆ}„¹Ÿ$÷AüŒþæ†(µP*øR©XÏ=V£ýƒO²^s®«æÏM!óÁyOjOögòYfe_6󉆈>=Ùî뽬Ïþ 5æK þ Œù҂ϰè'ˆ:3>==8f¦ŠÝÌ­¹Ñßâ$«u¼ŒÇŠ6©t¤û·‹*Lóg­ïûpÒ9š~ùå­ö§þý‰û }‚ÏÝí€$êôCGþSÿºR¼®û‘¿V1Ù¾I²)Ûr˜ÅÇ‚ ©´äŒ*;u“«wi˜Å¼QÑ¢ž]>©·Od?vtβ÷k»ˆ~…†×“áõÚMì9³­Š•M© w0a–,n4-R'‰kïË’ùDÍ[-æáô}‡0!—b°1•çbT-¬èàdÌa6èu†´è|¸ŒŠ}ì;ûYùÙÌeìÏŸÖU" :÷Uú‰Þ¬{›ð0/ZÌ[pý]˜çÖǘÛÄsq„ì¾F?Iƒ.PDzbÛc±Ù'ÙÍõ“öžÊfSŸ<Úüb)‘îÝ—éÝd,øÔS»:óÈêÒî—=šº‹þ9.¬uöÒœ-á¿Ö1÷Î¼ÐÆ¿/ãyq=àõ+ ñ¹üfÆIVIV®IÖêlÑ×YIÞ÷”7;fÏU^ÁŸÆÌ£ýš137.úƒõf {mñ¿Á\Œù`<ç…çt€×_°èú…‹ N2óŸ:uöÊf”ÿ®¨­“’œ¾ªl9êæHö i«‡ÞóH»¡ÆŽÔtOѸ|§:2ž³3ÉÇuP‹‰KåÄ›¤JÕ—ÒÓofå0ƒ ›E6Û!i÷\¦¤Î†Àó‘lÿÜËcϽ¦Šs)Þz„ñœ^!G¡ãÿÈmþ}KåIPGÿ.õŸ’ÃNõuã€&‹Íó™ßøì!%Í4 SoµÅ ±í ¢IÈßdôâyƒÜ7ËÐ÷x½ýív•Ïj–ÃæO­Ö±Ó¦,Ö«úwYéÏ•Tón\à¨O£Ùà&dZ7Šç¾ÑÀ ÀrIeœD_F'£¿-÷³äùM%s-e¨³/§æ¥M6+ü8wƒws{â·ÝlÒiÙýäý¯reÌÿVíôÏÃ#I'‰çåòœ>î[Äý«xÎ÷‹6èu ñOqÙl¤>¶~{°Ä¾r¿éþm‚ý5;ömAËc™-"IÌ-'ÁÓZô9ocôßá¾ÝÚ6Ç_¯kØ©”obê¹Ùì¨Kù»ÚüL6Tó¡çÃôt:½«IÈÜ…¾¬]™â]ÃFR¥¼bµ=Iï¦6n‡Dôlø?rß`«R~ÐJÔ©]fÐó;³Ø»AgÂ:¬Êdª{ Gö¯™AË4ÞÊó¨"y®ñ\&žÿË}´¹÷?/åsŽ:;¬÷¿X›Å6dI’¦f2÷‡!ó2Hð±ògµ÷Vôžø6’ç„} ùûÃý?¹÷+éslB…Ò+Ž÷¬=Y{Ò½S[—Löκۈ Ô Š>dRóåÖbÙ®û•öGŸ~™Ž¬KÅfÝtÎÆž;/ä*ˆúÀëŸ<60ÙóA&{Zuèw$™ì¢þñ^;“.? ´¶Pû³;SRÊæ­Œs`%4j¡ôõ×Mú2Ár ÑOŠ÷5×{IŸI)ê̼Z·Æâ¨L&µmЭÇó ¶Ðìà÷ñ™ä<ðîYu;6lé©;ŸšFÑÜvumîp6æ÷p=òþâ>L£Þ¯Ùn›íPÊV†:CUú6¢q&\;Ä9)ƒŒ—Ýh–In)“wjéDŽܟhª‘}¯šÍ«FŸ{îƒÅ}͹~xÞïCƒnPçò€´+r2ØVYN­}“3˜ë–E{æ{y.öaA Wç© çQÕn§©› îÏËëq?1îoÌý޸ߺA7¨Smò¾E×3˜}ÂŽ6— víÊ/~†gÑˮ鮡o½ÞìÚYÒô¾šyµ!E½Œy7üýã>½Ü×v~޹ӆ÷¹2èuöôîÕ!´\;·È|ÌS« V¹\ë™ï7d‘¦ã­ú•—¡Î±Œ#³pÿqc~7Ï9æ}Æ}O¬ùpÏJÚÉ8Ntƒ:²½åe®MgïŽWýü°bóX®¼Xt7‹>;} 3ïû³mìýùbþÝ £O–àcÜÁ˜wÃç<û6D ,”~Ý;ÂqyËtövßøÃõuél=í³©ÀÞˆ |Yãî“|5'æxÿ{6.[}K¿³íŒó`î£Ë}ì„qTô/CoÂC§ïS²æ+tþµN§³Çåš½~•Mcƒ+ÿh>0€ߤ;^E7Ô«6†»y÷óã?y¾ïžï$ôuuA?¨£OƒO°U2‹­=µ® é,æíÌeîdÓíecûIJz{øÕ‘Ô§lµ.Óz÷æ¾n|œæ?ù¸&ôµPG†:Òb·­Í—SÌÑO¤3õÚû÷É¡ÀRÉüªA¬e{Û2äIÝo ^²fââ>ÙÜ¿–ë”û‚ õkºÁë[VîºæN+÷ýÈì;vé¬B¡óúü¤zÿÀU¾Ø!ˆmp‹QôLŒ¤ÏÓ'^]|Á͘sÀóÚøßŸë†?Jæ6$ ÎóÉ;+_œÆ´­KžÔJg³–†?ªny’Åu+’´ǾΊO2ŠÖúêÞ}yÎ'ïc>®E¯_Ü:3¹»ÑGÑÐf¨ódËœ}Ú£©lA‡‡Ïw)Ù MÕ†5=ImËn]ûyì 6%8Ѳp’’ÕðƒÜš7ÂzÎ\ÐêìmÞྫྷË1ÖøÇJ+ïÜ%¶éhÕ•åN‘çàÍZ­.Š=7’xNUÈ–Ž&îûÌó;›Áç%%ýîPç~‹ qç%3½Z÷$vµ ýÉÛ‘§ˆç~6ÍT4÷Œ ë}&îËø<šûÄsÿkaÝ)êuvºèÆ>š~”¹Úelm2ˆX«×Ê&us)¨¦w`Ýc¾LŸÂ³nF]tö~Åž£Œë€ßúê ùt…ùbGÑ_ôÍÌ(”&•×/ÌŽ°&–•Ö,íJìÔÚˆæƒr©Öš»µc°.~ztÆÖfÍ0Ÿz{[Ò8×Ó˜ÃýS¹o&Ï)ƃÒùQÔÁ †;ÂVfì³³'± #¦\KË%ÓûsêŸ]îϾ;{ÌÈ=‘äà¶ËáxáÄÿž<¯MxýÑ×6ˑσßuÁ·]šÁóÜ~b=OŸÛ¶±Áz»X‹Ó˜  ñÀF¼WWxmE}¦úÝ_“îE;Ôü Õ(´¨–æÏ®v³×¬·Œ$_E/s+oc®:¾ð}Þ×ÏyøM“:3A_Bεu>¦+§>¿}e~}n×R4vTbrÃ!ì 5,c÷@=ÇíY¿ˆžVޤ§O<*óJÆç«øs”ç¹ðqºäsT‹:ë9Þo{m½xªNl÷4f7=ôÜäSghsý 1‘u|YÄ·UîœEµ—{מä7Úø¼æúãã'ŸçðqZÈKòÝL2 ¥ŠVú`èÌž¼·:UIcA;_9˜Ÿ%™_rÑ”“Þl™[Çí±wæQ™oüN¿œ>‚¸O6Ïñáëþœã>Û%Ç êè®? ›Ärn.jõn*Ûüèñ°a^g)u±Ï±e¬Ý§#-]', ÿI­ ¥zs|xž ï·öÓ'ú,×:ò<™’yÅRÔYÜßãêº?²~q/HeI[×~·î,yLü8xD¢Œ©_:$w¿€†yJ¾?;’øøÌ÷7xî&÷Ñü‚Ï©2èuž³]ä6áV.©L;¹©L¹7¶öÕ›g©íØ~.­f{³á›÷þ 3ŸžõÔÎúó¸O2ðç¯_ò}S N¾Ý¸ºµÎîe†ž2qËNegÏ®øîYËstßæ•ü';_6..rW\›y4xÄÀËæ£Æ÷µåã ÷Qçù›ÜÿºdfêÜ™YuIË=¬^@ C¬TöpxË•CÏQÒPwïþÌEoï-î#%aßÍ™h±½sÅR£½0®Ý3úu—ÜçU¢ÎW/œHd«nœÑõÉLeF[nNÎ?G±«ÛF~;v,sÝÝòç÷Qn˜@úßOä¹>ÜWŸÿæÎ,¤—~Å!æ;¡Î»­=F».ØÅ‚»/1ÍIe‹kǹ¿³=OûŠÛT~½6T±Ï©½Qdzvÿ¬65}y¿‚ÏpOQ§ÏŽrÞTöPÌ é]j¿Î$«PêÛüÁˆ¾mv2Ÿ±U6^Ö¤2Gê|yõy:¾Ä{÷BÔò0¢Hx]_âóÌae}j¶»çÄÒV¬k»ýà¯9¯ ¦œpfÆyA?¨S©²{Z¼íL£0|ÿI*t­î„ÞoÎÓ“··³ñ¾M¨â7Ã"5Š|ãÍŸºíK|]ÍŸËÜžçý ç KùOKQÇ¢ÿòÙÍã¶²wëë]0Oc›òêZ©éQnU´¸?Ö±Qô1ÐÞbš7ñu߇äû*ü¹Às¤JîCÊPǰ—À¤=f¯µJc r§Wîᮦ1;ìV÷eK+ÌsZëEÎcÂ>LÍ“?OÆQã:ž ‚¿³Ô˜;mÐêL3ndƒ\V{_¯–ÆNºuÇa¦šdß´™1Ö‡UëzdSãúQdõL¾òÖV_c'߯á9+|"ìX—ÚÿH@’‘eæ…­cµšöàu3•=/÷x¥Ýz55?Þ@ãÍÒ÷„½ÛØ"Š„¼¦@âû^|=Ê_Ÿïòõu³¯{YŒ»#ìK(Qçm£µÕ7nZÃ.>¶ˆ8–Êî4֟Ĩi¾takÏ 2–8láÁ£«¢èôÛŽË'þ|áó>ßåûæ%ó$µx}IóÁ“Ž®bƒWì_;ûP*»mq§Ïã›jêÿzh~hO™q}°øêÇÖÊ™ãûxüýàçZÂø|ͱýÄmeR*Ø–Ú_1ÉÆxó`˜­É°åÌ0ëIHeo“›[uú¬¦JWŸ;·’1ƒ û7ÑÆN¾næù~|>Àûšçß”ò9G³¹oO:Ë\޼úX Oe³bg^Ó6Ê£:Áï½ŽÈØ€ú¡’ƒ®Q4nèôeö‰ãM&œÿØs‹…uˆÖ±d.¦¯ÿ,iᙑÚH¦»²ïýÈàT–WæYÃÞÎyTpoª}y7{Ù4<»n»Hòkƒˆç8óm&ëó0Ÿš­Ne£~˜è6|lÕKN<á9Ä—½ù~JšfV¥Í×&/^dÌ«äŸ3Ϲà?ù9`©<'ÔÎ|™OžÊjgžŸ»,>ævûþš[–¾«kh•›Ó-2ΘÈç™ÜGŸÏ Ü_·ÙøõyÛÒÏÔÆÝ¡4Èð¥1ªÐTºæx©ï\zhÀR~UñòõcŽäwë&±§êç/|_šÏsxžpɼ%êû4js×ÇSIØ?IcOÒ|åO óhÙî¨0–5Žëˆ¤É]GGèúŽ#ž;Åspù¾4ß—äŸOÉÜb-ê”3mÔïð½ÊûX+›”ÆVþæäa‘O++ÍéµËy,«}?¿xžg$ùÔ*¿lÒó ñßÓ†ñü>^ ûðKí{šäJmúúÇv5¥Ñ:Þx¼= híW¼¶W>_—…w:€ß;ÞêþO‘4}†­}¿ÑÁÄõÀ÷µøþß.•‹Ž×·zµ ã¨Ê€]Ó뤱7 å9¦Sò)fîœ:¼X[ G´ŸI‚¿~°qÿçïòq’ÿäóæR¹¨³ÉyÁ!É*k²kæ‹4¶®¬ã½üùtùVMÅÜ~þl_w‹kÛ·FÐü2ý÷û¼OÂù_K&¬ß[Ç>Nò÷‹çô‚:†ø’škÈYš]ë›^Cž9žÌ§­z)û± ÎT¾zOAm7lçûíxâç•Âß¿¥qœäŸ;_ß”ÌGV Nø’fXâ­£c‹×× t$öxÞ€ž›žåó|Mv®‹ÝΖm#“ùgÌ6Xÿ­Œót¾_Ã×·|ÝY2ç u²ªè7R6RùOE¯nEkzy`«k_ *Ιû¼Õ¾ìóªÖîo¾¤ú¶m5s’q_ç¼òÏÿ·y•Âó\8·Vêû,2gÀÖÑ äÞÏtøÅÓÄÜÙ±£/Б1w7§Uñgß\.ðæ|-•¬öés7„xîÙõƒ¾ýsgÆÏùü™b\ïƒ^Pgy˾—¤mß%+ÜܺhLÐzÓbþ=¿;âyf½¹³Ez½)Ä÷Ïy®ßWðxÞ®_´â~Jéü+““…Rá¹»MÜoT2w³Ûåû¯»@/8Ü¡^åqlí‹#ùãEPűEo]™BçBÊ9²ÅR¦=îã"Ÿ%5æ“ñs£hWýF“´TÞuNêà;hœûæ{Õ×+Y§Äå/¢Î] NÕ_ŸHê<ž™Ëô¦ éÿzÒaùây‡|ýÁ÷ŸøOá|Ñ®Tλuì:«gŸ»±“No\+³¿¬d×<¶L©X@­®$Üowó°˜7ªVuê3<¼ÎâÉÆßãÿa]xÛ‘ß )™[/CÜ_¶MÊï”H+ô;&élÌ›-é# ¨{°íà¡!Áìē˟9Dˆó³ÆygñžAã91?ïžò©E­&Z©1§Æ Ôy¥ßÈÚMòÊþ! Õ¯¾3åÀ¬ZûaÝúæG'°m#ëRQ¿H¢—’½«O¦íç¸(gãsÍ^’î»Pg\G‰çXÆõA?¨£Ÿšåî%a~”ζø8U\s¨€*LÒßlÏÔVú“­Hšñ1òFRðdâ÷W„þ´7žðû,ÂøÝ½Ôy›u†ž¾ô¾É–èpœ"ïþÙt¦îSgr]ÕKèT`Õl3l·zDÉfåU!ÔCÑ;ü£Ywc^-?yðunÉsj-ê,Þ6Åü§N?RWÃÆtk³¯JfË‹ÔêE­³£kŒe«“—NÎÎŽ ƒV?¯³0„¸ÞùsŸ£ü¹ÃóÄJæ–š¨ ¥±»®MöH"y«é§ì]2X#ç9«{]¤;L0£ógÖÎjw²Ž KO¨Gîšl|® ¹”mŒç9ü¹S2çU‚×?¯_FW8@{G™ç×Ê`®#,Žx‘Öyò²ÉY¼{ÝsB’‚Ú¿¯’¿¸÷Tq~ÓšñŸ|¿†<'³äx#Eæ‡Â\Ûr€¶ÔúáúWg2ØÞAŠ™k.’ĈèÇô«çoÂt{ôÚ£ºM#¾þâ?y‰çÊŒïô‚×?}qÙÀ—M’E'ë¶u3™a¡¼HWLpê×Ç}õ¬’ƒBA9“ª´©=lšxÏ¥‹¸¿Õ…ñ{K|¾éUÿÜ/AÕ¥Æ\fƒ^T|}vBŽëÐÃ;“­ØhyWw‘Öt©ÿ}Åp?ÖbÏÑŠGú)H¸‡6͘S*äúu×MŒ÷—¸.ù<Á }ùª‡ß$Ããds&;í½ÁÏ­æ%š^rÍÔŸ]Ò.ÛÃ.ñùÕœÝ'4éÈû×NœŸ=0ÜßPâõ’uú ËCtÇäýœÏ2Y“½ßõhÙã•Ï{ö¸Þ+ôv¯É­àpº•ýaò‡ûSÉòÉüàE¶ýY#óVgLve½~ÈZsïÚã:™ç›ñ~3èud ön}Xœïe±íCžÖºD‡övîÚ¸ÎXvyçT‹ŸÃ©ü·Ç#ƒ³¦Ò#õÃP;‹aL¼‡e\óü`ž×Æs£ º8U(ÕQ%tØazPAÓg×þ,¶csÈŒñk/‘Óì Õ«Ù2Yã©þnÁ ¢*E7,™J†ã7¦lBá1Õ\Œ÷È„uE‘ñy\r¿Q‚:G4Oåø‰:]·}ÇË,vyøÖ­ ¹—Hûzí!ÇûL¸ÇA½nT’ü´p*­_j2Ñ¢ýp1[ŸpïÆœö¹~ãÆÏâxÿB<‡î)Î/Û ú@Ͷ¤,Ûöõ_9Dý´y6ëc7öá¿\¢–e ®4ŽÝ~êdªZ£ ]P›7knL×ýƒØÅïôX°Ë¶Ùxøµ£—Å ç[‘/Ój/¬TìÂŽMÐ'Š û22Ô™«,‡©òš_¨?PÈfo—Ö5÷iw™jȶei Ëí\Á¶û¢p2Œ›Óˆß707\ê!îŸ?÷1^8v¼;*ÅQ¼'"èQ:wfz->~„j}/—^Y•Íîïm÷bùèËôÁ+}RÏÅcÙ¼–?¿_Y<›Ä8Ÿº7Mvä?_û}ʳ×Í8o3èurlZ®Ìz™&X™åõTù±×ín.»Ö~.­;ìÚëüiÄŸ'ü¹Ï÷Ë„çÿcG!7ÐÎ87èuÎè¯1˜%¿oufÒ×{n]&ÍóùGS}ÙžÙç]£]”Vlë=øó„Ÿ« ãýqŸ¶È‘ÏoøùA?¹…Ò¶/[y&“Gÿn¹érXî½ö1Û*j¨yÜÑ2½|™0Uç¡sÅiÉӈ߿Æ…î¬JA%»´õ¯-®Lu¼½ü©¸Ú“ ÷¿„¾–äòýÆdÒê—Sr˜Yë–q*k Mm>ÀçÛû¾¬ßWk<4U„Áë›d‡$ÜŸè%Þodâ>úsÇõ¯6în|≣{@ÔÛ).¥Î¤¨3:¥AÇÈñGù£åO²Ž‹O8®¡J5Ê7mæÏ, 7áä·:¿cj–œøs]Ø×°7Þãæé:GÞ%Ϲd¨Ó¨£ã iÇÄý£“¬K§*ÏöEj¨`ð3ëõÏXᔨ-ÕÛ̦‚ )ªò¿„‘pãÌ;&Ý%©”ñ{¸|]б#r—…: Ôn¸øtœÒ»6í7+ê$K­÷U•¨ƒJ¼·AÓóC sßþ¡k…w¡4ïÜø'5/΢9åVÖë}©ãºçë ~¿8{Y‡™Ö™˜¿.ÎËP§•ÓÜ-}'ó ï¥/Ïd §c׌z ¡ªûmû|?ˆ=ÿ|7ºò¶Pš¾ÜyDñû0â÷^„ûDŒûf|ŸûQÓ¢›?;²á^÷º< úAaÝ}œŽÝÚb¥n©b¯î­+»¶ÁîX;âCÿ ¬¦yA‘õ¾YÔöèáº5ßʉç] Ïá_÷ó³Œv± ‹íKå¹jQgÝÊoÖ*Æœ »9Ö 竘û‹fKo¿B·+-^7ìÊ6íüq—Ó߯èjl+œA<ϯ?ù:ÿäyØ%÷MNJïÞ…Oâut«óÙñ®Šm{{ÙÁ+î ýR˜º²Ûviª~fNŸÙ x›uÓé~Ë‹Ž –µb|ÿŠÏ/ùü̖ ŽªùR³)´]Ûê^¿§Xó˜ò¿=y…Z+;6ˆÅct®_~½=ûñâÝ´éÆ÷ßàû€|=È÷‹…ó(q_u ï郡S(`üœåêïO±QCë^zw…ÊÞñËkýp,›£>4âõ±0ªry[H*3iߣͧ¤wï…üºNö3Û•ÎÕÄëÕ¥ä´sO¥Ÿ2Ǭìü諪ͿdÝò*=ŸÛÅÊ óØ·Žó*ú„’U²—{Ô89ñ{Žüó矻pмÔú\×Ƕô:™J»7ë/pç² {ýÒÐí*Ÿ_‘±Ý—õ9óä•åýPZ5vtõÏåÄ÷Ã…ý¦¶Œ¯ÿ ºÀëÅŒ»u}L4ñ^}. ?•e=ã*}8wøà >ÌmxUÏJÒÙÞp›Ã«)rcÞ$ß_à÷ùþ?ç.¹?ªDA†(–eé/°å²2K¬Ç¼\w•bl ?Ù߇u Oìä0—´ãlïï˜Iü?7à÷Aø9#Þ—ÚC•ÝÊ Œ®Fô"eØ„3sY~Ûº³÷ÞŸŸ9Eø°¯¦hæœTPðíÐÝšø™Æ~å÷÷ù¹<×!ßß(™×kr¦Pºú¦S}ˆœ Á§YdbBŸ÷ñ9¯séûÉ—yþØd[¹ |ðâýU ¹1ß–ßßûíý#á^sgã¾³A¨oyúçþYD7í[™µá4›ÛàÙ•VU¯‘õÜ:…¹|™óšuÃp²Õ_OJ%~o_|žÏÏcøy'¿dÐêÌìWéñçúJñÞäiV®ß»Ï-;_£ÜëƒúX`ž8¨ÒÁMa° xu0ñœ¶/³˜øÎµ_˜+r®ï9ò{á¿,Jè°2Ñ®Tþ° u¶šî¾vÅS)ž‡ža]4¶ –]#W‰þd7€®j÷±ÿP’çyÆ´ #¾¯/Ü;iÏø¼œçž ÷ósïu„}D% S­³ˆ>ÃîUYulÓ’kÆ}Ÿi›~Ñnu¼¿UÜ•Pã½g¾âûþ|ßG؇±/unš€:65W™&*ÉÇÌ«ëaÕ¨ÿƒ'®ÑÙ­éGß`µFGmäNq?«ýqK(ñ¾åç?ü¹ÂŸ_‚~º”Ú—W¢N¿¹íçU:«¤ƒ5_íÛ^û,ÛþՇޝÑó réÖãØÏ>]d>—Lîá16"Ô¸~åùÀ\÷|_žÏ×JÞwԢΚS!›Ë¤Ó®Ó>>¯&Ÿe†ØôÚ×éÚyË 3‡ø‹÷”çõ£•×½ÍCû™|]Ç÷}ø>ZI}šœÅ|lݹAûàõ+êggÙ­rç-¯ÓÇ[ÎÕêË>¶¿¼µBà,:ôàç&üü‡Ï_ø=®’ûóJÔ°çÈzíú 2‰3o±8à<«µùEÇ{ÕoP¢d]A¥³[©}ñtÍ|krcMÇYÄŸÃ|¿ŸŸûqÝÔ}—rgwŸn¥Ÿ;¨óÃ7=»æíÏ ·ì%ß9že5zt×¥Ë ºkk~À:Ñ9Ø_ÿÙsÝ»rÿ»F—fÿž¿‡Ä÷—øûfˆ¿Ê÷Åû›ç ¥_Wé——›šAù“ôBÍΕ;´±Ÿ÷ jæÞðÌuÛ¦}éÕÑV5G\WÍ¿×á,î›÷füü?øú¬ä¹™u6UI.|w!ƒŽ§ê7ÈÕlùË•A›—Þ Aó.ÜPcþŸ7MNkûëßàYäþQQ©¢T\ï;2~_ÏÇx~7ω6èuš­ùþýæW/¹Üt5‹¯Ÿ_.,ïÛî>u¯_ `©Y^;Ç4 §r•cù> 3~OˆÏ‡ùsš£üü¤d®¶ uô»ò m3©i;“%ù;Ô¬¼:Ê"øá ²_©¸S£E‹ÅÓbÊÖ94»×€…ÍÂBó~nÉïí Ï…ÿäΠÔ™0ëDó®²LZ˜±ùöY5«ÐæÕ‡mµnÒ* -w|ðcç¯iž¸¾ž#îËÏ$þ¼áëþïàÏ=aŸFX/%àõÝS;'yDfÒ™æ×©Ù‹ -zܤ¯ëÍhEÃ}Ùw¡§>ß{N>ÛöÝ0`& ÷?øùX'ãçÏŸkü{E%×eJÔ9Õð°™ô`õãàŽµóدótÕ}nR¥„}SÇFx3“nmesɳY‹ÄÛßɉç©óïeòõ¿ÐÏO¹ëÖzÏ× ºAÛúçÇJS3©]ÛSe]óXZʧQ7éô¸=‘ûÊÊÄ÷?œ&¯x°l\peÖÐï´ ×]ƒÅÏù#ßOkòñÍtíwÆïïts¾PÚÁ©Ú­Œã™ôM‘m¦Ëˆ<¶7k~rÔÖ›d~¦Ç©ŒÙ£YN—Ýû>Ρ2í;NXõzq]÷¾Äqí®#ßî9”šßHPgò´ÕÏöʤÇqëoWœÇ„ï3Þ¤;cƒýçfuʯn}}ÖÊ!pÆY$ÌË:ˆÏƒÎÆõŸ¯qÝ”\—IQGÒâ¶sÃôLò1\LÏc§µQ¶º[7éHÁ3:nÃäëíUyn§;0»Nþ»»žðæ»îîÎÆs>Þðýáž“ÐÏ2Ôqy>LÙã<ïç<t0.w´É-ºV¡ëÍ ‹dìÑŠÉ‹ý–)è²[Pû­Oå$¬Wº²ª^Sj/ºÕ‹•¾GwÍQ¸_ãTê{# ÔÉ«¨ÿ¢c&•ï¶²‹ÇÛ<ÖlýÞ]›Ü¢ò•¾n'oëÍ .6X<$‚2‚õ3w¹¸ß`˺.Štgüûü€Ÿ7ñû/ý NxY+<Âñùì³Ðø6Ég3~öÛfÙû9¬nÐöµ7[ÿéIŸX•‚ZÖ¸=¦ü’Pq½ÓÉ8oçë3þÜáßët,ŒÓJÔ¡žî ƒïeRXlý&å³È½W»EÚl<\;Á‡ ûáÆùt¿s {^Ö™mªºt…g¡•ñ>ï>¯/ùÕ¢Nã7zHßfRŠœM¤ÙùÌöÉ¢[“–Ý2ŽÇ7Û¼d6}7»ŠâEÕ0ñM'ã÷”øyŸ·ñï–×LÔ…Òþ[4U²hÜGë2Å»ò±Îìóâ#·hñþúããVú2EÑ–^ýÃéTýg¨ø=¢NŒŸ3ó?ÿ)9>Kðú?V·Ä©eå¦O([õt>c}Öµ »>óºg{ª)þFÚm  ÷OcCúäã$ÿüùú|„á‹xŽâì‚v‰“ÖFQÄŒµÊ5 ûû›ü<‹þÂóHjÜWÙl"üúâÉôÅ“éïð)1ÿ}jýç„&L¦eÿ¹’9_ZÑ—‰ûl–Ì7äy7Ü7ý÷äݨÅ\×?ãËô[ŸÍ?ãË$}™ôžÂqÿ‹ÎKÌŠÐ{ ÿ?:µèI/zÒqß’?›ÿ<éâżˆÿÄ—)ùø2ý+ŸÍäˤ}™¾Œ}_Æ>…Éß7ö™Šþs@&3ѧ©dŽ>ãе„']ɬ/žÁ=†Of„FÌ8ü3™Ö¿õ¤û3™Öb¦µ ÿ/üšd¢ŸúõlÒˆžM ¢Àú/ðUOø¾M ¢8ÿ“\kåȵþWžtÿ(×ZïI'ý2ö}ûLþÞ±Oß; À 4@*z«—Ì´¶DSÊKxÔ•ÌSýÆwøßeI$S4qP{4s0EC‡ ¢±þ‚r ®hò$`‰FWüƧÎM¯:à†æO /A ¬ †8P d… Ø@ñ ¸î¯^œÿÌ[gåüQouSÑ[]-z«s/Î?›qø¼ÕÕbÆ¡èôc Dš $j (^¬XA´q xA¼J`Çb ƒUÀbŽÔÿ³.˜Bè!@¤|b º/cß—±Oaò÷}–âßO«ŸÑ„IÀò› }¢>Û: Xþ&+ŒgKp/âß“-¡3ÿL¶u2 ác@џ̶³­í!Q$@ ì!–`*útêý×m œxQ<@ ì!¢` !… BP‰À ¢’-B\‰À š¿À‡=˜Ar ®¢_§Þ‡ý?É·Vý|kS;h€Of¹üŸä[ëã\¿Œ}_Æ>“¿wì“€D`†”-p=ØKf[KД  ®%òÃxÞDIoâ—7‘ÌÐÄr R4s"0CC˸¢±“€%š[tÀ Mž $hô nhød AÓÇ€"à…æW+ „ 6C¼(ˆ@ öF‚(Ž@ þì¾ýÕc›~\ûoŽiÿÉxÆÇ2ýç“,ÑP  ne͹ŽAséÊÿšÆ3%¤h¶D`ö;2%’€%R´ÀcV°Dƒ*€¸¡Q“ÍŠ€šV ¬Ð¸EÀ Í«Vhà8P dhd°A3Ç‹ ÔÀLÑÜ!@¤hòD`ŠF`†O¦hú R±ùØC ÀB … D!Zà q$KD¤Iâ_}¨®£’€¥˜“‡¯X?VA`*`‘Å‹B j`Á%ˆ¢ j`ñ%S0h€BLf£H!ÈD`QʸBœIÀUpƒPõ·_æe_æe&ï¼Ì^|ý"ýû„&T+4¢â7y9ú¦ÔgZ+Õo²Á’€%UtÀ ›,Ñ´  nhÞd AÇ”ÈAü3¹Ö*`ƒ†›þæZ—ÌBt…@’€D"Zà ±$KF4@ á$3 ~h+D”,!$Ð7*H ªPÜô™ÖÀS°‡Ð€)Ä4@ Ñ%3O´ÀL–¡è€Ę $d (^f2°´ùÏò­5 ãÚÂVpƒÀ“"ù79×úÞÔÿú½cß¿÷~êÿÄx÷ïÆº?;ÎýoqÿÉøö¿9¶éßçd AƒÅ€"à…FK›MŸa­Vhº"àV"L´ÀM˜¤_s¢µÀ͘,Ñ  nË’ ªnhÒd A£Æ€"à…†U+4m(24¯ Ø ã@1¡‘UÀãX¼ø j`æN¦hð R4z"0C³Ë¸¢éÍÐôr R4"0ƒä@Û@È74Åø4@*ÁŸf†h+’,!Ð7ˆ%Y¿Æ„`´À¢IfA¶¡¸AdÉ@¡)ÄœêxQp@ ì!¼` ñ… B„¦aÐ)ĘÌ H9ÐW3 XBœ  ®i°„P@Ü Ød hc@ð‚x“þÛ„1_æm_æm&ï¼ÍUüÿëß4¡ Ø c€HщbΡ>»ZlМ  nhÒd A£Æ€"à††M4m (^h^%°BÇ•È:ü3ùÕj`†O¦µþx†uɬC7$XB$  nK2@0  ®N°”à÷€¸ADÉ@!Å€"àA)DŠÄ¥,)„–Ì 69ÐWˆ. XBx  n`2@„1 xAŒJ`AÆb ƒ0•@bóßϳ–@Ø1 xAàJ`õ;ò¬õý¨ÿõÛ±ïŸy_Æ»¿n¼ûouÿWÇ9ý{¦Ô¶h¶8P\æ×¼j5°AãÅbý]Œk–Ó@ÜÐÉÀM©:à†æL4h (^hT%°B³Æ€"à…1M ¬Ð¼q ÈÐÄ*`ƒFŽ›9¨=š:^lì@ öhð`Š& E³'34¼h+? X¢ù@Ü ‚$` !(€¸BIÀ¢Pp…8"Zà*ÁŸ–‹è€D“ $N (^H ¢ nS²þž¥Rˆ*˜AXr ®X°„È@Ü ¶d àb@ð‚ð”À â‹:ýÝ`ˆÐ"  …)Zà a&3ˆS´À"M–ªè€› $m Ð7ˆ7H àP¼ d%°‚˜ã@1AÔJ w·‹û2Ÿû?3¾ýÿ:Ÿóÿw}5°G#Æ-pEC&³òBnµØ£9c@‘þ;ahR%°B£Æbà…†U+4m(24¯ Ø ãÅ&ü“ÙÕ EÃ'þÉìj+}f5(^ˆH ’P¼ %°‚`b€¸A8É@"Áï"à)„Š ‚Rˆ*^– ¨€Š+„–,!6Ð7ˆ.H ¼P¼ @%°‚ã@1AŒ*`AÆ‹¢ *`eóßϱ¶‚°ã@1Aà*`ó;r¬õ=)¶Þÿ÷ó¹/s¹ÿÞ§ÿ7«ôŸ-^l634›h€=š.^|è& ƳP¼ÐŒJ ACÆ€"à…ÆT+4g(24© Ø Qã@1aLS4n¼Ø¼@ ìÑÄ À4@ІN¦hê R4w"0CƒË¸¢Ñ“€%š]tÀ MŸ $hüP¼ €d b€„ $C (nE°„0@Ü$øs@‘Ä€"à±(Š ÂQ+ˆ'/ˆH©¿÷!i+Ä”,!(Ð7+H ®P¼ 2%°‚Ðâ@1Ap*`£??Eú»¾ŸÄ'Zà &KQtÀ ‚L–¥è€Ä™ $h (^ªXA¬q xA´J`áÆb ƒ€UÀ"Ž…T@ŸÂÏþظùϾKñïÆM5þ;ÿn…Fp`0üJÿ³L]({ürß9#²ˆû_õ¿ÿÔtW… b®PGfȇ¬©%î±³Qà Í2dFßÇ)ogމ"îgô67Åãò–0⾃ú: Ôyxi|˜›[uÈ_tõcë Lï¯i¢¥¶¼'¯Ü7š©wŠ$¶dÖ«z-ˆçK ù†]¾ÜÜ_Bðé²3æÑèë$ Î©Æú¤–,² Vû†¹]`š¼uçtÐÒëÑ-÷ÝøŽ¹Æª’bí">#‚ÿ’+2ÒãÍ('£O¯¹tøOÛ·9ˆ>x‚¿•u¢ô¶ðæ¨cð­»Àâåƒ.9jiÊÕâäkëFs.D?mé;[NíBB÷Õéʸ6}îÛ'ø\vgÜQ_G‹:Û÷TÞú¬\5œ¸`~vâörçôáýµt\)å'/v,ky¥&‹¢iÞ‘ ý'{„ó£y~÷5âþ<ªð~ «¼«Bû÷U"i`‡JN}ö¢OÍ/ÌóúèÄš¦ô0-óÎYô“ã¨Îõ©ÔíóÑ߸ãy‚Ý NÃ]:×vŸ3éÃÙ¬i‡0UÖÛ¯;ÎÓÒ´ Šó‡Çy³Ç©—vÕ"çuº~5+ŒºèúOšÛ®KyTæU\+î7T‘Ý´©·¿Ñ¾"GÁ‡j ã¾TÝ ÎÃz­LÒµ[x¨IPó¸ßáX¥5ZÒ &ÏÒ‡y~ëò¨Çê(ª1Lï¼Æs}EÃ!¢_ß}GŸº·]?„½sÌݽÑ;¨R¾ù ¨#ø;eÒwºâµ°€åF¶›ÿÍn-}¯·mºã-ú¸D}­¸Ÿ,Ï»ãºäŸ?÷|zD?(Ô©=$|ÈÓ ™4úÝ˰Žêñ[ç×ÒŒgmmêêd,}QZd'ßÚïwÒE2z& þ®]Ï©åu¦L ÏìÕIÐ ^×u‡&Ͼ “Ü ÁB‚¯|®–¼R¶îp(?†nüùŒÄQAÍfE¯kì1çS3ž£ÆuoÐC~¡ÔQƒ†¿gãJÍÏþXÀbÛ4ð™rEK»¨F#~é‚¿Òd1‡^&æûý„ùOî›_r¼’ Î»¨ˆÂ›™™´JoÃÛô"ÛݲGƒ+´ÔdäÑ·c6Ž`côvªµ"iE£m.…L¦1‡O~õ°Hʸï6÷_â¾8Bžf·R~ÿRÔ±óx«z°'“z·õÑx¸Èî]oq½Í+-™çTžµÙ“}~½ðÇÑTíóÊUR¦Šù³Ýþ‡/÷¹ŸmÉü2êÔžÜíÔð5™4*8ûÓÁYÙÚªºíú¤%©ô¯ƒu†‹}7ŸÏƵŽIKÎû}v¨ö«/1ÏÏåþíÝ[ö'î—(ød+P'åü‹¼;‹3é~›ˆUšŸ.2Û×å/„U¾MÖ¡Ö,ă½ÒÇoÔX@ï ô{jÌàylÜÿ•ûð~€¢Ÿº ¿ÔrR3)~«>Øã"{uZ¶ØµÖmªø‹úÄXóáì+õ‘uÓsÐÃWî.#Mˆû•ò¼þ~ññ^è'£®A¨ó"zôÜš›3©°¾ƒùºf—XÊôOO6ºM‡ìkéÉ qÁKÒæ),Þ6 ý~íY°GÍ+•Ft1úŠý?öÎ;*ªlYø˜Ñ1‹#f‚Á„‚Šª "TPrPÔQ‘ŒD›Ð€"؈&DDA7A1!9w££`ÆŒ ßî>§zšù¾¹sçÞ»Ö}ë­a­ZüwªÏ9Uµk×®S?ä˜0÷Éé0'OHõè&Î Œ£þf[þÒ¢TG„\Œk"ÍýwÙÙÇ ˜¹HAÄ¥èÜûÞM¾ÞÞÎaÃ9ãꫬµ9æ¿Ë”Ñu«phÁa~iL[·xå®Jè¾üVj¡v)˜ÕKqrÈràºv¦¦H dDi?Âp*ô%ë0ì¶Í¡ƒÞë¶Ç_°×™÷^—áìrybÿ¡zÝö¥!«€0œ¹JðTà¶:5‘õóuö ´AZõ‰šÏ‘‡‰…~ìœÂÌ“ó%›åRç6m2ƒ¢hÓ¼¦n‹9È "”,ë07‘Cõ<Úò:ðm©†·“O<_zÓL¹Äce—×·{'¾¡9¨4vŠOw " Õ½EYúäÃ3¼a>êùvÒÕÓ6Ýõ¬î¨|ÒÙ(zúÞµÕ Øøh®…a=üðøÓÈòàñú¢ŽžcÁ9×8÷ó-¼>ηÅ|\zþx.Õ#ÑnÛ«_>±)|0}¯îØ7wÚ;®úϹžî¿¶l®âOºJ:¿¾¼-o½/i©<~îÊ4ˆüÐÝ.fþTÀ¹¬¸?ÄõZš3%¤zzwû¥‡ò‡<Âð³ªaryŽîÝ~õge=!ï­5|J( <¸u?é㦗9Þ·T¹Î(1XgÜ"àâÏlþù9ØÒs‡e*š9êK~Q,Ê#bGk5,i¸aé›:øé`HjÞ9¿N…¿Áш$oÂpíô`€{ã÷= $s&1Å}ˆØ_èõßôèrïÊÞ<ÂÄ›èÕKÕoXE¨ÿ¬}ÐÆŸòÖÁi÷„7g™Ã§ºx…s ¡äàŽâ¶C}òªÑO0® ÿƒáÃhvxßþTÏSÑcÈÉ%~çc‡~s­ý¡:ÏÞ_Ñ~ÙµŽE[ûÓòæBسã‘>$u•ìñ›•€áˆŒ–Ì›Ç|¹Ò<>ÕÃøs.1° {:´žÐ]™âÖ:Ø®)Ѝ‹@üóGp‰ï¥ÀW×NûH8È™Å÷Œú—‰ó¡Å~Bõ|àõx¢æžK®z<³>&¾É°¾†(¬ZJæÃ4Å3“[vs%y-ÎÿÆùÌh¿8Ÿ9mÒsy…TÏÒaŸÕf¹D]溦kK ľ©&c_;;míaczMxÖ~ŽKü©7ô!˜1óöuþþßÿ›,SÙÌ™è9¶sä1¶Z®ŠÜÆXRµÂC*?˜@ÂåKmuñ\’l9UÖ#ÛGÂMDòRqm_5jA¶Gåí ƒ:8R_V±ºÜÞÒ6rÉÛñ½ÃÊ|$ó¹qÞ<î¤÷‘zÝMËJƒ7¨æàæx¨ÔÂ^Ó¤•ý´ê  ôNú…xSPiˆ*ê¶ŠKòåD S‚sBq>5þn\WÏäQ,Ç–ê‰x¸ð‰ý7B’®ŽQn] ƒå-:9)ÕÁƒhsÍë–€š·ˆ˜È%3½MƒžÉûœ»Šñ÷•ø>°ž!‡ý©žBw“SŠ >U|uÜÎZx[^³}iß:Hé|a«²9ˆq=Vadõƒ‘Ây?øŽv¥Opž=r´Ñ¯ÄþBõ¨$¼ÛÃ}{•0|ŸZPÜ`«mµ0\Mfí¦÷Ë [Ê —µ[ȪxäCÖŠÁÆÓ%Dœ',™ÇËÎ7gø(l}ŒêIöLk¿çp•¬Ù½í¸íÅZ8 A÷ℵÐéj]ÎÆ%–`Ü´‰faÄóÅ-{fùH¸öLEA²¯E{fîs 0vÈú Õól}üSnìŸiÔõ!½>÷ܘàÚµàÞ¾ãNäd+hÞ ¯Ø¹oɬÒsÝ ‚ë-òcÓû[̧ÑþÄþRÕÌqósˆ·(lÈÕAºZŸg[RkÙxd2â˜K>}pæí‚y0ú ³Në"ë&ü…êÉ02#C3‡pr—g¬Õ¯’¬º`¯Êì|d Ö©Û~ FDÔÌÖÉÈQ‘“䩨ëUÒ|½þ8Ó¼‚ôl™(³XéDU˜Ú-µ0#wçÓŠLšW¨•9™FÄx“V_¼÷A¿ÀßϼŸ{ºXÏ8µ¼tqß•ƒ¿¡z”ä{DMÌF5Ȇ×Aﳯu.µ0 )H4X]î.9xw¸d¿„sÿ‘…äÂྀá70ó‘ý©žƒ‡ºn0N¼LF>é½/çtØ$¤×ëšÖÂÍú)?™¦ˆ ÁîÇ| 3G{àœÌ_Ï‹ö.]WäS=¢©âúÝ/“÷o+ ¾^çD;éZˆÛØc†L{’i8Q;‚Øjd=Þßî#Y—}£”ùù“%ù+Ö˜8À¬s‹ý†êéTð¦¹nç%²&ˆw¿¨¹TŠ®[¦R O7î>§¾Ê B_…˜˜×†Qu¬j’¯„3‹u_Ü'aÜÄ:€ô|q!ÕÓTÞ#ܧï%ò|w´°¼G=œp¼g0°®|¬OýÂú$ß¾3=œ\ýìÛ’½ÂO?ÑÏñýc½&c‰ì¯çNdçN³œêfÎâ_º-09EÄcÞG×ìñ£T;}©Â[=?¶„µ‚šQWÂØúè&‚¼ä"0÷S¨‹vsÅ™|c ã7TOáìq« §g‘£7‡Ϫ‡ÄÚ×nó…5¾pýØ=Öp&¡ÜÖ°.ŒÔïLºìíGóöͼÿkl­˜åf*uàNs¨žŒi¢7w‘ˆ¢½…u=Ü›™›WµÎñÇ…3í!Ù×Ú,$0œè‹ ¾„yÏ  æåÈÇý,Ã;cööTÏý§¿ö.y‘lîŸ0¿bòS6O£zÚ³¾½¹œAFï!2ëásSeÄÙ™5µóöï™b ƒv<m8%’ï³7iÇh/‚y æ¯hghÈ“¾!Õ›ªryâ×tÒÂ7Ñ~÷c¼5X±[~T Ì}³Ð¨u9 U¸ß5Ø2’lZ7m™c¼AþÎ]gâ[¾Ä;Äð0þSÓÌ—¡w¤“üÉ·â»NkMŸ‡]k ¨Ú¹Eó‹ô)¹áÍŠH0}mëUu‚ñ¹óø¾;ìÏø ½®8Ì I'™§êÃÒè–vq×Õ°±¨{iáqs _+R71DŽ>'ôOÌÛ¤ýŸCõÛPª¥zè¹´Nؾ©ÌŸ&ÆýZ] Ç|MT5Í`m\Öˆ³œHb.*ø±ç~Êž<Ú/¾wäĈý„^ßë‰]x±ÜëuýcaR\©|UÖ¹°˜óÌ¥°kµ¼®ÒñH"È¿üúe‘/Ás<ôGf\ ¹´;æ?ËM§zöèw¡Ï;OîU½Û³EØ÷sÕ0Q´ _ 6K2º "Éð¹§ “e|%ÜŒø¾N¶Šäýˆýƒ^¶iòìËÎËam‹û|iº›<W ó\o]’³0‡~¶ÙéMi‘dÉ›§#ºœ÷a¹ÅÃ%y$ú¾æ=MìÀÏ¥z:ǧ»Yí9Gš6É.Qk„A5ßîTä2ŽÎ0K¸ãy6âóùHÂp|$ñ÷)XÁú9S×ß¡~!¤zÎvWí3µ×9’ÔRU¹¨8)xzýBß‹˜ó²g­™x)’ŒX:« Ü› Ç ëS˜áä[0\S¦î'SÛÌaÞg!;n†*­m„Ä«=ÕUƒçD…~Õ¶°øc'ãu"IĈ´S7—z¬[àùæå>»nKsÀ¨žw¦u‹û§ÞxQÝ»=‘7Ò¨Ã3£[L°ÑÕäfóˆ“,éMâb§=Ô2œȳÁ}&êcÖ±Ž\Õ3õü¸ ÊïS‰Ö‹Ï’áÈÏ9oú ¨á¥Ã—)ØÁšã›ãÊ£ydƒ_æMz™ÌæÿR<ð\ùxÞ€÷ƒqNì7TâÝ‚žó“Ï«íÊÂFا?ÿó›*X,£°³[ˆ-<în0rYŒ2{0óú`‚\Gä0ýž†ù,ÖMÅ~CõwUM\§}š¨_n{©±ÞݵØ>³¼ v67Œ½aL¾Ã#×6‰>}Öõ;†ïùéÒ>½~7q¡ñ$™cÜp÷ê›F(r¿È}žY΃ý¹Þ/¬!¬Ï±ãx$oæþÑ꡾’:î#~ßW€Eéóñ\ªG|lù:…<°Ú¹Ï¿—䕿𳕭/¾Õå§üb¹ÔÊ:Ô˨žŠ"pvyô:ýk®¹V|úéç*H¾·z£âS+Èz:Óli8¸Š^Ë@_"h(ç² ž7Œ~e>n)L2ŒyþDë+kg-º[ÊÞÕtª2›ä°¨â:=Æo¨¦%‘è+WgµÛ`Ý­¯×¨‚¬ c*ÞÛV€:·ÆêÝBi“?­]±ü·ûAî*rŒ;ÍÖ1©2} öTÞÅh-£g|òlá´®(ã&ÇnTUÚæz[(7òšÉ}I˜:µŸ¤ïy)~=[ÌþéØÔîqœŽª‡8?Ìû1;Ž­_`È“›¾©„ù釜qîÇ“¼ž$’Xý8ÿëâr?‚\Q¬û!7 ÷ÏÌyHêQbÿ¡zxŽsš’Xb‘ê{>V¯R/(~¿] Ÿ•Œ<=;9Ã}›«æ”G’Çwçk^ëKôs9k ¯uYf¥˜ŠýGt?‡ºtõxC¾G¯½ß9]Ïw®ÜÒ5±Ž…ó.“ pžé¹©1”G˜óJoÂì¿5ã$ú)êSÛé¨ýu­|!ÕÓÅš;áV Ý7 Hÿ–š»[ë] [«Ïkñ] o|^¥LVŒ"â6?oI}ƒášMŒg¸Oú³Ž³ç›õÍ?QšïN\»¯"ÏðùuôïçÑç@™†f“?è‚ëí1O»ÂÞ¾·Òb* Éä{¸ÙqK–“I¶>+ ¯„§ÊÔT$v†çZXç”æŽ+P=ÕýÎååÏ[µKÝ>W¡Ìí.Ió*ÀÝ+sšý pÍO{Ç“HR0÷Z¤ñQ‚ù²Ÿï½dÕ$çÎLßY•.r¨p"öª'Û¡îèíî~°mVìw%!Üë½÷ç¢ñðÖ¤ï“N:¶Ðþ*°WÅÉ9[¼§˜x<E®Ö9;ñ'9ÎmÖ}$ÂJë³ñ{ã7TþØT'ë; D ·L]UÁKš?LïSÁŠ3f™ÛCW÷ å~†Qä×]ñwNÞó"3;gYåX³ß˜ çö­„z¾|¹¿ß„ÍO˜>*ªç@ð÷YGõ÷Àà›Õ&Óx³aÂËCÏÊ!±KxÚ ¥ŸZÇëE‘nC; Þ¯à%9§Ýæù´x•Ÿ.͇¯EÿZ÷X·ìV£¬Þ·ºG3ŽÞÙ¸Þ°î&öª'u×®+AŠAÀÔm„°ióuϪ¢r¶þìŸm ܬ£HêöªUw?{ùçëB¦ÌŒ£Ì~í‘䜎Yô;ðˆs©ž¸Êu¦_΄‡¼ähS?]]¦ô<©*ï(\½Ì˜sÀ(rw]þ•)¼Øzô\Àúú?Ú5öÝHsá…T[Ÿ‡Àq«^í›&„†˜ø´k»Ê¡aíGköÀµžž”E¼"wÕt•÷&o»æ/<·†#Ù‡`|F?Bþ$cLÿLc3'?1T¹òA(™Ë=Oõè—­ûu°S9¤ïíÑX[fÃÄŽEvtÓpú{ä WÕp·¯ì?Irž‚÷…ïˆ7aöÍcÏ7±þ€çØ'&ÍWö§z|ß<97¡K<¸ [0ñ:‡ÚÛa‹q7[Ë ßûqùîÄå……îÑD»x·ùBoÂðÇK¸p¸ÇsHézŸ^¿Ò¢ۚÉ|H{,“7‚>/³4ňæŠ2ØØ¯í|¢Ê x4ö{ÙM¶£)ˆáq_™­¿ˆíg2fù°w%÷Ñ»yØJ×Ù“;Ôs©ž`¥cëWÙ%À-e‘JýÓÊn½žÌå2P~=«Ê¬Öªf ¹EBïÑyÑêCŽçh˜à:Êìs'w<ï¤zDtX#¯£pcwŒÉIjϯkfjWÇ–A–OŸÏ¥uvÐõÀ†WÃÞñÈÚr­Ë»| Ó®&9'B=xÎÒÁ_Íœ¾ÃVD¬O„eR.¦ëÜ”6•ÁãÃû3…_àØº¯Aűôý=ÿËãöRà=㎠è޶͌ˤùÞhÕ‹/6{Üo˜ì3A$Ï÷Ò~íO¯?,¨DvZj2Ô´>ò€>—ï6Gf<++…C:^‹öphí7ç°(²uÓ/·ü/Ðø¾yÒU×sûÑNq½göÉzâ.Ÿêy|n¥37;VŸÈÙ¤%„÷ ² N•ÂbwÜyAN°¤Ùƒ™QäXr‘âƒÇžã Ú擨û§¤×ù\ªgYÖ#eµ''`YU¡ðñBp3\²²qO)ˆºÈ——:Hòýïß°[îIбž€zÄvO¯7BTŸy t‹à( acØæË—J!`²æÖÜöpüàK¯%vQ¤mø‹¬&I~‚Ï/ÆWì+•î¿“6sz‘ò{±Á§aPyë„ÓÃéóy©¥«mX ‘©‹^ŸÙ¯TóÊ5Ë(ÂÄ9‚|P<Åó$<Ã~Pä[‹ý€êù%}DKBæhûô*o-Í[£_:ÿT ýwŸ·ØN;ü$ /ð`ózMÀþ_Ôã('òø™€ýb ×)x9+àF*ìo‰÷ ýQëŸì½¡Õ³\2‡­ÖµµFÝ/…hðÃQõ ¸®aŸº„'Î~7 öz]&ß? âÛ¥¿{ÐÅ^Žüg÷á]ÿ)SSÇØBÓë6»®FqûiOrc\µˆŸó¬ƒ2ùG©¤†©0qΟêIJqû§Ü4hwÞ×ïÕÃ[µãÒíûðÃc³*£&;PÜ# Ó‡v €ûSô7\ïò¯ÆîÞ>™=ÇfâŸê±5)ê¡{^ѧµ>§ô†cóO݇]¯G-á&–ÃTr"Iþ×ÁS,º{ÌÓ™¾›ßâ†Øîéõ:‹lÎCôÃÁñrBødÕanà}P_.w¿×*Gè¯WÑR!Ã#f—òL÷zfÁ~àé’ëáúŒý®Ìúɬ7Bª§¶yr±æ¤ 0ÖºH9k€æøÈé;Þ‡ôuWL†;Á£M‹³íxäáQr‘G½$õ›8uæÀ=É9.ڑ𲣑ïf»®±u¯¦fŽÒ4Kíyo/€ÿÁWBº/|üCF'Í™÷¡êÝœæ„ΠeÒ¹¼ü<Í7ä½Î\YäE°Ÿ ë›X_E}Xweò'Ö¨žÍ€z±é¾ñÚò-½„p)[^nÈ€ûpUã º<Ï|V½Èüî ÂÔƒ-¬êµ ’& \ ø¬·\sJ„$¾c_%ž—à9$žã‰ýDô|ÄçËÙ0ÙAk\Ý^ØoZ6ÁðÁ]˜üê¶Ý–ñ®0æÕˆbÓÁäâkõᕞß3æ{臘`_‹t_ŸêIäˆ:2³Ad5)À]›íô9¼õ.„&µk´=] þ´V|N¬r»®o¸ã!©ÿá÷Џ¾âú„ç7Òû£\ªÇlVAyÔÌÈ)®=îk-Ußð¡rwÁä–ñ•d÷5 ø¤Lý³A»ÿ£’|7æ;øýÖç±n#öªG÷®–×›9 Zš´}žö—^9žº:JÚA‡WÃõO±¦þäòÛk™þn÷h¿ØG‡}¡Lž€ß÷ˆýäa3çá:ÑW Tô¹‡úãLçK+ŒïÀ•;ÞÂÅ«àæínL g3>žP×rc¿5¦/Ø­k?—ô!2ñk1Œ>zäµ»†ŒŸP=:e&û\>_G£i&$æûÕÛ°Vø-ùþ!ãõ¡¡<œÌ‹öÅë&©Óã÷Aøþñü‰É£µØ}S/áP=¢®2ËW᲋(/€ãÃÝ·®Ž½ ×ß´õ¨;èÄæçáDßnÓûn÷Ø_‚þŽûKìÛC{û ÕãEŸôUø1¨çš¶ T(ô“émPê½@ÙGÉzŒq,T 'zãcgŒÉp“ì/{Ó’ô¡a<ú*ž«‰ý‡êaú ” Ñ¿ûå{#,úeQ­KÏÛ0"yJ¥±µ¬ë”ð¾Õ%œ0mn’¼¿Óżó<üî˜ñö’ê‘¿ÇOؾ“À¤µŽ;ζ4óÝì-°7w4¨×sOåñê<à ӿëFb¿[ŒœQªÆžgO”œ`^ÑûæÍkgvjwø~+—êIª™Ýw×'W¾]r-j„fóROo»Þku—šu€K‚Õ•pÂô#ºÜwc|Æ÷ý¢X·‘>·R=&¶gªçÂ÷(™¯KO6‚EªµÜƒi·à×sGŽw_'=Óãad·±èË47ÉwHÌ:?‹Ÿ-’óìO–®ÏÉüÚÌI=Ô¸%:”«¨å5‚—ŠæŒþ/Šaô¸¸ôÁ|Gh9ªøp±z9·÷jh›$¾áùjÇ>4¡.Sž,©‹ý‡ê™8ÓjÍA.˜'Zt‰ hÅiß¶U&Ïðƒ^T7'ð7XpIŽÕ®æ—ÜØ¾‡q’óf|ÿØ'Èì34%yبž“Æ{ÎíQÈpã©ô÷n„'ñ%F=Í‹á^´ê>Ÿ0'xê’k)à«:¹!k6æûS-ɹ~G!ù>QœoLËö-+ÒÌT€/Ãüý= êïYPÿ9)Gd˜?Ñ=–ˆÞ5D¾hî]g†-=—]–UÄrÿÑ|ÏÿÄ<( v.û?âåü'ñ¯ò±¥9‰ÈÇÎ¥¢*5?ÅòøØ¹TT©#q©´Q±§UDEƒ:U ëX®TJÆüÆÇ–æ$þ»sÙ¥y9B–C¥mRGN¢ô•”ßñ(p&rbÿl6;2søÿ+™9|ÖÿŽÇÀÿV ”eï¡Zô.¨!¦Péϲb¥g‹fâù²3ñþd&Þ‚“­ÍÎ8þGlŠ?c†ýUV¬43 Y±ET4¨Ãp©´Q±ÿVl êH1¬3¹R)¡¢MŠOE–:–•j)V¬43ìßq,ͦhaY±|ÖA¥™aþTZ¨˜²³¤¤ç»#+™‰6çù)ÿ7 ù¸ÿoÇ¿ÿÛñOd;|*²ÔݨTSá°¼Xœ,š›Ç¡F™Â¦é?˜§J•K¥Š=5Ú"*ÔpcXãu¥RBE›1Ÿ5dW*%T´©Aó©È²³óþˆcóOpÇþ*+Vš;†¬Ø6*öRó@¹ÀŠm£bO¨ˆŠu¢Ö‘\©”PѦŧ"+ÅŠEþ˜ˆiñïÎA–fZȳ¬Ø*YdÒó@…¿ã%ZRÉ¥¢J—ûOÌCF¾Eõ_d’Ió-Dv'úûßûþÎÿþïÇ?yö7 EÏšby–+=Y¾+ÃË®fÙd\*mTì©¡QÑ ÆÃì‚—Íag"ÿ#ÆÅŸ±Éþ*3VšM†ÌØ*ÚÔabX§qýfl mêH|*²Ô™Ü¨TSáP§J¡ÒŸ:–/¡3VšMöïÎD–f\´²ÌØ*²¿c“Ri¥bI6ëw3á‘™LÆ?›ï‰œ‹´O†œ ¾ ó÷w ü;þ·b •*ý©úRR1ff8;YHÅXÄ¿ "+Ú÷RÉ¥¢J”K¥Š=5Ö"*Ô`cX£u¥RBE›/ŸŠ,5`7*ÕT8ÔS¨ÈRcv£RM…C:…JjØnÂÂàÿ<³¿ÊŸ•fš!Vä,®TЍhP§‰ù­È‰\©”PѦÎħ"KÊJ5u¬*ýY­4ÛL^åߟ£,ÍÃ@¶Y5íßñͲ¨(Pg dçÂKóí©QÑ óOÌSF.Æ_åœIs1ø2Ìßß1ðïøßŠªìoh=KjˆYTXFm Sj”YTº2ìm!Ë:‹aÔ•J mj¬|*²ÿ!þ¶15î”?acüëì¯2j¥YgȨ­þöκ‰:oÃE„M$¨h€³T$^ áf#¿D­·`äfz£)Ð%ˆ`DňÂF¤%EÄ ± Px!Eq¦@Ûq ²@J[Œ,µA¼„]‘TAöÌü³)ç€Â~Ÿºß'ç<36yŸ&ÞpÒ¦#PAÛE6jƒ€ƒH^ †LvFHåiׄ6j·ÎLÍ´xAœ ÌO:ÈçQ`„"ÐCD·"£õ‚mŒ¨²Qëê ¶Î\ ,V¸ Sžms³­Gè ² DB‹@©Ý =ïþ+Ø;c=ïRn¥?÷ ü-œ}—:÷þ¶1þÓóŽuÒ9—¸‰ÁÎ6G’|¦%žg¿ÄY&½&> A˜ L•h,fAÀD GÈÜJЬ œ¨:áó5Î.;#Î.P#”vF„Ó4¨ý'v0.µgv9{´‰[flV… Û@Ú_|“Vl 8ˆàjÈ`!`„> é$ïÒ&î˜i!‰„ ²øÂ8A˜!ŽtÇ¢À‰D OØ¿6ÌB ûeÐ%ì0Z!W X˜q @Ù\ ,NLع¸Ü½²Ä O’üã÷û²ßïË~­û2ƒrˆô\!ˆÐ)´`F(…Æò¾¶´ÑV6Ë<@…Ú@p«¨U?gÛy‰­mÂíjÜBÀˆ û€awüŒÍ²ËÝ MÜ,c´!`„0^ †4ö‹lІ€"ù€29@˜ •h!–D6h7ËÌM:çQ`x"ÐC>·" €"z€ 2Ú@ ¥GSÚ õÍ›enEV+Ò:ADÙßNÜl"»™­ GÛªìÐ^înÛÛ‰íå%ýzg ^¶ßÜù—xö]êÜû-yÿ[çÜÿÄ'=Ç~éµFÈœ Ì›h8'ˆ3‚çj„Ï À€z€ A´ àH/P#”vF„Ó4¨„ AÕ ¨&Ö´­‡ðz¶ƒ0"ÈÞŸØ.»ÜÚÄí2¶A«†AÀ]dV 1ì ŒÄ4ÄÂÀYü Û³‰›e:Èã`†DÐA$ˆ „R¹±¬ Ì­HfUvË8ý¿÷ʤÝYè! D¶¥-m"z@X ¤ôÒ­ˆizÈ龂½2éoþI«žßïã~¿KúõïãŒÊcD¥çA^Ùž B)6–·¶¥}ÆàP/P#¤vF„Õw™{Û®Kìm›n?Ð à&Ý´»„€¡÷ ‚ïa`‚þ+Øž $¶gÃÀa|@iÙž Dò-dr‚0C*è – Doý÷ö¬’¹Ñ,@zçV¤³‚0@>PA@"z2ÚApÒ TÊö¬´ÔÂÀQ=@Ym tÖ¢Êövâfc ²¨ ³ ©½@±m?±£è*oAÀA|/šRíWx÷KœWúûkÿMçÞ/qæ]ì¼»Üß{ûOÎ8鹤×!s(° lÐ!p.OþNÂèj„ÐBÀˆ0ú€t€00!˜~ E8 Ì8×ü@‹°:A˜ZèÜ0"¼> A€ L²A¶ƒ0"Ð> A¨W°=+‚ïá„ýY $°ƒ0^dÖ4ÃÂÀIü@ Qœ ÌFHØŸJÛÚ8ÓDé3*$ŠJgD2¹¡¬ ËTË‚€ƒd ÂYfeÖ ÂÀù|@À Ý ÌQþì„ £"¥€rz€ ‚Ú@ jX «ôÖ­Hk`€¼ ‚À6Döþ~÷›9Ïþ?ßÇ™•_#…Ð À€0º•@ZA ±¼³íQ`D@}@ƒ:@˜Vÿenm»/±µmÐ"àNf]:„ÝÂÀ„ÐûÁw‚0Ch!D€2@!\ ,CzÈáV±‚ÐAˆ3„ñ-¤q‚0Cè´øu ÌI:ÈäQ`T"ÐC,·"—€’y€ ¢YA œ¨ ù¼@ í ŒÑ4ÑBÀ)}@ 1m@:ê`†¨^ †¬vzHëVÄ•¶·ý@ 8ˆìjÈl!`„Ô> †Øvî*HnAÀAv/PCx;#Ä÷©©ËArN•ø58ï‘þμÿKç]âYçHúåïߤ¯U”^/„Ë­Lz„Ì­Í*ý=0œe!`Dè|@ƒà9@˜@?Ð"„Nf„Q:œc.œcÐ!¤.„U”¾Ç€À† ¡õ-‚ë`F€}@ƒ;@˜f?Ð"ÐN&Û´·D€!€Aw(° ð"Ð#ônf„_‹ð;@˜ h!‚D€Y‹´Â "À 9 ƒ .ˆ"=Î/·r£` ‡º7mÿtžõJ×qà:½N]Y3¯”^;¼jw]ÚQjÒc­¥à­=ôí¶´ü훳éëþ*ñeSg¬Øs:/÷´Ýïcf½H¬W„õÏ5Ø™Áu: ÒràÆRÚÞêÆÖÓ{¥’wxò‡R'Í92â¥lZªJ?¯]\Ä·hû}g¿y:ÏúOØnëyb½R¬Wî’{8\ç騠U)|ãÆ`ÖÍGép;õ“¹-÷Ð4k'ÕÖú,º.¯ÝÓÅ[‹ø§ú|[óæ]Óy¶ïÊz¬YÏë9g{GÒã‡ðøy±Y¯$ïN%zaè¢a¥"m^ß÷îëÿ˜E_œ¬¥¿¤~ð§kòl<ëYf{Cìùb?³ž¹÷û9µuÜ?ö[˜‘¶“~küݽëjȹê|û÷g‰4wèÚô”IÛ–î±õYUÄ—”­,Éçå>Ò;ˆí ±ÖËÃúÝwG´¸NÅAqÓÛ wÒăuñUÔÐó…]½SDjá|gŸ‚ R7žðÃU³‹x¯íêõ7Ü;™g½ïl‡Cî{=Üõç³ÞºÄ~q×Y!Õ‘ÞI7~¦÷úÖêcñЉ ê1£ú¹Cí3(V/>¸HÙÏãY¯3ëë“ûžŽõû&õi¿iåQeŸéž=ó\'YŠQÇ]t®Ç³Q¡°†š|¤yþë”òì!ëÑ3hUÝÈvO¸x³8¾Mz»©¼¼ÓrO¼gþÂ=H¹¯v@¼G'æ ®“ò€îš‡ÞEÚûºή¡jqþÓ7[*èõf'§\µ%ƒnž¾èõ–\üÝ­n_¼??¾7ÊúÙóÆv?k‘—ʾ—^®=Ùýì:¹ïŃë\;·pÌ;kvQ£)7%çÕP}+>³ÅM´¾´gó­²äÿÝë\üÐäÒ ³ëóy¶û÷ÁšncîïÉa?Ë}%©ÄúÒbÞà:ÑRÃÊ.Ú³A•Úft Mœµl£¾¢œ²N-ß9´I}RÞjÊ©Û]üêÍÚÌ=óyy—©{|Gõ^²~”Ûzx×€-½ˆí+ÅüÁu&æüqYÿÝÔvõÌô^ýkhþ ±é´r’÷si|¬€×Å;tînM¦Lçšíœ±Þ,Ö÷Âú€ûÅ’>¯ãfýøÕÖ‡ÛM½cÒ5´}MÙ£O¶,§Íï<µbž•Þ¸ Ñu#\ü^Së?è<5Þ¯ÉÎ[Ö'({Š÷S&öäjqVµ×Œ5 »IÿÚÜ]×Ü\C›ÇöK­ÜXF/½Ò7ЬyÖŒzæÅ|ß¼wÇG¦ÆwA§eÓnûs/å<ø"Þ›µwýŠŒÉÃú6عãpþ­^ÐÌÒ”QðÙ!§N%×Іf[›qÆ2r ™×œ^´’ÍmuÍ]çâcõù“¦ðl—‡õà°^iÖŸ•wïÚ¯÷v5PÇ«®}mèGJÏ ®³=¯GÚÀô2Z™yP»ï«jâb »éowæ´9fÎUúì\|·wÕÇ=ÏåñlŸ‹Ãìüd=s¬/4ñysà:y3^­zî‰2jÞHØ’ûq5µþxÑÌÎ…»ié^‡¹ª$‡Fü%íã÷¶¸øýíSÖìhåYï;¿Xï;GÙÞ{¿ˆùƒëàqä~JäôiXºšôݧƵÙEÎÁÇjOŒÏ WîÛ:ðêB~»P»eR¶…õK¶Í¢µÏt8èû4‹î 6üú9<~·[;_¿­Y9›Ýóà‹õUÄwmO—;É’í.v~³ûGÖ;›Ø*à:ƒ]C·¬ž^N³ÖÝ;²OnÉïǸ4Oè[“’C“§Ü§¿¶krŸ™Ç³>vÖÛÈÎ9¶‘Ñ}´þŽõ=ˆí±ÇrŽëœØ¶¹qÝÂrºû¥A7 ª¢%]‹:¸” _hi>ú}íß4`ïÈ ¼Ü'h÷ƒ³Ý5ÖÛÇz»Ø~Yâ>eR¸Ž)Æ;Z9¥æ—ç9諸*w|øÃׂ²Û0‰ìïã4§€?úXóEçîšß%d=¹ìñYOÛá’ßçåû9-®óÔ„ùçm\A±/÷ú*šûìnê³T f%gZ«ÏçÐ_½õwÔ øw?0Z?,ÉæYÿ8»¼p/†õá%öžs¸ŽÔ^;³m´„JΡºîGFN!œK|Ž?ååШ؀t!?èÕ´­k³yÖsÇîçY¯&ËÛJ|·à:½;umç»*h#×¥iËGˆ¯ìïÈ=ÄÓÞ¤s’Þ̦›lÚ¹x]—þüÖmÙñ^MÖ«ÊÎo¶ï'®éÕà~Þë¨6ýãêiôZ«©ãÏo:BVs]Ož:l»zÞç8/òÇ<µéÝÌE¼¼K•ß÷eŸØç#¹7¾Wƒ>w_Þ?¬ ‰C't/,:B½vï[þò4°O~¸ÔEŸßºäõ#…|I›? uÉà£Ó{3çb÷Ól‡„õ÷±Þu¶gó×™l¨ .÷éB¶#tƒÞ–Òáü*¹¾¨éø³™tó´’ï· )äåž¼ åýa¸²CqŸrnÙï½¢öw®xû´ò| "¶¿ó×ynôÊV-Y ×‡!oMúäI;èþÜ‘þeÑM±Á·Bþ ©VóÆl¥ßxÉçÎ(2¸Ž{o»Š w|´Ùä³ýäóm°ìˉ:N~ߨ ‡êsô÷ßz„ê r|îŠ÷IîWϦÏœá°ò_þzÑ™ 9Êž†‰ä]šh̹¤±ëÇ4!O§9O\ºßÆooÔ-Y~|-ß™5¡úþÆ"Í~à¶^o7:B_ϱ-ëÚá}Þnœ\Ì¡¼݇Ön)à·KŸÖbjÏþ{ÖûÍúÐåžæïâût‰{ ®ÓöÌç©Ã{Š$÷WRÖ?÷ÝÖnò{´x¦aûÐÇr)ùûkúf~»P¹ŸÎŠe~³~[vn²×A¹!—=Áu¤›—G‰ÔsDpN÷òJš³~UjëõÛiïˆÇÛL.±Òà ßœ-¾º€uúÎ-?ÎäY¯¡¼K¤#¶CÊzBÙûV⮳×9¼•Ž=(Òò½Å¾Š+itlH¡„Ö=`N«KÉ£ìÅEoùÖð™•û:®Ÿ—©ìG÷ éî+gr÷øç¶¯Åî«ôÑâ:ú`‹ªsDZÕèô¤¿=YIò}i )˜¹o•¥}s윿@ÙóËTv‚úRÉ‚²iÍîìE¬·•õBÊ=•\ƒÏÙ®#ïç‰tÖ;ÿÜJs%UþqJŠÙO¢ó\Ü'—ž˜»{Ö™¤¾Ué™ÊPö¶ÓiåSµýg/Nl·çѹEÿbï< šÊ¶>Ž=vKÆGDÅØ²c ˆJèÅ‚5c‘&ꨱ£¨`%* Ö$€…ª±T@E+öoßÜ{î |¾qÖ{ó½7ï{ÃZ¿5k9kÝCr÷Ÿsö9ìÿË¢6úïôL¼1¾0§´°÷Á…:ۻ݂U¥¾ï‹…®ÉÜ0Ðî›t{?&˜Œ;p«Òœ$G·tY±Êªµëx¸‡(ÄýzÛQ—ëCY"‘0íŸWØÝWtó4_ʸ ¯k¡õC»Ž9ÕoK·¦dDåX¸Ühy³_­\áR½½c ½‚‰Ö‘1wâúPá|uFÁ¯ÅöÕŠ+±û(˜¸mR`÷#úWôƒã´²ß…dj9>œéø&}ôœS°zù…-ýæ;CMyЦBç"÷k›º]ùxã}ð84cÿ39Ž3qSå A2¼Ý|oÃêU:hŸ¿pñ9 ÓÌ "¾8ÁOŒ=[½`²û­c»ú.¤¥Y‡«y³ÅÀêÉš÷Á£qMý8Œý*•8NLÊHïÏ“aÞ6EôGÄ'Û(ª=F»—ص,‡Ò½fÕ÷o]CœjÛ6ñiàÂíG$@×lœßãý*éºÆØß#ÇIæ|ÿáÈd8¹¯ÎË…V:¸[!$@èO_¨[­º3X2۬ƫI¤/ãÈ,'¬ŸÞ8Î×Á¨Ÿ]Gô‚ϵ«¹aúüd8õV2~UUŒfÒ‹îDÞwsÜÂÖ,ñùõ®Uäôôy×ÆÌžL:GOžµí€˜(hYÇ…ïkO×MÔïÎØ_Oãøv¯óæ¬:*D.(Yz÷&ÜH^Tsà è88Ç»`¼+L¨5±šôî*®®±ØZM­ì¼9Œï×Z¶/µÐ>ò½<Ë—¶0,T’Á2»Ê¹+ê›°ªfÖùÇÁ%wpØen0úôų“V¶ñ$ÞÇö=¦uv½aÔ?Ø |~\ÕSÃÕâ`ëŸ7AÛ³­Ï‰Ç`¢•¾éû9îp´î³IÎDù³C} _‡ uºO¡ë}Ú¯¿ŒŽcfø…R`Æ!ÏÅN7aºnß§I£ŽÁÁ‡IyÀ¸Oê5<D¶V¹Ý|l-9¡¾tD×c´6­SôÏ·Åd¦Ø”fËÇ¿yÙç&l<øe¬í65Øš$j×÷ò€AÝÆí¶†°¾ rÞ÷œîÛé:‰ú3Ðõ¾±Ï®Çi«ÚÕtk ŒÒD¯3Ü„ÊîÕ[ÝHÓÛ¼sô€ëï;„VŠþ…ìLŸÐvÝ9¡õ9ši}ÐkT¿f3Ó_ñ>‘eôãìzW¥ãêÛ÷ °6 é„â(¼3¹·¹ÛwXÞPxüÌ*òuþÌÛÁ™rÂêé'¨ºŒ¨:;ŒçÞËS ë«öïÓË~N6iphñ¡h~o‘­b ¾¡]Â&Lk|RêV°Ülí… Uuž¹š°õGgòósy‚¤Â88!e*Gc¹Ïó„ë?ÿHÂΓ6@} zÁq|V.Ý?5ãlàA‹©î7 Åå°28å¼ÿrÎé¬WŸ $Kgeîú,s&…Ví>Ÿo á}:èþ‚®/é>ž—ØqL ó¥íÍ]“5c›·íwÌ~¨åf¹æ ´ÎÍgmâýu&Ô¾L*=qÉÞJNjÜ~T<¬½5_¢ë&:^—™ÎU±.3_Šp¶ Ôª6าú ˜ü£eòÙ~=þBѧ»åÒ¯d{gQÌTB}©ß9í£M}PézÆØ×[ŠãØíó¨S1vÔ~ïYµ$®oë²m_õ(ÙéúUÙ5Wèö!]‘r7˜·(Ú “º/¦úcýÚòxÿ>ê‡a¼”ã8N§vœR9¶µbŒÛ²aAÓÀÛ”ýÀ¸BÝíûà7A¤}ë dˆ i³³Ðc ‡+®Wµ¢ýaYìcí,ßb‰Cž`ÞÏížK¨­Gôƒãt_umZaýTØRW0¯ÝŒlx÷ÆZj¿x8X<ìðÆ×ìû¾‘­¸H®dÜ××t%l> a£^¶Yš8œë¯ÿ’÷ïc×£}úôƒã„VYÕËTè|e@ŸH‡lx.÷ÀÐ&{¡Š™¹cÅàÜ´Ä­g·@Âþþ΄Æ-õaëÏ$ÖoµÜ{<Ÿïn\oÐà8µŽo86•óïˆ^{n¬:´¶sè×h'¬¸;Xµgr 9öKÐÝÌì)¼<»/6çëßì~"GB} éº× '2ÕÅåÕìT°5LèÙÐíÞ³½,wÃ8ϯ£z)¼`}ÞZïM‚8BG¾IýIéþÎ˽<Ï—>©|îðšT͸”ö4 6~]66bÈ.Ƚ?¤Á^W/`ªH“Ò‚¸}ëdBýj& ‡¬xêãyr¬àQý¡V'øüÍ!×0#¦‚ÅKó'[“³à]æíµ³†o‡=g{ÝYè ¬ol0ñšZçì áTBûŒ³¾9t}ôRâØxÆ <åC õ× u ƒNpœd]fe©Ð¼“A²`E‰ßׄ[àX¥C¶I<€é Ÿ‘D~œ{Ʋw~ó·ï‡úB²ßS±„úê÷O—ã8;v劋}RabzË­þs²`}ïýêÛÚ­…äóÜ€­;êWJ÷ánÍ•ä¬{ÎÇ­Aø¼í>L„¦BêÏ-|îXeÁ²M¥Ï{ŒþtvA l¶ºãB¿×'ˆ<1}í}…¡ujz¾Èî#^JÆä gºÿøXBÏ;h|ô€ã|.ôÁ<†; z½øš Ú }·®5]}â#¬}\á©ß©û§ ²}šjwÉ 9¡ónÙóÍÛ’_Î8\]©È“Ðs úÿ zÀq®é_4>Ñ6\Uf1É9™ðâR^@«ûk›§ ø^^ÈÌ¿óS"š:Û9š¿i½Šî#i‡ú×Cô8Ž(¦Å…‘ PwûOzO΄z›3žt9â`ñO7åp5š1 !-+.i=Æ…°ç&ø})õ3)ï›Ãæãºì|R”/íüëÈÑÏk¥Â×|+§©ÊLÐD®¨zØc5´ts°*ê çìÛYew0 =üÉZØÆ…Pßê¿ÃÆ“†÷0žçEøü!mçWÜ*H…<ß.ï’í2Áûæµþ9Ÿ—Ã1Y\ÐÏÎðuãÆvûƒHÏqÁ¯zî–suªÀê@È×÷¨Ÿ%õÝ2ö—”â8¥/¯íÆÏÁœŽíh— êÂ{ £´‹!Åý¹É–|¨¹èvNtr ¹ÑdøóÁ1Sø÷Ï®ƒšÂÓ«OØó”[¯>äëòƾàrghÅÚ6ó[¤Â­)á{53á²Ó^çæ‚Á>å¶+TÏí»]3/4«„!àHèz¤ÈñˆÙæ‡m¹úî3Þÿæã8Sâ8±Yý§Öm— oÓtÚõ8>Þ/¾Òð…3ÜWkkÜÊrƒ³Úí¯y,óu$ôsÐ:aÙ8ËáÏgØ<Æî¿Ãqœº†‰2ßY0ôr°ï«鿹a—³WÜ!ãS×#£\ƒÈâ?;÷ÐBã”Æ]Ó};ÿw-s/@ƒãø2ò0M…sŒ,Ý—5_Ì©÷`7ÙÐL}(ù'è?8;zž$˜dÕnà×~×ÔÿuD÷CtŸÇÖ™Åeüöô8ÎŒ¤¬)·ª`<¯·_µ"\xTÿõ²³ýÎUVzÀ×VŸßÎדÃGtN¾c¦òq@ëôû¢ó[W]Ýò½Ý„;Gy‘/}~sGQß’ÈÍ'Õ¢§d@áÓ]%¡;~&Ù›M&Önî=äa¯½†Ž‘ýÚæ'L%Ô…ÖWÙ:ò#~=AëZô|Ò ÇíÙ»¦¦ÀÆaµg˜Ød@hç3‡jW^M¼‚¹µ‹;th}©ã}m09»ÌrÐj3¡çì|Ö ²? ¸µoâkɨc1;L²Šøz¢±_šÇa÷‰¸iÞ¬pT­ ˆŒq°;*˜Ü yh¹NíÚ%‡æçÎ &_a´Êj«;?ÿÒyŠÝÏ¿–ÐsNº/ û)ƒ~pœQŸ­Äõø±ó^ßO‡ŒlOa@tauç ¯véütC0©Î,ÇëyñþãÔ¯…Æ»Ï/à÷ùôÜØ Ç¢VJ÷­“S`Y—UÕ®M‡bû¤‘Ÿo ¯Ïwj?¤+T*òÿcBØsrO~£ñLçz~K}§ ºÁ狇OÝá*M.V¼¿²A7ÅùÒ]W^Ïžºå÷›4I‡ñ·š/™¹ƒ4ßÙ`»ú„'t‹²×ûH:®¹rðàé„ú[ÑûMÔ?{ý‹#U.=’°çª=Ö1 ºÁqXÿÎ’!Ûõo„“ ±?î ¸9 Ñl yoÅÛ‡Ðsz?„îûhÐû4 ºÁq¦_‹ï¶{X 4Û¸-jßé4¶\Q¥ÁÓÝ$m”Ý£¯ƒ¦AÏŒýO[¬!o^íǨ_¥÷šh]î“èù"Õ¯A78΀籾{ÌSÀe•£dýú4pN¬g¾/‚T{49Küx0Õ7Ó¤@bQä·áÁWBó=õ£¥çÚ´¾LëÁÆþ•J‡©ÊûÕL§bïÑSÝÒàEû^,‘ï#NïuOª4 ܪ27‚I}Ýöž\ =·¥ûêDçz®d|þŽãÄÏ›Ûÿy2t½ød„8 ~yß7¬j…H’³©û³®6Þ0Á‚qÒ &wÆVóï§rãöݸz…5ïCFëLt]Nï«ôƒãÌ*îì—ž õ#ò©ŸÃgNÍ[µ*Šóö¿b³ji/‚ÈšA»Ýïaïçtzÿž£S½Ð{yÝàóû‹þËv'CØè)ï-¯_‡eDæœäZ±zkiŒè=J\œ‚‰È?ßö—ƒ^„õyjÊû²Ñó1þwNËÆwO¬$_zçýRŸsó’¹úÙuèðÉuZR‹CäÅÖ!þ4r…a¦G•ú…¿Žúùc¦ñëV‡Mùúõïeçéz¬^ðùÛí É`߲ÚØþiC“m‹ë¬6éãÇï)ìü8˜óŸAh|²ù³1ÿ>h½Œž›•ñÄqüÌj.œ ÚZã.“ë× gŸ#6“¼+sÚøÆ~r†N]Þïút/ˆ,Ï¿©mÒ ~ýLß3Õ#Ý'ÑùÛ |>n"Zžî‘ Ûšfšmò»É VþØ7ð(™¼B°¼Þ`gsø€e ’ÓVüæçG3øõõE¤ë2ö^àC õC6öiSâ8½J²Cs›%ÃóÚŒ£è50wýÇÞ1Ñ„õAs……#w¿Âýꮾ–Y[ófºþ¦¾eì<óBBï¡Q_;zNkÐ Ž³mÒ©«'Ctƽù6®Baæ¦uüÕ„×Ý@xüLöÜŒ \Gvñ Bó>}/t¡°=&Z‘ú„¿‡h쟦Áq6'm]׿H fEý½ Ù]›i[#ñCdúïu‡ÇÌL0a×=Ó =Ç¡ã±órž$,S¨ïZÆgRÏOh›´##^Ëùg_…–5}½>g# ª«jô¹á•C+7™þ4˜è2¢OÍÓëÇÞ–?û)â]×ðõ1:ïçc“—ùÒœgÓ¦ø¬ÔsÊYoìU~~×'ø8ã0ê¦ÙO¨äØ·–ÁdÏf;ËØBõAuGïé±ñœ+aç·|]Ë 'ô(óB´`°7ìxbwåÌz‚,ÆYã¥'<ÚÅl„‚‰ë‹õëZL#TïÔ·‘®cè:Ó |nŽŒój4üº`Ъ§W`ð—11Εbȸw«%~ô€<à V0¹Ölý$‹-Óy¿Lú>YÙÇÜ:ù‰ÄØUŽÏgÜ釆$ááŽ+°~cÄCÞønüz»¦dv©7kDz¿éƒîƒé½.¶NñJBënì{êQÆ'O‰ãè­ÒEUÆ$AÏÙsÈ@»+°v³ù*/ד¤]±*u¢™; ¾yîà‰@²Þ\0Qº{&?î+i½‚ÝI¨ÿšAøüýÎ¥ªúIV'ÞPŸIº°Ú)òËž@ˇŮík¶8óõîükï;NÏSke¯'ny,aÏõóÙó|®å¡)£².ƒ¦ÿ¢7%i©ÑáÖÇ{N§NŸæmné ç&]©|i ÙhÙ~÷ëU Âú¶vz_„=W+à×[ôÜ£Ì>DZkP`æ¿þ2œXÚ ·J©Ð­ ÷Àö½c õuÖÕ=qX é–³ùƒÿO³ ½¯Hß'Í·tÝ@ç-ƒ?ñ 6š¼Ê—¾[ç¸aæˆË×rɆýýSax·+ddJ,y´7(êÑ;Ì·Þ‰=H‚ˆ[^EL»÷Mï½Ðy‰§² ß“Ÿ[±Vê®Y5/ƒïÅЧ•Ra·ùL';Ç84eÝh³é.ðöÔ–%²`²fa|d/ÿÙ„ÖYùV\Ýö¿“žG°÷"Øx•â8½ q¶>"R¬Šª¦§€ë‘!M _Æ‘"ßóÝ+]á‡Ù¼~ý1„DVdô³ õG§óëïy—¿KÏõývå8ÎöÈÞ<âY?øˆH·š&|ýëi²³ó¢ëêºÃŽíÞBf<:ÓáñÉ9„æ:Ÿó¾žÜé=ãû·J'<`p­>Á‰ ÒgU[¿?O‹õ¶:C¾T]ßlüKÔßè«« !%u™›os õ3§ß ]÷Ò:çÝWŸ¼#z÷+snŽã¼ª4Ñ¿¢s"ìÉNþ!°O Ìò»'¯t– ß9ääÃ/Èvd2a0ê»Ï}ïý¹Ü9Á~]Jï9Ñõ0½¿hÐ >ÿAäÁ4©e"ÜOŽL:U+æTía½4í,éx:kl §ÌŠ[{wë¼ ò)4çØ®Ä9¼jÒvÏÂ6’ÿuoî¯iÞ7èÇIs¾pÎäc,½ðAéíd»[êýçHÿŠaæcùÀñ:C'>) $c™ëp‡fs÷¶ºóþÄt=J㌮WÏ M^çK ×ßs ŽåP­tg2ЦkÿÚ.’!So$TtšÍûzÒý:3ªGzÜx½(Âq:÷Ûú49æör”z%ÃLÅ’C/¤K]®÷Ô ó× p( $ÁfNÏ-®Í&Ô?–úÐÓz==÷’Åÿ¤Ý¸Ð¦ì=b‡ÙÅ'ã8ìy{2¼(еlXƒÛ`Ë-®þžðCK.wF‘¾õ»ŒÉœCؼ!¶žô7ÿPšÏØ{½ùº¬A78{þ“O'¬«Ö:òšÔŒN&„Ðs¼ #´šß'ˆäUž1§tï\n?˜ÛQÛKù}õáe뽡ÎkÛ;¿Ø±ã(qœÛ--bŸ/J€““ârsµ?—ɲéÁ ò9Å š3Óáö@ÂÖçæ“OM˜Ø `×}àbÂÂ/ukóûmz.i\7ÇqRoº—Žé™KÇ3;^-¬xš}ªñj ™Û}OÝ7øU“׬¯Y ©3\åýq¢/ÿ~X¿X  ÷©NELP]¸iUfªÁq$~œýññ%ð«qzìõåZXî˜q8I§!†4ZXÇ× bcšÔgH:WÙû¹®Ú—Ðý½oFÏñh}”Æ#{~ÁݯÄq†§9Ô‹¸:æcÙjáñCóû#:Å¡ÅJï¹ïÜàã¦^S™v¿ìKèúÞÓ¤÷ãi\Óù¾Ì<ó&_ÚèüOÞõ½ÄÝÖBÇã]kÜSÆ“G¹•6Å.u‡{üM‚>á|vÂÂ6¾ü=¶þÝœ{y|þdó´¤Ì½7Ž3Ä?ï±úãEÚgߊ7I08s©éÌ›ñ$éú³×<øó|aëÈèüi¾„úFÓy“Íø:?·2úÁqÚö±©Xræ"øtõS½½š£¤SŠîY]$}MT­xðë‹h sÀìKöܹ5Qô^9]¿°û—B ÍG쾂›wpœéÌw*†6U%Áe×C EèEr¥ð•ðñO(ÐÌYYëÑîžÝ|r¿ÅJåâê=€½ßa ±kÛY¬+‘п›¡ukã÷£Äqæ7?Ø!¹÷E¸3ªxT¦gLßyËmàó‹¤Í/Í/ï%ž.V.}@Ö}·#v­4Ÿ Ùt娹C?ò÷ìi~£Ÿ‹Þ+6>ŸÇq>[\Tùc®Áq¢ýž®«—:3q–~uÜ£\"UÇ;¤ØxÂ'»¬-‹ddì—:ËC} ½oDçQZç£û{ºÿ¢WeÐŽã´-d£Ê3Û,Ÿ­xr&¿ ?&­’@®n˜VÃÍׯ<މ|´=€Rf|lñfW¯jÀÿ›oâùý=½'MÏ7 úy›/]râjÈ“êñ0÷çT׫ÚË )¸uøÃäbâvóð+w`þ*«Éê@2Ѽ]TŽ£7ÿÔãëÖ´¾C÷´N¾Ç¬~pœ _“œÐ@›>uí¾ É¡!ÒsGHpà† 7?¹BnÕÅK…­‚IAŸÄƒýýø}]ÇÐ}Õéq×6¦ÑÏz–©ïHqœ"IÒРn˜¼¾’oÿËÐkO×aM?'›ÕýG'Lwå÷1cŽg?þUéÇß ùòÃôÌÐôâBþ܇ÖŒ÷rçB—Ç{`ëù—Ax*þÑîá‰ä‚+>}ëI5œYý ½ïBë¬ô¾ =eïÝt.s¾¤ÄqÂÞ+¥ž Ù6óV«Ëðl¯ºM‰DüeW ›B`fçÆY«‰ÌúЂ¸æ [_ñù‹®§èç1èŸkþrÊð)Ö Õ±{ý¤ï¡°Îµ‘Ù¹‰dßœäöM&¹B£KÙL¬""R·]:¹¿/Þ,_–äJhÞ4èŸ7òÀˆË]ן¶î••¿lì=Ãú26õéÅ7ÀET·W’ÜÛ}_bÏsÚA߃¶®çó:7èŸËÆë9ˆhÞóÓÌ3‰PbӶˤçÁÌŸGz€Øzí§xÍ Ò6¬þÙS±‹Í?´þCëÐöJÍP½²ó€OÆø=Êñ¹õBÞš«O[÷K„19GT§ÌµdO×1%3§An?š5VX‡´[@è9]Ðý§!~ñyu®­÷ÝŠ“0³vÁ‰ÐÑkï‹eë´d‘Ï›ìñ?û@~Ë—ÏVÓ³NŽžº€¿?@ÿ.“ÖÅØù๤áçÝ[Úßì^fߎãt<<ÎÖîM,ìW­ŸÌí–¥Zâi¸Øí“Î_zž¦ =/îi=º½?¡ë:/²užR ×Ä·6hòú–¹Ç©Áqr«Ô;àÝ8\3ŒÈ韃ý­æ¦Ú'ýGß) Oø<<ÿÒ‰ÊkˆµáBþBB×ûô<÷?H"-^.ÛSû£„Ý_÷có>>ÿ£_ÁYG‡S 0\8K„019ç“LÖZ\›>Ân7Ë·É™º†°÷nªszÆÞ‡øÌÍco$íš- Zý®/ï¥ùR½Ù³qïwž„ÏÓ›µI„í¿ŠsÔJ!;û¦7±Øè¶Â_½»¯!Qã_¯öع˜ÿûZ ÷8ØúQ‰d[sæ¤Óš›ßØsJŽã?¯eÇÜÈÛ4a`n‹DÈxPÜþºs aëî0|ȵ‹;}H–…ù–ì>‹ùû)ì½ä–üß=Ðu]_×Ù¥8ÎËM­ßÏcz´&Â¥×çÑÇ¥s Ÿ ÓÜá”÷ôè*çWö|yYŸºLF–tæöB ÷èý]úwÁc Þ´/9Íž‡ÊqQÖàî9'ÀíZ··m›b·:Qgi*©¾qf…‰^0;ÿ¢¬‹ÏjÒéÞÏ_ü ½OIÿN™Þ{¦ëÚ)6ïµNì »MØŸ¿z4ýÕ£éßÕ£)Ü„ýa>gó¾Œú™x!iˆ30}è8_ÃïõÛTp½M„\_á´ïx» Êy»ª¸ž›òßáëeÜ[ØØ×ëŸñw•s½Ói¯&Ú;Ý¸çæ·z§ÿVÏÍßÓ;]nÔ;zÚ”÷64ö¶65êQ'æ¼½¨GDçíÀõ9±7êsbìm¨)×s³¼¯×oõª£=O¢8‘Ûrþ®®w‰†ó¶6îY§ú†‡Ä_¹ð¯\¨4ù÷çB÷9tÌûÀ` G D‡H10£S¦·çíõ½žuŒ‡„†ëó¤ä¼q~ËãдœÇa8à^ßñƉ*×§ÓØçŸñ8ôâúS¯kÚظoÝ·úÿVߺßÓØË¨ÿ0õ‘(ïïeìó*4êù$-×w]Ãyæ0=ŸJO «±(çï¥-×·ÎØG'ü;ýŸôˆŒóxôf{°«9¯kç#!*×*ü}Ø™˜d~þݹðÏšÿÙøÿ=ÿý£¹Ï”û¯OaÔ¯3`*"Å`Œâ¼%¾×ß.Šë×iËùç0þÖ^H"ÆÀ G¼ Îß:ƒX€A¬@Ò+ fÕw¼­Ë{ç{[«¸ —#ZÄ ƒ_Å À ICÄ(„pD€bP :DÊõ¸+eÖ‚\oâò=îÊ÷&þ­wßëKLûÛÑÞÄÔc¢¼/˜±§«Ì¨ßº¢œŸa1çg¨áút†õé¤^aa\bÚ箼§õ÷z­ÓŒŸ+ãc¨G¤\¿»RÎÏÕ¸×zÚ7¼ ÿZ÷ýµîSšüû×}Bî÷Ô3ß7cbŠé‡è¦bp*9ï0[ Ò8D„€#ö°Ä‚óœÐ"\Oýw<…å<okçmý[þ:å{|ûë(8ÄQÁõ+¦ÞÖ´_±±@ñ„ý~Å¥ˆ¥E¬PT*NX^¿³_±Â¨_1õ(ïfìë*BA*‘®×§qŸv-×§]Å Õ Ñ"VåüÃß 1Š7”ó݉BLQÌ~ˆ‘¡¨Õˆ…­D [ÎÓÕ´7Û³=Žó¶öãúw2¾®~ˆ‘qý?Ë÷mgâ‘ùù+þ9sáb¤9ð{ùO„D!¦|~ˆ‘bF!¦ˆ~ˆ‘a@ª!¥)@l18ãhRŒØsž¥\¯ö8ÎÓZè)obŠìÇyZK1£S f?D‡ˆ1¨Ã¿ãi]ÞsÇØÓ:œó´öBÒ1Š  ˆ‘¢ ¢S…¢Gd(ŽpN ^ˆ–ës€#ö(Í7úk PRŠÈQHZÄêwö9£ÈÂú3å}ÆŒ½amzµû•óNd<­å\¯v+¦ )EäFžc*N¨^HÚ7ü¬¿×§]ˆbVrÞ±bÎ7±€©râ6îÑ®û†gâ_ë¿?wÎûoYÿYp¿Gó}b0ª!¤)@l10ãgç9fAªA,0PÃRDŽ«E¬8¯Š4Ä ƒ7Œóåù{ÞŠÔ—ÇØ_‘ñÄ6å<±Ë›'aà Ååüyü8žÄ_ÑÑùc—"rޱBñ¨RDŽ"Ò"V"ü7„“’†ˆQTህ¥@Ò1 , Èˆ‘¢Ø¢Sœ¢C¤F~å}ÇŒýc-PH1b[®×{×ë= PH".ç;ÆøUHQ¼Qˆi÷²^=jDˆbV"ˆ-Š:¡°bÄžóŽr½ß5œ‡¶’ó«`üc•Hb‹âW£ÿ;£ÌÏ™ ™ü÷¯Ì{ÿ‰9ï·òÝÿÇ\Ǽ75"Ä@S"zD†§F„tJ¤±åň E¬æ|,ì5"DA+9ïòŠ­ëþ\9î¿u]'æÆ)f¾/ Æ8D„€#ö˜Äƒ3Œó “cj+ T¬^H"Æ  CtˆƒWÅùöü=¿EêÛcì·Èøf 9ßìßòîÑ øaHi9ï%çÝóø.*½‘w6#/$ £xÂ9y!iˆX„ÿ†PL D‡HQTQˆ) ËÑ!RXbŠ"óCôˆ ŦF„(8%¢Gd(¼(Äô>dÆ~²V(È0¤”É‹(L5"Dq*9)Š4 1E¡ú!:DZ·LÈP¼jÎOÛØË'¡˜bÄE­A,0hÂRDÎyÉŠPä ΋‘ñÔ@ôœ/ORÌäE"Ä D˜»¶ÀÆ&óCsá‘™øïÌÿ‰¹ïÎ{ÖœÇ|÷qˆ®±Å ‹CD˜çbÄžó•µÀ@ CJ9¤±Â Tq)ç‚“ñZb€*‘Ä5a°pžÙ¶´qˆˆñ%C °ú;žÙň=¶±0òÌ–a~S3{X v="ÃܦF„øJ¤±EÄ!"ARŒØ£Ôˆ) ÂÑ!V( '/$ ±B‘¨8¡x!iˆŽP4 D‡HQ<ሤ@tˆ……˜¢˜ü="CQE!¦(,Ý7üÉÊ{ËÊ b¢ (ç³(à|uˆ…(@!*ŒüÉ¢SÆSÑÃ'»±E¡Æ!"ÌiH1bÂÕ (Þ0ÎcVÆù+–2÷÷PÌqˆ) ZŽÄ!"vRð Å¿ÖyÎ\÷߶ΓqÏ)e¾ F b†”"r L-b…Á©âÔ ICĨሃUè)­ Ñ#R Þp¤ô7üµˆU9FÆW[T—õh,Fì1Ð5ˆ{RŠÈ1赈¾Š ~{DƒXpÅÿ Gc@9mŠFè)Š'  €ˆ‘ŠðßS“¢Gd(*5"Da)="C©!ŠL‰ ¶(¶8D„‚ @ [ž¢øü="CªËùÍŠQ*N”r$¡8="C‘ª! U‰è VQ´J¤±EñÆ!"pRŒØ£5ˆŠ9 )Eä(j-b…ÂVqâöâ¼f-Pä~œw#㹆p~³aH)“QüD„ )FìaæçÌ…ÿêøGä¿?ò®ÞÿEÞcòÍsÿÊGóÚ÷rÙÿ°÷ÐM]ëÖ¶éÂ4‡PLÝtÑEÓR ÓM7ÍÈŒM¡ 7Ø€aÀÈ” º!Ñe,\dše\]6D ˜.„oní½™“„ûçÞñå~ÿ c<#ç¤è¤9×^Mïä>÷AHQ øCP&àQi( .3@`:AdJ`RˆMDœ‚S3CxQ øC€&àj…¼lˆÑ| H-È~¦ñyÙ €`Í_þ‘•í‡q˼!b °?ˆÙÄtÈþ¶ ø@ÜZààÎ roˆ\¤ºˆ v°)D¯"_¬@Ä/˜@ lÀfˆ^0„Ø€/ŒaÞ0‡ØLbÞ0ŠØ€†‰^0ú£|Y3wßò2±ÕÀ|a¤xà3© øbl2o.KØ[ÿgv>ð‡áLÀ¦ÓPÀ|f uxús¿«€ }`B-È~0£p¿¾ú0„8€Æ0 Ì¡ ¢ …Q´nÙÙ&àÓ¨ øÂ<à© øŠñ×€7̤vàSÆŠvàƒ&‹ùÀf3N ò?Œgb˜OìÀ&4oQì@Cê¦Tð9µÀü`R#èQÀü`X#ôQ øÃ¼&àk(`d3ÀÌ:ÁÐJ`R[D0· X…Ll ° ™Ú:|?ÊÅ6 Zà Âk[àÿèXÈÆÁÿ/cà¿1û«¹Ø¿=ιqÿÆ|Ì}.æ>nqïÇÌ}Œ8€Â1 Ä£¤ …ô@1©€È!ªxàa)Ä¥>˜8¸1 B3 Ħ§f ðü!<ðø´À¡H D F%°p祃[?b\21ò?„j>—´À®H ^ `1äq<ð‚Õ@1Ç/Z lÀÂ6oˆ[ìÀ"7oŒC`~‡Œ@ ñG|àFÐ;ð…! À¦Ðð…9 Ü]Ä$0I>ð‡Q¼a °?Ƽa °?Œ=F †‰¢@>ð‡™LÀ†ÒPÀXf ¹t‚Á”À¤0š(¸ßoÁhÍF3.IÄý÷_췰‹0Vé…sS÷ß}ÅÿÉýáÏ$¹«‡ðZ‹pÎà>ÎYÜîÒ±ßRXHÁ»uV¾óƒó=ÂÿÖ8îÉ[´Œ½¿6ê+¯ÆsêDXÂåÝÆó_O>GYßë”ØsûŸ@XnyµïÕ£©"RècÛln¹*¾rXeýÒ¹:zÔ u6æ:D.pmÂ+%Ï’'·N:wŽÖ û3¶U’ߟi•Ie$Ú«æ”0Êú±þO¬oëSÂÿ¹]>Š&ÔyùsµRž?$cJç}}Â3‰ßÜ­éyúÖk÷ô%Qv]•3=’–´º÷íÊ®<Ö¯…õWa}äW¿*>F×§­+ÇŠ«cCáS'¶­÷ø‰ËßòÀ›DÒªûÕ°òËÏÓwRçÅôQ’"dw m¸ˆ¾îT¡ØÛ¯#(ëóÆ×kìêçÂúè±~ެï‰ó zsO>·Èù°äÙª^s¤ïÓD²ºWÅŸ§o:·í»üéR£0×qn!­yC´t‰4œòy*U]ýXÎß$Ï•çÞ·VŒ:×=ÛÆHVíögÊ8I¿ Íú^ š“5:O4¬Ì?I¿ ]Hù¼¡0×çÆúg°~p¬/$ßo´u¼59ê4¬ïî¹A Ôq¶©³¬<²h`èIäQÈ¡ÊÕŠ^¤ßL·ßÖž &böIù°0W¿_þ¿·¢«ßËd9¸î}ô4¨Óx/­ïña)÷ëØb´L9ž°ý»€‹´ÊᇾnLžj6¦Ž¤uv-ŒIÝNY_bÖ/‡õQb}±£'Û 9)|¿1=êPO.Ùå'ræ{mSEá$²9yÆäçG.Ò¾—Ÿ0ž¬®È5jФ ®õŽ%ó(ëWÏúß”TÛWkO¾¬vYs… ;e|EyÁ¾€¨S¯{Z³™õw“äœóûïz$¹ï‰Ùe=Óè®mSæ82_Z®¡dã"Ú`÷±âG{i\ý«YÞ¢öqË-¿'>sùt$—NlèBX.€Ó?¨Ãç“Ç“ŸïÙ˜…÷óåãŽ&éè4º¨Ö†þ5~GFoó«têÔ"Z|¾G•޳5.½ùn-Üðš©aýÙX=6þ¸ç¥x¼½'4ÙLZGí$½ó~í,–DbS+þÐÔF/Äù&>)4žŒ-9i죑t!ùÊÌ¢Êò1X.ë#ÏúÂñýª:È•£N·!}¶oRí |®VIf³x±Ð÷‰£öNõ &Θ­šQ4{7’D¸rªX_Þ‚þ9'ãû¡±ÐïufÍn%í°ŒtÜ+‰½¹\lK Mé.¸ÜB)ô¤m6iV÷e„«ßë Çúý³>M|ß úÁ)PgO»û;NßF¼fÞ]¿FyŸÜÊÿüX mµdÉÛO'’^ëw‡Ž›±^!\çöy”ïôa9@ìózüpáÎw6Ÿ?ÒÑ5ž;ýƒ:¿ÏØØðÇ­dy‘ý±Z'‘ߊrB-´ÚÂz‡B"'‘žåºý´ªõ*ÚS}ĸNó(ë+Îú²¾^,ƒïçØ¥`>êL›0f®öÇ-¤öªè »$‘-^„¥[hVê)‹9z2ɾûaHå èµ> Õõ"„¾dMëCÈûÿ¹«¯õžÎßíéÈû¯X«IÏ»°™Lk¶*G-K"aoOMMñL§ÙËs«Ïš9™Ôv6P[Hù|¼0Wžë×Äú—³>ãq^3zfÍníêƒäô êèî}|å¨óÔ+¹ÿí›Ñä¹´ôÝýÚ$RüÖŠv÷¿È Î¸jãÉ+n_ XHoE¯Û³"ÜÕ‡”Í/XßI–Åú¸ñý[ù\ êðý+—È7Ï ?ÚšDZvŸwfSç zƲ¢ÉºãÉ¡ú?Õ¹>nåó“Â)ëÈ÷¬'ÌÛÞ ãŽMÖxpûÊó®µ.k¡A.Ήnù±Äœ ¿M"¡Î@™ êŒO6ž`u«RÓ…ôš©Fü šá®q‡õïc}HY_gÖߟõvúuŒÃîî=^v!ùé¡_ßžÙIäpÙ÷©WfÐÊ¿$åeœÂ÷ãì³I#ªa*îšï²õëËúƱþ×îù¼&Ô)=c~»aóÉo«×K¾J"]™"GÏ ¯_Ýæ“3–¼ÙØ õÍ¡K¨³ý]µÊrXXŸz6/`9'¬+Ÿ«Ã÷oµ¡ÎŒ´¸¢Ö-é5/VH&‡c††uËÉ ÛÓ›ç7(9–ìL>¶&`)MþÅ«Cx—y”õ=.˜3|IÆúúÍ ;,«k]ðùóþžœÏ+%\úIyI2)Ó«‹!ªx&=[íĦ}=ƒÈŽëΕY´„¾ñ¼, Œ†²¾º¬o4ëËúѲuHuêðùX³„~ôÉä}‰-Ò•Q§ôøg•Éíåíß4븘òñ‰Êú4³u[_¹ž ¼Ã=_\Ž:ï·N»ÔÆç[²ò©ÏšüÉ$¥DnNnßLÚO–ûîÙÔ@²å›£ÏßgFQÇÛk«nžG‹™ÔÅ˼oíÊed9-,†}Nßàõù|ða¤ÎÀŒæ;â’‰1¶^ãýªLZİcØ´¹äæ.Pt1­«<_ïA±y®œ!–?¿2– ÆòZ äj ?.ö£ÃVí܃ïz I“Õ™ôÌÔåÊ "¹µ£7%Ÿ\L/ïàÖ"\ó‚¹s™²35'z¬´Éª:XvtùÊéÔásŠ¿£¿f<Õ}6™Ô¶+‡ô>žI7t|­<1–´<êuG¿`1=ågš- šGY¿\Ögñãþþl¾Åú‰:}ƒ:|^víÈÅ­<¿újIF2©°éië;e³è¡‹•C‚ɾ~8¹¸öbšqµÍÑËó#\ùiì{aót– ÉÏG:è³éñû=ùý„C03j÷¥J—“IXõʵ ³èî™§û @¼Î]ú*ŠŽORÍòH wåš±ñ*%vI+Óî'2>—ä¥k~[hGéê–Kmy¿ Î%—ÄE/¿­³¹Ù½dòóÕõNMÊ¢‡"÷ÔSΟ@^ÿäqkÊÍ(úzÿž6Š…SÖç˜é•Ÿ×r5_»¾ÖoÓéÔq¶#ß´”6-ó.~Ñûd!¯5‹®³¶Jó¸Ln<ñ«`ÿr1åóÀ°~?š‘¥èLØxï¹­–RA>ÈVç7Ú^úô+×üÓ=ßC:¤ÆŠ*ñÑ+(?N§n-~Í‹J΢Ó­¸Ñe<‰>ñÕᡇÓÕ˜Þ:Œ’e¡Ï«5è(äñµwíGðÏ¡§®ü¡¹æ¨³¥{íÔ=7WÒ«¶îíU'…ü¼ª³è¢úsKîj7–œyvïÚüíKhÓ—MEyáaB>{ WŸS¦gÖŸ”=?Ýs]ô¨s&{²3:bÈÄ¥“¤zvçÍø2›>šõUkÙü@Âç‚.¥å7u:ýûÃ0Êr†YÎÓËGásË;–ëô êLÿ-oUÅ3khN‰{ƒR§š­òºùµÍ¦¯5k}´¶‚\MñÄ#-š.xQ¡Ã××Ã\ýöYÿxÖ˜åòÖ<œÒúÂrKœþAáfxaZKŸnšª7uJ!ßFÒ“gÓ‹Çú[WàÉöàyúhÊç†Q–ËÅöÙºŠåÿÞpÞëåU»–ƒçôχ{òP.&fe­Û÷ç"'¦JÓ=üÕӳ錉ï~iÒ3€ÜX7sdÊÑ4©&÷ÎBiÒŠ–sšÕ&®ÃËïO]Ý3ä•l\µÑ½ Ï}(;øìœ"Ú÷kÂôàôêüjÌ™“Ñl=åF…˜U)dòÐ:/»­Ì¦k÷ÑiýÒÈÒðY½ûKè/ ;ϯÖ(”²ù8{~ñŸ×û8ûŽ—ÍZ=®¯kýàôêÌZå]thٔﯞBäžö4ólÊï)ˆøÒ´1%..¦º“ªýâB]óv¶ÞdÏMÖ—œŸÈ ÌsXnÉôÂ.È.…d= ðz”‘M»NµÄ{ $*çébzàmÝÒEbÃ(]û9Âü–¾]žoí³¡Ž3®i„ž¸_qÇ“²2²Ê,Ñël:ëÊóëì@2w4U´Ÿ¼”z§ÏîscM8eÏa¶¯ÍÆk6ogë÷ýI=÷ý”mTÜ·Íf:¨xYå}O39W­ÒðÚÕ.Ó•—o×-ˆÌ¹žâ~ì2:(ô‹))™á”ÍÿY>›wò}ýÉ?2n¦¼«°¯+ä¢Î²î²ýê›é—ó*̺[ÓL,Oíä2m׸AýçAäÄÓÇfÿ)Ë]ã(ÛÏc}Áù~õáù“/+í?½âÒ[_>§^ÎûuzÉû@J[hdÅ©o|{™‰Ï„v>w™–ðÛèÿtXéå âZFê½/èi(åsS¶ŽæÇ‡ŒíW³>ü,×é»ü~ rÖôE[èñç‰3ŽjÌäZB–w•Å—i`£ëkí$/®wVÇo]°>ºñc¨k>ÍÖ5Lg,–íï°ç¹Ó?¨stþŽúC»o¥'|Blc fR»½rðŸ.Ó =Ê-“îSPüÃÚË(ß·;̵ÎôÆ?G3d¬ï9›Ï¹ïKÉQçXhBÿå…·Ñ.¾êŽ™ìOßtúÜeºÿUð’'!cH–üy‡9'–Ó%ŠmWæý±ÏÆò-Ù~›g¹^ ¼¾¶¿ùâ5ë6Êï㥒ð>#vj\¦«[ÊÎMvg”_]AOŠ!@CÙ> Û?aë6Ïeù—ò4Qg¥Þm×·ÓW«…¶J%ïjî[ÂJÇ åG‘Ãß%ŽóÔÒG­Þí¬æ£òˆ%„åwò}ª ýª“ñãC Þ/xýç.õ»·ƒhÕáHë>©¤é¤WëïÔ±Ò…´" ë?’ Íxùõå…+„´y”ÏwªGXŽËÈŽQÇ–$C󢧇øü&c¹;ö¥Q'ñÈÕ3>ñ4Ò{ÿÜ!RIZɇ^=eVú~cŒ÷® #É¥ôñìå´˜mÓÀúg#(óõ±ðoVlGšŽÔ^úb|Q!‡Çƒ°}/¦w§_P'ùuÒöÆSvÑá‹ç½´$•, n5'j„•‡Ê*Tj0šð¹,Ë©3FòX„Ðß»ƒëõø~îe†QgWz-c¹°lŸÜi—Bvùw1wWLœ²›6éw¤RðæTRÉÜãî#µ•®«cº?úç1D“qBM¦® #ç¦~¸ó$BXw´öi}›°ñ†å¼¹gbÔ¹9»ÿ‡á?Ñ\.ŽðX*ÙülpӺ무ÚÌAÞ@â·;¼cÍÖZZéu­¸²ÿØ¿áÇã:®ñ™Í£YŽ´»Î䨳ÿ@ïÁÓÊì¡å’+n¥’9Ú¶I>i¥Ë«ãO`œ1ŽhØ<¥ t¶íTÓ_3Ã\çì\Œåijqšõg/ˆ:Å›¯h[÷‡=4¯ìzóùbgIw¿‡ªÞ¶Ò¹MÎÕ~DŠÜâ¢uié} [?ä…PÞÍ\9l¾ÎöqX^¿ÿâÃûuøý„=T&*ô¦|ã³dçåêÙž%¯Ðû»Ôò,5–ÔŒZ?(U+Œ/!”ÍûÙs€í³|–Kïþ~ô¨óó¹oÂï¥|ÞÞY’°z—4±ùš¾- PìŽ ’»bÛ¶È•´â û 7Ü¡}ONýÂûaWNËE`¹Çì¼Ç}ÝnB.½£Ç¿ÝK3çW/6ñ,©7»üѯ]¡ÍO¬ÞªE Ù—yH¼úõJú[ªw­à¡”Ÿßö ,—†_·?sÍטÝóål¨ÃŸßí£Í‹\xpÃY^`yŸNÿ NH¢=aá>~}ûÕ¹†çÈ“ÑÓûËÛ„KÙ®˜1”4.¼áfÒo+éõ kâ.½ ¥­£Ê=r´ð#,ƒÍsùuÉ3™Ó/xÝ}×ÿæ¹v¶ºüü!çÈ¥/;Ñ %n“ã=¹À¥¡D¾°®ïvëJêŒá«&¬s»vÎÅö‰ùçô[Øy¬Ó/¨³µHŒaZÃôêÙޘʟ#}ºu;2ç]®pŽ7Œ¬1.ÿ.)i%í³dž°¡ÂyQ{W~¿xïÚ·cù |>_Ç„:³ßdG¾ì~€ªÇ~è·ã)¾tnéoòsÉæöª ­%†“iOó,rn¥k=ÀÎ?YN;;?f9ìÁýýØPçVÔãá—•h¯ˆÄýþgð¹©Î^»KLÓ–UyVeY¢=¾1\ºà<‰žê­‰Í%]+·Ÿ7z$+:dë°Uô«R%½Gß £ì¼?7¨ý9)l<åÏÁøœmêLé4e™(ùýqÔ ñ¡Cç‰s=µ$—ˆdÅ1åJF<¿Ý¢ÙƒU4Ô`暯±ý(~üOv=§Ù½w]ÛP§.ˆ*Sé ]äa³ºwžŒÈô…q^.YvlMÀúeCȲ¯.({|5µ¾Ðë+ì £lþÂæãlÍÖŸ,–åR9ýSÔ.çsLÒ>“eûÊT¼@æ-<¿æ«Y¹„Kaºn0é_O÷mp³5®6°ç4Û'æó‘³\óBvnáôêÿœ¹"cûAüûòQ§ªmþäá§R~ž|‘¤œ¨;öH‡\òýIõKÛ¶1¤_À¸ÂËVÓwC4OÚM £lœgç8lߎé˜Ýórú¯ß»Ã˜G'é]5÷I^$ßÙU±P£\øI6hð–@2{êÕè ¯5”??qåm3]±s×zйÏÚ¦€ïm¨³óîô6—=D¥-ú…Ùz‘Sæ\ì…“ÜLXòïC:gß4w~vˆ&MïýSíïÒHÚ†uªÁ9äÚ±m‡}ë$Ëg;7œhÄnÏ2‰çRv~År}Ù½ÌõÏ7îª}â‘kÝÄrïœ~A¡O»ß]ISùÛ}Ø’FÒo6ŽÛ›œChfÚú%ƒý ï×XáÞK(õidðØQººk¾ÂÎy½]uwº“zÔáóÓ@î1u=<9ó`æñ2ÏñæÉéIÃÈʸR£jNŠ¥Ãëtmkû?îòë¦/\zffûföP'r ·w˜^]¾ëÄÁR2uG×Îå÷çnÖ‘òë0r¸KÈ™ð;±®ó~ÞTYÈñ,çÊÅd϶ßîþ¹ÙP§kl1LµÐÐEªóZˆ¥îÊ'¶äõ '¸ãý‰k©¿yT•ÁÕÂ(Y¾ÿýœ‘±s/öã÷A*ð¾)ŽõÙø¶£ûÏ–æÎhh·1”²sÛ‚ûq7„ÏíŽ0î<ïP NþéIkÛU?J3nÌjwg“…äuÍsnRém.Ñeý1äîoÇÆÒ£=¸DðÊÎÍÙ} 6¯eûÂ,ŸÛ}ý¬AÖsWªç¥íöV(œh!µ’žŒº82‡Ô›s¨T ‚\˜½PæŸK-fM ¡ìyËûõK×øÉöÙsÎýWÏéÀÇç›&¯Ò/ç•}žg!Ýß]ýÞ9¤bø‹ÎS÷)ÈÆãφÿXb-½:kÚ7AóC\ûÜLOlŸ†é_vu}Nÿ Îí3Æ_›xŒn ™¤ø©h:™³GôëÒŽ9¤­¦üÛ­Â}Òµt°JýÕ×ßþ‘'ÊôÍ>/þ¼0OæÊ9vÛ´¡ÎÉ×ýƤ£#¾¸n:isMu(£q1~õ°ßœR÷FZ#sZ,•ØÐò|É×>'ËfûÎì|¿W$-pß×£„]~èq­Réµt`àäÚæ¯ÒIî…Y³êTÏ!Ï ~“ÖÍELögwU{c©Wí}N¶qå½²ù»÷Íîá²ç¬û¼FŒ:Ï=*½yÙÍHGå•\°1(tjþâR@ÙR¶XQÜ’Úxg—úk]ëB6Ï/xÎ~ÕåS¶ÿè~¯GŽ:ç®qÁ÷Fê;…»y•NJ¯5¬œW(‡ð÷fý ßSçº÷ÍîÃñ÷¢¼]÷cÙü†åʺßQ N·"&ßÕ…óüt²rJZnèK™ãýøXúÈaDsL>?7EGûöÌα7þãܘå!³{ì|:aIÒ4Ï&m œçkPgW7é~¼°;-ô=xâñÀFKÛm/#’2gz$®ÖÑž§Êû.êZ²×aç)lÅîy²ûìNÿ Ž|qú«‹FÚ¶„è®ýi:‘tÊš”xÃFîx¦Ú9œxÄç&7»»–¦›ÿzé\Êö¯øùxR±ä¨2äq!òx„È}oŸÉ¸§Ç(©œ°¼v§P§sAn¤--GZè+_"u’<7°ØÈÈÕWÄ«¢G‘¹Î âµÔ9mô›ëš?³Ñ)ýÛÖ »ðZÖ@÷Bç2vÎë>Ÿ²¡‘nê8´QuNÛ¤—È yÕ¢¥OÛH”U‘SïÂÒ8wfÌÞæké¯õÙü<ĵ¿Îæål<`ç…l|sÎyˆìòײ! ‹%PYLLÍEC/‘AãD#{ldDØþäˆdpüÔæ¿]K·„q+Ãp!ÿ¹¶ëžÛOcë(¶¯ÊÎ_œþA‘7æ”^‘@·OjpkòŒKÄÄ]KÛh#-©CšFŒ%•6[=×Åê\¿Ï`÷0Ù¼‰Ÿl>³­ Ü“•£7ª-=“@•gû‘ø¥—ÈâoJwŠ^l#I½¿¯4êä8‘’oM Š£üs7ÜõüaóL6^³ýh~¿®]}bêìRîy¿ñ×j˜]á^üÖK¤^îkÍ’™6²R÷Í…w寓=Ÿ>-y%ŽiA3Æ%‡ºò¬Ùy¿Oœèº÷ÍÖÛîÏ9 êX­õw+~œvÇdÌvâ)×3ª/c#ûÛn/~gÔ8rîÇ¢íîÙâhY—£ãMs)»×ÅÖËìscû›îÏ7=^¿î¤2Û_Õ9N¯'H.-½z‰\››^¦H©Ð¢ØÕË ²qþ»ÉwŽÇÑÜêH¬¦l=Æ|ÃîA°qÇ=ÏÞ„×_Ѻ¯5¢ÕqúÛ}2)âÝ%¢È³Ô¹ÖÁF6}½zN­@ažGsÒòƒËÜœCw]ÿºÙ…½r×ï Ø9Ïø}#yûþ6Ôi¸­mñ^Ç)ÿ;£ "¯›½£‘,-)žôN@º: ÇQó¥ÈæñÓæR~^,%lþÎÖçlþqLÌÙ 3ä„Ýwú¥¤]þxÌð‰=Ó*)¾¯Dm3HÉ5N·ªf#¯ê~3Î?i$év{±øâÔy04­ç“P:-È·_ƒ©m µ9aïƒ}^gwm ü¶7Ÿÿ+Æëw¯çÙìáï ”¿?A:\_ÛPÆFü/vȸµcéÕÈþ*&YGOJŸf-Ïeûuí Ûÿc÷7Ø9>ÿ|éìZÿ:}‚:“W-I¾z+ö|6êVää—b+êyØH}[íì¦9#ÈÎúC»×Ý´–ú-~è=læ·{›¦þа{ˆLWlÅÖs잯Ó'¨³4º¬ï/§èÒòJî 2{Kê·còn‘£‰šk©±#‰OSî—2kiïa†eꃡt'·¬Ò}%ü¾ë?ï¥<î2júõF]„ûù2Þ'¨ÃÍŠª¢ÎßEÕG¦eó»«Œý.õyxk}ïJ£Èþµ;Dªµô`fM_Jù\ä¦dpá òÍï´tåó²}¶OÏî¯9ý‚:ï•ÒñSQçZ»èù¶§äDøÐ¸F{o‘àM)>i?"gßq vMY ë½ûh¨kÝÎÖolÀþÌÎÁøy ÿ; êI¸¦Ê¥@¯˜I¾Û~²´öQÝ:Ü8cn‰}P8 ت£ó ?¹“1ö{ølƒíC³uó%›'8}ƒ:eßL(þôûºã¸fm•v™äj,@T·H“cÃ7mWþê’ýÂÅqtZSŸÒgÛ„¸òÕÙçÂιØ8ÀßóhYàýxxÚåsd̯ž@§|8ÛkÕÀLòö·Ð[ûúÞ" æ}U½­*XƒÇÖ¿;<ŽîËo^ùéF5e÷¨Øs‘ù’ß÷|"KéxóvŸnÖ‡bÔŸ;ô«Ò3hÜqî‚u&áf}Wß"/î­|¹dt 9þ¢EÏñKâ(7;.ÿbŽë>ûÜØyÄ€Û«§MnúZÆCÂÏ£å¨óí–´U¾K ?DìLÆû¹ÜwRt‹\üâÇY­n(ÈwÕÕG®ÇÅÑëjìù0Cíº_ÁÆß" µz‡Ì("¬{ßÊø}i?ÂïCò¿/Q Î”=‰›LL "û© Wöe’]o×\Œ¼w“˜†Uiuæáâ;yN9Kxu|צÇЈ¹”­3ÙýKǤaËÎ>{+»þáèãÞʼl²±\ZW²6ïa«w;…}hÔI{Q~l\DåCTi™dHõ ÊzóMÂî[)¦o5èGg4YåÿKM:—‘°eµÄõûIvÿ‰íß°¿Ïû¨ïÔé|ú÷‡×ãýÔ¼u³­=“llÂÝD¹IâîNhSdÇhÒQÝ MϦq´ryÉñŸ;„¹ÖlÞÂîòÏÏ;Âú£…ðßÁï›P'`þ Z¾7¨ó¸Ö#‹$+åÓbÙM²¯WøáU£H¥Ê=ò|GßåÜ£%ö‡Sv„1ß°õ®k¼pÛDZ¡ÎÈ^uƒ_a¼æ–ÑU³Èdíãè‘ßÝ$müÖXŸ1’Dͯ2ÈGãÆÎÖEAÙïIÙý~~{]Øx cß‹ûüÉ£”]>Œ´K]áÀ¸3¥òº‡m²H«i!?·r“4š”7-zß(»ÊïmWs-7¦ÁíñpÊëIâÚ—òÌ,ÑþÔú粘'{·;óÂu^ÄîI9ýƒ:k«nÞ´ër=?¦öŒñ~Y$öÆ úö7ÉšþDŸ ï?XžÔ>m¾³hb½0×úã:·lyÑVX·"ó£æþæíý»óœPŽ×­+Én/IH ½:Ÿ:9)‹Tü®cêÐÊ7ɨ×úòbäý‚C#ÏÓÑ@Ìnke„¸~/©½Î]ÀkOøùrQ’׻Ⱥ±™„í统 (P‡›Ý_‰I «Z~­¿,‹l{ûsïç7H½N)5–´o×i`ø½µ4¥‰W—F«B(ÿ}·æçíȼcyæi³=„çCæ®+ ^¿žE1èÞ€º¶37àf‘g è7 ÏÝ aCoM©>žœç¶ájèèÍ™çÊ>-JÙýVö»¶þãÏmž¹ÎÜ߇uLr“~ñJ ÎŸ ÝÌ"Ä:ãvÃM7ȸáу‰óqú\GGO•d. uí?³}6eçü< ißû™P'í*'(#­ù¾Åå-ŲÉDî_ªäÁø³]¾ô›@rÒ“¿˜ô4Ž–œºmòÊá”é“=Ù¼Ÿ­7Øy>;7rúuÆW¯{¥n¦‘î]ð8FÚ$›ôkb{£ïzƒ„: LZŽ Ð„Ï\GŸÕ»¿.µ}(åÏØÞõ¹±ß°y»—è~ÿÑ£´]~þ©ç×ýÏiÀ——ªþÐ/›|èÛAå]éiüsÅŒØãÉîì_šž±ŽvmP¥C©ÐÊ~ɯkZºîo°ýÕå¾ÄPB\÷œ>Ak-g ›}ÚHW÷ãN4²I›×îÌG×É´”%–SñcIÍÜZG_(×ÑFs6ÝÞš4—ÆÜ·ÜŸÛ¾kÞʿޯ²m#tÝò@ö n~ä¦2ÂÆ™Ï9`Ÿ{7i<þÞMÂ{³pßQ¡ží ‘€·ƒh2`ÿ…tî=PtnÙ×f °u‚¸ÿ®Š{ö5Ë@üTË@tϾVÔ+ýÏr`Ý3Àþ+½šþ* Öß­‡¦Æ­‡¦Qè¡ùq¯&÷,X·^ÂZààziþEþá§òoDå^ëã*Ha`=ÁÄ*·¾'ñBþ¡ZÈ‚e™Þn½O¤BæõçìÃÏcÞÿ–1O$ü·[¹ï Ð?Ï~õƒH%¸=O>û+_È>4ýú>)H!f½[î«H!l=AÜJ`Rˆ\Dº X?Ê}e¹_ŸÊ‡`¹_šúÿYî¡{FΧ²¯ÿ.÷PáÖŸ.Ê­?IèO÷qþµ{ö!×?Ý,ô„Ò FTþEæ×§²!¼>Ê|ÕŒ«V o_°×“ øÂÌ!óK#ä²þéb·¼×¿ê÷Äéûãÿ…1ïr¼û<Öý{c§=AtªBÿ<çUìÀ5Џýk>ã:ÿ¿Ð#ݽ§Å-ã•ûS‹×ß÷átÏweY`—ÁrÀܳ] ný…íÀ¦01Œõ'¹Ö`~0‹ˆa˜(üaðy´À0‘H`$íŸô¼‹ùÀæ21ƶ(üa6“kÈòo¸¾èf ùt‘ö©ŒùGÙ® …Yõ@ê€ÈaÜxàóªMȈ0™Ö¡'ºŸ[¾«JÈwý8ìó¼îóX§ñøwÆ:oá¿ÍÆ}Æ…þy¦« îüMÈþâ2­B?u D«„« …€õ@«€È!æx·äò¬Í@"ôv…gíÓè?3­Ý3Y¯O)L¨"Qõ™_ŸÊ‚ðþ(Ë5xÁ¸j`¾0°xÃÄ`~0³QÈüŠò=×[Ž«¯ãúqÞ×ç1ïó˜§ñøwÆ<1ˆ^ŸºÐ?ÏvùÀ_ÄÝåÀ_2¬¹ œOõZׄ¬V·¬WD­V …¸õ@«€È!ôxàå–õÊ2À>•!ážæž÷jtëiœüaðQ´’_ò?Ìc>08€F2 ̤ ¥ …±tÀÁ­ia0ðÉ´Àüa6ðá´À\Ž„sÈòo¸>ë …õ’ö_É–ðý(óÕ ä0n<ð‚yÕÀ|abð†‘5Àü`h£]%ôXwÏ{U y¯ç€}ó>ygÌój۹ϰÐ?ÏxU3Ù_BnŽH=¹; Ð0„«V ‡€ãD¬6à 1Üò]mÀÂ6oˆ[ lÀ"7o]ì廲ܯOåK°Ü/÷|W._Â0ˆù/²Ýósâ¿Ï¸þ»ìC.ãÚ¤0˜N0™ò/r®?Î?T+ÄñÀ FTÿEæ×§²%Äe»€7Œ«và&ŽùÀf6 ™_Z!ÿÐÙçHÜr]ý„\×ó¾þ_Ú¿ûÿÛ˜÷¿a¼û7Æ:îû2ïB\ÏŠžïªŽ|¾«HJòYÖœP•À¤¥¸û(Ð0D«V ‡xã¬6·ŒW/ˆY l@QÇ/[ lÀ7o·ŒW–ö©¬ ÷Œ0÷œWð)´À0‡H`ÝŸäXk(`3À8:ÁÓúïr¹Lk+Ã`z ‚ÉT‘kíž‹èó© øÂ„à #jþ"3,øÃœ&àƒj(`T³æžåjb7 äØ| -pE‡¿ÏÑa9®þBŽëÇyaŸÇ¼ÏcžÆãßó¸ÏÝèÁõqûïe¿ê‘rÙc -ÉgX‹ X°zrw³áYàñª øBÄà !k€Ý-ÿÕ¢Ö;ð…¸ À×;ðƒÐ@\á X–Eæák(`3À:ÁîYd,Vc˜æÐ Q Â(ú?ɯÖÎ8J`RHD0‘ XfŠ^0”؀Ɗ"˜K,@ “éFS Âpz ‚éTÀ*ä$Æ/P lÀF4üIF™ˆaÌ(üaPðIµneîÙ°vàãæùÀ&6Y \N m²«u‚±•ÿÈ‹òbÝ3Ì´ŸÇ¼ÏcžÇ¿7æù ÿ®ƒû ýóŒX°–à~ Œ¿&d.ªKr¿uC ˆÖ¼Kq¿å€§€lbˆ8 äˆÙä–›ü!lð¸£@>ð‡ÈMÀ‡Ë]Ž/ æÃêá+Ha=Á*`r˜ADåÃêæPëŸd1Ú€/ cÞâOç_ÿ]#—m¾0X<ðú›ük÷±8¸ð‡ MÀFÔPÀf )u‚1•À¤0¨ˆ`R°)̪ «f qu‚y•À¤0±ˆ`d°9 ¼`j5°_˜[D’Íò®9} ²ûoyÿÓãÝÿ±îߘÃ)< Žmÿ7Ç56¦ýOÌ×þn,ãÞ£™û~ * ,%°i®¿24‘©€µ(×GÚ^œº8×ÿÂó‚ðÔ"®_¾м=¹þð!ðƒ¥¸ß—ã}€|àaš€Ä© €H} R-pŒ]&àÑj( ^3@À:AÄJ`RˆYD´ XÂŽ^· X¹u)D.‚ÈUÀ ä{<ð‚àÕÀ|!|ð†ø5ÀÎ=À^0Ø€/Ì`Þ0„ØŒab˜# ä?ŒYÞ0Š øÂ,n ÃØ€/LcÞ0ŽØ db˜( ä˜É|`(-pŒe˜K'L ,@ £é³™†sLgO'˜O ,@ êFT+ÃñÀ ¦´ Œ©Ì© …Iõ„ÿ ì_ýÌ@þþÜ‚­g?ÿØzÖB އV¾3‚óháËKÛåa ~»7Ñd¤¬ÏRÂ/'F%ÎÉvõ ¾W÷ÖÈ”ë„åÂŽ®Ñíhn a}Wί­¬¨µq«OGo’ó(»ëSû%ßuøüP#ý¢mú½­ÑÙ¤MγC¶]'·yDO°(È·‹øùì:Ú€‹÷<—ºò·…>E¬ïËyæûøÖ#,Ï”«£AECNðyl¤“./8œ¬Ï&GZ÷™²ë‡ë¤ØÉëç—-W¬’Ê:“Õëh;{ïióš‡PÖ¿–õÏdý`Y¿)ö¾Üßu´ówj›¼4Òœõ\àlòíÓ‹Ý]';ï|U+}—‚h_LäZKÒ½[H›çwB(ë£Ëú§³>¬ß„{n¨ ¯¿â·¡óŒtì£km™Ù¤LJ¿Œ_ä×Éï5‹] $Ñå_TIÔÑ&“ÚW…R–ÇâÊÇú1±þOY~ú©ð_W~ Wdž:Í—Ÿí›yÉHóŽÙÇíz™Mööº´¯ÆuÒÖž¤ô@’›wÔ€^:WÿTÖ”õáay¬ÿðÚ"]2vå 8…V†õë5Ò7Áë«e|y™ÜéÕ¥T×7×Hñ7mæÛ·‘‰v<>Û\GÏp]9Š…P–CËò'ùïÿ•Ðÿ뮌ϳäÊâʈQgâ€MwÊ®7Ò½ÿ‡½ó€Š*K¿=†VÌ(¨`@̘1—©¾2c¦ ˆ¹ˆ˜J,3¦3™fL-1žf0QD1cl̘߾uï¹ ýfœžùÿ×̬÷Úµ~«»éîûUß>÷ÜsNíÖºæžæ)ߪ¶}íéô¶ÑãµO&¹“àÖ]§³ž½ð‚Ï~÷Iâ¾X¢ßKŽ’ûÜJy—T»IøáÚkÛ˜ê¨PgÞÞœÛãfijÛÙ³‡4œB¾Å?6ìJ'ç"Y–­ï¹“Eá×ukõLp1IÏÐ1ž§ÃýÚ¹÷áùË*Ù‡çˆ>“éJž»\Àßu¢F}p®S/ž•üÅ·LËVFª}3î·fÛÓèò»òƒfwq¥o¯Flq7’-JÛíèw_#ž#ùG_.ч¨•œ×nÒêˆyH‡™Cã¹'›¸iüÎPãð…iÔ¦ôŒ‹g}]©ÑÄ ·9õLx51wg2ž›ÃsÌù8Ê}ó燪qýN¯‹¹vâ0‹ Ñ5kŠ‘†\½òÂÊ5ÖÞðUâ¥+}ž6ûÑî«å¼ñõµ¦„“ëº-œÛ\öMâþŒ±Î/¢‚n+ ø\ ŽèÃ}˜ö’ˆTËÃgd¯Ži´ýÅÑ ›ÝHôÛþÝ7))I,iOÜ'—ëSôÍRº¬¨»{E\×y›Ñ¨“7I„8Ì }è87-ÆHeº4 i\)Î4¹u¬é>wj?B¥é4k5ûòåCöÁ>3Ù”ì[SÆÖs$îó&úZ>”|¿(¯Eoœ;ÙyïÔ±´w+ºìaöÓR—3±§´­È·ðö/R)4È1=ÏÒ“.&¤¤½©g•lßÕ¡ÒL&ökýU’ßàsùó\,ã,úÉ9™&Ý ÎÃÁ±Èª¥ý\î©æ‡Ç'O¥’³çЧ¾{Q¿¾•ÝÞõÔ˹WŸæøŸª °÷ÅÆèû¶¦uu Ïø¥â}%ωïó}ÍÊå¨âÙ ;Äö«Žu]Z(•’×Uš2"8UÊ™ð¦î†ö¯£—ý§yþ÷Ñâ~V¼Ïä|¼|þ©v¨sìÐá!E±ŽMH^?•šœêÎF¥R7Ûj=‹Ïõ¡è/™|úêå|>á~â¼&QÉ}tù}-¿Ïœ u²7êƒì ´Sé×T!)•Û|YQ|%­Y:Øz„ž=O˚ث¬N¾òûωà}ÎóÊLºÁõm[×;3ràA&æ4¤Òò.m£{¿6RÎµÄæ›½}hé£B_?‹dûŽï²u(7‰:n+ÍÓ²Ÿ:Ÿßp¿P®_“nPg`'Ûà Ûuàlkeh*Ó÷P\?i¤ãñ]§ß:¢¡Æž6÷\*G²²Ç~³¶ýú¼ÏD?ÒæjÔ¹`ætvňluÓ3OW9¤Q•à¬z!ëSè¥6{Æ‘·ÔÃd˜Ávé{ã É_7›¿wÛs&òAú#yP0¯\ôë @%].Ûøsî¹rj±iäÿ¶ÄÓ­¾)¤xýp§í-O2ûÖðç÷LHO/oãÏx~ Ïsý,ŸÊùz¢ßu»¹¾Ñ¨SùW£f÷³GBÜ܈4º»kìÀÁ=ShWë¬÷ý½h¼~ã«n‘,Lx ÐúË9ßÜ_ŸÏo¹nóçSP§˜…2Lsa?³ mk¸<-ìC™ÝÊ*)ä¯ùz½ç"/j]¿Y‹B,‚uŸ§ ®ßkãï;Ï{ç~½<7Pôil+ûštƒ:ަo?Óv>ÔxLdM-硟øâ6¨JÛ´ZíEŽYíÂ66Œ`¿]ÞtRÙiòó,ï>”}•»×ËNqlQ ÿ̬|Žê ’Òs°”ÃŽ:.—Ö­V+ö3űƒ}¹ŸFÃ"Ò‹ûܦAîVG yRóû&ô‹`Q£Ýßh6YžrxÞ×âxýPÙ[°CŸ>‚xî»I?¨#æÉìgûÊ ‘i¤¼raY_Õm÷(ÍuT=ç ¬ÎoÕ.:Uô•r—»Ê~ú<ÿ÷ÛÓÎ ?RŸxŽI?¨³ÙvSrõ3ûX?«š~>§Q í­$W›Û4ìVÛ[ß³Ü(Àsk¯9ìÙ*‡Ò.SüØœ©O/Ž®”ó(xŽ#Ͽ⾷rpP'mpfí5»ö±ßÚW}}­t:m‰r¬±ãí-ÊhÑáQd57j›¹¯ë—Ó¨SUŸa¿Ï_Îä>Æ|þÉý» ø© ï—ƒà0¾µ>”2?«j:u ¯ÞxOÒ-2ÅÅf¹’å¡ Ë_¹F°¸-³W®ògÜ“¯§ðq“ÏoÄõž†òs£I7¨£6ٜDZWëôïÜìÓIHóHÙ}‹îíŒÊyãJ%>ûW(ÿ-œµîVñ[éIþŒçNòõ#>à>ºâ|¹àó§Y…Õ‡sšêCâØÞRBâA:¹¯e%Þ¢Ãèã3´ndŠUX.ÍKüÙ”IsNw[ЊxÞŸò¿Þš>øËòÝåù¢I7¨ÓiÍ »öÄ2?»g;n M§Nšäˆ«n·ÈgËÖ×þ ÝidáÜ;*„³C½…Íñ䆃ÛVž›Þ’äui€u¯ßgχ´8&aÃÝ+½EÝ Î#ÚrçdÙXÖ£TÒ¡(¿t*;¹x÷þío‘oÜþêzÒü"ÃΟÆ×)þÜçõTÆsSø<Н?ð\Wñ~0HòÃï,êuž86ý^ó믬gè²æ±+ÓÉl jn²·ÈeÔ½Ú'ŠŒ%…{fÆÄ¼=_Æ×S qc¯yú´ç7|¾.®#ñ¼q“nPgò.»bGßïaÝ–üyG:¥·ï&Yí¸õe]ˆ†ö×î˜èÇíËxîŸGs¿V¾.ÅŸò÷A4ê”õ²T»|ÛÍÖÆl=R1!ž†ï+=y÷Mš2¾SW…—7 y?ªü—Iálç[Ë«›øJy>eäu>Nóz|½K'Dÿiê|¾¸à­U»Ýlv‘ЪÝo§ÓÁv•fN¿I%ûSŸ)¼©J gËÅ["˜˜KáËø:#_Çáã4÷mæÏ·<÷ɤÔ¹¬Ÿßúê¶]lËÀçšjOÓMê¨àx“~1 Y¯ÒÐOýÍëàÉ–7Vâ¦ÊëD<'@̧|(÷5Ï÷u$Ö1³ÄsûlÛÍßìd¦|ºé´QX²¾I¦Ø¨Ã^”};³ã­š‘¬uüž&›ù1>žñ¼mÞ×ûFõp®­~¢,#ë;lœºT­ìc¹'‡÷É Ö´ÙÆáÅ î2‹.xÐaõŽ¦Ùž¬gá2mülýd}‹üüÀ^ÕJÊõýÝç–ç%çŸU¨ÓÍd¨»ƒ­ÍV3hÄÛ'ûŸ½AQcÓkd5p§>…¼¿ÙÎZœ[œz鯔Ø•ø}R¡Óžr_’#k÷”ùsuÔ¸¾¶¶ç²-™1ÌûÁ›¥Ûm3HH{)}ƒ®–:ulÛÏnÔw¢Õ°qöálÁƲ2^Of…ðâ’®·&ž&ÎÇ/HëƒYòçž?70uBžm KéÃT'–,Û,ƒf<(WlýÌô¤uã/לÜÈQXN c J,9°‚&3žç'>gØÉu¸^óçÇGãú3?™¹ûx;«`\»`l— ªZ³_±`çôüê…Îl«í­7`X¿”P6:MëR%o²œ£òÇy3ï>Ÿ2éׯ©›53%z;Ërž1¡sùmÝÛü£Ã Ú~¡ù·ZeÜ©sùeÖþÖ¡¬n-!!Ú—qŸy~þœÉó ÄyRsâ¹>&} NÝa‹7¥TxÝùšW¬*e=±ä ªìÝ)eàwòiÚÖúilˆ”ŸãËøº2~æã¿óçLž÷mÒ‡UŽÊð«ð†lg!v# -š‘A4›î¼wZŽ­Už÷ §a=Û&l ‘ÇIžŸÆ×›ÄõÇyý„ç‘ñù›I'¨3Þ@´u¬ÐlÀ㥔üÓ»&¾G¯“ë¬ÅÁV==¨I\ L}BX^Õñw§ùÊ÷åš…Ënì}¥©¼NÃÇe>~X@§e§N˜´ Þãi§ð ª;2±v|ðuó=(á-þø:„Mê=}Ï—Sä\ ž§ÄçÍ|À¤\wwá“Üʾ«ŽL¸³9ƒbuA]½¯S|»öƒVñ ¨ÐÊ«Æ`’žk0KXcM'S÷ÏýÁ›‘K¥W]îÌ¥Ünÿzî¦2Ïä±y¨“[Y¸cnaç2í†7JË ]šßÅo7“©s”óÞóOÔ4hƶHëKalK‚±KЂ)Œ?Wñq•ç”ñýž#'öe±ï+æ¨ö•Û×XŸ·™ )ÖÉ2(ìJ)¯Â’éÅ¥û3ꦡ%2ëNvcÓÜ6˜-Ìxn0Ïoâó7~Ÿã:7õ;®ÿÜzèÈjá›Ù‘óù¾Ì ‡¶#ûTO¦¹£"]ù0Š´]—§ß*ùýkåüJž_È×!ø}›ëš¯W˜úuÊ|›ƒGMlót³e_>ePÿ¼¯NOK¦Ž-œ×|:ŠvœjUºFùP&Þµr¾‚8_ª)­•s%ùý'>¢uìFÞ è&&tifÑLZX|Ê;óáÉt«ùºåÙ­GÓëÕÏ7l/Ê.9¼,–<|²œ#Çs•ø:ŸWóŸ?Ç!u¬¯\|_sÒFV>±îÊŽ™tén¯S%:$SèýþOÔ_Ç­)X:”Í2ö ÁÖ—•L{Û«~ âëÔ|Ås)x>‘¸Î"í· NËÓ=†±ÕÑláíÓ‘KjeÒû¡­º<°I&ïOÁçuí\Ik ôeEW¬îv"ÛWÎK¯£’Ÿãyî+ï÷üû:Ô±MÈóÎX³]«:aÓ:“,¿{Y7yŸDÕ6©v"É*—?û ŒC(ÓÆ¶m^§¾¯ìëÏs—xþÞŽaÎNÅ5w¥u‰ŸGP§°GޱsÝõ̶ձ—{3I;ÅciÖÕ$Êk·Ú²ÜÚ}~ö.¿{!l|Ý©‘g†Mf|Ë_/ßãïÏ,÷\)GuÀà6»w¬eÂî×LJµïñjÑ–$ûÆ“üŠõ}ûLËý²©è×E_ÿ}EùþÍsxîK<4Ô™_ãVìË5¬j {Örl&EĶ9×_—D—ÕË­†{Ñâ–,ªHÑPV3øÐ«ú Ç1ñù³†¼_-æ<_”×'yäÿ}T¨S+ð¶ðÕlpµâ£›Lˤ<ógWõI¢I»gìµ(íEj Éß¡¬«™öyª¯·<_àó¾®ËïçrÞÕu¤¥¼ŸiÒê„þìi=mžEEž~Ùá—LZ¤ŽõŸd—DÍ·—ïVß“ªß_ºló”P&æ¿x3ž#È× ø~2¿ÿYεò{dÛ…øº¼I?¨Cß;é^\Çóí§Ú¥‹DdÒæ+“'Z¾¼Fžß’ÛzWñ Ú†v|çÊrú OâãÏyäë¶|ŒÏGWŒ.ãTŧ›¼NnÒê¼Ô…ÞÙΚµJñÞ²5“â>¯¼·á¸Òþþç{n”¨¯ÐëmólWbk³…ã®÷#jS–CÕ_kìÎUòuŽ-vM‹ïG|^iÒê„9Üx¿ww3ÅΤÏÚukë|Æ·ºØmÄW",cF‡0çcõ¶´.6Aºï¶–×§xþ"_g× ÚLúAÃú U£ö…²Gæ6‰û/e’í¨M“Ë]£7ß„ÕEНç-ÂRNÊ:4^Î{äy©ü¾ÎŸçùü1ÿü׬rŽjaTÍ!V{CXÉÍÃR•2ÉæÌîãKÎ]%ãÏ˦µ‰r#Ï/Ÿ——*ºì (‘qtœœ7,®ÛYÉúáûÉü÷s²Äç8;Ô9üðJ0;²óù±Zy™4¬Åù׿θJ¶ 랎8íNök3‡×;ÌLm½Ú‡ñ¼~~ë‡ÿ>|¾eÒ ®ÿÂñݺ1aAìÒ¦Øe›ŠgÑÂ+½·Ì±¿JÍÜž¬ØòÒƒnO<–4r{°”Ïü»nøx3¢ò„nîËãšxývÎG¨QgpL@¿’ù Ë´VYTÏ£ùç_n\¡Ùf¿<:Á“ž>ûõp_0û˜òúâ»CÞòýŸáûn<'óò¡·Õ‰¯ë˜tƒ:KŸ­>éõ26Dcq¥Z-½ïùzÊì+´äüª.›yR󇳫áýìû­þÜ+}äçQ>îð¼~?ø¦¿½-îDi~#öY4ê ?qúERl ‹mWüÚÂzY´ûÙ®bÇë_¡ø–£VÒyÒk•Gp½¬`¶OǵÆ1ž—þÇuP>÷™êx2 Nê¾Y£~ù¾Ý5ØÀ¿U9ïn[þúeº8ûøÆöc=ÉãBµKÇž³M¹©íÆqŒ?Ÿð\2>/à9ü>žüÌFs ífwšË–úíœuÍ1‹¦[¶è‘6ë2 Žìr»„­§Üϵ?Lõ;ï6NΓäã4ßßáÏ‹|~kÒ‹uŽêhóN®KŠOeBºZÔÈ,zgŒ(4¶ÁeZا–sZ'ú’¸§×áº!lJÞËÛ|äs|ë…¿oâõë˜GÙ¡ŽiÙÙÊY×Ú¾÷ñ¸,mÚ¹D=ZiߟèèNb.O«¥hz[1鹫¥¼îÁ÷«ù~(ŸOçŸwªPGì—ï'gz/5Û>-‹.®ß1kêªK´êØÀì_cÜÈt<Ë2„ ´¯7)À›ñÜ%>N‹}Æ÷ݲÅç\7æûî¯"¥ypý´êbÌŠî—èÖ¢²Úön4pùŸƒÙ×ÞOï/êÍx>Ï¥â÷Þ¿<Ï'_ Î¬Æ³sæò¢Œ€kåj/Ì¢ÛÛ«…žýz‘šD50Ÿ®w%qÌlÚû;-y¦abwzùtˆÇTUy=R_Òäý)~NˤÔ¹ª-¢¤åZÚ>íÕœŒ9YôeAˆeÛø‹´¥Íä)¬«+ᦲöÃ÷`&æczË9E|RÔûeé\œQÉÏ äߟ6 N1S€ m[ÛìýèYYtkòÆÈ÷S.Ò“ø*©á®4qbß.÷‚Ù ˆš+ý&“s¤x>.Ï)åçãø~Oþ<álÔùÙÁ½ämÅLj^rþùÃøÜ} vOn4¼H-bÒç¬ßåF×ú:¬§flýÞaM«N`|½ƒëEügƒ¼ÈŸÃ ä Ùä¨VÚ…»õ¼@ß]²×?‹¢š=lQ'óŒ¨Öþ^kêøËù9•ú³ÇßžN¾ã3q=ðþõ~RZ7`Êéºkóš7(Wh‡:Vb v/¤±/kÞ97‹Z–¹é[mÉ:lÛhÝOZ¹Ý·{ƒ`ÖÁ·\‰;ŸÇ1Qíäç4^ço=ªp}¯·›ï†YB“ÊuQDŸí»4ö}³ ”g»äµú‘õOª·x@û`ÖÝt`Á[~àûSâ¸òA¾?òû´I/¸þŽèMO‡¶_E»öW¯Z’E)CËèÜL$qÜö¢ô1l´k0kܨ_ï·DzKBpY97PÞ'>vë겉yò~nþõÇÔy»­VÅs•BèA¿9ËúGdÑü€sͦ'Ò°¶íR–}ñ¤ {,8±$˜×«½ÍK>ßÉ÷¿¹~åÀþõfÎ×D㺆Nk|¨W8Ù›&bYtÌúÈ(Ûš‰”ÛmøÔ/®ž4³ï–VkγÅ[Í«åõ‡üãªrßòub~ÉÿÜg@¥ªªuÐÓO«KMödQ`Ûšî[®œ'•eýžù/yºgÞž.ržIUrTýX·ã~ßÖ‘8OÏ¢L›Ã÷·9/or¥—Ê|X"Œû<ùóÃPOùœŸÏóy^›UšíËŽ˜Iãgw9‡Í¤Ôi+ S?GÓù4á @©“̓Ǿ>G_z//Ùy¿š6ø­ˆ(Ò)ŒU¹û¢³yâXÆÏò>ãëÍÝÂbî)MÑïGtø¹Kœ5êØÞ=Ý}ÌFÒÌX×—eÑçIçŽ=GÞåÎ6]Ó[Mَѧ¶] e oÆõ~âÍľj!çÂñõà_»¦{Ôºñõ;þüaÒ ê̶Ññƒï&ÒL·ÍX‡ÏçÅõF%Õ“ÎÑÄ;Kߟ¨¦‹„DïPfxT­£¥ãë|¼äóÕ»…öf'öû¦?ŸÒ¹Néþ‚:?[ÎV^ ÜL+ŸÝy·zÛ“™<¼ñ9÷:ìÖÔN®ä$l«G„°óÂ6§¥†õ0Ý6ìäûŸ·¼ñ³ôøû÷ò}ŒçK›ôƒ:ëÕßý&¦m!ÍÕOaîûp¶YÚ!ëÁYZ1$ld9­U½9g\ýÞ!ÌP‹Í ,ãÅøøÇç-âxvDyþÜ”B¶Åß*ùüV¼¿—õƒ::Xýô©Û6úeªÎüþö,*÷FÅz¯>KWšû˜]Ü)¨™°ÂnXFèT©òº!ßWŸƒJq}è’ë7ÿ9ÈlÔy±a{¡[¶“pŠ{ÆÖ,šs¬g|Ÿ³4osS÷Áîô«ç‡ÁLS;¥_ƒ½žòùQþzÅñà€t¿Ì“r×+HÏÅDýTÍQMœpwúc ™âc²¨hÿ‰‰ßÎPÌÊ!½¯½u£ÉC÷ÖÚ\$„sÌpÒu,ãç[øï#Ž›û”÷[XÆ5‰y+å”WòÅ:v¨ó°äoSÊßÙAu<¹tWO(ZÇiÿJ¨\oÐO›ÝHHAÝ+„½¦¯k4ò9U>þòßGì»WJ~þW\×Ï ©PÇJWƒmúNê2âêÖøYTÍ´Ñ|†F.0Ÿ_¾»Ýt]4BÂÄý2”ïk!.â>÷eþóHj\÷xÑíÊ ã»h²éÁ>‹t“·SÔ:CŸ®EÕ˜ëíJã\+¥µ»Â5‹®ür¤F>7,ηÊÊý%>ï–Î9T.ðúP'Ûë•›kàn:%È.*‹æ½Ñ·í”uš–¬z[väR5ùš6lBX6Ô¤|¨‘¿ ΃ÊJûhï”\üœ¯I'¸¾8_ÙCs¾lí>;$‹¾/‹õlíiòx~ÇiP=5%*­ès!„]¾jH¼äÃÄssåå߃âçÿJÉOq|‘t‚:Ñ™5Ïš-ù•ÜR/×ܱ(‹ìÇ-=d3ê4µò±)w~ •MU5a¯cÆEM/÷¿ñ¾âý+®›”/xN ufnz–Pîê^*g¿fÊ"ÌÃö?èÐ¥²íiò{áµ1oùy|Y?ûaçKÇ3~~š×ã¿x_ø¬äýÆÏa›tR-GõTi¶£ßÚXšà2h˜æa ££2Úe%Èë˜×wêÝuEï_ã×ÿøç¯éשÚÄä7òç#>׈çÑìPç5f‡.q0·w¸ýÌ,^å×3¯×%H9­jj~xæ‘+CØC}ã—Ôb㯗÷1?÷Æ×cø<¬€NP‡õüú®d©}Ô3øà/Qg+ž¶¢†%Ð}å•:Ý™+²NΆ°Kaq"ãó-®»‚ýüZÉŸ+xŸ˜tƒ:svùÝ›r|½èÖËqË„,ú•šVjT.AÌÃt'Û¥…g×ÂLËœ}µŒŸÓãë,|.ÎcÞÊÏ•ùÇÿÔYžöµ¡aÚ~ªWiCa5æãig[Ý8qŠ\£Z>ï<Г²|ÕíºƒnŽºõÐÍÔ2¾ßÅçü¼“˜»ûVÉ×-ø|ݤÔYg9SS£Ñ2mÏɢߨéoguœN8w™Ökm&ÕûÈ-Áö5ZÛKõÊܾö¸Ú­lˆ´~¨•ÏŠó˜¦Äד"KTYÙåEar<5(1|F*ðýÔYŸì\7xöq:µÊÇøÛ’LïÔ½Cü¢£tܵÆ%œÜipa· M1Of‡æ`ª6‰=rÑÊ×A>×ÌÏŸòy™x>m€t.¬£¨Ô /×ùVέãäûfEÎz¿Ljû¬‹Ûä—G(èëâá½ÝÉ®á }êã`FCGıçîâë:ç#–¶0ìüMYrK š¾++9Ål_e@âzJQ7¨³¹¸²X£þ'¨ìûz¿<ðȤåWŒw?:¡ù­}_Ä6u§®¶§V„5 aÝ7¬jq¯°–µ ,÷<¯™ñýJ¾îÇóÜß Mi3¸ôþ‰ß0 Î/ß§ù)ëJÒLê>Ó5e͉xºÔ°|¿î×ܨJ«·´§ËöK}RÜo2ãã× ß÷ÿù“Rìñó}&Ý Ž¿uè·Ñ'ééÞ¡.ZʤƩ¥/}²'ó{wïõ«íFµl¦Œï*½_S˜8O±‘÷sø>¶8ÿ+$¯7ñqÖ¤Ÿ9ªS5æ|›™{’ÚëêµêÕ8“<ÝŠ®?»ö0÷=¿Û~’+<ׯL« ¡l·0Më2E:‡Xãÿú}Æ4OmZ{W!ù|bï]¢ÎèW7k—ÁÇ{$ð·3»+gÒáæ[7·â>™ uöôY‘tí&£þÝ„ÎÍ öZßkcV¢ÕŽÔ91ZM!ºé퇆…°Û¾‚r'2¾þÂõÊÏUŠû~Ÿ”Â))ËQ=i„°ëÛIÔêôÉiºd-íê¬O?‘‘Aµ4{ºÅüYí˜ÑµQg5]̶Ÿî·2˜MZ7÷Øó” Œï÷ñyÑ˱cÿúéµòͼ“ŸÕ)Dâ÷‚œ(òá³_¶‹}€:–nMw¤M4к¦Ë–\9ŸAÆîÉEj,8HË#ú]|iéJSì¬[ýa´Žy“4ÞóõþÜ'®—–îŸN$ö·¤á÷)ºs®Å+wãä–Açã(êó»4gx´"½­Ù°=Ù×*˜50}Ai²ü=_ñyÐZº¿e*uk¿f4êoF#szãŠNÄ÷³LúAnQOF×?E÷ü5.ËfЊA'ãâF Ë3×z'¼Ç¸ópfZ­BXòŒ¼?/œ*o„¯7ðó&â9¦¢Ò¼h ‰ëë¢N³QçDŒðÒSä¿tȇþzZç'±û){ÓËѽúyÑÑþÂ7vCÙ³í†Á4¹ßøº?:u~ðë—µ Kï×âç½Mú±ËQ}­ðêöÇçÈfïô!cfP?ÓƒÄ>zwA58i³†¢'F:| cå^^M›Py†´Nã ¯Çñ}ìa̧Ϋ÷UÚÇK^ÎÛ_;Þ—6˜‰þòfúË›éßíÍÄ}Á„ß/IøœþE_îE÷÷¼MþžÝŸñÎïEçòOzÑqŸaž#ÿ‡,Xžf!yn Ùˆˆ®ª°†òç}N~”ymA‚\àa€}>¯É“Nð; 9À ‚‹—2¯òùžü«^Ã<+ìeÂò¬0îÓô£LØ?ëÓijÂþV¦Ä_cß_c_€Ùnì3—^¿QøЄÑÀ¨F BCÆ 4¥dG4g,°FƒHžNŽhÔX` €à„¦ÿ¯ÓŸñáÌïí¤þ'½¸'÷X7ü!#‘gèXKþtB^˜uUa­¯ 8B(±Àºº°>‚Ÿ'ˆ&ÞV˜ïã: ·Æsaí!¨ ÔV"p€¸‚@PCdñÀB ¹À‚3H¹° 8ýü8y–Î?ÊIäy:<ûG9‰6›çéü-Ÿõ?æ…ýÿ:îý¿8æý·ŽwBÏDóчÓâøpþ=_õ”ŸóG_õ ÒWççð\D!ÿÚ ˆ·Îðüží(e…9B±ÀúŸðáüQ¬ döQÈê|>œAù|8ãÄr¥,Øø|>œÿJ~ϾVA1À¢ÔlàqÆJÙ×RöµBæ« ¢® ޱdGˆ9Xç˾þ{™ˆÍïþšß˜ýçÆ;kéõe ï3š0X u 8¢!c5š2ä'4g<°Cƒ‚à„FvhÖ@ \д`‡Æ ¹À lö·38 ¹õRƒk@"p@£ë¥f×€$ °¾ó­ ñµÀ˜Ï»“û¬'þ!#‘gèØ:)7Ì ¹À B‰¯&ìâg ¸Ø kคòjü8Ö‚ÒK¢Ò€$ €¸ô’À4Àì!´ Ô\¢”rËÿÀÓ“çèü(+1ŽÏÁþQ^âŸÍÁæ9:Ëw]èAáÏbìûw{yÿ=cžˆh<-00X  u 8¢c52ä'4f¼ä·r€š4Ø¡QAîßñ[ÿGÙ9ôZ×ÿ“^ë<;‡g$ºX çÚq}›ßs¯ªˆ^ÅB^˜_U8;$œƒÁïœ ŽøêÂ~/^'È.5„} \ÇîÇy°j $½$& H•äˆËì!° g/fÂ€Äø/fçØå˾v„0c5Är€D/e_JÙ×*6X@´: !ÞX` €là!Çkˆ9ä'ŒqñÀ._öõßËHükž÷ט`öŸóì¥ú9Âûˆ&ŒÖhÄœÐñÀMr šÓìÑ A ¸ Q ÀÍò€M›ìѸA ¨ÑÀ‰ÀMòJ ^½øp@C륦ր$ °<Õ 4¸$EÁï?C³kÑRðu€^_WQø¾³ðÝ]ü,ŸG{Ò²yžŽ… å†ÙC A ¸TÎÏágKÈ«.œÁkŽ^ÏòaT40‡¨´ÀTB60‡À´ 8@hzIl$åÃêAž ñÅ;0äÑì!Æ Ôe"p€0õ’8y®Îòóçêð\ìå%þÙ\ìÄø¸ =)üùÿaìûo÷þ̘'Œuÿ“qî¿mŒ>£X`FÓlàˆ†‹ÖhºœÐ|ñÀ r ÑìÐŒ ¸ ) Àò€ ÔìѤA ïOæè ¬I@Q^ð¿335³$…¥àY ±µÀh%xz`\ùrt„\ļÊÂ÷_ÖõßË@ük÷×<.Àì?7Æ)¤ëç ïš0Ø¡A.pAC€=š2ä5š38 Aõ ¨Ñ¨‰Àͪ—V’€W/5¯$šX/5²$EYÁwýަÖc9ÁOc°@ƒë€±¼àõˆŸ 4»ÎRð4C écuEÁëÚŽ@,°€tRÖNþ\Džµ#d^Jyaˆ^‰$ˆE/ F’€¢†pÆ×±ûqþ« ‚Š•dGˆ+X@`:Z40‡Ø´À(å¿FKÂÓ°‡ƒ@PCˆ‰ÀbÔK‚Ô€$ €0£¹ÃïØ¡ù@pBÆ;4b È.hH°GS< Fs&{4hÈj4j"p@³ê ›дz©qÕ 8 õRk@P”¼óÑïhh-0Z^Ð'€9š[ Œ¿Nü X ÑuV‚¯ú  ¬ÑôºJ‚ç˜-Ýü5 (l~Ï»VƒX)cL ò„±BI‹^Œ$…Ýó`µÀTT °€¨´ÀTWŒ$0 H -˜Cl º \ >°‡ƒ@PCˆ‰ÀbÔK‚Ô€$)#QȼΞa!P°‡Hƒ@PC¬‰Ræµ^ʼv‚pãÄr DlörÈ.´ØCÔA ¨!îDàðƒŒ0ž ö×|ï¯q/Àì?7î9Jÿžð>  Àò€ ™Дz©15 (РÑR“j@P Y£9V Œ@Ææh^-0–rï„ 7ü ¬Æ2B&Æ`¦Ö•²6ðúÐܱÀº¼à/ø¡ãghôX`m)øüb¬Nhúx+ÁïÈN@<°®,øn R³u6‚ˆ˜ƒTEð6ÀßW¾‹Ž×‘h@PT¾7ˆŸA0Z`´¾ƒë »çÃ:BP±À¢ 9À âŠÖX0„,„œE-åÃÆsO ¨—D¨I@1FsR Œ@aÆ ˆS’þAæ¢9D«Iùò°”½øgò°µœ¯(ô¨ðçÿå±ïGãÞ¿{œûo8o÷Ƶÿ1MxŸ Âg‡Æ ¹À föh² Ôh¶Dà€†ÓKM§IÀͧ—P’€-5£$š2˜£15 (Ê9{øšT Œe…Ü(hX auB >o4n,°@óê*^õøš8X[ žÍ/€:¾¢àU*ønâ¿‘²®ÍÑÜZ`´|¬Ä¬k š< (Ðèz©Ù5 (l…ïqâÿCãk¨ì~œ«ÙÀˆÖ‡dGˆÄ"Ñ#PA,1À‚Ñ‚$ €pô ¨! Dàé%!i@P@PÑÀ¢Òc31×:Ox^…ÀD¦—„¦IR®µ gö]Èjˆ/8`œÊêv?ÎråYÖZ`*ˆ4iIº¿æiÍÓÌþ³ó4éß ¨‰À¨—šQ’€M ÌјZ`*!ó˜£IµÀThÖ`†Õl BãÆ 4¯dG4q °@#ëÊy›ø:X—rä0F'4w¼…‹„÷ä'4z|!»?¹ÀÅJðxG¯¢ñƒ@p©$øï ^²øï@p„bµàm‡ŸÙdø}ANÁO ¯ X@$Z`¬&xbàgÀ‚ÑÙ ßñÆu œX`m‡ë€àÅ;)ä'*ØAT ¸@\ñÀ ÙÀB‹Ö[ÈN],°€ðt ( Àh`j¨ Æ`Aê@6p„0c5Ä©F €H£9„ªF ‚`c€D«F)[/ X’€BŽæ³öOfdë@6p„Øc5r€‰½*µÞ¿<öý»Æ½?;æý™ñŽoÿÓ±íc^öïÏþ]c™$ Ÿ *ä5+8 ¹ôRƒi@P Ñ¢9šM h¸h`ަÓ#P¡ùb€9P Œ@…FŒhF-0–r£ñ3`ÆÔ•rPñÙ¢Acµ…NhXë Bîþ8¡qã-…|¼7 ¸T|÷ñÏhäà„fŽhhµàÅŠk£±c…½4¶Qx¶Ds›£¹µÀX]ð#Áëht] á{õøÐð±ÀMr€š?^x®„r€Dì „œ  B²#„ ¬!0•49Ѐ$ €`¢9D£F ‚xb€…ð½ R´$& H ˆ*˜CXZ`*,¨!²Dà€1I/M04 ( <½$> H ˆ0˜CˆZ`*2X@”: !ÎX ¸ Ð?Û’~pn$š~ü|K?ž3þ½µ=¾—›D×úŒ¢£éÏéïíìrT™ªyÓ^?9%åж¤¼ÏgÇ_Ø™AŽò~TsòWT,úÔ~ŸœSÓjëÕ“|¤¼¨/Ê_Ÿ<ûÎz ®„¾ôäÍÏ=FšÍ`¦¸‡7£DßÔóœHôÊ "ýÚ´G+¯›µ;>sÙìVÜ®g¾–[î¢cc©_Üuf7_*µNqv .}”|üšð @y3Gäê–±¶w±€ Òš Tö’ÓÈ'¡—|¨bۉͩmsµ&¡÷O~RnXMZ¸fyÃÓñäüÀ¿ªV?KNP ë×9ª€9wØôÉ ßÞìÞ¯£3ˆšö4=t1¶¶•¦«7ùzëR£q(kRônIû©Œç8r?`î;êVå®ãçM×5àºî‡<†,\“@½ºÜ9ƒ~®u㙺ìnz0LñÝsÏXºžø¸â€N!lJ¥÷Nñ•}?x.÷5âï‹pÝl\7¬ÅŽe ”Ø]pðÎ ï¥[_ÞI–—ï•sjäEO–ÏÜÿk°œ“Ãßîë#ú£=Rٸ˽Owñºf5¹_iU]÷fPµâä¶»î¡=vÐÆ§Ï·ûìAM]v–Ò­f6š&X=œ"û¹‹¯[E5Ë&Z¹=x&å{|WrXîÿaê{ÔùTƲÌÀÜú’Ш×úgéä8ö§Fo>o§Eϯ¸²ÒƒZ7Ôî+“̺[<¾[t›/ãyŸ¢¯`W©Ÿ)Å\u3jj2PL£>EniqVôGR¡Ž¨‹Ó’¿Z:5:Ò¬ô×ømÜßr|…)Ô`ßm'Ÿf!,êÚ‘ôw×§²"á¯~kÙ´£ô¾ÿž¿R£L_Ë>*#BÖ ;é$ùrKý:æf+;ÍlzšÄ<Ét*¿¹—¯ßVнMÊ”=TÁ”Pͺ¿»mp.áÏ& ©Z|XÙ7Šç´Ÿþ<Ágñ'åçÈA…ݺPþ<„Ô)Û«IXßþ§éª·žNsýJY~©»…>,¸4õ£'¥UX:HÂ%ï_ãµ?ã9.<¿’ûì—}é[óóÙBrÞhþúhÔYRg†ÍÙ)§iȦçMÎJ§§[Uørq…nX¸jµ'5-”·}ð’\nF›]ÊirŽ÷•}ß®)û½pÎöëi!çO‹¯GÌÑ2 NÍZ­_¸E¦Çý½O»¦Ó’ISJ$ÛHsƒª¶Ú™àI=¢tÅu×CXâ‹ÈÎ¥§IùHJYÏܧ~S‹¹»ì­HÌié#噈¹½Ù¨³ê|éÁçØiòjQíÚXU:u¬¼±å™ÒѲã†j?η eÕ^Ö>\/ÓŸ9:}±öMéB¢/—JÎ÷x·²Äãí-hµ…o¯[þƒHÌ9ûÀ¬VŽêÍùmV¼8MbîB:mW%y±nÝ49óÜàAé•<Û$Ö e¾G×kSc`²ýSÑ&óé©C•²_Þs¯g‹¶1£ÔPÁè¸+q¿w“~P§ù¬n}—Ô9C—ò>ØT.›N5&LóY’Eµ+ž«4¿šeoX÷bV+¹²Ý5ã4Æó&yNÏŸË7tÅÅן”¢/R£~†*Ôé×ÿEéA#ÎP„~lÝ=yiä»>®YªNO]wÚöœàNǺ«wOR‡°Ïq½æœÙ«“}¹y<Ï•âþâbÎHcÉ×PôÉT£ŽIîóÎÐ4V27óQ%?™Þ¶’UYêõà²ÁMÊU fbåtÙÿ+â}¤ìgU ï ×_.üg(+¤Zì® iô,Q0’ %ÇOß)ª»Ñ¦ˆGÎæ»‚ÙØ£)½f›Á¸Ÿ3÷Gç>yÜ•û%æÏ7ŒFŸÀ†_l²ÎЋw/W}Ø™F=Ÿ\guƒ)(|Gèì®4\°·.ÂÖ½š=yGï™Rnßÿaï=ÀšÚÒ¶À† ÁõŠˆP,ä‰{TX °bGiÁŠ= (Š v쨨Á¶"ˆØ•bűǂÆÊÿÙÙ{mÑ—3sÎüg¾y¿ë“ëú]3rt¯”ç^{µ}ßædÒ|êÃÅö«¶?èS…íDVi¾²óëtÎïý&h¢Ô_üf d}5ë&ò€WFÃÆ(÷GU@åš_ëš‹@óâin{_0æóHËæAb;U?Uwò|—'ߦÏ:.¿ ÆƤ'¯€»0cìq;ßÝ7ÊêK$—gDØþQ 4g–ÍÉz"bïïf@ói zi¡ 8×ye¤†a‡¢¼Ã'Þ6v)4>»dÎÃ;ðÕb©ñ ƒ‘Ä-:Ø­bi —¿dÃûñå}={s¿ë{.§¤?n¢¾V½`;çgÖènª† 4óuï›PÃ5÷VõaðV_åÖÆ‘aŠÁ¨*‚˜Œš {´ê/Nóïžù¼yFvE >êeû31¶S<½–†œï\>ûFxêÊŽžxµ?bý]êdmøå¼«w&Aº¥!P‘× Í×£ý±¾dõ¹\A{ Ï lgJe¥pìz5|õñ ::,¾hº–ŒÞùn°˜Ü€p/M9³y¥³Û Íï ý>ÍÁ¡¾…}àuë^=t`Ë75l¯™4½Uœ}®=ßÏ.’ìöz¸"Ç ¶ÖéUe|jyÃ|ícƒ¸ñX;Þç“æ"°ã+öº2¼îó»+Ò+Úg@¥)UZ¼¯ZÎêm<ŠËÏ™Ÿ$œ•A¾¹'%ˆçQ?ZšãHï#àu;שQÜÈ'²ü{l1.ɇý÷3L,7á3T–O„ƒ¯™@²-¨gõª‚`¸}TÌÙ<[.¿¨;7þ,ªW•Iºe¯›€×­mCÕ 8¯•[>¿•áÇâÆ®ŽØHFæîýäÜDXÛ0qUè–âäm?~Øì`Bûÿá%mãj]éÈå\Z›a´Ý²ý„ Ûa}˜3`[±@qÛ>¹Ä¶ÖfâyÈg¤×D/˜1;úåÁ£„õ• &ÔŸÞÿØ~ûçÓG}ùqóù´½µìôã 0k×_#ŽÏ‡æ‡×%µÜOÆÜMÒT+ð‚.ꚤ¤Goµÿ£ô`>’úÄÒ¼xÚê¼¥V¬Ù 9w¿R&ŒxqíuLX>†eëÈô‘oÖj½à\÷ïò×ãç~ã|ÃÝCOÐÿeõzC4ÓokÕ”ìu…x]çNõ‡üÑ*Þ-ï1à¤O><û>ºÊ“­äû[ïöªé ›ïW DC¼ I(¡~øl.UÎÿ÷6çÍæ—ˆñºôÃk~vÊ„÷Ç7V{íœãæ[¦¸õÜN†H¯Þ¨áî cª wª_¬Ö÷d(ŸE}xiý±ý{?‘áu׌½x¥æLn<™þƒ6•bw¶a]›öžâ ;öVÝö²‚‚°vîr.»=ïLçcgã]Ng_¯¯ûåÛ¥•uÜ3Iãýdš÷gVV<Óí$!C‹ÒÓpÑhy¶i—ƒkIäºåc+ÙÉyÒûÍ# ¹ 4ç‘֡αeÜU5ffrã“<²wŠY†h7y½jJÜ´µ^à°Fºr˜±‚|pbœXå¼o,Í7dsˆ^‹XŸy£üœUxý7cg¹¾ Ø|…'™Þ÷è8˜újÒñKÙ|#+­x'ÙºÈ'îÇx3‰Í½Ìô•Çã½$ âí˜Þ<¸õ†HÎ7:„ÐÜ7šßHýâ+¤4´`V¸YQ°îuáÇ1`;lÞe&´¬¸ïéãyyå.ÑM(ÝGr/ 7úò€OzíÿfºŽÄúº8vN0é³|}øõ6¼þ©opp|ɽ®+B›.EsÒ› ‚ß*3Nž¬©Û v3¡3¬•ª^òzi~€4_ÛÝÈÉ×þˆ7RxtÙÍÄ- â}iÎ ]§a}ãk‚;cÏ]×öÈwŸ;l(«l'ç[é¾¹™0å‚ühT»‹wœÙÕ ¶£¶§¶÷ôLhm5]¾ñU.Ô›ÿ¢½¸K)Õ4´Y8y"„ßì²jaa¹4§‘ßžÓÁ„úÔÓþ•]·ÈårIŒy]º®gжS¡…ÍdϰLºö®À8/¾ u”})H"—6:§¿2ö†•3¾YÕm® âÝfÕªJ¨ï=Í¡>Èì|²:Ü,Êx·¸e‡òi ±“ïlx¯È„ë»ižË…WΕ³N„&6h—M ôOÒ*wÏ›ÕÄÔuÒ{q[9ŸÿMÇ{t}ö4Ïõ‡ÜËVZñ•wµ'n Éf5ïÁ¾\° Ríèu„4‹ó÷†ž¾±Ý%µÎ‚Õdæäœ©ËÇÉù|Zš;Hýbéz×” ±¯ÃCÚ4šœ »-ŸùÏŽÎ…–ƵûT=J¶&‡-¹sΨÝÎì5d{”ùþ½B¹œ kÞ‡š®¿±óJ#~Vöýˆ±Ýs˜„ LèÕòàÄq3rÁf”M£ÕyGÉ™>ûìúè†xýZ.×;˜Ï¦ó/š¯Íú"›ÃÓºe[JE?äEʰ‹'Dû&eÂÁšL@B.4 ÷î´lG2ñMŸlá5]2È¥‚°¯/ˆ$z±I‘ëò0¡xé' y„xçyŽ«ÏՈϯ3èÛIö©Ò)LØÖºûׄX×±}ïZûãòˆÿ€qC¦D6 ½Áò+Á„®ÑümêO<,w§! píΜ.¶tåó^ úÁvÞݘÈ'fBDU½}dÅ\Я·¶;Ûä8ÙúÇѧ³Žyòû-<ZFî|”Æ[BùþšÞi½ÕùÒ2FXP›ÿ<ËÎ÷UØÎB“W®a;«ûøVlœwòýJ'xkŽ“–¿5Í©»Ë '-yX3‚ žáZ0¯¯œÐ¼zÚÓùϲ~÷ù¼Oƒ~°[OÂïgªc»š_o@„{NïýN–5Ï[†½@5G½J÷^A/‘<ÓFNh.$½/МUê¿Nû êûlеVÌ~_™ y0«U~è hß6±B²©’âwEìúÂGÍ-(›‹%ÆvÒú¾|òtM&¼ÉžwâHŸP¥_ÊZ—¢¯g^êúÜ æ;ÅÔmºEA®¾Ø"ÔVλs9…ùõßó÷<ŸŽìdåæ–c; ¦våŸ á©ÛW·¸õûìüšB’F÷°ðõæ^·‚Ü8Ä|!rÞ§šŽ×h¾³:ÚÌ.eMNO­X'“c;s­ût$9­Ýµ céuhw}SÑï»O’ËvY´Óü'lÖOêã×…F »Í•6Ï×èú?í¯ÙusöûHÀëš>i=3\™ Ûç2 p×?Ô†r·SdJ;~ÍNxíôó_ÍúF‘s?õ¿CÎß輌ê’Õ);ÏQáu'Œq·Ä×Ûo…§®ƒæy»»MN“#wb>ú9xƒ³x[ÕºÄÁeÕ[‡­rB×Õè}ŠæÏ²þûÜþ^×ýúK7ã³ØÏ;œ}1ò:\"»UÞšØO ¼8< ÆÛµ8ídAº¶zŸ-%çóè~;{-ÚÙjnHl­ïý¼A­µân¢1›í.â}¾—bQmŸëÞv‡Û„!gH%ÂÚDÎ,@‰Þ˜ói¬‰œÏƒxy¹SòZ{ yJ´ÿ+›;)ÄëO›1Àã.êlT ·*âë°oßîÏ;Ÿž!N©M;Ïð˜ëFd¿h¹:‚¨.ÎÓÕ©"'l^„—/ÔŠ×ÙíÒãwNÿ$¢ëû†ºÇëà¿Ê~• 7mšÛ¯?¾NN£EgÉäç'?9ìÉÏûXy97OnÁ¯›ÒÜÉØKBí· õŽ×7üõ:˜lR¹o>-¼¸¸!Zfcñ{Îèù,›EvÍÃHO¥Iµ’wÍþG~[ï_ù|v:ß2Ô;ó9µá1¥™€‰¹º} ";è±YA>y¾R7xÆÍg¸qÂ÷œY:>¥õFçÑ]àõÃßTj¯IN6óÔéºÆïÄn©@¾T¬\êû¬vÝSAØýÁ0ÜìÑéÒµ½¹<’ž\ŽõmQ”iüªî¯Ì¹ñqÏò ÚhÅ­ Ý’>õ¿·ÃOr j\Ü{úk©ü‡îÃÊOXѬëF¥«‚,Lül´£KéôÑ'¶ñµÞЧ‹¢C—}øù.3Úì<Ö êŠÆV©8v¤æ£âz±:Ávööíc7¿‚nFºZLëx ©äӜ׿>Я§Nÿþ`©”¸Ç{rpÉÚç1iÐÜzºånüÚ]=´&¨×ÚÍkß €®tƒíÄã¿úýQ&´ =Û^º7ÎæôéŸJ¦õ,¾yÚ°x,¿ECÜšIq¬sm‰ôkO®ŸéÆéó ¿ë›5Wöá×× ºÁvÎÍ\\ãQA&Ä_k‡C½Hí·}prI*Ù|îžÏ Ú/ÚãvÍHòæ¥F:õ[(¡çØñ\Ø&𿕯@Ç?Ö†  Î|±A?ØN-/_^Ê„:8‹Xí­"ò¤‘èFŠYÃzÁcÏûê#ÈíP¯n³„p9A¶|ž{¿*ár?ŠNmøüÈZÜù‡ui£¶Zq‹…3üŸÄdry%90gcÉ™ûÓÈsã=j¥z‚Ÿ½åËÃï"È”ñ“‡ì !tŽÞoi».øEÄ̶Ì;ÝO0èÛ1­l.ŠòÍ„ßGô¶í—VãZÜió0$ä*lQÅ '·Š$Eëê¥Ý0–óës4gŠŽƒi¾ ¯”͉c;M:I–Š2¡’aá>¶î¿,|×0ÛQ_¸–²-Ê–ŸÐý¯ì,Ï*ÝJ?s㯞|‘A?ØÎm›-û3 Y³¬ÉŠ«0í dÌ´¦jë˜ro«µ JŸÝNn¸ç]ÕRF\Y('l>u˜îé<Ôjš=°óá'ü~1;/·úaþPˆíÈšÍò–LÍ€<߮ٽ§^…ag4*S“ÀÌ{ûçM“Áé#Õ+çÝŽ Æõ?–ôžBç)4ЖÏW¦ù@½´ÓŠÙ:Ï€ƒŒ®ÂìÛ÷ÖFlS“Å7³vØ$ƒN+¯Õ;’\r² z5]ί»Ðõ7zÿdÏ?|Ñy‹A'xý-˜’7ï:ßè*L¾¨¸’ýHM¾ÞÎŽŠ ‘¬ÅóƒòHÒ¨c“µã儿@Ñõ#ZOtýŠÍ‰çöñú³Ÿ'ÔÎ³Ë€ŠŠ›8’Άaâiº´ÌàæŸîpá´+³Eþ°\¶¿(çæÙ=øõZ·´ÿ¢çGØq1;n•a;É3ÒÇUË€9][ ­îgC³ª£l—xf·{g6{>F78qÕèn$aóˆäܾMW>çýœŸsyæzÝ/.{ÿ—c;ß^O8Öñ¾žeZ}´ ÙpåØõ¢›3ÈûÒSöt/‹ë>úcùV‹Y)s¹pö¼ÎØ<ž\>ç7Ñê+^¥=j:²ºÀë›åßxÔzŸǹÑÑV;³añˆIë^Ë -¾·ùr4X4n·yÇÇu\n¦œÜxºoŸÉ1G~ýqöˆ=-"2_p÷ɯœÎmè·TØÎû¨›+}ÕРdÀˤåÙ0Ú3eÛJ™äí²êŸKÁÈaeƒê½£8ÝÉI‰×·«~Ýøs3tý¸M¯„'·õ¢²ë7…xýËŽ5‡/5¸³!9ÔeþëΙ$s½Íü!£Ý Yd³ÌÜNQäBÜF™cžœ¬¶HHµã×·èyv¾‹Ÿ“aÿîÇ÷aÔ^+ÎîÅ쪡pÉ‚¬AÙP«êâ^{<3ɱ“«œrƒØÑƒŒýJב.Å…³-Ãø{šÇN÷SéxŸ¾?:7èÛéoè`ÕP«o˜(¢U6D¯¡o®À÷ã•$Þë}TÍ\ö¬#Õê÷XÆåÎöfÍ·‡ØÌ–ŸêQ(b÷Ÿ?‹–ã$Úèa×ò¹ÅØÎcï‘uéPǰ0™ v×üU™$ Ë¦Ú¥>R˜WoDȈëÈAÖò¹õ’×#‹?‰zp×ñyãôÜÍãb×ÝÙvdس«çs=Ü’»^žšzv.* 5{›I^¨_ôV<*¼0k]Ù9ŠHRß"GúzÏ—N]Ù±;.ˆ®\N«:x÷._ ¯Ÿ;ÝjWõÃéðà©~cW ÄÁmQQk éâ.Ïo?~Üí>ipxJ™ß<ü­¿É"RØödɦ&/ÙqûcoùÜç>»‹ïH%ŽüüÆ lÇ«û½a«7¤CƒSÁ£6¶¾k.ØÂGCº›ê.ž´w£¯gCß>ˆ"Ͱº,v,$yŠPs ó>šÿÆ~>ßDì|Ï‘_?2èÛaÒ›†¤C’IЉù/ƒ!æx·†Ü<˜]åB¾ ºßoéXEîV_·ga¡ëtÝŒ®sÐõUvŸ½+ÎÆ lçH‚­õ5ßtøÒ%=àÒ¾ËÐéMÚˆiÏ5„Í9ö€«z«‘uDh}âEàõPBÏɱû®ßóÅèx|&³Œ](æëÝ ›Zñ{±_âðtPÿlè¼Ë°Ê­ÖÐOvçÉÜÉ¢‘.Û<Öi²lÖ‘ÚSÆ•¼Dè¾ÝeÏ9Þ-^<'ܲTÄž‹íÏŸ«0èÛyüÂtw%›t^·«çÕ—áñøM™•ç'Ì¢£qötLà–½½Ö‘Ûf5WÏ[@è9-: ã :®<8àDË™ç=iÐ ¶³öÛ†ÉWMÒ!ó#¡ñ•/CuKëU)ç‰*p[¿ …îpCh8 K8Ô¯[Ù{?®¤ósvÿø©ˆ®ß—]·áõžÿq¸îù4h|úPÖ’¬K ¯x½âûó„½ÿmù‘³/ECŒkP aÏgXñß?]²s¥‚ŸŸ×÷ü¿]·Kƒß’ÝšÖ ¾%ëTòi“Eny}t¹J _F=KMvÖ»'ÑO &ôú4wš®4z{ϺÉéêüü¨ìûIÀvv½Ìê —«†´]"îp :6Нà/Í"K,'Ä_v…á à ȉ&»«TÚéû8„°¹Øøõº¯7ÿnÇÁ&|.0ý{Ý`;ÄÌÉä4Ølw±êÍGaé·ƒ³.Ì"g¾¦øj\èy7ÒPqæî××!üz íi=Ó~“ž³üá~ƒí¬¹ü±õš i;'ãƒzçEh5ë‡boɪw÷qó³#!/\öÛ¸në‰A¦‚ ÇÑq;_~ÄŸC¥¹´ôœ¯A76Z±‹«¯pý¥T`ÇqAæzQNiÐ79ucEhÚ¡ëË(³õÄÈõ¶Rý%ˆÏ¦ç„èþüãA6M¼nŽ>ÌIÇ|n«A7ØÎ izRZL*Œi3½¿cû‹ Ë™åSÿK9¼7Å¡QcWhufzЋÑDÑñI¶u0þê“~?q–& –Ö«/ÆÍ¼ÝÚ hÿgÐ ¶úaM£:ž©0>L{~*š‡7[du$l[ŸyÉR™iæ¨h²oÁÇ8«N!ü~í‡ÙõŸ«\Žz˜þZ_áBOþ‘A?ØNŸíÝÃãZ§1ÛÜi[ò¨uäêÝ%#.9Q‹ * FCgïÝM¯Gv¾Jè¾]o¢ëÝâ59|тϙ,;•c;Wlf»kÏAD¿JC‡]€‹ó¢ÇD…\ â´XûwWpíhä2ãáz2øsÑ·§3ByÒþ“Ž–ž}i±}¾?£¹¾ý`; מ[1òÀ9¨oN¿M w¤WÝ}´jíÚ¿Ó©‘пøKÝ̰ D1ððö‰¡ÿãíèº7ý^è~‚A?ØNn×Ê^‡æFK׆·¾ï;ׯp³Æ˜áv¨ùö #Àµ³cµ°Ç4äüyšwÏ®gÑš3—®nœôED÷cËê´ÛñªðÄóÚÀs00ùÑëä/YUÃûMƒŠO`EüÀÓoj ‡>ámÿxµj=¹Ù8»9¡ã#öõÖæóçÙþ새žÇ¡çû ú±ÕŠwÍ\´Õìâr³³ ~iÎןµ°°g{M_ (|ÚmºaˆÁ :!bÿ\*¢ó š»mж³ºlæv œúšY8ì΂aÞUµ}§Ùç:»º¦ …UæM¦þvx=Ñ~¼ÖoN79¡úhãâÐ ô}þ@%Š×pŸuŠÐý„²Ÿ›ÛQ>¹²#\\ýVNÊ‚M°ë˜ßs-Wº7ìÚèXò:{Þ~=›®ÏÑq!;.Hçægf|ÎîëgØN¨ºR£‘*Ø`üGëÑC³`[²ƒ>õ‘†ÛîØä×dNXÇ6Çøû9º.Bï t_œž0诿vÔ±ÛSÁ’±¢ÈÈ&Y`7rG Ÿ;Z¨WWò¦óÞ!0êº +0†xÜ}8¸ÎñP~…ŽÇé9áÇ7­Ô ¶ÓÈžÙù'àá˜gÛûáyk&Ù–uC «ýwŸ©!DZS§ C¦<¹ê2¬(”;Çg 쾑?_wQ3©b» z~ý¼ìù¶ã캭ùP£Ú?±®rô<ÜÛòa²Û%-xÔÝsãKܸÔôÚÍÎÇ7bCËù~úìOrëÚôódÿ;[Ï…ØÎÙ3“³7Ü= ìzòyvý!M l]’­·¹BµIúú¹«7æ3ÖõL×_é~'¯GéZï¬q¶OïçÝØiŃ?¶îá{.7´9ë{l~›¸pÎ -XŒ™$ØxK Û®ôo8Ã8†š›¼ié"çÇŸtÝ’î'ÑÏž³dçÙl;Bl§ªá ÐÈ[~áuµ>ç¡IBBÊÛ=Z˜?¥žÙå×cÁ¨Éð­'SbÈ+×¶uBö„òç…i½þV#ÛÂE›'bÏÉ”òëeõ)Ævní~hW!ú (GŸ– šŸ‡¡Ã•ö‰Õ‚˰•>ÕzMÃvíFòrý#ùø!!„î_Ðþ„®kÐñ]×({žS†íÔXûµÝúæg`Çžø‹ðIç¦Y”®Ô‚«Y޾M¼;\^ãrïcß„}!„?/LûO¶ÞÒøqaÙý\9^?ms³-§á“;s°Y+“{é¤ßÝîºÃí-+—®ê¿‘¿¯ýü>èxî[—,6ëMsöûHÀëޝšŸÇ%ÑÕÚìÕÀÚW]ÞMÑÂ`óF>5M€7ÌééF2¦ßS§èktœÖØuÑæüø‰®_Ñõ†²ã¶3¡D>_u 6‰-V æjÀ}ÉÀÚÝ&háíðgÛVkÆÁØ'Ç ¤Ë7Ïo§žLèº+=WÆîŸ<z¼jæëÏ"¶ßvz5èÛ™ëŸä`gy & DÍwõÑÀÑ×§úH$ZN¿^íÚ‡1~HŠCà$öQÿ/+ìƒ]¿d×›ìø}:¿¡ýÃ÷™Ž8jÛ¾EIüIh#}8¥›‰º/ËÛ4¢·vwÞÿ6ÅR nV_FÀôMd_3F˜Á„ýÜ[ðó1Ú/ÓýúÜ[ïÜyal§î˜Ü¯›„–=¶æmIÏɲ“ÉK´ðbýò̧Ÿ]!9âÙ®¨¼M„]ç å÷éhÞ9ì=JRÅט‡´ýá91¶Ó­¨ÇD÷#)°{~fën‘™ î»à½q{-d§·‰êÞÜR¾¾9¿âþ&"¼Z‡Žß÷ièë¦óÏ™aõ^·4ºÎIǽ`;¾=,Òç;¦@Õ<&gÂ¥³=º5× Måõ.Wèî05Ã/|aÒË„ȉ_wf&áÈÝg:{ÎQ ͇@D-9thΞžü7èÛÁ?Ô¶=©„íc=éÙ3ì½M ú5ÐÂV\[mk7ð[ݳÕb³M¤rкsŠB}î‰>7F×·èúCÂç™>§z_'ý`;ë-¯”;*¡Ó·àŒ%¸}¡êZ¸ûR¬ß7V Oz7¸~ââFr–éÆ¿}?ßÃŽ/l ¡eHàê:•€¾mý“µÃž7žçöi°Ú“_·6Ê8£ÏMï½*/\èùú[1ÔnºÆ«ošólÎ|ÝHæ-ò=à;+ˆÐ\qúü ]÷¥ë䛯Ïuƒ¹ËŸ;Àî b;C>tÛc3èL#AG<• Wg'¸½)†1&´L7 «‘•¶Gn"±NÙÕjŒæ×Íé:';θË?ÿh3m»ñéJ½`È™i‚gì¾ Q'º~s˜S˶g@iJZسÇŰÌnM—N[¤Ðh[µÐ¥±d·žBè<“í7›óëwì8ð“ˆÞïÊÎÛ„ØÎÛ¦9ÓÝ«“ŸëAÑP·ŸÙ]A1n¶ØyæœÔj3ã÷‡+bÉõÎú»¡ü¸“Îé}€]_3âû›²÷51¶Ã®çƒ¾'&\[´01÷åb7D Ǫ»»eòãqØŽ$sbå6Cùç;é<‡îÓ°çÎøç¯Øu3V?ØNëÓOµ<ɦ geÀ¶'û¾¦ƒûgåóŒ£ %ͪ㬰X2nâ¹Î5¾ßèü™®wÓõºNÀî?ÕdõƒíôeÙ– Vø¯î·×fUè¯,†Õ·šÌr±t…i† ÅX2~iéì •BÝG¥ãiv|«±ãé"ú\2û=Z°úÁvª-Žó¼!L†µ÷oQȱQÅ*µÃÞèƒ•É ]×d×¹-¹ñg?ngÇóCàâñk¹á²°ÕˆýùåõËê¿áµÅˆÍŽ5âÞ'õP ç²(ÿ¦o§i9¾e}Ú9ŸöŸóvb¸\EgÎçŽæQ¨8O(.[QXÆëŽzÿ¯;ÆWE‡H¹ììò²©W;õîŒ)“¹ós¶âßÍÎ./[ÑÅ^Æo…ÉδbÎ5ÿOÏykæ<þîO²ÿJv6ÍÜù3¯ö²™;šr<¡4åxµÿ;¼;õ…¿úB¹Ñ¿/4åÞG6ó}q~RZ.oñïxã™—ãWÖÃXÌyÿœY‘ÀeI8_)êá®á²µ¸ü1ë2ÞRÔ÷ó¯úKY£ ˆžÉÙærfËË£>ÆÔ/¡LnÅÏùc7g¶¼ü1[›Ñ!R.gVˆÂ G´ˆ¨´fÎ:ãï-"ù“ü±¿’3Ks+þÌǸlnEv9ÛÙåøÿ;üñ~õ…¿úB¹Ñ¿/4çþ—z„ª¸ Fÿ¿á­—ˆËñ×£ÞÇÔ_/û§, ê}Ìd”É9?wšÃÈämK8ÿcæEJËxºüMOwêj¢Püƒ¬2_D…³Þ{Ùeò,Êf•ý+9´åe•ÉZ2Ï"÷ erh%(ôù\–w8—{æˆÅ®@ôÌ|‹^…Xcá+="Ch[A '_$qDA$ ¦( .ç¶¼Ü3gJ¢G$(˜¤2y?çžýÝœÛòrÏÄ-?0#ƒà|¹œ[[^ ¢oÅxhà{BlQ„1ˆ¾5ó¼tù¹g%ç–æiˆQ´‰ˆ9 7 #sv_ßOy…åd|"bv"bŽâ@ gy"@¡Ë-"AÁ+!Š>Ñ!R¿ ±Æ@0Nã2`k“ùùÿÛþ£>ðWß÷.ûìßÙïý'ú<æ»3Ç>.ÉæòåH>"ÆLḎBÄ‹1 `AÊ-3ÆÂ`aÊ-⌚„~ÊÙ@ ¹ 4¢-“ëÈäxË%—ƒæ‹¨k.3H‡H±oS!ÖXÜ DȰÈ5ˆ-z Wì¾H6âÈåÚ–—…fŽýZ’Xcß&G ñolGÙl´%×¶¼l4&×ÖEà K†hk˜Ñ[3Þ)l¦­5—i[^6Ú?Ë´¥y¦(J$ߎ9‰¯ 1G”ÉãHDÌQ¬H!⌢MböDP¸ùˆÅ›ˆ˜£€BÄ…œ„PÌrD‹HPÔJDˆÂGtˆ®B˜Óó ø~>á×8ïïõu¿Æxÿ¾þΑkGË|žXˆ‰\12Iˆ‹RŽh §b†#:DŠ…ªâò¼Ã"Å¢U!B,ÜpD‡H±€•ˆ‹XŽh ³’ËKóETˆ [Îe 1¹Þ .3MŒ…û Ñ ¶Xô1\áû"Ùˆ# 1Eø#ùˆň˜£ ¸lÛò2Ó$(’DN(RD‰P0?e¦ýÝ\ÛòòÒœQd‰ˆ) ͟˵ulÅxñDç‹d#Ž­¿%#ƒ}ÿ$/í¯äÚØ2Ï`»(Ö$DБ9O¯‘ p“ŠWŽhËÉôÖ"Î(è$D€E#G´ˆÅ­D„(ðpD‡HQè*ÄÅ®@ôˆ E¯AlQø1er˜Zd~~õå÷ÿ7ÌsÿÓýߪïc>÷$D€Å'Gò¹œÈp¤qÆbLBXrD‹H°0•ˆ‹3Ñ!,R%"ÄB Gtˆ V‰±håH!âŒÅ›„°€åˆ–ËX‹Ate²"™Lo_DÅå¬ù#Ä‹\è™>‹]ƒØbÁÇpEï‹d#ŽXü ˆ) ÀÉGÄ\nyyk†ÉGlQ áˆ3Š$ñ§üµ%·¼ 6&7q´b¼{Ù\\_$±E±Åp‚óårqm¹\ÜŸ³ØþY&®…™ˆ˜£8ì˜çˆðµ¡H“A'æ<D‡HQÀÊNÌs!ø;DWNN¸‘ °•ˆÅŽè)Š\…X£Ðˆ‘¡à5ˆí?È}¤9L-3?´/ü?Ñ÷Ѿî?ݯý§û²?ëÇþ_êØÏN‰™³ÌX@ì«”ˆûªpD‡H±¯R!ÖØW)="ÅBS!ÖXl DH±èTˆ5^8¢E$X€JDˆEΜ]Æ"T2gS°±c¸BôE²G,ÈÄ‹ÒÉGÄXœ‰ˆ9hRˆ8sù·,V9¢E$X´JDÈœMÆÂÕ",Þ$Ä 8)ürp…Ø÷„#:DjÉäܰ9¸ù-™Ì ¼—…ë8Z3þÖøú±ÏñçrpÛ0Þ¥ø;Œ?’ß–ñèûkY¸Î(¨$D`Ç<÷ޝ ‘ ¸”™ç8ñu!ZD‚BS"B[8¢C¤(:%"@áÉ-"A*!Š0Ñ!R£ ±FA*="CajþIží¯qدq˜Üè¿ß‡I¹¿§g>,F%bŽ鋨k,L¢GdX Ä‹4†+T_$›ËúŽáŠÖÉFl±xc¸öE4ˆ5²Ñ#2,h "Ä¢@²k,n¢ãò¾="ÁbOḎàý‘|DŒ…Ÿˆ˜cñ …ˆ3Š  äˆ‘  ”ˆEŽè)ŠC…X£@ˆ‘¡P”ˆ9ŠÅÑ Ö(špD‹HPÆß¡ “A;Æã“ñ¯Äß¡8“AÆ— ¯‡HP¨JÆ__¢C¤vŒ¾>®ÑwdžgžiÆß¡ˆˆ¾œlp="Ea«k·Ñ#²®ÿ8;1Eáû#ùˆ;€D„q$ ¶Þ™ŸÇ8쯎Á˜þì?Õ—1}׿»Ï’}ï«þ¬ŸúOöQÿ­þ‰yß*Äš9Ìì `?¤B¬±R zD†}‘±Å~(†»yÊ b‹…ƒÈ°˜4ˆ-”Ñ!R,,bÅ¥@¤Ìùf¾‡…eŠ…åä#b,°DÄ‹,)Dœ±Ø’ö+rD‹H°ø”ˆ 0Ñ!R,DbÍœëeÖô±åˆqÆ‚LBX”rD‹H„x !“»×@tˆ´9“#‹×ÀþCè[09Šø~˜s¼Ø8c¿‘ÄÌå°ßÈoÅd©àëE̱ÏhÍxæãŸs,ú€¶Œ÷3þì/sA@{ÆÇ‡bHB6Œ/¾D‚ÂPÚ2>Køz"íÈø‡àëA¡è)ŠE…X£`ˆ‘¡pTˆÅŽè)ŠH…X£ˆ‘¡ 4ˆ-Š*†–/’8¢ÀS™?’ˆQl‰ðã3®IðûŸ?›Ò}€løq^˜Ï:"~Vqÿ_ÜY+nòyt»¾oó¾ †˜µ¦|>AOë¨69¿Ÿ[¢k>=/ÙÏpê“c43uñÕý›É—/  uR k| #ÎÛMZÝR9Z’a;É|â^C C hç1uUœ1Üé¾rûÐÑ]=:´ý¡Í$îCéŒÔÅa„æÒ<)Öÿ«XtãÑå×s[Wã|†úõ·gÚ‘c;oTb~Ž'|È07Ê€ŒÐ ÆL,†æ£n:]· !®\¾ÛLDV#6]z(çüÙêóïŸõ·Îçs±h õå`ÚIÀvÈû=É!I`*<¶|Ýg5´ŸTm…k1Pÿir*ºe· ›Ið¥™ÚÍ¡„úrQŸ6ê£H}€J0F~ð!Va;‡Äïv ž”¹»[§þª†„ÁÎæ#ûÃÂ5ŒQÜ8\˜>÷ƒQ™w[wêÉú`Âú[p¾"æ¼>ë—õ=ç«lÎv!¶Ã¤Tݯ–I‡æì±ø¤†=‹<¼ ‹AgUõZÏÄqÐâã@a˹q„Í·"4ÿõQ²à}?¨õ‹*ëßld¯ß.É©k<ôŒ=“º?·&Å›®¶)†9 1•×ÊÇB½o5¦çÆ‘—Õ‚“VÕ &Ôç‹úÒ: ¹9[¢&½~ðb;¬áAˆ4]-{ˆïÇ?÷{14yµEÙþ–î<¯uë}i¾gIäù=Á„ý~ks~üß-­6§FÂçžôƒíDŠ ‰ë~ÆlÏ|¥†‚š¹Å i8 úz“•š8²ºåþÕ‡ë„êÇHë»dÒ­•iïôœßGM ùO¬Ÿ"çÛ‰íâ›Ì÷CÄñëîªaÝ•ûæ²*ÅPÜoÛý»ý\ kÕ‰è ±q¤I—ˆÕÇ…p>ÁMøœ`ê{}vUß9‹kò~-eë@Žíüæè3@¾Æ1±–D ŒËñѯE`½¾Ë¹Û#áb¯î»G'Ä{ƒ±×÷ÜS6g 1Ÿÿxv¯YÝk…Æ|nHÙ¼ªl§ÃEÞ¢{ÀjMóÑ;ÔХƂ,õÛ"H©™<8Ûßô‰d‚ǹ8Þ¯‘úüÓÜ?ÞOΗÓCךðùy¬¿UmV?ØÎ<ƒ¡g"”ø1Žjeü*éDrÇy­+Ä ŸŠŒöÄëÞ®ƒwn‘ê+Hóö¨«àæ+Ю­ÅçQôƒí0â6š±¬x;}ƒýq¶kí»E ¸½iöØænð‘1ô‰#A¹öcÌÞÉùï‡æY°þ?Å¢õ%“Ž5Ô4.Ô÷¸êb5$¯4{aº ¯¿cvŸ†•ÆÁ`û£ëMrâ ÞTOùÝ‘ú,Ò<+öÏïET÷eýøTØNôæ:¾McAp%akèB5Ôß Ä?¤ _þxdôx6êÊ€W·zõ¦{…ð>y¬ª€÷a¤þÌ4·Œæµô€í¼¼X1-eéF¸¬¾÷¾âr5¤?|^~ÓÜ­kï™jPøÛS¯g,Kªóñ‘ô0ªÆ÷—ë¶ —Z¯ÔgÖÍ3¡ùl?äb;g×m6§ûJ(8z©c®‚„M:Üí\/|úA£¡£áˆ§²›ÑƒxrùZÐ\¯ê¿Es†¨xÙ…l§÷ZÓ‰ì%0ˆ±y¾§†ãž– ­ŠàãÐVw< ÊßÕßÏç_Óܪ¶ÿÍãórhÎí úÁv²¾šõ …VÅB©¡âü«G4,‚õM×Ïœèiò–n‡õq¤_óÎ W? å}¾¨_%Í)eß×'ÍW+ë\ˆí<[õjå ×0ÙËmL¿§jèÑî·2K¸÷A ÃîZ¸;¼ “?×<ê0 ¶Æ¿Ç‘1 WÉ«õ—s9¿]øû9ëg¬åó„©¿!GôƒílöšzÀ¦±ÍögÔ΀o-´²Á ìÜö5ö‚ãµæ_¸±6Ž÷O™2ûò°íøñ%õŽ­^;Bò¤2Ÿ;WÖVìHuÚ—Øl—Nó³ÏIçä]M.?†Kg®æ'ÆxÁ e“L@*-kÐgÇ®`BëˆúúQý,͹”|4þ»¯iÙû± ÛYÙiiמ±SÈÄÖ‘‡*ŽÈ€ÿ½3ªyýþ$E!×”¹Á!%ÔÙ‡HÈ•éÊ|š”+¤’J‘±2–{é4:\—Ìe ñ4QÉP™š;MDtC*„ÿþœÏçù8µ.÷þ×ú­ÿ]ÿõc­÷ú®ïw}×gwÎÙûyö³÷óÙ¯åÛ߇L¹Ra÷¯ä¿Ë¶ Ÿ¶X{ÞÝÛu¤a¿wš'©rûA¼€rÁègù}Áí̘^rHð"JϺ=Xv JÀš#°êܼå!ö`6&ëÌ„ÛI…ÚÍޫ߬çùìçjÏÏy\ëšÏõ”ŸŸ-A;Âw´»o&>²ÁŒ·`Û¾U5ƒƒ*@Û}Hñž ;0Vv´uØAòéšÀíñ1~ts>2Í¿d绞$‹´ÃòI·“½. jŸ½'ùT€‰ß‹”ÄZ¹Õ6\Ÿ~´:9MåË͹ t½¤~MçÓ¹·ôï‘ÅÚQPï§Ö{'Ù§èaèuû°üÚ ¸¢›²rC;[á¥xÇGù Q}9"8ÛyÇG6æçúQ 割¿Û&Ü…1•Â`ß ‡Ä”½Ä¥ÿ8£–¯oÁ¾’ÞÛ‚¬+à¸Ó©O‘¯làýÖ—ñ»ïF‘·i}ÕLê7Ê9 \(–›ËŸ3èçl½A;!©aÁã«÷‘£—;ŸUJŸàýö˜ãç9°±Lk‚-lOŸ“}$Šô´°ß+ó„ÎÏ¥s+éþCó2ùù¹B|þ#¥ø;13ì5LfŸ 7Œ-÷¶UWÍ]ÒNjØAÙÌÁ%m®G‘lCÝÈs„é9…ÎëgÿóëüOù9Ý"´“9ðM¿ça¡$ÕæÕa‚T,ÔÛè®Yz FQߘ©Ö ùQD¨}d•ãFÂÎÃåÆüù|ÝìþýÅ7¿4Bý–êLòß›,nÐNxªþḞYî{*¨ègÝT­­q«íÎë8@ü¼Ó– F‘ßæ=Óº¾PnÏ•7W·bÈí;yü93tÓí†úî“ùy´²¸A;“Ì•ûÙGs3é>fÞÑ?âUWï¦kæê{l.3ˆ"ì¹É—„(š>¼d3Xîz?î÷.¸±/lè‘sõ‚¥RUŸ×ΖŠóyÜj,öIˆ+ƒ×òK…·Šó¼Z–”Ãå}I6í:€ÿö›Ñ+^Eæ43=ÏçjÆÓÒ]'=g°ët÷û×qs´õ›ì£RÆü\½N÷0aóóT°œÝÊøzF9LnÙÎhm˜¬è©ÚO7ŠŒ”C}ŸO9£tÎõÛåKwÄ×Õ èù†rdq3¶R¨÷tÏç?ÈØß >š #cÏéu¹zß›×o“=´8¶îõ†‚(®³>¼?Óùåô\ÆÎ9ýÂÏ…—çªj¢@•/ÚNwކ‘ñó·Ô§Bêæ$ÜiË!±åÛªÒ`;hT8b´Æõ I÷¸q°Çp_BçqR¿¥ÜjÊ £œ[ùó¬í¤TÛžºá(Ç‹NƒM†•CUø‹«1>]/ÞöûAòÛç%m;„ù’‚·Ëÿ«ÇçK”§HówvÿÁó eñƒv&qŒ"þá³Óü»^jò~@ó:o–îkòyÂ8ôë¶îÞ,>ÃñéÓ¡PÁ×¹`h9°kÇÅŠ"÷ Ï„Uñ#”{CókÊ¡°íQbñÑû½€Î©—Ïs4ÑŽM÷žWsûÆkâeWŸLIóóç|Óz–Ãþ©>jÛ@æÃª®›b£Èù꾪YZ~üþFÏsl]°œÿ<ô<Éú —·¡¾uZä'Æ•gžø¤ƒr'CÿmÊÁýËá.ƒŠla節“&ED³/­º ùÕÐ9Ù´žÉ~ž~ùßÍQ¡K~™Þ ±äÅøñƒ\I‡ :HXð® ÚïëQ³ØîR›;bQdðbGüø9Ê´žÉ柹ùúª@9e”Ç#‹´SÖ§óï‡c‰ÔÒ²›í»tÎë»ÅEZ2¬K‚ü28:úÚ©7}qvžµaϽz@ך°ùTK óÝiÝN?hçÔñ¡m¯éž#½çé\Ë6¸ gÖ¥Oq½];jƒ§:vu„;qÇ7=u‹"ÝY°äÊ׺ ­Ð9ÊAÕ#NiÉ­{†@ýR?hGgÄøvÅIçHYʼq;ÜnC ÚIOóe`®›Ö#Ì&„¸Têõ‰"ܸB¼}øº:ûýuàùÔZíÓ:Û–«å‰ÓÿŸ,~ÐN䡸²éÎ;s{è6Ý;ªã^"Sæäî’]¯mêÎE…t÷ÊÚøõ|½Žæ»ì9.•ÿÞØç5åšT }Cf…ì²»@ôLÃ^¤ß†Eõ’Xá¶2è›àëù:×^E2 ØHÂòÑ}Íséy„å®Ý˜ßºà×CEhžÂò½Øsœ&Úiü|wg§¥I¯š÷6[knþWOŽ­.ƒ%oàýµuö üêQªYq$Ïw£ñI÷1–K¬Æf kìúÃ>_ˆÏ×îÆˆ/‘ì›G÷OÐÌ€O¯Ï[X9÷"Â\æØÁùûv¿;.‰"kN\ÔW}åßGi}“žGŠúlõóicòw>¿åæ1ÞE—È£˜âNUs2ÀôVFàP‹2˜šg›qÔ’Íç“Pqɼ8\Ò­Ú‡ãÓz¥uvÞ|£àïø~hǿǶ~ç½/Çj­’õ3`øÎÀ»©†e ð|•½=ÙnϪúd>.ŠTˆ‡Vƒ/¡ûåÞÒýŒò°éï$‹|þÿ4¯¼LØyìàf°ÉY§ ŒŽ¶Ðc/.¬Ú’·:’ŒLPÊ=çãKh~Fã€òµÙúv¬.˜€Ï3.ùá±qäþ–kç=ò3ÀLßúÚhõ2HvŠ]½ù¶LN}”væB±ž“ò¼ßB_¾ÎEç‹Ó<†­;6 èyI~Ý’¢’ñ竊vÄ‘G™­EŠÚw ª—â¹'KaöÜvŽ:>öð g‰ª÷êrNOñÞ¬s>ü¹–îëô|Fy0ôï??+˜V Õ¬]»ì,Ž#×g±,^qzz~Y~ái)ü<~ƒ‘k¡=8i;{zÿA:É6‚õüç¡¿+åQ¡,ð¹Wüüõ®; }}é [ÛãhV)Çß+¹xÔΩ÷n9¹e$±íÜjï꿼 åcçµÒ8ÅFü|ôN9õ&y¿}Ðýr¯dñ€vö?r7ÍZ{…ø®SÛûéhõ­uþåz)tš{æè¡F;ÈîY­sy@$©ÑW½ÒÒ›¨œ]œçb=˜ïóQ.œ×ÖÀw}ë4`û¡lþ*B;áŠÌIõ a~ÕÅ?ß…Uu§Ô?/…)[òó’ÞÚÂή’•†UäNVY½Ñ'/ÞŸè>AÏK”7ÁîG›ðFüÐÎæòH³î'®’$ç–jqÖìÀR˜¸v}¤ÔÿÚuip`q˜ÓýV¡¦7ÏQ£üZ?cû·t}¢õY| Ÿ E·–]#ŸœË¯ª¸ 0ûìÂVÛKAcÊš‰§ûÚ@zÌŽÑ{#C èµÏ›?ÿQå&Ó:Ý7›ðnÑN¤ãËiF}âɇWU ctîÁ––S£Ì×–Âé%{hmÁõà0‘äÎâiiÊ¦ë ­‹Ñ::»”ðõ&Ê…“çtIÑÎÉòÀýYñdœìpìÌFTt(…Õ¿ŒÔŠPÁãS_MºIv·c:ëÉDYã{8Ïã¡\Ê;¤ÜMÚ¿“Å‹ Rx$qåʯëä/… âN݃¾ §Œç–ÂÊ3ï$ï.…Á=Ïéº-’ô“ÍÖ6_¡uÅq@ŸOy=´ÞHûƒ²øA;ºóæZ¼¿N68#êùúLXã\8©6?XSÕ¯£ôVíÜz)‚Üì“—ÚvÃz¾¯A9Ít_dÏ•x¾U“øA;-ß ê»ùÙ÷bÑÆõNóþÕ°êkS=î*‚+G™‚i8—­ç9‘4O¥õ¦‚/— ¯Îü  u:ù>íË{„¸{íVÚvÜKfº¯êW awzö°·ÏÁÒ¨g3ÂÉB›KÌ\|­_S¢\¸ãƒVm:®úI óÃç>mœa!mmV’y»ˆü©"Jï´•¸ÙâùOlà" 'æ>©÷êCè¾Aóy6oMáÖÖ|ÝŒí{±\m Úy¡§y¡O)r™Ð:œb#¿”€–$¶bÚ;¨kg¹ªóüpræQÌp×óû/Í[(Gr¡Ø¸éËÅU6nÐŽ¬z¾2ôZ_·èè€L8½µzŸqU |Ì6šÿù‰=Ç'Å=Í\^zñý'êGô”¤hWØÙÒ&`J\ÐäL¨hÃGJ`ˆsë8Í…Ë óüüwWòÂÈ‚ôÞFÁ^d_ÆF â«Ç×­)g†òàé÷Äú[[6^ Rø¼FýÖŒ²’Æ`»m3Á|fìz“%P%>þÅÖÙÆ?ž}òì™02v¡ÐÉÔÇ‹Ð>%åÄÒu†òÎiÞEû²xA; ;V]?ªœHº+œˆ[³3ªÿ–iy´¬ŸO©Ýmæs·¯ð8ŒíM}nëÅŸûYŽˆ.¿/³uÚ¯}‡&ý´óÌ­Úãú¨DÒîøaÏý3áìÖ#J}ö”@¬Úµ1G7Ë¡f½Â‰¦×3«í'<ù¾Í¿h^Ay)”?#Ï£¡•§RL†,O$Ó”ºÌ%Ù™ðÇ…}±†%à¼þZÿ1"G¸÷éQÙGa¤ðâVŸÌó^„ž[i_ŽÆÿôØ ‘ ? è÷)ß·óC;Ù˜õú%æ41ë¯LP,¯©^¹´z FfG´^^´gžf‘aJÖó|+Úg×å|íÓ{@ìºÉÖ³$h‡åH%3—E·¦·Ëƒ˜=eþSK@(K\íAKÛð•mX()pÜšýa=Þ£ÜYš_htÝ·y±bGXqed'µgS¹º*ǃB;7çw4Ìz–HÖ5¬8gnš3"öÙ.k=^$Vo³ƒÞÏÕvÅŸ %·S˜ Ñ›?‡Ñ:ùµWMgíQä¾§v@óNù<\ŠvdmÐîI„í?dsmʤ_ —ÿÚÁÕ«v±Ó‡‘6vÃL/âîsÁØN<’¿gEy}ìúЂË_Gðß«,~„˜w<í}¬Ò"‰œîÜòJJpŒò[äß¡c Ü,;¼`bg;ìr®]Á¦0bâäQ¸0Ø“ï ±u³~|~C9묟è7©Ÿk¢ÁÚS³¼“ˆñviÎù,ºl›ÿI :ñ¦*-ÞÛBh÷+ÃÈYö¹Â«ž„ò2©ßÑÏÃækª|ýœÖ£eí˜ËSIDÖ.+΂aï'š­x!…øSÃ,§Í´ƒ¸VÒƒ³ú‡‘Æn*-í+=É`£§kS´†Àö[ÆGøëp¼»Ž£Õ†ÿÝšÔÍÐÎYÛ'‰üÔ|c‡lزýs›a¥x×¹qXG{ž 6ª®Ï—< Í+èú¶9Îæ¶‡{)¿°ý)“¦<5´3¦­ºÒÈ£IdþÑ¥ÚEc³áå0\˜¥¸pý»‹öðÔôÞ(›¡dCŸ?LÞº~ý}èþÂr‚Ë//O‘4 XÞ¦hÿN?hG!­ ¦àI$eü„߇;e³}ìSR🵾ãÊT{x#f Ö¡„<¼¾cŽ'™ùnHd‡ûü÷3ñtJHy~`³äUéds0Ñ—ý…lÜàó z5ýëV )ëõ{6ì­Qž¾H,…Õ#Þ6ØÚC—á;É«P2í©uÑÜž„Ö÷hÿœráØ~ŠßwlÂíD;¿I™„'‰´›;bÿ†¤l°Œj˜¾U WÌY¶|4ýyÿ_¡dÆ|†t·Žÿ¾(Ï’rÔtÆ} QWáï°\y®^6¾RøbÍŠKo’Hulí!ÕÙP€/ž–R˜üÅ¥ñÝYèTÛÍ;¥2”\öÝû¡ª‹aãa$PÎ[w})`óö¹"|îïS='œ…dÒÿŒÝ¢5kÀ´°°¥ºã¤à>¨MùJè{0(òrl(¹ÒUO˜ÿ«Ù÷<ó¹×h#`ëŒ#¡íCåÑ7Âæ+ôóÈsEýÐN†~uë¬ɤ‡ÿ‡Å9a€lJß­=¿ï=¾Ç,’Õ®¡¡v¡äÞ­©wÎïóàî{Žæïù\ î3$ò‚LO×Ý6¼e~§œaY Þ{»GïÚ˜LúÆ{,O~«¯yÜC ÑÇVø‹@À¸O÷P2¥Ö`GÅîÃHÎ?õ€½)^{toç*Öÿñ¹]›I-$É䯝IÃß=ÝBËqUm¤ð:0Óî/MXóËÄéµïÅD²{Ñè?ã=å¬Ñs=í·Ð>?åãÊüŸòÙ‘úÝg“‰›ºû…ýBÈ»õ‘5Åà5úÚ‚ß:ÙÂ$Éå`µP2¡R2͸փÏO(·ò¾ÇŸ²8PXXËß'¡}™ßO¨¶°ìn,ù3™x>R|ª5û!R-£^l¿¬1ÝÓfÛ½Ô³(¹åŸ›qé¡÷^Þ®íþùA»~ü½%¶ú–ç¾Óì_&& Ï Nšn¾–ôjÍÉÇ@×b«çÞ&PfÐ)V/ºÐ>=·ÊŸDhg’ìÂ@2²õ‘uRÞCHzÂkE1ˆa`GÏe°ë'&àÄd`gm©ú wÂÞ¿5áÎ þ÷^íÔ_åÜ{EþœÂÆE+6.ÐN»[9$“›¥£·íoû”–¯xîѳZI'†žC;åºÂÎwÄäÜ› Ñ. wž#KïÑ:*åÊ⟛К!ƒã÷”ê#”Œ|}Ç”Mª)‚“û”UL\fcM¶¾~ &·»Uhßp#Ôh˜ís]Ð::Û·ëÍÝ·Tbãí<›µhnÝâdbUlp,Ãöt<í2 ÅÍ"0h[è78Ýæ) “ë‡s#ôÜN¿ozïž{i>ÀþÿTÙø@;Ue‡þZ…ßÓŽyóó;ï}_txZ§¯žï©ÝÉFJ‡öϘ"&U'„z­;¸ÊŸ§Ï£uz?‰öí›Ü«4«fyí^|Ø;™ãi?‚/~5Ñv)‚Û¯çd-h+Ú«ãÖBLï4:áFh~IïU²ýèJ®« Íû‡²ø@;L5MeO2¹ðÌÀ°MÑ#ÐÖMëþТN0˜ÙÁ½Yç"¶f‡Šéãü Ü =ŸÓþ:»Ÿ• hÿÖyäãPˆvoo7ÜŸLÈ닎Z=íÙ‚VsuŠ@ð4Úbpƒ-øÄ•ÇÍ+&²å¼“;Ï£¤ù½ÿHÏ‹´¯*¿®ˆÐÎjõÓ]Vâç‘a„>†N†Ÿ|*qáoþÛØá~rfðÇ3bÒíIƒeÝ}7BëýZ-Ûšzwß_wšnÚkUÖ[ÍÙ|å’ú¡w™„-™oîèaù|§nmÔ~Rf'ø/ºjfó´­>m“ý‘¬º rçï¥R çÚÐË{§ýÐ&ç Ú 8ø¥O¼G2YôÔ}¾¥3~Cÿg aèÕwa}:ÛÃæàÃØ„2â¥þ€O×Ý å\S5ÿ&}{|®åŸ]œÔãÅÿèËÚ-WCºRÒñ_B¢eÿéw‡Ûsïuˆ‰:|1õ|åJè{l}Æ{ŸãµàòÞº{ã^ (ç—­³ç)…‰•ÂýƒžjV:%“;Þ#~ïôê1LŸ:þrȨB°ññî<Ù6ŽŠ0],&+­gÏï챆çÞ²uí‘ÀþÞŠà¾9øMµÎÒ ­y/æ ¹{xl~«‰v6ŸñmqÝãÅ-L)¯÷ï}üÆ[ÕBèk}0>WßÂZOz«Ð5”à!LCØàÂ×»iŸ“­Ó?ýz¯‚«¿Ê÷›„hGËãh¦×îdÒQvù Œÿãþáì²3cȸóìaüC_…YábèxlŒ×¾ßÔ¼Eã’Ö‘äï׈ÐÎ@2ià´ÓÉ$£ýë ½ŸÀÛ—Ó7Ç@üôô-‚Ãv°øSN?( !ªíRÎ…š¹Z—£÷ÞÙsÛE>^hÿ[~ñC;F-¶WÜ¿œLfg<þ }è Ì?Ö?öËþèörÈ[8U¢]ï¾6„ãêºÚ‡¥}:¶ÿ\' ß#µ/‹|~ÚÅC¶$™¬Jœµ8!ã lò±M’K¼÷¨S3±…›iûÆ!VnO\ ½FïËÓõŸÆ#̓›ÜSf~ÿgEÉýS“‰bÛwµð÷¯iárزT­Ù¥ÔrKŽ$´“¡ÃöÜžþЕÐý…~o”³MïÓu¬I¿íä)žNz”Lìû­ß¦•¿.ªÞ¼N·òÆç„kØ‚¤±pܯ–b¢¸Ä<¶À•ïÒ¿—}§\ÀîŸmùú¾ü=X…I•Bž¹(™týuúÛ=³s näI½!Šð%½g¶°©ßËü)Å\?Å•¯wÒ{bìû5‚“ËvÝìï«4ï–Ïc4ÑŽžFtê0ß[ʼQ•Uãëá ìÄ.¤—ØAçþZÁ—^‡Ö®+¡y)Ýè9ç—O Žÿ¢ÄßK”ïG ÑŽ¬ÜÒ#…ü6·ã*ƒ9.8ªw!Lêû SmµFFãfù†¶.áÆßs ÷)×}Ÿ§^ _T„Ïgï#¤ÛeSuïçÀÓc…s¦æÃ™œ¥3ú,ƒv»n6îBö˜®6ÛöþAw¾~›z˵EåZ»ê4©wû¡à«‘¾ãSû½å@miߎgæÃ\ɵôÎŽ«õÙfÚÞd¯±½âs[7Bëƒtý¥ý.Öî{>þå¹×´“T”a™BʤÙ#žuÏ•=çG ͇‡c¯Ýò,r„R…¾_žî')1ÉâÚzW¢8¡ª$câx ÷ƒÿ½1Î*ªZÀÞ  vØç'àóKÔ>o±0…$Îs¯24Í…+‚[/j̃ý;Ÿi»ow‚^'Ϙî'â™C/_èáÊÕo§Ã/²‹„pÅsØÇ€6 ¾•âs»ŒcNp)¤‹É¶EÇsaçë“[îÞÎãîa:–õÜáäÁï¤ÁèSîÞÎk¸ûFB®bÀõ7TáÜbó_tDmx¾¶ü{: 敆$û¢9)Dú“߯©ûs¡eÜüxkqÔE^Þ©ãr7Îø\Ý{?é*[®\ˆìµõá|¿ŒþÞ!¹#HyP{þ~°|X휚œÕbJ 9ŒÙAç´\XÐâ¾r—eyà° nãòSN€Á1ppϤ~\g¥WZ§¥þO×ûÃí·ïw(Sãó=ù÷„hçz/â&)dI~¾^z‹<(™aÖvŽa ÿexÝOá÷†‚6uKCHXÛ×ñ&+ù÷ÏØ¼\Ï󽜻´¿÷Z•ïK5ᜣqÞ,e/Ó¢¼¾“™«A,h_k•ß:îmc5;cXH;àŽ/&ËSîöÎ\E¨_Ò÷÷h¾iü°}?|þŸù?]‘ª¥‡›»ù*-Ï“-ååBˆ>x¿q€¢ôc©—ÔĤâ%sab ¡u¶¨ÆŸ+Øú2¿.²ÿ©ÌÆÚéÑù®¢uy2™ž7§P'<,†wŒ9› ¥½¥STC Qi‰i™ö»Î×r÷æØçKñùYéÌÅ„dr±ÇOõÖò`ÞµM‹£çå«Ná/ú]ZóÚoÒÙt€;»Ùy'¹zï’ÖÃ_.«Úv¬Qʯ´nÙ¤/?¹R¨ãt§ß ¥d’U5£Ôm`>Û¶)ræ \X¨ô¸«®ùþñ/RnoTVq^Ãß/¤u2Zÿgý¬%ÐþÛ'fãPí0·:#’ȃ [ÖÌÈçÞgÈ]O/G=Ùãšm™ˆ !S³n_O8åBè½1ê?ô¾$}ÿOøÜ+«¦zÞšDîþ¶¢­½G>¸ùÌßXs+þ˜Ý¸Õú°ôÜ乘ˆBȦ³•Wx»º^Óz ½ÿOïËËüŸûarñ‘åVIÄà·èn>aù gT¨×0IÍÿÓIè#{¼/=BØ<Ó…¿ŸrãȈ“玄GrÿœW.àßgfüŸ[Õ‚i„&‘Öög×mŒÏ‡}äæÃcËr@863jÀG8á4O«"„t[v(òÁPRÐñgÕ%µ†üý)º^ëÎßW\®Àïòë¶í0UôÕ‰DyÂ9¿6¸OêG÷W“¢„c·ù¿ŸÞµU­!ÎwªÌŒ\}O˜õÓá rvÀ‰Rþý^z¾¢ïÉâíŒ[¹[åVb"ñ•½pS“-ì²Ûå@Djüó×íaõdc=ϬrmÀÃÖ‘_¿z.dó¶nUäïÝ49‡ e]Œ%’Å#™7½ Àîp½Ï–Š'pbàÛ½7¦Ø›ŸhOBÜCRæw4\ÃßG£÷é=vŸn ô~†üyMÁ¢R¨täS‡˜_ɰÛÎASÌ àFäóÛj O ­…dÙ«…¶ ™záøÕ2¥Þ­í›ók{ïAƒÏÿèýGz?•Þ»ihgD–&ºN"Ypyü«Ë0`ä–Håˆ'°aÚÔÏÙ€òw¥ü€Ât]ÆV¬!4ÿ õšÌü¨2/|A þ½3Úß•(°ÿ~ÌLú13鿚™¤À}Fʱ–ps+]þÓ-ÇnÍüÆÌòæs+Ó¾Áne˜ 9Va7³2šãs13{™9&Btôh”::»'Jв@§Ai ãû¡*QVq(M ‚T ʃ!¥‹„j@‰¸Y&ÌŒ¸9~u4Ç£qúÍ·æ•Ëóhþ‰ShÁÍ«”Ÿ“ôwÌV!Ç£‘çj4›y"?7îß0¾ÅmÍü·U̳Çmý»™•tf¹´ÃÚ‰›áû-F—„óÁkà5ð¿ZU¸Ï@™­ÑÜÌ8Ϙ]#Ç)ÌùÆìÞæ3ã2¿Á)df—GËq¹¤Ü¼¸ŽY㉒¢,ÐÑcPèì~¨J”:}J?Uƒ²ÆH@éb¡P" †4”>„˜ 'T7Ï)HŽÕÃñ\šñ¾7³WžÙðOL.+nVœ<·úïø„³AžÉ¥‰A自DY5›ûôoæ—‹Q˜óF¡„cºpŒÂ¿›GçöV6㵺pó3¿Å¯¡ûð5ðÇø?±þß®ŒïH¾rZvƒð_p ^µ4¥Âͨk>X~N˜s^'T&ÊXÂq ™¹å9rÌ.†ÛàÉ1 …r³2=QR”:z JÝU‰²B§Ci¢ã jPÖ (] ‚ TJ„Á†ÒÇ€s³2™™å r|V)Çm4ã6|o^°<·áŸx]ÌÌàJ”XJƒ,€ãT[a°Å¡4¹yw™Í˜]VÍfeÊÏ,ÿ7LB1¨N¨L”±«Ú •‰26df5áÿÆðºP™r¬j&˜ÿnö5Çí’ç³J8fÍ·XÕŒß1ÿ~¬}?Ö¾ÿ*ÿÓàþF©ËfeØ «ð{sÐãäX…ÒoÌ 6FÇ• TÐy]8Nõßq -Сcä˜] ·ÁŠc2Ü?T%Ê =¥‰Î€ªAY£Ó' tÑñƒP (@Jƒ@Ì‚*eŒX‚RÁ pAe¢ô18ÄrLÖ8ŽÙàÙŒÙð½YÁò̆bv1³‚šñ©ÿŽYhÅ1ä™]º„¨”5c JÒï_ÎNÿ·Púna´ÆaêcP‹¹ÀvBeÊÍë¬iÆeõäæ~‹c#Q`ÿýX¬ÿÕ¨‰ŠVøÊk•¢,þ·aS[0œV”::ªS³9ÅŒÓ:¡2QÆè¼” :° *%DGŽæ¸…ÌÌt©»KÛc2Üš”:¹ªe…·ÒD‡@Õ ¬ÑñPºèüA¨”ƒ ¥ æ‚Á •‰2Æ pÁÌKO“c¶RfDt3fÄ÷æË3#þ‰Ý€ªaæ¦c% t1Ђ8&µ5\JƒÎcGȳ»¬Qq(M Ä€fóÒÿ ·P‚RÁ`uAå „r\jTÎ(fÆ)þo(u bOTŽ—Z…9÷¢ÒPúØb.¸E?B³¿5š øo±©% ì¿kà5ð¿Zu¹¿ò[v„&Ç0üÞ¬ö9†!좳JP*è°.¨”7¥ŽÎëù†¡:tœ¿‹áFXsüBMtðT Ê=¥‹Î„j@‰ÐéÓPúèøbÎùP™(c  JÁ•ƒb@D£Ô1(Ÿà^D°ÝÕ¦„ 9шz`rÒ¼@cÚ€ æÌ> …Ií@þ݈¾ÒȺ}ønDDc¡0vFŸÊg}âs¢Éƒ{Y³‡ó9]÷î÷áõÈ¿þˆøGÏ¿?gß7ûþsï:óøãκ\³ù®1 ½D¾‹Z QÚ ÂtÄi¡hð5„jRˆ5 x¢µ‰ý‡ÙÀ×Rè ³9DlZÙä³øsÎ7' \œ@ ¡›b77PAô ágPà À ”„îW??ó` ÁÙÀ´0ˆÈa#ð ûä0Œñ>úÊ0'šIœ@‰g»§õÀ ”0™xïê/ÓPÀxð- h¿ÞC+Œ)…1³€hÄîié ¾ Ÿ˜Õ6˜ïyÁ×oP÷´æ5·’ÏÇ>ñ÷°AýeÁý¯6úÝÓæáõçuÞÿY÷ñ:O-nƒu¿:€bäDAê(!L3@œà êZäû¬5«H!Ø,à×dbŸõ¯õ-ú‚vD͉ÂÖ'P@àœ(r=p%ÄnÞÜ@á[@(ÄŸ<@ XFÈ^ !l@Sd/PÃÖ ÎW'PÂ(Fà:Æ0 'Gœ@)þÞDzà ˜‰ ¥N „±Ì@s~¥ûšïmôÜÕÛ¨v …ù²€¨`B3Àˆà ’> …1í@sè`RPÜ£¿Ñüÿ¤ÃQ3ÅG5LmÒ¾;ûÔ0¸Hîê]4ïp>kï „FÕïmäuÌ¿~ï žÿ ³Í½ûyô¼c3.x¾±Ùöï˜kl¦ýÖ,csì^³‹?ö¡Öü@9€BâD1é(EQ逃âò5fRˆ, xb³— |@ áÙ³Êü@!:€bäÄžjDé Ì*N<9ë(!T3@¬à*ˆÖB!Ü àjØ ¤qð Äl¡tpóÏŸˆÂ¶9Äm~ ƒÈ@¡s¢Øõ@ÁsÀt¾( ~N4€8F0 Ì`n ‚),b/µ¸ùga+ÿûÄ ”0‰è`Œ’ |@ ÃØ¦1¿ØAm2(ø€Vì –ÁLÙÀ´ƒùnB|-Œåû§3”|o Ö‡Á¬ &ËÎaÿÝ!{¯®isˆðúóºëÏë®ÿ­ë.­øxê(!F3@à*ÓB!Î àJˆÔ, U l@ÁfÐB¸v {®ý@;€BæD1ë(!j3@ØàJ¾H rpÄn¡|ð5„oRˆ? x&°Œ |@ Cئ0ÐÀ6 …A À T0 'šEœ@ Ó˜Æ17Pɰ/@€(a&3ÀPà*ËBa®Œžõ»²¥0Zð5 gR˜Î@óePÄ #f'PÂfà:Ó0''Tœ@ £Ÿÿ[2Ö0-'W@s¢‰õÀ03ü@SÛ”|¿ö ø€·P˜ÜÜ‘|ÏÐwíj˜Þ ¤Q|Î3Þ+Ðü ×]ü äçßÂÜû½×\Ô¼»ûšë÷^oý«³ì~çØ½fÿ^ü÷âáDé(!$3@Là*ˆÊÌÿ~€îÂò Äe2,ø€B³9Äf~ ƒè@áqâ Xœ@ !šÅ“±8sÊ $¨¸ Bµ€Pˆ5x€¢µ)„›¼@Û€ "Î> …˜m@ Agÿ¬„-°@qs¢ÀõÀ ”ºH vp%Do…¯N „Ì@€¨` …!2€¨a +sxøç{aÿ< ¢ ¢r˜Äü@³8€†áDÓØÆ1?ÐÁ@ ‡‰ŒÀÏÏ"˜É0”ø‡ðý§|—'߯ˆã40˜ H‡ñÝWØ Œäûuð^øgwGðØo˜Î ø¼,"Ü;þÚß@è‚~~Æ®Ýôâó¿÷úý€…Ô¿´áïÉØóÄ!w݇râ ¸ûgqìw®NRÿgsn!9¡îU.þ[¥öªÚt<úÙõì”õ8d¹‹·íØ\ÈGnýY®aá¹@>§5Þ»*ÿXò©ŽÿbD#²éðÓ773Q–¯ØCáª8”DYž¿Žë,z{è\ß;í³øÎ_g}^N2¢,Šg§#™|µOG†n§|ëhÛÄ6Rô·.³3¤×m² Þèµfþò×ó¢„ ŸtÝPðÖ©Î7q¡…Ÿç†¿DΓMó™½vU)i±2©õä—Èî±2Ãv›è®¦±ón&ϧ,ßå ²>!¿¾Y ‡!8¯ÊƒulÖy‘Oó×Ï“f+kcK ý¡²@y-ž(gó‰V­‹Õ™fäܰüj–'ø¨ a9JÁ9b!1^Õœe9EŽï ©­zØ”ÉkÏElY’~H)éWðíÔMñ¤ãGìô%r4,ï§!C"“ùâ,OG˜Ã•bÎ^3rit¸çœú Âr®êüƒuv­–^PH‹Z¿ðIþyÒ'äÕíZ”’ ·æ¶ ±Æ“l>ÚÅÑë­FK¡,ošå y_ßrpýª×+©Â:“|aÉn.¤ÃÞ­MÏž'ƒÇ÷è<»²„L”;üÞà2ælÙ <—Æ/ ›‘J™?Y~½§ã‹ê9>nÆ¥ÉmÄïËÐz9D:¬Skûû'–Òýóè9ÈòÂYŽ·ð}o æ0©wÜÌX§g³ïçüø.ÞÏ­E¥kzTÓü×´ó§”ãôM9å‰Dèiäè´Qéã6¶K ôò°í°3–sÅrfëåb-MÞ/X½ zÛ[¶¦:²’,ê×ñ‰®=KÈÕcGO:šHi´0í–Ë$æ 'Q¶ý»sò…þצÛàœJÖé=qû„÷ é¨î/W=5¥’øW>p³ç³dPMÃÒõ#Iÿ›'žÎ’sô…¯ ÚŽþ¤@Ž4ë1eþ©žp{c’3,ÐoÏzëüó¤WÕ¤.¾:øXìÙ•dÙ–î~޳䳇»>óЖ²®yy§ƒµ¬šsæçä@.6Ëqjí»ž¡öây@*æ- 9ñ2~ûÿµz@zßBºÌ)²{Q%©«»Øp–ôÏÊñÝŠ''’ÛÆ¼(ɥ풆xÆ´€²\_6XÏcy‹¤§‹õùxÂÜæ ëÈ7$/I?T@WÚBŘ*IõCý›Ì0œ%þ;‡ŒOú¿¬ìÓ²†£§^Ù8ûØ”õ °÷1|§fÝ™ ¾(¶}–+Êrôê|ƒur]Û÷¶/ /G˜¾Ù™WIª&å°9K¦Ÿ™Ñ)eò,’¿º]ÍO9Úüë-ÇÏK ôвž!æa¾´ôt_faÒÝÍg¯ù ­ÓqI%Iº³_d·³dó´ ‘®3I䥢•V GÓß?4±GßTÊòVYN"Ë-¬wžÁvE?s0sâazd._\[I–|ùáó×n“¿~öòêˆÇãȡۻõ=ÍÑÑ×]ögš§òi™XN^½ó ¶{»aæ…ÓKÑÁ:ãϳÛyH·ie}m¥Å䣆WÖæÇ¾mæVmüÍ‹±ï§Q¶=–Ï-ìo•Øó#žG°ÝÅ]î4YrˆJvŽÙÓTî!/.•šö“ŠeÛ Ìgb‰_˜KŸÿ@ÓþÈ‘´Àq`>®÷¾Ž{£@ÏKp¿jÈx¯ŠOå?f¶ÑÛNn8>ØC¼Kù"öbÒb‹dÄVqÄ?eâ+1§sésQk×v};²í³\?aûUQu{2sA£@„¶óéš9­„y(Ã:ë’oFDͰÑCŸX]3ÒCžÚÞ)ùµÙÅDèóŒ#)u6—nϽš:éÅ´Àu8»ß`}¤BFã@Ž(ëÿ©óÖiûKnfEw]µL6ÑCÆêšõ:]L>ô¼µ©pÕLR|1+r4Cr!zYZàú•õHù‹Öó"Ìw±/ÛŸ³”†Ï-9H·Jv|]6ÍC|•ëŸÞ©˜,·ß&iO=T³ôç¯MtüJç™’Ëi”ùŠ/–?>èjòžëÅ­—?‹u®FŸO¾>ó ½þöÅYqòYt¯¹—~("þÖG¡K Û¦—E]ìk¢ÂÇô@þ0Ë= äÏ.}îðºÜ΄õe÷`›±NþˆÌã¯]<@]Ê «BæyÈ™‡Î”5Û:Ðs܇gÇ:««–ÆôH8@‹Övß"|?„^ ¶w{ï¸×?Ý“O§D\éñ‘‡ôµ¾´âÜ'ù ¢åÇQy³ÈˆÒG†oÉÑáuÅGi”å0³\E!÷½Aà¾7¸'A‡íÏÚýàΖóiÕÊS=Y=¤eÛ±ÇOrímÏ#¾I®¼Ù§Ex-G¯æî.m–NY¯û¹‚pžG;Y’ZUá!Ä®nå$ó>=±5ž= ëiVè ãÌË )Ë•gûËú&”e¨}tÎÙ`ùðÐhÂz°ê|1õµï¥éî¿$ôóyÈÔ˜”Hs'3ôAň] d@SWRæìytLY»Æc‚˜«i”9Ú„ŽzËs¤2¢sÌU1t M I*bˆ¹ !æw ¡-ˆ!TiC‘FÌiã}N³O—|éïëþÖí{¿{—^ë·Vÿu¶œó>ïÞûÝ»Þ'†©]_Ü]‘²ì_À}¿¸ï‡ØÓ’Ž­j?{fy]eÕG‹ºCØ–À+½؃¾«.çg¢>ç7O] ØáÓ¢¡ïÚ»Kín Ó11~'1îgùyÍç*·Ðã~óìÉÁjÌÞD^Yõqt'ž|;÷ô6¡õÅ æ"fŠŠ-0¸AÐúê¨ÞM‚iß{CßÕjä_1‘ñ~º¼_ùÜCߟr_åêSðjÁÅ>ó1Pãù¢ßl<Û;¶±sýrfªX Ù)]¡ Ôw‹ëÑje†‘רuQ÷û騵‹néO“~÷÷áý«yßXÞçø·ìá]\>ŸG0Žk`™Wa=·±Ò-6/ͧ4Kó`*[xt¬¢î0úx`i•ÊZÔŸ4”qýqÿÞ_þ…Öjô)û€óyÓªŒs³{·õ[˜Ãù •†Õ3Sû¥]Þ¹§’Øg?˜>ÌÞ3 Å_Çj'|M^Säþ®|'Žû@òUt Ë¶mË¿ÏMö©²êã.KŠêYßò•Â\Ì”ù03Éyz°,dšGŸ`úQ°¬¤c• L8»~ª<ð÷Æ}Ô*½¸íTõHqy>æóšU7§ÒÆ®Ÿ–zmb…Cq2S¯.ñ+‡N9OCJ÷ÍÖÉ}qËoÓ¼J¾iòüËó9÷´¥ð¶wùäõ®íþÀ„q–}xá걜Z²A'35¹;ýxÙjçið»Ä_O $ëçtÔ±EãbCvž›*û2ðù•×?–>__èÄKïWlÛ§Û®§EmlSÔeä§u,xy£]{›iЃV–‘§ÏÑ« êÝ~n9æ^Û+DÇŽnŸWèØº©rk^·á¾ª¼5ï#k»~Wbq=º–M˜ØÔ­‹™*Ô/5iŘs”œÒh\µŽí»f[¼Ž=žQÞtøüTÆûLóu%ßW}wß~¼¶ÎÙ‚÷%·êã|¸™¶|ÕŒ8Öiñí/ƒÌ4ÆoXÔ¡JçhÚ¥—×ò]  ã­Uûæ';péJ¸ï×Ó÷ûæû8ž×¸¿"Ÿ§¹ž¬úÁ8_ůßZtÝjÖ®¯à¤b&­áà„ÇÇSÈiçíA¡£ÉλJËŸ¿Ò±C„Æ4Æçî##ö©·¨¸_ÏØþ=Œ³¸å…ŒèV2÷îGâÇ›©ÓEŸŒW)ô]Äñ{Ê ZäîøÝ†yXWìºùópi²;1ÿŸO“÷G\ç|=É}ÀxýÅÖÿZ‰qìÜ×µ0n åª9 3S›=µãÎÐú³‚‘ÝpÊÐŽë¸ëã9©#~-sy*ãëR>žè3’¥j³<}ÇŽÅäõ&¯XuƒqªuÙ{÷ÙÞ…,Ëë·ÕKÂÍt´çÛ%‹œ¡×›\ÚT->‚î»5zï/Q¬|Ÿ‡uWfNaÜÇ@Ü§Ö Ñ¿õ‰jê6¼r3ñ=ùâ¹â:h®ä3d¦ž­Ãû¬4’8ÿ §Ô-þvú;QìþýÊ[gMa|_Îóß}î-®ÿ4xþ¢ÉUÆ ‡±÷Öí³™L{4mf¤&ÜŸL ’fGÉù˜¿_¾_ýJ~e]ÛuqÆrªž0]^æÈZ35ušw£úir]Ôið‡ º½þã”9…¢Y‡«sª­ì9=W^).ûŽòþáV]๕gÖ*¬Àü¦$•)¥7ÓÌ쬋SOSP˜{…æ>ZǵsƒzÑìÔ°ðoÞ5C® ð: ßω~v¢ðÜJ½;<ûfÄ6ôU'Eáíf²ÓÄ<ètšŒ+~}ë;ÞŸF5¡®ÑìLþ:!Y£fÈ>Ü™¯‹ùþçÏ|ÊzYÔV ¨¾ýToÖ,mZjæ3ùîuõ=]>lñØþTzᬮ§WF³ÍE m ¾7ƒñ¸ã}ÆEŒt•Õ'ô‡br½Î¶Þ¬Ä8ß¹®Zå°h(5_6zÿÁ]fŠ/º§ÒÀÙ§h@ûÑݶ– õ_Gg.‹fõÎߣ87SîwÎç)îg)úr—Ï‹>óÁ8{[µl7æôTZR²Ô÷{ÍÔaDœW¿F§È«œ×oí2è[ÉÀûMëG;û{käþíäuîÈÏÏlëÌ&Œ“6ÕÏÞnÑ*º¼«ËØ[fuãžùœ¤¦Qí{½³¢°“ߨeF³«ÝëíŒL›ÎBÚ¿Ù>boŠýÔ~=y],Ö›KÉþ²Ÿ?ö¶¨—fmZ~µKí^“Ü»Öm3Åí±>ÿIJßÑçõæÕ4gÊ[û÷æhi?“q¿=^?åçÞ܆ŸsÚúb)1Î×1>úšÖ’£¥Ûu¯L3å·( ǵvê‚ÅIpw8ÍæZÝÊkd¿"þþÅó™*¾ß¿Sù|ŪŒs°rûb×S—' óÎÝ4ÓÊË›_ë}‚T)ººÓРôkÍt9ëõ.o4ràÿ^î»'Æ[EÙÏš¯Ï­úÁ8ÃN fê´‘¼¶WÂÒÉL]ïçìþ˜D“–ú­]¸ÙŸõƾb7;ê~¨¡V^§ð|Â}Úù¹?gçõu«~0Ž`v¯Iß$úÌ#Þ–Œ¹òlK|9ŒÜ8->Û"ÂýÝŒbg?”ì UiåúŸßy½…ß àßÍ6¯Åaq>ÖS›UÏ-3×\ëÊôI¢Â½F 3úQç«ÝŽ]»Å&~ËL©•ë|<žÿùüÅ}žl×ߌӱH­!·{l¥=“¿.ü⪙Ғ¼f–L"ÑWé×ÙŸ° Œ›õ]Ø·ž›Ýýþ÷ðùP\çóóºŠò¾Ùv½jÂ8íôÓÔÛ¨ðL¦kxÍL­ úãtãÅÚç‡þä¬0é…W4ËTk'=¨•÷Gü;‹u°{R=º¢¼oþlßÒÇ¢Nú±LÃÉîñ¢¯ãu3¥ÄÿËôÀãä^x ØO(úæì‘¾ÑÌâs¿·tÙ7›×±øüdÕÆy»ïÕ¥™W¨Û‚·µ&!Þê ¯fÞÇ@¿.w«qB0=.n™ómÅ(¶´Ú³úWB´Œï[ù|Í×Ç­WGUK<[ó³u±Ïߨmxp„]$:¬Äw©Û|JÄo7½¬qfh¯ÓÈ9F³‘,yI‘ \µr]‰û¬ñõ€â¹_ÝŠÇŠÊçÁ<î­ºÁ8V•N»ÉýfÉ .šéݚݞÕK0j5<ª|z¾á䢨YfNT$\pÒžhäïÂß—¸Þ¸®âçç¼¾a{¾iÀ8C».Þ4¥éªÝÚ[3Í[?äÁ@õ1ÚYòƘª[†‘µŒW:Š]XçÓ"½F>OçëWq¸­ýpòÓÛÃŒ_æ&Ç¡U7Â÷¯ÐuÉ´Ú{)ûV/¿¶çÌ4ÃéÇBñÒ¬;5z7²“]A¾)ýÓåÄMÆ}ù<&þ=T¢Ý¥u§X‡³ó²¨×MkS¼¨bùï¸ä‘‚8ÞÐiÕÙ=GHû©÷6/löÇVZWlæÜh¶ R·Ù¥µr†ßwâþÁ|^ýÌGÏ?p3]ýbu/U¬ËųfŠÝ%=ðÍaZÑñIû ù‚¨ÑŽg1¿®‰fÃz;ttƒ–‰÷JªÊëp1¿\P½™•2þ7e!9ïØž¨1ÎñÑí^=²ŸnvÙyþÆ)X§ŽW›ÃTì×+WW¤ Á“#V׈fݯ,t“÷Ý<ðz?/â~S¶ó¦/Æ©0R¨¬ ÑOÒL öÍÚßfé!òm—R¾åš²f-MsN2g€*Œñøßÿ{Y¶ë ž+ú%%ÒWgª® 4B]„“惤(Û+¯û4§¸Pùb­&/)šÓ,LöÁuX\®ñ{güœCŒñü7ãô´¡¤'‘“·í8a¦Á —®ïr¶Î{¹¤KP 3F(%2»üW¶*ûû¿ŸÏâ:ð TG|¯âß›û,Yõq^­›ÈŠe¤oZÖ}vÌL>›;M˜‘Hâ¹N ]¸Ù.éÍGì·#®ÎêüL+ïƒùºœŸù»\lÒds!y^³=G0aœý/>¤‡>ùHßk“:d³H6fÉÔC‡ü´òy|Ý,Þ7("û}Ûú¢úbœýW*Ù’Ñ»¬òn.6ÓÓ”—›¦§î¥µwO–Ø1:â*>¨x)ùñiåú+Ï/âý³¼?æuf«nðüî#ZU7þ^‘Ïg#g^]u/µÓ$-Vv  ªªoŸô‹bùn6ðià eG&om7jne¹Þ%úžÝR‰÷óËó³íü‡qêüt±ÕÖò^ìÒ$ìgM>ª“öPÿ ¡]\ü)gË7®Ž)Qìð¯WW=q×Ê~¨üW<¹£2}UÓøfª½tÎîôyãˆëÎã´yBåô#ÌôCJí·Õoì¦w/„7Ôù‘‹W²×Û¢Ü'LËø~’Ïûã{o©¹ät¦ªXz‘Çb¥ý žÛtÉÏ?Týñ8©¦6ê³Ì×L.õk—¿Úv7 ÕF…ÚÆãiK²¢XƒØ–µöy†Éy˜ë€Ÿq_D~Ï6ßÛõCËÚüiûoÇiXÿê÷êö5Ó¸üón&mÙE/æw x]Õʸ¼sï89Jò “×+<óót1¿ÙÉë?Û|¯ìÇï&Ñ>áºf3Ýñ,3Áq}WmxóGÓüÈZ{)Ýc™Åø¹!×=×#M½›:ë£ì·l«{5ƹöýàZ{ï%ÑÅÅ„øÖXOÆ¿›@·s®»tÍïO/+N9i9ˆøõ¿Ó¯Ãlù|•—4–z7KÅÏû¬ºÀs›óõýOP½ÝW<‡76Óö¹%SbwJqéO[~åÉšÔùp´\ÕÙŒû™^Rµþª½…ˆ×»­:Àó2œ²Z”½t‚F—™e8[ÝL=œ{ßšÞëSÇÐÃûÐì2×?FF²º5•Wìž%ßóäûÄÀ¯¯u-:éƒ*9²d“ƒÛ?¨¸Î¸?½Ugã±Óž¤»Ýd—7S—¬ržÝŸl—Öç´R±tÙ¼‘¬Ï€GÊEf±½ö}=¨‰|L\¿¿QŽšßÔ°õ“Šß/â:±êãÄnïUê¢å$M°;9èh13¥ç˜ -ÚFvK®í?(å™HvʳÇÞma¬@ä³§.~ßoóz½8/¾ÏµÏ×q&ŒSáÄå|Ž“’©c­b ³>š¨äÞni!å¶Ê>•Mïµól6%’…Ç 9\Æøþ†?G'Uº×óLÅuÁïGZuÑߢžµ¶dý›Ï“©ZÙzçï?3ÑÆ+qéèiIÑUÓªí –ózÈ£g¾ý1ŒñsN^ßäõqÜ&²Ï UxþÖ_˜¿;E¬]¯¸žwMt~Ýñoßm"7¯Û·>–ë:ý¬/(Œ=¼ôl¹qÎÄýÎù}±^ûQ%~µtnû­¨ŒãÚmä–YGNQ“UìÆ\5щ1¶T·‘..|ôUPh0HjüêEÛµ¹ÊÇÛµŒû¨—þ¥ÙÎGÛ›ËuU¾ïq{¥½»ª]gòwð¸çD}`ëå4©R]‡¼;c¢ Þ=*ôv=5Ú·üNÌÜ ýÏ£Yíêò-Fi¥:HKúé‚ÿ'÷nÒzî©tß® Eh¾H{©îÒBœ?0γm:ªçœ¦—wŽW1ÑàÝQ› ×ÑÄà?wšHw;èeÑ,2qјäd­T§j%éÔUò¿/ÏOü|Ãöü!®?÷Ó>M¯}¦]¬ºÓD‹÷›Þ]¬³–ºèµ<€ 5IÛ×8.šUŸzž¿¾àþºâ9Ê+«î xníÉ;žžhc¤*–µûK¬7‘O”áÇò5T~Ä R)ŨÜàÇN©½¢åøå÷Nù<ØóŽbT@#ñy&ßñú†˜ÄçÚy[Ô ‹LѲ¿ï³‚¿l¨ÈwÈ_šG çùÂFC+Õ[xž×„ÄúÏéžækës•xîø££?É­Lp–q&꛼sÏ´ÄhZ¨§]íhŠõ€q93š]ïìF-ë~ƒíþyƒ«×;ôèSÞÿ•Ö8Çs‡_p,Ìõ Y¯›6Ñ×¥¸SDÒZïA­3ШaÍj=ÙµœùiÚ—uª•îϹõVl%ÕɳT˳ëmt8^Xö?·ï}1μZS¾J{†z’~A¹.&2.W¤ß”¥ôñƒ¢^—:ÕLµìËåìÒ:Õ™*͵lü´½n~:*°§í’'\ås¼JŽ»?*$Æ5ž{®½p³ÿ Ûr¤ŠÙÕD3.º¥/¦7—®/ÝÅŸž5ªøl•.’Í}i;.v‘ß7¯óž3ÝÊú¾I~Ùoý³ý6Æq°Áž¥ôÄlH ­×–Üâ5Ÿ*[O~Ô±xÚþ¢X¹_Yj÷X­\¯äþöb~>'Ÿóßðõ¦5Î1NìÒí'.ö=KÁƒÖßS—2Ñô‡/th7—zÝ¿ü4´óPú&yÑ©µ3¢Ù§Ô×–o´ò}>ñ}¤8ÿg«x}–¯?¬ñqšº¿Þã=ÿ,Ý;`ñßòê6…F)kÆ%i©înå­Ù ‡PáëUz;DèX¯Æ9®u‡É÷°ùù?Ïá:åï“ÿ>·ï,j$ù¦^ÉgéŒûó".Ü&õÜEU¼îL!…þŠÃ¡T_²4¬zkœo }ÆÃ˜xþáLb]¤¡´þ¾«ò®ð¬íí°|4qÞ#¬è›~vÏB‰qJ–vø)ôtk‘sí6ݦÇÓ·+qr,µjæ>4¦¦/Ù^Ý:zK ³nW—†1>ïósçG¶¿¯ùEåqW¿ô@~*výnvçºMåý‹U/gOjαD¯jÐ¥èñ­SnÓ˜æI†Ý çk¡ É£Ó±·¡–ÃbØ€ó­~yw'L¾GÉëþüü",|ò;…⣊çQqÝ$í¿1Îîo’ºwŒL¡÷ïߘöu½Mwx·¸U¥3=È)‘Øç¶-ŠÔՎױj³Êoþ¾ø,ù%¯wñ}²èc­âë «nðüÍ ½Ût¸œB“ºÉúôõm*ºÀ͈ˆÞláè[ObwùPû“ç·øÛéØ.ád5L®~~®—¥âû.Ûûbqx¾¸?GßYÈ-j{¬ÕôƒY»NCÎxéCÇOVë°ê`4[¶Íûý¦„0¹ÞÊÏ»ùý þþlïeÀó{\n½ûàðsd ]–½Eí"_½n;pùwlŸÕs1N`CÁ :•ú8ÿàæãLztfí£÷š…lïÈ£]{,õ£¥©cÊ [?°Ëî&×Yù¹?áëtþ{<Ûú´/Æ92Q8I¥!óÇ{Ÿ”I‡Öº÷ýÞ–âqtóØë~Ô;±êœ¬^:6¸„g¥áåÂäóJ^wã¿‹˜yàžqôÄl•¸~«óÙïØ4§²õ 7•Bh•I=­7°é2V1tÙì‡JìcÒÕöóÕ±øQ¯V¾<ö{½‚×yø9ŸÇxç«n0ŽõwxU/P³v[|2iRB©÷wG².kl–ýÑZm¨*dбS×lm[@+Ÿ'ò{(üœ—çž·ùúתŒs¦þz»F\  ýõ_ošIõ‹œl®Ô±Ã“{Îçê'§Å°…/…—†ñy‘×=ø¹˜XWþ *•¸CWú–Š’ÝëKnÑ^ÔÆexðcé¤ ô<æ×5›óeÒ¢±—Í?YÁj8t¯9©ÆP:VqðÞÓÅcXƒïõs?Ì}ëÅsÐÊÒ>ð•J¬çw•îYöu3À¢¶ë×iÑøÂi”q©äÌîWnRð‰m¯ßnXÉÖ”]¸|Rµ!43hê˹ïtì’ËѓޛfÊûcþ}ù:‹¿/îãn[Qbœ·³ -k¦Ñ>s‚köú›ô\ygA,[ÍJWsÙöó_Òy7*ÆrÜFIùw1<xãà‘JŒ÷ÏïÙª1Žm2Ú¦‘0ûïy“J{ZÑû‡8Vdßíëó–úÒo/3¢òŰª'S˵ک•Ï­ù¿—×Åx=œßß·ý/Æ)Ó¤öRK@ ZP!nd³›teq÷ç5g¬eb>BÃ3 cÄyÎ÷Ï|žâ÷#ø:/«^ðü’=w.O“ê’7è[̆½#×±³Ï+žB,/T.¾‚}c¼ÐqGlãúæ÷¨Ä¸uÿl‡ç¦#Š·§Qƒ†5_­>{ƒTÕ¿í·g=s^v¢8â©q…¹Æ‡Y1̽Lãžæ‡1^àÿ~þ» ®CEŒfpÍÎjâõ~«>0ÎoBÙ#=F6ï7tuä :šUãÛÚW6°³g–v J÷Š=[úvŒTÿ c|ç¿‹ש¯äïÍ÷±¶ûÆ©üÌ¿…ËÛ4Ñ0åjÀ ,ùd\õwÙ¥FÂ/1†RÛvŸ V¬¿‚i./©¹J«•÷ÿü¼[\‡=Pñ߉ù±ž\W´êd E½êˆ±àûJ)_³}?Ä7¾AëÆ+~¬²™êÜ~•Oê:íÚ1K¿‚•îScžOG­|®Ïï;ˆ÷‚-*s¾&c÷ªcs… ¥Ò¾Ï?¾Ýsû–©Ü”§=¹NÕ—V?}ÅEÏz¾ÜkÑ—v-XWDU8–=ÔePÓEZÆã’wþ]–Û¯^øíS;éþ{ÃÏÞ—ãúíšqBŸ‹ä|¸n©âû®Ó„²§|KµØÂÊÌ®çÙs09i†úŸù:–Ù¯3rë-óXŸ¿î CCy=)®#Ÿ©îu-°Â/ÝNÒeãÏê%¾G7¯Æ†QAÉxéyþ“¯Sklee÷ŽrªþaU»ÑÔÔ v,[º[(„ñóœFòïVø~K<¿kLëìÄÿ¾ô‚úÒ ê«TXk'þ'ü­¼·ÊŸíi'ôV±G°†HžÚê-_râ—œ¨±ûÏȉöÒß’&|³¿ÐoJ…Ÿ’ϬGý‰sûP>³ÎyôûÌÝ›=ÑÆgñ_õ(þ«^yyŒÙzÍf&($±?ë5kë1–Û‹"·Ç˜à5›”\x®>T ˆO,’×lb=Šs÷Êãc¶=Šóò¢¼fƒ¥^U ¯EÛ~yyùmó~y¼O±Ð///¯Å`©Ghî^SF DÙ‚ç"A"V¸áôûüü%'þßΉÿ×ó¡£ôÿ¼oèŸí×.ô U#Põ’çl^½sûTž³¾yøòäö[|·=à Rÿ³þÇÕ¯"·™>—ï¬B &à!y’ýYßYîI–—_En_2ÁwÖy¶¾THÿÖ á› ã€=‚2d5‚S ¡À<¨ @!ô#•üg=ó補ۿBðŸuË£7iî~ð_ÆÕKù¯zXäåUfëA›˜€?áßðØÈËÛÌÖÃV‹70'ÉÛìÏzØÚz›åöØÈím&xØ3§ÙÀÂ3'|”ã$zØ NbÈžd"PB”áÀdãm&4¤ýdžàa Ò¤÷ÜßQ ë£kÞ>Þ¡ ¸AÜqÀ>Gî-djˆ^…>÷ 8#è¤$ Œ@¸Ñ£#ÉãÅîïɉ&þQ.ü»sàå¿¿#÷ýO¯ýþ|÷wæ:á{%GY(Èj›8"àB x ð€Á§à‰ L”|kÃA¶à)„€4Øÿ£/‡à[’ËG(·w£àÑí-ø!h3€«ŽÞP`ú7ü8r{  ~!’ÿ™3‚>d ùí/xÕr´¼ü8rû åû]ˆF‰²7r˜8Iµ9À‚2'ˆJ,ÀâJö EO4#p†Ðt’ØòòéÐKâ<€" @ 1ê# LÀ´‡0ƒApƒ@〽àM 2l|¸sûé%ñú#p†ˆu øBÌÆ/ëº/ë:»ÿœu›4–Ex§È @Pj€x"8²7Õœ¬’_­/‚Öœ¸:)x}8#ˆu’_­‚9Ø# C@pC`Ç{wˆäEÄý-ÀÁžx °O~"P ø5À( | 0åá=”Ü„Z‰±*ü÷%'þ½9Ñ6þwå¼òàwü3ùÏ6÷ýQÎÞq"P Ð4À<p @ Ó ðDð%%0do¢8!#@ðEP3äyj”¼nCApC°ÆýO¤¼¾ÀÄ¡À<Ì @€Ö ð@`'‚[,ÀAž”ôp ¼ð†<¼× 6~·‚÷š„r„¼÷|om=Øì!˜Ô¹ü×t’€‚8AH øBPFà Qé$aù#p†ÀÂA¶ï 4GÉ“- ¸ApqÂþ¢Kn^°o"zà&û¦¢7d"PBŒ¡À< Ê €05ÀÅßûxÞûOÎwÿÎ=>Ýÿ~>Þ“Aø®¤p`ž¨D DP…ƒlàà2'XȾ4#pF°é¤€ iÀ§“‚/X¸»ŒT#õÀA2€Z8{@0*Œ`žÊD D`†ƒlà‰MJi8ÈÞVpBÀF€à‹À5'oȾ6þ¸:irþ þ¸@ ×G~(0@‘£BIòÇöF0gä(´HiÀ ‚‰öM0HÎ ž!_A@á÷QPCHz`1…€  †¨ôÂdá,¢²O+(!®p ¼!²à¡…ð€à€¢Ó‹O·Ô ð„þ‹½ó€j*k×?ö86챌ƎcÃ:±Lvì+46 M±ÇŽ¢°aÇŽ 6ìXÊÖ–€ˆql±ÇË(öÿsrÎ>F®Î{ïwïû_²Öo­¯Ìœ 'ïóž½÷Ùy µÀ”e¬S­ùÇüìÇüÌåŸ3?S ÿl6wOPFà†¢ Ù@ƒâ4wh„P¤À ä(V= `µÀ”(ÜXàŠâÕ P¢ˆc…BVƒ8 EAë€ ¨PØq@ŠâÖ+P¢ÈcA6P£ØÀ ²…on(þp 4 ¸C‚€È! ½ Œ`rD²B1)Ä¢ „hbÂÑ PB@±À" V ’á÷RJlÀÂ2ÄlÀ"3„¦V ‚àâ€¢Ó PB|±À V „c+ÄÌ@QêA6PAœq@ ê€ ¨ Ô8 …XuÀ”m8°%Ä!X ŒÿNv¸x@Ü ƒÀÀ¨!t#pƒØÃA6PCôFàá‡;P¡Ä)š€.GÖ7WçÜ×ÿ7æeÎýï¯zߤïý_™—}kNö¯˜ý«{ë[Üßä†>ejŒ¸¡G…ƒl A™€;Š(B(¤`r”HPTZ`r—HP`Zî\0ŠL…"‹®(´@`*\,pïC …ì@47a8P£À ŲEiî(Ì¡8€; 4B(Ò C ãÎÿ¢IдÀ ä(`= ˆµÀ”(æXàŠ‚V BaÇ)Š[lÀE¤(t° >¸¢èÈÑô@h(!†Xn­AX¸÷‚(¸ïƒAV ‚8â€+úM °ÄÇóåöù!;×g #pƒhÂA6·ÇñH!°ˆÈdR°5e2ô”0`jÌ\!²@`*ˆK $üž›wYx‡ÇÏUá?ëÚ”õÎÏLÚ?)0¿‡÷²´ÐyäÂü±퇥nËÞNù¼³WŠþ7»äžßQMš- غà ÉjÚ¿è+u‚/”;a¾d,Ç„Gq‚†ÿyÑ3$´-_Z~9ÿä²¼À„´ý;èÒ·ûXçIJ­ T^^±Žòyp!BþhmòNÃ7 W?¼–Øãèóßq¼ßÝÕ)jÁ±ƒc#ưâ²lùÂtÒáYoãoç/“ÊåÏ”^¡ÞI;ÜÑ\éX²7ùÔÞñ{æ:Ú.»lkßš!”ùƲüIæ/Êû5|V<ï´ìþ;EÞgr¬Ü1ŽãtáÒÉ"ÒIöÛg)#V_&±¯Ó~ÏvÒ”—ë] ÷"ÛÛ·k8%Ïzº-&pe|×ÊüÃy?Ó¢ÿ:ïßøV±âuþÁ]š~å7æ2Ȧä}1Žt§übÿËÄ:°ÈÚ­³vQ¥Ön =Õ“ôâì@‹¯§œ›|‡©!¢/ó_åÇ9®à}yŸ*Xns© ã4yn?BÓÉîz­”®p™$î)UònÅÝôÙ¶õ¯W¬ïEVút¼íA×Ñ+[o7̳2DÌídyw¼ˆQÈãx¦`>-Îù>JŒsú~éî¿e¤“µæê{7]µ£Ú-¿˜¼›¾U=3n–x’ãÞåKò^G³?¸· <BÙõ™o ïyRñ°éÜø61ïÌÇ髼JŒó¼8g<“N|>ìe!^ËJÅ,µ‡Ö)ý¬äÎ>dúªû)×ú­£–Ì)½?Ü¡,‡Žånð¾]7E_UæãÐ ®{g½¥íûtÒw*çÜb!ýÜW-¾^:ަ\qëûóu5¹¯Ãoí6®£õ['#„Pæ×ÁûBÉ ósè×kv'ªÜñdåú’A•-$¥‹.¿2$NÈO@:·HM™{=µ–ä’£BŸœ/þ,¼?õ+Ÿ'ýVÁrÓ¿ÊwÁ83Žú—Î ‘ñWê7¹t‰”âb¤ÿŒ£»öés§Ê â{¡S•ðÎëi YÁ÷“Jà÷òd™O+Þ(¶º½Ž)òHÑ0èò¤?Ê7üÊ_Њq^q6â53H×Ë L\z‰ä+Ó$lø^l-YþÛ Âët=­7mú®Î‹t¢¿ «ÿ€®¿UþRÈÕ»ã¸O.^6ål.ÆPžAöïi¥ê~‰Ü1 (uu/ ?ú´&h ©UéJ#ëzÊٶꬣÌßþë¼ë' ¾žn;®+Ãu'ºf¢ëlkîþ„ÏÑÙ@o>i-1­Ö‰~kÌïCϼ¨Íq] ®›´&}Éœ^$ïç§/Ê"a=³RoH÷SÙ”ûs¶«IM‡‘çZì‰o>Z$D¼Ì_Ïù|,䌼QðqKÅ瀣Î1Nµ×õÒ~dls¯ÛïY$-o‘ŽÑ“öÓ£[jòìÑ—ôT5Ú1Ù°În½¦Ãò!bÞ.óCe¿7>*š\)–ÞYôÝqÔ?ÆÁMD5ÄlÐʪÊ">ÅõÍØO[HV–TÝéC/r§XþHz°d*K†PæÄ|ÍLÐ%u\jü–Þ)øçGsÂî§£þ1NÛÞ7ÏÛ‡dBéU´ÕÍIÍžkÏÞv?@§Þ×wØú¾äTpˆL9'’–}!½iä—çÓ?óbuÄò?xNÞ/Ίq¢ùx( ƒ8löV^$«z'¿²â•8Œæû‘½7B¦öã;˶mþÅwœå¦1d懴xoI¯Ð^ ¾ò×tlSòùb¤¸KÅ ×/ þ¬hkmçµJâ=º\wiE}ºú÷°´}ª˜2Ë3áý¼ì ÞO°™˜oãÐÆÁC¶ŒÅ7ƒäsò_$–So{O [=B7ÎH®}ÙsoÃ(³Ï»«ÿ—¾Ár¦˜ïSõNx>´ý¥zÁ8¿Ú¢24ƒèü7ÿ^Û–INÈ Õ½r+ž¶\šä[¸À Òvü†ý‰¢i\,_ÿ1÷•ùa±¾Áú+ó¥uÎéÐ`œŸŠk%žÃ3ïS˜IS4ßÓç m9¾XÁï‡ûŸGý“÷ãìJ!¢/)÷Wª8z|ê§b…ì ¾Îš|õ÷è0Εì’+‹ŒÈ nG?íŸIçÚ–§ØÑƒ´xïE^E<ú“€³KèØ(ºÁKÞ¡ÌljùD²\æ[Åê€å’8ôƒq¿µÒ° rèF´›¹V&™ylÈ•ãÕh^U5ÏÔ*jòsÕÑÝjäÑÓU5Z¤ÕmB™2ó]ä}¤.*˜ßë,§Ì¡ŒS,ÝVÔŸOßå[wnytÔÊ¿,e]pݹê—æy’D‡m›žÖö)"kKe9Å,§ƒù’êæ~|[¼ücûÿ}|­gÃïë¶ùe«³‚ü¯o¿@>ì\Tø¬9ɳºjï¼½ˆÃ6¬¢ž®­¾jEÒùÊ߯†bð¾‘ÏìsbùTÝhlÊ){ÝÛuœAönÃ.W[ª–>YÆ@yx)¹½·ª›êż!æ×W%wÑèNgë“·WŒY÷øù+L†q&Ø»,Sg7%=k_rr t‚zÎµŠ½ºïMeãß{êiXÇ[w dy u„>ý‹àOúPÈ™Î}=sR•g·/—4’A.Ðê+kßË ûk÷ \½À@—:r`uµNdefÁ€ª#õôÚT.9&„²¼ræßÉüüØóÆ9ŸGƒë/Y0­$ƒTýäÝyÉ– 2~TžŒ~)º­©ÛŸÆªÈ£½{-›¨§Íu3nÐ)!”å½°¼/6ocÏIöütΛÒaœ2%×-Ñ6ƒdôœÒò(êyR™#½r½1ÐʳtÓ –îBÞvæ•ôâs€åÐ3:æÉú3óÉw®/=Æy¦i:)öñô¹¥éŒÃÑ-†zz’?¸8Êz*åì<èDŸM^6ïSúNÈ[hO*æÙ×4°9¯\„ïãY«­éäfúÉâ#ž›É˜ŠCçÍÚœH£½³Ümµû’óœ]¬Z/¬gu”­c˜5ïslU°õ¨ôñœQ µsNzÁ8|îÖS/òí¶×LâŠqA€IÔ1]¨ &[òúË»õ´ >}Í]˜—Ê|Ç,G…åʲçµsŽŽãÔØ•=çh:IqááÌD9fv¡Ì#It›¾cû¶>dÝ„UË®¦úMÆÜtâú]‡Ïg{-Ìg²EŸJg_#Æ™>udšn:¹0®aþ¬óä·Wê0 íÚfqÏݞŻwîëhšû°kùãƒE?j–gÂÏ›žŠëçü+®ïˆÇÚšN.æ¯3xÀyÒ×|àxjÀaÚ{u›¬‚•'Ùü‘å °|¶ž¾pm"–¢Í ëCÝ`œ°ß áµé¤Ð/\2ÆYR¦êª™K&¡k£&õªQó‹Ý+½nMÏÔÎÕ°`ÙPÊæÃìyÈòa&„.}ñ´Znѿҹ_j0­½³·Gt2G{f_­³¤ {ú‰ÎWЉ¡wxOìOø|Æhú§;— "æ¶²Ü)æ¯?nÌŒcíf6×­½àú±'¸à§tRõv_n×틦ëäÜLJGYîë·Ìï–÷Au!|®„RØá}õ'éPö•HY:q”óŠ3äÒ“}‡žL¦ÏÛÅ׳©'¹;½âÙhЦ=sî°`Ñ_¹|Ù¢³ürU#|Îä+a~áBxÿÛ¶Â<ª1¯ŒS¬2yÖOédQ­Py±ZgÈ*m£$yKJ÷ŸÛ¯CžA¤»â·ƒþÆhj<3Ù^²Àý³Ï§õNÕªk×^‰ûcžÛ#ó¤µ&}ŠEÌíò–ÿ{¬';,¢Äï¯ÌäÔ¬ˆÎÛ¦‘³ãÛ%wÞFé–¼ÃF<˜4€˜ƒ‹y%ˆ¦ê[¹ü{íþ’oÊtÃrˆØþëÎ~ô.¾6åõåsg÷¿`&~yø\è”FNî/ݾ_A#mÚqï¯ ¯õ#Mƒhº·ç,"öæsÎÏ—RÌ?œíë8ô‚ë×ÙôaÓ[Í䨅:þån¥’×ÃÒÆÌme¤®‡Î[7ÏW.ÍyÝ­hq½Ä>¶ÅÖe|Á]›§9÷%Æ™WãîÓñÓ̤^uÍ„)ãS‰ñúôv¤‘F]Z±¤_âÿáýÂBè3Ãi Öié7Ço¬ë܇З J¼*CÏæQß¹T,Dì/,·Œé‘íw²¯òY1NÖ®Èå×5“6qÛ½fl=M¬¶ ÃiLÝKÍd<ɰE­kÍ*#æ³²¼&¶~a9#¬ñùDM ûÜzÁ8e®ß­š\õõ»÷õÛ]N“Ù³ßê÷ÌHy¿iO¢t™FÓˆäK®W‡ˆ}Ÿõg¾=ò‰+Æø¨ºÕÝ”°zwèãp.Ën'®+;œyüÒD”Ÿê Ü[>…Zsõ®Õçƒ'9Ñ·åüñ™Ñ”¯ƒÊöØü‘=¿Xý:çXqý'Q[sÍÜtžÙâ ɘH×'}¬;¦ÐÛÛ:–¿šÕ‡d÷îÔéúK߉} =¡üüîKžÛν÷sû·_Õ¯‹ŸM9®ÌëÇŸ'Ï_uê7Gn" ¯®Ì=9…vªÒtÙ¶Zjò¸gDMWÌÞ³gâ¶±±ç0Ë#`óp¾¾¿~Ë0NBƈý{Kœ',gG÷zƧ¬m)´â̼A¹Ëö'—f8©/Cç–ÊS}­’Í'+æÐa¢ûy_Á|áç/JŒS·®¡²ÝxŽÌß‘š\'ãTMš<åF ­ÚI¾fÐ@28¾ÑÍÕï¢éüû#sEÿ©?wæwÏòÙº•=W˜Ï¿C/‡KkptŽ jüvȺ §H÷ÐÖaåŠ9|ža4uL+í:ÊrË™?û{XŽ{~:t‚ë»,øð®J³s¤sîÌÌ}ñ'‰KL¾"1ýRÞg]CöÇ'«Ò¢iá~}žé(¿Îª,ú¥³9‰ýÎÎÒÞýáY};t‚qî>ñó˜g‰ÓAÝ' :Ij®ŽÏãy”¦·ÒµŸñQC–ô¾ñ¶}Á:U;9kt’NÌÍcÏav¿X®5›‡9÷#Æ)—·OÑzûÎ’:óϬ[è$‰RõöÎÛGé£6>cŸú{“&œÍýàzáêOÞMW}ù\X¾¥Öc¯lfÚŲ´`^°÷àúW6þzµã賤Rù2wÇŸ =åcÝÕ>F˜sdmWo²f(7ጡC_Ê ßÛ££lýÀöùY¾èŸÃ¹Ä=›‚åà°>çЋ¿M¹µUbñ2¥Ï’êÓž1{ ®kº^)?òíw&ùæ«;Ò¸ô­¥SbèS¯§Ë7º„P–3Â꘭'Ùó¤qX±ÇÙ Ü Ó­C/çåý+¡›ÏÄ9åÊwÏ{‚¯)±i×1ºúàèæ]=5ä톚¯÷‰¡½Ç¹RóeÿžéŽís3Ý_¯Ä-xÛ‹û½`ÜÌŸåCÏ4¨åó®ãd}£_o„==&<ß5¤[úÒŸÚÆˆëV6cÏYæÏž÷~Ë–îÛ:ˆðý‚ŸWj0Î$n™[ ó–¡çÞ­ð=Nöòð{Sÿ8ÍwÓÿf‡âضêCç)ÿ,Vµ\¨˜CÄæál¾/ýã±-ù¬h0éÚ€¥$o®Fë{òºÁ8Ü[Âii$¼áäzU*'!Ýÿ¦=N5ÜZÚ>MCŽ }dÕŒ‰¡ …åO ¥lÅ꙽ßbûËl_‹õ‡n0NxÊü^»f§ ¹ÏÇÈ©·V”ÚœVÛ¼ÔëOÒ¿ØàšóÃchãQŸS;-ÿ’{Âê™ß÷=&îðýM¾ø÷NFŒ£¤Ïn–§‘scG—|ºò©ÂEërL½‰ßýQ#'mŠ¡ÇâFnJYî2»û{ØzŒõç+ÆáâßZS‰ofük…ç1qêÏNÈ z+ìóìƒ|ˆ¶ ·S#æ¹±\f–{Ãêš=ŸÕÏ}^Ôý«œ—!6e~KÇ­»§¥’JŸÖîìQìQD/*±}þ ZOz°êç[Á”í[°ý>÷ù…âÒÇä?vz¾VœJ4,Êoo&î§;ôƒqøÈÓd.VíüŽ¥W¿ý7ªŸö/|‰#¶ D ½VèÌOíƒÅ}–£Áöþ‹ÿ;øü; ®?õú¢|³[Ÿ&Ó½žgV+r”¼¸^ «Ö˜“´ôä²3oUõ!ÆIî>Q.è·7¯;.Ó‰ùlß—ýþü~ì]?ÿiúÕç¢ãþޤ}…ò_2‘Ï«G¤ç>BR½ó?IGM»:êlao2ô§ —,5bhÈ[Öˆ©,'¤áß'Ö!ÕHÝ»'·Ä܇Np]~¾j"±Ükç)¤ëO¿í ys’Ž™Wâö£*ÒÔ¾±üë z»—Ë¡fØ/Ùû1·\ø¼ºÀuùýUY}ZÛ**W )0ûM¡ºÍN‰ÏßÔãÜ#æ²±œRö¾‚倱ý\‡pÝÉI^k«O‘k…µ¾¿žJV\w¯°»òN»¸ïÌWˆ¯wŒóÛô¼Ÿì/OÏa FL«II;«JŸ²ÅD]M©™§“5„ÛÅíS$FÌ+bï;Y¾ëß¼ž+ØûfçÜ*=Ʊ/½}Ý‚$ùúÇç^ñÉ$©óoCÞ?2Q,v;ŸlèC‚$,Áºja×:³•õCè1}—§O¸‰óA¶ÎMXRÓm‰á¹ø~й1Θ‹c·T?Avz&ÕÜÔ4™Ô>yëZ³ÓôytTÞ¥z_ÒçeÅ¥ÏzÆˆç Ø~9[7±\œ†?î¶ä…b‚—ïêØë¿6¯tèãô?rì‰9î¸ð^êé»t’*kÎiê¥y?zðN?²¦—`CW=…©Ð—ó#ì¹Äç¿× ¬Î: °)?ôÈ\¤:NVåÝæ?â QM´ç¾ršòùbþB®j -Rþ]-ƒúË~0«£uÕæ—ÍEØþ:ס\Ÿï{ÇH›‡“^¯,p„Lèì½¢O£T: E7æNš?±^®tä¦"†Æ÷¾¾XÛ_GÙú•ï•Äý€ò\L´Ô…°<–ÇìÐ ÆÙh Y’2ÿIXóÓsÕáäÄÖ§5lóSi¾U%ä¦?üÉô•Ó×÷ˆ¡Õ×í¸×¼j°¸Æê˜ÏÕ~.î;:¯;5¸~ZÖ»ªÑõŽ‘Ötü¤9LÖ½ùýóÕ›©Ô¸y:½?ÙyÇwrȲZ-àLõ‡ù‚Åý3¶ncëtö¾Œ­ëúÀõçyuØ{5ã(Q›–ë]á0©Ðgë¡´&i4ùèµ4~dÇF[Ó°.p,'tbþ1;Â÷ï {oö­uãtluùìÂGI½Ÿ’ܦZ“„}‘4zÎï÷ÔZ}Iц^/·Ÿ‹¡6ZWø’OÅö7Øû’ɽjÔˆ8ñYÁÞ§²}<‡>0Žn݉³«%–—MÓü7&z+qü8sšwèCrwÞíó|]Œø>“ÍoØ~ Ÿ³øRÌcóSv^È¡Œx(ìÙñ)ä~ïF.½Ç&Çk¦ŠghK.†þ’1·ç^PÄPïòs–„Õþò^‹½obÏ9~Þ“‹°ÿßy=í2̦üTÛ3æq½Ò®Ûàó—ê&‘®ýZíH 8C»u`ÿàKZ~hÚoc%è¤÷IzÞ £,_™é‘å­I6nÒ¯5ŸS-Ãu‹6SuM2’çCŠ\ï{7‘„vÛå›Ëp†žjRrJK?ÒeÄ‹W¡q_žé‚åÛ1}°÷ål¿”­{úÀ8§«Üº×»‘‘x¾ŸÐjˆ>‘Œ©ñ¤÷Og)¿Nñ'§ÇO¯\ó¦Ò?Ÿ¾K6‹Ÿ;[ß²÷lÅþ.çç¡ã9¾oMÛž”_U©ëŸ~‰¤éŸµ¼ÎÒ&+ª—›áOÛìeb¨ÛÚq¦=Áâù v®…ÍŸÙ{3vóÎug넸O罓ÉΞ¤WH$~¬=ÊÅŸ¥—žÔ+cö'Ý«ô}ØÏëŽZª‘“‹éÄOÖÿØûR–çÆê˜íƒ8ô‚qbÇ·¯]E}„œ÷}Tîè!¢JNX\¦ð9šÚ¯ñ¢Œó6±ðçùchÇö¢N|ŸÉöø>EÁŸo±‹yX_íkaœ-«¥i¯:&³Š&Îj;æ©´­Ü®O¾çhÿ'ù'E{ù“ûÒð#×?FÓnÞ›Cço¦ì:l¿™åw²u˳r~Þ[1ŽG.÷ÈÖm“HÅj—«e>D"Êj*oHÕkÆÆ“;cSšN ’ŽÁ•ú50Óg¯ÌW&ü9­h:¦öýGÊ‹çEÙó‘sã÷Ÿß+X¿p>§aÄ8«–Ý­¾"ùÙô±X\Ÿ'IL·÷%ÝÍT–ø~²%j0©Ù+߯e-¢é“g#—\׋}”ÍëøþyS|¿Áöíøç„ׯy'ùpó>ÝO£} ¬4$‘Áe¶tc¦‰/?¨ÓiŽ~4ÿ§ïòaÁâ¾û|øqÒú3‚•üSv”æ¯MMnçWœ,î³~ÌçŸP°ó'½àºÍv•Ê}èø^²üø²qoš$ï½ol»rÀLkfðÕWñ!R» ûX/š&ÏŒ’ëf‹ï5Øó„½?qè×{ñ¬qýŠ’½¤ÈÂÃ;çå;H2fý?ý²™Þ8%x\ª9_ümýÍõ£é3Ï:%gl ÷eÙ}ç×'·„>òŠ?‰ë–ôËÛ[G*½ïW·ý‹xÒü銢YŸÍômÈŽV!q¾¤3¹ù8«Ñ—ûÌÎñùû»GuoZyÚÙ7 6ïv^_é0Îr£}|W¨ÿ|Àõ·û¹ÄÞ:¹™ó޾v¶W<š9 +túôä™îIÛ|Èfî.¸â÷/SìYó¸iâ¹Ô>Ï;Ü[JÜÄúäçïïìwÔ?®?EyyEà ÜÓ¯~…x² ]®rñ º}rîMÆûÖC3¶<¬M-¯ôúR»¦Qv…¿/µÄ}ö’–7­þXÎï_hpÝ×éÛêDÝ&.¹F\Ü]0žl(§®º£Y­[qj×L‚¹)f4Ñ´{µz+ªM£ìûâ¹Í[ÒÑþõï*øz«`ºs>g¯Ã8ï+q 6=9±hÀ™óEâÉÂj2íèAôçruÜêKÒWl˜áQ6š~^¹²æf}8¸t™;ÈÓ@ØÇ¿¡ø¥þÁy¶‚=œ¿7 Ç8¸í‰ëÉyÿá%W‰'ç—vÎx4;ƒúÝî¿1Óx6Éút^ hnn;*HÌIeë|ö^•åð:ê× ,TÝmÆÂ5Ä×\0zfýxr1{Õ̇qT=®ô‚¥†uŸwœoÒÓsÇ vÙ:1H\_²÷ul_a\Héç÷kØù})\·ê©¬Æ³|W‘ÆM ^ï€ûj}þäZMk•§É€cCÈÓj 5¯­ÒSIËÛ[G‰uÉžl–_óßpeS^)ãßÌT}¹pž4žä+qhv¯Bè®YÞ«æ!ŽåƒVO_§Ýs&H<·È¾ßÂ~VñsuÔ9®›0ãÝùµ•—Þ§~ÙîÁ³Ï¬Œ'õý)ºwÁšá»yùÁÜ~d·£Šç‡êéÕ¡+×d¼ Ïó°u"ÿ¼~ ¬Û?(œóyu£ØûâäìÏ\Љ'íÅO½ï];ôJåëµ}…<]=µþ1oY¸*H\·³u5ÿ½¨ ›ò^*ýÄï³âºÓ—µYµq-ºj¹Ç»6¦x¢IÞ{Õ¥ ´¹#¸Ü‡ìš¯]Þ×åºûÞFSÅÜz¶_Åêƒ­ÛØzÊQ׸~ôðWÃuZZ¥×¹âI¦gÚýšï/Љ©+x÷ð&+Þv¼±i˜žî;¼£’{±)”­‹ÙyLv>€í³÷.üó©ßß1N®Q;ú Lûî[@y5žx~3.ªb&-Ò Þ’?†iȤe%¹ýôTsÚ³îŒ^â÷†ØßÁï›ÜT°ó{ü¾]Âö³u?Ú¦t´©õs迾ý;<Œ'¿í(Sõy«Lzaì£êÅ5dÈ5R&¦­^X?ŠÏ)ö=§Cá1µW¼-žo½ß“ûâTҤ˨m3+ø~q –3íOKXLóqíþçƒäi¾!³«ʤ~ý×|Ý_Cøõ ž®:[qY·)bž<ëlÞÀÏ;?(N÷YÝ+·Or4yC»YÁ ù¾qÞn|™t_NË¿=|k[ǃ¤YšÿÆñ32iB‰Òùr6}Íð ö¾Âù{:ŒSkSÓü\×ÒË7g½_tÌíT|Ô©L:qÏòwûø’f+Û{—mE¼ìÑa ËTÊ>þþ"ƒËùw|5픸ÌôþÕzãp³ç{6Ðø”IÝÜ»~ªdϤcÅUoPÅŸð},ŠõîÞòCÓ)”Ëáõ]XÜŸåŸÏÎÏ#®Ÿ}ý†þÈ;+„¾(îO9ô‚qÞçžzóÜìmÔ÷ Ÿç¬u²¦ýÙÅ.^¤=ËLhxX5”$x—Ê»dLýýU#,Õ'‰ç+ØßÃëÿ‚¢ß=É„šlâ¼/¯Á8Ô§HÙC[wÐÞ¹Þ´ þÃ@ÊWԴΓE ½¯;ûµ~q¼F*E â¾á0IÜ/gýŒ­ŸYþ·|Ö/Mƒô5¾Zoê0N·6 U¹¿‹òç‘ â¦'4È¢ó×luï…?)ù<ëmBZ$U•¿šU&r²¸îaïÙ÷5Xfë—¯Þ{cœ‘Ò{w¿·‡.[ñÊý@‹Cä®K¡í}dÑ_~IWµÀs=rßÚݵ#騹 ýŠët¶¯ÍÎ;³}[ö>Ü¡\ßïAÛ²™ q´ÐÔ1…sUÍçy¢çdÑ ¢é‹<Ÿù“S>ëz7ÉŠ¤SwìnÛ´ÆʾWÄÖ9ì='{þ²Ïßùü±ã\hx¿Üûe{éQwîI’HÌwF¸ìË¢µó«š„TB¼pó­¯"ièÏŸó…ºLÏ¡±ïU±÷£†nË¿ã}.ŸsìA‚6ö‰H¾¤äu£µ)·lX›VdÎ>úÓ‘OÕ“É{e×°R׳h­i.ëÝz !;Ç6y=‹¤Mkç»æ•ñE7l¿“ßâç…yˆW–Ï:¾Ï·àuƒqr¯¿~âýØýT°GQé]"IHÞXEQèm“µ$vÇ2Ùqð*’Îèú©Í’@Êꇽ7ç÷ψç8ø}•_ÅóüÝ`œ@/îÁêýþ´´ò$âX6¿D×vxÚnS®¡$£Åöµ²‡‘teQî_œ,>g¾>·›"œwýò=ç}t Æù­ÝÆ–aâéþ„/O\O"—úÝ®ºkè%ú°Ï„ði©CHZ}éÀñ£è¹“Îì_6‰²¿ƒÇöøsÈl=Âî¯C7çÀ‰uÈóƒ´éª¸f'»&J ›$_y‰êÛþ’•Xi™zªv‹eiQ4ò\éͳ4Åó¡ì{Lì¼;Û·eç<عH‡n0ÎfWSÅ­ tî»AÜþ¡âÔ{ÆKtÎú]ãúúûþ²z:ûñáEgOç¬Øùpþ9Àï×q]îtaúØCtÇÙÏ;Wü;>&öox™0/¹œž¿BŽ—s¾Ëñ’:yþæÌ»þ^ž!Ë»æ¼M8Ï_.Ç‹y,Y¿áqn<ο—]Ã<0¿—]ãìqžÓc‰Ë{`þrî‚Ç’EðÀÔ ¢Õ™×2's¥åÅ<–â„LCàƒ"rXžWìw¼ç˜pÎìæ̲rz¡Äî)Åy üè?z£ÎåŸÓ%Âßò^sfz)sx:„ŒÃ€¿™ÿÓØEÈ84;åÝäôCg™7œº ¸£à#@6—ƒÂ7wȈÀÜ!„'Ÿ§œ>›j!ûÆÙeßȯMÛ7²^¿—éŲ^¥2Þs“ËÀaù×¶oøÛàïå?0ÿ¹ïå?8ûçÌ¿æ¼Ñ™”\ÈÀ¶ þs±BþC€÷êæä¬òpX¶AÈõ &Á#J甉÷Ÿ(æËéì“îìË©|9¥h:`hÀíþè?z£Ë?«7º ÿe¿æÌÓæðMçò•‚ßÝ¿—‘ÓkØ ÜòYö_år¾œü’¦ÿ€/'óMÏ™‹ó­¼C‹ã!äâH¿‘ýú½l0–ýÊùrr¹8\¶„e<ñœýˆ™'^ ÁüˆYf„³'Þ÷2#˜±¢Ô çE,ä`;û¦sy‡'<‹!dÀrÙàš#;\ð+æ²Â"oNµyè*dZ¿ãβqœ3³q…lœœÞœœÓ’á½ÓôƽQçòÏéRá÷µºðY°93ø,Ø¡Hv.#Qû7s%rúK„œD‹SŽNNOv–£Ãy³›¯Š>˜ůÌ@!è1|ÃT#äè8û³³7Á'Ô^þßfÁ~/3ŒeÁÊd¼_(—£Ã²±íßð-¶ ¾ÅßË•p…øÿ"WÂÙ·8g>¶Õ›àÙ.ø{¾Å®Bn˜YÈ c¾ÅB®ËÇ6 ¹aZ`;ËÖáüEey°5ÄntòuöswöõüEehaÀÔhFÀ`Ѿ.¹ŸZoü;}ñ{=ñG?ü¿Õe ÖåK^lÎL±Àî\†¢ ªÿ¹9½‘-@Ȳ±sf(²üD®¸€¸£È#„Bî(ö¡à€YðnÏ™«ó­ÜÄ0!WG-äêÈräÆþUŽËŽõñ¹:œxÔÀd9<“ @&x&[<“Y^…(!´Ø¿È«`¾ÉJ0¸r¾ÉB¶³o;—›hRô>࡬²Å8¡j€HsäaGÞÊ\¶˜^±FÈN” Ù‰¶ïx·³lçìDçl­ãñ€ p'œÔ„÷nÿ1?üÑu.ÿœ~è&ü>,S6gæ—)«©VðŠçrÿf®E °Š9¸ 9‹V§Ìž¿ò·% >HPôZ`J,@Z`J!H -09D¡Á?Þ”ÃCžËï1wˆ%d—ÿ·™²ßËc™²n2ÜCŸåòµ³¹> D²ú/r-¤Ÿî/r-Td~#c[†»à/ÏelÛ‚5¹B®,—;ì ùœm£SζIÈ  ¼çYÞ¸Aäá h vpƒàÃsøÏ»Aüá [ðŸ774‚pÍån£!˜wÂ<ŒðµÊýü_èÿ•¾øW=ñ¿»r}Pãò?×ÿþɽû¼â\¾äËæÌ'ãòeÍ@Ž"Ô y‹(ÆØ¿‘“¡D‘ÆWj —“”Ny‹,O;gÞ¢Ù)oQ‚bÖ9 Z$(j-09Š[$(p-°ù7²|¾•³.äøp>n92eÿ*·Œeʪe|†—¡­&àÁ„;PC8Æj|nË˰÷¼ Wôµ@`*ˆ+H!0™í¡€LÈT4DlB~—)L@–#/[²…³X 0„LE™©Èeö¨!T#pƒXÃA¶Sfs¦¢sfO˜Ù£† À ¢Ù@q~Ìû~Ìû\þY½O.ŒÇ²fsf™qY³±ÀEÌB>£X€E \Q¸À T(à8 Eë€ x ˜ @Ђ֛S^Å­6à"®(ô@`*|pEÑ+P¡øã€+¬@!ÄWˆ!X€¢ˆ®†˜Ñ‰dr.·QLάÙïe™±¬YwÏbùÚœ¸4ÀÜ!²Ah`n\°Ïd_°D¤¢Ø€i²9Û\Ž£²‡µ 4¬È„<3«g¦Ù ù¼m“SÞ¶YÈ3Ó+PBØBæ7wˆd „oî„Ð4ÀÜÑ"„fÌ€KÈ '|r?Õÿ'ûâÿTOTºüëûáÿf/d}ð?Óÿ§ûwï ._²isæ£Y€E+¢Ä R ,@‰ÂŒ®èyÀ T(Ô8 E±ê€ ¨œrYζsŽ£Å)ÃÑÅ,@‰¢Ž®(ì@`Jx,pE‘+P¢Øõ@‚‚×Ëw2#h€ ¸Wø:³ö¯rÓXf­d|¾vp‡hÂA6×÷ pCŸ và1Å)¥6àa€+Ĭ@‘Å)„¦6àÁ€ ¢ ²µÕŸ¸A€fàÚ‡Yë 1j¸åÈÔŽÊ媹B¤Z!³ÑMÈl̈ÖÜ!ÜA¼`ʑ٨˜MÀ]ÈlÌæz„mîw„0é Æó¿ó?—ÖüO%\eÙš€; 3B(N.Ë6HQ¤:`2"À¨P´q@ŠÂÕð@€ Eì@b6 : Ø Ûd(î0`j¹HQè:`(x¢èuÀ˜; ?d5`2ˆ ØbB :`…È Œ0`jÄÜ ’p![[±˜¸ï}@,á ¨…l[)„Ü…,í8ˆG ñ˜…ÜìADÀ 䓞[kBPfàQE fn),˜\ñ%ËÖôcÞõcÞåòÏšw©…ž+È`r¦HPœÀd(Ò0`rk8°­ÈP¸aÀÔ(`#pC‡ƒl A1›€ :d5 ÛÜPÜá hPäF C¡‡;P£à@†¢v Fñ v †Œ@1„ð€( @a耨 8 …H¨ –Xà Á+PA8q@ ñè€ x@D ƒtÀ”2ÜC`—}Éïv…¸´À”Y,p…дÀ ä\ÈÏÜ!¾`jˆÐÜ Äp 4¤ ¸×ù:ÇÛ ä¨H¸½6!Ï[±j¸C´áÀ< Þ8 €µÀÜó9ßV ‡ Ãx@ر@qk(!òXà ¡+PBð±@Ñk(!þXàŠ  D#ˆ®hÀ TÜY<ÂkB(ÅoöÆÿìœË¹þwô¿ïõ¾Ußsžgýoͱ¸>æ<§ú;½é?Ó—b„ßßÄÝgB„P ÀÌFQxpg‚Q:`è9 CÏ v F± &Ø=ÇÈ­ûÐsì@~cnè7aÀQô¿—{õ©µKæóS´ÎÕÅ-”åQÙ¯î| óµR$Ù}Í/zÑoÝ6¼ÎëÕׯSç\$—±6eÉû÷M7%чSc-–Lά¼÷¾›ÜB[„Ý?¸8ɇÔ=ø{{ß'QtËO«\>Œ§,où0ßHÞ¯ï‚ùˆå§q¿Kצ^=L©wLëŸ"“Éí²^¿¿h¡c¼Üùùœ7)¿å`fk¯(ÊýÖÒ•D¿PæwÂrv™¯‹³‰×ßýgû6ΡÁ)£-Ý(‘Ìë—y&ØBÓ··LªÒÈ›ð>±‘´Þ”Võˆvâ¿eÁ„åî0¿ó¯rŠp}Î]»Éúd:eE§À6{pýVî:{´….í<¹Ñò<Þ‚ï}$½0²ÒÜ”Eßkæ—ÃrIYËqö%Öaœ·‘K%‹4”Îáb_ŠÉléÛüÚãʹÓîèëMða<¹$’ìijLýTxßY™ø÷0Ÿe–Óàœ7¯Ç8¡Ü?îj¤ ’…éb$] ô½{ßB7í]ùö£7©¸ÿñïšñ‘4zèþ‡ãã'Šþ‰¼?PMÑ“Ï÷ú¨`þ1Îy¾FŒ³ôYÏ‡ËÆé]†ÇÆp#ÿÿØ{¨¦¶-ÜÆŽ{ì± Š-3z,±cC,HÐØQbX°cÏQcǎdžuEDˆŠ14 `‰ ûîì½¶‘ÿñ¼{߸ï½;ÞÓ1~ÃsÇuìÅNæ7×Úk/¾ï[ßq'*f÷ÄÙäÏ `wª%¬OŸëEóÛiþëwR‚÷Ý¥¾QV½à8U“æºE<Ö“Óóæî5éáÃŒÁª³Î™¤éÕuÇ7…ù‚oó‡£ûµÑ’ýÀ8Õê×CýO©Ï õ£¥¾$¶¾vv³-Ò†>Ï8í°~ªÛ—÷“I<Ëwº‘2Õ:Y 5Ä©÷)—°¼ B}¡X¿¦òÜ8ÇyßêwGÿU/8Έ¯ÇÏÎ>w<튿‘- ®w]˜IþÌU˜ÜÉêZ P4¤óÉrﯜ "Ôçˆþ¼4‹õƒ±ü­¿®ÇIúדú¥IQú­¾æ  YÐè¨çáLâ1õìËýaúÜ´®Ó5dk‰·Óñ~7ôócó šóFýmóêä8“²{É=‘$OwQ”&‚ù`÷/G²2ÉæW¿¶_éñÂkž. ù¼;~XÞA„æêPŸ öûÏ“t™û,qÊ’’^K7vÓnlÏ÷!«~pœ›ï¼ÑéIËMÌûpœ’äsiû,r¡l]þ°éJvÕÁ´¤¢ÕHrï«Ãû›ZïÇÂç#Qÿ.ZÿVýà8ÉK×x”H¢OW‘N©~ò¼˜ á,2ôñ¬VE'ý¡ÒÌ sèH-ù\·_d@™9¼¿ý›úxÑÜuÚw¬ºÁëßø¾ËÞöI+kÔE=*ê;.‹TÚ¼gá¬uu-°—–ýã÷ašò~w´n©ÍÕ85mÎõ!³Úòº²êÇ·nÆ€û‘I‹‹¯ا_„•%W Þ³8‹ˆŽŸ­]ú“t+ZZÊò^Cꇦ–-Ý%÷U¢~Ñ4Ÿ€­ë¼Ï¶­_®] EªT¿»öåöER¥p碫­“ ~ƒmñ·g» C´ŠJ~piøj—„ƒ¤Ÿ ™C¨ï½ê³Mýl©¯žm®˜ÇyÈ|]õ’ÈAƒ9å–$A³”GÞ9w²ˆäí×·úP_øP´ðÙÙ•2ÐEva¸!ˆÐ~Eó-XŸ®<>×]Óú^‡jÛœ~ô ÇqÆ–Ëm>Ë#‰¬Jm..L‚¨Aš¾—¾e‘Þº¯ÞÒÆÈÇ: éý6CïQ.˜÷q¤ù ÔWÑ67GŽ×'Y»¶áâ$òz´¶lȤKð8ðÛö𭲉ëh™{•?¤å5^±5YCê~þ2á„_ïoÄ~μ¯6Í‹¦ýß6OE…ã,ì?ëË™$r#³dϧy— ³wÛskFdEåíµ¡vÄ‘ó—5dÇÆyן]aS©îé'|\ø~I}èŠç³Yu‚ã´¿~:¢ä%rSXfHãQÉðp]Í‹·#²I]¿œ†×+M‚EçFìÒ«+œj©ZàzøÉ@}…ižÍ_³ê¯û|b¨;øÙÑpûÍIÉ0²BG²ÿp6 zp~ùÁ‰\‹†HJÜ1ªÔBý×h½°>SzÞ‡Þª¼nIݲz'Õ—H»”6Ý[¥@G;½Ç¡œlòí`¹Êé×ý¡°ãÌØ5$¹Vdýõâ@Bû-õ]¤>ƒÖºŸc‘®-W^”~‰›×Ÿ·|I x˜&µ:V6‡„ALíß¶øAñ‘ÃÉ´d×Û“¥Í[gó}úÆQÿò u‡fίÛÜìLÚ´ÿ°õsn ,˜õáXûâpðôÑzM|!ë²Y47HKÆnÛø@òlÞOžúÞQ_|:ßY믻*>.úV¿d²ÐôGÏG®èuäêîj#rÈ\‘ÿÁÏ+}¸ü-±[Õ}~»á„æ¶`nï}[>‡™ú,RR[:9ŽóÀØ$ûCX2©ñt.y P¡ZÇMÁ—òàêâ˜Á zÃû®5Ê|Äþ6ÕhH˜4{ؼJ)ß§i? ëk}ãõ§3 dR!|q§©…hñDî9úD´¹¹ãu›GãàéÁˆƒÕÚjÉ ÈN;‡FÆ-¼ì莼o.õ dý×¹.¼nÑ·Y‰ o&“Rjçé€Ë°Î}ëÒ‘qyð qÑÂ5¥Æ›ûûýó¡ùÅs¸èçd­g¼®5¶êu2çËy6t˜)n¿%¦7¿áÞiÿhØà×`Õ¡4 aó¯ } ¾ÓlÝÝá|Ÿ³õŒ×ý}VʖϵRȬ25ž…·øþj­k¼î»?Ÿsmb #\{¤‡Ú¥BjŸGŸkô̓›£Õ|-LŸÃç†ÑçpêGLý{in­m޼ÇQŸ>Ùä*i8"¿KrÍ› _:´±Âà yá‹×6ó•ÃEUóQGŠ4äBÀ!ŸÅɼ_(õ§: ù(ô9“ö'«.pÅZÏa/$WÉ"ùÈsŸß„šf%çõ1C쪱-—ªÆÃÊÛ¿ì«%ýnW_HhþÝ÷£ÏMôs+‹_cšë~ä:§Ö…oú^%)· ‡Ž­¸ UO~p•u2׺;»xŒƒË%[<¡%LªfµêsïèßlÎk¾„ö¿˜œÍsÆ6éÄëÓª§cDz›V]%ÉNk»÷¼v††O(çÐÚ -f†·ϪKþÄõeÒ†£!}ÆúýÒ\šNýhiÎã¾Ä8Îð¹]'_¸Jœšµ¬~²|¼º{ þñfp,’æmïç ÌÓy»Z²!rFûÉ+‚ Çèßì÷’ÄÝÏ›¿Íñ´SZ¤²½ [¾ùx•4~|«õÇßÓ`ÏšW²«šÁÓ+¿é¹R£jÒ[ Ö’Þãö¥Õ á÷ƒèßôù†öCš+Dý×­ºÁqîŸý¶ºz‹TÒÉݬY0/ šIê_^fo†·Ã*>m䃭ÂZbµý®Â?Gпé¼O¿še›‡'ÅqØuw*©2þ̱¼ø4è+5çœ~wN~Ô5haô'ãñöºÚZr¬û¼‹¡÷ƒ ͦó õ?§yX4ç‚ê˪çfðÀÜ ^©¤Bú„Bñó4h˜wÌÜûÞ=’1þã Úž`Ý ª¨%Í× ïz1=˜÷¿§×¡Ïét½Gszm÷T8Ní¾ZL™œJ‡L½Ñö0©.W/Ýcç.c@ûŒyÔ’êVƒò`~½DçsFn‹k¾–Ðú®üÆ={ñP6§G‡×g÷URÉý~i®ëfÞ‚V%{4¾ï üãÁçñ#ÆñŸ—“õ8„Ï]¢uMs…iÞ3}¾¦õnÕ Ž³´ùƒ RÉ#×à¥OÞ‚.7¼WÞƒè’)‡'¿ô‚ÐC'&„gj¸üå>§Œî±óÀ_öy¡¿jÕ ^Õ£º»+R‰öä‘Ì5OnÁ´^ËSt3îAf½äñy¬»5Øû&óé ä÷çi¿¢¹t~ï¶ûÎvs-Ò¶aÃ=÷êRÉ7ÏèÊMÓ¡O¿^æA÷ í·¦//È¡ñº±‹ºkHû¤'«Å•¼/=]?Q]Rßpš/gë¯.ÄqfÙ%yK%†ù·¯,ôNÍì®O´mîêöš¦šØVeã‘m[4„Ù­¯éÂïŸÑŸ—Õç5 Ýçf¿6?økKqœ®ÖØTb!ؘ †li¹²ì=`R»R}`Rå~¿­¨%¡öé[†¾¦ëfïoO÷Íhþ¢®SͽâR ó´U/8ΰ zßM%‚õƒN%žI‡YŸk7=¸ Ëg'w õ• v.ý7iɸåj&ö9Y t~¡yY_Õ»œë£ÍmN¿ÝÌæk¨ðúþuµKÖ§’µö'/z”#6.èkI¼ ß‚˜'9tn.l‘×SKb¢ªMÌúĽWræ÷MÙú}Ïç¬Í-«LKôï ý¯/í~lµ+«çAZÎÌ©d÷I¨]©ÎmÈðöoš¼í.äW½Z?¢Êï)¹¯!ÌSûùÖXÇaSÎÎYç4'›¾ïb?Ç"Iê‰[Ñòn@û¦U/8Îþ2ïÔ2Ôý•½Ÿ)nÃ×Z÷nޏ w/¬eØîÍå€kɬƒïâk—WºßD׿41%yv‰†eßp¹ò~Ì]Äqbk­*I%‹Rÿlq'ú Ùãs^Ý›½?Cé ›bªÌë¸_KØÜ%ÿ>®;èzƒîcT|Tw¢¢[±õÙ<‹´„u•J&ïkùzõùÛ°¨ú’&ëzÞšøMzjú½Z.÷Xɯ»éú…æúÑz+Ÿu¿°_Kçr‰…8Ž·50 •ü&)£WÚg@WÙ Ç7¾ cCý×7ðá÷!Þìb¾À¹|=³9b`ßäòïÿPy7í'åú ›#ÅqØ}å«äyÕ)‹G Í€aK·-ïö-nvܱü¯Y §Ä’yàcl±œöóúóä]¾ï°óÚ}‰—uâítŸÛªç*{‡÷ÓbåÍ.û6g€ypè|ç¬\ø#¬Qî÷…ÆÖ‰&–|ò¼0ó÷!„~þt_‹í£¯¸Ü€nýÜƒÕ ^__&"• 8²cVܽ é·#úH.ìþ*.zò‡\˜U§“«1–,Ô^Ûwãv0aóž[qû“¿ñ¹ôó¢Ïat^µêÇyî[†TrJ%´¼-ipÞ 8²}q.ôêý0;ÕÙü‡„Ÿ¥!sžW°,êÂçˆÒçšóÊæn½ã×M¶9.z‡y«Ùk@*Iaâ<îÀ Ç2 ÆæBÍ©^U®Vð‡ß­†\>p7/X¡$t™î/Ò\(Zô=‘U/xýÚ½«,¨èõµ ôà€•w ññÃ"ç\`÷iý ü©a7"ݵçHsä´¹|®'íÏtŸîÏÓþC÷M¬z™o‘~дèpÇa¾Õ;gïÀùU/2,rá[îÀ;L€éÛWËš­%ṯW¿1—ßß¡ù4_î§±ûx5Xÿ qqF'ñ©¡8Ïldðw Ì)qHÚ£H­³eRÇL9¼M˜y|ð8-Y;FôGlÖ\~‚®—è|¶«epØ–*$¶ÏR¼þÚ Þö¾¸ëo ¤2Á?o…_r8ûêe %Þ`mÛ]´äs@ŒÃƒ ó­ºN¡Ïëg(ÂæÈñº!Û›5Kë˜J¾8;6­¶ÈïÙ=º' …m뜨?m_8×­÷…-¼qŸ+FõGóùÚ÷R?{ìò™_ÙÎ÷*gòƒñr®[V®K<›b‚¾Û‚Ë©—倣Î>^ÐdØ­v»ÿÐ’ºÃz¿j=u>¿ÏÁîÓ·å×/ìóó‰þȤþSœ¹< 6F‡ãX˼^*©žV7j° ž¤vo¶`f<9p¦á+ã8д7~ÁM-‘À ‡†Óð9Îôy‰æÝØîGêñºlÎl*·¾Ï„’Ùõ{ÏÊ&Ù¿Šð‚ÀpæÖö‡ºC÷gi?¤û‘VàuU•_?(cºJ’þð¾V"2ìÝ=¨àš®.<½cŒ7¸mŸŸá:FKz‰+·kFh~9û<ÜŽŸoÙyŠÛ—]`‘>©aŸw]%æØª»Î]ÈY Ñ–¯ž3tþ‘‹wÈáªg÷ÐÒZ²þÜ®Øöïhn®˜Ï_ñy¡çvÏR@ßç²yŒÎ?äñqœ²ãF¥ f^%×£Óîš+dÁÑ6Ñ9“ ³áí»GÛø@ë¯/£ËßÖE‚†÷ÂÝ' ëÒ˜NÛ¿&•:ïyTQ/øAÌçuYëÇyßg[†ËUrªó†Šv#³à„s·¡Ó¯fCÿfÁÕNôò…#ÃÔ]xJC<ú®šcß'ŒÐs4—Ö)ÝÇg×WâsRpœéõúô(,ºBvÖY¾ûèº,˜½1ïþŽlØåµQÿ^æ5¿¥Õ÷üÀ–¦ðP^Çôù‰æuÓuÝçþA8г¾òøÒpG·×³MYðæIZЦyÙàUnÚãƒýöTÖ2 )ÿÛÓ¯GBù¾DŸãèó"=GB?WÛ>«Ãqî–¾ç|…°Ï7Ùð»ØóL'÷lðÞ.¾Û{°?Tøýþ›· 4dÙèö‹go%ô}.Í‘¢çè¾]¯ÐgÕŽ“<³|›š#®ªûÒŸExdÃŒÒjK~ólXçTP홟?|=6TSÙOC6oô¬³rA¿Î¦ûÝ´ÏÒ¼<ºoaûgÆqRúÇßi…Ëåʆ–šçÁ>eÁúwöÞêþpo/³¡£!×®oim+–3ׄϳÌ+qØlôUBó¦È- µH{Tüé2qX³9ØéZ6´ÝÒ¹Ùqc¸öÿë¤ú£\Ï¿”èÐYÃçAÓz¥uÀÎCv@óŒhž&?¬úÁqú‡%ö¼x™ OΜS:þXQg“|OvÝeœÔÖnZ¶r®†„í¹¼âä†0þ¹”ž#a¿ïIŸäcªº‚R@ÏCØîJqö½ãen][y¶]žÖãc}!„”/Ì}¨!¯&øDÇžãŸçÙº+ǽg3IØçàR@sælïGŽãmƼ ºLª'u”û7ÎY5: Þ]ñ‰õsŸÀ?g³ç¾Âù÷¬N|®!û~¥$ÿþ—>ÿYõƒã|Ù™µ§Àé2 ½![Ø'>Ž%÷sÃ5 6ñÈêžî!UŒ¡jHËI¢Êuá„öv[…ß'¾´¡²Ó©_$z·0ß‘9ÿx¾Çé:›ysi s¼¯OeÉO‚Q[ÆTÍ‚Â组¾ö;§­9VOÃß}¯L×íìý¼âÏa±ó±”Ó1û\§Çq˜4©»{ ĺ<æÂ»kÛëÏzž /Ε»±aÌòàØžû±|]GØãáíÕœ¯7z4ßs,³š‹ÄÝ/°úÁqfOs|4Ó@μtløit.¸=ÚXñDJ&HÃû¬ª3ȾÞì4¹n 83¼]]ç02¶ÂQãõÉ-østþ¥y°ooŒSq08>[<}¹sWV?a8¯maÈ DÕ¶×ýš\8»§vã<]&ÜØ$Z²Æ—ïí¬A¡$5½™ò÷ºÍùýºN¡ï§èzØv]$Äq²FÊ>ô;žBJÙ÷Ûêq3zì«í\in&Tô—økŠ/ôeƒ¯HxVÜಆñï h¾0—iÞ [g\þ/^ÿ~Íhíú~)ÄΗëuîBȬ›á—Ü3A—úvZÅB_^Ÿ›Ëd5VQEØýöïû!ì~øU>_˜®‡Ð Ž3kÄŒí&“à¡oô)ò»0³Å€a’V™`7ÐÔt®/Xœ®th¿DC"óSÔg6ªH‰è3ójÖiÀÏ47‘ý¾¿ñ¹v¶ë#Žs¥ûÕckOïõPUÀ×µíóªÇy{º·üÀÌ$ò$øÝ†²ïA ˲ƒŸš˜ 5vi¯Ï åà2yxÐêiZræÙÍ‹nü¾}/AÏåÒýWÛõ ¯Ú:&‘ѧIU2CÅ ý.*Ïav¿•$É¡l+%>²n%ÖmËç*>ßšý¼Zsù†IzLÚt?›î[±ó™¯¿b’Ñ.’í"cË¿š™¡ìªš}’îÞÐÅ-@}Å\¬7¸•DG -qNÅŸ¥û¯ìùë¯üþ´í:]ןսü‰K›ì%ÝÌp.8iAúé;0Ô÷Qµ£¥|¡ÛÂW·:m%›¥—y&©øœgú}蚇Í_Yý-ÿùÐ~m{žÇŒã¼|Tãþ¤¦ £Â„fH<ýôáõwàbÆ´Í¿ùÂÎåƒÚ,’j‰£÷±” Uüs=Õ»?‘Áí³¼–Ð÷ܶûºv*‹Ôåöº}ï%’þÌ‹Ýif8ôü˜©wàÜ—S ƒÒ6–ŽÛë?UCBËÕ¬?Þ!‚ßo§}™žÏ¢ïè~Ÿí{!ŽÓ~Tû»Iîóy“—©Ì<{·ßúžw`¬k­ÕWøA\=Qá`‰†Ü˜\K"‹àßÑ}pº/Õ!ºÊ³¢öâöó¥xý“' ÏŽ˜–H*Ÿ»tqþ*3 ?Îßka“¬…† `R¡»oðëëð•Z’_¦êÞɵ£øst¿ÅãÁŠÙóDŸ$o§d/¿øÆ"©Ê·pì:½'«‡íÈâ‚ãNšaÆÇ£ZmÏ€åYÛg já W\Ë8w]®%êÜuK‰"4ç—>oÐçZú~ÊRëtµˆ'C€Ý—ëÌêÇY˼Æ|œÆªZÅ “˜ƒv 2ÀþƒK¤eç8yû@‡óíµäÐoÝÚ׬E¬Ûr«[ñ¹˜Åë™Ýèt¿Ìªçu}愼žT,µqÜ¥|3X·£=3àözf£u´|¼ÖÄm¸>z½×wÃ÷£}Ÿ¾¯¦û¶ùœvié}ìO-דð!†?ød†VA—Ýú8g;øÂi縎nUñ¹Óº/Iè¹2v¿°¿Î uGçÿÞ»à8½Ó{m]í¬'n¦-Žk«çAjÓf!¡2ÀW]ýØå<_ Æݰ)–äˆÞlyAœæggÕuâÎg5å÷÷ùó9\Þ´m¿‘â8• æßÑ’¬ï²ªQë<ØæÖñURþm6¯Ùà†k¥$K˜ßP߉à÷Cé:“æ³Ò}]zÞ’æ_[õƒã´[ñf{09OÖ•¬ ¿"̓˜1³[œžöé- öž½Üù‡õŒÇñYž+ÙŸz–lûëðƒ SóÀºoN}¿ÛÐì18\Zêu?­]Yvl,ñ¸}õ¥Ëßû»¯‘-’ï8ÿÝ ýÞ~xï‚×v»ôÐ÷%βç"ò``sè6„Ô6ì¹/lZÕù† ¿ÿw­Rüf}‹ ì¹÷ï9íôœ}OÁî´ùñ}%޵hKøÀ3äœÝ'Kú†<8ÕhÀ¼Àê·aÒç#ß ÷N€ìÜRøèKríßËZIhÿ¥Ós1ôÍѶŸí"-Ò?õöמ&­­‚ò ©{×E% ÓaR@ÿöÂ)>°­ß„~§>Å’ËŸ~Øü ’¤¿'Åžó.âæÿwÚ'~ØOÃqö?uÔ/ãQt«š47!<¶÷}4êz:¤{VTtPËaËÊ êIÍ5äA…12½%’ßG¡û]ìùŽÜyfîÙnKœ}Ë >ÑU&mŽÿâKæœt\ß0ŠÐy˜žß¤ùÒtß´Ô†W/;´kËç[õÁŒsîâscü1b=îS3ºg­«§«’ÖåW¡’Ö˶oK‚ëȺNQü÷A÷ÿèybú>ÿ@·ˆøáº=ÏlÕG”Eºë]“¬U{’³76Èo”§vV)¹éÙ-®˜¹¬ïkÈ®m¬Ò.–ô¨ºÜ1Ø‘ÎÇuùyƒîcÒßS€í/ùœÙôé¾HêÂêÇ©³QuõŸd^K³´É‡q7¦öMM¹­'O ÛölÜíò-8óðâߺ+ŸGúž–ê™æ¾7ª4°ú؇$C¬«;,‰KÜšwÍÿ–â8ZæµÑ¼#dsýc¯nù°§µ¡µÝ¶[PéÛcûòg}a¯ÀnU÷-lŸÎˆ$týH÷æ.\úös£wüþ=‡GŸÛ­zÁqtqÓ<‡Ž'Áù³Ï–šO¿ôé:Cy ë ¾†áú²éû9Aø MÎWÍ=zèf$¿C÷èsÛ·>pïÁ~D4ý¶ì›ï—·FìÛºvð-°hÎÈíæ1gE¿Œ%=Âúè#ùuÝ'¡ï9¦±K!¡ë>Ú·­úÁq~xE—e>@:·î4t@`>8v]<íRÓ[ÒsÄâZaþÐÌ»ÜΤC2òÓœnu‘„êî›Ðóöô9iæÙà3\ø="=Ž3zØ IJßý¤ì–.«—…çC£ÉcÇ¥A^¿‰î†Vþp„ym8ZKzŸ+*2’Ðs•t˜¾×gõy_ÂΣþyʪçÈ{æ€è^r1òS‰*+ò¡úÈrò×Ó€9M:e±ÜÔê𣆺Œ¹½vU$¡¿ÏIß+±9à¯$ô=5=×aûûjv -ÒÓ÷ž´`ùØÃk§ûÆ|ø7®æî4ø­êøY-S|áÕoê½\K¬³Š1’Ðçmú>ˆ¾7¤û v¸7p`_`ŸC9ý,¤u½‹(+}LËÞš+ýGÞžÙž÷/7õæpß“˜-$z¢vÜpa™«¼ñ@Ð èûpvã­„í ï¹s„½=GÖ‘ÕŽu .>óßÁõá|™–þ¹çè4èÞxÑçP_8wO‘R‰l!Ž!QŠƒŠ(B÷ÝèúŒžO£óÍ„¥WÏ|Ùè{*«~pœ¡‹÷©ûÃ6R'EöNp<ʤ?¸[©Cô[ë=z®/˜N§ ¾ Ôè¶iI]õQü¾)}_ÂÎË&n]VÄ=?µãïÆª§î”kSŸhÈoã<|:èóa[m¼*¤Áú)[W¥Tôƒ)º8ƒmåÎ;/ä‰~O{G{¸—Uäñýžƒ ï¬úÁqz^‹i½lÔäø—6m]ɇãâóoBŒÿÄ _ûÁÑ›áÒ,‰8º¾mËm ݇_u¤úøÈáíþž=çKÏ}Ðó&Výà8˺޽Ò²ž,ßçHNZ>ôœØ Wã„›Àþ>±?,¬ÔÁE«#ìsÞBž{iÉãm ìsóGþyƒö «nðú»ßó(×3†¬ ÏËUË·ZùÏœž-¿ =§®}í<¬öuÄ…l.U:p!¡ó]/Ñωê†îÚ®—ìY¤‡ü˜7BKÈfIWóéü|¨wñýh½÷MHŽ­Ø!ó¾?´ھȥåVòV••Eèþ1ÝÇ¢¿§@÷Ëè{`ºoºÝŽýóËËè——Ñÿi/#;î^v¬oÈßù°Ùæ 9#ÝO²l3W…\nŒóf³ÍOPs’2.[°ˆÉ`å|E¹<-çgD}Ä¥\.u!çÓf›¥`úI–‚ôo|ÚdœO›ƒOÛ¿ã1bëÓF}uŸ6ê«ËäR3ù1L.µ™óm+î="mö=—Zù“ü˜â>’ÿªx4—/(ãüŒl³µd6nªbÙ‚BÎˈÉÕb¼u¯!—+Xh“Gíð7¾%(vå?äQÿ,WæQÿꉿz¢Êî¿£' ¸{11ßÙßx)¹ËÒb(D⇿ñ;(–µåÀeK›’1è†ÅªCŒÇ9—ß (Ïú'p93LΠ Wd½‡™¼-7,hWÔž\Ö “1-·ñVq¹[bÎ÷ü”7HófŠûžG#–Úß=ÏUˆ…y&F8¢@TˆqG¡$ BKt1¿s&g†úËðÔ Ù¼U&ÓÉ[¢˜”ˆ‘¡¨âG–ª›»êþ“L="BÁÅ Eˆüßð&6p¹[ÑŲ¥…\Æ ãuî‰BÕËÞ’s¹ÒbÎ缑sÙ[b›ìUws<∂V!Ä…ðÙ«?ËÞ¢Ù«¿Öƒ¿Öƒ*»ÿŽ>(â~ ó™cAÆ!X”JČȊenY¸¼é„2ï¿n›;èÆe?˜7΃½ˆó`g²Ì{.wË‹[‰˜1çÅnær§c".‚É^`áË‹ù±þ{!≢Ð#"F R„ÈQ zD„"‰F O&{¡`¢‘BÄ…£GD(ž¤‘£ˆôˆ…ƒ"ž((="â=ÙìU&‚É^-BÜQd ˆ…"ž6Ù«1?É€`¾bDÜPŒjN Ĉ¸¡0uˆÅ€˜)—aÇe@0™Ôbλɽ‘£p ˆÅ«.–»åÆeR P̈‘ñwç2·˜ìš¹*BG#…ˆ' ]ˆPì1ÿ¹ú³Ì-š¹ÊÔ#ó‡é‰¿úá¿×õÂÿL/d¾Ãxæ~°Mˆû_
´ÉðŠg²'ʰ¹Ó–ŸäJ±`ã,Z—Cá€Å«à²E\ö qÃbŽá²½¤XÔqˆ 2›ƒ“ÀåO+="â2)˜|/7,ø˜!ƒfá"ž(="BQÄ …ˆ;Š#¢@¢‘BÄ…’€Q,ÑH!≢Ñ#"N Rˆxr98ÑH!â.ÄŸ_Èf±*¹¬/ŠJ…XwW"´ÉcõüI^…£ðÔœøˆ£ÕœˆqCAêŠ2€Ë¬£8cŠeO‹¸ œ"DŽ‚5Ød1ÂUp¹ÓLþš±‚Ës³ÉdõDQ' Bv4Rˆx¢ÀõÿÉú³\0šÉúk]øk]¨²ûïè…nÜX…ÌgŠ8bQª˜üĽX&Xai6Z±X£‘BÄ‹V_,ãPЬF,ˆ YͳœË:tÀ¢Vp¹`ŽXÜ*ÄŒ¸a‘Çp=L6µš+x9—Éꀅ¯@ôˆƒ!ž(="B1Ä EˆEa@Ä( 5'b@Ä(’¤‘£X ˆƒ1YÕ("Fñ¨9)"F!©‘"DŽ‚2 "!ÞƒÍduoÌf²2óDôˆ…ƒ!r›LV5'<bDÜP€:D€" @LˆŨC(ÈÄ„HQ˜qˆŠS‰˜Š4ŽËSp9ÕL¦Ž­12Ù>(^]±\0)—Sí€bV"&DÊe‚9Ød±ŠQà1H"Ç2 b»ú²X– F³Xé¹®ÿ•=ñW?üÕÿGýùG,D%bFdXñˆ#¥ªÔ÷\²DÈåRþ$Q†E8bá ŽX¼\>¢˜ËG´ R,f5—U&âŽG°°\F"“M€1z —W&Å‚Wÿ 9‰"B R„ÈQDŒ¢P#Eˆ'ö@="B‘Ä Eˆ'ŠEˆP01H"Gá1ŠG!r‘¡b"ÄSˆ÷€8¢¨T\–™Å2=E¦GD6Y°Lž™£èÔœðˆqCêŠ01"n(F"@A &DŠÂŒCPœJÄÄdš¡HÕŲ©™ŒD5'Zbtúžw&@p¹ÔR² ˜"µÉ‹•#zD„AŠ9 Ýð“ÌØÊC£h¿Ö‡¿ú¡Êî¿£ʸk1Ÿd"Ä¢ŒF O,N="ÂAŠ9ªa±Æ Eˆ‹ÖP,·Q†¬C ²`1+¸ìFG,êÄ€±¸£ "Å"W#…\¶`Á+¸ìXG,üÄ€ˆQjNrÄ€ˆQ jN Ĉ¸¡0tˆÅ€7‰šŠ1"n(5'bDÜP<:D€ @Œˆ IljI±ïAÈfÇz6f³c(09b@Ä(45'6…Mv¬ ð"EÆ!(B%bFd(Æ8Ä©D̈ …8¢8UˆqG‘Æ#(ÔÄ„HQ°qˆE€˜)Š7q@+3"C!Ç#Ž(fbFd(êxÄÑ&3Ö ®æD®@ŒˆŠ]÷™±JČȰ Ä#ŽØT’ï™±Lí3þS=‘öCÚûhŸ£=í¿­‡Ñþõ¿³gý'ûÔºGéìØþÄ|FzDˆ…¡B,ˆ;H"Ä"‰F O,="*ÃæY!žX8zD„Ѹc%0gž±€Ü˜3ÍX< ÌÙ,7æ¼2ó>‹Ç€ˆ°Ä EL_Áb2 b,(57É*#â†}DÇM¸rÄ€ˆ±¨¹ÉWŽ1¢š+FbDܰè¸IY1ö57A' Bæ|2oÓG°€ ˆØ&#V7ì:D€}#1!Rìqˆ½1!R,þ8Äû†1#2ìñˆ#óΡXÖ´ŠD‡P(ˆ ‘¢`â’Ë™–¡xâ1»~Ï“U DŒ‚Rs¢R ÆŸdÊJQhqˆŠM‰˜™„Í“UþZ?ýZ?Ùý÷¬Ÿ<¹Ë£Ñ#",ʤ‘cq1¨š+RbDÄX¬j®`ˆaáÆ Eˆ;pR„¸c!Ç!ẌbQ+#"ÂâŽA ¹)â2°ã,øÄ€±ð•ˆqCèŠ@7ƒ  "EaÄ!(%bB¤("@¡ &DŠ‚Ñ!MbB¤(ž8ĤDLˆ…‡PLˆ qâ= EˆÅ•€8 ÀˆqC¡éŠ-1!R]â€ÂS"fD†ŒGQ„*Ä‚¸£ãG¤ ± î(ÌDˆâŒF OiâˆBU"fD†‚GP´JÄŒÈP¼ñˆ# X…Xwr"D1G#ÄE€QØJÄŒHQà:D€"@LˆÅ‡8 à•ˆ‘¡ðãG¿ ± îØ!6‚hÄ‚¸« ® ÿížøOk'¦¿1}ì¶gýÏö«¿ëUÿÉ5ÒÿÉžÄô£ÿtbîÛ€ˆ°¢‘BÄ Aˆ°b"DŽEa@ÄXjDŽÅa@ÄX jÄ‹DÏœfÎw0ç8ª°E¡@Œˆûƒš›0ˆqÃbÑ!ìˆ ‘b`oP FÄ {ƒ`oP FÄ L‡°7 &DнA€}!1"nØt̹ !Þs6—›pˆqàCØ"Ň8`P"fD†= qÄb6#2Ô<âˆúW!ÄõŸ€Qÿ*ÄŒHQÿqˆ @‰˜) !q@1(3"CQÄ#Ž( bAÜQ ñˆ#ŠD…X˜½vfÅâ†bÑ!LbB¤(œ8ÄÅ£D̈ E8¢æUˆqGQÅ#Œk“ þõ3vÌ»T7î ó»et?ŒùZæ(]¯ÿ=Zú3þÝZíŸö»˜õšØw£tïËÄ:Xÿäpÿ-\d‘V\·7rh¿P>Ÿ:eàöñ…óyŸ¬6‡üþ»ëMÞ·~Cô€Çojû󹆙G9»–Óš?Èú´G[)Žã’]ó£¼ÎT’3±uRÓWùp÷òž”oBù í3Ž<ó…}_kÈxÝN}Iu$¡>‰Ô_ŽúÏQ?Pêbë×*ÇqUouíá,7Èèlïwø}> =òÀqáßbÝfN€ÂußÍŸ !ù+‰äsÆ©_.õ‡¦þ Ô·™úÕ0ã¨pœãÂo¯i:Ø<†|xUiÑŒ Fø8léç^‡åàÖ¶Òç¸ÅÒ®DÑžK#ùœéòg× Y=½ŸëIý'öŠÍyéÞ‹ó÷aýTt8NZ"c`»9Æœ»û%FìÚ’¤3Âh¯î¹íGzÃU²qÔ¡“.''’Ï÷¤ŸË­‚a…3Û€úÐ ¬?Wø§l8T}÷[J¹ßY¿gSÇúíÃs·ÕJ@ÖJÉÃR‘Fï\¹v ŸñðiMçù.?ø»ïõZ/ЮèúÒŽ÷ tnFUǧm~øÜÌ8Ψý^Öó^¬Ÿql9ÝíQ‚ŸNWªbÿ{â8h5@gïg¸C7ÜÉç€SBúyѼXš×cëb·Ø"­6¹^øö?×Àttˆ®Ro ó.’á`؉F]…¶ëMÇÏiˆáDE·—©“ÃÿÏÇ‹ú^þ]®¹Ç)çúû>Ñø ¢`Œ~  $Ù6`ÝoFp¨8ßuK“±`ý¿ý5dÅÖäs*EñuM}©®¨?0õq¢¾ÈVýà8ó/ìéõ·cýâìùºbõøš»öç·[b‘.ܲ¢õÅ„ý ¿>hˬºp©Ó©…–ã×ùüÙÕqî¹ã>kSDºO‘„æ°ßsi>Wžæ ÙæÖ —Pÿ¹ƒPÒ7´Ôºzp¡]úþjÁ×á•„1¶ö×U?´Û¥%¥®Ã;Œ$Twl=•âý¬i¿§9-?ä¢á8ûí}f¼Ÿ}:^ßÝT…ãÜ´‹ê·Ñí:4j$ùå¦Í¬wkzŸ0¦©Ð(äfs}«ƒÀ•&þ.Õ±qßá8­×—9º­g,l½¼wÓ¼ü|X³ùXÈle&˜eOÿ*n‡~÷±Kö&Œ±h®·~(pn¢:¸ÙìÄçæ üî¿ï ”Kgð Ž3ôíG«¦íU`Í\ž˜z+ö«k&¯ž˜ ÃÏ ¨6.Æ÷k‘u°p,䯸«T{ÓÜ]ÓTµ±8”æGäSÊQäæÖ%ž_:çëûºÝÕÏAÝÑÌ"òáÓÍõe4Ï€†á1W­qÞòATöÁàh6*»ñN¿ !wœxñ”×jjõv*1/2Y¥—ŒT÷Î ëš77ÔÚ8~k>´êSÎâU:H}>vèãå“ º `NÎcg‚Åópú'õeÎ_„ÏQ‚óŒãL­ûÇça Üm´\î¿ ëùˆÿ¶Ç‰épêçæ£@ã^×¹þnSùð°KÒÛÂ÷BÜ ®›q¦KpmðúÍ}rûOn¯†f6…·çCTÓÙ³gnO‡$ëwoö5 ávk÷ŽQ0S·O¿ òM)Žæ]\ÜW!gΘ»'Ãq޼.›4ì˜FoÞhßô|˜'/wõàÌtX[4,sQg(=9aY˜­‚9µÏ/­U †x\®é1—7þUL¹ì%ž+xýÓrí¢¢Ž‰µoìNcóá@Ãjie¦Ãs–èìK/]׬‚ VL{;M$ä§Ò¼ŽrZÉ'”Ci¼þRà8Ú‹‡ù\Ä|€n'íoœÖfãšf8A¥mOK›¬`,›*’ªG?ɗĤú5æn«ñúu½·mzØ, –žï¿¬V¯|xoå¾!éÝé>¹Éér#aõ¡.[n®Q0iËÚ-ü|(HÈã£õåçrüœ¶%¸”:¼~¾¦ÌÆ;“àsùÝV·Í‡ÎEŸSf\¸ù¦‡óõ+FA“/k6+˜:K½Å ¯·(ïrE{ø=Oš¶ò“øˆCló¹‰Ýùõ%Ç'5Y­—¸¤½ê§¬~ØXáÆùð[÷°­Õv_€Ò˾›6êá,e¢Õ\Ó{@ÕêÝöûœ8?7_‚~Ãw>ç¹;ŸkÉåËŠðúº)î¶æ›ÎCYÈÛ©µóAkWwN)¯ pW?A,ÒºÇEQ03ŽÝš²uGÀ—*É?|'¦¼G.gÒ–ÏÇæslqé¾ìž•“A^±ãEÅ|Ø$o&ét¾<:·x£|j1tY„Ÿ‚ù„O“uÅyÌT¿”¿Myùw7ܲ;dÄõ6øÇyq…~L}“rŒ©Î >Áëê³/ ì-¬±û“$šmZw(Ü/ fÍKèÔeâhhãåæ9©s£ŒlÙB-gh=E> ùPÍܽnnæräx]ÝYÛWW×k`&»f•öthy~HŒlÛtçÃÑ.Ð÷Òçï¶ã#ôš¼J°œ¿ÿ-~Ø'̸NàuÞlç¹—îÖÅ[‘G§$t¯Ñ( \¯_ú´ª lÜÃÉ#þù‰øßij5Ô;^¯Í{ÓM×_7£øU¼hA*ô1\`Ô¯v:÷ʼnž×UÌí¢ç2·îx/ðèyjÍ.ÂÖ¸ç×Á—#b[?Höó½‡, "’Éz·ÄÖ>8ˆ!öÓ>}Z¹Åµ8#”NœSÊW7øc^sL¾&\„黇ڙtÓÁ`ÕÀø‘©)ÐÏnüÅØ·Ã Æåü€‚'Œ—°$ô Úo ^9ñ‰s@üƒ_pœÇÝ›ÜS^¹1¢‹.êਥmûûûR ]Õ¤nÑ.R¨²o|Qph³ÙQû¢ùº@†rÞi^È­—²Äò•_?V¯÷\8¿0žßJpœd³!ûÔ¿a¥e½–•×Aу³n¿§@ù™Õ™4N× yêfÖšŽõ'n dhÝEû(ÄÏ£} ®Þ¬Kîóâ8>mÏßæz p2ãZ;ïXØz¤œg D%åö Yæ[¦v޳ˆd=Œù¼>/!ÎíÐ÷Có7îùÒˆÛeðŽÓj{ªõÙM—`ãâøø ÇîwõKcKõLÂYê¾}–;Aü™K¿éÉÜ'ëÞ·|Cû‰O<6¥×šgn´oz°L¬?vPi¿gâ«O,}ʖσoÁùÇY·}VeêKòÛ”Êó‚îÁ§\•k) K{ýí3ž&‘Œw3­Ìéq C9ã\þxg˜´ñuAÓ×<‡ö£˜x'Æý@ãLlôqYàãKÀí3ß6½>z KëÝ æÚì29Á¼=oš—ȇú;qÅéû¢uµÁ7xýL«mcܪdÝa›%G-ïA÷X³ég“a_‘“Ø£ê(\^ÞzÖíFqÞ߯Ôû@;*pzx^9ñrh½kðËz½¤ÓªE‰û»gñó»»P»þ›ù!ÉЧ\vÔ¸R£€ã«F2!5ΜŽÄPý’_¨ŽiþD}Áx~#Âq†(Ãð‰›\¿¹ U^¾=)úm°è« _§U5ÏÌŽd¢5–ùœƒ>­?hHýŒî£ñç‘à8š ½w Ë‚¨Ù9­vÞÇ¡Qœ{$CnT×Ì6GÂm\Ýä|ŠdÔÏ:MY’^<ï¤ûNóuêgô÷àÎOªr~Áq†ž = <¯­ ßíuL!3n¶y2ø>ÉðÙêæ õÔcD}^E2ò¶òs Î?iÞS’§ò^Lû\tî`ð ŽSGÕëÚÚÊZÈ­rë·I]ï™ï7’'ça|ÕÌúsj8Õ'å=‹du=(=&PàšÒ>Rå«ËlSv<,ÁçQàu1Ï÷öj«…iSBo:V¸ ƒ=¿·ÉcÎC‡ç6-¾žs†1-οò/Å8|˜Wñõ‰@†r귳ǛûÛq…ˆ—@¼¤œf§ûâ)'G:háñ†©3ëݺ½WÄzÞÄuè¬÷‡Ì?ÿ1 z~«T¶TÇ(¦~Š÷ïÐ~ í#´ëùò3vub®/ÓsÁx?C‡ã÷>=I Ò:­¶¬8zfþxôœì<¼¨·°J˜+蕞ÆÙE1E_lúúž dˆ‡Fç=´¤uÁô’‹lUȵpÖf1þwàñ ÝØËVç!¶Ï¦oeF¸Ã¢Q9«g[E1á6V—½æß'â˜Ñ¾Õ“ÁxÝcJ;§Þ;´à¥Óy¸ë¤M|;$AòÛySÆŒ…žõ_gUÎd>N½½ëžM C¼ê»Ü¼V/~3ÕcõÙ÷Ü'eßH¹òvånºHžÛÀטê¢â• Ýv•âRÒù©¡îñú¡‘i/g~ÒÂú˜èàË·a×z“Y¿Y'ÁØf‘qe·9B×Meó*â÷‡O“ßìå ñáé¾gûž‹ù jö>•YY?ÌŸY›—½ /o¹yK+'ÁæÕ©7ïÝÑ_¯ Û²<Ši<ÑÕæ•] Ãù½=Ð9çÂzVd©³å:–àÝêpï–A¾}³A½õy®iò-8ºñ°‰PÚuGuACŸAž}bdóªî5$|ÏÄ åúö=¡ŸrÏ [ Žœ¡þCô’ŽðL68ØGÍü²êh§ut5öÇîÔL³ƒž«ç]Éž£dšÀA ù6¯ÊTgÝÄ6@ûvtnÇ}Î~0½õš¤ö«ÛsÏGR$h¾ï›_ÆÕ CoAó•;ÒžK„9Ü|Bwõ‡–çfû¿Ø¡8dä:‡§}HÃv\´pbð^B•:qûfù™ûúöªq º¾ïj7å@"¨­vÛÖÚÑ–{²+T%ã6ÅßÃý@ñ¼>ñ©®>ÉêÖ¿a]ò}Ç߿ﻩÙpóˆ¶BFîM˜þdá®Û[aßë…Ë/ÙÁá¨Ú÷¤EQŒï.-æEœ8á´oHwãï_Žã4L—Övz’ †S}ÅMHîgë'Âv9î}ä•öŒŽb2Þ[ÎŽûÈÐóšÖ#´.¦>Bÿ¹Á'x}¿¦o4+çÀÛU½â'ßÙ‚öSd‰°ãyÿ‚õ†À æ½Â¯‡E1,=ðãªãFÂ9 çëïÒ˜Û¤ÆëW5@<ìûÐ#£ÍMèê¿Ë½/~‹#ú ‡Ä9u»uÑF1»W³ ÆâûDón?å¦ÐOhÌàöúXõ5ç€'øê$öa7RaÛ,wó[-G@Ð…uM¦WQ2ƒ›N~ï·–ÎM›Sîóˆ‰ mðÃF½„}ÚN›®5+ùˆ½~ךµ?ý] ~5;¼éóämpï·¶JfÙ§mÝÇþ¶Œ¡ýDzÓ>íˆû[fÏhû{àu]å5ù0?º·aïÜ hyñêõu5Žg;Aý·ÊX¨£d,ú³;öË„y$í#о Í'ˆse|n-Áqî­ÝytÓ†`'MÚþ7àÖ¹ÀYWŽª!uyïMÝ&9A˰F·:ê¢^*ÕÍÃÈÏ´ŸIëAZO|€ãT_W Z¸/,6W±Ú ˆÞºç†Ùj5îçŸô«s%¿_i°¼_tCûTGt®Dï]ê¯ûæñ­ e|p|²\¸;lÆð›“ÔpÄ4ò[ãLG¸>$áÆ£Ô(¦|øÙ ‘Ý‹÷ãé>p×;/ô=C½ãuÙjó¿”; 8zlË…:|‡[÷UC£Õ¥—4î3ÇÖTP2K»T0}¤ÞC¢÷ÄhO}ÛPçxÝôYì†jpœõ\óòÚ»n+Þé9«|î}l˜’ÙªZ?'%%PØ£u qœîw¬y¬]ô[n¿ ¯;=~¯>¾0ªæ^}Ð*ÆÌ~صÍsn÷Ù¸¹}„#ØX«Å=(…ýbâXÒ>P‡_ÎÛðZXÒ¾Ý/C½oÒK¾7û}Í*÷’â_\‡îÏÏE0püJãò½ Âñ½nKpœ%[ýŸ·Ç~Fëâ?qóÏwÂû?Äw6Þ‡á8Üsî2¬ô©yþáë𶶃Ìb$º òòV:óïé)™õ0âF̾ wEïpý3§Äý—àu‡gTshv4Ù \?NºR|Îmø–V½Ý8hÊ(hl®rÓY)œGй(í×pûÝYÂy=½ÿ@竆zÇqFoÓ·=azÚÏy¨irÌÊ´öù´;”3Zãî ª&ùGÆ)™»Ö0/ÌŠ9št¿hŸ‹æëÄ¡6æÂÊqœwmöT˪qæFß[qøÆ5X™¸¤}LÛp±Z²;|º$ícÑ9NŨÆ^2x)ì¿¿#Áqj-úêà_i…¹Yž­®ÂçÉ󯤙Sþ(ðó°¾÷)æn´’©cxADÎP?¤õ4÷ý'ˆéý0z.•Ø¿Åq²–·XÕüaÛ,­÷áш×M‰}t¬ú}qýR¨3pш3JƯL¦Îqé='Ú7·]ÞºËbEóÏ=9^?ã®àÐXÒ4~ÊØÈ+°s‰ŸûÑþgàÈÉõÍp^>À:ÒeÖT¥°/Àù¿:Þ/¦ïÇø{Pàu¯iŽœ\{%|7•®$»ûs¼jWá ˜y>-òÛ3 î§$™w_¢dv^0ªÒó@á|ú÷}Ç ûÛôþ*íã|‚ãì¼4¬¼Ý…ØSÃ|³·åð.;lŒFkZf¦´Z>¾v8Æ…·ê©df·9×µ~›âú¥:£}ZÚ' }Zãy­ Çi~¼Úñ¶¡9`=ãâ¨q¯s Õ¾w-â nÅNUowØZpä„%®ûÆ,;зLñù=Gè=Pò#×·­KÔµÇáÞÃÌn±}^Äæ@›·EQ*–ÿÚsãocaYb\!E1Ù½Øwå ÕõÂ)SVùôZxžqûß6%8± ÇIró»Éôx0¨w¥¾~9лO…ÒfÖ*œvQñ¬òXh6nï¥ÚÊ(¦edºMAùùãÅœSâï ©ñú~nKu­‡ç€ô»ë›ûÖ9 1Ùé­Š׊›‚{œs‡½¦ù½v«£˜j/&”cª2Ql^oÑB8ï¤ý@Úo¦:,á‡ñ?î©ê󤻣qI“ eÂN< VxOZd>çm«å8=0§þb)ì'SŸÜRØJY9ñ½˜ö§Œß?2Ùª—T2¼ð“g²Óü%ŠlPe>9š{Žô»9ýQ—1p§Åí6oF)™äqì‹¡Â<ƒÎ—è=:¦u'½7jð Ž3l]Ê÷†9pÇŸm˜Ù oÎû4ôܳàzÓFnÀ¾Ý8b¢’éé¤?›Ú¾Ø7´OJýæê—¾7”£‹ÏÓŒç3‡±÷ʈ«•ëg÷~V/Ú­öZÑÉì4œ `ˆ¬m»c‚ßèâù í?sçUxÙxÿ\†×o²~ÁœúUr`qTâÌ™yZP¨ø>^q ,Ì̓ú_pƒwÕšÔ;¯?;°lʆ@á\öaéw´Þ¢çÁ'xýW´åeep=e^V£=¦…ê¯×¸v<å>wy²;„ÔþÒ5ÙWÉèWôÄGXq¿¤¿'ýîƒÖµÆûÌ ¼>K¯¿ü*RÝL«¸T ƒwîô°:sRø½‡IHÍk'+Ã1Æê+Í>;½ßD~§}7ãó5Žs¢òœCkna=U°yôÕÂÒ8Y¼›íIøêš{Ýv<œ¸T” Âyr©Ûmǵ­LϱfÂû4´^£u!×;ýîÀàÇlvµ¾’„lh]¦ÂÉùå´ðûeÛÕžÑ'`ðŒàžVs=a‰DÑiª“’;Ì,ØA%gè=uÎï–Â{ÓôÞùµÄ¹Æ6½dËòþ7SòabÈS§-Yàÿüò¶{UO@¥6ÞUÒ†M€×-˜-+ûé•{XΗ34o¤ù0'œÖñÆç´"Çpl¶<î¦×Ø3é·,Øtøò‹sÃe©¥k÷Ÿmû«ÝYÉ ‰ê¼+m¼\à(Óû…Ôéý*ZOÓsÁà箋ÔbËÔl¨TxçѺ͗àËÇ1û»Þ9VoGÞÞ z»Ï½ÝJÉXdzXΛ$ÞÓ¥œëŸÅÔ¸y†)ç—mÔ/³a%;½­w ~ìÙ¼ƒã1Hßà”uÇl<<¨¼w‹ƒ’±þxµc_¹°?@};xVâ} 9^÷Ûß'-:dÃ.®Íþ¸’)>/|.Æ@\ãÁ‹æÕ”Kïn³\Épý3€¡çŽ ù ÌwW2ž+ƈ7mZ*œË‘ïiÞGu:‡0îÃ:Ç5é¦EOì[Ë—[yhûf‚úÔ¬»›óÀ°àÙu¤ãÜ`*Äλ©’9Û¤CŸ*Â9}ú½õ/š×|²]/Y:jG«ÃŒÆì(üõl䆿ܸöøØÕeséÉ®ð ã¬iŠQÌo†¶'Þ› çõ_î½É—bâK—Xßã8Q)uÏïVhÁw鈠o}3 KsíÉ{ûðŠ=†½ï a=¿=»ż&Ï‘FÊ…y ù-mÛêŽê/Åôý>Äà¼þ*­i×#Z¨zçyµ-·Ò!ýʬrã|ÁÎØoS£Ü RóÍN=ÏG1²Çû.ÚâsžÞÿ¢÷µ¸ß½¸Òô{,ƒ/ðúâÍ_ßt–i!¹ïá©wç¤Ã&¿ÕÊï=qÃ4óšLŠ5¿¯µû#ŠiÇÚ½] Cç)ÔO¨oÑù ­+ùïr§›áàP ¥+g„Í®”=‚Ÿ^æäUX»Óù†;L±;ðòKDó&ýÝéižß\Ø·§ßÁ}Zn·Ð¤qàÖË]KüîNÁÞ¯m½p%†ý7ýÁàØàÐÚ)óXs >âå…q°/g¸í™SQLçT&KEç9 „÷­¹óØkâ‚áoÂ2M…󺟟à8kêTê÷àmt>ëºìÃÈ péIÇÁ—¢¡Ã·4'…LÜïh¢˜´½½·ÿáP¼¦} z®P=Ð:¢Äü ÇÑŒ]Þ*]™°o^h@ï¼..5i?8,ò|ûQVK,D1Ó¤«É6ðû‚-„ßÕqó¯»â·mpxV‹îwÎñ ~ ÕKé&Ää;gA™Ey—VlÔÀÒïRVÝI ËTÙØ~¨œ²„¡õ)ͳèù¸«éï¾uJ û%Ôï ¾ÁqjÏ_ì0áä%ˆîr±ç°ì4ð´®ÓpmŒ|VuÞfÏñ°0õCвµ’Q²Ç3–¿ï£ç1·š,æ®ûUÜiZ ñ–%öe8Žèy­ƒåÇ_â÷+Ò`E`ßЮ>‘pßÉdí­ Znl³ª…4¹ÉïKZ÷Ðy­Ãéܙַß°÷«ôµck^‚–·ª’E¹¥w˜¢JÙ–ÞÍdPÏ)&õÆü(Æiqõ™iW–û!ÔÏè½"z?×xÞ­Àë\QÞóFæEn3ååÚÓ©Ð³Í GÇ9áàq}õFy[p-rlÑ%Š©hÙ/ý¿ü–úý™Úì9ùNL¿×1~XãœÈ‰ª5ñE8påyà(Y*t{Õ¸ÁW?T,³ÂffÁ8`äãTóÆãzè–ºa´ÓR†îý®“æÝÜs浘αŒ÷©t8N×ǺÚfá뻊• *¤BVϲý}¾ï…’ÓÙJ¼_­¯9WÇ#ŠiXæøõ3°TøêмÝ3ÆÙ¼ÖAÆë-“zIÓ;Y­4Y™Å³žH¤¶>åžï†jQ}ÔæVãa tïÅ8çeN®²Tøèûäêé$¿òQøýñ{¬"Gï9Ðwq&t6 §@ë»3—œÞ «WÕ©ý6Ÿ ©Z ïSÕ¶vÉÞ%Âïh‹æ´ŸçÒ€Ý9±)¹®Çq| /Þg£§i->VOß·õ~û6%öòÑy³õ)]ÿðŠb†Ü¯3ÎᵿpŽAý‘ÞúòÇ«œ¡ž6¾²ÁxýØeïër2à÷ŒÖ3Ê«’Á÷õ“cíÊléáºó÷„O€‘ÓÞKÚØD1™§/_ –ù ózþÒçàÖÿÑþW¤ ÷çߌ¦3šþ¿Îh¢¼0öójM8~öŸñY~¶eËg¤ü,KNo”''ây‰lÎ¥‹"„gg»ðèÆ—RžÆ2!|«q9+ž“hÎç3ýŠfËçŸë²WŠ~ +üEî¯q&]È/˜Ƽ0–Ëf\²l–•Èf ³l ŸnÎóÂŒ¹±¦|Æ¥†g%ú¢t({>§®ˆgÇç\ç¯rÐsy~v(oÖsÐ)çòg9èZž—hÌÏþ/ÑŠÏA'>Žêoò³U?äµç¡°(ÿöÆ{£ÜäŸÓMùÏ“k±dÿŒƲdsQ,V_°?fHåHYñì06ÃNf”‹ÊµŒÏÿ5ίsáy9l&º¥CÙóÌ0 ž¥ý+VŽ„Ïþe™a4D4oŠy9l&úÏò:³©B‘‰N¼5ÏRdóëX>Ë cóY>„=ŸÿkÁ3sŒYŠæ|~–ç†ÉQz””Ï­b èòC†1"þ*Xdzd<#âÇ `ʰûYp.Ï3fÉþŠfÃg#Bý7Y²j”A0ªÏõ¤,+6Û“­QöÏßíÿ­¾ÈöÄûá§þOé…ÔÙhÎÿkÓRÍ 3Åbô6Êðü³ tžfeÄM”ñù,+ŒÍØS£D<+QÏgžÇð™¾<#Ìþ/¸ÙÞ|F±È(³Óæv6±ÆÄ1Î<×ü„•¨1bg³¬DûÆ\.Ë‚`YalV±Ï ÓY³³ÍÑL¾(JÂgõ±ÆbYa1( 4˜œÏ=gy‰Á?äõ⯲‹…|»ö%3Œóú~–alʳˆ!ûg¬0ŸcL,ˆÂ¿É-4Êíôå3Ï£ÿþ×÷þ§Ï-ø¿¯Î„cÈþ+ŒeÈêPöX¬Ñ(S,X/”e‹…«@¡dXÀ” Ï 3áÙ‰”Á®àù±^|ޱ w(ª¨ ÇMdÙ8",ô`”%åa"ž£ý+.Ž=ŸaÌ2ÂìÑ1|>él“_d”z£rQ>£ôg¹íÄÆÑðìD߯‚儱9Æ,BÊç‹x6Ž1;‘ejûò9Æ,',UÈöF4_ ŸåÎòU(1؈ñWYÆzž!ͳ ~Ì2¶@ãÊ‘e¬ãYaÆ Ù_±Âlù,cbAhþ&CVƒ²ÂF‚*â3MU(öDŠÍ4ek“ýóOè?öÅÿ¤'þÛÞÿ©½P„ŠF™ÿ NX4Ê Ó•‹²ý‹\w­ecÄLd™8Z” Ï +bׯXÌV<+±Ïr·À¢–ól0é_°³}ù|f_,zÏÀ1fg#ñg ã wíO‰Z#v6ËH”¢a¢yÓ°l06¯YijÁô–Åìl 4“¥GÙ£©¢ùìf– ¦B‰Ð`Á|Ž;ËI AéQR4ʈeñWÎæ˜Ú’cj‡üÀLd™Úr”Žçƒ… ŠØ‰æSñ¹ö,7Q²Â/'Ĉq¡B‰Ð”Á¨B” šS²Bƒ†ðŒ {žÆ2.ä(=JÊf£DhÜ`”%E«P"4q0ϸ°ÿ!û+F˸ˆ6b\hÿ&CV‹²ÁFÊ7”Å&SÏZ1ùÏ{ãÏ8ÿÓ{âÿ¤~ø¿Ó ÿndûßßé{?îR¿c¿§ŽûW̱”Ÿ¥cÏD°M±½Q6Xˆ¡|1z¡´([#"ËñÉEÙb*ø"õBiP6<‘eø¸`S¡DX¼Á¨B”Ë_ð±å¨\ž +Géy†1#›ˆ?cøD£ÌѾ?áæñ±Yc¸ bP¦"¬”Fı:‚Q…–Å|lš&Uˆ’¢ybPæ<ÃL²B#… ô<1UÈž… ±Ô<»CŽÒ£¤h2J„F F¢\x†™/Ïî°GãÅ ,Ð|r”%Å/7eF”£ô()ËÀèX̃ý‘cfÿÃöo2`eh^MoŽÓSÈszTÿÎïþß™ü³æw¶üx…&öÏf,¶å‚ŪBY`Áú¢t({,Ü”)¯7ËBIx†™9²/J‡²Ç‚Žáù¯,¿L‡’`qG£L±À½QZ” z(ª%ãÙe6<ûWL"–‡óË\Pj”Å\¢h”9Ä¥CÙ£QbPh9J’¢ibPæh_”eŠ1âåò¼Æ`”®1Ç/S Xc±ü2%ÇÄýßÈ2±ƒQzž_ÊÏ ¥FYð G ÊŠ*D¹´úkÖOÏ€ef"4k0ª傦U£¬Ð¸!¨B” X²B‡  y†™1öW 3{4y ʋǗg˜ýl.ÊeŠÍ@†Ò Ø$›ÿàü÷ÿ­Þøßè‹ÿIOüoõÃòÙï?¡²÷^eÂqfÿŠŸ¦B‰° ƒQz” 3eŽÅé‹ÊEÙb‘*P¦X¨Þ¨\”Ĉáè‹Ò¡$X¼Ñ(S,`o–E‹²åù2,f5Ê :„åÓ¢dÁ½FéxVm0Êþî5q£Qæh_”efˆAYü‚Ù¨3b^³ÌFJ…2a} ´(4MªÈ’c^[¡yBPE(4‘ eÁsÚ4(4U(ªg6*PE왚Lƒ¡Ñ‚Q…(4œe…¦ A¡dh>u[ŽÕ¦GIÑ„*”Œ*D¹Øp¬3š2Uˆrá9mÆìÚ9mÒ®«Í{œ¯Mñƒ_ËšÛ ¥EY¡ÉCPEìšÍ®þwøï<ÐäŸ5´ç¯Wd±l5(,ÌP¾8½PZ”-ϲ-BɰXÕ(¬¥GI±pU(s,^_”eEƒ²ÀB–£ô()´ŠçØÊQz”=w Ê Ü•‹²ÅBWðÅî…Ò¢l±è(S,|oT.Ê  @™Öâ˜Ý*”9šA†Ò Dh _”eæˆAY Aä(=JŠFQ¡Dh–`T!ÊM£BY qä(=JŠR¡ÌÑD¾(Ï© AéQúeŠÆòBiQ¶h0ÅìJ–ã‚*DÙ£é(S4ž7JƒñüJ-ʨ@¡dhH ÊMÊÓ ¥EÙ¢A¼IY–­e…f A¡dhZ ÊŠ*BÉÐÀ” š8U„rùe[ˆrAc«QVhn9J’¢ÉU( 4º¥û›,[J‚ eŽÍÀ ¥EÙ²{|ÀÕ;ûç?é¿âÙþ§=ñ?é‡Ôÿþ[½úÞßés2®§Qû'ô¯ÿÞõW}‹}¨M8ömª%âѠl°pBùâñB©QVXD!({,¤”“%Á‚ŠF™cQù¢t({,®”˜¥GÙc¡Å Ì±Ø|YF.JÂ?½P” `(_„^ÂÚÁÂ,¬]’­­CÙc‘Æ ,°På(=JŠ«B‰°hå(Ê‹7e,7âj{cç¢l±Cùbf9Ú” u(Ûƒ°°mù¼PVXØ!¨"” \ƒ²Á"å Ý ¥áù¸Á¨B¶Ç`á«QVXü!¨"” M FY¡BPE(ö5Jô—eÏŠ°§£ Ùs4ŽGŽÒ¡ì`Ðþ'טAkƒ& å&Ciþoý;ß2ùgÍ·\øÿ=[^(-Ê S2ÅâôFå¢$X¤ ¾P½P”l0ªå‚…«FY`ñÊQz”‹X…a!£ Yž7´e…EŒ*DI±¸U( ,p9J‡’`¡G£L±Ø½Q¹( }4Ê ß¥CIÐÑ(sž®FY ¼PZ”šBŽÒ£¤hJ„ F¢\Ð(j”š%U„’¡iÔ('UˆrA©Qh"9J’ ™BQ…({ö ”9Ë•‹’ Á¢Q¦h2oTnSŽŠ*BIÑtÑ(s4ž/J‹²BÊQ¹( 1š7£J‹²ES*P¦hLoT.J‚F™¢Ie( ÊÍÊÖ ¥EÙ¢q¼y½PÚÅümÖÈ2”e…†A¡dhl ÊÍŒ*d›†&W£Dhô`”e†AY éå(=JŠæW¡DØ‚Qz”=6‚”6oT.J‚MA\­ó¥ø—½‘úâ?¡'Ï·þŸ˜kýÞ&3ù?k¾õwzû™5ìýÇ‚ 勯 ¥EÙbñ(P¦X@” Q!JŠ…¤B‰°˜‚Q:”=U Ê KŽÒ£¤Ø«T(Z0ª%Å‚S¡,°WÉQ:”=`4ʋХEÙb1*P¦XÞ¨\” 3š]'bqæ²gìûÀXœXœr”%Å"U¡DX¨Á¨B” ¬e…EŒÒ£¤X¼*” 8eEXĦXÄÞ(-Ê‹YÁ´ ¥fßõÅ–°ïöbQ{£l°°CùâöBiQ¶Xä ”)º7J‹²Â‚A±} _ƒ²Á¾ÊO ¼P” š!”Ÿ x¡4(+ì+Á¨B” ö5Ê ûJªˆÝÛGóˆ°§£ô()ö””9Ê¥CÙ£±bPh.9J’¢ÉbPæìïP:”-Nb“¼PZ(þ÷ìo_s ÜŸ-ü¿–ïÐK^μêñŒ !'ülúÍ}¾îÉBN†™Ç¾3å›…œÇnOfÄÖ¼5<ëåÙ^ôQ\µãkn¤×x¯Sðê3ŠsØq8NÀ¹¤ Ï¥2 ê™—>Ÿ‡Aï'œú§»«ì=2üBö6YÉ”)ðûvrDqå8×–¸”koœ+ªf?Ép7Õ¡t8\«t\òÆó mú¬ö™&ëÀ÷etËGž ¬i¶o§k¤×Bù(”—ì÷ÙÂ⛘¸šìuuxÝÓ·Úw¾>5Ô‹íÖ™u8Ñl?å¾z —»š•Ý/ ÏI‚JkvM|粎ënø-t–Á”˼Å‘ÌË çûžº”!n1åâS.wÿ¾”ä~áõ“>^96èÉè~Õ-tÅÂ$8”¿.³i€|û…UYcÁe]óCëŽáç8Ð'ôÖ9†x»”O¼$Xü ÅwÙ7[V‚›ŠãìϪª:zj «4îmí$0`€øñßó°ñ²,™ÉØ93%s˜ üîþ”ò†ŒsMd8NÍ ‘ï^÷¼Ê÷k8J•7oç·É°œ1mWf>é_ºYó?ɼß~góÊ‹.åms¹?ÉBþ—7Ù¾—IŽãˆZ59ßú®¬ ¦‰0'ô¼Ã»"/p˜¯{´—qƒUÍÕM™Él:³¿á·E åÒç"þ —ùQÌå(wò  >Áq"ÿ_#Pë° g5L·¿=áI¿Ñp^U¿ÿôc "'xÇ’F‘ ›fwøÃbÆw›¨©"©Wš8c\ÀE0Î!Qã8ÒèýÔ5`³£µÅV5LàåqÅ¡có°c³;+Üa}úÐ+9·#˜êÝ—ôö8áÏçŸ7úù1äÚ¹ø¬m8·n^GÅÀû\} ãèpœüŒþÕcÓàÝŠ7ß@ ,wŒù4ùý•“ÆAdž=z¿L`xrÿÓM¹‘Äâ8_xœ-Æà›]zÉ“«&Êði lwäÕŽç Ôµo5gŠ7ÓùLÁaû!28¿äÁµ¨[B]S_áòôÍ…|dʱ#_Q>•Á?8Ž!uP› ÇÃw} œËs¿¹ƒåM_Æ^ßpì˜öãy[$ÓDúu™ýïþBN*å‹qy]IbâDR+Á‡Äq’Å¥j5œ g±àS*n›Ôò¢œIèå´×úÐx0)Ã"™;,&µŽ¿ŸÆå4U‡¹SÃÍâ>”Ê5Î’áõm;nø’¨N] ÿHx³&4ªýݧÿ fÔ§‰¢ÖúñpC–×ìbãHÆíê¦õsmó<s!‡“r{¸¼Ï·bÊW3Ηã8±újöOY¯e¿3K€ƒW›ûžYŤ< ¨¥ ,åHÓ,’)ìx~ +ãÇP>!—XÏé:"v}d:?°Å 1åéqÎ78ŽKÆ{›k’aFŒ÷¦GÎÛ…Üzcê®gü†$žj6ê¦Ù¿7íÉLµ"¦Þ`_†r7éópySÄÄK¤¼9âO|ƒãÜ=ðL:Ô!D²Ã7»XžûÖ{\ÝÛmdŽv4»8±@LËk¼îÉ\ÿ'²L÷¸VôOÊÏ£ûF\.ô÷¼ÁqšÅ¶¸³Msîb×r»|¢q1ÓoÀfÆ¡òþ9µRdP§zʃ*6‘ÌçcK“ú ùÕ”ÇFùS”ÇN¹QÄ»6øf·^’X:´£·ä<©žh«šæô?ÜxÄè­Ì·ð·:‡É2X–zîc•H&·Ú±Ä$‡E åüQþ&åtQ^õ3ãœI޳¨žý§“IðĪ 3›³PÏî½cÒØí̵ÂûJmò€´É† ;Æeö•Š—?,>Õ™Ëo¯úÞ z%¦|[cn–¯°Éí‰Îu“`Èê Ã5—ÎÀùªrî ÞÁ |×aÑ·qðõmXø†ýÌyݸ*ÒEüs¿ ßG* uF< ò-—kVžó ޳;rýúƒ¡ŠE³%nÎÀ§‚QÛômw1®º6ñïvŽü?eüðž_ºø1TGÜ}ásx?‰ÉïœoËq~Áë¯ ßí5Ú2þ¸[/·ub<”›´§RÓ=L EA¯ÆÜ«É|x˜¹p…ØWàÓ÷BõÅå$¿å¹E8Ÿàõ7ú7©qF Øÿ»K<ˆæÝ=Õ#ŒI:j·ýÅGp~d 8Å5>Ÿ‚ꔸwé˜þ{ãï[ã¸ü¡ˆx:Z MXLbùxØ 5vu>®`ñT¸¾Ó²ßÄd^²l_óQ›2Àñ‚ã Üö%U|D0vOýî¸x<,ØØ×µèS³(5aâýÄ… ]ŸrÚˆg¯l¹pé®jÅÞýYr`[0æðˆpœ¤šl,»u3écŒ?}$oQ$sén‰|µ òÕ:b—Á4(hŠb¡Ð—©ž)ï™Ë|%ä[–ð ŽÓ×%µãë+¦×K‡'Ÿ¢˜üKWFH<`ÚÝ9«×ãç›ß¶Eׯ> =Wè9FùÕ×þ…˜ž“Ô· ~Áq¸iÓYh‘ìîxêŽ v¼²æ™’ÑŸ¯š°Æq,°4“ƒO"Ž«ç#ä™Ò|œø©ƒt¢é£;¾2ôG9^·“ò]lYÝxsߺÝÓÍ*l1·úrí>fù®µ­Ï«Æ€šÅÝ]‰` _‹›Àù ç åLR¿§5ãPŽŸ{ÒÑôAÌ`AûŽá·—™‡X‡G05Þv\ýp¡À¤ïŸëéB¿'ž…q®´Ç |¶ºnÞ‹8àrvUаł±N‹£™ÇíÛ:jáŽño­&E0U…NýL9wÄM¢:6^gˆpœW“ÙáÓðríéÖ«žÇÂuUdT[ý†rË«:#˜¬¶fR§Œ… ù€òS‰÷DëYú<Æy“'Ê™uæ)¸–\Ú#ûp,ìéVvInâAf»Ù»Sâ1ÐÆbë3¯¦ÑÊÄ/µ;úë_Êa¥Ügâ™Ò|Úà¼>—‡~ ˜6AßOŒ…*c¦š. ;ÄTºâYhób äh×Þ;‚Éø½ËÖã}„¼IÊÛ¦uÌ›ÀËsž5«^’Ê׿þûê9ßZœ„! üDÇícù>v˜ HºqxRGw8e[ͳpfw=/ñãY> Õ ù…{~©ÅÕ_¾lR:¹¦À6î“ =æ=¶Ã èÚÛÊj@›X5|÷&Ï#ŒëÔR»îpmÖ­ûþp¦Õ¦£e=}âêÐz¯ÅêÓ¿×âûb«\5^¿[€[5›—´Wý”Õc¡Ž&"ÌcÐQ&¼ùÎ&“ÆÂWõȽ’Üpf]¿¯íg\,îÔ'I}8-un©Fj­÷¨|‚ã¨-ÿ¸Qæ|¼möÞãñiÈdqƒÿ‹½ók*ûú=Š%ŠÇ2–( ¨ X5;öØÑAAi¡¨¨¨HGEÁбa%‰ ; tp‡4ÇuXPE±ßurÎ>¸ã¼ÿüî}ç¾WžçûøÌ<º7IÖwŸµ×ÞYŸîq8Eê§ÐûêŒ&fžòýãw^Pû~ëí¨öó&}li.v9ãûN,g–¬£ŸÄ¨…(¬Æë‹ˆ¢EåNBÍ;öqlº7ë{¤ß´vsFòžE›$2œsÓ»ªï7?¶ÿ'ẮÕñÉÇo?´ÓEû¢ªúì¹"D„»¢ñ Ì3môS^ÕçQFqÜŒ›Ë’ІڜßZÄãëÍQúv='d§Ø»¢ËzX_nSKž/[!ñKúÖÓ<-fß0š‰¡´O`žöm·Î¸õÇYô­ùZ­®6IhF“5þñ8úZôn7#G4hÕØ)›ûÈ0ýï}Ù¾ò¤o9ÍϼÃäßߘ~ÙÖˆô}×ø桹»§ÑƒÎV·[$¡a½ýä­TñØïšüùÝ\DóE¥Ø#ºãå‡~l}îËÉg÷•4ç› z¦Ó¬w΃ØþÀßÀ<ã&ø~hÿôÊHÐiqën"ª,”—5);Lú£‡¾#zÙr„EX­—{SÎ`û˜“¸%ëeá÷g¿ÑBäõ’¼@ã˜gÕî»éIMaŒ‹Ñý»§‹x© øáím剎hçƒ;o ¹ûïAó‚Ø}2‰7â:l†Hßln¼Éažýg{úsÂqä°óÓì繉ȸÄâÛà~—ð£%“z¼íàŒ¢‚Clì÷ÈðàfZGG±ü%¶ß0“—Ñyñ;á&qû¥«`šS.Câø[ï–'¢>û¬›:K.áY»ä7j» ýøéZy nzûù½ž¯X¾á/ú†w~]Ý·V0âŒíîëk 8•µ°o0õÉKQ¥éþí×>$¢ìÌ[‰î}.ãC,®ªw‰‘É‘Åb®9k‡®.Î tE.™[¶µɰ;÷‹?nøiÉtËž«JèÏ_K¦žiÝ67áÀnÜFPR¸uJ j7CUoY“Ž'=˜{×ò–+š«9Hâ–Föœ³ôÇ$NÉzIÇÁ:Na¼®ÚÁ[¸;œ œ*ZœÂôáÎÀ“²ýuû‹QÀ,I—u›¤xÄû­_úc¹$ù0Ù7’óMœÂ¸†ïý ÜáþùÙF¤ ãé0w‘†Î0'F/ûôþ¼U"Å‹2üwë7 Ä„wCžÛtÞðŠÉëhÞ Æ\:|3/?‰Ÿï™ž‚¼–Íê‰õ’æ0ÿàŠ$‹]…æw¥xã°ÅµWú1õZKDê[+ò’³6Cä|K·0®AýŒ¶ŸFJqó¬ÇwÒ[¤"_ãÞmSê3q·¢:GÕ~W´í]p¯î=d˜Þ—áŽeë¦ñF"¥eåm±Aó»ÝžÜ*è »Óû[:$0~HÑ¢´£j†œEµÖ;õ.ë[öÂç [9fãǦ®¨YþÇFÃd˜æ6aš?œá1 CdÖ¦°0÷üZ“†ýíaü‹Ak2ìÏÇyö9:}ŠÚÞÒñÅìü6íÙIk=Ã4ù²̲-Ä…xáð“/‹Z!rn²~sÝçžïîï{æ\ÄŽc®‡²•tÝILs²ÒЭ‹ýg{al6«]Y}dÞe£â)ìkè:»?&çH$¯¥ß‚e^}y ´éõ÷˜Zø­%y±ØR"MC¡W|ýTãè²ÙN”-M¾.Åçºvýs`¶?&ü%R!ü:Ÿý$ üRÒÄ9ÌSÒëô†¨ÂÓØÖ`Q§¨â4$]œöAj&ÇÓSÚÎÙí슖½®=ª}õ{œ“:2ñ99· <¡g£Fõ››jÜð<æÉ:³âã³8xÅ Ó1ÝÒÑDÿ f¦KåxLÒ£ÒUëÅìzEQ¾ÎKýYî©ë“<–pâ Ï€»Ã<»guX:øÌ9¼çøb£Þé¨[xy—Á r~*„Ëõ¡ Æ¿«K%ØqX¾ÏþBrIº7´@ûNY{žt=müÕpXGF?xul_vßLž[„ûaêYÖa^3Ä=ß×:®~¼~°çš…ñX2ƒJL2ÑXsQÌ'½ll³fiκ,1jyÀóOÓR¬¾ŸÓ·àˆ/[_ óþ–²È½¥Í·OôsÆÕYý«{Æ“x¬פã¢LÔkÁÙ»³ædãdËÊ-/¬]Q/*Ý:(Ńą³LWû±ï ©GÑó<ìÈ,)=°ˆ>ǸÒ)#%à ®Ïª§Ž»‚Zе{çîÍÆu6ÑÚoÆ;iV‡_DRüü·Ø/7§ù³uc§*V,jfª]/ ×šG%†qsn4Ñ ªKÀË{íT/º‚f¿7”t¿š«·ïÎÎ(˜ƒJ÷Ym«'ʼn[ž\yÄŸå+‘ýo}î,XÐÖšœ³4äQ…Á<°yвÞw ¯Oñ}wÍ £)MrS¿dãîçMœ`í€>tÀîmažÉṀxû³uòÜ¥÷‹Oí^úõú”×åÞóPMüÃ<Ýâ»Ä4_Æ[ž41ýë9FÞ.û+É,g=¤€áö¨Çæý§üÆIqWªòàÉù=/úùÓöo_‡Æ¿í¢³bY›DlWµr|ï¡rÔûñ¹Ç¿;æà‚}æ!SçØ£»kO¤…í—â­yËZèìÏr­ ¿Ä%9×"uî¾WóøN·2MJÄ­;Ž¿”*GêP³O7çàoÃ'nÛ£®Ãöµª“bºNæÏžÛNíï:v=çò µN¨…öFŸg¢eIxäëÃfüËrÔeN¯CÓr°åÔ¯õ: ƒ®%MÖZCž9lè¼S=¿Ç‰[RW§ýðQ@給 xw|˜gÏU»Š~Éø¨û ~q™™,Xwêy–ì»Ñ?.É-9½.£Ç6M«;Ø£“?Ë%ñK?Ÿ*ô~¾ ŠºJ¸ÌÔ%„0Oê™é½ë“ñÜ5“µ¾öËBC«>dtÏÅ»{N©eÏATV“j!Ã×;Ù\ŒŸìÏÖ¿Iýƒð éóÛ/rnOþžÆ70àÚP·O…)x®ùF_™s9Ítòõé¹øa¤ý “gæ¢+òs¶Ç?IqyÇ}ÁÂÛß_ù|XsO€äƒt« 혧He°#k®¡ÊBAF«oŽß˜‹ï{a5Ô½n»áúÍ,)¾´z¤ßë9„ÓFÇ™’9÷ ywwµ àÜÌ4<³ÔáÓð;YÈý¯³÷7fæâižOšî/˜‹šß‰0N? ÅTòi74Ÿ‘}¹@?g› ²ŸÑøƯW%Nþµ]:n½iÊÝl´{_Ø´÷µ¹ø„B7i‡xª™(ökwTŠ[ϽPSÈîÛH~@¸4$®¹\nŒBÿ.()·yæøÄN6Úÿæfë? óð‡¹O¥v@ứ€“bÕÂÁ—œÂ䜇¬ä¼™¾?Q# ç(´oèü[ë¤Z¸’:¾Z‘#c¶; ;‘"†ØÎÊÞ×v\Œö¶G¿E„­š ;©ìƒ‘:w+¶>ûjØšÛ£/ä\‚ÜÛÑøÆzÑaÑF»L¬^¼ïêøêlô"`ÔÁÚ’:']¿°¢ýã/™z!Éc½ðø+á…9èdM3É;­|ynEˆ»9ÙÈ1̓ÊE®žÍpž—'Œéå\ÐÑ -½ë¾z©— /Ú¼·TQ€É~‘Ô_èýÝæ\û½€œCüPã˜'¯û²¿ýå¸>íNçsÑU *ÍÇ'Nñù7Wµ×µ…ÇXî´ÿq爣þ˜ðD ÷•pfésïwì=:²Òøåämæ1çô/Èq襒íO[å¡…î>ë"å㳆Ƌ‘ÓŧQÊpñÊà ³ß}ß§“ýy=tž['¨2¾!è¾³Ë‹Õøæ9þ¥}Üì9þ%âVN`ªè¶¨ï•>øaaÊ›vMÜhðÙ ”î2æœÃŸ­÷‘çÙïHú®^¹½c{¿‘ËÂ<¥Ë-dáÉ }²Õ·ŽËZP€eÊ„—=uÜØ|ºÄæñ§G30y‘ç99‡2бÓú¬N@ÎѸu1̳m}ÒáÉ˳pËè óÑ¤Ž“Ò²ÎàEq®ów£ìõ¥ç¼{ʰÂÍb͆â¶>CÎÉzFêgäžhƒ}ÌS{weÊŸ)Yx´²ôÌ>ÿ|äðׂF/ ð€«ùÖGt4ºÎÝÖf¾› Û dïµú9¯OtɶïïòI@Î'ìC`že“ÿ\;¤> O°‰¬«ÈG÷²›­ie¤ÀöÆÙÅEŽh½©ŽÑWNº©×j‰u&Ï+²n®9í›Ol~§Æ?0MÈ®Võ–Ù8ìh‹6Q‚Ôë—놑N üGÀ²qîk碯«9&À>dœ¦àÌÆ9‡&çQ$èýêv§ñÌ#ZBÝ4ÈÆ)µ ¢ PëòÃní£ذ×1m6ÌAšëZcdت.üÏ#cBØóN²o ÷šïTã—XµplÖ¶~³ñÁU”a ÐìÇŽíÎä(°¹ÿÿ´GȦَfÞ2ìÓ­yÄÚ¶EòpRW&uZÚG& ø†|˜Ç;¨½2Ô:×v¥H¤ ”q§Å7ï:^VóEÔaj¢]"]#Ãæ§Ýšž’se‹£Ï éß_ãê¼òíðòÒ瑇¶X¥@>ƒ7L qû©C­ÈŸƒBœì¶F¸Ë°q‡‡®Í¶‡°þhìC“Q}Ô6ïØá6+óó´Ý!lLâ‰ì–P×->5AUãŒT·Dƒp3%0OÕ„Ï[,r±Áç/îIž…Èt_ª¼Vˆ­°›lTk¤I¿`¨(Û40vY['y#í¿»Þ±cóçŒúÂÜ/ŒÈý]/`ž«ikLÇnÊÅí]¶D¦¢™–±}ÛáW³t\}ÚÍ’½0«Z%ů¾=^ßêØ÷x%üAº®WÍæÛ3êi}0"ç^_Àþ¶#Öö^.vmÚd±ºEOžþÅdgž¾ñmßy‡ÅheÖ)ë]Rìy^Zá2v%ûzèuñ–à™å¦ÄѲ̾‹Ï½e¸Ç=ÞÖ-V¡ì>šÎ ùÌóHÅî×4ñã:oüæ¯Ý<ßVŽydUŒÂöªK?콊·d—+Ïž#ÓCÖ}meXSö{ÊîkÉûIòúÅöÛ‹þ¢Ç•À¸:n–ûâ~ËÇï\TE#ù¬1›æU^Åv-Ý»âƒÜ«Yygʆà>«Ù{u$¢Ïïkwß|^xÌÆ¬ÁóLãL*¾5;¯~¤õ1ô[1ê¯:Z=ý×b22ºSÏ1šo~g𣡠Ý­Mº/”]ˆ¯è<í9{ϑܯàÞ§QÁ?+ë~nä*ö÷'ûo²¾UMÖ>èQAß“åøs;X–VËǧ¥må-Æ^C7´·z©^ã½cßÚ×Ü=˜Ö;ód81_ÒqtËP¶Eò0rnKç1Mè|Æ k¡k ò¹(…@‘p Nôm=¢ÑŽ÷~é‰bõ«—î%ÃÒÚÝØãÊî{I>I8Æäûš¸†qÍsÖ¤…7-À5I:%×ѵ¾:Ó†ì.Áý­ß)ó‘óà»^t€ãlûs ̳Kt§¾ãÞ<9þØòØ×QQþý'ê)×ðƒß~ïÕcót`b…Ž DŠW®Í |¯ŠI]‰žÇ‚ÙWU èçaSvÔ`? óÜ¢.ž`ÍËê¥D«OÇ ë|úαZZßmÒ)Û6®ü¶ŸßdÞqœ(”åGkï}ýjÈ@SvŸÈ½¿«‚q·¼µó^öׯ—Ã,%2Ûý0X }Ÿ=zRûë<ô!­Í·§-¾¿ïäýixï-U@¯×ô¾Vë¬Z¸¾nM»7Uxš‹gÓÕÛ•¨bÀ¼®®ãÑ‚í|›ÓžhÝŒ‰GÂŒdøÝªßÛêïߣ ¯Ÿä³a›¾|è`@Ç ÆuÏðÐÙ'8ÂläÁg…J¶ÿÊ*ûÓ×ñÚ±…'\ö`ã¤*Úô%ŠI¾¸öܩٮÎ}ŽzS· ëmBסZ?2ó~žys™ÑIRôòköˆ€w×±tmŽNï™èë!=ã+H±ë‹ÌzÙÈÕøM@ׯåmû°ç>ôóå™@Ò5·eàg=ö^÷«æIw9Ø[þ´/[?nÜái¥èJ肼ð^Jw·[½»µz¹ŠKñm7_ƒI“pÚés¥jAú‘Ê£—¯þ‚nŒyp¯h&“¯Ž¡ó˜'¼Fœ+h¢À»9^×ÝQŠ:ßè()ñH;Ï+%¦h•ñüLý")ÎÌ8#Ó2 “û7¤¾@òbùÙ-ÿ¸¯Æê£í+ßLF£Æ{Ù ·¦ãæ™÷µtØBΈ³t ),Ekù‡P“eJ¬Ñùj;¶®D×+:°ßÏj°ÎÃ=S-¥Øbmþ™Kû=0’×’s'íÔž“Wøi78Âøh8EºVàc†©Ÿ7½-Cýû—Šl¬K±aÔ-댛žˆþ^Ïê²Á¹ÓÈ0Lö'äyCû¼Z@ßÖfò ö>µÆ70OÜÑô]ð:ìl¾U[”#qÖ„v½=JñÍsC®˜œ‡";Ø>äÍ”â3«®‹Öcïé‘uŠ|tÆ'žý¡¹M ¿7óŒØàtzÁ(~½f‚]ìÒrÔWçM{)~òugŠw×yHÎ3²y…cÓš §¥a˜ÜÏ"õ ’ž2ùk¬í'©S7àµÃ<š¯eUàG~ç_*GÕ·ø÷º&—âÍL>ó<ý}D)~HªˆÃ$/&õRW õ}ò¹qï…Êa#­o{úÏP`-»å»¼)GÆ1=î V•bé’Èãß„žHkYx³¼R¼æîêžyßýÙðû?Ùìy6©_z­Æ70Oú8ñ¹eb^<¾EêV‹ DŸë—1ë¤'º´bÓ©›o¼0Vò±e8ûý[r.Kûç{.B>?_ÎÃ>°únÍŠ… ¬Ûô¯¾v>hbs×G·ÍËp¯‹-w?zìNí¹5I=PŠé{‘áìs–œ/’ïcÑÏÛæÌ:gØàû_|˜gjxñ5Y§(F:ß‹¯@/j{ýuʾ S»ƒÄÍhçÙ]Î »J±ªãs»GÃÙº©Ð÷ŒªÊG7_z™µ`÷é ò(˜gfMùëèp˜Çðp— uZ¼né–ÞkÊpî:~k¨ºã}.wÄØ:¯ïWt"“º5©[ómz?øYÀ½w,†ñç&ªIT`ÍvÉà:Ú\Yq² ³71Kn7þéùh¶wLUµùº:œ­¿“÷ƒ¬/ôþïµ€pÝéý&}/ æÙ355;ÝI=*ß fÝ@N‡ÜF×˰®(xкjOtò°ù;ØgÖyìþáÎ~/ƒìïH|Ñ÷~Þ Hœ“ûõ¿À<1Û÷i„ÏßqÅлo “·%~,ÃÉí+Ö‹ÎC³º¤‡Âë¡ÏuÃÝoÖa¿opßâ× =Ïi1¾ï× N"‡y4·® ¬·¶Û‹ò(Óÿei q9žô›×‡«V P®‹‰x¸¾L= ™m8&ù y]¤îK¿_FÝVlÝôÞ´A}Ló([·™sïä1í_Ô›ëÞDóã]f—3÷I¼Pïa9më?KpLÑÂé…Öáì÷ H€þÞ×ÁÔøËG´*¾ hÿ˜Ñ~¹ ÖÞŸá1º¸Ç£FSo¢F:§_l(ÇSºZIN ðB¿ÇºhØNŠ;ÍYl’¨÷=ŽI½êñàŽñf±oô½V-4håíÀ? ±ßÿ<¡Eÿüì‘ô³GÒ¿¡GÕ³ë¸ýC½n¥Ö÷>oÿÔ£—ô‰Õ7ÿßÕ„ACØ„£ºäA-™P|VP½ͨ¦zð!È#8Œj9àᲠÄ¨æ² ˜Þ”ú «UÁéQÎeÖ2}࢙þ”„OXÏôëfzTŠ8ŒBŠU-çôƒSƒlÁX)=hF!Õ«œêQIõL¢z•S,.£0¬£Ïô*'=*c9=*• £K˜’ê!§Y9% ÔT ‚QcAº`Ö`¦§‰ÓÓäG}äVef†QHXÕJ¦¯ ÕK®²Qß$[†SHÞ$™€ñ#9¼jãù§ðªs I¿rª’ýs­ü¹V†iýûÖJóº*µ¾÷gú§ž˜JÅ®a·1Ç•ð¯‹â¸ÖƒÄÔ v4ÜÇU2 äp\ ÏËëúÇ•Ëë3}å(®u0ÓWŽôñåòºê™N¦·avQƱex†õ ×p»(ž«‚ÓË©䯒3Ü.ªŸ/Õ_ΖO÷ó¥ŒÆåvE4âv™0ý|I¹8N¹J†ikü½÷S%HæŒé‚AƒA*ŒÒ§z›ƒ*–kô?ô3}Î)Ƶ˜ávžk%È‚é¥jĹv`Ø]<Šo R€,ÀøÑ¦«’a;üˆÝE˜®Ù]¤§/Åv â“úùO¯•?×Éïëä¿mü·®ºÌÿç5ùÞçüG¼›hNßM%ÈâoØ®VÆÅu5ŽÕƒÄÈ וê¹éA-ç°]ë9¬Âøúß•ËøŠÕv¢9†±LÒ`Û…î%Ìe|™0=ΕL;Âù²`x7J¦Ç]Ã~°bX¯õœ^ç|0R¨¶Íû’ƒø|ø7|ºÏ°‡÷•ÒˆõåÀô¾3Ã…TL¼X†qíÃôÜ$=Îy`DP%H†Œé‚)ƒA*ÌÇôÜôaznþ¨Ç9ź±bú«9œWÓs3…aŠA Ó‹˜â~Y¹#AõÔÚ&WpX¯„ñ#a½R‹á€5îIü3ü™?†iýûÖG}æ÷VQŸ§äú{ú€*ABÚXïox¯„Ax`& ½@J¶Äcx¯ y4‡÷ªd8\Øx¯\Õ—XÁá Vrúsy`Ô‹v`¸8õ&Ï€îËÈ㸯J *Tƒ± ŒêO\ß“æaSý‰y¸`‘¸`LbÊ€ [ŒÆð°)FNHL RD`Î8>4 ¤Ù‚QS@|0kHÅ0_%Œq½@JXâ1ýCå Û‹á‚î« dæŽdú¶s¹Øb† ¦ †÷)AV`| ‡ýZÉ0!~Ä#ìׯl0Ò£˜º%LÅ(õój­üO¬“ÿÓ×Èÿëã¿}mäƒbAºœží?bäP=Ûy > JU‹†|ØJ†9A8aÖ‚7š `/’Ç­‰ña¹Ì “`ÃraÑ úN41Ä8t¡{"s9aL¯vŠmÂa…Y1|œJÓYÅ0Ã$ŒqHÏv0P$¨¾Í S€LàMŒ©ø4Ÿ‚0Ã'–Ë £ú&+@`¶ Ç0°)6ŽŠÓ«]ÖÁ` $3ÆôÁa 5ÈŒ™Òe±Êôjfø×B¦—r-‡« öbzµSŒD/dÅôS¦ØaB0v4cn/’aÆJ8‹±Ã;–Ça‡5î©ü3oü÷¬‰?óÆïk£ ó{©©Ï‚3ăõU‚„¨± ]Ö` $‚ éþ G–ð,?ŒâÈò  }@• !v,H—áÈ*AVäG¶’áYpùa?âÈrùa> %‡£¨Y€9"ñÃx`1Ãê¡ CbºË‚a*ò~¥¹„#Fñd+A`¨hÆT^ %Ó0ƒRø4ׂË‹nÄ£¸ (fXÚ|0bÃÒ¶C¦€ôá ©A¶`Î ª9€Qå 0k$HͰdcA<0®¨$Ç‚tÁÄ>LßyЧíÃpÄOV ‚¹£™Þó\¶¶ÃÓÃû€*AB0~,‡)«bØ?b‰¦lc–X0HŰ-¨¸¥~þ±Û:ù£5’Zÿ;×Âÿß×Aê3‹éCÐù€*ÿë Ò…` þŽ,ác&áÈZA J@<VP%‡#K®W#Ž,—añ_°d —Ì ]Â;ÅOL¡þ„ OiÄ)³Ò§ùd*0€ ’ádˆ~,Úƒ 0E4c /dÁ‡Rƒ„V™¸«Œ2ŽH ²E‚Ô [†—Mq{Ô *¤¦ ©A¶`® ª90ìX}3š[ ²ÓI@<0ž¨’Ãí¡xÙ"0¢„á™90<3Š#ëR0¬DP%H&f˜f"0«ăµÍ‡Ã’å06~Ä5#,Y]ÛLk[,ˆêŽàó3ü™jý;×A+fÞZêý‡àŒéB€ƒT jH‚5 ¤ÙBЦ€ôÿ†'ˇ ŽàðÍ(ž¬.t0HA`Çôžl%HAËáÉR|3Q#¾Ùx²\¾Y0¨dÕ™f6ªAV`ŽÈF|3]0ŠÃâuýÎ8Óãx1üF]0‡sFqeU +0”Ä£ø J†s âñiζ¤ßˆs&iÄ9‚ùbAº`@¯¾4kÛŒɰ¶ÀrLª9€9å 0h$¨$£*@`ÖhP­)Í”é‚qƒA* Òƒ” o;˜áœ®l-Hæ–€êq·}Ö  RD`ü8[–bÙþ댰e³ÎÂ@jÑqJýüOZ+ÿÓëäµFþ¿º>þÝÚøß±.RŸA ˆA R„Œ± ]È` $‚ÀŒéCp†TìUƒl9 5­BðÆ‚t!€ƒA*·–ÁìÃáÖR 5]ì`P%ÃPû'v-ᨠ!ðc;ѼG9H à’7b« Á‘ È‚á«Q<[!Ã}Tƒ„`”hPí¯4ÏV ãR‚¬À8Ìã²âÃßÕ‚Dÿ‹½÷k*Û÷þÁŠK,£±N챌bcÅŽ=4¶1Š;:ìFÅÇb ;öXWѨ£bABÖXFbÏØx¿;{¯màÕ¹÷Ü÷œÿž÷užçóœ{ž¹g/H~ßµ×^{ñûTÿâ]óÍã]sC°ü8çP `!Àƽ[AÐ ‚_;Ø€¡3)‚ l@… !Ô;PçqàZ€ÁÔw„ÓX€!Õ ~m%ª¸&Áë¸ÿÄüg^±Nð±)f=pG ý<¸œ“M‚pkþÆÉæ%8Ù$NN6/„?p=äÿ}½ø}½èòÏ\/z ×µsŸ/ŠÓ$(P °% Õ¤(Ö``*­H¿âÕ¡ˆµÀT‚W‚‚Ö+P¢° @*øp-À Eëäõ% >¸£èýÿÆ‡ë… Ä   (Çû&9Ç·á6î} BbÅ;£& Epüÿ¤ò& ¼¸V @ ôÀóO ðB¸b»”w€›€AóàUƒ÷âº#tþÀ¼¾X Aýjó^p9‚ì@@š€ ¡Ô;P#œ& G@C„ú‚à°ê€½ïÄ5 ‚«V D€ @Šk€YλÂ5Àìäŵ%­îì ÷ @†Àk€(|ƒ“×T˜ @Љ Ø€ ‚ÑÉkJL Å ¸ŽÞJÂç†û‡›+ÿóä·æH|mÿöù‘Í…lÞûšóökóÛg>ûwÌcÜöw󛝨ÜôwsÑÿÉ<ÄýîF Á—n^˜{b V D!€Å ¬_ñÛÚ€ bRÁoë…B‰‹X·­;ŠÆßÉk $( °ÅßxmÝQXþÀ¼P`±À;' ZÁgkwò×ú¡ÐÌ@bÓwÌþÀÌ=o¢ðÜkð[3P õÀEè,Ügî]ŠÑ(x³µÀT(L#¡8µÀÔ(R£PC„bõœ¶Và…¢®X—ÜEtØjP¼VîY— ß'O­H‘o+P¢À @Š"6ÁQkRd[¬@‰l×uLó}=ô}=äòÏ\©„ÿW˜*`Rh0° Õd(V-°5ŠÖd(\-°5 Øä(â`j³HQÐÁÀT(l#¡¸µÀ ”(r¢Ðƒ ¨Pð AÑk€x¡øcÐ+P" E‚x!:`^‡Ø!1)‚âL@‚Àø‚ Cpü H ä’Ø€ $•Xá2‰”wt'‚¦V Dàb¡Ó+P"| EýkóÎnQ'„Ñ$9B"Ó$TÜR?` „U/ÖÁ 6 B€@†‹œ÷x P Ôz!Ø* Üšçvyk€Èø``ãþö Á7¯v Æ$`2LZ`jL& ä v Âä`2LZÀu€Q>WBI:æÉÿ¯æH|m_ÿ•y1Ïþ«yì¿3w}kÞúW×>ÿŽuÏ·æ6ßäk¸yF LÜõñ¥*1¯€_n0°æ#áËÖPáK7¾x-°5 Àd(‚``JƒHQÁÀT( C¬@‰1)ŠÄ E¢î(`^(˜X AÑh€(Q<îÜY^n}Ãý½,·×έ@ÁX€Š&H} °Š(E$Aù ðB1Å J¬ÜY áÆë L@ެ‡5ŠÍä(¸¡è|Að@ñé€ ÐÈQ„J¡HQˆÁÀ”Üßh¡ý¸³µ(H÷Lƒ¬J‘Ó``*äÔdÈi0° ×d(^-P!£Fîùµòiäž]f­LÂßÉ g0œç ­°ßä<>[WÅ:½›üÖ^“RØkrwÚkÊ{V-|ùïÜ»J3ßÉÀñÏ}áÿ–î³*x¿ËE±ÿ^›•Ý5^|GôGDüÆ5|¿E™¯æþ`œ1{Ç‘^Y[&ý"ÏOú.ÙÒ°~DrÂú+°þdÜ8 Œó îîÇs#.Òì=é³Âß!á*Xøðµ<“›=K¸¦v½ýÃ)ßG1@ìG–»_¹É“ïÿüYìkÉ|Ü8jŒ3îyõ¬y‹.R¿„_´}}‡Ì±]šs¤Ìmš¤Ù{lzØÂu›9N¹®HWˆýKY?rÖ§ƒõÿdýnœ½ ŒÓÏ!–¼HkÏ6wÛ(‰œ*ã©m{›ºÌ)Z¶x‡Ñ¤ §U¿N£Æ+›—RüoÞYÖ?‰ù†YÇ\}:ö1öE:öÔÞ×)“’È E™•š¾MïM䌖>$¿C¬NÃÖ¶^£g€Ø§‘ý>üõREOëÈ]߈ëïzÙ®féJé§7¶;‰Ô¸×·^Ö²ÛtU\Ÿ›wŒ"÷Z.ÞP´^ýõ]+Ï![D_ëkÅú ®wÛ¶²M6ß/Ï‚ëvð´&ïyŽÝ‘ѿ՟IdÚÒ^²ï6-µ¦äàçåG‘Þu÷oHA®íè•Ø.PìÃÍ>'¾à±¯b¯ÓSJKžÖû9 x¿UqÛÖpOåì 4úx¹s˜Éd]ÑK?ܼM£Û]nØïêHr¦×/‚Ž™>a\HJ Ø—õA·4Ù´¿Î®»¢ŸõrögH1ÎóB\ÇÊ ôô‡{§'Œ1“·K«”îðö6-ܾ+>ê‘ÄÑfxT]tvŠù¨,Hì_Í{­ÌžY®û-¦^‚׫’&;x×Û]U^*ÔL^÷í2Ì¥R"=üëæ‚)?Ž"+&ίóáQ8Þ쯱a•ƒÄ~ñ¬o ë‹44Ç>ìX¿"„õ+qî¿¢Æ8«6qBÁ tƒÏACŽÉLNû&ïxæ™HËïùÒt~©6z°üE×pªæºU'Š1ö9±þ[ì{à}0 Eï #§oyÎÄ|z/ÿqUÇOf2ñðÙFÇ$Òtå#EüO>¤ãïƒOÇý©£M[K‹|˜(ö“aùc¹kÜ)äÙãæÅ>/¬_¶#§ÓïSkç\ Ïš[p¹I2YØuÛÏËV$ÒFë»·«ëCJ–‰˜ß¾X8½_¦kÕà”õïb}ù¾>IÂïQ‚0/¤#\=Ýáˆ_ ¥÷ìÚ”9>™,¸}·æÁDÚäe Kp72qÁÚ„¿†ÓäƒË÷ü z"˜—œùé<Ô“›-IÂrýØêf+¡?µà-Â83/yt9ØõÝÓ¦|ꎈdR‘ÏŒæDšì:˜ÑχL™ûùz8½Þ¸{Âk€è!`}–˜'‡ïïãJ–êÏmϺÚ:wÿðX«Â¡ýé­[+4±MZ2Q„oóÌ—“H7©´ºÃŸ£ˆ£«µ%œ:Ú‰Ù¾Ì|ÿê„÷á%ˆ^þóôæÁ/Šq4Ù-¦n){ÖÚ¬(½B’B¦¯Ÿ¾wOÍ;tϺÕW—E6‡rÿ„S—|wv ÿ!²yù…X?Eæ5 uÿµ[â¬f¹ú‰)0Ί¸ÐW]/ÐÕ…lå½”)dp±OÃw¹C7¦UX²pØH2ßQPá´æÛ":Éà@±:ëƒÈü ×L¯ZàV)ÁoÓ$—ÇVqªçŸ3ë¯;ñÔq; J!óW¬‘oüzrýˆÝ;o¨É¿õ§Z_ÔQG;Úcb¿Ë¼^aÖwrk½Ìfe›ÖÕ‘ŒSnZëK7Å £bœÖ²÷ò;´∢ú«IÅAêlÓQ·«ûæÕ/$ö‰åîê„ÿ¼^zò¿ßgÁ“Ù„Ï ®à|þËÑÃâiVÈËòç)äá‡W ÿÜu‡Ž<ûIS¸šÈ¦/ž}\G‹4›œs¹{èÉduƼÛÌwÃ÷5l’Ë/eÄ8i]§ô:Ñ0ž¶‰¬K¯žJ¼Vnj½|‡^¹W«Ç8•½*„ ,Nç¬ÝJ=(ˆtˆ,äbSöó;òëY_Ÿª\¬h<]“u`{|¿T2[¶¿u…§wh/i‹ˆx5¹"çfäpººàâö¡]‚(ó‚±þެÏ_?EHc‡ø±i®Ïßå€UáwýU̪'çém×5sSɲù›úm*žD\‰.3 ãH²¹ý|õÅEátãl®ƒaeó«橞>>¼Èñw‰ñÀ¸ëc&4×-Ž|`œ{1Ó¦.Œ;O½}r/<•üÞ3dîOò$š<³ìÊlù(ÒÑÅïY2æ•€õœ@,2¿ ëÛÈÖ?ñJ69¾ç“'ï÷m–˳ À8õª´¯¿ú<Ý÷¦s‡¡×R‰n[ðšPï$Zã]ÅSJû·8MíAáô¯7'ÅúRæ»æ¯Ó@ÌGãš“]׺ü‡:¬}þ¨9auáÈÆéÜ`”ydÏóôÆÚaŸ;¾O%›7ª*®šŸD[—í³tÄ2­Õ/ÖnátÒÓ˜œ=н¶¤e+k ý¨›ë'ž-FhÌ ‡'Ç—s‚ŸŸÅ¾œŽ|`ù«?žw=O§[§«“F¦êww$ÑS«TY;š¬»xëe~ü>œEÈÕ ö=eýeù~h/=YŸ7V¿ÎþÆ 1î=9gWå&÷!i¤ò¸[¥W“èâg§V]]=šD>Ü}ßgv8·H—v4;@ôɳzbëcvŸæ?¯&¹¼žFŒã]ÁÖ4®K5o ×a]qô•{›DGÝÊ÷ã{r#ßá½c·† ßK eÿ{v_guÀ÷‘+-z#û¯[0NÓÙ «W{}ŽrÝÐ ˜ÒHÏ’]ÛU5Ó^¿f½=ña¹[}åö §Ãi‰è‘öà@Ñ÷ÌÖݬ¿›ñX§e/Þ¸ õüS.“ËA«b|ÌÃéAáçhF•}‰Uߦ‘罃/'t0ÓÄ ýß÷=’\hú©Ì‹;áԡ׊ó û}X~˜¿šù1û¼J1N‘¦ùPÚç转v…7ÖL'õSCo1ÓU—¬ô”$úUOfHŸ†ÓÈa•¨­{ e~?vÿg¾¨Rù6MÊ*)ö‘wîï¨À8—¬šZÏRÏR]¿Ú—3ú¥“ÐÚѱiú§6Ï®¯Q“ëKÇáG‰ …n‡õ}åHYßu¶~çïcO<çM^[*1›¿®×­ùWwiíYgé™dî" x7¹¡‡ÞL½.OËר¦šÈq#(ïEý{¬ž™GîØ·U>Ý.AÎ,±V†ÕËå×Ð`œ­eÜ×ùU?KŸ½<&úp:Y:éew“™Î}5æPsW5iW4)Ò¯]½³jv× ‚ľŽì{ñð÷;ë³Ìêùãë¡õjŸ(!zŸYÿJG^0ÎÀ‘ÍŒ“4gVòþ6éé$_د¶žV3mÛ½vÛð¤dA»‡†µˆ )ƒ_÷.˜D™7”ùm˜÷sɵž4âúCðÓÞh¤ýê÷Ìœè–A–Gô½"uK¦|ŽG¹3¢ÉÌÿW ;Qv¿b¹gýXÞÙºø÷콑ã^{ÎøÅg“>£^®õ·Ë!«"lp×ñ9”îZкj3eièh°˜Lû<ü|Z5áû8†ÓY}ª–ŸÐ+ˆ2ÿûyÙº˜ù›œŸ¤¸þËù\%RJâ§a†Ì ;Çw|2½£,5«1×Ç¿ôä‹·‡yŽ`þ-ö½³~þ‰÷¯½˜U·¨POõr{À0ÎlNs½î ¹§ßÜèMdwB؃+’)ïçV“Ò-Úžî·1œfßó]½çc Ž»]§ æ„y`øï9Ù3-çhú‰¾EÅ\òŸg!>'§h¶ºI—Oӆø Ë —¦õ<Ùno2-9mÔgc±‘¤µãFN#²§ŒYv-PôGò}k¾ü Oæ³vî©Áõó{þpgÉÓôt«a»Û=Ë C$–[p=™ÞØÕædõ¦#EŠß“ü—Ú, ÷Øú0¬æÿE^x²¾îüóDa>¸þݵœ@ýMï1¬R&ÙœÝ&KóâËç”üdzwEÏÚoTÒÛY=Øý·ŒèSduÅ<$ìyÏÙß`Ä8ƒúÔtß’rVÊù3‰ArP¿RèŽã#ú,^¦&Qe¶%<í!ôŽl¾bù`}BÙ¿çëŽïßjÁ8ÎŒæZRÃÃÕŸ7MÌ$^™;Æ+›§Ð®{ h£&ü¼AÇu¾Ýiûš@±ß1ËëCËú„:û][…Ì]cöÍ?A«W¿\á°6“$tîx!r@ mѹE—ðÊj}B³©"æ‘«5Êm+²õï¡­Oø\<>/Þ[(Åu]°æýÓãôäBî<“´«yTÝ{f }Qœ*ŽŒU“»k"®¥uŒ 7WW^Øë-ÞÛÚ€0ßèÅË›we{òÏseÄù6—WãÐÄÖXê§ y×5™©™djñèBdc Q·ÈýÉ.#Ih)·u&GÐß6W÷.»ŸÝ/ˆ^iÖG7­BýøÇ‹ ˆýåsõŸÅ8üó»F¨‘Ñ:'“t¹·¹gC ˜Å9G’|=özAqË”€@ê;˜{²•‹Ï·üsœÅ“_·òž[ ®{·Ðü…’êþû+e!SfÇwðIK¡Ù VÜrw)^ó´>}ÿ—<0«#~pKô8r€ë®:Bµ›{Œº.Ò÷ŒÔBJÈ–wi˜?•ÆP#®k[ÑÃ;åîQZ±JíýךXÈ€ÇÞÊ©´Ç½S;îèF‹~¯ÅW]*Œ¾(z–Y_éâ‰Añ¡<, °­Â{#,¸î‰3¥ð“¥C;•]×Ra!µÎÍœ—ãJ¯¾©å­Mr¸ÿ˜¯k2nþˆa»¯F ÑkÄ÷ÕæÉçÜEXW4Ìå¥r9bU8,SÆ#ôÔ\ÖßB./›U®ßÂTš¼pÕ¢÷õF“Ž¥º2ùwïÒS)óðû3?¿=ñ¬#kž³­dQá~ÓBì‹î¨ŒóàÙÖk¡?5ý5àŠ…T ¸ßayt*M ;m–zø$Nµ)‚ò^¶ï#ïÃì{eûb|.~Ö‡¼GqÆe¯{>â¿ï«¢oå›ÍçŽúÇ8¼Ÿå%ÒŽü6ßBÚŸ*R±æŸ©ôê³Þ7=Azwui¹¢B$ÝzºYÊ®Z_æ;–Wæßà?¯·ž|.šæZ×h0Τ+šCë¢wúïÞ¿o¹…tº¼ËÇV,Mð #³·ß‹Œ¯Iš^SÍíþâÃc~t~¥I®>ï:Œó‹n‡1_ÈAZ,̵‘i£…ì/2~UûŸÒh¥¿NÝÝÙu¹°bqõéï#hÖ¡™~±-D?{îàû[ľØüsi“ÜþxŒ³³l›JKß œE×{«…¬›³9ð©"výuôƒMgUäðÚ§Ñë“"h‰èuƒ†2/ë÷Ï?zV+Ñ󇡅û®[Åqã=@+îÜU$ÂB¼}¹±Ðà4Ú¦Nû)3³‘m¿Ô½â~1‚ÞJ+:òç_|ìçg}°_(§†/¹\”¬[hDHÏŸsí÷¹µ*Æœ/¨ë{"–V{åpm‡…\ý­Üð«~i´Å´ …nä "9Õ~ê)¹A·4ù£HÊ}¸Nc×á÷§-ÂïVþá+Å2°S¯×É"©ìdè5K‚({^gë0>ÇO=+~[âÃ:\÷\Ÿ* šCÿJiÖ#ßm ùxX1íh6>ßü=ë¶&ó}Ü\V·‹¤á^ï~-ú2ˆ2:“оeÞã\œÔl¸áXÍ--rûë0ÎÁÒœÀ0šž‰ÚÒøm†…”Õ†-éU$¾ÿ0/s_¯Ádg²‰¤£¯¼D¢‚Äç,v¶ndûOV²u!]ÿØö˜–|ýcœ¿ÊºÜ«Ø|ý£Ö“‚ÛZHÏgvß®‘NŸym¾ÿ¬ƒŠ¯Üibç°Hú|Öéæ5A”í×1o óñóiAr$ÇõήáÅ}*Gý³*Øýìì n§ÀB6>:›>¸M:5ÐóãoH$O$þz3’^³ÀÃ+8PôZ°ù“y^±üÔÛ·ž|¿ñæ¢ÌQÿGÖÑ»gÔvu<î¼²ø=:¶ê›NWÜŽ?vvóòbœÿ¡BÙ‘4eF¿Cþ¬ï|uñù“­'ù}¢?yyÀ8uîî}5uÓVú¾$g˜²‹µ N“VŽ«¿ü`?òyëŸ7*?¤FÌýðë e>æéd}úùë¾ðdûp¹¼0g÷ºfegŸ ¥ tÊ>Õ\³H³Wd?ÎJ§$±©Oå|}I!ß¡W[ß‹¤93§¤¬¼¥¡Õ~¨{õá4Âæ?¶nåóüÖ“­œ÷ 4G^&ez£ø Ô·Z}ûd·,"í^jÛè%éT’’M¬kú¸…}öôIû·m´«s§êX¶?m)úEx_ñ3ÑáÈ ®à6 ·ðµÔcÂâÇóJd‘yÓÚÑnL§%_+S—ôU’ôƒj(?ERþÿ/€2(Ë ó/¾WW¬|"¹q®÷¤F\ßWºñj•ß—Ó¤dí¢±îY¤Á©+çǤS~×ìkSà|­t·[ËŠÞKÄõË%¿?hóäïwo=Ùû_vßpäã|Øpmé¸"Xßqz̲Ydµáhtãé´Q§vÍfßëOnMÌ7¯çêÕDQ¤leß'ûyùy0Ó“Ÿ¿^:>æÕúœØzu¼–d‘ÍcË­=u-*âÂ~öáM\'LH2b­™Ô«îþ Ê>oö<ÅöëŠÞ.ÜòLï_”âºù^ø|uEê˜_ªd‘å7[m^x9Ø5âñ|ÿÄ5Êç—®;èïë– -Øä‹…½g`ûÌçé¨{\wkX‹{Í;!ÝÞl±^šEÒ.xWî÷!Ö9–ÖgþÄdKó˺î êÝVÄ….¢lÝÎÞ+±ýRnµ_z ïmVãºáMÖé+ÌŸK¢t©=*ÕÌ">[}ö.A?–dº?Ç›ü°»m@l$õ1´v¹»o¡¸~gyâsü“_ñõ§Áu9ëÝû#‹‰>®è±ùµ²H}UhfµÚT:ùfÍíû“Ô¾["W¯Ž¤º©Å6>É·PÜaûnÇÖÔ‘­1¼ð,WdX òü…'[¿°ç]GcœcO£êæÛ²šüøóµ:Yd‹º[þe-3èý]¾£ü‚”¤Ñªþµ/ER¹ÆÞ©ÇBÑßÄžŸ˜”ùm=£F\ŸÿýבGËûuˆ­‡½®ßa`š…œz{r¬^Ñ“ü6ÒøûnÕj-¢LÀ“/Ÿ;Û‡ä¯Môô2Ÿƒ£¾q}GÜÜCÈÂxm”E”1mO”¾c!×7ŸÇ“cRµ_q­o¾(:ª×˜¾æÎ”=ß°ÿdó™o¯vU¦Üx%ìë×Ìå{w9nUÔ²-ÝžSu YŸú¶Ý“ÆYdÊ4kpR‚…´(´Õ½µµYÝþF[M-àÚtÆ3] è5f¯ÑÙ÷'=ÎüîÛI!ƒtèF\·oÓVGšýa!Ë·u?õ²T{rdåíyõb¢h¯À–;úQæ«`Ÿÿógy’y÷ãýƒ>{æz>Æõo×L0g‡“ÊáÛw&5Ì"{Üu¶š& ™90äL’‚LÉŒüzN¬·â\£åD/”#7»&sòï!« .Í»WÈ"cïßý¥Ï. áìUƒ#{“èy[ooŠŠ¢MܧÊrËžØs¿Îþ(øüšæ~>8aUñ›[!ûâ^’|¸ŸÛý2Y$ð*àÖ´§µ\IJÉF’ÙB|ÿéÈ Æ)./»ñû~òyž-% ÷㥡wŸµ,sÉð¨%}I;[Õ´]Q´»mIóz~ìýöO„}þ·®d¸µÿô§à«,Jx/aywäãðûU±dßeW^Í"ewøHÎj-„Ÿ·û‘©'Æ,µ/ýòû°÷Ál­óF4InTs·«04׎Ü`œ?ß<_ón×ÒO# iR0‹täû~íR i\ʳÕþýHÙÞO[è£è¦ª½ÞŒþ žÏalöüÀÞGòë£f¢Õ‘ŒÓòs§1q›îmÁÁRØ»pÁB ©-KîÜàt_R~Y‘ öòÑ”÷fþúÒâ}šÕA…þáÕ¶„ûV#ñß;rƒqj-Œ¯Ôhú!ò‡ÏU“ô…T.+;}y¦…t^>ì }¡$3~2/ÚM³×^õ~U%@<çÂÎ…°óX£òMùGÏR¢—‡ùîùÁ8xXð´éaR¤õÍO/,dPHŵA“-äZém3›¦+I®Û¢iãÕ»ñç4¢_”í3-»³ç'æwäãôþ³xÿ¡O“F-‰~f!Œíïìm!½{yìZöº/ ù­Øœ1ôzäÝœÕú/Ï×ìù‘÷bßó\róêáCÛ\Äççóe.'­Š>«-X}„4ô¿1H‹ç˜|ÚZýî±c…†®m÷¨?ëVFÓ¿?¨µþŒF|îå×·¥Û×uä×ëèšz6xÁQr3úIÕdÜg«5Û}sšÒBê t2m ¹¹|ªlþÑhÿù༗±ñ¼ ;çÁ.e ó$óëÁf¹Þ£*0N i‡a­æ#sVŒ×ßÀýv¶«bD' é×ãJä¬!$ógk©‹PWxJ{ÝG#>W³ý‘.û¡O“ížaÅʬU>.DØû ç}5Æ ZÎ-ü dgÏ&…Û_´Ù'^ÿµ……Œ–ÞÙe8ÙRñ“¯Ç˜hÚtzù·‡ã‰ï…ÙïÅέÿxORËü‘uuP_h.žqäãôúË}Àª_Ž“Î¾*'Q YîÖ»ÆOâÙìæ–BGb¸q4=Ö©‘G]DÙþÛ¯c9áçá’â~óù&Æ™|¹\ƃ'ÈOA‘÷[ÈÈáñn,dÛµ×.P“s½LpM3—ý&¡÷·Xý²õ ótuZ¶±í¶¹ß?1ŽßKýž~:AdÜ‹œCRýì6ãâBRÿ·¹Ûû,S“^ÍÊ÷CÏÙǧ…ejÄ÷ ,oÌWÚë’lqã|EÄ}#ç÷ Œãs<*Qk8IMï3höÔïª`ɘg™dn„¶ÈÖù#Hû ^[»ÆÇÐe/ ÊW7@ôçò^>wñ}Kó_ $ÕQTÜ—tÞ'w9eUœkÚbÆ!rŠ4nž4~G”…ÌjÙg¦éj&;ϳlù:ÃI›©>‡j&ÆÐIµgl:?8@<¿ÁÖaüºñ¦g¯Ä»ö.Ë‹Ÿ§³ÿMŠq¶&ïK(|å ›1´F…-¨·¾}æ¶>˜IR[¯[[rÂb“ÿöaw =³xæ½ôµâ9Cvn‘~.¾÷bçžœßë(0έ†mΓI§IôÑÛíYc!q«ºëf‡fßÍÅŽ%¥"oJÛã®/Š¡×>N(éþÇ—ý.V·lžíC3?«óü¢Æ8¡œ6öÇ3dCèÍ÷Cñ¹UÙ\bU`&ÙÛ2ÞsØUor)ÿÚΛýcè³¾†ª‹ŸˆžQö{±õ;æÈ ®ﲬŽòü¢\Ù ÔgŽ…´9WÒ­âäLâºçqâÐ_|ºå•…ÄÐO'7+w÷Ë9=æcdyaç‰ùÀu÷oŠvó›KIó]†Oœj!µ&‡žk58“ö¯æ•Ñj)±Û¿æ+S Ý6üÌ›‘§DŸ<óßñ>Üûžüý‘ÿ^¸®c»·©‘ô¨vBwfŒ…¨]–,/ß9“Pÿgã i' -xüg ýonÖµÅâù8v`ûlÍ~g¯±ãt.5¿øÀ-Fâ§žiÂ<>ý¯R5Î$¦Mº»,~ôøyëÊ/chï„:Kû´ ¬NX~>X9}®ìƒø¹°ó2lþtäá´U±àÀ»»+Þ‰qã }ÇzYH™ÏºeÅ*g’Æ£¯Úñ|ávS$ÄЋ£§å¬}¬Ï—³ü±}þ\ìgÏùF•itŸ?·&ÅõyÿèY27cUÁÅí-Dš¿êé%3Iûè‰i}û $ïä<Œ_CÝmÓäi¼F<×ËÖ)ì\ ¿^ùäÉÎù±:väãp§+KGž%Û Ï÷'æõ„­êœ™Ï3H­©—ëõDnÍŽ¹Ð#†:žÖ\(;¿Íü~ìÜâÁ™'Úõ[_¸4ÉuÎZq{r峤a‡åš7´ð̤ ⽿ôìÝ[W»[)ä14ÓRlÝÎÀÊž3ÙyöÊçÏUÜïw~¯ Á8uôYCU-Ï‘œ êDI-Âý$ƒœiöóèMùUDo™Ò2†&Œªûûþ_Îa²ý@æÝYwJÀÎbŸø\ຽ}ºâ·s„üz'¦Ê¸ÿ‘½ŸìѤÝÊœee*2Sútgâ ñü ÛaïWØþ;ßYüQ¥±¾m›ä:ßiÄ8çz¦Òƒ7Ï‘Q = býpÝR¹Õ$Ò·ñ³¹‡åýn7^C]˨8Ù;²ýG¶OÒç®dʘF<™?˜Ãbó°#''H™qñF•8Ò{Ýÿ$“„ÛWn¯>;ƒ˜ò]9êÿã@ÂïÓÅÐb%Î íHÙ¼ÇÖYìûèrá°¦’[~Â{R›ˆëcGNÎX×Î6 ï6*ŽðëÁLò¦ä±Žq£2ˆÏŒåÛÝ{ 'WîÒ^O5añ+W¤lý‘û}ßeñý9l˜ëý°ãì:f ‹Ž##æß¨ÚxG&¹}¥Õ¹C½3Èèá-]f]êO ÿ>»ä=}óöQDõ{”}^Ì?ÎÎ{½™º"îµÝ“='å:…qN™C;<Š#ï.ÎK¼ü[& iÜØèÙ:ƒlu™3æêÂþ¤`Ÿêƒž ÖÓ¸ÜÁ”Ê>¶aë_v.‡=g9ûoÕ§£iNNµŸÎ“f©åÞ«3I³rKÍe„÷`÷'Ý~Èvé3D/>7°÷ŸÌ‡Ìç=ËóKÆÓ!Mò‰ûáÎç4Ç~¾µ÷™qçI¸†|g÷ˆ‰å•Í ’¥›h¢^›l|¼IO/ÇuØ;>#@u~ø£'Ÿ§º„;qäãL²Ž]¤K;OøóèdÔasËöÒ _B–_^òó†Î;é:I%R¶îaó1óÌóç>yòçÕëæšW,§k¾-fV'gN¹|°ÞÎ Ç•%½¼œN®¼Ø¿<Í©j)籓fu¹\¾ÛÉ/WÃÖmìüOÑÕ|Õ$dz_Rʇåê‰û¦Ž¼P«bÕðˆ¹ÕñÄÍzfKò¾ RTÞ`Ñ™½édW×JiwÊÿBo¾Ô25MON½µý“×—y’Ÿaû]ü~Z~ÂÎ;9Ÿ/“bœÇ£w¾YO¬œyyéå}oèêtò±Û…yÕT#Hþ?ð¥NÓÓ)öôWJ²}é/ŸÂÎe±çlvÿr>¿ªÀ8Wt)–N¯ãI>[ŸeÆdB ¸“®éäå’'[¼½!ü~¾ž~hÛeÎÃq]Ïæ–KöÇÎß³},G^0N‡YÆv½@r†^Z½J‘A³‚V…÷K'ñtFΧkCIZ@ë7Véé¸WÅî×P6°ýkö>™­[F•-°fjvkñ¼®#/§dÒ„RóÂ/WŽi󨝧7k™NŽ&´î“µWEµ<át¦žVjZ5ªÌ6Xl½R¯ƒîqª‡ÝóÌ®’eoY\ ®×Kø\›óÏ)gOµ°û]?^ oo-ºóÑžN~ñÞ“ð¶j:Y²1Áp%y Yí0¬â'=ÝÝ!ÿIÙ>|Yñ½ÿÜýJü»$W,*nv&³–=Ù°—¿fÄ8$ª‹/:^$3[Õ–ÖÉJ'ýi•¥ÅЦ“ÎÕ‰¥tÞx^ITKÑÓ-祱Ó}¿Ô[Gð§”"z}?®Ð5Y§o)z¸ùáê Ô°E~»H¨ûôŒœ‹éÄ»ñèJ/_§ö¾"ìJ‰%­÷è©bqM¯(s€¸oÀÖ­l½tîüÜÏ¥Š¹>÷zâ}Ï‘£UQÐñIð"óÍÈéd]Ü÷3Ò„¿ñ&W¥ÜƇž&^“‰*ùeÞaŸÞÿ†pžÊEœOÙs„#?'}3÷€b"†õ¯â3V¤“È©³ç1¥‘¦Æn¸‡ß§ñòªv ~ŸÍO.}Œœ(þÝ¿^~(îç°¿ãcï×¹Áõûì««Ml"žd¥{ÕÉéäö‘°Åw¥‘Œ° ‘Gã¼ÉË9ñý×4×Sîó_%‚Äõ[ϲ÷jl]Æþ.fh±C ׯóß‹ãìyتFf”‰\§Ãß>íNBÊVۮݚFJ¥¥½Ts ©6!U^40F|ßÍÖIìsçß;<ç56O;ïëi0ÎÏn¿žÜôÁDº•ê¸{]³tRZs)iÿâ4røRÖºÞµT¤A‰ú%Ÿ¢é;k‰wk‡~9ÏÊÖìý??äxòóY}ž3¹Á8Ü)°sÝ/‘™ëƒ’ HÒIÑ™SLJ#õÚÆ5VîL*”¨¸¦_‘ñ{aûBl_•ÿÜÒ<#<§ŒŽ#®Âú­¾ø|ç(3Œ3nËý¹í—ˆûÓUCF¾O#A©)ç^õÃçöüZÊä CȘ©u­8C[<²„&Ô ß³³ó¬žßÿÖu–KµÂ$íÕÇñ‘­ŠëGn0ά³–ÍmÞ_"÷LY>ãìü@ã‘Fºvû\¾ÿö!Äq¼éªžv/¶ÉÛ\\#¾_aó0¿¯Ý“½‡fûÈlÏ‘›³VEÏÊ%{ä'—ɤŠÆiÇÓH‡ýdÙõÓHä°™þ>[†îs%C¶nÙI[JžÄžk¸èËûáï3Øß•ñ÷Õ‚$N×3òR¼qÛ?<ÅOUÏ Æ©rèY7õ¯—I¹ 嫬O#ǪO<ø4á÷å‡zýFNªnÚI,¨ruí†/ûIì¾ÀÞ¯óçù\m-îÿ;òƒqøs9—É€øK# vWªëò4•<żLþ ­í:aÂNº«2¾ñ¼;—Ëï‡f‹uÍÖüûBW>?§=ºðýõˤlá„ì-ÓHB–»ºëÍTòÞuphëäA¤n©šÐl=­9!âz׊âsZÞû5;ßÂÖí|ðûŠŒs-øf†¥Ø2ô'î/ýÒÈoºÏ%u'RÉÉ'Ñ­ªš’µu2\·ÖÓéç‡à“  ìïuÙú€íﲿëeÿž¯ þ÷Ñaœ7)“öœo{…üxYY®ÿãTÕeÕ¾Ù;R‰"««6jÂ@r6~y¥žqzZf‰k}£Øú¶a_ÂÖìïNrÿÝ!N؈qš­ªSÁG}…ø^3¡Ì©Trõóƒœ÷«RÉð2ÅWŽ|54.WùUë»zz¤R™wª÷Ñ'Ïþ¾„­ Ø:š½—gŸŸ#?gTJËwƒÖ\!þ>+rM*óÈTûñÌTrº{pþ¦‹Udù£I®áoôtÀÎäü{_ê€Ý·ùüd ë©Äû€óy —sV=QO¹âìr­Â¼Æg|SIßðÇíCSɆ|UVÖ82„tx2ëí†Â;éÐyÛwuÈÿeߊÝØ9Oþ?+‹ï+ØzÕ‘Œs¿qÃ'ëÒ®ëãËûÎöJ%"ç­Ô“TòaŸÛçc¿'*¬^+JvŠû(yï ì¹¶^‹‡3ÏW¯,웵ÿÞÑ‘Œ³mä„?Þ¿B\3'gxÔM%\v¯#M% ÷›¶SMª%ûùzé´w\éP€x_`ϳü9€{ÂsT9òÃÍJA½ÝÚñ¹Áõ[¶hÓoÁ£+äeè³í1®©dÏ¡c/~L!×÷ }1Šðë=ÅC÷Ñ-=Ä÷ÞìscçÍV%¶?n )Dn¥Ïlq{+qþŒtáÿùÞ[é{o¥Bo%á÷MpÉí6T#}¥ÿ.ó3|­ÿ.s4„89`þ Gƒ{GCH¬Lp4Øò8`5yú’sAP9õ{óü† Á›×Ó z’«5¾‚ÿ•s7è/¶Ÿà§Q~æ54}â8wçåz*i¤¼Ç‹s7èÜ Î¯¼XÎÝÀúÇ9÷Wá‚ø¯ôáe./_Áååñ——LpÀr}xÕ‚Ÿ†õ#gý”8—— ¡Ö{sÞihz)}Íãåìý–ÇËÙÛð­>¼ßçÆïs£ÆåŸ37º ¿Ù%·¿K L@þ•þ—¬7ù×ú_²ÞäÌoÈü5×›\’§7¹.ãP.ô&·çñçéÁË9¼ÔÀ ô‰ò^^‚ç0or½Ð××É×à'8¹ÞäzÁùê/¸¼Wsw…^S\rÎq(•òŽCÎkÃõ(uêQîìµÉë8 qê=•<Bp“ýk½5™ÛÆOpÛ(¾â¶áüØ!B€}Wë½ËüØœ×FŽP‡Áöúîz|Ãiãì8ü–ÓÆ¹Gù·zrr5Éýówsã÷yñû¼ø­yñß5'º ÿÝ-ËK ìùþw ó~Í_Ã|† ÀCð`ÿÏõ#wörly¶Ið`P¡è yús>/­Ðƒ“ëI®| þy|†¬9ç|UTäûêq½ƒ½à2äz‘›ß«^p5ø ®æø² ½È9—!çÀVJy—¡Ä©9s:;°eš–ë/\‹wšœz‘;÷Þôø¸k˜›¹ ó:°Õ‚ËPÞ$w½'ÿ5ç­Q#Ì& _.ßp_'Vßö^;û ¿æ«áúÇ~_'~Ÿ]þYëD‰ðóZ\r{½|AðøJïPÖ'ýk½CYŸtæ9d›oõJ7iž~éú<žC¡_:WôΞCmžÞÄœÛËÐn/¥à:d=ÔmÀKð¼r}‰ýœ< \_bÎèŹkÇ«Fp4(Gsz™€´ßkóʤ¼ÿsÙ(0ƒSïug—M^ÿ!×£Ô áóf @õÀ]ö¯õ)e.Áeãõ— s º ŽW³SObæÃæ\6µ¸ >/³àÂþšËÆÙ}øwýEYïö¼}N¹ÎrÁÿÍuâ÷¹ñ>7þŸÌ‹ÿ/͉R îyœ^!BQª¿â®a®Ã¯¹k˜ëÐ ‚ûúï\‡¬W»³ës_{äq_'îk-°ss#ŠÞ˜§·2çõ 6¡_{, š<®CÖ«ózy! :Î1”Š^ðr}Ú-‚–ózIàž`^/»Ð§]/„I%å=‡R¡O;s:;¯åYˆ4_ÁYÃz´»!t~ÀüÓ¿î«a¾kæ7Ìë»öü†‚ –õr6;¹®¹û‚à!x½Ü¾á¹öf hõmϵ³×ðkž®/»áû:ñû:ÑåŸ5'Ê„ŸÇê’ÛçåÌ@Õ;7?¢PMN=æí@¢59 7Ä©Ç6ëP!ô›wËã:ä|Ø6 BŒ‚Ó˘€œ^*ÁwÈúÐÛRð¿J'…Fp*ƒà~ Ü*Á]Á\^ @Vï]Ï9yäRÞyh*ÌèÔÇÞÙÑ“×y¨îŸ?°/„0Hðåø‚à@ê€BéÌ@pê;ê_Fpö(¿âìaÎCwÁýʹ+¼äX'O¶(j=p<^Á‘ý5¿³óð[ŽÖ _†‰@ ì@ Á䘴ÿÁuâb^ü>'þß;'rße,¸æö‹é€ŠÒ÷+Næ8üšÓ‡sZ€—àÃþ;¿¡ ¨òø 9¶"›ó†…î L@Š‚6Á1¦v Ähà\cBp·¡ ¡Ð+P"z! *+x 5Àêä%“"4Á‚ùɸ©ªñ^C΃­–ò^CÎã,x|˜×ÐÙƒíÁ¹<€›à5ä<> NÜ:`ŠÿLJy°™×0¯ÛOð*÷«;Âë,NlÎáãÌ@@ëû7ü×þÀ¼Z}Ûíì3üš¿GÉÀø}ø}Ntùg͉Âx6—Üž3`^(СH}A£XC„‚õ À…«Š××ɻȹˆl@…b6 Z ì@Â69Š;Ø€Jð9û9/Q¬à%rö/rNl;P#&Áuæ€ Ð ®35‚arÎU$D%x_eŠ˜ 6nžDpŒ‚ó•sœÙ!289ÎÌ@Ž@WãGRüL€ —˜€!Óäñ9»½¼X Aø4À ”¡HD?` RÜJ`^g,  `ÜGœÛÆÍ“_q qnÇX œ¯V D Nnl ðâœ@"øÍ¬‚ûk¾$æü–/ÉÔ˜L@މ D˜ |AðÀ¤B¾œû>7þ÷çÆïóâf^侺òn6›àgÓw¦09 4D(R攣XC„‚õ ‚ÿ‘óa[RðaÿÿÑÔyüœÛ+›ó?z àuÀ Eï€ ůvÁצ‚ FÎ݆@hóøåG0°qó#B Ü50îÇ``«ÌûÞŒ@†ðhœon‘ºï}ä<ؾRÞû(G¨´Àæä}tö`+4=p¼à…ÐÅ ‚§Và…ê€BèÌ@0ê;é,À ÁŒn ¿x°™÷1¯Û_ð>z!¸±@ÂyoÕÉíŽ û ðB cäþk °e«oû¯}jL& Ç$"Lœ¿Íô}½ø}½èòÏš½„ëÙ¹ÏEi2¦Xªn(R?`(VpCÁú3P põÀ Åëçä®Ô;P£˜M@Ž‚ŠÚ$·ØEnü•ÁÀ ”(xäñXê…ø‚ C4À äDˆ _<pC@Ô‚WŽ  ð@`´ÀÎÍ‘ŽIpâ†òF E‚x PZ`­Æ;1õÀ áò @†+P"l†<~L%‚gR„/Ø€ !4‚è,À Œ„R¬@‰p€ à j›'X#!´Z`œ™ œ¸6 B‚ÛR¬@ɹ4Á6 BÀ@†k¨üšZ`jßäˆ0ø‚à‰@Ü0ø3P`RÐ>Ü?yçÆ¯Qüwî%²y›ÿ¾5÷±9ïïæ86¿ýÝ|Ææ26oqsÖò}ð·æ¥¿›“þÿœ¸¹ÝÈ}(-°sgbP Cð@Aè¸gWÌ? ÀÅ¡n(?`˜{l@%x¹e(-°5ŠÇä˜wB„«ÈPLJ'·(PTzàŽÂ’£°B€sŠ ÈQ`!B‘ù‚îŠÍÎ=‡¢Ød(6-°5ŠÎä(<+PrçWPxfR=·/Û sB,`NÐ+P¢0 @Š9!ظó)(Twª?°/l, h5À ” x?¶ s€6[ƒ‚¶%ŠÚ¤œCØšòNl ²¯V ä¼¶@ú v0°Õßø°åHˆ°ð yü¶œÏ6áûúèûúÈ埵>R ÿÿ\Aú‚ GaP¡@õÀEê,@bÕw¬?°/n,pGñú£ˆC„Bö À­n(j?` wˆPà¾Àd(t-° Þ¤(z °¥àùvCü€È„``„¸!~À ‡¸# ¾ x (Z` &D/H¸!@~Àd’X 6à%ÅÏÜ.?`r„L l@…° 6 BðŒ@†ði¨B#ˆ`åþÖ 4)B l@…p Õ+P ¨!BX} ÈÚ!¸*`2X ì@-¸¿åÿ…û[€›€!‚î L@ŽÀ‡¡÷ÿ‹½óoêÚò½é¢›nZ]´` €iZ¢›ÐL7ÆÑEwGtQ¦+À@,[tÓM=¢ØÛ`Ù,;„ˆ.jL%ðþGçì™Ifî›7åμäû~IîÍÙXZkír6ëg>H~¡P¡˜€'ŠØ/Š‚‰„|CQªÿ·~ãß»6ú«øeýûÏX'ýkµë?k½ôϼVÚ.þ¬þóF0Dó÷Ar„ ¨PƒL@† l@…@1O‹¨Pœ@À±oO´@`>$!˜,À¥¹À•ä®(à‹‹^2-ðA½1ð{3Ô+ðAЀ  lüÙ½€ 70Z B °c.ðçïq }P/ À ü˜ @ŽàŒ~Ð GF\à`5w 8ùû\/®8€êD#£@.ðçïò" ½ÐN Fm°ðgîî\¾F ÀÍ@š Nþ¼5AŽzr?ê(zàj$„x#)ôÀ ÔH ðF‚D‹I¢Vàƒd1êA0°’Çøî 6¡Sƒë¯»âßëÎ9T|wFëé?“WoòÌ‘úå­^qíþgü3ó@§øžŽ ÍMŽ×è93¬Å§än›j0qw|r'oÑ‚zl}ü!X¯ãXŸ~Æéñõçš¿¿I!C«]™ó÷äÐíäìv;öæPý²€×åǬQso¸‰RøZáúÛçI^?ÖOšõqažÂsg¶tY4¯™Ô_ŒÇŒqF“­x]2•Vm¸ôÉÂ:ô½þiÍÅ9¤ŸÄwhMõv+|ô[÷ø÷ní¦:çHýöØï—õMöz1º~¥3E¥¾~üóíx~Ñϵ4©uR)aõMáÃrè³æYÖ€‘9´jFõKáQÔ6àÚšU¡&®Aàês¯³%ßëƒÇ|B§ÄüP®/ä¼Cu$vÆÅmSiÁïYÇ'´È¡öºG–Q.~ªÇ¹p5=1”Íj¶ÏÄÅŽ¾ÒâMÝYëWÏ>/æ£d}Y_‹<}40ŽÐ¿*•ä‡9¶Ë¡òÖiïÕÈ¡¯ß˜Š>™5œ-Ú¼¢áyw¯Dbõi/fq¬_>ëwÃ<¡¬¥»OF…ç¿äµGA©ÔðsÀðã7³©ÇµªEóçP|k¶uN $S¾…1}w›¸|åk÷¼ï?Wò~°ß/ëß9}@ݺщŸùiÀCçŽó¬a{>"•nmj÷~ľl:øºÄ­è{Ù_oWY³"vóšŽõ&nG‡á¾K–è$o:ëwÆúý }4ÿ›ÏMLìw|i@*®)ÙrF6ímr+bû¥l~Ïá4ûeÕzUbLÜ íOšÜ›=Ÿc}PXÖ_FˆW¡oÏ<Œ©Tmnë_vΦ¡kìØ°'›¦m™wòIVà5Y'L\Ýõ?œL{>Ÿcý°˜gõcü€‚·ÁŒçÞøÖeΣ„!÷÷ž(•M-†ï¬~x%~]Ò#(ëêp*Êkž˜¸¢q…š¾/¶€cýµ„~¢õļÊU ý°_ƒÏõX™aœ×#•Z¸pÙèëÍ2ã”l:¼[ÿbíĤ-v¼ÚÇ[&ÊfÍÿ}ƒßV®è*L‚_®yÞþ±ªUñ¦¨LŒ[ïlÕ “*–/<¶Jïlúì <Ö¿²š>5^_ëòtó÷ŸÕêõGß Ö•ùXÿ=Ö'ÜÝC Ç8”|„¥ÒúãÏ´6š<ð¹>¬q¶”OËC>Õ+_ËÄ)<#— ZøGjÖGTèïUHˆk<ïCêÐm¡½R)…/_ßÚ(£ßƒ{ų©CûuÕïUÓãÏÕî§7q–aûæN^3OêK'øž²”;;ÞÖØÚ}€Ôx^‰GÃî‡M¥è~®b£Iç+ÝH|d£9E+Tî9’ªvß“çaâJ}—výìÙ¹R¿6ÖOùfÏŠÐÇH‡ç¬ÒìPÚèTÑ—šEO¯Ì;c±ÑÔŽ'f¼RÓžðé[J¾‹ãJÔ(3 ÖÒ¹RcV¯:ÌÔ\ùî‰ØgXŒgðpÁ 5õ°[•ÇKHpÞ\©ïóýÐJÁ£šÎÄ4 è>¨EžþrfŒshÖ‹x¿I©4mÌWïÎÏ¢›'úU‘/±QüÉe×7A#W}Å{Çýv‰oÌ9Wê“ÅÆc¾Kæ!cóó ¹âã|Ûíʹ­‘©T=~÷¥ƒ³HW»^oÅ}ü1·Ã”á´òÇï:<ÅÏ“7èÕ’qs%ëÃ#ôgÍQî·"±îO±ßTsb¾/W¼':T•w¾ØgV*ÍØS¹òݯ³¨ì©¤Ì ˆŸUþËûæóÆ8ic>·/‰xçÛ[FÏå •/‰üX_êWϼo¦„¿“¢Á¥£—ôzç-yd\ñŽq2>‡6 Ð¥RT©ùµç΢µÎYW7²Ñ&Å–»½&ní²¤œ_³ÿøÜX?®ó?¬izp_ŠÒ¼{Y…œ›$ÿ3‹cW`œ,îq‹ñsRÉÇsM¡^öë”ô¤jŸ¾¥l”>¢sJ…6#hG/Ò4q4dØîÉ\i>gqÆúzçKØÑ#,"Ÿä{a}®\ùqÐÎ7µ©”ºÉßÞèÄu:ºc­‡þYÍßï¸>i¦š\zj&®û¥Ýa%,s9æ¥ÉÛ'é’2»âþó·çç'ö¹º{ÉtçòÃѶ٩´5³±õ™?Où~ çS²èd™+w–¤ó oZÚ¿ˆãø®ú¢æJþ3ÖoR˜_({?lèîIÌcáÊ<¿KT£ñ¿­H¥öÛò½wÖ[ví›E¦*^WGÑ@ã­Ö8ÎZ*a_t™¹’¯€õú'?PÚ›mØ©Žú½n´¥tZÞºjÆ8¥šð«SÉèi©»ªýu*ØjEýúó²èvíÓ6ÔC[l?øŸã†<+4î»Zs8Ö¯ŠõúY>’üò,oÜûÛ1NH„Ç… Ó©4€_•¹NËÚÈÚ Ê"Ëݯ{–K;[¼Õ瞎ãÔm÷þlx3‹c¾ÖGP蛣ôQÌþf˜ØG,É¡òü~Sd³Ë©äuX¯K¿wxëoÙFYtøÚæ"ãÚ¥÷²·pqÜü´ÒµuŸÅå×+Kñ+ÔÁ?(Çs ÞŽø}Á/øýÖ~Õâõ‰kôË7“¿ÕäË¢VïI^¯ÆÐO–®K·ÄqË㾚px‹ÇUÑ_‡ç¾²úÃàǨßüÇ7ì½Í™c«và:=ÞühÞ´¥£¨ª«œš8ó“­í_)õ9c}I™'–õ¥có°ûü¨Ã8KKð íSéCXºþ¹âm¯VñpÎÊë”]mÅœ©%G’l庚¸É®‚1Cê¯Èò9Ÿ¡é¨.Ý(…¾ý%Ä~Š<Æiy8ŸM'»L÷F•ØsÇy•:÷Ôܸ7ù:åïXwÀ®‡#èb7ùð¬¦&î}±Œ]3gˆóQ=©O#›?™§‰­“Üûz›1ÎîÚÝÛ¨}™oÁUzZ«øíÞש@‰zCo—ò̵L÷ž%y™w€õsg^Læƒv÷NÙ1N•íÅæ-n}Y\^¥Ê‘a½B¼¯ÓÞÊá[~*D?mì8[Ç=ëz£ÁŽõufýcYŸ@ÖŸ–Õ“<}õ.:TÅ–éÙâ2•Ú!×Ξu•*Œì±¥Üu*ÿfdž"Û) AÁÐ¥µÿ˜ÇÙ<ËúÆ3_ófêê©äéƒ-Ç8Ž^9Áç&x¯RÕ´—³Þ\£µ¿É‚´åƒ¨â²pŠ“Çq5ó—úéÛˬž{Kûæ9i± yó)1e¤ýTïÆéú¦ãúBå.Óšï•Õê\¥Í?êW,ϾF#;×KÛD¥7[R }ÇíÙkÙw.Çúœ2?”çE¯nÊë-½ç)VéÞ¦?iéßdÒ‡´×¶çÈûÀ=¦³ß”Aý]}ú¤QÇ[ìv?‡cŸ«'lä1üŒ_qñûo§¿³ãÔu5¤N¥ìÙU&eR7ûÐÔÊ›¯Ñ>WùPÓ¡§‡[ÖòŠãÆm»¨H;5›ËÛw² m©“æâ w”²;Æ íø»èk•gbÀ8Ë 7ÀÒ;•Žüš³t>“ŽÍ3?5ó}ß¾æw稩À¯ö½>Är«T§öˆŸÅ1/ó±~9ŸÚÞ84À©ÖÓßHû~Wþ`œÉû n½ûÏ|‹J× È¤çjï:©C¯‚õ+T5 ¢Lyiå:¾l3ýäð™ó±Ÿ‡åë‡.ø%óúaì§÷öá¹ú¤Òg~¥’IÃÇÙ[_£²#5UŽ—IËÍþ¿šïÆrB¿o-Ç|3Â>³Œ´ßú;Å}—wÞ~âÔ7¾ÝsÙT:uBË:Ï2èÓô‡)Ó+\£‹*Ç#[ÿ‘ô$;l㦂qÜñiGú®šÎ1ÿ ûž„uð)ÉÍ꨻OSŽqŽúæ¬TÞO¡ö•ºÍìw2ƒn”¸Qoþ‹«TbÐõŸ‹‡Œ¤RæÂXÄq!5WÛ»%Gr¬'Û— ý™¯I> 6¿»÷-Wa¡?t -ËW¶·!*ƒ~<ôjÈÜ´«÷rCêø¦#)Âr9¸€2Ž«£Ÿ–)y7X]Î ë#-xøò~njŒ3 nòÍþWRÄþÔôäö÷™Í÷^¥Uòu£ºßQ“ßβc§5ˆãïÃtÉþ'a>{&yÙÿîîïÑaœ_Чëz"…–Çüâ­Ì Õ†NœÇŠ«t5éâ/×o#Š•Þô6–k×rCÕZŽå=û•ùXþ¸òÏ5?XRæÜŽjÓí`wSå šÜ6îÓý‰WÉ«âšAFRÉ]%ûtáb¹’ƒš­›{nÇ>6_²sw/¦Ï=ñít„t ­Ž»Wdß‹tÚ½ÑóJ~_Ì3)…2¯IŸ›¿)¹,–kß{Ü©½¯fr¬+«OÌ“ëîµã¹ŽïÚ!rRȯÊÄòkRÓIs]vˆ«u•j÷Y°&èÂ(:~§÷Üå}b¹Î÷F¤4Êš%å{>ó¤2/—+þ/9T?-ÚxtÞØRlü1§rl:}ž»¢|Ý™dì£*7òüh*ÝIufQ±X鼊Å%ûØ9Á¹ó2çKa].ÇsƒßYükïš4÷£±ëœtÒ,]½×ÕL —U+úm Myðaçó #÷môÅúY’w€Õ#!ž(Ùþ‚õ¯uÅ9žÿ0³áûÎ-Rè`¹öû§Ó'Û<çÀ™Ôëß³,cIï¬0)Ábä®E–›ùj†äscß#ÛgÆÐ£±ÉX¿öÆä~.©Æ8ýùmwÙºZÿøùzÍÓ©b³'Þõæd’~aÙq9ŸÆÒ‡‡®‘»0“O”éR¿_ö+ó 9ªmH-YŠŽO¿Ò7¤qžu‰ã”™Ú|W‰;Éô*¦×DMtzöàxÃ×ý2é¸SwoԸ»Eµ5(– SéȇŽå «GÌ›Áú3ï¢{ý6`œVƒGm]w8™üª^{¨ºn¥¨Qz×Ud’úç'Ck¾M Rgý8á\,÷êµþÕ„)>YÝž2~gáÝò‘àÓhEsÖ´›\«E}a~Àó[v{lš¸ ™ú{âù9ÞJÚ^ŠÍ—)Î;}nÁ‡|qÜn:ç­¾Á çX¤z*ô-¾#ýzþµV÷<ýÿígFÁÖýú&ÓžUÕ–Xa¥¬ GÍ~þ5ƒ<{?°º š¦¿¸äF‰8®íÉÕ”ú#³ùAX/Ü‘¼7°Šnׯ?YlOX±¹­É•Ð'=™êPUh=ÕJE\Âë íØ%¹PÕ h”SíA,×õõuó࢑R_~¶ž÷ÆÜwê>xnåeÒèåì‘+KÕΠ¨ï§ytï@7ïsŒÙiäæ9ßývvÒtŽù»Y<1o¼°®ÏG[þÚ¢ìO¥~ó®üÁ8ÃŽTøb¡2mætqè …­ßQfŽ,ƒ.}ÅlI  V.†‘Óª=nu^)îwëKû¶/ežJæµ`¾Wþ¤8T‚WÓBÛ.ïJ»6ø Ÿùµ3ûi:=âS‡Qê1åžÖÜÝ…[ñÿˆàXjÖÏZòɺÎ=Äs“fÒ¹—+0Žð¾ÅBX4“ÿ M¾<µlf:m]xîC¡ŠÃhUÛ¶ûºE¹“¹ _î!y¡Ø¹›w…¸{'ùÊÙþÞ•?gõà#1;ªYh¿öÑ÷…]¦€ôåóº$¤Ó¶¾‘wœ¥{–²ÑȵLØÛ¤}ÓŽý÷ì¼‰í·ØûæO`ç’®üÁ8Õž&Üšžy‘zõã ×—©[ãÓß_Ý’Ný.Ùª J¼Ýrüa#7õãÒäÅ-#$/[G±zÀöÌÛÃÎ×\ùƒqj&W:¬×]¤Ò[¶NÝ㜲S/J§o¢&'Í÷§*¿VÚ° ãÜ®nïQ|CÇ|Ì÷.|/vÉ×ÂÎ?]yƒç»^4¼HüiòøÂ—éfÌ¡_úLN§Ú‹«'ÅpC¨þéi³ŸâóÚòêÌó#ã#ÅóšúbùÚtíc§lãW¢ßWôåá¹k†ž>ÿÔšDwðöT »²ñS«AX„÷^ã¬7„¢^çÏœ`ä˜vÜ™8f:ǼÌÓ%œÿæJ>WægëaWž¤0¿g…„ œº}}*Õ¶OÝ]™N½VZÓ®>LÌC·4rŸž?ÒüÖtÉÆÎM»ì½°áîÏ÷•Åìgw];è)y±]ù‘Š}[ØÕô–I´aȾc—¦Ò¦;ϯŸNã:6~ób0Õv-8Œ\—>#Ò²OçØ~–Å/ó ê6¸¶º¨ô^ˆÍ«®üÀ8‹÷f'÷ü”H}º­y£d*½=²ôÙaÏtº| ÒØïç¡j† ˜×ÓãSƒi½Îι_Á9%óM=Âìp¼¾äÃtåÆ)µ¸ížà”D*{Ö'!<û5¼øÚJÆÿ±×Ÿ-}²°qñX®Û£ë2·†JùÎöKÂþÓ"ùåØûU÷þûjŒ3§æÝSŸW'RJ™æúñRhñìRoZ©ø¬œ[û/Pñú>E_ÇzØ(Á¢ß±¾ô’yÉ…ü”Ñ‘Ïù®ï ê,Ö/Áÿ£Ã8qO^-<á›H¶S»¨Shl ®ìŽsVJyœaõ{Deò-ñ3Ž‹ã^MÔeøí–æe6ñ{WY³”¥ü¨»ÅIõóÄë•"Î „<Á8ë£Ç×Ýë¼@®t¯›BK òñ~;¬TÂv!!ý¨š\oá4q\³YÙ‘9UB$?ÛÎÞ?ÞøÍ¯Õ¦Æ÷’”·•ÉûgV}s½8 ¾z"áÿ/¼?6cœv÷ÒWÆ›.P¼ÿù®Ï&ÓÕÓ_é¬Ô±Þ™yÑ FQ§#g—:²b¹ˆäUGö áØþ’Õc6¿Ì ”<~†Rò¤»òÏ1'dç·Ã.ÐÛÈž¡æ}Ét·áÉBǬ”ü¶Ùò×GQÔñ˜‰'‹Ærõ‚OÝú(XòDzó ö>Sv²RŸ¨iEÄzÒ†Ø{lWÞ\v¨š¬;VûÇŠÈÔÏ·ùîéÉ´z^ü–6-­Ô½ÔÅ+ZŽ¢KCm™¥æ¹ÚY½ì_,½‡d>uaßžª~źìÀø´±óž—Ë1NxÑ%7oß:O_Ý S·é”LýŽÔ_ó®ŒU|ï?’|wä¯ÿ³ÙȹŸÃÅM!󯱺Ïê0ó“n®¾óÌËåÍóz&1N«ÝÊ!Ü'—>®l2Åñ&{å¯CK¶¶Q‹çn±ÜéþÑo}¤ó\vžÇ<)Æú‘s7—~§â⛼ûŒC‰!ök¡ç©µýåyÙ­K4±pÜ«üÛÓhQH½À¹ëƒèµfWý—«c¹Uz,..}nyï'ÜTÞø|ô—ýÞ+™¯˜ºòãll2´Â­zçE?Ñ%<|ÃâÅÓèYRý¢_ Þ†|Ó}p,—17L‡Iç^ì½óc¿ ðá£7˜n6Ì30Δûú«.£,Mkç)—èĸ—[µN£Ì|Ÿ¿ÝÒ ëÙ®?ö9xÍÈMôRÖŠí*ú,‹HëL¶¾`ë?æÍÊãÑÃ8÷µ|&œ#å>ë~«|‰ýûö/”F“ÆE©Ø×Ÿ&§ç?¼wœQ¬ÿ!ó¹°}ý—ësv>Íö ®üÁ8ÇêûNž^úMl~gŠ1ËBídän:F+åÖ [°_Ù}5!Þ‹ïÇçÝ×`œéÞ£¶y,;MkÛæë½¨äEú^qxχ‹©T"-vn›RCȵœª+®oÆKïÙyËOá}…,Ï~S…ç[¨ETé'§hè”O#"î&‘£…áæŠM©ä·ðÑÞ Ñ«ËmR•Yî©áòú·<¥÷ΉëJ5;¾§¤4¯¹ïŸÕG;ïÔ½½NQÜPÃ/5“¨ì÷~+§ÒZ~yXnU·ÌœøuºµGUÏšû&‹ûôÂRÝηî*i­ÿÞ¸2é}œ+_ðüÙÝj}³`åIÒÔš¬iL¢#¬òwK¥o¯,ípxõ JXS½ê|­‘>ßÑcãÕ`ŽyØX`ß;óe¹òÏÍêóâ.9'(tÚÜó]&Q}£áçžURéñÞã¾›3˜Ì3Bnï®näÂk7nx;™>¼¬ýÇH K<ËVÜ(­wYÝaûá|ëtoƒÕk÷uˆãl³¬™Tïí,߮ʒ÷‰bvšp4™F÷¾ý|‡š™SJT5rÎV¿g¯."ÝCÎÑ®+“†¬L~!z‡­Ì§}”² -œôÌ‘Hµß}+¯™L¡ç¥ï™<‚úÖ‰ž8®‰‘kó´A›úo‚9¶®eû$á}Q¢R14:­Àñ~ž+øyP?Cß»=2©éÞö­k·I¦{o¿?ÚëV ýß¿£‘›ôâ‡káB¾¸7UOzŸ"Üÿî“™ñÜy\4V|GhWBÈòwÇé'ÕÃAûò%Óð¯OßJõ>¯åfaþÞO…H¾Gáûò‘<™ì½‚+žñܧcž0©ËaÚVè§S«w$Ò¬g}¢’­—È»ê€qm¸!ôjçèu-ŒÜœu³Ÿ|í*ƒ49eÅ–ÜæbÝx%Þ (-žƒåt‡êÝ›}GÌõ÷F3—U«5ãÔúýM„ýÇé} {#ü\¿(…÷õå¤û/îçÕfŒs«Èƒ‹ûÏ*3%‘jr– Š0 ­—[î÷5#SUEn%橜`ÿ*Î.o$]·‡\#7*;µæÎïB¥ó*v.ÂÖ#†f>¿Å4,+ywÙ:Ü•'ç»èž»Žn6Ñ•Ä_ßDžwÐ> å„ÿ¾®tßDøÞÓ”G"[ÑÐâý\<·{ÍK{nÞŠ¥ë‡”Äs;-Ù?Ö±ê"…­õ}ð¤@ÚTì¹ï©öFN8ß “îçwsM r)ž\y€çUK¯:]ÙHñ–ý5F!ßL÷F;ø"UM(€)eÝzòi§0£øçÂ8æ¹gñÁöŸS\Ú壸^ÏH‘tf̺ûêõìËø>…müEÒí^\*%~ýÐL™:ü¨‘s-#Ö„reî´ÜÿhO=iÞêÏÏJáü© çxîÉy»,1ÿDÞJË—HïØ3š=H¢fW~]Ô©` ¸Ï7ŠÞÊŽÕKö>ƒÝ›m©=3heAi>t?o4cœÆã+RÓ@w* ïñ"0‘~˜ÝêÔÄýIô¨é®G+R¥ì+oÜ1rkž”™¸x`ÇÞ# ÞÍFÒy«OÂ=Ä’‡Ø÷G;{V–¡éêp̺N6ó¿Í˜žD_vlŸH£J?]ˆ3Š÷çC8v®Ìγؾ|ÎâùÎbÕŠå9gôÈt¨*š˜_ `­X`þ4(‘Þ..ñºÖUƒø:‚hÿÓ§ëïꌜk:m&g²y“ÕSáýÏGÉkモcœ¾íÇ—íÝ@Û<œsgR"IÖWþ9‘Št¯3ö~šJ´[Ñc^S#gotâõ¦¯Â9ö9°} {Åî»0¯0óD»âãœâ³Þþ•ø*vNؼDzZ}Þ¡›˜¯ûW>rÀ2vùUSq¿ýÃõŸ¿vнT¸´?bçLB=û¤dç1ì¼Û•x¾à'þžfÿ8áỤ̈ï®Ýºj2ÖŸ¯~Xb aQ5;ùþØG«0N¸W\Gº§ÌÞ0Ÿ={?â~Y‡qVÎßQWb%][û®û¯1‰â¹L"MOì4úÆù1”^èu“°1ÜÑlÿ¥3ÿqÎÌò™Ý׬Q²W¹a÷ß)ÝÏý x~|Èï'•a‹iy«‹Ç{'$Ò‡j†]¼@Ï‚í3¿C…&$mø$†s]+*­¿XcçWñÉ«z«4´®°Óû¨w^O6ÆÙÚ—4 ãZL<ó­2²¾t¿˜ÿ³û‡GúËî–íþu^¿<Æñ (=¢Þ25'„ òªCöÂódk’ôœ£inÕQ§î2rKÞ½ÈÿdGÇÞW±ó0vÞC³î&j~’î» ÷Øï·ŠÿyÆè³ œËY3¨ÌÔæIT>Ñïå{Ÿó”Uz°ÌÏ9’ê»ãFNwb~þßî†IyÉÖ3Ì,ì×Þ*œ?T°r¾®$ä±pŸAqW7›Þ¤æ<ŽŸ¥ï™DKæô:¢~yŽ .Ó>Ü{$i0Ë´aä&wèì3.ûy‰Õ3ö>V8Ÿ«\Tânö½a}Ås€öBÞ`œsÎ.?³`TÁ”h‰:yÞr#)þÞ/Öæ¨Éµüïjäú:^cGúG~ žâºÒ="!>)ÙûDV\ùƒq" ýUÿäR.7æ»”†““èÉ‘G‹nM9G«»|5Ýc¦šÂ<ÃïÂüðòNÓ&¸;Ÿgô%—ΙÙ9¦0ϼR²u´û=A3ƹ|eûô†öU\·âÖ£›"’hÎ/-¿n|Žršqwõ_©édɉܑrF.z‡!ƒ7…K÷ÝòÞG|¤d÷:ÝÏ_ìx~HÛKƒ7 XèuaÝ!Ì+, =j>=KÊÒ=¢zs#H6}üÓ£Ø?Úº¦¨±0Bº'Êþ›_OúoJæigã¹òæšCնοæmÖq÷¬µ~~77‰z>+Þ™öaœ£Nu,­¦ö£Gsc8E¦&´YáHiŸÃ>¶/îç¿ÿÓ{ÉrŒãkýꘘ ÜéAY?mZ“DûWz²xÊYÚqïÞ NÕÔÔrN¤9"1†ë³©p×—‘Ò¹"Ë¿{-Vü&6IÙ~—ßÚ´[Ò{÷û*ŒSlÛÞ… OoäοxðócE¼®\øv£³ÔRWöý«jÎyb¸Î«½0uGHï}ÙýÄÄ7Ÿ+Íþ|‘û÷®Æó]¯ô›¹ÖDøœH¢­C‡•yl¦À™«W ƒz¶M>#Ý+ò¿±óKáû¿¥Þ; ï•uxnÌíÏ«L~[¸Ü–/޽‘D÷?¼œût—™O¬Y6­¶š4…ï™ú:†ë¦8º ¶n$Çê›OýCå’íüZBz?—ç> Æ©rþGCïRÛ¸-'^ ÝZä"­~:©Ü/ÌT}ueÓŠy#h‚~Aœ¾‘‘KZ’r43’cëq¶Î}²•ì<Þ•xnØÖM³µnõÍàÇ[^¤Y™ßOih¦£ò[ªÐpz{Ù§d?#÷û™y/o_˜þ/Þ¿²sVv¾âÊ<·]åju÷_ù‰S*k¶›6ú"}—}bÙ¬ÛYîΨpft4]º=ÈÈÝ\ýsÓm{´Ò}§ìœ¸÷­'÷. Äýu‡*ÿ7?„ìàn=¹Þ¼2ÖáU/™7•£,%ÿ'ˆdùÇ8lÜ+ã1ªTrǼïìžµPî*«Ô¶¼å%þùU’j±Oó»ò‰¥©!8 ™›ÆÃ­‡¤èìr÷1ü#ýuíb]“˜”þbIÞMãÌ@!öصŠ}’tbŸ$¿/œ ¬Ç.swiEw—\ôÓ¸û®=Ü<†Ìwíj$ºx#٣ń×+ð}×|òk€øˆ=QXŸÝx±W’ÚÍeø¥óšßðÎë¿kåßµRçñÏW+eâÏeã¿?±w êüB'O7o» ÛGt¾Úÿ¢ç¥ÊÍù l@%ötʇÖWÎÝqhwëGîÞÓIäH„(`û;é݇¼ÃF†ä¶¿è÷¤B˜€§èò²‹.ì/ý5¾n=å4¢ ›ù ù~äÁ5ó3ÈÜúÉ©Dw{/ò¤§&ß‹ÜO!ô‡â{‘«Å~r¼ŸA ,À[ì­i]ØQ¢ Ûÿ‹~ä¬Ï&ó×èDBt4¸;_en./æ|å“\¬ÀÉn2$|0°•è|•!ùƒ ¨P n=7D'¶ÆÍçõ¥÷•?佯ÿÞž›×Éÿþ:ù¿±FzŠÿ^&öÜ´ˆ=7õbOr•è°áؾÔx±p‚ØsóK‡ ëIÎü¯ C ‹=ÉyסöOzÞP‰Nlæ:twØx}Ñs3xŠ=ÉÝ]‡Ö/znþYOò``*Ñïåù>l­[¼h1‘4¢çïGn¨!¸½˜«õÍ“Õ\ØîŽÃÔ]ã%öÚ´‹ŽC½Ø?w5賡à®á]Øj º°£Ü‡îî»›»†waû‹®w¬›Û‹9`½‘ØÑbrk€ø É @ææ€õA€ I,öÛdþÞ‡­û3¿×—X?<°¯#ÿg×Çÿ­ëH/ñ÷mç¿§È `û¥3§»'[%:aÒÔ|Ýœ°Z`¾lƒØ?÷ š€'‚\ ìÀ÷ ¢Cì¥îÞ4ø# Ì@DÐP‰}Õ™ ‘÷Úx"9´ÀTHðD¢høâ‡^¢ëË!z²¿tÚðDð{ÛÜ<ˆžH,m Áóż |_b-°‹DÞiãÞ›ýé1Ê÷f÷GB&ˆýÙy¢Yô6h€øˆ½Fí¢/[/ú²y¯{¿vÖ{”ym¢D¯·ènpwÂzº¹¾˜V†$6 B²›€'^ ìÀWtÂz"ùµÀ|QLn}HÍ¢3;ØÍ÷õ¥VAÿï^Øÿ)uòKüßXYm”ðD ‹=Û}ÑbÏv_Ñiû²ý  ÀKôÂzý‰Ó†õlgnXðDkÅží¼Ql@…€6OµدèÊfDw§ ß³= ä¾x‰=Û݈¼ÓF…d0Uøó~íZ`¾¢çËë/Ù:`*$ŽÈx…è>ä{µ›jž/æ§P!¡LÀ³–àÇv÷þ£Þ{%öiç½‡Ñ —ÿULDµè±áýØÞY!ú±õnÞCwÃÍcÃû±Õ¢ÃÂÝ «ró|1/¬’ÙdHè``*$¶ xºyaU¨ƒ&à‰d×››Ë†wdûŠ}™™ëËÝ ëO‚öïuãÿ¼šøÿúQ!þ¾üçà4Oh0°ˆ=äµ¢kÇÝ›í+:bs/8x!ˆuÀüܱ:à~l“ܼ1x!ÈuÀü¾ð!²¾òf @ð먑àDˆ¹ÀWì/Ï|ˆñÀ É¡à‹$‰^Hp?$L‹Î¯ÜÊ‚?ûK_ïBŒ^H&­èÏf>D/$–®†àûb> /$™®¦àÎöû¢/}.ðGâ™ɧN FZ€71ZìM¯Æ—cûÓó.D‹è³6 B’êE·ïÏŽVÓä~õÑbòjDÏI¬]?>¢ÓÂÝëåæübŽXO$¹Ø/’=x!áuÀüDG¬’_ÀE ÈP‚EçïÐÖºy¿¾ôÄz“à‰ýÛùów­Ôyüy­ü¿©“ÿQ5’ÿ.ãR l@…À4§èD4‰AêÌ@.º6x_¬üO¼?^`›/6x!˜uÀ&z£€ø"¸ã\ÀOôh3/¢»÷GÀ×'P#,@Ž$Ð~áEä½?¾HŠxà‰ÄÐ;ðE‚Ä/$‰8€’%Èÿ¡ìÀIdž¼£Ct"ú"¡â—\ðvØ/’+žwÕüÙî>ÄÔ÷Ãûõ Wô!€StŸ€ ‰©?¼C;˜÷xˆíh7'¢»ó‡wh3çïÐÖˆnwg¬o Á‰Æ|±*$¶ x"¹µÀ|‘äñÀKôÅú"Ùã^ìn¾ÞŸí‡`ró¡¹ûb@M‚/öïuäßµQçñÏ·ŽôÇÍå?_ÞѼ Z` ªNô¹;µýDw¬“ÿ{p#ˆ£@.ðwsÇF\àÀŽ²â‚¿1ÈäQ ø»9y?‘¨øàà@¬À‰`Nà‡„0‰Iá€Ér’$È‘(Q øóŽG ÝhÎÊ‚SûKï}Lr$“ÎÍý˜äH¬¨‚ÍOŽäH²¨š‚S›÷™ §N FâY€7’/ZL@ °$¢ALF °¹è‡´’S ìÀI-ºŽx¯¶Aôj+ðA†É,º¼‘ÄÑ¢ÿH…dŽþÂ+ws£1w¬’\ÀÉžäHø( üEw¬Ér?Š@ðD!Њ$Þ­­só£}éå;äþÏÿ™kåuüŸT#ÿ;ëã_ÕFþ{Hr¢Ø/Ò$%/ 8µÕÀRm!Ái«@°ê€ø!h€åæµMrq°‹ÞH=p?u#°£@.ð½ÚÌéj»x#à£Å ×+P øunÞHðC"$/$ƒ8€’"È‘Q ø#AÌ@ñ>m=p?$N<ðBòèD_¤’(ßcËñÏÀɔ䵶»'RäÒ'P#É,À‰-&›XEOd4pŠžH“˜€¼ûÍ<‘ˆÁÀ"º´µÀ*º´ n®H+ðA’D—¶ÉjÁHXË]¿‚ÎÝ£ë‹:¼Ð:à~Hì =ºà‡$Or$zpÞ$&½?ˆ½pî]¾hHðèþ½nü{ݨóøç[7úŠÏuòŸï­r¨Ø€75 Ø üáúæ=–þ…§.¼þÀ b=pµ›SWœ@ÀNž¢ÓÒ r=pµ›×2Z z °¿ÈÁÀTH“˜ þ Èj` $‡8?’Ä ¼8 cÞHšh1q4À |@ ý—f @2E¹90Í@ÄÒ'ð—ãß’L_Sðbª‘là„‹“N¬ÀÉg2$`0°ï2$c0°…èÇ´o$§8øz‰$5§è7O$¬Ø€ ‰kžH^-°$±8/’Ùð…SWÄÖ§›SWŽ$¹ÀÉn $¼8Ztê*üzàj3ðB!Лèvàû'^] ^]>wø¿þ¬V²ºø¯ÕAVÿ#jÞt}ûïªm¬Žýku‹Õ,V£¾¬OÿžÚÄפ«ñ?»™ÿ.ð…GðãïÎàK·òweP[rù:ƒºb Ô½èË5£®(ø;Õü; †(PCôÀ Ô¢ŸÛ5$ZœD5À |D C —\ܹü¾ÁdrTÈþ,3P ^è¨yW.ðþ w.ðG­HrÔŠ( ü˜f GpF\à 5E-Á¿íj¬x#h£ÅÀÕ+ðA€ A lÀÁlàïL#˜½ø{Ñ¢_Ûdè``*q2×xÿáÌõG¾›Ýœ¹~ü GðG\à$0…èËÍþH3P )|ù;ÎâÆåÿ$^0 ~Ü¿×A¯ƒtÿ|ë ñ¿ãS Ì@vàƒ@ÕG?|ÞžZ5ˆ2¯X€7‚8Z d °t´Ô`^î``Þòh1Ð5À |ð CÐP!øMÀ  và‹Dˆ2$ƒ$O$…X€7’#ZL5°oÞ÷-&‹X’ÆdHœ``*$ x"‰‚x#™ôÀÉÿÙ9$•x#±¢ÅäR ðF’E‹‰¦Vàƒ„3’.Ø€ ÉgžH@-°_Þ <‘ŒZ`ÞHÊ(`>HÎ(ËÿÙ9$©ILTÞ¼°:`¾HÜxà…äÕP!‰Mb"û“˜Ð`ÞHìh1¹ÕÀHr=p5’ݼ‘ðÑbÒk€x#ù£Å  G!ˆvÑ#®à‡Â¼PtÀ|Q$âIÈ'1$]5òYû¸×¾¶õ_Ïþ+j_Ǿ\ïü[kÿè:ôo­{øß·äòwâðåÚø»¨N¾Ž vX€7êF´8ªxó÷~ùsw‚x£ND‹“£Xê„ÈP'‚ ¨4&à‰ÀÑò÷}8N~¯„à±Hœ|@ Y€7‚)Z ( °–ÈP‚¿£ÁïP ¨zàjŸ(€zàj¢x#£Å€Ô+ðA`€ Á l@… 5Oª¨ø{¼T9Wê‹@õD jø"`eüù'9 œ@<¶rXr?Û n=p5‚ܼèÑÀ Ôx ðFÐëÿA¯và‹à|× ­ÐqÀõ×âß˳ª|“U‹B÷I}àãE,7[¤~ÕÃF¾Þ9ø1/]“A‡¼g%Öo ð«s[Š×6Jý—NÎ~|~æŽHÎÝ«Â8 ‡h7,wîãJ.ï—•óÁB+•|æÜqâBy±ÇXzÇë¼:¹M‡wžèÞjºäÍfýïím©ÿ óÅçña¡ŸÃ~îC õÞœ–—(ú„5ß³¯Ž“!õõä¹£iDçàÀ¤ÞFN÷¡)«å˜?‰y¿Y_mÖ×–®Ïmxg{@—xnõÝ52¦]¢‹ò³[Íß%ÇŽàÛ~cF‘ÐßÇÈÉ­èë÷Q+ùÎX?_ÖׂýþùçðÜYÛvu*p žÓœÍîï»Dã×j¦6|{ŒžnLîþÖk$ÕhrÅë~A#×è— ñ?Íà˜ßŠõ/gýzþ{çÕÔ¶í}Å(–Øc **v,dE±€5T$vlV”€ ;*"4J€PÔ¡¹bǔތ="*XQ,ßÜÙ{mWÏ;ã{ï~ã~ãyÆøïðž³V²÷œsϵöÊÿGÏÃðS`Üí¯(CàsØJ`¹jTí¹2Mˆñ0ÿ«—ÕðBsX-Ã]½òü‰&~ßä¾Ó×á‘ tuð·Û i¾”Æ¥}Îaé„õ«MÍo"­üÇx¤I<–0ï„øÖ._‘ Ã.[­ÆÚ{bâ÷ôfÑ¢mg¾¾èûäh…5r_¶{§»ÿå÷Þõ&²>ÕJXgC<ËÅž×ÀwÏ´ó2~cWÂÁÙž˜pª‰ŸáÓ¡)«MÖ§‡øjêâæávšñÎ}F n+Lʉ»‰–¶îß,¨f<Sçb¤ÇGTÓˆÓ~Lœ ßñï¶cºƒ'ËG!þ$ÄÏ´Àô™¥á!&®aÜ”³7·ÜÄ1Xê"U ùr…¾üîŸæw]r¿±úål”Æ‘»ÖŒáœ£¼ý¶{²>rÄï•pÐõýÅ0nrCá’&]b±ã#Çž‡ÜBþ×¾ ÜQû<Tô`§¿«í°¿¢¸$Ãv½ðƒù×Ä?:ûû…¼h»OâH|Suq óø}uÚbC,Þ}ÇZyÜB÷ËÄ!·e·_m:ùäu» §Ï ßxÓ“åÏÓ¾HM?•8¹¿ê/žª™ß½ Ç[ óÔiÕw»ÇÅXbLìzvk7A<ø´'ë¯Eêá…>Áò§I]ŸÎrtñóÖ~Ùu¥_^|å¶`ï¤Ûh܉KñÅcPªs®¬D0µ}úQ<ÙK†uXM®û=ˆ¿í/úPð#ryÆT1©;ÃÙxÖåE®V2Ý(`k«ó˜p‰„ÝžžƒîÊöu{&F%eåå›exòÛˆcŸ¼ØûBüÇg>.Õ²9"ü0â£Ë¿ðüf/uìylT«èFøõÛh´ß¡qI÷Ï!¯]F_L§‰‘¢ýÜM[ÈðÓ>.K¤_¼Xî‡øDn.2šSƒáÏÁ¸ÓFïYm<*_mkæòå63Îx@ð”sÈNghè„,ó ‚Ž+dXUÓ¥ðΔ ˜Ôâ£Eü©: åÏï ¥}MÅ0nÇzF³®Æã‚Ñ.ã“zÜAw²[/ퟧ@RCô]GTÏ~e³eX‡ƒQo`ë‰â_Ôš*q•çr÷IÀž©OR—ÜA>þéa{(P½„>‡êjf±õwKìÌ[ó-7²õ‡\ï·Š8þ¿®âÓ.…qïïÌûÞM•€bÊ@þÚ³¬¼KçgQ'χSÂf¡71Në’ÞËpʾõOod¯ñÉ¥¿ÿÖï“ðõ}2U0Ý¥&ßm•øøùüžý²ï šoŒ²ýqáãgz¶vD‹¿÷0,—á|U[ù”vþ…oKû¾,l“;¶Îºï,¥ ïæ¹9,(?U‰ÍºäÙÙ|¹ƒ²^¹ îÈ=ƒL_%*ÆÎFŽX֢˅¼I^ÿâkLû©Ç?´Œ2KüÁø?v«ÂE3ÈÓ ¯Ì6á%â¥#>)è|Õ6>¿¹C‹ÓÈi`¼öÌ1¢ŸÃ2, êÅ~R׈¿ÍåúÄøfv©ÂßàÃ<íuFo‰ølÀ¿yŽwQáÞN©‰u¢‘¿Ñ‚:²æ f{w.i4©òþÿ)â?M|_É}jÕ¢Áæù†Uë«æY¾Zñã^"6Îk;¹žÿ]$áeŸÁù‘(·ðÝèÚyóPØ!Þ­ce8Bzªxú`o–_Cü¨ˆï+á‘ÿÊ*ü˜',Ir¨å€$üé®sÔþ´»hÌŠÉýöF žÁW‡•/@‹âìlç·—agª½³÷fù¢Ä·›ørÒùW! <@}.†æyWwÄÄ›²$¼æ¯æ´÷Ð{ÎÆã†ÈѤ¼Ö^sú,@xd—±§?‡à†Ê3AŠ6²jÒÞ5ñ÷"þÂúüW)ÌÓzÌë‘qm“ñçn¼ÛN÷ìøÄžÙ%a((;þΗ»óQFp—ã±µdøìz»EçUÙ|'õœ~>gyÄøªêò湤ZõöÑþd I2u{Ì=ôVjj´ãT(ò3¿eˆjZ>ehÏÈ‘2¼)¬Ó´Q¦Þ˜ø:ÿOšûõJÐÙ¬ßÏã ê"ÚçÒŸ.]þÀ<Ù:¼’Œ©§WËo÷аƓ­’¡Ž+M›Ϝ‹f$¶j¶C†ÅQ5w^>ìI<±PŸfe/:itõÅ _+PXù¶)x·×ì² S5Ã7 Aœí·ŠÑ›{#­¯…ÀçÖ…½„åÜ“~ŒpåH=¼zëAâ© DæÕå ÌÃ;,q2µMÁ «ØŽÂñj´,—{¿ÎôSÈØßàY©:vÊ4ñÛ6®­“KXßâ³Lûþ2²ÊˆŸÓH]~Àøc×PÄ›Ü5`ËÙ³kÔ(©QiäÜ“è}äªs JѬ§Çϼ±—á}‰}ä,}0yþëKxðÎã­Ú¸¤¿&ýÿÓœ71Ì3IµÖbî‰ì 3fU£Y… †­”¢-ÆÈ»mtDâ¶×%çŸOãDù°ßƒð"ˆ¯9Y\·Sôdœ5"þºü€yºœh—ßG“‚=Çr*IU£DÜÞtdhx×ó+w¼tD]zs¢ÛÌaß!¢ð¡I>,Äé»ißW#ÔH²FݦláߗΘgÙ­wbóT|÷‡0qùC5êxv•ûé®Áhóª˜× Öo—Ný5¡K*.®½fßp‡t”êur€xÔ!äûÀíUÇFb´8ú{ðëôÜi]Έ…=%l?]••ÈpªÊ4¹sUÿU˜gPYæžÇmR±=…{ôKGOü}äAÄë~õ¥wS1Ê^hp,s_íž1çëF¶?$õðYÈ}!\O²nÐå ÌsÊk˜I^*(˜Óäpb:ª“½a올imÎsm™gN½bæôy~µEÄבܲ%~ßú>Ìb˜§Éƈ›¥bª;kªMG . 7ÆïC:[È–bÔQ˜!wkÑíçŒìu#ë<ÂÚšäpg³«1ËÍ >ºº¼yhÞI*Öa›ÜGéÓýšÙƒZ ²ã|8!u×´{£Î„`‹\wÅUovLžƒÄï›Ôa]žÀ¸:œÇäT<¡³K³+ÂûȲwÇ.Mv°ùXÔ¶eÔŸ!x´e÷ôéL8$~Èú: ´kh½‹Æ¬¦>gUó”·¤ g*Ž§Ú°Å÷QKm^ µÉ6{¾eüY诫Û;‹dx¸“GŪt Ë— û¬/ºî:Á:‡ñÉ%ûº|yœŽî>´(û”Mò9p]1<<|—×&diÓcÐü™èî²éÚM `kdÿ4»¡ËA#}‰ãñçâ‚ 2Þ°¾ÈäßÓåK¡V¸A(í»xJ*×·ð•ìÂ}Ô—Â47• ~ùÍ`I1ÍèÙ:øý6þêÑ­>˜øy“qHÞÓû6ïäùL¸Lº|y»wîgk÷'ÈâÈÁ×÷‘®M™¿™E/\:y&jfvèZ/6zjøšU>ì¾¹OÄ'wX´M`aaMö¾¾œ._`ž›_æ¾½Ú"çŠu¼ÓþÒø[××a%z§-=²nÚžp;~ ÁeÝ–}ìæÃúH“¾‘ôÉÃ2Μ9[—yŽ dy3º|yrë ´o™ŠL´L>?îš’úê¯!"Ú§t6êïeòêZrÖµp݈ÿ(©gä9“ùônÙÚ®uý\1\¯!t¾À<_á¹õáû^ÚÖ¸Çú¨´Áà>R³1È2êà áT1r¯ÿõ~þ‰ŒvqÛ-oãÃòH!ÏÂ%ÚÿRýrýÀª¼w)Ì3ŠjçZ§bµ™ã×C²ÈX²Ã1õ |?›ƒF_[òþgdîrÓâqå÷!u‘Ä}áôVY¿Ã<ã.mê ó,Ù†;/Íx€Š“ÝÞ6m9žè18fü–yèôX È õÙªÞé w%,׊ÜÚû<Ã÷~/ \[]ÞÀøC*nnúÐ4ßîÒ}k”QZríû)§xw|¦Q›wÝ®ÍG/ƒë*_| ÁŸ­ŠÏN·¯ìcÈú„ø•“~†ô“úûEZáPè!°23Ù=Y7\ªùîƒçwôÜ|¤ÃtqJÚAæ2ÜÍàZ_g­7¦yôÝØu]o²ôs§"„æ±~~Sšý EC˜ïš•úúeÄíÄWvc>é°èËeØTæ²Èi½„õ­'ûgô¼ïX>Ýö¨â.†yvÁjk\Q ¾`q¸ï`ÚTãÒØ/n׺ ·ZŒv/x·/@†3×ÈÃ:¾’°Üús·D‡ÊBÚà6ÎtyãÞlõ‘»ûM ¦ýê3ׄ+.áíÇ=î·ÿØçÙlTÈÙ¹Ã2X†K¯D·°TÙ_’ø"²^Ñ匛ãå¸õg ž³;ö%G“\vÏóî÷ù¾ÊivQæˆ(·çv~2lÑ gÛUÇ}þ…GNöO×A—0nüɹÇpTÜÍþɲÁ52Ñ·~u3ïÄ¥!­>yÎuDë69Ÿv^%Ã.“n×Ç—}®ÞÙÿ9\7p¯í+ºþi`Üû—ÕÅOë¥b¿F—Bv›f¢qu¿kvïxµŠ{o¹#*oc¿ÇßG†!Híšžõeï#áÎÐû‰/ÉûÚuŽ«…È~„>'Íà¡VhpúKÙ!èK·8¿?25™Áß¹qãaØê•ù•~ÐÏ_W>~,—áo{;qÕ¿sÂç&ëÐåû·÷ºó™ñ?ï]•§óä/‹¾2´{*¾341L¸0Ý6¿ä¡è(¾›j¾¿uñ,4²Ñ䄨³2ì¸üêO_v¿Ü_ò\'û–Óm÷Å‚ítñ󜸹xâA©Ø†glž¸#}*öÓzµ ƺ×9³PŸ§9[]aZûÀã'M|ÙúGx#Ä·Ÿôsôú³ºø‡yŽÄ{/µMÅõîjÞ¶ˆÍDð°h÷õÍq<ܯÕCAލ/=„ºaâ±¢ž¡ª²Ï&¾Ù¤ ûGDúÓ*˜ä/éëIÿKêñk×ç6¨`žÂ»Þñ­`žoýBÚe¡å»†”Ž9…W•Ÿ(ÛãˆÔ÷)ãvXgÉ+ d|0Í™i]mÿ2]7ÿ}}Îæ¹%‰ 0‡uC·A]>©Yˆ¢(÷ Á>7ú,.Ož…ʺtk%û%uGûñaß?p‰!>àäûTáj´Â+q‚ç†xÍS›Eâ,Ô~¸¡„÷{ûÙÖ3WOŸ‰ìŸŸNj)Ã5&"|}Xnù“®§/„ÏKÖ_ú|/>Ìs¡IììzÏRp”c“û­6e!“8úc± +ή‰hôÕMÜg}åNô7vŽSõaó“Ü'²º~óößÚÓõPãÎ?x¿oê•|’KÓ³Ð=£ÓScBqí¦ÒYuØ£Å+{g¹ÉðtNÔ¹Íç+??yŽá¨èòÆ¥?O r9#ppZ²jyÿP€[.ToMGF¯t5¶‘azÝÿ9YW‘zBóhN¢Æ¥÷[RðÇq½z¾ÌBõGÔ²ÚÑ5{,r8–Õx:z4ûpBñ`¶¶ßÝ)z·ë‡Oú'ÂÉ¢ãߘŽ7"4±Ù¥%)xxñÚOkg£­–žõ*‡ãˆ#KÖ98Ú#ÅPøJ2|éBðˆÍÞ•×ä3—ÅL_F¿'TÁ¸Îhòˆ­v)¸–À™\u7[pZŽ9¢òFó#f =Äwëpì _±ÒÙ‡å\’ºG¯óŠY-ÙW"ïuaóŒ < ú:</ V¶9+ÌF“Ò6O¸°7O½¿ÞZ½Í}ijð¤e?è+FË[Néþ÷%}yN¾»¾o¿Á#­Ð]·A’‚Û=ŠÓŒtÊFE¾ñÏsvF⾟nÎ96_ŒºÖ\¹Ý´¡ |7£Na'–C®áêÒß§n•Ïχñ{}¾1Ib–‚—|ñ‰ê“øIërNDáuéß²ÞçÍA· ›ñ!æ×©XÛØ“ø#ëQRßÕiµgøë,ÿHŸ'(„y²¢Ò´’Ž)xYÌ(«!Ù¨u£]›Æ^Æ“ÍLœœ^ÍEFu?~hT‚5ãJ¸?%ìþ$á“~•ðoÈó­Ê¾.ÌCsºRðÛ3±~ÞÎF‹º÷úrâÛiìÕ`Å®yèÛÊ…ÉÃÛCþž©›1·T‚I®¾?BÏ÷Q@ú@òytùólé¼½Ó³7Éøuh£¾Ùhå@¿¬ÎbIQÝrÏyH5“?¬lž ‡MjÔ¼Ye=%}é¿f¶X>â¹Äˆåwès%¥0ϵêÅS2öé5ÓªKúÖ'uñ‰¾ ,»”3ÜÓ\d{êÐ$óÈçÏ?Ý.möaŸÛ¤ÿ%œpzœîoT0î¯75+ÎÂç÷2Î8:)é3§8X¥À#¦o¨ãš)F1ܶ5F×ÅߊÂPEe?FÖ)„ôbœ÷Ñ%÷k°ï£ô¯“†š§cú-³ÀdܦN¿œŒõ9hîç¶—ç¡sX{bKÄýdG¤[.›‡â=ëÛº¥û²\[§ë~®`æ‹Íkü:þ¤÷sk…›¹þ½Nú&ãYׯ×ïw"MoÔ1tïÙs¸þümÛÙÌ@ Ãf7ìŠóßô6:èÃî‘ÏGÞ›Ñuô½n\>Œ«ÔÎø¸1¿Øeöºþ… zÞfh“¼»çÍë¶)ÓQ¯&Vs?Èð†:ÍÚÌæú`ÒÏ‘÷\„ëBßWz\!Œ›×.õ‘`e2¶•™Ÿ•ƒÄß;Ot‰Á1i öî< %¦@‹2¼nIϼb ûÞ›|n:Ÿ ö¤ÞI?¼„æÐˆaÜEó:=w°MÆ‹¼¥ñosPkQžêR ~(N|;7ÊV÷øð32œ¿°ÛÓÊ>ˆ¬÷hÞÊw†+XF¿çƒqO´©åçÛ2÷¾7fFYý\´Û­á”eÍcñ<—ÓÏ6c;´è~Xq»\v†.¥Ý²Ê¾‡ôO7Î-žÒ×€}®ÓçhÆ/^7giü»$lù>jÐ"™ªF´èå‹O™gàÛ¡­3¤; ?iþ~•õ”\”ýù¿ÃÍXÜdbzI37;–'ÅØÒn«{~†ä©›dCÏ0îIçvª¿pÎ}ÓNZ±2]º&ŠXÞ.èý¤qÉ|;$½–ú¥~“P¶$òaܶm§›&ßOÂϼ߭{+ÏEªG_¾‡»„-\²|ÍT4òØ„˜Ìg2Üáf‹8‰ûÞšìç}Oº¤óOãÎ<2³Côµ$LŸSÈEs³,´][œÇ Fëåõr2êÜÆcç¶Ï2†géþo'ñA×?­À(±ýXUôúS ãFõQL‰NƒÖ3°+ÈE¶mÜàÌ>•Ù–“ Ÿ‹Ð“;CVÂõ¥ß{V>Øý ݺA+ ŸsßäyKú1]|Ã<¢'Eß¶nOÂÇÞ,|Qž‹öS¯-Ÿgú‹ñhëî˜}{‡²ùHžäOÂ_¿tÅãGC“Rö|•þ~€æ©' YýÒ) OÐmç¡­¯¢ÝöeÇ6}¢Ö)ÛŽA:Lí”Pœºknbhfå~=ï™ì¾£.®a¼]§j Œ»'añM•a<Äã¯Û¢v<žY+«¹uÑhÄ[ïÐÄÄ+g¿ñ4㮄½>dÝAÖçôýdâÆ]†Z>¹\’ˆ7À¿,•‡v5óŸô¥O<žÒ=<¨Èa:¿+ó[x(¦èKR,aóôåd}NÞ/éâú©VÒvà7—¤D¦OÏC¶â[çæÄãaaK &M…F,É»ÚïA(6òJXÿD&Ádˆð§È96’ﺸ†qç™ k0&(§üUþpÌÚ<Ô¿aò˜ûþñx‡äK+Ëi6(}߬Ö_CñB•´hL¸Kô9¼R½î# ÜSý})!Œßý²²õˆ¥‰LýÉC»=z쮉ã1Ç|Å÷ÈÆcÑò•å²&uÂpûãÒ8òÊÏMöèxÔ Èy«÷kZþxP¿c•uŒæáDŽ:kl–ˆWë<49ã°§My<~î(\ÒrÏ[ ó„6]íi;O‰sî‡D¿Ç¬ð¤ÂYЊ®+u Å›š…Ï0‰•`Òß‘}j²ÿIÖådýIÎ_èòæñm`ñàS.ú¬{÷2=¾5¢Ñä„l{üU…«¿ˆÙ Å…mK€7Ëõ¦Ÿ&ì¾þjß}ïÞ˜¾x#ï÷uyó| ^‡lHÀg$í6ÔÌGV̆Êð§ •†'&¢«{®ÜØ" K|øÂ­{6°ý$©û¤ÞÓïñß Èó|]ž<Ó ;S¯¹¿Çc;g~à6ùh|„á ý•x6N.-Ї^Öÿ¼oæ0ìº.Ë%YíÅöcä>Ñ\³,ý\0¬ÂæÃø9£†æÞÙÛõyf-êŸFç?yv‰»8ÒøçBt4zRÃtm6_ç"ÃlŸ@î?WO˜x6@½=s׿µªúÞXó4ÀG]r;ÄãÚÄ–Øæ£éagË)qºÅµô…Ï­Ñcß]’ºá¸ì¸Ñ­F}$,gŽ<H=¥Ï¾Zõ5s]nÝDìÛâ¯qŸVFCvEH<ÃðWë¾Ït•`Ò_~ƒìëÓyÿQ@ê ôs\ó<)Üw.ï[,^üÓ(Ú.9­«óþòé.‰øðâin¼жµl5±¦[+XÛ¬«ä_ÎyÒï 2™ü¬Áþ=ýœex‰ÏµÂôõ»OyÄ2\Ë|‰3ò~pb'Up¦aˆ¾n\’²ú€%óžª;?0Ï¢øî.ö‰ÁÏæÖ‹~Rß'º¶oüÎDÜËùÚh kDŸŸ ÃÃ;™7Ë͇=MÖõ¤o%÷‡œSÑçx‹až„„™û֞Û¾rl^€¶oî‘—ˆýµSµ+@%aÙc ô\㟽ÉÖ“u69§Fêš}ó²á}Ë„©Ï­—ÀG—?/´Â½Ôk…ÉÑ8Õpá…M' PñÌç«ÆÁ÷Ñ­·{¡ZºƒÓrÜfuÂÐ “ïA¸½VK’Gï+ü8+¶²ýÈöÛ¤nèòæq³oU>©~>>žœÈÄ<ÿñû$\ÒÍÖºÏöö(€Â–/–ãè©¢I»o`ÏßsXô¾ð&Î>Ðëwv·Ï×\‰ÀEVÂÏÏn ¦µ<“p·MÓë.ªÁ¤‡€¦Ú?u#&ç°É>ÙG%ç¥uùãÞ{¾»xµÓ|ø”É5YÚúxržåS—²Ä&%áøxú´Nû6Tö1U÷]ªí£Â¸Ëtäá˜^ `”ðè¢8 Óû-Ð —ì±SjÊÙ}I²î%×ì;–9]@ïãIaÜ«F7Ãf= Å…Ô±“&…È÷æ”s»œaÝ×pÎô7â^h­ìëÝ^cä˜Þ7«\7‘})º®¾¸û^Ð}‘õŒþ~Ž æ(ÿž5!@Æ<¿ Q¯•‘Üâ Ixa¨ìñ ›!Ìûw9n?ßÁ¢l´ûÞ™ô=¤ ïÑI_¬ÿ~Só<ö¯/Û9>w@­]Æ"»ý/<“&áçÛfN¼P<=©x<ù`¼/Øíá7j„}@úEzê&»oDúâ*çgµZaðZ½¾Ö=…ù%M£Œç¢wÝ{w«y' ;›Ò1éÒ(Ôï8çñÐcr|ýãÛ¯·¼Ù}b²ïCÎÏÒç+ä\šþóóü\ã’·ë÷ð¯ñf}!2ˆ;6xä÷$¼a×ì˜Àp4ÌÖ§–Å9¦(ÕÍü6²ëMR§Ès—œÓ%ç*õûb!ÌåÿîÀ’Xwœag!òâ^Ö3¶¶e£¡ Zû­§Æ|9H =ÖëÓF¶_%’>¢÷šo)ö¾c¡Ë _DËìŒ÷ßU¥Ý:Rˆ¾.Øù3pf2^xkÔ‹o#ЃÃÎ_Ü.Çôk=É¿œ $}7á²Ó÷£3{®G—'0Ë;Š¿í”ö> ‡8»P?K&IÆ{brKBÎÙû[Mý†ôæl=­úÞÅ‘ó¹¤/×å ÌÓ(­Óž¡Üø¢ö¾zƒâ ÑËå½ÜœŒM»(¿âZ¢#°+z(Ç­v†Å¸èƒÉ{Â'û÷ä|0ýüë_å\¨ æÙ’½¬ý»@\2¾ÞW Ѫ5JÏŸKƧ8ù¹Ó{¢Š€y1ÊŸrl|2eoÈ _–óKúx²@ׯšÈ´ÇÁÓc˜û։ΘÇo µq€;ØS¿t)D¯¤èòæ%Ô±€G·¿Ü§NÜz¯)DE6Å.&3õ‘‡6 ýPxÚ<‚=·C¯ølBúîºyOKm»ô©rŽŽã¿}´ÿÉ„—þø®Ýû6ûÞ¢ôäEMšßJÆMn[ݸäÕkúÜên¿œ«{½$a¯ésèçD»Ïá©û‰ûÞH—/0ÏQ+uÝz»ñ‚ ÿ„Yß Q‹Âü%YÉx¹­ý‚Òa]‘¾ÿ¥¾GÝ­«ê÷íWyþ°ï’ ck» "‹ÖgÚG— Èïè¼´¨rþD ó¤­ÿÙ¾Ë8?üh£ÊȈS„^Ú?çÖ#¸/±Ã÷½yÑù®iƒ¦¶ŠÀf¾– »Xl`ûrþ—ü.‰®/ïØ¾N¿Iólë‹Õ܄׼tÚ³irŸw¬ÕåÏÉøêÑWÅ#,‡¢“K@Ìáf–¹Æo„ØçÝ?tfÏ;õÎíieÈ>çI?¦Ë›—„7ï‰ë î°ZÝ©Ùé¸ôr‹LŸsFèc¦ÏŒzÙçy¿îðœ³Ú§ókÁîÌa‰iAƨêy.ú< ÆW>ßóãÐR7\·÷¡©Ë¡jEjòà,ñ´ÞúµBË× êÄïK¶Ÿ1A–ÞlÿCÖ_äþ“ßÅþJÿ{h`žÓTVF9ajõd[„Ÿqmë‚á!Ó¸ýK+tpÃôš?gFàcKƒN–¨|/EúmRÿIßHÞÿêï#k…äÜþ‚þŽW;¡^³o\»6[¹œÍpAh§ßÜwÆAXÒpvrÜ# Ûדõ©ÿôùÐÏ’÷úçù0ÏŽƒ+íÄhßÛÉÅûÝŠPkûœ¶Ç§ài>ŽÈÞ‚yÓÇoVæ%Ù¿!ûó¤¯'ýݧ˜Wá– ae»-¯&)]k@Êàë’"”¡ÄzSb ¦ÏoE9æ‡vßý©®¯ÂªòýYGs7×wôQE¾eûÓ*ç<`žƒ ¨ÿpZ±`ûÝ>;‹ÐÞ”¬U§àQÝ'§t „Ž*i"ì‰þ(½#Mž ë/ú|¸!"ïyô×u_YçÔ# ê\oÏþ"dÖ8üM'm vÒ(€F9 ;rÎ9ÓÃV>ÏH¿1éIÀŠeæŸÙómúçè¤Åä=á&Dýº­Í¡"Ô÷êºÔ½?R0oÛËÞ’°~hWoÅž'Û"ñcª ïè;'qDÎkêïת`ÜYþ=ne/ðCºr\„âìÞDF7NÅôº¢? yÑ<©±O$îÓîxå~yOö½ž¥­X[Jïø¯³ƒß -Û…fé~HW„Ü\”ýLÍR™û7½{ ˆçDb¿ñë½#ÞìºÄýž_]å=„Á+­pº—õ£Ø5þèYÆ]‹ó¡0îµÌ凥âÄ—[ʶw·B™=×GD²ûBäû“çyßHöouñãÒñ¿I5sGÖ+B·k ЬûÉþ9¹ä÷Q¼‡þxI þó¼‡ ˜ï­6 =Ð$ ãÀ‚ñaÔg¬RëÌxÔZ0 âÝ-g‚øW>iúþ!ALp;ëywÿŠó;v dI ñ $ ;PÄxQŒ,¿jµÄKMÃ0Výÿ†ó+v aP—3>µ„q@±²ªsV)Vñòcø–Œ¯š–áeÉ™ä£X0 ÆwDß³VÂø1ZTãQ+o?†™eß­Ò“Äñð¶¯ÆÌúç€ø¯©«q`„Œo •ÜÎ õ?àP;ƒÔ KH~)áv©A– ‡šp`ÔÕ|-«ñU ?ðO­üS+%ÿyµ’Ã|¯êþ2~‘†'èW'Hùy»2’– ç€øÕR¼,Ní_{-9ƒÔ K†³Êa|Úˆ_í¯8¿ãeiA"H%ˆOyL2¼,{†GM±`ü«ùN/&-à úÎÁ¯xY„³J%“½ž§·û/¸‚Fͧ™Ôþ 3KÈø3•2Lã×Fñ(Þ*·š¥ãÙfY¹ªb¼ký.ŒU2c¸Y”o­¸æWÞÞÄÓ)§ë€b­JAHnWPÎ?`­º‚r@BH~9ÃZu倄 k•°rªyµ «1³ÒfŸÔ?¿ª•êäÿû:ùÿKüwÔG.ó÷ÆÇRÉð•ÕX‚ÄË› Rg†s`aL{íRœ,Ëßxyë{XªÎÔ¤Ò÷Wœƒßq²xô$‚àW2œ,?†?mÏøxëó_ˆ7Åte|ê~Ç:ø'‹ðV-/o¤ÔH&WPNûJ5•XÎŒ7/ÅÊreü¼)ž Å¡Ö0Þu÷@ËxYŒ’ñ®s®Æ]¥xYö ‡šâÀøëùXR¬,³j ê_±õ½¼9½«2ÜKKHf)ˆó_0W¥äHrWPÈ’] â@»2¼UÂ;à@ò;ƒTCi<×ß°²þôÿyuñOÿHÇ õ¹5ÔýƒàôgüÆ)ž 5ž Öñæ2¼âóKq²¸ÄÎ 5È‚Y â@@»‚r@B†·JñÜõ|~Å;ø'«dI ™Q¾ 'KÌð¨)LP5/O H Æ·œb Jÿ†wð+Ná­r(_=ÿrÉ/x‚†p©ƒV– $œ”ñ0·g˜‚\†{@qWyÕ<>)® –aè³WÓÏß &IAi †—eÀð©õÙ0Õ}Îå .$²;HSy@1Wå .ÅiþsÕ¤Ù@ò+æª;H²a˜«„y@yý ¡(Jæ>+KͰ²þ»¼Õ?uòß['ÿ·ÕH>HâB@:3^èKФªÆ$^膷jiLûS¬,áo|Ð¥ ²+ÃZB@ËM*}Åpø+‹ï*ÙC¨V–?â3èú,âN1ÝAê¿á:üŠ—EX«–Œ:áRÍ¥–2ì,$œœI:1Ãä1ÜŠ»Ê‡$ti@6 g°”aGè³W)†–%çæ@’º‚Ô K†ŸÅaØÔú¼œê¾ò YÒZTeFPÌUˆÉ-iÿsUÒ‚DüJ†¹*iA"†¹J˜Z )¨œaFè³³rvÖßíCê×JR'ÿ'käŸúø§>ê×Gêž*@<ŠoJ3¤¹‚® 4†+H1mŽÅZÍ !PƒÞ— ¬Ä u倄På .±;ÃZµ`V0þ_11~Çÿ2ƒ€÷•ƒÄøi þ &(~Žº?Ǥf8‚PÎßp2~Å#¬U!$OKbPó ‘$ mûJ5ˤfØ`Pä8Ô¥ †AqWEz,Š)˜â3üŒ4=ö*•Î ‹šbéH™ät©A–Õ8Ô¿â j@6p£ $°”Ó›foPÜU ÈZñ¸«6è ’]ÂpWm é $¾„á®Rü %Åà€"àRƒÌ H~ÃûÓ?þ©ƒÿ¼úhÉÌ[J]N)¨ÜæJAT1H âS D$‚ÀU‚x¼® oŒAìÒ€l ˜ ´¤‰î*‚Û”BKAtWPHø7¼1*œAj%$‚”á¹2Lj!$…ÄÄp倄 r&I(~¢Ädq倄4r÷7¼1Â]åA2¹‚Ò@ cHŸ£HC„O-g’ͤqÎÅRäCò¹w¤ù«f„$bxŠå Q5+Å2œj.$©;($d¸c\†QM˜D ’WÒ‚DÄJÙT ²‡„V€8}höªćäö•þöª¨d¤bØ«~ R=Ã^5ƒbà*‰ (È™Â`_9¦a˜cÿ´üwÔÊÿmuòsü'õ‘ºJ‚Ѥ6¤yŒî 5ÃcôcxŒ"Rˆ*i@6°R†§&‚À•ƒ¸¼î È‚XâA K@Zší Rƒ,!°¥ ·ëß°Õ, àƒ˜ w©A–üR†Gí ÊYB"HAHWPÃaôi@–RÄ”þ‚·&„¤ bø²6¥þŽ_ÉàV€8hbÄ…„se•fx’Ž4³ÖÐTJÕK†SI%£}5n­dÉ©ñ A% ÈUâA²J@Z’V âCâúJAöÀ*$±?¨$†dV‚¸ ¯V2ƒÄö•÷ý¯yµþ r? äÉ_3¬Z (A r=ˆC)A|( ~šU+Bt^QÿPaHÕÿ«…ÿÝ~ðW5ïŸÔ»êµîWµ­z]£jXõÚEÕ-R£H=úwןW/FÕ©]_Hm¡¾¯ ”Sƒæ]—Rï1 v(A|¨~ -õn4j…¤‰à¦+A|¸ñ~ R=À”B ÈA\†BâBpYB}‚8P\A9 !Ô9óv !xä .;u>HâB¹ƒ4Ôš‚‰ ÁäÒPAùnå*oOó©ù*YÔJÈa>õ{†1+q!¸ÜÖ´ ™Ä…|u§Ø³ ›jœi-H¨ñ!ý@¥ {FÈŒ:³L½+€€Té±hËⵇ`ŲüdX´ö¸*â-‰!GùÔùdÈK5õëCÉŸ}¯?}‹Áæ¾—=óßQiR€8 bă@u¥, `ƒ@å 1nÈ ‚WÊY@ûJ©ß¼A0«@fÐþ rŠ™ ²€àiA"rˆ.iA"xˆA/iA"~%ˆ àÒ€l  $ƒ¤‰ )” $†¤‰ A” .$‰3HâA²H@Z’F âCâH@ZH âC¹ƒÔ 3H& ($„¤’2‰å Rƒ,ùðwüJ¶¶Ä…ds©@ˆI|gd ˆ)Î 5ÈŠ”)bÄ…Âà RÌ @øƒ¨_„Û#:§˜ÔÕÅWïò»Ú÷[÷þiû»šFú™ÒËü;ú˜ÿ©Ú#5ø×Z#¥4Ô¹^j?êˆ duÄTJí¥Ã çCÝð•‚ì¡n¨@fþ r‚ÁàÒ€l ( †;H²Q€xP3$ !Ô 9ˆ õ¤Ù@½P€xHd¥ñ ¨$Ôy]*ˆ%iA6` ‚LÒRç+ 8ƒÒ@pALÐ9ƒÌ ð„Ôy 83ê·Yp6p ‚NÒ‚D|<Èc H A *A|F?P)È‚R2ƒÀô•ƒÄ i ê-µ Aš²€@ ‰!XÓ@°ALЊAi Þ &€Å 4älów¥Ì¨3³Ô^5¹D¹tû¡ÊßÚ’½öêç4ÈoÅ,˜ßÑê>Òs•þ¦çªþ©žKÿ3ú3ïyÌyµ_푎1ÿ[õJ+Ôô¬X>äë·0QT)by¶íì¼€ /'}Ø!Wm ˆp‰ëJy}®G²œ÷P.e@%Áú<9 å7qi¬k±ÑIdø)=¢û‰"ÔxNý9£.]À§¸¶õK·A´a$ö9«ÍZê!aý‰¯íñX@ûá3Ÿ¯믠ûB%ZaVÄ;+ÓF§P³ë>Óo!/É›[‡__À‚Ň:•Z Gw[QŽ“‘xç¶Ïmì÷TúÜ?ÚGµDðfâûÞ·9¬ÿ’¾¿-æifÔñˆ°Qš?ßnóÑ"41ó}Çõ1¶üèó4Øz²ôyç²)«öGÙ «ôÁ&~>ÄÿƒöA2F6¢o¼UÙ“_*šó)„y(—Ûýdˆæ|¡'Ž\žÑã1yÖçn…õAãžÍÚÉú)¿âë%ÙöýK£V%â‹OûMDî|Sé¥Þ´OÌSße©É½±¡ÈEÓ £ëÞ"x%äøÄžwëf^ãtÝžèÖ‰&Å‘ØLw‰ßá*›2×'_À Yè0ŒÌƒX®5æévÅ´ìÝê0t[¸à[Å®"Ôêð¾žVpÝV¨ó½+âÞ¬°‡ñÍɾ̛ñ³”¶[×õCØûýã+Ó£0Ô¹ŸÝ|p/Ñd±á¸®,/öïyÂúì%.[}w¢›9"¾ýº¼yJû\å€#ÐìÍd-w¡æÝ®;↱ç§Ya/Aý‡el0˜…)ÐÒ|rÿû •£¹kå¬$ñÅÓåËk­°ÎÕiê¸+‘hsrìÚü-E¨ó«Î/<öaŽÄ÷’øñ $þXº|y>m¤çÏ ñ:àg’-‹ÆòTxÇ®Ø=—Ds›ÖÜ»âm$ÞªåZ6v®ô9'~Ž„N|è:dÆÖ{]¾ÀoŸŽ”Ë’½áì.äù,‹¸Š0n!óýcõÝšï£Âq—õ¬ÖópÂIX°éPçäŽ÷ uß©¡KÏëH`È«ç}ázíŠC;ß #[š4Øûv9ã Õ|-y?ù\ɲ„m°eÏWÒà¸u¶ê–ÊSД³wkˆŸ¯ûÓ¨­wt$é„ë‘zͳ}ÆÅëcÂÈ€N)ÙgΨ.;çzyd‰ 0ß(Þ’ÿ|u8î»&ö9 ªŸ†ˆ¿Oüít¾z–Z(†H²×ížâ ¡÷ª¦7ÂqwíÓyÔþó¯c<¾þøÏÖþçÙ8ÏÊÁW[ÆŸM‚gÝ‘ÅïÁù·9çz7!õ6yuˆKõ žðZ¹¸t«já"¡¯býãÍòu鳄ù©Yâü%~>›K\ã¸=k½)´øF$Œ.WfLJ8¶­dtŽ%Ëz:ö¾Û Žä*@¸àÈ|@Y¿ÛïÁÎ)#Äoè{½²ÀíeóZâç ©•ڽĸ(pæßó{bŒ}qús„[,y [;Ÿr#& ¼Ço\=>–ªt èµ³ÒÀpÂû¡þࣰþ–Õ]Ögó~¦â‚|xœ§±yjЫçàr3®CÉ‚œ›c†ÝKÿò&ãP?húlâJR?œ<~³:¤æÄÿÈíeõòÍâ›~Oë‚m…;¥žÕ šŒšºv—‰çcÈqžJðí9ø|ÌÿæøY jîÛ¶á¹X²®œŽîíÜæ§¬² '¼ÿQZEŸ†ù'3¿Fcõm×JÍ.e#ªËÝëOë“„Ïœ§ƒŒ'3zŸ‡ ÷Ç—Ü_‘eg7ßép?–¼»ß1¡Š'øg^ìùõ]iRú‹vÅ×…B¿ÅÞƒÌorÕþQyäeàôƒˆÖ¦ý½¡V…†×sý\ù¼ÁyÎ>Ÿ—:òØyè™SÒÄiY`(<ÑAlGø>ÀRwž×Û} #Åž§]îzàoÉòƒ}n|Ÿò]žo”W‡ó¬|Pgtïº §Ç†Ó+<1?ŸVSßu‰#žN§ëÇyBóéenO*N²êßmüÆgÁŸz:[3ÿlKžpq<èƒ>7ì,®³¸èßí³à±.<çFß8Ò©Áè3Åw„7×G}¨NÊ4é©—î^@߀Å)ã–H-Öîâß<ïÍæ•QÇáùÊ_„ï² 2‹ëdÁBÿEï"§Ç‘•Û^ÙÈWxÀ¸¯ãGG;„“QžIK<~ø}ÿÌUäý<ßñï7Á/;mÆ„‹PÞ—´¨lÔ…•»Ý‚ãHéfl¢Ê €€ŠyÚýÂH€B¶6úÖBrÚ“#+7¸ŒãÊ>oKü㸓Gq$“‹pÀ;¶ÂܯwáÈìÂç<ŽÅ‘…Žeëûõ‡‹÷¾¾qú‡ŸhçHÛïÞ: ù̸°ü¿/#øŒòïž÷)Çy¼ÛmR‚ïS}ÆÕÛy­Ù8â:>c“.·/ô·4 a$jàØ#{S ÜöžTz-MÌ—œ]_ße}d9øûûàn) ðåT8Ï“£Ï–Ä´!yµ‚}‡„»p_Y+ªM~y¨Uçš%ûÀí}EC¯‡‘VNëòZi û/¬_`ë Ö73®¸%îqü+Çj^ºœÀ™ [í´¡w¡sÜ@ípÛxbÓwLB´m¨JL½7æ…‘œW§¾è-¤ëÿbB\3ßàá6³F\ô( l½kíO®Ãyö¬»\Êûy1!ns6Ü…<ËcŒ'k_sÙ,8»a$ãd^Ë6ÅR~†­Ààù 6ô÷jP€ËãóUUL“úk»ð.œR‘þI<Ùß~ç™à°{DÃD‡Ëa¤YÕ–5–_HßE€õYlÃÿ~/½Ík£Ty|_Ñ›b|©Üä»àÑxë”ãâIÑ{uGµ·iÏûÈõaÄgæ²yãÆyaÜ9æÊóÞHØûÈz?K„ó ð<;^ÚÁû.8D·?ã½9žLÕ^húÕ$ƒ…7Uë~+ŒÔl»a{Éa ézÖ׈q@uÑägºš%ÖžLˆóèä5 Ω¶9µ½F¦Ø=œõuÉÁ×”ÅãÀð\ŸÖŸX¯cå8ž{êI:Øçbhðºî]جÛ^]8LÔtmt+zä¶ÝÕëï0ò¹öÞ¦yžó¿pV?XüòŸY¸FÖïWÎý¥*ÚÅ€åuPâ.L(}opN—reQ»MõŽ„Ls…-¥'‡‘Ç9CkÝšG?í»°8_=£Oñ€¯ÏÛw>{WâùIÿê@•‹ºE ì¯Í½‘2Á©óÅûqªRéô“†.Ÿ‡C[÷I{dž‘!Ÿ;dîo7_ð£f¾ÖÌÏ–÷Wÿ*aûhÖû>:œ§›Öód1ß8—æÿi&Ì-wÖûetI;¯Éh7 >©¤R…]`ÑÒ_+Ìú7Æ`\Æfk^D6Î3¶ý̹÷þŠòŠˆÐ7ÚLð\èùüæ«Ò­ËÁ¡ºJ¾PÒ²ñN^×Íßq¥Ý<;Åú3Ö/3¾,ó÷·æuÛ¼1J]vfùÖ‹:M=œ¹1¦6¼5r\Kdaª2Ó>m0pÍŸxq8©±Ñ×eûι„íï²õ&ã±u[ZóºE8Ïàüí«½ŽR5-ð_” _ëÔhþÂãù´º®xmØ ¸pxU‘‹û‰j§xTëÆóãl²ü~Òfåé.ûžHøºüQ¹e·ïàF}ðyƒóT›øz–S,LÚ—ÔÓÉ/*7™Û·×¼K$kA¾ûªHox•Z§ô´Éáämå²/Ý"æ ë¾³§yùT"øÓ»á4ðN¡®Yòç™Á-Ã[żóÁeÊŽÊ„›yC¾ïÚIà]r.ô•Í?ê%{¿0¿ö‹+½Ê«—-á¯O´?m ,,yƒó8ÍìbÔô‰…U;ŒoöÈ„w½^D¬ºv‰ŒìÛãI§A°ãÉ•/û§‡‘ÃÙg/M^(¬#ÙºŒë?Îö3¬ù€œçq¯”6›¦Ç‚«{Ðë"M3Áax¸ú ùéµ" b—Ñ>ЩÁ…é󟇒>5S®VZ(ðFÙ:ÿ¹S>[ç³:aÉœ§¬¥Š¾N2áN÷•³íê_&úEw3NÜö*½ë‡Ì^Jzvê¶¿CÐÊÖá<Ï#OÂïÓ’°õ„õ:+çIYÛýfÆ•Xh Í„æc«¾žéu™$Íîp¼ÓÄÁ ØÜìkÏPÒlÝÕ~©)ó~ó¡f¼W>¾ù¾Âæ­Qz"šKüX€#û õ›;0Ïw}©¥—É@ù¡‡rñ|$JÄ çBûç u†ùWóŸKÚÏ‘0îu]á<¯zœðÂ9®ßØÐ(ûD4Yy­Ï©Ë¤ç{ÏØáÅdå4M—·2”tbþä»m®P/ÙyOÚ—.¿8Ö–üÀq}vNjz­K‡÷¾_¾Å|æ–_ðè2©ŒÑè  c,´¡äêäz3·ÅÏ~~¶Žaë5æsÏx–¼Àñ¹ªž32ô#^~ :íÔÍô=JVà*ëÂøu¬Î²÷<ÿÞ}+aë^ÞŸç|«pÂÙÜûÅχ¸­wuñªÖYOüº;¾û.÷-M7ÎÞ¿>””9÷Ò±æç9ÇŽÅ«Ãìsb\ ë¾Eƒó„¼ZxwaX°nÊ;0ÿNlj§èÉâ’¥kM)5øsŠPR´×ŸäZsn«ÇÌÇœÕG>/›àépž ½iWœVÇÁ§×¶Ïö{ݟļúó¶èÉ‘NûÌy2o˜ÞøBÛjCɘ¾]$Æ9B¼²u9ã|óûŠÅAVýÆ—IeÄÂ~³%/pž¶#·”ÜïGæºîzz¸=¼³ã¢žDMqª”U˲ӹ‚J\ºéœ½Cçë(¶^ãù’$J×NnÞ»ÊR®Fóç<6ïŒÒ$óÅHïƒq0•Ã^Ôº7T»äéÉ㻯¢¼àSÒŽZ‹&þˆ>ŽÄ¹_s$7ŽÏ¨iw³,Íïú8ì"œ‡/ÖšÞ‹ì0žã®žð+s…,VØk×Ó ÜNß[vk(©üIîT-zyÂ¥gTaœOÞ“¼ðwþœPŽîÃâ¸!ž»§Å¯»Îø:76«½¦õâSø^…6½`ߘb["×…’Ì—Ìâys„}Æ…àûÈdÊpvŽÀÞ–|Áy,˜sÄTKÑê®d€j¤~ý®aWHç"ÉFò7—Q‡ž %Ñ;dÏ(p×™Ÿ?«»m×+BÖD9;±æC¨pž:•.U^R=*|×uJDL¯S"¥ÅÚ+är‹çïOÕ5Wm ñïJÔw<‹Íé(ð2Ù>-;¯,¼ªÝüêUÊÂæ÷EGªû¶öyZòçÑ^êÓnUçxHâðê ¸—UQs6ö Ik7·Ç”˜a(~Q4ëãΪ IÞ´Âú’‹ZøöÔ žº<ä‹ ¼óœp`ó¥2ôsk+ü^–|Áy]^>RÄÃü›ªlWeÀ…R•G~»BnÍT¯6¶”ƒ2_ýfí‚PòùÚ=3úÎú‰¨Õ ÓK4nŒ[ÈsËÐøj \T8¸òïßlœg–OÉg-6ăyúˆæIàڮЃÝ]®’§]Fû½7 |˼õÈÄ÷•s‡écNÖ™EØû›ï[šS.H²¤ýœg±“V– û^màKè«”~£ùºióërz)eç3ñ0lÝ`Ñ\Ï 8QªòÇÕWI骟DÊFÁ†nÍ÷˦…~=;Sàƒ1>ã. šóÙÑñ›¤T^ÕñŠŽ- pE8ÏÙW U»[ÞŠOµwËÅ>§´V7®’‰ T$‹ä°úêŠ6[0j~Ò¤ûëYB¿ÏÞ/ŒÃî”{ÔæØ“Ã9‰RœçDäwýLc<à‡—2¥fLl¶ø ]™Dr÷y­ˆ*ÇF€æ`ãôCɆ²sÛ†K;_aû|ßrB²ê\Ú5ÓÌWÍš,Çyö_+º}ìãxè=ÒÞíå÷tØR=æ¶³{"I¬ä¶gKÙaq¿ÂSïP®î žÍø=¬Ÿ`û*|óMè¬ûdÎ#6݉óÌ·XÓ!^>$¾ä‚DrmŒË’Mú!Às„BIï«:Z?KÈ–¯l½ÇܕÎc58O¿‹XIâÁ²s:îh¤ùƒŽ&’Ù†Ìu#›ÉÀØ´æ=y(‰œ{$ºÌ,ážÕž{u_’°¥L‹¨Ã¥aöª'GiY€3ªÃyª_ß°edÑHê“péüºtXs8¸_¿¬DòýÌ’B÷õƒáy™†EÝ[‡RälÂÞÃ,î6¾<²¿HÜc ßÇþ8/pNŽóœÒϾ¸§j44¾ßxI‘Ά·S«—ºF”í·~ØóÁp±ÊÈS—K†I—ëÁVc= œ&·|¿w[¿ÿ¿Q~n°^/Û|0JßzU/6¢iì8]1ø=¤ƒÙܰUïŽ×(Pi×î¿· %ël»Å¬m(œ“²:ÊÎCv¿u w‰úÆïƒá¸¡ïó¯IñsŠî‘¸¸f:„zp€ékdÏ»öoΓÁ;Õý没„ß× ¹7N¹5ߨÐù8•â<–.È#vE­\öòëmØ_l;Ùµç©7ñù”…'dð½^hßÅBÉ×–ŽuÊ/Ÿ-ÜëañÌ÷I’{Ž!—L Š»`ý¾”ã<u_V¿Šóðq|J/ˆ‰—|Ô:gw'V¦O÷´¸ðy‚ãû=ªüÆ¿kå5߆—jånMáëdܳö%.È dˆ&Ë9AK2—ç¶ÿëÔLÂê`¥¯{·7¸]—r}²…þÒ’8.wŠX¾q÷˜¼#`çmh¿öEÂùV×IþüYG’äe 8«%S•µ?~çO÷Oëë#ùúøA¢qŠ/6ûKƒç :ßrüT!J(¸®Ûðé\Ê_%G_'Ï_–»5¹Ö`àhÕ»OáÏý‘êú qÊ>vφß7·)P§²qü™™M¯J€³§ª–ÿ » k»Š¯¿N¾©o:~ÁìwfÕ’®a.#ÆMõ¸Û¬N±ó>îéþ¯Ù(õ}Û¿ÈÝ7ñÐ`éþÇin·áK³3¾~箓¾­Ýc¼ôƒ £Nj©Ð}ZÒÐêóÎ-Øyãð ­2µ[®ŠW„ã_ÉmèÇC£ìÝyªÝ†rN\D]''7W/Úî ½çYöb£–|©Úk‰²ÈLºX›¾×Ľ0ÆÅ´Ä9Ž[âíªŽ½¢ã¡cÿä þ݂jÙYõ®•½AF•¹Vͯ¼7´éñT;i‰–D·è4jU±™„í»²ý¾_,¹V¦âÍìB|\sã^øVW#O¼’¯Éºü~Ì 2¼æ¥ï%¼ÁùÊá{fkIÔ·á{Üžú ùÏ6¼·íœ§’JÅqÑü¢pnÀÖ·–øÆy¼sæõ¨Ý:Ž>ÙPôì-ˆ^y_«yƒ´7%Ì84Ö’oL¬¬ÐçcÅ6=|ä/p­Ùz™¯c/$¬Ÿµ^×hpüN 7U½c~yë-8Å}7H¶6Ñ1-Ý)sû«´äXýCúÝöÎuÙßü}²,Ê™ç?wŽ[å¶¹ïû¤883uêƒ9øùÜïó óÉdîÔ¶>£w‚úmNJÇqùs Â~.¶nb¼-v¯È×8nÖÈNØ"ÆÁ÷“¹—GúÞ‚DϽÊ&÷ðy.ª“¸éô`ÈPß.·rŠ–L·Ì ÂösØzÅ5ë/-qýÑ(_©máaÁq0-äH¹×[ÐóÃ…m¥Ê%nýÞZ_èüj̵á}ðçmX™Øs†°ÁÞ?|ßõ€žëò|0Žk_ÔA²Y–rSñ(þ_¨×;‰4NÞÿ¦qÞ0xa\U¹©–pôÔo§üN4û|ùó\á>%®qÜÔE·¾˜ûÄA% È1 fm«UaÙŠ$z®1ÊLôÎ[MK¶7^:ii?á^[ð}ãC ¾QX¸wa}_SŽó”,5$ë‰[×óø{º°OÍ~/v^ÊÏ÷VbÍ”ãø6 ún±P:ÆÏ©]›4ø«tÖ³²› äBñ™wﯓ°Úº·Ð’ÎMþv«{ZIعë‹Øù¿oñFÂöã-ñŽãïÕT8¯tö— G_ÆuîR1™$”êºcBæðé5¶ÖØ!Zr†ûém¦û•ŒWËxÆŒwÉÎÛ ì¿â<5òÚ>±=vÆÜo>¾O*8y *=¡N2yêwҮȾÐоæý{m´dæÙ§ÚNê,;?gû•ŒŸËÞÖûV*œçšzI›‡b`Mÿ­¾/Û¥‚Ç…;IÙ~­÷Ï½Ô jÇxãG§% †UktéãdÂ>/vÊÖÝìž Û³ä Ž÷ÎeØúØØu¡Õ°º©P¥zÅ¥³;%ÇyÍ/*Ö÷„ìÛîå—5Ö’ìñ¯F š"ô+ìü›íSD »ê]}ô ?ªNu£çq{vqLà³°Lï ÃþzÖ;Ñ=ß§[’õyÝ!>w#_KÏÁ§ õ˜=ÆËæû"[º®¨[ðÞ+ÎSöÅ;S«ÄÂܯ½æ¿°K…M^6ìí•LNLÏZzɶ;¼¹²úÁ-ávó«î.ôéì>0_$•Å=\l Ï¿ù>Ïæ‹QºÄ«–û=¬—QaÏÎÕ6ß„¶‡ü¢JŽH&aë“ûÜzÝ ,Ë£›!$Z—s|üX¥pÏ„å {þüï÷QRà¾8ŽŸ±0ľ Ï«¾%™¼nõxÝóÉ^éÛ ÙeŒçžÏmŽ;Exϰ8b÷oÙ:íûXdzçáncIûÆBõv̾ ûM­ß†…&“äFùU2}A;§A+üÚaþ³M{o’Àëeýx#ߊÉ%&ØÒ>Ñ$a÷Ï ¬—qžm߃~n ß‹7óI•ß„sÛ>?vÑ%¯â#ÛLƒ`ߢ%k*aùxûõÕwg& ÷ ÙþëcØ=QoÛÑå›=nQð¼ç) a£HƒX˜Ü«§›ºßMØ6L´3ìV2ÙybÕ’¤„A0ÇvTˆ ç©0*îfãq Û'ìwaZ9ǧ…÷s^ßEOJ±žcÛ¢Àþ¸ÍW£´Ã¡W›9ÆBdÑ3½#\o‚,ðu·§Éä–$Ð}íGoh6³žmhéº|aëç‚<{åà~’|ŸËmŒ4+ðýγ=ÕPT^8ZÝU%•­s¦|úz¸ë÷d2»èù ^0ÀöoS_-yruœšw§ ÷Øßì\ŸÝƒaëHëx“â<¦¶ÓwVÌÎÜ×yJÞ„KÜ×4ʧá‰U×ÎÑ }àæÞØ¿r×mv+…8`u‡õM%ÔRÈỄ½¿­¿Ÿ Çyøs¶¬Ñ¾ÓËXú¶Â¹WäɽÔÀ©z½c7°?KM«±fª§áϹëO¾®pÎÞ?ü¾X÷zT88ú¼Û°ðxZ3cX›¤^+ÖÌÈËÝÊaÑ›¶÷…S~ñ¾óKhÉ„7®¥r) ÿÁƒÒÜ«±P?ùç÷‰ßWÂqª[™¼3ªÝ¸ádD L™w®ž[v.ʾï#ÎîÂN¼\Bzîõ¹útÂÖ9ì~Û¯bûaìÜÛúQ‡ó$ž{Rè ¾ÿîÈ š¿-N¶>TÙ&#êTâvDûB5Î]]ô¼Hb.t ]mwtŨ;ê~ÿÚ±!¤øýÏÕN þQ‡Y<²ºÅîã[òǵ¬”Ø?|÷áñ¨ðhvûÙÆØ\,ùÐÜw ¬*)Ý?3?„¬í3èÎêӅõû¾ûÞ”%Þq¼¯Ëk¶˜;-Ä– w)°aä㛣rÁIvùU׃!ÃdWÒÒûeÓ„>ŽÕñRiK]¶çÐúZ˜o÷]ÔÕ_ÇÅ€$oÚí>^)ð†[öEäÂ×§æTü9_ ¹­pÓÝ´¬ë^S…¾ß5JØy¿%Žq¼‘Ï]n ì±÷âƒwöMщµjhs¡çiÉß%Ë„Qªœþ<ë¦{﹎¾S…÷3ÛOäûš'’G-+oªµûiÿšß'Ðà<³,1ÐòX2ÒH]|8ÄgO.t/ÝG»¼|9ë±û-½g1Uøþ{¯ðù˜+ÔM\´®hkS­À~çÙÜzQßÉ7uðVk&u¥À±rá»¶å¾2+¶Œ{Ô†žžÜÅwŒ–®7߸œ1‰0n<»ÏîÓòýÙs ;϶îŸE8OË1“KµwžiH†—åæ8¼<fE–zqáhwà—ãZâÕiû¢·ý& ÷`Ùý+v¯=øE‹}ßâ_ÓºÖ¨À½+)Îó5sy1“@nÄçuŽ'ƒmFmU¹àýe™lŸ¬+ðUKÊWH55=<™°>‰ë±ºùb„•G?½ÞkÖ}¡ç¹YiJÝÏ.¶G•‚voN†*¶WMùórafUÒ©‰Z!º.k½kª°ÿÊŸÔö_Ùþ6¿OЬ¿÷¢ÂyºÔwr+9ï"$Ïùkø¾¹ÉpL^íÕØ¬GŸ >® 4²^\JKVçlõéúpš°¯Áî1°ï÷ò÷z [X? ÎS#|óîüzá~ËÄÑ£“áä&ÿ¹PéCüý¿ûônµ&~BœmËìí}}špO†Ýƒ`÷âF¶ÈhV'œßÿÑá¸×æ\º8öÑp†jQÓz'ÃñVËoLž’ £7T:Ú‡~/$„xº· ˆœJØ}>_l¿Äú ǵ”•Ë ðS×…ÚÖÉ`Øù®áå±¹°ëÈu‡ô\P\_ONiCÈ´{® ¯Í›"|¿šõuì\‡Å“%lò¥/ ߉¿rÖø}«_±v2”ËM6Nš ×WHLyãOÝò<šB2n|ðztp²°È>‡ÑC®/SæØ7ḓZªß_¿aÜòÒ!®œþR­ìÀ\xß!ªÏÊ7ƒÀÅÒ˜‡Ccn´z_o²pnËú ¶_l}^ŠãʇLô,}LUiôÝs B\¨­ð›pÊf;nú6¢DY©Ýóàú$ÂöiX?Èâ=7öžüãôÇIeóïçdCoƒ Ï&ÔPo$Ž™N=o54Xv¹ÖöÔ<²[5”ÝêAy2\03oe'¨¯£ÊÊ\û Opk_Ge[«¨÷-ó9ažmÖÞ·ÿŒ¡àJÙ„?{‚36!ǶŽD‰0‚P¦j<›PgåÄØ[‘¿ñ½å¼P‚)OFNÙ­bÊÝâØ ²º?˜Ö“Ð@=STœJlÅ$ô Þp\bʬ<)OÆ…z©X{ßÚÿ‚¿õ+†+óF2Pï[5MlÊ€rÅ×ü®µe Þ%A”¿%¦žà¿b[gS¦Lå¸2~‚˜z;fS6áŸZù§Vªlþýj¥=ý½Òmxþçë@ýÁ9¿')e¸Úc°*b¸:PÿÛlÊ(ÔÒ –QvÇp•£ôÔJE9…bÊO`¸¿ðÀµö‘K§× T6Ê“ ÂÊÊÚßòŸù„K)ëg\Æßâ8®:” &P0Ê\çoé­X׌5£û…¦%væ®6ÔCŽcºRÎ ç.·â·R~«“0ˆó˜B¹Zq·dÔoŠó—[yÈ©(;c¸ýä¥éð Ö̯X…Œwͱf¤˜Ô”=&¶•Ž’b‚kÿÃU‰JG¹RoMŽ5Ã1¯ƒÃq5R~B$e2¯pWê#güù[ÿîuòk”Ûü©?×GúÿÛSþVz!žqÍùƒ+)»ÕƒScÅneü--e[3&¡˜úãeS~+ó×”Q.¡}IÞ#/’úý2Ÿ:ÒŸ;Û†çoq>ÄãZ…2¢Ü)»Õƒ5ð'v«#õ 6RFaeÊ)?c·*P”˜ú¡sœBWÊP`>Ä‘¿ð!ŽD‰0èƒ(Cã·£Œ(L‚H”#&‚ê'OQæžý ôlÎW”ò·~ö!fü-ŽßªG‰1Ô4‰Ô‡˜q®cæWž£”+e·rü%år>¤jšt +n«Šr[]1 ƒQF”ÔŠ»%GERvŽS¨C¹PâlÊn þÉ·Ôñœ™_± ëšã̸cRkQ˜Ø¨l”;&xÄ?`·¢²QRêÊqf¤Ô‡øWüVå(è(«0¸ï×α ÕÔ‡˜ãoq±ÉýùS+ÿÔÊÿµ’«“ÿ•)BiQ”Á•]ˆç[GPÆL e¸J1HµVWÆßŠ lkÆ&tÅà ¦¼Žãª¦,§|B hJGý–U(ãO~Ë*”åÁIÙÏU‡rÄÀW¡ŒÔ«ýgÎ c:ü‚QÈyµR×ÏÌŒÃÅñ\ÍÜZGS—ÍO|ëàß0f¸ÄR8ó,WWL0 e*)›óhWSŽ«;epqWJ‡rÄT6à9ƒ+˜z´slB5ÊÄíK6â=’(ËU÷cÆý|k)&®å€ÉˆÊFI)ßšcÌ¢ÒQRLh-Ê“:•ý–+ÇáÒ¢0Ù•”1£ü Ç•cWq^Ïy.¡Žr •(=õ}üÓGþ©6ÿž}¤ ý¹Œ6<‹+²ϹB™P”áêˆÁªú‰áÊq¸‚(ëšcFRF¡‚²,8†«•Žr¥~ô§PJy&” ƒ\‡a ¡L(¼å‚ALyÇU2¡d˜:”!eDy`BDZyÓáMoDyP—“%eBɬX\ÇÕ€rÅÒp>õ”Å•nż¶¡Þõ”+&–e/‡JGI)ÕcYRN¡;eõØcÒ)­ø­A”ß*Å$T£L(w+—¥£¾÷«Pc‚S–ÇpU£L(&¬ŽúßÿÌôù¯1¯(Lê”#&¶ eDy`‚Gþ†« eD¹câk¨ß<ǼÖü†ãÊñ¸8ž…ž2 Õx|΃^ƒ2SןZù§Vªlþýj%÷L#PŽ˜*”±Ï»Ž¤Ü幺ÿÄsåxc¸‘”uÍx…R b5åvprD˜ü”Íñ+¶+Ç F™;òÜB=å¢ (î[î8ijå¿k­t¥óšlxNš®Ï¿æXFë•ò]E¬A?ñ]9FZ0e_Ë0€u”Õ¨Dé)ß5•’bP«)¯Ñƒ[ƒ2sç9äz” z0ÊŒ’cÀëQb z5ÊD¯”™;ÏÁ$У\0‚Q&” B‡aR¡L(LŽH”$eBÉ('Í“%eFÉ­8iã5%ÅÒ¢('-ÛŠÍ1‘”¨t”K‹rá¿Ce£Üy¾«#&š eDy`ÂiQ˜tVl×`ÊvuÇ$Ô Ì(+>š¥G‰('Ò€rÅU£L”ïªA™¹sLX=Êå\¥_±"Û„’aRG¢D˜ØA(J† ®û|× ” åA¤¥ÉÏq°µ¿á¼rA2 \±0hPfÊ“ÔÒ"!ûηÿÔÊÿ¹Zù»:ùŸ©‘ÿ›ê#÷"Q" Æ ”©Ͽ֡D˜A”]ëñ»–c¾É0Xu”}Íø‘™²lµ( b%eHŠ0˜Q” u0ÊŒ’apëP.àÁ(3JŽ®G‰(×Ö€rÁ F™Q2 þH” e²âHŠ~Á‘ab¡L(&ˆå‚ILùpUyæ­=&Œ•Ž’bâhQ?ñ¯9†¤=&’•Ž’Šðß¡0©QÙ(wL®ÊTQ~¤”;ßFÙc²Éêñl9Ž™ˆ2 \0ùT”3§A™);RK“QO¹m?×€rÁÄ F™);Òš{퉉a²¡L(ʼa⡌(LàH”“8èŸpÝ"Q"Ln*å‚I®B~ÁÓåøsbL|5M~Ži ÜH*%žG÷§ü÷­‰ÿ?÷ît\3÷ù`pêQb P5 Ryaž±ë‚ÁlW±+ÆÀUÓà•£ô”‘ˆ2PÆ® eD¹cPk('Óƒ[K\2 Äèjì ”åŠA¯A™)gWK@2 Ä˜j”%DŽУ\0)‚Qf” “C‡rÁ F™QrL=JŒÉ¢¦ £@éP.”³›rÇŠ@9b©PF”&“å€ ˆÊF¹cbE EøïPF”‡3ÏØa¢¡L(&\Ê“NU÷_WMùº˜„Zšˆ2”%„ DP.”Ç™Ž’b‚jPfÊØÕÒdU  (1&m0ÊŒ’còêÃåäXãÁ›%Ǥ֡\0±ƒQf”\ÿ»Á(3J†‰²wãÙã¿áìÚcAP¢ÒQR, ZZ8v'ÇÆä¾a(>¯¸?ÿ¬Vþ+u’ÕDVÿ~Wûþ;jWçþQû¿­oÖµ«k¬†±zÅêÓw]b5é_©?Üï¦C¹àC F™ ñ,o=Êp0JfW»kFÉñë)Ã;˜»CƒµÄ‘»c]’ãƒ×£ÄøðÕ4Ü}j 5 9J‡rÁúŒ2£dXt(¬A(J†õA‡rÁúŒ2£ä@z”ƒÈäÄóu°.¢²QîX"PŽVüm-Ê.•rÇÀ‹@9bMP¡Œ( ÄH”ƒ1厒sgFî~ æ·‚»Á©nR›mBÉ0Pu( Ö`”%£Ül Ü`” %ÃÖYqmÍ¿aïêP.®ÿ‘³k@¹b€kPöäÊNú™?ýÌ¿g?#£ÿ˜ ”努AÙc*P”ƒUMV2 \1p5({ ^Ê€rÁ V¡ÒQb æ ” åA­¥-CE ì1À•¨t”+ºeÁ®D¥£¤ôZø2TÊ@‰JG¹b"hh2(P”“BMCŽÒ£Ä˜ jš$ ”åŠÉ¢AÙcÂ(Qz”'eDy`E¢D˜DA(J†ÉrÄ„R¡Œ(L¬H”H„ÿeBÉ0Ét(L´`”%Ç„‹D‰0é‚PF”“OCP†Š@Ùc"ÊQz” &¤ •Žcb¡²QZš¤2TÊ“U‰JG¹bÒªiâ*P”XM“X2ˆyθš&´¥G‰1±Õ4¹(Ê“\ƒ²ÇDW¢ÒQRLx5Mz9*åàÆs½#QöX”¨t”‹å€!•rDzÇâ —ð,rÎY^|žÑü/ÕJÖÏü+µÕºŸë«qÿ™æ¿«a5ìwýË¿Z¬kѶùWjÍúsê¹Ï¢š>HÊ€ãU£ÌÜz ¬%Ƈ«¦X2pk%îîwG®†>`Ê€rÅ­AÙãÃVr÷y±h¸µ>t=JŒµ@’c-Ðsk¬f®&`УÄXÔô¥©@P®XÌÜ} Ì{ʈòÀ¼D‰0ï³Qî˜ó(G *ʈòÀàŠD‰0À‚P&” M‡rÁ`3¢<¸{ºpznÎÄÝ£ÀVrwn1ø44å(=JŒ9«F™Qr J=JŒ©FÉ18õ(1¨%Ç|գĬj° ”効ªAÙcë¹ïfaðjPöÀJT:JЬE9`0r÷h¡à=6w«ïDˆéwh­çe}Ò?Ú'ÒÓïЪé} 9=cÓ="îÞš#?›Þ½ˆ 5!w±üyLÿ·Ü&_Zupô®ßÒíü¬oÏ ?88 ®³kŸKý‡L’;.{Ã̳|à܆šw*¼¯gaþOF„Ôø6‰XsŒU8ϸðĆ…Z\€¯“çäÌÍ4Àóï)²ãÍráöýòªE½A|í5.!]«Èä)‚/ã×&9—$¶À|·¬}Í58¾ÅÖüÁy˜þÊ´»p¢œ”[7æÔÍýš­ý®¾ð‚ŽMA¹¥–$¬oÐÔyºàËÃ|˜ó}e¾Þ¸\8ÏÆÚ-?üëÌGZt•tõ‚u=/MzBÄîŸN­þ¦$l|öù3Nó¥c¾TÖ¾g"œÇ¦FnbɈs”—cµÊnÒ­o9Ô$%¾ƒÎ jõ{7ö[r™Ñ¢è-¿/?ü úÒ=£œØ–ó´å—ƸRœg³†êô8a·›ÚŽY`€¤ Ò NïràõÀàüªÞôç !ׯ\׋üó eþ'¼ÙKÁ÷ˆþÝÀ‚uÉà}6å8ïë@ß…ÙÑ0­´k=Îóu¯o{’Ký÷•ì>†M¯ CH½+þ j•™!øÃ0Ÿ ÞÏïõ½³…}ö¡™ƒ%°`c‡)µ[ñœÎ#í38â¯ÀhQ!¥êÒþ¶o,Û~?Ò»'®µt¼‘¬ËÕ_ !wÚ»¦ÿí8Cðqcω÷I•ð¿• |×xWÙ¤–ün48O×Kû½c+DÃ-YìÇÞ_'5Æ_p¼ºjö ضaS!dõÍÄU[üó]aqÇÇuºdÀCÇiãš½£üîÖÀ8¬–üÁy\ܧ”5DAó}ÏÕÑ Uôôý;WràXL©Û§v‚#S8OˆàÏÌò‚å+ó'ãã­0$Ô¼s¹ÄÂ>4NZóùƒólò™ßõÁÉ(Xì>´S”ˆúPÉäÀ„%+·1ÖFM.þQB:xÏ_jæ'ðnXd\ ñ­ûµr=XŒro=aÙßkÅEvàóÇ6_jÁh,‹©¢Óüí.˜?w3ƒ–œÊKûòŸUzÂë32Œ;CˆýÌúSÃ^øægÄâ€ÏŸÇÞï±(õ½@¹ <—V„ó,?azwºY¸½–ŽÝPßkºö<¿",ž:~ÙŠyZÿñÒÝ×ÃBÏá˜!øß±¸æ9“ÿ;þóê&p˜-ùƒóðþ­‘ÔÔ6‚‹½9ðáÅ“ˆyç…ÞOi†àçÅ>7ž§p“ú ælÍ›“ã<-‡Ü]4¶}$|íÇ  PkÝØîqê˜þáºl½X0ô•´¤ýœ ÅÍmfÆcñÆ8Œ·ÀûxÖ-P¯U8Ϩ{úV8súFUQW2ÀãÓgm2‚s é)ÿEîôƒÀ¤Å9öNZÒm fÀã^3„zÍ|¯˜Ÿ«åª'ø=[òÇwÝÓÞ§a§³ÐâÆýe]ì ÐmâtÓòU9à·¢IuOŸ~p-©t¿Œü·Àx-ìóâý²(à•„?0ß]KÞà<¼æî%:yÒ˜ñËVÖò\’C,ÆÉýaR¦¸Ä’Ë!$öK¥–ƒ‹Î"¼·3ðþS¯¿%¾þõ¤¼%Þ9Ç/ºìVܶUgàÛË iד Ñ̨ê%ææ€cûjÛ*ïð|yŸìÙ„ù*²>ÕæOXÂðëN}Ñù8¶)œ/mäÖà½Ar’}“s]Ï&AÇfaÝ»ùåÀ–ýCKì?å É޳Ž\!=†néö¾ólÂüYaùoxxë…¢iQºc¨óáK] NÓ-gëìlËç ÎãuÔԬʫÓ`wxйú’`½Í•¤½ràè¢Ûn|ò†[ñ¶#“„m8PÙ,Âü5Ùóáû Ã÷#m~ˆ%_pž~~jïiØæ6¼r…¿“À¦šƒóÑa9×3~³û¾ÁТ”lF¥5!¤ïÙÃBã~ð6Øû€ùòü™”ŸêR?„ó)f;Þê4,ùëéýwÛ“`ÃÒ©µúyäÀÁîµ3ôU}¡¨Þì©ÓCH^øã1‹g¾®¹“f ó«´æªpüYvÊ“ÇOåcßšCµ=>nê’+ë¥ÆV  ‹Ì_ÆL!½–fÞ‰}3Cð?gu“ñ 7³~ûÄ&]Ž·Æë°ä ÎÃûàž‚k{-j¾< >úëÆnoCùzC¡[‹9…¯Í!éQ[õï:Sà€2ÿ^¾Ì|ƒ'»ß“ßµà¯fÉœgO åðuN§ ‘³’`ÛŠÆÇç7À÷ÙJóʦ|¡ûXYBµí!¤Ø‰Ý¾ fÍüõXaþ}<ïÎlñýÊÆqÓ›MumRúp”äq““ÀÍ®UŸª90¼Tó”´ 2È*y­D÷èÒ¢{Të…f õžÅ/ËÆu`<@k>‡]¾´PfLÐÛSðA2hyÑÑIð&5c‘W™pwäË_Ƀ`CÚ‘Ý›î…ÃMUç”Y?øà”óÈ81|<Ù |5Æ%°ä ÎóDÄDN‚[Š“ãœ!IPÚwÿœ¡v9p#¯]ÈóhOÁ/ú8—–f ¾¤,ïYü2.³%/pÜ®GSOŠ6Ÿ„­Éú¼J’`kÒ뎑ŸóÕ3wÈ7y~÷Ž[á~å&³óãeqÉ|È~¶ü·/å¯à¸}fèŽÖ? ‡WY¨”$Áù»úÍcࢯ†ª?ôÉ•ÝTVK6Å5Øå.žE˜Ÿ$óm7)sMÜ[£D7o_ùK¨ï+Ž+¹6âÌ®3'àôÿaï< ¢Ê–…b@AEL˜Q$˜FŒƒŠ]-Š‚£€ˆ€3:tTØ¢"*Ž(¨˜ MÝLà n̘š&¨¨ƒvÓ¨€a0‚ýëôÙûØxÇ÷Þ¬ÿï¿ë-ïZµf]]îê>]U»ví:õ½¿µÁ2 e.LyV#R7xGOIg_¯5{#§yéba?bqƒ·óW¢FŸ¬â-îR®#®k9,xy·áGᦡOÑYpÜ4wQ—GEðÓÒ-S΀F¯fÍÀý´O›9)÷g.¸õl®;Ëßù}µ‘Àן#˜zÆux>¤íëXrþÿRÃ,˜“NìÜ Š Côè¾{v¸Â£Oš'DNãé×ýçwØOÓ—Ž#^ŠØÜ]v.ay¤ÎþQÏó·/Ö—ïOøŠ=ŠžåJ(­ŒÉŒºU­:rŸÀ f)F:÷Àü-=„D½„T{ØŽÎÓû“Îi¥¼Šš%âß^¯+NXœ­çK¶þ¥„zzZ(‹Àñ‹ë^§+nà²Sb}>RNóè!^³8ʸg¿Ÿ¹™½uö'¿Zàº;·÷Ñöœý3ŸžÛ[¡äy)—‹ànðK§àÓ¸¯Áœ’lO9áãuaŸ“Ù9ã©®M©ÝïËGݺb\7òð`é‡Ú)°³Þû™µ²à‘Ùî;ÎAg«&w﹃“„„çÙÎwSF6ç’qôÿ€ñeuöë¦wr_{îB2}âëïÈóÇä¤@·3Ì=€0Þ#³–OòŸÿ½èF7¶ªßKPÏÍ’£ñù!É ?ß$ Îu\Ûðp¬yé~mìû_`Êžƒ‡—Ãøþâ_7ÏRà9±yÊl¿gç(ýùêRÔ³õ'nPf2ørøžFYPÛ›¤Ï)¬"¡Û0ಈÆr:oÿ+׌åYl¾>ãæéÛM®_°9³æÌ²#p¡k[|^.ž5vA×O–õ2r†Ò˲U‰r²nÇÊÏË;O²xËæÄGæÜ<~,Á@˜ θó:»G=/¶¸y™xE&qV ^®· n/ÞA†ÀÑ|ë&ëNàùU7·5Pà³²¹ôlN| ËÌò`sç¥?çÖ V‰xõÑԸחÃÌN-_¾RBÏìîÝ囊 <ôÅs7 ÆŽµ'œß…ß'kÚ‡zÂ\IÆâóáT!Ÿ`y·~=Æõ¬ ½zyy×ð/bÍþI%JØ"7>¹¾'Ýú´s£3,M±ì:INLΞùU ÄQ–_³ù³R«°àuÞŠØyCŸ{"F=ƒ~6º 9˜ôy•ÝW‚÷[Åá«‹ÀÝäÞÛV»q\XGNk_;'@àÿ±çø||¼ý$bÜ–ßèü¦ó›ƒf1nó‘ëJX“±{ÎýeEô¼ì" ’‘ÇJ(Ì!gõ6ÖëQô [Æë±ö9ß žÌR¯,—¿Pn‘Vôßh½´øùÅð`€¸üÑ éæîá[møRÂxvìó²8Åó•ËÉý^'ïD<æËG†à¢KËëË“uþ€ëÓs´¾úö>T‚ñ²Ôíý›¿äbÔR/Èÿkõâý˜Ï6‡uÁ¯ƒÎ:;?°ß™ñaØ÷Ðù®…«úHeàì‘sAUª„›£Î§eÔ-‚ê êµ7â #~9=D$'‰Þo— ugö=XœZ4»µqÆÛÚdžÅe? ž`LÒcV샩[z yª„V=òšUÁñ‰3RMFQ;‹IŽè=}ìVÆ[¨ÊM:+Jê8oY’q¥§OK稧éC÷Š9­÷BˆäÅõ­Ï1Ök½yr¹”Q9ÔÆ£àÔÈi‡våÉHÈμ-ûv öúí\f–çðõÜNWIçF%âCÄÊàKÜn0üù×2%ŒŠp)ý¹ú5v[5éã(0hün¼LFŽÎ^(]$äõ¬îÎxŒ‹ËxSìïuþzÂæç&V:ïjœÃé'JhZêp[ Ë›žÕ÷€f¤ö7ý\ê–ºB¤ŒÌ²Ÿ>ïlÃPá9Tåy<åj=Êæwæó5®[l÷ù›A–g¥5¿ª„ÑÇ—‹JÔ‚AÄòÚͼÀyïÜO«¯Éˆw»G6·D¡ÂþÌò6ß›Ïéý:%âëg;G|9SfL‹IW™SuŒÊãð9œv ztË~­·ï]û{22¥Kn·†óC.ãtóûe‘ˆ×c ¬Ž¨ÏÕ°@=:œš(l:—W?¦ëM}j­E¿ærÍfñ(jŸ2r½ÁøGuV„ Ü\Veó¾µly½f  o߸n˳+k 6Gÿ’œPB£ v¯^¦…w²üUNyÀÆ#5<¦ÜA»ó´ð\*prYæÏ÷D|þP[àBU±oÔs눮РþÛDýÕ˜·M~¹ßXió©>® /ùüq8Ìöø*# &q_ã:ãáðõŠóçV¿žës¿bãâß@w’“žAãë~~^7??úòaÝ8pwÈâ~;Éêîìþ?‡› ~¢Ï_60.Û5àþÅX _þ®T›m™]Ì+„ЗÒ^Ý ½ û‹ZÙc³ñû¬»an'êí,ïdç0þ÷oXÅß-p}v?Z<»ó»-YÐïžMŸ[W áä$mâÙº£ç¯ËȆИéç.† yÛ—å—;Ü‹5Æ;cÜ`Ÿàú<×cá ã¦²I!”ªJ–þÜÔŒ~¿ž–´LF®›SÝäz¸`WìóóùØ_‡ý"äMú¼Q_ÔßÇ&“ d:¼Ê‚Äç]nùž(žè Ѧmæ¶J‘‡“;K„s ‹çl_±Ÿe8Ë@ào3n‹ÎOPi­„à[æÃÇ¡5T`ëÒ£V÷…wÜ.¹g§Ñ`YÕ13KFòtG"œÃØ~É­èYÃT©‰ÀëbñZç'¨‡Ï[‘aº‹Cøk}%¿K á·¬Ië|ì¼€£@îÀøµ­æ½Ö&¡.ÂâÿýHÔôîo"͆zÀsˆ×÷ÔS<üžge0™µ¾Yަ p2³ÛTg›¬®3»Â n<Ô'ã¤ó$îAXÿ «ó±þSk.ͯÛÙ ö5áxA=‹l¾/œ,iž-º« ëgƒSV‚¡<ªõ•7Lö qļvò¹JIíZ¡ÎǟǺÜQVwë5|nÒòÓ"z~æû! LJÄ?öïþô’„tÓ,T0ºÚ¡#ÓB aá2ÿýc| `¥Èçr¤ŒÄ¯n¿wÞÌpÂøOm»ÍÛÕ¹í‡Èì€ÿž=…¿×ù ê™f‘õÓÃ2ö®¨¨«· ^z›•ö\P(Ô-r{xü•´CFÒ'nkŸQòu?fuW–¿ –?)ðq·þ\ç7¸~“¶‰1;S—‘„ Ž`®‚)‰k6L/«ØQý/äyÈ- å)¹bÊ BÎo¯…|Œÿ>ÕÀÙý“ùow…ú·ÎoP'h´œ`R©=¿L¯ö›Wl[|½z´püÜÑÁoI(aΟ´Ä­Í`Eß žÊ»ëo˜¼’ŒÓ]ô¨`cä43p/„¹“o oë?Š~/yh^]25+Œ°> V7fûJÃI››ßgÜóUú•¤¨gÅ™Õíún"cÖºU³KPA“n¾´C AžU?ípüHphzïnŒ/ÓK6·. 'ì<©Ã¡Ú·ø\,ayËÛt~ƒzL5™ûûok ¯«‚ŸM²d¢Bxë1soÜeWX²?$ËyÆMÝvþÕoX=†ß/¯ˆ®>œR:ª§ð}ôë–jÔ|øÊÀ¿“šä^e{TÐÌjÝ’e?‚µm¯/ õ‡Áp­SêGȼ—u>¡ÞGûøû›Ï¢qÆÇTÊYÖUêpõJÄ÷ÎrïÕ †¸Næ*±*ص8áü¸î…PÒºàPöPÊ;”‘«Î¸g,‘ù?ËÏôÄÄóÞ&~ñq¦S•|Ùõ,¸>Ô#çB ¹Øa|¦eš \lÛû-è\Hï-…{•ÁÓì*ögvcÏÕ7§Y?N‹QÏÅŽqÝü6Ö 2kå\RAAÁu­ Á{òYkÓ_À#õØg9‰æ òÁØ>Æs÷ i¾T[ˆ3ìžYç?¨'÷—èé¬6’Éš7Ûæª _ÿ³S޶.„çÍYÿÀß vÝI¼øDFYs寿ã–1n:ÿœŒ¤þý‚õt01Ü<þÒF¢»W«`ä³£I5šB쀑†!PÜ£w§=µû¶_´Æ·eù »_`uLý×NzÜíÓO Œ%Ÿ®8.2j• ó’­kÞx¥IsLw”4 Iz™^—‘ë÷5˜àÎò@3!aõ@ÖwÃx úy‡Aý±‹KƒAbc‰ÆÈ£ã’^Ù°±šS¶â‘,+Gvõb å÷¡ás&L8÷³þ!>Þ¼î­ùz‹u•z¿êQO\w4,–|î²¹ýûaÙpa_óÍïj ÚÙû§xø€¯…QÌ®©rÒ¥Þ'Edeaç%V§žÒBãü1è=í¿©WþH[W«ÌºJ¤õüj3̱ýÜX’3Ôßrz6Ø>ÔÿÆM ¤DÚYN^1b†¥ìY “N—{ú‡ÿK?!ëbùšóžê6÷3ºVáµú¢® wï|,ñ;ÝKc漸 Ë–ZºFxýwÃŒ¾à$êF¬ßï#{-ô+1NŸÎ_pýá—.ŸþÝ>ŽèÊ—Û²!Ñ¿åQët |\éw6îº;Ô¸Þ°GÌL9=¿J„ó?‹‹|}󶈷ëW"ý>()®¯¨¼í·"ŽÌ;Û°àØálgúå^9ªWjû~¹îàÓrªÍér²"ÂãøÂnáÞ‚=V?åëu•¢Á«7;$lîV¥^’z¸Ý0ázÑa-ÿȆ›ôNì¸_¥¡"+ǰ‘4’ëý©µNûš×²¾[ÆÏæý°GUÞ0®?O× GŽv»˜|):M]w±É. --»ºô„äMïžò>MF"qw4ó“ù?{^¬¯“çšÐýº7èóÍ `~^^4y×í8Â÷×fCLï0·-èy« rŠƒ'|H½VP"#kp÷N-ù®«±ß›õ1ž¢ÎOPOÇ õÔäÂ8¢ü,>5÷a6\¼¡½õ ZéëMýà lRM0“£Ñ³ß‰;Kë3`öËîÃy¿y)ôêç3bÔ3»‘MyÿÆ›ÈËÓžsn<Åßøù•ðÜâx»N›ÑÐJe,ÎìœÌêçìœÉŸ;> ç}î´/êÁÊêä¶MįfäÁyo³a·÷ˆv»4PWÙ)´‡éh8? 4Ïd$VëZâ[.ðÇÙý»·ä÷3žG(Áu»ríα›Éy™rOá—lµ×dÆ„¹x.ÖÃÏŒ‚ö½ŸOÙ†¿‡o ×9NØ}‹ë¬/Fç¸^@dR×Å1[ÈÔ¤ÎJ£X4÷Ô`˜¦HIròëH™4~NS9éÙü÷'=¥a„ÅkÆeœÛ)ÇwäÑ:0®;¼¹½t_çxò[ý„:#Ìr`yç_Ó@¤ç»ê>«]¡lDîÌÚãå$_b´àѳPÂî9XŸ.ÿ¹KE|¼à9·j\w“u6ñäj[‡ò691ëÌŠ#žP©°›> ŠvÜ8VN>• ’? ø×ÌNص<Üö`™(ãÀš&÷ð똖ˆ/y©Ž_¼OÆì] ±Égç(守U4 Ù‹y”û¼ûå?Ûq„â0¡~Æü–¯ ‹X_9ËuöëIÞ7tÔ€­dd·Š^6Ýr`•Öór  œ›-ÿÐÏ ¦739|jœ,U65Û \ˆ,_ã?¿Vä7b@«yÙ†7\?¯£žs½•Œ½]9µ]¿H8VsÅíAÐüv£M½áƒ€ï«’“Öë›+¢ÃÃ…~|vþäó´;BŸ¼ÎžqÝ(çéñ3¶‘ Õ)ÃÉà87´~û©€ûûÛÈ(GP(>lR®’“W/2}æ~8È,/fçô7¼áí×Mr}Y›ÙÛ‰´Ò¹as¯x^vgÚº~h«8|Çmì xÓ§OP9i¯Ûð%Â~Ïö/VãûßòvëêpµV;ˆÓŠð†½fäÀdãµÛ§õÖ€Ådóä¢NC`®Wèá“S0žŸÝq÷°„°º‹[ŒÛÉÇ-Ú/‚ëZÕZ¶iÐÚdWjkP¼ÿù‚_J7 L¨åa>ìOè3ÀÖÖ©³œ4}ÿvМdÉ¿<Þÿþ¢\Tj׸/ó,w’z¤¸nÊëÖÓËWÛjàé¯ÕSvu…ˆ–\ƒ”ŒÜk‚¡H"܃²ÏËêĬ.Áê :ûnˆç„ùǵ‘w’Š—æ\ÝŸ±ž²øå¶ðK¯Ø0nÿÆyžŒ„xl·?õ5?`ÿåïžó}Q¸^å½%ðké›Z§üÚéˆMmbÕLŽoÆ7cOhâ½)Ô÷ÑZC&Þ¾X=\è÷gvÍêúÜY1®›žÚ¾ûÀz‰$«K÷Q×s@µéÐ6 „õ=6aW/ÀÃŽï†Irb[_ú°|~˜P§aσϗoU±_\—?(‘Œ_žŸ“‡rl GVÓ@«–©Ö^t¥c¿×åÄòÏižÍ/‡ õ8÷Y?ïç„c¾\øu%¸îý(›ô­Ù‰äUË'ósà¤øÆü·jˆ) øéÉ¡Ñ ºú°1(ˆÓ•mw¸… çÖŸËxÅü¾Å?)®ë=ÐdÑsc)yØ"¿Ó¹Âè¨xµ*ù©²ÍZô½ï¼})H§–·=›® ~/vï¼séƒëÃ4]PM`÷¼:;ÆõSçïsé(%òc±çF¾ÊMxäÆZ5´<èRíX´+\¬el3볂¼3ê=h¿maù&‹Ÿüs® ì}}ž³×çÓq)Ù•˜t§kõ\Hy¹ß¼â¾ÞØÊZx8ÃÌò¦&‘Ùå Î û—çÂÞÛbçÖ?¯³g³ñ™Ë?<Ó[JÞ¸ßiš ö¾ ¿­†ØîZ³gÓœÀvû•§¤•*§ÉË0!c~Îî/ùïñVTå}#\ê”× æ*¤dÓŽFÁ~msÁjõü…u²Õ»ð£xDÔ`#¿V=XA }š¥f„ ýZlŸeç]ö> »Ñ¿ï£žÝN'_WæII‡—¯¶Í…)Ý'·¾¡†I»í8¹:Áî~ÞíÉIî=®Á"L¨³²8ÈòE¾^ðFè×Ó¿ôE=dÖçZæHɵ‘Ür¹°+äñÈÍ—Õ°çië«â÷CÀ¿ý‡osåägóÒäó]Ã…>sv~gyó+¶Oëü××µ·dH‰÷’sêmýs¡óÀ˜›‰jߟ}»ùáÎ[š*ÈD³™NyƒÃ…û4Æ ç󇢵y—RÏm«¬¿”å_:¿@=æ¸7Û*%?]›ã2íjå¥GÿPÿ~š3 0åvp©©Nô°ºF˜]²:>ÿ`¡è—̃ËmkÔò ýûŽ Ô3«MF{&%Ž7c:­ñÎ….£"ŽŸ8®›×½jÞYæ éaçæåŸTß¶;ÿx5&LØGYÿ»¿áó„2û=عAç'¨çзѻ–II— ²Ì¦åBí݂ҫÁgÙ¯öK¶ [ŽW‹ò” Ò×ÊÂZãF㨑ЧÃúSXþÁì‹ßÏ¿èô4*¯9Ø;îÞZ)ñÑ Ò,NºíVÃî»o.˜×ÒmŸ%‘qqw-b£C„º »Ÿàß3(ì™ß—¬«ôÑù¢žÒÚ‹78Ž‘’^.]:$æ‚tÂ"¿¡R5ðyLè\0aæ ÏI„«î¤ß úCYcïÏðûó;ŸØV©JP_g’Ñ’føhsÁîjjWI¢ØïÝ´w|¤Ær?ygɽy,ÜK±}Š·«çBŸcØ¢Òk3—Šà×S=™<Æû§'lÕnq¢”Ô¨ñÛ›Š3ho“ç•/D=»ëGnš®5!ºëâ&û ×Ýeî"ôy³{6>Ήøsp-àï-€ùVÉ$K1¿¿ ׺¯ÛÌÜ&%u¥æcz\É…MÚã‡àsûeo˜máÙ¶ÄÃFº:Y“Dï=C {ï€Ý§ñõ®|ÚOù‘ÆÏÞP:p`DZ§ø÷Õ¨Çíè­Gq)É4ñ¼L²ráñ¼vwçîRÃÑ—¬B-ˆæÅÁ‚%»’È´Ž%Ë¿öo°øÆúZ{šŽ(}+b÷GúuƒÆxÞî±ßD;OJTÇÆ¼=u/^õXzJ„övzå´Ô•;ÍIJyáÚwIäÂNcË3ŠP!~²:+ÛøûO"ió‹µ—|²©Òwez ²¿,ì:VJšxzV{’ ûº ¡ ~_wvIeDAòÜ$b›1Òåk?»§f÷ ì”Ϫ¾$F=Û&”{u°–’ž•f/o¿Ï…&{Ïf÷D=üs7‡€üÁ3~òO"3¯Øfújù}κ¸¥èþ×þA¿àºžÑ3UªD’]¯àYƒ<u¬öØeø{D ì:9ÓÉÜÆYF,Àus߯Œ(DȳX=€å™üç0æ/ú}Ô£{}¶S"±]v³o užy"#A ® ¦vmó^oÜPÏÀð]C£‚ ûþ|n/ôU²÷&yû³«Ú'‚zª5¶üå±O«{42½åóEÛÔày£ìƒÍ xþÒ±ŸÃŠ$’ÐaBÏ÷3‚…<‰õñqø¡¨J=×Ýà|¿¢Ñ¦d€É!W¥{ø©7œ¯µI ï/=<ïu.>ýP3=‰¬ãc\°ß±¼žÅIýåv)(gÁ_‹OÛ‡r±êUáEù¯ö”IÃÍ~ô¥Ü.ýÙ¸¾ßÌEÑŸéf¡ÇY‡Ëf%±ÙáŒmÈØØ7/…cc£¸ÿÍÌýoßa-èÏy3µâÙØj:;<™r»$tö›;åZйŒ›FçãFQ;ebÿÓМ2±3(cÁÿ›¹Å”‹OçÄ9SnWeÒ(¨“ûPÖ‚9eÁ²yI Ê‚õ§³rÅtvø?áÀrœ5 ÷V27§åG¬ü+%ÿ~±Òˆ~/÷ûUãætàŸ¡ú£äWçÞ?GÛÿXS:'WM™4ÉtŽx VJgÊùR¬é7ó¢b(ãPL9 ”qÈ1iôç_r3¤T(ö¸s‹Î!Ø)[½9âÎßa2^6›“Ëø]Œ…R†âƒŽ”bK9°e(>èTi(èXQ(em«Î—’|g–8›1•†bN9°ÅtNneÒDѹS>”ÝeKgÊ1¾!Ç£±¥ü× ÊíJû·Ë‚ò_3é<ñÀofÊ•Q¬”Ίr§Lι}(Ö¨7?W3ƒri$zÜìdÊ9ä¸4jÊïJþ‡¬Cn¦x1 w2Œ‡¯œ…ÿÛ™rÿ?cäøø¿#>r¶ô½™›þ(ùÿ ÿ•±_9V—‚²±õÙ3 =î+ÇV°§lŽû*þfv8Ç44§sô8¾‚eê³gØìp6kÓáŒgÈq±óQÄè StŒ@”|1:H< Ì8]Œûúw³69î«:PÔßÌÚÔŸžü7LCýÙáÅzÜWŽÓ%¡\lÆáæ†GQF—:aŠebsÌÊ{µ¥|®âïð¹Ü)﵂² ÔiÙ¼> Ê|UѹáÊŶ£œ5µC™†;Åô6¶‚ÎÚä8]jgtüdst~ J1Š;4 Qzáo‚†šüø¯æt.q1åФѹè=ö+ÇX0¥ŒŽýjŽFî’_ësãç¥sLCgÊY0¢LCŽCÃæ¡ø£ä›qw¸&Š:„Š ÅNo~ºûw˜†Œ‘Íæ3Nã¿Æ T ø¢#e¢ØQþkŠ/:UŠ-:V JŠþŠ-Oúí<õ(”2t¼  Êå8]>”‘Íqh¸¹¥Ü|bÊèâfÇèñ 9å¾rNêK¹¯Çç²¥ÜWÇÎñ óQìÑ‘¥”±À±_Ô©}(“ƨÏYàø¯¦tÎi&7¥ÇÉN£\CŽKSlÏsºÒþ!Ó0 ¥ Ń„þßsº~ÄÉÿqò¿3FþOÄG úçFhŒþ(ùÕ¸ˆè/(¦h˜†Ü̯ÿ˜ùjN™¯£+™r±õù3Éz¬×|cîž!Á±^¿™×Îñ -ÐÀQTÜŸ‹1*’©³rÜÂd#tZ?åTpœ×|:«=Š2±íé fŽõ*¦üމ†bþ ›cϘSNW1Ç0D‡OC±@§B)CñAçÏ@±åæAëq ¹9í\‡.7“Yõ#ü_ÿ·ä¶ôs©¹çÆ™Œb^›«Š6⎆šö8¯§+Š2±}êpï×sïŒãŸé1^9¦…9eZpŒW 4òÀzÜ»%ø÷t^=Ç1t§\ SÊ1Ì7åúñÏPLÑ ͸þü7è É(¦èþ(ù(öM¾Î²÷ùÇ1±QTzœ.Æy§N䇢B±§œWΡüP2Qìбâ©sù¢d¢Ø¡“ÅüÍ|û” _t¼L[Êyå8]¾”‰m‡ŽOÑ2ºìÑ)ã©crÜ7 Ÿò](Ÿ+ó;|.;Êwͧsñ9ž¡EŒŽ¬ ÎÌ1^“QŒzò³ê9¦)åZpœWstr:'ߎΫg\ì Ê5ŒB)³ç9]ÿiƒÂMßtÇ ¡ÞF¹ÿýwÆÊÿ©8ùï#ÿ£øø#6þ}lä~GŠ)b`5næ8>4ÈdsCn¦.ÆÈÿ„ñjA¯Å(îh´i”EY?izlW5Š3explWw4hŠ)u eÚ6àÞ1AnÀ½KÀ³ õY?b4xŠ)} J>w7ƒÆo„Æï§Ç-ä¸×Å(îè i( £PŠQÜÑ9ÔA|Zðl2ÆvÍD±C‡‰§NãGÙ®vè<ñ((¾èD™(vø» T øþ ·0Å+¦ÃW¦«-:Y å]3¾:\>/Êvµ­Ç½¿ŸÅ]A Þ%ÅÜ”{‰{wm ÅÜŒë…ǃâŽÎ†bŽؘëÍC[CLj§Îáûn#ã_KPòõxhÿ‡½ó€nâÚö·é¢‹.ºè&4)¢éˆ.ºé¢ÓM]6MtÓ‚ )‰p…* 0¦%& AlŶè"4š íÿ;šsÆ2rï}ë•Üÿƒµ¾uƒ/ž#Íì½çÌ™™ýq¿«(D& =ó»*P&àZ$–(ÔˆàZ$™ ¤ƒ$[<Ð ál,éBh˜ß•&`(ó_k‘ˆvêvD2š˜ M¤´W—|ÔŸ$=óº*™Íýš–y]=@Ë<^`@";¢žävu%’:”9ŽTHn󻪑äf 2¿’í=ÏuBZ?à9úGŽHhPl¬@“HqJÿ|ª•ÿ×Jÿ:ù±ù¿UéþwUêöA΀`£++uVàó€´lí¤ dNÚ4`TО’óÚÊ‘Æ<´~–—öM ì`ÁlN *@ß×—‘wDAú~.b€9"•ts!ú.~†€wUú®}nŸ>ƒŽm%Àä爤Îë4`DBÄ@$…¤#uÙ½_ \~^[7Ð"aì@¤11·­Écg  Ü@«Æ¾4™B?≌$˜ ¤3Ç-õÆil6–p¡À ´H<;P ùLÀ ´Ìw­@"š€ôᘣ  ”Ì GŸçA²z€–yq½@Ä5 qí̉Œvøy®ãAà{žë8ˆÄ¶‚tê‡D‚Ç ’ÜÆ=¸ o÷sBÆ ’?âÓ|ñÿL üw›/ØvÓèþËB=aˆ¨¤g¥þÄq¶ÞŸ«AÛX ‡7Ð  m~îÜ8˜öȒܹyH£5Áîd^Ìà*H{àÿ^Œp¢ï™âg ‹Ð÷¦è;@ôýü;`@bØÉúg&÷|GÐ"q¬~þ\P"‰ÌÀ ÌŸ«DB™ôH,Pª@z$™%Z(p-ÎH:–ùsH@‰è nM$£x€Ié¨.ù6ÍÀ Ì›«B’š™7WdµÖD gÞ\/Ð3ÿfF"»€’¹s〠Imn Fr›™?7In g>Nšð¡ÀíçŤÉ Üÿ9O'õdjQì€v+."å ýó?U+yäu‘×Á¥þ3õÖºÕ¹§Çë­]þuŠ×¨ÿl}²Hµ‰~WõV"/A0f¥6‚õŸðîjrIîÝtz‹@‰g.oHÏM{ÌâgôÞ'^¿æ§ý¦#œ`ŽªíO€¿ƒ`«0}?ÿxÁä*”h™Ÿ;^›"¸âfé*ÉÇ+=Í”63У~8€õÃD Wc_ê‡ Ä ÓÆ‚3¸õÃÎNàÔËëZÔ;sp›€ôfP" Í@zæßV"ÀÍÀ tP ØChôv  ï“ è-u%·¶ÓÏ¥­amêÐŽ$€%A(p-’ÁH‰á ÷nݧùѧùÑßs~dd¿—N÷‚3h 6¤¡À ´Ù©s 1Š€51uð ÆÁk"Ð*¨Cÿl"õƒç¡}¢ñ³¼´¯/ÆwD>Úÿ?‚Ü Òé»svP"àC Ò~QˆK¾¤c!Ú£?CXAzaú.>¶ ‘ #1@‰ä07Ð"Iì@D1ù9Å­À ôHH£÷@N BY€#™\@…„²0 ±œ@¥Æñ`@’9€‰f"Ð#á@‰¤3Ð#ù@‰40 @…d´/FR: ‰i^Œu5’Ô<À€duÔúnp;H$°¤ÓgWÈq@…d6x FR›Èœá  ’<xÉî`¾r©³‰o $¿ ˆ@"àJ3ð ‚³™ä·/У88t’G<Ä)ŸèŸÿíùÑ¿Riý{¿îýÕœˆ×6ÿºÆkÚe=ûX-ûgæ?´ýWÕ{€T{ègŽ£û× Ò³Pg/âhp mì`‡7Ðæ ®7ox6õ÷HA Ü@››º¤€nº†ÀH§×cŒ@úl0]£G`¸”´Ob¤caÚ×ÿbi ã¢Ït“&¸–޵Á 'P!€,À ¨ JÔ3ðÐë'5þŽzàZ™(h& =j(P L@zÔP¢˜˜N Bpz€ê*©xéú:ÕD GÀ:€’¾†€ ÏîÒg,¿n EÐÚÙ‰;¸¹k ³ ˆ@ v%Û <À€wÒuqä®(u’ñý¹JØ»«·/-}жe£ì?ý¦­wäïS/ÊÑÒßïþiáRé{[1enÕºÔGƒõzEK¹¿¬¸¿%ðþ RÿñYB¦þÃçjü˺%m|¾ï%É’…ʶ[eô{]›v.Š~§º¾w}xîô1}VüôÇŸrß*º]=¶{|gÁÊÊl|ºßMÉVC÷Z³æ{ÈöåΙÇ6 “© c/E Rû ‹ÀûIJþÂr"&g‰kWsÈß×ßk‚í§ä触e½ ùq/’†Û4ßY<äV|Ñ£›¾¨Ëú8E ¦ õ7ä²È}7>Ù´³Â‘ûºI£"sz‘#“Âí®L1•?Ô`½piæ€ÒŸýp‘\J­“4ÛC–ôÖ~i¹^—<.¶}áÏQ‚¯Ý¹3£_!ï²&­úŽ|ÇsÊŸ›n׎힧ZÈë…[+v©vé"©v.g”u¦‡H¾–ħ·rD Û'^ó~–Ñ'÷ïâý¤¥¾iÙäþëþ~8Œ“àL-r¯çz¡žO8z‘´rç`˜‡l]×­æÁ}M$¯û’(¡dôÙwÑ_Ì”}>¼÷ YU®Æ¦}ÏtÜcéßçÄC÷¿w¨Ní^/l4RÑõE2¯}Ó9•§yÈ“6uëLû޵‰©…-s¢„N>±ÄLy?ñ>1¼¯8ïÆ}xþ^ú€âwõÚÍç,áA„3¿â|{‘|™<±NÂD9²m¿Qß§qNÝYèÏõQÂiÉè+ÏûøHßËûú¾ú÷/UcœV;FºAú¿'[¾Bsëó ³÷ï¶¥i´¤EÈ-w”PÿÉÍ Y×Ï’û±ò¾{³ÍMÏS&#¤>éõ ïGãË Œó¥©ÂJOÛ BƒýccëN †Î!©OB=äNB‡zßoÓ“rŽå’Ñ‚±ç©»•ûÏx?P¾ÿyŸTéøä GÖ½º¨¯Of­n:¶b4NÆi^íè„™Ö r9ãw—Oü¶Ã=̯ޜ¼ë÷ù¡”†ÑÂdå”}QU2úEñü–úƒþ¦ãýîj·|SZ=Âý¾|Á8c:ì)ЮÏzáÙüÆȚ ä»)ëŸì!“ÞYéÑ’ùÉÎqÝFD û°i™k–ìqç}€¸—^êïôTÇûŠûûÌì§®~kî¢ßÙ²²Ûæ°Æ d]«©¿|ÎûlÕ#½ç,·:Z¨rwÙFWfÊý!yÿ8ÞÿŸ÷[ýóƒ0«–܇З?ç–«Gã»uÂñØ#aQ­ˆ}Op…üQWjíIw” $…‹\L«-$?žrÊ:S>>¼O÷Êð:̽Büsøòã„V‰M_tb;hÜ»Ÿ:$XïiRq€‡ó4:÷㘢¤çëÔBÅ¢¢…Ýk†G ÈóGI¸Œÿõ1(qW_Þ'J± íVä®»®g×r™Vý=¬_ÒcªjÇ.wû:ZXðíôdzg2ox~¹žT¨ÚìµµpN¹ß¡0Ô|äw=‰qE•˜ßµ’òãlÐZ¦šf~§ÚÚ Dœ¿=jÆévcÍ„±5s c’ìûÁ-ÞÒôøÛ{¼Þä%|<ÞŸŽ÷ÛiØw[¹­ç;“%ûÓ‹Üh,å Æù²Ü¢ÞÕFÚ„¯Ç_ªðvp)H¶/ÈýõãWKëÅE=:VxÌãê?D ÉU¯ÖxÒ{–Àû0óú"õ9l@$_ó=`»bþ+Ň7´ Ç—W7Þ›@¶»5·¶ÛùLàÂ:Ys““‰coUy-¤.ß°{õÊY÷!JÛ=­“úe‘û”û{™-ؾzï^ï¦<6Ö78½/üÚûGòxW$Eæþ^¾u½áMÑçöù™×¯ü²÷†÷Ñ’ŽÛ Ù×ÉKŠq—ZXyoø:!¨„«Yâò²<û°—ÙûyÈ‚–;‡œkU[¹lt¡n1‚Ô|–Àûóñ¾‡¼§ä¯ÕÊž _^`û’÷´z}ò+!hühû˯HÅpÅúR}kŒø<ƒ{—¸§÷e•Ž{sÖ_Wò z°ýÇ…êVYí]+D;àÝ®’£GàUw1t(?èNÇÖäùõybd¿÷Úqߦ”ïYd_€4o’ú&¨îêc;¥ï˜²F°¾ˆZ›[H ¹Ž]ÓÉCέ¹“w̹ܬÀü—5b„¨#{òæLš)÷åû‰÷Ù—úz§eò¨±ýÓÍjŒ:Ýh÷UŸ]Ï&¥>2´õù}û¹Z¼#ùA8ç2©c„®Û^ïôLù8KýsËâc«7ÔܾçŽì ð÷,é1Î#ÚE=z½Z˜Þß%Î]fo¼Ôo0ãTí‘cÛê&«„=h'ª]¾<¶_‹xŠ­dѿװ÷Íkc„mô°×ÏðRñ>€|>$õ7{¬ã}F3õ·Ä8_©ž·d¥ø:½ã²?HÂs ò0ïo ò{…RÉ-îÇŽkýRfÉ}óøùAòžÝÓ幘«Ñ±OtÒù«z&o˜ã›w÷­]z…¼>÷¢ÉùI¹y]U5Òv_ÙÓ‡7"qa¯G—$âl÷Ïä}Çø¤–‡xikž¡!´«ìÀ¢±Â¤ ÎÇŒ#Ï‹2÷™»¦cõ“Ÿ‚2{é0N±I;6çˆ\.üôzÛ„ õɹ wF×ðlk? ª]“L?¶¥Täá‡ÊƳšgx}ø¼‡×Gîó[¯œÜ>aZPf?]É»úAöÉrl™`Ú“ÿêÜ&‰ä×£5W—þÌ#÷u+W"¡âV[ŒKE7óaÞ§›÷1çý}%ŸHÝL~5Æé»uï/‹.Þõ?óÅ }"™€­+3æÇYÛÔ"ŽRúyÐrò,÷ÉäóÞ;éͱË1½žëøy׿ ã\5\ª8ªÑR!ˆêAÚ&’VÃÚ­?YÙCîzüeÁÛMÉÍ¢Ô˜#û ¹—@êË÷\'ï5rÿA_¾`»+ä?¦Y"´ü¥J¯+IÁGe*xȘºÀ”ƒz"õ£‹$?ÝlïgÞwŽ÷ç>Ñ'S©½¾ì_ñå Æ©q,2íR“a@ã#÷õJ$%7w~3© ΃í©«á^Æ9‹Ì):õöl{¸¯6_Â|íéõ·dŸä§j@¸Ï×/gù¨oflº¸XðŠ-ªìšHêy¸[”ðqbÀ³›ÛÜTïQ VhþøëZê}³åë~!çïéfŒ[U0áQ~Ög´•<òå Æ]ìMäúj‹…µ SšŸŸšH®Ü¹Ó¬daÙ¼¶oõ®¶$hEÕCCb…pǸ“ŠÙ²O›Ïwxîu z9bcéßZÉyàËŒ³fIÿuÝ …ï+ÿºÔ±$‘4êÜzØ›¼ÒK›œÿÍø6äiýëÓQ±‚kuÙÒsÍó{ž—R_Àk:iù§Nò·6#Ü“ãË—RwõÒùe0ÅWèÉÌÞ®æôo;©¯x»·$yC~ŠËR+VhúnÚ¥ÝMg |þÉÏÃ<_¸ŸDš/gö«1δ=âãùÂä9Ï\ö'Ÿ×;«‡ÍuämÛ¦ãÓ‘ÿÜ3Ëûóóô÷ º­ºñÃN~Þ¬“É· Ç87ÅðE¦_æ ]†ÝκîÇDÒ·òÕR?¾MeÞÙ†$k 5«Ä°~‹çM>ÿæ>,Éç¢$ÜOÄϾüÁ8ªAû~Ì{×"<{6óì¤K‰Ì‡œ*ÏosO(ØR,FX~ùÍgqÓ3úÁòþ¿’70€¬y}CUY,ÂŽ[Lç ÆÙš‡Š—-BP·ÜO$•öô%힥’Ï’³îrÇÔ%’?,FxõUãÅ‹Ìû)óxÛü´dtà¡·:>O7–ùõõèùzÓ—?Ç+äÚ=ð²Eظ«ÚáÚ¯Iö…˪NK%MJÕlqoQ=RðqÁþáõcÚÆ³Bô ÷ûæý¬¥u»^)F¾ø®ÈçózÔ‘ýɾüÁ8='ä»6d®0 Æq¼~¾$²»Ö:ïâû©dãã‹×iH²qMmÔ5V¸WW–<®+Ëý:%¿ÇÝ’mƒï Ð Ÿ5¼=õT…rÿ[_þ`œÇG…3žÍ&fßY´i©$r¼áõ3ão§’Ë£öŸï0¦9i4¾.i„ïósèÍ'K¿-÷5åõ×Ñ[³mz1àƒ>߀Òwõ—h¹0W8ëì–8¡jiyªD£9É©¤âø.U²éÛ¼ gO§âø›¿Ek™=GöâñýÂÏÜËÏãü:Í—?G{¸Ãô¤KáëQ'žY7‰”ÿ¶UÖHw*< õO9:×”_Â~@¼]žTût¯ás~/oª‘=Ûö®rW×ùÚ×c?בë4¿Žöåƹу6nµÑn}Í“ý­’q©¤æÈm«ÏìFntíÙ0fgŒ@?…î—Ù‚Ôo;„™Ïͽ¥¨NŒÅ·L÷˜?¥óX¶÷Ëô´"¹,‚ä?N"?µûzPJl* žåÁÞ =ˆ%/-Å·ï{×+ÃçÊýÀÜ'¥ì?ò?ŒS¯_»^EZÏb}Áñù+^Š/µ!•$ É2°µ'çP3^Œ0èþÑô­Íg |]JÊïʬޤÉ}øý×)ìØþœQݶÜ,0CÈs¨û¹yÁId±¶îÏÂT²¼Œè ŠïAºþ^wM‘Xáæ±„&gÌ/øqå}`¹7Ö—Øî2º|Z:\ØFÞIäÞôI“R‰zòîäÅg»©Ïo,›‡Ï’}0÷{ó>ɾ< ûãî£iö0v½›DÄãel†`¿W™s{ȉ.dðÎÚžá‹be_6Ÿ§dî÷~‘ùz¥í”Aûæa®‚÷VôœDçýmWTr÷I·¶:憮«ZÂç=p$—÷ØLÙŸÇ÷‡ÔýL^ 5¶›ýmÚ“ÔùÓ…‡³®32‰l¿¶æÐ˶©¤RÔ7—önGŽ-WÿÈÃXy>Âç ’§ãµnHVj$dñŒíIžÒ)‚-uT½ã’Hð°µ5M%’G¯-Y2xqÝ»bi¾=KÎOãFîÌý* ©ÓÚvÿný|lTδ‚q^4-šãÏÖã…­òoS›“ÈÞ-ê|ë4©döåÎkׄ´%´k{¹ã±By%5vÎ’ë§”çÜ#ýLî í›f$U–âÛ—æ C…¦Û}&[’ÈÀúÚu¨’JƯkgxøg[²aíƒV)“2ê&¯—ÒõÙ#Äûûâۥݬ•±½„C%ÇÇÝYœD/šsx©T²¶Þ ¢O†´#êØË *Ä Rð™rßl~=ÃûYó¾Ù¾xÆvO¯¬;½V"TøÐüei¡T·ú±òpÊ“Výˆ3ë̃aÈCŸf§ÖLÙwƯ_¤ëî³:É×.}^OÞï=€d«jº7~s©ý NñkYRIðÚËÇOnO”O,½ÿDŒpa墷oÏx?iž¼_µ/ŽËbžõ<ç [§¤cï6®3Ž$VGñ|çÓòÃØ3QŠFíI´®Oÿï„!ü䬛‰Ûg¼ï5¯ ¼¼¿Díò~ëâ–Swº8“HýéºÎÓ½)¤Ú´J׿þ±±vÿõŠ#|7þû¦õzÏx_uiÞTMž÷rŸ÷Kgšï`éú|‘übI$´iÇÆäæ³mC|Z¹\±BýÏŽ8Ìý6¼Ÿ>÷Ûô/1®õmË´~(Å5¶¿Ð\ùóÍ¿'C”ÆÆûâ“Hÿ£a!~K!?mÞ9sÊÊDùËù/Æ ×'WCi!¯ðù…´ý«:É+þÄ·,Øîâ®ôÌ>Š\Ào¿¹”DN¬ûm÷±ŸSH%[ÁØÃ{›’Òs¶îÜ9–Õ¹ŒõQîUàçÍõŠ|WË!y„ìØîFÓî_úOGºÏºS¢Š7‰TÝ8¸oùÓ)äõªY÷ÊÕoHFæOés«e¬*Ð èŒõ]>ÏäóYž‡¾xÆvïî²/ÚDÒëщXùüøªÍâRH‡Ë­¾ûù›zdARíqÚš±B±Uƒ‹M6[^ÿäëàüüÛ$ìþ‰Ñ‹™çÛ®¡f؉äz–á=v¥%‘£e¦¥Öý>…[4Næ¨GR» Å%Z¬ìýà×Üc"í‡{²¿uã»>e_¨.ÍOÊ¡NgR¸ö͉äÞ:ÁN"yÚxVà`Æþ‡<÷YP¬þrSÕ Ù÷óuuî“öG>²±ìÎcO–ÕËtEqööcõ&“ˆo&‹HZQ]…3…,.ÿBa}ÝŒ™×ëÙMc…œ‡Ÿçxzb–À}yü:Žï÷Õ¦ÍÞX07©3~[–ïsÔËäkÔcŸ#ûù,møV…HJf»§íH!³oçÊ3½±Ö»žÕ+Ì.=äûÛ½p=Œ³ZÙ•Õeÿ<¯WÒùãNªgµ2­ó„`œŸkÅ.:Õ|ßùp­UyEâ›fmJ!óÕŸºµ=“_T¼‰ö†\_¹õ׌óŸwóû_’—*€ ªRþHÞ5•e†/0ÎÚý»Óˆ«ÏíØÃD2Á'¦MaëÄ]Iú»‰tåEˆ}EE˜|ý^ÍêcYy]wW«Ëcn7È+{p¹×Ó—Ç´¶šrÚÒ©äþ¬æÉ©¾§òÎJ!½BÕ_-ÓÔi[4ë¡aìý ý³ñ>óÕ÷aHyrùn•r=å^ _ž`œ<ÂÅs—N!]ð­Šcœ€ /¶B*Ô{>ÁQ½Iÿ¶ç’=c„á=Kþ¬—¿oÇçy; f]7öZ²k¤âÇš3*ÊþP_Þ`œWãµù¦’õ•?À~›´m\¤»O 9Ù·ÿ‰{­{ƾï÷ð—áëzR#† |ÿóý'ïd\ï3o÷"ûò§ü]½äõ›Jf£ú_),ãÃ#âè6)D¸ßjþ¡yÝIî?ó6ò4F¸Ùyö²._… ü¼Èï J~«‹:é>n>R!kÈguòz”/0NpÀéøÕ§z—«G‘¼½ôûÙlA)dúÞà™?œ &ûÑëXAòÎïãðù_¿ä>éø5’Ï'¾üÁ8ùM-Ô4œÍ¹~ØÍš"¹±¡SUWÅòàYûö£_w&} ê±B‘¶{÷ÌŠŸÁîç¯yžr¿È˜Ï–¨½´v&ÿyÆ1åœ#Øj…½ð(õ{­HþœÕÞ­X Yw/þþâ DZOˆ 5WÛRn†|?Ÿßø~S]~D¼+ßé¸:5?Ëä—²`œÔ.OŠp2æÉ»¨JíD¢=üèè0E ñÎûWÄmåùKEmíßä —×—¹ÿ’Ç=ž5ŸßÊ#×O~Û—?竪;ìW:†³ëZ‘üìæŽço’Ië‡boìjM…ô¼€zž²"ÇÂaòùŠ×ivßGöóu_ÿu¹8Œó®Â›çS=3ɪsÉ|ﻼ‡Ÿ$“‡½Ü}óÛé¾@Œ0¢¶),¦d˜ìUÍìó¼©Ë=´c›—ºíAš?öT­šÉèÁ8_þеÉ#‚üÙ[»rñ;ÉäìŒ:‘1yÏu<ýCŒ°¯âè­çÚ…ÉÏa¼?ï“Öeþ`óʪ™ÖIÔ§»ûA•sÉ¡¶bK§ˆ¤ OŸLò¶µÔžz/˜|[è¸Ö#tÚ±Å4mLÆ÷áëÔÜÍýhü>¿g[q’þ¬YëÎ{M?†qø¼î3âëñÜWÍç¾¼ÁöÛú}q–].V³ˆ$ôL„s‘3™]ç÷"{ªsUA½Ùš÷÷þ·§„Éu€ß_“Žóù¸p;÷ùò†î¯Þ3[]Û»ÐÕí"3E’­ 5+&ÛÐr›*ЛüRù÷[nÇ17Ò#/D„ ü:žï/é>ø Öl:>t‰B¾Îç^M_Þ`œÅ£éðÅäuï{§n™Q.-ݳ ™ p'–™Ñ­7Ñ|=Æù8F8VË•×&ðù"gÉ?ùD·îR]ᦵ€¼®è=nÇ8; ½jÖv )“»¾x1L$¯ž ãŽO&b›óÙÊÏïEvÓå&OŒ ­GóãŸGö*³g¬(òŒÝg/ÈŽWkŸ¿ðå Æ9Z²Jü2R¥5ˆ$ÜúòÊ˾ÉäüÊ&¯íA¾îþyÈ#1BbÙe㺟7 üy ~=#ÍOoèNœ [0¯’ÜЦ>ÔŠÕ5iý׃qFMª›3qâræmGÐW«Þ«]2¹|´êþÁŸw#’çkŸÏË,ðó4¿^ç¾p¾ž-­Ï4ÊäÁ ¨pWßnº²iïr…«þMˆ·_ŸUœ“Ð0™h‹x]BW2r˜õˆ¢GŒPmaØÊ ÎfùºŒ¯ƒñ:Íç9Üû—É3„q þv쾜PÛSwì7÷ò•!O«&“ÄR´Ù×™øôÄåbäçºøº‚Ïo¤¸¨–éøë±ý…¿ŽÁŒb)8ûzÀŸ³D2Âôhj¬*™ ÊýÛÕáHÿª'ÏÌ#Ìî6ïmË•fyýŸ{s¤õÄ[:îÅåó„Ló5Œ“°öÜÍ\ËÈœ®1=n!ÿ}·•ó%“’úyŠMoGî/·¬^é¡áÊ“!oó„Éë2²?‹ù¬¦÷¨RÅvº }¬ƒnZµL>g Ɖ®@¤+H‰Íë–®@_æó(ižÄ~.Ý·¶cû%}â 2øÍ–“3±ý¼‘#xr•ˆAMgxÚ“ýŽ®“wŽvµ?XeÒñ0¹žñüçÞËw‹HXÒÜ#Dü×â0ŽïöD¹•¤¡ºå€ÆØ_¿ÝPðØí«äņ°³9Ÿv&GOÌ{•¥` óÞ… ¼föŒÕI÷²°çë4™žWð`œÈ$zl%_åœÝó$â«Õ¢-íc¯’ú§FFöhÛdñ ‚£…>•«:®õ7Ë×§ï×g©Žf%ü:ØÿüPñ®¾Ë¦v§ß•]E¢¹Pj¾Hç/˜³Õ‰«¤Î€|UC{“¼êD:G ­» :—Tsºü}øñåדײìöÄw.@¸Ï™Ÿ|ù‚q¾ùºÎóÏg®"Þº?ÕY,’YaYžŸßy•|¯¦Æå~„>_9ZXØò÷iϧÊLJǵt|néŽE(ú›§|ü3y¹0NßcµOÎ9¼Š¼In:ºSΛ†æV\%Ï_¦¬-²m ÉzèÔª\£#½lH˜"ß_àÇ™ûe¥û8…åy¼ÿ÷ Á8+S¯<IV“ ±kžm\"’Š­[)ëN¸JVG– ï?ˆTó-˜F ¹ª6ZÓ`ŠÀ}u¼NKçµ»º_<)÷úÕ-,Çÿ<À‚q|˱¿®&'Û<¼ûû¨3OÇ•É|•”|kîYuI?ynNÁÏ£…›t9`å¯Çòõ8>¿½ß0ÛÁövæÄv‹äÙæàç_’&Š&ýV‹¤pô†Ûê^e÷áeŽ oBVD ÔŽ>~ËTyŸç¥Tgܺ5ŠÍË›> 3¶õ¶KÒdòæÆaœI鞟û’øô–V‘¬mæ­Ô¤ØU’÷2‰úÞܜϾÔxÊ-|}<µÎˆŽÓd?'3þ|šû§!¹š¼{¥»Ó}@¯çëfö¥bœÙ¥^ÜnÔi ùE?üõ«å¸N˲¾åò×WH¡îÛÉoz“î›Ãß?-Œê} `ØÏSe_6ŸŸs_*÷6óû×™ò¥Ò]ýêð®·ö}»†Ô:ßwD¡¥"ùº¡îáÛWH^ùGVšÙ“ìþµ®zÆÑh!zo¿•K¦È×üþ_÷’žËx£3¥®d?Q7ó¼ ãÌŠí¸Â}n É•»Û1ûB‘´ÝÓÎQòÂr¢Pûõa%z’s®©| Z(6±É™Þë&Ëë1ü<Ãë²tÜ_Ë÷ùçðå ÆÙzñóÖWê®% õµ'Þœ+’9¤íÒÂÇ®?jåJìéI&–ëÒ$6Z°Ž¡{l²<ŸåqÊ×奸þSÇ×9¤ûÚRÝ Á8ûKœŸÖ)y-‰Ÿ]¼ã*ÌÏjoþYßWÈô–=ŸÝ‹,l5áqÚæháÐÛ[ß›,¯Ãòù?>«ÅnËqò©îV›ªž$C=Ù§çËŒó8iØŠðˆ¯H CðkÕd‘S|5»ì—WÈÙ/Çä6 õl}ù£çG Ò<`ŠÀÏ—|½—û°^·¼´£ÏS½Q±šÂÏã¾üÁ8×r}[Y·ŽTœ½üБL ›ìê=qððâmËÉóüÆí-¬kX%|Š<Ïàó@é~Ü t½öTǯŸüïcÆaœ5FkòŸ\G¤çVE²ùˆË+ ¹B›?9»*ÍH¾ˆ¥úÑBøµ_®ú|ŠüÜ6ß÷¨òy§´~_5Ós—Œó$f{±Ô`QøD}"©Ñç·à·í¯4=OÉÆW«^­Ížw™"ßGá×Oüy#~½Á¯Óü¯o*ßÕçóMÀmäØöºýÚõÂ|SÙi޾îrß°áæý–Fö|C´õé«·g—M–çé|;ü>¦T‡^èöwWÜ,Ü®¶|ßÁ—?çÕš¡{\ïl${9•>=X$/&d»±°äñú‹fc ÷!ÊÙ_M-ì>ûC‡_öNJ•(°`X–J„×9)ÎnêöL=ܼûÙäu¾®âËŒ³³„O¨K¥_H$!·›¦/Év…äìßpÁ”½ÈĦõk¶-Hç‹IòóF|%Ÿ]ÍþÖ …†ggçïºl¾ ­G†`œã7®É9k=©=ùpͪ ERüé¾c^fëսȫ3ªò#êD }ëoÚ”ï‹I²¯™ç©|?ŠÝàëˆþßÇ‚qôqýöi¹D|ë÷«âz°üëð)—.çç/6íI–½ójçã(¡°øõü‘-'Ë^HþÜ¿Ÿæ©»nwÕ¨?d(^Ä—?ç³’Q®‰Éß2ŒH¶êQÆéËäÚ…ºwJ¾êN¦]Ëu§ÇQBÌ ºp0Y>Og~~ý7/¢wAÛâN/5òu‰/0΀]ׯ;¾&òz6-!’ËΪ¹÷2ù£ñqCØ‹n¤Oî⋃„(áá´£õÛX&ËboüþŸ@:håûþüÁ8_*{®ø|iRaŠ»JÄ¡ùŸÅ"/“ñ;S¦®ÚÔ˜×-KïòK” $)ˬ›8YžßðûqÒs‡—åçõö ­¤Ü}¯‘ìïôåO•»ú1s›Wí¾a)b™Óam Hòß™ZþÊŠËäX…V³æìIÚMmb}v1Jx |çÜÑn²ü\_ÇàqÍ´îY/ÓóøjŒSéeu•i›ÈÉÁ£<íˆäØo¹ó »Lbž­<7µF/b¼™Tð»ãQbÌÒöî$Ç?ñ8ŽÏÙßé¾9Æy±š.¨l"g¦WŸzF+’Ú­›Mz™ºÑ`÷ï1½ÉçöíqYmQÂUûóþM»M’}­ü¼Í×9øzJ¦ó¶?ljôí/6‘_¯Ÿ>¡l,ßù¿ãe¢)6-&6¨/‰°~:Ú%<=©¸vþ‡‰_WâçSþ<(?.|žîÿ>†ã\^YtÕàb[HßWM¯lk„ýµ"Ðð£æ2™6üûîMCû“RW‹o^%|{®€k—m¢|}Ëë´tŠÓñûJü¾–ÿó vŒ3baÅäsÕíäᚈ@‘do~ñÀࢗ ¿Ÿ¥ú®kÀéø(¡†¯LøúŽ–×éúã±ÎÿyÃ8l?{Ÿ°¬ƒ¿µ“ý¯ãzmÖ‹d`™´ž¥_\"Òý‡d•åü­³×¢Øýÿ‰_WâþSþ\«´ =ÏêÁv¯¹û7ûþެ:¼iÖ­")òºŠM}ééX«ƒd(¿wáô&Ž 6 ÜSÌÏÿÒñ½ÎÎcÙäç–ýÏ—Uïê5ú¦ZÍÇv²èæ°ë½1Nžk5- ¾¿D¾É>jÌÝiýÉ€²ûìŽV–¸p­Âò ¿^áÏËð<çÏOøßGQcû¿·Œy«8i'¶…ÓºìÅþ™×nøÅ¶^"å&}±ö‰ÚHª ˆ¯äZ%lÈq¹l÷|&yßËà^féx³ç}±Ý³ñêé…¾±§õP=ê{ÇJ#ž‡-¿D.ЇNÝëI^>k5ÖiŠžd?Ñå»Ñ&ùùXž·|]‘Ï|ñí>;Ü&$f‚ThvY;¤–HßÏ«Ô(ì»OÔ­ÿ¢.˜ýç¹ äû$ü9¾Žä‹sl/ëúDŽô›Ó1àmu‘X-éV!ôiZºò]Lš'½¸¦®%¹P¯û£c¾^ÇïÛóçëR4¥w•‘¾¿ÛízîÈwväóP‘Ì]8OQÍx‰D6w$DÞìJ.¨b·EåŽècõ'ŽfϵW”ïKóç |qŒí¥«b´ ýìdˤNÅ_”InSx‰Gí.‘æïýt¢^7R~^Ôùè,QÂÀëñÙVµ-ßïæûSZº.?ÏŸ_àëྸÆ8ÒüÈNÓÙ£×éaá· Þv8փÜ_°Îs1W” =o5Fž7ñõ>æÏƒúâ¸òÜ·NIʸ/$›>K *\ái\åôØñ{$G [±(¡´+ÛÔŸ¶‘Ï÷¼~ñûÜ7ï‹_lwoøâ+‘ƒ#IŽRcoö™HôßÎH_‘çyÑwø7ûÞö&ÏæÞÜÔªd”à[^º8Fàu6ó}Íxöœ_šo»zl·S¯Ë×Km‰$Ÿ {{õoñÕ|÷aÕ!"¹µ9·ãç£I× }~ï«D¾5o½­iÄ8ï~?Ÿ§ûÚÎe ¯§ùßW³`œè鮲¥Šo#ÙËÏ/öm?œ·#ÇV9‡ë©NæÔ.U¯ Ég ÍÚ¦f”ð…qY×,šñ¯“R«)¿ç ýü©NšjÙõ¶ôܶ㔩BWN¶‘6µµó…A¨#U»q~Ýyµu­³±Ádc“•Kß5ÈÛ [ugÆÉóþ<£ôœÅUù¹é:·!©Ü=$K'é:*ãä[õ[Ò“ÛÈСý† Iƒãw©#’¾ Þ¥t'#ê\éÒ¿R”°(KÇ]C‘×xðó,?/Jñ]#ó}BŒSiÞDÓ]Û6²ä×z˂NJdõ”I5ªãºàÌΖŽ/{²÷Þv }‹wzÝíâ(Ç)ŸÏð÷„¤qÓt|½ÝþPý®þ ý÷»9·äMÕ§ˆä]ÊÁ1=rŠ$¡háðvI½ÉøeõJ[½Shvüí½«ûFÉïïðõL~~È\¤ûwjl¿ûCÝí–m#7&̘wjšHr¼p Ûñ0‰´(ko0×HtkBÏܵSØþ¦ ³÷ƒÑò:!??óõaiÝë¥Nš'Ö$ü½1_¾`œ¢ºþ¹rÍØFð!Џ¦bž^l«·jb¡OuÆèKf¶î´¤røNaéò­šŒ‘ŸsåÏëòy¯—ü>?~¾¼Á8{+ë;×ê¿<Øu6|Ÿ~ñ…—7>–Dr¥7¿6’ôך–æ};ëÓQ(é£äõ^¥óžG¾Þàï¡ñçR·H>õ0úÔÃèïÒÃ(€}縩§õ%ü+¾VÿF¡¬'¸†õOc.Bç{.BÿžàÌ·õ~ï[/óÈø÷wüGý‹Ô¬‘ׯxóXÛX‚„0o‚’õxt1—µù=Áµá’á=ÁßïñèßÃÈéçÛú˜·•û¶ü½­ëaĽ­êñhñó¶~ȹÇ<„VÖ¿ˆ;[©;ÁÆ’9„õ×ø¹¶h\ÞÏ-„ùZiï"î¡Bëí¦~ÏÙêx¯¿ã‡úëýú;Rgë§ù©FZþ^5RÁ¾S|€ÔwÉò/ú¹ÓZÅú¼¹YŸ7ó&™¯Õß·åß×ʼ2!ù3zQF0W«ñ½oå´v³oVÖã÷ÀMg®V;P°^MqÌIhbÞó&¼ßWÿÞÞ÷ý>ou†ÛÚåç—ù˜“;fü„s[s'á‡ú¼E0'áÇ3ñ̵ec Ì„´G¸y¶BYÿ[­Ÿ[ƈäv5úB…2'!uZs_õl™Y¨À÷¼„Î÷z¼}¨®Á¯ÇõÒX¥h¤õñ?Sýëâ?[?Õ¿w-üﮃJö3Ú\ºg"Ð#@‰ºg`@P: ižœ™{ÎE0«¿K‹÷Û52?u Ÿ#ÆÅü«Ô…ÀûÐÅ@³¤ƒÔºx ApÛX€A\QÚç Ÿƒõã¥-³_OKs T´O„äC00‚IZоg+õé5ý…÷ëõï]gPc;@U>ë–á¦vU%ú\~‚‘X®Ênj/F’¹ªÒµYº.‰Ÿ`$œ+^ãß4`ü@»`ê^긩©#&½ŽäÓŠÚ»—%*í÷íf.-KÚP?7 ïe©g>j½Ÿz´,©©SÐ THn ð¼× ïý^¿J$½™õÃÓÉ)øi>ø©Zþ^óAûÌnzlYòÅ1ÈýÔÔ1hf}õ¬ï/ äæ_õ÷hù÷ýµ±ÍŸÑ›ÓÊܫԉ4zÛ?ðSSÇ †ù©ÓýzþÒÄ îUP"AL¬g9u š™Aͼï÷ý5ü…÷ýõ‚`$–«<}Ï ³£Úßó!ß ¿/Æß9ø1G5wF€4`D‚Æ@樦ÎÁùbÜÌ¡egŽjîÔ#‘ÌŸebý~õ~Þ˜$wP±èqÌQÍ}ÔŸe"ë‡î爛jŠ€x?Òó—zª€Þ¹ ¾A§ô¯‘ÿJ}äµñ?[?ÕÃzøßU Õ’k•ö?§j%Ð <À€úç*¤xA0ÓE¯œ^ê†Apš˜ƒác®,Þ‡8„y§5~.˜8æVµ‚tê„A@Ç ‚ÚÆNú¡À ´Ô© ö‹Ñž¯RŸbêȲ±8í7(õ:§îÀ`$ƒƒ¹¨cÕTH ˆ/%õ26ÄáÀû;ª<}¿ßcǹÔô}¯ ÎiWEúŒ/~Ò€±2}V-Ã9ŒUés ø’-¤c ½‡…Ÿ!ñ¬ „ ]@$ŒiÀˆdŒqNÛX‚RO–hiÏcæ ¤}ÎEæÉ¢}ÌÈ0VÆzœÛ˜kÚÆšº²œ¬Ç9uº€ ¼À€DwÕz$«øà"9?Í ?ÕAKÀßk^È>“HëÏþ¯x¹{šz-À bóA„2¿ª¿/+¸–ù§pˆº;uA¸AoÿîiêÔ•üÓ4!ŒÀÉ\YÔ­ê*$ˆ™õt§A óA2D0”¢}°ðݘ/ëc>5*¤cyúÎ.¶£Îì ö÷Ü|È#èï¹ñ÷~ÌEÍ=‚VB ñ@Ã\Ôñܘ€È\Yæ¡æ.AÙÉAKìPÔHp3ó ªý<Ô“<¬¼¿Sz¨Õ( £¸€!¤5—<Ô.@ïhS§ ¿¿÷©F~ª‘ÿ[5Ò¿>ÒcI«4 ©+‡º§UL ð‚`¨ ¨¤ ¬q@€iÀ€Àu%‚×ÌüòwiÐ6Ô¡Ì=ý¾+'ž9Vm,ØChôv @à›€ôHP" Bh Åh?|ÉóQœööÆgDr8X‚“% õ¬ÆQÿÆ Ü °4í³öa†#™\åhO l¤£š¾£ßErY+d¸§ã@ Í Ò+Ñ÷Cð+g¸§Ó«Ðgñ3 AZA:­“HÄx A2ÚXB†‚8ˆÄ´‚t‚š¸§©+Gm"Ð#yÌ#h`@";¨Gƒ9¹#ÇÒA0sRç´96ZÉ!æJæŒH|+HÁ(. F°/F1p5 ‚É!øiþø©6Zþ^óG-ÓC Óö/ú ¹‡šú #€#ˆ@É|bî÷|b& =óP+àfàZº¹VM@z½ã8¨½~jE1êjAÎ1—u­ºŠSoím2b™oÕÒA0ÇÁ’‡úÄ\¥i?Cü[Œei1ì$”¤—£}r°€FÙCíïú'ÑßäïIü˜‡š{m,9Ch™‡Zñx˜KÌ©Éð&zA0ÙÅj3ð0˜xþ=Ï"uR¢XA:0¢Ä@+Ho.9©ã}£ÌJ¤ø¤þ«kä¿C}Ôü½jãß©.þOÖDz¨ïU Í"ù§ÕÆŒÊ8ˆÀ´‚t‚R+HÁV'P!`ÅøÎ´`;P ˆMÌ=­G0ÛX@‡7Ð"°í@à6èä D ›ðN BЛ¨ hQ­À[Œº€ðßÀ ‚‘NæV Š’´§<¾,EûVã÷‰¼  ã*C{câg ËÑžqø$¤—§=° A2ÙXBQït<Ð ±l,¹B»r†wš&Z(pÓëi–p¡À ´H<;P ùL h„6–ˆ¡À ´qN;€ j`@¢:™OѼ ˜ºdй=@ËØÈ<Š4‘ƒ(™‡-¨˜K1hà6ŒHô8ˆdiÀˆ¤Hü@ß\2É¥øižø÷«…ÿ×牶M/ýo¦ý_ô@r6õ@ZA0"ˆ]@…@63ï¬íJµx€Áí*¸ˆ@@w0ç¬x€Aïü®ì4?W¶ ZŒúëð;Ì9‘ %¨HrGZUÔ}"ygí,iŒÀ ¥i_tÚû¿‡$²‚ô2´ç.ö Ð ¡l,©BhÕ™}ÚbÚ+ßç#ŽI%Ï\…¾·œÙ1ù1Ÿ6÷LÚÉi"Ð3Ÿ6õMš°N BÒZ€#y]š ÿd0"‘ã@ ’9¤#’Ú”Hl3 Dæá¶/Ð#ám ÞóUR·EÀÆ Aˆ+ ÔÉh§8‘rþùw¯¼6ò:ÈkÞß±ÆýOÔ7^Ûhã5‹×ª¦NÑ}¢D]2e‘¼Ø+H!”x A°ØXÀ„7Ð pl,xŒÀÔ" ð=‚É(åJ—™y± 2;P ÐL@zœ(tfàŸ¨€àÁD½‚`ôÐç¥é31ôÙh¢›Þã@ ¦ÑëÒ2´7;¾Ò ÒËÒ^Çøn@ƒà´± n ­@{Òe¸­Ý@[‰ö Âϸ& VÎp[+Ä& m5úÞ0~†€61¾‡”n7Ð"Àí@ 7è?âµv‚ß¼ IàjÔ–ŒH P#1"€uÅNß ¡Ï² nh‘,!H–xzm‰:‘NkêD<РFXA:Aˆ'R|Ð?ÿ¿Ï§þ7çRUc>Í¡¤Údd¿“F÷)‚ÑÁ2¸i § ˆ@ u¥ŸK[€µt‚Àj¯x€Aì*²xA0ÚÔêà·¨ààÁÿ„k;Ýϵ­B˜@|1ê³Åÿ  ¼%¨;ŸxUÔƒ†ï¥¨{ ¿”H–P4HKœPàÚr´ï9~IdbyÚÇ7ÃÏm®@ûSâ» ±”H,s%Ú? G‚9ª 퇃¿#Ñœ@õ7·xA0ДHB3ð’Ñ THH ð‚`$¦ ¨‘œ ‘¤q ‰jé  4HZ+H§ï¼!y〠l E"[‡9¾#@0 ±í £ö9€‰nfžo-ÞHúPàZ$¿(t’ëÛ ´tmHy …ÝiýûŸš+ÑGkÙÿ…¹¯Yšýgký^*Ô ÀÆN†¡À ´¨7v @`˜€´ô úÌ­5 Òw)€4Jxþ{çÕTÖõ}PQìØcÏŒ ;¶ìØij( vÆŠ: 6 b‰{ìØ ŠŠÞŒ4@bdž`õÛ'÷œkä™™ï{æ[ï»Þ¢ký×Ì0xNrïÞûžvÿ?”%ÂR òP2 $ʃIŽÊA¹aPiP" ,*%ÃKF‰1È"PfT›–¬ñ“óÂdŽFÖéÉœ ƒ,å‚Iƒ-¥C¹Ö'^ïø™1ðBP&1ñ0ÆÏ€rÀ ”£L¿¿IüÊRÞø¦áçÂÀÔ 08剧ñ“ÁßC9` Ê‰Ÿþ¬% 5¡¤¸*”¯•ƒrà ֠DÈ TJ†ŒcPG Ì¨ n-Êë„U@æZìŽX#ܰ6¨È»^伿¾ Š>èƒP:” æ|$}è¡t(WÌùH:BéàûÙ¶ú¾ûs‹woÀ˜/MÙ¤¬8«ýÁWuñ†]~Ÿ§›?è†ûÖ~³/›úœ‘”zÿÆöÖRêGùBòñD¹où%c9Æ©õæÊIÜ8޳ædEa?§ÿ¨AP¯ÐnË«£™M°ùÙêpõêl¸PÿsÅ]Íï§Ëu­øâêIã®ï;RJð#aþG¼Ÿ ïW¤Åöm÷\Ùv6)šX5&pS/jòvV64̹PçÄêßËgíúâVi¼àWÊûÌ”|*y“À«å}*Kò¾ØtÏôü뢸‰ÊLð(¾ìº²ÁåhR3ç6Þ +SnÐí'jnƒ6·N—*8æcî/ó¡á¯§^ÂûÌ5|,-7¦Y¾ôFÀ²°™Ñ°üRíÀÜÎ&¸œv2jz@6¼\›>àMo±<·m²šñò«üðàñóç`¼Þé¡„ùò>ŒÍðc?úì7í®ŒÙÄäMÙÒ%ÇÔêëÜ;*¼˜öËçóÞ@Ü7–Rs<ˆ_Çüf˜ÏÏ¥~%a¾˜Œ‡iÍ)”b?vZyÉò_ö@þœ¯9™`¯âëÊ:m²áÙíú/4÷…S+GßgPq_õÎ] AãÒñþáÍ÷¡y)a~Â̯æ?qì§ç’)kÃöB÷¿zy;›`©ªî ¿úÙ”_â[ÃÝ îÜUq݉½[ ê{âÌG…ù¢2VæËõoû¹FlUìöA1‡™ïšµ6A§rÝÆ/› µû¬ …ûRG͹ý᛺qÖXŽñ˜?oWùüÀö:ßW–ß»blYT6¬DŒ=%º¼ì½άœ’˜á3~ýïÍD5× Ú…ê êŒæ/†ÅïOŸ&a~´Ö~îZl¿l㯎OÒö¦óí°ûMP–ƒï!¼Ö ÛI%}!wð›IÔÔ—iÇûŠŸVÆÅañË|ë¬ùš9Øë¸žãƒÍûÀX,Üko[ì›2Üg¢Î(Ä­äÚ®ö±Yj®úÞš+bG ~ÑÌù±ëÃîµÿ½Mó|鮋Ž×RêÄ€ùl·î0n×íñ¸ã¡5ÂòG›ö¸ßná^L¬æ^—í÷ëÅâ—÷U|"ñ+\PïœqE-ùýtlDp1 ¼mçQãöÉ^û¥ŒPaA›Ý}=a|»†/âߪ¹ Ù}›#p˜˜_«…)ýÖ£ßË^àÅZsè¤ØO«! ÍÎô`Å–—9MLp*xöó-ûŒ°ì„ˆŸzƒË«>Þã^¨9žo4Vð‹c>DÌg}…þü±?¶–øSü@ìgm…Š¿¶SY»ßK5À:yU^iè6#ä®r-íÚ ž}Üú(!ã bL›¥{¿Ç-óÕY‘â­[÷ûIßø#;lô<÷\í®ôxR¢m¥Þ©® Ƹ_n2c½ì‚¿ÄVî î.Vs_#1ñ§ÆrŒoÌüîøvoþ9–¼ÀvïòP%Ê,l½ªq ,~ãJ¥UF¨ò0^ó*Ö,XœÍj®X¹+»‚ËŽåX]bu^äPßÊkθ10Zþl\|U\ ›ÝÊc‰>ÉWË/Õ vÅ-\xj™š»éS*¬kähsÇò‚ϯ’-e6­qZ‚l×0ìºÝ‰1P±Çõm°Ýº.ƒ—Ÿg¤~T]ÀbÃ:KÍuŸé¾½š÷÷jnuè-óÉü±‚?ó­e¾qŒŸÂ;šÿÇØOŠ?!ðÄÀ½9œý¾fÈ¡±™ÕýÐ=¢VçÍ‘}áæ7ü75WÕ¾óùØnA‚¿óáåëÅe ãs°üúá¹€ýI =½«Ö~hÇ5²ù– ^«7 ßÛWWŽü}³7èk-Y¡æ2ÏÇlì.'ø1²úÇx3Ì/™÷;wüáþGa?=«®ï MÛ»6¸—Ÿdc‚…'Nµ5«uîþŸjù‚æ[§“.¨¹ˆÅƒ³RŒ¾Ïü÷ óGc¾l?øc?—Þ¹¤‚eg—ÃûcWÓ¶Új±ŽÜ”ÿñê‚/l_î'bã*°¸E/F ÷ŸÅÿ5K‘ñì­ã,ûÙÐvž×Ä,d<Þ³2¿Ï—UG•7ÂÂzâ~Õã| åÇ]…·;3_ÙßÿMÆ!ãëiqà¹ÙO$Œ/lÉ—ùÒUŠ2}”j¨DFuØþÎæûδ)4@…·²›K¼eP3T´þëðXN¼Ùõµtôo‚¿)ã}ðãµ”—l¦þ¸.?Ôq1ö³.\`5êf;ìèµa[ßgØ·Î<­u“~éKnp,ç;䉸p©QB<³xrìÖ0Oß)—ŽCù:(Åv«>o¶·po,XlžË›ÀuÌäÓ•nà•Ǻǟ$^|¬a©gb¹j›©TŸÆpÌߌ=§yN©AÂ8¹–¼Àv{O—t9æj·gõÅ:²+_1oßU´½YíS`MOøÃ¶Å°?ÚÇr·šÖ=ømZÇ|v—q"™Ÿ¯%°Ý;ëêh\:@}M0Nܹj¬ÖW^^~Kˆî÷ñ+ó¿g>€Z¡ÂûÆ&Ð…²·Ym€Äêfôºë ew.fà¸ñ¸ß¾‡^üì& ¨|½Ô öÀž»·¾½}Âû“äþs¾t˜OÒíwÂÎ5cG63Áôé¶ú°0Ž‡çØ›76Ô À<5w­ÒGç}Îã9V„¯C·%¼ÏàËõc»î-6xõ{~´XÕ6`»¹­ªþá>Ú ž„ >î•% c5w[·aÇ\Ù8Á‘w™?°ëcíW*Å~ôSÚ%4ƒk«½oh‚9ýV”õîoØ÷ò†‡ŸXì¥×©¹÷[ëUÝ=†ã9™­_RÆáù³¹B¶æ´b?«K yö~Pð?Z_í¬AÍñ¼«1Ï hSÄgõ•„̓ÙóÞïQýDyŸxÛÒ=*Œ÷Ë]Û…­|6˜› å ´þQ÷ÜÕª±\RÈ Åé6ãèüÇQàhòñT’Ž“¾Q?ànt~Õ’`? Ûj~ rƒë#Bû׫nM›fÕ>Ö2Àµ²ufÔÜã3û%JOöˆå~½»OßèAà }¯<ù`lÞs_ò²Ã¼dÙί6?fýZòû9\F?Òì¿êã]Yû¥*°Ÿ…_äÍ;~œ×´k°ðV6 ,X=#M¯§ó)7ÛäÍšÓjî—µG_5i6Qà[°¼gó Æ1æý’Û þ –üÁ~®8‹†Nësdµ ùRc6´ÞÕ5ÿ¢žÖþ0óäð­¿jÕÂ8šÍ‹˜O÷³±Oï/4K`Uø›Ú; ~ô–¼Áö;Jl`8 øÐN<”‘ NS%Íë'ë[rºëoX5ªaø¢­8¿ÿ0}ÆÅ‘N:OñãšæC?5xîÙž Ûþ蓌ýäë PM [— âKÞ‚÷éa[h«ôé‡}áƒdÀâ’#Õ\ê“×Ü_NâŒÝ–/Ì¿›Õ‡Ö§\ò¥6µöy~[§ó¶”’ o_/¿Vº‰Ñ‹O|õ…°!ùI×ÔÜklâÖ™¿ ã\Ý åHEÏÂ÷aã\¶ÎÃø>–¼Á~Ú—¸8æ–^žƒ–¸ÏgóüÞ9z˜³7sËyƒœ9½£ç¢yjÎÏÄýî_ÍüÞg‚ñByâ6‚¶%o°ž›¨±–……lX²ž H1ΤÕÖ5Ù+ƒ&ãÉÌUÍ¹Ž½¾^›ûÝWž=wv¾­yÀñøW [§ê{“KÈÜÛö®Z ös(ËÍóÑs Ì[œ¶±[R6¸î~¢òP=´¿?9éM´'´ý 4ŸâÔÜý´Ã¹6üÎ1nÏŽ®=Ì£XØgʉ2 |kÿ_öcÁ=Ð@ïnæ‚÷‡²¡Ür—5½ûëaÏ#iŤIn H>à¿çùµ+­\èyñwažÏüqÙz»~ì¾YÇAöÃû’kàÛÓ[Gjîφ¹\×é¹=ôЮ' Íï«zKËŸ¨¹;ƒN>ï>m’à3Íü ù:“!aþ¹l}Æz½G‹ýÖ>â_¿b<”/T-ù²3NçÝ;!ꨇ‡ÓÝÄU=zÁŒ¼·)µËÆÒ:3Y¨ÓÌ?—­·2Î {޳çœ%H\¯§ÓA<¸'¯¹µ5¦ßOq¯ë¬‡?F¿}>ùpoÖ—Éú€Ïä™å?eÞ ê »>lƒŸ'äHXZûöÛ´Ê—v«ßgÚèx" ê:{K6Œšøûù¯zð*a×qa-w¸×ô¡GWü>5ÆîÞ‘å"ðëYØFµÙ³ÏI­²~}Ÿ¼“Xs:ÄØ~ú“º“—.‰‡­/føŒØ” ´_êPXUþ;ß{TüØÂ.}{øòŽšëÒB66 ¡|¸ft<Ð6—®µºûóÊÀÆlaÉlÿÃ/ÁÇ?ÅÇÃi—-ÍD³¡ D^^[BD’_czúï×®æ,iÕ3„r¡:Bý*MÓs§¸B­7wë¥Ô¦óë/t]±ÅóÏ@ì'cpF®ë±xX°’Là²abÍ:½Ê‚]ÜÊíŸâž—o?.Rs+Oïì áûØF^h+ÔgÆy`ó)·=ÅšÜÔþ¸Î¡À~®Œ «wkG<œR»-Ž_œ )—oÄÈoeAÂÕî^Yñ~С–vˆÇÿ¸¤*~™"p©Ùü‚Ÿ·_¸P–üÀv×5Ø1íæx¸3˜ ²A½þ×¼z©Yðlî‰Æî³}a~zGyã¶jŽòä…ñ8[eÜ1>?øq‹ÛýÔ$9À{a)fî¯îû¼&ר9 >{Ð á¹Ê®ûÑþ æ9%Á6yû´¶Âú•%αý'aªW-&öt©àœ ß–ÚÙ»„f¡Ò¿Ò ®y‘'”š«ºùAµˆÓ9ÆÙcã7~}åÄz¼®ÀvKÇläiϯ'7Ê~- †í_»éòÍnÀÏ“q¾åØ“N~Ó„ý*öáØnIæ…,¸pDQË¾Š xž‘Š»Ð£ðì¦ÉÜù+YÇ£7¸¼¸‰í{µï½»60þ[‡ýaŸûá}üã¡õ·¼mmËgÃǺaƆÎYpvÜû€vÝ€ÂÇÏ+ƒ…}:VŸx^€åY§þ÷<YŠíWZ5~Y<´¬yÑí½=>Ç·Ü9r­fLˆ«—;î˜wkîÇÑçU\¿•o:ìâ›}Nž‘+y3?kÊÓ†<'Û<–´OyÁÙ0"ø !¨x$sçÆg£z•ù­@ÅñϩºkŸ_3Òç6 lwAgÙþ.'âa{ÛòËsm³ay¯3qSžeÂã-}Ûëï „‘ïúˆJ:©¹ÂZî Bì¦ ÏiÖ>[·b\OK\c»•£b6Ž‹‡„†i™Ÿ´³ò;c&„|7‚68ñôùžç,NØu(˜è¿êòëO”Ky@ØnnZèbI@Õòø#äwíTËéLؼ¤yüìý¾°ýœX3ŸcÇôu2Ïî¿ø™/?\Þ­:ܪÑü|þ¼¼àŽ"öcYLL²Š4è£Ö4_ñü¡L:fh“‡½¡ÝK×^}¦ª¹iÎ猹;ƒcëËŒ÷ÇöÕ£%“GŸ[šWå輙߷i›/­xqeËóŽ“Ïœ·F°sžá³3Ꟛ±»/| Ì9þ!DÍí²}7V?û;¿•¯{-…qãÁYﳋ±ý¦‡7ê†UN›¯MD?3Bq}Ëß•™0Õ¿Ì­j< Ë‡ÇòF«)ÿk†ÀdqÃÖ›?Oà÷?ìù¸Ç~®Þk˜^?.´ ÊÛ•c„¤6„œ »v7¼ä÷sýZÛø©¹^Ûû%MøŒÇÃæýlÜÇ8ÖãÊ@ìçKçŠO Á2œÓ¡ÓÙ°ÛC3Á¸§Ú¢Í‹=A×Ód~ë¡æÚ%ǵèÒr†°žÁžCjLꙫx ¬k±:aÍT`? î¨z"ìL´[dÄê&ãÍñëëîÀ¸·–<Á~Ößö°OaÜt)³àâ1#ø éuã—A™ ýªlÐ=µ„O›] ÇûÆVáG {>1κ%°Ÿk.Iû*¼N€ÑÈŠ²:_¹¤z&쪲rÃÌúÞ=Iö9n‡Š#³‰Ún3¾ßNmCÏÖg,ù‚í.¨ÝtHCÌûŽ™_¶/0Âè‹OÇ>µË„àm~Ùn'¥¶þܯá*Î2¼Ë Æ,ùü|Îï?`{›ž™ìÏ=Åëž:üåW¹â_‡ì¯ù!ÖØå|*é 7¦mÙZ"XÅ9h>ߪ ÖX^œ\[¯ùŽ#ïèùþ¹¬Àvû¦ï2þ^8û}MJ™j—Ç£"âždÀç´A»¦zõ ÖkŠŠÛŸ–Þ!:™%þÛçK ÍùfTðëTF8H¶sÒ2`ßÎØÇî’Î`׺óŠ;j7é^˜ÚÉxï „ºÎö±øúúJÂÖÛ­Ï¡ˆ±Ÿoõ›x‰ÒÀsé}Û[!ëc¯yÛ&dƒ¥Ñ è^N¨kl¼o‰sìÇrÝmŽ@¹RÝ¥qü]ûþø3àuäüËÜár‡›·"wª¸y·ó¯V¹&œKãyØ¿ëü9Á²tÓع2Kœc?º“j÷îvü7Îñ/ñÍÅ'ˆ½¤Å2àTÏî1ÝŸÿ©¸æ·‡ëñ5L¨¬~òë»O%üxçEj±ÝÕ]Ã}לèõûŒŸ 0²Áà.%ßé`d…&†£¼ q»#¶&…ŠË™o»¨b£Y¾;÷Áæ;ÖëH9ØnþÖKøÍñº Iܸ¹ÐÆÛ÷j~¤ƒÛk¡NCdZXþØh'ÏðWŽuš%œ#añÍÆ#¬ŽY.³k¾´îLRO¿þŸž:þ,K†bâõ­ø€}ÜÒ–Uz©8£úuוf çWYýYëžÛçY%g–¬qï–_×±Ý!á»b»O‚Ƚ­Ï×)a~ܧƒÅÙ˘3xŽ+Ö‘«žÞ}&Ì;Ø~[§O~Æ¿Ùp~¼$Åv‡µ";sIÐôP³Ï‡ìPjäõ´_Ô:x¥Hhê/÷‡¡v­tI-Uœ(°þŽ¯Ã„õ)~ÿ¡x?Øü»ÓÊ,'|ëqY ö3uYÌà$8\Ò±|»2Fºo¢ƒì¬€rAm ò9pöýó³ñËS¶ŸÅïÏ—ÆËå×Aø~ØO"Yn¨—ß©½Z•2™ žÖAßkvpr•¿P¯*D8ø_½ö/ë>Œ|ñÂTÛz¥øõÁ(l·¬åÆ%ÁÙùJ§ÀbFXåÑëtÌH|ZÞÐeeì(¾eG¯—~*îå·‹Jï ÎѰ:ÍÎwòë)ùul—?/›§«-+=¡À'¦ìÖ¯Ÿz÷¸°ÇïŒ/ø7l¬º7DÅñ<¾0ŽíK1Î7[°Ä5¶gÁ·7K‚ê7:¦{n€¬ø ¡G:ë`ccÇAŸ¼aØÄ—¾‚TœsÝi;ã[†qì¼ûþüçÌ£ûûü9G›ŽùÒ·_ôòüÆIàµŗF÷ Péýº‰‡›é £ó‰£C£ûƒGï«gv†ª¸g¢í×$LXÏ`u›ñZÙs“ŧõ¸RŒý8¤e<øÐ> öÌðk×Mo€C+ 5jë@w5'Û'dçùÿïŒ+o‰wìǹƒè‰æL4p1vp9n€ýSªž—½¹—Ò¯¤h”=¡s»ÍµËa?st‰ƒÞŸ%Ìߨº$W²}bö9·€ì¤òõû™þÛÞIŸ“àâõé%B Y½¢bÀk zÙ¯ÎÈ>°UZi…￱Ýà oeáû,þ'”­×²}ok¾ªûY5çÒ……-Ž‚¨‡ñûl«ã×s× çF™&)Î TÞn­ÌTqê–žøÕÂ9v=ï–ÕOíåÕnÜ)N×Ñùyu¶iàfßb#NÒÚ0ÀÖçÌ÷\ù&qƒ¨3Þ°Þ{ŒhæB÷î9¸.¬³²qëÅL§íêU¥ñÔ‰k›øõ›N˜ÇËÛ }¸ó(Œ*wðAÁn|N/¡l7÷\i³\*»äG÷ïT\üä”έ† Ï?6¾)~¼¾ç¬iUè}) Zl<Ö`{{á9fÉìçØ¶œm1׎Â'}¼Gþ„¿Ñþ¾ßçä+{bZ ø2.} wDÅué;6%îí,a|Ïòœoøyi)àßÃh/ä©%?°Ÿ=U9æ£`èBF$H™Tþè¯×`ÄÔçfD@½³£`Å>W‰Ë“ÍâØõgóh~\ùò‡sÂØnÂØÑÊ“öÇ C.­6ÀƒÀF†¯®Âáµ—ú=|u-éUÜÑ“¥òNoÖÏY<±ýE¾~ðëŒ lwŒ79±t ò–Ä<{»ÐÞéÛµW¡þ¦±ùÃúÉž´Qq§?ÉóN ÖÍXü°º<¢Õuçøv£°Ý·J¿ñø(‹h3Ç5×wËìÖ]…®‘ÇÇ8ÁP’aPåïÏ“¢qÿ¬}ñcîQ…v®ÙçØî6¿¶Æ¯WBôTÍv# лñW¡ý²Ï¿µðdøUIÅiŸíìâ¤Çq]ïâðÐך/M¥éúsI`ó^ëxÉÁ~æ4lÔµ\ÜQ¨:â¹ãUo”¼ä•¸Üí*„œl^½Öƒ~°£JxPýæ*.jyíc‘»Â…}"VOyÞò= ÿœüq¿Ý¦s¾tð(]éÝ ÂRN»àþ¼Áç®Â´êÞ“ÛÇ{¶¼7Þ½‡býZ»Ù…áÂugׇÕQ~\YLˆK¼cû¬ùø£°uøH§¬–¸ýÛd§ÊWaäÚj jµ'Ì´,l©¸GåÎ× ~αuaV¿øùÂM:îù,œeû1–xÇ~*xDŽÿìÊŽñÔ§ ¡Ufg½M‡6K݇eëûÁõðW½ÂSTܸÞP«ß–pwÎÆ›l”íK¯»2Ï›Óâ‡ëˆý¬ð鮉~,Ç6êà娓­Þda?÷6 óõéî’bóT\¹CÇ÷|{4K˜O±y;ÅøÁÓgqÙFØ7µäö³dJ‡ãò£PlÔœâëkÀ¾Ï¼zƒ§·êΉƒY>0p¬ÇÓàÒ*.àÁïŠ%‡qì|2«wìýÆu^ª:³ë^z'`ç;,ùý̺pzôƒ?ŽBb¤iöPìÇ¿ÿOÃÒtX¾Név³ÀJŸ©&SíßÏ9’mÛñraÞÉê[wo6¸jF™qÅ€¿n]áxÍÉÚÇK»ñù‚ý˜ ›È?Jß§1@òMl9<VÎ|yä©<¸yr“r¿0fë­l½ø‘gñ­£ôÀæd–]5ÏDÃîÔ9dÎç öó4VÚ¢dÅcpðËŽçø}ÌN¹+ÓÛ¤ÃXi©ýýSü eUMÝ•¦ýÜç€?‚{¬ü¾~"¬?Ðõv¶>wró燎R?XZ³VÿSúñyÓ%_<¿Äù5­ŽÁùу{V5@³ä ç‚ʥça77Uö…{#É‘Š+t5 –sŒsÎÞÏcÏk~½òµdü©êoï¸Ñuþþˆ±Ÿ»ü»¼s;¹ùC½=ìq|~{N~p¯Å]{Uð†À5Ë6Œ}£â"ÔôÕ\ø>ÎaëýìÜ{o¨ó´Š¥ï~î,œc±äösä€òõú Ç ü ïìŸõpioó9«.¥Ñsý`Ub¾}ÎX5§*œöhV¸0ncë¯üºÖ=á\)¿?ã lmÉìça¿üÀ/»ŽAÎnG]“×z¨ýùyïYÓÀæšw´Â;8ÔvÚ¾WÍívé~üólaß‚Õó+´:~ð‹„=W]ý­Ky×xÕ ìG4ÜýõлǠ¤å´R}¼g\Ÿk÷o›µ~c/ïSæÄ)ìgã身eÎΰùÄ“vK“ºG—ÖSÙû,Öï§Ea?‘$<ª&ÃÈ S—Ü×C×û»;ÎKƒ‰–B×dí•y9UÍ­äž—®|sŽÀÅfu…¾”ß}`”g¯ÒÂûÖãw-ö£ Ç¢Ú&Ã.åŽcñ×õ0´µ©^©à4ØzOVzË )¨‚·di¥æüÎå72‡cqÆ®{?°8=º yMÇoüzZ‰çÁ[%“aúÐUÏ<®èAºzèºáip`ìÊóæHÁ2M·Ws»…ÔÙ>ºªp>™½‡ÊÎDzu`ëó¤6]ó¥–ã½’a½-ybé¡¡}áç¾i0p¥:vIb7X[º ÝºjîxÇålÌæØº%kqäÙ<‡í#ðß—_Ÿc?U,DÉðHÕ%iŸZÔ¶«Ó9 Âw„ÝiíÕ6ÏøQAÍÍ;s=nLëÙûì¹0ÝGÝ`íÅÛô|ßgἚõ92)öóDb£î»=JOõlÑf=lyz]”Ó$ œîÌ=œÛê_ÚîZ%5Gv–·›Í)vM¹'uûþ|cñÌ?OËÐûîüÃs4ûùE35híódÈ=Ø¿cãzpñÐÈ«”FÏ‹öçG˦Ϩ¥æ–m%ìÙ¾»ÿ}ŸÌ™ÑÇ\ ¯·RÖ9g`õÂ’7ØÏÜÚ#Sr‡©çÏ.£Ðƒ~é›GÁ°¹4=ïmŠ'\ú•ìP¨9oËA£ÙÂxƒÔM'ÏW—-Oß{­üû³mÀú¼ö³ãF͘£úãPú~—íÚézx9sáÙ±7¯@bªÖóÄÌ~àñ²öˆp¬7Ò}Ní›ÍýR¬Ântgáœ%ÿü´¥ãƒ²ôùÙVXo°ä ön¹@' Ç·5šOÐÃá«­Äá§®À›Ôúåº|è)3õ‘ÓÔœaHñ!fsì}Z6Y}*=cË„BagÉlw{Á„>SVŸ]ïCÛ|†ëaò¦ùÇÓv]›w¬™ÜâÏ¿3TÍ¥+›-÷αûÉïGWö\fç!-ù!É—ZΣ<<ä­…r¾˜‡ÏüÍk^ Ï|¶«î [γÏ]Í‘Óÿƒ›ÏâØ>‹_~Ÿõ=ÝWzͯ¯b»ÕOùÚ~èrøuy=øÞ«9wþ„+p½\±æK{ƒAs·ÊS?5—»iú†…l±*Ç–¡ûüºžÛ;}3eŠOäI «žSÛèaϬ 5ë ¼µGˆãŒÎÝ!ô˂ۚIjŽßûû: ›/òëÔe`Î’ùeê”ýüBçÿx^’ÿ}|ˆ¿é>TŒ ÿ‡|¹v¡þãE=‘"þ†kèFýI˜ß®µoñ'‘á\ç¡dÔ·zõ–d Ý?d2Ö5cØØS_$õÜe\C9õw¥ÜWæ¹›\„ûêH=ws(÷UIù5Œsm_÷|#ŒÂ~µö–ü+rkoI°Qñ}s°ò}“Rß7kÿ]Gê/©ûÞµŠ&nõJ²ö"'ŒÃêÃkÍû*Ê-(âG.§ÜkGêÅ›gå‡â@y69”×@¼áD”q˜G½’¬y_EY æÃ« ¾(dµ’0ÖΟµSaó_·vÚÓïG˜°®Ô“·(#[ù7ì/„UYy^Z{;™QE˜°fTõw"~åÔSŽñLÿÿŸ°Œßà@¹Ù„oãfÅþRPO^)e#:Ôä½1µE؈į\IýÊ¥ÔSÎlÅ„u ^Qijœð­}åþÊ—×Ú[Žð"ŠøE‰¬ü¢Ü¨_”µg¦ õ˜3ý VC½3)?ÛÚŸ—pÀ”Ô?ÓšuS”•HÜÚ£WA±.ÔCÓŒ’MʈUPŽ6ñ1'ÞRbÊ#þ¼E87E=ÌÿÌs“x˜çPŸ9Â#1Jþü¬ÿùµóß­›ÿ›j¦ýÿ6”í C¹b€F¡ì1HC(ÛáÏx`„í  œŒwcícN<8#аdÅØÔÇœð•ÔOJ=ñˆOð¿Ë³fÊ2ƃ”r´ ïFnÅ#|L”ʺ‘RÖMQV"ó0'¬Äê­'¶âÉJ©9á%ºÕÿÑ_ïÏ<†‹úë%ñ1w³ò1—SskÞ†rŠòds(ï†p´óŠø &˜–2o¬YÚ‘4‘ƒP:”K¯a åÊR?s±•/'áÊj(K›y™Ë(L\„£­¥¼ÆKü+Þ á%’7óˆ/_òÏqæÏq¦Ííš)¢ŸŸ0f¥Ô·¸(s;òoa„1«±òME¹`@+Q¨À"ŒÙT y2õV'E­ÿ!çrÂg–ñD”ÃM¸82+FXõ.v£ EQMÞw4µCÑ)’z²†be?0Ƭ“+„ú²Ž¢•ƒrÿ¹±%ÂäS ò(ÿA‰ÊCÉ0“QbLH*%ÃÄÔñ%%ŒœÊ€(ÊšM¦þ¤A”ÇmígLxa‘Ô£Ôš•S”©H˜Ü֞Ɣ9ëŠ ¯D ˆ_)eÎFP&w@rnëå…?ãÀ"¬R˜¯û_yšowÂT”Ï û9ÎüßS;ÿ»ÔMR3Å(• Ï#, JŠªB9`Ê)‹âÏb.”;›cÅ˱özwÄ`VáÎ:b`+©×;á*F¢Ì”G¡¡ÞËÿ.KÌš=˘n”ËMx9 +ŽX2J„‰"§¬7ÊÊ)ÊTd^ï„©(Gé(Ç‚qgݨß;á*Ê0¹T(¼˜rñŸû8ç Ü0é4(å‰i­|ßóP2+ïwõ~·f梒)Ï¢(6rsŸÛ\ÄÓ9„rs¬ùÜQ({Læ” åjåéH¹„AD=à1Ñ#Py”A›LùÜÌÿ=€²Å‹°¹S)3‡±ÿŠ™CØŠÄ=Eûsœù¿ªVþwg:ÒÏ—cÃ3Æ4¶ÿÊêŽúÆaÑ&£0ƒP:”+t$ ê ",ZàA(-õ«'üÅT+FFÞ?äŒ1-cdˆ)¯ÛŒ °bŒF†%äIF‰1qä”õcÍ_”b"EÕæ}ï QE“бhŘ\rê}OŒ TJ&ÆÏ‰aÂ)Py(&^2JŒÉ2SVF$ÊŒ ÀdÔ¢1!#PfT&f2J„7HÊ£ %efeÒjQ"Ê"Ün1&pÊLycQ(sPQ&#aw¡t(WÊÎ lZ)&|$MúÀvä]HžM«¤ìî@,©(ʳ¡ÜnÆ "ÞùöVÞù®]Èy#üñÅG™º’}xÞ;Ÿœpž7ƘŒU7ÖËŸõò?»^’{KXgrTÊ ƒSƒýãÌ•²ió¬Bä„JE¹`mŒ,§uÁ ŽD™)‡1 U@™É(Ñ?àœY3j ×#%£¼nÂЍþ}¦E‰1I”!$£ ¡¢ Æ”Ž2(eƒ0F­ JE“*¥A‰Äø»¨”&™%ÂDS òP2L¸d”˜2ÒRQbL¾”€I˜Œc"F òŠ0…‚šÏ)Ê«5S®áv d˜´É-ˆ·*ÏKK¥l!kv· å€É,oE¼™ˆ¶E;¨ Ï !ÜÚT*Ê]‰2·ãÙµZÊîV¢ :÷Ïð÷P.E¸Ý:Êb<Æ¿â #9¥£,à(”=€Âýì@._$ŒgÎû—wƒ;qNƒÆòþTbì§ÁÒˆm¾—R`p¯yA¹—²@Q¬ãÔÚËÐfUã£û~‰)+>ª9¯åkú§^ ÿßyžkU ~iÜ¥PY¹$D'E§O™ÜöÿJÀ÷^¼oö“.'€¹¨vÉ¿XÓø,Ý:ÿ¤×žËԯݕ`fùXδM´ný²pŽÅãÄ0ÿƵ¨~WVð{]¾ý@l¿þ|(,üçû_Ù±% •ªÑeSÄeèëUcä»>nPoïþÔ_b9õmËýDá‚Oëg„í«çû•ü>xáV?p]ØWõõ·T8Äíϼ8 Ö]®:ÍaúeØØó}·Mv=`u‰Z¿?éË9«oL^<‹c÷›ù·0Ž7»?¼L+ÁÏْ؟/§à‹Kwù‘)Yp­™é邱—ákµ¼KOgv€®#ßÞêîË­mâžíu:LðãyV5€ù¬±ZòÛU.ñÍÍ­} v;û8–‘kVIS¸L}@Ó®Yž+ÇÄrÓvÕ±‹X&Äóñb¾ôÖ>+9Øn½˜9Óæ½KGf÷&•½²ÀÏ'-·úÿaï< šÊÚ½b‰Ž%ÖÁT;؈ y‚"6,(öØc%–QT±ƒ {¬ @ˆ AݱP! "êˆEÁŠû÷œœ½)÷Þu¿÷~ó½w\ë¿æ]μg§<ÿçì½ÏÎÿ‡×ås~*sŒî”£¤×÷+± ëEÂëe~f¼öù˜} Ï—·ùt]`Üih¿fѹ°Î×.ýÐ~l2Œ,“Y«[v3Ò¹ùÕÅ»ðº|Ü"Âò…X>±¢¿[½™iÕi¾” Í™v¸ðfà8¯š[]n¯8 ÇqDÁë0¢›géZ“Á®Æ¡”ç³Zsë›û<˜~”<ÍõßqUµXÈÍbù©I÷Æ=Ô^ |Ž~!“ç—¹ñ>Àqx Âi0cÜÄ×akéð#§%ƒoŶé7&;ßžãTÈñdu¡v®yÄ1ÎQȧdùÖ|ÿê|ÎngÞ8ŽkÝשsàþ’'>‡Þ¦C³]+ö˜ {$6šdG^\›ª>*äž²ºaybŒ÷É8Ñ,Ïh[îÓv_ÂxNw޳¸OÊ·˜Sy+†´Pç5úæl©S“fÕO%µ~6ä?ý¸H¨§â9®¹'åæ­àe“Äm‹ûÇyþÆ6wû¶ˆØÔ¾†ÿét¸³ãl^çÛž~n½Ë@[r}Âå;GɆ˜ÁÙ•‹„÷Ãr-ùü¬ÇBã&ZæÒêqœÁIV^—6$ÀΆ˦ Ü›«Z~.^™,p\çpIúG…¼ ÖÙçÖéEغSV‡·ÿ™™UmŸ¶ò¨Ì~Áq~xåqºËîX®Y'0ºŸYU§ÖAا³]š8ÙuKc,A–68ÐåÍ•#Äø OúÞ5ÛX&´Osr ãó¶­Üóå»[ÅfÏÂ¤”GlªœA¿„ñ_ûÃûް©ö¬–g:EçN®Þ?=öê˜åⱜêêç9½zŠ„<õb¼B§[ç4M×äØT¿îr•o:\ÐVjZeH2ô´Ÿ_j‡Èʶnâ7oa1csT„q¥‘ÏMûFóÍË ÜMÞOü8rçûU þM¬lÛïp…žéö} |YÏdО›Üì…´<øåàí°RöŒóÏV&a¯—åôñ9~Ù”ÃÊ÷+?¼î#8xïlåÓÐüx¦×Ô¶é0zR–ÃSçdh\÷Ûš€.PæîètÛ¨’¸7|ºÏ …ÂýˆåÅF¶x²B[>U໚ý€×5ÿWާA6jÎoÍë§Ãç\#J† åœÏîlo\r‚+Œ ôTÇw.r€Ùý”ñUù>’k¾®¯ËÍ–FIOƒÎzoÞ¢thÑxÍ©ï“)/¢) ½¹oGHdYç¹uÄKçEBþ/Ëc¹‹ì¾c®w¼îùŸÃ†%·; ÒÓ’FŸ§A½Ë/úo˜ 7sû¼ù©YE¨|;â—Õg#Ï—_,ä,.Xó$jyT;x1àÍÞeWE4ß“Ï6áu×÷ÓGÿÚä4˜± 7Óà‡÷ñêÚx]>7õêY³+õBŸc¹€|ž¢å(–§¼?®C«®ùò»/re+œŸ_>}z1 Ìñk¯?Ûåû‚[ÑÄt{MH°GÑ}Ÿñ“j½zl/Ö3žui0Ç7?u.–«(Áq’k¯uÍ~˜[ât—'O»yFûzÉpûu€bW—–бɋco÷E¹·ì¾Ïúû>Ï…¿Ÿ: Üs½ã8óÌ@õ¸ìÝdLùƒiPS2±!©– ¦¹ôë1¯3<¾×æó|¬÷Ï[`å/ú]n,ãAó9ŽŸ„œU–kn®ÇzFÄðÉK ùþ&Ï&¡iptõ…Ö‹J%CÊócöt‡Ò#âW̸Aøœº¢y2›°wƵàûzÓb9‘8NóšHμÒÀý ‹jùó$z¿ï{C掫ò<‚Ü«ÛÍJù¬(åòùÆeŒÛÌú¹%ßLã”ùÔ}Oßè¸3q¬ã²4¸y=$+ –&ãów¿ÈX.žÙ7xýjûªNõ=NÆœ<= Ö‹̨w, lk…¬ðµq‡8ŽJ‹ï£L-ýæå6‹„þÁr¿ìvÓ©ïuÙ¤z·ú–÷ÿJ9mM‹åÕ[uË—öÔÎþšA¯Ë`§NƒGÃ{·<»/ ¤/¶¨¦sƒzdwŽÖI%s\ŒqvøyreÞ/xýûO§ùN Œ»-ãz=ðJ땽·J·%ÁÍ*CE^…NàýòÈ®÷›#ÉôMk/ß¾W4e}›ñùñò…JËú’ã8›?ÌîÐkh<ÜM9ûZâ–aë›<úöK­Ÿ&Ðdôž”š‡"‰9žÿëBR²¿òëã6Ÿ|~xÝ•{ïhÜz´Lƒ§=+'YŸ'ýËýÖ–ƒÛ N>_I^ØoHs9Z4ß`÷yvßa<®ü1ÜJÁQðÙ8Ž g“#óOAöüN÷ÖNƒ6‘u_nÄqªR—çŠj“ŠOF>š7<’`q·ÿ8i±0`õÄßOßQ6O4û¯ßg‘íˆÝ»NÁÙs?6T& .Žk;kJpã¿’Ms>t–ˆ$}Í7¤Åƒ­»ø>VNÈgÍáäïä’F8…]xÙž$¨˜”t1j…ùp5Īcl$á `~Q¿_×óü¥<º®çóÌ­ºçË—,µµër[Ÿs7Õ¼pû?èXá`´9úSÿf¤Š¬IÊöÞ;4­a‘oƒqŠÍu×sÿšé¹Y~ÑZÓR½¦|mm]–ÛMî`s¸>ù޳O‘$C<¤°ý颺gý™å¯òû'Íוãuóš<»Ó[?^ï÷ÁåˆÚ÷¨såh |éù`e·d·®^´üm$Ù8òjj¥þEë66füiþõ—ãë¯Û~``Æóg'á'ÿ–}çm5ÂÓ&Uí#“ÀûŠ$Èí[E²u믋äF’ŽÍ¹‘J¨sÖךqTø>Óª§,ÇQg¯ßrÂÃ?mIYm„Ik7Ÿ‹ŒH¢ù¼Õ Ë‡m:¿ˆ$š‚6?¾Ú­òÙ¾?|+ù½pÔIïòß͇ÍõŽãD­)svÿÈ“03èÕŽaóù«ï”ÚG’ û‰}•“í¡.¸—²qé4êáÍ‹¢|j¶Žc9匫Èûß·ÒãõÏ• m§”Ÿ„¼•§çM2B”åâ%ïo |uŠê?~qá÷™ÖßÙ~?¿¾)ì/ñóGKh®wgh÷þo?:œ„-‹ûvc„[+6ot8þƒìíC/UÔ• '܉"°añ›ºMÆ_gý“Õ'¿ŸñFÖ}ÍÖ.{¶¶ø%æúï‘/= íU¿þIÈJ€ìˆ¡Fe='Ñ}„rdVÀö—¦fÒÔ™#Ö宲ºe\j–çËö‹ÍŸpœ…‚Þ(K„7ÖËSö7‚¦ç‚CÑg1AצicGZN/«“ŒÔ­ëÝ…9 ï‡Í£Ïx–Â^tü£ °¾ÍòËÍ>Áqö¿œ9qMJtq (Ø×ÝUo |äˆß.ônÚïLcbõëÒÏ.J ÙqLáâZ{‰0OcyÊŒwÅ8F,—Øì¼þ¶‡¾ý÷ÄÁ¥.-§\r6‚ÍO„h’À·ÉS¥·6"1GíëçúiÈ$¬Žeê%Â<œñ{¯š²ŸŽp*%Ì›Ùþ¢Ù'xý˜¡1SfÄÁ§níhšÁ}¹ÿë|¼ÿ7uèð}Oå ä[ ½ÿNg 95}^Ê€ÙKN.›çóŸ{Œq,ëWן´ºpuk·8¨gÏ%!©ž²”J›Ds’%°(|Æ‘† ·gKLÎbÂòûÙüœŸ÷çcèñºpÿYf»Úq`žnÕ2Âë¹yÛmðsç¹ÕN0¯üêìœûQÂ}€}Þì~ÆÏ÷ù~gÂë}ùe鯱à»zjŸã•¶mnàäÝI0nwªý9½š7ncµ!=ŠTï›TÄ™`Ÿ7ï»÷2þ¾Âsá¬zæËM{¨{#‚NœšPÞÕ Vë‹÷Ãë7C—øÔp…É׫ê¯åD‘>\oV‰ýÓö“JN…Ó¿•>=;‰Þ_*À¬í]wÔ”ìÚ3†Tcó·¢þÃx}|îöwÿ¹º ¹õæºÇqÞÎlåP19îü—ñ\篲W}B’u)Æ; ÀqÌËÌãÀQãÜ{¥Â+÷yQã¶& ¼Åµ¥Ì7(‚kDq¡÷ÏöÝO€çÐu|'ÛböŽiݧۆ®ÇÁ¦ëÓûWº§Âøˆš'ënL‚ì>}l¡ó¡«hÈnnÛòŠŠ°×ɸÔü<¿@vóÃ}‰]²s±\|=^êéy›lK«Íè=SáJU=¯€$èœ}}]°R ¯^|fhH$‡uî­êŒíóó[+Ê},q³ƒ…t^&ç}ƒã|°›uêÓ±cไ›ù¤B¿W¥:9ÍÄyèñ@ñÓ¼n°ìÃÆ:ÕÇiÈä§¶|ïOØçÄîSüºî›Ðø¾Úž·¬’>è¡ï|¹!åNÖ3c°ÕÞåJ«®©¶òßáI0½úÝ)v'{ÀLÇŒ±«p¾£\ôãËÄçþ„}ïlƒÍÛØý†Rûnö Ž#vk¾©Îm-lšwÊÉ-|Ö®™S¹[ÔüpñÞξ=áxà ~/?ŒÿÁ½r•0eóx¶_Àöÿ“ñ8̾ÁqÚ†•¾Ød”ê—êUªR§Tx·2Þc_«$è3PUgÑîpóý‚¾sôÒûV£)ΟTB_có8~žŸ.ÔãZò8üpœÈ‹ÜÄ*¶xìß6°U*¨¸åöI0×eYÒ’÷]aÞ–švѤ]½ÔÉò3 ['çneÌ?‰ñº e šò~ÁëÏYÝ£RÑð°êo·¥Bév«l?–M‚uFåNp“C‡'w&Ëp€¦EÂý†}nüë7ÊøùJ>åW´(ÆWã8$ýcA+w 4l{ðB7ÛTúÕLJMÙµ-ÃÌÓ!·hòtýËu}‡­WY½1þ6ãÊð}®%}üþ§Ç\wWÐÒQt~™ מy¦{j€Ž®³ï[/¾Ï#š9ð`ê„aEÏÇøõŽ˜ï}j½êzo…µð|ÇrŸÕ„ãœøÉ0R³4&âVh©Ð•òÞÇ{ÞÝëܧ†8™?1šèì.O}c¡PÏl߆ç>”†ˆ®£§Þs.칈åû±ê/ï¡K o·>îg” ñ¯œ mŸ5üt0ÉCm²«wÌé\•]MÌxñÌ¢zfóöüÅZw ÷ÜùÖ7ž½_³opœQóëmžtj‡lZ3²L*8ôhù¡Ô ¤ô»‘Sس7õG´àOVÏlß~ñBÆsäŸËZ±~wA»âû¹8NTà‹iË£`7ÊzÕ¢O)à>íV¬í6dbßébx0kñŠ‹ ¢É©Y±ž£6ú ý™ñ _ˆ=cÏIÌ~Áë_æðóê#Ðgôîñò÷)0üÑøÖoý 0>18JÕaxÙº`jü^ºd®¯8ÇÿwõÌ×Ùc[Çñû‰Ù¾ ï‡rV`¶yaš«%g¶f€£y1Õ“'{ÂùÃ)r¾ã¼†4n1ØÙÿwçøç%ßés·gô¹·;H[t\¬îÈûÇéØóiøÔá0Ç©læì/)Ð>Ï¥[3ü^z:ÌHèSÏÜS$VŠ&‹?înÚ>{°Áö%ùº ý³Î•'XwƒÀë[\йò¾Áq¾½Ûî~´!mU Çñ,û¬JçFùÞ=–Õ´˜¸mv×èßqùuFU¨ÓØða±­ðÜëÁð@Z0õT{ÊkÄë_Œ™õ"ºA˜ñÕV©p~”ûšUbÔ±T¹½ @Ãqî{NÀæì}„¼Œ:PæÂ[™ƒÔ}Ý'hÇû¥O¾ÜÉáºbŽÓaØ<¥Ã)[›TÙY¢q/m ¼ïNP¥é¥Äf¢IÆÉ«ùÇCU¿û¼øù_Œ¿ÈxN÷z_mËûÇY¿o—b˜Ý!¨t?tâ€R©à¦‘ªþ’¾Õ*®û¦)´=¼ýÅ|ïh’Zõc›Cm ë;þþò@Æï?=–ñóÐî”ßÈ÷e9^ÿÖ±ªA7߀Ea¥^,ü–aŠi=>&Ò}»BÙ5næMÆÞÙµlÑsWþõ6ö÷ø}³|¿îtöÜÑì'ÚþáÊ=×öDí‡oR'û;\?%ÂAx21Ù­:I I¼þÚ&š+Jõh5NEØ<–Ý—7™qª6¯ŠŽžÄÿÜìgoÔÊ•gÖìŽ2Øæ~ ´myªmů‰PA{ëŽÿÐz¤[ðÊj“p>»®Sâ©þ:Âø~lýÊøKl¹»Å½öÕöñçvÔxý‘¾žåƒz«aТÃÛl¯¤À†°Œ;C¾%Bÿû»¦v´! Ÿ~¹3‘hˆyùÜËŸ°Ï‰í§1¾#ÏA-ì9ûÞÌ>Áq²zv¹umý¸ÿê¹ïʳ)P¹wúí[ø½óõ"†.µîÜLC?·¢u {ŽÏöÙ¼œñSÍ>Áëo>”ùyÂ]P·J\Öó)woML^a"}®m7/l[Óè † ig5xöƒÂ:†ñ„Ù÷Ο¿ ¼­¾ØGÒ½ºžßcïªü'D¦@•øÀn³^%ÂЀ—fíêyí:–ÏÖ 7(Ý*õw¶ï͸¯kn¤¬›Y(Û.žÛûÆþuKðúVõ¿ux+h^—>ïy,Æ5}?ÕƒD¸÷NöéÊ&9¼°»¿xÙJ Yk“÷áëNaߌ}>Œúfʘµ§ß¿—Õüºo{³›Šß?pœ}ïB­ý›àÑȆ™ucSÀy»_®1.În1~ÃŰ_÷ ÿÒubm©[ñû4÷dl>Ìxq–çüüðú<¿ñW¨¸°´³w\ |ô6í.Ãzî¹êW߬ ÓÁ ;£ŠòœZ óGž‹›+œbü;Ëór8NÁùÌé¹ö¿@Ô…´!ñ )CÖîvQ'‚MŽÀë=_ä?ÙxIC$=E¿tÚ¨"U½bP[¡ö?³Û*ãå·ÙÀÎúGξY×®ÇKã¬0p ®õÐñúÏ'è ñˆ*cš®M„ANÞSs»ÃiÏŠë;óm§¦Šç¨Õ†SÁû›o+šóœ±âü;=ŽÃÿ÷kàÜÜ—÷C¤@«o‡>¿ôO„6!ÚYÍÜ¡´ô̺4$~ÿfqäQaŸ‘í˳y7[OZžÿ3áõ­W¬gL_Íó–u*,‹ÜڢܔDØÈ×ÒºçêaùÒѸ$宯_x.Äæì9£]eCqŸÊX¿åŸgòïê_¾üèÎuäyù@Þ¾¼ß.¬D•ŒJ„W£š×Üsߎqx¬òP±1òK»¢ú*>J‘±:àû²­3~ ޳»OÙ€3—@F³Sš¶K…MÕ®æTñJöó> Ÿx8曆txo­@%<¿`ŸÏ×Ëú;ó¿Ù'xý+­£~¾è¶F‹ݱÀ):OÞÛ(Ç=ÞdÜZ6¨rØ9°JZž†dÿr§íÞHÕïÖ[ì¼ß'¿Ëøý¾V¼OðúÍ»(Ü9.¹r3‚Tؼ<\ÝË)œÓvך3Ò¶Tâv¤5's¬îú ü_öœŽõØävLüüU*ô{³Op~Þ¥5¾wî´Æõ|vd½Øú‰ðËŽ‰eæô„Ë_~3nÞ­!*?«ûÝÖÍØóþ9v¦pî3² wÂÈ¥ÏUã|MÍÍ UöÖ— »gþ²ùAéDpÿ¡¼­ov?ˆð­ž^û¿_?_˜Ï±y#ÿüôì§×éÚ;;ߤÇë3ù¸õ/Œ‡T8UîfH÷ÜË8%{Þw }>£!½vèŽyÂ<žÕ/¿¾º%›P×·O©…Oe<çÚ©Øþ” Ç™ñ0¾ynlW½éÓñ˜A© u|Õbýe˜ÞMòúÕo˜û¶ðLÿe²è~ʪ_GÏΙðýþŒígóûm‹ùܪ¾|å§­}k'ß¾[rmN*ïi´:hçeP;qOʽáç¤ÁçÂñþ4(fN[Ç–ó~¼À;¤ÏET»¾þÖÒÓ ÏÏÒ‡çŠã‹²i#f“–ýîMmL…Z3?zôYt¾Uá*Å ^Å/=73KCBJ߯à1Wà³¾U=ëC—Û›~þ~RþèýÈqœAöÉÙÞ)þ¤RØÕkÎûSáÐÙVÏ3F]†º:›ùÉú‰-Û¾ @úl°Üpl®ÐWؾ¡©67ñ ë¸ ê~’.I‹í³ùá8¦×>íÛ~"K­¼wJO¥ÂÍ5W^Uè~Þvð ~;¥\éû­Ù²R[¹5$·É<Á/ìþËŸë{'cë8~?×™÷ ^¿JEɇ´äi4™ ›Ünqº Ü)ÈÚƒ]AvutÜî8 É{rYQ¾°îeóy¶nãÏe½¡ûüý]×_2fÔÑôZ«‰v˜L©tŸþ2Tž=î›þ'Ày³°Ÿ†,í|Âw_•¿;‡Ãúa˜Ãëeû+=•ñÏkZ Ÿ£Ù'8Î{ë©So­%³Þíz{öU*tû9ɶáez¾¹(4×=úæF‘ÚC82òa~Å|ÏÎã²}ÖÙçhö ŽÓ\Óâ³F´‘$Õ»’ðÄÚ¶þÑ.?^†"Ñ®‘7ÛÀ±ÏïÛEøÆ]ô~X_aç¢ÙúšïŸüúÐÊ3_¾-çèñ—kƒÉ U~S'‘‚—öXZù2dÛ†].ø©Ü–lZ¿kU±3ØX ÌßÙºü`߃·îÓûcYaßÀò¨Çq0oÐ…Ÿ‡+ð¥áêD}Õe¸ü¡Ì€%rˆ«²ðÊ_¢¿Ÿ¸@¸_ñ}¶%¼˜k÷ùRU1ˆ~ô šUŽ~Ó!·GSÓM®¼Opœµ¦ö³qÙ÷~¤ëÀjFØ ?5ÿù%Ø{wšwÝG=@ä¶sÈÞˆ(òT Ø9ìi¦„k#é¹xþù‡ŽsñøönÞ?l!™V‡öõ´3‚b»ú¾CÆ%xô¨nšÛÓ>0á‰æÙŠsQä7Ý%ìüóû~Y±û!›ÙE—Û”ó °õ„Ù/8Ný‘©â÷’ïbÛt4ÂXÉøÞ5b/ÑýR/Wåùø2$Š´»¼úÖ•¸¹Âó)ÖÏøç9·þ,¿Oؾø}ÇéºøÝ+£ÿ6"2OŒPÖ¼{ È”!‡ßtæÇ»¢H|VŒ·èáa^ÄæqŒ«Îö?ØóWö~;ÁqZX;•ÿñu(ÓóØoéS ê´£Ú÷I— ëìé9k. †¦9Qofm‹"Ü)÷1OçûëlÊŸÃ{ ãŸGµ*v.Ç„×èJº>ÚA¦<ôzA ›Ëdv®—`BÆ´)M ‡À®AÜAá(rþÀ†Æ™9s…ó %Ïùóýó“pÞØì—ùòi åÏNù´‹tï¿2k»¼Mƒ^Ž©r ~ëx³áë†ÀîS«_~"=óÓI“ùÂ>ãóœî2pþìîîËœ„ý ³Oðú—~qòom·—ø8gׯa„Ö~¥N¾xt¶«´Ü8.duŨ†l|2OòôÈ|áþËž«Ðsšô¼ô'áw fàõ«,럾ZM:¦=>°^gÏ–¦êóAölU©¸•¡ÅÐJç{EgtMz8OxžÆÎð÷‹k2~¿3OV’sköŽóñmÖVëÉûèó%#ÜXSs™ý¾‹0äÚ8ÛIü áúîf{ND‘çâïÚC½æþîü Ûfó Ö·ØgöŽSyOùþÕúî'‹êÏ6”M7Bîâž:¬¸ïn Y>¢¢'Ø ×—žy7Š˜·íes„÷ÃþÉžÛ±ý`Ëç[j¼þäDQ«Û âMJ»_LFø>']]iâEðõ?’8}Y¸3eZwÇÊrüöòÄ0[8ÿÀ΋ðý$G˜§°z3û¯ŸÕ†ÛÈ:HΨ''/°Ægʯ^áN΀^ÐÃ\PQäU‹i;sfÏî#l~ÊïϽö3ùu¨cñsë8Î#ßw ÉØiq¯C¾a§rpõüÆaîÚÖÊ^ƒ@+¹æÓawé?Ua£µž-<bÏç§yžª??õ†ìP³KwVù(cþçûógþ9½W¾<¨ïF×C+‘]ý:µÒàÀÓúIò`FŒÿ¾¢¡p[éS§p`Ùü±×½ƒS”„ÝWÙ|ž??#øÜ’_-ÁëOŽkùö\»Ã$½ÞðÓ`‰áëö¤ ЦrýWuŽúÀ„a#z>q‹"qU,h`–°~gß ÿ>beÂ|›Ö-_×üùj9޼o|¹-ºÃ¤ƒøÊ¤Ê½Ó`Ô KåÛ.@Tà¦[—n ‡„‚•oŽŒ"³Þ—Kžñy¦°ÁêŠe}‘íÿZžõÃq>tYv;Ü3Œøê¯T¬;1 fåÞ™½pܨQjô ã†Ãþ“*Uêò(âlûD{¾õ,a>Ï>ÖoÙ:žï7Í‹ûÇ1ê”’&?„“.§ëç¦Á‘­>Ýö4ºw(Ãþœ¯ÆŒ"ËÚsŸÈ¬ß ãt«MãkàŸ7¼’±ó~–¾Wã81¿ÔüÒéb8iôõý|Ó.üþÝ'‹ý~ÊÄœ y—;tåì$cºF‘ÒõºnÙá¦Ösl¾Êö»Øüˆ=×°¬=Ž3²ì®C:!•¾-uy< ^iwÆdœ‡m6nqcƒ­¬ÑáîÕ¢H'»(§¼@¥À³fõÆöWØó@ö\‹Ÿ?ñß ÇÉ–r?T:BtÃ~MKƒÊ9§¢hÎCbAVê¸æÞÀß"‰½÷Ë>¥0Ÿ`}ñßÙzåÍüÚß®Wâ÷­æË½+ÄŸ9èt”Ì‹ 0.*Lƒ ó6œ‡˜©vÕR{ûø¦ÛF’M×OšøY)<×.þØ <¸šÁýpÒžþ{þ\¿ǹ›ŸR=õÖQâ¿÷ÁKµÓ¡RÓ˜©³Îÿû€¾Ðï: ßF’òž?I>V þg>çï'ÉB?fýÒòü°Ç©€ŸÆ³€r³]íÛ§ƒÍöÝ=^>ײھ¾7Ý÷Œ$9»äc”„­?ø>œ,cûwl_Íò{÷Ãë·ï°G”Ó%’T8åºÂ+ î?ßuÎå7 ï2µÿQ×%}^:âI®ÿÔßíß0ÿí—Íœp¬y?àu+…;x¡%·»T^ù±åuˆJof3ÐýÜéׂ«8 "î¤~$Iïô­ûÄ S…s l]Íž¿šë¯ÇûSKª~½ý)®Ãj×ÖMkžƒss’;¼cv4ÒçûF’#mûºvÜ6M¨KÖ—øý¸ÒÂ}ˆ=Ïã÷SùçF8ÎáòÜ/¸µ$¼þ·‘¯ƒØ$;]øL‰½Çf?è7vqåêF’uíîì¾sz‰óí-Á˜<®œË÷Ï2ÖO×™–µ¼u̩ؼD㤶[ç5½Ì1Òqû«¸ôi×a³×/.Wô0gk!!žÐøLøÝèR‘„ÿýî Â~ÿÉêŸÿÚ#;ßÁ?o¡ç~ñúWO„ ]Òíû}þÌÛë¯÷«s±V «7¿öh¶ ·N$©hþaÐtÂÖ߬ï°õ{nÌÎé³ïñ þÏú'+ñß'ÈŠ¾gƒUQ–™e¶¬-Í–Í£yf¡Àcåx…e‹¸‚ ~µÍ–-¤¹Cá‰&Ê3Жà zXð^¿ÚÑ"oÄ‹òX ÿ̗ʰ–Óì³<š?JÍb™?¤¦ù²J æK8ÍRQžÍNùÕ*šÈåY2²,Y%YŒÅÊåÊf¡äh¾pš[¢¤Ù%”eP2W6ˆ²^¼(Õ–f#š(Ç œr«•¨,Ê §yCÅb•ZäÉ*hž¬ƒ³š±-™Õhv-Ê €ÊCy¡ñu%²P¸<6–‡b™Ç¦¦,Vš7Ä­ ¸LÙzä?=2ÀêïÕ#Eô=­Š²•,ó¹üí TÍWRÿK0”/cb9X°W½h$WÐ>4G–e³åÑüm] &–—³€±W¥Xø¡´ø}(K3ÁÄ-¢üUšËT@¹ÕjšÑfÉ­æ¸/bÊ_eÜ-eVÐüm4“–²Wh6Ç«¶ä½Xfo—ä½0Ž`Ê„ò@óiQb4  •Es&ƒiÆËšt <,ŽWàC‚šË–Gs·µ”¹ªB™( KKyÕÅä8/,Ÿ’ËÜ6¢-x«ŒƒeÉ[õB³ëP4|ªåƒÆ×£$hþ TÊ ›€%)‘N›‚åUs'F€¯WîÏßµGþQüŸîÿÕ¾øg=ñïÖ¹^X²þOô@1ý;®èX޶%·Å‹fSJhŽ6ÇtÀ‚ F–á¹€\޶£óÊÇ‚¥@¹-:ˆæàrÙsZÊär´óJ0¯(€qT¹”Y4ޱ|þ„= £UÍÐæØÓ*šI'E¨Q"ʞ΢ٔáìåNsÙÙ*”‰òS9æ€mÃâ¼iK %kZ…2Q M¤B™he8Í¡d| >‹e[qœ TÊ §¥ì¿šAÉñRÃQbʵ2Q¾´%¢|i#JŠÆTS¾4ã²pÙ~œÔàŒÔTÊ «CIмA¨‚¿Èœ´ÌÆæXŽ2.ŽgJs¿®Ðþ3üghõ÷›ÚÒלÅ}·X˜¡%26¹Üï`T!Ê‹f—dÿ©Q" v•£Õ‡for?šÑ+Á¢ì?,p} v•K€±QåXøj”¨*ŸÍɱÿDÕþcž@0å£z¡AÂ)‹Å‹fõŠKp¥9‹-å£2ž€Ž2¥9KÊ Í¤£lTŽ%P@yÒ– ËŒñ’ Æý B塼Ð|:”-0e¢9Ÿ¡ÔŒ,ëÓ‘r«8Ž€eþq,é ÊüóA£ê(5•G™U:Ê’þ+ÞÇ^a9¡*.«%µà¡2^•%ÕÍ®G9 áƒQ…(?4¾倿F¢|° èQØ‚P(šÊñþ|(Kš;‘ô~lõ÷í‘ÿ›ûãµ7þ]úâµ'JPá(‘uQf¹%_…ãùéQ4³œãù9b†Ò"UÐÌr©—ÊÏ‚…Dù*/:•‡òÂBÖQž—Y^P±8—*ˆ2U‰2¢¤”ågBI-˜ ~ÂLÐSjÍ-çxѨ,”ŽS^4ÇWñ ,T[šaœGYÑZšY€Ê£TJÒ°8'Ú’ãW’€Ê£?.¯ÜM€ÊCy Ù´(±W…1Uü(‹ÊMŒ*ä2ËÑŒ:ÊïãrÊó¸u1Ó–2¨ò(:%¦\è,”œc4P.4ã¨È-òÉÌÓÐ?`ž¡ P>hl=ÊÍŒ*Dù ÉõÜš^€ò)‘MÎ1û¤hü Ê„æ*ºæ‰û>ø¿qžè@_“‰ûî°0Õ´8(#Íb¥…êCóØKòúÂQb æ”Ô‚qê‡ÒQf Lj֣°°ƒ)¯Ï ÜP‚9ågÁq`ŒS,üp”‹_Ay}b –ƒ´úóB)çÔ ¢¥&á8ÑZ”m N´ŽËt§œSÆsÐSFt0ª€ã¢™ô”qʱ ð|hÆ¡)™ÿ^’EÃX}Á¨Ž=…æÓ£$hÀ TJŽFT£Døå(PF””ò¦8c*(§cCSNŸUOÙ¦A¨‚ÖË‚ËV’ ûW|{ʈrDó‡Ò @PŽØB)ûPŽ !”6ÊðÏüñŸþhõ÷ëVEL{,L-JŒÅ©B™Pr,Òpži¯@éKð½°pu”i„* L{ƉU¢ ( eRQR,l5J„Å­¤L{9y8J„…®DQR N,Ǵס$Xü*ʈ”  T(ÊÍŽ£!T(ÊNy±×^S^µå`Á¬öCÓPŽ”Ë™G2¢¤h"55cÚs¬X5J„¦R Œ(©ÿ%Bƒ)QY()M¡Ù”Œ/‰¦SSã1ž½#åÙ¢¼ÐˆZ”-šQÅñ&QhJ-JŒÆTµà” [¡I•(#eÄ2–½eD9¶-bTú9çT¢üÐÌ”#:U@9öŒ€2•àÃþÇ^ŽæG‰±(QY(965JÄñ*QʰdìJ.qU |­s¸Y²?²ÞøWýõÂÿnßûÏö¹¿k_ûwïij«â}ŒûüôÜkÅÂÉBɱw…së`,¢,î,5ǦEÙb! òP^Ø«tÜsÊ«ån¸ ”%Åž¤F‰°Ø”¨,”‹.”ž%»6•Ç=ÿÀ"cªPY(9ö p”‹R…Ê¢¼Úîö=ʲª Q~Øw (Gì9¡ôæÍqª (G,æPZÐ ”%mXœQmDI±ÈÕ´Ð-Õ¡´è(Ê‹?”ÀeàÎÁ L(9š!%FC¨P&”Ž£9”(#JŠý&”NüP”#ö›P:Yà¸Ôz”åR |ÐTzÊ¥F¢|Ð`z”-š, ÏÌõ@³…ÿ3W¡ù”¨,”MŽc¿Q¢²PRì7j”ûeDI¹ß‰Pµeü“µê?ó±]ßúg.ö×s1úÿ)ä>W,JÊ 3•‡òÀÕ¢l±H•(J‚Å„*@ù`ÑêQX¸Á¨B”°%Á"V¡Œ(ÊãÎBɱ¨ÃQb,lÊ„òÀ×¢ÄXä*TJŽÅJ Þ¥G9`á Œ(4@*å…FТlÑ ¨<”šB‹£1”(Ê–2» (GÛ"n·eDIÑ4j”£De¡äh p”M¤De¡äh¦p” ¥De¡äü;”Í¥B™Pr4Y8JŒFËBÉÑlá(N‰ÊBIÑxjj>”%A òP^hFÊ €Ê£,ðp”Í©Be¡ähR5J„FU¢²PR4l(5­e@9¢yC©(#JŠFE¢|ÐÐ:”M„ÊCy ¹µ([4¸ eBy Ñµ([4{*åÁñÃQ¶h|Ê„òÀŽcP¡Œ(l*Ç©ÆM!xqþª'þwçaÔë,ç^%ûÙÿ­žõßgqýêzëOÿQ_býè¯zׇþ¬ýYÿù£ÞSr¾t€¾‡ì-&”ö-JŒ_´ eâÎã—­CIð B |ð‹×sÏðËÏãÎŽ`ÏP¢²Prìá(1„ %ç~Gaaø¡ Ü3nÿ‹Ã‹Ã„òÀž EÙrç?¸=}ô¾刾¥7Lʈ’¢ïÕ(“eDI±¨Ô(–•ŭǰÀDX`JTJŽ…Ža±)QY(9ú^añ)QF”‹PͭŸóXˆXˆZ”-ú<å×r{öXœrô¶%Bo+PF”½­æöêÑÛŽX¼…(?,`ÊýJoÎ~( tÊ ‹Z‹ca«P&nm…>£‡U(Ê=¬åÖUèaJŽþ G‰Ñ¿Y(9÷».4—†ŸeNàÿ<¤ÿÛä/o~Eœ¯;&äêÄMæÒ×…\Î…ÃëÖÞ¡žËyGƸ÷|þÊcYÜʪŸK{EÆ#skvfÖ’çSˆe>ŠÕ |y©ÓeªÕ€ãdêªüÅ•Î]y¨}ßzx10'ëb7èýzÔ½Õ+#É€áýoV÷ü;ž&ã:3Ëß³ä]Jp—ÇaÒʧ“v󞩫ݼ{ƒÖ6›­‡ÔÆÁ£<Òºƒkçn³wFÒëOü].,Ÿ÷ôIÈ×d|öz¸qä8Î×ÚõìOÜŠ—êÏz}ZVêÙiß=¼u8\ï®·œæRî‚#IV4›†+' ù„,g‚åp³Ü@îº~xݳ^úŽãN\L`µ ½H?ô°Ç_ôâ[p1ƒÎ"IÅcŸW´™$ä¸ñ¹"!çñwXnwý¼¾9vnÖ rÞéÊžÒ2`¦ÆkÄ̆z8Öy\Ðÿp­ìö [EžÛ6éwÜC–7».ãÒÉs;JÓ÷Ó¼xÞŽÓálÛ6C¦œ /mï䛽Ÿ-³ÿòÀVŲ¡vyÃRç»Ç»}‰ ®æ@ÉBËY«Y~T%xñJÆøcÜuõxÝê'+ŒSO;AÚÞñÙxEÔL7¾kA`‚¯³Õ‚¤A0ÉsÛ¥‹"hNòd!W]Ÿå¬°|*³ðº“ÍÛ'ˆÃ Õ¦{û3À­’m“ŸFØå:¤¶Ý¶A4¯2‚¼y¤Œz¯,pï€ñdÍõ>8_™›ÒÏq× riÈÄ{5Ïe@³ÝT$”÷5 ܪ0‚˜ËñË$!Ÿå1Ùì‹ß·Ï/“àõVs˜©3'Èh3H54}}–\9KsõúÁãóm§~ˆ æxç›ÿ°œ>‡úlà ÷S†Ð²B}°¼s]ã8\ רä ï—¨ïü%$múÞdûYh2+¹iY/h«—¹ÌÏ -¢ˆ½Õ÷ BŽ «kÆñe<:>¥E1.‡ŽÃÿç1ä4\ï4ž¦=eÞYØð8ëÑ4׮ਰ³>p%‚<¾p¢tmë‰B~({½o–_Ÿý´ Ÿg€×Ko·>Á=†ti[³î—P7 RxØY˜¢îÖ"3^³xú¹DE¶÷>i³Ã' 3–«ÇséÓd|Þýó=ŸOC9,8ŽéÎÛÉàC öGK®ùÜ€õ=Ë,w9+p¼¤ ±ý~Ž ¯›µ¨›9xaùq¬þø>sSÈ!f¹Ð–|R=Žsäc‹Y¾1äcßuŸšøß€´°O.5kž…åoÜ–&¯ï ù73|AöŽøiþõ$¡Ù?6qëÚvú£/eŒ£i™#hÂqb|Ö>l¹<†ˆG.êpcÓ h²pP§g`ï$çüé'»Ï'Ž ßB3;3I¨+Æ`þdýß²[ É—?y²ñR§#1„ç߀ªžÚƒË/žv‡v|ûuc/0ãGÛF®yê¾Ò·“„ÜH¡®Ìyº6BîËÙáópxþ°Ç ,àÞx ‰Õ7¨Þü~ÿÃfx<ÿå Œ['-¬ß ?4ýz¦Fƒ¤ÙǪd\¥ÉB?fÿd|K>çüÍÙ¬@ó)7ÇÙàÞ,°r| qÝyôò¼6æ§mQ^g )áÖ»¼`kêë.:×ÂçDM"Œ Îî/|îÚIãX˜ý€×=7»¶sG#~N{Fæõ) ûÞLÜ/:Ÿ¶f8pÎ"KÅÚvéA«MºýmËD2ò‡Æ”)E|lþuž¢y[™2Æg¶ÌA Àq^ãÿ&†L­rõIýL¨ðvM—Þñ§á«ôÝo»ÅC R=czÍé¤ÉâÀvC&¼B–¿Îç‡]’±1æK–eö ŽÖ%¾j­š±$±—Ä” îáî«£'ží¸°ÊáχÀ¨M3û>\AR‚Ò³M?Œr´™Oøüµ»BŸfùMÅ|‚ãt?xØ·_ûX¸Äxbx&ÉøÕð òih×fÒ–c†@vt©%'F>ï{¬SÆêlhîú9‹>Ëxÿ¿’ñ9ù=„\(³Opœ¤!­–ò‰%Ó®gþaQ&”7ƒ ¼vi£2eÉ&òþ­GFÁGnÙDéý„¾ÈúÏó¬Fsô­)¯¨/å"ñ<1«¡ùò¥s—½ÓÍŠ%Ã9[n΄_Î6qQ&ÀìaÑØxC÷“Û:ôŒ <7ÙOȹfù{ŒOÙórL@‘ .5®Z›‡î°"2lè_{Þ/8ÎÛ«cìæÆ’¤S>.È„”·'4m[h:ÍÃ|C¹ã„:7ûÇé]cDæ×ñ±$"Ñþ§ç3ÁËuØË+oãÄS÷§õ†ùóFmxÖ'‚ænø ,÷Žñø:~Js Käïá8¥Ìޱ䭲Ôõ)w3aÃÆw T—ã¡Ô4GÅ’ø°Ð NŠ gÖ;u膟¯ËÞËÕfœK>b^?ëzåeý3cˆ£Ç§˜µß2!´‚kfæv|{«ýöÞª©­]÷‰;Û;V°m±ñÆ öØcGEÅŽbÛ;v,[I°  Æ:cÇŽmzìÁ;öÿ³²Ö\öÞß9÷Ϲ÷;ãnÇø ÇøÊš&ë}Þ5ç\3ÏSÅ·‘ò’ôìRí_´‰ ûÈqß[Ñß졟/ÙûµGàú;]•7r-Žc#——¼y¯Â]Ê,5çA½qGiE¹„-Îz“vð§·cOé™bÿ€¤M_Æ}¬y÷Qæ9}âIÿµ³èÝ¡w†*3uò傯c™Jøeù]Ú¾¼UÁ~gÐæ=Îs-éBßÞmmHßõÒ¼¸¬wîçÈs7źÊI>;rTK6Ö&î“jÓÆ):ÃëRh¾8ÖcwÎ>]öÞ¥GE ìÕ-Wç<öšòÅÕõ»Ïß±Ï×Óâú¹[Óbql›`Ûïj¢NZÍ0H[Ñ÷dîßÚÐîØU§º¾Ñ³þ¥~ïðcã~Íü{â9«ó¶|عu^Q¸îú™‹‚Ÿ}eÓtù-Gk ­Ø^yé¸ToÓNøhIGzºä€ï§ÓœF;»Î*ûõòû\£o±yG‘ò‚ ˆ:Àu§üæ˜ø,5–y$GŸg"ó……˽6¨öQuÚQW!5GKþ4sÔñÉCÙ×±]ÂL/Ëɹ½^·}¼‚\¥|c)Ï׽ؤ̀ßŲÝ× ö…›Hw6¡Í–YJYÝÌR©ICºr³óÕG¹£Ø´cUûöþùï•}Ñ÷dÜé7°˜”³PH®ûÜkM†*bqéÃá[c™SzƒF] -3æ‹¿JE$–:tÓƒºiÝÂë:E±Fcú­NT•ç¹\Ïÿ½b_~.ëÓVÏž“ËJu^¿q|ëDzp¯¹óÚJ{~dh¯énÉ;õ{¡|.Äçó¼ÚêãXÓ{¾¾Ã–š_9vj"½ÙZ¹è®´Ã$ú’¶¡‡ï}»MŸ®g§KVíî´cˆœÇÍsÉøs}áöGz¶Ì/í{T‘êQ¬3Æ9¹ÀlXº1†íÕλqx"åþ¼µ÷º}‡éF9! Ò‡®¿ŸáéªgÏç­3ßv"Ï#¹>¹o¯x¿¥œ­>*1¿2† ®´¥ ‰Täw×i‹ƒS‘¢·­µ÷t ãêÙ9ûÕ³/9„–Áòþï[¢_ëuyg«o\×9áÕ—Fî1ì¦çíW§ÿH¤Eëg”ÛÑì°”'Û‰¦~k7ýe®(&æ- –óPø<û¦‹÷U¬®»ìFýe ërñÇÃW‰4¥mû‘ÏK9-HôCbâãÎW~ñù ÷Ç>$“Jû@¸îþ~¹ÎÎêí¸äñ2wu;^eõ/—‘füí¼·>¶§Z›v~Ú7Љ¹ýå>ÅÿæÏ°?N½¾VA|½`Å8}ÛÕ<¹Íz€ù…ônî[.‰‚ßž·cÃ!z™¶ÑÚ¿zÕQH6bÞ“ü­;¥a¼ùߢð)—ä‹ÏáäûF¶úÇ8§.mksèí¶ñ¡÷×Å “hòÙÑ‘C&¢"­Ž³%  ¡Âë§G1Qg}dŸg^ßÜ_üû©¼°_o1ξ۱ÊÕ°¶ “$êþ&b¹¥Ç!:ÜgðÖß¼hɦÛ[N‰b½n½o}wþ ¹ñûÂ×缟M8t]dZ Ù?ÝVÿg&‡°æ‚}®_uÙ–Ë»hãCÄ}›—tqôØÒ"Љ¹eCäzâ}ÏÛxë·z–ý‡¾ª½ã„ —l¼_¹Osg'Qþ ᾩeÑÂòa_›ÑQ›K’¾é™̬-¹ó‘×|<>ßá¹<÷Õ¦ \Î’â Ã`«Ý… —$Ò¿úº=×·ƒôvڇÃַ¤|·>b&ªgì²y›‚ƒåç ÿ[\7Ý“÷ø<Ä>¯K…qÄœçìÅ }Þg“È8ðøÔ€›éÍ–óæÍõ¡;A3»ÎÁ¼mÊ¥¥w,ûo‹ëÛŠr¾8¾´^´¶Ù–µîYæÑ¾gEžÌ†+s`ß7Íù:æaµª0àBÑMiUØŽÕ£‡u¢Ã5=Ÿ[+е*'$ƒÉö®M%^?©âbt–rJ ȹG6½àú;âû¶Òˆf‡§ “dbS_pÊÅn?Þ¦DZ&Ø·Žbâzmˆ<Ÿ’óË¥½ò:í÷8ìKlÓ ®ïýðØê£~Ñì±`/_:™LÛ}+\+wÎvúêì=«3žßlNéêQlÎý áÇÖþ\gòqx~Ào'®ÞX?꫼¿”%oãËÿ##wÞh¶N×κ A2Öéáð$ŽœÇº^|¨eö<¢þtWÏ–¾¹ryðÇò>0÷«æþÃßnÖU8ö\žßÚÏ£ÍçÇŠŒ„Œý,¡µÉú®}2Ýð;¸ÿÍÑ8ZÐ.G¹!W[R ט£w&éÙŠüm©Ð_^ô+9¶õcí)/ä©ôœn˜%Ê¡_†ªöÁ´ñqö± ú&LšLŸohŸ±>ŽR¦Ç;¼Ï×”*ž©™ºž}~ujôº_ûËù0¼‰ý=Ó‹çC¹y;n÷=ßZº_¢N0Nðœ+tßÇζr¥eÓ’iÖïoïõ™Gb.E]Þ±øÒöz&æ{ ”×—|_£R5÷u. éùšƒ6ì¿ÒoJ'×ñâú_…q–µ\ÙçÄ™½lmLþ {–$Óô&þÆQqû‘¢zÖºþªwßngTÕ±‡û3UN¡!_ ï=:ŽÞf:'¯ï^ŸR†×8[é5ÖŸŒW‚¬CåÜZ^GÜwœ¿§á9RYrè0΄0—GùúF²E¯Îî)Ù*…•nú¦kÜ¥|8¸msj8ðh»üXo…ì{u¦EK¿lÏãšR=šd}Šó›‚ÄórmºÁ8oÜÖXç Ü͔ϋEåœBùV>xëGqs¬1;Zb]PĽË=˳ô}Žã~¾·àólž¿ÂŸgöó$_\?¹ãüàÊw1}­¼Çܦ¦ÐÐ#Ës棚¿ôZóL+*ÿ¼ÖÔÝzÛ×wù"?9GžïgòýYžÓÉóÐíç¿ZŒ³ËçdŸE¿³^Ëþº"…ÆG^ëÓóQ¬”“¦¢êÍouX:LÏ/IõŠºâÇøzœ×‘ø¼?ëÅç¼.ľ'Î/#0Nß.uº}s'û>ìãÃÁÛRhgÝË¥£nIÈ𢆪‘‘]}ôÌ\óèû åüä÷ |ÿŸ¿æs]Úç¼…û²¡÷”^ùv²"ÓçïM¡Bß—ˆÝKžïg?Üܪ1ÍX3ýy5ôdËW,¿ãó:ñó½•s¾ùzDÌ1çIfaœé¹oo캃]p?¾òá‘zèXúüªU±´ç×.(kQÑ!utIãôLH]ºÚ}€œ'ÁûŒØœ¥}›÷^|¾lÓËèreÎÀ†!ÛÙÔ ÷£ÊžK¡€5Õ\ÇÒÁÀ_©ÏÒâÔ¯¥—%qž‰ïÕúÈy<7Fü÷¾öêõÍ¡¯®—“œk%×ãt¶mômcÎÏG½Y}'…öûêï†ÆJûƧN† éºy£¤<µò¼’¯oÅ:»è•9º÷²Ko>‹úÀuOŸ¨7þΔv.WÃéùž¥ûÍ;#\qÝ&!ÏOZøÙkÉîr#úE±Ö¿†Õùu¯Ÿœ#ÁuÏsPø>ì_åNúbœ2!ÊŸ¢[ÙÝTI?R)ÇîÚ~~³bɯ¸6,ðE-2„yTÅÄÑQr _‡òÞ¿RÞ~¹£Ií,óo-ÆY"l‹•ÛÌ”y…o6•æß‰ÖiT,YÖG¾jëAT~nñÝ}óE±<ãvŽY7Zþžøó„ç¬ðs|iÓ®/¾ÇÙÈ>·ÞRy@ýTrýx'ß;Š¥£FÕèă>tð^të=K*óä×{GÊë/ÞO¸þÄ\š /qß¶†¼>³éã,5UÓñ[Ïš-¸0³D‡TšfÊç4Æ9–]{X½”š†5ªÿùFæ/U„ºÒ8uhÁù¢-6-¬$ïËŠ÷ÉAz>× ^6}`œvB%†³n…nX I¥îç¶,¬|6† -/Øçe‰n´¾brÕ±z6wôKˬ#Ãå¼dþÞ@\—¼–ßK‹uíže_Àa`†ªêùzߊ¼^Ëȶ’JNýÛFDO‘öw»S™AÓ†·ÓB‡ÙÌÐÃå}lqÞW[þÞx~<eÓ®¡ƒX½†]ZX¼ÛÌT’Öõ”·÷‡¢óîu£#¥׬]¡gÍ>–«ÓøåÏ>Ï÷ïxŽ1_G®¼<ˇͨås¨0Î9ݧkÅ­b3®8çj²*•N:ôÚîýä-J¬ÐÖ­+K莥g¥ÿèQbþ?y]ÏuÃóÝÕÝ|;V—ú@!Q'¸¾-V±èJ¶÷Ô}ÿÈTŠ*ÞHéÝÈ­Ò­¶¥fw¢UQ𝻢q}÷Ôʹg“ûÿ<¼ïrðûa¿îÖbœ¤ÒÓñ( cßü=‡;™JkfÕÞßüZrºÎâr™>tê‚/>ªž=,”{Ë´B#þ”ÓÉç“⾓Õë³°-ºÊSÎm±éãô95¾ÕÒ»KÙË\_öOº•Jõ*´èX–Ðô^](Ó†„]ż¯ô¬Ò‚²çw²²îy> ?·"^×QÎ×u/æR1ÎÊÕï<âš,bõ„ã+ORév÷ s{•8@ûuÍ-Z¶Y—tè™të–‘»¦m¾ýó=ïä9+Þ¼¬”C:¯ñQz.zÏQµé×ó ç3ñ|F*yl¾íu4ÕÜÔNõZÑŠÒ¯ÞVc—^š×ùËç‡øçáïyøûj±Ï ³édP†jOä©ùËÏf½ «§ÏŸFç‹u5œ¹M.•_•¹¤nCÆv>·£žy_yþÖõÑÈ?=ÅùªÙk嫽;œÎ<ôÿû†Y¾/%Æy˜R5¥æÛ@6zCЦí•ÒhìÜÊßœ·GS—A5Vlµ´!ï—ú;êY©nm^×=úO¹t<'›çÙðuw–÷|§ÞªÈ’Ó7 eel‚I£¡ï}½8)šnG6+SԇބϾ”ùQnjϷ4«u{”|.‡Ÿáï¸^Äy‘t^ׯZèy¦»š°ös˜Ð;Žy6ª£hš´jÈŠ€ÑivàÂb9+c¾ò~}¬×#ÆÏ9pÝð÷1|!ê¨ZÝk1Î7!{f-º¾Ù÷Ç”4:ñ)¦Ï‡hZâ»ô45•ê*(JÏl·eýù9)¾o« ­‹Ÿz‰ùT9ˆç°Û÷±ŒãR.eóÁi4©CT… ËÓ¨[Eÿ £÷SYæô4¹;5ð9Õ=~ž]ë³iÞÍZOÎX0;3o™Ï^§÷ä.y/ʼnÄ~ÚVÚ_ª.êãxGOøvÌk.U)œàH£êc•zzjõm»|^Å9½èöÛA)¦ŸÑ¤\}õÏs{\7|þÅçEâþLWižÑXÔ Æy—>÷n±¡$œw4jŒ‰ú¾7Ï>RoœÓöó÷Þ¤ÆÃ7ýRì?£åûÃuÃûï›ÚÙJÕ‚ßšÓOë¨#õÅeß Õ÷¸®› ú-#u)áC2o9°Ý^)7IC%7öÊ;»¿ž}+vavïËcåý~ž€Ïïù¾kÓ ®¿!¼ÎÙ§ËÃèTÙÈîåRÓ(°¬Óƒ¦K÷PÍŒ’É}‹÷¦‡E¼Ë…ÖÒ³×Ç2Ö–½?^Ηâûaü?gÆŸû|=iÓ Æø±W媑+¨Ì«ß–ÞyœF·=ŸÖ¦EQ˜¥‡ebãäÝ;ãe“Òzötå„¥~Ëäý¾ßÚç±bòìªßä\Eñûßïûâúв“ó,L[)Í7ÒhØèЦm¢(OãöGzýÑ•†õøå|ªR/í#È÷¿âyïâºÈ‰ø:’¯ómzÁ8BQ*o¬¦¤¥·\zf¦Q‹­•:•<¡§u‡Æ5îÔSM§S^>òj¬g5K°ê{züÌ£ãóžCW£eDF²g¦—¸nª”åyq¾e ¡ÖQ¹oîw·;¥S|ú²*=íø5ªb£ÎÒ¿Ç’Ð!zÖßÒ>—~Vãónq~QG^ñ}Ðæ[Ö–7\ª$ê×kë7ŸÂëé{ÕbÍ.ét`àÃóó:êø<¬kç M´×€…zÖ'¹Øò%£&ü)ÇœŸ‹áu&æVÎrîÍŒq~\|ò¸ÃFººoZÍ"EÓ)êF”cûV:zî[ðJé ÝȘg¾Âsžž)­F‡Ó 'Ê}Ÿß>¿ÇDy}/î+IëûÁªå÷ËL™Ü3õÙ{¥b±tš\nÐÒ•Û"É{V¹>ûM=éuDâ”ñm°îªÿiøÆÒ“äsWây›„?íGO¶Љ:Áõü~ìÙ˜ø­TxR»;ø}þØ®ßgÙM”Óç.vêC æ,{–þ^Ç®V,֯ŭ‰òõùsƒ¯ëù¹ ~Ñþ¾«0ÎbZÜdR¡m´èéÞÙ{‹¤ÓóI.×Ç–ÝM_Žžžp]×—Þ-tàÔislxpÌ^÷‰rŸäó}ñ~¼ðj¦W¯º~Ï*_ü=«M/§«{fƒjîÛiŒ­‘§Ó¦œƒjh¿‹†çÊ5âc_:T´J…‡tÌ\ôYO[&ÈûüùÂÏ©ðÜlq>˜Oú¼Å÷‹gá8!0qÙóÅÓ銶`ç­Á¿ÓÝ×¹F¬ñêCã_[·ä¼¬c½[µÛT|‚ü|áûHâü8FÎÕÇÏ'ê×/f{‘µ“¦¬ž›˜Ë5V¶*¿°Ì¡txÆòÏÏŠ÷¢{7ÎýZÇbãºO:r*@Þ?àû<ç–ßþ|çK™â9\Œ3¸Ûôé-wQ]ÛÁºt?óLë¹ÅvÒàuëbN?èJ¶QXÏnŽä|í— ìýœ.“ÞT,Ê×±rž&ŸoŠÿñü˜×w¦ ßNŸfÝYQ)ò½ö9Þló*õéø}w{sÔõìyWC¹ùÏ~Þ^gü|„Ø'_xé:mJy¥®œe¿ÍaÖE³<†™#©Ë5uÊëªé4Î¥[ÿÏÍvКSÎÃX+2ÕZ·ìÚ'sjPø×f'&ÊÏ~?Äñ>x‰ïû¾yå¿xñìÞ9nYr;•çxs…ã§"zšUòÐðšîéTÛX»`Ù×Ûɹ×Ô"3¨H<׫cúwE¯«ý3ÿ›ïS‹ëÈOÒ¹»÷ò9"û¼tÆyÙ*mâû¡Q4¬×«€¦ Òéô솓^Do§#ó_/ªÙœ|2ǽ=¯cm õIòº˜¿:¼¼ªÛrÃki>öA~_dNØãØŽA%졲¶Àé´bgßÏ ‚·ÓËv%¿U5!ï2ÂŽ„Ž5qú”?åÏŸ‡÷/~>Ñ~~¬Åõþ:¾JÓ}d›~·N'—ZßšTm±¾Z›ÝÒÀ‹ö4-‘¼s»ŽÝQ^y1ðå$y—5Ö$íO=“÷ìs®#0Î¥_œŸ®ÞO‹ ; éômë™é!ŽÛ©JÇ!}uoAây Èc¤ßÛO–×Güy)êå¶—¸Nºï%ÎëÊïclzÁ8™e4¿…ÍŽ¦]-…ÈtÊŸ¹êIô6*PvíàFZSe·™KŒÖIy“ä÷|_šÏ+ø¾¨}?6ãú¹lœÐÜßóÔé•N?Œþz«÷6:8júåS|He)tµ™Ž½|ÚÓo²j¢ÜW²îÞ•û±øŸ»e9?ã04CÕ>Ï)ýÔO¨á‹!2¦SD…›Un> ÷ .îŸÚv¯¾ÛÞRGÇšÙ…'×¹¾¸>ùõÅ÷éåsdYÖù§Ä—Ë//ž‰¡e± óðtº4eßÜ´Àºo|?1C×…šF9}÷ѱ‚3œ§LßKòýhñùüYžÙïï¨pý9÷úŸÉ¿,–œº~tô›N][ +àöh+9µk½y൮44¶Ò ™:&̪Ktþó÷ÅÏ‹óýûü\_\¿æ(g¼ã¨Q—)ñWÒ)æ·õŸÝÏo¡NŒk¼»Ñõî4ѱé¶ó$ù=$Ÿó<{¾Îã÷ƒÏËm:Á83Í5º(ŸÇÑ5×ǹÆNL§uªD.ؾ™ÔkÚßúR¡+…+2×xIÇ""Çhº÷™,Í#«Êûiâû‚ó^üóˆó*YΑE`œ>õëŘw¦m5±Þ„t*:u{XžÍ›¨wiŸv±©‘ùíÅ=+7þM¿É1“ï|=Éç-ü9é9¯FÃiU²ì1Î<íô<ÅË¢dì{p4æúáuóÇn¤ýŠï‡W–í@¶ãõtLóxïÑ‚w&Ëó >?âçŸùï2øúË~ŸÒŒqÄs}‡H|o˜Nóç¥Ý8øhõUÅ\ZûÁ›„Ólš :Vr¨oÙ …¦Èû\מÁ§†.²H눬ç!ü2T…»vÄRó0‰ß{:=xï´>OÍ ´Æ³Ùµ™/ÛPqay²YÇœc¶ô1eмOÁuÂç$ÖÝG/ñümÝ,ën%Ʊ Ÿ‘rè0°md¤Sc¯÷ON]O·Æ”[xêkÒr=¦éXç ¹Û¼u”û ¯cñý]’—Xï¤<øºò>“M/§ó´ö×57PñÆ­S6µO§%k߸L?NKvûãRº½šüdÑ“1:©?Êë;Þ/ùùñûz%ïWÙô‚ë' Û  t/ÎÜfP‹tª±Í)Æòdõ¸²iQë¯iÓYeôDKd)¼ãI ¼OÁç_|ÞÊ߯qÝÛÿnK‹qj–¬ñ£ïÀ#Ôqü¬:¥ê¥ÓÜ3ù*v»µ–F)pqV£î$î/éXýFé¡/ûÉï=ø9^þ^šë1Ë9^\_8eit8JÝót±¼¯–Nï³ÎÎ_C+*ïÝ|µK/Š›èîQ³˜Ž¹Ns?é¿qß嫸û¨aªÏН÷ž°c$Ê)çIïu§ê *4èrBÙ‘=éìúAWçèØÄ…üç’ŸïÙçÁü~ðõýï”gl÷fuômŽÓ„½¢KæM§ý‰Ú“.-§5{ipº+õ^xñÞ öu¸×jQ ã÷“Ï‹ù|˜ÿÞáÚ½¾/»óß'|߯`œS…‰Äqz;©U÷ÀÏiäûËë}g—чJmü4ç:ÐÞ¦¡×±]ë_Né6(ñ¾ÇßËŠ÷ÙèŇÂß³Ûÿ®Âãôî_fÍÆÈl-êœð*¨­{ŸËÝ—ÒÅ«—G‡µ&k½3mû0k¤¿5{Ïî@ù|_Gf?ÄÏõÛÿžF‹q.Ï¿ÄÖö>IãήJ½bI£¸šÖ$µXB‡ï×úõã¶fÒyH›ü"Ÿe~Ó Æû,¯ãçßÍßýÕêUªRüÇi®ò¾kÓ Æéúéî›KïOR›Rª¦¤Q¼µþ¢;SÓ#jWµã‰ú4£ù Øþ¿ê™ySáßOœ ’Ïð÷Þç^žüIéDw¿LÚÓ3·¤GqˆëwZðàSþHF=zú+×^M£:¯ìn^DþÝKýñ©=ú4Ê{ÂoMôì~Ž·ŸÖÅÏ·ˆzt">ÿŸyDÝ`œ¥™¯L­k¤‰êÓ“Y ¯vÕãG½EÒ¹O:s{m“øg:6÷£p°&HžGòzæû\—üœ‹¨[q‡áªˆEÑ÷2ÖÉmƒstüþ4é?áçPÄû‘á5ceÓ1ëWËò~ØãÜ Ñ™ÂOÂmZF³ûæ/·kÆ\z–¼XÓ…šä¬ä>zÈOÝðýaÞÿù{{þû0¾_aßÿµ'$ÇàÝš†§)VøÙkhµÒ,«²gÙl*9åcûQ1jJ?SåÂæ›:¶Ó¡«ÇмrÿÌz¾?ÃëIÇYGÝÌAÝî&}éQ¼†¨\ÿÑôï/9-J£¡}î4ºóc&ìºÀ©Û 5ÙŽ#aÝ}°äÀŽ©SäûÏÏïðùÊØ. ËO¿êLü9™e†qbmÇN“ðÖ|Îø4º¬Ú¿¨ÄÓ©ôhe—q»:Óƒôý=]ôlÓ©t÷áϽçë"qÿû¬±ofÛÃ8s¦”¡¥ÎÐ ëÈÎýÒ(âÑíêO«‘ÿ“Ä®-žv qã:vÑÜ×±G- º\3H^Oð:ãû¹üüM'#2TùÉsk“^g¨þ½uŸCº§ÑúfÇ_˜DÕF7?º=•ª¿hê©Ý:fûÙb™`y}Êû1ïÁ×+6]àºù¼ÃÚØq†j¯Ò»y½8ѽôþ:ÚÊÝgëoJsjŸ·–VÇ´‡Usî_–Ïÿðç×?nÓ®[¡ßÚý—­gh~݈niÔ|ËkS2cèWmMI“sÄëX ÄêQ(DžÿðïAì'¼n]NS´øöÍë/É?ϵÿ×ý‹¤Ï,d[ Y5öÙÖÑ’ç®ÆÎO2Xò0²Ïð²Ïvå~’öÙ®B>Cf1Ó0^ʪ •²­}PàÑÀUò!1K^$áÙ2’‡‘Á.¯&Á.ÇË"ù‘ÿU^ ÷#÷—2^=$Ï]îaò¯<Ýì=wãÿÆsWÈiRÞ«‹ä¹k¶ó5á¾n&;_7…äëf’|ݸÇI€Ç ÷uó À—DìŸÍïÄÞ×ÍÍ.ÛZ#e[»e˶ŽþïWˆ_+ùŸø  D×lÙÖÑ’¹Vò’Nõ ™†ÿôÈz¤Öáß«G*¤Ï$ä¾ y ö¹¯ɇÉ×ÎçMÈ9LÈ–acŸsÈ}ÞìsÃ¥‚ö—üv=¤Oö¾— ã{)ø’ ¾—öY6<ëgÙüUÖ¡![Ö¡ZÊ:²lïKÁûI A켟ÌvþO‚÷e00K>P‘Àb & ’| n0qHžB¶'T<ð°Ë}õ•r_=²å¾€+¯ †ð @)d P£ €2[î«AòÛ •|Þ„¼!ÏK¨_áÏßå5ü]_üŸÜÿ«ûáe/´÷‘³ïÕÿþUïÓ:ˆ}ïß¡ç¹Hÿ™BÊ`à™®‚§¥à7ç#åÚç[‡Kè/ùι¡·…L…˜Y(xŠ{H]BúJ]BŽ«X€Zò±t•|,²e ÙÖá.¢7½!e[HÙÖJÉË÷¯²¸—o„”Iãoçcù¯üÄíóhQü«<¥R̵VC,† ?3­-@ áDWˆG[YÌ´öˆ¢«dÍ´ö±ó×J>âÙ=ÄïÊ;ßÊHÉ·2Aò¬Œ ˆ.$O;¿Jî.xUúÚe¶†ÙerÙg¶ZþÆ«R ±$¯J-°u¶ÜV!“K $Ï;!WáŸ\®æyÿŽó|HžMP@8ÀTÙüæ¡v>æÑácmçcn>c4p±ó1WA˜‘Àâ & ‚H#€B Èæc.ˆÖß.«5Ü.sKÈk²Z­@ 1€‚V °@ q‡+ÐdËjò¶4¼'§„|‡ò¶þï÷¿æ…î…nÒ¿IÈk2ìóZã2§˜YcnR.¡amŸ_cŸKÙr #‹”·e–2"¤âöñÀE2Åͯ])åX'Øå<9Öê"bÎ Íßä<€+¢à¡Dˆ%˜€ ¢‰.N00(¸@DÁÀ TS$p ‚¨ ¬HI\<÷†çòÜ›¿Ê%ŒÏ–Kè+åºAˆa øâÅ7ˆ2 Xâ4% ¬@ ¡€b  †h£+„« †€£+D¬fà1G:˜€Ê.¯5@ÊkUeËkn|Ⱦ~<ð€øÃA¦m&<²åµ y[R&¡UÊyò¶„úþü¯ôÈÿþø?¥7þ“{ó®' ÷1ÚAÌhr)æ´fJ¹7áÀ*¼?Ζ_­B‘F!Ç $Ol„Q!嚀 Å™WÌ÷âù¬a Sè‰(h#P¢¨C9[ö ])ºDKÙÕZ)»Ú…ú¹Z`>C´”w L@aDÄL@‘D—lY7.ÿAÖ Ï­Î¾T|…Ÿ¹Õ™Âârƒ¸Â€h 2c1³:Tʬ²nŒÀ ¢ V ‘²n”`(°5„h®£X€¢4W3˜ \qsµÀ | ÖHàÁPA¸@ñdËeåÙ_B.«‡”Ëš 4¶¸AÜa øBäñÀ B™À7[.k&ð…øãàœLbØ?óÄæ‰Z‡¯žè))ä²j³æ²&7)¯'x XÃs‰ùÕöÙ=ö9‡‘ÀEñ3ëÐÅ \¥,0 ð‘²À„\VS4p… ´À| ¬èòY3x>"Ïú«|Ä„lùˆþR>¢„.‰Ñ$ˆ2d_ˆ3¸A a h T#pƒXÀh ZPB¸¡À 4°(!âP`jˆÙ\!è``>v¹¬ÁR.«O¶\Öà —Dï€'Ä!5<³å² y`ž^bV¢ðkP5‰y`ÿôÈz¤Öáß«G ÷Ô(ÊPǬ¹¯BªAÈÎÔäúsvµ 7¸ ¹fÀT(âHà"å%š :¸H™g<6\*pÜPèaÀ’-/QÈ®Ž ¿/0HÙÕ¡Rvµ'Ä,@ Q€ÂV †@ @ ‘hø@,‘À‚ fàáDWˆ'˜D \!¤``>T4p…¨‚¹üÏìjA`þ ¡ÂÏìjAl¾ x@tá øB|ñUÄìê0)»ÚBŒc8Ⱦ¥¸A˜a h P#PB¤¡À 4«(!X-°5„kJˆ7X€"Ž®²˜ \ êàl²Ò5…LY_Ǭ™²&à"Õ‚à‰bV AÑ[¶ Èhàªø™©F1€ ¬@ÂŽ–2e€ ¨Pä@B÷FàŠ‚&à!åh›'2…~ !¤\m`nEȾG˜' ? X€ —DàŒR>wˆE¸$ < pI$¾ÀÜ –0`ˆÆÜ œ0 |! #pƒˆÂ@&Ð@LFàA…L °ŒÀU‰û ,À"‹®š˜ \ º``*ˆ/¸@€ÁÀTb$pƒ¨ ÊH €0„Loà FDê€'Ä! Ö_Èön¸$^< âp |!æxàA‡+Ð@ØFàq‡+Ð@ä „еÀ T|$pò¾ø@üÑÀ ˜A4pE3Ð3ðASˆ 4_` Q'R9þ·Í³p{l=ð?;¿â½ìÿOËÞ¿þÕ\ê¿£oý]Ïú»~õ¿Ò«xŸúÏö¨¿ëO¼7 Ÿ1xàæ[€ @7AÈô#pC¯±5úŒ(ÑgBhP0F DÑhYxG€>ã‚2OáL±pfä!œöûQDñÀ…|QLñÂ? Jƒ‚2%Š*XÅen(°P`šQX—¡Ø¬@ƒ‚3 k2ô^4pAñ3ðA_ˆ.(Æ`*e$P /P¡/DúB0 7(Ð@ðD_ˆÖbè À=!\zˆûzȾ(øxà~ |Ñ ŒÀ ½ X½À”F(° ë/D X€D J0z@$pp‚ â‰.Ð00èß‚ D%ýyHâß¾#2Tç?HR×=+纬תÔÎ%MöMŠ,˜swhÙÄý,Û•þV.ª} J)Yó\Ƭ\´åæ©Ï´PǸ¿ƒs¥y"\C˜½ÏƒãÄWó{2vÌYö{»ê–©Ôw‰Óäâ-†cÌùåO2ZÒàž'¾v^¥cßgœŸŸ³BˆœK'çýÈì¸[Ztìε%ãr.÷ƉÀ8¡³L7wèÎÒ»siKÜ“R©Ù£…k;ô!!4Ï÷¹›\¡×¬y:6ðxùüBäœ=~îSÔõÁêñcj9“½¯›×ß?ÃñDÀ“³t?Ð_³ôh*=Ðy—Jù£-ýQ4ÇÀ|]häxëü‹=tLô±‘ý*¸_ƒè'ôPòµÿêõ²ßÞ¢ëdýfŒSycÔãÆÏÑŒ«-›R):ÝP÷þî_YÿA³Æjöu£´KE6+¡cµŸ®J\4„q_"î‹"úÞ”ý® ?h¸ÿ鞪²Ïˆ­ü3T-CÊ{Oò;GOs^lº0•n¼‹¿3³B_æè½»û™J½(žçíH&úZþôÉà¾N<¯çoüÑ»lã›Õ³ä„*1ŽB°»9Gê{ŠnÕS©ß½°˜§þ,s͉~…Š÷¡¶ÀˆHæÕòjXÅAr®ÿ<Ü·ˆçOðüCû¼4ƱÙ"}9G„˜¹þ©Tý…ÐŽ§™è+Ô—^¶PįSëØÝÑSÕêŲ¯wî‹4¼Lb‡ÆÙë^$ib ¤6zÆŠ„ )Ðsiç×s£ö+*ŒéDÁmû­iýAÇŠ7-µðó€`Ù¯Žû²ˆ>rd?zž{bïiÆ8ïGP·Ö .Ðâ¾úUØ“Bš¡,Õgù|vô|Ù*ÃZxÓj!ùZÇöYë”|½9˜q?M~ŸÅº>)û$qýоBŽ¢~Ff¨Žt»>Gvݘ”oÀ¢zyÌ4êÜ‹LôjE_„$=ÓôvU78Xö“ãþDb?8+û<ò‡}¾‘ãt²ÇÓ‹Öí|vŽM!ýþî]e6cä–t©3E.)¦g_´›yv°\ü>ñüDñº/½¸n²ø˜aœ!sç¯j0+ž®ÿ}žoϺ`¹t wãELÌ…jE¹_Ï.ûÃI/÷î»Èýª¸_Ïëã>JYüü1Φnkn;O~mòd·g mpðoœ¼/pY^÷6ý`œµ•ü”×ë\”ü­’i‰ãØÜE“–±ƒÝ‹xãæt‹uî;[Ç4Æ4Éa‘ý$y]ó„üwæzž[ÿHÎÊ’ãqütªQjôEºzyðDzg’ÉÙóÚá.¦ålíÊG•WŸTSy[P§Ž¥—¾¹–Õ_.AöÅä9ö~\f\¿vô…Öíc/RxѸ‹÷V%Óˆ·«ÎiW°Ãù=;\œØü.–¹|쩎èYwÍÌÓ!rÎÏóo­r˜½¯”è Õ’3‚±ß% q¾yzX2uMh»»ÎJv)üÊiKÎî4årÁ×…úéÙâå]â¯%Ë9¼ð81Ïã×…£†e¹­mé³ï/¥&Š~’JŒSÅ(v‰Oð|Õ8™Úê®ÅÔ¬b熯h”·o7ªõGÀŠiGõ¬aÂôk©û‚äçï¿ÜÏWôÁãUãÜoç·Íì-åýµŸ7çûàËwM½Dƒ+–¾/L_{j_ý:~5›î?õá(¦¦Ïùšy—ÂóU‚dß&®î+È}Ã«Ú ©jÉy56½`Áõ²ÄÑKt/¡_³ãO’Èao¨¼þkXyÇ•[[]ïH웚rXÏÞÄ®x¶kuìë-çŽJ¾Œâxdÿ2îfÓ ÆÙÙ½·×å·—èë£õëÎ$Ñ·.ß_–m¸–j*ÌØÚPîZGŸ¯gÝfv{qëu°ìëÍuÇŸÜ¿’û¼rÿ9›^0N‰Å“i·ò2?Þ´^¯I´³éƒ^›Î­e±ñÆGƒ¼¨§k?ÅDµžÕ=äò0b^ˆüy¸(÷Iã~µöº7âú…u#KÞêx™ôBŒÏ¤$:úæûÃÕUÖ±J6#îzdÍü°¯Å =Ë}¢Ñs°\Çü:¢ßv¢—˜'üJö·ÿfŒSùv~ÝöàËTͽž#k›D¦Ò£&ù®cg¼<:vMUò¶Ëé™*yÔ¿¤ÿôå×áuÆó¸¯·½o©ÃhÔ³ÍØï2 H=`ªZ<‰êçê§-¼|‹ZÉeÿ³jÔàÄYÍ.|žF[OÖbüÿŸÕGÚêÅs©ùý·é×Z“‡Ý¾~™ê„EdÖ}œHk®ôÊÙøê:6ùÇöâÕÓêQ‹e¼2ZëÙÍs»Ö´TÉóMþ9D¿G³÷côÙ‘£Z²±¶ì;iÓ ÆI.ZùÐ1ç+TÑÁóÃó#‰Ô9à‹{p¶1pé‚'ͨe«¹JÖÔ³9+Ö5<` bÜ7÷îï+ú?~“þ®-ê×Ïù¢`õÜ>W¤\•D:ÞGp\ g*ï8’kmKªÖ¡LˆRÏÓë6û,ç^ÈþäRýæ½íÜè䯷^<‡=KþÆùmÅó£\¡ò#6DóK¤RûÝã;jÖ³@‡ò%=RZÓ“øKîsëþ¬_Þï¹O-÷ççµ²ä7DàúÛŠÜ­»Ïp…¦”ÕW¿Ø$‘Ö¶Z2æÉ—õ¬}×àRSï´¦/¦SÃzÖÅ«ù¡aÆù{âuÌ}–ýJhŸ#ä™ìãžÅçã\©üÔiëã+Tåâ¤å &Ò6ïº,k7°]¹ ÆViEÓ7ß^÷ûf=sx8/ÏŽ>!r?æ÷ûøs¿¿{„äîšYóù0Ž-v±ØUêp`Ç„Èt©·œ9Ÿ»ÌFv'íu£g››ÒD!0QÏRû O¤Ÿó>Þ'yÞlb‰ýgîÏÎ!×÷Ñ´édL†j_~}έ¯ÒáÓnÚo¢™n önd_Còä²:Õ¢ÃS;OMÏÔ³àç#»ô§ÜÞ¹Ÿ ¿ÿügÓ Æ™\yݰ™WIÌ 2ÑõB°èF¦ëÓKíìïäÇž‡ó|Ô³±û6< $çxðëðûÃó‚¸Ï£}QaœW¿Ýypò*õz[fÅ«n&jñngÇQþ™ø|pg'~óôË™¡gk[<µÈ$û.óœ3žswåvåàV¥ªdY—ùâúÏë Õk4^»þ•¹š‰–T:wèK¾¬ÔÛt·rÇ›²[#SsL׳ª”.t(X¾>÷ïçþˆ¼Žy.ŠM'¸þ×/Kó9·¼Fã¶Õ,ùý.mî]ó¢õ· lLäÉ‹·ªØíÖ[—×»¯gïMkGTÿ9Ÿä÷UÎG—r Ä}Ê’§qnÕœu¯Q‡~ýÔO¼K/Ë&éšoÔèe߯*fºóÿ±wÞQMdáßGl¸êŠ\QƒQQbGWsƒ] vì±ãê* 4vì±âjD]QD×7 ( v챃5V°¬¾ßÉÌ “,ºçwÞ}Ïžß{ôœïÙöÌefžï3Ï}îÍýܯ‡çÔtàý Ï•V?²ïý„ÀšN{Þ$<_ú,1§Y VÖ•¬âðÅÍšò~Á8LVt:±¤‘=WHn»ž =ÖGQµÑäжx :üEŠË³—Œ–wþ#û3N:ãë<öõ­Ýÿ`-›yŸ ã˜núRåH:Yý)|ÈîWÈ2ÕÔ¢®7å92qtȺ_ë7ËËcì:Ì/Œ›Ê?O›s½~Ë–“Þó‹ »šN*JÎZp… ÷eÂJIåëDúû(ެG“.ÔYñ®ÒÇìý°ï$ûÞóõ˜mŸA‚qvm-Û?7üØ×ýA­+dT†6Ç«beý–E¾ŠÆjqtqÖ•ËݾäÕûìN¾¾|(ÓTL.ò©–õ?-Xq™DýÐûôŒ(¦Ú*›¥®KÔ]½÷ʈ£­¹ðqQZù¬oÂê£Ò/^TsLF\ ïEÜ/Ñ`œÖºƒgnÏ Á\[lèeR§e’·ÿ®(ÊóýÈõ ÜŒ3ÞzÎ3ûN2þë+ðçV;žÓ&î§ïŒÓpR…w{“2Èìj<•^&ãV/>qívÝÚHúj‡ù<ܽE¡åñ´çY_ã¹!V²¼PáeëÛs^Zó2?Oö%†ùuõß„÷ Ʃ،«è3ÈÈ?ö~^çp™¸ñº[h=(öÓòÖÏÊÂ=Óâ·©â©‹69„²÷Ͼ3Œ³Áx‡üø-ëÓY|3>[¼{Ì6çÔ Rl⃧¿Ÿ»DHèÍýo„suiê@Çvñ´ÙÍ=m>%…XùÌ7,?3Þ›¿ˆó¿ãL¾¼ÝmuJ¹1×Yí½ù™—ØM×ÿMÕM«ôîr% 5ŽøÒ²¤k<ñÓæM;òò?«ÿؼ’ÇÖ:@<—ci {-g2Ȱ+ïB~™t‰ÌHÝösÔ©(ÊçÅêtǯsb?:ÄÓèÉæÎ«òú,_3~ã­0n”˜¯§À87†g·q¹˜A|: þ}¸üÙSçx—ök£¨a“qóñ“twüí‹8ÚýŽt—×XXµð¯÷¥z*›uàAê„á{SǦÿ£Â8“zL{t#ƒl8iîæ/‘„UÞΘÿX·v¡±Í¶<Œ»gíË0ÿ0>+ãEí >ÔªÇò‚B]ÕȦŸ¥Á8~½ý¡øó rdOñ"Wn\$eûK–ÌœEùçP‰tïº7Ný*Ž:Tn~¾¢òoç~³þ«Ÿø|ÓØ†·cÀ8©þÇî6’ø•ƒÆTÜq‘4íÚKÕ<9ŠÆìÈê׿oSràÂåp…[<=÷¤ÛÝɵ”V>‹o{®.?okdÃ77aœ?[¯|þ¨œ‘ø/õŠ>õ"iW¡ÚË–ë騛CÇ÷ðjM¦-ÚÝ™¶§˜,D/Æ5ã‡ðqðHÆøÆâù¥Ã„ly[ŽÅW5’'æ×t¾H–]Ö£çõôËÔEÚ¶$‰Ã’"žËÄÌð-^,ï{Ãâšõ1X<3^$;ÞâŒÓ{Ì’±3<Œ$¡^Kïò•.’œMOvß F½é(YÝà^kòE²Êýäå8ê.Òºð¼y9ëc°8fý+1×^Žë/XÒ­€ÔHZ¬ ½Tðá’SÒñÎûÑ´rh§¢O¾´$Ñ;œW$Ÿ‰£+KMk'Ë;ïÕµ¬þg}`ö°ø×ÇÇ,n@+# Wµ‰íñçò¥ÌöŽ·ûo ü{¬Oº”_Úéq¡xŠäW>Yj峸⿛o„úïŒÕbß«0N˜ìtxq¹‘ðý äfãqgo ûï[gpqòdyJÓñteë~¹ú‡Rƽ`÷ÁÞû¤Ùå_>ªi–ñy»– _Mƒqr8¹÷sñâž}‰~HŵÞI×&o ½2ûü5 ¬½Ò9ñêÃñtU°Ï¡N¡y\ öþÙz‹/VˆyqŒ³oûý ™mŒDªqy>ÐõÙ|Ößc–ïšãÚ}Ö°bRjÁǼ§»Žu(ï½³ï<ë“°¼ÌîGüÜL'I_©íØvFÒ»ÑØÚ’gçIZÎïA†Ühêýàʯ¿6nL«X>»hCõèˆ a,_&[yÌbî™CP¶|B½Õw”2#éĦn8O¦›Çôþ#š¶7\9±|FcÚÿM×Â7^ÇÓrkbÖNŸ=Õê?V×óùžÊX>fýd¾xÏ÷“1N‹ûü67’ Xç<éu¨ðÀÍý¢éãË'%mnH§N©îUçnŸyÚøÜa"üçµwòz|7›¯¶ð9GÌ^—¦5ЦºñCçÐBÕɱIóJÜ¿ºKàM‡Zß;{¯ìûÎúUŒÛ'Î[Œó®Ö˃eŸfÖ¤§v@¡sdÌÏ7½»DSžëáJÖÿ){é•@©_à™ƒåòø!ìûÍב„ïÃê˜V„ñI-~Á8ܬ¥ ê/·ˆ‰¨\ŒdHnè¸C½¢©Wk„õo7+0r´Œfáyë–ì»Ç×Ûwde3sZ^[ýQfù<4öú=<EqN^»}µÊÑ ÒóôØšS"ŒÄÒ†%ÑÔ˜õgì} z}kÌîw¡ ôÖÎ'þ]:†Xû|Œ3Èæýü{z,ôE›[y)¿`œõÕ ÙƒŽdÒ}†;ÜH2ë9+ˆû±€"Ó=¨"Ž'P~]:ÄÚeyŸÕ­l½‚Õ6õÆ ¿¼¸éɃ$ì‘KÍ,o#IǬÅM³žNÍöhàß’”­ZUeAÝÕ kžÑ7¯oÉx%¬.bqÍߟ-Ú€qÞÏŽk9[—AV9-SÜûA>vŽo}°úzºjfŸ7w«úÒµsÆ{Yš@·4qš|8”²ù)û{Yß’ñ(Ù÷ËfÝãìTôêQK“Aj·~ònmù¹³¦ÞÒ(ÔÇ-:𳜶úqîûº#¨,8¾Ç´?òx8ì{Ìæ},îØ÷Ëâ›IÙò>Þ¶½2?ƒ4•ø<³A)èÕeÛ‡ÚQB_¯1ý‘F¿êž@¯5Ëé»âceë«,žYäõ˜!‹¼{'+¸öå‹Fõ½lù°ç×wòºÒdèiU/¤“öÕ7õmù6’ÎÛU=`Ô:eèV—}wÑ×˲6/ ¡||yYŸß§~+äËײµ™ûŽÞ\åe3O–cœ+Âk¯l›A<Õÿdš‘NNÜ9«}è€ù¸ã°2õﻓ"¿;·È*“@÷õ›ŸæJYß›Å{^lÝ’÷g=›¼¦À8Qs5[z×Ë Óúœ_<Á3X0ê>Qôu»Þ!»½IÇ1MW$)„~|^¾±_O`¾á×aë[×é-¾Á8 .%+â½Wt«©MO#ëJåtHWFQŸqû»tžT—„,[W°ÕÅÊsòÖÇØÙz?«óÙz’x}Dƒqø>h:Y×ÿE³kÓˆ¼ß£tÇ-QôFí㯖V“1-,dº4i´‡Òú=cõï›[²¶V TV~§˜‡cÀ8ʈ%¹]Ϧ“HnúR:øêX&6Іö¬Y32åKb÷yŸ=­«¥ÏJ&J£êäÕû,žyÎí+ÙYÓ­'ý8Ö_÷±M'÷ý‹cc#Òɵ^7«GÇ%eC‡:ÿ5+Šö £Êy<ò¦7ŸM³ØAK[8êû øû<Œï“ú…­(]Xï9KšÖV¬;WÏíYU‹VFõ!15jœO +£ï™13¯_ÊæYMWn_rа}l=™å#‹0΂»Ë›½¯—NjÊ»Ôðä ‘)¦>é»6’¦´¬;&¥Y[ŠoŒ¹aR‚µ_Îò'›ï1.΋f³ôþŸ[ãÚ¦o†qj<ßýfsñtR=SÿyЦ3dåêú1«nGÐ4•=Œíé™×¦çñ7¨ËÅg³÷äÕŒƒÈ¯eÈÞþz}IÒ›,™}gñÆI«>Kj¾‘F|ý*Hg8Cœ“îæAw$ÛmœÕžn»¶|E-Õõy¶^}Yù·uV×ðuT–,IÓyË©ON¥ ã=¿ñ±:”¼7ÞyWù3¤ñ¬—» ×Qw¯ŠûÝ6¶¥ŸëÖíÒÉWKo?ôòyjžØ|™ù‡õ›òãIi0Ž)óÏG ›¤‘Ã9`âi²ôFýêk©÷¾Ñ%2 =Z™#ji\=¥öx))û~²üÃÖe„úÑÚ7çQÆ ^1.aý£³dj‘’•xšœ;²¹Åèµtz¹„w¦5¦[ú]•ݯ§¥Q³OçæT ¥l>ü“ËóF¨nå¡òÏ-WÖåèxT~u­ýh‹0N«v?–n¶ý,¹›0¸]û¢§ÉÝžWÛy­¥Ÿf=<:}eúæãç´%eµÔ}MÎÄÆB(ãø²<Ǹ…kœ6.ýù…aßq¼9LÉ–ÿpØsšiøYÒg®ôÀôƒ§ÈÛñ ï̬´–’Õ»¶ïv¢?%mÐtùQKÏÔÛµ ¹UWŠÅ¿pÞºoŠí÷1$'ýö¼Ö…ÜÎ’3S~ª?鑎y´"Ùc--¸¨Ù 7—ªdDz£w5+i©fÙÀfÛŽ„X¹b¬ßÄúsl¿¿Ã6_Ë1Χæàø éÝcpÝSd‘~M×YÝ×R¶>Õ"ÄÑW-uÙ3Á\?ÔÚgbu"»ŸÄUë½¶îy$³`<'zÙúã ØÙ¥Øogˆoñb®ƒn¥Dze½wjð~,ó¤F¤PVÎ_Ñ.ZºùõO‡Û¾ ±öØ~ÆÃfëM¬?Ǹ³ÿ`œA ¸ò3$³}Ë«iKSɼS»&XGÇè”.œíCø>°–fÍù°®ù Êž ß—‘Vß°þ)›‡°÷hñÆ©R1.íKÜiòk…ÀP¿©DæW¸PíÑëhŸñÅξ÷!ú¿,9_K2‡„t«B ”p@#2ÃÍ]‘ ùí¥uȲ ®êcS‡0έøcwÂ7ëÖzlÓ\?IBBsOŒ=¸Ž¾-,ó×6$Á ß:¥µöƒÙý¤œ¹p0fÔÚbw>?ùØÜ ã<žóäx©Ó$ªLÇ7 Ÿ$ †\÷t0o«EŽ8õ\óË=­u}žßW…°úŠåM͙ӗ•}+3ì1ò׆6ûô‚³åÍ÷¦†$ž"]ÞŽø|®ÙI²oÙú:;FÐ+:½«U‚Ïù\äÙ%-˜Âàÿ¾¾Íæq¬aý›ú ãŒ;ç¸wרSdÍèÁ?~q‚`ŽaêX<‚2®gíàS>í÷hifcwƒ†Zçq,O³úƒíÿÏßä¸~§ŽuV8E‚(/k@ªÕ0*zjiÒáÉ¡ÃBè•«êY£œ½­~`ß3¶_‚­SØôÏ0Ί?u+41•ܵ”“'È•;mîwÓFPŸudžUiãK2Fs@C-íQòc«Ý]ƒéòÝeÏééM^Wü|¡d Âú]NyÎ÷v,&ôKëÚ¬Ój0ÎàR)'k¥’Jer>SÈœýKªVI'W+~·Kªávg•餥½UMMbÍ›,óýÌ32~ÿt®µ^³Ù7ƒqö¶,õ<ýÚIò1àØ„6KSHzçKwsÛGQ îNÑ•x,5ò£ZKåIÑM”C‚­uË7l__ïf[¿7Ÿàú?”Û>táI¢˜´%µ›Ò߫ݫµòõ´hï½s„w#%oõ}Ðú–޼w¬Âá°¿Õƒl^ÈÖµÙ¼@\8„dËO…$núÉû$ ªUúÎB)DW«kÇÑ´æµ¶n›{w&ë _«Ü£„޾óXpÄݼõSa>È×culößIp]IíjIun îñ'Ê\>‘L Wm¾ô¯hÚbÉ\sÉ:í‰%=9ë¨Ûóêq\ö¹^ÝjisŒV’–WŸ³ûbñû¸ÉÂ}­c>Xó–Ÿ~wM·ûϧmzÉô£IäPòSÚè©«ó¥©ÛÝéÖÅbžÔÑ&µ ß|^ií°þ㓳¸eûåyñÏË€q>ªøqÕ²òc7n&D¾¼5„]ع&ª ~qpö¢½š¹Ìº^VG_šyl|^ËúN|þ:eÝçÉòßÇá×ÉLç“ÿ˜ä³²rˆÛÖ×?‰øþ©®é:yâN9ðd[mG-¾î±ãàQJëþ8Vϲuæ¶ßÓÆ'¡Ùr~¿W2ùÕåBçœIdO÷¥w‹wÜ@?ÍO -Z¹"åh—cµt¿7×Pÿû~L6dÜm¶NÊ~Wañ Æߘ#b&“æ//-¿ë–D:ègUrõÞ@Ù:H•Û·šdý¢¥¦1 ïý¶-ôoûÔÏÄ|N~eíŸ0Ž=ÿ|ùþ–ã<‰‹sÜçšL¢£~o÷â8¹²Á3!Ãwå× %ÄXªú¼¨ûúg^øqV—ëz6Û/Íö°ú‚ÛíøCÝvÂ:'¿DqVN?thøî$rÞ´¿ùÈq"Ýòg­ 6а­ç£R.5$Gx­®­¥«Õ~×sËæñãYÎÏ+_ } dÁ‰²¾6âý‚ë[¶awN"åƒ2^o_vœÔ߸xÛ³ ´jÑ ý2úÿLŒ¯Zê«¥?X6|N±®—°¼ÏöÉzOQî°Ò,ôµš[9Ìß`œ0­»Oý{ÇÉò"æ ~þx^½&ÊŒ#§4¸>}r’¹)ùQW–xY«N¥ËS„øË[/gqÆÖËX½'îŸ0NDS·uEf')w›Í_ûÃqrêuÆñ^Ù¿ÓEW=‚Ú{ú’ßUÇl×Òëó¶X¶wеNfõï÷,ëzŒ¸ž0áú}¢jï¢5“Õ·cVo»|Œ4´lÐÛH‡9uªÙŠô-r©H­-qæ7™²ï<Û—Áê/ö{–_ÄùÒA™-x6£d—«ÇHÈ­£:ì‡ËO¦l^Âæ¬îbë~àº} }pjù1RsÀ ®ÅŸ5O¼7Ñ“ Ÿ+¾È¬M.â~È £1ûbÒ&ŽŸlí3óûÿZç“üõ?ÈØï±Øï¶,þÀ8EkÅ @Ž‘9Ž/î_~Œxw¸:î]÷MtÁ¡~ió‚*Ðܱ{Ú·ÒÑÖÆs;×M™b]çgï•­û°¾_?Tç}ëwHI~xÏ@†ø®ßØâɉ&=»m¢|ŸàKb)óÖùgêè肸‚’热y‹õ?Øúûûm¸ä¸~›×…‡ȧ‰¬.ŒLñ^rA‹MÔ»mäÓìÆn´ZÓŸŽ¯µtߟ•+‡Z÷'³¼Åò/óÇœøí}† ªiË%Ç8Ó½W¸ÈÑû³^…¾0ýŠM¬ÏBë æviéáO’¦m ±Þ‡->MvéSë«ÛúÞ·~çmö_bœÇóŸ‹Pr~i» WOH¿³G/•l¢q£–¦Ô súÍZê}ûƒîVl°õ»ÅÞ ë+³>9«÷ÄyÞ„qþœþJç?6‘ì?Â}è ¤Ì¼º¼—{û ,ý¬É)2$²sy½4µ×§Û¥óê:–‡Ù>?>߾ʷ¯è05[®ss/85ä( èw½ÜŠ%ÒÚR¸âýo(zìMѶ$rûävµǵÊU39ïœbýý›ç°ï;_·$l~ÏîÛ⌳¡ëžKÖ!ž?jnçL09_v8Ù^C¹möɔǧGO­¢£|Þ™BY_Ÿù1lÁìÜÜ>Èn¹n?a+b]ÿ±™ßcœ.–K‡É›…£6ìi ö^ý²ù1 ÏTÜ©‘ÖÜÞ:Æ¿q)­Ü²ìúë¦P¶OÅZ[æç÷„uÒOÖõ,ñú¢ãÜ­–3%øä!r¡Ë0ç€æˆ;þ±OÈfêØÁ±dÓà®$ö^¼Z-½»R9µEß)Ö¾"û³<ÌÖ­Ùz¿x_± ã&\ðûåÁAòÈûƒ äu´òè㛩Cçá)‡!é=öl˜w^KȸG“­ëã¬Nb¼uæ6ï²YïÇ8ûNhʶ.zÔ¬z¸øšr¢K)ûðn¹:|ü®óh;"ñävèjéGÇiwÒçO´îW·Ý÷y޺σí_çÆ­¸?²‹—žt NM *h Ãô·ÛOCý$å:•Ÿ.#ã“Wß<›¥¥}JE.ìü>ÈZ¿²}rl?Áœð©]]?ËÊÿµ9ªÖÛ¼iÂ8Ág§žHq€t·nRb&%•V8Íh¼$†¶¡RÎlHZ…úlFÞ‘Zÿ$+wõÙº‰Óa—®áŠ’Ä­ úwèÝȦ¯è0-[þ›WÉO±û‰[‰à€;ë(©ï²î~Í­1ô¸Þ°8÷u5rãp‘°ìgZ:ˆ[ö&Sh{‹}$Ö:‰ý>‚ß/[€°¾‰8ïH0N\ÄmTZûÈŒRn¡(™`صUºjˆ[\þÚ­²dtØéýnhiX‘‹ÑÝ_[×3Ø~/6á¿›oeüó•ÚôËå§áÎ÷Æ;ïæ ”|ò-ëÒô` ÝèêWdÂçò¤ð¢gÏ B䦋;Õ3Äú½±ýLšußóø÷W Œ³¸ò²6ÕßK^ã~É•H‚“O ½”C›ÎMh4%¦: î­©0WkÍo,o±xf¿Ëeu¥Í~L\¿¤+·û'á7šH^”êÔõ†)†®=û±ã‰éÞd_O®ÐRþ½æù†ù”íg`yTüûh ®ïûºyèáÁ{ÈÜð‹{—ŒI$» ǯ_b ½µèspY÷¤³åºZúnàÇ*åÅËglÞÂßÇÙ•òŽ_Ÿ+¬_âú£Ê7-8P½›D]»_×DR'ªG—/~[è¦qå¶ä¶nMºü¿´½LK/ÿ|ãÇ’K&Zû»|¿ƒý×lÝOÌêñû6a//}U³AGÒ›ÅL¿Ü$‘¬ã~Þµ…6iBýC²Ûim™Ì÷á~Úe¯Z5‘²~û°ù=뻲>¥ÅÓ³å÷NF^§Ö’&%?,à•HW,A|Ü"ì+éDÎ=¾_"¥²–Öszæ–6Ñ:`ωíóeë#l©¸^•`¾.ÜEξ1£z"×bû燿n¥©£_ÍÝÑ™\:ó¢qÔË:Dî}ã tÒßöw±>ŸÇîZó±ø{)çîg‘$=©X<ù¥¥fÜéò‰äs¡#snm¥§wü>ô×_º¾@+¸î9tiò$ëý°úífu›iøßÏ@ú~Òu’ƒpFþ¬‘HãÐ(œyÄ¿ËαçŠ96:á|ò¯ql2Ž ;Ÿ<(ŸóÉÆ+ÇnЮ؆R»³ŽRnC¤øò4vüj¹ˆ_$ðk|¾Â¯VC¹¢óKvŒk£pö¸rBàA™ˆµc»ú‰Ø JÍ ÿ Û5R0ŠJ…¤0L$”+:ÇÉæQWµe»2†c»2†—=›ÁþLݯõæ—ãñk$¿FÌ8ók¾Æ½3óã×0Æ!ã_‹Ïz 8‡ßâ×äwî¸ÔŽs¨ø5á„Qîß¿™óË_Ë}ùå½;ç}+ßýoÈuÿÓ³ãþ3xÿ­üÆÅ†rε³¹œV¡øLK1›Ëž¯À1—[ÁY8›×$œÍ+œÍkΗÓl áüÊH!`!#ä#°ª„3Âò˜rB0A™v¬V¥ÀQ` … |8­©Ttf¥ÁŸ*œYÉÎggV2F«˜ÁÅ­ƒËžŸ`vï×Τs†‘” òƒ¡tv|jŽMí s)íØÔ0šòÙ4‚á!#äãi.uˆKí 0© 'Ì.œMçSêÿIý5ö cRK`àpIÍ1eôßë·ïõ›Ã_¿¹ £‰{x+ãl™ö rE`ªòal‰ÙáÌÞ¯±²6;³W•Ï™½J?è‡àÖAÎð ¯%·ãSsìA9‚>rFà+!äèì¬þ"&‚J`"ø}…Áªœ`”@ÈùÀ0Á4 (’Â<‘‚ƒ•±µƒ•±µì™ögõš ?˜M¹Âp*£Úž?ȸ1ž7FÌsc¾Æ¨óóãÆ0þ cT+ äÙŒçTsüÁoqcò;¿ÜÇŽ?h¸1j"œíðïåÀ;ÿå—ûþoòÞÿ¤†ûÿ-×ý¿ÌsßÊqÜ;Ž…œdAvœ,g#h‚üx:;F–˜±þ Æ‚N`,pggA~RpNppV°œ;+Xà*î‹FàbA™\`rggBrt,䌠VB&;žªJ`0ø e>,U#äƒÀ‚?2VÎ;w\#¤VÍ㨊XŒ£êÁþ\áXȦQB&È9L¹ÂD*( ò‡™ô_aI»Â\*;–´3ÌeBr˜.r‚ñ‚ LHÆ ,iÆ‘Ö@NG:’”jÈ Àœ†àHçÇd iOä.µÀæ˜.†ïuÜ÷:Î`ã<…¿!‹{Öx^*ã\e9ò,@=$A`†çø³R!Ïo°Ì{ÁIÈáPä€ÖA®j•ÀôGpë!W¸Rà[ùÙ1¤9 ‚^¹"ðUPäèí©"æB¸À\ðÿ #5r†Q‚ LHÃÄBN0M d„|`MU[F*c[1F*c[Ù3ü`0ä “© ,ÈfÓC¼pGÚžÿÇØ3R=#æÿ‰Ù3_ãH‹ùYÂùêbþ ãÿ1Žt ”*âHs†þ‡&¿³Öåvü?Ž#-åj8’·ö=~ÏÿÄyø‘ÿ¸w§ƒ\€J7K¹ Œ¿,މРÔÛq´Ä5dþ B/° Â!3ä ÖC®d%d‚üÐ:笠3rw,äŒWB&È+°ý” òCÐë W¾ ʲãŸrÌ-³ÀÜÒ‹˜[öüÓ (’Ã$È F ‚2!) )˜&2VÍ㟊¹[Œê”SÂÆRB&ÈÓA®0™ Ê‚üa6=$áÂ!3ã¾Â‡–Àˆáv|hW˜R ™ ?¼`ä ƒ*!äÇ1·òáCÇBÎÚùÀÀ¹Ü\&Ný6t~L?1Z ³G lhŽ5“ú½þûžûþûúÏGÃÌ=KsÊø[f(Ai€<˜ê|Ø[‘B BFHŠ€UC¹› I¼‘{K N…<ÈjÈ   õA™¡Ž)Ià*»åoǀ΂üôzH‚À‡ÌP `°c›rÌ­TH*0·ræV~lSä £(!äÃè g˜&Ê„ä0OlU[¶)cn1¶)cné WKeAþ0˜’Àdá €Ù ' §ñŸíÙƒŒ«ã#puÄìA1[çküg1{ãëØñuñŸƒ £ˆÿü-ÎŽ òƒÉu+Œ®‚²ÖŽ˜CÈñŸ}¸üGø8äþ}Ïÿœÿ·ä¿ÿ-¹{îzH‚àS‰˜_zH"pÍPÒ`ÇS 8S!)4Ê…¨ÈÁª†r!…ÀóDડ\(l€$b”ù#˜õs0Ê‚üØ:ÈÁ­‚² ¹Nà ª ,ȯ‡$úpÈlÇ[UC¹CÌ bˆÙóV• òƒAb!g˜D ùÀ(È f ‚2«æñVÅ,1¹ˆ%¦„L̤ƒ\a(”ùÃXzHs…Cf(&3@ž0šÊ…0\êWxΞ0 ÚŽç¬‚³ R¹Â”*( ò‡9õù°œu«Àr΄ä0m¤`Ü@Èø,çüƒb–³L®Œ¿×}ßë>‡ÿ>÷ù9äñíîª'‚Q åB e*$E`F Á!©rB A™6RÚ@Èù x5BBFHŠ@Ž„r!Úy"¨ÕP.¤à˜ˆ§3ϵ7C¥óøÒjŽ1 |ƒi/f­BFȆˆL¡ø k•1íUP–ˆiï Ó(!äóèªÚ²Výa$½ˆµšùÃTzHc…Cf(3@ž0™Ê…0[*$…á"EÌi1KÑ  ‚2!9Œ¨ñL)ókÌi1W1RÀ´ÈÆU \ÅwZ e Üiޝè C+!äcë W˜[eAþ0¹’Àèáò‡áu"Îb&$‡ùc Ü?.~Ïßó—ÿþÍÜ—_Îãž™»>‚Î  Ï O Ê…ÄTHŠ`Œ22B>Lœ (’"H#…@ „R!)6RZ” y"xÃ!3€ 6@žd5d†üÐzH‚ ‡ÌP‚[Iàá @  O»Êqb¥üH!øP*ä ¨óáŪ ,ÈÆÐA®0‡ 2Ar˜$r†Q”©j;Ö¦QAY;V¹Â@*( ò‡‘ôf ‡ÌPLe€í–[š:CëusÒ¥+B<–:b(Ëa­¾*svßé£i6_fŒ3òâæOÊâdkõfŸ ub‰ÏÌóë);hÍ\&\Ùžp®é?<¡Û–¾Ó)‚FSæãÊ|G˜ŸìòçÑÛò%¼újž¡ãŒ.»µÐô¹»È3Ÿ”v Ä’ªQêyí¤ŸúL{ÑcWk²=bÓiò>†êK=x¨÷hÊü¿ÚÆ(Ö>¹–%ä¾½se¹ëZqÝ"‹ŽEÍÏ·“L°±F®{­sšÇä1;iÞÅê„Öò%Ï—¾\Up7Íí È-úó0 æû&×ß7.?”›0?F×|9Æù™jë½äy"ŸflKž»Ú+bÏN:›‹©(¥ œKcó»©×€â EŽ}§X^4ïqßçéOyµ7|}§\ý†Ü¦d(êõë«›:v+©ú±Y#–4í>0fOæNjØ_õtpˆ/¹¤ìøàWÉnz/-¨\:Zôýf>yÌ—ù÷1ßRæà #Ã8W¹x†Î2úö—”‚ßÇ’¢é«ƒF6ÝEs±Š¥[ãÝäÚÅÐCûv7WvE™Ÿˆ£)úZÝ“gnЀ0¿îú \¦­Rçcy6wCÖ—»ˆë»P.mºj]Üi|¥ )JB‹Ôëû22†ÎxUÛ$DüÌç…÷¯|/úäqw·ÄßlyÒŒ3¨y‡*Í·¬!þ\lyåX2@›{^~#-U¬IÊɉ*2Äù'F¨§á¢¿ óOa>SüçÊE:wúaÀëvJò~Zð±±+ä¼N0Αœ1Û Rô— ã¡“›µOä:ºÎHÇìÙW¿f•î¤Øo“¼«ûÄСþœ3x°è;ÂüŒ˜=ÿùòæ#Ìî›S'ç;g€¡žD‡­KéK6; ,ZA‡ŸnÛòà<5µ¤iÓ˜¶1tö¬ãó=š s9™/ócd9S—9ôõ²ùŠ[1ÎÜÝ»ÇE_@Ê9 =–ÈWèî¯Iו6»èß‹TXsáÖ¸Êû“ÿ!¿Ž÷kqˆþ<ÌŸÙÕ/ßq®ø›vÇšM¦†®–ybÉi-‰þ”IgÅ®m1Î/<Ü­?\ßCKɯå¦NÙÿŸù 1¿oÞ—ÇáÓ 8¶ƒÏøj¢? S7S3Ê… æ6ñLæ}l0+ËK¾jw¼ÓaõÝùËM¿n=H§ÈƒëëcèÃ¥.ç }“Øçb¾VÌ7S²ûçë!ê„ù;uƒqîöV7¾]a(¹R±¿»ù>êàûé#ëFÑîOŠŽY1ÀÑabôìº=åùð÷ûFþÁ†å2ÿ™B×ïg¶¯V_ìGNý`Þº ?ÑY!J–^LN¹u%Šnš¿ùÀ²-íIDÏ¿¡Å“îñ~9\ôed¹3¼¿Ø]ч™ÿ>’l~ÌÇé~oM λ}÷N JÚÈŒƒ:4Ц’Z¼™ÐšøpÑŽùŠúF)WߺõJÌÇÓ¾]:'ÿ:\f3Õ®æGfPuîê{~™LI¾¤Ç3¦ES÷Ûv¬9îKâºoR¤V¡¹ Ê )úLeÏ>#æ@yä.º¥Ã™:Ù}1ŽO¯ õN\@kªÆ%ì§dÎî€c?Y£…¼wáŸ1Ôójçê»W…ˆþ¯¬0a^§|øë×Éžc†q2~{ýFOkD¨ü^S²¹ÛIé°\1”÷jIjÖã²£i£ggF†kC(ó÷bã1?~æ§èl' êdóƒs`œ v‘Ï?n]IK§©²†U°’º;ê”IQÆÐ¸˜þs“[õ#wýñÛÑtsóZ—Ö=.öOæ¿•3˜ùJ»>×ܦe(t/¾+û¾Z8Í]¢³!¬• xÜw’!†ÎèkÓ{;’/­áÔñø9Üàûïÿ!Wžåñ>Wï|ø\êÙü”dgíú‘­ßF­§ÊRI¦¨áV’Qü³a~áÝtræô§ëu&ÕÃëåûeC4Õ¿ZêÓ–¡¢¿4ëoÌϘÿÏÏIwÏ(0Îè´à–ò›)Ÿ[j%a[.Ÿ(³`7*Pí“ûYáRá¦ë¢…ܱ¡¢o*{~ñ>WiBnž›è7ÆoyyÝ`œ£‡³nl’m¡¯_Ø'Úk%ý O-l¦+¬ žˆó#ίm\4ÕIOè>JYŸgý‹ïkq>¼oç1ŽÿÞøÜ9Æih¸½hÝî­ôÊî l%1ñå_f˜é#Ë.»]µ)W÷Våü3¢é˜Íåó…Í*Þæ;Ç÷™xæ?Çþ{¾ïåãõƒqJ-]\Üo;}bäE­Ä_¶o_úF3-¾:¨ä C7²íqϦ7EÓmÅ ׊óvŸYŽó×b~·Ù|š1αeïË¿“Öu†­R9ô Ï®{fújó*r0hu^cD4ÍWÏ~ ®!HœG±¼ –ÓË>ÏòSÓ•tjílymŒ³ùáý6U»hÞ ÑÁíÛÅ‘ Oö–íòì;‘j¥ û¢éó²¯v"A⼘åð×$æ´óýÙ;{Nóô Å»ºÒ¼6{ýum•\ÁÁq¤…ZZr¾ÿ!¡-±’V¦?Ümš\*H¬kæsË×Å{öÜÎè·üdÉ1ÞÙrFe§È®Ógo5Ñ6–Æú‹âÈì2=£¦ì¡ü¼¾%A1¯‚Ïól£Jî‚Äyó½äŸ×™¢¿-˳p}Ž*0ÎÅ7»£V¦¿Þ]ybL9§Éª¶yõmöâᑦ¾$0Íz¿í²h!×$Hì×Ìï’ùuó9ï²ùÌkpýNï'Wo®|—ãȆ£¹Ç_§{hïA«Ãão¡ßGÓ¼§Š×׉¾Ölæ È|™¼ë¼C‡qŽt˜põš~7=: mèdi<é·yåöä´=´û™îCz*È™ÝÝâÊGSÞÿ-Hœw°:ËžsvUÌuõ;5`œ•ŸÞhßÚL1_ž¯Såãó =‰3^fså}úû9Z^b}ò¹ì¿ ùfùI‡³ó[ì_Ú([Þ‡ ãlˆK«;¤ã^ê1~§}âârâjÀé·-÷QO­¾›(¬·¢(? ¿/6Ïcùƒe=mo'»»ÏÚ«ynø){. Æ™¼E÷«&q/ nÐ¥UÀry.àRä>ú|øæ÷QWI©îô»Ì(Z¨ö“Ï{¤?‹>ªL'lÝÎÆc¹Ül^êÔ Æi°P5,_ç}´\Gu¹ÕÉ΃—|ûÛO^Za÷ú螤\§„Ìâõ¢)Ÿ4àþ–,Šù[³ïÕõù¬Ã8·G-Xrÿ·}4=Õ·JÌÀDrï°d£Çý´òÏ©äU{2¥Hܨ2£iHA®£ ×QL—lžÆû?ôáë¢j6JƉɬóËûé©cݯnY—H4ý'„ ‰ÜOýfíwÊÞ™ ?)i\&šªW‰Z¼g ø½±¾Ïú1ómtê×Ý~ۿɯÕФÜýÎG_M$¥Ï=®³.m?ý<åÁCk;RnCش២hÂÔûW¶ß ®gX.˱gÏ}–˜-Gãd^ßQdÞŽ´òóò'U¥’ˆypú’ý…Ð3*^6§µ"í&H›ù§GÑÝg’;œÞ7à}˜é„÷õÎÏëcF†Â£ùuù€ÚiUS¥›ƒ’ÈU¯FIµ½ÐU)÷ìÓ±%97Su;å|Ô=è€õÇb_dßË[x)éPܺ¼„ù¤3}:õq=)_ïÔAÔ–”í²6‰Ì+}íì[ÿôsäðKš–$oIÅû£hÁÿœì0P|γç0óç,}mŒÏeŸ|œzÀu¹§i‹I‡hIÏŽÕ·“Ȉ¥î. =@N Üp¥%Q5©ºðÃÒ(ZÓl;€²y»>óygþåÎúÇu=f# ,4ëÞ›SÕj&“C>•¿¼ùíW±e.¤-Y{Øú`Ï(š¿¨6£ÐÁþ”­r΃yŸç§ÎëêpÝ5îV<øÊBO ’o:§K&ÝçäpíôÊçË(¿õ(z¾¹®Í´O17’}ïü¾„Çå8ëטïSqpâaZÿ„-0õb2)±dÿî5¹ÒGâG‹è@Ô¶>eÊEÑk“_´™|L#ÖûWþ{x ¬C?û¬ÿÒ³B“ Õ³åhY1ΜÕ㾨ÎоƒrO[\9…t×&mE‹ƒÔAPÙ¾½º>¹æLz/‰¢G¸¯gFô»g¹ üþÐG!/6Ë' »V¶úLÂ?‡ñõŽqÖÖ,u§êQª©t»é—ñ)d|@#ßKSÒ„Áœq­Š¤Öᜑ£èØsGÚžšÑW̽dû”üóó­˜Ótt͇û^ŠÒ5ýuµ‡ù\·™ŠOC‘šŽÒ Åwz­IN!AÇ$1cÒ®ÎòUé©!EÛ׎¢_ ÖéqIÓKœ·³ü6`ù üsÔ—¸æ‘È0ÎÚ:‰—z£%ºîûª”\ýýçÒË'^œÛ…Œþ¡ÿ‚ÑC£hÕ¦§jµÜÓýëw>g题£ÄçÐÔË–¥À8¹uÏm:uŒ«š6lÌ@Y@¯©%?D g=ÉûxVGB†> ÂótIÔÇúîžÝDß`¦W–«ÂÖÌ××Õ/Zƒq*í¸Ö¼è¬ã´Ð¦–1—öÙH7ϵ“f=Dùùk;Rð}á^EÑ;#Rïú‰Ïm6¿dû¼žõá?‡Wö<%Œ³Ïóò„i~±ôØòû‡c‹ /G•x¾cÕ!Ú_^ÿñ†-‰ßÌÏ-—ºGS›Ä’7"@Ô «7¶Þ}:äÉœ]ÝD_w×õµã î>{ý¢”:0çÑŽ'È– }Ç8DOøx»Ø@NŠ;ƒx£©—Eí7k¨šò÷ÙCÜGcûD,§”ï3BHxý`yñO×ßgPz«à›~vž ñ9#çCtȹÝok•©GŽE¿º>,*Šž|ýÎî½(ÛGe¾á¼>cÅy(¿~óàuƒëó9½Vú[˦Íg¿8A®æ®Gç_=D¿ .تÎLoÂmÞxU‰¢)5¥-ª¯è%Þ–×ÄòÂYÿâŸC•„~V×ͬ ùñý³«­t{ã›íFt>I®´;$=ãõJ,‘´N׈ _,IŽÛa¢Ñ BVø V‹>ÅlÞÄr#˜6Ë;tͽ‘aœ[¤ôÖV«Ð¿O’¶×Š·HËg¡¿e¯oò%ϪfíÌØg¢ç¬»=¡•ZìÃ9ó§Øçá¿ÇÙÖ‡ ŒS|׊aãšÄÑ®¡»L•"GÝõ¶9wJ²HY²×•O&aª÷½xzˆój–ÛÇtãêO¯Á8‰•¸ê8¯íq°ØÄSä»}5ÃnYèû¶kSº+Q}~V¡QÕ(z/¢]Ù›WzˆõÌî3Óç=û7º]z&®C\ýéu§é¸ûš‹§ó¾q£ö‰SÄšÎm YhðáÔÀµ«º 9`Q¼-€²¼>¶(䱊Ï/~?¨9aë7§n0N­—=~žOÍKîÍëàyšlª¹#¾ÁG m?Â8Æpó·ïzlFÿ\°œ ¾ñsÙ¾0?{%~¾¿6'+ßpüóÀŠqN–nô|J<9åœröib™Ñ [‡Ã4qËð'?îFƶL7t”GQóƒUO†7è.>ߨó‡å±}C~ÝÝ@˜ðù7Œã˜9,0¶NµTªç[$í4Ù{i=¿ä0½óá|Õñ]I—sOîôûýauÀ¯?¯‹ßË¥qõ¥w›¡0õ¶ëÄ¢º¾Iåªúgȇãy~»‘|˜&g\?ÓòE{âÖxá…[EÑñï}Þ®ê!>Øú_7ïÓ»âeo Ïég>,‡.[.ÆÙÚÖãDÔízrÁÇnagHlá ó«Ãto–±¬û¥–Ĺý["Š®hl==!Ó_Ìa¹ìýÛvý \ŸsŸŸW?‘.7¬VüÎ’¶>³OõRG輘ÍÛYÜÓÑn¢ÛãS[êguŸÏ¬/ó9¾wÅ\j¦K×÷wŒÓߘPèДDú¹†uÂúÆgɈ }Ǩk¡óÏ ûX§xCâ܆Ze¢-6­®h9éOÙuXþß÷3}\û°×}Vmà”ðã‰4`lå5_ôg‰CžæÓŸ¡ü{ÄF¤èÛe½WŒ0 û'=Å\ö¼dï9Ö.±L•‘Ÿ " w(c+eÓ¡ãðó¸DZ5¹þ§/Î’J×le×õ„è¥çÉ‹òº„÷¯ÒòSOÈÛÔŸ<ùÉøéJ—(š´´Þ„Ú„ÆÇnl={z=q¿DÈuæs1×2›.0N“߃–UM¦å.sïêΓÑköÕ>¬8&äšt#e·ÃŒ?JÈÇ$”庱}þ9ûÖ§÷—¬>‡ºñó+®ÛÚ)ÐdjŸröVÌšó¤s’Çø\ Ž ë>?!7 Ï=—<èKÙ{+–_ïó?8×U\oCÖ3™¶<7?ýêyâ·ú—rú³ÇhÕ—×:òV‘²ÖÞ2ß&:vÊ~ùÀðVÂ>¸§¸îfy ,¿ÂYïs3½û¿ïÛ*$™6Òpo.nWFVÝYø8½>rùÂ+o:’¢×MKçÅš(Ÿ7ÝŽ¶ž¿ºù¦ÕuÅ|»5/vìüž¦úðû9¿:¯+Ãuôx–oÈœdj0Sû^ ÁÃËÞøÅ÷8=ÆmwölOös¯Õ—™h±v=3ž5íHùœ9OÂÞ§³|¢âÏŸWÊÈ¿PງԬûns2ýWµ¨ÖÚ dÍïãê1ÇiÊþ' R’‚ È72Ñ 73f¬î,æ[³÷Íì¹»µèÜUƒï ó!\÷t«S$‘É”;Ü¡»t~¾ZÒsçqZ±H§ï{?lO¾ôê{øöO&Z7.¢XåçD=²zfù#ü÷[XÜ?aûÝκÆ8ƒ2ZýpéP2ÝÝgäúéEònmÅÊæÛÇéäÇ[ë‹ìBÆ97ÀMôeOn¡ÜQ\·²}1~Ÿæ©eßà´Æ ÛÏrÝÿ7`œ :æ¾t)™¾mV2ßûÖÉË+»kìñˆ¥¿Lû¸£ÍÔnDÖ÷jÝÔ"&ú]S±ö*Äç.›±ýR~þúÙy_­¸î¥ÌÚQåž'Ó·öÅœ}‘<òýÑâ£b鉈–Æ·ïȈ÷û*V¯d¢ü~ZW1wŠ­Yòóûç|}㺯Î]0ês2mPfÉ£†‹¤ÁÚf­µÅRM«S¥šlîNœ×V&ú*Arç|²å÷Weâ¾7«C¶ŽsÖ÷¼ …îÙ©µ¿J¡MWn©±óÂEÒëd«Ì‚ž”.ú[Àu'}»ÏÁ&ºåMïf~%º‰ûàl~ÈÖSüûÞ·Ùú  ×µkñã±²š´wú¦ü—H÷N;6‡Œ§´”N?þ×òdYÛ|]Î0QnUöµJÌudõÞKl¿•=÷õŽëשؾ}±V)tÛnÙuÃK„ 7£tn½S­U$°‘dÌÑ5Б%&¼øí.â}duÃïÏœÏCð¹‘üõ5¸>Ÿ·—BuÕj÷éÑÿé²ôÐÎ/)Ýuh}zp;r՛њ(¿?ÒI\?±ù›o²ý >§ª!_ï¸~•<ŠRG¤ÐÕ’€/²å—ÈØËkÜz±ÒºòÛ¡*²$}ӸūMtæ’¶oë$®›Øû;ORëA²Ï]¡^¾ˆù§ÙrŠ0ÎäãîN¡Ê.UG”J¼D³Þ÷ZSÖJ;\oµçÔÎú¤…3ÏD‹˜B=_ÚºŠºbuÉÏ. ëÀ—â9«lï¿1Î#ã¶{ÁƒRhOòÓ‰¥Y—È(g0¬•òû¦2Òó\ûçåú™„~ÆæeÅõ ÿžøš³úš¯\wÔƒ§Ï÷H¡kŒ·Ë¦Ö¸LÆùÜ»·’•²s à÷u,Ûÿa9tüó.ÍgefõßÅåÏvÎÁ-,CÁç§PŸôkQïû\&ç÷¶¿6×ïýhö¸°Ê_bc,òýL”_õuÀÖ•ü÷’ ®[ÔåÏ~ .êÍëמ¨}Ô0….{mÕêe—‰¹®gþS¸>Û‡Ðú<qµ£IÈ·ì#æ@3=°u8;'ÆÎ8u€ëß\Ÿæ}:«Ü?­K¥øËdýâUáAU¬´è³1’zŠóµì¹¶qÂ{ÿg>¬¿óŸSØGÂ8o; ¯ˆH¦ç†\~\ó*ùårLÁ¡‹­Â|­‘¸-i1¹Ž‰òùôÝÅù ›÷³úâߣ¤ñïpÝj%:5Ú·:™f•ù¡èìAW‰¡fïFe6[i¹ÁÜβ‚rÞ@u>f«ˆûmìùÌÖ_ì< ×õ{Ò`œ%ÍvÌz==™ŽyÛħ׆«¤ãÒŽq4ÚJ?/÷´J}9y”Vçø]&áÜ‚¿¸^eë#¶ÏÆ?¯Ó}ؼÏõ<“ã´uµ&ÓÕ$­t­ËW‰DùøeÃVaþ]Ÿ´¯U}Âæ{xn8sR»S¶/ů;* :Ißû±ù¿S'¸¾$°t§~É´h‰­S| §’ CfÞé“`¥=>¹õŠèш4êÇ%DFQe_T«Ñ›ÚŸéèZ{й¶¬ßž¶ç­•'KÌ…uê×oà¿|œ<™.ÜŒzù¦ß–MÆ¿>g¥7­hþa·áó~MÔÙö«ô§ Ók^ÛãAøyv5áy”èÃ?ÿ~óáß×UÉVOŒSÞ~¡Ô0Ïdî¸T`ù„TÒN7¿Nß»V:)9vн¸¶¤Ä÷ÜŒÍDWµ~ã»:ß@qÿƒ=XßåÏ/ßßgË^¡(¸+_Ý÷…’)>%•^pùòÅOVê(c+:§†Š<|Xî| m:qYÁ¬FÄyÓ {?Êú/{Ž»öÆqÆÍÝN¢æ¨†èP©d°aÊÂpÏ8ºoÅðékH™ûuâK>ˆ¤õHㆃwõ£œÚ¥òJâ>+;Ãò’Yÿrê×7º¹Ÿ3lI¢÷Tp Ä5rfô´º5ºÄÑÛ«,5ä=I’ÛüªªÄHÊï·õös< »¿ì¾³üz~_¬j¶u¥ãø<ÞÝS’D=V¾Õ°Ý5rêà ÿÕSâè¯ Ë;V¨IÇüº#nFÒòù¹™~ eçqÙù_þ=Ë=ñü¢S¸n{g@nm·ìÀÜn“¯#ÍcìGukWV;p¬'é¸lJ} ]|nЗæEÔâs–Ýo¶¿²þ寣Åýn×}ÆÙz©oëõ’¨oMïAãL׈=`혦×ãèÞ³-;]Ü@‚VyØÞ$Ì7{ÒŠõ&ØRÓCœ7³ýn~þ™‹°÷xÙæSgabÞFS 'QŸIÜ›ék¤þæã­ó牧·•,ÅüR­CËJÃM´ñOͺM}ÔS¬_¶Na¹‰ü>ÂkñüëûÆÙs«…âíƒDú–;Þ“ï:)å³·dåxú"e[¡Fï:‘A?ßl›'ÔDcÚª2:.PÌceÏEVWl¿…ÿ^ëdßW]˜¡ÐÚí$.)‘ÞT^«4´ñuU(á§ðæñ´ò¸]ŽU'Ú€Q÷J¿c¢ÍÆ+˜ö¡·˜3ÉæAlÞÀÞ«Öøéá¸Df„õ§N0Ξ{½žnM¤{ Þ^µ×Éå^ásÆw‰§²‰‹Ö|¨ ™ïúÏÎÔšhìÛÆÚÝ>}ÅÜL¶þ’‡†Ä œŸ.ž'eû»NàúÜ),éØDÚ*où–«Ö]'›êÕ±O<å 'ú£ÿ•˜Ïõš¹HW¨†úZrzýÊCÛ.öÐ’¶$`ɉ»Ÿ=M´s°69WÊú#»[ï°ý v~Ýu¿ÂqÐl/dÍI ’Gþ)5vÜ ­;¨­ûã)¿ßäY9·ìœÊ&ê¹}DP¿‰q_„é“í³ó}lŸ÷têfQ†âN‘¡ŽA¸?ž:T>ƒT=\d'{<å?)—ßÂNdñ¤°¶¤&z|Ñ€Ã;.kÄõ [_±ºfçüXß϶ïŠqÆž{wIã:këD·…o£k 5>ƒnZ<Øô  ¼£}õô7‘´òÀï¦_î/î3°ûÄöøý‘ßÄs>NÝàúƒÆ—>îŸ+b²Q2´ÚMrxlµM¤ 4—e[û1ãüȇ‡ŸJ‹¤Óë”­ÿãŽb=³y+Û÷b¿ÿpê×µüNÞñtqãUév“ìðý«Ñ èÚÒª<ýsw#óû¶ÝsóB$õïýX¶»À@ñ} ë_l½Éïg½ûêsK‡qÞ¬Ýçó <ž¾ëôæÜEÝMrtîØ‹qÝèùõŠ,ŒR‘´Dý†NxnØ]qÀ¬9Åõ«6ïbºg¿ÿqê×›zZ18ž¶sþã&©P÷™ªï¸:ñ^û Sd]È(·O±Ó_FÒ/“æï?ˆ²:bëö\aóTö9œºÀõý÷®ë]3žÖî·æ¹u“ü ½kû²ª+ôôÊÕ~Éwù¯Þ<ÿ%’9î~rþøÁâ{Mv€=wÙ{3×ù¯×¸.£oµ×qôVëÚg¢¥·HukœW7S}biÓU×Nx/c¢|Žùñœ[ïxµ•pÿ‰OHë·Q¿ì¯•íü¶Ûâ Å™qe‡F‹£GŽsͷȧÑåëxÅ'Щå ä›· ™õ~u“ŸK›èöE·†‚(;ÉtÇê‡ÿ~^øðó°ÚÙÏ£cœämñßOŠ£³K2ýù~ãàÒ[Rè»–% ”Ú–ô¹×àéj¢‰ýV4zì,¾âßgÕU£¥!ã†<ˇGwÏ)0Ž*¢Äà‘Õãè6Ü„þiÜxgµZ/hÅ#ÏõêHdk‹nÜ4ÁD%²óW|.>ÙúŸ`¿·bó$×u¯ã 5-·ùجôNùë÷o‘çs§ZP,‘>¯¯]üp¸aû'+îuÉÐ|ù‡÷ólýÆögœzàþþÜÏ)FZé†E5,ån“s÷ܺãy•>“»=ȶJ]ª]{Iלس…f„¸?Ææl]Å¿½ÉŸ“ÂusqÓÌJVÚâ»è.gU·Éì÷/~;”HU£Ê‡¸HÐaË6˜-;kµ ßï²y›‡®÷œ:ý‡âú#ÛysŒÓKzòƒ|¥å mŽž5ë6œ§uù¶é³äjë|èEú4}Ø|`¿Hš+öƱQÝ´â}fûblÞÃÖµüz«JöùÆ)ðL±ñ·ôXzûÐÌ”C·‰Ÿ¡ëýöéécf}ɧ½ˆóñ„yõ@î•Aâ}f¿§dï“]ß#¸-ÉPÔ+àÛÎÔ+–æé;$ÕëÉm²_"“¶ÊŸDý[o¢»¦&y#J6+;/’¬køáY­¸ßÆæ#l~ÀÖi|¿Êþ=É0Îûƒšˆ:Žãt)· Y>|)úá¥gÃ$º!0ר.[ˆ6t_þç¦HzøCùеÏjÅýC¦;V?ì~°÷|ñuªÀ8Û%}©àwœ¶<]¥ûÎiDÝxÖ IôX‡Úh ]³0«ËéHá=éï¿ocçfÙïØïÎØûºlûX§èÕàb“·£'bö/º49ømžßÝkB 7?•7,×D¥L5»Iw<ÛàM•_Äßg°ý¶ßÇtÎÎÿòõ'¼¿Æ8s#jÓ_=JÛ:8‘Fú~~‡JO¢ÁóW·=jCR/߯nʈ¤#nË«Ÿžºƒ\9¿n’üæÖ$:ãe‹i'µ$µê,9ÙùR$½{š;Ø>Lü{Ͼ7¶_ÍtÄÿ=rózÁ8óÃK´Uÿ5.µVœö9䓵ßn;D›=¨ÕÎó½Bø]H$½¾ØçažÜÃÅûÃöKØü[cç³\Ï-90ÎCÏêýµ‡©ûùñn•r¾%Q~½ß’\Pqà³^‘Ô¹ýõx8ezdûZìœÿ\|åÃÎ͸Þ·¥ŠÂç=B*Û-4Óû×õj:ȧ“M‚ÞßH¢›Ü•ùG~nCb;FtL®Iö£Ó,ñ‡÷@üç8àÃÎe²ç=?ž¯ŒS<òÒÓ=,´á[}æqâ “'ŒK]ò2‰.¿Ú䨕•È»`î‰IÍÜkŒÃÅç;‡Ì÷™Câï X½»î›*0λݯü”tˆú…éZ»9H‘ŽÆ9¥ÉÔv{Cïú©]ˆÛƒç·ýb~5Ôoóý¢¿ˆ¿Ÿaóþïmõaë6̦Œ3Ç\¶£ºÜ!ºôC'‰ƒ$dÏÖü½“i²þm¾®STdCÒÂ9£#éSSS¾7Zq–ý½Ùº”ÍSØs+Û¹BŒó²WÁ[UF¤¥.ç’NpIW¦?òòK¦Ok´oU~WÒ»_åýúDÒ]{Ž—~u[+žogû¨l¾ÅÖsì‘ë>‘ã49TðíÉcèFÿo…9þ:™Þ¨s­ÂÛnÉòUýòlI}_6™p´o°X×üú§¶ð»!ί·¹ñ¾y+}óVúßòV2€-`«ÿ‡û¼v·?ú’üU9W?a·?ñ%I ´ñ+yÖF—½Q"|žTî¾¢(Ã…ÂÔþ žM®þ›œgSH ±HQÈ¡À”(hóW²bÍ.Y±©@B7 Y±,cLŽ¢7¸x7Y„œí¿à7Ìr&˜ß°ö+ÙZ!_Ñ[ÈWd>tÌo˜ùÐq~Ã\Î˳ÉxN.kÇ&xѹfMp™±Y‚§YÈÛ É‘—%äMXräÆ* H#B”¡Àñ/N[/NÍŸxqzý/ΜþNÌÝ!ø¯¾â¿®ÏáI÷Ï2'T‚'ËÏf¹‹\]rþ+½ñ?Ñÿ=ñ[?üßé‡RáßK¾âÉùW¼×]óväâÇ)Eñ†GŽ\Y–7Ære%(ê!W–åiQävÏõt!3;ü_ð!æ2'˜1ça.C ìÜ#—ËmR/8€\ȾÈrɨ•AŒ:T¥Èþ Ù=œxÿQvÈ!hƒ j.£Ñ*d÷ü£ŒF;ð‚àõ ëŸdbx¡èAР!Ø÷«Úðo½ñ[otû{õF¹0^&÷½£(ÍÀ…© Dš;ŠTÒ Åj2lH*®ÈP¼a ¨QÄVà…BÖƒ, AAÛ¾’YksɬÍjºUȬeYf*½HQø¡À.dq™Ý©@!b „(ÌÀÂÐPB fà‘è¾’ÙÍr•BŽ£â và … BÒkÅßóÌÀ[Èâ²»å˜dq}Ò“Ï®•BlZ`r¸X€;Š18€EiRf(p% Ô ÜQ¤:T(V3pGÁê@:P¡p-@†â ™@"¶/²dΞm«Ï‘m+C‡ Ù¶,˶Õt @Ñ…Âç²¼Í@„€T €Œ@ 1„PBf …0B(!3p‡Ht (!3B0¡ÀäŽAØ€7D¤™ÏH“@P>Ï[ aixA`zO>ãV¡… Y\¦·¸Ct:ŸA ˹ ƒ3¹Þ1Z©é@ašâ  „H@ ¡jÈ!X@´Z`rˆ×$pH Ù$ø¬Gðú“¬ÇTà ¡‡ bW+ð‚èõ h ~ðFš€Ø—Òdø6_üÿ¦'þ_™/*…ëeqß+ŠÒd(Ì0T(P ¡HÃ@&P£X­À «™@µ/¯d ŠØ¼QÈáB1ký+Ù·v—ìÛ, A¡Û„ìÛp¡àÕÀ ÜQø: ¼…ÜoPBfà1è@:PA ƒ0Â@:PA  ƒH¾’ûÍr#¹Üo.;R ñ„‚T ‡ˆ @!i xÉð™A:CXz>û[… "Ózò¸î[°o!GÒ5ÿÛ ©9rp5À¼!ÊpA˜j`^hH*Õd«¤DkR78€6)D @ 1›;­ ‡° ‚¸5À dyÈj!_RÁ‡€Tà ᇠâ×;£ € ¤ºä³ŒI®Ö¹?§Þøg}‘ë‰ÿS=ð¯ô?ÖçþQoû«}í¯ô´·Ÿý³^Æzؿӿ¸ïÁÊÝ3Œ¤ Ç ÜQ<:T(" ¡Â@&P£ ,@†¢ ™@â²/˜d ͼQláBÁq9·v!ë6Ü%ëÖ ¨rn5(D›s”Üic(p%ŠÒ ÜQ˜:T(P pG‘ê@:P¡X-@†‚ ™@ÅeÚw¯(PÀF AkÈQÌá ¨QÔ6´7wöYxØsùÛV Cq‡% Ü$.ùµ^(v=ÈâúŠÞƽã@ágr½½ÆÜ!H*ô3÷~½&(ÐgŒ@Š>R‚1)D @ ñ˜¹³,xCDa ¨ & APrÊ4• xCXႸ´Àä™H ´ ßæ]ßæ]n¯y—Zøßs©Và…ÂÔƒL FZŠT²€ÅjÞ(Øp4(\ðFñ† ¬v G!€ÅREmvHr¸A(r-°9ŠÝ$Üo<€ ÈPøaÀäBVw:PA ƒÂ@&PCVàaèA&PC Và‘è¿’×­Y@ áX;Ä£ €ˆŒ@ !…;ð–á3ƒL €°ÂA&PB` ÈB€ È ¶P ä]x•ìùÝ2P@!bÔ;C” 05À¼!P=ÈjÕ ¼ Ö0 ”­¸C¸:T°¸CÄ:T³È è0\–7@ÜZ`^¹d ÄnR>8€Â7 ÄRMÀ¤h¡.¹ÞfÀ%Á诡ÿã}·î/ÿÖã¯Î·þ¹ëu®ó*Ö«þÊÜɵý»ó%®ÿüwÏ‘4ÀÆ]77“ë5è- Ù@n^¸éz4¸ùVà…Ѓ, A!Ø€7Š!\(-°9 Ã$(Ž G„"ÑðF±„ £vî] GÅFÑè@:P¡x,@† ™@B’¡Â@&P£ ¬À E¥Y@â² , (¹}~Y*·Ÿ…B³£ÈäÜïP\z+x£ÀÂ…¯ع=y\·¢ ™ÜºŠÛƒ‡ž@ -›;´ @‰â4w¨¤wfƒ;«BÍj«x¡`(X#·ÏŽ¢µ9ôjpH ²HQÌ¡À”ä¿óÎþ¿rÄœã7ö®S&ü&‚õ ¶6c…¶r~!’îÁoö‹>+ÓšþØ@µÖAòYCóùÔ€,œ÷¶¼zI2eþ.†µ¯_ì$ø.¸‘NÃÁH1×à!Ùž[tÍæŸ‰qæ-j1»Ú~*‰l»;¿ÑAü‹Æ¾‘L»ä›Ñt{G2ÍK¦‹¤¶Vi·N–øÝïƒ÷I®%úŠðþO|˜Ï­«¿Œ›>C±h¦ãů?’ü—6´8ÈV{š—L/fÔüp°-^·®Õ'’®¬çsºïÁßý1˜¿óe¾?ÌWËÕ[†qòvßÖ3ÏʽtI¾9¾k“¤)èíºKÉôCÿ´ˆ }‰ÂÚëÀ/-#©lC× ¦•#ÄÜWæëÁüÈ™ï6ï£S/[¾¨ã<ïÉ9í¡-ÙWI.9ÈoŸl,w?™’y›šè×5#ÎxÙ‘ôdܰÜß)~æ“Áü°X ïO¨|ªxŸC Æù!u`¥×̴͵Þ'ßÝuߘ}›½H¦ùöÇ.ýà'21ªL™ûu"éÂ÷Þ’Ñ!¢óÉ`ye­£×Ü¿ñPÈåôÎæÛ¨Ã85Ü’hÓwÓVœ{¦ƒ4.•Þú”,ä.ÉÉÖêÍ>0Ÿ×0Š2ŸægÃòûx?Žt1w)[N7ÆÙ²• Ž¡S pVg²æQù5§¿K¡|î­‚´v'EÒ\ ‹®;ú~ñ,‡å 0?zWŸ2+Æ)¦ä1¢hÄ/¿N,t‡Üˆ¶>Y"K¡ßu[Õû‹[’”äwx~¯Hš§Ú kãÑï2»ïK’ð½½É–7éÀõÓê5³OI—&h>*s‡Txw¦hîæ)´ð‰…›ÕS‘Ðó=õCjEÒýê÷kÎ+ê†ùË0_f>O&݇ù6ºú£¸-ËPìxy'pæ"#²‰ ¨v‡¼I[™g^ÿªÏŸ«•¡uwòfâ¢ßNŸŠòƒÆˆ¹2̇‹×‡]ð[à³¾BDìË…õ³å Ê0Îôãñ3?äÚEjs]ïÑà™õD±(YŸBWõ-zô¹š´^8ìчˆz©ÿœÞ>ËG‹ù™ìþ3ŸýÿÇÞy@5•vý;vÔQ[°òDl #"6ì± 86:¨£Æ2бaº{¬Oìˆ $V°¢¢2z÷É9û~÷ýÞû®ûͺW×ú/gX3g“dïýÔüÈOgÞÓ¸n Ž›Œq„nªl1÷•ƒŽôý¶ yùÕ›Ôö´Ù3éªqdUþy/Cs匑ºïSÆrEïñ¾Ø };Åðü¾ö{ÌÃÑ–3Ô‘­ýo·-¼I5—†=HÜ1†dåL 41ÔaõÍ¥†úðù…>xéÅN™GÆ>ãøÒŸ…»Ìü\Ò»ñ%$gP¿¼¢O 4aH«o‚¡:ÒfËÍæ’Jڥݖ — wòrãÖ+nÆÐ%…vòáýÑ–õaz%<5ùÊØ“¿q~YÝŒü¿¥gyÕ=}6¯;@›ŸÑ7{¸ŽË%ÍÜ2¥~‘+I;<,Ýñx }øðö¡Xwèg•eÓ4¡e|žådÇëyÝ>‰¡õmÛ†åxúóÖ¥_0ÿ2¯ƒõÁJ"ýŠ û—â³à:%m ]hټŕ`Iò Ün‡’ªdŸšý}בTû“™c©KÝþq[»ûóþX˜ÇXl¼\!柡¨â,óz‘Ô£Ê ðÄ÷ ñ-:Ü z¤’Þ^zêäŒýÈhfXq‹¥‰G_,võãëó }¤Xޱ A¿/#_Mˆ£‘W8d!Žßk«Z õBí¾È•ôÞå™mÝÉÒacxæKÛXÊòî}yŸ'|ÿÐ̓åò ¤çc—¬Í». Yþ)uò7ëˆ,âùº½iJº¡F¿¡m‰@(†ZuíVò¥è“‹þ8ŸÁ>€þ|†¾b ˆssï¬7C{N¦Õ‚o_ñÜïÛd÷9n¯”ÔlíôGCIÎc7óm1Ôb}䉭—}(r;1^i{ôYeÇÎ' â°þÞNdÚŸyÑá:â£9ïQµ$y·íSó$WÒmËí¹-ÖÄЈ}w:iÞyóqÐwýÆ‘ƒiT/[sE Õg¤å|rˆ–÷ëHÁüÀSí¬’èPëîÛ’¦¸‘Í>ÌHCÍwI¦´vñæß/ü›õý»Å×%úÏÍË ŽI’Ná…ÅäùÝT›SGtÄîÚ¬ˆ‘ƒ’hï¶‚v:'WòpÝßžŸŸÅÐV>›ÔäÍÏ{1ÿÌæÝ+¡Þž~[#Þžâ\wè8ûz¯U¤ú¼Þ;ŸÄêHt7ÛŠiÓ’h…ƌ㼠YÚ£šé‹œ:Ö)ÈeoCÞ÷Ϙ+öˆã¤}à}ó8çÈç«_š½ŽØˆë-’éË?N¢× ÍE#k $,ß,†j¦µK¬ìËû bý Ç9XŸ¬_Ë{’@œ‘²™þ•CÉÕ©³µƒOéÈýößF_ŸD£´Ëusp$\ùà~,†ö¬ºÏ¬OŽ/ÿù£ïæ!/AÊ|îŒd¯­ä[òÊÂßÎêÈÞ¾£›XîL¢é[mŸv#•–´–C·Üªà’qÆ÷ÅCŸaäêëa+ò£vŸ“k“.BžúÒþ#¤ItFÝC÷^MíBÖDMîv-†v¹U[{¶??ßBŽ2òsИý}YŸt-<ÿà­æ×RÛMN+FïWèÈÂl'/×è$z?{^ï‚=ÉÙâ5…ïÇP×~K{ú>ö/S×8>±ý)_ˆ>‚FþåÛrE¬ßÛ>¢ç]Õ‘w‹ïNI”%Q͵¿†%øu&û4‰<}7†ãð~¡˜§èŸjăç®ì¶±]ãéR¢Øþ&ÃôšŽTLÎËýv.‰~o˜“ô:¨yb¹áÀ‹1´ÐŽ‘ð>Ò8.¡tÃ-½Ää=ï§Çö ¶ÎDçÍ—ÝÏߊ _V.™¿EËCÃZ¦$ÑÓ—œ½RÎ ­3äß'ˆ¡-¾ë4° €ÏKüýÑo–÷FˆuÁþwl^Š!Îëã§~|ˆØ<٫芎\ÝmBígItúeë„‹\IV•!5:I`¾ÐL—42÷½4æ_áýõùÏÝ6§ÆŒÀFGÈÉgù'‹ásvÆì±©šLÍ.¯=n$‘¿Øô}çœÎ8_‡à<óýAõùÏø`q~ÀÇH¢¯vx®MͰ§º&Ó·¾&'l=Ǥ£Œñ} ­ú»öÞc‡¾ŽÐ§}3ýWl)x׺€çýÍ£ NÍö$æSn4‰z½6¡&Õ‘Þ‡êçLJ¦µ½ûî4e,©×Óá¢ûŽêµsÁþ<_çéÈ¡Àu»áüC Ï›~Vâ±Ä¡ñ E#Îúc‰ÕåÀõÉt¾~B1š|W)ôÔYPC÷¥îoýøù3~ÎX¿¬ÿá ýûc²=WätâuÖºkñ¤ÌÂwÖ‘·^A‡^OæüÓGÓû-›xÃù^ûñ¾Ô¸®e9#xnŸaý àùü'Oß@ôþ¢Çu¤Å­“L4É´VZäÒÞuyü•™’š±4s«âE3‡’y¾?8^¶E¿P; Ùϧ•‘ϽâØÕo™BdäÔF2"úò—ýç¼Ò%SÖؼyt²I”e,½³¦Kô‚]~üüÇ–/wƒã=Õ¿?bxîéaùòèFÇÈ’4çUƒà¹]ÖXU±z“Ìñ¯»‘|ç û:ÆÒÃQJË'/ü)Ž8C?TCÿ <·Éf÷êÃsŽ‘êæ“²š%èȾq;_þL‘›ù÷¡·í¬ci«›÷º¯šHÑ7÷Øüÿ$Äy½áx(…çïÈétÂôúqréqT¢Ž´_½ûþÍ/É´Jtå–Y©í‰8§Þ¡—béÛåcÄÊWO»ïcã;ú™³ãe£ñ]qÒ\«NŠ;A–«RÁ¸{~ìçÌú•R¨³[±¹ßýn¤›ªúƒgy1tõé‘mËÌ#ÐýƒõùÏU½ünë¡“ä« Bct$²áö«ë¦Ðiu¬ÒŸNw Î÷;9Ö ÷ºO^2®à>î°}ó¿_`èÓm²#Wd=@a9*æ¹ÔmAz@´ŽHªÔ¨j™B· ›„ÔX4€(;êE1ü|óû4æ'Û—ó„ìøÐ– ß²¾ ŽÃvè4ùk×éež0z0þÜãv½RèTfz½Õ…ÜéÜ÷™C&¼¿ÚÏ©d^ã/îƒ!ÇùAØõuqúŒûãb¶×"}Ð#BG:0Û7î)ôäÖË# ^ýN6No³xÕî’ñ×ó˜_øzØÏ=KØòãýŽä-}Ç!›wrÒëûÏ«»u¤J±…Ë ïz{¥*;5Í•ènݨ7'?††UÖÕ°¾ëGq¼Âþ€~´Ø?lèK,8-Ük…zUçR+–ã‚ûñýûî‡âþ¡ƒ×ªÐÚ˜¶3W´‚y;Í.qk³‰ŽìJ~ãù#…îZu|å‘$vÏzú¶z,1vü WŽ|`A¿`|],G±ÑüFq®µÎ/ðO¸@þ\>.Â9úûÌ=Mï4PÑZ3:­=ø©©2ÿ±zdóXÊö½@мÜWÃõ*ÎÏÂR–9Ó%ùúÒ×Ä9¼Ž1Z¾H~Óu¤éäS÷­Úª¨;ù[»—„L¿åØÍ>–fÃê©ÅÚ ŠûSÈ«Bÿ~Cn‰ž5žÙ¸¾H ½+Þ™= òx}dav*Ú1ºÒISˆ÷!žý¤¹Ö¥fEŸkœÏà>¡a½Kà¹Ë†Ç|¾øyú½õˆ‘:bztM×U´žoÖ›ÕûsëõX:otýÌjã)Öö{¬ œÔJJºvt…5ïO­¯ˆSÐ&wwR/Jr, ó¦#&«Ô>8^E‹ [µivÍ™œŸ6(x±Y,Mþª¾æoÈÏo°Ïã¸Õ ã³Ãƒ­ß„È{2ÇG]ñmöU$¯{—f¦.:2ãÂ^åŠ`tt¾Û˜Yn$<"€¬ãç¶êÞXîPòzðoœÇ"÷ùkì<´*[ç´mà•¡Þ ¢Ï—:’VÁ>~ÊÝ‘}‚þ<Š0ô‹Ç1Ôñ´§bÜ@Šó1Ì+œÇ²Ÿ7Ÿ Ïù-O™®V*HûêkOn$:âÐi¡ÎO¥¢š¾SþÎuKªæ/oþ£J,m8¦Ctô׊ç%¥×mÇ' ÓZœË®#à¹ÿ~q~=óËÄŠÛëH@^ŇAßTßEûÆ’VMïKê7&øßû!®‘kóœïêóâŒm»eI¨çeRÙûDð1‰éTã¼µšzµ¾?¬}âh2~ÖŸS&ÆÆÒ9Ó߬کõçë ×ÏìçüVÈò;嫞ÿ>Î7¢ ñ2¹[gÙ°{Ö:2$âÉ»injê¨,~[üfÏYÁýóÒýû+{Ärt%ðÜ’üZ_/“™û'ŽèÈ?ÇÀ óÕÔáò÷×Nºý§U~ª¥cF|Þ'Ïp¿ÌÐß] Ï•åÜ›³Èí ÑcÛ鈬F×fÓW¨é¦>Ù…wÚ8’'­>ûÜŒ¥/{ýnç|Ù·±o#ôæ ß -ªU&85ÜQ@÷}uf8žºBV$ml5§¶ŽÄM^•´AMdÝýäÙ²aù‰±4¤Óík}~ex؇X?ô"¡ñ¾3»NÑBü-Gÿv•Ti¤Ø¶¼’ެ¿3þIý-jŽÓSs›³;ho,x8ÔâP€™y!»Jæ8joؼߕ+Š_ÿÐËdÁUâŸ:ܧY‘–ho4;·9LMÙÿþ«Ð4,¨Žgt,íšýU–ÀÏ“qÜÁq`Ü SÿåíÞ ‘?g8Þ Ž‹•r•Tì¢Üñüµ–œZ÷]£Ú qŽù¶¨|§.é{eöåõ—bé=²¤?àïFäÁ±ó›®üû¨¯ˆsƒ¦É½׈]úÖ/ƒ³µä£Mݸmj:*/ùÙº~sºTûZ,íè¶öµyßï0¯ÐÏŸý\^pó£qZ q>m¯–ÛÞ÷é´ò• niÉœm»Ÿ4 WÓ™îûïL©Nmq!1–¶»ÑíïúùüüÿF¾ î÷Ïé°þJ—u]Œæˆcsñï³^Êk„ÁÐËZ²êYŸýj:ÍBçümQ{¢ß>ÙKÝwM«³bS Ï)Á÷ çkì~ožåt¶5Ú•Bœ vCv~o{ÜΪëwBK¾\¡ÓïQÓν¦¥´ìLÒ?;qºÅÉ›[òCÌì«ÈƒgçiVïª[/çôY[Ç©k¯“ÌÔÏ#ŸÑ’?«=;³1NM[w÷õI´jKV803îX:+ïóṵ́þ| ×ì8‘ÆïÇïëݽ~„-_·úz8íšÖ=ñö:Ë Ý¥%>I>Qþ25íêuc°Í¢ÄbAÇ‹=›ÆÑ¯µÔvÏ+YÿaßC®/r‰#‡óP}ýìÎmXÐŒŒºAÚ2¸ïõZÒ=ÍD}â°õûíRnÓõ†ÆÑ¶ Ó'úøøSü\ðî'–ǵÀóWÎy—³ìì bÙ²pî˜%ZÒàÝö…q jº`CŽnKW<7m¢<ÇŸ`þàø€û~xÇrº¯? Ž~[·ÅMrÀqgŒ‹·–ìÏ~Ñ¿j´š›ÏØÑ[£¶ìHŽ£[:¬;îaÉ~¾ïÈÛ@N#ò• yQbˆãþaÃ’›d¨ÿŽq«¦iÉÊØNžþ{ÔtFØ–²(Gú 3rÄÑUõg>ø¾=€=˜×ØÇd“G5›öYˆ<2v¾Äæ™âTùW«Çi7‰uÍ)S^»kÉš§£nX¬USö|°?íx¸øðƒ¨8zdfÝ“ûRœoâùòóØsÅ—üù…¾^àù‘Ó¦OÜLIúe´SÛIKØóp5=õað´­ƒÑOáγ{nŽ£Þï[|>Z2¯Å׃ý 9QÈ£4:‡8Ör+g+É£‚-Û m´dò]Ÿ9Õt˜{aÓåwÑVQ‰/ç숣' eÉëÀþ_šŸÈ®w:òçAúz8w=m}‘+I„çcÒ¨ô™Mµ¬× RSräýƒ!£ÐÁ-˜,ŽZ}©G§ÕâÏ{ðõà~?žÃï´–^±åÏÑôõ²'Wôå|ÐÝp³$2îà U«ß´dâ9{mLO5õèîIx_šú]tv^v½}=r»“ ˆçHaŸg×…QÖËÖ~-ü~/òØÅž|Ð× ÄÉù«oãA‹’H=»™õ?VÔöœ[M¹sÊî+–}=¸¿„ç0šäiÕúüøÆ­Û»"ˆsç· ®ïŸ$‘jú¾lbçßNØYMÙ÷£!¹Ößœl\G3oÙß}%_âý¼W€\äUî#Š÷à¹R2‘ˆ7µ g²É†±u]¿ÚªiÑ8Å+·®¤á&Áöiƒãè²¹a-ó;–ì×à¯çÓF6ì¹°¶l šæ?/Œ{¼9Ï`nÏÅñûx?×™ìyy‚<^}ÀsO pm 94Ñ‚æ É&©ï–õ´†ùi—û&§Ú’‹¾çE:ÅSyÊÒǹ©~û"»¾|)S7|Íï_lx.’¾.à¹Ýÿ¸4D˜Bœ³zo‹èM&¹ð¦ÛX5-Þ²äu »vdI²í¸GËâ)­‘÷øÅF?~~Šã<®3Øÿ l×lÑú5Ÿ;­_MöæŠ>8ÈûÎ9“BΟ>3!¬R6iâXéx5Çû*ìáDF&Nˆçøå%û Ø?ÞÌ|ýWT± ÷~w0âb àù‹Ü˜›)dÜÜ­ën<È"3‹Ç ZêuÇlófYÐïÖ¯TŽmâé\f;ÿbÙ}<ÏgçEŸ„xïÉpßXq*ÊÇ]ð0W‘=¶jÿÄ,rêû“â‘j3˜¶šq½-=”pêsL‡xŽë]²Áñ÷sqEö\±¿1Nô}Ô¾yljkîη%ú]eY&O&«‰ßÌóN-;=&KFôøè­¦uû-ê:=ÑÌoâ½#ìy<]5Ù9(³¯óvþ¤\þºâ$¾nüfõ<5QWXÑ{Qñ#²ÖÇùd—eÐÏ*¦ïîw¿7a6+·âéàŠµ!ÅKê×ó¸¿Ì¾¾lá7ªMÒdÓËîÒ¬xúºgôß÷\Kæßø9!GÇ_öut6Þß‚8‘Ηê;¯U“‡QOm+mDfg5ºÕÖ•liG’Z0ËŽÒ/®ªE¶ÛJîÛá|bÓEõ­]óç„úzç.¨¿R‘¿ðIŸkã‘ÃU»¼z·]MÙ÷·@x®á•?:M=JõÛ½#ýøy žg°ûs¹¾;==„¹Èt”Z„xv_s¸¤_â<ó—çgrùec4Aœû6ýŸ®¤’&qêq›’ú{¬Y¶DM¯;?ôÈÕ–^•´{¬è(ÕoÏâ_öC\?²ë®üzˆåà²\l1Ä™Üà¶ÅJ×TÒBwR;pÊCÂòîÔÔ)Ä¢ïÎðæô³úïô§—ŽR]ÿµ;ö–á²¼åÛ|Ýã¾áüQq¦uf®F¦’^æ¯dW:?$½íšZT Âuwsv_&ø(åwö²Ûš’õž_à>0î÷ãþ¬á>¼âœy}¤}Ž©ä…cªÝÔoÈ‘œ°óM î›ÞIšÞ‹{ÿÒìÓTmx.,¾Ø—Ç<ßà»Èúǽ4æª* Ž.ÿí¤•—R‰]Ñ·ës“I+ñ¥ªo~¢‰+MWô#ÙvKGéëÍ6µ<|øý0œ7a°}翟d8>k!N…¨ ü¥RIAõÌ…M÷< Ó\;j¿HÕt¹÷ðˆÊƒû“v}R:9;Jk.m:í‹€2ëb<çÅúÑ×Ë\Qô¦ÑCÒ SÉ¿]»+/x@†çÏoZ|BMÇKíörïÇÕÑQú"wâˆ!¦|ßÂûxiIXß¹­º[íO àù5ôKÒÈ(fÉ $Ë!ݲ×U5=3§HåìׇLoûbœKǺ¹ù¨'}n”·Ø¾öBØ(ÓO¨Ûò–ãAö!xïI_'ÇåÜÌuÏ4)}s¾ÕRÜ?÷òV•š^‰¯ÚX÷¨-©gÒlñ§‰ 4(â¬këÎeÆaä'¾ê±æ”ÓÁWÜ>ˆ­ñùÄ©äôZ—2 V\Vô%“Œ›:àÍ$5Ì_BŸE‡iEßÌ™ôǪ¥ ´Þ÷špŠï>ëçÇÈo4¼W+8ÕÚ×8ëžF†Ü­)T'e’áQ:“×ÔÜýÙ>tq£UÂM ô]ÿ,ßÓýøõöcã{urÝ çIRˆóû‘ÞsÒHTZyBx& ¿l¾Åú¤š.ýé¹ÏE'Õæµ·ÿöÚ¯Þzó@ó’óQ|ÿpþz)lw§ÃÇ?ó÷EÙu{¿Vq6½P&w]™F÷©ÔºëœLqîlÑÃýj:.iÉÔœ›é„‰)yhº€9Q÷ãÇ{¬7¼—ŠßãÀý8ªÇ”òõRú*®'p¿`Ó±“WŒìj´Ÿ#8ì=Ã4îþLñ óp'„ùñ¨;W¼Êîâ÷P°ßãüÂpÜ—Böû iäÏQŽm_Þ'ñS¼ŸÔ[M§µÛs¡~X;zeÎΞÍv$P‡Ï-ºô~ç_f_Ÿ­—{ܽ5ö\BÏež:õsÙöð“ã«Äûä“—xwF75]²è檕;Ó";æFp"-H¨5Ú6€¿·ë:Ãû|ZxžpBµj‹hHSýwŸXæúEuPÓî?úŽšÜ¾½°Q½iT"ÿ{bßÅñ ÏØó²b~ Ï÷õõ‘+š¨ßØÒÖÒ+õ»ß'ƒçÄÚ·TÓA¶]µ´§ú¯¹¬J¤‹t©m™ì_f÷>Ìž²î§OÂÏ_w+Žêh4?@Ñ¥U5êtÕ¶–[Nç[Ý'k׿ýѹ¾ššî»¯>%¤]mwäî I¤•7îpQëÇŸsà¸î»¼aþ˶Zn¾õEÿ>‰à¹»Oýú‰hˆ0¡vçÅ÷ˆmë Ž¦à¹Cû=UýiV%ÿ)©sis=)Éçc?góæ©G*ðüÇÈÜ4dRd»ËMoß#K[ŵ|­¢¢ÉãNd·Lµ«ÍŽN¤mô^þeö—°ïõYøæÊk¾ Ù÷Åx>*8³<¿…ž™¨!Nã§kªGÜ#.?Õ]r[E-V„,üfîL#wZav"Ýxfûä=Ëžká}J¬c}¾Ãs믙+˜4WCÒy÷ÈÁMöñçT4îVÕó¶ ¤ŸÜÿ«åÞDZãþ!oÇAü<Ÿýß'üÞ…áý&Ä©qiö×7 ‰kr®®èyóºÆMÇ(µßR“F¨|Û‡ëYë©ùâ®—¼6ñ}ÛxÞžÎßó^œ¶ü¹i{£û³ZˆÓ|Ë{÷Wa¢?Nhxä¸é¦×Ü«¢Ü90ýB[wÕ+‘ö[ñäføù²ëhÌ#œ'\:l;~ðèîÆuq0WôÝóó³©â®ßO'vÄY|qªdŸt¢û©Ç•éýúýÒfðùŠë*üžžSŸßù홵ÈÎ(¿‡=oÕ¿TÛûºœN¼®Æ}úzXÅw½Èƒ—¶ÉÝ»&Ò‡îÕ:†—ÌGð{8¿bÇÛïÂçÛiï;ÿJq­Z왫!~ŸR½âÂÒIEÿ˜ kNªh¨`ÙµN$§»þ‹ôy‡F´}|ɹŽŸ¸/€÷*ØyеÑ|Q q 缎ú¯!ê>æª)édX¤ç7 õ÷zú×ôôÒ¯‰tŸ×XËçV%yŒýÏKƒmô=Bx>{ïRCÌ×^i“N¾ùÞ_z_Ec¤Ìÿy>ãɘ~µdüý|¸¿Å®s ¸þm|o@ Ï%º72.QC¶T/êV9dgxtQÜwÇx¸h819”+J;ÔA»_C|,½:`å]òì›mj¶ŠrùN¤O¨4¡­Œ~¨°<õð°þž?ΟpœÂû"ø=HÃóQĩۑ9Q‚8 ×gÿ6æ.É+hòñÔ=û­YËΩö”ýÚ›Œ;g,Y§áçƒ÷ð{¸îÄu©¾N Î×%?^\_«!aa-V±ºKš=™Ý¿.äoÒy»ÍµßÙÓ‚ÝcÆÔ”Ñþ›ÍÏhøzÄù.®oñûúº€ç†Îa~ ñ‰T=÷éé<óÄfèÃæ[ÙyÑ–f¬ÈjÚßDF÷^Ê?5+â¹ÞÛÁûÂXïúz€ç&yÌû>%@ÃÝ˾CtÃû×uJEÏÖ©ñ¾1ÛïýFKxîöSÌã@ŠýÕ£Q¾SöŠ|á˜üA/¶kãu<÷|þ;¥Ç< 9vøÑ’M›ïV7mß~‚~±öÐÔ—EuÈÊCÏÒ{W•qû$%÷‹p>Ëžƒdðç7˜?8ŸÖ×Ĺ2ðO«™²N5Q[àq‡ä^'­&žQÑΣO˜¯ÎlKR'ìŒ4•ÑY3BÏ›Ž,ÙwÃÏßo¬?œ÷î»i!Îã[?|;‡º®Õûô~Ë;¤ÐròƃU´êŽyù}:u!;ý·­Ì¬,£¿%˜~?ãËïaÿÁsö¾Ôá*çÕÍ&¤‰¸Ÿ³ç:&‡až37÷Ö¨áÒ×§÷äå9·‰jÐËâßnªè«{7º_èFôÇúßiÃç·‹þëËǼÍÞã)侇ޓàzæ—Ò//$‰É?Ç É„{½“²lCdÝ”fþŒ}Î%¯H²‡$–‚LâU".Å|Õø!ðn¾¡7çYÂð¯C9^Æ×M2åø†ÿªoyF9ük…G¯–ã~¡'’˜ó¢Dþk^)®ãÑ‹^o Ë» åü—†,Ä 6“èÈt`X`Á  ŽýÊã|äŒï%ÇûW=zµå°_•˜9óÙØ^œ_ræ¡y8WTŒ”RÀ2¯8†‹ÍðnÐ3œ+6/ÞX´Ï!£”O\y\D ÇE´ç¸ˆ¥Y7è]Ìy—²nJ{—3…\šˆ¬›hމ\Љþ¼ŠR¬1ç].ø #1˜ã9”æ¾2ùÌüùïôÆÿn_,¯'þ;½ðßéƒ?ëÿNïûïö=¦ßýOõºòzÜ¿Óßþ«ÞVº¯™qÿnZŠáeÈc0dx²]£ Ø®=$d ‰ç ʨ^¾ï¥3$£ d é Êà<éÁÀp»¢9ÏKoÎó’ñω8f׿âá[šåZÄñhÌ9fµd ÊypÌjC†!âa¼Å_ñ"Ë[PZ²¬jC’㸆s…aƱ[‘¯Àøù¢Ï %dÅ Ò° M¡p¼ÊaS#«=Ä™¢2dS² =é”k&$âxÔLщAJÎÏ7¤93 ‹Z2‡~$å€Ü8¼_ÜÖ_óµò|Íœû}µ&eù[ÈS(Íßú·5dÉ Ò‚œ!‰e sHä`ä -™s¾›†|B­¿Ú´n SapI@ZŽÝ*1|(ãÅÉ1¸þUoßœrØ­oÎ<Ž7#5`* …&¥|Ë¥ Sü ¤°L)W` k}<¥·ÕÛÀ×7Ô€¿%3à)0¾¾ÎPˆ2ùOØ„!›Pı K³foss(X çonÈš)íonZ›Y32Že-áØ„È´F__e)ÖŒçwný6¡„ã)”æ¶2ùÊüù'÷ÆÒ}ñßí‰ÿ„^ø£þßîÿê|Žùœ£MX&«!_ËÅ`È×*Íd•0Y3@"HÌh$g0H A’FƒÌ QƒAZ3Ç̹AâÊAf¼Á -Èžc20|-ÈŒc²¢—yÈ™cký+Ä¥9¬Lâ{°Fµd… Ä£Ú5Ȱc =Ì™"ñi,Y>µ!7FÃ1Xvƒ)ÇÑBö*rÌ ¯ƒ´ g(*È KÊ1` šA‘y—äF¶ z–ò¨ ¹‚ Ú 2œ+J/ŽGÍpbB@Z3Ç£6…Bõi8¿ãÐR *¹AòËA(€PÈ A²†bį«<Öª©˶A¦ àΉ h¢¹ÂñâÔ†LA†m“²ÀÏ,kÕ”aÉò§ ¹6g5dÆ1½äÓ 9«nPx29Ÿ”rƒ"”ƒ Ï ”ס„%hE\{Y‚nP¨òrØÓ†,A†=m,™2ü Ž=Í0mB9N…ÇžfXÞ (îðRÜiäÙ0Üi(xÈŠ>TCñËqVõC“þ<Ñž‹—gR–÷U—ÃûúgU@ò†€ò@Ä 5$r(ä ­YCRKJñ ÔæèÞ  Žù Ê3cY«2)$¿H²æ˜_E 1ƒdÎ…H²‡â‚ŠÊa­jAöP0R®h<85² 3 x†fPHÁ -È J2ÀÏ@Z3—Ì’ePƒ´ g(4ÇY•€r@ÎPtRÞÃYµ† å< ?áÚ@a†sÌC7ŽyhÈò€bU€¬¡`CA9¥Ø@PÈ ŠXαJsÍ{°Œ Ç åØ‡È 6…äñæÔÈ 2ƒ¢epÜ ò8ˆ¡ œr8«ÿÉyâ¯Þøßëÿ‰¾øÿJOd>¹ Ëo5ä“)@Ö”¡¥ød¥ù­ Žß*å€Ü iå $n(ä , ‰C@y ŽXÄÜÿƒ¤V‚Ø! <3$¸Ô€S¦ 8ŽkÈ’>œK|d  *‰¡” (†p® ¼@ŠŸ°]Í H¼ 8ˆ Z r†¢‘LŽÇŸ6ä ƒ´ ‘~2ƒ‚ i-Yöt4È Š+¤9C‘ɘû-ûÕšc¿æ< èä ^(ä¨Y3<4PQ‡ö¡¤Þ4r= @å𦠹‡ oZ… 2ƒâ æxÓÎPÄá <æn Ç›6çxf {(li)Þ´Š<š+t1H ²‚çŠÞ ¤(‡ ýkžøkžhòÏê‰ÎÜóŠ˜÷’R ²Ä ç’Ó ¤Ù@’†s‰ú3.¬d É *‰!‰• HäPPH ­Ù@R‡€2 ØÈµfØHô`$‚„™±lX9È ’ß”²"ç Á ¤ÙCAHA¦PÞ  Š#š+ÒlØ &dÊÜæ˜×ÈvÔr|GÈ IʹAAÉAü ”rƒâ’ƒÌ¡À$ šœã†€ò@nPtÑ\áyq\X(ÀPPH …¨ü /Ò SÊ1#6¶d E *‰¡X• (ØpPÈ W@ñ†€ò@PÄ  CR@Qƒ”#;œcJ"#Û =¤9CÁË@æPô$ú _2”W–ÉuæÏÿDo„Mß±bdúö½ÿÓž‡½{Ü?±¿ý+½íßékØÏþw}ìg=¬¼ÞżO æs‚„ Ä8J $O8¨$†$R–ÛUr¼ÙPÈ’K²† < Ñ kH¶PPwÉÔ d IÊÜO†Ä3…ij†Ä³‡Ä“2g|J $`8—„^ È’Q 2eÎwAÊrX³Þ æî1$©$©$i0Ǧv†¾#™Cß‘€r@ÎÀ29$±”cÉr©e sHj (äÉ-™Cßñ)A6Ÿ¶$†ÄW€¬!ùCAE 1ô%ÈúN87)ðbXµÐsBÊáQ‡‚Š@ârXÔrŠ(„cQ;C1É@æPPŽE]ÄÜ[aî«pLYWt)ö´3Çöi@öPpRi9ìZÙ¯yׯy—É?kÞåÁý÷LBz4 {HL)È’Ó¤ÙC’JA¦¨^ ÈVÊ%­H ²ä çØ ¤ÙC"‡sÉìÒ€ì!©CAZ’;Ú€™­YC¢K@9 gHx)—ô^ È’?¤ÙCHA¦PÞ   "dE Ò‚œ¡8d S(1H ²B 圡`d 3(/Ž©m ŠʹAÉA(¤PÈ J²ÀÏ@y (.HÊy@¡)@ÖPl¡ " dÊ0¹A=`8W„^ ÈŠQ 2…‚ôe€DP˜Ñ\qŠAJ i8W¨^ È V *y@á*@ÖP¼¡ "ŠX ²†Bå< ØÚdÅ-°¼Í¡Ð% ¼$€¢倜¡ø£AfЂAZ34)¨ä Ab¶C›ï\*þ´7þW}ñ?9ßÂÞgØ÷þ§ç]ÿNoû§Ì·þOÎ\Ëë[ÌëT‚l Y¹„ñi@ö\âx4 H p.‰¼@6H¡ "z•d‰Î%—¤Ù@’…s‰f ‰æ ²d gîC™AOR‚l ñ´ $_4sn ¨ÙCJA¦ˆÞ  2dI Ò€¬!9CAEL_bîC’f0÷‡™sHTsHT (ä=G@Ï åÜ ‰å $rÈ ú$€¤å< ¹ ô›`dý&œô• Hüpnð÷i@öPR)ƒ7ÈúL(¨ˆé;PJ H8W$^ ô˜PÈŠF²f¾'ʹAÉA(¢Pž[H^ SÈ J2‡¢’€r@nP\2˜7($‚B‹™A±™C±I@9 7(:9)ûÿ˹O,àîk¹õ žàŸ“Ü? çŠVÄÞŠ«0DÃû>_‘½Ú<ñ6ïûãÙU²=ç–Š¢o'úi#g¢u=‹Ë7>&Rôoš»kò´N~ÔÐ÷PqÆw–Ýqâ&FÏYx›8ú Y—ùHE‡ÏyVùÌnäâÓßš'=O¤‡›NQ/Ê÷B ô?FN úÎú†Š!Îá r;ˆ3­($¼¾ËmÒñ/QŸg*ú´Ý8ÇF[¬Hý!SL{¿O¤VŒ=Ñ~eü“+ÃúC}à|CûäD1q$Ǥ×úÆ5ûkˆ×‰W~§Þ&É×õæØlþ­JD0ũʚÆ2Þo}UÐï„õѹÏûE³¯×†÷óaâH!NœFÔiOo É?Ÿ»£ù“[dÐø´Sw_¨è¨âU=ÐW×é4XFîñ¤‚g ÏõMiCÐ× }6‘Ûmä³ q\ô*R{ä„W‚Ä[¤®åȧCž@œOQû©ÛÐôj PEFGžôíjÓÑŸ÷GŸ8äÄ¡ïúÑúªh!Ný/]Žti¢!ýÇœYäºèi.î÷ñ‡*šÐKP”Ú¢ Ýêû¹·p¼Œ>’_/~¤ñç_ú«°>Tï„1íç/©ù·ðl“ùŠ—kúq>›]ÙÌ>’+rl¿Õ≆¶qÍ~¨èÜ÷ uú¸’b_ưZFÏd eüxÿô:³¹õfy¾p›éþ }ß› »'XÆßp2öÄ8‘S™F“FÜ·MÛkHÅoý7™Zª©âÌ€µùÝH\›ãuw’Q±jGü:o?þó7öa|"d}rŠ„é¦ yË–°>fœŸ?ÄwyAÿ ÷SI\à0ç9µ4$z•ªOˆ“šÖ•'„×Ër%—­Xß°‡Œº/ß:O[Ç¿ŒÏ6æ3ú†³¿‡1¯É$2Wt`ü’€ÑÒTòèÌx«.7ÓH~-ó&©éÒ±S䩃ɹ ½'Æ9Êè1úf¿C'ÞŸã!w û4ú{úy NöãäúûËÔœ/Xº´ïZÓ­ Ÿ RÄœKüÑ¿ˆícO„IÙÓ^ìnÂûòð¤çÔ†»‹;D©‰ë íSÕN%Ãî7˜Ô^ϯ¶¦Ç¿Tâü¢e4Ú⢅Ԯ¤ß ë[tYذ:|XïòyŽ#ëÉñ‹!ŽÞöÌOM6¾FjÔÄm‡Ù´æK?’~©ÃÓìYíet&ƒwééÏþ˜gÈmÂ÷Ïo¦…ç§öìiÕIÍqÕ$gsKíx>ë_Ø€\Ž{žú»Œf¾qþ¸oŠ?Åÿã Ç}^ ùV&Q¹¢Ì”Ówî©ÈáM›â¶LR“ìåCëuX¡¦èoÅò¼dôZõߣšúSô Å~ÏÖ}+#*<÷N—È]ïT¤©¨©&–s¯TS0üŸ­SÛí¹ÐlL›ñá6üÞb]uK¿2þ`èçŠ>¢èãkÄ„8©kOnn¨"½gTÊvGEûÛÏÌܪ¦šG_ìGô"‹¿šë^&Òï{[]zàÃÏç°!O}>ë?/†çž;ÌëSH7‡O'<Ö©Hƒ§ƒ:E¨é¨Œ1Ohà@Ž<ö)èt"ýó^ñu ½ù~ˆ~sè7á媀6?Êå/K Nýn¡³v.H!{Žh°D¨"»Œ¬}L¦¦;' öÆÞs$¬]"äZ=ÄEîÍÿþØß1Œœs6YŽ‚â0‘Œ)¤k£ÕÊÜ×)ä¨úT ª¦•̵ª/~}‰òDÊa|"½¸aÚÙ#éÞeòçÍÈ4ä›+àùúéóûdò>fï§m{Sˆpé_“ÕtâþMÚ‘æ¾ÝIÒé€ù6Üñ.“§èÛŒ¼E–»bAÐ/Z_çäÓÕÙÃÎ$Ó„‚ÊW\SÈßÖ›¤ÜVÓîÞ{*'WhGöÚ0àÖDúŽÁ ›øP¬WŒÇλ. ÑÏ ç³lÞ}×þ&ѹ¢az0s2û¾ø`墉 èdrêÚéGÌ$¹9 Ÿë1íL"­U¥ë׾ǟ¡Œ}\z-Þ_}|‘kcÄ·€8«íï]Ÿg—LþXµ4r2¼®£Û?i’©¦ìü ÿÒú¬ÉbøüY¿=¿2<vÌâß81\Oˆ ΜsûsÎå%‘m‡LFØLO&YB¦páóÑc>ÔC·cóáɔ͛ÿ]œ· ×ùRÇÉÈ?Q qL÷Œ>—D¯ìú{dd"}W0}…FM‡¿»­ûs`+ê:õÈŠu±P’jUÍ„e¹ø¾!/ 瓆¾œˆ“³ÒsnÎÌ$2ðBÀ÷·Ç“Ƚ¿®=y”¢¦ìç[‡NÓ£yîpéyúrúKá¹>?ý”b•Db—™%…NO"çæõ™ä£RÓäÄEM?ÝkJd¾^[Þþ™HÙùïˆþÖèóiÄg…çæòu-WIž®[È·A‰L•ß„¼=c²ôþ± =È»ÃÇÔ›H¯tëé‚xó>ÝØoq\Å>hij€ç× ê²¡ð ’lðbŒ:•$·[è·'jŽ¿=€tñ;ש]ÏDÊrçSäáû€ëôï3ô7‰ÉU9Ö§Â0xîT¯ßÕJ¢ÿ“^¬æ^¿+ùó̧‡méùÛÝÜßÇÌåç8>#÷!ìýÑCU®>Ó¿ßx®ÎªŽ4ûóMRümCÍjNJÔ+¶Kß–©´â¿¼ß½ÜÉ2é£ÓïG$Rå«1i.ïçðóô•Çù ò;gõGœ/µ2šG‹ α¨æßwÇß$;[ û8ãûMòôÝ4·‰RiR·:Ä%Å쨺tÔÕ¥‰´Ò»…ßOŽ˜Wf~Ãrw® ¥ÿ‹½ókêúÿ?Z«Q«Æ AEqÇIp‚Šâ@A­âŽVq¥j+nÜ(¥Eëˆ;®%@pââÄ­BGÕ8 NÔVSçï}sï¹½ ö»¿¿þ}ž÷c{¹÷óþÜs>çäórŸ;+ªâoB׺Âî\ÐØtÊóéÈáÓ/^ŠN \רŒõ“ßë3É8â‰3F§‘Ñú¶ïÚŸ É5Û÷¦ÓÓ»6 íÙ”¸yù–-ï™@Gì;Y:7OCY}ƒñÉX<óóêÞüçuËÿÞÀ8wM½O™FÆü°ïí§„ëN}òh:MÖN¿ÛÜ»- ›ˆ”@tˆÊê1¬>‹gÆ æßçnb¾wøãŒ>gÚ½TÂuM?˜žFèŠ;§Òi)Ã¥+Ót$Ë|?ëðM÷[|ä¤S&‹¼à‚|=öžŠ+;ÙdVü6{ß;ü‚ëO›¹³IçTÂ×ÒHÈÖOÊæ¥³¾çDvªh}ÍýxscH絉"/–Ý'Æwâëµò¯7pýMÍ"—=™"pqÒHúÜG•È2èæºª†ú‘1'®ôH8O?kï6Íâ>²çÀæÍŒGËÖüïŸ^bÃ8\µ!Õ;…dœ­ÒµJ#ý;×êâ–Aùy†ys¬’Kë#ñ´eÍŒ±ªcãÄçÎê~Œ'ÎxB<‡½šðÜùùœSl®êüšöæûÇM$.`Ç«vŸ¦‘]>7C¬í2èÙ¨o¬÷º]ÔPÏGãiÏ»A×>/Bó÷¡¯&ô‰?êÍ8õìçü<˜çQ»bœÏVU rêg"ï~ÚRär*ñ÷_à¹#0ƒÓ/>¯Y7r¿†öªÇžx4ðÓÐa!bŸmæ;¶dóFVÇáã£(ïŒó¤<‚=NŠø/*Ó'!•d^˜Þ:ulm¼fÂ’1sºkQŸ.öYOë(›Ýú¡x¨ø>c~`ó86ÿ•ÎãÕ¸~óÅ%ÎÆÏ:N¸›i[Jœžìê;nfÒì˜sì3Y ,×@±)žVïëû¤Ñøñb݉Þ?ãå°øÎçÃ1Žo½3j?N&;€g©¤õµÄV¶¥”¬k3¶•—'ñ_Voøæxj ~2jdıîÄüÂò [Ç98ÀÕjåçcœÛ/Ú¾¹ô‘üúCÊ'ª¤’ CŸ·¹º"ƒÞIòuÃê 2jñï•+ãi’ÇwÏü}¢Èybœ6_d}ðùy™[>_1ÎïGoîêVÝH"ÇÏ®ÿê—ÒíÙˆkÚ ª«ø‹ÿ=MCRuKQuÇ…ñÔ”²oò£„Ib<‹q]ÀŸlž-õ§ ãÜØß³FÙž”„8¯o•œBn=ö¸>gKeï×GŠn>6ž*éÈm>¥&‰üÖ—žÝ7ƧfùRÊqvÒçªô¶ƒn'Æ'“#³Âý´.…DžW 2èÉcôlÛŒn™šØ,žÆä&Â>Ϭ®Îž¿Žÿ”÷ Æi¹"k‚ËÒc¤cùÔÊù)dàž/êÊ2häiUJÜè@ºO9^íuMK <´lðñÌÞ#ŒçÍò™4žU¸~èN¹ÙýÛ£¤Ý¹Ç¦ RÈ•Ñ&ƒv›á³¥®7‚ßÙxöf5ÏxW»A¯?|Y{ÍÖ½Òß_ëï¿6KÆ#Ä*¯¹öË~)¤x…JäÄÕ ³¾\òÝÒ$ªÓÊAÇRâ(Ï«#æ{6[÷2þ"›_Jëág5‚?|Þabköê«;¤¤o«|ÖÏÉBË-k±eTd?‚I¼cr-V¤å´ºQâ¾›ç³÷ [_ñq瞯î¬Ã8Ž·^ô!âiÚV§wŒÓ{âá„Jú´i‰ó#mýÉ‘áëësãèæÌî+æ¨ÅýÆifIEKýö«ïŒócPÙÔ#‰×¹#+Kæ1ZU{ØRäQo’UsÐïcñtãÕÇ•r¾Ÿ ›/1~ëÇÎ8e,Ž>‰ËU¹¤µ»—“t€|±nî²°û&5£Ö—~jžwõÒž‹]Ég‰/ë~öI<Å?ª~iu°˜/Ùz…í7ð}Ëëççºâúýû6Ð-3ì'‹6ŸÝqÞD¾Õo±3ÐBç¼°þØ”zõ q}KŽ£ ‹ûµž_'˜²º+{®¬>Àûþ–ÈCÍÇç>Gʈ¼}¤Bè’þGMäBäÖ!-tÕâ„„¯vµ >ó§?ÍMŒ£ËWFû]±!îc²ûÆx¬n·ËŸ»³õÄu”Ã/‡¯7ì%Üî&ò`ƒo»›ƒ-t=¼¸äÛ:„¼ÍÚæ}*ŽvšÞ}cåÀ/è†wkµËj(Æ{Ï;xÇ<ùëêP åçÁUɰWEgÞH£=¿*Ò¼Õ„ÑB}¥†˜¿ôî-0”¼îýÓ5™Ï›7b}6ÿãlËml&’´Ú¾›-5Mè€ †[èhß纻‘ªó“çãóDr ™1b>fu –¿øñ‹Žå‚t˜%;q<Ž~²·ÓŠG¿Œ×l^Æúä³}TÆy”Γm‡O£²ºQàó5SLäu…5µ–[èô×µ W;’U-¼Ï ?Gƒ¢ÜõQ‰_ˆõv_¯Vä/ÂWqŠÏU¥lþÚ>~O<¡Mž:ÉDºgû&ÄN²Ð®Ý·³ùw"w‹w®5 )Nè÷?Jü<,ÿóþ¹.ú“çW´Í·äŠq¶Ÿ)¾nôm=qlsNÀülZLÏñaúvGtõSç:‹þ!}»,ùüèH‘‹È>ãøÇfŒ m™¢Âõ]ü²»A,áãÈD¶?ˆØüx…?±•/yÙxtüg»ãèaãÄàÑjʸ֬~ÃæcŒ'ºgþ´Äq;ùêÐjŒƒIê€NÓw‰Ÿc÷ÎÉ?FYèérEš¬méKšf .¿<ŽÚoF>s&¬ûˆùŸ­+w—ññØ<ÇáŒÃïkÿHü{?ü¬ß)uóiÙÖZèÁeÝ'}Ò´3ÙÞ¾–yfhýmôÛLÏqƒEî›—3.&ãy°uL¾yÆ™‡lùúÊN¢ª²­³ÍÏD\8<Ú =¼¶Rê/²hTãÁ£âhuÇÄj°Xÿfûl߯3\ó~½ëI–ÿ(W¯Jò¾Á8gfmgz¾\ˆÛüý5O“Pï²Ð•¿-¶¢w+²ÿ꺒K¦ÆÑ3²/D¨‡¾Ç÷åçÿgÄ÷3{¯åãUb¿åfÃrÏm$=²âˆ‡&²¶»Ëº ÝzÇ;hÅ'Iüá²çWŽ£a<{—z6\Übù™­c^ª«Õ8|©¹¸åðKB®J­Ÿ¬ž²]GHÔ¬g5ê›HŸzÚÐà8 ݯ´ø¬SâßýœFqtþì’•kW‹÷‹ù†Õc˜oøõ­G¾çïŠqJ-™Z¦UëÍı hl"O=òv½H@žÉù­Á“CuIöø½‰žé©›VWIósvþàqðó¾{"ß[ZïSaœoOûŸËººž½?ÁLZ™HמçŽöX舊Q!O<ˆ÷Ì”9·/èiñ²p¤˜7ÙºœqLX<³º¥tÞ¤Æ8¿ãN¤¬%w]*$¿ÀóŸã]áÈ =i\Ýmå¬6$§âŒ•ÓÎêiFó–9#i~_Ua¾œ-îÓ¶Î Ç8Üi™w·V‘e] ig"õä¥v.?f¡õ–z—m7Á‡Äz’HõÔu­ò©jôHqžÉþfuRöÞ)lý§Ã8ÓŸ¯.‘Ûp% ß´¡í­Ö&òeëK›-”çäv'_ªž¦oÕÓ/-ö£·š²¼Ìò&«c°}[Æ«æã¯31ÎÒ~wïÖÈŒ"ûj7¬“ÒÈDr#§.ïrÑB${/*êH^f]Ù¹DO—D^Í>?Lä³x`ùsÉáAgi‘Öÿl‡çì,!cKmºhu7céd¹j5SÛmsÒýéÐëKêéÏ>zm}6X\²¿Ù|Ö>~`Ôé§/…÷@ý|õk'C®jRЇÌåú\¢ïwgV×:&R­o-´W³ñ%Ì’Ôé£0åÑÓþÝœ<#Ræ 6æ×yÞìÜ;âð ®?‡³üKî;÷¼†‰dŒˆ RTɤ¯º7é‹$½¸ò£UO3ã.þªÓõŸ†w|ŸqÃIð‹«0oçó™ ã¨ÃÜOMÓ‡pTÀFÇ«ãêZMšfÒ8¯mö_‚ú“×5ö ¬].Ž–³|ׯýÄu&¿ŽÉæ3Å}i¾TãúUŸÊ&í˜0’¶Xü|ñ&rd^“.KU™tõÙãÖ_.±û.ÆQ>.Þ'6bqÌê±ì\C¾õ Æ ïš›Eë}Eµ—9P›‰ ™°ã€KŸLZç΂o'u#òiûv»×Š£''EÏ@˜åcVWæß+ùøä:\ÿ‡ÍŸŸ=Ÿ–I«5ù)ÞcZ«Ûö˜ Lª³Žøº7ÁËg }¤§Æ:tnD™4â–÷™sõÂ:WwÏ_G6°ýøe´ÄÞ’Š&óL¤U×6mȤ#¶)®ùönCNÝ÷ûüX=õL»µmp—þâú…=vΈÕw´R<ÝS¿~¾ß߆qê†nËèVm]—×óÍÑ]ˆ×úñÉ•GgRe‘ŽAû·%±C¼›¬¸ÞŸ‹ë0¾^PCäI1~•ÿ±Iåï7ÎWçuJÌUñëâÕÂ92)ª»s®ah&}üüœïo¢)i$;{è)Ͻ ö¹\„:¿‹·üó~&r°¾Àõ·ùrÄ6-õ‹©Ö×÷ žÃwœ’IÛ$/ÿåäáΤ· 몧u7?îJ‹ë öyغ‚cayQÊÙVaœ¦{6=þñÄêÕäÜú»µRÈÜzáÏçÎͤMO”2ÇÌîNvVš6«ûzÚ¦á§?Ï ¢¬þÅâ—Å-ßrÄz‚”¿¥Æ8ܪ¢ÇÙÍ´ëžnøD)ä›ß+ ÙøM&ü¼Ä鉯z“Rô\Ɔåzá¼Üçbý•ý¾¿…^‰Lù5G|/J÷Âqý‡Ëeõ»­£Q#{[ê/M!]—”n>}c&­úÕ‹¡{ˆ%«ò„º›õôØ{'ëÿ(Ö÷YÝ’ÕwØ9F¶>–úO‡qÖ”ä~ÑmtƽÅîO!-î—I«Nj›X#ÌîU²ÅZ=åow€Ç,NY=_'ÿ"œ­›¿.†qM÷(õë²4ñjV¨ßÕÒrwbÃ×É™ôûÎ3Uß•$÷ͯ†_–ÕH­³Ä_|_±¼È8tŒãÍÿ¼~¾º… ãWÙj-µþús§uÞ¤‡ ÒfdÒ«¼úýžÓ›\8Umy‡kzzJÉ=¹î"ïŽÝ?vžBZ?rÚƒywÿožyn¡E~^ÛêJåTòKß¡Ÿ?ÿ9“Ö.Ó«â»ÝIßF7J{«§þW螬](›Ÿ2ŸññsλyíƒÜÖ¯ÅsÒz›+Æá×»éÔò×— jžJ4å§îø6זּƒÞ^ìB|7öÞsþŽž>+fꪢÌìy0ž3›w±ú¡ô¹«0ÎôÊ}çö}K笞ý Y@*ÙïR­øÒ§™´LlXÝgfBxn¦žÖ¯93r鋎”íÃç?Wúß”å_¶ÿçð ÆYøzÂíÃxO\o“Ósò¼TÒ’¬|xÑžIù<ÑŠÔºý›ºïl=-Õ³£nÂi_ñü*;ï˸Ôü¹†&ùÏGâúé·ZÚ)žÚßpoR‰§×Â_gR>îë“ãwkv¬¸>\±IßYb]?ÿ~Îeñœß£!q×ÞrÎqðëGÆéÆáèæ'ÐØŸiQ2d%Íï5û]&åã²¾P‡×Ó¦Y©Œß l]Ïò«ç²óÊEÚÌÒ¹ ÷‹¯#1κ1}Šn˜j ò™m1UL#ócoœô*šE¿s´o‡Æ„Ÿ7èéàc)-† ‘ Éþfyžrø×MØs ¡KÕD:sbÛ£6§‘#Ek¸ùÏ¢GV¸4Þ´¯ yj¿~ºžòuš Êò›Ïñ~,Nøy\qÞ{sU.©é•Ú'$Ò]ÆLnøK™qjGã9eðû~viy·g>¤æ\ÏŸ÷tÖSþüâ1^™/øß»„àïâ„?7ØK8ŸÈŸOvÅ8ÏŠ>ØÞoàúÈûd·ÈV'Èé-•úÖ­–E‡9}5<9 'ql÷Êõtx°Õã¾g¿÷êê|}¥©^º¿ÜÿÞ§„ç÷'ü~¹‚÷Ɖ?ïò¼S‘½tVrð¡ ÓN®Eü*4Ì¢Fy‡ÛHN´Î>Öû^,; æC6?mß<ȧ ñÑû­ùùga?×ÝàRiÕ¶Õ{éÚèrx²'ÈÞcg‚S;dQâqí |}?2êX`ã^×cEð×ãÿÿpüÿS`Ð}tA†Cê=‘¦­É¢¯#u-¾ñ$›ÊeT]L/Ô‰º‰ñÉžËÃù8­¸nXö­|{lÝ‹þ§ˆûÌisïl̢ū MwšâEN­l·fúùX!¯ôçAì=Âò.;ßÉê·Ò÷”ã´(ýpcðüÃtÞÃʾ9Eôoe)á;³hM—±äÅòÎDwS™z(–Ž^¹b¯áGqžÍò.Ûb¼SöÜùu.Ÿ‡çDè³w»ë¡7ÏV?špú9ø}SÏc†,*_Ýõ̃g~d›öh‘àäXvNý½:1Û'büxG¼ãºüy†#ôúŽq­Ë&ƒ¸ ”ä,zyòÊÈ Ï{’IΟ¿u%–æT9\aþ½>â>‹¶ÁΟH÷µöçªjWlxöî—GéKAËf 8Mõy¶eÞ™,ºhu‚Ƴ©?‘;¾èK‡æô(¶{^€øžeqʾïÀÏSžy ç…óÂüã ÝUå¢ý(å럧ÉCS#®dÑr6}u±nor¿ß·§n¾¥#¿ /Qüù(ó«;±õ-›æû~ÆéìxÀÇèiMÇ-E~;MJ¬zú®Ï½,ZòŽõ<¹åOL¯vyKõçúé{ˆñÊæ‹¬®Á¯ãx«qÝW«Ó—Ž-™L—Vú¤ÞzÕO¤—áQÙëö,:'|‡÷¼èžDöõ몲¢ðW£ë­*lõyoþÉê1|ž(Jعgé9³pŒsöûñ¥FLù}ÇŸÈöµú; ‹gSR¥Ûò‰/º‘bŽ /=ÝPkWò³È–bù¯/eëƒçެΠÝgÖaEE_¿ŸÓ“©¾:?‘ÞO’bª”Ïê/Èš0Õ¥UõT˜ŸQ6eÏ…=¶qø×M·=®º× uÃ3äò:û Õ²i£½/´o¯t _ÕÚÝðT{½pÿÛ‹>`õ Æ-.lÞ†ëG¾ë°­ÓW”Ö»ÞðfôR{ñýÀ$—ljl6ãé–z­ÈJâöÈKOù÷Twq^ÂöûØù¶ß—ïœâæ7JûxeÙ8CEô•S'›ö±ÌW™"ËÚko‡¶ÖÓ«ºçC:öë#lÝÂê,?±sDÒ¼äŠqžïá é;÷]½Z”8KTÅJWñªŸM_OmÒcô̦äT­¶+Ö•Ö ó?Ö™|Þ©-žëdû=‘¶y/%æÿ‹ ãhö÷úí@#uiS?uhß³¤Ê½-kÎ7ʦ‡æ–~ý´¢’dÌ¿#«fŽæ%ý);ÏÃò[ÿ³õ [w°sz`œ+/sÉø¹Fº0$.dêÆ³äŠéYßÄÙô)Ù\ôÓ…*Ò¹h×µžKcia-¿]ñ ²yxþó^÷„sE¯½G¬ù:´™§q¸ïÉûãôÝ ]­ê‘NJ jÖ¿k6­jh–¤¨Ùl½lëòëøXa_¡—XWfù‘­×Ø÷Cùçß4_}ƈq.~ØãñÀHº>œ¾?¬®³yE\¿lÊïö"³{•05–òß#ô/PÏòv+k®4êö}áü] ñÜœÃ/¸~Ÿ:&*«~œFVï•’W>ƒ¸ìøÑì6 ÷kÅÈÊ“G÷&fîxÖ¸XºK·íÞÀö}ÄüÎî;Éò!ÿóVb}Èᛃ¹ªcg.KìuœN©ò|_ʈ 2°³—õ˜&›ži«”x«qÎÐm· –.­V½O±/{‹ç“Ùý`ûW'×,oiÜýXðMËüß3Ä8ú¨ÄŠÃ¾›Av_lZô‹9ÙT_­Úíf¦.$Ës\õJgcišËå“¥æöó0‹ö=þþ=õæë=Íò×›0ÎpÿÍ{Öǧ%ÇNÙú4ƒ\œ1ÆC¹$›Þªd*7õQ2¥u·s«é©c¹WÙO<ÁÖWìü«7±ý ~þœ”š‡;¶xú8½æ–æ´¬¾…ø¸”®‘°<›·öo¥ÜÙ€pß݈yÛÊ\Kî Ï®âóaótv‹÷açK¤çñÂ1Ά¾>¾çÓµE³Þ–²M‡KS²#³)«ï\·_nÕï¯FißžØ:·›¸OÊê¬ÄÞc¬Þ/ÍŸ:ŒPã|®êÂqúòAøýåâ>Ƨj ÆÖƒtóˆU:j‰¥µ…Ï.Y¹—¸¿ÈæålžÂÏ#bÜá\Ÿ_§åÛçT({ÌB*|W{qW\¿§Y¿Ð£X zª}Ía;ÄÒAW*}(ægö|ùûrÛ›ýmJÞÔeѼù¾olÃ8›Ûj&êØ&½g!Ë틾‚çxkÕä M^x×ÛвÝþXúÜgͧçõ§ìü‹Vdóöw3¹ÖLœg8|“„ùŠc!f¢ü:>“Ì,_¡ì¨o²©óÛZš¯OòbfKb¬XÇfç%Øûžíò}ö=PéºÉ5‰ÍóLÔ^åBÔôn™ÄñÏ£³©Û&Íâ£$F¯Œ-mwÇR­iaðÅcŸSvž˜å6®riª÷Å÷€´¾©Â8™£ƒ›¬7QÑ¿Lœðu&)·*Üë'm6ݰþð¦];GuYK…uºXç(xNžÕë_ÃXZWÌÛœø?{-}ìµô¿ÝkÉIø¬î™! u A©¬ Á©ƒdó²û0ÏZÊGäxÖJIŸ8iß_)#"IÂGÌøˆ:!È9>b’À³ŽôA7à…I{þF|€ÆX°I…ô’ãzþ:Ã(áËÚOÒ=ìïèmÉõ@/ØÛÒõoô¶ zþº =¥l)#VÚc©°Þ–ÒKÒÞ–I–µM`#†àCpæeý- ðr8žµ½5ÏG4Kx9yBô˜¿£ºê}–b éA÷1'~̉áNœ(>‹•{&ÈHŽ  ƒl‚3’#@à ۟0c¥ 1Ž«’ôs’öΔöB7Jbv!#0c9†˜Q`ÆFKzþš 0s¤ý5£?ÀÌa Ec!½žŒ+Œ!ðb$ý~Ãÿ޾s\¿ß‚}ç<þFß9µÐoÓCè·)íƒ.å)J¹Ù…õ“r³¥}çŒ^lŽÀ /Ðã*²Þs®Ø:ÁÐ!Bï9Ɔ° = GÏ_¿°´ …ô‰rô?túÏåÄ&þ£yðß™ÿ›ùï¿ÉŠýßäƒÉ…ÿ–!È4R!Øb 9. ²B*^ÌßàÄJù`rIos)‡±# °Á<6ÇËø°F¡gpt¶”{cü Û9-B`#ìeÎõÔ 0b9×GØðOô´Ëƒ‚þ¤¯‡ÀÀ¶ LD£ÀDTÁD1FÒ@VHU §RÀ\ZÁ`!úÚåIø°ÎãFÊBT ½í p8>¬Bà:pÆd\O¾§ð‡ú ³wa òƒy suÒžåçuçuáNy³ð»Ú¸{Ž€4@ÎÊp( @p gh8”ó'lX)ó‹cÃú!€u¬@ŸNÖ]-°aó‹ nŽùeذóË,°aµ’žÂ–|iOíø6Œ…ÈõMWàCkƒ„@fÈF‰¸°A’~ÂÇ! ‚yŒ  Ù!5Œd®Í÷ކì¦2»ñ|ìhÈ©a03¤€É´‚ÑB„¾ž ¡¯§´º”ƒ(åd‡A6Ȇ4@²œl$ƒA5ÇqaóÞWDžê 1Dàe{`9plX™Àü²JXœ¹ƒ–Ãßê)Сpf6ÇCtE"ˆ€¸Ø ÂÇ/÷çßÝOø̓ÿJüoæ¿¥çð¿šóþÓó¹†õPXnãžm $GP…A6ÈÁe€œ`6ÈAf(„ïê\³ËYÒ#]ʦa Ãè¼.®ÿ°R ¬.Æue}ˆµ×R&ú|kä¯h[X°/º ’ð\9FWžÀ‚H‚\øP!˜  ÂÍ2B0C4dçÖ§0…L Ù!5r—R¸óLkÎ(j §ãYûÁ4ÈÆ ƒl Éa¢0È)a&$ƒ¡,¦Ò ÆouØ3RF¡ fÓr5º ŽÝª„u¬-Ï‚0C ñ¡ÅÎ0g8”À¤IBáœBzžœ·}œ·…;ý5æmÂï’ÃÝSd䊠Œ€ò  g䊀òþ„Õ*epq¬Öp $G‡@H)é¯"°ZƒKV–gp% ¬VŽÁeX­ B†€×\Bƺ‘!ø5R ½× ãÝ0.!×{] sè Ǻ,FÑ fáø[fH!p ìæ1C H+˜(²@ Wü â YÜx~µV0Wd”0™’ ü«Ð]'˜.DèÉ.åJùÕáPC&AòüêHƒ† Æiµ ü­èýÙ9>¡F`X+ ° 8V«\`pÙ$,Ì­X®0y”ÁìFȆ†òþ„cÍñ =¢!;¤&||rþ_ˉÿH>üP.”æÁÿÍXXþû¿œû¸gf€œtáP€àK‚\€áP€@L*„Éš$0Y 2¼8&«²BªÆÔ Á˱¼,R`²Ú U9žëجH‰ÀÖ•ÿƒO-åß„|€O­@Ðk…À,R0€Z`°FÃv9a„<`ˆhÈ©a 3¤€9´R»âgFÑ fQCfHÓhã„@HéÎó¨e…p`¨$Ȧ ‡r¸5*ÌåÌ1W¡H“Å@rä6 d…T0]Œ`<Æ_õÀÃÔ¬)cІÔAvn‚c°ª`ÒH.°(,RàQä@0näŠÜåAA”ð€±#Æ` n„¸oCFœ÷}œ÷9ýurŸR+»gH#ä Œ†ìÁi„< Ñ½Ø‡Ù«RÇ^ B g±²B*³’! 5{•1ÀäÌ(°W9˜U`¯Æ@r|XÞŽÁÙ ?˜ æÌÆ9´B*˜#’s¼È )aÀ]åø_H Óhã„@H é L¤¬Ò?ƒd0”²ºñ,j$ƒ¹4RÁd1\àðØ  É`: d)À=”ò¨# <(†4BÎMò3© 3 Y%ÜUάÿK Ù!5Œkˆa—Z GCvH-°WX¤‚±c 9Ì™!O4d‡Ô0»RÀðZÈÞþÃ\j3¤@"Ð É „ðñÈýùÿ='þ_χå\ÈÝÿ$ÈAåAAF#䀌€ò  ¦±–«Q`¹ä‹q,×0Èù!xu ¬‘ðuΘR \×ȯÏãø®È ©è1L V!èc ¹ÀB,ŒAm”0ƒN`!j +¤‚1tÜþ…À|UÀ ZÁ$jÈ )`­`˜È)a`žÈ)a"$ƒ‘B ¤„¡t ¦Ò@VHåγ§å…0ó  ÎyÀtPó%A®0`”ùÁˆÈf ƒlLi€dÍy¬RÀ æ Ę5F0ld„<`Ühë g˜XY!Ì åq{0µò€±£!;¤†ÁL Ù!5Ìn†0|´À@TÃøfˆûö±öã¼ðã¼Ð鯓 ý„kÙ¹{‚€4C ¥VÌÈ ) Z!H?Ƶ@J®N^5”¹"ˆÃ ä‡`Žäè0 ë‡À6@În dذM`à g|8”ù!ð 3‚?Ê`$‡ äC$,Fäs g$²A*%FàÂj +¤‚it ÆÑ@VHÅ@r˜( ²A*Wü ’ÃPaÍçaÇ@r˜+ ²A~0™Aàa‡C9 g€ä0]d-Àh”ò°£!;¤†!Í…0±“ W4²I¸°2n.éÓ†@×.°±U0±V0rˆÀ†u…¡# <ÈÆ6@Î0·²@ ˜\+=²@J^'˜¾0^¶²@J$ÄuÔ>¦¹?ÿîœÈr!˃Ê,÷}Ì{…ç½å<–ë¤9.܉Ïm,¯±|ÆrÙ"q÷ÅÈ=?P4d‡Ô$3¤@0ECvH 2‹5 ÌX­h!Ü9c[·§€“#ଠAÉxaû®0@`ņA6Èh€ä†µ‚Ò9#0Ã>À°¶B*m $Gà†A6È9*’!5;V‰€ÖA2ä¨È)à:H† ×@VH…`dx d…TüHŽà×@VH…É‘£Â äçÎó«aŽ0È ©£tRÃ0fHÓDCv(æ1B0P4%A®0R8”4ã9²N0•¦Òrç†a,Œe†0WÌ•ÄíKÀ`6î, Lfçö$`43¤€Ù´‚áB 3¤€ñ´‚ùB ¤„ µ‚ÿ‡½ó€n"É»½É‚ÁÄYda’É€K€‘EYdÏÁd‘‘M60€ÈN“E, ˜4lK"KD™!˜h“ßmuW[6ðÎì÷¾}oö{pÎï°ËÙí²Ôÿûïªêò½¾ (¸õèùØt_úŸ6Óÿ[®}A"P ( @‚Âô‰@5 ŠÔXÅ$(X?`Jn8 x} ÈQÄ:àjs,¢ uÀÔ(l#¡¸C@"ðD‘‡P£Ø@†‚׃4 Fá ůi@BÐPCF …(tÀÔ‡È =p„ ¤K°DÜ!œ`*(H!"p• ÿ¤”8*òyÙ±@ q逨!2£—­i@ Á¢ÓP@| É–™&ˆÑ$~#7Ûäh(p%„Ü!V_$­°OˆW/äg« b@È~ È!èPÔ¶QÈÒV €È @¡û+PBðá@ò<í`J4‚pïÌLl®–…2ü·öÄ¿Ó]{áÿä>¨uû~ü¯Î˾7'síkÿÎy÷™¸û‚B ŠÉ$Š*L(,_(0 Èü@"P Ø @‚‚ó‰À…§iÜ» l@…BŒR£8¸sÂ(Ê îœ0 SŠÂÔP£@@Š"ÕP£X@†‚ÕP¡pcÅ«6 BÇ) Y@Þ ÜQØ!À”(ðpàŽ"÷V D±‡w|° ?¸£øC€ ¨ ‚X …B€ ¨Ð»bÂÐPC Fn] ‘Ø€ }+\˜8ø‚DnM)G €'4‘ È!$=Hî<0eå~ß‚2pg!*wˆ*( ¬4 ¸LÜûÌÁDæ b3 çÂ3 Äç¬@ €BôV „ ÃIÖ~•ý÷ºŒäë³yß{W+Ëö»¬×±?…ÿ¬5¦*¾­Z¦ðÁS¢o÷+¯»¡…¶]} ÜÏP¬ÛšD™¿ï3«}ߪ)Þ̼¿!Jô1¾ÇÙŠ5èA]smu‡K«Mº~ж«ð*ÇÌ?¯9ʤCþ±I´Òµ„Òëút ª<Ûózs”ÑCô#c9…üøO¼—p6(=êŠ>}Üõ ¸¾¶™tOW·8:mOúÝ…o¯–ózFO¢ë”EJ'¨È»e lÚEO7ç‚Ú{Ò¬þ¥DÿO–kéç“=z-Âüì¹qLF–sG×t_³hX•«¤ÿçÅó$ç’¨3}#¬ Yx·—¹ñé(ºxP!uéß2ý°™1Ë`9,ßÈÕ7̆qÚ2ÇçG›øÐÖÝ®’ îî\M¢íÃ~nõ w+Ò¬ÌËËïFÑiïû²´eù-Ìw™ùÈÛê­Ù]-2'aþœ®¾Øn‡S•Ò+:ÝZG÷lÉ›ä9õ*)RlR“£×“(ï¯ÓŒŒõ«ôþÀ›(º§ÁÜ?G?é.ú ±ïù¬­L«±½àÉ·Þ̧ÉÕß[†qèõ“qT?ÃzukÄUr°éÛN[=ð%‡9r`RêÁÚ5qÏ¢èÇÒígùåé)úýˆù5Â÷Æ|×¹ë*qÝûsƒB$÷âèOÕýf¾J:Ÿy”»òƒ$ÁÇ­6éã ܉ýÏ™zÖ|‡Þ¼ŸZÖ<-®ï´Ï{Gó”4­œ™+‰Ì¯\اdjí9WýüUz¤ÜŠÓŸÚ툢\z²éá1ÿ€ýÍüí˜ÿ6Ë]ç}l„\JŒ3£è¦f'?ÇÑ_¹Ò:IäñÙ dÓÓ$!_©î°¶¬EíÛÝ\ÕuÈWù¯Ìÿžù‡0r×\lƹ¶16>W<-päø¶zý’È GÏœ¯“h=§aYS²èø/ƒ÷Ÿ¢ÊN’íU }d˜_ Ë©aßóÊ¢Œò2uOmŒÃç&‘“Uûó}L¢Ïç¼y7±%ÉÝïȬ±ñQÔ'7s˜è‹•Õÿ÷žà÷Rô½sÍY´aÞ¿&ŽÖì*{R<*‰Hmžó&Óùõ (¼6t þg<«û®`þ[C„ÏSYô½áýÊžyŸŠŸü¹ðOnâø®õëv$UYlcâ_]ïÆÑhÿÆårÜL"—ŠÆ,W"YÈoéF ;sÌÞÖ5ŠÎ)xÿÚƒþƒÅþÅ|q˜?z߇’ ™Õžz3_N§>p}.ݪ¨%ŽÎ_u%áQ‰d2)½Hþ´ê¸þþŸ/ö \ŠÛ†jQ4Ði ?Pô‘ÊžÊü˜¸S'¸þ/–ŒNo/ÇQ§íoŸdrc暃›'ÓI FîÖGõ$ñÚÏÖÖŒ¢|ÞÕ ñ¾3¿3æƒÄr6x?¤rYüžµ§•öAb¥qô~g.Ð5™ä÷xý“ILǹة‚_’<Ãü1£c”P¿Z1—„ýÍû®]ó¼X~”kîã þsLG-mÚR’LÂë×Ëyyh2]úJuÉ÷MW²rÛèjwÆDQ¹9âe‹ÊCÄܬ¾[‰Bþá#1/Ôµ¾ gSsÎx=Ž&\|&=™4¼šNü’駤½ýò˜Û§ ßÜ(ZÈù8ÒŠßóåd~h;å/gl)ôÁ›÷ý¯–%/„q"Gš»+¯ÇÑ~3:º}®‘BÂ7D¥˜œLßtµmåob;¾õÌœ°(ºü 6PìÃÌïˆõþû{!æLó¾K¼¿± ãÔ–<˜VöR-ú–˜ú¥óOçÄè’郦cƘê5"mÜÙs£ŸÅÙÖý‰ïñó0}³|æóïÔÇÑTåú¶ZU/ÄQÉÒÆK4 Sˆ÷ò&#ŠÍM¦†|µ¯_í[‹¤´Ø{ø·Qôeþk“ʬ(öÉì÷å÷²çb?qŒs|!÷Ž£íÊ•ëSùh )v¤ãÝh}2X´à¢!¯ª‘s}rÖØ3:Š®Ës½\÷‚ZÑO“ùuñÏûÞ¶UðÕ’çÞÏ‹ß5KA^—úœTˆ÷õUbœû–Â{NžŠ£Ç—(†çJM!9{Óu^”Lù¼\O²´W‡Ë¯‹FÑ´ô…×–÷"ê]̧tÈ|×ø~\Ÿ°˜Lù<—¶„Ï©Œ¢½|e«/•J™O'û›÷qKón\ýGÝŽ¥*›þ4åº}wí_åiÛ ¯ÌdÛUï‚ÃN&S#ÿõê¡¶¤g©3·dý¢hŽFÆìªûõÏÍüBù\‡ÇÞ¬n]óèdÇx.¼þ¢£qtuÅ%Áþe,Ľþõ×{.&ÓõKÜÆ•¬Û–ü¼)¨ÒÉžQ´Æª´9ƒf û0óee}‹åë±\–GåÔ ÆÁÅJþžG_°èg•…üVÿÞØí–dº0ùô¡“ë|ˆ×ä·w(zgLà°É§†ŠÏ-¶¾`þîN=àz³9רÄ8º½d‡j;'XHånqE³'S¯ ¥Ëï¹Ø’äÏ9BFÑ'·*<ïUs˜è£Çü™ßiâù¡ù¼¾|ðf~³YòÀ0ŽL~èé”ä8ú;7}Üi!MšíjÜ/ßÏ« 6'‡õÏ㣉ü©3sçØß,?—ù03;×ü$Ɖ‘WÍõ8ŽZº¬œãsÝBšÊzè—=O¦ÅÆNk×zjc²hÓ™ãï EÑ¡ª\¹ ùUÞ8óSdþƒÎúÇuÛõ*æs+_< Л®*h%ïiV&¼N¦ª?êØ\y¨8”?=RÌÿdõÉ_§˜cÊ?‡îx»ú:Ûp}~¹O.xJ+)§‹{ÿæ]2å}÷‘??¥Ü;‘I7Œà*~£lžÉÆáïÃ!q>Êü€ùÿÝGþ¹qû³‡ÿXÑçœÝo–×ÁÖ¡ÙsèœzÀ8B/=ø›eÎþ3V²NÿdëvŸy¿Ö oæG蚟©Å8µ‰ßªêîñtTÊ£ßj¾µ’ %‡®[R5…HÎ×äÄú¤ù¼ú=O÷Œ¤£g»²{×xQßì9È÷½#¢?9›Ïñzás¹tçÈÜÃû8Êç]#åÏGýyÚ+…êÖ®¬~àXr`Ǥ³5¼"Å\^Öï˜/,¿®>!Ö/[ºÞÆ øtÔ;ÐG÷=¼4×Ȳ:ËFÎë‘B£ïel¾ªïKø\¾HºtÇäú_£){°ïõsöœb>¬ÞºÁ8AÉWüí…É?¶éškäsz³ñ'ƤPÕ¬6kȨ /¦ùGàùTèמ¶á£)óf×aþñ¼_ÿ3!·§fKWoO^I¬£ûäSÞL¹F4ïV>>;…Žíÿ|ùŠû=IõS¿ïìs>R˜g¡ìúL?l=Ç>ÿ}UÉ’kév"UùlÂñ†mtqtVùßnx¸Nú¸etZ¿:…ž¹2òa±¸®¤Éü–˜‘GÒ>TOñ+úN³ùgö¼Vþy^%Ëïq+ú³¾Àú¡êô)‹‹½ëŸñ9pZŒÓ¡`ΚóJâ9¬ôm1uíury[gLQSèè[CÆu¯UŸ´mÕÿÏíÆHÚwÞÖ¨ ‰cÅþÆê­·˜i…z“Çm®Y1Ëþã”õÝfý½SÔ-8WžÛ箓Æ÷}´Ý¦P~~X‹ôúÔ|¤!’^i¹ÆÏQ{œXoLG¬ؼ˜õ×:0`œ^ÝŽ¿¾ãíØtð“ã×I‰–ÝßgI¡Ï4E-hD¦É&=RÿIó›Q<øá8ñy–}ýãêËnÂu+¿Íoö=E›;Î 2qˆû§'ShF_ÓàêjBÊß­À%…Š}†ÍKÙ÷ÅæõlÁÖE®~Ý6Œs)z»nÿ—“´iõ·‰Þ½oźíBϧyo­ÉÚC½zöˆ¤ücÜï«ù óÑeùi,Èuÿæ*OÌ븤Ùö“ôu“Íî9æÝ Òc7Ö6§ÐŽÄþÄ\¿=9ðyµÛGŒ³kL…—«£ÆSÖ/Y3=²õówçÉ0ÎÒE)¿ô:I!ÊYgÝ ‡¤—‹»—B#Ÿ-à ¹‰)ü×â~C"é±e“îÎ;î«ç›Çðëà×b¨kN·ã8ãaФ½¶<©ýà9U´°|ýËz~«içmÚ…Ð6Õ;îJÏì—ì{guÅòºÀõ¢Oït“^6Ñþ¬(q“,ÝdIí–ÓL5>Ž=Õ$¤Fð9EÛHz°ÝxãÒ›¿Q¶oÉú¯‹dñ9éÔ®Ëç*šh×>kl“þÌͰ͔ç+ø4½+Y[ë©_.’à–A¿Žþê9ÂüØù|¦Wb½ºæÉ0Îè Æ²»•&êž‹KB¾I~ÖÞxÒ·¢™vïRxD.ŸNdïˆwG ~‰ ÷ëûÍðnŒ8/b~¿,éÿ|„­ÛºÀ8|î¥)Þå–– ¿IŠwîýÁ£®™ÖëUfeJ{òóÑçÒò"èô ÇçGM'Ö+Ë¥dù&,OåÃ9õ€ëלØ*g[Jc£ÖÈsë&y=hôö3^fÚæÙÔ;[ât©v¤9 Ê /®£Ø~{Þ²ûÀò±YßuêÁ”ª }“ü8*ê=¸å’ÿ¸b·Èb8c3UNz´ß¨$e§r3çH:bñd}Û‰~â¾9«Sæ[Ìÿ÷gâüËõy(Ã8UÚ^®Tõõù 99¾õ-²Ôg’g`G3]xªÎ‚ò òñâŸòµŠ¤'ž˜Õp˜¿ØGX½²u![ﲜ„,¹Åçõ/“ã‡ÓÚ“¸Žu‹¬ÝÒ{b73•t0mq±‰Ž¨Uà¨<’®óÓ¤ÜMÄ÷¬~Y¾óãæ÷Ëkˆ÷Í©ŒÓ®iXgÝã´TŸeW­¿E Ä^»1±·™&)?ïäÇFäúMîèŸEϧ±ÎØ|…å1°¼±5¹¸€‹†Yrºt§óo¾¹bs§îŽŸ2+þ)›÷UŸ„fú1­ÅØ ½ Ä]æ©1‚îì?A?à Î#XN"›³:fzrê×çÞZ”XpŒÞ>3²]äó[¤È‘UãÏŽ0 Ï6$RË%µGЦWKI'õ ¤ìyÍþfyì9ÂÞ3dÉùÆ8¯Î-°?v;Fy›øÛä§V“*´ 4Ó)·Ô/Ù…ø— 9psmuÆ®4 új½ÅòÌX..«c×¾kÃ8Žº¯õWÒý•¸ ÉÛdúj¡AsÍtRÌó¸–­zƒ¾MÁf‹"hhzäªü4è«ýz~>|FÌ5c>ü®ë:·“©Ê·Ó·–kòñíSícw2þ69ûû¹Ý“×›iÍ‘5>¯¼Ü‡h4~Ä63‚Ö:òf]ùâAâýg}žõw×ý®[xfå™yסʭA©ƒ—ß&…¶y®4î3Ó®{S¬ÔiÈþ }yðêè}{Äy Ó Û`y:ßš×+1ÎÈ¿ŽöñzZ*é–Yè'jr}y¯"ãêGRÞÞŸ²¾’½¾Ø~#[Ï»>×ugüŒ:¥ë—?L·‹˜Sýõm’T»Y‚–”î=’ êÕ-[g±}!§pÝç¥_o?DÑ\Ïï' 뇤寜¯q†™²|y™Çâ°k"hŠìâÓAÏ¿ª'>wñº8¿Í’#y*UyT>Ù6,ö Ý6fÙþ2Óî¼ëÏXëw¿ Ó·ø\ -¶ö°éA-ýñÓЃÃÅù-ÛÿÉþނ培®ÛdÇ­w»%AyÒö&Kûm¸C:_ª»rà{3][`õÒöU!¹ïÙ®ÖÃçàûOðWóuöü`õÊŸ-7ãûµûÆhËÚy '¸Cfnþôyög3­ÝkŸô÷kÈ9‡®Ê“tɲ'E~û=H|N±ý2– õ­<(-®ÿéfâÊ Ó÷ÓDzÝùVܽCr~ ziÍc¡9¾<¿׎ÑÚ¨Ü ã"èqÿîaéŠ@ÊòŒÙýf÷ƒß‡µ‰ï]ó‚t§õ²gŠßßGWt!˜ËF>-o=YYØBO5ºº«~Lk² ω-ýwGÐ>Þ.5¶W€˜sÍÞÛ³}ö¾Š×IÅ,ù7Œ3|׿äA>û臾µÚ¼,a#}×z]û¥´…þT`výÅÕÚ‘“ã¬åû"hÜ‹©y“×û‹ý–í×±÷l=gÙ÷êÔÆÉÅÅwDí¥#¯^µªšLÿ<%ízU zÖkäcª"í=z\l™Aý/´ë~5Îï«ý.vÿyÜëÙõ½’ ã,”ü¬O޽tgÎg“>7´‘±/·ç¸TÏB'}8>(ª‹Š”jf¯ªxAƒåëGùœ˜ Þ,/¦¥1g7¯³Ö•[\ªr§CUçKÅ=´bŽ­Ö·Æ÷T`¹Þ«……¾zéQñçÒ8´Nã]űÞìºëÄ?ñ9ξöÞ•åê°|¥,ïÙ1NÝôsÝtòXº'.ìuzwi¾zp±¶±cƶ{§[>ÿ:Rx/æ/®;Øü—àó%ÓžUbœBg_•R¡§Ë¬m÷Yk#“A*Z$äèá«ê‘Ãw6Ë«³÷™ýŠícñïa^й7l½æú^\‹qî^ÜýÐíd4uþ¿üld™µ½µhg õნˆbtÚ _"…sÙö¹dâ{dþû|ê5›Ï£Ñaœ¢Ö?fjEM<‡nÒÙÈý&Í$i,lŽjC_ÿZâS½29£~·9A_õI¶ïÀö³ÙûÝ,û\§vìYŸÐCˤ¢5·.³‘ú—.ûæÅ8;Ë* .|[Žî-ts¦úeå͵u,®ÛØßl¾Íæu«¬ŽßZ^VÌ3uêãÜ[|±ò ÏpêÜ_Ýj#MÊÔ¬w¹ƒ…–~Tõ@Þíåé¡¶\ÇŒ ~ဋß;ÂæAl¾Íç×É¿Žà÷QlG6ã×n›îï CʼÞdŸ´ž¢ŽjÝÞ"œÿysâÍÆó¿v=A½;Œ¥¯S¶^guËž[¼þoy³>Ä߯Oü>q|ª²úññSŸ®ÝN#n—¶zœ´‘ ‹jÖY‚ÏÃ×iuÒvm¡úç2û?ë»ìó°>Ãò–Øû×\@Æ)ø¸ÿà¾ÛèôÓ¥ë\¶‘5ÚêQÖ.·n|j //âþp$!äš‹óT¦Wv×=ÏG¸Þ%ÆÙ2pîÊM=· ç ldÆÛÊm†k,4}ö… w²6d•q‰ÿéÓtfÏ6Ýñ½ »O,ÿŒ»aû*®÷G‹q©æÍ󜾅^]ú{‡m¤y¯ ëùGX¨JV¼C‰)]H€sAA;Šl}4AÜ÷fŸ‡½ç`û\Yó‘„}bŒã³pÌ£›©¤B‡ý÷_ØHÙ_*Ä ¶ÐwWŽôI¿Ö¼ý×Î/Ñô·cA+¤¹'ˆçWØ}bûÑlÝÊÎw¹æ9âÙy5½ðfØŠ6R4ul¥ÔùcûÇ/=È•½Ï*üt%‚æ«ÖdAt£ Q?lžÁÿÜwż¨,ïáqý:GjUk¼‰v•” ÊŸÏNDÔØ`¡ïŒ2Ðt'wÄ\Œ¾Öÿ’×½wÄë³\²CK«É—_x×·5DZ<õ³œS³áú½ôíî>Hýƒ:ch‹ØÉ’Ua£ªî²Ðò­:ªØ‘ÈÔùh›I™îç ç ÛOcó7~|,ë-·Ó©ÊM1Ï4cV¬£}m5¼)m'¦ÔÇ*²:'äâš_´6DÒJóµÝs3sÝÈž,/ŽÝw¶~tÝ”aœ©Î û0:>%8|G;Ù”ÜY:û¸…f ÞPjßÚ¤|‰/}æFRgy¼ lŸ™Õmïm.#ò,ó %®{kåË~ùo­¢£×ßõïVÛNˆOߘ±& }³$ÿ£ü^2ÒKÚ_ Æ:Þ=°}Ê„@ÊtÎꉽ'-‘êÙ‹oîÓi1NÓç_îÍÉ¿‚v*½5¸g#;©uõ×YOY„\½*ä"çKÌ Ë¨?ßQ¦/öœbûçl=ÏêÊ5÷P‡qÖUâîÀRúÉW1bœ·üq^SÙÿ´…ö”‹‡Ç{’NŠ×% ? SÞm¨ÖàöÊž·ìÜ%›±õu–u<®~tÕ 5ñóiý<£ÿèÜÎNò|þöôy<÷›åÙÝ)FAJÎX¼EZkŸa ›HÙÏÏêŸìÿËXŸ‡º{bþT}`œö…[G­h0‡nÍç·f;Y°äþË W-tn¡Hß|‡”dkôNð}ñyž(ëól•~%‰ó0öÜϲŒqFŽqŒœa˜J5=úŸÐÍNÊ[YtÃBë¯+.—¹šð¨Dב”ÏÏ\dzy{ÿÎÖì<œSgR•VwÙåGÏn £éa'/\íré…?°Ôï@Dò¶ =lD×Hê¯ä«ÀÖâ¾û>øyþ1çÌ©\·à£Ò#}›w£‡^T÷(ÓÛNVïo‘25ÍBoõÕ6L¬Ø†¶Þâ÷«e9'bÀ8#OÆWh³a>9ؾͰ§ãíd×ð1‹w´Rþ>U$³éà‹«£#éì÷«›,H³¯ÙþªdÙw0áú ­É“z~\*äÍÛɶJ¹×(d¥ü|¦ÝVú픡賛‚¯ÎÕ"H¼ì:üzý¼xÞ­³\çÛ6ŒÓrˆzÇ•ûËI»æ×.-šn'cOôÿíÏŸ¬´r™Ïóuï<èóðz.¤5%Þ ¢lŸÕ+{nðýï/1×õ|˜ÛÙTe…ŽŽN¬">ã_¤mœm' טºçÌg¥½Ó+Ê]hL£ï›8ãÈ$¡›‡å þj?˜ÍØü”åóódþþË0ÎS¯ÿn #öÕK·m›g'³ý¹£²›•ú¬RÇØÕœ:nÇU=‹ïmE\õ *ÏÌõ<ëßYs&­üy`\÷a¥Åö úƒ á¶-çÛI'ñÊúúâäuÄXoºò÷Òs«ì‹¤½v„Í6l ÷ØsƒsdûN]àºü²Á@nšKn\³ÀNN½¹´¥ì ÌÛçúšÑ®$|añó‘T_äÔÖÅ•'ˆõšõç½#œK÷\ß³ê0N‘RÜq3Éh´<·ãÏœiÞû—…zxÔʹ«@úWŸú÷­‘´âÊtÿ†í&ˆÏmöý0ݱsÒ¬ï±óN]`n·5ÿï[IY-§@;©±$üMåÇÊÏ_r¹B$Ýʽx,¾ge÷™Í XަS¸®qw®G‹äÛÉD,£Üðó?L:Q|ÒS Õ¸Ÿß#½>)<¼Å÷#©ªÑ“ôx¯à¯æµ·=ËÄTˆNöÝ߉Ï6/uêã¯rðh>Çòêü›ƒk1ŽïàäöE^ãç7œº³€Ù[&¹-ü)ìû‰çÙ÷Åîó³Q£æÅ¼)¾OÈrî7!UYzA³Û÷…“Õ{ úG/´KŽÑž¾-´ŸAq£I÷–¤%÷ëÒ(áÜ\ 8¯e}ˆé›½ÿtÖ?®»qJá5e'F’¸Yr^d'«ÍžÛJ *™ÏÞŒ<î7eS$Ö£ü~‚ÿWóþ½A’ø¾“Õ“S¸~tTÎÒæÑ¤\èœiÍp;ìþ|J+-ù öÞŒpÂçIGÑ™ÿO6ø‹Ïi6{/Èëö¥°¯‘5gW‹qÔ꽟å‰!黣Ö˜k'-æŸð— /õ4'X_¥P_ÑaІ(úôЄ-Uªˆë2¦ ?õÙì ©âyr>/Ô›L[ÞlL¥üû0Æiœk@hí »IÉ›…C´:;ñÎv^ë–±#u_íÓ¯íã«Å}&þÜtyaŸ&]èKÞ'¶Õë×®Wñ÷?œ:Á8æòo[帱‡d48Öw6žÅúMñA?‰p¾gÙ¡íèÆ|G==E/÷Ú½§cæ:†õ6/äïÛ;qßÉuÿĆqú¬,:«×›½Äjîú¥o ”Xô®ãC Í2òÖºË>ôìœ°Ž‘çhÚìWÂÅygÖuÙyñÜ';‡ïº¯év.UÙ‚‹5/½Ÿ¼ðàfìv²ó§‚ûWß²PÛ‘¡mC¦(éAn—"6ŠŽÓ¿X×'x‚8fã±ýS¶ßø¬ÿ®bkîUÍšŽqÊ&[¢<@¼ïî£ñ³“=—¦ßc¶PÓö]X7¢¯Kl+µpG ѺÙ[ÏŸ@Ù¹ö½°u [‡°÷#®ûAJŒ“/ß”b­’–ž\¿=ó¸ѱ—1Ï=º¬|Í û«Ò3ÝÏï·.Š.šsðŽþÁ_}¶ÂrÝ¿µ¢Å8mœ5‘V“”­„qÊM×¼Odëât¼û®c—D}µžÍ~¾‚_¾ÏåºöKÆiçõ8x¡‘´óÊU¹îh;¹W°pÔüËðëæü‡ÓóÐôÀ/÷-‹¢[3—ž\(îk³}Zö^thi»êÃäwâº3ËûwŒ#[¿ÝLJÉà}{²“ešN»d¡¿6¤nk)jéØ#·âx]|uûŒö™ý’Õ5ûÞØù_V×Yæ]gë#îü²”;ž=ÔNhéÞ;_°Ð³áµÃ:פ;ÝÒ¨¥Ñâ<›õ5ÖwX_fïßÙï…¸®omÇ·ÕŠøåGIé“[Âó¬dnÃú³ÚÀo}îó9ÓbÃs÷TÌŠ¦¤Âˆø<†Ì>š½O³ÏÃ×[í,ß›ÛùT%w:Q1êi:»èHÌçkè=¤Ö»I§st=àO(=ºªª×…hš«Õ_ö >þb°}€jÊð‡—š¿î‹aûÃNÝàú¹ƒÏo-3ä8ù¥¶òÆoX—„„%²PÅ‚|ÏóÖòæÑtMùÎo†μ>û¾ØûpÖ?=z6ùeÆ„¿uêãô\eÿƒ'ˆ3¯¹;îÿ˜á….EYè˜9 KùôkO3¾øŸšs%šÖÓžëUkz 8ÿbÏe6¯`ïÅùõ}ƒ,çÙµg\=Ò¤áJVœñÅ ×N‹/±ÉBƒRÖ¸õ)Ô™n¨-Û_þD4½õ×¶µž‰ùálþÂÞ'³yþ9Q_Üwêãì\ÕJ¶Fa"“r7é~Pe'íb/¬j¡1Md–ïB¯.w·ÂÁhz-$öô¸MÁ¢nØ÷Æò£Ùû?~_Ø#KŸ6`œÐŽÜ/\™ÈÖò[®”óAÿœ:lØaEø½¾Îô£b]åš4š«<ìÀ»Á⺑õ¶nwê×+¸ôS­Õ•N’âÁËõ¶“•ï÷îïh¡3SžÜ,œÔž^U$??eަ'Ï5öË÷øë} ¦ö\f}%Ëï)bœWdžëO’s/®jÙÈNþ˜V÷òÊQÚ~QÏ?/=õ¡Îé“#šþÕ8ü“¹K¨vß_Nßû±>àÔÅö{>'IÐ_ZÈåvâ^×ûhýAZîÙt¿µuåNË×¼McûÖ)³áU øübû ì½Lè³z[>Ç¿׉l^àÔƹ۬Þ_§Õ§÷[ÃJÙÉ›´©«úX¨Wñ®óh@åÏïù.‰Ž¦)OŸÔ,|5sÈê…=Oøs¡Ÿùõ®Ëï"›4;ϧ´“–Ê‘Ý=,ôÀ„Ƥ”Œ¾:v~4-É=¶öŠól¶ÀÏç/{®w­Nå¨â9”,¿w…qún›?QW=Žüjß[`};yõjÕÛcÝ,t–~Ò©´<Ð`k…B;£iº£Pú²þâ:”ÇÎ#ðŸãµø¼r½ß:ŒSºÞ¾ËÃVÆ‘U)A-®¼±Û³nUr«1W©]PŽs¾ÞE¼ýÓ7*à«ç›O0}³u¨ëï¥0ÎñSÜÄ8ž½©ºV)ÕFø ^X½ž#-g‡÷>Ò˜iXóƼc»è¤s_î?¿í/ÞwvV¿lߌõÛ,ïÝ1ΑM=B7-Ž'šÝØÚä¦,ÙÜýFsô÷©½BìÒÖÔâÙúÞ|Y mØ®÷ôR‹2ÇaëOv~ƒ=Ç\Ï·Ûpý±þ‹/ÿOjrÓ¢+6òrvDÙö m¹29&fwZêÑøVsÆÐ/¾ÅñϲïÝþó\ßðó0~¾âv1U9Ñê3²ŽßiÒ QþÛ¡gläþ™{šK5,´u»!—½îBóKÞ.³‹N[5õIueﳞӾ-ÎWØû×}eÆé}°ð¤ )§ÉêË/›ÚHÛ™k;¬+m¡}¶¸U·WWúSëû¯ßLÝE«×­Ÿƒ¶eûN…çjyÂÞó²sì÷JØ>‚S7ç­¤QëHùÒaê³ÜvÛHù‚¿Ïiú“…nj•Iµ€NôÁ”¶•ÎÚ%ž‡È~žƒÝ¦KöÝõù®Å8W#jnz0é ñéüú|«xåxy~Ý'3=°cøÊSíè´ÊGF ܺK87¹ÏÌþfûPl¿œ­WÙ}têãx¥N»7úòR¶`°Æ¾ÚFÊyL3ó•™ptÈ9CI#æ{c±‹æÔ »¼ñBfŸdçyØ{%¾þäbøáéôÃÓIçöÏðtr>ë·|PLBöÄ·<‡õ.¹bÙ³'X6,—-Æ|êÔ(d£àUççâWçš/f|6u‚ç0ç[g2»¤ ŠÞTäÛØœ'Š>[¶»?Á<‡¿•Aa„Â|‡9ï:–EÁ|‡YΘ-›Ï&ó_wõÙtÍÝù;þë®>›~‚ϦÒÅgÓ×Åg3LðÙüVîŽòoäî…LŠÁ×I)x»gËÀŽÍæ½îêéô½< æéd<¾å½®ýŽ÷úžø£'êÜþ=Q"|–Dîž¡ @‚¢ô<;åßñì uÉãÉîÃÎòe.ÞP²éþP®y<&ÁëŽËãI<¢L@Žb@‹¢OøN~¬"Í–+|Ø™Ÿç·|سûy*]¼Ø™Ÿ'ËçqdóºcÞî^w®™Ç{ØÕë.DðºS¹xÝù¹xÝA~+“Bõ72)L‚;—Çhrz8¯Oi¶üXc6ßa×<íïy±³Líp!Sû[¾Ã¾ßñæj•ûóÿs?üïê…ÿÕ>Èz ëÿ¤Þ÷½¾÷½ž÷½~ÇÕÉ·<=3„ÌÅoeïp™‹,+;{æ¢NÈÊV»x¬ëAÚ7<Ö]³²Ó€ZÈ\” ëi@ƒÐä(æPQäÛù²œ§§É%c6MðX7¸dï|+sÑ5wÇÏ%o‘åî°œli6?<ηØÕåMü]ÏbÎÏQ÷Âs|ð‚ |A¢àƒ÷_ɘлä+ÆÖãs±YÎŽ*[άÃÅËXqêAZc>û[¹Šî¬(¿áeüÃÇøÇüîŸ>¿“ ?«•»'(ÈpàŽ¢ ¼?=¿ãýæ’³“Ý“å-r9;¡ ƒË˜@!'ŠY@-g‡ËÔ– 9;œ¨…žî±€ûM2ák“ûó½žøï쇸mÿ1ó¼=ðëøÒÿ¸{ÜQx!ÀT(ÀØœ|†â·2{¸ E–­=C1TÈÖæ2€' 6L(Z 0Y¶\m®ˆµB~"—×&´/H ¶á;Y²¾Ùrd¹‚×Ù‰,«ç{Ù‰®Y=:—ìD–ÕÃ2µõIP£ÏL†i²¬9:àjˆÈd’¤ erˆ*¤5Äe2,Ø€ B î[°¥wÁ e^Ȳe^˜€´nfö…RÈÓf¹‰¦z|ž6ËéÑdË’ÍZ7xrye‚€Y–vöÌD­ †°Ã;ÄlB.F8p‡ÐC€ ¨Ÿ‹ñcþ÷cþ§sûgÌÿÂXî;EA E©6 Dq†wh° 5Ü%ëÇ ”(Úpà.d0rY? °HPÄ~À £È„lm‡K¶¶‚ôVà a† âä²Ë€'DÒ€ bRÖXRÈläòµµpðÌ–#›´H²ek‡ ÇXBÿF¶¶8€â7ºäø8€À8:ýžø£'ºýsz"wbŨ FQ]²­@‰ î(Ò!Ë‘ekgÏr Š—ËrL ±H¸>L@Ž‚ÖgËÖ– ¸}…,GŠÜ$(t?`J|øwòj%€_¶¼Z Ä ²e…þ“娆PŒ@±è]²c´\f¶¶ @!™€\†@ Q ÂÒƒ4 ÀL@‘…‚  …Ø€'2€Â39ħ †cBÔPA á2A‚‹ 2¸Þ‘&„l@%dk³,Ç„z|¶v(ÈZ—<Û0A̾ (¸Ì4 qÉÕΞãÈåjË!v=Hˆ>H!|p@ì7òÑbIæÔÿ©=ñG?üÏè‡*áZiÜw†b49 28€ … ¤(Np5Š4¸£PC€ ¨P°±@*äAZ’Ëéî(à` r˜P̾Àd(j½Û­Dq‡wx° =HQì:àøN¶­ ˆuɶU@¡ ƒë‹„ È!ŠP´GC ¡ h!#A,zTM¬ßí  Dä b @ Q%O+L—/H ˆÌ$šŸß­€àÂ\²!M@.äw§¹äwË Ä` Ò$¥/H ˆÓ2€"5„l@%äFr¢õ‰@‘-×Ö ”r8poœ™ó­BŒîw°Dû¬ïì9rˆ_Ò€MÀäh¡„¯kîÏ·zá¥~¯fßüïìw¬×ý>çÚã¸þöOèm\_û^Oc½Œõ±ç߿һ¸ïÉÈÝOޤ Èl@…BŠR“Øòdfr»£¸B€{¿!äqû+P¢ØÂ¹s,(8O— n?(QxáÀÅl@…"ŒýNn­;úRH¶ÜZŠTŽ"Mª ÈQ¬¡ hP´& G?r5z‘±,Ÿ³í‰BäZwÏP4¡ hQœÌ—×տņë?Ø¿óþֳĭGÞŸ}µ‘RžËç]~d¦ å¶T\šzI¼ú¥,¡×´ö*—*~•WÉüXîór~Q—R•åõµ®Æ7K ùÖ,izYc#œå™ þë´F•µ)ÍnÆÐ˜uôS׈¾˜,oåWñþ ¢±kŽ› ã4ûå|‡ DãUacU•,‘­Úîž™žÚQc`¯nÕ©GŸ5 3bèí¦+7{ìÈô½`¾9̘÷|%æ¸úg)1NbþͳëØÈÇ&u»Lmd# ÇkvÜ1Ó-GgÌûô®uÚ6?Ž¡ýFM< 2Ó?‹ù¹°zpõ5Ô⺼¿ÿ9ò{ùz“ÇU´‘ý·ºŒézÝL/žZÜÁ0±)͈ðhè~!†ò¾”_ù¶0ßMækÅò¸ëëpý]¿µo×4ìáÒd‹¶‘èË Ï]¹j¦G†èRûPÒ{Ý<ìùÇÐǽ[÷`¯?e~U,ße[Ï—{«UË’WoÀu‡,)O¥ž#ÇB.<äã²âØœžëÏ™©=íVËvy}(ç ^ÁC qS'åxë/Ö§˜Céô¡·ˆyšÌ¿Ð5_„q'¹÷Êhpžì®Öµog˲gòßc'ÌôÛtËžþmhl“zUªÛM¦* ~ üÊ—›ùºúnÚpÝ…‘ƒS§†œ'Uçßs‡pî†å÷›©QêVe¾íå4þÝM-wŠêftþN~ÌmÑßù"ºú˜¸ý™ª,m›5¦ï‰ódÝ“M;s̾C~¢Õ3ÒLÛäXÖpœ¬55Û·î¦/^wèû»bÂW:c>Óü}y+ú™¸úLË0Žß4ÍøäÈ3În©Ç’:¸I‘-›Ítã›;M;WÒw—…LòÚMZ5hýâà¯ü+³ç» ¶&ü¶G½)1Ž¢ó¦r~/¢ÛŸü¥Ú²¨c¯ëw×™©JýQh©O/ßl}*ýs -å·zùƒ*™}‰å ò~Y!7³!a¹N=àúN}¸@ôz;°ÈÇÛd٫ϤK˜™ú)Z4í¹¡0uú½]ý$Ù÷Íî/ïO÷Êûд¥ïÿ*ÑX¸_|™×7U¢Óõ….’û×ô¯`¾M~žºçkÍ´È›‹7w­Dôu‹µQÝŠ¡©¶É˜þ™??ób}Õí¤Ë3Hjdñã1`œ%šl?Öû"IœQxÐÑý·ÉXN¦˜iúüð:s£Á ¯˜Žþ½>rÊðÀL¿^æ»Æ|’x<r×åY|ÌMç‚;—´w‘vŒªè¿ô6Q÷¸`~¿ÅLÓ/ö}¡ ¦ÊœóÜn1çù¹íkã4šsïXNËísõ³aœ ³GµZ‘\,Xfħ‘·ÉãÇKN7Ž0ÓŠ[ÿ{ßÕÖ¬‹A3F0€(PQ LŒ ¢¢`D娘Ì(¨càˆ Ì˜1.DrPgF0ŒŠ * *¢òzÏžµ™ô¿ïÞªÿ¾ÿÕ9U]VÏÙ½Cݽz­ù¾ñ)©ýAÞ«Üçì`!ŸC.e©ÔûP×éMSò£•p:ò“}´•>Ü;SZº)†•¥žî±KBžxêîzúÕŠ¯~ÃŽZDX¾>oBõ*)<›w‹x-ŸÛ•.n?¨Ž'xý:Wýº2Q y„0íZ|}“„ô¯LoPÞ02ø—vÝZ}D„Y½Ù†{WÓa ¼ö,> •ú'}€êB(ð‚~ÜǶÏ-vƒ(ðFŸ°þ9ÐøÁÝdµ„Øu7=,uAŽ5ós50‘±>µz÷]\©—YU§šê¹°|Û½ÔyaÑÏ¡Ÿd{‰¡Eí±{äÀû=f–IHAÈ‚õ+¶Ž Ëk5nd("‹¥Œzûc9Ç£Êòÿ6ƒåö~þØù³RÏ­œÇ~@yêxA?»œ'ˆ·Š¡[ç^»²²áû¥éG5\%ä|p)h8ŒxMgÑE$õaßám|ªéÈÓ|ù¤âÚÓ›¿s¼¤jëôÃö‹bø±ÃbfîñlxÑò¥õûÙbå0q£›%qSÜ–ˆx?b{kyµuí¨n²ù¬m œMÔú¡hôèaÛ…ŠáÐôM.¦®ÙÐܷ뉎%ÿ|‚ßRœÕBDâ<´L[,¯ÆsLù\×]è±¢çÐ.ý‡»®™?°ýÔ4]°éº|¦j0Ï£"›Ï’ïÍ ãŸúõ…î'v'4‘ð]›ÍÞPÉ·H× ´¾(ø…³º©ñõjdåó»Æl9;-Y ;ö—·ßQ;œƒf '!ï;<ži‘1Þ¥Zuùë“°ú±Þ\½¤u’æcÚ7ªÖ/C¼~c€‰Òl1C>|Öàác®}äØH‰R‡ÌÆ:Î|Õ÷±èUˆB­+ybi¤ºÏ´¾P]f6)uŒÑÏë]|ƒk§Å¾«CƒìÓvŸóIˆ‘[Áâµ—lÀÛ,Xãþg!¹ZQKr~–wÝTNw”êÔ°ú]Aëö~Û=K”ëôóôpßœßÅ ;þq~ŸÇðfGHFòVô3j³`zâ0˜×YOøãøä†ûÚ&yWãÕ§ÏCukÒ½óG?“˜eù/1ôgº‹¡ÁÃcÉá»%¤èõ× Ÿ.YÀ›Mï~ "c˜Õ€žwµø¢úz,.ßרã'@?‚ÖLÁ|™ýléhÝÇpG÷Môí½2µá­´è%M¡n¢øâÆ1"Âæ©eœí3ªê‘Ö¨¯‡~²|›¶§\ . µŽIe2ÈäM-ÌÁþ¯ûŒæ™Z ;‘Omçø--"vø²=-#´žÐ¾›Í Fjëk9^wƘCûíð{hÄ÷uÍ;!ƒâvÖ.–˜ïlyA2s²#©­Ó›Á"Ò=1cTdð²j}åi§zz4¾h_«ÀɃ|þ›}ç ŒKÅ€IãÀÍy2˜c0¬–?ö•ËËâMêGÜÿý1¹—ˆüØýâ≸eÕúJªCJõ•¾ø´þõ@§‹Ú¼Ãý0MF`‰®wßþac4Énå3 ë‹Ñ8—¯'›“ÛðìÂTc¹´`^à-ÍÊy½_ªsGùº©N‡ªÞ)ýø .OÞøU ãÝ~ö¬U*…¼œ ¡7ý%Äñ /ÿÄàv„a£Ý‡yC³mS‰{Z_¨!Õ¬‰g× ýt èW¤[(†‰_£œâ¤PV¯Þ§âu®{ZB&Ü‹ÛѦ/Uã˜òSܨòÀûãõ+Ò°ÃÃÅ3OÖìÞ#…N¡‡èbߪëåò+ZÛŠM5Îh*RÎ+ã—ú¡ºE´.Sœ°zŒõXœ Ÿ\ó:‹>‰¡CÀ’I™Rèÿr¿zÆ}rw§%l`Ú*=ùܪëÝl/Bùt©® ©~‡h¼®Ôν÷)¼ûÝI/u–Âã+y â\ßgRݧu:íÛ«ˆ¼ÛçµsÞÏjzt]Mó<ÕSÓùF?ús®$hçc?ÁÈÍHà^ÿ­z?HÈš¶ ê,¯kg÷/öh$"¹c¬´‡­òàò½_úþ)ÿûn¦ÜLî­¦;¬ñ0Ÿ>±qfž4ó»-Û%û^ÇÄG$„å-7ƒ…N¹óÇcY[°£íõ Ng†Î³(ï;û]Šy¥^ “ô@e>iÉâäaåó°uÑÕ­èò¾Å­NÇ$$»®þ!xn %ZÚ ‘õÂ<É¢Õž„êÐû¥zÜw÷íqæÒ[^Å‹îT¯Qôƒ‹ê±oÅð™É`°r¯]~q¢}YœîÖ6TDÌK%"{&/ïr¸Â“Ó·¤ï¯j_ÌæßÎjzNè§$“!VCѱ b HwìõïqBB®ÎŽ™Ö}¶6²yæ¹…CE¤ùôE&W[UêÛÐ97ö=>çÝŒ;g¢¶žðG?Ìê~}®& öñíüm˜ë< ßÛÔ÷n=ê_éFºivxþÌ󥇵eO_//l½jÇážæeöúÔp/@?Ãß$ ²åbØ5lßô;÷Aæ‘Æs{b%y¯1ÐíC–ÎZ<£›ˆ$o_ÑbÒÚʹ6}ŠKª§JõõTõ-£ÑÅC“.GáºåÎyÔÎGð8sÿ.Œëoký܇°Ë61Í(ñUÖcÚÇÒë³y¬›‹¨âTŽ~re«·x¦Šá³<šøÊn­|„õ«ß¤1sÛô$“Î N½‰|Í”^˫͋©~¯Î¸fŽoÊxw™Û î®6ÇÕx”Ï·±Ý;<6M &þ.ó’Ú=‚tŸNí0°sš:DØét}"!ѩȯ¯u»R¿—æªC´!üìÔ9³ŒÔò€!^_«dÎÛ¿ñú›3Ã|~kæÎ½¡±GBzú߯„Ž9p'4×ùŸú)º¶¬š^í+)O¼lx@ ¬NÖ¸[ñ.5è©„À~‡ˆ³-ÁþUhp,ö-T?‹â˜öGTo‘îG°ë;vaˆ×¯È î§³C ¿œÇîù{õh(_PäòBBžuç}ÝoØ Üö´ÊzÑQDö™J<+ã–â¾/ªã§¦Ž×?$ê?ÁW Éö÷ÂÎLxÃßÙ|ý¥DÙ/dß ÙÙý~T[饨ðäôQh|QÜÑù6ûú­+ œ Ÿu§.9Ÿ/†’KW,:=€ E=<Âçðp5Ò¼TV‡œkÙ$.WGDd6M7™ <9 ÚÇS²øétÿA¼þ­6ãìÄ M)Ë‚IK÷ºïÆç ùÎÅMnmñAHZWð^Ŭ¯Œ_ŠCª+LyúÙuV753úé3ýɺyƒÄðýÊŽ_âÔ,84 ³4#WBüŒGúljo‹g?šÞ“ɇ÷¼Z°ðäüSœ°ûyÊ|U¤|®®pn|È“Ovì\'ý̯¢£ßE !gÊæÎ΂‘;§½“û»¯úw¶ÝÓ†¾ù ÉâüL{Û7Ü|šæ_º¦Ï¥®Âê¿ÉÑÏÐÅëtŬN‡[x)¼"ݲÃë—ñxvú•û:íçè¼Jôãm]‘rÕ5 žM]½¤ÿT1¬u лßWªÜ¨OJÖžn?àG$Ùd³¥c†'×ÿÒø¢ç¨ŽÛ›X?NèÇ?Ä̹ŸiìŽ`Ȉ!»dëÊd|gÁñcjÝû7"ÉçÌ—;'xr}#}ºoÊÎY~rºˆjû*èG>öÅÐËïSanQp̘zb¸7Îýö¥ÄÞqdvÇéƒa”¶øÚQŸH’TÛØóýÒ¥Ü>ÅGQ]D:ßW[Ÿ Ÿ#…cÞ>— ÎÛÙør\Œœ¿ëÑP)ùË®þÏÁ{Gó÷ÙÃ=gF’î{–n9rqµý`š_hœÑýµõ úIÌl7½l~*„èïÛ¿Ý1š•ߌñ²•’ûu®^µ…·S¬w/$Ú~Ù/„IîÜüƒâ“êVÒu ©æ9úÑ:×xbïT¸Â°g€pËj‘Ÿ³”ø´·õœš?˜¬'ˆ$]›¼˜Sw—·GãÎÁèz˜ý÷¦jsVÇù|ƒ^?ÐMÒ¸AÉwÓ!ÏoÚ„+7}3ÍÍÏNŒ í&§D’暃ãÎu­6G süsÓ§Ú5p}¡<`Ìí'(ðƒ~4ÎGy”¥@¯Wy®LÛ¨K' wJI~¸Óüµëì¡uàg?‹"ÉÍSôÂÏ/äê}/tß“æev~ÝYíýxUìÍç§À‡g;bëZ¤ƒâ?!UÆ•8´Ûõv]!©ó·sé6WîœÍ'µ½]FX¿UÎ ¿ñTuðúÅÇ“Ýl“R {¸½ÝÄâ4[?ç•öæ½Ø[ ÔÕ6vß›=7×o^áÔ;:Ø¿ß" tã_ýlv]JÞ/ñ7'Á^mNi­‹$_C¯C+7îœ ý.4ϰs“ržfZ¤ŸiÓ¾júŽrô3ì@o׎µRÀV!´— Kƒ)q¬(y}Rk2ÅöÁšý‘äÝt½ärKwnAדt^ÈîåúÕ}/ì|~æÅcf&$CþÍ‘#©0Ø…ç>ëŠTy>£Q“:IŠŠ>^ÕvQ•}‰ÆÜ>1Ûï—póiÕ¼iˆ~ö™,Ü” ÓÍŠ¬'.L ãØ™“ðyêuëWt„°³{¢ ÖF’)§§Õ9°Å›Ð}-n=±ÇõlÀ  ¸T=oÄG?s>Ü)=e• ‰G÷ýØ)Ú3 oHI瞯wé^o¦yû;E’½`›Ëçú•y†ö›l>.àôÅèß+ð‚×w]icÎ?•Œjm¯øÖ³[ϦIR²r‚î÷ž‡AîÝGƒ¼FæÔa³Y ¹ó4´®Ñx¶ôõ¼7w{7?lV¸¨K×ôÓ®>s"" >½æ ÔÒK™»§®Î’’°&#º4 ÊîFÙ‡öŒ$w¦[9:d>·£}8=Ç+}¸n^ou]d¼~úްí<ƒ$˜Uz¯ìáÅd˜:ARghŽ”„6®}xñ èÃ.¿/m# ÿ.¿yOi<º?A¿C^ÐOñIíwŽoÕ+M~×ôà‡/¤dƒ à¥õ(K8Ö„9©ì¸8¦ó¶.?äÖ÷ô9ÔôÃsòù#oiuƒs‰0ÆùÙ«qÍ’áíÑ(íyX_F2ŠåæpÚa©›E—HÂTË6ÅN\ý§ïÖö»”rûkª}¬!ú™3"ÇÜgZ"ÄoYZÒ 9 ØýÃJ¼Äjwšô ÖËm7óÝçV›çÑ8¦:Ât¤:á£c¯©#ÆM€{»ôÜ·" >4ØiúiÓ9ñ›Ÿ¾0*ƒ:DãA)=†]œ_íü'íËi£ý²ê\Á ý ¯ÛnØÁ£ ÍYÐ=¶st¿¿úÔ{\—…ü|b:AHCGå-|AÖ×3›/÷_Àåê¯ê>;WÇ¿?úYצ×2Û0^Ú"–/M„H£ºÞáóhÛëW|7õ¨ƒÜôfÉ(ë¶»ŽpAµúÏâæ%‡¾Töµ@û'nÐP$_]'>®4Ñúº=FÍ»¼v7ú‰;¨k~#ü'Ïå‰Î©«§"È‘õÉ¥ßZ/äÖÉ4îØ|&áÖMô¼½jÝŒF?²1y½¶_އ¬Ä·-l­ÁúÈý3‘øÞhÜÌ´|õ³]×tòÑÚ ¹uÕ‹¦¸aóÂgÞñF»÷M{«~NSŽ~Øý‚x0N8–uï{ì¾,»øQJ,Ýsé0Ü„ųš.Œ ‰Îfë6§Î'ô>ÙzÒh_Ææí ®OW=ÿ ñ$Ÿß= ¦×Žñ`ar´(1ε¿¢³ï«”,:òÚuÀÅèiËß[D3¹ïOç't]Nu@iߤº~^tcv c=?z«Rí/#í6Öõ«Ýj t\ñ·xâ†;õ:,™É­Ù¼¥Ïé/³ù¿Œ§ÚÊñún“´nÞ9s"CîôÍ>›jk4éá #Q‹[·x Êó΄݇œC輚ÆczœÆ±ê~»ÆSÄÿ5“ËCï.îZË7ÄCHûº¾ fÉ=_ÚàÒñk|"È™„OK¾_žCè:…ö3´?§çÒè|Fíw*èÇW!0õOü2%Æ{ VDÎÆ8Ny¦9ôgÁÝ™¯n:æAÈÈ®c#¾9ró ÚçÑ:M¿?ý½š,úaÏ7Ç@‘Íí!ÇLãáÎ6Çó+ñyæ–ŸÎtoJž?™cõÑ1‚4ë–öÆË«Ÿìû’ò¨ž8íÿTÏó8áõ ³Cu¶†ÞƒsÑŒv<èo¹S¯ÏÖÇ Þf-ÝÞÛl"ˆµ÷¼×‡ï9pñEÏ9³ó2nßHmý×÷¾];sŸ{Сlãú·Ïâàt?¾î'|O&îËïtî¥/J|;Fö¼ä n¿•®ëŽhÚ3ú}]8ܰÍîaµÖ1>ðú_O] ‹66ËFÄUÂ^ý se¤É‚.;EŸ-`é8[‡—µ"”ó±ÕtŸ§¾Þ¹lµI¹rߥй¢ê>U4úI«d^nh8ßÓ'Pº1–×óϸâ&#¡-˜ƒ…Vp8ÉsÈ ôcô!gôˆÒiÕú zž£ç8øÀë×éÜ{‘Ëzî‚2¼ƒ8^Rç”—ŒÄë4ì¶½ÿPh—±§u±ˆŠè9¤÷”j}Í‹´ÞÓõžêþ·Æ³|þ2»˜q9ä.4ë¡»p­EŒ±»à°ZF _©ÉE>Ô¾utÈ|¬'ì:e2—7W¦çÄØ:©ÜwA?.¤«äÜç;ðàëãZÒq0¯]¢ÁÅ 2R”pZË¢lÌý+èÎbÌ=œ—ì{n³‡Úù'ôÓl^]{Ë ·!â óÁcaäl?íÀ]2Âæ×PK.»ü¶O ²^49| ¡ñC×ç´ï{7th·7ºª÷cègEÿÜ­coÁIëÇób!kªÑÞ52®h‹«¨&æäÆâåé¶^ã¸õ}.eþá­Ù²¾T«Ýw*îèç˜î<««ßoÂıަ›ÅÂÛË·ÎF?ôõ0ëÝ–h;%G×êA¦¥?k½dÊ$.¯PüÐç8kòyÝ)rž_óHë«GÀ¥¹Ì¹,nÐϵõƒíι v±¶´…#+üfìÙ)#Æü°7iCú‘4á{íÿÚy›Oãò$Å —Qìß÷R›3ÈÑÏ Ý¯v9[nÀÛþ/f¹ ÇÄ­]¶ÈH*œX’m>ˆì9ûºAäçp2ðJ⊻'fpø¤ç‘èï€èïXTó°Æó|¾»™Î}ëØ(øt¾Aêð¿cùÞx™rŽ8‰W^¿ÎÕaZ·èóÐy ³9F·´tQ?ƒ~ÊÌßDå^Å6[`,4k8öÅ›U˜/­krŠïGî,Ö·4œX.x¼?úÍL®~Ñç¡û t¿šÎ³U÷Iøè'˜i닯Á€ïA5—Ƕ²ñ·gûÈȨS[ŒƘùFi¯%–áʹÿLîûÓx¦û‰ô\2ÍËj}ú)à*äu¸âàöúޱ øðmãe2’ô lÅv͈b¬Ð:œø•ÌüÛøž#—ÿéýÒߟ°óÍÊsªçÄýÑÏÇ‚C¹þ³®Â"æø¬},\9»ýÕ|ô£ßr߆Yu:BÛ/ƒ^ê„v_r:WÇhœÑþÕu¼U»¥™_¸ó6ª}²ý4p^úmÙ‰+ ‘Ÿ cû4x±ógKëK¾ €Ô7˜ & o­1 `Woh~£q@û%Ú—¨íË Ÿ‹O²Ümž\†a+žÅ/°Ž…«/WÜo²NFfuˆ¯ðÕe·_ž³nN?OÍšÊíó«Ï*ÏÁÑý?Õs rô“XpØvj£ËÐLje ƒ"üßnÜ!#£^XSh2çqßýº…“¯ýœ¿ºMáææt}ÇîkÜäÑõ]O±ß统)Ç÷f¢c¡5úhNq/á›Æ‚îJˆwë[~vÚ°¢ÑPríˆV‘M8©3ìý‹”¹<@㛿h®îÐ>ƒý~,~ÐÏ_6¥Ïž .‚Žâ±ð8i³Gý3Ø—½šTèaŠÏ3B1É"Ëä–-4¶!t¾§>ÊâæÙt :o⣟F¢=f•‰ Düw†n,¼—d²Y(#wÏë6  µM˜jáäÖáò\þ®îÐïCóÙÂn¡î•pûŒj¿C?‘ÎÌI)Ä”ŸŸöã>؆ìsé]Fö•/lÕ>ÛmÓsdá$ÉrÔ%ë°Ñ„=çÜŠ{¶OⱿ·)äÙ&ŸÙÒ_£­Z?ë~¶k=J‹{ ûfþþ½à>¬úê“áyCFÏ«Ámæ¿–#NOO º+µ#UçJôœ ý]/x]ߢµOÖ–…ÃRE#v†~ÚÕQ# ó™2^ô/]èô$œœÈ7Šïós—7éü‚Îþç¶ü‡cé?ƒcICùœ‰ê<%vJ Ê÷[Uû‹ê¼VÕþŠRr SŽ¥š8Ð-«p,ÉT´¿4þÀ÷+þƒöåXRÕÿ TÑÿRåû­‰KŽr™P.9%Ÿœ*ß/Ãi"Gã#€Â”¼&žh24¾a%¯œ+šͲ ¯Iâ¿àûÕPá5axå‚”dxMÑ̈AJ0º*õÍTôoªòŸkTÑSåú•Uá<©Ê5ÇÙIELU÷õwz`&*|sNøD43F÷U üš´¬î“rá?¹Ð_ã?j*ŸCÌ|O Æ e@:(¹Ï)gfUMªƒXU'ZÉõK5¯kâúåWѼ–«èâ0Üç¿ãÒ”ýA‡j^«jã©hãPNÍßñ:1 qUáuÚ¦äv¢Üš†Œ&Zš £ˆ¦‡ òE“£ÙVr‚/ Mè‰&Cã3\çJmV_¥6_E{¡*÷¯^ÊÃé–‡fƒÀ¡é#x}Ñäh6b‘’ÓSE‡FU«°&™’“S€¦‰`÷D“¡ñôahz¿Ñeõ6^™~— ÿyðw9ðwùïÚ›©æ¼ß廕ëþSóÜ¿#Ç1ßV€¦‰ÁåŠ&F³TêÊPMUÝiK º ßèN—¢Ùa0F¡ÖÀ¬‡Áé‹&G³QÑôýƒžÌïô¦Å5hM‹[¨ëÈüŽÇÜRèNJsªã€A¦ïfƒÁ/BÓÇ—äoXÉ]†¦‡`¡ñ•¼åÿJ7†á-CÓSò–ËÐ,84MÌYžh2¥Îj˜RC‹j6Tå –UÑ”VÕŒ¡é!è|Ñäh6U8ËÍŠžt"š2è7zÒT/†ò• Ð4°žh²?h¬þÓ¿ýÓ¿ùküï÷o&Êû3߃1 MÒ-Ís[ 4TK°ª £1m¢¢1m‡…fˆA¼ ­ÍNEcz›’Ѥ‰BÓÇàöGËC³Ã B3Ä@ߦäGÿ6MX Ú4Œ– ‚ÁMŒf‰  i"0<Ñdh|Hš‚ÄM†ÆG°)ã„–ˆf†À B+Es@E£"ˆ¶¡¢9â}¡é# üÑòÐìX"4}—?š CÓC ù¢ÉÑlp"4}?Zš ~š>ÐMŽfÃð¥+µUý•ÚY6*Ú ¾hr4¨H©5Mõkì¬Qh†Ømh…hvÜ(4C¯?Zš‚8 M_ɧNulTukÒ±‘£ñèahzv_49š ‚^„¦ÿ]U†#‰CæŸrá^.ü?ì tSåýÇy™¥¬u8È@áòGæE""­lô‰ˆ4@ÕL6ºÍ /2_fñe†Ó ¨ èVi” LƒNž+-%:À*›K@%b…È›ö¹É}BŠàÙÎqÿ—ýáœÏ9ðÜ4ù}~¹=m¿ßÿ”=h¾~UPÌއ8¬›d^‡M~Ç´ƒ­H‚ iÃ`C\ŸÕ]£ºU·t=”"så)º¥UwM H^ň^‰oèQ­<}?øzþ'Ý–Z×IšÏ7Ã9êAg0'éÅQ„'öã˜ÝÒö¼ni7 3ÄHƒ;¯[:i«+ÇáöC Ü ¹:ƒ€Ô7ôå„OÒ—cvÚa<ÄÀUPŒ'‚„Á†$>H€Y*¡a¼P¥ˆSiÉã(èH€4x4h凔ÙeˆXÐËIp"Ylˆæƒ$¸.Òù!.ä‹€†€>H‚ #V—ªßêòr!elˆéƒ$¸4bõJ«N7² #lÒàF\täõC ÜHl€†È¾¼~ü^Óõë$Á‰èa°!»’àBúˆÕ—s²UßéûÁù믧÷à·¿Íç? 6†°àd+­NC7Ci4mØ'íd@«NÑ']ÈÀz v×)s2Àh ±RàÎë4ô[†.;Ã퇸­NÃSõIW@â$}Ò «Sº ‘a<ÄÀUPŒ'‚TA1’ÔƒQ‚–,^¨i7ò kü$Á…HÐÉIp!UŠ«àD°0ØÌIp![4„óAœˆòù ®nÙ.1"V@œ›Ùã ɺ¤ý7¢ !«RàFÚhˆë‡d^—¡ê’Ž™«NÑ%]ŒÜ'’‡Á†è>H~C¿kÕéûÁÓ÷ƒþwìA§uNÊ|>Æh dÄÀÎ`!nÔ!õC\ k4ÖouT—ï©ö@ì qÐdO‹ã}ÕAk¨=á@< yì zÒàfà Ðú¤ÁcuWkà‡¸­þD * N¤ƒ 1|‚D@C?$Á…,UPŒ0ã!Ä©‚BäñB=Ø‘(h‰ät„ @ÚìVD,tä @ \H Ñü7 #]ÒàF>tôC ܈hXÝ®H)# !¦RàFPt$ @<È;Â-i=;ò $Ž‚ŽÈ~H‚넾Å$¸;‚û!®¾§îOÔOÑëêÙ5¸ ó÷à?³Õîûÿ²÷Nï¼mç™Ï[4ÍIóë! \Úü~–¦ û«]ì¶ðIú«½ c½ùõvZÚÜo ft†3is¿1¤Q°[Õ)p3°è mÒàaxoè¬öAò:«« ˜}V p2øa°1ü>H‚ Â`C„ñƒR„¨„B¤1°#GÒæç»H»Æc7² #ŒRàFœØÇIp!Q4DòC Üe€ŽT~H ¹" !˜RàF´hÈæƒ$¸.šÙ © »ª2FAGȤÍ݆˜èÈ€¸4’×U'†OÑUmC`$Á…ÈÐÙ©SôÈž¾¯;}_çkô?_ç¶þ^Úü=Ãh€Î@ú ¥ f%¤ÁÀFÁÎ n†Õ X=ØŽÂã]Ø^¨‡R†¸ [4ìî„B†Ú õ`g¸ƒÖ€{¡JôJkØ=;C´ßkõbë€4xÁ|RD@C ?¤À èH€¸‘% 6„©€8' ÅÈ3bPŠD•P¨ñg;B-©¼;r! n$3@G´¤ÁƒpQ°#]ÐÏQ°#`ÒæÏ–!bìÈ„4xÒ1‚FÁޤAKT/ÔC)ÂVB!Òz¡J‘7h ì…z°#rRàFèhH퇸‘ÛÁ7¢ #{ÒàAú(Ø?hÉï…z(5†"ïç(Ì]¨öà¿ë~îÛÞußöžûwßÃýwî¶“í5s§ý;ö™ù±æù QÊ$/è SÀüÙ p³¯" ±¯ü·ùýÆ ZÌüÚ„õfë…z°3xAkø¼Po~ʦÍÏCÄ(ØÆ 5^ˆ‚Î` mî+4 vö“Ràf? 3¸)p2¼a°±›|ÃöC ÜæÏN€ÆpW@ y›??¥ {¥5ð^¨‡RÇöRìH€4xÁ !ü7b #GÒàA’¨ùy'¢¤Á,è€4xÇyü7 #RÒàA¨(Ø‘*h‰å…z°#XÐ’, vD Bܧ±‡’àB¼hÈ燸‘Ð ý7B #eÒàAÎ(Ø4¨$=Éß;Ù}›ù3°†8õ篚õý-êû“‹­ïmQ¿–[¿7;Úþõ•µ;–ärZžl·pÁs¯Éåio÷Ì|5ýrLªœðÒ1/rg{+/ä`ÙÒó_íq$’*W!CÚ¼\æç+$¸Îk7oZ´¶à9ñPÅy7Î__#æ4jåk²"&›Å½{úwµ‹¿ Òì´-$ïê±úÚ_l¾6—K¨r.T^à½#ºt ®>V–ù>R}Å?;Üä¾8×l»^¿2&ûŒî;䊻/¯|~þíß ÉlþîõR噪œX•‘Í»hœëÊï¯Ó¸ŽsD¿§&îV¬[W²èþ÷jÄÑIwnZ¹&&Û ×gYûËD[ßÀïÊÕ3Yþµœ-ÕÒ8²xÐ]÷4nÐâàüëë—×¾å}F¼5öp‡<ÿ¿3k„ÞÉU—¼û|¯.Wœ5ü•—–†dxÍ€Á/M’ë…P¹|*¿[å·˜çz8÷œ9/n(Ú¹H¤Ë¯¾0ÏÏŒÎÚøqÄä5Éç¾âr1·ãÄQĬ< ‘Ë¡R¹#*FõÈšçú8·KSÇ÷KÆ-{ÌÚŒš1R^ÓâìOb2›[Ö_ ÚûÓÍÓ~²þ¿W.oF倩üó¼JÎkþlÏ)ó¾X :Ûß+±¯¨¹ 7lß“UsŠVnoáëf>~SÁ­!iõJæòXU^í°­³ïÓý‹Ìyç™­…ŸN}Z¬¼xã’ó^dž‹¯(Yv0&§uùëªöþ‰·Ïî»tÙU!+Ϲ××òp²y+„ʧÌÌ1çŽ,4ÄæŠûVx´w¤F´­ºÄ•_Åäì&o¼»ôÖ^"ºn|Ó2²æì©æWÍ[6ŸþhÙ‡öö/t 5ÊåÒçç«4Ú²Ãaý«ÞhPÈâ;?<¶¦FÜS,x´Q\ªø}“ÍÚ •O4:Y&U®¹ÊÉ>?»Ë¾›)ol}Ùü{ó[.;Ô¹eÓ'Ä«ôàO6ÖˆŸG_QÎù_p<µ+^(»t¾íæk~’ýŠv¯ß8öÊ\o‹ÊqQ=`*ÿ.?ÈÁùMgOm÷Ðy³Äó‹—4_´»FÄSòË£19ªkÑçÏ8GöЖwX9$$¯Þ2°â~Wnž•Ÿ*ÇéÌg:z=bOvž9÷W¥ûª/uÄŸgNØVTP+Î0* Zñü[¯—d <4óÆ4ÛPÎNŽÈ½®*çF宨Ün•§ö_f¾¹Î3ºº·Žùø|Þ…<ÒZ1këÕ;<\ç±­ûzî®n+21ÛW‡ä¶/ú|´ük}Fª‡àš-¶q¿èñqÙE÷ǽ±ÝE ó”¹Î•?ޝ›ñ›)â¬?èOÖõ¬Ë_ŒMZ,&†?’ØeÃ'Í›ønH>>ààeOœ1"÷<©¾7•§ýþ¾#£÷=!–ó³9ËÅ{ÏÏŸõaI­(¹îõë ârúöŸ~üó~Qµù¡çãoUK-e4Z5m¸T¹ƒjo©¼.Õï«úøòs\':»è²¿%ïÎD‘8Ö¯VÜöÌìºÅq™Ís yK«¶+þX-^^·¸|Õˆ\ÎÊ9ÏöX}bå57Êåæ÷Ô6ÚºÃqÃë5ŸÕ‡½bF·¼5´Vœ©>RÑ!.ïøbûÑë\bÚ—{›|º¸:·‡Õ¿W—Ú“*ßVåç÷#j\gÊ\³¸è*QûD‡¡~^+õxþ£íq¹åìÛ®kôôñA&v²ZžeêÙüøœ©*µïÕÞTyAùùS®³yÌ]·Þ·j¸œþü—ΘR+F¶œpSj@\vmi6ã–‹Û3ÁªÕòÏ;ï t½Fª¾2•s¨z]ä&q.‡~¯H-³9Úµâ†ÃŸ}ø†¸¼zL\®½u]T»w˜øê€Y\’Î7FD¿o`.§K½.*6ÛS²+—«ú2žp«¢Íúͽv¢•“T+¦6Ó½»ˆ[¹™ƒÅS7›â!Ùô¥þ3w2 ÷þ«òU˜êUy ù=-×9Ôªu«á©)rÝÈ{Î]ÒuµX¶ç¯cçøã26lú½}æôùåw]8;$7Œé0í#WäæXÝÿdssSeê¿?¶ª¾‘éòj}€Ñ©|µ¨f+6 Äåä?¼5oÃ;}DïEËKÒ«Øç+.nÝò“Á¹Ç¯z„VÕÞw´¨Eª,»ŸÓ¹Üßüç©Ñ¶ýòk‡<» —7Žù µZ<9åÚ‚Ž³ã²Í®3ÇLéÝUü}ÈÊøöº|í‡Ï\Rðôñ½«òfÕ^?pû¦é5û“™Ç¯qn]›Éç̶ϒ#3‹hµèüà±é-ŸŒËž®ážÆCºŠÝo_Ù!9,SP:<烺?Qý½ÙœÎl>–ƒs'ìœYðÊŸ—·_T<¸nµØ_´æ¦csârõ»Z]Ò[ô1+ÑjTHNúÌS[Ö¸<÷xUŸ„ÊÅUù¾ÙÜɆùx®sÞD{êýûƒò~»Ú]جN”nÝùò¡Ê¸<÷ÞÁÍvû±hòjÏÛ^î’³Ürm.ïOí Õó‘Í»ü´,ÛÓµÁ~òqŽÝÒcýŸÏ‘½2úuâØ5Eíý\\vìbzøìâû¡_õ9·qH^û“w¹.÷:«÷UÕKÚónßkWÎ<¾oó{>*¹Î¼C޽ëΓ[>‘Í–þ¬NŒ.mÿÑâåqùû7§]±lÒ qÇUÛ&_œ®Î奪=«ÞÕûRvOí-S}hùyÂ×™!?kþ½MOK[}‹{{̨ëç‰W>z#.'ìiºkVÉU"sÙ:$÷×~ôNݰÜuT^]vžvæÞgósÞœÿÈz3X}œÝë¹…­Œ:ÑÑ3¼SÍÛq9÷QŽ»f Ã.Z¿yJ•÷8D*ŸÕû“êïTyu>ÞáØW>íý–%•r纪//ø²Ntš+ßñ~\n7ú—þ•ss¥|Só¤ò Uÿlfþ9/›·¹Pfbh/Y#:µîûeÙö¸,_;ïáGÊIJE설o’æ˜úÈP©ò!UïkvOtn0—Îm}Kç6Ž[$놶ê½à®5BÿΟ۔}—·4{óÚ!=EÓŠçé¿9î«Ú¿'Î¥êW¹çù}Z®spÃÄ÷ޤË-_Þï]²F|Ç÷ƒþSvÇev/î)ë•) Y=éÃry}j¿e{çb¹½ü~Lç_ÿc#ß|äYywæ g¸{ÜyívÆœxFÎyycyÕ¹Ûx†‡äºÿ:{äe†-wZõÎLöO8l³-ë7ÿ‰Ž‘·:7ÈÓ¬ä:©ÛÍìr¶÷Í­£¾\#¶M;jãθÓæ_˜ÓMžÛøýî7vÉl¯cyî~FõYåÞ?'ÏÜ»»óÞ\ÏI~?¶Áuö­XzÃSUr_fýFÅKœÛl¯÷ÜÎS+&¶½H¾¾jòáÆE!ÙÜŒ1>ëxî´š#uÿ:wßÓÏuzíÓ²ë>¸}¦Ðô%¸ÎçÁÂK._"K÷·)ÚsiT<¾aþcwmá>¿ã’¾³ËÁ7^ºçXµ¼ Ïö{j;üZωÊV=gCy4¶]Ýr#ãÃvîgÇ›IŠÕr¯ùépyT”·ØtÆ¥Æå‡¦øhÞEêï<¶fÃÞj¹pÐ-ƒVþi®DõveŸ§¤õþÜG¨œŒœßóæGò¼Œ>X}Ï–;£bI×7ûžóA\ªžØ ÊKÚNÜT-ÅÑw•½ù³Ü}@Ãþíõ¹ÜÖlîj÷ÜûRÆ®c¶RG»¼ _Ùõl×&ó¢¢ù½§lŽË©kS‡ÎoÝK—1Õ¹ÜV5_jŽÕý¿ÊïÎöe6|]<\gÿá£ë¦·^*;gŠY¢btëÉá>ŽËÞµ·-1°¿˜ôvQç)WŽÌå´«ûۭ>Î#eÏ\lßûâØ`û8ß±¹M÷¿–ƽsK®>óMÑbâÔ’Ñ)|9üúÕW;…«ýßv8¸?{ìý©y’»Uýž*ø–vÿ`ïÌãc<Û¾u“±”±6D±Å[Òœc #¶ØC-¡ÁX;±DŠ2J5Ö¦(QÛ¨mL,cÉF8'jIT˜ L’™dì *!– x~×\×yåJ¤}îöyï÷éû~ø|¾ŸÞÿÜ×93×ñ;Îã\rü®«_Íy!ú3JבzŒóª·ób¢§~<<»{"©rFöøu*M±V?ºr²€7h¥#—ªžVô&öeyœß÷ùUðaÎó¸T—fŒ3ݼ¿a¿aé>ÍŒMé‹IéUGWôÿ0>YÙÕ?+£/™9ú”¡Y‚Î0ÑG‡­›Ø{édô[sñz®øþY?O§^0ŽËþÀ³Ý‡èü!ä«GmûÖJúò÷&ŸîK×?‹uYÔ/—ù30Ÿ¾_õ§„ù;õr7[µÛµÔNÍíCtÛïä'’çžžjÜ8ú/œ¬ þ©;q.;1﵎˜óqÍ!ïôm÷Ë„ý æ/H–ïTŸtƒq4IßÓ#†ÃôÙ‘_F¯iwŽTJ‹;–Ù:öjîKÛ„}Fä΂s/þåü_º-*XϰþÚÌŸ•ï?ZàßÇžÚ¼n0·§?þòñçG¨"|ûÙœ©çH¨[Ù6 Ê4úñÃùÚ -Z’LCµ]9íöÒòþÓ«-Ï$Î㬿9‹kæÛÅú7³õ“S7§›sá|„ÎíùÅ‘UÆsBŸó4zîÀ÷gWÝI‹„u·ï·ÞKÝ{ì=½ÛÅ_Ôóa>DlÝÁâMê§©Ã8£×_'vU$¾Åuö„ßΑ¦¿TI‰ê”Fyß‚ÊäÛƒß*\`> Êœ´9¦ 5ó9aó<[°ý–/œúÁ8o8;½õ‘tHnºüf»_‰**øÁAï4Zk{ÿq3òÕ‘½ä^êl¿Ý?à¸fó3óifù†ù78õƒqxŸÌ(ú¹sAõ+©~$íÃ]Ò¨Ö÷Êøç¤#qnÞŠ _—©Vk”|Ì;þ0¬®fý¢y‹Ž„ù«9õƒq¦ž½22µT4åªí×)¿’ÃCÞœÉòM£gÏL/QÛµ)7Ï'zÔåÁ§¹àû°øf¾G¬Þæã±Ua_½,¬C¸åìÕh:ö‰­ËPóä£û按CÓ¨³­Ö‹4?´9g7æ£Õ{ý0¯AÀ;ëOö{1%Þ¿³YaŸ0ŒÓïPÊíµº:¿ööNyÓÏ“aÉ^g’ǧÑPå åÀk~Äï—hÓ´¸ºoJÇë"F‹úaûYüï”.ú¶°õŒ´_¼ ãpMÜ+U¥ç}æ5ü<©Y}ûþÈÙÈ'´hø ?¹3bYĹ4ªR¿à6ÛG¿ó}XÊênæíÔ ž?øe™ ÑëbiðÉ^Ú{%“È›ÃË+üšFùzËLúŽ6œ|%‚rî}»½ÞÉk¬Þæã.“ß§Ås·fÍ]”Kù}Ë$’èùmË6¤ÑKåÃÛz“îNn Æ:ÁÖcZŸ£ÍLjuëgÌüzÀóšVÍžß·óQúE¹Î|Óˆfa³—/v"ÿ¦=¼RùºñOQcû^º!úÞ§ƒŽ-Òg>SˆÏ_N©¯ŒÏOšžšµiëQºû烟³’Èëèö†i4wΜ(ÿ*Ò}þk¹;ê;}¿[=/µ+ð±bºfï“í²ý„BóÆ©ÌÙ{55(´¯§À8ûË?ìG7 ÕtÛ± äÞË~êy1itO;ϧfšÔ]^wÿ¦ãx¯1§·YÅxau[73?iæÇQ¨Ï7Ɖštmñ‘3q”÷—¸HFçžrMŽL£s “ÜÛ5 Ãì÷:dA3fs†0>\…}9¢_±3ÞñÜ‹•^´ØÙâ8}xßÜ¡žïEÒ¡Úèí§ çLîTã}wÚµ^ô€C›_š ú{²ç³s æƒÄö_¤¾¨:ŒsæUЫÛkŽÓÞ{ziµú"±Ž¾Øã(ÃnVGW&Ö˜7#·vˆ óºõ^ZÎDÑG˜_‡Çоê,ïHûºëñü»§j§-{‚ÆÉ®íuó"ÉhµdÑü>žU ±·"Cg7‹êéAÓÓ›'–86I¬«ÙïÌö#Y½ÀΟøüô¯ Œs¸Ñ¤!'è‚jŽcIîÒbRçîqiôÕ­¤%Ûæ¨È²™d·‚DPeß²yµ'Lço6ï1ÿPæ{Ït!]:0Îo;4zf9A©gçÛzXˆjòüßwžN£ [TŠmò“š8³š"Bôeº`¾Äl亮¾Æ)×àßòur¹—­Þø_Ó—Ö£Ôñ…鯠ÉòÁ¼fíeŒS}sò«É}IýœZçüªEÐæ—Nu4¯/Æ/ó=e¿[_qÝæã"jZ÷*0NÀ̯fÄ ¡tR¿Ä¿³yæå;=o¦Ñ?·êéþº/¹¼£ê̹=#h'g‚/úÊîÃ/Æ;¯añíÔ Æ¹Ü²Ò—›P:ÏÖöÕöXÈ„›—ºTyœF÷Æ®¾¼r|/Ò—[ÞGGÐîG>8{Ü„w|äŠúH/w,hšzPÐ žßk¨ieÈ!JG龞y´ HÜì æ{œÁM»¯¼?Ub¯àS¤¿¿žº$îCsÝô7}Wx]­Ãós*è3)íîÑö›•÷-$³Ù—þ¥Òi=Ó¢µÙ×Û‘ûW%ÞxAëûúŸvŸ(úy±ú€ÕïÌŃÔGDq,^ï¸âj¦º 91™”)1èü¦2ét­lËŠŽ9 YíÕ¯±~Ëþ)±}ºM#ÖÕì}0_2VZ·ãùÙó8G33%ÊÊKµI&W;Wý¦\ºàƒ}ÿW4{-Œ ;{½]=h—†ö%e¾ ¼ùùú¡¸!õq`¿!出ªÌô¬ïùë‡${Ÿ]昲é‚?¡ ­ØchöÃO#hì«Zuš_м“cÃ~n¼.ê²P§½÷‡ ÞÏVÝ;\rÓñfš™©?þ2$™¸”˜œ²¿×'£uÖæ#Ë“¹Ör¥¦¸FÐÑÎ ‹‰¢ï6Û×fç­Mºè³Ó•ùü~ž;ËåÔÈãËÌt¿g@LÒw…Œ„ßiéö1wG¨Zõg9CT#]ü]Õ’õ'QæÄï‹=÷YÙ~„Sx[yËv›é½õ¡Ñh2ÉS|æS¡R:-]eAÕYw¼‰2NáÑî7#= {½úãÉ¢ŸË»ì\˜Õ—ü>Æ'„ù9õ€qºMü2÷ÛD3­ñý€2ý²’ÉÝÏÕL§™Ãmí®ÕéAÎ,ÿ¶îô—FZ‡ÛÞ¨6åh~žÍý•˜¯ŽÔ÷\‡qÚ¦¬yÑ#ÓL£©§Ì®x‰œÞþB¶Í3êòíi{¼ñåv!å”÷K"êŽÅ ócùíK÷5ôçu¼yFÎu3u¹ÞuéºN—ÈWªÔµM;¥ÓùŸÝ:>€|ì4ô€G=?’; ™,Æ-;Wb¾ê7[W9ØÜðDÜÏ-äã…q¼ø)香X×±M¼ö±,™<¼o:áR’@V^Ú¹ g“ê¶æÍ¨²'Ѿ-UÅóýŸ÷X-ð©7[·ÒƉǷYq¾¯Éíp^"ʃɋÎN§3û­\?mé䜈#hßr£žTÛQ0ï²ú„í;ðãÜæë«Ùª)kŸn\šm¦+¤ùI…qÆ æNã…}¸Ë$x¢ÆðlU:ýàF`r“ì$tü–1¿Î‹Ü®ìññT1o°ÏÍί˜P¡}+<ßÒR'~þ<žä~éËä©cG_6¤ÓCù†šnWºß¹róêøü~íýSÅõ›ÇYýÌöœ:ÀsÏoƒ¥k<œ:Û'—^&KjsâétîW½=(Ý‘ üüžâòPc®lX1MܧdñÇÖóü=žçÞ¬Þ•þ>zŒ³ õ³¯ü ñ´ý°Õ¨¼ËddøºŸwíNüR>!Ý“†&žE^âýî¾ã“ÍÌÏŠÅ _G+ Ó˜1Î(ç†jœ…|¹0NwKxÏ+åNÒé•cåSÈÀÎ>)ž¤ÓK]bbæ¥ %¿*·\ÔéwS—·vœ.ÞãaëU¶Ncç'l=Ïò²S7Çé¾Wõ$­Ô¿7¦†²#bæìÍÚè‹kÏ=FU‡41ŒôÜë > ½ƒÞÙe>@e¯¸¶?±1OðnPø ÆÉìëq’ÊNÒýo[èŸB‚¦Å´­çi£³nvôFžÍy~vnŠ‘6­Nï¤ó Ó?Ûwc~óÌÏR:O™1NÓV}‡?Šêé²a³OÎ •¾q?½hvÉ!ÄÓÓ§éóØ>En û?_ë76ïJ}À§Ê“æœÊЧ½üÝñ¨¢õxùt >ÛÙ¹½OR÷‡¨G0+r+-eõÓ=_W4.äóäò0[u;à“KnÇS—=}6ÙrRˆû’{7µÑ ½SnäwïIÂBÿЭeÛg\«OX§‹þß…üq ÖçÒu”ãpÕr½ËñtÇ _Eƒ«$êvå×aSm´ïÞÍÓ×mïLò‘½Ü+DÐ?Óg\¨öÞùúç‰wbæØ{Û¸ˆûRßZÆqÚŠþO'Œ{=â*ùì›àI³lô÷åúVk íɦôÅsÇeiðëo즩AâýGvŽÊÖ™||•$ÌïYºŽÀ8Uº>ôuBe~Œ¬~yøù¾*ëo6(t¯A…qNò èèO+;X¯‘“Ëîû6ÕF.Yn»œÒ‡¬Ö¶ùÁÖF:câï'›öœ&Ö,?³ú”Õ5ìýºw‚qz¶œô<Â%žþ¸y¾ßG­¤¥«—ûÀ;66lïúm=È%í9Û¼zFúÀ«áëãU§½³ÏÅïÏ%xóŸŸ¿7£ÃswENœr T<¹Q{àüçVr¤ëЯËäÚè—¬%¸z“ªÃ¯¾þ¢®‘Þ1½Zuýà—”Cºw‹çt÷û«åâéM÷²[÷-²ý/ó¾*ñÌFù}•fdÔ¸©;µ0Òœ’çÕ ç¢ë(þûç‰>ÎRw3Æññ=üU­Òñ”/Ï­ds¯ Á©Ïm”ÕO1#^[ë#åßïôwü}Ù¹»·ÉöG¤ïÕqÚ.Ê7¯zk¦ãΤûH²’ce“=ø>_oyšÙÁ·>ez¸QªÒž‰Íxgí¿°óNgüçf«š—køÆóž™ŽvnÀXÉ·³G]œ—g£·/< n\Ó‹VnÙïî²FZ¡ubÂ0ë Zø±œ°xäïýæŠ~”Ò}1ÆÙ°)­Æ®(3õê9nYY*Ql®¶÷|ŽnxÐíáªVÍÄÏ» 7‘Ï??«;X¼°ó:á¾tá{'‡sÙu 6S×9†©¿x¦’'™‹®µøÍÆê:üJ£Ø_éëÔï«þ0f–øÞÙsXd÷\ñŽç6Z´ýVJ3Ý4¥äl}ßTÒ¡©Û<·ôº©>mV›øîÚC¸>˜ôxí,1o³ÏËôÃûâ½I©ÿ°ã,Òp'Ø”ê¯'ÕŒ;Jv•Iïw&ß&ÜGëF¾¯aX±`ëZÉi Oúôf*é>§ÿ±Ñ¥ì´J¿f»BOö!§8&ï¡Óú6(©š%® Ø9;ãóö#þž!žë­°l8²çÍ ^|ù›ÒHK{ýÒ «Ùic·Ì1õO÷'ž ž¯ªYÅH½bûîÛ6S¬kÙ=4ö>ÝÒrHÖ÷oÅuZ!?àG¨7[uzvØÿ]ãîsµ¦o™P±b\d#;Ýõ¯ ‡‚ $Z¶.A»é÷ÊÀ’Ùcg¼ãÎüYÙ½%6ª£0N¬K[ßõoŽÓ«ÈÙsÒHΤQsôŸÚiƒ÷4ãg /-¥ž6Ÿa¤CœFÏyƒé‚éùh¦Ý9ódqƒÚ…Ï90ÎuµÓ{ÓîÃÖH#ßV}ü¼so;ÍóiÕ2ø`?²® ½rq#~·mßüRÎcº¨V·ñ뀋âyŸ·jº_€qFõ¨uháÌãôî-Ÿ†ŽkidZ•5¶ÒÃí4g}©ÄG/zY¥»ºzÊHƒnVÏ›Ñu†X?³÷Ãî}°õ ;Gæ÷xŸ^ÆñüFY±‘×qª?u·¯ém)¿§õ´Ó”å÷/:¢"µý>¹]6ÇHy_癢Ù<Çæ ö}Ø92û¡A:ùòMµ¬ÄÉvZÖgô‡w{Ò°æGã[Fê>6îÎàͳÄõ «Ùú“Ýdñ&]G™1΋^Ë_ÖŸGOy—¨Z¯W:©džÞx`]¸¿ÔŽlœuæùéFùQrpo{þ™¾Ùóyð×ÅÞ/s`œ~β8ŽþðSp«¤™éd[þŠ­ugÛiçUÏŸxÔïD¦–Øö4wœ‘ÖiÑááÚ Áâ=&¶NbçüûÉófëhéûqyœ­š¦9×—ŽÑVk«üÖdG:™þõÄÍØ©Wó{klcºÓêïí¥|”;%©Ø-XÌó…×…ñâ¾>Ÿkõ¿\ñ˜Ý;>&ø$§“¾†{,³Sï mǼJTßð‰¯†1Ò_›ï[rê³`±žçEáûðçœiâù”ôÞ± 㔟ÝbÅ“ŸÒò¥¯Ù’ߦMÌÿÕëí´vì‚àäm}ˆy˜ïº5~FšWbá…}f‰õû/;ÏcçTû'ÈÎ6›ëQ¨ À8ÃuQ){Û¥;–ÎÖ5jn#L}?ýd·îq¹ÐyI7?âœ6?5Rwµv™ùŽ9Ó)ËŸlËÇÿ÷1:Œóôç`Z67–ò÷vlÄPÛÍa1všÜIç3ÿµyÐöÒ€¯:éÕ³]3í禋÷2Yܱó"¶ÃǧpAX`œŠå"w}u6–š]¹ÂÛF:N\òb}¢¶ml¬÷@çGF|z§Ó£tÚ‰JöÃû ö‡XÞaçmì\íHïI˜1Î˦܉q¬°Od#å–o |jµÓž™wÔ}ÈžŽqKV¢®ˆ=QѵLÁ:‘Å7›ïùó„GÂúº†øwRNý`œæÉÃÆWZKO÷þ¾ÿ™±¸ü­ÔÛváÜ«ùÍ÷WõñÝFz óè9¯f‰÷ŸY¼±yAê£ì’—­ ºý`sòXê]·ã—_¶‘°K•ÆU͵ӎ÷úÙDE «§ï0R~r†80ý³û ¬¾`ûhÒõ§ãT~î)%–Æ»_2˜mdgu߆»ŸÙi`‡ý÷ãZ·%£ô;Ì„i÷‹Íg6ø`–_lŸÕ]üï× °ï<ž?ÔUeK‰ˆ¡Î2î†ÈÚTîîÿÂN/•ØñC“Û ²ÿ§»¡óÖiÖ7/×u,®×ؼÉÖOìܾÒÍvîí-\_`œÞ;Ún<;&†Ö=hÿLõÜF<¿,神]·¬µÙ˜s‚;•i÷àÛ¨rïv‡ƒÅõ:»GQr[Ľ|^u n¤Ãsù|Hý†1ôÔ¡ ]”³“TCøÚó¯ìôiðoÓÊU§X4Õ|g¤§v\rv~°X_èÌŸ»²ÊSoþï\„ù¬n¡ù^ço½ÃMŒÑtÑ®Cº×³“™Þc>·úXp…²9QÅBâOiU31S¿3ß³<¯ÿó¼¥ó¼Ïçfù©Ë¢éki¹7žŸv~ÞØ|;}8aÂwû_>>ÑêÞð¹[Fª»ä·=,0X<Ÿbã°z¢èyªtÓq¸¿RóêM}»®ì²z˜<›±bþ~®;Ù9éX™GÎ?¨ÖAeÄý*¶ßÏÖ—lŸ¬Ð=«'Ù*ç5:·h:¢SÜ]ót;Élú¯E‘xüyªI[=¸Ò4¬cïÖêTå§ú߇­»Y]Ìëî¶7¯­i3鄯wp#^Çy]ìfÝê¿ûÜóvò}ô®ey%2hÍà ¥n•èHêfi÷âòîÙ«m,®c‹îû±}†–Ó¶—ˆ+ÕZ8¿¨Çëã8·õ·FÑ3Wïk±ÛN:ε»G™ Ú·åŠ~#²Ud)ƒéïo•cNjÞ¯Øù0¿ÏtG¨+[õ9ï2ã\œ´ð³†¢èö„á]÷·“ô­/A×Ѻ¥£º´iÍß_ œõν>¶><4²ûz/¼§¶oßgks1®zÁ8Îê,7’6Èjù$4ÙNØÒ³ÿÇBÞëL¶lP^óŠ üß×Ô{,ÞØúš÷¡%æ鼨Ç8fYÍ6KçDÒHîé=;¹©zZ­~†pÝ‘\½½V¾8‚~ËíÞ×¾sNÌò.[·°yDº>2cœek^}¼ìƒHÊï7g£Ïµ‰A2hÝf5¢jm©/ÞS;_ÖçèÖZ1®Ùü!ýûžÇÿ=óÊÿ½h™÷úíÃËxÞÄv‰mÉö4{sÙ˜»/"è ÆVnq+ˆ½WÉöùœÛ¹~õ ݳvyš­ú0cèí.· ç¦dm­rã:5È ý‚âOtYÞŠnèeŒÚ舠ÇkÕK­7S¬çØ{à—QìœGºï À8quW–Ý4å0­×ëŽÆà ÙwΡРÚ2ó¥)ÃÐŽVRVÖ˜öDP×C[†=« þaó‹[–×Ùýzöyv¸ðÿÞ÷jzß«é³W×3ìg°Ý…ÿÇ}g3ð,Á÷=qžd¡Ezræ =9™gÏŸõh/®w“§ÄsQêÙÃy.Z…þÄ:¡?qq~jÁ¯Âíü*þ¨×Eê*ô¯ó’øËJûr†}ÚU’¾œÒþM¬/§VèËÉú7q}Ú9ßÅâú´s¾=RlO/ 䃀?é“âÅyÍ }ìŠöJa}ìX¯”0„>vRïYioN¿"^Ø\¿%­²b¼°å‚wëÕ^\oNæÝü9/ì°³wŠNâcö>G¾Ï‘:—VŽ” ß){·Ì0¡—§Jðm”ö·ã‚5@âcñg}‹‹óÛö’øšI},8_3‡Ðã3TèñY\w?¡‡»âz¸×;* äW+ìK&ôRJü¥=îtBïbµ¤ÇÔ‡›õ¸ zÜ1/nN\œ·Yq½‹cŠxÓzAxá‚ø4 xA„á‚5À”¤^è'a&/ˆ3\ÒO*xA¨á‚X5B?)©Ç£´ÏZ+PAÐ /ÆŸÖMð³`ý‹‹ësÇü,˜¿çOòAAðB2‚X€RèçÎü}¸xæþ—#ÿN~ü+¹‘ˋՓöïæB•Kñyð¯æÀ¿“ÿþ,÷ýÕ¼÷G9ï?‘ïX®ûwòœ4Çq1¡wáûwý;9ŸÎs[.ôÀ³/¡^–г8¦˜žÅr`pÈx¿m=•å{Çñ¦à|Êb_ž¢>ºÿÆ£±¸ì¬W§Ô—Ö,ô`O^Eúàq½ŠY<5‚ÝÜð:à*¾ÈüZ`­Ã{’íS&øP¨$žd xýINÐs½hoNÖsõæL^V8PB\z kY¸žÔ“–ëË©V *âIëxO°ÞÄÅõÂã¼' ‚ïD¿ѓó`ävµtïë¸÷uœË?¯Žs>³…{wÌp¡Ç§Zâ˨B Jò܉ÿÄŸõ).΃[)ñ#“úOp~dY@)ôjÏÿƒ^íþB¯vÏ?èÕòAD‘¼ ŒpARÚp+xñ0oƼŠ\Áƒ"(jö玑øs;jxts½Š9O²âz›‹xÔ*!<=A|Z`JˆPd¢X ‚4¢Ô PBœzA `JUd«$ñhTB¼z óâ}(¤>µ † MÀ­ŸZ…àCÁú›'ÄòA€Ä‡‚ù’q>µz! h€(‘ ô@†„ V ú2_ž?ò¡`9òÏòãÿ¤†Ã+óâÿ­œøwóáß©çþjü?UÏýÓrË{ Þ‹– 4 HÇæ"žÌsL.ø.zã»È|¸ÿÌw±¸~ì ÅøÏ&pÿEà[€Áò¹\'ô:æü(t øA1@Q„5„arˆ#8êð^cE{‡ ^jÁgL,@ … "Ò PBLz ƒ ´BÿõpAX`‘ô_çD¦ „Øô@Á€¢Ó P cRÏY9„@]Äs6KâAÁõ;¹ÀB5Oˆ5Lð `cœ…X€.ˆX,@ 1ëLè¿Îu¥}_ッ÷\þ9OZïy ŸÉʽ¦ä?‰ï¢j*Éûpk%¾á  x€8äºïí’øI})8¿±\ úïùÎ{q=ß„žï^Ðó=\„X€ÂÐYÿY=ȼy˜÷¢X€Rð¦È¼)ÌÅxq›ÿÀ‹[®à=Ç,@ ‘é BÓ ÞRZ„grˆ/X "49„@d2ˆR ¬@q€ Õ+PA¨ ‡XC€¥ˆ£ â5¹àO!õ¡Í~t PãCë)øSäƒ=xAìá‚à5 æ;ÆùЀ I@ ¬@…d`r$„IßöpI¿wvÞö>GäÈ÷ùñ/?rï’ó¤•! µÀ"xöp^Œ @  B êÿ‹¬x"hÃ@>@ðÆ8äÊxonpC0kß2©ó-s“ø1zãÇÈü¹ÿÌ1xA á‚ 4ÀRŒ/­EðíÑ+PA,á‚`Ì‚'F(ÈþxB@a øAH1À bÒ¬:¼oY8à„¥ „Àô‚7ó¦å¼Ë´À TÈ <-°hrˆ0X€bÔ©V „0õ@qj¨ RƒàǨ†XMÀ ‚Õ+P þeRZ7ˆX²€_Ú\Á'à Gºüór$÷>8[9‚2X—àûhžRÈj«AX H^Üp!x5À <9¿[8?ï @@‡~jJ¶È$~j ‰÷£²ïGæéýgÞ „ ô@Qhµï[+ð„PB€¨!=A4(â_Ë E,@ Áè ¢Ñ+P â ܈H ™€„¤YÀù!(¼ <´õïטïk(—^ å/ür¹ÿÀŒr§Ä‚4$”ä}ºC¨´& Gà†P#€M@&+ðëV•)ðìö&à†ÀÖ,à‡7wW˜€‚=8€Aonüà*ÀäAp5Ä`n„d?ã¿Ø;à&®µý›nº©]tÑ0MGtš @L ÝtÙ0]¡š.º¨À€‹0MÔ#ºlJØ À’Emšè¢ÿŸÕî‘%CòÝ’ûŸû}f~3÷z2{$íû¼{ÎÙÝç11Ä  "1?E Œ@Á¨AP@8QÀâQË7ò»-á-ã8 ( 2Ch‘À!8 º``ˆO "4 —á œ@ Aš€¢Œ q ¡bÕ€4 …hµ‚pƒH `5°9„%ˆ9˜A D­¾¶ X€~y°ƒ ˆ]D¼X€Â×_î `üƒüm ‚8A/ËÌàæ4Áýcý‘+®þ•s©ÿd¿Ë:—ú³Þö­¾öŸžGý£s¨ÿÔüéŸ}w‚ûžbô$5Р è=~è;Àäè;QÀ}'˜A úŽø¢xLÜ3Ãè9A("=¡×Dp  ì@Ž¢Š~(¬`rXðC‘©€ÈQlQÀì …§"Ÿ¤ŠÐÄÜ»°è-n¯)BO±€@ôðEª€È…B & EÁjAŠVDè#jÜsÀ(d÷LŠÙÁõ´ˆQÔ‘À‚QÜF Ak@P Ð @Œb ¼ˆQô‘À‚QüJ¿‰ÛÇܳ¾€ H!­ „`„øB*`rˆ# øA À‚ _%˜¹ç= ˜4 €h @Œ¾  }Á$è  ‡¨¢/°¬ïºzþ·çAŒ„_Ç}«ÿˆ…ûì9Ö{Ø¿áË_§Ë{Þmv®„ÿ>·¯Â¾+MÉÙP›ÛWž÷!·Ñ’3·|$ø7H‘w²§´Sï|‰*‘¹Ul¾œé¿Å£Ä8[ŠÇ¤ä¿O§ŽRîɹÈFø²YówXÞåü¨S¿Ü¹ÔŒ0ÿ]n;ÆÙý¢eå"¥÷Ò®ØýÄFÏ)•·rmMîUm˺‹-Éþ¸AݖH¦Ïï·œìöuåÏ{}ÂòX\…ö&]~"¥z‡l[õ´YøÛ’\©¤D…!sÚè…S&•®Ð‚TÍUÒ¸êgÔQ…¡gréB¿ò[a>(Ì·_פÄ.éA©à_ÍçBˆ1N+×iÒÓQm¥…ê–K%Ïz,»ù¹™öotú‡¹MÈÖ[/…ŽÝCùú uÿ^Ìç„÷‹y*›>ïgg¾²ïe³ÚòÝ¥º„÷wáÇ‘cœÉß]Ÿÿ:VÈùM%¿U<ˆØ(óù:|ºZƒñø>½ kçw~§rû;3ßö»e4š õÖ÷îœHOl%ÆáíWchÓ©Ã÷÷ê˜JîVû<{Æù©þͺ•÷¡Wúï\¾&q-ä2Fuçñ0_-æïË“áö£ôÊÓÃ8ÉæÜÊ•¢iß÷ÿ)•4îÑoÙð6špéÂ1½&€.¹æ×ËÙ0šÊ"ž\w:Ì]Ï,_dų˜m¹Nß—ñ9¥µ¼r4t8þ¢Ïæ‹+Gí¦§Bz,<9•\¸ú²ØøF6*ž:5AN—5ýͲvt4íZè—ðJ'Ã)óóe߃å+0ÿZÞ®àCÇ#ÆY}¨òÆ’Ç¢h—zõפ¯M%‰[2>ªQÇ/?ß_U5µuÙõ¦Ë ]xw‚Û·ùa3Ÿ;æßÆë¨¶W. ãú UÚI?}~ýÝ¡T]1²^%Íwë¾£cõÖÔûàwúÀhZÎ4Cfúw²ÏË|k˜ÏËIòò]~›.Ÿ¸«ÈûuS¶ÓÞù×_~5•lyr·C{‘>Ú1s܂ִç²CLhš^#,ÒÐt‚Û™ù·±oÞWÆéÎôì›bŒSøÀQ®“ÛèøzÒZÅÓSIޤz£%~6zó—)}¾—ÓÎgÏ[ŠïãÜÙsAü¡pw=³ñ˜OÊ—ƒÖ#ÝßËNüZ¿o‡^ I¾c+»-Ã×ã\ÅlÛw+>¶äÒÒð¯rh™ÿ J±W<ûBºŒ÷U pç»»ô‚qž9T©~«Mtt¿U7ÅÅìÄÒnᡜ6Ê÷§·²Ó‡ç•*Ý-šö,]ßoƒÃ¿Êd~?ÌWˆÿ~õ Ë¡téã, ={¶ûáu´Û¥¹Û”³“ÝËw¾iˆïÓ-¥ùþ›[«–Ûüýþ0œ¹p·þY_c¾ë,Ïšå{]g0ÎöŽ5ÒÞ¬XC励·ßJìDµººßDœ—Ä]›ŽìT‡ IoãŸt(š¦¬õf­uK[N­;ž2¿3æKÇ~·r¬œäC˜ï“g¿ñq¦Ë¹Ôϲq:þø˜­[ØÉ$bè¼¶°^4ÌY;‡ˆôÞ,·ÔèCy¿ëPw¿aã1ßK–ÛÇòV¼òÅ0ÎÅš£sÄ‹è+уukÛØÉŒ[/nWÄ8öR•Mo§Š¨‹A\C/tx{²Êí³ÇôÉüêøqøür9Žû]¤hç¹T~òìÂÒíäñÄhß'ùm´}ËôtSãFBþm å}YUn_UÖ§X_f>±.}à¸õv¬{:¡ÇLº†¤–¬ÝËNŠÛsõkŒº=ubSÛ93[Ñjcv?]~)†¾{Ýf´^F™ÿÿ»9Ýy?ž¹ jwÀ0‹äQ“ñôCóÛÛšô·“SqÖ>yg¥gFuÙ•­i:zÙ´-cÜz`×uö;°óÊÏ#Þ¸ëÇ+÷ãðuû-ÝýV™iídõ”£‰3ŸXéô¼ªdóÀŽ´Úݘ—ãÖÆÐ²«7Dm?<Á­v^Ùüÿÿ¼¯¡Ç”28½M2_7¹×p;¹7fûa÷¬TúkùO 5íèú'ù¦ëÇP.x€rbfN¦à»ÈçH½•Mú¾jUíÙ/n±ºrÕ?ÆÙ··èËè_‡’Ý꩛ɜעÉýrܶÒÀÑ»tkNKº‚€chÿÐОc·NtûK3½m¯>qƆÂïÜóà!+–ïÓïìB˜®«þߥËg2èÆ›‰ÂПvW×J¶Ò|®Îq·&Ý–ñC³ ›ch{ÉÁY;ªNtÿNì¼²ù–ÛO_胒zŸøü1Æqv»«§“+åd'vô¼F®ZiëÈÒÍ×jËѤ!œ{ ­;_éë>È®õÂÕG;,·»çAžùòrŒã²]î1›Ì/=·Ê¾)v"å‚ÌVÚÿ¡¥¨ÆYb”ÁCË/Ì>½R·0w_góvæ/Îæõª¶o£Gí¯íåS¯Ä8»ü§­_鿀ļï?æì ;y#öxÙJƒ.l³¼2UE´j;òV Ý–c…~\õ̾Áæ,ç€åƒ~Üõüj—AR¯üQ5ÆÙ>|Ê‚½—‘»åuæA‡yw¬ïsÁJíµŽ¼^_>€°¾’1=†n5Ç?­?”ò¹"üyZ?}Oæ3|OÆòÅøóRÛ+Y‡qæ•;÷+]»‚´Qõ?×e‰Y½eîOg­4¬ÝøØ°xåó+ci!­ýB)›²œj– Ä|_YN¶×¼ ã´o·±k|òjâ²U^i'[Ê}?ü˜•;ýzv‰ìmio—ag,mS<ûá3¡”å°óÃò.X®_wÕ½rí'fÌÂóíhÉ“Í-j'­G]ôŽ®¥·Òòg›f¤¾£?–êYfcd,aê;ct˜{>ä3þ0KN6ïÛéó>]žýúî‹]]Oüý9ã\[s++tã¦fæ0}TF›"‰Çñ{žžÑ-züæi0ŸY>§ ¾W.”ã¼?ðúÚÌëIÞ/•B.þj' N7vÑXi›-q#ûŽïNëqv¼‡ÿ}2שl<æCÊò~pvâA ¼uƒqJiŽÛ>=ßL:É&úKvÙÉ¢÷{¿a¥O/îÊÛ¡QwÚrÝÌW]ÖÇÒŠ«Þ†t˜øU®4;/l™ÑªU¾‡y_P%Ž¿¥èú±°u Vöv’ëmÔíc­ôqÍŽm,èF“¼‘õšK%×BÂêçžø•þ™?2›§ò×yᆵÆ8%C\+\Âÿîvr+ïÂ)eZiï‹Ã¦H:QMÉáÆÏŠ¥ÙºÌ)Ø-.œzÏ‹–çÞcWååç?¹}[=ýyuǶ¢¬~OÂ6ÂûôÚÉЩ÷únëi¥–úŒ-Y¯=ÕÛŸpge,m=©ãÆÝÇ»û ;Ëù¼yŸ²eÇ×îœ+¯< Œ³>þZáŒJÛ‰2û¡§q^4Ÿæw[ý•ØÛË:£§Œr.¨Ÿ÷ÅÒU¶&'Þ\ÌÌma}†ù³¼#–'á¹ïbÇ8;žç«2y9÷©ÃŽ6;ídÏÓÇóLm­tïwe‚-uhßö-W_‹¥k&%çxXq¼ûüóÇ©è^w±>Ã뾦{>æÒ͇tù”ÕíÏErF÷:Z õ|ã^©e1­­”Ïô¥ÙvNz>#%–Þ~7cä±ðLݰþÉú2»®ñ¾ÜM¼úŒã-ò¹Y»KÈŸ³“F¥Úö-ÕÆJMŠ—+æ!«‡Fþãø’ JÎÌ÷`ç‰÷ýþ]ÆÏ'2~}.'žùÄrŒSæ»'íö—ÛCÐ5  <º>mG~·Éš6#Š7)F²Ýê½ï²3–ÎælšKŽÿjÞ;Ë +ð{éa!-Z¯ÜoŒsáD­Y_b¢ÉåÕlÙ6£®{DÓÄöVºÊQc{“od•_š*hGwHÖž«w,Ì=`ë·zmµÓ>ÊvÕûó®üŸÜ¹ï,ßÍ¥Œ“oÞø‚ bÉò%Æ Á8c^M®ÙÎJc­[Í©LŸVêܽî’8*M8·Ÿ,Ìý}Øx,ïˆåíòyœu¼úšã$Úz\öŸG¶e¯~Û¸ÉNÖ_º'Õ+]7Áo®={3šðp÷䥦8ë#Í0eæyûŒßt¯¿]zÁqs×¼—:¼¿ž¸b¿6ØIÿRïÖhb¥{nÜ ·¥ûžÿ©oRåÒNü.fîyçPÙel^ßw©W>¦ãTjrº ó£^ðùÆü2ýN‰«µ¬´äØwAßM ¢Ù\ à8º0wˆ|‡ÃÜza×VÇ,—Ïm!¬øý#Ÿ˜oÖ?™©ÞKžqqƸ^Nj²»nó VÚ RtÅ‹AA´ÂÈÛÒ|³âèoÅ›Åííîþ>l}ʾ›_xú½‹qüžZ%M÷‰'WO›3î/¶“rk69_ýrÞEÇûêmè+›(»zp ˜{yÔã¢Üë6ÿ«Lj?8'»ëöæ×¿µ¼òšäY^{< ,.ë—g¾„—vv/쵸ëó†T» Ò¯c‡ÇÑ [¯”;s9s½ÍêŠßÿpÈ–¿teÝÈn?~¯¼>Œãú:=ö‘l·OFNŸe'Ÿtu{Hò[éÌCLã&:Nä¨\oÔ ŸãhémðÕÃÝuÅú.Ëã×qo¾™Û¥Æ89—·[‘g?qåÌ´“=¶ÞÎÆ}a¾PaÓ‘}6ÇѤ#GÒãÝ×v~غ‹åè¹tãî‘ýÐo/ÝOjž]zn æ•uK›šüpÓ¶U³yõÉ&K›Á'ã„ÖÌý5V_ì¼3`—.pܥ͚ŶŸx€l¾Ò»êò鸎H ‡•°Ò yÆõù­o-òdÖÉF“^ÅQWünù0wÎG`„êäàiîuݷίǵü4±[ùƒ$›û¯^Oü.W|ÚïþøÜmšÖ9P†\^Ðrÿ²zzqMôBÕÊLݱùË£eûžy‚>Ÿ°~ÈuòÞí£ ¿Ÿg') k6 Y©£s¢¸¾ÜbKu=õÛ·vÌΘÌýZv^Ù<ûƧ·¢{½‘ñ>Ñõyàø\Ús¿I‡ >7oìD;)ú꘱;>ÿË©oý´®Mš¶zÚãºzzeµoÏ/â0·Ÿ?[G³ù"^³¶ìé·/Ç8\zÆ™ôï¹(rÖ[Ëöçë^ 놓ߛVOiLIø<¹´Tïž—²ymÖ¼O~>óÁí3Íÿ^|ž‡㌖Å,qÏ@ÞfÜÛòl¬8§Í~p:x[4üìõé­è‚֎̬­§ š‰ó~˜æö{gã±ü¾8\u¤Æqý¿«¶sâ¼ÃäyRå‚cGÙ‰dÖJÓºlVaþ܆>kñ|c±ž¦ÙNW=¿)sÿŸùc³û ,·ÃU÷8îÛæÅs½o{„”j6Q±ÇM×j¯ås¦Ð==_¯u«-Ý:õz£¾…ô”Ï{ïöguÏŸg‡Ì9ê‡%‰/Þ˪5»P»õÞFB~¿gÄ8«JœNÎæw”lú.·º&~—ª?.¿ù8…êCæ˜NnK7\ø0›ž¦ÔP»ÀòñîüÖgX.ÁÙÕ…êŽþ$û¼Á¿ú‰[¡$ïâ×;íÉëãÞÁMÈ’>¥–Lj'÷.íóð^ ýù]éÀÞ¢V”Ÿéé³]߬ژ9Ïa}õ9–»x{äõR©þ“HGÉ ÅÇ`^ŸQO#l¡ —#5Ò†L@=%G[rOI¡ë>ß.Ó © ¼‘æë«òß3ó•˜>Ø~??Ë!\ç–‡ëÒÆqÅ*Ž“¥vVÜ­²“mÃGüØÇ’BÍO¹ Seú]þµ½,ô”Ïw̼ޱïÃî— *}'èÔw2vßßÿðåõq’+mÕËv‚HOÓm'%Ú®œ’ ã¬}ð¨ÁÇeˆÙ Wɯ§dÄüæöÁg}œí“±y<óÙ÷Ì¡Qbœ;†ÍÞr‚´iQ´^·ávrîî ™/~7>¢ éè·"—ž~®ÎaeÖÛ_aë¶¿|¯4§ÜšÞ÷1Né­ùfÎkBÉØ±»²“ÍG iôn MrFNî\ÛxÌ”SOå’oX½ÝvÏ×y½7%ÂïÌëÇ=›íՇϗ(‘™óèó£4Üs5Tó(EÈÍnMæ ^Ó2¯žÎœ¿Uþ‹iüW¿Û‡e×~ßÀ[/'qGÿ;?Ö5’»Žthü®s­¸Ž›B?7l:jG}9‰áìÿÑ׳,ÛrƸÌuûغóü¹°låó¼’ñóÍzîû(.½`œ–CçëFÉšôµ‘‡ºbû ¢ÑûÚÖŒ@ÎÜ§Žžö[߯bô¹0ÊŽÏöÝûvÛ£¾õ³ ÷Ok{¯?¾¤ËÓ _jqx‡‘ð¹Yvb¼ÀÝN¡|Ž…? 8ú4&¨³žÆÔÈeöÕu„­§&ÏYðúc…7Â~Wïý^ŒÃß5’ûÅâ_6nŒúj¼+$ã¹&¼å„ýMœŸº¥”ßöUþ-¼Kî| —>pÜÆ+Ç<\ÿ$É7×7pNM;»¶CÐSüNɾŸ:[ŸÆàêÚ}¨ž®oÓ#Õ•ã¿Ê%auËîÕö/4gH¶Ê^y]JŒÓïàuÿ¼cN’ E7 -YÑNêŸ(ŸýÎû¡÷ºrÕÌiõ¹“—5좧už$õ+ÍÌã¯Çg„ýýTÿ¹*z]ŸÔ8>?ì$¹X#[ý¼þvP»ý£ž¥Ðj‡RºMÕ˜*qïc‰ž¶Ù-0tL溉ýμ¾/»óUÙ:Ýs_C‡qü -®/.pŠô›–Oý¼€¼ù™üp0#…ÎÒq7È$tu»þí ëéñ竽 ¥ì~4»nw»+;´î÷<?z£©ºª^yêFŒ³¯êžJ)CN‘…{OÔÊn'—–,UX0Ÿ“ZšTZ:!´LA=mÍÅ]Q¹ónXî4éìèòòµ6íí¾ÿêµ/Œq–KöG8ŠÈ”kÖòY*I9aØÅ§)ôåÛKnOmJfbÕ¼R¤§ú2ë$ŽV¹õÈŽÃï?ßp¯7ùï[×k}æã“!çsuN“ׯ§aêžJÚKŠ´(õו’г·&‹;ã–WÑ•{ÎtÉê—ÏqÿÝU¿bwà§_NO›|št0É7ÿx"•´n4îá+CÌÍû¹5YXnI›O˜gñû ¡î¾ËÖ¼®ËºìÝ¿É'é)¯ w 5WnÿÛi2söÌxRÉŸu¿_Ík¥eÒµ‘øÈÉúÅ5oˆž>¬0}åŽV¡_ÍwØý¶Ãë]êÕw•ǵ¼!gÖýt)Û¬TR«{wŃÂlþÓ,¾ql÷¾¾zšjÏ¿r׬Ì\h6/ç¯Oeé?­H,>^Jª•²hþÛÚ¼.püêó»¡4ΧOÖÜWÿ˜J\µÙóc~Øóãœà­Á9H§ŠšNe'gÖ+;>«{–_ÈòÓÙúÛ¥ãŽß bÏñë‰.7J%SvY´7ŠXéÔjí&Ì)'¥¥G_¡§ÁÙNÜ>öÕ¾Ëgù¤÷#7lH¦në­=qCÊëãT¨åùü Ù{'}u|TbÑ-œ¨Âü3<Áº˜¼!t~×ߎîÕéé× ëÌy"›Ÿ°ý䊅LÅÝÿ ã×…=ÉŃ׮G*[ðzÀ892.E½«y–dŸýk·=i6RÆtd¥³jž¼Î¿=µŸ(´±áV½{¿?k?dyCl}yöµÃ[WÉ ëë.9dË—ôïð ÚȳäÞ'ç 6²C×8×ôÃÆ£šunÞ‘v/žVl2~7.%kMö0wž ›?²y [÷o(·ëÄËE ¼öÄÇP®tÉm±gI­nâÇÅ÷ØÈÈ-CÂFþžBGNú¼­j»ïèÙóÕ‡ü>FO«&ŒŸ^¡Peûˆl¿‚=WÄÏGøý09Ž›XFù°¹ó,©3mzL§%6Òåì´zá·ShñËTvµ£Í¸Ç*Zè)Y°d×ÌßÉ»¯'¹û`ÛkZl^SÏëó+1NüÆ9Wý[Ÿ#þ›j°³‘¦åŠúνšB5gª-©ýªÈÅl×»s¹³Ö/Ëeë5þ~m ¯|!5Æ ØØ Ijä92h[tjçÎ6rèuݦ/§ÐÓ‘–£ë×–§[ëP*óë75Î ýòõ>Ëegý‰=çÄÖé.½`œî¿Ü/´ÁtŽÜ?öeY±j6rbØáÑágû)ƒcÚ×"ßëÏÝ ÇüÍðpéçµ£2sÙxlŠ=÷ÄîC{Þ5bœ#Ò«‹n}:G\»?>6rr*p”"ü>ÍIhŸ¹—|üQÇŸ×Gw/úU}±¼B¦–gí™ÇjÇ8 Öîß®ÆyräFÇé}nYIᢟíǼ÷M«5¹ŠÍlKø}(=MH¬7»~¥Pw_çûI’°/nu_7<ïóùdÏ[Í«6ÍPœ'ù6·ŽMÚg%ÊÑ·n¡~óuj¡ØŽtÑE¶+Ð\OÕÉË+oú9³©çzW¤tŠŒåã ÷·[ߺôãÞW9yÒŒóäCðÉqm[IƤ£¾H¡ŸRG_.û¤ I{w­ý„fzú¨üÍþ~ËÜ`ÏQ±ûl?¬½kû“_çÈqü§f½hBÌyrtÃÎf÷‡Y‰ïÌ ù‹}ÄuoDï" Z­ÔÊáóG¬WÖÊ<ßìþëól^ÍÏ÷êx=G¡ä¾Gë‹U{Ý>ONlí—ÞÊJºTžTé*úÖºœ‡“ÿTÔ\6vN»vzšsBâ¶2CÝ×qþ~—?aϰ}vay‹.`œKÁ›60þq1+Ù}§È…²X¯ ý>œXïmBw½W©rö| Ë¿dûÌü:¬–×ý6ÆùõΪÃïÚ›H×§“$ù^¥I5ç>ÿœB3j\jUkQzfUÐÖµ§Ü…£Ù^fæ}1½ñë‘D÷}d—.pÜ9G÷M¼=×DˆkœBŽT{zJŒù®+uQeç;)¹Ü¢1=2¯ëì{°|<Ïý0;Ž»ËVÚRó¤‰ÔrÝ`M!{^æˆoð:…›û¼nØn)uÝŸ,¦öíBÝûžì¹þó¿“ñÏÖ÷ºÿà“#Cþ¾`±‚=&ÂÝ-]®M!êa~,ÏSèža‹ÏVîGGò»xå^]:¹\¨)wæ}hÖÿøû!NÿœX¯¼81Ž_G–Ë‘;ìÝšûštZ é¶s^‡Ú/S„ûÜ5ÈÔ­N¾G3½[ÿ >ô«Ü^öœú?ÂqŸ·›zLS(<œ´þbD¿²°¡ù\?g MÛ>hð€-Èêa/ <Œsçñ>Ç–m¨„~ÊíjÜ1î«>Ç~–sÇæÉž9Àjî÷w?ÄŬQÞ?…”ÊUvSׂVjiØìúT{GR Oëx5ÖÇm»ížÞl,ey¸,ïŽÝÏ*‘?ÖÓçîûÌ®zÇñëç¾úQ‘@Æœý?õÅm¢|ñzÔ–ÒVŠƒ•o¨èHú÷zóc‘q4ååÇÛš¡,×—Ý·àóÓÝ×7Ïœ8#Ž_ɵàM eŽÅ%ÎM¼M6wÏ9ë»jVÚÁ7ã™Ïî6ÄÞ¹³ÿ ×q´¥¶x…_4cÜŸŸå$×8fñ&G¯õ¯Çv´u…ÚƒȵÈuÓËo»Mdæ¡g/ÔµRM‡#_4mAÐËÍW±ž¿IºåÈÝqeû3LWl¾Äú(~ùù«OÎ ¹³ÌÃI ¤=·¬šv›¼ø&þI€•Ö{´¿ÊÚ€Ôð-Ÿjk¤§~} ÿTm¡ê«üGö|»Ä?ÏÀ?,Æñùýãâè²"¸Çm¢8|í÷ýVAWbòθôKöVzêšÞ^T}=¯}méÇca^ùÈ}^=ï#Ë1ÎÔWó>˜bÈì£ï©q›œ¯wlÅý¦VÚç¡oøÏÕ>É:n³geC=å÷ 2×?L_ì¾;{•åôyíÓb~ÿ"”s¸ß&‡›ŸS™1ŽÐ·N¸nsàº_úôF]—B™çƒýnìywö»±õŠgž¸ãÏ©neåôY}øÃxµrééá´ûúÏÏûª¹×.]`ùüw/²?N …ÝG‹Œžöóþ¾Í­4bã§”Z]Åäû ËFÇ®£ücx*á{ž’1]°çI¼îƒã¸Å]7ºH±#îFGÞ"ß_öŸZOf¥lÞVºëº ãÚÆ¹Ÿ£fŸ›¶oÃÖì¹1Ïy˜㜽Ûdîê|‰¤ëÌ7•Û ¹EªùÜ7¯••JÎ=»^݈ÜZV|ùÀøü/V%‡· ußa÷õùón‘±ù Ó·ç:Þ'W†<¼`À;‰dQ·lÒÍ­nK>b}ÐÁJkZ³Çš£[‘\®Cãè–‘¯Fª¯ª(ëÿì~»oÈóØ=õz~ã8Ft0${")ìú!nkÀù·{Xi>2«v¯}AäÇ&ÛõnG',Ívó‘ueûìzÇ~7¾¼’±çè½r½1Žñâ$G±<‰äÍôÀ È7I_mÍã·bžñ*bÔîÀnÄü¢…¡ù¨8ú]Þ“»§¼ã¾ÆtÏösX_téÇ}ç×sÉ€‚‰ä˜¾Q¿É 7‰åz·/}Æ[éý_EÝÛ¸âÄ'ÆÑ…—ÎØ2h´û9`¦{^Iîù»¯îyßPqø¾—HZŒ¸ŸoÃMR üIþ´¹Vúó*®Áõ Îq6:GÆÑ™#¸Á(÷õš}~6ïcë°.·iüÕ_½ŸÖaœ*KK«•H&~šeÕ¹IŽèN íºÖJ—[{vYAD<úx{hýÐro×ÕÏF|uÿÕËùe¹ñ.àø{ý¹H$ÅÌú¾BÐM¢«Þ³Ú£VzOe§¾éŸHøõÜMÒ¬NRú€•¾ñÓÂcoª‘-óÆ ´FÄÑï×T\:!t”»ßgÕc÷{«Æ®ýVvl²+iWøžüý\1Æ™p¡Zd"qÉò®…\Œjc(vÐJÙz“h\ˆpþOÝŒÚ`´ûºå½¿“(c9ëžïgÈqüÊoòêD}I‘ùû-d˜âŸ÷â{°ç?ø|æ8º2ìmSYßÑîóÎÞÿÉ÷k…%yæþü.}à¸çuÅZçù1‘|è»¶ô–_,dH›û'âsç8\¡Ó”ñ•É~ÝVöb,ÍŸ4È!}2Ê=äç}é²aeovÊ;é“û¹e¯çuq|c®›ñÓpüó5š­¸0ÓBê}ÿtúˆ•=‡ONÜê¾qÛÒXz=ûÔοdŸ¶Ó­Q…i—޺ߓòÜ7Ðaþ}‹DòVÖknîA£‰Ê­8e¥­§Wë½%¥É6Z>ÇKܿ˻c¤û¹rVWl~Ï®ïžõjÄñ;Ÿß8üq§DR‹8ÙÖBæ¼r"Ç%ÌÎvî°}zÉQéÌê}“béì”vÚ­ýGR¶þaó2ö¼1{Î?µ¼ß÷À8Ùå*Zœ$’çºZÑBún¬1ï-+}}»Aó‡k;“­†{]f,Š¥³–¹É°¯ÞW`û¬Ž}/ÅN­U´!aÏÙºt‚q\±êsIÀn†sƒLŸT«SxIMž[HõVÒ€ÜÙàÚx¥{? Y\xßpÊ®Óì:Âî—°úbïÅxöƹ”¯µLK$üüüñi¾3ª”îi=`dj“R$¾ÞÍýÙXÊß'¡YŸ_äïû¼òî?¸ß‹ñì+FŒSª×¼Ð&‘‰¤êÄ1ãU¹AªÈ_ŒOÃ8š§õ·~>óBõCbƒ3{ciõ"w~ʹdeó6¦;6OdïÇñ¿§÷{>vŒÓva¶¢]t‰¤YBïµßg¿A~èܯhoŒ³ùU©=’ßO v½àKËÞ{{øêîóÃtÃÖ÷l?Ú¥ß ùöN_VôÜ‘HD:¾èŸz$HæÖËŽãòzÖ¸w»1Kª~za„ûº‘õþ(ÿ»½Þ¿l Ô3ÿùÅgCnFH¢U#_'׿ׇqÐ,ƒèôr¤Òâ_ÎC÷M}¡WŒéîlþÆ®ìy&¯uŽßâÄñQ浉dÛÊí×?,¹NæH{û©Œ.è¸R}·>i`úqNÄêXÔ©×­»#Ý×sÖOØ{=ì¼±}AÏ>¯Ä8ñÉ\ð{"±r¯_*¯“·Eb¨d£QþïˆÏÉI‰ñ~áûwÇÐ-õWFùO˼ž{?ŸowÏ]ºÀq{«Ÿ5·1QX÷]'Ý6T_2¿ŽûòÅÜ[êŽäÀ⤩5wÆÐ7ù'ž>Øn„{>ÍôÇÞbïK°õ¾×ûçx¶a'fã<Ì‹‹›°«ÈurcÉUŸ¦-l”{[xÃ.Ù=Ä'êî¹ÚQ½¾ïÁëÃÝû‚ì~[ïóçå…¬ÞØmÙŽåjàõ„ãhi뇉¨×œËÆ=+ý*™8÷ú”ÐÕFýÛí?µ.gORq¯µ¥üm -ìjÃÜÏ1³ÏËÖ…ì=žni¯«??ÜÐkÇŽqÒÏm‹qª-qF~#™´,4û]­!6ºzí–5‹6õ"Ò×/wT*KþøZ=Ù4ä«ç:˜.ØóUêŸÅòyK[v=qé$o†üæ¥Å3p~"k ¶ø`2 ÊqíÌšÉ6:Lžgg·c=Iãû¿”:“בƒWGíÛ;È}ÞÙybëN¶ž«[Å2úÈ {Î¥ŒÓðtû>t]"9Xxò…äeÉıwwñç‹Ñ‡ß[ }ü{äÄ9?eüC[Ì;?£d§îý6V_l’ÍãÙýÏý%9Æ‘smuðzÈç+MF$“rŠÛµ´›lôKŽîƒ’;‘×ßõ™x=†Y»í|ã@ÊömÙ>9{ž‘=÷Æöß<÷¹•§¦é·ö±Ƀg¶îƒ['“9‹.윾ËF¯4Þ¶èEhkÂÞâŸcì^W±ùzH—–eÇ^yéÞçóìój?úþàI?¯H$óqw¨“Iº$Ͼ¼z=[sUóJ«É¥ ¥Gì:#<1Äý<»o;ÿ¼’Ãý,{/䫇åßLÿ7=˜|„ïËru‚‡eˆàñ˲¯Yf¢§:ó¯ôô@ÿ–÷ó@×Ã=JÈc~(A(rýŸø¡D;BñëPƒ4 Èâ-—Õß7DÈ‹å2"Ôþ¾zÁßW)øûþ‘ÏœÆÃgŽù¤0_…ØÇÇÄV$p~s\N„oåLÏ”¬¹×F ð4À ” IðM‰ü†oJ„G¾˜Fð°dè~™‰ô¬ÞKžcâ,cY³då‚:ËÐù³ÌD–¡,x ‹<<Ðÿ(31Jð?á<,ÿî÷FµÏOoô¾Ë‹¼é¸lEKöÌü7ŠAðà <8ÿÈ?Jëá!exN'£o „¥NÁGŠó>÷ƒÈTÀ\ùë,YBxZA|!À ¤‚§”CŒF |9Y†ŽVð¥cþ¾"|1©‡¿¯gÆvd– I– ¬ù‹A‚¿/ˇø³|1–¡l–é Åü}ÿ(_Œó¥ãv:8o(®–¹ÿHoüWûâSOÌÚÿ¬þQü³ø­þ§ôùº÷ýOýîíuÿlŸû£þöWõ6?áÿ³|0.ÛA.d'rž›,7–Ëc¾Áž~wÌ7Ø’‡Û/Á߀Ÿ‘mæ²ÑÏÒ²øs¾wv!ï†ùlªAÚøl¡`õ@„¢Uƒ4 @ñ€™Å¯ÿ|ÌÚûþªùÛ¿Óïþ™^Çã(ŸÌŒ.{6>¿Ð/‡w¾k”‡o±¡õ¾Åv„bÔ‘}má2 Q˜Ž,þÅjV€Ïœ11Š58€Ekb®¤ ØÄ(âHàÁ(f#  5~éY3g¸ÌÂ`Á'Ë›‰!¯åÍ|Ë'Ýìá‘î 1¨*ðY3œGº8â0©‡GºB‰¾Yò\9Ñ„3„xt‚€”BÎŒBÒ'P 93æ,ÞÆ¾Vˆàm,…À´‚È<3­Y¦Œg¦uP@| †#²dDdÍÜÒ ™[,'B ‘j£Y¦×ñ·ò¶Ô€{R[ëÿž×ý=¯óùïš×I„ÏÃò¶¸¬±G˜æ‘×ʲ¶Ò€ÅjÈÅ=k†Ï Ò€âOò¬}QÈJ!ÏZŒ‚ŽŒÂ6 Š[œ\®5ŠÜ$(t p‚`¼HPôàä2 Qü& …´‚B€H  p€`ˆÂ$B—Eh“Ë‹€PL@ ±D;‚hô@á¨AP@@QÀ"Šv1逯c3„°tÀ—ËÅ æ²q€½ò×Y­ ‡ð¢€Äì@Ž£„Ì ‚ÔxääD âT“Áò¶ä«øfÉ´Özäæ˜A`–윬Y„\¦µÞ#'âÏò¶XN—imRˆ_œ’·Å2­# _›Ü¿ÿ¶Þøw_ü×úâ¿ÚÿÙ~øWõBîüé}ø|/.›"  ¹ƒ¢Þ¹¬z ÊÅ=o¿ ÔDy¸gèð7 @±|¹gFøÜj; p5À (`£ˆ#£Ÿ—c´£¨@‚ÂŽŒ7 Š\œ@‰b7) ^  ø²rô‚¸¬“•£N!seåD;‚Hô@$drY9r& øA4ø¬©cN@!À !$ HA”øeÉcÕ_ˆL,@±E_.DÈÊ‘BxZA|!BV—Y­ 5À)ä…ùA”*`\^—yÁ­g³äU³œ–WíÁ®H ^u–ü‹oe‡±ü‹@[œ\æ G®Å·2Ä"÷„ç~cø{^ø²ÿýož ã± 1# 0##{f+Ës€à\Ü{ø V øO2ªýPÈ!BFµ­N Da›€Å­ <˜…®Š] L@Š¢× …Ì Ð_ˆ@L@ 1h(! r™>ÀäˆNI0 Ä¢i@Ñ€‰ é"Rƒ4 €˜¢€Ÿc CXQÀËúF áò~@Ú72Xí ÂÓħi "Œ¾¢ X€‚Ô‡ÿÃå'úBœ!À ¤ùaAkðË’QÍå)²L gÉÊš©ÈeT€âVÿùaQÀOȨ6ƒ@ˆ_+4€?ÊcÕš¿{ãß½Ñç¿«7rçÂàÃg˜EPÙŠâ™Ù°\®™!'÷î"þ 87÷÷> þ Ø—{5$dS§) Y+s00 ŠZœ@‰â6) \ œ@‰B7)Š]œ\DÑ›€…¯Š?˜A D N „Œ@Ah€(…\E.Ÿ:˜”»×+…ËUÔ£i@á€XÈU´ƒ ˆHD’X@ c_ˆJ,@qi( 2UñΕ~]°ƒ ˆOü @0ƒ@Q|!F0 ùÔÀ¤§V(—¦"5X€‚~mH–|j_Xå‘O­N „ M@ QG;‚¸õßÈLÓ ¹i*`rˆ>J¾˜€ô²Ó²æ¡ýÝÿîjŸÿžÞ$ÏÉý.(J¢05À™=3wVŠ"ÕgNîÝhü HQ°ZàÌͽïöu~vˆ¡-B!«òñYÚR´V(ê`\–6ðE«€¢ÐuÀÅÌ E¯¾(|°9ü ‚`ƒND0ƒ@CÒ@—« |!°)Ä  ¢1 „£N „€ @ E†˜ô@$ÆØÀ‚ ,=A\*`RˆL•¿ÎœM ÏÄ_$pD¨~b°ƒ Rœ XÈôƒ8UÀ!R-pĪ¢z™Ü 7 øA¼À‚ bð…Ußȓ䲸@ qG‚4 €È @ ¡G;‚àõ@ÔŒÏä¶9į¾hJ`R4­Ð Bd™ÙÜZÂ×!÷ï?ÕYOüOöÃ?ê…ßêÿJÿû£ÞÇzÞ¿ÛïXŸûw{Ü?Úßþ‘ÞæÙ×þ¬§m~#wÎPDàÁÙ9?'ü-Ÿ_ëÁ99oü Å¥Î\Ü;í¨Q A¡i€3÷~"þ¤(º@Ž»7‚Â3)ŠO+`0ƒ@¢N(Æ`(J­P˜!À Q :à‹"U ÅL@Š¢Õ u.+[, …¬Á(f£WEFq®i@B71ŠÝä(ø(à‡¢vîyd¸Šw¦­ˆ 5H ÂDE°9Äü„>£;­¾(j°9Š; ø¡À#€ÈQèQÀÅ® GÑG?~°ƒ @DX€bˆ¾„ X€ÂÐPpyÝÀ"‰v±h€“{G ¢1)„£ÄŒ@i€(!&‹16H ËÄW0ƒ@ˆL œ@ ±™€‚Ó†ðŒ@ñi€C„ ‚Õ ( È(A”J`"ˆ3Ø"Õ'·Ž…X @\/3ã;ÂÕÄ«i@G?9X€‚Ž~BÞ· H n p€`ˆÜ$º¤oâf|î·AüQÀ ˜A ø¢¨d™Ùß:Â×»PŠÿroü«çfÕ¼Œõ½¿z>öWô¸¬s2ÖÓþyØ?Û¿þÑ9ë[Ü÷3qçÅ¢ F L@ŠÂÑ Å£& Ei…B R“V(¨`(,9 +Š»â2ƒ@˜ø¢ÈTÀä(¶(à‹‚S ·–DòEñ©€ÈQ„QÀ…ì é‹‚T3Daê€/z’ý(ÈQ¨QܳÅ(V z8…kRo$p€`±HPÈi Ŭ"´¤Uä‹Z Œ@‚âÖ'P¢È @ŒBŒ‚7rïP ï¨Az ‚$A$CQÜýˆÁÈÝG€ Ü;@ QD;‚8ô@X€½E|!°pÏ C4Áˆ!œHàÁˆ "5H ô?ô°p÷ q…3D/Ñ.q#˜Iæ{^~Â;^ì_’ð¿uy3ä"sþIu'ºý®œ ºÏIz’äöKI¸˜«î²6ÊürêôÚ'šw³‘;‡ÏE¡,g¢ÚÖåO.B=}tŒG7ÿVãų‰¬r‘Ò'Ï%‘Yw²å[ÔFWôì”ê×­9™:O79†¶osn[ÏSƒ¿ÊM`>l,'÷[ðòùµcç9?Ÿ²SIÝØçëÿ’Dæ¥_?×ð´®<»«eã6$|ƒ*îb¿:Øä7Øí¿ËüÀX>ÿ}HK—a,ïï“/C>1jG•GªDòH³áNîiIäÁ¾…Ÿ^¶Ñ‰íÎíWG2úwÿηw ½Ý#Ïä–Ú!n_æóÆ|<³ú°1ÿ4n1ÆÙxúÀ­ÓI¾#Ç­ß7‰lò:¸­ÍF‡»lZ|>íàzðDPÈ…ÃC¾òma¾÷¼¯åïGY•ÜzxîÕœª|>¨ã´ÏSe`j×D²ãˆzm©ÆIäÜÖôÇC6Ú£Ô½¦¡=ÈúÕOÚØÂbh÷®û÷h^ vç50¿æãÂûö–ÍŠæ‚Cªzå(1ήNçê¯h™H\1NE’ˆý‡ ³§ÒËùOÝѽ‡àc4Èí¿ÇüN˜_(Ëe9éž¾‹j_´:)oH¥D’#ï ƒŸ^#Q\LyñT–§é>/õI“€¡;¢ÌOƒý^ÌÇŠåBð>›5¼ýÌ0Ÿ÷‘@ ´<Ù—x¸ü«¤Ò)±ßMß¿²3Q®l”Q±t }µýñ/؇²<$–ÛÈry—»²ß{p޲õ½òáŒgG²Æpþj —´éÕyû5¢r§ÒEÝoÜúгé2ìXÌ«[ÑteΡñ{ÊòY®*ó«ã}·É®Y'4¾ÿKSw^¶K/'|ÆÂ{Æ= $®5g˜{,œnX_«i*=_µáìMyZ±#Uyº7šò¾³=(ód9/¼¿Ê !—ç³àWÝ‚0Q—nògÈË-Ö#cE‰|¾þ‡ ½¯‘-yȶÉRi±û{õÏwK—‚´vw4éï×ìí®Vg¼Ÿé[!·í³¬íÎß­ÁŠ@oqŒs)£Üÿcï; šÚÖ­±s,ˆQìQ,Øã±ÅÆŠ [ìA,±kDT<¶`E=jìˆ-v$A°ÇºbC”€¢èQ Á‚ØÀŠ"ú;{}Ûzî»÷þïïþcƘÃ{ñ¸V²÷œß*{í9'/]r•ðy‡7È»]‰s;¤Rž÷N¤G³ûÏöœ3Ò´R~ý­£}…gôÕÁœ[ÌOAÿ(»^ ýä£+üö;J:r¶êån½a«¢ uL¥Ì_è܉ìºõ+ß2ÒìÓO7Vµ rP7˜_Ïÿþ¥G¾ hÿð»’i!c®’¥R/œÊN"¡¾Ñ¯ûÁçgþ°ÔÏ…i— é8â‡ú‚~þ觃yÝâœ[-ô3pëøFñ¯’+î{â“ÈâIÉ´ô30ÏaðEÈô¨/ #à:‘Ÿî´¼ÀÈ¿ðÁM|11ïZìK¬‡~fL¾ûÇ ç«¤³ÕK~_Y”{"(®W÷æ_·;Õ'•–¬®·ÆHätürbä9˜§‡ù&èß—ÏGú9Wæþ‘ƒ‰1¤m˂٧æ%‘¹}×Všß®Û [Ôê-‰²ŸO¹}Œ´eÑmÎm2F >Œø}Ð' Ç9þ~Iòù7[¡ŸÐ—ÓûßCì6²Ê$R:ì…K|¯TÊçÎu ǹÿÚÁHËTäœ[GüÂvÐŒ÷3¼'ÔKq~£CIÌ ‰!yŸj´TÊ’È/ñÛ‡¿òI¥¢›¤Uº‘~³Ú^ð;o ×qÁgªüà0×sOxýÔÍç÷)~¦Þ”ûÐ=†pñlÏ\“È’áúÁÍÔ©tßÍÚé®W{‘F‡·½Úm ¶øÈÇç‡0Àšßù7=ñ`¾nÌG¶Q>¿>9ôã(9¶l]î2äëÁ„­ïIÜ/óÌ L¥c uÒ§-èCVÎ~S¹N%¨g§48ÛÒWð‘E?8ôÄœ’ õW\h¼¼q¾< ôSbN—Ãn\!3d§¼gÞN$ÕÛÕŠž³<•¾ìvºaЫ>¤tƒ{¯×¬7R>7h°À¼O˜Ó…ãÿ=›~žÃ×g-ôÓ‡–ð=²á IóŒ-ßít"yªŽ*±%•V©ç[ë…goR#Ê_½æ…‘òãÕ A?¨Ómµ þ¾Ä%]ðOüÄÙI­“±¼Ú¼~ Ÿne5Ð+®£Eݺ¸)‘[ã©*v •NÛûñܯN^D95ü˜´D½¶nBñÑ3 õ}ÃÐ?“ÏŰz| ¹µïÐYOV¿ùúl†~ôÑg?–úõ áó²I㺃ÆM8’J œÓú «ÔÌY­Ê—+Aý­Éþãê(xôÙŠ–Š¿òïÃüXùúf…~ê]ïÓõJr4y0s|L$=}6n|*•Î(ygy×7MÉ›èn#¤)ï[9DpꉹJ|®nÓ|¼v(•)çýù£É»ý=Ö<‘¤uo—[ûl*­^±èRÿ¼:äq‘”®ûi@Â)ϸ a‚/ŽÓü÷1 þ¬È³|þ²ÐÏ«ª-ê\Mb:¥Þ-›Höî®_ÇœJýgËZ mJøü¨×·WßÚ{(ÍŸkè,ä_óy>UYÿEyÝ@ûûœ^ÿ.­M¦xÏŠXôÒBÖ»KÖF§²<²n@™É¿Á¸Yºë §/Ûø >˜» ø52Bq¾§ ÚO\3äK§O—Ù8`!nít†®–T:¶yÏf²½]ɬ§uš:ôñãÊ‰íŸ ÆÌ}Ä\‡€9Ge£Bš1?þ~k¡ýþáïøÆ^&MZt$ý"-dC‰6ѧî§ÒIG“/õéEìvû~FºoÔõfïkû ×óÌøùq¦P'Åù¡zh¢ãª8ÓËdz&Í&®·MWB×tx ußž[¤ }š^O]ÔÑH«}3(fˆÏ~z8®`n 暉óþÌÐ玺î2™t0ôIð ¹íV-ôâ×TZ·Ì’}kÚö!ÕÇ…Få6Ò97ÞÞ)p{€àWó—gúXÖNÌôp÷ I(äç?¿Úç}£/“ÈLcù”QR Ékƒ¿£•Fë>é=GAòN{L»µßÈòû þƒX'óû™¾öð-qÄrݯN¾ïáà:ßÛ Å³Ýe²,4ãMO ™Q÷©á]i+í:iiƒ’-½IÍf\²šQÈ»ÿ>ïýQçè/l×´ß¼ÉÒû3K^&3»û›Ö±š¾,rr±ÒG1åÎn[Õ´½D&>M„º¸¼ò‰Ý>¡2ò ýûÅ×Ií×\6ÌóнKdÍÑÊsã‹YHÃñ+¢[T´ÒìœÁhkòZÅÙÈrW=…ù®øûpWÈ‘,yõꥈùóTÐÛ·›%\"šÀÇßM ¡ÛKÌ›QÙJ·”yå}/ 6[.®QÄ#½1±ÚÒóŸ»õÛáç-iBþïÈçh¡ý%eݯú_"Ãzn?z08ªa€öùëZŽÖ#Þ? 4RÙØ;ëÌ{ã!úüaN æXØõížûÐJéq‰Ôˆ1ìåº2{l7mh%+Ë-w££†u­r8ÈH¹”‘RyýhÓ¡ÁÉÕG¸ õçsèzn‰ÕôÇ–üù—fè§¾2}b›‚—ˆ´‰Ù£Íô²¾q^÷¾pÏÞôá~Åš”s§Ÿ·ÒHùyº’âú×]è_Ê׿—üøXŸ×´¿¨+—øu‘, û%$ô*Ôf¹«•.ÝØÀߣAuÚÈ>q6Òi;ª ^àóƒo4Þgôßµó¿t¦ü·'s×®¼Hæ­Z]~\Érpz/h×;²X›¯¹çnZ‘ÓËH»NýhÓy¥à‹ûèÇ/ÎG@» ªb2^$*”Ð.Ÿ#SƒùL)?Î+…qýùñÆ"ì?ฉyvþC?Åÿì#½HZ…ß2î¿NŽY» ï÷w{Ù =Òý[‘¡Ü27Ù@ùõžàÛŒüGß|Ì7åçWlýí|>îP¹‹$±Ö¡=÷æ^'­¸ånU+-×Yv~mÑŽ'½ n1P¾òOpÅû’:äÎ0ßÈÎè§Êµ5†½Hœ¼Cür]'>ÚRÏÍJ7ë™m ó$Ãk}4d¹ºs¶µÑý…| ¼üõw`9Žï<„õhÞ©‡~Feo¹à]ä"ËÅ»Nv­ ðoPÏJÝ›¬<Õ4Ä›h:qÉ~–;ÚOÈÇÀ\pÌëEŸ]±¿µÚ÷k¹ú¢êËr`"·Â¸N¥•mùº‰•6WÖ­ºåFwÒÃf Í0ЇZæÝYÝŸò~ÖEØüì«àŒ¾ùvþC»üx\ïÜÕöèÙ1¯èr§VVº¿qBëÆ›»‘ËKIßT5ÒèÜ©¹Ö `ã^÷‚Ì¿•ù&;gÊÏWN 3_½@²–4¯¯9|8%·6„·³ÒÛ¿Ìêuib'²Ñ2´ì¸.FZ5}ùŠÝþß®ï[€cÙç~åñ½ï¿]Ч×5Hlp ùî€k„~Õ“yrøÜ'B-óÇ·"ýý{=ß9×H{Û·?/_ß ôóG¿÷Öù îâýsåÐþ­–—Ów ¾@jvªä"»FÎ}Dßw´ ùìUž÷„’n¤×>ªµþœ‚âuÆyæãüÛÎ{h—Ï%½@Òn[;ÓéÙèr³ÀàÎVº6w\…ªwk··½l—m¤“”½SÌg [7äz´™õüÂø¥Ÿ<øý¶8ÿû k¡ýj½ãJytžL\ÿn˲§ñä¸ÛãÝ·ºX郢Λ{þYŸtZØH39ð!¸/QíK¼öÀu=¯ãÂùô¤‡ö7rñåkÏ“]¸…v<‰,¿êV1/+ݤ¾[ú‹6dë”ÎŒŒìsû²}<aÁýB;¯¡½-0‹ªÙã<ÙrµÕŸ÷BâIÕ¶a—¦÷°R>'«+Y˜[ tI‰‘å±()òõ‰þ´Èü½87à ýDŸ~¾üy2)¢IÙ»þñ¤å0—BóûYéúøh¡ ƒ»ðûZÈH'—ó™à~lEž`Ýá÷¡ß{`ÞúóëEæ‡\&S÷ÆúÒxßL¤·ZIOö‰'½—¼­=fˆ•j¢Z5­U·y×`[鄲F¹iŸ£æ÷A/ñÏF¯÷ï¿õÀýNœ—ˆ÷Ÿ%Ðc;©6k§™¬mßñš®~<é[ø~¡Šc­´$L{‡ÞìGø\]#¸1ÎóÉçþÂuÃyÿ²ÓÉ×WLÎñÀÜsœçñã#óÕ‡~6sexš™4ë7x~w‡xÒtA¢.+]iEIEû>wÁHß¾ÐëÐøo:C~ò>È9=Ó¶N&}ãóâ|>ûÐÏŒ=Ÿ®7ñ6—°ËÖwãÈÚÈ•UgY©¡c¡C+Ÿ×f¤Ÿ?°ëÞæççÁwëžx?E íó·ËL’7$¼ngŠ#ÓË5myNk¥÷æÄ8¼+Ñô³¨FÊÏkz u óÃ0QY>»cê‚lAùò‹ ŸE[þ¨ÑDI¯ÃÞºì‰#sA<Ú%Vš±êhñ>ȆQ‡M_cŒ4£Ìî'åOõr@pŸžßz#|ñ:Ê íOè,uj\•’:Œ“«‹#ûç^—©—[éõɶÏÖ¤h™~niÅ«ÕvŽõ ä3¡5¿/üNЉxßÙÊ݇ð9 ^‹Î‘¯&®~ ‰# Ò†\,¹ÒJ•U®ï$%üί8·ÖiÓ/Õ¨ìõm\ÆëqeãòßÌᯰœÇ²™òÊO–"ïΗ6‘‡º÷#¦A#N­²Ró‰Î˲ßáüÍH_‡Åæ|ðî7?/Ìö5Þ°œÏ7¸Âüe»N .E6tÐYrvjß²8R{JmëœÕVºMzæÅ¶†ÎÄgwµ]‰0¾<唼~ôPaÿ¯¿þ|çÑ.\±.!í›N0ÇȮ观Öói=C¦>P®m¥8²¢ÑùÇ ·º¦¼MÎŽ$ºÂ‹ìŽmŒôϽ¥–î NQ‡é¿ýz¨QØ[~þ÷Üy'~®¥‚ö‡6ÝλöÒ¹bøÈúy±¤-½¶´¤ÎJùý‚âdpãÊÛÞô3R—5#\¦Œ&ŒÓø'®×ð¹Vþ\<æýtì Åpéõ=¬±Än;¾ë1¬“V÷Ù>«µ‘Þäbç?Æ=žÇŸ„y¿ž{á9<ŸÙ~ôó¢Ñ£9‡>œ"I¶?Fô²ÄCƒÊáë¬4Ó4}­cFkr!7|HØgåóY ã>³õŽƒGû’B©Û´mÍëúyÈ}ÃS¤ÀÞž5®Æ’3MªÖ+³ÉJ%Yf‡ K;þ9 =ßêOqüÆy~}íy³WþúN¨û,/× ô1ùÝÖ·çN’+¹@ÆX’ÛöÏÝ­¶Z©ãù·ÅÊåvæ|ûý~Ð ¿øYÐ?îwi¸áô(ËÇû5Sž6'S¹÷­‰LÉZ|µÿùXrnî¸ËA;­Tõ x~tWr¦ð~Ýâ³ëH|ÍþB=N+iéùÅŸ£à:×¥v½@ûüüÕD>įuhq,–Yv?X¶ßJÏ«Ð<ÝÍ‹ j¯ªöÛ#=¹üò”â ºäs·> ûÀÈ/ž|n©Ú·ÔæÜœ ¹4ot(–Èöô Za¥ÚÍëë;Ó•Ô™ÖðUx1#혡ï.{;ð‡ûŸço¸àë0?>ª ƒ¼QÑÒ7Žu|­Ì";bÉ×Ke%ýXé ÿ\yÏàΤÀRÅÞ±¥l¾¯®òÌó |ó³;9ø'®ŸâoÖ‚!Ïc-ôcÓ–'Ñ{è¦ cIÎ~î •¶Vú—ÔܶD^öý‹Z5´‰¢¯ª@a^ºÄûÂëëƒÇ‘.öž`>®]/Ъæƒ6_g#Ëf LZ>%–l¨ãýD}ÊJÏt[ñ4§1ñ«TîZ!¥‘úó¶½Hôî ®[p^ûZb=š¡ýú~µtN9ÊrëbÉœnç&8c¥+FmWÊØãw)¿}¨Ö•xð:ÍZ´ìÝçêïÝc^š]'ÐOqiÃùç%íš×K\çÅ}xrÖJþq |É‘¦ÄyM茦׌ô÷㎙«ÆG¬û|ýÊòdqœç88”Ë”ËõÍüúM?B†´yÜnÔðXÒ¤êõAµÎãz©-ÙÖHr´Ú9œÖKxpÃõö/žWH ËÍgåƒ&;®…'$Œ%KǤ\kzÙJOÌ]ý陋' }¾c…FÚìãØ-•oŒú¡ŽaþÐ÷óñ>‘ú1]ãh‡Èý„z1 ž±äž£4ñrÜŸ•ý ÏPca½ý—ö4Ò_*oV/N%Ôœwóóú·‚.Åóz´ÿpLφY.‡È¢}ú7‰%'k¦H¶ÒVé«ó–ö@f¹½‰©^ÒH{L™ß¸ÒoÄy=~^~=ž%ÜaD´®ÒB?k:]ºv`t9paGÚµJ±ÄTwwíL+¬Ç?v9(÷!e*7ܺç£Aøü¨ûù'ÅL™‘åã±CËžÓÙuí'ëügÓé‘$c‡ud—R±¤±kS7_Xéöúm^,zíÃú5Rþ¹Î0A'ø'¿/ö–­SÞõE|ÎÀ ýlÛºÓ”Þó ™¾6¢ñþ±„ôLÚø ÷G‘ƒ'w}ób~ž0TàŽû8æïs¶ò<_¾ô3dåsï8¯²ºêÇ.¿Ä’5Чå5EÒèW¿£c§ŸéGr‹œñç.#¹Ø×cíÚaB½Äïƒ÷yŒŸ#ß~­K¦<¼K禳 ‰cÍͳb‰gó¸H©S5U[ü¬©y_+ªu±#­×Ž{â1Bà1¶‡ëœ7áøe× ´Ÿ¥àP„“ßÃ&]twŒ%Ý‹/m½µ\30ýø•*]ȳ«ŸwOI2ÒÆœ,{Œê$¶w%Ú¿@µbo…çæøüQíÃ%£²ümè§E½"÷‡%…‘ßûÖiq´@,y³î²²@Å4ZéMª{µ3mICm½BÅŽiÍ+·š-5V¸^¨o¬+¸ÎCþ~RU¬|êN^/ÐOòÁ_üVvØOâçú—“%i±óH«¤±üäÖdDØÅâ'æ©î^h€oÍqŸQ7x½pþŠûÓ˜ób× ô³~™o‘¦–½$ 3vܬj±Dz4u¾¥zoä(oµÚƒÜt¯µ9¹­‘®éè“óið8aÇÿ™-ð ë–˜Çzhÿ@JÈí2K÷÷G/_×êd¥•·+ÔJ£|NY'ò¹R·š"F:«:·S9NÐ ~ÜÿáyýXXßå˃~Ú—èMì&|Þm,ùí´¬í¨zi4qgÕ e˺ÛÀ%^h€çú÷E‡«…ë…ç0°Žñý?t)Þ²B?m?·à¦ôıÈ^õ£a±Äe“G§&i´ÇëI%¬3{’sãÇ\hòÎ@ÿðÛ7{ÛÍqì¹CžÖcܯÁïc×GùLùøq!wûÛI&Ω“ûÄ/–<Ø`zZ_–F /’ô9ü¸I»ÁMˆÔÓwCç÷Æ íbN¢8?\íñó=IÊÛºàóÄXrl—¿)·CáøjÄ£%H‡S)Gû:)ÿ¼~´0?Áëξ÷ü~q¶pÝùºËÎ]A?yúÆ}ÝKì ¿:½|q6ÌÃd~Ó{¦QÉ€aí®ýêCj…ŒÛîï9½âo:Z8¨;¼Ï8?±ºŽûj¿?©…v/<¸¤ÛÚc+q\ßóä…Ó±d–G¡Ìkê4–7=„„ê{©–‘.*;öî— c~˜Çíw=W©\6yãÏOÄëQ=ô3ùÞÈy“Õ¡dÃé„ìD[,y4ÝO6#ÖoÉ­t}I¶÷Ú'Ÿ<Œtt”­ÿ„c)ŽKØ®Qw¸ÍŸ‡ãë ú‘x:®n¹*„4_|}Âó²qäK¡÷¶ä…itJdù§«÷Æñ‚cúÌj<î‡uîÛãzÑÎ{hwpÊ §ù=7’Ožö'ͤKKÏ•ui4âè­—$÷¼#¯E{é‹R礛뫅uþ‰ûÂÈW;ï+dÊGŽiþzÖåu¤ü¤UÓ¶hâHùê - M£“ëd…Ä êGnô鬄õ ¿î¼ςžp<½÷À±CÞ‹ŸîÿH uõ°T_CΆ¬ZGB7<²;ö{xì„ñ3ϵ†‘:F½«Qާ¸ÞÄë‚çÐp=/?Ú/°fö¼{«È]½ê ÂãÈÕ¸P¥ÕFƒ}{ŸË,Û›|µÄ¯›PÜH[—곘\œ@±îà}Æû‹ë;ï¡ÝªñuÌQŸ—nW·³9ŽÜóxçy$Öúc@ëÝ[{‘e'®Ó¡ï ôÏé§Mxƒ:Æs&xî÷Kxò×G ý¸S(¬[LZŸLÝén‰#ó³ž%Ž9F“W<«·ð¨‚^¹¹óY«Ú·‡¦Mxƒ÷ÏgÖ‘‡=¾Öî­>çÏôÐÏ”ì¬í…₈t‹ÃÅ×OâÈàðó™ó.¥ÑÔ§‹–Ó„¥.ޏk |~Ø$¡Îaø\Ç5;ï¡ÝUò3‡GÍ&ëZoœ™\(žHtY#‹&¤ÑUc¯5H¾™²)ï”òûgغ,GØOÇyŒïÐ^ÞÛ-ÈrÍ>ý‹'ÕÃÞî}7ª|Cß­¦$7§¶8l`ûG~ë%®‡øçSoŠç]®™rþ¹å(BçijO.ž4íq ¿·ã1ùª$ó›,.ví òÿîï±n"ßqŸ ëŽx\”@?Ü,´ZŽŒ477¶©O<¹½µä›4j1i$µJ(Ù~¥AÈ[ÆyÖ7œ×á8oç;´{¼‰¾ÂË!J5•{¢ONþ±¥’úK­2óUÏÀƒýÉÄödcïh.[1^˜ÏcûX÷ñ¹2òR¬+ôÓ$õSÔƒ°14íÙ„¡ãÅ“ÚÁ…· .f£Í’¶øMW­ö¯- _>ÍÞz`¨[|>‹û’âœL-ôS¢Ë©ýtSèP{Àz<¹°µËú-¥l4)0åSʯH·Ò ëš©/÷Ô7jÅ:‰ûPv~C;Ýg—Zœ0ƒêºy^¿°xSFð26°`Íë—n¿‘wÞšæi¤#Îçi‹ÕüÆù!žsÅç øÜ6ß¹Zèçb™=}Ÿ«çÐ+?Y²/žd\t:·¢¬^ŽXàšq·¹ÛŽ d4ÒÊcòÆŽ<¥®?îÿñçœ_zà/žZ¡}~Þ£¥\ší†£ñdÒ“ÜÙÐ>Ž£¯|n«[[ tÓåKÓïj„y!òÏ»àó<·cçÅLùƒÖëwÖß·€Ö(è´ÓûZ<ùm¼cŠòW=PorÐ¥ˆ*~£q¹Æ@Ïѳßek(¿> zåŸï¿òqŸ\üXý¸´šÜ”´ZDÕ‘Õ>½Œ'á¹ÇËW)g£Ã›Þiìf¨A;ÊÕoa k«g7¸¥™Jq¿÷Ïð\®×q^(~^"‡~Æ.ìñ›ù%ô’cZbt™k$tÿ¢©A.ð}ÜÖ\©ªmL2zÜíßÍ@[{îVÑ_ø>X¯ñ<>'Ãz‡ûBv=@?sLMýÚS×—åäiÝP}¬²«nKÆsÛçÍ ´D©K‡7wò¾Öi~è ëׇòÛÔB?tþÛ¥A+èxûˆk¤Æ§¯ ^V±Ñ¨!±ý«ŒìJ¢'ï0ÖÛ@k潟nÝ:U¨§8®ásO¼?xݪ¿»Ý ¡‰­§¡Ÿ©«÷–ªXM›w/xóæákäYã¾ØÜlô¦}ûPA^UÓçú5м{–õÛæi„:ˆã3Öíòw¦y¤­yáûâsÛfè'iÊðb×+®¡ O™ñ'\7Õ uûÚ¨6á¨_™÷}ÉãYš™·&h÷˜bí· ˜Bq?ÏÑáþ¯]Ð^ ¹_X¯µ´tGù¹Eů“:Ï"½ZÚhpäKmÍýHÞJ[1*?[±ÅðÚSã}ÀùnÓéŸÏôZëf‚çµí:©”)¿SÇeå1ºŽÞº(©BÇë$°kôø7ÄF¯þæDºÅõ%—£Ï¬’6Ðu½&Î,ýM'xpˆüÂu­¸ÎJ Ÿ­®k×-óÝ@SSõg?^'ÇO=æe£×LÞ0u›‚¬Òµ;=Ç@m’±Žj„û׉Ÿ?óÀsùžóqísÇ´Ûn¢ôÄÓ„y{®“ú•Rý6zïëñû§úxï9/ çFhÄŸú÷¾m§ Ï¥ñzázçv=@»e ä‚Tµ™z¾|š¹êòu¢Ý‘øq€Ž)þ‡AÛ´I–pˆ týb.xwª0ïÂú‡çåPgüßóë-´ß"Û)kÏâ-4ÍâÛîÌ“ë¤ìâÓ¿*mÔzj¤gàl)>üY_··ßtuÿ ãž[ÁóåvþWÂsøÛèÝÕåÖŒpI «ýÊŽP¶Q|/!²S‹Ú>K ´Ô¾9Á!?Ö |u€ûîâ÷ ÌÐÏÀï:ß^¼ƒ–©Ÿ0\ž@¼‡Þ¾YÍׯžSIˆ¬œ‡o±bZ6eëÂq§ý0?BþöŬÜuš;þÔv=[ÂÂ&ûUGÉ2†Ø¨ç©O% e· S§;\z6œ½3MXGáxõç1âûàP9SÞ&°Nón wRkiòµ}`)¾hÛÈd•Þ^²|ê—:ÉøÆ­\3£ÂéÂ%_~i<0@ï2[,=ÖqW¦G §˜r#>ûés) ´ßelcÍ,ãNÚ¬|Ïqu& '›ýZrœ6ïë=ª´WOYûáÂí×Â)Gj}óa}€õS»4ïc™JÏ…º—ÿ=ö맛מIŸwÒŒûJ…ëúRýÊåb §Øh«^‘­.}ÉÆç)Ž—ž…S÷—ëRwMÖQ8®bûÈ_ÜÇ^»úéëù÷aTÐOÍ‹õÄ´ÛE×ßÏèz1|ù`3 ´Ñã¶Ä9‹ýIÝž wÍr0°÷aü…sI8¿Æºu‰ß×iÀëÚOÏ3å½¾º‹ÊSË7L.b!‘}$yò…6ÚÇëvváqH¿BמÜ{NKÊw<î/<âÏ©>òÐq½&>g¢‡öuï‰yð±Ý´ñ•ÎíëYˆÛèË.gVÚ(,R’#¶÷#£6…=¨”N“bž¸ônÿMwxßñ~ð9ï…ñG|ÎÞ ý„&œüó]Òj[½ëú½NÒºt]é¶6úâ]·nã?÷$ãL]¾êa8UÒ˜ž³ÓŸæ¯¿x ¿p*^WY¡ý,.~9~/ Ÿ·÷Mš…<çòðÂV-t²z÷ß§u"«x'¼-k ô¶s•MS§ Ïmñû`N2>—Âû-®¯U2å‹,­ÐyÏ>Ê=õn4ÍB:.-^ÝWocÏ7[‘ì»Wëm gbïî  ¬ßð¾ðÏomÂ~]ÐîÂVï§ñÏ‚;µÔYȪží…ì²QžRòÜÜÚÍ{¶>ÖÝìxõa€P·±nˆ:ðHÑW†pºýºËÞEªé¸ùý><^'çÅó$8ŽŠï‹úé“3±j•çÔh(àÝ)‰<êÙüK³6Z¬N«åÆýÉ¡ò; ªÚhÈbëléÓ„ºõÇü^¸/->oe†~¢V¥/õv;H•_ŸH‡K"ºòãJN+˜Nß÷kÔìx Y±¿šŸÆ×@/·kàw¹Õ7}c{¸O€ã]ÐS7¶Õ=Hù÷Å“ˆÒjqXøK:=³¢ÃÔ2õº õiîïã´G(îà8†çþóé¢Z¦Ü‰mU«ì\V©pöà)?§tvŽT.|Þ#óš5<]˜'áuÆqõóy» }ó“¥e.쎠U6l Û{2‰|J1ùT(›NÛ˜õÄ@Ú“¾%j¯ë×Ö@«¼t;Qçþ aþ‚û¸^À}+ì—×+ÙëÀÿüí­ô··Ò‚·çõµÇÿá¾·Å!¿_‰ú'~%j€…ù•|ï÷Wž%z–©ƒ˜ræ±Ä‘[%ÊÔÁ¬D.ã:Še\~ç!—%Ê„û,ÉY®N@Ár®E^rßgŠq>ÁWn”Ϲöb¾&΢\¿ÊK ûI^"æêˆýÓ1WG-òYúYNlˆ([Ìü]®ŽŒåêpþé\^¢ å\ËE¹:\Îu˹ְ¼D.W'ÅçËȬc™°úï2±¹ ›nMÊíÝÁ3*ò¹^¦ë¥ý.Ó‹Ë€{c—çpaºñù×rHÀD¢©?ÿ:G”!ayèIÌåyåp{v,ûÕ•åBd0⬆ù³¼bR™Nä…n¸ƒàt€Ϋ„ç ³²lC̼þ>Û0ƒeŠ3¯1Û0DäUlbÙèWÌex¡g±8‚;9ìbû{ž÷÷<Ïá?sžçÎ>WwýœQW ¨`xQ£®@V- ठ8qV€8 à $Ö2 –ÿÊåD²Šó‡\¶+;P”9^\þk ËÕ2 ~óaÇœCq&¶’eEp‚P³ X‰ ÷.÷~Vþ<5ÀXB˜`Tœ/;ˀŬˆ¿Êò2ÿ$Ë ³"‚Y¥(+B[“ÏÄþ«œCqNNÊwY –!aY^€L”«eEp°1,6˜eyqYz€#ˆW ˆHAÄ!LÈj€ A²J¶ à̼Ü1;'Ã<ÝÍwæéŽù9èéÎeÆÜ¡èDyQ,ÏKHȿˋàr`¹4ZÂó“ûùŸ¬•ÿnü»Fþ÷×Èÿ¤úÈÝÏ(€31`x)ÃÎ@Ì@€à8I)95 à „ X^@Ü(€+WË2`@bSqÎã›óaæ<…ógS`>˜N”ÿj¸:sþ[œ×ü=‡[—åþ¬6rõëàÿ5kÖ=®ÞaÃúö¯<#þ¾®ý+5«gßÏéþª~ía×ÀÌÝ' I0  ²˜ L0  â˜ O0  ™ R0  B™î@* ç.;´Át€,€ˆp-ÅeçÀ½râ²O¾eÒºs{}YÎû~T,Ü»@B%Ðä’?; à „ÔR2 f#§`H¤!Œ¨j€ †r* n @ äÕr* q @ µHÈ(Ôf€;[È(àf€;<¸ö·ll5Ô! ÷¼HŸÃ?†º#åæ_=ßÈ‚¬9ˆ@p„ú¢¤äÜ™=€3·öĤ À뻼k @  a"R‰ò®³d¼ Ô ˆ*àŠ¸‚¸ÛËœ5ÿó¬¿×˜ÿ÷uæïyÖ¿>ÏR²Ç‘RˆHœ:@@$H¨!Œ¬*€à¤Õr* o @ a$V,Yq.+¸ „Ö°,m)[È(à¦R\^—Åå€ìaŒðj@Œ3—'׸ çÿŸ?S[ˆ¸–ã¼{9?ZèDÈx8¢®8:¨·% à b ¤Täüe M€3GH©Ìy ÀïÎ ¢@@ @bÒAP€ “ÀïŽ . Àa"S,)ˆ-„ N ˆHAx!,s[ 8ƒÕ @bÔ2 eÀ„©¤¤ P-À ð¡êŽ V À€hµ+À ÄpkYp1k€;ˆ:P€¸£Î p  ¡ë™ØÕ€€D °ä þ0@@E  B È(¡ ˜( Á+@Å!„ðügtü—jäÿ‹úˆµ‘«‰X Åuëßÿïó+®Îýoͱ°žý³ó,¬[Ü÷‰áî EÈáêÆ pÒè9'àÊ(Df€;IȨ€P1)*„K °dÜû€€ˆf*Éå\rYŽ îψ'uærhàÿsëA _ @ô†•㼘9a¸žå9Tø=1 à „ tå¼ò8ÿ7ÎË ø äÔR²Êœ üˆª¤Tá|8ïž´j€ ò†0«€Œ«AŒÌ*@ @ u(PÁcR ¹Pqï{A Jáž °É€+Ôœ@€Œ›cqïÀ‚œ¡ÎX2DÀ Dpa¬/îÀê‹`È@4!' àʽ'HÈ@Hzî} “BmÑr¸w¸@\€;LP€ÈL M È(@p*\ ះâ;®òm.æÌÎãO<ûßòj™òUÅ|Ÿ¿÷‰òhÆæôn¥°& ùË '7ñû£|ºàÉû‰u|¿ßöæ’É BŽî”®²F‰3©8_@ý̵ÿGК«¦O­\ê©ÃÅ‹WIg9^äKç1CcÃé¸ä'㼟IÑo }'П}vÐwI쥅~Ö›¹#}w`nŸ°£å RìÃI]^Ítê7W»Çc¾‚嘅ӮO>—»4Sð—Cß Þ§ñ¡à—†> è«Âõ£‡~úü¹¤s»ƒt—ã?ï ºA.z5ÿ¡A:=Yêh‹¦?±XÈùËËÃé»7ûjºDÏ|`ÐïåÜÚІ{?ü*¹vÍÐn/{àÎAê¬l}4fÆ "?577µ%´»(ãØ¼É#wy¹øp:¢B—ÒsJ~ó}Aÿ ôÃûŽZ¡Ýɽj’»DÒ‡=ç­èµñù2´³6°c:]8áeÆü“ÉŠ´L‡àÓá”sùé1¢/3ú– ïòDì+âP=Sþb´fý™6‘ÔéŽüTÊÑäÑÆÜeSz¦S¡÷¥Ô@\ú=­»õ~¸à„¾2èoþèwŒ|çGH ë­ûí’kDRëö'½+'ß ÷œJ­è3(kÈ%ö%­Ò†m>‘N?r¶Ùë§ þ\è…>òè÷b×´û8óJe"©z0ç¬|ƒ„¬¬wdëðtz¼BC)mÜ‹D7Sgì°Âý´ûÝMø‰×]ȧg¾(¨Ìõµëú9òâh‹š®‘ôâ.@í&éµHíXsl:åý¨»Ù=K÷N §Åí7pšw•?'/MðãF¿ž¿ÌŸúyÿ‹ÞÕç·HZvïÙóêÜ$™³Õ¿?ŸNßýù[ÛÇ›Ú>/=œ.èWÝëAëÁ÷}~Pè߉~&œ«¿³Œ÷ÇÓC?m›7ô:!’j†6u;Óþ&™kÛ+/19jçä.?ù‰½Ä¥†ÓmêA5Õ.øš ÞÐ÷û³ þм¿7ï›f†~&½öëÕHZC¾dUÕþpÝ4¹MjhÒiGrŠ” ?¾í¸gaõÏ}´Îåât!×}¢ÐïQÝdª«yÿ +´{bþ˜½ÝêEÑÉK—\›p“t{] Ž‹:å}n¤¤É§ÛãKhÖù ›ZV™!øî ¯x<|øÐ7*Ÿo±$S>Í[ÞãÂâ(ºÆ³H¯ãA7ÉìáûZ½˜–N¹´»##›‘Z›¾êæå†Ó@•CZ§e3?0¼ÿèK…ù˜³ù­ön Ÿ´Zת߸EC7s?7ÉâRáêb3Ó©û¾ónÖèv¤ŸåEí•pßߪðˤÖ3ÿ7ôÆï‡zä}ùöåÐ~çœãÕ!jhÿ|qÁã7IX§¡ƒ/ÎN§nÇU½¦OêLn<°–Xw œÍK©El3^á÷àý£r  ß/¯sôóºíR·û!ÊûvÝ$ÏË:”NËV<~²•79~Æÿì¤}át˜ÝÐ.ð»ïñÅs;±ŽÙuí>(â]¼¡ö½3˜K½IuM[œNù¼Ÿ^$}—0 º¸4ãƒÃý™‚Ï^—B; £ºwù(øT㸠ö÷×C?z½²å™Ñ&œ½þç›äžO‘ÙCuéôŒw#oÚLA®dwÚ[¦™ºØÖÎj3óßN¬ãè'„~mâœ#3ô³ ºÇ®aY‡èíEG£õ¿&“m µÏ›CÒiМ_\ª SA3ïÖÅ øyá÷Á¼vÔæP\9eZY4‹÷µBûŸ«TotÝõ0Ò€»É$¹þ€¼½útÚÕ7+O½®qí¼gßÐê™Ð( vÁé?øa>#ÎзP_ñR±Ÿ™¯qLùʹW£6:Lû–Ô© vI&nÍü§FîO§sÑ$[Ft#!-™hÃSïB«• æ#Xÿr& Zûú“à†þáö˜àÎÌ×úéQÙ©{!r˜jë62pD2¹ûiÕ’c:ås á}c ,hš;‡~¤˜ÿˆ~ØèOj×´_ÿѱ²§º¦Y¹­šôš“LN¿WE´ˆL§Md“ªV'ÕiáÎiÝr5­ÎáÂøý½/)ŸÛÎüZ¡ÝUQaÁ7º¦öŽd2¸IZhwà7}Õ¸:Ô±ÀDzõe˜s1Cð9Ã<>Çì¹P7ĺÖBûË×}#+y˜åÊ$“‹¿{m«‘N½oŽý@*7§ëVßå©ï¦J;w˜)ÌòûÕ¿ürùq¤á}ªY^ôÓwëîU« ‡è×w"ÛÞO&ͳ¼F, O§ÏŸÞ9©£”†˜#NÏ 7Ð+ž’a·›RôoÄzŽßýgÑgó’íº€~~k}¬YI‡h\¿¼Fn‘Ž+%m¤S~ÞUŒæÎê·¦ø(µÇ_)ÌÏpÔs$Ë5~ì6¶ÍÑÎ=ß~tw»ENô=70ú™ÔM9&«C=2oêoß!:¼xéСfü÷ù¤Çü-ã“ ’úõOÿ”åx`®¤ØΡf¦üd¿6ʯO¢¨w‡›š{Þ"N¯«Ï<¼uw»áY)¨=:YzsÄb?‘säž!øâýæó ÌOÂqjmÜ|/:·¯è'7b›û/Ë¢èJÇèó{GÝ"9}Jå¶?”NfûtêUØ“$¥vOëpÄ@ß#};/™þCÞú¢ŸÓ[Ì”¥¼> ývãß(:ã@™O›¿E’G§N'Òé«ME®fìJ$sÜ×^1P>·ù[=ÁùÎËQÍ‚K?Ïi·¯‚ö“׸m ZIGÍ Z{‹T9:¨zé³0nly¬¦¤ 1oàŒb ôϾÅfµ Æs¼ïè7Çç f ~}ü8ËòÛ¡Ÿ&çŸ *I/9<ûuÔ-"¿8~Ãùtº}¦cðË/íIA™>α:Ó«©|×/?ú{¢5æ>b½ä¯'ós…~.Í’==zN®ú¬yÒ-Ònh]òæb:M~x={F½úäk`äàÍYZ5êÿ°wÞQMe]ÿu°cCì± €¢ ¨Ø8E[,hìØFƱ„¦Øƒ:ŠTÔQ#‚¢R®ǃu°KœX–t,¿}sϾ¹ç™yßwýæ]ë»\ ëœäž½÷Ù§ÜýÙ}nÀ q<ðû O¶óÌÀSA?<ñ”òøÒ"W4…ò«‚¡Ïo-ÃS²NƒýömSöÅüc©ß¹¬ñ¢O+O\4°_u>½òÌÖ¹Ó­æsäʽóD!. q8ú™3bѧÐÉ´mð­˜—C³ÈŽ´RÛ[ì7Ñ5þô=3‹Ð—Õá]’ÄzÓ…ëêZê—bÝR+Æø‹Ð~MÍÜžšDÛ7ã-;‹Œá /‚Éd…¾ê:l¡í3ËïÒ’ÎÈáš.öÓ-ùÔº{†7ž÷á%ãÓW%Õ?l]ß4K¨Ó¬…öשs\êÝÞMý_/þvsñr#nWЉî¨ãQ~ÙºµYåÄðùƒ¬2v??­·9à‚_>ëã¼eöèç¬ïèÛ&Èo²ÞL뜞E*_Q¬#Ì‹óÌ`Ôª4šÇ+Á÷p¯Ü¶Ó/ý-y»5çã æ R›úÁu’g“þ.š²È¥@e¶?ôÓïyÓ‚®Õȯ_ÌkžDgtSe~˜èø}°Î;Öó7ûCcÈïmêZs×.êqìÍ…h™Ž<~oOá9u]VöÁ[r^ɯ̓èªS¿^=gY—ãóêNç‹u³Í~ížÊ]åZ^µ‹ò´ÕÊö:ò祥%ºÂ<±Ã=éÕáÆ¤ñ¬…ø'ÑÆ> ºË¡b}WaôɪNv)‚< ³@û†¥ÆçßÛI#Ú¹=qÕ‘ýò˜ü °Om…²1#ïw £[§—ÊÞ Ï#åùÉ.^–ºâh§8cOéz)ú™UyòÙk#vÒ¤öá)~:¢ý®ÅÍ‘0ù ö¿Vjtk’ÕRWÁ`—L§l+v´T•¢qóÛZåÚöÎýCXg@»úAû.K¤]|#J¹Ž×‘‘ÉçÊœ?ÑxÛá’k«ä;½zÕ“Lõg¶$NR…ˆõõ1nã~—ÀÈ÷”>-´?!€'C&ÒV—ÝÞ©Ñ‘äøêw”оcÇ-|S›Ò~æ —dj.|ÃRçí÷Y?Šq[j—éÐÏúŒ‡wÐÅ‹]:oБ(™—þ€‰†>lÑâ]ªýPílÄà_“éâ·/‹?Ù"úÖ¯F®ÎoXŸßl÷ÐþÂò÷²ïÛAy*kíý:òÈeƺ`G¿µiºôqˆ‚®%|¡ôdú²þ˜¸01_Æïƒþ‹üIä5J9h2ÜßÙA›žX´cðy©¼õàÞGà_Ý–¬í´y-¡íG=ù¥ .™¶ü~LDjæO/ÄñÀùójÜŸzVóãÕ B\’C?s—ü4ìÜvº!³ñžø›:Òjtî¦tÈoÿ˜|øÛuît{ìí6ÿdJÌ ¥%ÄxŠv„q¢/Ú?lç¢0LÜN n…þZñ…Žœ|ÿk|'È7GÈÂFS–£|•àªNÉtØ^ç½»‡‰ë$Œ¯ÈÅÿ/TçÚoñ°ÿpÿ7 Ôà ®Í&·8ç‚Ï¿ÛkäÄ;íœHÂøøÛÚ'3dh‘ýSSöVœïpÜ¥ë{ ôã±Õ–GýRw×—{›Øe“Ú?¸»òÙèIô ™Ó†4zë'w˜–L·GÔ¿ÎÝ ÷0Àõî»æ/ uwµÐoܺ~ÍÅS¯g {õk‘MöÌËù± ¬›œû ñªo’».òÐ%šLU+’VìÁºÊODîÆWä@šýÚmuO7s‘:žzߨå÷Ô;›Tß?oà °[¯ÈZןùÁ†c=>$‹ûèß8Oc<ż ×Q}ÏÇ/j+ø FègäKòöçéñ4(Êö~¹¡Ù¤ÜTù”ؽ&c³yyÇç2r«›ËÅdÛZn}³XêBãxãþþÏQÊq’5ÉU¼ä”ßo€~¼[nSMù.›¸UýóÈ ø>ý/èrnI3*ûèä÷$™â§Ûá–<ã.ò±1?À¸.]gÊ¡Ÿ™<&Ù1ž^ÙÙlËýÙdÞ¶ºíÞÃ|twnz‰6„^¼Ø.nÖdºn×þÓm<-ãÞaÆ“¿³âÈ•+ÌKöïEd4ì}t½"»]¼ÌÒlÒ Õ›©‰àï‹_ßÐ%ËWüflÇ, 7ý:ëÃ1}’ÿf¿…yÐ~»SáwwØF•æÄ3›, úؤZœ‰ú_cÿí`%-3 ¦§©F uH;<((TÜ¿Åø$Æ3Ǭ5ÙyìÕ²*¯{~íó»6¶«ãèwÂVMJÉ&ªv Æm_o¢Åçœ]SD?:&®KÙÍ^)4^ÖÏulYË~rªqÞCŽÉ£QÑç«…ë#-´ÿøÌÏÆØJy:ãðŒlzgÖ9ÛÕ&4bùÍ«”´äùíÃïŽH¡+=»¤ŸlÉ/1®ã:óqäÔI×-éÐςɧ~¼uAK¹ä-÷=È&7N\~'[b¢·ßù;´=Õ››¤Ðˆ‘i!£ã§ÓÂ~máÊáü|éyúÉš9k¬– \Y=Y¹?u]±ù&ZÓ}ýÚ£üh½Û!íáyA0ž³r•%Çýsïw_\çMnw¼]ï-Bû²¦¹ŠW÷KézìØL«¥ô¸;³šžÜ¬Ðã}ËÙ&º¾ìÚU¾»Ñþ}ô+æQŸã) Ÿ2îºk3þD»§·³ž Ýû›!ÔDíB¿ÛÛÆ>ö˜›è>&…ººäþxs´…›‚ýá¾·ÈutâdÛ˳ýZè'}ï•øj¡©¶ï=ß+môÄ»v[ÍätyðLÇÚt'Ï—Âøµ!"¯çä›àºÇ[z~¢~̘rÕ¶>Ò“Yñušƒ!ïò°cy|ôsÉÿUÕÏ‹rGk‡û·X·^ºÓB?›Éä;J¬§ÑOø—žø{¿qúò£R´ò2ûiö?¦Pþô çM°è'¸nÁ8…óF!®;´ï—9*§ò¶u,ÞêI“…ûdU ý›jÕÛs¡µ}¼bèèw)´j¹ÙÞ‡FÌGð|÷y¤a#ô“íyÏe0YKó}^{Ô,= éZpt±ÚD—Xݧê¤VÔÏ{ìÓç“Ri£êgìæ×±p1Ðß…züo=ŸM˜°8åÝKqž’®‹e޹ ×C³Ò•±[¬'•® X?ÙDõã6o7vPÐÒãR§Ï;š*rp\ÐßÏÝ“; µ…«‚ü³¿@û+S®Ü kC“Ú+\{¬Ò“Vµh¢ ö~4®èN“ók?M»›JÏ÷!‰Ë,çY˜—àº;°wç:S.¿²äÙNœú9Ò§‘ËšC?ÒjkÃÙ1zR‘nœ’=ÚDSÎdø$Íò£n¾ïu?• ü›P‘Ë‹ö…û¬¸ooöh÷À¤ä–UôÑôàøô!~kõ¤RD£ˆÒÃLôƒ§‹ò§§=é{Ó/ÇŸH¥kœÈsÃÄóx<ïF®ò ¤ûÎh¬¦[UÇ1«éM—sÅ~Þ 'Á ÷8Oh¢<zÝqj;$~ÉtM*Õy_.QA˜8ïaž€~‡ëUÌÞ„°¯ª…~ 'D¬¢åö¿Úb»ž¤‘jö5ѳ?MxÒ³­'M˜0sÉž^©4}íà”CCia{)-ò60E>Œ”ÿ“ýìŒä°œn>@¾oŸž´)•aßÛDËßíœt“«BoïÔä}ÿTš›ùhF;;Ë<‹Ïý¿žoIó8#ôÃ?ê;–Òm!s—šÒ!¾Gt¸úx ®ä[ú,ôá’Tq‹v‹öƒ~‚|ÞÂçÂB*sÊUŒ¾ëŠ]V$íì{äÛ—á¹u[òâ%ä%!ÍýÆÍìL_6~´á\*å©OÑç-ëLœ¯‰\—ÂÜr»!‡~lóèQ·îb É•zú =y¾æÚ¸þ&Zynlȹ‚ä[ÆÑêçOde±¬÷1/Áý{Œ“f¿€v¹ûkOnýlYÕÛç–ž|?'¸›h¿[‡µdÝÓÇ×àh³ÅvÙ—òCÄy×k¸¯#å#@»Â|±€fî?u>ðžžl¼´“j¢¯"®=nÜ€»3ù¶‡gÅyz#î ã$#x€ë&äŘýú‘ã»þš†º?ôìž¼/xìŸÐOBÓis7VªIkå?h×Ë£>=*]ö¨…ß‚qÏ÷q>Äx»ÏÛ¼@üú©z¤çïI‘3é¸su~ý9WOü9b¢uì—þðý'wú[àîè OŽ~zpzIê´P1®{¸0,²ñ'‘ïeöhïÂs÷õ/S3†ù…žÚU8ãYò›J-›w¡Ú÷·:NìÅQóvyõ0Ñ>1Ž ëû’âù±t½g„öÏòxƪ4óà†°3ùzÒ~¹Ç€,°ËÄr&—IíJ/Þ/Ýµî Žú›®aâ>þ]x?ä•x¾'åèÉœs)ºQÓúÖDsÃ/(Æ¿×ÇAƒûæ÷4Ñç“?9•󦦵Ì?×yFOñ{àùáÇÍû:û û,rhWÈS{ mÃêUÿ¤'q¯ÂBu7Ñr?ö9w;¿±<"¬áh×Úš ‘¶ÓE{ÇqÅx‡y³ÙÞ¡Ýç!³÷{Œ $`¶ªVÂ@~Þã=ý@½5¾VâC/wõÕ”æŽÕ—´_KîL×Ù8žÖyžSJ÷ ¶î$î Ç®í\Æ@xšýŒÎ&šõ¸ir™}¨Ïͱº‚_U^š¬ýnI-ÌO,~n\¯,3Îk–½‡ÝöíeN©k2G‹ûh¸~Ç}W³7ËU$WçìQDÖVî5¼½.k_µŸ—‰ž®§?[vnGò:±€6ræh«ò2N ÇãŒë_è,ÌÐOã·/ÆŒŽ%ŽãNž4Í@D§ÍñIˆ_¼õú¹ö´G΄ܰe0>Ÿ£—‰ÎÖmj¯]]ˆ{»”£µ[Þj\:"Lä¸áýÜ_Æõ›t¿?ÚW“Ó£] \7uŽjªüù Ö)Â}‹VäÏ=¾sO¥r´[Û¨m“CÅõPá{KÄ{À¸Ž‘rPÐÏT¿É ŸÅ‘îeÇß}ü½l=åeÓ¨Ÿ‰â¹ÊvçÉA+~ƒy0¾æ²íûBDûÅqÞ;¢û FÄû,˜×KÏad.¹ŠÜÎk®nŽÞF4W”Û¢Æj^ò´AËwîZ´Ï¦@v°æ2G‡¾2µtɵì›âß…¹·–s ¼?cöèg–[LÕ§Îñ$/Yv´¿x¥Þûm8Œ‹Áp¥Òªï¼èE9ÿ9jÆÕ ¥¸®Â|÷O1žàó”î( Ÿ¦Ž|~*žŽ<Ú@漺û ò6»Çe'-t÷¥w½GuÙ°‡£//m-ïï&~ÌKâ<§Œ;I,ç?˜÷#Ïì7ÐOüÒßýo´M 'Ç\;ðÆâã©-ë ]õ©xMùÞ´z=÷ŸŸ%s´³óÇç‘eÃÄó+´7\§â=m¼tß»‰1˧•à7ÐO›®»GŽO 5ª¸9 ó~ESãÑ&ª)Y©n¯k½hé ªOâè¡Mkßó[Ü'Çõ#Îcëò;ÃBûZh¿ÔûZ¾óÕ ¤GjiÇ m d]ð1Ý,7Ý8¿û»5|é¦ëÅgõЇy«WAÂÜÛ–ùŸ×º Û«ÒvÏ!ß³KZñ²¼vüÚ¿§]¸þà¼2É ¬…çTÒ÷‘©žƒv¥õj\oˉ\Eô{ìçyÌñ<]:_¡Ÿ±£ö¾X–@l3Vv2þsÖpp„xÙ°ØÇ…ÛÐ.IµŸ¯„õL¥a3ݯ‡¹€ùÞGÇ{Ggìê:e‘°ï%k‘«1´|½í $:æµëþ¿oÜÑá^e÷¿…:¿N"9zü^÷Õ9OìõÈi7û´+p]ÈÇkᚸÂ2Þ>¿Í1ïMŽÄÑvÚRÿ=Ò«÷üvñAb^ŠãŒ÷e0oÄýé9ƒú9lâ/êo' vë ùiÉŸwþâëíGAN‡Ú‚:ª•QßQ¹£Ó‘ q?Õ#\}|ìñ>ŽpoÂ8­É[8r^Á/ ýœê};_1n'¿éþkD=Éÿ ¹¿¹%O}›_³FEÈV{oÿ&<¸Èùº°ž,&rÂ}¶ojHw!Â},…àÐO]óBai>¾æïªr0±ÔýI®&Z¬ÔÅM}¯v&˜£6²•gµ)r¾Žö„öµè ¿`mD¤ö«…~¦Õ®`tI$;V¯=o¨l õBÇ)ú¹›¨ûuÅõÈ83–Ï©1oÄý\7àþT}ó¨ÉÊŽš÷­mKÁO Å´à7—‰`Þ2» çäwí`=eÞŸq'ƒ‡í¡O6Ãø»u½ä›Pñ:ÆAŒ¿¸ÎÇ}"ìÏì'ÐOP{~Çb'Ö)Ò{£Ýè–íM÷BRœÿLᨬVBÏOÑÎ)î³ažû†xßOº?,k™«ÐÇÀì$·ÏWÙ4ÞÎ@¢Ÿy]ýLî:k÷¸ƒµh§I [×H㨦û£+´±%>â÷Á÷)0ÁçYˆ ý8xNŸÿ!k'¹ú:»XVuȇû֪ññÓ"2£bíft•zh^ÌÇùk%Áa"÷ïeáùxáuLYÁ_ ýºužô.n'!mFñ-o ­ £Ïjk¢ný.¶ñ«Mã*6»ùÆ?üZ¬mˆoX‘¸"œkÝÏ›0`ždöègK©­GWmÛIøÿò^O^|“X½'ô#Ü'ªIü6í¾ƒ<Ì|`dŒ“‚_ÞÏ­ñyJù¹ègQh¸iÇNb䯉ü¡'ßfnÏ‹ØõX:9¿[qtÿ…†Õ†u ïíÞ÷ÙÐÎ1›ýÚ·¹Ðuûl›]dçw5®öÊ×þ­ƒµ·^5õÏ›Ú̇÷n>F7š£¯OÚܽ|&Hœ¯ ß#¹ÅÖu÷ļÏ¡Ìþý|3*¶Wõå»Èw˾ŸÝäO=9\ii¾K7MÉ}³íez?2¤ÕÙ—)39úǦQ1Zªç-!˜ÄqÇ}q³Ÿ@û'çÜ»oØEæ×ýTj¾Ì@ÔŽ³Vìc¢u;UÝÐxí@²èì\»ž0î{¿HK´ ã<ÆáooËýE³¸æ*”¸øðæÓ]¤Fù”ÃÛ>éÉéô;×’T&6$YÇß>ÅÑj{_µmÛÑ2à¾=þ[ú¹åÐnƒÒGÞ”z½‹ RmZ¿×zzóΪÕcL´AöäÑMK÷'é£.¬M‚ü÷w2öu~°­íÏañ¹a^iö èg–«“§gƒÝdÎPïyôäRˆ3&š(ÿvÙ±-~äd^e7‡hŽöwäoæ‹ãŒv‹ãŒû‡h¯xhö è§Íú¯|¿›ÔÐm˜­'3^•}w|ª‰Ž>þAóMiáß2¿£Ñáü 6Ëy~Íâo+׺)ÆEüRÿÓ@?«êÏßuy7™^¥òGïL=yR:½ÅŒ`“×=i³È‘Ô÷ÛïÅ~Ž:güÖ=ec°¯ ŸŸÄ}ŸBï÷A?%32÷,ðK"›G:ýj{VOŠ•ÈÆõoY6þýðzKò1Xôs¼‡‡ýây¦Ù/ ýË¥þp 9’DÒI‚_Ì/zrèt÷‰ƒLtֲ߹U;›ÐöemKµ†ç5jý¡Ü-ﱡJýÌíýºÕûà«IÄmí£u‘‡ô$Émů#Ô&¶?Ù„þ⟵uŒóǦóòWÖ²œ¿b‡Ï÷ÿq|¤ÏEæ–«°½pÙ”ß6™Œ(8þöÚ=™½©e?{çÉ“¥·],§»š—ýÙüÙqLå“ǹŒö„ñ×Õf¿€ö?Ž[^iߺd’éÄ žÔ™þ¼wøeóÿ±6Ï.N äèʤ÷­ì¹7 Äï»â¾ö‹ïƒ˜ýú9©q¼§ ™ô9ôÄö§'ý[=oZ¾G·o©”Žâzº—ÙA‚‹ì«ã=w¼G‡ãû°f¿€~ܵﲱN i¹lÉųô$î‰ËýÙ0Îq›Z<ðR´'W‚µ‹õmÁ/ê¿hvCTd¾@Àû ¸Î‘®Û4Ðϲ©|àK!ù¢em ŸuìIß0}PñBí /r³{‰ðr9:¤oò±#Îjq¾Àç³èÈ‹ ÕoÄ8ˆù‚ôÞ–ú){ÃcãJY ñÚSÝÝÆ?%&µúÜ™&šñþéû'«{sšåÈÑ®çûîšÓaЏÞŸ*¬§?ˆ÷Iñyéœy½Ð­ß¡Ÿâãrt]RȱVS¯‡%êI¯¤‡Ÿ8‰Ö)ýjpÆp_rµ÷[U{Žò»õnNíóÜÂ}T¼¿/½O`„~6< ë?jm áwq*nÒ“¾g¼^d¢?÷ˆ¯Òµ‰~¼=&Ë£¿ß›»8N1U|n¸‡çd˜ÿã#¼' k•«Èð>éØ$•ÜÚÛõýɵz2 ‰žï¾ü|ӌۭzy‘ 5Wõ/Ó—£.³ç$÷\¡¦…ÏUÊ<¯Æý¡_§Bù¢úyå¹òAÆùTröFë…c¡óþ@¬ÔýZ7A;’ËõZÆŸìÊQó¶s• qý†þ-Øq1‚ï7á>íÊ=UGÎ ä¿ è§@±^5=82óD•JŽõ¤í§Ÿ¢MTøþ­IéžaÅZ¶æh\æÞgõË‰Ï ç?|×¹èÿºZØß€~šoªô[•qÉõ”íìý“ž¬ôzZÅu‰ ï“ÈÉ騟zÝtæèV~[íŠZÜ¿A{Ö%ïÄ}é:TíÏñ+¹kG~vh0øG=Y¸ÚŸ9ãO{ûžÂ>G…sC5E¿C¿ìgŠ™:©y¾¸žÆŸKß{ÐB?Þükíq‰¾[wÊb=ÙT¾uö=èý¹`Ejç}à—7{Lé}Ä%ˆ~Ï·˜è—¸Ÿ†þ(Ø{còµÆÒ¿§ÖÈ×:KB½ûΙ2cÅêûú°ú¾Ÿã„ñÈ8aÈ~•Ö´”Ö¶¤¦p «)ìÊøØ’žrpŽHPžUOUOëZé_â "Ë9ˆá ]}¾¾|m 7ÛÊßÀlÁéÂA™¬^z«—îÃØÙÖL¬ëÈø°ŽàœQŒ«'Mc|¯HPH)á F‚ò@*pÞ4½+¿'©«Î³mÔ HNÈØ°R¶²ÔŒÝàÊØ°ÈöBvC$c7(»AÊÐþ+¾ _÷SM›æÿXÇÈÿÍøøwcã_ÅDëxø¥X(ƒ YÑø¹ø÷¹Ø÷¿Áêú§ñîÿ*Öý·µ†ÿI|ãÇ4Qfa»f¸…<—Æš{m)JY¸×jä †kU{>`ˆÈŒQÃ]Èw@F—‘1ºøÚÙ¬ö0Ïw(°µ°®å¬±Œ6TÀ¯K%ŒBžA£#N³+ÌŸù›ç¦ä`à‘ <~Z×´Î)Áàmàá¨A:9_û >ÈŒ?PÂw06âë™ÀϘ3Hy„áŒ5ƒ,k)k&–9Œ ”²oÆß_‡¾AJp {þÞ!ü¤”Ô>åXÕ>WKÖÒÚç:+þ–šÕ$Föƒ5+OR“8ä1Iû˜Õ:Vë\û5ûš¿Éþ}ù›#ûL92͚Ȍ“¯­žñŽ ²s$,B0^5Hò#Ö‚l‚ç³ÊÁ #Ay ã³Zó·Ò‹gBäH˜¶`ôjP¦„iÃs¬•àÈž1!2‹0–9E (äÎ*)ÁIÒ@rp”HPÈ&ñ3uØ¿Ä"DÖ ²y޵ä#‡Ï²o`áX+æB¤ìó5’ V‹=–ÕbW2–µmS ÷ÆÃ‘ï>8¥šñY]Á9cŸUÕŒFàoñë~ÿI\äÇ!MfaÁ‹ \ÅXf”Ö ke) ¿ZÊ)ÀXA6`°j䆫åT`Àé GÆ‚åe Ó£,”R‚q§ìÁÀÃAF ] ²‘ð«]Áè5 Œ?dÊ”0yge€äà‘ <>6‚s¤}©hØŠ WpšXæ8 Œº¿º Ï¿ÙËy> |*­_Úe‚<ÇŒgÅ*ÁÉÒ@¶V\ÅÈ&|MD°y ¿Z Ò9ò5œàs9 üØ@PÈ2 TÀÇÅæü{óðàœQ þ=Qø=+8j,¨¤råß_ßcLEdWs {p` (ÇŠw¦A>àÔ‰ [Æ•EÞY,sòPÈœÝò‡çþ³šg)ú@ à¾æ‹_óEÙ¿/_ôamÈÞlÈŒS Ò\ÁHc™¡‚2»1 T á7Úƒñj@9 0bd†Θ³®`б̨sÖŒ;T #Ï9‚¡G @*0ø4Œ^2‚|Àø9ÆÌ¥ƒÁ4 £„ïh˳A:8‡dʹ‚£Ä‚ @*p˜4=8”òù Σ 8“ZÂyäYÚy •œgÁçi`ai+ÁÑA¶àlá  #8](§1_3~²i"°Ó›ðõWáw›òõ1ás€Cr {'¾NœÀU€s&2îl (äÑ\àmóÎ(aAjA6à¸jP&È8–9±Ê—‰?ì”RS§3æl(¤OÉÁÉ#A9 Æœµ‡Wƒt 8¾”RAH“p¸ó@*é G‘ < C:ˆ¯ˆI_àÿ`ŒÄØøWq‘‰_Ї_Š…ÿ›øgÍ—•Æ»ëþ›8÷¥‡±i2 CöKñëŸÄ.ŒYÿ4^})VýOâ÷Óe_Ö C[¼( ;¤*%°°#Áh| q [0žp¤#*àï­€!e€\O–7(G0¨( Œ*$‡Ø£å€|ÀÈA¶Œy —ÇßMƒSƒt Þÿcï<À›¸ºnmºé‚PDh¢›.0E4oÑE%`º0MtÑMh¢ B1-8TAØX6²i&”Qhqh¶1Õ‰ÓL„r×xæ c“üíæÞÿûï Ïó>|1ŸçH3{í9gÊZvá¾ŠÏ üP€!À+ôôР ­ P˜N)ßÚ t(P;ðE‘š»¬˜m*¼cË z¤ƒÀ B–Æ®(d³à¿…ç‡QÌná¹áªŸ²«ƒý/QŒ‡Âv ÷)PÜn E‡JEnq@‹¾*ðÍÀ tu…w¨ñï½ ENu,Ð@6!ŽX @l …gí‰ù±n CŸ° ÷i!3Ð@õÏ\êÿ¹T ô;BAš ¨Q˜Á èP và‹"µhQ¬¡RÁš@Рpm QÀ±@ƒ"¶èPÌvà‹‚¶èQØv©¸ÍÀ ´(òP©ÐMÀl(øT`DÑÇ ß â€°T`„œ@ 1XA2ÐCa@a˜è »$p ÄbéÀÑ„„ ’ *ˆ(ð儌[1/Û[^È×ÄçÚ Ÿò²¨+ ùMh!¶P ¨ <3ˆ~ÕOv|`„cýb|6 ¦¨ N àk žybž¶/„j|mÁï?*ˆ6ð@§|ðÿÞ#qÀB^`‚ ã€¢^`‚¸ã€ÒB*ˆ=$D¼Âšâw)2¶½À„F´h!À+¼[†¦„7»CH¬m©ÿÃó(Öÿ³s©ÿ꺒õÀ¿»ÿýó+ÖëL>bcýíïœgeíkÿ]=í¯ú™ðÝã„c„"Ò£ˆÂ„ûè_n Cï²/0¡¸â€ ,Ñ·bÅf©À ¸Åg¾(@3ˆZb(ð 2ø = ¤#ŠÓ Ôè[Vá}/ªWxÆŪF±ƒd`@ц_á]À- 8T:¡›Aœð^ ÚR… Ô(nèQàa@…"·¾¬G/d«c»å…Ìgü>Š>x5B*¶)<  o%!¿ Ÿ "à…g€!„táùˆAí'xâc\`„(b…{t5VŒX€èj {øzðuO)Á'I¸…ûèA麂r?ˆ*x âr?,¤ƒ@-¨!6 ࢠ*Ï\À´t!º€Ä¼Âš¢ôƒ(C€àDqºèÓýá×8ßçò•ÞçrÓ§çKس%ìÏBé»ê§é%ß-Ù× UÕRMòϼFÌÏ@Ì+KáX.Õ»Õ³”kP‚˜/žýtʃó:9滘뺭ڑï&ró»{(æ]$cœ‰å7¼ÐIÅ2Œ®Q®é½´!…«åé?jh£úÔnCã%cìåîÇ~™´þÛ‰²oós¯>~^xþ÷²_*óad¾Å_È?Mßì½ÉIußwêñ]Ð5ÚÞ¼…Ïór~"µ_uûú‹‘{¹å?^¹µ{Ï'¿ 6˶W™3sÅ/e?󨑾?ך)ù/cœ}dýµ“r<žña÷k´ªÓO\¤=… úFç-[® =:ºâÂÊ^{¹#5˜,ÆÉþYs™˜7óÑÌä/‹q 7/¦‰¦ [¹æ×hö®°6•ÂS85ûºšCzÐê™\ÿYù¬O Œæ˜Ÿ óCa~¬,7S™KnÂö…ÅɦhÊ-ØW¿F1ËOv‹Iá–´¶–íГé6Íÿû½\¯olAÏr›9æ;Äþfþ$ÌŸ-“ï>¶¯Ñ©wÛØhZ™-éÁÍòר렼òMᢾY}ýN‘TiqÙ3;¹½Üêe'›9æÇþΚ_ÅòbæM‰õƒ”ׂqvåkWejöÚëûáК²×èñÑË—ŸJáF9mþŽ®TkÁè_\ÚË]›:¡mÐüÑr^;¢obªì›Æ|%3ù’cœ-Çü¯í©CW®ésO}nj>Ny.…ˈs¹×ŽÜ¦ž÷º?ÝË9z»¿¬9NÎÉì›øÉÿù?Š9¦bŽ\2ÆóºcÈO°kúâ5h2fW=w gŸ3¤õ<[ùN{WÒ7»“3¼[Öp€g¼ì—ÇüUX^ó\¥ü‘üëÄíû4HÓWh~MT;†~ê1Ðu®À5ª°èÝlUB —«C›-ƒ.Ö£y-ãwõb/çÿÃËC9“'|–{ÁüøŸ¼ìè«W²–2AƒqŽ®¯Òô\­ªæÝu_ŽkWá÷»‰)œ˜ÿ^‘®”Ñ„~ÿó^ní3”fᘯ_æ<ì—r.«;¥ß¯ã”X·õ~•ša(™D¹WR¸Ö\ÿÓKæT¤À¨CzÙörýæ/·æko‘óU˜¿ ó'W~~¶[=·¡Á¼Š1´º–‘DÙë—Ö¸šÂ?ê )ö°­³sîJíör³/LJJÝýgÇlGò¡ 1”·úN¼ƒÏÿCv]ž¤nLJ€ß­CZ*•z­¤;ÿ^nMeß>úíãeŸ(1Wã®ḭ̀ۤc»åë4y¼®P mÑZ7")‰æÔˆ5ûz Wþ‹êîNÔÑúá¶¡oDq¯›Ëõ¶Í'¿&¶}æ³ÊêVù¹]ؾóÝãhÒT묾p6‰jfئp |¦r,•ùå1¿Ee^–OÃ4½˜gMQB öþ$šòmß…AR¸)¦ÏÒt¥}íÜ…¢¤:-û1?0–‘QÿØÞÜzBc4=Ú¶;Û‚IÔ½@ý«ž§p…º$ÝÏ3µ;9söºú*Š›½¦ÙØŠþ#?ËÓfõÃtÀÎÊü-=Æ©t$¾~'Ñ”û¸>‰’§>ì½è] ×,àÀW;Ëö ïò6朥YþÏðÏ|¬Ä|ìßËÎJßfƪok@ .$æ`œºhð¹<Ü×ÙšÎ-6ÒížMUo²—kûòŠ«wÞr²~«Ì‘°b{ÅO鯷ÆÐt!þÚDk®ëÏå,èáB÷ž¶ªݯið/Ñe/7ªÙ®Çgë˜eŸ/¶]æ›Ìö;;Ï*sí§ñž„yŽÝ1´pX¶/b§&Q¾/G)÷Ìù2ºZÕ6ôuƒËkߴǼ@ˆ é5ZöÛc9,/ŒùH‰¹¡’/,¶üÖû§ƒÄÐàvÑ7âG'QÎâÛ:?*á‘òešöÕ Y—Êíåv ½èÿªÊù¼ÀÎģŒÕ'ëÛâ÷”Î glÞ7ö‘ûbhóø¤ †$ÑŽŠÏÇ(åáJÜ6zÇ–­EO_›xòI7òÑÁü¦Ž–û6û>ì|Íòõ˜£Ò7ß§Qš^·h}¾{b¨xÈ+rõK¢±çª¼­PÚÃU¨Uê`™­•IÌ9â¾¾µ"×¢–£>óÍfçæsÅæQJŸc Æ…̧ŸÍ •y—>Óô «žÐã@4µ˜_ºzÿÊI”+|‹å.›ãþåþƒzQQão¾=ªGqÛ_õoÖ½hü}Ø|‡ùè²z`óß­2&š¢^0Îå‰3k쎦É[ïu+DÏk|C†x¸¥šÉù6¼íJ½Û¯œ’»]gq6®W¹ÚPy>Íü™?0ó'eã+ó×ôçåÖ³£ºý‚ý–±I¢>ïûQÆìá&Úž÷ðë¶4ÿ«—Ïšu‰âfä Ž?1|¸ìÏÏú3;Ÿ°>Ã|þ”çuÆ9ünÉ‹ëW£iÈ©€lÅ*%Ѩ›®ëáR Ýùê^P dX²DÅ-ºä[þÙù¼•Õ×þøšµvÆÜûÓw+Æ0%&ñkîÓñ¹úÕ/æNðp3GîÿªÃ°ºT›,ë«©¢8Ñ·z¤|^ϪSq^í‘çqÊãcÇ8º…5δG“Ø“h~•‚•oY<’¯²M+)œ¹£8×Ìö+òÖùY.=Ëd9o¬Þ•y.Œ3xÓŠõ¡#£ic´¹i3Ÿ39ª˜~äáš~Y«åƒÅõivîÄMÝŸGr3Œ'G|æ+Èêšùà*}“±ýˆ #V–‰–ô–$åŽx¸Ú®Ú…Ê>mJ˜±]䦽m9p§q„ì'Éü0™ï8ÛOJßM]šþÌMM¿š×œônB•f²cÞ¸Ý4¦ÿ ägß’Øq»#¹M+}Æ—¨;BöQg¹,Ysq”¹Éläs]»{bÞýcž¦|x í×rטÝ{´£ó‡GGr;ý…êòq`¹l^ÁúZ¦ü l?rܲŸÛpRà=uȱ[<- —}äB´ïH˦ï1§G$÷‘º†j7~šo1gvœÅ\à;òþWæš0ÎÐïæ, ~à¤ÈWÎ’ùxžf·ÙÝjÃ7.~ÁùÛíJ¹¯Þ¸ôqR¤”û1TöŸf>žLbÞ’”‰í.ù­RPÇÊÑd[øä}Ák|­‡[R,Gåúî¤Ë0¤ŒäæLHØöÞ0ä³<6ŸfóÞÌù‹â8vŒc*WÿNkc4-Ý¿‚º'ñTk{‰[úÍnvFÐqwJÚw¯~üQÜmnÆÄß#~6¿cëqV¯_¤©œ÷Dqs#Å>ïÂ8ÅÖíX?s^4Y¹U%ì§z³Út^ºÓÃ%Þ|¨·¦m+ßÎwU£(nx²õÛ(SYl>Á®/0ßn–¿¢Ì±KÆ8b®C4%~7ÓàÅq§èÙè7 eó[^t ³o/œšR!Š;0áŠÍ4@ÎÝbõÅôÆòòÄí%1¿HìW>MÒô±e¿,ñ}T4UÉåé—WÚG{¸€»a†ÞVTºèëÀ·nÔïÏOƽÝ7Xög}‹õw–“Ã|´•~¾š&Ÿ¾áýË|ù¡KIã /x¸Ÿ[ì}¾a#ÚS e¼*2’«™q€‡È:ÏìCþ0€Í—Xˆ~»b_Ôcœ!áu’‡/ަ]6ænûœ§«7›_®pÈõٞ¯ò…SÕiitg®ÍÑHnYߦý.:³í‰ß“ýà3Ÿo$½`œn9rwØÖë·¹WG^|ËSýäKcc=\È‹B–+Б„-Õ¶î‹äªÙ~,¬=ô3Z¶ßØqçI’n°}ñüM=s}¿¦i¶$Ê»g»yÆaúùŽv³æ—§;3ÛUl0?RÊ9*ÏÃØçeyÍ캜2çÇŽíÏ͘`9iú–‚oFæÆ|ò”ÌÔ=œxžñ£ó„`ùHy‘9/7½}ý›“/Rå|2öse.Ž ãˆu夗5„$º$úã>™sÔʹÖgÆÀ¦”o‘/Ne‘’/íy=Äö;lþÅ>‡ò|˜Œq÷ÿŽiNjZ~k•8ß$Zøªí–b?y8!Eûþšv4qůÃ>6äJ?"èG“<_aŸ›]—cëöï™ò^š¦é×.ß¼¨Å#'U2Ÿ¯œ–+‰ö ×öpyXun|gR×ôäJ} H91ÏôÙ÷óÓïeÎ{Áv«m+w½~²“tÙÒþôGžrì(ÑäíEwtä%Î¥t£u;ïÊ×.’Sõ+<¸ê2“|]‰Íټ̓ØúT¹Ÿôçb·‰e‚q>‰nþý—¯xúuל&…®z¤¼n”®.÷GßZ‘\Ñ…5íÆnƒåëɬO²$–_ÁæÝºÀöƒ®¾šÖi’“&~\}ß}Ÿ§{MWìïuËíéÕ鶪['úPý«kGJù/ýå>Ìæ%,oR¼n÷L>ïfèÛŸß³¼áV'ÉSOýÔ·‡x<Ü ~å$hÚÑdóêG³(’ëYåì­ûÈçs¦s–%æ²½’ýÇ•y[vŒS?×nÆIÓßË<•í{§Ò}×;)þCP‘ò·m]×!’ûúÌña)?õ–¯'2²ûl]ÇÖßâõK±/º0NpÆwÒ¹ö=âOºq<šÔù®ÔC÷mñÄlýrjivêü·ß6‰”òüúÊu›5·†å°°õÉó©¥>$”Öó'EýK¹í#œ4pWÕŸJÇóôèŽíLåGNô—.IK®o;¤d$÷åháÎÉ@Yï¬~Ù<ˆå[³ã’é|Ò,M¿è¡pCÂI?^í0»/Î÷ã–f;´ã°u¡Þ6,.ú¥ƒÛ¹'ïŽÇ9LòõqVÇìú»/Âê/SÎ Æé±Iwøöv'õi!œùy ~°½âsŒ3¢AÝy¨î¨åWº_vprúå“^˜zÖ¼@eÿÕcû£ÞׯŠÛIrf«?å!O㹹ﱇó”*Pt–m/ïupÍ3‚?†Èë,vÞbëF–CÀr2G0NôÛJr|ë¤y–•úíOÞµË_¤{¸F«Ì»¿9¬¥ÅÎÕŽ\ìà¾Ï±Æ9¡Úù|År’N®[S7&ê4,™éº‡ÛçÝ´¬—“Š­´ßãJªöÇ’Ï=\êACµoNÓºÆ8¸óùÚþ¸­§I¾îÁÎó¬²y˜òxÛ±}1ÑICC¿ØÿËoËk`ùj¢~îËûË^êTži憎:Á89{ª³e/eرCµ#kæ?—óù2Í–:« MZ>l•ÑßÁmçómü6°¯|Ü™X~›x½è®|…ù¨gè¤yš¾Vzñ›_wÙKß.¤:ÏS—³kûÝʇûøµ ¸®TpœìêàÞp•jôjÜGî“ì¾Ëb× ”ûKƒíï³~vŸœ{é»­ÎS9Îâ¸G «èóÅÎÝ~J)‡;“xË!­KzËónÖDz®2tíž½yÏkžE­òúûˆí.³ëgŸ/ïpº';kíHçg塉>‘\bM!ˆ«˜˜;kÇ8E~œsÅwZ<5åæêhžUÖœ«v‡k4ûÒŒÔUèËÉÕ=#¹Åýަ(_·eã1ý±<†ïT“;\ž&~¶ßcÞÚqÉ…$¤¿ç©áª‚Õ~w¤ëfÏfe¶Gro[ûw‹ªn’s ؼ”­{Øõ–û‘éþ:ÆÁäzEÌœrž]t–û–§šêõ‰y1ŽxfgÀõ†"ë¥:8ñxùìþg½¶Å§×,¯KÙuñsHç‘iúøKºÄ'{¨TÆž ¾ØhÞu™²ÅœYuï~EÒ-è¿Q…¬±}†pYóÁÙyWü\§Äy\MRæfh0N猺{è—~­ÃÙxê_÷à=—©[OKYÛ%l?!v•ƒK·#çûEƒ¸Ì¹–ÏÄy°Wº®z*@Ì5®•é:·ã”€Ú çÛC‘æÉ›¯/äIç^ñ¶á÷—iû¸õm¨°¶ÎÁ-¸Ñ6tÇ€~«[6^æÜÓR=×ÊtÚ„qôo«vY°#œ6¶œeúy.O_—|òó£ —)ï”yUËê;RcŸÒª Q.Ôÿzñ·¦¾ëW,'C<þ!åMÿ$çí)û¾ãü8Ê¿kë^á”1Ü žn¨¿Ýºô2i­éÛÛtêJÊ_)}à„CÊêDZ¾Ë®?«¯=¡ÔUÄz8Àò“•ë`;Æ)·tÃîÉmÃéÔ#·S=•§˜CW½œq™’¶î?WÔÛ• ‚/f%88ñþrŽ×ÙþcëaqüÃr¥Ø*‰úÁ8‹WJ|Ü'œüŸ¬Z2Oªë篖sYšÿw¢aSôE_=rp7æÿð£uC_ŽÝeõÆæó†½G7>»úcËíd9‚úÁ8›V·|\±s8Úq`Ç…‰<ë^³óàËthi‡ñ9j·¦jÝúv¹úE$·ëÙ´… óûÈuÀæwlݘõøˆ÷qÄq| Ÿ"þ4oN£_GøÜÇÓ°½Óçís™¶lj”Ò MCÿnéÙÅ #¹’GSkü{sìø³ãÃæ•â|édÀšss ÜìÚ¢n°ýßò¯ Ÿß=œê z¾çâhžjýPöÑø^—IÌ£-FÍ>NKÚÛ,’cû‡ý-®³¼².3ôí]è?µìžêáR¿åÉãßàÍOØÞì={{vRkW­Säëґ܆)ë$å ä˜þØßâùð4ß?§IøœîþÍÞ £€€ Í& å)0ܾ# û¡÷šÅÊ’–»ß.°×©û®ZHRî/^6`ºþ­à¨^ÉÃjR‹­ß–=+îg+¶¿§på'eΆQF,ï@žºûÌl<à2µ;¿ºé®µõ¸ïîëÎÁýèº=b˜IÎéûù[)ÿ¦¦XïØÞîèc%^Ü £¯¿YòºL OXD†Ô2É}»4°èȶ‰®Wö ¢uöæ)!_ÙE ¡£Fî9œZƒÄçÀ²ÎŸ°ä ˜üë¼;¾Õ3õ7=ÆiÞù»ÒÂé‡bSfv@}îØ¸Æq²ûešòqGñê·ZÑݳökÉmܸ¯Š&åéÛ[þ™óÙÞˆ÷ ëHóq>kÂöË¿Ž¼²ô\8­Øwß7yOǽ;Z 2\¦;Ô¡jçc­)lÒµž“º:¸»9ÇMòîì)÷iÖw¾à_7¿¶öÓyG<Ô“î{‹9ôVŒ3.¥[¯FŽpÊûÛ¥3EÆ`Ýq÷ýB-.S“]çU³µ¥ƒÒGuäàøÄ½ÞÝî.V¿Á›ßߨÙÕ‡X¿a:óÉÅçîìgͯ5·~½3œ„«…ÅÑ×¢ûì«Sï2m»9¦Gé»m©Ø½°ïSF;¸žû'ÕÕÖì%÷ ö}ÄçrÓu—c㋨Ô¤þù÷¹/ŽÇqaöp÷a‘áÔuî«Jm‡ñ4­tâœ1Õ.Ó­í“¶êÖ†<›qº'æâýÖ~YúÆ)Wò\ÇlÞÁ®¯eèã/¿-dË¡pZݪ¯÷m?žÊoùñYßrÐ{³5U›ÑÓ›kþ²CïyK &y]³õE©¿Ãäþ»b· ù>ë[Îg{• zÀÖâu%±ŽõØþ©Êâ*ņ“Ï'·ºC'¥þhœ¸./úJ¹™y–~hJu&zæÅUŒäí¿.I3T^w°õ8»½á΃úïv×Ì4ß4aûVÕ/!C ~ Wtyºï=Y£IöË$æ€éirí~Öƒ—\ÃØÈÚÍë—çl‰9p^ùxˆÏWV—ÖƒÒzãLng©¸‡:OrŽçéa·· ½I${ùí?e³µ£/¯²U_íànM/°ê}­ò<ƒÿEݽ•ëŠé]ù¼€ã,;÷4_›n{HxŠ# ó™o>¸Ï¯}Hâ:ÅHŽF÷6äàŽ½?lŽ &÷aö½Øspìû°uM¦ëW'¢ïÈYƒì¡mÙ^ŽHœÅÓ·ò¹¸dl¿î›mïn6ÛCCzõ¨f_ÊÓìEGFÝw%ÒTÛ/Ãùô¥?5œþ‚spâºÇıëѬ^ÙùI9ßói™¦?prðچ軧Ï%Þ±Ž§Kºý>yw"UŸ¾-åûÓ}‰"wPƒçî'=P!Sþ›ãœ|:+wâ¦p:<ñ›7‡·c?E¿þý›i‰¤ñ Ù>4ÏWÔêÛ\_Ì-É-Ø.‘û"û>,/‘ÍÇÅëU3í7ÆYPBž§?rr}OSÆnCÃi…eçìcŒ„A¦Þ-$ï/¦q~Y5Óý4kËOùÀ—"¯¾°Ûyò_~¤åþÞ‰TÃ0ûû‘§ÚQ¡ãÚïj¨#¹býóä™9TØs¨¬NÅóxyi'Ý/Ç8¿8Y`Eµ=´¶×®ÐgU|ÿguº$RCýˆé±-Hc솩 ÷M÷«×þè5\îlÿ°çŽY]±zU>ÿåÂ8ô‹W–íµ‡Â¾z±dÄVžnµ;œ–Þ.‘jg­p^­ $:FÐùVÍv÷ÅþYhÿPÈÞ4‘òùÇŶØÜŒv–9_Õåtp¢>FÉû‡]Çcûåð²ÓòÕlHl¾“¡lÿé³)Q~Ut²ý©uÔÍ…âOŠÌ¬Ÿ(ý^+ºTñú›98oß½[úR‹£åú¹×yî¦ÑñÙi}óƒáxýLÏ÷è±Ý¦÷g§ŒùÕA®æÃÎ.Áç}$*çk¿Dºµ>ö~ ]{Ò^iŒ3¹ƒ{ÿý„ø~£åþÃþvE,+~íVŸËª#å½Kçl_ºÎDÍGý“úíW¼yrñ2‰ÄîgÿasÝW˜×ˆ×»F~ÖGÝgƒò4ýøi=ÍÎ;Êú´bœi«s:I 7ò iø-tu,nð‚‰´«O1×­BXOcãB¿NsߟѸÄ0yÿ°uëõŽíž­*ºN%Ô»Wê5Ê×m”Ï÷ú´IÓ¿SO¾ZôY4 W©Š.ä)²Ã€±'$Ðâø û÷m5Rþß–ZáàÕ,ßÓc‘û&«qž•]z.ã¨ô¾OõLój ÆyœöÕ°)úêë_?n ÆfM$H÷óúP‰Ñ]ž¯ìéß—`:fóCv~l—1-Ôò9=¶¯ëšïy¹‘1Ô:»ðfO­FÚJ¹[&Pý‡ßÜžPx mè)\(qH×ÕÇ}ÖŸÙý'v¼•÷ÏLØþšÏ{\#åêò䪞§ÐÉŠ T/¤À­w5M´"Aõ•×ßÁµáÎyûë§óÛ_Mg<<1zɧ>Á¾G†°ýÛêìÖ¡¿ÆÐŠ“JYÆSÀ ïõo³%І×7KÝ<˜½¸Î_sGußæ)4Fžÿ°qÄãõiÝÌÖËâùLZ`œN÷¿Tk™Ïv¥°oxúXó7®®'ž¾98¥DžßúS!ï;m+è9Çú§OüëŒÍ’gÿùzvÓÇ>e›Äg>Þ.ŒƒÅ}‘â{öÑÅÓ·_åDÝ.ÞzëJµ³ñ´üáºÀ¡_ö¡£‘/®u8¸ÐõO—É9N>ïd½Þ Þw:ÑW“±Ý#ÃÊTJª´ŸŒ+³·ùi9O?±Þï{ žŽ_Üà·|zwØãÀÍœ·Ü›íñ¶ïf—ç·ì8‹¹îoØ{|l=“¡‡¶iú7®öS„p[z¾HÎïê\tÏ ”§­æÈ'îwM…³%÷[>»Ãú);ì9Aå}e ÆÉÙgFö!»÷SGé'+ñ=æO>raS<­¸ÇßÓ¬õÎÿ°Þêxv^ž$ëšÕ­X§OåzÍж;&ÿ¯NŸÙO¾Mž|Lξ.˜ÿ£ÿºxzÚqͽ·ÍÉ0!vÕN¬ƒÉr¿fŸ_¼ú:ÓùÌ„ín©­Ù_îø~šRyÃÇ9<•ôX—¶"ž>ŸX9¸õ—U2­W­Gü|ûéb©î×JcÙóÇ=;êØâ¥y\c¢Æ †ï.ïàN§ìèצØäÏÖ‘ì:%›±ù'[/eèãÜ ùý”+,|ø˜Ù<í>٫͆yñ”-×…-ÝZPŒê"¸Rž:'ŠÝù´ÿÙý+Ö¿Ù<&Ó{JØ~ñI?l͵u”GXØ`ÝmN~8sF<56ÄÏÕ,kMïNÔì°õA÷Ý¡´¦½zNúì¼µ±ëFÊÜòdŒÓfÔ„ôE¿ì'áéš•žrÎÕ~|íÒÄÇ5  ’O·„¾ â©O‰fî7ƒã©J÷‹º-ìJæœl³ ˜ƒ+\éyÜ„ÏÖgì=¦‹ `»?wj¼´¥é]©±¬O_ž*O7W=æé%êÝÛSa˹º·jÔ¸/¯Gp³„Çì7L‘çlý3õÇ=Væ v_°Ýþ²=§oLìºs†0NsáµÈ­±t*Å uxòiÖxÜ™û—èÜÝ#?Ü=ª§úg–$;Áe¼thЬ‡㇤lòæ¬Ì Uä€ ž›Ö,`*g0H)V™Ær4Y<ë,R–C¨t’7+²Y¶ ËBü³l”e*eÛÄ*<6S%Mç_xl²læU* à Ü@د”ùż„Ã$ÁžÂ&E&¬á x «ý¹—à/¬®ñÉÏN~TYü…•™°JOÍ)ûPÈ­ö ÞšbœÂg85K&¬YòÔÔfñÔd¹°Á oòP…7¹+‹7¹}ho.< ÙoXxÓîŸyÞ?ó<«Ï¿Öp]SÁDlfà–<Û­R¦µM!Tj fà:ëRøów÷Èÿúã?½ñïí±t5ŠÑ RAÊ¡P¡0ƒô(Ð0à‹"5+²(„ °t)"LÊ‹~(ÞE˜ÙªA!Û@:”2[Y˜2B‹•Š<Ä*ò(’E&ä‚IÖ,“eé°LÂ?ËÒÑJÖB&¡¥´JðãˆÆÒA Ä«ÈÒIÖ¸’JƒÿÉÀA9¢²‚T`„¸b 3Ky­ê*b~Ëk2+¼B.!„ç~_ð‚@ˆÐü DHF2h¤ì è!Î0I ,¯ÕBµ‚d —rt|¤'ð…x-€Zˆ8TÊSf¶¾¡à¥+øÂŠâVf¶Ú@*0JY:‚àÍÀ ´~¨$~!ÃÚ |Ñ, hÑ B¥†`úgÞøÏ¼Ñç_¯7ê¤1Ó…}'eiPœV Œ(ÒX F¡ZA*У`òdi¹­BÖ˜ø¢ÍRn+Ëó ;h¥ÜVeÖ˜…n¾(v àE ¼B¯Dñ»€:K¦u,Ð@ 6Œ…¨! +Hz$ øJÙ=n ƒXìÀ‚±7ÐA8¡À LPÐBD! !¦X  ¬ 5øÐ@\6^AÌs?-¤’˜i­—²Æ¼RÖ˜]Êlµ7ÐA„¡’ÍÀ td¨$JpeÉ´„H@ ¡ZAj–ÌVp Ä x ¯'æü(s­}!h pK¹­6)·Õ;"<Ð)r[M:Á›¿ÓDðŽÄ¿7¼þ°¯/š€ð@+å1 ùÖ†‚¿ þÁøÁ@¬máÏ?=2sü?Õ…Þøg}ñ¯z¢²f탬²þ÷W×…~'ô:¡ÇýYûö¶«¯ û0V8–($HF”¨QTV (.'P¡À,À ´Rþ˜JÙ¯j)ûÕ ´(¾PE™ÿê‡B ^`Ê’C&¦¸j¾(Rp RAÈ!j)‹še(Æ"CÑ€bv5 Úx)C1T*n3pŠÜTð¯Æ6¥|éX †Ðø‚Ï"~.å´Zþ™_ý_Ÿ_ýs-ìßž_J¿#¤¸…¼À„~(ÒœbfµÅ T(X à…¼Âûa(`'P£ˆƒA20 ˜Àm<У°Ã€ Å ’Eîjº¤ > ø¢èÍÀ üŠ~ʰ6Aq@ !„Jb0ðƒ(B€!ŽX †@‚A20@(N †X¬  š0à áXôx BŠZˆ)x…÷Ã4øÐBX¡’¸ÌÀ t™!´0à+¼ãœ@ ÑYA20@|a@ƒd`€À b´w–œk3p?4¤„êjˆÕÜ@ ÑÚ@*0B¼öz™ó®ÕþB¦¯O‹íCС’¨M … Dlâ¶T`€È@ ¡[€è ø Œ~,P7üðñ³f‚9¶)e^¢8:@ðsÅ¿#‰õ-•âßö¾ÃßÕÿŽ~Èzá¥þW{ ÐûL>ÿ==OÙïþ#½î¯æ^ʾöoͽ„ï'P(ð‚@’ ø¡˜B@:DQ¹€…e<СÀìÀEæ~(´d G±… .¸…g¾(> àET(Ä` Â3oÂ5}¥hQ˜¡ ¢o¹€ 5x üзl ¢€]Âõ|q²p-…¬B!ƒd`@A‡ŠÚÜ@‡â¶_¸ø¡ÈC€¢Ø]À¼À„ÂZ¨$3p ïfAˆ ¨  à½) ¨ à •Dbn …Xl B4. ‚p‚A2ÐA@¡À Œ’¨!¦X  lÀQ…IÂ28àÙ@:0 ×è ÙvØ–$6uS!»FÈaÁïCxNὫæ‚ÿ¼à ŽŸA„N ¸­“x_@ÙÙ³vÂ57“4'ô“ÞMÏò~,›êï~Yï~Ù²¼ûå'­5…^¨|Ö.]zÖÎEŸÞ—î¡ÆQæktnúôTÒçg6Kÿ[ß>M_ñö‰URb‰ùÁ•îXzh5^Îi˜=ôø¤ÉÉ—¤ü»W¯}nnð¿Þ–˜eÝ›•sÏ[Á1_Õa}úµKk1•}ã%ÿ{Œ#¸Õt.v˜áݨ~âÙòŠFJï>÷؉ùÜ„#î»WO“s#˜ÿ‡˜§ñÉ‚ùZeòmÂ8WO\S7;LUÿ¸7Ê¥áiûÏ·Zîà.ÑÛ0÷²Ãz“àfxtJGaß;{bºìsÁ|‚˜ßŠ2§Ò.ø@ÜòJv˜âÚtÜ7£ OQþ¥îÛw]"Ñ/¬?u?28—w|÷¶Êï‘Ï'L—ýE˜ïËõeþ(âï•&Ñ'EÌtaœUr•p­;LîÃ×_ÆãéÖÕ5Û¶Ú.ѽŒc’|ë#¸Ž—¶Ø¿jªìƒÃþf¹òl? ÛMÆv›ôÛ\ïüaRå{äétQM¯ç#.Q¥^ƒ;ßï6„f¾°«êOÜ“ðͯÖmž,ù4ýÀry˜óÍ÷¿˜êcHÓxÞÀòâ0•¹°zýàÜ< {±æ@ó¶—¨äˆí[j™hNFe7£¢í¹%û'öyr±ÛÄZ™üÖ4Ønü©f®µ%~¤Ã> :nøp•fVsvô¯v‰ôOü/öÎ*ŠlÛû((˜s›[EÅ„`ì6Š1ansë0Acl3ꨘ1­ŽŠ’%Iµ"Ѐ¡ æ6·¨ˆŠŠŠúíê:§º€ ïÞ÷½·¾û­a­ÿºwͽ³Tí½ëœS§þ¿õ½ÖŒƒÇ—¯5 a¦LgŸñ׃úÇQaš¯^X²‹pÜÎçC‚ã\ñ_QÞÆ% 62Ÿr`góN!«¥CVƒ·3§Œ„ÁJÿþU{†0;X[œ»‹ê‹Âû Ÿ{z–Ÿc”oa¨'aæ+t~ lkfâøéu(32Z~ÓÂçz[ $aC iõ¾íÜC˜ Á¦âü¼%¼ õ{¡>xôþþÒvËÛͶ%ü¸8NKUwó›ëbàÇ¡5Åsžæ€£iMÓZùZ­Õ«íñn.0ÁºaX—·ÅÖ¾<ÿ˜Ö,{š(_ûÙúrÿœp¸Øøx ÔH°œšñ0Š«Yw?óT Í—Zìo0º¬ÜþõUºP½)]å¼niLCÞc¼ƒ³œÎFÇ€sËW›ÜÉ#{ªÚvW  Ä«®VîÕÎ=–< fÜNö=õ⾜¿.´¾8Á"g¡©ã~n~gÛéØˆ¸;sÏþŒ˜¿­å¨ -4ØÝôÔo±}avÇcoc‚™Š—6˜èäLI.Åþ~Rÿî<”y’[ÑÇv[…ÄçwšfËç¾Ö$k¡ÂùøŸ¹ÀäŸKæÝù5˜™ËÚ>ÆÉyÿWzJrÈÔ¼/ ÐïFŒã|zûõÚ˜#1ôp}èíÔXZã∷1Z`®8Î{1^ædý9ûÛÞvÉËð¢èõ.áO†qwh÷.jºC5N\ºœc“z=º{F Õ·[ŽÉ¯ë ,Ý·N·`f©g«®çËÉy©Ò>nBŸS){]¦ÛG|‰Š8%††Ñ9ã“’µO ʧØ÷¾†I0ó«U“¹"|xŸ~o±çé;’ßðuK}s>®ºè}sÂyÀq–^©YÝæ` ông7mIptXV3÷ÐZ-ühvÌÉtûü¹ÇéŽîÁÌ»ƒWV¨cì—%}ú_ÁŽ%øVJŒßÐ`üyÎ&˜Z9p1vÊQûyZ¨z1çþ¾m#À:£Û~ÓƒóV›Cº.f8û¼çSåHü³ /ãvϸÞ'®¨½}´3 ìv>ö«…ž‘/ã=€ žÔý°(˜é×-À¶[èbÞ7ÞOêÇDý‹„¶O3£7T¾ó´`@ëN\þã8w{V ÅÀ½ìº¿íÛœQ7׿®çªÕ;ÖàÓÖv½q²…"˜ijÛ=·¥OßMÊ}àúŒ34+oytàu[.ÿæI¦ ËcàEðÓ©¾«sà—OŸ명*óÅóφ›ŸôÕ>ïfÚì 7óœâËs6è}Žœxetۉߜ«Å’ Ûœ€rS yñ ¶Õ5cÀáDܨ>¾9P«û¶Y[k!¤vL/Wp`íÒf’ÙÇb-c_Plüþ¥FƒbçiÍz˜m3ëBþˆã^8v}Á¼Z1Ð2lêø ràèaù¢ µðæXßžåkõ…m’¸°Iª`&ðŽ§¹Ÿ“œ÷©*Í{¡~øÔwÑÿby¥:bÀî¶\•8/ÂCûõï]G íó[¼½,×îݚ̉f mÁK^&/)÷‰æ‘!ß1îð‰íŽÆZÇ€rķŽf`¿ù9.e[5-6Zöyf¦+»Óföɧ¼ºZ`.œãÔ×q†SÒÿó á‹~t¦>¸B?\5Æ·-Ç:cÅÀÌi±Ãs Î¤ãÏÞ$|7Wà|ƒ™ÙCv¯ë·”¿Îô9D}eià¸YKhÂùÊš¸çI¸ú‹1tåšn9P¿Þ¹Ôwá7a®ý¦«C®Œ‚6â7«‘g˜q =® ZÆ_wžGeè[¦@yJtþjÈsŒÏÍ›báì;é–ûÎ9ðtD³[ß–ß„¨M¿ãîaS°…adâ½×í\ÊûÒ¿£ãøª·DS+òóê,ôÍ–à8§ ˆþÖ(¾ý‘߸k”Ûë¾sß›°o¼øÐ™ì `°©¬Ìž¸õªùñõJó³c¿À×/»ç9”³ÎRµ¬‰O1Ž“[Õ»÷…ö±pî@XëÛ8à´dØ~ó›Ð忏 ÊO„uQ ?Ýž’o)ßòÞ/—ÞwʹúÞ+pœ±¯æh  v¹írÀ7)ÔqËõà9¤útÓ~ãaÓ­ó[f f®æ÷ܼ҇Ÿ‡Ð¿k\½¹ýž+>ðuÆåW ²Þ!|§Øä÷n ÆÂ³}—E;lr 4¥RJTà ¨mÑ3ñLo/¨öóeÅÊqÁLtÝ’»³Ï³Òóú÷˜9 ©±Eä#ç}ÛöQ¾¼ëXäL¹<ÔŸÝP'ƒò$œzlRkâuJΦï|î_»´ß^àÖÁÌáéuæV^Ê÷:Ò\Ê…4Ô Ž³}TäïÇÅA#yä½ý•ðù)n_ÿB£ìÖÓú—Wøî]¥Û~ÁLu×Ñ/ó{,+åÃl Ô¯•úrógÂwÇøQa54‰ƒ;Ym6¬½Új•é ¨*ùÑa|Ä@к„ôœÌt[|<æ-ãýÎi~¶.¿tCÜ|ãIw¦Ö^d· :„4|› «»­­ôæùu«sí§µ?Ï.{¯òfFWª»±3³´ ï‡ržhþp¼—À­»¹u˜ÇÑäº9àm,äW˜±¾ùýlذûkµyI×a3<ûðL: Ëõ¾ÁÌÔü±}œõ~|žÒü¡ý–Öõyä8¼\_Wâ8ë>œžº'#p›m4ÙÄü:tßü"9f8|͵W}ŠõE~~|}Óuñ½ŸîÇ ûêÌq5mK<ŸÕtåÕãkb ¯;jwñuì{zäé£Ü ³¶…w35mÙ?~ýB¯ç#üƒ¿^t}éÉb$êp‡Ç1 k7cÝÅXØl6üzȉlx°oÛn÷ë`ÙuzùÛ.£á([&0¦Ì¬|8GÎïKÐëÅñËCþ¸ÐZûž´,ÉÝœ'¹ÉÚ¤þ NûW> wúÎjÒð:twÚ]ÍjôÏ Ç fμË<ÅJÎ?—èý§ü¡¿ãÊ–,Üðx[,pûÙpãRû ó®Á—hoM°á63ú»,Ï—ç”RÞý}…þŒû}cáݜձpyûìÜ·›²a]¯kA}£®AŸÇ ;Bd^`(· ÁÌšûºF¨}Ëð•8î‡ßo„¼ )Æw}Q\;ã׆¤{‘벡×Ç7)²M× Z:éh¢Åhôs爓ÁLñSIíkÆë1Éþ¶m‹`£ŸæËIlAÙAü]Ô¯ ï ã纞 [¾7¸¼Ë'Ÿ¸y&^}•¤[lG€åšmØ9‚ןi‘²rKùç4çwÈ›žêdÊ_'C¾cܦGg¸ÕIŒ…7û[–›=;Ú>y8s¼ã5Ø2užÅ‘Ïð  Ö fÊO;˜i|>Ó¾Éíÿ”#<ð‹ÎJ‡:§í.Ø•œ/á8uÙéö‰çÍãk›˜ Ïn¼óiÓàá$ ‚°ó÷}Çõ*Ç9XÊ?Ÿi^rÜžr¼¯)çóß©$OÇq~1/Ç}x4Ñ ¨²o$ö‰_ÆÞø3 "_lÍ_Öqœ"©59!˜Y°óùöYs—–©/Êk£õeÈ÷!y’n)Û‹êüX£ÝlØáÖíì†çi0ǼŧJJ7†«t+Õ¸^b·mz--ãK|°Åùªzå€ön^Ñ©D‰qœ"–Œã"˜×¿õÊ]»I½l®§ß[ÓW»ÜaiЧÙxŸmOß™·f¾ñ:qëÊïä9Т„ÿ¾ãΩ5¡ÞÊ8ø¼­A­)¶Ùð9«J!„§Ç÷€†Ì—Í f–À`KËÔk3KMí)O¿9SžÇÿß÷îúç=¶ÆK‡˜Ô<vFo|zSá§Œ€±Y;·-´ fjè ml7Î#[I‚ž_ïeÆå9ƱèÅ=ãÀ0|Ãl0àP&¤AXÆš¸„£¡^ô©àžuƒn±¬,ÿäqv¾¬CE¾žèsVÈGUâ8ë¶ÏªG8EÙ`Ù49Ѽ}œí~©÷ì”1°÷æû^Q=ƒ™ÎAÝk|7>·ö½;q²SÄû­sûHœßºãö5ôâÀ3{~«“UðùÚ=+rXQ*èÑ%ý×¼1€‹öž]÷ažÏ`‰å~üó•ÖåRÓüã8Ö ·„ãèpœ»µ·o™]ãÔN=ò# , åVïS!ÍÔ5jUC/øÖ{ÒÒoÁLƒGoz[h|ùyNå[æñûž”JÇ1äûÐß‹·(íw¹Â¤óÉU^H¸|Çqš¥­Ù›ã!F?}Ⱦ,8™Ûþ½KïTèwîè0ï™-aŽ|é‰ö}CÆûç²R€oÎ!móÖ¨*ýä÷e8ŠC‰þ¥ÆqªŸºìˆŒ‡¤¯[9lÎ×ëg×ôH…ä‘ =¿Mµƒ£ÊÚ¡÷ª„0÷ÀxÝ(GãP·)Á+ÒaÜû/oÔºy;Þ¦íSyEÔþ`>Ñß.ægùîV£;l¼WÕA÷!˜iÜüÞ´~¥ö5^8˜5isÜ'Êc&ëû–ä÷çæß&y’s{öýžæ.ÝàBYåˆSášîÁ«±ö}àW·Úc³¿3Çy9¤€*óÇ¥Ô€Ðú“ÖyTA0cÓOÝlÄi¿2ü1ú\¤åÉÓ÷H†:Áqzùn³x"Þæy»]²`Úù—S÷ßN„Æ“L€}$}Ì´[—åu厜ßïãxO|Þrý­?¬sÛÐhÜM.o¿KÍ£5æŒg`~[ÏÉsšeO{“œIê}av'çÖncï4y“yóÀ”u”7î÷ÏÚµéòëÆuÇ3íO fGwæ8dJŒ/Ûktz:âsçô‡+gÁÚÚ½Ö?Ü~gx¿bÁéãB˜âŸŽ“¯ÈŒë_Êgàêâ-¿î­ñ¤kx^ázbüKF*ë®eàCãâ«å oAŸƒ¾ŸÇíM¿ûs.ìhξ.‰aXjfÄ%þ>ÓùWç|¿æ8ïÖ%¸À:gÀ€ê}ƒw1±ñSÄš‡· úêóæ[R€ã«´Q•¾O ?†0çö–Ïøa.çûµ²åÊe[k}äŸë”›g¨‡axk°Þ»›&vOËi· ýH¯IµüSàI§Z‚ÌiOQÄP“Pƺÿ4¯Ä†e×]”ïUâ9€qGµ«µòôN¶nÿNqöí¿:Ûik |0»2$b¶SaVÒ±—¯C˜QoÖœj\Ñü¤ûNt½Èqz¸u„ã×[u·–õ>~äd¾÷ˆOv~>-0²f^ºÑk~wfHï•ÝÞaÜáÑëìNÆü§uÜÙG?rk¡3å ”àžcü§·ŸkšÍ€í™þýìýnbës‡O¤@oå¶æIIŒé¹>;ò_„0˜<ãŸtöã×W´qó©WÆ÷w>QëëŽ_·ð¨¼úÜø†x :()0¡¢§hà½6L½©ÒÆÉÕC™Éþ‡â ÷ãû6åbÑú¢ïÑ„ï”̪߰£†'¿Ül_Éã8hVÝ»}68~Ä3çÖwFº}Êìùø`KÇ¥üû_º¥ûIÂþ£F÷ÔPýÜ‚ØÙ_îMÃßûÁ©¡õnÜéóc¦o,ÚÊ ˜¾¹»E¥LIî|žKù-†<ǸǽæÍêj­·Ë-¸çšºø·˜áòTz×µ/á+†2½.ÿxuï¼1—ÍÝQ=ëm5~ÿK˜/&žy’'ë÷Tn«†¹©uSÀýÊÍ”CrÈתDKBC™¡×ŸUìÛØ§ çîrÏßv`ÓFer²*Ç÷Sc|­tø‹aïXµÁá—‚øLø¡‰_R½A áM¾w¾õöJöœg¡Ì²/‡[u~`¼þô÷§|å䘨­ Z•àjë<)D AåÖþ>48¶ÊöNšÜûÙSÃBš SôqZaFxœò2\¤Ý¿ýÚó­ ÿ¼¥yY¢ßϓҹ–v¶8¼zǾLøT8'¢[3¼Nšµ6fõɦ›‡¥?C™‘õ :%¸øñ÷›Öݧ¥óú\ö1ŽÓ¥úŠÕ PDÎÎY— ¦› K§ý•jhšzrü£P&àòæá¡ëë$:X¼fÇûüïù:°ŸpÇÛ«-¿_e¨ ‡]Õ&ب¡ž›G±hQ&4¸ñÊùBC|.nrXÞ¨^Gxµp dЕPþ=6Í+ú÷Øô¶Ößêaä]Òý<:ž¡NpœFAê”nj}sÀÛ†“2¡îÎ5Lk`ÿ¸Øi_e]/xñÈìdfL(“]§ØÆ÷”ƒEyêô9`¨ Œ{qbæÚ5žjhÒ%6?Ô- mÈÉ?4PÁis׋ýÈõ eL~în;ì“O™ýÚ¢_FoM}ÿ•¯kºO(ä$*qœó£Îw¿1W Ý—”ëØ9¶Í)j]þ¹î÷~=èè‡`ØN7c¤Nì6®'-ó5û–hÜ¿¢œDá~¡ãײ¼”˜°L ÷>´KgÂ8¯{ÖÁÉx{>àîf¯¡›ÛòÌÅŠaŒÝo½ûþ4óåç‰t}FϵçÑ:Œ;«‰º9³R µl¦ÔH°Ìm¦¢wÆ) Ì:Z)ús˜ÙŽ%:‡2®ÛWt™èÃÏ#¸øåù~Í­ÿÜaJm³íóßäêaDžäú˳¹ËÕÀRºo|Í€_¼­¦úkÀi^˜ÇØyCaý8ç;›„1çVv¶Ÿà¿„ÏzýçËZZœýbÊ_Ÿ°[.¼<€Ì]¸zÀqâä0ÁqØUs#},OÍ>2K«]úýÚÉg0¬Õ½{3amÃîzܹ„Ÿ¿që¥/ä=¨=Ðu!ÿ1î–3“p¥¡†:A‰ºÉw2àŒôKàù!Ø:ȧ¹o²„µ ¸=¦0ŒaiÖÓ|Êœ»jPe„Õà<ã9#Ú—„ûR§k¿:?ªÎWƒù«Ÿž§e³ü씨h®xæyò±¢»¬|´Ç:œÙzh½Ó›_~}Fó“òÓ„\PÆÝÑ—%ªáÑÁ“7-£2 CŸˆ:]:hGÛºV0ã£e8sµb•Ö³~È˼7¢ûžt?UÈRbü—N-¼3\ ñ÷Z»–;–pûAT¶~8|ºYlU8´±VàWaÌÒà°¾][–íwÜ~´)ÿž\¸>Rcü)ƒ¾.kÓK ë{ïwÙµ%fp/€ãl?oùÊ{ñžü0æá•˜wùcÿ¡ë{:o¤ùCçÿB.ºÇi—Ö9>ÿ+[YœKN_´Æq¦uN··?UŠ~í'3kÎÌš·^ÕÀÝxž€¾Ï¡ýšž3â®#Ù™'¹ã’¶ç‘?ËeŸ™ W×y÷¢¥æŽ{»s×Skˆ/:æ4Ñ-œ™W{Ì/6‘Æþ@û]wÓ瀰?ˆ1~öé÷N-j¨aÍ™ôàr±ÎöE9ÜXÏš­ÛaÚjœžU/sP8sôCƒØ~ë$Ê»äæ«‘|î K0þý¼î/W2&6–gÞ.—“Õ¬,Ÿ M[ª¿nªm[»ÍñÅq¶®9¨ë9ã>+Ýß ×9vß·§6’.%öã$ßJ5êÍ€xÈz½z”T“ÅGªUÞ=1žuŸ3Gmß’¹Ÿÿ̹;^z¨ô~4WsëR{~]fÈ{ŒßâX`\¹ñðíìùá‹¢Ó!éÉ÷Z§'C£¶ÍÎY³cÂÚ²/øÃSU¹SýÊ<×ï}(žu¼Gø°¤þÌjÜüPqÙS@ÊNñW>}W§Óé°ãÙ¼f·ç&ÃwË—åœlUè‘瞇3Z-\Nܼ´ÌygzÝéó„î×òãŸ9¸…yS)üzuï=éжâñ7%ƒb׏mìë3Ñž7×x<g²|já­ñãó|Ø“Ýóç´ÿ\"ÿÔo{ñeõ¢·—ÀqõK\¥ó¨ð©Ç’dàæõ–àҼ˚­¯ÂùëLû wÞê;ׄœ5iBK ç‘ ùñ-ï­îñ,ýLߺÔßÅ7$¹7ðN†¢Vañ-¡iî»Ib ¿£Ïo›17Mg™À\‡Ëƒ8=·gÈóÑy[QžêÊ%˜}ì¦kýépø,|bèó¦—àr·Ç)óf¤ƒÉ¯31Å’ÁºÒÌñ÷Gtü¶äTÏ¡*†åÖ 4þþ´ïQ>2忯ØÙsNóÎd>ñO ÖW¿ÇÎ]k^{\:ô¿hÚ †}2lé>õõV=!wzÑPóé@“Ú»-áŸst>@y¤´Ó÷,ÂuƒÇ¯hvfUŒß6Z¼Ô3.ÿuvj‹d¨zWúÞ£exìd¾·Å4ó®pà˜ Ž>üy•䤅嚘›ñ繄çÉÕwòÇÁÝã ¨nöV_×t˜>°ë÷“É<£%ÙwT1žïó™žõ-³Î}=ãÕúSÅ&ä¼BàöǹýXÆßz;ÕýG,ÌÏqn昅ªŠV5HæŸ[zžªâ÷Kç÷þÌ„ÏÊý{v%æe&^yÃëì;±`7ède—–é°ï¡¥Ÿw£dˆ–ÆŒuÛ”ÙèNý“TL×È9¡kßSpçšçÑèþªðœ˜ã¿›zm‚»&šü82¬z:±}û1Þçúï]«·bN¶ºÜ0#HÅdœÙ»x÷Zãþ>íýöx¨"C«ååÒsu%Î5à8 Uóâb!·ý¾­7¾hávæ,Guëd°Ôï9¿G æÛ˜öýßïQ1—{lkÚv«¼Ìù:¥ç§i¿~'"Åq¬'?ÒüJ,Üø¼­ÑK-¸=è¾ûhÛd üWûÉ]ÚÝݨb¸ço©uÐ+çÓmæ­>]å;wÞãqïËcaõn¶P´ðHw¹Âö–É»£I»Ãç»BöØŠBÅ^G5æÑŠß>>ì>Ь$oãen´ š¿?ð!¹·|†.EW²ø\;&Þ¯ºsÿŒþð5ãî‘-T ÷\÷)õ=Å ¾ïÐûJ÷…û¾jçTʯ÷LŒ…®ž~=¯h¡óožóGI‚MâE•÷}µ÷=©ãÿ›ªÌ>}FŸKtßE8ÖaüÃ5­vy7‹÷Ѫ­ò³Zp™r?Ó.' œ4ÅoŠ_ÙÒMƒÓ6q[ÒÑ•Æy}ž|^›¶ø‹¸NÂïcò$=ÎçÅ@Ïa.>'´ÐôHÀá‹IÌÔ¾uR9 /ú-¢ãms'`Õ «¦Æó@ܼ¨Ÿwâ1ÆsðçU§ÿ8 …I¯Û™µFV’Ö2ò+¦MŽó–s~*~ð-“wuqõ`¥6ç¯7}îqó î¹'Áq~Lvß~ri T)¸ÿ|ë.¼¯Û®]–ÎσÜÚõi‹%5?½Q1)bß'mŒû¥t¼¯ë\}Lššóù½ç¨Éå5ƶv–ȹy ô¶yûD¶M âîOú{ÏMŸŠÅÚØ™Áj˵×TŒµt±ß¢(ã¾#½Þô½"å‹sÿ;_ñG³Û*W¢¡Í+‡Z™ë1þÑF±_g&ìò =aÊ_X[㛊)?}xXmÙëD÷ñi?ãžbrþ¢—ÿ8NØŒÃïV,ˆ†*êÐX?-h–K”g%‘çn}ÆõÌÛâcfÌú µ ìç¾éxôÜ=]ÿ Ïß©1~ù©+Lw5Œ†‘î=R/ÏÑBÇ¥Q¶.ó’àIûnŸ6jÅ$nwífµ¦†¦å¶^V~üú†Ö?·¿lì3´ŸeY|tiÉç€ï¡c-¢£`Y\€eõÉZHõ¯"9¾8 ^K-¯5\МytË|§¯eӺƣIf[ýx.1í›ÜüÌœç\ 9æ&cq«Ö¤ðŠ‚ü>‹»ŒÕÂÆG+^Z’Ä'Xéä1îå"˜ª,ù¤ñ=2}þ–þŠî£qÏ{n=.ÆqžÃïã-£`úÃ:øèÕBO¸¯—…Å¡q9¶c[¬[ž§bŽ,T÷sSã{Zwt>!<¯%Á¸áKÕ½¦¥^„‰ñ'_¦…דŽšƒù´±]ÄòSµzÀƒ]×ÍT1[º%GŽò-“¯ô{"ºž¥}®Äw8Îu½kB›Eaíùoú ÐBÃfƒ+î•…’fUW÷¾ÛBñ/W1o>æoÿ|f C÷Çi£Ïõå§Ô´}jÏÕƵTWìçßî"Ø6eßXjaßÏÃÎöN‚I‡†[Ç\qA±Ø–¦bN®nš¥zhüžŽ;f|éiñ´¦«-yNqçt•¿SÍ~⇠ðIê¹|y-ì|°Ú½FÛ$ò^Î ÛTÙ*&½™·µVº¨Ô9‡W„oÌSzŽM¸ß«ÆqØ])“  0hÁÈÛ¾ýµðËS³‹?k&ÁøÁzˆ¬â «ºêäðPÅ€C—é§š.æÏ Ðû@Ÿ»ô9 |/§Ãø“ÕùuàÈüRо·‹®ÌÕxý5ª8µnÁêþqßIòù™Šñ==¤Eã÷tw~ô¸wnx¹^ H\®O˜ŒË“L=ðrbë‘0»ÞÉ~ZÈùÚüh‡‰À~>æy¿—ï‰üNÅÔÕ6(èë’2ß«rûÎt^Âý\|1ÆggÍ…Ç#!kLU2ÆwŸ}¤(;LEºk_õ„ì ²ÅæLͺuôHñá¯KÉù[ÿ)·_į’`|¿q/#o{E–¯Ö¾¹ÿXùk͆\K„n»WòíÀ}xŰo‘FV7®³h¥yνâ¾û“b\bo˜´a$ŒömaÞO÷/¿hÉ>O-¶æGŸí¢fy¿A›¬—ú•ù®…îÃÒu,÷~”›Ÿ(0þ¬rwF»q~ÄnÔh!bâ·¨×I‰d~—?ý[ÀÅñßU ›5þù~üïMûšÎ~_x«3åûn5ÿÞe‹nU»Û\ÿTâ8}šÕk7û<Ì·ýq¡µ›†L{^~_r"ÐùWým/:+¨˜'Û¾tèáÇEûw½Í U£¥[6~n_âý«ã)œ9Óü<œz]¸6㧬¨ØqäÕD¸v!3Û_ÚšKo|õ†Š©2µJoËeßCquUƒ¯¯¡ú­ßE÷%ë°ž\þã8þ•³®'žƒgý[érpœ)cŸ ¯•Ó7¾óª7D|ŸökõsÆóatœÐþY’sy~^A×§ô;nCŒÏ“8ÞYðöä9ØXgÞ·Z`O‡×W&ªî–õjOT²uKÅló¹WûÒ‡¡ÏyÚßèþÍSCþcÜ(ý´ÓWžƒšëÚ)=†ja]ýJ-B–'’yá¸Ü8hx“û*F·®Y£>KÊìWÓóã´ïsÿœ‹/Áø#®öêv°xFí®…;‰]2/ Áû{`¶ï˜ ^ðÎfOŠqJ_òÈüÅ"þùÅíg;sç{òçB ùqe† š³pØ5ñgc|®|PÈõj˜…³ê|?:6nªØF^9‚™ÀHæŒ[\ê¼Ûþû:O¼XÕÑ=ea²ÑΘp?ÿx?ýÿå­ùŸìÿÄúóŸ@D2á˜a§M¸öZhMŒ^v”!!dˆåŽ˜?áë}Ò)_G,àëHˆO:å$ªH²KžÂÅ’ œX‘€±C½Ò©ÿ&eìx&¶Ðc…ú S¾õज á%/<¡ß õV’â’ ü…)SÂøD±G}¢XNÖ3e&Ú‰1¾˜óÆ£l –5æ]Ê+ŠeQ<;ÂLÔ/ñÈó&~Ã6ÞåÈþ‘_Tácn¢Œø±ˆ‰Ož–ð%„LÙ@Rð^OÖ/Oò žyÄ7/Š0ɤ„+K=£„\Ya’ ¹²”I¦&žQrwz€À3JMزrâβÿé©ÿôT…ÉFOµ k®‰Ñ£Šz´ ¹<:Âå ì ¡Ÿ1eOØØnÄϘ2Ë¢ˆ¨ŒøUÙýO;e5Šü êiL=÷(BNø³vX(¤X¨—(eôPß=Ê p#ì2–Aë†Å¤B‰° Ñ â/J=¬ìží„ÇͲ)›õÞc½Y~™£ã‹9/+êÛ."üF!“›åõÈ­9_+GÂ/+`}ޱP£ˆ¿•œøŽÚ•âõDý ›»ˆ°hU„aƲh5„E+x–föXfeC|® P^ÄëJè;%"¬n­€Õ-ä<Š ³GÈy¤Ì au+>ǤH ³Ç†øðéÇŒ­'öçzê¿ÚOÿ¬—þWû襇þ;ýSØ/i¯dû$í¥{ã¿ÚÙ^Èö¿?ê}´Ï±ýímlOû«>&ìalÿb{×µoý+ýJØ«¬ÈÿÏ¢œÑweäùØ,WÑ[à¥Çz눗eI9¹„#@Ø8$¤ÁTôǤ*@yÔOÏ‘øé¹nË‘zR.å)R?=–!!ôÿ¤G¾a™‰”{íˆÉØ„c‚ÉPZ1ç¡N9‰,ï:eÁú£r ï†(ˆ_zI`–‰È²­-0½ BB¸¯”U-dRO:Öÿ\Œ‰íO5,Ÿ&$¸¥&ljTaSG:!“ÚƒøàYaÈP”aR8q~x¬ŸœìŸyÕ?ó*“ÿœy•ˆü-:öžbÂ*ÞŸBöž°o 'BècL9vN„ñ1¦L/5ñe}Ùs‰è_y³Sî¡€A½ŒY¶·€¡ ùßeåü‘§1Ûÿ'z¢°Ò^øgóµÓä>³½ú·³C!ÿšå²ükçw¬GI*–åO(‚å|é GI<ÛeÁ/d¹×¨"v-)àPÈQ¹( &¯’p(X¶« ¥E9Ë-,ªÃ1(X¶—œ0]m0Á(=Û߈Oû_qp4¥X×,%)iSŽMá+WÌù´Sf!˺V¡¬°Hä(áàP¯vẊ±püQzâÕnAx…,çÚ É¥G¹aA¡¬Ø9*·Ǧ Ì/%ª¨ÇÀ±ð­)§eßýqiö ˶V¢,°0e( a[ Š8¶5Ë(•âZ³>ÄQ,£‹X‹r$<ë"T”3Ç©øgþ÷ÏüOaòŸ1ÿ³!¿«ž½g˜°A$i¥åKòw GI8^˜Ìj”HÀ±pp,XNX”€¦A‰ˆ¼%ùxÊ?´°,<°(T„{-°,ü ×U‚…„²Âb‘¡´?x-ÊQÀ³ð"¼0–íê…R£l° Pz”VJ„Å¥@éP7¼”ð¯Y"å_K°ð›rÌ071ÆGY43zÄÛ¢ƒ-ÆÂôg½â ß5ˆ©¥A‰±XýQ:â/äù°lÄ?â`³l /¶Æk.a¼ú£ô(‰€é#ôŽ÷FiQŽXø¤øe,;eMÀ¥%~î, ['`a ™Š, ;°W‘ò~r ;U€rÃæDØŠÞ„ýã(ðuwû§§þÓSMþsz*{ŸU(+LVoT®€»(dd³ÜE–‘­E‰1‘ýQ(7OÃKÀÔðp5XÎYa±ìE+Âú3ö"ËÉ$… %l „¥C¹aa ø”)ëÊEIˆòU¤p¤„±!®Çq6X®¬’?ª€í­XPQÃÒ–âe»aÁ¡,°èdM9æË—•£t(G‡‘efG¡DX ”ž°‚T(+,No™µÁ" @ <°XU„ÄrYf¶nËšEy`«P"vî‰ÒFš¿€‘D [JxAV¥¸Ù”Ç(!¼ ¶è¥( Ê‹?ðxA,7;e… Á¥%Üì@Ò¤„Ç(Æ&!/ÅÍf†”02DØ8ä¨\”„p³Ù&â…R ¸ÿôÔzªÂä?£§:’ߥ€½'˜°*”&­¬£¨ˆ0Š‚H"KQ”ZŽÒÞZ In–·¦ðÖ´(1&»¥G¹aÒ¡¬0ñå(Ê @… ’,§ÛU€ò¢ˆ"œn*eG˜k,ÓÖ E…a±x£rQvX4¨\”‹'”p×X®­¥AÙaA¢ P^XXj”‹Ë¥G¹a‘¡¬ÃHMX“Bf·²)Ç^óc|”¡ ¥%,#–7)ävÛ`a ôÖÛ–å¯Y`‘ÊPZ” kJr+Å7r'…,o+·ÕË·ÕÆmªå&`ya±G¡ÄXðrT.J‚…¯DY`ñ{£Ô(+lrT.Ê®‹2¨‹’e+ RŠ"ªÿˆå¢ŠPØeéw¿Å\c{%í‘õ^—öD¶²}ðÏz p¿ñÞÛþ+ö³?â¡•~OËö,Ú¯Ø^õ¯ì {ÛJ÷?ê7;Iù³þòé%¥ûÛC$¨(”oŽŽ°…l¼Q¨"”ö‰ öÌÞ89Ç1cŒ¢¿à/*ÙóuxSÅxSõ¨¢Fv¬¥C¹áMV¢,°¾5(¬í¬é"¶¾ñ拱–ýQz”Ö² eE8Š*ö]*{“B’^¢ÃU€òÂamÊQv˜((¯ÖkÚ“&U„òÂä‰B‰±ýQzTa±ìW*Ê–cZ`bÉPZ”#&˜L‰²èÊ1¤Uì{v èÀ1£Y¦¡ Ö‘BÀŒ–±g(zqlh –Í…Ò°g'ÀxfòŸyÈ?óÿ—æ!¥ç ndœ"özc²F¡¬0a½Q”&n I^)J…²À$–¡´(LfJräV¢,0±e( JŒ ®@å¢l0ÑýQ(LxJ„I¯@éQ˜üQ(±€™Èr¬PEì BM8Öþ(Ê‹#p_=°H¢Pb,9J‡rÄ‚ DéPnX8J” ¥&ìWJ‹rÄbR¢ŠPR,* Ê +U€ò`ûJ„EæÒ–¢wD Ï ¥F‰°½Q¹(GÂS2¯í°(QÖ6 e…êÊEÙa¡¢ PX°*”‹V^б(d_‹V‹²#X=áÀ¢ŠPXàJRäR”eƒÅ®@éPnXôA(+,|9JƒaP t(ÇR¼EaRî"e² BF؇bÂ>Ô>¶’4 /ÂÈfYŒ Â1T’&ÂòÙ¼g„½ôÏú¨°‡þ]ïüŸd¿Ò~)ì•Ö'ÿùwýñ³/þYOüwú!íƒ×ÿþ¬÷ýÝú‹ö=öÚ©Q"öý(ʱü3®sQv˜lì™[L8+L8J‹²ÃÄ @éQn˜€E(LÂ(”{›¥C¹aRªP"LLJ’`‚¡,°¯©Q6Ø×üQ(L\I^)áÂ*0‰õì»ÏÚ “Y†Ò¢ì0©ý 6L d(5Ê=U€ò„B‰0éå(Ê“¿å…±ïEQ”XŒqQJRR”eƒ…€*BI±@¢PböŒ*åˆÅ¢D±ûþX4Q(‘‘c-Á"R’B’¢Ô(v¾F±¨"ˆUF¬š0¬e( ʆ0¬ PnXtA(+ìYÞ¨\”‹Ð ‹0ˆýû”ˆå¢t( eÊ‚0«í°?ù xÕ*”«%Æ‚U ô(7ª¶Â╡دƒýÿÙwúg¾gòÿæ|ïö¼H,6Y½Pj”“VŽÒ¢1y•( L`* e…‰ì]ccÛaBû£ Pì:e…ÉíÒ¢l0ÉýQ:”&{ªå…I…câû£ P^Xj” ¥'œì@R2”†°²Pz” DIŠÄ ¥FÙ`±(Pz”‹F‰Ò£<°x‚PVX@Þ( JŒ…äÊEI° ‚HQÉPZ”W ªå…E…c¡ÉQZ”]“’ülÊBŒù‡Ò ÄX„r”%ÁbT’‚40´QŽX˜JTJŠªF‰°Hå(Ê‹U‰*ByaÑF¡ÄX¸ ”%ÁV–âj‹±˜½Q¹(G,ê@TÊ‹[I Ü „²ÀB—¡4(;,x”å…¯B‰°ø(-JŒMÀ¥GIïºHÀ»ccP ô(7l*”á]kï:€ð®Yîvʇ´Çß¶Áâïdä[9s\ki©óq7ïû¯ÎùhßüïÌõþÝyžÄä®?þ§Î÷„½ðßóý«ý½”Ë%Áä $ &CiQvØó1Ù”ì™`L8&œ7*刉ˆ*@y`zaªÙýuìmz”öµ(”˜ý^U€rÃäT¡¬°¯iPvØÓØý4”&­&­ ¥aÏ~`ò°ûéìž&° %Â$öFå¢Ùý5”åˆI­DY`óFiPvØÃQE()&¼%f¿a@éQìa¨"öÌ/‚%Æb£´(1ÆGaaX`aÈP”H )J²ÁbñGéP,š R82”%ÆÞ¥@éQnXLA( ,(Jƒ²Ã D9bq)IIQQ(ö- JŒçÒ¢ì°ðPE(,@J„=KŽÒ¡Ü° õ(,Jû½ö*1§¥G¹a‘ªPVX¨r”#ö¨TÊ 7 eÅž…CiQ6XÄþ¨”{%bÏÄ¡´(Öµ2ŒýP$؇§k`+ÒÕ¤ú“~èEÞmÚ¾kU“ï¯è9vý«cdû¢¸õ±ù6C ܾ½ù^CÎ9:~®‘ÿ®Ÿ'¨M®l< ÔW¡×†ä•uݵ@}îÚNÚùB¨ô¬øÝi‹Gó>¬k=$§7Š`6]9òèzøÝd˜ÝÔÊ> õ·fÇQâ8ÌUšð¦g!úñã {ý´` L¾Ç_…ÔÀkWô¦ÃÁ¾µlWBë¦oCE5+£¿õÊ*îsûÄèBÞÏúÛOðÏj:™ãߨqœâG/óðX$Ûñf9háÈÜ;ö ~» h¤µg‘#xŸPê‹Dý8#›|{ÆÍé¼nê^§—6|7ÍA ‡ž‹ü¾á*TúõcµAóúAÅAºìû½ð:<`AFþ Ç|ÏûñP? Îg’øOÈ“˜šŸ_\á® ¬ÕÓ·ÓBòlÖ¨÷*ø½leoÒ ú.Yôî¢S“;þªï”²¾ûœÏgê VÂ7ãëf¼›2Ù_E[ÃÎYkAsפù¹YWáõXûšÏ:C·]ó/DÛG0Î}®´Ýlô ¡œê§,ô5•`Ü×6_5ëªén%£m¡ƒ½ï¤«PyXý±E#»€âÀØ Ùõ"˜Uë&«wËêƒÎù±Øõ fãI1ÞØ8/QóSáðzc®ôþžiÝg~½;â*$ž:1qüÐî0:oŒUê7ϧ¥÷ã—ytÔOIÈ=U`ü'§]Ü˃îï%Óv´ÒÂÔ{ µö¿ G?ë9¬fðmpúdF½sy¾Œ¾½ÓNXÞÏÈ¢<îú^Æg]ц¦>i1«Úk!ÞV–äjwî§^|òþªHîekô*¦àò/ûº52úáRßGêcÃñÒ¬Kø~©1þï§4Í?&/×餯M–]û*˜Vm5æñø!Ïô^öñŠñ^á5ÿVåE çøÑ™úé–àá`¼Û­êld‚¡öø Ë]ðzôlsÿTaLœùØ'ap㩘§¾øÚ›÷™¢>JôúRƒ!Ÿ'æI ›FÎ õÃ~TmÖÝ4ÂKÃq زA'°º‚± _q—'Ÿ›;Å’Nîé#Þ§b8^â"¾nèRŸPÚ·¨ßç÷M¸˜8N·g•ß.¬q 8n²FJ^µ3)‹Žuþì‡ÜŒPª˜9{Ó\^/æ}Héýà®W>ßg¨ÏžÐÿ\ãäØõ}²I|2æ·¸ˆù²AH¾«$|Yûž½ƒa‰™÷9y„Š.¹óÓä—%|¦÷Ççg_Èfï]ÃN@½éÝ4ÖX÷=w?Y'À騺dàÛ„Ü¡q*f]ÿþ‡†œ5ÆáüÜ KðSÔo÷àè+±ã‡0ÛSfW±î¶M6ë[5øæ¹@Ë+¦_V1áÎM¾æòáý‘éïÅqÍxŽ õç3ä9Æçx´Çaí°‡øw'(ZŽŽ(ºUê:üÝ©h¤‰§³¯¨˜Ö_j0Sªý)w”òÖ„Ü*“Iy·ê(,Ûà}ílk-¤Æ¤DO|z »H gõ‡Çן›àïÍÅó-“‡··²ÙUŽËkŒ'v[uò¬ÊÇtœyŸ œµÓÓ®€K;ϸÿÃÞ@E±mkÿ0Š¡Í˜1Òf̘11#&DEÀÜ¢"**"*æ6cÆŒ¹Í ©QTL¬Am‚ЍØfÄ„ÅðŸ«k­¢€½Ï=÷¾÷ýÆwÞ±㹞s÷Ù³º«æœµR?¿FÛûAÇàÀ¥/æÝ‡‚ùvÂòãÒÃårÅ÷$çL4’Ë?^çsîïÄõ•‚ÓžíËg´ÆÏt3¶‹&J@6õs€>o*+ÚœÂúÑ•HŽ=>Gäé÷;ÜtçÙO…òLà)ÜE%Æo{²ìÓfûae˹¹Áا‚ïÍܺ>\[øÌ=Pg8<ñÞÒê„FäAä)ÓÆüPa¼çMƒ^-8¼ÚKK²ÖÃC+íçà‘ÑÐøXïú÷t#Á8|د!ofÔ˜žçgÌ}¸¬ð^Æ jŒÛâŽç–…öÀHjÿªÐCÜ’„Ës-¢ÁìͰWˇ+A•–|$ð€†8ÖlsúÖo1îà'æ3&µÊã= º<£¢ùëæùxIZŒŸ‘9ÁF®ß ›ŠS²šdU»asï X¥Ž_5ÊÆÄdž_ª!w®÷|ô0ÞK|ßðûPÇÌýÆ8˜ßx3ÑŸ/¯6øƒÅEÔÆÞ>rçÍW`Êó±J'KW˜ºrÉñ±ç4¤šù© ·g{‰ý–ûñç)õy6Qâû==cF¿òþpÚ–:顟罻ž}¯ÀŠª¤îϼ1¼òØå³ qF9w€ç#ïãR.¥¯3éÚýþa‰;¡Dsû3ÕÜôàrºé©6¥¯@È>דGŒ«rWûÆl×ù¡ï¯ÚöÈ{Ü—~þ~“ú<+0¾ïª‘)—žng¾àzèdC¿‰3Y½Î ìKª;y†\ñ ¿ú÷S4ˆã‚ϛ÷—||c¼Î{“Á##ƒ·Bô”+룖ë¡È •å‡i¡my©Ü¹CáDHÅ+Ö‘òëóÁðÛ«Šû/óñ­1ÿ1îÄy˶-ão÷¹-›»F#'v}4x£ʼp¼Ûçñ`(¿vb éyÒrì¤é³ q[ŒòK®8©˜è;ƹ[Òç­Æë¬œeâ{qD˜R0¶¬¯l-Qi¦bî\?àï0n±†8Ÿ¾™é-ú¦q¿OÎ{ÊÇ}¸Í7`5L~WïñÂ¥zH“;¦£„º+›|þ±MC»<Í}24¯dXÕ µÎó©ÆB\ÆøW+Áv…V¸-*v¤“tã×[çÔ±Ujcûwó4ä›÷œë®{½ pßÛ8þ\érØÅT|?óñ¿õÊfª÷l|–âá‚AJ÷_Q«æ<}¸E^kàÈG çkÌо\3ïºkà iTŸ’Egò=øløûXÊycü¥FÐÞbXõn«uóÃz¸ù[q~ú#Íwz¬ž´Ør<êÔ~3HCb»®›:«@þ¿ó_ꯪÀ¸c/YàG˜Õ›n_¦‡!N_G`Âë(™ï­ 8 ]«Ùµï{=‰ÀÉËséøA‰ñ:¼Þð~ý€9 …cý·_ÖCÕŽ¦£ýZ¸ÑDŸ`}ÊŸÛ#×Ü’[Êv>;ƒä±ñq¢p?¿㮡¸è†³ IôêÎñzèÖbþãÙ Q øtº€-m«U5äàÓ#±5®zâ‰r_^Α¾ÿÕN“Mãºx€£SÊ—^wõÐkl¬ÿÉ©QP:Ú³T­ïΰ>vféæÿTKÓ›COyˆ¾¼vYswÏЋÌ_tÌkµáóá¹aäMNk³b:O"øÀ~b>ËÌãür·ž4#jY»ˆ6î$XQc˜æÚ½ ð}ï—›å>ŽƒG2Ì #»µHÝóbºø½y?¸º}këS¡¯Å<ÞWÆŸt¬_Ó̯^Ä<ùq½ “àþ®ÅîVΠ´W‰m—(axN»K#V„·2Mô—-è>h*uµe>æÿA‰ñ ™Ð#¡Ñr)äsºGpô~q?¡ííóßkÔ£$³qÖ÷Dè¢a…8¹£ªOïõBõÉÆ8©d ŵ>%Êýâª0îŠoÃÏ;|ŸGŒöÒ‘IÐh·Õ{åyXå¶Ñ*·ùH¨®žŽ¯€0Rþûä–Mç3|΢ÜÀÊ£^|û¤à'ß*ߺƒ¯c‘cl}{Y¿q~Yº$xõ«C…jÅγù¼#ã…‘ñOV>;3o<Àú'Ï Þ×ø: ð9Ì„üÆëܯqülªíÒ¦7%«%ÁKŸs.GBú„´êä‘=ôt:·À~ÖÏŠÊIa!3 ŒÇÞ³zÌ4æŸãýÔ…ô;×h91 :÷ÆìOdY¼ò™µ&ž·<•PÓvŽÝ»q‡éeU¾UÙ3Äqdé£îJx/Žã…ïSƒù® >ø&³ƒ&7YqäÙ*Ò2kÛƒñ•“!wGSÛô‘‘0uíŽ$ÝËÞPqÊ×÷…‘U§Gݘh=£G»9×@¸_µÏÓŒù׉jér²ŒÏZßzE›úÉðü­ìDñÖ‘l<ÜÆ«suB1boZæÝÞ_ÿà<®$_oä|dcàu4×{õ?=|t“a‹í¶)÷þœƒ2 Ó‡Åõ¹t¿SÊUñi2SäW‰Üyö|ùz˜”Û§Äøw+©¾ÄsyסxÛ®Éà“vÐâЕsŒ{çwfSRG(Ö—§‹÷‹÷[ÎuãùÊ}©¥ën*¼ŽÓî¦!¤‘©áÝ#S=¿Çà%¶Wžÿ×Þ˜6ÚA¡d¬Ýš5VK¦Šuǹ¦ü{ô¡¯% y>¾‚šÆÿ~QÑo ±pk}ß~T2ä¼Mßþ¸Õ9»þÊOaé³gÞù"”Ì«Tñwo½»Xwü¹t;é°íÖãl¶Z#?p-ÆoU7c¶²óVbQ)ê['w|Þ‡ú[¦¥Ÿ…î‡s^º8‚0>eÜ%wq~(ò­Øú;¿O|~"Í+^g¼G=m©m¤cüÄïd°ÚÓ¥}Ò†³pÞ£FÕ‡Žp¹ËuÕ›/¡ÄÜøÂž\h'¬‡ä=wáy´ÞGŒu2 ߃\Í\:o'>gì.µ:Ž}Z­®Ðõ,´\Zãe|î0H¼yx^3C(9¾ûÝœ¡ãÜ ñºÞwZépà8o—rðä¿á¹ÆwêvŸ²›µØ™ Cξ•×z{ÂZ´¨|Ïu(d?ºdýáv(9^éSðÑI…x¼>øýâëŠR®¢¯Ò&s¥ÞvÙÚPæ¬8” Ë;/ø™qü <}Þ«ZÌ^ðœ\£Æ³Prþé %ëíÝ ñ|…÷m˜s]óq÷ð:ªsŠåO®û“kÓp„“ ŠŽ ûM›|†•súÖ@=Zf^nSÌ$ŒT:¼È¶L)÷¿í·œ‡À×Q½°î£¢÷-é†åÎQ{È‚šVv?"’¡óP‹Äê Øo˜tÿ•´sŒuüQ*Œ¤<œÓñÙÁ)â:ï_ë/ ÑoõȲáœ9a_]¨ŒȾ#›6í%¿¢–~z“ +v›”S=?Ö5´Úp²7¸ºÏñZý$”ðq_·âßÃ¥Ú‡–û¯s‹/w›·ˆx*Z¼ŽÀqßG:4óØ%”¼!7í5µŸ+t”ý°ª¿ˆ}žë9wÙX'nYŠÏGâBMvßîú |¹Hh)StÚ 7ûFÛÔtoZ,8Q4”ì:6¢Õ´BëOœ3ÀyÔ|i¬Œ¿xDzàùjòÚ¹‹ÅF)017½×¼‘pH»óÔäfƒaíø5m^½!½ÌaãÂOÓļöS^‹Ïój”Ý´Õù¥°>§ÀøëŠGvˆÜÛ¦}Q»[ }¯§S¥ÆUs„„FÃïÊ!eR]³­ÞNŸ7¿OÂýÉß·ÆzÀ¸ýý§ä:;&ëä7¯–˜[¼l¯»œ; þΛþæÃמ:·ýS)UµöX³-yý\à |yR¢ ãŽ6N4“š3û$:§ÀùáÓ.Ýx&úϘޭÝh‘³û¤¨ÞrÌÉ…úÏÎùÖgã·>üÖÿBó#$²ë´scÜS`™Í–&ýîž‚·ŽaçcÚ× %Ñ­é t’8þàóá~ÿó†¯_I9zZ¼Î•çánýަøõ•O ¤_/3ÜSñ 0^v~4¼½X?çQì»{-ÓhB!¾ÏOÞ·¥\1Æ/Ÿ´yMÆß9jd¹µ)PwdÍИáð>{ã]¯:# Žqã(”ŒÚ3ª^ð5W1o„÷ÿ±¯Þ½GAL­áý¼>7Ùºüä,E±‘–O9Br‹_ÿp tz¬öòÝápëø’Îå«;Bä6ßìÖËCɺ­~v÷sò>?ïûªÀ i–«,Çë€qaø±MkêT.:…qÂáw»ÎÓŽ·q€žÃ;èÊ8u“òxòl_˜ç?ç˜JûãÛ ¡+fGÈ剛¥@EJí¨s3O¶.Ú|÷Ñ %Y™‘5ïíp+Ô'Ô–,ÜXù‹qüªÄxû'¿бîQ¢½Q¶Ö¤_) k>íëÞgx6cš›oT˜cAWÌBIË䘮Úmyñ¶g7=VöÊW1oø>³ðÏ…Ï«Âøýu;t÷àQû¬œÕ§:©PîKq‡H |ïÖaЦ}¡_ÅNú„’#‰³fT~7IgðùçŽH÷õÕw¹ãº:{#ö ûß\Û=¾ûرi….}½è wBlËèJzuôkÕ1db¡ºâ<@΋â[‚mdÑÒ_>³ýU¼Îh¹í‡ LjWÕõª8¥BËæenôÒÀ§e)³^7¦pr©°úṠ0›Yê9Á¥-óŸU(JÂæŸ|F;Vìs|=‡óG8çXÊÉ‘cüuÝ#6Wýyœ=„ó2¥˜Oü½"¬ÓXäç×`ܵ´ñŸ ?š}O£N…õ‡ƒWn»†Ü¹Óã=¡Xðð‹†áù/4.âãH!æãN)1~Ý–~v‚dV<ò²Ú…T)œ©Ü¨^ø<}w¹Ô­á _8Af²)„%Kq¸O½¸àŒ@’årìóÀÖ·¡×Ë×™36ž„À¾¤þÄØÞNË T(™q¤È¥â•&ŠuÅûð¾z'¬cb¼U%C6?Iþ”x{ûzOŒW,9bA÷“ðràÒ½S“{€À‰ !I!w?«Õ…ûŽÀsû$¬gb¼>YÝw¤8I\¿š~eèmПÝ3çÚ·@Æ«´…{¶n~Bª­±þøé âû›÷I¾¿Ëû Ÿ¯JÏ)ñ:þ‡G×$Ù'ÉÔ/·‡/yÚm\¦¿ƒ'•ÐÜ»\[=ãkÉør4šÞxWµuO‹£§òx½œg$Ý7VaÜbÏ0WŸp¾ž+f884×wî]yÅxíÕ_10:„PêNQW×B甄u³Ü|ûÿjŒ;rò¢q£Ož$;‹Ó…òÛPw•ŸïÄxz1£ïÒùCÁ/§ê´H]1âЂ&à%ý±‰{äš5¬‰Ïou—`Ï}O’èª'6߆Æ}´^Ø îXïÝd2à ̶h¸"¾—y_âçÜÛ̘xŠˆü%¾î ]ß0àu’ž~ëø+x'‚·¡S“çG_Œ?SF í—Óyíz4¥íÓR›·xè&®#êã]Kvù“Ëxì±6?Ó*GÓdj–ÂïèÑ5‡n“9¦Å3ânÃÃS=^Ýy—Þ|—þ~$ë“!D>Þ\󬙛x>G˜äÚ"Ù8½n#¬» ó$Æßça:OmHܬú4,Ýò lÒÀgã‰#`²çÛ»ÞS`i›Q§ša^ÓÝëZvÎb¾ðùFtp‰êgŸ'&ß>¥ãßÖ<ªüÚ1¬»´ÒÁ®wàäW»]ŽÀ°Ã÷‹lôµƒK%¿z{+„üºh3ûΉÑbÝð>[¯¼®Šë³\ñ},Œgºå[ÖâuÌF-h{[ ñ"=‡¨ßMíÇ|¾u~7¡3Ξà`P†å]Nt»0¾O‘sñÄýÐÉÃûMðš¹äj¯lü‚ב—;‘Øé$y_Wëåv"bf†Í:|Ê{þuÙ®µ±˜B?îQ÷ÊÆ‰â¼Œçç™±ÑÎÍÆæ²÷?ä™LËRø,8Ö¢gí 2€;Xt¼³ºou9 —Ÿg]oüÝê®ÚBŽ7|íé=©ÐùΑåï%a½¡3ã¤17^'>ÖÜåú‡ b¶×|ë¶; ø”qÙ®Üa8þqîÊ”å}ÁûæàYµ}Bˆ°þ8±'ríÅÛ7×ÏÈ»_üý'ôcÆãÆëPŠ¥Ë±`¢ÈÝR¿mð°ól0iýÑCЧÆÅMË? ‚Ÿ­¸Û*„Œ²M©ÔüÈøBóÞ7ùóŸ»6+dYH[àçÖŒu‚ס”ÌÝåCÈð•{74»z2nïz³Á!¨þQ6ã¨ÇØ5k˜iâË`R¯hy|¥-p^£ì*UsS·EaÐ}r*ùh{(ÿÙáþê!ÂùLÆ·í£XÕÀ.„dw(v}Òƒ;4fÆúj(UáYVňa°fèŠӯyÏëå›Í#ö;ÞW+^$vË;‡À9kÂü´£P/x Ñ?§Í!SÖO[Ô8÷|–<¿§þ Øõêql”v8ÜèG7¦‚I£›ëÖÚäGø¸§àúßæçXõ‚×9G_¿±!ä4m«uï‚°ÿxÊU?"¨«;÷Bêܸ˜Uä“Rì¯üo¾¾Àßgœ-|a߀×i—á´`zÇPRá㫦6wAàºïgëÙÃÙ¹Ùò~KâðOµÇÚ‡àë»ùÏAÔfýF7›xd)øüMŸíV.Ãù.”«Ý|ƒïå½phºßÑ? G:É|Þ ÷âéïî·ü„R_pnçkJÇrŒ›¾)ðâ©2¡¤Óÿ 2ï»P%£µ&Ís ïµkY•ÕCáH—:ºS±ÿ¾¬éæÞ-o<ÎßC¼>„}¸ùÖ_è÷!äéuÿ‹;ýîB݃žõRó¯Ïé-çwk ž!¤T»éâûO(4®àý½à9k)wP‰×™zo丆!dÛðŠ3Úž¼ Û]dãNõ‡_ZÈž/n—Íã×ÎÅñ\ ¥iý˜Iâ÷àýŠç­ûJ2—ççc|uàÝ–E'„êû~í.Äï°Z;eÇ.(:bw—{Õûƒpž5„¸ ~4pàdqù›ÆüÇ8Oé1ù–!¤ç–w/«<» '=Ê_³9·Z'NXѵDØÕ‰œ !ÂzòäBûçùÎWb¼ÜÌÔ·>3ÛÛ™·MÒ ìÚS.dî€N‡Îô­òg §#~yãžüþ¾éhz®Ÿ:çëÙë[ð´ˆÀ÷ŒyŽ×y¼[ß0üh0 þ-»ªj”mF¾Ù«þ¸šš?ß0vDOm1~ú†2íý°áîr·Bë̼~N|[à甌ù==K!ðɃIWãÁª4Øó£õµ¹!Û``øˆg—;A›Ò˯Ÿ›BW÷vð7©Ð&âyc~cÜÎÍé7&é³wï)63 ¬›ÎŠnj¾*UNÍn< ­Ü}v餼q~þõÀ¢bÞ ¼Ôfù8µ Œ?ªÔÃF³œ‚ÉÈóÕý«®Kí}¯gºúGÂkßž•0ÓÕξñŒòåþÝe³ûäÅçÏód9oZʵVbüæiÝ.˜ r)>ýx_=äÌò Ñ…:z_ßñ@O‰;ïÈ{ó~Éï°öÔFz_T׸=\9˜üÜô8ü`l}21©Ù«5Y]uèpïñ£ÏzVûgFÝ•ªE¥&‰ïùüç$Ÿ‹ù(Œ3jæÛ7PãuôeŸ˜výD:yô, Z]“¤YO´_¼^ŽƒÕA¦òwY!dÿÞŽOÛ÷šT ß¿µI¹‘!³ýõV¬O~nIÚg´x¸­wλDŒ]ÓäüIãLr)|[Ï<øá(¸xöܨ­¦¡¤F\ÝCnvyó>+ȳæëì<=Æ¿ó|»ÊeÎ߃G¾ïFÜuŸÏ5êÛ3”ÎðùǶœ8ÿ|ÕÔË7²ó¤û·¯mV''Fœ>`"Î;„ñ¬eþ:˜‘¥(×ùìz8>ZÜ}ÜéÑïÁ‚K†ï«à 'âÚü®_nœ7iß×ï"äÕ¤BãIaþZ8ŸX¨ÃüçCäxëÍͺ7Ýv’ÈŠuÙÖqø=(Qc {§™ ¯ÙäV‰“°Ù¡ÝÑuOBˆð¾t+p9W<ÏÏ/!¬ ïEÆ/3f­âK…“¤Kñï÷tsîA™Òtf9¾{nÓ¹´Nàï)¾þÂ÷aÌŒßyiåÒ=Hr§)5«$ÞƒJ=/‘õ2%™™rð—]Åà×–îL„¿{¼GÕŸ\è<¯°þóG‡ó㮲j„û Rϸ@pV´þðf×gr%<Ú7íâHðK‰û…ãÒk¹³rŸo+8ß.œ|ÝA:ß6™™¥¨a<ˆtœ´pM?ðÇ=ØØ/=ó›Ë¢éß®É|2 † ¶X?ƒ‰æùŽ×ÓÛM߯|~COñцça¾õLŒïýºCwKËcdùœÚàX3Ê«}›9vQ­ùõ½bÍѰxÌê?Þ¦!ì<É$ñùòû#¬£¼Ï£ä[×Äø«ŸM|âd{”ìÓzÀq&¾ïO½SãÑäB}‡ïóó:àó$éy[5^çéð;cŸfçrÒ¡\rÈÈEëÈ„ZE·ln2 †ú³NYBÙ×&²©…ÆËûïŸÒõ -^燾ø—–³“¢·Áw¢.‚/¸¸†]Ù@꫞=þÄ|Æí(³ciÉy²þ«O½éâ}ãý'Áñzd›¼¼åûPÒó#¼N}ƒÙÉÔ7‡©Ë³t¸PÍ÷û K›È1ÿÄ–Q§=Z{L!g—ü¸µgf¡uIþ;<¾_!_˜xf)›–èwÐé9­¸Øs]‘û0¸Õ¢vò6~„¤ÞÚ»ÎÑj ¼š]±M‘—IªçÙгÐsáûQÆ:Àxuk-óCÔD˜‡ß‡Ïƒö”èýÉ,»·sïX;˜ØpáÊ=õBȱ–¡v¿™Yhý½vY}EÇÌo6œÃÎÏãJ9é ¼Î-³M#¾6T“æ•—ìtœ³Z?³…l«1³ùåŽ`Ù|Ýö ¶!$Õx\&ïsózæp¾ÿ-ÝTbüÚeç¸<Þy7;܇€Sc}×o%Gûgô×~·‡¾ÏO‚ã.7íˆþ;·yÚgáó{þù…õaÆ«Çø7ÛÏ3?HŽUñ^ØoÂ}øóyx@ÃCÛHÐCm«Ô ¡ð¥Bý^!ä«ÞÆ)û¾§xÎç)_gœ»¢Üç/õblø¹h>Î3Ö^çá­¦º[}toú­ûÐí±êÃä”턎R»Ôr†Øî;m¼ñ> y8úçôÁžb_Ê¿^N\Ÿâû˜ÒóÝZ¼Î‹e4›í÷ã.¼ß}ˆ==.y¶ÛN²ëµîÍšv#!åkXpû¦!d„¡ù…/{fº_ÖÊŸ÷Ú*ü¹wq¤îá¤îÐÚ¸à.¬»ð:nƃ‹ûÈõ–/^®>~®ØÍÿV±”?Sb¨yÿ#àçqÍPÛÞ8¿¡ËÌʼs†|÷ ~.ÌX³²—ƒéÆÑ^"œ[½Ž™·j÷nVsÇóZ'èiœ˜àûmÒ•‹ÞÓÅù¼÷ò-ß5ø(žÏÊw>ã–\´Ä¼^·=ä =¦žtÖz?{òv–‘öË{ÐPØÛ1Ð=8„dÅOž_÷ÁôBëQ–¶ 3S»|µá}‚ï·K× xéÆ'2±î÷Ë^Þ‡à&E¾ÙKŠlòoèèh~µWú‘3—¶ªÙöôŽûbÒ¤Ñ"ü~¡;x$q !‰õ«Œ²MÉ;^°ø>¶t}Æ€ñ7ê6™é­Ëk<€!íþô~âцO¾5ij8¿Žþçá‹Jë2[<Î×ÿ ~î`áÏ?SÿxLýÿ»ÇõC 2þÐ{¢GY2¿•Læ¹âǼŠ—Œ&³ ó›’²#¬™ßT&ó9—òx¤œkó9÷e>çŸsÊcÔ¢,™'‹qÊ|™ç”B“ua¬kîŧ–xñi˜×¨ ó5“xr¿sÊ.ã KæËG½Z8“‘³®9“GØ<>˜<œu­‘0yÒ“‡zS&õ:§LF³žSÜg”2y(ëZÆ<[ô̧ϓùôY1†YcÓrß)æwι£þŒÉÈ™<2 “Q.a];0&œùŒr¯s?æ3jÇ|F¹×¹NÂäá>£º^ç æ9eÆøzæ9åÇ<§¨g çÜRÎÙ?=öŸ«2ùÏê±2öÓPV˜¸~¨l”óµÊa~ð‘ÌÓú8SF·Ôžòý˜§Ÿ]–„”y«g>Î~ÌÇÙEâãL™g:”ƒó9µf,ÈlæëÇYJƽåþW¬HV8Jæíg.ñöã~Î&ÌûTǼO©Vvõ<îçÞržD$ãI¨ ð$8÷6R“00žõr¦< êåL¹gÔËYÊùæ¾~.Œ{k†…êɸ·–̯ÞÀ¼Pý˜·åDrþ· ósæL‰4‰·g‚§I¼±dX䞌{fÅxÔËÙ…y9gKxf„{ëÂx–Ì×{9û3_?‰•ãžqž÷õÓðr¶cqs據Æ8âþŒ#N}«"%ŒZCôí±ÅLþž!ñ_y7Kû&>cÏ”öËÕ+ÿªOþ;=’öÇÿioüW}±`Oäý°`ÿ“ö>Úóþ7ú´Ïý;=îïúÛÿ¤·I}™y?3cÿ;êKjÉY>#õœ×¢äØ£|Ë‚{1S–˜¿Ä“O.ñ`ÎA)™ß(e1jQrÆâNC)˜'÷—O“øË[a"ú1fÊ» Þ|æÌ›s¸)ƒQËüE9+̱ÂþŠÁ=˜)×q¨3gbP?¾4 Á™2Âì0Á5tœ‡}G__ð¥3ùÏŸ™³ïd ÏוƒrÀV³$V2^ó\ÖI|çibS~"åsç0Ïe)gCʤMcžËþÌsY)ñ\¦¬/=Ê‹ÁŸù’*C‘²º$ EwÆ¥¥…¢d~ôfX0žÌ^ƸÝ:ÆÛðEeK¼—e̯TÏüJýQ9Õóx_œKËYZÆÚð-ÀÚà\Z­„µ‘ÉXVŒÔw™ò¾¨ï²”ဒ5X”KkŽ…êø´VÌ«>“ù–R¦P¦„£HYÞJæ½ÌyÔ{¹ Ë›rXà(3,rÆû²f¬ ÆP `¼/ÎÚ0—ð¾¬$\Z%cmp®7÷]V³A}Fµßå‚>£œ¡(õ]v`|o9ó250Æ·š1¾9C‘®¼Rv­úç¯z,í¯´·þW}•÷TÚO öRi-ØCÿªwþwÆjÿ½ò¯ú$푼?ìÿN_üßªLþ½>øW=ðcüÆû^Áž'gÿ]† ¤DéPrÆ¢eÇXDÙŒÉ8Ùî(­„­‘Ù´0'›2$í°À5(s,rc©)WCÆxDVøœ«!—°Ô¬%v&Ê{©?* eÅáÏXEvX$¨L”‹EÊ¡Ü",-Ê ÇSÂÈ–ò)õ(+ó<ž›ò_ð=,%|k ßCɘn”ï¡Be¢¬äß#å‚…¨EɱU(Êš±Ý2QvXœš†×]§¤¬"ÊÈ–cѪP”‹×•rÀ"̱֠U(Ê ZÃø”>(ÊNÂw£|^=Ê Ý•ÆxþŒ÷aÇxfv¯6ÖÜQ:”96OÊïEYccðCåй3c¾qö‡e‡ÍBƒ2Æ¡DéP–Ø8|QÙ]óØ”fØDÉ{$íÍMòúÜßõ8Þߤ½ö5ÚÓh?£=LÚ¿þjÍPÚ§¤ýIÚ› ö$i?*Ø‹¤}ˆ÷ Þþ®÷Hûï9Õköè-×üè=‰¤ŸLçG8áœIá|` ÆÊՠ̰x¢ô(kì9eN®>L=ã€[ãͦ=ëXGÏÚIXÞR®#ç Qæ­eͺŽî)°EgÊ9³¤û©(kL€”ëR‡²ÄdÈÆdp`,[ÎdT¡,±î|Q™ôl=·‰‰’c½e¢0i"é|k-åÀøežXgi(k¬1Êš½`³Œsj­±®Ô(Ö”%ÇšòA)°žü; ¼2sLJ*å@ϺÑõ}LМ®KÑ“Ô “ÔE¥ÿü3Q™ügGìØ5sè½ÇÄÕ Ì0yÝQ‘(sLbTš©ÀæV¡ (;Lj Ê[Y"?§;%ÇDW`Óf£ì0ñ5(3ÆêÖ£¬±üP™(, J†á"á7ºKŽ*Ƨ5ÇBñAéP–”áˆJCÉ+ ìnÊ H͘Ž.¨H”‹É•‰rÀ¢Ò Ì°°|P ŸÖ ‹Ì•†R`±©Q2,8w”N§MC)°Õ¬Ý-Ž·¹ÿ9J²®'ð¼9Û‘ò¼åX >ŒOk……êÇø´vX°þ¬h]P ßQÇøŽ”é­GYa1û¡ršfzç \°Àµ(K,r?TãÓj×Û¥E™aáû ÒŸÖ•²“ði}P ß›6—yœoOTÊ…?*å‚ C‹²”0Ø<ëÛ1­±‘¨‘3Íó1 eÍÅ„:¡þOz,ï­¼¯J{êÿ¤Ÿò^Ê{(ïŸÕ7ÿ7{æ¿ê—ÿ“^É{$ï´'þoœqãýïïzïyÿN¿û?éuô^iQæ˜<>tÜ„²Â$R¡ (…©ÀàÎFÙaRÜ>(Sa‚Ya‚e¢ì$,ÚHºO€É–†R`Âù³¤saLZsL>Tcnû Òè]LƺoнL=,å€Éi‡ÉÀ^þî(==‹+akkPæØ³|Pi(&p€„G›†R`2«Q2ìYî(=Ê “Û±hÕ(&¹;J‡²Âd÷C)0áýYÒ»£ô(+L~_T&JÁx´9ô .ãÑê° äXVX~(ºÆýÈ Ã•rÁÑ2f¶/*傽H˘³>(J¤ çÞP.XH‘mò8³ ì=(3ì=žô÷ X`zNƒ™‰’c±ù¢²Q.Xt‘ô -ö=Ê ÐŸ¡;ãa›Óón¨4”µÀy¥…I¹¯‘÷›šÿWÇuÿŒéþßÓ¹°˜4a]P‘(sL\O”%ÇV¡ ¦sÛ•‰rÀ„ŽDÉ1©ÝKägpkQ–˜ä¾¨4”&»*å€I‰2/#ð¸ÓP ,T6íƒX‘(3,%*eŽEá‰Ò£¬±8|Qz”‹D…Ò£¬°XüP”eeÑ­À @™Ñß{¡´(K,"?T6Ê‹)eŽ¥Be¢ì°°4(s,.Ê€²Ã" @™a¡y¢ô(K,8Ê€²Ã @ɰø<-F·\Žÿ•†RÔXÝfXžõV·%¦ •†²ÆõgEê€R£dtM ‰’aÑz¢ô(KK׆²Æ"ög…\×M‹Z‰Ò¡¬°¸ýY» "³[…ҡ̱àU(Ê ß•CçÁØ"QrlªÜn6e‡<~·Ê€R`OU£rhÅf¡CYaÃðGå ì°qh$ ï4”›Hk$.ÝÆ6exû  (65õÀRõÿÊxî_õRÞGÿ«qÜßÍÿÙ+ÿ7ÆsÿÛ}ñßßI{àÿí1½:úŒ1¡T(ÊË•‰²Ã @å 0Ñ4(sºV†Ê¤ã4íóùñÊ1î±#µkUdÏX×)­BEs?×Fë’Ãû[½è¡èÆ Ð• !ü+GÄ=ž->ßÊiߺ¥o+#úJ½·5¾Êl«|\`Æï_êÊÉß“*ƒfóì†&_Õg%IÍ^U¿?²7¬\Z±½[¥²`zG'×s ùdÙ]·Z^¯Dqѯ‡ów¤þŒJ¼NDh’—zÍ’ºôÎÏœaåŠËkëu>FïLžm[¿Ô_ßþCùì`òìAãÍ?Í-ä/ez(h€ÞßEÔ÷F…ñ¬®sí(™Gz¯¨ÒmÕè‡à6åÄ ¯Çȩ߫RŒ³,À³X`0éYKUÎ×lžÈ‰ãü6~Ÿ8/GèGŒ…ñ¿™<ÜÕîþ²ùÙG‹ä™á͹Z¯Â“‘á6®ûþè ‚Q0ùùÇz|´û\±/œ¢\Ñÿ†ûWIýo´¿èÞÙÙݽHÈåÁÃ-{7×W÷Öò8yñ&g]ÜÜ~° ó›¨ >ÁÄæU¯ånsI‘È#ýfÏ)">_Û=áî]ºvf|ÁÏÛ€qï~{,¯?™8R ÑC˜Ú sþ²#ÇI@Å^Mvt F—9¬ÛÈnw6”õšG úRó~)øËðSõÎR\ßG0ö$¡çñE²“ÁéÄùíNÝ9¿Ç·mÞX5þu9˜˜¬ÿù£^GŸB¾}Üß‘û¹ñüÏÇÉÁë¼(6Ý+çè0hxöbĘ́‡ÐãìóÒíˆÀi ¦‡´+˜üøXôÍ‘aóÄû/øžæùµrî¹Ô_Mñ›n«ÕûÎTxúgS€Ãǰ{Ó{Û¨.dÙúqѧž*áL»ÓWêâ>¨Ï±¿q,Îëâþ[ù|ƒ1~²áìÙìKÞ´õzÊGÓ x¹ïÕÌaƒN’¡=þÖh<|T®hLæ>ñºT4i¶ÈßàÜ4)7X…ñÎ;Rç´ùðqÞû€6õ2àsÛuÏw: Ÿk²¿ÆBÛy-ëY|&[‹=.m™:[ôyäïiÁ§òs>ßK5Æ-ž6;ÅmlݶvTñ6Ðfï¨E“:““*¶þÜÒŸ˜»:°eyO«Oë]ÈOóùÓ=RMļä¾gùò¯£´Øáãˆ%pªO÷^Gºf€KØâÑsŸ“¾[άºpÈŠNÌL³ !‹K¤îòiN!ŸÓŠwvMÙñ½P]Iý¼ x*7¸MÊUA«x¿~}2`FÔÙ¶cBÈ”SéÎóÇô‡U§²¿œiB¦‡îyé»h^!ç‡Ký*Mæd)¿Gvtܰ&,™áoŸ·ÊùÒHBJ©ºîiïÜB—Þ|óG0Ù4*áV¹AóÅ|¬µ~èþ)åDßWÁ§˜s0®]®…2$}L1ýe@ÙëþʇuBÉŽ¶ãª|rí ªµ¸]üR0Ñ}ÿÛ'lxÿù})¢níÚ«ï7±Ÿ ãœZùxH ¼Î×¥å?=/î ò]Ö3 „øJ®½JOìñ¡(µõz}0ãË.û1÷+ç<)oJ‰q©)¦Õ²õÐöYÚ‚Õž`(åÚûÔ›PRfJψ²MÓW;N•LJ=é¶Oë½ ¾p¿ó¸A¼žŸsÆ ÄëÔ¢6É5ýÀ|þˆÊeeÀn³ÙýnÏ #—]f)O…m^ß:ÛŒ &™Z¯•eŸÍ?¯˜]Ïîçq¨„qHÃ|ï5Ư6`Ë¢F~[Áú®Uϧk3à>aÛ´Ò²»ôÎÍý^;B|@Û mñ}õùË¢D¯ùb?¸û+*=xøWÆå8cÃ9ÙÜ×ݘÿ߈k¸ß/ÎKõÏ€åÎ7öïVjˆcvü³u¶NÐ¥Aÿ…+Z£]þ1ŸB<øå¾ósÍÍÛH}< W»°ïÆRmvÁ^jK,Ì‹_yzÿ¢†ü8üIß²™ ¼û`b´s^œ—ïÜïÏÚïYÀÖsEEŸ{©»É\ÌÏÁ[zÆ$î†âíŽ+7žÎ€ÃmúÕúU7œ8õ¿õ¹Ò„p Ghêi9ö‹iï2—žŸ+>_‘ÉÆkœ£ÅŸ¯ô}+Çë¼?Y_)ûàrÍðå±°öúæGV…“//ß„þ XäU4u‚ ±£ÎÔó K8_Yêc®À¸•Ï•vUO;Ý*¼»™žK?­)óüU8Ñ Ÿ[fÆø±ëhÛØÚÁdIÝ#Ý>yùòáã\+Þ7…ñ~ý|~‹J¼NÐÚÈA‚6Û+¿Í€ºÛz¶ìwŠ(ÇÏót;éýÏ5òºR#˜”¿øÞ¼nîüB>îœ;%õ¯UaÜ#ËÓÿ0T-bÿþɯ ¸òãYâêC§ÈâZ%M½‹ ‡Ã ïtYûþ‹ÍS¦×\H¦î`±(ñ›˜‡ùü‚1^ˆûì}÷WЍ•¥Ë?‚¢=ü,G|:Eî›ýu{~vñæAdñhꀹP¼Ü7Ó˜ÏgÅ;­> oVO_ß¶Ö#hõ0n`ù^§‰Àr„ïm^„6zD„qð"qüõwó3î#-}oð:Ž~7Žît ï#˜<8²Üˆm§IÕG_l~Ü›fïõ K"”ò´e±èÏÈë3êdù*)†"Ày6Ò¾h2/KÑ#Â)¢óÍcP%Üwüš6@SõÞl›Ç§I誈z#WƒF~•.ž=DžÌn’¶tqþÜ3›/dÞ˿͗wrŒ»íDðñ×kÃŽÏV]:?‚‘ƉMiB™üq„Q=/ûž "›,µÊ^$ö“jôò[Þ2ÿYÖ¿1žà_‚‹ôï¹±Ç#8wµj‰Z#HݤOcLôΰíÑámÇî#®àù"±G89”tÿ Þgî+\W0þ\úتÀÑ ‘&íû?‚”¦íÏTüÁ晣ÁëAÓ:Á‚ˆá£K»¶ºÅâç¥.Ó;Ï~²Uæ´þæ”Æù8<*ŒÛ¬H›RÕ?¥\vx³º—¾{Äó YÚèMå %Ø^H‹* &eÞ´Ù’ì±äoçÃܧõú…È%²¿V5ÆŸ¸³{)›°æÊϪmÁÎÃÞ¹ý¼RÞû³ ·ˆÎ“Îûäx›k^¹¾û›zÅïc?ŗÁU#Écížós]œÁåEÈ…ò·ƒ‰B;òÌ´ X|(ާ¹?ç+sn‰±0~#Š=> e×®ÃøÓI½ïïÝ"IG+ÓGKJö=ÉI×f¿Vž^(òJ ò.8w?c`|Ò|ùŸ‰ap`>€=âÍÛ‰$¹R·—š8ªz;PüP0™:ÛºOœgÏï“p1X!oUxm¼ìÊ%5Pæõµ‹ ð:E³¼Þ¯¾I|³SuÃrûAñ#[»L2ž×ª|:¯.v}8v¼2ÉÇ3©a¾ù“šÆÛÒPæ¬p£ÿÌS¶–?OüúVú±ó@þè{ïRØçSÚ]Žq9¾T¼Oü}ÄŸÃâ­]=ê·Ëÿ´?µBVýU§4ð;bÈþò˜O–ßv&Îpžm¥“Cã«•HÅþoäA-%œG#퓌“öÂäJípøœa^T…ŸsìÁί‡m:OºÚœ~´ÎPØPµƒÿêÇAŒ_¿¤/ßÞߤÜF“ùYŠ“JÇ¡MÔáàôåc×An ÇâÆN‡œ'«ísk|Ù¢sOŽøD’2Ë»N.½X|/óõDË]Tôáçœ]c¾cüI&O®µ<ǪÒÂ~m^ ó˜Õö1?™\¯±3xŸ{}¬iÑ`âàTÖ¹¤bQ|ùfØtƲÀ2¿Äû-õßW`|›„±g÷Ÿ=vÚÌ/<AOÓp~ÈÕ˜Ûç–vqã^mð9ˆÌŒÜü`÷ÂBãkÁ×·ˆßÏgËÎ6§ÁHÇx'No»2äã’Ûs“¬ÞBˆQ±CÒË ¢þ}úÅõq ‡ŽBÖ¤øî¦ }Ž*Œ{jå™6s£OCVÄŒé3¾ë¸_¯.’ ê7ó¼œNŽ•/‚ÈÔoT¯×åõþ9…y)ð¼îO“|ëj¼ÎÝŒ'õŠø–õôÐûÀêg׎cN^$º%Õ´Ü1'2AdëHËÝûÒç‹yÍû×õk^Eê–,&¾§ø{˘×e6ý` ¼Á§dÓÛ_~\$Ñ‘Úu9Ÿ†‚uÿÏ"¿]ÏN¾2o| ŸÅú¬ø->Gc~c¼Ö9í›´n{¨ë·æßõusŠwìq‰4Ùî½Ây™ÃÿÔ Ø[¤•nçó ²¹zòãz–’‹[ê6ßñEìK<žô}*ǸŹ?«< i“rw¢ïk‹ó^½D~W(c¦|c*P`\9Ù/{uûfªBýó&ùûš÷c>cüûëÝ»w¶fu%–¶`ŒÛâ™>/.1þBoxõöVïž×ðù×#T…ÆUK.yÂ9Ñùøxa~x&ÝhZÇug¦Ÿ6»L„uƒÞPˬ^è¾ËADX–V‰ý•û3™zýÕÏ9âýâãR©¿´ ¯#Œ›ÏAh«Åb>‚¥†U²mÝ/“´v]î,4ôƒ-IEú¥áøqðö•½Ò¯åõYþ}¸¸1¿1ÞΰÙgD‚óÛ=~wÌÁŒ;žûÍ¿L"/šW©>œOMh`Dz4H‹ü=f))øÞáù'Gh1®À#‹Áoß›.¡•Ö\¿L~׊]1ÏÔ ÌÆ8N¬L”Û:dÕ«¹XôïæëvšÑñ޵]¿±ñJMx*„<ÇøÕÖ´#{L/áÀËÁµng@âÕЭíjIn£î“Ý®Œßþ ܾÎ&Wz!É?Žûn³K;ÛFøç„~Žqû°—@uN±üÉõ Öaýxÿ1Z²1nMïðe#`­ìO}÷„`²¥, -Ÿ/¿O£ªOïõB•7ŽøŸ5…>ŽñkÝ%¯ÛM¾ ówœô!2ë>”­_§%– K= bF.*­úLŽ>¡ ´y}‘n^Ÿ|_¯+óÞa!z­÷x™˜ËõØ44TK*tÐ^à?5;ѽÑË`âºj”ÍÖ­K ½79¿œ¿çì® ÓíXЄçßF¨¼ÎË gæ-‹‚!S[~ëŸ7N-o– %fk7¸Ü~0^Ïz4ud0©Z'î9Í«ÿÒG-Ü•ð‡õáŽàXÔµR«gB\“EY a_$ <Š_î´:4P|œ©%­–z«»ï-(˜#˜¼tÊ´^*qÿS˜÷güR+àïQcÞcÜc•Kß3‚@ëɽ¦ÌÌ×îÖ—]!•·8î=fäÖýr·y0™ríð«7Uâ|÷Ô˜>N ”yãÚ¿òÃW`üVßm/‡À™À };:g€Om•W£+¤ÆÁÊ¶Ï %>9ëF×Àû^ñjù Éy}¡ÍœŸ—ì7´áë£ùÖ1®oeû×µ€7©â» è´bâñ½¯™N“Í N±OìLjN­²5!k1±öñ¼2amŽøüè[¶]«ìù y¢Â¸–fs× _¡… ûÌ›`‹q÷L¼7å ™æcñæü‡a°lÔ¼OŽk‚‰Ðßóæé?§öK{—÷¾o³ðÞÜôš¶ðÛÿÎñðËŒK‰ñ£*><š¤…ÓtÙ¹C¸YÔL¨ºý ÙÖæi¥7‡‹û1†ôµ[ýìŠãOÎQ¸M±¸g¬a³Gí*òC‹q«WxßYSû |þ•êóªq<µIlØ›\!C<”%¢~9B¥*píÁ™`Ò|Jl§*6yãB>^i6²JRéÉyãÚîvZDÆ·~ŸŒùׄ£Qs—+é2dÅó ðªôþØŽ—WˆPßÃÀÑÄ &%e/2?,\˜×ççR(ו¹X|uèÝô\ǪŒ7¿8K1'èåÑo¯À6ŠY3Í€Þc–ñ+M «6¼y?rÇ¤í ¹L¢*Ù­s¡¸¾Ãï“À¹) ¼¿×øŠrŒ¿þ ]Øb•yý–üð~Ü2šØœÕÎOØk8é¸uàF0VíØ>áy}Lÿ£ÿ]£òøyRn¸ãºSlDñh“[tÁã›a÷nCc»hR]Ó*Òªv?øqæKÊÒ;ÁD_>2Կⱎøz¢1¯1Nèù#žŒ†‘—¯¾ÕkBë˜ÞýŽŒ‰&šã—“b=Áüt£ ú‚ ¼ï¿Ùü>pZÙ½ß1¿VèUmÊ“:ìý)ìã«0þ÷åÕ*Y]ˆ†RZ8ÖûChà¬NøâM&jž8NßžŸ©taÀ“`Ò­£n‡î.-Äâ×ãïOa»5p^…1Ïñ:ëN¯^í*@­ó3úÏW'y¾ŸM[¶ÿs |3èZ{̱‹¯ƒIíÑ]^t›°´Ð:çïò>(ݯÓbüQÊÍk·»]…÷:—é¿Ç=„-ƒ—Ø^YMnÿìqï˜sC¨aÜ0 &¦ø4VmYRhÞ"ì/9²Nú¼Ø–`ÙTcr¼¬0_4àul—Íûø*ü*ô¾S³ÿÛÞÁ.2=u*š¸Ô¦Ý ÎrÝGJ…Ó¯ZÊòÞO¸mB=iñ:OZ½Ÿu<ŒeyðŒìØôâà^WEÎØžo{fx…b:}øŠþyë½üyòu.þ<ùºˆp3!ßñ:6Ïý[¼‹…õså7–?€+-~›œq½Jö\®:ÔªÈÐbAqUóú#ÿ[øÄ|äûSÒ~i²4KQ¤ÃÖ×`IíÄ-;Æ=€;KL ›w•X¿ºî¹ºE7p%Mî~ !6ì>{‘Øç=Âå+n|×w„qAàãacþcüÇ‘öÖt¿µ-³ímº?€×tz¹ô*Ñ­«šžÑŒËвP2ÅzÒŒ¨Š‹ ìƒ=³áœiñ\‚±n:‚oŒË×): ÔõÃÞk0ã¨Ç–ˆZ 6îWùÞË®¯eU?¼ldB2\{— %ÕìZ·qþbq¼W°Ïñï#üs+–çÂ{W‰×9?dbÈ¡Ôk`¸²Éç÷<wáÏ’«dPxÄ~“ÔßQ×(>êWùXêÞüZ{‹uǹˆü{ðs@ç=((Jˆ¯ÂøS+7ùÖµÊuø3÷^Xׇ÷áɰcÉŸ1~Ï£š5ºP¢.™äf¦†a?pq¡üz7yòšÐEϼÓfÀ×#õ‚×¹5íM¥Û£®ÃšŸíVähïC“.+®ºJ„új¯&~Yr$„dŒ¸ø¶Çì¼ûÅŸ?ïýÃßsRž­¯#ðº¯CÉsgV½`Äim %_ma¡¾!Ì;MÄu¡?µÎi3Ö^çÞØ2 f–ƒâÑöáSÍîðó¸ô÷URëA½Ù«°éõ£/»O‡=ÅÝ….,0O|es^ya¤õÈ?â}ãë]|¾n|,x¥_ôžèï;¥Ïºú>²½÷ÿL¨CâhOd(Ú¬¶m³ØPÒhL»ïn{ ¯³òù"/ñq…4OÕxe¥ûúõ3ãÑt˜âïû¦V ù4³[/»å€ÒïêÄ„’3ÓéNþÂBýƒ¯£óué9 -ÆosóÑÊÅâ!óÒµVC.§C±IÓíÄÕÚŽkÌ~6„×µüXž %ÚýHòŒ_Dø:#Þ|MzÀ€qk/éôðTÏx˜¥+‘{<挶Уq yÞnó©öÇK@nIºJæUÐ/î|:¯Íto$;õÝòïµÏ·ßf² ç»é†O<\88ÌïàÆtX; Wè:ŒïVûÞ€Ró~Eý¹qƽH:~î Ë Þæ‡ž¶­Þ2 ˜8®Ö÷:Àʽš]ì*ÔÆ¿öó~ûþx°›V¶K=ïtx2S7¤Q õrå߆ˆ¬ž|\âa¬/,.4_úOqñ\™éé[Þ½lÏÎyØu×YPÓÊîGD<(¦;ž0‘Ž›âžüÆûÏãÌॠmFö÷/¡j6c‘8/ø²%€sÒë-¨¢ÀQUbÜý+&÷ص“´qÓaù6Ýîeøù…yVÈs¢ëàna$iô‰-;ãÎ_ççø9Mc¾c¼Ò‡ß™ú¥Çƒ6ñIl´Y:X>Îñ;nCZ÷òóª}7ðoS|Ú¾AaÄX± óÿýÑ'“«Ý͉‡æFÐö=ø¹vfbtÏrðá´¡µ^ô†´K'OT†‘i½÷>}Ðx‘˜Ç¼_òu¢|ÜyŒ;~Æ7¯ƒµo€Ï®õ9ö ÷àÙܺ7f‰!Û3¶Î›Rw<Ù~þ{ï0rgÓ“Ús¼óúø\™óÞ‘yëÂü}ÇßKÆ<ÆøvT í{¼SÜö­¸ÏÓÝÄá<‚=üXüçEìÚ0Ò~òâø6#ß ëªEÙ¼¡W™ªÏßd9ŽÛðí1Øóhràtñ•÷`~ŸŠ²f»1«ýܲøµ=4he²1ùXyvïù(‹; Ï«üëÝ@8g)Ä•c\aßä„mŸä;aÌ=øåè™òãl )™]ÍÎá§ŒU-öΊ#ÆãÁÝóú °œÇ§N/f¾u„vS£úÛÌeëòÿLGú„nÀSëì©çÛ݃àѦg3âcˆÙyï&í:+ ÿéùµK| #©Øõ‹ýÌÛÊê°æLÃ?Ä}+a^'Œû”wO}í«1—n€\æwhBÉ{0í¨Ið«Û1D]²ezòˆàäÖÿõÌRÒà›÷œë®yó{~ß îGòóûÒóê*¼Žq{íÁ è´.¸Ãöô4xzøËñ´2oX£Fþ±¢†þuLE ™X¢ÙÓG“óΣðû$ô±¯âû[6&ÝÓ¥Y¾sj¼ÎXüÿÖ̹Å}—צIÏút¦CX?#B^hHyýK¾þ³¨Ðû•Ÿ‹âã&áüqsȲµm:ò¼ð<´xzŒ·zŒ¶Ùºµîê4Ù¶~p½„rqÚˆmIú3ì™jˆ0n\ThŸ‹¯OñïÃ9ŸÂ:ps¡ð:æ=;'…öHkãA¢4pµ¤ë³+1dLÈ §ÉVduȽø¿ÃH«MñƒR“óê­ü»ÙõrcóæÓÒñ¥ÉŠ,ÅP³nQ—§%ÀÊåC#¼Z§A…q‰7.Åë¡¿k¼O«A.umë´÷O)i,ø¼uµž±ÖK+—.ÂÎÓ6Ï·ÿ'Ǹ³¿ÞtÚšŽë}]?–HƒÄj#_6Ÿ/µ(_tÍY‹÷åèÚyª&-‰ï!!^Þ~+=O§ÀøÂ²qÌûóì}Æ]hXÔ"ñ0Þ§”/½î®jÍÎ[iȰõ6Í™µ°Ð~.ç†óã3f¸õSíyö.TºbéÏQ8ïÐ ²«Óx¾“ç¿p_ŠÏϘçoÒ/7× f‰@Jg?|±ñ.œ¨ñmý’{øý[o¼ÐÆ¿ üV•,af£!JµrJUòó–ÅõaþTR\O¬pçOç„]BÞ©1þGŸ‘K ÍžfÝ>á.lÎuzó<†œùÔ×u[ŸŽPiÕÎ9\4äð®!-ÎE,ß_¼ð÷=¿Ïŵ>%Êýj<Ïù×1ŽŠ‰p™S:Ü…ÞûϯYù>†ãšV{½ÉÄ—Ó5$æõâÇçÝgþ·û îµg$™ yŒñŠÅu]ÓÖ1®¶ ¤×»0 õžÄáüg5 «¡+iÈæã ÚþÉ\XèܯÐgLAÝ©j ÕY+àûŠÆ|^™¥ha׮ڠɉàXä[·¥éw Lf³åG?Çß[¿¿iÔÖœÐÓt­}5¤mÒg«<¯¿ðõÎßÖé…÷°ã–tïUÉ9‰0}s†§Åù;šb7àùÛÒ(nöb‹òmÉü-¥r:lÕTˆ»ow]$>OÞ;nv?±þ¼‰¿ojNµ;ç%B?ãñ;l}/†û @Ðc9»4Dxä}Îz»ýô«TBèÛ§¬1!¡Y ““‘³î@ý%ìnŰußž$ÓwÑ®¡gÔøc{zæ}.~_…s¶ÅÄù!_W–rÛUxͦí}2+Ò.¤Ê~ ¼½l%¸CjïõqmÓ»ik<¨ !Rú×ó°¨?WXO5aïÏPÆc¶Î÷6æ5^§ZÕY]âœáРò½ëß7¯K_ï~"†¼yú3àñY[Bw» û5ä,VŸ™IÞ}áßGXW5Í÷{-ÆMž]fÌZÌãŒYë6=ûx¶<à»ß÷GÏÖ½åãÑt^ Ä$Î;óó¥¼¼ãýNXŸ0÷'…uìfùö³ xüÐWÛ%Ân»)7_½ Bû/ŽØC„ýðŠÄëÇ‚(·ó"ë{ÒÖ?ï\¯Ï½ Vû,­^DgôÙ™~)¨n¾sÐ&«²Í:¾˜S/ÞùÐ…·¡W³;êîŒ!ºc Ðß¹r%#&¯ÿì/|ž*ŒgãZ¦¦ô5TH„Ië>Ìœp×½¨I?Co ýÜ¡1Àï=ÁC*$iH_ǘW ¸ÿßÄý >ŽYoXÚü^x=6Îá*ð:~­:†T)š!V‹ÃêYß§´²ž¶8Nºµ'&öëŸ>\'¶sV¦†X4Ï™îûaA¡}E¾îÇç)g†ÊžUêËÆáë˜)5¿%@Øè™{ÍnC±»ÇÕÔÇ”‘kFÔÙV”Y!œ ±šPúŽõBq½^Ø—( ÂïÛ?f¬Œ}4'gå›hå§Îió"¶lß=ÏöU ©qÕöeú¥>p¹V™Ò+Ú†‹çÚ‚zŒú¨S)q>âTÁÍÀïVÀ×ÇŒyqÇê @w¹¬Î¥‚q÷3†lí>=à >Û„ûìA)“KæÕ—yú{ÈÜ\„<ÉÏïÖbÜ£ôgjšhSÖÅ«êúT¸¡l³ù,–”½ú¦týoM!ùú"…º]81þL©FÞ¸Ág߯ÍíMÄç(Ì_±u#Æ½ÆøÖ·Nº*ŽUñ^ØoB*„x,*Ëæ;ÕIߢå:ΩN„óUyã‡OSÆ­»ôõk¾s8&«³Ý]??èáŒã†®WSwvI…*]sÒŠÆ÷x{Xßžü¬álÏ-®NÇ-»Ë,"üw-ü=fÌcŒ#,?%€fC¹‡o*¤BtŸ##ð=sjȆ'eúu#Ÿâ¿œÝ]:œ4ÔgÇøEDا–‰ã$þöQظã†Z6*”ur»õ™ÿây 4ôÉT~C¼oï2q.×Ô²«¾× ãîš5Ì4ñe^¿åuÁ×M¤¿£UbÜÈK¾ºyö/ÿéñìB 8/ñî½2=†„Žo^îÐûvd´ÓøvÚâá¤èÅ=Ý&uX,žŸxP½y쫥ńüÄ8t6sçê X6ú‰ÎtK d ‘­‘Šï¿“ÓS••ˆñk®†xλ3ã¢~Q¡ß/,>ðåQçþÅØ¼)ÿ>¬ã—ûóªDiœ'ܽL0œ}STœƒóaÞYr3SwÜú¨!ÆcØûZ'ä¿ãã%žWÒßeiñ:K[¯*™zÜ µ)àYcYtŽŸ2*Å—ô± çd4DاÌ뺹 HÏI0Þ¬kVMÜ·á|RQéëÛ†)`bXüÃûq 9|e´íÚUV ô£p²áK¹3ªä{Æ«%ÄÏ+݇2Y“¥¸ò¸xÅÀ)7`ÉÓi·Z”Jêow®½žCvn}Þp{”èÞOÿqÚ"œ, íÙ¡Ñüù_BÌÛ? 耣U¾õ.9Æ÷oÝZkÓåÔïðÖuÏ«d(›»ÆfÊÇ"¬g´‚µïc‚«÷ '6#÷µI(µ À9Úïâýæë×ùÎyaü1ÃÓW½,.¨£’ÁÕ»õ‹ýßcȹY·W§þ¶ã2јpbüUÌÇÎuþ²1=o1`ÁlSÖ±ãZ&ÿÚ·üg<Ü{íÒ¦dpòé–¼ê—8ƒgv“•Ï&…‹ûéü¾ð󃟘ϘԪ¸8ÿÍwã;ÛÜødxÏ>_2Ìxz²\ Æ?36Ú¹ÙØ2DØ 'ÛjÌl~¹ãÂB¿CéªØýú^Þù\þ”îK«ñ:_%zÝËŒ‡ùÎÝ•uÛ&ÃÎoMN´Àû¥2ýcbÖ‚x4òÞ3"œ Ð#¡Ñð……ö§x]‰çÓL„?ÿ¯z:ýãÕùÿž¯õ— 1þÐ{£7Éó9É,"°§9û†³ -%þNvŒ}cÂütŒÑàÃØ_œÑ#ñµ£~ÁÜÛÎZÂh ÞvæmçÉø®2 §ÁRÂxµbŒWê…âÀ¸7Ü?Ü ñ»£~(ŒójÆ Úþá”;íË|Q¸ïã4DJ<Ä¥ì){š³o´Œ=íüQ¨ÿõ(ÎF9Èö4õG¡Ü0 ã†ù0VƒóÀãÞŬp)«ú¤X3¶¡Ô³“²aýÿ†ËÙaj‰g'÷÷a~)RF¬œùvJY ´¸·ÉcO«˜gžóyâ¾þq5óQáüi3ÆŸ6HøÓ´¸0^e‹¹3v,m(.ŒAØ ZÆlpg YÊlP1¿'Ê’ý§çþÓsU&ÿ™=Wƾ{ÊØ•]DàÉrnçY1Öb6óÔ `¼1OÆÅ¡¢*ÆÅ¡Þì,éÿÊgŠû‰RŸv%ó•3?QóÔã~¢V£5cãd£\³{$gJ<¨Ìª\œHÆÖöalm©G2eÉú¡2%^Tæ5ßw­Ä'YÊmòd9·AÇx²*T󣢞£9(¹À“5«'ðq"%|ê;êÀ<©¸ï(åãP·'*ñdÕ|õ¬™?Õ_ñ9'@â«Ç}’)oL/áãè—[%ñ”§þ¤2lžž¬/ó¬²fŒnî­§–x%SæXv‡<¦¬9cÊfJ˜²²Î‚U$cäx2ž£Œñ"YcQ2+s '‡úÑKÙŽ´韂=÷ïúí_ñdÿUoý«¾úW½ôßí£ÿª‡ì›ÿN¿,Ø öCÞ ÿUä=ð¯úíu´¿ÑÞöw}í£—ìW´WýUŸ¢ÏOm"p`¹÷§ÔߨŸyß)) QÂ?Ô0¶µ§Äß³ g;çÇXJüÚ˜1”eíÎüÙ©—§/ó5¦Þw‘ŒýÙ)¿:SÂ7ÌaþÆÔÿÎ\âÇnÇüŒXâ¹3Æ„œñ· ÌïŽ2`(ïÕW.0_3ë Lj c¿è÷ÅÀX…ܓӓqµ¬YÒRFa&®¢wšzSaN+=hÆØƒz汩n#ø¤SÞ`$ó¬S1¾„ó­KcÞšœãj×)5ÁY®~ŒÙjÅØ‚v¦`$Šî†¨þOý3ž2ùÏO™³ïf@Yaû¡rŠüVÎà,kƤìjÆ ,ÊLc~”3h`¾ì” “^‰Ò¡ä˜ü¾¨L‰o'-w”eɼ;õŒ5È=<­%¬AŠ•ƒR2æ÷'¦žž.X<‘(ó*7GËøÖ*Ƹ–zSv«?*å€7"%gÞí:‰O±”;!å·rñ[}Q”ÂBð÷¤E©” üVózoGËx;¾ÌãÓ‹5@âñIù;”íƒ20~+å`™5Éã_SÖ`Àß°9“‡²eXäîŸbÊÁJc>ïžÞ åag2¿wêýiF½Þ%üV?T6ó}§Lìl” cOp¯bÊÂÊéÇp•3†k¶„ájFÙ=ÌÔ¼‹à ªc~ÅîŒã*c~ðzæ ê#ñƒ—ri]Ñ?Õs öXÞ_ÿ®·Ò¾J{ê¿ê§{)ï¡ÒþÉ{ç¿Ó/ÿªWþW}RÚ#¥ý‘öƧ/ì‰ÿ“~X°J{àÕ÷ ö¼ÿ‰±´¯ý]O“³F¹©J”%§I‚ âŽÒ¢äŒÍ¥ðû(/ÇŒñû 2ÁWÝ¡#ÇJâ§î áLs>ŽãK[b_òc~ÃZÆÂ¡éì*y<>š€ÔgXË8”{*—ø¥;0Ž4õÖ0†„'cHPæ©/cžªïÔ…ù¤sî åœRt_Tv=Éx7ÜÝŠ1£){/€ñMÓP Lt5JƘ{”qcŽ}Æe@Ù`E[I¼…]kÏŸ‡'ãÙ(°Ÿ dX(J‰ÿ¹%Ç^â‹JCùa±”°K$< Î.µfL=cêY3ŽåÖ¨% =êHâûÏ8íŸqšÉî8Í’}öLúŒ1ýY+%, Σ,h5Kl%cIP>˜‘CˆR`¢û1~õVD™aÒ»£ô(KL~?T6Ê‹ €ù«{¢t(+,_ÆÑ¡œB_T&JQA`éPf¡J+wÆ‘°Æ¢ñGå ”XŽ"åBg3v Êœú³Kx¥þ¨æÕN¹Ð9(%cJ˜a“pgŒ0 ³Ô’1Ks$ÌRsÊéAéPrl$*Æì1g\ Ê-5cþíi(KÆíáþíœÃ¨ü§çþÓsMþs{.}îSæŽÒ¡, 0,(§L† í‰Ò¡,ÂrÊìšó)»Çœñ3QÖ˜ôþŒÝãòìÊcT£r»GʇæìÊcŒD™3>4e÷Xa±ø³‚Q¢tnO&c÷P6tN•<£ ‹I‰Ò1æåÊjQ–X\¾¨l” ãCg£°Ø"÷‡q/8׌r/÷‚¡¥A™Yä1|¬äß,e‡Åé‡Ê©'0¢µŒáã‹20†gDS&£†ñi}P”qÊŒñ)¿GŽ­Be¢ ð¡ÓPÖXäþ¬Ð) Cà ž2Õ(¾ cD›3&#eøØa#Р̰¸£´(Kƈ֡,éøe@Yc“ðgl4+Ƴ¥ ÃEÂÉà\[…„ËhǸŒÙŒ—A9iÙŒå£fÍ…²$lFŠþÚÀûOÏ5ùÏí¹Öì³eÓgˆ ¬FÉŠ \ÊRÂLãlHÊÚvGi3Í•‰²ÃD÷g !Lx-ʓޕ†²Âä÷Gå \(e†…àƒÒ£¬± üPÆ‹ôCe£ì*\!ÊŒtÀBÑ dX,ž¨4”‹FÍ Ç¥CY2ÆeHZa!ù1–·5”šeà°âR¢t(+,2cIZb±ù¡2QXtš\+,@_ÆM£\T6Ê 2e&ÇÏ 8¸–Œ9¤gÌ!V¨î¨H”¬'cɱp}QÙ.å¦É›äqÁ°˜#%ü!Μ”ò‡(sÒ‹Ü•F÷o7-eÉDœ;éÊA9`ˆDɱøJ8¸jÖ;œ6wT$Ê›„'c§É$,\+ÆÂ¥ ÆY¸rÊ%BéQ–ØH|£HŽ Å‡ñpÍ)_e@Y1NQ&ã q>%åÑZ¥hÏ-ØkyŸý»û¯Ö$y_ÅG–o_—sÒ öËÕ+yŸ¤[a’× ®=ò~H{!í¼ÿñÞÇû]Á^GûÜ¿Óãþ®¿ìm¼Ÿ©L„^öW}Œö0Ú¿hïâ}Š÷(ÚŸh_*Ø“ ö!Úƒxÿ¡÷%¥/"0iýPÙEV™>X=Ê ®/ã”90æ6e3êJ ¼m_ƤUÓ½|ðZ”œ±XP¦¶“@Kψ`¨Q2ìî(+ºVˆÊFÙ1n¶ ã,ša‚¸£,1Itô<öˆs“í‚}A‹2ÇÄ10YNm/‰2dž²ÆdògÉDy‰VXï~¨Ìûšr#KV…ÊD9`kP挑h‰5î‹ÊF¹4Ëã]P ¬o5JÖR`"Ð3sŒoMYˆ™(Æ”5ÇZÖµ˜ÖVtÌÔQàÈR†¬®“À¥|CÆ6ÌA)º LCcjº (Ê‹&%ÃÂñDéQVX@>Œ±h…äÏ8Ú ,¨”¬ºÀŒÕ dX\î(=ʋ̗±­°ØüQÙ(,ºÈÜXk,@?T&ãÆªQ9(,H Ê\ŽŸA.pc­°8}sQEªFɰP=QZ”9¬å-¢,±pýP9n¬eÙD`lSþ¢ ³%Âv—ðýYq»3þ"en«P”»•²Â¢÷•0Õ¬¸ ´(Kl~nlJF÷`‡[Æ8ÜZ”›„J‡2“°c­;VfÇŽµÄ¢B¥¡¬°‘ø¡ (Kl(*Æ•ccQ¡2QÖØ`üPÙŒÇÈ9žöcÿOûíÅåýUÚ[y_ýïöSÞ7ÿªgJû%ï•ÿÓ>ùW=’÷Æÿ“1Ó¿ê‰ÿÝ^XûWã©¿êw¼ÏñÇ{ïkôiéçÇäH+"p`ýQ9(L ÊÆ•FÏÃaâø¡²Q.þ5å^û¡˜H,™ÜQ:”%&•e‡‰%ÃÄR¢4åε¥Ã$“c’©Pi(&[Ê Î¥GYcŸòGåо… ¨Gù`?Ò£¬0ýYBR†uý­&¦S…R0æ+MP”†1_ÝQZ”“Öe@)ä÷5‡þýG†‰ìŽÒ£¬1¡ fµ ö-ã½ú¢²Q.˜ä‘(9ö_”ö?T{I8Õ~¨L”BÊ ‹ÁI׺°(̰(Ü£ZŽÅá‹Ê¦=‡q_åX(ž(=Œ5L&JE£è(0_)ïUßI`½f£ì°¨‹Úûˆc½Úa‘iP‘Œ=­GYÿ3NûgœfòŸ;Nsa±iò: 4(sLbTʓٕrÁ¤Ž,&ð­}Pz”&¸?*傉À’Ý¥GYbÒ«P™(&J†àŽÒ¢äX¾(Ê BÊFYca¨Yq¸ P2zV¥E™c±¨P™(, Ê Ç•†²ÆR¡ (’šñ°í° 4(3,*%*e†Åå‰JC)°ÈüP™(k,65*¥Ä¢Ó¢Ì±ð|Pi( ?*倅ÀŠQ‰ŠDÉåøP”5§ e@Ùa‘ Ì°P}P:” V…JCYaáú³âuGéPVMNvJ‰Å¬C™aA{¢ô(k,l5J†Åí‰Ò3N¶/*å€Å€ÊAYcÑû¡²é>JFYÙ(Ê ?k.( ÊŒîÁ2v¶cgë?[…ң̱Y¨P™(6”6w”e… Äe@Yc#ñGe¢¬°¡ø¢ (Kl,¾¨l”Œ?*‡Î±ÑhlòØØ´~XÚþ_§I{ì_Õþ'}UÚSÿÕØ­à¸í{ÌöïöÉÿ[ã¶ÿjÌöïöÀ‚c7iÏãýN‰ÒÑωIc@)°¿©Ù Û‰’c"©P”Ê•CÏŸ`biQrL.+L.”&˜%£¿e@éQV˜l¾¨L”&&;*’þMÇoô7­˜€z”%&¡/Ê€²ÃdÔ Ì1!}Pi(ö15  d˜¨îô7U˜¬iô7¬Ø»ÒPÖ˜¸j”Œ%¯;ÊØ—žõ¥ëf˜ÄJT$ʓ٥CYbR«P™(;LîšàØŸPf˜èž¨4”Þ^Ê¡ç{±7éPVX~¨” A‹²Ä¾ä‡²Æ¢ðg…áŽÒ£¬±@üéïPX(”9‹'JKÇtX4æôœJ²ÄâñCåÐÞ„E¤£çy±Ò¨°˜XLÙ(;,(;ì9” Ë•†²¢kn¨”ZÀ(Qtí å@Çsô·OX€Z”{LJ¾¦û÷•켯[[·fcB;?¢cç|}%çGø¼’®«kAXow`çIt ¬»ó®áß³“ô8þg;ûÏÚ5YŠûíMMî{ÐöOæÞöå’ÅÿÞ´ñLÕî÷Ü'ê“À£îD¸oÆ‹y{|F…Á¯»Xí5¹úñåB"ø* þ¼Nåž^mgÅõµas«¼L‚[ zh§=Ž!YMm›¯ï.ú0Ü¥vD~y~#縘èÁ9xÜÓøE|³E<+#½âA›¥ÛrS›ñ‘ŸÊIŠ!OWÕ ;¶ù2aÛO_Œï8Ü]¾31Ïo…û„q%Áw¸9p?%^Žñ÷>ëûs]‡xP[uwì+éÖuGt ‰³ià‘ÒΚTõû Ú‘áäfmJª*ìG\Ð߇ÆU`ÜÞýìŽNÿ;GR§Ú$X°"uk»ó1ÄöT#‡)Sêj´Ç9œì«Þ$*}È¢B>µ_6•zYª‹™è¿ÃýÓi|%Ưp®çU×Ô8˜Zöd9û$2ÏñõiŒŸ^¤í­c @þ:ð¶3Æ8†y¾ƒÜ·®^y]×g¹¢¯'÷}ç¾Íô:*¼ŽùÈýûEÆA§«år~Z%AÈÝÝ/[^!ÜOóPZé=;] ýt5µyþoÜŸHð½úÏçGq¸ð}q07¦^-»êIà:¹ôþ»i1¤KÑÈ—\ì`îÏÚëÊp"ø\åñ6¸O÷ä|CcÞcÜ®ú-m½ ?ïÓö½ªþÖÃêà_ö}Ž!ÝZÌ<;ÁÞöލ3l^8I2µXQõÄ‚BŸ—ûéðûò¸ÜGÃÄæùü² x{{J/ý«Rë:ºJ,ù“3úÜаg7ýNž«Æ :pjèo"p¤òü±¸‹Ô¿Þdm–ÂH vŒƒÎ冬‚«z=ãvÍ÷íb ‰¶žñ2| D­­×ywd8 “ý>·µÎÂB¾n?VökbQRÈsŒ'/Ûí[ÝVqpºÄû ¯CzHÍØ¹Äg`,ÙÒlóˆû½àQõ]'#bÃɯÖwwÏó³æ÷ûøñç(øzµî¿iÌ{¼ÎÂå1s¿™ÄÁ_Kì0zè;«Ý²ë£cÉËsßýŠdu‚¹†}]'ýŒ ¼çʯÇ}­¹_JÔjC䆽ýY_|‚•xoé µYSêaç‹^bIܽUVºõàRѤmmÃɼo&wµËó!âÏ[ð#û.ú{r^”Ÿ¥ÂëøW]ç 'tÌKÛßO¨özR,Yiˆ~«Ç›ó±Ô°8œD¯ê¶¼VÓ…"'“ÿÍ} ù÷áÜ©/ª¯cóª×ò@7ätr³©«‡uÑjaî±DàÌ–ƒåë)`±ð÷Ùú>äHñ«ŸEÁ—³ó1d¾ëcR—“{ä:xý«O×9· ¥™ÿ“â±$´7ÏWþŸ6,ÎóËá}ˆ_Gð{Ìó)å<>ÎK7Ö^çgtó~^_‡1Gž+*œ¹EªO¼ñqN,97-'Ánv˜ýicæ9ádé”!Ÿ•/œ·Â÷)Vˆ/È}¢Œõ±.Kaw¤h“ûÚëÐF7¼Å’[0É©³ÐU±äbͬ+ïVõ„å.gÛM 'Ã8›n_½@Ìçü<ö’¢¿ÔGRŽñ?´ªþa¿ÿuh5ÇïîŠ~·`™lÖó7ûcIû‰ýNeÙA÷ræ ã{¡§‰ç›{³çâÝÏí—èÈûˆ±N0¾ëõ‡ö¹_‡w¥k6”»é&ôEKÅ:5ŸÑi 4þ³,ìN¼pÉ^ÁG|?ð>%øúçùØ ü›¦Àÿ6Ö ^g÷ õ‘á-¯CË1W:>‰» ¡î7‡Û߉%—ÛV©mêl3ïVÌ:œ8MzTõÊŸ¹díÅÛ7×ÏÈ}ü9ß•ó/Œuqî×50_“y~ÕMØûÝâd—/±ä…ól•r“=4In÷½_8Q ¦¤Ñ¹âýáïÿSs.tºÉÊÆÅÅ„,·ÌÏ Æø”þððô5øØÈÑÓÇö&éÙµhåÊ×Èü÷¦¯·u+­6›NŠ´õ~£®4Oô!úöo±®ÛùVx“ÓÚ 8ÇXÿÉ6û†þS¯ðžN„eçg½8ÕâiÜþ×ÙF}áIÜ ·ÏGÂÉ×¥å?=/îSè=1±]R›6'Š‹y{#åüáíVŒ;h%Ô^GÀx\ƒw‹ ÃØ ‰0uzÍûÓl¯ë.ü®ÜFéá$ÑgÔ«3÷òž3çÊ ÜêrâûHè‡Í—]ðñ2YŸ¥h{mͽgcátòÑ*Þ !%òAñ÷×Èú³ÞÕJ>î õ}cØN¦\/GöΘ_Èw’ûró÷ç¿äã¿ãuB:lO_ï vëÛ­îl›¹ËVVsœùSäÎÉ1íàA3ße¡ØGŒxY‹…êBO~¹ R>»ãÇûP&¬a,Ø÷n¶¡e±Dê¾%aóÄkl<Ùâ,jpar8ñ ¸ ÓÂ\Îíàã1ž¿Ò>¥ÄëÜt¯îåÇ10Ésû¥.×`þ¥¥OÎz\#NO®>݆ù‡†“±‡*ì ?”×oÎ_Iñy<±6ùxY*ŒùÏæÊ7ÄÀQsyR±u 0þÓÅÑs0þ·ê:7: AË;NÜ*+]~/Z(úù9=ßàµÀ2W¬kÁŸµ!>ѸUñÃÆijmÕª”˜TsP”õ~[&sÕ5Rl¥|È©¶ÐbÏMûØ?âpÔ¬»±@|Ï ü½"¢?Þ_ñµÿçækìo]…s)w|•µÀ5çéU{®±~ÚŽvœåEz†JÙßgA!ŸÞvs}¢†o,&úækdq±Ìö†PÄ÷₪5¯¯“aê=î¦ÇUÕ}”Ñ!óh·/3y?‡!ÝîP?Wì#¿ŽÌLnoYÐî­ ó ·)ø¾æ­É†,Åï¤NSjV¹ U㜋6 ¿C¾±›þ䙳é¸Åü ÎP·Cã˜ÑCÃEm~?ùo6±;Ê·9ü+QŽqG[Ü©u&:®}ßëૺ#§šï¾o~LëØ»cŸC£@à‡“åuþ_n2¿¿þÜå>©'ø7*0^½Ù“¦{FÃü•Ë,ê}¬F9Ï||·oÒþèóQ°³z†“ÒV-–FµôÇ<ÿß þtpi‚LìKÜ_Wðµc~“x“ûÇOP'®Þ½{!Uv›Z»&¯¼N~¿^–{|øØë–0 ßÓþ'f÷nZÏ§Ðøé^µ°«O–åñkîÖù}ƒñ:Ë·ìꮽÃôomLЇ²Æªqܱñ±ÛðÝž}þ²(Ñ+œ<M3ܧï®àÓ'Ëç/¨Æ¸_*Ô¯áåq~¤†÷µ;úÊÇÞmv¬U[Ì ºdÚmJ8ü<âij}y}•×™Àa-%rô¤ýA‹ñGäü¹«æˆp¢„ùx`¼bQ£Ä¯_} [Ƴs™ÃÉÛrQV»›ùâ,^½,§tí6|oO(þ,»_£ÄxÅâ~<& šY,?™ž¥›WutÔ‘z»¶>ÓVL¹ô´J89óS;ü€Â§‡žûtòû"K ]ؼRO!¿1þÐì*^Ç\† ¥¦Mx³2št¶h?KG”KŠ®ø#³ŸK,ù®![zÐŒš'öaó‘W§KPB[¡¿cÜríÚ=¹Gí•ÇÇ÷õ`Ë™[tdé¶Ñg9€À5ÑÛÅ/%h†Ìï·ÐŸJŠ}—óĤ\-ÆoPõZµåµ/±ñmtô¼gçiþºüìm®Cà…_j¸g²ÍÏî~Nå9…8+•Ó¾uKß–+޹«ð<…ëð:ƶ>à"”¾?øZŽU|l±ïÏ©;:råvöÓºE†BÇÑÏ„¾Óè#Üy2[¼ÿüz|œËßÒñ‚É&¾>t&ú¼™^%6ŽšUmo®ŽXï¼âZ·çØ·öUàßò®g†×— ³ÅûÄ¿ç¯ñ¾Îù´Rn¢¯³Wžecx§TOøMfgÛh\;Žº­ß^Û͸¯nîçô;dÞâxôX“¹KöVÈã±òuã)Ô—ã«gЉ„ oß{lÎÐ¥„V´Ž#ϛܶ©³¹ãìjÈëÍVe]¼æˆã·‚ïs«Ó(ŸY‰ñŸ<5zHÿH0-žwâºüJz˜¶cëQ=áTäw¦†¨7ŽîtìÒÜBý†ûŸ œ¬F°úZeÛ}k„ø*ŒÿÊ•¿ŸƒÀ¶.ÚFè`^ýdÕúqq„sw.,Ÿ³í¡†lc}¡ÿ¼¼ñ:ÎŒË·Òy¬¯C&ÙïŠ9 tÚõ¸ZÞ4QlzñÓLz•éñÑô…¦!Ã\Õ‡³æ‰ù$ð¾1¾Fm6ÏÖ1µ×{üÑêgrÏ@ScC×Á|÷òÎ#Ë+öÞ:õUWXê[®š}²†ø¹íÝÂÕ‡ð¼Þß6||&Œ×˜?6Æí¿“Ûœ1Ô†|³ŽqkâÈG8P´ø ô· wv’†T3.¤ùâª4ë¡~uß:o>Æù#R_j“ÍYŠ©ƒ>mæ>¥Ï½µ\£Ÿý4ÝGÜû*]Ÿôî 3® £½¡!oMüÓ­\^ßúÏñ=Â×uòñˆ1þ„ñ¾û¢œ†#]}÷7õÖAÏ™;;µ8G*ĸ—múeרñ¬U´†PúoÀ¯¹„s–g—o4W̧þQä` ñÊ yŽñzµÚÔ/ª>OÎYå¬åv5Ñ7ˆ'9;.ªPu,ÐyÜ›ËÒÍáD· çê7RŽãíŠóìv°H8„ÏlRññ8ãvÄ“Þ&ŽþÓO„T¼¢! µ|9tôñûóucþbg}DL¼»Vž+YOŽq6}¨>Åß/žü9©Ûx=|$ãƒãó™’ëì´Ç[|þüï>×"T5e¦báu.å®ð:ÑU胴‹„÷shÀ˜¢Ç£ãÉÏéŸïLÚヲÌÌ^§!G¼)ÑÐ[\÷ä·–=—5eó7Æ÷À¸Šcý·_>vTì³ û•Ú¿èã ÛÛu5¿µÕ’{n¿ÿÅ«P~ ù“#öqé¼ÃdK–Âpùȵ•þG õ\«Sþ:èúíª÷µ}7H“íÞ+œ—9Àj¿O¿Á^C¨[ýù˜Yb?àu[P+ßûMŽq§9ô%{Òa¸ÞòÅËÕøÞœ{ NÕ£Q7È´§6–ç@m¥Œ®!÷«l^?µâ,ñs×,ãh6(Kà$)0ް®z(5Ò&DË ]ún_¯÷h_lÛÒNŽ>u:äI„}>G§Ä_;.agð:5òï!Ôsžï|ûûU(k ùÆüÅëTþ\}ALæn˜ç¶üñè«:xùioÊÜ1 äÂàWÏVê ת¿ýУ‹†l þÙÖ¼W¡÷¢0~5›WK <1g@>N¡ã/[x¦;î‚í¾Ù­—_ÓAõ~È=È)'—U@ã“çJœí¯!é×x¸Ì.Ä×ãD^çãJÞ¬1$½+pî›1Ÿ·f)v¨üW–™¿>îPô“®“ܵ,I ç—”ùù±²5 .ºwvö@ ‰ ³p]±j¶x¿„uÏ_"”ÛŒùŒqgÜÿ@Æn”ª ¼ÑAÒʹ¥}ȽÒ_ߘÚŽv * Y×YÖeäm/q\%òöŒ<úâó–®o+0~Ÿ^mæ›&l€kl¶ýú¤ƒÙ•ÍÞ5Ýž@¬=xfË¡Î`Ü–¬¥!OMmmæ%î/<.fÐ úmÃDZÒum%ƽ—dú>z5?3Å#¬xìØdyDu$tÙÕ}b#ÛžÐäð¸Qð+Œ´0Nìfâ™ š0 ÷waüÝ.ÿº#ÆÿpÀôFŶ*X˜þ8,®f î{6Ðó>×aëø —-IEú¥…»˜ív‡wÍ*´~š ›Z¬…iŽØ¿×G·ZW7Ç„}+a^­ÆëüÜô8ü`¬¼Y“¦|Ü0èêK¹û ä³¢f±²Ë†Â¯( à##Ëv¸¿zãZýÉÇIÓÒü83èCd€¼YZ¬ãÀbæ ª/MI͉êÞ,ç ­ëGu»FöÑב{^¿ã\Z>Žrè wfÊÁ_vGÜeMwŽŽƒäÍËŽ°N$¾÷(hëUíkÄÕ0’0!Q'Ÿ7«ÐºSkoÕž[²ÅûÀçµB \n“mYŠs§ÃºÙ žE$_ùkâ Á4÷±S¼ÉIÛîõ¬áV½ìÇÝ #›ä;\û>õŸ§°þ÷‚íë×„Ú {}_^õkŒK©o=1ŽPL(¿|“Ó™DÒ´î¥Z%ôîëõ½Â«0"ð\gˆü"þ<x¤lJÊ~#Î8H¸ÿl½¯ãÿpÛšU#Wù}gO|7ün²ô["‰—'Ç›/-_Y–<]JCNë´B©»÷ü–º™å×+1ݳœ}Éú?«a'McÛß$%W}+Ó¢ãhÈ1r±OÞü6ìé±i…Þ“|¿‘בÐïÛ‰ûEÆ|ÇëÌÞwåÂI›ÈãS¥÷iÏcÜ'?™|“Ú>qOëì iÆÉeR—â‚FÏû˜0ï/|þÆydÂsaûPT–GR[‰ÏâÁƒ;ÇC“1½‘Í7I­qò;­†Â}«Ò˯Ÿ #'w©ÛγмYGçA<„ù9ÛŸÅëè³ÝÊe8ï &ÖW/Ó3>ä qÐÜ$ó&Ûy.±‡òFs§Ï #õFí »‘=³Ð:•À×Þ³Œwö|›îã×î"ß›×Íëžjƒžß¼ILC޼sŒDˆÒbÿ…#fˆù/ÔÇka=q{–"lÑΗWî&kb¦½]ÃŽ–uóö¦¸Åù±Ây…Æ›)72d¶¿ÞÚp®ž”&ÇøåÖ½(Òâã^rj™wø”cñàýhaœYÅ[¤òðÐã‡~ƒ™÷êý? ïsòúæg?l Í/|ÙS·=ãAk©À¸ÇýéÆç~÷²êàîÉñà³&~N©î·Èý©wj<ªî gr‡«Þw #>=Â`í­éb^óûÉ×åãœv +êÚΟ°>§Äø°¥I¿»•)Ë«/.~-ã–0ã™4e`K“¶#àF+óѳû†‘©•.ÕÛXÚ£Pþ{I8WÖz@œW¶n)ÄWaüÌ“<2Ý’ø×Ï–µ¾Óc|—ºEààôô6#!kl“/“¦àçOrösk1U¼ïÂ:GIqþ#¬ËuÎk4æ5Æ·7‚´Õ¤hŸ]ÖL»E4ÙÏÖÞ¹EfÕö9óç±$iS­%aäËDºs2•äLsÞÿ1Ì×…y„е÷\™Ä95§" =ºž¡¾•§Ìµ®ZLO¦Dm¿áçí ÏÑí0r'ðc÷§ŠëXüþ„޲VuâÎs•rj M@ôÁlj‡I§Œ:5‚o@¥sa-kêIÙ.×.ŒIKêXOî…÷Å¿XM)ôÞç¼N¾ÇçùÆ|ß‘¥Xú%ræû£¤…kÚø?nÀºðÐ\O”>4C@JÀ #ÍêV¿]ÿ°{!^úÅ-u›ï(?׫Ûl 輯óã û½ÇÈkç.%ÀÒy/è¬'Å#¢¶~yÞdÁµGNìFF5X>Ëó•;±áËtŠ ÜÎ *zÆš9vª¾ô~;!Ï1žªœ¯™óÍãäcÒ“ ãí`ƒG3µ{êIàñ¥GŸÆv‡ô—mâÛµ#z=\‰]7µ—1gÁ&àô¡Ï‹-` |\aÌsŒ?¸Ûä2²ä挖Ûû$@o#`ROÞe Ÿè­h ÆQ™"ŒÐ·K¥4ÂùM|cúà‹¿Ù‡Còó§T¿Ü¬àõ÷ÝÈâƒ'@pÑ3æÝꉰîe )Gü&:…‘ýw¦;ä†L/TG×w®k«=ù^Oóõ a>(¬Ã©ñ:s_NÑÊɸS”ë&Àâ÷ñk“ìõäzÄý”-aºÓâг®aÄÚíÞ6í Bã]a<úV\Ö]…¼ÔbüÑÞ§R@rãÛ†^îÅ¡VdÀEç¡z"pVÛsƒ‹Ém‡†‘Z‘¦sâL+´ùûÀie÷~_ò“3`Ü™ç–|3õ$ù°ßÿ‹«e"´­yÀÔÓEO„<êëÖôÔ.ŒT[­{õúûÔ㋯ì\Sqœ(Ôƒ07Ù™¥8ÒAīм´ÛÀD°˜³Ê÷¹RO^W²xU'²´˜Tã‰Kõ0Ò]Õ{É/³iâ8ƒŸà¼5~ÿ…qp;hí0TYd 0ãu²|“~ÇëÍŽM^óZÖ³øì¡'É›W{ñb´Ì¼Ü¦˜I9ÐpL»ïnÓÄ<å÷§âû÷õ‹Æäñ7ùqÛ^ÿº@L|Îû¾ N„kö>¥ìëÉíUå=¿Y…z#›§×~J„}¥iâzÔ®R57õx[”}î“6|~$=‡§Äø SËö !#¢ÓÍ»>N„"¢×öÜ¢'¿¬¿<Øo6öRff()UµöX³-S p†_Šœ1ay‚ñÍ,óqUxη4%]n´è^õ&ÞtfÎq=©èÖpƒæãpÐêz÷k(ñû<¥ê¯CS qûJ§–ìµ÷“8^’òOÕÿ·ÿãá—CÉÄ¢K66´½ ó†Ò™¤ž|ÝS·ÊöÃÃ¡ÜÆ&Ë­+„‘q;çNmÕÉ]<ÀŸsúï.NËÇïÂ~!ãLbüžY¦qØ·ìªÆO¿ ½JŒKÓëI…µmº®_K©ÍG´ #Q˜ã&>ÞŸ…Ïo [o,µ#‹[æç0þíWŠ;ÂÂHïIW÷ľ ›­'š¾z¢'Ӛъ=xF<]ýhP ºÑ´H›R“ ñž?-K™õºaÞº ?Gc¬ƒ]8Ÿ/ýÞ«â-HYPj\`Žž­·(?4”Ô©3åÁ4q_P8m*ž'Ö[å[‡R`ü%Ã[9þ>£!?~3œp 5˜W?Ù$‰,·m9^×[»>¼qW( ɽÞÃ[æ!žçÎ-–fç|[‚pŸØ¸ãÚA ádomz"ñTµøÑϬT©×öëÌ€¦=áa“¾E+CI]ŠgÌwçñêˆç‡0Iw7¹ÛøyDcžcüâë¯î—Èù¨· ±\·-•“HX•Èæ'ÛÛÁ“®m^Ç:„’Ä .AÅ¿º¾Æ÷µŒùŒq2×”8EpÐü¼Çó[ðsó Å9~ÎU?›U¶‡Ã²Àû÷œCÉ£ŽÃêÕ];™×̲áçül#‹–þòYø\ZŒ7,ÁVŸtò›ÏëáÅ®å\Û$‘ÏWe“® 혯¯íçá÷ÎÑ¢÷ÇIbý5€ϯÙ|´Î)7ÏwŽÐ€q›Žjø¶Åi2ïÐyû-õpÄ'{†¶G™_nøûàIµœu3°.ã«f<¯5,Ü??£íÀ¢ÀÏ“IÏ šøã¸­”o¿H¿Ódh¹Üîáöz89¿]…â.Id{ÚÈbÓláø9¨^®ÎGŒ‰7¾ÐûR8_K̯è¨ý½V.m#žƒ0æ/^g³Ó™£GjG_V=|"fé¡n± $2sFÑèBä³Èa]\ˆÀáVêG¹([pAWòÆßÓë²!e_y:‹PõàÙdIj±ušã°<÷vaê¸0òqLÿ1m7ó×rPƨ7CˆyÆÏ óá½¦Äø¥~”éîú9‚t¸Uñ{«czx“ž1£ß¦$±ñÙ.·qö`Ü.kFðe³·VÊÂÇ)ÂþÁ1¥ïKÆÖÿTù¾ÎgÈÂCª·Ê=”ÞêÛåò¾$Ò|IÎŽƒû³s–¡Dè·nâøS8¯ñŠõçhñü¬0ïú¨ã_??(ÒùŲ§±Ôxª‡`ÙÛõ]“ȉ.kCzkú€É–%¹]·eg‰óŽÅÎÅþèáèu *M"År/(«ö‚w;Ÿ©Æ %tuá@=ñ}ÌÏAñï!ìßt‡óëbg–nÞAÈ{Œÿìâö kŸ#½®m¸ F¼®7ggÄå$rÕ$åôÔz=`û·Yíû:…’ã ÷§î:†ã:›¯B6açÒÚç?›ìÎR,.«¡;}޼œýnîåöIý²ûÊ×’ÈË¡£‡c «­üh³)”„ާ#Y1/ùûLà¹~µòRžïÜ›ã/*ë´ojËHìòó¸fh˜í,;ªü­$ò>l¾FnÙ¶,jäWéb(IšwîT¯ÉS ½Çøº‹1Ï1žñ˜ÅöHÒ¹Y§!f'A|Ä›?ÉiIdý: ë ¾÷ñ¾u!”ýÇMÌsþûiÿTb¼"Vl{þ‰$K†¾MùàŸ·¹•<þ,‰´{Ñ÷Ô¯þ`œ.o% ¦wtr=0‰ûö•Ÿƒ>¯P‡*Œ7Ÿ€ržxå>ßVõjÜÉ´ºPýSéA—Oª;ÀbÛ2¥ÌÇ„’ö;v»<ÔM¸>>a`ø¹¿Ó¸Ž®ÚœšÐÀ,ìu'6®aë„¿ÆË™=ê½@\,±øÕÛ$Xð«ß¢wÅ’ÉDC£‡]+ ƒõ?ÎKëJ>½ð ùª™,ÞO>®ÖÊÁÞ:QŸÖ qµwîï·§ÆÍ»@šSßP3Vm»{¯F2±Ð.öùpo8d?øâYïPÒ2kÛƒñ•óÆKÂzÛñü7_×äç8ù‹ñ3é²áù3ºýÄný“Á8ì°J&~øÚ¯jå 5O§%¯öÄuºÿÀ]|sî.ÿý…°ÞÑø~›1÷d)V¿<æ:aìEbf:&·è‚dH¸<×¢cßdò4QßjÇt'¸;mƒÃºPráðv³à““żàó+C›]aOëEº¿&ÇøoÑ¥‹d‘]NÆ#u2Dw o“‰ç†ÐÚÓü†AƒU§²¿œ %EËÞ88³Œ›øù…Gì7IýÍê%Æfïç:“ó~Æï}§fÿ·½/‘âÚ/ž«M†[«¶ŒM‘Lj÷+rzƒ=]ÞyÁÏŒPr,ýÄÓ6¦“ÄÏ/üý‡­KÆÚX´Y0ãPó6À÷ÃŒyŽñWfÓp‰ÈS{—žô8^^´/fa2QLì8}š¦7ô6NB‰Ûeóøµs'‰ë7¼òs´B]³™»6+dYH[ö;–ÿx©ÓzY•ou™œ©µ8¡d±¸öZã£^™L.MϾå4ÓÆ„’¹Ãâ§5òžL„õ”\aÿÿ}:êß3÷2Ñõ¦¿xI[Æ)º!™¸n©z*0°+ܵ߾²Wz(©ÞRqê)â}æŸWãmÓm€_à÷[W·¾ÿdÌw¼ŽïÁ?u/áuºÑá‘} ìë§ø Û–L ¼§œê` SN¼ðZq(”X4§¿,›*¾wø85¸ãѯ×åµÅz•®s0~Àñ†¯=½/“ÎÍéˆ!¬Ü]¨º?™ô3]ÛiQí¾àóªá(ì³¶¾Ø¨YQõk†¯’TðÑL=Ó¢X ù=Ã3Íc¯ 1êåB‰ÍÍöãsãÜÉrßù¹ææ¿Å÷8¯ÃÊÙÓ–ŠfãlŒ»Èu³Gí¢HéÉçvÆy¦Â¡o×ÌLj§°óŒÎÐëW+D|_’SBùT1¿…ñÙq"¼où8ÞZŒæ÷N“ŸÃ¢HÅÇãŠmÜ † õ}Ú¦]÷•.:Çö4þ¢€|rÖ®á˜7çõS»¬¾¢cæ7ñ¼Šô}nÀøÂ¿Eôªí¦F¥Bxuz#SHr‘£[š=w€w'n\àJöGÑjš8^æ÷[܇`ó/éïGMöe)?5«{(Š| Ë÷ÏS¡j—°ðŽ)äà :aé-î®^7+”,¢ÃœýS Ïka}K&æ?Ç`ÌoŒ{÷ꮵõF‘q®-RZW¼ á7_nU*Sˆð9zƒŸ=áJ”¡½ËèÏNóã}§¥‘~Ûë:í@8GÇÖÅ1®p¾-Š÷á6¨âššBþöš÷rOO¸àö1±c§P¢ËrºÕï}Þ:ïßÂx¤ˆ8ðìõ-xZ„_‰ñU\]d@h‰J£à·ÁS?vq7ïR~Úª^T=àÃçþ#V[‡á|FÞzϾ?ÎÏ“Kç¢Âøo~¿8þ~©aúÚzIÀmØ÷´“Á~q òÞþ銞½`Ðá±ÙSð¾|ÒY”íöm†˜/Âï?Ùðy?çÉ×™ùŽñ…ýÚ(Òôæ¾×oß¿•‹»®õM!ëAârªÔ¿~§ÝÊ ¡$¨Äøß¼f’ÆŠ€‰Ý>‹ç_yqœ*Îh{²,!Jeøõ)~‚ƽZä³3…‡c°+äKï£BIDßÇmSgŠýƒ÷ëŸkYžÿ-¬b¼Ô”¾† @ˆçèkƒÊµ¿‡ÛFd¤Ëª€Þ›¢†‰÷•žztî2“ðß ë Å€ÿ~XZß&û³f!U§ošMHãàá•ÇÞzÖ-•Bz¿ÿ^âÆbg0‡ ?Õ %ÛÖ]KtoFó Åó­Â:L«|¿?“cüÛÝOŸz€IÏZ¼™²æœsÈ Ús'…hR~_Ž÷K=¶Z|hÊΉæíCµîåÿæUûŸÂ8ã¬bÖ-ê2!ï‹\h=ùìÈþ2rNtv iÝÎñ9Ú‘púÖ„í“dž’kŽyÏ›.®ÏðóBÞ¶É7RbÜ©ŒëþŽê<¿Éu,|×”I%gkÚg ¦É¿Kº…áœøôBûåª5¿¾W¬ùSÜáë|½âϧ<ŸT&ÿ™žO&ìÞP/ÊžV1?;æ‡Bý씘ÐZæìÃüPì °9ƒGÍX¯JæM.—°^9BÎx‰°Ô…ów Œ¿£–ø’G2_rwÆ–ÿ wÚ“ùrZ1þN6ã¿J}9'Ñ…qÍ'Mâ÷D‹MêËéÂ<ñ(‚zâiQò¿à$Rî4å$rw '‘s§í‡+õT `E«dyrƃHcüXæMnÇX‰ÜóI'ñ&ÏdìiMcO˜ç“?ó&ç¬Ä4ÆJT³F༌©gžŠù¬pötcBø2&õÏS³f¡dìiÊàá^zÔ{Å—1ÎÌOOÊ„IX‰Ü›V–ÌÞ áEf3„F‹ÔKü³Ÿ6²šñi)BÁ<™î™qÏ(ŸVÖFðµ¢>¥–Ø|Q ŸÖÀ¼äý˜g)õ¹ `<wƧµ’ø]Y3¿ªLæaÀmó¡§uFÿüW=÷¿Óoÿ«^‹ò¿Õci-Ø[yOå½TÚG&ïÌûç¿Û3ÿ«^ùW}RÚ#ÿ§ý‘öDÞiß“ö»Õçþ®ÇI{›Ôÿ˜÷±ö,éÿ†&åÑF2¯Pó ¥žî9Œg£a>¡*Æ—°c| êìƒJ+)xèå \°gi ð)ÇÆç/|A]˜2e†ù ÒPVÌGOÁ8µ.•òXµvÌ;O˘Ù”5åM0&˜c‚QF­&¢'ãd[cBúÕüØ-™;gÓjƒ‚ú sõ÷TKØ_R>-åQPoOÊšy{RvÊY¤¨4ƨVðj·ÆðGå \°´(K,J/aPPV˜ ãS›a¸3>µœ1((+ÕúoX©¾¨l”cPÈ÷P[€O-c =c¥úàSS¢'¢,+LüÝ=Qi(ë¿`…QV*e…iQæŒAÁYaœ•ªd Ê S¢´(s,ZTÊšù¼gKø‰ô)/Œ3 (v+n%ã…I9vŒ—J9”QM>9^XvkFy©æØpÎ÷ý³›ì~¿÷x€¡`>îþaŽj?wÿX@˜»ÔìÜ¿Áœ·n Cp˜¸ûG‘àþÕ‘ =ðÂÅ|@Š1÷bÿÌÜ?37雹*þw ' ^\;/bðŠÙ B@‰¢¶ñÂ6Üã[cE®^î“4ƒP‚SRŠÂ7 P£ì¼ ôÀ”h 5šÂÁCÏ]Þ24ˆ‘;&h wâªïãĵ‚(Т‰ÜïÍÜ“>îø6qÏ7s­™¸k9qmÜA):¿™‡Ò‚@É]k Eó™@¨ÿk9q™kÍähLK‚kMt‟»Ö ÀähZ35š×Æ}F¢ŸR‚F6pßšè¨  ÛÍ}kî[S¢É­ Â}kvÞðÌîá/úÖ¢íßóâÊ™×`ÑÔ2/nhzàrî[c^\ sŒƒТÈ<@Do‘Ab>îÅ}k*‹ D» r;÷âj¸¿(4Dèsöu¿ÌóöŸe­˜±÷æë?ËÖÄ\½÷3ßÄ,MÌÑNIóST–—,+YNŠY(¾§È2PÌ¿Ä÷Å̳e˯ûeWbn‰ŸÓÞ/ŸXÝï3Z–=bÞ|ʳÅÉÿ-nv_+ .3?PV\´<Ðãáø€ò‡™}vÌÐàIðÀŒ ÈÝŒ¢—ÑxˆV­!8h%Ì[‚ìs îD´€p‚Q†¢7ƒРø@‚0?P¡¬ 4h7 ) µÊ<×&îGT¢I¬Ü÷ª¹ïÕÆ›GÜ@VOp%ú¹+‘ù¯eh(3q׫7s`û¸7‘y°C@…f3/¡éÌ 4h>7  À›,¸^-ÀhH+mÓ2׫Mj~ @³Z@hдvÞ¸¢OQŠ6‚S1´hhrßk¨ÐÜ6:î{• ÑõÀ $ì½5àæoà¾WÀ B@ƒ pð0}¯,´À $ðBÂÂ}¯ZæÏQ Ch¸Áa> C€Ÿû^­ Ô;ÿá†9Ü÷ªå¾×(СgØ—˜µbÎ&fìÿd¾Š¹*fê¿ûç:u’¥ÿ*GOvþ«Üü¯æ¥˜•‰9É2ò¿›‰¹(fâ½û¡ß“böÝû9)»Wö¬Q¤DÙù¦Hïã|Õ3ç+5ûÌD¬A;Ø™±(PÈV´ƒµEíR¶ ø nMü¯ZzèPìn kZæU!³ìlï…&ˆ°3´ìì&ø©h ÄÆ7z C³˜¹ÿÕ$h/û4ŽcA G™€(¹û54h(;ÛÆöiÀh.?qµMfZټ紕}–šàœŽ²³h>7÷·2w«M(CÖ˜›Êmþó}¦?÷xIÜ=žŽ¯eF»El~ @1[A¨QÔ Ea›@¨Qà E‘¨PìVZ½ÈQøZ4€HÑFj4ƒ „MáÒZåØf*4Š D€ ã24„€Ícçl=ð9ÛÛq¶ eáNl9ËÂ@ƒs wcû’û±Ã@¦³£ù, ´hB¢ÀrÜJ4¦ D€ ê24© „€ÍjA DÓZAhѼ A€ÈÐÈ&j4´Dí24· „€Mnç®n eçP€fï- ŒÀ” -Á $ð û x€!a~ DXXAè6zàRˆø¼kyG¶ D€Áâ„‹¸yÈ耇‡Ž½Ã¾ÄÌeYû±¿³ôß±§û=Ùy¿ÌüßÞÓ%fbbþg³ð÷d`bþ‰Ù÷{sïÞ=žxÙóDá¨Q8:䚇åE®…ädšŠýסÐÌ ´È47±ßEA BÚE(Cš …hcçvy1ÊPŒf¥HP˜à ¨™ª«–‚u)ŠÖü@ÉÎÀ(ТˆÝ@‚B-ŠÙd(h3²s¹(l ; ô(n#›L ÔÈ&ÛÓ¡è£ì,.;dÈ$#ð%Á B@†° òÈäìLP£Iìýo4Š(CA DYÙûmh3ðšÈ¢@‹frÊü@‰Æ’£±Ì Th0ˆÍäh6P áÌ@…¦³5ÏΛO<@Š&4?P²÷ë€9clÚ‘…Ÿ;jù™:=?S'ç¿ûÄÎv(øïS±ó"âûè‰ÙfçûG#ßCÊùïQ…ø9GÂÙá6¯.V?ÛÝ>áH]o|^AÓ ƒSÎUË'ñ¹¿]>X—Ò"—ŠsôÍ+?hùÕž‘ñ9–‹~X1¢÷y}…,Þ¨Ù—Jª”Ì*Ý1x"çâ±ëØqj¿55{ÒKçÄšç“‘?‘_ºåÒpõÃm·FÏ‹‹þ–ÅWMºgÏÅôKæ­ÿ}™w°Üœ/¬GYßKk|PG—44Ÿlœpuâ’¹t甬ãcZ²‚i%Š\ô˳]öˤø ñ¿‚¿¤Ì;(Ì9VaN¶0W$„ë|/snØRÍKW>ZÔø/æ|²÷RÃÖ·_È¥‚G I0qó;{\tÓDÔ+›ó.Îûçuˆ ñ:±²¦Xíë5MòÖ-Ú˜OšÝr9ùõ\º½kQvéôÞ$¦Yã¢×ZÕú~èE#µ–´__z¨²0G߉G{ÜKoÝø¬iÝÃù¤ðÉÏ¿k:5—.8¹Óò±^ÄÊ}hÙLUÏo¦ÙœB+y6ôËœ^)öýj|ÿóUÛû¿jç¥)#j(,É'[ÚÏY[uN.mûQxá„Ù}ÉÚ™/÷«uÑ&Íìýtj|®§8÷U|>✤Äycz¬¿é 8xiçýŸ[Û(H~PLUdçÒuÉTmHÜK.,ìßÌEO ¿ñÄÒëSãs‚ú8}tñôÏñùÍâ|aÞŒà2cýé9ª¾ÛžñÒÛKÇ,ŸôlèÞzC5ãcÔAØÕIZ8„L]p~Iç_œ´ægc£ÛTš2ºÎɇ_«LÞSå¼îv¬×rþ¬÷;<ç¥w?ÿnîçÓƒ$ifÚë.W.=™rùñÓ£‡“ê-JÅÇœ” é³`øž)4¹¹÷Nvˆ0ç¥l¾G¬N±^ÇîN‡z©¢Ä´üìú ©¥9°oõ!ÜßÝS>i^{ù>û¡Ç·:©•;n8¥‚ï@œÃÝzìÄwWGR‰8—3VŸXÿrçåó*ƒdì­«Ë ré€åÎlýÌá¤Q7QªNZ¥×•sGûL¡¢G}¥4³_`F‡rsù’Ö« ˶\­ñÒî1Añ)òíÄS‡û\Ï¥©i½M¾2”lÿ²ö­Ÿ:iAWUp•¬ìy :/›4®m™Ï@œ+(ÞçX½bý¦SŠ|ݽôàØ×CÏtŸ8§ZòÅ F]ù¹Ûf¬¯Ü÷øÈJýójeìÞqŠ”Î¢3Š6ds³¥ßõ Ì\¿«‹ (2ãsÑÄùÀb.+žr'}VC˜¿dǺ.,Z¼a*¥$‰MÐïX˜ /ÇúGÛ8ê±ÖùèB]ËšRå“.ó&õPá9ö&ÞK =°ÁIßо|¹÷Ìx‰÷iÕÕ››ì¾ZÎ;­Æº¼?ùzƒ›{é–qoÌ]ê/ ONh{mÑ Mß³ºƒ®åsdôÓó ËqÒ £N¥_lS¶®0ï­Ìo«w{ÜcK_^5,-¯´€Œavv}€6’Ì›^}(é—w.œrÆÉçwψÏ{¤$³É¯ß–yEÿS¹ùæXÿºO7±ôŽôgIÇÞ[§ÉŒðÎËÓôÛawª½¶kiXû¶îŽßIXæVVô÷ˆu)܇»éâ¼ÎDϯëçL­ÝÿEÉ^:8&’>M®În|-ç=ÔKkCN½:ŠD4cì~'Íí;½«õÖ´øSÑŸúÃÓ-BùšÔrž#/Ö]ë>TåÈg{hL_;û4¡Užû°Ë¦¯98.Eš/ «sÔI~UïÕu«3+ø „û]9îÉúGÿ®³ˆé‡ï¡1­ÚúÓäìfóì54@×|}¾ÇKƒFž¯,mQTÉE?Ú²ãÛŽéežÜœ³/í¿ÿâ²Ä¹fIëŠÕ®”_]’ÒÝô™ä•Þ:qšœ™í¹èù½øébÇÅÛƒÉú@NýEÝ\t\L”>-ž÷‚'ãfº07³!ëºUVŒØ·¢g3V×Xwþ•ÁžÆó·ÓÂàUÍ­ÕEä‰ÍvêOäÑþgGvÌD:ÇÄ$¸?ëÕyp\V…¹—[SŠßvW».Ôóì?*ßlp{Í_÷ñ²­‹ÈƒµëÃ…y|Žp?Ò«¤é€Ám4gé ¯ïhV|å½9(ä·àÁ‘cÝ‹·ôCæÌÙFÇgxÒw©ˆT»Ùö¼pMý|¦§Qƒ¾Ä\rt嵇tIæ*ãÇfÇÿýâœUñþŠ“D¬ë¯?ÙèЉ:Ûèî¿]~ð᳤ °Ç=näÑ7C)ƒäWû6±¬¶Òl­KHêÜøçyÞç}Þñß:Æ5îûçNöžsîÕrý(v¸n*|*¶:õýÏGdXÛá%óÏìáyÇFÄù“ÄŠÇÏ?è´â¾Áy•ÜÿQêשÁø÷›Ø¶p0Vƒff*¼k¿ëã"qDwôÊ¥6xõ¾íOÞÜ ±ó:ù|åïÚ¿Åù„PG„üÆø[ÙüU£#”¢4<ÚæoÑûD¥8R¼¾êa¿”жĕoîû“ÒÙ7¢ÜK®ý;¹<¿ï9ú6ƽ¹äÈPßkA$ðÊ÷÷‹RásèÝYaãȘ‹^ËÍQCàZ™3cŠ=õ'r2îp§"+DßæÛ·6 üî$õã5c¼Pݬ+í›’e=6n¶+ú·ºy}D÷8®Tìì¯ÂøÉŸ”ã9¨ºÒzŸ/l®ë°94X×R£ÍÑLņɓ¾û\ð'Â~½L:› ·žë®Ž#Ï®œËûÕ0 ºíëò?çú %pæ¼\Ì»v‹_]Ÿ¶æ›˜œóöaAÅ_±Å…u(Æ?6ÄO“¢Ñ5\kSáÚtã®”q$å£ÛôSò±P¦G5ïF1þb^ç\¿±òãl)2¡h§§ÈÓóË7{™ uwË7ñ@ÁâÆ wny5^øÝŸÿ1Þ3~ëõh×HàïYÁß›ù÷cü’î}ù­ñ#éùÚÿ*šß eöÔÞ¹ýr9wôŽ!5GÀ¨§…WîÇþÿ.žN¼ÝÄqïoŸ=£Ñvª0?×rBcü9ùO–m_é¹7¹DÏÆeÍð|Ä´y1q¤ç,¿yÚôAðp×±ý{p¼ZòMdÂÌ KrñÉ¿²ÏŸ%òÝ„<æÅZŒ?ÜÑ ç¾##®8Û׬‡Ÿÿr©9‘ãHõ<[v‰ê‹lÊÿ2€Œºšžî·L'þÝŸPq¾Nrì^»È1ò¬`—ªCåf(µØC¹ +Ž\oJW¸ºAÆ•[M\ =›NûrÚÆýÞ!_·äuÁçÒy¯s«eìÕ~™GˆoâÕ¤z=ÌP+_Ó)_1ï6Ê ÷ZØ Ž•KUeÏ ù«tÞ±§£æ_æ_æÛ‡Öæ¾·~î­PïÚa’P™®€š!0Á'¡z>éQâöÉ ­:ƒ0ÿÅùãÆ³ƒS6iÄ|á,¤$ÃÒgÎøEÝ4b}p~3çV ã$!ßÔw|É× ý$pòþwËç˜áE\³ÂsšÈçZ™§žÎ ¶ÙçZ"€èï/Ê*SH#>G~Ÿ…õŽ×ì{ï) Æ­R'(²™l/™=áL­‡‹Ìà'Ûó ¡ÂD.gy}89aøÉiÃô'Ý®,øõ:Äýu Îoåù!ýÜZŒ¿³vÔ§år_²l϶ »5f˜sàû•CLdYÐtluCaÍÀ¥¥fb¿\aLÏ÷£Ü 熈¼cÆáK^cÜ’%>×]ýd;ÁAôc—MfØm(¿M9ÃDFL³ßh?òô]Y¼?¹øÎï'oWˆ÷ƒû^sŸfÎ'´ä1ÆïT'p¨-$âVš!x÷ÆÔ:ž&â]÷S]Ë!P)Å?ìû)âžàׯPÈ "ŒÃÿh›ã™ ºšÙ±ö²e)uŠ7Cêð9j‡&Ò ®ÎÇ®5Aúü­MNø“^ã{^ü^Å=W_x!ö5Á§¹ ðõKþbü“½ú4Ÿ»šÄ´þÕuÒ 3ìœ5Ê.ñ„‰øì]Ý·pcPúÝÛ“þÄò.¯×8÷^¨‹z9Þ Œ« ,ò`|–†mûr6Ö 3ÎVÉ8tÞDŠ]-9`çôÞâçžnY8Õˆï£sc®k0滸ǹnÜ÷Û’¿ÿý™-/o_DÁìÓÌàò¸ñU7L¤IÙo§Ž·ê-f?\àwÜŸ¬Z:)åÔKk]Üö|B¾b΃Iu­~±Õ 3ô÷ëó¨o¤‰|óÉ“Y¡uWh’/r`È>¢ü¥6_ü¢9OÿôžÐb<áý_”Xìßßš!|\õAM&²’\ZUøWgè’/xÆ3B«Ù«Œ‡ø½ù{ç)ŸH9CzŒo:ïp¦SÝ©àR`µÿ¬Ofø¦Ÿ÷6ÍD6¦}odêóëä}Þþdðìâ3v¨=þx¹_xj˜½Ð&ŸÇŒq_Oî•ú-ºê\æ‡.Œ‰Í—ºÃ:[?WRóççæ}øióq_Vÿ×°Q¨°`s"S1î÷‚Y ¼`i¹ŠU}ò§AȬ¹¾šÈ»ÛGŠ´úÚ: è©ò'=¶œ[=p©Çã÷ïÝ·æÅ?ñxex™ï‘Ûog®›aô¤0>'ÍËïúØÉ<Î7üÖ¼µ?¹9ÆAÝÞÞzùž[Þ¿©õ^œHûšãöz£[{”Ô²€Ò Ç¸ˆ «Å“òïž×±ÓƒæÃ“Ü'¶ó'OV§ö½ðJ#öÎ’Ž[ÕÏ+Y7sÀäm0üèÚEšziÐðbÓb?åñd<s¹äHð¹2ϡ˲&­Öø^µ=ĺæ÷ƒç1_‡øŸï^ƒñÕ·Ý=dŠ@) kš¦ÁH™48žL^êT¶|ÝÑÐdÐ6sñ©þähë9sIž‡…E®2çÇH¹ ZŒ;B›\#Üf7¸¯º¿£S«4(>·Â¸usãɱëO×q­T+÷{‘? _W©Ï k]sž2ÇçØ‡Á¸§z/tKWÒ`ÏРÛ/|âI±–]/uÝ3ôNí<=ãOêG<›cßðýGž|!p„÷¼ãNšu­Tò™ð}Á‰öý;¤Áo_.ô''·o¼=?zGÏ+ öuž<¿-ùë—©8’o«nv½Cðaëï;©Ó`eç“ã£nÄJãZ¸f0 ¾±¢¿ÿ œçÚ Ü+¿èþÇ<ݹü»Î©žyÄûËÇ–¼Åø[4ÑO#ÒƒÏáȤ.Ó _ä§šîãÉŠwß_jÑ’ë&5ü04€Tj^íXé‘›Ã×ùøAʹP`Ü÷Ëçœì5ò(tÙ0ŶGlš²ºK'ñÄ{æ³—Ïõ‚˜Êoj]¨@‚ËÞ–·¾‡¸¾ÊÇ|¼.ÝGPcÜÄÕÔØÿŒ0Å–p:,º¿î}äI ør¬fs_®y¶œíO¦,réêQqÝÓör…~Þ³ ‰y!¬·qµ—ÎR+Êý À†&³K Kƒ)m^̸P&4jSèšsä`˜^4ªwø-b½²þÝcìpÞÏ3¡ÿ3ž,Æ9ë@¦ÏÞ“>àâÚiи#4Ö0Ø]Œ2[ç µKÄ+.™üÉÖW¥¦­lí?ò}K^ŸÌTÔ±€°üá@ÑZWý0Þúag·¯Q'˶ɮ‘PuQ¯B/ã¼q[ jþø¼|ß&ÅÆBòãZ¶iM`ácÜE)5d—–&†#f¯[:*…Ýúôö›?x8q~kŸð26ÿvmÿ¢Ûëu@àÔ ëÁ ŒÛúÎæìrÓuPÒ¸¼íŒ;³Úš°;È…9W?øå £NÅ”Ëö'§zf­nÙÀúÞçùÌ×;ù~GŽ¼Æøî‡CÆõ Êã]N<ÇøîkcÎH ”rUÁwL:QýA·"äÁî¥Êì÷?Ö³8gÏ’Ço{o: ˱Ž1ipªÄÅŠ³ô $Hu®ÞÖ¯ LùYÍRáüä†M¿º‹ã+þ¹kÔíðçtAq‚óO;ØY=ô.Ëk¼Î–¢‘æ·΀ëƒ×éò¨pµÑ Ä˸¾öún}aYñ°9ÛÙ“ÖF6_ÏûòWÆŸÏž›(8²ƒ¸ïlÉgú—|³ý‘vÆî©©÷£Ç¦ù»§$¡wƒ=EÞ)¯t «ÚWè¾ÄCüüÁ; ós™Ü®Á뎭!¢ÝãïéÈ“ÒtaÄSìO¹×Q-ùŒñ>S(|:Ø ~ LƒÀ{í¯ý™@ú¾j^ÐC¿ŠÍ~d^@Æ*š&]tôüƒƒ,œ‹±æ¯K~c|¿SFE£½a…«ÓŒ?زà—HìUÝÛäawxu½dä‰MäÞç³/~³¾_øýæEEn*ŸOXòã÷QhÁ–K01ÿ`¹'Þ F´L"¹¥.Ùæ]õ>p'Ïå=&w+œõÑxˆïEÎñ+ö¼Òd—Í@à /=Æ}òù^½†«/C¹+¾”ŽcóÝ.R+QÜÏPýþá1Îÿ£;P€¶æqÁ«É/WøaÃæ%ì¼ƽWâ]É‘K®À—ç‡k<‹3·¼Ð*‘ßøŒ×t“=ÓFÝ(@žopx]üšæùÚ›)ô D^6®m!æ¡%¯Og*zÏÕÖí‚ê<ñ:€ï]øsØ©g"9^æYú`xûÔ©m»2há·N£jç™ ïÙ®P«ñŽ µöµòãí[7Ø~¤í584uíŽhe©ðÛÛut"qžºo׃Jƒ!9òÚ{YDzy£âJàØ?ïƒÐÿ‹€0ìÎÆÿr!Ÿ1þŽqIÅŸ»½:]ØÕ²{LèYöÜf×DÒãœÓÞ¢¥€ëŽzv ×°óYÖ~-Œ« Á‹±[ï–çgÌì?‡ñª0nɺá·ëM$°¾MóÍ?:¥Á°¤Yòt÷DRþiãl¿^`ó«þïÆR9X߃ËW{d©ò͉ïñý.K>cÜÖ>MZ”ÕC'ÏôÛ¾íÓ Û^ïZg×%’ÅešwOXÖN/ùº¿n‹@RyÒÏÉã/iþèwÂý~/®ãçàTaüÕ•³¾Ž[©‡Ì—_÷÷'q~ËÏ×Hyä6þ߾扠ça0?ÚçC9¬÷e¿M$µ×8•h;£̬”8½ÓÛ²àî‘Êãxüqþˆ¯¿óù‹°o/Ä—a|_{®D½‹N/–“®9< L$Âýí_È‚“È…îù›c¬óܺ-(Q„­Ï5ȱ«À¸ã/Ù†ë×áÈÖvyúâüvkâÓR[/'’ϵºMtï Á)~åŽãç>0úÚ§qW­}דð~üáÄù‘Òq¼ã7í·ìÜù °´Ùofx}¹fvêÝD’–òrD³Ò* £­ÍÉÜ_uËÖ4[çÂzk–Ó¦à2c<5e|Jv>㟨úkÿ H ÜuÜ6Ë §÷>sS'&’O› ?/ÜN}|¹~e 18D]©²ðÏyÍÞZ«ÝÜ+¼¹õ9Îëaü: òî±­|ÚÆ®|]ï±ZO‹Z¸33‘Ô{¾d ¾|!Ó‚‡$ƒè²Ýëþ¯¬?ËãdÑŸNü=,å=ê1þ´cv†:›n=ÝRþ‘Š>ŒOýœHdO n<åÚ ’‹ŒkµSH„qÊr"p~‹‰Ül¾¾dÉoŒG©«W¿Ý„†åþy× #K'ØÚ$‘Å{.Z&s‚û]Ž/³=HTéóê™Ü—‹ý›×OF•]÷‹-,ÁÖ³ˆóK~d*¤ÜŽ®2<œ1ÃýFû§yH"{ÏÒm´#È¢š¤~ÃÏ+Û׿êéí+ˆ°>\DœÏó&«a^†qt8Ôýyµlµ³f8·²F…N¶IduLÄÙ3dÐE>9~›>œ­ñû¾ÌÉZ—|>fÉcŒÓÄ2Á¼Úõ«7ô8i†µ÷nP8‰l·=°¡ý[ØàÒþø›»¤¶Q=èù?Æ7™­Öœë|8Ó‰Ïs-ù‹qžêŒC¦[`ÙŽÛg†½sW-ºˆŸÏSû:½Gw[2ñeÅëÍî’wWI§¥ŸŸÖp¡ª6X!ãó}É.>^¥''uy“T3­uÎë±ÈÑê.jøíÄëPúþ1cüO‡’"Y·!£ÉÚ35§™¡K¿“£ÞãóÖ'›Ã÷ÌSŠÆƒH“*¶wòÖÕˆû@Âz›­¸)ݯ± ÌTüa€~V´ž;Ú ßOh—DŽhŽÄžLë , ¾Aäsz`Èú¹g»Ô± ùšOÉûe!ãCÛ™!\;:ªF»$²ûêí©)*8vçÂix_øº?/Êßû¿œQwìY2ke­9ˆU·´òãkËÙ‡\zxlª>»WÔÑ µŽÞ‘ÅôJ"!æøÅ « {±özºoÀåy®Zïí‘#“‡wúé$œ/íÂÎIuòãö· )­Õ0ƒÇº¯òH"¼<¼ÑÐ{lNMüøçýàë\N¾ïX«“8ï³ä7Æ+3ºÂ ×»à|û]—c¥Ì0“Ôøúvrémð÷rÈß¿~0j–£Žä;ïUê{~ë~Ï uT˜ÍÚGXòã÷IÞ1Ú1ô.¬·_h¿íW*ŒŒ¯S`N©™ïAÓ €öæKã»ëˆ°>m}ŸñÏ/üó§8âçÚ…}a=Ø&(Sq#.îÒÛ{0a—_J%S*Ì>Ö\öhaéxt^|—¡Í¡ˆc#÷kuäõ$zÂǺ&ÝO•aœs'/õh=ìîw(¼öl*¤ÖÝzÀ´8‰èfó$ùkÁ·ÐÖƒ7|"ïw•ÿÔóæùçdòuwa^)œ_P`üóSvæ÷;yf­o^yâÖT˜xK³¥Ã²$Âß{ÅÜv;âDZ¤íú¶xumk\Û+¶Ïñý-K^cÜÝŒÕù>rM.h’ÈÆ‡.|[ƒ°Dºyt3ïX?Îù~8åèö[%Äy¥ÐÏ[€°¿*Ô¥¯^yw_êûÐldHƒë}S¡š¾&YáDÚí¯T-ø~'°½;ó{µOA¤¾‚=µŽ«Zov9±þ¢ÏçY¡À÷ù¯ß‡OMR6'6M…Beµ£ oK"O¾XŽÃÛéÈÁ»SûßiëA„ñƱëR%Å}k~ÿ¤ýUלÞîV¹ p!fú™àÒ©@?UôÁ$"Œ{Ó¨¹Iõ«êH“ôöÍ^†kþ8"ü3?Œ,zÆ9µnÎ}qŒÿ¦KÊÜO"`K#uÞ oR ãÍJ¿bIÄö\»í?+a~ç mo¹Ž¼ü]‚uÞÃ×[‹Ý¹s3ÀÓ„ç,¬«Ùè2Ý;eeŒ€½'í6ß¼—–ýýÐ$RV¾¢ïÒ·]¡êÆÝ,±´Y;w²üÀ§Ô¶½ò‹ÏŸ‹•ò²e÷s¬û£Ùàâv¦àÛÓ)pׯù†Ëaø>«ùcZ ”}9øë-:rµjÚYs7÷?Ö:nM:­=SP<7ÏÏsZòãN7äÛÒ-ÎO²rï†0Mý=f’!‰4u$Ï•ÍÁÖ²P¢#u™ºNnb½ß|†sÜw=}Ùüljœ¿Qcü%ÇÖ]ç »/êŸONÐóÞÖ»I„ÿîÃkÝ©±/–éH_KcÓüq>Œï{ðóQÒõF Æ·ÛæZc³9¾–›jè˜ôTõÎ;Idë ã‹Åmê’ƒþö%4:ѱö²·³­}„çËÓÊ•ïp+ÆÎ-ÕËñ\µwãáåÒêFA£KŸöT+›çoÖ©9,<‰dÒðå[’“&߸Rktäɇu'ªMµî×\Ûº§ÑÑ/Œ/^ƒÍJ yŽq…óüQ Ë»ìÂâÇÉ0ÏeËëe$‰T)H‰ßmÙ>ºŽ8§ç™4(ÐÚ·„ñ÷+Kÿ3cœE“* ¼cc½<žM»è.†˜wk´Ë¿);òp릹Ž{uÄr\¡¸‡ø~äãNá÷Vïø8[øï…q±Mp¦B8Ïf„NËïík²>îh»<I"ïž~<ý6¤ö•ðþæ£#Hë¸ÿþ,çïAÚåø} ãs¨ú³©5:ß³Û„dðl5ïµ.0‰¤•é^bFV}âü,àR‰‡:ÜoÇÛŠk­ÏOØ´®« ûçÍÄóÄ–üÆø¤¾ÿ`Õ#¨l [Û&ƒêÌçÆõ±Þ9Ûx®©¶ÂÙóÖ\¯„Öh‹Ù>æià|o>±ä3Æ;Þ=cJæ#àËÉùTÑd8ña×ý)A8Ùíd®«lÇ ÓR:ÒàDÇ:Ïo®ïGˆÇüà©Çì@8_ä—DÎ}7je¤„y•Žx(¦ÄÏ\ñÇ9Xú$ÜÍ뻎ŒýŽLÈ7-ÆwnWý@ƒ† ¸r7áx²‹_7Ö'‘3Mj—>¦;ÔûÒ¾lŒ_^åwb³Þº.ÏóDøÝÊáÆÛðþþ½q_ŒP¹q¡%~3“ ¸Ä–'ùî'‘仿¿©„žñ5§¶ÁxÂù~ ÎÏÝ娗Áx]û~üêP+Ügþüx°U¤^òÝjJ" ߬y±¡mO˜_Ì0æí/yûË-hÄnëü‹Ÿ#§îjÙr¶o.œ;¶ ÉTdŽ|6xŸh(nÙXM„ˆ…º>~’D|¯÷ïݼìkå6vGÑ`B'ƒvÛ¬ëúÂùœŸN憴ð[æXÇ‘aÜ<&Þ{¿ ÏnxXÄGß&‘EÑ?}Hè OGVT9˜ô7v‹¦!ü÷üwcÒuqE_gކˡî®w›\=ló9‰‰l°¼¹]+¸§9³½Q½`Ò¡1]ò³®;ñç¿öȸç£%ÄóÒBÿoº©°Ï¡Æëì¹ï6òŹh°³lœ'ÂÙQ[> ÿšD„ó•aÊ9}µ2õƒÉù‡ÓmÖ~Ïß'üw?ü:ÿ½‹´ohð:Žeº)“#£aI+ÅT¿‰ðr}ýŸõ¿%þùø÷éWäCµ){þìûë¯ 0n‘)žëÆ7µrœëÕâuì Ïl»+=î{W4¨6^çHöþ{ø}„ueœ÷v/xq½c0ÉŸ¾à§g²µðu>_Î÷ëEzŒ{­lµ–—ßàçpylìhëô;ý~þºU–¬_ó¥ ´DOê“ÒúˆôðëÖû$|¾üâ8GxµÎÁ‡7cüóŸè‹=z_-ž›®‡µý…ã6^ÜÚ!îKš,˜8zÞêsxŒu~Àߘçx±*ƘV œcž¯Í™LEШÙ8‰†_òžé¼%TG¿·-LÆ9•<ÚI¯€GƒË¯]6˜´Ùä¼¾ë}gëì¼B+îûÊ0—b÷׋V»*[àœNïÇ_éT2™8 ³Éî³·;ø$ܼó³D0±ls]q×5øç~—R\ì×BŸrÌñ¾UàuBö­Œ©Ð9„廨r¥ÈÊìÊÉäŇÝGÙô‚уû?«Lî†ÍÈ[ìžµOñûßá”j[TšxžJÚÕèÇ5“ŒŠ®3t®[ŸÅCOÕ°˜§Éİøwõz}”0Ò2a &-Mþá.®?ð}O~_¤ýKƒq³+¿M[ôTRtP<¼©›}üE‹d2á‹Û²G>]àDT‰Ð@ß`q_•xýòõKž7üý#ýݧ¯#œ3Žzuˆ&ú÷qØÔ·C2©¾ntëÚv0½X»óÁäKÇÌ aΚ\¿ÛãûãÙùÏŽ9æ%zŒ?°=q}ûz¶9: :Oñ®hì”LìŸ<óûx#è×ùEÑσÉý"Ý.d—ð}ga¸8Ì_ØŽùƒ™?¨”‘ÈYÕ æŸÇ½†¥l3óåþQÜ#ÔŽq¹‡÷æzYŒÿêËø¯*æjÏ<¤¸W¹còH}BåfµÔ—ØYâ#å¼Bírqyܘ_¨ãòp^¢+ó-–1>™‰q"|™—”R­V1NY6ó’òcž¡Ô³œ{†Rf"÷,×äb&Rvµ’yïÑ¢æìjÊL¤ìj=J&ñlq`¬©g ÷à e>|®ŒiæÈx²f檑ð«¹o¹šù–Û1_>#óåã¬óåËb~RÔ7”ògVƼùŒ¾,æÑçÇü¤TŒa'ñ“²g¾¡¡ßòPÆçñÎå'E‘³Ä»Å1©ËßÞü·7klþsz³-»F]ëËüL홯Ÿ‰ùúI9iœ_«dþWÜÛÔ ñ6Í`Þ~œÇͽýì+{`qSî•͘ZÆ„tfþ~2ÆææÎÆ‘úû)$ÛÜž§~ŒéÊ<þìs±$4ÌãÏž±$83ÍMâªaìH%cød1†gÙ:3†-TgæçL}þ¨Ÿ3÷ù£Ü4îçì‹›Fy¶*æ£e[Ûʳµg<[Ê‹Ü eb~ZÔ5¥Ä¢÷“øié™§–ãøPŸToÆúV0vgÚrOgæélÏ<¶LÌcˇyÜ+™ÇçPê‹RÍ8>ÿÆkKÇáÎÌß^ —1¯?½ÄÓYϘ>ŒSɹá¶ÌÓ™r|˜÷VãIÐú¤¤½™ödÚ‡iÿýW}÷Ÿzî¿ëµÿÔciýïöVÚWÿ©§rŽ„´oÒ~É{$gHHûâ¿ê‰ÿ®þW½0w¤=PÚûr÷½ÜœZÞïþ©×åîs¹{œ´¿ñÞ–»¯ñ^&ícvìïчOùµÊ¼³‚rjMÆ e9rÆ ö%ÊAâÏÇ}å9Û†ûÊS†£ö!oTcjû¡Txa[L*”%cÌ Î©•òlLe^£Ô£z.ë˜ç²³„—M9ÙÞŒmÆdôÅdÌ®.xðùÉOåPÆs•0ÀL.-çQ6mv«?¨šqh¤þ r‰¼cЄ6x_vŒ÷%ÇÞà˘×JæꘊnÌëÝßÌ8×>Œ3ãüï”/ãÀø2”£(e[g¡”ŒåE=•õŒiíÃ|ÜuÌOÙíïxìïxÌæ?k¹ø^´˜Q:”„c+c[#Ê‹\ƒ2£äÌß4 ¥Â¢×¡ì°ð]Q”ó¬73ŸSÆW2ÆgÙrïeWæ½L=ì½Qf”‚yžf1ÏÓÐà*º0{Gl$Þ(3J EËšŠšyŸÚ0®¢ž1‰8/œrÝcƒ{/c×5 )7œz/QŽØ¼ß‹ò5hÓ?ÒÞLû1íù{ð¿ê¿´8Ï‹ö[|T9zí?õWÞSÿUÿ¤½“÷ÍܽòŸúdnž×ÿio¤}‘÷DÞ¥c1ÎýÊÍÀàc/Þë46Ö>'íoÒÞ&ík¼§ýS/£}Lk#ô-iŸú¯z”Ì&'ßÕ/¯À»à,Wó{ç< ÏÇ{‘2}ã‚ú»s~÷vwÄ„ð¡<0ÆÇÖQ>6&‡k©œ\Ê4£˜(Y”ÈýŽ»‚óW)P-á]»1Öµ‰²WãÚË•R`_в—º¥c G=õn˜†ù±û0~g¬êê Œ ó\÷ežë:ÆÕÑS¶cU¸1ocEáG:v~åç8bM{£2(«š rû¨S^Žc+ƒš²(¨g±²§±FÍ(9ãMKýÑeÇOÇO6ÿYã'ö}Ì6 •rÈ2ò j#Zž‹EÆy¨Î¨P”&¾ósW`h«GÉ8Ô”q¨FPŒG¦G9`qhPf”‹DÇ<ÝÕŒEmËXÔÆî¡,jÊ ȇñ*”µJÂEU¡t(;Ê?d™%gÜ )ŸŒòQÕ¨P”½„êÀø¨&”‹Ü•R`±û¢²QÎø CQö”ň2¢™G|JɸŒ”K­bŒ2ÎH EÙK¸ÔÔ3Þ•Rb³Ð2Ïxglú\ÌFÊrE™Prl$>¨ ”ŠÊ›Š JϸÔ.ÿxÎ¥¦| cWP.µ†1ʤþî¹¹Ô&”’ã“Qnß;ûÛ›ÿwzóß¾üÿ]_v´ÉÉ—50>¤ŠŽQ¶ù¬| ÛüVÖ¬&»&?ÈVÂ’a¸¢L(G,TãlhQY¹Bn(3cUkPF”‹Å•]B`U‡¢²QÎvÞŽ=QF”ãªQ޹8B”™R2^u6JE×÷P¶XlΨP”-]ëcLʬua,!ÆâÌjÊŒ42f5eFšQŽŒW-Çõ¡\” Õe+ÿ EÙbѺ  Œ»æÆ¸k”wëÊ@)j üµl rpQ¡(; KH†…î2£”Œ%D‹Þ™1#í³Ú€²§c[Æï°§œ\”¥Ä¦ Ee7˜‘”%dÏXBœÉYBrl>¨,” ‡–±„ÔŒiMÄ eBÉKˆ2#åŒI™ÕJl.Z ³ZKÇÆ(•„íá‚2¢±ñx£2P Ƭ¶¥\ ”Eé š¿ãå¿ãe›ÿ¬¾,gŸ7ÃFàöR¦[VýíØ–Š\\7ÎîU£ô({L| ÊŒRbø±"²-]PF”#c»PŽXÞ¨ ” ‹$e‡…â‚ eœKÊ72e]šQr, _TJUÊÊ w–0|Q¡({,.·²7œ2’¼QF”#›7ÊLùáXt¾¨,”#Ÿ„ñæƒ2£^’‹2”ñ’\$,_W 7‰rÇ (,X ÊŒRÈÎ[J¬ÍÅy£<_”%“ð|Ï׌R`‘û ²PJ,v-+x5J’aákP&”€* ¥bœLÚ œë3}õ(YgN™™ŽØ$|QY(6 ?Ö0Ô(C.f&e,¹¡Ì(6_Ê<§l_l(:”6W”ñÏ]QFÊÐd¼%c.y£Lm&º7ã!)Ú <%Ê]r‘ðÜ$<$_Ö”(ÿˆö*ú‡÷fÞyÿå댴çò}_é~¯´§Ò^Zß&gï¤=“öJÞ6B¤}ö;ÚçhoãcMÚÇÔ6Ö¾4‡õ›eÿÐKV±^Á×í¶Kê>÷ú\«az}ΠuÆ/š×ÊõƺT2Ö¢ åFÏb`ýeSѳxS4¨ ºîÆoåÇ¢BéÙŠR;6 ëFEϲáÍRWb¶ŒiMù†rƯVUx`ÊÚÌs1ƒæ#e²¢dx½QŽ =G@ÏwµØ€¾Œól‹7Öe¢{ÿv³Áéï{÷ï{÷?뽫dŸ'ËF`©†¢²ói_T6J‰Iî‹ÊF)%¨ ÆšÖ¢²Qr,>_TJ…Eè‹Ê@)±µ¬ ]Pz”¦«„­ê†Ò£dÕ¬jG,Xïê‡Z)Ãx2E­ÄöCeÓ5},äPÆWuEP¾ªœñU3PJ,r_T6J…ÅłwAPXøÞ(3J À•MçGŒgHùÔj”Ÿ„±j°ªÍ(96 -*åŒÍB‡²Å†á‚2æbʱhP(%6-*›²V±¡„¢ì±©¸¡ŒŒ[í†2¡äØd¼Qf”‚q¬ÍŒcíƒ2£”Œcm/áR–µ¦£À¤eI¯CÙa⻢ ¶´ %§{—ŒÕªÀ¾§ çÃPÙôÌ)‡ ã¡ì°@\Qt¿’qZåtÏ•E0u¬œV;,5Ý»DÙc¹¢Œ(G,&TJ…E凲ÅÂR3†´Œîg¢Œ(ÆkõAe¡TXt~¬ð\Pz” ÐMÂŽöE)èÞ&+HʋҥGɰ—iPf” Õ¥ÀbÕ²‚U¡üP¶ØÃüØàÉ凲ÃBvC™Qrìa¾¨,”’±¢í°À]PFýõ³÷ßõ¿ãL›ÿ¬q¦3»^6½¯˜Ôz–Ø”g­e ®BiY¢«P:”=&¼+ʈrÀÄ÷Ae¡œ±BQvXj”%Ãbp£ggQ , _” ¥ÀâðEe£ÔX$” Å e@ÉëÚŒRbáhQY(%+"5J_Jà_» ô(• Ê€rÀâòf l9™/ÊŒR`±ù¢²Ê l?Vx ”•M×à±µ¨,” ‹Ñe‹éŠ2 ì±0ÝPF”¨e@9T³r±åX°>Õ>¶J†ñd#[…¬cEì‚Ò£dXÌn(#Ê‹Zƒ2£Xܾ¨,” ‹\Ë Ý¥CÙaÁ»¢Œ(G,|TJ‰ @Ëš€¥CÙ5ØÙ:”-6”QÂÏÎ@)°Iø±F¡F…¢ì°a¸¢L(6 Ê„R`ñFe¡TØHüX3Q£ô(6 ÊÄXÚ”¥`œë ”’1µ3WÛ•R1®¶ ÊĸڔwŸ֔wMu\A¨›ÿ1æÿÆøòŸúîgL™»ÇþÓØòÿf/ó¿[J{èß±¥Ð'é½rÀÞèM?€ f@Ùc’¹  ({ì‰n(3JŽ=Ñ•EÏŒ`†¢d˜ˆú»TLÆ ”Òe‹I©FÙÒˆÒ£0A}P LRTJ‰Éꇲ£çÝ0a]Q”&®7&oýí)&p=Âj”-ö85*e‰íŠ2 \0Áõ(&y(Ê݈’c²û Ì(&½/* ¥ÄÞ–…RbhY!8£BQö2„ʋ•R`/óEeÓß-a¡Øb¡¨QöX,.(J†Eã†2¡äX<¾¨l”3=‡²Ãæ‚2 °‡¹¡L(G,0G,0_T6Ê M‡²ÅbsEPXt” %ÇÞ¥E)±oiQ¶tmeéŠ2 °gy£2PJ,R-J‰…êG×éù9”e‡½J‡²¥géP:”==O‡Ê@)°WiQÙ(v(Ê‹ÛeBQ7b°þ†ÔÄ~?êÀú"=«§f¿yrcëÝÒó!®’õnoÉz·ŸdÞ­a>Nû$]ÿ6@Îy¹„¿§d=ÔMp¼°ü9ÊþÝ|&Sqêü^óÞã1À}>øx}rúu\T¯k2Éw±zï%óò1?Ôr¢_‡àŸL¸?Þ€o ‹»Î=²\èl¦âš÷ù ™10&¾€×ô7&8y¨¸¾ ÆüK’è7Õ‹F“ƒ†mG§×uýžJIÑo]ê÷*øÛ)6ö| ,©ä¨üvֵǥö«Ù9™t:>=iÀÀºdmpÒõJ0éqçô¼b†ø§þ˜ÙßÇô¦ü—Yñǵl˜¸æJ ì,¸bð&pk¼Ì4Ù)™lºU¡Djcr¹^É¢çŽê¢åÚÁú¹+Õ2|Yjo'úwp΋à³"ÄWc|ê6QJ÷åêº %”&Èèf|Õ.™Líäå7ôR]bûty•ˆ-ÁĶæú–ïJhˆàƒTJä þ˜ÌGãm˜b4BX L˜Õ¤‚ j^ì^®dÛdæoZ’¸Ü»XvØŽ`Òù~!‰}5ð&_R;ÑŸFðÝ)&øi`ü—);ª•©ñ,JØ6cÆüÏ+@&L_ñãX0°ºš\þ½_™?°ÈÑžGqÁOãg¸ éÝîn D~Û>!$4^½8Ô­C2±/¿Õst¾f`ŒÕtŠ9L,XËî"ÿ02xnµü±%ó6¥¾„fŒ+øýÆ€[…cÍq0ºÿüÑÙ’ÉÑùFÖqîOÝß/zëLÒŸ¬XsXáþÇHð_³c~Y²Ü›s™ ç-Ñyzšb LÑeÝ.Œ‰ƒ¶…{-,Õ;™èŒì7¨ XO»VL†hË{­™jõüríD^”Ó'øÃ+\ˆ¶I‰®GÚ{ï¯/½5X2i=iôÖ‹½à²{£®kôÁ¤´i9é„5.÷“áù,åš)0îÆ½•\œžÇÀõc"[|~k+6\:jR2ùð Þ}P‰>ð¼mÍÔc‚ÉðÛÖÝJp'÷ÍãgÑ÷‡ó,yŒñtÛTÞÿ!.7P­»ñ\.Ö|~fn2éœ~c‹¿‹ò6ÖûDÈeEOÒ`DÙè"SJ‹}DðS+ ‚o à£¨Á¸©Ù -zç…oµšØl|ƒçö{uhy2Ñ6“¿=Ú 3G‰ Á$[‡qÇ_»“³žY!®GJ‹u!õ‘Ób< ž¨L,th>tïïa@»@±6«’IB~ûÚž=Õ}ß”L¦S[šw"øç•KP>¨À9Ôc<› ùàÔ8R︩mÒº¬M&(²\]¸“¡©ý*1˜dyyîþ‡™à¿_Z¼ŸÂýükÌßÂ) ¥µÇw|z•OÄUy´?ï¯vIgÙÂìwYòÝ &‚ÿÕŸÈmßϤ†ýìD.£4¯lÎg*æ¶HÒD•Œ…W}}¨tù!ô”_~u}2y =ëö¹Z†qƒÉ¾é¾‡ÞIüD«b¢oïk?…å-Æ/Ÿò´æ5¼Ï‚oõCèÖÝڛɤ؅滊˜Á Z[|ºL6÷mLM._Ôâ"o$7ãz}ÛÙvtùXˆž¿°¹¼×C8?2ÉÇgk2Yüýê˜Óýä»®‚ý¾ô·ÝK’kZïG¥©Õ®ý(…÷ã~c\J“.^!Ò ”:9µÂC(ÓîõÛ“L„ÏÑõ`òvHÃ2+NZû<ï›ÊÛŽž5 €3Ý, 3!1®ÚΉ…º³5»ßš@Úϼ닟H&}ÛiZ<ÖnÖe¨LÖ=Ý9´Kº•»Êÿ)p¢m…üÅxC\d;#0ßßêöõ:ü^$—¸ñó|2°âK‹qZìjß"Ì50˜ µ€Søûî§ø9¿ÜRbÝ }¬Qßz=^Gèß± )ëônÂHy5­o?C2™=?ué»PØ~tJ¼ÎÆ:þƒËX9´ü9 ÷×ê_%øZ÷`>Ȃﯯ#øýÆ‚Ìd}IeÜg7‰Of¾XýaGè¦9ááÁ¤«4³â¿NmK7–ùäÔ)4o‘O›åðo³¹©89Í£cÝXÆ…:Ù«¯?O&î½u½¯¤û{ïÉøLö7ý¬9÷›ªÈÆaBÿS`ü³’#ásX3ës¡»3caá”­?}¿ã¸É­Iç)Þm>„ï5+‘û‹s¾½”ϪƸêŒRGžc¥þ|7æ\óXHK[³]g“ÂxMàõ……‡k×!^Çë í^ˤ؟¸•µ¼ è//åíi0Þ—ß]}ìbáX|‡^_càiì•[ò¥þ¾j—oô÷¼!¤Ù™¨ Û§Xy–Ü¿žsߤ¼-ÆM¼Öãç§"±wÇ5yœu^ …´tkbùîÕ¡Æp}þYÉÁDÈ#ëóã~„ÜLꧬǸû».Èß+1°¬ÿÞz ¥ÉÛˆlSh3pqûëÁäÙ°yõ¦DàT¼wâü é{ÀŒñNí‘e:ÙÄÂù•ËŒg†ÇÀ¶V›]NM!ï¨%ó· &QSW+¶²>§™ý[U_ñEô”rBmB3CǼ¼Ÿ‹>ï(ô¢~ 8× ¯]:…œ¬z¶øÖDz³W¾Â~7ïÓ„m?¼Ý‰àZ€q[Š<K¾b¼Ö£ž|oåÝzÌý úXâÞ¯J ù`ÞöaZž0uèù’‹ñ}Òçû³_™s¬¾tÜÏ—s"¥\ÆM4º‡ãŸz&¨«Þކ¦ Ç,ÜÝ0…nÛëâÐGÀnk>æGøžª5QÕÄêC'äS~‘šÙ‰’¯ÿ{5ƽßmY½ÉW1î‡7nÛ¢Á=¡O$¤î—¾Ë÷n4ŸÿJ[:.˜´?ÿÉÿxÛ%ÁþÕê™ë›÷†‡‹ÿH-ÕMÈ[Œ/ðwb ÿÝã£ÒFGæJ¡~—‡¥’ÆšE±áàÜ~ݼ8/9Q¨À1—§Öüâ<Àí«‚‚œìÜÇÍ’·7ÆFõ÷;c@™Òvû¡Ñ0s²S³sRH:å;Û¯!ޝÌ3a—ß ²öòÃÈõ³²E?;î?¼Ø-Êã©­pŸõ÷ó®ämkVÅ@Ò•9}¿aú¦«ë7¤ ož,qq8D5íe›üß|Ñ :u|Ã; ã¢æ"7Ó’¿·É¯óõ”3b @jËå õF°Ü·£)dÈе. – Ü ªR(„TlÓʸ,r9Yÿ üBØžü¢ïk޾{1S±ŒâcûÆÀt÷)K?®6¶ji%÷„¦in·›8†uI¯Ö»z©Ÿñyë-—åpã¸_'p¿d)¿E†×üµc€ºŠ§ö1‹„¹kWRÈƒŠ‘/Î÷†›cé 8„<™Ô·QV¹åV¿]ËüŒñ¼1Ž×Ñþ§3JÆÀ»½×{(g„zí<Ö{B–n!Ó*·êËǼ{P«x¹*÷pSú²?æMŸD¾[XßÊÍàã )§V×ÉWx]õ o¢áyZþã±—¢`æñçž“ñ>wÝåQvug(´{â“F¯‚ÉÒþOÏžx²L윋{/ŶÓÏŸ"ï‚÷K)ÇYƒ×©—ÝçsT4ä¯0­g(è>=_•ç)dÿ¡2ûõQ]à±_RׯÁ¤ùˆCÊt].¾¿û¦í›1ÆñƒÈ•Îs´·à„®‡!Ñ0­ùã™Çâ"a㈂^½K!Eº}fvêwæëøà|0Yâ7󆃭u<“ïÐé ½»}uâ~ÝRþ¢ã¶§˜±Ѱ¹m”i׌H¨t&±ªíï¢PØ–®3½´pUô†ó§k_Ú¸9­}&wÙ¹¹çËüð{ ½A9yÃfŒ{a<%E³÷O$4lëÔÅ.• ÜyëA^Ù ¸>YS4$˜ T6?½(t9yÛÆ=Tuàã½5ÌÉñ¾”©XÞ©(}¢Á‚gÞÝÓn2ÔN%'w9`û¸ÉÁÄ‚?yfåš }4/ð>Ïç©–<ƸI§àÐ5ù·¸Ö´I|¼Ú°^÷ö©ä+ÅQ¶µÇ>zÇ[Å uÑ^&öå«n,*Tµ°èÇ-õ‡W`Ü/Îä l ù _ ñ> .ð«wÀÀTR|ûÝ»O? mv³g·×»w…ã/ýƒgÑ5àæ®'‰ŸEŽŸ§rî»%¯ñ:Öhs£x4é8F¹fÍ}PöU§~pI%Ó£O|kWnTJ­°ëÔÙ`Ò$ï„åù¶-ëgø3Ûùu_;Iç=Œg;¿îÌSoŒ°ë$5½8I¯¹,•t}ü$^Qn ëSØ?7ÏZÙ­Û²?ø*œ“ÅûÇ“ïbúŽ—åô+Çëì(A·œŸÛû\M¹Ï(joL%˜ÌBÆöýAyÿà÷¥®ÂïYD‡ülÞÓ ž5äóhÁïXq7\œ^ø«Ö™Ug­Y}’[±;•Œ,å³nħ®àðñÛ¶ì¤`²×5èþÈËÿðYÞ[¿™lã\\3ÆÿV'=àÃl#Tœ“Çe{“{Üãýñ‡S‰ðÿïo‚ª} À÷mvúúÏn5–³ù{^qþÃ}¥-y}çïϧï¼×Ý 'o N¸ ú]U;Æû¥J£^ÓbZm›}þb0éÙk‰ýˆýÖ¾ÄóPð9.Èøf9xU2ŒOé1®5æó0àÀ¶»p¯ê«|î©$~MÁyê´†àþÔp<˜|xÜ´qæ6kåüSÞïø8_xß ãÆßùš‚ ¢ €çÜ…»çB6}<“J4vw|&¼l —©]µk0IØèô,_Þåì½/ÞoÎU|§Cœ87RÊÃSãuJ¥ßo$ Ž%–¾h.v:,,øÃx9•L¾ÓñÐÆÎ ð5‚I9y|Á7 +¯‰ÏûõßbÿÖ }½Ç瑜=!4xíOe©ŠC£àFÏ=püÜèåíçVúv*©´ì4™½±'´]³6ÿ `‘3Å¿÷;æ¾î|~gÉwŒ{-¬BÝA¢àõ•›ß]ÉC¦’%y¯¨ï­Vñ¸³±æô`òUI´õ>ñçQ£„¡ìø'ßEé\{ŒïEÆÞßé ÓK.=¤ym€…“® lŸ‘JÕökøbî`8;üÓÅ„uÁdë=w%Yn½?«- ®…2·÷hc€Âo>—45“–iïϼ::ŠÕÛäx0ù¼,bn|Æ2‘ ò;ÞO¨WáZañûp¾†°Þ,øfËð:ãÖÍúãzTšVvëýÌÛàž2øèu{3i÷#á cÄp0Ö{_ûÅž`BGC•ôÖþ,äK‘%]wP`Ü•¡s?GºD°õØÛÐ&Æö•¬Ž™ÜÙïÝÒœ> Þyz?8»>˜¨»5ž_"ÖÛ®wÇŽ—!ôW¨˜ï'äÊéjŽ%#àTý;íªŒ¾ ~UÞ¬p4“×ÝŽê: ¦‰ýFÖòÄû`—.ùƒÎùPœ_ŸÃÇãëû°cÈÅûRúƒÿÑr·¡jª×²íÌdÞÛ”VBõ²ÛïXLº½ß¡èõj‰øú>¼ õ+_*ü,[ÓzäƒÎf"¬?«àåfÇbÎsƒ‰ûÔŸ”XJ&·ìÛB~¬8ž—ð}p9æóŒñjûN›Ü¸À}XêR½aöÌ[—·—™¬Ž.Twk—þÐëmå±K'cñëW(ä€õ~ >ØVîçNIÇ?fŒÿøÛ¢åÏüïÁžB:CPõ[°£ÀèbMšÉªâ§\ ]PAÇâöµ—&N#ö5»_xÉ|kžgœ‹œƒKq5SñtŸ©Ëwç{pTUþÚµGá°¸·×«‚#ÍÄØ÷sƒÂ’uUjÎê‡ï# ¯j1êü¥%dø÷o†ìî2°è=x’}(Æ{w8ÈþN)=ÉLî¾\T!eï8ùù…Û}E09^¤{y—Î¥´ä#þýÛ^~÷¿ sÚ·lÔ}t8ln’øá«™Ü\û5_ӮΰzcÈŠÍ‚É Ž»Ý?ö]"΄}‚|âø†×«%?1n×c¥Z|ïp¼Tt™§žÎÛ¶ÜLò«ßš~hœ­v­OÙÁ¤r£}G¿NX"ŽGÇ6‹oRëtñ¹ÜçÆ"Ç’ŸéçQÇë†Ý‡ï×zÆj]~‰ý˜?K~bÜI‡ÕTÝçâ×.¾ ÙgǸùšÉÞÄH[MûPgþ®›ÃK¡þÿ±Âùüó >û9ù‚z¼N¿·é?CàȈ®e·µ¹ •»?k½ú˜™$TyÞ:mÌph>dR*~~¯/C.ª¾.úÃÿ|äïìQ†´âS›'6Ì9oÄøÃ-@tXºÝM軦_Ôå`3©uêxÂè¡Ã`ªôL¼÷×Ç™î"ñ{pî§%?¯e*^•+ŸTÒÍk’е1¸Š‘¹]1“#KnU¬6tÔ~÷~~ Îó'-r¿²(×<ÿ«çYòãÍϼ;eq5tTößÒåæ ºÛ Tßp3±­dŠ*äÚzCÚ«G̓IuÎ7u7ÜÄzÞ›6àóèꥨ¶lºE®‰ã7Ÿ[þóÙ·Ù<àÆ9º¯ºo&“&Ìž7ÿx/8º.}È£ÖÁ$ ¼iBÍc;‘Çëhÿp¸‹£¥âǮû©^qoÌdަÿø[wûAHã|8tÖ÷•ãô[O»‰ë0¯òë7sÈ,y‹q]8[©ô—›ú"jÅQïëÐúpøÕ39X<(áå¶p¨t\³ÀP©9È)ÿ#Özr¿ðÔ0{¡ ãN·wŸ>·Ãµ«Ó»n‚w;ý:º¯ªëž'¼÷¡`P‡Ÿ|t]GNûöÂÊXH„÷S>‘ÇÆ9ð–<ÅxõC©¦5½ Š”0q¾¯K8<§xi»³J•-Û‡ƒ…&¥# m¾ Ûü}¡ø9ù> þœ{)¬§²¾‹ñiUT½‹×®Óú:Xö!+¥‘ K®—.é0Ž?ú¾qa’ŽÝjÓ²×"ñ}Q¥˜±ÔàŒ/Nœ¿-¿k1nƒMù‚v¶¿|Xéë0oTÜâIi¤E»¥×gFÀùýç»ç¬#«ÛLϺÖ`ѼÑB±ë[]ØÿÎR_zŒw·|ÏËÑ·¯ClyºàWúl²¯UÉþX³v•›Î ðquäJÐÝUwÉB1¤T3ƹ޼õü3ptiyÉË[3Û‡Niäv—•µÃƒ¡Øà[ïgmõüÅóBþ1_8W?D^«%Oõ8®P‘¢#Ï„¯>àòâSa¶dkE¯4åR°O{äu ä_é«ÚRÛvi·øÕõik¾9IÇi2Œ3¢!}c„Û¹¤Ý…W‡AJ¥åsú H#)k-(S£Ä/ê–EvXó‡ß7þ\øú£%1ÞÉ”J¦az(4jØ}ÛÙaðaáÊXÏaiDàÓ÷†m??´TûèÈ‚Ûã÷n¹PìKœ ÊÇQ1ßoÅ.¬ª1nAZ¥ =Ì{w¡c§ña¡ÐŸ›5&ëÖýalt@ÜG­ŽôÎ3õw>ÿ¹xÎ_ÙzGAqSà‘žB~âuœ~~ôËFÏ”½í1, –ìU.?%ØíÀ¥ø`ˆ_kŽ4ëv±åŠ´bÿã<žÿ §N%W€3X¼»§lÈ;õúäkplÊ’µÁ}ÂàùÓ¢çòÌK#¾ô}<VW§À^8] sñ‹€À™ëCc?u[B_ŸÂ>Ú5ˆ"£?¿ìú.yŸ&hò—¬ÚçÁ( òË;u$oüËäêoˆã%ž·Bÿ. ±É Z?9Ø–qo„þeÆø·h[κ )yèFM<(Ws§×æ4rØödbü°1°£Q½CWéˆe› äq}LØÇ+Ý- GÐV¼Yháa~o–©HzTþÀ®uWaxbÙÍë§…AÔáE ÌÒȼ"»¾UÛ3®ž¤ÀT©tdÁàVæ‹ë4â~дªEõŸ ‰ûÒõƹ”ƒ¯Â\ûûckÌ ƒ³Š—J H#™þ—«½3ŽcÝ5ýÛmÑËvSÄ<ñyòû^Š|tJIÆmG[8¾©+ô3Æ\‰î|]ó$:Ò ƒ:žõG^N# ›îõ\¶Ý¨·_GÊ:,Thé<‘«•ó¸ï§4Rhܬ/sö ]FŒŸ¤#¾TkÒöÍ|ñ9òõ‡îŠÝ/ãm¡Aëg nÖh(òÓ,yŽñ£ÒlÇy˜:;kÕÁaPÒäx`ß4bî°~{•Éý`CÃ&›îöÕºëxyÑ|‘ÓÅë´TÜÒö±;¾:q^¼tQñËͳ›öÔ9˜³ùXÉѪ0ÈôøØÞüédÀˆYÕý €6‡¬žÓFG”'WÖûømžØ·x¾ã˜,§Üë39¸lxëa)7}ö…#…œ 6Ä>ö¡üðeƒ‹§Ϥ̔¦EÂĨ÷ÔÖ‘Ò¦}^S:ÏÇÂ~ìG'^ÿ|_É’çwmí%ÃçžýoOÜêsG-Ó¿/ŸÎÆËƒa͆øÉu¤y;Yáï çŠ÷‡÷G¹Ï¿­ò²}ªæ9øŠZŒ¹›Ú¶:Ü=RyÜ€0º¹Û“5ÓIý¤´IlœÙ< ßóÎ}u8ç.,çñ¼ø¨Mspßõx•%.­ì2;ªÛeôIÀç\j~ɉ¾MÒI=1p\ëëþ¹V7ÙPfìk‡È9b‰|<ËçÏ Âsn"žÇ±ä?ÆÏW{¢Ôz˜£eú‡ÁÍÃ&}±é$;ÃE—?`$”ø¨J\=@G,ÛŸÇç>¯âÏ›¯‹XòþF¦"h×q[×%AÙ! œàyÏtòæV½ÂM¾€rí+­ù6ZG„õkÿâýàäð¡ªB.yľÈÏkYòãßk°úfÇ@hšÝ²^Óæa0Òeÿ‰¬¡é¤Ð<º#ë ÓMœ;m ¾O+?|¡x4O¼ï<ïyŸÞO²ûÿ Œ?Á²‰ëÖ- ùîÏl5>ïiÌ›1úÍÓtäó¹&­:ü?ÎåóŸNü¼„%Ï1nuº|±à4ô¼ÒaÃ^a4ìAùÓÓIÅ1GOG9ª`²]uÓ»±:b>8°ÎÝ”¹$7×§t¿Fƒq}Ž]sèáI8»0qÕ³vaàLõÜt²±Ïš‹n+ÒÐ\AGÖÏxþý仹â8€ÞMW#¢wOûáÄyhÂsòC‹ñYÀØ~0f²Éáe›0ØG ¯²uK'w4~Ý´; ÇíÊëˆgsÅq1ï“9ïOCž'Öcüžq}®Å?;EÏ—ê¿°Eä§@½å餟÷ì ª1½`ËÙÊËïÒ‘û;ý×¹n›#òs9]:¾5chŽøžæ‰Vž£%1ÞÉRô`Ûa(>kzÑ(ìOèòÂîtò pÐç†ÂˆG?'Ôh‡u}ðÖկŭý•ç³p¾ç¯gi(0~¥ìÅ¿w<Âza,ßwséÔcédëãMßœ)5ÂmÖÖÕzêkŒçàãÎ!¶ä3Æ?Ðíí`}ǃpgXÞúÁÓàÌ-uÉ6Áé¤úÎ §ü/ ƒ9ÃWEØTБ™O.Õzn®øù9/¯ËZŽM¤óQ ÆÞmï㤺ûà…úçÁ8ޏ´NVù•tR¾r¿ƒÓ‡À<ËÄQG,˜ÂÔ9â92ÞGæz”{÷¼Ž8o–rþ´ÿýÛMªØî†ôªæžEwáø­S™+Ën¥ã|iKáìVáÊsýÜ•Åt¤±åÀöqüÃßË[D7kv¢€È“æë™–|¾Éçi;À—b×ua0ùûKרÈt¢ ›âSïM_ŽWêÈm|ªeŠ[ëE8ùÉŸ‹—î/˜1n’öóÈö¶Â°³m#g^Ç÷äœÒo=L'Nj9…•TÂàÍ-CëHÛ/G½›#ÆâYÏ;IÇ›6á™ †T·‘#À÷dBvhbB:ñùXbÔ:U'H®3ghWG¼ÝktóMs_÷áߟïCZòã}j<ïR£º«!aøÇ~’ÂàÅg¦¤“õ‘?[•l §ƒ‹<¯#P}ÒÍÚ9býÝ×}®òóAqxÓ‘‚Î;ßO³ä3Æ­õe>N!—BFùK¥=2àt`ës5Íé$°ãÏà§Áê ;yiQÏ+q pß¹b>ó:äçs·.8Ö·O7¶nßEÈgŒoÁŸœ –mÓa°ÿg›§ú´tr÷ˆþD i´ʨu¤d1Ù2‡h븘ÿ®Ax¿:±óêÂù! Æ5ìR…¥@b{o˜ô­Îu(ätzNâãtâ`¼/ÙÕ¶¾<¾=®—ŽÜ]»°ÜÀÖ¸-Çä«;¶ˆ8>àã&é¹-Æ_ª-griiYªu‡«¯ÃéÆ ó]}–Nn7{ýùlµ¶à<ÿe«Ž:âa;çé«ýsßOç÷…ïËržªô<ƒã~˜½ëç%/Òàf­wïç_‡ù¯÷ ÎL'eÃz>¹¯ï ÞF'uχãnËþ¡ë<[þ¾åã@~®ƒŸg´ä7^çè.ö°q=igÛnÄí×!Ëãz·mYé¤æÐ„ŽJõ† S/Ýÿ ˆùëúÇz?'ÈïßO’rÙmne*`vƒãfl!Æ-Ž}ºp6Ohu"©D´.¶änø¼¾(¶qû›A¤RójÇJpýã<“p¾ËÊ‹Ý[õäµë› ùñ•cn3·z*¬Îãë /1îÐÒïñжÃögÊ †‡Æ”¬DÂïÅ^<¼ÝU|B_, 3OŸÎ{NÝÏV´h’G Œ{öÙí±#ì"«GÄ^y\ölUäð,¢¼¨8bt¦ao‘Æ®k.ÎêG'WyǶƒ÷—dãË–ì|ª0žUc|Ër¥ó‚“·å½o@…Ö#ã!pd¿Aå&µgjN;Dò$5Âãq|Âûâ§SŽn¿U‚Í›«äxj0¾<©Ç¬¾—ö“.Îô¤ñ ˆiW`ð¶Ëñ@žM¾ÓøË (5{¿ûåWADàÃ[û¯Xòãt|´¡ØÜ ‡HHÂ’i¸K"ÆøøÅCÅeª‚?ÛË2g™FΕ©Scã;¿ÌñþÕcœŽŠ<’!5ÓŸ nnsŠjç•>æ·&¿ÊZvº¬}3<Î¥­Ž´z1Û×Åz?…ñé;ñw–üÅx{;‹~ŒøãÓ³ïp’ÚõÌÿqC<,èdZYRÓÚÖûltŠãèÁG¯›:»Šã¡üvÖyjäÜ¿¾©hû(|fKÍ ¢/DAò7¡uÂeU‘•ñ@wõ*ë;A×Ï“U:òvKÄU¬Ï%ç8ý‹È«æó=K¾b|½îÇ×óê“„®–?r²†¬¾Ÿµ$Vw©3ñQ§®0ºåĽê舑.ƒåŸK„s2`èÓ s—8|w’¾§/¼½å¤ñ¯üvÓ†‡7áÌýÛ#2fÇÃÞeSó6ô‚ËŸW¨“D®>ͼ]÷ëÂß§œ«ÍÏùJ?§ã ¼zRº¯Ö»[±pP-µé4[W•pýâ0ò6œ¼òBYøÓ3Y7sã`çáw96 Œ—®9ñýe)÷ZƒñžZ¥\HºÜ»äÞ¨k8œüq«¹sŸxØ|íMÅ&†Â6›Vçf‘ïy—¤E®²öQá\¶ßÿ‘®Ûj1î÷mÙI…?Ùîû, ‡Ï3cVœk»—¾vš±É®SœóA¬+—íM~ö¶Ž“ø9]a<Ú"Ç:šãÖ´‘~uQG¾¬Ûf8$ŽzÙéKåxè1oâÓ]aÎÛâêMçãAdB1ÿÇÙ‡æŠó,¾Þ"ü®¥5¸í”ÕÒ^Ϋ˜1î˜Uh‹%³f,<ú-2 Ÿ©4*<üln_«ôª¡l½(ˆlZ\uŽ¡ õ}ÅÇ|ýZXÿmü¼€%Ÿ 8J¸]dEåÒápçóÞ…C '¯sÞš ×°! Œtu¤J©_xíÙ`Gó>“'ôHÏy ò°s’ Øû–­ÿa|Õ¬Ä/~$„dÍüv¦zý[°ýô:SL1»cPÕfýÁ2¼-…óÏJ«7{ן#ž÷àûD“«Ä÷.¼¨8ØÒrsnã³ãïˆ=°uÞø3äæÎj}?M¼Kû–,Ò+Úeå+ú.}ÛLMfÊב‚%\^9oóû“9üœÝÚç¥Äßµåàtcü³snŽXVä,ÁIbá°S· Sw½qõ;&è?ȵªw´ò÷*õ=¿ŽU‰dõš+Î?ùççç…óIĉÿ¾ÿ>Ò’ïx:ë¿u–ÌmVðÑœ·`ïåOÕM¤å 2úâxçú¤r­ñ½îÞ„¾ çŠã4þO>ÿàóQéï½´_ys»òð®s¤ªåàêm(^BqVfá|m%h™º²3~Ÿ¶‚_Ö¼öG?‰ëEÒßÿé1n?zÌdÚy2ïÐû §;nCð”ùþ_ô&Ø×öÊ£íE óãíòê=ežz˜ÏkÞ;5íêûêEË,ôâù Þw-ùñO†µ,V½ÔÒ1zAZ¡ç·aÿ–•–b|aÙè¯HŸf‘og×ý2Þ·®cò~À¯Áï?ßo–®ÃÚÜÉT¼~;csŠëòºäöÄÏ ÂïQ¯™Äs0NÂDR\û¸å}lí—Â|0çï~d?tQño1‰HCT÷À½80(¡‘ßE¬i¶©u‹ƒrMÓgk)OêûVý9Ïáç5ø{S8ŸÔú&’˜£-…zÀë ™öYÑÐ1”„U_ñki–œ–®-V0ÄMgÉs¥@g˜·¿ûˆ³ƒÈ»ùî•jn} ¿—($Žo§+“&¼èÒzä9¬¾%ÄWc|š¥W6…’­ ¯¾û{ÜáÝî|²9a‚°1Z>®¥ßµ5ΚDfÜéÝ¥µ«¸ÞÎóI8/j'îÓ ó !¾ãûþèÿëMÕ‹Ä¥Fž#÷öÞ×Ùëî,Üm‡Z±Ý+yôý˜qa?5Ab¿àïUÞ—Úí¯T-ø~)q=_8oÈÖO0þ¾ï§o/?}‘íÃܸb—"kMp®OÃk.÷‡©~”ÍO‰ó~¾€÷òñóœÒ¶þþÎý³ßwaüÇå?Ìë2è±[êy|»šõf± 2vû½íîØº´ì|7ˆDzǤ˜‹Î#|^?¾Ršòû’ÂBÞcœÒ‡JÍõûöÕîB׫«*5˜ŽùrøéÓ!û€ò˜)ZŽãï!a~Gû)æ‰ïW¡¿ó„ïãå8—zûZ>ú‚¾L&ëY?ãó]°ì‹6±qnДøð´€)H\§ÊyŸ‹ç y¾ðº•žáu„óu—‰÷»=à ½IÝý®ù4ÁÓnuÍqÊÎнfKÏ/ƒÈ›Ì!ç+æŸç}öôaï¾ÿ&ìob¼>––]&èñ…[÷`Êõ§Sâ{šàCäðÛžáéÄô¡ŠéHð‰ª¿öø[óãÕä—«Nü°³6Ÿ¿^T½¨þ¼¨¸·½gz«ŸQâ7žÁ|OµßSê7NyÓ L|-*›ù®è BͼOí%ž~ æé—ÍXc”‘-a»f1FVÂ8äŒgÆ8¤þ~®Fg:2O3cú²ââžãœqȽO}£Œ³ ì%¼i)ßÐñy2oZÊ‚p“øµx3~õ^ö‘ð |ÃŒ{RQïSÊèáÞ§ÔsÜ›1ÂÈ<}™' ª¶ÕÇťޕ9ý¯‡¡Æ!÷—2z´ŒÑC=ÇC™'•ú‡Æ!eN{£Ì(óäÌiÎèñ–xŽk£Ç‘yr&„7ãÌR¿@_ ‚ûº0Æ¡#ó>ÍxŸÚI<©8;-›y°pÊèùÛ£ÿöhÍV¶e÷Ä`cõÄ2I¼©)‚zúI<ýƒV‰‰ïÇ’_ͼ±ì2”ñz¸G–’ydÑ ¼?V ”×ãÇ|UŒCË9jœ ¡f5ê—å&áBpŽš Ê•QJà¨iGûSsŽ÷ Ô2^3óÑ’I´R†š#cBd1-÷gu`¼ž ”’ù³š™?«/óÈW0_­,Æ™¤LoêH¹Ü7úSû0Žš#óm51Ÿ--óÙrÆסì°È]%ÚÅQÓK8jÜŸZÊ…ðcMA͸”vÌ707GÍQÂQ£ZTJÉ|·8‡–s!|$þÔ~Œ !gþ[ÜÏ•s{”ÌÓ•2T.WÆQ“3ßÀ,‰o õ§æ|KÎì¡MI)ñ ¤¾X4—éÚŸy_¶ÒîÔyïÅÇ%úæî¯¹{)í£´‡òþùïz'ï™ÿÎû/·‡4gÌþ«~Gûœ´Çý»þÆ{Û?õ3ÚËr{÷ýSÿ¢}‹ö,Ú£´6ÿº?I{“´/åîIôQSÊ £þ|2ì=¦|kÖ„rd¥ŒÙH™×… ÷Âwc^øæOªa^|”;ëÃø_ÔïÞ¯¤ÕsTÃX_>ŒñEÃ…ñ½™½Šq½ì׋rgõ­\Eî#ªg\Eê!êŠÉcD90®¬šñ©ÿ¼·ÄTË^äöŒÃåÀ8\JæÅL½ó\%^Ìæ›gáI3æ÷`V3Þ–=ó“wdŒ-ÊáPc²ê™Wžõ_fìÊjÑHüâµl à˜ÐÜOÉXЮŒí&ñ¼óù;Nú;N²ùÏ'Ù³ïlD9bbû Ìy¬ÞÊ”áA™†:Æ4tfÞÊ4éU(ʓߥgÞô”k¨gþô>¨,ÊÑ¢Ð1/QêO¯c~¢ÎŒkH Å™ñf9?‹s<\?Ë H#áxp~– Ê•UJàgù1~÷Wæü,cmû±‚£~£z”ƒ„5+egÉÃ#›±fݘ÷(÷±ÏB©˜÷hóÕJüGý˜Ÿ=eRwvuãᇢE¬b¼YÊÏ’3?Rêo¯¤>¤¬¸Õ¨P”=¹›„7û¯øY ?‹û+K9:Æñp‘° ]ÿŸ%—ð³(oÖ•…Raóð“ðf9ÇÃW⯬c6‰O)õ½ÏB©˜W© ó* EÙSQÆÏR0¶!e€;cÒ3eÎ6¤Þ÷~ŒmH}Cu(z"ú…Òz¥¤=ú_õgÚÿ]ÿ•öWz‹i_ý§žJû)í¥Ò>ú¯z¨ÂF蟼oþSüWý‘÷Ei/üW}PÚyÿû¿é}›œ}OÚó¤½.wŸ“ö8ioã}íßõ4)/ìŸzïc2ƒ>tÊ!²ÃïÊü04(s>Áªd,ì¬Bcˆ{¿k ,13ãdP?c_æé®c~Æœá‚}FVZ`‹É±¿ø2&†e‹Éä*áóQïvg C›‹¿'c ÎÞ3JØ{®Ø/ (GæYL9¨.Ξ«Ä‡=¥ÄDõ«#00(ûô_ñ/¨_±=ö7‰_±7ó+–3µ‚ys†­„¡Ç> äwcŒSνpAɰî5(SK+ÃÇUÂïq`ìî©îÇü‰]Q2,o” FǸÒnŒ+íˆÅc¦ó¢¿ã°¿ã0›ÿ¼q˜ûN&”Û•Rb‚û±$§| PÆ¢léPÆ0uF…¢ì0ù]QæåNA”#å&¢²)W ‹"eÇ<ÝCQvÌײ‚l‹ œ‹P Os.\OM†ä-á\pžš J‹Ê.%ðÔtŒ§æŠ2JxjfÆ—Ö1ßw”å(a˜JYj Ƹ ÅH¦”IâŸrÆÕ2xª*›2)Oˆ-å;ú±â¥œ L`L;3Ž)å©)° }˜/¼ [‡²ÅâvAéQ2,r„cú¯xjF O- ¥ÊŹeœ W [Òíxj OrLµ¨l”36„cÊ9”1rn)0)iSQ¢|QY(ó‹ÏF9c£Ñ1Ïx5J’aÓÑ0žš’1ˆhR£ (‡Ó2‹yÆs‘3*EO~¹þíÑ{´Í^vdÇ6À!¢¼K{Ln7”åˆIîÊ@9b²kÃÍ¡€À²5£ä˜ü¾ŒC¤bœéìB‡È€rÀ‚ðf"Ι6K8Ó&”#ã¹eHøY¦-*e/ápØc1¹¢ (‡ÒƒrÝX\Z “Ãe‡…æ–‹w™]^àçRΛ½½•ÍAyº.(JFǶŒC$å]:`qj‡ˆó.M¹x—2,Z7”%“á¿£L(9±eDÙc1»J˜—”EdBɱ¸}PY(¹eËøúÿ‚ߊ’aРŒ(Gl>(3JÁ8ÓY(%6m.އ„ãA¹p®(ʇ†±ˆ–‡+Ê€rÀfâ23>œ«„ED™—z ‹ÈÄXÓ”¹‘EyqŒëa×NànQ”5ŒÊB9c# eÜ ãnȱ)iPtÎŒÍÉ÷oþÛ£mþóz´œ}f3}¶˜ØZTJ… ®CÙ2’žñ(k\ϘÄj”eÉï†2¢Ɉ’c1hYA¨Qz”=† JOyäX .Œd‡…â¸ĜgD90.1åÅ9`ù ²i¿–ðâT(?VT.ŒKLyqn(“„—Á¸æ¡”mŽçŠ2¢ä&±”§ÄBôcìsÊ$öF™é˜ Ó—§å‡ÊF9c¡êX±:£BQ¶ÕžetÚÊðßQ¡2Ó©®!p‰i1+Q¾¨,”3v(Ê‹Ûe@9`‘{×±r‰ÿ/Î$áÅe£œ±ø¡l±¸ ô({l n(cwjþ§”ðâ(—Ø55*TÂ%6£”-¦;m&jÆð´¥cg”•rÆãÇšŒвÃäsAPØt¼/NÅØK” ï‚2¢Ë“²—œ%ì%5J¢¿ ¬#ú‘éÚ›‹ é!öVzˤk’| RÚë¤=Ž÷3iã}Šö%Þ{f°þ2õVó¼ÞwJjù迨S^—ôÚ.ôü=ûÆ6­]è|´¸À s¦û”%Î,eƒé0§í1Ÿ5åö!ý¢”-ëŠyk¬˜“mÈù…™À%ÔÔ°”×¥¥gèþ"æ™®Yãsµk!°³ øÜðyÙãrÄå‹Ê¦s¼azºÎŒ÷?Ëéïûñïûñ?ïý¨dŸ)ƒþ;&¶*›®›c‚‡æ8Ô®Œ(Ë/p¨ Œ¥ê‚2 d˜ü” åHy(JÅà‡²Å‚pAP2, W”%ÃqEPöX(®Œ§ê€£A™PŽŒ§šrÄòeEäŒÒ¡léJ‡²Å¢re‰…a‡…aD9JØÒ”+í2£°P4(ÊA†ÿ=Ê,8ÒX4Þ(3JÅã‹ÊF9c…2öª+ʈrÀ‚Ò Ì(–+.5Ê€rÀ~gBɱØ|Q(%*¥bŧF…¢ì±ÝPFÆ^uCQŽX”Þ¨ Æ_Õ¡ì±@ÝPF”#ö9Úï°`è¹2”#­7ãE˱x}Õ‹8%£çËr±QiQ»06ªc£Rªyößqçßq§ÍÞ¸Ó™]3‹>LlKn5JŸWàT»¡Œ(‡ü§Úˆ’aÒ»¢Œ(L~o”%Ç"ðA™QJ,Ê ÂeD9`a¸¡Œ(,7”%ÃBqCQŽX0Þ(3JŽ…ãÊBɱ€´([,"5*eG÷4Q¡(;,*7”åˆÅåÊ@ɱÈ|QÙe¦µ%ÂӠÌ(%že‹Å§BéP¶ôŒ­„míŒòEeÑ1(¦Ê‹ÓвÅ"uAéQvX¬.(ʋ֥¯.p®]P” ‹Øв£k1(?VÔ.(J†Å­A™Pr,r_T6Ê‹]‡²Ã‚wAéQ2,| *¥À EÙ2öu(Êž±¯(l Þô /JŽÍÁ‡ÎÅQ lZT6Ê›E(ʆ *e‡Ãe@9`ñFe¡œ ÛŽ±°({l*j”e‹ÍÅвÃ&ãŠ2 dØlÜP&”›Ž/*»­ÀÄÖ3&¶Ê̘Ø~([lF.(ÊÁI`b&¶75Bÿüo÷èÿ/ÆŸÿ7½ù2ýW½Wm#ôÜÿ—ãÐÿ×cÐÿ×½•÷Uz¯BQö˜€n(ÊÑeF)0!}QY(&¦/*%ÇõEeÑ󻘨~,YQv˜°n(Jމë‹ÊB)0}QY(&²/*¥À„öEe£Tt=%· JÏíb’Q˜è” å€ ¯A™P ú›T6J… cE FéQØ?}P(%†Ê‹Ã e¤ÿÄ"1¢dX(”™öQ,{,JŽEãˆEãrÄÂñA9bñx£Ìô\ ÿ;TÊ„Å$Ãb2 ± |P(%––—¥GɰÈÜP&”#›7*¥Ä¢Ó¡l±ð\PF”#öD3J…˜…Ra1êXA:£l±(]Pz” ‹Sƒ2¡±H5(JŽÅêƒÊB©°hCQ2,\ Ê„’cûÒß<`QŽXÈ” %Ç‚öAe XØZVÜj”å€Eî2£Xì~([,xW”åHÇ¥¨,úûQÖØ^$çò8n¹Æ¸<†íCz³}Hû]“-;¯«aìKÏñÒß9ÀúÛTú¿Aø{Jö[(7ÁQÁòç(ûwõÝLEÞKM§œoxEôH;kî6¶Ó}Ñ?¡F—š†}M ð\ò‚®W‹z‹I{ÑO¡CcpÝQOG87¥ò»‰mZ|›C¸Ÿ½Ž¯Sþ©ÈžWÈ=õ.݇ҵvÌ›×ÞoÏ ºZŒtdêœ[Žõ\æ1Ÿ¤| ø[ý©¹ŸçÜÐøZŒ¯²€Ÿ®’/›*•ß$ÂJÚ·:ÙÜ‚/k78ð¦Ã»}uDàµ,ý2ø?Ãw”hvÑÿ§“”o¢Ç¸óW¬{¬?}•´ìÙ¨þ¢ƒ°ü™ÿ¶uMàYÔÅ”2º/”š×óáÂ2:òÚ!r@Û DßXî«#ø‰‚Ú>Gî„;€À}`þu¿CÓr•?´»Fv?0Tç‹„Ø5ÃÊ™ ç»ªÉƒ jÕ–1Þ·¾vózZ}¤E_Íu6‡æaÏ«i®Í½LEÍ_ãzo>~<¤H¸^`sñ6øùßÝ=¾ÙþH{N‚H™‰ùË=ç‹Ü WU°ÌëÞW‹Ÿ… ãÜXþäÑÑÄkdO­†¤Ø­HØ2ô×­Œ8XsvY™¯O†A·‰Îá•w‘´1»/d¶›/úºsŸ!ã·^v´ú ¾úMûvÑë(ð:1í/C˜_H\ ©Ò¼it8{L—/Ü3ªOKt,âDŒ=(ùn¾è£Ãï»àgjõïo1íZ/§…9óE×K?æ`Bj)r> Zö™yÒëJ$uÚ²­ÉáÐ.xÍç`¼ŽÚbT2_ôâ÷‡s"9ÏRð­ü×4¿Èþ8SS„ *´¸£oY#¬wÌüæ'›önßjWoXy¥ZŸu›ƒHÇ¿ÞzYð‡÷‘¶ä7Æ;®Þxæ…­žLîø®Ç#¨ú _õPT¯£ÿ¶¶lW¸jó=ãÁŽ ÒÄb fõÕç~0‚Q1ÑÏÌ’ß·G~ç=ÚëÉAj³ãc„Ö{JÿžìÇ|±ÛÃýWìŸâs]“’žÖ±ÐBÑ÷Š÷‡º-(QDô-â\u)oތ׉›zôØû…zr¿;%&aͪß{mÅë¬n¼ìD Ó—v† "ø4[yü~Ä—º‘ç8J¹ó6÷3“f]+•|FOš×à2ü‹–n,SN½)¾­˜veþ6˜ðjå.óƒ æ{¹PÌŸÈ;Ï ´È#ø·È0·¹¦å·³ôdÙù%>׆—¿«<»·>®p®â‰§²‚ˆ«´håzp_>îãœÃ_ã—íí[:ŒÌ|"ßðþÑÐhîî'[ÖÆAÜ—4Y»õ`öÁ!“{½ "‚¿Í"1/¸?”À ý褾jŒß|(u8#ê¶Hº$,~k«ã`Ñ𼩵4k“©ST‰­=ªÏëö‡ï¦à˜_ô1“òS4¿¿„Fýq4áD4œÏß ñLÏ8ÈS«ÙØ–öŠ\Ûñl¹Òž~+OJð-·Ë‘ZŒ÷ºØ ‘™²0B]á½ECg§Œxÿ%qPÁpøàØ^] ÷és6 "ÔEpàøEâóây8øþ¾µ]”}­øûó>-yŽ×ù:ɱ{í"a¤øÆzžò’1PiTýKOfÅÁŒï.žï½{3nB9Ù|ÃåNgÎæÏu¾ç–÷ojåû ÷“úâšñ:ûÖ“×…ñù¾ëýóÊIe ¸²é‰±q`³çË›nÓTPusE¿ îAd”ÓÖ­ÕV/ß»ü9 ¾ô_œrð/"2Îtª[1ŒÌZ;ʵ { ˜ŽíàÐ7 %V5ði9:–|™03ˆlZ˜”uùÅB‘çËëG¨ËŸNü>q¹Vó ŸJe߈¢÷çî×ñoí|#î?Å}ö¸ªð¿3ß/¿ëÚ£[„‘Áíœ?wŒ…}ªœŠ,+É¥U… »à°ë=÷‘žš=#Î?²~~ÞÏ_F[¸~mוîÍ€s€-ùñ}W-ìwFFÂÒ ”:95ÆZ¹¡õÏGÐx}ìˆôÒàøòyîŸBƒÈ¯wcÎ5O[$¾¯ùû‰ç÷å•roµÑ‘Í_5¿Úõì©I{c¡ÒìÕ O¾z7ûþ(ÔýL¶€T‚È!:œ°·Ö/çÓ[òãŒQ|íÒFa¤û·Bn1±ðøjIÿþ) ¼ö…{¥OT~DF\½ñÚ¨sý°y}q$_sŸ_Kžcü NY±isù)ÅÀúãg¶…E?‚)_]ÝŸlè{b¾Ì‹ýq¡üR¯Eq‹ÿàìÈÝ\Ã&¬µ…oꊕ/Å7ÍáÇe™©Øy¾(ÂÈ/¹‰×z<€šÕž¨*…?‚~ž[Gߌé ï&ÜÝÛD Ù>Ëx·té¿ðm¶Ö+÷ÃÌÁ5Âë¸.wžýû‚±è=€³u &õ;ÿڹϺᦂ!Ù-® ÷ "çniËt.´\ìó¼ÿH9 Œ§­Ô4¹vÁ0²Õyà§ÛàM“‚áÝŽ>ÕZŸ±OŠ †>®™ùî´"·n9£;±\ôãïiáóòãm4¿ö®ßz2¬S±ù¯‹>„¡QoZÜøˆñë†Cÿ…-ŽT/D†^®{´UÁå¢/w |/+'…ƒ¤ü ^çüÁ ïÆ}Ö“ëk3âõ>Õ4ˆšûˆñWGÁîú¤ŽMü#µT¹SKIîñ'÷–¾÷µ·…eà¢'»6F~­¿é!Œömÿ¢ÅÐGPqMחᅩ‚;†á¦ØADîñâì,¯%ð†8oÖ’×/à0´üðDOðKŸ¸ñê,?÷ë]ÛG’¾zåö®#aiÒ…õš ~¾Á’?ÞoO{çÛ3áè#-}?›1~•7µ.ÔMÖ“Ucó>‚Uy²>¾­þš×ô¯q_é  ¹{eH)\®Ê»-Vi‡SªmQi6âø[xRÞ» ã'5=èo¨'E;•èå;õäOŒ9ûðóC8<~´ÇFã@‘Ä–çãª~%ú!œïQöw•õÁ4qãïîAŒ§³L¬gáþ~ýÆ¥ŸS‹q5Y‡ºöÖ“†e_¬è×)"–~©ÙÊð¼ùÚeìû)^ºF)ñuJÁwËsåÝO§r…G‡7VB÷Q;ÇøMשâÕÏ©£žÔ˜O;RÌ4Ÿ©táêCÔð„oÊðî°Ðt $3FO[¹âø ñ~ó÷oÒïóÉ—|sâþú98ˆÒàŠ·’ezRÀÛ³â ]sõƒî!œ‰Ü_ {¾¾0ãÜš«*} ü1Ýsñy¾‹œK^3C))ˆ‚ã·dÆÁ¯jó¯Ô>›8lªP‚j ‘÷¾3·t/wñ~ózâZyÌÜ7—s!-ù×oI|BF§T |XÍfׇ +oxŽOO0p(\k>ûá¿@’ä>±Ýü%+Ä÷zn®ô‡Å¯ÍæËŒ—ŒñW¥ žðð׎0A÷IÕwœÿþép¨y*kŠÛ™@2 Ü õö-ûç Zòã,^øõÑšO×È奚7÷v›`¨_íC‡?„gÏ*Gw|9:”OÙ]dçæž/­üyÎm°ä7ÆÝ¾vdfÆ«Dàv%@üâÊ{«ž|±Ê¬†K/ôcþí¤ý Œ+·šxˆŸ»K¸Ü½Lk ÜÇü\-Æ÷o3mÕ‹¥WIø¼óã’Pt¶l–/>O2o»ƒl lz™úi7ÖñöUAA NzˆùÇûÏXòã5ôö\žç*›ï&Àº/W>[ùž+Âë º<ú…{p)8·_7ïA´Çã á¾Z¹ƒ|=Ê’ßÿQëðLJG\!OW>!F5žÜ®ÁÌPØ·HûG†Ã¸d·EýÇÈCœñùél—:¶!_ó‰xé|Ý&&S‘˜ä{@wó2!J—{Ë&Âý±¯xMd”ü6 îÙQRj ±`Kyˆ\Q^÷¯Zç»ÐSûCXÁxÅv¸7u™\˜==¤{"”|Wrä’– Î°¡Xr4\~÷Æà<3<Þ³áµCš?¸{k­vs¯»Ey<µ­ŸÃ¯_ñçÓÇ–}‰¨Z?-òvn"|¹3@ã`ÿ&Ä×K¬7„þH KÅ‘’Fä¯-ô*þñSü ð1sÔ¹ão¹±M—ÈÖڶÇÁ´|F›É¿b¡Â°ã'ƒ²‡AÊ©—ª¾=Iä÷[¶ÔˆuÎïÃúáÂö䇡%}×ôùê(ÞoK^cü’Ý_•Q4»DZ?Rípt"<íß5ìy,|U¾Õ³Ë_¼ŽêÖ%\¬8Kÿ|‡8ï|Ðm…üÅ8…]—Tx{û"±{¹qĸo‰pÃtK÷ š+~^_w|V\¶¬s ÙØÝ)È-Ӄϻ˜ÈÍ”~o=Æë²€’P/’ÑkŸJÖL‚zk5Æ…ÇB—áÙßFìê™>{Ò-ÔºReaª‡¸~Í¿7_·á}ˆ?¥Ï͌׉ì?§ŠÛ¹PrqÓ|…&ÁîµËG§…Æ‚àÏ݆ÕTýôR—ê ³gz{ˆ÷—ÿ“û[ò66SÑ™*„:ëÉX• 4h”7 ú¬ùP®§Z ÕRæ©Ûâ}x¹~åù}½=Ä÷±àsþIÈWŒ“Ø7«oì” äÓþ±Ûµ$ ö}Š,þþ`,ÄÆù.s.Û&Ùø¥ßjŒõ5! [ä!æ_ÿæü£ãŒÛoâ³¼»nŸ';_wI™û) ´òÄ6·Ä¸Ž=ËÉ&õg<×@¢½±lqžÏÖ¾àó¦Ùá_7óŠù#ô5a>ªÆ¸©¿FµœØá<Ù»q‡ï”:É7­äÒCšX¸5ssÛaõé|²7™dÍÌõÍ+Oôßs§*|ñK±¿H繌+¬çœ#Ìˇ4œ Ž-n÷u™ ŽõŠ'¨ †AKKͼý €Ô®ÿbœËOq=€?ŸÕ—†G¬t-(æ—ôsk1þí½?ÊïotŽ«FW@’áÍ®þC‹ ‹ò jïºÁÎ`Á´Ä‰þ­µóï3ïcü:Üÿ[º®¦Çø[#õ†{{γ¿ó<:• /¼Î=ê ádþ#¡ªÌ÷Èí·dÅ+üú³óñ½%_1žVé,iÑ?°¾O|2¼ŠëÜênõX8únHx4ÆRûöB$mñI“oœ)cúÒ!a›0µy©øFÍgȧ”™ã~Ë“7†¯9rzþ¢ÑpÃÔÙÇ«t q,Ѥê¼Ö¼âﳷϞѨÈÁ’¾'e?ÆO§µëRÕÿ÷<ߦ)0Ùïéø)Oc`ÿضÏû¸‡†4‰…–£ð×+ss†ùû’ó@…ÏÁ¸´xÛïº+Õâ 9÷k§ÍA)ÐGszu‰{1ÐúCý¢—œA˜÷’‰²¨&©ß4¤ï‡UMó þïjüûƒ‡9Ýû`!êˆ<žm—¤ÀnÕ¢Á/ÏÄ@ÒöµÚ Œæ­ §ø’¸ÄÉ nÖ²Ö=ÿ¼|½sùrŒ0~¥Ÿ.òI³BØx:üFLžÑE§öÒ…Ö·A•N ÂÉÍÚ£ µB=ÈEõ¥ò¿Åºæ¡BŸÒb<¿4 Œ&'½)x02[yº)öõ(¹Q¨‚zå³×ÍÂqÍÍÌÍŽÅœ=IÎü|é4±òè^yw’ö?=Ƶà“Y7l«¥¦À¼Ön—ú­ˆ ¡[Óª®RA‡ÉÚæ¿–’SãH½G'=‰°^——­—ÔÉ9OÃx?6-Ô9DGæø¯Ot±I…•ŸÀ›1ð=%ë~¹· âܻȉ¡p™îg<Åu¤ßm‡í‘Gì×Âû¡p޽%f*(¥Êø2ˆüx}ÑZ©P|ËÚ^Ãc`œíÛqOWÿŸû_GWÆy`uõý—¬ñù{‹¯¯ñüríúÅúÙFÀ9t–<ÆëÔüvîS¬{ ©û{YÚ#ª˜V-Ò9¼wõ¹ç '^œÉØ0Üyñòëž§žìù \þ}@*ýø9þüÄTXûî“]F½(ñ»«~YñQPÃ2 $«Ð­—ø¼øû‹sÖ8o‹ó;,ù‹ñÈi²´\Ū>+SáØ#íƒ|Ec Ÿs£ÍîºÑàåxaéÅþ$^V;¢ºqèT;ãA»‚`kFµ`ïo!žãÉûQ2Þ)R94ß‚»GRaž¦ÀÃÓ¯£Á湬KMÃ(Ø5ÄT̵S ™4wÚßë}Ýø°ÓEƒ¯°Ž¨Å8ÕãGµŠ*u’Äõ”_~5â=Û>< ƒ.:ÜÄv$Ü,Zs`l“@’wL>.ždoÑÒ[T/ 2^AÎï©ÈûÉ òsúâ§KSáPé¸f¡ÑÐem{¯†Ý†1^5ŽZQ2œµÿó}qm›r'Ï;-Ìú‡ã.x¢œ¢~rŒ4*þÃoõÏTð›Ø«ÕÏ#Ñ`Ù^4’öõ*¨iHèªÉŠÖžb_戦ó5—{l±aýÞ‘­Ç±ùÙ£LEïªO>©%)¶±” v¹¾¯Ûöh¸÷.h]’[¸vlBd‹@âäö:fõ OÂ9”œ·fÉCŒSVVÿ§Ã¤p…÷¶³j›a~¼Á;Ú­÷Ê*Þ ;”|¿Ä±¾S ¹4½ðWí/ñû óg[±žø¾Œ”§ªÀøïÎÒêÛ"W+-âÕÜ ÷íšÊ®ˆ†‡W]úU¦3ëFdg‘§Vó"¹ß{Â>æ''Þ¯.ª°®¥ÆøÃgl[w+á ÉèOµf8µlþæR ¢Ù¸°<~\&äCë@b9ްÌK ë5YÂúÆùZnz¨¡ã~²ášgË ÌÑBq¦ÕŒhèû|qÝÌË`áìnã=+’¦ñ¢x‘ Ý® ÙøQìwÒõ6-Æ{Ó™’Zö¾Wòu˜:Î EÊ-ªà5>úO*Ô»áü®½eÔ¯.ßÈÝωJz‰ïA¾ŸÉûý™n–¬¯÷Â+»ßºc»Þê3|+{~Èï¡Ñ`9FâÜZ>úù9#€¨†òpní%ÖÿÞü?óþTêq« Lÿºc¯£]¿zC“ÛÉüb†1oW˜¡CñVEzFƒåX@ý~ð®?›M—6d}n F”.2EØŸ·‰ËTü®s²O³B[ȯПïï¬3ÃȉµžÊ£Áñü¹MZ €ÖÁúaó_1ÇÜ{6°Æá|=ÎáûÚ–¼Å¸6®¯âçí^GlÛ‡Ÿêäk-Ôº½¡n4Ì}?C[äÎ øÕãx—ÏÈóÝó?6{ŠÜ"¾ÿÏÇÂüè¬S‘+ÛúožY‘½®“¯s=yøå×½Èq¬¦ÏÇÌ0©«sßRÑ0aÃpEÈÝ¡üЇWÈS\wäógÍšŸ_KUzå$¬…8IבÕËÞ´‚ËV¸çC‹¾ 6Cá­1·ã¾¡çÏÓ˳|œÅ÷Mšž òão}p¤Àâç¶ä1ÆkšúM—â7‰TžôsòøKfHwÄè’f„2S´/d ƒž¥*b $×VÏ_ìEržÇy.rŠ„:«™ƒ_©ÅøB¿é£¿í:Ú<Ü Ý¸—?~×S3¾í~¸s¸Ù>>1gv øÆ^â:ÿ'Ïoλç×±ä7Æ_[Wu³æê™PuÄÄö©QfØîó«oÊy#\œû9Òåô@ø±®Õ¨'Iþ<Íç¿ÒZßü¹òù®%1^íó— e\[K/×i«Ž3CþàÛó7Â@2¹lÝçýá´¢qÁ’±¤ÑÞ¶µÏ©þ×LoÝ­u÷CEç‰%¯M™Š_1‡îT] ³dúìMÃúN_âø.#Ü[²´F“^½ÀþÜ÷!𷤔ÿ©]©S=E¾í‘#“‡wâû´urìÇË0îÕ¹Wgï¼ê/íÝvì+3,NÊ“Ñi½Z,ðxg·ç'û™×¡ “<Åu~…ý„ÂìüGr³¿ËÖáWo¼öùÕõ_6CvÓs½†¸¡ÖÑ;²˜»Jè1gS»vb=òýŠF#}bJMÊŸƒÇ§Æx½ƒ"nõº¿ ¾Ãš¯ïó¦ÁÄB;B7Í1B‰…‹ë7ìКÐc+;‰ÀyöʵùÛir˾-äÇ ‰ã=éüIƒñkõZêÕ´ÏNX·}òy±4èéQÀq’ÚÍÞ7{÷mNÅzøŸ$)¿oã€ÏK\§áãÞçøüL:ÓbüÓ¿Ooª²Îìšy" T”XÝÎßµ·J­4>Kþ9FtÏì¸ó£Éí†vë{Îyâù =' Çx‰ß^Àôû ïÊâýƒÊ¦A‘´0‚zäžó Õœ¡Ú›y>~$ªly;0Óóu Ö›]N¬¿h'~nKcÜÏ!¯Ï¶ª©áX”¾Rcc‘*Fh_õËÀªÃàR’ÚïF‘ òpûĪ›­ùÆóYàÒ–ﯔgŸ© »#†%3îiÎàç~»ªLzb#,¨0÷üÝÑC Ì²Øñy‚H©ÔµÃ›®öüc_|èO›'‡ÇÂså\÷ÅëÄöoçÜö(´)_¶à¤JiàerZ;ÿI8oË;¹bǰfãŠJ_I¾È!ûVZÇEÒõ6ÆqzÑÕóääã œ“I§¸€ÁoFA]|⢡}`Ç6‡ìðóÎNœ²c·µ®ù:&σ…Ów»ýõcŒû£%”úAÓï}/5Lƒ®~3–œŽ‚éƒwÆÙŒì Eš†,Y;ˆÔê¾rœÞó^¶Ð ‹Ï¯÷ ÿ?6Ãë |ûÎ×ã”ú1»NûÖi0u[µ´’{¢@à6wºë×|`7£†¡Sa/qý‰sŒ…qb³óV-ÆmC—M»úCßwÎuJÚFûoŒ‚ÆCÎØ¯Žo+ø–[7?ˆì¶£#"/qƒ÷%þü„:w>_´ä7Æ7¼j´äDÞ@ðìpyÙË.i°Ï²:¯ŒbûRMa­Ñ¶u {9u©Gëa^„Ÿ¯áÜpaÜ(ŒÍoM_º`´»oU¦A³yuK¿[ãœJí¤w?o|ÝrÏ/qžœs¾õ^ÏpºÀÉ®c“©˜D·7é`®eÀ›6DsqNÌR:¾ÐðÖp‰,Iº0"ˆÌ-ÿùì±^bÞ \½/NoF”Ùõ¸ë¾¸ ã ë½ÁÐaõíå{cÿûÑ­R݉QðÁP½X‡/N ðOƒH/:jµóúcš¯Ÿð¾*G*0>¾äpFŸ”ØkÀÏWþ«* Tµû%÷6vƒô'+ÖV‘n½Î,®RÐKÜßãûùÒ÷‹ã9~³,ºÚ(G—µ¤Á5›à‘—äQp¹Í‚kžôa?2ˆl)i~[Áë^3ç[òãÕh—SþJgA3!êÀ½Ái0“âJ«D óZ}ÁõìãÕ©}ƒˆ"åôк?¬})çy;±ŸöÏøTïÝÅœ|V-^§\@RÑûEÎosWEïaiP2<6-îW$Ô‰\ç·Ö©?ì£Ç—‡Õõ>‰$ÄSÜ矿ØC/yøîÀÇa|¿3Çz^'ªi/ãØŒspî$Mà4h Ý“Ô6%ÚÉ _Xz̽¾2ÚF‘Ç|£Øï)¾o„õ`aÖŒqÞÚôêÜ4õš7¦ETº ö ºýB¿-ZþÌ?ˆýRÉó±µð}"!®No$â8¦.]¡¹Þ{2>às[8ÿB÷±û"!ȽyN}ú³mån<Ì£#·Ê{VÙîè%~ÿÜë”üûó÷­°_ÄÆxáÜv(¤û?º¾o´O«#ÿä ¦+fF÷€S=³V·l #Žy}r/a·ðÏo¼;¾P»ßß„ys]Ö§„÷¹ãÛü¼æþ!=,ãÌçÂ>ï3+ŠŒŽ(9³@Ojè7Ûb·óüã}Ö’ÏoТIŠ^ßš?—Dm-5.îi+è2há·N£u¤•‚’!­ãsþÏÌVkÎu>\Êý<´»^\Îù–ã·ž§œvhâ%HŽªoˆêž*–Øá3<„|ªõšÙúW¡#Åæy¶n»Ì“Í/“ÅüÎ!ç󛯗 ßGà]kñ:›òP åe¸Ÿ…fE(®;ªï ‰„©õK†ÕÛP\,SGŠhí‡7¿mý|Ü+ü®à‡SŽù"Æü­NzÀ‡ËP]û}îäöi 9ÓvÞÙþx>¤:T»Ò’÷ÌY³‚Žd”:ò¼ü%k}òû,p!óˆó0K~cÜÞùO¹Ûݹ¼$ *&-ݰ{$´L{æÕѶ°kþv¯øü:â­öqšÆ“Ôªús—­È—Îçl’2ÂþÇUhö2\Õ¯e|ZY§šªu$ vŸ×Êå@gX¿0¡R)I‡d…Ç"븉ÿSÇ®»ò¹ 8¯ŽSeÿ`õî¶›[_ƒE.]=Ú, R8}Ü&‹„_å2î¼\Ô> ^“T¬Ž¬œÒyÛÍ­>G¾ŸdÉcŒ×=b¦þ5Ðmî7úk£48·²F…N¶‘0½óï“£C•Ðk?7UGFÏ ÿ²ÔÚ'øçêÎ:Î;¡˜<%º}Îù€¯3rW¥COhAqÔÅqYø•îï"Äs@G^¼šèº]G,m©ºg®s)/ž÷qß;-Æz>×½´.5x½í6¯ûÝ^ê¡1?«¤Á·aÚzA© {´AéÐvàÿ¬mÍÔc:ÒÜòBò Õ‹÷)3ò™ð;-þýJ-?öw ÖC—Àgd8~¼}´Ò€V#À>ÿ÷Á—ÔuàÅ'™q·ŽøµŠhß/Úãó.œÏÍ×Ñrìs`ü´ÑMÒÛ7 ƒ!ÅæøMƒ˜:?ïØ˜ÚêNK8R˜„Wï¶ÿâ9éBgÌÌ;ù>eîu>³ä5^'+iiè“Ð0~w’馩#ìM°pÒ•í]’‹7·ª¹^Gzû”R¥ÙzŠã2~¿ù>ß—ŽÏl’3‹Äqíx,ç•›ac·Øø;æ˜i²9q ©~ ŽÁÖOGjÞhbè{~ ð e§ÿóu;ë®CÊíè*ÿšá\ÉÎY¯"`m£Æ+Ku ãÖ';¾¯#ßGæÍêÒósÛüw¹ç1ÒsW ¼ÎÈå †hËߟc~ø¯³8ÆÍô-*½Î;{«cGâh¹aX瓌$óûÏCÎçï>ß—þ.D×ù1ôåͧn7À%å8òÒ {öµŠµÇzº’waÖ–|¶ùûЗ:R¸ÅÌßw{YÇQ9ùÇÂy# Æ›jY8¹OVÍw³}l†­ó ]R:’çkNœ·vˆû’¦#{–W;Òყ˜/¼ïñûŸ”cã÷*>Ó¦µì&,i}ÚiX¢’/œ3kí#Ùùè¤Mª÷›áq:¢°w{Óq”µ¯ò|ay Í,­›'1=Æq¡Óë oB•סi‹bÍqªižÀJ‘ ðà_]ë–Ev¼2éÈ·ñoÃ+`¾ó>Èyôü|¹òHÞz‰zá’ã®kkÛnÄÛÐkׯ:'Q­=O‡áçî•Ð%øÞñšpq= ®#tîñG?y§š}hõÝ"9æ§6)™Šibû|i–íœ[fðUÑ“à‘p¢{—£o·€ôaþi5¿èHZ÷»å{^ö •j¾,µ·îŸ³çÂòO8·"ø‹3.~8p²÷h Þ Jñº`$DŽ™a·ïEKxÒ|Ö4íW]W¹bÝO÷ÏÙïž8\:ŸQ`|íì¢;3óÞ‚£sËœkyïG`ûz…~F€Îe¥¡Åâ–Pº÷ÝäçÙ:òj@hµU/­ûtü½hÉ_ŒÓãÙ°w»šƒýfša¥M”ÿÈ0w©¼Í¨=Í iÅÛÊ϶Á¤úÕ4§¹Ó­u-œ7.K õ½ç|ÜdÉcŒíóÎaGÖ-˜ïÐeHŸcfè?µÄˆáXç]w¨tçªÀ€&ÇºÕ &noýšÕxæ)Žÿ…sX¶âsÚþ¹àXß>­àùÀQC>n&ä1Æ_9¾¡Ëˆñ·áÕê™ë›ï5ÙíÛäff$üj—tféÚ—ÀXeï`âyû¢âßþÜŸæ¿ÿøåûèxðÕÖ9xæzŒßÎiľf÷oñ¤å›6ï4C›Æ×ÇÌ}Áö5+’§Fùý˜LJ—žZÙý°g®sä6°üÀ§Ô¶½ò‹yÂ?aÉgŒ5ùáói ð®ÛÒ+>›Ì¯Ì¬Œ*ï# éø¾©MÚjV¤’ÅÁDØwñ9Ûü÷1üþH?·Mj¦b½ › 0Åcáš²^f ¬zEÿ9ÊÜïxçú²j„ž¢5O&Ó,1ÖûÂ×#Ow3-µMaàó á<±0ÏaüÕþ³>íûh€mò†‡ë.1C3]‹å¾FÀÉáCU…\Þ][wwu«Ý‚‰sžk‰WæxŠyÇïÏí[›†~ÏHÇS Œ_qAÀÕþCî€3>Û ÆìŠsoãç¿°¶ç¬|kÁ.OoLb ÏËõïh}ñ¼úŸp.@ñTêo_t¦¥ÌY·i¢’Fuÿ>Ÿã¯­__Õi.‡…·¾„Ãx·ÖUês#Ëú~xë?ø{Vº©Á¸MíGÍëñû\*;lÇòaf(s¢gꈤø™:#²Êë.`Ù~r &8I/ßç‡Çù'äC~x1vëݲóÅßgYòãÿYjužÞwaVù¦åW+ͰðãÄ#öáì¼qOpÿ\«ÛDç`r «ô¾o Ï?ÎI>m±9¤åñ‚â¹Q~.Ð’ß_×Ü…øŒ}Óåfh^hŸM5ÿ¶Ï¦„ã'µ|ù·95ñã°®W ç[KˆõÈ×G,yq㰚ʼ ~³šü:_Ï +”…‹ÊwD@Þ7Õºu¶éBÛq‡ƒ  ¿á!Îù}çŸ;òFá>'´ÇÅ–ü6g*†EÌëz­÷=¨üñب€ï¯QÏ<# H½Ýê®rº+PJLÌe^þzÀzŽ‚ÿþRø½f{!Ÿ1=õz×= ÃÇ}yÍ`. ¿;ºa>·rø¤¯Ùê…UŽñÃx4í¯Y?/ÿ§°^]:jº­øi'ÄU`ÜümžW&܃{Ìoü“S¡EÑ_†k "x_ K?®þn&‡>l? ´µ¯ò>È÷;…ÿÜ0Çy%5ƶcî}[– M…gŸ“y-‰€òO‡dû5 Õ?äñˆ<LæÑeùÖq<¿Ï_¼îÍÿ*+º_ýu®ÁøÑ_4݇r®QNlL…6ï×½;íÁÞC-‰–þ¼$$˜œØÑY¶KnÍ?éy-Æ‘-~®Z}ê>´tÉ8hŸ † oÆ<»½¹ó‘U­HÕ*¯ú»L¶÷~\aLOëüðâ¾Qû"쀯‹IÏëés;ÖŸì}¸²Ó§ä’Ö©àTtó­¾øùªž-¾õcS²Gáv†`ÒܲâùǸ‹ß_aÞâ(žoµ¤ÆïÚolT\£x¶]㻲h*lŸ’oˆò@ŠwyÛ¹¾ŒtTößÒåf0ùt…n[ÇI|<#|¼Pâ£*qõG8g#üùëeõ×Ëê?ÅËŠû³Ñûf@É0¹5Œ›H=X|$ÜDΗva~Vö?+G棚!aAØýƒ/ ‰ùúJØÒZ «GÊLä¬ c&rñ,ÆLôcªÔc܇19[Ú†±¥C3‘³¥eÌÙ̘f>Œ«dÜDÊ—V2^™q)_:£¢à3îǘgr‰Ï¸BÂëQ2¾4åõPU­„×CùÒÕ­|iêÕ¢d>ãYŒ×ãË ›rµ^”›hÈåÛâ ñm‘KøhÎÌÓŠz«%žV.¹<­¸×¸ãA(˜×8÷´òc^ªœ!cžVFæ¥Ê=­8cš3{´Ìk\ʃà^.Ük0ƒñ rsÔhâžVvÌkœ3{8cÚQâ7H=U|‚rÕþö꿽ZcóŸ×«mÙ}¡Œ[êEÏØjŽ˜ä¾¶gܺ2nõäln9ó¤Üê•¥CÙÿƒ_–™ùei%|[? BÊUã<oÆUã~×ÙŒ«¦c¾ƒÎŒo›-áÛÚ2¾­žqÕ8ßÖy¹f0n/cPª[2nUŒ ‘ÁØj”q›UQð¼Ö±ÂTH<¯•&„Š1nm˜÷ Ÿ„ A·YÕ­ŒÛ,™À¸Õ1¶š’y^SF¸Š1n9BÊV3¢°à5(3óØòAe1ÿWÎî¡þ¯~¬¸Høá®ŒeÉâÜ÷Ú—5 %󽿰Œmibþƒœ/Î9·œ áÇ|¯©ï÷„õF™%¾[YÌ67»ÇV·g¾×œ Á9·r‰÷õÇòe\ÊJÿHûô¿ëÑ´?ÓÞüïúò¿êǹ{1gáæîÁœƒK{î·×òËû«´·ò¾J{*í§´ÒJû'ï¼oò^ùOü[iäý1woä}ñ¿Ë¼ÍÝs÷@iÿ“ö>ξåýŽ÷9iãýíŸzïiôYrÞ-å©ïÖ‘yP›™µ6¿À馌îPÆ…ô–ø¦r¿~;æÓod¾Óœ5æË8cÜsÚɶ¤ÕoÚùMS¦˜7câJ9b”‡ëÌ5öÖ£Œ1Ä4ÌWÚ•±Ã4ŒIãʼ¥Ý˜·´#c}Pï>Γq.&¨œqÀ¹`tàÀ}LÕÌÔre ŒÏèÃ<ú(#[ËØØj”&´šñ)GÆ€rÄ>áƒ2£† ça;£ôÍ®ùE›Û1c옾LÂZ”cø2Þ5÷‡eü[‰÷=]…Óü§ý§ÙügŽÓìÙ÷¦ \GLnÆî’c’k%ì.ÎÀucû”¯¨a|EÊòaûj,„P” ‹Aƒ2£XZ浯Äâð“ðou.‰”ÛŹ$>ŒÛÅý¡i©˜?´=ó‡Ö²¢âü[;Æ¿50nçß:2oÓ,‰ç~6c…k×™±I²»‹2p³+ ÑÔ甲”h•„Mâ̸”MBYâ: ›„2p³«[¸Ù2Ê ZÅ<¢)cÜ™1p9›DÊî2¡±à½Q(9¾/*›y¡jY ^¨:æí*aŽ»1Æ"gŽsŸh-óEU1ŸhΧ>Ñö_TGÆX¤|'G ‡œsp9ŸDÇ|¢5TTJÍÆ•ÍüQsû÷Û16¹‘ñÉÝ$|ÎÁUPoTTvGƒ«e>þÎ Ôýó?éÕÿ/útîý¯úó¿ëÍÿÔ“6B?–öâÜ=8÷xŽ÷\Σý•÷VÞWÿ«ž*í§Ò>Ê{(÷|þ§¾ù_õÌ7¦û¯z%í“Z›ÿóùïú£ŒÅäüY=ãϺ0þ¬œy>g0Ïg¿üܯ€ÀwAéÓzçg Tï|Îl²gÞù&æ÷Ì$ÔïYË$ÜëYËëÙ‘y=›Q Æq`>Ï”5+Ç$öeüʘU3>“Œñ 0©5(&¶7ów¦\YÆ+¤<&7æï¬aþÎF”œ1G( Ž2c½1éÍ(7L|SM g–ø9KY#”*ñ¹§\%#ãšPr,’,ʤë€X(z”=‹ ã ºaÑQr,_TJ)a(qN·eh.0`Ì·Ù‡1`•ÌÞ²ÕÌÃÞžù×»067g R6·‚ŽY!*%¾Íœaç*ñ®×0fý¥÷ßñãßñ£ÍæøÑ}/3JŽÉíËØv Lr? ÛŽ³_5ÌŸ2š¼%Œ&_V.(=Ê‹Á•RbQø1_|‡NÂ} •ðE¤\;Îñe\;ã‹P®ž±š\÷ÕVÂ}µgÜW#ãÚqò1QÙo|Zxjƶ£ìW5cŒPö«Š±_mŸ[ÏøM”1¢CÙSÞ«„1¢fìWÊ¡|îP cDÇ ˜³_mûUÏØv”1¢«)ð¹ÕŒýÊ#R¶%Ç‚÷Ae¡XøZVü*Æy¢^ú®õF·67 £[ÃXORF·¢±Àü´k"0ºý$Œn=ct{£ÌÌWŸóžäF7ç¿rÎH(ct{£L(96_TJ‰ÍFËŽòüõíÛY½ñ)£[#áŒpþ«“–5'5óǧMJý·WÿíÕ6ÿ™½Ú‘ý=ÊÒsaüÊÒse,=Ê/Ñ2~‰^—_`t;£tF·+ÊÀ¤¾¨,Ê1ÁbEÙKXP”AªA™QJ ¿DIDZ~‰eKDz(=Ê‹ÇeBɱˆ¼Q(%]ëd Ê-öeìb—–eëq–± cA90)et;báy3¾ž ÊÄX&nŒuìȤ”åHÇ»(3ãšx£L(9ãî™P ,X_TJŽ…ëƒ2VÏÉÞsÀBöAe è¸eF)2OTXà~¹˜'”«¬FéQöt eð L©¥ ë©¨l” ›‚e‡Áe@ɰA¸J8¤” ¥À†¡Ee¡T”s3+§ÛeD906e (%£¥Àée †ò ô(ãÛüWÆéæRÊéVbòcœnJ‡²“°ú(ÇÄ eD9: œh#c‘úüíÕ{µÍf¯–³ÏM™Ñ Ln-Kp%J'a÷qf4e½g1Ö”„5EÙ§¶X®(Ê‹Á•…RaQèPÙ(g,ŽP /ZrÀBÑäâöe X8ZÆíSbéPvŒÛg`¼)WÆ‹¶“ð¢eŒmbÜ>΋V`±iYÁ©P:ÆBuaì>Z€.¨PVˆÎŒm‹é‚20þ”3*%ÃU£ô(;Æî£Ìhûj¿ž2£eX¸®Õf´­ÌÊŒ¶­!0£ ŒÝ§F…ÖX÷”ÝG™ÑXàÞ¹Ø}(¼/*¥ÄÂ÷CÙÒóMŒQe‡MÀ ¥GÙc3ÐHªÞŒU%e¨*±IèPöŒ£ª“pT Œ£êƒÊ@)$¼*6?”] +7Úž²ªPz”6”¥À&£Ee£TØlüP¶tÏ凲ůã‚2H¸ª”[åˆÈ•A×B$ÜhÊeë$p£u®Íiú‡¦2ïÍÒžL{±´—·¥çÒ~‹G주ÒžÉ{%í‘´ÿѾÆ{íQ$½hF®^³À&çšeîuʵ’±MÒvKúÀ!IÝ+X=»1&\6Ö§ ë2”ŠžmÁ/nd,Íl”ãfRöpÝ Á›àFÏbeÙ aΦÜ`Gz†„ŽIð†()[Ž+0?³è¾AmákļsÀ|óf¼H%{ñ„ÒõƃôÆ›g¦ó5z^o¢ eÆ<Éj)0Å)×Ññ);Ü—qÃÿ¾oÿ¾o56ÿyï[%û\”•«ÄäöCÙb‚«P¡(&º&Ÿ••KYãÙ(c;fÓyc;ÚѺFQr,_T6Ê‹"”†¥—pr (G,o” %Ç‚ñAe1Þ¸*›r±€BQöXD.vsܱ”À§œ\{ '×qrÍ(…„“«ÄbóCÙÒ½OT(c™3“€×ý´õ|ë{ßç¾Xë¿ô¾/Ù™Ììý?gΜÙ?YKŽW.F31áBÊQE%Ø”Y3>d´ RÇø¡¨B”;H(ª¨Cqn9ÏË£©È(·e‡æŠÊC¹£É„1£ñF)Q–ôÙ'J‰²DãñEéL8‘yŒñ(GÒû$^®7J‰²tæx¹ÑŒÙè \mÒŸ’^mêÓ¦{ÕxoþÏ|ÙÔ“ñ¼øï|˜÷_Þ{yÏýW~Ë{­©ÏòûwÞjúl“ú©©—ò>j꡼šzç¿òÍ’{×J>ãüü‘÷FÞÿ•þGûÖLý÷>…™Ñ÷x¿û¯xïsô<ñüZ_”޾C‰‘Gï+09¼194()z™%ÆDÉBI0Y,a¤( JŒ>&CÙ¡‡…0Nm}O‚ öÞ¨h”%&–/}7“KO÷Úb‚Ñg•ŒE[h¡™ph}1ñt58ŽwÊ‘ñg1 m{6 åÈØÝ!˜”…(9&¦¾ÇŸÍCy ÷¢$è;Y üYGLÚP”zM?5æø³EtýY‰a2ûPaBké{ ˜ÔY(=z‹;zJʃ>£D‰0ѽQ” ú‰?J‡’aòçQÑ=°è!Etÿ+ú‡eƒ!EièÞW:ÿ²çø²yt=‹¤¨ÇÙ¶ÄBñEiQþX0YÅÙÚèJ”7P4ÊÆ„%k‹Þ Ce¡±°²œ9žö?ó¸æq2³ÿ{ó8oö¹Eôú`r+Q–ô½0”e‹‰‚ÊBÙ•åÝ4éÝQ –üR”eƒE Ce¡$X VR”%ÂÂðAiQvX 2”刅"GéQ,˜PTQeŽÑ­dÅãÒ ÄXD¾–§ÛÑŠãtkPb,*J‡²Ãâ’£òPîXd Vh(%Ê NŠÒ l«[ƒ²´áxÝZ”%¢Jƒ²¡û9PY([,L”e‡ê‹Ò¡ÄX¨þ(-ʶÇñÖ5àXÞ2”e#ÆÒ¡lè¾FF¦·oc#×Û¥C9b‡¢òPîXèa¨"”|J„EïŠFÙ`ñû 4(1š@HKŽím‹f Gå¡ÜÑB™1x£¢Q"ÆøÖ lÑ(|çÛ #„q¾Ñ8¨"”ˆ‚™ˆ)ç[ŠÒ lÑTBP:”#š‹Uˆò@“Q¢Dh4RT4ʆ²¹QÑ(ÊçFe¡ìЀä¨BÆûE¡¼Ñ”(K4%)*eƒæäÒ hg>ÊÞ¦uJ¨WÿwçpçÇÿ•¹[IïýWs¸ÿÍù[IýïÎáJÎÝJzäu÷¯æoÿó¶’>÷wWrÞF}MŠÒÐãÆ$È¢ïS¡—ÒûSú&„åƒþ¥EÙbrèQî˜ "L”e‹ž‚’Ó¹ZE.i¼Q"ô&)Jƒ²Á$òGiQbL¦TÝ{Ëû<”;&WÊ›î'C/òAiéûP˜lYô](L8=J‚I—…’`âÙaâ… ô( úŽ‚>gCÏ)¢ï§bBæÑ÷S1)å¨Bº—½¦åŽ>£G9b²†¢$˜° ”#¯¡ŠP¡,-1}©èJŒ‰,£ó5”&tÝã€I]ˆòÆÄV¢,1¹¥(-JLçkô]SLöB*º–M4|¨0ùµtÿ+@}Ÿ”®—Ñ5VV >(:GCéè;¤Xz” D’`‘(PE(oô‚húî(%ÆÂñGeÑwFÑ$XDz” )¸çgr¶O–ßçÀïq ÷† ¶Í×ä¹}ç”f²«î]Tú~Ýï nÿ,ÿßuÀýž;»—ôç:b~Îò”/÷ºY¥Õ¼àû^ÔÛÝç—TŸ <Ï«JíüJË)ð-¸ŸYà Àñ:ò„>¤ýGþåx7^E:†T}UÔÞ*Þ{ZØ·Ï¥iÏõÍÀÏiroÓÉKê•Z ò6åúºîOÝÝzK‡Ôl QçbÝ}’TdØáWmŸ- ú1óý!øþ2Åúîc\®¿| ÈÚ¸®ÑlÉß,«ÇͶ¥€kÓ¯fÛ*veýÓTäޣȄ:ŸdBÿ(¾o(ß×”*Ÿð,´ÍH…íGg68+4æ«EŽÁ)P”¹ËjÕ”ž ^ñkiá=)ûxÑÏ ²<£gŸNõ椕)ÖO[†q ž?ž»4(ÖºYtßÐ7ÖV躽7žÿ×û¦ú­q…ŠY‰5rUdÐþ>ñ¿ë®¯º…п‡ë[Þ¸ïÁÆU·ÏöWƧBŸ3vË#åB#¯Aâ-£S Ãt—¾þnмv×JKñx—N_gvbq`‰þ¤_ß þppe²‘sÀóæø~ôs4ø9ƒÅ¯ª‡—¿n³üõø·\”àØ;|Ûöõ÷¨Œ[#,^¯"kNŒH´¿øGÿBîóÌ…þ2ÅúÁ`|Úý#´ß-¨°³Édñ­P¹Ì»ŽRà‰H´ot¦3üìÝ}N‘Š\±I\ëWÃØG‰ë£gì3ÈóÖLù‡fó%ªÁfñÚ­· l8=Ò0żÑÝ´)P§~Sᆞ:#Щ¹št?í›Ôg¨±¿*ßgd¬Ù¢q±•© ðýõ yŽñÇ~/½äQê-¨X]2a͘¾¹ÖíGRXÝØÀ̃›nZx©‰Åò7ö¹ýýFæ‘¥¡½ÇPi©]‹õß‘`ÜSo.ûóP k•Éàåx«Ã—ëTž ø¶´e59]ª_¯.A„çAò¿aû£q½ltànhXóF€è)0»×ÒðÉçëYK<<Ö W“¸ Ê9—»ÏëŠ9•­r^Wò‚ëgÕ øçÍ- ñe?jOD‹‹ítðé®f¸yÍ0¢èe;hž© ÏËu,•è¼èæÄ½^ÔBŸHþ{¶aþÛJB_0§;F'rý7—ëW¤ŸHçßÞä@fýçÍãó}Uìàgÿ¡»'VQ®oþŸ}qø¾I†üÅxÏýÚI¢Žß•Ño®DÔH±­d äzB\›³ ö”S“‰ÙÉN®6öÁáýñX ¿{«rýjôÏ&y|£StpF#Ï8s æ¬îùñw2Ë;wØ.b_ïá![]‹?ù'\?KÖǾ³ÐÏÞ¯Oò%ßG ß³=H“Çæô.ãŸÝ <7%É0¶é‘˜²;í¡QRÐZX§&û¿üžw-8Dôº7óyçJÅýã„Í û®ƒ~·>ZOÚ}.l®4!™Õ‡Î?Û5u¶šˆÇÎÕ²lÐ}¶KöÕýK½ao·bœL=~ÎÔ Gcâ,Ò€vçß:ï>TQ<ü2w\2èkÓ†…"(à³Õ{¨šŸ8i\ŸzAp!U-½kÖ£*춬ÅÔ+îà]/õÇŒ*\?$³§|Ÿß48¢ú¼ø|Ÿû0`yÕ›Ç$C™Cá“ú»™ÃëK‹öS“)µ{׺`'š ˜8ê™§(‡¿Þ#¿ëÎå)ÆKüqdîíNið©èÙòz÷az… –0*äw¯\¼µÓzWh:áá 5ii¾öìF0^/þ¼d׌Œ{h ?f–g½qfý«ÚqyŒñož;¹¥Áw'Õ oïA—SþڮÓ…zùönWÍO}Õ¤tU ñ2ÛÀ?ø\_ÛrB_6¾º!1¾upk…Çà4HÔ”j;öê=¨¸Ïª_ÙÁɰ˼Î&—×öЪk‹Ï:¬ãi†Ž%û†=e}L9.€ ãþùð4x>bLºé˜e%îXï’ ¿Ÿ}v¡—3ã©IÙE‰GêN0ÖïëK÷äÚàø5 Œ7¨eäÝØ4X:µ¯lϨ{°çJ㈡vɬ±<Íè6¯«šLœ>8¡«‘Z5výÐýÓ+ ýM¹ŒëåëßÓuFTÑ”w i} ¸;›dÖo·Œ}¬-³ÅMM:Ÿ-•%þч}{aËcWË÷3./ÜæX¬Žõàݰ5> Ó iÀôŸmKÝƒî ªš?üž¯çžuôü l~í¯#Ôd;YÚg£y`‰þ…/…ùˆ!oŸåK¢_|Mï½( î„Rp@6ôXÕÚmWN´+sV$ö€f†DW“ç —o;Þ3ð¾¸üùà¸~Ž\þbÜz¢„ÒÍ}ÓàÑn]SÕÑl¨Ñúì•$ˆ·{}˜:ÖÞÚ/ý½HM¸yC`‰~ãOK…\ZR£¶‘[ÀóÜLçüœ)ªF횃Z¯–´›— öîʾ‡’àå‡!½Ç˜õƒ sŸÆ³üÕ$­ôÙ3S÷þ1^Vy³ Ñ÷øR¾ãWvä ^Ϥ,]Ê/I6|x|}D÷uIph¶üèo‰+Lµ¦ät5éÔgøŠÚÿ?y¼!1^§j/êLMð_-¦š/ÛìC“Ä!N¿zÀ©«ØÏW§˜ãXadáГM¶ÜüéœúhÔë í›ãZ(0Þà²ó ’¦A¿ ¿ë=Ï‚®[}odÌNŽ“ÛOk¸'@M Ãå#7”Ÿ¯ó}ÝLóLƒq½OUòß=! oؽ,* ÊæÎyã5% ÞÌvíÙí¯Û.nåÅ@5±þ«Tÿˆ‰ÆóÚ¨yrëòBÿ[ÓúÐcÜýcß›–GkQNLoZ´ãÊè$h¿éÆ¡0sÅcnËÕäÙÊ÷‹ß†•ìSûÝ9¯Þ®d ¿*мޒõk¾´ ÑÏWæpý®ÍžçKžÔ¦ «Ó véíïÊ‚Wþº²g`<È­>³¢'pü85q)x”äjeìŸÌ÷£ã9*¦œ 1Æ]7Œ’ñÒàW‹•_6ÕÉ‚è)~Éí»&ÁTÕ¸áóÇ»Ã註~ V ý Kæ7/üfÈ ƛӹ`ÃÛõiPu²Ó¹où™Ð·ÌZ‡eõð87ËüÚŒö`<'5ùú9¯ò—@ÂóbM9§RŒSë(ÛÝûÒ`»ýÉC•5™°mc…ѯ>'Bß‚˜÷öyÅöêÞf8Ÿé=îîõÒã瓯7®¯%§Ëå›çb|éé¹ÒùGÒg8ÍILòHJ„²ó×6©ê:¸ë©&ëdµºúX&äÉq‚ã¿:ÀôîÇß$¶ã|Bñë6Ž=œÉœO;ÌÈßa+boH¿åvÝfì–VÝUo1Žÿie×y_7ò•smNÜ(\^žñvÿ‹«Á¸et¶c¿íJƒ²7§äÜ‘dB·ãŽÞ}%BŸß_\ð„ø„ŸUÜ07‰wLìóDF¸ñ± ä6–-3ï ´ªß7îÈå-Ƴïñ9Ê{]<¦eX=BZvYyH¢Àñnš±xÅ 5 -:Öñ‹Lè×»uO›£ê ¬/iŒ3Ïý߬á¥JÛ¹~Åf/ò%OÁ»Ï¯Lƒ¶çrçž}r.Å«ŸwHdÈõ­–`ü¿ò,­}ÒÀkþ W‡–ß…Ž,¹Q;n:˜‘ܹ 4_ÿ}ó€582¿?øF‘ŒŒþ]4æÂPsÆ_èãð`|œ„ß~Ø? òu/jÞ…w¢ZÕ¯cOlÙ™x¿|s3ÿVÉIM”ywg.ñ uÁõ37öÕçÏG1þ/Æ/¼~ºV¯®i§™lñ4êeõåg©ƒ- ò:Â×à•Vxÿ—}îʃ­õdôO·y?©E­Xc?yS®ãwq²µukBÖݣ΀s™-š|—>Ö·q†þÛ¯Û×^M*_WÛ¡³ìy,}ï¿êãâ6/Æ…Õc|ëÁ~à|ÕcSi׫2 z“þϽsñø›äÊú@£÷'¶U“Ÿ9ºíûWÈ„¾£üçT¼SÁ!vïá:ò8/_ÒtwF÷œ*èGĞØMÊ€`'‡ï¯&À`Ãr“¬Òw¿¬¹št¬;yë–¨?ý‚»ßäøbŒ7Z·t’È, šîܲ«s<˜Öß"z<=ñÆ{–'Lá$m`Ì~žÃs)8~Fàyˆ†|Ƹ¶ËÎW©òYýŸ½(—mÔ.]:/J€E{¤ÛäG¼ ¯ÿÜ•íêà}ਪ㛯3ú?nòùÁͯÛ[בbüµÖtxùVc ÿà\èWé}•þ 0ZÝêÚÀÞÃàî°ÀQxÜÜý´ŒòoÀãÓ!{òtpÞúbÿǧï@§J.  fjÑC¶ƒƒ .k©&´[þ¶¡ÆëÎóì•®r¨_J;¡/®!_1®r„·/èu°SuhïÏÀ;àöÁ¬æ×OZØ­ª¨½é rŸÒnmðxvËÆ7é+ø,»ß7 «s¥"ãé´cuÆú€cÜ®´³¿Žñ–î@õ»Ëú¿MÓ‚[ij‡þ!pîxÀÍ–ÝÔäÅGƒñ<êß}ãÓ¶¼à üú’!?1ní Vk]’t]+Z–ëx´>é7í£´0ë»OÐûþ *sëÊšþj²öÍÈLŸ®Æû2_ržD9-‰ÓAƒ_{N©z?éµG gä>(ì 3lœwU“†v¿2a])óË#q£D–GÜqŠ1ÞÙÍ5~t¹®òfድ/ÒaKäÉз«µ0çH©Ëå¬]…õµ¤ý¯~±Rð1~^ǯ§qóZnÝN‚që^rýtW£ƒùðQ:4}°zß™ùZèjÅõ¼™ô›UVM¼»žÕúÅ®Öcùûþ~øIƒ ¿Zós)ÆëKA&:8öq@û;ÓáÇÁÊ·ÓÂÇNRùÇén3…~#òÍEð{¥À;\å¯Sžp¼Æ Øöðð¶c:ÈîõÝûêÜtø´Û{õü>ZÖï¹?ôYxëbï$©OqÅž2Âó\øúá¹1\ß}æ¯÷öðf[–Ëu¸„ÞÓ¡ìÏVSbmµP!}}ç û‡0® J¨{>¯x4Ïý1å¡h0î‹ßÃúlZ¨ƒÚ3}/4K‡áßg­-¥å³³;;ë{óÛÅ«ÈO³ Ót2Â÷ çꀛ¿é1Ç•ÔÁ•n7e¯>݆ð_g̫ܹ WÞýþ¨œ0ª/¾œ07ME>R\ÝÙ÷6ô‘Âo§d:ÿ6ËÏ—¼l9?$º«ä‡Ssz]» ’èAGn‚8k@ím#½A1s¼¤}ŽŠT÷oòAkÌ+ž_Ìçïÿ†|Ÿ¿G~xÒ¾­*Tj1ýWèmHùò÷Ô7!§Ò¼!·}FÀK ¶T‘ó«_›8i…p^mG†Þ*3ÝLÈ+~½Ü”,Áø×ÏÎ}ÙÏËAÛjϽ =îqÈésj¹½¶»¬Ô~÷jÊ}y¸aOäÖÍË…ø7¤Òã.7güjŒÿ£îÙ «êÀwܾ¢}nƒîÑÌڛ܄, ¬ ›ÿj’®"m鲿½åøcF~xxésŽpÎwäê3.1îªôÄ-è°ÌuÀÚ¦·axÍΕ¿ ¯\®´šp¡/t¯]¯YdªŠ\û¼­ƒsò aœáý†ã‰8 뎆<ƸIÊvy¡¿?êô ò-w*¸†´žöö\üòÒyk7ÈØulÿ¬ˆƒ¿¢žß\)ðJJÎùã¾WÖf'<äüGƒñ#S;ˆ—^¹•lï¤÷ÑãýÉÈÐVWî߀E×oLȈwÃoÊÈï]W‘ -Üg-®jùù׿¿3p÷ÛŒ'‚qw»Ùž:ŽÇíÐ~вsi@ÚFMZ‘pêìù!mÕÇ…õUWixÙõq»WþÁ™Ü1­C‡åØzJçbëqfù’ZsmJËnA· ·‚›¯MƒŸÖïî~½pæ¾+wáØ 6ÏvXp÷D½m*’Ÿ8- AÎra>ïë¨MŽöÅç½·ãÚ%WOt½÷g×ÚSÐ) .½³íÀ¦00®Ò¿~ ËØÒd Š u·_½\8¿#Ÿ‹6ÿ)Ô÷Ž,ÃDËcŒ´¨xÕ¹„be¬ÒØs±ð¿½•½ÈZ–©pv¡ñxùù#÷œñ­³üM‡Ã¿®¿w6]ß–aÜi­®7y÷>z½èì·6_»JMk9rРȲ~q—=Ca÷ÅÖ—¤¾*Âå»ñxùçb¤È™¯CSn‰ã?öw.“Ÿ’ ê{?H¾VŽ&MiqFhKI%xÂTÃ*ÑU‰ŽµZ!äÜ¿DIú–ƒoÒÚu/f·~]ÇÏßpÛv4ö·zØÑúœ÷*½üއÑk¬¶Á >nXžpc•Š˜EL¾Ù[¼BXϸv}ɯª•Ì„zá¹3¦\=ÆïyP»íèÌTHŠ¡` [÷Їs{ˆ§u“at)¿Ô¹Št}/™¼¥ù !Oj˜© o8.ŒÙ«|Iêújã_Û¦Bãçïô‡ó*ÒáâüÓñpñlkçÁ=aô×Ï=wªÇ¡1ž~=˜?¿/Œ»/cÜ™‹Óò²©ðjmD%pÔÖ©tSX}Î9ï­d=O¢ ^i’3Ù«ö £rãb)ð¿øLÔøç¦†<Ƹ¹õk‡§üNçô1[]Œ´{×gÈ´xhÐeËîJcAAwbÛs¥Šx{]ÙtôrÂùÏa~Ï=‡f>Œñ† :.Ÿ›íš™V¿ÿê)Gˆó%ãôaIî`5"5·ölYÝâã·mEË„ïnܵ­Æ}çÖÃ0NÓ˜k>ÞO0€—oÁÙ•^ï:ÅÆmývƒšß>VEúö[b3jÿ2a^ÊÇã¸SüsÄ–Å¸Ó Œox¼®JÍÙ°Áó×Ü‚• ¯¯iÏÖ/zCÇG»¾xªHoœ­Ômc¯Ôc{o"ýê1Mt³ÍR–§×þÉìc™SS`¤‡Íöépܘz¼j´y<ÈGžÞµ#§ŒZõeXŒÎ#Ž—ßs+f©GA!ßml8ΡãpÜߨáù²Å¾©0;ìJÛŸ…×áÞ{™Ï¾ýÁp{€qn ‰Y;jÏRáø¸õÀ²ÀÏïL9ïf¯ó%å àòdS<Þ¦T(|_ûÓ¹ôëòjÐ탡YÓÛ]~¹ªÈs8ú0¶Ê²>ðRXã×øõ!~_‚!_ñs®>í½åÜ_É`Àxº¥Âså÷MTסL*¶’˜¡ð½¡ô̽Î*ð²yÑée„÷î~š«' Æá8ɰÞÞ”N…«v§†û®½­Îff¿½ üÇkû+5Q‘a!}?{¹LÈ{îyBiÆçê ä¿!O1n ÅZ壗Iðvî¤À´&˶Ÿ{F@—„ÍEà "ó£BQ]E&¥÷k$ïo<×N—¯õ(§pž^ìùoWÎg1.w=’ r×q‘ï’h‘º÷Î#ûël\sÓþt·R‘§^>óq éyÚ}çƒe!ù|úÝixæÖ\ŸénÏå)ÆÛéÕT’êéö¼G ÜÏ—t¨|z'oév|ÛøàœTKEr‚Ž]”í |χœZ­ã_®,Ëöutžòã?œÓ·JVF"ÅboŸ’yÓ5â—éɰ¨n@|QF,¯[¡Ì²nppþ€š_ªÈÛßO‚Íë®üónþ×zF—®øé#[Ï}“/šQvÈ—R‰0BU4p±dp›Ÿ÷D=cÆÉdp ªÛrtSáêg‰0.9Nrχ¹¸bŒÛ°oߪ½ÂàUš9Uý“DÞW5>PV9&Ñ«´uQÕèÔVE6-Øë™¼TðC§%>×ç­¾,̯MW‚qŸíËÂ;¡x)kúê~ßdpr«båp"”£;<>ÑÎ&Ò7*2þÎÌÈ¥ÂuäÏóÂõ+i>UظôCÝæÝàÜPÑSë>Üs;)ÆßÖ­ÔÀàÊ àҳ娘ZÉоã¬íŸöÆÁZçëáCÎ:À“uë̯¦"3þòr9q áya"+l{ü¤PjÌ•[×”a¼¿ÕÎÞ7´P¹]YHð^ °)Ž­wõœT7L¿§$=®þ*È9 Ô/7/ þÀÍOzAìÑ£ú ãâ+0þ‰Æ5nÔ ÒBЖ]Uš$Ø}‰iâ ¹ê÷ŽÂ‡}@™DÁ¨Jòªã´å‰·@$ŒãFhÇÔöê^ìy«ã}´=æko-¬·§ IIÐ[1ìj™qqÐîs˜ù«¥ƒàI½E ÇlT’zéÕvúK„çKüùæÇ ~æŸK›Žzüœš– ¬ZQ ¶•;Wì;5 žNÿÞ#vVýÒ'Õ”³ï¾>÷¤’ì2ûáyÊ5@È?žÿ\fÇ»·Ûµ)ÆY5{›/™yþýÖß 7!0@»½jÏ$øXîÉN·Zqð%¯ò—-£=átýø®ùyJr±Ýý²|Á—ø}\e¢\¶¼yÑ©8Wã6mååPkåM0`qí“` }¬ýæ³T*¼ð¼ÙJE&¶¯®¬¿ÁŸ”|ŽÇŸ÷¿fƶêSl]F‚ñ÷ûŒhô¬ÅMÐÜŠö7M‚×Òé«7]»û<›^¼Vs8l»™þ¾ÌO%«›¸óÆ+þOn¾g -Ë»w l<N÷TzžîÆå7Æo8`ÝæÁÚððȯ%«Ë%ÁÎyE}Gí¼mz´ßa0‚. LQ‘žÓnÏo ð“'¥hÅ‹{ û* ùñ~GNš|ÎÙôðýy"Üþ¹/èǬk°ó™<`ípOP K×®".)òVëFã4¿îÃ?o1å?)0®ªËAþæ7`eH嚃n'BÅÞ‰—6»_ƒÕ³ªv¨>ÕÞ§²ÖAEÙ,ÚT*àN,wßhœgòãî¥ØÞ}ñP#ñZfæÅD­pp߈Ö×àÔ·þ=gWìã W%9›ðhÛ ¦þÂ8Øã”Ƕ[ ¹yÆùzeàÊÏMâaÏ›ECÇïL„ÿN U¿º)ñIíà}§KÂÓ;©JBWWœýî¾þ“³éz‰Ya¾¤Óçfu«ž¿gÍf‡œ–¢V»‡ü}Ï~;9Pã 8¸Ý›uZI¾’&˜y‹ÿXÏeûµ€óù6ß“w%Æø /pÜœú>¥‹C"dxOktüåUø¬~}¶scw/;ðÃ&Ï…M ß'Ã[<^–Ö#¸õ£ÂçòãÊ  ­×áá Æ×ˆ(.Oqš9[w¢R÷—ë]f œî^îs¨’TTŸoÙ`ñëôü~*þ:ñ¼IC¾büÇ‹ÓÛv¿K^=ûQÌ|u¡îKÕUÚpëL¯wpï׎Í)ÉÄ»ëíóY,ø}hóì†si¡Þøñš¿ÙýÆïƒ³«¡Kã Ë™ê¥c®'@åsûGo¼ ^׿I¡'0šQJ²_Fü/|‚ÞÈûózýÊÖÙªFì>Ÿã *0¾èTïÈò¶q0ÊZq|癘µöŸ¯BëYå£Å£‡ƒU×åNã1þÛËÎ:N2ú?®òã¡!1Þ쌛šu ú¥®u:»9|ÒgH:]…†±SZ¥VÇ•Ìçý ·O{>®ÇßO\?@¹åxÕݲbvÄlë6¦üUœ¼£íëöÃ!t:Ý!¦$æô±¯•¿ðŒ[Ÿk<7Ñ¿ïò%ç~h†\ƒy›Uë‘Û·Ÿ:²û†íC å ÀF%y8\ªÍ÷öáffËWNµl_l–ã)G¶«»ÿÃUðyžÐ¹œ}Ô+£Î¼¤í„õŽEõÁÉ×¶)g”d³GÇ£ë/ÖsæÖx÷¢Ç‘—`œ¿Äš%Ÿ½ uû½v;[?ÎÇ(ý˜×÷S‘; èø àh,Ž+›]sh‘05Ûõ¹¬Œ0îòûzŠí¿Áø¡§ã-ÊUXÓy‡Û„Z Ðfþî§[Öj@äúäi¶Ä ¶‡¶º¡$½ 7ª~B^r÷e^07ß³æòãŠÇe¶Ïª|.,kÚÌÉ"Ê7‘캸Hã#ò‡Œâò/§v˜%‘:Ñ#Z,ä ·¡ —w'÷YãØjQèÚkÞͽ?´ ß2mà㉰v(=`á;ˆ 9?)?BIú¼øQýfàbÂÕ_6>sÇ£Á8›Ô+:v«á“ÌgNz¥Ms«ƒ50vofã¨ë®àI·š(É‚‹mšwih<~Ÿ ¿.ez§Ç¸qAßKUµÐÀÖȲC'fjáŠÎºÎâ®°¸u|E×*}ÀªsÚ‹#”DÓ˜¬©ì'Œsüxį«5­êZsúcnnö>_r~Šfd¿VÚßX“¤…iârƒiÀc³ööãýà[ó«(IÙç<Ë.® ÏnÞ-©‹ŠßÄÏã¤õ”¹- ¬N1«5ãÝ[ò¨%”Õ@™Óص—̯5aÝüéJb3dò™C …|l. {žÒ£,{îhWlU‚q9n|,\ѧﻬÅùSýìã×dn˜Özz(DL±¯wkš’¦WÙ ÿ¸äù üsÓbûj0~ö5ê~è ·õçÏ^ÖÂÀ˜i¥óçˆó\!’ÆxÁ«ékHó™JRyýœ™-Æe¾¸çP…õ ~~oº_^†ŸóóòI÷Õª+Я…õ€ÎQZø}DökC=‚õj·ruò0h1i̼yJk·»•ÍŽ…„[ŸùêÌïËr3Üøry‹ñ®W¤™îL]~ß1-¬ÌYñur|,Ük·brp©aЭüäÈÅ+•äà¢;Ë8-$éO†ÎmmÜgÎß7™®Ïh0îfßQ…‡#/Ãã©…è–º> cÁ5fE¥ï=áÄûßy{•ä<}m f¡0òûµw×ô(3¡´µ0žð÷SÜøÅÖ¿ðsž½Í2Éå2X6jaß®€ ZÆ·Ïdtsz{#èš’·/;¾[$\W.+ û¿MŸ·›}È—„cÕß¹u U± µÐ¦×վϮ@¯>R.|t—;Ú7kœ] Ï=æÖc%ßfln½ˆŒ‹ÐlŒ¶Iô-Xok¾}áª+PsÈœ.ªº=ç®·Òx1¸î¢?x™E3GlL|ÿÍ™ÎlzüRŒŸ²™œ |:¹_õÔîÐB—uýN»>ÆMÚãêQcºt{qBIR"–¶¶®¶P8?Ö™îróéѡغ¿ãî¸Ðd_ÍËÑТfѺ9W´ð0§lðÒŠWàcㄉCnö…ý½GµšÛGIÚ\ü´§Aõ…„×j°:l_̧ô¯Û­¬]³zDC¦¢ý•Ÿ1Zðé5ÒólÊex2qx×_ÆÀ´ÉòK"O%ùt?3pAoãyåü š[jjùê‰3½8n7÷^ŒÙÇ|ÉÉŽ_ä…W.€KTAîºëZðظkŽõªËðÆ©TÌô,/Hk|ÿëŠJò«*¹Ù"ÌûvÄèAž5&bçYãÌóœ¹ú¯Æå7~Nˆ5½Ã¹{½:Ýý•ª…_ çeÛ·º Ž+.}[}[§Pn£¢Ÿ0àöæ9óßÃÏoÆ—p³‡³ÏƒÆåy¢âžš ~=Í"õÈâ¾}úš; ²\§¶óÅq¦×ãn7jç\œ·†8RŒ3êbÐìëÛÎÁ²;ã#•z-¤ìœ^É%˜r¬o˼ÏCáŰý¥—*IæLúB…qqassÛÍÑ¥„ëeºF†q&áÛYh^稾åS-Ø?ת]ÇKp®½Û¸¥•< î=³Y’`%áêÕ8ÿÙt%%m÷ŒÎü~„bÏ'0®Ãã„9S?FAåµKÛ,ÏÓBû*þ§6~¿×DWt×ÜùŸ&mS’µ6#ÆÔ3ú·Ÿ…›Ÿh0Îññs»ýVƒ²ô² O´Ðrî–ø>è§O|?–pchÓñæGqÞm¸^Dø}TüuæïL}^qÝ·ŽÑ[¨Áku0-dÕëpýû¥‹PcIÔå [:ÃŽJ;V¶T’SOæXžYD8Þógg~¿w¼ÜþO³Où’%ëê5ž3Hqé­§Ô~¬…ÝöI_¯„]„¿?Ì|íݪ¬úÚz2Σd·=ŽÈ'}×wøû«¸_‹Ý¯„‡¾ c:¿ÔBﻋÊHw]„ýv—_ïocQÛÛ´8„óÿÕŸNk²˜x×|çò0è—gøûGV>‘œ Ü:§®öªP­óš‹P*úHß‹:À°¯¶ˆ6*‰SÕ7©÷f/|™>Æ=·¸*ìSÊHUÑý;ã8cü}W¶ŸÚ?¾oŸ¤Žþ­…õÖ»ÇUXpöõþö«VWXÒõUì$¬‹iËÆ9uÛ_¿~Ïöë|êûüÅó?§WÕ¬çòÓ`÷1$mA¥h¸vm­aÙ|Ç^ßö˜ª$Qtº²H¸Oü6+}SZ!·O]q,_<*{<ý„z\}TÎ*v;8åîiuQxo EÑéz£”¤kkz$ …¼æüè{~_¸}h׸û*Œëw­¿o~™“àtwƒÅ|œÏûêÆ-ïñ1v?ŸÖ©Ìñ ¹f'M¦$M—ïi4l¡°Ÿž?Ίú«§2Ô–Àýÿ×ÙùqaûN¸}“züœ›Öwod™Ÿî}¼XÙÜ¢úµ³1Pc}ðè'Yƒá9¶S’k?jØ(¿HxÃ7/¨g·]ò> ÛÏÜ›Ë×Ïù’ÈQ» ûÿ< S 7Æ `¿ùÇUÍ‚=ygèµÜÁ@Ú¼¾3fŽ’(:ݱmº{‘°n7ø±Íœ)íž ëtܸXƒóOŒûš>æùrvg¿èØ1zÜzwÝ. Ó ºŒ#¯$[οkѪ®áÏw= •WÞ öÞH+ðþµl´Ò‹›§J0î0rûka›C`¾&÷ñ#§?·þúø7Ñ0ÃåhaþT¸Ô§ËˆŒ[uùâÖýúnýêlÛRivÜ¢ ÛWËíë“b¼`‹§ÙÏF„W?íŽrN€ í¿ë¤ÑP^.wˆT †{ m/¼F?×h³¾w¢Ÿ0_çŸð뉦ûewÎÕ¡c5Iû wù˜õvn 0cóüõ^ ¢!å׳ßß6öúVQ \Inµ1÷ðLòò£U«6¥ÏT¬ÂÞÛ¼.ì»0Ýg¬ÀøW|_\so¿^fÞü{d\Ûòù“½$Zx/É0mT ¯ºšòüþÈ¿œßç\RQx¾Îí¯eó_Œÿi:ݱ³‹íëK€Ÿf£N¯ æ[oßÌüêWã×Õgøýº†¼ÅßçÆßå–V¦áªx4¿¼¼ ð{Éf9GT®ñDIzxœèqÑÊOxÏ‘?NnHÝbëSf_ò%‡‚â*5º \”§Æ­8‘½ÏºÐ'ñD ›·{mVoøõ>aÝ£|ÿ ûÃŒëG­FUO«8ÍZ¨7~=Áô~BŒñ¥ ª0¸ún¬àÓüM…§·]€N +’¯ø †¶:¹¤(ɽz/º<@øù ?o3]—”`¼|—Ó¿Dqr˜âUûÆq"”ùòþé‚tv3m>zó{"¥Ã›_€p¸÷m,„<0ä+ÆÛZn€þîƒ 0½^þâ 3aÐêôº·çA>o©£Ã˜Q`/Ÿ¶kîr¥°žÌ×ÕvÑ Ýßý†ŽaºB†ñcï_ž74t l¾N7˜'BÙ.Z´Xy¾ì H)ÿq,”2«ÝÍOI¸çþ‚ðŠŽ™:²çOö~v¼3ÿ|Òôýf~·ï7¸ºJëðOj–9ýû®=aï8x¯ô˜¹g1Þ'ã¤Lçê/Ô·þñžóaŒÓªA­ŒÆ‡ýa\v¹U3ß$BݧO;}[s|û<ù¬ëX¨šC–(IuQ÷øS=ý ç/_œ/¡FÛ®˜¿è1žÍ«¿f¯·Ÿ Ò—ËüwŠ“àqÜ–Ó>6ç@4<#ém§‘PïܵWþB1ø½îÙÅÔÿ0ž«Ý®³jÈ©Ø{ºfEè;{7´Š‹î‰tÛ»sÔéuÿð|ì'Y€Ö žŸ¹X%c;ú£ËÈ¢o£ ×QxŽöxq»gj€ß·žczôfû¨ØûjV³…»®B²wìܲdpÔ¥¯øž…¾u6ï7®>¯×£Ú%Ù¸oµÓëÂÅBýqã}U¶´7ÐYééÚ}á¢÷óO¯§z=ý_êõD{˜G]0ãúÚŘq?ô\jÌŒ}î(ç€r§å&=S³ËñZ Y/O¾”„õÎ.b}Tä¬_¯· ¿Õ‡õó™ôó´d\ëçÉ÷Ï.É¥¡üiãOËYÊ;5ébÒo…çOKMzåù°^P´§§ëéÉ÷Ðc=´=›†¨;cš±žž|/(¾§'íÂ8_vŒw g}Yxþ4í9¬0éÍBy<šöÕËsüiÚ[¯°ÇŸe}Zx®¡¨)×cOcÒ³EkÂ5Ô›pi M¸†yŒ&g=\M=š÷ç¿óà’¾K=–úkI_ý<•úiI•šqÞYÒ3©_–äÌšöï£þXÒ©/–ô¿óÀ¿ó¾ÿÈëxo3õ5êiÔǨ‡™úïYôÚx°ÙǤç(å´d1v¶?*‹ñ ìJ°YL™Ù”y(A e=û(KK¡ÿøXp}ûhŸeÚcYT•cÂò½CõŒEÙ¯YŒ‚ÒSQæ+&L(ã[{`Ò„Õ1öt·d=Ý5¬§»?ã½ò½?)ߊr_Ŭw»Žõú”3žU(ëñI“̇1¬ü—‚gWù°Þ춬7;íçéÏX¯”UEYRÖ‡ò¨¥Œ'¨cAžwBûvòÜiãÒ^|Ö/9ÏãL2δ1ͳ¦·•ò¥´Œ-õÏüêŸù•Ììßc~eÃÎ…eK{³£”áª`É/EE—áx6ÞŒ H‹Áƒ±)ëÚƒõM¦¼)oÖ3žï?Êó)ï†ç]û¢tŒ9åo»æ{'—d`P–«ž±\¨"”;Y˜ #0U„ò6a¹ú¢4(Ú£”1EŒƒmÒ?9šõO–2íŸì͸S´²/cR.¶ ¥G9Òþ¥u9&¤×Ë´åaÂr¥½L•(µ7JÁŠ›²\¨"1Çr cÅNY®a(‘ sʋߗ±\Åh2T– sв\y†™ sªˆ1€Ì(¤( JŒ†áËÔ8(#”q§(#”q‚('PÎXAr(*ïoØ Ʋ§ÌÍ0V`(%cz`±)XÁIM˜›”e¯E‰±eÕ9Ž %c$kP6X>Œ!fÜMʰ±áxö”fÃÉYŒ‘‚ÊCI°pŒ½ãެ@Q®½ sSÚ€cÙ[bQKQa ÌÍ0”cn*q,{Êܤ,{KeÙû3æ¦-š@H3ŽeÏ3À(s“gGˆL`Ô («' %B£ðAiQ¶ô^“1EŒ¡`Bù Æí‘ ™„¶ãø=<'YdÇq•Œáã‹Ò¡ìÐhä¬7¼ 'Ô„å£dæãÎEŒ½I™öyŒµHYÉZ”#Ç÷Ñ1¾Ï^¤¬äPÆ‘àYÉ…Œ¿fµc<0oÖ÷çñÏÇþ]<ý?ÿ÷ôs;‹&º”±)ˆ2Yu(GL|9JÏXŽ”œÇØÉ!¨<”#DãhHJp€BKð“i±¸£ÂP",o”’²\±xüQÑ(K,"”%¦<4Æ»çy¯:ÆògLGÆPÖ¢l±Øä&¬4Ê‚-d, =Ê PŽÊC9b!ÊQy(wÆ»Ïc Ê»ÏCI°@¬H½QJ”%«/J‡²1áÙÖçXjzƺ§ŒÛaƒæäƒÒ lпe(c€˜ò;|M¸mþŒÝÆó@þ™Ÿÿãç2³?wdßEOÿŽ /Gå•âØ¼J”%&¿/J‹QãXZ–+β¤\7 ãºù ”(,ÿ|¤,sŽÉ-CéÛ-¤Çç¦|KªåŽÅ¤`åͽ…ŒÑ«D‰°À¼QÑŒyéņaÁù˜0ze(Ê–®-0þ¥M Ž©E‰± }QZ”-ãôjQb,PÆw3&¦ž11å¨B”;nã,y`‡±"–š0z}P” µJÙÐÈèU¢D8Fo4Ê’1z£Q6&l7[,~côÚ¡ ÈQy&l7jR”†1zy¶›¨%ÇdR¢,Ñ(|Q:”†Œñ4-Ñ8¤¨0Æw“¢Â£ÉÍDÑŽc5Q¾fÊÒŽclF£Ä”­‰ÊB9¢Ñ„¢ Qîh8 ~S4JD¹n¨0fB”Õ«@¢ìx:ÊÑ‘c:e•`:Q§UdÂâ,b¼^%3/Æë¥œ7)JiÂy£yNL½œú8õnêÉÔkM× ©_RŸ¤þH=úŸ)[ú¿H=LÊüÈÔ‡æ1Ïù;Ÿ b¾Á{†ÜÄ#Lýá€ÙŸÏIO›ýÉ0£5-Aù0.£ ÖnViŽÃ¨Ç/MNÄX#:”cŒy`=Òy æ¼;æw*šîm`ŒWÊ&tlÈ1]Ý1ÿ¼1ï”(Ì;[Ì·Pú¼óK‚yŠ*jűZ)Ÿ•òe˜'yvÐO¬ʆñ›õô¹"½Oce4‹ðZ‹»s¼.ÊWõgœ-Êëúg\þg\–™ý{ŒËîìXóPLøPTa)޵²Áä÷GéP–X¾ŒShS®8«Ð¥E‰±8|© ÄX$2”eG™…(½ o:åˆÅ#¯Äq§)Ç0 U„òÀb C‰° ¤Œ¹ZT…c®F£,±À¤( +4)J‰²Ä‚ó5a®†Ð}(;,@9cŠkpœCʦ¶Å‚ôGéPvŒ»JÕ¶X ¾( Ǫ¦ÜÃ<Æ=¤¬ê"”®’¯7J‰aû˜0W}QZ”‹ÚÝÐÈ\FY2æªeØ«”‹^†ÊBÙañ‡0æª#š@(ªåf dÌU”–1W}QZ”%Ý‹…ŠFÙ Qø£²PŽh!Œ—H9×>(%Ê Ä¥D¡<ÐLÂPEŒ¡¨DÙ0Ž"e_ÛRŽ"J’ Ñ(PE”ÁІ†*By£ñhP–h>Þ(%JÔ…c¯†¡ŠPŽF&¶)„±ÝѠ˜IQÖb3+ž·HM‹òW£Q"4/oÆ_£‰ù ¢Qô0p9NxOÿW~Î{ù…ÁJ½›÷l¼”‚_ÿWÿ+ŸþWý_Yÿâ=ù¿âÇÿÊ‹M}˜z0ï½ÿÊwÿ»žkêµ¼ÏòþÊ{ëÿ¦§ò^Êû(ïŸÿ™ošz&ï—%½Òƒý&Q4JŒ‰$CéQôÇP”#&–œ “«%ÇË£ëP˜dy(wÇ .BycÂy *默x”“Oƒ²¡ûÁP:”­ ËU†ÊBÙbr† lÑóüQY(GLÖPÆu-¤õF¯ËCI0yCQ…( &q(ªˆþ‰É\ˆ Å„.¤ûP1©•ôù'ÝC†Ò lÐßüQbô6TÊÑ„ñŠÉŸ‡’4ä¯YbŽïªg|×TÊ>Ey`q(YHQÑ(1ÝWÆx¯vX0rÆ{uıEï’¡ôtÿ+"=ÊŽÎûPvXL6Œ­EÙ`Qù£ô(;ô¨T!Ê - %ÂbóAiP6Xt¾Œ-fŒh,@]Â"ÔÐ{ô$z‘%¤/JKçtßJ‹²E AÙb¡ÊP:ú§Çf•aÑæÑ=¢X¸ŠÖ{þ™WšýûÌ+½Ù±ÒkŠ ¯@ѵ|L| JL÷„ ²P6Xþ(JŒÅà‹Ò¡ì°(|Q:”-‡?Jƒ²Å" Ae¡±XBQy(ÇŠ»%Áâ e$E)Y!y£”(K,(T4+,)Jƒ²ÁóAiQ",4T4Ê Î•…rÄ“£ô(G,ÀP”e‹…(cül;,H* åH÷é2޶¨?*ˆãiËQ…(wÆÓ¦EëŠF‰°x¥¨h”%±/J‡²Ãbö§{PP¶XÔþ( J,Æ¿£4(,rJ‹c±û£´([,ú”åˆÅ/G¢$h Tõe4ƒh”%‚/J‡£1ø£t(4)Jƒ£QÈPz” CNïɇ۲AñEE3#ñF)™¡x£¢Qb4_”e‡‚ÊC¹£Ñ„1³ñF)™éHQZ” š²DòF)™IŒünwG޽‡ò@ƒR¢D”ÝR¢DhV>(-cpKQ”%š—¥AÙ¢‰ù¢4(Úq'¸Z¥?ÿÓyeɽh¼WÿGsÉÿ7Ì#yÏý»ýiÿ›Þúw~ZrþhêŸÿÿš?þgsG)û»%&Še‹É‚ÊC¹£*PLžP*L "T(&Q!}׉%’åþMßk¤²Å¤Ò¢Äèiþ¨,”zš-&ZJ²Ã„“£ìÐÇd(=J‚ ¨0™DHéúʽK*B¹cr*X‚*PEôOô­"º§–®ß¡,1i}PZ”=K†²E¿’Ñ}´˜ÌEtžHçˆèO1~6Ê“;%Á—£ QèGalB⊦{ç0ñ}P”-€ •…r¤ë¨<” "}(îA £ûê°@òPŽX$z”#Š%ÆbñG‰éz!*åˆ~#§{ïè~X,"%]?¤ûïPZ” Ê¥EÙbaÉéš"WÊ LKïcÑ_,ÑWl°ØüQ:”-?J‡²CO‘Óy"a* ±‚ÅH;Ôz`A†÷®‘¨Äóö.)aû@ø}°àæ˜t,}ïH Å÷Éê€û=w¶wÄŸë|`ø¹Ìþ.)Ê—pýG¦ }$8î`ð}»bR"{^ìxhðz+@—ÇsÎ}8Ü_诠Îx¶]ª$ÿ¦;L›0¸ûΤN-JDáú^Kñs&Néô> ~áø&IPÕ÷‹­ué³`×ÈN߯7X4zñså>%Y:ƒv¨]"ôuàkëÛ3GÊÅ}tæûqp<+[àû‡ÑÏ‘áçH#Ü*éÎ/!+Û¯®‘N7š÷:˜_Ó.Žø’íÆ\N¼w\IòÕ¡YËÆ,ú.ðý|=TâUI/ìwߥ×ÇãNßj÷𛌴£mu'Á¨›'ί;[ýºØîã/™) W’R´íuöR²÷Ãþ“.½Ž×´O›ãÕ¹Ö!é@ٿȸI»Ârë$C˜äªØucìp쑺⤡ø•¤Ò‰…v¯— ×'ógì½ÓÃx.ìu·fÚGDñ¯âê—ëÉŠ:_ž; H†ÎíïÌ»|îU½öºp7%Ù>`Ðk Ï¥„ç s\K;Ö7˜‹gö5_Rnp£ù#åÄ5g_¿ò²dx_îöŒ×£`Üü­ûJö€T›çegÏW’ò})au 1ío*Æß§]Õn[ÉÚm2Ê]N†‡M†4HÎQó:Çúÿ >zé܉JR¡Égs…±¯:Ÿ\³*`z]$wtó¸wËÊo'nšŽíø5¶wv»¿AÍúÞ ƒ‡kVÛŽW²>XB&þzsy[Iè/Å÷å>·:—¿ø9ƒ.¼²ü­ÜA½Loõ­W ŒjôÙ³mG5¼w£ ÂFÀŒC“çϘ¦$)eãbþª½Dè"pYß\®ÿ$W†qi÷\Ïz»Hã!”|’²o­¼¶êTPyÓkËQ3¼ÁÆÿÓ<¯yîy=hbìÏ®_Už³)7^qw-ܾ*»l(Yî=÷NÅô0²rÛ:Cv¡“×.ðÿq´#Š’Ä7¸w³âŠ¥B4¾ß×ç¼9ãeq}c4wÀÙ¢MI¡Ä€Ñ¬— ®M¿šm«¨®õàwA9GIÖŸh0ÝwôRÂõÏý ô±ä®+×ïGÿ•ç®í&D6.zÁ„T_´&¦¯J wÎÚ);¶X)|ï“-瞬ôÓ™ç r<2®ï“Ù·|ImÚÖü×"\S1&uz4pöx%,—õ]mžBÂö]À3r©g|ÿ˜Ùƒ;7\–RAÈ3¾¿çÜçˆñsª9vµÓ>è¼¥EßÌTp''Ç×PB“ku®mîµö¯¨$ À¢eBß:Þf;\ux°>ëûÄõ=”`\Ù›¤Ý¯+î'‹¶Ù”^åtÐû©Ÿ„ož9w­è–ê6¡EG”„‹¿ŒÈÖüüjUçU1¿’bœ=3³ŸÔx:­IæÀ[:³Íåæ‘à¼íç‡NR¨Ó%ñ[Êu%鸱y­IÒå‚ßr\Ÿw¬¿)óUŒg5dZÂAâÕÐ=·ëö[Ð˯«,;4ÚûÜèc·Ä´^7È-%áú†® <ƒïsdzoc‹ Ǫ-äëê[{dÞ‡—‰óÞœ¨ÙgÝì/# ½¡Á·’ˆiû¯;+…>RÜ8PÒ,êòô`WkgÈOŒÛýYÚFe˜‚dxýxh¥ƒkÖgçØ6:þî¾V=ö„¶»ž¿YIlò•×Ú®$k?{6ÌÅ‚õûäx&zŒã÷3èrö!reÞÐÐ/Ž:°LšZ¥oÛÓÐôû]'Ÿ6RȈoÿ2¯o/O¿o=— ãH?½xæ{+¡oԯлÇUWºç|Ï—<›hqúIÑ!ò½‰E™ctPUÐn¿8vÎcy¿Åˆ”|<:ótYË>~–®è/–7m‹õc< É‘…/Ç&Ÿ{î,Wm¥ú«h} n«;ôû$)Tma·ÿîl%¡ÝÖÞ5Ö%Ÿ7¦| =¾J£Ü5y‡ ׇJÙÚ:{FÔ9 ?>×iÑJ34(è^IÚØZ$vÚcäØð󆵗2R×Ï)ræ}•?nC~büš~ ¹³û¹a×Âg[œ^5uV¯0Øž? @ÖÙ:ï³!ýiâ׳¯]N¸>zeçµóÇÍõ7ïXŒ7*Ãø_î(y.¿ã’ðTÝ.ßÝ^G~BÚܾÞ]ã MWÚæ,U®þ ¿¢h¶béÆjå„ñ€ã5wxІüÅøÓçw(wÞQÒ‘b¦Ë§AÏÅoþ}¶ºœUÍ>=˜q1~»ÜÍ÷Û¯ê‹ëÇ,‚Ž!U_µgüŒçwãKü±VÇX_É4ð84Þì¯uÇ…>ØrMÿðx_O¹?}¦«‘Ÿ« ˜Êga!mGÚQà’òã$®—ý:Æx£i@ÆîµÏiv¦}Øï]ª/µål]<Î;Uó¯V9§<—ŠïÛ&È&ïë÷#_à÷õîšOÇÉð¾“N™ýSmõ+ÇÀoÊå¡Ý}úƒbͽ.‚”$÷E‘ϼ2Á¿ùq‘÷Cþb¼)µ,"bŽœ ”î4Û7 ÞúvïÔ¦÷1Ó~×oCÁ§©w–íX¦$5¿~ê5Ë„kËûª)×L‚ñ ‡]/ŒdíµÙºmm ýuT^'á(0õ= /«g³ˆI8þæ;2Âû ŸW|ÿCSþ‰ã¤øÂÝa„ãñ¥AÈòsÒ]‚²mð=Zmžìæ¦$Ü|I&Ôﯺ”¹ÐWñC¨ -#ïVÅx—2üœµšåµÿFFïÝèô48b>wÆwõ˜PýdÆýCA¶o—'”¤÷[/Ó­•Âuãÿ´zû¶qé묿<ÆÓOR>öšy’캹gKÏ7i»£ËÔ&Ž@üÔ-G …–¢s;+I\Ë6æ­òËu‡‡òÜ àû’þÝyÑ`üQ‡Ü¿,¨xŠŒ¶Ý½ï^íÛ°kÉ<§)òà¯\eïìjž°¨þ©– Ý”dÛº÷f9uÜü¥È¹á§ÌÖm¢« ýb^™ceSÐZèOmÈgüœô¿ânk6Ÿ"·bß‹Ünûz´ƒï!È|ö¶k¥»ž0PâfÑ])pZ‹s7>;çðבïŸiÈcŒcohÔN¼Â##*Þ¹ ‰Vwî¶áÌ;ºüŠ…eoØÔ9õx%™ÿ«yõÆzY‰~ÔÎî7»SEèKÍóù¾µ†üÆÏù2¼ió°GáäFg:ù õÜîÖé÷z?pü_¨w;L“ÐEI8΋Ñ?øóâ%‚䘹–/ô¤ã¾–^w.ušØ)j½c“bÝSŽÙ1µçh^¬é ¾æ8ÖOIjÇÏïÞÈx=¹y%—¿g,Žjõà49›Ü¸úèžé»§Uj…‹ûÀo¹]·;Ý„þÜ#çLïÜôM ±åBÙ¥>[ ù~â|ýñóHCÿÊ—l·Kÿyú4ñ=91on:ÖÝ`ßá/÷‚âûü©—\Cè_šLÄã\Ó‘ì)Sv•p¾­2—vOßñUðQn>ÄõUcÜ.Ûæžép†Ô§m7w§CÿÁ‡çÖ»¶ìÛMÝñdü0øööêÌ]x~;~º·nú*áüòבÿ“¿Ž|~S>?giÚõåÁgÈ ï΢—ÒáÅ §Í]vÃí“*ÏŸ? rz—ñ¯ÔTIŽ ’T›·J8?׋ã¦H1Žf×µUS3ÏôéÊŸ{˜õæg5®²bX¨†=Xá5¾í:jõñÒ5èäÔ»AÂõ⎳”зÏ®þGã[{< mAf\^ˆ3õ;Ð5¬J™!;`VrAH¯.R0´§¯¯$·;Ó(ø×gÿ‡0¿6½¿R`Ü/~ýçk""È»ý¡Ÿ&ÚÞÓíÞmÉ ÜåÞ~ú}iðx¸vϦû£fJöÝìhc]sãÉûb}Ï5¯Ö”.Ú¦M"I턇¦ºßô½Ÿ»>QlK‹î¬y: ¶Dß¼½¦’¼k5sïãyÂxÅóø~½¦ß_qU×Ú6ìÛ7’¤Î¾vÅ~îØöõU3{9TŸ¤«?}ì}ÚçÇ:Oö­ó²- 2ú=ãqóç—ç&dˆ(À†q–~çK^ÍÒâ)’¤Û}{rß´¾Ò[Éa‚sÕ£=5°)â6Þ`¾ÀïAäX ¿{«š ÷¦:1Æs?–•æx'’xÞ òȽyzN¼´Töf=lõêÿÐrp˜ó½Ž@J²Ýª–¬÷Ë áÏs¿9î.›oàçd‡fZ­™¥$ËYœ^+fÅš©F_ nÞÐV­îTØAI¤å'¹Ö ó+RÁëè´ªÂ<–ŸßsX6ÏÀø[Z­1ò¾’x‹— mÞ9’›k”?¾þ²@±ä¯Ma[Úx^I:ìÙñºWnÙÛä/ÿ•µøûÃ¶Åæ- Œ§øJY*r§Õ·^gÀ ¢Ñïâ¾Ã×ïâæ=~x@åÙf]Ä.J]®ÿ¢RíW uÇÏy®Ï1ä1Æu[U½Çê1*rpØÔ~s3`•ô`¿Z§ƒàs“üSÏx‚aÚ‹çûŒFžqæ€Ñ׸ûù/Îüz$Ç­dœFŒ[oœå–=~*2 ]«ÚÎ /Ëj;¶ž E[¼¹1 ¶m~P®_E%©1¯[Âð]«„óËsùyÏ1åQ˜™Hf­°¿±FEæºM ª›VÍ7—>¸¦yßíó®ûˆhõ=Bô+’Ä?9<ʵú*¡ÏnïgeuDe€_‡)¶þ€qo}´ž´{…ŠtŸ×u\`^DŽrvÈ_{$Vëmü¼ÁßuuþóÇ‘„®Æí Ægž[És9 ùŒñî>‰Íè6OEf½É[Só.ìðø¤{2 ²¶<²y$¼üxA‹¬HB)ë1vÆ<ãùH/È*^Þ6xól6À¸ExW3ã.7Ö](++óÛL •~icUÅúKG’ĵ~5†®|2»fdÜãÀÒ°·þÉØë9.¨ 㵪{׫æj̳u[š-¸ ŸôGÆu…S9ŸÃoo3?§eýÓ‘¤'= 6ÆqˆçñÇiÈWŒ‡ƒ·Õµ#*b¿-¬Ö²=w!·•óÇmâ¡„_Ï’Ï8Ý16’4 šçû2tÕ|@η:»¿×`\ŸÅî$‡UäÒúîBï¡õ†ô™LÐ\n“¦ÃàbÛ-•Rõ‘äÛÙu¿tÉ«„ïÍûyÃʪ~n.øÏáx•ŒŸ€Ÿ³9¾ËIŸÓ*"«:îÒÙGw!½ëžO;Ì!íšoÉê›5,FGíØõ#’àMl ß[üç”Þß8¾Gy._KH¾«Ïz.ˆQ‘Åû+V>ª8\µm‘›Ø+§çèjXV’]¾ymä®"ÜøU^ X¹wÆíÒ?É”;&Ƹ›òŠ NªÈËåOfÞj“ ÙoŸ¶«Ÿº˜œIî×7þœl¼5ùwÊJ²ÀÐØ9X8Nþ|, ÚòþM“Ò‚óœ_S™?GZj@mG…Šl¥xÍ™Ð@´rQ¥jˈ­ç¥þ†À½§…}[X+‰®Òâv‚…ûž—mºŽ*Åx”‚¼ï¨Š<ÚÑôÖ§å™ð´sÅ£ }dDeÞЬE¢8Ö8iwÞï À¤ÕÂuî7~ß7OW–Ë_ŒWîa§å~gšeB¯¯ž^sXE6†ßÏ_…u[¿ñœAÍÊ(Éïó¾?Û¶ÚÈC¨—Ýß|qeëfÊõU`Ü¿Âˈßä«H|©ß¥dBÛìiÁ³—üEúPnôW†I¯‚wéqü–½Ø­¬þcþë~Ó.¨QùrB¾ñ뵆|Æøo7mÈxþ[E:W=Ï{— ˵fŸ*Õ]G~×;åã|jÓö¶[ýŽD’-Ÿ‹ó|V uR§’—åÀür\¾bœ`Ù2óõÔdƒ¢V\{|®Ô¦É–œOmSŒ‡›²WŸÞlŽ$¿ø&Ì;,œÏ«ãW¨o.Ü·ñ|Î׸û"³Ò’›û¦½êßEMôí¾/:Ñ= * _b½ü‰œlY5»á@qÐY·,õAD$yÚ’V |;þs‚×~úѰ¼pøç—œù/~ÎWëVŽ—ÅjwiÌõų`R`ÅÃ}¯l%¯/-W ]F$ü~I |îxa7;Ï™¥ Zá̦æ×XÞb¼NÍ^\o &£·g‹·mÈ‚~_·è“7Kûþ,—¼ƒ4ý5ñ‹=ž°Ÿâ.­°]½ý ñüóÚÜ{ y›«ÿ܉ã'rëz2Œoo2¨I™ÑÍp(ϯ$õý£$Ç·ÊÆÂÑSæ‡ß”1Ö ŸçüºÀÈ å€ãmvŽ‹Ê|ãÞöMÌYÖDMÜöǬ ~›·¢kLSÅì"£C®5íSnöËŒ$ÓåA'ä­W ó-žË[Œ‚ñæjmöKE—=ž~±r6üré4>qJ(±M|»¢¹…+X=Uï>IvÒöíÏ‚…ëÇû¿²°ÎËsë_ 3ìóØ\¾ãçD|rsª"Ï^‹N”kŸ Ãçx›' %eƒÅCÔÏ{Âb«ã¶»nD’æ\\ÝL¸ï_ „¾ö†óý[èçÏ}nÛbëfe ${ë9ã­ ~ÎÞÙs Ά‡\½åªÝ$!{µöDO(ÛÕvÂñבdã¸Êuf ÷Mü÷âx"Ÿ¸õ8Œ×óR¯u¥¬Udþì0›s³ah—½ËïÔØÃò¤¼jGÁmJBg}wó‘ã–}žOòãÕ­¬o+W(I"Å´†fCY?¹½Óå=Äò™¤l¥Þ°ô›èÇ£‘ìþÝè˰ÍûÌ •øñOQûz¿Ü}¿㪫 òl¨$s y6ô‹œ_9kÉ^²{‘åj}é~гµÝäEá‘äðq®÷;ó‚çFp÷ Š­oÊ0î‹Ù³üŽ~‹ ó †™Í8åûÈ’ÃróýËÃÎò+¼âVD’^t9Án5¹÷«[N”§¨/BqnMÍx1£uÑëqƒþ ïA£RîÖ-çï'C¼¶L©·b(|=ì4Î}M$ñ›soCºåj!ϸõscÞ~“Ö®{1»}±ñIƒñ­'fw?r†¬u³è¾¡ï=P| ¥n9@â{´žïàû)~ûj$á8ÀÆñ‰ç1ó~ÁÏ+LïÇõ?v¦nWî¯ÓDÜëf•VóîA€æ¯wOŸ$‹b_žèÛÍ‹]HÒâÞ0÷¯}ÇÿfÚ´5ßÞ œ!Cž–-p|ÁÓäetì;ïAѳvE›¤æ¶×©ƒC¡ñ¦Eóêâ<`nûéî1/—ïEÂñróÎ&lˆãGˆ1¾ùWÅ´¨üp¢ò<]'8æ؇Öùá]﹑6õyµ¸Á`¸½ÀñzYå«ój;ó÷!×3×w=½_^¸4}®.Áøu¾µˆöNjeMj|,û˜Y^ rÛ|ˆT˜m;sÝù¾pdxhl&Æou†43ûüÇzÏãï÷Šqñ0¾b­òÑ˧Hkûnbóï÷ ¬ÿLÿ†¯‘¨N¥tóó]áKùñ¡jà}§÷üë ÷OÜ}GÙb>'Ãx 'Uóì$icéÔr[û¥ï©fõ¤'÷Í}[þž}û×D«A`¸}Åù[Ø·–¼ó#ŽWö¡Øú‡ãu8vy¸Ë Ò£ãU߈ ÷!_ì5H¼åyvÍ¡ŒuOà¸zJbÀVúçEëHfTkñ¥ûQ²öª:¤Ö·áÀ=çÅûýÙ?Ö&þµšôמ^e[¶‚ðÜÈ”{&Æx™­e+û#GòGtkxà>l¶úÔb§Ù12îÄ«ÁNmGÁ™ã2F¶U’+Ã]÷ÝÇŸ?3àêËL¹ËŒ7e8}br”(FäÌq|¼ò$IÜúV4Ößx* —o«Þ¾“õ¹£:”ø—¼¯šŽbüœiî5âÃOï'IV”p”ßï¹.¥=I¬\>ÛÍ8ã †å¢úJ¶Î¸ú}üxËs®L×%TÿRÓ—ÙGÐDbDbüš9Ëž$eN„ÔÏÔ9À³É‡÷´P¯3«Gú¼àç¿;+›Ù||Åbù'ÅxÍÖî¼}û½ô’G©9PÞëÍÇ®mO+/:êN˜íq8ÎÌëq°Ô§©Á„_Çà›¿¿çý„Ú_?ÃÏñ°Ù>½SL(ù²¾Úø×9@>Îýw¶ž†ñ–í¶uÉßIT]n ò7û~ÔÜßæÜ)â<ö*«¹l*ízuCK%9t¿#‹ø:úéüjjÁê?ÌŠqL5OW8µrîˆdû¬E]›‰0~s8™2·å‹õWúÀ#uŽ¥Ú)IÁûSÈc]ª]tº©Œ°~ÆÍS¹û =ÆÝJAsÛHX¼~Â=‡Pã݆Yûç„“ÊV9¯*ûÀÈÁgb/¶R’Û *]+1æ!Ï™äïgM÷ó™UÀûåõq»Ç§l!”VöcàøôѪ(îV8iå¢xyßÑdï,pHV’/ÝN¶ïß[A¸n¦|w1Æ[ãùüyÝ´är>€c}ŠV5owš,°ÍÛ›íÙÊúFù«ì”ĩկ·!ÿ¼Ok¶h\¬G%á<˜î“‘`ü¯ý×kº8„ˆ 7* ¡‘“âæªÓ¤åæjI·?ô‚¡rŸÒnJr°ûÕ_9Á„Ï_~<äÆÙ6Åê\Šqùõ§Ðê Ê÷?ç¹ .M8Mºæ¦¯—ûJÀºC³­yS”„B!æáu[¬–H×σéx!ÃxïÞ™gÔ]J'¹Z ½ðž\½TªR¹3¤ÛÊ„eŸ­»BµÉe½ƒ”äÓ¡Jù£Ÿÿéϰôi¼ÿ*î9Ÿã•5,¼Î!Ï̃÷OÌx—®÷YR¡Ý2øcä^QšlrZÚÎs›’¸v‘·ërÆx¿ðÖae´Ç*l]±ü×`\Î?< uÓº… Íò kÜÎÍÒý—5„¢õë÷+É±ê —ö,\¯†Í4ßÖV/+páx_3ä+ÆmdŽ‹ .j™y.¶õxœ!ÜóÆ_Îs¯¯P,Ztsâ^¯`!_¿÷ñ3kXËOQ„Û´–U*¸q©I.Ì<9_~cÄ{ªJõt½ô”YÔï¦îïøó8¹îØ~¥* õÏíçi_ìû‹1~ÒÇþ×G¿X #ü®ê÷tÏ…ò‚þ÷'ã|?eÖvVMÁ±ý eçŽ(Éš¡K­fß ö ñ~ί3˜ÖãŽÃ»{¯ïÁ@W‡çÌ…»›¾o²â YÞö€]­vÓ6¡Ô¥=J² £GŸ§—Œãæûjª¶aùqÙŸÏï×kõøÅëÀï¸.`ãØ\p4ÖÏÞÍ•Þ#ê£%>heJâ³kîrï¹ÆóÀÿ©KœX¡ÛïïÂqòãÆl,_XÓ]æ>U0'ÖjúDùž!ëBg—žìg®~?5ϯë‘î!û[“ç[óû+L}[qŸz)ýyp0¾+r!¯ò¾¬¼žäþ-›}‹‡ºAð¾/¿ç]S÷œI/{Õ2^~ý†Ÿ7ã6bÜwW\“µôÓÆ8ZnÍ…„)[û4ßAÙžÖ²x•úÒcå='}wn}ÖÔ¸¾’¬Q¶M—Ÿ§M?ó«§–ìfû€sA]VÇæNáòÏÈ›…/Ö¾P’Ÿm]5‚ÿØÊÏ?yîés3óÉ›íUîþîº*-¬>ÿX.l ›`~®0‚Äø·s™R2…oZ $1¿ÆìZ°ŠðÏ;øû~=–Ÿ—pû7Ø~4Œ¿#ýÀÖ™X¹V̉\xs)kFüër-Z³®èCc’{cjŸSo•ÂúXñuéß?ܯ¯YI ‡9‡àeÕ”1ÇsÁ[>µÍžT<^Šƒ¯æH|—]÷û¢$òׄì]%Œ³üùè°èÇåA›KC±¼Å¸¯|(ÈñŒ2ssáହcfn ?}§Ì‰íIÒzÈÜVüT’½'-7_O2®ÿódôöÓÄÝ]Éõò•ZLÿUòúýtNx81ß³£¥°?¬Ø¾3Œ;Óðà÷8l·§Ïrau·!¼?Ÿ!•›,¨7´Á“Õè7Ͻ5ú,ŸÜú¼%˜Ö™㮟G7`„Á¥Ö—¤¾)¹ðPóvbø²W±koLZObýÅû›®œŠ,7lL2Æåø„óÊû¸éó%=Æ/×ñ¸tcÔIHp¢—\XÔøªWÇ™gȧûöÝŸïêA®†$©¦"âe¶i[o®ÎGÉy*Ï·6?šU,<‰{5åþôpxå1°ïÝG¹pb,½‘:CÚÏ9Rêr9{²rf‹~.UdŠã[²%«W¥¿žÜy1+v^Ä78³ÝlÇ6g@’“®ÍË…%SßÜhq†œ ¾Ÿ7´1éÑî”›k! <߸ŠpœÍòÀ=¬Ëù-ÆÙë0¨â‡‘pþ@£zQ¯ra´ýò)ÛëŸ!Ü:IirHF7˜¨ÈAgÃåïߟ{Þjü>GS~¼ã\u8Àl½ènŽsáöÜñRkŸ!üsÏfMowùåª"|ý@£Ot xumÆšŠÂuãÖû»B޽qïÌå1Æw hØgÁd4¹ì$*õ5ÆÐÛŸ†g¾wÜÄ;ç>ãqg¿rÿ´¼ñ>ˆÛ!*v>oŠYØãmÕÀ?¹`ÛyÆÂ+MÎŽÛ^äÜtà–]U¤¡ l<^Ž]¹Ø<_ƒñ*fñuª5[ýä‚Õ–Oí68C¸ïU‡¼Ì0;v¨·ŠÜ8üòÕd_c ?»‰ÍBÈ®ogž&ÜþÉîdÿÝÙßÏ`^Ñíaª`>O8Åßo@wƒ;-ÿ ñk½®Úqš”»1LâBºåçE×ÉVn^hüÞ<7y©a¡ÊŽñW¹uO ÆMnYªƒy­ó°È>Í…äqç÷Ÿï}šÔÉý^×s%âVt¦¯"¡Gn¾ým•ð½¹yæo¶ŽØf¶Z­Ý:nªã:‹u»Ïž<ÒgºÆ÷1¤ËÚdúN‚ÉÅÕæ¿\]ÅuÏUÝÙë‰>Où?Ü>.'\÷Ý– úføu„M*ºðÛžËSŒ?·í¶GþÎÀüX{ºs{ÿ'œx%ï[ëúÙìÜæñÍE«"Üþ«`á¹zlïáM¤æ°¿ÕÃŽÖ‡:ã™!_1î_'Û.’g^¿šWêD Ë¿çT„¾Ýä¹9ø÷V|¬ncœñë×Ü~ã:ܼ?‡ÛŸ õývèñ9N]Ý¿eb8 w7ã¡Cmrt^ØÃÕ˜†·'dÁ$bô Ï“¹õz=þ~Þç­7|êÇÀïÔÏy•ñ8{zNI}éNljn [¦4ðÛ¡ÉeÙM·£×äçÉ_…ù>øçiÜçö™YHè]ÇU œþ³m©¢\ÛF•)ý+œ,û\*ÚÒÂövÀë÷±ÆÑÚë÷»ðçã‰sJS7bNt8µìüþNC>c|îùÈEÈ›UêЧÂ\p=Ø)`®*œ­[w†ùöóÂÅåÕ„®†Õ²êuŒVƒFý0¸,J`´=Ì•í~«o¡&å3sÒ~î=«šàÑåYÅ·ó‡÷\‘‹'Åx/WÆÔl+¹Çeg—º› ßj}ɨô1œX}JÎÙ8¸1|s3ÿVÉIM–~z§[|Á¸Ÿˆÿg¡áů~@ßîÉiéÄå1Æ ™46§w™Ëà¿SÜDq-Þî*—ðîk8i3Z~ÛjJuB«èb5ùüö[òȃAÌû†p@_çÆ‹îp¬ÆC¢YÜç(ðsèî%ÿs—!­ì:ïëÊ\XÑüæ? 'ÞnݜىÌÛÔ­[Doµ°?ŽŸ§j.¸®}÷‰ÛϨ¡ç!póÕužW ÓÇÁ¾ªÃ¹UaT`£ðpb¥ÊsÍåžä×ÝAÛƒ]ÕÄܰQkÕón½Ú8îswàxÕ8ÆøO‡ìÉûpW‘ñÉ;sáÑ„{_F„Ïf‰¹CSûç£w¯G¼®}ƒó‹*ÐÊË¡ÖÊûÁô}D³Ê’÷ãÎÙ?Ú ^ èd.œy[ùÖ˜w§ÈÃ4˃«õ%Grų́î+øù/?_à÷=ðq‡|j½¿ê-îy¥ã'ˆé“=U  ÏG,I7"Q¶}ÆŒNïCN¹^N1DM¢š…7Ιlôç&-¢î²A› ³7ì/´/öT‚q;}ºè&=M@ì2¦ëÒi¹°sƒh¦¨£°DèËuJŒÛN|¶Aìã}ËÊ Ï´sý,ŽÄ{†|Ƹitƒœ{ô—[yä‚xíÀ:#óN’{ú—ݾî’?¿Ÿd€š¬¯úk}Žq»¤¹Û¢àÊì8Û!ø16e WhàpFBíuôþõ\ð2]ÔIr"ÎËuW`'2ÃÆ¹ñqÌMåV÷ÜÝÛ¸æÍàW&‹˜Å8›î7W`\ÿ]ë‹%k ×‚ý½GµÊ…27\·K¶Ÿd¾Ö’üø¾¡ÞÚ“AoÿŒþ$ÌK8Þ|UáxMï‹4·aö˜Î·¬®‚ýpº#¯Û—>-íƒO²óX‹4é·tU{<MßÖKô¨aô¹­ß§ÕªÏXwü>£bïc`üµ{=Ì<®ÂWKº™ Ó;Ô¸jÙIRóÝ‹f–š då—MuªMTú–Úå…ÆóÌqÝ+ÉÆ5nÔ ê!<0äs•ÉÖ”aêm¹ ëkUêõôãÐpÕ¶Ÿ’pÏ1jÍØÏƒ« } ò× þþè±P‡Þ5ß¹< ²Î7·ÞÙƒËgŒoØÆ–}ºIìúl~øbk]œÝm,æGÑØÆ¶Ú–díÒ6Ëó‚ÔÂý"¾«<Ž9ó$¢ºp^¸÷éº{ÿE‚ñ¹õÈkÐñHÃÊ'’ÀÝš½rkõ8IluÉŽê]ö„›O¨‰Öe¡¨ž¹±^¸çâå„ùVEÃ{à¹÷†üÆø+Þ [ãs ÊOŽ\¼òò€Ëª2–'I¸{÷¦3ãHéþßíU“ó­?^µïbÇùõ C>cîqë5ÀÁ¾Sô™0ãEþòݹa¤&µ×ú=H=Ãaµ0N—ùçºã›5¼Ti{'àö_qóNÆ\^êŠï‹kÐJ{«wÄÞ`î§^÷úhÙë™}¦îs¼Ã:§tmÏý•áFÁÎ?-ª;ARõ– Ϫɸ÷ðõÒb£oóßCþ¦Ãá_×KÿÿáŸRÿž=¤ÌØyäy†þŒghËx¬Ædå{HÑþÃ2ÖCŠ2 äŒÍJ{“ú³R6¬‡”Ö¤g8ßC*„õ&ud½I³XÏðP¦’qǼ¯Õ†õ&Õ²~+¾ŒaËú­d¡­8^´Þ¤?_ë!¥`Åça‹æy†"Æ3T²¢ôf<KÖ/\Çú…û2^4åú¢tŒgÂx>|¿pÊó‘²¾¤6%ú…Sžß?Šö%5e@ð}üäŒaËú…SžˆõjÑ0^´¯I¯–Ö«E‚† @¢ÜÑŒíÁx>E¬o‹‚õt¦=¤&¼hE ž!íKêÍú’òý£”ŒçãÃúG‰ÏçEûšôûó7á@ø°R”5Â8¶ŒÇú’ò ‰ ‡Í»Dÿ(žÇæËúGÑ^€!Œm‡Æ%gýÝKôpùÇËÿñr™Ùÿ}/±óijÙdŒÍfW‚Âx>ŽŒçC9Û’r\?,=ëÈ3#ÅŒç£3é3Í3·å¬ „õÔ³>Ó þÏó‘2ޤ˜õÔ¡ìÏ'‹õÎ EéQ+ŽM›gÒ?«ñ¹)ÏGTë¹Ê³iy6›%c³Q6­ˆ±iµŒÍæËzÚ26›Ž±ÙüQYŒÍ&gì¾Ç´ë1­cìÓÓ”ýÀ³½i/@Ú•öÔ5é¹Êz²Ú±Ó”ý`‰…/Ei›Ö¥EÙ¡ÈQz”;BªåÆÆØ´ÞŒý@Mƒõà¢fAYàa&lÚ°l6j"RÖ gƒG3öí¨c=¦)›gÓú›ôå’±Þ­´Ï¿/ã†S¦œõpµclZÊ~ ½ùþÿî&<ž©iY‚çãÏx>vhXrÆ¥uDã e½»<÷ç²Q/ ?¼—ÿwó¾Mý™úòßõù3íÍ{,=Tê›ÿ¿òHê%=‘z ©çýg~G½®¤Ç™úZIOãýÌ”KÁûØßñ)xÿ2õ.êYÿʯ¨Wýw<ŠŽ£ 3®·3åe•6ò²iß{%cEz°^|”{íƒUGyŒõHסl`±þô”[Mù^y¬¨œõÑs·ázé‰js½ôÂØ Mû.‡Õá´”å¥aýôBPúz/#” ਢƒš2EŒÁH™.r6ßÓSÃzzú3.—-&›ŒõX¶c}<³xn‹IeÊ΄!-g½ólð§Ì ž]Ë÷/dÌD›x³I”õU¦ŒDÆ_)ɇ.d +ùœ´žJËz»Ó¾ÊzÆÑRü3úgeöï1²açAoÆ1´BCËño¸ˆy¬Ÿ(íEO¹Ûîåè~é?¹ˆ´¯rã"ò}•y7í«\hÎqC¯CÂ8Ü<¯ƒïUÏsi¯z_TÊ‘õ«§=H±À¨<”»Ǹ-D¹cÁ…±žõ”Ñ­DY²~¤<ã–ghÙ0†eÜZ2Æ­Ž1´hOe=ë©,cŒ[ÊÐ’¡ôŒ¡Ê —ï©,b=•³«Ã´§2euðŒo™˜ë[JûÚ+P…”Q„…®`½KYOeÊê°ÁÂ÷AéãV†Ò¡ÑBQy(4%3o”’™ƒ”±:(ãÖ¥d½L)Qi¸U–`h‰1šq)S\ÃX”‹˜Åz*S†ϸ•¡t([4›ÖßTlÂF´Có e}Nã–²:(‘ï‰ïaÂR2å"ò½ñÅŒ‹˜…rDà e|[ —UäÄñm£MøYÔèi?eÞ¿©wS¿¦>]Íì?÷äÿ‰ÿG^lêÃÔƒÿÎM{)S¿¥^K=–÷×’ì ÞKÿ•‡òžÉû$õH~®E½OfÆùÞ¿ò»’^G½­ä<ª¤oQÏ:U£Ô&žÄû–„Á×, ,Œ ¾”±M¹}–è/JÊÔÆ‹§DY¢—øšÓýfxí;[ƒòE/Ébì‹Æå£Üly n¶ˆõi§Ül¾G»¥•‘±jÃØ?&ŒQže&f,3ʵaŒÑ,Æ2“1n´#c™éË,•ÇXf ÆœðAisŸq£)sB†ÊjÀq£)s”'æúº‡¡ŠPXèa¨¢Æ7ZΘb,|_TcŒ† ²P4ªå†á•¢¢cÔ‡1'(cTŠŠFY¶æ8Ñ&ŒÑè,3KÆÒ0>åFks‚r#)7Ú‘±ÌxÆh* e‡f#GéXÿwž'éˆæ£@éQÆ¥Fäá`ìO¹ÑÑÃâûÂóì =J‚†¥`¦åŽ cæ%e¼ žcö—ÿãå2³ÿû^nÇþ e1ÞšeiŽDyÑ"L|Æ‹¦ü Ê‹Žf\J_Æ¥´a¼èh” Ûñ¢iHQÑ(,”Öœî÷çxÑZ”%Ž%º·–clÈ—’ò¢CKð¢-é|”ñ¢)—Ò¥CÙ06åàŠ?Hg‹æùArN[IöeæJCDm(%Ê Ô‡ñÚx‡Ò„צC9báÊQy( pÊ’®¢ÂX1{ ¬¨¥bŽmØ”:vŽ1)¥(%+zÊéU°â§¼Þ0”MÀ¥E‰Ñ dŒåfG9”á‹rlÉ1;ôŒçÂøAŽŒÅØr~‡¥GILXÑ4“PTÊF‰²¤ëŒõF FŠR–à1^tãEû 4ŒD¹o:”Ø„Iéh¤¤¬èPT!ÊÝ„íF¥DY¢Yù0~ÏÖ¡ìмäŒGyʼü/7û÷ðrGö=x\(cÁ¹3’Þ„T„ò(K{Œà¿Gyc1(ɽ¼‘‰äˆÅʘšŽX$¡&LÍ0V0”“†*ByTâØä", ”ekÂLr¬B÷wb<”;–UˆrÇS¢ŠPÞVc˜›7*%ªÆñÍ5(1Ÿ¯ c˜gÁÙ2e ‹cXÏXp!Œ½)a,¸<Æ‚“£ ëp,¸0”%®/J‹²Á–1nº#rJ߀ã§ËrNÊZ’£ QXàJVäÞ(%+vÊä Eå¡l±ðýQú¦cXŽÒ£ÜÑÂPE()‚e‰¦àƒÒ0ư/JËÃ>( ÊÍBŠÒ˜0†5%Xp6ŒÇ¤eL&ÊíÔ¡l·“òÜ%ŒÇ3†å(=ÊÍ&•…²cìÎ<”Í' •‡rgŒa‘7J‰²Ád”¢4Ãk’¡²LxMy(w4¬0Æö@)Q"gŽ/¬5áÀQ¢?%½œ÷pS¿¦þ\Ò›ñ2|ØÔƒ©ÿRßå×%©ÏR-¹Iý”ú&õKꑼ?Òc2õDê‡çƒÿÑš£©¿QOãý¬¤…˜øÓ¦ž´ƒyÐ^æ3üÚ¢©‡P¿0¿ãIôaœ4Ê ·,Ãq!ýRŠ'U„'ÕGDß¹¦ï㹤ÏaQvXסlTTæ¸á”î'^lÅqre&lð¼jó, /H4^¼ÑŒe–E÷Ða½¹c)QR:wÂÚ C‰ð‚ùˆ9†w^8[¬)*«1Ǹ CY2¦­-^È:Âz‘3VYãØJ°&BÛpŒm:˜z l:9µÑtCç-tCgŽeH9ØÞ˜›6ŒF™³”#öÏ|âŸù„Ììÿþ|Âg½^¥8¶,Mx”œ1=0ùÃXx£ÂPEei¯+ŒÁ £<í±ƒÿyÚ»…ö1áX‹Æèæy‹J”ˆ2º«›Ž7ãu[bù¢²Pv•é;Oô=úN žkT!Ê +U„òÀ‹fE&µâز",6)Jƒ²Ä¢“¢´([,>¶lJÏØ²!Œ-kËØ²y(G,N9ã3ºc‘†2¶¬k(ªˆî/Á¢U¢lÏ[‡c‡0ž· YÞÀÈôiȱÝÅU„òÆF‰°È¥¨h”¨ Çp¤ŒïB”¾ •×”cˆ¢òPhJf>(-ÊMÁ¥elY”ޱe}QZ”ÍÂ¥5aËjQb4”†ñe(Ê–1³œ2y¸¼ƒ‘-ŠÊCIÐl(=Ê‘q Qîh>JT!ʃ±e-ш¤¨h” É¥E‰Ñ˜d¨,”TJrD£ E¢<а”Œ+ëŠFY:s\Yо%®^éMmSÿÏüû¿êݼoãå,6ïà=š÷ç¿cÌšzòÿÄy/¦ü¯¼—÷]Þoy¯5õYÞSÿ•—þ«}j¼‡òþÉ{gɽj¼OòÉ{ãušÂÌè…ÿüWHÏ“å‹É¡EÙ ßÙ`’ø¢tt®„Éâ[–öSÄkYŽöã£=Ü0OËÓ`˜oèq>¨h”%݈Ңl1±t(ÛŠ´Çæ/ÊÑ‚¾/ŒÇ‹I&b¼lV¶ ekÅqem1ñB#›ò± QÆÇ¦lY:Ïb é C¡Ü19•ôLP Ç•Õ`’Ú2ž¬%¡ýwþlêͼ/›zòÿÆ\OböçüŽú-ﵦkRÿ9ï¥ÿÕ¹Þßyæwžgê…ÿ›s=SÏûÏæxô»kQºR”7A{òãwAÙbRø£´(;LŽú¼½L‹ò­@{fâï`ÂØ™Ó>t˜[(;ô.ÊH^‰öÃÁcByÐõ1”eÚ#€¾qPbô©”zTÊŽÎçPzúþ*&[]3CoR Šè;O˜|J”7×Ñu4úŽú‘–®§Ñ¹JŒI©EÙabæ¡Ü19(oLP Ó¡ïXb¢F£l0Yµ([LØ”úLÝk‡É«¡s;º¶†žâƒŠF‰1™e(GLèP”“:•‡rG?Qй&y!*½ =ľåIïIFßAB¯Ð ¤XÑ(,Kô_”‹AŠÒ l°(d(1† ¥GÙ¡/„  é»FX,…t,L+o” Ç¥¡{c±€ôô]Q,"=J‚…¤`“)pï=˜þ7ÓwG5À½SJßC¢ÿV Üïòÿ]Üï¹³w”ü¹N †Ÿ§ìïÒª’;•«û·8x]è{<›¶áhþ@øß«û/¯:psë3Q :<÷œ5Ͼ·ÐoƒvŸþ©&Íë-Y¿æK¶&ÇÂA¿Šð|?ú92üœy§×ß÷1‹‡ª½Â·u¬þºì¬TF¬ùP£¯Ôõ5V“NS­? æûž|úÈÍŸ»"ÎuU'à¹?4®ãv|´ë[€g<Ęuê·ëWÔ5¹ûÃÁ'‰S¤©_Ý 4îM÷¿nªIðòæ| úÐÏžv²|øw#çïœïÈÕ)fÅúªk0þ†˜™æ_ñPØeî¾êÏs  žróÐIÂñ(z‚ªãêÔ™¯ÔäýùÛ3£TAB_ˆ:Ú6¸\‰ëq8Þ\ lÔ;¶T½“¤¢v`ÜñàæÐvû€A¯-¢HžÕ‘5/û™q}`Jã‹1Þ™yÁºž з•grOÝ}è˜×­W‹n'‰{zþ'Çúö°ägßeoÊF |=þ|ð|½b|Œ÷kâùÉ  ^uæ¥ûPühöÛÉ' ¥ÛYt€Œ&•ç̬E¢Ê¿ ŸÈèS+Ã[ó_MøþÜñÚ Ü9C~cücEEÁ¯.'€ÍòrÓo¾z?ùTn÷Ir¢Ý­®ívw…F††åQäÕƒ†o‡µ6æ×± è[SF'˜X½ìæ¹o»qùŒq+ôª?<úY¼kÓ³·dõ}è_«BŒ'ɵÓåk=Êé ]œlmÝZGÚ*é¼ñºññyÞ²)—Cq/vpš°¶B"ä›÷uÎÅßÖ9ÿ$áúsµ‡CG(83Št (›ðÕ˜o¶sG¿Rù‡p\®¿–ã.~{Ý\ÛD8yäÉŒÉ#îÃðÔUÒoeNާØDðYîóý—¹~æ¿. Ïá,Æ=Åøßjûbh"„7¹XUrž<¤žO‘FÍ{ü[—‡§—¶_œ\/ŠhtMzßjkä©òÔº@’üºÅ¤e‰àEÛGµ½cÎoX÷¦ê)Öo°ïöŽsíÚ×úãñ|6CÞbÜÇy­Œ–Aëktýë7‹Û·í‰oz!p§y?0 '¿Õ“7“¬"GÇBàýðó­’©ÓÒº³¾\wu¯¹ï $AÈßÒéÓïÁž!NO,ª†“ÀúGTô§6wö·O“©ÇŽ>åÞ¸þ|Qd£ÅüZÖûvε`ˆçÖÀÏŸMûžÉ0îܪ.’ØàdО;4qɆ²÷oŸÍø|š´’¾[·ó½†Ìܳxßá(ÖÏ:èq¹Qmõ‰O+u•¥ŠfÅxá Œ_cþ±å%ƒ:ãÙvYh6¤'.è¸õÃiâ~ɯJÅ·µˆhs—MÞë±¾-žGÊc‚ˆîñÝ7>m9^¨?ãUàp©6n8<®pz6˜ó²N“&]·îÕ„h›6™6ëö{£.ÞŽ}¼_nžÆæÇ'yY˜g–ü†k6|]º !(ý4©S¿À©pC#rzè’ã»p~ MÞyzï*6?®õʡӵX?`³êŽKŸÛ¯]¾¹»y64zãV53þ4ÛôHLÙÖ¤A˜gƒ¢(»ËŸ/¾?:?|%MZy9Œ`ü®ªã÷ëµÑeëȸãÞÿÙkQ6LªÖEÒŶ€ÅŸ‰¯E’.Ë<´'ˆÇï‚k­`ßÀ>fëG@·ÓtãâI0Ç I†²;ͽɂ¿iÃ21¶ç(u¶…]ÊŒ‘·"oëwn~}LÐýçøù)?Îò}/,ßü­ F.OñsZ:Wsê¡0àM2² åủòŸN“Ýtº×¡ˆÌ(©#Š• ®Úl”±JEé»`‘•À32í[(øÁnnû©S`Íw홾²`f÷ uš49CÊ ŸÔß­+ ±÷oEh7ü°ÁAÂý‰!ñ÷$ÙÛ[_NO±ïÎMÛ™ãüœ§ò>CÚßü+!rIwˆhÛ£} ·Éû× çGÜ<ÀRà}›r4÷2ÅsÞMs‡ÚÃþš—ÓbË Üq†tJX‘|Å€¸û$ÅT"²½ñëW7 bü#w—ç8qõÅõÛÔcÜM)›¼~ÅóHq§Ã²`BÛ­÷2Îj«ßµ›JœN­(²B;:bŬ á~‚ÏÓˆ¾šÍ¿Ú•Ë×’jËC*¥²>©Y ›íogcÁx$Î6;ÎVÔ Šìî^Ý5§!ñ÷G¼Þ#¿k“ iZÕÏ‚ÃÝu~ߴDzåå‹]áW ÚY4ŠÖt÷øa³ŠÄÈ·Üy¾ðó-¾o·!1^DÖx¿Á RYñL8’s³î©äeΑ׃Úv€)C²ÜÒÊD¿zàU§ÃŸ÷\ÿù Шt•CýRÚë +Åø-<®7þ«~*œž×¥~©œL°]õk¿ûú¢\H;Ð6‚Ï·«—ôV-ÜÜܹÎ^sê·Àÿ3çdÏ`µSáøíÁŽ—ÎeÂУªÓù{#ÇŸ­v½›Vl[ &‰ƒO-ï†ñšKž§ô(+ø¹éñ)0^o]hß;x=üÏ÷™½9{Õ¬Ò2<‚ð÷öÜŠ¹ÿI-Œ?™æƒ®Ï2ÎÓ¹ûÆžÀóÌ ùˆqEâsk·}OÌ+5ÖšÏÈ„çMß.^A’OÕž4¯o{8,v凚صÍß–3Áè—ÝVïtÀ83òpTrc/àï y‰ñ Óå)@áq7]3Á÷É¥®¥ÞG Õ°+¼œaÕçü'‡ð¼ä]=òð* >½çª°ákÏ4äeÍIûuÏ]û˜wO­v»N&¨&ÍÞŸàI.䙵íyÓ5â—éj2)´ÚÙ„GÆñÝ"c•cüîÂùàîû»»ocü ôvç} ,/gïwaB¦¤ìÔ[‘Ärа‚* <Ááº×85qJ[ô¨Â cßPa}­_ð~ÄÏÛ yŒñéѵú–»ñj:§Ü…¾c¬zÿ%Ii×2$©»7lÏuˆýœ¬fü/þ|—/v¿"Å8'kZÅ?­œ ¼ô±»€ƒýÙM§”ì¾x4$^§Fc¿ï’œpÓ>Ã2Œ÷˜N¯ª§Bb§=c6ÞÇEwÖ<­£"Ëê<¿øpÄhð«Éǵ=ú^œDÚ/”]ê³Å8.efËWNµl_lýAq¿()¨/.7¤gê.(j\)ê¬"o 7fί?ZêZ¼oúRM”?÷ðÞZ£n/¤ UüàK³ýîÕéí)æuã‡a|Ž?œ 6='5Òþ.œª›;±Ó#áúôƒÅ—æ¦%¨IûN™Ó "|_TC~âï?jQEñðK ¼Û±]=Ñ]Øš!ò.o§&8ðúz`ô©_D“Kj"±ñãd2Îñã—¯"áõúÎÜ}B§bó*=~ŽÿÚ <®žè°Åü˜OùËþk¥ó„»vpЇ—ã¼Êe³ÓéqÊcÛ­Gf0¾ö”>—ÝäÖÑjã}'Åw%÷ Á…>OÒRCÚŸ'µ½o¾ëu¬'ô_Ð:8¯'¥¬m•nÝB$ðLûË1žÇ"»¥ÙÉð+”RÒaÃäN»4CÏ“#?Ï~~²×l3«k_µQ“ûÕ7¯ŸaeRçýW½*_ø~ùŽÍ£1nÃÏ.¥î_MÃ4fg:ŒŠÐ~=½ì<ݧutêm ~©,ë« Ç1æLJ®Üzm/°m©4;nÁõã—bÜÂû–O:G&CêìkWìç¦C›ka¿+D'§[å)ÍCå§ÙÏF7T“Ýïèƒ)cÞqóÝ*À=wêÅå1Æë5²èÛ¨]É0ÞÆJ‹ªºœ¯çI-·³×v—õ‚ùë6=}ßÐXüÜ*¡` P:tõrjÒ§Ü óÝeÂuåÏÇã0~][e\ß:’s|A24¸žZ½[äm(ìà¡ßMfÆt¬fñb¼ Þ¥¿SçÅËËþ*ü°’ÍDÏÉt,Æx @Üdød=i÷е·azê°AwËÄ¯ÒØ }BFÀ†£óú•U“`º Ö^F޵ð[±·ª9üØï0¾Kÿ86.‡%×íŽëÁÍöɰQtãê±I·¡Ó¼ Û ÆÄ‹ûÒ÷ͼ`½W¼×7™ñ50¼G ìëÉßÇò¼ žWhÈsŒÏ É@W’ÜnÃÁ¸FšMGcÈ„àó`ðŒ(lW늨íx[{­Œpü93öœÔ\Xåßô9š ã§ÇÆ%9I°þƽ‡Ù nCCÍrÿwÙ1dzô«r/ÝáQ3ÇOOU$#’ޘʄ¼çÿä8´"ÁÇy¾€éx¯ÀϹ5¨|Ÿ„ð$xÝöÙ2Õ—4¸?ìˆúrù‹Œ·ã tu')SEÔ—$}ËÉþx~Êsa ùñö좶“ ™óâ Ÿ™iÐky`Õêí.îy´ ̩پæ_ZIŸºï¯°k+…ãæ}›{NÇq|ôoðÈ™ÕÆ&Ad“*Ù’‹i¢¿rÀë"Ùv6í­ê”+¼Q*öþeŒ×ñÊuïã+…ùÿ<†ÎÂÈò¶Å¸Nfu $gî+>îž±é£7ìMƒ&Ñ]¾Þ–]$ͦ¿žµBí+W'ïèyNEÖ l˜¡\)pùlÞOjQ+Öœñ€ÛryŒñ~-¿±ºL£$Xp°NUÓÀ­Y¾-.\$ñ;ªtˆ9=œ“>èßœV‘½Ã†ŸÉZ)ð€ŸÕ­›TÎß‚Ë[Œ“2‚Ž\IÐzNÎÄsÒàÂå?ý—‹ŒÛìÁÖIUäÁ›gÎ]+Çþzp| a=|t¥(]êtîzKiüÔË[éÁq@'÷«ži°æËÕuá‰w57ÞaÖîUnþ_ZªžVqZi!_ùuZÓy ã^ÛkCæåD¸íÃPU‡4¨×`|Yw‰ø•ÿ¡»4Ͷ¸ß/ª¶CEFœ? æãùä¯~ç5ç\s\MÆ›i %‚O…1#’Eià=xh9÷ìK¤IÝ#ºÊaCÁÿ‚ÚuÚn™hWeÒí•‚ñuÌsVMŸŸj0®ß©e·Üƒá®û£ƒ­-—t¯ßä2Y½îÒúžó†@}§sÓñüNÈ}2 Úyc>ñ\2~=…UŒ'‰ñS¯ð+š™ûÛœË{V‘}g‡Ž˜|™pûY¥Ö8ª"ZÛ[—ëù­Ö9•ŠÔkVÎ3¿~ÂÏ y[¯@âdqfPªG"´˜zÅ&q­Ž@§O÷\&c®×;9©[_˜%Ú”}RE&Ô¾Ö!i…°NÊχ"zÝ›ù¼3·¾-Æx¿G'lÚ(I„+®eŸ™¢ƒf!e÷ºy™š-?ú[â *g…ç—®ºØ}X.ä^øç—v}j+éÄî?9ßQ`ü¶c†Oè¨IJÃrïl³N~\Õã ±<ñàëæ®Ì?TDêß,aÁr£?6ªðݦÄ>‰À§6ä7Æ÷íy¾Íø] ᤎ™qà4IÚvn׊+„{ÛÊylh³{’Šp÷¿8îÙŸ- ·­.œoîy»+ ¯ºfÀW.®ã‘S€Mxîl´iѼ[ð©Ó¥¾Ó.]!ýôâ™#ì‡@†òaµ/ñôûÖsìQÙÏ[7fôŒÑ†–gã\7¶‹åuýÉÛ:]€ç‘îÞÿÌÔ¥}¨y,9s1ªnãjÃ`ý hR‘Àȼ»3—ÈHÆ—ìc#Ê õÂs~M÷ïˆ1î:X×mAÕ˜õ&oeLÍ[ðµÍÂD÷‰±äÅ•»ý6.ôlŒP®ÎŒõþeUÒ¯ârÂ<ƒ_¿çò¦—ç¿þ¢…c6¾Ò ž+ºÌ ¯§?öß}9–8–êá}nÔpx˜3ÞéÍhqÙå›×¶Á b;2ôV™éfÂqòã< êXô=^ Ž÷sB(SAþfÃèy5 yÔwª‡¶å0pDÓõ«"wl›îÎè¾LÈ þ8 ù‹qÒs*Nè¼S 3'tU­I…Ⱥ±ÖÓ¹ÿvÕÍÓ‡Âþžõ*ŒS‘-²´g)–þÀï&ÐæÙ gkö\©-tœÛÏÙ­ãa|Žï¤…?鯑TØ~³Ýi¿0®÷@xt¡Ñ™µKÙz.;–u­ÙV³]¸Òœíé¾®_NÏ<ËÖ30.¥ðmª…¢‘NQMSÁ!¹µýŽlBŽ,¤D8Wè=îîõÒãU„ãñ-ö'ñ­Â2¡Þ¸|ýàÌççGuç;òã·ª{׫æê›`ýW©þS a¥Ã¶f-5Äyî‡K$Ù>«òýš*r38´ÿ©óKÙzéWáxM÷yH1Þ×~âf~³oBâÏ*nÎ)0úƒó˃Ý5Â<2ì龬^ߕĿ•þÀ‹ÁK‰Å•ªCvά$øÇmÅârë#2Œ;wÕ½¹¯]o§¬¥¦µLÇS+Ñ$ó¦ó+è I‡ÜÎø©$…ëû»÷x á8ðeþ!Ï33ä/Æ{޳&Ër7á˜ÝÛyÇÍS þªñùhÈÁÌM¡›Gõn¿ŸŠ”:’´7>x~2¿Çï{4ä/ƽ™¸:‘Ü€‡2^‘ íÆôj&ÓÔ¡ê}Á·û€5ÝÞe­bëòK…úå8ªl㬉»­Ù<ãÔ ª·Ý.=v×ݺw—†X ˆ>´@ÛV8Ò*ÒllǯS÷.%†ô˜Íý¾YÃɳhÏnÞ¿ã¡ÿé ½ñ>¼ÔÀàʃ#5ijõ‰ÐÜ‘½Ò–?~U’M (¹m©7ü<”W<Øó! —×cSi׫âáVß·uÇ/M†f†/ª!«¾íì:¶f/°Pn4㺨eæ9— ëÌüóîèpàÇQCb܆‡îf#ƹ%C®3=1Ƈí {G¿[Ë\EnÑm>[–Ž—n ª£9Ë7m's\Û¦œáâI1wÜs¨d°|y!²Ì iï|É~ÜI'ð-¿’„¶Åñ/ª©d`Û¥¬K]ÚŸuàl’5Û÷1rÏ´I9µ'—ße{{ŸF¥â¡ÁÏö™‡Ë%ƒ+ÉK?hˆ×‚˜«k:CþÙ9˜±*ÂåóÂí7´üþlã‡oõ ܾÄ>\^b\J3.ûä:$8kÕÒÇIp õ±khHÅóçw”yÜ Úyý:wy¾ŠTz;ßêáÚ€?öËŽµh;cš0^òãÆÚÏÍXv6ß´ðºA’ ¯îä¢6fWIsÛN¿T©Ïo](בŠløTyÀœê¬ŸqõTùÓuC\=Æ]¸µt%éu8ûÿ°wæÑMí/R–B H–Ò%,–T¶°Ø'P„°¨e R(`Z¢ÈiÚ"„Õ‚" ÊRö¦)‹LšTM ‚‚D©X¥P„*U‹Üg2Ï”ÐãûÞåÜsϹïKÏyüÓIúËÌ7ó›™ßçSwÑ·¶µÇaæ4ã‰O±”øÌÝ“? è µ6èÌLìÓ¤Wüa¬u#°[óêuO¿ «Pwõg+†ÓüxÚÛÇ¡2- Ö¥úE콤TßOî+aw÷lë0ÿ«tFÿ+ ËIµS&žÒDôSlg¿³Þq—Óó#G›+_:Πӆ=SÄîÙCõ¾=œñ)-û›ÙŠf“bvf0yÅÓñ÷íæ_]̶çUÌqømPìŽ.bb½òE(jPdÇš™ôúzúþÞŸéÃW6H²Á%œN < Õî_ê"ÖË’È3ÖÁP÷‹±iØïº•ž9/&£zýYœSzìú°=nÝÛiƒÓWUëß/¹ž£"ö…æJiéËC!¨ïÉ?ªÌL|¯¦W·'úÝÕ×Ù¶zU§ü}ÇÅ}¶{xAJñ‡?X!µhØxËñ8ù\ðŒ ‹Ø¥µ#;yi4¹Pª13åÂØð¡Éòs«ò_áû“ëÃE?Ãv+[i?0ηBÔòÓ=w­/*+¶ßÎ"vìäZÅûiCAè¼b˜™‰ñ—Î"šôk1í’¬»Û&7äóZŸm·»Ï+LØtKô3lwÄÅSî©~Vøý×øyºpnŠú`­£ˆMj}ö§¤!Ðôû·b¾if¿Û‡Ÿ¼žr§eõzþ5ÅPuÄr±ÿãÓ¦Bû驤ÕEÐû|N÷Ò%pçÈõ5Ï^Âöv.]ç:÷"ì¿Þ§O»±fV´Guv¬žÞç³tó,Èóš­9F”_ûØî©#f“RQö*ßÙߦf½Tº¸Ï/E¬ÊýBʦ.qP0ràäÐÉfæó^Õmºéiœ…UïƒÞ}ð{@H+Ø9fTB½dq]ÕØîº—ݳYàÈgÇ­zªòwèúýUÄz†tìS±äy˜“ýùŒ^)f6fÙåóWë™Ü•ï›S+ï¨ò§}ˆÅ|Ûm<({ÚŸ£-tkl߸rôo¹²GY+ùéÛƒê÷çzŽyÃÌø*ÓÆxÙ¿¢ªç×ú”vxÙÄ9ÍK¢ßb»â¾†ÁïßÙg/qÀé߸^kheâ¼e48Fvœ;\kfëOèÇ]?ÎäüDÜ„Àªs=Ž~»:„GüŠè·ØîÓwoÞÝÛ„ÁÏ=•ïHlƒ+ûíàëY Ç/U›Ù–†7Æ]};É~%¼­a‹jå®îá ÎûÛE¿Åv7\zßÁ1°_Ô®d›^èúÒw­lüÞÔ„Q¯+! S£uŒ™½è·}be–žÉù çØòŠvà™.ùÇR¾ç‹~‹íž¼œ2²éçÐÚZ9­tƒæ¼ô†íh¸•å}w>mT+xŽ/·53¾JúãÂ4&ïEˆ†ƒ—:v»¿Y¬«û„W¨§nÞøMÇ£0>UyfâbX>Ø:ðdŒ•}ôʨ÷‹â Ïë_o¿ÑÚÌ–Ôy?ffã4&û«<×Ó`khr"„‚ôߦëOÍ¿â;äüÄÓñu<:ùÞGàë\‹£Þï½õÛ¬ýª>Vºï½•ç¯ô¡õH3ËÚØoÍÓ˜X¯ ƒ‡|!#:ê²ü\4ˆs‰¥¢c»KÛòtŽÜÚ«’à€[»ö4==ÂÊü=7xñÐ*ó·W·G™Ùæ!9Ž/‚ôL^᳎‚ãÓv-ØS[IëøD?ÆvYüPÓ+—Áس«>xKé€KŸ•le~mgµ´ ‹‡k³ø R¼¿ãÚâõ´Ò'_•Ó<Øúm,ÝW›D?Æv‡<8ºS³øÝ7:`™ªïnU†•±›ñ -è –×]Ü¿Zž ß"s§ˆØöy‘øÜ,ؾ»9?0yÔöë6»µüâøÉ%+ëûTo˜<èÐá¥Aøý³Â–øWƒt&ïoäùÖ³ób?›¾Qì•a{ëY®˜§B¿!K#2‚0À#^·²Ný²o^ïÒÌA³÷~þŠ™™ö̃ÍéL®¯Š~ÕŽæßm)ÏîÄÕóðÒ¹ûÃÚ”ˆ÷íÓ¶B}Zûg¯ó9¡Å…+áÇš9`ÄðU³U‡­lÇçiËþÖ·ÿ§>ÿÆ·é´£¤uùpX`úùÒ€þAôÜ×M‘ÇØîI³>>ö|vgû.þµ¹4`g±µú9‰7Wf>œdfs<`:“ëWâÜFó)‘‡%¢c»­ú´ÿâƒ/@Žîïê9`éëlëµV¶ü¯l±ócavxшÎÓÍìõ‰¯ôªê*çiª÷Å}OKšÿˆ~Œíî ¾c-€ š¬ß{/wž:ceâ÷ºÂ®³£G×Å<¾PžæLgòþ|kgåÝü¨(QµH»E+ö ØÞÕkK¶ßüåSÐEöêVûŽ,÷þ~õE+kÿéýì¿Î÷‚ /84â×õЎݽZd2ù¼‚ÈÍöpk`‹«÷Ôí«û_ ôG}‡¯üÍ[ñuŠþª(-þ²·¼ÂÜvèÒ­}Tä=|s‘¿ôkîâ=ñ=ÌlÔ¤M\ÊdrÝT®»É} ïqmÁv·MmRP;>Ö~µ~eŸ[vໂÍÙXþèi¯=3ئýcbZšÿd»9%“Éœë¿ÑÐ1%Úç@½0ÚŸ½7`Öä+k‹´Põž)öÃ\ñþËðu&lXþQöëfض`Ù® ×í´Ïlc'6Í~þ‡˜¦š’z,ÀÌ:òå³ &Ÿó±™xP)@œ' |ì{Ð'¢B=Ûs08æ«y0Ûa‰ñÞ_ð²Eö_lcî+.èBÍlaÂ…¯N·Jgòܶ¬xŒSŠŠC_431?L¯Îgüë¢ÏEÀ¶÷›Þ¾Ä|Û •TzÒx¨Íj<¿­þÖaø‚ËJ¹Z±üÀ è½ä«¹-ã÷êÔ]úžt&ÏcÊóhâ~±?år^œèw£áû:AkàÇþ¢Ÿãëln¾·´á‰]P/zË„qà€üû/¼8­ÄÆÝ¡Eñõâáç©ý!äe37§áN£ &דå>X‡Z×2«V|ìûÛ„ížhÈohwBM åüq˜¿šð6Ötù© î¯ú2ùYœwì;ý–éÝ &÷yå¼£Vþ—+®]§ë|9.犺É-L×”&]§ÜÆ×¹6ôμ#r¡1 -ÕÍò³ß±]·±q)ý®:C`ÿŒ¡Gð¾'%ôîšÝ¡™L>¿%öSiøØ¼± Û}*sÈÖ.vï%! °hŠ˜ó÷lôœ\g˜·%âKS̬~êÖ+ 2™¿Ø÷pˆ~Y¡v6i»h@Ëp$²Gâèp»Ó‰á¯?´±[6¸_m×"æÜžé| çwæäž½‚ç0ùyÊ\çŽB»ÞaØî®)ÓßYéÜa ³-{pwýèÙ£³ñ “z-ZÓ³úûæ›_û[:‡ÉÜ“×]¼ÿ.4^v‰~íþ‘æê7u;ð§Mž:Žß»ggtú4¸˜µ¼rN¤±/(=}د›q?·'“Éu#±® «nïÍ©k‹ v×ǵHý]3(CK×_|މø:â9ßmàwaô•¾Wpà»Ôϳðä7§§ô‡I›¿írï×ÄyÌ &öé:‚œ?Y;ñ¤èü¶·vÏ2݇C·Á—ËB†ØÜhxV1o˜¦˜mv5X¿F;~Ù™ôÑ×Í,½N÷aŸi2˜|~UîS‹ëÓ‚æ}·ãø©ë÷ZÊìt,yå7¾ŽòÐÐÉ{7o…㿵™yè4î㈙T̶µYWÜdÑ 0ÜM»‹÷»jËØÓûfÐ}‹²z]XìÿܹíÙûò'¥¶Âlσž% Úo ­Ê(f»ÙÌåÁwœÙ#0Ÿ,77õîx&ƒeæŒÊ>vN ý=ÇÃàТòs?ibŸæŒèÏØnïfë#֬΃ó¦lØ®¦þ°þTóŠYÏÚ…cŽj5p`èÆÆ“_0Ó¼&“‰ö” ÷~ê·ë‚sPØcyêU¡Ö{€s ÓØwf,ÑS}^¹§˜ ½þf»Êƒ/B“›Ò’°ð§ÞVÿšIÏ´yŽñÚÙAÏÍQVç”§?c»Sßhz´Íò-н*õð”wK |Z©:êX1«ßäò€‚xˆqÔ:²¾·™úÁ&ß§¼o“ýNî÷ˆuQ-ËŸŸ_§"uÓ ã†Íp­ÛÅ×Ö,ª[êŸ Aë ÚTVŸ3ôôclO¬W™èüt |oÍ^WVÌÞ>ÿöáKÙ½`ù™Ìö;ð:;î˘Ëäžùw–ÿWs¼f†{çwMÎÏdž»’ÛÇóÔ;KyŽÊ •™ùϲò’‘<kf#ÏÅ”ƒ2ûdî|gõýÏ7zέג‡ûÌ,Ä÷Ë"/¥šü7¼c$PçÐ’K×—x¥¼£p¯®žXÍrèZüˆ1ßTpäs¹K›üÙ ò‰q×…pds¤šÜØnâ,ç’û‘»±¹ÛEnî ãLPî–á,xî|T£¯K…ya xÎåŒs9y‡r‰ÏœÐZ0ï9#•»wí¡‚“ª ì{_ν¯Ágæ^rv=–“œYäälf’Í̃@ƒ•K€•ëåø²Ôp|ùbPh°LX•üÿ¹ZrïòÑŸ™ûwý1L’±,XA*ÉÄ[åÎ#–+ CFåÄ Ã°Éªáå(#Ï—‘øÌ äù’þÝBòïê½øÌò|I>³t6æRXq>³…¼ÜÙhÇ #>³“øÌÜóÅÇ>ÿùÏò[º8d6KçÏbÉa–Ž/É`æ9[3[{ú<š»©}þ{úws8ïŒä¹ø2Qæ!ÏAoÏ<ƒÏã9ö¿•]ÿ×¹åqù<ò%cÙ±˜OFrrG¸©®ðÈ&’oŒ³áƒˆ§Ìý°œïùbÇRK™óÞsÉí£%·/fEww“ÃÇHþ -ù/‚°£$“# 3BOžnîä¼äËËÍýA¾8uäSUá 4’CÄ™>žª !ÎÏÂr’ÓQº±¹#«•ð;&ß‘bîÆæ,ú PáRu†òóîø{a‚Iïƒ[‡eÇ âLúpáÆV¶åçÁðs †‘ ‘¼Ø‰Xr`$`åaù⇩ÅÊórŸÙk¸Ïü1(°r)0°ò°|18É¥ê#8öFò©a˜è°ìXa*:,;V†‹ˉ¥À1`¹°6ÆŒròŸeSiÉ&}ªò©°\XJò`pÿ™*›BJKnl_ «ÿ`ïL ›¬Ò>^6@h EB–BZAS–{ÃRˆÈV#|BFd.C”ë4@ƒ¢P {AÄà2&D¹¡M¡…°¨Q‰,%X°|V¿ÿ“÷¾iËàrœïÌ÷œó;ôÊM›û<ÿÞ&ïûüÌÂM ›pck^6áÁ ;LÈö{]Fw²üN–ÿËòŸËq­XƒŠÜ\Éed~ EÑ;Eá…ÃRQKrb»„ˈœnÞJN7w%7.9ÝTh+(j4ŠM¸@´ÂïŽ7(*4øV8±}ÂùfÞ7j,³ðbSƒ%/¶Rx,+ûŒÜ@Ig^à 4 ø…ÏÈ)šÑ,¼!j4¥U¸C4Mh6äÄ&‡¥K8± Miv~V#šÖ+–äÄö7§û:+œØä2r‹f&‡%¹ŒÂjºçõ"ÀЊ®ÇÇô¾‡pQ³›Ûеƒø^èµK4¿…GÎFçh EØ4’ãWvÉ‘çWŸ^áÑ"$r„ˈ–6á2Ò!4 ´+pp%×\ä~É œ_ÉgäábnáŸ3 ¢38ðUrÐù€ác¾]%_‰aä z½xÁd>áĶƒ Ð!¨  "°LÀ}‹ŸäÎyüN†ÛâþþKçqø>‚@b·ƒ 0 è]@!Üœáo²ƒÐ SèÑN&/1ÃBÂEìMb.Ñ,æJN:‹p«Ñb=šÒ "ÀˆætEšY&y 4hTTò{’g]ƒÆu€ˆpB‘ãS‘"9× RèÞhÉE5Ý»‰ì*4wð·¤ûªðq+ÉÁ®C³Û[Ó½tÝ=¾&&á%7»ø€A`^ D ˜·’“΋“N… 07P 0LÀ ”‹p+ &à>b5Â$ø¡’ü@ƒp±Ð"dì ´'B'_øˆÉK— ³ðÒÉ>bŸðÛAèLá¥3  ò…JvŠ*Vá—]SäÕ"¼ì  LòÒQnÐÊñÊN:9¯å×;)£)—å,–_ã¤l¥,•s”2”òSÎÎ_ÊM93)/oÍJ9)å,4ÇIyG_seg\å\“3,ç6ùtk&ýRÝšEËn“A?—?¿–=¿–;ô)3JlŠÙ’OÞKäŠä^8U¼ðûÊ^_lV žäz#¹)Qr“‡RÍóUÍNÇ:èíhD³”i¾0¯1ÍEÅZèk?]c(ãäštS3šƒO‘âFô«½i?€mè>¬ ´è=µ­pµ‘7 |»NáÜTò³Q¹ÄeS†äÞU ¨ÌÀK£¸|@^Éž6?СO D£ð"ô^zÄ ”(B P¢s€_8» ô(N'0¢@Ý@Aïí¯p«Y™ä_»sÞ¹sÞ±ÅýñÏ;ñu†èc»„€EïJ᪠-ÀÂ@Fp€00 !òA„®mAc8AX8xÝÂÁkn Þ DÓX…ƒWƒæ±·h"³ðYªÐLVàŠòâcN ×ùÓð±’¼\x\4œ‹Þ£¡3NCr£HžKS’ä/'Ï%ù˽@uÍp—<¼ä.ÏIþr7P¢A-À´hTg2ÍX”Ü—vá3× Ÿ95¯Y80•hb ðM ÉÁZ5þ¨+ç iE÷PÓ}ÄtÏ,>„€¡ Ý[m|³pd’ûÜ ü@… 0ƒ B X@Ð ìÂÁ«E@Ø@¨fàJ†‚Ã*¼*ˆä ¯ab E¨Ø@h.v:„Œ„€a“"ô~BÇ%<¼&„ (@PPÉÃë^Ù¡®G09AH8Ô]@YÉ·©BXY…C]ƒÐr·&yÔ Œ±|&õ?ý‘³üçrüvõÛ]Ÿ÷[sûvy]9«oõóޚѿåps¢äW¡p¬ÀOï7$ÏÖªFä8!¿öŠ®ßJ•øèžº–hQ`áþ6£Ð"Àˆbs‰‚3Ÿð~ÛAhQ€ŽÉûmj!ù¾ -iþrEÛÍ·ôäóÕölõKçªÿ«3Õáš[‘*yÆ R%Ïx5šÝ|@¦·ÐÒõÌ t; ÂÀ €Šrø€áü@°€ Ð",ì t ;ÂÃB@q‚0Ð#L\"PLÀ-‚Å Ü@…€±P#h¬ ´º W¹Á“ÂÝ$W¹¨BVàj„QŽp–kJÎ$9¹É Lï ¤\¿ãüôŸ<;ÝšÁrþVÎÞÿ³Óo=7QÆþ;g§_ËÔ÷üd«°™^ Æ†Ú@è5'`ÆØd6Ù4Øè ÂfÛ@h°é6:ä…àÊ"„¹c! Gî€6\ò¨ŠUIÞj|L¯]Ýs…â Ð äš$rã¡w@QPn BQYŸî±R‘—_ Ì)ŠÌ" ÍÜto δ(:Gsš{‹ÏGñ9A„^£FšP„.`¤sp#K”È/P¡0­týТ@í ”JsÐoôŠ5t(XˆÐ5!È—8H‹²…Z³„€•îQEéuaz˜îWBé÷ºÞ‚®cE¤IPÑ?ÇÅǶ¶åúð[áüo¦®ŽÍM*ñç´²_Ø%|&ÍØ[þqnéé¼uÓ›ólW’™îÚØUi{»Š9‚_eeÜõÕ3×.Ï}–Q3®h&—çÐãäãqέè Þ”²Š¥Œ8Ú½°ÑnvyÝ£K .ïà&{éÓK™lB&™B=\š#736'%6O'»¨Å¨â–U絕}"+ØôŸ?¶N»›UT¯½ÆN.Í_ïÁ^˜ôRÜÛÏyxÒØÙÃ[frÙ/ÍïPeY륩ÏäÚ6/c4Ý£©a7#ûÐâ†;yùòr'Ïë͆ÄÓ ÏýÖ58þÙ|Ô¹~e ™FøYÚ³»õ7;ü—Gògǵ+×9µùÓ¼¥bþçn6*#ܵ˽;ù3µ›^i¼b ëœDÆ4¿¸nà–6þEÜÒ.6wKš£Õ*æ7juä­ƒ©ÇLLú<1OCS»%;Ù‰/»Ýõø»Y‹„ÏÝûÐNîzu䀽‡±áÛ5èá;œ/˱qÉÇ›%y&%¶ëë{(cêÃKXrRi ÓñÝìýÕo^2m'_}­ñû†s£ØwÓÖlÜÚÃ¥1Å6.ûœä¹6’÷®9“¼$¡¬·=ŸÝsñ;S•9xf=ŸXÈ¿jÿ}ò©Q¨ÿµ- M÷ðµÓ{Õ­­šÁe/†<Jò"¦ŠyYKuŒuï>Ñwk´ìµËÏtî?ª„}›°ýLrV!ïVÒsì™ÒLÏÏùTçáVÿØ=/Îàòü0Ùÿ·6Y_{ÆUÉ'l'û æ³Ï†Þ;ðÈ“%L5º÷±û&òÒwœ[´ÎÈ¢Ç<üëõ´13yúˆ®sub²'ayÝ §ÚÆæ­<ßúRíüªsmâ4åú#öùÚ2½»q´yÁ¼¦|+~HÇ9…|¨yý¢õeýÙËÂ3Óñ8ëÞ!±“-6×KÚ¯&ù>…ëIsÈóØ?ï_ýžê%ìÒ{Êk–òA'ŸoûÃÖ^lÀ7S7 ŽÎŒ¯ÅëUø’ä¿¥¹ƒ~©N:2Z5iŠ4g_Ç‰Ž‰m™Ëêä«éXTÂv®z±ÍŽõ…Ü<³úœŸY¬åâù+æ>àá#מîpbú¬™×+õKý*s€ÍX×ÃiàÎ_Ù_šX—,:QÂ>©^0ñÛM… ܓMxwwZµ ŸßpÜÍž _—üõKóÕ+¼Þ#ª?Öà¾ãU<÷69 ^OÑn:’뛫äÜöµ²ÙžAy®ª<÷gft|“Ÿ‡h}§•ëû†Ì??xyJóKY×Z¯\¼q¿´ëO+7cÏQ vððkkÞ˜›2‹Y2ºŽáZû.%Ï6½v6;7`ÑÉ«YÒ|Q5Ö…4n0~gN^‘TVÊ6•wëÛuTï£:~@±v8ëwà›â ÆÃ×þo¿-ö<”¸/5»q°^l¿¾|~Äõ#‰ÙLšk$­¯ÇúýfÞP6mÿ¿ÿrñP›fðòñ²ñû»×1œ=0»iÚ˜6.=Ÿõ'yïëÆêÎñ$}Föìä™_ô#Í_7cýìǘ3ûY~tñà6Î'ö°¯œ¯~úÍ"ÞõYÝà ï eÎëCnþØÜÃ_¼wkÅó-=Θ—ºrÙ°nâ䕹[O?Ç~\rêCçfyuhŸ‚Â"^нӟ?îadÞnq0»Ž‡Ÿ|÷øøçfÍŽõ½äŸ¹%÷¡ä-¾1¬»uÇag7LçÒüº=¬hË ïÃeE|Ù»/Û>xeû¡eòá^§Ý<÷ó¿>ÒÌáò\gÉ[}%æ£ÏCÑzƺ’VÏÆß OÌÙx×^öÚ¤Î[T5|b¾ìC,³fÑ„ts^'|¸ìÙW^Cøê±'Ó_Þ~ßK÷1ùü­g¬;èÒÞ¶^³ùàé›ßÔõßËj•®r ¹÷jžŸ7ËÀjÄ“‰ÅÍk¾²´ïgÁ9±~“}Iòþ-UNyðËib~ãh=§—ë™ãÓ)š>sùâëöqž¿ïe ×%íâã¥K ;ÎVËfzÒÜT÷ð¿?b9Ù¹n^lÿžÌÌÎì·Føl°ÎÓ4¯™;Æ—7Ù¾—Iž{öÉS[?êͲs-eÅ]<1o¯œ?Uû:©Š_Ru;¯yð±·¼Ìã³/ÄÝse/ûrÂÉÖGÆø¸4·-›ÕK¾ÚÎkòp©îæÄ¼•²hÈû¿ŸžÝ€Ñ³›ѱZ9wÕ»!Í[5cýÚG®5Ýôð^ÜÞ¾ÏçÖCÙ‚—~ê±¶·‡÷Ì(ßiœó Iç€xVccï…?žì,úBò2æcÝ„»ÕÓ5ûñ>4î­³Ÿ¿|ßÙ­>þƪ«CSÚby¹‰ÿÜÀßÐÖû¢ÿ޼ñØË_§1*¤›ÍeiòOKWt9 bË›ÓO ŽUêAuOœo±ò¸7xRïøG·¯Á×¹Ù×>cn1Ÿ?iý +>ÌF BçËÜüP§‡ª ¹ûúø½?ùòøŒUÿ}¤Û€Š¾•½Yò\ÄhÝb݉i î}j9ï>Í8¯\µ!tßÿ¤þ.®{ê£AŸ}ˆ‘} 9s~Ïš»GfÌõ±|®êdO8¹_ø–°^Tï›¶‚Rõø>U·=ßgêžwÒwñm7’ZõØ5íMƒGÝ|u5ú땬˜×÷z}©ñÿ;._·­ýJ>´Óº—ŽŽÜÇ4g>¬¹­ÿ.~õX|$·Åýç3ná—˜ÛgÅÖÆƒí“+¼ëò\dy>h´>Û—ë_ÌŒkªl¹Š/\6-£ô¯ûØäeÇf==i×v*úöެɭ[ׯެX‰bÁJ±€ Œˆ 5 X±cÇŽ•ˆ”€ ;v,(XCÅ:£ `=4ØP,رÿcæ3×úÎ>gÿû”½÷òºžËõ­o1ÞŒ1fÍsñ~å¢ãvä¯ú2ùšO îóåõÏù^ÂsxÛ"Œ›»±Þ¦ õ÷“’5soL…:/v“TÕ‚$Ü`˜èe½ã•cHßï‰Û›ùý9Q›gòÖiãj]ØOVWÍ?¿ûh*œµ4÷Æ…›Dæ·²‘•ÇHHÙ!¹ò°R qêöòs’] ¹ywíCàëDÎ¹ÓæÆ‹>PSY¹o™éE‰©`š0wÔ¶ç7IöÈÏ;-u‡þfûÝ{~”Çí•êúÕûý<9%óñÝ·‹ÛUƒ ÛÞ¾éÒ¹#¸j°ŒËˆñg_û©Xþ.‚8ésôYA*¸Ý+¨êÒèÉKgîeï§G_löPNì“`ÖóÔßýôÚµO¿TýÍÑãë }îcÆXybÎídݻ۷&|N…‹ŸÛôtt‹tŒ7{ù<¸êLÁ3XŸm«Jˆ>ÿI‰?/pj’‘Æs¬ÕH‡èÝ–•—ß"b;JNp‡gZœ„œŒÔ>ˆÀ?øÊø¹[åt6;n\füÓ`ÜMt8µ:DZ·ØtæmÛ4˜þ£S¹Ò£·È¤ÍÝ¿ö9:vL0-QÊÉŽê>½ƒþÂçÞÝðvEoƒz:¿ÉZ$yA.Bt*÷=d/ÛÛî0I~ßH2·ì·œïµæÞ-â¼Ú%\a@rfÊ©89Y—å’YÑ%X×7^O›|êkø8ùgªÍt;Ð箋0î$- !’¤,ϼ0> vXBþ²w·HëÁ{n%–ƒ :fVº(g}5X—ÿŠEç{¹n¨ËüTíuu Í[Œëå9ݵÚùH²pé‚„‘ËÓ ^[kÝ&Ym*N{kt:=fîîh9sóËÄ7ׂuãHk牣ž úœÆ9Ÿ÷1­^¹#L§$UŠHƒ?¾»î2¿M*•5ËV -†p?æëòoUK‚ÿÂ©Òæ%Æ™©5h?B\=ÿöuJôl³§m‘ø6iObj4xï9f5¢ÊÉÐ6sê'‰?¿¢¦;n×X\ zIûá ÍO1žgäg «#äÐysê¾NƒòV“U“GÝ&§ žÝÜl0lµ¤ #9é^z(ªËïß/§ÚÇa·fÔg~ìÌ:m;k¶§;p?{mžbü¥òksö8B&·ŒÌéY+¦½:Sý΢ۤQdI?Q…¡pgáé-_r2Ö&òâHÇ`/Ò´¦sÝÑO«êê’ûÑjóãîkKÁ·GH5-8;̵û¸xÛm²½xv—ú….6¸éRÆÅAy]ùÁDà TfëýrB>vÆõ²–/t„ܱ{ô­Ð5^Ÿ°h×…ÛĹÁž©m.¹Á‡µßôˉW…–IÛbƒuCïKw76ž3º4™¼yS¬œPêîÝ.AºuÏÁçVàõH1^_Y‡ioÖ!žVœ=¿4®|>4dYÀr¯zÓE‚1·&Ûî»''ï¶f.앨Ë[¡ê–ÙçˆÀx™E™‘ð9|ángš»;ô(ð>s‡t­îéo•ìÆIKmW?—“ÍÏUÏ—ÚéÖ?B}—»À#Ýý÷5„µ]T›gå:,}ÞÆÊð„3h§×œ…üÅ×Y~@úÊ+é8l0èGe›I…wÈå”xëÙ’qÜžç6çàö§ADˆo _úô‰üö!íÂ26®Ì}—×â.ÆÃA*ø|ÖMÕyO^îM bõ^Y·n¸Ñ%BÍêsP?øÁ³/Žc›£›ËEdÀÂ*Ž5íî’ìeªOk€¶Þ[Ûî•“ÙMú÷.) ÒqmùóZyìôp— ÆlŸ]àˆ1î†r9ŦG‰YÞ—U3.fÀ‹˜NîºÞ%-Dã+€‡½c‹ï‡Ê‰o›®;¶ f\Û† ¬*°¿ë–áÍxaÜÖžÁ‡Ž/Ý2¡VÛeíßÞ¼K&\%/_æ íq&/'|î4ûaL¤ûç=;5Ðqì…qŠû¨?æ—ÎÎtèv¬+܇ÚXãç¶1ú‚¢zå`ÂÏ—¸?=ß·Ö“B\Ưxëµ¶ûÊT§ú׎g‚ùvƒ»lî‘¡Cl…|pËÕjYÈpžþK´¹ErÎg›ç…àço/•ñ0®øó“Û‹‡e‰+ge‰YI¶­FÝ#5fTíÓÙ_rÓ‰k·ÈɽŽU%Ãoîû/ø7‚Ž£ÃÒŒ§´‚saÛm?óHÈ_Œ{J=~ñ°æGˆD:òu¥,¨P{ZÎë¥÷HÍÎÓ ÎÕÆúƒœ Ô‚zçoô=ùo$­tçZ÷ñ¹¿·ýÏ7²j™‘¤Vƒ¡iß­²@àÍÞ#¯ÏÏÞÖàñȹG7\夻L2 8_šÓøùÑç¨Î'ÌÊž_`ܳŽë[Ÿˆ$Š«’N{dAÒ…³+_ÄÞ#»:/»(ݬī֋‘“½u—{›vþyñytûž‰’“íÊž_`Üâ–X‘dn­ìµ—gA“iéÆÊ;·«{Ãrãëæi—”äØÙÊçÖðs »zÂÇëR–ï„q{÷š9E’­}s¯ïÏ—'vï)¾G:Šm66¿—“—§µr4“éø@œŸ-ppÚ‚êë ¬£5++liÒ/k„Ä—Ö3x4PèËVÅbÁ×>’ôó®Ø$›dAµ–{¼VP‘Q1U—Û9æ%ïþÞûæä{ûne´’‘6M—­ þÜQwÞ5äì&÷uãùy¢JÈgŒ›?˜ñG’vCý ^ÜÏp1­£"ÇŠâêÞœ6¼NÌõšHNÀ^s~bÛ?¶Ðñ-ÆÖ©±nÂûîeüôÅw ÷­sõÜ#‰ß´å‚¾eAAÇ6÷ZªÈÆ!;fT_è %7Ü®DÉÉðâýíçÈt犜ç°{kAÛƒçû–åmšÕ$þ ©ÿ 7ds6¬-}& Rßõ ·¯ ý'4ìW{…œÔÖn0ëÎç„þß z+ZK¦O7c笯v‰×š,ö`Váýkðuú^=øú2ñ¼Ñq¸š õ/NVR‘;‹èŠw,¬Õ!ÿÝ&9ùì9åHÜÏ Â¹RüPØWmÂ:˜qr¬9'í±ŸO3 Ê_Yò©D‰ùݹÉÞ÷ÃÇÀòýÇ+`ÝOm@ÉA:®ßçà}N“㮼[pjÇ‘¢4£3Y5D×ÅïÍS‘Ëë.Í7ië ½ÞÏW¯Äñ°³í2þváýtçÓi]+éU†O)Æx6óæSÔÙÝ£U¼ÄZ sVÕLÛûVEü*ÆMÊñwƒëV›{9Þ‘“ÝÏd+v¸“þüki¦ÛG¿jf/ÝXÝ© OŠ㎢ÓñÏû‰°VÃÑzí_w®œJ–ž¶ñôtßÞ}gäâø:zGãû£ùøÚFW/°üñ5ß5ÍuyÁóOØg>?)¾ÎöÊ«ÜWí'v§c»ÍUCò’å×O%£é,@>ޤ ³½/'Í´ ‰`Æ»l­Û_8•Â>KÆësrúýy¡ûÈžƒf羫¡ôÙÜf©„Bµ†´.ÊÅ–±ÿE—ü‚‰°ï+Òq>„sF°4 äãwÓO:>Ž~Ý(ñu:Wìa>áÈ^"ÌëÕÐmqg+{ËTrèÁ¤í Bõ]Û=a}Z¥Š* k¬ã rîªÀ góŒ÷´’zÀÑS{ȶ#òÔСZ¥3[{¥ù¡Ò`¨œÛ̵FØïy?gæ¹ÍÀnéË«3‚ÃÝO+u)'ä›A\ÏŽ%^6³viÂq÷M©jèTaÁNœJ¾¾¹2sGwgð ˜ Ü|Çq2öSñÐ`]}pQ ëOs£Ú™éÖ_Ú<Ƹ3ž.¶ø¼“,°°ìPï¹¼‡F½m::•„Ùþ”ÛrçÒr¶OLø½ Ît3„Ûçæ²óeŒ·"áqÂp»pÒ£oþžA•sÀ¶ÆÚOͼSÉüÁ†@“K#ºfȉg’<1üC0y=údÝZŸ×- zZv¼ëÆ4‡M»V^±Êƒqß„qË _çó+‡Ûã¶“´&¯ÍζÉ£±ÇÃ#}SÉÚcç¾¹1<ìL÷µNÁu8]ÎËt<þzƤŠÛáilÜMú5ư¶€‚ɲnâéQ.90wtd¥¥Á©Œw2 Ò4gΔ\”“¶¹VL‚®×í½'û4ã‚,»ÅÐÛ¦Ðó˜d˽‡%}>+ðüѯñu"g¶y0kÁ²ÍÎrà`³Õ!!;RI­÷¡ÎS> …š4}°.…{Á:.$?›Ùê”ᡟe9©÷تÈ÷=7“;’Ópöes$•ø¶ªðdt’3œn{¾ówœñƒu÷xþ Ë÷uHCÛ2K Æí9ý¡¢ÚîM$cÒèÊ{"s lsÍQ?©$~ÆŠ[½9íåO·7pÞd08Ï"àó³ '§dOÓŸõ§sB^w-7ȯíëe°‰hq —r ŠF¾jååTòøs÷9û²÷C™Ö%•,ß»ô¾µ³#ʹã¦å6ïïs¯£nø?B~c|‡–GúÖYO6uøØÑðI´]²áìàÔT2`S|ëòÞÐ.fæò ßv:½A$ÓÕ ïOË7‘Mºu+;ÿÀ¸‰ïžåùG†’íáÓZŸ,Í ¦íׇ礒½RãÁQ4‰!Ây1ïÿuuó„½­Ê/ ª_&7;¨üÒb¾_¬?ÎJñub~L^W;6˜íOç‚MÛ'‡Ÿ>L%F–Ña§9ÂGéÒ”­µcˆõÍ.ÁÁD8G­Â='sà÷„us¶0¿Æ¸™¢Û¯Æ½^C\r›¬˜` 5s%Yõ^¥6üo¬o9³f Y ’9yÁ$à\Á­•óëéî¹-^ii7c{ë2ûÂJŒ›¯~éôq¯Y¨ äÂøzï'6ü–JzZÜýË}ãgÆ]Ë ZZfý^Çñ|žIŒîì˜ß£LßÓ`ÜùÁC&?-¿„ ê[Ú°÷¤\Xg¿Õ}oõ4â42'íçD80¤Öþ…-cˆÿ¦Ýb”¿ëóiónØ_ï_æ|Ê [±¸jý¦ãŒ6Í w°{Õ‘æÂYëÕ4MÓÈ;¿+>¹‚çá%Ò¶1äñÁQ}ëm &ü<\¸ÿÒ¬&MÙxüeݾSÊÒ_¦m=À¿Ù¯JþBÿáë¼$w‡ìž7ˆ¤–;9uo.4qN,1¶J#†sÆ6Ÿ4>üÈð}Þ&†\p4í8©O0q:T¾mž²ðù¥°¶ÖhóãΫàëŒÆÁ ëÅOå„ÐùÓ¿;¦‘„w~ëûú4:6±} ©Ó þ<»Aºþ!ìŸÖéò>A_Ë›êøLã·/žÑÙÆ„{pBzá댔¾é>wÏBÀÅ‘ÑÆ¤\XÕ $»ª[9âbSyøša9åðýá=bˆÀi Òñ …Ï¡>¼w)>¸>¥)[ï¦ ýã¾Ø»M^ÇvÆ·Ò=ÿùcŸm; 1/ÃÏŒÀ¸X½3‹A¸Ï“ Ù§ÛÇ´˜“F–†õ™^Ϧœ­Ngª1¤€ââ‚ç—ñ~uâÜæôõSíËp•wÃIš°ka÷éÍ *åAiÆ·ì K҈ϠŒ©ŸÁ´× |c6?›ƒËƒçÊ çθNøÙlwçÌþl]zTÈoŒ»(åŽO‡¦ÅeåÁý¸…^[—§áÔ *Üy–ÿjY y{~Õ•9ê ÂçMœë«Œ™voÊ k](Ý5êL–;|:ás4è^,Þûù×¼«› Ü¥ãÙ¸ÆypzC§úS¤iÄ—Q»‹çÛÁ©©{ß®œC.Fhœ¤[¯óþýñaíóÌìËŒ "ŒoýpÇ×¥[@اʃމ.Qcý±>ãwº­šÔNÇ-ÄÁãaŸ ö9Ö¾o+ì6`ý…í‡`ÜKGªTŠôÞËeý—ÙçA즰W-‚ÒÈÑ{8f‡ mˆ’İùBÛ/¯§ãõ-_:xÍËÊM`þêúoŸµöo¼0î¼gW,fì€ÌÓt£ßoóêMN‡b\»“ýäÝAàíŵºõ±³•ƒtûX|ŸìÅo{æ©Z•©G)Æî]ì„×;†¬áž+¥‡üÂÒˆ§¶`^³[µScÈž+,¦bóêúÀŸÇr‘÷…ÒMVe¸…·â“3+7~Ý ‡{(ÞÝòÊƒÏæuÊG…§‘Ý+¦‹¬³Ùà ö‘kbˆçL󸆩Al}n Â=ÃæP›^‡3sæÑO;,eï…Èú$¥³ò€Ðÿl_ËW[ص“þÁßòúe²þÁ:n(Ï;~þËûÿœzô•Ýó÷´y¯ã©NµÍØ5Rƽùé›ÂùGû½»Á›_ªŠ!Ïå†þ˜ÂÇ™Vºy°Û‹WíÅÛ” lŠÅÊ)›î*#@˜Ïå–K•F>›¸øM¬j -£7߈Ž!æßJí‚ ç’ò}-¾¾k³Àáá¦W¸È?8f¨TpåZ(Û§Æ×iv*³ù'ÇðÚw˃ƒ[ò`GXíeݧ‘Ãç:r·í9–ÆïÙ×§ËÚ!Q¿N|y»ã÷¾dþûïÓÙuÎCÕ¯o)ƽ:ØçE…‡ eü¨Œ¶çòàú„e5"¢ÓH¹„C,jAz_<5^ÞDAî}i·¡Âé ]¾p.ìåÍ»:VØ”é{wò¦6÷Ëí; šË:¦%å¦mìÓYGÒHØÍ ü:¹ÝP±‡% ._ÒÍßù¾oûQõR«M³ÖõÿÆ_.bçl¼ò_§Ã§[‚#aú‰{_¥æAü.2¼Î¡4ÎÕ&£GûŽkß]¡[ÿsn0ÿ<Ûì\{êJL/!¯1^‚ý̳c½ÀùN›ªßÕ䯡ìõϽi¤ËоMºÓø4ÎtR)ÅSññ°®nÝát§fÏ“›ÌXÿ(tÖɣعµð¾ l‹Åå§ ?UÒù(¬½’7÷uLßÒüaí]i$²íâU»k·#•æÜÞLA„ùQ°Ž™c”VÕýSV7·…>ñlºÛ»®|vNùeO»û5eíw`áû¤G7m¿™g¬ U'zõê÷óæã­À1v*Éc\'íArŒŸp凴J>ôZ-~ßgg§z÷´‚ª×ÛPAVÞ®RÑnKyÒ6Ó¡ÙÆvºs¯×6­~8,³¯ê…q‹›çŒév/t:b’FSo¤õ:F*l¬×=ê`/pžë×¹±µ‚ÐÓŽõ‚tû‡üyWoÚØËò†k™ùã ûKÇ@ºÇrB×ù -GÌçÞËÏ bІ© ]éu*E »oÙ^7ŸT6-Lú¾Úæ÷þ²G³i<ÊŒ·ø:+jŒÜ3£Ó øºë‰"µG>xžàØ 6ŒýpþÅ‘ð¡Û­)‡(ˆß‘qg ×ê¸ã|¼];iŽáþÏb!1^ª½‘t.5{§é—wîyWv¾Fø¾´,‚^ÈUÃ'+›ÞÙH´ydPÿ+kðu´×wžßöš}φåCñíð‚×Ê4’ÿëLÁy—Æp|lÝ´Æk°^N4y³a{Åâ-Q WìÚrÆÕëUbå‘~aMº»šF<šÞý>£–ˆ|ž¿¿i%™‚ÐÕ¸M ðs"ü¹×â½ïŠúËÁTûÅ…|°²ÝZ|_϶J£:½Ÿv%AÍ®&;dÙìî#'î~NŒ?gÓåkªÏM9µ/;-ú-~óî2>!¯{‘6>©rJA¶^´K–¾~Î n付 â10v”ɳ¹ù0øPÝá]âÓÈ’ìœ0¿©bâ¨m8 ’L¯J‚´?'ÅŸ›m?{½áõá¹ßgq>Ü<2æáØSi$Q¦¾°k‡ëS 2cØ1ÁÂÏEàÏMÒnè+ÀÊëÆˆŽ«ò!5Zõd>ŽWnÓ~ éácK´é¨Pz{¬§·ðsJú<ÝŸ™„]RÅÿȇ1‹Ù>ŒýqÉ[ÉvK2hù ç# ²íM£•¡ðsü¹ý)[Ïl =ëX {š»>¸Ö(ƺ”Û'æÖ‘–ä—øÜ쇄žnÔM`ŸŸ]±8gqncãu±Ð^ ¶Ï‡+»[9WÁŸÛmäë×ИŒif0Ïý´‚4ݶ;*òûüðçèä%ìS,øh´ù025ISîìÆ6æÊ‘°!ïÌV]S…±,ûcuá}Šñçûw)ýv-fn¸•›C"7>™Fº:ÏŽ^s±3´êCgܱdmÃê}ãüŸß‡à÷‰ø¾›°>¿æ ¬—êÿ~‚öóÆ×9Ý{ü²o âáÁŒ½ MOçù–ë¾úÉÓHÉ¥cï3¨²´Wx=ÓX²(yân·®A$qëf Å©[|ÿ÷m`¼8Óv-ÛŸãÇ;ÏäŽöÁ |NÚï£4€¨F®ýÞ¶%†Çcâ­‚Ÿ—óõ…°Î7ú}êeA>·Ï@¹a£N\ͳÂãÒÈ‹nÁñŽ¿:ûP÷îœN±dè®ÊýÞ¾Þä}B›/çÍuÿ«ÎgáÓ]ïã›Sò!÷ôöí/±ßýº¹7M|©Q·ßÏ“÷3aÿ®;Ÿ.pàÏYxŸælÿ ¡ÐðuvØü¤ú,Ð]¾Fø:Sw¾˜Ó'&Ìýfó–Ç­Húáz —Œ%gqÖZ\?ˆÝ×±¾ÃÏù=;~NÅ׋Ú|´/·xá`=$v™u 5®çÃòü³£Úb½-®‘:à½IûnurȤXroj泂øx®[7sÞµ0®¼c÷-uçüÚüÅ×¹“"Zb|$’ZI1Kȇm{WIâxà=Àkba¿nd€ýœÒ‚…±¤AHÕ¥ ‚t|mλ{muðgÒ;a?ÚÝîu‰1þáã —ì­yæÖY£¼y ŽûW¹èógý>¼ß¿+±«ô%'eQ,±ò ÿtçG ë—–ºu ߟæÇø}iþý$mžãë4ÛôÆõÅæsЧB̬§X‡µ×V¤Äy}9ÿûÊõIxHËÃs¦Å’Nà³­-®{…õAg¿Z¨£Â÷?±{GÝA¨á^½_'Ð|¼Ê±üyè[PE’ï ˜ôs7®ŸœEyE®uÈ÷ïŸ5ñƒcI×ìé‡#ßý~^üÜS8ÿ~ïP.Âbbߟ„õO×2ŸK¾NóBÓ3¼ÏÃreÏÉ7ƒóaØ“¸£÷¤‘WŒî\Ú¢#èÅžXÙ‘пÇþù ëʧ½O8m/(øàÐkßvÓ„›]€÷ mÝàë4©;±stîy˜¾°JL—À|Xc°ð}ö¯Gõ®Ö^ðÚž}¯(–li*?~cøïu1ß·âûxÂ=èa]q¡Ñ£Ä—S.Àš¦Ëqüu/ çãSè4¯q_ ·Úâ†Ç’ØU]¬ÆÊuû›üóö«Þ:ð{SÚºèY,þÕÊa‰ÿ +Úúy^><é#é¶ Ç¯±Û¾z·íÑÿòÕZK.†Õª=á{ãŠ7Õ}_“?aò½0?ĸëk9]½¼÷" Øø ïާ½(¸ÇaYà¨ô‹ú‚ª]ʽþ§bIt]U_uI€®yÚŒ³—Û§oûâ ÜË-æ‡×Î}kÿ—à™ë˜ŸÆæƒ§»—Ùái$®úÅWŠáBæl±$mAõ±!â@]^ò|¹oÙä”é‰aÞûE˜b\ËSB{^†k;Ïæžon}8²òn1ÏŠ~×ËÌ FXôÉ÷YȳYå²ïev‚•›ígµìÒ4V;N·9Vèí9RRÅ[àÖK1‹½=Ö]†×½ SvHòÁǤÁ+{Uû^` Ø9à§—æ\,i´Ñµê°¢@Ñ(©Êâïmuó{á{ÇO„¾÷NXï`Ü=,SE•,ëš¹åË€|è4-ò×î;iäøP“ûŸ†6 ÏÖ^j8>.–Ê»–jD OÍõñh|ÖçEÂý¤Â¾Æ°e­•|»wï™+î4-¾•F:ˆ5 ÊiM™¯lñøb,ùv›^ø "Ë ¿œm ¼¿óß_¯…ç Á¸oè2®®ü®æœœb}kcÇJ“p\Ÿ—”ñÓœôé‘zÊñf,yÝâár¿5A„/-è¼çŸOú÷[ z‹ß{t±N«zÚ$mŸ &Ušìv9ô:¼ §ÏÈ–¤ÿ7e¯{±d[ü· QAÄÃÈøÃ‡z°¢mßžëü{eöŸ0îíÀ•pjÍšK!-ò¡ÒÆ›Që¯ásºîZë•Fì0–¸Wš-6ˆ­“,t÷|x} ãê#Ý}Gmþb|uz-¿!YJHÙÙf4ʇšµÄqW҈З  vül“JWbIÜ’þ£·õ "bêŽónÁîk63èøäºC¡ƒþ9ÆmÓ\ÒýIµ+ m÷Fù0îzi#œ/_œ]roäÜæÝåsXÉ¥Xv¿2P÷½T>Þw»ŸŸ,ä/Æmš^w»¯ø Ó݃|X[ðè­g"®Ç´Ü!r¨¸î„DŸÏŽÞ\a1ŸXêîåuœãPæ>%ÆíÔúÖ@ó•W`ÇüËêåoóàüâXÉŠëi„Ÿ»›¡+ØX2\œûË`f ¾_Ø #œݸf«/ùû=/,<þñJ2øÇ‹ò¿²%÷\¥ÏRi x¼Q–B óy“2Ÿ7KÆWU3¾jóP’0¿7c)pÏ7ƦÜAJ£Ç.Ñó.¡Üh',žæ]âÁü· ôü“8w0Šq£Gß»ôÿm'æ¿­ÏÖ0n´”ùÂqntc)p^îmB=z¥zÞp2ƳeÜE°_ô|”¼˜7÷¤TéyRR%±HðQ¢ X ó‹£Üh–‚÷ õ£ü1%ó?¡Ühµžÿ‰šyÇ…1n´D;èÁü(M˜ÿ6÷£ä^JúÌA‰sPÂX8Ô{Û—ùnëûPrvléß°Ì?Á‡ù(iCA˜ÑJæGiÉ8²̳NÌ|…5Œ%˽…©‹’±ÃþàÈþ'e óàÖgr&½UBÙƒÿôôzºÔà¿GO7dÏ*Å@ðò.'°d©÷”ŒyOÙ2N£†qÃY1P>õ¡2¯,x s/*_Æ’¥Œ³0T‘K–2ÎÄX4áŒ%+Áâ‰bäÅ<† G©Ç8“3–¬„1ô=†i¡é{ K˜Ç°>K¶ˆ±d)ãL¥Ç’-ažëÜëÓ¥FYRÏu=Ϫ0ÆÇ3Ÿá¿ããpž·7óæÞxj=o<Êõ¦|Êõ¦|æcEY²^¨ægEù8)(cÉjP¶ØÂ˜¨˜ù0¦gœy1<óæþxœý­Ï7óÐã›y0žõ–2oa}o<Χ¤MåO&Že„MÆ—ñÀ‹˜/¼ãȦ°æcÛMàUF1-'lFáÌ+ž2+ÃYcòF¥0ÎYø¼Ê¿óÉS1Ÿa}Îç*Pž,åœÑš¥hOÿ÷ösÚ»y¦½˜ö_ú(ißµ0øÝkÿ_ì£ÿzè¿êŸ´'þïîƒôó0|ÖÃÙÀ/a¾ FŒó¨DUxÚ)Œó(c>ë”ƽ>m1qÂj ìʼ¦z>Ì;N¼—KÄøŠœçõ‡''÷7W2޵ãXëóÍÇZcr†£JPN˜¤(CSÁ7r¬õùŠÔß\ÆüÍØ''÷6/EIËÖHÏ—Óˆy›§0&÷"Ö÷Ó£~Ä (#=~5å¸pObK=Ob[ÆÃ-é$°«å(C ú}5Œ2büÊSü“ݽ8©_9g²pÍR[­•‚²d|Ä”“=Ý“Åÿ%é)øöÑbõ`¼ Î×â¼ Î×úg>øÿNûg>ø¿·š°g¡B‰0á}Ê Ì[sLü0Tórg>É”—Áx‰ÔÓ=e‰Eá‹R¡,±8¤ŒyK[á¨=æ--'T+”eȘ Œùí˸ߜ±•À˜·Œ9¡ï‘L½Þõ=’=˜G²>ó¶„1o)cK­Ç¼-eÞïÜ¿TŠÒ lñÁ„¡ŠPbæaZ˜áãï¡Ç÷a^É”—èÛ\à%ÚbQG˜ LqêOy‰¥(/êiʘ·Þ(%Ê Þ¥B™3æmJŒ œyœ:1SCÆœàŒ-ÊœP1æDØß°õùZ^z|-/Æ› ^É2æ•l‹$ U¤ÇH4ü>P ʤ ý~ƒÀ+§l-ó®ôÞ¹À»U¡ éšžñ©orJÒÞÄÿž1©Ïª!6&”Šq¶"þà%аYù¢T(óžôìCàNXþÁÙâÜ z…r¶h]Ñ?ÿ‘=öóÿˆ^N{7ïÙÿÞ~ýÒ»þ¿bþsÎùïé³ÿªÇŠXœRÁ‡>‚ñh©½’ùÐSŽa ʤ¢ÀW1Žaó¡÷ÐãúDU8´J=-çù2­9&°¥fþóa( JÌXâ%5Ïg_LjÊ[…2Çä–2¿gcÍrv™Ï¬YÊ/óÕãõa!x£”zþò)ŒîËXáúlBKÆ × œ°`"P¥( NÊ‹ÇÛT`…S6!åš3¾,õ—cA…£JôüåiqyüÁ—Maœ_Æé±mûÛZŒ½2œM¦¨7´eÒþ7#œ2z¸?´­ž?´˜±eK; |ð”±·õÀŒÍ£úlžR”SÁ?ž3wlY‘Sfš eËØ‚¥( }+|”eØKð¡æÜ4ú­zC‡sÓþ™Çþ3•ü÷˜Çš³ßUMÿÞ—±b-1ñÃQ%ÌSŸ²K—(Š1½Q*”-…¥FÙbqÈ+–²Ó"P¥†¿Y±”&AE1V¬*eÄXœù-eÌoÎNS²Âòb¬Z`Æü¦žû”ù…2dÌï„?X±¥ŒKÙi=V,-Hê߀aaÊPE(1h8ªå„…Á8Fœû­ïÇoÈXFœûí˸ߔ9)m.p'ÅXÔQ¬°¹7?-poTcÅú RP",x”eÉX±%('l¨”AcÅzë±Ó(÷C͸áŒ_ÉyG¥íËrÓ¼õ¸iÞŒùaŽ $ UÔ™úì` T ʃq.¬¨/Æ@asñF©¬éw–ñÙ1Ö7e¦YbÃñfœX5c~;u£ßßE• <°E¡J»ÓïJà?£Œ°1ù¢ÔŒŸõÓÜžÞKÃÿe‰MKÆø¶ðÓ8ÿƒÞЦü4ZôÏ?=ýŸžþ5ïý?Ù§-ÙKØUNàìz¡RP"ƸT¡D¦·š1.ÃY’{é1™(ãRÎ8rZïßp䤌#g‰Å CiP¶Xá¨"”ãy—2ž\8JØr2”%¢‘¢Ô(K,)J²Ä"’¡4(óº#…3æ|ÿàýRΜ9c\r&“e‚çƒJA‰°ð|—‰2½¥Œé­Ï¸´eLï"” 3ЧJŽ2Á"õ1˜Þ”q) ŒKË3¸å„Å*EI°ˆ#̆°cÑq–°Šñ™¤ŒÏ$ÆG• œÌ©—=þ\;êaŽ?׎zXÿæyS6Sª¨õJÅŸC• œƒØ€±¼•(l >¨ êW%p™Ôÿ‚ËD›†„ž9aÃðeì%=ö’Ø–~—ß6”º½»/p+iCñ@ÉQ†”ã‚J@aƒñF% Œ°Ñx£è­7Êxñ“÷Ï|úŸÞ+5øï1Ÿ¶e¿‹e‰ /-'°ˆm1ñ#P¥ô¼Œ1>K§Š2ÒM°|Pj”‹B†Ò ÄXaŒELÙyQ¬P8‹˜²ó'ö^‹P¶˜ð²r3YŒ‰Œ߃±IixWøî",_”å„E†*B9aq„3f²‰e¨ÇL6Á‚ñB%0f²e¼£DX@Ò³”òÞÃïÝ JʘìƒJaìR/Æ. ì÷ãßì÷”?˜É†Œ™…*©÷›™l„é…JAYba†£JQ,Ð(V¤(9ʰ±ÀƒO@Qž)J‰2ÁâõaLxKÆ„çlSÊ„§lS uÊH„?‡J@aû¢RZìd)J²Ä‚—¢ŠPâV?™¿JΚ€*ñ“}Q*”¹9eÚáÏ™S†Ùoî©JŽ2ÄFáƒJé@™(øs(Êœ±“5(ÛÎÔ—ß3J‚$Š5oT‚%õâŨ+ê[Š?gM}*ñ?Á&ŽR¡ »Ro;üÿ?¹¨«À¦÷@…¡RX#òF%  ±!IP 6ÔŸª%Á•ÐCà°Ê‹Õ›UªÈž~o_U‚rêE¿ÿ…ÿŒrÂ&ÅuÚ“ƒÐ§ ~Ï÷øïO^ò¿Õ§ÿ®?óÞÌ{òßq’õ{ðßñ‘ÿìµ´gÒIû#Ÿ»ÑÞGûís´ÇÑžFûí_´OüžOéÏŸþWú í´wS>±ý¾>°"zv‚un„NŽ2ª"°Ñ)“X„õíû“XSU`ÛâCC¡œ°®KPøÐ°–#P¶øðÃPæt~„*¢Â¶Å# 勈êþp~0¥´fñÃQÒZ¥ë-z^A¿KƒH)½_c&p‚-±>Šèúˆž/Ð{Òônt' n‰J= wZè¾wꅯǸÞRTÍKü°ä(“Ô_ÿJB÷õþ™'ü3Oøï3Oð`ï¥%Æ„C•¢œ0ñå(Cº'„J@bøT8ææX RTJ‚EŽ*AI°8"Xx¡PFX(Þ(%J„ãR¢DX8¾”gŽ2Ç’¡ÔŒmÎØæ–XP2Æ77¡÷\P*” ˜7*en,0Ε(Qs®B™`áy¡ä(#,@o”UJç XŒ)(,Ho” e‹…ÁŠÓ%Gb‘z¡PFŒ}®D™`Ñú RP",^_Æ@·e ô"”¸¹À@§íR¢LDøs(%Ê \ŠR¡l±Ðe( Ê ^†*A9aáËQ†Xü^¨”!6ï6”ï¿{[ÊsÆçƒ²Ä¦ C• œ°9ÈQ†ôŒ•€2ÂFá‹R¡Ì;RŽþʲe7á{îD>˜[¬‰x ä(Cl&>–”c€?‡MÅ¥B™[SOoü9”mêWŒñèÝnl6N]©,¾.ª¤«À`÷B…£T(ClD>Ý©' þ÷ØpK|àætü§ßÕeÝ•B>½K÷Xéw¯ØO {§ôþ'ÓýPz/–Þ…¥{œô®+>ðº—Iï`.Úb~ÉPjú7æW Í5Ì­[êÿ…ÿU‚ò°3ÐýI\€ ”½ŠÅªÌ‘/-.\åkt§34&%ƒ6?ËÓù,zÍu¼ž’F¸T„MýhË3–À}Voë³vÖ³XÆwêÜDßFƒ¯s`ŸCy¿Ò+0´÷ªîó ò`°¤f¿[·ÒȉžÔЮ=ø´9R=æk,©g6ø©Çý@âÓ÷ó‰™q=aqÈ‹“«OZë|–õy1ÅâÒGŸnµípU=V•ç*•+zšªóO„¾Iw¢'Ä‘7Öe>ý¨ó±á>˜ÜODðéýæP†ŠñOT½~1â*>|y€9±zzüÿ^:4w9pþ\i,¹ùõNÒÂADðgo†wN-ïP§ ><Ïô9BbŒ›ðìKzÿEW¡ò¤¾UŠ<ÈÒîô6Uk°hÜeIò¾Ù÷¤rb‰|Þ  ‚~s*˜ïŸà÷•ñßv/Œ5âCðÔ}W¡Ã¡M÷:ì˃g’ cá7ÓˆàÓÔ’<(Íí2¸|Ù\7|hqáÜîÓ+ð”+0»ڸRŒ+øÈ_…·þ²Œ¸µyp/qr¥ÆÉi¤M××óÏ´nK´éÓŸsã‘g!Xç·'ĵ[¯ï9]G7×qXhÜŒÛó@öþ¤gWav…%C—æÁï_HJã~‹„º0ÎïG¦ægˆß÷Öùþrç‘? FE¬¤ãHp"m~c|únÖL„ñ%Ӈޛœ6¦ÖâWÓȆKwRwÎø~yó–Ñ•¬âHÝérÊëü¡“ÌŒNÛÀìÙAÊîÁ¢2ü3 Æu¦¶ö‰ ÅeŽÈƒ'Ç›<ÊH#§FÌÛ¢n9ŽdÞzÓuçÛ Âë‰û”sÿ$Ï×3€bq-&Ú¸ùøö_yç¦éj‘8hC/èÐ%òãÙŠq„8QÌo_kîÇ*ÔI ó7º£}ß"ŒK©áõ%ÂõþM·ZæÁ¡Àú AÒÈ×€‹ Lx\šó)–´ýbL&Ö ÖñthmÈòã}t¶°Úþ<L`ýò÷Mò Í´ùË·”¤‘ƒkw$ìß;µñ>Œä#_í "'Ì^··ÄZÇçáï2~«w¶hì¬Qí’àXÿÓ•ÍkæA·¯ÁNÿJcü ˆ @ï8b÷¢(¡qN ÑÚø÷c¾;¶:ÿ.!/2„<Ƹ¥sÞ_8! ú t:<û{.\=³±yít2¦hPÅc~pÉ‘Q˜Ç‘“œN d\0ÝçfÑ7üåó®ß¸÷ûÑ÷W‹À×uá•ソI`Ÿ,}ùñu.,µmcéØ,\3ùQÓï3ÜZó¡^¾q¤cÿ±3ß $Õ:ÿŒ1Âüõ:ó-*ãªÄ¸ ²ë¼ë}/ îo.Ü+hu­véÄÉaJñ½® st…Ñ­=âȵ”Í=¶/ $Âsþí{)øÐÿö÷óö¤ÄiÆuÄø'Lw?ð= ï4œ|+´¾Í–éd礥`ï¾%“zOˆc~¼Dkëhd¡ë§œ'¼žÀu1‹ïß.ØîôYWl¥È…‰UŽ-í’N7+îU²Î ÌkÜìºkLYýŠ8?p¾(÷;Þ÷g}^ŒãîpíW3—k°7cGäÞ]¹°ûAfÃS÷ì…Ø+´-Œ±5Ú\É9Žô\ô³jgîŸÛE÷~…¾X¾¬ÿ;ÆÕbŸ}®ýº~¹ðÁ°UC×®éÄíÓÑ} î´‚ÃÝ*ï5²‹#m»Ü¹lvó¿êÅêº;l;ícÓ©ó|Ù½0®Iêbç‚m×àmû™» çåµóå›´’®ó¿§”ÚÆ–q¤öÊ%/ "³GÔÉ©âÙ8Eð§j­ó#×g¥ßÍíÇ g‰×`FÄ—=ÆæÂ’†k [º¦“s¡×æVëÐ ´¶ûâÈ« -K "ƒ(0Pýãš _2Ŧ w ã­MÜ9þN¹ë0«áã€\0öüåøÂ%xÌͨ–þÙŠÿÝ>ŽhñG5‚t>Jœ{!øC}c¾DŸFÖvþ¸Î_k£éz„>Ÿ çÝX:Ê3Ôxœód4À¯ 'F\hG>ßù‘ùèr Ž?À}¸…¾üÞç‡61nó™ý+Ÿ[{¢f'š6Ï…xï#¢ÇÒÉõ*çþ8æ3fäŒß*Ž”Î–½ÝåH¿Ä–:?^î/Æ}„µyÜ»X|©k?iú…ëPèé¿NZ-._ú|õŒtÒòmÏò7ûB‹§›½Â÷»JÓ~˜èe‰w5|\g@go¡¼ïÈÙ¯ºÔ,3žˆ0î•fQÛ\ŸåþI‹?ç€hcûŒÇ Ò‰¬ü¬OŸööƒN¯2ÆÌÁ¼Ø¹'·Ñ‘3Dàôa^Јù@3lŒ'ŒgÉPwàëÐ'9°y\Æk÷ÕédÏM³yýœ`µ…8r‘Ò25d|kÓ Õ·v…¾!Û{îÛnùÍ ýU7ƒí_øË«¾qàóî¯Íg|Áß3â6Ùš–T¿1gmºÎ//>jØüà!q$èɽ³•s®¨˜ñŽlËúac¼[þ!j— Þ{ýØŸ˜î'½#Ì2ÝQ`{×*ÚlWãö}±©òÙ@φû¦ ~ßfëfºâÎgÁG°“З1þŠE$ƒ€OÍ$¾nëéäýÝï×’ý{¢¯¾‡ìãt~ªÜÏnØÍÃAÝ :ÀâÉÓjöholü$û^ }™ÆMNWÁ=®ÏWmÝ›&ÅMÂó£ÒIDÍj[Ç=±ƒvc6^ýˆãJ5cÃ3~ûCrß@a<ÉòãÍŸ1-<×5ª¿™oü $¦ñì×êt:ÙW9F}k´|jôxr!Î Ÿ€ûjsžª6_±~#.×qJ†ÎѹsüçæÀôbûÆ…±élži¯û}­+$ïëLD ˜Ü®↊ƒØøs_ÈSŒ×æxËüÉnÉ`:ÙÓò퀸ùô¢ÑÅtòóØì 7/G¨˜¢ŠY3(NÇ“|®»Ÿ¿öÝ&‘ÇŸ¬¡ã: Ä®Ày¡ÚüÅ×ù\Tóó¦ÑÉp¨Âfùܶ90.öH¢[R:9ÚõÄûs­œ ±féwKÇ8rlÊÌ•›TAŒ3ÛŸù¸Ûƒþ¼Ç ã5_;Û5Õ#jTqTH«æÀÝ}õ'B&¾ï.=f±’@ÔœÎ?Ï´#w¶Ì¬6yñïçÊÇ Á·­*ó!üæ°ù–ŸYÙIÇkÒæ/¾Žñq¸jé• † ÛÌ>öZ O†äô½ò Çý‚k 27˜BÑöqŒOD¸o*çt ~’ßF>Y7™ù7¡cÜÖ¯— áF fªAQ¹Gõ?±.ª˜}ªá¦ê·ãE†q$\vö.D¸_ª°ŽêžO ®nóÊAÇÁ¥y‹q;–Ô/X6$^¯€nãΫáaîãÁg‰›ê+¢£GÃpj+iG(¼}“ Âýy_[è¿éÝk³òeçÇ7ý ÃfÝ“AýîæÇ3;Õphö•{· òåèiÇÖUÇB«’àý¿p½ÐA D $½wÅxÛÙ÷—öÖ¾çÈž³P}ŠÅÏš¤ö*nœ Ÿ¶Sƒ<5HÚ)¯˜»fIŸÓ» , n"w'‰âH³:Ûu>„v|{l.îÊü÷êxÜÏ–ó1´ù¯ób©Mƒz•“aUKoO5øz Y1jFáü­Žç?îj^/ŽÜš¶´yþÞ@"p€la²¼Ðmæ„þe8,bŒ7þÌÞ3ý+&ƒÀWÃs÷€ þº|x2¹pdïqdîDºÂäóͶŒ£Ð’·‡Z+½qxÙ½ÂÙß„qÕ²L½xáë´|äÒþaÕd˜š³Eù´©œ»" Þ–AÜ-´pÑÎÎÛ4Ž#³¶ßêÿì{€nÎý¹?-÷EÕæ3Æ=Ÿï•X-jAÙXº¸Ü“È ²}“éöù“†Á•ãOî:ã¸t¡½dí•ÄR-÷qÉÀ¶Öºyì…MÍ;ìûèÀý µùŒq=’ßö‰4N†“Ÿ›¼Jx˜ ïKÜËãð¹l«euîÄX‚³¤±7ÁýéÉóµu>·ÓR{°ZôýâÐïø:J›ÏwÓ„ús'7O†ÔÊ”ž æ§í+3H£ýu×m]2 VÄÙN ¯§óIø ½`oû]êè 5.ÕvÙ>³z™õ¯ã.þ’ü±2L~6{ÖâÃÙPóF«U¢Œ Æ•p…›×LðWŠ%ý·Mȯy0PçËÌ×cuïg|šbÚ^7¯àórÎ«ÖæyßbñûÓO¶ÏN†WBSä¡Ù`Ô§ò#O2H?Ó^wwpƒäI•Û?zKÊÕMXd3,ñH@7ïÔçnŠ0^ýkÇOÌM›¯áµ fC+?Ë’üo¤üÆs+º¹Œ­M^,Ñbr–Ö v7£„kÖ7jêæÇÜÏ’óqµyޝ3¸Ütœª%ƒƒC û¹“²¡í gÞ_3I=ë[ÞL AÑ…eÇ_@2_?^>ÞVÇ=ÑŸ×{a¼Ô1”˜ >qp†š -Š+n¼ižI×>ZðeãHÆ›ˆ%­–ìj1"€qH»ýæñéñ¥ït›ažC²“!êšfB®M6Lnˆp„L`Y§[Ç#!½Ý1Á÷·<«Û¨ZŒ#n|þPf^Œñ:k¢Ép´WëgIͱ.ŒŠ‹êºe’¾n=C&%Œ‘ùÙWË3bÉ%› znLà€Ø1_pa^©Äx~š¥HO†÷Sz¾«’ ¿l?#“ØÇ?¿ìêìâ¼Yðs_Rßu•ë«5d2БÝZÇ©_uLMxÝ‚/(­°Þ‚²}Bƒ¯ãºqXÊÝd0¾ö¸¦åû,88õÐ]›•™$Õä³åµ©.`¶§ÁÅ¢X²®Cç 7‡[mCéÉøš¶lžR,ôå~Åâ¸Çoã¾+“áêÁ–CÛædÁ"ç¶f¾ë3É8r¡äþ g £{=Œ—øÔn}\óóµ€ÝëÚ'&س|ýê pÍ àÚ­ôs· ëÆÙé¹y•Ødþ–rràÙ,øÚeþmï]™äúÇ­½G€*;6ô¸§Ž%Wnt÷©ò"€8]ž²mYw¶ÙU·þ0~ó¦eù¤ B¾bÜ> öZÀ\4‰èša~8“|°€ì«ÖÒ§î ŠºKd =¶Õ0ÔùåÆûxâÒØØþ‘ƒË£­sguÖ^ײVçf ö%ƒx°»|½o<ŸRïV§“™Äø[ôöFybHX0ápÃøXr|醔%íø8À÷ËÎůÚ߀íK^òãºWάÜö`2 è>D:! ®ÏºqÌðl&Ù•·`XͽaÂýGÎuÏÄ’¡iùÕ&’¦•ß»§Œ—ë3'hp™ùPÆnö¡ËÇóÉàÞRòcSœŸ6öÐe&™´0t¿ÑྰÊÀu·í¹X²­ÝSQ‘w ‰¶lßm‡ýïy óæûÂ<ß´ùLóâiòøQɼgÚËÁݳàÜž1{OÞÉ$飂=›ë¦|m]x2–Ä5¿ì\¯E yD±XYý¡t^×#W „®ã*f·_MÇ¿Ôæ/ÆÖÎs¨ãsì3cæç·k–mš㞟I;|üž¥T©Ç¶/ÜK¾}5vÍå"ŒvÀÿæóÙöŽÏólKøï£ÍçþÅâ½5¿L«ü6¶l˜o¹Û üÞÛéY’IBÏ4»uãp¨½ªÐàëÊX"øÄèú/ÝE1²µîs,p± „<Ƹë÷özU’ 'Ð4ÆýZX¡R¹ÿ¡™ÂæÙH¸ê=òLí¥±äc­³}'þ®»öÝŸ.Jja52ר^ÛùÑû€kóãÖÙ>*ê+ö¡Ê»îËË„6nšE>öw]Rmœ'Lª:sÒË€XC^îëÙ1€ÜŒÞ;aÆà^0äÒc“b{àóŠ2ûÈwuŸ(ר*)WPaáø»™PËBæ4Ä>‹\÷vöÍ"ÅŽ罞âÂú)–ÌÍÒ¬Ñ9€Íž,a󛓇*%>vX2¼uëðk¿„ù^7Æqbý_çaúž‹)Sàúœý#¦Ê„.±YKC³ˆ°o0¼7{¸¾vˆ%¯Óˆ5ºüãûZ|]}j0 XìûîyL§ )PK»àÍ„Sýª«ÎìÊ"Ö;5¡Ë– ‚±3VKÒ64YeSàÏö {ÀEè œ'Q†ÿŒqOëÑfí·d¨–e»{ƒA&döÙ¸àSt9cöôPV¿Á ìcÇ’É+/*ô'ÝøcŽÇ¯M?'›ôsÛá˜Uu(\ ìéߤ],Y{$vËRwBbz‡)uýOà½7Õùêkóãþ¤¸ÜÉp=äôâzÏÒa‰ºïÔÎ>Ùä]øê›¥ŸÂÙö!/×tŒ% Lç3ø³þ×,æ*w±’5˜÷nU”a÷ÔAà•~ެÍoŒ/p`’ÁùÁLà ·ÒÁàS›þ+²Ék·yú霛ƒbÝŸ8¶ãÁtr;£•oŸÆÝtç–œo)p>„yžã~»õúF¢c2TXÙrØæSéð¹îƒ]ÁÙäxjå ·öƒNÚ‰5ö½”gõ‡õò'aù»Žniƺ~q¢ ðófá<¹œÐ§1nç&{ßoš ýjÛÝœoÎÛ?us6iÔß2)¢|ðÚ4’*Ç’6ß]an{"¬GzèÖ'œ›)ü-pM ‹—jš×áWÏš&­V¦ƒ8ÚåPñÞlÒ4äa×  8ØrUãÏ RÎ{kçƒý‰À‘áû^ƒÙ¾ä}}nºã¦Ùf¼¹šuÖÐãÛéé`x?7Ýôx6éiÛeÄÚGƒáúƒOC^)Èþ‘©¦»–úëöƒ+l{û¦KçŽ:NŸÿsž¡ðúÂþ‰_ÇòBÛÚÕã¯C×i+oZy¦CŒÏ«ýå/dyx²¤b[XRïYÔ¡G 2vöµŸŠåþ¤§˜#†µ.Ù¹ßÜúA÷ñRu§±5@˜ùç…q—™ŒÚ»çÀu töUCÓá[‡Æ-ÞÝÎ&“{œ*¾híér%CAÚZX—#ýýÙ9A7ݾmß“I;ç=uàç=Âü§C™ý)¾N5’qowèuh”{ë¬C:Ì=4f‘ï£lÒýC¥GÛû¹C{í‚OANeP@ÌjÝz™Ïß…ý×Âú£¢ßWºêY:ï:ÐS—Néàn5øíÑïÙ$±÷³Ü‹ <Á`a¸……RAž'4ÎÙ¶}µîüœëüÜîò±ZõÒ5B*1®vãvú$×j?¯Y:t$Óü¾ÕW“=fô?þËvî“'U¸© Úa¿ÅjÂÏûø¾ãÇyk?9ëGÆ'Á¸öQI‹Öu¸¢qÙêšéðëYfdqg5}Ïìä\â'?ÉVS+ˆ_þª/“¯IÉìÇçÛ=‰wûµ«>øfÏê±”éÛƒŠÅ‹ž6¯sùó5 ÔšïÒ`F7¿£­ú«IÎÓ™öu2F½e!ÎVGŠK•>~ñqïoóußGöiÙ>¾Î`Ï Û¬k× çG§5y¹i`½J5ýö(5™—¡ìWpÉ‚ZÌÿºì²‚´«·V“™Ùq×#úÀ ºÜ ¯Žï(Ì_‰ÐÇ1nûäÉó~mºð·­s> 6Wo·kât5™1eÚbÿƒCÙþ¿‚,±œ¸ß t5é"«ý²ÔÂxžÏã±î¾‰°>ëPf?Å _§‡ù„#¯Æ_ƒAÚÄOƒs»Ür[¢&) ZNÝ­·‹e}º‡)Èáîóæ“>«º+ãÒ:è8`Âÿþ)ôqŒ»¢öަK]ëMi³š§9–”°FM_½wc¯,»Z§¶ùn)ˆkt´Å1©n¾¶Ùw©ûVGÝyÀÿú%ÌO0®ÝÝÙW/Yãû}û¶jÎÒ4È}°jÊ 05©úyn…Gá]µ¾XŠ 2À¶“oª»”ÝêÆ>·îlùƒÀ©bûÓ7÷QàB_Ãk0ÁÅøè–Yi épþã®}j2¦ÛB gx« Ò½ÅI«¢)úÒ ¶Ú„sÙšðuVú†Ô’|!¿1îÕO[¬n'Á”õ¯ü™å˵pÚzBMm(—S\0 ŒZzç;JAîúP¸”ñ\ß8yýÖó«…~’æÀùZÂó®%¬'ãûZE±/ ²ï=¦§Á®”yÎ.©Iò½bå_.dwÂçÖy¶ÎüUÍËRÂy9üõ„×ÙêÀóC873òãK³¾—^›JX¦Á“Þ¦©Ñ*59Ûþqä§•ÃáE¥ýO÷Sœåoû-¿(e¼¼]œ§Äç³ÚüÆxv•'Ÿ^â—M[Ÿ¾k%JƒˆiVå«Iù%/ò¶¤º‚Áú´H¿ ²05ì­ù6))û<¶êÖ‘Âz»1»‡!䉯_²Æû¤÷‚$8ó9º}W£40_q¦V­Oj"ð8%Ð3ïM>>”Ô¦ž_¦ð÷ª«wa¿ÝDwN¢ÍkŒ«½&3# ‚*´ðTVLÿµõ»…WÎ!‘m›†ÁÑÞ©=¥ø<‡¼|¾½”C–è8yüýóû"œ-ÌÞV¾ÎÖÏtƒ" ÚhpRáòFÏ;êç;Üúõq0XíÑ}“‚ ÛÐ7÷ºŸîy ûéº<á÷^´yŽqo½¹Ç" ~¾ïêö!®xF«Ü2‡ øn±âùæþ›ÚÌvV Ó¶XçG„s†·ºç-ܨÃ8oÂüGƒq“/˜MŠÿ’#ž˜µ{ž ó‡¾<°²cáëÒ³që|öPËköÛJWù¾ïÍï-pޏð~…ñÌÀŸ÷±¾9£o&Bly'Öæ¥Âˆ¥Î¯¯tË!AË7ŸxÇúøwª C:_WÚmð#œ?Ëë…ß[ÆßÊB>cÜáwNDJãAàU¥‚U—–UòrˆÚeí’î»A˜ß+H›rôŸ¿ß˺çËϧ…y‰WŒq•³è2kk"¼2¿ë2 9Æöÿ6ýÕ€L6Žðì¨m­‹XïÝŸT{3ߨðý7}>¥ƱלŸØß7.V<êv1d½+uç’Ãö¡ûB¢@û†ý¤q➈!µüÈŸù%¼¿ _ÕYÇ[Öæ1Ư±Í¯Óé1‰Póç*«ÑŠT˜úzæœícrtóô•›TÛ´S«¢ºNœê§«~ÞWãÆ¤“þæÂ¼ãíÛée›m™ïÃÚ™úK…P±äÆÖ9äåIQéݡнç%×í-¤¢vÀñÓÕ3?'äõÀÇ1ÎmÖæ-ÆŸ–å*Îýu¶¸ _‘ ¦õ¶ܶ<‡\Y¼¶IWWøqhnZWs©©ÅrJ‰WW8§˜ßÑæ+Æ«ÞvúÏð¬«ÐaÈ Þgw¤B—IMÊoÚ˜CºÜ3¸“%çÖZö;×UÁö=üçóûúã¨Ábñ›cUn÷9rZWy9ãÝÖT輸b›#9Äß,ŸÔ\çå–|lo­ u:DH†ù¾ÏÏÏ%ô?ÆËzÜîøóeWáñÁQ}ëmI…îû'ˆ.ç ß»¾¾3×–|ÚVå9~>Oè¶øˆU„sÉøñ l~i<#È-(¼Ú¨n.Éj<èU¿¸!pR¶«è½‹‚Lüò~öו„ŸïušåÀçÉœk¨ÍOŒ«lmðkkû+°°ÏðÅ_{§‚²ÝÑÎ%×ßœ ¾»Â (pSAš»$¬ÒÝsî•ta¦³ŸopàyWß»ÆXaßk¾e¹O§\R¡Õ.±ñZ'ødØ­Ï1ÌwË_ùµ9¿ó‰ÿþ3ò'=ïcü|Z›§C±ï©^µ^Ÿª„%ƒç+OµIëyÇE•ír‰2xÌù-;áTÇT쩮 rálK«Þ5ýˆ°šéÀÏŸ[8È1;7Áxß<;ö{·M ûv„®_Ú,*lz4h@.¹p(ÞCìî'n^îàÿ+†xozµº­"|žËïw ãž­î|X›§·7ÅI{(¡Î¡g Î×ÁúŒþ~ÝÚ#—Ü7ËhÏs7Àä,XZCŽ,§7òVÞïB°)<êúûµ^¯Ø^,V‚v[¿z*œ‹rºÓü\b²?!­Ew ·Q«ÜŒ!šwp¸’ð¾ÄÇO¾¿,ì[ôÔ­k´ùŠñ5:ßߘG W»-s RaÓËÊ*¡¹äé]gË=3= |&È&§ü~ÂxôÉAX•ꞥr5ìÅîqY y‹ñÛÊÎ×Ï @OKë|PÁ¼Ë-«Ö8”KîT‹XP'Òšêù~¾:†,*¿Ë°É¾U„¯+"ë?”Îê¥;gÕæ+ÆK³;¶Kôâ2ÜL¢Ã*YŒêÞîB.ñnöuË^3wØòÐס‹âÐjÅ›¹ªU„ß“àùÅ÷+ùþ6o1îýòUCM']†¤4åÆjøhÑjjj.©›Fnäq-VûS ‘ù©ÓE¯"œÈû‹°¿ß]· ÍÛaÅâù÷%W­{,¼w¾ÿ-˜i’rÉV¿´Ï› ‡Oíè‚)†,˜þýj‡«t}‹~|þŸ+¿Ï¥ÍcŒ?ç²qAì©K°çÇêùy—Upº ݱÍ%ƾÎ ßæ7-}\»²‚´r4S'ü\Iø<€ßÐ_ÄïŽÝ£o…®—àMÿ“*øévkÝlÃr"~þÏŽC³yDT¢4¸ì UvN~Üñe éó«bÃ3¤¤l{£ÛWã\amÞbܺY×ÕU-Îp/P !‡Û.^…yЕžx€è‹ÍÆibˆ¼(kæ2‰”ð{Hºq—}¯a­Æ¯CNL !_1n-X>šUlvrÆ@ôpÛk¶-„J¿4¶ém®%·ü,†4<3µƒÅ?Ý|&$} ÑÖÀïãjóã­úaԤ㞘*›46¿¿ ŽvkxÃûT1ÚÖÿöË÷ð~ˆcHñÈ{ß4áñÔº>Ëó–ïÿkóãž´>Êopü¤m`€ ^ÝŽ®šœG„ów½¢šôm 9h—ãÎdzº{úÂ|Î’Ó ù¥Ä¸žIòÄðgañá¯w-©ŽŒ’)È#3B¶¥¦<F7Â&c~íÍš-ùvr¥îóâûPBŸeû}e׌ßgäÙeC—…ýôÚ¼DÒkª÷wyÄìb/Ãr_\Òpë¶R°{H+ ÿ„°]—WTòÕ¥XLú^\ôóÕ˜Яߞ¡8Þ¸Ÿny¨B>™X~ö„ÛÎÃàëÎe˜©¸>×Îwþì_q¬oÕ)³žaÜ6'ÖãH6júßl€yPÉÛ ^Šq>¹³Ýyü@Xø2¢N¶•‚lðX;¬œå*ÂÏy:Q1Êù{õua‹RîøTPfô ¬"ü<•ß .sÎŒñ ^-›j g«´Wwl¿NæÓòI€côÄ{}\uã*¥’ïvý=nó|âç“üž"ßïÑæ©k±¸ƒÉ¶ŒªÞ pkl{o’ JNgLß²4ŸLžTï|¹®àWî+H Sãá-CV‘…®Ñf›’ t÷µ9ÿ]›ŸïÔÀ³­ç_‰ØÖô² N5[~âÞÚ|â°^dá«׫€u´cdŸÂæƒýÿþ ¯{ýõ†ãIw:“Õ0޼ ö^±{®Þ“O2^HÞmœ$mï¯Ý_k¡ O·ÔOÌ,'%üœ†ß·Îý„þì…ñŠí܆o^$‡š×e}`¼'g68‘O’²’÷…Kœ¡¹5%Éã:S[ RÂ׃¼.ùù²þyžã õqÊ÷ë8Q=ï×?¢sù$ëô½Ï§ö8éÖÃÂã’þeüãëwþ=3¡Z–©‡|9–GŸ‚1vO{NÂ|ptë0¶zR>éxÕéìc}àdÀëͶX¯o÷U¸el-Õ­kv¾kÕøRc¶îª.ä+Æë<ïñê”–'¡°çåÂQ*¸6ÚqçÛùä×2:`:€oë Vš*H|ð%‰rñïq¯¿ø÷øóÆYaßSƒñ‡Ô_7èEÅЯӓF#U°¿e°lwz>±ûž»ßòNh2Ðß§R= p j:úžT×Wù|Mˆ[EÈ×áÅbÅåÕ7Ö·<5µày<¾õuMn>ñI~bõÕL;÷x½µ®ëÏ]®]¥êj]=ðý'^¯B|!®ãÞ›±ºW×c°ú^m³·,l“2éa>y\õnû•ÖbзãºÎHDOpV³¼ø [ë÷1ÆÛÞÚîVGÇh¸Zàyá•#ö-ñŒ9šçù¤Q¦ºÞªñŽp}º¬ýw¯b—瘰š”ý~Æç­þ>¡ƧÓAqdžª:}}oœÇßy)ùC>éþÚ¼]õóN`yÀ?±zKir9pÑ£‚ÕDø=?;ðýQ¾^àyÆ÷MôÇ)¾Î ÇÅ÷¯O= —‡ø}2ë§‚¦Çïw¨X¡€8lùñ¾«—Âò,öŸÀ¾[zuò1/7Âï•ó|ò[xß~ÍÂ\q¢?» ]Žã·øMö©Æõ H`ÁTM÷ªnÐB{$†ÝÇõ×­ïøûçûß|߃Žúù¦Ä×vçIå>ÍŽ@?ÿè©Y8ÿr¹ßríÆödüžá­Î_ ûÃöžÉ‰!­&ÅñË\Møù¹p¾Ó„Öòãm6Û»zÓŽH(^?jÂ×q*>×öµÿ=Y'v^C`QóÙMWëú¹pÿV¤û>‘6Ýp~ØÞö¢¨e$l^xçñîÙ*8Ÿ=p¥ç¨â;ʬmÂw¨¸qî›ÆbˆöóI)î“=qÆYa}$Â8Â=ÇðD»WÁä*Û6Ì+ ÇŠ¿ÜPˆpm}󾫂\ÿ|-²ýìÕºù¼pNÜDw^¤ÍWŒ—Ýõm¿atñÚ€3{pÜ×áÀ…ÃäÊ䝿œîßn{îŸïŒó×¾A#.®Öå—÷‹C÷ÞGמ3*3>F`ÜÛÛO„úl9kêk¿¸{›·Ê9U@º‡¬}õ«“# ;1üÉòþ¸î°¸ì½±¡?‘ÿøbÜ8_7ßJ0þüãáó‡ÏUö •ó²RTŒù'-ÿ›ÎX)⊚ú|P´œyy£”ÌïÃ¥bþ_RæÿÅ=?¨*e@‡¡J(ÿyh›ŠûðK˜?-$TóøóbÔƒŸóª¨wOóPub¾ EzüçÆŽ`¾==ÞŸãý1Ï¥ž?÷Yý;þ3çýéqR8ÿ™sR¨¯3çý…¡ŠLÞå¤1NŠ’ñþ|ôüýÂõ|{¸¯³×ÿ$‡µˆqR(ïOÃx”ÿ¬až«œ_¥Ïbµd,Vîí,eÌ?±óO̘Ô…2 #þð_å>üQ¬ÉH˜¯³óì‘3¿}>Š„ñQJ™ŸJã·:é±þ¨§J‚žg+÷ê÷až­”ýì‹JÑóê§Ü«ÿŸþO—ü×îá†ì¥¼+êϯf¼+™·5‚ùó;U¸­¥nkókòA¥ DX¾(5ól’¡4([,’0æáH¹­á¨Rʾ¢‰`^Ž¶Ì¿‰û9R¯kC,$/”œù¯y3ÿ5ó?8*áÆ‚Ç£ -U¢Çl-eÌÖ(V|z¬+/ƺ¢ÌVæûĽ²Õ(ËÁlå¬+=o~ÎlåÞüÔ£•³®ÂQ%¦ëŠzó›0oþƺòÕó^£ ÎÒæ­Þÿ“üÁæÍΘ­NŒÙZÔ^ð©ä}¡-crŸVã]9éñ®œïªÔRà¶F¡JQkÁëÒ°‹à£-g>­Ì£•òZ½ãJËÜÖóä÷`žü´ y1?LÚŒ$zœ+/”e„ÍÉ[ÏwÛ•Âx­R”JÏw[­ç»MkœþùŸíáÿôïÿ{ý›ölÚ›i¥ý”öMÚio¤=ö<ÚÛh_£}ì û|" &ªóÙ7d¨=&*å@ù2ª9ã¡1iÎC¥Þ‘”‰jh(xLÿéiĸ¨)z\ÔÆñfÓœ-¢a ¨0Æ€rb>tŒ+B9}†Ì_:…ùKK™o¤HÿdÎøO”5mɼj5Ìã?ŒñŸœ˜o¤¾¿4÷ü;n*ç?9éyüsn*÷ø§Þ·œÿ”‚‰~{üS/¼æ‰Ë}þÌʲ¦¥(5õÊe¬išìÔËŸòM}-¦‡-ã7[Ú2¶óR¤l æ_ÿéê÷Ÿù×ÿúüË„=•ÀG¢>ùÆG ÓãF±ä—0Þ¨ã*Q",_” eŽE!EiP–Xa¨"”‹$œùfRÞh+TóϤŒ‘(=ÿÌ”’7*%b>ùjæ§I½µ)ƒÄ ,UB9IXhQ¨Ò:¿Y£´è$Ìû–2Ÿ½ôØHÞŒDY£¾(ÊŠ¥AÙþ Ö(g#‰ô<ò9k”{äSï[ÎFŠ@•š l$ê‘/bù*ÆF¢ùE(1ã•pþ3÷¾õùŸdØ•2üÆ•0ÖhI{Á'”sKô9vbƱãþ·aŒ$Ñã#IÉ€ñF嬩xX ^£F]¿ñæëżo)gÔ‡1‘LþðÆ÷bÞø†Ø„¼™')eÝyèq‘¼Q)(lN>(%Ê›”¥bœQJÝ‹úq þåô[uôf“ „:¦þéáÿ9z8íß´wÓÞL{1ïµ´¿ÒÞJ{)í£|>Fûí}¼ïéÏÍDìÿ§¾W9ÁûŸ2:)I©ÇèT3’”1:-£³„y{ÿ£“z{ÿÉ­7aŒN•£SÅ >ÌÛ›3@ŠG‰2:‹PL´Æ}óbÜ7#æë­b¾Þ2Ƴ§|NÊHaþÁRÆP¶e>ÂE(Û‚Ÿp ãsFýáëm„ ëý/øœœÁ$ÁDŽh*°8ŸÓ“Ú»¹àKÌLœk†*BI0Ù£XÂ{ ä(“?Ê2”eËÊNX †mf=å·q–¦”ynSåS¶<å!…1/ì"æƒMž²1¼þ™·ý§«ùæmÿëó6sƒßœx[Æ-(b|£p=®¥œq <×Òq-SPæ•sâ-«üæÄÛbq„£JPNX$¨’ª×RŸ/G•2Šœ7Jù'Þœq (Ø–ù S&ŠØX`Ä— Œx9+6δ4dLËÆöÖcù0¶eZJQj”%¥ U„ÿ ¦%g™ë1 8Ó’3 "šÿfqFñ”oÂñJƳôeL#ѬoÆ* lx”’±ê¼ô¸F>Œ/Âæä‹JA‰^Íx–œoî@=Äñù£l±y…P/ôÏÿŽþgÿ¦½›÷mÚ«ÿìÓ´Gÿ«þL{3íÅöáÿˆüŸ©ÿþ]ï¥}÷¿Rϵdÿ åiz—Ø&åžTŠWSSAàKÉ_Ó–ñ5KQŒ5eÈXS”µémX–÷.bœwÊ™òÕãÁ”0ÆT =Sf¬8oÆŠ£Œ`”y-%c`1åJ ÄØ+#cʃñÚ ±¼P (,ŸÁä G•Ð3c6Éá—QEßN9žFŒã©BYVþÍo·­ò›ß.Æâˆ@•¢$X$Q¨ÒªÇSŸßžÀ ‡ò}P†X@>¨”?øí”‰,E¡ÄXXáŒ÷ãd,°Û »=±Û9ÃÓˆ1<•õ†²›Ë—±¹(ÃS†Ò l±(ÃP%(§Áðäl.K,X)J­Çð,A9aG±"¦l.În§l®”%µ ¥al®0T)Jb&0ˆ8OY‰2i-ð”5­6aãJ°D±&àa.ð éž$cs06—œ5 å²&!AE±f!é$ð Ké?3Ž'm^z|./Æç2bOÊo7¢ $”%ÂæâÃøí"ÆnOaüN)crį” 6T c¶û¢R/Ñ[ËåË¸íæØœ¤(ÊœqÛ5ŒßÉ¹í–Ø´d(êæ$ÆæB Ò?ÿôðÿ÷{ø¿Õ»iߦ=›÷iÞ£ÿìÏ´7ó¾Lû1ïô÷ê÷]Þcy?¥½”ïqò^IŸaù RP¢òÓŒ²Þ-1¡d¨¢ ã,ŒqOÅ•ö)M2/Æ;3b¼3%ã¿û xÎ?U¡Ì« ¨”ˆòÛP*Æq£ŒyÎ1öÁ"KAù`¡)Q&¾«¹OÞ‹Î’î‹Òy*£2&±Ÿ-^˜Ío¾[½ˆ…gÄø€ ¿ïÐþßî=ÿôæŽÿ;æŽN¿ðN˜äå¶«¤¼À€çlW%ʤ¢À€§lWÆvU£l+ÿfÀ‹õðNXQ¬8H{`V/´¿ÑÞF{íc´oÑž5‘õ§úÑ¿¿Ññ„Þo¦wkè]fì/†•μe5'KÐbæM ëIc¯¡wYè=¬iCzO˜ÞQ¡w‚Ù ª¤gô®/Ö Ì§´ÊüðålPõ¢ÌX”kË¥B™cmIQj”9Ö–¥BYbÂHQ*”H›,Â$òE©P"¬-_T JÔYàϪ(ƒžžÛ¢Ô> ¥A‰1ùÂèY.Êk+ U‚cB†£JPb¬­pÆL–`mE JQNX[á¨"”k+ÜFàØ;a"‡£ŠP¶ô\UŠrÂäŽB•¢$X[¨Rú= ¬-9ÊëÊ•€2¤÷ý(ë Âfò?ûkÿÌ‘¤ÿµ{’{%ô³ÂDbÉî’£Œ0é½Q)(&¿*%Â"¡4(1C8ªå„EÁ C‚’£ éž*eˆ…âƒR¢Dô|¥DÑ;¨” ¥FYb!ÉPjÆÒC•¢$XXQ¬¸ ýÎ,Öµý>Ö®’®w°^Åô»®ô¬’î_Ó;´t_kÃÞ‹¥{ÎtÝ€TÊóÝ¥D™cÎKQj”%æ¼ ¥¡cΫQ¶øÁÊPj”9滥F™ã‡-E©Qæ˜ïR” eN¿€R£l1ße( Êó=U„rÂ$ G¡Ä˜ïá¨R”&Nªå„ù*¥ß?Â|b­*A9a¾G JPL¸T JŒùÎb JÎdT˜½Q (æºJIÏ1ßSPÔaŒÎ¿lÙ÷\é\z¯C¿ædìu=Ø][º&áûCô»ªüÏ Á5Â@éV,e«—À} æÝàš–¨î_sí†GÊÙ?¤Àkî ܇!Ù‰:^*÷%˜Ö/£ïþþDßGAƒ¯“åî^i†UHšd>g©`æwß}¯E—Ÿh×* |b]¡ælßü ÷íâüq}ã#ŠÅõíNÇ vÛ­›Kº?QzÁ£¶¦· H–õ包… ùý^=ÄfÖè­9¢5„û‹qŸ Á§‹sc þ7íèˆÁS÷Á…jí ú“ Æ<Þw굺€”yË+ž Ù2¯¦cìÌ÷Ÿ”õ?ùÁ|rt¾0œ3PÆ_§“%½ì…Q-®¤Âúݽž-a‡¶Îœ2ž©‚|é9ðüTÝûçÜîãCãya¼_Šåïä’ݤØÙǵz*4XñXdý©€Ô½ÝëÆÕC¡©yÉP‡^ 28ÌXòÐÐ_ç¯Á}´¹ß±áSË;Ôé¢ã…ÓøÒœ#»Þ/H÷o˜ ά1þVñ>ùdÖo²ÇµÁ[ò1¾s71(Ÿull]"ðŽ>8~?™ˆà3ñ"†‡í_?fl~öÕ¡i«T¨ý²’¼Î}r`ô—Åcö9Aë”8ª 7C×w]å¯ó¥ålîK­ÍcŒ'øSn‡i†Ùï– ^«m=×ü>y×¶}“,·þPk×v 2¬åUbØxÎO…ûüêûñj0^p·mý&4Ü)K™¶uN…›ÆNoÔî>ÙSx»ZÄGhõnì ±Ö "prÖîÇýåôߟÁÈbñ•§M{Öݵj}¼ ï6:JÃæ5±¾OÜ áö¹¹bh¬5ÊVwŽv=Þ®!üsáþéÜ¿ž×¡à;Ö ¸O¹6ñuÖ•½½Ìj+¼¼<É÷å´TØ6üm·ó=»;ÍèÝÃNWÛýø:œ›È}Ҹ料O˜ãýêåû*-h øVºR´4ܽ³ cá>¹qçÖEyXWðŒüàla¥ ?êŽ9u%€p>®ÆjÇé6Ç~s6ô}8½0nä…âY)°¾OŽë¾>¨Ëé°¾÷ÉÑÌâyv­,á‘Mà¶jíÄalAŒºM ®Î¸ çƒp.%÷#äþiÚ¼Å×¹výâÛÉ[`üŠÔæ‡S¡uç3¿Î:Ý'KÃúL¯gÓÄÞ½Vì4Wx—½µ&÷ ÔùD ÜgæÃ(øF`¼û£³;•ßÇ&mKër ?Gj;4ô>1y7©mÃË=t¾v·ô;óþG¸=*Î%äœjmÞb¼Ð©óºmJ Þt':]7ÿÕˆû¤ÒÀ¾{ÇÝí ~³6›¾Åx‚]á~©œÃ[Æ/ãÅ…ISŸÜÙÔ¥Ó8?ZDšÝ4÷ºO´¾·ÕÀœÈì©“Z+ȯ¥-¾üů†ÿþܧ›s&?BÁ§ÜÀ½X<®^¯+MÐ=Fé¾°8œ´ ÑûäëÔOý¦ …s]êÖxÖXAúYò˜3=Pçã$ôs«2ý@„ñÚjm¹ÂàäajŒ› mŒ/ïYuŸx-¨µ¯êW˜µ£{Óí•äkzÿEva¿óÁnéË«3‚¿ê|ÙîóÍĸ´ú®u§ò“VVHÕm[ÅŽÍ÷Éàwu\g·;(Nøm YžûðôƬ/>sà^}®ˆÆ¼iEë°:k!ÖqÓëgõÒàÁ÷ ÊÑ÷IjëÞUwøb1¤æµfsßÐùÈsη~Ÿ‘b¼sßššvº+It)sÛ¥g½Å.”÷ÉÞ×mÚ练ÅÕzOÒJcȃÒÜ.ƒËózÍpà>žÂóâE`¼ž òŒ ‚^Õ²ùôJƒê­ªò9÷ɹŸ¤_WÝa¨P« §ÎúõdT뫹:_8ÎÐæ'ÆkÚÇÀçeÎhéÓJå5< –úÍ¿¹ùÍ}òúþî’1íFÀšÈóÒ¤õÂIžÆºq…ç÷á䟿à fVf<×àëþY«áG@ìè[“Ó u3ª? Uµ¯áСÛòˆú& 2ªNÄ‘í' ÷Õ÷_4ð(×¼ïþÄñ‰Ì>µë™lE8ùÜhð€L̹Ý":ÐÜw /?±Ž‚œŒ”Æý*¤ËžOü}ò¾ÂýîŸSÖgñubêwíc½ ¬7÷r¼–žŒNjÚîéÿ¢×¶ô}®°ªGìØµ±Î*ù4² "‘m¯Ú]û‹ƒÀß)ëß%Æx†ž œ¿»,„é®ÕÎ_:œcnlX/îù€ÌjtÞ줣Ô;5àá²z âüuy»žÞA„¿?ÎQž·à¿é…ñ„¾;Ư:zcÝÙ4è™_«æZ—äÒÂש‹Ž­M;ÖçÝëƒnÇn"|Þ¢ÿ{J1ŽÐÏGC!µ¾“cš®]¦? Ãæ•[/ó€áŠ=i d©ôD8ßFðmTÆ÷,ã1Þ1øõi`r/‟ÿbå~¼ÉÖLOˆÌ«sNSCAæ^P9º‰¿¯/ìó(Ô}>ÜG_èW‚Ϙã3:é`uoШ·i0Rú¦ûÜ=ˆýÞEÙfÍGÁ‘jý[/*¯ :=s3"è/óL}_D ÆsÓ‚´§—)¸+ž›n ?ã3Cœç7ñö„ªß×1þCÒg5¾ò=Pç·Çó‰s@ùçÛO;á(Ë}ö,̱zT9¤óµSÔO‡Ý¸ùÝ}@$»ýûýéÓ:X|Ùÿ=†L~qê¥ÿ•@"pxè8+úómÆK1Kؤè7Ÿ”˜4ÿæÙ1öø^zqöÉÆ ©óá•¿1|dz á\Ç¿Ë'1Æ;:z±ÌO=Ÿd¬šÙvc:ìH{nqìÛ2s¾£uç©# þ±ÝkÉ«9µv\ù>Aºq”×§¾ß¢ÆëÛ=¬s÷“ ‰Öv}\:\ -}_ÇPCš´NÜž8¦4Ûp*­ †,^¿½B¯Œ Ï9ŸW \J¡î¥orÏA­{XBµØtØkñiÜ c ©½±–çë®à9ãÒïä2¾½‘Áº¼âyÏý&ù¼MßÇ7ãS*ôÉròøÓ­¶‚ÒaQ@3#ëÆ2pÙÄgg¿H Gu:‚Ä ™ Ò¾]ÖÕ'÷AÔçœ(1^vN˜ßT#?riDö]›Ó¡^}¯ZóZjÈeyñq³$Œ×CNìWóÈ™Îדçç“òñNŸ;¤Áø5Z<ûá·GJ´8#é°EÜCéß^CÞ­‚DUZߎümèx÷Û/™Ž' Œ÷Ì_Gá¼çÓh{—:þä~ÏÌ6‰é°þÀç7­5$­ÜáMíŸHW%†¼¢mehˆn^)|îms{´ù‰ñÌüçù<_C¨»]ÚÃtÈ;´Ln¯! ¢Æw©6¾L ¹sÜîx¥O2ç…ÏŸ¸_Ÿ6?1ÞýæÒUÉû…§Ì[WÌ€Œ½m÷ÅöÓÁGk"G€uÓ{ÓÄ—bHPØûŸ0TF8‡†ÇøíÊøÎza\q}›9VLªW[c½¾Mäw×±†‹†Œ³Lxºë’ºmÞ?CdË÷4XWÿ;ÞF©KÔº~ÅýÒyßÒæ-Æ/ías¿YÁ¦o5j@$/˜õ>~¬†˜5ù"ý2 NØktâ¶V6ñ]e™Î›¯_õýâ#0žàÿJ‚z½*Éžœ^UÚ¥'ÎÔ_?^8*ºÙú»…ÆóÞ)¾[ýÎ+>nW8p|Òà~_^ôîÝnÔ¹¶¬¿ ó4%Ƨ£É¶qkÉóê4Á2`ÞÞnÓ–iÈÚª"\Òƒ:ÖaÓvÌź¥8²z2ݾ÷·Æ=Á\ƒñ:m;k¶§Á:¥]vgÀ±Ú­Þ4 Õï_Qï §ƒìËÒq bȧ—眇øúQ7?ãã©6OG‹µ˜Ÿk‰À È€‚³ñšˆp ¡4ìFn£a׫j+åëbÿ\ùøÏÇa=ÕºÌz]„qcò¦mÛ™J*ש×ó3àÖ›œ«"5¤q¥%#¯ŒsõÀ•ž¹Ñ1dHУ/5¢‚ çFòùŽÀGeóRŒ·ýNÓÍ£ÌCHh¯¸õ¿gÀÔaåw/kHÓ¯í> ‡®ŸÃJ.‘âucDÇUÃeWøz?OîëÍ[Œ?¡{¹ '÷‚ɣE™ð8úǺ+ç4ÄälÇó7¸—ÝqÇú»1dF×s&Še:nBÿí·c.o^¦^¥oÏ•S‘ÏOmœ»:eB’Ñ›7-¯jHRûþ§ûH`v3Su )¢Ëù¾!ºy _gó~È}mõ×­ßæÚ£ƒ£úCOĵãgg·ägç·ÝÔ¶ÆAG6Ù»€ÕÎö&Û2bÈèçñ9!!ìý¾wàó(ýþ§Äx}š_Y¿µ“?8=™ÐÛ¹œN×ǬS“[ ‡õ¯E‹¨bH‰ubO¢ã¾p?X}nžãáª¾Ž·”TsxÖ¦¸L˜m=l¸O† wjcø‹á`­5H!ÏÖo¹z19Dç¿/¬ÏÊßÒæé˜b±\tÇ£ëÞ•$gõ™_¼õGô3 9»fVÜ’n íšÒ¸KȲ+GCçpŸu}ÿ^Æ{ЄNÔ—’£œ”Eï2Áô§ÄÔæ†”»¹Í2dúXzã×ã7÷cÈ” OÆ~”†èÖO|=Áû5_ŸèsæÄ¿[ñº7k/"o(f§nlûYoå‡ïò5´•åºc#à~Ñ$>ÏÏ#+O !|<åó ýy Æ›¥]0Ì#ꮊÞo“ÚeO•‡äGÎÆz›&ŒÓÁ¦ç#.ÇÃ÷‡÷xÕ.ä/ë a]õɯÏ®ƒðùK1þ®o;ûF³ÈäJ¤¦Õ¸,pŠ˜újt‡$dÐ*Óé·Ü@åW{Üÿ¦¤Ä|¬ñP¦ã²ðý}_ÙŒçWúåÍ•™ÓH³'^XYYÐ?¨ºÅ’¦ID‡ÑÝí3rFo…ñ>û¶hÖüªL÷ûó|/3OÅxñ¿Êe;‰ÌT¿ Yt, ì[g…_oýñ?]€L~t¥Á…r­Ç‹¢„Æ¿óóVËŒ÷¯Áœ/Nƒ–yK ς꛽·èŒñ.ÞÜ¢¥:9¡‹Çí¸¢[G ùY§Ì|Ï`l±XÈ+"ÎÏ<žR”Ãü\ Îv{Hv¿ßÝâ‚3ŒÞY2øÇŲçÚÚÀóCuû>ÜÏžsÚ9ª oã/­â›vuJ?Èpüä•a6œµ[Ùó!P²È0mù@Xu¬ZÍ$E éTU2üVV¨Îoœû2ësÅOà@Ž„ µÅ3ê¶Í†Ûš‘w’„î_Ò¤O@¨7œÿI«+¦| ý ç‡sÂùx¢ïÿì…ñ·ž>½(zH¶~»w6h}À>$ë·\’ìšicl6ÇŠ=§?TT[K8ÿY˜ï[”YWK1žÐGf“=ê>ß<²!¼û’aK‡=$¾'ï.?¹ml7tû%Âx%—Û7lÿ+”ðçIgs1—º—Ù§‹ÀxkæXL_—5( µ™O6ìz½Èuüȇ¤´pí'ßC`DÎ’~%8žXŸ6«•#%œ—̹ô>}?Ÿ˜×±Ì:W‰q“;»\ê&_-Ù»¹&6Êîf§Ž}H\VZEB¿a ðd‚óÞÂëö_¿Â8™üO9~lë2ü4 Æ+RΨñx)Ð]ã&;³á¬WtgÍ”‡äb_IX—(¨®5ŠŽ!Ž^OT-óBþ²OÍ} …~ýDWúõj0®XlUÃc~ýµ~Ðõãù~^'²!òUôÓ5s’º F•»We›‡ÄaÑ­µù‰?¯ÙO¢ýáìÎjo.fC«! /Zö4¹¹ëàìþî°gX³ã[÷ÅТì¬a¿BtùÄë€s©øó8 ʬ/Åø:]3Ì[í „xeóºíîdƒË®ùí/=$®åzßÜiã冾)ü‘C"fŽ[ä‡è8ŠÂ¼í‘ƒÀyoRf¾î…q-t,Š,ã9ÖÇj<ʆw-±6?$U¸{ËÞTËMºýåW3—çy?ت{¿œ?"Ä|ý¥÷û÷ÏšøÁk¡B¼IOçoÙàõ=§ëè}‰p^16Jº-Œ!Z;ìM!ŒGôÂsÜy¿öq« ù‹qƒNÌù¸çÃ(_³û¢æõÕ ¶ßÞÉ)ú!Éô³:3sïpÈ\\wEú‡âWðünÝ{!l_í©CæwÇœH÷Ǻq Ì<ã þÚaPŽØJ U·çdUR<$©öçÏŒ98 ìs(ïWCì)¾Á6”ðq_¿×ßÏÐßoÓ`üõ±Ï 5S7ÁôÄ Ó5Ô Y×qç¤sÉÃÐeK›:ÃJ-(×NC.„O ÕíƒòÏO›.Úü_,6Ús`÷Mpp\Éô¡ãÔÐlÉ *Åä!ÙT£Ç™}-œÒ¼g=‰!Ã*Îk>Ô+T7?äu"ìß>tà>ô<ÿôß¿_§Òœ‚;Ûm«8»}æ©á†øK«ä‡$jò n?̆@ªâµiõÔò’ܲ{^(9v°Ýö3tœMa^+ðÅ/ovÃ]Å]7Ã:Š¥X¥†ÁÁác²îb¿0ùWZ;æö›èßÇõÙëö–ÔÙªãAry^Çeö 0îcõ² ŸÛ›¡ÝöÑ£j†¨aòÛOË/e=$§ã)HVU“Š¿VÂñòý¦­,ãBɘ¸½¯F7œÝ÷©ô‘Ÿúëp)Æ-ÞbÚ™[áüÇ]ÍëmUÛcí<$q²;³¾wvéNC 90™’—ø8WÌÎ7 8QŸÓq¿Óm]“ípÿ‘sÝ3ûÕ`·ûù9ͳ‡d¼UNg³ã.pu݈! µ1¡:žï#¼òsý:Tb|Ÿ±Vf{…ƒÀ%RCÇÓW”<$7—‰¼/”ºÀZ.9¿Çç?y·|¾«ÏíÐ`\eÕ@CÛ€]PG»¡£†ÆO^½òÇ‘ìÆk¦¸€‰aùÉE8Ï÷ˆŽ8øÂ=TÇEæùÇ9å¼~Êòn?Ö çûÍÞËö€ù^'ËÌÛj¸Ùuטuå ‰À•C¦µ]sè1®ŠŒ=kÊægù|]Åy—Ú<Æxýöž x³Ú4¢;1˜Çýf~«YµÌÏhòkXíÁ°3CUÙ«‚B7®pÎ)_ø~ ·õשbÿSïí•êîyùg—>RCJoõŒk’sU²7÷}ÒFx‹pI§ ÉSò3ÄïCtëj~^ÅÇÎáöÿZ”åpàë8±Ø•º1høS5ô:«ÚfX¯ô¸Ÿ¾6ÌG ™m†¾^b® Û=&vðýËïaœ½Ü>}[Žî÷à¼3Îc׿=¾Žºäà Ϡ˜úå|_ÏÕ0·Ý³µ—‹A‹Ž,RÚA»ÊN]W·T¡;®%=}ô;„}"¾ÿÈùGÂ@ÈŒýáíÓO @ßåFåß©A»Ñ¸,ÿ.Ùs¥ Ðþâ 2iÀ# ©¾–ð÷Íyjú|g%ÆKp8a3#ðLï¾1Ñë§:GûÅ×n^Hü¶tþ´Ð~ìO\±´œ‚»š}èòñw^òz*a1±ï€g:þK™õÆßþq¢yÛƒP£Š£BZ5Ò>“Ÿ…äþʾå¿Wí÷‚§Uµ.¯ »ZSðp¨nžÃ÷_…ÏSx¾ŠÅçFUûí tu÷Ì«—ãL×mØÕºŒK®þ¼D )–ÆHAÒ§TŽª;'äûCeÖwïìòsÃ6”? ÓŽŸ~EŽŒn_H>ìè’׿‡nxüÎTA(õÅêE(¹oÙä”鉇76~ ’}÷o¸y~Š˜Æ]Ѫu¯‘05°eÁ½v90[¹îî>ËBÒïl‰:j>Îy2ãf¬ä=úN(ã4½Ò[Ú¼Å8gÑ t"¢kl½Úpu [ü|*­m>&Â×Ù´³ú˜æ3Pe·ÝÆÐ_9P-Ì=ëåÙBrh³1âZd€[ÔœÎ?d¥*Öóã¹PÝùXb„ó¡×ÌÏ´ù‹ñr+ââæB)îØ0¾d¿»ùŸsÈ…Ì»k瘭—"÷X|ºó¼Ü9Ñï÷xϹŒ|ßkßvÓ„›fÀ×§Ú|Æøg«~¾yqD,hqÃÆ¹c”VÕã—;ñ”׸ÇÊ^ãcã ‘ÏöÍO0nÃY«ÜÇc^È?wldœe åB7µ^Ð1–hgß%¿ûŸ?ëï‡i0Þb-ø-’ªÑ_.¢Ó¹ó…D1¶ÿH3¯Ð/ó4®xb‰°ÿ»¯ñ}6Þy^éï#L);k² zfÆÝ{Žñ‡i7² ÉŽ1¢=DzzAFúMmˆ%ϽèLý÷xÆ×I<ßø|u¶v'³Ì})¾NM-xî lÞÜ–÷ëHö»÷üè´&–ðóV~ÎÅÇQ}^§ÁÔbñæ—Æ3‚ÜÎAøÑýڵȅÍÓ†N\ž ¾M â¸üã[>_û”â+‡Keº~ÌÏÓ®&-ûY»z‰nÃÇýy‹_GÛÏŸƒb÷1MsÁD9èCÑŠ\¨t~·óêy•HÑš)³Š¦Æ’=GîÕJ8%#{[•_Tÿ‘ƒþ}71ÆQOj™Óó¿;#Þ7Ýtê ­>îCý\;n®KÓÅØ¿œN,\m ÏÏôíl‹ŸÇªÇY‡óêüÞæu®¿?ñ¦Ûy}³óE0µ8œØÇ$6–ë)Áü¼ÐøÅ•×]`ˆõüŸâbuçμo•åª?Ðå+ï[úûQJ|tÙΕÍ]„«ïíÓ(ò~.}1+Z”¯u`ÐÎ0®cMéÙ±„žÊ­ù»ñÏŸ?¼Oòq¢L¾âëH_ßÚùªÚ%¶Ï 5öÎ:7=`ùãk¾kšƒòUÕ:y±¤jHÜzp ù˾ pÏü…Ž·Ê÷õëÚ`Z±ØÃ`xåZÞ—`†wy9?ËÕ¨ß)Ô;ܾxô¨KöXNèÚ!/–à /±!'þ©Ÿ§êïçˆ0Þú]§7o,¸•ï-­‘ ûû¯¶Âçc¯=ïDn8¤(¼ ±.¾Ý\ó¡^ˆîÞ+ÿ<õÏ¿ÅïÀjî œ.C©&~p“Z¹põTþŸ\`÷²‰0~ÅóUëÎͬòÞ,_¯ñϕחþûöÂ×éõvO'QÜeè)9Úó<>‡Y×ä…¹ÐqtXšñ”®Ä”^ªG†i/¸‡è¾gÁ÷ÏøtÙƒÒ\i&'«š S?®©ïŸ ƒ¯2/¼lJ¬–tjaGƆˆ?Önòž4Ÿ×ó:Ò¯W ÆîÓ+!mvȟʹ»ˆîøç‚_  å†EóšÔŒ#3¦…çºV Ñ»óùJ™}ïb±—éÞóï<•°R11ÁÎ æ'ZQ Ÿƒð= k¸1ïèBùÏX’qžkøg™nÞö·ûïË ‘þ¡J˜D¯Õ•æ€ùµ ‘Øïüwµ»x¾äÕm…K˜8‚“‹iæ¯ß‹Ó_ˆ1Ž“v‚¡„òùÞ›^åÀ„£Ý äÂÌV§ ý¸g´ÁóS«8’²9Ì)OÆÎMÒuó§2û¾ïže|d­wJö·r`ݳ•¾`_P»¬]Ò}—#4¸|9+²œn¼ä}ß3á÷øù—þ~¤ãߌ|[ýt«+°0$sòr`ÇØµó<½r¡öB»#ÄÌmküp|¡» =îñq⬃þü(ã,Üísúöè+PÜ5\¸ºÍ+ݾ ¯[ýþ Ä×™³ÖºÉäÍW òùO•>àú¸{M±ç³¡¹à17£Zúg;¸¶àŒTµ,Žì>yñ±ß»`ÂÏ»x>ò{"Ú¼ÄxM.ž¾xó Þêkìz6²¤®? Ì‹9‡Ê]¬d ÕHƽݡqä]ì¦â#[ƒuûc<yhórz±X¨Ÿ«ðèë’•OOä€ü—Íìþ¹p¿ÎÍSÞµ…›6yùáûât÷Áx?៓þ¾¦ãy©ô`¯«PúíÚ¬Ç0Ÿ,[Ýöì› éÙá+<ê5!ÁCï]ˆ‰#†å¾Ôi/#üû|ÿ™ç)ß7çûWúóG1¾Îâ#ª¥ëÇ^…µ]ßÖ*9œ5ãÞ6øéˆó=í<±!\ç8R)Ô#IžøûÞï¿ü?‡ç÷ôÏs½ðu$ëwÌ©³æ*ÜÞÑK8.?Î=þBœ ?tú¶¬¼ ËÆÄ82s×’=Íx|ì°!¦î8ÿáexòRŒ«X™2úÔU°]CÆßÞžÓÎn¿áÓ3–k/Æw…³¯–gŒ?GBG¬±<»ü÷ý&~îÌ×›¼.x^jóã·~¾ë†MÞU°×näåÀ;å™®³»å‚¯½` ýšã±82~̱´Ù26oùªÛŸäyÌ_W›Çwïäã·Ú•Kí×8sÀo^ÅŠwq]‘Ñ'ÇÀ8¸7ìù*~w÷ö‰Äqæ^ö¼î²uã‚þ= Æó,ý:jGãDh}ú®•h9ÖÅ3ÍN®WLÜÛþAGØmê7Ýei2àĵ£¿ûÿýù¸Pf¾0£X|© ½ášÚãàù9 ´©\ø6º|ɰhGذ-|Zë“q¤ýÚ«C›Ëtã$Ÿ_ñóë2ç7Âåü‹‰ð@öÚ3Û;ÒλΩ“ ¶äðX&†‡­î˜¦çÆÏÐÇVo•á\õn|äû›ú}\Œq—t<3°ß¤DÐ~½gltz½«xcõ\îÙv‡ˆ9Jû/q„îþîº-ÓÝïàóV~/O›¯/°Ø%¡y`"4ìW{E‘9° äí)Ó*¹Àïƒj†œ}iô+ŽŒº”øJ%ÿëz…Ÿ óçÌ÷½ô÷‘¥ø:¯Ur“E'¡B¸µxp8ÿ¸íT ×-'œ¶T„ô:þÇ8åâd}|‰ŒëŸ×lµaÙ}^ŒGW£Åa¹¬ÿ#ûðVS Åx r8<Üôãr@Ä¡žÇ-¾~$¿G}W÷¹ñ}ýzSbÜJt{§A,ÆÑZÓ) â«}Àñœ^+{}¼!YYiúõƒ7¶»ï޵2Ý~ÿýù÷jõû‚ãÆÞYÿ¼êà$pé¼~¼oë?ÿúaÎC”;ß?ëÞÓœœmUååŒwq$íñÄ'ɯ_Þ?=~®-wkXæ~€ÁÌbqyí…ù$¸û À±bÓpôﵺ:Λ,è×6œÛ‘'<÷-ŽØ\=Ørhۿܧök_êòŽï÷”¹·†¯£|‘²é®2 ¢gy ÷¬—GÎdôW;Œu;ýâDRç³ÇWU¥x2jÍçç$¿ïòùC™<Æx&ÚÁ$6/Êz¦9Î']m;«ãg‡K¦4Œ'{fUX14ä/yÇë…¯“y}—™ãëût×@u¶ÒàEår EæT×/ø|.‡/yjáÐzìLaT!žl}û^Í!!ºûf|>¡?>K1Þóôö_ût¹öÍR–Íø¨†Ù© m¶àçÝ»Ð[ÝÄïþ9ÿMÒÝ_åçâÚ<ÆxÂ~ì5h§=°QCXƳÚó+äÂ÷è·iC&Š¡÷¤6.¥qä¡ùÒEÊ…á<÷³n¼çÏC›¿¯gÝ]­¶o½٥Ο!;¾ÊÌ“_°®»¬ývF_( séH•x"êo¸±û™îû |üäßcâã5ïúó ¾NÛ+MÒ¢”× `BÝç”jè\ì9sÍëÖ7úÁêÇG÷jOÖ ˜\®®L7.ñ{b|É¿/Àû=ß'ŒMyÒ¿á9ˆÏ1å{é3ßË—ñ½(Ó!ÌTàRnt”HðfñÐã{91¾õçÞE&zÞàæz Ê.ôa.F̃.…yÐq\îAWÄ|\8;š³ ‹G®ç]¤ïñ¨dÞE¾Ì»ˆ6Ê.Œ` =¾õ.Ò0¾÷Ÿ“0ê?gË<‹˜Ç#÷Ÿ³µ)륟…^Œ[hÈ¸Ñ (Ã?¸…>p£‹˜¿#õW3_ðzú?=]jðߣ§²g¥4¼1¥(Ê_Ê<Ì)§ÑçNcãêȘO¯Xϧז±ÌÔ(sÆ U3mJÃ|Ì}gÇœù˜kP–ÌÇ\Ÿ;RÆÒ¶Ôóë5g~½jÆ3£Z5ÊÒXðëÔü /sÕ¼FKæE§ïÙËYR=­/cÑR?sÙßø™;1?sê¡õ§Ÿ¹’qz¤ŒÓCýÌÃYS­eÔBð3çœ ãôÐÂçLm‘žo¯¥ïrÍ|Q)ÌÛ\Ê|E-õ|E-Û Þ\”G+ÖãÑr®åÑJ˜gõèûÑ¥p$cö2®Yã=ø2OQK=fXÏ—Ë›Oóå3?ºæGÇ}¹ÄÌ/½”ri±9ÉÓÌ›1ÍŒ‹V‰2úƒiæû‹¶„yÑ…1-õì¥ýŽþ¡½œöqÚ·yϦ}šöcÚ‹iÏ¥½•öRÚGiÿ¤½“öFÚiÏ£ýŽö5ý>ögó2úïUœ«º€õœôë%ú½ƒóÂYoØ¯× N²ÚŽÓ«e:æ8±A+¢‚s°²À³§>”÷,¯)°(ëÔ±Q(›™LQÌ›šzP‡é±Jè Dý¦) žò¥4"™l‰¹Ž*m%øÜR¶õˆ¦¾‡"æ÷VÔQ`;Yœ'êCe%x¤ù0_gÊ'¦lb%ó#¤lQæKoùþ3.ÿ3.ü÷—MسHA‰Ê \xu9 /+/xXSÞï¼»Æ cÞ°NzÞ°bÆ^âlxÊÕ0Æ(gÃ[2†²†1”ÃQE(Û‚7¶šñEdŒ¡l«çkÉüa5Œ¿D9£œOý&‹˜—v8õDÙRïI=ÆgæQ޲ìXî¥/ÓcJkÔ 6 U„cᆣJPÊÌCa{¡PFXÌÞͯKÊ‘1Έ­©Àˆ§ &Ê¥Œx,to=ΈãŒPŸXÎR6×ó‰µÕóÓ·dœxóû–1¿L[=¿L[laŒ7ê¤Çå&Êõ` =êõÍY$FŒE¢b,=c’1“œùéK™_¦-c””0¿Ì(Öx(#^Îcé•2–^ª”ùe†³ÆÄñ&ÌK_Éñ>zŒx}“ôÖ(õˆub±”5J=bihúG\æcr]ƒßã2í½´ïÒ>K{¬…ð+hû&*´=‘Ï´ÏýÆh/ƒ¿ŽÏÜ7v™Á¿=.‡”üý˜,2| )ó¢c£a-*+ s£ªÓ—ò|é/žPSàJRîeò0þ.õ§Üpê‡l¼Ãõ˜”A}Ž) ¼H$pqm1?#˜Ù®„ NÔ—X†ùTÒQàÕJ,oxê¯*·˜´Ô?˜òf){Ë’±)#ÑyžR× é?ãñ?ã±ÁŸñØœý®*úÏåÞ·¦œÀûCiëKúë«´¢À™G¡$X¨RæÕ®Çü¦,Ä"ÆBäÌo[Æû*b<ÚT J\CðtÖ0^EãÓŠ±°ÂPƵùÿØ;親|‰C. F°ôˆX"(±4 ôÔB )‚1ˆ@ä! Šm…8<Œ%`G"c02.O›‰@ŒØ6#(/2õ1÷Û={§§XÇuïZλ„µ~‹®…î„f_6'§ÿŸ—ûÍ܇(¼ßæ3 } L §½âº0©œ_ÌaëI`@0=ª™í•Ñ͈Ö ÒÌàú@†yÀàÐ!ÄvæÌ:„Ùb@P{@’y4ò÷7sÛ0'bHº„Žûl™gC«òÙP.ç>[1·ÝÈýßq>¿Ú’À„ÖRÀ„¢ðò²°¨¼ˆÂo“Ãç¶GùüjáÞÐq÷FœûÁ<ܽ¡ã~›ŸÛÎܶi>¿ÚÃçW[P: ¹Qq‡€¦ Á½ÁŠˆ¹7¼,|~µÆØàþ–øÌö(w;UîoµÛÆ}Ž‘•s‘ù¸ÑJ ×ùíN?ßççûüÿ²Ï ü±˜wÊÍZlx÷n°o á^[á&³ƒÐ!:înd>qFqŠë-Âbå^[ ÷Úª½îµµ0;"L6ࡲqs72çxhÙQåÞ°7á(ӨܶZ„Ð<ŒVv¦ä>#w’³pZuŠ“\‹þ¶5á)“¸§,Ù„§Ì§òo¸ѧxÉ™¿Ñ#)^r÷”©ý¶)`Bèý<øà€{´ùŠËÑÏçºÊ„ã–98Ônòs=JÌEà`FxA ¹ß6Ã>Ïâ~Û w¸¹‹ÀÂÎɽ¿­ûmu*O™p”ǹ§Ì¢@â~[á)spO™Ñ¨8ÊS*Á¹Žr ÷dTŽr-w(E„sp‡›¶ç>>?ßç9¿>7ò¿Kš)žòT3ÅSî)`ä.Þ40!^æIñ4°"~ w) W9óM¦5ŠoR¸ÊMÌ­ÒÜÇë`F¼ Ž+^îã5#X^â>%÷NZ¸wRøÊ½ Ìžd€™9èÚ+ÎsÅG—áN^/H#‚é)`æN%ážôp÷¤õ ° ¸~^ !v0 f'ˆBí)`ÎSœåÌ©ÄÜ“Q GÐÌ}$îåeÊËkD¸A’{y½ ýJÌ[žƒ¤€ái`FQø¸ÒªòO ¯óOڹϠòËHÜ/“änlæzÌh×£ða›dØ×ì³" ð4ûòñPY€¤ósߣ•û…ÛÇCg> ð4ûAôñ0Z€¤ Áô4°  >•óÑËÃj~Z+ Âka Gˆ ôÌ» ’ÀˆPû@Xò7¶$)ÎÇ0 è.æ¶zÞâ@ºZqõ¦ à©®Š»×ÇËÀÌýØ)`D1ø@XP>…Ÿ{m*¹÷ÑÁýwF”ˆĹÿÎRÜç) G¹8 ŠÛÀýw¬h,À4(wcGd EYÙ}@Ëî­ 36¸± ÜC笠±Ý*7¶¤€‰»±ÕÎGm‘â¿ ð2³“²ÿÙ¯óþËþïÖçÿŠþþÿØÝ¾ÛÔvv:ln; »× „›ÝbÜÑëa gžqBàa Õ(þñ0Ð#N:ö™0÷ôj¹§7 t‹„¹§×¢@‡ðØAhÙ9”{>%„ÉÂ@‡PÙAè~ÆÕ«CØì t„€᳃Ð!„vZ„ѦS|è„2 $Ó bÀ€€ºAš»z½ ÌlhZ; áu±Ï¯ó©d€Yåê5qWošÝ'‰p‡€·²ûÏ€A·ƒ(ûûyuçøyÜSªWyJ (7÷”Q>^ à`æn^Vv&ƒâWϰ{/ØYè˜û“{yõ(7Hr·º$¹—× â@ß„—7©r«g€…ã맸Õ-*·º `ŸÍPÜêj?iœµQJNî(5 œ¼çÏÝçÏÝ9¿s·•?—05Süê™fŠ_Ý2ÀŒxy,ÀÏîÑagmà¡°ƒÐ"6P9ÖC<,ö‹ëàãá±°ë@ƒYdØ×”Ÿ];@¨¬À2ì Ž€xÈl ÀÃÆ<ë~ Aè¬ 4ŸøA†} úa´?È3‚éö¹»Ïd€E§¸Ö5ì³z„ÖB@‹ð:@bˆÂì)`B¨ý Ã>÷ÉS\ëz .)®u#‚îq`@à ôW+Žé 0£¼ ÝUqNû¹sÚÂ}ëi`B1øAXQ~^V:”…D¥áQ Cy8A˜P"”‰¤ ¥âg×=€åâ2(¾u#JÆ4(+ðmÅíw­Ç€Äîa! CÙØ=T@‡B²ƒŒ ®u# Ê’ý׺GåZ÷40s×z†õ; ,Àî¿B‰ÙA°Ÿ4w’öë_Ýéÿ¬ÏÿݺüÚ㬿©·Õ×;®¯­®>·§ÿ·ý,ºYÝË¿f'Ûø«Áfu€0°i ´Ø¼6vÍè°‰ ôØÌNljˆ=6·³¥âº×a“»@°Ù] ƺ›> tØø.àBà1 ! :„ ¢@p8AH‰Ä€„°¸@ H„„ð8@ H‘„ar€0*»F bìçØÏ_=‚æq`d׈A˜<ȰŸ!e÷O±û˜Â4°Hø3ÀÂh>~vÿ2‚Z„ÓB@BÏ:@Œ—ØÐ ´ÂëQ !ÄN„Ù’ÀˆP{@˜n?Ð àVàggh=4»˜x/½Ä€Ä~ÄÙý§(H3ÊÀ R¬gQ IöóC(†$0 Rìž(„—„øyYXA€—†„€åaa G‰8AP&.F”Š”ÿÏÊ?wÏáÿŸú|Íî…µñ³5»'–ÝÆî`×ÎÙ϶°÷£êl¯þ™Õ¿oËͯ™³ç*~}Dü‹ûO˜ê&Syùוٹ5cï}hâÝ[ã$æ,—VOòY‚Ä|Γ“ž¬ê2ûVó >^XÒö’Û‚Ùù ‹ë4•g=ìa$<ÎõäXÝM¡)­ýî6oœ*Ú4sS"AÒ™õ;KÚ"oŸC—ÖÙ‚raß›F.þ¢<ë?/Û9¦j©ãXÖŸÐÔÖÿܾ¼âû"ä ä’fKã´jyÕ—m>H2ÖH<³hçÎ)Aù÷¯ÞÕ,æPˆç+潩ç]ذþäŒÛÛnh„6ä°Á«qJÎ,Ÿ¿?A3êâô¤‚g;µžo Êó¯:sÑD·,ÖUæíÍλRæ4ž“ãÂú_l6þîí±ºîÛåq[œŒ¿ødé{ jÁ…#+¦^E§?ºù®.– <¹W‡@îSîì÷çxAyðæÇ³þõòô0î Êãˆw¥#årã}W$|-ʼʃÙ9ðæÙâq ·XR±9B{6®êøv-mÞ3êÖg—$HñÜF7ýãÁÄ6{©r(i¶œÃ·e6µþï§Ê³ó%ÅßCø»Ä\¤F>¬_÷ð}oÏ[¡þšþc«ÿPK;J'ý‡=Akº·œþP1%V¯yfሠ<¶ûì!ÆëÝYÏ‘ð 6šë…õ¦\Rc|~y„lUÍí·°–î´îÜ_7!AËºí¯¼v)QpËÎ!}ï ÊÝÿýŠ>%îsüÅçóbÙﻘÖÈK2ë„©jõ à®'"tè¬_ž0©–N?üõÖÒ1 ZܶdÅÝ_RïíS—ENoüþîtË¢_²óëø¾VÏi”°îÑüû¦ÏzRPfVéüÔ)ü/b»˜7«žïdÃã4«’GèFövtE-µ^ÿô°V% Ú»eý¤û†]O§†±]A¹^ËÙÌÇû]ô˜xÔóÆ\xœõƒâ#ÔüØUw5¯¥?M÷ØÍ êØj”¶äxuzdãöI%Ayþœ²#Ë æ>5ö_Öd=bŸ6òðàq‚7ÿôÆÅ:œÿÒ^ÃÉòA×{/¿9A#BmƬOôå’»ž¼#(_Z/škðVŠù²b~¨x½ê÷?Ö-ü~ÖÎ{Ê#ôÄÄ×Ë–}TC=åÜo“Ƶ¸/–;½˜JJ-ÜT”_yø“Q›v7Ìùó•ļҦž3±^T¡õ²Œê[±åºm==«üðÃÃè¦W[œñâõ˜Y/Nh˜w'úXøÔßÿœÙ'L-'Í:;}|vÁŽ!³C5ô×à‡Ç6]‰óÈÜm—W•ÒÞ~W¼¤ùMoÜ&U5ÌmRòõvöõs¤ÕsÞ$¬_¯ Fè¹býŽG_©!iÍœ)y'hÛ®Öµo¬M}éÇ6býPtàø_wgç ç›üǪœÎúÍåën¼%iöíŠÐo1s ÝÐc︩§â´3’{Í=ƒî¢¼©c §†å¶ëúl|£Ðõ¦+ç L‘˜‹¯ö‘ØØ÷ãøª!…»#ôÊúµûÚ”ÕPÀ|ÁÿÁ8åücU÷Ò3wÒœðkù#ÆeÃÔù¯~±©<ë£UæÙfš|¾.¬û ›¢ŽÐÖšÝ×½1·†¾Oõqó»qR溦q;j.¿xfP~ª¦´ºyiƒ7]™cÖ,;ÿ]ýúù°îÖ6Lȡة¿yjR í¸ò…ëqšöÞ_|z$ ¼§`üˆyAYÓ¼ÿʾ£Ë³}#¼mÏ_]æ|äòf$æ¥5šõsæì^ú·Š-ÎOÖškHÿùª½×lˆÓsºÇ¢7‡gŸ÷œŽÌS.‹<*ûãû&ç‹'±n—‘z¾ò\„Ê g¤ßé^C¡—†ÎÿLœè´=ÿÏEfzäÌÕÅSqެxfüô+¶4Ì!çm1WPíÿÈqœ0M8}é¦+–E¨í×SZÈmjèžÏ¯ûjzyœôï¶«\ë*¦ ffl”‡2­ÌÎr¹ö‡wVŒ>S´?ùɉ±½ÛeÏCæ”cÝ*ªË/AïÎ?Ö¹Ý;g«éʙӊz?§Çf° ùƒiA‰yFëþAùÖ+ Ãû˳s±Å|fãÒî‹|—fý·êï³ ë;ŠæWŒ\!f…ê´ššçïÛtxvœ: úk«+ç¥ieOo¸OPVæÂ•gý b>¾²Þ‡ÙóŒxUÏÙ³áqŽ9وКÕù/ûUÓÜDj݆Éq2™4í®™1œôÜZ|kï üôºÇ~.—{ò‚Ùž¯§zÞ½ ëÇßšÞgø-ZðŠWv|XMùå˶â¼Ý¬fÄ&Ÿk$õîf_¹§[PnÛ±v~¤<{~T~?™íÛ¦æXû°¾ëTëŽuÝ"Ĭy÷﫦Ce¥ÚEqš½ë—õÉMÑSÚÎAÙ½þÚy¦4äSx”Ä¿kÔs?ÃXwÉ-þ‘j¡úñ¸r5)sòãTõæéιmî¤Ûw?>àÑNAÙ¨ýC‹Û“e?9G*¾¿Ú¦çócýô“ÉGz$*i¹×}g_­¦/#-^_Ü.NG–=/¿¶o4­Ý~ íñ.A¹K]ð¿MX÷Õ7* ŠVVRí“}ÿòVI5ÅF=7·ÿÁZªYoÜÛªîvªŸi¿ö,96÷dYöý²±ÏsWöõý"æ¿×ïs<γ÷PMm]¿7bÃŽ+Q,Q,±ccÆŽ=¢"Š;vìØ D°`Ç;¢`  Xp‡¦Ø#5XcbÁŽzÔo®ìµ6;Ÿ÷¾wŒûÝqŸç=Œñç©k'{Ï9÷\%ÿŸa·Ýæ-kS`úÌSÅã;dÂè Ÿ&ÜȆÃ͇:5ÞÝwºTµ:¾Ìå«c ÐW°þ‹Õ™?qZ•8þ¦=î5U¥À …u›d‚s«¿Þ‡%b¿ç»r[ëq=á¹ýŠ-G»á<5+øÀ¯@®tFÉw¸Ðò%Wã¸Ò eb.IÍzŸèÝ52!aò¸¤:g³¡òª*Xzºƒ¼òø¤tG¼?sbÞHy'iÎüóÌÞóü5·˜=!À¦À_5 h-ZÞýx@£É†m7 /²ä šDà±\àÏ}îÇ®r–¼Nœø]xŽâ÷¤Ç»k×ùQC§ˆtSqï}TV®ê·-,:?kÖÇá»ÌXˆüÿ]æ“Ìî+›Ï‰ùJVórå_£È,jïìóËÓ˜^ŽÏføïφÖ‘ ?îë Ÿî*Ñëc Çúyæ¯ÏæAÌoUÌ—à¸fûüoÉÖîFçA·3àS÷¼Èi[³¡áD[í‡FýàÁ¥)}ÂßÅp¹^ûGâÓü¦>ÒŸ…û̸,âù³¯3›`_Ó’¡Çúî›GÆgÀV·ì¿†úgÃþöƒkVð »ÝJûŽ~Ã} ø‘Ñ·à=ÁÏs;Gû.ˆšvDìþ˜ã™Œ;­]ƒ·QÉ@¾œUx46=›2{A6d¯›êí “aæËÛ1œë¸í‹¦·ò†}^v߉ñØÍñŒã÷T9N}·.ôiUg:ìË€'õºW:é™ «Ï;}uÌpPxG,‘Ã]X®íþùùÂçáRÁg^wj·Íck½tL2Lð÷pÞ¼9r+'Lê› yç7Ü’&õ“š>(ð·äˆ½ò…õ-b¿jŽ_Ö  I†ÉKi¹&¦MIëß*v[§µ=ë­¯í+ÖþE w•Ø ÷ äX?Ìâ…õáìy›ãÇõXÚ6sË·$¸Vþ}¥°,sε×vø‡©Ï_©â½ö ŠÎ|ÃMé«Ü5êt€0ÏbïcÆm±ôwÏ|NÍñ>?W¾Dž³ÕñB\:øòõ$ï °*>ïé½"Ù0eçÆXýÃáà¸&Ó=ñN 7wC§N‘½ ê,ë¿Ù<‹­'0Î9îqü­ŠïÝS—$AîúQã¿Í€ñšÛÒÊo² Ôæ´ËÙß\¡fû«ßo$Çpy 3v´¯ ¼ï/nÞÕìpô á>±ëšãÇ­S¬NÄô¾Iàó$àáÀ ¨›­¯¾înlZ29!y0t/9&ñÀ™nIW«€¿å“ºáÊeë+v¾ó«Ó=íPƒÀ?¯—zâuÊ›ix­O”¬gtµ¾â_åFìðºS!ñM?ˆt HÂëܵùdÒmáó³úÀÖkÙûÓï8nú mT¥‰P£NÈš3`Éñ'é²ÀpZªíÖ¨/JÕãx| çìP±fÂ%á¾³÷ßb½Ç}³"¼t¹äD;yÖÐ.-ðóš¹Yp°¦¼ÔŠï}aÐ[÷Ž11\~› #Wõx¯ì=ÏÞcb_|Žë«ÝÚ¬ñD0cXêf€ã™‘ã÷É‚ä¬ËûBàÈ~ïE3vÆp#|вU€°®Ëž‹{ö¾ó Œ8~Ù…î¶oL„yª¸ŽÛÊfÀþê[Ù‘ù#uã+ÃòwsôcWÄp—Ýø³YÇž#ëX}á×]í,øîV råP÷û[·í‰° lêØw¿Ò¡[•Æ®Ÿq~U¡ÒÏv1£‡Ðõä®~[¿õ¹ý…|b}Ë#Ög1>ão›ã¯SÍ LNã€Õ'|N‡ª'Öi³0 R7Í/é¶Ô®¶ixq ·¿Èç)Ë ž+û>̧šÍcz[{ °uesüãuž7ô9™ÚïÒaBée«¤]³àâÍÒu‹‡@•ÜaßömŠábƒ§]¶wÀdÜ;ænŽwç$‚lÏŒÐïÓ¡F¹[EœšeÁXm‘—· †ßÎO}Cb¸wbl¿FÈO¶¯À¸a¬0Žƒø½ªÃë”é¾Ä¾ÏüD8½¯^míëtØT³Á†€:ÿCsÒJ~=/†KÚ£X~ÿÆøˆb~šÇk~Óîy±Y‰°óü ðþ^i\sÔÐ Yà}#±G¼?8V¿ê¥j~1Üv²|ù¬àþ²÷Ë«?qh­b¿O–KÜ >oT”+š!#OîØv¯ »ÄÝŠóŽá:¾ÏÜ𸶿ÀÅ`ý›E?Žã¥œc×D˜t¥öµó¯Ò!bLƒ_ópª¬½ºØ2ë>àübvvÿ¡1\+O3nÊü~{²õmñ:¨Ç5osÔN„•íJÚ<7¥CÝõ=~¶x »z¿íy¸HØw9-°E ×..¢y—–þB¿ù'>¦'Žw|º_Ø«DxÚ²ù«-÷Òaû¯«ïrfBÚ¶‰åæÍëÁ)sJ;Vኼ\kaý–Íûzo¿uáD]`ÏÍ·8îâiIã§ ³;ù‚é oyï¬,-*•ûÚþ d Íe¹‚|cï[öýY|ñë> Àþs¶c³8{>Žqü‘U ù¤K€)û/Ko]H‡Òƒ{œÚ•˜ ù£ÞºœPÀÿkMg–ˆáxNÃa…å³köê6þ¾4àãÇm0®ÔáäÈ8/]jœ¨I‡Ë¯oëÚFeÂŒQó7­{î ;W®õÉýKËñõ~P'X¿ÌÞŒ¿À¿G%|ãø¯^:äîO€‹ý7t>’]Ÿ=>k³7œJÖ¨Ôíù0™¬I ý¤åfì«}J¾Fài²|×5«E¹ò÷-ª¿ßšÅŠ´^ðZ§v9]:0N ²{ðeè$Äñ^Ëut5P=}ÐòÏÈúo ®,Ž_I•ý¹Ì¢~4tµúP:TY5§ÅHïL½pôdÿÖ# ÒÁåÝÊ”Šá=ê^7aýÇËÞ‹Ün¯dºlyÏ жo³&‹÷§Ã7ÇRŸwºgÂ¥wgo.w‡Ÿv6ήà «ÙJ{kbÁýåß#/„÷ 㔉×íÑïG‘ 1Ü[‚++ZPßX½`y-¼‡ÿ0ßVâu"þjmçP)Fœª¨èf|?v¬ÕeLcÌäßSœÒÛ›/§ÐrG‘àjŽõšg«q¼ý]wï›§ò¶+»1âF»oÞa› ‹Fkõ{8˜¶vÛíT-·Ã›ìè­î ‹kwlT<¿Öáø}©U}LS_- N‡w]Ï8þ‘÷Gz¶Õ×YÉÖãnGh9¯bµ²¹ÜÕt=ú‰Àgë$æ8ÆñœU—6Ñè rwJ°:Ä?©Rçʳ €úE~­)?x~¸–Ût»H_ÙÕÂúhé [oœUÃbŸÑjq®<êh´ôõA4ox­¯tE:Tãšœ¦Ïë²×öÏ)ã•?t»5%SËÿ½pöu«9¶~ÆòAÌ7•àx­MkôÝvè`÷\²Ð™gWôšu;.Â=š ¾äã´£Ûwz¡åÖ‡æ¼huu5ÇâŠ= î1ŽWÔ§LéJlñír~y:´þkÀ„XœwÉÖ…W=Áê]²è[––ë[¡Ç‰-mÖ} [ÿeû8¬ÿgýœO ¯Ch‡v#u°¸ÅºO¥ƒ¬S…ûý°/5ÇÏhó­Ù­Õçµ\ ÙVì°FàV³þS¼ªÄñzê¬ÚÛDV›VþèäcW°­9%úö[j7jïh˜8^µçâ>-Gg½oÄë¯øz$Ôã?½ïÔxYŸï~âÀ~ê.ÍÏIéÞøãÆ‹}3 è'·8ù}ؼäi…xÍΡ-»Î#!¿Ä÷[‡ãyì¨y`ÿqŽ®+§CàȺ£jb¾¤òúßnŽ‚K?ûíqLËU(+Y.½í'ðLÙþ¾x½Íˆãu¼0õ†7kw½b¥H‡è™Ýúõ©’Ï缘ºñ‚;üîÖ ÇŒÿðñ\ã¬ã~ÛdyÅÖ“Äë³V>¹òaæ…)Ê%õIÞê’¹ƒj?+þ3>¾8Õ¯‹Dýœ´®æ$Og•èDZûø'¾ ÇëÚ÷Ü”7.Ò}–t8õ)äá¦é°Áoû¤t«aP³Ý¸†öøü£Õ2iº—ŸP¿ØçbŸ›õ÷lÝTÌ‘ãuvÝx" å.B¿mÒeý;¦ƒC±Ï‹­2ÓaHöªî“†@}‚ÿˆ×r òxmv÷ã,÷~XÌ¿=q¼©{߯˜{ü",Ÿhcµ¡k:œ«äûÊ>9&6¾P옳ºe_TŽÓrûÝnÛïZâ'ÄËöï-×…¾YÌ•xbÇ«t®x. €ôt¸™§Ù•“s–|9Ú¦Û xµ-ºìÜ“ZîÚ…áÙvù ]qð"-gÞæÂÏÅÖ1Øsgëâz`µ$W®˜[Û'ö^<ªïΦXg“2‹Ø.N‡Ñ%¾ŽÍË*÷˜×z®–kœplÄU?¡_`Ÿ“ÝO¶nÌê˜w¯óu¡™ø 74C²æ4J‡&fÀ\:Ìzz®É³XW¸}ã—üì,-—´‚Uý8¿lß‚õÿýŽ{wð¥|Ù²x°>ý‹Ü1¶ö¹g#’eÏdßß±aîìîd-÷xÞëÛ[ òƒå_'ò„8cï5ñüݯ£òZP2ªM<ŒZÕßêW“th{ë4`|tu ïwz8—Ží(-W/¤í%?Ž­±¾Ý¯8ν¸”¿îé/PžÎ[2*Èm˜_ªË:Üí]I±s×rŸ’lݾä+¼7Øþ[7gõÂb½Ç—˜7>.@-uÛ iƒtT¬Úä»åñ>Ç,>PÅÆ †¾Ú+r× Õòó°˜:Ü^:¥¶»• ™Í‰/\ÁÓ§á•ù+´Ü$É­¿+Þ&Ë—?­+Jp\òmÓ›Ÿ‡OƒŠßûøWôœ¸¹ý÷ø4pÏïiŠZ¬€ðU¶WB&j¹¼¯Á9›‡ûr,~íÓzvíûYø¼âu'9Žëkê»3CÕ^ø5 fÆ9|½™ 3¦N¨zu ˜—3ûh9né•ùÒø²–wl?Òb¿Ç%TÑ[ãÏÁ‡×"*ä¦A‰W2†ìOƒk¹izÅ›´ŽI@Ë}Ù>ï¢aYA¼±y&;ÏÆÖŸØ{Ï¢ÿÅëì>ñ¼cý‡g¡³÷MUÚƒ4X1qâY«iàåުȤõƒá̯Èt-øÈaB¿¾Üçéwƒ“>™„u!Ö¯‰ûT5ްilÆÛgáÌnãî£øüüWt^k³* ¶Ïm{·ñ0ØÖÞ1w¨–ëåøÕZÛÅßc¡±y½E?ã†êýáZðëƒiP¥MÙiûf¥Á Nák? W£–íï®ån\  ;å+¬Ç±ûË8õŒWÆ>¿xßÙˆ×!»KW:ÅA¾«þõ¹4èÕÅÁã˜G¸žüòüiW8ÓtíëÕÍ´¿Žä+Ì{Y?ËøvŒ+kÁù^–+šóÌÃ>ë œìâ«z2 û{¿*Ú7 Öt?>áVW²&ãÍkG-÷­¥m±T½¯P¿Y>2>*»_\¿ÀëHÞ¬RÆN?;Eö^t$ Ú xv«CL©öצ¹ƒ é†¢§¶wÖr½Ç’™ƒ¯À1µì;*ZžÿÃqó¦ŸmS¹ì(z{Këã0jî ÝÕ$Æu?øx¢#ö’ZμŒ\Ï·Ð:½NøÜl¿EÜ÷{âø¡3n[ÇDœ†%¦ó#¦Az^Ú‡°:iP&pëðËÆ>¼åþuŽŸ²©dýà¶÷‡õçü¼¬°}Ds¼ã¸µKŽm¾xÊi¨àëà[bwH¼Îçoª˜óžG7ûÕ6ªšlê‰ïçÃZ[ ó¸‚üû±¶å:Ž×aΞ*Ï»ž†r?ºF Ú†õïP£áíK¦SPÉw%šõ×3ÚS]0"Ë+ãNŒ(ˆGVOXŸÇÞüs”ûüæ¸Çë [?¶œ¢æiXq½d±N[Ò`Ô°¥£Kÿº #2Χçåð£^{w§¾ZÎ|¹ÁõœÅ#{O²uÏ"ªóK«Ö¨ÃÇ;Žÿùà"®t^,øeæíip´7N0?߆Ìáì¼ÞºÈ”yð¾tûpó@Y¬l}˜ÿÜW…x©iÍ9çwÍñ¾Zø.uhaµ> G¸sþ6Ì hVÛÕm ˜>]¨U¦´–;¸ý¹›Í _Ží‡þé<¢n9Û¿‰oyÿïÝ §“·áIÂù"eŠY—3Æä×rfÌf¸ïßÎ'ðñqE˜ßþ駯³µÿ“êcûFCÕ”'g¥Q¾õmðØåQïä%WˆÌ¬û¥{-§þéR±†›o¡ýÖpË)~/Y­À÷Röê¾ïßGA]]}n¥* úU±íû$à6|]ÖÂÿäP0c®+i9þ÷ ÊBûA/Y|ÎQ‚ãÖyYvÝ…“Qp°œ®DO×±}·–æßÏY­ž®H ûÞÜ®õÖAËÝäáà7W)ìkóŸï¥0/Ÿ–ã¸ÏogLáÞ.x±6 kŸV­w›ò¸]!­NòÍ*˜'üñw%­[Ï…¾XÜ¿zâx¼>ÚFAß}¹?¼CÒ`LW¿EÞ†7‘?#k4UÀÌ[ÍJ)ð}5a¦uÙk«þ–',Þ ¯×YœÿÀëÜ ø«Íê| b, ž¢Ô¡‘9ηÁõšDÕõ×8ÂíH\=Ÿßauhk¥PGXŸÈö­Ø{ÙÏ8îí⟛Ï?§¢ËÏ,yrû·»¥O$ÊnCëj"bZõ¯íÅÂŽã|¤Îµó¯Š`?[x?…å#‹?¶OÆö)ÌqŽ×Uní²f+40›ìx¦A‘¦Snö¬ê,îW2÷÷h5ºŽÕÜZ®Dy/œú û·¬oæ×µêãñšãÇ]¼•>¶¥ίjÖ3P—#Öx[é6ø— ÷*yF·¯¸æþì­å¶çÎjSõqAbëü~Z#º¯P‹ë•¹òþE!|n75ù:ãòØS ®j}.3 6í[!ÿñZãmÞ0iñ¸s+-÷£Kï%ÏŸ)¹ÂóV¶ÞÍó” %ÂÏÎt‡¿rÿîŸc#Aqyb‰¦8/¹œ[¦uå,=´üzeˆRêóÏ5é”–³ß×0Õ&Lù·ù%‹GÖ?³õ‹ó!xá­¶­L̉€„ œ—kó»ªëÇëAñ©ôðeõG²5»ê ¯¢åLÿ4]™¦ž'û>…Ï™±ûÏ8Ñæ¸ÇëÌþkíÕ€vPìÙé¿§Á-Û #¿ÖÃÖ¾ÑÚõOGÀ¡ˆ~õàûaàt¯¢š"¾Â9Ö'²¸dó|ñ:°ǯ»ÎÐX9á$ÔÚûqhí’éðy¢SjôðyѶßwʸÁ¦ KÛŸpÖró'=Û‘`ÇêoE`ç¨Åý›Ç{¿fìªJûOÀÜ‘þ7¬pþù&w²R㣇Ó]¼® %¹Ç31Î÷_6øJGöNÎg±~Öß8^­~ozÅÔ9-.;ÚvÅyüø/#ÚvŸ¤‡µï?ÛšŽ …MÝ ™\Ëy½îß¾îLß¿G`늅ûeñç¶Z•+÷õüñœeá Ëïïáœ+•ŸPvˆȯ»ì\aVøÛM7†k¹î—ß÷8RÐϱuv¾—×d}…x=A‚×¹0êíõbIÇá솹))CÒ¡¾æF½b ‡ëo¤JÛ •|õ²ªëJ×7é¾Â>%ÛŸ÷ùro÷¦noë8:Í_ßN{¦Còdïw ›ë¢Žš0cð盵\9cóu~Áû“Ýþ~œ×`ÏÁb]¯Óæ¨çzíË0xØvÅ"ÝÂtwó^;«¶.ôT„´ #]ß:_ƾ|YË‹^«û yË~b9O/mñ;%ŽŸwÔc‘jUàlèJçÀth£õÏZRZ[¶.Âõv…99õ~ïq?ëh÷.~Âïüø>¥ î/âxå§ 3Nªeòî?_¿%îÔèÿs|Þ-м<οŸ+¨Þï±Ð ëmÆî!‡û:XعGÖwò<ïŽ4nZðñŽ×9ÞækH^ü1ÐHn¸·Ý›’Æìn\½Ÿâ÷î<°8b Ör5‡Ü©µ|¼Ÿ7ìþz+¢$«¯½æÕ=½¸ï®p‚Í×V¹p+šóy€×ñzµ0Xç~ ð&Ò¥C°i}ßvoÑß= ‡ÎÃ/Nm€óßU?:ጞ­Ïö{¢È©6—›-k ¬.›ã^™+_ùØêûŠßG!"áGøè°tØò‚üÀðì9y2µG§@~5ãÔTËùaÊØvËïoë üóÕ õ’çº7¶ßiŽ{¼Žú~½«µGßwJ‡s‡ç,îu foš¾aÂwØ~­÷‹¿°^FO™rÞf5ÇöyY]dç«Íqã•ôIKœ\õ(Tâ&”«~6\¯[n@å[0ÌÿDØ”e#ìV,« ålnD.s¬´Zx¾<Ÿþð{¾?ªmyžÇÏ>B&BG€_GM‡þs,™Ç]òjS‰3±GÀ£~õ= Óán¼ïìŒS7aë¬Î©½—€×­6¥Í¬[ð-Ï=¥çðÛ'4Ç;Ž»fï„LÉõ#ðcKþ½R_ÒA?æLDšßMØ»»ý“¶=‡ÃÕN»µ\ìÑ%—›tòûÛ>žç¢øï`ÙÇàuîMVãÒ(Ý<÷W”m´¬QßzÚ¨›à»•4>®ðcá±Îƒq>WêXñ–ßKû })_ÇôBŸç5’ü"RbqnÔˆãWß·#hý’#Ðèh™¨ï Y•\ºRû›ÐpÀ„QφòÉÔΪ5 ¾«7,þô\­|så\ïÍ’ÑEŽÀ¼u“6*Úd@ÀÙ-Ϋބù¥w|¯»k4V« Ÿ®å¦öÊè¹£_¡ß?ü>‰ÍÄçt%8þã—dã0l–¯¸·OÜﯯêñõÌKì”?ú½_*g‰–sû8eßè‚ý:Ö—Í\픲ó™°>À×Ëšý°¯söטýsÁ€Ãmw_ŸUƒ×x<1Üúî·‹âÛöRj9Ÿe¶Ö®áع]Öψû`O/¥3©¤‡àHµ~Ž-Ê ë]7 E¿šo¬ç¸Bƒ¦Ã:T_¥åz—ú^¦ë„5\áùùñ{î½S4°ì_pÜoÇñ :ÛN©ôÙ/”p ‡ÿÆðÙýåá«áÀŸ¿Öräÿ]Å´ZØG˜TkL?ë%¹B]áëN‹sJjœù`ÊAˆÎXÊÝÞÆÁ&Å£‰7À¾ëÆÝŽ[ÝàSбºÓ¼µ\k«asŸT[ý·~?|Ë™ýî•­/˜ãÇ/ñÞ·ÎïâaÔù7ÝçïÍ€R±ÛÎiwêIÆu/8J9í¤åv×9~ñc°Ÿ°ÞÅò–õ_¬Þ|\XãWz¹yeÄë|þòâ`½' èŠúƒ7GfÀƒ4Éù„7 ýeËð§Ý öw‘¬ð1ZμL¾ÃO8(œï¦yp©r·=ÏÁÊãenð·³À“!M•ŠÏ€cÇrvº{¦ªjè^9>4^ËI#5Wÿmý”ïbùÅÖ{Ø|;ÊŠÿûÇ{ëï­gï-+zŸŸRJù”Fê·¯¡žˆ^Eyïl;ÊŒ6ˆ˜Ñy”MF=µ|(—’q°ò©Ï¾†úi/Ä8êßâM½%”Í<öÃh’xRÿ[êÉ­GÉ('ÚH9Ñ„Gi¤þújšHîÔ‘øwû  ”J½k]¨w­奤ReåP2_ý<ê«Ï¸WŒmEùÐq”M8”qàCDÂñ#Þµ„}E<´¤"^ ñÐRQïZy!­ê­ïD½k‰·¾'å¥_wê‰h[ÈQI=‰¯K(åP2_};ê[«£Ü+e¥((+…øº¸S?D꩟J™W>ÔQVˆA)fCë('E%òC ñ'5ÔOß“úéËD¾)rê5žO)ÄãÅŽ2¡ÅìIeO/}ÒEϬjñ?µXiõï_‹mè} lBeš¨·xõÓò¦þ´Ê 6Š8Áù”K¨¡+%e2æ IOêmH8V^Ô—VB}i‰—–”ò™Ÿ¸†úOÚTê¥EÌùm <:‰çr( µ& MõàTP~ á­8#é#þéþé¬þ3ú;ú= ;͉²Óò¨G2ñã´ÃÀö¡~œRÊ56‰¸Æ$ØÝ©?2ñ¶WQfc’ØPodõµ÷¦^œRêÅi@É(Ϙù"ÇQ&ñâÔ£$Ô‹Óˆ’S–qe‡R–±;e‘ØR?ûTêéB™(Ç8¬ûÒ–zp(#MMiÌ ÙŠz!3 cÛR†q*eFZêÆ!ö<»I&á9$& ùÙ‡Rö%ñ³W‹üìÕÔ™pÒ4ÔÙ›ztÚ5æýíS©G§J’b²‡ L(9&}e¤1/d½TRFšå^o{OêmoG½íS)¿˜ø („ð‹ ”_,棉ùÅÄÙ H(ÊÔŽ÷¿T‹Øh:êìM=ï’õë´éÌûÚ§¢¤”[,æ¢åwå¹hÄÿ˜œ ",zRCȫŬ“ú+î ×Új4þIM%5Tì[Lj&«¬ j ¸î‘šGê©uâúö¯ú…Â5i®UAÏàCk «+~´žX°3þT+ĽÁî?Ôq]`´U $ôÿcC=yU”KäNÙ·Ä{—øîšJð|D»õ¡^»„H¸J¼ÙN”sHøAqÔ'—0ˆ®p )ûdž²#|˜ß-æ–re C–ðc5ø¡¼êñA½ÏxðAò\oÂß!àÞÔ‹–¼àt”«C˜!Ôs–xx–™ŒzÊZQNŽŽøvS.«ŠzÊ?YŒK/ÊÕ6Páõ¹S–㮪P&” †æŸ¾àŸ¾Àê?£/Òïa´âÙ jÊN ŒìT”[‰Ò£d”§šW¬€§jS‚ç•é¨Gwe•1f‚-åcÞÁ¥GÉ0!T(#ʉrT­DllºñAPRL%Ê„r¡ Õ|ÊPUS†ª'e%ØQn=JŠ ‚Ê£üT eÜxŠ7>(#e“…Ñ„SP¶ åa3Fc§ÚQvªž²S ›Lÿvj¨=Ϲ!LlÂI`Lì0ê×í"âÜßî0‘o7a>æS>cbû t( õîÖ£¤ø•(J†ÉŠÊC¹`Òk(›Œñ° REÙdrʼ!ÅÀ‹zwK¨w·žrS ÛHù„›j¤ÜT1—LÌM%,l95*%ÇB&b’¥¢¤”™j¤,I»±¡>Þq([êã­GÉ(/UÌ#³¢<2ÂÀ&ŒIÂÀf{ÿÔâjñÿ‹µø¿S‡eôgKùc©()sˆ5ϵ&ü5åFzbpljØczÊS¡òJðÌHˆ“`@9að‡ L”gÍx‘ ʳ& á‰JEI11T(#JNY‘bf-&‹W!Þ˜%ÅäQ¡Œ(9åXçQ^Meù Œ(&–eDÉ)«†pƼ)A‚ɦDP2L:e$0Ö˜ñ¬15*¿W×ËžgDÚHðs âP¶˜¨Þõx†µ]}ža­£ŒH”%ÃV¢R)?A‰26äÖŒ)§ ë<ʰ£ŒHOLô8ʈô¡ì19&}(*å.â'x¡REœåÔ„RN‚pH_ÛŠgiP¶”£ §Æs¢¡FÙPFM*eCzS6a)„P>ᎅ¡l±¨xâV)Ÿ†p!ó;ð|Û‹NŠürCÅ'îŸ~øŸlõÿv þïöÃNôs–˜ e‰YQ¤%ÅÀV¡ ('ÊïÍ/VÀïµ¥±T” ƒ>”¾;å?ÚQþ#áÕH1”(Ê "eBÉ)·×FÄ~$Ì%ʈ’a²¨Py(eö’ÄQPvI /”Žòj|P” *•_…çõÆQV—ˆU£D™(3LC™aî”knK¹æ:”ˆÕ+¡¬^eõf˜á¬^µ=ϯ‘Kð»J ¸š´Šú<Ã&åéÀ³Ïí0‰½(÷‘$3á†1º•Š’brû  (&¹ eD9a²«Qù(&}e†)E ÈÊ s¡›<ãE‡’bQðA(§—0 M(9åôšZñœ^1/LÌé% H, a¨|” ˆ¦GÉ(£×Dù„kcK˜(Ê 7Ê€r¢|^1'̆rÂû‘0×Iî’?º¤®Áâúû£î®·âZKêìUcÿU}eëq¤ž’:Zx­É‘ºø¯öñXÍûïÔ»Âun÷¯ÖìÔVÿzOïO5INÿI8Z2¬A¡Öõx–7á Qª†<¯›0Õ”¨ /x/”޲•”_储¦Ah‹AèÒ£œ(ð¸Ýe<וð©Ç5 e‹ÁéM8®”GŠAšOÎ+a ÚQž6ai«iCà)âNÞT(Šü"ÕX÷OŸôOŸdõŸÑ'¹ÐÏ‘Gž³eCù~” ;eDÉ)oÕJÄ[µÃ`÷¦¬l' z5Êß“rý$”ëg@É0T(#JŽ ŠÊC¹PΪ­ˆí'Å$Q¡L('L–T>Ê2Vm(cUC«Þ”-ÅDR¢Œ('L(5M*wÊô³£L¿T”“L…ÊC¹c²Å¡l1á<)ÛŽ²±SQ[UJÙªFÊV AÿÀV CåÛð±ç/eCÖ Qš¼^<[‚IìMy6ä(=Ê ƒYmÍs¯Ý1¨ÃP6”±š*b¬QNè¡4ؽPz”ƒ>eB¹`ð«Qù¥xÞuÊÁ“ò®m)_Õ€rÂÄE塘 š$î(JBùªzÊWõ¡|U'LžTÊ…r®I"y¡RQRÒ;’ýdLªTJɇ’`‚)Q””œ;CQrL¸ÒOЏªy”«F¹ªî˜ˆa(›º<_U‡’`RzÛó|k[ ~OT*Ê“T‰Ò£$õy¾u*J†I«D™PN˜¼*”%Å$A™ð­óQ.”oM›ð­5(²¯JEI0ÑU(J †²!çßP”&¿Ê€’“}oT>Ê…²­óÉÙP, q([Ê^£ìU”åTˆ½ªAÙ‘¥§\k%Ê„r"ûä´(DLSÊ´–‰˜ÖrÊQµ¡ÕT”Œœ§CPNXxÔ´øx¢RÿCjð?½ðÿììN¯“Oþ5rʃÙeD9aP‡¢L( n ÊÜ ‡’` ûPε> e‹Aï…Ò£¤ü*”å„I‚2¡\0Ô¨|”“Bƒ²ÃÄðAP2LTJމJ“Ň²%ûæ([LÊ·–a©P&”) eƒÉä‰JEI0©|Pz” “+•O~+…I¦CÙa¢yQ¶µ„²­õ()&žJ’aªP&”&b(Ê„rÁ„ÔФô$g‚hr2Þµ &©;JGΓ5BTʆœ%và¹×RL^”e‹IìÕç^KÉc”å„I­B™PrLîPTÊ“\ƒ²!¿‹B¥¢¤”{mB¹`â«QyÍxîuÊ‹€¥'\h,*”©%ψV£òQ ,a¨|²‡ƒ…"%ÁbáƒÒ£¤X4T¨<”;8” w”%ÅBâƒ2¢äXPBPùä·RXXâP,.Þ(=JŠEF‰2¡\°Ø„¡lD,k;,<>(J<›Ä5ùûw®Áÿ>øß¥þSíýw¨»ÿÖ\q½%÷L‡’`À*Q”7Œ¯'Jƒ²Å öAéQ2r¦eBÉ1¨Õ(ò”%ÃEå¡èa4ØÝQ”-½J‡²Ãà÷AQrL5*åNÖGQ6˜ž¨T”C‰2 ¤˜ J”%ÇD Eå“3“˜0” 9;‰Ò“sK˜9ËŒI¤CIÉz)ʈ’aB…¢L(L¬PTJŽ ŠÊ'¿…ÂDÓÐdóDiP¶˜t>¨T”“Ï•в“àwDéQLFÊ€’bR*Qz”Y{Eå¡ä˜¤” 5•GöghÂ*P” ÙGÅ¡l1}Pz”9•GÖ0¡5([Lj/TJ‚É­DQ.˜ä¡4Ñ( Mx/”ެÙbâ{£t()%ÊDÖp±„¢òP ,q( %JB΢òPr,a(²¶€Ò¡¤XW•(Ê G(*å‚uUƒ²ÅBâÒ£œ° ¨PF” qõBéï•ÙïXÉ'öww#°’øåÊ/ï›é>ô€À垟u¬ö–” Á_n׬¸œi±×çrä9Çù¤«>µÌã}†C¹Ù3´ãµs¥‰ÑÏjNì%Çë\“½-q{Ô0ÛçßÌÍýÇk¶^‡-A—î<Ì V¦Ôl1OËñ<áŸæ{Àü™ïã-1n0¹Ž'^çÆó¹v×Ç€ßs«V»—¹#¶l_‡1V Ç^Tô‡åo‹ý85_Ë¥Õ\=ȦNƒùhx ìZ{öí‚Oã‰y/J¼N@Çn»k;€/ߟ­ü:&gí¹ñiôuH[½í¸aê“Ô¯ÓäEp|õqk ù^¾óÕû`©qü¥ªÖk‹ÏåÒêæ‹~dt6ž›Ðû:d9û¸¬ûÖ¢ìþé‹÷‹›èózjÔÁç‰÷½º)ø¿±ç"öwÑáøešÀñ//ÕpjôœÝÇm3aŽ1¹w…Ö×áÒš®›;LîëZÌ)Ÿ=]Ë= >4¯²¿à[ÏûRÕ£~ɼ¯«Çs;³tÐÒ‡jhÞ­·Üß!úô\×zQë°h…¬Óôí½`ï^r#µϹÿ;Š÷ëù,ø­0Ÿ=1¿Èju®|÷¯×}‚RÕpåzñÛe›/1uk”¸Š︺f_(vyò½ ¹– ´;µdøTÁ·‡ùG1_±ÇMœ5ëÑ’ãjði²ðŠSïL°«ÓîZéW× ×í°É¶Éƒ O±'Æ´VZ.»hpº û­3ßaæÃÂó¨øëÈñ:ƒGÅLœ¢†#^ÏÆzºeÂÝç˧NO½+S="WÎtßÅ^F5×rŸ§‚kÏÁψ~FgÆÙç—'ùüw˜RM}3Áþe¸*®Áù·CÞK‡¡ Woªåˆ+ß¾›÷…ñkØçæ}],|i”8þ€Ï½š»Bªl_uN&ŒxëiŸ?ÿ¸)NFî{;f×Ú7©¯åúðd@åÁ™åãk°¸ä¿W}˸Çët_T»þì}»QVÖ}^&ÜëÛ3¿z·kð^1ç@ÀÕQ0ÞNó´i5-×euÀ¯R-ÿqÆwó¨t8Þ¹»ŸÓªÙ¾£ÊÖ=º"ÜRJÏzWêDì™°têÈÑPÓ ÎÒrG”1¿‹>¬ÌŒÕEv˜ÿ…?^gZòuçCö〇3l6dÂòý×ë¯BÌ™~SNøŽ†)in‡ÊZÎtáR‹!ñþ«/­Æ¨2íÇK-¹qkråÙG£â«}گ̞òéP&ìé<¼F½W!KÒøØm »; *ý±®–+7Ä’vûsÌ¿”ùñuÅÑ’‡ãN=³ýŠw—ý°Ø›_Ì„ÃÄÎÛó*hN-<^ñûHè1oïQ'wNl ¼xÞ_à=1¿.æ«Ç\XÞcvïW)ÇñOû¦µÛ²ÌxȬL~>pF‹«Pìeüºøyî0*só†y2-×Ê'ôËŸþ‹?ß;mç÷Í\ÔÆÂWØÇ5U#$Œýð½\år®y™p³nÒÅüŸWàKô›˜võÝÁ߆¦µœ~gÌñs} âù2ßqž×<Êhõ7§ñþkJÿç·ÓžÇ[¨¸ÕÅÍ‚J–týxã Ä\y´ePw Ô“¢ÎZ®¦jr›ÀÃB^2óíå}ð:ÂÅcܺÝMù¸Æñ­:w˜uišƒæþjT% ¼ºv¶÷ ¼üqÿªë޴ÇuŒ}Ë ¾?||?s>>ÒMQÒë‘óá6²ÑÚC›éû9/jÌÇ9Žÿ- qj®Q -.é:m°Ï‚fÝÆN¿y§2¦mY2 ®Çz¹ã¦åúK.š5'c¾÷Ì7ŽqøzÕ Xý5Ç7Ž—û· 8þ%‰‘[¸<<Çó9‡¦Ã´Üôª„hUÀ_eõ‹q½Øýáó ¡WP‚ש")!uj.6·î ,ˆ;ÝÑåøTxüteàAù8Õ\s¹g?->îårŸ‚çËûF¥ÒûÞXßaŽwžµÞÛ÷$MQ–‰žœ·%Îë´*^¯,Nã ÌõŒqø>šN°S/ Þ{¬¾°:À|ßy—ž8>Ï©: –>X_Ü? :ÔÔyHº¥B© ¯ò—ì ¥<~ÔUË¥n­Ð-Óôw(ó£bþ›î¿–{h†5¶ä#âu¸rìaX]¥‹ÿèãYàûµçúüb©Qù}+mÓ1À× -w÷ðÁ¨/‹_CæGÅxnï{?ßR>îqÜc »".†MÒ˜“?.gAY·=Ó›_¿ ö%猼5ÊžVŽþؾ½–ûþÁúõ¡¡Œ‹þVà¾1Þ½9Îq¼·ÑãŸûxnìÉÃ,øýñUÚ—áûe#Ž„aÜ¿ÀúrmÀ´ŸÍ‹¨¿Wöžàý-û £?㢆6Ç®M˜ð1 ž•0M|2å2¼ü]æÜüê#`µk̼–8n»íšö—©„û<ò¹ÍßFoœ[.Pžï³ÉHŸ«ƒ…ï½U@®¼ØózC‹#HfgÈ‘JÚ.COí!ÞS‡Ãþõ†µÑrÑ_Ÿî3Gõ/âäªPg˜$Ÿøû#Áë*é\ÂqИTÿHN—òÙ ïôy³íe¨4ês•a L ´–#Ôª=TBÃø¬?eñ˜¤pèJŠÔ"ŸäxU­<¢›&VùW͆Eefdž¾¼/¬Ú==Í–Ý;3ªq -·uêØòçß©IÆ«zTŽƒ:ZÔO·ú»Ëov > «tE‹ÚØgƒÃ»ÛI—`df‡Ìß\!Ç“µÜc‚ª³V¨“Ìg|oë¥U? õŒÅ#ÿž¥ñŽ×ùäm>íþQù]_üsól*ÑjM{/÷ºÈÚ3B†Â•Ô‘†tì –øÜò}f³–+Ü×1¤9Îq¼3+¿ßÚe ž/9`¦s6ä¯/ýµá¢K‚ßîù}ÎÖZNÿó¬WªJà±ûü×ñ÷i'XúêHÜ\«Ø:dê1P»6¼úÀ5¾¦ßÙ8§ß%[~±v3×°¼Ù “ß÷hîK¯î7¨8ö¾ããå•3ã Xpoq\m™Ù÷nƒ1ºkekMΆ‰Q¼Õ¸ ägº,K ̉Íí͉ԗ¼¦øh|?PË’_ˆó®g%zÔq ƒ×:žÁÏÙãò´'ORàÁ¸Ðc7y@؆áýn}Šæ¶íêª/]v­À¿dï!¾ï¿.Ä7Ë{ Ž"^§Lï† ­w…AíÓͯΠɆwÃ+¯<ž õk:ý;ªÝéxÃúM4§»ñ8%Ñv­PÇYb¾…þ½8nÃmMžKLa;§”ŽdÃÁ‹ÅMOÐrÙÜl<ÁºEêög¹Ñ\ùû¢Zæ¨ßEÆ/`ÜJÆÑû {âøË*ýãpbK›*‹/dÃ…¾‰ l›¦@gÿ"yŸÞÏŽÝK|xÍi›Œð٬⚿ï¹?P<ßUâ¸ÀƒÖ`5qßÖgÇõ­›…¼J¦}õXØòø VØh®MRï‘ÜNÇü¢™¶?Ç›?Y1Ë»~8ŸÚMúîI6¸}zl_12Vëƒ÷/fÍ\tãí׊KþEë­åØ<öOÏK‡ãUj9øEk8¬Ðz^µ/Ù óVya2„†WX¶Ñ¢VÎö-š“¤ÍWòæZŽÅË/ñs2âx•ÍF¸á`oÙàA?Ý·º “ú_;´hŒ¬šð!šKÙ<ïkGç ¡dïCVߘO4ó´àèråONíd]•½Vþ´5À¥nù%ª–I†8mé/çÔ#…8«ÕlÏáoƒè<$ř͋ÅÜ ŽWé°ëk¯Úá@ÜpwÔ6@œ‡'³“ p0q ‹t SÖåEs]ÏU¬VunPÿ™)ßwG ïæ-æmÉñ:5Ùmù56cÏÀ>h6ÈVoøWVuØÒ°É82°oÖ#S4gž­äÄ›wXr”xNƒÇ ØÒvÕ€'`㣨ý)®˜ä,Á)F"¼ôŽ3ß±Ðô÷¨±gDs+;ö_°}­ol=†ÕwæOËòÚÏ8þ‰®¯ý­OŸ€¼AÎ]OO6™ÍµóI„‹KõZ¸f ”¿ÿºÂÖ»ÑÜüýµ‹«üÖr–ü3ãã°ÿ\Ìù³Z›+Ï«®mx¢þI¸³ ˜·ÖÇ)Šß êvï_Îa¾Ô_;¶wÔ=¬;±äEUðùÙº[÷c|ö=,8x•óH‡}F®]â–d€í3j…Ü,·¿ÙX:Â"¾hª—6Ds·ðé7r üÔÙ{šÅ³ØÇWŽãò¼Ý“`žVì0€ÙþönlÏÉ*>Ü ådõoGs‡®’Æ Bu9FˆÖïŠ}÷=qü„öd†}ÎÖ>%ÿtØý:ͱù®I€•k4{Þj¼iÒ±ñ}4§ŸH:™`a¯sùBÝg¾çüü„úîâøú>5æñ: Ó†.Ü83ëÈ÷:%ÀÓz{¯Õv…/K¿^^–Íu=àÀÇšÁ›±>‰­Çˆûu5Ž»Mº¦Ì’“ )ÙðzŒô£5¿49¬¶ÖlwîÁ¨¼wrµx¿‹Ïq9dÝ8˜c\¶Â~»íí0®}ÿ'Î|m ̯Ùÿx+ìËW:x>¾¸ëwäœfxt”DvKG{§:g¹Âå »‡µÍŠæžõÞà,ÔAö\ùõG ‘ÇÝm½so¯w'!ðØˆ«­“ pN1lÓäÚ8îž¾ò÷6C¡ÔÜg¯÷âsýö%²Û¸¥ÁBŸÊÖ!YŸðÂuôð/cZÁ$>âr‘Ÿ¯ZåÊyühd`jæ‹éŽ_tÒglw 3Ž9 û‘UÍOž,Ì+_ç±7µèO%8nÖωõ:‹„´Ž2×h€Í‘;—koè@?9åZ‹ùáyñÃ?+h¢¹‰K+U }‹w~}ʲ“ã¸g«5—ß f[çO˜2Ó4e•Z÷T~ Ê:¸C³•CÝ«£¹j©/s¿ ÷ƒ_O{ëÌú[qþxâ¸êõ£;¹ ŠÃ?:Ï;~çv˜­ƒ†,˜Ý`8ÚtËq_4×Ç}XòËÁBŸÇøÂbî˜Ç“Wí0»œžûUJêßýè ƒÔWn·ú¾ó€ñ÷}O:Í•]ð¦ŒÉ?øoóÑÂóƽûj«Éóà ”s§`±zÐ’9P¡Ã{ûÚ?9êÇ?NW ùºhŽP™–ÅÿëÁê#[÷aëTâ¾U‡×_£Ö¹{ |^}Îå@³¨7ás œþz£ìãàë Ö—Inï¼ â„½—ù¼ù*ÔìœUSl[['7Ç;^§²§yæ -¹ÏžÖ.o޽|¥<“ÞYï6xÿúh®×‡mò~¯ ž/ã­°ù;ÿží?¿îÓîUTfì§v|Üãø£¯MrÚwKeÊ%Gï쑳‡LÁ–î"ܪø­Å‘#¡\»Ò}§|ŽæÎU~ÍÝÈòõ…Ðgñ\”nÀûn·åãÇí±yd|Ò (ÍÝ8þq L‹‡²-Êöéï9£Ûá%0žJvV–ZDZýôû Û?Ýß~/%ÊßOïK£`êÙ™ nÎÇ›%Ïô‰‡µ=L¾ï '¯SÎu±µÚ¦¸?zÝß|ÂY|²¸q«8à› ˜_¸9ð:ú=Ÿ›\žmŽßÍ‘S~õ?` >ÄÕ¯vƒ õg”;ò%šsRq4ô^'ôsì½Äî7ó·˜Oâø^Ù›œŠÇ™%â$9ÐùlÞ eòè?­ÏùZÃG@ÑX».~Ds„¾Ð¾è:!ÏXü×+ŸZeÂÓ\!nXï{èð:fŒl¥hPû± Ë”èÓ¨ó¦ó/Àà–/lnu ýŸ½±9V\Ë=Jü`á{ðÿL¾›gŠ×=ŒÁŒç ó]¦˜4/ö¬*¾ôÃ÷óоDþ•“=À­âìÖáeµÜ“£%vÝ:[PçÙz ãWð}.¬Öa_Z«‰Gƒ7Ñ \E'–÷®ßÖoýy8j|è&3z@\‘ýö½m´Üãù «ö|îÂ÷…Ï£º÷_‚㟴&7X ÷”´–äj½íÏÿðf“x÷èözÎ ø9|Y×Â÷]Žã’ïOÝrX q[?¦<ÎaÆRzEŸƒ˜øÆ’å<„yË–sãÛ ó?—ŒßÆúÉÚ z~ó«ÆßgO¿½Ç’+¿ŸjɦÑð ›0î¯ämèîrÚ›A#A_†Ð£9ó6Ðï ŽùÕk{¥ÍþÔÊÞbŸJ‰ãEq¯÷uid6Üok,l”:qü“³€/©¬G@.dG(šÛ¿Éf½ç“ ¿ñÙóc÷™½÷,ö{ð:µc?ÅÄ@Îu+ÛîÉž+ûìk·ö,,q,òbÙ_ÃÀÏ>Só°²–ëØ§NÁÂ~-ß¼ö“XŸÁÞ'æøÆñÉî×¶¼ЖxwbÞ0c';…‰ûòf Ÿå 7ï°CÖrê ZgB÷Ä ãΰÏOv–©ÛÑ8äë€Çÿ0¦ß˜Öb¡TbUEرø|r”*ùc4{åäð.AÕ ²^Q_+ä)‹oÆU`ùc^ÞÎn ]÷m·»êÀÇùú\¹6Ô°|t­Óà½3}r«¨¸±àÔ–ïÑqú`Gkà ˜=¨aQyU-×ÿU±¶ÛÔVÇØç矋 z›7߇Ipü’eOûzT¾‘+ŠÄç€B~%=lQÄÇZKÝ3jߨ´m\ -Wât?ÓºB}Øu¡¿|õp@{+GàÿI÷ƒpüÚ»¢Ó+¼: aAÇ?U¾™co-Ÿß K|(•³¤Öî`›»~Ôx¬Çïûm~ñÝyð^bûX¬obû{â¼òÄñõÝ­{ïèpÕèXfÙ8Ý­C¯M¿Ï@‘ßïž$õ {ÊnK°~5ûÜÌæÙŠ¿×{¾y/ÄãsŠùJ¼ŽÌ×cñÇag`áS—©žOsàCpñŒ?ïkU}Ûÿµöuëñ»Xu-׫"!Ž®ãž½ÿl]˜­ã˜ãÇ þÝù`÷…g ¹§õ™·Ïs`ÄFõ^ŸùgàINÐÁ“k†¡~ö¯«åÒUйNØïäã¥e¼ãx5ïwÙÄ8ñ¦QþÑ—9à°rúøó’3”c9î<ñ_àƒu±z¯ Ë˺­ò‰ñMÌqãœv¹³Þùù0ªã¿û˜r`mô¬\§¡Ï’nûŽ€bOû‡óð½Ü©Þ}ÂçÆâŽ­“Š×]­6äÊmÖô©Õ'Üâž94ÁÏ÷øGàÎú“OÃûgbjw8Ý¿VùþEµÜýCëÌk³îoçع“ë |zÔlg±'Áñ_¬“¾)w1œ.Hê·{“ÚGßJ9 Žk2Ýï¸C…w5µëÄ3›¯'¶ª›ïÏg:×ÙØ„_¯4 Ó:gÁf!!œåÀfÃáMþûbA¹skãØ #àå*Ò¹k¹Åó’^P·Ù:›Ï±}Ójù3ëtžOØŒc¼N`r×íÎ ÎÂńꆿ3›U7Εǂ¹±sø§µN0Wáb°ëÞiu`ý­I¿»”sÆß2Ç+ŽWr[܆¹)gá\àšw?ËÝ×Àkƹ1²öý” aÂ~]Ý”Ž¯LqAB_Ë΋ïe˜õ„J-ž¶Æ;2Ç-Ž¿ô¯GáÇY8ÐßéSµ wà­ábûÛ{cÀåÔøG«\áµWíW‹Ç|^Ë8ûîí» jÚ°ó æøÅqœPës°KósÒ:÷¬[Ö¦•n1°ÕfߺÎïpøifÇ“ î7ÿ¹Ûý0Ï×É¢<–|ç²/jNñêÒʲnãur×½ î?ü|RÜ b{Üô#˸Wñ=Ý:‘7>Ÿ½ôs²–«xTºãRËuº ‹Cçlß‹Ÿ¶–—æxߘ+7g»ò]'»’.«V½»«¥\ß°&nÞ—›^ا”X¾Ò®Þ:a_Å%_7Úïüý—à¸US_7[zìTÊOºµ ïÓr—üÕZ¨9½Êæë¯BZ˜îJ{o-—ýcý¢{yóö~æ÷38¡î‰û[9ŽÏ¯;ƒðÞ§JH1~òkÐŒÚUÜô¹åáAÐïþy-wäKý;Ž ç&XÿÉs_õooÓ‡m*h™6?ÄϦûúx)÷2ä;ž‡e«[8ZúhB/+Š5ÖÂȉ5wœ7„S=Ú5¨åJ˜6XØGâç_-@ÚDcu´,¿^¡Äñæ^½·ÜáÜy¦{ÍkUâ 驜õ6véõéÒá~p¾aGÏ[´ÜÃcázy³`޽YÿÀ×1žû¬Æñ*ìis0¦Ã(í;úqjÑ;`¾ÍñÑ`_£D༟½¡|ŸÆûësŒËÏ››Xð0u8Žà|ÜV (rŠÖª7°Ä¦hØv¾é‰žm{Á·ƒ]Ǻà÷ä×1ƒ…u8olžÆøHl—ñÍqMžÛ2Ó¼É/¶ø‘úâ»XM‹†…}bw—éáÙÍ­'®ÐrÆ;k7‡¸ ï–ßâýV«\ù½M;Ô)”Slí ïs`…qÁ½N}£áfR©Çv†þÝvöÆûùqDêèà úwü:Сþ±÷‚˜ã)Áëœïù9Kç_‹¸ÌzŒï×W½\œT­¢ÁüqÙ~½!¿Z®L·òýB§ ý«x]LŽãôÛ¾lÒüx¸W¾\ðì˜:|Ûå§u£ácÿò/‹tm ©×v¹µ\ø„¦?×hƒ9ÆçdõAüôÄñrgï{²;žçÃ_ÉzO/üÞX9¢—íøz¿F}Øxtiëß&-7%g‹îyíuœÍ¡CSFvû)p¦Øú8®”8nuߨ‹¾Wâ¡Ýò2¹—ÎçÀ³¤ƒ]Ùha?÷D·Ð»ñ÷´\èƒ_£ÛNZÇÏ}ÞU·-°sSlFÝpå²õ•?;³õ/>.h×1/–¾ó¾Uxyæö-Ö›^‹GCÿ *T|ƒŸgaÑâ®h¹+æïç±NX?eõ‚ñÊØz ;—c±/×iqufHßÞ¡ê|Û1áø^~ï{”ð^*o>P¤å"[+–Ü`P/Xü±>ñè,¸\8¾ÝÒdÓYÿ‹Ð“”ƒC9`õÈtË!7 ãtAù[ÃçO±j9[ùôÊ¿ u”åO錒.îþ(¬÷óó§ú}žÕ¦\yö÷úš¿¸ýn}ª4qgøÏ91}KF,yW4wK‡ÎðÕZÛ}Ó[-çqóH\øk?—…yãõ‰¹€·Wó¶n<·ÞqyÎÏ®4v­ULì=–ç6À£;/uðmѮdzq׳ Ö™Y½cŸ›Ÿ‡4´Ø÷—ãø‹'W/y–ƒ¤Ìj«›ifGÁ»c—òVô†gqC;¹ÿÆyÂÍzŸè`Žõ'[óš)›Pp¾SËúå‰ã6okê ó£†NŸ}q>ÜÅkÑ}(høüׯ¸™}¡žÇöS×ò´Üà1ßšÝZ]0>›'0ަ¸_Uâ¸;ŠLm2rŠÌ:1rêò(hÕys£(xR%±Âü·ýAÖgãûŸ´Ü¾cÚ|›,pïY¿ÉâRÝ¡êqÙi]Wåßjø·}8CÕAl`¼B·(ØzSÏm MlĈ4ÍngÜpÛ¢c‚…øgŸŸõCl={Ègǽnµ¶àœêð:á‰R¦–ŸT·çç€ç`ÿµŠÓøKÊ×êÛõc*MíÃñû‰ÁÂùhž;úMXïç×;Z[5Ç?Žßøkç*Å¿ë`£¿¼ÅÜzbØç~è¶+Ê«Sç¾pņզÆpAäØÑþ á>±üeï96¿¯YmÆ÷Á›;&@£âOË_¿œ™1T›Ží^ºe[/(Ó}‰}Ÿù1\Ü‹oé½ Ü;¶¾ÍÖ}ؾ‘9îq\¯­-~öwM€5gJÖ“ŒËóÙ‡Û_wÔ€[Àõ¼ï{€ÏÛ®£çÅp¹ö=[?5ìß<ÕŠ´?™'Ü6ïïûÉqü]p4vÚÌÈ >°ÇkDì fça£ÁKÊöòîIiºÓc8þ¼IAü°þŠÍWÅûÌž8®jÿïº%€­0šv/_µ)›| Ú¯Žl³à`hh ªI1ÜŒUS—} (xÿ³}T~=¥žÅ:¿Ç=[c¶îE`Dž¹1LÕ'Ê- ¶mæ)wÞ®J8¿ûX§§Sb¸¤¸Z=gô*èwØ<›õ—âõõföžJ€#¶© 7tÉn%º,-WæôlðÍjKé¾Â}X”\¯–Kõ‚üäëLžPYhq^ÇŸi³áZÜñØU*`~¹69PYÿ²îÊC‘³YJ)ë~Ð'¤_Ô¡¹1œUÑnʲØï^·fï ñù#Ž;ÃóD±à¤ »z•äÀVùÖ%Ÿ;GB%û “z%¹ÀqChvÅÀŽõYl]Ÿ­³ñÏ“ò£·äÊ?tïÔeÍû0O «åÀúosªTxŸ¿­¾§Ò v_+Ðéd qÚ~|$ÔÙ³žçF9ú-Ôò²uªoñü$8~ñÙdfš;3;c‹‘~KùmŽ€AÑK›Õo)‡©ìn¶»Ã9tmwRlÇÎ0¾»øü†Ç«yhá°vÝ¡Dï±YÉÖ9PêcRDãq°ùsИMƒÚÀ|ßko¤Æpk&\úü.H˜‡>ïÏú+öÜØoŽg¼ÎÕû/ò½æ&BÍj‡"c¿ ÚôbGZϸ®ùRûgF0¾ø•éöºe0gy.?EØgþÓ>¶Çï2}€êìáDˆLwéÿìX½vWºR‘ëúqžaÅå®nð,×Ûîó¶ÎÃÞŸ,¾Åë–;˜—ÓÉÉÀÖŸÒúùØyÁò§‰¡m,ÞïV[sååï½}æœ^‹]ZÉ@]vÿÅSw#`FÊ›‡_¸6æ…–XîQúž ©ÅÖql^Äs kÀn‡ŸUÕß;³}þùµ·<Ž×iæÒ¦ÚÀ©IPyòœ&/‚ Ð{ÊÚ&¯FÀ•_ÛŒW˸OGÎ@õr±\ê™Åå¾§¼/-ûÆWBÝfûnq×1oklO[÷Ž1©‹ Õ­êcµH˜`=küõö\'ó×XÎü³¶š&œ[²ØçÄñb:uì1÷rpŽ~¿#&`Ëà¶1GëF«k•Í?ø‰åŽN¼ÙæKÂøfçÃzF$ïxz÷¹Ð_‰÷”8þê¯ÃÏ*¾%Á¸]õu/Ç Ð1jù±Ê‘0g‚Ë F³ÂÇEkÒýªÇrÎÚòÍ úOö{7åÍ 2`ëáæøÆñeŸ?­_5.~÷1÷0À®@ë™_¾DÀ³þEwMÌÁ¬…JÅr§ï$•zsŒ'ÎÇI;`¿k1Ç7Ž·ãwÈÊ’!õVïÈÝ®8Fñù[øŸl3³YØTîpð@ÇXÎ<]\^p?ÞNùýÝß)˜o˜ãÇ}W‡ü€'Ê|VÜÐÇå›õI•ï‹€Ò̻Ҹ|‹¸ê‹C,Lp¿ì><ö}¶KÔ‰­,ÎXmË•›Eú$C„ÿH¯mñþš¿]ºèp­3_LªË<¨½^¿JÁzë7ØùvŸùºÝÌb>'Áë´wX}¯Wh2Ä~[•¹ÉÁƒR²MÏ›EÀ£ ¥‹kÚw÷…ne^·Šå`è)b— Þ,>,ë`#‹ü‘ãø;÷Ü©qôt2d «æ?¦ŠrJ¼•ïýpL’i;½#”XÅ…6ËÅ-òµËª á|¢åyê gþëgvŸÄû&žä{D®Êžz3¾+ûûe d;íUâI(}³éŠÖ¶í ÌU[r”o,×wrPG›NAB=dóñ~—Çû¢wvË»› ?û’_ÈeC™#Ͼøî: öÖ¬u½lvØë»iG,7æqjÑM½‚8K®rªP¿øsM-~¢Æñ;§æ&ÎO~ß.öä ­¾mÉIhÞÊ?¶¶€ç‘ Ÿ®ÞËíjXdúôœ‚þ•›Îl)Þ_Ðá¸[«&e±Mãê<Š1fÃÃ+Ö¾MÆž„«EûÄ­r„ǽNþÜËñüé‚zÅÎÅó¿idñ~0â¸æm®ê)°ôr“N›¯eC§;k¼îrÜJ¿¡›Õ2â:pãÆ óöüØódë§ŒçÎòÓb¾¹=W~Ì9äÂ|i œOßÛxŸ6&oM¬î[ö¤/Ú-Zû*"–‹YPü®ªqÁ¼Í;YÜóë©–ç‡%8þI·F¹B dÏRüˆØ›M÷—NÀêþÕFw‡qiv‡Âc¹éÆ\¸Zð\ù>ö½·lßÈâ÷l8þ£׿î—A'Ûm½œ ­çzÝ]z¾tÛ^¼òªžð¦¯çüòø\}Ú½þš$Ü'þýó\8ÄâR|nÐÇ'³NÛQ) ÙY~ï¾ÅÙ1öe=ÎázÿÝïúš^УXíîÛvÅr &Inµx$üžˆÝ÷Wݺ5u¶1=Kç8îÈÀC',Nç>äÍ” M|JŸu½‰íÒ"ZGö€î£;.›ËñëHA\ô˜Þnž/ÙùYV_øy_ÇÔ8îÜÚ>±÷v¦€>oJ¹#²a̹-;k«ÂáÁ'ŸáNÚñ'1Ärwï6¿Rä|A}áçiÏí[-}À±•Å> Ç]wFïgS`OÂÖSúgâïÝÆV„ƒ»ï §E»ÚCç¹Çúšb¹cEGޏÄYî?X ÷›ïËŸ9¼ËE§n ìw9æ¸ÇëÜ)¡ ÉL}ænèÔ)ò®Ÿk·¿I8ømp‹=|¨|¬Õ¨æa#öE§\ˆø$Ôa7ü{þ®P'ùß)u ÿ¹#÷;råüþO ¨.<{u¹Q6”øK~jåp(ºíý»6-jÃenPG±Â¹$VØ÷açVÙû…O°82Ç?^dzSÄ]õ—90»ò˜êÙÞî­´I™phµð¯ ƒ6ZC½¨û]å_c9þ÷cÁ_ÏLÂçgó!‹uFw¾l·UÒ‡±ÝÔLk“M×WÃß×ýq1änËý'íNs=ì›MìÑ!˜cûÉìü!»_æ8Çñl·GØý3&Lï_çéç,h?m›òáàò4lók®ãù¤MNs2s‚ãüÎÙïCØþ9¾q¼ÆƒÞ.––¾c›/žâ÷( ¦­ïÖx ŽÇçÛÇ‹»JjROÙŸæjØOiywPÁýeùÈÞ?쳸ßWãø¥È2~ÅKð$ì^Ïæ7²Àö€•¼~ÿ›oªŒ_Ò»"´þmÚݶÜiáü!û¾‰ÉKU(“çœùW÷œ##žÒþ¿”ÅyRŽ_äþŽ6w«^‚¶f_}! ÎZµí·ã×qˆ>wçZC#:jU«_±\ŠûÑqõ¬ðým¶3ë«YßÉâÔç8~wó«.AªöÚç“øù••'_}|Ü«½ïþЯL{]zw‘ÓܸeSú*w¬w°|e}.«¬_´8Gš+Ó̾йõ%X¾7cÇ‘½Y3·ÜŠ„¸ã°¤¤OZâävplÓö«w+žæ2»ôyz~kA>±õmÖÇó×µ…ÿϪ<\ÿßóOù¿é[eEï‘Þª€I¸O.ДMQž'-æAE<ȼâ*6ÔGEO¹OŒé"òQq¡ÞÄGÅ‹zÚRß*ˆ9¢§Ì9/êá*y¸ºˆ<\=©×=ó®2Pï*e?É(SÚŠzW¥2Nõ®r¢~*V”'Gy^”i‡I©ñ Õ4A‰w ñ®–м oÄ›z©H¨—Šz†Pæ“'e>1{=e>yQæ“„zÜë)KÚ‡²¤]( Ò–²¤Å,ÈÂ÷žÔãÞŽ2ŸR©Ç=aAê) RI=î]¨Ç½˜%mK=î KZŠ…C…2P–4óUa,iÊ’ÖQÏl%õ”ßlT>áBRï@â«âCYÒÌW…y2/mâøO þ§+­þ½k° ½„ÉËØ{$˜¨8”-eòŠÙ{&{/¿8ÏäÕ l©—•žzY©¨—• õ²²Å$ðBÅQ/+oÊä•‹Ø{ TMõ²²ÅDñ¦^Vv”ûGý[½©—•”úhë©6óTˆü½¨¶TÄ}r¢þ„7âD¹¼„7âMýÅÜ'âi¥FÙP&¯Ž²÷¼){O‚I©±÷ˆ‡6a>?+âß*ùY1ÿVå „P¦ñ³ ¥IìE9#Ì?Û@9#Þ”3"¥þÙÊãUR¯‚r÷ì(WÌÝ+ìŸíEý³%”3¢§þÙ*Êãu¢<Þ<ÊÑâñÚQÿlÂã•aáA)7•ß¾€ÇkKy¼©NžTÄÛÊ‹K-0îÔÛÊ ’òxåXpBDÞVÌ¿•ø[‘ü'â\˜ÇKj,©¯¤¶’šÊêiá:Jj(c…ú(³ú;„Ô?VûHÝûSÍûWµŽÔ¹ÿUcuñBÄõŒÕ2R¿HíbõŠ1}Ib5ŠÔ'¥_›HM"µˆÔ!RƒXÝa5GÌöÝù/êÌ?Ôæ‰Gî¯ÚŠgù>ˆÒšgùzR–/áÂKðþú„ÇéC=õMÔK_M™„ûFøBjÂٴ幚j|pî˜Çq(;ÂѬZÀÓ&^t^”Õæ„UM¬ õ»×S¾/ã³6›BÄe#žs{žI<î ˆñÜ)Ó›ò×dÔ[NAYfßdÊ÷ lÆõˆ£þÈÄ3.”ú"[Qvñ@&¾pFêy̸À¶"Mõ×TSMõ×ôÁüPQoyâ©©éÂ{ÉËþéCþéC¬þýû;úVÌ3ÂòpGéPv”,fžå‰˜gV”‡²#¬pê©éD}ä󩧦e‡IàMâ(;LÊv1ÏÜQ”MyÞSS‡²ÃDñA驼7åOJ©¼‘2ˆˆ±ú‡ˆø“”mUžMžJù“ŒGDø“¡”ç!§`Âóð¡üI'ê%OøD.˜€a([ÊN¥Ì3Ê<“bR†ˆ˜gÄØøÈ{Kx_M'LÔ”Qä«i¤>ò¡ÔGÞXM9Þ”ãÁü‹”ãáC92ê_l¤ü_åÿºSÞ™„òż³ÂþÅÞÔ¿XJ9ê_Bù¿rÊÿͧ¸Bü_ õ/&ü_',¡(åÿ†Ò"Âø¿v”ÿ«§›!ÔSÅEƒ²ÁãI˜“( åÿº`Á E™P.Xx¨Ϧ7ðõ„ü±\Ìêϼ3RsI½euö¿ª±â¾„ÔRqeýI;þ«Yð5ÄõÔ;òÙXïAêë/þTŸ ÷¬®;«DõDÜOì u‚1ÆÄ5àD¡œ—Xðf 3B%âÌ2¦˜„²eMÔ—ÜŠòyt">‘òdÃ( ‚pÈ÷8cðßñ<[žKè‰7]‚yh@9áÍϯÊó^ »A^ƒgljñLwòr«Çsn 0œ¨Ïl>õ™%³·,å&()7°t"YÂIÐQ‚‰zÁFjj{ž‡êNY2Ä3[Ñ…çÚþgÅüóžÿÏ{ÏKéw lSÆì"œOT*JBÙ¦bfa›2f— e›êP x%ʈ’Sïlüî¨8”“À•Š’`2()ÛT!bvy¢âP¶˜ ž¨T”E‰2PïlÊ”QïlåÅ()gÚ‰r¦sPÌ™ÖbÆî šò \(ßÔŽr¦3&”2c˜€”e›ê)³KI™]2LÊP³‹0¦‰¶Ê€’c¢†¢L(&¬ e¢þÙj꟭À£œÊ)‰øÒN”×e¢¼.5åu®iåšzR^—”rMż®|ÂÁ CI(¯Ë@y]*Êë’S^—‰òºBi œ]!®)á„P®© ‡•G¹¦j”ˆk*¡\SJ†E%•O=°ãP¶X`¼P:” Måš*°à¨Qy( Šœ€õ>žÉßÿ‰üÿJýýw¨½žVÿ÷êîZÍ%ÏœôŒÏEX„á"b¸þ‰ÏEXŒçªB¹P.Œ åÂ¤Š¸0&Êr EåQ.Œ†r ›‹°\•(c!.Œ &Ee$º`r„¢òmy–4c$º£ÂhÓ¢CI1q”(#JŽ J“ȇ²#kC¨Tã•ð \0¹ÂP6˜`ž¨8Ê‘&\#J^›g¿æSþ«†&Ÿ§ˆÍ¥Díy†4cÂxb#ÚP."áÁØQ áÈ1YÕ4a=Qq(›B¬/”Ž2¹Ü)GVJ™\y”#˘\„U ÅW¡L”S q ËFöwW*å¨(K†Å@E9î”ÇE¸³^(åqy¡ô(YžA›‡rÁ¢F aÁè( F‰2 dXHBPydm—riíþ,˜úÜú\¥Õ¿wÍu¢Ÿ‘°i˃٠¥GI)›VÌã²ñ¸l)›6%Å€W¡L( |5ʃߥCI1 ”(=JŠÉ ¢lZwË ¥CÙa‚x¡ô()&Š eDÉ(#†p0qT¨<ʈQQ®¶¼2ÏÖf\D1[ÛPˆC؈a4Ñ”O+©Ás¶+FMY1q( eÓ(“KE™\N˜”j“‹p%üߢŒ(LT5*å„ ‚ÊCÉ1qÃPù(wL` Ê“X‰2 œ(‘ð»å”Ç•Gy\a”ÇE¸´¡4ѽ(KF¹´bI~/T*JJy\FÊã ¡<.ÊãÊ£<.Â¥µÁá…J-Ä¥•aÁ¥\Z,a¨üv<—6 e+âÒJ)—Öˆr¢¢¦…Å¥CÙañF¥¢dXhBi±qG…¡òÉ¿ÆÂ‡"'•Àç'ù+\ƒÙÞš¸ö²=¶?Õ[ñ~©³ª¯¤¶’š*^×"µó·nÊ­þu­,\'ÿ»ûq¤6Š÷ãþÕ^ÜÿªÎ®qJ«¿×6¶GÇjZázFêXáúEj׿ªY…ëUá:Eîá¶2†•‘œÁ¢¼V1»JF­!¨<”ë†ò 0ƒ!²Y”É*ñ©T„ÉJöó+ðAGy” ‹*%àQ¡L( 5Êk‰'JGØ×ÕyΪ‰Ô * Y¯ÃÀò¢Œk%ÊDùªäeGÎG‰S*”ÉžgZ‡¡l1½êñü>Âî#üj#Êkƒ Ö/ÒŸ‘ýv¢lTeFåS6*cF¥¢d˜û!¨ÊåliÆÙ³¥(L,5M.ÂÛÓ l0ÉÜ)Û”ñ¥óP Lº0T>™ËbòéPRÊ55¢ä˜ˆ!¨<”2 eƒIéeϳö¤üߢL(&i*%Çd Eå£\0i54q=Qq( &° eDÉÉåJ‡`Bç£\¤/2$Ÿ¸A0’0d‰þhDB꫈þhêŽv9’Ó\ÕDw´¤$«•¾„u9}ï 8Ék`>Só™ªÈf–ÌÔgêHj=p}' d‚$¹ ÈèZàHx=ó™*˜ÏÔ T(æ\>SZ tÌgˆ¢` Ï²™ÓÔ|§ifsÑij2 =pƒ`Ž8ų́Q@̬ˆè˜ÓT‰bb. DQ14‚âbý´Oø¹¾ý´O˜³æ†³9hë€ (Ìà*µd‚p·Èàà ºx o™ o~=p’ ø€ É`f ¡N Db€(‘ à*$Š™¾CÔH˜8à$Ž™%OÈ?¸¤åH&sI—}Òi Ée2$˜8€‰¦.‰S:„#ñl,ùtÀ‚‘„q hŒf 4HJ#1õÀ ‚øoA§ïò°dÕ KÚà2$¯8IÒ€Élc ­f–Ø!Ì)M<X‰n^ F›A&­ÇH|#ù À T(q hP , „£(ØXaÐD0/P¢P¨Q0¬ „£p8€ ÅC @"bi@…bb@ƒ¢br=p% Œx…ÆJßAB±Ñ¡èè€ P³_Üá:—†ÀÿÚû©æþÿWséñs`mÈj¯•°8l^ F@ÇÑ÷†þ–·8‚þ®ø€Š»•¼¸@0Ý @M÷DAP#¬,´ÀdHŠpúÎ"Ó÷‹®q‘$Hð5’Å 2A8ým¤{¥À ”tÏx‰2i­EmuË\ µ5d‚p$› È‘pZàr$žx h™ \Ï HFp9’R\ Éi¤ïO‚`$© d€p$« È‘°àô}JàH`#p@$²xšþÞÈÔZà $·xIna‰@Ž„×'P ñ À”¨­àjÔV È Ñ}Yà  #p%Š…¤ І-p9ýÍð ‰d ŠÈèï7€£¸£ÈÄ N׸DüTî1lìwªz¶Væ¿Så¿‘’±ßG¹Iöo­2Ø»OüŸ]bw‚…9]½k']œö÷Ií_Çj}³âŠ¿_‚gÇOS—Gn'¼Ŭ)~ÑÎjM–þ¾{SS÷Ú.mÓþB\»°Ð7·îµ}UHæû5åk¤ýOÕ˜çÆç[í)íN“çWGþ0Ót…ä=ýèú…öÛI`¹¥ƒóµ#Çÿj®ÛÛÖ. /“ÿlj¿sŸÞý¶½ïNÕà>ë/R%‡_K‹q{L »6½Óirhüo›>̼BªTˆ0Íxk%·5º¢ùºžôŪ.v¿Þß{?¤}oiVGÊÓDéï9æ ©å´½{m·’JÏæèW5 $¡¯ì^iŒgX¹0³×ùì~9¼ÿï›ÁûBð¾3RïóȧÿY§QËӤ–î—†^!U<¡5XI©Ëëçu)ÒŠŒÏ!Çú}Lüxs¿¤Ô/èÄxYmo[&ÍW©p«ÝRñÖ_Cu´’…Ýß(ô¿4#—§~×HÕ Ç!ºQhRhvß™œ}þÚféBVÝoËûts/LJyŠü¾z ãÚàaMê^!b{+9Ráñ‰gó“Rg½úÚ.Œ~j/zaZv¿~œx.Þ¦m+ž§ZŽ~¿«ÒÕbߪÓ$ôNËÓeË_!GâoÛšdl#“3»~õSh#Ò´cÙÅ&Ú…qOJ§Ê—ÝG÷Ñáý}¥^uƽ2`@± O“¬öéy¯-»VøNm#*ƒþĈ¹þ‹O9cš]ÏWv¿%Þ)åËá»Çgv¾e_tºÂëÍe_eiŸ±€Õéêw´=ÿ‘$2gÊÜ?Sɲ5+g «²…TîU`^ËX )šqëÁËì~/ï³Æë÷ˆý2Yß#Œ»ò~z£wÛ’ˆÅ:.¼ßÀTRe×éÒWNo&C´o' ÝÕ‘ Î<ñ:eŸÝßw—Ç ï#ÚóØ„Ré­ïs•ßwÌ»“u»®O"Ëÿ\ó¨}*Ùñ8öx­Y›IìúnG_”lGÞ™8+"Ñ.l\Ù§ÞÁøì~6‹¹K…¦=dÇõ^ÛÙK[«Ú¸ 9¾¹áW]‹qŽñç¯ô¥ZšDZ{‡¼T'•åopíd›ÍdøºK5N8U$íXÃügì©TH˜}þø¸bŸªdL«­ÏÎ6P‰qq—¼X6öçÅI$oñfÓ*—M%kæÝ¨Rb3Yž÷„gïˆFdÌÇ|»ÂŽØ……ËE7²ûÏñúÈã÷[?wrW•1öPcñó[0OÁkû¿¼ ‰Œèl˜%Ï›J.4¸x1üñ&²|á¢1yš“ÓîÞZ‚ã~ôÀw7æ?Ⱦ~ˆý⟶ý`¾²uß±f„÷ÌŠkŒ»µsÚèÇÓ’Èþ]Þ÷Œ÷qä}•¤×e5æÙWæ—yk'‘Ÿd¡KSHàíæ—ªÖØDæ¼ïšøšDn¿,q©Ýߎûw7Nh¿ñ±?ÎùõSÚg[‹ñ·Òö©Ÿ'‘¹[^Þ‘B.w+u#©tk÷åò;’.m÷/± |Åó’ÿ×)mE؃¶Ck)º<§Üˆy~¨NMFI¤øäòÃb'§†YÀäÏ‘.7ÓluÏøaðÆì:Å?7¿>¸ŸÅ 7vÌé±·`Ü´;›,‰(>•24…ü5¢ëÄ yñùÿT§ÆážäH ýâvál"m,ǽ2ùX}Mö÷÷ÏÑ_㺇?ð6‘\[ñÓ’™½SÈÛ¼3o_œ¿¬ÛU(¾°79×¥¯ç”Û.\8ûªÊÄCÙÇEì“•æ_'ò~mÒþ¹>ŒŸÙ¨OTÊÓDòÛvã¬õ$… úzë¹BHª§ybò‚^$K»yÓ.déà§d×Þ_Ž¯ç¾ é«ÍÓ£6‹{±kÀÚtõûogÜŸy#‘,¸³¸ùëúÿZêêµq²àÖ‰UÛ¿êA¬ÍT¥Þ_· ò*{Ökœ÷üs‹ùÔ€ðõPVÜcÜuõñ•'’¬ËK…RskÑ}oªYHç=Xm\ÓáY tí‚Øß-;î¹—F\5b}ÉÄ>jŒÛtÆÙ±‡×'’yT#›?…´J/œgïüŸIÍìM£;“÷ÇÊTn‚z¸§ëÁ“OÄø¯‹|½Å뮸.Îùùµ˜‡vÃvÉÔ„²oÏ=K&Yº±+ëY>v"mϱ¯³Û…‚‘ß>K›ãï[˜Óg|Ëß—WÚÏÕˆñTK>§O"]+rÓ½Ôd²xu‘y¿\Ož\mßôìÏIZ©M˶ óÍ*~bRŒ¿O;ï‹,íïm¡Çe])'’r[¢´aÇ’ÉžÒõJŒž³Ž$ÓîBÓI]HR@LÍ\ßÚkï»«Þøûq¯ŸÏüëî#®³œ˜çfç|†¢ÕɃüã'gnN&òYïZh÷¯%_5øbÝË~=X?Êìõ:÷‰ðu3÷ŠHóÞ‡qŲ–HžøºâŠdæ)[CÒ“BzÅï !W{-êˆ8üuÏÊ­2}v_;þ—÷+æë ú Ö¥«ÛöÆ÷•%’«åÖ¯Œý!™QL)²r5i®ñÌUÄö#ÃuÓ&/¸c~_ªª»±fv¾òu¨¸¾¨Ë—¼ß2ïšÿ˜gÏ:eá˜øSäž#ê®ó’ÉŽêú“+ÉWŒªÑŸÔéw¾û²]øÍôlàU]ŒÀûMsOÀ6勹‹§·íÿ¼óƒ%D™Ã× Åø[ÖØpôÇSDó-5¤%óú°¢K ?cçG¡z¹0Nå° %þ]]`‹îþíIòhöÝo/ÕK&¯Œ)ó [BŠ©?Ôÿz_7’|¬×ãs»°ýÖ޼ߌø÷çýz¥ý!oëÈ…·|y’}ôS¥;AÉd@Èð*QKâH“‘·ãö?Ö†?=Zi:hž‰C ]Œñ¯ÿùý(¿?á}@³âã^¼Eü$ût&“…ŠÚ³¿_÷#éy©9òCÿ}Eª…~ͽï2¿O€_¾Îãu9+¾1¾XÿN_§h[´/˜L²ÚÝ×^Lvž«§aá.d©âë<ógÚ…’Ï.^_ž÷çôù×£<ùú,+Î1îˆa¦µÇן [‡”-úÓCj{â¢=‹ˆ±ÜÖn¿§!?ïhŸoß8Ü_\®”x±L¬À×AÜ3(]ßZ0ÞȽÓçp‚L/óк鮇L¸Wöî€B Éì™g¢^æïIJ^N+1|4î÷MÚ©(˜Ý›Ÿw¾âõüÝöçžžÃ9ú~;1Ïà†Õ޶‘ ‹¹ó¡Z²‡ôlÝÁX)†õí !¥£Ç)ãs_n“^¡~¡XAô {ü×OÞš_—²âã~QjQd÷3N’š^nÞ¾ãâjvxyþ üþÒ/¼uNÜÑÛ…¾EÛÜ0ûúÏ׉¼ŽóûÃ>ÆŸÓÕ}ß¾öÕr'Y¬>ºg¨ÍC®l6D $Úó?íŠÕ÷#¦?n\7Å.|]¿M¬¿òë2÷\çð`Ü¥gËL‘Ou’G‡æïéhñ7Ÿ_0Ž)A:½=0óÎòP‚k‰»èt»°ì‡Bƒž¼Šñ{ïy~ýyµn=GPÎýŒ+zdœäÉøÆeï,Áù,Ö½çùÄ9äPlÒÄ"uÃHíQCæÚ…&GFþ–]EÇuÿñàû•Òõ­ã÷H:}t±ÊIÊ<~T-#ÚCþú³‘:üû$¿á›[«/…’»¿¾[cèÝØB§Éßw–Þ–¯ëø>‚t=jÄ<ô®«ÚYUê×ÊQF)Òçó¯2æ’<îÉËüÙlZ¶åÊÛì‚éÕ¾ˆß˜ü¾ ~¿ÁÇçq)毒ÕAñüZ0ýPÃ6ÃP6–=¯{Eï!GK,,ÔÍ9Ü{ÿÙÁQ_ô#gæÎS¨Ø…kÕRŠmßh¸gxÁá¢ôÚŠÞœº„û£³âãnn²æÌ°` ÙzùÞpÄÍ®ÆÇ¿l0ŠKaÛü®/YæjëÚ¯µ ½ …{ÍVÄø}vâñÈO’Ïý*k÷þ©ÿ>F<Ïírx&}˜gYìéë¿];NªžþvHê@¹Ýf×M[ép"m´eõ‡>¬¶]È÷ø‚õuþ×H¿Þâ6„ûc³âÞ‚q³>ßqr=© J¶‡4£m¡«È JÝ>L*Ö—ü>iká½íÂz2>t[¾ìë³X_êä8Ÿ ŒŸ¶®ˆãá1ÒyÐŠŽ¯Úyȇ’EåÚ'áBÕ2ƒÚ%÷%ôªÏYæâ„úËn›üýøùqæëÛñŽqŸvНÔoú1’°uÆ™Ú-=díû›u{ɵBÉ‚N®íJj|\^§]xšüÜ<ï¬É¿¿Í¿¿è‹¯“£o»ã¾1—”M­yŒ4ÉZ€xˆâXëÌ{GÇ f²"¦”¡?Y·vƒãnOäÑ«_”´›üû8¼¿7ÿü{FËÎÔ›Õ(‡'ˆñ÷Ö:Üàï(qº«u¾TßCªÝ¾¢zÕeŠ`9<„ŒéN¬—%½ï‚û¹û;TêÏÇ¿å¿Ïâ~©ßÕ‚q§G—ÉW}õQ2yáëCj{È}Ëf;nþü€| '¿'•ú¼½]h5qn ¢ÿ–7ïøõ>k7->LTßRc8Æ«¿?ÓZÁ$èÚqÝ‹}ˆãë÷ÞêÄ.$ÎÜÔß|<Úþøý îÁûgÅ3Æ|]wäžb‡É´GCq‰ðÔ{ŸW;V¸—,Û¸¬[O{¦S‚IiÆïYýÐô}´À÷r¯ûøþŠô9“ãÿÞ/L§øé[÷xÈž9ãPú {KܘTy{W2ç~»™±íÂã=O"N4f×{ß/-õ1Þéq¿ì5?D œìµo¬ÜC´é»o~3ÿáêÔ—EiHÈ»À)WK#¯éò§r´?ˆïkñu÷åXŸ`üFY‚')³§Ëí™8¥n> ²•ÿQ0Ûž¨š|Ñt®^¤~ú‡áo]»»F ü9¿nIדNŒ×3sËœ_CÄ“ØÊ¹¬œ‡ ñbXŸqqÂK‡:W÷"å¯]ü«ßÝÁ7¦ÑÝñ[¢îçãÏ­¸OKºžôÑãºÈ^'æÉAò$¸æûcøœoŸÜØü~ì¡÷{×È.‡,š+Aï£ýõ‚_y½àëWþùùº3+Ž7âó/¸ûº˜õ i©(üö»ÒòÐUãÑŸKÑ7܇t*}lÑðC BúTEúvä7¿¾s¿¾$úB>Ï ŒÿêÉ¡=#j\ó…‡ÌøyU¹G— &í‰gBÉØk_ ­Ÿ ˆÞˆh?gãÇ™{ üÏɲ|A9|UjÌóûäR¿Å 0hŸðÄN ZúþãK);aýW•fA6¤õ…Ï’õ…{–î~6Aè4¼{íßçãÏ;x¾Š÷ Ùõ—í#bü½ÏÂÇ-‹µ“鯭‡TQ%×n’°J`Û¨m­pbtì°är‚`.ôsܺh~\øççëåû‰õÐ y"Z؉¸Ÿã!‹ßUwàҰlíOB’~tþR‚P>¢âòàäþ8â¹™¯³¸¿UO>Ì#tècé}/LïIïô=dù‘©ª¡KÖ é? ŸFŒÅ*µ²&&á“Ë.ü­Ìç—˜ÅýÏCù}?ÿ÷Yù°)]½§SQ·}ui¶üúB]+ù¶ªwp§ôuÂëG1™æadzI÷ì„Z–ÛíGóîáǃïëòu¿ïʱ¿ŽyZ/83§\÷R:ÒyvC'ùªê°Ú7[ÿ̞㇒9fe\ß’ üTõu7E?üûð¼à×)~=‘Þß©1O»å‡t}O¾ªo;Ó±›‡Ü+Sé—ûÄ"ÔªÖ¥ñ‘~äÜè•o®K´Ó¿Lœ’}¼øþ™èórû¯3üþ4‡Oó½jŸçƉxÒ{÷ñÃuBÏo^¯²òãvõ\fÊ8@úf =Äš·kãI7 Oƒhö#B±Ó¦Ž„bûÞT+–oÀ×üºÃï÷¤ûRNŒ+®W9ILîí!mîUñÛ7 eÊü©:Fò¼º¼½.>oÍñ;ž-¹0_àÏG¸‚{by^pÿ ߇ÉÊ ÌÓtø7?ŸQ ß®¯öb0Îs`ï—OÜ$¼p%öýeKRovZÄ›Ÿ„Š+ÖX·š/ˆ÷)mù¾’ô|lNWÛv5©]àÖ~RVEZÔ>QVn³Ó6qgŸø¤PÖMkîuy;_àñÂÏ_ïð/õˆ*0þãúÏÞcßOç¯0îqk!aó ¿¶YÀÍßë~«Â ¾ÄàF‹eñ¦Eº~3ß¿ÿÅó€{VyÝãq$õ¡ª1©Çkyèû ­®ë1Ï©Æïò¦®Þ"4[a©ûÓ2céÔ ÷Ö$ç åo¹¬Ù|AŒ‹Â~ÿWîúÍ¿_ß#æ9uäëÄéÃ÷{½¡+oy·í eF¯­‚öô¡î…’•­ŸÐïI^:;þmå¿î·Ï¹],‡§Åˆy‚×]òv9¸¿ãèU¨•ŸO±ðÙVáÂ{š_ú’þó¾*Vyk‚ ¼é¾ºä|žñxâuöŸž'Z0~‡]d§ŒûH½Œ²·föôM·=Ÿ½M¸³®ÑÓ>úšÍ‚®.ˆMÆýt®óÃwQþïÁÇãñÊŸÃñûõ¬|Àøgtô ï#î5Të!ÊÊÆ·ù¸MÐjü2evoRbC· •u¨{ô±Ôý(ûdx]âûÍ|‡û½øõ0+0Oß.gR]{llÿÖC.§ïXóÀ`âåÉSëméIæLLþù½&A OOˆú[ür'ß׿ù&}(`Kºúþšñzô¶‘EQöµÝ'yHȂ޿]}j&_lÛ)1²)|«Æ¤þ„² ›Ç-‰øõ†Ÿwñ:wÍ__ù~_×få æÑϤm/)¢«:Î0ÓC¢6ï9hØvA8©šðp_²eÔæ_ûµH %FYc·Gùë8ÿ>ÜÆïùûR?šóTÏ*T{Éñ§æžç!åäÞ¤l´sòF~”õ"ƒ›^*õºA‚ð¡s« ™·¢¾Ê÷£Äã–ê÷»K÷G´˜þB¥÷_î%¡S‰ö­O‡Ów‡ð>½fû&ùû9q£N$%wcnšüÙ|{~øñóä:{ "ü~;+?0þÉW˶=¿‡¬™’ÑC·ÔC¶}“T3íþá»QÃV¹Êõ#Ontí˜Y>AhñDoP'2>¬ôµB›‰× Ì#¾·´‡l¢ZÛÕ¸¯ªUföªÈ´ã¶umJ&ÇTCD%£ë$V{þ"JàùÌ×ͼžÏ™úøìè½HÒ §×¯BÄ<ÁøQ=Ú¶ÙCRJÌíyÅê!—Ô;r¤ö.¡N¯¯¿ìF ÷­ý]“ Âý:å„Ú»²ó¯ ø÷˜°)ÏÑ¥QòµI±ë(æÆ?U‡ýånr4_ë1·÷{È C“û˜nìºöÑt®?y=*·B Bz·lŠøz’?gàÏiÇß;\û~B_Bwcó¼n/æÅÖtu©;g¯µÚ´›Œ+2ò»rÇ<ä⤠Ÿ=[±[(½<ìŒoéÒÎç‹J%U¦µz{62JàqÈÏ7Ïs~œÄýÉÞDôީżÀ<gÚ‹úÝäUµN#ÓŒÄOþ2¸n™¡pãñÏv‹ô×w~~øshñ}³6„ûͳòãßËÜà1­ÚM =M=Óá7)ìzQ¸Ic›0|ï7ýFŽ%­7íîVõm¼Ð®ãØëIM²¿_ïˆïí¥µ=y|]Ǩ¹ ÉH²p‹æ¸¸eÁøCtÔH¼› ip§UÃt©NGØ„òOêÆÞîKFÏ¡oêÅ ú‹®Ý)ðu3÷ÍòóÍë†t}îÄøë;|¾cxݤÌ,]PÝLy¶dòZã9›ð$Ïö|%Ob™÷ûƒ3îÆ š›#u(%ð¼Îy.µåïH×å>Œß&áÍãêÝ„þOzY2麻ÝöBåö ÞúyGÌÎ×>_ŠßW™ãyóüy}Ü®ÄÖ;IÔódGs!™XŽ6y|Z/ÄNÚS«3i“µ~~Tãt£÷þøâóq6ߟ¿ú×mE•³ÍÙy÷絘G|ll?mÊóƒÉdåûv­-tâBɳýCqg/$ŒÛýeéëþý ¾/ÿ¸ZFôÏÛ’šg.ŒþK|ïÔˆqCƒ4¿¶X¾,úfÔÛ8ŒÛóA‰>­öÇ \y´·é@Þ¶î<ãÁýxarÈÉ7„Ÿo¾OνÊ<mýŸ®Ž»¢"¢ŸT|/Ê‚y†ýÖ«êIÁJZ¶¦G*™¼ü#àü·7âgUaŽ©xg2¥þWF{j¼pwËšD…-‚=ß/ï®ÊŸðó"Çú9ŸŸaž: O6ˆ­l%ñ÷žÇ¿ÃyUЖ„xz’´³P­÷²m åV•¤xA¼žEüúÃïçù{Lü}eÒûfæé#t`Å6Ò£‰æD?W2Y•Ƕ¯=òÛåì~xz/BïÒ/gדHïCð¿üûäô¼gˆy²=]Ýð²¢@ï*ÛH÷eÃO29¼>ípFd‚ð²ê„í¿NëCÆ·Ìëˆñ_|_0eMŸHÿõ‰^~¿ÁëÖ?ùL˜§^;S—îÞJ&¾<Ýuدɤ‡èQÞ Bbå¹%Ìõú‘qCÕ_Þ</4]õÜîù6»>òûL¾®â÷KüydV~`üí…öšÕm+)üÚ2úÀãd"X—UM«lZÿU¹A‹g¡d_ßvR÷Æ 7oÜ#Ò¿/¯‡âõé’?¾x]‘ÖG-æQˆi¤×ïwÞ;Þ'w_]ɱÓìBå·ëuzFÎÔðpÁÖxáNRÛÇ”ôßoðç|=Åó_Ïùs®¬|Á<Í*ÛwcôRùÃê]}J¦ví[|÷ç%»°;®gÏ[ýI³[žÅ÷6®Š«]áHïÿòçšü=¾~àÏmÅ<ýK¼ž`žIy“ÇÜ*¸…Lö¥N]3…L©Þºsåú…ßšõ«R9f Î»Ç Ú¶("/)ˆûa•üï9ð÷Mø}-ß/çûC¼®`žðÅqóZÙLNû¡V„*…tZcª{P8uvÿ¤iýÉ¢±¥úôø5ž=o`ëêJlÝ\ðzÏï£øý¬Ôkë£ñ<¬IÝÑ›IÄÇÝ#ÏtN!ešf8ÿ pmÄì!=÷'ãÚî)^ë¹³1ß•í;'Âÿ;'~Â×Óü¸ñçÒó°#]ýà¬åº¯ãfÒ¾êø»½CSHñò‡¶ílåF¨ûòí¦Pr Ø¤] Qwª×~4L7-Bà¿É7<ÿÿÉs«À<ûšdlªðj)Ü´Ãå21±kÈþ…Á<Ñ[êN~„Úî—xâ9ùØÆð4¿¯›s™àëœû7â>ŠóŒT¿šù×™M¤Ý›{ŒÕ¥&ŠEúy‚ªø«sç'õ#Ê—?b%/lLØxaÒ„HÝçë0~{¿¿_–•?˜'_­ÎJÖM¤wÁ.¿ìœ‚¸žµZ5¤æ!áx•³ç• %-W »/Ô2.©)ðu6ÿ>O,ôÝ»Z9|ÕNÌÓ®Á¤{ó\I;åïwu‹SˆuTB‘]õ MwúW±äìÉ«W§Ä y¢JÖø*Bàu“?×âûÊ<øq“þnˇyBš‡øÖ}¿‘ì(R|?8çñ—ã?_Ÿï\©FRuñ´I_O%•Ø®o(xT˜×aL™æˆfk‡m1þš»7kÞ¬É~?yÃ_Or¼ŠñÄõæz²ÑcZ5»r*±Äeê«×=*”»8w`zj;ò\~º÷]\WмzóòÇìãÂÿò}~_'Ýðaü«ÁîÆ(Ö“3»¦×N%U¬B>k¯£BÜ$ÅÜÄ"íÉíê‚’¯£^ý9j̶þû^~?Ä÷}y½GÎßìJWg½ÓjY;¶þùö­RIzËÐ~K§Þ=çyÿmýyѼÅ÷ž˜ü‘‚¸o¥`ë‰ ÿûü:L%×, çï…˜‡[¥µÄb¦¿è!#v§›±á¨°ÿA©§Sv!/ŠUxSË/ˆë›¾n×I¿û㈯ϥï#«1Ï‹êVÿÒ| ™ºF¿÷ü TrhÙÝí“ õõKTzÞÔ*SÕ'ß/D7VçËŸG¹÷±ÿé=S-Æ/jvî>2c59p¹Iš”Jæît¯¹_ü˜Ð2ï`K×B!$ë6|“$NÙ~uÎ:/îï1^ÕÉofÿfñ„>rqA*9£N{ìí{LذòÕäŽ#ú’Äéï,Ü/”Ôò—þ+#…œëõRþ}X~Þ¥ûúŒ§ËÛ©Þp3©yg÷ˉ+S‰bP«Wá?t=ÛTœp¹™:;¨Déñ‚jÔ„ã¥neß'òý&žWüý$~_,}ÿ‰yzö(?üÏ.f²fÚé¿’¶¤’ZaKœ©Ç„Ôu·ä«¬¡$k;h]¼pNslÛäëÙûA¼îñýþ—ÿTz÷ažÒ:Ûö—V3‰ë^q†b*1ÍÞ6!¹ãq¿¯rmʪÕñÂ¥^ôBŸ½^à¿#â×!_Ò÷åv§«oÕþpà‘™œuæ©?øD*y×äÙ…‰æãÂÓµßÌû.:”4·¾¿Òk9Ö=èé_÷ðãÆ¯wü{ðóÂ×ÁYù€y²Û”ZE¬±Ûÿøìb*iºíæâà IíeÕúœêKÎÌpnÝ*ÔUm¾ª‰+²Ï ?ïü¹§xÒüëy¾.Ý þó©×Õ§¾+ÿÎ}WØqpˆ^7ëiE}#æ\¾9s¼¹Yi©o„öšNc½¦­,¸¥}[õÌ!Ëû¶¦1çˆEÒ·ÕÉO¼§•Š9GxA+ë³¢g=[¹ßÉËüNqÌ螺ô½—z ¬g«ŠõlÍÌåw —ø¨OÚ%é#è–ô¬v³¾­qÌSÎzWJú­¨‘€– Ñ'M=ŽR¿í{μµÔ;B{Y{«‰.i KRs;ñž÷’>+Ô#mb^'ÚÇJê²ó±>VÜo«c½®Ì9’!é%ÈÜi’^­ò\ýU¬ÌÛ¨ÏÕ¿JÎzú˜Ë‰÷h彫BXÿÀ@#óFs_có5JûÛg2_#ío/ÏåùTK?ÕRcÀ¿-•±ïI{U²ýî\=Yy¯þ@ænòæêɪb=Y3XOVó7Iû˜C“÷Ì`}ü­’þ€.æ¡þ&/ëYe–ô¬¢=YåÌ•ç“8D|Ì!bf‘Iÿk©+ÏÈzªYo@šDR‡ˆVâ¡N\·¤w•WÒ“ÕËúš™Ã‰÷®R ñŒÀ4H@kèÄ¥®<©C$“õeµIú²ú˜×*ñáz%½¯©¿IÜÌ…ÇÜ!¹ÝMi¹ÜM¼/«’yh_VÞ¯JÁüx’~€Ô`i@ƒÄ·17íyÍâ6ÖÕÈü·!’>€&æl g=ªr÷…r0÷­.W¯kZ8BX¯ëÀ\ŽŸôZCyýüWµ“;蘆‘×EZ¥>Zy ”Ö?î«ãuÖ4ZÃhý¢µ‰×¥U‹h ú¿Qhíù§ºóO5ç¿¢ÞüS­ùWu†^Ë4ìb¨cn"îà¤n" 뫯cÎMÚãNƒl¥ýó™GÛÈúÛ™†y³]¬?¾õ ¥½ìYß: ëUGûÔ3§‡–y¬iÏzÚÃÓ¼A¢gû:¨¯Ú'qûÈ%n5s«Qwí™ìeþJÞ¯S˼jŠÚwT˜›š»|™³’÷¡a=’ÌG­ÉÕ—ÓÇz#[Aj`ŽJ ë=(é‚t0?¥‰õA:˜;ÍÄü¹û!Y7M.'å§õ̧õŒ1àß=Ⱦ‡;@t™˜‹RÚ{“÷˜W0÷/WïM5ë½I{ÌkYyê!¢Np‡,Û N}”jæ£ä=æ©RÆ|”næô01¥†õ˜—!t¬g s¢¥I|iÌçaa>pI¯c©ú<2˜ÏÃÆzK}:‰Ïƒº½@‰ä2Ÿ¤'uRª˜“’&œ¸€‰gi  h ݿԉ&õy°>œIδj¢÷—úи÷×'ésLûÌë—9ÍÌå‘Û”‘ËÄûo³>ó4¹µÔ{”̃Fûs%í1o ‰ï`4ÚÛ˜;Ҭ黎y~©¿Ã ”ÍEo:í-O{n:"Ž9~¹ûLÎÜgÒžÆ2æ>s0g’´¯<͵€€œëZ3i½¤5RZy_bZqÈÿ¶~‘Ö½ÜõŽÖ:Zãhm“Ö5ZÓhí¢µŠÖ©ÿÓúô¿S›þWjÒ¿K=ú­wx ¢ç‰zÇeÌ#Dý‹Ô1.ÏíZ¤÷G…Eo¸¨Â\ázæPT1g­õéÕ1·™‚9'Ò˜oÂA÷‹Ê‰N +sêÓ.æô63OOëa”홥¾2ýuÝUDŸwšÄÏȼ>昵Júêú˜ßÐRK쥫¬ýwww sw{™»ÛʼÝÜgÈû”‡³ºJ䯉9»©ËÐÈ_#óDÐþ¹6ˆ 72aëON&A}愵°äÍíÛ¡.H©oÇ|Ì·CûËÜ:àÁÌgÀœNÖƒ<d‚p$¾“¹Ì¤>nÚ\‰BÇ<°Ôïà’¸¸iaÐF0³"ÁfÌaæ s;P‡™“ùu¤}Çi¬Òþ_ÕRœºÿ¨:ú©†þ×ÖÐ`ößQ7$õk[œysÜÌ 'qCÚ˜7‡ûµ©RŸË é*µ dÐçn#ÀõÀÅüÚ> F°[A&GÐÛ˜[ÛÀ¼j‰’zÊtï É gþ[ê)ÓP"9ŒÀÇ|ÜSF}Næs0/Ý+“øTŸõáZY2…0gŽì Ñ )Cbé›yµ-Ì—ŽDsEP¶—zʸËÁ¼Ur:µ¹+GÁ<ÔS¦a‡@$§¤Õ=9V C¢ê€×þ»K[Á\Ú>æÒ¶1¤Nâ‚4ƒÌ¢§Ì‚qòã˜G›z @‰D71wCÞHzs@R7Ž“yâ@&uä ¸˜ÿ‘zq2©¿¹vƒ™o!ù¬Ì·`>ælˆm²ÝÙÜýøiúij ø÷¯¡*ö9|¢ ÇÌœ!Z#põÀ‚™ '„ - „  m@† ÖsáP磋9ã˜ó1„9qdxs>2ç#õ„Kœ8áHD2€ (™{,“º‘æ× g.ñæwE.÷˜™%O8s‰"‰tÀ ‘Là*æ×M*$WÈj$™…957Žx ‰g™@‹t‰~]êËZ$£ƒyq Ì‹£DbÆÌj¢[—zǸ[7„3ï£ kiÌ­k²ðáäòá˜@óáXÉ­n b¾1s>RN0ÞÌ’^ \Ì5&u”»@0 ™pK|å2•¸ ÂÂ\ºÜ1¦`ޱ4‚¢áÌ1æbþ3 ¿ÈÑ1Çè?´–þ«:Ê÷çhýdŠù¬šIOù?ÕJi¤5RZ•9ë¡´Jkà¯þ©þuíãuïÖwûOÏ/µÙµ-wM“Ö2i£õ‹×®ÿQÍ’Ö+Z«þwêTî%­KÿT‹Ôì/õ|Û@ ê1¯èùV1ÿ!õ{ÝÞæ;L£k7A&}§5Æ næñ6 ‚C‹àp0w7uR—!õk9å¢7VúaÁHû,Û«¥E ¹€uÃ|ô/ËÔ.3 0ꊵ1g¡£‚è+t9j…x™“›Ÿ82HôÃR‡–(«ˆþípÔ'óÁR¬( &ºGÏü¯z ªí×V"pÓ˜S[/q ZØEú¯Ü@ÅÚÔ-è¢÷žîpä·(äqÌ'¨eN×`¼™]øuÀÍ\‚4tÌåJýTÔIHŸSÒ÷¯Úd;±¹+¿(]}Z}Zý;­‰4lž4z>™0ÁêXpjQÈáb+ äpàr´¸’¹ÝÌ hfANØ6 G°ë™#PÁÜm™@‹$pÁÜ  afIAý×V–Ü-cþkP"YŒÀ TH !q´ÿµ¸˜ÿÚ¼@Í\¬@Ä2ƒL A‚Y™+:° Éf> FÒYXâé€+Ht±šMB]шd47FRšYbR«KâaÍZæT"YM £–è`µ)E¶ø€ le~@p%’9d ’Ú‘Øàj$¸È™Ð THv !áuÀ ‚sù¯Ý@…"`aÞU=ðJÜ×rð5Šƒ•9W ÀËœ«&ÂQ0œ@¢¡g>@Ї…Ý¿Á¾ÜÿiýTCÿ×kèRý¤Ç׸ÏÚTS^Ñg­FÀšYÐrŸµÁ«/ðwŸµšù¬3€Amf­.úln^泎i ocA¯Næ³6‚4 a>ëLúî*’Á̯ªDR¨q ƒ¾¯†$±°DÑ7P"aâ@P!qâ@Ð ,@ö¹èYu’I œ@ޤÒ'Dr¹¬­@†DÓFÂʼnžU­ÿ#ùLÀWEôX›A&Ð"]̱'q¬:É2@’ÔÁüªàj‰¿ZÜ É2˜¿Ú!ñWû€ -CÍÔ/P#¹Í,ÁuÀ ‚‘èfIï)‘ð.Œ¤7³Ä×1Ÿª Àd(zàj ¡ è™KUÂ`aÔPÐçÁ yPͬ`pWu0 ‡ùÓ¾Üÿ·õó?m ÎÆÉ ç Áj™@‹ u×¼@6³ ÖÑwã€"¨À ‚ÜFàj¹ÈèZàxp%?N⪶²$Ð'P"LÀ TH 1Oµ È ºÒ¢¯ZÎ|ÕnŒ„1P#q¬@ŽäÑI|Õàf¾jð ’Ê2Éea l,Ѩ³Ú H8H$žÈ|zà*þ C"꫈¾jÒ¼@…Ä´’SÜ Ijf‰ªnŒ„™ ‰ëPŠžj#H!Hb#‘õÀ ‚‘Ðf BØ @rhä6ˆD7/P#á­@ޤ×/PåòS{…À ä(à“¸©QŒÀ4(6ˆ"a>Œb2éo©P4\@‰Âa^æ”¶¶½ÒúOµôS- øÏ¨¥ô9€ ªN DÀÆÑwÿ€k2¯¸€’î/7P"˜MÀ4j È·Èèo €(éï €¨ðfÂø Cðë # Lt„ ,,!tÀ ”H pƒ`$ˆ xšîK‚L‚„±ýð‚`$™¾gÔH"3È ¿¥¢ï9Jœ@N÷( "Á ÀH4H!H8ý=’NÜ@…ä3³Ô7P!ã@ABZXRꀨœf ´HRF¢šA&GÂ:Ik> AòÚ@ ý}ðÙ 2é³$´"© hÜV G‚€hè C²ë¨ô–ø:à* ¡è¨Q ¬@Ž‚`> Aa°9Šƒø€E 2A8Š…(Q0â@P£pX€ ÅCÜ€Z‰-D|–ld÷ûÁ¹Þññ·®*ö<:ƒ=¶±çÑzöÛºî5±ß‡¨Y¦ûnò÷ßyý7öά©lëûØQGÅÇdžƒ;6VìØCP,(£bÇ2{QÔŒ Æ;vÔ€Xpˆb EE':–(ŽbDZ}kçœ}r‚sï;ßûÞ÷ýî÷}ãóüž{Ÿ{÷NÎYkµ÷ÙùÿØ¿ýœK‚ìxL“Xu¿Ç3µàó²`Nœ÷"ãmÁ¥üâ }8Â|Ç×Wÿ±Iº»à»ê<Á¤Ü|B+øMÍ^©MÖ¿ œgÁ¦˜!/ãÔ@Ýç·˜nC÷²?çÝšCÈ(óÀÒG–Éaͨ ²ËˆÕ'ùI°Ï%pÝyžó‹s”8Oðô+w®«¡Oâû/e¿ æR8!Ý& ß”ò`(¨ëtºöÇ «¯óQa¾+̯‰ùŸ±ñÅ~:œ§µ9üC æ/ßX2 º—(õþÕKB\Û§ï(ë= V¾©Ñ¥O¾–Ô Ú¼ña“•‚ﻮ̿‚ù÷0¿±¯ˆçø]I§Õµ¶¥†l©—ŽDºþ{iœø}bøJwˆ™A™Z²·g…]½N|ëOÎü.Ù÷b~ebÎÓΛ:Tm…¯'»ÎÞß> ŽÕ8ôÕ]¦#“~Û;²Èõ7óŒÏ´‚¯ó-fÜGö=˜O¡ 7ñDìÓGY ¼/?7¨ùu~Ÿ,عû¾KáxÄ®ÅÇ¢á‚Ï‘£Ñh¥à¯Çx"ìó3^‘˜Û*Åñ_Û_óW¹tlve”G´ÚÖÂ~ZG6{›}ð†y»«%í"¢j/Þ,ø.³ëÅüÙ}ùS>4Î3}îÍa³ê©Azö&m,Fš:>Bvúš7H—D왦%mý¯o¹4XðÁfã„]t7lœ–+|æK$æWúã<g%;7WÃç6Ù{ËfA~Õ¡Ǽё÷vw¶´Ïúï(yBKŽëT·ï ü¼™_ó«a~>Ì¿Dìs¥Äy†•¤Îäjp•½x}^¸/¶²Ayþ@qĬõ÷÷%ܦã}¹3¶Çs¿ãVßCvÝ8?–ËBüŠy?œÚ¨ÎUCFÞî°UáYâöξcïR©M« ¹“åðÓ\Yµw¿^^Ô6Xˆ_6Ë{_Œ7(ö£×á<Çê'uyjVCð¥7ïïÍ‚×ãk\>.XlÄ? „¼ŽŽ·5kIÙ×>úQu¬yÂùâ<x´b(Ž{£%¢öVÞ7< bÙSgáÂ’r>n]ÙÂ~p¨ uäÕ’ ¯ÎüRpзßñ>ï“ÖÞÆGÉîdìdYJ’Ø ÃdÕÇ]ÎÈ‹í*ܨ×yOLÿ>‡ß=Q\×’•[^QõÂþÿÌG‡ù¾q×£­¿ŽÇ_rêýý°wjð»y îȳ,¨Ú3Ìï¾Òòßä€N½@5Œ&´–¨Û´Ñ¹v ®‡í> B^3¿7qü(qž ôvÖRCн4ìŸ †#¦'M $xBÕ6oZ¹ÁüjU¿ô5hÉÃÂÍ« |å˜ÿQî—®ùg<Å|9mù±œgȾ«Iß-ÐbãÉÒã³á¬}ÛîÛÿH ÛNvíVg0lY¸÷¤–p>ÿ!Âó‰Í§\ýùCÕïó?0æ§)öÉÓáºöÒl8¹{ÄôuÉÀĹ?6—ȵœ–Gj‰±oZ©†Á!‚ï{>1?iv(Mþí›F¶~8*¸ÚÄÜ/› 3µûÜ’ J}ý²»%’u¦vó€%ót?'…kI`ãì!-N†>]Å}åXòÈÎýèUóGŽ]tÌ=¾côð›x>W6„hq@@"\kÇÄfñ^0êhg!%­¼ æ{Åò]¯6A;`þ‚–|ÁñO,ËžtóH˜GmÈÓ²A[ºN‰šëIï ’g áùÅÒ‡\UZrÝ?Ü­ÆÈU‚óš½¼æËÇMM‚¯W:ãÑ[òç¡Q¤ -š–>úôa6ô¿4êö¾ó‰d÷Ò²k+wöõä>ÞÛ´¤åî‡ ‚æ¯"¶~TiÂ÷[îf÷ÜnÀ|Ý-ù‚ãO3l¹ûeT$XlJ?eÃÌ7m:˜IÇÓ†9Ñû¼ã'jIØ‘±O+V }ësgœ=Ÿ–lì6í‡öm¾‡çÙ|f~?¿M‘ÀñŒ0¥zó÷Ýj\&œÏðp¨zoléu[µd~«F ßL³ò•˜_wÝÚ{ÎZòÇ­Þ¿AhËôH¨X!¸ÝºfF¨Ñäz™4Ý]édn'ÔIl›ºKK®<ï¶*eé*Áw›ù‚±xe¼o–7–¼Àñ=~¼W>þK$Èí’ô»á½[—¡º —Ivø$§µ§=à8µyÒ’>¡N“^„[¯?»Ï̧‹ù‚q|ôÚ6õÑ„óšöà|‹MPºDV$7B¥íÕFJÃ.“q5ßþ´Ó8Œ–pyºŠ0ÿ·?ëÏìNáõ¶ðg"Ò0káë³þkõÑ—É:Ù¸·ÏÆx殬íi-ѬÕùÀEk|çRÚöÕÜsZŠãï]”ÕqdåHØI±ŸKðƒôüÇùÆËäLJ¤õÚõ(b´ä¹¶ã’r«¿ñAäüN3–‡bÞŽ ç©·ÄB6‚ØŸ†¯Ün„Ô÷á}K_!s®®È¸¾ßæ…é|ÕiÉ×1ªl½ZˆOî¾f ý?«bÎŽ?ŽÏùãG@JVû•›Ð%êÌ€¯EÃg´òƒû ×RO±ÿ¿cˆÜ¹T¾ZX·°ºÄ|ÂY3_oñýUâ<–rp1ö$V¹yÈÝÛòš}…¼0Ö)ìýbŒà§ºr×#·n[ù\ÜsB/ø2ŸbqŸ©Áñ—µs„Y?Û]o„Ý­Úšæ¾BZæÝÿÙ·»?,zŸ²èö5k=b~‚¶ñú«Ð7±ç„%püY Ïu| ›~ª¿þDºÖ\sZüÛ+¤Î˜ýGo9ûCD ÍÚºg±?°ô¹«{î°u×/ØúCšp\Î/;ºþøèËS#TyYÅoa‡$2ÜòÆÂÁf uÓ1¿šFxv»œ¹Jð¡d™Ï…âþØît¬Î£ÒÓgE…س~KåÀ¯©F§Õ³’ˆ_Õ‚Þ7ú†Wþaw\jIëÔì EÃV Ü ¶~a¾š,/˜_¼ ŸçyU°Úè/^ÐrS+Öµ|SïYT U+î^½‘Ð%vW£zgð><{-y¸Õš¬ïgÜ V/˜¼ ç™aY(F ÃïMs`Ô‡ˆÔÜ$ÒjXÖ˜?†øç·¿Z¨£Œ/À=¯°á7úãx?~^yÆ/5jvžÑ:çÀ/Úº˜yÉdì^ç»}‡êœôÌóÎ ¡Bž±ºÁúæÏx®–øÇñßÏý9eüöHµîÙÀT·H8úðæ`çdR¡H5h~;O˜ÔÿÈ‹O{qdÉŸPÁ·˜åëÿX>su´9°ûmÉœ§c/ð<é ÍÛÚ«72ê½ú1Ùkx2)·oÀœŸÝ!`üëYÓñ{ÔÎ<{ýÉéP!ÏØ}`>¼¬¾2¾´˜ó¡Ãy~¸ÿÈ«]$¬8^­eåI90¾•k"™ô-7ãù‡£î02þòï†h-Qô-$›ž…~Ãc>Ž,ïX<‹÷'L8OØýáY’"øñs `Æ®§ªíÉDñ6¤àØ,wXróΉ-µdÿѹówVZ#ðßõ±gµ¶m^œ)u¾uÆÄ7à´çB“›ks@3èN×ýñÉäݱ˜žûºCÄç×ü±9ëx¦g³:V>:«Å9cÌÿY\g¥8O«3öIÞ†H8002þÀŽðÝØC4™Ü¹ï”Úèµ´°,`´äÆÑ®G˼ â–ùp³u=ëÇmü_qüÆ“Æ ëöiÎïÓêÆñp\~njù¢2{™gwñÿFs&ȧãºëÝØÇo„~ÓW2ó±ûƒúãøþt9[} ô²×o‘'ä@ó›Û3ïµK!¿e7K?xÁ•gò&OÕI¹-ë»Ü²Æ[ç2¿rV_Ù¾{îYòçiw|HÀ¢Vj(÷lÊ«ÈÛ9p¼ÿª„=£RÈÁÝ©mîâsul¿SùéS´¤ÁÁ%s–½ úpÆ fûl¬~‹ë†ÇÏír£äº~®'Os@îr!fðªRrV¥ìûyË~÷åxvÏ\ë}Ã5„ÝOVïØþ ËCK>à¸['žÏ°d+(«v˜Xí-^ÿný[8™Âs<¼`iƒ}Ý_ÏÖ’‘oœµ]×ÏÆA`u•» lú0ŽßÿkjL`‰mгò@lys@/­çÛ>;…´Þ·÷cðFO8t6Zâl… ý «#ŒÃê*Ç—j%ìãYòA[ ÛtîC¿¹·¶AÝW·¾»_*ÞÎj\÷K ùœ ›óâžì¦go\¾8bU¨ÐO²q”}Ö³ü³äήiU{Ôôµó,[9Öh¤“äõ¤å”Ãá=àµ]­o{k‰ßè¡åC„ ÷}nÕ,ô¯•Ž*¿Öƒ-ÇçéûfjÁ¡¯;Àõô¹)»çBÙŠ×O†õדðåß\/é?•\º®IO-©ô)jÕç]ÖïÃò®Qe}ñ Ž —Ým¹x8Ïp ÐzTünħ]r¡ÕM >qõ$9*ù`áGà|‚µÄ‚Ùi¿FX³ºËžOl=ÌÕÛB~Zòç¹™³güŽÝÐØ² ç’>å"õ$¿¶SÒ“e^}V ×’ö÷KG¯ê!ë;X¿À|õmú(?756#+T$óƒsáíα‘¢'én›fÛOô†¡“ÊòÑý˜_'×Öç±} Ö×2u¶¿ æ,èpž£Më?ôï´¢<Δ¹¿‡}¯À×ÏõdãZóÆKG€ÅâyiɾkÎÏ˦­ê!ÛwaëTglŸNÌ)1á<_hù~³žMo_óþ/¹°øØ}»7¾J2{øØ öŽÿ­%¿Þ›è¾¶ßñW¡¯ï3ÛÅÈRü“gí…§O¨!z. H›Yz¨ßU2n@©5‚ /$ >ŽÐ’êoj/¼b^SlÝu¹˜ÿ<ÏUÅq5t›÷Ü>Èo³û˜äD.,úqðÊÓ›®’ók¥-–, jÌ]4 @Kº8Ž;øûØ5†]¶_ÊÖ½l_Ç’8> P<›tj?ïóœ œµ¿.3\% Fiž °+Ùu¼ÃÅÁï‡[?7‹Wöœc}-Ëkî¹øÁrÝýq—•?v\¤9“© ·1rÎ/ãp}•Yx®ÚìCá<¼…çA¸_b‚ç‰Â\ˆ¸9¯Û©׈æü˜<Ä¢ëƶº6 çYþD;#xí7=Ö̆?¤Ãy8ný!8pÇà*̓Mëâo¤m½Fºelª£ôÏ&¯&ÍðþdÍlv°â)Ì ž/Æ®ç>XŸkÉ×Tµæ‘ía‡àҠÃ’ÛæAzv˯ ~èŽß]ë°m”–,Þ™¹åÀεBÿ_œ?ÊÖ«+Ö{Çìß×Ýöù‹}áQ¿ÍãA©gû<}\óøçì52ÿÐfû“qž°âh÷åÑžZ¡fÏç6s•úÿbžßÜõon³?.Åñ9^Ç!(0ùÏï–†Êq'ÔUSɞ¥˓VyBj›à¶?¸kI«ò”è¸VàG³ïî?—7?Ø\Y,ãSGAQ‡ÌSóàCþO'Ó]RImýÞÝczÂmܵd¾åÅB˜Ðç°ç9ãT³ç,Û—³áKà<[ƒ5û†·: SÏÕðÉiëÈCåüSÉ£€ô®e¼pßçˆð}XÝǯçy×´n•XãqÈ=ºaõ¥ yõìÈ»Ý×Éœ¼:%‚ú{B¥¤ú3_ùiÉ{ï&Í¢î…{ßzO¨[¶\Z®_Ðàø“¶öuŒ]qèÛI¿ypcØ…Ã«Ý ñʨ¾ë/y‚ûæ«%9‘NSû†Ãá)¿6ï½qü}óH…Â;' Ò]Ÿ‡½æ<¯*ýÐå)ÓðîÍ:Ç<¡|cæìs®F¯íÜnC8)ÎaqkÉïåÛ˜Ö{÷Ù¯®½Íƒ˜”‰{Æß F=1=Ð{A) €QK¶õ‰7eì°ŽÇî/«ì¹*Þ?µ‹+Ýá!;òO‚WXèøWeó!õTÙ.]ÖÝ ¦½ÏÇ â ¹ucèaÿ™#ê,»l½ÇöÝßÉ&pžc¥¤ÏŸ†Ú•WþT¢z>ì©wáIo»Û­G}—é ŽñÇõÌâ3 c–„ \ Ö_²ý›çŽÛ\ýéCl4ôœ”~ðiƒ|èl|xàXá Ja…ó‡Ô‰•´Âõö¤³›¯… ë1.þn}%{>ˆ÷ýqü[¦T»ØèLík!óÀÇ9iª—Ž7É÷¿—œ¹Ñy4Pª[•æZ2<±úÂÏÂ…¾•õMì~²u«¸Î)q|ŸÒ_ý®®?Á”Hœ3T_ãÎL¸IÜ\Ïo·y,`¹þH3- 89LxOÈê*ãåÚ>O«ÙœÇÐà<µ¿;qnß×S›U»üô.ùÝõÆèƒ7É×ÌŒ½[WøÃǧ×{4Ñ’¨OS<\ö ”íC±8bûâ¾@‡óšýåÖ¸ÓpötLµ|x8$§OÂã›ä¹ª\JZÂXØY‡±oê8²òyØ7\^¶nÝdŒ‰¿³±žÍ÷0áø­Êìÿ\%ú4œv«þÂnX>´íš4,´é-bú­äªí½Fƒ4àùÈ^®Zòrå˜eÕv[Ÿ l]ÇêÛ´á§ž+•wÈ·£ýªàTÓË+"ê×]®}‹ÔX6³õˆ ‘ÐóeÀõу´Ä‚o`SnÜláú°ý1GQŠãÿ¾ýãÑ”%g` å…Y>œzä·pÃú[$=éà¦^RXó|Dv`-)óñ䜌‰áB½fý8{N³|cÏi1ßE†óÌô`Eû¢3СڞªÓFåƒGÙ3'æ»Ez½I†Uô­Ýô£#&áúbÒ’kmG„ çJX=eñËþrß§µ oÎçéñÓµÕš)Zx1g±Ö% Æ­;óÄÞt‹´Û’V®™œødë˜ñZ2nzøÎÂjáÂþ#»n¬îquÖYàžZòÇŸÒᜤT"®Þ9nJ>èêÝ¿ò©„AØ7(MËÊOZ’¾a•ç£Gaßð-Ù{$öÞÂ’8nZã~·ZÍVMüç.˜“ïžÔ¯~³ºŒvky3¾¥”ÍšõIŽë"cëé.-+… ëw¶îeùÀÖa–øÇqýO­ Ëψwô‹äCr¬wÒ„fRª{eGÙ98)£ A-‘(ž÷5;\ø¼,¯ÙsŸÕ¥ö¡UžµqæâÇ÷ÙâYr|µXhs ò«…ùàžUczû®RãûôWCx‚×÷mÏÜÂué݈Õ!#3¿9WÅâ”­{-q¾@6ÌÔŽ…‹7W}jœÁ³;)Î5™ Çkµ¯?BN…HË5Õ’Þ³R¶ªµN¸ìþ±8açļU)ŽŸœ7ðääXˆép¶—ïº|ÈöuÄë¹+9”\¸ÄFþ8³¿ ÖéÙw"_,¿î›}êâ\söþÒï8þíE?êÑô,}x‘0uK>Ç__d »³×«7Œô ÷NíNòÀ~±ùùÖŸLÖ:ÍÝ¿ÛÂ~÷œi&ÜgKœãø«œÙ2ý,Ämà\ek> Y#Ýh eÚbÚh|À/fj¯‘xÝ9Îfø7çFغ“×b÷Õ&ÞqžÊm#c³ÎÂXC¯’ýð{ÜŠòz0õˆ<Ñ|91Ðj¸–²XKF$Ììž.<غ­C-qŽãÕw™ÔgòÌ8P|·ásËÍùp´÷UXŸ` Ks‡lŠô÷†Ú;[ÆÜ©%ýú´]Pêº5΋ïc0.£ø¾êpü2 ¿å]({¸ý°|¸vraÝwYÒÎá|×X//¨œ=¥Ê¢=ZÒZ¿ùaA»paßžÕv]ع Æe_ÎcX`o·þt¶ôˇ’š‡™-žH¾•«v>äA‘»¦(µ$5˜`´Æ=»>ìý¨_íé})ÎûÙp/ÈÊ3¿mþò`g“’ WÕüM8×g‰{»ÝåWóÎCȋϕræÃÍm“¿[Ù:”WŸ\⯢åS·áç}P´'=të:ẳûËúrvÝÙºÕï8~XP„ûÉà¸jù>É`€žiÄkgCզÞP¶õ†Žƒ±¾×}Þøl³o??‹{VÏÄço48þ̸ ùÛæ^€ƒ‹è†J><öø©‰wiûÈsÚ¬vÞçÌÇïy¶+ü›}Zöþž½aïw-ñã,WðµÞ£ P7€ž´Áºýµkçéi$yöÁŸ"}`P Ùû´äà‹Ž¥S&„ ÜPö€½±áùâ¸#rŸË÷¿]ü}"á}-¬1ûÀ®4ò¹e·É«>øÀžóçŠòviI½Sµv—ô·Ö3Ûó &¡oæâSj³?hw±@vuCQÍ©q¡êúïšDçsëêiÄ3*†XN{„kÉ<ùš‰µÞ°>šÕ3Ö°sŽ6<_œ§\ãwå5’x(sgLºäx>ÌI˜¤M#©­Ž¯ºÒÃ~|Þ ëD·9UÊÿú1L¨ÿ¬c}tPŸ÷Ǧj[ÚìÇËpüw›g_2.Ї>&7MÂAìú®(-½šFúîuš6n–'œtuÖ!-)ñÉóHŸœ0ÂÞ?²yXü³ç96§y«Üùýî9ãó¤î*Ýé±)¶t]«úö|˜D´™—ÆŸƒ‘ÃQWIZñëÞˆYa„½dûólå±øù®Äñ§öŸµ¾k×Kp8§Ôq*J¯/ýý´§iDWaЖ¿‚&•sdçZ²uéEÁ'ë÷`yÇÞÛ3.¦%p\;•Í/Á/ýÊ ]ž-ºv®Vé}© rùàÊ~prþ²‹>ǵ¤Qý‰E“­÷™Å[G²ºv=³‰¢÷÷MmöÓt8ÏÉ‚ÚÏVM'05Ã8¦"ö'3ó½_"g½ƒÏ­è93¿;¼WKæ ” N ú·¬¯bñÄ­3šrÏ?oòÔ>ΕuðÛ ‡ã5§çC­¥çS7”O'EæÀèÒÇ»Áú‹Z{Fh‰[ííÞ–[¿«l_Ðö|q3›ó>vñØÇ5ÜyþÕ\”þÐñ÷ñùYóòíé$æñºç‹Út n6]K2ÞöÉih}Þ³ûÌÖìz¹í+Ù/¥ÊÕAƒ;öak]ò!¯Ö„Nú&édH‡} “ë¸Âdº”§%ßß=–ðñˆõùÏ/Wâx}žYP¯l|ˆ²ì]c\Ïœ|ürËtbt›ßi[/¸œá4¡Î}-)å×Ô·ÞÍoûrv_Y~Ý«4ÙËô““Íu×à<ÍËvnï”Óž›—«•µïnß´¯S:ÙæA _(§þPy)ÎÓÓØÂ»R¢µ¾²qqòOS*Û^oŽKwùÍI€wË*¿~X&ÎÐõê™NòÿHN}×Zw1%þW-‘Y Xã“ýeë_qþšpÜÃ/²s Ð}hÚ/£¾äÁ{ÍøGƒÓɰô¤l8÷U?׬%O]醎u=Çž¿¬¿µÄ÷¥Yå ”ü™©oc·Vx™UgG}öM'ŠNÛª}èÎ]ªR.†øÛÝë½f³u[¼î‹×?R÷Î Ïú³Û'BœBqkùÃÿ¨Äq“Ûšw›á±ËÙòï¯åÁŒ_÷}Y’NÊôXÛ¢ÿÙ>°D¦i?Ù3†·K6F¬ òƒå[²óìâxÓàøòCÝÏW­uðaåÐýRüÐekÜpU:y_ö¼;™Ø†ùÌo; †Ü\ÓC»¡¦5_Øu[gBÿ7‹­ëÁìÕ²‰m@ܧépž-Þ½ï7tÊ_V6õ9•O‘Æ·¶¥“Œi V'|êËÿ>&†pç­÷•õ;âø3áxq®tCë2|ZÛqÔƒ]yÙeðÓóÓIgç1ê—~ Ùײ¨Œ°Nf}6{¿ÃÖljW~©R‘ïß Ö;Ëï.ƒK+Eš*J|®Óe¢6ü®piæÜk +UvÀnïò“ìÝÂ÷)aB½cùÍí«Ýê6«{âë.Åy.M™ØæíexÙ²g?YH¸uò¹1ç Ö)ŸÀ¶‰MÝ!äÉ¢J ³bÈí[Û×zI„ëΞsì\¢x.ÃqoÊÒ_’Á¸¨ËÎ.΃úU¦ ÊÈJ'³·>øe͘°þáè·ÊÂõ£aBž³¿ìùÆ®7÷½êóû9¯¹xÇy¸ýÊ+°mÓï½ïÎ΃—·N9þò$,]ùô¼¦žüïQbHPÓnJ¾üö}-ëcØûv_,qãwH›;ï ¼YÓ}Àù‰yм÷îò)Êk˜«/ð€–-æïþm_ É›±bæ6×°oÖ ì¾²ëÃîÛw´Ä?Îó$ÀUjØzr“wGMó̓©õ4OûUÎ >˯,9$Öº°Ä{k áÞdž Ïi¶^cç ÄõV‡ãné¾ksø+àvoaýó`M» .]ëg%¥"êöÍN–Æ0†ìp=üÕ)뾫ì¹Éêƒ%î ;}6/8z¢wÇ<¸üàS©í-2>Œ~ëЧ/Îwk+Ãë>ýaSÌkÇâ”{bûîì9$®vºÙÛ,wùZI´¡íüVò`R¬Ó›„v䀃¾éúî2诗í})†Ø]k~sÑš,NY±ç‡%îq\Ë[fY\ª=V›Rëûý‰%7tÉ ; iïr 3ì©Þ«ÜèD¼Þp÷¨w3k¿Âžol=ÅòŒåµÍ¾>Γ–ê¸ÙodÐ]‹>äÂxõèž\3HÒ¦ÊmÏkZ•2íá²f›ùµ{?kßÂâˆÝW¶ÂöQ-ñã/´DHû9¶|| ©¶´gI¼´³ÏÊe`²wl•©1ĹõðÉaSÃ…ç«k¬fq#>'¦ÄñKîÒI‚Ž-èŠ'ZMìúãï½3HXfÒÙ„mMàª*  ^§ŽÛF…/ÿÙú^B܇jpœÎ‡}Þ™U* L»Ÿ ÷*8ffôÏ «7;Ívuj®óW|Î>‡÷qC”üΨðoî#{žŠÏßép\õ´îÆñg“ H¾·é†C¹PË;d\ÝÁÄ•*¹mìÏ Ñ’ŸÇœ³2œ°÷ÀB>ò}ëßÅëŽß¯ïŽ¡§o'Ávýõ2­7ä«úý<ê¹g®Æý~è íêÝš$‹út'l¿Ÿí£°ºÃ}þ¯\OÀøx¼/<Î.®ô–ÀºE¹°rl8æ“Ažv\ÓkoW“u¥äØ4ëóˆ=‡Øó•­÷Xž²º#¾þRœçcÚ€$óçs¡â½û¯&Í åw…w{á ƒÑ‡~Ù|-/† Þ»À.Ìú|eñÎ~Äú$V?Å×K†ó¼ïñô¤o2Ìw4wóˆ\ørpbR³À "{}7Þ­R/¨¯_8åííâ~ñZîA…µf÷Áöw¦üyQ·FDgÝõùÉPõðäÚƒsaÎÞ8×r³2H™ëí6©ÑìëÑ7’1ä÷>ÜöO·Þ.ÿŸ Ÿ[|ý•8.÷~&’þÚ5«Îè± ƒÌ$üÒ£8µ½5päË’µþ~½ŸçZ?/ËŸŒÔ»ö=?ÿî*>'¡Áq+·:½óÅ¡d8»lÂ-rar·Q=”äËÆÏš¶sµ)}cBc‹å{ð¼¶9߆ãyÐò|!2ƒø.Ÿê2o['pÒÍßÞyh,yõ´^©ÓÙÖþ™ëG¯ qÁÖƒ¬ïý3ÏÔ¿}VþöYùwòY±ã¯å:ºð~Uö©Œ_Ä8Žzž_ÊûªÈx¿¿Bž‡Ë¼R)Ë1Jä­¢1q™_*ó÷ñ©™¿ óý3‹xpb6®#ï¡bäYF WŽQ-ƒ?÷þ»†þ]Cÿj¨=ÿ)N&âñ<#‘OU ÊÀûT)Q¦27Jä;mâ=ª¢QØ ”±ןgâŠYp2Þsš¾¯ÈëOUŒãíÀûSQ2ž‡Ëøzž¿¡,Æ¿dþùÔ/U.bÀŠpj>‰y¯TGL¦PžÿÆ|¦¥¼Ï4ó÷£¼LÆ §¼L™”ó÷£ü7¹ˆ»ÄûL3?š„Œ»Á|¦)wƒrp5|búóJ5ñ£âüÌâ*ž½Á<þ$"?ÆÞ` 8ÏÞ  8Ê÷¦*â9šÌçrà¢Q˜ø Þ›Ÿ±4™×óœ1q•]8)æQÅ<¤¢Š15ERÔ‡JéÊqqÝ€ËúïÏjèŸÕÏ¿Z7éífõ²x­¤·è?S#ÿj}üGµñ?ª‹Åk"­‡ÿQ-ü«uÖÀ¿Rÿþ«µO\÷þ«5ïñwY­£uÎÿï™å~»ð(òÔ§^¦ÔÃÔç³1è¨rç[!ò/µÿL6ê³g¨dõ×óåyÞø!‚PFžçÅû”ñ~ÏÎ"_½h”U‰2‹ø•ž¿Æ¸”½F=žõ<·’²‚ä<÷ƒy;‹}ôä&” =Šö}R+kÍ̳?Š{ç÷r–ò\_æAʳ(ˆ2ºu(G£[†É¢áÆÿOüò¢y¿<Æqù8ûó|5ê=ªà=ð"¿<“Èw”r)å˜pq"¿<æã¬±}™÷¨‚çQ6· &¤†gsûólnê•íúwÿöwÿöïß¿Iøï`¢÷BÄ0Rð #ê{…rÀÀ BQÎÀ¡(sŽç-òk¦\#9uJ‚­D™Š±|y–¯˜¡æÆ{5ÛWæ˜!z”3ïwo±Ê)‹R‰2¡ÜxŽ/ã…x^Hh1%ó»§Þ¢¾"vZˆ¦á½îƒx_QgL&ÏMcþÌŽ¼?3ãS%ãS%åGI9nš¯ˆ¢àý™e˜€Q¼?3ã„0fÊ ¡üÞ(”=&¦1_Q3ÏL*Ρ¤üsÏ?wá=š)+Ä“XÇ{4‡¢Ì"Vc§yVe§™QrLò(>Ñ)‹2 åÐã§Å¡$˜øJÞ÷žñ(Í(¹È«9HÄòeÞrÊXâ=Få"PƤtá™”fžÊûˆÊ‹ú惘ÒúI릸fŠëeñZùêä_©‘ÿ;=œ¸.þ³šX¼þ³øÕ¿eíû³º÷¯èߊ×;ZçŠ×8©÷,vÀ R”à˜å2 &MIŽY$ò¤×ðÁEÙHŒ·«ù(G—ãxåÔÞ„rÃÀ‹±vyn›ï™l¤Œr Ä"Z×0%Œ ”‰g’;ðü6#ÏíPó‡’b†¢ kZù”wÊó:4|àR_dÏ}¤Œ#_žÑÁü éúk–ë•å†rÀ £r>ÈŽ<›Ã>eäybßcGž“kD¹ð#7LjòÅõ(gž/î†I…²ÇD ä=¥X“BQ…(9&OJ"bq¸ˆ<yΛ„÷ާLG%ʈ’a=2£Ü0Ñ¢ùdóEéPL:¥ÈóX#b䯡¤¼¼£ˆ-.äŒêÎ1Å)ÿrqéimÅßûp÷qvÿþ}œ#ÿ)ÿM.â)y~õ‹FI0p(ÊX…*,Ãñp)ÿMŠÁʳ‹|1¨u()v(Ê\Œ…ijpÅì79Ïwà™” ï_(bŠSnQ(ÊŒ’ó\ÆÛ0ò¼ U1Ž%óˆ/¢ü7óM!b¾Eñþð ”å‚ɤæyoþ˜Tz”3&–Jħf4JÂsßt()&~(ïÏ™…´~b!ˆCIyöcáªPE¼{_ |y†ãfšy¯x5Ï—ñ~ðE¼&å§3>¦¯?”ãºQ¾°åÈsÝÌ(9&`J‚I¨@Q2LÆ(”}3Ž/‡’`b¡ (gLPªˆç1F&ãQæz(Ï]wÃäâ8e@9c"«QE(9&tœˆífF¹ñl·"ú®“<åОceÆ¡¤<ßMrÄÄW¡L"^f­¥Xt(GžñÆXÃj¾0ø£t({ÊvãÙDŒ™IÙDn<›ˆ2Ûݰp¨ùâá\^Ð¥†ÒúùWëå?zçûêäŸÕÇ¿RÿjbñzøWjá?«ƒ¬þý³Ú§´ûóýBZëh+^ãþ£úFëZñšÆÞí²ZF¯+e­I1XBKpìt9MtIŽN™•fº~.Í1Õ Q¾@ºr#]…*Dùb0Åñ|_#ÏRsÔ¤X§”(3Êë”e5*ˆžõÃ,tàxèq()¢bÊ®Qz”3¥šLÊžtæYiE<+-šg¥)P&sÒ7ç¤9c«ù¾?JrÄ€V¡ Q¾Ø:”ƒ;e’rœ45ªˆç¤E£$ðJ” ƒ^ƒ²ÇÀB¹ðÌ^3Ê “@Í7 ”5©CI±Q2žwNć’`¢8c¢¨Qþ˜,z”#Ö%ÊÌ3Ñ¢9š#&‘™gJªPfú—ž7áY’(Ê«å‹É-âòêQÎX_T]9–¹.%á9æWŽÃëˆÉ \¼Ðÿ}Ù¿¢'û»ûÿ·sãç(¢× Træy‘…(_ VÊ‘Ö0T!Ê WÃ/åîêQÎå8v9 ä@”3³UTÞ–·«äy»ÎàjTÊŸg–K1Ø(Ï,§AÏxåÎüjTeîòœ])&B(ÊŒ’ñ¼r1R†u. eI„2¢œyÆ®%¯Ã±Ê¥˜8¡(Ê ( eI„2¢d˜L§œ2!)§œ1!ýQ:”C#ޝkD9c²©P…(_L:JЉ§D™Pn˜€Ñ(ž¯«CI1(#Êëš_,ÉE\H%ÊTŒU.ǤFÙc⡌(L` ŸÄ¾(JŠÉŠ*DÉ1©£ùÄDéPž ©C9b¢+P”3&¼eñ!iò¢ô(g,¡"Ö®e!¥G9`aDéEŒÈ"” E_,ä(«•ãHãŸþûe-û²nÒZùWë$­Åkãöýî?ª‡¬²þŒÕ>zÏÅõŽÖ¹¿ZãÄ5~w½?<ªK܃(®$ÇE¢äPq( ª@”žçߪPE´¶aéËqq5ªå§+ƾuãÙ·E( BÊ‘žÇC¢dQ( JJrÄàT¡Š8†¸åÈsoÍ(9l4=_ŒAk@¹`àjPö¼A<;Ü…¾ËåÙ‡’`@+Qf”; å€Á„2 \0È5({ ô@”匯FÑu&¾åˆÁ¯Áà÷ÅàCI1BQf”&BÊ“A2ÒsÄô 1&…==“‡ÒÓóØ&”Ï÷Ç$Ñ¡¤˜( ”å‚ £á“&e çN0y QrL 8”“HrÆD ¥¿µ ¿³À„*¢gLèï+0©œ1©T¨¢Î§Ö bÈQ.<﻾ƒ ïkQÒ—–ºç¨þÉ»†ÿëÍ¿²Wû»OûŸíÓŠ÷h¾üÿŸf Ê€Rc€¡ü1Hõ(g Tª%Ç:…²ÇÀ DP.å8–·=qÊ€rÁ`ÖðˆÒ¡¤Ø¡(#Ê\Ãy ÏñvÄ`W¢Ì<Ç;e/âx»`hø$DéQŽ˜ *T!Êgx;`b(P&”&H4Ê“D2¡\0YT¨BZÛêpünGLÊŒŠÆrÀR L(7L¤(·ÛØ€ãvkPöRŽÛ­GI0Á(ÊM*BùcÂéQŽ˜t¡(3JŽÉ‡’`¡ô(GLD%Ê„’aBjPöô·e¨8”“3e.ÆîöÅdC9`Â*P&”ŒgwÛÓß–¡ô(GLbªå‹É‡²Ç„BéQRLì ”åŒ ®DQ.˜èT!Ê >eI„2 \0ùU(3JŽE 倅 e@I° ¡ (g, j¾8ø¢¢é_z.E ‚€Ë >$ÿ-ú³ÿîÞLf÷¯]Çþgj#­…Ö“ý£zGëܵ÷¢õªx­úgëIúYõôúÒŇ?J‡rÄ Q¡ŠP¾,:”&e@9cà¨ùà DP.D>Qz”3T(ÊŒ’c`iøà DéQÎôìÊ -%Á`S¢ (g :5x(=ÊP…*Dùb ÆÑsÁŒF” 2 å€A©@Q2 Î(z†ƒ3Uˆò¥5 %Á@U Œ(lʃ6eD¹`ðjøDPÎÈ*” ƒ9Šh”åH*DÉ1È£Q t%ÊD÷òéY v=ï‹o¦ëEºNÄ€7¢dôQ({ ü z„®1øu()&€å‚I ¢¿¿¢¿½â“!þæ ÂBÍ'…“ÂHŸJû*LŒ"º¿ŽÉ¡§gri/…’bBQ.X'ÔÀýgÿŸ5ÀýîJÉÿïùó…üyhà~»Åz7öÛ-vö7ˆÿý«‹è·[2ѺÑÜïhÙbzö„ý;ƹ#ØÉ d‡"³š[§¹Ú׎랕#øä¼u°YÂö Âüã9¿Šî‚_Æ€ªu¼_ö‹%Ì¿à~?Y`p"æ¹ùã<ïÆÅ»; N­¯Ã—\MÎIƒëeæìÏ )ɳK4(×\Þ=;7xH, :µ·l†s8a>Œ‡ÅüE˜o‘?ŽßêÆqtx¯bSLL–I2Egêw3ßMh8nK¯ÿ>&–Zèoõ;aõ“d\Îïä©àÍ8¬bÞ…çÙãì˜8;œúùèãý9ðK•…Ž^Ê kÏ8y¡Íø¥ï6E9E,iö9¾Fƒa„qw˜ï lj¸%øJ1þ¨Øw\‡ópþ9)}õû·ër`³Sí¸är¨ñ¶-CÁÜ®cù»ªXrÀùŬƒåÈ-/ê[ßD1Ä„ãç\ot8dc 8øvÑêçå€Å6?ƒôk¨v«¼d0ïÛK:äÕüÿNa¾]Ì—…ã7¾¨b?s»Ë²¾ï•>˜›–Pgþ€ŠE¥ŸfÝ^š Wk¸Á‚‚OyH,yÕäɶ«ÃȉIxòƒà;Ä|†™?†˜Ë#ÅñÕ‰Á³ãS`h³5¯Èr`Êëý/£ßf†Mu¬©ÑÆuÜÝ)9–´ëv÷°’ùÁYù`ÌŽù‡0ß-±¿‡ çqjóa÷§;)ðGïöÃN´È³žmxô%ƒ”Ý<ýe×–®U7&ñM|,áøñÖûÀ¾Gü”48õ‰p¿™¿‘اÉ礪*¿g¯âF:9°bFæÉ:e2É@Ÿá™eƵ‡ª–þ%ÏÆ‹}ŽËG‚­<ï/ƒãNÿãLÃzX}÷þ½år Æ€#fÚgÎG¸1LW èw31Vð­a÷—û¼W„ëÏò…ó_ùÃ2¾Çw·æé¡½Ç‹Ã;Þ¡©ÿ­vÊgÎo° ,»ëµ?ÑK6t¹eÜ2ÍZX½a§ÌÇņ‚óLŠ+±»a?=<È»PvÉ#ÔL:zlz…L²`åš·Ÿ¾s=ð4–4_Óôáó9á„ùI±ëÎiÂqC*.(ÝÙC_ _ÿœm nÇe~5ŽÇŽ-ˆ%çw{ªv¯³úú0ÁטÿüŒ;-öÕµ»R óè}dŒædª‘d„Çyöª˜IÖ\¸}3lF‘ë‹¥ºO¾KžTlôøó2«»>ì>0®(ãSXòÇw^¹ãý×YzXñоŽþŒrooÛ¡ú.“”8öä¶ß˜0ªÑ±äjY?K?;Ômiõq*î×ÇâŠóy«+ø—[òç©&™'‰ÐC}óžØJûŒðUÝÖ¿I•L¸í| •™±D~ùlôŒ‹aßpИß%ãf2¾² /çYf?êyZ#´x·êT·ê™dØ„rƒœæv€áíì¼fýK¾Œ§$é0ÁW“ù³øe|WbžŠç ®ãœ£‡NóÖ<=¾ÜN¯?î«Q'“|-ßÚ;Ó¿+XðŸcɻ {í‡}ã§Ïü+Y½e>Ôbßu Îct¯ç™=„¸ú&¯ša„˜ìëS;I3ɧåK–éồã?T:K¸SGjëucó1Ÿ@ö}˜ß®8Ît8¥™Þ+s˜TÜ2Ü[>ŸÏØÙ<“dd«ûÖèÞUg´;òÝY"µÜx«¿$óMcuÜ÷iÌ·Þ’78ÏŽ{e/•\…íª,ÉÜ^F(·æË÷#[g’Fû†4L½Þ ÜL¬¢-y–4óî×x·5ž·ŒùùÝm°R¹¸|G>ßsù’„ýÂßµœ®ÂL§øNuŒP±fºámûLr©¹´s¥ííÀ9iLå³dkU:‚Õ7˜Å׿ ÏóZÿ‹½ó€j*ëú>öXp2Vì± ±klìˆ-b‹Ø+¶;6d¬Q@# âØ2Ö`ÅŽ-‰OTDÀ–@€Ð$vK[ìß¾¹÷$ñ)ßó¾ï·æû>]ë¿Xó¬õœ“Ü»ÿûîsîÉþåʬ3êt…s3æß4§ëœggÁÒ9‡¼atqË C•LpòØGMc±‘´óñžø“´%øÞØù‡ŸXC¾\ðDëÒþ™.öx};-+üê›<{žYsÖZÅåA' œ›opž.íZô3=ò¿ôî2Ëš–ĺŸëu1Ú—ù~^³œ1S4d zEÙAÚ'÷‹'½n”ÇLóÛ_¸û߆õ ÎsÏ«îåµ›!·¦©Ùåûéà.ÌèÕ¢›‘<÷Šm6^Sâ?l—)ä{jZwÐ8 yŒöAcóDg }#m¾ÁyØ¾Š‰·ä·bï é Û5ùfeOÌ76”#+Ô=°TCxÅ>Tj&ް×A4®Ù~ùÏì\–‡Öh\Û|ƒóì~ü°Wcs"ìˆÞ®ê•t» R|#yÒùöiÉ €iÊé2Gó^åùÐç{…Äĸc+……û±âø=ãnšä’ôÞî“—+,ùÔ´§‘ìi‘ÞA°³=¼>ñhsþL ©¨Ê}?{¬ƒ3R¸ÿ¥Å^Ÿdt›¶Kí3Û…õ ÎÓžÁu¸'Á­¹y;v§ƒk̳2zo#I$'¹-H¯Â4ÎÕØû§íÇLŸg´o`öÊýçå[¶{Ö7ñù’5–Ö+ãû'öYç¡C~O‡¡åDaCdã듇{‡7ylÒÔÍwþìªuÔÁ´_8­'ÊF¼uí?kT¿˜×\Úv8ëßeòO’Às롾–UéPŽ_ªí?#‰õ!pô.)¼î|?ÒuŸ†ô }öøþìð¿ðT¿î:í×­ï[;Ç`t—Á•*M-ÏÌÛV*s ëœ'zÓ ; W9ÞçÞ¢tDLOÏiäêÑ~Ð9ò­ñÙ ùµ²Ÿï×%޾jt]@ó$í[)¹º½}иÉðúqÀ±w1#X¿àÁ¯;3È òU¦,6’ ’¯-GŸô†/• Ò>h4äñÑA‡‡Û×%t>Ú'Ž~¶O¥/¤ûwÒ÷˜Ùõ ÎÓáè¡å.$AŽáÛÜ–#Ó!Â#f݃ÕFRkÀšåm#zC½o'}5hHøÁºSF…º.aýlïz¨5Óq® |[Ä\X6¿¨pü¿×+hž– MÓØ#¦lèXnäïF¼4´ZÃÑ^Àr}4¤]Å·bÚ‡Z_Ñz†öÓ£ñE¹Q´>°ùçá?Õœ(ñ$ bÖó¡E:Ìzœ¿öì6#×O´°<9 Ù¼¼å‰Ñxÿ麃Ö´*]O8sIÍ8~©©ñ{ž>O‚øK“\®–·\.ö¼oãru„>›ÕÚx 9Ùβ·æ;GüR¿Ó|ÿ½> .×ó%Æž»×·¹Ÿ™÷kîNüš;Õ5m9`$%®e\Ú&4éѵ'5d§wiy³Y޼ÅöwÖØó"½?μ ޝ½ùô”Ò”#Ç5:ó( £KÚ6’Ú¥_HÝÂ;\?7@«!ÉÃÎ_¸½ÊQÓõí§Iûu:sÁ$8þ¦vËûOOI%ñzœ”£æ·}qÔHº•nÉÉiµ.žH MÒ6žù}R­1ÞÅË÷¤}Ñ)÷Çùþúáø¶åTzì8p§¢öx|ì|~ÔÞFò>øÆüw`ºà‹’5¤d‚þd°w„}½3wEÕ‚'îf{œ²õX=nÝËrå8þðòÏ=6$'­ËwdDTüᔑÀŠËËKþÔÒ~Ù€,ŽÏO¯7å2ع½6îwÂ}éq¶ßDZMƒ¥î‘•.¨±ÞÞÒÒp¦5 tm·{úzXk’2)Þ±N§ùŠÆÝ ûÎ}Au8mÕ«O‚ñ“gIƒrC¼/%œ7’ë[ÂÚè·Óî¸'c4äóç÷æ³ýyŠæwÊ)ºþ)´^Çyf>Ž4z%&AZñÅý÷µKƒš«ÚÝQ#y¸ù[fù9Lú¡ïY q½§œ<¨¸ƒoG¹”´žûWÚ%!_òû±VKÆ%Á“kkNVIƒpUZFûx#aº†g7í;¾¬˜›u ëÆ±é­M®áö>þ4^©/J~bèRÕ¾>²ùÇ÷_´æd’kw„v{aI…>º6òb·D÷5²¡W‚'œûx²ï²8Gþ£y‰í»›aÿütŸ ŸÇgëƒ$ˆ¸´²ÝDC*ˆc~=^'ÍHü‚Üç-èW'RK[bAÒÞpB×4²õˆ£'ïĘÌßf\ÝSõÎg`Œ›êKÌB=bŸÔjñ>ÇHŒ¶ð“ËÕ#›ËãǾã¢}ÅÙï)„ÌÇñoBÜë²>Áyz3ÛH¿ìz2ŽI ¶ÈIÆîE#Û ‘À¬*¿Lâ<ç3ê 5/èsÖ­ÈþL–ý¾ÐúÚ™[¯Âyºî.ööWcüöøQ̧u©p~¹!¬Ê+#y”P%vçºîpg³ Ô]Pi×/•ý›ißfz_¨ß)Ðæ¿Ò•½kâsé—ìå“:ÏO…gMÞo¹õÞH<gfÖìn.¹Îté ðrô]¦õõåµ:ï+šqüÛƒû\OMH‚¹UÃs« O…}ÕÏ~æ’Jüy¿>4O¥l\C:¥„¼h2Ñ1~á~úqöúæ[ç>ï.‰ù’Óg†Ì;w9 l8±Ž©pƒðë—M%Ì,|±4ó}0£sq-9º®öÓ Žõ5­iŸqšO¾·_#Ày0'[Ú\M‘­±l*,tÏ­r*y9êXå­zqÜ ±ä<^»ñŠcýFù_E9æôsØ|‚ã×ë¶~^¯\kfÛ~ÅSaÖqÙÈYõRI¥M.†÷4{£ß°ngðÃ&Ç÷ Y^ož}ýNù_Îû*~8O»=g:ZÑÔK×Ì7BÅûçŽ=h•JÚìž¾ñ¤ nXßר^QKZØ@Ý4Ï?´×ÎûrÏ»I¥þíO'Á’”7ÅÒp¿y€¿¼{*¹PóÙå—¡C`…@²j]-Ù]'`ŒGCÇzÆSÊ]wÎO*_Ÿ\uFC¬Ù>îFÐæöž=fx*Ù2úÚýŽ¡Ã@ªø³TËFZ’þim`¶Å‘ŸhcZoÒõ­3ŸT‡ãk7*°2O‚Ë$Áv¡Q˜žJØ:{(ËyWÜTK:ÎŒŸªhæ¸î´þ§¼AÊ'¥û6àø6iV+mhT´~y¡éfKÈØå©¤³lé,ODKæ<\‘Ð`€#nèó‡æ ç}%—¤|IÚ±]ïvLÝ/Þ[6ÊŒP&¡ëÖ‘©¤àr¼ìÐÌ è½ßeˆ–<ªÀ…\šOéu¡×ݹÞàø‡føù¥J” 8tRd„,]è!»RÉ¥ŸŽº;C ÑåhɸîÛNúwÿKÿqº^¡×¥×Çh2¥LôÛ°+#$Çÿ<½àP*ùèZÙÕÇÒæ^}u`T –À£JƇÙï+}þÒý[ê+ú|s~ÿà‡óì<·:äÕ—DØýÔ=¾Í—hôÓî;Y§SI©S${õ€³Cï® ©%¿ha©šfÏCô¹@ýD}@ë çë%Çy\Ãkñg"Hm —8f®”%ŽM%^ ûî¨:¸xÙ²ZröàæJáç {Ló„s^Uá<ãÊÜ®183ú,p ½“RçajLåöU{ËAÑ’O£g^ûzÊÁå ×®WWÅWî¾cuC Ü^›p|MÏVâ`’}÷lÜB“_¦Uäß4¤’à—Ù×z}ë™â -=KiI¥rý+GOÿRîÍÛlH¹P X_à<×Me[_>”÷^Í ŒJvsÆoÌÏN%%_mëÝ÷koˆ]¿·Ó©?5$z½®Þ²¯_ÓëD¿Ç¥}#û k Óÿ¨pýuSÖ7ò%[ãÿDЕ å‰CRà×!eG奒ý¦Ëd}àìL·R—hÈŠÍ=Âg~n±àeÛÐ×áN©(õf˜}A÷ ¨ïÙ¼V“õŽ»¡¬µýï%aßÁ„ú÷'cÞ½—“å™Fêzõsm8¯xÜÎ ñ*©%³ÿ5ÿ”1Ìžw ×g/ìõ}oàÌ-7ã°5¡ÔË{ Ù5¶é þõ°¿phN9²…úîãøÍxw×lM€³÷k•Û},kë:)ã­÷æê7 ièÑÝ5÷J¡õË߬õ±Ç3W46]?+*ÜØ=»ü–døÔíäÀͳÓHõN®|ÑD6€§†˜û´þ>Îqèõfë"“ýy'lãr »Ïì‡ãÞÏ @È`s—$CòñÁW§ñ§±»šÞécÿ¼ê> iÓ',§¦À“{ÊÛnVè}±Çç5oWP1ܱ¦SLJ†X×Åù®Š4r»·U8ó‚7tŸpa±ü¥c}Hy4ô¹I÷G =pÜ.×wWf]‡&¶s2ô=ÖýP™-i¤mÉQòŸ×‚øv)±ѧsÃ?œ‹jêÈŸôóÓýWqPÀå‰kò<÷µýyªqcû:Ûï88¤YûŪëPüb©Jè2ˆ>nLÕè4’s¿ùú¯} öA•:‰4öçfa¾F6÷~ï©g77”Ô°P}gÆñ=ƾ>|{Úuv$éRódHQ—µ!6ìá l0¥ôpÐ6ÜQíbžãú÷ÞróäÅ#u®kh>pòÌNãKÏTÞ—þ±³êªS]nåKÒ³~m×ð:ÇÃK†m¥¿nKM#]¯gí÷—øBnKK~µ`žXîuÝ)aöý6¿4²s¶ÙxýÓ“® ëkÎ3yî´)ÊÌxXáÅ h€7¡í;G¼L#­Z$ö“žýʼ:27 Ÿk^ð–jÆí'6ºîe÷E¯z¸·cÆXÑk;?±ùçáÙŒO-üøA ;°ÁÂK'™Ö1 „ ¿@É‘çWÎÄuû²Ãå\ãN­!ô{P.}?»õQ~›Ï›sç)Xï>=W ‹‡3Fã1À_žMëÚ ¼ôèÓËçò¸Ô6óp£SÂò„×Cvd¿’5úž•ægö>|´sîœy£rœ§¤í`F<´·o `Õ‹ÓÉ’ ÃÆµ†Â£ÔN¥'ÐÃüõq7RÖpï¯ë½Ï4dSçœ̽ÿÆqE|f'ì\ZÝo]—ýè9¼R»ËýÒIÎäšÑOÄ>0yvÓ'á±Rj›å½†ÐõÁë5¾¦¸6²çi6¿±ëMŽ›üÙãØ€‰× ±í…ŽD]R§ÇI'>õ~Ÿ>´@#Ê­}×å}æMz´õ²‚ÐçÓ$ß/½ÔÀ¾ç¼ÏcÆqË~Õü^§Ö5(»ÖïÁÇ…(»/îø‹™é¤ê†ñUgO美ÙÝ)_C~mðÛÑkìûo4ÏÑ:˜r8ãÒåv¾DùîÖ—Ôq jùlcöxÌ_öhGÛ¥é$Âz(ôÅÊþ0×ö"MC¾ät™Ö_±Æ¾MýEó3åÆÒõ³-îqü“Ñ ¬ðâàÒ¦€7€ºÔæâµ#ðº0¸gí` o›Okˆí?ë„‘Âû´¥¹}ÐOöç:»ÏZha‹{œgØÞ%6­Šƒ…{×K ðqÑÄc½·§“è±3ÏQ„I]½Ý»FiÈŸc϶¹·5̾Jëú\¤õ<}¾;¿O÷Ãy^]±uÐð8x¡½·0¥¥²f`w$ìî0¨æO£AæµÔ: ™×<$ÕŸcô~ÐóJ…ù«Õ ­÷å8¾¡ûÖ€¼–qðaJé‚u PM{¯vÍ édöüÜʼnüÁp8½eñ‰K5ÄP¢^pÕƒaö÷4¿ÒsEÏÉ:…ó𷜌Úþå*ôR},³¡‚„žŠãÃn¦SŒzôõWI­ÜFÏÓLê×ÌãÃìõ<ÍŸtÿž^7ºé¼¯Ãyòç ò¥^…È–7Ò'[õ0±Ý½3sÓɸ2óüî*†ûÞACˆ[=8PÚq_èu¡œzZÑï[ˆO„ó0‡ÌdÑWañGÞç{OôÐ)O3ÔûM:aèÍ£kƒ-#_uÌœ£!Ö}•ËØæð }î°ßç¬=ÎØuü'OZïÛür'_R_0ΫÔê«`ìÑî|°IÓ*]¬¿¶œ‰,zÖ緾áq»¾-š.Ô oû¸•n±Æ~Ý(¯‰¾¯¡qîÌ¥àøì~*~=ùW~º­¯‘õe뛈G¯sí–Ý7ÎU±ïÿÍ^Kšü«°_/úž€Þºßë\?Jpü¸3³_ž¨{†~={qn¬N<ײƒ‰Ž Éí>JgœZ2f•†l6–õo0CA ¯JÚσPßÓóp4þl>ÁyÜ™cbYW@"›•õ>ZØ8}MäýôÏ)#¢| QÚý!.þùcý¾ŽïAçcó¡Å~ßé~)}Ïbó γüSç€ÛŠ+0ö­ü·„Mz¨›®¯1ÊDZÉÞX÷QWªX·…ÄT¶Ú~Ýh<Óó—´^t~Ëqü™Å¢ÞZ&ë tìòJ»»èA?þÒE—O&²ËÅXW¢ÛüÄ颲¶¬ÇÖ¡3WÛï?ýK¹„t?ù{ûû*œ‡_ÿøŽØ¶:8–Ù¾þ1üK ¿åUË c¯)O‚=Wë6H4¤Ný¿~XE ?7tœÏó¾{¾S‡ã×¼âqcWIÇSÖCØT Oë‘A®|0žô~ú hOflxÖECRo¼j÷GÁª¿ðÉéû)šçÙ¸«n_ió Γ[õò·á§ì˜zåÝFœGºoæç5ÞÄÈ`¸}G@Àèø®í4dÛ™CçûtXm_ÓüEǧëçõŸ‹!_’a#ò`9‹zeꮼôË =;D¶êpl( 1¾xÞ\CEüyóÆøÕöý:ÎÇ)ë ÏÍó =ç8»¸Aúçç5 ôð«öÀ££ó3ˆa3oè7<ˆ?PßXCzw'½ÅjûzÞWúÞ¨ð~ãB÷]‚óàCkTÐØKðeOIL1zˆî™Ðznh9îÑÃ³ÇÆAp«sbYC4=®N0žuÜ—¢õÊöo#êtJnZh?Þǟ׬†Ëam,ˆ§[.5«®Ññâñ=7e³Y)‹&¹õçÎhÈ‹ ¬¹Þ«ìëNêzž^/šW Õ_8Ï‚n%‡WŒv[]7ôu¦VÝ“AÖ]=îþÎ\Ž4ìÓ¹æÉ‹›võ_eßߦ߇õaœý9Oýî\w«pž0—ƒ –]qÕC"5_óŸ´jõñDéóð¦ó“úÃ¥7Ü{®&Ÿ~_[fÔóUöø¢×žû£÷¥Ð9)¿Ÿg`uaÓ‹À¦M=$?n¿u)ƒ¸ÄžŸŸ³ •üy}SÕ¤ö™õ~Zºš>‡“nßߤû’Îç#Í8þóO‘üG8¾$ÖA‘âš·3Hsì ª ^wkøsÍËj²úË.߃I«ÿÂßfÏ8ÎÕÓ¼å|?\’ó%¶í²E iÐá¥1®‚|®Ü¾›A’ê+éœ*Ÿ'u;«&Ï]¹0:Îẞ¥ßƒú¼Ð{nŸ½¾ç!j@ÅÝóÑmBÝ>”¶dK·· #†M«j†6:­& Wj¹ Òász¾žŸ ëWç} ŽßažtZÔ¤óÐÅgé“êîzx{±Vùr%2ÉÔ9ñ¢&þCàC‹;Áp|fµÎÕö}$ö|O²ýúÐóÎïüpüa9 ·y|,W»{mj­‡¡Ý?õÙ\=“l®9oðƒ¡Ð§N /¨IZrWõ_xðì9–óöïQ”kióÎ#<[ÝØÿ|Üö蔡“^E­3‰[d’W׸3|(ôª,œðóU5ùÍŠ_eÿô¹AŸôü‡ó¾… ÇÏX\ÐkñE-ôå—{uÓÃÊC¿¦ùH2I£1&xÓwÌ©¿ÞÜ;IM„KÔ+®²Ç+õÝ£÷»fõŠ!“Š5,T7èpž_&½è©…ع±3xéaMlé¶£‡d’§í[|¾#óvßTͽG µŸ‡¢é~õwÛi—¼=›}¯jóÎxîÅâ ¬+ÚýÞz8óécü’É™¤‡—÷žqƒaë쥾³j{÷KÁØP{ýKÿ=GÎþ^¡9ÐóÊ6¤äK.¼<&í/Ó@í¦£½Ày3@Ó…™äJ÷ˆúÞ×dГ¨—}¼£&;ßháu2”Ð÷‡m?=·¶qç¯Ù÷ ϶úL Ý-ÖwÇ»ëaýñ–[óVgÏ?'\ìþ“ ’tÅZŽA_»ßª—’Ùn•}½N÷õèúàQ¯Ææti ûX6?àø·êú—ZuT ešì7 ôñ­ ŠP™I.ìëâ)±G£&•5jr®ZKIÖ´U„î{ÑødïC;Uþ´þöTöÜ©ŽËþ·’¢ÛD\ÀÏ­½xXèLÒyïûô®¾2ˆû_«‰âgæ€Ã*ûõf?oŠý½»Ùnõ¨éÎÆ?ŽÿÎÏgÉ/5dÍf~y¡‡¬´ Ë$“4~0oå`x0¸Ù½²±ècÛ‹¶PBëX'ì¹’6Üú‚}ߥÂq;ן¯w¯­†ê½~ZRa¸†|?.¸™I¦”bN¶öý¿šÌ)ž25§t¨}ÿ…þeë€çöë^«û¨æö}P[Üã<+ŸÜ½ê~ý,¬O*ñëå5ÃÅå÷3É W—˜ÍÍGÀ¦»/½»©&dbÐó)'Cìç¦éï^èó™úø£_Zç3Zƒó{3Îs>–yñv>x ÜgêAÕ¢~\ç™diþÕE{‡ûÂérÛ‹µÂïsâùÊËí†úü§¾Í~ýyêÞÎ-þžËçÆ|‰¼ü©ÉΟr•{Ÿ>µ×aão¿X)‹D”ë~:ÒÝ £º^|¢S“ “†¿ èb_ßÓ?éùLöy ,T_JpžÒZÁ¨-'N»¡‡›ÇswwÏ"é_.e6ÎêêVnzKMØs!öx¥ß‹Öeô¾Ó÷Îï»üpž£_yWåî§áØô3µó¢ôp²Ê¬]ëGd‘Kð*ýxÍaP¡xóÕÕ2Ô¤å ãèY¢¿ÄÝO õûoZh_AŽóäêöÞpö°ù_ WϾ¸ez™>rÞ†ˆÇ>peOƒMpÛ± ¿B?'­÷éûNz_œë~Ž?¹cÛ†€Spªe‰Û>§°ÞxµóþÊ¥Y$ðe QÍ<T´ì ½¡& \Ÿ°}(«böó¸ô~°õ`óB÷C‡ã?T¿Î)ôÔïËî«zèë£è ^›EŽŸñ¨U6c Og^¨É¨ÆW –”v\'¶ȰÇ»žiQø< Ži­:]›p&ÇgyŸ¸¥‡Å{-Ù™E”µ¦´ºÑfä”c~§&us=º¾ ¶OÇ¡qõÄgô°wc<€®¿mþHÍ—<²ômR©ÿI8­ÙôúÚ]=Ì\SL³ñXqÙöþe¯i2ؾ þýµýj²î†öЊÇÁ„úšÞGv¿(Ǔó·v׈õÎsbáò‹#ŽÅ€²Õïgz0•5–éx)‹7úRfx0¼¯ÛªÓËMjÂT;y­÷ž»¡÷aœ{½ å7µöœ›g%8þûÒãpè(Ùyc‡aïõ èº±ÏŠ;Y¤ãÀŽVõé˘7sj’É£[ò—}\šÇÙqÛ}ióŽ_Ùv é´¨8eYû2ý¨lÈÎûY$6×µâö™C`sœuŠš\ݡŠú;Ý“}¯wϳ7Vi 'wäžÓÜóÇ {Ø|Å&Ó1X^Õ|áV-,ØÝÒÃü.‹xšo¬Û5c(|r¸gÆ(5éÓ5ãVÄ2Gü=gÈÖÛb¨ú%ê&éìuWáø]߮8 £Gº®YÜÂ)í_ ›–Ï&ÇÇ7wz5ª2Û\#ÔäY‰Ä.«Û„z>™ÞOæ´zÇ©¾±Å=Ž»÷˜wƒO‚#ðU^¦4ßÓ¡=fXêf“½Ã»•šL†€1MáW{´š|®Ùwe@©ûóž~þ¢çé™·Ç­ÂZz~šqÃHÃc±æ<lý>q°Þš¸9¹m6ùùi¥©âap]UÙ«Ì5)ûA5åô3G|²õÖm{ž£þu¾>.iù’ª>öŒ†sgÌ›h­ñ½þ4›<œ/Tñî¾µož¦&ªªn§Î§÷ìunÿütÿ®ÐïõpüKU×”f=qÏ'gMn€òÙ׎É&̯(7íòf³²',S“.bÓv·ßƒIÑz‘Öïô¼°-Þq܇—R;Ïétf»ÎØì7¯ËÓ)ª¼€l2`RxßF>{/Ðß7BM {·¶?/é}¥çt ½§ÀqkÝvƦý°äÕlýØ¥´{ýpÛÊlR»YýÓ÷ÈNùÝ_¯&Ó{Š*¶ªLXß=éý¤¿Ûv^·ÊqÜ Ì¶QÞ>H,ÉD„F~.6íÚïÙdÿOŷθ7æ©VgvÀÏ[·Ú´¯×ÛýÉÆc’'­?éó® mqÎ\çËß5ìµüë5·ÎT Á±ˆ¨÷QÙ¤qµ†OÓÂ-øØx@°šà¢uaó~!9—E÷Á躃½^õ ­7t8ϼ¯—¹¹ ÎúÑØdöopýd6Ù1]UÐS7&‰wÝ‘ã<{ uân‡Ø÷ñÙësÙ“ÞWv^ÇïÎp~ôΔ»ü}z2Ñ^'.Ü÷vîqò½þrfŽ Í-í/÷zœ(zœèQâ"ÜCçž™Îý˜îC$Ç}¥ý˜þ÷•öc9ñÂüŠp(3ŒöÍôwâå|¯y×7SÂõͤ=™hßLÚ¿Ü—ãòê±}O˜s´OpžÀÁ­¦¼œ<Ž{¨ü'½O¤/Ç­HO¦hŽ+Àõ ¦ìǬ¦ÌC•Sïò„"½Ëû1í™IÙ° [LR„w¨ãx‡Aï°hßrÚ/SɱÅ|9¶˜Çcz«Hº8ú«8÷¦=V„ž,«ÚìɲªäʹRîò÷Ë•<î{é™û‡Á©úý ò8NN ÇÉ¡ý Ä¼*8eBI¸~P< æ” %)Âsî]ÇãxŠz®wº’c*RFvQ®b ÇV¤Œl±#Ç¿H/uÊÈ¡½ëœXßë1,çz×1=†cœxÙ´í1ìÇ±Åøh¬€zl(¦'Ó#Ê"p0b)ëÁ±ÅT(+ÊͧE Ѐ TLjÕr=†)?›2NíÕI{«ó9>,åŠE;õÖé/ìÌÑ.Ú»Î͉“#-ÂKà˜brŽ)V´·0í[§âÌïÇ1r#'ë ¥â‚s_O¦¿”‰ë/Åpaó8.,ã—ž'™üÈäÆÿé¼X4þ+ì¿Ãý^Þ£XšãþÓüöÏ8°ÿ*§Ígιì_ñ¾œ9LÞ*š³˜|U4WѾuÎùˆ¹—Î=4‹ö9âØ†.ÿfßLÇn5q|.¦ëC§G‰1ˆTÜCÖŸcS 9&u4Šô3Xþ‡Z\„]È0¨Í\Ï9Úë÷{}~{Ï™QR®ïÓßWRíƒIy1‚ú,³ÕR¤¿/ÃÚÒ§¦Çoe¸ 2ÌnLo_”YèàÃp| Çn¥l­¢ý}E˜'”ÜŸéA'Ä€W ¤{aÁq,-%ÇÐbøY´Ç/íEçϱ¢)'ZËõ°dúW2œÊa•ÿ¨~ÔG.ÏúÈûÜ&æþ`pF£x þ¨”Uù^åA(J‚Áâc¡Ì()r4ŠÁ„2£¤EXXf”<šãMq¼i Ç–aÞï0µC²§%N¬™€"½Ê)kF‹rãzûRÃ÷zû2  J†Ò:q¨óP2§þ¾þ Ë „2Õc{h*QVƒ½J VŽ…ÍÏ¥C Ñ€‘Lïrw–½ªãúûFrŒjÊd¸3´·&íeîÆqW)+Æ©·¯©Ho_gFµ’3²JÇqFV„¥çX ŽU´¯¯ %á‚<Ž!˜À1#¹Þ™2LÑ(^WG¯N¡SïLq‘þ›ŒW˜ÿ*WÒ<ùŸæHšs#Í‹4þïöîer “÷þYÎûwò“ë¾—ã¾—ßþUnûŸÊkr6Ÿ9ç²T19Ë9_9×GÎ9j/w/TÌ=Æ@™Pââ…û»ql> ÇaúŒ L( H4ŠA„2¡$e¬û ŽoͬÕ0ÿ˜P "ÇáS` å1ûXLnLr”%Á R¡xLo^ŽõìÌÝcÏLO^†ƒ sûko^-³þœ’‡’aðÅ Ü0ƒœúò2+†qOy. Û>’cWÑ^¼ ·Š2î-(_®G¸€c‘2_ `-³'…AœÇqô(¿%ˆã·H9©ÇBø^¯^†… Æ€W¡xô~¨†£‡Á/ãx-rŽ_Åp«h¿^ЇfàXÌÎfæS–Ábá,LoÁ:éGäò÷¬“„Üç23׃3ÅÇ @éQb TÕwúŠËQf”ƒ7å†,Gå¡dÈ1(7 fyY£Þ™SŰ–¥N¬e9ÇZ–r<ÇZ¦<gŸŽcñQÖ²”㹸¡!‚Šô§LJ€&‘;1”(+ÊM“€q +Ê ¤sâ-[8Þr Êã 0¬*†Q/G™Qb^+c2Ê ¥Æp «ŠaÔóÐxþ¨” ¨DYÝYf(ÃAq|>†·Lù|B4¦eázk9>½Â‰SÅ0ÜаrŽ EãÆ øEXË*ìJà˜}LrK–Mį̈2qŒªHŽQ%C³kQ|4|ÊŒ’r ?>ÇðÓs ?¦Ç¸µ3Ë¥§=Æý¹ãLŸòHT×cœá„2¿(d8¡ô]Ð\ù6WþÈ“ÿ8O2÷“a€ó1(ƒPf”ƒSŨ?Çòp,?3ÇòcX1| Ú ”%ÅàA¹aËQf”´¬ƒIï̽ŠAñ1°ƒPf”<ÅçX~z”ƒ=eAÉ0èµ(¾•‡’¢¢Q|4AÇU.Êó£\åHTJЉAñ˜ÜˆJ@ Ñ,‘(+ÊM£C Ð8 ”å‹Ò¢h"9ÊÌìqì+†_Êpé£9cQ^ Ã¥W:1P"Ž…Åpé#ÑtVfM‰ÆÓ¡„ÃÔÊð•Ñ„:”¨@Y„,Ërbä'FÆ1L.Ãm0£¤hÔÍ„2¡$hÚho´?J¡#Q”/ÇSp• ÙT(^C–«Å0èùh¼”%Fª82ìS=JÜ„åº8ñEhÌH”•ák¡Au>Ò‰©¥C а TJ†ÆÕ¢ÜаŸ£Q|–ÿ¬çX‚J†yÓ†eÏ;ó´ÌOKÉñ´|Ñì:”^ŽÊCÉ0ˆ´'ˆãâ0Ì—î¼åÆ1kL³F‰²pÜy—$n ÃÌ¿ÿî\ùŸæÉÿîùïäÇ¿{n,šsâ5þ§¹æA&~Šæ?•Ë_ss½cPnhrTJŠâaÐpeAIx­,óžòT¥h¶h çßeÞ»¡ñ‚P&”„á¢xOÕ„’4a† 7šr ÅhL%gN?TÇ»WrFõC% „hØH”勯ա­ ó£cœøÑ&Žu¨âLͰîõ(1š[ÊCI8Žª•É“hö” ¯@YP¾h|˜e Ê"J‚I Åã8÷:”€a"¢Ì( &ÊÚåÜGs¼B†£ÊÄ(óÏ9WþÈ“ÿýyòGnü繑¹ŽZ”Q’a0Æ øÌ¹8”¾˘V¢,(_ R-J€ª@YP¾°:”ƒV² |1xu(!p$Ê‚òÅ@Ö¡Ì ”å‹A­E 8¾´%ÁWqAî‡J@‰0Ø•(+ʃ^‹`à+8¾´ ƒrCÈøÒJ”勦СÜÐ(JŒQ¡xh”%B³(9Ãø£P"4N$Ê‚’¡¢9ù¡´(¾¯Ê$p°\ùh¬”‰ù=šK…â¡ÁPz”¦BñÐl(=J„¦SrÆóC% „hÀH”•ɉhĔ͉² |Ñ”Z”©@å¡dhP-J€& B™Q4«Š3¬?Ç”1gí8óÊP1(Ç€Õ¡„hæHTJ†¦Ö¢hlÇ“–rš%eFIÑ4Ñ(>' ãšÇ1®P"4“•‡£©”(+J&@ÿ¡¹ä¨<” MƒrC£ P4œeFIÑx1(>š/eFIÑ„1(>1eBIÐ*MéÒ£ÄhNЇõGéQ"4ªeEù¡aPÂÖ,ÛÊq±µN\l3J‚FŽFñÐÌ(J‚¦ŽDYPR4w4gp”%D£G¢¬(?4|JÈ1©¶µÍƒâc@% „˜ä¨<”B4—|Q1(æ—ÎAÀÆ"ó¯hŽü1729QâòïçC&2yðï\þßó˜ë¡cî'Y$ʼÁ@“£L(1œ eeÞ`ŽÓ¡„˜ã"QV”cJ„‰²¢ü00P" N%ÊŠòà M@ 1P#QV&çaÀêPBæ, *%ÅàFñ0€ýQz”YųJ‡bPG¢¬(_ n-J€®@å¡dè*.Øý˜óÈð øhƒ>eB‰1øU( ¥G‰ÑVæl óÛ,4ƒ›¯ÊŒ’¢)bPnhŒ ”™ù=š#ÅGƒ¡L( %ÅG³¡L(1šF…â¡qüQz” ¤äLäÒ£Dh&%ÊʬÑT:”‰² |Ñ`:æs>%E£E£xh¶”%FÓ©P<æ]0J‹â£P (Q‰² |Ñ:”9?ˆ² dhÎ „2¡ÄÌYB”%Å܃âcî @%0ç ÑÄA(J‚ùKì¹9÷[VQ‘óÇ1ÀæH1w6ÇÂýÆ‹žÍ àÎæˆÎ0K¸õ4óEìÚ\Áý6„9óHÿ=ìÊþ5§åKª1m)'îµ÷;áÙþì}\2Ɉm®fÚoöxø†¬G?ûØù.ÍN¹f¯PÚ¿3¦Þ„àÐŽ>F¶‰Òó%â*ž£Ê”Ù ®oÞê¸Çª>)1&-›Ì¾|¬ZÛ:Ãà|_¦sšx•ÛåuÜlïçCûv°}ÞØûjÐþ{Îýý8OåóýîUìeß_[sÂ+¾>IÝŸŸMøu+Mô¼6zM|ñjÆz5QüºkôA°½Ÿ /ò¥Çž¯qrý/ïsýƒjî‹ó¼ê\ëÏ;öÀæ .òµ¨±ßÒKP"‡,3dñRGÀÃLgU5ù©‚`‰Ð°ÒÞç„þ¥\*vÜ:…úxøáøƒ<»©'ë¢ ¸úÒRSã ­:íÏNµsȨ†+ç<õ…®—b§ë·ªIé)–÷á+íýGh?RÚσö¥tæ#Êqü]É—?Âê(®o£š ^,—·Ï!w7kŸ6€.>Ý÷Ù¬&=;•ã—rô¢ýÍh¢îÚâåÞ¾©_¨/ž ÇgºØUFÁtëóÀºCg¦-é—CÜŽÌíÝ©ÛP¸ï}óôïjòmäØsw;íëqÞ“r|œù‘:—í×¥‚é;_v-0@™O›CTÃ.—{y0¬ÿÔD´SMæV{wæê8GßÚ‡tæ öõ–ÜzoïDù4”OeóÎóÄårínËvÃTðýÏ*„gçÝâ=½—¬Êé†âgŽ©I\7X»8ø/ý¿iŸ6Ú‡ì{×ÉÅ”/á»ü3hä.èôrSÅ´oÈßìÿ`iI49h€þÀöÁR“ƒ~?þLJ/èõ¢œ:ÚOòû pž-gPÊN‘÷(¨T2d]êóåmXÙÑgZï#ߤ0'qÎÁù1jÂt­¼<4ØÞ¯ö)a¿Ç{ÿ@ÚÑ™ç ÁyÜêwÍOØ åV^×,tM†®.Ýü [rHy¾ßs·S½ Š_lµl¿šðn_ܼ’ãºÑþ'”åÌwôÃqmø‘º;¡7Ó¦Ü-¶½ñ©•CŒgD1m›õ‚K«ÌÚˆíxÖŒí}2{¥ý:QÓ>1´ÿ%Ë›nXˆK$Çyfÿì–_K¹R7;ØÍ=æw-ÿ„Í!ŠÐ‘)ô[[½j²~.C„Yi/:{ÿíš§l¾ÀñmÝítÛÁçlNÉÜvÉPÙCÞo”&‡h>ªê4Ö÷³ÿuW‚ßãà¨@ÅrÓJRø>—ê;¶ßU£B×I‡ã{5®Ñ©üâm•ÖW2\ÙžyéùÕÒåÝ—Gûº÷ƒ¯çü‚6©I£Wµ“dU¾¦œ3š—X‹;Ð~46_àøÉ×—HTmÿ€¿ö†š“¡Â¦*¾.)9Ä\#¡bh3/j2©qõëð˯I·÷/üùAûÏŽ6†ñmlUÖù’>ÇDKOÔWBÝð™>ßd¸¿?´ñÃ2´ø„J­† ÂÍ–5×—;ØžW[Ï—_è³ÁÁ¯~Ö½{Ó‘çšê¿.Àñþ^LVk+0ÇÕÓ“áÌ»Ê/{—C$¯ïÆJ]} iM`UŸej²=ÐãÖüÁ„r h¾¸ªê¿7ñšÐα·Å=Ž›?lÒ|I¥-Ðÿó`ã‹“¡ãA«¼»dÕMËÇ&•} ûëN /ŒU“NÊòñqOï/Ëi½ã©ÿè¶u”ÙÞ×4½jÛ+YÁÜsç9ÖߺÙÝMÐêh­Wë"’áľƒ;ÔºK*&=«àÕÚD&׬j“Õd¾WžªŸØÑgˆå~ezÒëDûì;÷]–ãølÞÜ7¦0 Ód8r¿Må“-ï’KM]·ûÀ ÑëF©Éº Ïž¶ê¯ý’hßxz½ ÷µc¹*œÇeöŠ’×Öo€ñmu¥2N%C艡;Hî¶Ï§tæu™ŠÏ‡,Ÿ2¿uS†y>dÚ¹l´>pæœè˜ïñY65îæ:Ø;ÿé¸ß“’aL©'^Û}î’-ZTΘàOÏ…ï©R“e5ß?îØ?„æÖ=iÜâBงt¹jܲx«hqF2ìÒ5»µcÒ]r*ó3y–0Ÿ•ù¡äÚ?zÆš÷™Æ?OZ±üxŽO™/©$»Çóiº r¯}šóéQ2¼å¯})¼K4§Ot• Ó>¾ ýŸ}6d‰>˜ü#­‹œû7 püâãú¶çÛí^'CìñóS×­¾K<¬Ïþ¨’:j«ºN€Ïý÷Ç»[ôÉá_Ú÷²¨U5âÊ~î•“käucý€ó´­5é÷ §å`kÏÿ-Ê÷v_P|Û]r[‘|×\~Ì™3tÖ¬¢²úÕÜ»ÀQ/ÒûLû‘ÓçZý⣼o „?˜r%°ëœgÜóXëžnË JUåXvùXwþàøãw‰~òµ­æ ƒ{[?þ6dƒš¸2xÊQŽûL9¿ýÖyÄ&¨6ëƒÔ{‘”õŽ+°5\¡15ûùÖJŒöÓ;÷¿v—´¼ùdE;ž/”I»ºuš\,yÐ3ò¢#Ñú‹>gVW)Ñh›d0<8Ô§fvZoö9€ãŸi0mÏ>¿Á¥·ãc7OUOöO˜x÷.9ûdíËÅ­껋ûu vÔ©ô>Óx¤~¶?÷ÏWLÝ4i@¡ë£ÃyºÚ(·gÇ™©)0ëyÃbgÞß%W–\]ÇÇy–ôÒŒMÁy²lUãyF¹´Ôoà z?ÞRd{\JØçŽ?ºV³øÛ æÉô-7z§€{Õt¯L~.‘m_Ùûã×pƒý¼v­šØÚ ×sô᣾ \SÚ­ÃzËKïÄú"+_tNñ*îèà/êpÄsD ÄÍzz_Þ$—$œÛbSÁPÔhì.5a¾}å%Žºžò)ϤY‡Ç âê7Êw²ùÇgûìM„î–FNNåY•©»ä×ã­ ¥ÌOùuǪh5y·xÂÎf¹Á‰Wö9q«ç[ÚùN6?àøkž[qlh\÷ ‚–¤ÀÞñ{oõÏ%vÌÞ3îvÚÕ쀚\Øúé¡PâÈ«´#Íw´Ÿ)Ë7°>`>î µ[gMŸm·ˆ”)°{óO;ÖÊ%lŸÚ~Ç´ëź˜íCò õ}¾âgáø|Û‚c è`¿÷¦Ø˜¸©ÂþíSsÉ›ÚÏ£¯uéýê\ÿ óƼ=cB7ívÂòãÆåÏ…‰ÿ‹½óŽŠúÚö8±b‹Q°;¬{ìcÃQŠˆ"ceË(–1Q™¨ F1h,ccÃ2㨉;¢QP¡(CS  IÔ¼}æwÎðƒøî{÷¾»ÖÍ[KÖú®ü‘¬ýûÍùí½Ï>%ûóu§ÂŸ»¦€ý&µ4}c6©{£ßµ ËÜ 6Fk“ubÁ¼¨W[ûÀóu¯ÃxÍû'Zâ!«H:uôŒ±¦!óÁ¦×” J¯ð¹¥Oy°'›lš;É.³íXkŸÇ%™!z¯]mí—ÏÇKøÞW¬õ¼Ðç¹y™þË|Žs•6Æ °{°8NíZ•ïnÈ&‹ Ç,^7iLM¿Ñü@ˆä¿íù>ýÛÕÖ~ðeyDéÖß#䝯eùíøœ}ÕéË›çÿôç·)ÐtÃתNײÉáAÁǃòÇÁ­áÏù}m /mç¾´ß©Ð÷?ÛÚï_X õží&W¡D°Ц—bü†ý)mœÝ Þ½l{ŸgŒðð€F¦öçsUòxS“Üf†Òqâß[X7$Yó9_W•áásä= ª?_0…õ­O—;ÿ¹âËl2ÉýòoÒï=Á‚Gšc Ú¹Úø>¿­þ ƒ×e|^åëQ1'A‹Ïq§å½\ëç{ï˜)¶ü(9hSÉLN‘xŽìx~6Îß½tÙ'ÄšGøïáuÿ|_@Ü73Ÿs¶ÍžU¶)À‚Q}žëU¶«m&ó¦ÊÜÚÌõ„õ›kLjh }"‚R’ç‡XŸŸø:…¯ÃÅ<3>Ç‚ÿûi ´X[}ë슩°eõù¢G Ìä÷èÏ;ôô€ß^š‡ßGÙ2mt»£!åúV—r18gG\ÏÚÜ+’Ѷü}§Â‡¶Áo×5Lí¯ä³Û-ͤæÏ'¿oÙÐFÛ<õó•â{¿ñ‘Ô¦¡åòb~Þµ íVø!¨x”r*äøvÎíÓ5*¹ÙÔöêd&gL'ÇÙ¢¿ìòµ«7Ht}ÿšA+C­ãÃëþÞ¼¾/ÓÏí?Nz´Ô¥þtXŽIX34.½󺧙\p©øEEwÐþòº+; d{Ûí'*¯ý‹ßò:–׃œ.î#­ÀçLØûjT—®3a±œ’ÍRá̲cc¤fòfXÙ^ÝF§?ªº맪êÛfÔ %¼ï9¾o%~ Úµí_¥ƒ›~&(iZ? ,7MÖ¹}/ÄZñûõ!¿ÝŸs•øz=õ†OuÙO)Ÿ#Ãê­þvh:ûX¦L*ìuqù,g²™ /¸WEŸõ`áÊß7õò µÖ|?‹÷ÙåùU¼.5£ýîuzöýyÜLxp4út7]*®å§r4a]>–Ðå(·\S}bCB ÿ¾e×e8P6÷‹¤!ɇf7{9„:5Ʋ^]P›ÉÏGd-¬7 –{›‹ëõƒý7q eû¼w­~Ãû‹ãV‚vã bŠæ|9úßÚÑóÇ;©PgKÝ€¾af’ñd÷»c“`š­Íº~˜¯MÖß}$„Ío§­}ßùü-^ÿHÑ®ÀÙžóF,=¼ú¾ï!Ú3™èðÞûöD¨ñ¤ëúÛ³K¹º9ÛÊsàó5ÿ¾?G»7¾¦ä¥i°¿Ï˜¾þ5î¼ݓ©w›IëJ,ñ*OG\žg W;ý¹G\©Ÿðñ(Ëu«Yæ½5hÜŠqOï¼P‚ÌRÈÝ…‘þõÇŒ3“K…“oôùu<ô¥+¤R?äõ5ïGÍë/>¯ˆ¹‡Z´ÑØhpà@PÌ Í}ò]ÌnËÏšI>[«WXä•,ÀJö!JýOÈ÷Ç­ïÏó ÿ÷ÿFû<ðõYP÷Tõ©ÚÀ»ðóÔñ{î\2“ é¤U¶+ÇZë•þ¯<03Ô:>¼~çuÏÃâ85£ý@ÅÁJ‘gAïcaoŽ­¼ GÛ×ùÅLÜ›:ëÜΙœÖÅT+3”ðþî|}ÃçCqþ²ÉƺaÚÍ/ß´„g73æ8l¹ {ËÂÖ§˜ÉÎ1PqHØHxµ¦ïð33 Dm)†Y÷59Gœ;Ÿ×Åœ Úx·0 ã½ZéïBã”ôV[2ÍDÐöyøÑ‘Ы†WJœ?\öþä50Œp¿ãõ(w~>bñs´ûÓæ­³ •qýñ!·ïÂãÃ\.äšÉí56ûL ·ç¬™„󞋪*þ«0kÞå0>b>—íútn´íW÷ÙàóÊ­rÖ¯wáÅ®êðØL„¸qƒ}CñÓ¯6±0Âý˜×iü}y~*ÙFû–eÐ…yP{ÐÁ _Ú§Au|×—f²$-=:x¦ò.|ÝQõà.ŸÁö¬~ÂÏ x}þ±ùH‹öûþîwdH ,=Ô A~ç4hšxðæåßͤ¨iú¤·ÆÂÙÍÉ߆ºˆÀ_ #å9›âý²x´÷¥e`>äö¾RÏaX<¬íx§bÙ?J’Y8nŒ9ä^ðõP\W”Ô¿»6Ìʇá~Ç9<â}3ÚMÌýÐòÎÎ øüõÙ¸Ó á £»g­â¶/ÞXÝÑ ßô‘ïïk ½ªíí²zkáü%>¾üÜç‹?›‹¤IÃÌÿL¹‚w7qy77 ·ìš3Ô!‡\îÛaÖeÐï[zµ]o9¿ÐçÔ€§a„sÇy=Á× âü'A»îKŽV ºÛñÅÊ4Èöskx²yyàKA—0ýPÏ?¢Ý¬7ýnß<¿r.œÅÑ.Ý]³RÁŽŠИžŠ/Ôó;æXù¢r›Ë ßõÂuN÷!š;gíþÀ÷8„sÁ8gÔâÏfΙ™ö«ú^Ø™o:Ý Úá’C®4hêUw‡ôj-i“3Ð@Ïi4t@q8áëp>ñó;q?{ Úµ`7R¡eÈñâ×ú4¸yç¨sÈK&øÆzÁ‰–©KVŒ5`ÃÌ]Ö¾?ËÇÁ‚¿üµ,/[‹vÕÉã£gv €Y¿KèŸ%°ø—-n9¤eµš»*‡¤ɹQS dêIzÐë<À¿cy~™¸_~<Ú¯FqÔu”à³µëji`fßÍ|rÈ®¿!;ã½`ÿ¸·Ð>¥Áe­XcoÞ¿žŸwˆÏMÍhWà²Ï„;²¬iÒ [ë§;M3rHÅýáMҒƃÍÚÛ{ƒ‡ÈW êѺô½9Ï‚Ÿóóq½`“S$þ;èb¨¦ŸÏÒ¹CäŸÞ©v5'À£Û¤UL;qíâ¶L¿;ÜúÞœ#À¿'ß·.“§Ñ¾P÷Ï„¯ts.:Ùš@7z@ËåÁ9äÝ»·fýȉPeø¯äfò*{UZç9áÖù‘ÿοáë^÷”©Ëñ9¯ÃÖ׸i~î`ÏožZ›C\š^ p;í þÕ·¥™ZÈ À#œÅýk>áue®ÚMò+¬³ûáL¨« ãdç,ûǃ·åó¿Ý[ÛWƸ¹?õéf èñÅ©Ò8ªŸÔ?gýSëüËý\<¿kÐþ‘)ôdm6¬]Ñ}k7Äœ?tÐ÷ú»öûN'OWÀ-ÖÙ@®ì‹(ù¾t|8Ÿœ_ñºW‹v…|°\G^[ðÅ\Uu:ŸC| M™2NµkêÚÂ@vHkëqÜy^ö/[¹¿ÿF{£M+º] [ ÷wLž0ÓšuŸ?åN!O­:½r*ÿ¹þ«ûŽŽUDvh¸Õùü%ŽG3ÚÛC>ƒ¡ÂpÓ© áè+6^]SCšîè‘?X1,˜T‰û^á„sø8/Vœlr‹¤Æë>|ø ŒûqòŠý&H®k®<ñM1÷<’áF^éú]?¬ÃlèÎë¹<ç+òzƒÏÛexÍhê\r¯‚Æ–ž V¸ÊÂ_VÎ%ƒO¼«žgô…«”*+7‚)ô„¯4Nø<È羿&¼ÿ{‹})ÚoP±Èu…n5èr&z»Ü7Á­›'v¯´Ï%KªŒè|z"Ôë58këYû%ÐhÍ_â„çm¾.û§[ÏÙwçL­™=£Vš_˜`Ih¥‡›ç¿çÔG}&Àåyò®r¸¾iZ‹½k¬~Q>òïÉýÝâÏhñï|÷ÈCáV·HùìÊé°ñîê µ:ça=8®5«Ù÷mSYfÛò‚D÷Ò:^<hÑÞÌáš->†PhH3Òáe»‰Áûzå’óÓ_={Ô ZÖÚûýøYëµÉ½äö”þÂø—«§Ñ^RëÆ_E†…dÐÕÏÛ§ƒnÒ÷Iu‡ä’naÏvO_åöUf4ÝÙ@:U“»_¿Q.Ÿ>´®«ËÔhׯÝQ)yžý“k:,ú¾îy.is*k̲@/ø)¦uïë dÓ“í¥¢«ðzšóvãöï;2Zد´É+’^ß^©çCóòe:4ôq¯61—Ü–Å,°éÙ¿ÇÝ×Õ3âÈ‘ž¹¬>ˆëϹt<_Š9^´[)!éØªQÑbÏ\¿tí‘xåþÌ\²ìhàe™Ô îÌnvþžÓ…ßnˆ |?×sâu„í xšh(ÌpHª±$Ò:í?P}A.)ÌñóZÓ Ò'&þ6õ¹žLÈ žÞ{aáóŸ?øúDœ×h·úÛÄŸ<ÓÖ³}ìtá½–å’¤Kí7öiá ?Œ_äU㉞LuKj6&‚ðóžwÅu¬íEÎ~øÇßAìyÇõN'ÓÁ=|ý<›°\bÁÚy@­î;‚Né‰PçEXëC>Oˆëz-ÚóÜsÁ40zT´LdéPtôx¼è\ÒëvÇ¥Ü!(Ã}¾ŸPïD¾¯Áó6ÿÞây3íf’ã·÷Øo„w¶¦¦Ãn `Ÿ\²´ë}mÕpwp?RÜÙá…žH, ³Hk]Èy«Ü_y=/Þ3£}Jí›wj#¼YAh:Ø?m¿çÝž\2Òò€³ÕÛÁ7z2w\²÷}"­þÊëp^òýõ2÷Êò‹¤‚¦Ö~:-²Rí÷àyhQ¿ŽGs‰úÑõ%1>PQÑwUè=qkß00Ø?’ëí#ÖïÆß[Ì– Ý„µ¤Æ€fÈŠ÷v2 ÄµsÅé\2ëYšû” ñíÓ®’Œh•|ÝiS$áë=þý¸Ÿ•á·¢Ýçë¢Rü¡k¯ø²kT­”~ñb.éPwÅ.žÐ¶dߣ äÅ©~¦ž´Îó|^âãÁ×gâsÚŸóí}U³Ó1àþpì‹àaàýõºoæ’õ;ÏÅW­>·°¼¤uôg §J¿'ñþ¹í½[“Ú#&tACÚ5÷΀œšVtÍÌ%›lX¾sý^£.z—©'ª¢¹ÛGGZç k”lõ~ÏC|£EûÂyR $v¥›Æ£Fæ’„Ç^·†?Ÿ[ÔtçQOŽïüáý7 "­÷z8‡¯ í ã³ J.ïm?g~Ì©Ÿ2©îÛ\2îÑÜ€j“¡¾4©'Y;§t}ìÃýáœõ»ñûfâýp3Ú^›`ßËÅ«ï¬Ì€uRzŸ­’Gî'=÷z±ÓÏèIFJ@+aýôÐšÏøzRÌS·)(’ÎæÚIü=÷z2`ò„Ù­òˆýˆz_7ýN \z$-»©'¯.4I„ïWò÷Þ·AY1ÚM¼–v&%¾ïóåyÕ‘ ØDÛæ‘‡‹še®Ýìžï-Ö¦ë‰×Ýž—óvEÎíãëI¾~*ÃçF»i¿º¾âÞfØà3èÀÏPƒâ{æ‘^ºÏqÉà vý7*¯åéIü“í};¦”ο|^~£2ûD ´{r}Ѿi[àK·A9·2àþ€aUì‡ä‘}aã±ôðŸáÎí,Ö›ž’“z•úÏw¼¾ê’Æeê Úßrӭʰk?@ƺA’c®ÚŽ>56t¶ÿýÇ}=ÆÃ‚ÐÜu.¿éɸio´öŒ´Öo<yýñà ­ÄØºêá^ Úw®3o[ðÙ­ .®[5éyXxn“òˆóÍÕ×ow¿)çßkªÈ7·j·\=,’ç»?Yã„×;â{'ñhצ‘]ó#[·Áä¼·=ßãw|=öÝÍy¤þع=5 v×¢§50ùMݽ——\ë<І–%o;–½×…ö¿‰s¨nÚ’' ·UÏ„“ý b;ÎÍ#Á§ æ-–¥ÇÙȬCï·=MŽ$œ³ÉÏíSmé´+ðu«Å¯à°9eÁ¯^ZóŽlw…¶™ñX]Áî!¢}ÓÝ1NøíP¢ÇdŸ+7´t?™Gšzиñ@³Ã,´ï¨~ÖoÒ‚(ë<'œû<¶æK¡néZ–‡ö{6ù,«ãä=pIýËÁÞ3¡Vµ›·.’<£ý¨ó‚`åƒk=*Hç„MEÝ¢ØþÍsë¸Xüó!úƒ¡ýš'«öÀÕ.?}—:"žŒÎLÌ#¿]¶ÉϾà ùgÚè¿ÀyþTëçDYãŠï{ðºÒâ§h¯dŒ‹Ü¼m¼Þž8k̵LxQ{âWÝSñ{U¾Ñí«V°¹ã3èÖÄ@ÚMúöÂk§¨¿¬ùýv‹¿¢½ÀiOVoÞ³7D\ÉÈ΄˜~î¿ÞÏ#[Ï zó kxô²ãR÷m|³"­÷wøüÃÏ/øý—qixÔk_f_Wñó^÷B•­gÀ›Lö¡É›‡ydc¾½z¥÷X0¼éïRÅ@Î,¸9Ê9Êz߉ïKó{ <¿”©‡Ñþò–gü}wï…íRS;¯ZY0zN¥~]ŠóHÈìÚ]ígÊÁ‚Oµ5„åå.(~¾Ãç#Îõø1ÚµŸ¼Ò½™l4s®SU< Zv²$Tî;°ëE9|ÖµšÃK´S#&¸ÓÑÒ÷æy‘ïç-œ<í{Ýý²œàx´ß¬Ëž‹ƒ÷CË‚9 C´›ûY>ivÛ~߯ec!õ¦³~ïç8.óé ]êÇ<ÿð¼!ìo–å)›Ñ¾»mþÚêàù••´£²à¥ûÌêêùÄûIÄÂ- wp ,>×ÞÁ@‚ÃkÕw»Íß¿ô\]ðöeÖÇ6…EÒ'™Ã—87Š¿œžê—ƒµöùäZë]Oz¶:{>vWï3{iÞàëzÎ[ŸsIÐ.&£Û]€N­ —eÁ¬E¿€§$Ÿ|¨Mox{Ã/‡öjNþ©';â.ULÜWúùïæë‡|Ý(ö)>ç—Ä7Íçþ¼nGÌsZ–5<¾1¹S>‘^»£[Üwd7¢;¨zBéé{æDYýžïƒXïß³ý6ñºTö›ôÑ]ZuúD î’ƒ‡Ž=öuß|âãÔ?Ï?ÁÂ3èÆ…žœI§`àH«ßð¼ÇïcñýÈ2ûhÿ¤Íœƒü2Ñ t‘YðlÙ–gI#óI“/lCÆ]ž †í´ÿ¡žÌjpà­‡[i½ÈÏGø©Cªzìñ÷ƒâYn“§W(]ÿ—~¿˜Ï—ó.£w4)óãñ9v¿ô=½Oz\Ó«<“nË‚¾O{ÏÈ'‰¶M é óÞ˜¸ó˜ªuŸž^DXϧøwå÷ÓÄãbF»ýï­: g“éEò,x”«ÙŸ¡Î'ú¼ýÈh•óó—ÒÖyÛÝ9òÒqçëžo§Cä^Ù¹e¾«Í£"©åz˜G£ÇÞNG0žÖ•œ9’OF^¸um«Ú wGmºˆPFZLJÿ‚oê½xØÚlõË2ÿÚï»ã³×3SâàÝ®8“­æk“ý×ç“7Î.7'yAHUú?6Ⱦv¶Þ¿iÝÿåãÍÏù¾™ÅßÑnꤹÎ)SŽÁs:‹ÆgÁŸFïC}·æ“Öi¦ñ› wô­k ¦¦{®ŽüË9 Ï ÿF{†íÍŸxr „ûn8q¯µÝ—OL*L[^Ñ(u{T‰žŒ·\¬ŠüËynN/Úxó?]§‡~KÎüz) úuìâ1áh>qíúãšWc¡À°üÛß‹ôä÷vë*ÝTêÏ#Ì’ÀñÝ’­u"_¯‰×?Z´ï³½êÿ§Çað¬yÅ!ײ ²çÕÓ£ù¤Õ4ýoÁ©caENL«[¸nÒvÝ søëüÀçM~¯ÀâÇhwˆeCèì*NïíjÊ‚‹™÷¯U8ŸOVËBO¼%‡QwL“kHG§š‰Ý·D~Pæ|í,÷ž—RýÎ ðó­§»lÎÉ7[ôó®å“aö{¦9ž—Cïæ “Z76aKìú¸–Î3<~ù{òù€¯‡ÅëL›ÇEÒ:†$Å? ­‹T cžeÁŠv£7ÞLÎ'kÞ]ýç99>w÷TJ ¤ßm³ÁPeÝ/ã÷ºÄû´—pf{ᙓ°ûfÖ  o³ÀyïšÛ½2pþ:©é?_¦•gWmY:¿”!Ï]²ÖüÜY¼_"Åçd^ºöþó!z8}püúäÏîA»*²îßäæ“Z5ö|Õü޶œ  yÜßæÀè­¥ó$ÿ'?çáõúÇîs)ð9o–F½¼q][¦·L©yÜÿÇÞÇùärÍA[ü3Ç@ë¥Gž_` Ÿk³ßΛEž»åÛï[×|¿¶Ì¹6Ú-xqò]¼§Riúp¼ÇÖ%ù佫|“ÝÔÑ€É|™ß$Y^^¯~ùº-ÏzNPæ~Ú Ó]Ø‘ó‹Þ%~ªõ=XWàûZó[> ìÛ¢8¹ãe¹øh Ùsî»¶»QºÃÇûc÷øãÑîª ÝƒG‚·ëÖÚùô üÂ!óÏ|’ö6GÒ§‘±â¢ÄݧÀ¥gè|—{°*)²Udå°&&9áረ°¼Å˜ï äFñŽ¹ÚøÒ¸çû*¼.,¯ÜâïEEÒ÷õ·uÔß?kSèÿpu¾ùêË€sÕ ˆsÛZò»ƒáò¢¨ñwÄ««Ôz_7Šðº[È+9ýùýHñ9‡íZŽ1ªa‹Þ>ú ܃Õû‡nϨ]@š(¶¹×q0œ<’¼@†ñšvã™éQ„×SœW.æµKÑ^JÊqý9™®w:z ßó€AÚ¶]½rþanb÷×C¬÷¿ªûŸÚtMUºNàë;Ÿœ¯nñg´{Îys{Ç# I=Š+{ tÓ½hܨ€LoÞ»ÒºJ#`çôþ±òzÕÙÎŽQ„¿ÿ'çË‹÷ 4h7çÅSßUçŒp¯åôºN_Z_µEY’U|öÑ¦Ñ ÜÃ6œäqÁYºÏÁó ÷?~.oñg´{òa·Õî!-ìõÐÃÎ÷àìÐëß4mvOÈ—]Á|JOÓªœ1[o k½]IxÍýŒïSYüíÝѧ¸¡¤ÓIÉzræŒqDýXi®8Ö£Ÿ3œ¤"n9ï7è$b8y‹Øœ…¬G?íêb óhÃ\ÂXvRƱ£ýU)×WÝXø´—–D"°èœZ½ ½w×Òû”1?Ì(~DgÆÄ gL]Ί+qú™:3¦Žõ0µcì…¨·½”õ†¢ý¤ŒŸæûOó½Íß¾wd¿Ál#00Ŭðx”:m4c`Rf‘‘1‹8“²ÂµÌ™yŸf ëÓ\ȽÚr½H>Â-rñÂ]ÑùcYp¦3ë_/æ`:‰ú×ËD¼p¥ˆ‹*f½šµ¢^ÍFÖ«Y-by„³~¤¼‡=e†ËXϾß—ö$U°>öŽ"~‘´YÙž¤ZT±DèÙ¬eA¨d¬7Çr½ì)ȲÃÛÃÈú“ªLjòÃ5(3ëãLùfQO{ÊÆ¤ q- dc¿Q°Šñ1i`sþe+Y¯RÊÈTˆ¸êr ¸ÂrýJ•"N&çI׈r2]Eýþ87XÜãÞ‘±2+“2Å E=ÿxogʦy€þý_r(Í›«ãÏ$0î çÏÈ0à´,è¼%w°D"ôßÕ1—Šñ¸$åzŠS eYK{²ZY_qãY‡£ YO^Ê_(õ§ BÊ´Ö¡lY_ÞxÆlU3¡m'ÓedÜV*‰±•"ƒ¦¯‹²[e˜âPv¬Ï8çr.Œqi(PŠÉA‹*ñ[ŽÆ%ŒI˜T®®H¬¨W/e¸RŸ¥§úÏæÏÿ¹SaóïÏ›çœù¿Í—ô{Šù„&”+:f,ª„îo2>¡3ãŠY4”a­B%‰]b6«#:± •„rEgÖ2‡æ<'Æ£áœBʱV2†5çÚ°ä "N!eÑðäœ_í*âW+kò¹Tá²ÊD\V*NÔœò o²Y0x4(S9Œñhì0 ”¨”“DèQN™…”»@ù¬I(W ´XT1eÒ`À·Ø4”YXÈ8]Ñ(3ëY®eæ”W£eI²­ŒÃ ñZ•¨øOûBÿñù©¾üŸó%gYs¦¡˜e„rE§Õ2¦!åÓ$0> gR–uÊY‰Jb܆XÆm \×8”:¸eF¹~„Qã$âYËÐùuŒg͹†”Q£+Ç5¤Œ- oÏZ-âYëX°x£âP¶4JTÊ ƒ'efÊx-FÉ1˜âX@y£â˳^«&‰ñ 9«Fާk&°^oHƒOÎøvŒï@Y^NŒjTʉ1k(ÛÚ‰ñ(óU‚AªaÜÊ·ŽF£¤ŒùPŒ’aðêXSÆuÊŽîu‹_Æ;´ë$p¾â V21桊ñœ0àÃ˱¾(VŽ ÀˆrÄ$ q9ÇFÎ86”{(Ãä c ‚sa©£)QIŒeCÙ‡&Æ>¤ÌëÆiТJPrƉ¥þNÿxåùócgAÿS¾,Ÿ+yždŸé_:úO寔ÿ»œø¯äCš ÿSyPœyþã¹ï¿Ë{ÿ›œGdz Í()›œ•ŒSèÊØ4”W­F™Dü-1³UÎgBIÑu"M4cRNµæ1cUs!åШD,Bê¬JTcrNµTÄ©V¡oKýF«\ÄhU¢Œ(Gtt5ãJÐáÕŒÓêŒ9,ef\šXÆ#¤lGÌ_*TÊY‚ÿJÒ\`´šPRÌ]ZT ­1`JZ ¬Ê%,fÌ­XT!JÆx54¼Qƶ£ÕJŸ„ ,5ÊĘ[Ѩb” ƒMËØÕ”»Ž2£¤|ZT cXÇ–cµÚb@*Qñ(G L ÊŒ’²åÜʬI¢ìÆÜRcÀ&¡œé>!ãÕÈ“ò«ãQŽŒ·Åù¬”%“ð©ÎûTçÙüýë<{çŠÙÕ&”VÇ8„êJ»ÚUÄ!¤ìj#ÊY…2¡\Ñ©µÌ±)§ÕˆrD×  QRtt-Ê]‰Jb,Bί–£óÇ1~5gÊ0âʱ¥:”m]EÈùÕ¿:e‹Á¢@Qv4*Tʃ'Uˆ’1Vk ʃÉÈÖ ”e'âµJ0ÀÔ(cF£ŠQÞpqÍ^«eDÙÒ{’(#ãxiÇ˃Qƒ2¡œ1(Ã[ ,kg NMkáê„AŽ*l#ð¬cQ%(­U‚’cðÆ1F!eZQŽô,DÄ÷ gœBÇNã+ñ\5ô¬„± Õ¨”3|t9Ö ~oTXçg¾‚}2ÏMÁ(:`,ÆžÏZ¤3¬×a%:Ÿu&R¦3`¹öIž÷½‡&ý3éóúæIŸŒ=òvÝ%Q‰¯ù"‘Žx= ’i˜‹’© Î"¡:`2¼æ"¯ÙçµH²-p2H¶.Hóñƒ4¯Æ` 8 $,ÙÑÙÔgélê‡ïëûa´fD÷!¯Ò>Ä2ÕÔfhí99æŒ÷ NNÔÓ‰ùt¼n²Ië&k´îs©áù¼Ü~¦÷×ÏDcÓ'êM÷å3d<ÎãÁOá´/á¯=Ý¿‘ ‚~ B~ "~ ߃`?…X¿ qþ ¢l@Œ„ÿñÍè†ÎçÞ9î ­ ‘ýâú;„õ.„õ/«A=1½v!¤spÚÿ~7Úxòm8y"zâù+¸ñ|~ᜇh® ó5å/à'´ðökˆäQä¨ÒBÛߨ0à%*ªúñ>øÄð}ˆà]° | ÿOð<¹ó›à":äCð{tÆÐ ï€tÿ'×@Îý0œû?àWpoîýx~þümð[¸ø¸øèÃÉ/ÁÉ?ÆèøÇpô?ì˜2uŒ˜Yܹ»3wg„ÏøNûÒ“¿xöÅp;ÈzNèäé7=?¬¹yþ´ß l3Ÿkï6òN½ž}Å ZÍ ½X ¡iþ•e]a³ÇºlÎþEÈx°&+žÒeÿgŠlê¬,üöÇN;tZéôšøaëÕÓ_çÔ·Ó¿zׯÓiE—л…WÜBËyÑsv »~«]k6²;­ºô\Zפ<ï}×i;‘>åñ¢¼Ì4K͇­ÆË7ôäÏ/J¡S©û¹Z£Ú”nÚ’!ŸÞÒ©­ä-7Xes4§.ùƒå¯·qÜ Þ?G‹Ë~”ùãFŽ›äÞ?G›ú­µ9k…7z§…*â¶Äþä—jƒ›¼©ë§ù?þÏT¦Ì–®nÜçþ¿`ÿ‡~'ÌVœ¶¿ˆïE6 Ñÿ«ˆû6ú[ÔïYÓtÑÿ«ˆ%ú5ù/ü%±@ÿû°Uóœûñ¸ÙüOWŒ“ù/Cbþ·‚™$yµ–Ä'†´†A»ç3†œ»›k´œfÎ÷vNÌ NñÍõv ¡ Ó³ÌÌÊÊe™4½ Û9›©²¬]™]‰*µ‚í\e§V÷¤ÿ «Ê,ÇÕܬ®*j–i2ËÚ®ªg}Y²#û¶*}#9ï¬Öê¾”lT•/3« ËÙÈÙ¶©æIóÅö E÷¼ªb›ŽãV¼Šé¸U¥Mj:ÞÌŒ¬,gë²l4» ²œ­á3“qÅ’ç´Z~½žk»[5¿â×¥†b_¿2L“×ed¾µqYafT=‡Ÿ…™øÖFÕñ]ËÕ+̪8vEef¥bxŠêXÓRÕêBŸä–ÓÎÎ~ßðêTY3™¥\¿D>Ûüòk´ÔÙ5V ÃÕLÕñåªf™¶é™²®šZEµmÏqT÷«¼FE?þ gSÓ¹®ÐQ=æšÃtVU™«Ë®bª|Žá»¦â{ÖW{…¸À;ÕóÇþ¿ÔØÇó?CŒÿV öÿÂc?KŒÿU1þ[M,ÕÿÇþË¿0{îËÚ¸Éø¿Yõý¯3Íã¿UÄÓiYÝ@Þ®mûÙðÕÀ—žK+šäA½æ:a­ÙÈ7Ýгí°å;Ûâ^ð½·ÿxñ\é¿Dþk†!ò%!òÿþŽ[Èÿ*›/ý—ÉMÑDþ¯"Dþ‹!B„"Dˆ!B„"Dˆ!B„"Dˆ!BĽ_ÔE—ÕÈŒcasacore-3.7.1/measures/Measures/test/tMeasIERS.out000066400000000000000000000060611476623553700222030ustar00rootroot00000000000000Test measure class MeasIERS --------------------------- MEASURED 51116 X: 0.15712 1 PREDICTED 51116 X: 0 0 MEASURED 51116.5 X: 0.157685 1 PREDICTED 51116.5 X: 0 0 MEASURED 51117 X: 0.15825 1 PREDICTED 51117 X: 0 0 MEASURED 37660 X: 0 0 PREDICTED 37660 X: 0 0 MEASURED 37665 X: -0.0127 1 PREDICTED 37665 X: 0 0 MEASURED 55000 X: 0.091192 1 PREDICTED 55000 X: 0.09119 1 MEASURED 55809 X: 0.166538 1 PREDICTED 55809 X: 0.16653 1 MEASURED 600000 X: 0 0 PREDICTED 600000 X: 0 0 2015/06/29/00:00:00 57202.000 -0.675 2015/06/29/01:00:00 57202.042 -0.675 2015/06/29/02:00:00 57202.083 -0.675 2015/06/29/03:00:00 57202.125 -0.675 2015/06/29/04:00:00 57202.167 -0.676 2015/06/29/05:00:00 57202.208 -0.676 2015/06/29/06:00:00 57202.250 -0.676 2015/06/29/07:00:00 57202.292 -0.676 2015/06/29/08:00:00 57202.333 -0.676 2015/06/29/09:00:00 57202.375 -0.676 2015/06/29/10:00:00 57202.417 -0.676 2015/06/29/11:00:00 57202.458 -0.676 2015/06/29/12:00:00 57202.500 -0.676 2015/06/29/13:00:00 57202.542 -0.676 2015/06/29/14:00:00 57202.583 -0.676 2015/06/29/15:00:00 57202.625 -0.676 2015/06/29/16:00:00 57202.667 -0.676 2015/06/29/17:00:00 57202.708 -0.676 2015/06/29/18:00:00 57202.750 -0.676 2015/06/29/19:00:00 57202.792 -0.676 2015/06/29/20:00:00 57202.833 -0.676 2015/06/29/21:00:00 57202.875 -0.676 2015/06/29/22:00:00 57202.917 -0.676 2015/06/29/23:00:00 57202.958 -0.676 2015/06/30/00:00:00 57203.000 -0.676 2015/06/30/01:00:00 57203.042 -0.676 2015/06/30/02:00:00 57203.083 -0.676 2015/06/30/03:00:00 57203.125 -0.676 2015/06/30/04:00:00 57203.167 -0.676 2015/06/30/05:00:00 57203.208 -0.676 2015/06/30/06:00:00 57203.250 -0.676 2015/06/30/07:00:00 57203.292 -0.676 2015/06/30/08:00:00 57203.333 -0.676 2015/06/30/09:00:00 57203.375 -0.676 2015/06/30/10:00:00 57203.417 -0.676 2015/06/30/11:00:00 57203.458 -0.676 2015/06/30/12:00:00 57203.500 -0.676 2015/06/30/13:00:00 57203.542 -0.676 2015/06/30/14:00:00 57203.583 -0.676 2015/06/30/15:00:00 57203.625 -0.676 2015/06/30/16:00:00 57203.667 -0.677 2015/06/30/17:00:00 57203.708 -0.677 2015/06/30/18:00:00 57203.750 -0.677 2015/06/30/19:00:00 57203.792 -0.677 2015/06/30/20:00:00 57203.833 -0.677 2015/06/30/21:00:00 57203.875 -0.677 2015/06/30/22:00:00 57203.917 -0.677 2015/06/30/23:00:00 57203.958 -0.677 2015/07/01/00:00:00 57204.000 0.323 2015/07/01/01:00:00 57204.042 0.323 2015/07/01/02:00:00 57204.083 0.323 2015/07/01/03:00:00 57204.125 0.323 2015/07/01/04:00:00 57204.167 0.323 2015/07/01/05:00:00 57204.208 0.323 2015/07/01/06:00:00 57204.250 0.323 2015/07/01/07:00:00 57204.292 0.323 2015/07/01/08:00:00 57204.333 0.323 2015/07/01/09:00:00 57204.375 0.323 2015/07/01/10:00:00 57204.417 0.323 2015/07/01/11:00:00 57204.458 0.323 2015/07/01/12:00:00 57204.500 0.323 2015/07/01/13:00:00 57204.542 0.323 2015/07/01/14:00:00 57204.583 0.323 2015/07/01/15:00:00 57204.625 0.323 2015/07/01/16:00:00 57204.667 0.323 2015/07/01/17:00:00 57204.708 0.323 2015/07/01/18:00:00 57204.750 0.323 2015/07/01/19:00:00 57204.792 0.323 2015/07/01/20:00:00 57204.833 0.323 2015/07/01/21:00:00 57204.875 0.323 2015/07/01/22:00:00 57204.917 0.323 2015/07/01/23:00:00 57204.958 0.323 casacore-3.7.1/measures/Measures/test/tMeasIERS.run000077500000000000000000000006241476623553700222020ustar00rootroot00000000000000#!/bin/sh # Define the casarc file and create it. cwd=`pwd` CASARCFILES=$cwd/tMeasIERS_tmp-casarc export CASARCFILES cat > $CASARCFILES << eof measures.ierseop97.directory: $cwd/tMeasIERS_tmp-data/geodetic measures.ierspredict.directory: $cwd/tMeasIERS_tmp-data/geodetic eof # Untar the IERS tables rm -rf tMeasIERS_tmp-data tar zxf tMeasIERS.in_tgz # Run the test program $casa_checktool ./tMeasIERS casacore-3.7.1/measures/Measures/test/tMeasJPL.cc000066400000000000000000000220171476623553700216430ustar00rootroot00000000000000//# tMeasJPL.cc: This program test JPL DE functions //# Copyright (C) 1997-2002,2007 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main() { try { #ifdef _OPENMP #pragma omp parallel for #endif for (int dd=0; dd<4; ++dd) { const MVEpoch dat = 51116 + dd*33; ostringstream ostr; ostr << dd; ofstream os(("tMeasJPL_tmp.out_a" + ostr.str()).c_str()); MVDirection mvd1; os << "Test measure class MeasJPL" << endl; os << "---------------------------" << endl; os << setprecision(9); Vector val(6); os << "DE200: " << dat << endl; os << "---------------------------" << endl; os << "Mercury0: " << MeasTable::Planetary(MeasTable::MERCURY, dat.get()) << endl; MeasJPL::get(val, MeasJPL::DE200, MeasJPL::MERCURY, dat); os << "Mercury: " << val << endl; MeasJPL::get(val, MeasJPL::DE200, MeasJPL::VENUS, dat); os << "Venus: " << val << endl; for (uInt i=0; i<3; i++) { mvd1(i) = val(i); } mvd1.adjust(); os << "Venus: " << mvd1 << endl; MeasJPL::get(val, MeasJPL::DE200, MeasJPL::EARTH, dat); os << "Earth: " << val << endl; MeasJPL::get(val, MeasJPL::DE200, MeasJPL::MARS, dat); os << "Mars: " << val << endl; MeasJPL::get(val, MeasJPL::DE200, MeasJPL::JUPITER, dat); os << "Jupiter: " << val << endl; MeasJPL::get(val, MeasJPL::DE200, MeasJPL::SATURN, dat); os << "Saturn: " << val << endl; MeasJPL::get(val, MeasJPL::DE200, MeasJPL::URANUS, dat); os << "Uranus: " << val << endl; MeasJPL::get(val, MeasJPL::DE200, MeasJPL::NEPTUNE, dat); os << "Neptune: " << val << endl; MeasJPL::get(val, MeasJPL::DE200, MeasJPL::PLUTO, dat); os << "Pluto: " << val << endl; } MeasIERS::closeTables(); Vector openTables = PlainTable::tableCache().getTableNames(); if (openTables.size() > 0){ cout << "ERROR: cache not empty!" << endl; for (uInt i=0; i val(6); MeasJPL::get(val, MeasJPL::DE200, MeasJPL::MOON, dat); os << "Moon: " << val << endl; MeasJPL::get(val, MeasJPL::DE200, MeasJPL::SUN, dat); os << "SUN: " << val << endl; MeasJPL::get(val, MeasJPL::DE200, MeasJPL::BARYSOLAR, dat); os << "Barycentre: " << val << endl; MeasJPL::get(val, MeasJPL::DE200, MeasJPL::BARYEARTH, dat); os << "Earth/Moon: " << val << endl; MeasJPL::get(val, MeasJPL::DE200, MeasJPL::NUTATION, dat); os << "Nutation: " << val << endl; MeasJPL::get(val, MeasJPL::DE200, MeasJPL::LIBRATION, dat); os << "Libration: " << val << endl; os << "DE405: " << dat << endl; os << "---------------------------" << endl; MeasJPL::get(val, MeasJPL::DE405, MeasJPL::MERCURY, dat); os << "Mercury: " << val << endl; MeasJPL::get(val, MeasJPL::DE405, MeasJPL::VENUS, dat); os << "Venus: " << val << endl; MeasJPL::get(val, MeasJPL::DE405, MeasJPL::EARTH, dat); os << "Earth: " << val << endl; MeasJPL::get(val, MeasJPL::DE405, MeasJPL::MARS, dat); os << "Mars: " << val << endl; MeasJPL::get(val, MeasJPL::DE405, MeasJPL::JUPITER, dat); os << "Jupiter: " << val << endl; MeasJPL::get(val, MeasJPL::DE405, MeasJPL::SATURN, dat); os << "Saturn: " << val << endl; MeasJPL::get(val, MeasJPL::DE405, MeasJPL::URANUS, dat); os << "Uranus: " << val << endl; MeasJPL::get(val, MeasJPL::DE405, MeasJPL::NEPTUNE, dat); os << "Neptune: " << val << endl; MeasJPL::get(val, MeasJPL::DE405, MeasJPL::PLUTO, dat); os << "Pluto: " << val << endl; MeasJPL::get(val, MeasJPL::DE405, MeasJPL::MOON, dat); os << "Moon: " << val << endl; MeasJPL::get(val, MeasJPL::DE405, MeasJPL::SUN, dat); os << "SUN: " << val << endl; MeasJPL::get(val, MeasJPL::DE405, MeasJPL::BARYSOLAR, dat); os << "Barycentre: " << val << endl; MeasJPL::get(val, MeasJPL::DE405, MeasJPL::BARYEARTH, dat); os << "Earth/Moon: " << val << endl; MeasJPL::get(val, MeasJPL::DE405, MeasJPL::NUTATION, dat); os << "Nutation: " << val << endl; MeasJPL::get(val, MeasJPL::DE405, MeasJPL::LIBRATION, dat); os << "Libration: " << val << endl; } } catch (const std::exception& x) { cout << x.what() << endl; } try { const MVEpoch dat = 51116; const MEpoch mdat(dat, MEpoch::Ref(MEpoch::TDB)); MeasFrame frame(mdat); MDirection::Ref venr(MDirection::VENUS, frame); MDirection::Ref sunr(MDirection::SUN, frame); MDirection::Ref moonr(MDirection::MOON, frame); MDirection ven(venr); MDirection sn(sunr); MDirection mon(moonr); MDirection::Convert vc1(ven, MDirection::Ref(MDirection::JNAT)); MDirection::Convert vc2(ven, MDirection::Ref(MDirection::APP)); MDirection::Convert sc1(sn, MDirection::Ref(MDirection::JNAT)); MDirection::Convert sc2(sn, MDirection::Ref(MDirection::APP)); MDirection::Convert mc1(mon, MDirection::Ref(MDirection::JNAT)); MDirection::Convert mc2(mon, MDirection::Ref(MDirection::APP)); cout << "Venus JNAT: " << vc1() << endl; cout << "Venus APP: " << vc2() << endl; cout << "Sun JNAT: " << sc1() << endl; cout << "Sun APP: " << sc2() << endl; cout << "Sun APP: " << sc2().getValue().getAngle("deg") << endl; cout << "Moon JNAT: " << mc1() << endl; cout << "Moon APP: " << mc2() << endl; cout << "Moon APP: " << mc2().getValue().getAngle("deg") << endl; MDirection ven_offset(Quantity(1, "deg"), Quantity(0.5, "deg"), venr); MDirection sn_offset(Quantity(1, "deg"), Quantity(0.5, "deg"), sunr); MDirection mon_offset(Quantity(1, "deg"), Quantity(0.5, "deg"),moonr); MDirection::Convert vc1_offset(ven_offset, MDirection::Ref(MDirection::JNAT)); MDirection::Convert vc2_offset(ven_offset, MDirection::Ref(MDirection::APP)); MDirection::Convert sc1_offset(sn_offset, MDirection::Ref(MDirection::JNAT)); MDirection::Convert sc2_offset(sn_offset, MDirection::Ref(MDirection::APP)); MDirection::Convert mc1_offset(mon_offset, MDirection::Ref(MDirection::JNAT)); MDirection::Convert mc2_offset(mon_offset, MDirection::Ref(MDirection::APP)); cout << "Venus offset JNAT: " << vc1_offset() << endl; cout << "Venus offset APP: " << vc2_offset() << endl; cout << "Sun offset JNAT: " << sc1_offset() << endl; cout << "Sun offset APP: " << sc2_offset() << endl; cout << "Sun offset APP: " << sc2_offset().getValue().getAngle("deg") << endl; cout << "Moon offset JNAT: " << mc1_offset() << endl; cout << "Moon offset APP: " << mc2_offset() << endl; cout << "Moon offset APP: " << mc2_offset().getValue().getAngle("deg") << endl; } catch (const std::exception& x) { cout << x.what() << endl; } return 0; } casacore-3.7.1/measures/Measures/test/tMeasJPL.out000066400000000000000000000317761476623553700221010ustar00rootroot00000000000000 *** tMeasJPL_tmp.out_a0 Test measure class MeasJPL --------------------------- DE200: 51116::00:00:00.0000000 --------------------------- Mercury0: [0.112635159, -0.378671577, -0.214656378, 0.0214596316, 0.00873928166, 0.00244336875] Mercury: [0.112635159, -0.378671577, -0.214656378, 0.0214596316, 0.00873928166, 0.00244336875] Venus: [-0.590618963, -0.402576215, -0.144035251, 0.0118285844, -0.0146926142, -0.00735831801] Venus: [-0.810021815, -0.552125037, -0.197541397] Earth: [0.791134643, 0.541235184, 0.234895275, -0.0104883661, 0.0126342196, 0.0054783985] Mars: [-1.17789402, 1.05226562, 0.514485072, -0.00936377554, -0.00801489163, -0.00342273851] Jupiter: [4.94633894, -0.187963308, -0.20105115, 0.000286172646, 0.00725274959, 0.0031018692] Saturn: [7.99348975, 4.46636965, 1.50082979, -0.00311780692, 0.0043883827, 0.00194651371] Uranus: [13.2265021, -13.5166811, -6.10703428, 0.00290631103, 0.00224550355, 0.00094236082] Neptune: [15.681093, -23.6715664, -10.0793028, 0.00266008804, 0.001553314, 0.000569557175] Pluto: [-11.1730412, -27.468227, -5.20568214, 0.00298537174, -0.0012626622, -0.00129351537] *** tMeasJPL_tmp.out_a1 Test measure class MeasJPL --------------------------- DE200: 51149::00:00:00.0000000 --------------------------- Mercury0: [0.0890072953, 0.262034834, 0.130064613, -0.0323181706, 0.0076486265, 0.00743846524] Mercury: [0.0890072953, 0.262034834, 0.130064613, -0.0323181706, 0.0076486265, 0.00743846524] Venus: [-0.0230609058, -0.662710026, -0.29697698, 0.0200866925, 1.15370993e-05, -0.0012658419] Venus: [-0.0317391808, -0.912100918, -0.408735292] Earth: [0.335089735, 0.848378106, 0.368063371, -0.0163999299, 0.00542627641, 0.00235273955] Mars: [-1.44224518, 0.753961388, 0.384818439, -0.00657196266, -0.00995206358, -0.00438674651] Jupiter: [4.94924294, 0.0515220017, -0.0984682041, -0.000110353008, 0.00725831738, 0.00311391419] Saturn: [7.88899486, 4.61027332, 1.56475686, -0.00321504913, 0.004332731, 0.00192771489] Uranus: [13.322138, -13.4423024, -6.07581113, 0.00288978779, 0.00226228341, 0.000949943793] Neptune: [15.7687832, -23.6201675, -10.0604479, 0.00265446761, 0.00156176791, 0.000573157265] Pluto: [-11.0744581, -27.5097325, -5.2483373, 0.00298935492, -0.00125281796, -0.00129164357] *** tMeasJPL_tmp.out_a2 Test measure class MeasJPL --------------------------- DE200: 51182::00:00:00.0000000 --------------------------- Mercury0: [-0.331311505, -0.285367361, -0.118710002, 0.0137310916, -0.01635621, -0.0101599982] Mercury: [-0.331311505, -0.285367361, -0.118710002, 0.0137310916, -0.01635621, -0.0101599982] Venus: [0.557019216, -0.403882033, -0.217240262, 0.0125918269, 0.0145530863, 0.00575045917] Venus: [0.772012955, -0.55976913, -0.301088888] Earth: [-0.232608565, 0.878528905, 0.381134395, -0.0170233289, -0.00366421299, -0.00158930834] Mars: [-1.60688756, 0.403217341, 0.228403276, -0.00334774241, -0.0111816336, -0.0050378917] Jupiter: [4.93905032, 0.290874392, 0.00437676429, -0.00050736228, 0.00724468333, 0.00311773998] Saturn: [7.78130783, 4.7523088, 1.62804978, -0.00331126209, 0.00427516155, 0.00190808024] Uranus: [13.4172269, -13.3673716, -6.04433847, 0.00287315555, 0.00227896031, 0.000957483215] Neptune: [15.8562875, -23.5684899, -10.0414744, 0.00264881537, 0.00157020406, 0.000576750881] Pluto: [-10.9757442, -27.550913, -5.29093046, 0.00299329928, -0.00124296762, -0.00128975817] *** tMeasJPL_tmp.out_a3 Test measure class MeasJPL --------------------------- DE200: 51215::00:00:00.0000000 --------------------------- Mercury0: [0.307534086, -0.212476055, -0.146000505, 0.0120388937, 0.0210275571, 0.00998445572] Mercury: [0.307534086, -0.212476055, -0.146000505, 0.0120388937, 0.0210275571, 0.00998445572] Venus: [0.696470303, 0.169562869, 0.0319233572, -0.00476961866, 0.0177424895, 0.00828409543] Venus: [0.970657039, 0.236316455, 0.0444909585] Earth: [-0.726525981, 0.619692327, 0.268911125, -0.0120670967, -0.0115601057, -0.00501233949] Mars: [-1.66023034, 0.0244619354, 0.0561307379, 0.000145702342, -0.011639765, -0.00534248207] Jupiter: [4.91576491, 0.529459559, 0.107211764, -0.000903654515, 0.00721182062, 0.00311330582] Saturn: [7.67046341, 4.89241305, 1.69068101, -0.00340639825, 0.00421568891, 0.0018876138] Uranus: [13.5117651, -13.2918921, -6.01261772, 0.00285641503, 0.00229553377, 0.000964978865] Neptune: [15.9436047, -23.5165342, -10.0223825, 0.00264313138, 0.00157862235, 0.000580337981] Pluto: [-10.8769008, -27.5917683, -5.33346119, 0.0029972048, -0.00123311139, -0.00128785924] *** tMeasJPL_tmp.out_b0 Moon: [0.793219046, 0.539884505, 0.234327217, -0.0101744223, 0.0131311316, 0.00563522659] SUN: [-0.00839636156, 0.000716225798, 0.00055030648, 3.56132935e-07, -8.3589462e-06, -3.58657669e-06] Barycentre: [0, 0, 0, 0, 0, 0] Earth/Moon: [0.791159666, 0.54121897, 0.234888456, -0.0104845972, 0.0126401849, 0.00548028117] Nutation: [-5.17424774e-05, -3.70226372e-05, -2.0672392e-07, 1.19726803e-07, 0, 0] Libration: [0, 0, 0, 0, 0, 0] DE405: 51116::00:00:00.0000000 --------------------------- Mercury: [0.112637744, -0.378676342, -0.21465838, 0.021459632, 0.008739282, 0.00244336657] Venus: [-0.590616356, -0.402580995, -0.144037238, 0.0118285851, -0.0146926137, -0.00735831838] Earth: [0.791137221, 0.541230466, 0.234893242, -0.0104883661, 0.012634219, 0.00547839922] Mars: [-1.17789144, 1.0522608, 0.514483169, -0.00936377497, -0.00801489194, -0.00342273798] Jupiter: [4.94634107, -0.18797011, -0.201056025, 0.000286178011, 0.00725275016, 0.00310186884] Saturn: [7.99349803, 4.46636103, 1.50082792, -0.00311780118, 0.00438838396, 0.00194651469] Uranus: [13.2264768, -13.5166788, -6.10703383, 0.0029063126, 0.00224550748, 0.000942363076] Neptune: [15.6810431, -23.6716735, -10.0793458, 0.0026600922, 0.00155330188, 0.000569551256] Pluto: [-11.1724049, -27.4679268, -5.20569128, 0.00298543173, -0.00126258865, -0.00129350676] Moon: [0.793221624, 0.539879787, 0.234325184, -0.0101744222, 0.0131311309, 0.00563522725] SUN: [-0.00839376368, 0.000711458554, 0.000548321414, 3.5653306e-07, -8.3588289e-06, -3.58654944e-06] Barycentre: [0, 0, 0, 0, 0, 0] Earth/Moon: [0.791162243, 0.541214252, 0.234886423, -0.0104845973, 0.0126401843, 0.0054802819] Nutation: [-5.17424758e-05, -3.70226373e-05, -2.06726636e-07, 1.19728605e-07, 0, 0] Libration: [-0.0338641195, 0.431994327, 2465.69683, -0.000115944255, -6.9822504e-05, 0.230073691] *** tMeasJPL_tmp.out_b1 Moon: [0.336758556, 0.850070007, 0.368568183, -0.0168685807, 0.00583002213, 0.00251877046] SUN: [-0.00837788108, 0.000440220871, 0.000431704144, 7.69356913e-07, -8.36278898e-06, -3.59914788e-06] Barycentre: [0, 0, 0, 0, 0, 0] Earth/Moon: [0.335109769, 0.848398417, 0.368069431, -0.0164055559, 0.00543112327, 0.0023547327] Nutation: [-5.32879743e-05, -3.92916984e-05, 1.74425847e-07, -2.63110313e-07, 0, 0] Libration: [0, 0, 0, 0, 0, 0] DE405: 51149::00:00:00.0000000 --------------------------- Mercury: [0.0890099276, 0.262030068, 0.130062618, -0.0323181692, 0.00764862677, 0.00743846909] Venus: [-0.0230582802, -0.662714785, -0.296978977, 0.0200866928, 1.15377386e-05, -0.00126584207] Earth: [0.335092315, 0.848373363, 0.368061368, -0.0163999297, 0.00542627548, 0.00235274061] Mars: [-1.44224258, 0.753956552, 0.38481655, -0.00657196205, -0.00995206376, -0.00438674616] Jupiter: [4.94924524, 0.0515152212, -0.0984730875, -0.000110347676, 0.00725831811, 0.00311391406] Saturn: [7.88900333, 4.61026475, 1.56475503, -0.00321504336, 0.00433273237, 0.00192771588] Uranus: [13.3221128, -13.4422999, -6.07581061, 0.00288978932, 0.00226228741, 0.00094994608] Neptune: [15.7687335, -23.620275, -10.0604912, 0.00265447183, 0.00156175577, 0.00057315134] Pluto: [-11.0738198, -27.5094298, -5.24834616, 0.00298941489, -0.00125274402, -0.00129163486] Moon: [0.336761135, 0.850065263, 0.36856618, -0.0168685804, 0.00583002127, 0.00251877159] SUN: [-0.00837526996, 0.000435457508, 0.000429719978, 7.69758629e-07, -8.36267115e-06, -3.59912065e-06] Barycentre: [0, 0, 0, 0, 0, 0] Earth/Moon: [0.335112349, 0.848393674, 0.368067429, -0.0164055557, 0.00543112234, 0.00235473377] Nutation: [-5.32879716e-05, -3.92916935e-05, 1.74414992e-07, -2.63103005e-07, 0, 0] Libration: [-0.0359983338, 0.431487116, 2473.28795, -0.000114287667, -1.12199908e-05, 0.230060509] *** tMeasJPL_tmp.out_b2 Moon: [-0.234166951, 0.880396261, 0.381879696, -0.017527503, -0.0040015613, -0.00167833354] SUN: [-0.00834560744, 0.000164624592, 0.000312918701, 1.18849766e-06, -8.33802324e-06, -3.59974994e-06] Barycentre: [0, 0, 0, 0, 0, 0] Earth/Moon: [-0.232627273, 0.878551322, 0.381143342, -0.0170293813, -0.00366826276, -0.00159037706] Nutation: [-4.55312625e-05, -3.90546978e-05, 3.59193306e-07, 2.57141724e-07, 0, 0] Libration: [0, 0, 0, 0, 0, 0] DE405: 51182::00:00:00.0000000 --------------------------- Mercury: [-0.331308885, -0.285372125, -0.118711954, 0.0137310912, -0.0163562097, -0.0101599999] Venus: [0.557021848, -0.403886776, -0.217242259, 0.012591827, 0.0145530866, 0.00575045933] Earth: [-0.232605973, 0.87852413, 0.381132428, -0.0170233283, -0.00366421396, -0.00158930727] Mars: [-1.60688494, 0.403212501, 0.228401395, -0.00334774177, -0.0111816336, -0.00503789157] Jupiter: [4.9390528, 0.290867638, 0.00437188052, -0.000507356992, 0.00724468422, 0.00311774009] Saturn: [7.78131649, 4.75230027, 1.62804797, -0.0033112563, 0.00427516302, 0.00190808125] Uranus: [13.4172017, -13.3673689, -6.04433787, 0.00287315705, 0.00227896438, 0.000957485534] Neptune: [15.8562379, -23.5685978, -10.0415179, 0.00264881964, 0.00157019191, 0.000576744949] Pluto: [-10.9751039, -27.5506079, -5.29093904, 0.00299335922, -0.00124289328, -0.00128974936] Moon: [-0.234164359, 0.880391486, 0.38187773, -0.0175275026, -0.00400156219, -0.00167833238] SUN: [-0.00834298304, 0.000159865127, 0.000310935433, 1.18890098e-06, -8.33790479e-06, -3.5997227e-06] Barycentre: [0, 0, 0, 0, 0, 0] Earth/Moon: [-0.232624681, 0.878546547, 0.381141375, -0.0170293808, -0.00366826373, -0.00159037599] Nutation: [-4.55312437e-05, -3.90547002e-05, 3.5917965e-07, 2.57143364e-07, 0, 0] Libration: [-0.0379049212, 0.431017817, 2480.87891, -0.000146245524, -9.55196349e-06, 0.230105131] *** tMeasJPL_tmp.out_b3 Moon: [-0.72907543, 0.618782946, 0.268746266, -0.011893655, -0.0120706388, -0.0052011305] SUN: [-0.00829925419, -0.000109710634, 0.000194249615, 1.62165234e-06, -8.28065349e-06, -3.58921394e-06] Barycentre: [0, 0, 0, 0, 0, 0] Earth/Moon: [-0.726556586, 0.619681411, 0.268909146, -0.0120650145, -0.0115662345, -0.00501460588] Nutation: [-4.51402598e-05, -3.49725159e-05, -4.48113339e-07, 4.04469046e-08, 0, 0] Libration: [0, 0, 0, 0, 0, 0] DE405: 51215::00:00:00.0000000 --------------------------- Mercury: [0.307536714, -0.212480808, -0.146002525, 0.0120388949, 0.021027557, 0.00998445453] Venus: [0.696472938, 0.169558128, 0.0319213697, -0.00476961856, 0.0177424893, 0.00828409584] Earth: [-0.726523366, 0.619687525, 0.268909189, -0.0120670958, -0.0115601064, -0.00501233876] Mars: [-1.6602277, 0.0244570982, 0.0561288572, 0.000145702995, -0.0116397648, -0.00534248216] Jupiter: [4.91576756, 0.529452838, 0.107206887, -0.000903649283, 0.00721182167, 0.00311330615] Saturn: [7.67047226, 4.89240457, 1.69067924, -0.00340639243, 0.00421569048, 0.00188761483] Uranus: [13.51174, -13.2918894, -6.01261704, 0.0028564165, 0.00229553791, 0.000964981217] Neptune: [15.9435552, -23.5166425, -10.0224261, 0.00264313571, 0.0015786102, 0.000580332043] Pluto: [-10.8762585, -27.5914607, -5.33346947, 0.00299726471, -0.00123303665, -0.00128785033] Moon: [-0.729072816, 0.618778144, 0.26874433, -0.0118936542, -0.0120706395, -0.00520112979] SUN: [-0.00829661646, -0.00011446618, 0.000192267247, 1.62205727e-06, -8.28053441e-06, -3.58918669e-06] Barycentre: [0, 0, 0, 0, 0, 0] Earth/Moon: [-0.726553972, 0.619676608, 0.26890721, -0.0120650137, -0.0115662352, -0.00501460514] Nutation: [-4.51402633e-05, -3.49725156e-05, -4.48112788e-07, 4.04466843e-08, 0, 0] Libration: [-0.0398875031, 0.430746468, 2488.47, -2.59648596e-05, 5.05567023e-05, 0.230011362] *** tMeasJPL_tmp.out_x Venus JNAT: Direction: [-0.80543, -0.55002, -0.220818] Venus APP: Direction: [-0.805689, -0.549705, -0.220657] Sun JNAT: Direction: [-0.805053, -0.544252, -0.235964] Sun APP: Direction: [-0.805313, -0.543938, -0.235803] Sun APP: [-145.963, -13.639] deg Moon JNAT: Direction: [0.81807, -0.530141, -0.222963] Moon APP: Direction: [0.817893, -0.53038, -0.223043] Moon APP: [-32.9623, -12.8878] deg Venus offset JNAT: Direction: [-0.79725, -0.565086, -0.212298] Venus offset APP: Direction: [-0.797514, -0.564774, -0.212138] Sun offset JNAT: Direction: [-0.797087, -0.559381, -0.227474] Sun offset APP: Direction: [-0.797352, -0.559069, -0.227315] Sun offset APP: [-144.963, -13.139] deg Moon offset JNAT: Direction: [0.828817, -0.516793, -0.214448] Moon offset APP: Direction: [0.828646, -0.517034, -0.214529] Moon offset APP: [-31.9621, -12.3879] deg casacore-3.7.1/measures/Measures/test/tMeasJPL.run000077500000000000000000000003111476623553700220560ustar00rootroot00000000000000#!/bin/sh rm -rf tMeasJPL_tmp.out_* $casa_checktool ./tMeasJPL > tMeasJPL_tmp.out_x for nm in a0 a1 a2 a3 b0 b1 b2 b3 x do echo '' echo " *** tMeasJPL_tmp.out_$nm" cat tMeasJPL_tmp.out_$nm done casacore-3.7.1/measures/Measures/test/tMeasMath.cc000066400000000000000000000322331476623553700221100ustar00rootroot00000000000000//# tMeasMath.cc: This program test MeasMath functions //# Copyright (C) 1995,1996,1997,1998,1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main() { try { cout << "Test measure math (MeasMath) class ..." << endl; cout << "--------------------------------------" << endl; cout << "Euler and RotMatrix ..." << endl; cout << "--------------------------------------" << endl; Euler eul1(0.1,0.2,0.3); RotMatrix rot1, rot2, rot3; MVDirection dc1,dc2,dc3; MVPosition pdc1,pdc2,pdc3; cout << "Euler(0.1,0.2,0.3): " << eul1 << endl; cout << "-Euler(0.1,0.2,0.3): " << -eul1 << endl; cout << "Euler(0.1,0.2,0.3): " << eul1 << endl; cout << "Rotation none: " << rot1 << endl; cout << "Rotation squared: " << rot1*rot1 << endl; cout << "Rotation from Euler: " << RotMatrix(eul1) << endl; cout << "I-Rotation from Euler: " << RotMatrix(-eul1) << endl; rot2 = RotMatrix(eul1); rot3 = RotMatrix(-eul1); cout << "Euler(1,2): " << Euler(1.0,2.0) << endl; cout << "Euler(10 deg, 20 deg): " << Euler(Quantity(10,"deg"), Quantity(20,"deg")) << endl; Vector vec2(2); vec2(0)=30; vec2(1)=40; Quantum > qu2(vec2,"arcsec"); cout << "Euler(30, 40 arcsec): " << Euler(qu2) << endl; cout << "Direction cosines (MVDirection)..." << endl; cout << "--------------------------------------" << endl; cout << "MVDirection default: " << dc1 << endl; dc2 = MVDirection(1,2,3); cout << "MVDirection(1,2,3): " << dc2 << endl; dc2.adjust(); cout << "Normalised: " << dc2 << endl; dc3 = MVDirection(0.1,0.2); cout << "MVDirection(.1,.2): " << dc3 << endl; cout << "Last 2 *: " << dc2*dc3 << endl; cout << "Re-angle: " << dc3.get() << endl; cout << "10 deg, 20 deg: " << MVDirection(Quantity(10.,"deg"), Quantity(20.,"deg")) << endl; cout << "Re-angle: " << dc3.getAngle("deg") << endl; cout << "Shifts (MVDirection)..." << endl; cout << "--------------------------------------" << endl; dc2 = MVDirection(Quantity(0, "deg"), Quantity(0, "deg")); cout << "Start: " << dc2 << endl; dc3 = dc2; dc3.shiftLongitude(Quantity(10, "deg")); cout << "dl = 10 deg: " << dc3 << endl; dc3 = dc2; dc3.shiftLongitude(Quantity(10, "deg"), True); cout << "... true: " << dc3 << endl; dc3 = dc2; dc3.shiftLatitude(Quantity(10, "deg")); cout << "db = 10 deg: " << dc3 << endl; dc3 = dc2; dc3.shiftLatitude(Quantity(10, "deg"), True); cout << "... true: " << dc3 << endl; dc3 = dc2; dc3.shift(Quantity(10, "deg"), Quantity(10, "deg")); cout << "dl,b = 10 deg:" << dc3 << endl; dc3 = dc2; dc3.shift(Quantity(10, "deg"), Quantity(10, "deg"), True); cout << "... true: " << dc3 << endl; dc2 = MVDirection(Quantity(0, "deg"), Quantity(60, "deg")); cout << "Start: " << dc2 << endl; dc3 = dc2; dc3.shiftLongitude(Quantity(10, "deg")); cout << "dl = 10 deg: " << dc3 << endl; dc3 = dc2; dc3.shiftLongitude(Quantity(10, "deg"), True); cout << "... true: " << dc3 << endl; dc3 = dc2; dc3.shiftLatitude(Quantity(10, "deg")); cout << "db = 10 deg: " << dc3 << endl; dc3 = dc2; dc3.shiftLatitude(Quantity(10, "deg"), True); cout << "... true: " << dc3 << endl; dc3 = dc2; dc3.shift(Quantity(10, "deg"), Quantity(10, "deg")); cout << "dl,b = 10 deg:" << dc3 << endl; dc3 = dc2; dc3.shift(Quantity(10, "deg"), Quantity(10, "deg"), True); cout << "... true: " << dc3 << endl; dc2 = MVDirection(Quantity(30, "deg"), Quantity(60, "deg")); cout << "Start: " << dc2 << endl; dc3 = dc2; dc3.shiftLongitude(Quantity(10, "deg")); cout << "dl = 10 deg: " << dc3 << endl; dc3 = dc2; dc3.shiftLongitude(Quantity(10, "deg"), True); cout << "... true: " << dc3 << endl; dc3 = dc2; dc3.shiftLatitude(Quantity(10, "deg")); cout << "db = 10 deg: " << dc3 << endl; dc3 = dc2; dc3.shiftLatitude(Quantity(10, "deg"), True); cout << "... true: " << dc3 << endl; dc3 = dc2; dc3.shift(Quantity(10, "deg"), Quantity(10, "deg")); cout << "dl,b = 10 deg:" << dc3 << endl; dc3 = dc2; dc3.shift(Quantity(10, "deg"), Quantity(10, "deg"), True); cout << "... true: " << dc3 << endl; cout << "Positions (MVPosition)..." << endl; cout << "--------------------------------------" << endl; cout << "MVPosition default: " << pdc1 << endl; pdc2 = MVPosition(1,2,3); cout << "MVPosition(1,2,3): " << pdc2 << endl; pdc2.adjust(); cout << "Normalised: " << pdc2 << endl; Quantity pdcqu(5,"km"); pdc3 = MVPosition(pdcqu,0.1,0.2); cout << "MVPosition(5km.1,.2): " << pdc3 << endl; Quantity pqdc3(1,"m"); MVPosition ppdc3(pqdc3,0.1,0.2); cout << "Last * DC(0.1,0.2): " << pdc2*ppdc3 << endl; cout << "Re-angle: " << pdc3.get() << endl; cout << "6 mm, 10 deg, 20 deg: " << MVPosition(Quantity(6.,"mm"), Quantity(10.,"deg"), Quantity(20.,"deg")) << endl; cout << "Re-angle: " << pdc3.getAngle("deg") << endl; cout << "Length: " << pdc3.getLength() << endl; cout << "Length in dam: " << pdc3.getLength("dam") << endl; } catch (std::exception& x) { cout << x.what() << endl; } try { cout << "Euler(10 deg, 20 m): "; cout << Euler(Quantity(10,"deg"), Quantity(20,"m")) << endl; } catch (std::exception& x) { cout << x.what() << endl; } try { Euler eul10(Quantity(0,"deg"),Quantity(0,"deg"),Quantity(30,"deg")); RotMatrix rot10(eul10); MVDirection dc10(Quantity(10,"deg"),Quantity(20,"deg")); cout << "Rotate (10,20 deg) over 0,0,30 deg: " << (rot10*dc10).getAngle("deg") << endl; } catch (std::exception& x) { cout << x.what() << endl; } try { Precession pc2; Precession pc1(Precession::B1950); Nutation nt1; Nutation nt2(Nutation::B1950); Aberration ab1; Aberration ab2(Aberration::B1950); SolarPos sp1; UnitVal AUperDay(1e-8,"AU/d"); Double factor = AUperDay.getFac(); Double facAU = 1.; cout << "B1950 precession MJD 45700(1984/01/01): " << pc1(45700.).getAngle("''") << endl; cout << "B1950 precession MJD 45700 using derivative from 45701: " << pc1(45701.).getAngle("''") - pc1.derivative(45701).getAngle("''") << endl; cout << "with rotation matrix: " << RotMatrix(pc1(45700)) << endl; cout << "J2000 precession J1984.5: " << pc2(45883.125).getAngle("''") << endl; cout << "with rotation matrix: " << RotMatrix(pc2(45883.125)) << endl; cout << "J2000 nutation J1984.5: " << nt1(45883.125).getAngle("\"") << (nt1(45883.125)(0)+nt1(45883.125)(2))/C::arcsec << endl; Vector tenth(3); tenth = Double(0.1); Vector hun4(3); hun4 = Double(0.04); cout << "J2000 nutation J1984.5 derivative from +0.1 day: " << nt1(45883.225).getAngle("\"") - tenth * nt1.derivative(45883.225).getAngle("\"") << endl; cout << "J2000 nutation J1984.5 derivative from +0.04 day: " << nt1(45883.165).getAngle("\"") - hun4 * nt1.derivative(45883.165).getAngle("\"") << endl; cout << "with rotation matrix: " << RotMatrix(nt1(45883.125)) << endl; cout << "with rotation matrix combined 45882.5: " << RotMatrix(nt1(45882)) * RotMatrix(pc2(45882)) << "or:" << RotMatrix(pc2(45882)) * RotMatrix(nt1(45882)) << endl; cout << " equation of equinoxes 45882.5: " << nt1.getEqoxAngle(45882,"''") << endl; cout << " equation at 45882.5 from derivative at 45882.7: " << nt1.getEqoxAngle(45882.2,"''") - 0.2 * Quantity(nt1.derivativeEqox(45882.2)/C::arcsec,"''") << endl; cout << " equation at 45882.5 from derivative at 45882.54: " << nt1.getEqoxAngle(45882.04,"''") - 0.04 * Quantity(nt1.derivativeEqox(45882.04)/C::arcsec,"''") << endl; cout << "J2000 nutation: " << endl; Double eq; for (eq=45837.; eq<45884.; eq++) { cout << eq+0.5 << ": " << nt1(eq).getAngle("\"") << (nt1(eq)(0)+nt1(eq)(2))/C::arcsec << endl; } cout << "B1950 nutation: " << endl; for (eq=40632.; eq<40678.; eq++) { cout << eq+0.5 << ": " << nt2(eq).getAngle("\"") << (nt2(eq)(0)+nt2(eq)(2))/C::arcsec << " " << nt2.getEqoxAngle(eq,"''")/Quantity(15.,"''/s") << endl; } cout << "J2000 nutation 50449.5: " << nt1(50449.).getAngle("''") << (nt1(50449.)(0)+nt1(50449.)(2))/C::arcsec << endl; cout << "J2000 aberration for 45837: " << endl; cout << ab1(45837.) * (C::c / factor) << endl; cout << "J2000 aberration for 45837 from derivative at +0.1: " << endl; cout << (ab1(45837.1) - 0.1 * ab1.derivative(45837.1)) * (C::c / factor) << endl; cout << "J2000 aberration for 45837 from derivative at +0.04: " << endl; cout << (ab1(45837.04) - 0.04 * ab1.derivative(45837.04)) * (C::c / factor) << endl; cout << "B1950 aberration for 44238: " << endl; cout << ab2(44238.) * (C::c / factor) << endl; cout << "B1950 aberration for 44238 from derivative at +0.1: " << endl; cout << (ab2(44238.1) - 0.1 * ab2.derivative(44238.1)) * (C::c / factor) << endl; cout << "B1950 aberration for 44238 from derivative at +0.04: " << endl; cout << (ab2(44238.04) - 0.04 * ab2.derivative(44238.04)) * (C::c / factor) << endl; cout << "J2000 aberration: " << endl; for (eq=45837.; eq<45884.; eq++) { cout << eq+0.5 << ": " << ab1(eq) * (C::c / factor) << endl; } RotMatrix fromE = MeasTable::posToRect(); cout << "Rotation matrix from ecliptic: " << fromE << endl; cout << "J2000 barycentre Earth: " << endl; for (eq=45837.; eq<45884.; eq++) { MVPosition mypcd; mypcd = sp1.baryEarth(eq) * facAU; cout << eq+0.5 << ": " << mypcd << endl; } cout << "J2000 barycentre Sun: " << endl; for (eq=45837.; eq<45884.; eq++) { MVPosition mypcd; mypcd = sp1.barySun(eq) * facAU; cout << eq+0.5 << ": " << mypcd << endl; } cout << "J2000 geocentric Sun at 45837: " << sp1(45837.) * facAU << endl; cout << "J2000 geocentric Sun at 45837 from derivative at +0.1: " << (sp1(45837.1) - 0.1 * sp1.derivative(45837.1)) * facAU << endl; cout << "J2000 geocentric Sun at 45837 from derivative at +0.04: " << (sp1(45837.04) - 0.04 * sp1.derivative(45837.04)) * facAU << endl; cout << "J2000 geocentric Sun: " << endl; for (eq=45837.; eq<45884.; eq++) { MVPosition mypcd; mypcd = sp1(eq) * facAU; cout << eq+0.5 << ": " << mypcd << endl; } } catch (std::exception& x) { cout << x.what() << endl; } try { cout << "MVEpoch checks ------------------------" << endl; cout << "5.3: " << MVEpoch(5.3).get() << " -- " << MVEpoch(5.3) << endl; cout << "5.3 + 10.9: " << MVEpoch(5.3,10.9) << endl; cout << "5.3 + 10.9: " << MVEpoch(5.3)+MVEpoch(10.9) << endl; cout << "1.123 years: " << MVEpoch(Quantity(1.123,"a")) << endl; } catch (std::exception& x) { cout << x.what() << endl; } try { cout << "Separation and near checks -----------" << endl; MVDirection dc1(0.1, 0.2); MVDirection dc2(0.1, 0.20001); MVDirection dc3(0.1000001, 0.2); cout << "Separation between (0.1, 0.2) and (0.1, 0.20001): " << dc1.separation(dc2) << endl; cout << "Separation between (0.1, 0.2) and (0.1000001, 0.2): " << dc1.separation(dc3) << endl; cout << "Separation between (0.1, 0.2) and (0.1000001, 0.2): " << dc1.separation(dc3,"arcsec") << endl; cout << "Near 1.00 \" (0.1,0.2) and (0.1000001, 0.2): " << dc1.near(dc3, Quantity(1.0, "arcsec")) << endl; cout << "Near 0.01 \" (0.1,0.2) and (0.1000001, 0.2): " << dc1.near(dc3, Quantity(0.01, "arcsec")) << endl; } catch (std::exception& x) { cout << x.what() << endl; } return(0); } casacore-3.7.1/measures/Measures/test/tMeasMath.out000066400000000000000000000453631476623553700223420ustar00rootroot00000000000000Test measure math (MeasMath) class ... -------------------------------------- Euler and RotMatrix ... -------------------------------------- Euler(0.1,0.2,0.3): [0.1, 0.2, 0.3] -Euler(0.1,0.2,0.3): [-0.3, -0.2, -0.1] Euler(0.1,0.2,0.3): [0.1, 0.2, 0.3] Rotation none: Axis Lengths: [3, 3] (NB: Matrix in Row/Column order) [1, 0, 0 0, 1, 0 0, 0, 1] Rotation squared: Axis Lengths: [3, 3] (NB: Matrix in Row/Column order) [1, 0, 0 0, 1, 0 0, 0, 1] Rotation from Euler: Axis Lengths: [3, 3] (NB: Matrix in Row/Column order) [0.936293, -0.289629, 0.198669 0.312992, 0.944702, -0.0978434 -0.159345, 0.153792, 0.97517] I-Rotation from Euler: Axis Lengths: [3, 3] (NB: Matrix in Row/Column order) [0.936293, 0.312992, -0.159345 -0.289629, 0.944702, 0.153792 0.198669, -0.0978434, 0.97517] Euler(1,2): [1, 2, 0] Euler(10 deg, 20 deg): [0.174533, 0.349066, 0] Euler(30, 40 arcsec): [0.000145444, 0.000193925, 0] Direction cosines (MVDirection)... -------------------------------------- MVDirection default: [0, 0, 1] MVDirection(1,2,3): [0.267261, 0.534522, 0.801784] Normalised: [0.267261, 0.534522, 0.801784] MVDirection(.1,.2): [0.97517, 0.0978434, 0.198669] Last 2 *: 0.472215 Re-angle: [0.1, 0.2] 10 deg, 20 deg: [0.925417, 0.163176, 0.34202] Re-angle: [5.72958, 11.4592] deg Shifts (MVDirection)... -------------------------------------- Start: [1, 0, 0] dl = 10 deg: [0.984808, 0.173648, 0] ... true: [0.984808, 0.173648, 0] db = 10 deg: [0.984808, 0, 0.173648] ... true: [0.984808, 0, 0.173648] dl,b = 10 deg:[0.969846, 0.17101, 0.173648] ... true: [0.969846, 0.173648, 0.17101] Start: [0.5, 0, 0.866025] dl = 10 deg: [0.492404, 0.0868241, 0.866025] ... true: [0.492404, 0.173648, 0.852869] db = 10 deg: [0.34202, 0, 0.939693] ... true: [0.34202, 0, 0.939693] dl,b = 10 deg:[0.336824, 0.0593912, 0.939693] ... true: [0.336824, 0.173648, 0.925417] Start: [0.433013, 0.25, 0.866025] dl = 10 deg: [0.383022, 0.321394, 0.866025] ... true: [0.33961, 0.396586, 0.852869] db = 10 deg: [0.296198, 0.17101, 0.939693] ... true: [0.296198, 0.17101, 0.939693] dl,b = 10 deg:[0.262003, 0.219846, 0.939693] ... true: [0.204874, 0.318796, 0.925417] Positions (MVPosition)... -------------------------------------- MVPosition default: [0, 0, 0] MVPosition(1,2,3): [1, 2, 3] Normalised: [1, 2, 3] MVPosition(5km.1,.2): [4875.85, 489.217, 993.347] Last * DC(0.1,0.2): 1.76687 Re-angle: [5000, 0.1, 0.2] 6 mm, 10 deg, 20 deg: [0.0055525, 0.000979055, 0.00205212] Re-angle: [5.72958, 11.4592] deg Length: 5000 m Length in dam: 500 dam Euler(10 deg, 20 m): Quantum::assure non-conforming unit type 'm' Rotate (10,20 deg) over 0,0,30 deg: [40, 20] deg B1950 precession MJD 45700(1984/01/01): [-783.709, 681.388, -783.801] '' B1950 precession MJD 45700 using derivative from 45701: [-783.709, 681.388, -783.801] '' with rotation matrix: Axis Lengths: [3, 3] (NB: Matrix in Row/Column order) [0.999966, 0.00759941, 0.00330343 -0.00759941, 0.999971, -1.25516e-05 -0.00330343, -1.2553e-05, 0.999995] J2000 precession J1984.5: [357.457, -310.678, 357.438] '' with rotation matrix: Axis Lengths: [3, 3] (NB: Matrix in Row/Column order) [0.999993, -0.0034659, -0.00150621 0.0034659, 0.999994, -2.61026e-06 0.00150621, -2.61012e-06, 0.999999] J2000 nutation J1984.5: [84388.7, 14.6939, -84392.1] "-3.4427 J2000 nutation J1984.5 derivative from +0.1 day: [84388.7, 14.6935, -84392.1] " J2000 nutation J1984.5 derivative from +0.04 day: [84388.7, 14.6938, -84392.1] " with rotation matrix: Axis Lengths: [3, 3] (NB: Matrix in Row/Column order) [1, -6.53579e-05, -2.83402e-05 6.53584e-05, 1, 1.66898e-05 2.83391e-05, -1.66916e-05, 1] with rotation matrix combined 45882.5: Axis Lengths: [3, 3] (NB: Matrix in Row/Column order) [0.999993, -0.00353239, -0.00153504 0.00353242, 0.999994, 1.36022e-05 0.00153498, -1.90245e-05, 0.999999] or:Axis Lengths: [3, 3] (NB: Matrix in Row/Column order) [0.999993, -0.00353237, -0.0015351 0.00353239, 0.999994, 1.36024e-05 0.00153504, -1.90248e-05, 0.999999] equation of equinoxes 45882.5: -13.5737 '' equation at 45882.5 from derivative at 45882.7: -13.5727 '' equation at 45882.5 from derivative at 45882.54: -13.5737 '' J2000 nutation: 45837.5: [84388.8, 16.8666, -84392.1] "-3.30772 45838.5: [84388.8, 16.7303, -84392] "-3.28351 45839.5: [84388.8, 16.6, -84392] "-3.28194 45840.5: [84388.8, 16.4959, -84392.1] "-3.29909 45841.5: [84388.8, 16.4306, -84392.1] "-3.32841 45842.5: [84388.8, 16.4089, -84392.1] "-3.36247 45843.5: [84388.8, 16.4284, -84392.1] "-3.39408 45844.5: [84388.8, 16.4811, -84392.2] "-3.41692 45845.5: [84388.8, 16.5552, -84392.2] "-3.42609 45846.5: [84388.8, 16.6354, -84392.2] "-3.41859 45847.5: [84388.8, 16.7054, -84392.1] "-3.39386 45848.5: [84388.7, 16.7484, -84392.1] "-3.35406 45849.5: [84388.7, 16.75, -84392.1] "-3.30422 45850.5: [84388.7, 16.701, -84392] "-3.25189 45851.5: [84388.7, 16.6007, -84392] "-3.20635 45852.5: [84388.7, 16.4594, -84391.9] "-3.17691 45853.5: [84388.7, 16.2971, -84391.9] "-3.17052 45854.5: [84388.7, 16.1412, -84391.9] "-3.18946 45855.5: [84388.7, 16.0187, -84392] "-3.22997 45856.5: [84388.7, 15.9495, -84392] "-3.28263 45857.5: [84388.7, 15.9402, -84392.1] "-3.33454 45858.5: [84388.7, 15.9814, -84392.1] "-3.37272 45859.5: [84388.7, 16.0506, -84392.1] "-3.38761 45860.5: [84388.7, 16.1176, -84392.1] "-3.3757 45861.5: [84388.7, 16.1531, -84392.1] "-3.34047 45862.5: [84388.7, 16.1366, -84392] "-3.29132 45863.5: [84388.7, 16.062, -84392] "-3.24087 45864.5: [84388.7, 15.9378, -84391.9] "-3.20135 45865.5: [84388.7, 15.7843, -84391.9] "-3.18148 45866.5: [84388.7, 15.6265, -84391.9] "-3.18467 45867.5: [84388.7, 15.4877, -84391.9] "-3.20908 45868.5: [84388.7, 15.3848, -84392] "-3.24899 45869.5: [84388.7, 15.3263, -84392] "-3.29686 45870.5: [84388.7, 15.3124, -84392.1] "-3.34488 45871.5: [84388.7, 15.337, -84392.1] "-3.38614 45872.5: [84388.7, 15.3888, -84392.1] "-3.41519 45873.5: [84388.7, 15.4534, -84392.1] "-3.42847 45874.5: [84388.7, 15.5143, -84392.1] "-3.42471 45875.5: [84388.7, 15.555, -84392.1] "-3.40515 45876.5: [84388.7, 15.5602, -84392.1] "-3.37369 45877.5: [84388.7, 15.5185, -84392] "-3.33688 45878.5: [84388.7, 15.4252, -84392] "-3.30346 45879.5: [84388.7, 15.2852, -84392] "-3.28317 45880.5: [84388.7, 15.115, -84392] "-3.28461 45881.5: [84388.7, 14.9413, -84392] "-3.31242 45882.5: [84388.7, 14.7948, -84392.1] "-3.36492 45883.5: [84388.7, 14.7011, -84392.1] "-3.43361 B1950 nutation: 40632.5: [84395.4, -6.21514, -84404.2] "-8.83579 0.380134 ''/(''/s) 40633.5: [84395.4, -6.29397, -84404.2] "-8.84304 0.384955 ''/(''/s) 40634.5: [84395.4, -6.36774, -84404.3] "-8.86776 0.389467 ''/(''/s) 40635.5: [84395.4, -6.42252, -84404.3] "-8.90784 0.392817 ''/(''/s) 40636.5: [84395.4, -6.44722, -84404.4] "-8.95894 0.394328 ''/(''/s) 40637.5: [84395.4, -6.435, -84404.4] "-9.01503 0.393581 ''/(''/s) 40638.5: [84395.4, -6.38422, -84404.5] "-9.06922 0.390475 ''/(''/s) 40639.5: [84395.4, -6.29898, -84404.5] "-9.11474 0.385261 ''/(''/s) 40640.5: [84395.4, -6.18894, -84404.5] "-9.14589 0.378531 ''/(''/s) 40641.5: [84395.4, -6.06854, -84404.6] "-9.15896 0.371167 ''/(''/s) 40642.5: [84395.4, -5.95505, -84404.5] "-9.15299 0.364226 ''/(''/s) 40643.5: [84395.4, -5.86606, -84404.5] "-9.13017 0.358783 ''/(''/s) 40644.5: [84395.4, -5.81667, -84404.5] "-9.09591 0.355762 ''/(''/s) 40645.5: [84395.4, -5.81673, -84404.4] "-9.05847 0.355766 ''/(''/s) 40646.5: [84395.4, -5.86793, -84404.4] "-9.02793 0.358897 ''/(''/s) 40647.5: [84395.4, -5.96113, -84404.4] "-9.01443 0.364598 ''/(''/s) 40648.5: [84395.4, -6.07534, -84404.4] "-9.02544 0.371583 ''/(''/s) 40649.5: [84395.4, -6.18056, -84404.4] "-9.06284 0.378019 ''/(''/s) 40650.5: [84395.4, -6.24548, -84404.5] "-9.12086 0.381989 ''/(''/s) 40651.5: [84395.4, -6.24818, -84404.6] "-9.18677 0.382154 ''/(''/s) 40652.5: [84395.4, -6.18486, -84404.6] "-9.24437 0.378281 ''/(''/s) 40653.5: [84395.4, -6.07185, -84404.7] "-9.27961 0.37137 ''/(''/s) 40654.5: [84395.4, -5.93931, -84404.7] "-9.28539 0.363263 ''/(''/s) 40655.5: [84395.4, -5.81973, -84404.6] "-9.26351 0.355949 ''/(''/s) 40656.5: [84395.4, -5.73719, -84404.6] "-9.22299 0.350901 ''/(''/s) 40657.5: [84395.4, -5.70174, -84404.6] "-9.17599 0.348733 ''/(''/s) 40658.5: [84395.4, -5.71011, -84404.5] "-9.13383 0.349244 ''/(''/s) 40659.5: [84395.4, -5.75012, -84404.5] "-9.1045 0.351692 ''/(''/s) 40660.5: [84395.4, -5.80572, -84404.5] "-9.09214 0.355092 ''/(''/s) 40661.5: [84395.4, -5.86046, -84404.5] "-9.09764 0.35844 ''/(''/s) 40662.5: [84395.4, -5.89963, -84404.5] "-9.11923 0.360836 ''/(''/s) 40663.5: [84395.4, -5.91153, -84404.5] "-9.15301 0.361564 ''/(''/s) 40664.5: [84395.4, -5.88851, -84404.6] "-9.19334 0.360156 ''/(''/s) 40665.5: [84395.4, -5.82779, -84404.6] "-9.23348 0.356442 ''/(''/s) 40666.5: [84395.4, -5.732, -84404.6] "-9.26652 0.350584 ''/(''/s) 40667.5: [84395.4, -5.60929, -84404.6] "-9.28626 0.343078 ''/(''/s) 40668.5: [84395.4, -5.47298, -84404.6] "-9.28819 0.334741 ''/(''/s) 40669.5: [84395.4, -5.34026, -84404.6] "-9.27041 0.326623 ''/(''/s) 40670.5: [84395.4, -5.22966, -84404.6] "-9.23431 0.319859 ''/(''/s) 40671.5: [84395.4, -5.15761, -84404.5] "-9.18494 0.315452 ''/(''/s) 40672.5: [84395.4, -5.13469, -84404.5] "-9.13051 0.314051 ''/(''/s) 40673.5: [84395.4, -5.16282, -84404.4] "-9.081 0.315771 ''/(''/s) 40674.5: [84395.4, -5.23372, -84404.4] "-9.04619 0.320107 ''/(''/s) 40675.5: [84395.4, -5.32919, -84404.4] "-9.03335 0.325947 ''/(''/s) 40676.5: [84395.4, -5.4237, -84404.4] "-9.04514 0.331727 ''/(''/s) 40677.5: [84395.4, -5.48959, -84404.4] "-9.07818 0.335757 ''/(''/s) J2000 nutation 50449.5: [84382.9, -1.38426, -84373.1] ''9.75274 J2000 aberration for 45837: [1.40505e+06, -876895, -380118] J2000 aberration for 45837 from derivative at +0.1: [1.40505e+06, -876896, -380119] J2000 aberration for 45837 from derivative at +0.04: [1.40505e+06, -876895, -380118] B1950 aberration for 44238: [-1.72787e+06, -243406, -105560] B1950 aberration for 44238 from derivative at +0.1: [-1.72788e+06, -243406, -105560] B1950 aberration for 44238 from derivative at +0.04: [-1.72787e+06, -243406, -105560] J2000 aberration: 45837.5: [1.40505e+06, -876895, -380118] 45838.5: [1.4208e+06, -854798, -370543] 45839.5: [1.43617e+06, -832463, -360867] 45840.5: [1.45115e+06, -809891, -351091] 45841.5: [1.46574e+06, -787082, -341214] 45842.5: [1.47993e+06, -764039, -331238] 45843.5: [1.4937e+06, -740768, -321163] 45844.5: [1.50705e+06, -717273, -310991] 45845.5: [1.51997e+06, -693560, -300724] 45846.5: [1.53246e+06, -669635, -290364] 45847.5: [1.54449e+06, -645503, -279912] 45848.5: [1.55607e+06, -621174, -269372] 45849.5: [1.56718e+06, -596654, -258747] 45850.5: [1.57782e+06, -571954, -248040] 45851.5: [1.58798e+06, -547086, -237257] 45852.5: [1.59765e+06, -522063, -226402] 45853.5: [1.60682e+06, -496900, -215484] 45854.5: [1.61549e+06, -471612, -204508] 45855.5: [1.62365e+06, -446216, -193482] 45856.5: [1.63132e+06, -420728, -182414] 45857.5: [1.6385e+06, -395162, -171312] 45858.5: [1.6452e+06, -369530, -160180] 45859.5: [1.65141e+06, -343842, -149026] 45860.5: [1.65717e+06, -318106, -137852] 45861.5: [1.66246e+06, -292325, -126662] 45862.5: [1.66731e+06, -266500, -115456] 45863.5: [1.67172e+06, -240632, -104236] 45864.5: [1.67569e+06, -214718, -93000.4] 45865.5: [1.67923e+06, -188756, -81747.9] 45866.5: [1.68233e+06, -162745, -70477.5] 45867.5: [1.68499e+06, -136685, -59188.3] 45868.5: [1.6872e+06, -110577, -47880.1] 45869.5: [1.68896e+06, -84422.2, -36553.4] 45870.5: [1.69027e+06, -58226.1, -25209.4] 45871.5: [1.69111e+06, -31993.6, -13849.8] 45872.5: [1.69148e+06, -5730.67, -2476.8] 45873.5: [1.69138e+06, 20556.1, 8907.22] 45874.5: [1.6908e+06, 46859.9, 20299.9] 45875.5: [1.68973e+06, 73173.4, 31698.7] 45876.5: [1.68817e+06, 99488.5, 43100.9] 45877.5: [1.68612e+06, 125796, 54503.1] 45878.5: [1.68357e+06, 152085, 65901] 45879.5: [1.6805e+06, 178343, 77289.6] 45880.5: [1.67693e+06, 204554, 88662.2] 45881.5: [1.67284e+06, 230704, 100012] 45882.5: [1.66823e+06, 256774, 111330] 45883.5: [1.66312e+06, 282749, 122609] Rotation matrix from ecliptic: Axis Lengths: [3, 3] (NB: Matrix in Row/Column order) [1, 4.50877e-07, 0 -4.13671e-07, 0.917482, -0.397777 -1.79348e-07, 0.397777, 0.917482] J2000 barycentre Earth: 45837.5: [-0.556335, -0.765206, -0.331964] 45838.5: [-0.542205, -0.773864, -0.335717] 45839.5: [-0.52792, -0.782301, -0.339374] 45840.5: [-0.513483, -0.790513, -0.342934] 45841.5: [-0.498899, -0.798498, -0.346396] 45842.5: [-0.48417, -0.806254, -0.349758] 45843.5: [-0.469301, -0.813778, -0.353021] 45844.5: [-0.454297, -0.821069, -0.356181] 45845.5: [-0.439162, -0.828123, -0.35924] 45846.5: [-0.4239, -0.834939, -0.362196] 45847.5: [-0.408515, -0.841515, -0.365047] 45848.5: [-0.393011, -0.847849, -0.367794] 45849.5: [-0.377395, -0.853938, -0.370434] 45850.5: [-0.361669, -0.859782, -0.372969] 45851.5: [-0.34584, -0.865377, -0.375395] 45852.5: [-0.329911, -0.870723, -0.377714] 45853.5: [-0.313889, -0.875818, -0.379923] 45854.5: [-0.297777, -0.880661, -0.382023] 45855.5: [-0.281581, -0.88525, -0.384013] 45856.5: [-0.265306, -0.889585, -0.385893] 45857.5: [-0.248956, -0.893664, -0.387662] 45858.5: [-0.232537, -0.897488, -0.389319] 45859.5: [-0.216054, -0.901055, -0.390865] 45860.5: [-0.199511, -0.904365, -0.3923] 45861.5: [-0.182912, -0.907417, -0.393622] 45862.5: [-0.166263, -0.910211, -0.394833] 45863.5: [-0.149567, -0.912747, -0.395931] 45864.5: [-0.13283, -0.915024, -0.396918] 45865.5: [-0.116055, -0.917041, -0.397791] 45866.5: [-0.0992472, -0.918799, -0.398553] 45867.5: [-0.0824103, -0.920296, -0.399201] 45868.5: [-0.0655491, -0.921533, -0.399736] 45869.5: [-0.048668, -0.922508, -0.400159] 45870.5: [-0.0317715, -0.923221, -0.400468] 45871.5: [-0.0148643, -0.923672, -0.400663] 45872.5: [0.00204894, -0.923861, -0.400745] 45873.5: [0.0189635, -0.923787, -0.400713] 45874.5: [0.0358747, -0.92345, -0.400567] 45875.5: [0.0527776, -0.92285, -0.400307] 45876.5: [0.0696674, -0.921987, -0.399933] 45877.5: [0.0865392, -0.92086, -0.399445] 45878.5: [0.103388, -0.919471, -0.398843] 45879.5: [0.120209, -0.917819, -0.398127] 45880.5: [0.136996, -0.915904, -0.397297] 45881.5: [0.153745, -0.913728, -0.396354] 45882.5: [0.170451, -0.911291, -0.395297] 45883.5: [0.187108, -0.908593, -0.394128] J2000 barycentre Sun: 45837.5: [0.00188316, 0.00857178, 0.00354321] 45838.5: [0.00187472, 0.00857196, 0.00354349] 45839.5: [0.00186627, 0.00857213, 0.00354376] 45840.5: [0.00185783, 0.00857229, 0.00354404] 45841.5: [0.00184939, 0.00857244, 0.00354431] 45842.5: [0.00184095, 0.00857258, 0.00354457] 45843.5: [0.00183251, 0.00857271, 0.00354483] 45844.5: [0.00182407, 0.00857283, 0.00354508] 45845.5: [0.00181563, 0.00857294, 0.00354534] 45846.5: [0.00180719, 0.00857304, 0.00354558] 45847.5: [0.00179876, 0.00857313, 0.00354582] 45848.5: [0.00179032, 0.00857321, 0.00354606] 45849.5: [0.00178189, 0.00857328, 0.0035463] 45850.5: [0.00177346, 0.00857334, 0.00354653] 45851.5: [0.00176503, 0.00857339, 0.00354675] 45852.5: [0.0017566, 0.00857343, 0.00354697] 45853.5: [0.00174817, 0.00857346, 0.00354719] 45854.5: [0.00173974, 0.00857348, 0.0035474] 45855.5: [0.00173131, 0.00857349, 0.00354761] 45856.5: [0.00172288, 0.00857349, 0.00354782] 45857.5: [0.00171446, 0.00857349, 0.00354802] 45858.5: [0.00170604, 0.00857347, 0.00354821] 45859.5: [0.00169761, 0.00857344, 0.00354841] 45860.5: [0.00168919, 0.0085734, 0.0035486] 45861.5: [0.00168077, 0.00857336, 0.00354878] 45862.5: [0.00167235, 0.0085733, 0.00354896] 45863.5: [0.00166393, 0.00857324, 0.00354914] 45864.5: [0.00165551, 0.00857316, 0.00354931] 45865.5: [0.0016471, 0.00857308, 0.00354948] 45866.5: [0.00163868, 0.00857299, 0.00354964] 45867.5: [0.00163027, 0.00857288, 0.0035498] 45868.5: [0.00162185, 0.00857277, 0.00354996] 45869.5: [0.00161344, 0.00857265, 0.00355011] 45870.5: [0.00160502, 0.00857252, 0.00355026] 45871.5: [0.00159661, 0.00857238, 0.0035504] 45872.5: [0.0015882, 0.00857223, 0.00355054] 45873.5: [0.00157979, 0.00857207, 0.00355068] 45874.5: [0.00157138, 0.0085719, 0.00355081] 45875.5: [0.00156297, 0.00857172, 0.00355094] 45876.5: [0.00155456, 0.00857154, 0.00355106] 45877.5: [0.00154615, 0.00857134, 0.00355118] 45878.5: [0.00153775, 0.00857113, 0.0035513] 45879.5: [0.00152934, 0.00857092, 0.00355141] 45880.5: [0.00152093, 0.00857069, 0.00355152] 45881.5: [0.00151253, 0.00857046, 0.00355162] 45882.5: [0.00150412, 0.00857022, 0.00355172] 45883.5: [0.00149572, 0.00856996, 0.00355182] J2000 geocentric Sun at 45837: [0.558218, 0.773778, 0.335507] J2000 geocentric Sun at 45837 from derivative at +0.1: [0.558219, 0.773779, 0.335508] J2000 geocentric Sun at 45837 from derivative at +0.04: [0.558218, 0.773778, 0.335507] J2000 geocentric Sun: 45837.5: [0.558218, 0.773778, 0.335507] 45838.5: [0.54408, 0.782436, 0.339261] 45839.5: [0.529787, 0.790873, 0.342918] 45840.5: [0.515341, 0.799085, 0.346478] 45841.5: [0.500748, 0.807071, 0.34994] 45842.5: [0.486011, 0.814827, 0.353303] 45843.5: [0.471134, 0.822351, 0.356565] 45844.5: [0.456121, 0.829642, 0.359727] 45845.5: [0.440978, 0.836696, 0.362786] 45846.5: [0.425707, 0.843512, 0.365741] 45847.5: [0.410313, 0.850088, 0.368593] 45848.5: [0.394802, 0.856422, 0.37134] 45849.5: [0.379177, 0.862512, 0.373981] 45850.5: [0.363443, 0.868355, 0.376515] 45851.5: [0.347605, 0.87395, 0.378942] 45852.5: [0.331668, 0.879296, 0.381261] 45853.5: [0.315637, 0.884391, 0.38347] 45854.5: [0.299517, 0.889234, 0.385571] 45855.5: [0.283312, 0.893823, 0.387561] 45856.5: [0.267028, 0.898158, 0.389441] 45857.5: [0.250671, 0.902238, 0.39121] 45858.5: [0.234243, 0.906061, 0.392867] 45859.5: [0.217751, 0.909628, 0.394414] 45860.5: [0.2012, 0.912938, 0.395848] 45861.5: [0.184593, 0.91599, 0.397171] 45862.5: [0.167935, 0.918785, 0.398382] 45863.5: [0.151231, 0.92132, 0.399481] 45864.5: [0.134486, 0.923597, 0.400467] 45865.5: [0.117702, 0.925614, 0.401341] 45866.5: [0.100886, 0.927372, 0.402102] 45867.5: [0.0840406, 0.928869, 0.402751] 45868.5: [0.067171, 0.930105, 0.403286] 45869.5: [0.0502814, 0.93108, 0.403709] 45870.5: [0.0333765, 0.931794, 0.404018] 45871.5: [0.0164609, 0.932245, 0.404213] 45872.5: [-0.000460735, 0.932433, 0.404295] 45873.5: [-0.0173837, 0.932359, 0.404263] 45874.5: [-0.0343033, 0.932022, 0.404117] 45875.5: [-0.0512146, 0.931422, 0.403858] 45876.5: [-0.0681128, 0.930558, 0.403484] 45877.5: [-0.084993, 0.929432, 0.402996] 45878.5: [-0.10185, 0.928042, 0.402394] 45879.5: [-0.118679, 0.92639, 0.401678] 45880.5: [-0.135475, 0.924475, 0.400849] 45881.5: [-0.152233, 0.922298, 0.399905] 45882.5: [-0.168947, 0.919861, 0.398849] 45883.5: [-0.185612, 0.917163, 0.397679] MVEpoch checks ------------------------ 5.3: 5.3 -- 5::07:11:60.0000 5.3 + 10.9: 16::04:48:00.0000 5.3 + 10.9: 16::04:48:00.0000 1.123 years: 410::04:13:04.8000 Separation and near checks ----------- Separation between (0.1, 0.2) and (0.1, 0.20001): 1e-05 Separation between (0.1, 0.2) and (0.1000001, 0.2): 9.80067e-08 Separation between (0.1, 0.2) and (0.1000001, 0.2): 0.0202153 arcsec Near 1.00 " (0.1,0.2) and (0.1000001, 0.2): 1 Near 0.01 " (0.1,0.2) and (0.1000001, 0.2): 0 casacore-3.7.1/measures/Measures/test/tMeasure.cc000066400000000000000000000670701476623553700220210ustar00rootroot00000000000000//# tMeasure.cc: This program test Measure functions //# Copyright (C) 1995-2000,2002,2007 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main() { try { cout << "Test measure class MVAngle" << endl; cout << "--------------------------------------" << endl; MVAngle mva1 = Quantity(190,"deg") + Quantity(59,"'") + Quantity(59.95,"\""); cout << "Degrees: " << mva1.degree() << endl; cout << "Radians: " << mva1.radian() << endl; cout << "Fraction: " << mva1.circle() << endl; cout << "Degrees: " << mva1().degree() << endl; cout << "Radians: " << mva1().radian() << endl; cout << "Fraction: " << mva1().circle() << endl; cout << "Degrees: " << mva1(+2).degree() << endl; cout << "Degrees: " << mva1(-2).degree() << endl; cout << "Fraction: " << mva1(0).circle() << endl; cout << "Prec =2=: " << mva1.string(2) << endl; cout << "Prec =4=: " << mva1.string(4) << endl; cout << "Prec =6=: " << mva1.string(6) << endl; cout << "Prec =8=: " << mva1.string(8) << endl; cout << "Clean=2=: " << mva1.string(MVAngle::ANGLE_CLEAN, 2) << endl; cout << "Time = =: " << mva1.string(MVAngle::TIME) << endl; cout << "CNO_H= =: " << mva1.string(MVAngle::TIME_CLEAN_NO_H) << endl; MVTime mtim = 44362.6; cout << "Days: " << mtim.day() << endl; cout << "Hours: " << mtim.hour() << endl; cout << "Seconds: " << mtim.second() << endl; cout << "Minutes: " << mtim.minute() << endl; cout << "Prec =6=: " << mtim.string(6) << endl; cout << "Day: " << mtim.string((MVTime::formatTypes) (MVTime::TIME | MVTime::DAY), 6) << endl; cout << "YMD: " << mtim.string(MVTime::YMD, 6) << endl; cout << "DMY: " << mtim.string(MVTime::DMY, 6) << endl; cout << "Day+DMY: " << mtim.string((MVTime::formatTypes) (MVTime::DMY | MVTime::DAY), 6) << endl; cout << "Day+YMD: " << mtim.string((MVTime::formatTypes) (MVTime::YMD | MVTime::DAY), 6) << endl; cout << "Weekday: " << mtim.weekday() << endl; cout << "yyyy mm dd: " << mtim.year() << " " << mtim.month() << " " << mtim.monthday() << endl; cout << "yyyymmdd: " << mtim.ymd() << endl; cout << "Yearday: " << mtim.yearday() << endl; cout << "Yearweek: " << mtim.yearweek() << endl; cout << "Week 960101:" << MVTime(1996,1,1).yearweek() << endl; cout << "Read it back:" << endl; mtim = MVTime(1980,5,3.6); cout << "Prec =6=: " << mtim.string(6) << endl; cout << "Day: " << mtim.string((MVTime::formatTypes) (MVTime::TIME | MVTime::DAY), 6) << endl; cout << "YMD: " << mtim.string(MVTime::YMD, 6) << endl; cout << "DMY: " << mtim.string(MVTime::DMY, 6) << endl; cout << "Day+DMY: " << mtim.string((MVTime::formatTypes) (MVTime::DMY | MVTime::DAY), 6) << endl; cout << "Day+YMD: " << mtim.string((MVTime::formatTypes) (MVTime::YMD | MVTime::DAY), 6) << endl; cout << "Day (NO_T): " << mtim.string((MVTime::formatTypes) (MVTime::TIME | MVTime::DAY | MVTime::NO_TIME), 6) << endl; cout << "Day+YMD: " << mtim.string((MVTime::formatTypes) (MVTime::YMD | MVTime::DAY | MVTime::NO_TIME), 6) << endl; cout << "Day+YMD: " << mtim.string((MVTime::YMD | MVTime::DAY | MVTime::NO_TIME), 6) << endl; cout << "\nTest measure class ..." << endl; cout << "--------------------------------------" << endl; cout << endl << "MDirection state transition matrix:\n" << endl; cout << MCDirection::showState() << endl; cout << endl << "MPosition state transition matrix:\n" << endl; cout << MCPosition::showState() << endl; cout << endl << "MEpoch state transition matrix:\n" << endl; cout << MCEpoch::showState() << endl; cout << endl << "MFrequency state transition matrix:\n" << endl; cout << MCFrequency::showState() << endl; cout << endl << "MRadialVelocity state transition matrix:\n" << endl; cout << MCRadialVelocity::showState() << endl; cout << endl << "MDoppler state transition matrix:\n" << endl; cout << MCDoppler::showState() << endl; MEpoch tbm(Quantity(MeasData::MJDB1950,"d"),MEpoch::Ref()); cout << "Epoch B1950: " << tbm << endl; MEpoch::Ref tmref(MEpoch::TAI); MEpoch tm(Quantity(MeasData::MJD2000,"d"), tmref); cout << "Epoch 2000: " << tm << endl; cout << "Epoch reference: " << tmref << endl; MEpoch::Ref tbmref(MEpoch::TAI,tbm); MEpoch tm2(Quantity(MeasData::MJD2000-MeasData::MJDB1950,"d"), tbmref); cout << "Epoch 2000 ref B1950: " << tm2 << endl; cout << "Epoch reference: " << tbmref << endl; cout << "Test measure conversion ..." << endl; cout << "--------------------------------------" << endl; MEpoch::Convert tconv(tm2,tmref); cout << "Converted " << tm2 << endl << " to " << tmref << endl << " as " << tconv() << endl; MEpoch::Ref turef(MEpoch::UTC); MEpoch::Convert tconv2(tm2,turef); cout << "Converted " << tm2 << endl << " to " << turef << endl << " as " << tconv2() << endl; MEpoch::Ref tgsref(MEpoch::UT1); MEpoch tm3(Quantity(50082.0,"d"), tgsref); MEpoch tm4(Quantity(50082.72315521759259259259,"d"), tgsref); MEpoch tm5(Quantity(44238.0,"d"), tgsref); MEpoch::Ref tlsref(MEpoch::GMST1); MEpoch::Convert tconv3(tm3,tlsref); cout << "Converted " << tm3 << endl << " to " << tlsref << endl << " as " << tconv3() << endl; cout << "Converted " << tm4 << endl << " to " << tlsref << endl << " as " << tconv3(50082.72315521759259259259) << endl; cout << "Converted " << tm5 << endl << " to " << tlsref << endl << " as " << tconv3(44238.0) << endl; MEpoch tm6(tconv3(44238.0)); MEpoch::Convert tconv4(tm6,tgsref); cout << "Converted back " << tm6 << endl << " to " << tgsref << endl << " as " << tconv4() << endl; MEpoch::Ref tasref(MEpoch::GAST); MEpoch::Convert tconv5(tm3,tasref); cout << "Converted " << tm3 << endl << " to " << tasref << endl << " as " << tconv5() << endl; MEpoch tm7(tconv5()); MEpoch::Convert tconv6(tm7,tgsref); cout << "Converted back " << tm7 << endl << " to " << tgsref << endl << " as " << tconv6() << endl; { MDirection j2000; bool success = j2000.setRefString("J2000 "); cout << boolalpha << success << endl; } { MeasFrame b1900((MEpoch(Quantity(MeasData::MJDB1900,"d")))); MDirection lsr1900(Quantity(270,"deg"), Quantity(30,"deg"), MDirection::Ref(MDirection::BMEAN, b1900)); cout << "LSR (B1900): " << lsr1900.getValue().getAngle("deg") << endl; cout << "LSR (B1950): " << MDirection::Convert(lsr1900, MDirection::B1950)() .getValue().getAngle("deg") << endl; cout << "LSR (J2000): " << MDirection::Convert(lsr1900, MDirection::J2000)() .getValue().getAngle("deg") << endl; Vector vlsr1900(lsr1900.getValue().getValue()); if (nearAbs(vlsr1900(0), 0.0)) vlsr1900(0) = 0; cout << "LSR (B1900): " << vlsr1900 << endl; cout << "LSR (B1950): " << MDirection::Convert(lsr1900, MDirection::B1950)() .getValue() << endl; cout << "LSR (J2000): " << MDirection::Convert(lsr1900, MDirection::J2000)() .getValue() << endl; MeasFrame flsr1900(lsr1900); // Next one precision problems with cos(90 deg) in Linux // cout << "LSR frame: " << flsr1900 << endl; } { MDirection dirJ2000(Quantity(0,"deg"), Quantity(30,"deg"), MDirection::J2000); MEpoch ep(Quantity(50083.,"d")); MeasFrame frame(dirJ2000, ep); MRadialVelocity rvBary(Quantity(1000.,"km/s"), MRadialVelocity::Ref (MRadialVelocity::BARY, frame)); cout << "Radial velocity (BARY): " << rvBary << endl << " (GEO): " << MRadialVelocity::Convert(rvBary, MRadialVelocity::GEO)() .getValue() << endl; MRadialVelocity rvGeo = MRadialVelocity::Convert (rvBary, MRadialVelocity::GEO)(); rvGeo.set(MRadialVelocity::Ref(MRadialVelocity::GEO, frame)); cout << "and back: " << MRadialVelocity::Convert(rvGeo, MRadialVelocity::BARY)() .getValue() << endl; MPosition obs(Quantity(0,"m"), Quantity(-289375.79, Unit('"')), Quantity(50,"deg")); frame.set(obs); rvGeo.set(MVRadialVelocity(0.0)); cout << "and 0 (GEO) to TOPO: " << MRadialVelocity::Convert(rvGeo, MRadialVelocity::Ref (MRadialVelocity::TOPO, frame))() .getValue() << endl; MRadialVelocity rvTopo = MRadialVelocity::Convert(rvGeo, MRadialVelocity::Ref (MRadialVelocity::TOPO, frame))(); // The following necessary for errors in Intel chip Double mrvback(MRadialVelocity::Convert(rvTopo, MRadialVelocity::GEO)() .getValue()); if (nearAbs(mrvback, 0.0)) mrvback = 0; cout << "and back: " << mrvback << endl; rvBary.set(MVRadialVelocity(0.0)); cout << "and 0 (BARY) to LSR: " << MRadialVelocity::Convert(rvBary, MRadialVelocity::Ref (MRadialVelocity::LSRK, frame))() .getValue() << endl; MRadialVelocity rvLSR = MRadialVelocity::Convert (rvBary, MRadialVelocity::Ref (MRadialVelocity::LSRK, frame))(); // The following necessary for errors in Intel chip mrvback = MRadialVelocity::Convert(rvLSR, MRadialVelocity::BARY)() .getValue(); if (nearAbs(mrvback, 0.0, 3e-13)) mrvback = 0; cout << "and back: " << mrvback << endl; } { cout << "Test real radial velocity" << endl << "-----------------------------------------" < veqgal(eqgal().getValue().getValue()); if (nearAbs(veqgal(2), 1.0, 1e-10)) { veqgal(0) = 0; veqgal(1) = 0; }; cout << "Converted B1950 galactic pole " << gpole << endl << " to " << galref << endl << " as " << veqgal << endl; cout << "Converted B1950 galactic pole " << eqpole << endl << " to " << eqref << endl << " as " << galeq().getAngle("deg") << endl; MEpoch app(Quantity(50083.0,"d")); MeasFrame appframe; MDirection::Ref appref(MDirection::APP, appframe); MDirection::Ref j2000ref(MDirection::J2000); MVDirection j2000vec( -0.373798658, -0.312643465, -0.873228852); MDirection j2000(j2000vec); MDirection b1950(j2000vec, MDirection::B1950); MDirection::Convert j2000app(j2000,appref); appframe.set(app); MDirection appc(j2000app()); MDirection::Convert appj2000(appc,j2000ref); MDirection::Convert b1950j2000(b1950, MDirection::J2000); MDirection appj(b1950j2000()); MDirection::Convert j2000b1950(appj, MDirection::B1950); cout << "Converted J2000 coordinates " << j2000.getValue() << " to: " << j2000app().getValue() << endl; cout << "Converted back to J2000 coordinates " << appc.getValue() << " to: " << appj2000().getValue() << endl; cout << "Converted B1950 to J2000 coordinates " << b1950.getValue() << " to: " << b1950j2000().getValue() << endl; cout << "Converted J2000 to B1950 coordinates " << appj.getValue() << " to: " << j2000b1950(appj).getValue() << endl; MPosition wsrt(Quantity(16,"m"), Quantity(6.60417,"deg"), Quantity(52.91692,"deg"), MPosition::WGS84); MPosition::Convert wsrtitrf(wsrt, MPosition::ITRF); MPosition geod(wsrtitrf()); cout << "Converted geodetic position: " << endl << wsrt.getValue().getLength() << ", " << wsrt.getAngle("deg") << endl << "to: " << geod.getValue().getLength() << ", " << geod.getAngle("deg") << endl; MPosition::Convert wsrtwgs(geod, MPosition::WGS84); wsrt = wsrtwgs(); cout << "Converted geocentric position: " << endl << geod.getValue().getLength() << ", " << geod.getAngle("deg") << endl << "to: " << wsrt.getValue().getLength() << ", " << wsrt.getAngle("deg") << endl; { MEpoch tim(MVEpoch(50082), MEpoch::UT1); MeasFrame frame(wsrt, tim); MDirection appvec(MDirection::Convert(j2000vec, MDirection::Ref(MDirection::APP, frame))()); cout << "J2000 coordinates: " << j2000vec.getAngle("deg") << endl; cout << "Apparent coordinate" << appvec.getAngle("deg") << endl; Double d1, d2 , d3; frame.getLong(d1); frame.getLat(d2); frame.getLAST(d3); cout << "Longitude: " << MVAngle(d1).get("deg") << endl; cout << "Latitude: " << MVAngle(d2).get("deg") << endl; cout << "LAST: " << MVAngle(d3*C::circle). string(MVAngle::TIME, 8) << endl; cout << "LAST: " << MVAngle(d3*C::circle). string(MVAngle::ANGLE, 8) << endl; cout << "LAST: " << MEpoch::Convert(tim, MEpoch::Ref(MEpoch::LAST, frame))() << endl; cout << "HA/DEC: " << MDirection::Convert(appvec, MDirection::Ref(MDirection::HADEC, frame))() .getAngle("deg") << endl; MDirection had(MDirection::Convert(appvec, MDirection::Ref(MDirection::HADEC, frame))()); cout << "Back: " << MDirection::Convert(had, MDirection::Ref(MDirection::APP, frame))() .getAngle("deg") << endl; cout << "Az/El: " << MDirection::Convert(had, MDirection::Ref(MDirection::AZEL, frame))() .getAngle("deg") << endl; MDirection azel(MDirection::Convert(appvec, MDirection::Ref(MDirection::AZEL, frame))()); cout << "Back: " << MDirection::Convert(azel, MDirection::Ref(MDirection::HADEC, frame))() .getAngle("deg") << endl; }; { MEpoch app(Quantity(47165.8,"d")); MeasFrame appframe(app); MDirection::Ref appref(MDirection::APP, appframe); MDirection::Ref b1950ref(MDirection::B1950); MDirection bappc(Quantity(85.4267,"deg"), Quantity(49.8502,"deg"), appref); MDirection::Convert appb1950(bappc, b1950ref); MDirection b1950(appb1950()); MDirection::Convert b1950app(b1950, appref); MVDirection mvpole; MDirection bappc1(Quantity(85.4267,"deg"), Quantity(50.8502,"deg"), appref); cout << "Converted to B1950 coordinates " << bappc.getAngle("deg") << " to: " << appb1950().getAngle("deg") << endl; cout << "and back to: " << b1950app().getAngle("deg") << endl; MDirection apole(b1950app(mvpole)); cout << "Rotation angle: " << bappc.getValue().positionAngle(apole.getValue(), "deg") << endl; } MEpoch tbm(Quantity(50927.92931, "d")); MPosition pos(MVPosition(-4750915.84032, 2792906.17778, -3200483.75028), MPosition::ITRF); MDirection fmb0(MVDirection(0.5, 0.5, 0.5), MDirection::J2000); MRadialVelocity fmfrq0(MVRadialVelocity(100.), MRadialVelocity::LSRK); MeasFrame mf(tbm, pos, fmb0); mf.set(fmfrq0); Vector tvec(3); tvec = 0.0; { cout << "------------------------------------" << endl; cout << "Testing all MDirection conversions forward/backward" << endl; Bool isok = True; MVDirection mvd0(0.5, 0.5, 0.5); Double tp; for (uInt i=MDirection::J2000; i #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main() { try { cout << "----------------------------------------------------" << endl; cout << "Test MeasureHolder " << endl; cout << "----------------------------------------------------" << endl; String error; MeasureHolder q00; MDirection x00(Quantity(30, "deg"), Quantity(-40, "deg")); Record y00; cout << "Input value: " << x00 << " (" << x00.getRefString() << ")" << endl; if (MeasureHolder(x00).toRecord(error, y00)) { if (q00.fromRecord(error, y00)) { cout <<"Record output value: " << q00.asMeasure() << " (" << q00.asMeasure().getRefString() << ")" << endl; } else { cout << "From error: " << error << endl; }; } else { cout << "To error: " << error << endl; }; MeasureHolder q01 = q00; MeasureHolder q02(q00); if (q00.asMDirection().getValue().getValue()(0) != q01.asMDirection().getValue().getValue()(0) || q00.asMDirection().getValue().getValue()(0) != q02.asMDirection().getValue().getValue()(0)) { cout << "Error in copy constructor or assignment" << endl; }; cout << "Is measure: " << q00.isMeasure() << endl; cout << "Is direction: " << q00.isMDirection() << endl; cout << "Is empty: " << q00.isEmpty() << endl; cout << "Is epoch: " << q00.isMEpoch() << endl; cout << "Is doppler: " << q00.isMDoppler() << endl; cout << "Is position: " << q00.isMPosition() << endl; cout << "Is frequency: " << q00.isMFrequency() << endl; cout << "Is radialvelocity: " << q00.isMRadialVelocity() << endl; cout << "As measure: " << q00.asMeasure() << endl; cout << "As direction: " << q00.asMDirection() << endl; cout << "Error expected:" << endl; cout << "Input value: " << x00 << " (" << x00.getRefString() << ")" << endl; if (MeasureHolder(x00).toRecord(error, y00)) { y00.renameField("units", RecordFieldId("refer")); if (q00.fromRecord(error, y00)) { cout <<"Record output value: " << q00.asMeasure() << " (" << q00.asMeasure().getRefString() << ")" << endl; } else { cout << "From error: " << error << endl; }; } else { cout << "To error: " << error << endl; }; } catch (std::exception& x) { cout << x.what() << endl; } try { String error; MeasureHolder q00; MDirection x00(Quantity(30, "deg"), Quantity(-40, "deg")); Record y00; cout << "Input value: " << x00 << " (" << x00.getRefString() << ")" << endl; if (MeasureHolder(x00).toRecord(error, y00)) { if (q00.fromRecord(error, y00)) { cout <<"Record output value: " << q00.asMeasure() << " (" << q00.asMeasure().getRefString() << ")" << endl; } else { cout << "From error: " << error << endl; }; } else { cout << "To error: " << error << endl; }; cout << "As epoch: "; cout << q00.asMEpoch() << endl; } catch (std::exception& x) { cout << x.what() << endl; } try { { String error; MeasureHolder q00; MEpoch x00(Quantity(30456, "d")); Record y00; cout << "Input value: " << x00 << " (" << x00.getRefString() << ")" << endl; if (MeasureHolder(x00).toRecord(error, y00)) { if (q00.fromRecord(error, y00)) { cout <<"Record output value: " << q00.asMeasure() << " (" << q00.asMeasure().getRefString() << ")" << endl; } else { cout << "From error: " << error << endl; }; } else { cout << "To error: " << error << endl; }; cout << "As epoch: " << q00.asMEpoch() << endl; } { String error; MeasureHolder q00; MDoppler x00(Quantity(30456, "m/s")); Record y00; cout << "Input value: " << x00 << " (" << x00.getRefString() << ")" << endl; if (MeasureHolder(x00).toRecord(error, y00)) { if (q00.fromRecord(error, y00)) { cout <<"Record output value: " << q00.asMeasure() << " (" << q00.asMeasure().getRefString() << ")" << endl; } else { cout << "From error: " << error << endl; }; } else { cout << "To error: " << error << endl; }; cout << "As Doppler: " << q00.asMDoppler() << endl; } { String error; MeasureHolder q00; MFrequency x00(Quantity(30456, "MHz")); Record y00; cout << "Input value: " << x00 << " (" << x00.getRefString() << ")" << endl; if (MeasureHolder(x00).toRecord(error, y00)) { if (q00.fromRecord(error, y00)) { cout <<"Record output value: " << q00.asMeasure() << " (" << q00.asMeasure().getRefString() << ")" << endl; } else { cout << "From error: " << error << endl; }; } else { cout << "To error: " << error << endl; }; cout << "As Frequency: " << q00.asMFrequency() << endl; } { String error; MeasureHolder q00; MPosition x00(Quantity(6, "Mm"), Quantity(20, "deg"), Quantity(30, "deg")); Record y00; cout << "Input value: " << x00 << " (" << x00.getRefString() << ")" << endl; if (MeasureHolder(x00).toRecord(error, y00)) { if (q00.fromRecord(error, y00)) { cout <<"Record output value: " << q00.asMeasure() << " (" << q00.asMeasure().getRefString() << ")" << endl; } else { cout << "From error: " << error << endl; }; } else { cout << "To error: " << error << endl; }; cout << "As Position: " << q00.asMPosition() << endl; } { String error; MeasureHolder q00; MRadialVelocity x00(Quantity(30456, "m/s")); Record y00; cout << "Input value: " << x00 << " (" << x00.getRefString() << ")" << endl; if (MeasureHolder(x00).toRecord(error, y00)) { if (q00.fromRecord(error, y00)) { cout <<"Record output value: " << q00.asMeasure() << " (" << q00.asMeasure().getRefString() << ")" << endl; } else { cout << "From error: " << error << endl; }; } else { cout << "To error: " << error << endl; }; cout << "As RadialVelocity: " << q00.asMRadialVelocity() << endl; } } catch (std::exception& x) { cout << x.what() << endl; } try { cout << "----------------------------------------------------" << endl; cout << "Test MeasureHolder extension " << endl; cout << "----------------------------------------------------" << endl; MDirection x00((MVDirection(Quantity(1, "deg")))); MeasureHolder q00(x00); cout << "Direction: " << x00 << endl; cout << "Holder: " << q00.asMDirection() << endl; q00.makeMV(2); cout << "Number of values: " << q00.nelements() << endl; cout << "0: " << (q00.getMV(0) ? "not " : "") << "ok" << endl; cout << "1: " << (q00.getMV(1) ? "not " : "") << "ok" << endl; cout << "2: " << (q00.getMV(2) ? "not " : "") << "ok" << endl; MVDirection mvd((Quantity(2, "deg"))); MVDirection mvd2((Quantity(10, "deg"))); cout << "Set 0: " << q00.setMV(0, *q00.asMeasure().getData()) << endl; cout << "Set 1: " << q00.setMV(1, mvd2) << endl; cout << "Set 2: " << q00.setMV(2, mvd) << endl; cout << "Number of values: " << q00.nelements() << endl; cout << "2: " << (q00.getMV(2) ? "not " : "") << "ok" << endl; cout << "0: " << *q00.getMV(0) << endl; cout << "1: " << *q00.getMV(1) << endl; Record y00; String error; cout << "Direction: " << q00.asMeasure() << endl; if (q00.toRecord(error, y00)) { QuantumHolder q0; if (q0.fromRecord(error, y00.asRecord(RecordFieldId("m0")))) { cout << "m0: " << q0.asQuantumVectorDouble() << endl; } else { cout << "Cannot read the m0 vector" << endl; }; if (q0.fromRecord(error, y00.asRecord(RecordFieldId("m1")))) { cout << "m1: " << q0.asQuantumVectorDouble() << endl; } else { cout << "Cannot read the m1 vector" << endl; }; if (q00.fromRecord(error, y00)) { cout <<"Record output value: " << q00.asMeasure() << " (" << q00.asMeasure().getRefString() << ")" << endl; } else { cout << "From error: " << error << endl; }; } else { cout << "To error: " << error << endl; }; } catch (std::exception& x) { cout << x.what() << endl; } return 0; } casacore-3.7.1/measures/Measures/test/tMeasureHolder.out000066400000000000000000000044271476623553700233760ustar00rootroot00000000000000---------------------------------------------------- Test MeasureHolder ---------------------------------------------------- Input value: Direction: [0.663414, 0.383022, -0.642788] (J2000) Record output value: Direction: [0.663414, 0.383022, -0.642788] (J2000) Is measure: 1 Is direction: 1 Is empty: 0 Is epoch: 0 Is doppler: 0 Is position: 0 Is frequency: 0 Is radialvelocity: 0 As measure: Direction: [0.663414, 0.383022, -0.642788] As direction: Direction: [0.663414, 0.383022, -0.642788] Error expected: Input value: Direction: [0.663414, 0.383022, -0.642788] (J2000) From error: Illegal Measure record in MeasureHolder::fromRecord Input value: Direction: [0.663414, 0.383022, -0.642788] (J2000) Record output value: Direction: [0.663414, 0.383022, -0.642788] (J2000) As epoch: Empty or wrong MeasureHolder for asMEpoch Input value: Epoch: 30456::00:00:00.0000 (UTC) Record output value: Epoch: 30456::00:00:00.0000 (UTC) As epoch: Epoch: 30456::00:00:00.0000 Input value: Doppler: 0.00010159 (RADIO) Record output value: Doppler: 0.00010159 (RADIO) As Doppler: Doppler: 0.00010159 Input value: Frequency: 3.0456e+10 (LSRK) Record output value: Frequency: 3.0456e+10 (LSRK) As Frequency: Frequency: 3.0456e+10 Input value: Position: [4.88279e+06, 1.77719e+06, 3e+06] (ITRF) Record output value: Position: [4.88279e+06, 1.77719e+06, 3e+06] (ITRF) As Position: Position: [4.88279e+06, 1.77719e+06, 3e+06] Input value: Radialvelocity: 30456 (LSRK) Record output value: Radialvelocity: 30456 (LSRK) As RadialVelocity: Radialvelocity: 30456 ---------------------------------------------------- Test MeasureHolder extension ---------------------------------------------------- Direction: Direction: [0.999848, 0.0174524, 0] Holder: Direction: [0.999848, 0.0174524, 0] Number of values: 2 0: ok 1: ok 2: ok Set 0: 1 Set 1: 1 Set 2: 0 Number of values: 2 2: ok 0: [0.999848, 0.0174524, 0] 1: [0.984808, 0.173648, 0] Direction: Direction: [0.999848, 0.0174524, 0] m0: [0.0174533, 0.174533] rad m1: [0, 0] rad Record output value: Direction: [0.999848, 0.0174524, 0] (J2000) casacore-3.7.1/measures/Measures/test/tMuvw.cc000066400000000000000000000222701476623553700213470ustar00rootroot00000000000000//# tMuvw.cc: This program tests Muvw class //# Copyright (C) 1998-2000,2002,2007 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main() { try { cout << "Test measure class Muvw" << endl; cout << "--------------------------------------" << endl; cout << endl << "Muvw state transition matrix:\n" << endl; cout << MCuvw::showState() << endl; MEpoch tbm(Quantity(50927.92931, "d")); MPosition pos(MVPosition(-4750915.84032, 2792906.17778, -3200483.75028), MPosition::ITRF); MeasFrame mf(tbm, pos); mf.set(MDirection(Quantity(10, "deg"), Quantity(80, "deg"), MDirection::HADEC)); MVuvw mvb0(100 ,10, 0); cout << "uvw: " << mvb0 << endl; Muvw::Ref mbref0(Muvw::ITRF, mf); Muvw mb0(mvb0, mbref0); cout << "uvw: " << mb0 << endl; cout << "uvw reference: " << mbref0 << endl; Muvw::Ref mbref1(Muvw::J2000); cout << "uvw reference: " << mbref1 << endl; cout << "Test uvw conversion ..." << endl; cout << "--------------------------------------" << endl; Muvw::Convert bconv(mb0, mbref1); cout << "Converted " << mb0 << endl << " to " << mbref1 << endl << " as " << bconv() << endl; Muvw::Convert bconvb(mbref1, mbref0); if (allNearAbs(mb0.getValue().getValue(), bconvb(bconv()).getValue().getValue(), 1e-8)) { cout << "Back " << mb0 << " : ok" << endl; } else { cout << "Back " << mb0 << " : not ok" << endl << " as " << bconvb(bconv()) << endl; }; cout << "--------------------------------------" << endl; cout << "Make uvw from Baseline" << endl; MVBaseline mvb1(100, 10 ,1); MVDirection mvd(Quantity(30, "deg"), Quantity(45, "deg")); MVuvw mvu0(mvb1, mvd); cout << "Baseline: " << mvb1 << mvb1.getAngle("deg") << endl; cout << "Direction:" << mvd << mvd.getAngle("deg") << endl; cout << "uvw: " << mvu0 << mvu0.getAngle("deg") << endl; cout << "--------------------------------------" << endl; cout << "Testing all conversions forward/backward" << endl; Vector tvec(3); tvec = 0.0; Bool isok = True; for (uInt i=Muvw::J2000; i > vq(3); vq = Quantity(23, "m"); x.putValue(vq); cout << "putValue: " << vq << ", " << x << endl; cout << "uvwAngle: " << x.uvwAngle(mvb0) << endl; cout << "uvwAngle: " << x.uvwAngle(mvb0, "deg") << endl; cout << "get: " << x.get() << endl; cout << "getRecordValue: " << x.getRecordValue() << endl; cout << "separation: " << x.separation(mvb0) << endl; cout << "separation: " << x.separation(mvb0, "deg") << endl; cout << "crossProduct: " << x.crossProduct(mvb0) << endl; cout << "getAngle: " << x.getAngle() << endl; cout << "getAngle: " << x.getAngle("deg") << endl; cout << "getlength: " << x.getLength("cm") << endl; cout << "radius: " << x.radius() << endl; cout << "getXRecordValue:" << x.getXRecordValue() << endl; Vector x1(3); x1(0) = 30; x1(1) = 40; x1(2) = 0; x.putVector(x1); cout << "putVector: " << x1 << ", " << x << endl; MVuvw x2(vq); cout << "VQ constructor: " << x2 << endl; cout << "Pos constructor:" << MVuvw(x, x2) << endl; cout << "Q constructor: " << MVuvw(Quantity(50, "m")) << endl; cout << "QV constructor: " << MVuvw(x2.getAngle()) << endl; cout << "QV constructor: " << MVuvw(Quantity(34,"m"), x2.getAngle()) << endl; cout << "V constructor: " << MVuvw(x1) << endl; cout << "D constructor: " << MVuvw(Double(78)) << endl; cout << "operator+: " << x+x2 << endl; cout << "operator-: " << x-x2 << endl; cout << "operator-pre-: " << -x2 << endl; RotMatrix rm(Euler(25, 1, 0, 0)); cout << "operator*: " << x2*rm << endl; cout << "operator*: " << x2*2 << endl; MVuvw::assure(x); cout << "assure: " << "ok" << endl; cout << "getLength: " << x.getLength() << endl; cout << "operator*: " << x*x1 << endl; cout << "operator* " << x*x2 << endl; cout << "operator*: " << x1*x << endl; MeasValue *xmvu = x.clone(); cout << "clone: " << *xmvu << endl; delete xmvu; cout << "getVector: " << x.getVector() << endl; cout << "near: " << x.near(x2) << endl; cout << "near: " << x.near(x2, Quantity(1, "deg")) << endl; cout << "nearAbs: " << x.nearAbs(x2) << endl; cout << "!=: " << (x != x2) << endl; cout << "==: " << (x == x2) << endl; cout << "Original: " << x << endl; Double xa; x.adjust(xa); cout << "adjust: " << x << endl; x.readjust(xa); cout << "readjust: " << x << endl; cout << "All MVuvw functions: ok" << endl; cout << "----------------------------" << endl; } cout << "Exercise all Muvw function" << endl; { Muvw mb(mvb0, Muvw::B1950); String s0("azel"); Muvw::Types tp; Muvw::Ref mr; cout << "getType: " << Muvw::getType(tp, s0) << ", "; // next () to stop egcs warning cout << (uInt)tp << endl; cout << "giveMe: " << mb.giveMe(mr, s0) << ", "; cout << mr << endl; cout << "setRefString: " << mb.setRefString("hadec") << ", "; cout << mb << endl; Muvw::assure(mb); cout << "assure: " << "ok" << endl; Measure *xmu = mb.clone(); cout << "clone: " << *xmu << endl; delete xmu; cout << "get: " << mb.get("cm") << endl; cout << "getAngle: " << mb.getAngle("deg") << endl; cout << "getDefaultType: " << mb.getDefaultType() << endl; cout << "getRefString: " << mb.getRefString() << endl; cout << "Original: " << mb << endl; Muvw cpc(mb); cout << "Ctor copy: " << cpc << endl; Muvw cpas; cpas = mb; cout << "Assign: " << cpas << endl; cout << "tellMe: " << mb.tellMe() << endl; cout << "showMe: " << mb.showMe() << endl; cout << "All Muvw functions: ok" << endl; cout << "---------------------------" << endl; } } catch (std::exception& x) { cout << x.what() << endl; } try { cout << "Test Muvw exception" << endl; cout << "---------------------------" << endl; MEpoch x; Muvw::assure(x); } catch (std::exception& x) { cout << x.what() << endl; } try { cout << "Test MVuvw exception" << endl; cout << "---------------------------" << endl; MVEpoch x; MVuvw::assure(x); } catch (std::exception& x) { cout << x.what() << endl; } cout << "---------------------------" << endl; return 0; } casacore-3.7.1/measures/Measures/test/tMuvw.out000066400000000000000000000144161476623553700215740ustar00rootroot00000000000000Test measure class Muvw -------------------------------------- Muvw state transition matrix: | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ---------------------------------------------------------------------- 0| -- 10 10 18 4 5 4 4 2 18 18 18 18 18 18 34 10 10 2 18 18 47 | 1 1 14 4 5 4 4 8 14 14 14 14 14 14 15 1 1 8 14 14 21 1| 12 -- 13 12 12 12 12 12 12 12 12 12 12 12 12 12 36 13 12 12 12 12 | 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 16 2 0 0 0 0 2| 16 16 -- 16 16 16 16 16 16 16 16 16 16 16 16 16 16 38 16 16 16 16 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 17 1 1 1 1 3| 32 32 32 -- 21 21 21 21 32 22 22 22 22 22 32 32 32 32 32 22 22 32 | 14 14 14 4 4 4 4 14 20 20 20 20 20 14 14 14 14 14 20 20 14 4| 6 6 6 20 -- 8 11 11 3 20 20 20 20 20 6 6 6 6 3 20 20 6 | 0 0 0 3 5 6 6 8 3 3 3 3 3 0 0 0 0 8 3 3 0 5| 7 7 7 9 9 -- 9 9 7 9 9 9 9 9 7 7 7 7 7 9 9 7 | 0 0 0 4 4 4 4 0 4 4 4 4 4 0 0 0 0 0 4 4 0 6| 14 14 14 14 14 14 -- 15 14 14 14 14 14 14 14 14 14 14 14 14 14 14 | 4 4 4 4 4 4 7 4 4 4 4 4 4 4 4 4 4 4 4 4 4 7| 17 17 17 17 17 17 17 -- 17 17 17 17 17 17 17 17 17 17 17 17 17 17 | 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 8| 0 0 0 0 1 0 1 1 -- 0 0 0 0 0 0 0 0 0 40 0 0 0 | 0 0 0 0 4 0 4 4 0 0 0 0 0 0 0 0 0 18 0 0 0 9| 27 27 27 27 27 27 27 27 27 -- 23 23 24 24 27 27 27 27 27 43 27 27 | 20 20 20 20 20 20 20 20 20 10 10 12 12 20 20 20 20 20 19 20 20 10| 25 25 25 25 25 25 25 25 25 25 -- 28 25 25 25 25 25 25 25 25 25 25 | 9 9 9 9 9 9 9 9 9 9 11 9 9 9 9 9 9 9 9 9 9 11| 30 30 30 30 30 30 30 30 30 30 30 -- 30 30 30 30 30 30 30 30 30 30 | 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 12| 26 26 26 26 26 26 26 26 26 26 26 26 -- 29 26 26 26 26 26 26 26 26 | 9 9 9 9 9 9 9 9 9 9 9 9 13 9 9 9 9 9 9 9 9 13| 31 31 31 31 31 31 31 31 31 31 31 31 31 -- 31 31 31 31 31 31 31 31 | 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 14| 19 19 19 33 19 19 19 19 19 33 33 33 33 33 -- 19 19 19 19 33 33 19 | 0 0 0 3 0 0 0 0 0 3 3 3 3 3 0 0 0 0 3 3 0 15| 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 -- 35 35 35 35 35 35 | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 16| 37 37 37 37 37 37 37 37 37 37 37 37 37 37 37 37 -- 37 37 37 37 37 | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 17| 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 39 -- 39 39 39 39 | 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 18| 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 -- 41 41 41 | 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 19| 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 -- 42 42 | 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 20| 45 45 45 45 45 45 45 45 45 44 44 44 44 44 45 45 45 45 45 44 -- 45 | 3 3 3 3 3 3 3 3 3 9 9 9 9 9 3 3 3 3 3 9 3 21| 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 46 -- | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 uvw: [100, 10, 0] uvw: uvw: [100, 10, 0] uvw reference: Reference for an uvw with Type: ITRF, Frame: Epoch: 50927::22:18:12.3840 (TDB = 50927.9, UT1 = 50927.9, TT = 50927.9) Position: [-4.75092e+06, 2.79291e+06, -3.20048e+06] (Longitude = 2.61014 Latitude = -0.526138) Direction: [0.17101, 0.0301537, 0.984808] (J2000 = [-33.1687, 80.0127] deg) uvw reference: Reference for an uvw with Type: J2000 Test uvw conversion ... -------------------------------------- Converted uvw: [100, 10, 0] to Reference for an uvw with Type: J2000 as uvw: [99.9944, 10.0562, -1.50948e-07] Back uvw: [100, 10, 0] : not ok as uvw: [100, 10, -1.50877e-07] -------------------------------------- Make uvw from Baseline Baseline: [100, 10, 1][5.71059, 0.570096] deg Direction:[0.612372, 0.353553, 0.707107][30, 45] deg uvw: [-41.3397, -64.0657, 65.4799][-122.833, 40.6561] deg -------------------------------------- Testing all conversions forward/backward All forward/backward uvw conversions: ok -------------------------------------- Exercise all MVuvw functions putValue: [23 m, 23 m, 23 m], [23, 23, 23] uvwAngle: -2.1853 uvwAngle: -125.209 deg get: [39.8372, 0.785398, 0.61548] getRecordValue: [0.785398 rad, 0.61548 rad, 39.8372 m] separation: 3.14159 separation: 180 deg crossProduct: [-230, 2300, -2070] getAngle: [0.785398, 0.61548] rad getAngle: [45, 35.2644] deg getlength: 3983.72 cm radius: 39.8372 getXRecordValue:[23 m, 23 m, 23 m] putVector: [30, 40, 0], [30, 40, 0] VQ constructor: [23, 23, 23] Pos constructor:[7.07107, -28.5774, 40.4145] Q constructor: [0, 0, 50] QV constructor: [0.57735, 0.57735, 0.57735] QV constructor: [19.6299, 19.6299, 19.6299] V constructor: [30, 40, 0] D constructor: [0, 0, 78] operator+: [53, 63, 23] operator-: [7, 17, -23] operator-pre-: [-23, -23, -23] operator*: [23, 19.7536, 25.8418] operator*: [46, 46, 46] assure: ok getLength: 50 m operator*: 2500 operator* 1610 operator*: 2500 clone: [30, 40, 0] getVector: [30, 40, 0] near: 0 near: 0 nearAbs: 0 !=: 1 ==: 0 Original: [30, 40, 0] adjust: [0.6, 0.8, 0] readjust: [30, 40, 0] All MVuvw functions: ok ---------------------------- Exercise all Muvw function getType: 1, 10 giveMe: 1, Reference for an uvw with Type: AZEL setRefString: 1, uvw: [100, 10, 0] assure: ok clone: uvw: [100, 10, 0] get: [10000, 1000, 0] cm getAngle: [5.71059, 0] deg getDefaultType: ITRF getRefString: HADEC Original: uvw: [100, 10, 0] Ctor copy: uvw: [100, 10, 0] Assign: uvw: [100, 10, 0] tellMe: uvw showMe: uvw All Muvw functions: ok --------------------------- Test Muvw exception --------------------------- Illegal Measure type argument: uvw Test MVuvw exception --------------------------- Illegal MeasValue type argument: MVuvw --------------------------- casacore-3.7.1/measures/Measures/test/tNutation.cc000066400000000000000000000041621476623553700222120ustar00rootroot00000000000000//# tNutation.cc: Test program for parallel Nutation calculation //# Copyright (C) 2013 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include using namespace casacore; void doIt (int nthr, int n) { double incr = 1./nthr; // Do nutation a number of times, if possible in parallel. #pragma omp parallel for for (int i=0; i #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main() { try { cout << "Test Parallactic Angle machine" << endl; cout << "---------------------------------------------" << endl; Timer tim; MPosition obs; MeasTable::Observatory(obs, "atca"); Double dat(52332.2+1./24./15./10.); Quantity qdat(dat, "d"); MVEpoch mvdat(dat); MEpoch medat(mvdat, MEpoch::UTC); Vector vddat(5); Vector vmvdat(5); Vector vmedat(5); for (uInt i=0; i<5; ++i) { vddat[i] = dat + i/12./15./10.; vmvdat[i] = MVEpoch(vddat[i]); }; for (uInt i=0; i<5; ++i) { vmedat[i] = MEpoch(vmvdat[i], MEpoch::UTC); }; Quantum > vqdat(vddat, "d"); MeasFrame frame(medat, obs); MDirection::Ref refj2(MDirection::Ref(MDirection::J2000, frame)); MDirection::Ref refaz(MDirection::Ref(MDirection::AZEL, frame)); MDirection::Convert j2az(refj2, refaz); MDirection dir(Quantity(20, "deg"), Quantity(-30, "deg"), refj2); MDirection pol(Quantity(0, "deg"), Quantity(90, "deg"), refj2); cout << "Position: " << obs.getValue().get() << endl; cout << " " << obs.getAngle("deg") << endl; cout << " " << obs.getValue().getLength("km") << endl; cout << "Direction: " << dir.getValue().get() << endl; cout << " " << dir.getAngle("deg") << endl; cout << "Time: " << MVEpoch(dat) << endl; cout << "--------------- Full conversion to AZEL -----" << endl; for (uInt i=0; i<10; i+=2) { frame.set(MEpoch(Quantity(dat + i/24./15./10., "d"))); MVDirection mvd(j2az(dir).getValue()); if (i>0) cout << ", "; cout << setprecision(4) << Quantity(mvd.positionAngle(j2az(pol).getValue()), "rad").get("deg"); }; cout << endl; cout << "--------------- Full machine through HADEC --" << endl; ParAngleMachine pam(dir); pam.set(frame); pam.setInterval(0.0); for (uInt i=0; i<10; i+=2) { if (i>0) cout << ", "; cout << setprecision(4) << pam(MVEpoch(dat+i/24./15./10.)).get("deg"); }; cout << endl; cout << "--------------- Fast machine through HADEC --" << endl; pam.setInterval(0.04); for (uInt i=0; i<10; i+=2) { if (i>0) cout << ", "; cout << setprecision(4) << pam(MVEpoch(dat+i/24./15./10.)).get("deg"); }; cout << endl; cout << "--------------- Quantity --------------------" << endl; for (uInt i=0; i<5; ++i) { if (i>0) cout << ", "; cout << setprecision(4) << pam(Quantity(vddat[i], "d")).get("deg"); }; cout << endl; cout << "--------------- MVEpoch ---------------------" << endl; for (uInt i=0; i<5; ++i) { if (i>0) cout << ", "; cout << setprecision(4) << pam(vmvdat[i]).get("deg"); }; cout << endl; cout << "--------------- MEpoch ----------------------" << endl; for (uInt i=0; i<5; ++i) { if (i>0) cout << ", "; cout << setprecision(4) << pam(vmedat[i]).get("deg"); }; cout << endl; cout << "--------------- Double ----------------------" << endl; for (uInt i=0; i<5; ++i) { if (i>0) cout << ", "; cout << setprecision(4) << Quantity(pam(vddat[i]), "rad").get("deg"); }; cout << endl; cout << "--------------- Vector versions -------------" << endl; cout << pam(vqdat).get("deg") << endl; cout << pam(vmvdat).get("deg") << endl; cout << pam(vmedat).get("deg") << endl; cout << Quantum >(pam(vddat), "rad").get("deg") << endl; cout << "--------------- Timing ----------------------" << endl; cout << ">>>" << endl; const uInt N=1000; tim.mark(); for (uInt i=0; i>> Full AZEL for N=1000: 2.68 Full HADEC for N=1000: 0.32 Fast HADEC for N=1000: 0.08 <<< --------------------------------------------- ------------- Expected exception ------------ A ParAngle Machine has no frame, or a frame without an Epoch(to get time type) or Position --------------------------------------------- casacore-3.7.1/measures/Measures/test/tQuality.cc000066400000000000000000000101201476623553700220300ustar00rootroot00000000000000//# tQuality.cc: This program tests Quality interface class to table data //# Copyright (C) 1994,1995,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include void roundtrip(Int &int_in, Int &int_out); Int check_str_type(String &qualstr); int main() { try { Int qualint; Int myInt; String qualstr; // try a round trip for the first QualityType qualint=1; roundtrip(qualint, myInt); AlwaysAssert(myInt == qualint, AipsError) // try a round trip for the second QualityType qualint=2; roundtrip(qualint, myInt); AlwaysAssert(myInt == qualint, AipsError) // try a round trip for the third, // non-existing QualityType qualint=3; roundtrip(qualint, myInt); AlwaysAssert(myInt == 0, AipsError) // a type -1 does not exist // but should not shock the system qualint=-1; roundtrip(qualint, myInt); AlwaysAssert(myInt == 0, AipsError) // make sure there is the type "DATA" qualstr="DATA"; qualint = check_str_type(qualstr); AlwaysAssert(qualint, AipsError); // make sure there is the type "ERROR qualstr="ERROR"; qualint = check_str_type(qualstr); AlwaysAssert(qualint, AipsError); // make sure that everything else is zero qualstr="whatever else"; qualint = check_str_type(qualstr); AlwaysAssert(!qualint, AipsError); // make sure there are (currently) just three types // two defined plus the undefined one AlwaysAssert(Quality::NumberOfTypes==3, AipsError); // check the functioning of in/excluding the // undefined type AlwaysAssert(Quality::allNames(False).size() == Quality::NumberOfTypes - 1, AipsError); AlwaysAssert(Quality::allNames(True).size() == Quality::NumberOfTypes, AipsError); // just some eye-candy: present all names Vector allNames = Quality::allNames(True); cout << "All names: "; for (uInt i=0; i type: " << myType << " --> name: " << myTypeName << " --> int_out: " << int_out << endl; } Int check_str_type(String &qualstr){ Quality::QualityTypes myType; String myTypeName; // convert the string to a type // and back to string again myType = Quality::type(qualstr); myTypeName = Quality::name(myType); // output all cout << "name: = " << qualstr << " --> type: " << myType << " --> name: " << myTypeName << endl; // return just the type return (Int)myType; } casacore-3.7.1/measures/Measures/test/tQuality.out000066400000000000000000000005731476623553700222650ustar00rootroot00000000000000int_in = 1 --> type: 1 --> name: DATA --> int_out: 1 int_in = 2 --> type: 2 --> name: ERROR --> int_out: 2 int_in = 3 --> type: 0 --> name: ?? --> int_out: 0 int_in = -1 --> type: 0 --> name: ?? --> int_out: 0 name: = DATA --> type: 1 --> name: DATA name: = ERROR --> type: 2 --> name: ERROR name: = whatever else --> type: 0 --> name: ?? All names: ?? DATA ERROR ok casacore-3.7.1/measures/Measures/test/tRecordTransformable.cc000066400000000000000000000055441476623553700243540ustar00rootroot00000000000000//# tRecordTransformable.cc: Test program for class RecordTransformable //# Copyright (C) 1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include int main() { try { Record rec; { MeasureHolder measure; { const MDirection tmp; measure = MeasureHolder(tmp); } String errorMessage; if (!measure.toRecord(errorMessage, rec)) { throw(AipsError (String("Cannot convert class to a Record. The reason is:\n") + errorMessage)); } AlwaysAssert(measure.ident() == "meas", AipsError); AlwaysAssert(measure.RecordTransformable::ident() == "", AipsError); } AlwaysAssert(rec.isDefined("refer"), AipsError); AlwaysAssert(rec.isDefined("type"), AipsError); rec.define(RecordFieldId("refer"), String("b1950")); { MeasureHolder m; String errorMessage; if (!m.fromRecord(errorMessage, rec)) { throw(AipsError (String("Cannot update class from a Record. The reason is:\n" + errorMessage))); } AlwaysAssert(m.isMDirection(), AipsError); MDirection md = m.asMDirection(); AlwaysAssert(md.getRef().getType() == MDirection::B1950, AipsError); } } catch (std::exception& x) { cerr << x.what() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/measures/Measures/test/tStokes.cc000066400000000000000000000107051476623553700216610ustar00rootroot00000000000000//# tStokes.cc: This program tests Stokes interface class to table data //# Copyright (C) 1994,1995,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include int main() { try { Int polint; String polstr; polint=5; cout << "polint = " << polint << ", Stokes::type(polint) = " << Stokes::type(polint) <<", Stokes::type(Stokes::type(polint)) = " << Stokes::type(Stokes::type(polint)) << endl << " --- receptor1 = "<:: invalid object accessed. Sorry I don't know from where polstr = XY, Stokes::type(polstr) = 10, Stokes::type(Stokes::type(polstr)) = 10 --- receptor1 = 0 --- receptor2 = 1 polstr = LX, Stokes::type(polstr) = 15, Stokes::type(Stokes::type(polstr)) = 15 --- receptor1 = 1 --- receptor2 = 0 polstr = AB, Stokes::type(polstr) = 0, Stokes::type(Stokes::type(polstr)) = 0 --- receptor1 = Caught exception of receptor correctly: Fallible:: invalid object accessed. Sorry I don't know from where All names: I Q U V RR RL LR LL XX XY YX YY RX RY LX LY XR XL YR YL PP PQ QP QQ RCircular LCircular Linear Ptotal Plinear PFtotal PFlinear Pangle ok casacore-3.7.1/measures/Measures/test/tUVWMachine.cc000066400000000000000000000277521476623553700223710ustar00rootroot00000000000000//# tUVWMachine.cc: This program test the UVWMachine class //# Copyright (C) 1998,1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include int main() { try { cout << "Test UVWMachine class" << endl; cout << "--------------------------------------" << endl; // VLA position from an MS MPosition mLocation = MPosition(MVPosition(-1601162, -5042003, 3554915), MPosition::ITRF); // A time and position frame MeasFrame mFrame((MEpoch(Quantity(4.1216294e+09, "s"), MEpoch::TAI)), mLocation); // A source position Vector d1(2); d1(0) = 3.25745692; // 3C291 d1(1) = 0.040643336; Quantum > dir (d1, "rad"); MDirection mImage(dir, MDirection::B1950); MDirection mIm1; MDirection mIm2; // A UVW position MVPosition UVW(-739.048461, -1939.10604, 1168.62562); MVPosition UVW1; cout << "Original reference: " << mImage.getRef() << endl; cout << "Original phase centre: " << mImage << endl; cout << " " << mImage.getAngle("deg") << endl; cout << "Input UVW: " << UVW << endl; { cout << "-------- B1950 -> J2000 --------------" << endl; cout << "Input coordinates: " << mImage << endl; cout << " : " << mImage.getAngle("deg") << endl; // A new output reference MDirection::Ref oref(MDirection::J2000); // A UVW machine UVWMachine um(oref, mImage); cout << "Conversion to: " << oref << endl; cout << "Converted to: " << um.phaseCenter() << endl; cout << " " << um.phaseCenter().getAngle("deg") << endl; cout << "UVW rotation matrix: " << um.rotationUVW() << endl; mIm1 = um.phaseCenter(); // save for later MVPosition uvw(UVW); // save UVW uvw *= um.rotationUVW(); cout << "New UVW: " << uvw << endl; cout << "Phase rotation: " << uvw * um.rotationPhase() << endl; uvw = UVW; Double ph; um.convertUVW(ph, uvw); cout << "Phase: " << ph << ", UVW: " << uvw << endl; cout << "Repeat with copied UVW machine" << endl; UVWMachine umcp(um); cout << "Converted to: " << umcp.phaseCenter() << endl; cout << " " << umcp.phaseCenter().getAngle("deg") << endl; cout << "UVW rotation matrix: " << umcp.rotationUVW() << endl; mIm1 = umcp.phaseCenter(); // save for later uvw = UVW; // save UVW uvw *= umcp.rotationUVW(); cout << "New UVW: " << uvw << endl; cout << "Phase rotation: " << uvw * umcp.rotationPhase() << endl; uvw = UVW; umcp.convertUVW(ph, uvw); cout << "Phase: " << ph << ", UVW: " << uvw << endl; cout << "Repeat with assigned UVW machine" << endl; UVWMachine umas = um; cout << "Converted to: " << umas.phaseCenter() << endl; cout << " " << umas.phaseCenter().getAngle("deg") << endl; cout << "UVW rotation matrix: " << umas.rotationUVW() << endl; mIm1 = umas.phaseCenter(); // save for later uvw = UVW; // save UVW uvw *= umas.rotationUVW(); cout << "New UVW: " << uvw << endl; cout << "Phase rotation: " << uvw * umas.rotationPhase() << endl; uvw = UVW; umas.convertUVW(ph, uvw); cout << "Phase: " << ph << ", UVW: " << uvw << endl; cout << "Repeat after reCalculate()" << endl; umas.reCalculate(); cout << "Converted to: " << umas.phaseCenter() << endl; cout << " " << umas.phaseCenter().getAngle("deg") << endl; cout << "UVW rotation matrix: " << umas.rotationUVW() << endl; mIm1 = umas.phaseCenter(); // save for later uvw = UVW; // save UVW uvw *= umas.rotationUVW(); cout << "New UVW: " << uvw << endl; cout << "Phase rotation: " << uvw * umas.rotationPhase() << endl; uvw = UVW; umas.convertUVW(ph, uvw); cout << "Phase: " << ph << ", UVW: " << uvw << endl; } { cout << "-------- B1950 -> B1950+offset ---------" << endl; cout << "Input coordinates: " << mImage << endl; cout << " : " << mImage.getAngle("deg") << endl; // A new output reference MDirection::Ref oref(MDirection::B1950); MDirection odir(MVDirection(-0.990818, -0.125371, 0.0506217), oref); // A UVW machine UVWMachine um(odir, mImage); cout << "Conversion to: " << oref << endl; cout << "Converted to: " << um.phaseCenter() << endl; cout << " " << um.phaseCenter().getAngle("deg") << endl; cout << "UVW rotation matrix: " << um.rotationUVW() << endl; MVPosition uvw(UVW); // save UVW uvw *= um.rotationUVW(); cout << "New UVW: " << uvw << endl; cout << "Phase rotation: " << uvw * um.rotationPhase() << endl; uvw = UVW; Double ph; um.convertUVW(ph, uvw); cout << "Phase: " << ph << ", UVW: " << uvw << endl; } { cout << "-------- B1950 -> J2000+offset ---------" << endl; // A new output reference cout << "Input coordinates: " << mImage << endl; cout << " : " << mImage.getAngle("deg") << endl; MDirection::Ref oref(MDirection::J2000); MVDirection mvo(MVPosition(mIm1.getValue()) + MVPosition(0.01, 0.01, 0.01)); mvo.adjust(); MDirection odir(mvo, oref); // A UVW machine UVWMachine um(odir, mImage); cout << "Conversion to: " << oref << endl; cout << "Converted to: " << um.phaseCenter() << endl; cout << " " << um.phaseCenter().getAngle("deg") << endl; cout << "UVW rotation matrix: " << um.rotationUVW() << endl; mIm2 = um.phaseCenter(); // save for later MVPosition uvw(UVW); // save UVW uvw *= um.rotationUVW(); cout << "New UVW: " << uvw << endl; UVW1 = uvw; // save cout << "Phase rotation: " << uvw * um.rotationPhase() << endl; uvw = UVW; Double ph; um.convertUVW(ph, uvw); cout << "Phase: " << ph << ", UVW: " << uvw << endl; } { cout << "-------- J2000+offset -> B1950 ---------" << endl; // A new output reference cout << "Input coordinates: " << mIm2 << endl; cout << " : " << mIm2.getAngle("deg") << endl; MDirection::Ref oref(MDirection::B1950); MDirection odir(mImage.getValue(), oref); // A UVW machine UVWMachine um(odir, mIm2); cout << "Conversion to: " << oref << endl; cout << "Converted to: " << um.phaseCenter() << endl; cout << " " << um.phaseCenter().getAngle("deg") << endl; cout << "UVW rotation matrix: " << um.rotationUVW() << endl; MVPosition uvw(UVW1); // save UVW uvw *= um.rotationUVW(); cout << "New UVW: " << uvw << endl; cout << "Phase rotation: " << uvw * um.rotationPhase() << endl; uvw = UVW1; Double ph; um.convertUVW(ph, uvw); cout << "Phase: " << ph << ", UVW: " << uvw << endl; } } catch (std::exception& x) { cout << x.what() << endl; } try { cout << "-------------- deproject -----------------" << endl; // Output direction MDirection::Ref oref(MDirection::J2000); MDirection odir((MVDirection(Quantity(30, "deg"), Quantity(37, "deg"))), oref); // Main direction MDirection indir((MVDirection(Quantity(26, "deg"), Quantity(34, "deg"))), MDirection::Ref(MDirection::J2000)); // A UVW machine without projection UVWMachine um(odir, indir, False, False); // A UVW machine with projection UVWMachine ump(odir, indir, False, True); cout << "Input coordinates: " << indir << endl; cout << " : " << indir.getAngle("deg") << endl; cout << "Output coordinates: " << odir << endl; cout << " : " << odir.getAngle("deg") << endl; MVPosition uvw(10, 100, 200); cout << "Input uvw: " << uvw << endl; cout << "UVW rotation: " << um.rotationUVW() << endl; // Save UVW MVPosition u1(uvw); Double ph; um.convertUVW(ph, u1); cout << "Phase correction: " << ph << endl; cout << "Corrected UVW: " << u1 << endl; u1 = uvw; um.convertUVW(u1); cout << "Corrected UVW: " << u1 << endl; // Save UVW u1 = uvw; ump.convertUVW(ph, u1); cout << "-------------- Projected: ------------" << endl; RotMatrix roma = ump.rotationUVW(); if (nearAbs(roma(0,1), Double(0), 1e-15)) roma(0,1) = 0; if (nearAbs(roma(1,0), Double(0), 1e-15)) roma(1,0) = 0; cout << "UVW rotation: " << roma << endl; cout << "Phase correction: " << ph << endl; cout << "Corrected UVW: " << u1 << endl; u1 = uvw; ump.convertUVW(u1); cout << "Corrected UVW: " << u1 << endl; u1 = uvw; cout << "Phase correction: " << ump.getPhase(u1) << endl; cout << "Corrected UVW: " << u1 << endl; Vector vd; vd = uvw.getValue(); cout << "Phase correction: " << ump.getPhase(vd) << endl; cout << "Corrected UVW: " << MVPosition(vd) << endl; vd = uvw.getValue(); ump.convertUVW(vd); cout << "Corrected UVW: " << MVPosition(vd) << endl; u1 = uvw; cout << "Corrected UVW: " << ump(u1) << endl; vd = uvw.getValue(); cout << "Corrected UVW: " << MVPosition(ump(vd)) << endl; cout << "---------------Vectors: ---------------" << endl; UVWMachine umcp(um); UVWMachine umas = um; Vector > vdd(1); Vector vmv(1); cout << "Input uvw: " << uvw << endl; vdd(0) = uvw.getValue(); cout << "Phase correction: " << ump.getPhase(vdd) << endl; cout << "Corrected UVW: " << MVPosition(vdd(0)) << endl; vdd(0) = uvw.getValue(); umcp.convertUVW(vdd); cout << "Corrected UVW: " << MVPosition(vdd(0)) << endl; vdd(0) = uvw.getValue(); Vector > vddo; vddo = um(vdd); cout << "Corrected UVW: " << MVPosition(vddo(0)) << endl; vmv(0) = uvw; cout << "Phase correction: " << ump.getPhase(vmv) << endl; cout << "Corrected UVW: " << vmv(0) << endl; vmv(0) = uvw; umas.convertUVW(vmv); cout << "Corrected UVW: " << vmv(0) << endl; vmv(0) = uvw; Vector vmvo; vmvo = um(vmv); cout << "Corrected UVW: " << vmvo(0) << endl; cout << "---------------------------------------" << endl; } catch (std::exception& x) { cout << x.what() << endl; } return 0; } casacore-3.7.1/measures/Measures/test/tUVWMachine.out000066400000000000000000000127331476623553700226040ustar00rootroot00000000000000Test UVWMachine class -------------------------------------- Original reference: Reference for an Direction with Type: B1950 Original phase centre: Direction: [-0.992475, -0.11551, 0.0406321] [-173.361, 2.32869] deg Input UVW: [-739.048, -1939.11, 1168.63] -------- B1950 -> J2000 -------------- Input coordinates: Direction: [-0.992475, -0.11551, 0.0406321] : [-173.361, 2.32869] deg Conversion to: Reference for an Direction with Type: J2000 Converted to: Direction: [-0.991307, -0.126601, 0.0358136] [-172.722, 2.05241] deg UVW rotation matrix: Axis Lengths: [3, 3] (NB: Matrix in Row/Column order) [1, 0.000588764, -1.45692e-06 -0.000589097, 1, 1.44823e-06 -4.24861e-07, 3.6803e-07, 1] New UVW: [-737.906, -1939.54, 1168.62] Phase rotation: 0 Phase: 0, UVW: [-737.906, -1939.54, 1168.62] Repeat with copied UVW machine Converted to: Direction: [-0.991307, -0.126601, 0.0358136] [-172.722, 2.05241] deg UVW rotation matrix: Axis Lengths: [3, 3] (NB: Matrix in Row/Column order) [1, 0.000588764, -1.45692e-06 -0.000589097, 1, 1.44823e-06 -4.24861e-07, 3.6803e-07, 1] New UVW: [-737.906, -1939.54, 1168.62] Phase rotation: 0 Phase: 0, UVW: [-737.906, -1939.54, 1168.62] Repeat with assigned UVW machine Converted to: Direction: [-0.991307, -0.126601, 0.0358136] [-172.722, 2.05241] deg UVW rotation matrix: Axis Lengths: [3, 3] (NB: Matrix in Row/Column order) [1, 0.000588764, -1.45692e-06 -0.000589097, 1, 1.44823e-06 -4.24861e-07, 3.6803e-07, 1] New UVW: [-737.906, -1939.54, 1168.62] Phase rotation: 0 Phase: 0, UVW: [-737.906, -1939.54, 1168.62] Repeat after reCalculate() Converted to: Direction: [-0.991307, -0.126601, 0.0358136] [-172.722, 2.05241] deg UVW rotation matrix: Axis Lengths: [3, 3] (NB: Matrix in Row/Column order) [1, 0.000588764, -1.45692e-06 -0.000589097, 1, 1.44823e-06 -4.24861e-07, 3.6803e-07, 1] New UVW: [-737.906, -1939.54, 1168.62] Phase rotation: 0 Phase: 0, UVW: [-737.906, -1939.54, 1168.62] -------- B1950 -> B1950+offset --------- Input coordinates: Direction: [-0.992475, -0.11551, 0.0406321] : [-173.361, 2.32869] deg Conversion to: Reference for an Direction with Type: B1950 Converted to: Direction: [-0.990818, -0.125371, 0.0506217] [-172.789, 2.90165] deg UVW rotation matrix: Axis Lengths: [3, 3] (NB: Matrix in Row/Column order) [0.99995, -0.000506192, -0.0099867 0.000406302, 0.99995, -0.0100019 0.00999126, 0.00999729, 0.9999] New UVW: [-728.123, -1926.95, 1195.28] Phase rotation: 26.6586 Phase: 26.6586, UVW: [-728.123, -1926.95, 1195.28] -------- B1950 -> J2000+offset --------- Input coordinates: Direction: [-0.992475, -0.11551, 0.0406321] : [-173.361, 2.32869] deg Conversion to: Reference for an Direction with Type: J2000 Converted to: Direction: [-0.991949, -0.117866, 0.0463105] [-173.224, 2.65434] deg UVW rotation matrix: Axis Lengths: [3, 3] (NB: Matrix in Row/Column order) [0.999961, 0.000994203, 0.0087388 -0.000902666, 0.999944, -0.0105106 -0.00875065, 0.0105042, 0.999907] New UVW: [-747.496, -1927.46, 1182.44] Phase rotation: 13.8153 Phase: 13.8153, UVW: [-747.496, -1927.46, 1182.44] -------- J2000+offset -> B1950 --------- Input coordinates: Direction: [-0.991949, -0.117866, 0.0463105] : [-173.224, 2.65434] deg Conversion to: Reference for an Direction with Type: B1950 Converted to: Direction: [-0.992475, -0.11551, 0.0406321] [-173.361, 2.32869] deg UVW rotation matrix: Axis Lengths: [3, 3] (NB: Matrix in Row/Column order) [0.999962, -0.00090232, -0.00874877 0.000994555, 0.999944, 0.0105023 0.00874069, -0.0105125, 0.999906] New UVW: [-739.048, -1939.11, 1168.63] Phase rotation: -13.8153 Phase: -13.8153, UVW: [-739.048, -1939.11, 1168.63] -------------- deproject ----------------- Input coordinates: Direction: [0.745134, 0.363426, 0.559193] : [26, 34] deg Output coordinates: Direction: [0.691639, 0.399318, 0.601815] : [30, 37] deg Input uvw: [10, 100, 200] UVW rotation: Axis Lengths: [3, 3] (NB: Matrix in Row/Column order) [0.997564, -0.0419805, -0.05571 0.0390073, 0.99781, -0.0534238 0.0578307, 0.0511206, 0.997017] Phase correction: -6.49614 Corrected UVW: [25.4425, 109.585, 193.504] Corrected UVW: [25.4425, 109.585, 193.504] -------------- Projected: ------------ UVW rotation: Axis Lengths: [3, 3] (NB: Matrix in Row/Column order) [1, 0, -0.05571 0, 1, -0.0534238 0.0558767, 0.0535837, 0.997017] Phase correction: -6.49614 Corrected UVW: [21.1753, 110.717, 193.504] Corrected UVW: [21.1753, 110.717, 193.504] Phase correction: -6.49614 Corrected UVW: [21.1753, 110.717, 193.504] Phase correction: -6.49614 Corrected UVW: [21.1753, 110.717, 193.504] Corrected UVW: [21.1753, 110.717, 193.504] Corrected UVW: [21.1753, 110.717, 193.504] Corrected UVW: [21.1753, 110.717, 193.504] ---------------Vectors: --------------- Input uvw: [10, 100, 200] Phase correction: [-6.49614] Corrected UVW: [21.1753, 110.717, 193.504] Corrected UVW: [25.4425, 109.585, 193.504] Corrected UVW: [25.4425, 109.585, 193.504] Phase correction: [-6.49614] Corrected UVW: [21.1753, 110.717, 193.504] Corrected UVW: [25.4425, 109.585, 193.504] Corrected UVW: [25.4425, 109.585, 193.504] --------------------------------------- casacore-3.7.1/measures/Measures/test/tVelocityMachine.cc000066400000000000000000000141251476623553700234740ustar00rootroot00000000000000//# tVelocityMachine.cc: This program tests the VelocityMachine class //# Copyright (C) 1998,1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main() { try { cout << "Test Velocity<->Frequency machine" << endl; cout << "--------------------------------------" << endl; MVTime dat(1998,5,10); MVPosition mvobs(Quantity(3828488.86, "m").getBaseValue(), Quantity(443253.42, "m").getBaseValue(), Quantity(5064977.78, "m").getBaseValue()); MPosition obs(mvobs); MDirection dir((MVDirection(Quantity(0, "deg"), Quantity(80, "deg")))); MeasFrame frame((MEpoch(MVEpoch(dat.day()))), obs, dir); cout << "Date: " << dat.string(MVTime::YMD + MVTime::NO_TIME, 6) << endl; cout << "Position: " << obs.getValue().get() << endl; cout << " " << obs.getAngle("deg") << endl; cout << " " << obs.getValue().getLength("km") << endl; cout << "Direction: " << dir.getValue().get() << endl; cout << " " << dir.getAngle("deg") << endl; MFrequency::Ref frqref(MFrequency::LSRK); MDoppler::Ref velref(MDoppler::RADIO); MVFrequency restfrq(QC::HI( )); cout << "Rest freq: " << restfrq.get("GHz") << endl; VelocityMachine vm(frqref, Unit("GHz"), restfrq, velref, Unit("km/s"), frame); cout << "------------------- Conversions ------" << endl; cout << "1410 MHz to RADIO: " << vm.makeVelocity(1.41) << endl; cout << "1410 MHz to RADIO: " << vm(MVFrequency(Quantity(1.41, "GHz"))) << endl; Double bck(vm.makeVelocity(1.41).getValue()); cout << "Back: " << vm.makeFrequency(bck) << endl; cout << "Back: " << vm(MVDoppler(Quantity(bck, "km/s"))) << endl; vm.set(MFrequency::TOPO); cout << "1410 MHz to TOPO: " << vm.makeVelocity(1.41) << endl; vm.set(MFrequency::LSRK); frqref.set(MFrequency(Quantity(1.405, "GHz"))); cout << "Frequency offset: " << *(frqref.offset()) << endl; vm.set(frqref); Vector fx(3); fx(0) = 0; fx(1) = 0.005; fx(2) = 0.010; cout << "Frequency list: " << fx << endl; cout << "List to RADIO: " << vm.makeVelocity(fx) << endl; Vector vbck(vm.makeVelocity(fx).getValue()); cout << "Back: " << vm.makeFrequency(vbck) << endl; velref.set(MDoppler(Quantity(1000, "km/s"), MDoppler::RADIO)); cout << "Velocity offset: " << *(velref.offset()) << endl; vm.set(velref); cout << "List to RADIO: " << vm.makeVelocity(fx) << endl; cout << "--------Test copy-----------" << endl; VelocityMachine vmc(vm); cout << "List to RADIO: " << vmc.makeVelocity(fx) << endl; cout << "--------Test constructors-----" << endl; VelocityMachine vma(frqref, Unit("GHz"), restfrq, velref, Unit("km/s")); cout << "List to RADIO: " << vma.makeVelocity(fx) << endl; VelocityMachine vmb(frqref, Unit("GHz"), restfrq, MFrequency::TOPO, velref, Unit("km/s"), frame); cout << "List to TOPO: " << vmb.makeVelocity(fx) << endl; cout << "--------Test assignment-----" << endl; vma = vm; cout << "List to RADIO: " << vma.makeVelocity(fx) << endl; cout << "--------Test reCalculate-----" << endl; vm.reCalculate(); cout << "List to RADIO: " << vm.makeVelocity(fx) << endl; cout << "--------Test (Quantum)-------" << endl; cout << "List(0) to RADIO: " << vm(Quantity(fx(0), "GHz")) << endl; cout << "--------Test set-------------" << endl; vm.set(Unit("GHz")); cout << "List to RADIO: " << vm.makeVelocity(fx) << endl; vm.set(restfrq); cout << "List to RADIO: " << vm.makeVelocity(fx) << endl; vm.set(frame); cout << "List to RADIO: " << vm.makeVelocity(fx) << endl; { // test restfreq <= 0 throws exception MVFrequency restfrq2(0); VelocityMachine bogus(frqref, Unit("GHz"), restfrq2, velref, Unit("km/s")); try { bogus.makeVelocity(20); // exception should be thrown before we get here AlwaysAssert(False, AipsError); } catch (const std::exception& x) {} MVFrequency restfrq3(-1); VelocityMachine bogus2( frqref, Unit("GHz"), restfrq3, velref, Unit("km/s") ); try { bogus2.makeVelocity(20); AlwaysAssert(False, AipsError); } catch (const std::exception& x) {} } } catch (std::exception& x) { cout << x.what() << endl; } return 0; } casacore-3.7.1/measures/Measures/test/tVelocityMachine.out000066400000000000000000000024471476623553700237220ustar00rootroot00000000000000Test Velocity<->Frequency machine -------------------------------------- Date: 1998/05/10 Position: [6.36457e+06, 0.115264, 0.92034] [6.60417, 52.7316] deg 6364.57 km Direction: [0, 1.39626] [0, 80] deg Rest freq: 1.42041 GHz ------------------- Conversions ------ 1410 MHz to RADIO: 2196.25 km/s 1410 MHz to RADIO: 2196.25 km/s Back: 1.41 GHz Back: 1.41 GHz 1410 MHz to TOPO: 2190.2 km/s Frequency offset: Frequency: 1.405e+09 Frequency list: [0, 0.005, 0.01] List to RADIO: [3251.56, 2196.25, 1140.94] km/s Back: [0, 0.005, 0.01] GHz Velocity offset: Doppler: 0.00333564 List to RADIO: [2251.56, 1196.25, 140.944] km/s --------Test copy----------- List to RADIO: [2251.56, 1196.25, 140.944] km/s --------Test constructors----- List to RADIO: [2251.56, 1196.25, 140.944] km/s List to TOPO: [2245.53, 1190.2, 134.874] km/s --------Test assignment----- List to RADIO: [2251.56, 1196.25, 140.944] km/s --------Test reCalculate----- List to RADIO: [2251.56, 1196.25, 140.944] km/s --------Test (Quantum)------- List(0) to RADIO: 2251.56 km/s --------Test set------------- List to RADIO: [2251.56, 1196.25, 140.944] km/s List to RADIO: [2251.56, 1196.25, 140.944] km/s List to RADIO: [2251.56, 1196.25, 140.944] km/s casacore-3.7.1/measures/TableMeasures.h000066400000000000000000000143211476623553700200560ustar00rootroot00000000000000//# TableMeasures.h: Create Measure and Quantum columns Tables. //# Copyright (C) 1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_TABLEMEASURES_H #define MEASURES_TABLEMEASURES_H #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // //

        // Create and use scalar and array columns of Quanta and Measures in Tables. // // //
      • Measures //
      • Tables // // // // // Table columns containing Measures and Quanta // // // The TableMeasures system exists to provide a way of creating (defining) // Measure and Quantum Table columns thus enabling the direct storage of // Quanta and Measures in Casacore Tables. //

        // Defining Quantum and Measure columns is a once only operation (for each // column). It can be seen as an extension to the existing Column Descriptor // mechanism which adds a column of a specified type to a table. The // TableMeasDesc and // TableQuantumDesc class // hierarchies are used to define Measure and Quantum columns. //

        // Once defined, Measure and Quantum column objects are used to access a // column for reading and writing of Measures and Quanta. For Quantum // column objects see the class // ScalarQuantColumn and // ArrayQuantColumn. For // Measure column objects see // ScalarMeasColumn and // ArrayMeasColumn. // //

        Conversions

        // The classes accessing the data use the underlying // Quanta or // Measures classes to convert // the units or references of the measures or quanta. // The TableMeasures classes do not test if a conversion is possible. //
        In general one can say that about every unit conversion is possible. // The Unit class adjusts units as needed. //
        Conversions of Measures are only possible if enough information // is supplied for the measure's reference. //
        Take a look at the abovementioned modules to find out about conversions. // //

        Performance

        // Using the TableMeasures classes makes it easier to deal with // measures in tables. However, there is a performance penalty // compared to handling the values directly in the table using // the Tables classes ScalarColumn // and ArrayColumn. // // The performance of the TableMeasures classes depends on how the // measures are stored; thus if a fixed or variable offset and reference // are used. // Of course, it also depends on whether the measures have to be // converted before they can be stored. //
        The TableMeasures classes are always slower than the Tables classes, // but they offer more convenience. // In general one can say that for large tables it is better to use // the Tables classes directly to put/get the data. // However, even when putting directly using the Tables classes, the // column itself should be defined as a TableMeasure. In that way there // is one standard way of defining columns as table measures. //
        For example, the TIME column in a MeasurementSet should be handled // directly uisng the Tables classes (because it is so large). // On the other hand, the FIELD table is very small and it may make life // easier to handle its columns through the TableMeasures classes. // // In a test putting an array of quanta using class // ArrayQuantColumn took // about 5 times as long as doing it directly using class // ArrayColumn. The quantum column // had variable units. so for each row the unit had to be written as well. // Reading it back took about 3 times as long. //
        When using a qunatum column with fixed units, putting took about // 2.5 times as long as using ArrayColumn directly. // Each put involved a unit conversion. // Reading it back took only 10% more than when using ArrayColumn. // //
        // // The standard Casacore Table system does not directly support Quantum and // Measure columns. These classes overcome this limitation. // //# //# // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/TableMeasures/000077500000000000000000000000001476623553700177045ustar00rootroot00000000000000casacore-3.7.1/measures/TableMeasures/ArrayMeasColumn.h000066400000000000000000000246531476623553700231310ustar00rootroot00000000000000//# ArrayMeasColumn.h: Access to array Measure columns in Tables. //# Copyright (C) 1997,1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_ARRAYMEASCOLUMN_H #define MEASURES_ARRAYMEASCOLUMN_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class ArrayColumn; template class ScalarColumn; template class ScalarMeasColumn; // // Access table array Measure columns. // // // // // //
      • Measures //
      • Tables //
      • TableMeasDesc // // // ArrayMeasColumn objects can be used to access array Measure Columns // in tables, both for reading and writing (if the table is writable). // // Before a column can be accessed it must have previously been defined as // a Measure column by use of the // TableMeasDesc object. // // The ArrayMeasColumn class is templated on Measure type and MeasValue // type but typedefs exist for making declaration less long winded. The // Measure classes (like MEpoch) have typedefs ArrayColumn // to assist in creating ArrayMeasColumn objects. // // Constructing array Measure column objects using these typedefs looks like // this: // // // Read/write MEpoch array column // MEpoch::ArrayColumn ec(table, "ColumnName); // // //

        Reading and writing Measures

        // // The reading and writing of Measures columns is very similar to reading and // writing of "ordinary" Table columns. // get() // and operator() // exist for reading Measures and the // put() member for adding // Measures to a column. (put() is obviously not defined for // ScalarMeasColumn objects.) Each of these members accepts a row number // as an argument. // // Care needs to be taken when adding Measures to a column. The user needs // to be aware that Measures are not checked for consistency, with respect to // a Measure's reference, between the Measures being added to a column and // the column's predefined Measure reference. This is only an issue if the // Measure column was defined to have a fixed reference. For such columns // the reference component of Measures added to a column is silently // ignored, that is, there is no warning nor is there any sort of conversion // to the column's reference should the reference of the added Measure be // different from the column's reference. The functions // // TableMeasDescBase::isRefVariable() // and // // ArrayMeasColumn::getMeasRef() can be // used to discover a Measure column's Measure reference characteristics. //
        // // // // create an MEpoch array column object // ArrayMeasColumn arrayCol; // // // should be null. Can test this and attach a real MEpoch column // // The column Time1Arr should exist in the table and would have been // // defined by using a TableMeasDesc // if (arrayCol.isNull()) { // arrayCol.attach(tab, "Time1Arr"); // } // // // This would throw an Exception if the object is still null....but // // hopefully won't // arrayCol.throwIfNull(); // // // create a vector of MEpochs // MEpoch last(Quantity(13.45, "h"), MEpoch::Ref(MEpoch::TAI)); // Vector ev(10); // for (uInt i=0; i<10; i++) { // last.set(Quantity(13.45 + i, "h")); // ev(i) = last; // } // // // before adding something check the isDefined() member // if (!arrayCol.isDefined(0)) { // cout << "PASS - nothing in the measure array column row yet\n"; // } else { // cout << "FAIL - there shouldn't be a valid value in the row!\n"; // } // // // add the MEpoch vector to the array Measure column at row 0 // arrayCol.put(0, ev); // // // now read the array from the row. Could use same object to do this // // but here we'll create a ArrayMeasColumn to do this // ArrayMeasColumn roArrCol(tab, "Time1Arr"); // // // need a vector to put the MEpochs into // Vector ew; // // // setting the resize parameter to True automatically sets ew to the // // same shape as the array contained in the row // arrayCol.get(0, ew, True); // // // // The standard Casacore Table system does not support array Measure columns. // This class overcomes this limitation. // // //
      • ArrayConformanceError during get() if supplied array is the wrong // shape. // //# //# A List of bugs, limitations, extensions or planned refinements. //# template class ArrayMeasColumn : public TableMeasColumn { public: // The default constructor creates a null object. Useful for creating // arrays of ArrayMeasColumn objects. Attempting to use a null object // will produce a segmentation fault so care needs to be taken to // initialise the objects by using the attach() member before any attempt // is made to use the object. A ArrayMeasColumn object can be tested // if it is null by using the isNull() member. ArrayMeasColumn(); // Create the ArrayMeasColumn from the table and column Name. ArrayMeasColumn (const Table& tab, const String& columnName); // Copy constructor (copy semantics). ArrayMeasColumn (const ArrayMeasColumn& that); virtual ~ArrayMeasColumn(); // Change the reference to another column. void reference (const ArrayMeasColumn& that); // Attach a column to the object. void attach (const Table& tab, const String& columnName); // Get the Measure array in the specified row. For get() the supplied // array's shape should match the shape in the row unless resize is True. // void get (rownr_t rownr, Array& meas, Bool resize = False) const; Array operator() (rownr_t rownr) const; // // Get the Measure array contained in the specified row and convert // it to the reference and offset found in the given measure. Array convert (rownr_t rownr, const M& meas) const { return convert (rownr, meas.getRef()); } // Get the Measure array contained in the specified row and convert // it to the given reference. // Array convert (rownr_t rownr, const MeasRef& measRef) const; Array convert (rownr_t rownr, uInt refCode) const; // // Get the column's reference. const MeasRef& getMeasRef() const { return itsMeasRef; } // Reset the refCode, offset, or units. // It overwrites the value used when defining the TableMeasDesc. // Resetting the refCode and offset can only be done if they were // defined as fixed in the description. // // In principle the functions can only be used if the table is empty, // otherwise already written values have thereafter the incorrect // reference, offset, or unit. // However, it is possible that part of the table is already // written and that the entire measure column is filled in later. // In that case the reference, offset, or units can be set by using // a False tableMustBeEmpty argument. // // void setDescRefCode (uInt refCode, Bool tableMustBeEmpty=True); void setDescOffset (const Measure& offset, Bool tableMustBeEmpty=True); void setDescUnits (const Vector& units, Bool tableMustBeEmpty=True); // // Add a Measure array to the specified row. // void put (rownr_t rownr, const Array&); // protected: //# Its measure reference when the MeasRef is constant per row. MeasRef itsMeasRef; private: //# Column which contains the Measure's actual data. ArrayColumn* itsDataCol; //# Its MeasRef code column when references are variable. ScalarColumn* itsRefIntCol; ArrayColumn* itsArrRefIntCol; //# Its MeasRef column when references are variable and stored as Strings. ScalarColumn* itsRefStrCol; ArrayColumn* itsArrRefStrCol; //# Column containing its variable offsets. Only applicable if the //# measure references have offsets and they are variable. ScalarMeasColumn* itsOffsetCol; ArrayMeasColumn* itsArrOffsetCol; // Assignment makes no sense in a read only class. // Declaring this operator private makes it unusable. ArrayMeasColumn& operator= (const ArrayMeasColumn& that); // Deletes allocated memory etc. Called by ~tor and any member which needs // to reallocate data. void cleanUp(); // Get the data and convert using conversion engine. Array doConvert (rownr_t rownr, typename M::Convert& conv) const; }; } //# NAMESPACE CASACORE - END //# Make old name ROArrayMeasColumn still available. #define ROArrayMeasColumn ArrayMeasColumn #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/measures/TableMeasures/ArrayMeasColumn.tcc000066400000000000000000000404541476623553700234500ustar00rootroot00000000000000//# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_ARRAYMEASCOLUMN_TCC #define MEASURES_ARRAYMEASCOLUMN_TCC //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template ArrayMeasColumn::ArrayMeasColumn() : itsDataCol (0), itsRefIntCol (0), itsArrRefIntCol (0), itsRefStrCol (0), itsArrRefStrCol (0), itsOffsetCol (0), itsArrOffsetCol (0) {} template ArrayMeasColumn::ArrayMeasColumn (const Table& tab, const String& columnName) : TableMeasColumn (tab, columnName), itsDataCol (0), itsRefIntCol (0), itsArrRefIntCol (0), itsRefStrCol (0), itsArrRefStrCol (0), itsOffsetCol (0), itsArrOffsetCol (0) { const TableMeasDescBase& tmDesc = measDesc(); AlwaysAssert(M::showMe() == tmDesc.type(), AipsError); itsDataCol = new ArrayColumn(tab, columnName); // Determine the number of values in the Measure. M tMeas; itsNvals = tMeas.getValue().getTMRecordValue().nelements(); AlwaysAssert (itsNvals <= tmDesc.getUnits().size(), AipsError); // Set up the reference code component of the MeasRef. It can be variable // and therefore stored in a column which may be either an array or scalar // column. Additionally, the references may be stored as strings or // refcodes. if (tmDesc.isRefCodeVariable()) { const String& rcName = tmDesc.refColumnName(); const ColumnDesc& cd = tab.tableDesc().columnDesc(rcName); if (cd.isScalar()) { if (cd.dataType() == TpString) { itsRefStrCol = new ScalarColumn(tab, rcName); } else { itsRefIntCol = new ScalarColumn(tab, rcName); } } else { if (cd.dataType() == TpString) { itsArrRefStrCol = new ArrayColumn(tab, rcName); } else { itsArrRefIntCol = new ArrayColumn(tab, rcName); } } } else { itsMeasRef.set (tmDesc.getRefCode()); } // Set up the offset component of the MeasRef if (tmDesc.hasOffset()) { if (tmDesc.isOffsetVariable()) { const String& ocName = tmDesc.offsetColumnName(); if (tmDesc.isOffsetArray()) { itsArrOffsetCol = new ArrayMeasColumn(tab, ocName); } else { itsOffsetCol = new ScalarMeasColumn(tab, ocName); } } else { itsMeasRef.set (tmDesc.getOffset()); } } } template ArrayMeasColumn::ArrayMeasColumn (const ArrayMeasColumn& that) : TableMeasColumn(), itsDataCol (0), itsRefIntCol (0), itsArrRefIntCol(0), itsRefStrCol (0), itsArrRefStrCol(0), itsOffsetCol (0), itsArrOffsetCol(0) { reference (that); } template ArrayMeasColumn::~ArrayMeasColumn() { cleanUp(); } template void ArrayMeasColumn::cleanUp() { delete itsDataCol; delete itsRefIntCol; delete itsArrRefIntCol; delete itsRefStrCol; delete itsArrRefStrCol; delete itsOffsetCol; delete itsArrOffsetCol; } template void ArrayMeasColumn::reference (const ArrayMeasColumn& that) { cleanUp(); TableMeasColumn::reference (that); itsDataCol = that.itsDataCol; itsRefIntCol = that.itsRefIntCol; itsArrRefIntCol = that.itsArrRefIntCol; itsRefStrCol = that.itsRefStrCol; itsArrRefStrCol = that.itsArrRefStrCol; itsOffsetCol = that.itsOffsetCol; itsArrOffsetCol = that.itsArrOffsetCol; itsMeasRef = that.itsMeasRef; if (itsDataCol != 0) { itsDataCol = new ArrayColumn(*itsDataCol); } if (itsRefIntCol != 0) { itsRefIntCol = new ScalarColumn(*itsRefIntCol); } if (itsArrRefIntCol != 0) { itsArrRefIntCol = new ArrayColumn(*itsArrRefIntCol); } if (itsRefStrCol != 0) { itsRefStrCol = new ScalarColumn(*itsRefStrCol); } if (itsArrRefStrCol != 0) { itsArrRefStrCol = new ArrayColumn(*itsArrRefStrCol); } if (itsOffsetCol != 0) { itsOffsetCol = new ScalarMeasColumn(*itsOffsetCol); } if (itsArrOffsetCol != 0) { itsArrOffsetCol = new ArrayMeasColumn(*itsArrOffsetCol); } } template void ArrayMeasColumn::attach (const Table& tab, const String& columnName) { reference (ArrayMeasColumn(tab, columnName)); } template void ArrayMeasColumn::get (rownr_t rownr, Array& meas, Bool resize) const { // This will fail if array in rownr is undefined. Array tmpData((*itsDataCol)(rownr)); Bool deleteData; const Double* d_ptr = tmpData.getStorage(deleteData); const Double* d_p = d_ptr; // Determine the dimensionality of the resulting Array. IPosition shpt (tmpData.shape()); IPosition shp; if (itsNvals > 1 && shpt.nelements() > 0) { if (shpt.nelements() == 1) { shp = shpt; shp(0) = 1; } else { shp = shpt.getLast (shpt.nelements() - 1); } } else { shp = shpt; } if (! shp.isEqual (meas.shape())) { if (resize || meas.nelements() == 0) { meas.resize (shp); } else { throw(TableArrayConformanceError("ArrayMeasColumn::get")); } } Bool deleteMeas; M* meas_p = meas.getStorage (deleteMeas); // Set up get() for reference component of measure. Three possibilities: // 1. A column reference is used (itsMeasRef) // 2. The reference varies per row and is stored in a ScalarColumn. // 3. Ref varies per element of array (hence stored in an ArrayColumn). // With options 2 and 3 the reference could be either stored as a string // or int. MeasRef locMRef = itsMeasRef; Bool refPerElem = ((itsArrRefIntCol != 0) || (itsArrRefStrCol != 0)); Bool strRefs = (itsArrRefStrCol != 0); Array intRefArr; Array strRefArr; const Int* r_p=0; const String* sr_p=0; Bool deleteRef; if (refPerElem) { if (strRefs) { itsArrRefStrCol->get (rownr, strRefArr, True); sr_p = strRefArr.getStorage (deleteRef); } else { itsArrRefIntCol-> get (rownr, intRefArr, True); r_p = intRefArr.getStorage (deleteRef); } } else { if (itsRefIntCol != 0) { locMRef.set (measDesc().getRefDesc().tab2cur((*itsRefIntCol)(rownr))); } else if (itsRefStrCol != 0) { typename M::Types tp; M::getType (tp, (*itsRefStrCol)(rownr)); locMRef.set (tp); } } // Setup for offset component of MeasRef. Bool offsetPerElem = (itsArrOffsetCol != 0); Array offsetArr; const M* os_p=0; Bool deleteOffset; if (offsetPerElem) { itsArrOffsetCol->get (rownr, offsetArr, True); os_p = offsetArr.getStorage(deleteOffset); } else { if (itsOffsetCol != 0) { locMRef.set ((*itsOffsetCol)(rownr)); } } // Fill the measure array typename M::MVType measVal; const Vector& units = measDesc().getUnits(); Vector > qvec(itsNvals); for (uInt j=0; j tmpMRef; if (refPerElem) { if (strRefs) { typename M::Types tp; M::getType (tp, sr_p[i]); tmpMRef.set (tp); } else { tmpMRef.set (measDesc().getRefDesc().tab2cur(r_p[i])); } } else { tmpMRef.set (locMRef.getType()); } // the offset if (offsetPerElem) { tmpMRef.set (os_p[i]); } else if (locMRef.offset()) { tmpMRef.set (M(locMRef.offset())); } meas_p[i].set (measVal, tmpMRef); } } // clean up meas.putStorage (meas_p, deleteMeas); tmpData.freeStorage (d_ptr, deleteData); if (refPerElem) { if (strRefs) { strRefArr.freeStorage (sr_p, deleteRef); } else { intRefArr.freeStorage (r_p, deleteRef); } } if (offsetPerElem) { offsetArr.freeStorage (os_p, deleteOffset); } } template Array ArrayMeasColumn::operator() (rownr_t rownr) const { Array meas; get(rownr, meas); return meas; } template Array ArrayMeasColumn::convert (rownr_t rownr, const MeasRef& measRef) const { typename M::Convert conv; conv.setOut (measRef); return doConvert (rownr, conv); } template Array ArrayMeasColumn::convert (rownr_t rownr, uInt refCode) const { typename M::Convert conv; conv.setOut (typename M::Types(refCode)); return doConvert (rownr, conv); } template Array ArrayMeasColumn::doConvert (rownr_t rownr, typename M::Convert& conv) const { Array tmp; get (rownr, tmp); uInt n = tmp.nelements(); Bool deleteIt; M* data = tmp.getStorage (deleteIt); for (uInt i=0; i void ArrayMeasColumn::setDescRefCode (uInt refCode, Bool tableMustBeEmpty) { Table tab = table(); if (tableMustBeEmpty && tab.nrow() != 0) { throw (AipsError ("ArrayMeasColumn::setDescRefCode cannot be done; " "the table is not empty")); } itsDescPtr->resetRefCode (refCode); itsDescPtr->write (tab); itsMeasRef.set (refCode); } template void ArrayMeasColumn::setDescOffset (const Measure& offset, Bool tableMustBeEmpty) { Table tab = table(); if (tableMustBeEmpty && tab.nrow() != 0) { throw (AipsError ("ArrayMeasColumn::setDescOffset cannot be done; " "the table is not empty")); } itsDescPtr->resetOffset (offset); itsDescPtr->write (tab); itsMeasRef.set (offset); } template void ArrayMeasColumn::setDescUnits (const Vector& units, Bool tableMustBeEmpty) { Table tab = table(); if (tableMustBeEmpty && tab.nrow() != 0) { throw (AipsError ("ArrayMeasColumn::setDescUnits cannot be done; " "the table is not empty")); } itsDescPtr->resetUnits (units); itsDescPtr->write (tab); } template void ArrayMeasColumn::put (rownr_t rownr, const Array& meas) { // If meas has entries then need to resize the dataColArr to conform // to meas.Shape() + one dimension for storing the measure's values. const uInt n = meas.nelements(); IPosition shp(meas.shape()); if (n > 0 && itsNvals > 1) { shp.prepend (IPosition(1, itsNvals)); } Bool deleteData; Array dataArr(shp); Double* d_ptr = dataArr.getStorage(deleteData); Double* d_p = d_ptr; Bool deleteMeas; const M* meas_p = meas.getStorage(deleteMeas); // Set up put for reference component of measure. Three possibilities: // 1. The reference is non-variable so ignore reference component. // 2. The reference varies per row so write reference once using // reference from first measure in the array. No check is done // on the reference component of other measures. // 3. Ref varies per element of array. An array of references is written. // With 2 and 3 references are stored as either Strings or Ints. MeasRef locMRef = itsMeasRef; Bool refPerElem = ((itsArrRefIntCol != 0) || (itsArrRefStrCol != 0)); Bool strRefs = (itsArrRefStrCol != 0); Array intRefArr; Array strRefArr; Int* r_p; String* sr_p; Bool deleteRef; if (refPerElem) { // References are variable per array element. if (strRefs) { strRefArr.resize (meas.shape()); sr_p = strRefArr.getStorage (deleteRef); } else { intRefArr.resize (meas.shape()); r_p = intRefArr.getStorage (deleteRef); } } else if (itsVarRefFlag) { // References are variable per row only (thus same for entire array). // Use the reference of the first element as the reference. // Take care in case the array is empty. Int tp = 0; if (n > 0) { tp = meas_p->getRef().getType(); locMRef.set (tp); } if (itsRefIntCol != 0) { uInt tabRefCode = measDesc().getRefDesc().cur2tab (tp); itsRefIntCol->put (rownr, tabRefCode); } else if (itsRefStrCol != 0) { itsRefStrCol->put (rownr, M::showType(tp)); } } // Setup for offset. Bool offsetPerElem = (itsArrOffsetCol != 0); Array offsetArr; M* os_p; Bool deleteOffset; if (offsetPerElem) { // Offsets are variable array element. offsetArr.resize (meas.shape()); os_p = offsetArr.getStorage (deleteOffset); } else if (itsVarOffFlag) { // Offsets are variable per row only. // Use the offset from the first measure (if any). const Measure* offptr=0; if (n > 0) { offptr = meas_p->getRef().offset(); } if (offptr != 0) { M moff (offptr); locMRef.set (moff); itsOffsetCol->put (rownr, moff); } else itsOffsetCol->put (rownr, M()); } // If reference type and offset are variable per element, conversion // is not needed. const Vector& units = measDesc().getUnits(); Vector > qvec; for (uInt i=0; i& mref = meas_p[i].getRef(); uInt refCode = mref.getType(); const Measure* offptr = mref.offset(); if (refPerElem && offsetPerElem) { qvec = meas_p[i].getValue().getTMRecordValue(); } else { if (refPerElem) { locMRef.set (refCode); } if (offsetPerElem) { if (offptr != 0) { locMRef.set (M(offptr)); } else { locMRef.set (M()); } } typename M::Convert conv(meas_p[i], locMRef); M locMeas = conv(); qvec = locMeas.getValue().getTMRecordValue(); } if (refPerElem) { if (strRefs) { sr_p[i] = M::showType(refCode); } else { r_p[i] = measDesc().getRefDesc().cur2tab (refCode); } } if (offsetPerElem) { if (offptr != 0) { os_p[i] = M(offptr); } } for (uInt j=0; jput (rownr, dataArr); meas.freeStorage (meas_p, deleteMeas); if (refPerElem) { if (strRefs) { strRefArr.putStorage (sr_p, deleteRef); itsArrRefStrCol->put (rownr, strRefArr); } else { intRefArr.putStorage (r_p, deleteRef); itsArrRefIntCol->put (rownr, intRefArr); } } if (offsetPerElem) { offsetArr.putStorage (os_p, deleteOffset); itsArrOffsetCol->put (rownr, offsetArr); } } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/TableMeasures/ArrayQuantColumn.h000066400000000000000000000236501476623553700233300ustar00rootroot00000000000000//# ArrayQuantColumn.h: Access to an Array Quantum Column in a table. //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_ARRAYQUANTCOLUMN_H #define MEASURES_ARRAYQUANTCOLUMN_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class Table; template class ArrayColumn; template class ScalarColumn; class String; // // Provides read/write access to Array Quantum columns in Tables. // // // // // //# Classes you should understand before using this one. //
      • TableQuantumDesc //
      • Table //
      • ArrayColumn //
      • Quantum // // // The ArrayQuantColumn class provides read/write access to quanta // stored in a array Quantum Table column. The Quantum column should // already exist in the table and would have been defined by means of a // TableQuantumDesc object. // In addition, // for a ArrayQuantColumn object to be useful the column should // contain Quanta. // // The ArrayQuantColumn class is the array version // of the ScalarQuantColumn // class. // //

        Quantum Units

        // Quanta retrieved from the column will normally have the Unit that was // specified when the Quantum column was defined. // However, it is possible to override the default column Unit by // supplying a Unit in the ArrayQuantColumn constructor. // When constructed in this fashion the retrieved Quanta will by // default be retrieved in this unit, i.e. they will by default be // converted to this unit. //
        // By giving a unit (as a Unit or Quantum object) to a get function, // the data can be retrieved in another unit than the default. // // // (See TableQuantumDesc class // for an example of how to define a Quantum column). // // // Create the column object with default units "deg". // // It gets the quantum array from row 0 and prints it to stdout. // ArrayQuantColumn roaqCol(qtab, "ArrQuantDouble", "deg"); // cout << roaqCol(0) << endl; // // This retrieves the same array with units converted to "m/s". // cout << roaqCol(0, "m/s") << endl; // // // // Add support for Quanta in the Tables system. // // //
      • TableInvOper if the Table column is null. // // //# A List of bugs, limitations, extensions or planned refinements. //
      • Support for fixed unit per array element (e.g. for positions) // In that case #units should match first array dimension. //
      • Functions like getColumn, getSlice. //
      • get as Quantum>. //
      • optimize when converting when units are the same for entire array. // template class ArrayQuantColumn { public: // The default constructor creates a null object. It is useful for creating // arrays of ArrayQuantColumn objects. Attempting to use a null object // will produce a segmentation fault so care needs to be taken to // initialize the objects by using the attach() member before any attempt // is made to use the object. The isNull() member can be used to test // if a ArrayQuantColumn object is null. ArrayQuantColumn(); // Create the ArrayQuantColumn from the supplied table and column name. // The default unit for data retrieved is the unit in which they were stored. ArrayQuantColumn (const Table& tab, const String& columnName); // Create the ArrayQuantColumn from the supplied table and column name. // The default unit for data retrieved is the given unit (the data is // converted as needed). // ArrayQuantColumn (const Table& tab, const String& columnName, const Unit&); ArrayQuantColumn (const Table& tab, const String& columnName, const Vector&); // // Copy constructor (copy semantics). ArrayQuantColumn (const ArrayQuantColumn& that); ~ArrayQuantColumn(); // Make this object reference the column in "that". void reference (const ArrayQuantColumn& that); // Attach a column to the object. Optionally supply a default unit. // which has the same meaning as the constructor unit argument. // void attach (const Table& tab, const String& columnName); void attach (const Table& tab, const String& columnName, const Unit&); void attach (const Table& tab, const String& columnName, const Vector&); // // Get the quantum array in the specified row. // If resize is True the resulting array is resized if its shape // is not correct. Otherwise a "conformance exception" is thrown // if the array is not empty and its shape mismatches. // void get (rownr_t rownr, Array >& q, Bool resize = False) const; // Get the quantum array in the specified row. Each quantum is // converted to the given unit. void get (rownr_t rownr, Array >& q, const Unit&, Bool resize = False) const; // Get the quantum array in the specified row. Each quantum is // converted to the given units. void get (rownr_t rownr, Array >& q, const Vector&, Bool resize = False) const; // Get the quantum array in the specified row. Each quantum is // converted to the unit in other. void get (rownr_t rownr, Array >& q, const Quantum& other, Bool resize = False) const; // // Return the quantum array stored in the specified row. // Array > operator() (rownr_t rownr) const; // Return the quantum array stored in the specified row, converted // to the given unit. Array > operator() (rownr_t rownr, const Unit&) const; // Return the quantum array stored in the specified row, converted // to the given units. Array > operator() (rownr_t rownr, const Vector&) const; // Return the quantum array stored in the specified row, converted // to the unit in other. Array > operator() (rownr_t rownr, const Quantum& other) const; // // Put an array of quanta into the specified row of the table. // If the column supports variable units, the units are stored as well. // Otherwise the quanta are converted to the column's units. void put (rownr_t rownr, const Array >& q); // Test whether the Quantum column has variable units Bool isUnitVariable() const { return (itsArrUnitsCol || itsScaUnitsCol); } // Returns the column's units as a vector of strings. // An empty vector is returned if the column has no fixed units. Vector getUnits() const; // Test if the object is null. Bool isNull() const { return (itsDataCol == 0); } // Throw an exception if the object is null. void throwIfNull() const; protected: //# Quantum column's units (if units not variable) Vector itsUnit; // Get access to itsUnitsCol. // const ArrayColumn* arrUnitsCol() const { return itsArrUnitsCol; } const ScalarColumn* scaUnitsCol() const { return itsScaUnitsCol; } // private: //# The underlying data column stores the quantum column's data. ArrayColumn* itsDataCol; //# Variable units array column if applicable. ArrayColumn* itsArrUnitsCol; //# Variable units scalar column if applicable. ScalarColumn* itsScaUnitsCol; //# Units to retrieve the data in. Vector itsUnitOut; //# Convert unit when getting data? Bool itsConvOut; // Initialize the ArrayQuantColumn from the specified table and column. void init (const Table& tab, const String& columnName); // Deletes allocated memory etc. Called by ~tor and any member which needs // to reallocate data. void cleanUp(); // Get the data without possible conversion. void getData (rownr_t rownr, Array >& q, Bool resize) const; // Assignment makes no sense in a read only class. // Declaring this operator private makes it unusable. ArrayQuantColumn& operator= (const ArrayQuantColumn& that); // Comparison is not defined, since its semantics are unclear. Bool operator== (const ArrayQuantColumn& that); }; } //# NAMESPACE CASACORE - END //# Make old name ROArrayMeasColumn still available. #define ROArrayQuantColumn ArrayQuantColumn #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/measures/TableMeasures/ArrayQuantColumn.tcc000066400000000000000000000310741476623553700236510ustar00rootroot00000000000000//# ArrayQuantColumn.cc: Access to an Array Quantum Column in a table. //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_ARRAYQUANTCOLUMN_TCC #define MEASURES_ARRAYQUANTCOLUMN_TCC //# Includes #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template ArrayQuantColumn::ArrayQuantColumn() : itsDataCol (0), itsArrUnitsCol(0), itsScaUnitsCol(0), itsConvOut (False) {} template ArrayQuantColumn::ArrayQuantColumn (const Table& tab, const String& columnName) : itsDataCol (0), itsArrUnitsCol(0), itsScaUnitsCol(0), itsConvOut (False) { init (tab, columnName); itsUnitOut = itsUnit; } template ArrayQuantColumn::ArrayQuantColumn (const Table& tab, const String& columnName, const Unit& u) : itsDataCol (0), itsArrUnitsCol(0), itsScaUnitsCol(0) { init (tab, columnName); itsUnitOut.resize(1); itsUnitOut(0) = u; itsConvOut = (! itsUnitOut(0).getName().empty()); } template ArrayQuantColumn::ArrayQuantColumn (const Table& tab, const String& columnName, const Vector& u) : itsDataCol (0), itsArrUnitsCol(0), itsScaUnitsCol(0) { init (tab, columnName); itsUnitOut.resize(u.nelements()); itsUnitOut = u; itsConvOut = False; for (uInt i=0; i ArrayQuantColumn::ArrayQuantColumn (const ArrayQuantColumn& that) : itsDataCol (0), itsArrUnitsCol(0), itsScaUnitsCol(0) { reference (that); } template ArrayQuantColumn::~ArrayQuantColumn() { cleanUp(); } template void ArrayQuantColumn::cleanUp() { delete itsDataCol; itsDataCol = 0; delete itsArrUnitsCol; itsArrUnitsCol = 0; delete itsScaUnitsCol; itsScaUnitsCol = 0; } template void ArrayQuantColumn::init (const Table& tab, const String& columnName) { TableQuantumDesc* tqDesc = TableQuantumDesc::reconstruct(tab.tableDesc(), columnName); if (tqDesc->isUnitVariable()) { // the variable units column could be either an Array or a Scalar String varColName = tqDesc->unitColumnName(); if (tab.tableDesc().columnDesc(varColName).isScalar()) { itsScaUnitsCol = new ScalarColumn(tab, varColName); } else { itsArrUnitsCol = new ArrayColumn(tab, varColName); } } else { Vector units = tqDesc->getUnits(); itsUnit.resize (units.nelements()); for (uInt i=0; i(tab, columnName); delete tqDesc; } template void ArrayQuantColumn::reference (const ArrayQuantColumn& that) { cleanUp(); itsUnit.resize (that.itsUnit.nelements()); itsUnitOut.resize (that.itsUnitOut.nelements()); itsUnit = that.itsUnit; itsUnitOut = that.itsUnitOut; itsConvOut = that.itsConvOut; if (that.itsDataCol != 0) { itsDataCol = new ArrayColumn(*that.itsDataCol); } if (that.itsArrUnitsCol != 0) { itsArrUnitsCol = new ArrayColumn(*that.itsArrUnitsCol); } if (that.itsScaUnitsCol != 0) { itsScaUnitsCol = new ScalarColumn(*that.itsScaUnitsCol); } } template void ArrayQuantColumn::attach (const Table& tab, const String& columnName) { reference(ArrayQuantColumn (tab, columnName)); } template void ArrayQuantColumn::attach (const Table& tab, const String& columnName, const Unit& u) { reference(ArrayQuantColumn (tab, columnName, u)); } template void ArrayQuantColumn::attach (const Table& tab, const String& columnName, const Vector& u) { reference(ArrayQuantColumn (tab, columnName, u)); } template Vector ArrayQuantColumn::getUnits() const { Vector names(itsUnit.nelements()); for (uInt i=0; i void ArrayQuantColumn::getData (rownr_t rownr, Array >& q, Bool resize) const { // Quantums are created and put into q by taking T data from // itsDataCol and Quantum units from one of itsArrUnitsCol (if units // are in a ArrayColumn) or itsScaUnitsCol (if units are in a // ScalarColumn) or from itsUnit (if units are static). // getStorage() is used on each array to return pointers which are // used to iterate through the respective arrays. Bool deleteData; Array tmpDataCol = (*itsDataCol)(rownr); const T* d_p = tmpDataCol.getStorage(deleteData); // Ensure q is the correct size. Resize if needed. IPosition shp = tmpDataCol.shape(); if (!shp.isEqual(q.shape())) { if (resize || q.nelements() == 0) { q.resize(shp); } else { throw(TableArrayConformanceError("ArrayQuantColumn::get")); } } Bool deleteQuant; Quantum* q_p = q.getStorage(deleteQuant); const String* u_p=0; Bool deleteUnits; Array tmpUnitsCol; Vector localUnit(itsUnit); if (itsArrUnitsCol != 0) { Array tmp = (*itsArrUnitsCol)(rownr); tmpUnitsCol.reference (tmp); u_p = tmpUnitsCol.getStorage(deleteUnits); } else if (itsScaUnitsCol != 0) { localUnit.resize(1); localUnit(0) = (*itsScaUnitsCol)(rownr); } uInt nrun = localUnit.nelements(); uInt n = tmpDataCol.nelements(); for (uInt i=0; i void ArrayQuantColumn::get (rownr_t rownr, Array >& q, Bool resize) const { if (itsConvOut) { get (rownr, q, itsUnitOut, resize); } else { getData (rownr, q, resize); } } template void ArrayQuantColumn::get (rownr_t rownr, Array >& q, const Unit& u, Bool resize) const { getData (rownr, q, resize); if (! u.getName().empty()) { Bool deleteIt; Quantum* q_p = q.getStorage(deleteIt); uInt n = q.nelements(); for (uInt i=0; i void ArrayQuantColumn::get (rownr_t rownr, Array >& q, const Vector& u, Bool resize) const { getData (rownr, q, resize); Bool hasUnits = False; uInt nrun = u.nelements(); Vector hasUnit(nrun, False); for (uInt i=0; i* q_p = q.getStorage(deleteIt); uInt n = q.nelements(); for (uInt i=0; i void ArrayQuantColumn::get (rownr_t rownr, Array >& q, const Quantum& other, Bool resize) const { get (rownr, q, other.getFullUnit(), resize); } template Array > ArrayQuantColumn::operator() (rownr_t rownr) const { Array > q; get (rownr, q); return q; } template Array > ArrayQuantColumn::operator() (rownr_t rownr, const Unit& u) const { Array > q; get (rownr, q, u); return q; } template Array > ArrayQuantColumn::operator() (rownr_t rownr, const Vector& u) const { Array > q; get (rownr, q, u); return q; } template Array > ArrayQuantColumn::operator() (rownr_t rownr, const Quantum& other) const { Array > q; get (rownr, q, other); return q; } template void ArrayQuantColumn::throwIfNull() const { if (isNull()) { throw (TableInvOper("Quantum table column is null")); } } template void ArrayQuantColumn::put (rownr_t rownr, const Array >& q) { // Each quantum in q is separated out into its T component and // Unit component which are stored in itsDataCol and, if Units are // variable, itsArrUnitsCol repsectively. If units are not variable // each quantum is first converted into local units before it is // saved. // If q is empty, write empty arrays. const uInt n = q.nelements(); if (n == 0) { Array arr; itsDataCol->put (rownr, arr); if (itsArrUnitsCol != 0) { Array arru; itsArrUnitsCol->put (rownr, arru); } else if (itsScaUnitsCol != 0) { itsScaUnitsCol->put (rownr, String()); } return; } Array dataArr(q.shape()); Bool deleteData; T* d_p = dataArr.getStorage(deleteData); Bool deleteQuant; const Quantum* q_p = q.getStorage(deleteQuant); // If units are variable they could vary per element of the quantum // array (i.e., the units column is also an array) or they could vary // by row (i.e., the units column is a Scalar Column where each row // contains a single unit entry). When variable by row, the unit of the // first quantum in q is used. Bool deleteUnits; String* u_p; Array unitsArr; Vector localUnit(itsUnit); if (itsArrUnitsCol != 0) { unitsArr.resize(q.shape()); u_p = unitsArr.getStorage(deleteUnits); } else if (itsScaUnitsCol != 0) { // Take the value for unit from the first entry in q. This // is safe because we know here that q contains at least 1 entry. localUnit.resize(1); localUnit(0) = q_p[0].getFullUnit(); itsScaUnitsCol->put (rownr, localUnit(0).getName()); } uInt nrun = localUnit.nelements(); // Copy the value component of each quantum into the local data array. // If using an array to store units, copy quantum unit to local unit array for (uInt i=0; iput (rownr, dataArr); if (itsArrUnitsCol != 0) { unitsArr.putStorage (u_p, deleteUnits); itsArrUnitsCol->put (rownr, unitsArr); } q.freeStorage(q_p, deleteQuant); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/TableMeasures/ScalarMeasColumn.h000066400000000000000000000232101476623553700232440ustar00rootroot00000000000000//# ScalarMeasColumn.h: Access to Scalar Measure Columns in Tables. //# Copyright (C) 1997,1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_SCALARMEASCOLUMN_H #define MEASURES_SCALARMEASCOLUMN_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class ArrayColumn; template class ScalarColumn; // // Read only access to table scalar Measure columns. // // // // // //# Classes you should understand before using this one. //
      • Measures //
      • Tables //
      • TableMeasDesc // // // ScalarMeasColumn objects can be used to access scalar Measure Columns // in tables, both for reading and writing (if the table is writable). // // Before a column can be accessed it must have previously been defined as // a Measure column by use of the // TableMeasDesc object. // // The ScalarMeasColumn class is templated on Measure type. // Typedefs exist in the various Measure classes // (e.g. MEpoch) to make declaration // less long winded. // Constructing scalar Measure column objects using these typedefs looks like // this: // // MEpoch::ScalarMeasColumn ec(table, "ColumnName); // // //

        Reading and writing Measures

        // // The reading and writing of Measures columns is very similar to reading and // writing of "ordinary" Table columns. // get() // and operator() // exist for reading Measures and the // put() member for adding // Measures to a column. (put() is obviously not defined for // ScalarMeasColumn objects.) Each of these members accepts a row number // as an argument. // The get() function gets the measure with the reference and offset as // it is stored in the column. Furthermore the convert() function is // available to get the measure with the given reference, possible offset, // and possible frame // // When a Measure is put, the reference and possible offset are converted // if the measure column is defined with a fixed reference and/or offset. // If the column's reference and offset are variable, the reference and // offset of the measure as put are written into the appropriate // reference and offset columns. //
        // // // // This creates a Scalar MEpoch column for read/write access. Column // // "Time1" must exist in Table "tab" and must have previously been // // defined as a MEpoch column using a TableMeasDesc. // MEpoch::ScalarMeasColumn timeCol(tab, "Time1"); // // // print some details about the column // if (timeCol.measDesc().isRefCodeVariable()) { // cout << "The column has variable references." << endl; // } else { // cout << "The fixed MeasRef for the column is: " // << timeCol.getMeasRef() << endl; // } // // // Add tab.nrow() measures to the column. // MEpoch tm(Quantity(MeasData::MJD2000, "d"), MEpoch::TAI); // for (rownr_t i=0; i // // // The standard Casacore Table system does not support Measures columns. // This class overcomes this limitation. // // // //
      • AipsError during construction if the column specified variable // offsets which are stored in an Array- rather than a ScalarColumn. // // //# //# template class ScalarMeasColumn : public TableMeasColumn { public: // The default constructor creates a null object. Useful for creating // arrays of ScalarMeasColumn objects. Attempting to use a null object // will produce a segmentation fault so care needs to be taken to // initialize the objects first by using attach(). // An ScalarMeasColumn object can be tested if it is null by using the // isNull() member. ScalarMeasColumn(); // Create the ScalarMeasColumn from the table and column Name. ScalarMeasColumn (const Table& tab, const String& columnName); // Copy constructor (copy semantics). ScalarMeasColumn (const ScalarMeasColumn& that); virtual ~ScalarMeasColumn(); // Change the reference to another column. void reference (const ScalarMeasColumn& that); // Attach a column to the object. void attach (const Table& tab, const String& columnName); // Get the Measure contained in the specified row. // It returns the Measure as found in the table. // void get (rownr_t rownr, M& meas) const; M operator() (rownr_t rownr) const; // // Get the Measure contained in the specified row and convert // it to the reference and offset found in the given measure. M convert (rownr_t rownr, const M& meas) const { return convert (rownr, meas.getRef()); } // Get the Measure contained in the specified row and convert // it to the given reference. // M convert (rownr_t rownr, const MeasRef& measRef) const; M convert (rownr_t rownr, uInt refCode) const; // // Returns the column's fixed reference or the reference of the last // read Measure if references are variable. const MeasRef& getMeasRef() const { return itsMeasRef; } // Reset the refCode, offset, or units. // It overwrites the value used when defining the TableMeasDesc. // Resetting the refCode and offset can only be done if they were // defined as fixed in the description. // // In principle the functions can only be used if the table is empty, // otherwise already written values have thereafter the incorrect // reference, offset, or unit. // However, it is possible that part of the table is already // written and that the entire measure column is filled in later. // In that case the reference, offset, or units can be set by using // a False tableMustBeEmpty argument. // // void setDescRefCode (uInt refCode, Bool tableMustBeEmpty=True); void setDescOffset (const Measure& offset, Bool tableMustBeEmpty=True); void setDescUnits (const Vector& units, Bool tableMustBeEmpty=True); // // Put a Measure into the given row. // void put (rownr_t rownr, const M& meas); // protected: // Make a MeasRef for the given row. MeasRef makeMeasRef (rownr_t rownr) const; private: //# Whether conversion is needed during a put. True if either //# the reference code or offset is fixed for the column Bool itsConvFlag; //# Column which contains the Measure's actual data. An array column //# is needed if the data component of the underlying Measure is //# represented by more than 1 value ArrayColumn* itsArrDataCol; ScalarColumn* itsScaDataCol; //# Its MeasRef code column when references are variable. ScalarColumn* itsRefIntCol; ScalarColumn* itsRefStrCol; //# Column containing its variable offsets. Only applicable if the //# measure references have offsets and they are variable. ScalarMeasColumn* itsOffsetCol; //# This is either the column's fixed Measure reference or the reference //# of the last Measure read. MeasRef itsMeasRef; // Assignment makes no sense in a readonly class. // Declaring this operator private makes it unusable. ScalarMeasColumn& operator= (const ScalarMeasColumn& that); // Check if refs have the same value (as opposed to being the same object). Bool equalRefs (const MRBase& r1, const MRBase& r2) const; //# Deletes allocated memory etc. Called by ~tor and any member which //# needs to reallocate data. void cleanUp(); }; } //# NAMESPACE CASACORE - END //# Make old name ROScalarMeasColumn still available. #define ROScalarMeasColumn ScalarMeasColumn #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/measures/TableMeasures/ScalarMeasColumn.tcc000066400000000000000000000273671476623553700236070ustar00rootroot00000000000000//# Copyright (C) 1997,1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_SCALARMEASCOLUMN_TCC #define MEASURES_SCALARMEASCOLUMN_TCC //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template ScalarMeasColumn::ScalarMeasColumn() : itsConvFlag (False), itsArrDataCol(0), itsScaDataCol(0), itsRefIntCol (0), itsRefStrCol (0), itsOffsetCol (0) {} template ScalarMeasColumn::ScalarMeasColumn (const Table& tab, const String& columnName) : TableMeasColumn (tab, columnName), itsConvFlag (False), itsArrDataCol(0), itsScaDataCol(0), itsRefIntCol (0), itsRefStrCol (0), itsOffsetCol (0) { TableMeasDescBase& tmDesc = measDesc(); AlwaysAssert(M::showMe() == tmDesc.type(), AipsError); // Create the data column. If the underlying measure can handle a // single value for its data then use a ScalarColumn otherwise an // ArrayColumn is needed to store the data component of the Measures. M tMeas; itsNvals = tMeas.getValue().getTMRecordValue().nelements(); AlwaysAssert (itsNvals <= tmDesc.getUnits().size(), AipsError); if (itsNvals == 1) { itsScaDataCol = new ScalarColumn(tab, columnName); } else { itsArrDataCol = new ArrayColumn(tab, columnName); } // Set up the reference code component of the MeasRef if (tmDesc.isRefCodeVariable()) { const String& rcName = tmDesc.refColumnName(); if ((tab.tableDesc().columnDesc(rcName).dataType() == TpString)) { itsRefStrCol = new ScalarColumn(tab, rcName); } else { itsRefIntCol = new ScalarColumn(tab, rcName); } } else { itsMeasRef.set (tmDesc.getRefCode()); } // Set up the offset component of the MeasRef if (tmDesc.hasOffset()) { if (tmDesc.isOffsetVariable()) { if (tmDesc.isOffsetArray()) { throw(AipsError("ScalarMeasColumn::ScalarMeasColumn " "Offset column must be a ScalarMeasColumn.")); } itsOffsetCol = new ScalarMeasColumn(tab, tmDesc.offsetColumnName()); } else { itsMeasRef.set (tmDesc.getOffset()); } } // Only need to convert (during a put) if some component of the reference // for the column is fixed. itsConvFlag = (itsVarRefFlag == False) || (itsOffsetCol == 0); // For an old table write the reference codes and types. if (tab.isWritable()) { tmDesc.writeIfOld (tab); } } template ScalarMeasColumn::ScalarMeasColumn (const ScalarMeasColumn& that) : TableMeasColumn(), itsArrDataCol(0), itsScaDataCol(0), itsRefIntCol (0), itsRefStrCol (0), itsOffsetCol (0) { reference (that); } template ScalarMeasColumn::~ScalarMeasColumn() { cleanUp(); } template void ScalarMeasColumn::cleanUp() { delete itsArrDataCol; delete itsScaDataCol; delete itsRefIntCol; delete itsRefStrCol; delete itsOffsetCol; } template void ScalarMeasColumn::reference (const ScalarMeasColumn& that) { cleanUp(); TableMeasColumn::reference (that); itsConvFlag = that.itsConvFlag; itsArrDataCol = that.itsArrDataCol; itsScaDataCol = that.itsScaDataCol; itsRefIntCol = that.itsRefIntCol; itsRefStrCol = that.itsRefStrCol; itsOffsetCol = that.itsOffsetCol; itsMeasRef = that.itsMeasRef; if (itsArrDataCol != 0) { itsArrDataCol = new ArrayColumn(*itsArrDataCol); } if (itsScaDataCol != 0) { itsScaDataCol = new ScalarColumn(*itsScaDataCol); } if (itsRefIntCol != 0) { itsRefIntCol = new ScalarColumn(*itsRefIntCol); } if (itsRefStrCol != 0) { itsRefStrCol = new ScalarColumn(*itsRefStrCol); } if (itsOffsetCol != 0) { itsOffsetCol = new ScalarMeasColumn(*itsOffsetCol); } } template void ScalarMeasColumn::attach (const Table& tab, const String& columnName) { reference (ScalarMeasColumn(tab, columnName)); } template void ScalarMeasColumn::get (rownr_t rownr, M& meas) const { Vector > qvec(itsNvals); const Vector& units = measDesc().getUnits(); if (itsScaDataCol != 0) { qvec(0).setValue ((*itsScaDataCol)(rownr)); qvec(0).setUnit (units(0)); } else { Array tmpArr((*itsArrDataCol)(rownr)); Bool deleteData; const Double* d_p = tmpArr.getStorage (deleteData); for (uInt i=0; i M ScalarMeasColumn::convert (rownr_t rownr, const MeasRef& measRef) const { M tmp; get (rownr, tmp); return typename M::Convert(tmp, measRef)(); } template M ScalarMeasColumn::convert (rownr_t rownr, uInt refCode) const { M tmp; get (rownr, tmp); return typename M::Convert(tmp, typename M::Types(refCode))(); } template M ScalarMeasColumn::operator() (rownr_t rownr) const { M meas; get (rownr, meas); return meas; } template MeasRef ScalarMeasColumn::makeMeasRef (rownr_t rownr) const { // Fixed reference can be returned immediately. if (!itsVarRefFlag && itsOffsetCol == 0) { return itsMeasRef; } MeasRef locMRef (itsMeasRef); if (itsVarRefFlag) { // Get reference type as int (from a string or int column). if (itsRefStrCol != 0) { typename M::Types tp; M::getType (tp, (*itsRefStrCol)(rownr)); locMRef.set (tp); } else { locMRef.set (measDesc().getRefDesc().tab2cur((*itsRefIntCol)(rownr))); } } if (itsOffsetCol != 0) { locMRef.set ((*itsOffsetCol)(rownr)); } return locMRef; } template void ScalarMeasColumn::setDescRefCode (uInt refCode, Bool tableMustBeEmpty) { Table tab = table(); if (tableMustBeEmpty && tab.nrow() != 0) { throw (AipsError ("ScalarMeasColumn::setDescRefCode cannot be done; " "the table is not empty")); } itsDescPtr->resetRefCode (refCode); itsDescPtr->write (tab); itsMeasRef.set (refCode); } template void ScalarMeasColumn::setDescOffset (const Measure& offset, Bool tableMustBeEmpty) { Table tab = table(); if (tableMustBeEmpty && tab.nrow() != 0) { throw (AipsError ("ScalarMeasColumn::setDescOffset cannot be done; " "the table is not empty")); } itsDescPtr->resetOffset (offset); itsDescPtr->write (tab); itsMeasRef.set (offset); } template void ScalarMeasColumn::setDescUnits (const Vector& units, Bool tableMustBeEmpty) { Table tab = table(); if (tableMustBeEmpty && tab.nrow() != 0) { throw (AipsError ("ScalarMeasColumn::setDescUnits cannot be done; " "the table is not empty")); } itsDescPtr->resetUnits (units); itsDescPtr->write (tab); } template void ScalarMeasColumn::put (rownr_t rownr, const M& meas) { // A few things about put: // 1. No support for storage of frames so if the meas has a frame and // this column has variable references throw an exception. // 2. Convert meas if reference and/or offset are different and // not variable for the column. // 3. Convert units if different. // When the reference is variable, measure's reference and offset // are saved as well as the measure's value. // check if the entered measure is "legal" if (itsVarRefFlag) { if (! meas.getRefPtr()->getFrame().empty()) { throw(AipsError("ScalarMeasColumn::put() measure has a frame." " Illegal for variable reference column.")); } } M locMeas = meas; // Conversion is needed if the reference for the incoming measure is // not equal to that of the column (and itsConvFlag is true) if (itsConvFlag && !equalRefs(itsMeasRef, locMeas.getRef())) { MeasRef refConv = itsMeasRef; if (itsVarRefFlag) { refConv.set (locMeas.getRef().getType()); } // cerr << "\nDOING CONVERT!!!!\n"; // cerr << "itsMeasRef: " << refConv << endl; // cerr << "locMeasRef: " << locMeas.getRef() << endl; // cerr << "itsMeasRef: " << refConv.offset() << ' '; // if (refConv.offset()) { // cerr << *refConv.offset() << ' '; // refConv.offset()->print(cerr); // refConv.offset()->getRefPtr()->print(cerr); // cerr << refConv.offset()->getRefPtr()->offset(); // } // cerr << endl; // cerr << "locMeasRef: " << locMeas.getRef().offset() << endl; // if (locMeas.getRef().offset()) { // cerr << *locMeas.getRef().offset() << ' '; // locMeas.getRef().offset()->print(cerr); // locMeas.getRef().offset()->getRefPtr()->print(cerr); // cerr << locMeas.getRef().offset()->getRefPtr()->offset(); // } // cerr << endl; // cerr << "Before convert: " << locMeas << locMeas.getRef() << endl; typename M::Convert conv(locMeas, refConv); locMeas = conv(); // cerr << " After convert: " << locMeas << locMeas.getRef() << endl; } if (itsVarRefFlag) { if (itsRefStrCol != 0) { itsRefStrCol->put(rownr, M::showType(locMeas.getRef().getType())); } else { uInt tp = locMeas.getRef().getType(); itsRefIntCol->put(rownr, measDesc().getRefDesc().cur2tab (tp)); } } if (itsOffsetCol != 0) { if (locMeas.getRef().offset() != 0) { itsOffsetCol->put(rownr, M(locMeas.getRef().offset())); } else { itsOffsetCol->put(rownr, M()); } } const Vector& units = measDesc().getUnits(); Vector > qvec = locMeas.getValue().getTMRecordValue(); if (itsScaDataCol != 0) { itsScaDataCol->put (rownr, qvec(0).getValue(units(0))); } else { Vector d_vec(itsNvals); for (uInt i=0; iput (rownr, d_vec); } } template Bool ScalarMeasColumn::equalRefs (const MRBase& r1, const MRBase& r2) const { return ((r1.getType() == r2.getType()) && (r1.offset() == r2.offset())); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/TableMeasures/ScalarQuantColumn.h000066400000000000000000000220411476623553700234500ustar00rootroot00000000000000//# ScalarQuantColumn.h: Access to a Scalar Quantum Column in a table. //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_SCALARQUANTCOLUMN_H #define MEASURES_SCALARQUANTCOLUMN_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class Table; template class ScalarColumn; class String; class Unit; // // Provides access to Scalar Quantum Columns in Tables. // // // // // //# Classes you should understand before using this one. //
      • TableQuantumDesc //
      • Table //
      • ScalarColumn //
      • Quantum // // // The ScalarQuantColumn class provides read/write access to quanta // stored in a scalar Quantum Table column. The Quantum column should // already exist in the table and would have been defined by means of a // TableQuantumDesc object. // In addition, // for a ScalarQuantColumn object to be useful the column should // contain Quanta. // // A ScalarQuantColumn object is used much in the same way as a // ScalarColumn object. // //

        Quantum Units

        // Quanta retrieved from the column will normally have the Unit that was // specified when the Quantum column was defined. // However, it is possible to override the default column Unit by // supplying a Unit in the ScalarQuantColumn constructor. // When constructed in this fashion the retrieved Quanta will by // default be retrieved in this unit, i.e. they will by default be // converted to this unit. //
        // By giving a unit (as a Unit or Quantum object) to a get function, // the data can be retrieved in another unit than the default. //
        // // // Quantum q(5.3, "keV"); // // "QuantScalar" has previously been defined as a Quantum column // // by means of a TableQuantumDesc. This example assumes the column // // already contains quanta. // ScalarQuantColumn qCol(qtab, "QuantScalar"); // // return and print quanta as stored in the column // for (i = 0; i < qtab.nrow(); i++) { // cout << qCol(i) << endl; // } // // The following retrieves and converts the quanta to GHz. They // // are then divided by the Quantum constant QC::h (Planck). // for (i=0; i < qtab.nrow(); i++) { // cout << (qCol(i, "GHz"))/(QC::h); // } // // // // Add support for Quanta in the Tables system. // // //
      • TableInvOper if the Table column is null. // // //# A List of bugs, limitations, extensions or planned refinements. //
      • Functions like getColumn // template class ScalarQuantColumn { public: // The default constructor creates a null object. It is useful for creating // arrays of ScalarQuantColumn objects. Attempting to use a null object // will produce a segmentation fault so care needs to be taken to // initialise the objects by using the attach() member before any attempt // is made to use the object. The isNull() member can be used to test // if a ScalarQuantColumn object is null. ScalarQuantColumn(); // Create the ScalarQuantColumn from the specified table and column name. // The default unit for data retrieved is the unit in which they were stored. ScalarQuantColumn (const Table& tab, const String& columnName); // Create the ScalarQuantColumn from the specified table and column name. // The default unit for data retrieved is the given unit (the data is // converted as needed). ScalarQuantColumn (const Table& tab, const String& columnName, const Unit&); // Copy constructor (copy semantics). ScalarQuantColumn (const ScalarQuantColumn& that); ~ScalarQuantColumn(); // Change the reference to another column. void reference (const ScalarQuantColumn& that); // Attach a column to the object. Optionally supply a default unit // which has the same meaning as the constructor unit argument. // void attach (const Table& tab, const String& columnName); void attach (const Table& tab, const String& columnName, const Unit&); // // Get the quantum stored in the specified row. // void get (rownr_t rownr, Quantum& q) const; // Get the quantum in the specified row, converted to the given unit. void get (rownr_t rownr, Quantum& q, const Unit&) const; // Get the quantum in the specified row, converted to the unit in other. void get (rownr_t rownr, Quantum& q, const Quantum& other) const; // // Return the quantum stored in the specified row. // Quantum operator() (rownr_t rownr) const; // Return the quantum stored in the specified row, converted to the // given unit. Quantum operator() (rownr_t rownr, const Unit&) const; // Return the quantum in the specified row, converted to the unit in // other. Quantum operator() (rownr_t rownr, const Quantum& other) const; // // Put a quantum into the table. If the column supports variable units // the q's unit is stored into the unit column defined in the // TableQuantumDesc object. If units are fixed for the column, the // quantum is converted as needed. void put (rownr_t rownr, const Quantum& q); // Test whether the Quantum column has variable units Bool isUnitVariable() const { return (itsUnitsCol != 0); } // Returns the column's value for Units as a string. // An empty string is returned if the column has variable units. const String& getUnits() const { return itsUnit.getName(); } // Test if the object is null. Bool isNull() const { return (itsDataCol == 0); } // Throw an exception if the object is null. void throwIfNull() const; // Get the column as a Quantum >. If unit is // not empty, the returned Quantum will have that unit. Else if // the units are variable, the values in the returned Vector // have been converted to the unit of the 0th row entry. Otherwise, // the units of the returned Quantum are the units specified in // the column descriptor. std::shared_ptr > > getColumn(const Unit& unit="") const; protected: //# Quantum column's units (if units not variable) Unit itsUnit; // Get access to itsUnitsCol. const ScalarColumn* unitsCol() const { return itsUnitsCol; } private: //# The underlying data column stores the quantum column's data. ScalarColumn* itsDataCol; //# Variable units column if applicable. ScalarColumn* itsUnitsCol; //# Unit to retrieve the data in. Unit itsUnitOut; //# Convert unit when getting data? Bool itsConvOut; // Assignment makes no sense in a read only class. // Declaring this operator private makes it unusable. ScalarQuantColumn& operator= (const ScalarQuantColumn& that); // Comparison is not defined, since its semantics are unclear. Bool operator== (const ScalarQuantColumn& that); // Initialize the ScalarQuantColumn from the specified table and column. void init (const Table& tab, const String& columnName); // Deletes allocated memory etc. Called by destructor and any member // which needs to reallocate data. void cleanUp(); // Get the data without possible conversion. void getData (rownr_t rownr, Quantum& q) const; }; } //# NAMESPACE CASACORE - END //# Make old name ROScalarMeasColumn still available. #define ROScalarQuantColumn ScalarQuantColumn #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/measures/TableMeasures/ScalarQuantColumn.tcc000066400000000000000000000165231476623553700240020ustar00rootroot00000000000000//# ScalarQuantColumn.cc: Access to a Scalar Quantum Column in a table. //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_SCALARQUANTCOLUMN_TCC #define MEASURES_SCALARQUANTCOLUMN_TCC //# Includes #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template ScalarQuantColumn::ScalarQuantColumn() : itsDataCol (0), itsUnitsCol(0), itsConvOut (False) {} template ScalarQuantColumn::ScalarQuantColumn (const Table& tab, const String& columnName) : itsDataCol (0), itsUnitsCol(0), itsConvOut (False) { init (tab, columnName); itsUnitOut = itsUnit; } template ScalarQuantColumn::ScalarQuantColumn (const Table& tab, const String& columnName, const Unit& u) : itsDataCol (0), itsUnitsCol(0) { init (tab, columnName); itsUnitOut = u; itsConvOut = (! itsUnitOut.getName().empty()); } template ScalarQuantColumn::~ScalarQuantColumn() { cleanUp(); } template void ScalarQuantColumn::cleanUp() { delete itsDataCol; itsDataCol = 0; delete itsUnitsCol; itsUnitsCol = 0; } template ScalarQuantColumn::ScalarQuantColumn (const ScalarQuantColumn& that) : itsDataCol (0), itsUnitsCol(0) { reference (that); } template void ScalarQuantColumn::init (const Table& tab, const String& columnName) { TableQuantumDesc* tqDesc = TableQuantumDesc::reconstruct (tab.tableDesc(), columnName); if (tqDesc->isUnitVariable()) { itsUnitsCol = new ScalarColumn(tab, tqDesc->unitColumnName()); } else { Vector units (tqDesc->getUnits()); if (units.nelements() > 0) { if (units.nelements() > 1) { throw (AipsError ("ScalarQuantColumn is used for column " + columnName + " but its description has >1 units")); } itsUnit = units(0); } } itsDataCol = new ScalarColumn(tab, columnName); delete tqDesc; } template void ScalarQuantColumn::reference (const ScalarQuantColumn& that) { cleanUp(); itsUnit = that.itsUnit; itsUnitOut = that.itsUnitOut; itsConvOut = that.itsConvOut; if (that.itsDataCol != 0) { itsDataCol = new ScalarColumn(*that.itsDataCol); } if (that.itsUnitsCol != 0) { itsUnitsCol = new ScalarColumn(*that.itsUnitsCol); } } template void ScalarQuantColumn::attach (const Table& tab, const String& columnName) { reference (ScalarQuantColumn(tab, columnName)); } template void ScalarQuantColumn::attach (const Table& tab, const String& columnName, const Unit& u) { reference (ScalarQuantColumn(tab, columnName, u)); } template void ScalarQuantColumn::throwIfNull() const { if (isNull()) { throw (TableInvOper("Quantum table column is null")); } } template void ScalarQuantColumn::getData (rownr_t rownr, Quantum& q) const { // Quantums are created from Ts stored in itsDataCol and Units // in itsUnitsCol, if units are variable, or itsUnit if non-variable. q.setValue ((*itsDataCol)(rownr)); if (itsUnitsCol != 0) { q.setUnit ((*itsUnitsCol)(rownr)); } else { q.setUnit (itsUnit); } } template void ScalarQuantColumn::get (rownr_t rownr, Quantum& q) const { getData (rownr, q); if (itsConvOut) { q.convert (itsUnitOut); } } template void ScalarQuantColumn::get (rownr_t rownr, Quantum& q, const Unit& u) const { getData (rownr, q); q.convert (u); } template void ScalarQuantColumn::get (rownr_t rownr, Quantum& q, const Quantum& other) const { getData (rownr, q); q.convert (other); } template Quantum ScalarQuantColumn::operator() (rownr_t rownr) const { Quantum q; get (rownr, q); return q; } template Quantum ScalarQuantColumn::operator() (rownr_t rownr, const Unit& u) const { Quantum q; get (rownr, q, u); return q; } template Quantum ScalarQuantColumn::operator() (rownr_t rownr, const Quantum& other) const { Quantum q; get (rownr, q, other); return q; } template void ScalarQuantColumn::put (rownr_t rownr, const Quantum& q) { // The value component of the quantum is stored in itsDataCol and the // unit component in itsUnitsCol unless Units are non-variable in // which case the Unit component is ignored (i.e., the Quantum's unit // is not checked against the Column's unit). if (itsUnitsCol != 0) { itsUnitsCol->put (rownr, q.getUnit()); itsDataCol->put (rownr, q.getValue()); } else { itsDataCol->put (rownr, q.getValue(itsUnit)); } } template std::shared_ptr > > ScalarQuantColumn::getColumn(const Unit& unit) const { std::shared_ptr > > qv; if ((itsUnitsCol && itsUnitsCol->nrow() > 0) || ! unit.empty()) { Unit unitOut; if (! unit.empty()) { unitOut = unit; } else { unitOut = itsUnitsCol->get(0); } qv.reset(new Quantum >(Vector(), unitOut)); Vector& val = qv->getValue(); itsDataCol->getColumn(val); Quantum q; for (uInt i = 0; i < val.size(); ++i) { get(i, q, unitOut); val[i] = q.getValue(); } } else { qv.reset(new Quantum >(Vector(), itsUnit)); Vector& val = qv->getValue(); itsDataCol->getColumn(val); } return qv; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/TableMeasures/TableMeasColumn.cc000066400000000000000000000065721476623553700232400ustar00rootroot00000000000000//# TableMeasColumn.cc: Access to Measure Columns in Tables. //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableMeasColumn::TableMeasColumn() : itsNvals (0), itsVarRefFlag (False), itsVarOffFlag (False) {} TableMeasColumn::TableMeasColumn (const Table& tab, const String& columnName) : itsNvals (0), itsTabDataCol (tab, columnName) { itsDescPtr.reset (TableMeasDescBase::reconstruct (tab, columnName)); itsVarRefFlag = itsDescPtr->isRefCodeVariable(); itsVarOffFlag = itsDescPtr->isOffsetVariable(); } TableMeasColumn::TableMeasColumn (const TableMeasColumn& that) : itsNvals (that.itsNvals), itsDescPtr (that.itsDescPtr), itsTabDataCol (that.itsTabDataCol), itsVarRefFlag (that.itsVarRefFlag), itsVarOffFlag (that.itsVarOffFlag) {} TableMeasColumn::~TableMeasColumn() {} void TableMeasColumn::reference (const TableMeasColumn& that) { itsNvals = that.itsNvals; itsDescPtr = that.itsDescPtr; itsTabDataCol.reference (that.itsTabDataCol); itsVarRefFlag = that.itsVarRefFlag; itsVarOffFlag = that.itsVarOffFlag; } void TableMeasColumn::attach (const Table& tab, const String& columnName) { reference (TableMeasColumn (tab, columnName)); } const String& TableMeasColumn::columnName() const { return itsDescPtr->columnName(); } Bool TableMeasColumn::isDefined (rownr_t rownr) const { return itsTabDataCol.isDefined (rownr); } void TableMeasColumn::throwIfNull() const { if (isNull()) { throw (TableInvOper("MeasTableColumn object is null")); } } Table TableMeasColumn::table() const { return itsTabDataCol.table(); } Bool TableMeasColumn::isScalar() const { if (itsTabDataCol.columnDesc().isScalar()) { return True; } IPosition shape = itsTabDataCol.shapeColumn(); if (shape.nelements() == 1) { if (itsNvals == 0 || Int(itsNvals) == shape(0)) { return True; } } return False; } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/TableMeasures/TableMeasColumn.h000066400000000000000000000135271476623553700231000ustar00rootroot00000000000000//# TableMeasColumn.h: Access to Measure Columns in Tables //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_TABLEMEASCOLUMN_H #define MEASURES_TABLEMEASCOLUMN_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class String; class Table; class TableMeasDescBase; // // Read only access to table scalar Measure columns. // // // // // //# Classes you should understand before using this one. //
      • Measures //
      • Tables //
      • TableMeasDesc // // // TableMeasColumn is the base class for the templated classes // ScalarMeasColumn and // ArrayMeasColumn // which give access to table columns containing // measures. // // This base class offers some common functionality like getting // the column name and testing if a row of the column contains a value. // Its main function is measDesc(), which gives access // to the TableMeasDescBase // object containing a description of the measure column. // // // // // Create the object for measure column Time1. // TableMeasColumn timeCol(tab, "Time1"); // // // print some details about the column // if (timeCol.measDesc().isRefCodeVariable()) { // cout << "The column has variable references." << endl; // } else { // cout << "The fixed MeasRef for the column is: " // << timeCol.getMeasRef() << endl; // } // // // // This class contains the common functionality for the templated // derived classes. // //# //# class TableMeasColumn { public: // The default constructor creates a null object. Useful for creating // arrays of ScalarMeasColumn objects. Attempting to use a null object // will produce a segmentation fault so care needs to be taken to // initialise the objects first by using attach(). // An ScalarMeasColumn object can be tested if it is null by using the // isNull() member. TableMeasColumn(); // Create the ScalarMeasColumn from the table and column Name. TableMeasColumn (const Table& tab, const String& columnName); // Copy constructor (copy semantics). TableMeasColumn (const TableMeasColumn& that); virtual ~TableMeasColumn(); // Change the reference to another column. void reference (const TableMeasColumn& that); // Attach another column to the object. void attach (const Table& tab, const String& columnName); // Tests if a row contains a Measure (i.e., if the row has a defined // value). Bool isDefined (rownr_t rownr) const; // Get access to the TableMeasDescBase describing the column. // const TableMeasDescBase& measDesc() const { return *itsDescPtr; } TableMeasDescBase& measDesc() { return *itsDescPtr; } // // Test if the object is null. Bool isNull() const { return !itsDescPtr; } // Throw an exception if the object is null. void throwIfNull() const; // Get the name of the column. const String& columnName() const; // Get the Table object this column belongs to. Table table() const; // Is the column a scalar measures column? // It is if the underlying column is a scalar column or an array // column with a fixed 1-dimensional shape. //
        Otherwise it is an array measures column. // // It is not 100% determined if a measure column is an array or a scalar. // If the underlying table column is an array with a variable shape, // this function will see it as an array measure column. However, // it might be accessible as a scalar measure column. // Bool isScalar() const; protected: //# The measure's value is represented by this many data components. uInt itsNvals; //# The Measure Column description. std::shared_ptr itsDescPtr; //# The data column. TableColumn itsTabDataCol; //# Does the measure column have a variable reference or offset? Bool itsVarRefFlag; Bool itsVarOffFlag; private: // Assignment makes no sense in a readonly class. // Declaring this operator private makes it unusable. TableMeasColumn& operator= (const TableMeasColumn& that); }; // For backwards compatibility: #define ROTableMeasColumn TableMeasColumn } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/TableMeasures/TableMeasDesc.h000066400000000000000000000266251476623553700225240ustar00rootroot00000000000000//# TableMeasDesc.h: Definition of a Measure in a Table. //# Copyright (C) 1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_TABLEMEASDESC_H #define MEASURES_TABLEMEASDESC_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class String; class Table; class TableMeasRefDesc; class TableMeasValueDesc; // // Definition of a Measure column in a Table. // // // // // //# Classes you should understand before using this one. //
      • Measures //
      • Tables // // // The TableMeasures system was created to add support for Measure // columns to the Casacore Table system. // Measures are not a fundamental type of the Tables system and hence // cannot be represented directly. Instead a Measure column can be created // with the aid of // the TableMeasDesc class hierarchy. The TableMeasDesc class hierarchy // creates a Measure column by associating some number of fundamental data // type Table // columns into a unit. The associations between these columns // is represented in the column keywords of each of // the columns which make up a specific Measure column. // // Creating and using Measure columns // is a three step process: //
          //
        1. For each Measure column some number of columns are defined and added // to the Table descriptor. //
        2. A TableMeasDesc object is used to define a (empty) Measure column // from the columns created in the first step. //
        3. (RO)ScalarMeasColumns or // (RO)ArrayMeasColumns objects // are used to access the Measure column for the reading and writing // of Measures. //
        // // Defining a Measure column (that is, steps 1 and 2 above) is the more complex // operation. However, for each Measure column it is a once only operation. // After a Measure column has been created its subsequent use is not // much different to using "ordinary" Table columns. For information // on how to use a Measure column see the // (RO)ScalarMeasColumns and // (RO)ArrayMeasColumns classes. //

        // The TableMeasDesc class hierarchy contains classes for defining each // component of the Measures to be contained in column. A // TableMeasOffsetDesc is used // to specify the offset component, a // TableMeasRefDesc to set up // the reference code component and a // TableMeasValueDesc names the // column used as the main Measure column through which the // Measure column is subsequently accessed. //
        // The final step needed to create a Measure column is the creation of a // TableMeasDesc object whose // constructor takes a TableMeasValueDesc and (optionally) a // TableMeasRefDesc. After construction the TableMeasDesc object's // write() member is used to make the // the Measure column persistent within the Table. //

        // The following examples demonstrate the creation of Measure columns using // the above components. Further details about each of these components // is available with each class description. //
        // All examples write the measure description into a TableDesc object, // i.e. the argument used in the TableMeasDesc::write function is a // TableDesc object. It is, however, also possible to write them // into a Table object which is useful if measure columns are added // to an already existing table (see example 2). // // //

          //
        1. The following creates a MEpoch column with a fixed reference. // // // Need a table to work with. // TableDesc td("measureTable_desc", "1", TableDesc::New); // td.comment() = "A test of TableMeasures class."; // // // Define a column and add it to the table // // The main measure column is always an Array column of type Double // ArrayColumnDesc cdTime("Time", "An MEpoch column"); // td.addColumn(cdtime); // // // Create the Measure column for an MEpoch. The MEpoch in // // the column has reference code MEpoch::TAI // TableMeasRefDesc measRef(MEpoch::TAI); // TableMeasValueDesc measVal(td, "Time"); // TableMeasDesc mepochCol(measVal, measRef); // // write makes the Measure column persistent. // mepochCol.write(td); // // // create the table with 5 rows // SetupNewTable newtab("MeasuresTable", td, Table::New); // Table tab(newtab, 5); // //
        2. Same as example above, but for an already existing table. // // // Need a table to work with. // TableDesc td("measureTable_desc", "1", TableDesc::New); // td.comment() = "A test of TableMeasures class."; // // // Define a column and add it to the table // // The main measure column is always an Array column of type Double // ArrayColumnDesc cdTime("Time", "An MEpoch column"); // td.addColumn(cdtime); // // // create the table with 5 rows // SetupNewTable newtab("MeasuresTable", td, Table::New); // Table tab(newtab, 5); // // // Create the Measure column for an MEpoch. The MEpoch in // // the column has reference code MEpoch::TAI // TableMeasRefDesc measRef(MEpoch::TAI); // TableMeasValueDesc measVal(tab.tableDesc(), "Time"); // TableMeasDesc mepochCol(measVal, measRef); // // write makes the Measure column persistent. // mepochCol.write(tab); // //
        3. An MEpoch column with a variable reference code with a fixed offset: // // // The following three columns will be used to set up a Scalar MEpoch // // column with variable references and offsets. 3 columns are needed. // // The "main" column where the MEpoch will be stored // ArrayColumnDesc cdTime("Time", "An MEpoch column"); // // Variable (i.e., per row) reference code storage needs a column. // // The column type is either Int or String (Int is faster but String // // may be useful when browsing the table). Either a Scalar column or // // Array column can be used here dependent on whether a Scalar or // // Array Measure column is used and whether in case of an Array Measure // // column the reference code has to be variable per array element. // ScalarColumnDesc cdRef("TimeRef", "Reference column for Time"); // // // add the columns to the Table decriptor // td.addColumn(cdTime); // td.addColumn(cdRef); // // // now create the MEpoch column. // // want a fixed offset. Offsets are Measures // MEpoch offset(MVEpoch(MVTime(1996, 5, 17), MEpoch::UTC); // TableMeasOffsetDesc offsetDesc(offset); // // the reference // TableMeasRefDesc measRef(td, "TimeRef", offsetDesc); // // the value descriptor, create and write the column // TableMeasValueDesc measVal(td, "Time"); // TableMeasDesc mepochCol(measVal, measRef); // mepochCol.write(); // // // create the table, etc // ... // // //
        4. An MEpoch column with a variable reference code and offset // // // Variable (per row storage of) offsets needs its own column. Measure // // offsets are Measures therefore a Measure column is needed. // ArrayColumnDesc cdOffset("OffsetCol", "Variable Offset col"); // // // A column for the variable reference code // ScalarColumnDesc cdRef("RefCol", "Variable reference column"); // // // The main (value) column for the Measure column // ArrayColumnDesc cdTime("Time", "MEpoch column"); // // // add the column descriptors to the table // td.addColumn(cdOffset); // td.addColumn(cdRef); // td.addColumn(cdTime); // // // Create the Measure column // // // The offset column is itself a Measure column, but write() is not // // called // TableMeasValueDesc offsetVal(td, "OffsetCol"); // TableMeasDesc offset(offsetVal); // TableMeasOffsetDesc offsetDesc(offset); // // // the reference // TableMeasRefDesc ref(td, "RefCol", offsetDesc); // // // create the Measure column // TableMeasValueDesc val(td, "Time"); // TableMeasDesc mepochCol(val, ref); // mepochCol.write(); // // create the table, etc // ... // //
        // // // Creating the required keyword for the definition of a Measure // in a Table is somewhat complicated. This class assists in that // process. // // // //
      • AipsError if a reference code string is invalid. // // //# //# A List of bugs, limitations, extensions or planned refinements. //# template class TableMeasDesc : public TableMeasDescBase { public: // Constructor with measure value descriptor. The Measure reference for // the column will be the default reference code for M. Units for the // column will be the default for the Measure type. TableMeasDesc (const TableMeasValueDesc&); // Constructor with measure value descriptor and Vector of Units. // The Measure reference for the column will be the default reference // code for the Measure type. Number of Units must be compatible // with the Measure. TableMeasDesc (const TableMeasValueDesc&, const Vector&); // Constructor with value and reference descriptors. Units for the // column will be the default for Measure type. TableMeasDesc (const TableMeasValueDesc&, const TableMeasRefDesc&); // Constructor with value and reference descriptors and Vector of // Units. Number of Units must be compatible with the Measure. TableMeasDesc (const TableMeasValueDesc&, const TableMeasRefDesc&, const Vector&); // Clone the object. virtual TableMeasDescBase* clone() const; // Copy constructor (copy semantics). TableMeasDesc (const TableMeasDesc& that); ~TableMeasDesc(); // Assignment operator (copy semantics) TableMeasDesc& operator= (const TableMeasDesc& that); }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/measures/TableMeasures/TableMeasDesc.tcc000066400000000000000000000070051476623553700230350ustar00rootroot00000000000000//# TableMeasDef.cc: Definition of a Measure in a Table. //# Copyright (C) 1997,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_TABLEMEASDESC_TCC #define MEASURES_TABLEMEASDESC_TCC //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template TableMeasDesc::TableMeasDesc (const TableMeasValueDesc& value) : TableMeasDescBase(value, TableMeasRefDesc(M::DEFAULT)) { M meas; Vector > val; val = meas.getValue().getTMRecordValue(); Vector u; setMeasUnits (meas, val, u); } template TableMeasDesc::TableMeasDesc (const TableMeasValueDesc& value, const Vector& u) : TableMeasDescBase(value, TableMeasRefDesc(M::DEFAULT)) { M meas; Vector > val; val = meas.getValue().getTMRecordValue(); setMeasUnits (meas, val, u); } template TableMeasDesc::TableMeasDesc (const TableMeasValueDesc& value, const TableMeasRefDesc& ref) : TableMeasDescBase(value, ref) { // Set the units of this measure. M meas; Vector > val; val = meas.getValue().getTMRecordValue(); Vector u; setMeasUnits (meas, val, u); // If the reference codes are variable, set the types. if (ref.isRefCodeColumnInt()) { initTabRef (MeasureHolder(meas)); } } template TableMeasDesc::TableMeasDesc (const TableMeasValueDesc& value, const TableMeasRefDesc& ref, const Vector& u) : TableMeasDescBase(value, ref) { M meas; Vector > val; val = meas.getValue().getTMRecordValue(); setMeasUnits (meas, val, u); if (ref.isRefCodeColumnInt()) { initTabRef (MeasureHolder(meas)); } } template TableMeasDescBase* TableMeasDesc::clone() const { return new TableMeasDesc(*this); } template TableMeasDesc::TableMeasDesc (const TableMeasDesc& that) : TableMeasDescBase(that) {} template TableMeasDesc::~TableMeasDesc() {} template TableMeasDesc& TableMeasDesc::operator= (const TableMeasDesc& that) { TableMeasDescBase::operator= (that); return *this; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/TableMeasures/TableMeasDescBase.cc000066400000000000000000000157321476623553700234520ustar00rootroot00000000000000//# TableMeasDefBase.cc: Definition of a Measure in a Table. //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableMeasDescBase::TableMeasDescBase() {} TableMeasDescBase::TableMeasDescBase (const TableMeasValueDesc& value, const TableMeasRefDesc& ref) : itsValue (value), itsRef (ref) {} TableMeasDescBase::TableMeasDescBase (const TableMeasDescBase& that) : itsValue (that.itsValue), itsRef (that.itsRef), itsMeasType (that.itsMeasType), itsUnits (that.itsUnits) {} TableMeasDescBase::~TableMeasDescBase() {} TableMeasDescBase* TableMeasDescBase::clone() const { return new TableMeasDescBase(*this); } TableMeasDescBase* TableMeasDescBase::reconstruct (const Table& tab, const String& columnName) { Int fnr; TableRecord mtype; TableRecord measInfo; const TableRecord& columnKeyset = tab.tableDesc()[columnName].keywordSet(); // get the Measure type fnr = columnKeyset.fieldNumber("MEASINFO"); if (fnr >= 0) { measInfo = columnKeyset.asRecord(fnr); // Older TableMeasures has a separate Type record for itsMeasType. // Newer ones simply have the type field in the MEASINFO. if (measInfo.isDefined("Type")) { mtype = measInfo.asRecord("Type"); } else { mtype = measInfo; } } else { throw(AipsError("TableMeasDescBase::reconstruct; MEASINFO record not " "found for column " + columnName)); } // get the units TableQuantumDesc* tqdesc = TableQuantumDesc::reconstruct (tab.tableDesc(), columnName); Vector names(tqdesc->getUnits()); Vector units(names.nelements()); for (uInt i=0; iitsValue = TableMeasValueDesc (tab.tableDesc(), columnName); p->itsMeasType = TableMeasType(measHolder.asMeasure()); p->itsUnits = units; p->itsRef = TableMeasRefDesc (measInfo, tab, measHolder, *p); return p; } TableMeasDescBase& TableMeasDescBase::operator= (const TableMeasDescBase& that) { if (this != &that) { itsValue = that.itsValue; itsRef = that.itsRef; itsMeasType = that.itsMeasType; itsUnits = that.itsUnits; } return *this; } void TableMeasDescBase::write (TableDesc& td) { TableRecord measInfo; // Create a record from the MeasType and add it to measInfo itsMeasType.toRecord (measInfo); // Put the units. // Use TableQuantumDesc, so the column can be used that way too. TableQuantumDesc tqdesc(td, itsValue.columnName(), itsUnits); tqdesc.write (td); // Write the reference. itsRef.write (td, measInfo, *this); // Write the MEASINFO record into the keywords of the value column. itsValue.write (td, measInfo); } void TableMeasDescBase::write (Table& tab) { TableRecord measInfo; // Create a record from the MeasType and add it to measInfo itsMeasType.toRecord (measInfo); // Put the units. // Use TableQuantumDesc, so the column can be used that way too. TableQuantumDesc tqdesc(tab.tableDesc(), itsValue.columnName(), itsUnits); tqdesc.write (tab); // Write the reference. itsRef.write (tab, measInfo, *this); // Write the MEASINFO record into the keywords of the value column. itsValue.write (tab, measInfo); } void TableMeasDescBase::writeIfOld (const Table& tab) { if (! itsRef.hasRefTab()) { write (const_cast(tab)); } } void TableMeasDescBase::initTabRef (const MeasureHolder& measHolder) { itsRef.initTabRef (measHolder); } void TableMeasDescBase::setMeasUnits (const Measure& meas, const Vector >& val, const Vector& units) { itsMeasType = TableMeasType(meas); // The input unit vector cannot be longer. if (units.nelements() > val.nelements()) { throw (AipsError ("TableMeasDescBase::setMeasUnits; Unit vector" " for column " + columnName() + " is too long")); } // An empty or non-given unit gets the default Quantum one. itsUnits.resize (val.nelements()); for (uInt i=0; i= units.nelements() || units(i).empty()) { itsUnits(i) = val(i).getUnit(); } else { if (! (units(i) == val(i).getUnit())) { throw (AipsError ("TableMeasDescBase::setMeasUnits; invalid unit " + units(i).getName() + " for column " + columnName())); } itsUnits(i) = units(i); } } } void TableMeasDescBase::resetUnits (const Vector& units) { if (units.nelements() > itsUnits.nelements()) { throw (AipsError ("TableMeasDescBase::resetUnits: Unit vector" " for column " + columnName() + " is too long")); } // An empty or non-given unit does not change. for (uInt i=0; i #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class String; class Table; class TableDesc; class TableRecord; class TableColumn; class Measure; template class Quantum; // // Definition of a Measure in a Table. // // // // // //# Classes you should understand before using this one. //
      • Measures //
      • Tables //
      • TableMeasDesc // // // Abstract base class for TableMeasDesc. // // // See class TableMeasDesc. // // // Creating the required keyword for the definition of a Measure // in a Table is somewhat complicated. This class assists in that // process. // // // //
      • AipsError during reconstruction if the column doesn't contain // a MEASINFO record. //
      • AipsError during reconstruction if the column has a MEASINFO // but it Measure type is invalid. // // //# //# A List of bugs, limitations, extensions or planned refinements. //# class TableMeasDescBase { public: // Null constructor. TableMeasDescBase(); // Constructor with value and reference descriptors. // Note that setMeasType is always called by the derived class. TableMeasDescBase (const TableMeasValueDesc&, const TableMeasRefDesc&); // Copy constructor. TableMeasDescBase (const TableMeasDescBase& that); virtual ~TableMeasDescBase(); // Clone the object. virtual TableMeasDescBase* clone() const; // Assignment operator. TableMeasDescBase& operator= (const TableMeasDescBase& that); // Makes the descriptor persistent. // void write (TableDesc&); void write (Table&); // // Make the descriptor persistent if there was no refcode vector. // This is only needed for old tables without such vectors. void writeIfOld (const Table&); // Get the name of the underlying column. const String& columnName() const { return itsValue.columnName(); } // Return the reference code. uInt getRefCode() const { return itsRef.getRefCode(); } // Returns True if the reference varies per row. Bool isRefCodeVariable() const { return itsRef.isRefCodeVariable(); } // Returns the name of the ref code column when the ref code is variable. // The null string is returned if the ref code is not variable. const String& refColumnName() const { return itsRef.columnName(); } // Returns a reference to its measure reference descriptor. const TableMeasRefDesc& getRefDesc() const { return itsRef; } // Get the name of the offset column. Empty string is returned if no // offset. const String& offsetColumnName() const { return itsRef.offsetColumnName(); } // Returns True if an offset has been defined. Bool hasOffset() const { return itsRef.hasOffset(); } // Returns True if the offset is variable. Bool isOffsetVariable() const { return itsRef.isOffsetVariable(); } // Returns True if the offset is variable and is stored in an // ArrayMeasColumn, i.e., offsets are stored per element. Bool isOffsetArray() const { return itsRef.isOffsetArray(); } // Returns a reference to the offset. const Measure& getOffset() const { return itsRef.getOffset(); } // Returns the descriptors measure type as a String. const String& type() const { return itsMeasType.type(); } // Returns the reference code for this object given a string. Throws // an exception if the refString is invalid for this object. uInt refCode (const String& refString) const { return itsMeasType.refCode(refString); } // Translates the refCode for the descriptors measure type. const String& refType (uInt refCode) const { return itsMeasType.refType(refCode); } // Return the Units of the Measure values const Vector& getUnits() const { return itsUnits; } // Reset the refCode, offset, or units. // It overwrites the value used when defining the TableMeasDesc. // It is only possible if it was defined as fixed for the entire column. // void resetRefCode (uInt refCode) { itsRef.resetRefCode (refCode); } void resetOffset (const Measure& offset) { itsRef.resetOffset (offset); } void resetUnits (const Vector& units); // // Reconstructs the object for the given table and column name. static TableMeasDescBase* reconstruct (const Table& tab, const String& columnName); // Does this column contain table measures? static Bool hasMeasures (const TableColumn& column); protected: // Set the initial reference codes and types in the table. void initTabRef (const MeasureHolder& measHolder); // Set the measure and possible units. void setMeasUnits (const Measure& meas, const Vector >& val, const Vector& units); private: TableMeasValueDesc itsValue; //# The measure value column. TableMeasRefDesc itsRef; //# The reference. //# this gives access to the columns Measure type etc TableMeasType itsMeasType; Vector itsUnits; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/TableMeasures/TableMeasOffsetDesc.cc000066400000000000000000000120401476623553700240130ustar00rootroot00000000000000//# TableMeasOffsetDesc.cc: Definition of a offset measure in a Table. //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableMeasOffsetDesc::TableMeasOffsetDesc (const TableMeasDescBase& column, Bool asArray) : itsTMDesc(column.clone()), itsVarPerArr(asArray) {} TableMeasOffsetDesc::TableMeasOffsetDesc (const Measure& measure) : itsTMDesc(0), itsMeasure(measure), itsVarPerArr(False) {} TableMeasOffsetDesc::TableMeasOffsetDesc (const TableMeasOffsetDesc& that) : itsTMDesc(0) { *this = that; } TableMeasOffsetDesc::~TableMeasOffsetDesc() { delete itsTMDesc; } TableMeasOffsetDesc* TableMeasOffsetDesc::reconstruct( const TableRecord& measInfo, const String& prefix, const Table& tab) { TableMeasOffsetDesc* p = 0; if ((measInfo.fieldNumber(prefix + "Msr") >= 0) || (measInfo.fieldNumber(prefix + "Col") >= 0)) { p = new TableMeasOffsetDesc(measInfo, prefix, tab); } return p; } TableMeasOffsetDesc::TableMeasOffsetDesc (const TableRecord& measInfo, const String& prefix, const Table& tab) : itsTMDesc(0) { Int fnr; fnr = measInfo.fieldNumber(prefix + "Msr"); if (fnr >= 0) { // this is a non-variable offset. The offset is fully defined in the // column keywords. String error; const TableRecord& measRec = measInfo.subRecord(fnr); if (! itsMeasure.fromRecord (error, measRec)) { throw(AipsError("TableMeasOffsetDesc::TableMeasOffsetDesc() " + error)); } } // offset stored in a a measure column fnr = measInfo.fieldNumber(prefix + "Col"); if (fnr >= 0) { itsVarColName = measInfo.asString(fnr); itsTMDesc = TableMeasDescBase::reconstruct(tab, itsVarColName); } // offset stored per array element fnr = measInfo.fieldNumber(prefix + "varPerArr"); if (fnr >= 0) { itsVarPerArr = measInfo.asBool(fnr); } } TableMeasOffsetDesc& TableMeasOffsetDesc::operator= (const TableMeasOffsetDesc& that) { if (this != &that) { delete itsTMDesc; itsTMDesc = that.itsTMDesc; itsMeasure = that.itsMeasure; itsVarColName = that.itsVarColName; itsVarPerArr = that.itsVarPerArr; if (itsTMDesc != 0) { itsTMDesc = itsTMDesc->clone(); } } return *this; } const Measure& TableMeasOffsetDesc::getOffset() const { if (isVariable()) { throw (AipsError ("TableMeasOffsetDesc::getOffset() " "attempt to reference undefined measure offset.")); } return itsMeasure.asMeasure(); } void TableMeasOffsetDesc::write (TableDesc& td, TableRecord& measInfo, const String& prefix) { writeKeys (measInfo, prefix); if (itsTMDesc != 0) { itsTMDesc->write (td); } } void TableMeasOffsetDesc::write (Table& tab, TableRecord& measInfo, const String& prefix) { writeKeys (measInfo, prefix); if (itsTMDesc != 0) { itsTMDesc->write (tab); } } void TableMeasOffsetDesc::writeKeys (TableRecord& measInfo, const String& prefix) { if (! itsMeasure.isEmpty()) { String error; TableRecord measRec; itsMeasure.toRecord (error, measRec); measInfo.defineRecord (prefix + "Msr", measRec); } if (itsTMDesc != 0) { measInfo.define (prefix + "Col", itsTMDesc->columnName()); measInfo.define (prefix + "varPerArr", itsVarPerArr); } } void TableMeasOffsetDesc::resetOffset (const Measure& offset) { if (isVariable()) { throw (AipsError ("tableMeasOffsetDesc::resetOffset cannot be done;" "the offset is not fixed for the entire column")); } itsMeasure = MeasureHolder(offset); } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/TableMeasures/TableMeasOffsetDesc.h000066400000000000000000000166451476623553700236740ustar00rootroot00000000000000//# TableMeasOffseDesc.h: Definition of an Offset measure in a Table. //# Copyright (C) 1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_TABLEMEASOFFSETDESC_H #define MEASURES_TABLEMEASOFFSETDESC_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableMeasDescBase; class Measure; class Table; class TableDesc; class TableRecord; class String; // // Definition of a Measure Offset in a Table. // // // // // //# Classes you should understand before using this one. //
      • Measures //
      • Tables //
      • TableMeasDesc // // // This class assists in the definition of the offset component of a // TableMeasDesc // in the TableMeasures system. Four possibilities exist for specifying the // handling of measure offsets in a Measure column. These are: // //
          //
        • an offset is not used //
        • all measures in the column have the same offset //
        • a unique (and probably different) offset is stored for each row //
        • a unique offset is stored in each array element per (Array)column // row //
        // // Note that this last option is only relevant when using ArrayMeasColumns. // // Examples of each of these follow. //
        // //
          //
        1. Specifying a single fixed offset. Note that a Measure offset is itself // a measure // // // create an MEpoch to use as the offset in an MEpoch column // MEpoch offset(MVEpoch(MVTime(1996, 5, 17, (8+18./60.)/24.)), MEpoch::UTC); // TableMeasOffsetDesc offsetDesc(offset); // // //
        2. Storing an offset per row needs an offset column. Measure offsets are // Measures so a Measure column is needed: // // // Need a column for the offsets. This is to be a Measure column, // // so the rules for creating a Measure column apply. // ArrayColumnDesc cdOffset("OffsetCol", "Variable Offset col"); // ... // // add the column to the table // td.addColumn(cdOffset); // ... // // Create the Measure column to be used as the measure offset column // TableMeasValueDesc valDesc(td, "OffsetCol"); // TableMeasDesc offset(valDesc); // // Create the offset descriptor // TableMeasOffsetDesc offsetDesc(offset); // // // //
        3. Storing an offset per array element per row requires one change in the // constructor used in the previous example: // // ... // // set up column and TableMeasDesc as before // ... // // Setting the asArray parameter to True in the constructor specifies // // per element offset storage // TableMeasOffsetDesc offsetDesc(offset, True); // //
        // // For an example of the use of the TableMeasOffsetDesc class in the context // of a full TableMeasDesc declaration see class // TableMeasDesc. //
        // // Creating the required keyword for the definition of a Measure // in a Table is somewhat complicated. This class assists in that // process. // // // //
      • AipsError during reconstruction of non-variable offset if a // component of the offset measure is missing in the column keywords or // is corrupt in some way. //
      • AipsError if getOffset() called on a variable offset object. //
      • AipsError during a reconstruct if the column doesn't have a Unit. // // //# //# A List of bugs, limitations, extensions or planned refinements. //# class TableMeasOffsetDesc { public: // Constructor which defines a constant (non-variable) offset. All // measures in the columns will have the same offset. TableMeasOffsetDesc (const Measure& offset); // Constructor for defining a variable offset. If asArray is True then // the offset is stored per array element. The default is for the // offset to be stored (and hence variable) per row. TableMeasOffsetDesc (const TableMeasDescBase& offsetColumn, Bool asArray=False); // Copy constructor (copy semantics). TableMeasOffsetDesc (const TableMeasOffsetDesc& that); ~TableMeasOffsetDesc(); // Assignment operator (copy semantics). TableMeasOffsetDesc& operator= (const TableMeasOffsetDesc& that); // Reconstructs the TableMeasOffsetDesc from the measInfo TableRecord. static TableMeasOffsetDesc* reconstruct (const TableRecord& measInfo, const String& prefix, const Table& tab); // Get the (non-variable) measure offset for this column. If it doesn't // exist (thus if the offset is variable), an exception is thrown. const Measure& getOffset() const; // Returns True if the offset varies per row. Bool isVariable() const { return (itsTMDesc != 0); } // Returns True if the offset varies per array element. Bool isArray() const { return (isVariable() && itsVarPerArr); } // Gets the name of the column which stores the variable offset. // "" is returned if the offset is not variable. const String& columnName() const { return itsVarColName; } // Reset the offset. // It overwrites the value used when defining the TableMeasDesc. // It is only possible if it was defined as fixed for the entire column. void resetOffset (const Measure& offset); // Write the information into the record. // void write (TableDesc&, TableRecord& measInfo, const String& prefix); void write (Table&, TableRecord& measInfo, const String& prefix); // private: TableMeasDescBase* itsTMDesc; //# Stores variable offset if applicable MeasureHolder itsMeasure; //# The offset if non-variable. String itsVarColName; //# "" if offset non-variable. Bool itsVarPerArr; //# Is variable per array element. // Constructor which uses the measInfo TableRecord. TableMeasOffsetDesc (const TableRecord& measInfo, const String& prefix, const Table&); // Write the actual keywords. void writeKeys (TableRecord& measInfo, const String& prefix); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/TableMeasures/TableMeasRefDesc.cc000066400000000000000000000235201476623553700233060ustar00rootroot00000000000000//# TableMeasRefDef.cc: Definition of a MeasRef in a Table. //# Copyright (C) 1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Set the default type getting function. TableMeasRefDesc::TypesFunc* TableMeasRefDesc::theirTypesFunc = TableMeasRefDesc::defaultTypesFunc; TableMeasRefDesc::TableMeasRefDesc (uInt referenceCode) : itsRefCode (referenceCode), itsRefCodeColInt (False), itsHasRefTab (True), itsOffset (0) {} TableMeasRefDesc::TableMeasRefDesc (uInt referenceCode, const TableMeasOffsetDesc& offset) : itsRefCode (referenceCode), itsRefCodeColInt (False), itsHasRefTab (True), itsOffset (new TableMeasOffsetDesc(offset)) {} TableMeasRefDesc::TableMeasRefDesc (const TableDesc &td, const String& column) : itsRefCode (0), itsColumn (column), itsRefCodeColInt (False), itsHasRefTab (True), itsOffset (0) { checkColumn (td); } TableMeasRefDesc::TableMeasRefDesc (const TableDesc &td, const String& column, const TableMeasOffsetDesc& offset) : itsRefCode (0), itsColumn (column), itsRefCodeColInt (False), itsHasRefTab (True), itsOffset (new TableMeasOffsetDesc(offset)) { checkColumn (td); } TableMeasRefDesc::TableMeasRefDesc (const TableMeasRefDesc& that) : itsOffset(0) { operator= (that); } TableMeasRefDesc& TableMeasRefDesc::operator= (const TableMeasRefDesc& that) { if (this != &that) { delete itsOffset; itsRefCode = that.itsRefCode; itsColumn = that.itsColumn; itsRefCodeColInt = that.itsRefCodeColInt; itsHasRefTab = that.itsHasRefTab; itsOffset = that.itsOffset; if (itsOffset != 0) { itsOffset = new TableMeasOffsetDesc(*itsOffset); } itsTabRefTypes = that.itsTabRefTypes; itsTabRefCodes = that.itsTabRefCodes; itsTab2Cur = that.itsTab2Cur; itsCur2Tab = that.itsCur2Tab; } return *this; } TableMeasRefDesc::~TableMeasRefDesc() { delete itsOffset; } TableMeasRefDesc::TableMeasRefDesc (const TableRecord& measInfo, const Table& tab, const MeasureHolder& measHolder, const TableMeasDescBase& mDesc) : itsRefCode (0), itsRefCodeColInt (False), itsHasRefTab (True), itsOffset (0) { Int fnr; fnr = measInfo.fieldNumber("Ref"); // Read back. The refcode is fixed or variable. if (fnr >= 0) { itsRefCode = mDesc.refCode (measInfo.asString(fnr)); } fnr = measInfo.fieldNumber("VarRefCol"); if (fnr >= 0) { // Variable refcode. itsColumn = measInfo.asString(fnr); // See if the refcodes/types are defined in the table. // If so, read back. Otherwise initialize with default. if (tab.tableDesc().columnDesc(itsColumn).dataType() == TpInt) { itsRefCodeColInt = True; fnr = measInfo.fieldNumber("TabRefTypes"); if (fnr >= 0) { itsTabRefTypes = measInfo.asArrayString ("TabRefTypes"); itsTabRefCodes = measInfo.toArrayuInt ("TabRefCodes"); fillTabRefMap (measHolder); } else { itsHasRefTab = False; initTabRef (measHolder); } } } itsOffset = TableMeasOffsetDesc::reconstruct (measInfo, "RefOff", tab); } void TableMeasRefDesc::defaultTypesFunc (Vector& curTypes, Vector& curCodes, const MeasureHolder& measHolder) { Int nall, nexact; const uInt* codes; const String* types = measHolder.asMeasure().allTypes (nall, nexact, codes); // Remove the duplicates which are at the end of the arrays. Bool found; while (nall > 0) { if (linearSearchBrackets (found, codes, codes[nall-1], nall-1) < 0) { break; } nall--; } IPosition shp(1, nall); curTypes = Vector (shp, types); curCodes = Vector (shp, codes); } void TableMeasRefDesc::initTabRef (const MeasureHolder& measHolder) { itsTabRefTypes.resize (0); itsTabRefCodes.resize (0); theirTypesFunc (itsTabRefTypes, itsTabRefCodes, measHolder); initTabRefMap(); } void TableMeasRefDesc::initTabRefMap() { uInt maxcod = max(itsTabRefCodes); itsTab2Cur.resize (maxcod+1); itsTab2Cur = -1; for (uInt i=0; i curtyp; Vector curcod; theirTypesFunc (curtyp, curcod, measHolder); if (curtyp.nelements() == itsTabRefTypes.nelements() && allEQ (curtyp, itsTabRefTypes) && allEQ (curcod, itsTabRefCodes)) { initTabRefMap(); } else { uInt maxtab = max(itsTabRefCodes); uInt maxcur = max(curcod); itsCur2Tab.resize (maxcur+1); // First map current codes to table codes; this may add table code entries. maxtab = fillMap (itsCur2Tab, curcod, curtyp, itsTabRefCodes, itsTabRefTypes, maxtab); itsTab2Cur.resize (maxtab+1); fillMap (itsTab2Cur, itsTabRefCodes, itsTabRefTypes, curcod, curtyp, -1); } } uInt TableMeasRefDesc::fillMap (Block& f2t, const Vector& codesf, const Vector& typesf, Vector& codest, Vector& typest, Int maxnr) { f2t = -1; uInt nt = typest.nelements(); for (uInt i=0; i= 0) { f2t[codesf[i]] = codest[inx]; } else { if (maxnr < 0) { LogIO os; os << LogIO::WARN << "TableMeasRefDesc warning: refcode " << typesf[i] << " does not exist in this Casacore version" << LogIO::POST; f2t[codesf[i]] = -1; } else { codest.resize (nt+1, True); typest.resize (nt+1, True); maxnr++; codest[nt] = maxnr; typest[nt] = typesf[i]; f2t[codesf[i]] = codest[nt]; nt++; } } } return maxnr; } uInt TableMeasRefDesc::tab2cur (uInt tabRefCode) const { AlwaysAssert (tabRefCode < itsTab2Cur.nelements() && itsTab2Cur[tabRefCode] >= 0, AipsError); return itsTab2Cur[tabRefCode]; } uInt TableMeasRefDesc::cur2tab (uInt curRefCode) const { AlwaysAssert (curRefCode < itsCur2Tab.nelements() && itsCur2Tab[curRefCode] >= 0, AipsError); return itsCur2Tab[curRefCode]; } void TableMeasRefDesc::write (TableDesc& td, TableRecord& measInfo, const TableMeasDescBase& measDesc) { writeKeys (measInfo, measDesc); if (itsOffset != 0) { itsOffset->write (td, measInfo, "RefOff"); } } void TableMeasRefDesc::write (Table& tab, TableRecord& measInfo, const TableMeasDescBase& measDesc) { writeKeys (measInfo, measDesc); if (itsOffset != 0) { itsOffset->write (tab, measInfo, "RefOff"); } } void TableMeasRefDesc::writeKeys (TableRecord& measInfo, const TableMeasDescBase& measDesc) { if (isRefCodeVariable()) { measInfo.define ("VarRefCol", itsColumn); if (itsRefCodeColInt) { measInfo.define ("TabRefTypes", itsTabRefTypes); measInfo.define ("TabRefCodes", itsTabRefCodes); } } else { measInfo.define ("Ref", measDesc.refType (itsRefCode)); } } void TableMeasRefDesc::checkColumn (const TableDesc& td) { if (! td.isColumn(itsColumn)) { throw (AipsError ("TableMeasRefDesc::checkColumn; No such column: " + itsColumn)); } else { if (td.columnDesc(itsColumn).dataType() != TpString) { if (td.columnDesc(itsColumn).dataType() != TpInt) { throw AipsError ("TableMeasRefDesc::checkColumn; Reference column's " "type must be Int or String: " + itsColumn); } itsRefCodeColInt = True; } } } void TableMeasRefDesc::resetRefCode (uInt refCode) { if (isRefCodeVariable()) { throw (AipsError ("tableMeasRefDesc::resetRefCode cannot be done;" "the refcode is not fixed for the entire column")); } itsRefCode = refCode; } void TableMeasRefDesc::resetOffset (const Measure& offset) { if (itsOffset == 0) { itsOffset = new TableMeasOffsetDesc (offset); } else { itsOffset->resetOffset (offset); } if (isOffsetVariable()) { throw (AipsError ("tableMeasRefDesc::resetOffset cannot be done;" "the offset is not fixed for the entire column")); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/TableMeasures/TableMeasRefDesc.h000066400000000000000000000302421476623553700231470ustar00rootroot00000000000000//# TableMeasRefDesc.h: Definition of a Measure Reference in a Table. //# Copyright (C) 1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_TABLEMEASREFDESC_H #define MEASURES_TABLEMEASREFDESC_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableMeasDescBase; class Table; class TableDesc; class TableRecord; // // Definition of a Measure Reference in a Table. // // // // // //# Classes you should understand before using this one. //
      • Measures //
      • Tables //
      • TableMeasDesc // // // TableMeasRefDesc is a class for setting up the MeasRef // component of a TableMeasDesc in the TableMeasures system. With the aid // of a // TableMeasRefDesc the following possibilities for defining a Measure // column's reference exist: //
          //
        • a fixed, non-variable, reference code, where all Measures in a // column are to have the same reference code. //
        • a unique (and probably different) reference code stored in each row. //
        • a unique reference code stored in each array element per // (Array)column row. //
        // For each of the above options an offset component can be specified // along with a reference code. When a Measure offset is required a // TableMeasOffsetDesc is // supplied as an argument to the TableMeasRefDesc constructor. // With references containing an offset component either component can be set // to be variable or fixed independently of the other. // // // It is not necessary to specify a Reference when defining a // Measure column. In such cases the Measures retrieved from the column // will have the default reference for the type of Measure stored in the // column. // // // A fixed reference code is trivially stored as part of the column // keywords in the Measure column but a variable reference code requires // its own column. A Scalar or Array column can be used dependent on your // needs but its type must always be either Int or String. Note that it is // legal to specify a Scalar // reference column for use with an ArrayMeasColumn. In such cases a single // reference code will be stored per array (row) of Measures. However, // attempting to associate an Array column for references with a // ScalarMeasColumn will generate an exception. // // Because the reference codes stored are the enums defined in the Measures // classes, it is possible that they change over time. The type strings, // however, wille never change. Therefore the reference codes and types // valid at the time of the table creation, are stored in the column keywords // if the reference codes are kept in an integer column. //
        // This has only been added in March 2007, but is fully backward compatible. // Older tables will get the codes and types stored when accessed for // read/write. //
        // // // When storing Measures into a Measure column with a fixed reference code // the reference code component of the Measures stored is // ignored. // //
        // //
          //
        1. Simplest kind of TableMeasRefDesc (apart from not specifying one at // all) is a fixed reference code. All Measures subsequently // retrieved from the column will have the reference MEpoch::LAST. // // // measure reference column // TableMeasRefDesc reference(MEpoch::LAST); // //
        2. A variable reference code requires its own Int column. // // // An int column for the variable references. // ScalarColumnDesc cdRefCol("refCol", "Measure reference column"); // td.addColumn(cdRefCol); // ... // // create the Measure reference descriptor // TableMeasRefDesc varRef(td, "refCol"); // //
        3. A fix Measure reference code with a fixed Offset // // // Create the Offset descriptor // MEpoch offset(MVEpoch(MVTime(1996, 5, 17, (8+18./60.)/24.)) // TableMeasOffsetDesc offsetDesc(offset); // // create the Measure reference descriptor // TableMeasRefDesc varRef(MEpoch::LAST, offsetDesc); // //
        // For an example of the use of a TableMeasRefDesc in the context of a full // TableMeasDesc declaration see class // TableMeasDesc. //
        // // Creating the required keyword for the definition of a Measure // in a Table is somewhat complicated. This class assists in that // process. // // // //
      • AipsError if the specified column doesn't exist or its type is // not Int or String. // // //# //# A List of bugs, limitations, extensions or planned refinements. //# class TableMeasRefDesc { public: // Define a fixed MeasRef by supplying its reference code // Optionally a Measure offset can be specified. // The reference code and offset should not need a reference frame. // explicit TableMeasRefDesc (uInt refCode = 0); TableMeasRefDesc (uInt refCode, const TableMeasOffsetDesc&); // // Define a variable reference by supplying the name of the column // in which the reference is to be stored. Either an Int or // String column can be specified. This determines how // references are stored. Int columns are likely to be // faster but storing // references as Strings may be useful if there is a need to // browse tables manually. Optionally supply a Measure offset. // The reference code and offset should not need a reference frame. // TableMeasRefDesc (const TableDesc&, const String& column); TableMeasRefDesc (const TableDesc&, const String& column, const TableMeasOffsetDesc&); // // Reconstruct the object from the MEASINFO record. // Not useful for the public. TableMeasRefDesc (const TableRecord& measInfo, const Table&, const MeasureHolder& measHolder, const TableMeasDescBase&); // Copy constructor (copy semantics) TableMeasRefDesc (const TableMeasRefDesc& that); ~TableMeasRefDesc(); // Assignment operator (copy semantics). TableMeasRefDesc& operator= (const TableMeasRefDesc& that); // Return the reference code. uInt getRefCode() const { return itsRefCode; } // Is the reference variable? Bool isRefCodeVariable() const { return (! itsColumn.empty()); } // Return the name of its variable reference code column. const String& columnName() const { return itsColumn; } // Is the reference code variable and stored in an integer column? Bool isRefCodeColumnInt() const { return itsRefCodeColInt; } // Do the keywords contain the reference codes and types. // For old tables this might not be the case. Bool hasRefTab() const { return itsHasRefTab; } // Returns True if the reference has an offset. Bool hasOffset() const { return (itsOffset != 0); } // Returns True if the offset is variable. Bool isOffsetVariable() const { return (itsOffset != 0 ? itsOffset->isVariable() : False); } // Returns True is the offset is variable and it is an ArrayMeasColumn. Bool isOffsetArray() const { return (itsOffset != 0 ? itsOffset->isArray() : False); } // Return the fixed Measure offset. // It does not test if the offset is defined; hasOffset() should be used // for that purpose. const Measure& getOffset() const { return itsOffset->getOffset(); } // Return the name of the Measure offset column. // An empty string is returned if no variable offset is used. const String& offsetColumnName() const { return itsOffset->columnName(); } // Reset the refCode or offset. // It overwrites the value used when defining the TableMeasDesc. // It is only possible if it was defined as fixed for the entire column. // void resetRefCode (uInt refCode); void resetOffset (const Measure& offset); // // Make the Measure value descriptor persistent. Normally would not be // called by the user directly. // void write (TableDesc&, TableRecord& measInfo, const TableMeasDescBase&); void write (Table&, TableRecord& measInfo, const TableMeasDescBase&); // // Initialize the table reference codes and types and // the maps (mapping a code onto itself). void initTabRef (const MeasureHolder& measHolder); // Reference codes can be persistent in tables. // Because their enum values can change, a mapping of current table // to table value is maintained. The mapping is created using their // never-changing string representations. // These functions convert current refcode to and from table refcode. // uInt tab2cur (uInt tabRefCode) const; uInt cur2tab (uInt curRefCode) const; // // Set the function used to get all reference codes for a MeasureHolder. // This is not really needed for normal practice, but makes it possible // to add extra codes when testing. //
        The default function simply calls MeasureHolder.asMeasure.allTypes. // typedef void TypesFunc (Vector& types, Vector& codes, const MeasureHolder&); static void setTypesFunc (TypesFunc* func) { theirTypesFunc = func; } static void defaultTypesFunc (Vector& types, Vector& codes, const MeasureHolder&); static TypesFunc* theirTypesFunc; // private: uInt itsRefCode; // The name of column containing its variable references. String itsColumn; // Is the reference code column a string column? Bool itsRefCodeColInt; // Do the keywords contain the reference codes and types? Bool itsHasRefTab; //# Its reference offset. TableMeasOffsetDesc* itsOffset; //# Define the vectors holding the measref codes and types. //# These are the codes as used in the table, which might be different //# from the current values. Vector itsTabRefTypes; Vector itsTabRefCodes; //# Define the mappings of table measref codes to current ones and back. //# There are only filled in and used if a variable reference code is used. Block itsTab2Cur; Block itsCur2Tab; // Fill the reference code mappings for table<->current. // void initTabRefMap(); void fillTabRefMap (const MeasureHolder& measHolder); uInt fillMap (Block& f2t, const Vector& codesf, const Vector& typesf, Vector& codest, Vector& typest, Int maxnr); // // Write the actual keywords. void writeKeys (TableRecord& measInfo, const TableMeasDescBase& measDesc); // Throw an exception if the column doesn't exist or is of the // wrong type. void checkColumn (const TableDesc& td); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/TableMeasures/TableMeasType.cc000066400000000000000000000064321476623553700227170ustar00rootroot00000000000000//# TableMeasDef.cc: Definition of a Measure in a Table. //# Copyright (C) 1997,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableMeasType::TableMeasType() : itsNtypes(0), itsStypes(0), itsTyps (0) {} TableMeasType::TableMeasType (const Measure& meas) : itsNtypes(0), itsStypes(0), itsTyps (0), itsMeasHolder(meas) { Int nextras; itsStypes = itsMeasHolder.asMeasure().allTypes (itsNtypes, nextras, itsTyps); } TableMeasType::TableMeasType (const TableMeasType& that) : itsNtypes(that.itsNtypes), itsStypes(that.itsStypes), itsTyps (that.itsTyps), itsMeasHolder(that.itsMeasHolder) {} TableMeasType::~TableMeasType() { // Note that Measure::allTypes returns pointers to internal (static) // data structures. So those data should not be deleted here. } TableMeasType& TableMeasType::operator= (const TableMeasType& that) { if (this != &that) { itsNtypes = that.itsNtypes; itsStypes = that.itsStypes; itsTyps = that.itsTyps; itsMeasHolder = that.itsMeasHolder; } return *this; } const String& TableMeasType::type() const { return itsMeasHolder.asMeasure().tellMe(); } uInt TableMeasType::refCode (const String& refString) const { Int i; for (i=0; i= itsNtypes) { throw (AipsError ("TableMeasDesc::refCode() - refType " + refString + " unknown for measType " + type())); } return itsTyps[i]; } const String& TableMeasType::refType (uInt refCode) const { Int i; for (i=0; i= itsNtypes) { throw (AipsError ("TableMeasDescBase::refType - refCode " + String::toString(refCode) + " unknown for measure" + type())); } return itsStypes[i]; } void TableMeasType::toRecord (RecordInterface& rec) { String error; itsMeasHolder.toType (error, rec); } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/TableMeasures/TableMeasType.h000066400000000000000000000074161476623553700225640ustar00rootroot00000000000000//# TableMeasType.h: Encapsulates the Measures type in the TableMeasDesc. //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_TABLEMEASTYPE_H #define MEASURES_TABLEMEASTYPE_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class String; class Table; class RecordInterface; // // Definition of a Measure column in a Table. // // // // // //# Classes you should understand before using this one. //
      • Measures //
      • Tables // // // This class is a helper class for // TableMeasDescBase // to know the type of measure it is dealing with. //
        It eases the process of converting reference codes to their strings // and vice-versa. It also writes the measure type to a record to assist // in making table measure definitions persistent. //
        // // Create the object for an epoch measure. // TableMeasType mtype (MEpoch()); // // Get the code for the given string. // uInt code = mtype.refCode ("UTC"); // // // Creating the required keyword for the definition of a Measure // in a Table is somewhat complicated. This class assists in that // process. // // //# //# A List of bugs, limitations, extensions or planned refinements. //# class TableMeasType { public: TableMeasType(); // Construct from the given type of measure. explicit TableMeasType (const Measure&); // Copy constructor (copy semantics). TableMeasType (const TableMeasType& that); ~TableMeasType(); // Assignment operator (copy semantics) TableMeasType& operator= (const TableMeasType& that); // Returns the descriptor's measure type as a String. const String& type() const; // Translates the refCode for the descriptors measure type. const String& refType (uInt refCode) const; // Returns the reference code for this object given a string. Throws // an exception if the refString is invalid for this object. uInt refCode (const String& refString) const; // Creates a record from the MeasureHolder. void toRecord (RecordInterface& rec); private: Int itsNtypes; //# number of refcodes/strings const String* itsStypes; //# refcode strings const uInt* itsTyps; //# refcodes MeasureHolder itsMeasHolder; //# Holds the measure }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/TableMeasures/TableMeasValueDesc.cc000066400000000000000000000061451476623553700236520ustar00rootroot00000000000000//# TableMeasValueDesc.cc: Definition of a MeasValue in a Table. //# Copyright (C) 1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableMeasValueDesc::TableMeasValueDesc() {} TableMeasValueDesc::TableMeasValueDesc (const TableDesc& td, const String& columnName) : itsColumn(columnName) { checkColumn(td); } TableMeasValueDesc::TableMeasValueDesc (const TableMeasValueDesc& that) : itsColumn(that.itsColumn) {} TableMeasValueDesc::~TableMeasValueDesc() {} TableMeasValueDesc& TableMeasValueDesc::operator= (const TableMeasValueDesc& that) { if (this != &that) { itsColumn = that.itsColumn; } return *this; } void TableMeasValueDesc::write (TableDesc& td, const TableRecord& measInfo) { checkColumn (td); writeKeys (td.rwColumnDesc(itsColumn).rwKeywordSet(), measInfo); } void TableMeasValueDesc::write (Table& tab, const TableRecord& measInfo) { checkColumn (tab.tableDesc()); TableColumn tabcol(tab, itsColumn); writeKeys (tabcol.rwKeywordSet(), measInfo); } void TableMeasValueDesc::writeKeys (TableRecord& columnKeyset, const TableRecord& measInfo) { if (measInfo.nfields() > 0) { columnKeyset.defineRecord ("MEASINFO", measInfo); } } void TableMeasValueDesc::checkColumn (const TableDesc& td) const { if (! td.isColumn(itsColumn)) { throw (AipsError ("TableMeasValueDesc::checkColumn; No such column: " + itsColumn)); } else if (td.columnDesc(itsColumn).dataType() != TpDouble) { throw (AipsError ("TableMeasValueDesc::checkColumn; Column's type " "must be Double: " + itsColumn)); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/TableMeasures/TableMeasValueDesc.h000066400000000000000000000122311476623553700235050ustar00rootroot00000000000000//# TableMeasValueDesc.h: Definition of a MeasValue in a Table. //# Copyright (C) 1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_TABLEMEASVALUEDESC_H #define MEASURES_TABLEMEASVALUEDESC_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class ColumnDesc; class Table; class TableDesc; class TableRecord; // // Definition of a Measure Value in a Table. // // // // // //# Classes you should understand before using this one. //
      • Measures //
      • Tables //
      • TableMeasDesc // // // TableMeasValueDesc is a class for setting up the Measure value // component of the TableMeasDesc in the TableMeasures system. Its purpose // it to specify the Table column to be used as a Measure column through // which Measures are subsequently written to and read from via // either an ArrayMeasColumn // or ScalarMeasColumn object. // // The column used as the Measure column is always an ArrayColumn // irrespective of whether it is to store scalars or arrays of Measures and // irrespective of the type of Measure. // // //
          //
        1. // // // Add a column to the table. This column is to be used to store // // MPositions. Measure columns are alway ArrayColumn // ArrayColumnDesc cdPosCol("MPosColumn", "MPosition column"); // td.addColumn(cdPosCol); // ... // // create the TableMeasValueDesc object // TableMeasValueDesc valueDesc(td, "MPosColumn"); // //
        // For an example of the use of the TableMeasValueDesc class in the context // of a full TableMeasDesc declaration see class // TableMeasDesc. //
        // // Creating the required keyword for the definition of a Measure // in a Table is somewhat complicated. This class assists in that // process. // // // //
      • AipsError if the specified column doesn't exist or it isn't // an ArrayColumn or its type is not Double. // // //# //# A List of bugs, limitations, extensions or planned refinements. //# class TableMeasValueDesc { public: // Null constructor TableMeasValueDesc(); // Construct the MeasValue column descriptor for the given column. // The column must be a column of type Double and should exist in // the TableDesc. TableMeasValueDesc (const TableDesc&, const String& columnName); // Construct the MeasValue column descriptor for the given column. // Checking if the column exists is done in the write function. // TableMeasValueDesc (const String& columnName) : itsColumn (columnName) {} TableMeasValueDesc (const Char* columnName) : itsColumn (columnName) {} // // Copy constructor. TableMeasValueDesc (const TableMeasValueDesc& that); ~TableMeasValueDesc(); // Assignment operator. TableMeasValueDesc& operator= (const TableMeasValueDesc& that); // Write the type, unit, and MEASINFO record into the column keywords. // It checks if the column exists in the given table description. // void write (TableDesc&, const TableRecord& measInfo); void write (Table&, const TableRecord& measInfo); // // Get the name of the underlying column. const String& columnName() const { return itsColumn; } private: String itsColumn; //# MeasValue column name. // Write the actual keywords. void writeKeys (TableRecord& columnKeyset, const TableRecord& measInfo); // Throw an exception if the quantum column doesn't exist or is of the // wrong type. void checkColumn (const TableDesc& td) const; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/TableMeasures/TableQuantumDesc.cc000066400000000000000000000127731476623553700234260ustar00rootroot00000000000000//# TableQuantumDesc.cc: Definition of a Quantum in a Table. //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableQuantumDesc::TableQuantumDesc (const TableDesc& td, const String& column) : itsColName(column) { checkColumn(td); } TableQuantumDesc::TableQuantumDesc (const TableDesc& td, const String& column, const Unit& u) : itsColName(column), itsUnitsName(1) { checkColumn(td); itsUnitsName(0) = u.getName(); } TableQuantumDesc::TableQuantumDesc (const TableDesc& td, const String& column, const Vector& unitNames) : itsColName(column), itsUnitsName(unitNames) { checkColumn(td); } TableQuantumDesc::TableQuantumDesc (const TableDesc& td, const String& column, const Vector& u) : itsColName(column), itsUnitsName(u.nelements()) { checkColumn(td); for (uInt i=0; i= 0) { String unitColName = columnKeyset.asString(fnr); p = new TableQuantumDesc(td, columnName, unitColName); } else { fnr = columnKeyset.fieldNumber("QuantumUnits"); if (fnr >= 0) { Vector unitNames = columnKeyset.asArrayString(fnr); p = new TableQuantumDesc(td, columnName, unitNames); } else { // A measure such as Doppler does not have units, but uses // a single empty unit for easier processing in other classes. p = new TableQuantumDesc(td, columnName, Vector(1)); } } return p; } void TableQuantumDesc::write (TableDesc& td) { writeKeys (td.rwColumnDesc(itsColName).rwKeywordSet()); } void TableQuantumDesc::write (Table& tab) { TableColumn tabcol (tab, itsColName); writeKeys (tabcol.rwKeywordSet()); } void TableQuantumDesc::writeKeys (TableRecord& columnKeyset) { if (isUnitVariable()) { columnKeyset.define("VariableUnits", itsUnitsColName); } else { columnKeyset.define("QuantumUnits", itsUnitsName); } } void TableQuantumDesc::checkColumn (const TableDesc& td) const { if (! td.isColumn(itsColName)) { throw (AipsError ("TableQuantum::checkColumn; No such column: " + itsColName)); } } void TableQuantumDesc::checkUnitsColumn (const TableDesc& td) const { if (! td.isColumn(itsUnitsColName)) { throw (AipsError ("TableQuantum::checkUnitsColumn; No such column: " + itsUnitsColName)); } else if (td.columnDesc(itsUnitsColName).dataType() != TpString) { throw (AipsError ("TableQuantum::checkUnitsColumn; Type of column " "should be String: " + itsUnitsColName)); } } Bool TableQuantumDesc::hasQuanta (const TableColumn& column) { return ( column.keywordSet().isDefined ("QuantumUnits") || column.keywordSet().isDefined ("VariableUnits")); } } //# NAMESPACE CASACORE - END casacore-3.7.1/measures/TableMeasures/TableQuantumDesc.h000066400000000000000000000331571476623553700232670ustar00rootroot00000000000000//# TableQuantumDesc.h: Defines a Quantum column in a Table. //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MEASURES_TABLEQUANTUMDESC_H #define MEASURES_TABLEQUANTUMDESC_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableDesc; class Table; class TableRecord; class TableColumn; class Unit; // // A class for defining Quantum columns in Tables. // // // // // //# Classes you should understand before using this one. //
      • Table //
      • Quantum // // // A TableQuantumDesc object is used to define a Quantum column in a Table. // The use of this object and the associated Scalar- and ArrayQuantColumn // objects make it possible to store (and retrieve) Quanta in Tables.
        // // TableQuantumDesc objects are analogous to ColumnDesc objects in that they // add information, describing the characteristics of a column, to the Table // Descriptor before the Table is created. However, rather than // replacing the use of a ColumnDesc object, a TableQuantumDesc is // used in conjunction with a ColumnDesc in the definition of // Quantum columns.
        // // // A good understanding of the Table system is essential // before attempting to use this class. // // // Defining a Quantum column requires the following steps: //
          //
        1. Use a normal Scalar- or ArrayColumnDesc to define a column to use for // the Quanta. //
        2. If needed (see // below) define a column // for the Quantum Units. //
        3. Add the columns to the Table Descriptor. //
        4. Declare a TableQuantumDesc to associate the column defined in step 1 // and the Unit column from step 2 and update the Table Descriptor. //
        5. Setup and create the Table. //
        // It is also possible to define a Quantum column after the table is created. // which is useful when columns (to be used for quanta) are added to // an already existing table.
        // // The type of the quantum columns must match the type of the underlying // Quanta that are to be stored in the column. Hence, for a column of // Quantum<Complex> a ScalarColumnDesc<Complex> must be used.
        // // As with standard Table Columns Quanta can be stored in Scalar and Array // columns. This must be specified in advance by using either a // Scalar- or ArrayColumnDesc.
        // // After the Table has be created a Quantum column can be accessed for writing // and reading of Quanta via the // (RO)ScalarQuantColumn<T> // and // (RO)ArrayQuantColumn<T> // objects. // // //

        Quantum Units

        // The treatment of the Unit component of a Quantum in the TableQuantumDesc // class varies depending on your needs. The main consideration // is whether the Quanta to be stored in a specific column are to have the // same Unit or whether their Units could differ. In the simple case, // where the // Quanta have the same unit, a TableQuantumDesc is declared with the // Unit value specified as a parameter. The following defines a Quantum // column with units "deg": // // // ScalarColumnDesc scd("QuantumCol"); // ... // // defines QuantumCol as a Quantum column with fix Units "deg" // TableQuantumDesc tqd(td, "QuantumCol", Unit("deg")); // // // This constructor stores the value for the Unit as a // column keyword. In situations, however, where it is necessary to // store a distinct Unit with each Quantum, it is necessary to define // an additional column for storing the Unit component of each Quantum. // The TableQuantumDesc constructor for this takes the name of // the Unit column as // a parameter. Hence an additional column must be defined for storing the // Units and its type must be string. The following // example shows how to set up a Quantum column with support for Quantum // unit variability: // // // // the quanta values stored here // ScalarColumnDesc scd("QuantumCol"); // // a String column for the Units // ScalarColumnDesc scd("QuantumUnitCol"); // ... // TableQuantumDesc tqd(td, "QuantumCol", "QuantumUnitCol"); // // // One further consideration is that for Array Quantum Columns it is // necessary to // decide on a level of granularity for the Unit storage you would like. // In Array Quantum columns it is possible to store a distinct Unit per row or // per array element per row. This distinction is established when the // Unit column is declared. Defining a ScalarColumn for Units specifies per // row variability, that is, each row in an array column of Quanta will // have the same unit. Alternatively, use of an ArrayColumn for the Unit // column // specifies that every Quantum stored will have its unit stored as well. // In both cases the Unit column's type must be String. The following // defines an Array Quantum Column with per row Unit storage: // // // // for the Quanta values // ArrayColumnDesc scd("ArrayQuantumCol"); // // per row storage of units // ScalarColumnDesc scd("QuantumUnitCol"); // ... // TableQuantumDesc tqd(td, "ArrayQuantumCol", "QuantumUnitCol"); // // // And finally, an array Quantum Column with an Array Unit Column: // // // // for Quanta values // ArrayColumnDesc scd("ArrayQuantumCol"); // // per element storage of Units // ArrayColumnDesc scd("ArrayUnitCol"); // ... // TableQuantumDesc tqd(td, "ArrayQuantumCol", "ArrayUnitCol"); // // // // After constructing an TableQuantumDesc object use of the write() member // updates the Table Descriptor or Table object. // (RO)ScalarQuantColumn<T> // and // (RO)ArrayQuantColumn<T> // are subsequently used to read-only and read/write access the Quantum // Columns. //
        // // // // // create a table descriptor as normal // TableDesc td("measTD", "1", TableDesc::New); // td.comment() = "A table containing measures and quantums"; // // // This example sets up a Quantum column but any valid Quantum // // type can be specified. However, the type of the Quantums to be // // stored must match the type of the underlying table column. // ScalarColumnDesc tcdQCplx("Quant", "A quantum complex column"); // // // For a Quantum array column an ArrayColumnDesc is first defined // ArrayColumnDesc tcdQDoub("QuantArray", "A quantum array col"); // // // The QuantumArray column has variable units. A string is needed // // for these. This could be done in two ways depending on what is // // wanted. Units can vary per element of array per row or // // just per row. In the first instance an ArrayColumn would be // // require. Here we want to vary units only per row. // ScalarColumnDesc tcdUnits("VarQuantUnits", "Quantum units"); // // // Add the columns to the Table Descriptor // td.addColumn(tcdQplx); // td.addColumn(tcdQDoub); // td.addColumn(tcdUnits); // // // Create the TableQuantumDesc with units "deg" and an Array Quantum // // Column with per row Unit granularity // TableQuantumDesc tqdS(td, "Quant", unit("deg")); // TableQuantumDesc tqdA(td, "QuantArray", "VarQuantUnits"); // // // Update the Table Descriptor // tqdA.write(td); // tqdS.write(td); // // // Setup and create the new table as usual. // SetupNewTable newtab("mtab", td, Table::New); // Table qtab(newtab); // // // Now ScalarQuantColumn and ArrayQuantColumn objects could be // // constructed to access the columns... // // Note that writing the Quantum description could also be done // after the table is created. It is meaningless in this case, but // it is useful when columns (to be used for quanta) are added to // an already existing table. // be used as // // // Setup and create the new table as usual. // SetupNewTable newtab("mtab", td, Table::New); // Table qtab(newtab); // // // Update the Table Descriptor // tqdA.write(qtab); // tqdS.write(qtab); // // // // This class assists in the definition of a Quantum Table Column. // // //
      • AipsError during construction if the column doesn't exist. //
      • AipsError during construction if the unit's column doesn't // exist (when variable units). //
      • AipsError during construction if the type of the variable unit's // column is not String. //
      • AipsError during a reconstruct if the column doesn't have a Unit. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class TableQuantumDesc { public: // Constructs a Quantum column descriptor with null units (Unit == ""). // The column should have already been added to the TableDesc. // An exception is thrown if the column doesn't exist. TableQuantumDesc (const TableDesc& td, const String& column); // Constructs a Quantum column descriptor with the specified Quantum unit. // The column should have already been added to the TableDesc. // An exception is thrown if the column doesn't exist. TableQuantumDesc (const TableDesc& td, const String& column, const Unit&); // Constructs a Quantum column descriptor with the specified Quantum units. // The column should have already been added to the TableDesc. // An exception is thrown if the column doesn't exist. // TableQuantumDesc (const TableDesc& td, const String& column, const Vector& unitNames); TableQuantumDesc (const TableDesc& td, const String& column, const Vector&); // // Constructs a Quantum column descriptor with variable units stored in // unitCol. Both the quantum and unit column should exist in the // TableDesc. //# Note that the Char* constructor is needed, otherwise the compiler //# cannot choose between String and Unit. // TableQuantumDesc (const TableDesc& td, const String& column, const String& unitCol); TableQuantumDesc (const TableDesc& td, const String& column, const Char* unitCol); // // Copy constructor (copy semantics). TableQuantumDesc (const TableQuantumDesc& that); ~TableQuantumDesc(); // Reconstructs a previously constructed TableQuantumDesc. static TableQuantumDesc* reconstruct (const TableDesc& td, const String& column); // Assignment. TableQuantumDesc& operator= (const TableQuantumDesc& that); // Returns the Quantum column descriptor's units. A empty vector is // returned if units have not been specified. This could be because the null // unit constructor was used or because the units are variable. const Vector& getUnits() const { return itsUnitsName; } // Returns True if descriptor set for variable units (one per row) Bool isUnitVariable() const { return (! itsUnitsColName.empty()); } // Returns the name of the quantum column. const String& columnName() const { return itsColName; } // Returns the name of the units column (an empty String is returned // if the units are not variable). const String& unitColumnName() const { return itsUnitsColName; } // Makes the TableQuantumDesc persistent (updates the Table Descriptor). // void write (TableDesc&); void write (Table&); // // Does this column contain table quanta? static Bool hasQuanta (const TableColumn& column); private: // Name of column which stores the Quantum's values. String itsColName; // The Quantum's unit as a string. Vector itsUnitsName; // Name of units column if units are variable. String itsUnitsColName; // Write the actual keywords. void writeKeys (TableRecord& columnKeyset); // Throw an exception if the quantum column doesn't exist. void checkColumn (const TableDesc& td) const; // Throw an exception if the variable units column isn't a string column. void checkUnitsColumn (const TableDesc& td) const; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/measures/TableMeasures/test/000077500000000000000000000000001476623553700206635ustar00rootroot00000000000000casacore-3.7.1/measures/TableMeasures/test/CMakeLists.txt000066400000000000000000000005061476623553700234240ustar00rootroot00000000000000set (tests tTableQuantum tTableMeasures dVarRefMdirCol ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_measures) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/measures/TableMeasures/test/dVarRefMdirCol.cc000066400000000000000000000123771476623553700240070ustar00rootroot00000000000000//# tTableMeasures.cc: test program for the TableMeasures class. //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // A demo of how to write variable ref MDirection column (i.e each row has a different // ref) // Filler writers should make use of it if FIELD and POINTING subtable etc if necessary int main() { try { { TableDesc td; ArrayColumnDesc cdMDir("PHASE_CENTER", "Variable Ref MDIR test", IPosition(1,2), ColumnDesc::Direct); ScalarColumnDesc cdMDirRef("PhaseCenter_Ref"); td.addColumn(cdMDir); td.addColumn(cdMDirRef); TableMeasRefDesc tmrd(td, "PhaseCenter_Ref"); TableMeasValueDesc tmvd(td, "PHASE_CENTER"); TableMeasDesc tmdMDirection(tmvd, tmrd); tmdMDirection.write(td); SetupNewTable newtab("dVarRefMdirCol_tmp.tab", td, Table::New); Table tab(newtab, 4); MDirection::ScalarColumn tmpCol(tab, "PHASE_CENTER"); tmpCol.put (0, MDirection(Quantity(0.0,"deg"), Quantity(0.0, "deg"), MDirection::URANUS)); tmpCol.put (1, MDirection(Quantity(150.0,"deg"), Quantity(15.0, "deg"), MDirection::J2000)); tmpCol.put (2, MDirection(Quantity(170.0,"deg"), Quantity(12.0, "deg"), MDirection::B1950)); tmpCol.put (3, MDirection(Quantity(80.0,"deg"), Quantity(10.0, "deg"), MDirection::GALACTIC)); } { //get the values now Table tab("dVarRefMdirCol_tmp.tab"); MDirection::ScalarColumn tmpCol(tab, "PHASE_CENTER"); for(uInt k =0 ; k < 4; ++k){ String Ref = tmpCol(k).getRefString(); MVAngle mvRA=tmpCol(k).getAngle().getValue()(0); MVAngle mvDEC=tmpCol(k).getAngle().getValue()(1); cout << "Row "<< k << " Ref " << Ref << ": " << mvRA(0.0).string(MVAngle::TIME,8) << ", " << mvDEC(0.0).string(MVAngle::ANGLE_CLEAN,8) << endl; } } } catch (std::exception& x) { cout << "An error occurred. The test ended early with the following"; cout << " message:\n"; cout << x.what() << endl; return 1; } return 0; } casacore-3.7.1/measures/TableMeasures/test/tTableMeasures.cc000066400000000000000000001526561476623553700241310ustar00rootroot00000000000000//# tTableMeasures.cc: test program for the TableMeasures class. //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void showKeys (const TableRecord& record, const String& indent) { record.print (cout, -1, indent); } void testMain (Bool doExcep) { // Need a table to work with. TableDesc td("tTableMeasures_tmp", "1", TableDesc::New); td.comment() = "A test of TableMeasures class."; // Each measure column needs at exactly one ArrayColumn for storing // the value component of each measure. Additional columns are required // for storing measure offsets and measure references when these components // vary per row. The value column is always an ArrayColumn irrespective // of whether Scalar or Array measures are to be stored. // A scalar column of MDirection. Static reference so no addional // columns required. ArrayColumnDesc cdMDir("MDirColumn", "Simple mdirection column", IPosition(1,2), ColumnDesc::Direct); // A scalar MEpoch column with a fixed offset and reference. Fixed // references and offsets do not need additional columns as they are // stored as keywords. ScalarColumnDesc cdTOffset("TimeOffset", "MEpoch column with fix reference and offset"); // The following three columns will be used to set up a Scalar MEpoch // column with variable references and offsets. 3 columns are needed. // The "main" column where the MEpoch will be stored ScalarColumnDesc cdTime("Time1", "An MEpoch column"); // For the offsets. Offsets are also measures so this is effectively // another measure column. ScalarColumnDesc cdVarOffset("TimeVarOffset", "Variable Offset col"); // an int column for the variable references ScalarColumnDesc cdTimeRef("TimeRef", "Reference column for Time1"); // a scalar measure column with a variable string reference // a column for the measures. No offset or it is to be static so // no offset column required. // The "main" column. ScalarColumnDesc cdMEVS("MEpochVarStr", "Another MEpoch column"); // a string column for the variable string references ScalarColumnDesc cdTimeRefStr("TimeRefStr", "String variable reference column"); // An array measure column with a variable (int) reference // A column for the measures ArrayColumnDesc cdTimeArr("Time1Arr", "An MEpoch array column"); // An int column for the variable references. ScalarColumnDesc cdTimeArrRef("TimeArrRef", "VarRef co for TimeArr"); // An array measure column with a variable (int) reference array column // and a variable offset column ArrayColumnDesc cdTime2Arr("Time2Arr", "An MEpoch array column"); // the offset column ArrayColumnDesc cdTime2ArrOffset("Time2ArrOffset", "Offset column for Time2Arr"); // the reference column ArrayColumnDesc cdTime2ArrRef("Time2ArrRef", "Ref column for Time2Arr"); // An array measure column with variable (string) reference array column // The "main" date column ArrayColumnDesc cdTime3Arr("Time3Arr", "An MEpoch array column"); // The string array column for the references ArrayColumnDesc cdTime3StrRef("Time3ArrStrRef", "Array string reference column"); // An array measure column with a scalar reference (string) column and // a scalar (per row) offset column. // That is, one reference stored per row // The "main" date column ArrayColumnDesc cdTime4Arr("Time4Arr", "An MEpoch array column"); // A scalar column for the references ScalarColumnDesc cdTime4StrRef("Time4StrRef", "Scalar int reference column"); // An array column for the variable offsets. Even though we want to // stored offsets per row the column must be an Array column because // offsets are Measures, i.e., offsets are stored in a Measure column ScalarColumnDesc cdTime4ScaOffset("Time4ScaOffset", "Scalar offset column"); // a "spare" column used for testing purposes ArrayColumnDesc cdTestCol("SpareCol1", "Test of exception column"); // a spare offset column ArrayColumnDesc cdTestArrOffset("SpareArrOffset", "Spare int array column"); // All of the above column descriptors are added to the table as usual td.addColumn(cdTime); td.addColumn(cdTOffset); td.addColumn(cdVarOffset); td.addColumn(cdMDir); td.addColumn(cdTimeRef); td.addColumn(cdTimeRefStr); td.addColumn(cdMEVS); td.addColumn(cdTimeArr); td.addColumn(cdTimeArrRef); td.addColumn(cdTime2Arr); td.addColumn(cdTime2ArrOffset); td.addColumn(cdTime2ArrRef); td.addColumn(cdTime3Arr); td.addColumn(cdTime3StrRef); td.addColumn(cdTime4Arr); td.addColumn(cdTime4StrRef); td.addColumn(cdTime4ScaOffset); td.addColumn(cdTestCol); td.addColumn(cdTestArrOffset); // We have the columns we need but there not yet measure columns. { // The following creates an (empty) MDirection column. The column // used is "MDirColumn". This is the simplest useful TableMeasDesc // declaration that can be done. Default MDirection::Ref is used // for the reference. // The value desc. specifies the column to use for the measures TableMeasValueDesc tmvdMDir(td, "MDirColumn"); // the TableMeasDesc gives the column a type TableMeasDesc tmdMDir(tmvdMDir); // writing create the measure column tmdMDir.write(td); } { MEpoch obsTime((MVEpoch(MVTime(1995, 5, 17, (8+18./60.)/24.))), MEpoch::UTC); // The following creates an (empty) Measure column. This // particular column is to contain MEpoch with a fixed reference // and offset. The column named "TimeOffset" is used for the // MEpochs. Columns for the refernce and offset are not needed as // these are fixed (and stored as keywords in the TimeOffset column // The fixed offset which is itself an MEpoch TableMeasOffsetDesc tmodObsTime(obsTime); // The reference descriptor associates the fixed reference and the // just declared offset descriptor TableMeasRefDesc tmrdObs(MEpoch::TAI, tmodObsTime); // The value descriptor specifies the column to use for the Measures TableMeasValueDesc tmvdObs(td, "TimeOffset"); // TableMeasDesc associate the value desc. and the reference desc. and // gives the column a type (MEpoch). TableMeasDesc tmdObs(tmvdObs, tmrdObs); // (test purposes only for purify - test assign and copy contructors TableMeasDesc tmdObs1 = tmdObs; TableMeasDesc tmdObs1a(tmdObs1); // finally write the descriptor! The column is now a TableMeasure // column. tmdObs1a.write(td); } { // Set up a MEpoch column with variable references and offsets // An offset is a measure. So for variable offsets a Measure // column is needed. The Value descriptor specifies the column to // use. TableMeasValueDesc tmvdObs(td, "TimeVarOffset"); // The descriptor gives the offset column a type. TableMeasDesc tmMOS(tmvdObs); TableMeasOffsetDesc tmOsDesc(tmMOS); // NB: this descriptor is not written. This is done via the write() // of the "main" column below. // Reference desc. specifies a column to use for the references and // associates the offset measure column. TableMeasRefDesc tmrd(td, "TimeRef", tmOsDesc); // The "main" measure column TableMeasValueDesc tmvd(td, "Time1"); // The desc. associates the value "main" column with the reference // (which includes the offset measure column). TableMeasDesc tmdMEpoch(tmvd, tmrd); // write creates the measure column tmdMEpoch.write(td); } { // A variable offset column is a Measure column, so a TableMeasDesc // is needed. TableMeasValueDesc tmvdObs(td, "TimeVarOffset"); TableMeasDesc tmMOS(tmvdObs); TableMeasOffsetDesc tmODesc(tmMOS); // Simplest useful TableMeasDesc declaration that can be done. TableMeasRefDesc tmrd(td, "TimeRefStr", tmODesc); TableMeasValueDesc tmvdMEpoch2(td, "MEpochVarStr"); TableMeasDesc tmdMEpoch2(tmvdMEpoch2, tmrd); tmdMEpoch2.write(td); } MEpoch mjdToday(MVEpoch(51234)); { // An array MEpoch column descriptor. The TableMeasDesc for an Array // measure column is identical to the Scalar measure column. TableMeasOffsetDesc tmodToday(mjdToday); TableMeasRefDesc tmrdGast(td, "TimeArrRef", tmodToday); TableMeasValueDesc tmvdGast(td, "Time1Arr"); // create a tmp and test if copy constructor and assignment work Vector u(1); u(0) = "h"; TableMeasDesc tmp(tmvdGast, tmrdGast, u); TableMeasDesc tmp2 = tmp; TableMeasDesc tmdArrGast(tmp2); tmdArrGast.write(td); } { // Used to demonstrate an exception, specifically, that a // ScalarMeasColumn cannot have an ArrayMeasColumn for its offsets // the measure offset column for the array measure column TableMeasValueDesc arrOffset(td, "SpareArrOffset"); // The descriptor gives the offset column a type. TableMeasDesc tmdOffset(arrOffset); // the True says wants to have an ArrayMeasColumn for offset TableMeasOffsetDesc tmodOS(tmdOffset, True); TableMeasRefDesc tmrdGast(MEpoch::GAST, tmodOS); TableMeasValueDesc tmvdGast(td, "SpareCol1"); TableMeasDesc tmdMDesc(tmvdGast, tmrdGast); tmdMDesc.write(td); } { // An array MEpoch column desc. with variable offset and reference. // the measure offset column for the array measure column TableMeasValueDesc arrOffset(td, "Time2ArrOffset"); // The descriptor gives the offset column a type. TableMeasDesc tmdOffset(arrOffset); TableMeasOffsetDesc tmOSDesc(tmdOffset, True); // measure reference column and associated offset TableMeasRefDesc tmARef(td, "Time2ArrRef", tmOSDesc); // the "main" value descriptor TableMeasValueDesc tmAVal(td, "Time2Arr"); // create a tmp and test if copy constructor and assignment work TableMeasDesc tmp(tmAVal, tmARef); TableMeasDesc tmp2 = tmp; TableMeasDesc tmdArray(tmp2); tmdArray.write(td); } { // An array MEpoch column desc. with variable string references. // measure reference column and associated offset TableMeasRefDesc tmARef(td, "Time3ArrStrRef"); // the "main" value descriptor TableMeasValueDesc tmAVal(td, "Time3Arr"); // create a tmp and test if copy constructor and assignment work TableMeasDesc tmdArray(tmAVal, tmARef); tmdArray.write(td); } { td.show (cout); for (uInt i=0; i u(2); TableMeasValueDesc tCol(td, "Time2Arr"); TableMeasDesc tmp(tCol, u); } catch (std::exception& x) { cout << "The following should report that the column's "; cout << "unit vector is too long.\n"; cout << x.what() << endl; } try { // test TableMeasValueDesc - invalid unit Vector u(1); u(0) = "m"; TableMeasValueDesc tCol(td, "Time2Arr"); TableMeasDesc tmp(tCol, u); } catch (std::exception& x) { cout << "The following should report that the column "; cout << "has an invalid unit.\n"; cout << x.what() << endl; } } // An array MEpoch column desc. with (string) references and offsets // per row. // the measure offset column for the array measure column TableMeasValueDesc scaOffset(td, "Time4ScaOffset"); // The descriptor gives the offset column a type. TableMeasDesc tmdOffset(scaOffset); TableMeasOffsetDesc tmOsDesc(tmdOffset); // test exception thrown on requesting the offset when it is variable if (doExcep) { try { // get getOffset on a variable offset column tmOsDesc.getOffset(); } catch (std::exception& x) { cout << "Attempt to reference undefined Measure offset "; cout << " exception on the TableMeasOffsetDesc object.\n"; cout << x.what() << endl; } } } // Define some commonly used values. MVEpoch mvobsTime((MVTime(1996, 5, 17, (8+18./60.)/24.))); MEpoch obsTime(mvobsTime, MEpoch::UTC); const uInt tabRows = 5; { // Finally create the table SetupNewTable newtab("tTableMeasures_tmp.tab", td, Table::New); Table tab(newtab); // Add the last measure definition. // Do that using the Table object (to test that part as well). // measure reference column and associated offset // the measure offset column for the array measure column TableMeasValueDesc scaOffset(td, "Time4ScaOffset"); // The descriptor gives the offset column a type. TableMeasDesc tmdOffset(scaOffset); TableMeasOffsetDesc tmOsDesc(tmdOffset); TableMeasRefDesc tmARef_tmp(td, "Time4StrRef", tmOsDesc); TableMeasRefDesc tmARef(td, "Time4StrRef"); if (!tmARef.isOffsetVariable() && !tmARef.isOffsetArray()) { cout << "PASS - TMRefDesc doesn't have an offset yet\n"; } else { cout << "FAIL - Reference should not yet have an offset\n"; } tmARef = tmARef_tmp; // the "main" value descriptor..testing assignment too TableMeasValueDesc tmAVal_tmp(td, "Time4Arr"); TableMeasValueDesc tmAVal(td, "Time3Arr"); tmAVal = tmAVal_tmp; // create a tmp and test if copy constructor and assignment work // test assignment too TableMeasDesc tmdArray_tmp(tmAVal, tmARef); TableMeasDesc tmdArray(tmAVal); tmdArray = tmdArray_tmp; tmdArray.write(tab); // test getting on the new descriptor reference TableMeasRefDesc testRef = tmdArray.getRefDesc(); if (testRef.hasOffset()) { cout << "PASS - Reference has column offset.\n"; } else { cout << "FAIL - Reference apparantly doesn't have an offset!\n"; } // At this point a table "tTableMeasures_tmp.tab" has been created. // It contains a number of empty Measure columns. The remainder of this // program tests the usage of Scalar(Array)MeasColumn objects for // putting and getting Measures into and out of the table. // Check that columns are measures. AlwaysAssertExit (TableMeasDescBase::hasMeasures (TableColumn(tab, "MDirColumn"))); AlwaysAssertExit (TableMeasDescBase::hasMeasures (TableColumn(tab, "TimeOffset"))); AlwaysAssertExit (TableMeasDescBase::hasMeasures (TableColumn(tab, "Time1"))); AlwaysAssertExit (TableMeasDescBase::hasMeasures (TableColumn(tab, "TimeVarOffset"))); AlwaysAssertExit (! TableMeasDescBase::hasMeasures (TableColumn(tab, "TimeRef"))); AlwaysAssertExit (TableMeasDescBase::hasMeasures (TableColumn(tab, "MEpochVarStr"))); AlwaysAssertExit (! TableMeasDescBase::hasMeasures (TableColumn(tab, "TimeRefStr"))); AlwaysAssertExit (TableMeasDescBase::hasMeasures (TableColumn(tab, "Time1Arr"))); AlwaysAssertExit (! TableMeasDescBase::hasMeasures (TableColumn(tab, "TimeArrRef"))); AlwaysAssertExit (TableMeasDescBase::hasMeasures (TableColumn(tab, "Time2Arr"))); AlwaysAssertExit (TableMeasDescBase::hasMeasures (TableColumn(tab, "Time2ArrOffset"))); AlwaysAssertExit (! TableMeasDescBase::hasMeasures (TableColumn(tab, "Time2ArrRef"))); AlwaysAssertExit (TableMeasDescBase::hasMeasures (TableColumn(tab, "Time3Arr"))); AlwaysAssertExit (! TableMeasDescBase::hasMeasures (TableColumn(tab, "Time3ArrStrRef"))); AlwaysAssertExit (TableMeasDescBase::hasMeasures (TableColumn(tab, "Time4Arr"))); AlwaysAssertExit (! TableMeasDescBase::hasMeasures (TableColumn(tab, "Time4StrRef"))); AlwaysAssertExit (TableMeasDescBase::hasMeasures (TableColumn(tab, "Time4ScaOffset"))); AlwaysAssertExit (TableMeasDescBase::hasMeasures (TableColumn(tab, "SpareCol1"))); AlwaysAssertExit (TableMeasDescBase::hasMeasures (TableColumn(tab, "SpareArrOffset"))); AlwaysAssertExit (TableMeasColumn(tab, "MDirColumn").isScalar()); AlwaysAssertExit (TableMeasColumn(tab, "TimeOffset").isScalar()); AlwaysAssertExit (TableMeasColumn(tab, "Time1").isScalar()); AlwaysAssertExit (TableMeasColumn(tab, "TimeVarOffset").isScalar()); AlwaysAssertExit (TableMeasColumn(tab, "MEpochVarStr").isScalar()); AlwaysAssertExit (! TableMeasColumn(tab, "Time1Arr").isScalar()); AlwaysAssertExit (! TableMeasColumn(tab, "Time2Arr").isScalar()); AlwaysAssertExit (! TableMeasColumn(tab, "Time2ArrOffset").isScalar()); AlwaysAssertExit (! TableMeasColumn(tab, "Time3Arr").isScalar()); AlwaysAssertExit (! TableMeasColumn(tab, "Time4Arr").isScalar()); AlwaysAssertExit (TableMeasColumn(tab, "Time4ScaOffset").isScalar()); AlwaysAssertExit (! TableMeasColumn(tab, "SpareCol1").isScalar()); AlwaysAssertExit (! TableMeasColumn(tab, "SpareArrOffset").isScalar()); cout << "Create MEpochScaCol from column TimeOffset...\n"; cout << "A column of MEpochs where the reference and offset are "; cout << "non-variable.\n"; // create first a null object and attach it and copy it etc. // to show that these operations work. MEpoch::ScalarColumn tmpCol; if (tmpCol.isNull()) { tmpCol.attach(tab, "TimeOffset"); } tmpCol.throwIfNull(); AlwaysAssertExit (tmpCol.columnName() == "TimeOffset"); cout << "Null MEpochScaCol successfully attached\n"; // no assignment operator but there is a copy constructor MEpoch::ScalarColumn timeCol(tmpCol); Vector u(1, "s"); timeCol.setDescRefCode (MEpoch::GAST); timeCol.setDescOffset (obsTime); timeCol.setDescUnits (u); { TableMeasColumn tmcol(tab, "Time1Arr"); AlwaysAssertExit (tmcol.columnName() == "Time1Arr"); tmcol.attach (tab, "TimeOffset"); AlwaysAssertExit (tmcol.columnName() == "TimeOffset"); tmcol.reference (TableMeasColumn(tab, "Time1Arr")); AlwaysAssertExit (tmcol.columnName() == "Time1Arr"); } // print some details things about the column if (timeCol.measDesc().isRefCodeVariable()) { cout << "The column has variable references.\n"; } else { cout << "The MeasRef for the column is: " << timeCol.getMeasRef() << endl; } { MEpoch::ArrayColumn arrayCol(tab, "Time1Arr"); Vector u(1); u(0) = "m"; if (doExcep) { try { arrayCol.setDescUnits (u); } catch (std::exception& x) { cout << "The following line should report an error "; cout << "in ScalarMeasColumn::setDescUnits - invalid unit.\n"; cout << x.what() << endl; } } cout << "Units of Time1Arr: " << arrayCol.measDesc().getUnits()(0).getName() << endl; u(0) = ""; arrayCol.setDescUnits (u); cout << "Units of Time1Arr: " << arrayCol.measDesc().getUnits()(0).getName() << endl; u(0) = "s"; arrayCol.setDescUnits (u); cout << "Units of Time1Arr: " << arrayCol.measDesc().getUnits()(0).getName() << endl; } // Add the rows to the table. // Thereafter we'll do gets and puts. tab.addRow (tabRows); cout << "Adding a few MEpochs to column TimeOffset...\n"; MEpoch tm(MVEpoch(1234.)); uInt i; for (i=0; i (tm.getRef().offset()); AlwaysAssertExit (offptr != 0); AlwaysAssertExit (near (offptr->get("s"), obsTime.get("s"), 1.e-10)); tm = MEpoch::Convert (tm, MEpoch::UTC)(); AlwaysAssertExit (near (tm.get("s"), MEpoch(MVEpoch(1234.+i/10.0)).get("s"), 1.e-10)); } // for coverage of reference member (via attach) cout << "TEST of attach/reference...\n"; MEpoch::ScalarColumn testVarStrCol; testVarStrCol.attach(tab, "MEpochVarStr"); cout << "Column attached...\n"; // copy constructor MEpoch::ScalarColumn testCopy = testVarStrCol; // and get the values for (i=0; i timeColSimple(tab, "TimeOffset"); MEpoch tm; for (uInt i=0; i(timeColSimple(i), "s"), 1.e-10)); AlwaysAssertExit (tm.getRef().getType() == MEpoch::GAST); const MEpoch* offptr = dynamic_cast (tm.getRef().offset()); AlwaysAssertExit (offptr != 0); AlwaysAssertExit (near (offptr->get("s"), obsTime.get("s"), 1.e-10)); tm = MEpoch::Convert (tm, MEpoch::UTC)(); AlwaysAssertExit (near (tm.get("s"), MEpoch(MVEpoch(1234.+i/10.0)).get("s"), 1.e-10)); MEpoch tm1 = timeColRead.convert (i, MEpoch::UTC); AlwaysAssertExit (tm1.getRef().getType() == MEpoch::UTC); offptr = dynamic_cast (tm1.getRef().offset()); AlwaysAssertExit (offptr == 0); AlwaysAssertExit (near (tm1.get("s"), MEpoch(MVEpoch(1234.+i/10.0)).get("s"), 1.e-10)); MEpoch tm2 = timeColRead.convert (i, tm1); AlwaysAssertExit (tm2.getRef().getType() == MEpoch::UTC); offptr = dynamic_cast (tm2.getRef().offset()); AlwaysAssertExit (offptr == 0); AlwaysAssertExit (near (tm2.get("s"), MEpoch(MVEpoch(1234.+i/10.0)).get("s"), 1.e-10)); MPosition mpobs; MeasTable::Observatory(mpobs, "WSRT"); MEpoch::Ref mref(MEpoch::LAST, MeasFrame(mpobs)); MEpoch tm3 = timeColRead.convert (i, mref); AlwaysAssertExit (tm3.getRef().getType() == MEpoch::LAST); offptr = dynamic_cast (tm3.getRef().offset()); AlwaysAssertExit (offptr == 0); MEpoch tm4 = MEpoch::Convert (tm3, mref)(); AlwaysAssertExit (near (tm3.get("s"), tm4.get("s"), 1.e-10)); MEpoch tm5 = timeColRead.convert (i, tm4); AlwaysAssertExit (tm5.getRef().getType() == MEpoch::LAST); offptr = dynamic_cast (tm5.getRef().offset()); AlwaysAssertExit (offptr == 0); AlwaysAssertExit (near (tm5.get("s"), tm4.get("s"), 1.e-10)); } } { Table tab("tTableMeasures_tmp.tab", Table::Update); // A column of MDirections MDirection::ScalarColumn mdirCol(tab, "MDirColumn"); if (mdirCol.measDesc().isRefCodeVariable()) { cout << "Error: reference is variable!\n"; } cout << "Filling the MDirection column MDirColumn\n"; MDirection mdir; for (uInt i=0; i (mtmp.getRef().offset()); AlwaysAssertExit (offptr != 0); AlwaysAssertExit (near (offptr->get("s"), offset.get("s"), 1.e-10)); offset.set(MVEpoch(1234.1)); me.set(MVEpoch(51234.2), MEpoch::Ref(MEpoch::UTC, offset)); meCol.put(1, me); AlwaysAssertExit (meCol.isDefined(1)); meCol.get(1, mtmp); AlwaysAssertExit (mtmp.getRef().getType() == MEpoch::UTC); AlwaysAssertExit (near (mtmp.get("s"), me.get("s"), 1.e-10)); offptr = dynamic_cast (mtmp.getRef().offset()); AlwaysAssertExit (offptr != 0); AlwaysAssertExit (near (offptr->get("s"), offset.get("s"), 1.e-10)); offset.set(MVEpoch(1234.2)); me.set(MVEpoch(51234.3), MEpoch::Ref(MEpoch::TAI, offset)); meCol.put(2, me); AlwaysAssertExit (meCol.isDefined(2)); meCol.get(2, mtmp); AlwaysAssertExit (mtmp.getRef().getType() == MEpoch::TAI); AlwaysAssertExit (near (mtmp.get("s"), me.get("s"), 1.e-10)); offptr = dynamic_cast (mtmp.getRef().offset()); AlwaysAssertExit (offptr != 0); AlwaysAssertExit (near (offptr->get("s"), offset.get("s"), 1.e-10)); offset.set(MVEpoch(1234.3)); me.set(MVEpoch(51234.4), MEpoch::Ref(MEpoch::UTC, offset)); meCol.put(3, me); AlwaysAssertExit (meCol.isDefined(3)); meCol.get(3, mtmp); AlwaysAssertExit (mtmp.getRef().getType() == MEpoch::UTC); AlwaysAssertExit (near (mtmp.get("s"), me.get("s"), 1.e-10)); offptr = dynamic_cast (mtmp.getRef().offset()); AlwaysAssertExit (offptr != 0); AlwaysAssertExit (near (offptr->get("s"), offset.get("s"), 1.e-10)); // put one in with no offset me.set(MVEpoch(51234.5), MEpoch::Ref(MEpoch::GMST1)); meCol.put(4, me); offset.set(MVEpoch(0)); AlwaysAssertExit (meCol.isDefined(4)); meCol.get(4, mtmp); AlwaysAssertExit (mtmp.getRef().getType() == MEpoch::GMST1); AlwaysAssertExit (near (mtmp.get("s"), me.get("s"), 1.e-10)); offptr = dynamic_cast (mtmp.getRef().offset()); AlwaysAssertExit (offptr != 0); AlwaysAssertExit (near (offptr->get("s"), offset.get("s"), 1.e-10)); // Test of exception. Try putting a reference with a frame into // a column with variable references if (doExcep) { try { MEpoch epoch_frame(Quantity(MeasData::MJDB1950,"d")); MeasFrame frame(epoch_frame); me.getRefPtr()->set(frame); meCol.put(0, me); } catch (std::exception& x) { cout << "The following line should report an error "; cout << "in ScalarMeasColumn::put - not allowed to put a "; cout << "measure with a frame in variable column.\n"; cout << x.what() << endl; } } } Vector ev(10); { Table tab("tTableMeasures_tmp.tab", Table::Update); cout << "Creating an MEpoch Array Column\n"; MEpoch::ArrayColumn tmpArrCol; if (tmpArrCol.isNull()) { tmpArrCol.attach(tab, "Time1Arr"); } tmpArrCol.throwIfNull(); cout << "Null MEpochArrCol successfully attached\n"; // no assignment operator but there is a copy constructor MEpoch::ArrayColumn arrayCol(tmpArrCol); MEpoch last(Quantity(13.45, "h"), MEpoch::Ref(MEpoch::TAI)); for (uInt i=0; i<10; i++) { last.set(Quantity(13.45 + i, "h")); ev(i) = last; } cout << "Adding a vector to the column at row 0.\n"; // before adding something check the isDefined() member if (!arrayCol.isDefined(0)) { cout << "PASS - nothing in the measure array column row yet\n"; } else { cout << "FAIL - there shouldn't be a valid value in the row!\n"; } arrayCol.put(0, ev); Vector ew; arrayCol.get(0, ew, True); // now row 0 should contain a valid entry if (arrayCol.isDefined(0)) { cout << "PASS - valid entry in array column row 0\n"; } else { cout << "FAIL - there should be something in row 0!\n"; } for (uInt i=0; i<10; i++) { AlwaysAssertExit (ew(i).getRef().getType() == MEpoch::TAI); const MEpoch* offptr = dynamic_cast (ew(i).getRef().offset()); AlwaysAssertExit (offptr != 0); AlwaysAssertExit (near (offptr->get("s"), mjdToday.get("s"), 1.e-10)); MEpoch tmp = MEpoch::Convert (ew(i), MEpoch::TAI)(); AlwaysAssertExit (near (tmp.get("s"), ev(i).get("s"), 1.e-10)); } Vector tm1 = arrayCol.convert (0, MEpoch::UTC); for (uInt i=0; i<10; i++) { AlwaysAssertExit (tm1(i).getRef().getType() == MEpoch::UTC); const MEpoch* offptr = dynamic_cast (tm1(i).getRef().offset()); AlwaysAssertExit (offptr == 0); MEpoch tmp = MEpoch::Convert (ev(i), MEpoch::UTC)(); AlwaysAssertExit (near (tmp.get("s"), tm1(i).get("s"), 1.e-10)); } Vector tm2 = arrayCol.convert (0, tm1(0)); for (uInt i=0; i<10; i++) { AlwaysAssertExit (tm2(i).getRef().getType() == MEpoch::UTC); const MEpoch* offptr = dynamic_cast (tm2(i).getRef().offset()); AlwaysAssertExit (offptr == 0); MEpoch tmp = MEpoch::Convert (ev(i), MEpoch::UTC)(); AlwaysAssertExit (near (tmp.get("s"), tm2(i).get("s"), 1.e-10)); } MPosition mpobs; MeasTable::Observatory(mpobs, "WSRT"); MEpoch::Ref mref(MEpoch::LAST, MeasFrame(mpobs)); Vector tm3 = arrayCol.convert (0, mref); for (uInt i=0; i<10; i++) { AlwaysAssertExit (tm3(i).getRef().getType() == MEpoch::LAST); const MEpoch* offptr = dynamic_cast (tm3(i).getRef().offset()); AlwaysAssertExit (offptr == 0); MEpoch tmp = MEpoch::Convert (ev(i), mref)(); AlwaysAssertExit (near (tmp.get("s"), tm3(i).get("s"), 1.e-10)); } Vector tm5 = arrayCol.convert (0, tm3(0)); for (uInt i=0; i<10; i++) { AlwaysAssertExit (tm5(i).getRef().getType() == MEpoch::LAST); const MEpoch* offptr = dynamic_cast (tm5(i).getRef().offset()); AlwaysAssertExit (offptr == 0); AlwaysAssertExit (near (tm5(i).get("s"), tm3(i).get("s"), 1.e-10)); } if (doExcep) { try { Vector u(1); arrayCol.setDescUnits (u); } catch (std::exception& x) { cout << "The following line should report an error "; cout << "in ScalarMeasColumn::setDescUnits - not allowed to put "; cout << "when the table is not empty.\n"; cout << x.what() << endl; } } } { cout << "Open table again in RO mode to test ArrayMeasColumn...\n"; Table tab("tTableMeasures_tmp.tab", Table::Old); cout << "Creating an MEpoch Array Column\n"; MEpoch::ArrayColumn arrayCol(tab, "Time1Arr"); Vector ew; arrayCol.get(0, ew, True); ew = arrayCol(0); for (uInt i=0; i<10; i++) { AlwaysAssertExit (ew(i).getRef().getType() == MEpoch::TAI); const MEpoch* offptr = dynamic_cast (ew(i).getRef().offset()); AlwaysAssertExit (offptr != 0); AlwaysAssertExit (near (offptr->get("s"), mjdToday.get("s"), 1.e-10)); MEpoch tmp = MEpoch::Convert (ew(i), MEpoch::TAI)(); AlwaysAssertExit (near (tmp.get("s"), ev(i).get("s"), 1.e-10)); } } { // a bunch of extra tests Table tab("tTableMeasures_tmp.tab", Table::Update); MEpoch::ArrayColumn arrMeasCol; arrMeasCol.attach(tab, "Time2Arr"); // copy constructor MEpoch::ArrayColumn testCopy = arrMeasCol; MEpoch utcE(Quantity(1.45, "h"), MEpoch::Ref(MEpoch::UTC)); MEpoch taiE(Quantity(1.45, "h"), MEpoch::Ref(MEpoch::TAI)); Vector inArr(10); for (uInt i=0; i<10; i++) { if (i%2 == 0) { utcE.set(Quantity(11.45 + i, "h")); utcE.setOffset(MEpoch(Quantity(12. - i, "h"))); inArr(i) = utcE; } else { taiE.set(Quantity(13.45 + i, "h")); taiE.setOffset(MEpoch(Quantity(15.6 - i, "h"))); inArr(i) = taiE; } } cout << "Adding vectors to the test measure column\n"; for (uInt i=0; i outArr; testAttach.get(0, outArr, True); const MEpoch* offptr; const MEpoch* offptrin; for (uInt i=0; i<10; i++) { AlwaysAssertExit (outArr(i).getRef().getType() == inArr(i).getRef().getType()); AlwaysAssertExit (near (outArr(i).get("s"), inArr(i).get("s"), 1.e-10)); offptr = dynamic_cast (outArr(i).getRef().offset()); AlwaysAssertExit (offptr != 0); offptrin = dynamic_cast (inArr(i).getRef().offset()); AlwaysAssertExit (near (offptr->get("s"), offptrin->get("s"), 1.e-10)); } // see if the reference is variable if (testCopy.measDesc().isRefCodeVariable()) { cout << "Reference for ArrayMeasCol is variable\n"; } else { cout << "Reference for ArrayMeasCol is not variable\n"; } // getRef for the column cout << "Reference for the ArrayMeasCol is: " << testCopy.getMeasRef(); cout << endl; // stuff to increase line coverage of classes if (doExcep) { try { // test throw if null exception MEpoch::ArrayColumn nullCol; nullCol.throwIfNull(); } catch (std::exception& x) { cout << "The following line should be a "; cout << "null column exception.\n"; cout << x.what() << endl; } } // array column with variable string references MEpoch::ArrayColumn varStrRefColtmp; varStrRefColtmp.attach(tab, "Time3Arr"); MEpoch::ArrayColumn varStrRefCol = varStrRefColtmp; for (uInt i=0; i (inArr(i).getRef().offset()); offptr = dynamic_cast (outArr(i).getRef().offset()); AlwaysAssertExit (offptr == 0); MEpoch tmp = MEpoch::Convert (outArr(i), inArr(i).getRef())(); AlwaysAssertExit (near (tmp.get("s"), inArr(i).get("s"), 1.e-10)); } // test putting of an empty array Vector dummy; varStrRefCol.put(0, dummy); dummy.resize(1); varStrRefCol.get(0, dummy, True); AlwaysAssertExit (dummy.nelements() == 0); // last thing to test. Array columns with scalar column offsets // and reference. First test attach and copy constructor. MEpoch::ArrayColumn scaStrRefColtmp; scaStrRefColtmp.attach(tab, "Time4Arr"); MEpoch::ArrayColumn scaStrRefCol = scaStrRefColtmp; // Only one reference and offset are stored per row. The reference // and offset stored is taken from the first element of each // Measure array stored. for (uInt i=0; i (inArr(0).getRef().offset()); offptr = dynamic_cast (outArr(i).getRef().offset()); AlwaysAssertExit (offptr != 0); AlwaysAssertExit (near (offptr->get("s"), offptrin->get("s"), 1.e-10)); MEpoch tmp = MEpoch::Convert (outArr(i), inArr(i).getRef())(); AlwaysAssertExit (near (tmp.get("s"), inArr(i).get("s"), 1.e-10)); } // Check that the column can be accessed as a quantum. ArrayQuantColumn qcol(tab, "Time4Arr"); Vector > q = qcol(0); for (uInt i=0; i<10; i++) { AlwaysAssertExit (near (outArr(i).get("d"), q(i), 1.e-10)); } { // Resetting cannot be done, since the table is not empty. MEpoch::ArrayColumn arrayCol(tab, "Time1Arr"); Bool excp = False; try { arrayCol.setDescRefCode (MEpoch::TAI); } catch (std::exception&) { excp = True; } AlwaysAssertExit (excp); excp = False; try { arrayCol.setDescOffset (obsTime); } catch (std::exception&) { excp = True; } AlwaysAssertExit (excp); } // One last thing to test...test array conformance exception if (doExcep) { try { Array badShapeArr(IPosition(2,2)); scaStrRefCol.get(0, badShapeArr, False); } catch (std::exception& x) { cout << "The following line should be a "; cout << "Table array conformance error exception.\n"; cout << x.what() << endl; } } } // Do finally some performance checking. { Timer timer; } } // Define helper functions to create a vector of ref codes and string. // These functions simulate the change of refcodes. // It only deals with MEpoch for the test in testRefCodeChg. // So first we have 6 types, thereafter 7 types with some different codings. // type: LAST LMST GMST1 GAST UT1 UT2 UTC // old code: 0 1 2 8 4 5 // new code: 0 1 2 5 8 3 4 void getRef1 (Vector& curTypes, Vector& curCodes, const MeasureHolder& measHolder) { TableMeasRefDesc::defaultTypesFunc (curTypes, curCodes, measHolder); AlwaysAssertExit (curTypes.nelements() > 10); curCodes[3] = 8; curTypes.resize (6, True); curCodes.resize (6, True); } void getRef2 (Vector& curTypes, Vector& curCodes, const MeasureHolder& measHolder) { TableMeasRefDesc::defaultTypesFunc (curTypes, curCodes, measHolder); AlwaysAssertExit (curTypes.nelements() > 10); curTypes.resize (7, True); curCodes.resize (7, True); curCodes[3] = 5; curCodes[4] = 8; curCodes[5] = 3; curCodes[6] = 4; } Bool check (const MEpoch& ep1, const MEpoch& ep2) { if (ep1.getRefString() != ep2.getRefString()) return False; if (ep1.get("d") != ep2.get("d")) return False; return True; } Bool check (const Vector& ep1, const Vector& ep2) { if (ep1.size() != ep2.size()) return False; for (uInt i=0; i va(2); Vector vb(3); Vector vbi(3); va[0] = MEpoch (Quantity(10,"d"), MEpoch::Types(8)); va[1] = MEpoch (Quantity(11,"d"), MEpoch::Types(8)); vb[0] = MEpoch (Quantity(10,"d"), MEpoch::Types(4)); vb[1] = MEpoch (Quantity(11,"d"), MEpoch::Types(5)); vb[2] = MEpoch (Quantity(12,"d"), MEpoch::Types(1)); vbi[0] = 4; vbi[1] = 5; vbi[2] = 1; // Create a table with measures and write values. { TableDesc td; // Create a MEpoch column with variable integer refcode. ScalarColumnDesc cdTime("Time"); ScalarColumnDesc cdTimeRef("TimeRef"); ArrayColumnDesc cdATime("ATime"); ScalarColumnDesc cdATimeRef("ATimeRef"); ArrayColumnDesc cdBTime("BTime"); ArrayColumnDesc cdBTimeRef("BTimeRef"); td.addColumn(cdTime); td.addColumn(cdTimeRef); td.addColumn(cdATime); td.addColumn(cdATimeRef); td.addColumn(cdBTime); td.addColumn(cdBTimeRef); TableMeasRefDesc tmrd(td, "TimeRef"); TableMeasValueDesc tmvd(td, "Time"); TableMeasRefDesc tmard(td, "ATimeRef"); TableMeasValueDesc tmavd(td, "ATime"); TableMeasRefDesc tmbrd(td, "BTimeRef"); TableMeasValueDesc tmbvd(td, "BTime"); TableMeasDesc tmdMEpoch(tmvd, tmrd); TableMeasDesc tmdAMEpoch(tmavd, tmard); TableMeasDesc tmdBMEpoch(tmbvd, tmbrd); tmdMEpoch.write(td); tmdAMEpoch.write(td); tmdBMEpoch.write(td); // Create the table SetupNewTable newtab("tTableMeasures_tmp.tab", td, Table::New); Table tab(newtab, 6); MEpoch::ScalarColumn tmpCol(tab, "Time"); MEpoch::ArrayColumn tmpACol(tab, "ATime"); MEpoch::ArrayColumn tmpBCol(tab, "BTime"); tmpCol.put (0, MEpoch(Quantity(0,"d"), MEpoch::Types(0))); tmpCol.put (1, MEpoch(Quantity(1,"d"), MEpoch::Types(1))); tmpCol.put (2, MEpoch(Quantity(2,"d"), MEpoch::Types(2))); tmpCol.put (3, MEpoch(Quantity(3,"d"), MEpoch::Types(8))); tmpCol.put (4, MEpoch(Quantity(4,"d"), MEpoch::Types(4))); tmpCol.put (5, MEpoch(Quantity(5,"d"), MEpoch::Types(5))); tmpACol.put (5, va); tmpBCol.put (5, vb); } // Check if the values are fine. { Table tab("tTableMeasures_tmp.tab"); MEpoch::ScalarColumn tmpCol(tab, "Time"); MEpoch::ArrayColumn tmpACol(tab, "ATime"); MEpoch::ArrayColumn tmpBCol(tab, "BTime"); ScalarColumn refCol(tab, "TimeRef"); ScalarColumn refACol(tab, "ATimeRef"); ArrayColumn refBCol(tab, "BTimeRef"); AlwaysAssertExit(refCol(0) == 0); AlwaysAssertExit(refCol(1) == 1); AlwaysAssertExit(refCol(2) == 2); AlwaysAssertExit(refCol(3) == 8); AlwaysAssertExit(refCol(4) == 4); AlwaysAssertExit(refCol(5) == 5); AlwaysAssertExit(refACol(5) == 8); AlwaysAssertExit(allEQ (refBCol(5), vbi)); AlwaysAssertExit(check(tmpCol(0), MEpoch(Quantity(0,"d"), MEpoch::Types(0)))); AlwaysAssertExit(check(tmpCol(1), MEpoch(Quantity(1,"d"), MEpoch::Types(1)))); AlwaysAssertExit(check(tmpCol(2), MEpoch(Quantity(2,"d"), MEpoch::Types(2)))); AlwaysAssertExit(check(tmpCol(3), MEpoch(Quantity(3,"d"), MEpoch::Types(8)))); AlwaysAssertExit(check(tmpCol(4), MEpoch(Quantity(4,"d"), MEpoch::Types(4)))); AlwaysAssertExit(check(tmpCol(5), MEpoch(Quantity(5,"d"), MEpoch::Types(5)))); AlwaysAssertExit(check(tmpACol(5), va)); AlwaysAssertExit(check(tmpBCol(5), vb)); } // Remove the refcode vectors from the keywords, so the behaviour of // old tables (without these keywords) can be checked. { Table tab("tTableMeasures_tmp.tab", Table::Update); ScalarColumn timCol(tab, "Time"); TableRecord& kw = timCol.rwKeywordSet(); TableRecord& mkw = kw.rwSubRecord ("MEASINFO"); mkw.removeField ("TabRefTypes"); mkw.removeField ("TabRefCodes"); } // Make sure the keywords have indeed be removed. // Check the values again. { Table tab("tTableMeasures_tmp.tab"); ScalarColumn timCol(tab, "Time"); const TableRecord& kw = timCol.keywordSet(); const TableRecord& mkw = kw.subRecord ("MEASINFO"); AlwaysAssertExit (! mkw.isDefined ("TabRefCodes")); AlwaysAssertExit (! mkw.isDefined ("TabRefTypes")); MEpoch::ScalarColumn tmpCol(tab, "Time"); AlwaysAssertExit(check(tmpCol(0), MEpoch(Quantity(0,"d"), MEpoch::Types(0)))); AlwaysAssertExit(check(tmpCol(1), MEpoch(Quantity(1,"d"), MEpoch::Types(1)))); AlwaysAssertExit(check(tmpCol(2), MEpoch(Quantity(2,"d"), MEpoch::Types(2)))); AlwaysAssertExit(check(tmpCol(3), MEpoch(Quantity(3,"d"), MEpoch::Types(8)))); AlwaysAssertExit(check(tmpCol(4), MEpoch(Quantity(4,"d"), MEpoch::Types(4)))); AlwaysAssertExit(check(tmpCol(5), MEpoch(Quantity(5,"d"), MEpoch::Types(5)))); } // Add the refcode vectors again by creating a writable column object. { Table tab("tTableMeasures_tmp.tab", Table::Update); MEpoch::ScalarColumn tmpCol(tab, "Time"); } // Make sure they exist again. Check the values. { Table tab("tTableMeasures_tmp.tab"); ScalarColumn timCol(tab, "Time"); const TableRecord& kw = timCol.keywordSet(); const TableRecord& mkw = kw.subRecord ("MEASINFO"); AlwaysAssertExit (mkw.isDefined ("TabRefCodes")); AlwaysAssertExit (mkw.isDefined ("TabRefTypes")); MEpoch::ScalarColumn tmpCol(tab, "Time"); AlwaysAssertExit(check(tmpCol(0), MEpoch(Quantity(0,"d"), MEpoch::Types(0)))); AlwaysAssertExit(check(tmpCol(1), MEpoch(Quantity(1,"d"), MEpoch::Types(1)))); AlwaysAssertExit(check(tmpCol(2), MEpoch(Quantity(2,"d"), MEpoch::Types(2)))); AlwaysAssertExit(check(tmpCol(3), MEpoch(Quantity(3,"d"), MEpoch::Types(8)))); AlwaysAssertExit(check(tmpCol(4), MEpoch(Quantity(4,"d"), MEpoch::Types(4)))); AlwaysAssertExit(check(tmpCol(5), MEpoch(Quantity(5,"d"), MEpoch::Types(5)))); } // Use the changed refcodes, so TableMeasures have to map correctly. TableMeasRefDesc::setTypesFunc (getRef2); va[0] = MEpoch (Quantity(10,"d"), MEpoch::Types(5)); va[1] = MEpoch (Quantity(11,"d"), MEpoch::Types(5)); vb[0] = MEpoch (Quantity(10,"d"), MEpoch::Types(8)); vb[1] = MEpoch (Quantity(11,"d"), MEpoch::Types(3)); vb[2] = MEpoch (Quantity(12,"d"), MEpoch::Types(1)); // Check the remapped values. { Table tab("tTableMeasures_tmp.tab"); ScalarColumn timCol(tab, "Time"); const TableRecord& kw = timCol.keywordSet(); const TableRecord& mkw = kw.subRecord ("MEASINFO"); AlwaysAssertExit (mkw.isDefined ("TabRefCodes")); AlwaysAssertExit (mkw.isDefined ("TabRefTypes")); MEpoch::ScalarColumn tmpCol(tab, "Time"); MEpoch::ArrayColumn tmpACol(tab, "ATime"); MEpoch::ArrayColumn tmpBCol(tab, "BTime"); AlwaysAssertExit(check(tmpCol(0), MEpoch(Quantity(0,"d"), MEpoch::Types(0)))); AlwaysAssertExit(check(tmpCol(1), MEpoch(Quantity(1,"d"), MEpoch::Types(1)))); AlwaysAssertExit(check(tmpCol(2), MEpoch(Quantity(2,"d"), MEpoch::Types(2)))); AlwaysAssertExit(check(tmpCol(3), MEpoch(Quantity(3,"d"), MEpoch::Types(5)))); AlwaysAssertExit(check(tmpCol(4), MEpoch(Quantity(4,"d"), MEpoch::Types(8)))); AlwaysAssertExit(check(tmpCol(5), MEpoch(Quantity(5,"d"), MEpoch::Types(3)))); AlwaysAssertExit(check(tmpACol(5), va)); AlwaysAssertExit(check(tmpBCol(5), vb)); } // Change a value. va[0] = MEpoch (Quantity(10,"d"), MEpoch::Types(4)); va[1] = MEpoch (Quantity(11,"d"), MEpoch::Types(4)); vb[0] = MEpoch (Quantity(10,"d"), MEpoch::Types(4)); vb[1] = MEpoch (Quantity(11,"d"), MEpoch::Types(1)); vb[2] = MEpoch (Quantity(12,"d"), MEpoch::Types(8)); vbi[0] = 9; vbi[1] = 1; vbi[2] = 4; { Table tab("tTableMeasures_tmp.tab", Table::Update); MEpoch::ScalarColumn tmpCol(tab, "Time"); MEpoch::ArrayColumn tmpACol(tab, "ATime"); MEpoch::ArrayColumn tmpBCol(tab, "BTime"); tmpCol.put (1, MEpoch(Quantity(4,"d"), MEpoch::Types(4))); tmpACol.put (1, va); tmpBCol.put (5, vb); } // Check the values. { Table tab("tTableMeasures_tmp.tab"); MEpoch::ScalarColumn tmpCol(tab, "Time"); MEpoch::ArrayColumn tmpACol(tab, "ATime"); MEpoch::ArrayColumn tmpBCol(tab, "BTime"); ScalarColumn refCol(tab, "TimeRef"); ScalarColumn refACol(tab, "ATimeRef"); ArrayColumn refBCol(tab, "BTimeRef"); AlwaysAssertExit(refCol(0) == 0); AlwaysAssertExit(refCol(1) == 9); AlwaysAssertExit(refCol(2) == 2); AlwaysAssertExit(refCol(3) == 8); AlwaysAssertExit(refCol(4) == 4); AlwaysAssertExit(refCol(5) == 5); AlwaysAssertExit(refACol(1) == 9); AlwaysAssertExit(refACol(5) == 8); AlwaysAssertExit(allEQ (refBCol(5), vbi)); AlwaysAssertExit(check(tmpCol(0), MEpoch(Quantity(0,"d"), MEpoch::Types(0)))); AlwaysAssertExit(check(tmpCol(1), MEpoch(Quantity(4,"d"), MEpoch::Types(4)))); AlwaysAssertExit(check(tmpCol(2), MEpoch(Quantity(2,"d"), MEpoch::Types(2)))); AlwaysAssertExit(check(tmpCol(3), MEpoch(Quantity(3,"d"), MEpoch::Types(5)))); AlwaysAssertExit(check(tmpCol(4), MEpoch(Quantity(4,"d"), MEpoch::Types(8)))); AlwaysAssertExit(check(tmpCol(5), MEpoch(Quantity(5,"d"), MEpoch::Types(3)))); AlwaysAssertExit(check(tmpACol(1), va)); AlwaysAssertExit(check(tmpBCol(5), vb)); } } int main(int argc, const char*[]) { try { Bool doExcep = (argc<2); if (doExcep) { cout << "Test of TableMeasures classes.\n"; } else { cout << "Test of TableMeasures classes without exceptions.\n"; } // Do main tests. testMain (doExcep); // Do tests where a refcode changes. testRefCodeChg(); cout << "Test completed normally...bye.\n"; } catch (std::exception& x) { cout << "An error occurred. The test ended early with the following"; cout << " message:\n"; cout << x.what() << endl; return 1; } return 0; } casacore-3.7.1/measures/TableMeasures/test/tTableMeasures.out000066400000000000000000000252421476623553700243410ustar00rootroot00000000000000Test of TableMeasures classes. TableDesc tTableMeasures_tmp version 1 (Directory ./) --------- Comment: A test of TableMeasures class. #Keywords = 0 #Columns = 19 Name=Time1 DataType=double DataManager=StandardStMan/StandardStMan Default=0 Comment = An MEpoch column #keywords=2 0 QuantumUnits : Array[-1] 1 MEASINFO : SUBRECORD Name=TimeOffset DataType=double DataManager=StandardStMan/StandardStMan Default=0 Comment = MEpoch column with fix reference and offset #keywords=2 0 QuantumUnits : Array[-1] 1 MEASINFO : SUBRECORD Name=TimeVarOffset DataType=double DataManager=StandardStMan/StandardStMan Default=0 Comment = Variable Offset col #keywords=2 0 QuantumUnits : Array[-1] 1 MEASINFO : SUBRECORD Name=MDirColumn DataType=double Nrdim=1 Shape=[2] DataManager=StandardStMan/StandardStMan Comment = Simple mdirection column #keywords=2 0 QuantumUnits : Array[-1] 1 MEASINFO : SUBRECORD Name=TimeRef DataType=Int DataManager=StandardStMan/StandardStMan Default=0 Comment = Reference column for Time1 #keywords=0 Name=TimeRefStr DataType=String DataManager=StandardStMan/StandardStMan Default= Comment = String variable reference column #keywords=0 Name=MEpochVarStr DataType=double DataManager=StandardStMan/StandardStMan Default=0 Comment = Another MEpoch column #keywords=2 0 QuantumUnits : Array[-1] 1 MEASINFO : SUBRECORD Name=Time1Arr DataType=double Nrdim=-1 Shape=[] DataManager=StandardStMan/StandardStMan Comment = An MEpoch array column #keywords=2 0 QuantumUnits : Array[-1] 1 MEASINFO : SUBRECORD Name=TimeArrRef DataType=Int DataManager=StandardStMan/StandardStMan Default=0 Comment = VarRef co for TimeArr #keywords=0 Name=Time2Arr DataType=double Nrdim=-1 Shape=[] DataManager=StandardStMan/StandardStMan Comment = An MEpoch array column #keywords=2 0 QuantumUnits : Array[-1] 1 MEASINFO : SUBRECORD Name=Time2ArrOffset DataType=double Nrdim=-1 Shape=[] DataManager=StandardStMan/StandardStMan Comment = Offset column for Time2Arr #keywords=2 0 QuantumUnits : Array[-1] 1 MEASINFO : SUBRECORD Name=Time2ArrRef DataType=Int Nrdim=-1 Shape=[] DataManager=StandardStMan/StandardStMan Comment = Ref column for Time2Arr #keywords=0 Name=Time3Arr DataType=double Nrdim=-1 Shape=[] DataManager=StandardStMan/StandardStMan Comment = An MEpoch array column #keywords=2 0 QuantumUnits : Array[-1] 1 MEASINFO : SUBRECORD Name=Time3ArrStrRef DataType=String Nrdim=-1 Shape=[] DataManager=StandardStMan/StandardStMan Comment = Array string reference column #keywords=0 Name=Time4Arr DataType=double Nrdim=-1 Shape=[] DataManager=StandardStMan/StandardStMan Comment = An MEpoch array column #keywords=0 Name=Time4StrRef DataType=String DataManager=StandardStMan/StandardStMan Default= Comment = Scalar int reference column #keywords=0 Name=Time4ScaOffset DataType=double DataManager=StandardStMan/StandardStMan Default=0 Comment = Scalar offset column #keywords=0 Name=SpareCol1 DataType=double Nrdim=-1 Shape=[] DataManager=StandardStMan/StandardStMan Comment = Test of exception column #keywords=2 0 QuantumUnits : Array[-1] 1 MEASINFO : SUBRECORD Name=SpareArrOffset DataType=double Nrdim=-1 Shape=[] DataManager=StandardStMan/StandardStMan Comment = Spare int array column #keywords=2 0 QuantumUnits : Array[-1] 1 MEASINFO : SUBRECORD * Time1 QuantumUnits: String array with shape [1] [d] MEASINFO: { type: String "epoch" VarRefCol: String "TimeRef" TabRefTypes: String array with shape [12] [LAST, LMST, GMST1, GAST, UT1, UT2, UTC, TAI, TDT, TCG, TDB, TCB] TabRefCodes: uInt array with shape [12] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] RefOffCol: String "TimeVarOffset" RefOffvarPerArr: Bool 0 } * TimeOffset QuantumUnits: String array with shape [1] [d] MEASINFO: { type: String "epoch" Ref: String "TAI" RefOffMsr: { type: String "epoch" refer: String "UTC" m0: { value: Double 49854.3 unit: String "d" } } } * TimeVarOffset QuantumUnits: String array with shape [1] [d] MEASINFO: { type: String "epoch" Ref: String "UTC" } * MDirColumn QuantumUnits: String array with shape [2] [rad, rad] MEASINFO: { type: String "direction" Ref: String "J2000" } * TimeRef * TimeRefStr * MEpochVarStr QuantumUnits: String array with shape [1] [d] MEASINFO: { type: String "epoch" VarRefCol: String "TimeRefStr" RefOffCol: String "TimeVarOffset" RefOffvarPerArr: Bool 0 } * Time1Arr QuantumUnits: String array with shape [1] [h] MEASINFO: { type: String "epoch" VarRefCol: String "TimeArrRef" TabRefTypes: String array with shape [12] [LAST, LMST, GMST1, GAST, UT1, UT2, UTC, TAI, TDT, TCG, TDB, TCB] TabRefCodes: uInt array with shape [12] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] RefOffMsr: { type: String "epoch" refer: String "UTC" m0: { value: Double 51234 unit: String "d" } } } * TimeArrRef * Time2Arr QuantumUnits: String array with shape [1] [d] MEASINFO: { type: String "epoch" VarRefCol: String "Time2ArrRef" TabRefTypes: String array with shape [12] [LAST, LMST, GMST1, GAST, UT1, UT2, UTC, TAI, TDT, TCG, TDB, TCB] TabRefCodes: uInt array with shape [12] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] RefOffCol: String "Time2ArrOffset" RefOffvarPerArr: Bool 1 } * Time2ArrOffset QuantumUnits: String array with shape [1] [d] MEASINFO: { type: String "epoch" Ref: String "UTC" } * Time2ArrRef * Time3Arr QuantumUnits: String array with shape [1] [d] MEASINFO: { type: String "epoch" VarRefCol: String "Time3ArrStrRef" } * Time3ArrStrRef * Time4Arr * Time4StrRef * Time4ScaOffset * SpareCol1 QuantumUnits: String array with shape [1] [d] MEASINFO: { type: String "epoch" Ref: String "GAST" RefOffCol: String "SpareArrOffset" RefOffvarPerArr: Bool 1 } * SpareArrOffset QuantumUnits: String array with shape [1] [d] MEASINFO: { type: String "epoch" Ref: String "UTC" } The following should report no such column for TableMeasRefDesc. TableMeasRefDesc::checkColumn; No such column: SillyColumnName The following should report that the column's type is no good. TableMeasRefDesc::checkColumn; Reference column's type must be Int or String: Time4ScaOffset The following should report no such column for TableMeasValueDesc. TableMeasValueDesc::checkColumn; No such column: SillyColumnName The following should report that the column is not array for TabelMeasValueDesc. TableMeasValueDesc::checkColumn; Column's type must be Double: Time4StrRef The following should report that the column's type should be double for the TableMeasValueDesc. TableMeasValueDesc::checkColumn; Column's type must be Double: Time2ArrRef The following should report that the column's unit vector is too long. TableMeasDescBase::setMeasUnits; Unit vector for column Time2Arr is too long The following should report that the column has an invalid unit. TableMeasDescBase::setMeasUnits; invalid unit m for column Time2Arr Attempt to reference undefined Measure offset exception on the TableMeasOffsetDesc object. TableMeasOffsetDesc::getOffset() attempt to reference undefined measure offset. PASS - TMRefDesc doesn't have an offset yet PASS - Reference has column offset. Create MEpochScaCol from column TimeOffset... A column of MEpochs where the reference and offset are non-variable. Null MEpochScaCol successfully attached The MeasRef for the column is: Reference for an Epoch with Type: GAST, Offset: Epoch: 50220::08:17:60.0000 The following line should report an error in ScalarMeasColumn::setDescUnits - invalid unit. TableMeasDescBase::resetUnits; invalid unit m for column Time1Arr Units of Time1Arr: h Units of Time1Arr: h Units of Time1Arr: s Adding a few MEpochs to column TimeOffset... Reading the MEpochs back from TimeOffset... TEST of attach/reference... Column attached... Epoch: 1::00:00:00.0000 Epoch: 1::00:00:00.0000 Epoch: 1::00:00:00.0000 Epoch: 1::00:00:00.0000 Epoch: 1::00:00:00.0000 The following line should report an error in reconstruct - invalid column exception. TableMeasDescBase::reconstruct; MEASINFO record not found for column TimeRef The following line should be a null column exception. Invalid Table operation: MeasTableColumn object is null The following line should be an illegal offset column type exception. Invalid Table data type when accessing column in ScalarColumn ctor for column SpareCol1 Reopening the table read-only and reading contents... Filling the MDirection column MDirColumn put: Direction: [0.565521, 0.205833, 0.798636] put: Direction: [0.565521, 0.205833, 0.798636] put: Direction: [0.565521, 0.205833, 0.798636] put: Direction: [0.565521, 0.205833, 0.798636] put: Direction: [0.565521, 0.205833, 0.798636] Reading from MDirection column MDirColumn retrieve: Direction: [0.565521, 0.205833, 0.798636] retrieve: Direction: [0.565521, 0.205833, 0.798636] retrieve: Direction: [0.565521, 0.205833, 0.798636] retrieve: Direction: [0.565521, 0.205833, 0.798636] retrieve: Direction: [0.565521, 0.205833, 0.798636] Test of column TimeVarOffset... A column of MEpoch where the reference and offset components are variable. The following line should report an error in ScalarMeasColumn::put - not allowed to put a measure with a frame in variable column. ScalarMeasColumn::put() measure has a frame. Illegal for variable reference column. Creating an MEpoch Array Column Null MEpochArrCol successfully attached Adding a vector to the column at row 0. PASS - nothing in the measure array column row yet PASS - valid entry in array column row 0 The following line should report an error in ScalarMeasColumn::setDescUnits - not allowed to put when the table is not empty. ArrayMeasColumn::setDescUnits cannot be done; the table is not empty Open table again in RO mode to test ArrayMeasColumn... Creating an MEpoch Array Column Adding vectors to the test measure column Reference for ArrayMeasCol is variable Reference for the ArrayMeasCol is: Reference for an Epoch with Type: LAST The following line should be a null column exception. Invalid Table operation: MeasTableColumn object is null The following line should be a Table array conformance error exception. ArrayMeasColumn::get: Table array conformance error Test completed normally...bye. casacore-3.7.1/measures/TableMeasures/test/tTableQuantum.cc000066400000000000000000000513161476623553700237660ustar00rootroot00000000000000//# tTableQuantum.cc: test program for Quantum columns in TableMeasures module //# Copyright (C) 1997,1998,1999,2000,2001,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main (int argc, const char* argv[]) { Bool doExcep = (argc<2); uInt nrrow = 5000; if (argc >= 2) { istringstream istr(argv[1]); istr >> nrrow; } try { cout << "Begin tTableQuantum.cc.\n"; // Need a table to work with. TableDesc td("tTableQuantum_tmp", "1", TableDesc::New); td.comment() = "Created by tTableQuantum.cc"; // This test uses two ScalarQuantum columns. They will be a // Quantum and a for Quantum columns. The Quantum // will have static "deg" units but the units for the Quantum // column will be variable. This requires an additional String column for // storing the units. ScalarColumnDesc scdQD("ScaQuantDouble", "A scalar column of Quantum with units 'deg'."); ScalarColumnDesc scdQC("ScaQuantComplex", "A scalar column Quantum with variable units."); ScalarColumnDesc scdStr("varUnitsColumn", "Units columns for column ScaQuantComplex"); // Also create ArrayQuantum columns for the test ArrayColumnDesc acdQD("ArrQuantDouble", "A Quantum array column"); ArrayColumnDesc acdQD3("ArrQuantDoubleNonVar", "A Quantum array column"); ArrayColumnDesc acdQD4("ArrQuantDoubleNonVar2", "A Quantum array column with 2 units"); ArrayColumnDesc acdQD2("ArrQuantScaUnits", "A Quantum array column"); ArrayColumnDesc acdStr("varArrUnitsColumn", "String column for array of units"); ScalarColumnDesc ascdStr("varArrScaUnitsColumn", "Scalar string column for variable units per row"); ArrayColumnDesc bogusCol("BogusQuantCol", "an array column but won't be made a quantum column"); // These must be added to the table descriptor cout << "Adding column descriptors to the table...\n"; td.addColumn(scdQD); td.addColumn(scdQC); td.addColumn(acdQD); td.addColumn(acdQD2); td.addColumn(acdQD3); td.addColumn(acdQD4); td.addColumn(scdStr); td.addColumn(acdStr); td.addColumn(ascdStr); td.addColumn(bogusCol); // Now create the Table Quantum Descriptors. Three are used below but // a couple of dummy objects are created to test assignment and the copy // constructor. The object we finally want is tqdSQD. TableQuantumDesc tqddummy(td, "ScaQuantDouble", Unit("deg")); // test copy constructor TableQuantumDesc tqddummy2 = tqddummy; // test empty unit TableQuantumDesc tqdSQD(td, "ScaQuantDouble"); // test assignment tqdSQD = tqddummy2; Vector un1(2); Vector un2(2); un1(0) = "MHz"; un1(1) = "GHz"; un2(0) = "kHz"; un2(1) = "MHz"; TableQuantumDesc tqdSQC(td, "ScaQuantComplex", "varUnitsColumn"); TableQuantumDesc tqdAQC(td, "ArrQuantDouble", "varArrUnitsColumn"); TableQuantumDesc tqdAQC2(td, "ArrQuantScaUnits", "varArrScaUnitsColumn"); TableQuantumDesc tqdAQC3(td, "ArrQuantDoubleNonVar", Unit("MHz")); TableQuantumDesc tqdAQC4(td, "ArrQuantDoubleNonVar2", un1); cout << tqdAQC4.getUnits() << endl; TableQuantumDesc tqdAQC4a(td, "ArrQuantDoubleNonVar2", un2); cout << tqdAQC4a.getUnits() << endl; // test the exceptions if (doExcep) { cout << "Testing TableQuantumDesc constructor exceptions...\n"; try { // no such column TableQuantumDesc taexcep(td, "SillyName"); } catch (std::exception& x) { cout << "A no such column message should follow\n"; cout << x.what() << endl; } try { // variable unit's column doesn't exist. TableQuantumDesc taexcep(td, "ScaQuantComplex", "SillyName"); } catch (std::exception& x) { cout << "A no such unit's column message should follow\n"; cout << x.what() << endl; } try { // The variable unit's column exists but the units type isn't String ScalarColumnDesc eucol("testvarcolumn", "variable units column with incorrect type"); td.addColumn(eucol); TableQuantumDesc taexcep(td, "ScaQuantComplex", "testvarcolumn"); } catch (std::exception& x) { cout << "A message about an incorrect variable unit's type...\n"; cout << x.what() << endl; } } // ...and make them persistent. // the last one is done later after the table is created // (to test if write() works fine with the Table object). tqdSQD.write(td); tqdSQC.write(td); tqdAQC.write(td); tqdAQC2.write(td); tqdAQC3.write(td); cout << "Column's name is: " + tqdSQD.columnName() << endl; if (tqdSQD.isUnitVariable()) { cout << "Quantum column " + tqdSQD.columnName() << " has variable units.\n"; cout << "\tIts units are stored in String column '" + tqdSQD.unitColumnName() << "' \n"; } cout << "Column's name is: " + tqdSQC.columnName() << endl; // create a table with 5 rows. SetupNewTable newtab("tTableQuantum_tmp.tab", td, Table::New); Table qtab(newtab, 5); // tqdAQC4.write(qtab); // Check that columns contain quanta. AlwaysAssertExit (TableQuantumDesc::hasQuanta ( TableColumn (qtab, "ScaQuantDouble"))); AlwaysAssertExit (TableQuantumDesc::hasQuanta ( TableColumn (qtab, "ScaQuantComplex"))); AlwaysAssertExit (! TableQuantumDesc::hasQuanta ( TableColumn (qtab, "varUnitsColumn"))); AlwaysAssertExit (TableQuantumDesc::hasQuanta ( TableColumn (qtab, "ArrQuantDouble"))); AlwaysAssertExit (TableQuantumDesc::hasQuanta ( TableColumn (qtab, "ArrQuantDoubleNonVar"))); AlwaysAssertExit (TableQuantumDesc::hasQuanta ( TableColumn (qtab, "ArrQuantDoubleNonVar2"))); AlwaysAssertExit (TableQuantumDesc::hasQuanta ( TableColumn (qtab, "ArrQuantScaUnits"))); AlwaysAssertExit (! TableQuantumDesc::hasQuanta ( TableColumn (qtab, "varArrUnitsColumn"))); AlwaysAssertExit (! TableQuantumDesc::hasQuanta ( TableColumn (qtab, "varArrScaUnitsColumn"))); AlwaysAssertExit (! TableQuantumDesc::hasQuanta ( TableColumn (qtab, "BogusQuantCol"))); { // Play with a null object first cout << "Creating a null ScaQuantumCol()\n"; ScalarQuantColumn sq1Col; ScalarQuantColumn sqCol(sq1Col); cout << "Check if isNull and then throwIfNull\n"; if (sqCol.isNull()) { if (doExcep) { // test isnull exception try { sqCol.throwIfNull(); } catch (std::exception& x) { cout << "Catch an AipsError. Column is null...\n"; cout << x.what() << endl; } } cout << "Object says it is null...attach a column\n"; sqCol.attach(qtab, "ScaQuantDouble"); if (sqCol.isNull()) { cout << "Apparantly still null...this isn't correct!\n"; } else { cout << "No longer null...good\n"; } sqCol.throwIfNull(); } // This should be a quantum column with fixed units if (sqCol.isUnitVariable()) { cout << "Columns units: " << sqCol.getUnits() << endl; } // put some quanta into the columns. Quantum q; for (uInt i=0; i sq2Col(sqCol); sq2Col.throwIfNull(); } { // Could also read values from sqCol but instead a ScalarQuantCol // is created here to do that. // test attach member for this first ScalarQuantColumn rosq1Col; ScalarQuantColumn rosqCol(rosq1Col); if (rosqCol.isNull()) { rosqCol.attach(qtab, "ScaQuantDouble"); } rosqCol.throwIfNull(); cout << "Column's quantum units are: " << rosqCol.getUnits() << endl; uInt i; for (i=0; i q; for (i=0; i rosq2Col(rosqCol); rosq2Col.throwIfNull(); } { // Store a column of complex quantums with variable units. ScalarQuantColumn sqCol(qtab, "ScaQuantComplex"); if (sqCol.isUnitVariable()) { cout << "The units for ScaQuantComplex are variable.\n"; cout << "getUnits() should produce an empty string: " << sqCol.getUnits() << endl; } else { cout << "The units for ScaQuantComplex are not variable.\n"; cout << "This is an error.\n"; } Quantum q(Complex(4., 0.21), "deg"); sqCol.put(0, q); cout << q.get("m/s") << endl; q.convert("ms"); sqCol.put(1, q); cout << q.get("m/s") << endl; q.convert("g"); sqCol.put(2, q); cout << q.get("m/s") << endl; q.convert("Jy"); sqCol.put(3, q); cout << q.get("m/s") << endl; q.convert("GHz"); sqCol.put(4, q); cout << q.get("m/s") << endl; } { // Lets have a look at them ScalarQuantColumn rosqCol(qtab, "ScaQuantComplex"); uInt i; for (i=0; i q(1., "m/s"); for (i=0; i > quantArr(shape); Bool deleteIt; Quantum* q_p = quantArr.getStorage(deleteIt); q_p->setValue(1.41212); q_p->setUnit("GHz"); q_p++; q_p->setValue(1.4921); q_p->setUnit("MHz"); q_p++; q_p->setValue(1.4111); q_p->setUnit("kHz"); q_p++; q_p->setValue(1.4003); q_p->setUnit("Hz"); q_p++; q_p->setValue(1.22); q_p->setUnit("GHz"); q_p++; q_p->setValue(1.090909); q_p->setUnit("Hz"); quantArr.putStorage(q_p, deleteIt); { // Now for array columns. This set up a Quant Array column with // variable units where the units vary per array element. ArrayQuantColumn tmpCol; if (doExcep) { try { tmpCol.throwIfNull(); } catch (std::exception& x) { cout << "Catch an AipsError. Array column is null...\n"; cout << x.what() << endl; } // test attaching a bogus quantum column try { // create with a real column but not a quantum column // It will succeed because the QuantumDesc does not require a unit. ArrayQuantColumn testCol(qtab, "BogusQuantCol"); } catch (std::exception& x) { cout << "Exception should not occur" << endl; } } if (tmpCol.isNull()) { cout << "Array Quantum Column is initially null.\n"; tmpCol.attach(qtab, "ArrQuantDouble"); } // cover copy constructor ArrayQuantColumn aqCol = tmpCol; aqCol.throwIfNull(); if (aqCol.isUnitVariable()) { cout << "Array quantum column: units are variable.\n"; } else { cout << "Array quantum column units: " << aqCol.getUnits() << endl; } // cover putting an empty array (which should be OK) Array > emptyArr; aqCol.put(0, emptyArr); // put the quantum array in the column (having variable units). aqCol.put(0, quantArr); } { ArrayQuantColumn roaqColx(qtab, "ArrQuantDouble"); ArrayQuantColumn roaqCol(roaqColx); // test array conformance error exception on get() if (doExcep) { try { Array > badShapeArr(IPosition(2,2)); roaqCol.get(0, badShapeArr, False); } catch (std::exception& x) { cout << "The following line should be a "; cout << "Table array conformance error exception.\n"; cout << x.what() << endl; } } { // This should succeed. Array > badShapeArr(IPosition(2,2)); roaqCol.get(0, badShapeArr, True); cout << badShapeArr << endl; } cout << roaqCol(0) << endl; cout << roaqCol(0, "Hz") << endl; ArrayQuantColumn roaqCol1(qtab, "ArrQuantDouble", "kHz"); cout << roaqCol1(0) << endl; cout << roaqCol1(0, "Hz") << endl; cout << roaqCol1(0, un2) << endl; ArrayQuantColumn roaqCol2; roaqCol2.attach (qtab, "ArrQuantDouble"); roaqCol2.attach (qtab, "ArrQuantDouble", "MHz"); roaqCol2.attach (qtab, "ArrQuantDouble", un2); roaqCol2.reference (roaqCol1); ArrayQuantColumn roaqCol3(qtab, "ArrQuantDouble", un2); cout << roaqCol3(0) << endl; cout << roaqCol3(0, "Hz") << endl; } { // A second ArrayQuantColumn with variable units but in this case // the units only vary once per row as opposed to per array element // per row as in the example above. This can be done because the // TableQuantDesc for the row specified a ScalarColumn as the // units column. // Could just construct the column completely but test attach member ArrayQuantColumn aqCol; aqCol.attach(qtab, "ArrQuantScaUnits"); if (aqCol.isUnitVariable()) { cout << "Array quantum column: units are variable.\n"; } else { cout << "Array quantum column units: " << aqCol.getUnits() << endl; } // cover putting an empty array (which should be OK) Array > emptyArr; aqCol.put(0, emptyArr); // Put the quantum array in the column // Use unit "kHz" for the 2nd row. aqCol.put(0, quantArr); quantArr(IPosition(2,0,0)).setUnit ("kHz"); aqCol.put(1, quantArr); } { // another way of creating the object ArrayQuantColumn roaqCol(qtab, "ArrQuantScaUnits"); cout << roaqCol(0) << endl; cout << roaqCol(0, "Hz") << endl; cout << roaqCol(1) << endl; cout << roaqCol(1, "Hz") << endl; Quantum q(0.21, "Hz"); cout << roaqCol(0, q); } { // These should complete the coverage of the class // contructor ArrayQuantColumn aqc(qtab, "ArrQuantDouble"); // copy constructor ArrayQuantColumn aqc1 = aqc; // attach ArrayQuantColumn aqc2; aqc2.attach(qtab, "ArrQuantDouble"); // cover putting an empty array (which should be OK) Array > emptyArr; // non-variable units column ArrayQuantColumn aqc3(qtab, "ArrQuantDoubleNonVar"); aqc3.put(0, emptyArr); aqc3.put(0, quantArr); ArrayQuantColumn aqc4(qtab, "ArrQuantDoubleNonVar2"); aqc4.put(0, emptyArr); aqc4.put(0, quantArr); ArrayQuantColumn aqc3a(qtab, "ArrQuantDoubleNonVar"); cout << aqc3a.getUnits() << endl; cout << aqc3a(0) << endl; ArrayQuantColumn aqc4a(qtab, "ArrQuantDoubleNonVar2"); cout << aqc4a.getUnits() << endl; cout << aqc4a(0) << endl; } { // test ScalarQuantColumn::getColumn() ScalarQuantColumn col(qtab, "ScaQuantDouble"); std::shared_ptr > > v = col.getColumn(); AlwaysAssert(v->getValue().size() == 5, AipsError); std::shared_ptr > > w = col.getColumn("rad"); AlwaysAssert(w->getValue().size() == 5, AipsError); Double frac = M_PI/180; for (uInt i=0; i<5; ++i) { AlwaysAssert( near(w->getValue()[i], frac*v->getValue()[i]), AipsError ); } ScalarQuantColumn ccol(qtab, "ScaQuantComplex"); std::shared_ptr > > x = ccol.getColumn(); AlwaysAssert(x->getValue().size() == 5, AipsError); std::shared_ptr > > y = ccol.getColumn("rad"); for (uInt i=0; i<5; ++i) { AlwaysAssert( near(y->getValue()[i], frac*x->getValue()[i]), AipsError ); } } } catch (std::exception& x) { cout << "Unexpected exception1: " << x.what() << endl; return 1; } // Try it with a readonly table. try { Table qtab ("tTableQuantum_tmp.tab"); // Could also read values from sqCol but instead a ScalarQuantCol // is created here to do that. // test attach member for this first ScalarQuantColumn rosqCol; if (rosqCol.isNull()) { rosqCol.attach(qtab, "ScaQuantDouble"); } rosqCol.throwIfNull(); cout << "Column's quantum units are: " << rosqCol.getUnits() << endl; uInt i; for (i=0; i > aqArr(shape); aqArr = Quantum(1.41212, "GHz"); Array tabArr(shape); tabArr = 1.41212; ArrayQuantColumn aqCol(qtab, "ArrQuantDouble"); ArrayQuantColumn aqCol2(qtab, "ArrQuantDoubleNonVar"); ArrayColumn tabCol(qtab, "ArrQuantDouble"); cout << ">>>" << endl; Timer timer; for (uInt i=0; i>> put tq var arrays 2.04 real 1.18 user 0.77 system put tq fix arrays 0.98 real 0.62 user 0.33 system put tab arrays 0.28 real 0.16 user 0.11 system get tq var arrays 5.96 real 2.46 user 3.21 system get tq fix arrays 1.88 real 0.37 user 1.4 system get tab arrays 1.6 real 0.2 user 1.36 system <<< Execution of tTableQuantum.cc ended normally. casacore-3.7.1/measures/apps/000077500000000000000000000000001476623553700161135ustar00rootroot00000000000000casacore-3.7.1/measures/apps/CMakeLists.txt000066400000000000000000000007061476623553700206560ustar00rootroot00000000000000foreach(prog findmeastable) add_executable (${prog} ${prog}.cc) add_pch_support(${prog}) target_link_libraries (${prog} casa_measures ${CASACORE_ARCH_LIBS}) install(TARGETS ${prog}) endforeach(prog findmeastable) add_executable (measuresdata measuresdata/measuresdata.cc) target_link_libraries (measuresdata casa_measures ${CASACORE_ARCH_LIBS}) install(TARGETS measuresdata) install(PROGRAMS measuresdata/measuresdata-update TYPE BIN) casacore-3.7.1/measures/apps/findmeastable.cc000066400000000000000000000052231476623553700212220ustar00rootroot00000000000000//# MeasTable.cc: MeasTable provides Measure computing database data //# Copyright (C) 1995-1999,2000-2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include using namespace casacore; int main (int argc, char* argv[]) { String tableName("Observatories"); if (argc > 1) { tableName = argv[1]; } try { if (tableName=="help" || tableName=="-h" || tableName=="--help") { cerr << endl; cerr << "This program shows the location where a Measures table is found." << endl; cerr << "Run as: findmeastable tabletype" << endl; cerr << " default tabletype is Observatories" << endl; cerr << endl; cerr << " E.g. findmeastable IGRF" << endl; cerr << endl; return 0; } String tableNameLC = tableName; tableNameLC.downcase(); Table table; TableRecord kws; ROTableRow row; RORecordFieldPtr rfp[1]; String vs; Double dt; Int N = 0; String rfn[1]; if (!MeasIERS::getTable(table, kws, row, rfp, vs, dt, N, rfn, tableName, "measures."+tableNameLC+".directory", "")) { cerr << tableName << " measures table could not be found" << endl; return 1; } cout << "Measures table " << tableName<< " found as " << table.tableName() << endl; } catch (std::exception& x) { cerr << "Error: " << x.what() << endl;\ return 1; } return 0; } casacore-3.7.1/measures/apps/measuresdata/000077500000000000000000000000001476623553700205715ustar00rootroot00000000000000casacore-3.7.1/measures/apps/measuresdata/measuresdata-update000077500000000000000000000036101476623553700244550ustar00rootroot00000000000000#!/bin/sh # # measuresdata-update # Must be executable. Call it with all defaults or arguments for measuresdata # echo Calling measuresdata "$@" measuresdata "$@" if [ $? -ne 0 ] ; then echo Severe error calling measuresdata with "$@" exit 1 fi if [ ! -f measuresdata.rc ] ; then echo Severe error: no measuresdata.rc file returned exit 1 fi while grep -qe "^status:\\Wcont" measuresdata.rc ; do data=$(grep "^data:" measuresdata.rc | cut -d\ -f2-) ftp=$(grep "^ftp:" measuresdata.rc | cut -d\ -f2-) http=$(grep "^http:" measuresdata.rc | cut -d\ -f2-) https=$(grep "^https:" measuresdata.rc | cut -d\ -f2-) dir=$(grep "^dir:" measuresdata.rc | cut -d\ -f2-) file=$(grep "^file:" measuresdata.rc | cut -d\ -f2-) arg=$(grep "^arg:" measuresdata.rc | cut -d\ -f2-) if [ "$data" != "ascii" ] ; then echo Severe: only ascii data protocol supported exit 1 elif [ -z "$dir" -o -z "$file" -o -z "$arg" ] ; then echo Severe: missing dir, file or arg data exit 1 fi if [ -n "$http" ] ; then url="http://$http/$dir/$file" elif [ -n "$https" ] ; then url="https://$https/$dir/$file" elif [ -n "$ftp" ] ; then url="ftp://$ftp/$dir/$file" else echo Severe: missing ftp/http/https exit 1 fi if [ "$url" = "$old_url" ] ; then echo Warning: measuresdata requests the same url twice: $url # exit 1 fi old_url=$url echo Getting $url curl -L -O $url echo Calling measuresdata $arg measuresdata $arg if [ $? -ne 0 ] ; then echo Severe error calling measuresdata with $arg exit 1 fi if [ ! -f measuresdata.rc ] ; then echo Severe error: no measuresdata.rc file returned exit 1 fi done if grep -qe "^status:\\Wend" measuresdata.rc ; then echo measuresdata-update finished normally exit 0 else echo Severe: unknown status given in measuresdata.rc exit 1 fi casacore-3.7.1/measures/apps/measuresdata/measuresdata.cc000066400000000000000000001675661476623553700236030ustar00rootroot00000000000000//# measuresdata.cc: Program to read IERS and other data for Measure conversion //# Copyright (C) 2007-2008,2011,2013 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Version const string PROG_VS = "20110502wnb"; // Using using std::vector; using std::map; //*************************************************************************// // Description //*************************************************************************// // // The measuresdata program is able to analyse ascii tables // (or sinex files or xml files) and convert them into casa Tables which // can be used by e.g. the Measures class. // // The files have to be obtained by other means (e.g. a python script, // manually or another script (e.g. the provided measuresdata-update script. // // Normal operation: //
      • // call the program measuresdata. The program can have arguments (see // later, but the defaults suffice. The program will produce a cout log. // the program will return with either a failure status or with // success status. In latter case a resource file (measuresdata.rc) will // be present. This file will have format: //
      • // status: [end|cont] // ftp: // dir: // file: // data: ascii // arg: // if the status is given as 'end' the program is finished; if it is given as // 'cont' the script should obtain the file given by ftp, dir and file and // call measuresdata back with all the info in arg. // loop with calls to measuresdata //
      • // The arguments to measuresdata are given in casa 'Inputs' format. I.e. // as 'key=value'. In the following the key is followed by default. //
      • // type[all] [ALL|IERS|JPL|TAI_UTC|....] : type(s) of tables to produce // in[-] [ | -] : input ascii file. // Normally determined by measuresdata program // refresh[n] [y|n] : force a table refresh within the minimum // refresh period // renew[n] [y|n] : force a table renew, rather than an update, if // table has to be refreshed. // derange[1960,now+20] [yyyy,yyyy] : the wanted range for the JPL // planetary tables. // dir[./] //
      • // //*************************************************************************// // Declarations //*************************************************************************// // Structures struct tableProperties; struct inputValues; struct columnDescr; struct formatDescr; // Routines Int last_mjd(const Table *tab); Double today_mjd(); Double today_now(); Double double_data(const String &in); String date_string(Double date); String version_string(Double vs, uInt w=9, uInt p=4); Double vsdate_mjd(const Table *tab); String get_version(const Table *tab); Double dget_version(const Table *tab); Double dget_tversion(const Table *tab); void put_version(Table *tab, Double vs); void put_vsdate(Table *tab); Int int_data(const String &in); Bool split_data(vector &out, const String &in, const Regex &pat=RXwhite); Table *openr_table(const String &tnam); Bool close_table(const String &tnam, Table *&tab, Double vsup=0, Bool timup=True, Bool timshow=True); Bool IERSeop(tableProperties &tprop, inputValues &inVal); Bool IERSpred(tableProperties &tprop, inputValues &inVal); Bool JPLDE(tableProperties &tprop, inputValues &inVal); Bool TAI_UTC(tableProperties &tprop, inputValues &inVal); Bool IERSeop97(tableProperties &tprop, inputValues &inVal); Bool IERSeop2000(tableProperties &tprop, inputValues &inVal); Bool IERSpredict(tableProperties &tprop, inputValues &inVal); Bool IERSpredict2000(tableProperties &tprop, inputValues &inVal); Bool IGRF(tableProperties &tprop, inputValues &inVal); Bool DE200(tableProperties &tprop, inputValues &inVal); Bool DE405(tableProperties &tprop, inputValues &inVal); //*************************************************************************// // Data structures and constants //*************************************************************************// // Inputs description struct inputValues { // Data derived from argv inputs String appVersion; // Program version String type; // Table type (e.g. TAI_UTC) String dir; // Table data base directory (e.g. /aips++/data) String in; // Input file name (e.g. ./tai.in) Bool refresh; // Refresh, even if not necesaary for this file Bool renew; // Force renew complete table, rather than an update Block derange; // Range of DE table years. String ofile; // Name of output link file uInt x__n; // Current pointer in list of processes Bool x__rep; // Repeating Bool x__fn; // Should be a filename given vector x__val;// Parameter values // Derived data Bool testOnly; // Test if to be updated/renewed vector types; // List of all types to do (e.g. TAI_UTC IERSeop97) String intype; // Given intype (e.g. al) String fulltype; // Given proper input type (e.g. ALL) Double lastmjd; // Current last mjd Bool noup; // Skip update Bool forcedel; // Force delete Bool end; // End of cyle }; // Default inputs const inputValues defVal = { PROG_VS, // Program version "all", // Table type ".", // Table base directory "-", // Name of input file (or - if unknown) False, // Force refresh False, // Force renew Block(2), // DE table range "measuresdata.rc", // Output rc file 0, // Current pointer in list of processes False, // Repeating False, // Should be a filename given vector(), // Parameter values // Derived True, // Test if to be updated/renewed vector(), // All types to do "", // Given type in input "", // Full input type name 0.0, // Current last mjd False, // Skip update False, // Force delete True // End of cycle }; // Current inputs inputValues inVal; //*************************************************************************// // Format descriptor struct formatDescr { // Format types enum FormTypes { X, // Skip A, // ASCII I, // Int F, // Float N_FormTypes // Number of formats }; FormTypes form; // Format size_t start; // Start address in string size_t n; // Number in sub-string }; // IERSpredict const String IERSpredictFormat = String("") + "i2 i2 i2 x f8 x a1 x f9 f9 x f9 f9 x2 " + "a1 f10 f10 x f7 f7 x2 a1 x f9 f9 x f9 f9 X51"; // IERSpredict2000 const String &IERSpredict2000Format = IERSpredictFormat; // IGRF const String IGRFFormat = String("") + "a1 i3 i3 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7 f7" + " f7 f7 f7 f7 f7 f7 f7 f7 f7 f9 f10 f10 f9 f8"; // 24 coefficients and SV //*************************************************************************// // Column descriptor struct columnDescr { // Column types enum ColTypes { CTD, // Double CTI, // Int CTS, // String CTAD, // Double array N_ColTypes // Number of types }; String colName; // Name of column String unit; // Units in column ColTypes colType; // Column type uInt colId; // Number input column }; // TAI_UTC const columnDescr TAI_UTCCol[] = { {"MJD", "d", columnDescr::CTD, 4}, {"dUTC", "s", columnDescr::CTD, 6}, {"Offset", "d", columnDescr::CTD, 11}, {"Multiplier","s", columnDescr::CTD, 13}, {"", "", columnDescr::N_ColTypes, 0} }; // IERSeop97 const columnDescr IERSeop97Col[] = { {"MJD", "d", columnDescr::CTD, 3}, {"x", "arcsec", columnDescr::CTD, 4}, {"Dx", "arcsec", columnDescr::CTD, 10}, {"y", "arcsec", columnDescr::CTD, 5}, {"Dy", "arcsec", columnDescr::CTD, 11}, {"dUT1", "s", columnDescr::CTD, 6}, {"DdUT1", "s", columnDescr::CTD, 12}, {"LOD", "s", columnDescr::CTD, 7}, {"DLOD", "s", columnDescr::CTD, 13}, {"dPsi", "arcsec", columnDescr::CTD, 8}, {"DdPsi", "arcsec", columnDescr::CTD, 14}, {"dEps", "arcsec", columnDescr::CTD, 9}, {"DdEps", "arcsec", columnDescr::CTD, 15}, {"", "", columnDescr::N_ColTypes, 0} }; // IERSeop2000 const columnDescr IERSeop2000Col[] = { {"MJD", "d", columnDescr::CTD, 3}, {"x", "arcsec", columnDescr::CTD, 4}, {"Dx", "arcsec", columnDescr::CTD, 10}, {"y", "arcsec", columnDescr::CTD, 5}, {"Dy", "arcsec", columnDescr::CTD, 11}, {"dUT1", "s", columnDescr::CTD, 6}, {"DdUT1", "s", columnDescr::CTD, 12}, {"LOD", "s", columnDescr::CTD, 7}, {"DLOD", "s", columnDescr::CTD, 13}, {"dX", "arcsec", columnDescr::CTD, 8}, {"DdX", "arcsec", columnDescr::CTD, 14}, {"dY", "arcsec", columnDescr::CTD, 9}, {"DdY", "arcsec", columnDescr::CTD, 15}, {"", "", columnDescr::N_ColTypes, 0} }; // IERSpredict const columnDescr IERSpredictCol[] = { {"MJD", "d", columnDescr::CTD, 3}, {"x", "arcsec", columnDescr::CTD, 5}, {"Dx", "arcsec", columnDescr::CTD, 6}, {"y", "arcsec", columnDescr::CTD, 7}, {"Dy", "arcsec", columnDescr::CTD, 8}, {"dUT1", "s", columnDescr::CTD, 10}, {"DdUT1", "s", columnDescr::CTD, 11}, {"LOD", "s", columnDescr::CTD, 12}, {"DLOD", "s", columnDescr::CTD, 13}, {"dPsi", "arcsec", columnDescr::CTD, 15}, {"DdPsi", "arcsec", columnDescr::CTD, 16}, {"dEps", "arcsec", columnDescr::CTD, 17}, {"DdEps", "arcsec", columnDescr::CTD, 18}, {"", "", columnDescr::N_ColTypes, 0} }; // IERSpredict2000 const columnDescr IERSpredict2000Col[] = { {"MJD", "d", columnDescr::CTD, 3}, {"x", "arcsec", columnDescr::CTD, 5}, {"Dx", "arcsec", columnDescr::CTD, 6}, {"y", "arcsec", columnDescr::CTD, 7}, {"Dy", "arcsec", columnDescr::CTD, 8}, {"dUT1", "s", columnDescr::CTD, 10}, {"DdUT1", "s", columnDescr::CTD, 11}, {"LOD", "s", columnDescr::CTD, 12}, {"DLOD", "s", columnDescr::CTD, 13}, {"dX", "arcsec", columnDescr::CTD, 15}, {"DdX", "arcsec", columnDescr::CTD, 16}, {"dY", "arcsec", columnDescr::CTD, 17}, {"DdY", "arcsec", columnDescr::CTD, 18}, {"", "", columnDescr::N_ColTypes, 0} }; // IGRF const columnDescr IGRFCol[] = { {"MJD", "d", columnDescr::CTD, 0}, {"COEF", "nT/km", columnDescr::CTAD, 0}, {"dCOEF", "nT/km/a", columnDescr::CTAD, 0}, {"", "", columnDescr::N_ColTypes, 0} }; // DE200 const columnDescr DE200Col[] = { {"MJD", "d", columnDescr::CTD, 0}, {"x", "", columnDescr::CTAD, 0}, {"", "", columnDescr::N_ColTypes, 0} }; // DE405 const columnDescr DE405Col[] = { {"MJD", "d", columnDescr::CTD, 0}, {"x", "", columnDescr::CTAD, 0}, {"", "", columnDescr::N_ColTypes, 0} }; //*************************************************************************// // Table properties struct tableProperties { String type; // Table type (e.g. TAI_UTC) Double version; // Double version of table Double updper; // Minimum update period in days Bool renew; // Always renew, not update this table (normally False) Double MJD0; // Start MJD of table Double dMJD; // Increment MJD in table String tnam; // Table name (e.g. geodetic/TAI_UTC) String connectAs; // Connection protocol (ftp or html) String protoc; // Data protocol (ascii) Bool (*rout) (tableProperties &, inputValues &); // Routine to call const columnDescr *cdesc; // Column descriptions TableDesc *td; // Table descriptor vector colnames; // Column names vector colids; // Input column id vector columns; // Table columns for access String title; // Long title String contents; // Contents indicator; e.g. leapSecond Bool repeat; // More than one input possible String info; // Information fields space separated vector vinfo; // Information fields const String *formatString; // Format string vector fdesc; // Input format descriptions String fileAddress[3];// Remote file address (node, directory, file name) }; // Types that can be specified const String intypes[][2] = { { "ALL", "IERS IGRF JPL"}, { "IERS", "TAI_UTC EOP Predict"}, { "EOP", "IERSeop97 IERSeop2000"}, { "Predict", "IERSpredict IERSpredict2000"}, { "JPL", "DE200 DE405"}, { "TAI_UTC", "TAI_UTC"}, { "IERSeop97", "IERSeop97"}, { "IERSeop2000", "IERSeop2000"}, { "IERSpredict", "IERSpredict"}, { "IERSpredict2000", "IERSpredict2000"}, { "IGRF", "IGRF"}, { "DE200", "DE200"}, { "DE405", "DE405"}, { "", ""} }; const tableProperties allProperties[] = { //Type Version Update period //Always renew MJD0 dMJD //Table name Connection protocol Data protocol //Routine Columns Table descriptor //Column names Column ids Columns //Title Contents //Repeat Info Vector info //Format string Input format descriptions //File address { "TAI_UTC", 1.0, 31.0, True, 37300, 0.0, "geodetic/TAI_UTC", "ftp", "ascii", &TAI_UTC, TAI_UTCCol, 0, vector(), vector(), vector(), "TAI_UTC difference obtained from USNO", "leapSecond", False, "", vector(), 0, vector(), { "ftp.astron.nl", "outgoing/Measures", "tai-utc.dat" } }, //**********************************************************************// { "IERSeop97", 2.0, 34.0, False, 37664, 1.0, "geodetic/IERSeop97", "ftp", "ascii", &IERSeop97, IERSeop97Col, 0, vector(), vector(), vector(), "IERS EOPC04_05 Earth Orientation Data from IERS", "eop97", True, "", vector(), 0, vector(), { "hpiers.obspm.fr", "iers/eop/eopc04_14", "eopc04.xx" } }, //**********************************************************************// { "IERSeop2000", 2.0, 34.0, False, 37664, 1.0, "geodetic/IERSeop2000", "ftp", "ascii", &IERSeop2000, IERSeop2000Col, 0, vector(), vector(), vector(), "IERS EOP2000C04_05 Earth Orientation Data IAU2000","eop2000", True, "", vector(), 0, vector(), { "hpiers.obspm.fr", "iers/eop/eopc04_14", "eopc04_IAU2000.xx" } }, //**********************************************************************// { "IERSpredict", 2.0, 3.0, False, 0.0, 1.0, "geodetic/IERSpredict", "https", "ascii", &IERSpredict, IERSpredictCol, 0, vector(), vector(), vector(), "IERS Earth Orientation Data predicted from NEOS", "predict", False, "", vector(), &IERSpredictFormat, vector(), { "maia.usno.navy.mil", "ser7", "finals.daily" } }, //**********************************************************************// { "IERSpredict2000", 2.0, 3.0, False, 0.0, 1.0, "geodetic/IERSpredict2000", "https", "ascii", &IERSpredict2000, IERSpredict2000Col, 0, vector(), vector(), vector(), "IERS EOP2000C04_05 Earth Orientation Data IAU2000","predict2000", False, "", vector(), &IERSpredict2000Format, vector(), { "maia.usno.navy.mil", "ser7", "finals2000A.daily" } }, //**********************************************************************// { "IGRF", 2.0, 0.0, True, 13193.75, 1826.25, "geodetic/IGRF", "http", "ascii", &IGRF, IGRFCol, 0, vector(), vector(), vector(), "IGRF12 reference magnetic field", "earthField", False, "", vector(), &IGRFFormat, vector(), { "www.ngdc.noaa.gov", "IAGA/vmod/coeffs", "igrf12coeffs.txt" } }, //**********************************************************************// { "DE200", 2.0, 0.0, False, 0.0, 0.0, "ephemerides/DE200", "ftp", "ascii", &DE200, DE200Col, 0, vector(), vector(), vector(), "JPL Planetary ephemeris DE200", "DE200", True, "header.200 ascp****.200", vector(), 0, vector(), { "ssd.jpl.nasa.gov", "pub/eph/planets/ascii/de200", "" } }, //**********************************************************************// { "DE405", 2.0, 0.0, False, 0.0, 0.0, "ephemerides/DE405", "ftp", "ascii", &DE405, DE405Col, 0, vector(), vector(), vector(), "JPL Planetary ephemeris DE405", "DE405", True, "header.405 ascp****.405", vector(), 0, vector(), { "ssd.jpl.nasa.gov", "pub/eph/planets/ascii/de405", "" } }, //**********************************************************************// { "", 0.0, 0.0, False, 0.0, 0.0, "", "", "", 0, DE405Col, 0, vector(), vector(), vector(), "", "", True, "", vector(), 0, vector(), { "", "", "" } } // last table }; // As vectors/maps // All input types vector types; // Expansion per input type map > multypes; // Properties map properties; // Check existence of and fill properties void fillProperty(vector &field, const String &in) { for (uInt i=0;; ++i) { // Check if properties exist if (allProperties[i].type.empty()) break; if (allProperties[i].type == in) { properties[in] = allProperties[i]; field.push_back(in); }; }; } // Expand multiple fields and fill properties and types void expandTypes(vector &field, const String &in) { vector tmp; for (uInt i=0;; ++i) { if (intypes[i][0].empty()) break; if (intypes[i][0] == in && !split_data(tmp, intypes[i][1])) tmp.resize(0); }; if ((!tmp.empty() && tmp.size() == 1 && tmp[0] == in) || tmp.empty()) fillProperty(field, in); else for (uInt j=0; j field; if (intypes[i][0].empty()) break; types.push_back(intypes[i][0]); expandTypes(field, intypes[i][0]); if (!field.empty()) multypes[intypes[i][0]] = field; }; } // Create table properties void makeProperties() { // Create Table descriptor for (uInt i=0;; ++i) { if (allProperties[i].type.empty()) break; properties[allProperties[i].type].td = new TableDesc(allProperties[i].type, TableDesc::Scratch); for (uInt j=0;; ++j) { if (allProperties[i].cdesc[j].colName.empty()) break; properties[allProperties[i].type].colnames .push_back(allProperties[i].cdesc[j].colName); properties[allProperties[i].type].colids .push_back(allProperties[i].cdesc[j].colId); BaseColumnDesc *tcd = 0; switch (allProperties[i].cdesc[j].colType) { case columnDescr::CTD: tcd = new ScalarColumnDesc (allProperties[i].cdesc[j].colName, "", "IncrementalStMan", "IncrementalStMan"); if (!allProperties[i].cdesc[j].unit.empty()) { tcd->rwKeywordSet().define("UNIT", allProperties[i].cdesc[j].unit); }; break; case columnDescr::CTI: tcd = new ScalarColumnDesc (allProperties[i].cdesc[j].colName, "", "IncrementalStMan", "IncrementalStMan"); if (!allProperties[i].cdesc[j].unit.empty()) { tcd->rwKeywordSet().define("UNIT", allProperties[i].cdesc[j].unit); }; break; case columnDescr::CTS: tcd = new ScalarColumnDesc (allProperties[i].cdesc[j].colName, "", "IncrementalStMan", "IncrementalStMan"); if (!allProperties[i].cdesc[j].unit.empty()) { tcd->rwKeywordSet().define("UNIT", allProperties[i].cdesc[j].unit); }; break; case columnDescr::CTAD: tcd = new ArrayColumnDesc (allProperties[i].cdesc[j].colName, "", "IncrementalStMan", "IncrementalStMan", 1); if (!allProperties[i].cdesc[j].unit.empty()) { tcd->rwKeywordSet().define("UNIT", allProperties[i].cdesc[j].unit); }; break; default: throw (AipsError("Program error: undefined column type used")); break; }; properties[allProperties[i].type].td->addColumn(*tcd); delete tcd; tcd=0; }; }; // Create format descriptors for (uInt i=0;; ++i) { if (allProperties[i].type.empty()) break; // End of list if (!allProperties[i].formatString) continue; // No format given // Split the long format vector tmp; if (!split_data(tmp, *allProperties[i].formatString)) tmp.resize(0); uInt off(0); for (uInt j=0; j(*tab, tprop.colnames[j])); break; case columnDescr::CTI: tprop.columns.push_back (new ScalarColumn(*tab, tprop.colnames[j])); break; case columnDescr::CTS: tprop.columns.push_back (new ScalarColumn(*tab, tprop.colnames[j])); break; case columnDescr::CTAD: tprop.columns.push_back (new ArrayColumn(*tab, tprop.colnames[j])); break; default: throw (AipsError("Program error: undefined column type used")); break; }; }; } //*************************************************************************// // General support routines //*************************************************************************// // Match a string (non-case sensitive) to a list String minimaxNC(const String &in, const vector &tname) { String a; String b; uInt N_name(tname.size()); uInt i(0); a = upcase(in); // Exact fit? for (i=0; i= N_name) { size_t ia, ib; ia = a.length(); for (i=0; i yn) { String out; for (uInt i=0; i0) out += ","; out += uIntToString(uInt(yn[i])); }; return out; } uInt StringToUInt(String s) { uInt out; istringstream ss(s); ss >> out; return out; } // Get today's MJD Double today_mjd() { return (floor(today_now())); } // Get now as MJD Double today_now() { Quantity qdat; if (!Quantity::read(qdat, "today")) { throw (AipsError("Problems obtaining current time")); }; return (qdat.getValue("d")); } // Get string from date String date_string(Double date) { return (MVTime(date).string((MVTime::formatTypes) (MVTime::YMD | MVTime::CLEAN), 4)); } // Get version string String version_string(Double vs, uInt w, uInt p) { String out; ostringstream sout(out); sout.setf(ios::fixed, ios::floatfield); sout << setw(w) << setfill('0') << setprecision(p) << vs; return sout.str(); } // Split line (at pattern) into vector of strings. Bool split_data(vector &out, const String &in, const Regex &pat) { out.resize(0); const Int maxn = 100; String sout[maxn]; Int N = split(in, sout, maxn, pat); for (Int i=0; imlen && (tmp[mlen]=='D' || tmp[mlen]=='d')) tmp[mlen] = 'e'; istringstream(tmp) >> x; }; return x; } // Make Int from string Int int_data(const String &in) { Regex tst(RXint); Int x; Int mlen; if (tst.find(in.c_str(), in.size(), mlen) == String::npos) x = 0; else istringstream(in) >> x; return x; } //*************************************************************************// // Table related routines //*************************************************************************// // Test if readable table exists Bool testr_table(const String &tnam) { return Table::isReadable(tnam); } //*************************************************************************// // Test if table tnam has to be updated. tprop are the table properties; // inVal the switches Bool testu_table(const tableProperties &tprop, inputValues &inVal) { inVal.noup = True; // Assume no update needed inVal.forcedel = False; // Assume no forced delete inVal.lastmjd = 0.0; // Find if update needed if (inVal.x__fn) { // Input file expected if (File(Path(inVal.in)).isReadable()) inVal.noup = False; else cout << "No expected input file " << inVal.in << endl; } else if (!testr_table(tprop.tnam)) inVal.noup = False; // No table yet if (testr_table(tprop.tnam)) { Table *tab = openr_table(tprop.tnam); inVal.lastmjd = last_mjd(tab); if (inVal.noup) { if (dget_tversion(tab) < tprop.version) { // A new program version inVal.forcedel = True; inVal.noup = False; } else if (String(inVal.type, 0, 2) == String("DE")) { Int uyr = MVTime(inVal.lastmjd).year(); if (uyr < inVal.derange[1]) { // this only guarantees there will be /some/ data from derange[1] // but it looks like the DE ascii files finish in December of // the expected year so it should be ok. inVal.noup = False; } } else if (tab->nrow() && tprop.updper != 0.0 && today_mjd()-vsdate_mjd(tab) >= tprop.updper) { inVal.noup = False; // Update period passed } else if (tprop.repeat && today_mjd()-inVal.lastmjd > 2*tprop.updper) { inVal.noup = False; // Update since table out-of-date }; }; close_table(tprop.tnam, tab, 0, False); delete tab; tab = 0; }; // Find if forced refresh asked if (inVal.noup && !inVal.x__rep && inVal.refresh) inVal.noup = False; // Find if forced delete necessary if (!inVal.forcedel && !inVal.noup && !inVal.x__rep && (inVal.renew || tprop.renew)) inVal.forcedel = True; // Message if (inVal.noup) cout << tprop.tnam << " is up-to-date" << endl; return True; } //*************************************************************************// // Open readable table Table *openr_table(const String &tnam) { if (!testr_table(tnam)) { throw (AipsError(String("Unexpected problem finding table ") + tnam)); }; return (new Table(tnam)); } //*************************************************************************// // Create new table if 'renew' or if not exists; else open existing. // tnam is full path name; td is table descriptor; // vs is initial version (normally 1.0); // title is description; type is short name Table *create_table(const inputValues &inVal, tableProperties &tprop) { // Test existence and renewal Double vs = 1.0; if (testr_table(tprop.tnam)) { if (inVal.forcedel || !Table::isWritable(tprop.tnam)) { Table t(tprop.tnam); vs = int_data(get_version(&t)) + 1.0; String oldt(tprop.tnam + ".old"); t.rename(oldt, Table::New); }; }; // Open existing or new table Table *tab; TableDesc *td = properties[tprop.type].td; if (Table::isWritable(tprop.tnam)) tab = new Table(tprop.tnam, Table::Update); else { td->rwKeywordSet().define("VS_CREATE", (date_string(today_now()))); td->rwKeywordSet().define("VS_DATE", (date_string(today_now()))); td->rwKeywordSet().define("VS_VERSION", version_string(vs)); td->rwKeywordSet().define("VS_TYPE", tprop.title); td->rwKeywordSet().define("TAB_VERSION", version_string(tprop.version)); td->rwKeywordSet().define("MJD0", tprop.MJD0); td->rwKeywordSet().define("dMJD", tprop.dMJD); SetupNewTable newtab(tprop.tnam, *td, Table::New); tab = new Table(newtab); TableInfo &info = tab->tableInfo(); info.setType("IERS"); info.setSubType(tprop.contents); }; return tab; } //*************************************************************************// // Close table tab (with name tnam) and update version (if vsup>0); // the version date (if timup True); // and show the table time statistics (if timshow True). Bool close_table(const String &tnam, Table *&tab, Double vsup, Bool timup, Bool timshow) { Double vs = dget_version(tab); uInt n = tab->nrow(); Int tim(0); if (timshow) tim = last_mjd(tab); if (vsup>0) { vs += vsup; put_version(tab, vs); }; if (timup) put_vsdate(tab); delete tab; tab = 0; cout << tnam << " table " << version_string(vs); if (timup) cout << " now " << n << " entries"; else cout << " has " << n << " entries"; if (timshow) cout << " until " << tim; cout << endl; return True; } //*************************************************************************// // Get last MJD in table Int last_mjd(const Table *tab) { uInt n = tab->nrow(); Double mjd; if (n<1) mjd = 0; else ScalarColumn(*tab, "MJD").get(n-1, mjd); return Int(mjd); } //*************************************************************************// // Obtain VS_DATE from table Double vsdate_mjd(const Table *tab) { String dat; tab->keywordSet().get(String("VS_DATE"), dat); Quantity qdat; if (!Quantity::read(qdat, dat)) { throw (AipsError("Illegal date in VS_DATE: " + dat)); }; return qdat.getValue("d"); } //*************************************************************************// // Put VS_DATE in table void put_vsdate(Table *tab) { String vs; tab->rwKeywordSet().define("VS_DATE", (date_string(today_now()))); } //*************************************************************************// // Obtain VS_VERSION from table String get_version(const Table *tab) { String vs; tab->keywordSet().get("VS_VERSION", vs); return vs; } Double dget_version(const Table *tab) { return double_data(get_version(tab)); } //*************************************************************************// // Put VS_VERSION in table void put_version(Table *tab, Double vs) { tab->rwKeywordSet().define("VS_VERSION", version_string(vs)); } //*************************************************************************// // Obtain TAB_VERSION from table String get_tversion(const Table *tab) { String vs; if (tab->keywordSet().isDefined("TAB_VERSION")) { tab->keywordSet().get("TAB_VERSION", vs); } else vs = "0"; return vs; } //*************************************************************************// Double dget_tversion(const Table *tab) { return double_data(get_tversion(tab)); } //*************************************************************************// // Put TAB_VERSION in table void put_tversion(Table *tab, Double vs) { tab->rwKeywordSet().define("TAB_VERSION", version_string(vs)); } //*************************************************************************// // File read/write //*************************************************************************// // Read data from ascii input file. The table (for reference only) is tnam; // input file is inpath; out is vector of file lines. Bool read_data(vector &out, const String &, const Path &in, Bool del=True) { out.resize(0); ifstream infile(in.absoluteName().c_str()); if (!infile) { throw(AipsError(String("Cannot open the input data file ") + in.absoluteName())); }; String line; while (getline(infile, line)) out.push_back(line); infile.clear(); infile.close(); // Remove file if asked for if (del) { remove(in.absoluteName().c_str()); }; return True; } Bool read_line(vector &out, ifstream &infile) { String line; out.resize(0); if (getline(infile, line)) split_data(out, line); else return False; return True; } //*************************************************************************// // Write the measuresdata rc file. Bool writeLink(const tableProperties &tprop, const inputValues &inVal) { Path pout(inVal.ofile); ofstream ofile(pout.absoluteName().c_str()); if (!ofile) return False; if (inVal.end) ofile << "status: end" << endl; else { ofile << "status: cont"<< endl; ofile << tprop.connectAs << ": " << tprop.fileAddress[0] << endl; ofile << "dir: " << tprop.fileAddress[1] << endl; ofile << "file: " << tprop.fileAddress[2] << endl; ofile << "data: " << tprop.protoc << endl; ofile << "arg: " << " in=" << tprop.fileAddress[2] << " refresh=" << boolToString(inVal.refresh) << " renew=" << boolToString(inVal.renew) << " type=" << inVal.fulltype << " derange=" << blockIntToString(inVal.derange) << " x__n=" << inVal.x__n << " x__fn=" << boolToString(True) << " x__rep=" << boolToString(inVal.x__rep) << " x__val="; for (uInt i=0; i lines; read_data(lines, tprop.tnam, Path(inVal.in), True); // Split data lines into fields vector > fields; vector field; for (uInt i=0; i > allcol; for (uInt j=0; j()); }; for (uInt i=0; iaddRow((allcol[0].size()-tab->nrow())); createColumns(tab, tprop); for (uInt i=0; iputScalar(i, allcol[j][i]); }; }; rmColumns(tab, tprop); // Ready close_table(tprop.tnam, tab, 0.0001); // OK return True; } //*************************************************************************// // Fill geodetic/IERSeop97 table Bool IERSeop97(tableProperties &tprop, inputValues &inVal) { return IERSeop(tprop, inVal); } //*************************************************************************// // Fill geodetic/IERSeop2000 table Bool IERSeop2000(tableProperties &tprop, inputValues &inVal) { return IERSeop(tprop, inVal); } //*************************************************************************// // Fill eop table Bool IERSeop(tableProperties &tprop, inputValues &inVal) { // Test if to update if (testu_table(tprop, inVal) && inVal.noup) return True;;; // Determine what to read next Double ml = max(Double(inVal.lastmjd), tprop.MJD0); String ytd = uIntToString(MVTime(ml+1).year() % 100); if (ytd.size() == 1) ytd = String("0") + ytd; tprop.fileAddress[2].replace(tprop.fileAddress[2].size()-2, 2, ytd); // Check if in present and to be used if (inVal.testOnly || !inVal.x__fn || tprop.fileAddress[2] != inVal.in) return True; // Read the data file vector lines; read_data(lines, tprop.tnam, Path(inVal.in), True); // Split data lines into fields vector > fields; vector field; for (uInt i=0; i=20 && fields[j-1].size() < 2) --j; if (fields.size() < 20 || fields[j-2].size() !=16) { throw (AipsError("Incorrect input file for " + tprop.tnam)); }; // Create table fields vector > allcol; for (uInt j=0; j()); }; for (uInt i=0; i= 37665) { for (uInt j=0; j 0) { tab->addRow(Int(allcol[0].back() - ml)); createColumns(tab, tprop); for (uInt i=0; i ml) { uInt k = Int(allcol[0][i] - tprop.MJD0 - 1); for (uInt j=0; jputScalar(k, allcol[j][i]); }; }; }; rmColumns(tab, tprop); }; // Ready close_table(tprop.tnam, tab, 0.0001); // OK return True; } //*************************************************************************// // Fill geodetic/IERSpredict table Bool IERSpredict(tableProperties &tprop, inputValues &inVal) { return IERSpred(tprop, inVal); } //*************************************************************************// // Fill geodetic/IERSpredict2000 table Bool IERSpredict2000(tableProperties &tprop, inputValues &inVal) { return IERSpred(tprop, inVal); } //*************************************************************************// // Fill predict table Bool IERSpred(tableProperties &tprop, inputValues &inVal) { // Test if to update if (testu_table(tprop, inVal) && inVal.noup) return True; // Check if in present and to be used if (inVal.testOnly || !inVal.x__fn) return True; // Read the data file vector lines; read_data(lines, tprop.tnam, Path(inVal.in), True); // Split data lines into fields vector > fields; vector field; for (uInt i=0; i= tprop.fdesc.back().start + tprop.fdesc.back().n) { field.resize(0); for (uInt j=0; j > allcol; for (uInt j=0; j()); }; for (uInt i=0; i inVal.lastmjd) inVal.forcedel = True; // Old one too old Table *tab = create_table(inVal, tprop); inVal.forcedel = olddel; // Restore inVal.lastmjd = last_mjd(tab); // Fill table tab->keywordSet().get("MJD0", tprop.MJD0); if (tprop.MJD0 <= 0) tprop.MJD0 = allcol[0][0]-1; tab->rwKeywordSet().define("MJD0", tprop.MJD0); Double ml = max(Double(inVal.lastmjd), tprop.MJD0); if (allcol[0].back() - ml > 0) { tab->addRow(Int(allcol[0].back() - ml)); createColumns(tab, tprop); for (uInt i=0; i ml) { uInt k = Int(allcol[0][i] - tprop.MJD0 - 1); for (uInt j=0; jputScalar(k, allcol[j][i]); }; }; }; rmColumns(tab, tprop); }; // Ready close_table(tprop.tnam, tab, 0.0001); // OK return True; } //*************************************************************************// // Fill geodetic/IGRF table Bool IGRF(tableProperties &tprop, inputValues &inVal) { // Test if to update if (testu_table(tprop, inVal) && inVal.noup) return True; // Check if in present and to be used if (inVal.testOnly || !inVal.x__fn) return True; // Read the data file vector lines; read_data(lines, tprop.tnam, Path(inVal.in), True); // Split data lines into fields vector > fields; vector field; uInt expsize = tprop.fdesc.back().start + tprop.fdesc.back().n; // Size for (uInt i=0; i 180) { if (lines[i].size() < expsize) lines[i].resize(expsize, ' '); field.resize(0); for (uInt j=0; j > allcol; uInt n(0); // Number of coefficients uInt m(0); for (uInt i=0; i 0) { // Found field n = int_data(fields[i][1]); m = int_data(fields[i][2]); vector coldat; for (uInt j=3; jaddRow((allcol[0].size()-tab->nrow()-1)); createColumns(tab, tprop); for (uInt i=0; iputScalar(i, (tprop.MJD0 + 5*(i+1)*365.25)); vector col; for (uInt j=0; j Vcol(col); static_cast*>(tprop.columns[1])->put(i, Vcol); col.resize(0); if (i == allcol[0].size()-2) { for (uInt j=0; j(col); static_cast*>(tprop.columns[2])->put(i, Vcol); }; rmColumns(tab, tprop); // Ready close_table(tprop.tnam, tab, 0.0001); // OK return True; } //*************************************************************************// // Fill JPL planetary tables const uInt DE_FN_INC = 20; // DE ascii files are for 20 year intervals Bool JPLDE(tableProperties &tprop, inputValues &inVal) { /// cout << "--- JPL tables cannot be created yet ----" << endl;;; ///return True;;; // Test if to update if (testu_table(tprop, inVal) && inVal.noup) return True; Int uyr = 0; // value will be set from data file name // Check if header present Path hpath(tprop.vinfo[0]); if (hpath.isValid() && File(hpath).exists() && File(hpath).isReadable()) { if (inVal.in == tprop.vinfo[0]) { // "in" is the header file // (first time after header retreival) tprop.fileAddress[2] = tprop.vinfo[1]; Int de_syear = Int(inVal.derange[0]) / DE_FN_INC * DE_FN_INC; tprop.fileAddress[2].replace(4, 4, uIntToString(de_syear)); return True; } else { // "in" is a data file Path dpath(inVal.in); if (dpath.isValid() && File(dpath).exists() && File(dpath).isReadable()) { // file data file is present; output the next one to be retrieved String syr(inVal.in.substr(4,4)); uyr = StringToUInt(syr); tprop.fileAddress[2] = tprop.vinfo[1]; tprop.fileAddress[2].replace(4, 4, uIntToString(uyr + DE_FN_INC)); } // data file present } // working with data file } else { tprop.fileAddress[2] = tprop.vinfo[0]; return True; // Get header first }; // Dates Int stdat = Int(MVTime(uyr, 1, 1).day()); // Check if in present and to be used if (inVal.testOnly || !inVal.x__fn) return True; // Read header if (!(hpath.isValid() && File(hpath).exists() && File(hpath).isReadable())) { throw (AipsError("Cannot obtain the header file "+tprop.vinfo[0])); }; uInt ksize(0); uInt ncoeff(0); Double stepo(0); uInt incepo(0); vector kwnames; vector kwval; vector ptt; Vector pttA; vector hlines; read_data(hlines, tprop.vinfo[0], hpath, False); // Split header lines into fields vector > hfields; vector field; for (uInt i=0; i3 && hfields[bc][0] == "KSIZE=") { ksize = int_data(hfields[bc][1]); ncoeff = int_data(hfields[bc][3]); break; }; }; for (++bc; bc= n) break; }; break; }; break; }; for (++bc; bc= kwnames.size()) break; }; break; }; break; }; for (++bc; bc= 3*13) break; }; break; }; if (!ksize || !incepo || kwnames.size() != kwval.size() || ptt.size() != 3*13) { throw (AipsError("Illegal header file " + tprop.vinfo[0])); }; pttA.resize(ptt.size()); for (uInt i=0; i line; vector allmjd; vector > allcol; while (read_line(line, infile)) { if (line.size() < 2 || int_data(line[1]) != Int(ncoeff)) continue; Double st0; vector res; while (read_line(line, infile)) { for (uInt i=0; i= ncoeff) { res.resize(ncoeff); allcol.push_back(res); allmjd.push_back(st0); break; }; }; }; infile.clear(); infile.close(); /// cout << "sz: " << allmjd[0] << ":" << allmjd.size() << ":" << allcol.size() << ":" << allcol[0].size() << endl;;; // Create table Table *tab = create_table(inVal, tprop); // Fill table tab->keywordSet().get("MJD0", tprop.MJD0); if (tprop.MJD0 <= 0) { Int istepo(stepo); tprop.MJD0 = ((stdat-istepo)/incepo-1)*incepo + istepo; } tab->rwKeywordSet().define("MJD0", tprop.MJD0); tprop.dMJD = incepo; tab->rwKeywordSet().define("dMJD", tprop.dMJD); for (uInt i=0; irwKeywordSet().define(kwnames[i], kwval[i]); createColumns(tab, tprop); TableColumn tcd=TableColumn(*tab, "x"); tcd.rwKeywordSet().define("Rows", 3); tcd.rwKeywordSet().define("Columns", 13); tcd.rwKeywordSet().define("Description", pttA); /// // Data uInt row_nr = tab->nrow(); tab->addRow(allmjd.size()); for (uInt i=0; iputScalar(row_nr + i, allmjd[i]); Vector colA(allcol[i]); static_cast*>(tprop.columns[1])->put(row_nr + i, colA); }; rmColumns(tab,tprop); // Ready close_table(tprop.tnam, tab, 0.0001); // OK return True; } //*************************************************************************// // Fill JPL DE200 table Bool DE200(tableProperties &tprop, inputValues &inVal) { return JPLDE(tprop, inVal); } //*************************************************************************// // Fill JPL DE405 table Bool DE405(tableProperties &tprop, inputValues &inVal) { return JPLDE(tprop, inVal); } //*************************************************************************// // Main program //*************************************************************************// int main (int argc, const char** argv) { try { cout << " " << endl; cout << "Create data tables for Measures" << endl; cout << "-----------------------------------------------" << endl; // Make type lists makeMaps(); //*************************************************************************// // Program inputs //*************************************************************************// // Enable input in no-prompt mode Input inputs(1); // Define the input structure and version // Set defaults inVal = defVal; inVal.derange.resize(2); inVal.derange[0] = 1960; inVal.derange[1] = Time().year()+20; inputs.version(inVal.appVersion); inputs.create("type", inVal.type, "Type of table to create", "string"); inputs.create("dir", inVal.dir, "Path to data table base directory", "string"); inputs.create("refresh", boolToString(inVal.refresh), "Force refresh, even when not needed yet", "bool"); inputs.create("renew", boolToString(inVal.renew), "Force table renew when an update would suffice", "bool"); inputs.create("derange", blockIntToString(inVal.derange), "Range for JPL DE tables as yyyy,yyyy", "int"); inputs.create("in", inVal.in, "Hidden: name of input (ASCII) file", "string"); inputs.create("x__n", uIntToString(inVal.x__n), "Hidden: pointer into execution list", "int"); inputs.create("x__fn", boolToString(inVal.x__fn), "Hidden: filename should have been given", "bool"); inputs.create("x__rep", boolToString(inVal.x__rep), "Hidden: in repeating input cycle", "bool"); inputs.create("param", "", "Parameter values", "string"); inputs.create("x__val", "", "Parameter values", "string"); // Fill the input structure from the command line. inputs.readArguments(argc, argv); // Create output link file /// Path pout(inVal.ofile); RegularFile fout(pout); fout.remove(); if (!fout.canCreate()) { throw (AipsError("Cannot create in ./ the link file " + pout.absoluteName())); }; // Check the type inVal.intype = inputs.getString("type"); cout << "The requested type is: " << inVal.intype << endl; if ((inVal.fulltype = minimaxNC(inVal.intype, types)).empty()) { throw (AipsError("Unknown type requested")); }; if (multypes.count(inVal.fulltype)) inVal.types = multypes[inVal.fulltype]; cout << "The processed type[s]:"; for (uInt i=0; i= inVal.types.size()) { throw (AipsError("Program error: pointer outside processing list")); }; // Get DE range inVal.derange = inputs.getIntArray("derange"); if (inVal.derange.nelements() < 2 || inVal.derange[0]>=inVal.derange[1]) { throw (AipsError("Illegal DE table range specified")); }; if (String(inVal.types[inVal.x__n], 0, 2) == String("DE")) { cout << "The requested DE table range is " << inVal.derange[0] << "-" << inVal.derange[1] << endl; }; // Get and check the table directory specification inVal.dir = inputs.getString("dir"); if (inVal.dir == "") { throw (AipsError("The data table path must be given")); }; Path dirpath(inVal.dir); cout << "The data table directory: " << dirpath.absoluteName() << endl; if (!dirpath.isValid()) { throw (AipsError("The table directory path is not valid")); }; if (!File(dirpath).exists()) { throw (AipsError("The table directory path does not exist")); }; if (!File(dirpath).isWritable()) { throw (AipsError("The table directory path is not writable")); }; // Get and check the ASCII input file inVal.in = inputs.getString("in"); inVal.testOnly = True; Path inpath; if (inVal.in == "-") { cout << "Check and request mode only" << endl; } else { inVal.testOnly = False; inpath = Path(inVal.in); cout << "The input data file: " << inpath.absoluteName() << endl; if (!inpath.isValid()) { throw (AipsError("The input data file path is not valid")); }; if (!File(inpath).exists()) { throw (AipsError("The input data file does not exist")); }; if (!File(inpath).isReadable()) { throw (AipsError("The input data file is not readable")); }; }; // Get and check flags inVal.refresh = inputs.getBool("refresh"); if (!inVal.refresh) cout << "No f"; else cout << "F"; cout << "orced refresh asked" << endl; inVal.renew = inputs.getBool("renew"); if (!inVal.renew) cout << "No e"; else cout << "E"; cout << "xplicit renew asked" << endl; inVal.x__fn = inputs.getBool("x__fn"); inVal.x__rep = inputs.getBool("x__rep"); // Get extra parameters String val = inputs.getString("x__val"); if (val.empty()) val = inputs.getString("param"); vector out; if (split_data(out, val, Regex("[(]"))) { val = out[0]; if (split_data(out, val, Regex("[)]"))) { val = out[0]; if (split_data(out, val, Regex("[,]"))) { for (uInt i=0; i //
      • Measures: // Coordinate conversions // casacore-3.7.1/mirlib/000077500000000000000000000000001476623553700146025ustar00rootroot00000000000000casacore-3.7.1/mirlib/CMakeLists.txt000066400000000000000000000010211476623553700173340ustar00rootroot00000000000000# # CASA Mirlib # add_library ( casa_mirlib bug.c dio.c headio.c hio.c key.c maskio.c pack.c scrio.c uvio.c xyio.c xyzio.c ) target_link_libraries (casa_mirlib casa_casa ${CASACORE_ARCH_LIBS}) install ( TARGETS casa_mirlib RUNTIME DESTINATION bin LIBRARY DESTINATION lib${LIB_SUFFIX} ARCHIVE DESTINATION lib${LIB_SUFFIX} LIBRARY PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) install ( FILES hio.h io.h maxdimc.h miriad.h sysdep.h DESTINATION include/casacore/mirlib ) casacore-3.7.1/mirlib/README000066400000000000000000000043471476623553700154720ustar00rootroot00000000000000# README: README file for miriad library. # Copyright (C) 1993,1994,1995,1997,1999,2001 # Associated Universities, Inc. Washington DC, USA. # # This library is free software; you can redistribute it and/or modify it # under the terms of the GNU Library General Public License as published by # the Free Software Foundation; either version 2 of the License, or (at your # option) any later version. # # This library 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 Library General Public # License for more details. # # You should have received a copy of the GNU Library General Public License # along with this library; if not, write to the Free Software Foundation, # Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. # # Correspondence concerning AIPS++ should be addressed as follows: # Internet email: casa-feedback@nrao.edu. # Postal address: AIPS++ Project Office # National Radio Astronomy Observatory # 520 Edgemont Road # Charlottesville, VA 22903-2475 USA # # $Id$ This code is a subset of the MIRIAD I/O library. The MIRIAD package is a package developed for BIMA and actively being used by BIMA and ATNF for their offline calibration and data reduction needs. More details on the MIRIAD package can be found on: http://bima.astro.umd.edu/bima/miriad/ or http://www.atnf.csiro.au/computing/software/miriad/ Although the BIMA and ATNF versions use a slightly different directory structure (January 1997), the source code is essentially the same. You should be able to find all subroutines in $MIR/src/subs, except for miriad.h, which was specifically generated for the AIPS++ project. MIRIAD V3 (the BIMA version of MIRIAD) has been placed under CVS control, mirlib is a direct extraction of that release (June 2001). A new version of this library will be needed to deal with large (>2GB) files, which is to be extracted from the CARMA version of MIRIAD (2011). Most of the low level C code was developed by Bob Sault (rsault@atnf.csiro.au). Correspondence concerning MIRLIB should be directed to Peter Teuben (teuben@astro.umd.edu). casacore-3.7.1/mirlib/bug.c000066400000000000000000000326561476623553700155370ustar00rootroot00000000000000/************************************************************************/ /* */ /* This handles errors and can abort your program. */ /* */ /* History: */ /* rjs,mjs ???? Very mixed history. Created, destroyed, rewritten.*/ /* rjs 26aug93 Call habort_c. */ /* rjs 14jul98 Add a caste operation in errmsg_c, to attempt */ /* to appease some compilers. */ /* pjt 23sep01 darwin */ /* pjt 4dec01 bypass fatal errors (for alien clients) if req'd */ /* through the new bugrecover_c() routine */ /* pjt 17jun02 prototypes for MIR4 */ /* pjt/ram 5dec03 using strerror() for unix */ /* pjt 1jan05 bugv_c: finally, a real stdargs version!!! */ /* though cannot be exported to Fortran */ /* pjt 26mar07 bugmessage_c: retrieve last fatal bug message */ /* pjt 27mar07 bugseverity_c: also overhauled bug recovery */ /* and removed VMS specific code */ /* pjt 17may07 removed old-non ANSI declaration */ /* pjt 5dec07 add Name to bug output - why took us so long? */ /* pkgw 6mar08 declare Name as static to prevent symbol clashes */ /* dhem 12feb09 added hooks to allow alien clients to completely */ /* override the default bug handler */ /* pkgw 14dec11 Make errmsg_c public for use in uvio.c */ /************************************************************************/ #define X_OPEN_SOURCE 500 // For strdup definition #define _DEFAULT_SOURCE // For sys_nerr #if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "miriad.h" char *errmsg_c(int n); static int handle_bug_cleanup(int d, char s, Const char *m); static char *Name = NULL; /* a slot to store the program name */ int reentrant=0; /* keep track of state */ /* helper definitions for function pointers */ typedef void (*bug_cleanup_proc)(void); typedef void (*bug_handler_proc)(char s, Const char *m); /* external bug cleanup handler, if any */ /* only used by internal bug handler */ static bug_cleanup_proc bug_cleanup=NULL; /* bug handler function pointer */ static bug_handler_proc bug_handler=NULL; /* forward declaration */ static void default_bug_handler_c(char s,Const char *m); static char *bug_message=0; /* last message */ static char bug_severity=0; /* last severity level (i,w,e or f) */ #define MAXMSG 256 static char msg[MAXMSG]; /* formatted message for bugv_c() */ /************************************************************************/ char *bugmessage_c(void) /** bugmessage_c -- return last fatal error message string */ /*& pjt */ /*: error-handling */ /*+ This routine does not have a FORTRAN counterpart, as it is normally only called by C clients who have set their own error handler if for some reason they don't like the MIRIAD one (e.g. C++ or java exceptions, or NEMO's error handler. This way the bugrecover handler can call this routine to retrieve the last fatal error message. bugrecover_c(my_handler); void my_handler(void) { char *m = bugmessage_c(); printf("RECOVERED: %s\n",m); } .. */ /*-- */ /*----------------------------------------------------------------------*/ { return bug_message; } /************************************************************************/ char bugseverity_c(void) /** bugseverity_c -- return last severity level */ /*& pjt */ /*: error-handling */ /*+ This routine does not have a FORTRAN counterpart, as it is normally only called by C clients who have set their own error handler if for some reason they don't like the MIRIAD one (e.g. C++ or java exceptions, or NEMO's error handler. This way the bugrecover handler can call this routine to retrieve the last severity level bugrecover_c(my_handler); void my_handler(void) { char s = bugseverity_c(); char *m = bugmessage_c(); printf("RECOVERED: (%c) %s\n",s,m); if (s=='f') exit(1); } .. */ /*-- */ /*----------------------------------------------------------------------*/ { return bug_severity; } /************************************************************************/ void bughandler_c(bug_handler_proc new_bug_handler) /** bughandler_c -- specify the bug handler callback function */ /*& dhem */ /*: error-handling */ /*+ This routine does not have a FORTRAN counterpart, as it is normally only called by C clients who need to set their own error handler if for some reason they don't like the MIRIAD one (e.g. C++ or java exceptions, or NEMO's error handler, or scripting languages such as Ruby and Python that provide their own exception handling capabilities). Absolutely nothing is done before or after the bug handler is called. This is even more of an override than bugrecover_c provides because on fatal errors, habort_c is called before the bug cleanup routine installed by bugrecover_c is called. Another difference is that bug_message and bug_severity are not set; instead the severity and message are passed as parameters to the bug handler callback function.. If NULL is passed as the new_bug_handler parameter, the default bug handler will be reinstated. Example of usage: void my_bug_handler(char s, onst char *m) { .... } .. bughandler_c(my_bug_handler); .. */ /*-- */ /*----------------------------------------------------------------------*/ { bug_handler = new_bug_handler; if (bug_message) free(bug_message); bug_message = strdup("no bug_message has been set yet"); } /************************************************************************/ void bugrecover_c(void (*cl)(void)) /** bugrecover_c -- bypass fatal bug calls for alien clients */ /*& pjt */ /*: error-handling */ /*+ This routine does not have a FORTRAN counterpart, as it is normally only called by C clients who need to set their own error handler if for some reason they don't like the MIRIAD one (e.g. C++ or java exceptions, or NEMO's error handler Example of usage: void my_bug_cleanup(void) { .... } .. bugrecover_c(my_bug_cleanup); .. */ /*-- */ /*----------------------------------------------------------------------*/ { bug_cleanup = cl; if (bug_message) free(bug_message); bug_message = strdup("no bug_message has been set yet"); } /************************************************************************/ void buglabel_c(Const char *name) /** buglabel -- Give the "program name" to be used as a label in messages. */ /*& pjt */ /*: error-handling */ /*+ FORTRAN call sequence: subroutine buglabel(name) implicit none character name*(*) Give the name that is to be used as a label in error messages. Usually this is the program name and should be set by the user interface. Input: name The name to be given as a label in error messages. */ /*-- */ /*----------------------------------------------------------------------*/ { if(Name != NULL)free(Name); Name = malloc(strlen(name)+1); strcpy(Name,name); } /************************************************************************/ void bug_c(char s,Const char *m) /** bug -- Issue an error message, given by the caller. */ /*& pjt */ /*: error-handling */ /*+ FORTRAN call sequence: subroutine bug(severity,message) implicit none character severity*1 character message*(*) Output the error message given by the caller, and abort if needed. Input: severity Error severity. Can be one of 'i', 'w', 'e' or 'f' for "informational", "warning", "error", or "fatal" message The error message text. */ /*-- */ /*----------------------------------------------------------------------*/ { if(bug_handler == NULL) { bug_handler = default_bug_handler_c; } bug_handler(s, m); } /************************************************************************/ static void default_bug_handler_c(char s, Const char *m) /* Default bug handler. ------------------------------------------------------------------------*/ { char *p; int doabort; doabort = 0; if (s == 'i' || s == 'I') p = "Informational"; else if (s == 'w' || s == 'W') p = "Warning"; else if (s == 'e' || s == 'E') p = "Error"; else {doabort = 1; p = "Fatal Error"; } if (!bug_cleanup) { if ( Name == NULL ) buglabel_c("(NOT SET)"); fprintf(stderr,"### %s [%s]: %s\n",p,Name,m); } if(doabort){ reentrant = !reentrant; if(reentrant)habort_c(); if (!handle_bug_cleanup(doabort,s,m)) exit(1); } else handle_bug_cleanup(doabort,s,m); } /************************************************************************/ void bugv_c(char s,Const char *m, ...) /** bugv_c -- Issue a dynamic error message, given by the caller. */ /*& pjt */ /*: error-handling */ /*+ C call sequence: bugv_c(severity,message,....) Output the error message given by the caller, and abort if needed. Note this routine has no counterpart in FORTRAN. Input: severity Error severity character. Can be one of 'i', 'w', 'e' or 'f' for "informational", "warning", "error", or "fatal" message The error message string, can contain %-printf style directives, as used by the following arguments. ... Optional argument, in the printf() style */ /*-- */ /*----------------------------------------------------------------------*/ { va_list ap; va_start(ap,m); vsnprintf(msg,MAXMSG,m,ap); msg[MAXMSG-1] = '\0'; /* backstop */ va_end(ap); bug_c(s, msg); } /************************************************************************/ void bugno_c(char s,int n) /** bugno -- Issue an error message, given a system error number. */ /*& pjt */ /*: error-handling */ /*+ FORTRAN call sequence: subroutine bugno(severity,errno) implicit none character severity*1 integer errno Output the error message associated with a particular error number. Input: severity Error severity. Can be one of 'i', 'w', 'e' or 'f' for "informational", "warning", "error", or "fatal" errno host error number. */ /*-- */ /*----------------------------------------------------------------------*/ { if (n == -1)bug_c(s,"End of file detected"); else bug_c(s,errmsg_c(n)); } /************************************************************************/ char *errmsg_c(int n) /* Return the error message associated with some error number. ------------------------------------------------------------------------*/ { /* check for linux leaves this compat with old style build * this should be removed in favor of HAVE_STRERROR once * is only supported using autotools/configure */ #if defined(linux) || (defined(HAVE_STRERROR) && HAVE_STRERROR) /* new POSIX.1 style, 20 years old now... (1988) */ if(n == -1) return "End of file detected"; return strerror(n); #else /* very old style code -- stdio.h is supposed to supply this */ # if 0 extern int sys_nerr; extern char *sys_errlist[]; # endif if(n > 0 && n <= sys_nerr)return((char *)sys_errlist[n]); else { sprintf(msg,"Unknown error with number %d detected.",n); return msg; } #endif } /************************************************************************/ static int handle_bug_cleanup(int doabort, char s, Const char *m) /* Handle cleaning up a bug ------------------------------------------------------------------------*/ { if (bug_cleanup) { if (bug_message) free(bug_message); bug_message = strdup(m); /* save last message */ bug_severity = s; /* save last severity */ (*bug_cleanup)(); /* call handler ; this may exit */ if (doabort) /* if it got here, handler didn't exit */ fprintf(stderr,"### handle_bug_cleanup: WARNING: code should not come here\n"); return 1; } return 0; } casacore-3.7.1/mirlib/dio.c000066400000000000000000000247151476623553700155320ustar00rootroot00000000000000/************************************************************************/ /* DIO -- Disk I/O routines for a Unix Enviromment. */ /* */ /* Makes calls to the UNIX I/O and directory searching routines. */ /* All of these get implemented in a pretty straight forward way. */ /* */ /* Portability Notes: */ /* These routines are intended to run on BSD UNIX and UNICOS. No */ /* attempt has been made to make them any more portable than this. */ /* There are some minor differences between the two, which are */ /* selectively compiled depending if BSD is defined. */ /* 1. The mkdir system service is not present on some systems, and */ /* may require superuser priveleges to implement using mknod. */ /* In this case, use 'popen("mkdir ...","r",...)' */ /* 2. The Berkeley directory searching routines are used. These */ /* can be relatively simply implemented in other UNIX's. */ /* */ /* History: */ /* dakr-ages rjs Original version adapted from werong. */ /* 31-oct-89 pjt _trace_ added as defined() option, errno */ /* -nov-89 rjs dexpand_c routine */ /* 6-dec-89 pjt extended bug call */ /* 26-jan-90 rjs Reincluded , which is needed by Unicos. */ /* 27-apr-90 rjs Added ddelete_c routine. */ /* 26-aug-93 rjs Added hrmdir. */ /* 5-nov-94 rjs Improve POSIX compliance. */ /* 26-Oct-95 rjs Honour TMPDIR environment variable, if set. */ /* 10-Jan-96 rjs Make sure scratch file names are unique. */ /* 17-jun-02 pjt MIR4 changes, and proper prototypes */ /* 5-nov-04 jwr Changed a few size_t to ssize_t or off_t */ /* 3-jan-05 pjt ssize casting to appease the compiler */ /* use SSIZE_MAX to protect from bad casting ? */ /* 2-mar-05 pjt template->templat for C++, just in case */ /* 02-dec-11 pkgw Fix semantics of I/O syscalls in dread, dwrite */ /************************************************************************/ #if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #define direct dirent #include #include #include "miriad.h" #define MAXPATH 128 #ifndef NULL # define NULL 0 #endif #define Malloc(x) malloc((unsigned)(x)) #define Strcat (void)strcat #define Strcpy (void)strcpy #define Lseek(a,b,c) (off_t)lseek(a,(off_t)(b),c) struct dent { char path[MAXPATH]; DIR *dir; }; /************************************************************************/ void ddelete_c(char *path,int *iostat) /* This deletes a file, and returns an i/o status. ------------------------------------------------------------------------*/ { *iostat = ( unlink(path) ? errno : 0 ); } /************************************************************************/ void dtrans_c(char *inpath,char *outpath,int *iostat) /* Translate a directory spec into the local format. On a UNIX machine, this merely involves adding a slash to the end of the name. Input: inpath Input directory spec. Output: outpath Output directory spec. iostat Error return. ------------------------------------------------------------------------*/ { char *s; *iostat = 0; Strcpy(outpath,inpath); s = outpath + strlen(outpath) - 1; if(*s != '/')Strcat(outpath,"/"); } /************************************************************************/ void dmkdir_c(char *path,int *iostat) /* Create a directory. This might be a privileged operation on some systems, in which case dmkdir_c will have to work by using popen(3) and mkdir(1). Input: path Name of directory to create. This will usually have a trailing slash, which needs to be trimmed off. Output: iostat Errror status. ------------------------------------------------------------------------*/ { char Path[MAXPATH],*s; /* Usually the path will end in a '/', so get rid of it. */ Strcpy(Path,path); s = Path + strlen(Path) - 1; if(*s == '/')*s = 0; *iostat = 0; if(mkdir(Path,0777) < 0) *iostat = errno; } /************************************************************************/ void drmdir_c(char *path,int *iostat) /* Delete a directory. This might be a privileged operation on some systems, in which case drmdir_c will have to work by using popen(3) and rmdir(1). Input: path Name of directory to remove. This will usually have a trailing slash, which needs to be trimmed off. Output: iostat Errror status. ------------------------------------------------------------------------*/ { char Path[MAXPATH],*s; /* Usually the path will end in a '/', so get rid of it. */ Strcpy(Path,path); s = Path + strlen(Path) - 1; if(*s == '/')*s = 0; *iostat = 0; if(rmdir(Path) < 0) *iostat = errno; } /************************************************************************/ void dopen_c(int *fd,char *name,char *status,off_t *size,int *iostat) /* Open a file. Input: name Name of file to create (in host format). status Either "read", "write", "append" or "scratch". "scratch" files are using $TMPDIR, if present, else current. Output: fd File descriptor. size Size of file. iostat I/O status. ------------------------------------------------------------------------*/ { int is_scratch,pid,flags=0; char *s,sname[MAXPATH]; is_scratch = *iostat = 0; s = name; if (!strcmp(status,"read")) flags = O_RDONLY; else if(!strcmp(status,"write")) flags = O_CREAT|O_TRUNC|O_RDWR; else if(!strcmp(status,"append")) flags = O_CREAT|O_RDWR; else if(!strcmp(status,"scratch")){ flags = O_CREAT|O_TRUNC|O_RDWR; is_scratch = 1; s = getenv("TMPDIR"); pid = getpid(); if(s != NULL)sprintf(sname,"%s/%s.%d",s,name,pid); else sprintf(sname,"%s.%d",name,pid); s = sname; } else bug_c('f',"dopen_c: Unrecognised status"); #ifdef O_LARGEFILE flags |= O_LARGEFILE; #endif if((*fd = open(s,flags,0644)) < 0){*iostat = errno; return;} *size = Lseek(*fd,0,SEEK_END); /* If its a scratch file, unlink it now, so that the file will disappear when it is closed (or this program crashes). */ if(is_scratch)(void)unlink(s); } /************************************************************************/ void dclose_c(int fd,int *iostat) /* This subroutine does unbelievably complex stuff. ------------------------------------------------------------------------*/ { *iostat = ( close(fd) < 0 ? errno : 0 ); } /************************************************************************/ void dread_c(int fd, char *buffer,off_t offset,size_t length,int *iostat) /* Read from a file. ------------------------------------------------------------------------*/ { ssize_t nread; #ifdef debug if (length >= SSIZE_MAX) bugv_c('f',"dread_c: possible incomplete read"); #endif if(Lseek(fd,offset,SEEK_SET) < 0) { *iostat = errno; return; } while (length) { nread = read(fd,buffer,length); if(nread < 0) { if(errno == EINTR) nread = 0; /* should reattempt the system call identically */ else { *iostat = errno; return; } } else if(nread == 0) { /* unexpected EOF -- no good errno code for this */ *iostat = EIO; return; } length -= nread; } } /************************************************************************/ void dwrite_c(int fd, char *buffer,off_t offset,size_t length,int *iostat) /* Write to a file. ------------------------------------------------------------------------*/ { ssize_t nwrite; #ifdef debug if (length >= SSIZE_MAX) bugv_c('f',"dwrite_c: possible incomplete write"); #endif if(Lseek(fd,offset,SEEK_SET) < 0) { *iostat = errno; return; } while (length) { nwrite = write(fd,buffer,length); if(nwrite < 0) { if(errno == EINTR) nwrite = 0; /* should reattempt the system call identically */ else { *iostat = errno; return; } } length -= nwrite; } } /************************************************************************/ /*ARGSUSED*/ void dwait_c(int fd,int *iostat) /* This nominally waits for i/o to a file to finish. Things work synchronously in UNIX. ------------------------------------------------------------------------*/ { *iostat = 0; } /************************************************************************/ int dexpand_c(char *templat,char *output,int length) /* This expands wildcards, matching them with files. Input: templat The input character string, containing the wildcards. length The length of the output buffer. Output: output All the files matching "template". Filenames are separated by commas. ------------------------------------------------------------------------*/ { FILE *fd; char line[MAXPATH],*s; int l; Strcpy(line,"echo "); Strcat(line,templat); fd = popen(line,"r"); if(fd == NULL) return(-1); s = output; while(fgets(s,length,fd)){ l = strlen(s); if( length-l <= 1 ){(void)pclose(fd); return(-1);} *(s+l-1) = ','; s += l; length -= l; } if(s != output) *--s = 0; (void)pclose(fd); return(s-output); } /************************************************************************/ void dopendir_c(char **contxt,char *path) /* Open a directory, and prepare to read from it. ------------------------------------------------------------------------*/ { struct dent *d; *contxt = Malloc(sizeof(struct dent)); d = (struct dent *)*contxt; Strcpy(d->path,path); d->dir = opendir(path); } /************************************************************************/ void dclosedir_c(char *contxt) /* Close a directory. ------------------------------------------------------------------------*/ { struct dent *d; d = (struct dent *)contxt; (void)closedir(d->dir); free(contxt); } /************************************************************************/ /*ARGSUSED*/ void dreaddir_c(char *contxt,char *path,int length) /* Read a directory entry. ------------------------------------------------------------------------*/ { struct dent *d; struct direct *dp; struct stat buf; char npath[MAXPATH]; d = (struct dent *)contxt; do dp = readdir(d->dir); while(dp != NULL && (!strcmp(dp->d_name,".") || !strcmp(dp->d_name,".."))); if(dp == NULL) *path = 0; else{ Strcpy(path,dp->d_name); Strcpy(npath,d->path); Strcat(npath,path); (void)stat(npath,&buf); if(S_ISDIR(buf.st_mode)) { Strcat(path,"/"); } } } casacore-3.7.1/mirlib/headio.c000066400000000000000000000647761476623553700162230ustar00rootroot00000000000000/************************************************************************/ /* */ /* Routines to access "header" variables. */ /* */ /*-- */ /* History: */ /* rjs Dark_ages Original version */ /* rjs 23aug89 Fixed char variable overrun problem, in hdprobe. */ /* rjs 12feb90 Added some comments, to appease PJT. */ /* rjs 21feb90 Corrected a comment. */ /* rjs 7mar90 Added hisopen with status='write' */ /* rjs 27apr90 Fixed bug in hdprobe, which got the lengths of items */ /* less than ITEM_HDR_SIZE long wrong. */ /* pjt 19mar91 output double prec variables in -20.10g */ /* rjs 26aug92 Corrected hdprsnt declaration, and the value that */ /* it returns. */ /* rjs 23feb93 Rename a defined parameter only. */ /* rjs 10aug93 Use hexists in hdprsnt. */ /* rjs 6nov94 Change "item handle" to an integer. */ /* rjs 15may96 Fiddles with roundup macro. */ /* pjt 27mar99 make history a static, so nobody can see it :-) */ /* rjs 29apr99 Get hdprobe to check for string buffer overflow. */ /* dpr 11may01 Descriptive error for hisopen_c */ /* pjt 22jun02 MIR4 prototypes and using int8 for long integers */ /* pjt/rjs 1jan05 replaced shortcut rdhdd code with their own readers */ /* this fixes a serious bug in rdhdl for large values */ /* Also adding in some bugv_c() called to replace bug_c */ /* pjt 12jan05 Fixed up type conversion for int8's in rhhdl */ /* pjt 6feb05 rdhdd_c() : no more type check (see comment in code) */ /* pjt 17feb05 fixed bug in reading int8's from old MIR3 files */ /* pjt 6sep06 read integers via rdhdi */ /************************************************************************/ #if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "miriad.h" #include "io.h" #define check(iostat) if(iostat)bugno_c('f',iostat) #define MAXSIZE 1024 #define MAXLINE 80 static int history[MAXOPEN]; #define Sprintf (void)sprintf #define Strcpy (void)strcpy /************************************************************************/ void hisopen_c(int tno,Const char *status) /** hisopen -- Open the history file. */ /*& pjt */ /*: header-i/o */ /*+ FORTRAN call sequence: subroutine hisopen(tno,status) integer tno character status This opens the history file, and readies it to be read or written. Inputs: tno The handle of the open data set. status Either "read", "write" or "append". */ /*-- */ /*----------------------------------------------------------------------*/ { int iostat; haccess_c(tno,&history[tno],"history",status,&iostat); if(iostat) {bug_c('e',"Problem with history item");}; check(iostat); } /************************************************************************/ void hiswrite_c(int tno,Const char *text) /** hiswrite -- Write a line of text to the history file. */ /*& pjt */ /*: header-i/o */ /*+ FORTRAN call sequence: subroutine hiswrite(tno,line) integer tno character line*(*) This writes a text string to the history file associated with an open data set. Inputs: tno The handle of the open data set. line The string of text to be written to the history file. */ /*-- */ /*----------------------------------------------------------------------*/ { int iostat; hwritea_c(history[tno],text,strlen(text)+1,&iostat); check(iostat); } /************************************************************************/ void hisread_c(int tno,char *text,size_t length,int *eof) /** hisread -- Read a line of text from the history file. */ /*& pjt */ /*: header-i/o */ /*+ FORTRAN call sequence: subroutine hisread(tno,line,eof) integer tno character line*(*) logical eof This reads a line of text from the history file. Input: tno The handle of the input data set. Output: line The string to receive the read string. eof This logical variable turns true when the end of the history file is reached. */ /*-- */ /*----------------------------------------------------------------------*/ { int iostat; hreada_c(history[tno],text,length,&iostat); if(iostat == 0) *eof = FORT_FALSE; else if(iostat == -1) *eof = FORT_TRUE; else bugno_c('f',iostat); } /************************************************************************/ void hisclose_c(int tno) /** hisclose -- This closes the history file. */ /*& pjt */ /*: header-i/o */ /*+ FORTRAN call sequence: subroutine hisclose(tno integer tno This closes the history file associated with a particular data set. Input: tno The handle of the data set. */ /*-- */ /*----------------------------------------------------------------------*/ { int iostat; hdaccess_c(history[tno],&iostat); check(iostat); } /************************************************************************/ void wrhdr_c(int thandle,Const char *keyword,double value) /** wrhdr -- Write a real valued header variable. */ /*& pjt */ /*: header-i/o */ /*+ FORTRAN call sequence: subroutine wrhdr(tno,keyword,value) integer tno character keyword*(*) real value This writes a real-valued header keyword. Input: tno Handle of the data set. keyword Name of the keyword to write. value The value of the keyword. */ /*-- */ /*----------------------------------------------------------------------*/ { int item; float temp; int iostat,offset; temp = value; haccess_c(thandle,&item,keyword,"write",&iostat); check(iostat); hwriteb_c(item,real_item,0,ITEM_HDR_SIZE,&iostat); check(iostat); offset = mroundup(ITEM_HDR_SIZE,H_REAL_SIZE); hwriter_c(item,&temp,offset,H_REAL_SIZE,&iostat); check(iostat); hdaccess_c(item,&iostat); check(iostat); } /************************************************************************/ void wrhdd_c(int thandle,Const char *keyword,double value) /** wrhdd -- Write a double precision valued header variable. */ /*& mjs */ /*: header-i/o */ /*+ FORTRAN call sequence: subroutine wrhdd(tno,keyword,value) integer tno character keyword*(*) double precision value Write the value of a header variable which has a double precision value. Input: tno The handle of the data set. keyword Name to the keyword. value The double precision value. */ /*-- */ /*----------------------------------------------------------------------*/ { int item; int iostat,offset; haccess_c(thandle,&item,keyword,"write",&iostat); check(iostat); hwriteb_c(item,dble_item,0,ITEM_HDR_SIZE,&iostat); check(iostat); offset = mroundup(ITEM_HDR_SIZE,H_DBLE_SIZE); hwrited_c(item,&value,offset,H_DBLE_SIZE,&iostat); check(iostat); hdaccess_c(item,&iostat); check(iostat); } /************************************************************************/ void wrhdi_c(int thandle,Const char *keyword,int value) /** wrhdi -- Write an integer valued header variable. */ /*& mjs */ /*: header-i/o */ /*+ FORTRAN call sequence: subroutine wrhdi(tno,keyword,value) integer tno character keyword*(*) integer value Write an integer valued header variable. Input: tno The handle of the data set. keyword The name of the header variable. value The integer value of the header variable. */ /*-- */ /*----------------------------------------------------------------------*/ { int item; int iostat,offset; haccess_c(thandle,&item,keyword,"write",&iostat); check(iostat); hwriteb_c(item,int_item,0,ITEM_HDR_SIZE,&iostat); check(iostat); offset = mroundup(ITEM_HDR_SIZE,H_INT_SIZE); hwritei_c(item,&value,offset,H_INT_SIZE,&iostat); check(iostat); hdaccess_c(item,&iostat); check(iostat); } /************************************************************************/ void wrhdl_c(int thandle,Const char *keyword,int8 value) /** wrhdl -- Write an integer*8 valued header variable. */ /*& pjt */ /*: header-i/o */ /*+ FORTRAN call sequence: subroutine wrhdl(tno,keyword,value) integer tno character keyword*(*) integer*8 value Write an integer*8 valued header variable. This is only supported on compilers that know how to handle integer*8 (e.g. gnu, intel). Without this support, some files in miriad will be limited to 8 GB. Input: tno The handle of the data set. keyword The name of the header variable. value The integer*8 value of the header variable. */ /*-- */ /*----------------------------------------------------------------------*/ { int item; int iostat,offset; /* Sault proposes to write an INT if below 2^31, else INT8 */ haccess_c(thandle,&item,keyword,"write",&iostat); check(iostat); hwriteb_c(item,int8_item,0,ITEM_HDR_SIZE,&iostat); check(iostat); offset = mroundup(ITEM_HDR_SIZE,H_INT8_SIZE); hwritel_c(item,&value,offset,H_INT8_SIZE,&iostat); check(iostat); hdaccess_c(item,&iostat); check(iostat); } /************************************************************************/ void wrhdc_c(int thandle,Const char *keyword,Const float *value) /** wrhdc -- Write a complex-valued header variable. */ /*& mjs */ /*: header-i/o */ /*+ FORTRAN call sequence: subroutine wrhdc(tno,keyword,value) integer tno character keyword*(*) complex value Write a complex valued header variable. Input: tno The file handle fo the data set. keyword The name of the header variable. value The complex value of the header variable. */ /*-- */ /*----------------------------------------------------------------------*/ { int item; int iostat,offset; haccess_c(thandle,&item,keyword,"write",&iostat); check(iostat); hwriteb_c(item,cmplx_item,0,ITEM_HDR_SIZE,&iostat); check(iostat); offset = mroundup(ITEM_HDR_SIZE,H_CMPLX_SIZE); hwritec_c(item,value,offset,H_CMPLX_SIZE,&iostat); check(iostat); hdaccess_c(item,&iostat); check(iostat); } /************************************************************************/ void wrhda_c(int thandle,Const char *keyword,Const char *value) /** wrhda -- Write a string-valued header variable. */ /*& mjs */ /*: header-i/o */ /*+ FORTRAN call sequence: subroutine wrhda(tno,keyword,value) integer tno character keyword*(*) character value*(*) Write a string valued header variable. Input: tno The file handle of the data set. keyword The name of the header variable. value The value of the header variable. */ /*-- */ /*----------------------------------------------------------------------*/ { int item; int iostat; haccess_c(thandle,&item,keyword,"write",&iostat); check(iostat); hwriteb_c(item,char_item,0,ITEM_HDR_SIZE,&iostat); check(iostat); hwriteb_c(item,(char *)value,ITEM_HDR_SIZE, strlen(value),&iostat); check(iostat); hdaccess_c(item,&iostat); check(iostat); } /************************************************************************/ void rdhdr_c(int thandle,Const char *keyword,float *value,double defval) /** rdhdr -- Read a real-valued header variable. */ /*& mjs */ /*: header-i/o */ /*+ FORTRAN call sequence: subroutine rdhdr(tno,keyword,value,default) integer tno character keyword*(*) real value,default Read a real valued header variable. Input: tno The file handle of the data set. keyword The name of the header variable. default The default value to return, if the header variable is not found. Output: value The value of the header variable. This will be the default value, if the variable is missing from the header. */ /*-- */ /*----------------------------------------------------------------------*/ { double dvalue,ddefval; ddefval = defval; rdhdd_c(thandle,keyword,&dvalue,ddefval); *value = dvalue; } /************************************************************************/ void rdhdi_c(int thandle,Const char *keyword,int *value,int defval) /** rdhdi -- Read an integer-valued header variable. */ /*& mjs */ /*: header-i/o */ /*+ FORTRAN call sequence: subroutine rdhdi(tno,keyword,value,default) integer tno character keyword*(*) integer value,default Read an integer valued header variable. Input: tno The file handle of the data set. keyword The name of the header variable. default The default value to return, if the header variable is not found. Output: value The value of the header variable. This will be the default value, if the variable is missing from the header. */ /*-- */ /*----------------------------------------------------------------------*/ { int8 lvalue,ldefval; ldefval = defval; rdhdl_c(thandle,keyword,&lvalue,ldefval); if(lvalue > 0x7FFFFFFF) bugv_c('f',"Item %s too large for rdhdi: %ld",keyword,lvalue); *value = lvalue; } /************************************************************************/ void rdhdl_c(int thandle,Const char *keyword,int8 *value,int8 defval) /** rdhdl -- Read an integer*8-valued header variable. */ /*& mjs */ /*: header-i/o */ /*+ FORTRAN call sequence: subroutine rdhdl(tno,keyword,value,default) integer tno character keyword*(*) integer*8 value,default Read an integer*8 valued header variable. Only supported on some compilers. See comments in wrhdl Input: tno The file handle of the data set. keyword The name of the header variable. default The default value to return, if the header variable is not found. Output: value The value of the header variable. This will be the default value, if the variable is missing from the header. */ /*-- */ /*----------------------------------------------------------------------*/ { int item; char s[ITEM_HDR_SIZE]; int iostat,length,offset,itemp; /* Firstly assume the variable is missing. Try to get it. If successful read it. */ *value = defval; haccess_c(thandle,&item,keyword,"read",&iostat); if(iostat)return; length = hsize_c(item); if(length >= 0){ /* Determine the type of the value, and convert it to double precision. */ hreadb_c(item,s,0,ITEM_HDR_SIZE,&iostat); check(iostat); iostat = 0; if( !memcmp(s,int8_item, ITEM_HDR_SIZE)){ offset = mroundup(ITEM_HDR_SIZE, H_INT8_SIZE); if(offset + H_INT8_SIZE == length) hreadl_c(item,value,offset,H_INT8_SIZE,&iostat); } else if ( !memcmp(s,int_item, ITEM_HDR_SIZE)){ /* this is to cover old style MIR3 files that were using int4's */ offset = mroundup(ITEM_HDR_SIZE, H_INT_SIZE); if(offset + H_INT_SIZE == length) { hreadi_c(item,&itemp,offset,H_INT_SIZE,&iostat); *value = itemp; } } else bugv_c('f',"rdhdl_c: item %s not an int8 or small enough int4",keyword); check(iostat); } hdaccess_c(item,&iostat); check(iostat); } /************************************************************************/ void rdhdd_c(int thandle,Const char *keyword,double *value,double defval) /** rdhdd -- Read a double precision-valued header variable. */ /*& mjs */ /*: header-i/o */ /*+ FORTRAN call sequence: subroutine rdhdd(tno,keyword,value,default) integer tno character keyword*(*) double precision value,default Read a double precision valued header variable. Input: tno The file handle of the data set. keyword The name of the header variable. default The default value to return, if the header variable is not found. Output: value The value of the header variable. This will be the default value, if the variable is missing from the header. */ /*-- */ /*----------------------------------------------------------------------*/ { int item; char s[ITEM_HDR_SIZE]; int iostat,length,itemp,offset; float rtemp; /* Firstly assume the variable is missing. Try to get it. If successful read it. */ *value = defval; haccess_c(thandle,&item,keyword,"read",&iostat); if(iostat)return; length = hsize_c(item); if(length >= 0){ /* Determine the type of the value, and convert it to double precision. */ hreadb_c(item,s,0,ITEM_HDR_SIZE,&iostat); check(iostat); iostat = 0; if( !memcmp(s,int_item, ITEM_HDR_SIZE)){ offset = mroundup(ITEM_HDR_SIZE,H_INT_SIZE); if(offset + H_INT_SIZE == length){ hreadi_c(item,&itemp,offset,H_INT_SIZE,&iostat); *value = itemp; } } else if(!memcmp(s,real_item,ITEM_HDR_SIZE)){ offset = mroundup(ITEM_HDR_SIZE,H_REAL_SIZE); if(offset + H_REAL_SIZE == length){ hreadr_c(item,&rtemp,offset,H_REAL_SIZE,&iostat); *value = rtemp; } } else if(!memcmp(s,dble_item,ITEM_HDR_SIZE)){ offset = mroundup(ITEM_HDR_SIZE,H_DBLE_SIZE); if(offset + H_DBLE_SIZE == length){ hreadd_c(item,value, offset,H_DBLE_SIZE,&iostat); } } #if 0 /* can't do this: some routines, e.g. imhead, actually depend * on it falling through. Sick, but true */ else bugv_c('f',"rdhdd_c: keyword %s not covered here",keyword); #endif check(iostat); } hdaccess_c(item,&iostat); check(iostat); } /************************************************************************/ void rdhdc_c(int thandle,Const char *keyword,float *value,Const float *defval) /** rdhdc -- Read a complex-valued header variable. */ /*& mjs */ /*: header-i/o */ /*+ FORTRAN call sequence: subroutine rdhdc(tno,keyword,value,default) integer tno character keyword*(*) complex value,default Read a complex valued header variable. Input: tno The file handle of the data set. keyword The name of the header variable. default The default value to return, if the header variable is not found. Output: value The value of the header variable. This will be the default value, if the variable is missing from the header. */ /*-- */ /*----------------------------------------------------------------------*/ { int item; char s[ITEM_HDR_SIZE]; int iostat,length,offset; /* Firstly assume the variable is missing. Try to get it. If successful read it. */ *value = *defval; *(value+1) = *(defval+1); haccess_c(thandle,&item,keyword,"read",&iostat); if(iostat)return; offset = mroundup(ITEM_HDR_SIZE,H_CMPLX_SIZE); length = hsize_c(item) - offset; if(length == H_CMPLX_SIZE){ hreadb_c(item,s,0,ITEM_HDR_SIZE,&iostat); check(iostat); iostat = 0; if(!memcmp(s,cmplx_item, ITEM_HDR_SIZE)){ hreadc_c(item,value,offset,H_CMPLX_SIZE,&iostat); } check(iostat); } hdaccess_c(item,&iostat); check(iostat); } /************************************************************************/ void rdhda_c(int thandle,Const char *keyword,char *value,Const char *defval,int len) /** rdhda -- Read a string-valued header variable. */ /*& mjs */ /*: header-i/o */ /*+ FORTRAN call sequence: subroutine rdhda(tno,keyword,value,default) integer tno character keyword*(*) character value*(*),default*(*) Read a string valued header variable. Input: tno The file handle of the data set. keyword The name of the header variable. default The default value to return, if the header variable is not found. Output: value The value of the header variable. This will be the default value, if the variable is missing from the header. */ /*-- */ /*----------------------------------------------------------------------*/ { int item; char s[ITEM_HDR_SIZE]; int iostat,dodef,length=0; /* Firstly assume the variable is missing. Try to get it. If successful read it. */ dodef = TRUE; haccess_c(thandle,&item,keyword,"read",&iostat); if(! iostat) { length = min( hsize_c(item) - ITEM_HDR_SIZE, len - 1); if(length > 0) { hreadb_c(item,s,0,ITEM_HDR_SIZE,&iostat); check(iostat); if(!memcmp(s,char_item,ITEM_HDR_SIZE)){ hreadb_c(item,value,ITEM_HDR_SIZE,length,&iostat); check(iostat); dodef = FALSE; } } hdaccess_c(item,&iostat); check(iostat); } if( dodef ) { length = min((int)strlen(defval),len-1); memcpy(value,defval,length); } *(value+length) = 0; } /************************************************************************/ void hdcopy_c(int tin,int tout,Const char *keyword) /** hdcopy -- Copy a headfer variable from one data set to another. */ /*& mjs */ /*: header-i/o */ /*+ FORTRAN call sequence: subroutine hdcopy(tin,tout,keyword) integer tin,tout character keyword*(*) Copy a header item from one data set to another. Input: tin File handle of the input data set. tout File handle of the output data set. keyword Name of the header variable to be copied. */ /*-- */ /*----------------------------------------------------------------------*/ { char buf[MAXSIZE]; int item_in,item_out; int length,offset,iostat,size; haccess_c(tin,&item_in,keyword,"read",&iostat); if(iostat)return; haccess_c(tout,&item_out,keyword,"write",&iostat); check(iostat); size = hsize_c(item_in); offset = 0; while(offset < size){ length = min(size - offset, (int)sizeof(buf)); hreadb_c(item_in,buf,offset,length,&iostat); check(iostat); hwriteb_c(item_out,buf,offset,length,&iostat); check(iostat); offset += length; } hdaccess_c(item_in,&iostat); check(iostat); hdaccess_c(item_out,&iostat); check(iostat); } /************************************************************************/ int hdprsnt_c(int tno,Const char *keyword) /** hdprsnt -- Determine if a header variable is present. */ /*& mjs */ /*: header-i/o */ /*+ FORTRAN call sequence: logical function hdprsnt(tno,keyword) integer tno character keyword*(*) Check if a particular header variable is present in a data set. Input: tno File handle of the data set to check. keyword Name of the header variable to check for. */ /*-- */ /*----------------------------------------------------------------------*/ { if(hexists_c(tno,keyword))return(FORT_TRUE); else return(FORT_FALSE); } /************************************************************************/ void hdprobe_c(int tno,Const char *keyword,char *descr,size_t length,char *type,int *n) /** hdprobe -- Determine characteristics of a header variable. */ /*& mjs */ /*: header-i/o */ /*+ FORTRAN call sequence: subroutine hdprobe(tno,keyword,descr,type,n) integer tno character keyword*(*),descr*(*),type*(*) integer n Determine characteristics of a particular header variable. Inputs: tno Handle of the data set. keyword Name of the header variable to probe. Outputs: descr A formatted version of the item. For single numerics or short strings, this is the ascii encoding of the value. For large items, this is some message describing the item. type One of: 'nonexistent' 'integer*2' 'integer*8' 'integer' 'real' 'double' 'complex' 'character' 'text' 'binary' n Number of elements in the item. Zero implies an error. One implies that "descr" is the ascii encoding of the value. */ /*-- */ /*----------------------------------------------------------------------*/ { int item; char s[ITEM_HDR_SIZE],buf[MAXSIZE]; float rtemp,ctemp[2]; int iostat,unknown,size,i,itemp,offset,bufit; double dtemp; int2 jtemp; int8 ltemp; haccess_c(tno,&item,keyword,"read",&iostat); *n = 0; bufit = 0; Strcpy(type,"nonexistent"); if(iostat)return; size = hsize_c(item); unknown = FALSE; if(size <= ITEM_HDR_SIZE){ unknown = TRUE; size -= ITEM_HDR_SIZE; } else { hreadb_c(item,s,0,ITEM_HDR_SIZE,&iostat); check(iostat); if(!memcmp(s,real_item,ITEM_HDR_SIZE)){ offset = mroundup(ITEM_HDR_SIZE,H_REAL_SIZE); size -= offset; Strcpy(type,"real"); *n = size / H_REAL_SIZE; if(size % H_REAL_SIZE) unknown = TRUE; else if(size == H_REAL_SIZE){ hreadr_c(item,&rtemp,offset,H_REAL_SIZE,&iostat); check(iostat); Sprintf(buf,"%-14.7g",rtemp); bufit = 1; } } else if(!memcmp(s,int_item,ITEM_HDR_SIZE)){ offset = mroundup(ITEM_HDR_SIZE,H_INT_SIZE); size -= offset; Strcpy(type,"integer"); *n = size / H_INT_SIZE; if(size % H_INT_SIZE) unknown = TRUE; else if(size == H_INT_SIZE){ hreadi_c(item,&itemp,offset,H_INT_SIZE,&iostat); check(iostat); Sprintf(buf,"%d",itemp); bufit = 1; } } else if(!memcmp(s,int2_item,ITEM_HDR_SIZE)){ offset = mroundup(ITEM_HDR_SIZE,H_INT2_SIZE); size -= offset; Strcpy(type,"integer*2"); *n = size / H_INT2_SIZE; if(size % H_INT2_SIZE) unknown = TRUE; else if(size == H_INT2_SIZE){ hreadj_c(item,&jtemp,offset,H_INT2_SIZE,&iostat); check(iostat); Sprintf(buf,"%d",jtemp); bufit = 1; } } else if(!memcmp(s,int8_item,ITEM_HDR_SIZE)){ offset = mroundup(ITEM_HDR_SIZE,H_INT8_SIZE); size -= offset; Strcpy(type,"integer*8"); *n = size / H_INT8_SIZE; if(size % H_INT8_SIZE) unknown = TRUE; else if(size == H_INT8_SIZE){ hreadl_c(item,<emp,offset,H_INT8_SIZE,&iostat); check(iostat); Sprintf(buf,"%lld",ltemp); bufit = 1; } } else if(!memcmp(s,dble_item,ITEM_HDR_SIZE)){ offset = mroundup(ITEM_HDR_SIZE,H_DBLE_SIZE); size -= offset; Strcpy(type,"double"); *n = size / H_DBLE_SIZE; if(size % H_DBLE_SIZE) unknown = TRUE; else if(size == H_DBLE_SIZE){ hreadd_c(item,&dtemp,offset,H_DBLE_SIZE,&iostat); check(iostat); Sprintf(buf,"%-20.10g",dtemp); bufit = 1; } } else if(!memcmp(s,cmplx_item,ITEM_HDR_SIZE)){ offset = mroundup(ITEM_HDR_SIZE,H_CMPLX_SIZE); size -= offset; Strcpy(type,"complex"); *n = size / H_CMPLX_SIZE; if(size % H_CMPLX_SIZE) unknown = TRUE; else if(size == H_CMPLX_SIZE){ hreadr_c(item,ctemp,offset,H_CMPLX_SIZE,&iostat); check(iostat); Sprintf(buf,"(%-14.7g,%-14.7g)",ctemp[0],ctemp[1]); bufit = 1; } } else if(!memcmp(s,char_item,ITEM_HDR_SIZE)){ offset = ITEM_HDR_SIZE; size -= offset; size = min(size,MAXSIZE-1); *n = 1; Strcpy(type,"character"); hreadb_c(item,buf,ITEM_HDR_SIZE,size,&iostat); check(iostat); *(buf+size) = 0; bufit = 1; } else if(!memcmp(s,binary_item,ITEM_HDR_SIZE)){ *n = size; Strcpy(type,"binary"); } else{ Strcpy(type,"text"); *n = size + ITEM_HDR_SIZE; for(i=0; i < ITEM_HDR_SIZE; i++) if(!isspace(*(s+i)) && !isprint(*(s+i)))unknown = TRUE; } } hdaccess_c(item,&iostat); check(iostat); if(unknown){ Strcpy(type,"unknown"); *n = size + ITEM_HDR_SIZE; } else if(bufit){ if(strlen(buf) > length - 1) bugv_c('f',"Descr buffer overflow in hdprobe for %s",keyword); strcpy(descr,buf); } } casacore-3.7.1/mirlib/hio.c000066400000000000000000001331661476623553700155370ustar00rootroot00000000000000/* The routines to manipulate the file hierarchy. 6-dec-89 pjt extended bug() messages 30-apr-90 rjs Support for zero-length items. Added hdelete. 15-jul-91 rjs Check for valid item names in hopen and hdelete. Some mods to some error messages. 18-jul-91 rjs Fixed the name checking to accept the "." file. 2-aug-91 rjs Fixed the name checking to accept '-'. 16-oct-91 rjs Truncated an item when it is opened for rewriting. 12-oct-92 rjs Changed "roundup" macro definition, for pjt. 3-mar-93 rjs Add hflush. 10-aug-93 rjs Add hexists. 26-aug-93 rjs Add habort,hrm. 30-aug-93 rjs Add hseek, htell. 7-sep-93 rjs Bug fix in habort. 23-dec-93 rjs hexists did not handle tno==0 correctly. 5-jan-93 rjs Added hmode to check access mode of dataset. 4-nov-94 rjs Changes to the way trees and items are stored. 15-nov-94 rjs Fixed bug affecting small items being rewritten before the dataset is closed. 27-dec-94 pjt Fixed (?) bug in hexist for regular files and documented this feature 13-mar-95 rjs Increase max number of open items. 30-jun-95 rjs Declaration to appease gcc. 15-may-96 rjs More fiddles with roundup macro. 18-mar-97 rjs Remove alignment restriction on hio_c. 21-mar-97 rjs Make some previously dynamic allocations static. 30-sep-97 rjs Start ntree off at 1 (rather than 0). 28-nov-97 rjs Change to cope with text files which do not end with a newline char. 09-may-00 rjs Get rid of spurious error message in hrm_c. Why didn't I see this ages ago? 10-jun-02 pjt MIR4 changes to handle 2GB+ files and new int8 types 15-jan-03 pjt fix a few prototypes for Const's 30-jan-03 pjt allow itemnames to contain _ (e.g. for cd1_1) 23-feb-03 pjt merged MIR4 22-jul-04 jwr changed type of "size" in hexists_c() from int to size_t 05-nov-04 jwr changed file sizes from size_t to off_t 01-jan-05 pjt a few bug_c() -> bugv_c() 03-jan-05 pjt/rjs hreada/hwritea off_t -> size_t for length 12-jul-11 pjt applied ATNF fix of some static function use off_t/size_t 19-jun-12 pjt fixed hashing bug colliding with handle=0 */ #if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H #include "config.h" #endif #include #include #include "hio.h" #include "miriad.h" #define private static #if !defined(NULL) # define NULL 0 #endif #define MAXNAME 9 #define CACHESIZE 64 /* Max size of items to cache. */ #define CACHE_ENT 16 /* Alignment of cache items. */ #define IO_VALID 0 /* Set if the i/o buffer is valid. */ #define IO_ACTIVE 1 #define IO_MODIFIED 2 #define ITEM_READ 0x1 #define ITEM_WRITE 0x2 #define ITEM_SCRATCH 0x4 #define ITEM_APPEND 0x8 #define ACCESS_MODE (ITEM_READ|ITEM_WRITE|ITEM_SCRATCH|ITEM_APPEND) #define ITEM_CACHE 0x10 #define ITEM_NOCACHE 0x20 #define TREE_CACHEMOD 0x1 #define TREE_NEW 0x2 #define RDWR_UNKNOWN 0 #define RDWR_RDONLY 1 #define RDWR_RDWR 2 typedef struct { /* buffer for I/O operations */ off_t offset; size_t length; int state; char *buf; } IOB; typedef struct item { char *name; int handle,flags,fd,last; off_t size; size_t bsize; /* bsize can technicall be an int, since it's an internal buffer size */ off_t offset; struct tree *tree; IOB io[2]; struct item *fwd; } ITEM; typedef struct tree { char *name; int handle,flags,rdwr,wriostat; ITEM *itemlist; } TREE; static TREE foreign = {"",0,0,0,0,NULL}; #define MAXITEM 1024 private int nitem,ntree; private TREE *tree_addr[MAXOPEN]; private ITEM *item_addr[MAXITEM]; #define hget_tree(tno) (tree_addr[tno]) #define hget_item(tno) (item_addr[tno]) private int header_ok,expansion[MAXTYPES],align_size[MAXTYPES]; private char align_buf[BUFSIZE]; private int first=TRUE; /* Macro to wait for I/O to complete. If its a synchronous i/o system, never bother calling the routine to wait for i/o completion. */ #if BUFDBUFF #define WAIT(item,iostat) \ if((item)->io[0].state == IO_ACTIVE){ \ dwait_c((item)->fd,iostat); \ (item)->io[0].state = IO_VALID; \ } else if((item)->io[1].state == IO_ACTIVE){ \ dwait_c((item)->fd,iostat); \ (item)->io[1].state = IO_VALID; \ } #else #define WAIT(a,b) #define dwait_c(a,b) #endif /* Declare our private routines. */ static void hinit_c(void); static int hfind_nl(char *buf, size_t len); static void hcheckbuf_c(ITEM *item, off_t next, int *iostat); static void hwrite_fill_c(ITEM *item, IOB *iob, off_t next, int *iostat); static void hcache_create_c(TREE *t, int *iostat); static void hcache_read_c(TREE *t, int *iostat); static int hname_check(char *name); static void hdir_c(ITEM *item); static void hrelease_item_c(ITEM *item); static ITEM *hcreate_item_c(TREE *tree, char *name); static TREE *hcreate_tree_c(char *name); #define check(iostat) if(iostat) bugno_c('f',iostat) #define Malloc(a) malloc((size_t)(a)) #define Realloc(a,b) realloc((a),(size_t)(b)) #define Strcpy (void)strcpy #define Strcat (void)strcat #define Memcpy (void)memcpy /************************************************************************/ void hopen_c(int *tno,Const char *name,Const char *status,int *iostat) /**hopen -- Open a data set. */ /*&pjt */ /*:low-level-i/o */ /*+ FORTRAN call sequence subroutine hopen(tno,name,status,iostat) integer tno,iostat character name*(*),status*(*) This opens a Miriad data-set, and readies it to be read or written. Input: name The name of the data set. status Either 'old' or 'new'. Output: tno The file handle of the opened data set. iostat I/O status indicator. 0 indicates success. Other values are standard system error numbers. */ /*-- */ /*----------------------------------------------------------------------*/ { char path[MAXPATH]; TREE *t; /* Initialise if its the first time through. */ if(first)hinit_c(); /* Find a spare slot, and set the name etc. */ dtrans_c((char *)name,path,iostat); if(*iostat)return; t = hcreate_tree_c(path); /* Either open an old cache, or create a new cache. */ if(!strcmp(status,"old")){ hcache_read_c(t,iostat); t->rdwr = RDWR_UNKNOWN; } else if(!strcmp(status,"new")){ dmkdir_c(path,iostat); if(!*iostat)hcache_create_c(t,iostat); t->flags |= TREE_NEW; t->rdwr = RDWR_RDWR; } else *iostat = -1; /* Tidy up before we return. Make sure things are tidy if an error occurred during the operation. */ *tno = t->handle; if(*iostat) hclose_c(*tno); } /************************************************************************/ private void hinit_c() /* Initialise everthing the first time through. ------------------------------------------------------------------------*/ { int i; nitem = 0; ntree = 1; for(i=0; i < MAXITEM; i++)item_addr[i] = NULL; for(i=0; i < MAXOPEN; i++)tree_addr[i] = NULL; /* Tree-0 is a special tree used for "foreign" files. */ tree_addr[0] = &foreign; expansion[H_BYTE] = 1; expansion[H_INT] = sizeof(int)/H_INT_SIZE; expansion[H_INT2] = sizeof(int2)/H_INT2_SIZE; expansion[H_INT8] = sizeof(int8)/H_INT8_SIZE; expansion[H_REAL] = sizeof(float)/H_REAL_SIZE; expansion[H_DBLE] = sizeof(double)/H_DBLE_SIZE; expansion[H_CMPLX] = 2*sizeof(float)/H_CMPLX_SIZE; expansion[H_TXT] = 1; align_size[H_BYTE] = 1; align_size[H_INT] = H_INT_SIZE; align_size[H_INT2] = H_INT2_SIZE; align_size[H_INT8] = H_INT8_SIZE; align_size[H_REAL] = H_REAL_SIZE; align_size[H_DBLE] = H_DBLE_SIZE; align_size[H_CMPLX] =H_REAL_SIZE; align_size[H_TXT] = 1; first = FALSE; header_ok = FALSE; } /************************************************************************/ void hflush_c(int tno,int *iostat) /**hflush -- Close a Miriad data set. */ /*&pjt */ /*:low-level-i/o */ /*+ FORTRAN call sequence subroutine hflush(tno,iostat) integer tno,iostat Write to disk any changed items. Input: tno The handle of the Miriad data set. */ /*-- */ /*----------------------------------------------------------------------*/ { TREE *t; ITEM *item; char s[CACHE_ENT]; int i, ihandle; off_t offset; t = hget_tree(tno); *iostat = 0; /* Determine whether the cache needs to be rewritten, and write out any modified buffers. */ for(item = t->itemlist; item != NULL ; item = item->fwd){ if(!item->fd && !(item->flags & ITEM_NOCACHE) ){ if(item->io[0].state == IO_MODIFIED) t->flags |= TREE_CACHEMOD; } else if(item->fd && !(item->flags & ITEM_SCRATCH) ){ for(i=0; i<2; i++){ if(item->io[i].state == IO_MODIFIED){ WAIT(item,iostat); if(*iostat)return; dwrite_c( item->fd, item->io[i].buf, item->io[i].offset, item->io[i].length, iostat); if(*iostat)return; item->io[i].state = IO_ACTIVE; } } } } /* If the cache has been modified, rewrite the cache. */ if(t->flags & TREE_CACHEMOD){ header_ok = TRUE; haccess_c(tno,&ihandle,"header","write",iostat); header_ok = FALSE; if(*iostat)return; for(i=0; i < CACHE_ENT; i++)s[i] = 0; offset = 0; for(item = t->itemlist; item != NULL; item = item->fwd){ if(!item->fd && !(item->flags & ITEM_NOCACHE)){ Strcpy(s,item->name); s[CACHE_ENT-1] = item->size; hwriteb_c(ihandle,s,offset,CACHE_ENT,iostat); if(*iostat)return; offset += CACHE_ENT; if(item->size > 0){ hwriteb_c(ihandle,item->io[0].buf,offset,item->size,iostat); if(*iostat)return; } item->io[0].state = IO_VALID; item->flags |= ITEM_CACHE; offset += mroundup(item->size,CACHE_ENT); } } hdaccess_c(ihandle,iostat); if(*iostat)return; t->flags &= ~TREE_CACHEMOD; } *iostat = 0; } /************************************************************************/ void habort_c() /**habort -- Abort handling of all open data-sets. */ /*&pjt */ /*:low-level-i/o */ /*+ FORTRAN call sequence subroutine habort() This closes all open Miriad data-sets, and deletes any new ones. No buffers are flushed. */ /*-- */ /*----------------------------------------------------------------------*/ { int i,iostat; TREE *t; ITEM *it,*itfwd; char name[MAXPATH]; /* Don't do anything if the hio routines have never been called. */ if(first)return; /* Flush everything belonging to tree 0. */ hflush_c(0,&iostat); /* Check each possible tree. */ for( i=0; i < MAXOPEN; i++){ if( (t = hget_tree(i) ) != NULL){ it = t->itemlist; while(it != NULL){ itfwd = it->fwd; /* Wait for any i/o to complete, and prevent further flushing of the buffers by pretending that nothing has been modified. */ WAIT(it,&iostat); it->io[0].state = IO_VALID; it->io[1].state = IO_VALID; /* If its an item opened in WRITE mode, remember its name. */ if(it->flags & ITEM_WRITE)Strcpy(name,it->name); else name[0] = 0; /* If the item is open, close it. */ /* If it was in write mode, and the name was known, delete it. */ if(it->flags & ACCESS_MODE)hdaccess_c(it->handle,&iostat); if(*name)hdelete_c(t->handle,name,&iostat); it = itfwd; } /* Pretend the cache has not changed and finish up. Completely delete trees that were opened as NEW. Otherwise finish up. */ t->flags &= ~TREE_CACHEMOD; if(t->flags & TREE_NEW)hrm_c(t->handle); else if(i != 0)hclose_c(t->handle); } } } /************************************************************************/ void hrm_c(int tno) /**hrm -- Remove a data-set. */ /*&pjt */ /*:low-level-i/o */ /*+ FORTRAN call sequence subroutine hrm(tno) integer tno This completely removes a Miriad data-set. Input: tno The file handle of the open data-set. */ /*-- */ /*----------------------------------------------------------------------*/ { char name[MAXPATH]; int iostat,ihandle; TREE *t; haccess_c(tno,&ihandle,".","read",&iostat); if(iostat == 0){ hreada_c(ihandle,name,MAXPATH,&iostat); while(iostat == 0){ hdelete_c(tno,name,&iostat); hreada_c(ihandle,name,MAXPATH,&iostat); } hdaccess_c(ihandle,&iostat); } /* Delete the "header" item. */ header_ok = TRUE; hdelete_c(tno,"header",&iostat); header_ok = FALSE; /* Delete the directory itself. */ t = hget_tree(tno); t->flags &= ~TREE_CACHEMOD; drmdir_c(t->name,&iostat); hclose_c(tno); } /************************************************************************/ void hclose_c(int tno) /**hclose -- Close a Miriad data set. */ /*&pjt */ /*:low-level-i/o */ /*+ FORTRAN call sequence subroutine hclose(tno) integer tno This closes a Miriad data set. The data set cannot be accessed after the close. Input: tno The handle of the Miriad data set. */ /*-- */ /*----------------------------------------------------------------------*/ { TREE *t; ITEM *item,*it1,*it2; int iostat; /* Close any open items. */ t = hget_tree(tno); for(item=t->itemlist; item != NULL; item = item->fwd){ if(item->flags & ACCESS_MODE){ bugv_c('w',"Closing item -- %s",item->name); hdaccess_c(item->handle,&iostat); check(iostat); } } /* Flush out the header, if needed. */ hflush_c(tno,&iostat); check(iostat); /* Release all allocated stuff. */ it1 = t->itemlist; while(it1 != NULL){ it2 = it1->fwd; hrelease_item_c(it1); it1 = it2; } tree_addr[tno] = NULL; free(t->name); free((char *)t); ntree--; } /************************************************************************/ void hdelete_c(int tno,Const char *keyword,int *iostat) /**hdelete -- Delete an item from a data-set. */ /*&pjt */ /*:low-level-i/o */ /*+ FORTRAN call sequence subroutine hdelete(tno,keyword,iostat) integer tno,iostat character keyword*(*) This deletes an item from a Miriad data-set. The item must not be "accessed" when the hdelete routine is called. Input: tno The handle of the data set. keyword The name of the item. Output: iostat I/O status indicator. 0 indicates success. Other values are standard system error numbers. */ /*-- */ /*----------------------------------------------------------------------*/ { char path[MAXPATH]; ITEM *item; TREE *t; int ent_del; if(first)hinit_c(); if(tno != 0) if( (*iostat = hname_check((char *)keyword)) ) return; /* Check if the item is aleady here abouts. */ t = hget_tree(tno); ent_del = FALSE; item = NULL; if(tno != 0) for(item=t->itemlist; item != NULL; item = item->fwd) if(!strcmp(keyword,item->name))break; /* Delete the entry for this item, if there was one. */ if(item != NULL){ if(item->flags & ACCESS_MODE) bugv_c('f',"hdelete: Attempt to delete accessed item: %s",keyword); if(item->flags & ITEM_CACHE) t->flags |= TREE_CACHEMOD; hrelease_item_c(item); ent_del = TRUE; } /* Always try to delete a file associated with the item. */ Strcpy(path,t->name); Strcat(path,keyword); ddelete_c(path,iostat); /* If we have deleted it once already, do not give any errors if the second attempt failed. */ if(ent_del) *iostat = 0; } /************************************************************************/ void haccess_c(int tno,int *ihandle,Const char *keyword,Const char *status,int *iostat) /**haccess -- Open an item of a data set for access. */ /*&pjt */ /*:low-level-i/o */ /*+ FORTRAN call sequence subroutine haccess(tno,itno,keyword,status,iostat) integer tno,itno,iostat character keyword*(*),status*(*) Miriad data sets consist of a collection of items. Before an item within a data set can be read/written, etc, it must be "opened" with the haccess routine. Input: tno The handle of the data set. keyword The name of the item. status This can be 'read', 'write', 'append' or 'scratch'. 'scratch' files are using $TMPDIR, if present, else current. Output: itno The handle of the opened item. Note that item handles are quite distinct from data-set handles. iostat I/O status indicator. 0 indicates success. Other values are standard system error numbers. */ /*-- */ /*----------------------------------------------------------------------*/ { char path[MAXPATH]; ITEM *item; TREE *t; int mode=0; char string[3]; if(first)hinit_c(); if(!strcmp("read",status)) mode = ITEM_READ; else if(!strcmp("write",status)) mode = ITEM_WRITE; else if(!strcmp("scratch",status))mode = ITEM_SCRATCH; else if(!strcmp("append",status)) mode = ITEM_APPEND; else bugv_c('f',"haccess_c: unrecognised STATUS=%s",status); if(!strcmp("header",keyword) || !strcmp(".",keyword) || !strcmp("history",keyword)|| tno == 0 || (mode & ITEM_SCRATCH) )mode |= ITEM_NOCACHE; if(tno != 0) if( (*iostat = hname_check((char *)keyword)) )return; t = hget_tree(tno); /* If we are writing, check whether we have write permission. */ if( !(mode & ITEM_READ) && !(mode & ITEM_NOCACHE) ){ if(t->rdwr == RDWR_UNKNOWN)hmode_c(tno,string); *iostat = t->wriostat; if(*iostat) return; } /* Check if the item is already here abouts. */ item = NULL; if(tno != 0) for(item = t->itemlist; item != NULL; item = item->fwd) if(!strcmp(keyword,item->name))break; /* If the item does not exist, create it. Otherwise the item must be cacheable, in which case we truncate its length to zero if needed. */ if(item == NULL)item = hcreate_item_c(t,(char *)keyword); else if((mode & (ITEM_WRITE|ITEM_SCRATCH)) && item->size != 0){ item->size = 0; item->io[0].length = item->io[1].length = 0; if(item->flags & ITEM_CACHE) t->flags |= TREE_CACHEMOD; } /* Check and set the read/write flags. */ if(item->flags & ACCESS_MODE) bugv_c('f',"haccess_c: Multiple access to item %s",keyword); item->flags |= mode; /* Open the file if necessary. */ *iostat = 0; item->offset = 0; if(!strcmp(keyword,".")){ hdir_c(item); } else if(item->size == 0 && (!(mode & ITEM_WRITE) || (mode & ITEM_NOCACHE)) && !(item->flags & ITEM_CACHE)){ Strcpy(path,t->name); Strcat(path,keyword); dopen_c(&(item->fd),path,(char *)status,&(item->size),iostat); item->bsize = BUFSIZE; item->io[0].buf = Malloc(BUFSIZE); if(BUFDBUFF)item->io[1].buf = Malloc(BUFSIZE); if(mode & ITEM_APPEND) item->offset = item->size; /* If we have opened a file in write mode, remember that this dataset is writeable. */ if(!(mode & ITEM_READ)){ if(*iostat == 0) t->rdwr = RDWR_RDWR; else t->rdwr = RDWR_RDONLY; t->wriostat = *iostat; } } *ihandle = item->handle; if(*iostat)hrelease_item_c(item); } /************************************************************************/ void hmode_c(int tno,char *mode) /* */ /**hmode -- Return access modes of a dataset. */ /*&pjt */ /*:low-level-i/o */ /*+ FORTRAN call sequence subroutine hmode(tno,mode) integer tno character mode*(*) Determine the access modes of a data-set Input: tno The handle of the data set. Output: mode This will be either "" (unknown access mode), "r" (read-only) "rw" (read-write). */ /*-- */ /*----------------------------------------------------------------------*/ { int iostat; int ihandle; TREE *t; /* If its tno==0, give up. */ *mode = 0; if(tno == 0)return; /* If we do not already know the read/write access, determine it the hard way. */ t = hget_tree(tno); if(t->rdwr == RDWR_UNKNOWN){ header_ok = TRUE; haccess_c(tno,&ihandle,"header","append",&iostat); header_ok = FALSE; if(!iostat)hdaccess_c(ihandle,&iostat); } /* Return the info. */ if(t->rdwr == RDWR_RDONLY) Strcpy(mode,"r"); else if(t->rdwr == RDWR_RDWR) Strcpy(mode,"rw"); else bugv_c('f',"hmode_c: Algorithmic failure rdwr=%d",t->rdwr); } /************************************************************************/ int hexists_c(int tno,Const char *keyword) /**hexists -- Check if an item exists. */ /*&pjt */ /*:low-level-i/o */ /*+ FORTRAN call sequence logical function hexists(tno,keyword) integer tno character keyword*(*) Check if a particular item exists in a Miriad data-set. By setting the input 'tno' to 0, one can also check for existence of any regular file. Input: tno The handle of the data set. 0 also allowed. keyword The name of the item or filename to check. Output: hexists True if the item exists. */ /*-- */ /*----------------------------------------------------------------------*/ { char path[MAXPATH]; int iostat,fd; off_t size; ITEM *item; TREE *t; /* Check for an invalid name. */ if(tno != 0) if(hname_check((char *)keyword)) return(FALSE); /* Check if the item is aleady here abouts. */ if(tno != 0){ /* miriad dataset */ item = NULL; t = hget_tree(tno); for(item = t->itemlist; item != NULL; item = item->fwd) if(!strcmp(keyword,item->name))return(TRUE); Strcpy(path,t->name); Strcat(path,keyword); } else { Strcpy(path,keyword); /* regular filename */ } /* It was not found in the items currently opened, nor the items that live in "header". Now try and open a file with this name. */ dopen_c(&fd,path,"read",&size,&iostat); if(iostat)return FALSE; dclose_c(fd,&iostat); if(iostat != 0)bugv_c('f',"hexists_c: Error closing item %s",keyword); return TRUE; } /************************************************************************/ void hdaccess_c(int ihandle,int *iostat) /**hdaccess -- Finish up access to an item. */ /*&pjt */ /*:low-level-i/o */ /*+ FORTRAN call sequence subroutine hdaccess(itno,iostat) integer itno,iostat This releases an item. It flushes buffers and waits for i/o to complete. For small items that are entirely in memory, these are saved until the whole tree is closed before they are written out. Input: itno The handle of the item to close up. iostat I/O status indicator. 0 indicates success. Other values are standard system error numbers. */ /*-- */ /*----------------------------------------------------------------------*/ { ITEM *item; int i,stat; /* If it has an associated file, flush anything remaining to the file and close it up. */ item = hget_item(ihandle); /* May be a binary file. Flush modified buffers, wait for i/o to complete, and close up. */ *iostat = 0; stat = 0; if(item->fd != 0){ for(i=0; i<2 && !stat; i++){ if(item->io[i].state == IO_MODIFIED && !(item->flags & ITEM_SCRATCH)){ WAIT(item,&stat); if(!stat)dwrite_c( item->fd, item->io[i].buf, item->io[i].offset, item->io[i].length, &stat); item->io[i].state = IO_ACTIVE; } } *iostat = stat; WAIT(item,&stat); if(stat) *iostat = stat; dclose_c(item->fd,&stat); if(stat) *iostat = stat; hrelease_item_c(item); } else if(item->flags & ITEM_NOCACHE){ hrelease_item_c(item); /* If it has not associated file, it must be small. Do not release it, as it will need to be written to the cache later on. */ } else{ item->flags &= ~ACCESS_MODE; if(item->io[0].state == IO_MODIFIED)item->tree->flags |= TREE_CACHEMOD; item->io[0].state = IO_VALID; } } /************************************************************************/ off_t hsize_c(int ihandle) /**hsize -- Determine the size (in bytes) of an item. */ /*&pjt */ /*:low-level-i/o */ /*+ FORTRAN call sequence integer function hsize(itno) integer itno This returns the size of an item, in bytes. Input: itno The handle of the item of interest. Output: hsize The size of the item in bytes. */ /*-- */ /*----------------------------------------------------------------------*/ { ITEM *item; item = hget_item(ihandle); return item->size; } /************************************************************************/ void hio_c(int ihandle,int dowrite,int type,char *buf, off_t offset, size_t length,int *iostat) /**hread,hwrite -- Read and write items. */ /*&pjt */ /*:low-level-i/o */ /*+ FORTRAN call sequence subroutine hreada(itno,abuf,iostat) subroutine hreadb(itno,bbuf,offset,length,iostat) subroutine hreadj(itno,jbuf,offset,length,iostat) subroutine hreadi(itno,ibuf,offset,length,iostat) subroutine hreadr(itno,rbuf,offset,length,iostat) subroutine hreadd(itno,dbuf,offset,length,iostat) subroutine hwritea(itno,abuf,iostat) subroutine hwriteb(itno,bbuf,offset,length,iostat) subroutine hwritej(itno,jbuf,offset,length,iostat) subroutine hwritei(itno,ibuf,offset,length,iostat) subroutine hwriter(itno,rbuf,offset,length,iostat) subroutine hwrited(itno,dbuf,offset,length,iostat) integer itno,offset,length,iostat character abuf*(*),bbuf*(length) integer jbuf(*),ibuf(*) real rbuf(*) double precision dbuf(*) These routines read and write items of a Miriad data set. They differ in the sort of element that they read or write. hreada,hwritea I/O on ascii text data (terminated by newline char). hreadb,hwriteb I/O on ascii data. hreadj,hwritej I/O on data stored externally as 16 bit integers. hreadi,hwritei I/O on data stored externally as 32 bit integers. hreadr,hwriter I/O on data stored externally as IEEE 32 bit reals. hreadd,hwrited I/O on data stored externally as IEEE 64 bit reals. Note that hreada and hreadb differ in that: * hreada reads sequentially, terminating a read on a newline character. The output buffer is blank padded. * hreadb performs random reads. Newline characters have no special meaning to it. A fixed number of bytes are read, and the buffer is not blank padded. Hwritea and hwriteb differ in similar ways. Inputs: itno The handle of the item to perform I/O on. offset The byte offset into the item, where I/O is to be performed. length The number of bytes to be read. "Offset" and "length" are offsets and lengths into the external file, always given in bytes. Note that "offset" and "length" must obey an alignment requirement. Both must be a multiple of the size of the element they are performing I/O on. For example, they must be a multiple of 2 for hreadj,hwritej; a multiple of 4 for hreadi,hwritei,hreadr,hwriter; a multiple of 8 for hreadd,hwrited. Inputs(hwrite) or Outputs(hread): abuf,bbuf,jbuf,ibuf,rbuf,dbuf The buffer containing, or to receive, the data. Outputs: iostat I/O status indicator. 0 indicates success. -1 indicates end-of-file. Other values are standard system error numbers. */ /*-- */ /*----------------------------------------------------------------------*/ /* This performs either a read or write operation. It is somewhat involved, as it has to handle buffering. Possibly either one or two buffers are used (system dependent). Read-ahead, write-behind are attempted for systems which can perform this. This is intended to work in both a VMS and UNIX environment, which makes it quite involved (because of VMS). Because of caching of small items, buffers are not allocated until the last moment. */ /* Define a macro to determine if a offset maps into a buffer. */ #define WITHIN_BUF(b) ( (item->io[b].length > 0) && \ (offset >= item->io[b].offset) && \ (offset < item->io[b].offset + \ (dowrite ? (off_t)item->bsize : (off_t)item->io[b].length))) { char *s; int b; /* 0 or 1, pointing in one of two IOB buffers */ off_t next, off; size_t size, len; IOB *iob1,*iob2; ITEM *item; item = hget_item(ihandle); size = align_size[type]; /* Check various end-of-file conditions and for adequate buffers. */ next = offset + (off_t) (!dowrite && type == H_TXT ? 1 : length ); /* if(!dowrite && type == H_TXT) length = min(length, item->size - offset); */ *iostat = -1; if(!dowrite && next > item->size)return; *iostat = 0; if(item->bsize < BUFSIZE && (off_t)item->bsize < next)hcheckbuf_c(item,next,iostat); if(*iostat)return; /*----------------------------------------------------------------------*/ /* */ /* Loop until we have processed all the data required. */ /* First determine which of the (possibly) two i/o buffers */ /* to use. If we have only one buffer, we have no choice. If our */ /* data is within the last used buffer, use that. Otherwise use */ /* the least recent used buffer. */ /* */ /*----------------------------------------------------------------------*/ while(length > 0){ b = item->last; if(item->io[1].buf == NULL) b = 0; else if(WITHIN_BUF(b)){ if(WITHIN_BUF(1-b)) b = ( item->io[0].offset > item->io[1].offset ? 0 : 1); } else b = 1 - b; iob1 = &(item->io[b]); iob2 = &(item->io[1-b]); /*----------------------------------------------------------------------*/ /* */ /* Handle the case of a miss. Flush the i/o buffer if it has been */ /* modified and read in any needed new data. */ /* */ /*----------------------------------------------------------------------*/ if(!WITHIN_BUF(b)){ if(iob1->state == IO_MODIFIED){ next = iob1->offset + iob1->length; if(iob1->length%BUFALIGN && next < item->size) {hwrite_fill_c(item,iob1,next,iostat); if(*iostat) return;} WAIT(item,iostat); if(*iostat) return; dwrite_c(item->fd,iob1->buf,iob1->offset,iob1->length,iostat); iob1->state = IO_ACTIVE; if(*iostat) return; } iob1->offset = (offset/BUFALIGN) * BUFALIGN; iob1->length = 0; if(!dowrite){ WAIT(item,iostat); if(*iostat) return; iob1->length = min((off_t)item->bsize,item->size-iob1->offset); if(iob2->buf != NULL && iob1->offset < iob2->offset) iob1->length = min((off_t)iob1->length, iob2->offset - iob1->offset); dread_c(item->fd,iob1->buf,iob1->offset,iob1->length,iostat); iob1->state = IO_ACTIVE; if(*iostat) return; } } /*----------------------------------------------------------------------*/ /* */ /* Wait for any i/o and perform a read ahead or write-behind, */ /* so that we are ready next time. Do this before we copy the */ /* data to/from the callers buffer, so that we can overlap */ /* the copy and i/o operations. The next section is skipped if */ /* the underlying i/o is synchronous. */ /* */ /*----------------------------------------------------------------------*/ #if BUFDBUFF if(iob1->state == IO_ACTIVE) {WAIT(item,iostat); if(*iostat)return;} if(iob2->buf != NULL && iob2->state != IO_ACTIVE){ next = iob1->offset + iob1->length; /* Write behind. */ if(iob2->state == IO_MODIFIED && (!(iob2->length%BUFALIGN) || iob2->offset + iob2->length == item->size)){ dwrite_c(item->fd,iob2->buf,iob2->offset,iob2->length,iostat); iob2->state = IO_ACTIVE; /* Read ahead. */ } else if(!dowrite && next < item->size && next != iob2->offset){ iob2->offset = next; iob2->length = min( BUFSIZE, item->size - iob2->offset ); dread_c (item->fd,iob2->buf,iob2->offset,iob2->length,iostat); iob2->state = IO_ACTIVE; } } #endif /*----------------------------------------------------------------------*/ /* */ /* If its a write operation, possibly update the file size, and */ /* handle possible non-aligned non-sequential write operations. */ /* */ /*----------------------------------------------------------------------*/ if(dowrite){ if(iob1->offset + (off_t)iob1->length < offset && iob1->offset + (off_t)iob1->length < item->size) {hwrite_fill_c(item,iob1,offset,iostat); if(*iostat) return;} iob1->state = IO_MODIFIED; iob1->length = max(iob1->length, min(length + offset - iob1->offset, item->bsize)); item->size = max((off_t)item->size,iob1->offset + (off_t)iob1->length); } /*----------------------------------------------------------------------*/ /* */ /* Copy between the i/o buffer and users buffer. */ /* */ /*----------------------------------------------------------------------*/ off = offset - iob1->offset; len = min(length, iob1->length - off); s = ( ( off % size ) ? align_buf : iob1->buf + off ); if(dowrite){ switch(type){ case H_BYTE: Memcpy(s,buf,len); break; case H_INT: pack32_c((int *)buf, s,len/H_INT_SIZE); break; case H_INT2: pack16_c((int2 *)buf,s,len/H_INT2_SIZE); break; case H_INT8: pack64_c((int8 *)buf,s,len/H_INT8_SIZE); break; case H_REAL: packr_c((float *)buf,s,len/H_REAL_SIZE); break; case H_DBLE: packd_c((double *)buf,s,len/H_DBLE_SIZE); break; case H_CMPLX: packr_c((float *)buf,s,(2*len)/H_CMPLX_SIZE); break; case H_TXT: Memcpy(s,buf,len); if(*(buf+len-1) == 0)*(iob1->buf+off+len-1) = '\n'; break; default: bugv_c('f',"hio_c: Unrecognised write type %d",type); } if(off % size) Memcpy(iob1->buf+off,align_buf,len); } else { /* If the data are not aligned, copy to an alignment buffer for processing. */ if(off % size) Memcpy(align_buf,iob1->buf+off,len); switch(type){ case H_BYTE: Memcpy(buf,s,len); break; case H_INT: unpack32_c(s,(int *)buf,len/H_INT_SIZE); break; case H_INT2: unpack16_c(s,(int2 *)buf,len/H_INT2_SIZE); break; case H_INT8: unpack64_c(s,(int8 *)buf,len/H_INT8_SIZE); break; case H_REAL: unpackr_c(s,(float *)buf,len/H_REAL_SIZE); break; case H_DBLE: unpackd_c(s,(double *)buf,len/H_DBLE_SIZE); break; case H_CMPLX: unpackr_c(s,(float *)buf,(2*len)/H_CMPLX_SIZE); break; case H_TXT: len = hfind_nl(s,len); Memcpy(buf,s,len); if(*(s+len-1) == '\n'){ length = len; *(buf+len-1) = 0; }else if(offset+(off_t)len == (off_t)item->size && len < length){ length = ++len; *(buf+len-1) = 0; } break; default: bugv_c('f',"hio_c: Unrecognised read type %d",type); } } buf += expansion[type] * len; length -= len; offset += len; item->offset = offset; item->last = b; } } /************************************************************************/ private int hfind_nl(char *buf, size_t len) /* Return the character number of the first new-line character. ------------------------------------------------------------------------*/ { int i; for(i=1;i <= (int)len; i++)if(*buf++ == '\n')return(i); return(len); } /************************************************************************/ private void hcheckbuf_c(ITEM *item,off_t next,int *iostat) /* Check to determine that we have adequate buffer space, and a file, if needed. ------------------------------------------------------------------------*/ { char *s,path[MAXPATH]; TREE *t; *iostat = 0; /* Allocate a small buffer if needed. */ if((off_t)item->bsize < next && next <= CACHESIZE){ s = Malloc(CACHESIZE); item->bsize = CACHESIZE; if(item->io[0].length > 0)Memcpy(s,item->io[0].buf,item->io[0].length); if(item->io[0].buf != NULL) free(item->io[0].buf); item->io[0].buf = s; /* Allocate full sized buffers if needed. */ } else if(item->bsize <= CACHESIZE && next > CACHESIZE){ s = Malloc(BUFSIZE); item->bsize = BUFSIZE; if(item->io[0].length > 0)Memcpy(s,item->io[0].buf,item->io[0].length); if(item->io[0].buf != NULL) free(item->io[0].buf); item->io[0].buf = s; if(BUFDBUFF)item->io[1].buf = Malloc(BUFSIZE); } /* Open a file if needed. */ if(item->fd == 0 && item->bsize > CACHESIZE && !(item->flags & ITEM_NOCACHE)){ t = item->tree; if(item->flags & ITEM_CACHE) t->flags |= TREE_CACHEMOD; item->flags &= ~ITEM_CACHE; Strcpy(path,t->name); Strcat(path,item->name); dopen_c(&(item->fd),path,"write",&(item->size),iostat); if(*iostat == 0) t->rdwr = RDWR_RDWR; else t->rdwr = RDWR_RDONLY; t->wriostat = *iostat; } } /************************************************************************/ private void hwrite_fill_c(ITEM *item, IOB *iob, off_t next, int *iostat) /* A nonaligned nonsequential write operation has been requested. Read in the portion that we are missing. We need to fill the i/o buffer up to at least offset - 1. Inputs: item Descriptor of the thingo we are reading in. iob Structure of the i/o buffer. next Fill up to at least byte (next-1). Output: iostat I/O status. ------------------------------------------------------------------------*/ { char buffer[BUFSIZE]; off_t offset; size_t length; offset = BUFALIGN * ((iob->offset + iob->length) / BUFALIGN); length = BUFALIGN * ((next-1)/BUFALIGN + 1) - offset; length = min((off_t)length, (off_t)item->size - offset); WAIT(item,iostat); if(*iostat)return; dread_c(item->fd,buffer,offset,length,iostat); if(*iostat)return; dwait_c(item->fd,iostat); if(*iostat)return; offset = iob->offset + iob->length - offset; length -= offset; Memcpy(iob->buf+iob->length,buffer+offset,length); iob->length += length; } /************************************************************************/ void hseek_c(int ihandle,off_t offset) /**hseek -- Set default offset (in bytes) of an item. */ /*&pjt */ /*:low-level-i/o */ /*+ FORTRAN call sequence integer function hseek(itno,offset) integer itno,offset This sets the default access point of an item. This Can be used to reposition an item when reading/writing using hreada/hwritea. Input: itno The handle of the item of interest. offset The new offset. */ /*-- */ /*----------------------------------------------------------------------*/ { ITEM *item; item = hget_item(ihandle); item->offset = offset; } /************************************************************************/ off_t htell_c(int ihandle) /**htell -- Return the default offset (in bytes) of an item. */ /*&pjt */ /*:low-level-i/o */ /*+ FORTRAN call sequence integer function htell(itno) integer itno This returns the current default offset of an item, which is used when reading/writing using hreada/hwritea. Input: itno The handle of the item of interest. */ /*-- */ /*----------------------------------------------------------------------*/ { ITEM *item; item = hget_item(ihandle); return(item->offset); } /************************************************************************/ void hreada_c(int ihandle,char *line,size_t length,int *iostat) /*----------------------------------------------------------------------*/ { ITEM *item; item = hget_item(ihandle); hio_c( ihandle, FALSE, H_TXT, line, item->offset, length, iostat); } /************************************************************************/ void hwritea_c(int ihandle,Const char *line,size_t length,int *iostat) /*----------------------------------------------------------------------*/ { ITEM *item; item = hget_item(ihandle); hio_c( ihandle ,TRUE, H_TXT, (char *)line, item->offset, length, iostat); } /************************************************************************/ private void hcache_create_c(TREE *t,int *iostat) /* Create a cache. ------------------------------------------------------------------------*/ { int ihandle; header_ok = TRUE; haccess_c(t->handle,&ihandle,"header","write",iostat); header_ok = FALSE; if(!*iostat) hdaccess_c(ihandle,iostat); } /************************************************************************/ private void hcache_read_c(TREE *t,int *iostat) /* Read in all small items, which are stored in the file "header". Errors should never happen when reading the cache. If they do, abort completely. ------------------------------------------------------------------------*/ { off_t offset; ITEM *item; char s[CACHE_ENT]; int ihandle; header_ok = TRUE; haccess_c(t->handle,&ihandle,"header","read",iostat); header_ok = FALSE; if(*iostat)return; offset = 0; while(hreadb_c(ihandle,s,offset,CACHE_ENT,iostat),!*iostat){ offset += CACHE_ENT; item = hcreate_item_c(t,s); item->size = *(s+CACHE_ENT-1); item->bsize = item->size; item->flags = ITEM_CACHE; item->io[0].offset = 0; item->io[0].length = item->size; item->io[0].state = IO_VALID; item->io[0].buf = Malloc(item->size); hreadb_c(ihandle,item->io[0].buf,offset,item->size,iostat); check(*iostat); offset += mroundup(item->size,CACHE_ENT); } if(*iostat != -1) bug_c('f',"hcache_read_c: Something wrong reading cache"); hdaccess_c(ihandle,iostat); } /************************************************************************/ private int hname_check(char *name) /* This checks if the name of an item is OK. Generally an item must be 1 to 8 characters, alphanumeric, starting with an alpha. Only lower case alpha is allowed. The name "header" is generally reserved. ------------------------------------------------------------------------*/ { size_t i, length; char c; length = strlen(name); if(length <= 0 || length >= MAXNAME) return(-1); if(length == 1 && *name == '.')return(0); if(*name < 'a' || *name > 'z')return(-1); if(!header_ok && length == 6 && !strcmp("header",name))return(-1); for(i=0; i < length; i++){ c = *name++; if((c < 'a' || c > 'z') && (c < '0' || c > '9') && (c != '-') && (c != '_')) return(-1); } return 0 ; } /************************************************************************/ private void hdir_c(ITEM *item) /* Read the directory contents into a buffer (make it look like a text file. ------------------------------------------------------------------------*/ { size_t len, length, plength; char *context,*s; ITEM *it; TREE *t; #define MINLENGTH 128 /* Mark this item as not cachable. */ item->flags |= ITEM_NOCACHE | ITEM_SCRATCH; /* Get a buffer size which is guaranteed to hold all the items that come from the "header" file. */ plength = 0; t = item->tree; for(it = t->itemlist; it != NULL; it = it->fwd) plength += strlen(it->name) + 1; plength = max(plength,2*MINLENGTH); s = Malloc(plength); /* Copy the names of all the "header" items to this buffer. Exclude the "." itself. */ length = 0; for(it=t->itemlist; it != NULL; it = it->fwd){ if(it->fd == 0 && !(it->flags & ITEM_NOCACHE)){ Strcpy(s+length,it->name); length += strlen(it->name); *(s+length++) = '\n'; } } /* Now read through the directory to get all external files. Skip the "header" file. The size of the buffer is doubled as we go, when it gets too small. */ dopendir_c(&context,t->name); do{ if(plength - length < MINLENGTH){ plength *= 2; s = Realloc(s, plength); } dreaddir_c(context,s+length,plength-length); len = strlen(s+length); if(len > 0 && strcmp(s+length,"header")){ length += len; *(s+length++) = '\n'; } }while(len > 0); dclosedir_c(context); /* Finish initialising the item now. */ item->size = length; item->io[0].buf = s; item->io[0].offset = 0; item->io[0].length = length; item->bsize = plength; } /************************************************************************/ private void hrelease_item_c(ITEM *item) /* Release the item on the top of the list. ------------------------------------------------------------------------*/ { ITEM *it1,*it2; TREE *t; /* Find the item. Less than attractive code. */ t = item->tree; it2 = t->itemlist; if(item != it2){ do{ it1 = it2; it2 = it1->fwd; }while(item != it2); it1->fwd = it2->fwd; } else t->itemlist = item->fwd; /* Release any memory associated with the item. */ if(item->io[0].buf != NULL) free(item->io[0].buf); if(item->io[1].buf != NULL) free(item->io[1].buf); item_addr[item->handle] = NULL; free(item->name); free((char *)item); nitem--; } /************************************************************************/ private ITEM *hcreate_item_c(TREE *tree,char *name) /* Create an item, and initialise as much of it as possible. ------------------------------------------------------------------------*/ { ITEM *item; int i,hash; char *s; /* Hash the name. */ s = name; hash = nitem++; if(nitem > MAXITEM) bugv_c('f',"Item address table overflow, in hio; nitem=%d MAXITEM=%d",nitem,MAXITEM); while(*s) hash += *s++; hash %= MAXITEM; /* Find a slot in the list of addresses, and allocate it. */ /* avoid hash=0 since the hash is returned as a handle and it will */ /* collide with the special stdout value that MIRIAD often uses */ /* could also return hash+1 ? but what if this > MAXOPEN */ while(hget_item(hash) != NULL || hash==0) hash = (hash+1) % MAXITEM; item_addr[hash] = (ITEM *)Malloc(sizeof(ITEM)); /* Initialise it now. */ item = hget_item(hash); item->name = Malloc(strlen(name) + 1); Strcpy(item->name,name); item->handle = hash; item->size = 0; item->flags = 0; item->fd = 0; item->last = 0; item->offset = 0; item->bsize = 0; item->tree = tree; for(i=0; i<2; i++){ item->io[i].offset = 0; item->io[i].length = 0; item->io[i].state = 0; item->io[i].buf = NULL; } item->fwd = tree->itemlist; tree->itemlist = item; return(item); } /************************************************************************/ private TREE *hcreate_tree_c(char *name) /* Create an item, and initialise as much of it as possible. ------------------------------------------------------------------------*/ { TREE *t; int hash; char *s; /* Hash the name. */ s = name; hash = ntree++; if(ntree > MAXOPEN) bugv_c('f',"Tree address table overflow, in hio, ntree=%d MAXOPEN=%d",ntree,MAXOPEN); while(*s) hash += *s++; hash %= MAXOPEN; /* Find a slot in the list of addresses, and allocate it. */ while(hget_tree(hash) != NULL || hash==0) hash = (hash+1) % MAXOPEN; tree_addr[hash] = (TREE *)Malloc(sizeof(TREE)); /* Initialise it. */ t = hget_tree(hash); t->name = Malloc(strlen(name) + 1); Strcpy(t->name,name); t->handle = hash; t->flags = 0; t->itemlist = NULL; return t; } casacore-3.7.1/mirlib/hio.h000066400000000000000000000020001476623553700155220ustar00rootroot00000000000000#if !defined(MIR_HIO_H) #define MIR_HIO_H #include "sysdep.h" /* * magic numbers at the start of an item, these are like BITPIX in fits, * so don't change them or your MIRIAD files won't be exchangeable between * other MIRIAD implementations * MAXTYPES is pretty arbitrary, just make sure it's at least the last H_+1 * */ #define H_BYTE 1 #define H_INT 2 #define H_INT2 3 #define H_REAL 4 #define H_DBLE 5 #define H_TXT 6 #define H_CMPLX 7 #define H_INT8 8 #define MAXTYPES 10 #define H_BYTE_SIZE 1 #define H_INT_SIZE 4 #define H_INT2_SIZE 2 #define H_INT8_SIZE 8 #define H_REAL_SIZE 4 #define H_DBLE_SIZE 8 #define H_TXT_SIZE 1 #define H_CMPLX_SIZE 8 #define MAXPATH 256 #define MAXOPEN 26 /* prototypes are now in miriad.h (mostly) and sysdep.h (pack routines) */ /* Other handy definitions. */ #define TRUE 1 #define FALSE 0 #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) #define mroundup(a,b) ((b)*(((a)+(b)-1)/(b))) #endif /* MIR_HIO_H */ casacore-3.7.1/mirlib/io.h000066400000000000000000000034411476623553700153640ustar00rootroot00000000000000/************************************************************************/ /* */ /* A general header file for the various file and i/o handling */ /* routines. */ /* */ /* History: */ /* rjs Dark-ages Original version. */ /* rjs 20aug92 Correct "roundup" macro when rounding 0. */ /* rjs 15may96 Moved roundup macro elsewhere. */ /* pjt 28may02 Added H_INT8 */ /* pjt 17jun02 different MIR4 structures? */ /************************************************************************/ /* Binary items start with a sequence to allow routines to blindly determine how to read them. The "binary_item" is a catch all with only indicates that the data is binary valued, but does not hint at the format. */ #if !defined(MIR_IO_H) #define MIR_IO_H #include "hio.h" #include #define ITEM_HDR_SIZE 4 #if 1 /* MIRIAD3 and below data structures */ static char binary_item[ITEM_HDR_SIZE] = {0,0,0,0}, real_item[ITEM_HDR_SIZE] = {0,0,0,H_REAL}, int_item[ITEM_HDR_SIZE] = {0,0,0,H_INT}, int2_item[ITEM_HDR_SIZE] = {0,0,0,H_INT2}, int8_item[ITEM_HDR_SIZE] = {0,0,0,H_INT8}, char_item[ITEM_HDR_SIZE] = {0,0,0,H_BYTE}, dble_item[ITEM_HDR_SIZE] = {0,0,0,H_DBLE}, cmplx_item[ITEM_HDR_SIZE] = {0,0,0,H_CMPLX}; #else /* MIRIAD4 data structures - not finalized on this though */ static char binary_item[ITEM_HDR_SIZE] = {1,0,0,0}, real_item[ITEM_HDR_SIZE] = {1,0,0,H_REAL}, int_item[ITEM_HDR_SIZE] = {1,0,0,H_INT}, int2_item[ITEM_HDR_SIZE] = {1,0,0,H_INT2}, int8_item[ITEM_HDR_SIZE] = {1,0,0,H_INT8}, char_item[ITEM_HDR_SIZE] = {1,0,0,H_BYTE}, dble_item[ITEM_HDR_SIZE] = {1,0,0,H_DBLE}, cmplx_item[ITEM_HDR_SIZE] = {1,0,0,H_CMPLX}; #endif #endif /* MIR_IO_H */ casacore-3.7.1/mirlib/key.c000066400000000000000000000755431476623553700155540ustar00rootroot00000000000000/*********************************************************************** * * Key routines provide keyword-oriented access to the command line. * * History: * rjs 24apr91 Original version. * jm 28jun94 Wrote the ANSI-C version. * jm 01nov94 Added expand and local traits and added keyl(). * jm 17nov94 Rewrote #if definitions for ANSI prototypes. Sun * machines define __STDC__ even when it is 0! This * involved creating and using PROTOTYPE in sysdep.h. * jm 24oct96 Increased MAXSTRING from 256 to 512 and fixed a * few lint complaints. * jm 01aug97 Changed getKeyValue to properly handle single and * double quotes around a value. * pjt 03sep98 fixed malloc(size+1) bug with interesting side-effects * on linux and HP-UX * pjt 5aug99 increased MAXSTRING to 1024 (also do keyf.f !!!) * pjt 6mar01 increased MAXSTRING to 2048 * mchw 15mar02 increased MAXSTRING to 4096 * pjt 22jun02 MIR4 prototypes, also added a few more Const * jwr 22jul04 changed a few vars from size_t to ssize_t, since signed * arithmetic is required. Also made failure of wildcard * expansion fatal (it would crash later if only a warning * is given) * pjt 13jul07 make unique messages in different pieces of code * pjt 12jun10 added keyputc_c for ATNF compatibility * dm/pjt 2dec10 better protection for keyword values overrun * keywrap.f2c is now calling keya_len_c() instead * deprecate keya_c() * pjt 21jul11 keyl_c() now calls keya_len_c() *********************************************************************** */ #if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "miriad.h" #ifndef Null #define Null '\0' #endif /* if you change MAXSTRING, also do keyf.for */ #define KEYTRUE 1 #define KEYFALSE 0 #define MAXSTRING 4096 typedef struct ckeys { char *key; /* Pointer to a malloc'd string holding the key name. */ char *Pvalue; /* Pointer to a malloc'd string holding the value. */ char *value; /* Pointer to current spot in Pvalue. */ int isexpanded; /* False if not yet expanded; true otherwise. */ int islocal; /* True if defined locally; false if globally. */ struct ckeys *fwd; /* Pointer to next ckey structure. */ } KEYS; static KEYS *KeyHead = (KEYS *)NULL; /* This will be set to KEYTRUE only when keyini[_c]() is called. */ static int iniCalled = KEYFALSE; /***********************************************************************/ static char *skipLeading(Const char *string) { char *ptr; if (string == (Const char *)NULL) return((char *)NULL); for (ptr = (char *)string; ((*ptr != Null) && isspace(*ptr)); ptr++) /* NULL */ ; return(ptr); } /***********************************************************************/ static KEYS *getKey(Const char *key) { char *ptr; KEYS *t; /* First, check that the key routines have been initialized. */ if (iniCalled == KEYFALSE) { (void)bug_c('f', "The Key initialization routine must be called first."); } /* * Search for a key by name. If the key name is not found, * return a NULL pointer. Otherwise, return a pointer to the * private structure of the key. */ if ((ptr = skipLeading(key)) == (char *)NULL) return((KEYS *)NULL); for (t = KeyHead; t != (KEYS *)NULL; t = t->fwd) if (strcmp(ptr, t->key) == 0) break; return(t); } /***********************************************************************/ static char *getKeyValue(Const char *key, int doexpand) { char *r, *s; char quoted; char string[MAXSTRING]; int more; ssize_t size, depth; KEYS *t; FILE *fp; if ((t = getKey(key)) == (KEYS *)NULL) return((char *)NULL); if ((t->value == (char *)NULL) || (*(t->value) == Null)) return((char *)NULL); /* * At this point, there is a value to return. Scan through to * the end of the parameter value. */ r = s = skipLeading(t->value); depth = 0; more = KEYTRUE; quoted = Null; /* Initially, not in a quoted string. */ while ((*s != Null) && (more == KEYTRUE)) { if (quoted == Null) { /* Not currently within a quote. */ if ((*s == '"') || (*s == '\'')) { quoted = *s; /* Set this to the char that ends the quote. */ } else { if (*s == '(') depth++; else if (*s == ')') depth--; else if (isspace(*s) || (*s == ',')) more = (depth == 0) ? KEYFALSE : KEYTRUE; } } else if (*s == quoted) { /* Inside a quote; read till matched. */ quoted = Null; /* Reset this to mean not in a quote. */ } if (more == KEYTRUE) s++; } t->value = (*s == Null) ? s : s + 1; *s-- = Null; /* Subtract 1 from index for following test. */ /* Remove leading and trailing quotes. */ if ((*r != Null) && (s > r)) { if (((*r == '"') && (*s == '"')) || ((*r == '\'') && (*s == '\''))) { *s = Null; r++; } } /* * If the value starts with a '@' character, then open the * given file name and store each line as a comma separated list. * Next, if there is anything left in the keyword value list, * add it to the end of the newly generated list. Finally, re-call * this routine to read the first parameter off the new list. */ if (*r == '@') { r++; if ((fp = fopen(r, "r")) == (FILE *)NULL) { (void)sprintf(string, "Error opening @ file [%s].", r); (void)bug_c('f', string); } more = KEYTRUE; while (fgets(string, MAXSTRING, fp) != (char *)NULL) { if (((size = strlen(string)) > (size_t)0) && (string[size-1] == '\n')) string[size-1] = Null; r = skipLeading(string); if ((r == (char *)NULL) || (*r == Null) || (*r == '#')) continue; if (more == KEYTRUE) { depth = strlen(r) + 1; if ((s = (char *)malloc(depth)) == (char *)NULL) (void)bug_c('f', "Could not allocate memory in the key routines."); (void)strcpy(s, r); more = KEYFALSE; } else { depth += strlen(r) + 2; if ((s = (char *)realloc(s, depth)) == (char *)NULL) (void)bug_c('f', "Could not allocate memory in the key routines."); (void)strcat(s, ","); (void)strcat(s, r); } } (void)fclose(fp); if (depth == 0) (void)bug_c('f', "Trouble processing the @ directive."); if (*(t->value) != Null) { depth += strlen(t->value) + 2; if ((s = (char *)realloc(s, depth)) == (char *)NULL) (void)bug_c('f', "Could not allocate memory in the key routines."); (void)strcat(s, ","); (void)strcat(s, t->value); } (void)free((Void *)t->Pvalue); t->value = t->Pvalue = s; t->isexpanded = KEYTRUE; r = getKeyValue(key, doexpand); } else if ((doexpand == KEYTRUE) && (t->isexpanded == KEYFALSE)) { /* * Otherwise, if expansion is desired and the keyword has not * yet been expanded, call the dio.c routine dexpand_c() to * return the result of the system call to "echo r". */ size = dexpand_c(r, string, MAXSTRING); if (size < 1) { (void)sprintf(string, "Error doing wildcard expansion of [%s].", r); (void)bug_c('f', string); } else { if (*(t->value) != Null) size += strlen(t->value) + 2; if ((s = (char *)malloc(size+1)) == (char *)NULL) (void)bug_c('f', "Could not allocate memory in the key routines."); (void)strcpy(s, string); if (*(t->value) != Null) { (void)strcat(s, ","); (void)strcat(s, t->value); } (void)free((Void *)t->Pvalue); t->value = t->Pvalue = s; t->isexpanded = KEYTRUE; } r = getKeyValue(key, doexpand); } return((*r == Null) ? (char *)NULL : r); } /***********************************************************************/ void keyinit_c(Const char *task) { buglabel_c(task); /* Let the bug routines know the task name. */ iniCalled = KEYTRUE; /* Is True only when keyini[_c]() is called. */ } /* hack to be able to use the ATNF fortran subroutine keyputc */ void keyputc_c(char *string) { keyput_c("unknown", string); } /***********************************************************************/ void keyput_c(Const char *task, char *string) /** KeyPut -- Store a keyword for later retrieval. */ /*& pjt */ /*: user-input,command-line */ /*+ FORTRAN call sequence: subroutine keyput(task,value) character task*(*),value*(*) This task stores the keyword=value pair for later retrieval by the other key routines. If the keyword has previously been saved prior to calling this routine, then this version will only be saved if (1) the previous was not defined locally (ie. task/keyword) or (2) this reference is also a local reference. If the keyword is locally defined (ie. task/keyword) but the task name does not match the value of the input string task, then the keyword is not (ever) saved. NOTE: This is an internal routine that is only called by KeyIni. Input: task The name of the current task. value The keyword=value pair. Output: (none) */ /*--*/ /*---------------------------------------------------------------------*/ { char *s, *key; char *pequal, *pslash; char errmsg[MAXSTRING]; int localkey; KEYS *t; if (iniCalled == KEYFALSE) { (void)bug_c('f', "The Key initialization routine must be called before calling KEYPUT."); } if (((s = skipLeading(string)) == (char *)NULL) || (*s == Null)) { (void)sprintf(errmsg, "Badly formed parameter-1: [%s].", string); (void)bug_c('w', errmsg); return; } else if (*s == '#') { /* Quietly return on comment lines. */ return; } key = s; /* Get the key name. */ while ((*s != Null) && (isalnum(*s) || (*s == '$'))) s++; if (*s == Null) { (void)sprintf(errmsg, "Badly formed parameter-2: [%s].", string); (void)bug_c('w', errmsg); return; } /* * Search for the local keyword character (/). If it exists, * it must appear before the equal sign with text that precedes * and follows it. If the key is local, the key and value will * be saved only if the task name matches the text before the * local flag character. */ localkey = KEYFALSE; if (((pslash = strchr(s, '/')) != (char *)NULL) && ((pequal = strchr(s, '=')) != (char *)NULL) && (pslash < pequal)) { *s = Null; /* Terminate the task name. */ if (strcmp(task, key) != 0) /* This keyword doesn't match task. */ return; s = skipLeading(pslash+1); /* Skip blanks after the local char. */ localkey = KEYTRUE; key = s; /* Now, get the real [local] key name. */ while ((*s != Null) && (isalnum(*s) || (*s == '$'))) s++; if (*s == Null) { (void)sprintf(errmsg, "Badly formed parameter-3: [%s].", string); (void)bug_c('w', errmsg); return; } } *s++ = Null; /* Properly terminate the keyword. */ /* Now move to the value part of this keyword. */ while ((*s != Null) && (isspace(*s) || (*s == '='))) s++; if ((*s == Null) || (strlen(s) < (size_t)1)) { (void)sprintf(errmsg, "Badly formed parameter-4: [%s=%s].", key, string); (void)bug_c('w', errmsg); return; } /* * See if this keyword already exists. If not, then create it. * If it exists and was not previously declared local, then * this version will override the previous reference of the key. * If this reference was previously defined locally then only * another local reference will override it. */ for (t = KeyHead; t != (KEYS *)NULL; t = t->fwd) { if (strcmp(key, t->key) == 0) break; } if (t == (KEYS *)NULL) { if ((t = (KEYS *)malloc(sizeof(KEYS))) == (KEYS *)NULL) (void)bug_c('f', "Could not allocate memory in the key routines."); if ((t->key = (char *)malloc(strlen(key) + 1)) == (char *)NULL) (void)bug_c('f', "Could not allocate memory in the key routines."); (void)strcpy(t->key, key); t->fwd = KeyHead; KeyHead = t; } else if ((localkey == KEYTRUE) || (t->islocal != KEYTRUE)) { if (t->Pvalue != (char *)NULL) (void)free((Void *)t->Pvalue); } else { return; } if ((t->Pvalue = (char *)malloc(strlen(s) + 1)) == (char *)NULL) (void)bug_c('f', "Could not allocate memory in the key routines."); (void)strcpy(t->Pvalue, s); t->value = t->Pvalue; t->isexpanded = KEYFALSE; t->islocal = localkey; return; } /***********************************************************************/ void keyini_c(int argc, char *argv[]) /** KeyIni_c -- Initialise the `key' routines (C version). */ /*& pjt */ /*: user-input, command-line */ /*+ void keyini_c(int argc, char *argv[]) Keyini_c performs some initial parsing of the command line breaking it up into its keyword=value pairs. It also stores the name of the program (which is currently only used by the bug routines). NOTE: This has a different calling sequence than the Fortran version. */ /*--*/ /*---------------------------------------------------------------------*/ { char *task; char string[MAXSTRING]; register int i; size_t size; FILE *fp; /* * Get the program name, and tell the bug routines what it * really is. Then strip off any leading path characters * so only the task name remains. */ keyinit_c(argv[0]); task = argv[0] + strlen(argv[0]) - 1; while ((task > argv[0]) && (strchr("]/", task[-1]) == (char *)NULL)) task--; for (i = 1; i < argc; i++) { if (strcmp("-f", argv[i]) == 0) { /* Read args from a file. */ if (++i >= argc) (void)bug_c('f', "KeyIni: No parameter file given for -f option."); if ((fp = fopen(argv[i], "r")) == (FILE *)NULL) { (void)sprintf(string, "KeyIni: Failed to open the parameter file [%s].", argv[i]); (void)bug_c('f', string); } while (fgets(string, MAXSTRING, fp) != (char *)NULL) { if (((size = strlen(string)) > (size_t)0) && (string[size-1] == '\n')) string[size-1] = Null; keyput_c(task, string); } (void)fclose(fp); } else if (strcmp("-?", argv[i]) == 0) { /* Give help. */ (void)sprintf(string, "mirhelp %s", task); (void)system(string); (void)exit(0); } else if (strcmp("-k", argv[i]) == 0) { /* List the keywords. */ (void)sprintf(string, "doc %s", task); (void)system(string); (void)exit(0); } else if (argv[i][0] == '-') { /* Others not understood yet. */ (void)sprintf(string, "KeyIni: Flag [%s] not understood.", argv[i]); (void)bug_c('w', string); } else { /* Otherwise, the argument is a parameter. */ keyput_c(task, argv[i]); } } return; } /***********************************************************************/ void keyfin_c(void) /** KeyFin -- Finish access to the 'key' routines. */ /*& pjt */ /*: user-input,command-line */ /*+ FORTRAN call sequence: subroutine keyfin A call to KeyFin indicates that all of the parameters that the program wants have been retrieved from the command line. KeyFin makes sure all command line parameters have been read. */ /*--*/ /*---------------------------------------------------------------------*/ { char errmsg[MAXSTRING]; KEYS *t, *next; if (iniCalled == KEYFALSE) { (void)bug_c('f', "The Key initialization routine must be called before calling KEYFIN."); } next = (KEYS *)NULL; for (t = KeyHead; t != (KEYS *)NULL; t = next) { next = t->fwd; if ((t->value != (char *)NULL) && (*(t->value) != Null)) { (void)sprintf(errmsg, "Keyword [%s] not used or not exhausted.", t->key); (void)bug_c('w', errmsg); } if (t->Pvalue != (char *)NULL) (void)free((Void *)t->Pvalue); if (t->key != (char *)NULL) (void)free((Void *)t->key); (void)free((Void *)t); } KeyHead = (KEYS *)NULL; iniCalled = KEYFALSE; return; } /***********************************************************************/ /* Returns FORT_TRUE if keyword is present; FORT_FALSE otherwise. */ int keyprsnt_c(Const char *keyword) /** KeyPrsnt -- Determine if a keyword is present on the command line. */ /*& pjt */ /*: user-input,command-line */ /*+ FORTRAN call sequence: logical function keyprsnt(key) character key*(*) Determine if a parameter is still present. Input: key The keyword to check. Output: keyprsnt Is .TRUE. if the keyword is present; .FALSE. otherwise. */ /*--*/ /*---------------------------------------------------------------------*/ { int isPresent; KEYS *t; t = getKey(keyword); isPresent = ((t != (KEYS *)NULL) && (t->value != (char *)NULL) && (*(t->value) != Null)) ? FORT_TRUE : FORT_FALSE; return(isPresent); } /***********************************************************************/ void keya_c(Const char *keyword, char *value, Const char *keydef) /** Keya -- Retrieve a character string from the command line. */ /*& pjt */ /*: user-input,command-line */ /*+ FORTRAN call sequence: subroutine keya(key,value,default) character key*(*) character value*(*),default*(*) Retrieve a character string from the command line. If the keyword is not found, the default is returned. Input: key The name of the keyword to return. default The default value to return if the keyword is not present on the command line. Output: value The returned value. */ /*--*/ /*---------------------------------------------------------------------*/ { char *s; bugv_c ('w', "KeyA: keyword \"%s\" length not checked", keyword); s = getKeyValue(keyword, KEYFALSE); (void)strcpy(value, ((s == (char *)NULL) ? keydef : s)); return; } void keya_len_c(Const char *keyword, char *value, size_t vlen, Const char *keydef) { char *s; s = getKeyValue(keyword, KEYFALSE); if (s && strlen (s) > vlen) bugv_c ('f', "KeyA: value \"%s\" of keyword \"%s\" is doesn\'t fit in its " "Fortran buffer, which is only %zd bytes.", s, keyword, vlen); if ((s==(char *)NULL) && strlen(keydef) > vlen) bugv_c ('f', "KeyA: default value \"%s\" of keyword \"%s\" is would not fit in its " "Fortran buffer, which is only %zd bytes.", keydef, keyword, vlen); (void)strncpy(value, ((s == (char *)NULL) ? keydef : s), vlen); return; } /***********************************************************************/ void keyf_c(Const char *keyword, char *value, Const char *keydef) /** Keyf -- Retrieve a file name (with wildcards) from the command line. */ /*& pjt */ /*: user-input,command-line */ /*+ FORTRAN call sequence: subroutine keyf(key,value,default) character key*(*) character value*(*),default*(*) Retrieve a character string from the command line. If the keyword is not found, the default is returned. Input: key The name of the keyword to return. default The default value to return if the keyword is not present on the command line. Output: value The returned value. */ /*--*/ /*---------------------------------------------------------------------*/ { char *s; /* Expand any wildcards and match them with files. */ s = getKeyValue(keyword, KEYTRUE); (void)strcpy(value, ((s == (char *)NULL) ? keydef : s)); return; } /***********************************************************************/ void keyd_c(Const char *keyword, double *value, Const double keydef) /** Keyd -- Retrieve a double precision from the command line. */ /*& pjt */ /*: user-input,command-line */ /*+ FORTRAN call sequence: subroutine keyd(key,value,default) character key*(*) double precision value,default Retrieve a double precision value from the command line. If the keyword is not found, the default is returned. Input: key The name of the keyword to return. default The default value to return, if the keyword is not present on the command line. Output: value The returned value. */ /*--*/ /*---------------------------------------------------------------------*/ { char *s, *ptr; char errmsg[MAXSTRING]; *value = keydef; if ((s = getKeyValue(keyword, KEYFALSE)) == (char *)NULL) return; ptr = (char *)NULL; *value = strtod(s, &ptr); if (s == ptr) { (void)sprintf(errmsg, "KeyD: Conversion error decoding parameter [%s=%s].", keyword, s); (void)bug_c('f', errmsg); } return; } /***********************************************************************/ void keyr_c(Const char *keyword, float *value, Const float keydef) /** Keyr -- Retrieve a real value from the command line. */ /*& pjt */ /*: user-input,command-line */ /*+ FORTRAN call sequence: subroutine keyr(key,value,default) character key*(*) real value,default Retrieve a real value from the command line. If the keyword is not found, the default is returned. Input: key The name of the keyword to return. default The default value to return, if the keyword is not present on the command line. Output: value The returned value. */ /*--*/ /*---------------------------------------------------------------------*/ { double retval, defval; defval = keydef; keyd_c(keyword, &retval, defval); *value = retval; return; } /***********************************************************************/ void keyi_c(Const char *keyword, int *value, Const int keydef) /** Keyi -- Retrieve an integer from the command line. */ /*& pjt */ /*: user-input,command-line */ /*+ FORTRAN call sequence: subroutine keyi(key,value,default) character key*(*) integer value,default Retrieve an integer from the command line. If the keyword is not found, the default is returned. The integer can be input as a hexadecimal, octal or decimal number using a prefix 0x, %x or h for hex; o or %o for octal; and +, - or nothing for decimal. Input: key The name of the keyword to return. default The default value to return, if the keyword is not present on the command line. Output: value The returned value. */ /*--*/ /*---------------------------------------------------------------------*/ { char *s, *ptr; char temp[MAXSTRING]; int iarg, dummy; double dval; *value = keydef; if ((s = getKeyValue(keyword, KEYFALSE)) == (char *)NULL) return; /* * This business is done instead of a call to strtol because * hexadecimal and octal are also acceptable integer inputs. */ (void)sprintf(temp, "%s~~1", s); if ((sscanf(temp, "%i~~%d", &iarg, &dummy) == 2) && (dummy == 1)) { *value = iarg; /* Token was just a simple integer. */ return; } /* Number is floating point; find it and then take nint(). */ ptr = (char *)NULL; dval = strtod(s, &ptr); if (s == ptr) { (void)sprintf(temp, "KeyI: Conversion error decoding parameter [%s=%s].", keyword, s); (void)bug_c('f', temp); } *value = (dval >= 0) ? floor(dval + 0.5) : ceil(dval - 0.5); return; } /***********************************************************************/ void keyl_c(Const char *keyword, int *value, Const int keydef) /** keyl -- Retrieve a logical value from the command line. */ /*& pjt */ /*: user-input,command-line */ /*+ FORTRAN call sequence: subroutine keyl(key,value,default) character key*(*) logical value,default Retrieve a logical value from the command line. If the keyword is not found, the default is returned. It associates (case insensitive) words starting with 'y', 't' and '1' as .TRUE. and words starting with 'n', 'f' and '0' as .FALSE. Values of .TRUE. and .FALSE. are also detected... both with minimum match. Input: key The name of the keyword to return. default The default value to return, if the keyword is not present on the command line. Output: value The returned value. */ /*--*/ /*---------------------------------------------------------------------*/ { char string[MAXSTRING]; char errmsg[MAXSTRING]; int state; if (keydef == FORT_FALSE) { keya_len_c(keyword, string, MAXSTRING, "f"); state = KEYFALSE; } else { keya_len_c(keyword, string, MAXSTRING, "t"); state = KEYTRUE; } (void)sprintf(errmsg, "KeyL: invalid value for a logical: [%s].", string); switch ((int)string[0]) { case 'f': case 'F': case 'n': case 'N': case '0': state = KEYFALSE; break; case 't': case 'T': case 'y': case 'Y': case '1': state = KEYTRUE; break; case '.': switch ((int)string[1]) { case 'f': case 'F': state = KEYFALSE; break; case 't': case 'T': state = KEYTRUE; break; default: (void)bug_c('w', errmsg); break; } break; default: (void)bug_c('w', errmsg); break; } *value = (state == KEYTRUE) ? FORT_TRUE : FORT_FALSE; return; } /***********************************************************************/ /* * The following macro is used by all of the subsequent multi-get * routines. It assumes that: * * Const char *keyword is the name of the key to retrieve; * T value[] is the array to receive each returned value; * int nmax is the maximum number of items to get; and * int *n is the number of items returned; * * where T is the type of the variable (char *, double, float, or int). * * In the macro below, * task is the name of the individual item retrieval task; * defval is the default value to be used if the keyword is missing; and * name is a string representing the task name (to be used in * an error message. * * The do {} while (1==0) construct is done so that the macro may * be called like a regular subroutine. */ #define MULTIGET(task,defval,name) \ do { \ char errmsg[MAXSTRING]; \ register int count = 0; \ \ while ((count < nmax) && (keyprsnt_c(keyword) == FORT_TRUE)) \ task(keyword, &value[count++], defval); \ \ if (keyprsnt_c(keyword) == FORT_TRUE) { \ (void)sprintf(errmsg, "%s: Buffer overflow for keyword [%s].", \ name, keyword); \ (void)bug_c('f', errmsg); \ } \ \ *n = count; \ } while (1==0) /***********************************************************************/ void mkeyd_c(Const char *keyword, double value[], Const int nmax, int *n) /** MKeyd -- Retrieve multiple double values from the command line. */ /*& pjt */ /*: user-input,command-line */ /*+ FORTRAN call sequence: subroutine mkeyd(key,value,nmax,n) integer nmax, n character key*(*) double precision value(nmax) Retrieve multiple double precision values from the command line. If the keyword is not found, then zero values are returned. Input: key The name of the keyword to return. nmax The maximum number of values to return Output: n The number of values returned. value The returned values */ /*--*/ /*---------------------------------------------------------------------*/ { MULTIGET(keyd_c, 0.0, "MKeyD"); return; } /***********************************************************************/ void mkeyr_c(Const char *keyword, float value[], Const int nmax, int *n) /** MKeyr -- Retrieve multiple real values from the command line. */ /*& pjt */ /*: user-input,command-line */ /*+ FORTRAN call sequence: subroutine mkeyr(key,value,nmax,n) integer nmax, n character key*(*) real value(nmax) Retrieve multiple real values from the command line. If the keyword is not found, then zero values are returned. Input: key The name of the keyword to return. nmax The maximum number of values to return Output: n The number of values returned. value The returned values */ /*--*/ /*---------------------------------------------------------------------*/ { MULTIGET(keyr_c, 0.0, "MKeyR"); return; } /***********************************************************************/ void mkeyi_c(Const char *keyword, int value[], Const int nmax, int *n) /** MKeyi -- Retrieve multiple integer values from the command line. */ /*& pjt */ /*: user-input,command-line */ /*+ FORTRAN call sequence: subroutine mkeyi(key,value,nmax,n) integer nmax, n character key*(*) integer value(nmax) Retrieve multiple integer values from the command line. If the keyword is not found, then zero values are returned. Input: key The name of the keyword to return. nmax The maximum number of values to return Output: n The number of values returned. value The returned values */ /*--*/ /*---------------------------------------------------------------------*/ { MULTIGET(keyi_c, 0, "MKeyI"); return; } #ifdef TESTBED /***********************************************************************/ int main(int argc, char *argv[]) { char aval[100]; register int i; int ival; float fval; double cnt[5]; keyini_c(argc, argv); mkeyd_c("cnt", cnt, 5, &ival); for (i = 0; i < ival; i++) (void)printf("cnt[%d] = %g\n", i+1, cnt[i]); for (i = 0; i < 5; i++) { keyi_c("alpha", &ival, 100); (void)printf("Alpha[100] = %d ", ival); keya_c("beta", aval, "def-val"); (void)printf("Beta[def-val] = %s ", aval); keyr_c("gamma", &fval, 10.0); (void)printf("Gamma[10.0] = %g\n", fval); } keyfin_c(); (void)exit(0); } #endif casacore-3.7.1/mirlib/maskio.c000066400000000000000000000333631476623553700162410ustar00rootroot00000000000000/************************************************************************/ /* */ /* A package of routines to read and write masks (bitmaps) */ /* These are used by MIRIAD for data flagging and blanking. */ /* */ /* Masks are implemented as integer data items. The 32nd */ /* bit in the integer is not used, as this could cause */ /* portability problems. As it is, this software assumes that */ /* the host integer is at least 32 bits long. */ /* */ /* History: */ /* rjs Dark-ages Original version. */ /* rjs 6nov89 Does not abort if the mask file is missing. */ /* rjs 3mar93 Make mkflush a user-callable routine. */ /* rjs 23dec93 Do not open in read/write mode unless necessary. */ /* rjs 6nov94 Change item handle to an integer. */ /* rjs 19apr97 Handle FORTRAN LOGICALs better. Some tidying. */ /* rjs 03jan05 Tidying. */ /* pjt 7jan10 yes, and fixed 64bit offsets (not lengths!!!) */ /* pjt feb12 added masking support, getmaski,setmaski */ /************************************************************************/ #if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "miriad.h" /* #include "io.h" */ #define BUG(sev,a) bug_c(sev,a) #define ERROR(sev,a) bug_c(sev,((void)sprintf a,message)) #define CHECK(x) if(x) bugno_c('f',x) static char message[128]; #define BITS_PER_INT 31 static int bits[BITS_PER_INT] = { 0x00000001,0x00000002,0x00000004,0x00000008, 0x00000010,0x00000020,0x00000040,0x00000080, 0x00000100,0x00000200,0x00000400,0x00000800, 0x00001000,0x00002000,0x00004000,0x00008000, 0x00010000,0x00020000,0x00040000,0x00080000, 0x00100000,0x00200000,0x00400000,0x00800000, 0x01000000,0x02000000,0x04000000,0x08000000, 0x10000000,0x20000000,0x40000000}; static int masks[BITS_PER_INT+1]={ 0x00000000,0x00000001,0x00000003,0x00000007,0x0000000F, 0x0000001F,0x0000003F,0x0000007F,0x000000FF, 0x000001FF,0x000003FF,0x000007FF,0x00000FFF, 0x00001FFF,0x00003FFF,0x00007FFF,0x0000FFFF, 0x0001FFFF,0x0003FFFF,0x0007FFFF,0x000FFFFF, 0x001FFFFF,0x003FFFFF,0x007FFFFF,0x00FFFFFF, 0x01FFFFFF,0x03FFFFFF,0x07FFFFFF,0x0FFFFFFF, 0x1FFFFFFF,0x3FFFFFFF,0x7FFFFFFF}; #include "io.h" #define MK_FLAGS 1 #define MK_RUNS 2 #define BUFFERSIZE 128 #define OFFSET (((ITEM_HDR_SIZE-1)/H_INT_SIZE + 1)*BITS_PER_INT) /* off_t is used for size and length as well because mixed arithmetic with */ /* size_t and off_t gives no end of trouble */ typedef struct { int item; int buf[BUFFERSIZE]; off_t offset,length,size; int modified,rdonly,tno; char name[32]; } MASK_INFO; private void mkfill(MASK_INFO *mask,off_t offset); /************************************************************************/ char *mkopen_c(int tno,char *name,char *status) /* This opens a mask item, and readies it for access. Inputs: tno The handle of the data set containing the item. name Name of the item (something like "mask" or "flags"). status "old" or "new". Output: mkopen_c This is a handle used in subsequent calls to the maskio routines. A NULL indicates that an error was encountered. ------------------------------------------------------------------------*/ { MASK_INFO *mask; int iostat; char s[ITEM_HDR_SIZE]; mask = (MASK_INFO *)malloc((unsigned int)sizeof(MASK_INFO)); /* The case of an old mask file. Perform a number of checks to make sure the file looks OK. */ if(!strcmp("old",status)) { haccess_c(tno,&(mask->item),name,"read",&iostat); if(iostat) { free((char *)mask); return(NULL); } mask->size = hsize_c(mask->item); if(mask->size <= H_INT_SIZE * (OFFSET/BITS_PER_INT)) ERROR('f',(message,"Mask file %s appears bad",name)); hreadb_c(mask->item,s,0,ITEM_HDR_SIZE,&iostat); CHECK(iostat); if(memcmp(s,int_item,ITEM_HDR_SIZE)) ERROR('f',(message,"Mask file %s is not integer valued",name)); mask->rdonly = TRUE; /* The case of a new masl file. Create it and make it look nice. */ } else if(!strcmp("new",status)) { haccess_c(tno,&(mask->item),name,"write",&iostat); CHECK(iostat); hwriteb_c(mask->item,int_item,0,ITEM_HDR_SIZE,&iostat); CHECK(iostat); mask->size = OFFSET/BITS_PER_INT * H_INT_SIZE; mask->rdonly = FALSE; } else ERROR('f',(message,"Unrecognised status %s in MKOPEN",status)); /* Common to both old and new mask files. Initialise the structure describing the file. */ mask->size = (mask->size/H_INT_SIZE) * BITS_PER_INT; mask->offset = -BUFFERSIZE*BITS_PER_INT; mask->length = 0; mask->modified = FALSE; mask->tno = tno; strcpy(mask->name,name); return((char *)mask); } /************************************************************************/ void mkclose_c(char *handle) /* This writes out any stuff that we have buffered up, and then closes the mask file. Inputs: handle Pointer to the structure describing the massk file. ------------------------------------------------------------------------*/ { MASK_INFO *mask; int iostat; mask = (MASK_INFO *)handle; if(mask->modified) mkflush_c(handle); hdaccess_c(mask->item,&iostat); CHECK(iostat); free((char *)mask); } /************************************************************************/ int mkread_c(char *handle,int mode,int *flags,off_t offset,int n,int nsize) /* ------------------------------------------------------------------------*/ { #define SWITCH_STATE *flags++ = runs + (state ? 0 : 1 ); \ t = state; state = otherstate; otherstate = t MASK_INFO *mask; off_t i,boff,t,len,blen; int bitmask,*buf,iostat,state,otherstate,runs; int *flags0; flags0 = flags; mask = (MASK_INFO *)handle; offset += OFFSET; state = 0; otherstate = 0x7FFFFFFF; runs = 0; /* Get a buffer full of information if we need it. */ while(n > 0){ if(offset < mask->offset || offset >= mask->offset + mask->length) { if(mask->modified)mkflush_c(handle); mask->offset = (offset/BITS_PER_INT)*BITS_PER_INT; mask->length = min(mask->size - mask->offset,BUFFERSIZE*BITS_PER_INT); mask->modified = FALSE; if(mask->length == 0) BUG('f',"Read past end of mask file"); hreadi_c(mask->item,mask->buf, (mask->offset/BITS_PER_INT)*H_INT_SIZE, (mask->length/BITS_PER_INT)*H_INT_SIZE, &iostat); CHECK(iostat); } /* Copy the flags to the output buffer. Use special sections of code to deal with all bits being set of clear. */ boff = offset - mask->offset; t = boff/BITS_PER_INT; buf = mask->buf + t; len = min(mask->length - boff,n); boff -= t*BITS_PER_INT; n -= len; offset += len; /* Copy to the output, in "flags" format. */ if(mode == MK_FLAGS){ while( len > 0){ blen = min( BITS_PER_INT - boff,len); bitmask = *buf++; if(bitmask == 0x7FFFFFFF) for(i=0; i 0){ blen = min( BITS_PER_INT - boff,len); bitmask = *buf++; if(bitmask == state ) runs += blen; else if(bitmask == otherstate ) { SWITCH_STATE; runs += blen; } else { for(i=boff; irdonly){ hdaccess_c(mask->item,&iostat); haccess_c(mask->tno,&(mask->item),mask->name,"append",&iostat); if(iostat){ bug_c('w',"Error opening mask/flagging file in read/write mode\n"); bugno_c('f',iostat); } mask->rdonly = FALSE; } /* Check if we have the right buffer. Flush if not. */ while(n > 0){ if(offset < mask->offset || offset >= mask->offset + BUFFERSIZE*BITS_PER_INT) { if(mask->modified)mkflush_c(handle); mask->offset = (offset/BITS_PER_INT)*BITS_PER_INT; mask->length = 0; mask->modified = FALSE; } /* See if we have to read in any stuff to fill in between the last write and the current write. */ if(offset > mask->offset + mask->length)mkfill(mask,offset); /* Copy the flags to the output buffer. */ boff = offset - mask->offset; t = boff/BITS_PER_INT; buf = mask->buf + t; len = min(BITS_PER_INT*BUFFERSIZE - boff,n); boff -= t*BITS_PER_INT; mask->length = max(mask->length,offset - mask->offset + len); mask->modified = TRUE; n -= len; offset += len; /* Write to the file, assuming the input is in FLAGS format. */ if(mode == MK_FLAGS){ while( len > 0){ blen = min( BITS_PER_INT - boff,len); bitmask = *buf; for(i=boff; i 0 ){ while(run == 0){ if(nsize == 0) run = n + len; else{ t = *flags++ - (state ? 1 : 0); run = t - curr; curr = t; nsize --; } state ^= 1; } blen = min(run, min( BITS_PER_INT - boff, len)); bitmask = masks[boff+blen] ^ masks[boff]; if(state) *buf |= bitmask; /* Set the bits. */ else *buf &= ~bitmask; /* Clear the bits. */ run -= blen; len -= blen; boff = (boff + blen) % BITS_PER_INT; if(!boff) buf++; } } } } /************************************************************************/ void mkflush_c(char *handle) /* Flush out the data in the buffer. A complication is that the last integer in the buffer may not be completely filled. In this case we have to read in the value of this integer, and copy the old bits to the output. Input: mask Pointer to the mask structure. ------------------------------------------------------------------------*/ { MASK_INFO *mask; off_t offset; int i; int t,*buf,iostat; mask = (MASK_INFO *)handle; /* If we are writing at the end of the file, make sure the number we write is a multiple of BITS_PER_INT. Also update the size of the file. */ if( mask->offset + mask->length >= mask->size) { mask->length = ((mask->length-1)/BITS_PER_INT + 1)*BITS_PER_INT; mask->size = mask->offset + mask->length; /* If the last word is only partially filled, read in the rest of the word and transfer the bits. */ } else if((mask->length % BITS_PER_INT) != 0) { offset = (mask->offset + mask->length) / BITS_PER_INT * H_INT_SIZE; hreadi_c(mask->item,&t,offset,H_INT_SIZE,&iostat); CHECK(iostat); buf = mask->buf + mask->length/BITS_PER_INT; i = mask->length % BITS_PER_INT; *buf = ( t & ~masks[i] ) | (*buf & masks[i]); mask->length = ((mask->length-1)/BITS_PER_INT + 1)*BITS_PER_INT; } /* Write out the stuff at last. */ hwritei_c(mask->item,mask->buf, mask->offset/BITS_PER_INT*H_INT_SIZE, mask->length/BITS_PER_INT*H_INT_SIZE,&iostat); CHECK(iostat); mask->modified = FALSE; } /************************************************************************/ void getmaski_c(Const int mask, int *masks) /* Decode an integer into an array of integers representing each bit. Due to lack of support for masking operations in Fortran-77. User is responsible for properly allocating enough space in masks[] The sign bit is not used. ------------------------------------------------------------------------*/ { int i,n, m; n = 8*sizeof(int) - 1; /* skip the sign bit */ for (i=0, m=1; ioffset+mask->length < mask->size) { buf = mask->buf + mask->length/BITS_PER_INT; t = *buf; off = (mask->offset + mask->length)/BITS_PER_INT; len = min(offset/BITS_PER_INT + 1,mask->size/BITS_PER_INT) - off; hreadi_c(mask->item,buf, off*H_INT_SIZE,len*H_INT_SIZE,&iostat); CHECK(iostat); i = mask->length % BITS_PER_INT; *buf = ( t & masks[i] ) | (*buf & ~masks[i]); mask->length = (off + len)*BITS_PER_INT - mask->offset; } } casacore-3.7.1/mirlib/maxdimc.h000066400000000000000000000020311476623553700163710ustar00rootroot00000000000000/* ------------------------------------------------------------- maxdimc.h - include file for C code containing MIRIAD-wide parameters MAXDIM .... maximum number of elements in any one plane (ie, maximum dimensionality of a map) MAXANT .... maximum number of antennae MAXBASE ... maximum number of baselines MAXCHAN ... maximum number of channels in spectral data History: 04aug91 mjs Original version. 05aug91 mjs Put parentheses around MAXBASE defined value. bpw 20jul91 Created as xyzio.h mjs 08apr92 Minor mod to compile on VAX rjs 23feb93 Merged maxdimc.h and xyzio.h. Include MAXNAX. rjs 9sep94 Add MAXWIN pjt 30apr01 re-aligned maxdimc and maxdim mhw 07may14 Increase limits to 64 bit values ------------------------------------------------------------- */ #define MAXBUF 16777216 #define MAXDIM 32768 #define MAXANT 64 #define MAXBASE ((MAXANT * (MAXANT + 1)) / 2) #define MAXCHAN 70000 #define MAXNAX 7 #define MAXWIN 48 #define MAXWIDE 18 casacore-3.7.1/mirlib/miriad.h000066400000000000000000000417741476623553700162350ustar00rootroot00000000000000/* // Header that gives C++ code access to the MIRIAD IO routines // Is now also used a global prototype header file for all C routines // // History: // // 21-oct-94 pjt Created as follows: // cproto uvio.c hio.c dio.c bug.c pack.c headio.c maskio.c > miriad.h // and edited to please the C++ compiler and the eye: // // 22-oct-94 pjt added hio.h definitions - clumsy .... pjt // (the hio routines are macros we don't want to allow that here) // // fall-96 pjt minor tidying up for AIPS++ bimafiller release - pjt // 13-dec-96 rjs Regenerated, using // cproto -I$MIRINC -p -fARGS hio.c headio.c uvio.c xyio.c xyzio.c bug.c // 07-feb-97 rjs Added "Const" to definitions, and eliminate some rubbish, as // suggested by Scott Gordon. // 19-Mar-97 rjs Check for definition of various thingos before doing them. // 15-jun-01 pjt Added key.c for BIMA version (ATNF uses keyc.c) as well as // the uvget* uvrd* macros from uvio.c // 28-may-02 pjt LFS patches: make it for for > 2GB file using off_t/size_t (prep for MIR4) // 17-jun-02 pjt added interface.c routines; now used as global prototype file // 23-jun-02 pjt define MIR4 here if you want to enable the LSF and MIR4 // 30-aug-04 pjt removed deprecated ARGS() macro // 1-dec-05 pjt added bugv_c // 18-may-06 pjt/df added mir.c prototypes for mir (the miriad->mir converter) // 12-feb-09 dhem added bughandler_c prototype (new function in bug.c) // 01-apr-09 rjs Add additional interface to scrRecSz // 7-jan-10 pjt re-aligned ATNF and CARMA code // 14-dec-11 pkgw Declare errmsg_c() */ #if !defined(MIR_MIRIAD_H) #define MIR_MIRIAD_H /* comment this out if you only handle data < 2GB and need to be compatible with old MIRIAD */ /* or simply define MIR3 through compile options */ #if !defined(MIR3) #define MIR4 #endif #include /* provides off_t */ #include #include #include "sysdep.h" /* since it now contains the "pack.c" prototypes */ /* Define const and void if needed. */ #ifndef MIRIAD_TYPES_DEFINED #define MIRIAD_TYPES_DEFINED 1 #ifdef __STDC__ #if (__STDC__ == 1) typedef void Void; #define Const const #else typedef char Void; #define Const /* NULL */ #endif /* (__STDC__ == 1) */ #else typedef char Void; #define Const /* NULL */ #endif /* __STDC__ */ #if !defined(__cplusplus) # define private static #endif /* __cplusplus */ /* Define the argument list if needed. */ #if !defined(ARGS) # if defined(__STDC__) || defined(__cplusplus) # define ARGS(s) s # else # define ARGS(s) () # endif #endif #endif #if defined(__cplusplus) extern "C" { #endif /* hio.h */ #if !defined(TRUE) # define TRUE 1 #else # if TRUE != 1 # error "TRUE should be 1" # endif #endif #if !defined(FALSE) # define FALSE 0 #else # if FALSE != 0 # error "FALSE should be 0" # endif #endif #define H_BYTE 1 #define H_INT 2 #define H_INT2 3 #define H_REAL 4 #define H_DBLE 5 #define H_TXT 6 #define H_CMPLX 7 #define H_INT8 8 /* hio.c */ void hopen_c(int *tno, Const char *name, Const char *status, int *iostat); void hflush_c(int tno, int *iostat); void habort_c(void); void hrm_c(int tno); void hclose_c(int tno); void hdelete_c(int tno, Const char *keyword, int *iostat); void haccess_c(int tno, int *ihandle, Const char *keyword, Const char *status, int *iostat); void hmode_c(int tno, char *mode); int hexists_c(int tno, Const char *keyword); void hdaccess_c(int ihandle, int *iostat); off_t hsize_c(int ihandle); void hio_c(int ihandle, int dowrite, int type, char *buf, off_t offset, size_t length, int *iostat); void hseek_c(int ihandle, off_t offset); off_t htell_c(int ihandle); void hreada_c(int ihandle, char *line, size_t length, int *iostat); void hwritea_c(int ihandle, Const char *line, size_t length, int *iostat); /* Macros defined in hio.c */ #define hreadb_c(item,buf,offset,length,iostat) \ hio_c(item,FALSE,H_BYTE,buf,offset,length,iostat) #define hwriteb_c(item,buf,offset,length,iostat) \ hio_c(item,TRUE,H_BYTE,buf,offset,length,iostat) #define hreadi_c(item,buf,offset,length,iostat) \ hio_c(item,FALSE,H_INT,(char *)(buf),offset,length,iostat) #define hwritei_c(item,buf,offset,length,iostat) \ hio_c(item,TRUE,H_INT,(char *)(buf),offset,length,iostat) #define hreadj_c(item,buf,offset,length,iostat) \ hio_c(item,FALSE,H_INT2,(char *)(buf),offset,length,iostat) #define hwritej_c(item,buf,offset,length,iostat) \ hio_c(item,TRUE,H_INT2,(char *)(buf),offset,length,iostat) #define hreadl_c(item,buf,offset,length,iostat) \ hio_c(item,FALSE,H_INT8,(char *)(buf),offset,length,iostat) #define hwritel_c(item,buf,offset,length,iostat) \ hio_c(item,TRUE,H_INT8,(char *)(buf),offset,length,iostat) #define hreadr_c(item,buf,offset,length,iostat) \ hio_c(item,FALSE,H_REAL,(char *)(buf),offset,length,iostat) #define hwriter_c(item,buf,offset,length,iostat) \ hio_c(item,TRUE,H_REAL,(char *)(buf),offset,length,iostat) #define hreadd_c(item,buf,offset,length,iostat) \ hio_c(item,FALSE,H_DBLE,(char *)(buf),offset,length,iostat) #define hwrited_c(item,buf,offset,length,iostat) \ hio_c(item,TRUE,H_DBLE,(char *)(buf),offset,length,iostat) #define hreadc_c(item,buf,offset,length,iostat) \ hio_c(item,FALSE,H_CMPLX,(char *)(buf),offset,length,iostat) #define hwritec_c(item,buf,offset,length,iostat) \ hio_c(item,TRUE,H_CMPLX,(char *)(buf),offset,length,iostat) #define hwrite_c(item,type,buf,offset,length,iostat) \ hio_c(item,TRUE,type,(char *)(buf),offset,length,iostat) #define hread_c(item,type,buf,offset,length,iostat) \ hio_c(item,FALSE,type,(char *)(buf),offset,length,iostat) /* headio.c */ void hisopen_c (int tno, Const char *status); void hiswrite_c (int tno, Const char *text); void hisread_c (int tno, char *text, size_t length, int *eof); void hisclose_c (int tno); void wrhdr_c (int tno, Const char *keyword, double value); void wrhdd_c (int tno, Const char *keyword, double value); void wrhdi_c (int tno, Const char *keyword, int value); void wrhdl_c (int tno, Const char *keyword, int8 value); void wrhdc_c (int tno, Const char *keyword, Const float *value); void wrhda_c (int tno, Const char *keyword, Const char *value); void rdhdr_c (int tno, Const char *keyword, float *value, double defval); void rdhdi_c (int tno, Const char *keyword, int *value, int defval); void rdhdl_c (int tno, Const char *keyword, int8 *value, int8 defval); void rdhdd_c (int tno, Const char *keyword, double *value, double defval); void rdhdc_c (int tno, Const char *keyword, float *value, Const float *defval); void rdhda_c (int tno, Const char *keyword, char *value, Const char *defval, int len); void hdcopy_c (int tin, int tout, Const char *keyword); int hdprsnt_c (int tno, Const char *keyword); void hdprobe_c (int tno, Const char *keyword, char *descr, size_t length, char *type, int *n); /* dio.c */ void ddelete_c (char *path, int *iostat); void dtrans_c (char *inpath, char *outpath, int *iostat); void dmkdir_c (char *path, int *iostat); void drmdir_c (char *path, int *iostat); void dopen_c (int *fd, char *name, char *status, off_t *size, int *iostat); void dclose_c (int fd, int *iostat); void dread_c (int fd, char *buffer, off_t offset, size_t length, int *iostat); void dwrite_c (int fd, char *buffer, off_t offset, size_t length, int *iostat); void dwait_c (int fd, int *iostat); int dexpand_c (char *tmplte, char *output, int length); void dopendir_c (char **contxt, char *path); void dclosedir_c (char *contxt); void dreaddir_c (char *contxt, char *path, int length); /* uvio.c */ void uvopen_c (int *tno, Const char *name, Const char *status); void uvclose_c (int tno); void uvflush_c (int tno); void uvnext_c (int tno); void uvrewind_c (int tno); int uvdim_c (int tno); void uvcopyvr_c (int tin, int tout); int uvupdate_c (int tno); void uvvarini_c (int tno, int *vhan); void uvvarset_c (int vhan, Const char *var); void uvvarcpy_c (int vhan, int tout); int uvvarupd_c (int vhan); void uvrdvr_c (int tno, int type, Const char *var, char *data, const char *def, int n); void uvgetvr_c (int tno, int type, Const char *var, char *data, int n); void uvprobvr_c (int tno, Const char *var, char *type, int *length, int *updated); void uvputvr_c (int tno, int type, Const char *var, Const char *data, int n); void uvtrack_c (int tno, Const char *name, Const char *switches); int uvscan_c (int tno, Const char *var); void uvwrite_c (int tno, Const double *preamble, Const float *data, Const int *flags, int n); void uvwwrite_c (int tno, Const float *data, Const int *flags, int n); void uvsela_c (int tno, Const char *object, Const char *string, int datasel); void uvselect_c (int tno, Const char *object, double p1, double p2, int datasel); void uvset_c (int tno, Const char *object, Const char *type, int n, double p1, double p2, double p3); void uvread_c (int tno, double *preamble, float *data, int *flags, int n, int *nread); void uvwread_c (int tno, float *data, int *flags, int n, int *nread); void uvflgwr_c (int tno, Const int *flags); void uvwflgwr_c (int tno, Const int *flags); void uvinfo_c (int tno, Const char *object, double *data); int uvchkshadow_c (int tno, double diameter_meters); /* Macros defined in uvio.c */ #define uvputvra_c(tno,name,value) \ uvputvr_c(tno,H_BYTE,name,value,strlen(value)) #define uvputvrj_c(tno,name,value,n) \ uvputvr_c(tno,H_INT2,name,(char *)(value),n) #define uvputvri_c(tno,name,value,n) \ uvputvr_c(tno,H_INT,name,(char *)(value),n) #define uvputvrr_c(tno,name,value,n) \ uvputvr_c(tno,H_REAL,name,(char *)(value),n) #define uvputvrd_c(tno,name,value,n) \ uvputvr_c(tno,H_DBLE,name,(char *)(value),n) #define uvputvrc_c(tno,name,value,n) \ uvputvr_c(tno,H_CMPLX,name,(char *)(value),n) #define uvgetvra_c(tno,name,value,n) \ uvgetvr_c(tno,H_BYTE,name,value,n) #define uvgetvrj_c(tno,name,value,n) \ uvgetvr_c(tno,H_INT2,name,(char *)(value),n) #define uvgetvri_c(tno,name,value,n) \ uvgetvr_c(tno,H_INT,name,(char *)(value),n) #define uvgetvrr_c(tno,name,value,n) \ uvgetvr_c(tno,H_REAL,name,(char *)(value),n) #define uvgetvrd_c(tno,name,value,n) \ uvgetvr_c(tno,H_DBLE,name,(char *)(value),n) #define uvgetvrc_c(tno,name,value,n) \ uvgetvr_c(tno,H_CMPLX,name,(char *)(value),n) #define uvrdvra_c(tno,name,data,def,len) \ uvrdvr_c(tno,H_BYTE,name,data,def,len) #define uvrdvri_c(tno,name,data,def) \ uvrdvr_c(tno,H_INT,name,(char *)(data),(char *)(def),1) #define uvrdvrr_c(tno,name,data,def) \ uvrdvr_c(tno,H_REAL,name,(char *)(data),(char *)(def),1) #define uvrdvrd_c(tno,name,data,def) \ uvrdvr_c(tno,H_DBLE,name,(char *)(data),(char *)(def),1) #define uvrdvrc_c(tno,name,data,def) \ uvrdvr_c(tno,H_CMPLX,name,(char *)(data),(char *)(def),1) /* xyio.c */ void xyopen_c (int *tno, Const char *name, Const char *status, int naxis, int axes[]); void xyflush_c (int tno); void xyclose_c (int tno); int xydim_c (int tno); void xyread_c (int tno, int index, float *array); void xywrite_c (int tno, int index, Const float *array); void xymkrd_c (int tno, int index, int *runs, int n, int *nread); void xymkwr_c (int tno, int index, Const int *runs, int n); void xyflgwr_c (int tno, int index, Const int *flags); void xyflgrd_c (int tno, int index, int *flags); void xysetpl_c (int tno, int naxis, Const int *axes); /* maskio.c */ char *mkopen_c (int tno, char *name, char *status); void mkclose_c (char *handle); int mkread_c (char *handle, int mode, int *flags, off_t offset, int n, int nsize); void mkwrite_c (char *handle, int mode, Const int *flags, off_t offset, int n, int nsize); void mkflush_c (char *handle); void setmaski_c(int *mask, Const int *masks); void getmaski_c(Const int mask, int *masks); /* xyzio.c */ void xyzopen_c (int *tno, Const char *name, Const char *status, int *naxis, int axlen[]); void xyzclose_c (int tno); void xyzflush_c (int tno); void xyzsetup_c (int tno, Const char *subcube, Const int blc[], Const int trc[], int viraxlen[], long vircubesize[]); void xyzs2c_c (int tno, long subcubenr, int coords[]); void xyzc2s_c (int tno, Const int coords[], long *subcubenr); void xyzread_c (int tno, Const int coords[], float *data, int *mask, int *ndata); void xyzpixrd_c (int tno, long pixelnr, float *data, int *mask); void xyzprfrd_c (int tno, int profilenr, float *data, int *mask, int *ndata); void xyzplnrd_c (int tno, int planenr, float *data, int *mask, int *ndata); void xyzwrite_c (int tno, Const int coords[], Const float *data, Const int *mask, Const int *ndata); void xyzpixwr_c (int tno, long pixelnr, Const float *data, Const int *mask); void xyzprfwr_c (int tno, int profilenr, Const float *data, Const int *mask, Const int *ndata); void xyzplnwr_c (int tno, int planenr, Const float *data, Const int *mask, Const int *ndata); void xyzmkbuf_c (void); void xyzdim_c (int tno, int *naxis, int *dimsub); int xyzpix_c (int tno, int dims); /* bug.c */ char bugseverity_c(void); char *bugmessage_c(void); void bughandler_c(void (*handler)(char s, Const char *m)); void bugrecover_c(void (*cl)(void)); void buglabel_c (Const char *name); void bugno_c (char s, int n); char *errmsg_c (int n); void bug_c (char s, Const char *m); void bugv_c (char s, Const char *m, ...); /* scrio.c */ void scropen_c (int *handle); void scrclose_c (int handle); void scrread_c (int handle, float *buffer, off_t offset, size_t length); void scrwrite_c (int handle, Const float *buffer, off_t offset, size_t length); void scrrecsz_c (int handle, size_t recsize); /* tabio.c */ void tabopen_c (int *tno, Const char *name, Const char *status, int *ncol, int *nrow); void tabclose_c (int tno); void tabsetr_c (int tno, int row); void tabfmtc_c (int tno, int col, char *fmt); void tabcmt_c (int tno, char *comment); void tabwcr_c (int tno, int col, float value); void tabwcd_c (int tno, int col, double value); void tabwci_c (int tno, int col, int value); void tabwca_c (int tno, int col, char *value); void tabgetr_c (int tno, int row, float *data); void tabgetd_c (int tno, int row, double *data); void tabgeta_c (int tno, int row, char *data); /* key.c */ void keyinit_c (Const char *task); void keyput_c (Const char *task, char *string); void keyini_c (int argc, char *argv[]); void keyfin_c (void); int keyprsnt_c (Const char *keyword); void keya_c (Const char *keyword, char *value, Const char *keydef); void keyf_c (Const char *keyword, char *value, Const char *keydef); void keyd_c (Const char *keyword, double *value, Const double keydef); void keyr_c (Const char *keyword, float *value, Const float keydef); void keyi_c (Const char *keyword, int *value, Const int keydef); void keyl_c (Const char *keyword, int *value, Const int keydef); void mkeyd_c (Const char *keyword, double value[], Const int nmax, int *n); void mkeyr_c (Const char *keyword, float value[], Const int nmax, int *n); void mkeyi_c (Const char *keyword, int value[], Const int nmax, int *n); /* mir.c */ void mirInit_c(const char *f_name); void mirClose_c(void); void inWrite_c(const int conid, const int icocd, const int traid, const int inhid, const int ints, const int itq, const float az, const float el, const float ha, const int iut, const int iref_time, const double dhrs, const float vc, const int ivctype, const double sx, const double sy, const double sz, const float rinteg, const int proid, const int souid, const int isource, const int ipos, const float offx, const float offy, const int iofftype, const int ira, const int idec, const double rar, const double decr, const float epoch, const float sflux, const float size); void blWrite_c(const int blhid, const int inhid, const int isb, const int ipol, const float pa, const int iaq, const int ibq, const int icq, const int ioq, const int irec, const int iffc, const float u, const float v, const float w, const float prbl, const float angres, const float vis, const float coh, const float sigcoh, const float csnr, const float vflux, const float cnoise, const double avedhrs, const float ampav, const float phaave, const float tpvar, const int blsid, const int itel1, const int itel2, const int iblcd, const float ble, const float bln, const float blu, const int soid); void spWrite_c(const int sphid, const int blhid, const int inhid, const int igq, const int ipq, const int iband, const int ipstate, const float tau0, const double vel, const float vres, const int ivtype, const double fsky, const float fres, const float tssb, const float integ, const float wt, const int itaper, const float snoise, const int nch, const int nrec, const int dataoff, const int linid, const int itrans, const double rfreq, const int pasid, const int gaiidamp, const int gaiidpha, const int flcid, const int atmid); void codeWrite_c(const char *v_name, const int icode, const char *code, const int ncode); void visWrite_c(const float *re, const float *im, const int numvis, const int startvis, int *nbytes); /* interface.c */ void pad(char *string, int length); char *zterm(char *string, int length); #if defined(__cplusplus) } #endif #endif /* MIR_MIRIAD_H */ casacore-3.7.1/mirlib/pack.c000066400000000000000000000442101476623553700156650ustar00rootroot00000000000000/* pack */ /* & pjt */ /* : low-level-i/o */ /* + */ /* */ /* This converts data between disk and internal */ /* format. Disk format is IEEE reals and 16 or 32 */ /* bit integers (most significant byte first). */ /* */ /* This assumes that these are the local machine format */ /* (float == IEEE real, int == 32 bit integer, */ /* short int == 16 bit integer). */ /* */ /* packx_c, unpackx_c, pack32_c and unpack32_c are */ /* implemented as macros (calling bcopy) in the */ /* system dependent include file. */ /*-- */ /* */ /* History: */ /* rjs Dark-ages Original version. */ /* bs ?????89 Improved efficiency using "register" declarations. */ /* rjs 1nov89 Incoporated Brian's changes. */ /* mjs 28feb91 Merge Sun and Cray versions. */ /* mjs 18mar91 Added convex definition. */ /* mjs 19feb93 Added mips definition. */ /* pjt 25jan95 linux kludge to include packALPHA.c */ /* pjt 14jun01 packALPHA.c now included in this source code */ /* and using the standard WORDS_BIGENDIAN macro */ /* pjt 21jun02 MIR4 prototyping */ /************************************************************************/ #if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H #include "config.h" #endif #include "sysdep.h" #include "miriad.h" #if defined(WORDS_BIGENDIAN) static int words_bigendian = 1; /* never used actually, but handy symbol to find via nm(1) */ void pack16_c(register int *from,char *to,int n) { register short int *tto; register int i; tto = (short int *)to; for (i=0; i < n; i++) *tto++ = *from++; } void unpack16_c(char *from,register int *to,int n) { register short int *ffrom; register int i; ffrom = (short int *)from; for (i=0; i < n; i++) *to++ = *ffrom++; } void pack64_c(register int *from,char *to,int n) { register short int *tto; register int i; tto = (short int *)to; for (i=0; i < n; i++) *tto++ = *from++; } void unpack64_c(char *from,register int *to,int n) { register short int *ffrom; register int i; ffrom = (short int *)from; for (i=0; i < n; i++) *to++ = *ffrom++; } #endif #ifndef WORDS_BIGENDIAN #ifndef unicos static int words_littleendian = 1; /* never used actually, but handy symbol to find via nm(1) */ /************************************************************************/ /* */ /* The pack routines -- these convert between the host format and */ /* the disk format. Disk format is IEEE 32 and 64 bit reals, and 2's */ /* complement integers. Byte order is the FITS byte order (most */ /* significant bytes first). */ /* */ /* This version is for a machine which uses IEEE internally, but which */ /* uses least significant bytes first (little endian), e.g. PCs and */ /* Alphas. */ /* */ /* History: */ /* rjs 21nov94 Original version. */ /************************************************************************/ void pack16_c(int *in,char *out,int n) /* Pack an integer array into 16 bit integers. ------------------------------------------------------------------------*/ { int i; char *s; s = (char *)in; for(i=0; i < n; i++){ *out++ = *(s+1); *out++ = *s; s += sizeof(int); } } /************************************************************************/ void unpack16_c(char *in,int *out,int n) /* Unpack an array of 16 bit integers into integers. ------------------------------------------------------------------------*/ { int i; char *s; s = (char *)out; for(i=0; i < n; i++){ *s++ = *(in+1); *s++ = *in; if(0x80 & *in){ *s++ = 0xFF; *s++ = 0xFF; } else { *s++ = 0; *s++ = 0; } in += 2; } } /************************************************************************/ void pack32_c(int *in,char *out,int n) /* Pack an array of integers into 32 bit integers. ------------------------------------------------------------------------*/ { int i; char *s; s = (char *)in; for(i = 0; i < n; i++){ *out++ = *(s+3); *out++ = *(s+2); *out++ = *(s+1); *out++ = *s; s += 4; } } /************************************************************************/ void unpack32_c(char *in,int *out,int n) /* Unpack an array of 32 bit integers into integers. ------------------------------------------------------------------------*/ { int i; char *s; s = (char *)out; for(i = 0; i < n; i++){ *s++ = *(in+3); *s++ = *(in+2); *s++ = *(in+1); *s++ = *in; in += 4; } } /************************************************************************/ void pack64_c(int8 *in,char *out,int n) /* Pack an integer array into 64 bit integers. ------------------------------------------------------------------------*/ { int i; char *s; s = (char *)in; for(i=0; i < n; i++){ *out++ = *(s+7); *out++ = *(s+6); *out++ = *(s+5); *out++ = *(s+4); *out++ = *(s+3); *out++ = *(s+2); *out++ = *(s+1); *out++ = *s; s += 8; } } /************************************************************************/ void unpack64_c(char *in,int8 *out,int n) /* Unpack an array of 64 bit integers into integers. ------------------------------------------------------------------------*/ { int i; char *s; s = (char *)out; for(i=0; i < n; i++){ *s++ = *(in+7); *s++ = *(in+6); *s++ = *(in+5); *s++ = *(in+4); *s++ = *(in+3); *s++ = *(in+2); *s++ = *(in+1); *s++ = *in; in += 8; } } /************************************************************************/ void packr_c(float *in,char *out,int n) /* Pack an array of reals into IEEE reals -- just do byte reversal. ------------------------------------------------------------------------*/ { int i; char *s; s = (char *)in; for(i = 0; i < n; i++){ *out++ = *(s+3); *out++ = *(s+2); *out++ = *(s+1); *out++ = *s; s += 4; } } /************************************************************************/ void unpackr_c(char *in,float *out,int n) /* Unpack an array of IEEE reals into reals -- just do byte reversal. ------------------------------------------------------------------------*/ { int i; char *s; s = (char *)out; for(i = 0; i < n; i++){ *s++ = *(in+3); *s++ = *(in+2); *s++ = *(in+1); *s++ = *in; in += 4; } } /************************************************************************/ void packd_c(double *in,char *out,int n) /* Pack an array of doubles -- this involves simply performing byte reversal. ------------------------------------------------------------------------*/ { int i; char *s; s = (char *)in; for(i = 0; i < n; i++){ *out++ = *(s+7); *out++ = *(s+6); *out++ = *(s+5); *out++ = *(s+4); *out++ = *(s+3); *out++ = *(s+2); *out++ = *(s+1); *out++ = *s; s += 8; } } /************************************************************************/ void unpackd_c(char *in,double *out,int n) /* Unpack an array of doubles -- this involves simply performing byte reversal. ------------------------------------------------------------------------*/ { int i; char *s; s = (char *)out; for(i = 0; i < n; i++){ *s++ = *(in+7); *s++ = *(in+6); *s++ = *(in+5); *s++ = *(in+4); *s++ = *(in+3); *s++ = *(in+2); *s++ = *(in+1); *s++ = *in; in += 8; } } #endif #endif #if defined(unicos) static int words_unicos = 1; #define TWO15 0x8000 #define TWO16 0x10000 #define TWO31 0x80000000 #define TWO32 0x100000000 #define HILONG 0xFFFFFFFF00000000 #define LOLONG 0x00000000FFFFFFFF #define WORD0 0x000000000000FFFF #define WORD1 0x00000000FFFF0000 #define WORD2 0x0000FFFF00000000 #define WORD3 0xFFFF000000000000 /* Masks for IEEE floating format (both hi and lo types). */ #define IEEE_HISIGN 0x8000000000000000 #define IEEE_HIEXPO 0x7F80000000000000 #define IEEE_HIMANT 0x007FFFFF00000000 #define IEEE_LOSIGN 0x0000000080000000 #define IEEE_LOEXPO 0x000000007F800000 #define IEEE_LOMANT 0x00000000007FFFFF #define IEEE_DMANT 0x000FFFFFFFFFFFF0 #define IEEE_DEXPO 0x7FF0000000000000 /* Masks for Cray floating format. */ #define CRAY_MANT 0x0000FFFFFF000000 /* Including unhidden bit. */ #define CRAY_MANT1 0x00007FFFFF000000 /* No unhidden bit. */ #define CRAY_DMANT 0x0000FFFFFFFFFFFF #define CRAY_DMANT1 0x00007FFFFFFFFFFF #define CRAY_EXPO 0x7FFF000000000000 #define SIGN 0x8000000000000000 /* Mask of a pointer to char giving the character offset in a Cray word. */ #define CHAR_OFFSET 0xE000000000000000 /************************************************************************/ void pack16_c(int *in,char *out,int n) /* Pack an integer array into 16 bit integers for unicos ------------------------------------------------------------------------*/ { int temp,offset,*outd,in1,in2,in3,in4,i; if(n <= 0)return; /* Return if nothing to do. */ temp = (int)out; offset = ( temp & CHAR_OFFSET ) >> 62; /* Get offset of first word. */ outd = (int *)(temp & ~CHAR_OFFSET); /* Get address of words. */ /* Handle the first few which are not aligned on a Cray word. */ switch(offset){ case 1: *outd = (*outd & ~WORD2) | ((*in++ << 32) & WORD2); if(--n == 0)break; case 2: *outd = (*outd & ~WORD1) | ((*in++ << 16) & WORD1); if(--n == 0)break; case 3: *outd = (*outd & ~WORD0) | ((*in++ ) & WORD0); outd++; } /* Handle the ones which are aligned on a Cray word. */ for(i=0; i < n-3; i=i+4){ in1 = *in++ << 48; in2 = *in++ << 32; in3 = *in++ << 16; in4 = *in++; *outd++ = (in1 & WORD3) | (in2 & WORD2) | (in3 & WORD1) | (in4 & WORD0); } n -= i; /* Handle the last ones which are not aligned on a Cray word. */ if(n-- > 0){ *outd = (*outd & ~WORD3) | ((*in++ << 48) & WORD3); if(n-- > 0){ *outd = (*outd & ~WORD2) | ((*in++ << 32) & WORD2); if(n-- > 0){ *outd = (*outd & ~WORD1) | ((*in++ << 16) & WORD1); } } } } /************************************************************************/ void unpack16_c(char *in,int *out,int n) /* Unpack an array of 16 bit integers into integers for unicos ------------------------------------------------------------------------*/ { int temp,offset,*ind,i; if(n <= 0)return; /* Return if nothing to do. */ temp = (int)in; offset = ( temp & CHAR_OFFSET ) >> 62; /* Get offset of first word. */ ind = (int *)(temp & ~CHAR_OFFSET); /* Get address of words. */ /* Handle the first few which are not word aligned. */ switch(offset){ case 1: temp = (*ind >> 32) & WORD0; *out++ = (temp < TWO15 ? temp : temp - TWO16); if(--n == 0) break; case 2: temp = (*ind >> 16) & WORD0; *out++ = (temp < TWO15 ? temp : temp - TWO16); if(--n == 0) break; case 3: temp = (*ind++ ) & WORD0; *out++ = (temp < TWO15 ? temp : temp - TWO16); if(--n == 0) break; } /* Handle those that are Cray-word-aligned. */ for(i=0; i < n-3; i=i+4){ temp = (*ind >> 48) & WORD0; *out++ = (temp < TWO15 ? temp : temp - TWO16); temp = (*ind >> 32) & WORD0; *out++ = (temp < TWO15 ? temp : temp - TWO16); temp = (*ind >> 16) & WORD0; *out++ = (temp < TWO15 ? temp : temp - TWO16); temp = (*ind++ ) & WORD0; *out++ = (temp < TWO15 ? temp : temp - TWO16); } n -= i; /* Handle the last few which are not Cray-word-aligned. */ if(n-- > 0){ temp = (*ind >> 48) & WORD0; *out++ = (temp < TWO15 ? temp : temp - TWO16); if(n-- > 0){ temp = (*ind >> 32) & WORD0; *out++ = (temp < TWO15 ? temp : temp - TWO16); if(n-- > 0){ temp = (*ind >> 16) & WORD0; *out++ = (temp < TWO15 ? temp : temp - TWO16); } } } } /************************************************************************/ void pack32_c(int *in,char *out,int n) /* Pack an array of integers into 32 bit integers for unicos ------------------------------------------------------------------------*/ { int temp,offset,*outd,i,in1,in2; if(n <= 0)return; /* Return if nothing to do. */ temp = (int)out; offset = ( temp & CHAR_OFFSET ) >> 63; /* Get offset of first long. */ outd = (int *)(temp & ~CHAR_OFFSET); /* Get address of words. */ /* Do the first one, if it is not aligned on a Cray word. */ if(offset==1){ *outd = (*outd & ~LOLONG) | (*in++ & LOLONG); outd++; } n -= offset; /* Do those which are Cray word aligned. */ for(i=0; i < n-1; i=i+2){ in1 = *in++ << 32; in2 = *in++; *outd++ = (in1 & HILONG) | (in2 & LOLONG); } n -= i; /* Handle the last one, if there is one. */ if(n==1)*outd = (*outd & ~HILONG) | ((*in++ << 32) & HILONG); } /************************************************************************/ void unpack32_c(char *in,int *out,int n) /* Unpack an array of 32 bit integers into integers for unicos ------------------------------------------------------------------------*/ { int temp,offset,*ind,i; if(n <= 0)return; /* Return if nothing to do. */ temp = (int)in; offset = ( temp & CHAR_OFFSET ) >> 63; /* Get offset of first word. */ ind = (int *)(temp & ~CHAR_OFFSET); /* Get address of words. */ /* Handle one which is not Cray word aligned. */ if(offset==1){ temp = (*ind++ & LOLONG); *out++ = (temp < TWO31 ? temp : temp - TWO32); } n -= offset; /* Handle those which are Cray word aligned. */ for(i=0; i < n-1; i=i+2){ temp = (*ind >> 32) & LOLONG; *out++ = (temp < TWO31 ? temp : temp - TWO32); temp = (*ind++ ) & LOLONG; *out++ = (temp < TWO31 ? temp : temp - TWO32); } n -= i; /* Possibly handle a last one which is not Cray word aligned. */ if(n==1){ temp = (*ind >> 32) & LOLONG; *out++ = (temp < TWO31 ? temp : temp - TWO32); } } /************************************************************************/ void packr_c(float *in,char *out,int n) /* Pack an array of Cray reals into IEEE reals. ------------------------------------------------------------------------*/ { int temp,offset,*outd,bias,*ind,tin,tout,i; if(n <= 0)return; /* Return if nothing to do. */ temp = (int)out; offset = ( temp & CHAR_OFFSET ) >> 63; /* Get offset of first long. */ outd = (int *)(temp & ~CHAR_OFFSET); /* Get address of words. */ bias = (16384 - 126) << 48; ind = (int *)in; /* Do the first one, if it is not aligned on a Cray word. */ if(offset==1){ tin = *ind++; *outd = (*outd & ~LOLONG) | (tin & CRAY_MANT ? (((tin & CRAY_EXPO)-bias) >> 25) | ((tin & CRAY_MANT1) >> 24) | ((tin & SIGN) >> 32) : 0); outd++; } n -= offset; /* Do those which are Cray word aligned. */ for(i=0; i < n-1; i=i+2){ tin = *ind++; tout = (tin & CRAY_MANT ? (((tin & CRAY_EXPO)-bias) << 7) | ((tin & CRAY_MANT1) << 8) | (tin & SIGN) : 0); tin = *ind++; *outd++ = tout | (tin & CRAY_MANT ? (((tin & CRAY_EXPO)-bias) >> 25) | ((tin & CRAY_MANT1) >> 24) | ((tin & SIGN) >> 32) : 0); } n -= i; /* Handle the last one, if there is one. */ if(n==1){ tin = *ind; *outd = (*outd & ~HILONG) | (tin & CRAY_MANT ? (((tin & CRAY_EXPO)-bias) << 7) | ((tin & CRAY_MANT1) << 8) | (tin & SIGN) : 0); } } /************************************************************************/ void unpackr_c(char *in,float *out,int n) /* Unpack an array of IEEE reals into Cray reals. ------------------------------------------------------------------------*/ { int temp,tin,*ind,*outd,offset,i,bias; if(n <= 0)return; /* Return if nothing to do. */ temp = (int)in; offset = ( temp & CHAR_OFFSET ) >> 63; /* Get offset of first word. */ ind = (int *)(temp & ~CHAR_OFFSET); /* Get address of words. */ outd = (int *)out; bias = ((16384-126) <<48) + (1 << 47); /* Handle the first one if it is not aligned on a Cray word. */ if(offset==1){ tin = *ind++; *outd++ = (tin & IEEE_LOEXPO ? (((tin & IEEE_LOEXPO) << 25)+bias) | ((tin & IEEE_LOMANT) << 24) | ((tin & IEEE_LOSIGN) << 32) : 0); } n -= offset; /* Handle the bulk of them that are aligned on Cray words. */ for(i=0; i < n-1; i=i+2){ tin = *ind++; *outd++ = (tin & IEEE_HIEXPO ? (((tin & IEEE_HIEXPO) >> 7)+bias) | ((tin & IEEE_HIMANT) >> 8 ) | (tin & IEEE_HISIGN) : 0); *outd++ = (tin & IEEE_LOEXPO ? (((tin & IEEE_LOEXPO) << 25)+bias) | ((tin & IEEE_LOMANT) << 24) | ((tin & IEEE_LOSIGN) << 32) : 0); } n -= i; /* Handle the last one, if needed, which is not aligned on a Cray word. */ if(n==1){ tin = *ind; *outd++ = (tin & IEEE_HIEXPO ? (((tin & IEEE_HIEXPO) >> 7)+bias) | ((tin & IEEE_HIMANT) >> 8 ) | (tin & IEEE_HISIGN) : 0); } } /************************************************************************/ void packd_c(double *in,char *out,int n) /* Pack an array of Cray reals into IEEE double precision. This assumes that a "double" and a "float" are identical. ------------------------------------------------------------------------*/ { int *ind,*outd,bias,i,tin; ind = (int *)in; outd = (int *)out; bias = (16384 - 1022) << 48; for(i=0; i < n; i++){ tin = *ind++; *outd++ = (tin & CRAY_DMANT ? (tin & SIGN) | (((tin & CRAY_EXPO)-bias) << 4) | ((tin & CRAY_DMANT1) << 5) : 0 ); } } /************************************************************************/ void unpackd_c(char *in,double *out,int n) /* Unpack an array of IEEE double precision numbers into Cray reals. This assumes that a "double" and a "float" are identical. ------------------------------------------------------------------------*/ { int *ind,*outd,bias,i,tin; ind = (int *)in; outd = (int *)out; bias = ((16384 - 1022) << 48) | (1 << 47); for(i=0; i < n; i++){ tin = *ind++; *outd++ = (tin & IEEE_DEXPO ? (tin & SIGN) | (((tin & IEEE_DEXPO) >> 4) + bias) | ((tin & IEEE_DMANT) >> 5) : 0 ); } } #endif casacore-3.7.1/mirlib/scrio.c000066400000000000000000000137251476623553700160750ustar00rootroot00000000000000/************************************************************************/ /* */ /* A collection of routines to manipulate scratch files. */ /* */ /* History: */ /* rjs Dark-ages Original version. */ /* rjs 6nov94 Change item handle to an integer. */ /* rjs 26oct95 Better messages on errors. */ /* pjt 19jun02 MIR4 prototypes */ /* jwr 05nov04 Change file offsets to type off_t */ /* rjs 03jan05 Include file rationalisation. */ /* pjt 16feb07 Minor doc improvements */ /* pjt 11dec07 More helpful message when scratch files fail */ /* rjs 01apr09 Add scrRecSz routine and associated work. */ /* rjs 13may09 Make returned handle always positive (some tasks have*/ /* relied on this). */ /* pjt 7jan09 Merged in previous CARMA changes, long live CVS */ /************************************************************************/ #if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H #include "config.h" #endif #include #include "miriad.h" #include "io.h" #define MAXITEMS 100 static int number=0; static int items[MAXITEMS],first; size_t recsiz[MAXITEMS]; /************************************************************************/ void scropen_c(int *handle) /**scropen -- Open a scratch file. */ /*:scratch-i/o */ /*+ FORTRAN call sequence: subroutine scropen(tno) integer tno This opens a scratch file, and readies it for use. Scratch files will be removed when they are closed, multiple scratch files are allowed, and they always live in the current directory, unless the $TMPDIR environment variable points to another directory. Output: tno The handle of the scratch file. */ /*-- */ /*----------------------------------------------------------------------*/ { int iostat,temp,i; char name[32]; /* Initialise the first time through. */ if(number == 0){ for(i=0;i #include #ifndef Null #define Null '\0' #endif /* * Void is typedef'd to the proper word depending on the level of * ANSI conformance. Also, if ANSI conforming, Const is defined * to const; otherwise, Const is defined as a NULL statement. * * PROTOTYPE is defined only if function prototypes are correctly * understood. * * ARGS defines a macro that aides in presenting prototypes. * Use it as (double parentheses required): * extern void keyput_c ARGS((const char *task, char *arg)); */ #ifndef MIRIAD_TYPES_DEFINED #define MIRIAD_TYPES_DEFINED 1 #ifdef __STDC__ #if (__STDC__ == 1) typedef void Void; #define Const const #define PROTOTYPE 1 #define ARGS(s) s #else typedef char Void; #define Const /* NULL */ #define ARGS(s) () #endif /* (__STDC__ == 1) */ #else typedef char Void; #define Const /* NULL */ #define ARGS(s) () #endif /* __STDC__ */ #if !defined(__cplusplus) #define private static #endif /* __cplusplus */ #endif /* MIRIAD_TYPES_DEFINED */ typedef int int2; typedef long long int int8; /************************************************************************/ /* */ /* UNICOS definitions */ /* */ /************************************************************************/ #ifdef unicos #include #define FORT_TRUE _btol(1) #define FORT_FALSE _btol(0) #define FORT_LOGICAL(a) (_ltob((&(a)))) #define BUFDBUFF 0 #define BUFALIGN 8 #define BUFSIZE 16384 #define defined_params #endif /************************************************************************/ /* */ /* UNIX definitions. */ /* */ /************************************************************************/ #ifndef defined_params #if defined(convex) || defined(alpha) || defined(__alpha) # define FORT_TRUE -1 #else # define FORT_TRUE 1 #endif #define FORT_FALSE 0 #define FORT_LOGICAL(a) ((a) != FORT_FALSE) #define BUFDBUFF 0 #define BUFALIGN 2 #define BUFSIZE 16384 /* Some machines have the "strerror" routine. Linux whinges significantly if you use the "old" way of doing effectively what strerror does. */ /* strerror is POSIX and should be supported under any POSIX.1 system */ /* Moving check for strerror into configure steps to define HAVE_STRERROR */ /* left old style build compatible check in bug.c */ /* Short cut routines when no conversion is necessary. These are used for any IEEE floating point machine with FITS ordered bytes. WORDS_BIGENDIAN is also defined though the 'autoconf' package and should appear in config.h if it's used (sun's, linuxppc, etc.) two routines, pack16_c() and unpack16_c() are actually defined in pack.c */ #ifndef WORDS_BIGENDIAN # if defined (sun) || defined (convex) || defined (mips) || defined(sgi) || defined(hpux) # define WORDS_BIGENDIAN # endif # if defined(PPC) || defined(powerpc) || defined(darwin_ppc) # define WORDS_BIGENDIAN # endif #endif #if defined(i386) #undef WORDS_BIGENDIAN #endif #ifdef WORDS_BIGENDIAN # define packr_c(a,b,c) memcpy((b),(char *)(a),sizeof(float)*(c)) # define unpackr_c(a,b,c) memcpy((char *)(b),(a),sizeof(float)*(c)) # define packd_c(a,b,c) memcpy((b),(char *)(a),sizeof(double)*(c)) # define unpackd_c(a,b,c) memcpy((char *)(b),(a),sizeof(double)*(c)) # define pack32_c(a,b,c) memcpy((b),(char *)(a),sizeof(int)*(c)) # define unpack32_c(a,b,c) memcpy((char *)(b),(a),sizeof(int)*(c)) void pack16_c(int *in, char *out, int n); void unpack16_c(char *in, int *out, int n); #else #if 1 void pack16_c(int *in, char *out, int n); void unpack16_c(char *in, int *out, int n); void pack32_c(int *in, char *out, int n); void unpack32_c(char *in, int *out, int n); void pack64_c(int8 *in, char *out, int n); void unpack64_c(char *in, int8 *out, int n); void packr_c(float *in, char *out, int n); void unpackr_c(char *in, float *out, int n); void packd_c(double *in, char *out, int n); void unpackd_c(char *in, double *out, int n); #endif #endif #endif #endif /* MIR_SYSDEP_H */ casacore-3.7.1/mirlib/uvio.c000066400000000000000000005105701476623553700157400ustar00rootroot00000000000000/************************************************************************/ /* Bugs: */ /* God only knows how many inconsistencies and bugs are left! */ /* */ /* History: */ /* rjs ??????? Original version. */ /* rjs 16aug89 Fixed bug, in uvread, of initialisation. */ /* rjs 2oct89 Fixed need for nschan,restfreq for wide-only file. */ /* rjs 18oct89 UV data selection code. Better planet treatment. Tidied */ /* uvread. */ /* rjs 1nov89 Fixed bug with the trueval array. */ /* rjs 2nov89 Fixed bug when calculating velocity channels, and the */ /* "amplitude", "phase", "real" and "imaginary" linetypes. */ /* rjs 7nov89 Fixed bug in uvread_velocity, when velocity increment */ /* is negative. Better error checking in UVSET. Bug with */ /* window selection. */ /* rjs 8nov89 Allowed you to select just 1 visibility in uvselect. */ /* rjs 9nov89 Allow negative step parameter for velocity linetype. */ /* Check for variable names greater than MAXNAM chars. */ /* rjs 13nov89 Made uvnext so that it handles OLD as well as NEW files,*/ /* as advertised. */ /* rjs 2feb90 Added uvoverride, and modified uvscan, to implement the */ /* capacity to override values of variables. */ /* rjs 7feb90 If not linetype is set, uvread_init checks for both corr*/ /* and wcorr before deciding on the default linetype. Added*/ /* lots of comments! */ /* rjs 13feb90 Modified uvinfo to handle object='bandwidth'. */ /* rjs 12mar90 Significant mods to the uv selection stuff. Modified */ /* uvinfo to handle object='frequency'. */ /* rjs 23mar90 Fixed minor bug in uvselect. */ /* rjs 27mar90 Fixed a bug where the UVF_COPY and UVF_UPDATE flags */ /* where not properly set, when uvselect was skipping some */ /* data. */ /* rjs/bpw 6apr90 Greater control over amplitude selectoin. */ /* pjt 8apr90 Made the CHECK macro more user understandable. rjs */ /* re-installed the changes into this version. */ /* rjs 10apr90 Wide flags, uvwread and uvwwrite. Added first_chan and */ /* first_wind to the uv structure. */ /* rjs 23apr90 Incorporated and enhanced pjt changes. Corrected a */ /* comment. */ /* rjs 24apr90 Fixed bug in uvread_select for RA selection. */ /* rjs 25apr90 Enhancements to uvscan. */ /* pjt 14aug90 Peter's TESTBED code. */ /* rjs 16oct90 Improved uv_override somewhat. Changed uv selection */ /* so that ant1 can equal ant2 (autocorrelation data). */ /* rjs 17oct90 Handle selection by polarization and "on". */ /* rjs 2nov90 Improved uv_override somewhat more. */ /* rjs 14dec90 Fixed bug in uvread_select, when dra and ddec parameters*/ /* are missing, but dra and ddec selection used. */ /* rjs 8feb91 Fixed integer overflow problems in uvread_amp, and */ /* amp flagging of H_CMPLX data. */ /* rjs 11feb91 Added PJT's "testbed" (so he can sleep in comfort?), */ /* uvselect for time treats a "Julian date" of less than 1 */ /* as specifying the current day. Better checking for */ /* uninitialised variables. Added the uvmark routine. Some */ /* changes in uvread to fix a potential bug when wcorr and */ /* corr do not always appear in the same record. */ /* Allow dra,ddec selection, when dra,ddec missing. */ /* Yet more work on uv_override. Corrected select=inc(1). */ /* Bug when selecting non-existent channels. Added ability */ /* to get sfreq from uvinfo. */ /* pjt 28feb91 Added record count to TESTBED - declared uvopen_c() etc */ /* rjs 1mar91 Corrected bug in uvmark. */ /* rjs 5mar91 Fixed error in calculation of uvinfo(...,'sfreq',...) */ /* uvwrite writes out u,v,t,bl only when needed. */ /* Changed definitions of TESTBED offsets. */ /* rjs 11mar91 Write out variables, in uvputvr, only when they really */ /* change. Fixed two more bugs, which lint discovered. */ /* rjs 19mar91 Added some more comments, and discovered a bug in uvinfo*/ /* rjs 21mar91 Improved error message in uvgetvr. */ /* rjs 22mar91 Just comments. */ /* rjs 27mar91 More comments. Routine uvwflgwr. */ /* rjs 16apr91 Added shadowing to uvselect. Always writes wcorr in */ /* in uvwwrite. */ /* rjs 18apr91 Change to uvset(...'corr'...) */ /* rjs 29may91 Corrected planet scaling, in uvread_updated() */ /* rjs 12jun91 Changed calculation of "restfreq" of wide channels. */ /* What sense does it make? */ /* rjs 19jun91 Corrected shadowing calculation. */ /* rjs 5aug91 Improved some error messages a bit. */ /* mjs 05aug91 Replaced hardcoded MAXANTS by maxdimc.h MAXANT */ /* rjs 09oct91 Uvwread returns nread=0 if there are no wides, to */ /* appease pjt. */ /* rjs 22nov91 select=auto selects autocorrelations only. */ /* rjs 13dec91 Added uvopen(..,'append') status. */ /* rjs 9dec91 Minor enhancement to uvwrite, to handle case of */ /* preamble variables being written somewhere else. */ /* rjs 10jan92 Slight mod to uvwrite, to account for lgm program. */ /* rjs 25mar92 Selection based on the frequency of the first channel */ /* and the source name. */ /* rjs 6apr92 Added specifying flags in runs form. The Convex found */ /* found where I had forgotten a semi-colon. */ /* rjs 12jun92 select=window makes sense for line=channel. */ /* rjs 10jul92 one of the checks for linetype validity was incorrectly */ /* too stringent. */ /* mchw 29jul92 removed step int8 */ /* pjt 13may03 (04?) MAXIANT usage to limit MAXANT */ /* pjt 03jan05 fix last few int -> size_t/off_t as per RJS's email */ /* pjt 03jan05 MERGED IN THE ATNF CODE FOR: */ /* rjs 27jul04 Handle uvinfo_variance Tsys table in a more elegant */ /* fashion to deal with many antennas. (MAXANT) */ /* rjs 16aug04 Add selection based on LST, elevation and HA - but */ /* only when the relevant uv variables are in the dataset*/ /* pjt 24oct05 TESTBED program can use checker table of uv variables */ /* pjt 23nov05 Added new dazim, delev (scalar!) uv variables */ /* only when the relevant uv variables are in the dataset*/ /* pjt 25apr06 Add ATNF's new uvdim_c and match sourcenames w/o case */ /* pjt 22aug06 merged versions; finish dazim/delev selection code */ /* pjt 22may07 added code for purpose, fixed uvread_match() */ /* pjt 06feb08 allow seeing() selection on smonrms or rmspath */ /* cf. 08oct07 addition to ATNF version of uvio.c */ /* pjt 8may08 wrap HA back into -12..12 from -24..24.. */ /* dhem 13may08 Change uvputvr_c to always update var's buffer */ /* dhem 14may08 uvputvr_c always reallocs var's buffer on size change */ /* pjt 3dec09 allow minsize2 threshold on INT2 vs. REAL for corr's */ /* pjt 16dec09 cloned uvread_match() into uvread_matchp() for purpose */ /* pjt 22jul11 better antenna based handling ELEV, DAZIM, DELEV */ /* pjt 31aug11 fix bug in ELEV selection */ /* pkgw 05dec11 Move definition of MAXIANT here, reference the */ /* thorough BASANT documentation. */ /* pkgw 14dec11 Use errmsg_c() for cleaner I/O error reporting */ /* pjt 6jun12 Merged in a few useful ATNF updates */ /*----------------------------------------------------------------------*/ /* */ /* Handle UV files. */ /* */ /* A uv data set consists of the following data items: */ /* visdata -- Varable stream. This data stream consists */ /* of a stream of "records", each record giving either the */ /* length or value of a "variable". Variables are anything */ /* measured during an observation, and which can be */ /* vary during the observation. These include uv */ /* coordinate, correlation data, system temperature, time, */ /* etc. Each record starts with 4 bytes which gives a */ /* number identifying the variable, and indicates whether */ /* this record give the variable's value or length (in */ /* bytes). The identidying numbers range from 0 to N-1 */ /* (for a file with info on N variables). */ /* vartable -- Table of variable names and types. This is a text */ /* item which which maps the number associated with a */ /* variable into some more useable name. It also gives the */ /* type (real, integer*2, double precision, etc) of */ /* variables. */ /* flags -- Flagging information. Each correlation has a flag */ /* to indicate whether it is good or not. Flagging info is */ /* written into an item consisting of a bit map. */ /* */ /* The UV structure */ /* ================ */ /* Each open UV data file is described by the UV structure, which in */ /* turn contains a number of substructures. */ /* */ /* item This is the item-handle to access the variable */ /* stream. */ /* nvar The number of different variables in the */ /* variable stream. */ /* offset Current offset into uv data stream where i/o is */ /* being performed. */ /* tno The file-handle of the overall uv data-set. */ /* flags Miscellaneous flags. */ /* callno This is initially zero, and incremented each call to */ /* uv_scan. */ /* mark This gives the "callno" relative to which variables are */ /* deemed to have been updated. i.e. a variable considered */ /* as having changed if the variables "callno" is greater */ /* or equal to "mark". */ /* variable An array of structures defining the variables within the*/ /* variable data stream. */ /* vhash A hash table of pointers to VARIABLE structures. This */ /* allows fast access to the a particular variable by name.*/ /* data_line A structure (LINE_INFO) describing the data line type. */ /* ref_line A structure (LINE_INFO) describing the reference line */ /* type. */ /* sel The uv data selection structure. */ /* corr_flags.handle Handle used by the maskio routines. */ /* corr_flags.offset Offset to the next flag to read in the mask file. */ /* corr_flags.nflags Number of correlation channel flags. */ /* corr_flags.flags The flags for the last correlation record read. */ /* corr_flags.init Have they been read? */ /* corr_flags.exists A flag whether the corr flags are believed to exist.*/ /* */ /* Because we know that certain variables are accessed every call to */ /* uvread, we keep pointers to them. */ /* */ /* coord corr tscale time */ /* bl nschan sfreq sdf */ /* restfreq axisrms dra ddec */ /* nwide wcorr wfreq veldop */ /* vsource plmaj plmin plangle */ /* ra dec pol on */ /* obsra obsdec lst antpos */ /* antdiam source pol smonrms */ /* rmspath */ /* */ /* The VARIABLE structure */ /* ====================== */ /* This structure describes a single variable from the variable data */ /* stream. */ /* buf Pointer to a buffer containing the current value of the */ /* variable (in the format of the local machine). */ /* name The name of the variable. */ /* length The length of the variable (bytes). */ /* flags Miscellaneous flags. */ /* type The type of the variable, whether it is I*2,R*4, etc. */ /* index This gives the index of the variable in the "variable" */ /* array of the UV structure. */ /* callno The call number to uv_scan when the variable was last */ /* updated. */ /* fwd Pointer to the next variable. This allows a linked */ /* list to be formed for hashing. */ /* */ /*----------------------------------------------------------------------*/ #if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H #include "config.h" #endif #define VERSION_ID "6-june-2012 pjt" #define private static #define CKMS 299792.458 #define PI 3.141592653589793 #include #include #include #include #include #include "hio.h" #include "miriad.h" #define UVF_COPY 0x01 /* Set if this variable is to be copied by the uvcopy routine. */ #define UVF_UPDATED 0x02 /* Set if noting updates to this variable. */ #define UVF_UPDATED_PLANET 0x04 /* Set if planet things have changed. */ #define UVF_UPDATED_SKYFREQ 0x08 /* Set if sky freq things have changed. */ #define UVF_NEW 0x10 /* Set if its a new uv data set being made. */ #define UVF_APPEND 0x20 /* Set if we are appending to a uv dataset. */ #define UVF_WAVELENGTH 0x40 /* Set if uvread is to convert uv to wavelengths. */ #define UVF_OVERRIDE 0x80 /* Set if the value of this variable is being overriden. */ #define UVF_NOCHECK 0x200 /* Set if UVPUTVR is not to check this variable as to whether it has really changed. */ #define UVF_AUTO 0x400 #define UVF_CROSS 0x800 #define UVF_RUNS 0x1000 /* Does uvwrite receive flags in runs specification? */ #define UVF_INIT 0x2000 /* Set on first call to uvread or uvwrite. */ #define UVF_UPDATED_UVW 0x4000 /* Set if things needed for uvw have changed. */ #define UVF_REDO_UVW 0x8000 /* Set if u-v-w are to be recomputed. */ #define UVF_DOW 0x10000 /* Set if the caller wants w returned. */ #define UV_ALIGN 8 #define UV_HDR_SIZE 4 #define CHECK_THRESH 6 #define HASHSIZE 123 #define MAXVAR 256 #define MAXNAM 8 #define MAXPRE 9 #define MAXLINE 128 #define VAR_SIZE 0 #define VAR_DATA 1 #define VAR_EOR 2 #define MK_FLAGS 1 #define MK_RUNS 2 /*----------------------------------------------------------------------*/ /* */ /* A few definitions to coax lint to like my code. */ /* */ /*----------------------------------------------------------------------*/ #define Sscanf (void)sscanf #define Sprintf (void)sprintf #define Malloc(x) malloc((unsigned)(x)) #define Realloc(a,b) ((a)==NULL?malloc((unsigned)(b)):realloc((a),(unsigned)(b))) #define Strcpy (void)strcpy /*----------------------------------------------------------------------*/ /* */ /* Macros to simplify life and obscure the debugger :-) */ /* */ /*----------------------------------------------------------------------*/ #define BUG(sev,a) bug_c(sev,a) #define ERROR(sev,a) bug_c(sev,((void)sprintf a,message)) #define CHECK(x,a) if(x) { Sprintf a; bugv_c('f', "%s: %s", \ message, errmsg_c (x)); } #define uvputvra_c(tno,name,value) \ uvputvr_c(tno,H_BYTE,name,value,strlen(value)) #define uvputvrj_c(tno,name,value,n) \ uvputvr_c(tno,H_INT2,name,(char *)(value),n) #define uvputvri_c(tno,name,value,n) \ uvputvr_c(tno,H_INT,name,(char *)(value),n) #define uvputvrr_c(tno,name,value,n) \ uvputvr_c(tno,H_REAL,name,(char *)(value),n) #define uvputvrd_c(tno,name,value,n) \ uvputvr_c(tno,H_DBLE,name,(char *)(value),n) #define uvputvrc_c(tno,name,value,n) \ uvputvr_c(tno,H_CMPLX,name,(char *)(value),n) #define VARLEN(var) ( (var)->length / external_size[(var)->type] ) #define VARTYPE(var) ( type_flag[(var)->type] ) #define NUMCHAN(var) ((var)->type == H_INT2 || (var)->type == H_REAL ? \ (var)->length / (2*external_size[(var)->type]) : \ (var)->length / external_size[(var)->type] ) #define MYABS(x) ( (x) > 0 ? (x) : -(x) ) /************************************************************************** *** GCC 14 detects problematic pointer passing: *** *** In file included from casacore/mirlib/uvio.c:288: *** casacore/mirlib/miriad.h:182:51: note: expected ‘int8 *’ {aka ‘long long int *’} but argument is of type ‘off_t *’ {aka ‘long int *’} *** 182 | void rdhdl_c (int tno, Const char *keyword, int8 *value, int8 defval); *** | ~~~~~~^~~~~ *** ***************************************************************************/ #define RDHDL_C( TNO, KEYWORD, VALUE, DEFVAL ) \ { int8 value; \ rdhdl_c( TNO, KEYWORD, &value, DEFVAL); \ *VALUE = value; } /*----------------------------------------------------------------------*/ /* */ /* Types and static variables. */ /* */ /*----------------------------------------------------------------------*/ static char message[MAXLINE]; static int internal_size[10]; static int external_size[10]; static char type_flag[10]; static char var_data_hdr[UV_HDR_SIZE]={0,0,VAR_DATA,0}; static char var_size_hdr[UV_HDR_SIZE]={0,0,VAR_SIZE,0}; static char var_eor_hdr[UV_HDR_SIZE]={0,0,VAR_EOR,0}; typedef struct variable{ char *buf,name[MAXNAM+1]; int length,flength,flags,type,index,callno; struct variable *fwd; } VARIABLE; typedef struct varpnt{ VARIABLE *v; struct varpnt *fwd; } VARPNT; typedef struct varhand{ int tno,callno,index; struct varhand *fwd; VARPNT *varhd; } VARHAND; #define LINE_NONE 0 #define LINE_CHANNEL 1 #define LINE_WIDE 2 #define LINE_VELOCITY 3 #define LINE_FELOCITY 4 #include "maxdimc.h" #define SEL_VIS 1 #define SEL_TIME 2 #define SEL_UVN 3 #define SEL_POINT 4 #define SEL_DRA 5 #define SEL_DDEC 6 #define SEL_INC 7 #define SEL_RA 8 #define SEL_DEC 9 #define SEL_POL 10 #define SEL_ON 11 #define SEL_SRC 12 #define SEL_UV 13 #define SEL_FREQ 14 #define SEL_SHADOW 15 #define SEL_BIN 16 #define SEL_HA 17 #define SEL_LST 18 #define SEL_ELEV 19 #define SEL_DAZIM 20 #define SEL_DELEV 21 #define SEL_PURP 22 #define SEL_SEEING 23 typedef struct { int type,discard; double loval,hival; char *stval; } OPERS; typedef struct { int discard,select; float loval,hival; } AMP; typedef struct { int wins[MAXWIN]; int first,last,n,select; } WINDOW; typedef struct { double *table; int vhan,nants,missing; } SIGMA2; typedef struct select { int ants[MAXANT*(MAXANT+1)/2]; int selants; int maxoper,noper,and; WINDOW win; AMP amp; OPERS *opers; struct select *fwd; } SELECT; typedef struct { int nants; double uu[MAXANT],vv[MAXANT],ww[MAXANT]; } UVW; typedef struct { int linetype; int start,width,step,n; float fstart,fwidth,fstep,*wts; } LINE_INFO; typedef struct { char *handle; int nflags,*flags,exists,init; off_t offset; } FLAGS; typedef struct { int item; int nvar,saved_nvar,tno,flags,callno,maxvis,mark; int minsize2; /* at -1 always use REAL, else use to trigger INT2 */ off_t offset, max_offset; int presize,gflag; FLAGS corr_flags,wcorr_flags; VARIABLE *coord,*corr,*time,*bl,*tscale,*nschan,*axisrms,*seeing; VARIABLE *sfreq,*sdf,*restfreq,*wcorr,*wfreq,*veldop,*vsource; VARIABLE *plmaj,*plmin,*plangle,*dra,*ddec,*ra,*dec,*pol,*on; VARIABLE *dazim, *delev, *purpose; VARIABLE *obsra,*obsdec,*lst,*elev,*antpos,*antdiam,*source,*bin; VARIABLE *vhash[HASHSIZE],*prevar[MAXPRE]; VARIABLE variable[MAXVAR]; LINE_INFO data_line,ref_line,actual_line; int need_skyfreq,need_point,need_planet,need_dra,need_ddec, need_dazim, need_delev,need_purp, need_ra,need_dec,need_pol,need_on,need_obsra,need_uvw,need_src, need_win,need_bin,need_lst,need_elev,need_seeing; float ref_plmaj,ref_plmin,ref_plangle,plscale,pluu,pluv,plvu,plvv; double skyfreq; int skyfreq_start; VARHAND *vhans; SELECT *select; int apply_amp,apply_win; AMP *amp; SIGMA2 sigma2; UVW *uvw; WINDOW *win; } UV; #define MAXVHANDS 128 static UV *uvs[MAXOPEN]; static VARHAND *varhands[MAXVHANDS]; static WINDOW truewin; static AMP noamp; static int first=TRUE; /* void uvputvr_c(); */ private void uvinfo_chan(),uvinfo_variance(),uvbasant_c(); private void uv_init(),uv_freeuv(),uv_free_select(); private void uvread_defline(),uvread_init(),uvread_velocity(),uvread_flags(); private void uvread_defvelline(); private void uvread_updated_planet(),uvread_reference(); private void uvread_updated_uvw(),uvread_preamble(); private void uv_vartable_out(),uv_vartable_in(); private void uvset_coord(),uvset_linetype(),uvset_planet(); private void uvset_selection(),uvset_preamble(); private void uv_addopers(),uv_override(); private UV *uv_getuv(); private VARIABLE *uv_mkvar(),*uv_locvar(),*uv_checkvar(); private int uv_scan(),uvread_line(),uvread_select(),uvread_maxvis(); private int uvread_shadowed(),uvread_match(),uvread_matchp(); private double uv_getskyfreq(); /************************************************************************/ #ifdef TESTBED static char *M[] = { "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" }; static int checklist = 0; /* The following compiles a main program to give exercise to some of the * uvio routines. It is essentially a debugging device (both for bad * files and bad behaviour of uvio!). * * Call several uvio.c routines, some of which are the non-public ones, * to get a 'human' readable listing of a miriad visibility data set ` * Because it needs some of these 'static' routines, the source code * of uvio.c needs to be included here directly, as opposed to linking * it with the library ($MIRLIB/libmir.a in Unix) * * Note: This program does not have the normal miriad user interface * */ int main(int ac,char *av[]) { int i,tno; char *fn; printf("%s Version %s\n",av[0],VERSION_ID); if (ac!=2) { printf("Usage: %s [vis=]vis-dataset\n",av[0]); printf("Expert listing of a miriad UV dataset\n"); #ifdef MIR4 printf("MIR4 mode\n"); #else printf("MIR3 mode **probably will not work in MIR4**\n"); #endif exit(0); } for (i=1; i 4) { /* see if vis= was used */ if (strncmp(fn,"vis=",4)==0) fn += 4; /* if so, increase pointer */ } uvopen_c(&tno,fn,"old"); } my_uvlist(tno,fn); uvclose_c(tno); return 0; } my_uvlist(int tno,char *fname) { double *dp; float *fp; short *sp; int iostat, intsize, extsize, i, *ip, eor_count=0; off_t offset; VARIABLE *v; UV *uv; char s[UV_HDR_SIZE], *b, buffer[128]; uv = uvs[tno]; /* get pointer to UV structure */ offset = uv->offset; /* should be 0 at start */ printf("0x%08x FILE: %s\n",offset,fname); while(offset < uv->max_offset) { printf("0x%08x ",offset); hreadb_c(uv->item,s,offset,UV_HDR_SIZE,&iostat); /* get header */ if (iostat == -1) return(iostat); /* End Of File */ if(*(s+2) != VAR_EOR) { v = &uv->variable[*s]; /* get name of var */ intsize = internal_size[v->type]; extsize = external_size[v->type]; } switch(*(s+2)) { case VAR_SIZE: hreadi_c(uv->item,&v->length,offset+UV_HDR_SIZE,H_INT_SIZE, &iostat); printf("SIZE: %-9s Count=%d,Type=%c\n",v->name,VARLEN(v),VARTYPE(v)); v->buf = Realloc(v->buf, (v->length*intsize)/extsize); offset += UV_ALIGN; break; case VAR_DATA: offset += mroundup(UV_HDR_SIZE,extsize); hread_c(uv->item,v->type,v->buf,offset,v->length, &iostat); printf("DATA: %-9s",v->name); if (strcmp(v->name,"time") == 0) { int z,a,b,c,d,e,alpha,month,year,day,hr,minute,sec; int dsec,nchar; char string[100]; double f; dp = (double *) v->buf; z = *dp + 0.5 + (1.0/1728000.0); f = *dp + 0.5 + (1.0/1728000.0) - z; if (z<2299161){a=z;}else{ alpha = ((z - 1867216.25) / 36524.25); a = z + 1 + alpha - (int)(0.25 * alpha); } b = a + 1524; c = (b - 122.1) / 365.25; d = 365.25 * c; e = (b - d) / 30.6001; f += (b - d - (int)(30.6001 * e)); day = f; hr = 24 * (f - day); minute = 60 * (24 * (f - day) - hr); sec = 600 * (60 * (24 * (f - day) - hr) - minute); dsec = sec % 10; sec /= 10; month = (e<=13) ? e - 1 : e - 13; year = (month>2) ? c - 4716 : c - 4715; year %= 100; printf(" %20.10lg ",*dp); printf(" %2.2d%s%2.2d:%2.2d:%2.2d:%2.2d.%1d\n", year,M[month-1],day,hr,minute,sec,dsec); }else switch (v->type) { case H_BYTE: strncpy(buffer,v->buf,v->length); buffer[v->length] = 0; printf(" %-8s\n",buffer); break; case H_INT2: sp = (short *) v->buf; printf(" %d\n",*sp); break; case H_INT: ip = (int *) v->buf; printf(" %d\n",*ip); break; case H_REAL: fp = (float *) v->buf; printf(" %20.10g\n",*fp); break; case H_DBLE: dp = (double *) v->buf; printf(" %20.10lg\n",*dp); break; case H_CMPLX: fp = (float *) v->buf; printf(" %20.10g %20.10g\n",fp[0], fp[1]); break; default: printf(" (Invalid data type %d)\n",v->type); break; } offset = mroundup(offset+v->length,UV_ALIGN); break; case VAR_EOR: printf("========== EOR (%d) ========\n",++eor_count); offset += UV_ALIGN; break; default: printf("No valid record code %d",*(s+2)); exit(0); } /* switch */ uv->offset = offset; } /* for(;;) */ } #endif /************************************************************************/ void uvopen_c(int *tno,Const char *name,Const char *status) /**UvOpen -- Open a uv data file. */ /*&rjs */ /*:uv-i/o */ /*+ FORTRAN call sequence: subroutine uvopen(tno,name,status) integer tno character name*(*),status*(*) Create and/or ready a UV data base to be accessed. Input: name Name of the directory tree containg the u-v data. status Either "old", "new" or "append". Old files can be read, whereas new and append files can only be written. Append files must be pre-existing uv data-sets. Output: tno Handle of the uv data set. */ /*-- */ /*----------------------------------------------------------------------*/ { UV *uv; int iostat; char line[MAXLINE]; if(first)uv_init(); /*----------------------------------------------------------------------*/ /* */ /* Handle an old file. */ /* */ /*----------------------------------------------------------------------*/ if( !strcmp(status,"old") ) { hopen_c(tno,name,"old",&iostat); CHECK(iostat,(message,"Error opening %s, in UVOPEN(old)",name)); uv = uv_getuv(*tno); haccess_c(*tno,&uv->item,"visdata","read",&iostat); CHECK(iostat,(message,"Error accessing visdata for %s, in UVOPEN(old)",name)); #ifdef MIR4 /* figure out if to read old MIR3 or new MIR4 */ #if true RDHDL_C(*tno,"vislen",&(uv->max_offset),hsize_c(uv->item)); #else int old_vislen; rdhdi_c(*tno,"vislen",&old_vislen,hsize_c(uv->item)); if (old_vislen < 0) ERROR('f',(message,"Bad conversion MIR3<->MIR4 in UVOPEN: vislen=%d",old_vislen)); uv->max_offset = old_vislen; #endif #else /* MIR3 and before format: */ rdhdi_c(*tno,"vislen",&(uv->max_offset),hsize_c(uv->item)); #endif uv_vartable_in(uv); uv_override(uv); /*----------------------------------------------------------------------*/ /* */ /* Handle a new file. */ /* */ /*----------------------------------------------------------------------*/ } else if(!strcmp(status,"new")) { hopen_c(tno,name,"new",&iostat); CHECK(iostat,(message,"Error opening %s, in UVOPEN(new)",name)); uv = uv_getuv(*tno); haccess_c(*tno,&uv->item,"visdata","write",&iostat); CHECK(iostat,(message,"Error accessing visdata for %s, in UVOPEN(new)",name)); uv->flags = UVF_NEW; /*----------------------------------------------------------------------*/ /* */ /* Append to an old file. */ /* */ /*----------------------------------------------------------------------*/ } else if(!strcmp(status,"append")) { hopen_c(tno,name,"old",&iostat); CHECK(iostat,(message,"Error opening %s, in UVOPEN(append)",name)); uv = uv_getuv(*tno); haccess_c(*tno,&uv->item,"visdata","append",&iostat); CHECK(iostat,(message,"Error accessing visdata for %s, in UVOPEN(append)",name)); uv->flags = UVF_APPEND; #ifdef MIR4 /* figure out if to read old MIR3 or new MIR4 */ if (1) { RDHDL_C(*tno,"vislen",&(uv->offset),hsize_c(uv->item)); } else { int old_vislen; rdhdi_c(*tno,"vislen",&old_vislen,hsize_c(uv->item)); if (old_vislen < 0) ERROR('f',(message,"Bad conversion MIR3<->MIR4 in UVOPEN: vislen=%d",old_vislen)); uv->offset = old_vislen; } #else /* MIR3 and before format: */ rdhdi_c(*tno,"vislen",&(uv->offset),hsize_c(uv->item)); #endif uv->offset = mroundup(uv->offset,UV_ALIGN); uv_vartable_in(uv); /* Read items and fill in the appropriate value. */ rdhda_c(*tno,"obstype",line,"",MAXLINE); if(!strcmp(line,"autocorrelation")) uv->flags |= UVF_AUTO; else if(!strcmp(line,"crosscorrelation")) uv->flags |= UVF_CROSS; RDHDL_C(*tno,"ncorr",&(uv->corr_flags.offset),-1); RDHDL_C(*tno,"nwcorr",&(uv->wcorr_flags.offset),-1); if(uv->corr_flags.offset < 0 || uv->wcorr_flags.offset < 0) BUG('f',"Cannot append to uv file without 'ncorr' and/or 'nwcorr' items"); /*----------------------------------------------------------------------*/ /* */ /* Somethig else -- must be an error. */ /* */ /*----------------------------------------------------------------------*/ } else ERROR('f',(message,"Status %s is not recognised by UVOPEN",status)); } /************************************************************************/ void uvclose_c(int tno) /**uvclose -- Close a uv file */ /*&rjs */ /*:uv-i/o */ /*+ FORTRAN call sequence: subroutine uvclose(tno) integer tno This close a uv data file. Input: tno Handle of the uv data set. */ /*-- */ /*----------------------------------------------------------------------*/ { UV *uv; int iostat; uv = uvs[tno]; /* Finished with the flagging information. */ if(uv->corr_flags.handle != NULL) mkclose_c(uv->corr_flags.handle); if(uv->wcorr_flags.handle != NULL) mkclose_c(uv->wcorr_flags.handle); uv->corr_flags.handle = uv->wcorr_flags.handle = NULL; /* Flush out all stuff appropriate for a new or append file. */ if(uv->flags & (UVF_NEW|UVF_APPEND))uvflush_c(tno); /* Close the visibility data stream, release structures, and close the whole thing. */ hdaccess_c(uv->item,&iostat); CHECK(iostat,(message,"Error calling hdaccess for visdata, in UVCLOSE")); uv_freeuv(uv); uvs[tno] = NULL; hclose_c(tno); } /************************************************************************/ void uvflush_c(int tno) /**uvflush -- Flush buffers of a uv dataset to disk. */ /*&rjs */ /*:uv-i/o */ /*+ FORTRAN call sequence: subroutine uvflush(tno) integer tno This close a uv data file. Input: tno Make sure anything buffered up is flushed to disk. The disk file should be readable (up to data written here) even if the caller or computer crashes. */ /*-- */ /*----------------------------------------------------------------------*/ { UV *uv; int iostat; uv = uvs[tno]; if(!(uv->flags & (UVF_NEW|UVF_APPEND)))return; /* Flush the masks out. */ if(uv->corr_flags.handle != NULL) mkflush_c(uv->corr_flags.handle); if(uv->wcorr_flags.handle != NULL) mkflush_c(uv->wcorr_flags.handle); /* Rewrite vartable, if needed. */ if(uv->saved_nvar < uv->nvar || (uv->nvar == 0 && (uv->flags & UVF_NEW))) uv_vartable_out(uv); uv->saved_nvar = uv->nvar; /* Rewrite the description indicating the type of the data. */ if( ( uv->flags & (UVF_AUTO|UVF_CROSS) ) == (UVF_AUTO|UVF_CROSS)) wrhda_c(tno,"obstype","mixed-auto-cross"); else if(uv->flags & UVF_AUTO) wrhda_c(tno,"obstype","autocorrelation"); else if(uv->flags & UVF_CROSS) wrhda_c(tno,"obstype","crosscorrelation"); /* Write out things to help recover the EOF. */ #ifdef MIR4 wrhdl_c(tno,"nwcorr",uv->wcorr_flags.offset); wrhdl_c(tno,"ncorr",uv->corr_flags.offset); wrhdl_c(tno,"vislen",uv->offset); #else /* old MIR3 and before format */ wrhdi_c(tno,"nwcorr",uv->wcorr_flags.offset); wrhdi_c(tno,"ncorr",uv->corr_flags.offset); wrhdi_c(tno,"vislen",uv->offset); #endif /* Finally flush out everything to disk. */ hflush_c(tno,&iostat); CHECK(iostat,(message,"Error calling hflush, in UVFLSH")); } /************************************************************************/ private void uv_init() /* Initalise everything imaginable. ------------------------------------------------------------------------*/ { int i; first = FALSE; external_size[H_BYTE] = 1; internal_size[H_BYTE] = 1; type_flag[H_BYTE] = 'a'; external_size[H_INT] = H_INT_SIZE; internal_size[H_INT] = sizeof(int); type_flag[H_INT] = 'i'; external_size[H_INT2] = H_INT2_SIZE; internal_size[H_INT2] = sizeof(int); type_flag[H_INT2] = 'j'; external_size[H_REAL] = H_REAL_SIZE; internal_size[H_REAL] = sizeof(float); type_flag[H_REAL] = 'r'; external_size[H_DBLE] = H_DBLE_SIZE; internal_size[H_DBLE] = sizeof(double); type_flag[H_DBLE] = 'd'; external_size[H_CMPLX] = H_CMPLX_SIZE; internal_size[H_CMPLX] = 2*sizeof(float); type_flag[H_CMPLX] = 'c'; /* Initialise the "true window" array. */ noamp.select = FALSE; truewin.first = 0; truewin.last = MAXWIN-1; truewin.n = MAXWIN; truewin.select= FALSE; for(i=0; i < MAXWIN; i++) truewin.wins[i] = TRUE; /* Initialise the table of variable handles. */ for(i=0; i < MAXVHANDS; i++)varhands[i] = NULL; } /************************************************************************/ private void uv_freeuv(UV *uv) /* Free a uv structure. ------------------------------------------------------------------------*/ { int i; VARIABLE *v; VARHAND *vh,*vht; VARPNT *vp,*vpt; vh = uv->vhans; while(vh != NULL){ vp = vh->varhd; varhands[vh->index] = NULL; while(vp != NULL){ vpt = vp; vp = vp->fwd; free((char *)vpt); } vht = vh; vh = vh->fwd; free((char *)vht); } /* Free buffers associated with variables. */ for(i=0, v = uv->variable; i < MAXVAR; i++, v++) if(v->buf != NULL)free(v->buf); if(uv->data_line.wts != NULL) free((char *)uv->data_line.wts); if(uv->ref_line.wts != NULL) free((char *)uv->ref_line.wts); if(uv->corr_flags.flags != NULL) free((char *)uv->corr_flags.flags); if(uv->wcorr_flags.flags != NULL ) free((char *)uv->wcorr_flags.flags); if(uv->sigma2.table != NULL)free((char *)uv->sigma2.table); uv_free_select(uv->select); if(uv->uvw != NULL) free((char *)(uv->uvw)); free((char *)uv); } /************************************************************************/ private void uv_free_select(SELECT *sel) { OPERS *op; SELECT *fwd; int i; while(sel != NULL){ fwd = sel->fwd; if(sel->noper > 0){ op = sel->opers; for(i=0; i < sel->noper; i++){ if(op->stval != NULL) free(op->stval); op++; } free((char *)(sel->opers)); } free((char *)sel); sel = fwd; } } /************************************************************************/ private UV *uv_getuv(int tno) /* Allocate a structure describing a uv file. ------------------------------------------------------------------------*/ { int i; UV *uv; VARIABLE *v; uv = (UV *)Malloc(sizeof(UV)); uv->item = 0; uv->tno = tno; uv->vhans = NULL; uv->nvar = 0; uv->presize = 0; uv->minsize2 = 4; /* trigger REAL (vs. INT2) storage, or -1 for always */ uv->gflag = 1; uv->saved_nvar= 0; uv->offset = 0; uv->max_offset= 0; uv->flags = 0; uv->callno = 0; uv->maxvis = 0; uv->mark = 0; uv->select = NULL; uv->need_skyfreq = uv->need_point = uv->need_planet = FALSE; uv->need_pol = uv->need_on = uv->need_uvw = FALSE; uv->need_src = uv->need_win = uv->need_bin = FALSE; uv->need_dra = uv->need_ddec = uv->need_ra = FALSE; uv->need_dec = uv->need_lst = uv->need_elev = FALSE; uv->need_obsra = uv->need_dazim = uv->need_delev = FALSE; uv->need_purp = uv->need_seeing= FALSE; uv->uvw = NULL; uv->ref_plmaj = uv->ref_plmin = uv->ref_plangle = 0; uv->plscale = 1; uv->pluu = uv->plvv = 1; uv->plvu = uv->pluv = 0; uv->apply_amp = TRUE; uv->apply_win = TRUE; uv->skyfreq_start = 0; uv->corr_flags.exists = TRUE; uv->corr_flags.handle = NULL; uv->corr_flags.offset = 0; uv->corr_flags.flags = NULL; uv->corr_flags.nflags = 0; uv->wcorr_flags.exists = TRUE; uv->wcorr_flags.handle = NULL; uv->wcorr_flags.offset = 0; uv->wcorr_flags.flags = NULL; uv->wcorr_flags.nflags = 0; uv->data_line.wts = NULL; uv->data_line.linetype = LINE_NONE; uv->ref_line.wts = NULL; uv->ref_line.linetype = LINE_NONE; uv->sigma2.table = NULL; uv->sigma2.nants = 0; uv->sigma2.missing = FALSE; uv->corr = NULL; uv->wcorr = NULL; uv->coord = NULL; uv->time = NULL; uv->bl = NULL; for(i=0, v = uv->variable; i < MAXVAR; i++, v++){ v->length = v->flength = 0; v->buf = NULL; v->flags = 0; v->type = 0; v->fwd = NULL; v->index = i; v->callno = 0; } for(i=0; i < HASHSIZE; i++) uv->vhash[i] = NULL; uvs[tno] = uv; return(uv); } /************************************************************************/ private void uv_vartable_out(UV *uv) /* Write out a variable name table. ------------------------------------------------------------------------*/ { int item; char line[MAXLINE]; int iostat,i; VARIABLE *v; haccess_c(uv->tno,&item,"vartable","write",&iostat); CHECK(iostat,(message,"Error opening vartable, in UVCLOSE(vartable_out)")); for(i=0, v = uv->variable; i < uv->nvar; i++,v++){ Sprintf(line,"%c %s",VARTYPE(v),v->name); hwritea_c(item,line,strlen(line)+1,&iostat); CHECK(iostat,(message,"Error writing to vartable, in UVCLOSE(vartable_out)")); } hdaccess_c(item,&iostat); CHECK(iostat,(message,"Error closing vartable, in UVCLOSE(vartable_out)")); } /************************************************************************/ private void uv_override(UV *uv) /* Determine if a variable has a item of the same name. If there is one, then the value of that item overrides the value of the variable. In this case, get the value of the item, and set a flag to indicate that the variable value is being overriden. ------------------------------------------------------------------------*/ { int item; char *b,varname[MAXLINE],vartype[MAXLINE],descr[MAXLINE]; VARIABLE *v; int tno,iostat,n,ok,isnumeric,ischar; tno = uv->tno; haccess_c(uv->tno,&item,".","read",&iostat); CHECK(iostat,(message,"Error opening directory listing, in UVOPEN(override)")); while(hreada_c(item,varname,MAXLINE,&iostat),iostat==0){ v = uv_locvar(tno,varname); if(v != NULL){ hdprobe_c(tno,varname,descr,MAXLINE,vartype,&n); isnumeric = (v->type == H_DBLE || v->type == H_REAL || v->type == H_INT) && (!strcmp(vartype,"double") || !strcmp(vartype,"real") || !strcmp(vartype,"integer")); ischar = (v->type == H_BYTE && !strcmp(vartype,"character")); ok = ( n == 1 && (isnumeric || ischar) ); if(v->type == H_BYTE) { n = strlen(descr); b = Malloc(n+1); } else { b = Malloc(internal_size[v->type]); } if(ok)switch(v->type){ case H_INT: rdhdi_c(tno,varname,(int *)b,0); break; case H_REAL: rdhdr_c(tno,varname,(float *)b,0.0); break; case H_BYTE: strcpy(b,descr); break; case H_DBLE: rdhdd_c(tno,varname,(double *)b,(double)0.0); break; default: ok = FALSE; } if(ok){ v->flags |= UVF_OVERRIDE; v->buf = b; v->length = n*external_size[v->type]; v->callno = 1; } else { free(b); ERROR('w',(message,"Cannot override variable %s, in UVOPEN",varname)); } } } if(iostat != -1) ERROR('f',(message, "Error %d when performing override checks, in UVOPEN",iostat)); hdaccess_c(item,&iostat); } /************************************************************************/ private void uv_vartable_in(UV *uv) /* Scan the variable name table, to determine the names and types of the variables. ------------------------------------------------------------------------*/ { int item; char line[MAXLINE],name[MAXNAM+1],ctype; int iostat,type; haccess_c(uv->tno,&item,"vartable","read",&iostat); CHECK(iostat,(message,"Error opening vartable, in UVOPEN(vartable_in)")); while(hreada_c(item,line,(int)sizeof(line),&iostat),!iostat){ Sscanf(line,"%c %s",&ctype,name); switch(ctype){ case 'a': type = H_BYTE; break; case 'j': type = H_INT2; break; case 'i': type = H_INT; break; case 'r': type = H_REAL; break; case 'd': type = H_DBLE; break; case 'c': type = H_CMPLX; break; default: ERROR('f',(message,"Bad type (%c) for variable %s",ctype,name)); } (void)uv_mkvar(uv->tno,name,type); } hdaccess_c(item,&iostat); uv->saved_nvar = uv->nvar; } /************************************************************************/ private VARIABLE *uv_mkvar(int tno,char *name,int type) /* Add an entry for a new variable. ------------------------------------------------------------------------*/ { UV *uv; VARIABLE *v; int n,hashval; /* Check if the variable already exists. */ v = uv_locvar(tno,name); if(v != NULL) return(v); /* Check that the variable has a good name. */ if((int)strlen(name) > MAXNAM) ERROR('f',(message,"The variable name %s is too long, in UVPUTVR",name)); /* We are going to have to create it. */ uv = uvs[tno]; n = uv->nvar++; v = &uv->variable[n]; Strcpy(v->name,name); v->type = type; /* Add it to the hash table. */ hashval = 0; while(*name)hashval += *name++; hashval %= HASHSIZE; v->fwd = uv->vhash[hashval]; uv->vhash[hashval] = v; return(v); } /************************************************************************/ private VARIABLE *uv_locvar(int tno,char *name) /* Locate a variable from the hash table. ------------------------------------------------------------------------*/ { VARIABLE *v; int hashval; char *s; hashval = 0; for(s=name; *s; s++) hashval += *s; for(v = uvs[tno]->vhash[hashval%HASHSIZE]; v != NULL; v = v->fwd) if(!strcmp(v->name,name))break; return(v); } /************************************************************************/ void uvnext_c(int tno) /**uvnext -- Skip to the next uv record. */ /*&rjs */ /*:uv-i/o */ /*+ FORTRAN call sequence: subroutine uvnext(tno) integer tno Skip to the next uv data record. On write, this causes an end-of-record mark to be written. On read, this causes data to be read up until the next end-of-record mark. Input: tno The uv data file handle. */ /*-- */ /*----------------------------------------------------------------------*/ { int iostat; UV *uv; uv = uvs[tno]; if(uv->flags & (UVF_NEW|UVF_APPEND)){ hwriteb_c(uv->item,var_eor_hdr,uv->offset,UV_HDR_SIZE,&iostat); CHECK(iostat,(message,"Error writing end-of-record, in UVNEXT")); uv->offset += UV_ALIGN; } else { uv->mark = uv->callno + 1; uv->flags &= ~(UVF_UPDATED | UVF_COPY); (void)uv_scan(uv,(VARIABLE *)NULL); } } /************************************************************************/ void uvrewind_c(int tno) /**uvrewind -- Reset the uv data file to the start of the file. */ /*&rjs */ /*:uv-i/o */ /*+ FORTRAN call sequence: subroutine uvrewind(tno) integer tno Rewind a uv file, readying it to be read from the begining again. Input: tno The uv data file handle. */ /*-- */ /*----------------------------------------------------------------------*/ { UV *uv; VARIABLE *v; VARHAND *vh; int i; uv = uvs[tno]; uv->callno = uv->mark = 0; for(i=0, v = uv->variable; i < uv->nvar; i++, v++) v->callno = ( (v->flags & UVF_OVERRIDE) ? 1 : 0); for(vh = uv->vhans; vh != NULL; vh = vh->fwd) vh->callno = 0; uv->offset = 0; uv->corr_flags.offset = 0; uv->wcorr_flags.offset = 0; } /************************************************************************/ void uvcopyvr_c(int tin,int tout) /**uvcopyvr -- Copy variables from one uv file to another. */ /*&rjs */ /*:uv-i/o */ /*+ FORTRAN call sequence: subroutine uvcopyvr(tin,tout) integer tin,tout This copies those variables, in the input uv data set, which have changed and which are marked as "copy" ('u' flag of a call to uvtrack). Inputs: tin File handle of the input uv data set. tout File handle of the output uv data set. */ /*-- */ /*----------------------------------------------------------------------*/ { UV *uv; VARIABLE *v; int i; uv = uvs[tin]; if(uv->flags & UVF_COPY) for(i=0, v=uv->variable; i < uv->nvar; i++,v++){ if(v->callno >= uv->mark && (v->flags & UVF_COPY)) uvputvr_c(tout,v->type,v->name,v->buf,VARLEN(v)); } } /************************************************************************/ int uvupdate_c(int tno) /**uvupdate -- Check whether any "important" variables have changed. */ /*&rjs */ /*:uv-i/o */ /*+ FORTRAN call sequence: logical function uvupdate(tno) integer tno This checks whether any ``important variables'' has been updated in the last call to uvread or uvscan. Important variables are those flagged with the 'u' flag in a call to uvtrack. Input: tno File handle of the uv file to check. */ /*-- */ /*----------------------------------------------------------------------*/ { return(uvs[tno]->flags & UVF_UPDATED ? FORT_TRUE : FORT_FALSE); } /************************************************************************/ void uvvarini_c(int tno,int *vhan) /**uvvarini -- Retrieve a handle for the "uvVar" routines. */ /*&rjs */ /*:uv-i/o */ /*+ FORTRAN call sequence: subroutine uvvarini(tno,vhan) This routine allocates a handle for the uvVar routines. These routines are used to keep track of changes to variables, and to copy them when a change occurs. Input: tno The handle of the uv data file. Output: vhan Handle of the list of variables. */ /*-- */ /*----------------------------------------------------------------------*/ { int i; VARHAND *vh; UV *uv; uv = uvs[tno]; /* Locate a space handle slot. */ for(i=0; i < MAXVHANDS; i++)if(varhands[i] == NULL)break; if(i == MAXVHANDS)BUG('f',"Ran out of variable handle slots, in UVVARINI"); varhands[i] = vh = (VARHAND *)Malloc(sizeof(VARHAND)); vh->index = i; vh->callno = 0; vh->tno = tno; vh->varhd = NULL; vh->fwd = uv->vhans; uv->vhans = vh; *vhan = i+1; } /************************************************************************/ void uvvarset_c(int vhan,Const char *var) { VARHAND *vh; VARIABLE *v; VARPNT *vp; vh = varhands[vhan-1]; v = uv_locvar(vh->tno,(char *)var); if(v != NULL){ vp = (VARPNT *)Malloc(sizeof(VARPNT)); vp->v = v; vp->fwd = vh->varhd; vh->varhd = vp; } } /************************************************************************/ void uvvarcpy_c(int vhan,int tout) { VARIABLE *v; VARHAND *vh; VARPNT *vp; int callno; vh = varhands[vhan-1]; callno = vh->callno; vh->callno = uvs[vh->tno]->callno; for(vp = vh->varhd; vp != NULL; vp = vp->fwd){ v = vp->v; if(v->callno > callno) uvputvr_c(tout,v->type,v->name,v->buf,VARLEN(v)); } } /************************************************************************/ int uvvarupd_c(int vhan) { VARIABLE *v; VARHAND *vh; VARPNT *vp; int callno; vh = varhands[vhan-1]; callno = vh->callno; vh->callno = uvs[vh->tno]->callno; for(vp = vh->varhd; vp != NULL; vp = vp->fwd){ v = vp->v; if(v->callno > callno) return(FORT_TRUE); } return(FORT_FALSE); } /************************************************************************/ void uvrdvr_c(int tno,int type,Const char *var,char *data,const char *def,int n) /**uvrdvr -- Return the value of a UV variable. */ /*&rjs */ /*:uv-i/o */ /*+ FORTRAN call sequence: subroutine uvrdvra(tno,varname,adata,adefault) subroutine uvrdvri(tno,varname,idata,idefault) subroutine uvrdvrr(tno,varname,rdata,rdefault) subroutine uvrdvrd(tno,varname,ddata,ddefault) subroutine uvrdvrc(tno,varname,cdata,cdefault) integer tno character varname*(*) character adata*(*),adefault*(*) integer idata, idefault real rdata, rdefault double precision ddata,ddefault complex cdata,cdefault These routines get the first value of a variable. If the variable is missing,the default value is returned. Input: tno The handle of the uv data file. varname The name of the variable to return. default The default value. Output: data The returned value of the variable. */ /*-- */ /*----------------------------------------------------------------------*/ { VARIABLE *v; int deflt,oktype; v = uv_locvar(tno,(char *)var); oktype = TRUE; deflt = (v == NULL); if(!deflt) deflt = (v->buf == NULL) || (v->length == 0); if(!deflt){ switch(type){ case H_BYTE: oktype = (v->type == H_BYTE); n = min(n-1,v->length); if(oktype)memcpy(data,v->buf,n); break; case H_INT: switch(v->type){ case H_INT: *(int *)data = *(int *)(v->buf); break; case H_REAL: *(int *)data = *(float *)(v->buf); break; case H_DBLE: *(int *)data = *(double *)(v->buf); break; default: oktype = FALSE; break; } break; case H_REAL: switch(v->type){ case H_INT: *(float *)data = *(int *)(v->buf); break; case H_REAL: *(float *)data = *(float *)(v->buf); break; case H_DBLE: *(float *)data = *(double *)(v->buf); break; default: oktype = FALSE; break; } break; case H_DBLE: switch(v->type){ case H_INT: *(double *)data = *(int *)(v->buf); break; case H_REAL: *(double *)data = *(float *)(v->buf); break; case H_DBLE: *(double *)data = *(double *)(v->buf); break; default: oktype = FALSE; break; } break; case H_CMPLX: oktype = (v->type == H_CMPLX); if(oktype)memcpy(data,v->buf,internal_size[type]); break; default: oktype = FALSE; } }else{ if( type == H_BYTE ) n = min(n-1,(int)strlen(def)); else n = internal_size[type]; memcpy(data,def,n); } /* Give a fatal error message if there is a type mismatch. */ if(!oktype) ERROR('f',(message,"Type incompatiblity for variable %s, in UVRDVR",var)); /* Null terminate the data, if its a character string. */ if( type == H_BYTE ) *(data + n) = 0; } /************************************************************************/ void uvgetvr_c(int tno,int type,Const char *var,char *data,int n) /**uvgetvr -- Get the values of a uv variable. */ /*&rjs */ /*:uv-i/o */ /*+ FORTRAN call sequence: subroutine uvgetvra(tno,varname,adata) subroutine uvgetvri(tno,varname,idata,n) subroutine uvgetvrr(tno,varname,rdata,n) subroutine uvgetvrd(tno,varname,ddata,n) subroutine uvgetvrc(tno,varname,cdata,n) integer tno,n character varname*(*) character adata*(*) integer idata(n) real rdata(n) double precision ddata(n) complex cdata(n) These routines return the current value of a uv variable. N gives the size of elements in the return array. This MUST match with the actual number of elements in the variable. An exception is for the character string routine, where the size of the "adata" string must be strictly greater than (not equal to!) the size of the string. Input: tno The handle of the uv data file. varname The name of the variable to return. n The number of elements to return. This must agree with the size of the variable! Output: data The returned values of the variable. */ /*-- */ /*----------------------------------------------------------------------*/ { VARIABLE *v; int size; v = uv_locvar(tno,(char *)var); if(v == NULL) ERROR('f',(message,"Variable %s not found, in UVGETVR",var)); size = external_size[type]; if( type != v->type ) ERROR('f',(message,"Variable %s has wrong type, in UVGETVR",var)); if(v->buf == NULL) ERROR('f',(message,"Variable %s currently has no value, in UVGETVR",var)); if( (type == H_BYTE ? n < v->length + 1 : n*size != v->length) ) ERROR('f',(message,"Buffer for variable %s has wrong size, in UVGETVR (%d != %d)", var,n*size,v->length)); /* Copy the data. */ memcpy(data,v->buf,internal_size[type]*v->length/size); /* Null terminate the data, if its a character string. */ if( type == H_BYTE ) *(data + v->length) = 0; } /************************************************************************/ void uvprobvr_c(int tno,Const char *var,char *type,int *length,int *updated) /**uvprobvr -- Return information about a variable. */ /*&rjs */ /*:uv-i/o */ /*+ FORTRAN call sequence: subroutine uvprobvr(tno,varname,type,length,update) integer tno,length character varname*(*),type*1 logical update This checks whether a particular variable exists. If it does, this passes back the type and (current) length of the variable, and whether it was updated on the last call to uvread or uvscan. Input: tno The handle of the input uv data file. varname The name of the variable to check. Output: type The type of the variable. If the variable does not exist, this is blank. Otherwise it is one of 'a', 'r', 'i', 'd' or 'c'. length The number of elements in the uv variable. If this is not known (which is true if the variable has never been read) then this will be zero. update This will be set .true. if this variable was updated on the last call to uvread or uvscan. */ /*-- */ /*----------------------------------------------------------------------*/ { UV *uv; VARIABLE *v; uv = uvs[tno]; v = uv_locvar(tno,(char *)var); if(v == NULL) { *type = ' '; *length = 0; *updated = FORT_FALSE; } else { *type = VARTYPE(v); *length = VARLEN(v); *updated = (v->callno >= uv->mark ? FORT_TRUE : FORT_FALSE); } } /************************************************************************/ void uvputvr_c(int tno,int type,Const char *var,Const char *data,int n) /**uvputvr -- Write the value of a uv variable. */ /*&rjs */ /*:uv-i/o */ /*+ FORTRAN call sequence: subroutine uvputvra(tno,varname,adata) subroutine uvputvri(tno,varname,idata,n) subroutine uvputvrr(tno,varname,rdata,n) subroutine uvputvrd(tno,varname,ddata,n) subroutine uvputvrc(tno,varname,cdata,n) integer tno,n character varname*(*) character adata*(*) integer idata(n) real rdata(n) double precision ddata(n) complex cdata(n) These routines write new values for a uv variable. N gives the number of elements to write. Input: tno The handle of the uv data file. varname The name of the variable to write. n The number of elements to write. data The values of the variable. */ /*-- */ /*----------------------------------------------------------------------*/ { UV *uv; VARIABLE *v; int size,iostat,changed,length,i; char *in1,*in2; if(n <= 0){ ERROR('w',(message,"Variable %s has zero or negative size, in UVPUTVR",var)); return; } uv = uvs[tno]; v = uv_mkvar(tno,(char *)var,type); if(v->type != type) ERROR('f',(message,"Variable %s has changed type, in UVPUTVR",var)); size = external_size[type]; /* If the size of this variable has changed, write it out to the file. */ changed = (v->flags & UVF_NOCHECK); if(v->length != size*n){ changed = TRUE; v->length = size * n; var_size_hdr[0] = v->index; hwriteb_c(uv->item,var_size_hdr,uv->offset,UV_HDR_SIZE,&iostat); CHECK(iostat,(message,"Error writing variable-length header for %s, in UVPUTVR",var)); hwritei_c(uv->item,&v->length,uv->offset+UV_HDR_SIZE,H_INT_SIZE,&iostat); CHECK(iostat,(message,"Error writing variable-length for %s, in UVPUTVR",var)); uv->offset += UV_ALIGN; if( !(v->flags & UVF_NOCHECK) ) v->buf = Realloc(v->buf,n*internal_size[type]); } /* Check if this variable has really changed. */ if( !changed ) { length = internal_size[type] * n; in1 = v->buf; in2 = (char *)data; for( i = 0; i < length; i++ ) { if(*in1++ != *in2++){ changed = TRUE; break; } } } /* Write out the data itself. */ if( changed ) { var_data_hdr[0] = v->index; hwriteb_c(uv->item,var_data_hdr,uv->offset,UV_HDR_SIZE,&iostat); CHECK(iostat,(message,"Error writing variable-value header for %s, in UVPUTVR",var)); uv->offset += mroundup(UV_HDR_SIZE,size); hwrite_c(uv->item,type,data,uv->offset,v->length,&iostat); CHECK(iostat,(message,"Error writing variable-value for %s, in UVPUTVR",var)); uv->offset = mroundup( uv->offset+v->length, UV_ALIGN); if(v->callno++ > CHECK_THRESH) { v->flags |= UVF_NOCHECK; } else if(!(v->flags & UVF_NOCHECK)){ length = internal_size[type] * n; memcpy(v->buf,data,length); } } else { v->callno = 0; } } /************************************************************************/ void uvtrack_c(int tno,Const char *name,Const char *switches) /**uvtrack -- Set flags and switches associated with a uv variable. */ /*&rjs */ /*:uv-i/o */ /*+ FORTRAN call sequence: subroutine uvtrack(tno,varname,switches) integer tno character varname*(*),switches*(*) UVTRACK allows the programmer to set switches and flags associated with a particular uv variable, to allow extra processing steps of that variable. Input: tno The handle of the input uv file. varname The name of the variable of interest. switches This is a character string, each character of which causes a particular flag or switch to be turned on for this particular variable. Valid values are: 'u' Remember if this variable gets updated, and when it gets updated, uvupdate returns .true. the next time it is called. 'c' Remember if this variable gets updated, and when it gets updated, cause it to be copied during the next call to uvcopyvr. */ /*-- */ /*----------------------------------------------------------------------*/ { UV *uv; VARIABLE *v; uv = uvs[tno]; v = uv_locvar(tno,(char *)name); if(v == NULL) return; while(*switches)switch(*switches++){ case 'u': v->flags |= UVF_UPDATED; uv->flags |= UVF_UPDATED; break; case 'c': v->flags |= UVF_COPY; uv->flags |= UVF_COPY; break; case ' ': break; default: ERROR('w',(message,"Unrecognised switch %c, in UVTRACK",*(switches-1))); break; } } /************************************************************************/ int uvscan_c(int tno,Const char *var) /**uvscan -- Scan a uv file until a variable changes. */ /*&rjs */ /*:uv-i/o */ /*+ FORTRAN call sequence: integer function uvscan(tno,varname) integer tno character varname*(*) Scan through a uv file until a particular variable changes. This always reads to the end of the record (i.e. until all variables that change simultaneously are read) after "varname" was encountered. Input: tno The handle of the uv file to be scanned. varname The variable to terminate the search. Output: uvscan_c 0 on success, -1 on end-of-file. Standard error number otherwise. */ /*-- */ /*----------------------------------------------------------------------*/ { UV *uv; VARIABLE *v; /* Locate the variable to scan on. */ uv = uvs[tno]; if(*var){ v = uv_locvar(tno,(char *)var); if(v == NULL) ERROR('f',(message,"Variable %s not found, in UVSCAN",var)); } else v = NULL; uv->mark = uv->callno + 1; uv->flags &= ~(UVF_UPDATED | UVF_COPY); return( uv_scan(uv,v) ); } /************************************************************************/ private int uv_scan(UV *uv, VARIABLE *vt) /* Scan the UV data stream until we have all the data we desire. Inputs: uv Structure describing uv file to scan through. vt Structure describing variable to terminate scan when found. ------------------------------------------------------------------------*/ { int iostat,intsize,extsize,terminate,found,changed,i,itemp; off_t offset; VARIABLE *v; char s[UV_HDR_SIZE],*b; uv->callno++; offset = uv->offset; found = (vt == NULL); terminate = FALSE; while(!terminate){ if(offset >= uv->max_offset) return -1; hreadb_c(uv->item,s,offset,UV_HDR_SIZE,&iostat); if(iostat == -1)return(-1); else CHECK(iostat,(message,"Error reading a record header, while UV scanning")); /* Remember that this was updated, and set the "updated" flag if necessary. Save the internal and external size of an element of this type. */ changed = FALSE; if(*(s+2) != VAR_EOR){ itemp = *s; v = &uv->variable[itemp]; intsize = internal_size[v->type]; extsize = external_size[v->type]; } switch(*(s+2)){ /* Process a specification of a variables length. Allocate buffers if needed. */ case VAR_SIZE: hreadi_c(uv->item,&v->flength,offset+UV_HDR_SIZE,H_INT_SIZE,&iostat); CHECK(iostat,(message,"Error reading a variable-length for %s, while UV scanning",v->name)); if(v->flength <= 0) ERROR('f',(message,"Variable %s has length of %d, when scanning", v->name,v->flength)); if(v->flength % extsize) ERROR('f',(message, "Non-integral no. elements in variable %s, when scanning",v->name)); if(!(v->flags & UVF_OVERRIDE) || v->type != H_BYTE){ v->length = v->flength; v->buf = Realloc( v->buf, (v->flength * intsize)/extsize ); if(v->flags & UVF_OVERRIDE && v->flength > extsize) for(i=1, b = v->buf + intsize; i < v->flength/extsize; i++,b += intsize) memcpy(b,v->buf,intsize); changed = TRUE; } offset += UV_ALIGN; break; /* Process the data of a variable. If we want to keep track of the value of this variable, read it. */ case VAR_DATA: offset += mroundup(UV_HDR_SIZE,extsize); if(!(v->flags & UVF_OVERRIDE)){ hread_c(uv->item,v->type,v->buf,offset,v->flength,&iostat); CHECK(iostat,(message,"Error reading a variable value for %s, while UV scanning",v->name)); changed = TRUE; } offset = mroundup(offset+v->flength,UV_ALIGN); found |= (v == vt); break; /* End of a block of synchronised data. */ case VAR_EOR: terminate = found; offset += UV_ALIGN; break; /* Something is wrong. */ default: ERROR('f',(message,"Unrecognised record code %d, when scanning",*(s+2))); } if(changed){ v->callno = uv->callno; uv->flags |= v->flags & (UVF_UPDATED | UVF_UPDATED_PLANET | UVF_UPDATED_SKYFREQ | UVF_UPDATED_UVW | UVF_COPY); } } uv->offset = offset; return 0; } /************************************************************************/ void uvwrite_c(int tno,Const double *preamble,Const float *data, Const int *flags,int n) /**uvwrite -- Write correlation data to a uv file. */ /*&rjs */ /*:uv-i/o */ /*+ FORTRAN call sequence: subroutine uvwrite(tno,preamble,data,flags,n) integer tno,n double precision preamble(*) complex data(n) logical flags(n) Write a visibility record to the data file. Please note uvwrite() closes the record. Any wideband data should have been written with uvwwrite() before this call. Input: tno Handle of the uv data set. n Number of channels to be written. preamble A double array of 4 elements giving u,v, time and baseline number (in that order). data A complex array of n elements containing the correlation data. flags Logical array of n elements. A true value for a channel indicates good data. */ /*-- */ /*----------------------------------------------------------------------*/ { UV *uv; int i,nchan,i1,i2,nuvw,itemp; float maxval,scale,*p,temp; double *d,dtemp; int *q; char *counter,*status; FLAGS *flags_info; VARIABLE *v; uv = uvs[tno]; /* Initialise things if this is the first call to uvwrite. */ if(!(uv->flags & UVF_INIT)){ uv->flags |= UVF_INIT; if( uv->data_line.linetype == LINE_NONE) uv->data_line.linetype = LINE_CHANNEL; if( uv->data_line.linetype == LINE_CHANNEL){ if( uv->corr == NULL ) uv->corr = uv_mkvar(tno,"corr", ( uv->minsize2 < 0 || n <= uv->minsize2 ? H_REAL : H_INT2) ); uv->corr->flags |= UVF_NOCHECK; if(uv->corr_flags.handle == NULL){ status = (uv->corr_flags.offset == 0 ? "new" : "old"); uv->corr_flags.handle = mkopen_c(uv->tno,"flags",status); } if( uv->corr_flags.handle == NULL) BUG('f',"Failed to open the corr flags, in UVWRITE"); } else if( uv->data_line.linetype == LINE_WIDE){ if( uv->wcorr == NULL ) uv->wcorr = uv_mkvar(tno,"wcorr", H_CMPLX); uv->wcorr->flags |= UVF_NOCHECK; if( uv->wcorr_flags.handle == NULL){ status = (uv->wcorr_flags.offset == 0 ? "new" : "old"); uv->wcorr_flags.handle = mkopen_c(uv->tno,"wflags",status); } if( uv->wcorr_flags.handle == NULL) BUG('f',"Failed to open the wcorr flags, in UVWRITE"); } else BUG('f',"Unrecognised or unsupported linetype, in UVWRITE"); /* Create the preamble variables, if needed. */ if( uv->coord == NULL ){ uv->coord = uv_mkvar(tno,"coord",H_DBLE); uv->coord->flags |= UVF_NOCHECK; if(uv->coord->buf == NULL){ uv->coord->buf = Malloc(3*sizeof(double)); d = (double *)(uv->coord->buf); *d = *(preamble) + 1000; } } if( uv->time == NULL ){ uv->time = uv_mkvar(tno,"time",H_DBLE); uv->time->flags |= UVF_NOCHECK; if(uv->time->buf == NULL){ uv->time->buf = Malloc(sizeof(double)); *(double *)(uv->time->buf) = *(preamble+2) + 1000; } } if( uv->bl == NULL ){ uv->bl = uv_mkvar(tno,"baseline",H_REAL); uv->bl->flags |= UVF_NOCHECK; if(uv->bl->buf == NULL){ uv->bl->buf = Malloc(sizeof(float)); *(float *)(uv->bl->buf) = *(preamble + 3) + 1000; } } } /* Get info on whether we are dealing with corr or wcorr data. */ if(uv->data_line.linetype == LINE_WIDE){ counter = "nwide"; v = uv->wcorr; flags_info = &(uv->wcorr_flags); } else { counter = "nchan"; v = uv->corr; flags_info = &(uv->corr_flags); } /* Update the size of the variable, if necessary. */ nchan = NUMCHAN(v); if(n != nchan) uvputvri_c(tno,counter,&n,1); /* Write out the flagging info. */ if(uv->flags & UVF_RUNS) mkwrite_c(flags_info->handle,MK_RUNS,(int *)(flags+1),flags_info->offset,n,*flags); else mkwrite_c(flags_info->handle,MK_FLAGS,(int *)flags,flags_info->offset,n,n); flags_info->offset += n; /* Write out the correlation data. */ if(v->type == H_REAL){ uvputvrr_c(tno,v->name,data,2*n); } else if(v->type == H_CMPLX) { uvputvrc_c(tno,v->name,data,n); } else { if(v->length != 2*n*H_INT2_SIZE) v->buf = Realloc(v->buf,2*n*sizeof(int)); maxval = 0; p = (float *)data; for(i=0; i < 2*n; i++){ temp = *p++; if(temp < 0)temp = -temp; maxval = max(maxval,temp); } if(maxval == 0) maxval = 1; scale = maxval / 32767; uvputvrr_c(tno,"tscale",&scale,1); scale = 32767 / maxval; p = (float *)data; q = (int *)v->buf; for(i=0; i < 2*n; i++) *q++ = scale * *p++; q = (int *)v->buf; uvputvrj_c(tno,v->name,(int *)v->buf,2*n); } /* Write out the preamble. */ d = (double *)(uv->coord->buf); nuvw = (uv->flags & UVF_DOW ? 3 : 2); if( *d != *preamble || *(d+1) != *(preamble+1) || ( nuvw == 3 && *(d+2) != *(preamble+2) ) ){ uvputvrd_c(tno,"coord",preamble,nuvw); *d = *preamble; *(d+1) = *(preamble+1); if(nuvw == 3) *(d+2) = *(preamble+2); } preamble += nuvw; dtemp = *preamble++; if( dtemp != *(double *)(uv->time->buf) ){ uvputvrd_c(tno,"time",&dtemp,1); *(double *)(uv->time->buf) = dtemp; } temp = *preamble++; if( temp != *(float *)(uv->bl->buf) ){ itemp = temp; uvbasant_c(itemp,&i1,&i2); uv->flags |= ( i1 == i2 ? UVF_AUTO : UVF_CROSS); uvputvrr_c(tno,"baseline",&temp,1); *(float *)(uv->bl->buf) = temp; } /* Write an end-of-record marker. */ uvnext_c(tno); } /************************************************************************/ void uvwwrite_c(int tno,Const float *data,Const int *flags,int n) /**uvwwrite -- Write wide-band correlation data to a uv file. */ /*&rjs */ /*:uv-i/o */ /*+ FORTRAN call sequence: subroutine uvwwrite(tno,data,flags,n) integer tno,n complex data(n) logical flags(n) Write a wide-band visibility record to the data file. Make sure this routine is called before uvwrite(), since that closes the record. Input: tno Handle of the uv data set. n Number of channels to be written. data A complex array of n elements containing the correlation data. flags Logical array of n elements. A true value for a channel indicates good data. */ /*-- */ /*----------------------------------------------------------------------*/ { UV *uv; int nchan; VARIABLE *v; char *status; uv = uvs[tno]; /* Initialise things if needed. */ if( uv->wcorr == NULL ){ uv->wcorr = uv_mkvar(tno,"wcorr", H_CMPLX); uv->wcorr->flags |= UVF_NOCHECK; } if( uv->wcorr_flags.handle == NULL) { status = (uv->wcorr_flags.offset == 0 ? "new" : "old" ); uv->wcorr_flags.handle = mkopen_c(uv->tno,"wflags",status); if(uv->wcorr_flags.handle == NULL) BUG('f',"Failed to open the wcorr flags, in UVWWRITE"); } /* Update the size of the variable, if necessary. */ v = uv->wcorr; nchan = NUMCHAN(v); if(n != nchan) uvputvri_c(tno,"nwide",&n,1); /* Write out the flagging info. */ if(uv->flags & UVF_RUNS) mkwrite_c(uv->wcorr_flags.handle,MK_RUNS,(int *)(flags+1),uv->wcorr_flags.offset, n,*flags); else mkwrite_c(uv->wcorr_flags.handle,MK_FLAGS,(int *) flags,uv->wcorr_flags.offset, n,n); uv->wcorr_flags.offset += n; /* Write out the correlation data. */ uvputvrc_c(tno,v->name,data,n); } /************************************************************************/ void uvsela_c(int tno,Const char *object,Const char *string,int datasel) /** uvsela -- Select or reject uv data, based on a character string */ /*&rjs */ /*:uv-i/o */ /*+ FORTRAN call sequence: subroutine uvsela(tno,object,string,flag) integer tno character object*(*),string*(*) logical flag This specifies the portion of the data to be selected by calls to uvread. This sets the value of a character string to compare against. Input: tno Handle of the uv data file. object This can be one of "source". string String value, used in the selection process. flag If true, the data is selected. If false, the data is discarded. */ /*-- */ /*----------------------------------------------------------------------*/ { UV *uv; SELECT *sel; int discard; uv = uvs[tno]; discard = !datasel; uv->flags &= ~UVF_INIT; sel = uv->select; /* Either move to the last "SELECT" structure, or create the new structure. */ if(sel != NULL)while(sel->fwd != NULL) sel = sel->fwd; else{ sel = (SELECT *)Malloc(sizeof(SELECT)); sel->amp.select = sel->selants = sel->win.select = FALSE; sel->fwd = NULL; sel->opers = NULL; sel->maxoper = sel->noper = 0; sel->and = TRUE; uv->select = sel; } /* Selection by source or purpose. */ if(!strcmp(object,"source")){ uv_addopers(sel,SEL_SRC,discard,0.0,0.0,string); uv->need_src = TRUE; } else if (!strcmp(object,"purpose")) { uv_addopers(sel,SEL_PURP,discard,0.0,0.0,string); uv->need_purp = TRUE; /* Some unknown form of selection. */ } else { ERROR('w',(message, "Unrecognised selection \"%s\" ignored, in UVSELA",object)); } } /************************************************************************/ void uvselect_c(int tno,Const char *object,double p1,double p2,int datasel) /**uvselect -- Select or reject uv data. */ /*&rjs */ /*:uv-i/o */ /*+ FORTRAN call sequence: subroutine uvselect(tno,object,p1,p2,flag) integer tno character object*(*) double precision p1,p2 logical flag This specifies the portion of the data to be selected by calls to uvread. Normally data that are not selected, are not returned. Exceptions are the "window" and "amplitude" objects, which cause the corresponding visibilities to be flagged as bad, but returned anyway. Input: tno Handle of the uv data file. object This can be one of "time","antennae","visibility", "uvrange","pointing","amplitude","window","or","dra", "ddec","uvnrange","increment","ra","dec","and", "clear", "on","polarization","shadow","auto","dazim","delev", "purpose","seeing" (should be 28?) p1,p2 Generally this is the range of values to select. For "antennae", this is the two antennae pair to select. For "antennae", a zero indicates "all antennae". For "shadow", a zero indicates use "antdiam" variable. For "on","window","polarization","increment","shadow" only p1 is used. For "and","or","clear","auto" p1 and p2 are ignored. flag If true, the data is selected. If false, the data is discarded. Ignored for "and","or","clear". */ /*-- */ /*----------------------------------------------------------------------*/ { UV *uv; SELECT *sel; int i,i1,i2,discard; uv = uvs[tno]; discard = !datasel; uv->flags &= ~UVF_INIT; if(!strcmp(object,"clear")){ uv_free_select(uv->select); uv->select = NULL; return; } /* Ignore "and" and "or" objects if this is the first call. */ sel = uv->select; if(sel == NULL && (!strcmp(object,"or") || !strcmp(object,"and"))) return; /* Either move to the last "SELECT" structure, or create the new structure. */ if(sel != NULL)while(sel->fwd != NULL) sel = sel->fwd; else{ sel = (SELECT *)Malloc(sizeof(SELECT)); sel->amp.select = sel->selants = sel->win.select = FALSE; sel->fwd = NULL; sel->opers = NULL; sel->maxoper = sel->noper = 0; sel->and = TRUE; uv->select = sel; } /* AND or OR operation. */ if(!strcmp(object,"or") || !strcmp(object,"and")){ sel->fwd = (SELECT *)Malloc(sizeof(SELECT)); sel = sel->fwd; sel->amp.select = sel->selants = sel->win.select = FALSE; sel->opers = NULL; sel->fwd = NULL; sel->maxoper = sel->noper = 0; sel->and = (*object == 'a'); /* Selection by time. */ } else if(!strcmp(object,"time")){ if(p1 >= p2) BUG('f',"Min time is greater than or equal to max time, in UVSELECT."); uv_addopers(sel,SEL_TIME,discard,p1,p2,(char *)NULL); /* Selection by "on" parameter. */ } else if(!strcmp(object,"on")){ uv_addopers(sel,SEL_ON,discard,p1,p1,(char *)NULL); uv->need_on = TRUE; /* Selection by polarisation. */ } else if(!strcmp(object,"polarization")){ uv_addopers(sel,SEL_POL,discard,p1,p1,(char *)NULL); uv->need_pol = TRUE; /* Offset parameters, dazim and delev. */ } else if(!strcmp(object,"dazim")){ if(p1 >= p2) BUG('f',"Min dazim is greater than or equal to max dazim, in UVSELECT."); uv_addopers(sel,SEL_DAZIM,discard,p1,p2,(char *)NULL); uv->need_dazim = TRUE; } else if(!strcmp(object,"delev")){ if(p1 >= p2) BUG('f',"Min delev is greater than or equal to max delev, in UVSELECT."); uv_addopers(sel,SEL_DELEV,discard,p1,p2,(char *)NULL); uv->need_delev = TRUE; /* Subfield parameters, dra and ddec. */ } else if(!strcmp(object,"dra")){ if(p1 >= p2) BUG('f',"Min dra is greater than or equal to max dra, in UVSELECT."); uv_addopers(sel,SEL_DRA,discard,p1,p2,(char *)NULL); uv->need_dra = TRUE; } else if(!strcmp(object,"ddec")){ if(p1 >= p2) BUG('f',"Min ddec is greater than or equal to max ddec, in UVSELECT."); uv_addopers(sel,SEL_DDEC,discard,p1,p2,(char *)NULL); uv->need_ddec = TRUE; /* Phase centre parameters, ra and dec. */ } else if(!strcmp(object,"ra")){ if(p1 >= p2) BUG('f',"Min ra is greater than or equal to max ra, in UVSELECT."); uv_addopers(sel,SEL_RA,discard,p1,p2,(char *)NULL); uv->need_ra = TRUE; } else if(!strcmp(object,"dec")){ if(p1 >= p2) BUG('f',"Min dec is greater than or equal to max dec, in UVSELECT."); uv_addopers(sel,SEL_DEC,discard,p1,p2,(char *)NULL); uv->need_dec = TRUE; /* Hour angle, LST and elevation. */ } else if(!strcmp(object,"ha")){ if(p1 >= p2) BUG('f',"Min HA is greater than or equal to max HA, in UVSELECT."); uv_addopers(sel,SEL_HA,discard,p1,p2,(char *)NULL); uv->need_obsra = TRUE; uv->need_lst = TRUE; } else if(!strcmp(object,"lst")){ uv_addopers(sel,SEL_LST,discard,p1,p2,(char *)NULL); uv->need_lst = TRUE; } else if(!strcmp(object,"elevation")){ if(p1 >= p2) BUG('f',"Min elevation is greater than or equal to max elevation, in UVSELECT."); uv_addopers(sel,SEL_ELEV,discard,p1,p2,(char *)NULL); uv->need_elev = TRUE; /* Selection by uv baseline. */ } else if(!strcmp(object,"uvrange")){ if(p1 >= p2) BUG('f',"Min uv is greater than or equal to max uv in UVSELECT"); if(p1 < 0) BUG('f',"Min uv is negative, in UVSELECT"); uv_addopers(sel,SEL_UV,discard,p1*p1,p2*p2,(char *)NULL); uv->need_skyfreq = TRUE; /* Select by sky frequency of the first channel. */ } else if(!strcmp(object,"frequency")){ if(p1 >= p2 || p1 <= 0) BUG('f', "Illegal values for sky frequency selection, in UVSELECT"); uv_addopers(sel,SEL_FREQ,discard,p1,p2,(char *)NULL); uv->need_skyfreq = TRUE; /* Selection by uv baseline, given in nanosec. */ } else if(!strcmp(object,"uvnrange")){ if(p1 >= p2) BUG('f',"Min uv is greater than or equal to max uv in UVSELECT"); if(p1 < 0) BUG('f',"Min uv is negative, in UVSELECT"); uv_addopers(sel,SEL_UVN,discard,p1*p1,p2*p2,(char *)NULL); /* Selection by pointing parameter. */ } else if(!strcmp(object,"pointing")){ if(p1 >= p2) BUG('f',"Min pointing is greater than or equal to max pointing, in UVSELECT"); if(p1 < 0) BUG('f',"Min pointing is negative, in UVSELECT"); uv_addopers(sel,SEL_POINT,discard,p1,p2,(char *)NULL); uv->need_point = TRUE; /* Selection by seeing parameter. */ } else if(!strcmp(object,"seeing")){ if(p1 >= p2) BUG('f',"Min seeing is greater than or equal to max seeing, in UVSELECT"); if(p1 < 0) BUG('f',"Min seeing is negative, in UVSELECT"); uv_addopers(sel,SEL_SEEING,discard,p1,p2,(char *)NULL); uv->need_seeing = TRUE; /* Selection by visibility number. */ } else if(!strcmp(object,"visibility")){ if(p1 > p2) BUG('f',"Min visib is greater than max visib, in UVSELECT"); if(p1 < 1) BUG('f',"Min visibility is negative, in UVSELECT"); uv_addopers(sel,SEL_VIS,discard,p1,p2,(char *)NULL); /* Selection by visibility increment. */ } else if(!strcmp(object,"increment")){ if(p1 < 1) BUG('f',"Bad increment selected, in UVSELECT."); uv_addopers(sel,SEL_INC,discard,p1,0.0,(char *)NULL); /* Selection by shadowing. */ } else if(!strcmp(object,"shadow")){ if(p1 != 0 || p2 < 0) BUG('f',"Bad antenna diameter, in UVSELECT."); uv_addopers(sel,SEL_SHADOW,discard,p1,p2,(char *)NULL); uv->need_uvw = TRUE; /* Pulsar bin selection. */ } else if(!strcmp(object,"bin")){ if(p1 < 1 || p2 < p1) BUG('f',"Bad pulsar bin number, in UVSELECT."); uv_addopers(sel,SEL_BIN,discard,p1,p2,(char *)NULL); uv->need_bin = TRUE; /* Amplitude selection. */ } else if(!strcmp(object,"amplitude")){ if(sel->amp.select) BUG('f',"Cannot handle multiple amplitude selections in a clause"); if(p1 < 0 || p2 <= p1) BUG('f',"Bad amplitude range selected, in UVSELECT."); sel->amp.discard = discard; sel->amp.loval = p1; sel->amp.hival = p2; sel->amp.select = TRUE; /* Window selection. */ } else if(!strcmp(object,"window")){ if(!sel->win.select) for(i=0; i < MAXWIN; i++)sel->win.wins[i] = discard; i = p1 + 0.5; if(i < 1 || i > MAXWIN) BUG('f',"Too many windows"); sel->win.wins[i-1] = !discard; uv->need_win = TRUE; sel->win.select = TRUE; sel->win.n = 0; for(i=0; i < MAXWIN; i++) if(sel->win.wins[i]){ if(sel->win.n == 0) sel->win.first = i; sel->win.last = i; sel->win.n++; } /* Autocorrelation data. */ } else if(!strcmp(object,"auto")){ if(!sel->selants){ for(i=0; i < MAXANT*(MAXANT+1)/2; i++)sel->ants[i] = !discard; sel->selants = TRUE; } for(i=0; i < MAXANT; i++)sel->ants[((i+1)*i)/2 + i] = discard; /* Antennae and baseline selection. */ } else if(!strcmp(object,"antennae")){ if(!sel->selants){ for(i=0; i < MAXANT*(MAXANT+1)/2; i++)sel->ants[i] = !discard; sel->selants = TRUE; } i1 = max(p1, p2) + 0.5; i2 = min(p1, p2) + 0.5; if(i1 < 0 || i1 > MAXANT) ERROR('f',(message,"bad antennae %d",i1)); if(i2 < 0 || i2 > MAXANT) ERROR('f',(message,"bad antennae %d",i2)); if(i1 == 0){ for(i=0; i < MAXANT*(MAXANT+1)/2; i++)sel->ants[i] = discard; } else if(i2 == 0){ for(i=1; i <= i1; i++) sel->ants[(i1*(i1-1))/2+i-1] = discard; for(i=i1+1; i <= MAXANT; i++) sel->ants[(i*(i-1))/2+i1 -1] = discard; } else { sel->ants[(i1*(i1-1))/2+i2-1] = discard; } /* Some unknown form of selection. */ } else { ERROR('w',(message, "Unrecognised selection \"%s\" ignored, in UVSELECT",object)); } } /************************************************************************/ private void uv_addopers(SELECT *sel,int type,int discard,double p1,double p2,char *ps) { int n,i; OPERS *oper; /* Allocate more space if needed. */ if(sel->noper == sel->maxoper){ sel->maxoper = max(2*sel->maxoper,16); sel->opers = (OPERS *)Realloc((char *)(sel->opers),sel->maxoper*sizeof(OPERS)); } /* Shift the list down, to make space for the newcomer. */ n = sel->noper++; for(i = n-1; i >= 0; i--){ oper = sel->opers + i; if( oper->type > type ) memcpy((char *)(oper+1),(char *)oper,sizeof(OPERS)); else break; } /* Squeeze the newcomer in. */ oper = sel->opers + i + 1; oper->type = type; oper->discard = discard; oper->loval = p1; oper->hival = p2; oper->stval = NULL; if(ps != NULL){ oper->stval = (char *)Malloc(strlen(ps)+1); strcpy(oper->stval,ps); } } /************************************************************************/ void uvset_c(int tno,Const char *object,Const char *type, int n,double p1,double p2,double p3) /**uvset -- Set up the uv linetype, and other massaging steps. */ /*&rjs */ /*:uv-i/o */ /*+ FORTRAN call sequence: subroutine uvset(tno,object,type,n,p1,p2,p3) integer tno character object*(*),type*(*) integer n real p1,p2,p3 Set up the way uvread behaves. This determines whether uvread returns correlation channels, wide-band channels or velocity channels. This also sets up whether u and v are returned in wavelengths or nanosec, and what planet processing is performed. Input: tno Handle of the uv data set. object Name of the object that we are setting the type of. type The type of data that the user wants returned. n Some integer parameter. p1,p2,p3 Some real parameters. */ /*-- */ /*----------------------------------------------------------------------*/ { UV *uv; uv = uvs[tno]; uv->flags &= ~UVF_INIT; if(!strcmp(object,"data")){ uvset_linetype(&uv->data_line,type,n,p1,p2,p3); } else if(!strcmp(object,"reference")) { uvset_linetype(&uv->ref_line,type,1,p1,p2,p2); } else if(!strcmp(object,"coord")) { uvset_coord(uv,type); } else if(!strcmp(object,"planet")) { uvset_planet(uv,p1,p2,p3); } else if(!strcmp(object,"preamble")) { uvset_preamble(uv,type); } else if(!strcmp(object,"selection")) { uvset_selection(uv,type,n); } else if(!strcmp(object,"gflag")) { if(n < 1)bug_c('f',"Invalid value for average channel flagging tolerance"); uv->gflag = n; } else if(!strcmp(object,"flags")) { if(!strcmp(type,"logical")) uv->flags &= ~UVF_RUNS; else if(!strcmp(type,"runs")) uv->flags |= UVF_RUNS; else { ERROR('f',(message,"Unrecognised flags mode \'%s\', in UVSET",type)); } } else if(!strcmp(object,"minsize2")) { uv->minsize2 = n; } else if(!strcmp(object,"corr")) { if(uv->corr != NULL)return; if(!strcmp(type,"r")) uv->corr = uv_mkvar(tno,"corr", H_REAL ); else if(!strcmp(type,"c" )) uv->corr = uv_mkvar(tno,"corr", H_CMPLX ); else if(!strcmp(type,"j")) uv->corr = uv_mkvar(tno,"corr", H_INT2 ); else ERROR('f',(message,"Unsupported correlation type %s, in UVSET",type)); } else { ERROR('w',(message,"Unrecognised object \"%s\" ignored, in UVSET.",object)); } } /************************************************************************/ private void uvset_preamble(UV *uv, char *type) /* Set the preamble that the user wants to use. ------------------------------------------------------------------------*/ { char varnam[MAXNAM+1],*s; int n,ok; VARIABLE *v; uv->flags &= ~UVF_DOW; if(uv->flags & UVF_NEW){ uv->presize = 3; if(!strcmp(type,"uvw/time/baseline"))uv->flags |= UVF_DOW; else if(strcmp(type,"uv/time/baseline")){ ERROR('f',(message,"Unsupported preamble \"%s\",in UVSET.",type));} } else { n = 0; while(*type){ if(n >= MAXPRE){ ERROR('f',(message,"Too many parameters in preamble \"%s\".",type));} /* Get the variable name. */ s = varnam; while(*type != 0 && *type != '/')*s++ = *type++; if(*type == '/')type++; *s = 0; /* Locate the appropriate variable. */ if(!strcmp(varnam,"uv")){ v = uv->prevar[n] = uv_locvar(uv->tno,"coord"); ok = (v != NULL && v->type == H_DBLE); } else if(!strcmp(varnam,"uvw")){ v = uv->prevar[n] = uv_locvar(uv->tno,"coord"); uv->flags |= UVF_DOW; ok = (v != NULL && v->type == H_DBLE); } else { v = uv->prevar[n] = uv_locvar(uv->tno,varnam); ok = (v == NULL || v->type == H_INT || v->type == H_REAL || v->type == H_DBLE); } if(!ok){ERROR('f',(message,"Invalid preamble \"%s\".",type));} n++; } uv->presize = n; } } /************************************************************************/ private void uvset_selection(UV *uv, char *type, int n) /* Set the way the uvselect routine works. ------------------------------------------------------------------------*/ { if(!strcmp(type,"amplitude")){ uv->apply_amp = n > 0; } else if(!strcmp(type,"window")){ uv->apply_win = n > 0; } else { ERROR('w',(message,"Unrecognised type %s ignored, in UVSET(amplitude)",type)); } } /************************************************************************/ private void uvset_planet(UV *uv, double p1,double p2,double p3) /* Set the reference parameters for a planet, for scaling and rotation. ------------------------------------------------------------------------*/ { uv->ref_plmaj = p1; uv->ref_plmin = p2; uv->ref_plangle = p3; uv->need_planet = TRUE; } /************************************************************************/ private void uvset_coord(UV *uv, char *type) /* Set the flags to do with the processing of uv coordinates. Input: uv The UV data structure. type A char string containing a type consisting of the following string separated by dashes. "wavelength" Return u,v in units of wavelengths. "nanosec" Return u,v in units of nanosecs. ------------------------------------------------------------------------*/ { if(!strcmp(type,"wavelength")){ uv->need_skyfreq = TRUE; uv->flags |= UVF_WAVELENGTH; } else if(!strcmp(type,"nanosec")){ uv->flags &= ~UVF_WAVELENGTH; } else{ ERROR('w',(message, "Unrecognised coordinate type \"%s\" ignored, in UVSET",type)); } } /************************************************************************/ private void uvset_linetype(LINE_INFO *line, char *type, int n, double start,double width,double step) /* Decode the line type. Input: line The LINE_INFO structure describing the line type. type A char string being one of "velocity" "channel" "wide" n Number of channels. start First channel to select. width Width of channel. step Increment between channels. ------------------------------------------------------------------------*/ { if(!strcmp(type,"velocity") || !strcmp(type,"felocity")){ if(width < 0) BUG('f',"Bad width in UVSET(line)"); if(n < 0) BUG('f',"Bad number of channels, in UVSET(line)."); if((width == 0 || n == 0) && (step != 0 || start != 0 || width != 0)) BUG('f',"Invalid line parameters in UVSET(line)"); line->linetype = (*type == 'v' ? LINE_VELOCITY : LINE_FELOCITY); line->n = n; line->fstart = start; line->fwidth = width; line->fstep = step; } else if(!strcmp(type,"wide")) { if(width < 1 || step < 1 || step < width) BUG('f',"Bad width or step in UVSET(line)"); if(start < 1 ) BUG('f',"Bad start value in UVSET(line)"); if(n < 0) BUG('f',"Bad number of channels, in UVSET(line)."); line->linetype = LINE_WIDE; line->n = n; line->start = start-1; line->width = width; line->step = step; } else if(!strcmp(type,"channel")) { if(width < 1 || step < 1) BUG('f',"Bad width or step in UVSET(line)"); if(start < 1 ) BUG('f',"Bad start value in UVSET(line)"); if(n < 0) BUG('f',"Bad number of channels, in UVSET(line)."); line->linetype = LINE_CHANNEL; line->n = n; line->start = start-1; line->width = width; line->step = step; } else { ERROR('w',(message, "Unrecognised line type \"%s\" ignored, in UVSET",type)); } } /************************************************************************/ int uvdim_c(tno) int tno; /**uvdim - Number of channels. */ /*&rjs */ /*:uv-i/o */ /*+ FORTRAN call sequence: integer function uvdim(tno) integer tno Input: tno Handle of the uv data set. Output: uvdim Number of channels. */ /*-- */ /*----------------------------------------------------------------------*/ { UV *uv; uv = uvs[tno]; return(uv->actual_line.n); } /************************************************************************/ void uvread_c(int tno,double *preamble,float *data,int *flags,int n,int *nread) /**uvread -- Read in some uv correlation data. */ /*&rjs */ /*:uv-i/o */ /*+ FORTRAN call sequence: subroutine uvread(tno,preamble,data,flags,n,nread) integer tno,n,nread double precision preamble(*) complex data(n) logical flags(n) This reads a single visibility from the data file. This starts by scanning the uv data stream until a correlation record is found. The correlations are then converted to complex numbers if necessary, and returned to the caller. Uvread also performs some massaging (see uvset) and selection (see uvselect) steps. Input: tno Handle of the uv data set. n Max number of channels that can be read. Output: preamble A double array of elements giving things such as u,v, time and baseline number. Setable using uvset. data A real array of at least n complex elements (or 2n real elements). This returns the correlation data. flags Logical array of at least n elements. A true value for a channel indicates good data. nread Number of correlations returned. On end-of-file, zero is returned. */ /*-- */ /*----------------------------------------------------------------------*/ { UV *uv; int more,nchan; VARIABLE *v; uv = uvs[tno]; /* Initialise everything if this is the first call to uvread. */ if(!(uv->flags & UVF_INIT)) uvread_defline(tno); v = (uv->data_line.linetype == LINE_WIDE ? uv->wcorr : uv->corr); uv->corr_flags.init = FALSE; uv->wcorr_flags.init = FALSE; /* Scan the input data stream until we hit some correlation data. */ *nread = 0; more = TRUE; uv->mark = uv->callno + 1; uv->flags &= ~(UVF_UPDATED | UVF_COPY); /* Scan the input data stream, and do any selection necessary. */ while(more){ if(uv->maxvis > 0 && uv->callno > uv->maxvis) return; do { if(uv_scan(uv,(VARIABLE *)NULL) != 0)return; if(!(uv->flags & UVF_INIT)) uvread_init(tno); if(uv->corr != NULL)if(uv->corr->callno == uv->callno){ nchan = NUMCHAN(uv->corr); uv->corr_flags.offset += nchan; } if(uv->wcorr != NULL)if(uv->wcorr->callno == uv->callno){ nchan = NUMCHAN(uv->wcorr); uv->wcorr_flags.offset += nchan; } } while(v->callno < uv->callno); /* Perform uv selection. */ uv->amp = &noamp; uv->win = &truewin; if(uv->select != NULL) more = uvread_select(uv); else more = FALSE; } /* Update the planet parameters, if needed. */ if(uv->flags & UVF_UPDATED_PLANET) uvread_updated_planet(uv); if(uv->flags & UVF_UPDATED_UVW) uvread_updated_uvw(uv); /* Apply linetype processing and planet scaling. */ *nread = uvread_line(uv,&(uv->data_line),data,n,flags,&(uv->actual_line)); if(*nread == 0)return; /* Get preamble variables. */ uvread_preamble(uv,preamble); /* Divide by the reference line if there is one. */ if(uv->ref_line.linetype != LINE_NONE) uvread_reference(uv,data,flags,*nread); } /************************************************************************/ private void uvread_preamble(UV *uv, double *preamble) /* Get the preamble associated with this record. ------------------------------------------------------------------------*/ { VARIABLE *v; double scale,uu,vv,ww,*coord; int bl,i1,i2,i; for(i=0; i < uv->presize; i++){ v = uv->prevar[i]; if(v == NULL){ *preamble++ = 0; } else if(v == uv->coord){ coord = (double *)(uv->coord->buf); uu = coord[0]; vv = coord[1]; if(uv->flags & UVF_REDO_UVW){ bl = *((float *)(uv->bl->buf)) + 0.5; uvbasant_c(bl,&i1,&i2); i1--; i2--; ww = uv->uvw->ww[i2] - uv->uvw->ww[i1]; } else if(uv->flags & UVF_DOW) { ww = (VARLEN(uv->coord) >= 3 ? coord[2] : 0.0); } scale = (uv->flags & UVF_WAVELENGTH ? uv_getskyfreq(uv,uv->win) : 1.0); *preamble++ = scale * ( uv->pluu * uu + uv->pluv * vv ); *preamble++ = scale * ( uv->plvu * uu + uv->plvv * vv ); if(uv->flags & UVF_DOW ) *preamble++ = scale * ww; } else if(v->type == H_DBLE){ *preamble++ = *(double *)(v->buf); } else if(v->type == H_REAL){ *preamble++ = *(float *)(v->buf); } else if(v->type == H_INT){ *preamble++ = *(int *)(v->buf); } } } /************************************************************************/ void uvwread_c(int tno,float *data,int *flags,int n,int *nread) /**uvwread -- Read in the wideband uv correlation data. */ /*&rjs */ /*:uv-i/o */ /*+ FORTRAN call sequence: subroutine uvwread(tno,data,flags,n,nread) integer tno,n,nread complex data(n) logical flags(n) This reads a single wideband visibility record from the data file. This should generally be called after uvread. It performs no scanning before returning the data. Thus it always returns any wideband data (even if uvread has detected end-of-file). Although uvwread is independent of the linetype set with uvset, it otherwise generally performs the same massaging steps as uvread (e.g. data selection, amplitude flagging and planet scaling). Input: tno Handle of the uv data set. n Max number of channels that can be read. Output: data A array of at least n complex elements (or 2n real elements). This returns the correlation data. flags Logical array of at least n elements. A true value for a channel indicates good data. nread Number of correlations returned. */ /*-- */ /*----------------------------------------------------------------------*/ { UV *uv; VARIABLE *v; LINE_INFO line,dummy; uv = uvs[tno]; /* Determine the number of channels in the output, if none where given. */ if(uv->wcorr == NULL){ if(uv_locvar(tno,"wcorr") != NULL){ uv->wcorr = uv_checkvar(tno,"wcorr",H_CMPLX); }else{ *nread = 0; return; } } if(uv->wcorr_flags.handle == NULL && uv->wcorr_flags.exists){ uv->wcorr_flags.handle = mkopen_c(uv->tno,"wflags","old"); uv->wcorr_flags.exists = uv->wcorr_flags.handle != NULL; if(!uv->wcorr_flags.exists) BUG('w',"No flags found for wcorr -- assuming data are good"); } v = uv->wcorr; line.n = NUMCHAN(v); line.linetype = LINE_WIDE; line.width = line.step = 1; line.start = 0; if(line.n > n) BUG('f',"Callers buffer too small for wide data, in UVWREAD"); /* Apply linetype processing and planet scaling. */ *nread = uvread_line(uv,&line,data,n,flags,&dummy); if(*nread == 0)return; /* Apply reference linetype, if there is one. */ if(uv->ref_line.linetype != LINE_NONE) uvread_reference(uv,data,flags,*nread); } /************************************************************************/ private void uvread_reference(UV *uv, float *data, int *flags,int n) /* Divide the data by the reference line. If the reference is bad, then mark all the data as bad. ------------------------------------------------------------------------*/ { float refline[2],t,rp,im; int refflag,nread; int i; LINE_INFO dummy; nread = uvread_line(uv,&(uv->ref_line),refline,1,&refflag,&dummy); if(nread <= 0 || refflag == FORT_FALSE){ for(i = 0; i < n; i++)*flags++ = FORT_FALSE; } else { t = 1.0/(refline[0]*refline[0] + refline[1]*refline[1]); rp = t * refline[0]; im = -t * refline[1]; for(i = 0; i < n; i++){ t = *data * im + *(data+1) * rp; *data = *data * rp - *(data+1) * im; data++; *data++ = t; } } } /************************************************************************/ private double uv_getskyfreq(UV *uv,WINDOW *win) /* This computes the sky frequency for a particular something. ------------------------------------------------------------------------*/ { int i,i0,*nschan,start; float vobs; double *sdf,*sfreq,restfreq; double temp; /* Check the validity of any window specification. */ if(win->first != 0){ if(win->first >= VARLEN(uv->nschan)) BUG('f',"Invalid window selection, in UVREAD(skyfreq)"); } if(uv->data_line.linetype == LINE_VELOCITY){ start = win->first; if(uv->data_line.n == 0 || uv->data_line.fwidth == 0) uvread_defvelline(uv,&(uv->data_line),win); } else if(uv->data_line.linetype == LINE_FELOCITY){ start = win->first; uvread_defvelline(uv,&(uv->data_line),win); } else { start = uv->data_line.start; if( win->first != 0 && uv->data_line.linetype == LINE_CHANNEL){ nschan = (int *)uv->nschan->buf; for(i=0; i < win->first; i++)start += *nschan++; } } if(! (uv->flags & UVF_UPDATED_SKYFREQ) && start == uv->skyfreq_start) return(uv->skyfreq); /* We have to recompute. First indicate that we have doe that already */ uv->skyfreq_start = start; uv->flags &= ~UVF_UPDATED_SKYFREQ; /* CHANNEL linetype. */ if(uv->data_line.linetype == LINE_CHANNEL){ nschan = (int *)uv->nschan->buf; sfreq = (double *)uv->sfreq->buf; sdf = (double *)uv->sdf->buf; temp = 0; while(start >= *nschan){ start -= *nschan++; sfreq++; sdf++; } for(i=0; idata_line.width; i++){ if(start == *nschan){ start = 0; sfreq++; sdf++; nschan++; } temp += *sfreq + start * *sdf; start++; } uv->skyfreq = temp / uv->data_line.width; /* VELOCITY linetype. */ } else if(uv->data_line.linetype == LINE_VELOCITY){ restfreq = *((double *)uv->restfreq->buf + start); vobs = *(float *)uv->veldop->buf - *(float *)uv->vsource->buf; uv->skyfreq = restfreq*(1 - uv->data_line.fstart/CKMS)/(1 + vobs/CKMS); /* WIDE channels. */ } else if(uv->data_line.linetype == LINE_WIDE){ temp = 0; for(i=0, i0 = start; idata_line.width; i++, i0++){ temp += *((float *)uv->wfreq->buf + i0); } uv->skyfreq = temp / uv->data_line.width; } return(uv->skyfreq); } /************************************************************************/ private void uvread_updated_planet(UV *uv) /* This determines the planet rotation and scaling factors. ------------------------------------------------------------------------*/ { float plmaj,plmin,plangle; double theta; /* Determine planet rotation and scaling factor. */ if(uv->ref_plmaj * uv->ref_plmin <= 0){ uv->ref_plmaj = *(float *)uv->plmaj->buf; uv->ref_plmin = *(float *)uv->plmin->buf; uv->ref_plangle = *(float *)uv->plangle->buf; } else { plmaj = *(float *)uv->plmaj->buf; plmin = *(float *)uv->plmin->buf; plangle = *(float *)uv->plangle->buf; if(plmaj > 0.0 && plmin > 0.0){ uv->plscale = (uv->ref_plmaj * uv->ref_plmaj) / (plmaj * plmaj ) ; theta = PI/180 * (plangle - uv->ref_plangle); uv->pluu = cos(theta) * (plmaj / uv->ref_plmaj); uv->pluv = -sin(theta) * (plmaj / uv->ref_plmaj); uv->plvu = -uv->pluv; uv->plvv = uv->pluu; } else { uv->plscale = 1; uv->pluu = uv->plvv = 1; uv->plvu = uv->pluv = 0; } } uv->flags &= ~UVF_UPDATED_PLANET; } /************************************************************************/ /* return 1 if record not selected, 0 if selected for output */ private int uvread_select(UV *uv) { int i,i1,i2,bl,pol,n,nants,inc,selectit,selprev,discard,binlo,binhi,on; float *point,pointerr,dra,ddec,seeing; double time,t0,uu,vv,uv2,uv2f,ra,dec,skyfreq,diameter; double *dazim, *delev; double lst,ha; double *elev; SELECT *sel; OPERS *op; WINDOW *win; selprev = TRUE; for(sel = uv->select; sel != NULL; sel = sel->fwd){ if((!selprev && sel->and) || (selprev && !sel->and)) continue; discard = FALSE; n = 0; op = sel->opers; /* Apply antennae/baseline selection. */ if(sel->selants){ bl = *((float *)(uv->bl->buf)) + 0.5; uvbasant_c(bl,&i1,&i2); if(i1 < 1 || i2 > MAXANT){ ERROR('w',(message,"Discarded data with bad antenna numbers when selecting: (%d,%d) baseline number is %d\n",i1,i2,bl)); discard = TRUE; }else{ discard = sel->ants[(i2*(i2-1))/2+i1-1]; } if(discard) goto endloop; } if( n >= sel->noper ) goto endloop; /* NOTE: The following tests must be in increasing size of the SEL_?? parameters, because the list of selections has been sorted. Note that the SEL_?? parameters are numbered in roughly increasing order of the difficulty in computing them. */ /* Apply visibility number selection. */ if(op->type == SEL_VIS){ discard = !op->discard; while(n < sel->noper && op->type == SEL_VIS){ if(op->loval <= uv->callno && uv->callno <= op->hival) discard = op->discard; op++; n++; } if(discard || n >= sel->noper) goto endloop; } /* Apply time selection. */ if(op->type == SEL_TIME){ time = *((double *)(uv->time->buf)); i1 = time - 0.5; t0 = time - i1 - 0.5; discard = !op->discard; while(n < sel->noper && op->type == SEL_TIME){ if( (op->loval <= time && time <= op->hival) || (op->loval <= t0 && t0 <= op->hival)) discard = op->discard; op++; n++; } if(discard || n >= sel->noper) goto endloop; } /* Apply UV range selection, coordinates given in wavelengths. */ if(op->type == SEL_UVN){ uu = *((double *)(uv->coord->buf)); vv = *((double *)(uv->coord->buf) + 1); uv2 = uu*uu + vv*vv; discard = !op->discard; while(n < sel->noper && op->type == SEL_UVN){ if(op->loval <= uv2 && uv2 <= op->hival) discard = op->discard; op++; n++; } if(discard || n >= sel->noper) goto endloop; } /* Apply pointing selection. */ if(op->type == SEL_POINT){ bl = *((float *)(uv->bl->buf)) + 0.5; uvbasant_c(bl,&i1,&i2); discard = !op->discard; point = (float *)(uv->axisrms->buf); nants = VARLEN(uv->axisrms)/2; if(i1 < 1 || i2 > nants){ BUG('f',"Bad antenna numbers when checking pointing, in UVREAD(select)"); } pointerr = max( *(point+2*i1-2),*(point+2*i1-1)); pointerr = max( *(point+2*i2-2), pointerr); pointerr = max( *(point+2*i2-1), pointerr); while(n < sel->noper && op->type == SEL_POINT){ if(op->loval <= pointerr && pointerr <= op->hival) discard = op->discard; op++; n++; } if(discard || n >= sel->noper) goto endloop; } /* Apply seeing monitor selection. */ if(op->type == SEL_SEEING){ discard = !op->discard; if(!uv->need_seeing) seeing = 0; else seeing = *(float *)(uv->seeing->buf); while(n < sel->noper && op->type == SEL_SEEING){ if(op->loval <= seeing && seeing <= op->hival) discard = op->discard; op++; n++; } if(discard || n >= sel->noper) goto endloop; } /* ==PJT== TODO: bleh, this could be in the wrong order .... */ /* Apply delta RA selection. */ if(op->type == SEL_DRA){ discard = !op->discard; if(!uv->need_dra) dra = 0; else dra = *(float *)uv->dra->buf; while(n < sel->noper && op->type == SEL_DRA){ if(op->loval <= dra && dra <= op->hival) discard = op->discard; op++; n++; } if(discard || n >= sel->noper) goto endloop; } /* Apply delta dec selection. */ if(op->type == SEL_DDEC){ discard = !op->discard; if(!uv->need_ddec) ddec = 0; else ddec = *(float *)uv->ddec->buf; while(n < sel->noper && op->type == SEL_DDEC){ if(op->loval <= ddec && ddec <= op->hival) discard = op->discard; op++; n++; } if(discard || n >= sel->noper) goto endloop; } /* Apply visibility number increment selection. */ if(op->type == SEL_INC){ discard = !op->discard; while(n < sel->noper && op->type == SEL_INC){ inc = op->loval + 0.5; if( (uv->callno - 1) % inc == 0) discard = op->discard; op++; n++; } if(discard || n >= sel->noper) goto endloop; } /* Apply RA selection. */ if(op->type == SEL_RA){ discard = !op->discard; if(uv->ra->type == H_REAL)ra = *(float *)(uv->ra->buf); else ra = *(double *)(uv->ra->buf); if(uv->need_dra){ if(uv->dec->type == H_REAL)dec = *(float *)(uv->dec->buf); else dec = *(double *)(uv->dec->buf); if(uv->need_ddec)dec += *(float *)(uv->ddec->buf); ra += *(float *)(uv->dra->buf) / cos(dec); } while(n < sel->noper && op->type == SEL_RA){ if(op->loval <= ra && ra <= op->hival) discard = op->discard; op++; n++; } if(discard || n >= sel->noper) goto endloop; } /* Apply DEC selection. */ if(op->type == SEL_DEC){ if(uv->dec->type == H_REAL)dec = *(float *)(uv->dec->buf); else dec = *(double *)(uv->dec->buf); if(uv->need_ddec)dec += *(float *)(uv->ddec->buf); discard = !op->discard; while(n < sel->noper && op->type == SEL_DEC){ if(op->loval <= dec && dec <= op->hival) discard = op->discard; op++; n++; } if(discard || n >= sel->noper) goto endloop; } /* Apply polarization selection. */ if(op->type == SEL_POL){ discard = !op->discard; if(uv->need_pol) pol = *(int *)(uv->pol->buf); else pol = 1; while(n < sel->noper && op->type == SEL_POL){ if(op->loval == pol) discard = op->discard; op++; n++; } if(discard || n >= sel->noper) goto endloop; } /* Apply selection based on the "on" parameter. */ if(op->type == SEL_ON){ discard = !op->discard; on = *(int *)(uv->on->buf); while(n < sel->noper && op->type == SEL_ON){ if(op->loval == on ) discard = op->discard; op++; n++; } if(discard || n >= sel->noper) goto endloop; } /* Apply source name selection. */ if(op->type == SEL_SRC){ discard = !op->discard; while(n < sel->noper && op->type == SEL_SRC){ if(uvread_match(op->stval,uv->source->buf,uv->source->length)) discard = op->discard; op++; n++; } if(discard || n >= sel->noper) goto endloop; } /* Apply UV range selection, where u-v is in wavelengths. */ if(op->type == SEL_UV){ discard = !op->discard; win = (sel->win.select ? &(sel->win) : uv->win); skyfreq = uv_getskyfreq(uv,win); uu = *((double *)(uv->coord->buf)); vv = *((double *)(uv->coord->buf) + 1); uv2f = (uu*uu + vv*vv) * skyfreq * skyfreq; while(n < sel->noper && op->type == SEL_UV){ if(op->loval <= uv2f && uv2f <= op->hival) discard = op->discard; op++; n++; } if(discard || n >= sel->noper) goto endloop; } /* Apply sky frequency-based selection. */ if(op->type == SEL_FREQ){ discard = !op->discard; win = (sel->win.select ? &(sel->win) : uv->win); skyfreq = uv_getskyfreq(uv,win); while(n < sel->noper && op->type == SEL_FREQ){ if(op->loval <= skyfreq && skyfreq <= op->hival) discard = op->discard; op++; n++; } if(discard || n >= sel->noper) goto endloop; } /* Apply shadowing selection. */ if(op->type == SEL_SHADOW){ discard = !op->discard; while(n < sel->noper && op->type == SEL_SHADOW){ diameter = op->hival; if(diameter <= 0 && uv->antdiam != NULL) diameter = *(float *)(uv->antdiam->buf); if(diameter <= 0) BUG('f',"No antenna diameter info available, in UVREAD(shadow_select)"); if(uvread_shadowed(uv,diameter)) discard = op->discard; op++; n++; } if(discard || n >= sel->noper) goto endloop; } /* Apply apply pulsar bin selection. */ if(op->type == SEL_BIN){ discard = !op->discard; while(n < sel->noper && op->type == SEL_BIN){ binlo = op->loval + 0.5; binhi = op->hival + 0.5; if(binlo <= *(int *)(uv->bin->buf) && *(int *)(uv->bin->buf) <= binhi ) discard = op->discard; op++; n++; } if(discard || n >= sel->noper) goto endloop; } /* Apply HA selection. */ if(op->type == SEL_HA){ discard = !op->discard; ha = *(double *)uv->lst->buf - *(double *)uv->obsra->buf; /* ha can be -24..24 so needs to be back to -12..12 */ if (ha < -PI) ha += 2*PI; if (ha > PI) ha -= 2*PI; while(n < sel->noper && op->type == SEL_HA){ if(op->loval <= ha && ha <= op->hival) discard = op->discard; op++; n++; } if(discard || n >= sel->noper) goto endloop; } /* Apply LST selection. */ if(op->type == SEL_LST){ discard = !op->discard; lst = *(double *)uv->lst->buf; while(n < sel->noper && op->type == SEL_LST){ if(op->loval < op->hival){ if(op->loval <= lst && lst <= op->hival) discard = op->discard; }else{ if(op->loval <= lst || lst <= op->hival) discard = op->discard; } op++; n++; } if(discard || n >= sel->noper) goto endloop; } /* Apply elevation selection. */ if(op->type == SEL_ELEV){ bl = *((float *)(uv->bl->buf)) + 0.5; uvbasant_c(bl,&i1,&i2); discard = !op->discard; elev = (double *)uv->elev->buf; nants = VARLEN(uv->elev); if(i1 < 1 || i2 > nants){ BUG('f',"Bad antenna numbers when checking elevation, in UVREAD(select)"); } while(n < sel->noper && op->type == SEL_ELEV){ if(op->loval <= elev[i1-1] && elev[i1-1] <= op->hival && op->loval <= elev[i2-1] && elev[i2-1] <= op->hival) discard = op->discard; op++; n++; } if(discard || n >= sel->noper) goto endloop; } /* Apply delta AZIM selection. */ /* @todo: should only consider the current baseline */ if(op->type == SEL_DAZIM){ discard = !op->discard; if(uv->need_dazim) nants = uv->dazim->length / sizeof(double); else nants=0; if(!uv->need_dazim) dazim = 0; else dazim = (double *)uv->dazim->buf; while(n < sel->noper && op->type == SEL_DAZIM){ if (dazim==0) break; for (i=0; iloval <= dazim[i] && dazim[i] <= op->hival) { discard = op->discard; /* printf("ant %d: %g in-ranged %g %g \n",i+1,dazim[i],op->loval,op->hival); */ } } op++; n++; } if(discard || n >= sel->noper) goto endloop; } /* Apply delta ELEV selection. */ /* @todo: should only consider the current baseline */ if(op->type == SEL_DELEV){ discard = !op->discard; if(uv->need_delev) nants = uv->delev->length / sizeof(double); else nants=0; if(!uv->need_delev) delev = 0; else delev = (double *)uv->delev->buf; while(n < sel->noper && op->type == SEL_DELEV){ if (delev==0) break; for (i=0; iloval <= delev[i] && delev[i] <= op->hival) discard = op->discard; } op++; n++; } if(discard || n >= sel->noper) goto endloop; } /* Apply purpose PURP selection. */ if(op->type == SEL_PURP){ discard = !op->discard; while(n < sel->noper && op->type == SEL_PURP){ if(uvread_matchp(op->stval,uv->purpose->buf,uv->purpose->length)) discard = op->discard; op++; n++; } if(discard || n >= sel->noper) goto endloop; } /* We have processed this selection clause. Now determine whether the overall selection criteria is to select or discard. Note that we cannot get here if sel->and == TRUE and selprev == FALSE. */ endloop: selectit = !discard; if(selectit){ if(uv->amp->select && sel->amp.select) BUG('f',"Multiple amplitude selection clauses are active"); if( sel->amp.select ) uv->amp = &(sel->amp); if( uv->win->select && sel->win.select) BUG('f',"Multiple window selection clauses are active"); if( sel->win.select ) uv->win = &(sel->win); } selprev = selectit; } /* for(sel) */ /* Check the validity of the window selection. */ if(selprev && uv->win->first != 0){ if(uv->win->first >= VARLEN(uv->nschan)) BUG('f',"Invalid window selection, in UVREAD(select)"); } return !selprev; } /************************************************************************/ private int uvread_match(char *s1,char *s2, int length) /* This matches two (source) names in upper case. The first name may contain wildcards (just asterisks, not the full blown UNIX regex). The second string is not zero terminated. Used by select=source() Input: s1 The first string. Can contain wildcards. Zero terminated. s2 The second string. No wildcards. Not zero terminated. length Length of the second string. Output: uvread_match True (1) if the two strings match. ------------------------------------------------------------------------*/ { while(*s1 && length > 0){ if(*s1 == '*'){ s1++; if(*s1 == 0)return 1; while(length > 0){ if(uvread_match(s1,s2,length)) return 1; s2++; length--; } return 0; } else { /* here we match ignoring case, before april 2006 * we didn't do toupper() and ignored case */ if(toupper(*s1++) != toupper(*s2++)) return 0; length--; } } /* in order to match s=NAME* with s2=NAME we need to do one more test */ if (*s1 == '*' && *(s1+1) == 0 && length == 0) return 1; return *s1 == 0 && length == 0; } private int uvread_matchp(char *s1,char *s2, int len2) /* This matches two purposes in upper case. No asterisks allowed. The second string is not zero terminated. Used by select=purpose() The first string should contain only 1 letter Input: s1 The first string. No wildcards. Zero terminated. s2 The second string. No wildcards. Not zero terminated. len2 Length of the second string. Output: uvread_matchp True (1) if the two strings match. ------------------------------------------------------------------------*/ { char *s; /* could do a strpbrk on 'BFGPRSO', the current CARMA allowed ones */ /* i.e. if strpbrk(s1,"BFGPRSO") is NULL, BUG out ; but skip for now */ while(len2 > 0) { for (s=s1; *s; s++) /* loop over s1 */ if(toupper(*s) == toupper(*s2)) return 1; /* match */ s2++; len2--; } return 0; /* no match */ } /************************************************************************/ int uvchkshadow_c (int tno, double diameter_meters) /**uvchkshadow -- Check if the record comes from shadowed antennas */ /*&pkgw */ /*:uv-i/o */ /*+ FORTRAN call sequence: logical function uvchkshadow(tno,diameter_meters) integer tno double precision diameter_meters Returns whether the most recently-read UV record comes from a baseline involving shadowed antennas. The antenna diameter used in the shadow computation is passed in as an argument. The test performed in this function is identical to the one performed when using the "shadow()" selection keyword. There is no way to obtain the results of the keyword test, however, without filtering out data records. This function makes it possible to check whether a given record was shadowed without filtering records. In order for this function to operate, you must apply a selection of the form "shadow(1e9)" when reading the data. This is because UVW recomputation must be performed as the data are read in, which is only reliably triggered by applying a "shadow()" selection. The extremely large argument ensures that no data are filtered out by the selection. Invoking this function without applying the necessary selection will result in a fatal bug being signaled. Another routine, SHADOWED, is provided with MIRIAD and provides similar functionality. It is unclear, however, whether SHADOWED is correct. It and the "shadow()" select keyword do their work differently and yield different results. As of 2011 Feb 17, no tasks in MIRIAD use SHADOWED. Input: tno Handle of the uv data file. diameter_meters The assumed antenna diameter in meters. Output: uvchkshadow Whether the last-read baseline was shadowed. */ /*-- */ /*----------------------------------------------------------------------*/ { UV *uv = uvs[tno]; if (!(uv->need_uvw)) BUG('f', "Cannot check shadowing without setting up UVW recomputation" " (try adding a shadow() selection)"); return uvread_shadowed (uv, diameter_meters); } /************************************************************************/ private int uvread_shadowed(UV *uv,double diameter) /* This determines if a particular baseline is shadowed. Inputs: uv The uv data structure. diameter The dish diameter, in meters. ------------------------------------------------------------------------*/ { int nants,i,j,i1,i2,i0,bl; double u,v,w,limit; UVW *uvw; /* Get the table of U,V,W coordinates. */ if(uv->flags & UVF_UPDATED_UVW) uvread_updated_uvw(uv); uvw = uv->uvw; /* Convert the diameter to nanosec, and square it. */ limit = diameter / CKMS * 1e6; limit *= limit; /* Set the number of antennae as the number of positions that we have. */ nants = uv->uvw->nants; bl = *((float *)(uv->bl->buf)) + 0.5; uvbasant_c(bl,&i1,&i2); i1--;i2--; if(i1 < 0 || i2 >= nants){ BUG('f',"Bad antenna numbers when checking shadowing, in UVREAD(select)"); } for(j=0; j < 2; j++){ i0 = ( j == 0 ? i1 : i2); if(i1 == i2 && j == 1)return(0); for(i=0; i < nants; i++){ if(i != i0){ u = uvw->uu[i] - uvw->uu[i0]; v = uvw->vv[i] - uvw->vv[i0]; w = uvw->ww[i] - uvw->ww[i0]; if(u*u + v*v <= limit && w >= 0) return(1); } } } return(0); } /************************************************************************/ private void uvread_updated_uvw(UV *uv) /* Update the table of vectors used to computer u,v,w. ------------------------------------------------------------------------*/ { UVW *uvw; double ha,dec,sinha,cosha,sind,cosd; double *posx,*posy,*posz,bx,by,bz,bxy,byx; int i; if(uv->uvw == NULL)uv->uvw = (UVW *)Malloc(sizeof(UVW)); uvw = uv->uvw; uvw->nants = VARLEN(uv->antpos)/3; if(uvw->nants > MAXANT ) bug_c('f',"Too many antennas in uvread_updated_uvw"); /* Get trig functions of the hour angle and the declination. */ ha = *(double *)(uv->lst->buf) - *(double *)(uv->obsra->buf); dec = *(double *)(uv->obsdec->buf); sinha = sin(ha); cosha = cos(ha); sind = sin(dec); cosd = cos(dec); posx = (double *)(uv->antpos->buf); posy = posx + uvw->nants; posz = posy + uvw->nants; for(i=0; i < uvw->nants; i++){ bx = *posx++; by = *posy++; bz = *posz++; bxy = bx*sinha + by*cosha; byx = -bx*cosha + by*sinha; uvw->uu[i] = bxy; uvw->vv[i] = byx*sind + bz*cosd; uvw->ww[i] = -byx*cosd + bz*sind; } /* Remember that the table has been updated. */ uv->flags &= ~UVF_UPDATED_UVW; } /************************************************************************/ private void uvread_defline(int tno) /* Initialise everything, ready to start reading. In particular, this determines what variables are needed, makes sure they are there, and makes sure they are being tracked. Inputs: tno The handle of the file of interest. ------------------------------------------------------------------------*/ { UV *uv; uv = uvs[tno]; /* If no line has been specified, return the default type -- i.e. all the channels. */ uv->corr = uv_locvar(tno,"corr"); uv->wcorr = uv_locvar(tno,"wcorr"); if(uv->data_line.linetype == LINE_NONE){ if(uv->corr != NULL) uv->data_line.linetype = LINE_CHANNEL; else if(uv->wcorr != NULL) uv->data_line.linetype = LINE_WIDE; else BUG('f',"UV file contains neither corr nor wcorr, in UVREAD(defline)"); uv->data_line.start = 0; uv->data_line.width = 1; uv->data_line.step = 1; uv->data_line.n = 0; } } /************************************************************************/ private void uvread_init(int tno) /* Initialise everything, ready to start reading. In particular, this determines what variables are needed, makes sure they are there, and makes sure they are being tracked. Inputs: tno The handle of the file of interest. ------------------------------------------------------------------------*/ { UV *uv; SELECT *sel; int n_win,n_or; uv = uvs[tno]; uv->flags |= UVF_INIT; /* Open the flagging file, if it is needed. */ if(uv->data_line.linetype == LINE_CHANNEL || uv->data_line.linetype == LINE_VELOCITY || uv->data_line.linetype == LINE_FELOCITY || uv->ref_line.linetype == LINE_CHANNEL || uv->ref_line.linetype == LINE_VELOCITY || uv->ref_line.linetype == LINE_FELOCITY ){ if(uv->corr_flags.handle == NULL && uv->corr_flags.exists){ uv->corr_flags.handle = mkopen_c(uv->tno,"flags","old"); uv->corr_flags.exists = uv->corr_flags.handle != NULL; if(!uv->corr_flags.exists) BUG('w',"No flags found for corr -- assuming data are good"); } } if(uv->data_line.linetype == LINE_WIDE || uv->ref_line.linetype == LINE_WIDE ){ if(uv->wcorr_flags.handle == NULL && uv->wcorr_flags.exists){ uv->wcorr_flags.handle = mkopen_c(uv->tno,"wflags","old"); uv->wcorr_flags.exists = uv->wcorr_flags.handle != NULL; if(!uv->wcorr_flags.exists) BUG('w',"No flags found for wcorr -- assuming data are good"); } } /* Make sure we have the info to get the preamble. */ uv->coord = uv_checkvar(tno,"coord",H_DBLE); if( VARLEN(uv->coord) < ( uv->flags & UVF_DOW ? 3 : 2 ) ){ if(uv_locvar(tno,"obsra") != NULL && uv_locvar(tno,"obsdec") != NULL && uv_locvar(tno,"lst") != NULL && uv_locvar(tno,"antpos") != NULL){ uv->flags |= UVF_REDO_UVW; uv->need_uvw = TRUE; } else { BUG('w',"Unable to compute w coordinate -- setting this to zero"); } } uv->time = uv_checkvar(tno,"time",H_DBLE); uv->bl = uv_checkvar(tno,"baseline",H_REAL); /* Set up the default preamble if one has not already been set. */ if(uv->presize == 0){ uv->presize = 3; uv->prevar[0] = uv->coord; uv->prevar[1] = uv->time; uv->prevar[2] = uv->bl; } /* Get info to decode correlation data. */ if( uv->data_line.linetype == LINE_CHANNEL || uv->data_line.linetype == LINE_VELOCITY || uv->data_line.linetype == LINE_FELOCITY || uv->ref_line.linetype == LINE_CHANNEL || uv->ref_line.linetype == LINE_VELOCITY || uv->ref_line.linetype == LINE_FELOCITY ){ if(uv->corr == NULL) BUG('f',"Corr data missing, when channel linetype requested"); if(uv->corr->type == H_INT2){ uv->tscale = uv_checkvar(tno,"tscale",H_REAL); } else if(uv->corr->type != H_REAL){ BUG('f',"Bad data type for variable corr, in UVREAD."); } // was there not a H_COMPLEX ?? } /* Get variables needed for selection. */ if(uv->need_point) uv->axisrms = uv_checkvar(tno,"axisrms",H_REAL); /* uv_checkvar can only handle existing variables */ #if 1 if(uv->need_seeing)uv->seeing = uv_checkvar(tno,"rmspath",H_REAL); /* CARMA */ #else if(uv->need_seeing)uv->seeing = uv_checkvar(tno,"smonrms",H_REAL); /* ATCA */ #endif if(uv->need_pol) uv->need_pol= uv_locvar(tno,"pol") != NULL; if(uv->need_pol) uv->pol = uv_checkvar(tno,"pol",H_INT); if(uv->need_on) uv->on = uv_checkvar(tno,"on",H_INT); if(uv->need_purp) uv->purpose = uv_checkvar(tno,"purpose",H_BYTE); if(uv->need_src) uv->source = uv_checkvar(tno,"source",H_BYTE); if(uv->need_bin) uv->bin = uv_checkvar(tno,"bin",H_INT); if(uv->need_lst) uv->lst = uv_checkvar(tno,"lst",H_DBLE); if(uv->need_obsra) uv->obsra = uv_checkvar(tno,"obsra",H_DBLE); if(uv->need_elev) uv->elev = uv_checkvar(tno,"antel",H_DBLE); if(uv->need_uvw){ uv->obsra = uv_checkvar(tno,"obsra",H_DBLE); uv->obsdec = uv_checkvar(tno,"obsdec",H_DBLE); uv->lst = uv_checkvar(tno,"lst",H_DBLE); uv->antpos = uv_checkvar(tno,"antpos",H_DBLE); uv->obsra->flags |= UVF_UPDATED_UVW; uv->obsdec->flags |= UVF_UPDATED_UVW; uv->lst->flags |= UVF_UPDATED_UVW; uv->antpos->flags |= UVF_UPDATED_UVW; uv->flags |= UVF_UPDATED_UVW; if( ( uv->antdiam = uv_locvar(tno,"antdiam") ) != NULL) uv->antdiam = uv_checkvar(tno,"antdiam",H_REAL); } /* Get extra info needed for decoding frequencies and velocities of "corr" data. */ if( uv->data_line.linetype == LINE_VELOCITY || uv->ref_line.linetype == LINE_VELOCITY || uv->data_line.linetype == LINE_FELOCITY || uv->ref_line.linetype == LINE_FELOCITY){ uv->nschan = uv_checkvar(tno,"nschan",H_INT); uv->sfreq = uv_checkvar(tno,"sfreq",H_DBLE); uv->sdf = uv_checkvar(tno,"sdf",H_DBLE); uv->restfreq = uv_checkvar(tno,"restfreq",H_DBLE); uv->veldop = uv_checkvar(tno,"veldop",H_REAL); uv->vsource = uv_checkvar(tno,"vsource",H_REAL); } /* Get info for decoding wide band stuff. */ if( uv->data_line.linetype == LINE_WIDE || uv->ref_line.linetype == LINE_WIDE ){ if(uv->wcorr == NULL) BUG('f',"Wcorr missing, when wide linetype was requested"); } /* Variables to determine the mapping from windows to channels. */ if(uv->need_win) uv->nschan = uv_checkvar(tno,"nschan",H_INT); /* Variables needed to determine the sky frequency. */ if(uv->need_skyfreq){ if(uv->data_line.linetype == LINE_WIDE){ uv->wfreq = uv_checkvar(tno,"wfreq",H_REAL); uv->wfreq->flags |= UVF_UPDATED_SKYFREQ; } else if(uv->data_line.linetype == LINE_CHANNEL){ uv->nschan = uv_checkvar(tno,"nschan",H_INT); uv->sfreq = uv_checkvar(tno,"sfreq",H_DBLE); uv->sdf = uv_checkvar(tno,"sdf",H_DBLE); uv->nschan->flags |= UVF_UPDATED_SKYFREQ; uv->sfreq->flags |= UVF_UPDATED_SKYFREQ; uv->sdf->flags |= UVF_UPDATED_SKYFREQ; } else if(uv->data_line.linetype == LINE_VELOCITY || uv->data_line.linetype == LINE_FELOCITY ){ uv->veldop->flags |= UVF_UPDATED_SKYFREQ; uv->restfreq->flags |= UVF_UPDATED_SKYFREQ; uv->vsource->flags |= UVF_UPDATED_SKYFREQ; } uv->flags |= UVF_UPDATED_SKYFREQ; } /* RA,Dec, and delta ra and dec, possibly needed by the selection routines. We can do without dra and ddec (we assume they are zero if they are missing), but we cannot do without ra and dec if they are needed. */ if(uv->need_ra) { uv->ra = uv_checkvar(tno,"ra",0); if(uv->ra->type != H_REAL && uv->ra->type != H_DBLE) BUG('f',"Variable ra has the wrong type, in UVREAD(ini)"); uv->need_dra = uv_locvar(tno,"dra") != NULL; if(uv->need_dra) uv->need_dec = TRUE; } if(uv->need_dec) { uv->dec = uv_checkvar(tno,"dec",0); if(uv->dec->type != H_REAL && uv->dec->type != H_DBLE) BUG('f',"Variable dec has the wrong type, in UVREAD(ini)"); uv->need_ddec = TRUE; } if(uv->need_dra) uv->need_dra = uv_locvar(tno,"dra") != NULL; if(uv->need_dra) uv->dra = uv_checkvar(tno,"dra",H_REAL); if(uv->need_ddec) uv->need_ddec = uv_locvar(tno,"ddec") != NULL; if(uv->need_ddec) uv->ddec = uv_checkvar(tno,"ddec",H_REAL); if(uv->need_dazim) uv->need_dazim = uv_locvar(tno,"dazim") != NULL; if(uv->need_dazim) uv->dazim = uv_checkvar(tno,"dazim",H_DBLE); if(uv->need_delev) uv->need_delev = uv_locvar(tno,"delev") != NULL; if(uv->need_delev) uv->delev = uv_checkvar(tno,"delev",H_DBLE); /* Get info for performing planet corrections. If the data are missing, do not perform planet corrections. */ if(uv->need_planet && uv_locvar(tno,"plmaj") != NULL){ uv->plmaj = uv_checkvar(tno,"plmaj",H_REAL); uv->plmaj->flags |= UVF_UPDATED_PLANET; uv->plmin = uv_checkvar(tno,"plmin",H_REAL); uv->plmin->flags |= UVF_UPDATED_PLANET; uv->plangle = uv_checkvar(tno,"plangle",H_REAL); uv->plangle->flags |= UVF_UPDATED_PLANET; uv->flags |= UVF_UPDATED_PLANET; } else uv->need_planet = FALSE; /* If line=channel and select=window, make sure our restrictions are met. */ if(uv->data_line.linetype == LINE_CHANNEL && uv->apply_win){ n_win = n_or = 0; for(sel = uv->select; sel != NULL; sel = sel->fwd){ if(!sel->and) n_or++; if(sel->win.select){ n_win++; if(sel->win.last - sel->win.first >= sel->win.n) BUG('f',"Unsupported window selection clause, in UVREAD(init)"); } } if((n_or > 0 && n_win > 0) || (n_win > 1) ) BUG('f',"Unsupported window selection clause, in UVREAD(init)"); } /* Reset the variance calibration, if needed. NOTE: THIS DOES NOT RELEASE THE variable handle -- which will not be released before the file it descroyed. This result in a mild memory leak of sorts. */ if(uv->sigma2.table != NULL){ free((char *)(uv->sigma2.table)); uv->sigma2.table = NULL; uv->sigma2.nants = 0; } /* Determine the max visibility that the user is interested in. */ if(uv->select != NULL) uv->maxvis = uvread_maxvis(uv->select); } /************************************************************************/ private int uvread_maxvis(SELECT *sel) /* Determine the maximum visibility number that the caller wants. If this cannot be determined, return 0. ------------------------------------------------------------------------*/ { OPERS *op; int temp,maxvis,ilo,ihi,n; maxvis = 0; while(sel != NULL){ temp = 0; for(op = sel->opers,n = 0; n < sel->noper; n++, op++){ if(op->type == SEL_VIS){ ihi = op->hival + 0.5; ilo = op->loval + 0.5; if(op->discard && temp == 0) return(0); else if(op->discard && ihi >= temp) temp = min(temp, ilo); else if(!op->discard) temp = max(temp, ihi); } } if(temp <= 0) return(0); maxvis = max(maxvis, temp); sel = sel->fwd; } return(maxvis); } /************************************************************************/ private VARIABLE *uv_checkvar(int tno,char *varname,int type) /* Make sure a particular variable is present, and make sure we track it. Return the pointer to this variable. Input: tno Handle of the uv data file. varname The name of the variable we are interested in. type The data type that the variable must be. ------------------------------------------------------------------------*/ { VARIABLE *v; v = uv_locvar(tno,varname); if(v == NULL) ERROR('f',(message, "Variable %s is missing, in UVREAD",varname)); else if(type != 0 && type != v->type)ERROR('f',(message, "Variable %s has the wrong data type, in UVREAD",varname)); else if(v->buf == NULL || v->length <= 0)ERROR('f',(message, "Variable %s was not initialised before it was required, in UVREAD",varname)); return(v); } /************************************************************************/ private int uvread_line(UV *uv,LINE_INFO *line,float *data, int nsize,int *flags,LINE_INFO *actual) /* Determine the desired line. Input: uv The uv structure. line Info about the line we are interested in. nsize Size of the "data" array (in complex elements). Output: data The calculated line. flags Flag info. actual The actual line used. uvread_line The number of values returned. ------------------------------------------------------------------------*/ { int i,j,n,nspect; VARIABLE *v; WINDOW *win; int *di; int rei,imi,nc,start,width,step,*flagin,nchan,*nschan; float scale,ref,imf,*df,*d; FLAGS *flag_info; int ggflag; ggflag = uv->gflag; /* Determine the relevant variable and flagging info, and get the flags. */ if(line->linetype == LINE_WIDE){ v = uv->wcorr; flag_info = &(uv->wcorr_flags); } else { v = uv->corr; flag_info = &(uv->corr_flags); } nchan = NUMCHAN(v); if(! flag_info->init ) uvread_flags(uv,v,flag_info,nchan); /* Handle velocity linetype. */ if(line->linetype == LINE_VELOCITY || line->linetype == LINE_FELOCITY){ uvread_velocity(uv,line,data,flags,nsize,actual); return(line->n); } /* Determine the parameters which describe the line. */ start = line->start; if(line->linetype == LINE_CHANNEL && uv->win->select && uv->apply_win){ win = uv->win; nspect = VARLEN(uv->nschan); if(win->last >= nspect) BUG('f',"Invalid window selection, in UVREAD(channel)"); nschan = (int *)uv->nschan->buf; nchan = 0; for(i=0; i < win->first; i++) nchan += *nschan++; start += nchan; for(i=0; i < win->n; i++) nchan += *nschan++; } width = line->width; step = line->step; n = line->n; if(n <= 0) n = (nchan - start) / step; if(n <= 0 || start < 0 || start + step * (n-1) + width > nchan) { printf("n=%d start=%d step=%d width=%d nchan=%d\n",n,start,step,width,nchan); BUG('f',"Illegal channel range specified, in UVREAD"); } if(n > nsize) BUG('f',"Callers buffer too small for channel data, in UVREAD"); /* Return the actual line used. */ actual->linetype = line->linetype; actual->start = start; actual->width = width; actual->step = step; actual->n = n; /* Miscellaneous initialisation. */ step -= width; scale = uv->plscale; flagin = flag_info->flags + start; d = data; /* Handle the common case of just a straight copy of the correlation data. */ if(width == 1 && ( step == 0 || n == 1)){ if(v->type == H_INT2){ scale *= *(float *)uv->tscale->buf; di = (int *)v->buf + 2*start; for(i=0; i < 2*n; i++) *d++ = scale * *di++; } else { df = (float *)v->buf + 2*start; if(scale != 1)for(i=0; i < 2*n; i++) *d++ = scale * *df++; else memcpy((char *)d,(char *)df,2*sizeof(float)*n); } memcpy((char *)flags,(char *)flagin,sizeof(int)*n); /* Handle the case of averaged, scaled integers. */ } else if(v->type == H_INT2){ di = (int *)(v->buf) + 2*start; scale *= *(float *)uv->tscale->buf; for(i=0; i 0){ *d++ = rei*scale/nc; *d++ = imi*scale/nc; *flags++ = ( nc >= ggflag ? FORT_TRUE : FORT_FALSE); } else { *d++ = 0; *d++ = 0; *flags++ = FORT_FALSE; } di += 2*step; flagin += step; } /* Handle the case of averaged, reals. */ } else { df = (float *)(v->buf) + 2*start; for(i=0; i 0){ *d++ = scale*ref/nc; *d++ = scale*imf/nc; *flags++ = ( nc >= ggflag ? FORT_TRUE : FORT_FALSE); } else { *d++ = 0; *d++ = 0; *flags++ = FORT_FALSE; } df += 2*step; flagin += step; } } return(n); } /************************************************************************/ private void uvread_velocity(UV *uv,LINE_INFO *line,float *data, int *flags,int nsize,LINE_INFO *actual) /* Calculate the velocity line type. Inputs: uv Pointer to the uv data structure. nsize Number of channels to return. line Pointer to the structure defining the line type of interest. Outputs: data The velocity line type. flags Flags indicating whether the data is good or not. ------------------------------------------------------------------------*/ { float idv,idv2,odv2,dv2,scale,wt,v,vobs,temp; double *sfreq,*sdf,*restfreq; int nspect,first,last,fout,lout,i,j,n; int *nschan,*flagin,*flagin1,*flagout,*wins,doint2; float *wts,*dataout; int *di,*di1; float *df,*df1; /* Set the default line if needed. */ if(line->n == 0 || line->fstep == 0 || line->linetype == LINE_FELOCITY) uvread_defvelline(uv,line,uv->win); /* A few simple checks. */ if(line->n <= 0) BUG('f',"Bad number of channels, in UVREAD(velocity)"); if(nsize < line->n) BUG('f',"Callers buffer too small for velocity data, in UVREAD(velocity)"); if(uv->corr->type != H_INT2 && uv->corr->type != H_REAL) BUG('f',"Bad data type of corr data, in UVREAD(velocity)."); doint2 = uv->corr->type == H_INT2; if(line->wts == NULL) line->wts = (float *)Malloc(sizeof(float)*nsize); /* Return the actual line used. */ actual->linetype = line->linetype; actual->n = line->n; actual->fstart = line->fstart; actual->fwidth = line->fwidth; actual->fstep = line->fstep; /* Set the weights and data arrays to zero. */ nsize = line->n; wins = uv->win->wins; wts = line->wts; dataout = data; for(i=0; inschan); if(nspect > MAXWIN) BUG('f',"Too many windows, in UVREAD(velocity)"); temp = line->fstep; if(temp < 0) temp = -temp; odv2 = 0.5 * line->fwidth/temp; sfreq = (double *)uv->sfreq->buf; sdf = (double *)uv->sdf->buf; restfreq = (double *)uv->restfreq->buf; vobs = *(float *)uv->veldop->buf - *(float *)uv->vsource->buf; nschan = (int *)uv->nschan->buf; scale = uv->plscale; if(doint2)scale *= *(float *)uv->tscale->buf; wts = line->wts; if(doint2)di1 = (int *)uv->corr->buf; else df1 = (float *)uv->corr->buf; dataout = data; flagin1 = uv->corr_flags.flags; /* Now compute the velocity channels. The first loop moves over the windows, determining which channels, in this window, contribute. The second loop moves over these channels. The third loop moves over the output velocity channels, accumulating the contribution of a particular input channel. */ for(n=0; n < nspect; n++){ if(*wins++){ v = (CKMS * (1 - *sfreq*(1+vobs/CKMS) / *restfreq) - line->fstart ) / line->fstep; idv = -CKMS * *sdf / (*restfreq * line->fstep); idv2 = 0.5 * idv; if(idv2 < 0) idv2 = - idv2; dv2 = idv2 + odv2; if(idv > 0){ fout = ceil((-dv2-v)/idv); lout = floor((nsize - 1 + dv2 - v)/idv); } else if(idv < 0){ fout = ceil((nsize - 1 + dv2 - v)/idv); lout = floor((-dv2-v)/idv); } else BUG('f',"File velocity increment is zero, in UVREAD(velocity)."); if(fout < 0) fout = 0; if(lout > *nschan-1) lout = *nschan - 1; v += fout * idv; if(doint2)di = di1 + fout + fout; else df = df1 + fout + fout; flagin = flagin1 + fout; for(i=fout; i <= lout; i++){ if(*flagin == FORT_TRUE){ first = max(0, ceil (v-dv2)); last = min(nsize-1,floor(v+dv2)); if(doint2){ for(j=first; j<=last; j++){ wt = ( min(v + idv2, j + odv2) - max(v - idv2, j - odv2) ) / idv2; *(dataout + j+j) += wt * *(di); *(dataout + j+j + 1) += wt * *(di+1); *(wts + j) += wt; } } else { for(j=first; j<=last; j++){ wt = ( min(v + idv2, j + odv2) - max(v - idv2, j - odv2) ) / idv2; *(dataout + j+j) += wt * *(df); *(dataout + j+j + 1) += wt * *(df+1); *(wts + j) += wt; } } } v += idv; flagin++; if(doint2) di += 2; else df += 2; } } if(doint2) di1 += 2 * *nschan; else df1 += 2 * *nschan; flagin1 += *nschan; nschan++; sfreq++; sdf++; restfreq++; } /* Normalise and return. */ flagout = flags; for(i=0; i 0.0){ *dataout++ *= scale / *wts; *dataout++ *= scale / *wts++; *flagout++ = FORT_TRUE; } else { dataout += 2; wts ++; *flagout++ = FORT_FALSE; } } } /************************************************************************/ private void uvread_defvelline(UV* uv,LINE_INFO *line,WINDOW *win) /* Determine a good, default, velocity line. Input: win The window to use. Input/Output: line n,fstart,fwidth,fstep are set if needed. ------------------------------------------------------------------------*/ { double f0,df,rfreq,fac; int n; float vobs; /* Get the frequency, etc, description of the first window. */ if(win->first != 0){ if(win->first >= VARLEN(uv->nschan)) BUG('f',"Invalid window selection, in UVREAD(skyfreq)"); } vobs = *(float *)uv->veldop->buf - *(float *)uv->vsource->buf; f0 = *((double *)uv->sfreq->buf + win->first); df = *((double *)uv->sdf->buf + win->first); n = *((int *)uv->nschan->buf + win->first); rfreq = *((double *)uv->restfreq->buf + win->first); if(rfreq <= 0)BUG('f',"Invalid rest frequency when setting default linetype"); /* Set the defaults. */ if(line->n == 0 || line->fwidth == 0){ line->linetype = LINE_VELOCITY; line->fwidth = -CKMS * df / rfreq; line->fstep = MYABS(line->fwidth); if(line->n == 0) line->n = n; n = (n - line->n) / 2; line->fstart = CKMS * ( 1 - (f0+n*df)*(1+vobs/CKMS)/rfreq ); } /* Translate a felocity linetype into a velocity one, if needed. */ if(line->linetype == LINE_FELOCITY){ line->linetype = LINE_VELOCITY; fac = CKMS / (CKMS + line->fstart); line->fstep *= fac * fac; line->fwidth *= fac * fac; line->fstart = fac * line->fstart; } } /************************************************************************/ private void uvread_flags(UV *uv,VARIABLE *v,FLAGS *flag_info,int nchan) /* Read in flagging information, and apply the amplitude flagging if needed. ------------------------------------------------------------------------*/ { int *di; float *df,amp2,amplo2,amphi2,tscale,ii,rr; int discard,i; int *flags; flag_info->init = TRUE; nchan = NUMCHAN(v); /* Allocate space and read the flags. */ if(flag_info->nflags < nchan){ flag_info->nflags = nchan; flag_info->flags = (int *)Realloc((char *)flag_info->flags, sizeof(int) * flag_info->nflags); } flags = flag_info->flags; if(flag_info->exists) mkread_c(flag_info->handle,MK_FLAGS,flags, flag_info->offset-nchan,nchan,nchan); else for(i=0; i < nchan; i++) *flags++ = FORT_TRUE; /* Return if there is no amplitude flagging to do. */ if( !uv->amp->select || !uv->apply_amp ) return; /* Flag the appropriate channels. */ flags = flag_info->flags; amplo2 = uv->amp->loval / uv->plscale; amplo2 *= amplo2; amphi2 = uv->amp->hival / uv->plscale; amphi2 *= amphi2; discard = uv->amp->discard; /* Case of real data. */ if(v->type == H_REAL || v->type == H_CMPLX){ df = ((float *)(v->buf)); for(i=0; i < nchan; i++){ amp2 = *df * *df + *(df+1) * *(df+1); if(amplo2 <= amp2 && amp2 <= amphi2) *flags = ((*flags == FORT_TRUE && !discard) ? FORT_TRUE : FORT_FALSE); else *flags = ((*flags == FORT_TRUE && discard) ? FORT_TRUE : FORT_FALSE); df += 2; flags++; } /* Case of integer*2 data. */ } else if(v->type == H_INT2){ di = ((int *)(v->buf)); tscale = *((float *)(uv->tscale->buf)); for(i=0; i < nchan; i++){ rr = tscale * *di; ii = tscale * *(di+1); amp2 = rr * rr + ii * ii; if(amplo2 <= amp2 && amp2 <= amphi2) *flags = ((*flags == FORT_TRUE && !discard) ? FORT_TRUE : FORT_FALSE); else *flags = ((*flags == FORT_TRUE && discard) ? FORT_TRUE : FORT_FALSE); di += 2; flags++; } } } /************************************************************************/ void uvflgwr_c(int tno, Const int *flags) /**uvflgwr -- Write uv flags after a read. */ /*&rjs */ /*:uv-i/o */ /*+ FORTRAN call sequence: subroutine uvflgwr(tno,flags) integer tno logical flags(*) This causes the flags associated with correlation data to be rewritten. It is typically used by a flagging program to overwrite old flagging information. It will typically be called soon after uvread (which is used to get the old flags, and position the file), thus overwriting the old flags. Input: tno The handle of the input uv file. flags Logical array of "nread" elements ("nread" as returned by the last call to uvread). */ /*-- */ /*----------------------------------------------------------------------*/ { int nchan,width,step,n,i; off_t offset; UV *uv; VARIABLE *v; FLAGS *flags_info; uv = uvs[tno]; if(uv->actual_line.linetype == LINE_CHANNEL){ v = uv->corr; flags_info = &(uv->corr_flags); } else { v = uv->wcorr; flags_info = &(uv->wcorr_flags); } width = uv->actual_line.width; step = uv->actual_line.step; if(uv->actual_line.linetype == LINE_VELOCITY || flags_info->handle == NULL || width != 1) BUG('f',"Illegal request when trying to write to flagging file, in UVFLGWR"); nchan = NUMCHAN(v); offset = flags_info->offset - nchan + uv->actual_line.start; n = min(uv->actual_line.n,nchan); if(step == 1){ mkwrite_c(flags_info->handle,MK_FLAGS,(int *)flags,offset,n,n); } else { for(i = 0; i < n; i++){ mkwrite_c(flags_info->handle,MK_FLAGS,(int *)flags,offset,1,1); offset += step; flags++; } } } /************************************************************************/ void uvwflgwr_c(int tno,Const int *flags) /**uvwflgwr -- Write uv flags after a read. */ /*&rjs */ /*:uv-i/o */ /*+ FORTRAN call sequence: subroutine uvwflgwr(tno,flags) integer tno logical flags(*) This rewrites the flags associated with the last call to uvwread. It will typically be called soon after uvwread, thus overwriting the old flags. Input: tno The handle of the input uv file. flags Logical array of "nread" elements ("nread" as returned by the last call to uvwread). */ /*-- */ /*----------------------------------------------------------------------*/ { int nchan; off_t offset; UV *uv; VARIABLE *v; FLAGS *flags_info; uv = uvs[tno]; v = uv->wcorr; if(v == NULL) BUG('f',"The wcorr variable has not been initialised, in UVWFLGWR\n"); flags_info = &(uv->wcorr_flags); if(flags_info->handle == NULL) BUG('f',"No flagging file exists, in UVWFLGWR\n"); nchan = NUMCHAN(v); offset = flags_info->offset - nchan; mkwrite_c(flags_info->handle,MK_FLAGS,(int *)flags,offset,nchan,nchan); } /************************************************************************/ void uvinfo_c(int tno,Const char *object,double *data) /**uvinfo -- Get information about the last data read with uvread. */ /*&rjs */ /*:uv-i/o */ /*+ FORTRAN call sequence: subroutine uvinfo(tno,object,data) integer tno character object*(*) double precision data(*) This returns extra information about the data read in the last call to uvread. Input: tno The handle of the uv file. object Indicates what information is required. Currently this can be 'velocity' returns "nread" numbers, giving the velocity (km/s) of each channel. 'restfreq' returns "nread" numbers, giving the rest frequency (GHz) of each channel. 'bandwidth' returns "nread" numbers, giving the bandwidth (GHz) of each channel. 'visno' returns 1 number, which is the number of visibilities read from this file. 'frequency' returns "nread" numbers, giving the rest-frame frequency (GHz) of each channel. 'sfreq' returns "nread" numbers, giving the sky frequency (GHz) of each channel. 'amprange' returns 3 numbers. The first gives the amplitude selection for this record, the next two give the selection range. Possible values of data(1) are -1 : Data outside the range [data(2),data(3)] was rejected. 0 : No amplitude selection. +1 : Data inside the range [data(2),data(3)] was rejected. 'line' returns 6 numbers, giving the linetype. Possible values of data(1) are 1, 2 or 3, corresponding to 'channel', 'wide' and 'velocity'. data(2) thru data(5) are n,start,width,step. data(6) is the first window used. 'variance' returns the variance (based on system temp) of the first channel. If this cannot be determined it returns 0. Output: data The actual information returned. */ /*-- */ /*----------------------------------------------------------------------*/ { #define VELO 1 #define FELO 2 #define RFREQ 3 #define BW 4 #define FREQ 5 #define SFREQ 6 UV *uv; uv = uvs[tno]; /* Return the visibility number. */ if(!strcmp(object,"visno")){ *data = uv->callno; /* Return the variance of the data in the first channel. */ } else if(!strcmp(object,"variance")){ uvinfo_variance(uv,data); /* Return information about the amplitude selection. */ } else if(!strcmp(object,"amprange")){ if( ! uv->amp->select ) *data = 0; else{ *data = (uv->amp->discard ? -1 : 1); *(data+1) = uv->amp->loval; *(data+2) = uv->amp->hival; } /* Return linetype information. */ } else if(!strcmp(object,"line")){ *data = uv->actual_line.linetype; *(data+1) = uv->actual_line.n; if(uv->actual_line.linetype == LINE_VELOCITY){ *(data+2) = uv->actual_line.fstart; *(data+3) = uv->actual_line.fwidth; *(data+4) = uv->actual_line.fstep; *(data+5) = uv->win->first + 1; } else { *(data+2) = uv->actual_line.start + 1; *(data+3) = uv->actual_line.width; *(data+4) = uv->actual_line.step; *(data+5) = 0; } /* Various bits and pieces of channel information. */ } else if(!strcmp(object,"velocity")) uvinfo_chan(uv,data,VELO); else if(!strcmp(object,"felocity")) uvinfo_chan(uv,data,FELO); else if(!strcmp(object,"restfreq")) uvinfo_chan(uv,data,RFREQ); else if(!strcmp(object,"bandwidth")) uvinfo_chan(uv,data,BW); else if(!strcmp(object,"frequency")) uvinfo_chan(uv,data,FREQ); else if(!strcmp(object,"sfreq")) uvinfo_chan(uv,data,SFREQ); else ERROR('f',(message,"Unrecognised object %s, in UVINFO",object)); } /************************************************************************/ private void uvinfo_variance(UV *uv,double *data) /* Determine the variance of the first channel of the last data read with uvread. For raw polarisation parameters, variance = JyperK**2 * T1*T2/(2*Bandwidth*IntTime) where JyperK = 2*k/eta*A where A is the antenna area, eta is an efficiency (both surface efficiency and correlator efficiency), and k is Boltzmans constant. For Stokes parameters, the variance returned is half the above variance, as its assumed that two things have been summed to get the Stokes parameter. ------------------------------------------------------------------------*/ { double *restfreq,*tab; float bw,inttime,jyperk,*syst,*t1,*t2,factor; int i,j,bl,i1,i2,nants,nsyst,*nschan,start; off_t offset; LINE_INFO *line; VARIABLE *tsys; /* Miscellaneous. */ line = &(uv->actual_line); *data = 0; /* Have we initialised the table? If not, intialise as much as possible. */ if(!uv->sigma2.missing && uv->sigma2.table == NULL){ if( (uv->pol = uv_locvar(uv->tno,"pol") ) != NULL) (void)uv_checkvar(uv->tno,"pol",H_INT); uvvarini_c(uv->tno,&(uv->sigma2.vhan)); uvvarset_c(uv->sigma2.vhan,"nants"); uvvarset_c(uv->sigma2.vhan,"inttime"); uvvarset_c(uv->sigma2.vhan,"jyperk"); uv->sigma2.missing = (uv_locvar(uv->tno,"inttime") == NULL) | (uv_locvar(uv->tno,"jyperk") == NULL) | (uv_locvar(uv->tno,"nants") == NULL); if(line->linetype == LINE_CHANNEL){ uvvarset_c(uv->sigma2.vhan,"systemp"); uvvarset_c(uv->sigma2.vhan,"sdf"); uvvarset_c(uv->sigma2.vhan,"nschan"); uv->sigma2.missing |= (uv_locvar(uv->tno,"systemp") == NULL) | (uv_locvar(uv->tno,"sdf") == NULL) | (uv_locvar(uv->tno,"nschan") == NULL); } else if(line->linetype == LINE_VELOCITY){ uvvarset_c(uv->sigma2.vhan,"systemp"); uvvarset_c(uv->sigma2.vhan,"restfreq"); uv->sigma2.missing |= (uv_locvar(uv->tno,"systemp") == NULL) | (uv_locvar(uv->tno,"restfreq")== NULL); } else { uvvarset_c(uv->sigma2.vhan,"wsystemp"); uvvarset_c(uv->sigma2.vhan,"wwidth"); uv->sigma2.missing |= (uv_locvar(uv->tno,"wsystemp") == NULL) | (uv_locvar(uv->tno,"wwidth") == NULL); } } /* Return if we do not have enough info to determine the variance. */ if(uv->sigma2.missing) return; /* Is the table of variances out of date? If so recompute it. */ if(uvvarupd_c(uv->sigma2.vhan) == FORT_TRUE){ nants = *(int *)(uv_checkvar(uv->tno,"nants",H_INT)->buf); inttime = *(float *)(uv_checkvar(uv->tno,"inttime",H_REAL)->buf); jyperk = *(float *)(uv_checkvar(uv->tno,"jyperk",H_REAL)->buf); if(line->linetype == LINE_CHANNEL){ nschan = (int *)(uv_checkvar(uv->tno,"nschan",H_INT)->buf); start = line->start; offset = 0; while(start >= *nschan){ start -= *nschan++; offset++; } bw = *((double *)(uv_checkvar(uv->tno,"sdf",H_DBLE)->buf) + offset) * line->width; tsys = uv_checkvar(uv->tno,"systemp",H_REAL); } else if(line->linetype == LINE_WIDE){ offset = line->start; bw = *((float *)(uv_checkvar(uv->tno,"wwidth",H_REAL)->buf) + offset) * line->width; tsys = uv_checkvar(uv->tno,"wsystemp",H_REAL); } else if(line->linetype == LINE_VELOCITY){ offset = uv->win->first; restfreq = (double *)(uv_checkvar(uv->tno,"restfreq",H_DBLE)->buf) + offset; bw = *restfreq * line->fwidth / CKMS; tsys = uv_checkvar(uv->tno,"systemp",H_REAL); } if(bw < 0) bw = - bw; /* We have everything we ever wanted: jyperk,inttime,bw and Tsys. Compute variance. */ if(nants > uv->sigma2.nants){ if(uv->sigma2.table != NULL)free((char *)(uv->sigma2.table)); uv->sigma2.table = (double *)Malloc(sizeof(double)*(nants*(nants+1))/2); } uv->sigma2.nants = nants; nsyst = VARLEN(tsys); syst = (float *)(tsys->buf); factor = jyperk*jyperk/inttime/(2.0e9*bw) * uv->plscale; tab = uv->sigma2.table; if(nsyst < nants){ factor *= *syst * *syst; for(i=0; i < (nants*(nants+1))/2; i++)*tab++ = factor; } else { if(nsyst >= nants*(offset+1)) syst += nants*offset; t2 = syst; for(j=0; j < nants; j++){ t1 = syst; for(i=0; i <= j; i++){ *tab++ = factor * *t1++ * *t2; } t2++; } } } /* All is up to date and OK. Return the result. */ bl = *((float *)(uv->bl->buf)) + 0.5; uvbasant_c(bl,&i1,&i2); if(i1 < 1 || i2 > uv->sigma2.nants)return; bl = (i2*(i2-1))/2+i1-1; *data = uv->sigma2.table[bl]; /* If its a Stokes parameter, multiply the variance by one half. */ if(uv->pol != NULL && *((int*)(uv->pol->buf)) > 0) *data *= 0.5; } /************************************************************************/ private void uvinfo_chan(UV *uv,double *data,int mode) /* ------------------------------------------------------------------------*/ { LINE_INFO *line; int n,i,j,step; off_t offset; double temp,fdash; float *wfreq,*wwide,vobs; int *nschan; double *sdf,*sfreq,*restfreq; /* Get the velocity of the "channel" line type. */ line = &(uv->actual_line); n = line->n; if(line->linetype == LINE_CHANNEL){ vobs = *(float *)(uv_checkvar(uv->tno,"veldop",H_REAL)->buf) - *(float *)(uv_checkvar(uv->tno,"vsource",H_REAL)->buf); nschan = (int *)(uv_checkvar(uv->tno,"nschan",H_INT)->buf); sfreq = (double *)(uv_checkvar(uv->tno,"sfreq",H_DBLE)->buf); sdf = (double *)(uv_checkvar(uv->tno,"sdf",H_DBLE)->buf); restfreq = (double *)(uv_checkvar(uv->tno,"restfreq",H_DBLE)->buf); step = line->step - line->width; offset = line->start; for(j=0; j < n; j++){ temp = 0; while(offset >= *nschan){ offset -= *nschan++; sfreq++; sdf++; restfreq++; } for(i=0; i < line->width; i++){ if(offset == *nschan){ offset = 0; sfreq++; sdf++; nschan++; restfreq++; } if(mode == VELO){ if(*restfreq <= 0)BUG('f',"Cannot determine velocity as rest frequency is 0"); fdash = (*sfreq + offset * *sdf)*(1 + vobs/CKMS); temp += CKMS * ( 1 - fdash / *restfreq ); }else if(mode == FELO){ if(*restfreq <= 0)BUG('f',"Cannot determine velocity as rest frequency is 0"); fdash = (*sfreq + offset * *sdf)*(1 + vobs/CKMS); temp += CKMS * ( *restfreq / fdash - 1 ); }else if(mode == RFREQ) temp += *restfreq; else if(mode == BW) temp += (*sdf > 0 ? *sdf : - *sdf); else if(mode == FREQ) temp += (*sfreq + offset * *sdf)*(1 + vobs/CKMS); else if(mode == SFREQ) temp += *sfreq + offset * *sdf; offset++; } if(mode != BW) *data++ = temp / line->width; else *data++ = temp; offset += step; } /* Wide channel information. Getting the velocity of this does not make a great deal of sense. Assume the rest frequency is the same as the sky frequency of the first wide channel. */ } else if(line->linetype == LINE_WIDE){ if(mode == RFREQ || mode == VELO || mode == FELO){ BUG('f',"Invalid object for wide linetype, in UVINFO\n"); } else if(mode == FREQ || mode == SFREQ){ step = line->step - line->width; wfreq = (float *)(uv_checkvar(uv->tno,"wfreq",H_REAL)->buf); wfreq += line->start; for(j=0; j < n; j++){ temp = 0; for(i=0; i < line->width; i++) temp += *wfreq++; *data++ = temp/line->width; wfreq += step; } } else if(mode == BW){ step = line->step - line->width; wwide = (float *)(uv_checkvar(uv->tno,"wwidth",H_REAL)->buf); wwide += line->start; for(j=0; j < n; j++){ temp = 0; for(i=0; i < line->width; i++) temp += *wwide++; *data++ = temp; wwide += step; } } /* Velocity channel information. This is pretty trivial. */ } else if(line->linetype == LINE_VELOCITY){ if(mode == VELO){ for(i=0; ifstart + i * line->fstep; } else if(mode == FELO){ vobs = *(float *)(uv_checkvar(uv->tno,"veldop",H_REAL)->buf) - *(float *)(uv_checkvar(uv->tno,"vsource",H_REAL)->buf); for(i=0; ifstart + i * line->fstep; *data++ = CKMS*temp / (CKMS-temp); } } else if(mode == RFREQ){ restfreq = (double *)(uv_checkvar(uv->tno,"restfreq",H_DBLE)->buf) + uv->win->first; for(i=0; itno,"restfreq",H_DBLE)->buf) + uv->win->first; for(i=0; ifstart + i *line->fstep)/CKMS); } else if(mode == SFREQ){ restfreq = (double *)(uv_checkvar(uv->tno,"restfreq",H_DBLE)->buf) + uv->win->first; vobs = *(float *)(uv_checkvar(uv->tno,"veldop",H_REAL)->buf) - *(float *)(uv_checkvar(uv->tno,"vsource",H_REAL)->buf); for(i=0; ifstart + i *line->fstep)/CKMS)/ (1+vobs/CKMS); } else if(mode == BW){ restfreq = (double *)(uv_checkvar(uv->tno,"restfreq",H_DBLE)->buf) + uv->win->first; temp = *restfreq * line->fwidth / CKMS; if(temp < 0) temp = - temp; for(i=0; i 65536){ *i2 -= 65536; mant = MAXIANT; }else{ mant = 256; } *i1= *i2 / mant; *i2 %= mant; } casacore-3.7.1/mirlib/xyio.c000066400000000000000000000413301476623553700157370ustar00rootroot00000000000000/************************************************************************/ /* */ /* Routines to access and manipulate an image. */ /* */ /* History: */ /* rjs Dark-ages Original version. */ /* rjs 6nov89 Neatly handle the case of a non-existent mask file.*/ /* rjs 7feb90 Added comments, ready to be stripped out by "doc". */ /* rjs 13jul92 Improved error messages in xyopen, to appease nebk.*/ /* rjs 23feb93 Include maxdimc.h, which contains maxnax. */ /* rjs 6nov94 Change item handle to an integer. */ /* rjs 27feb96 Added xyflush. */ /* rjs 15mar96 Inlcude an exrta include file. */ /* pjt 17jun02 MIR4 prototypes, > 2GB patches */ /* rjs/pjt 3jun03 "append" mode in xyopen - long live non-CVS devel. */ /* pkgw 09may12 Prevent XYOPEN callers from causing it to scribble */ /* over our image data structures (and potentially */ /* beyond). */ /*----------------------------------------------------------------------*/ #if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "maxdimc.h" #include "io.h" #include "miriad.h" #define OLD 1 #define NEW 2 #define MK_FLAGS 1 #define MK_RUNS 2 #define check(x) if(x)bugno_c('f',x) #define CHECK(x,a) if(x) { bug_c('w',((void)sprintf a,message)); \ bugno_c('f',x); \ } #define ERROR(sev,a) bug_c(sev,((void)sprintf a,message)) static char message[132]; static struct { char *mask; int image; int naxis,axes[MAXNAX],mask_exists,image_exists; off_t offset; } images[MAXOPEN]; #define Strcpy (void)strcpy static void xymkopen_c(int thandle,int mode); /************************************************************************/ void xyopen_c(int *thandle,Const char *name,Const char *status,int naxis,int *axes) /**xyopen -- Open an image file. */ /*:image-i/o */ /*+ FORTRAN call sequence: subroutine xyopen(tno,name,status,naxis,axes) integer tno,naxis,axes(naxis) character name*(*),status*(*) This opens an image file. For an old file, determine the size of each axe. For a new file, it writes out this info. Input: name The name of the file to be opened. status Either 'old', 'new' or 'append'. naxis The maximum number of axes that the calling program can handle. For an 'old' file, if the data file has fewer than naxis axes, the higher dimensions are treated as having only one element. If the data file has more than naxis axes, and the higher dimensions are more than 1 element deep, xyopen bombs out. Input or Output: axes This is input for status='new' and output for status='old'. It gives the number of elements along each axis. Output: tno The handle of the output file. */ /*----------------------------------------------------------------------*/ { int iostat,length,access,tno,i,ndim,npix,temp; char *stat,*mode,naxes[16],s[ITEM_HDR_SIZE]; if(!strcmp("old",status)) { access = OLD; mode = "read"; stat = "old";} else if(!strcmp("append",status)){ access = OLD; mode = "append";stat = "old";} else if(!strcmp("new",status)) { access = NEW; mode = "write"; stat = "new";} else ERROR('f',(message,"Unrecognised status when opening %s, in XYOPEN",name)); if(naxis > MAXNAX) /* If the above is true, the current code scribbles off the end * of image[tno].axes. With a bit of work we could probably * handle this situation, but it's a lot easier to just say * "don't do this." */ ERROR('f',(message,"Program wanted %d axes but XYOPEN can only provide %d", naxis,MAXNAX)); /* Access the image data. */ hopen_c(&tno,name,stat,&iostat); CHECK(iostat,(message,"Error opening %s, in XYOPEN",name)); haccess_c(tno,&(images[tno].image),"image",mode,&iostat); CHECK(iostat,(message,"Error accessing pixel data of %s, in XYOPEN",name)); /*----------------------------------------------------------------------*/ /* */ /* Handle an old image. Get number of axes, and then the length */ /* of each axis. Also compute and check that the size of the */ /* image file looks OK. */ /* */ /*----------------------------------------------------------------------*/ if(access == OLD){ rdhdi_c(tno,"naxis",&ndim,0); if(ndim <= 0 || ndim > MAXNAX) ERROR('f',(message,"Bad number of dimensions for %s in XYOPEN",name)); Strcpy(naxes,"naxis0"); length = strlen(naxes) - 1; npix = 1; for(i=0; i 1) ERROR('f',(message,"Too many dimensions for %s, in XYOPEN",name)); } /* Check the file size if OK and that it starts with the "real_item" sequence. */ if(hsize_c(images[tno].image) < H_REAL_SIZE*npix+ITEM_HDR_SIZE) ERROR('f',(message,"Pixel data for %s appears too small, in XYOPEN",name)); hreadb_c(images[tno].image,s,0,ITEM_HDR_SIZE,&iostat); CHECK(iostat,(message,"Error reading pixel data label for %s, in XYOPEN",name)); if( memcmp(s,real_item,ITEM_HDR_SIZE) ) ERROR('f',(message,"Bad pixel data label for %s, in XYOPEN",name)); /*----------------------------------------------------------------------*/ /* */ /* A new image. Write out all the axes infomation, and initialise */ /* the file with the "binary item" sequence. */ /* */ /*----------------------------------------------------------------------*/ } else { wrhdi_c(tno,"naxis",naxis); Strcpy(naxes,"naxis0"); length = strlen(naxes) - 1; for(i=0; i < naxis; i++){ naxes[length] ++; wrhdi_c(tno,naxes,axes[i]); } hwriteb_c(images[tno].image,real_item,0,ITEM_HDR_SIZE,&iostat); CHECK(iostat,(message,"Error writing pixel data label for %s, in XYOPEN",name)); } /* Common to old and new. Copy the dimension info to the local description. */ images[tno].offset = 0; images[tno].naxis = naxis; for(i=0; i < naxis; i++) images[tno].axes[i] = axes[i]; for(i = naxis; i < MAXNAX; i++) images[tno].axes[i] = 1; images[tno].mask = NULL; images[tno].image_exists = TRUE; images[tno].mask_exists = TRUE; *thandle = tno; } /************************************************************************/ void xyflush_c(int thandle) /**xyflush -- Flush out any image changes to disk. */ /*:image-i/o */ /*+ FORTRAN call sequence: subroutine xyflush(tno) implicit none This flushes any changes to an image to disk. Input: tno The handle of the image file. */ /*----------------------------------------------------------------------*/ { int iostat,i; off_t offset; size_t nbytes, length; float buf[MAXDIM]; /* Simply flush out the mask. */ if(images[thandle].mask != NULL) mkflush_c(images[thandle].mask); /* If its a new file, and not all the pixels have yet been written, write zero pixels. First determine the proper size. */ nbytes = H_REAL_SIZE; for(i=0; i < images[thandle].naxis; i++) nbytes *= images[thandle].axes[i]; nbytes += ITEM_HDR_SIZE; offset = hsize_c(images[thandle].image); /* Determine the number of bytes to pad, and then pad it. */ nbytes -= offset; if(nbytes > 0)for(i=0; i < MAXDIM; i++)buf[i] = 0.0; while(nbytes > 0){ length = MAXDIM*H_REAL_SIZE; if(length > nbytes) length = nbytes; hwriter_c(images[thandle].image,buf,offset,length,&iostat); CHECK(iostat,(message,"Error allocating space for image")); offset += length; nbytes -= length; } /* Do it all now. */ hflush_c(thandle,&iostat); check(iostat); } /************************************************************************/ void xyclose_c(int thandle) /**xyclose -- Close up an image file. */ /*:image-i/o */ /*+ FORTRAN call sequence: subroutine xyclose(tno) integer tno This closes an image file. Input: tno The handle of the image file. */ /*----------------------------------------------------------------------*/ { int iostat; hdaccess_c(images[thandle].image,&iostat); check(iostat); if(images[thandle].mask != NULL) mkclose_c(images[thandle].mask); hclose_c(thandle); } /************************************************************************/ void xyread_c(int thandle,int index,float *array) /**xyread -- Read a row from an image. */ /*:image-i/o */ /*+ FORTRAN call sequence: subroutine xyread(tno,index,array) integer tno,index real array(*) This reads a single row from an image. This accesses the plane given by the last call to xysetpl. Input: tno The image file handle, returned by xyopen. index The row number to read. This varies from 1 to NAXIS2. Output: array The read row. NAXIS1 elements are returned. */ /*----------------------------------------------------------------------*/ { off_t offset; size_t length; int iostat; length = H_REAL_SIZE * images[thandle].axes[0]; offset = H_REAL_SIZE * images[thandle].offset + (index-1) * length + ITEM_HDR_SIZE; hreadr_c(images[thandle].image,array,offset,length,&iostat); check(iostat); } /************************************************************************/ void xywrite_c(int thandle,int index,Const float *array) /**xywrite -- Write a row to an image. */ /*:image-i/o */ /*+ FORTRAN call sequence: subroutine xywrite(tno,index,array) integer tno,index real array(*) This writes a single row to an image. This accesses the plane given by the last call to xysetpl. Input: tno The image file handle, returned by xyopen. index The row number to write. This varies from 1 to NAXIS2. array The read row. NAXIS1 elements are written. */ /*----------------------------------------------------------------------*/ { off_t offset; size_t length; int iostat; length = H_REAL_SIZE * images[thandle].axes[0]; offset = H_REAL_SIZE * images[thandle].offset + (index-1) * length + ITEM_HDR_SIZE; hwriter_c(images[thandle].image,array,offset,length,&iostat); check(iostat); } /************************************************************************/ void xymkrd_c(int thandle,int index,int *runs,int n,int *nread) /**xymkrd -- Read the masking information for an image (runs format). */ /*:image-i/o */ /*+ FORTRAN call sequence: subroutine xymkrd(tno,index,runs,n,nread) integer tno,index,n,nread integer runs(n) This reads the masking information associated with a row of an image, and returns it in "runs" format. Input: tnoe The handle associated with the image. index The index of the row to determine mask info about. The last call to xysetpl determines which plane to access. n The size of the array to receive the mask info. Output: runs The mask info, in "runs" form. If "i" varies from 1 to nread/2, then pixels runs(2*i-1) to runs(2*i) are good, whereas pixels runs(2*i) to runs(2*i+1) are bad. nread The number of "runs" read. */ /*----------------------------------------------------------------------*/ { off_t offset; size_t length; if(images[thandle].mask == NULL && images[thandle].mask_exists) xymkopen_c(thandle,OLD); if(images[thandle].mask_exists){ length = images[thandle].axes[0]; offset = images[thandle].offset + (index-1) * length; *nread = mkread_c(images[thandle].mask,MK_RUNS,runs,offset,length,n); } else { if(n < 2) bug_c('f',"xymkrd_c: Runs array overflow"); runs[0] = 1; runs[1] = images[thandle].axes[0]; *nread = 2; } } /************************************************************************/ void xymkwr_c(int thandle,int index,Const int *runs,int n) /**xymkwr -- write image masking information (runs format). */ /*:image-i/o */ /*+ FORTRAN call sequence: subroutine xymkwr(tno,index,runs,n) integer tno,index,n integer runs(n) This writes out the masking information associated with a row of an image. This information is passes in in "runs" format. Input: tnoe The handle associated with the image. index The index of the row to determine mask info about. The last call to xysetpl determines which plane to access. n The size of the array containing the mask info. runs The mask info, in "runs" form. If "i" varies from 1 to nread/2, then pixels runs(2*i-1) to runs(2*i) are good, whereas pixels runs(2*i) to runs(2*i+1) are bad. */ /*----------------------------------------------------------------------*/ { off_t offset; size_t length; if(images[thandle].mask == NULL) xymkopen_c(thandle,NEW); if(images[thandle].mask == NULL) bug_c('f',"xymkwr_c: Error writing to image mask file"); length = images[thandle].axes[0]; offset = images[thandle].offset + (index-1) * length; mkwrite_c(images[thandle].mask,MK_RUNS,runs,offset,length,n); } /************************************************************************/ void xyflgwr_c(int thandle,int index,Const int *flags) /**xyflgwr -- Write image masking information (flags format). */ /*:image-i/o */ /*+ FORTRAN call sequence: subroutine xyflgwr(tno,index,flags) integer tno,index logical flags(*) This writes image mask information. It is the counterpart of xywrite. Input: tno Handle of the image file. index The row in a plane to write out. This varies between 1 and NAXIS2. See xysetpl to set the which plane is to be accessed. flags A logical array of NAXIS1 elements. A true value indicates that the pixel is good. */ /*----------------------------------------------------------------------*/ { off_t offset; size_t length; if(images[thandle].mask == NULL)xymkopen_c(thandle,NEW); if(images[thandle].mask == NULL) bug_c('f',"xyflgwr_c: Error writing to image mask file"); length = images[thandle].axes[0]; offset = images[thandle].offset + (index-1) * length; mkwrite_c(images[thandle].mask,MK_FLAGS,flags,offset,length,length); } /************************************************************************/ void xyflgrd_c(int thandle,int index,int *flags) /**xyflgrd -- Read image masking information (flags format). */ /*:image-i/o */ /*+ FORTRAN call sequence: subroutine xyflgrd(tno,index,flags) integer tno,index logical flags(*) This reads image mask information. It is the counterpart of xyread. Input: tno Handle of the image file. index The row in a plane to read in. This varies betwen 1 and NAXIS2. Set xysetpl to change the plane being accessed. Output: flags A logical array of NAXIS1 elements. A true value indicates that the pixel is good. */ /*----------------------------------------------------------------------*/ { int n,i; off_t offset; size_t length; if(images[thandle].mask == NULL && images[thandle].mask_exists) xymkopen_c(thandle,OLD); if(images[thandle].mask_exists){ length = images[thandle].axes[0]; offset = images[thandle].offset + (index-1) * length; n = mkread_c(images[thandle].mask,MK_FLAGS,flags,offset,length,length); } else { n = images[thandle].axes[0]; for(i=0; i MAXNAX) bug_c('f',"xysetpl_c: Too many dimensions"); size = 0; for(i=naxis-1; i >= 0; i--){ if(axes[i] < 1 || axes[i] > images[thandle].axes[i+2]) { printf("i=%d axis[i]=%d images[thandle].axes[i+2]=%d\n", i, axes[i], images[thandle].axes[i+2]); bug_c('f',"Dimension error in XYSETPL"); } size = ( size + axes[i] - 1) * images[thandle].axes[i+1]; } images[thandle].offset = size * images[thandle].axes[0]; } casacore-3.7.1/mirlib/xyzio.c000066400000000000000000002462621476623553700161440ustar00rootroot00000000000000/******************************************************************************* Routines to read and write an image dataset in arbitrary XYZ mode History: bpw 19-apr-91 Created bpw 01-may-91 Algorithm ready bpw 03-may-91 Ready bpw 19-may-91 Add reverse bpw 20-may-91 Include dummy masking scheme bpw 21-jun-91 Installed bpw 25-jun-91 Created get_buflen function bpw 27-jun-91 Moved FORTRAN-C conversion to xyziowrap.h pjt/mjs 28jul91 Renamed internal variable "max" to "themax" to eliminate conflict with max function. bpw 29-jul-91 Got rid of themax, and made it into sizeof bpw 09-aug-91 Added '-start' to bufend in zero(2) bpw 08-sep-92 Made ndata indeed output variable for xyzread rjs 22-dec-92 Delete inclusion of xyziowrap.h in xyzio.h rjs 23-feb-93 Include maxdimc.h, which includes definition of MAXNAX and MAXBUF. Use MAXBUF. Get rid of xyzio.h bpw 2-mar-93 Add real masking bpw 9-jul-93 Added xyzflush_c and xyzmkbuf_c, and changed buffer allocation scheme to avoid unnecessary allocations bpw 27-jul-93 Fixed allocation bug introduced in previous update (problems for 1-plane datasets) rjs 4-sep-94 Change "word" to "words" to satisfy Cray compiler. rjs 6-nov-94 Change item handle to an integer. bpw 8-dec-94 Adapt two loop in bufferalloc for the fact that since 6 nov image handles are no longer in sequence. bpw 12-feb-96 follow rjs to eliminate nested comments (without using tabs) bpw 12-jun-98 for zero(1,tno) set whole cube mask to FALSE pjt 16-nov-00 Fixed a pretty serious problem of bpw mixing up | w/ || and & w/ &&. Also forced initialized of mask due, there were some side-effects here too if no mask was present (note xyzio writes a mask, even if full mask ok) pjt 11-jun-01 added rjs' 10-jan-96 code changes that seemed lost "Correct comparision bug in bufferallocation routine." pjt 20-jun-02 prototypes for MIR4, made most local stuff now static, largely thanks to Amtrak for a long boring ride NYC-NCR pjt 14-jan-03 cleared up some more prototypes, fixed bug in *s[ITEM_HDR_SIZE] declaration (no pointer, just char) jwr 18-may-05 print address using %p instead of %d rjs 18-sep-05 Added routine xyzdim_. mhw 09-mar-12 Replace a lot of int's with long's to cope with large cubes (>8GB) pjt 28-jun-12 Fixed get_buflen() , fix (?) usage of MAXBUF pjt 22-jan-13 bufferallocation() : another forgotten 64bit allocation *******************************************************************************/ /******************************************************************************/ /* */ /* Declarations */ /* */ /******************************************************************************/ #include #include #include #include "miriad.h" #include "io.h" #include "maxdimc.h" #define check(x) if(x)bugno_c('f',x) /* There is only one buffer array, of a length determined at run-time by get_buflen. buffersize is size of the virtual buffer for a particular image, which varies with the number of images handled (ntno) */ static long buffersize; static int allocatebuffer, neverfree=FALSE; static long currentallocation=0; static float *buffer = NULL; /* data */ static int *mbuffr = NULL; /* mask */ /* Most of the code for reading and writing is exactly the same. Where a difference exists MODE (values GET and PUT) is used to discriminate. UP and DOWN are used for copying or reverse copying. ALL is used to see if all axes are reversed. */ static int MODE; #define GET 0 #define PUT 1 #define UP 1 #define DOWN 2 #define ALL 2 /* MAXNAX: maximum number of axes that this can handle. ARRSIZ = MAXNAX+1, so that element 1<->x, 2<->y, etc */ #define ARRSIZ MAXNAX+1 /* imgs: dataset info; bufs: buffers info .itno: image handle for hio routines .number: counter of how many datasets were opened before .naxis, .axlen, .cubesize, .blc, .trc: you know .lower, .upper: lower and upper coordval of elements currently in buffer .filfir, .fillas: first and last element from cube currently in buffer .bufstart: abbreviation for -.filfir+tno*buffersize .lastwritten: to see if old data must be read in before writing buffer .nocopy: true if no transposition or region present axnum: relation between axes: i-th axis used to be axis axnum(i) reverse: tells if output data array must be reversed written: to see if buffer must be flushed on a close or new read to same newbuffer: to allow a check if xyzsetup is called more often for a dataset ntno: number of datasets currently opened */ static struct { int itno; char *mask; int number; int naxis, axlen[ARRSIZ]; long cubesize[ARRSIZ]; int blc[ARRSIZ], trc[ARRSIZ]; int lower[ARRSIZ], upper[ARRSIZ]; long filfir, fillas, bufstart; long lastwritten; int nocopy; } imgs[MAXOPEN], bufs[MAXOPEN]; static int axnum[MAXOPEN][ARRSIZ]; static int reverse[MAXOPEN][ARRSIZ]; static int written[MAXOPEN]; static int ntno = 0; /* loop variables (dim and d) and dimension of subcube */ static int dim, d, dimsub[MAXOPEN]; /* arrays used to limit number of pointer calculations inside big loop in loop_inpbuffer (i.e. remove index [tno]) and to improve readability of the code */ static int naxes; static int imgsblc[ARRSIZ], imgstrc[ARRSIZ]; static int imgslower[ARRSIZ], imgsupper[ARRSIZ]; static int imgsaxlen[ARRSIZ]; static long imgscubesize[ARRSIZ], imgscsz[ARRSIZ]; static int bufsblc[ARRSIZ], bufstrc[ARRSIZ]; static int bufsaxlen[ARRSIZ]; static long bufscubesize[ARRSIZ], bufscsz[ARRSIZ]; static int axnumr[ARRSIZ], inv_axnumr[ARRSIZ], reverses[ARRSIZ]; /* Some variables not used, but left in for the (hopefully never occuring) case that an error occurred and debugging is needed. Most if(.test) statements have been left active. Some, the ones in inner loops, are disabled. They can be found by searching for '/ * $ $' (without spaces) */ static int itest = 0; /* Information on buffers and datasets */ static int otest = 0; /* Information on subcubes */ static int rtest = 0; /* Information on each array element */ static int vtest = 0; /* Puts numbers in buffer without reading a dataset */ static int tcoo[ARRSIZ]; static int nfound, i; static char *words[4] = { "get", "put", "filled", "emptied" }; static int nio=0; /* static functions */ static void get_test(int interactive); static int putnio(int x); static void ferr(char *string, int arg); static void get_put_data(int tno, long virpix_off, float *data, int *mask, int *ndata, int dim_sub); static void do_copy(float *bufptr, float *bufend, int DIR, float *data, int *mask); static void manage_buffer(int tno, long virpix_off); static void manage_the_buffer(int tno, long virpix_off); static void get_buflen(void); static long bufferallocation(long n); static void copy_to_one_d(int tno); static void set_bufs_limits(int tno, long virpix_off); static long get_last(long start, long finis); static int check_do_io(int tno, long start, long last); static void find_block(long start, long last, int lower[], int upper[], int axlen[], long cubesize[], int blc[], int trc[], int naxis); static long transform_back(long pix_off); static long c2p(int coords[], long cubesize[], int naxis); static void p2c(long pix_off, int axlen[], long cubesize[], int naxis, int coords[]); static void fill_buffer(int tno, long start, long last); static void empty_buffer(int tno, long start, long last); static void loop_buffer(int tno, long start, long last, long *newstart); static void zero(int bl_tr, int tno); static void testprint(int tno, long virpix_off, long virpix_lst); static void limprint(char *string, int lower[], int upper[]); static void testsearch(int callnr, int coords[], long filoff, long viroff); static void get_test(int interactive) { if(interactive)printf("iTest >"); scanf("%d",&itest); if(interactive)printf("rTest >"); scanf("%d",&rtest); if(interactive)printf("oTest >"); scanf("%d",&otest); if(interactive)printf("vTest >"); scanf("%d",&vtest); } static int putnio(int x) { return nio; } /******************************************************************************/ /* */ /* The FORTRAN-callable routines */ /* */ /******************************************************************************/ /** xyzopen -- Open an image file. */ /*& bpw */ /*: image-i/o */ /*+ subroutine xyzopen( tno, name, status, naxis, axlen ) integer tno character*(*) name character*(*) status integer naxis integer axlen(naxis) This opens an image file. For an old file, the number of axes is returned in naxis and the size of each axis in axlen. For a new file, this information is used to define the dataset. Input: name The name of the file to be opened status Either 'old' or 'new' Output: tno The image-file handle Input or Output: naxis For 'old' datasets: in input dimension of array axlen, on output dimension of datacube; for 'new' datasets: dimension of new dataset axlen The length of the axes, output for 'old' datasets, 'input' for 'new' datasets */ /*-- */ void xyzopen_c( int *handle, Const char *name, Const char *status, int *naxis, int *axlen ) { /* This accesses the image data (hopen and haccess). Then it checks whether this was an OLD dataset. If so, the naxis. items are read from the header and a check is made on the size of the image file. For a new dataset the information is written out and the file initialized with the "binary item" sequence. Finally the information is stored in the imgs structure for later use. */ #define OLD 1 #define NEW 2 int access, n_axis, tno, iostat; long cubesize; char s[ITEM_HDR_SIZE], axes[8], *mode; static int first=TRUE; if(first) { for(tno=0;tno n_axis ) bug_c('f',"xyzopen: Too many axes for this task"); if( *naxis<=0||*naxis>MAXNAX ) bug_c('f',"xyzopen: Bad number of axes"); for( cubesize=1, d=0; d<*naxis; d++ ) { axes[5]++; rdhdi_c( tno, axes, &axlen[d], 0 ); if( axlen[d] <= 0 ) bug_c( 'f', "xyzopen: Bad image dimension" ); cubesize = cubesize * axlen[d]; } if( hsize_c( imgs[tno].itno ) < H_REAL_SIZE*cubesize+ITEM_HDR_SIZE ) bug_c( 'f', "xyzopen: Image file appears too small" ); hreadb_c( imgs[tno].itno, s,0,ITEM_HDR_SIZE, &iostat ); check(iostat); if( memcmp( s, real_item, ITEM_HDR_SIZE ) ) bug_c( 'f', "xyzopen: Bad image file" ); } else { wrhdi_c( tno, "naxis", *naxis ); for( d=0; d<*naxis; d++ ) { axes[5]++; wrhdi_c( tno, axes, axlen[d] ); } hwriteb_c( imgs[tno].itno, real_item,0,ITEM_HDR_SIZE, &iostat ); check(iostat); } imgs[tno].naxis = *naxis; imgs[tno].cubesize[0] = 1; imgs[tno].axlen[0] = 1; for( d=1; d<=*naxis; d++ ) { imgs[tno].axlen[d] = axlen[d-1]; imgs[tno].cubesize[d] = imgs[tno].cubesize[d-1] * imgs[tno].axlen[d]; } if( access == OLD ) imgs[tno].lastwritten = imgs[tno].cubesize[*naxis]; else imgs[tno].lastwritten = -1; *handle = tno; ntno++; imgs[tno].number = ntno; dimsub[tno] = -1; } /******************************************************************************/ /** xyzdim - Return dimension information. */ /*& rjs */ /*: image-i/o */ /*+ subroutine xyzdim(tno,naxis,dimsub integer tno,naxis,dimsub This returns dimension information. Input: tno The image file handle. Output: naxis Number of dimensions. dimsub Number of skipped subdimensions. */ /*--*/ void xyzdim_c(int tno,int *naxis,int *subdim) { *naxis = imgs[tno].naxis; *subdim = dimsub[tno]; } /******************************************************************************/ /** xyzpix - Return information on number of pixels. */ /*& rjs */ /*: image-i/o */ /*+ integer function xyzpix(tno,dims) integer tno,dim This returns dimension information. Input: tno The image file handle. dim Dimension information. */ /*--*/ int xyzpix_c(int tno,int dims) { int dim_sub; dim_sub = dims; if(dim_sub == 0)dim_sub = dimsub[tno]; return(bufs[tno].cubesize[dim_sub]); } /******************************************************************************/ /** xyzclose - Close an image file */ /*& bpw */ /*: image-i/o */ /*+ subroutine xyzclose( tno ) integer tno This closes an image file. Input: tno: The image-file handle */ /*--*/ void xyzclose_c( int tno ) { int iostat; xyzflush_c( tno ); hdaccess_c( imgs[tno].itno, &iostat ); check(iostat); if( imgs[tno].mask ) mkclose_c( imgs[tno].mask ); hclose_c( tno ); ntno--; if( ntno == 0 && !neverfree ) { free( buffer ); buffer = NULL; free( mbuffr ); mbuffr = NULL; } } /******************************************************************************/ /** xyzflush - Force output buffer to be written to disk */ /*& bpw */ /*: image-i/o */ /*+ subroutine xyzflush( tno ) integer tno This flushes the output buffer to disk, just like when closing a dataset. However, the dataset remains open. This is intended for usage where there is a limit on the number of open datasets, so that one cannot have them open all at the same time, and then do all setups once. Input: tno: The image-file handle */ /*--*/ void xyzflush_c( int tno ) { if( written[tno] ) { MODE=PUT; manage_buffer( tno, -1 ); } written[tno] = FALSE; if( imgs[tno].lastwrittend) viraxlen(i) */ /*--*/ void xyzsetup_c( int tno, Const char *subcube, Const int *blc, Const int *trc, int *viraxlen, long *vircubesize ) { /* This initializes some information needed later. It keeps separate values for each tno that was opened. dimsub: dimension of subcube axnum: relation between axes imgs.blc, imgs.trc: lower left and upper right used from input or written to output bufs.axlen: length of virtual axes imgs/bufs.cubesize: cs(i) = (Prod)(dnaxes || !*axisnames ) ferr( "xyzsetup: Axis outside cube", *subcube ); if(axisuse[d])ferr("xyzsetup: Axis given more than once",*subcube); dim++; axisuse[d]=TRUE; reverse[tno][dim]=reversal; axnum[tno][dim]=d; reversal=FALSE; } subcube++; } dimsub[tno] = dim; subcube = sub_cube; /* Fill out the arrays axnum and reverse, so that all elements are defined */ for( reverse[tno][0]=FALSE, d=0, dim=1; dim<=dimsub[tno]; dim++ ) { if( reverse[tno][dim] ) { reverse[tno][0]=TRUE; d++; } } if( d == dimsub[tno] ) reverse[tno][0]=ALL; for( d=1; d<=MAXNAX; d++ ) { if( !axisuse[d] ) { axnum[tno][dim]=d; reverse[tno][dim]=FALSE; dim++; } } /* Save blc and trc */ for( dim=1; dim<=naxes; dim++ ) { if( ( blc[dim-1] < 1 ) || ( trc[dim-1] > imgs[tno].axlen[dim] ) ) bug_c( 'f', "xyzsetup: Subcube blc and/or trc outside range" ); imgs[tno].blc[dim] = blc[dim-1]-1; imgs[tno].trc[dim] = trc[dim-1]-1; } /* Save axislengths and cubesizes */ bufs[tno].naxis = naxes; bufs[tno].axlen[0] = 1; bufs[tno].cubesize[0] = 1; for( dim=1; dim<=naxes; dim++ ) { bufs[tno].axlen[dim] = imgs[tno].trc[ axnum[tno][dim] ] - imgs[tno].blc[ axnum[tno][dim] ] + 1; } for( dim=1; dim<=naxes; dim++ ) { bufs[tno].cubesize[dim]=bufs[tno].cubesize[dim-1]*bufs[tno].axlen[dim]; } /* More initializations: pointers to window in file that is in buffer; variable indicating if write buffer was filled; variable indicating if any transposition must be done; */ for( d=0; d= bufs[tno].cubesize[naxes] ) bug_c( 'f', "xyzs2c: Subcube lies outside cube" ); p2c( offset, bufs[tno].axlen, bufs[tno].cubesize, naxes, coo ); dim = dim_sub+1; while( dim<=naxes ) { coords[dim-dim_sub-1] = coo[dim] + imgs[tno].blc[axnum[tno][dim]] + 1; dim++; } if(otest) { printf( "\nsubcubenr %ld starts at vircube coords:", subcubenr ); for( dim=1; dim<=naxes; dim++ ) printf(" %d",coo[dim]); printf( "; orig. cube coords:" ); for( dim=0; dim= bufs[tno].cubesize[naxes] ) bug_c( 'f', "xyzc2s: Coordinates lie outside cube" ); *subcubenr = offset / bufs[tno].cubesize[dim_sub]; if(itest) { printf( "\ncoords" ); for( dim=1; dim<=naxes; dim++ ) printf(" %d",coo[dim]); printf( " are for subcubenr %ld:", *subcubenr ); printf( "; orig. cube coords:" ); for( dim=0; dim bufs[tno].fillas ) { MODE=GET; manage_buffer( tno, virpix_off ); } *data = *( buffer + bufs[tno].bufstart + virpix_off ); *mask = *( mbuffr + bufs[tno].bufstart + virpix_off ); #ifdef XYZ_DEBUG if(otest) testprint( tno, virpix_off, virpix_off ); #endif } /******************************************************************************/ /** xyzprfrd - Get a profile from a dataset */ /*& bpw */ /*: image-i/o */ /*+ subroutine xyzprfrd( tno, profilenr, profile, mask, ndata ) integer tno integer profilenr real profile(*) logical mask(*) integer ndata This routine provides a (little) faster version for calls to xyzs2c and xyzread for the case that the calling program needs profiles. It should be used after a call to xyzsetup was used to set up one-dimensional subcubes. The calling program can then loop over all profiles (from 1 to vircubesize(naxis)/vircubesize(1)). xyzprfrd takes care of reading the datacube. Using this routine instead of xyzs2c and xyzread reduces the overhead by 10% (for 256-long profiles) to 30% (for 64-long profiles). Input: tno image file handle profilenr profile nr to be read from virtual cube Output: profile will contain the profile mask FALSE values indicate undefined pixels ndata number of elements read */ /*--*/ void xyzprfrd_c(int tno, int profilenr, float *data, int *mask, int *ndata ) { long virpix_off; #ifdef XYZ_DEBUG if(otest) xyzs2c_c( tno, profilenr-1, tcoo ); #endif virpix_off = (profilenr-1) * bufs[tno].cubesize[1]; MODE=GET; get_put_data( tno, virpix_off, data, mask, ndata, 1 ); } /******************************************************************************/ /** xyzplnrd - Get a plane from a dataset */ /*& bpw */ /*: image-i/o */ /*+ subroutine xyzplnrd( tno, planenr, plane, mask, ndata ) integer tno integer planenr real plane(*) logical mask(*) integer ndata This routine provides a more convenient version of calls to xyzs2c and xyzread for the case that the calling program needs planes. It should be used after a call to xyzsetup was used to set up two-dimensional subcubes. The calling program can then loop over all planes (from 1 to vircubesize(naxis)/vircubesize(2)). xyzplnrd takes care of reading the datacube. The caveat is that the calling program should have an array that is large enough to contain the complete plane. Using this routine instead of xyzs2c and xyzread reduces the overhead by 1% (for 64**2 cubes) or less. Input: tno image file handle planenr plane nr to be read from virtual-cube Output: plane will contain the plane as a 1-d array mask FALSE values indicate undefined pixels ndata number of elements read */ /*--*/ void xyzplnrd_c(int tno, int planenr, float *data, int *mask, int *ndata) { long virpix_off; #ifdef XYZ_DEBUG if(otest) xyzs2c_c( tno, planenr-1, tcoo ); #endif virpix_off = (planenr-1) * bufs[tno].cubesize[2]; MODE=GET; get_put_data( tno, virpix_off, data, mask, ndata, 2 ); } /******************************************************************************/ /** xyzwrite - Write arbitrary subcube */ /*& bpw */ /*: image-i/o */ /*+ subroutine xyzwrite( tno, coords, data, mask, ndata ) integer tno integer coords(*) real data(*) logical mask(*) integer ndata This routine, xyzsetup and xyzs2c work together to allow writing an arbitrary subcube in a n-dimensional datacube. xyzwrite writes a subcube to the datacube, as defined by xyzsetup at coordinates calculated by xyzs2c. The array coords gives the coordinates of the axes complementary to the subcube axes. E.g., if 'subcube' in the call to xyzsetup was 'y' and the datacube is 3-dimensional, coords(1) and coords(2) give the 'x' and 'z' coordinate of the requested line profile, respectively. Or, if 'subcube' was 'xz', coords(1) gives the 'y'-coordinate of the plane. For a datacube of dimension 'd', only the first 'd - dimsub' elements of the array coords will be used (where 'dimsub' is the dimension of the subcube). The array data (of dimension ndata) holds the information to be written. If the subcube was 0-dimensional, the first element of data is written. For a 1-d subcube a profile is written. The first vircubesize(1) (as returned by xyzsetup) elements of data are used. For a 2-d subcube the array data gives the requested plane, as a 1-d array of length vircubesize(2). Etc. The mask array indicates if pixels in the data array must be set to "undefined". A TRUE value means the data is OK, FALSE means it is undefined. Element 1 corresponds to data 1, etc. (this is not yet implemented, so ignored) N.B.: to write a datacube pixel by pixel it is more efficient to use subroutine xyzpixwr instead of xyzwrite, as the conversion from offset to coordinates to offset that xyzs2c and xyzwrite do then is superfluous and time-consuming. Input: tno image file handle coords array of which the first (dim cube)-(dim subcube) elements are used, giving the coordinate values along the complementary axes data array containing data to be written mask FALSE values indicate undefined pixel ndata number of elements to write */ /*--*/ void xyzwrite_c(int tno, Const int *coords, Const float *data, Const int *mask, Const int *ndata ) { /* The calculation first needs the pixeloffset of the input coordinate. For the varying axes the pixelnumber is 0, for the fixed axes of the subcube, the pixelnumber is the input minus the lower left offset. The input array had the first fixed coordinate as element 0, so a shift of -1 is necessary. After finding the pixelnumber in the virtual cube get_put_data is used. */ long virpix_off; int dim_sub, naxes; dim_sub = dimsub[tno]; naxes = bufs[tno].naxis; virpix_off = 0; dim = dim_sub+1; while( dim <= naxes ) { virpix_off += bufs[tno].cubesize[dim-1] * ( coords[dim-dim_sub-1]-1 - imgs[tno].blc[ axnum[tno][dim] ] ); dim++; } MODE=PUT; get_put_data( tno, virpix_off, (float *)data, (int *)mask, (int *)ndata, dim_sub ); } /******************************************************************************/ /** xyzpixwr - Write a pixel to a dataset */ /*& bpw */ /*: image-i/o */ /*+ subroutine xyzpixwr( tno, pixelnr, value, mask ) integer tno integer pixelnr real value logical mask This routine provides a faster version of calls to xyzs2c and xyzwrite for the case that the calling program provides single pixels. It should be used after a call to xyzsetup was used to set up zero-dimensional subcubes. The calling program can then loop over all pixels (from 1 to vircubesize(naxis)). xyzpixwr takes care of writing the datacube. Using this routine instead of xyzs2c and xyzwrite reduces the overhead by more than a factor 10. Input: tno image file handle pixelnr pixelnr to be read from virtual cube value pixel value mask FALSE indicates pixel is undefined */ /*--*/ void xyzpixwr_c(int tno, long pixelnr, Const float *data, Const int *mask ) { long virpix_off; #ifdef XYZ_DEBUG if(otest) xyzs2c_c( tno, pixelnr-1, tcoo ); #endif virpix_off = pixelnr - 1; if( virpix_off < bufs[tno].filfir || virpix_off > bufs[tno].fillas ) { MODE=PUT; manage_buffer( tno, virpix_off ); } *( buffer + bufs[tno].bufstart + virpix_off ) = *data; *( mbuffr + bufs[tno].bufstart + virpix_off ) = *mask; written[tno] = TRUE; #ifdef XYZ_DEBUG if(otest) testprint( tno, virpix_off, virpix_off ); #endif } /******************************************************************************/ /** xyzprfwr - Write a profile to a dataset */ /*& bpw */ /*: image-i/o */ /*+ subroutine xyzprfwr( tno, profilenr, profile, mask, ndata ) integer tno integer profilenr real profile(*) logical mask(*) integer ndata This routine provides a (little) faster version for calls to xyzs2c and xyzwrite for the case that the calling program provides profiles. It should be used after a call to xyzsetup was used to set up 1-dimensional subcubes. The calling program can then loop over all profiles (from 1 to vircubesize(naxis)/vircubesize(1)). xyzprfwr takes care of writing the datacube. Using this routine instead of xyzs2c and xyzwrite reduces the overhead by 10% (for 256-long profiles) to 30% (for 64-long profiles). Input: tno image file handle profilenr profile nr to be read from virtual cube profile contains the profile to be written mask FALSE values indicate undefined pixels ndata number of elements to write */ /*--*/ void xyzprfwr_c(int tno, int profilenr, Const float *data, Const int *mask, Const int *ndata ) { long virpix_off; #ifdef XYZ_DEBUG if(otest) xyzs2c_c( tno, profilenr-1, tcoo ); #endif virpix_off = (profilenr-1) * bufs[tno].cubesize[1]; MODE=PUT; get_put_data( tno, virpix_off, (float *)data, (int *)mask, (int *)ndata, 1 ); written[tno] = TRUE; } /******************************************************************************/ /** xyzplnwr - Write a plane to a dataset */ /*& bpw */ /*: image-i/o */ /*+ subroutine xyzplnwr( tno, planenr, plane, mask, ndata ) integer tno integer planenr real plane(*) logical mask(*) integer ndata This routine provides a more convenient version of calls to xyzs2c and xyzwrite for the case that the calling program provides planes. It should be used after a call to xyzsetup was used to set up 2-dimensional subcubes. The calling program can then loop over all planes (from 1 to vircubesize(naxis)/vircubesize(2)). xyzplnwr takes care of writing the datacube. The caveat is that the calling program should have an array that is large enough to contain the complete plane. Using this routine instead of xyzs2c and xyzwrite reduces the overhead by 1% (for 64**2 cubes) or less. Input: tno image file handle planenr plane nr to be read from virtual-cube plane contains the plane to be written as a 1-d array mask FALSE values indicate undefined pixels ndata number of elements to write */ /*--*/ void xyzplnwr_c(int tno, int planenr, Const float *data, Const int *mask, Const int *ndata ) { long virpix_off; #ifdef XYZ_DEBUG if(otest) xyzs2c_c( tno, planenr-1, tcoo ); #endif virpix_off = (planenr-1) * bufs[tno].cubesize[2]; MODE=PUT; get_put_data( tno, virpix_off, (float *)data, (int *)mask, (int *)ndata, 2 ); written[tno] = TRUE; } /******************************************************************************/ /* */ /* The routine that figures out if i-o must be done */ /* */ /******************************************************************************/ static void get_put_data( int tno, long virpix_off, float *data, int *mask, int *ndata, int dim_sub ) { /* This checks if the needed subcube is in the buffer. If so, a piece of the buffer is copied. If not, manage_buffer is called to fill or empty the buffer and then the copy is done. */ long virpix_lst; float *bufptr, *bufend, *bufsta; int i, coo[ARRSIZ], next; virpix_lst = virpix_off + bufs[tno].cubesize[dim_sub] - 1; if( MODE==GET ) *ndata = bufs[tno].cubesize[dim_sub]; if( MODE==PUT && *ndata < bufs[tno].cubesize[dim_sub] ) { bug_c( 'f', "xyzio: Input array too small to hold subcube" ); } if( virpix_off < bufs[tno].filfir || virpix_lst > bufs[tno].fillas ) { if(itest)printf("\nNew buffer starts at %ld MODE %d\n",virpix_off,MODE); if( virpix_off >= bufs[tno].cubesize[bufs[tno].naxis] ) bug_c( 'f', "xyzio: Caller tries to access pixel outside datacube"); if( dimsub[tno] == -1 ) bug_c( 'f', "xyzio: xyzsetup was never called for dataset" ); manage_buffer( tno, virpix_off ); } /* Plain copy */ if( !reverse[tno][0] ) { bufptr = buffer + bufs[tno].bufstart + virpix_off; bufend = buffer + bufs[tno].bufstart + virpix_lst; do_copy( bufptr, bufend, UP, data, mask ); /* Reverse copy */ } else if( reverse[tno][0] == ALL ) { bufptr = buffer + bufs[tno].bufstart + virpix_lst; bufend = buffer + bufs[tno].bufstart + virpix_off; do_copy( bufptr, bufend, DOWN, data, mask ); /* Some axes reversed */ } else { copy_to_one_d( tno ); /* Apply a trick to avoid a very strange error on the Cray */ /* bufsta = buffer + bufs[tno].bufstart + virpix_off; */ i = bufs[tno].bufstart + virpix_off; for( d=1; d<=dim_sub; d++ ) { if( !reverses[d] ) coo[d] = 0; else coo[d] = bufsaxlen[d] - 1; /* bufsta += coo[d] * bufscubesize[d-1]; } */ i += coo[d] * bufscubesize[d-1]; } bufsta = buffer + i; for( i=1; i<=bufscubesize[dim_sub]/bufscubesize[1]; i++ ) { if( !reverses[1] ) { bufptr = bufsta; bufend = bufsta + bufsaxlen[1] - 1; do_copy( bufptr, bufend, UP, data, mask ); } else { bufptr = bufsta; bufend = bufsta - bufsaxlen[1] + 1; do_copy( bufptr, bufend, DOWN, data, mask ); } data += bufsaxlen[1]; mask += bufsaxlen[1]; next=TRUE; d=2; while( d<=dim_sub && next ) { if( !reverses[d] ) { coo[d]++; bufsta += bufscubesize[d-1]; next = ( coo[d] == bufsaxlen[d] ); if(next) {coo[d]=0; bufsta -= bufscubesize[d];} } else { coo[d]--; bufsta -= bufscubesize[d-1]; next = ( coo[d] == -1 ); if(next) {coo[d]=bufsaxlen[d]-1; bufsta += bufscubesize[d];} } } } } #ifdef XYZ_DEBUG if(otest) testprint( tno, virpix_off, virpix_lst ); #endif } /******************************************************************************/ static void do_copy( float *bufptr, float *bufend, int DIR, float *data, int *mask ) { int *mbufpt; mbufpt = mbuffr + (int)(bufptr-buffer); if( DIR == UP ) { if( MODE==GET ) { while( bufptr<=bufend ) { *data++ = *bufptr++; *mask++ = *mbufpt++; }} if( MODE==PUT ) { while( bufptr<=bufend ) { *bufptr++ = *data++; *mbufpt++ = *mask++; }} } else if( DIR == DOWN ) { if( MODE==GET ) { while( bufptr>=bufend ) { *data++ = *bufptr--; *mask++ = *mbufpt--; }} if( MODE==PUT ) { while( bufptr>=bufend ) { *bufptr-- = *data++; *mbufpt-- = *mask++; }} } } /******************************************************************************/ /* */ /* Buffer control, figures out how to call loop_buffer */ /* */ /******************************************************************************/ static void manage_buffer( int tno, long virpix_off ) { /* This controls the buffer. It tries to do the absolute minimum number of disk-i/o's, using the array buffer, whose total length is determined by get_buflen xyzsetup. The array divided into sections, each corresponding to a particular opened image dataset. The first section is used to collect data read in or to write. The size of the sections varies with the number of opened datasets and is xyziobuflen/(nopened+1). When elements are copied from or to the buffer, they come form or go into the appropriate section. If a request is made for a pixel outside the section, manage_buffer is called and the parameters of the section will change. manage_buffer takes care that all elements of the image section are read/written. Disk-i/o uses the first section. After reading into it, each pixel is checked and if it is in range it is copied to the appropriate element in the section corresponding to the image. The reading and checking is repeated until the whole section is filled. For writing, all pixels in the first section are checked and the ones that are the range of the section for the image, are copied to the first section. This is continued until all elements in the image-section have been written. A special case occurs when the subcube specification was such that no transposition or region was given. Then the read/write is done directly to the image section, and the loops are skipped. There is one extra stage for the case where reading and writing is done to the same dataset. If a new read is done, the old buffer is first flushed, if it was ever written into. copy_to_one_d makes 1-d arrays of some arrays, to reduce the number of pointer calculations and to improve code-readability. For reading data, some buffer parameters are obtained first, then the buffer is filled. For writing, the current buffer is first flushed and then the buffer parameters are set up for the next buffer. set_bufs_limits figures out the virtual-cube pixeloffsets of the first and last element in the buffer and the range in x, y, z etc in the virtual-cube buffer and the input/output cube. This allows shortcuts to be taken. Further it defines bufs[tno].bufstart, which gives the first element in the buffer corresponding to this image. Before leaving manage_buffer this is changed into a number that can be used to convert a virpix_off to a bufferelement. So, inside this routine bufs[tno].bufstart points to the buffer, outside it points to the buffer index of the first element of the virtual-cube. For output writing, on the very first pass the buffer was still empty, not full, so all that is done is to initialize it. Only at the second and all later passes is it written to disk. After all this the first and last pixel of the virtual cube are converted to the corresponding offsets in the input cube. All required pixels lie within that range. Then a loop is done over all pixels in the input/output buffer and elements of the virtual cube are copied. This is done in stages, as the full range of input/output pixels may be larger than the size of the buffer. So, in each stage a range from start to last is searched, until the finish is reached. Sometimes it is not necessary to really do the i/o, so then it is skipped. */ if( MODE==GET && written[tno] ) { if(itest) printf("Flush previous output buffer\n"); MODE=PUT; manage_the_buffer( tno, -1 ); MODE=GET; if(itest) printf("Set up new input buffer\n"); } manage_the_buffer( tno, virpix_off ); } static void manage_the_buffer( int tno, long virpix_off ) { long start, finis, newstart, last; if( allocatebuffer ) get_buflen(); copy_to_one_d( tno ); if( imgs[tno].lastwritten == -1 ) zero( 1, tno ); if( MODE==GET ) { set_bufs_limits( tno, virpix_off ); written[tno] = FALSE; } if( MODE==PUT ) { if( bufs[tno].filfir == -1 ) { set_bufs_limits( tno, virpix_off ); bufs[tno].bufstart = - bufs[tno].filfir + bufs[tno].bufstart; return; } bufs[tno].bufstart = bufs[tno].bufstart + bufs[tno].filfir; if(otest) printf("\n"); } start = transform_back( bufs[tno].filfir ); finis = transform_back( bufs[tno].fillas ); if(itest) printf( "%s %ld values: from %ld to %ld\n", words[MODE], finis-start+1, start, finis ); if(itest||rtest){nfound=0;if(imgs[tno].nocopy)nfound=finis-start+1;} while( start <= finis ) { last = get_last( start, finis ); if( check_do_io( tno, start, last ) ) { if( MODE==GET ) { fill_buffer( tno, start, last ); loop_buffer( tno, start, last, &newstart ); } if( MODE==PUT ) { loop_buffer( tno, start, last, &newstart ); empty_buffer( tno, start, last ); } } else { if(itest) printf( "Did not %s %ld values: from %ld to %ld\n", words[MODE], last-start+1, start, last ); } start = newstart; } if(itest) printf( "virbuffer %s\n", words[MODE+2] ); if( MODE==PUT ) set_bufs_limits( tno, virpix_off ); bufs[tno].bufstart = - bufs[tno].filfir + bufs[tno].bufstart; } /******************************************************************************/ /* */ /* Find the length of a buffer that fits in memory */ /* */ /******************************************************************************/ static void get_buflen(void) { int tno; long try, maxsize, size, cnt; int *mbufpt; if(itest)printf("# bytes per real: %ld\n",sizeof(float)); maxsize = 0; for( tno=0; tnocurrentallocation) ) try = bufferallocation(try); allocatebuffer = FALSE; buffersize = try / (ntno+1); for( tno=0; tno buffersize ) { bugv_c( 'i', "xyzsetup: tno=%d itno=%d dimsub[tno]=%d",tno,imgs[tno].itno,dimsub[tno]); bugv_c( 'f', "xyzsetup: Requested subcube too big for buffer (%ld > %ld)", bufs[tno].cubesize[dimsub[tno]] ,buffersize); } } } /* set combined masking buffer to true, just in case no real mask is read in */ mbufpt = mbuffr; cnt=0; while( cnt++ < try ) *mbufpt++ = FORT_TRUE; } static long bufferallocation( long n ) { long n0 = n; #if 0 long maxbuf = MAXBUF; #else long maxbuf = n; #endif if (n > MAXBUF) bugv_c( 'i',"xyzsetup: Trying to allocate %ld pixels but MAXBUF=%d", n,MAXBUF); if(itest)printf("Trying to allocate %ld (maxbuf=%ld MAXBUF=%d)\n",n,maxbuf,MAXBUF); if( buffer != NULL ) { free( buffer ); buffer = NULL; } if( mbuffr != NULL ) { free( mbuffr ); mbuffr = NULL; } n = ( (n < maxbuf) ? n : maxbuf ); n *= 2; while( ( (buffer == NULL) || (mbuffr == NULL) ) && (n>1) ) { if( buffer != NULL ) { free( buffer ); buffer = NULL; } if( mbuffr != NULL ) { free( mbuffr ); mbuffr = NULL; } n /= 2; if(itest)printf("try %ld\n",n); buffer = (float *)malloc(n*sizeof(float)); mbuffr = (int *)malloc(n*sizeof(int)); } if( n == 1 ) bugv_c( 'f', "xyzsetup: Failed to allocate memory for %ld pixels", n0 ); if(itest)printf("Allocated %ld reals @ %p\n",n,(Void *)buffer); if(itest)printf("Allocated %ld ints @ %p\n",n,(Void *)mbuffr); currentallocation = n; return n; } /******************************************************************************/ static void copy_to_one_d( int tno ) { /* All this does is make one-d arrays of some 2-d arrays, so that the number of pointer calculations is reduced. And also it makes the routines below manage_buffer more readable. */ naxes = bufs[tno].naxis; for( d=0; d<=naxes; d++ ) { imgsaxlen[d] =imgs[tno].axlen[d]; bufsaxlen[d] =bufs[tno].axlen[d]; imgscubesize[d]=imgs[tno].cubesize[d];bufscubesize[d]=bufs[tno].cubesize[d]; imgsblc[d] =imgs[tno].blc[d]; bufsblc[d] =0; imgstrc[d] =imgs[tno].trc[d]; bufstrc[d] =bufs[tno].axlen[d]-1; imgscsz[d] =imgscubesize[d-1]; bufscsz[d] =bufscubesize[d-1]; imgslower[d] =imgs[tno].lower[d]; imgsupper[d] =imgs[tno].upper[d]; axnumr[d] =axnum[tno][d]; reverses[d] =reverse[tno][d]; } for( d=1; d<=naxes; d++ ) inv_axnumr[ axnumr[d] ] = d; } static void set_bufs_limits( int tno, long virpix_off ) { /* This gets some information about the virtual-cube buffer and the ranges of coordinates. First it figures out which range of pixeloffsets from the virtual cube fits in the buffer (from bufs[tno].filfir to bufs[tno].fillas). bufs[tno].fillas is found by figuring out what the pixeloffset of the last complete subcube was. It is limited by the size of the virtual cube. It also finds a pointer to the element in the buffer that will correspond to the first element of the virtual cube that is present (bufs[tno].bufstart). Next it finds the lower and upper limits that will ever be found for each coordinate: bufs.lower and bufs.upper. This is used later to limit the number of transformations by skipping over ranges where no buffer pixels will be found. Its main use is to be able to take shortcuts, to reduce the overhead. */ if( virpix_off == -1 ) return; bufs[tno].filfir = virpix_off; bufs[tno].bufstart = imgs[tno].number*buffersize; bufs[tno].fillas = (long)( (bufs[tno].filfir+buffersize) / bufscubesize[dimsub[tno]] ) * bufscubesize[dimsub[tno]] - 1; if( bufs[tno].fillas > bufscubesize[ naxes ] - 1 ) bufs[tno].fillas = bufscubesize[ naxes ] - 1; find_block( bufs[tno].filfir, bufs[tno].fillas, bufs[tno].lower, bufs[tno].upper, bufsaxlen, bufscubesize, bufsblc, bufstrc, naxes ); for( dim=1; dim<=naxes; dim++ ) { imgs[tno].lower[axnumr[dim]]=bufs[tno].lower[dim]+imgsblc[axnumr[dim]]; imgs[tno].upper[axnumr[dim]]=bufs[tno].upper[dim]+imgsblc[axnumr[dim]]; } for( dim=1; dim<=naxes; dim++ ) { imgslower[dim] = imgs[tno].lower[dim]; imgsupper[dim] = imgs[tno].upper[dim]; } if(itest) { printf( "fill %s buffer; will be full after %ld pixels\n", words[MODE], bufs[tno].fillas - bufs[tno].filfir + 1 ); limprint( "vircub", bufs[tno].lower, bufs[tno].upper ); } } static long get_last( long start, long finis ) { /* This routine figures out how many elements will fit into the buffer: the lower of the amount needed and the size of the buffer. It returns the fileoffset of the last element that fits. */ long allocate; if( finis-start+1 > buffersize ) { allocate = buffersize; } else { allocate = finis-start+1; } return( start + allocate - 1 ); } static int check_do_io( int tno, long start, long last ) { /* This routine checks if it is really necessary to read or write data from/to disk. It calculates the lowest and highest coordinate value that will ever be encountered. A comparison is done with the lowest and highest that might go into the buffer. If at least part of "the subcube selected from the inputcube" and "the subcube from the virtual cube that will fit into the buffer" overlap, a disk-i/o is required, as there will be at least one element of the virtual-cube-buffer read or written. This mainly comes into play when the buffer is smaller than an image plane and z-profiles must be read/written. */ int imgslow[ARRSIZ], imgsupp[ARRSIZ]; int do_io; find_block( start, last, imgslow, imgsupp, imgsaxlen, imgscubesize, imgsblc, imgstrc, naxes ); do_io = FALSE; for( dim=1; dim<=naxes && !do_io; dim++ ) { do_io = ( bufs[tno].lower[ dim ] <= imgsupp[ axnumr[dim] ] ) || ( bufs[tno].upper[ dim ] >= imgslow[ axnumr[dim] ] ); } if(itest) limprint( "i-ocub", imgslow, imgsupp ); return do_io; } static void find_block( long start, long last, int *lower, int *upper, int *axlen, long *cubesize, int *blc, int *trc, int naxis ) { /* Figures out from the first and last pixeloffset what the lowest and highest coordinate value are that could possibly be encountered. To do this it calculates the first coordinate value in the 'plane' (e.g. how many lines fit into z*(#lines/plane) and subtracts this from the non-modulo calculated coordinate value of the last pixeloffset. Then it checks if the next 'plane' was reached. If not, the coordinate limits are determined by the coordinate values themselves, else they are the lower/upper ends of the ranges. */ int bot, top; int strcoo[ARRSIZ], fincoo[ARRSIZ]; p2c( start, axlen, cubesize, naxis, strcoo ); p2c( last, axlen, cubesize, naxis, fincoo ); for( dim=1; dim<=naxis; dim++ ) { bot = (int)( start / cubesize[dim] ) * axlen[dim]; top = (int)( last / cubesize[dim-1] ) - bot; ( ( top > trc[dim] ) ? ( lower[dim] = blc[dim] ) : ( lower[dim] = strcoo[dim] ) ); ( ( top >= trc[dim] ) ? ( upper[dim] = trc[dim] ) : ( upper[dim] = fincoo[dim] ) ); } } static long transform_back( long pix_off ) { /* Transforms an virtual-cube pixeloffset into an input pixeloffset. */ int inpcoo, vircoo, axnr; long result; result = 0; for( dim=1; dim<=naxes; dim++ ) { axnr = axnumr[dim]; vircoo = ( pix_off / bufscubesize[ dim-1 ] ) % bufsaxlen[ dim ]; inpcoo = vircoo + imgsblc[ axnr ]; result += imgscubesize[axnr-1] * inpcoo; } return ( result ); } static long c2p( int *coords, long *cubesize, int naxis ) { /* Converts a list of coordinates into a pixeloffset */ long pix_off; pix_off=0; for( d=1; d<=naxis; d++ ) pix_off += cubesize[d-1] * coords[d]; return ( pix_off ); } static void p2c( long pix_off, int *axlen, long *cubesize, int naxis, int *coords ) { /* Converts a pixeloffset into a list of coordinates */ for( d=1; d<=naxis; d++ ) coords[d] = ( pix_off/cubesize[d-1] ) % axlen[d]; } /******************************************************************************/ /* */ /* The routines that do the i-o */ /* */ /******************************************************************************/ static void fill_buffer( int tno, long start, long last ) { long length; long begin,i; int bufstart, *buf; int iostat; nio++; if(itest) printf( "Read %ld values: %ld to %ld\n", last-start+1, start, last ); if( !imgs[tno].nocopy ) bufstart=0; else bufstart=bufs[tno].bufstart; length = H_REAL_SIZE * ( last - start + 1 ); begin = H_REAL_SIZE * start + ITEM_HDR_SIZE; /* hgrab_c( imgs[tno].itno,(char *)(buffer+bufstart),begin,length,&iostat );*/ hreadr_c( imgs[tno].itno,(char *)(buffer+bufstart),begin,length,&iostat ); check(iostat); length = last - start + 1; begin = start; if( imgs[tno].mask ) { mkread_c( imgs[tno].mask,1,mbuffr+bufstart,begin,length,length ); } else { buf = mbuffr+bufstart; for (i=0; i= start ) { fill_buffer( tno, start, imgs[tno].lastwritten ); bufptr = buffer + imgs[tno].lastwritten - start + 1; mbufpt = mbuffr + imgs[tno].lastwritten - start + 1; } if(itest) printf("zero buffer 0\n"); while( bufptr <= bufend ) { *bufptr++ = 0; *mbufpt++ = FORT_TRUE; } } else { fill_buffer( tno, start, last ); } bufptr = buffer; mbufpt = mbuffr; } p2c( start, imgsaxlen, imgscubesize, naxes, coords ); bufoff = -bufs[tno].filfir + bufs[tno].bufstart; for( d=1; d<=naxes; d++ ) bufoff += bufscsz[inv_axnumr[d]] * ( coords[d] - imgsblc[d] ); to_in = ( MODE==GET ); while( bufptr <= bufend ) { if( coords[1] <= imgsupper[1] ) { #ifdef XYZ_DEBUG if(rtest)testsearch(1,coords,start+bufptr-buffer,bufoff-buffir); #endif if( buffir <= bufoff && bufoff <= buflas ) { if( to_in ) { *(buffer+bufoff) = *bufptr; *(mbuffr+bufoff) = *mbufpt; } else { *bufptr = *(buffer+bufoff); *mbufpt = *(mbuffr+bufoff); } #ifdef XYZ_DEBUG if(itest||rtest)nfound++; if(rtest) printf(" found element %d; value %f %d",bufoff,*bufptr,*mbufpt); #endif } #ifdef XYZ_DEBUG if(rtest) printf("\n"); #endif coords[1]++; bufoff += bufscsz[inv_axnumr[1]]; bufptr++; mbufpt++; } if( coords[1] > imgsupper[1] ) { #ifdef XYZ_DEBUG if(rtest) testsearch(0,coords,0,0); #endif coords[1] = imgslower[1]; d=2; while( d<=naxes ) { if( coords[d] == imgsupper[d] || coords[d] == imgstrc[d] ) { coords[d] = imgslower[d]; } else { coords[d]++; break; } d++; } if( d > naxes ) break; #ifdef XYZ_DEBUG if(rtest) testsearch(2,coords,0,0); #endif filoff = -start; bufoff = -bufs[tno].filfir + bufs[tno].bufstart; for( d=1; d<=naxes; d++ ) { filoff += imgscsz[d] * coords[d]; bufoff += bufscsz[inv_axnumr[d]] * ( coords[d] - imgsblc[d] ); } bufptr = buffer + filoff; mbufpt = mbuffr + filoff; } } if(itest||rtest) printf( "found %d elements\n", nfound ); *newstart = bufptr - buffer + start; } /******************************************************************************/ static void zero( int bl_tr, int tno ) { /* This initializes parts of an output datacube that were not accessed because the new dataset has a blc and trc inside the full cube. It is called with bl_tr==1 just before the put buffer is first set up, and with bl_tr==2 just before the close. */ long start, last, finis; float *bufptr, *bufend; int *mbufpt; if( bl_tr == 1 ) { start = 0; finis = c2p( imgsblc, imgscubesize, naxes ) - 1; finis = imgscubesize[naxes] - 1; } else if( bl_tr == 2 ) { start = c2p( imgstrc, imgscubesize, naxes ) + 1; finis = imgscubesize[naxes] - 1; } while( start <= finis ) { last = get_last( start, finis ); bufptr = buffer; bufend = buffer + last - start; mbufpt = mbuffr; if(itest) printf("zero part of buffer 0\n"); while( bufptr <= bufend ) { *bufptr++ = 0.; *mbufpt++ = FORT_FALSE; } empty_buffer( tno, start, last ); start = bufptr - buffer + start; } } /******************************************************************************/ /******************************************************************************/ static void testprint( int tno, long virpix_off, long virpix_lst ) { int vircoo[ARRSIZ]; long inpix_off; int naxes; naxes=imgs[tno].naxis; p2c( virpix_off, bufs[tno].axlen, bufs[tno].cubesize, naxes, vircoo ); for( dim=1; dim<=naxes; dim++ ) tcoo[dim] = vircoo[ inv_axnumr[dim] ] + imgs[tno].blc[dim]; inpix_off = c2p( tcoo, imgs[tno].cubesize, naxes ); printf( "coo: " ); for( dim=1; dim<=naxes; dim++) printf( "%4d ", tcoo[dim] ); printf( " offset: %10ld\n", inpix_off ); printf( "vircoo: " ); for( dim=1; dim<=naxes; dim++) printf( "%4d ", vircoo[dim] ); printf( " offset: %20ld\n", virpix_off ); if( virpix_off == virpix_lst ) { printf( "%s copied element %ld\n", words[MODE], virpix_off+bufs[tno].bufstart ); } else { printf( "%s copied %ld elements starting at %ld\n", words[MODE], virpix_lst-virpix_off+1, virpix_off+bufs[tno].bufstart ); } } static void limprint( char *string, int *lower, int *upper ) { printf( "%s:", string ); printf( " lower" ); for( d=1; d<=naxes; d++ ) printf( " %d", lower[d] ); printf( ": upper" ); for( d=1; d<=naxes; d++ ) printf( " %d", upper[d] ); printf( "\n"); } static void testsearch( int callnr, int *coords, long filoff, long viroff ) { if( callnr == 2 ) printf( " -> " ); for( d=1; d<=naxes; d++ ) printf("%d ", coords[d] ); if( callnr == 1 ) printf( " filoff %ld viroff %ld", filoff, viroff ); if( callnr == 2 ) printf( "\n" ); } /******************************************************************************/ /******************************************************************************/ /******************************************************************************/ /* Text for the userguide */ /* To read or write a MIRIAD dataset the following set of routines can be used. xyzopen( tno, name, status, naxis, axlen ) xyzclose( tno ) xyzsetup( tno, subcube, blc, trc, viraxlen, vircubesize ) xyzs2c( tno, subcubnr, coords ) xyzc2s( tno, coords, subcubenr ) xyzread( tno, coords, data, mask, dimdata ) xyzpixrd( tno, pixelnr, data, mask ) xyzprfrd( tno, profinr, data, mask, dimdata ) xyzplnrd( tno, planenr, data, mask, dimdata ) xyzwrite( tno, coords, data, mask, dimdata ) xyzpixwr( tno, pixelnr, data, mask ) xyzprfwr( tno, profinr, data, mask, dimdata ) xyzplnwr( tno, planenr, data, mask, dimdata ) xyzopen opens the dataset and readies it for reading/writing. 'name' is the name of the dataset. 'status' can be either "old" or "new", depending on whether an existing dataset is opened or a new one must be created. For old datasets naxis gives the dimension of array axlen on input and the dimension of the dataset on output. On output axlen contains the length of the axes. For new datasets naxis and axlen specify the number of axes and their length. xyzclose closes the dataset. The rest of the xyz routines can be used to read or write an arbitrary subcube in the dataset in a manner that minimizes disk-i/o. To do this, the datacube axes are named 'x', 'y', 'z', 'a', 'b', etc. 'x' may be RA or DEC or velocity or anything else, but it is the first axis. The xyzsetup subroutine is used to define a subcube in the dataset. There are many subcubes in a dataset. They have axes with "varying coordinates" and axes with "fixed coordinates". With n "varying coordinates" the subcube is n-dimensional, and its position in the original cube is given by the "fixed coordinates". The subcubes are also ordered, along the "fixed coordinates". E.g., for profiles in the 'z' direction, the first subcube has (x=1,y=1), the second has (x=2,y=1), on to (x=axlen(1),y=1) and then (x=1,y=2) etc, etc. For datasets that must be read, the 'subcube' variable of xyzsetup specifies which axes from the original cube have "varying coordinates"; e.g. 'z' for profiles the z-direction, or 'xy' for image planes. It is also allowed to transpose axes: e.g. 'zx' (which would usually correspond to making a vel-RA plane). To understand the meaning of 'subcube' for datasets that must be written a little explanation is in order: the xyz routines produce a "virtual cube", one that never actually is written on disk or resides in memory, but which is conceptually useful. In this virtual cube the axes are ordered such that the ones with "varying coordinates" become the 'x', 'y' etc axes, and the ones with "fixed coordinates" form the rest. So, if 'subcube' was 'z', a profile along the 'x'-axis of the virtual cube contains the datavalues on a profile along the 'z'-axis of the input cube. The 'y' and 'z' axes of the virtual cube were the 'x' and 'y' axes of the original cube, respectively. For writing a dataset, the 'subcube' variable gives the correspondence between the axes of the virtual cube and the output cube. E.g., if 'subcube' is 'z', this means that the first ('x') axis of the virtual cube is the 'z'-axis of the output cube, and the 'y' and 'z' axes of the virtual cube correspond to the 'x' and 'y' axes of the output cube, respectively. Preceding an axisname with a '-' results in mirror-imaging the input or output data for that axis. The blc and trc variables of xyzsetup give the bottom left and top right corner of the part of the image cube to be worked on. The first naxis elements of blc and trc are used. For reading, this is the region to be read, for writing it is the region to be written. In the latter case, if the output dataset did not yet exist and the region is smaller than the total cubesize given in xyzopen, the outside-region is automatically set to zero. The viraxlen and vircubesize variables of xyzsetup give some information about the virtual cube: the axis lengths and the 'cubesizes'. 'cubesize(1)' is the number of pixels in a profile, 'cubesize(2)' is the number of pixels in a plane, 'cubesize(3)' is the number of pixels in a cube, etc. So, for a 3-d input cube, 'cubesize(3)' gives the total number of pixels to work on. The subroutine xyzs2c can be used to obtain the values of the "fixed coordinates" for a given subcube number. The first element of the array coords then corresponds to the first "fixed coordinate" value, etc. E.g., for profiles in the 'z'-direction, coords(1) is the 'x'-position, coords(2) the 'y'-position. Subroutine xyzc2s does the inverse operation. xyzread, xyzpixrd, xyzprfrd and xyzplnrd do the actual reading of data. xyzread takes as input the "fixed coordinate" values and returns the subcube in the 1-dimensional array data. The other 3 routines read a single pixel, a single profile and a single plane, respectively. In each case the array data (whose dimension is transferred to the subroutines in the variable dimdata) should be large enough to hold the entire requested subcube. The logical array mask is used to indicate if datapixels were undefined (this is not yet implemented). mask=TRUE means the pixel is OK; FALSE means it is undefined. The write routine works in the same manner. If the program wants to loop over pixels or profiles, use of xyzs2c and xyzread becomes less efficient than use of xyzpixrd or xyzprfrd. In fact, for looping over pixels, the xyzs2c-xyzread combination is about 10 times less efficient than xyzpixrd. This is because with xyzs2c and xyzread the pixelnumber is first converted to a coordinate with xyzs2c and then converted back to a pixelnr in xyzread, while xyzpixrd avoids this overhead. A typical call sequence using the xyz routines to work on profiles in the z-direction would be: call xyzopen( tno1, name1, 'old', naxis, axlen ) call xyzopen( tno2, name2, 'new', naxis, axlen ) call headcopy( tno1, tno2, axnum, naxis ) ! axnum(i)=i call boxinput( 'region', name, boxes, maxboxes ) call boxinfo( boxes, naxis, blc, trc ) call xyzsetup( tno1, 'z', blc, trc, viraxlen, vircubesize ) call xyzsetup( tno2, 'z', blc, trc, viraxlen, vircubesize ) nprofiles = = vircubesize(naxis) / viraxlen(1) do profile = 1, nprofiles call xyzprfrd( tno1, profile, data, mask, dimdata ) call work_on_profile( data, mask, dimdata ) call xyzprfwr( tno2, profile, data, mask, dimdata ) enddo A warning is in order: each call to xyzsetup causes all internal buffers to be lost completely, so xyzsetup should be called for all datasets before starting to work on them. Output buffers are flushed before the buffers are lost, however. The overhead introduced by the calculations done by the xyz routines is shown below. These were calculated using a testprogram that was complete but for actually doing something with the data and reading them from disk. The first number gives the times when using xyzpixrd, xyzprfrd and xyzplnrd, the second when using xyzs2c and xyzread. The overhead does not change with changing buffersize, but the number of disk-i/o's does. In a test using xyzprfrd and xyzprfwr on a 128^3 cube, with a 4Mb buffer, it took 120s to copy the input file using these routines, and 80s with a unix cp. With a 2Mb buffer the copy took 120s too, even though the number of i-o's increased from 12 to 22. buffer of 524288 (2Mb): 1/2th of 128^3 cube; 1/16th of 256^3 cube cubesize 32^3 64^3 128^3 256^3 pixels time(s) 0.3( 2.6) 1.6( 20.6) 12.4(170.2) 98.2(1396.6) n_i/o 1 2 13 97 x profiles time(s) 0.1( 0.2) 0.6( 0.9) 4.2( 5.2) 31.2( 35.5) n_i/o 1 1 2 16 y profiles time(s) 0.4( 0.4) 2.2( 2.5) 16.8( 17.9) 129.8(134.0) n_i/o 1 1 2 16 z profiles time(s) 0.4( 0.4) 2.4( 2.5) 16.7( 17.7) 129.5(133.7) n_i/o 1 1 4 256 xy planes time(s) 0.2( 0.1) 0.6( 0.5) 3.9( 4.0) 30.1( 30.1) n_i/o 1 1 2 16 yx planes time(s) 0.4( 0.4) 2.2( 2.2) 16.4( 16.5) 128.6(128.7) n_i/o 1 1 2 16 xz planes time(s) 0.3( 0.3) 2.2( 2.1) 16.4( 16.4) 128.4(128.4) n_i/o 1 1 4 256 zx planes time(s) 0.3( 0.4) 2.2( 2.2) 16.5( 16.4) 128.2(128.3) n_i/o 1 1 4 256 yz planes time(s) 0.4( 0.3) 2.1( 2.2) 16.9( 16.8) 157.4(157.4) n_i/o 1 1 4 256 zy planes time(s) 0.4( 0.4) 2.2( 2.1) 16.9( 16.8) 157.4(157.3) n_i/o 1 1 4 256 cubesize 128*128*112 256*256*64 z profiles time(s) 15.1 34.5 n_i/o 8 32 */ /* Number of operations per call to xyz routines, 3-d cube: xyzs2c: 82 xyzr/w: 82+3n xyzpix: 15 xyzprf: 36+3n xyzpln: 36+3n pix/prf/pln xyzs2c & read ratio pixels (15)N^3 (164)N^3 15/164 profiles (36+3N)N^2 (164+3N)N^2 (36+3N)/(164+3N) planes (36+3N^2)N (164+3N^2)N (36+3N^2)/(164+3N^2) 32^3 64^3 128^3 256^3 512^3 pixels 0.091 profiles 0.508 0.640 0.766 0.863 0.925 planes 0.960 0.990 0.997 0.999 1.000 */ /******************************************************************************/ /******************************************************************************/ /******************************************************************************/ casacore-3.7.1/ms/000077500000000000000000000000001476623553700137435ustar00rootroot00000000000000casacore-3.7.1/ms/CMakeLists.txt000066400000000000000000000204421476623553700165050ustar00rootroot00000000000000# # CASA MeasurementSets # set ( parser_inputs MSAntennaGram MSArrayGram MSCorrGram MSFeedGram MSFieldGram MSObservationGram MSScanGram MSSpwGram MSTimeGram MSUvDistGram MSStateGram ) foreach (src ${parser_inputs}) if (BISON_VERSION VERSION_LESS 3.0) BISON_TARGET (${src} MSSel/${src}.yy ${CMAKE_CURRENT_BINARY_DIR}/${src}.ycc COMPILE_FLAGS "-y -p ${src} --output-file=${src}.ycc") else() BISON_TARGET (${src} MSSel/${src}.yy ${CMAKE_CURRENT_BINARY_DIR}/${src}.ycc COMPILE_FLAGS "-y -Dapi.prefix={${src}} --output-file=${src}.ycc --warnings=no-yacc") endif() FLEX_TARGET (${src} MSSel/${src}.ll ${CMAKE_CURRENT_BINARY_DIR}/${src}.lcc COMPILE_FLAGS "-P${src}") endforeach (src) include_directories (${CMAKE_CURRENT_BINARY_DIR}) add_library (casa_ms MeasurementSets/MSFreqOffset.cc MeasurementSets/MSProcessorColumns.cc MeasurementSets/MSHistory.cc MeasurementSets/MSHistoryHandler.cc MeasurementSets/MSSource.cc MeasurementSets/MSFlagCmd.cc MeasurementSets/MSPolColumns.cc MeasurementSets/MSSysCal.cc MeasurementSets/MSState.cc MeasurementSets/MSTable2.cc MeasurementSets/MSWeatherColumns.cc MeasurementSets/MSDopplerUtil.cc MeasurementSets/MSPointingColumns.cc MeasurementSets/MSAntenna.cc MeasurementSets/MSWeather.cc MeasurementSets/MSTableImpl.cc MeasurementSets/MSDopplerColumns.cc MeasurementSets/MSFeedColumns.cc MeasurementSets/MSField.cc MeasurementSets/MSAntennaColumns.cc MeasurementSets/MSObsColumns.cc MeasurementSets/MSFeed.cc MeasurementSets/MSColumns.cc MeasurementSets/MSObservation.cc MeasurementSets/MSDataDescColumns.cc MeasurementSets/MSStateColumns.cc MeasurementSets/MSFlagCmdColumns.cc MeasurementSets/MeasurementSet.cc MeasurementSets/MSMainColumns.cc MeasurementSets/MSSourceColumns.cc MeasurementSets/MSProcessor.cc MeasurementSets/MSHistoryColumns.cc MeasurementSets/MSPointing.cc MeasurementSets/MSRange.cc MeasurementSets/MSSysCalColumns.cc MeasurementSets/MSDoppler.cc MeasurementSets/StokesConverter.cc MeasurementSets/MSDataDescription.cc MeasurementSets/MSTileLayout.cc MeasurementSets/MSFreqOffColumns.cc MeasurementSets/MSFieldColumns.cc MeasurementSets/MSSpectralWindow.cc MeasurementSets/MSPolarization.cc MeasurementSets/MSSpWindowColumns.cc MeasurementSets/MSIter.cc MeasurementSets/MSTable.cc MSSel/MSAntennaGram.cc MSSel/MSAntennaIndex.cc MSSel/MSAntennaParse.cc MSSel/MSArrayGram.cc MSSel/MSArrayParse.cc MSSel/MSCorrGram.cc MSSel/MSCorrParse.cc MSSel/MSDataDescIndex.cc MSSel/MSDopplerIndex.cc MSSel/MSFeedGram.cc MSSel/MSFeedIndex.cc MSSel/MSFeedParse.cc MSSel/MSFieldGram.cc MSSel/MSFieldIndex.cc MSSel/MSFieldParse.cc MSSel/MSFreqOffIndex.cc MSSel/MSObservationGram.cc MSSel/MSObservationParse.cc MSSel/MSObsIndex.cc MSSel/MSParse.cc MSSel/MSPointingIndex.cc MSSel/MSPolIndex.cc MSSel/MSPolnGram.cc MSSel/MSPolnParse.cc MSSel/MSScanGram.cc MSSel/MSScanParse.cc MSSel/MSSelectableTable.cc MSSel/MSSelection.cc MSSel/MSSelectionError.cc MSSel/MSSelectionErrorHandler.cc MSSel/MSSSpwErrorHandler.cc MSSel/MSSelectionKeywords.cc MSSel/MSSelectionTools.cc MSSel/MSSelector.cc MSSel/MSSourceIndex.cc MSSel/MSSpwGram.cc MSSel/MSSpwIndex.cc MSSel/MSSpWindowIndex.cc MSSel/MSSpwParse.cc MSSel/MSStateGram.cc MSSel/MSStateIndex.cc MSSel/MSStateParse.cc MSSel/MSSysCalIndex.cc MSSel/MSTableIndex.cc MSSel/MSTimeGram.cc MSSel/MSTimeParse.cc MSSel/MSUvDistGram.cc MSSel/MSUvDistParse.cc MSSel/MSWeatherIndex.cc MSOper/MS1ToMS2Converter.cc MSOper/MSConcat.cc MSOper/MSDerivedValues.cc MSOper/MSFlagger.cc MSOper/MSKeys.cc MSOper/MSLister.cc MSOper/MSMetaData.cc MSOper/MSReader.cc MSOper/MSSummary.cc MSOper/MSValidIds.cc MSOper/NewMSSimulator.cc ${BISON_MSAntennaGram_OUTPUTS} ${FLEX_MSAntennaGram_OUTPUTS} ${BISON_MSArrayGram_OUTPUTS} ${FLEX_MSArrayGram_OUTPUTS} ${BISON_MSCorrGram_OUTPUTS} ${FLEX_MSCorrGram_OUTPUTS} ${BISON_MSFeedGram_OUTPUTS} ${FLEX_MSFeedGram_OUTPUTS} ${BISON_MSFieldGram_OUTPUTS} ${FLEX_MSFieldGram_OUTPUTS} ${BISON_MSScanGram_OUTPUTS} ${FLEX_MSScanGram_OUTPUTS} ${BISON_MSObservationGram_OUTPUTS} ${FLEX_MSObservationGram_OUTPUTS} ${BISON_MSSpwGram_OUTPUTS} ${FLEX_MSSpwGram_OUTPUTS} ${BISON_MSTimeGram_OUTPUTS} ${FLEX_MSTimeGram_OUTPUTS} ${BISON_MSUvDistGram_OUTPUTS} ${FLEX_MSUvDistGram_OUTPUTS} ${BISON_MSStateGram_OUTPUTS} ${FLEX_MSStateGram_OUTPUTS} ) set(top_level_headers MeasurementSets.h MSSel.h MSOper.h ) init_pch_support(casa_ms ${top_level_headers}) target_link_libraries ( casa_ms casa_measures ${CASACORE_ARCH_LIBS} ) add_subdirectory (apps) install (TARGETS casa_ms RUNTIME DESTINATION bin LIBRARY DESTINATION lib${LIB_SUFFIX} ARCHIVE DESTINATION lib${LIB_SUFFIX} LIBRARY PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) install (FILES MeasurementSets/MSAntenna.h MeasurementSets/MSAntennaColumns.h MeasurementSets/MSAntennaEnums.h MeasurementSets/MSColumns.h MeasurementSets/MSDataDescColumns.h MeasurementSets/MSDataDescEnums.h MeasurementSets/MSDataDescription.h MeasurementSets/MSDoppler.h MeasurementSets/MSDopplerColumns.h MeasurementSets/MSDopplerEnums.h MeasurementSets/MSDopplerUtil.h MeasurementSets/MSFeed.h MeasurementSets/MSFeedColumns.h MeasurementSets/MSFeedEnums.h MeasurementSets/MSField.h MeasurementSets/MSFieldColumns.h MeasurementSets/MSFieldEnums.h MeasurementSets/MSFlagCmd.h MeasurementSets/MSFlagCmdColumns.h MeasurementSets/MSFlagCmdEnums.h MeasurementSets/MSFreqOffColumns.h MeasurementSets/MSFreqOffEnums.h MeasurementSets/MSFreqOffset.h MeasurementSets/MSHistory.h MeasurementSets/MSHistoryColumns.h MeasurementSets/MSHistoryEnums.h MeasurementSets/MSHistoryHandler.h MeasurementSets/MSIter.h MeasurementSets/MSMainColumns.h MeasurementSets/MSMainEnums.h MeasurementSets/MSObsColumns.h MeasurementSets/MSObsEnums.h MeasurementSets/MSObservation.h MeasurementSets/MSPointing.h MeasurementSets/MSPointingColumns.h MeasurementSets/MSPointingEnums.h MeasurementSets/MSPolColumns.h MeasurementSets/MSPolEnums.h MeasurementSets/MSPolarization.h MeasurementSets/MSProcessor.h MeasurementSets/MSProcessorColumns.h MeasurementSets/MSProcessorEnums.h MeasurementSets/MSRange.h MeasurementSets/MSSource.h MeasurementSets/MSSourceColumns.h MeasurementSets/MSSourceEnums.h MeasurementSets/MSSpWindowColumns.h MeasurementSets/MSSpWindowEnums.h MeasurementSets/MSSpectralWindow.h MeasurementSets/MSState.h MeasurementSets/MSStateColumns.h MeasurementSets/MSStateEnums.h MeasurementSets/MSSysCal.h MeasurementSets/MSSysCalColumns.h MeasurementSets/MSSysCalEnums.h MeasurementSets/MSTable.h MeasurementSets/MSTable.tcc MeasurementSets/MSTableImpl.h MeasurementSets/MSTileLayout.h MeasurementSets/MSWeather.h MeasurementSets/MSWeatherColumns.h MeasurementSets/MSWeatherEnums.h MeasurementSets/MeasurementSet.h MeasurementSets/StokesConverter.h DESTINATION include/casacore/ms/MeasurementSets ) install (FILES MSSel/MSAntennaGram.h MSSel/MSAntennaIndex.h MSSel/MSAntennaParse.h MSSel/MSArrayGram.h MSSel/MSArrayParse.h MSSel/MSCorrGram.h MSSel/MSCorrParse.h MSSel/MSDataDescIndex.h MSSel/MSDopplerIndex.h MSSel/MSFeedIndex.h MSSel/MSFieldGram.h MSSel/MSFieldIndex.h MSSel/MSFieldParse.h MSSel/MSFreqOffIndex.h MSSel/MSObservationGram.h MSSel/MSObservationParse.h MSSel/MSObsIndex.h MSSel/MSParse.h MSSel/MSPointingIndex.h MSSel/MSPolIndex.h MSSel/MSPolnGram.h MSSel/MSPolnParse.h MSSel/MSScanGram.h MSSel/MSScanParse.h MSSel/MSSelectableMainColumn.h MSSel/MSSelectableTable.h MSSel/MSSelection.h MSSel/MSSelectionError.h MSSel/MSSelectionErrorHandler.h MSSel/MSSSpwErrorHandler.h MSSel/MSSelectionKeywords.h MSSel/MSSelectionTools.h MSSel/MSSelector.h MSSel/MSSelUtil.h MSSel/MSSelUtil.tcc MSSel/MSSelUtil2.h MSSel/MSSelUtil2.tcc MSSel/MSSourceIndex.h MSSel/MSSpwGram.h MSSel/MSSpwIndex.h MSSel/MSSpWindowIndex.h MSSel/MSSpwParse.h MSSel/MSStateGram.h MSSel/MSStateIndex.h MSSel/MSStateParse.h MSSel/MSSysCalIndex.h MSSel/MSTableIndex.h MSSel/MSTimeDefinitions.h MSSel/MSTimeGram.h MSSel/MSTimeParse.h MSSel/MSUvDistGram.h MSSel/MSUvDistParse.h MSSel/MSWeatherIndex.h DESTINATION include/casacore/ms/MSSel ) install (FILES MSOper/MS1ToMS2Converter.h MSOper/MSConcat.h MSOper/MSDerivedValues.h MSOper/MSFlagger.h MSOper/MSKeys.h MSOper/MSLister.h MSOper/MSMetaData.h MSOper/MSReader.h MSOper/MSSummary.h MSOper/MSValidIds.h MSOper/NewMSSimulator.h DESTINATION include/casacore/ms/MSOper ) install (FILES ${top_level_headers} DESTINATION include/casacore/ms ) add_subdirectory (MeasurementSets/test ${EXCL_ALL}) add_subdirectory (MSSel/test ${EXCL_ALL}) add_subdirectory (MSOper/test ${EXCL_ALL}) casacore-3.7.1/ms/MSOper.h000066400000000000000000000034701476623553700152650ustar00rootroot00000000000000//# MSOper.h: Miscellaneous operations on MeasurementSets //# Copyright (C) 1996,1997 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSOPER_H #define MS_MSOPER_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // // // Miscellaneous operations on MeasurementSets // // //
      • MeasurementSets module // // // // // // // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSOper/000077500000000000000000000000001476623553700151105ustar00rootroot00000000000000casacore-3.7.1/ms/MSOper/MS1ToMS2Converter.cc000066400000000000000000000751671476623553700205540ustar00rootroot00000000000000//# MS1ToMS2Converter.cc: MS1 to MS2 converter //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MS1ToMS2Converter::MS1ToMS2Converter(const String& ms2, const String& ms1, Bool inPlace) : ms1_p (ms1), ms2_p (ms2), inPlace_p(inPlace) { LogOrigin OR("MS1ToMS2Converter", "MS1ToMS2Converter()", WHERE); os_p = LogIO(OR); if (inPlace_p) { ms2_p = ms1_p; } } MS1ToMS2Converter::~MS1ToMS2Converter() {} void MS1ToMS2Converter::removeColumn(Table& t, const String& col) { if (t.canRemoveColumn(col)) { // t.removeColumn(col); t.renameColumn("_OBSOLETE_"+col,col); } else { t.renameColumn("_OBSOLETE_"+col,col); } } Bool MS1ToMS2Converter::convert() { // Check that table needs to be converted, if so (deep)copy it if needed. { Table t(ms1_p); if (t.keywordSet().isDefined("MS_VERSION") && t.keywordSet().asFloat("MS_VERSION")>=2.0) { throw(AipsError("Input MS already in MS2 format")); } if (!inPlace_p) t.copy(ms2_p,Table::NewNoReplace); } // Fix the main table, rename columns, add columns, remove columns Table t; if (inPlace_p) { t = Table(ms1_p, Table::Update); } else { t = Table(ms2_p,Table::Update); } t.rwKeywordSet().define("MS_VERSION", Float(2.0)); t.renameColumn("DATA_DESC_ID","SPECTRAL_WINDOW_ID"); t.renameColumn("PROCESSOR_ID","CORRELATOR_ID"); if (t.keywordSet().isDefined("FLAG_HISTORY")) { t.renameColumn("FLAG_CATEGORY","FLAG_HISTORY"); TableColumn flagc(t, "FLAG_CATEGORY"); if (!flagc.keywordSet().isDefined("CATEGORY")) flagc.rwKeywordSet().define("CATEGORY", Vector()); } removeColumn(t,"PULSAR_ID"); t.rwKeywordSet().removeField("ARRAY"); if (t.keywordSet().isDefined("SORTED_TABLE")) t.rwKeywordSet().removeField("SORTED_TABLE"); if (t.keywordSet().isDefined("SORT_COLUMNS")) t.rwKeywordSet().removeField("SORT_COLUMNS"); TableDesc td; IncrementalStMan ism; MeasurementSet::addColumnToDesc(td,MS::PHASE_ID); MeasurementSet::addColumnToDesc(td,MS::STATE_ID); MeasurementSet::addColumnToDesc(td,MS::TIME_CENTROID); if (!(t.keywordSet().isDefined("FLAG_CATEGORY"))) MeasurementSet::addColumnToDesc(td,MS::FLAG_CATEGORY); t.addColumn(td,ism); TableColumn flagc(t, "FLAG_CATEGORY"); if (!flagc.keywordSet().isDefined("CATEGORY")) { flagc.rwKeywordSet().define("CATEGORY", Vector()); } ScalarColumn stateId(t,MS::columnName(MS::STATE_ID)); stateId.fillColumn(0); ScalarColumn time(t,MS::columnName(MS::TIME)); ScalarColumn timeCentroid(t,MS::columnName(MS::TIME_CENTROID)); timeCentroid.putColumn(time.getColumn()); ArrayColumn uvw(t,MS::columnName(MS::UVW)); TableDesc uvwtd; MeasurementSet::addColumnToDesc(uvwtd,MS::UVW); uvw.rwKeywordSet().assign(uvwtd[0].keywordSet()); ScalarColumn exp(t,MS::columnName(MS::EXPOSURE)); TableDesc exptd; MeasurementSet::addColumnToDesc(exptd,MS::EXPOSURE); exp.rwKeywordSet().assign(exptd[0].keywordSet()); ScalarColumn inter(t,MS::columnName(MS::INTERVAL)); TableDesc intertd; MeasurementSet::addColumnToDesc(intertd,MS::INTERVAL); inter.rwKeywordSet().assign(intertd[0].keywordSet()); TableDesc timetd; MeasurementSet::addColumnToDesc(timetd,MS::TIME); time.rwKeywordSet().assign(timetd[0].keywordSet()); Int maxAnt; // ANTENNA { Table anTab(ms2_p+"/ANTENNA",Table::Update); // Find out if we need to renumber antennas ScalarColumn antIdCol(anTab, "ANTENNA_ID"); Vector ant=antIdCol.getColumn(); Int nRow=ant.nelements(); maxAnt=max(ant)+1; Vector antMap(maxAnt); Bool renumber=False; for (Int i=0; i newAnt1(t.nrow()); Vector newAnt2(t.nrow()); ScalarColumn ant1Col(t,"ANTENNA1"); ScalarColumn ant2Col(t,"ANTENNA2"); for (uInt i=0; i ant(feedTab,"ANTENNA_ID"); for (uInt i=0; i ant(syscalTab,"ANTENNA_ID"); for (uInt i=0; i ant(wTab,"ANTENNA_ID"); for (uInt i=0; i type(anTab,"TYPE"); type.fillColumn("GROUND-BASED"); ScalarColumn flagRow(anTab,"FLAG_ROW"); flagRow.fillColumn(False); ArrayColumn pos(anTab,"POSITION"); TableDesc postd; MSAntenna::addColumnToDesc(postd,MSAntenna::POSITION); pos.rwKeywordSet().assign(postd[0].keywordSet()); ArrayColumn offs(anTab,"OFFSET"); TableDesc offstd; MSAntenna::addColumnToDesc(offstd,MSAntenna::OFFSET); offs.rwKeywordSet().assign(offstd[0].keywordSet()); ScalarColumn dish(anTab,"DISH_DIAMETER"); TableDesc dishtd; MSAntenna::addColumnToDesc(dishtd,MSAntenna::DISH_DIAMETER); dish.rwKeywordSet().assign(dishtd[0].keywordSet()); for (uInt j = MSAntenna::NUMBER_REQUIRED_COLUMNS + 1; j < MSAntenna::NUMBER_PREDEFINED_COLUMNS; j = j + 1) { MSAntenna::PredefinedColumns i = (MSAntenna::PredefinedColumns) j; if (anTab.tableDesc().isColumn(MSAntenna::columnName(i))) { TableColumn tbc(anTab, MSAntenna::columnName(i)); TableDesc td; MSAntenna::addColumnToDesc(td, i); tbc.rwKeywordSet().assign(td[0].keywordSet()); } } } // DATA_DESCRIPTION { // create and fill table and write it out { Int nRow = t.keywordSet().asTable("SPECTRAL_WINDOW").nrow(); SetupNewTable ddSetup(ms2_p+"/DATA_DESCRIPTION", MSDataDescription::requiredTableDesc(), Table::New); Table ddt(ddSetup,nRow); ScalarColumn spw(ddt,"SPECTRAL_WINDOW_ID"); ScalarColumn pol(ddt,"POLARIZATION_ID"); ScalarColumn flagRow(ddt,"FLAG_ROW"); Vector seq(nRow); for (Int i=0;i time(feedTab,"TIME"); TableDesc timetd; MSFeed::addColumnToDesc(timetd,MSFeed::TIME); time.rwKeywordSet().assign(timetd[0].keywordSet()); ArrayColumn pos(feedTab,"POSITION"); TableDesc postd; MSFeed::addColumnToDesc(postd,MSFeed::POSITION); pos.rwKeywordSet().assign(postd[0].keywordSet()); ArrayColumn beam(feedTab,"BEAM_OFFSET"); TableDesc beamtd; MSFeed::addColumnToDesc(beamtd,MSFeed::BEAM_OFFSET); beam.rwKeywordSet().assign(beamtd[0].keywordSet()); ArrayColumn recep(feedTab,"RECEPTOR_ANGLE"); TableDesc receptd; MSFeed::addColumnToDesc(receptd,MSFeed::RECEPTOR_ANGLE); recep.rwKeywordSet().assign(receptd[0].keywordSet()); ScalarColumn inter(feedTab,"INTERVAL"); TableDesc intertd; MSFeed::addColumnToDesc(intertd,MSFeed::INTERVAL); inter.rwKeywordSet().assign(intertd[0].keywordSet()); } // FIELD { Table fldTab(ms2_p+"/FIELD",Table::Update); removeColumn(fldTab,"FIELD_ID"); Matrix dd,ddr,pd,pdr,rd,rdr,pntd,pntdr; uInt pdtp, rdtp, ddtp; { ArrayColumn delDir(fldTab,"DELAY_DIR"); ArrayColumn delDirRate(fldTab,"DELAY_DIR_RATE"); ArrayColumn phaseDir(fldTab,"PHASE_DIR"); ArrayColumn phaseDirRate(fldTab,"PHASE_DIR_RATE"); ArrayColumn pointingDir(fldTab,"POINTING_DIR"); ArrayColumn pointingDirRate(fldTab,"POINTING_DIR_RATE"); ArrayColumn refDir(fldTab,"REFERENCE_DIR"); ArrayColumn refDirRate(fldTab,"REFERENCE_DIR_RATE"); dd=delDir.getColumn(); ddr=delDirRate.getColumn(); pd=phaseDir.getColumn(); pdr=phaseDirRate.getColumn(); pntd=pointingDir.getColumn(); pntdr=pointingDirRate.getColumn(); rd=refDir.getColumn(); rdr=refDirRate.getColumn(); MDirection::Types tp; MDirection::getType(tp, phaseDir.keywordSet().asString("MEASURE_REFERENCE")); pdtp = tp; MDirection::getType(tp, refDir.keywordSet().asString("MEASURE_REFERENCE")); rdtp = tp; MDirection::getType(tp, delDir.keywordSet().asString("MEASURE_REFERENCE")); ddtp = tp; } IPosition shape(2,2,2); Int numPol = 1; if (allEQ(pntdr,0.0) && allEQ(ddr,0.0) && allEQ(pdr,0.0) && allEQ(rdr,0.0)) { // all rates are zero, use only one term shape(1)=1; numPol=0; } removeColumn(fldTab,"DELAY_DIR"); removeColumn(fldTab,"DELAY_DIR_RATE"); removeColumn(fldTab,"PHASE_DIR"); removeColumn(fldTab,"PHASE_DIR_RATE"); removeColumn(fldTab,"REFERENCE_DIR"); removeColumn(fldTab,"REFERENCE_DIR_RATE"); removeColumn(fldTab,"POINTING_DIR"); removeColumn(fldTab,"POINTING_DIR_RATE"); TableDesc td; MSField::addColumnToDesc(td, MSField::DELAY_DIR,2); MSField::addColumnToDesc(td, MSField::PHASE_DIR,2); MSField::addColumnToDesc(td, MSField::REFERENCE_DIR,2); MSField::addColumnToDesc(td, MSField::NUM_POLY); MSField::addColumnToDesc(td, MSField::FLAG_ROW); fldTab.addColumn(td[0]); fldTab.addColumn(td[1]); fldTab.addColumn(td[2]); fldTab.addColumn(td[3]); fldTab.addColumn(td[4]); ArrayMeasColumn delAmc(fldTab, "DELAY_DIR"); ArrayMeasColumn phaseAmc(fldTab, "PHASE_DIR"); ArrayMeasColumn refAmc(fldTab, "REFERENCE_DIR"); delAmc.setDescRefCode(ddtp, False); phaseAmc.setDescRefCode(pdtp, False); refAmc.setDescRefCode(rdtp, False); ArrayColumn delDir(fldTab,"DELAY_DIR"); ArrayColumn phaseDir(fldTab,"PHASE_DIR"); ArrayColumn refDir(fldTab,"REFERENCE_DIR"); ScalarColumn flagRow(fldTab,"FLAG_ROW"); flagRow.fillColumn(False); ScalarColumn numPoly(fldTab,"NUM_POLY"); numPoly.fillColumn(numPol); Int nRow=fldTab.nrow(); Matrix zero(shape); zero = 0.0; for (Int i=0; i ddir(shape),pdir(shape),rdir(shape),pntdir(shape); ddir(0,0)=dd(0,i); ddir(1,0)=dd(1,i); pdir(0,0)=pd(0,i); pdir(1,0)=pd(1,i); pntdir(0,0)=pntd(0,i); pntdir(1,0)=pntd(1,i); rdir(0,0)=rd(0,i); rdir(1,0)=rd(1,i); if (numPol==1) { ddir(0,1)=ddr(0,i); ddir(1,1)=ddr(1,i); pdir(0,1)=pdr(0,i); pdir(1,1)=pdr(1,i); pntdir(0,1)=pntdr(0,i); pntdir(1,1)=pntdr(1,i); rdir(0,1)=rdr(0,i); rdir(1,1)=rdr(1,i); } delDir.put(i,ddir); if (!allEQ(pdir, zero)) phaseDir.put(i,pdir); else phaseDir.put(i,pntdir); refDir.put(i,rdir); } ScalarColumn time(fldTab,"TIME"); TableDesc timetd; MSField::addColumnToDesc(timetd,MSField::TIME); time.rwKeywordSet().assign(timetd[0].keywordSet()); } // FLAG_CMD { Int nRow = 0; SetupNewTable flagCmdSetup(ms2_p+"/FLAG_CMD", MSFlagCmd::requiredTableDesc(), Table::New); Table flagCmdt(flagCmdSetup,nRow); t.rwKeywordSet().defineTable(MS::keywordName(MS::FLAG_CMD), flagCmdt); } // HISTORY { if (t.keywordSet().isDefined("OBS_LOG")) { // Table hisTab=t.rwKeywordSet().asTable("OBS_LOG"); Table hisTab(ms2_p+"/OBS_LOG",Table::Update); TableDesc td; MSHistory::addColumnToDesc(td,MSHistory::PRIORITY); MSHistory::addColumnToDesc(td,MSHistory::ORIGIN); MSHistory::addColumnToDesc(td,MSHistory::OBJECT_ID); MSHistory::addColumnToDesc(td,MSHistory::APPLICATION); MSHistory::addColumnToDesc(td,MSHistory::CLI_COMMAND); MSHistory::addColumnToDesc(td,MSHistory::APP_PARAMS); hisTab.addColumn(td[0]); hisTab.addColumn(td[1]); hisTab.addColumn(td[2]); hisTab.addColumn(td[3]); hisTab.addColumn(td[4]); hisTab.addColumn(td[5]); ScalarColumn time(hisTab,"TIME"); TableDesc timetd; MSHistory::addColumnToDesc(timetd,MSHistory::TIME); time.rwKeywordSet().assign(timetd[0].keywordSet()); t.rwKeywordSet().removeField("OBS_LOG"); hisTab.rename(ms2_p+"/HISTORY",Table::New); t.rwKeywordSet().defineTable("HISTORY", hisTab); } } // OBSERVATION { Table obsTab(ms2_p+"/OBSERVATION",Table::Update); removeColumn(obsTab, "CORR_SCHEDULE"); TableDesc td; MSObservation::addColumnToDesc(td, MSObservation::TELESCOPE_NAME); MSObservation::addColumnToDesc(td, MSObservation::TIME_RANGE); MSObservation::addColumnToDesc(td, MSObservation::SCHEDULE); MSObservation::addColumnToDesc(td, MSObservation::SCHEDULE_TYPE); MSObservation::addColumnToDesc(td, MSObservation::LOG); MSObservation::addColumnToDesc(td, MSObservation::RELEASE_DATE); MSObservation::addColumnToDesc(td, MSObservation::FLAG_ROW); obsTab.addColumn(td[0]); obsTab.addColumn(td[1]); obsTab.addColumn(td[2]); obsTab.addColumn(td[3]); obsTab.addColumn(td[4]); obsTab.addColumn(td[5]); obsTab.addColumn(td[6]); Table arrTab(ms2_p+"/ARRAY",Table::Old); ScalarColumn arrName(arrTab,"NAME"); ScalarColumn telName(obsTab,"TELESCOPE_NAME"); ArrayColumn timeRange(obsTab, "TIME_RANGE"); ScalarColumn flagRow(obsTab,"FLAG_ROW"); flagRow.fillColumn(False); ScalarColumn time(t, "TIME"); ScalarColumn interval(t, "INTERVAL"); ScalarColumn observationid(t, "OBSERVATION_ID"); ScalarColumn arrayid(t, "ARRAY_ID"); Vector tim = time.getColumn(); Vector inter = interval.getColumn(); Vector obsid = observationid.getColumn(); Vector arrid = arrayid.getColumn(); Vector arrnm = arrName.getColumn(); Int nObs = obsTab.nrow(); Int startInd; Int endInd; Int minPos, maxPos; Vector vt(2); for (Int obs=0; obs obs) { endInd = i-1; break; } } vt(0) = tim(startInd); vt(1) = tim(endInd); vt(0) = min(tim(Slice(startInd,endInd-startInd+1, 1))); vt(1) = max(tim(Slice(startInd,endInd-startInd+1, 1))); // Sort just in case the time column is not sorted vt(0) = tim(startInd); vt(1) = tim(startInd); minPos = startInd; maxPos = startInd; for (Int i=startInd; i<=endInd; i++) { if (tim(i) < vt(0)) { vt(0) = tim(i); minPos = i; } if (tim(i) >= vt(1)) { vt(1) = tim(i); maxPos = i; } } vt(0) = vt(0) - inter(minPos)/2; vt(1) = vt(1) + inter(maxPos)/2; timeRange.put(obs, vt); // telescope name telName.put(obs, arrnm(arrid(startInd))); } } // POINTING { Int nRow =0; SetupNewTable pointingSetup(ms2_p+"/POINTING", MSPointing::requiredTableDesc(), Table::New); Table pointTab(pointingSetup,nRow); // TableRecord tbrec = t.rwKeywordSet(); t.rwKeywordSet().defineTable(MS::keywordName(MS::POINTING), pointTab); ScalarColumn time(t, MS::columnName(MS::TIME)); ScalarColumn interval(t, MS::columnName(MS::INTERVAL)); ScalarColumn fieldId(t, MS::columnName(MS::FIELD_ID)); Vector tim = time.getColumn(); Vector inter = interval.getColumn(); Vector fi = fieldId.getColumn(); Table fldTab(ms2_p+"/FIELD", Table::Update); Cube pd; { ArrayColumn phaseDir(fldTab, "PHASE_DIR"); pd = phaseDir.getColumn(); } // Table pointTab(ms2_p+"/POINTING", Table::Update); ScalarColumn t2(pointTab, MSPointing::columnName(MSPointing::TIME)); ScalarColumn i2(pointTab, MSPointing::columnName(MSPointing::INTERVAL)); ArrayColumn phaseDir2(pointTab, MSPointing::columnName(MSPointing::DIRECTION)); ScalarColumn a2(pointTab, MSPointing::columnName(MSPointing::ANTENNA_ID)); ScalarColumn numPoly2(pointTab, MSPointing::columnName(MSPointing::NUM_POLY)); nRow = t.nrow(); Int fld = -1; Int pnt = 0; ScalarColumn numPoly(fldTab,"NUM_POLY"); IPosition shape(2,2,numPoly(0)+1); Matrix pdir(shape); for (Int i=0; i obspDir(fldTab,"_OBSOLETE_POINTING_DIR"); MDirection::Types tp; MDirection::getType(tp, obspDir.keywordSet().asString("MEASURE_REFERENCE")); ctp = tp; ArrayMeasColumn dirAmc(pointTab, "DIRECTION"); dirAmc.setDescRefCode(ctp, False); ArrayMeasColumn tgAmc(pointTab, "TARGET"); tgAmc.setDescRefCode(ctp, False); for (uInt j = MSPointing::NUMBER_REQUIRED_COLUMNS + 1; j < MSPointing::NUMBER_PREDEFINED_COLUMNS; j = j + 1) { MSPointing::PredefinedColumns i = (MSPointing::PredefinedColumns) j; if (pointTab.tableDesc().isColumn(MSPointing::columnName(i))) { TableColumn tbc(pointTab, MSPointing::columnName(i)); TableDesc td; MSPointing::addColumnToDesc(td, i); tbc.rwKeywordSet().assign(td[0].keywordSet()); } } } // POLARIZATION // SPECTRAL_WINDOW { Table spwTab(ms2_p+"/SPECTRAL_WINDOW",Table::Update); Int nRow = spwTab.nrow(); SetupNewTable polarizationSetup(ms2_p+"/POLARIZATION", MSPolarization::requiredTableDesc(), Table::New); Table polTab(polarizationSetup,nRow); t.rwKeywordSet().defineTable(MS::keywordName(MS::POLARIZATION), polTab); // Table polTab(ms2_p+"/POLARIZATION",Table::Update); TableDesc td; MSSpectralWindow::addColumnToDesc(td,MSSpectralWindow::NAME); MSSpectralWindow::addColumnToDesc(td,MSSpectralWindow::FREQ_GROUP); MSSpectralWindow::addColumnToDesc(td,MSSpectralWindow::FREQ_GROUP_NAME); MSSpectralWindow::addColumnToDesc(td,MSSpectralWindow::NET_SIDEBAND); MSSpectralWindow::addColumnToDesc(td,MSSpectralWindow::CHAN_WIDTH); MSSpectralWindow::addColumnToDesc(td,MSSpectralWindow::EFFECTIVE_BW); MSSpectralWindow::addColumnToDesc(td,MSSpectralWindow::MEAS_FREQ_REF); MSSpectralWindow::addColumnToDesc(td,MSSpectralWindow::FLAG_ROW); for (Int i=0; i<8; i++) spwTab.addColumn(td[i]); ScalarColumn snumCorr(spwTab, "NUM_CORR"); ArrayColumn scorrType(spwTab, "CORR_TYPE"); ArrayColumn scorrProduct(spwTab, "CORR_PRODUCT"); ScalarColumn pnumCorr(polTab, "NUM_CORR"); ArrayColumn pcorrType(polTab, "CORR_TYPE"); ArrayColumn pcorrProduct(polTab, "CORR_PRODUCT"); pnumCorr.putColumn(snumCorr.getColumn()); for (Int i=0; i freqGrp(spwTab,"FREQ_GROUP"); freqGrp.fillColumn(0); ScalarColumn netSideb(spwTab,"NET_SIDEBAND"); netSideb.fillColumn(1); ArrayColumn resol(spwTab, "RESOLUTION"); ArrayColumn chanWdth(spwTab, "CHAN_WIDTH"); ArrayColumn effBw(spwTab, "EFFECTIVE_BW"); for (Int i=0; i flagRow(spwTab,"FLAG_ROW"); flagRow.fillColumn(False); ScalarColumn reffreq(spwTab,"REF_FREQUENCY"); MFrequency::Types tp; MFrequency::getType(tp, reffreq.keywordSet().asString("MEASURE_REFERENCE")); Int meas_freq_ref = tp; ScalarColumn measCol(spwTab,"MEAS_FREQ_REF"); measCol.fillColumn(meas_freq_ref); TableDesc reffreqtd; MSSpectralWindow::addColumnToDesc(reffreqtd,MSSpectralWindow::REF_FREQUENCY); reffreq.rwKeywordSet().assign(reffreqtd[0].keywordSet()); ArrayColumn chanfreq(spwTab,"CHAN_FREQ"); TableDesc chanfreqtd; MSSpectralWindow::addColumnToDesc(chanfreqtd,MSSpectralWindow::CHAN_FREQ); chanfreq.rwKeywordSet().assign(chanfreqtd[0].keywordSet()); ArrayColumn chanwidth(spwTab,"CHAN_WIDTH"); TableDesc chanwidthtd; MSSpectralWindow::addColumnToDesc(chanwidthtd,MSSpectralWindow::CHAN_WIDTH); chanwidth.rwKeywordSet().assign(chanwidthtd[0].keywordSet()); ArrayColumn bw(spwTab,"EFFECTIVE_BW"); TableDesc bwtd; MSSpectralWindow::addColumnToDesc(bwtd,MSSpectralWindow::EFFECTIVE_BW); bw.rwKeywordSet().assign(bwtd[0].keywordSet()); ArrayColumn res(spwTab,"RESOLUTION"); TableDesc restd; MSSpectralWindow::addColumnToDesc(restd,MSSpectralWindow::RESOLUTION); res.rwKeywordSet().assign(restd[0].keywordSet()); ScalarColumn tbw(spwTab,"TOTAL_BANDWIDTH"); TableDesc tbwtd; MSSpectralWindow::addColumnToDesc(tbwtd,MSSpectralWindow::TOTAL_BANDWIDTH); tbw.rwKeywordSet().assign(tbwtd[0].keywordSet()); for (uInt j = MSSpectralWindow::NUMBER_REQUIRED_COLUMNS + 1; j < MSSpectralWindow::NUMBER_PREDEFINED_COLUMNS; j = j + 1) { MSSpectralWindow::PredefinedColumns i = (MSSpectralWindow::PredefinedColumns) j; if (spwTab.tableDesc().isColumn(MSSpectralWindow::columnName(i))) { TableColumn tbc(spwTab, MSSpectralWindow::columnName(i)); TableDesc td; MSSpectralWindow::addColumnToDesc(td, i); tbc.rwKeywordSet().assign(td[0].keywordSet()); } } } // PROCESSOR { Int nRow = 0; SetupNewTable processorSetup(ms2_p+"/PROCESSOR", MSProcessor::requiredTableDesc(), Table::New); Table processorSetupt(processorSetup,nRow); t.rwKeywordSet().defineTable(MS::keywordName(MS::PROCESSOR), processorSetupt); } // SOURCE { Table sourceTab(ms2_p+"/SOURCE",Table::Update); TableDesc td; MSSource::addColumnToDesc(td, MSSource::NUM_LINES); sourceTab.addColumn(td[0]); ArrayColumn pos(sourceTab,"POSITION"); TableDesc postd; MSSource::addColumnToDesc(postd,MSSource::POSITION); pos.rwKeywordSet().assign(postd[0].keywordSet()); ArrayColumn direc(sourceTab,"DIRECTION"); TableDesc directd; MSSource::addColumnToDesc(directd,MSSource::DIRECTION); direc.rwKeywordSet().assign(directd[0].keywordSet()); ArrayColumn prop(sourceTab,"PROPER_MOTION"); TableDesc proptd; MSSource::addColumnToDesc(proptd,MSSource::PROPER_MOTION); prop.rwKeywordSet().assign(proptd[0].keywordSet()); ScalarColumn inter(sourceTab,"INTERVAL"); TableDesc intertd; MSSource::addColumnToDesc(intertd,MSSource::INTERVAL); inter.rwKeywordSet().assign(intertd[0].keywordSet()); ScalarColumn time(sourceTab, "TIME"); TableDesc timetd; MSSource::addColumnToDesc(timetd,MSSource::TIME); time.rwKeywordSet().assign(timetd[0].keywordSet()); for (uInt j = MSSource::NUMBER_REQUIRED_COLUMNS + 1; j < MSSource::NUMBER_PREDEFINED_COLUMNS; j = j + 1) { MSSource::PredefinedColumns i = (MSSource::PredefinedColumns) j; if (sourceTab.tableDesc().isColumn(MSSource::columnName(i))) { TableColumn tbc(sourceTab, MSSource::columnName(i)); TableDesc td; MSSource::addColumnToDesc(td, i); tbc.rwKeywordSet().assign(td[0].keywordSet()); } } if (sourceTab.tableDesc().isColumn("SYSVEL_OLD")) { cout << "Array column SYSVEL_OLD seems to exist" << endl; } else { sourceTab.renameColumn("SYSVEL_OLD", "SYSVEL"); sourceTab.addColumn(ArrayColumnDesc("SYSVEL", 1)); // Construct a measure for this column in a temporary TableDesc. // Copy that keywordset to get the measure in the SYSVEL column. TableDesc td; td.addColumn(ArrayColumnDesc("SYSVELX", 1)); TableMeasValueDesc mvval(td, "SYSVELX"); TableMeasDesc mval(mvval); mval.write(td); ScalarColumn vold(sourceTab, "SYSVEL_OLD"); ArrayColumn sysvel(sourceTab, "SYSVEL"); sysvel.rwKeywordSet() = td.columnDesc("SYSVELX").keywordSet(); // Set data to the old SYSVEL. Vector vec(1); for (uInt i=0; i inter(syscalTab,"INTERVAL"); TableDesc intertd; MSSysCal::addColumnToDesc(intertd,MSSysCal::INTERVAL); inter.rwKeywordSet().assign(intertd[0].keywordSet()); ScalarColumn time(syscalTab, "TIME"); TableDesc timetd; MSSysCal::addColumnToDesc(timetd,MSSysCal::TIME); time.rwKeywordSet().assign(timetd[0].keywordSet()); for (uInt j = MSSysCal::NUMBER_REQUIRED_COLUMNS + 1; j < MSSysCal::NUMBER_PREDEFINED_COLUMNS; j = j + 1) { MSSysCal::PredefinedColumns i = (MSSysCal::PredefinedColumns) j; if (syscalTab.tableDesc().isColumn(MSSysCal::columnName(i))) { TableColumn tbc(syscalTab, MSSysCal::columnName(i)); TableDesc td; MSSysCal::addColumnToDesc(td, i); tbc.rwKeywordSet().assign(td[0].keywordSet()); } } } // WEATHER { Table weatherTab(ms2_p+"/WEATHER",Table::Update); if (weatherTab.canRemoveColumn("ARRAY_ID")) removeColumn(weatherTab,"ARRAY_ID"); ScalarColumn inter(weatherTab,"INTERVAL"); TableDesc intertd; MSWeather::addColumnToDesc(intertd,MSWeather::INTERVAL); inter.rwKeywordSet().assign(intertd[0].keywordSet()); ScalarColumn time(weatherTab, "TIME"); TableDesc timetd; MSWeather::addColumnToDesc(timetd,MSWeather::TIME); time.rwKeywordSet().assign(timetd[0].keywordSet()); for (uInt j = MSWeather::NUMBER_REQUIRED_COLUMNS + 1; j < MSWeather::NUMBER_PREDEFINED_COLUMNS; j = j + 1) { MSWeather::PredefinedColumns i = (MSWeather::PredefinedColumns) j; if (weatherTab.tableDesc().isColumn(MSWeather::columnName(i))) { TableColumn tbc(weatherTab, MSWeather::columnName(i)); TableDesc td; MSWeather::addColumnToDesc(td, i); tbc.rwKeywordSet().assign(td[0].keywordSet()); } } } // get correct shape for array weight if (t.tableDesc().isColumn("WEIGHT_OLD")) { cout << "Array column WEIGHT_OLD seems to exist" << endl; } else { t.renameColumn ("WEIGHT_OLD", "WEIGHT"); t.addColumn (ArrayColumnDesc("WEIGHT", 1)); ArrayColumn sigma (t, "SIGMA"); ScalarColumn wold (t, "WEIGHT_OLD"); ArrayColumn weight (t, "WEIGHT"); for (uInt i=0; i arr(sigma.shape(i)); arr = wold(i); weight.put (i, arr); } } os_p << LogIO::NORMAL << "Conversion done" << LogIO::POST; return True; } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSOper/MS1ToMS2Converter.h000066400000000000000000000067131476623553700204050ustar00rootroot00000000000000//# MS1ToMS2Converter.h: Definition for ms1 to ms2 converter //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MS1TOMS2CONVERTER_H #define MS_MS1TOMS2CONVERTER_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations class String; // // Class to convert a MeasurementSet v1 to v2. // // // // // // This class converts a version 1 MeasurementSet to version 2. // The keyword MS_VERSION tells the version. If not present it is 1. // If it is found that the MS is already a version 2 one, nothing is done. //

        // It is possible to do the conversion in place. If not, first a copy // is made which is thereafter changed in place. //
        // The conversion process does the following steps: //

          //
        • Create the newly required columns and keywords // and fill them with new or existing data. //
        • Convert the old way of defining measure info to the new // TableMeasures way. //
        • Rename obsolete columns by prefixing their names with _OBSOLETE_. //
        // // The constructor only keeps the names of the input and output MS. // The actual conversion is done by the convert function. //
        class MS1ToMS2Converter { public: // Create the converter for the given output (ms2) and input (ms1) name. // The input name has to be an MS version 1. If not, nothing will be done. //
        If inPlace==True, the ms2 name is ignored. In that // case the ms is changed in place. MS1ToMS2Converter (const String& ms2, const String& ms1, Bool inPlace); ~MS1ToMS2Converter(); // Forbid copy constructor and assignment. // MS1ToMS2Converter (const MS1ToMS2Converter&) = delete; MS1ToMS2Converter& operator= (const MS1ToMS2Converter&) = delete; // // Do the actual conversion. Bool convert(); private: // If possible remove a column from the table. // Otherwise rename it by prefixing it with _OBSOLETE_. void removeColumn(Table& t, const String& col); String ms1_p; String ms2_p; Bool inPlace_p; // Logger LogIO os_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSOper/MSConcat.cc000066400000000000000000004002751476623553700170760ustar00rootroot00000000000000//# MSConcat.cc: A class for concatenating MeasurementSets. //# Copyright (C) 2000,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { MSConcat::MSConcat(MeasurementSet& ms): MSColumns(ms), itsMS(ms), itsFixedShape(isFixedShape(ms.tableDesc())) { itsDirTol=Quantum(1.0, "mas"); itsFreqTol=Quantum(1.0, "Hz"); itsWeightScale = 1.; itsRespectForFieldName = False; doSource_p=False; doObsA_p = doObsB_p = False; doProcA_p = doProcB_p = False; } IPosition MSConcat::isFixedShape(const TableDesc& td) { IPosition fixedShape(0); Bool isFixed = False; const Vector hypercolumnNames=td.hypercolumnNames(); const uInt nHyperCols = hypercolumnNames.nelements(); Vector dataColNames,coordColNames,idColNames; uInt hc = 0; while (isFixed == False && hc < nHyperCols) { td.hypercolumnDesc(hypercolumnNames(hc), dataColNames, coordColNames, idColNames); const uInt nDataCol = dataColNames.nelements(); uInt dc = 0; while (isFixed == False && dc < nDataCol) { const String& dataColName = dataColNames(dc); // The order of these if conditions is important as I am trying to get // the biggest possible fixed shape. if (dataColName == MS::columnName(MS::FLAG_CATEGORY) || dataColName == MS::columnName(MS::DATA) || dataColName == MS::columnName(MS::FLAG) || dataColName == MS::columnName(MS::SIGMA_SPECTRUM) || dataColName == MS::columnName(MS::WEIGHT_SPECTRUM) || dataColName == MS::columnName(MS::CORRECTED_WEIGHT_SPECTRUM) || dataColName == MS::columnName(MS::FLOAT_DATA) || dataColName == MS::columnName(MS::CORRECTED_DATA) || dataColName == MS::columnName(MS::MODEL_DATA) || dataColName == MS::columnName(MS::LAG_DATA) || dataColName == MS::columnName(MS::SIGMA) || dataColName == MS::columnName(MS::WEIGHT) || dataColName == MS::columnName(MS::VIDEO_POINT)) { const ColumnDesc& colDesc = td.columnDesc(dataColNames(dc)); isFixed = colDesc.isFixedShape(); if (isFixed) fixedShape = colDesc.shape(); } dc++; } hc++; dataColNames.resize(0); coordColNames.resize(0); idColNames.resize(0); } return fixedShape; } Bool MSConcat::checkEphIdInField(const MSFieldColumns& otherFldCol) const { // test if this MS FIELD table has an ephID column if(!itsMS.field().actualTableDesc().isColumn(MSField::columnName(MSField::EPHEMERIS_ID))){ // if not, test if the other MS uses ephem objects Bool usesEphems = False; for(rownr_t i=0; i 0) { if (itsFixedShape.nelements() > 0) { const MSPolarizationColumns otherPolCols(otherMS.polarization()); const MSSpWindowColumns otherSpwCols(otherMS.spectralWindow()); const MSDataDescColumns otherDDCols(otherMS.dataDescription()); const uInt nShapes = otherDDCols.nrow(); for (uInt s = 0; s < nShapes; ++s) { checkShape(getShape(otherDDCols, otherSpwCols, otherPolCols, s)); } } const MSMainColumns otherMainCols(otherMS); checkCategories(otherMainCols); } } // merge STATE Block newStateIndices; // INTO TABLE Bool doState = False; // STATE is a required subtable but can be empty in which case the state id in the main table is -1 Bool itsStateNull = (itsMS.state().isNull() || (itsMS.state().nrow() == 0)); Bool otherStateNull = (otherMS.state().isNull() || (otherMS.state().nrow() == 0)); if(itsStateNull && otherStateNull){ log << LogIO::NORMAL << "No valid state tables present. Result won't have one either." << LogIO::POST; } else if(itsStateNull && !otherStateNull){ log << LogIO::WARN << itsMS.tableName() << " does not have a valid state table," << endl << " the MS to be appended, however, has one. Result won't have one." << LogIO::POST; doState = True; // i.e. the appended MS Main table state id will have to be set to -1 } else if(!itsStateNull && otherStateNull){ log << LogIO::WARN << itsMS.tableName() << " does have a valid state table," << endl << " the MS to be appended, however, doesn't. Result won't have one." << LogIO::POST; doState = True; // i.e. itsMS Main table state id will have to be set to -1 RowNumbers delrows(itsMS.state().nrow()); indgen(delrows); itsMS.state().removeRow(RowNumbers(delrows)); } else{ // both state tables are filled const rownr_t oldStateRows = itsMS.state().nrow(); newStateIndices = copyState(otherMS.state()); const rownr_t addedRows = itsMS.state().nrow() - oldStateRows; const rownr_t matchedRows = otherMS.state().nrow() - addedRows; log << "Added " << addedRows << " rows and matched " << matchedRows << " from the state subtable" << LogIO::POST; doState = True; // state id entries in the main table will have to be modified for otherMS } //See if there is a SOURCE table and concatenate and reindex it { rownr_t oldSRows = itsMS.source().nrow(); copySource(otherMS); if(Table::isReadable(itsMS.sourceTableName())){ rownr_t addedRows = itsMS.source().nrow() - oldSRows; if(addedRows>0){ log << "Added " << addedRows << " rows to the source subtable" << LogIO::POST; } } } // DATA_DESCRIPTION rownr_t oldRows = itsMS.dataDescription().nrow(); rownr_t oldSPWRows = itsMS.spectralWindow().nrow(); const Block newDDIndices = copySpwAndPol(otherMS.spectralWindow(), otherMS.polarization(), otherMS.dataDescription()); { rownr_t addedRows = itsMS.dataDescription().nrow() - oldRows; rownr_t matchedRows = otherMS.dataDescription().nrow() - addedRows; log << "Added " << addedRows << " rows and matched " << matchedRows << " from the data description subtable" << LogIO::POST; addedRows = itsMS.spectralWindow().nrow() - oldSPWRows; matchedRows = otherMS.spectralWindow().nrow() - addedRows; log << "Added " << addedRows << " rows and matched " << matchedRows << " from the spectral window subtable" << LogIO::POST; } // correct the spw entries in the SOURCE table and remove redundant rows oldRows = itsMS.source().nrow(); updateSource(); if(Table::isReadable(itsMS.sourceTableName())){ rownr_t removedRows = oldRows - itsMS.source().nrow(); if(removedRows>0){ log << "Removed " << removedRows << " redundant rows from the source subtable" << LogIO::POST; } } // merge ANTENNA and FEED oldRows = itsMS.antenna().nrow(); rownr_t oldFeedRows = itsMS.feed().nrow(); const Block newAntIndices = copyAntennaAndFeed(otherMS.antenna(), otherMS.feed()); Bool antIndexTrivial = True; for(uInt ii=0; ii newFldIndices = copyField(otherMS); { const rownr_t addedRows = itsMS.field().nrow() - oldRows; const rownr_t matchedRows = otherMS.field().nrow() - addedRows; log << "Added " << addedRows << " rows and matched " << matchedRows << " from the field subtable" << LogIO::POST; if(matchedRows>0){ // may have to consolidate SOURCE IDs if(updateSource2()){ log << "Consolidated Source IDs in the source subtable." << LogIO::POST; } } } // OBSERVATION copyObservation(otherMS.observation(), True); // PROCESSOR copyProcessor(otherMS.processor(), True); // POINTING if(!antIndexTrivial){ copyPointingB(otherMS.pointing(), newAntIndices); } // SYSCAL copySysCal(otherMS.sysCal(), newAntIndices); // WEATHER copyWeather(otherMS.weather(), newAntIndices); // GAIN_CURVE copyGainCurve(otherMS, newAntIndices); // PHASE_CAL copyPhaseCal(otherMS, newAntIndices); // EARTH_ORIENTATION copyEOP(otherMS); ///////////////////////////////////////////////////// // copying all subtables over to otherMS // will need to be done when creating the MMS from the concatenated MSs ////////////////////////////////////////////////////// MSMainColumns mainCols(itsMS); MSMainColumns otherMainCols(otherMS); const rownr_t otherRows = otherMS.nrow(); const rownr_t theseRows = itsMS.nrow(); // create column objects for those columns which potentially need to be modified ArrayColumn otherData; ArrayColumn otherFloatData; ArrayColumn otherModelData, otherCorrectedData; if(doFloatData){ otherFloatData.reference(otherMainCols.floatData()); } else{ otherData.reference(otherMainCols.data()); } if(doCorrectedData){ otherCorrectedData.reference(otherMainCols.correctedData()); } if(doModelData){ otherModelData.reference(otherMainCols.modelData()); } ArrayColumn& otherUvw = otherMainCols.uvw(); ArrayColumn& otherWeight = otherMainCols.weight(); ArrayColumn& otherWeightSp = otherMainCols.weightSpectrum(); ArrayColumn& otherSigma = otherMainCols.sigma(); ArrayColumn& otherSigmaSp = otherMainCols.sigmaSpectrum(); ArrayColumn& otherFlag = otherMainCols.flag(); ArrayColumn& otherFlagCat = otherMainCols.flagCategory(); ScalarColumn& otherAnt1Col = otherMainCols.antenna1(); ScalarColumn& otherAnt2Col = otherMainCols.antenna2(); ScalarColumn& otherDDIdCol = otherMainCols.dataDescId(); ScalarColumn& otherFieldIdCol = otherMainCols.fieldId(); ScalarColumn& otherScanCol = otherMainCols.scanNumber(); ScalarColumn& otherStateIdCol = otherMainCols.stateId(); ScalarColumn& otherObsIdCol =otherMainCols.observationId(); ScalarColumn& otherProcIdCol =otherMainCols.processorId(); ScalarColumn& thisScanCol = mainCols.scanNumber(); ScalarColumn& thisStateIdCol = mainCols.stateId(); ScalarColumn& thisObsIdCol = mainCols.observationId(); ScalarColumn& thisProcIdCol = mainCols.processorId(); Vector otherAnt1; Vector otherAnt2; if(!antIndexTrivial){ otherAnt1 = otherAnt1Col.getColumn(); otherAnt2 = otherAnt2Col.getColumn(); } Vector otherDDId = otherDDIdCol.getColumn(); Vector otherFieldId = otherFieldIdCol.getColumn(); Vector otherScan = otherScanCol.getColumn(); Vector otherStateId(otherMS.nrow(),-1); Vector otherObsIds; Vector otherProcIds; if (doState && !otherStateNull){ otherStateId = otherStateIdCol.getColumn(); } Int defaultScanOffset=0; std::map scanOffsetForOid; std::map encountered; vector distinctObsIdSet; vector minScan; vector maxScan; if(reindexObsidAndScan){ otherObsIds = otherObsIdCol.getColumn(); Vector theseObsIds=thisObsIdCol.getColumn(); otherProcIds = otherProcIdCol.getColumn(); Vector theseProcIds=thisProcIdCol.getColumn(); Vector theseScans=thisScanCol.getColumn(); if(doObsA_p){ // the obs ids changed for the first table for(rownr_t r = 0; r < theseRows; r++) { if(newObsIndexA_p.find(theseObsIds[r]) != newObsIndexA_p.end()){ // apply change theseObsIds[r] = getMapValue(newObsIndexA_p, theseObsIds[r]); } } thisObsIdCol.putColumn(theseObsIds); } if(doProcA_p){ // the proc ids changed for the first table for(uInt r = 0; r < theseRows; r++) { if(newProcIndexA_p.find(theseProcIds[r]) != newProcIndexA_p.end()){ // apply change theseProcIds[r] = getMapValue(newProcIndexA_p, theseProcIds[r]); } } thisProcIdCol.putColumn(theseProcIds); } // SCAN NUMBER // find the distinct ObsIds in use in this MS // and the maximum scan ID in each of them Int maxScanThis=0; // read the initial values from a file if it exists std::ifstream ifs; ifs.open(obsidAndScanTableName.c_str(), ifstream::in); if (ifs.good()) { log << LogIO::NORMAL << "Reading from " << obsidAndScanTableName << LogIO::POST; uInt n; Int tobsid, tminscan, tmaxscan; ifs >> n; for(uInt i=0; i> tobsid; distinctObsIdSet.push_back(tobsid); ifs >> tminscan; minScan.push_back(tminscan); ifs >> tmaxscan; maxScan.push_back(tmaxscan); } else{ log << LogIO::WARN << "Error reading file " << obsidAndScanTableName << LogIO::POST; break; } } if(ifs.good()){ ifs >> maxScanThis; } else{ log << LogIO::WARN << "Error reading file " << obsidAndScanTableName << LogIO::POST; log << LogIO::WARN << "Will continue with uninitialized obsid and scans information" << LogIO::POST; distinctObsIdSet.resize(0); minScan.resize(0); maxScan.resize(0); maxScanThis=0; } //cout << "distinctObsIdSet " << Vector(distinctObsIdSet) << endl; //cout << "minScan " << Vector(minScan) << endl; //cout << "maxScan " << Vector(maxScan) << endl; //cout << "maxScanThis " << maxScanThis << endl; } else{ log << LogIO::NORMAL << "Will create auxiliary file " << obsidAndScanTableName << LogIO::POST; // determine the values for distinctObsIdSet, minScan, maxScanThis from scratch for(rownr_t r = 0; r < theseRows; r++) { Int oid = theseObsIds[r]; Int scanid = theseScans[r]; Bool found = False; uInt i; for(i=0; imaxScan[i]){ maxScan[i] = scanid; } } else { distinctObsIdSet.push_back(oid); minScan.push_back(scanid); maxScan.push_back(scanid); } if(scanid>maxScanThis){ maxScanThis = scanid; } } } ifs.close(); // set the offset added to scan numbers in each observation Int minScanOther = min(otherScan); { defaultScanOffset = maxScanThis + 1 - minScanOther; if(defaultScanOffset<0){ defaultScanOffset=0; } } for(uInt i=0; i tempV(theseRows, -1); thisStateIdCol.putColumn(tempV); } // finished all modifications of MS Main table one // now start modifications of the second one log << LogIO::NORMAL << "Working on appended Main table ..." << LogIO::POST; Bool copyWtSp = (!otherWeightSp.isNull()) && otherWeightSp.isDefined(0); Bool copySgSp = (!otherSigmaSp.isNull()) && otherSigmaSp.isDefined(0); Bool copyFlagCat = (!otherFlagCat.isNull()) && otherFlagCat.isDefined(0); // MAIN Bool doWeightScale = (itsWeightScale!=1.) && (itsWeightScale!=0.); Float sScale = 1.; // scale for SIGMA if (doWeightScale){ sScale = 1/sqrt(itsWeightScale); } if(reindexObsidAndScan){ for (rownr_t r = 0; r < otherRows; r++) { Int oid = 0; if(doObsB_p && newObsIndexB_p.find(otherObsIds[r]) != newObsIndexB_p.end()){ // the obs ids have been changed for the table to be appended oid = getMapValue (newObsIndexB_p, otherObsIds[r]); } else{ // this OBS id didn't change oid = otherObsIds[r]; } if(oid != otherObsIds[r]){ // obsid actually changed if(scanOffsetForOid.find(oid) == scanOffsetForOid.end()){ // offset not set, use default scanOffsetForOid[oid] = defaultScanOffset; } if(encountered.find(oid)==encountered.end() && scanOffsetForOid.at(oid)!=0){ log << LogIO::NORMAL << "Will offset scan numbers by " << scanOffsetForOid.at(oid) << " for observations with Obs ID " << oid << " in order to make scan numbers unique." << LogIO::POST; encountered[oid] = 0; } otherScan[r] = otherScan[r] + scanOffsetForOid.at(oid); } otherObsIds[r] = oid; Int procid = 0; if(doProcB_p && newProcIndexB_p.find(otherProcIds[r]) != newProcIndexB_p.end()){ // the proc ids have been changed for the table to be appended procid = getMapValue (newProcIndexB_p, otherProcIds[r]); } else{ // this PROC id didn't change procid = otherProcIds[r]; } otherProcIds[r] = procid; } // update or create the file with the initial values for the next concat Int maxScanOther = 0; for(rownr_t r = 0; r < otherRows; r++) { Int oid = otherObsIds[r]; Int scanid = otherScan[r]; Bool found = False; uInt i; for(i=0; imaxScan[i]){ maxScan[i] = scanid; } } else { distinctObsIdSet.push_back(oid); minScan.push_back(scanid); maxScan.push_back(scanid); } if(scanid>maxScanOther){ maxScanOther = scanid; } } std::ofstream ofs; ofs.open (obsidAndScanTableName.c_str(), ofstream::out); if (!ofs) { log << LogIO::WARN << "Error opening file " << obsidAndScanTableName << "will continue but the next virtual concat will lack this information:" << LogIO::POST; log << "distinctObsIdSet " << Vector(distinctObsIdSet) << endl; log << "minScan " << Vector(minScan) << endl; log << "maxScan " << Vector(maxScan) << endl; log << "maxScanOther " << maxScanOther << LogIO::POST; } else{ log << LogIO::NORMAL << "Writing to " << obsidAndScanTableName << LogIO::POST; uInt n = distinctObsIdSet.size(); ofs << n << endl; for(uInt i=0; i polSwap; for (rownr_t r = 0; r < otherRows; r++) { // Determine whether we need to swap rows in the visibility matrix // if we change the order of the antennas. This is done by // creating a mapping that makes sure the receptor numbers remain // correct when the antennas are swapped. /////uInt d = otherDDId(r); Int p = otherDDCols.polarizationId()(otherDDId(r)); if (p != polId) { const Matrix &products = otherPolCols.corrProduct()(p); polSwap.resize(products.shape()(1)); for (Int i = 0; i < products.shape()(1); i++) { for (Int j = 0; j < products.shape()(1); j++) { if (products(0, i) == products(1, j) && products(1, i) == products(0, j)) { polSwap[i] = j; break; } } } polId = p; } Bool doConjugateVis = False; if(!antIndexTrivial){ Int newA1 = newAntIndices[otherAnt1[r]]; Int newA2 = newAntIndices[otherAnt2[r]]; if(newA1>newA2){ // swap indices and multiply UVW by -1 //cout << " corrected order r: " << r << " " << newA2 << " " << newA1 << endl; otherAnt1[r] = newA2; otherAnt2[r] = newA1; Array newUvw; newUvw.assign(otherUvw(r)); //cout << " old UVW " << newUvw; newUvw *= -1.; //cout << ", new UVW " << newUvw << endl; otherUvw.put(r, newUvw); doConjugateVis = True; } else{ otherAnt1[r] = newA1; otherAnt2[r] = newA2; } } if(itsChanReversed[otherDDId[r]]){ Vector datShape; Matrix reversedData; Matrix reversedFloatData; Matrix swappedData; if(doFloatData){ datShape=otherFloatData.shape(r).asVector(); reversedFloatData.resize(datShape[0], datShape[1]); } else{ datShape=otherData.shape(r).asVector(); reversedData.resize(datShape[0], datShape[1]); } Matrix reversedCorrData(datShape[0], datShape[1]); Matrix reversedModData(datShape[0], datShape[1]); for (Int k1=0; k1 < datShape[0]; ++k1){ for(Int k2=0; k2 < datShape[1]; ++k2){ if(doFloatData){ reversedFloatData(k1,k2)=(Matrix(otherFloatData(r)))(k1, datShape[1]-1-k2); } else{ reversedData(k1,k2)=(Matrix(otherData(r)))(k1, datShape[1]-1-k2); } if(doModelData){ reversedModData(k1,k2)=(Matrix(otherModelData(r)))(k1, datShape[1]-1-k2); } if(doCorrectedData){ reversedCorrData(k1,k2)=(Matrix(otherCorrectedData(r)))(k1, datShape[1]-1-k2); } } } if(doFloatData){ otherFloatData.put(r, reversedFloatData); } else{ if(doConjugateVis){ swappedData.resize(datShape[0], datShape[1]); for (Int p = 0; p < datShape[0]; p++) { swappedData.row(p) = (Matrix(reversedData)).row(polSwap[p]); } otherData.put(r, conj(swappedData)); } else{ otherData.put(r, reversedData); } } if(doCorrectedData){ if(doConjugateVis){ swappedData.resize(datShape[0], datShape[1]); for (Int p = 0; p < datShape[0]; p++) { swappedData.row(p) = (Matrix(reversedCorrData)).row(polSwap[p]); } otherCorrectedData.put(r, conj(swappedData)); } else{ otherCorrectedData.put(r, reversedCorrData); } } if(doModelData){ if(doConjugateVis){ swappedData.resize(datShape[0], datShape[1]); for (Int p = 0; p < datShape[0]; p++) { swappedData.row(p) = (Matrix(reversedModData)).row(polSwap[p]); } otherModelData.put(r, conj(swappedData)); } else{ otherModelData.put(r, reversedModData); } } } else{ // no reversal Vector datShape; Matrix swappedData; if(!doFloatData){ if(doConjugateVis){ // conjugate because order of antennas was reversed datShape=otherData.shape(r).asVector(); swappedData.resize(datShape[0], datShape[1]); for (Int p = 0; p < datShape[0]; p++) { swappedData.row(p) = (Matrix(otherData(r))).row(polSwap[p]); } otherData.put(r, conj(swappedData)); } } if(doModelData){ if(doConjugateVis){ datShape=otherModelData.shape(r).asVector(); swappedData.resize(datShape[0], datShape[1]); for (Int p = 0; p < datShape[0]; p++) { swappedData.row(p) = (Matrix(otherModelData(r))).row(polSwap[p]); } otherModelData.put(r, conj(swappedData)); } } if(doCorrectedData){ if(doConjugateVis){ datShape=otherCorrectedData.shape(r).asVector(); swappedData.resize(datShape[0], datShape[1]); for (Int p = 0; p < datShape[0]; p++) { swappedData.row(p) = (Matrix(otherCorrectedData(r))).row(polSwap[p]); } otherCorrectedData.put(r, conj(swappedData)); } } } // end if itsChanReversed otherDDId[r] = newDDIndices[otherDDId[r]]; otherFieldId[r] = newFldIndices[otherFieldId[r]]; if(doWeightScale){ if(doConjugateVis){ Vector datShape=otherWeight.shape(r).asVector(); Vector swappedWeight(datShape[0]); for (Int p = 0; p < datShape[0]; p++) { swappedWeight(p) = (Vector(otherWeight(r)))(polSwap[p]); } otherWeight.put(r, swappedWeight*itsWeightScale); if (copyWtSp) { datShape.assign(otherWeightSp.shape(r).asVector()); Matrix swappedWeightSp(datShape[0], datShape[1]); for (Int p = 0; p < datShape[0]; p++) { swappedWeightSp.row(p) = (Matrix(otherWeightSp(r))).row(polSwap[p]); } otherWeightSp.put(r, swappedWeightSp*itsWeightScale); } datShape.assign(otherSigma.shape(r).asVector()); Vector swappedSigma(datShape[0]); for (Int p = 0; p < datShape[0]; p++) { swappedSigma(p) = (Vector(otherSigma(r)))(polSwap[p]); } otherSigma.put(r, swappedSigma*sScale); if (copySgSp) { datShape.assign(otherSigmaSp.shape(r).asVector()); Matrix swappedSigmaSp(datShape[0], datShape[1]); for (Int p = 0; p < datShape[0]; p++) { swappedSigmaSp.row(p) = (Matrix(otherSigmaSp(r))).row(polSwap[p]); } otherSigmaSp.put(r, swappedSigmaSp*sScale); } } else{ otherWeight.put(r, otherWeight(r)*itsWeightScale); if (copyWtSp) otherWeightSp.put(r, otherWeightSp(r)*itsWeightScale); otherSigma.put(r, otherSigma(r)*sScale); if (copySgSp) otherSigmaSp.put(r, otherWeightSp(r)*sScale); } } else{ if(doConjugateVis){ Vector datShape=otherWeight.shape(r).asVector(); Vector swappedWeight(datShape[0]); for (Int p = 0; p < datShape[0]; p++) { swappedWeight(p) = (Vector(otherWeight(r)))(polSwap[p]); } otherWeight.put(r, swappedWeight); if (copyWtSp) { datShape.assign(otherWeightSp.shape(r).asVector()); Matrix swappedWeightSp(datShape[0], datShape[1]); for (Int p = 0; p < datShape[0]; p++) { swappedWeightSp.row(p) = (Matrix(otherWeightSp(r))).row(polSwap[p]); } otherWeightSp.put(r, swappedWeightSp); } datShape.assign(otherSigma.shape(r).asVector()); Vector swappedSigma(datShape[0]); for (Int p = 0; p < datShape[0]; p++) { swappedSigma(p) = (Vector(otherSigma(r)))(polSwap[p]); } otherSigma.put(r, swappedSigma); if (copySgSp) { datShape.assign(otherSigmaSp.shape(r).asVector()); Matrix swappedSigmaSp(datShape[0], datShape[1]); for (Int p = 0; p < datShape[0]; p++) { swappedSigmaSp.row(p) = (Matrix(otherSigmaSp(r))).row(polSwap[p]); } otherSigmaSp.put(r, swappedSigmaSp); } } } if(doConjugateVis){ Vector datShape=otherFlag.shape(r).asVector(); Matrix swappedFlag(datShape[0], datShape[1]); for (Int p = 0; p < datShape[0]; p++) { swappedFlag.row(p) = (Matrix(otherFlag(r))).row(polSwap[p]); } otherFlag.put(r, swappedFlag); if (copyFlagCat) { datShape.assign(otherFlagCat.shape(r).asVector()); Cube swappedFlagCat(datShape[0], datShape[1], datShape[2]); for (Int p = 0; p < datShape[0]; p++) { swappedFlagCat.yzPlane(p) = (Cube(otherFlagCat(r))).yzPlane(polSwap[p]); } otherFlagCat.put(r, swappedFlagCat); } } } // end for // write the scalar columns log << LogIO::NORMAL << "Writing the scalar columns ..." << LogIO::POST; if(!antIndexTrivial){ otherAnt1Col.putColumn(otherAnt1); otherAnt2Col.putColumn(otherAnt2); } otherDDIdCol.putColumn(otherDDId); otherFieldIdCol.putColumn(otherFieldId); if(doState && !(itsStateNull || otherStateNull)){ otherStateIdCol.putColumn(otherStateId); } if(reindexObsidAndScan){ otherScanCol.putColumn(otherScan); otherObsIdCol.putColumn(otherObsIds); otherProcIdCol.putColumn(otherProcIds); } if(doModelData){ //update the MODEL_DATA keywords updateModelDataKeywords(otherMS); } } //-------------------------------------------------------------------- void MSConcat::concatenate(const MeasurementSet& otherMS, const uInt handling, const String& destMSName) { LogIO log(LogOrigin("MSConcat", "concatenate", WHERE)); if(destMSName.empty()){ log << "Appending " << otherMS.tableName() << " to " << itsMS.tableName() << endl << LogIO::POST; } else{ log << "Virtually appending " << otherMS.tableName() << " to " << itsMS.tableName() << endl << LogIO::POST; } switch(handling){ case 0: // normal concat break; case 1: log << "*** At user\'s request, MAIN table will not be concatenated!" << LogIO::POST; break; case 2: log << "*** At user\'s request, POINTING table will not be concatenated!" << LogIO::POST; break; case 3: log << "*** At user\'s request, MAIN and POINTING tables will not be concatenated!" << LogIO::POST; break; default: log << "Invalid value for handling switch: " << handling << " (valid range is 0 - 3)" << LogIO::EXCEPTION; break; } // check if certain columns are present and set flags accordingly Bool doCorrectedData=False, doModelData=False; Bool doFloatData=False; if(handling==0 || handling==2){ if (itsMS.tableDesc().isColumn("FLOAT_DATA") && otherMS.tableDesc().isColumn("FLOAT_DATA")) doFloatData=True; else if (itsMS.tableDesc().isColumn("FLOAT_DATA") && !otherMS.tableDesc().isColumn("FLOAT_DATA")){ log << itsMS.tableName() << " has FLOAT_DATA column but not " << otherMS.tableName() << LogIO::EXCEPTION; log << "Cannot concatenate these MSs yet...you may split the corrected column of the SD as a work around." << LogIO::EXCEPTION; } if (itsMS.tableDesc().isColumn("MODEL_DATA") && otherMS.tableDesc().isColumn("MODEL_DATA")) doModelData=True; else if (itsMS.tableDesc().isColumn("MODEL_DATA") && !otherMS.tableDesc().isColumn("MODEL_DATA")){ log << itsMS.tableName() << " has MODEL_DATA column but not " << otherMS.tableName() << LogIO::EXCEPTION; log << "You may wish to create this column by loading " << otherMS.tableName() << " in imager or calibrater " << LogIO::EXCEPTION; } if (itsMS.tableDesc().isColumn("CORRECTED_DATA") && otherMS.tableDesc().isColumn("CORRECTED_DATA")) doCorrectedData=True; else if (itsMS.tableDesc().isColumn("CORRECTED_DATA") && !otherMS.tableDesc().isColumn("CORRECTED_DATA")) log << itsMS.tableName() <<" has CORRECTED_DATA column but not " << otherMS.tableName() << LogIO::EXCEPTION; } { const MSFieldColumns otherMSFCols(otherMS.field()); if(!checkEphIdInField(otherMSFCols)){ log << "EPHEMERIS_ID column missing in FIELD table of MS " << itsMS.tableName() << LogIO::EXCEPTION; } } // verify that shape of the two MSs as described in POLARISATION, SPW, and DATA_DESCR // is the same const MSMainColumns otherMainCols(otherMS); if (otherMS.nrow() > 0) { if (itsFixedShape.nelements() > 0) { const MSPolarizationColumns otherPolCols(otherMS.polarization()); const MSSpWindowColumns otherSpwCols(otherMS.spectralWindow()); const MSDataDescColumns otherDDCols(otherMS.dataDescription()); const rownr_t nShapes = otherDDCols.nrow(); for (rownr_t s = 0; s < nShapes; s++) { checkShape(getShape(otherDDCols, otherSpwCols, otherPolCols, s)); } } checkCategories(otherMainCols); } log << LogIO::DEBUG1 << "ms shapes verified " << endl << LogIO::POST; // merge STATE Block newStateIndices; Bool doState = False; // STATE is a required subtable but can be empty in which case the state id in the main table is -1 Bool itsStateNull = (itsMS.state().isNull() || (itsMS.state().nrow() == 0)); Bool otherStateNull = (otherMS.state().isNull() || (otherMS.state().nrow() == 0)); if(itsStateNull && otherStateNull){ log << LogIO::NORMAL << "No valid state tables present. Result won't have one either." << LogIO::POST; } else if(itsStateNull && !otherStateNull){ log << LogIO::WARN << itsMS.tableName() << " does not have a valid state table," << endl << " the MS to be appended, however, has one. Result won't have one." << LogIO::POST; doState = True; // i.e. the appended MS Main table state id will have to be set to -1 } else if(!itsStateNull && otherStateNull){ log << LogIO::WARN << itsMS.tableName() << " does have a valid state table," << endl << " the MS to be appended, however, doesn't. Result won't have one." << LogIO::POST; doState = True; // i.e. itsMS Main table state id will have to be set to -1 RowNumbers delrows(itsMS.state().nrow()); indgen(delrows); itsMS.state().removeRow(RowNumbers(delrows)); } else{ // both state tables are filled const rownr_t oldStateRows = itsMS.state().nrow(); newStateIndices = copyState(otherMS.state()); const rownr_t addedRows = itsMS.state().nrow() - oldStateRows; const rownr_t matchedRows = otherMS.state().nrow() - addedRows; log << "Added " << addedRows << " rows and matched " << matchedRows << " from the state subtable" << LogIO::POST; doState = True; // state id entries in the main table will have to be modified for otherMS } //See if there is a SOURCE table and concatenate and reindex it { rownr_t oldSRows = itsMS.source().nrow(); copySource(otherMS); if(Table::isReadable(itsMS.sourceTableName())){ rownr_t addedRows = itsMS.source().nrow() - oldSRows; if(addedRows>0){ log << "Added " << addedRows << " rows to the source subtable" << LogIO::POST; } } } // DATA_DESCRIPTION rownr_t oldRows = itsMS.dataDescription().nrow(); rownr_t oldSPWRows = itsMS.spectralWindow().nrow(); const Block newDDIndices = copySpwAndPol(otherMS.spectralWindow(), otherMS.polarization(), otherMS.dataDescription()); { rownr_t addedRows = itsMS.dataDescription().nrow() - oldRows; rownr_t matchedRows = otherMS.dataDescription().nrow() - addedRows; log << "Added " << addedRows << " rows and matched " << matchedRows << " from the data description subtable" << LogIO::POST; addedRows = itsMS.spectralWindow().nrow() - oldSPWRows; matchedRows = otherMS.spectralWindow().nrow() - addedRows; log << "Added " << addedRows << " rows and matched " << matchedRows << " from the spectral window subtable" << LogIO::POST; } // correct the spw entries in the SOURCE table and remove redundant rows oldRows = itsMS.source().nrow(); updateSource(); if(Table::isReadable(itsMS.sourceTableName())){ rownr_t removedRows = oldRows - itsMS.source().nrow(); if(removedRows>0){ log << "Removed " << removedRows << " redundant rows from the source subtable" << LogIO::POST; } } // merge ANTENNA and FEED oldRows = itsMS.antenna().nrow(); rownr_t oldFeedRows = itsMS.feed().nrow(); const Block newAntIndices = copyAntennaAndFeed(otherMS.antenna(), otherMS.feed()); { rownr_t addedRows = itsMS.antenna().nrow() - oldRows; rownr_t matchedRows = otherMS.antenna().nrow() - addedRows; log << "Added " << addedRows << " rows and matched " << matchedRows << " from the antenna subtable" << endl; addedRows = itsMS.feed().nrow() - oldFeedRows; log << "Added " << addedRows << " rows to the feed subtable" << endl; } //for(uInt ii=0; ii newFldIndices = copyField(otherMS); { const rownr_t addedRows = itsMS.field().nrow() - oldRows; const rownr_t matchedRows = otherMS.field().nrow() - addedRows; log << "Added " << addedRows << " rows and matched " << matchedRows << " from the field subtable" << LogIO::POST; if(matchedRows>0){ // may have to consolidate SOURCE IDs if(updateSource2()){ log << "Consolidated Source IDs in the source subtable." << LogIO::POST; } } } // OBSERVATION copyObservation(otherMS.observation(), True); // PROCESSOR copyProcessor(otherMS.processor(), True); // POINTING if(handling<2){ if(!copyPointing(otherMS.pointing(), newAntIndices)){ log << LogIO::WARN << "Could not merge Pointing subtables " << LogIO::POST ; } } else{ // delete the POINTING table log << LogIO::NORMAL << "Deleting all rows in the Pointing subtable ..." << LogIO::POST ; RowNumbers delrows(itsMS.pointing().nrow()); indgen(delrows); itsMS.pointing().removeRow(RowNumbers(delrows)); } // SYSCAL if(!copySysCal(otherMS.sysCal(), newAntIndices)){ log << LogIO::WARN << "Could not merge SysCal subtables " << LogIO::POST ; } // WEATHER if(!copyWeather(otherMS.weather(), newAntIndices)){ log << LogIO::WARN << "Could not merge Weather subtables " << LogIO::POST ; } // GAIN_CURVE if(!copyGainCurve(otherMS, newAntIndices)){ log << LogIO::WARN << "Could not merge GainCurve subtables " << LogIO::POST ; } // PHASE_CAL if(!copyPhaseCal(otherMS, newAntIndices)){ log << LogIO::WARN << "Could not merge PhaseCal subtables " << LogIO::POST ; } // EARTH_ORIENTATION if(!copyEOP(otherMS)){ log << LogIO::WARN << "Could not merge EOP subtables " << LogIO::POST ; } ////////////////////////////////////////////////////// MeasurementSet* destMS = 0; MeasurementSet tempMS; if(destMSName.empty()){ // no destination MS was given, write to the first MS destMS = &itsMS; } else{ // copy the structure of otherMS to destMSName, then copy the subtables of itsMS to it String absNewName = Path(destMSName).absoluteName(); { Table newtab = TableCopy::makeEmptyTable(absNewName, Record(), otherMS, Table::New, Table::AipsrcEndian, True, True); // noRows TableCopy::copyInfo (newtab, otherMS); TableCopy::copySubTables (newtab, itsMS, False); // noRows==False, i.e. subtables are copied } tempMS = MeasurementSet(destMSName, Table::Update); // open as the output MS for the new Main rows destMS = &tempMS; } // STOP HERE if Main is not to be modified if(handling==1 || handling==3){ return; } ////////////////////////////////////////////////////// MSMainColumns destMainCols(*destMS); // I need to check that the Measures and units are the same. const rownr_t newRows = otherMS.nrow(); rownr_t curRow = destMS->nrow(); if (!destMS->canAddRow()) { log << LogIO::WARN << "Can't add rows to this ms! Something is seriously wrong with " << destMS->tableName() << endl << LogIO::POST; } log << LogIO::DEBUG1 << "trying to add " << newRows << " data rows to the ms, now at: " << destMS->nrow() << endl << LogIO::POST; destMS->addRow(newRows); log << LogIO::DEBUG1 << "added " << newRows << " data rows to the ms, now at: " << destMS->nrow() << endl << LogIO::POST; // create column objects for those columns which need not be modified const ScalarColumn& otherTime = otherMainCols.time(); ScalarColumn& thisTime = destMainCols.time(); const ScalarColumn& otherInterval = otherMainCols.interval(); ScalarColumn& thisInterval = destMainCols.interval(); const ScalarColumn& otherExposure = otherMainCols.exposure(); ScalarColumn& thisExposure = destMainCols.exposure(); const ScalarColumn& otherTimeCen = otherMainCols.timeCentroid(); ScalarColumn& thisTimeCen = destMainCols.timeCentroid(); const ScalarColumn& otherArrayId = otherMainCols.arrayId(); ScalarColumn& thisArrayId = destMainCols.arrayId(); const ArrayColumn& otherFlag = otherMainCols.flag(); ArrayColumn& thisFlag = destMainCols.flag(); const ArrayColumn& otherFlagCat = otherMainCols.flagCategory(); ArrayColumn& thisFlagCat = destMainCols.flagCategory(); Bool copyFlagCat = !(thisFlagCat.isNull() || otherFlagCat.isNull()); copyFlagCat = copyFlagCat && thisFlagCat.isDefined(0) && otherFlagCat.isDefined(0); const ScalarColumn& otherFlagRow = otherMainCols.flagRow(); ScalarColumn& thisFlagRow = destMainCols.flagRow(); const ScalarColumn& otherFeed1 = otherMainCols.feed1(); ScalarColumn& thisFeed1 = destMainCols.feed1(); const ScalarColumn& otherFeed2 = otherMainCols.feed2(); ScalarColumn& thisFeed2 = destMainCols.feed2(); // create column objects for those columns which potentially need to be modified ArrayColumn otherData; ArrayColumn thisData; ArrayColumn otherFloatData; ArrayColumn thisFloatData; ArrayColumn otherModelData, otherCorrectedData; ArrayColumn thisModelData, thisCorrectedData; if(doFloatData){ thisFloatData.reference(destMainCols.floatData()); otherFloatData.reference(otherMainCols.floatData()); } else{ thisData.reference(destMainCols.data()); otherData.reference(otherMainCols.data()); } if(doCorrectedData){ thisCorrectedData.reference(destMainCols.correctedData()); otherCorrectedData.reference(otherMainCols.correctedData()); } if(doModelData){ thisModelData.reference(destMainCols.modelData()); otherModelData.reference(otherMainCols.modelData()); } const ScalarColumn& otherAnt1 = otherMainCols.antenna1(); ScalarColumn thisAnt1 = destMainCols.antenna1(); const ScalarColumn& otherAnt2 = otherMainCols.antenna2(); ScalarColumn thisAnt2 = destMainCols.antenna2(); const ScalarColumn& otherDDId = otherMainCols.dataDescId(); ScalarColumn thisDDId = destMainCols.dataDescId(); const ScalarColumn& otherFieldId = otherMainCols.fieldId(); ScalarColumn thisFieldId = destMainCols.fieldId(); const ArrayColumn& otherUvw = otherMainCols.uvw(); ArrayColumn thisUvw = destMainCols.uvw(); const ArrayColumn& otherWeight = otherMainCols.weight(); ArrayColumn thisWeight = destMainCols.weight(); const ArrayColumn& otherWeightSp = otherMainCols.weightSpectrum(); ArrayColumn thisWeightSp = destMainCols.weightSpectrum(); const ArrayColumn& otherSigma = otherMainCols.sigma(); ArrayColumn thisSigma = destMainCols.sigma(); const ArrayColumn& otherSigmaSp = otherMainCols.sigmaSpectrum(); ArrayColumn thisSigmaSp = destMainCols.sigmaSpectrum(); const ScalarColumn& otherScan = otherMainCols.scanNumber(); const ScalarColumn& otherStateId = otherMainCols.stateId(); const ScalarColumn& otherObsId=otherMainCols.observationId(); const ScalarColumn& otherProcId=otherMainCols.processorId(); ScalarColumn thisScan; ScalarColumn thisStateId; ScalarColumn thisObsId; ScalarColumn thisProcId; thisScan.reference(scanNumber()); thisStateId.reference(stateId()); thisObsId.reference(observationId()); thisProcId.reference(processorId()); Vector obsIds=otherObsId.getColumn(); if(doObsA_p){ // the obs ids changed for the first table Vector oldObsIds=thisObsId.getColumn(); for(rownr_t r = 0; r < curRow; r++) { if(newObsIndexA_p.find(oldObsIds[r]) != newObsIndexA_p.end()){ // apply change thisObsId.put(r, getMapValue (newObsIndexA_p, oldObsIds[r])); } } } Vector procIds=otherProcId.getColumn(); if(doProcA_p){ // the proc ids changed for the first table Vector oldProcIds=thisProcId.getColumn(); for(uInt r = 0; r < curRow; r++) { if(newProcIndexA_p.find(oldProcIds[r]) != newProcIndexA_p.end()){ // apply change thisProcId.put(r, getMapValue (newProcIndexA_p, oldProcIds[r])); } } } if(doState && otherStateNull){ // the state ids for the first table will have to be set to -1 for(rownr_t r = 0; r < curRow; r++) { thisStateId.put(r, -1); } } // SCAN NUMBER // find the distinct ObsIds in use in this MS // and the maximum scan and minimum scan ID in each of them std::map scanOffsetForOid; std::map encountered; vector distinctObsIdSet; vector minScan; vector maxScan; Int maxScanThis=0; for(rownr_t r = 0; r < curRow; r++) { Int oid = thisObsId(r); Int scanid = thisScan(r); Bool found = False; uInt i; for(i=0; imaxScan[i]){ maxScan[i] = scanid; } } else { distinctObsIdSet.push_back(oid); minScan.push_back(scanid); maxScan.push_back(scanid); } if(scanid>maxScanThis){ maxScanThis = scanid; } } // set the offset added to scan numbers in each observation Int defaultScanOffset=0; Int minScanOther = 0; { TableVector ScanTabVectOther(otherScan); minScanOther = min(ScanTabVectOther); defaultScanOffset = maxScanThis + 1 - minScanOther; if(defaultScanOffset<0){ defaultScanOffset=0; } } for(uInt i=0; i0.); Float sScale = 1.; // scale for SIGMA if (doWeightScale){ sScale = 1/sqrt(itsWeightScale); } const ROMSPolarizationColumns otherPolCols(otherMS.polarization()); const ROMSDataDescColumns otherDDCols(otherMS.dataDescription()); Int polId = -1; vector polSwap; for (rownr_t r = 0; r < newRows; r++, curRow++) { // Determine whether we need to swap rows in the visibility matrix // if we change the order of the antennas. This is done by // creating a mapping that makes sure the receptor numbers remain // correct when the antennas are swapped. /////uInt d = otherDDId(r); Int p = otherDDCols.polarizationId()(otherDDId(r)); if (p != polId) { const Matrix &products = otherPolCols.corrProduct()(p); polSwap.resize(products.shape()(1)); for (Int i = 0; i < products.shape()(1); i++) { for (Int j = 0; j < products.shape()(1); j++) { if (products(0, i) == products(1, j) && products(1, i) == products(0, j)) { polSwap[i] = j; break; } } } polId = p; } Int newA1 = newAntIndices[otherAnt1(r)]; Int newA2 = newAntIndices[otherAnt2(r)]; Bool doConjugateVis = False; if(newA1>newA2){ // swap indices and multiply UVW by -1 //cout << " corrected order r: " << r << " " << newA2 << " " << newA1 << endl; thisAnt1.put(curRow, newA2); thisAnt2.put(curRow, newA1); Array newUvw; newUvw.assign(otherUvw(r)); //cout << " old UVW " << newUvw; newUvw *= -1.; //cout << ", new UVW " << newUvw << endl; thisUvw.put(curRow, newUvw); doConjugateVis = True; } else{ thisAnt1.put(curRow, newA1); thisAnt2.put(curRow, newA2); thisUvw.put(curRow, otherUvw, r); } thisDDId.put(curRow, newDDIndices[otherDDId(r)]); thisFieldId.put(curRow, newFldIndices[otherFieldId(r)]); Int oid = 0; if(doObsB_p && newObsIndexB_p.find(obsIds[r]) != newObsIndexB_p.end()){ // the obs ids have been changed for the table to be appended oid = getMapValue(newObsIndexB_p, obsIds[r]); } else { // this OBS id didn't change oid = obsIds[r]; } thisObsId.put(curRow, oid); if(oid != obsIds[r]){ // obsid actually changed if(scanOffsetForOid.find(oid) == scanOffsetForOid.end()){ // offset not set, use default scanOffsetForOid[oid] = defaultScanOffset; } if(encountered.find(oid)==encountered.end() && scanOffsetForOid.at(oid)!=0){ log << LogIO::NORMAL << "Will offset scan numbers by " << scanOffsetForOid.at(oid) << " for observations with Obs ID " << oid << " in order to make scan numbers unique." << LogIO::POST; encountered[oid] = 0; } thisScan.put(curRow, otherScan(r) + scanOffsetForOid.at(oid)); } else{ thisScan.put(curRow, otherScan(r)); } Int procid = 0; if(doProcB_p && newProcIndexB_p.find(procIds[r]) != newProcIndexB_p.end()){ // the proc ids have been changed for the table to be appended procid = getMapValue(newProcIndexB_p, procIds[r]); } else { // this PROC id didn't change procid = procIds[r]; } thisProcId.put(curRow, procid); if(doState){ if(itsStateNull || otherStateNull){ thisStateId.put(curRow, -1); } else{ thisStateId.put(curRow, newStateIndices[otherStateId(r)]); } } else{ thisStateId.put(curRow, otherStateId, r); } if(itsChanReversed[otherDDId(r)]){ Vector datShape; Matrix reversedData; Matrix reversedFloatData; Matrix swappedData; if(doFloatData){ datShape=otherFloatData.shape(r).asVector(); reversedFloatData.resize(datShape[0], datShape[1]); } else{ datShape=otherData.shape(r).asVector(); reversedData.resize(datShape[0], datShape[1]); } Matrix reversedCorrData(datShape[0], datShape[1]); Matrix reversedModData(datShape[0], datShape[1]); for (Int k1=0; k1 < datShape[0]; ++k1){ for(Int k2=0; k2 < datShape[1]; ++k2){ if(doFloatData){ reversedFloatData(k1,k2)=(Matrix(otherFloatData(r)))(k1, datShape[1]-1-k2); } else{ reversedData(k1,k2)=(Matrix(otherData(r)))(k1, datShape[1]-1-k2); } if(doModelData){ reversedModData(k1,k2)=(Matrix(otherModelData(r)))(k1, datShape[1]-1-k2); } if(doCorrectedData){ reversedCorrData(k1,k2)=(Matrix(otherCorrectedData(r)))(k1, datShape[1]-1-k2); } } } if(doFloatData){ thisFloatData.put(curRow, reversedFloatData); } else{ if(doConjugateVis){ swappedData.resize(datShape[0], datShape[1]); for (Int p = 0; p < datShape[0]; p++) { swappedData.row(p) = (Matrix(reversedData)).row(polSwap[p]); } thisData.put(curRow, conj(swappedData)); } else{ thisData.put(curRow, reversedData); } } if(doCorrectedData){ if(doConjugateVis){ swappedData.resize(datShape[0], datShape[1]); for (Int p = 0; p < datShape[0]; p++) { swappedData.row(p) = (Matrix(reversedCorrData)).row(polSwap[p]); } thisCorrectedData.put(curRow, conj(swappedData)); } else{ thisCorrectedData.put(curRow, reversedCorrData); } } if(doModelData){ if(doConjugateVis){ swappedData.resize(datShape[0], datShape[1]); for (Int p = 0; p < datShape[0]; p++) { swappedData.row(p) = (Matrix(reversedModData)).row(polSwap[p]); } thisModelData.put(curRow, conj(swappedData)); } else{ thisModelData.put(curRow, reversedModData); } } } else{ // no reversal Vector datShape; Matrix swappedData; if(doFloatData){ thisFloatData.put(curRow, otherFloatData, r); } else{ if(doConjugateVis){ // conjugate because order of antennas was reversed datShape=otherData.shape(r).asVector(); swappedData.resize(datShape[0], datShape[1]); for (Int p = 0; p < datShape[0]; p++) { swappedData.row(p) = (Matrix(otherData(r))).row(polSwap[p]); } thisData.put(curRow, conj(swappedData)); } else{ thisData.put(curRow, otherData, r); } } if(doModelData){ if(doConjugateVis){ datShape=otherModelData.shape(r).asVector(); swappedData.resize(datShape[0], datShape[1]); for (Int p = 0; p < datShape[0]; p++) { swappedData.row(p) = (Matrix(otherModelData(r))).row(polSwap[p]); } thisModelData.put(curRow, conj(swappedData)); } else{ thisModelData.put(curRow, otherModelData, r); } } if(doCorrectedData){ if(doConjugateVis){ datShape=otherCorrectedData.shape(r).asVector(); swappedData.resize(datShape[0], datShape[1]); for (Int p = 0; p < datShape[0]; p++) { swappedData.row(p) = (Matrix(otherCorrectedData(r))).row(polSwap[p]); } thisCorrectedData.put(curRow, conj(swappedData)); } else{ thisCorrectedData.put(curRow, otherCorrectedData, r); } } } // end if itsChanReversed if(doWeightScale){ if(doConjugateVis){ Vector datShape=otherWeight.shape(r).asVector(); Vector swappedWeight(datShape[0]); for (Int p = 0; p < datShape[0]; p++) { swappedWeight(p) = (Vector(otherWeight(r)))(polSwap[p]); } thisWeight.put(curRow, swappedWeight*itsWeightScale); if (copyWtSp) { datShape.assign(otherWeightSp.shape(r).asVector()); Matrix swappedWeightSp(datShape[0], datShape[1]); for (Int p = 0; p < datShape[0]; p++) { swappedWeightSp.row(p) = (Matrix(otherWeightSp(r))).row(polSwap[p]); } thisWeightSp.put(curRow, swappedWeightSp*itsWeightScale); } datShape.assign(otherSigma.shape(r).asVector()); Vector swappedSigma(datShape[0]); for (Int p = 0; p < datShape[0]; p++) { swappedSigma(p) = (Vector(otherSigma(r)))(polSwap[p]); } thisSigma.put(curRow, swappedSigma*sScale); if (copySgSp) { datShape.assign(otherSigmaSp.shape(r).asVector()); Matrix swappedSigmaSp(datShape[0], datShape[1]); for (Int p = 0; p < datShape[0]; p++) { swappedSigmaSp.row(p) = (Matrix(otherSigmaSp(r))).row(polSwap[p]); } thisSigmaSp.put(curRow, swappedSigmaSp*sScale); } } else { thisWeight.put(curRow, otherWeight(r)*itsWeightScale); if (copyWtSp) thisWeightSp.put(curRow, otherWeightSp(r)*itsWeightScale); thisSigma.put(curRow, otherSigma(r)*sScale); if (copySgSp) thisSigmaSp.put(curRow, otherSigmaSp(r)*sScale); } } else{ if (doConjugateVis){ Vector datShape=otherWeight.shape(r).asVector(); Vector swappedWeight(datShape[0]); for (Int p = 0; p < datShape[0]; p++) { swappedWeight(p) = (Vector(otherWeight(r)))(polSwap[p]); } thisWeight.put(curRow, swappedWeight); if (copyWtSp) { datShape.assign(otherWeightSp.shape(r).asVector()); Matrix swappedWeightSp(datShape[0], datShape[1]); for (Int p = 0; p < datShape[0]; p++) { swappedWeightSp.row(p) = (Matrix(otherWeightSp(r))).row(polSwap[p]); } thisWeightSp.put(curRow, swappedWeightSp); } datShape.assign(otherSigma.shape(r).asVector()); Vector swappedSigma(datShape[0]); for (Int p = 0; p < datShape[0]; p++) { swappedSigma(p) = (Vector(otherSigma(r)))(polSwap[p]); } thisSigma.put(curRow, swappedSigma); if (copySgSp) { datShape.assign(otherSigmaSp.shape(r).asVector()); Matrix swappedSigmaSp(datShape[0], datShape[1]); for (Int p = 0; p < datShape[0]; p++) { swappedSigmaSp.row(p) = (Matrix(otherSigmaSp(r))).row(polSwap[p]); } thisSigmaSp.put(curRow, swappedSigmaSp); } } else{ thisWeight.put(curRow, otherWeight, r); if (copyWtSp) thisWeightSp.put(curRow, otherWeightSp, r); thisSigma.put(curRow, otherSigma, r); if (copySgSp) thisSigmaSp.put(curRow, otherSigmaSp, r); } } if(notYetFeedWarned && (otherFeed1(r)>0 || otherFeed2(r)>0)){ log << LogIO::WARN << "MS to be appended contains antennas with multiple feeds. Feed ID reindexing is not implemented.\n" << LogIO::POST; notYetFeedWarned = False; } if(doConjugateVis){ thisFeed1.put(curRow, otherFeed2, r); thisFeed2.put(curRow, otherFeed1, r); } else{ thisFeed1.put(curRow, otherFeed1, r); thisFeed2.put(curRow, otherFeed2, r); } thisTime.put(curRow, otherTime, r); thisInterval.put(curRow, otherInterval, r); thisExposure.put(curRow, otherExposure, r); thisTimeCen.put(curRow, otherTimeCen, r); thisArrayId.put(curRow, otherArrayId, r); if(doConjugateVis){ Vector datShape=otherFlag.shape(r).asVector(); Matrix swappedFlag(datShape[0], datShape[1]); for (Int p = 0; p < datShape[0]; p++) { swappedFlag.row(p) = (Matrix(otherFlag(r))).row(polSwap[p]); } thisFlag.put(curRow, swappedFlag); if (copyFlagCat) { datShape.assign(otherFlagCat.shape(r).asVector()); Cube swappedFlagCat(datShape[0], datShape[1], datShape[2]); for (Int p = 0; p < datShape[0]; p++) { swappedFlagCat.yzPlane(p) = (Cube(otherFlagCat(r))).yzPlane(polSwap[p]); } thisFlagCat.put(curRow, swappedFlagCat); } } else{ thisFlag.put(curRow, otherFlag, r); if (copyFlagCat) thisFlagCat.put(curRow, otherFlagCat, r); } thisFlagRow.put(curRow, otherFlagRow, r); } // end for if(doModelData){ //update the MODEL_DATA keywords updateModelDataKeywords(*destMS); } } //----------------------------------------------------------------------- void MSConcat::setTolerance(Quantum& freqTol, Quantum& dirTol){ itsFreqTol=freqTol; itsDirTol=dirTol; } void MSConcat::setWeightScale(const Float weightScale){ if(weightScale<0){ throw(AipsError(String("MSConcat::setWeightScale: weight scale must be >= 0."))); } itsWeightScale=weightScale; } void MSConcat::setRespectForFieldName(const Bool respectFieldName){ itsRespectForFieldName = respectFieldName; } void MSConcat::checkShape(const IPosition& otherShape) const { const uInt nAxes = std::min(itsFixedShape.nelements(), otherShape.nelements()); DebugAssert(nAxes > 0 && nAxes < 4, AipsError); if (nAxes > 1 && itsFixedShape(1) != otherShape(1)) { throw(AipsError(String("MSConcat::checkShapes\n") + String("cannot concatenate this measurement set as ") + String("it has a different number of channels\n") + String("and this cannot be changed"))); } if (itsFixedShape(0) != otherShape(0)) { throw(AipsError(String("MSConcat::checkShapes\n") + String("cannot concatenate this measurement set as ") + String("it has a different number of correlations\n") + String("and this cannot be changed"))); } } IPosition MSConcat::getShape(const MSDataDescColumns& ddCols, const MSSpWindowColumns& spwCols, const MSPolarizationColumns& polCols, uInt whichShape) { DebugAssert(whichShape < ddCols.nrow(), AipsError); const Int polId = ddCols.polarizationId()(whichShape); DebugAssert(polId >= 0 && polId < static_cast(polCols.nrow()), AipsError); const Int spwId = ddCols.spectralWindowId()(whichShape); DebugAssert(spwId >= 0 && spwId < static_cast(spwCols.nrow()), AipsError); const Int nCorr = polCols.numCorr()(polId); DebugAssert(nCorr > 0, AipsError); const Int nChan = spwCols.numChan()(spwId); DebugAssert(nChan > 0, AipsError); return IPosition(2, nCorr, nChan); } void MSConcat::checkCategories(const MSMainColumns& otherCols) const { LogIO os(LogOrigin("MSConcat", "checkCategories")); const Vector cat = flagCategories(); const Vector otherCat = otherCols.flagCategories(); const uInt nCat = cat.nelements(); if (nCat != otherCat.nelements()) { os << LogIO::WARN <<"Flag category column shape does not match in these two MSs.\n" <<"This may not be important as Flag category is being deprecated. Will try to continue ..." << LogIO::POST; return; } for (uInt c = 0; c < nCat; c++) { if (cat(c) != otherCat(c)) { os << LogIO::WARN <<"Flag category column shape does not match in these two MSs.\n" <<"This may not be important as Flag category is being deprecated. Will try to continue ..." << LogIO::POST; return; } } } Bool MSConcat::copyPointing(const MSPointing& otherPoint,const Block& newAntIndices ){ LogIO os(LogOrigin("MSConcat", "copyPointing")); Bool itsPointingNull = (itsMS.pointing().isNull() || (itsMS.pointing().nrow() == 0)); Bool otherPointingNull = (otherPoint.isNull() || (otherPoint.nrow() == 0)); if(itsPointingNull && otherPointingNull){ // neither of the two MSs do have valid pointing tables os << LogIO::NORMAL << "No valid pointing tables present. Result won't have one either." << LogIO::POST; return True; } else if(itsPointingNull && !otherPointingNull){ os << LogIO::WARN << itsMS.tableName() << " does not have a valid pointing table," << endl << " the MS to be appended, however, has one. Result won't have one." << LogIO::POST; return False; } else if(!itsPointingNull && otherPointingNull){ os << LogIO::WARN << "MS to be appended does not have a valid pointing table, " << itsMS.tableName() << ", however, has one. Result won't have one." << LogIO::POST; RowNumbers delrows(itsMS.pointing().nrow()); indgen(delrows); itsMS.pointing().removeRow(RowNumbers(delrows)); return False; } MSPointing& point=itsMS.pointing(); Int actualRow=point.nrow()-1; Int origNRow= actualRow+1; Int rowToBeAdded=otherPoint.nrow(); TableRow pointRow(point); const ROTableRow otherPointRow(otherPoint); for (Int k=0; k < rowToBeAdded; ++k){ ++actualRow; point.addRow(); pointRow.put(actualRow, otherPointRow.get(k, True)); } //Now reassigning antennas to the new indices of the ANTENNA table if(rowToBeAdded > 0){ MSPointingColumns pointCol(point); // check antenna IDs Vector antennaIDs=pointCol.antennaId().getColumn(); Bool idsOK = True; Int maxID = static_cast(newAntIndices.nelements()) - 1; for (Int k=origNRow; k < (origNRow+rowToBeAdded); ++k){ if(antennaIDs[k] < 0 || antennaIDs[k] > maxID){ idsOK = False; break; } } if(!idsOK){ os << LogIO::WARN << "Found invalid antenna ids in the POINTING table; the POINTING table will be emptied as it is inconsistent" << LogIO::POST; RowNumbers rowtodel(point.nrow()); indgen(rowtodel); point.removeRow(RowNumbers(rowtodel)); return False; } for (Int k=origNRow; k < (origNRow+rowToBeAdded); ++k){ pointCol.antennaId().put(k, newAntIndices[antennaIDs[k]]); } } return True; } Bool MSConcat::copyPointingB(MSPointing& otherPoint,const Block& newAntIndices ){ // prepare otherPoint such that it can be virtually concatenated later // (don't write the itsMS) LogIO os(LogOrigin("MSConcat", "copyPointing")); Bool itsPointingNull = (itsMS.pointing().isNull() || (itsMS.pointing().nrow() == 0)); Bool otherPointingNull = (otherPoint.isNull() || (otherPoint.nrow() == 0)); if(itsPointingNull && otherPointingNull){ // neither of the two MSs do have valid pointing tables os << LogIO::NORMAL << "No valid pointing tables present. Result won't have one either." << LogIO::POST; return True; } else if(itsPointingNull && !otherPointingNull){ os << LogIO::WARN << itsMS.tableName() << " does not have a valid pointing table," << endl << " the MS to be appended, however, has one. Result won't have one." << LogIO::POST; RowNumbers delrows(otherPoint.nrow()); indgen(delrows); otherPoint.removeRow(RowNumbers(delrows)); return False; } // else if(!itsPointingNull && otherPointingNull){ // os << LogIO::NORMAL << "MS to be appended does not have a valid pointing table, " // << itsMS.tableName() << ", however, has one. Result won't have one." << LogIO::POST; // RowNumbers delrows(itsMS.pointing().nrow()); // indgen(delrows); // itsMS.pointing().removeRow(delrows); // return False; // } Int rowToBeAdded=otherPoint.nrow(); //reassigning antennas to the new indices of the ANTENNA table if(rowToBeAdded > 0){ MSPointingColumns pointCol(otherPoint); // check antenna IDs Vector antennaIDs=pointCol.antennaId().getColumn(); Bool idsOK = True; Int maxID = static_cast(newAntIndices.nelements()) - 1; for (Int k=0; k < rowToBeAdded; k++){ if(antennaIDs[k] < 0 || antennaIDs[k] > maxID){ idsOK = False; break; } else{ antennaIDs[k] = newAntIndices[antennaIDs[k]]; } } if(!idsOK){ os << LogIO::WARN << "Found invalid antenna ids in the POINTING table; the POINTING table will be emptied as it is inconsistent" << LogIO::POST; RowNumbers delrows(itsMS.pointing().nrow()); indgen(delrows); itsMS.pointing().removeRow(RowNumbers(delrows)); RowNumbers rowtodel(otherPoint.nrow()); indgen(rowtodel); otherPoint.removeRow(RowNumbers(rowtodel)); return False; } pointCol.antennaId().putColumn(antennaIDs); } return True; } Bool MSConcat::copySysCal(const MSSysCal& otherSysCal, const Block& newAntIndices){ LogIO os(LogOrigin("MSConcat", "copySysCal")); Bool itsSysCalNull = (itsMS.sysCal().isNull() || (itsMS.sysCal().nrow() == 0)); Bool otherSysCalNull = (otherSysCal.isNull() || (otherSysCal.nrow() == 0)); if(itsSysCalNull && otherSysCalNull){ // neither of the two MSs do have valid syscal tables os << LogIO::NORMAL << "No valid syscal tables present. Result won't have one either." << LogIO::POST; return True; } else if(itsSysCalNull && !otherSysCalNull){ os << LogIO::WARN << itsMS.tableName() << " does not have a valid syscal table," << endl << " the MS to be appended, however, has one. Result won't have one." << LogIO::POST; return False; } MSSysCal& sysCal=itsMS.sysCal(); Int actualRow=sysCal.nrow()-1; Int origNRow=actualRow+1; Int rowToBeAdded=otherSysCal.nrow(); TableRow sysCalRow(sysCal); const ROTableRow otherSysCalRow(otherSysCal); for (Int k=0; k < rowToBeAdded; ++k){ ++actualRow; sysCal.addRow(); sysCalRow.put(actualRow, otherSysCalRow.get(k, True)); } //Now reassigning antennas to the new indices of the ANTENNA table if(rowToBeAdded > 0){ MSSysCalColumns sysCalCol(sysCal); // check antenna IDs Vector antennaIDs=sysCalCol.antennaId().getColumn(); Bool idsOK = True; Int maxID = static_cast(newAntIndices.nelements()) - 1; for (Int k=origNRow; k < (origNRow+rowToBeAdded); ++k){ if(antennaIDs[k] < 0 || antennaIDs[k] > maxID){ idsOK = False; break; } } if(!idsOK){ os << LogIO::WARN << "Found invalid antenna ids in the SYSCAL table; the SYSCAL table will be emptied as it is inconsistent" << LogIO::POST; RowNumbers rowtodel(sysCal.nrow()); indgen(rowtodel); sysCal.removeRow(RowNumbers(rowtodel)); return False; } for (Int k=origNRow; k < (origNRow+rowToBeAdded); ++k){ sysCalCol.antennaId().put(k, newAntIndices[antennaIDs[k]]); } } return True; } Bool MSConcat::copyWeather(const MSWeather& otherWeather, const Block& newAntIndices){ LogIO os(LogOrigin("MSConcat", "copyWeather")); Bool itsWeatherNull = (itsMS.weather().isNull() || (itsMS.weather().nrow() == 0)); Bool otherWeatherNull = (otherWeather.isNull() || (otherWeather.nrow() == 0)); if(itsWeatherNull && otherWeatherNull){ // neither of the two MSs do have valid weather tables os << LogIO::NORMAL << "No valid weather tables present. Result won't have one either." << LogIO::POST; return True; } else if(itsWeatherNull && !otherWeatherNull){ os << LogIO::WARN << itsMS.tableName() << " does not have a valid weather table," << endl << " the MS to be appended, however, has one. Result won't have one." << LogIO::POST; return False; } MSWeather& weather=itsMS.weather(); Int actualRow=weather.nrow()-1; Int origNRow=actualRow+1; Int rowToBeAdded=otherWeather.nrow(); TableRow weatherRow(weather); const ROTableRow otherWeatherRow(otherWeather); for (Int k=0; k < rowToBeAdded; ++k){ ++actualRow; weather.addRow(); weatherRow.put(actualRow, otherWeatherRow.get(k, True)); } //Reassign antennas to the new indices of the ANTENNA table if(rowToBeAdded > 0){ MSWeatherColumns weatherCol(weather); // check antenna IDs Vector antennaIDs=weatherCol.antennaId().getColumn(); Bool idsOK = True; Int maxID = static_cast(newAntIndices.nelements()) - 1; for (Int k=origNRow; k < (origNRow+rowToBeAdded); ++k){ if(antennaIDs[k] < -1 || antennaIDs[k] > maxID){ os << LogIO::WARN << "Found invalid antenna id " << antennaIDs[k] << " in the WEATHER table; the WEATHER table will be emptied as it is inconsistent" << LogIO::POST; idsOK = False; break; } //// the following could be commented in if a warning about undefined antenna ids was deemed useful // else if(antennaIDs[k] == -1){ // os << LogIO::WARN // << "Found undefined antenna ids (value -1) in the WEATHER table; these will not be reindexed." // << LogIO::POST; // break; // } } if(!idsOK){ RowNumbers rowstodel(weather.nrow()); indgen(rowstodel); weather.removeRow(RowNumbers(rowstodel)); return False; } for (Int k=origNRow; k < (origNRow+rowToBeAdded); ++k){ if(antennaIDs[k]>-1){ weatherCol.antennaId().put(k, newAntIndices[antennaIDs[k]]); } } } return True; } Bool MSConcat::copyGainCurve(const MeasurementSet& otherMS, const Block& newAntIndices){ // uses newSPWIndex_p; to be called after copySpwAndPol LogIO os(LogOrigin("MSConcat", "copyGainCurve")); Bool itsGainCurveNull = (!itsMS.rwKeywordSet().isDefined("GAIN_CURVE") || (itsMS.rwKeywordSet().asTable("GAIN_CURVE").nrow() == 0)); Bool otherGainCurveNull = (!otherMS.keywordSet().isDefined("GAIN_CURVE") || (otherMS.keywordSet().asTable("GAIN_CURVE").nrow() == 0)); if(itsGainCurveNull && otherGainCurveNull){ // neither of the two MSs do have valid gain curve tables os << LogIO::NORMAL << "No valid gain curve tables present. Result won't have one either." << LogIO::POST; return True; } else if(itsGainCurveNull && !otherGainCurveNull){ os << LogIO::WARN << itsMS.tableName() << " does not have a valid gain curve table," << endl << " the MS to be appended, however, has one. Result won't have one." << LogIO::POST; return False; } if (otherGainCurveNull) return True; Table gainCurve = itsMS.rwKeywordSet().asTable("GAIN_CURVE"); Table otherGainCurve = otherMS.keywordSet().asTable("GAIN_CURVE"); Int actualRow=gainCurve.nrow()-1; Int origNRow=actualRow+1; Int rowToBeAdded=otherGainCurve.nrow(); TableRow gainCurveRow(gainCurve); const ROTableRow otherGainCurveRow(otherGainCurve); for (Int k=0; k < rowToBeAdded; ++k){ ++actualRow; gainCurve.addRow(); gainCurveRow.put(actualRow, otherGainCurveRow.get(k, True)); } //Now reassigning antennas to the new indices of the ANTENNA table if(rowToBeAdded > 0){ ScalarColumn antCol(gainCurve, "ANTENNA_ID"); // check antenna IDs Vector antennaIDs=antCol.getColumn(); Bool idsOK = True; Int maxID = static_cast(newAntIndices.nelements()) - 1; for (Int k=origNRow; k < (origNRow+rowToBeAdded); ++k){ if(antennaIDs[k] < 0 || antennaIDs[k] > maxID){ idsOK = False; break; } } if(!idsOK){ os << LogIO::WARN << "Found invalid antenna ids in the GAIN_CURVE table; the GAIN_CURVE table will be emptied as it is inconsistent" << LogIO::POST; RowNumbers rowtodel(gainCurve.nrow()); indgen(rowtodel); gainCurve.removeRow(RowNumbers(rowtodel)); return False; } for (Int k=origNRow; k < (origNRow+rowToBeAdded); ++k){ antCol.put(k, newAntIndices[antennaIDs[k]]); } } //Now reassigning SPWs to the new indices of the SPECTRAL_WINDOW table if(doSPW_p && rowToBeAdded > 0){ ScalarColumn spwCol(gainCurve, "SPECTRAL_WINDOW_ID"); // check SPW IDs Vector spwIDs=spwCol.getColumn(); for (Int k=origNRow; k < (origNRow+rowToBeAdded); ++k){ if(newSPWIndex_p.find(spwIDs[k]) != newSPWIndex_p.end()) spwCol.put(k, getMapValue(newSPWIndex_p, spwIDs[k])); } } return True; } Bool MSConcat::copyPhaseCal(const MeasurementSet& otherMS, const Block& newAntIndices){ // uses newSPWIndex_p; to be called after copySpwAndPol LogIO os(LogOrigin("MSConcat", "copyPhaseCal")); Bool itsPhaseCalNull = (!itsMS.rwKeywordSet().isDefined("PHASE_CAL") || (itsMS.rwKeywordSet().asTable("PHASE_CAL").nrow() == 0)); Bool otherPhaseCalNull = (!otherMS.keywordSet().isDefined("PHASE_CAL") || (otherMS.keywordSet().asTable("PHASE_CAL").nrow() == 0)); if(itsPhaseCalNull && otherPhaseCalNull){ // neither of the two MSs do have valid gain curve tables os << LogIO::NORMAL << "No valid gain curve tables present. Result won't have one either." << LogIO::POST; return True; } else if(itsPhaseCalNull && !otherPhaseCalNull){ os << LogIO::WARN << itsMS.tableName() << " does not have a valid gain curve table," << endl << " the MS to be appended, however, has one. Result won't have one." << LogIO::POST; return False; } if (otherPhaseCalNull) return True; Table phaseCal = itsMS.rwKeywordSet().asTable("PHASE_CAL"); Table otherPhaseCal = otherMS.keywordSet().asTable("PHASE_CAL"); Int actualRow=phaseCal.nrow()-1; Int origNRow=actualRow+1; Int rowToBeAdded=otherPhaseCal.nrow(); TableRow phaseCalRow(phaseCal); const ROTableRow otherPhaseCalRow(otherPhaseCal); for (Int k=0; k < rowToBeAdded; ++k){ ++actualRow; phaseCal.addRow(); phaseCalRow.put(actualRow, otherPhaseCalRow.get(k, True)); } //Now reassigning antennas to the new indices of the ANTENNA table if(rowToBeAdded > 0){ ScalarColumn antCol(phaseCal, "ANTENNA_ID"); // check antenna IDs Vector antennaIDs=antCol.getColumn(); Bool idsOK = True; Int maxID = static_cast(newAntIndices.nelements()) - 1; for (Int k=origNRow; k < (origNRow+rowToBeAdded); ++k){ if(antennaIDs[k] < 0 || antennaIDs[k] > maxID){ idsOK = False; break; } } if(!idsOK){ os << LogIO::WARN << "Found invalid antenna ids in the PHASE_CAL table; the PHASE_CAL table will be emptied as it is inconsistent" << LogIO::POST; RowNumbers rowtodel(phaseCal.nrow()); indgen(rowtodel); phaseCal.removeRow(RowNumbers(rowtodel)); return False; } for (Int k=origNRow; k < (origNRow+rowToBeAdded); ++k){ antCol.put(k, newAntIndices[antennaIDs[k]]); } } //Now reassigning SPWs to the new indices of the SPECTRAL_WINDOW table if(doSPW_p && rowToBeAdded > 0){ ScalarColumn spwCol(phaseCal, "SPECTRAL_WINDOW_ID"); // check SPW IDs Vector spwIDs=spwCol.getColumn(); for (Int k=origNRow; k < (origNRow+rowToBeAdded); ++k){ if(newSPWIndex_p.find(spwIDs[k]) != newSPWIndex_p.end()) spwCol.put(k, getMapValue(newSPWIndex_p, spwIDs[k])); } } return True; } Bool MSConcat::copyEOP(const MeasurementSet& otherMS){ // uses newSPWIndex_p; to be called after copySpwAndPol LogIO os(LogOrigin("MSConcat", "copyEOP")); Bool itsEOPNull = (!itsMS.rwKeywordSet().isDefined("EARTH_ORIENTATION") || (itsMS.rwKeywordSet().asTable("EARTH_ORIENTATION").nrow() == 0)); Bool otherEOPNull = (!otherMS.keywordSet().isDefined("EARTH_ORIENTATION") || (otherMS.keywordSet().asTable("EARTH_ORIENTATION").nrow() == 0)); if(itsEOPNull && otherEOPNull){ // neither of the two MSs do have valid EOP tables os << LogIO::DEBUG1 << "No valid EOP tables present. Result won't have one either." << LogIO::POST; return True; } else if(itsEOPNull && !otherEOPNull){ os << LogIO::WARN << itsMS.tableName() << " does not have a valid EOP table," << endl << " the MS to be appended, however, has one. Result won't have one." << LogIO::POST; return False; } if (otherEOPNull) return True; Table eop = itsMS.rwKeywordSet().asTable("EARTH_ORIENTATION"); Table otherEOP = otherMS.keywordSet().asTable("EARTH_ORIENTATION"); rownr_t actualRow=eop.nrow()-1; rownr_t origNRow=actualRow+1; Int rowToBeAdded=otherEOP.nrow(); TableRow eopRow(eop); const ROTableRow otherEOPRow(otherEOP); for (Int k=0; k < rowToBeAdded; ++k){ ++actualRow; eop.addRow(); eopRow.put(actualRow, otherEOPRow.get(k, True)); } ScalarColumn obsIdCol(eop, "OBSERVATION_ID"); Vector obsIds = obsIdCol.getColumn(); if (doObsA_p){ for (rownr_t r = 0; r < origNRow; r++) { if(newObsIndexA_p.find(obsIds[r]) != newObsIndexA_p.end()){ obsIds[r] = getMapValue(newObsIndexA_p, obsIds[r]); } } obsIdCol.putColumn(obsIds); } if (rowToBeAdded && doObsB_p){ for (rownr_t r = origNRow; r < eop.nrow(); r++) { if(newObsIndexB_p.find(obsIds[r]) != newObsIndexB_p.end()){ obsIds[r] = getMapValue(newObsIndexB_p, obsIds[r]); } } obsIdCol.putColumn(obsIds); } return True; } Int MSConcat::copyObservation(const MSObservation& otherObs, const Bool remRedunObsId){ LogIO os(LogOrigin("MSConcat", "copyObservation")); MSObservation& obs=itsMS.observation(); TableRow obsRow(obs); const ROTableRow otherObsRow(otherObs); newObsIndexA_p.clear(); newObsIndexB_p.clear(); std::map tempObsIndex; std::map tempObsIndexReverse; std::map tempObsIndex2; doObsA_p = False; doObsB_p = True; Int originalNrow = obs.nrow(); // remember the original number of rows // copy the new obs rows over and note new ids in map Int actualRow=obs.nrow()-1; for (rownr_t k=0; k < otherObs.nrow() ; ++k){ obs.addRow(); ++actualRow; obsRow.put(actualRow, otherObsRow.get(k, True)); tempObsIndex[k] = actualRow; tempObsIndexReverse[actualRow] = k; } if(remRedunObsId){ // remove redundant rows MSObservationColumns& obsCol = observation(); Vector rowToBeRemoved(obs.nrow(), False); vector rowsToBeRemoved; for(rownr_t j=0; j0){ // actually remove the rows Vector rowsTBR(rowsToBeRemoved); obs.removeRow(rowsTBR); } os << "Added " << obs.nrow()- originalNrow << " rows and matched " << rowsToBeRemoved.size() << " rows in the observation subtable." << LogIO::POST; } else { // create map for second table only for(rownr_t i=0; i tempProcIndex; std::map tempProcIndex2; doProcA_p = False; doProcB_p = True; Int originalNrow = proc.nrow(); // remember the original number of rows // copy the new proc rows over and note new ids in map Int actualRow=proc.nrow()-1; for (uInt k=0; k < otherProc.nrow() ; ++k){ proc.addRow(); ++actualRow; procRow.put(actualRow, otherProcRow.get(k, True)); tempProcIndex[k] = actualRow; } if(remRedunProcId){ // remove redundant rows MSProcessorColumns& procCol = processor(); Vector rowToBeRemoved(proc.nrow(), False); vector rowsToBeRemoved; for(uInt j=0; j0){ // make entry in map for (j,j-removedSoFar), i.e. move j up by removeSoFar tempProcIndex2[j] = j-removedSoFar; } } else { removedSoFar++; } } } // create final maps // map for first table for(Int i=0; i0){ // actually remove the rows Vector rowsTBR(rowsToBeRemoved); proc.removeRow(rowsTBR); } os << "Added " << proc.nrow()- originalNrow << " rows and matched " << rowsToBeRemoved.size() << " rows in the processor subtable." << LogIO::POST; } else { // create map for second table only for(uInt i=0; i MSConcat::copyAntennaAndFeed(const MSAntenna& otherAnt, const MSFeed& otherFeed) { // uses newSPWIndex_p; to be called after copySpwAndPol LogIO os(LogOrigin("MSConcat", "copyAntennaAndFeed")); const uInt nAntIds = otherAnt.nrow(); Block antMap(nAntIds); const MSAntennaColumns otherAntCols(otherAnt); MSAntennaColumns& antCols = antenna(); MSAntenna& ant = itsMS.antenna(); const Quantum tol(1, "m"); const ROTableRow otherAntRow(otherAnt); TableRow antRow(ant); TableRecord antRecord; //RecordFieldId nameAnt(MSAntenna::columnName(MSAntenna::NAME)); MSFeedColumns& feedCols = feed(); const MSFeedColumns otherFeedCols(otherFeed); const String& antIndxName = MSFeed::columnName(MSFeed::ANTENNA_ID); const String& spwIndxName = MSFeed::columnName(MSFeed::SPECTRAL_WINDOW_ID); MSFeed& feed = itsMS.feed(); const ROTableRow otherFeedRow(otherFeed); TableRow feedRow(feed); TableRecord feedRecord, feedRecord2; ColumnsIndex feedIndex(otherFeed, Vector(1, antIndxName)); ColumnsIndex itsFeedIndex(feed, Vector(1, antIndxName)); RecordFieldPtr antInd(feedIndex.accessKey(), antIndxName); RecordFieldPtr itsAntInd(itsFeedIndex.accessKey(), antIndxName); RecordFieldId antField(antIndxName); RecordFieldId spwField(spwIndxName); if(!feedCols.focusLengthQuant().isNull() && otherFeedCols.focusLengthQuant().isNull()){ os << LogIO::WARN << "MS appended to has optional column FOCUS_LENGTH in FEED table, but MS to be appended does not.\n" << "Potential new rows in FEED will have FOCUS_LENGTH zero." << LogIO::POST; } else if(feedCols.focusLengthQuant().isNull() && !otherFeedCols.focusLengthQuant().isNull()){ os << LogIO::WARN << "MS appended to does not have optional column FOCUS_LENGTH in FEED table, but MS to be appended does.\n" << "Output FEED table will not have a FOCUS_LENGTH column." << LogIO::POST; } for (uInt a = 0; a < nAntIds; a++) { const Int newAntId = antCols.matchAntennaAndStation(otherAntCols.name()(a), otherAntCols.station()(a), otherAntCols.positionMeas()(a), tol); Bool addNewEntry = True; if (newAntId >= 0) { // Check that the FEED table contains all the entries for // this antenna and that they are the same. *antInd = a; *itsAntInd = newAntId; const Vector feedsToCompare = feedIndex.getRowNumbers(); const Vector itsFeedsToCompare = itsFeedIndex.getRowNumbers(); const uInt nFeedsToCompare = feedsToCompare.nelements(); uInt matchingFeeds = 0; RowNumbers ignoreRows; Unit s("s"); Unit m("m"); if(itsFeedsToCompare.nelements() == nFeedsToCompare){ //cout << "Antenna " << a << " same number of feeds: "<< nFeedsToCompare << endl; for(uInt f=0; f newTimeQ; Quantum newIntervalQ; Int newSPWId = otherFeedCols.spectralWindowId()(k); if(doSPW_p){ // the SPW table was rearranged //cout << "modifiying spwid from " << newSPWId << " to " << newSPWIndex_p.at(newSPWId) << endl; newSPWId = getMapValue (newSPWIndex_p, newSPWId); } Quantum fLengthQ; if(!otherFeedCols.focusLengthQuant().isNull()){ fLengthQ = otherFeedCols.focusLengthQuant()(k); } const Int matchingFeedRow = feedCols.matchFeed(newTimeQ, newIntervalQ, a, otherFeedCols.feedId()(k), newSPWId, otherFeedCols.timeQuant()(k), otherFeedCols.intervalQuant()(k), otherFeedCols.numReceptors()(k), otherFeedCols.beamOffsetQuant()(k), otherFeedCols.polarizationType()(k), otherFeedCols.polResponse()(k), otherFeedCols.positionQuant()(k), otherFeedCols.receptorAngleQuant()(k), ignoreRows, fLengthQ ); if(matchingFeedRow>=0){ //cout << "Antenna " << a << " found matching feed " << matchingFeedRow << endl; if(newTimeQ.getValue(s)!=0.){ // need to adjust time information // cout << "this " << feedCols.timeQuant()(matchingFeedRow).getValue(s) << " " // << feedCols.intervalQuant()(matchingFeedRow).getValue(s) << endl; // cout << " other " << otherFeedCols.timeQuant()(k).getValue(s) << " " // << otherFeedCols.intervalQuant()(k).getValue(s) << endl; // cout << " new " << newTimeQ.getValue(s) << " " << newIntervalQ.getValue(s) << endl; // modify matchingFeedRow feedCols.timeQuant().put(matchingFeedRow, newTimeQ); feedCols.intervalQuant().put(matchingFeedRow, newIntervalQ); } matchingFeeds++; ignoreRows.resize(matchingFeeds, True); ignoreRows(matchingFeeds-1) = matchingFeedRow; } } } antMap[a] = newAntId; addNewEntry = False; if(matchingFeeds != nFeedsToCompare){ // cout << "Antenna " << a << " did not find all needed feeds " // << matchingFeeds << "/" << nFeedsToCompare << endl; const Vector feedsToCopy = feedIndex.getRowNumbers(); const uInt nFeedsToCopy = feedsToCopy.nelements(); rownr_t destRow = feed.nrow(); uInt rCount = 0; for (uInt f = 0; f < nFeedsToCopy; f++) { Bool present=False; for(uInt g=0; g(antMap[a])); if(doSPW_p){ // the SPW table was rearranged Int newSPWId = otherFeedCols.spectralWindowId()(feedsToCopy(f)); // cout << "When writing new feed row: modifiying spwid from " << newSPWId // << " to " << newSPWIndex_p.at(newSPWId) << endl; feedRecord.define(spwField, getMapValue(newSPWIndex_p, newSPWId)); } feedRow.putMatchingFields(destRow, feedRecord); rCount++; destRow++; } } // cout << "Added " << rCount << " rows to the Feed table." << endl; } // else{ // cout << "Antenna " << a << " found all matching feeds: " << matchingFeeds << endl; // } } if(addNewEntry){ // need to add a new entry in the ANTENNA subtable antMap[a] = ant.nrow(); ant.addRow(); antRecord = otherAntRow.get(a); // determine if the antenna was just moved Int movedAntId=-1; if( (movedAntId=antCols.matchAntenna(otherAntCols.name()(a), otherAntCols.positionMeas()(a), Quantum(100, "AU"))) >= 0){ os << "*** Antenna " << antCols.name()(movedAntId) << " (station " << antCols.station()(movedAntId) << ", ID " << movedAntId << ") has changed its position between MSs." << endl << " Moved antenna is on station " << otherAntCols.station()(a) << " and will have ID " << antMap[a] << "." << LogIO::POST; // String newName = otherAntCols.name()(a)+"m"; // Int secondMovedAntId = -1; // Int count = 1; // while((secondMovedAntId=antCols.matchAntenna(newName, // otherAntCols.positionMeas()(a), Quantum(100, "AU"))) // >= 0){ // append numbers starting at 2 until there is no match // newName = newName+String::toString(++count); // movedAntId = secondMovedAntId; // } // os << "Antenna " << antCols.name()(movedAntId) << " (ID " << movedAntId << ") has changed its position between MSs." // << " Moved antenna will be named " << newName << " (ID " << antMap[a] << ")" << LogIO::POST; // antRecord.define(nameAnt, newName); // append an "m" to the name to make it unique } antRow.putMatchingFields(antMap[a], antRecord); // Copy all the feeds associated with the antenna into the feed // table. I'm assuming that they are not already there. *antInd = a; const Vector feedsToCopy = feedIndex.getRowNumbers(); const uInt nFeedsToCopy = feedsToCopy.nelements(); rownr_t destRow = feed.nrow(); feed.addRow(nFeedsToCopy); //cout << "antenna " << antMap[a] << ": copying " << nFeedsToCopy << " feeds." << endl; for (uInt f = 0; f < nFeedsToCopy; f++, destRow++) { feedRecord = otherFeedRow.get(feedsToCopy(f)); feedRecord.define(antField, static_cast(antMap[a])); Int newSPWId = otherFeedCols.spectralWindowId()(feedsToCopy(f)); if(doSPW_p){ // the SPW table was rearranged //cout << "modifiying spwid from " << newSPWId << " to " << newSPWIndex_p.at(newSPWId) << endl; newSPWId = getMapValue(newSPWIndex_p, newSPWId); feedRecord.define(spwField, newSPWId); } feedRow.putMatchingFields(destRow, feedRecord); } } } return antMap; } Block MSConcat::copyState(const MSState& otherState) { const uInt nStateIds = otherState.nrow(); Block stateMap(nStateIds); const MSStateColumns otherStateCols(otherState); MSStateColumns& stateCols = state(); MSState& stateT = itsMS.state(); const ROTableRow otherStateRow(otherState); TableRow stateRow(stateT); const Quantum tol(1, "K"); for (uInt s = 0; s < nStateIds; s++) { const Int newStateId = stateCols.matchState(otherStateCols.calQuant()(s), otherStateCols.loadQuant()(s), otherStateCols.obsMode()(s), otherStateCols.ref()(s), otherStateCols.sig()(s), otherStateCols.subScan()(s), tol); if (newStateId >= 0) { stateMap[s] = newStateId; } else { // need to add a new entry in the STATE subtable stateMap[s] = stateT.nrow(); stateT.addRow(); stateRow.putMatchingFields(stateMap[s], otherStateRow.get(s)); } } return stateMap; } Block MSConcat::copyField(const MeasurementSet& otherms) { const MSField otherFld = otherms.field(); const rownr_t nFlds = otherFld.nrow(); Block fldMap(nFlds); const Quantum tolerance=itsDirTol; const MSFieldColumns otherFieldCols(otherFld); MSFieldColumns& fieldCols = field(); const MDirection::Types dirType = MDirection::castType( fieldCols.referenceDirMeasCol().getMeasRef().getType()); const MDirection::Types otherDirType = MDirection::castType( otherFieldCols.referenceDirMeasCol().getMeasRef().getType()); MDirection::Convert dirCtr; if (dirType != otherDirType) { // setup a converter dirCtr = MDirection::Convert(otherDirType, dirType); } MDirection refDir, delayDir, phaseDir; MSField& fld = itsMS.field(); const ROTableRow otherFldRow(otherFld); RecordFieldId sourceIdId(MSSource::columnName(MSSource::SOURCE_ID)); // find max ephemeris id Int maxThisEphId = -2; // meaning there is no EPHEMERIS_ID column in the field table Vector validityRange; if(itsMS.field().actualTableDesc().isColumn(MSField::columnName(MSField::EPHEMERIS_ID))){ for(rownr_t i=0; imaxThisEphId){ maxThisEphId = fieldCols.ephemerisId()(i); } } } if(maxThisEphId>-1){ // this MS has at least one field using an ephemeris. // maxThisEphId==-1 would mean there is an EPHEMERIS_ID column but there are no entries // find first and last obs time of other MS RowNumbers sortedI(otherms.nrow()); MSMainColumns msmc(otherms); Vector mainTimesV = msmc.time().getColumn(); GenSortIndirect::sort(sortedI,mainTimesV); validityRange.resize(2); validityRange(0) = mainTimesV(sortedI(0)); validityRange(1) = mainTimesV(sortedI(otherms.nrow()-1)); } TableRow fldRow(fld); for (uInt f = 0; f < nFlds; f++) { String ephPath = otherFieldCols.ephemPath(f); Double otherOrigTime = otherFieldCols.time()(f); try{ delayDir = otherFieldCols.delayDirMeas(f); phaseDir = otherFieldCols.phaseDirMeas(f); refDir = otherFieldCols.referenceDirMeas(f); } catch(std::exception& x){ if(!ephPath.empty()){ LogIO os(LogOrigin("MSConcat", "copyField")); os << LogIO::SEVERE << "Field " << f << " (" << otherFieldCols.name()(f) << ", to be appended)" << " is using an ephemeris with incorrect time origin setup: the time origin (" << otherOrigTime << " s) in the FIELD table is outside the validity range of the ephemeris." << LogIO::POST; } throw(x); } if (dirType != otherDirType) { delayDir = dirCtr(delayDir.getValue()); phaseDir = dirCtr(phaseDir.getValue()); refDir = dirCtr(refDir.getValue()); } const Int newFld = fieldCols.matchDirection(refDir, delayDir, phaseDir, tolerance, -1, // don't specify a tryrow otherOrigTime); // compare at the start time of the other field // cout << "other field, newFld " << f << ", " << newFld << endl; Bool canUseThisEntry = (newFld>=0); if(canUseThisEntry){ String thisEphPath = fieldCols.ephemPath(newFld); if(!thisEphPath.empty()){ // this field uses an ephemeris if(ephPath.empty()){ // the other field does not canUseThisEntry = False; } else{ // both use an ephemeris // is the time coverage of this ephem sufficient to be also used for the other field? stringstream ss; for(uInt i=0; i<2; i++){ try{ MDirection tMDir = fieldCols.phaseDirMeas(newFld, validityRange(i)); } catch(std::exception& x){ canUseThisEntry = False; ss << validityRange(i) << ", "; } } if(!canUseThisEntry){ LogIO os(LogOrigin("MSConcat", "copyField")); os << LogIO::NORMAL << "Ephemeris " << thisEphPath << endl << " from field " << newFld << " (" << fieldCols.name()(newFld) << ") " << " cannot be used for data from field " << f << " (" << otherFieldCols.name()(f) << ", to be appended)" << " because it does not cover time(s) " << ss.str() << endl << " creating separate FIELD table entry." << LogIO::POST; } } } else{ // this field does not use an ephemeris if(!ephPath.empty()){ // the other field does canUseThisEntry = False; } } } if ( canUseThisEntry && (!itsRespectForFieldName || (itsRespectForFieldName && fieldCols.name()(newFld) == otherFieldCols.name()(f)) ) ) { fldMap[f] = newFld; } else { // need to add a new entry in the FIELD subtable fldMap[f] = fld.nrow(); fld.addRow(); fldRow.putMatchingFields(fldMap[f], otherFldRow.get(f)); if (dirType != otherDirType) { DebugAssert(fieldCols.numPoly()(fldMap[f]) == 0, AipsError); Vector vdir(1, refDir); fieldCols.referenceDirMeasCol().put(fldMap[f], vdir); vdir(0) = delayDir; fieldCols.delayDirMeasCol().put(fldMap[f], vdir); vdir(0) = phaseDir; fieldCols.phaseDirMeasCol().put(fldMap[f], vdir); } if(!ephPath.empty() && otherFieldCols.ephemerisId()(f)>-1){ // f has a non-trivial ephemeris id maxThisEphId++; String ephName = Path(ephPath).baseName(); if(!fld.addEphemeris(maxThisEphId, ephPath, ephName.substr(ephName.find("_")+1, ephName.size()-4-ephName.find("_")-1)) // extract comment from name ){ LogIO os(LogOrigin("MSConcat", "copyField")); os << LogIO::SEVERE << "Error transferring ephemeris " << ephPath << " to concatvis." << LogIO::POST; } fieldCols.ephemerisId().put(fldMap[f], maxThisEphId); } else if(maxThisEphId>-2){ // this MS has an ephemeris id column // for the case the appended MS has no ephemeris column, need to set the default explicitly fieldCols.ephemerisId().put(fldMap[f], -1); } //source table has been concatenated; use new index reference if(doSource_p){ Int oldIndex=fieldCols.sourceId()(fldMap[f]); if(newSourceIndex_p.find(oldIndex) != newSourceIndex_p.end()){ fieldCols.sourceId().put(fldMap[f], newSourceIndex_p.at(oldIndex)); } } if(doSource2_p){ Int oldIndex=fieldCols.sourceId()(fldMap[f]); if(newSourceIndex2_p.find(oldIndex) != newSourceIndex2_p.end()){ fieldCols.sourceId().put(fldMap[f], newSourceIndex2_p.at(oldIndex)); } } } } return fldMap; } Bool MSConcat::copySource(const MeasurementSet& otherms){ doSource_p=False; if(Table::isReadable(itsMS.sourceTableName())){ MSSource& newSource=itsMS.source(); MSSourceColumns& sourceCol=source(); Int maxSrcId=0; if(!Table::isReadable(otherms.sourceTableName())){ return False; } const MSSource& otherSource=otherms.source(); if(otherSource.nrow()==0){ return False; } if(newSource.nrow()==0){ maxSrcId = -1; //cout << "Initial source table is empty." << endl; } else{ maxSrcId=max(sourceCol.sourceId().getColumn()); } TableRecord sourceRecord; newSourceIndex_p.clear(); Int numrows=otherSource.nrow(); Int destRow=newSource.nrow(); MSSourceColumns otherSourceCol(otherms.source()); Vector otherId=otherSourceCol.sourceId().getColumn(); newSource.addRow(numrows); const ROTableRow otherSourceRow(otherSource); TableRow sourceRow(newSource); RecordFieldId sourceIdId(MSSource::columnName(MSSource::SOURCE_ID)); RecordFieldId spwIdId(MSSource::columnName(MSSource::SPECTRAL_WINDOW_ID)); // the spw ids Vector otherSpectralWindowId=otherSourceCol.spectralWindowId().getColumn(); for (Int k =0 ; k < numrows ; ++k){ sourceRecord = otherSourceRow.get(k); //define a new source id newSourceIndex_p[k] = maxSrcId+1+otherId(k); sourceRecord.define(sourceIdId, maxSrcId+1+otherId(k)); //define a new temporary spw id by subtracting 10000 // later to be replaced in updateSource if(otherSpectralWindowId(k)>=0){ sourceRecord.define(spwIdId, otherSpectralWindowId(k)-10000); } sourceRow.putMatchingFields(destRow, sourceRecord); ++destRow; } doSource_p=True; solSystObjects_p.clear(); const MSFieldColumns otherFieldCols(otherms.field()); const MSFieldColumns fieldCols(itsMS.field()); for(rownr_t i=0; i=MDirection::MERCURY && refType=MDirection::MERCURY && refType 0){ // the source table is not empty TableRecord sourceRecord; // maps for recording the changes in source id std::map tempSourceIndex; std::map tempSourceIndex2; std::map tempSourceIndex3; tempSourceIndex.clear(); tempSourceIndex2.clear(); tempSourceIndex3.clear(); newSourceIndex2_p.clear(); // the source columns Vector thisId=sourceCol.sourceId().getColumn(); Vector thisSPWId=sourceCol.spectralWindowId().getColumn(); // containers for the rows from the two input tables TableRow sourceRow(newSource); // convert the string containing the column name into a record field ID RecordFieldId sourceIdId(MSSource::columnName(MSSource::SOURCE_ID)); RecordFieldId sourceSPWId(MSSource::columnName(MSSource::SPECTRAL_WINDOW_ID)); // loop over the rows of the merged source table for (Int j =0 ; j < numrows_this ; ++j){ if(thisSPWId(j)<-1){ // came from the second input table sourceRecord = sourceRow.get(j); if(doSPW_p || newSPWIndex_p.find(thisSPWId(j)+10000) != newSPWIndex_p.end()){ // the SPW table was rearranged sourceCol.spectralWindowId().put(j, getMapValue(newSPWIndex_p, thisSPWId(j)+10000) ); //sourceRecord.define(sourceSPWId, newSPWIndex_p.at(thisSPWId(j)+10000) ); } else { // the SPW table did not have to be rearranged, just revert changes to SPW from copySource sourceCol.spectralWindowId().put(j, thisSPWId(j)+10000 ); //sourceRecord.define(sourceSPWId, thisSPWId(j)+10000 ); } //sourceRow.putMatchingFields(j, sourceRecord); } // end for j } // Check if there are redundant rows and remove them creating map for copyField // loop over the columns of the merged source table Vector rowToBeRemoved(numrows_this, False); vector rowsToBeRemoved; Vector thisSPWIdB=sourceCol.spectralWindowId().getColumn(); for (Int j=0 ; j < numrows_this ; ++j){ if(rowToBeRemoved(j)){ continue; } // check if row j has an equivalent row somewhere else in the table Int reftypej = getMapValue (solSystObjects_p, thisId(j)); for (Int k=j+1 ; k < numrows_this ; ++k){ if (!rowToBeRemoved(k)){ if(thisSPWIdB(j)==thisSPWIdB(k)){ // the SPW id is the same Int reftypek = getMapValue (solSystObjects_p, thisId(k)); Bool sameSolSystObjects = ((reftypek==reftypej) && (reftypek>-1)) // object with solar syst ref frame || ((reftypek==reftypej) && (reftypek==-2)); // ephemeris object if( sourceRowsEquivalent(sourceCol, j, k, sameSolSystObjects) ){ // and all columns are the same (not testing source, spw id, time, and interval) //cout << "Found SOURCE rows " << j << " and " << k << " to be identical." << endl; // set the time and interval to a superset of the two Double blowk = sourceCol.time()(k) - sourceCol.interval()(k)/2.; Double bhighk = sourceCol.time()(k) + sourceCol.interval()(k)/2.; Double blowj = sourceCol.time()(j) - sourceCol.interval()(j)/2.; Double bhighj = sourceCol.time()(j) + sourceCol.interval()(j)/2.; Double newInterval = max(bhighk,bhighj)-min(blowk,blowj); Double newTime = (max(bhighk,bhighj)+min(blowk,blowj))/2.; //cout << "new time = " << newTime << ", new interval = " << newInterval << endl; sourceCol.interval().put(j, newTime); sourceCol.interval().put(k, newTime); sourceCol.interval().put(j, newInterval); sourceCol.interval().put(k, newInterval); // make entry in map for (k, j) and delete k tempSourceIndex[thisId(k)] = thisId(j); rowToBeRemoved(k) = True; rowsToBeRemoved.push_back(k); } } } } } // end for j Int newNumrows_this = numrows_this; // copy of number of rows Vector newThisId(thisId); // copy of vector of IDs if(rowsToBeRemoved.size()>0){ // actually remove the rows Vector rowsTBR(rowsToBeRemoved); newSource.removeRow(rowsTBR); // cout << "Removed " << rowsToBeRemoved.size() << " redundant rows from SOURCE table." << endl; newNumrows_this=newSource.nrow(); // update number of rows sourceCol.sourceId().getColumn(newThisId, True); // update vector if IDs } // renumber consecutively Int nnrow = 0; for (Int j=0 ; j < newNumrows_this ; ++j){ if(newThisId(j) > nnrow){ nnrow++; //sourceRecord = sourceRow.get(j); //sourceRecord.define(sourceIdId, nnrow ); //sourceRow.putMatchingFields(j, sourceRecord); tempSourceIndex2[newThisId(j)] = nnrow; sourceCol.sourceId().put(j, nnrow); } } // give equivalent rows the same source id Bool rowsRenamed(False); Int nDistinctSources = newNumrows_this; Vector thisSourceId=sourceCol.sourceId().getColumn(); for (Int j=0 ; j < newNumrows_this ; ++j){ // check if row j has an equivalent row somewhere down in the table Int reftypej = getMapValue (solSystObjects_p, thisId(j)); for (Int k=j+1 ; k < newNumrows_this ; ++k){ if(thisSourceId(j)!=thisSourceId(k)){ Int reftypek = getMapValue (solSystObjects_p, thisId(k)); Bool sameSolSystObjects = ((reftypek==reftypej) && (reftypek>-1)) // object with solar syst ref frame || ((reftypek==reftypej) && (reftypek==-2)); // ephemeris object; if( sourceRowsEquivalent(sourceCol, j, k, sameSolSystObjects)){ // all columns are the same except source id (not testing spw id), // spw id must be different, otherwise row would have been deleted above //cout << "Found SOURCE rows " << j << " and " << k << " to be identical except for the SPW ID and source id. " // << newThisId(k) << " mapped to " << newThisId(j) << endl; // give same source id // make entry in map for (k, j) and rename k tempSourceIndex3[newThisId(k)] = newThisId(j); //sourceRecord = sourceRow.get(k); //sourceRecord.define(sourceIdId, newThisId(j) ); //sourceRow.putMatchingFields(k, sourceRecord); thisSourceId(k) = newThisId(j); rowsRenamed = True; nDistinctSources--; } } } } // end for j if(rowsRenamed){ sourceCol.sourceId().putColumn(thisSourceId); } // cout << "Ndistinct = " << nDistinctSources << endl; if(rowsRenamed){ // reduce ID values to minimal range sourceCol.sourceId().getColumn(newThisId, True); // update vector if IDs Int counter = 0; for (Int j=0 ; j < newNumrows_this ; ++j){ if(newThisId(j) >= nDistinctSources){ sourceRecord = sourceRow.get(j); tempSourceIndex3[newThisId(j)] = nDistinctSources-counter-1; sourceRecord.define(sourceIdId, nDistinctSources-counter-1); sourceRow.putMatchingFields(j, sourceRecord); counter++; // cout << "Found SOURCE row " << j << " to have a source id " << newThisId(j) // << " larger than the number of distinct sources: " << nDistinctSources << ". " // << newThisId(j) << " mapped to " << nDistinctSources-counter-1 << endl; } } } if(rowsToBeRemoved.size()>0 || rowsRenamed){ // create map for copyField for (Int j=0 ; j < numrows_this ; ++j){ // loop over old indices if(tempSourceIndex.find(j) != tempSourceIndex.end()){ // ID changed because of redundancy if(tempSourceIndex2.find(tempSourceIndex.at(j)) != tempSourceIndex2.end()){ // ID changed also because of renumbering if( tempSourceIndex3.find(tempSourceIndex2.at(tempSourceIndex.at(j))) != tempSourceIndex3.end() ){ // ID also changed because of renaming newSourceIndex2_p[j] = tempSourceIndex3.at(tempSourceIndex2.at(tempSourceIndex.at(j))); // abc } else { // ID changed because of redundancy and renumberning newSourceIndex2_p[j] = tempSourceIndex2.at(tempSourceIndex.at(j)); // ab } } else{ if( tempSourceIndex3.find(tempSourceIndex.at(j)) != tempSourceIndex3.end() ){ // ID changed because of redundancy and renaming newSourceIndex2_p[j] = tempSourceIndex3.at(tempSourceIndex.at(j)); // ac } else { // ID only changed because of redundancy newSourceIndex2_p[j] = tempSourceIndex.at(j); // a } } } else if(tempSourceIndex2.find(j) != tempSourceIndex2.end()){ if( tempSourceIndex3.find(tempSourceIndex2.at(j)) != tempSourceIndex3.end() ){ // ID changed because of renumbering and renaming newSourceIndex2_p[j] = tempSourceIndex3.at(tempSourceIndex2.at(j)); // bc } else { // ID only changed because of renumbering newSourceIndex2_p[j] = tempSourceIndex2.at(j); // b } } else if(tempSourceIndex3.find(j) != tempSourceIndex3.end()){ // ID only changed because of renaming newSourceIndex2_p[j] = tempSourceIndex3.at(j); // c } } doSource2_p=True; } } // end if(numrows_this > 0) } return doSource2_p; } Bool MSConcat::updateSource2(){ // to be called after copyField // Go over the SOURCE table in the light of FIELD table merging // and correct SOURCE IDs if necessary. Bool rval = False; // were changes made? if(Table::isReadable(itsMS.sourceTableName())){ MSSource& newSource=itsMS.source(); MSSourceColumns& sourceCol=source(); MSFieldColumns& fieldCol=field(); vector rowsToBeRemoved; // the number of rows in the source table Int numrows_this=newSource.nrow(); if(numrows_this > 0){ // the source table is not empty Vector thisId=sourceCol.sourceId().getColumn(); Vector thisName=sourceCol.name().getColumn(); Vector thisSPWId=sourceCol.spectralWindowId().getColumn(); Vector thisFSId=fieldCol.sourceId().getColumn(); // loop over the rows of the merged source table for (Int j=0 ; j < numrows_this ; ++j){ if(std::find(thisFSId.begin(), thisFSId.end(), thisId[j]) == thisFSId.end()){ // source id doesn't exist in field table //cout << "source id " << thisId[j] << " doesn't exist in field table" << endl; // find equivalent source with different SPW and take that ID Int foundRow = -1; for(Int k=0; k < numrows_this ; ++k){ if(thisSPWId[k]!=thisSPWId[j] && thisId[k]!=thisId[j] && sourceRowsEquivalent(sourceCol, k, j, False, True) ){ // do check direction but not transition and rest (they are potentially different for each SPW) foundRow = k; break; } } if(foundRow>=0){ sourceCol.sourceId().put(j, thisId[foundRow]); thisId[j] = thisId[foundRow]; } else{ // no adequate source id found //cout << "Selecting row " << j << " for removal from SOURCE table." << endl; rowsToBeRemoved.push_back(j); } rval = True; } } if(rowsToBeRemoved.size()>0){ // actually remove the rows Vector rowsTBR(rowsToBeRemoved); newSource.removeRow(rowsTBR); //cout << "Removed " << rowsToBeRemoved.size() << " stray rows from SOURCE table." << endl; } } } return rval; } Bool MSConcat::sourceRowsEquivalent(const MSSourceColumns& sourceCol, const rownr_t& rowi, const rownr_t& rowj, const Bool dontTestDirection, const Bool dontTestTransAndRest){ // check if the two SOURCE table rows are identical IGNORING SOURCE_ID, SPW_ID, time, and interval Bool areEquivalent(False); // test the non-optional columns first if(areEQ(sourceCol.calibrationGroup(), rowi, rowj) && areEQ(sourceCol.code(), rowi, rowj) && areEQ(sourceCol.name(), rowi, rowj) && areEQ(sourceCol.numLines(), rowi, rowj) && // do NOT test SPW ID! // areEQ(sourceCol.spectralWindowId(), rowi, rowj) && (areEQ(sourceCol.direction(), rowi, rowj) || dontTestDirection) && areEQ(sourceCol.properMotion(), rowi, rowj) ){ // cout << "All non-optionals equal" << endl; // test the optional columns next areEquivalent = True; if(!(sourceCol.position().isNull()) && !dontTestDirection){ try { areEquivalent = areEQ(sourceCol.position(), rowi, rowj); } catch (std::exception& x) { // row has invalid data areEquivalent = True; } // if(!areEquivalent) cout << "not equal position" << endl; } if(!(sourceCol.pulsarId().isNull())){ try { areEquivalent = areEQ(sourceCol.pulsarId(), rowi, rowj); } catch (std::exception& x) { // row has invalid data areEquivalent = True; } // if(!areEquivalent) cout << "not equal pulsarId" << endl; } if(!dontTestTransAndRest && !(sourceCol.restFrequency().isNull())){ try { areEquivalent = areEQ(sourceCol.restFrequency(), rowi, rowj); } catch (std::exception& x) { // row has invalid data areEquivalent = True; } // if(!areEquivalent) cout << "not equal restFrequency" << endl; } if(!(sourceCol.sysvel().isNull())){ try { areEquivalent = areEQ(sourceCol.sysvel(), rowi, rowj); } catch (std::exception& x) { // row has invalid data areEquivalent = True; } // if(!areEquivalent) cout << "not equal sysvel" << endl; } if(!dontTestTransAndRest && !(sourceCol.transition().isNull())){ try { areEquivalent = areEQ(sourceCol.transition(), rowi, rowj); } catch (std::exception& x) { // row has invalid data areEquivalent = True; } // if(!areEquivalent) cout << "not equal transition" << endl; } } return areEquivalent; } Bool MSConcat::obsRowsEquivalent(const MSObservationColumns& obsCol, const rownr_t& rowi, const rownr_t& rowj){ // check if the two OBSERVATION table rows are identical ignoring LOG and SCHEDULE Bool areEquivalent(False); if(areEQ(obsCol.flagRow(), rowi, rowj) && areEQ(obsCol.observer(), rowi, rowj) && areEQ(obsCol.project(), rowi, rowj) && areEQ(obsCol.releaseDate(), rowi, rowj) && areEQ(obsCol.telescopeName(), rowi, rowj) && areEQ(obsCol.timeRange(), rowi, rowj) ){ areEquivalent = True; } return areEquivalent; } Bool MSConcat::procRowsEquivalent(const MSProcessorColumns& procCol, const uInt& rowi, const uInt& rowj){ // check if the two PROCESSOR table rows are identical Bool areEquivalent(False); if(areEQ(procCol.flagRow(), rowi, rowj) && areEQ(procCol.modeId(), rowi, rowj) && areEQ(procCol.type(), rowi, rowj) && areEQ(procCol.typeId(), rowi, rowj) && areEQ(procCol.subType(), rowi, rowj) ){ areEquivalent = True; // passId is optional if(!procCol.passId().isNull() && !areEQ(procCol.passId(), rowi, rowj)){ areEquivalent = False; } } return areEquivalent; } Block MSConcat::copySpwAndPol(const MSSpectralWindow& otherSpw, const MSPolarization& otherPol, const MSDataDescription& otherDD) { LogIO os(LogOrigin("MSConcat", "copySpwAndPol")); const uInt nDDs = otherDD.nrow(); Block ddMap(nDDs); const MSSpWindowColumns otherSpwCols(otherSpw); MSSpectralWindow& spw = itsMS.spectralWindow(); MSSpWindowColumns& spwCols = spectralWindow(); const ROTableRow otherSpwRow(otherSpw); TableRow spwRow(spw); const MSPolarizationColumns otherPolCols(otherPol); MSPolarization& pol = itsMS.polarization(); MSPolarizationColumns& polCols = polarization(); const ROTableRow otherPolRow(otherPol); TableRow polRow(pol); const MSDataDescColumns otherDDCols(otherDD); MSDataDescColumns& ddCols = dataDescription(); const Quantum freqTol=itsFreqTol; const String& spwIdxName = MSDataDescription::columnName(MSDataDescription::SPECTRAL_WINDOW_ID); const String& polIdxName = MSDataDescription::columnName(MSDataDescription::POLARIZATION_ID); Vector ddIndexCols(2); ddIndexCols(0) = spwIdxName; ddIndexCols(1) = polIdxName; ColumnsIndex ddIndex(itsMS.dataDescription(), ddIndexCols); RecordFieldPtr newSpwPtr(ddIndex.accessKey(), spwIdxName); RecordFieldPtr newPolPtr(ddIndex.accessKey(), polIdxName); Vector corrInt; Vector corrPol; itsChanReversed.resize(nDDs); itsChanReversed.set(False); newSPWIndex_p.clear(); doSPW_p = False; Vector foundInDD(otherSpw.nrow(), False); // loop over the rows of the other data description table for (uInt d = 0; d < nDDs; d++) { //cout << "other DD " << d << endl; Bool matchedSPW = False; DebugAssert(otherDDCols.spectralWindowId()(d) >= 0 && otherDDCols.spectralWindowId()(d) < static_cast(otherSpw.nrow()), AipsError); const Int otherSpwId = otherDDCols.spectralWindowId()(d); DebugAssert(otherSpwCols.numChan()(otherSpwId) > 0, AipsError); foundInDD(otherSpwId) = True; Vector otherFreqs = otherSpwCols.chanFreq()(otherSpwId); if(otherSpwCols.totalBandwidthQuant()(otherSpwId).getValue(Unit("Hz"))<=0.){ os << LogIO::WARN << "Negative or zero total bandwidth in SPW " << otherSpwId << " of MS to be appended." << LogIO::POST; } *newSpwPtr = spwCols.matchSpw(otherSpwCols.refFrequencyMeas()(otherSpwId), static_cast(otherSpwCols.numChan()(otherSpwId)), otherSpwCols.totalBandwidthQuant()(otherSpwId), otherSpwCols.ifConvChain()(otherSpwId), freqTol, otherFreqs, itsChanReversed[d]); if (*newSpwPtr < 0) { // cout << "no counterpart found for other spw " << otherSpwId << endl; // need to add a new entry in the SPECTRAL_WINDOW subtable *newSpwPtr= spw.nrow(); spw.addRow(); spwRow.putMatchingFields(*newSpwPtr, otherSpwRow.get(otherSpwId)); // fill map to be used by updateSource() newSPWIndex_p[otherSpwId] = *newSpwPtr; // There cannot be an entry in the DATA_DESCRIPTION Table doSPW_p = True; } else{ // cout << "counterpart found for other spw " << otherSpwId // << " found in this spw " << *newSpwPtr << endl; matchedSPW = True; if(*newSpwPtr != otherSpwId){ newSPWIndex_p[otherSpwId] = *newSpwPtr; } } DebugAssert(otherDDCols.polarizationId()(d) >= 0 && otherDDCols.polarizationId()(d) < static_cast(otherPol.nrow()), AipsError); const uInt otherPolId = static_cast(otherDDCols.polarizationId()(d)); otherPolCols.corrType().get(otherPolId, corrInt, True); const uInt nCorr = corrInt.nelements(); corrPol.resize(nCorr); for (uInt p = 0; p < nCorr; p++) { corrPol(p) = Stokes::type(corrInt(p)); } Bool matchedDD=False; uInt numActPol =0; while ( numActPol < polCols.nrow() ){ *newPolPtr = polCols.match(corrPol, numActPol); if (*newPolPtr < 0) { // cout << "need to add a new entry in the POLARIZATION subtable" << endl; *newPolPtr= pol.nrow(); pol.addRow(); polRow.putMatchingFields(*newPolPtr, otherPolRow.get(otherPolId)); break; // break out of the while loop } else{ // we have a Pol match if(matchedSPW){ // We need to check if there exists an entry in the DATA_DESCRIPTION // table with the required spectral window and polarization index. ddMap[d] = ddIndex.getRowNumber(matchedDD); // sets matchedDD to True if a matching DD table entry is found } //cout << "Found matching pol. Fould matching DD? " << matchedDD << " d ddMap[d] " << d << " " << ddMap[d] << endl; } ++numActPol; } if (!matchedDD) { // Add an entry to the data description sub-table ddMap[d] = ddCols.nrow(); itsMS.dataDescription().addRow(1); ddCols.polarizationId().put(ddMap[d], *newPolPtr); ddCols.spectralWindowId().put(ddMap[d], *newSpwPtr); } } // Finally, see if there are additional SPWs in the SPW table which are // not used in the DD table for(uInt otherSpwId=0; otherSpwId 0, AipsError); Vector otherFreqs = otherSpwCols.chanFreq()(otherSpwId); if(otherSpwCols.totalBandwidthQuant()(otherSpwId).getValue(Unit("Hz"))<=0.){ os << LogIO::WARN << "Negative or zero total bandwidth in SPW " << otherSpwId << " of MS to be appended." << LogIO::POST; } Bool chanReversed = False; Int newSpwId = spwCols.matchSpw(otherSpwCols.refFrequencyMeas()(otherSpwId), static_cast(otherSpwCols.numChan()(otherSpwId)), otherSpwCols.totalBandwidthQuant()(otherSpwId), otherSpwCols.ifConvChain()(otherSpwId), freqTol, otherFreqs, chanReversed); if (newSpwId < 0) { // cout << "Second iteration: no counterpart found for other spw " << otherSpwId << endl; // need to add a new entry in the SPECTRAL_WINDOW subtable newSpwId = spw.nrow(); spw.addRow(); spwRow.putMatchingFields(newSpwId, otherSpwRow.get(otherSpwId)); // fill map to be used by updateSource() newSPWIndex_p[otherSpwId] = newSpwId; doSPW_p = True; } // else{ // cout << "Second iteration: counterpart found for other spw " << otherSpwId // << " found in this spw " << newSpwId << endl; // // } } // endif } return ddMap; } void MSConcat::updateModelDataKeywords(MeasurementSet& theMS){ Int nSpw=theMS.spectralWindow().nrow(); MSSpWindowColumns msSpW(theMS.spectralWindow()); Matrix selection(2,nSpw); // fill in default selection selection.row(0)=0; //start selection.row(1)=msSpW.numChan().getColumn(); TableColumn col(theMS,"MODEL_DATA"); if (col.keywordSet().isDefined("CHANNEL_SELECTION")) col.rwKeywordSet().removeField("CHANNEL_SELECTION"); col.rwKeywordSet().define("CHANNEL_SELECTION",selection); } // Local Variables: // compile-command: "gmake MSConcat" // End: } //#End casa namespace casacore-3.7.1/ms/MSOper/MSConcat.h000066400000000000000000000154051476623553700167350ustar00rootroot00000000000000//# MSConcat.h: A class for concatenating MeasurementSets. //# Copyright (C) 2000,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSCONCAT_H #define MS_MSCONCAT_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class TableDesc; class MSMainColumns; class MSDataDescColumns; class MSSpWindowColumns; class MSPolarizationColumns; class MSAntenna; class MSDataDescription; class MSFeed; class MSField; class MSPolarization; class MSSpectralWindow; template class Block; // A class with functions for concatenating MeasurementSets // // // // // // // // // // // // // // // // // // // // class MSConcat: public MSColumns { public: MSConcat(MeasurementSet& ms); void virtualconcat(MeasurementSet& otherMS, const Bool checkShapeAndCateg=True, const String& obsidAndProcAndScanTableName=""); void concatenate(const MeasurementSet& otherMS, const uInt handling=0, //# 0 (default): complete concat of all tables //# 1 : don't concatenate the MAIN table //# 2 : don't concatenate the POINTING table //# 3 : neither concat MAIN nor POINTING table const String& destMSName=""); //# support for virtual concat void setTolerance(Quantum& freqTol, Quantum& dirTol); void setWeightScale(const Float weightScale); void setRespectForFieldName(const Bool respectFieldName); //# If True, fields of same direction are not merged //# if their name is different private: MSConcat(); static IPosition isFixedShape(const TableDesc& td); static IPosition getShape(const MSDataDescColumns& ddCols, const MSSpWindowColumns& spwCols, const MSPolarizationColumns& polCols, uInt whichShape); void checkShape(const IPosition& otherShape) const; void checkCategories(const MSMainColumns& otherCols) const; Bool checkEphIdInField(const MSFieldColumns& otherFldCol) const; Bool copyPointing(const MSPointing& otherPoint, const Block& newAntIndices); Bool copyPointingB(MSPointing& otherPoint, const Block& newAntIndices); Bool copySysCal(const MSSysCal& otherSysCal, const Block& newAndIndices); Bool copyWeather(const MSWeather& otherWeather, const Block& newAndIndices); Bool copyGainCurve(const MeasurementSet& otherMS, const Block& newAndIndices); Bool copyPhaseCal(const MeasurementSet& otherMS, const Block& newAndIndices); Bool copyEOP(const MeasurementSet& otherMS); Int copyObservation(const MSObservation& otherObs, const Bool remRedunObsId=True); //# by default remove redundant observation table rows Int copyProcessor(const MSProcessor& otherObs, const Bool remRedunProcId=True); //# by default remove redundant processor table rows Block copyAntennaAndFeed(const MSAntenna& otherAnt, const MSFeed& otherFeed); Block copyState(const MSState& otherState); Block copyField(const MeasurementSet& otherms); Block copySpwAndPol(const MSSpectralWindow& otherSpw, const MSPolarization& otherPol, const MSDataDescription& otherDD); Bool copySource(const MeasurementSet& otherms); Bool updateSource(); Bool updateSource2(); Bool sourceRowsEquivalent(const MSSourceColumns& sourceCol, const rownr_t& rowi, const rownr_t& rowj, const Bool dontTestDirection=False, const Bool dontTestTransAndRest=False); Bool obsRowsEquivalent(const MSObservationColumns& obsCol, const rownr_t& rowi, const rownr_t& rowj); Bool procRowsEquivalent(const MSProcessorColumns& procCol, const uInt& rowi, const uInt& rowj); void updateModelDataKeywords(MeasurementSet& ms); MeasurementSet itsMS; IPosition itsFixedShape; Quantum itsFreqTol; Quantum itsDirTol; Float itsWeightScale; Bool itsRespectForFieldName; Vector itsChanReversed; std::map newSourceIndex_p; std::map newSourceIndex2_p; std::map newSPWIndex_p; std::map newObsIndexA_p; std::map newObsIndexB_p; std::map otherObsIdsWithCounterpart_p; std::map newProcIndexA_p; std::map newProcIndexB_p; std::map solSystObjects_p; Bool doSource_p; Bool doSource2_p; Bool doSPW_p; Bool doObsA_p; Bool doObsB_p; Bool doProcA_p; Bool doProcB_p; }; template Bool areEQ(const ScalarColumn& col, rownr_t row_i, rownr_t row_j) { T value_i, value_j; col.get(row_i, value_i); col.get(row_j, value_j); return (value_i == value_j); } template Bool areEQ(const ArrayColumn& col, rownr_t row_i, rownr_t row_j) { Bool rval(False); Array arr_i; Array arr_j; col.get(row_i, arr_i, True); col.get(row_j, arr_j, True); size_t ni = arr_i.nelements(); size_t nj = arr_j.nelements(); if( (ni==0 && nj==0) || // no data is regarded as equal allEQ(arr_i, arr_j)){ rval = True; } return rval; } inline Int getMapValue (const std::map& m, Int k) { auto iter = m.find(k); return (iter == m.end() ? -1 : iter->second); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSOper/MSDerivedValues.cc000066400000000000000000000273551476623553700204350ustar00rootroot00000000000000//# MSDerivedValues.cc: Calculate values derived from a MS //# Copyright (C) 1996,1997,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSDerivedValues::MSDerivedValues() { init(); } MSDerivedValues::MSDerivedValues(const MSDerivedValues& other) { operator=(other); } MSDerivedValues::~MSDerivedValues() { //break reference ms_p=MeasurementSet(); } MSDerivedValues& MSDerivedValues::operator=(const MSDerivedValues& other) { antenna_p=other.antenna_p; // should copy all data here, for now, just init init(); mount_p = other.mount_p; radialVelocityType_p = other.radialVelocityType_p; return *this; } const Vector & MSDerivedValues::getAntennaPositions () const { return mAntPos_p; } Int MSDerivedValues::setAntennas(const MSAntennaColumns& ac) { Int nAnt=ac.position().nrow(); mAntPos_p.resize(nAnt); Vector mount(nAnt); Vector avPos(3); avPos=0; for (Int ant=0; ant0) { avPos/=Double(nAnt); mObsPos_p = mAntPos_p(0); mObsPos_p.set(MVPosition(avPos)); setAntennaMount(mount); setAntenna(0); } return nAnt; } MSDerivedValues& MSDerivedValues::setAntennaPositions(const Vector& antPosition) { Int nAnt=antPosition.nelements(); AlwaysAssert(nAnt>0,AipsError); mAntPos_p.resize(nAnt); mAntPos_p=antPosition; Vector avPos(3); avPos=0; for (Int i=0; i restFreqVec; try{ msdoppler.dopplerInfo(restFreqVec ,spwid,fieldid); } catch(...){ setRestFrequency(Quantity(0.0, "Hz")); return False; } if((restFreqVec.nelements() >0) && (uInt(whichline)<=restFreqVec.nelements())){ //using the first setRestFrequency(Quantity(restFreqVec[whichline], "Hz")); return True; } else{ setRestFrequency(Quantity(0.0, "Hz")); } } return False; } MSDerivedValues& MSDerivedValues::setRestFrequency(const Quantity& restfrq){ restFreq_p=restfrq; return *this; } MSDerivedValues& MSDerivedValues::setAntennaMount(const Vector& mount) { Int nAnt=mount.nelements(); if (nAnt>0) { mount_p.resize(nAnt); for (Int i=0; i fieldid)){ MSColumns msc(ms_p); const MDirection dirn=msc.field().phaseDirMeas(fieldid); setFieldCenter(dirn); } else{ MDirection dummy; setFieldCenter(dummy); } return *this; } MSDerivedValues& MSDerivedValues::setAntenna(Int antenna) { DebugAssert(antenna>=-1,AipsError); DebugAssert(antenna #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MSAntennaColumns; class String; // // MSDerivedValues calculates values derived from a MS // // // // // //
      • MeasurementSet //
      • SomeOtherClass //
      • some concept // // // // MSDerivedValues calculates values derived from those in a MS // // // // MSDerivedValues is a class that computes values derived from those present // in a MeasurementSet. E.g., calculate feed position angles on the sky from // time, antenna positions and feed characteristics. // // // // // // calculate the parallactic angle and the observatory velocity for the // // first time and first source in the MS. // // set up // MSDerivedValues msd; // MS myMS("myMS"); // MSColumns msc(myMS); // msd.setAntennas(msc.antenna()); // MEpoch ep=MS::epochMeasure(msc.time()); // ep.set(MVEpoch(Quantity(msc.time()(0),"s"))); // msd.setEpoch(ep); // MDirection dir=MS::directionMeasure(msc.field().phaseDir()); // dir.set(MVDirection(Vector(msc.field().phaseDir()(0)))); // msd.setFieldCenter(dir); // msd.setVelocityFrame(MRadialVelocity::LSRK); // // now we are ready for the calculations: // Double parAngle = msd.parangle(); // MRadialVelocity observatoryVel = msd.obsVel(); // // // // // Values derived from those in a MS are needed in various places, e.g., for // plotting purposes. This class combines the commonly needed calculations // in one place. // // // //
      • //
      • // // // //
      • the interface should be less cumbersome //
      • probably needs speeding up // class MSDerivedValues { friend class VisBufferAsync; // to work around dysfunctional operator= and // thread-hostile shared pointers (Jim Jacobs 111104) public: MSDerivedValues(); ~MSDerivedValues(); // Copy constructor, this will initialize with other's MS MSDerivedValues(const MSDerivedValues& other); // Assignment, this will initialize with other's MS MSDerivedValues& operator=(const MSDerivedValues& other); // Set antenna position from an antenna table // Returns the number of antennas. Also // sets the observatory position to the average of the antenna positions. Int setAntennas(const MSAntennaColumns& ac); // Set antenna positions, index in vector is antenna number // for calls below. MSDerivedValues& setAntennaPositions(const Vector& antPosition); const Vector & getAntennaPositions () const; // Set the observatory position. Note that setAntennas will reset this. MSDerivedValues& setObservatoryPosition(const MPosition& obsPosition); // Set antenna mounts, should have same number of entries as // antPosition in setAntennaPosition MSDerivedValues& setAntennaMount(const Vector& mount); // Set epoch MSDerivedValues& setEpoch(const MEpoch& time); // Set field center MSDerivedValues& setFieldCenter(const MDirection& fieldCenter); //If you have used setMeasurementSet then this version of //setFieldCenter using field id makes sense MSDerivedValues& setFieldCenter(uInt fieldid=0); // Set antenna index, sets the position reference for the conversions. // Use -1 to set the reference frame to the observatory position. MSDerivedValues& setAntenna(Int antenna); // Set the velocity frame type (e.g., MRadialVelocity::LSRK) MSDerivedValues& setVelocityFrame(MRadialVelocity::Types vType); // Set the velocity frame type (e.g., MRDoppler::RADIO) MSDerivedValues& setVelocityReference(MDoppler::Types dopType); MRadialVelocity::Types getRadialVelocityType () const; // Set the frequency frame (e.g., MFrequency::LSRK) MSDerivedValues& setFrequencyReference(MFrequency::Types frqType); // get hour angle Double hourAngle(); // get parallactic angle Double parAngle(); // get azimuth & elevation const MDirection& azel(); // get LAST for given time, antenna const MEpoch& last(); // get observatory radial velocity for given epoch, position and direction const MRadialVelocity& obsVel(); //Set an ms does not need to explicity setAntennas and is necessary if //setRestFreqency(fieldid, spwid) is used MSDerivedValues& setMeasurementSet(const MeasurementSet& ms); //Set restFrequencies...make it look for it for the fieldid, spwid and line //number defined in the SOURCE table return False if it fails to find the //restFrquency Bool setRestFrequency(const Int fieldid, const Int spwid, const Int linenum=0); // MSDerivedValues& setRestFrequency(const Quantity& restFreq); // get frequency from velocity Quantity toFrequency(const Quantity& vel, const Quantity& restFreq); Quantity toFrequency(const Quantity& vel); // get velocity from frequency Quantity toVelocity(const Quantity& freq, const Quantity& restFreq); Quantity toVelocity(const Quantity& freq); protected: private: // initialize data void init(); Int antenna_p; MEpoch::Convert cUTCToLAST_p; Vector mAntPos_p; MDirection::Convert cRADecToAzEl_p; MDirection::Convert cHADecToAzEl_p; MDirection::Convert cRADecToHADec_p; MeasFrame fAntFrame_p; MDirection mRADecInAzEl_p; MDirection mHADecPoleInAzEl_p; MDirection mFieldCenter_p; MPosition mObsPos_p; MRadialVelocity::Convert cTOPOToLSR_p; MDoppler::Ref velref_p; MFrequency::Ref frqref_p; Bool hasMS_p; Quantity restFreq_p; Vector mount_p; MeasurementSet ms_p; MRadialVelocity::Types radialVelocityType_p; // Vector receptorAngle_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSOper/MSFlagger.cc000066400000000000000000001006661476623553700172370ustar00rootroot00000000000000//# MSFlagger.cc: selection and iteration of an MS //# Copyright (C) 1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSFlagger::MSFlagger():msSel_p(0) {} MSFlagger::MSFlagger(MSSelector& msSel):msSel_p(&msSel) {} MSFlagger& MSFlagger::operator=(const MSFlagger& other) { if (this==&other) return *this; msSel_p=other.msSel_p; buffer_p=other.buffer_p; return *this; } MSFlagger::~MSFlagger() { msSel_p=0; } void MSFlagger::setMSSelector(MSSelector& msSel) { msSel_p=&msSel; buffer_p=Record(RecordInterface::Variable); } Bool MSFlagger::fillDataBuffer(const String& item, Bool ifrAxis) { LogIO os; if (!check()) return False; String itm=downcase(item); Int fld=MSS::field(itm); switch (fld) { case MSS::AMPLITUDE: case MSS::CORRECTED_AMPLITUDE: case MSS::MODEL_AMPLITUDE: case MSS::RESIDUAL_AMPLITUDE: case MSS::OBS_RESIDUAL_AMPLITUDE: case MSS::DATA: case MSS::CORRECTED_DATA: case MSS::MODEL_DATA: case MSS::RESIDUAL_DATA: case MSS::OBS_RESIDUAL_DATA: case MSS::IMAGINARY: case MSS::CORRECTED_IMAGINARY: case MSS::MODEL_IMAGINARY: case MSS::RESIDUAL_IMAGINARY: case MSS::OBS_RESIDUAL_IMAGINARY: case MSS::PHASE: case MSS::CORRECTED_PHASE: case MSS::MODEL_PHASE: case MSS::RESIDUAL_PHASE: case MSS::OBS_RESIDUAL_PHASE: case MSS::REAL: case MSS::CORRECTED_REAL: case MSS::MODEL_REAL: case MSS::RESIDUAL_REAL: case MSS::OBS_RESIDUAL_REAL: { Vector items(3); items(0)=item; items(1)="FLAG"; items(2)="FLAG_ROW"; buffer_p=msSel_p->getData(items,ifrAxis); buffer_p.define("datafield",itm); } return True; default: os << LogIO::WARN <<"No DATA derived item specified, buffer unchanged" << LogIO::POST; } return False; } Record MSFlagger::diffDataBuffer(const String& direction, Int window, Bool doMedian) { Record retVal(RecordInterface::Variable); LogIO os; String dir=downcase(direction); if (dir!="time" && dir!="channel") { os << LogIO::WARN << "Unrecognized direction "< flag = buffer_p.asArrayBool(RecordFieldId("flag")); Array flagRow = buffer_p.asArrayBool(RecordFieldId("flag_row")); ; Int fld=MSS::field(item); Array diff; Int timeAxis=flag.ndim()-1; Int chanAxis=1; switch (fld) { case MSS::DATA: case MSS::CORRECTED_DATA: case MSS::MODEL_DATA: case MSS::RESIDUAL_DATA: case MSS::OBS_RESIDUAL_DATA: { Array data = buffer_p.asArrayComplex(RecordFieldId(item)); if (dir=="time") { diff=MSSelUtil::diffData(data,flag,flagRow,timeAxis,win, doMedian); } else { diff=MSSelUtil::diffData(data,flag,flagRow,chanAxis,win, doMedian); } // remove data field from record Record gr(RecordInterface::Variable); gr.define("flag",buffer_p.asArrayBool(RecordFieldId("flag"))); gr.define("flag_row",buffer_p.asArrayBool(RecordFieldId("flag_row"))); if (fld==MSS::DATA) item="amplitude"; if (fld==MSS::CORRECTED_DATA) item="corrected_amplitude"; if (fld==MSS::MODEL_DATA) item="model_amplitude"; if (fld==MSS::RESIDUAL_DATA) item="residual_amplitude"; if (fld==MSS::OBS_RESIDUAL_DATA) item="obs_residual_amplitude"; gr.define("datafield",item); buffer_p=gr; } break; case MSS::AMPLITUDE: case MSS::CORRECTED_AMPLITUDE: case MSS::MODEL_AMPLITUDE: case MSS::RESIDUAL_AMPLITUDE: case MSS::OBS_RESIDUAL_AMPLITUDE: case MSS::IMAGINARY: case MSS::CORRECTED_IMAGINARY: case MSS::MODEL_IMAGINARY: case MSS::RESIDUAL_IMAGINARY: case MSS::OBS_RESIDUAL_IMAGINARY: case MSS::PHASE: case MSS::CORRECTED_PHASE: case MSS::MODEL_PHASE: case MSS::RESIDUAL_PHASE: case MSS::OBS_RESIDUAL_PHASE: case MSS::REAL: case MSS::CORRECTED_REAL: case MSS::MODEL_REAL: case MSS::RESIDUAL_REAL: case MSS::OBS_RESIDUAL_REAL: { Array data = buffer_p.asArrayFloat(RecordFieldId(item)); if (dir=="time") { diff=MSSelUtil::diffData(data,flag,flagRow,timeAxis,win, doMedian); } else { diff=MSSelUtil::diffData(data,flag,flagRow,chanAxis,win, doMedian); } } break; default: break; } buffer_p.define(item,diff); applyRowFlags(flag,flagRow); // need to apply row flags to flags for stats addStats(buffer_p,flag,flagRow,diff); retVal.define("median",buffer_p.asArrayFloat(RecordFieldId("medTF"))); retVal.define("aad",buffer_p.asArrayFloat(RecordFieldId("adTF"))); return retVal; } void MSFlagger::addStats(Record& buf, const Array& flag, const Array flagRow, const Array& data) { // axes PFIT (Polarization, Freq, Interferometer, Time) // take median along T and F axes (medT, medF) // calculate median of medians along F and T (medTmedF, medFmedT) to // find outlying times and channels, estimate medTF as minumum of latter 2. // calculate average absolute deviations over T and F medians, and TF planes // (adT, adF and adTF). Array medT, medF, medTmedF, medFmedT, medTF, adT, adF, adTF; getStats(medTF, adTF, medT, medFmedT, adT, medF, medTmedF, adF, data, flag, flagRow); buf.define("medTF",medTF); buf.define("adTF",adTF); buf.define("medT",medT); buf.define("medFmedT",medFmedT); buf.define("adT",adT); buf.define("medF",medF); buf.define("medTmedF",medTmedF); buf.define("adF",adF); } void MSFlagger::applyRowFlags(Array& flag, Array& flagRow) { const Int nXY=flag.shape()(0)*flag.shape()(1); Bool deleteFlag, deleteFlagRow; Bool* pflagRow = flagRow.getStorage(deleteFlagRow); Bool* pflag = flag.getStorage(deleteFlag); const Int nEl=flagRow.nelements(); DebugAssert(nEl*nXY==Int(flag.nelements()),AipsError); Int offset=0; for (Int i=0; i& medTF, Array& adTF, Array& medT, Array& medFmedT, Array& adT, Array& medF, Array& medTmedF, Array& adF, const Array& diff, const Array& flag, const Array& flagRow) { IPosition shape=diff.shape(); const Int nCorr=shape(0); const Int nChan=shape(1); Int nTime=shape(2); Int nIfr=1; const Int nXY=nCorr*nChan; Array diff2(diff); if (diff.ndim()==3) { // make 4D reference to diff's storage so diffMedian will return // correct shapes Array ref(diff2.reform(IPosition(4,nCorr,nChan,nIfr,nTime))); diff2.reference(ref); } else { nIfr=nTime; nTime=shape(3); } const Int nXYZ=nXY*nIfr; Bool deleteFlag, deleteFlagRow, deleteDiff; const Bool* pflagRow = flagRow.getStorage(deleteFlagRow); const Bool* pflag = flag.getStorage(deleteFlag); const Float* pdiff = diff2.getStorage(deleteDiff); medTF.resize(IPosition(2,nCorr,nIfr)); adTF.resize(IPosition(2,nCorr,nIfr)); medT.resize(IPosition(3,nCorr,nChan,nIfr)); medFmedT.resize(IPosition(2,nCorr,nIfr)); adT.resize(IPosition(2,nCorr,nIfr)); medF.resize(IPosition(3,nCorr,nIfr,nTime)); medTmedF.resize(IPosition(2,nCorr,nIfr)); adF.resize(IPosition(2,nCorr,nIfr)); // calculate medians over time diffMedian(medT,diff2,3,flag); // calculate median over channel of median over time diffMedian(medFmedT,medT,1, (medT<=0.0f)); // calculate median over channel diffMedian(medF,diff2,1, flag); // calculate median over time of median over channel diffMedian(medTmedF,medF,2, (medF<=0.0f)); // make a guess at the overal median (per pol and ifr) min(medTF,medTmedF,medFmedT); // calculate average absolute deviation of medians over time { Bool deletemedT; const Float* pmedT=medT.getStorage(deletemedT); Int offset=0; IPosition polifr(2); for (Int pol=0; pol0) { count++; ad+=abs(pmedT[offchan]-med); } } if (count>1) ad/=count; adT(polifr)=ad; } } medT.freeStorage(pmedT,deletemedT); } // calculate average absolute deviation of medians over channel { Bool deletemedF; const Float* pmedF=medF.getStorage(deletemedF); Int offset=0, nXZ=nCorr*nIfr; IPosition polifr(2); for (Int pol=0; pol1) ad/=count; adF(polifr)=ad; } } medF.freeStorage(pmedF,deletemedF); } // calculate overall average deviation (per pol and ifr) { IPosition polifr(2); for (Int pol=0; pol1) ad/=count; adTF(polifr)=ad; } } } flag.freeStorage(pflag,deleteFlag); flagRow.freeStorage(pflagRow,deleteFlagRow); diff2.freeStorage(pdiff,deleteDiff); } void MSFlagger::diffMedian(Array& out, const Array& in, Int axis, const Array& flag) { // collapse array "in" (with absolute differences) // along specified axis by taking medians by profile taking into account // the flags. Int nDim=in.ndim(); DebugAssert(axis>=0 && axis0, AipsError); IPosition inShape=in.shape(), outShape(max(1,Int(in.ndim())-1)); outShape(0)=1; // cope with 1-d input Int nLess=1, nGreater=1, nAxis=inShape(axis); for (Int i=0, count=0; iaxis) nGreater*=inShape(i); } out.resize(outShape); Bool deleteIn, deleteFlag, deleteOut; const Float* pin=in.getStorage(deleteIn); const Bool* pflag=flag.getStorage(deleteFlag); Float* pout=out.getStorage(deleteOut); Block values(nAxis); for (Int j=0, offj=0; j0) pout[offout]=median(Vector(values.begin(),values.begin()+count)); else pout[offout]=0; } } in.freeStorage(pin,deleteIn); flag.freeStorage(pflag,deleteFlag); out.putStorage(pout,deleteOut); } inline String multiple(Int n) { return n!=1 ? "s" : ""; } Bool MSFlagger::clipDataBuffer(Float pixelLevel, Float timeLevel, Float channelLevel) { LogIO os; if (!buffer_p.isDefined("datafield")) { os << LogIO::WARN << "No data loaded into buffer yet"<< ", use fillbuffer first"<< LogIO::POST; return False; } String item = buffer_p.asString(RecordFieldId("datafield")); if (item.contains("data")) { os << LogIO::WARN << "Can't clip complex data,"<< " use diffbuffer first or load a derived quantity"<< LogIO::POST; return False; } // retrieve the data Array flag = buffer_p.asArrayBool(RecordFieldId("flag")); Array flagRow = buffer_p.asArrayBool(RecordFieldId("flag_row")); Array diff = buffer_p.asArrayFloat(RecordFieldId(item)); // retrieve the stats Matrix adT, adF, medTF, adTF, medFmedT, medTmedF; Cube medT, medF; if (!buffer_p.isDefined("medTF")) { // we haven't got stats yet applyRowFlags(flag,flagRow); // need to apply row flags to flags for stats addStats(buffer_p,flag,flagRow,diff); } medTF = buffer_p.asArrayFloat("medTF"); adTF = buffer_p.asArrayFloat("adTF"); medT = buffer_p.asArrayFloat("medT"); medFmedT = buffer_p.asArrayFloat("medFmedT"); adT = buffer_p.asArrayFloat("adT"); medF = buffer_p.asArrayFloat("medF"); medTmedF = buffer_p.asArrayFloat("medTmedF"); adF = buffer_p.asArrayFloat("adF"); /* GlishArray(buffer_p.get("adTF")).get(adTF); GlishArray(buffer_p.get("medT")).get(medT); GlishArray(buffer_p.get("medFmedT")).get(medFmedT); GlishArray(buffer_p.get("adT")).get(adT); GlishArray(buffer_p.get("medF")).get(medF); GlishArray(buffer_p.get("medTmedF")).get(medTmedF); GlishArray(buffer_p.get("adF")).get(adF); */ Bool deleteFlag, deleteFlagRow, deleteDiff; Bool* pflagRow = flagRow.getStorage(deleteFlagRow); Bool* pflag = flag.getStorage(deleteFlag); const Float* pdiff = diff.getStorage(deleteDiff); const Int nCorr=flag.shape()(0); const Int nChan=flag.shape()(1); Int nTime=flag.shape()(2); const Int nXY=nCorr*nChan; Int nIfr=1; if (flag.ndim()==4) { nIfr=nTime; nTime=flag.shape()(3); } const Int nXYZ=nXY*nIfr; // iterate till no more pixels are flagged Bool iter=True; Matrix sum(nCorr,nIfr),sumChan(nCorr,nIfr),sumTime(nCorr,nIfr); sum=0, sumChan=0, sumTime=0; while (iter) { iter=False; for (Int ifr=0, offset=0; ifr0) && (abs(mt-mfmt) > channelLevel*adt)) { chanCount++; for (Int j=0, offset3=offset2; j0 && abs(mf-mtmf) > timeLevel*adf) { timeCount++; for (Int j=0, offset3=offset2; j pixelLevel*adtf) { pflag[offset3]=True; count++; } } } } } iter= (iter || chanCount>0 || timeCount>0 ||count>0); sumChan(pol,ifr)+=chanCount; sumTime(pol,ifr)+=timeCount; sum(pol,ifr)+=count; } } if (iter) { if (deleteFlag||deleteFlagRow) { cerr << " arrays have to be written back "<0 || sumTime(pol,ifr)>0 || sum(pol,ifr)>0)) { if (nIfr>1) { os << LogIO::NORMAL << "Polarization# = "<< pol+1 << ", Interferometer# = "<< ifr+1 << LogIO::POST; } else if (nCorr>1) { os << LogIO::NORMAL << "Polarization# = "<< pol+1 << LogIO::POST; } } if (sumChan(pol,ifr)>0) { os << LogIO::NORMAL << "Flagged "< "<0) { os << LogIO::NORMAL << "Flagged "< "<< timeLevel << "*" <0) { os << LogIO::NORMAL << "Flagged "< "<< pixelLevel << "*" << adTF(pol,ifr)<selectedTable().isWritable()) { os << LogIO::SEVERE << "MeasurementSet is not writable"<< LogIO::POST; return False; } if (!buffer_p.isDefined("datafield")) { os << LogIO::WARN << "Data buffer is empty, use filldatabuffer first"<< LogIO::POST; return False; } Record items(RecordInterface::Variable); items.define("flag_row",buffer_p.asArrayBool(RecordFieldId("flag_row"))); items.define("flag",buffer_p.asArrayBool(RecordFieldId("flag"))); return msSel_p->putData(items); } Bool MSFlagger::createFlagHistory(Int nHis) { LogIO os; if (!check()) return False; MeasurementSet tab=msSel_p->selectedTable(); if (!tab.isWritable()) { os << LogIO::WARN << "MS is not writable"<< LogIO::POST; return False; } if (nHis<2 || nHis>16) { os << LogIO::WARN << "Invalid argument: 2<=nHis<=16 "<< LogIO::POST; return False; } if (tab.isColumn(MS::FLAG_CATEGORY)) { os << LogIO::WARN << "FLAG_CATEGORY column already exists"< coordColNames(0), idColNames(1); TableDesc td1; if (!found) { // If there's no id, assume the data is fixed shape throughout ArrayColumn flagCol(tab,MS::columnName(MS::FLAG)); Int numCorr=flagCol.shape(0)(0); Int numChan=flagCol.shape(0)(1); IPosition shape(3,nHis,numCorr,numChan); idColNames.resize(0); td1.addColumn(ArrayColumnDesc("FLAG_CATEGORY","flag history",shape, ColumnDesc::Direct)); td1.defineHypercolumn("TiledFlagHistory",4, stringToVector("FLAG_CATEGORY"),coordColNames, idColNames); // fixed data shape Int tileSize=numChan/10+1; IPosition tileShape(4,1,numCorr,tileSize,16384/numCorr/tileSize); TiledColumnStMan tiledStMan1("TiledFlagHistory",tileShape); tab.addColumn(td1,tiledStMan1); fillFlagHist(nHis,numCorr,numChan,tab); } else { { ArrayColumn flagCol(tab,MS::columnName(MS::FLAG)); idColNames(0)="FLAG_CATEGORY_HYPERCUBE_ID"; td1.addColumn(ArrayColumnDesc("FLAG_CATEGORY","flag history",3)); td1.addColumn(ScalarColumnDesc("FLAG_CATEGORY_HYPERCUBE_ID", "hypercube index")); td1.defineHypercolumn("TiledFlagHistory",4, stringToVector("FLAG_CATEGORY"),coordColNames, idColNames); // data shape may change TiledDataStMan tiledStMan1("TiledFlagCategory"); tab.addColumn(td1,tiledStMan1); TiledDataStManAccessor flagCatAccessor(tab,"TiledFlagCategory"); // get the hypercube ids, sort them, remove the duplicate values ScalarColumn hypercubeId(tab,flagHypercubeId); Vector ids=hypercubeId.getColumn(); Int nId=genSort(ids,Sort::Ascending,Sort::QuickSort+Sort::NoDuplicates); ids.resize(nId,True); // resize and copy values Vector cubeAdded(nId,False); Record values1; values1.define("FLAG_CATEGORY_HYPERCUBE_ID",hypercubeId(0)); Int cube; for (cube=0; cube0 && hypercubeId(i)!=hypercubeId(i-1)) { values1.define("FLAG_CATEGORY_HYPERCUBE_ID",hypercubeId(i)); for (cube=0; cube flagCol(obsIter.table(),MS::columnName(MS::FLAG)); Int numCorr=flagCol.shape(0)(0); Int numChan=flagCol.shape(0)(1); Table tab=obsIter.table(); fillFlagHist(nHis,numCorr,numChan,tab); } } return True; } Bool MSFlagger::findHypercubeId(String& hypercubeId, const String& column, const Table& tab) { // to find the corresponding id column (if any) TableDesc td(tab.tableDesc()); Vector hypercolumnNames=td.hypercolumnNames(); Bool found=False; hypercubeId=""; if (hypercolumnNames.nelements()>0) { for (uInt i=0; i colNames,coordColNames,idColNames; td.hypercolumnDesc(hypercolumnNames(i), colNames,coordColNames, idColNames); for (uInt j=0; j0); if (found) hypercubeId=idColNames(0); } } } } return found; } void MSFlagger::fillFlagHist(Int nHis, Int numCorr, Int numChan, Table& tab) { // fill the first two levels of flagging with the flags present // in the MS columns FLAG and FLAG_ROW. const rownr_t maxRow=1000000/(numCorr*numChan); // of order 1 MB chunks ArrayColumn flagCol(tab,MS::columnName(MS::FLAG)); ArrayColumn flagHisCol(tab,MS::columnName(MS::FLAG_CATEGORY)); Array flagHis(IPosition(4,nHis,numCorr,numChan,maxRow)); // flag level 0 Cube ref0(flagHis(IPosition(4,0,0,0,0), IPosition(4,0,numCorr-1,numChan-1,maxRow-1)). reform(IPosition(3,numCorr,numChan,maxRow))); // flag level 1 Cube ref1(flagHis(IPosition(4,1,0,0,0), IPosition(4,1,numCorr-1,numChan-1,maxRow-1)). reform(IPosition(3,numCorr,numChan,maxRow))); flagHis.set(False); rownr_t nRow=tab.nrow(); ScalarColumn flagRowCol(tab,MS::columnName(MS::FLAG_ROW)); Array flagCube; Vector flagRowVec; for (rownr_t i=0; i<=(nRow/maxRow); i+=maxRow) { rownr_t n=min(maxRow,nRow-maxRow*i); if (n tmp0(flagHis(IPosition(4,0,0,0,0), IPosition(4,0,numCorr-1,numChan-1,n-1)). reform(IPosition(3,numCorr,numChan,n))); ref0.reference(tmp0); Array tmp1(flagHis(IPosition(4,1,0,0,0), IPosition(4,1,numCorr-1,numChan-1,n-1)). reform(IPosition(3,numCorr,numChan,n))); ref1.reference(tmp1); } Slicer rowSlice(Slice(i*maxRow,n)); flagRowCol.getColumnRange(rowSlice,flagRowVec,True); flagCol.getColumnRange(rowSlice,flagCube,True); ref0=flagCube; for (rownr_t j=0; jselectedTable(); if (!tab.isColumn(MS::FLAG_CATEGORY)) { os << LogIO::WARN << "FLAG_CATEGORY column does not exist"< flagHisCol(tab,MS::columnName(MS::FLAG_CATEGORY)); Int level; flagHisCol.keywordSet().get("FLAG_LEVEL",level); if (newLevel) { if (level+1>=flagHisCol.shape(0)(0)) { os << LogIO::WARN << "No space for new flag level ("<<(level+1)+1<<") in " << "FLAG_CATEGORY column, using current level instead"< flagCol(tab,MS::columnName(MS::FLAG)); Int numCorr=flagCol.shape(0)(0); Int numChan=flagCol.shape(0)(1); const rownr_t maxRow=1000000/(numCorr*numChan); // of order 1 MB chunks Array flagHis(IPosition(4,1,numCorr,numChan,maxRow)); Cube ref(flagHis.reform(IPosition(3,numCorr,numChan,maxRow))); rownr_t nRow=tab.nrow(); Array flagCube; Vector flagRowVec; Slicer slicer(Slice(level,1),Slice(0,numCorr),Slice(0,numChan)); for (rownr_t i=0; i<=(nRow/maxRow); i+=maxRow) { rownr_t n=min(maxRow,nRow-maxRow*i); if (n tmp(flagHis.reform(IPosition(3,numCorr,numChan,n))); ref.reference(tmp); } RowNumbers rows(n); indgen(rows, i*maxRow); Table sel=tab(rows); ArrayColumn flagHisCol(sel,MS::columnName(MS::FLAG_CATEGORY)); ArrayColumn flagCol(sel,MS::columnName(MS::FLAG)); ScalarColumn flagRowCol(sel,MS::columnName(MS::FLAG_ROW)); flagCol.getColumn(flagCube,True); flagRowCol.getColumn(flagRowVec,True); ref=flagCube; for (rownr_t j=0; jselectedTable(); if (!tab.isColumn(MS::FLAG_CATEGORY)) { os << LogIO::WARN << "FLAG_CATEGORY column does not exist"< flagHisCol(tab,MS::columnName(MS::FLAG_CATEGORY)); Int flagLevel=level; if (flagLevel==-1) flagHisCol.keywordSet().get("FLAG_LEVEL",flagLevel); if (flagLevel<0 || flagLevel>=flagHisCol.shape(0)(0)) { os << LogIO::WARN << "Invalid flag level ("< flagHisCol(tab,MS::columnName(MS::FLAG_CATEGORY)); IPosition shape=flagHisCol.shape(0); shape(0)=1; const rownr_t maxRow=1000000/(shape(1)*shape(2)); // of order 1 MB chunks Slicer slicer(Slice(level,1),Slice(0,shape(1)),Slice(0,shape(2))); for (rownr_t i=0; i<=nRow/maxRow; i++) { rownr_t n=min(maxRow,nRow-i*maxRow); RowNumbers rows(n); indgen(rows, i*maxRow); Table sel=tab(rows); ArrayColumn flagHisCol(sel,MS::columnName(MS::FLAG_CATEGORY)); Cube flag(flagHisCol.getColumn(slicer). reform(IPosition(3,shape(1),shape(2),n))); ArrayColumn flagCol(sel,MS::columnName(MS::FLAG)); ScalarColumn flagRowCol(sel,MS::columnName(MS::FLAG_ROW)); flagCol.putColumn(flag); for (rownr_t j=0; jselectedTable(); if (!tab.isColumn(MS::FLAG_CATEGORY)) { os << LogIO::WARN << "FLAG_CATEGORY column does not exist"< flagHisCol(tab,MS::columnName(MS::FLAG_CATEGORY)); Int flagLevel; flagHisCol.keywordSet().get("FLAG_LEVEL",flagLevel); return flagLevel; } Bool MSFlagger::check() { LogIO os; if (msSel_p) return True; os << LogIO::WARN << "Flagger is uninitialized"< #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class MSSelector; class Table; class String; // // MSFlagger specifies selections on a MeasurementSet // // // // // //
      • MeasurementSet //
      • SomeOtherClass //
      • some concept // // // // MSFlagger is a class that sets flags in an MS // // // // This class is used to change the flag and flag_history columns in // a MeasurementSet. It provides functions for automated flagging based on // clipping the data that is too far from the median value. // The ms DO uses this class to allow flagging from glish or a GUI. // // // MSFlagger msFlagger(myMS); // // // // // Flagging/editing of data is a central requirement in data processing, this // class provides some simple flagging algorithms and the code // that modifies & creates flag columns in the MS. // // // //
      • //
      • // // // //
      • add this feature // class MSFlagger { public: MSFlagger(); // construct from an MSSelector object MSFlagger(MSSelector& msSel); // Copy constructor MSFlagger(const MSFlagger& other); // Assignment MSFlagger& operator=(const MSFlagger& other); ~MSFlagger(); // Change or Set the MS this MSFlagger refers to. void setMSSelector(MSSelector& msSel); // Fill an internal buffer with the data item requested, similar to getData // except that the data is not returned, but kept around for further // processing. Only a single DATA related quantity can be requested, the // corresponding FLAG and FLAG_ROW columns are read automatically. // Reorder the data to 4d with ifr and time axis if ifrAxis is True. Bool fillDataBuffer(const String& item, Bool ifrAxis); // Difference the data, subtracting the average over a window of // specified width and taking the absolute value. Complex quantities are // turned into the corresponding amplitude after differencing. // If doMedian==True the median difference is returned for window>2. // For a window width of one, the previous sample is // subtracted, giving a derivative like quantity. // Note that the subtraction is done on row-by-row basis for TIME // differencing, it is up to you to select a single baseline (if // you didn't use ifrAxis=True in fillDataBuffer). // Available directions are: TIME, CHANNEL // Returns statistics over the buffer: median for times and channels, // average absolute deviation over times, channels and all pixels. Record diffDataBuffer(const String& direction, Int window=1, Bool doMedian = False); // Return the contents of the internal data buffer, including the flags // as a Record Record getDataBuffer() { return buffer_p;} // Clip the data buffer at a specified level by setting the corresponding // flags in the buffer. The cliplevel is specified as a multiple of // the average absolute deviations returned by diffDataBuffer. // A value of zero or less will skip the corresponding clip operation. // Clipping will be done repeatedly, recalculating the deviations, until // no more points are clipped. Bool clipDataBuffer(Float pixelLevel, Float timeLevel, Float channelLevel); // Replace the flags in the buffer with those in the supplied record. // This allows interactive flagging from glish to be written back to the // buffer for subsequent operations. The record should contain a // flag and flag_row field. Bool setDataBufferFlags(const Record& flags); // Write the flags in the buffer back to the table Bool writeDataBufferFlags(); // Clear the internal data buffer, reclaiming memory Bool clearDataBuffer() { buffer_p=Record(); return True;} // Create the FLAG_HISTORY column and initialize it from the // FLAG_ROW and FLAG columns. Returns False if FLAG_HISTORY already exists. // The first flagging bit is filled with the flags as found in the MS, // subsequent bits can be used for user generated flags. Bool createFlagHistory(Int nHis = 2); // Apply the flags in the FLAG_HISTORY column to the FLAG and FLAG_ROW // columns. Returns False if FLAG_HISTORY doesn't exist. // The default argument will apply the currently active flag level // (as specified by the FLAG_LEVEL column keyword). // Sets the current level to the flag level restored. Bool restoreFlags(Int level=-1); // Save the current flags to the FLAG_HISTORY. Save to the currently // active level or (newLevel=True) the next highest level (if available). // Will reset the current level to the level saved to. Bool saveFlags(Bool newLevel); // Return the current flaglevel (value of FLAG_LEVEL keyword) Int flagLevel(); protected: // fill the FLAG_HISTORY column from the FLAG and FLAG_ROW column void fillFlagHist(Int nHis, Int numCorr, Int numChan, Table& tab); // find the HypercubeId column for a tiled column (if any) Bool findHypercubeId(String& hyperCubeId, const String& column, const Table& tab); // copy the flags to the flag history void saveToFlagHist(Int level, Table& tab); // copy the flag history back to the flags void applyFlagHist(Int level, Table& tab); // get buffer statistics - med=median, ad=average absolute deviation, // T=Time, F=Frequency. void getStats(Array& medTF, Array& adTF, Array& medT, Array& medFmedT, Array& adT, Array& medF, Array& medTmedF, Array& adF, const Array& diff, const Array& flag, const Array& flagRow); // add the statistics to a buffer void addStats(Record& buf, const Array& flag, const Array flagRow, const Array& data); // reorder from 2d to 1d (removing ifr axis) void reorderFlagRow(Array& flagRow); // collapse array "in" (with absolute differences) // along specified axis by taking medians by profile taking into account // the flags. void diffMedian(Array& out, const Array& in, Int axis, const Array& flag); // apply the row flags to the data flags and v.v. void applyRowFlags(Array& flag, Array& flagRow); // check if we are attached to an MSSelector Bool check(); private: MSSelector* msSel_p; Record buffer_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSOper/MSKeys.cc000066400000000000000000000123601476623553700165740ustar00rootroot00000000000000//# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include #include namespace casacore { Bool operator<(const SubScanKey& lhs, const SubScanKey& rhs) { if (lhs.obsID < rhs.obsID) { return True; } else if (lhs.obsID == rhs.obsID) { if (lhs.arrayID < rhs.arrayID) { return True; } else if (lhs.arrayID == rhs.arrayID) { if (lhs.scan < rhs.scan) { return True; } else if (lhs.scan == rhs.scan) { if (lhs.fieldID < rhs.fieldID) { return True; } } } } return False; } String toString(const SubScanKey& subScanKey) { return toString(scanKey(subScanKey)) + " fieldID=" + String::toString(subScanKey.fieldID); } std::ostream& operator<<(std::ostream& os, const SubScanKey& subScanKey) { os << toString(subScanKey) << endl; return os; } String toString(const ScanKey& scanKey) { return "observationID=" + String::toString(scanKey.obsID) + " arrayID=" + String::toString(scanKey.arrayID) + " scan number=" + String::toString(scanKey.scan); } Bool operator<(const ScanKey& lhs, const ScanKey& rhs) { if (lhs.obsID < rhs.obsID) { return True; } else if (lhs.obsID == rhs.obsID) { if (lhs.arrayID < rhs.arrayID) { return True; } else if (lhs.arrayID == rhs.arrayID) { if (lhs.scan < rhs.scan) { return True; } } } return False; } Bool operator==(const ScanKey& lhs, const ScanKey& rhs) { return lhs.obsID == rhs.obsID && lhs.arrayID == rhs.arrayID && lhs.scan == rhs.scan; } std::set scanNumbers(const std::set& scanKeys) { std::set scanNumbers; std::set::const_iterator iter = scanKeys.begin(); std::set::const_iterator end = scanKeys.end(); while (iter != end) { scanNumbers.insert(iter->scan); ++iter; } return scanNumbers; } ostream& operator<<(ostream& os, const ScanKey& scanKey) { os << toString(scanKey) << endl; return os; } Bool operator<(const ArrayKey& lhs, const ArrayKey& rhs) { if (lhs.obsID < rhs.obsID) { return True; } else if (lhs.obsID == rhs.obsID) { if (lhs.arrayID < rhs.arrayID) { return True; } } return False; } std::set scanKeys( const std::set& scans, const ArrayKey& arrayKey ) { std::set scanKeys; std::set::const_iterator iter = scans.begin(); std::set::const_iterator end = scans.end(); ScanKey scanKey; scanKey.obsID = arrayKey.obsID; scanKey.arrayID = arrayKey.arrayID; while (iter != end) { scanKey.scan = *iter; scanKeys.insert(scanKey); ++iter; } return scanKeys; } Bool operator<(const SourceKey& lhs, const SourceKey& rhs) { if (lhs.id < rhs.id) { return True; } else if (lhs.id == rhs.id && lhs.spw < rhs.spw) { return True; } return False; } std::set uniqueArrayKeys(const std::set& scanKeys) { std::set arrayKeys; std::set::const_iterator iter = scanKeys.begin(); std::set::const_iterator end = scanKeys.end(); ArrayKey akey; while (iter != end) { akey.arrayID = iter->arrayID; akey.obsID = iter->obsID; arrayKeys.insert(akey); ++iter; } return arrayKeys; } std::set filter(const std::set scans, const ArrayKey& arrayKey) { std::set subset; std::set::const_iterator iter = scans.begin(); std::set::const_iterator end = scans.end(); ArrayKey arrayFromScan; for (; iter!=end; ++iter) { arrayFromScan.arrayID = iter->arrayID; arrayFromScan.obsID = iter->obsID; if (arrayFromScan == arrayKey) { subset.insert(*iter); } else if (arrayKey < arrayFromScan) { // take advantage of implicit sorting break; } } return subset; } } casacore-3.7.1/ms/MSOper/MSKeys.h000066400000000000000000000076741476623553700164520ustar00rootroot00000000000000//# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSKEYS_H #define MS_MSKEYS_H #include #include #include namespace casacore { class String; // A sub scan is a unique combination of observation ID, array ID, scan number, // and field ID. Negative values are allowed to indicate all values of the particular // ID are desired. struct SubScanKey { Int obsID; Int arrayID; Int scan; Int fieldID; }; // define operator<() so it can be used as a key in std::map Bool operator<(const SubScanKey& lhs, const SubScanKey& rhs); String toString(const SubScanKey& subScanKey); std::ostream& operator<<(std::ostream& os, const SubScanKey& scanKey); // A scan is a unique combination of observation ID, array ID, and scan number // Negative values are allowed to indicate all values of the particular // ID are desired. struct ScanKey { Int obsID; Int arrayID; Int scan; }; // create a ScanKey from a SubScanKey, just omits the SubScanKey's fieldID inline ScanKey scanKey(const SubScanKey& subScanKey) { ScanKey key; key.obsID = subScanKey.obsID; key.arrayID = subScanKey.arrayID; key.scan = subScanKey.scan; return key; } String toString(const ScanKey& scanKey); // define operator<() so it can be used as a key in std::map Bool operator<(const ScanKey& lhs, const ScanKey& rhs); Bool operator==(const ScanKey& lhs, const ScanKey& rhs); // extract all the unique scan numbers from the specified scans std::set scanNumbers(const std::set& scanKeys); std::ostream& operator<<(std::ostream& os, const ScanKey& scanKey); // An ArrayKey is a unique combination of observation ID and array ID // Negative values are allowed to indicate all values of the particular // ID are desired. struct ArrayKey { Int obsID; Int arrayID; }; // define operator<() so it can be used as a key in std::map Bool operator<(const ArrayKey& lhs, const ArrayKey& rhs); inline Bool operator==(const ArrayKey& lhs, const ArrayKey& rhs) { return lhs.arrayID == rhs.arrayID && lhs.obsID == rhs.obsID; } inline Bool operator!=(const ArrayKey& lhs, const ArrayKey& rhs) { return ! (lhs == rhs); } // construct scan keys given a set of scan numbers and an ArrayKey std::set scanKeys(const std::set& scans, const ArrayKey& arrayKey); // represents primary key in the SOURCE table struct SourceKey { // SOURCE_ID column uInt id; uInt spw; }; // define operator<() so it can be used as a key in std::map Bool operator<(const SourceKey& lhs, const SourceKey& rhs); // get a set of unique ArrayKeys from a set of ScanKeys std::set uniqueArrayKeys(const std::set& scanKeys); // given a set of scan keys, return the subset that matches the given array key std::set filter(const std::set scans, const ArrayKey& arrayKey); } #endif casacore-3.7.1/ms/MSOper/MSLister.cc000066400000000000000000001540651476623553700171340ustar00rootroot00000000000000//# mslister.cc: class for listing records from a measurementset //# copyright (c) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Null constructor merely sets private formatting string // MSLister::MSLister () : dashline_p(replicate('-',80)), mss_p() { pMSSel_p = 0; } // // Constructor assigns pointer (if MS goes out of scope you will get rubbish), // initialises the output, sets string to format output, and initialises the // listing. // MSLister::MSLister (const MeasurementSet& ms, LogIO& os) // : pMS_p(&ms), : pMS_p(const_cast(&ms)), logStream_p(os), dashline_p(replicate('-',80)), mss_p() { // Move these into initList()? // default precision (in case setPrecision is not called) precTime_p = 7; // hh:mm:ss.s (0.1 s) precUVDist_p = 0; // 1 wavelength precUVW_p = 2; // 1 centimenter precAmpl_p = 3; // mJy precPhase_p = 1; // 0.1 deg precWeight_p = 0; // unit weight // initializations wFld_p = wSpW_p = wChn_p = 0; is_float = False; //default datacolumn is complex // initialize list params initList(); pMSSel_p = 0; } // // Assignment operator // MSLister& MSLister::operator=(MSLister& other) { if (this==&other) return *this; pMS_p = other.pMS_p; return *this; } // // Destructor does nothing // MSLister::~MSLister() {} // // Reinitialise output stream. Do this before setMS() if doing both. // Bool MSLister::setNewOS (LogIO& os) { logStream_p = os; return True; } // // Reassign MS pointer and reinitialise MSLister object. Do this after // setNewOS() if doing both. // Bool MSLister::setMS (MeasurementSet& ms) { pMS_p = &ms; initList(); return True; } // // initList() does things that need to be done once per MS: initialises // the pagination/formatting, lists some header information, initialises // the MSSelector object, and gets all the attribute ranges up front. // void MSLister::initList() { // Establish the formatting // setFormat(); // the # of decimal places for data // // Initialise the MSSelector object. By default, initSelection() takes all // // polarisations and the first spectral channel. // mss_p.setMS(*pMS_p); // // mss_p.initSelection(); // Get the ranges (into ranges_p) of the following usefully // selectable attributes. range_p can be changed later to // refine selection. // **SPW** items_p.resize(6,False); items_p(0)="time"; // the range of times items_p(1)="antenna1"; // the list of antenna1 id values items_p(2)="antenna2"; // the list of antenna2 id values items_p(3)="uvdist"; // the range of the UV-distance (m) // items_p(4)="spectral_window_id"; // the list of spwin id values items_p(4)="data_desc_id"; // the list of data desc id values items_p(5)="field_id"; // the list of field id values getRanges(*pMS_p); // Set up for selection on channel or polarisation MSSpWindowColumns msSpWinC(pMS_p->spectralWindow()); MSPolarizationColumns msPolC(pMS_p->polarization()); // nchan_p = msSpWinC.numChan()(0); npols_p = msPolC.corrType()(0).nelements(); pols_p.resize(npols_p,False); for (uInt i=0; idataDescription()); spwins_p=msDDI.spectralWindowId().getColumn(); // Signal completion of initList logStream_p << LogIO::NORMAL1 << "Listing initialised for this MS" << LogIO::POST; } // This function is not currently used. However, if an output precision // option is added to this class, this will be useful. void MSLister::setFormat (const uInt ndec) { // Set up data display precision nDecimal_p = ndec; } void MSLister::setPrecision ( const Int precTime, const Int precUVDist, const Int precAmpl, const Int precPhase, const Int precWeight ) { // Set private precision vars on basis of user input: precTime_p = precTime+6; // internally, time precision includes hhmmss. precUVDist_p = precUVDist; precAmpl_p = precAmpl; precPhase_p = precPhase; precWeight_p = precWeight; } // CLEANUP: This function is currently not used. Remove it? void MSLister::listHeader() { // Construct the MSSummary object and output the header info // ALL OF THESE SHOULD BE GIVEN PRIORITY NORMAL1. MSSummary header(*pMS_p); header.listTitle (logStream_p); header.listWhat (logStream_p,False); header.listSpectralWindow (logStream_p,True); header.listPolarization (logStream_p,True); header.listAntenna (logStream_p,True); logStream_p.post(); } // Get the ranges of a fixed set of MS key attributes void MSLister::getRanges(const MeasurementSet &ms) { logStream_p << LogIO::DEBUG1 << "Begin: MSLister::getRanges" << LogIO::POST; // Get the full range of columns for an MS. MSRange msr(ms); ranges_p = msr.range(items_p); // shapeChangesWarning OCCURRING HERE logStream_p << LogIO::DEBUG1 << "End: MSLister::getRanges" << LogIO::POST; } void MSLister::list (const String&, const String& datacolumn, const String& field, const String& spw, const String& antenna, const String& timerange, const String& correlation, const String& scan, const String&, const String&, const String& observation, const String& uvrange, const String&, const bool , const String& msSelect, const long pagerows, const String& listfile) { try{ logStream_p << LogIO::DEBUG1 << "Begin: MSLister::list" << LogIO::POST; String chanmode; Int nchan; Int start; Int step; MRadialVelocity mStart; MRadialVelocity mStep; // Empty spw string means select all spws and channels. To do this in // MSSelection, spw must be set to "*". String newSpw = spw; if(newSpw.empty()) { newSpw = "*"; } // Choose MSSelector keywords according to the value of datacolumn dataColSel.resize(2); if( datacolumn.empty() || datacolumn == "data") { dataColSel(0) = "amplitude"; dataColSel(1) = "phase"; } else if (datacolumn == "float_data"){ dataColSel(0) = "float_data"; dataColSel(1) = ""; is_float = True; } else if (datacolumn == "corrected") { dataColSel(0) = "corrected_amplitude"; dataColSel(1) = "corrected_phase"; } else if (datacolumn == "model") { dataColSel(0) = "model_amplitude"; dataColSel(1) = "model_phase"; } else if (datacolumn == "residual") { dataColSel(0) = "residual_amplitude"; dataColSel(1) = "residual_phase"; } else { logStream_p << LogIO::SEVERE << "datacolumn = " << datacolumn << LogIO::POST; throw(AipsError("Unrecognized value in parameter datacolumn")); } // logStream_p << "dataColSel = " << dataColSel << LogIO::POST; // cout << "dataColSel = " << dataColSel << endl; // Fill unused variables to avoid compiler warnings. nchan = 0; start = 0; step = 0; selectvis(timerange, newSpw, scan, field, antenna, uvrange, chanmode, nchan, start, step, mStart, mStep, correlation, // IGNORE PARAMETERS THAT ARE NOT YET IMPLEMENTED // array, msSelect); "", observation, msSelect); // List the data listData(pagerows, listfile); } catch (const std::exception& x) { logStream_p << LogOrigin("MSLister","list",WHERE) << LogIO::SEVERE << "Caught exception: " << x.what() << LogIO::POST; throw(AipsError("Error in MSLister::list")); } } // // Select data (using MSSelection syntax) // // Questions: Is it necessary to sort the MS? Leaving sorting code for now. // CLEANUP: Remove parameters that are not inputs to mssSetData; they are no // longer used anywhere. void MSLister::selectvis(const String& timerange, const String& spw, const String& scan, const String& field, const String& antenna, const String& uvrange, const String&, // Not inputs to mssSetData const Int&, const Int&, const Int&, const MRadialVelocity&, const MRadialVelocity&, const String& correlation, const String& array, const String& observation, const String& msSelect) { try { logStream_p << LogIO::DEBUG1 << "Begin: MSLister::selectvis" << LogIO::POST; // List input parameter values. logStream_p << LogIO::DEBUG1 << "timerange = " << timerange << " , strlen = " << timerange.length() << endl; logStream_p << LogIO::DEBUG1 << "spw = " << spw << " , strlen = " << spw.length() << endl; logStream_p << LogIO::DEBUG1 << "scan = " << scan << " , strlen = " << scan.length() << endl; logStream_p << LogIO::DEBUG1 << "field = " << field << " , strlen = " << field.length() << endl; logStream_p << LogIO::DEBUG1 << "antenna = " << antenna << " , strlen = " << antenna.length() << endl; logStream_p << LogIO::DEBUG1 << "uvrange = " << uvrange << " , strlen = " << uvrange.length() << endl; logStream_p << LogIO::DEBUG1 << "correlation = " << correlation << " , strlen = " << correlation.length() << endl; logStream_p << LogIO::DEBUG1 << "array = " << array << " , strlen = " << array.length() << endl; logStream_p << LogIO::DEBUG1 << "observation = " << observation << " , strlen = " << observation.length() << endl; logStream_p << LogIO::DEBUG1 << "msSelect = " << uvrange << " , strlen = " << msSelect.length() << LogIO::POST; // logStream_p << "feed = " << feed << " , strlen = " << feed.length() << endl; // logStream_p << "average = " << uvrange << " , strlen = " << average.length() << endl; // logStream_p << "showflags = " << uvrange << " , strlen = " << showflags.length() << endl; // Apply selection to the original MeasurementSet if (!(timerange.empty() && spw.empty() && scan.empty() && field.empty() && antenna.empty() && uvrange.empty() && correlation.empty() && observation.empty() && msSelect.empty()) ) { logStream_p << LogIO::NORMAL1 << "Performing selection on MeasurementSet" << LogIO::POST; } else { logStream_p << LogIO::NORMAL1 << "No selection requested." << LogIO::POST; } if (pMSSel_p) { delete pMSSel_p; pMSSel_p=0; } // Assume no selection, for starters pMSSel_p = new MeasurementSet(*pMS_p); logStream_p << LogIO::DEBUG1 << "Calling calling MSSelection constructor with selection parameters." << LogIO::POST; // // mssSetData Param Names MSSelection *pMSSelection = new MSSelection(*pMS_p, MSSelection::PARSE_NOW, timerange, // timeExpr antenna, // antennaExpr field, // fieldExpr spw, // spwExpr uvrange, // uvDistExpr msSelect, // taQLExpr "", // correlation, // corrExpr scan, // scanExpr array, // arrayExpr "", // stateExpr observation); // observationExpr // Check to see if selection returned any rows. Bool nonTrivial = pMSSelection->getSelectedMS(*pMSSel_p, ""); Vector selSPW = pMSSelection->getSpwList(); MSDataDescColumns ddCols(pMS_p->dataDescription()); ScalarColumn ddpolIDs = ddCols.polarizationId(); ScalarColumn spwIDs = ddCols.spectralWindowId(); Vector selDDIDs(selSPW.size()); uInt idx = 0; for (uInt i=0; i 1) { for (uInt i=1; ideepCopy(selectedMS, Table::New); logStream_p << LogIO::NORMAL1 << selectedMS << " written." << LogIO::POST; cout << selectedMS << " written." << endl; */ // What channels are contained in the selected data? chanList_p=pMSSelection->getChanList(); logStream_p << LogIO::DEBUG1 << "pMSSelection->getChanList() = " << endl << pMSSelection->getChanList() << LogIO::POST; // Do not make a list of all channels! uInt nrowCL = chanList_p.nrow(); // Determine if more than one spw will be listed. multiSpw_p = False; Int t_spw = chanList_p(0,0); // 1st selected spw for(uInt i=1; i 0) { // for(Int j=chanList(i,1); j<=chanList(i,2); j+=chanList(i,3)) { // // push j onto end of Array channels_p // channels_p(lenChannels) = j; // lenChannels++; // } // } else { // if chanList(i,3) == 0 // // push chanList(i,1) onto end of Array channels_p // channels_p(lenChannels) = chanList(i,2); // } // } // if (chanList.nrow() == 0) { // channels_p.resize(0,False); // } // channels_p.shape(nchan_p); // If non-trivial MSSelection invoked and nrow reduced: if(nonTrivial && pMSSel_p->nrow()nrow()) { // Escape if no rows selected if (pMSSel_p->nrow()==0) throw(AipsError("Specified selection contains zero rows (no data)!")); // ...otherwise report how many rows are selected logStream_p << LogIO::NORMAL1 << "Selection reduces " << pMS_p->nrow() << " rows to " << pMSSel_p->nrow() << " rows." << LogIO::POST; } else { // Selection did nothing: logStream_p << LogIO::NORMAL2 << "Selection did not drop any rows." << LogIO::POST; } // Set up for selection on channel or polarisation MSSpWindowColumns msSpWinC(pMSSel_p->spectralWindow()); /// // If no channels were specified for selection, select all by default. /// if (nchan_p== 0) { /// nchan_p = msSpWinC.numChan()(0); /// channels_p.resize(nchan_p); /// for(Int i=0; ipolarization()); // npols_p = msPolC.corrType()(0).nelements(); // pols_p.resize(npols_p,False); // for (uInt i=0; i uInt maxDigitsToPrint(const Array &values) { // Guard against log10(0). if (max(values) == 0) { return 1; } else { return (uInt)max(1,(Int)rint(abs(log10(abs(max(values))))+0.5)); } } void MSLister::listData(const int pageRows, const String listfile) { // Now get the data for the listing. // Currently we are extracting the data through a Record // to a Record to Arrays using MSSelector::getData, which requires // a list of items, a reasonable default set of which is defined // here as a Vector passable to MSSelector::getData // (eventually, user will have some choice here, e.g., to get // real/imag instead of amp/ph, etc.). // Eventually, it would be easier if there were public RO access to the // MSSelector::selms_p member... // **SPW** try{ char cfill = cout.fill(' '); // fill character for terminal output Bool prompt=True; // Default myout as a synonym for cout. (iostream makes it next to // impossible to declare a non-initialized ostream, or set the rdbuf of // an ofstream.) ostream myout(cout.rdbuf()); ofstream file; // Optional output file. if(listfile != "") { // non-interactive -> redirect output to file. //prompt = False; // Guard against trampling existing file File diskfile(listfile); if (diskfile.exists()) { String errmsg = "File: " + listfile + " already exists; delete it or choose a different name."; throw(AipsError(errmsg)); } else cerr << "Writing output to file: " << listfile << endl; file.open(listfile.data()); myout.rdbuf(file.rdbuf()); // DON'T redirect cout to file! } // The user will press Ctrl-C! logStream_p << LogIO::DEBUG1 << "Begin: MSLister::listData" << LogIO::POST; // FLOAT_DATA of single dish has only Amplitude if (is_float) {items_p.resize(10,False);} else {items_p.resize(11,False);} items_p(0)="time"; items_p(1)="antenna1"; items_p(2)="antenna2"; items_p(3)="uvdist"; // items_p(4)="spectral_window_id"; items_p(4)="data_desc_id"; items_p(5)="field_id"; items_p(6)=dataColSel(0); if (is_float){ items_p(7)="weight"; items_p(8)="flag"; items_p(9)="uvw"; } else { items_p(7)=dataColSel(1); items_p(8)="weight"; items_p(9)="flag"; items_p(10)="uvw"; } // Get ranges of selected data to ranges_p for use in field-width/precision // setting //getRanges(*pMSSel_p); //myout << "pMSSel_p.nrows=" << pMSSel_p->nrow() << endl; ///////////////////////////////////////////////////// //////read whole ms into mem is not practical, slow and waste memory //////so we split it into 5 mimutes chunk Block sort(2); sort[0] = MS::ANTENNA1; sort[1] = MS::ANTENNA2; Double timeInterval = 300; // 5 minutes if(pMSSel_p->isNull()){ return; } MSIter msIter(*pMSSel_p, sort, timeInterval); for (msIter.origin(); msIter.more(); msIter++){ MS splitMS = msIter.table(); if(splitMS.isNull()){ break; } if (!splitMS.nrow()) break; //myout << "splitMS.nrow()=" << splitMS.nrow() << endl; getRanges(splitMS); // UNCOMMENT TO: PRINT RECORD OF RANGES (Records can be written to ostreams, but not to LogIO.) // myout << "ranges_p = " << endl << ranges_p << endl; // From here on, all MS access should go through mss_p. // TURN ALL THIS DATA GATHERING INTO ITS OWN FUNCTION // Initialise the MSSelector object. By default, initSelection() takes all // polarizations and the first spectral channel. mss_p.setMS(splitMS); //logStream_p << LogIO::DEBUG1 << "mss_p.setMS(*pMSSel_p) finished" << LogIO::POST; mss_p.initSelection(); //logStream_p << LogIO::DEBUG1 << "mss_p.initSelection() finished" << LogIO::POST; // Now extract the selected data Record. Note that mss_p is the *selected* // data, and mss_p.getData() is an implicit Record object //logStream_p << LogIO::DEBUG2 << "Getting data from mss_p" << LogIO::POST; dataRecords_p = mss_p.getData(items_p,False); //logStream_p << LogIO::DEBUG2 << "Done getting data from mss_p" << LogIO::POST; // Construct arrays for the Record items. // The V-float declaration // appears to be necessary (instead of V-double) despite the get() // function's claim to do type promotion. Vector rowTime,uvdist; Vector ant1,ant2,spwinid,fieldid; Array flag; Array ampl,phase; Array weight; Array uvw; //myout << "type=" << dataRecords_p.type(dataRecords_p.fieldNumber("uvw")) << endl; // Fill the arrays. rowTime = dataRecords_p.asArrayDouble(RecordFieldId("time")); // ACQUIRE ANTENNA NAME VECTORS // antenna 1 if (dataRecords_p.isDefined("antenna1")) { ant1 = dataRecords_p.asArrayInt(RecordFieldId("antenna1")); } else if (dataRecords_p.isDefined("ant1")) { ant1 = dataRecords_p.asArrayInt(RecordFieldId("ant1")); } else { logStream_p << LogIO::SEVERE << "antenna1 isn't defined" << LogIO::POST; return; } // antenna 2 if (dataRecords_p.isDefined("antenna2")) { ant2 = dataRecords_p.asArrayInt(RecordFieldId("antenna2")); } else if (dataRecords_p.isDefined("ant2")) { ant2 = dataRecords_p.asArrayInt(RecordFieldId("ant2")); } else { logStream_p << LogIO::SEVERE << "antenna2 isn't defined" << LogIO::POST; return; } // Convert antenna ID Vectors to antenna name Vectors Int ant1Length = ant1.size(); // Get length of ant1 Vector antennaNames; // Hold name for each antenna Vector antNames1(ant1Length); // Antenna names for the ID's held in ant1 Vector antNames2(ant1Length); // Antenna names for the ID's held in ant2 MSAntennaColumns antCol(pMS_p->antenna()); antennaNames = antCol.name().getColumn(); for (Int i=0; i datadescid = dataRecords_p.asArrayInt("data_desc_id"); fieldid = dataRecords_p.asArrayInt(RecordFieldId("field_id")); uvw = dataRecords_p.asArrayDouble(RecordFieldId("uvw")); // dataColSel(0) (the data identified by this variable) if (dataRecords_p.isDefined(dataColSel(0))) { ampl = dataRecords_p.asArrayFloat(RecordFieldId(dataColSel(0))); } else { logStream_p << LogIO::SEVERE << "Column " << dataColSel(0) << " (for amplitude) isn't defined." << LogIO::POST; return; } // dataColSel(1) (the data identified by this variable) if (! is_float){ if (dataRecords_p.isDefined(dataColSel(1))) { phase = dataRecords_p.asArrayFloat(RecordFieldId(dataColSel(1))); } else { logStream_p << LogIO::SEVERE << "Column " << dataColSel(1) << " (for phase) isn't defined." << LogIO::POST; return; } } // weight weight = dataRecords_p.asArrayFloat(RecordFieldId("weight")); // Number of rows that will be listed Int nTableRows = rowTime.nelements(); spwinid.resize(nTableRows); // Convert units of some params: rowTime = rowTime/C::day; // time now in days if (!is_float) {phase = phase/C::degree;} // phase now in degrees // For each row: translate Data Description IDs to Spectral Window IDs // This must be done before column widths can be calculated, before // data can be written. for (Int tableRow=0;tableRow uvminmax(2); uvminmax(0)=min(uvdist); uvminmax(1)=max(uvdist); ranges_p.define("uvdist",uvminmax); //logStream_p << LogIO::DEBUG2 << " Setting data amplitude min and max values" << LogIO::POST; Vector amplminmax(2); amplminmax(0)=min(ampl(ampl>=0.0f)); amplminmax(1)=max(ampl); if(amplminmax(0) == amplminmax(1)) { myout << "All selected data has AMPLITUDE = " << amplminmax(0) << endl; } ranges_p.define(dataColSel(0),amplminmax); // Find the range of phase. Take care to avoid creating a 0-element // MaskedArray, if all elements of phase are 0.0f. A 0-element // array will crash function min. //logStream_p << LogIO::DEBUG2 << " Setting data phase min and max values" << LogIO::POST; if (! is_float){ Vector phminmax(2); phminmax(0) = min(abs(phase)); phminmax(1) = max(abs(phase)); if(phminmax(0) == phminmax(1)) { myout << "All selected data has PHASE = " << phminmax(0) << endl; } ranges_p.define(dataColSel(1),phminmax); } // HERE LIES CODE THAT I THINK IS NO LONGER NEEDED! For some reason, when the mins and maxs // were originally computed, the author looked only at the data not equal to 0.0. I don't think // this is necessary. We shall see!.. // // MaskedArray maPhase(phase, (phase!=0.0f)); // myout << "phase = " << endl << phase << endl; // myout << "maPhase.nelements() = " << maPhase.nelements() << endl; // myout << "phase(phase!=0.0f).nelements() = " << phase(phase!=0.0f).nelements() << endl; // myout << "abs(phase(phase!=0.0f)).nelements() = " << abs(phase(phase!=0.0f)).nelements() << endl; // myout << "min(abs(phase(phase!=0.0f))) = " << min(abs(phase(phase!=0.0f))) << endl; // myout << "min(phase(phase!=0.0f)) = " << min(phase(phase!=0.0f)) << endl; // if (maPhase.nelementsValid() != 0) { // logStream_p << LogIO::DEBUG2 << "maPhase contains at least 1 element" << LogIO::POST; // // logStream_p << LogIO::DEBUG2 << "phase = " << phase << LogIO::POST; // // CANNOT BE DONE myout << "phase(phase !=0.0f) = " << phase(phase!=0.0f); // // CANNOT BE DONE myout << "maPhase = " << maPhase << endl; // phminmax(0)=min(abs(phase(phase!=0.0f))); // myout << "phminmax(0) = " << phminmax(0) << endl; // phminmax(1)=max(phase); // myout << "phminmax(1) = " << phminmax(1) << endl; // } else { // phminmax(0) = 0.0f; // phminmax(1) = 0.0f; // logStream_p << LogIO::NORMAL1 << "All selected data has phase = 0.0" << LogIO::POST; // myout << "All selected data has phase = 0.0" << endl; // } // if (!is_float) {ranges_p.define(dataColSel(1),phminmax);} //logStream_p << LogIO::DEBUG2 << "Setting the weight min and max." << LogIO::POST; Vector wtminmax(2); wtminmax(0)=min(abs(weight)); wtminmax(1)=max(abs(weight)); if(wtminmax(0) == wtminmax(1)) myout << "WEIGHT: " << wtminmax[0] << endl; ranges_p.define("weight",wtminmax); //logStream_p << LogIO::DEBUG2 << "Setting the uvw min and max." << LogIO::POST; Vector uvwminmax(2); uvwminmax(0)=min(abs(uvw)); uvwminmax(1)=max(abs(uvw)); //if(uvwminmax(0) == uvwminmax(1)) // { myout << "All selected data has UVW = " << uvwminmax(0) << endl; } ranges_p.define("uvw",uvwminmax); // Records currently only support output to stdio, not to LogIO! //myout << "Printing out the Record ranges_p:" << endl // << ranges_p << endl; //logStream_p << LogIO::DEBUG2 << "Setting flags for output:" << LogIO::POST; // TURN THIS FLAG SETTING INTO A NEW FUNCTION // Make flags for showing index columns. // Test to see if more than one value is present // in the data column. // If the array consists of only 1 value, ranges_p will have only 1 // element, and the boolean value will be set to false! if (ranges_p.asArrayInt(RecordFieldId("field_id")).nelements() > 1) { doFld_p = True; } else { doFld_p = False; //logStream_p << LogIO::NORMAL << "All selected data has FIELD = " // << fieldid(0) << LogIO::POST; myout << "FIELD: " << fieldid[0] << endl; } // doSpW_p = (ranges_p.asArrayInt(RecordFieldId("data_desc_id")).nelements() > 1); if (ranges_p.asArrayInt(RecordFieldId("data_desc_id")).nelements() > 1) { doSpW_p = True; } else { doSpW_p = False; //logStream_p << LogIO::NORMAL << "All selected data has SPW = " // << datadescid(0) << LogIO::POST; myout << "SPW: " << datadescid[0] << endl; } if (multiChan_p) { doChn_p = True; // Output a CHANNEL column } else { doChn_p = False; //logStream_p << LogIO::NORMAL << "All selected data has CHANNEL = " // << chanList_p(0,1) << LogIO::POST; myout << "CHANNEL: " << chanList_p(0, 1) << endl; } // logStream_p << LogIO::DEBUG2 << "Fld id : " << ranges_p.get("field_id") << endl // << "SpW id : " << ranges_p.get("spectral_window_id") << LogIO::POST; // From this point on, don't change scaling in list arrays, since the // field sizes are determined directly from the data. // logStream_p << LogIO::DEBUG1 // << "(Min, max) uvdist: " << uvminmax[0] << ", " << uvminmax[1] << endl // << "(Min, max) uvw: " << uvwminmax[0] << ", " << uvwminmax[1] << endl // << "(Min, max) amp: " << amplminmax[0] << ", " << amplminmax[1] << endl // << "(Min, max) phase: " << phminmax[0] << ", " << phminmax[1] << endl // << "(Min, max) weight: " << wtminmax[0] << ", " << wtminmax[1] // << LogIO::POST; // Set order of magnitude and precision (for field width setting): // If prec*_p < 0, then enforce >=0 (detect minimum decimal places to show is NYI) // If prec*_p > 0, then increment o*_p to provide space for decimal oTime_p = 2; // this is space for 2 :'s in time if ( precTime_p < 0 ) precTime_p = 7; // hh:mm:ss.s if ( precTime_p > 0 ) oTime_p++; // add space for decimal oUVDist_p = maxDigitsToPrint(uvdist); // order if ( precUVDist_p < 0 ) precUVDist_p = 0; if ( precUVDist_p > 0 ) oUVDist_p++; // add space for decimal oUVW_p = maxDigitsToPrint(uvw); // order if ( precUVW_p < 0 ) precUVW_p = 2; if ( precUVW_p > 0 ) oUVW_p++; // add space for decimal oUVW_p++; // add space for sign oAmpl_p = maxDigitsToPrint(ampl); if ( precAmpl_p < 0 ) precAmpl_p = 3; // mJy if ( precAmpl_p > 0 ) oAmpl_p++; // add space for decimal if (!is_float){ oPhase_p = maxDigitsToPrint(phase); if(min(phase) < 0) { oPhase_p+=3; } // add space for sign and column border else { oPhase_p++; } // add space for column border //oPhase_p = 3; // 100s of degs if ( precPhase_p < 0 ) precPhase_p = 1; if ( precPhase_p > 0 ) oPhase_p+=2; // add space for decimal } oWeight_p = maxDigitsToPrint(weight); // order if ( precWeight_p < 0 ) precWeight_p = 0; if ( precWeight_p > 0 ) oWeight_p++; // add space for decimal // Set field widths. // Use function to set width of baseline column: Ant1-Ant2 wAnt1_p = columnWidth(antNames1); wAnt2_p = columnWidth(antNames2); wFlag_p = 2; // the flag is always 1 character if (doFld_p) wFld_p = maxDigitsToPrint(fieldid); if (doSpW_p) wSpW_p = maxDigitsToPrint(spwinid); if (doChn_p) wChn_p = 3; // The field width for non-index columns is given by the // sum of the order and precision: wTime_p = oTime_p + precTime_p; wUVDist_p = oUVDist_p + precUVDist_p; wUVW_p = oUVW_p + precUVW_p; wAmpl_p = oAmpl_p + precAmpl_p; if (!is_float) {wPhase_p = oPhase_p + precPhase_p;} wWeight_p = oWeight_p + precWeight_p; // Enforce minimum field widths, // add leading space so columns nicely separate, // and accumulate wTotal_p: wTotal_p = 0; // initialize // wAnt_p = max(wAnt_p, (uInt)2); wAnt1_p++; // add leading space to separate from previous column wIntrf_p = wAnt1_p+1+wAnt2_p; wTotal_p+=wIntrf_p; if (doFld_p) { wFld_p = max(wFld_p,(uInt)3); wFld_p++; wTotal_p+=wFld_p; } if (doSpW_p) { wSpW_p = max(wSpW_p,(uInt)3); wSpW_p++; wTotal_p+=wSpW_p;} if (doChn_p) { wChn_p = max(wChn_p,(uInt)4); wChn_p++; wTotal_p+=wChn_p;} wTime_p = max(wTime_p, (uInt)12); wUVDist_p = max(wUVDist_p,(uInt)6); wUVDist_p++; wTotal_p+=wUVDist_p; wAmpl_p = max(wAmpl_p, (uInt)4); wAmpl_p++; if (!is_float) {wPhase_p = max(wPhase_p, (uInt)4); } wWeight_p = max(wWeight_p,(uInt)3); wWeight_p++; if (!is_float) {wVis_p = wAmpl_p+wPhase_p+wWeight_p+wFlag_p;} else {wVis_p = wAmpl_p+wWeight_p+wFlag_p;} wTotal_p+=wTime_p+nIndexPols_p*wVis_p+1; wUVW_p = max(wUVW_p, (uInt)9); wUVW_p++; wTotal_p+=3*wUVW_p; // Make column-ated header rule according to total and field widths // replicate does not work if the first parameter is "-", but it does for '-'. // Bug report here: https://bugs.aoc.nrao.edu/browse/CAS-511 String hSeparator=replicate('-',wTotal_p+1); //myout << "wTotal_p=" << wTotal_p << endl; //myout << "hSeparator.length=" << hSeparator.size() << endl; uInt colPos=0; colPos+=wTime_p; hSeparator[colPos]='|'; colPos+=wIntrf_p; hSeparator[colPos]='|'; colPos+=wUVDist_p; hSeparator[colPos]='|'; if (doFld_p) {colPos+=wFld_p;hSeparator[colPos]='|';} if (doSpW_p) {colPos+=wSpW_p;hSeparator[colPos]='|';} if (doChn_p) {colPos+=wChn_p;hSeparator[colPos]='|';} colPos++; for (uInt ipol=0; ipol antNames) { // Determine column width for a Vector logStream_p << LogIO::DEBUG1 << "Begin: MSLister::columnWidth" << LogIO::POST; Int antNamesShape = antNames.size(); uInt maxWidth=0; for (Int i = 0; i < antNamesShape; i++) { if (maxWidth < antNames(i).length()) maxWidth = antNames(i).length(); } return maxWidth; } void MSLister::_polarizationSetup(const uInt selPolID) { // Setup the class polarization information. // pols_p holds the polarization names, in the same order as the main // table data. /* logStream_p << LogIO::DEBUG1 << "Begin: MSLister::polarizationSetup" << LogIO::POST; MSPolarizationColumns msPolC(pMS->polarization()); npols_p = msPolC.corrType()(0).nelements(); pols_p.resize(npols_p,False); for (uInt i=0; ipolarization()); Array pols = polCols.corrType()(selPolID); npols_p = pols.size(); pols_p.resize(npols_p); for (uInt i=0; i indexPols_p holds the indices of pols_p that // match the correlation selection. nIndexPols_p holds the // number of elements in indexPols_p. void MSLister::polarizationParse(String correlation) { logStream_p << LogIO::DEBUG1 << "Begin: MSLister::polarizationParse" << LogIO::POST; Regex alpha("[A-Za-z]"); // Any letter if(correlation.empty() || !(correlation.contains(alpha))) { // If correlation is empty (no correlation selection) select all // polarizations by default. Fill indexPols_p with indices 0 // through (npols_p - 1). logStream_p << LogIO::NORMAL1 << "No correlation selection; selecting all by default." << LogIO::POST; nIndexPols_p = npols_p; indexPols_p.resize(nIndexPols_p); for(uInt i=0; i parseCorrs. nParseCorrs holds the number of // elements in parseCorrs. Vector parseCorrs; Int nParseCorrs=0; // Use Regex to do the parsing. Regex startRegex("^[^A-Za-z]"); // leading non-letter Regex cRegex("^[A-Za-z]{1,2}"); // 1 polarization selection // strip all leading whitespace logStream_p << LogIO::DEBUG2 << correlation << LogIO::POST; while(correlation.contains(startRegex)) { correlation.del(startRegex); } logStream_p << LogIO::DEBUG2 << correlation << LogIO::POST; // Acquire 1 polarization selection while(correlation.contains(cRegex)) { nParseCorrs = parseCorrs.size(); // get size of parseCorrs parseCorrs.resize(++nParseCorrs,True); // append one element to parseCorrs // Store polarization in parseCorrs parseCorrs(nParseCorrs - 1) = correlation.through(cRegex); correlation.del(cRegex); // remove polarization logStream_p << LogIO::DEBUG2 << correlation << LogIO::POST; // strip all leading whitespace while(correlation.contains(startRegex)) { correlation.del(startRegex); } logStream_p << LogIO::DEBUG2 << correlation << LogIO::POST; } logStream_p << LogIO::NORMAL2 << "Correlation selections identified:" << endl << parseCorrs << endl << "Number of polarization selections = " << nParseCorrs << LogIO::POST; // Query the number of elements of indexPols_p; store in nIndexPols_p. nIndexPols_p = nParseCorrs; indexPols_p.resize(nIndexPols_p); // Verify that each polarization in parseCorrs actually exists // in this data set. for(Int i=0; i #include //#include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class MeasurementSet; // List visibility records from a Measurement Set // // // // // // //
      • MSSelector //
      • MSSummary // // // // The name comes from being a Lister for a MS. // // // // MSs containing (u,v) data consist of amplitudes and phases for each // baseline and sample time, typically sorted in TB order. These // visibilities sometimes need to be examined one record at a time in // a text-based format, giving the user access to their raw data. // This class provides that access in a choice of several formats. // // // // // // Define an MS // MeasurementSet myMS(fileName); // // Define an output stream // LogIO myLog; // // Construct the Lister object // MSLister myList(myMS,myLog); // // List all data // myList.list(); // // // Send the next output to a new location // LogIO newLog; // setNewOS(newLog); // // List all the data, with default output options // myList.list(); // // List only selected data, with specified output options // datacolumn = 'corrected'; spw = '3:5~10'; timerange = '<13:34:25.1'; // scan = '5'; pagerows = 10; listfile = 'myList.list.out'; // // ... define any other parameters, then call function ... // myList.list(options, datacolumn, field, spw, antenna, timerange, // correlation, scan, feed, array, uvrange, average, // showflags, msselect, pagerows, listfile); // // An MSLister object is constructed from a MS // object, and then logged to the supplied LogIO object. // A new LogIO object is defined for a more restricted // listing. // // // // Note that if the MS goes out of scope, this class will // retrieve rubbish (probably giving runtime errors) as it just // maintains a pointer to the image. // // // // The viewing of the raw data is a basic capability that is // commonly required. // // // //
      • Several of the input parameters to MSLister::list are not // funcational presently. //
      • The (pointer to the) MS is declared and used as non-const throughout // MSLister, because MSSelector requires it. MSSelector should be // changed to require a const MS since it claims not to change the MS // anyway. Then the pointer/MS should be made const here too. //
      • Add more sanity checks. //
      • Actually do something with the nDecimal_p number. //
      • There are more formatting options planned. // class MSLister { public: // Null constructor MSLister(); // Construct from a MeasurementSet (set pointer), set formatting string, // and initialise listing with os. // os is currently not used as the primary steam for log messages. // This should be corrected, or os removed completely from the class. // MSLister (const MeasurementSet& ms, LogIO& os); // Copy constructor, this will initialise the MSLister's MS with other's MS MSLister (MSLister& other); // Assignment, this will initialise the MSLister's MS with other's MS MSLister& operator=(MSLister& other); // Destructor ~MSLister(); // Change or set the OS this MSLister uses. Do this before setMS() // if doing both. This method avoids having to reconstruct the MSLister // object if you change your mind about the output destination. // os is currently not used as the primary steam for log messages. // This should be corrected, or os removed completely from the class. // Bool setNewOS (LogIO& os); // Change or set the MS this MSLister refers to, and reinitialise the // MSLister object. Do this after setNewOS() if doing both. Bool setMS (MeasurementSet& ms); // Page size for various formats, output devices (default for landscape // printing). void setPage (const uInt width=120, const uInt height=20); // Format for output, ie data display precision. void setFormat (const uInt ndec=2); // User choices for list precision (sensible defaults): // (time precision for user interface is fraction of sec) void setPrecision ( const Int precTime=1, const Int precUVDist=0, const Int precAmpl=3, const int precPhase=1, const Int precWeight=0 ); // List the visibilities, with optional data selection and output // specification. void list (const String& options="", const String& datacolumn="", const String& field="", const String& spw="", const String& antenna="", const String& timerange="", const String& correlation="", const String& scan="", const String& feed="", const String& array="", const String& observation="", const String& uvrange="", const String& average="", const bool showflags=False, const String& msSelect="", const long pagerows=50, const String& listfile=""); // Set uv-data selection via MSSelection void selectvis(const String& timerange="", const String& spw="", const String& scan="", const String& field="", const String& baseline="", const String& uvrange="", const String& chanmode="none", const Int& nchan=1, const Int& start=0, const Int& step=1, const MRadialVelocity& mStart=MRadialVelocity(), const MRadialVelocity& mStep=MRadialVelocity(), const String& correlation="", const String& array="", const String& observation="", const String& msSelect=""); private: // Initialise the listing. initList() does things that need to be done // once per MS: declares and initialises the private MSSelector object, // and gets all the attribute ranges up front. void initList(); // A preamble of abbreviated MSSummary information. void listHeader(); // Get the ranges of a fixed set of MS key attributes. void getRanges(const MeasurementSet &ms); // Most of the heavy lifting is in here. Get the data records and list // them. void listData(const int pageRows=50, const String listfile=""); // Column header line for pagination of output. void listColumnHeader(ostream& myout); // Setup class polarization information for specified MS. // pols_p holds the polarization names contained in the MS // in the same order that the polarization data are listed in the // main table. void _polarizationSetup(const uInt selPolID); // Parse the correlation parameter value; fill indexPols_p to output // selected polarizations. If correlation is empty, all polarizations // are selected. void polarizationParse(String correlation); // Calculate column width for a Vector Int columnWidth(const Vector antNames); // Pointer to the MS MeasurementSet* pMS_p; MeasurementSet* pMSSel_p; // Output stream LogIO logStream_p; // A formatting string for convenience const String dashline_p; // The MSSelector object used in list() etc. MSSelector mss_p; // List of channels Matrix chanList_p; // True if listing multiple channels. Bool multiChan_p; // Pol counters uInt npols_p; // SpW/Pol info from subtables Vector pols_p; Vector freqs_p; // SpWId map from DDIs: Vector spwins_p; // True if listing multiple spws Bool multiSpw_p; // Polarization indexing variables; for polarization (correlation) selection. Vector indexPols_p; uInt nIndexPols_p; // Field width variables uInt wTime_p, wAnt1_p, wAnt2_p, wIntrf_p, wUVDist_p, wUVW_p; uInt wFld_p, wSpW_p, wChn_p; uInt wAmpl_p, wPhase_p, wWeight_p, wVis_p, wFlag_p; uInt wTotal_p; // Order of magnitude control (digits to left of decimal, including sign) uInt oTime_p, oUVDist_p, oUVW_p; uInt oAmpl_p, oPhase_p; uInt oWeight_p; // Precision control (digits to right of decimal point) // (precTime_p includes hhmmss, so 7 yields hh:mm:ss.s) Int precTime_p, precUVDist_p, precUVW_p; Int precAmpl_p, precPhase_p; Int precWeight_p; // Page params Int pageWidth_p, pageHeight_p, nDecimal_p; String date_p, lastdate_p; // for assigning desired columns from the ms Vector items_p; // Bools for column showing and to identify FLOAT_DATA column of single dish Bool doFld_p, doSpW_p, doChn_p, is_float; // Data selections // data --> "amplitude", "phase" // corrected --> "corrected_amplitude", "corrected_phase" // model --> "model_amplitude", "model_phase" // residual --> "residual_amplitude", "residual_phase" Vector dataColSel; // The Record object containing the MSSelector ranges Record ranges_p; // The conversion of the above to a regular Record object Record dataRecords_p; // Clear the formatting flags void clearFlags(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSOper/MSMetaData.cc000066400000000000000000005645661476623553700173650ustar00rootroot00000000000000//# MSMetaData.cc //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define _ORIGIN "MSMetaData::" + String(__func__) + ": " namespace casacore { MSMetaData::MSMetaData(const MeasurementSet *const &ms, const Float maxCacheSizeMB) : _ms(ms), _showProgress(False), _cacheMB(0), _maxCacheMB(maxCacheSizeMB), _nACRows(0), _nXCRows(0), _nStates(0), _nSpw(0), _nFields(0), _nAntennas(0), _nObservations(0), _nScans(0), _nArrays(0), _nrows(0), _nPol(0), _nDataDescIDs(0), _scanToSpwsMap(), _scanToDDIDsMap(), _dataDescIDToSpwMap(), _dataDescIDToPolIDMap(), _fieldToSpwMap(), _scanToStatesMap(), _scanToFieldsMap(), _fieldToStatesMap(), _stateToFieldsMap(), _sourceToFieldsMap(), _antennaNameToIDMap(), _intentToFieldIDMap(), _intentToScansMap(), _uniqueIntents(), _uniqueFieldIDs(), _uniqueStateIDs(), _avgSpw(), _tdmSpw(), _fdmSpw(), _wvrSpw(), _sqldSpw(), _subScanToNACRowsMap(), _subScanToNXCRowsMap(), _fieldToNACRowsMap(), _fieldToNXCRowsMap(), _scanToIntentsMap(), _stateToIntentsMap(), _spwToIntentsMap(), _spwInfo(0), _spwToFieldIDsMap(), _obsToArraysMap(), _spwToScansMap(), _fieldToScansMap(), _fieldNames(0), _antennaNames(0), _observatoryNames(0), _scanToTimesMap(), _fieldToTimesMap(), _observatoryPositions(0), _antennaOffsets(0), _uniqueBaselines(0, 0), _exposureTime(0), _nUnflaggedACRows(0), _nUnflaggedXCRows(0), _unflaggedFieldNACRows(), _unflaggedFieldNXCRows(), _unflaggedSubScanNACRows(), _unflaggedSubScanNXCRows(), _taqlTableName( File(ms->tableName()).exists() ? ms->tableName() : "$1" ), _taqlTempTable( File(ms->tableName()).exists() ? 0 : 1, ms ), _spwInfoStored(False), _forceSubScanPropsToCache(False), _sourceTimes() {} MSMetaData::~MSMetaData() {} uInt MSMetaData::nStates() const { if (_nStates == 0) { _nStates = _ms->state().nrow(); // Allow an empty STATE table. if (_nStates == 0) { _nStates = 1; } } return _nStates; } std::set MSMetaData::getIntents() const { if (! _uniqueIntents.empty()) { return _uniqueIntents; } vector > statesToIntentsMap; std::set uniqueIntents; _getStateToIntentsMap( statesToIntentsMap, uniqueIntents ); return uniqueIntents; } void MSMetaData::_getStateToIntentsMap( vector >& stateToIntentsMap, std::set& uniqueIntents ) const { if ( ! _uniqueIntents.empty() && ! _stateToIntentsMap.empty() ) { uniqueIntents = _uniqueIntents; stateToIntentsMap = _stateToIntentsMap; return; } uniqueIntents.clear(); String intentsColName = MSState::columnName(MSStateEnums::OBS_MODE); ScalarColumn intentsCol(_ms->state(), intentsColName); Vector intentSets = intentsCol.getColumn(); // Allow an empty STATE table. if (intentSets.empty()) { intentSets.reference (Vector(1, String())); } stateToIntentsMap.resize(nStates()); Vector::const_iterator end = intentSets.end(); vector >::iterator sIter = stateToIntentsMap.begin(); for( Vector::const_iterator curIntentSet=intentSets.begin(); curIntentSet!=end; ++curIntentSet, ++sIter ) { Vector intents = stringToVector(*curIntentSet, ','); *sIter = std::set (intents.begin(), intents.end()); uniqueIntents.insert(intents.begin(), intents.end()); } std::set::const_iterator lastIntent = uniqueIntents.end(); uInt mysize = 0; vector >::const_iterator lastState = stateToIntentsMap.end(); uInt count = 0; for ( vector >::const_iterator iter=stateToIntentsMap.begin(); iter!=lastState; ++iter, ++count ) { std::set::const_iterator lastIntent=iter->end(); for ( std::set::const_iterator intent=iter->begin(); intent!=lastIntent; ++intent ) { mysize += intent->size(); } } for ( std::set::const_iterator intent=uniqueIntents.begin(); intent!=lastIntent; ++intent ) { mysize += intent->size(); } if (_cacheUpdated(mysize)) { _uniqueIntents = uniqueIntents; _stateToIntentsMap = stateToIntentsMap; } } vector > MSMetaData::getProperMotions() const { // this method is responsible for setting _properMotions if (! _properMotions.empty()) { return _properMotions; } String colName = MSSource::columnName(MSSource::PROPER_MOTION); ArrayQuantColumn col(_ms->source(), colName); rownr_t nrow = _ms->source().nrow(); vector > myvec(nrow); Vector av(2); for (rownr_t i=0; i MSMetaData::getScanNumbers(Int obsID, Int arrayID) const { ArrayKey arrayKey; arrayKey.obsID = obsID; arrayKey.arrayID = arrayID; return _getScanNumbers(arrayKey); } uInt MSMetaData::nScans() { if (_nScans == 0) { _nScans = getScanKeys().size(); } return _nScans; } uInt MSMetaData::nObservations() const { if (_nObservations == 0) { _nObservations = _ms->observation().nrow(); } return _nObservations; } uInt MSMetaData::nArrays() { if (_nArrays == 0) { // because the ARRAY table apparently is optional _nArrays = max(*_getArrayIDs()) + 1; } return _nArrays; } rownr_t MSMetaData::nRows() const { return _ms->nrow(); } rownr_t MSMetaData::nRows(CorrelationType cType) { if (cType == BOTH) { return nRows(); } rownr_t nACRows, nXCRows; std::shared_ptr > subScanToNACRowsMap, subScanToNXCRowsMap; std::shared_ptr > fieldToNACRowsMap, fieldToNXCRowsMap; _getRowStats( nACRows, nXCRows, subScanToNACRowsMap, subScanToNXCRowsMap, fieldToNACRowsMap, fieldToNXCRowsMap ); if (cType == AUTO) { return nACRows; } else { return nXCRows; } } std::shared_ptr > MSMetaData::getNRowMap(CorrelationType cType) const { rownr_t nACRows, nXCRows; std::shared_ptr > subScanToNACRowsMap, subScanToNXCRowsMap; std::shared_ptr > fieldToNACRowsMap, fieldToNXCRowsMap; _getRowStats( nACRows, nXCRows, subScanToNACRowsMap, subScanToNXCRowsMap, fieldToNACRowsMap, fieldToNXCRowsMap ); if (cType == AUTO) { return subScanToNACRowsMap; } else if (cType == CROSS) { return subScanToNXCRowsMap; } std::shared_ptr > mymap( new map() ); map::const_iterator iter = subScanToNACRowsMap->begin(); map::const_iterator end = subScanToNACRowsMap->end(); for (; iter!=end; ++iter) { SubScanKey key = iter->first; (*mymap)[key] = iter->second + (*subScanToNXCRowsMap)[key]; } return mymap; } rownr_t MSMetaData::nRows( CorrelationType cType, Int arrayID, Int observationID, Int scanNumber, Int fieldID ) const { SubScanKey subScanKey; subScanKey.obsID = observationID; subScanKey.arrayID = arrayID; subScanKey.scan = scanNumber; subScanKey.fieldID = fieldID; _checkSubScan(subScanKey); rownr_t nACRows, nXCRows; std::shared_ptr > subScanToNACRowsMap, subScanToNXCRowsMap; std::shared_ptr > fieldToNACRowsMap, fieldToNXCRowsMap; _getRowStats( nACRows, nXCRows, subScanToNACRowsMap, subScanToNXCRowsMap, fieldToNACRowsMap, fieldToNXCRowsMap ); if (cType == AUTO) { return (*subScanToNACRowsMap)[subScanKey]; } else if (cType == CROSS) { return (*subScanToNXCRowsMap)[subScanKey]; } else { return (*subScanToNACRowsMap)[subScanKey] + (*subScanToNXCRowsMap)[subScanKey]; } } rownr_t MSMetaData::nRows(CorrelationType cType, uInt fieldID) const { _checkField(fieldID); rownr_t nACRows, nXCRows; std::shared_ptr > subScanToNACRowsMap, subScanToNXCRowsMap; std::shared_ptr > fieldToNACRowsMap, fieldToNXCRowsMap; _getRowStats( nACRows, nXCRows, subScanToNACRowsMap, subScanToNXCRowsMap, fieldToNACRowsMap, fieldToNXCRowsMap ); if (cType == AUTO) { return (*fieldToNACRowsMap)[fieldID]; } else if (cType == CROSS) { return (*fieldToNXCRowsMap)[fieldID]; } else { return (*fieldToNACRowsMap)[fieldID] + (*fieldToNXCRowsMap)[fieldID]; } } Double MSMetaData::nUnflaggedRows() const { Double nACRows, nXCRows; std::shared_ptr > subScanToNACRowsMap, subScanToNXCRowsMap; std::shared_ptr > fieldToNACRowsMap, fieldToNXCRowsMap; _getUnflaggedRowStats( nACRows, nXCRows, subScanToNACRowsMap, subScanToNXCRowsMap, fieldToNACRowsMap, fieldToNXCRowsMap ); return nACRows + nXCRows; } Double MSMetaData::nUnflaggedRows(CorrelationType cType) const { if (cType == BOTH) { return nUnflaggedRows(); } Double nACRows, nXCRows; std::shared_ptr > subScanToNACRowsMap, subScanToNXCRowsMap; std::shared_ptr > fieldToNACRowsMap, fieldToNXCRowsMap; _getUnflaggedRowStats( nACRows, nXCRows, subScanToNACRowsMap, subScanToNXCRowsMap, fieldToNACRowsMap, fieldToNXCRowsMap ); if (cType == AUTO) { return nACRows; } else { return nXCRows; } } Double MSMetaData::nUnflaggedRows( CorrelationType cType, Int arrayID, uInt observationID, Int scanNumber, uInt fieldID ) const { SubScanKey subScanKey; subScanKey.obsID = observationID; subScanKey.arrayID = arrayID; subScanKey.scan = scanNumber; subScanKey.fieldID = fieldID; _checkSubScan(subScanKey); Double nACRows, nXCRows; std::shared_ptr > subScanToNACRowsMap, subScanToNXCRowsMap; std::shared_ptr > fieldToNACRowsMap, fieldToNXCRowsMap; _getUnflaggedRowStats( nACRows, nXCRows, subScanToNACRowsMap, subScanToNXCRowsMap, fieldToNACRowsMap, fieldToNXCRowsMap ); if (cType == AUTO) { return (*subScanToNACRowsMap)[subScanKey]; } else if (cType == CROSS) { return (*subScanToNXCRowsMap)[subScanKey]; } else { return (*subScanToNACRowsMap)[subScanKey] + (*subScanToNXCRowsMap)[subScanKey]; } } Double MSMetaData::nUnflaggedRows(CorrelationType cType, Int fieldID) const { Double nACRows, nXCRows; std::shared_ptr > subScanToNACRowsMap, subScanToNXCRowsMap; std::shared_ptr > fieldToNACRowsMap, fieldToNXCRowsMap; _getUnflaggedRowStats( nACRows, nXCRows, subScanToNACRowsMap, subScanToNXCRowsMap, fieldToNACRowsMap, fieldToNXCRowsMap ); if (cType == AUTO) { return (*fieldToNACRowsMap)[fieldID]; } else if (cType == CROSS) { return (*fieldToNXCRowsMap)[fieldID]; } else { return (*fieldToNACRowsMap)[fieldID] + (*fieldToNXCRowsMap)[fieldID]; } } void MSMetaData::_getRowStats( rownr_t& nACRows, rownr_t& nXCRows, std::map*& subScanToNACRowsMap, std::map*& subScanToNXCRowsMap, map*& fieldToNACRowsMap, map*& fieldToNXCRowsMap ) const { nACRows = 0; nXCRows = 0; subScanToNACRowsMap = new std::map(); subScanToNXCRowsMap = new std::map(); fieldToNACRowsMap = new map(); fieldToNXCRowsMap = new map(); std::set subScanKeys = _getSubScanKeys(); std::set::const_iterator subIter = subScanKeys.begin(); std::set::const_iterator subEnd = subScanKeys.end(); for (; subIter != subEnd; ++subIter) { // initialize subscan map (*subScanToNACRowsMap)[*subIter] = 0; (*subScanToNXCRowsMap)[*subIter] = 0; } std::set fieldIDs = getUniqueFieldIDs(); std::set::const_iterator fIter = fieldIDs.begin(); std::set::const_iterator fEnd = fieldIDs.end(); for (; fIter!=fEnd; ++fIter) { // initialize field maps (*fieldToNACRowsMap)[*fIter] = 0; (*fieldToNXCRowsMap)[*fIter] = 0; } if (_subScanProperties) { map::const_iterator iter = _subScanProperties->begin(); map::const_iterator end = _subScanProperties->end(); for (; iter!=end; ++iter) { const SubScanKey& subscan = iter->first; const SubScanProperties& props = iter->second; const Int& fieldID = subscan.fieldID; nACRows += props.acRows; (*subScanToNACRowsMap)[subscan] += props.acRows; (*subScanToNXCRowsMap)[subscan] += props.xcRows; (*fieldToNACRowsMap)[fieldID] += props.acRows; (*fieldToNXCRowsMap)[fieldID] += props.xcRows; } // doing this single computation is more effecient than summing // the xcRows in the loop nXCRows = nACRows - nRows(); } else { std::shared_ptr > ant1, ant2; _getAntennas(ant1, ant2); std::shared_ptr > scans = _getScans(); std::shared_ptr > fieldIDs = _getFieldIDs(); std::shared_ptr > obsIDs = _getObservationIDs(); std::shared_ptr > arrIDs = _getArrayIDs(); Vector::const_iterator aEnd = ant1->end(); Vector::const_iterator a1Iter = ant1->begin(); Vector::const_iterator a2Iter = ant2->begin(); Vector::const_iterator sIter = scans->begin(); Vector::const_iterator fIter = fieldIDs->begin(); Vector::const_iterator oIter = obsIDs->begin(); Vector::const_iterator arIter = arrIDs->begin(); SubScanKey subScanKey; for ( ; a1Iter!=aEnd; ++a1Iter, ++a2Iter, ++sIter, ++fIter, ++arIter, ++oIter ) { subScanKey.obsID = *oIter; subScanKey.arrayID = *arIter; subScanKey.scan = *sIter; subScanKey.fieldID = *fIter; if (*a1Iter == *a2Iter) { ++nACRows; ++(*subScanToNACRowsMap)[subScanKey]; ++(*fieldToNACRowsMap)[*fIter]; } else { ++nXCRows; ++(*subScanToNXCRowsMap)[subScanKey]; ++(*fieldToNXCRowsMap)[*fIter]; } } } } void MSMetaData::_getRowStats( rownr_t& nACRows, rownr_t& nXCRows, std::shared_ptr >& scanToNACRowsMap, std::shared_ptr >& scanToNXCRowsMap, std::shared_ptr >& fieldToNACRowsMap, std::shared_ptr >& fieldToNXCRowsMap ) const { // this method is responsible for setting _nACRows, _nXCRows, _subScanToNACRowsMap, // _subScanToNXCRowsMap, _fieldToNACRowsMap, _fieldToNXCRowsMap if (_nACRows > 0 || _nXCRows > 0) { nACRows = _nACRows; nXCRows = _nXCRows; scanToNACRowsMap = _subScanToNACRowsMap; scanToNXCRowsMap = _subScanToNXCRowsMap; fieldToNACRowsMap = _fieldToNACRowsMap; fieldToNXCRowsMap = _fieldToNXCRowsMap; return; } std::map *myScanToNACRowsMap, *myScanToNXCRowsMap; map *myFieldToNACRowsMap, *myFieldToNXCRowsMap; _getRowStats( nACRows, nXCRows, myScanToNACRowsMap, myScanToNXCRowsMap, myFieldToNACRowsMap, myFieldToNXCRowsMap ); scanToNACRowsMap.reset(myScanToNACRowsMap); scanToNXCRowsMap.reset(myScanToNXCRowsMap); fieldToNACRowsMap.reset(myFieldToNACRowsMap); fieldToNXCRowsMap.reset(myFieldToNXCRowsMap); uInt size = 2*( sizeof(uInt) + _sizeof(*scanToNACRowsMap) + _sizeof(*fieldToNACRowsMap) ); if (_cacheUpdated(size)) { _nACRows = nACRows; _nXCRows = nXCRows; _subScanToNACRowsMap = scanToNACRowsMap; _subScanToNXCRowsMap = scanToNXCRowsMap; _fieldToNACRowsMap = fieldToNACRowsMap; _fieldToNXCRowsMap = fieldToNXCRowsMap; } } void MSMetaData::_getAntennas( std::shared_ptr >& ant1, std::shared_ptr >& ant2 ) const { ant1 = _getMainScalarColumn(MSMainEnums::ANTENNA1); ant2 = _getMainScalarColumn(MSMainEnums::ANTENNA2); } std::shared_ptr > MSMetaData::_getScans() const { return _getMainScalarColumn( MSMainEnums::SCAN_NUMBER ); } std::shared_ptr > MSMetaData::_getObservationIDs() const { return _getMainScalarColumn( MSMainEnums::OBSERVATION_ID ); } std::shared_ptr > MSMetaData::_getArrayIDs() const { return _getMainScalarColumn( MSMainEnums::ARRAY_ID ); } std::shared_ptr > MSMetaData::_getFieldIDs() const { return _getMainScalarColumn( MSMainEnums::FIELD_ID ); } std::shared_ptr > MSMetaData::_getStateIDs() const { std::shared_ptr > states = _getMainScalarColumn( MSMainEnums::STATE_ID ); Int maxState = max(*states); Int nstates = (Int)nStates(); ThrowIf( maxState >= nstates, "MS only has " + String::toString(nstates) + " rows in its STATE table, but references STATE_ID " + String::toString(maxState) + " in its main table." ); return states; } std::shared_ptr > MSMetaData::_getDataDescIDs() const { return _getMainScalarColumn( MSMainEnums::DATA_DESC_ID ); } std::set MSMetaData::getScansForState( Int stateID, Int obsID, Int arrayID ) const { if (! _hasStateID(stateID)) { return std::set(); } std::map > myScanToStatesMap = getScanToStatesMap(); ArrayKey arrayKey; arrayKey.obsID = obsID; arrayKey.arrayID = arrayID; std::set scanKeys = getScanKeys(arrayKey); std::set::const_iterator iter = scanKeys.begin(); std::set::const_iterator end = scanKeys.end(); std::set stateIDs, scansForState; while (iter != end) { stateIDs = myScanToStatesMap[*iter]; if (stateIDs.find(stateID) != stateIDs.end()) { scansForState.insert(iter->scan); } ++iter; } return scansForState; } std::map > MSMetaData::_getScanToAntennasMap() const { // this method is responsible for setting _scanToAntennasMap if (! _scanToAntennasMap.empty()) { return _scanToAntennasMap; } std::map > myScanToAntsMap; map subScanProps = *getSubScanProperties(); map::const_iterator iter = subScanProps.begin(); map::const_iterator end = subScanProps.end(); while (iter != end) { SubScanKey subKey = iter->first; SubScanProperties subProps = iter->second; myScanToAntsMap[scanKey(subKey)].insert(subProps.antennas.begin(), subProps.antennas.end()); ++iter; } std::map >::const_iterator end1 = myScanToAntsMap.end(); uInt mySize = sizeof(ScanKey)*myScanToAntsMap.size(); for ( std::map >::const_iterator iter=myScanToAntsMap.begin(); iter!=end1; ++iter ) { mySize += sizeof(Int)*iter->second.size(); } if (_cacheUpdated(mySize)) { _scanToAntennasMap = myScanToAntsMap; } return myScanToAntsMap; } std::map > MSMetaData::getScanToStatesMap() const { if (! _scanToStatesMap.empty()) { return _scanToStatesMap; } std::map > myScanToStatesMap; if (nStates() == 0) { std::set empty; std::set scanKeys = getScanKeys(); //std::set subScanKeys; //_getSubScanKeys(subScanKeys, scanKeys); std::set::const_iterator end = scanKeys.end(); for ( std::set::const_iterator scanKey=scanKeys.begin(); scanKey!=end; ++scanKey ) { myScanToStatesMap[*scanKey] = empty; } } else { map subScanProps = *getSubScanProperties(); //map scanProps; //map arrayProps; //_getSubScanProperties(subScanProps, scanProps, arrayProps); map::const_iterator iter = subScanProps.begin(); map::const_iterator end = subScanProps.end(); //ScanKey key; while (iter != end) { SubScanKey subKey = iter->first; //key.obsID = subKey.obsID; //key.arrayID = subKey.arrayID; //key.scan = subKey.scan; SubScanProperties subProps = iter->second; myScanToStatesMap[scanKey(subKey)].insert(subProps.stateIDs.begin(), subProps.stateIDs.end()); ++iter; } } std::map >::const_iterator end = myScanToStatesMap.end(); uInt mySize = sizeof(ScanKey)*myScanToStatesMap.size(); for ( std::map >::const_iterator iter=myScanToStatesMap.begin(); iter!=end; ++iter ) { mySize += sizeof(Int)*iter->second.size(); } if (_cacheUpdated(mySize)) { _scanToStatesMap = myScanToStatesMap; } return myScanToStatesMap; } void MSMetaData::_getSubScansAndIntentsMaps( std::shared_ptr > >& subScanToIntentsMap, map >& intentToSubScansMap ) const { // This method is responsible for setting _subScanToIntentsMap and _intentToSubScansMap if (_subScanToIntentsMap && ! _intentToSubScansMap.empty()) { subScanToIntentsMap = _subScanToIntentsMap; intentToSubScansMap = _intentToSubScansMap; return; } std::shared_ptr > > ssToIntents( new map >() ); intentToSubScansMap.clear(); if (_ms->state().nrow() == 0) { // because the decision was made to support MSes with non-existent STATE tables std::set sskeys = _getSubScanKeys(); std::set::const_iterator ssiter = sskeys.begin(); std::set::const_iterator ssend = sskeys.end(); for (; ssiter!=ssend; ++ssiter) { (*ssToIntents)[*ssiter] = std::set(); } } else { vector > stateToIntentsMap; std::set uniqueIntents; _getStateToIntentsMap(stateToIntentsMap, uniqueIntents); map props = *getSubScanProperties(); map::const_iterator iter = props.begin(); map::const_iterator end = props.end(); for (; iter!=end; ++iter) { SubScanKey sskey = iter->first; std::set stateIDs = iter->second.stateIDs; std::set::const_iterator siter = stateIDs.begin(); std::set::const_iterator send = stateIDs.end(); for (; siter!=send; ++siter) { std::set intents = stateToIntentsMap[*siter]; (*ssToIntents)[sskey].insert(intents.begin(), intents.end()); std::set::const_iterator initer = intents.begin(); std::set::const_iterator inend = intents.end(); for (; initer!=inend; ++initer) { intentToSubScansMap[*initer].insert(sskey); } } } } subScanToIntentsMap = ssToIntents; if (_cacheUpdated(_sizeof(*subScanToIntentsMap) + _sizeof(intentToSubScansMap))) { _subScanToIntentsMap = subScanToIntentsMap; _intentToSubScansMap = intentToSubScansMap; } } void MSMetaData::_getScansAndIntentsMaps( std::map >& scanToIntentsMap, std::map >& intentToScansMap ) const { // This method is responsible for setting _scanToIntentsMap and _intentToScansMap if (! _scanToIntentsMap.empty() && ! _intentToScansMap.empty()) { scanToIntentsMap = _scanToIntentsMap; intentToScansMap = _intentToScansMap; return; } vector > stateToIntentsMap; std::set uniqueIntents; _getStateToIntentsMap( stateToIntentsMap, uniqueIntents ); std::map > scanToStatesMap = getScanToStatesMap(); std::map >::const_iterator end = scanToStatesMap.end(); std::set states; std::set intents; for ( std::map >::const_iterator iter=scanToStatesMap.begin(); iter!=end; ++iter ) { ScanKey scan = iter->first; states = iter->second; std::set::const_iterator endState = states.end(); for ( std::set::const_iterator myState=states.begin(); myState!=endState; ++myState ) { if (*myState < 0) { continue; } intents = stateToIntentsMap[*myState]; scanToIntentsMap[scan].insert(intents.begin(), intents.end()); std::set::const_iterator endIntent = intents.end(); for ( std::set::const_iterator myIntent=intents.begin(); myIntent!=endIntent; ++myIntent ) { intentToScansMap[*myIntent].insert(scan); } } } if (_cacheUpdated(_sizeof(scanToIntentsMap) + _sizeof(intentToScansMap))) { _scanToIntentsMap = scanToIntentsMap; _intentToScansMap = intentToScansMap; } } uInt MSMetaData::_sizeof( const std::map & m ) { uInt sizeInt = sizeof(Int); uInt size = m.size()*(sizeof(Double) + sizeInt); std::map::const_iterator iter = m.begin(); std::map::const_iterator end = m.end(); uInt n = 0; while (iter != end) { n += iter->second.ddIDs.size(); ++iter; } return size + n*sizeInt; } template uInt MSMetaData::_sizeof(const std::map >& m) { uInt size = sizeof(T) * m.size(); typename std::map >::const_iterator iter = m.begin(); typename std::map >::const_iterator end = m.end(); while (iter != end) { std::set::const_iterator end2 = iter->second.end(); for ( std::set::const_iterator iter2=iter->second.begin(); iter2!=end2; ++iter2 ) { size += iter2->size(); } ++iter; } return size; } template uInt MSMetaData::_sizeof(const std::map >& m) { uInt size = sizeof(T)*m.size(); typename std::map >::const_iterator iter = m.begin(); typename std::map >::const_iterator end = m.end(); uInt nElements = 0; while (iter != end) { nElements += iter->second.size(); ++iter; } size += sizeof(U)*nElements; return size; } template uInt MSMetaData::_sizeof(const std::map& m) { return m.size()*(sizeof(T) + sizeof(U)); } uInt MSMetaData::_sizeof(const vector >& m) { uInt size = sizeof(Int) * m.size(); vector >::const_iterator end = m.end(); for ( vector >::const_iterator iter=m.begin(); iter!=end; ++iter ) { std::set::const_iterator end2 = iter->end(); for ( std::set::const_iterator iter2=iter->begin(); iter2!=end2; ++iter2 ) { size += iter2->size(); } } return size; } uInt MSMetaData::_sizeof(const vector& m) { vector::const_iterator end = m.end(); uInt size = 0; for ( vector::const_iterator iter=m.begin(); iter!=end; ++iter ) { size += iter->length(); } return size; } template uInt MSMetaData::_sizeof(const vector& v) { return v.size()*sizeof(T); } uInt MSMetaData::_sizeof(const vector >& v) { vector >::const_iterator iter = v.begin(); vector >::const_iterator end = v.end(); uInt size = 0; while (iter != end) { size += _sizeof(*iter); ++iter; } return size; } uInt MSMetaData::_sizeof(const Quantum >& m) { return (sizeof(Double))*m.getValue().size() + m.getUnit().size(); } template uInt MSMetaData::_sizeof(const std::map >& m) { uInt setssize = 0; uInt size = 0; typename std::map >::const_iterator end = m.end(); for ( typename std::map >::const_iterator iter=m.begin(); iter!=end; ++iter ) { size += iter->first.size(); setssize += iter->second.size(); } size += sizeof(T) * setssize; return size; } uInt MSMetaData::_sizeof(const vector >& m) { uInt size = 0; vector >::const_iterator end = m.end(); uInt intsize = sizeof(Int); uInt qsize = 20; for ( vector >::const_iterator iter = m.begin(); iter!=end; ++iter ) { size += iter->size()*(2*intsize + qsize); } return size; } std::set MSMetaData::getIntentsForScan(const ScanKey& scan) const { _checkScan(scan); std::map > scanToIntentsMap; std::map > intentToScansMap; _getScansAndIntentsMaps( scanToIntentsMap, intentToScansMap ); return scanToIntentsMap[scan]; } std::set MSMetaData::getIntentsForSubScan( const SubScanKey& subScan ) const { _checkSubScan(subScan); std::shared_ptr > > subScanToIntentsMap; std::map > intentToSubScansMap; _getSubScansAndIntentsMaps( subScanToIntentsMap, intentToSubScansMap ); return subScanToIntentsMap->find(subScan)->second; } std::shared_ptr > > MSMetaData::getSubScanToIntentsMap() const { std::shared_ptr > > subScanToIntentsMap; std::map > intentToSubScansMap; _getSubScansAndIntentsMaps( subScanToIntentsMap, intentToSubScansMap ); return subScanToIntentsMap; } Bool MSMetaData::_cacheUpdated(const Float incrementInBytes) const { Float newSize = _cacheMB + incrementInBytes/1e6; if (newSize <= _maxCacheMB) { _cacheMB = newSize; return True; } return False; } std::set MSMetaData::getSpwsForIntent(const String& intent) { if (! _hasIntent(intent)) { return std::set(); } vector > spwToIntentsMap = _getSpwToIntentsMap(); std::set spws; for (uInt i=0; i > MSMetaData::getSpwToDataDescriptionIDMap() const { // TODO perhaps cache the result, but atm doesn't seem worth doing std::map, uInt> spwPolToDDID = getSpwIDPolIDToDataDescIDMap(); std::vector > mymap(nSpw(True)); std::map, uInt>::const_iterator iter = spwPolToDDID.begin(); std::map, uInt>::const_iterator end = spwPolToDDID.end(); while (iter != end) { mymap[iter->first.first].insert(iter->second); ++iter; } return mymap; } uInt MSMetaData::nSpw(Bool includewvr) const { if (_nSpw > 0) { return includewvr ? _nSpw : _nSpw - getWVRSpw().size(); } uInt nSpw = _ms->spectralWindow().nrow(); _nSpw = nSpw; return includewvr ? nSpw : nSpw - getWVRSpw().size(); } uInt MSMetaData::nPol() { if (_nPol == 0) { _nPol = _ms->polarization().nrow(); } return _nPol; } std::set MSMetaData::getIntentsForSpw(const uInt spw) { if (spw >= nSpw(True)) { throw AipsError( _ORIGIN + "spectral window out of range" ); } return _getSpwToIntentsMap()[spw]; } std::set MSMetaData::getIntentsForField(Int fieldID) { if (! _hasFieldID(fieldID)) { return std::set(); } vector> fieldToIntentsMap; std::map> intentToFieldsMap; _getFieldsAndIntentsMaps(fieldToIntentsMap, intentToFieldsMap); return fieldToIntentsMap[fieldID]; } std::set MSMetaData::getIntentsForField(String field) { vector> fieldToIntentsMap; std::map> intentToFieldsMap; _getFieldsAndIntentsMaps(fieldToIntentsMap, intentToFieldsMap); const auto fieldNames = getFieldNames(); std::set intents; uInt i = 0; for (const auto& name: fieldNames) { if (name == field) { const auto myIntents = fieldToIntentsMap[i]; intents.insert(myIntents.begin(), myIntents.end()); } ++i; } return intents; } uInt MSMetaData::nFields() const { if (_nFields > 0) { return _nFields; } uInt nFields = _ms->field().nrow(); _nFields = nFields; return nFields; } std::shared_ptr > MSMetaData::_getEphemFieldIDs() const { // responsible for setting _ephemFields if (_ephemFields) { return _ephemFields; } MSFieldColumns msfc(_ms->field()); ScalarColumn ephemCol = msfc.ephemerisId(); _ephemFields = std::make_shared>(); if (ephemCol.isNull()) { return _ephemFields; } Vector colData = ephemCol.getColumn(); Vector::const_iterator iter = colData.begin(); Vector::const_iterator end = colData.end(); uInt i = 0; for (; iter!=end; ++iter, ++i) { if (*iter >= 0) { _ephemFields->insert(i); } } return _ephemFields; } MDirection MSMetaData::phaseDirFromFieldIDAndTime(const uInt fieldID, const MEpoch& ep) const { _hasFieldID(fieldID); MSFieldColumns msfc(_ms->field()); if(! msfc.needInterTime(fieldID)) { return msfc.phaseDirMeas(fieldID, 0.0); } MEpoch::Types msType = MEpoch::castType(msfc.timeMeas()(fieldID).getRef().getType()); Unit sec("s"); Double inSeconds= MEpoch::Convert(ep, msType)().get(sec).getValue(); return msfc.phaseDirMeas(fieldID, inSeconds); } MDirection MSMetaData::getReferenceDirection( const uInt fieldID, const MEpoch& ep ) const { _hasFieldID(fieldID); MSFieldColumns msfc(_ms->field()); if(! msfc.needInterTime(fieldID)) { return msfc.referenceDirMeas(fieldID, 0.0); } MEpoch::Types msType = MEpoch::castType(msfc.timeMeas()(fieldID).getRef().getType()); Unit sec("s"); Double inSeconds = MEpoch::Convert(ep, msType)().get(sec).getValue(); return msfc.referenceDirMeas(fieldID, inSeconds); } void MSMetaData::_getFieldsAndSpwMaps( std::map >& fieldToSpwMap, vector >& spwToFieldMap ) const { // This method has the responsibility of setting _fieldToSpwMap and _spwToFieldIDMap if (! _fieldToSpwMap.empty() && ! _spwToFieldIDsMap.empty()) { fieldToSpwMap = _fieldToSpwMap; spwToFieldMap = _spwToFieldIDsMap; return; } fieldToSpwMap.clear(); spwToFieldMap.resize(nSpw(True)); std::shared_ptr > scanProps; std::shared_ptr > subScanProps; _getScanAndSubScanProperties(scanProps, subScanProps, _showProgress); std::map::const_iterator iter = subScanProps->begin(); std::map::const_iterator end = subScanProps->end(); for (; iter!=end; ++iter) { Int fieldID = iter->first.fieldID; const std::set& spws = iter->second.spws; fieldToSpwMap[fieldID].insert(spws.begin(), spws.end()); std::set::const_iterator spwIter = spws.begin(); std::set::const_iterator spwEnd = spws.end(); for (; spwIter!=spwEnd; ++spwIter) { spwToFieldMap[*spwIter].insert(fieldID); } } if (_cacheUpdated(_sizeof(fieldToSpwMap) + _sizeof(spwToFieldMap))) { _fieldToSpwMap = fieldToSpwMap; _spwToFieldIDsMap = spwToFieldMap; } } std::map > MSMetaData::getFieldsToSpwsMap() const { std::map > myFieldToSpwMap; vector > mySpwToFieldMap; _getFieldsAndSpwMaps(myFieldToSpwMap, mySpwToFieldMap); return myFieldToSpwMap; } std::set MSMetaData::getSpwsForField(Int fieldID) const { if (! _hasFieldID(fieldID)) { return std::set(); } return getFieldsToSpwsMap()[fieldID]; } std::set MSMetaData::getSpwsForField(const String& fieldName) { uInt myNFields = nFields(); vector fieldNames = getFieldNames(); std::set spws; for (uInt i=0; i myspws = getSpwsForField(i); spws.insert(myspws.begin(), myspws.end()); } } ThrowIf( spws.empty(), _ORIGIN + "field (" + fieldName + " does not exist." ); return spws; } vector MSMetaData::getFieldNames() const { if (! _fieldNames.empty()) { return _fieldNames; } String fieldNameColName = MSField::columnName(MSFieldEnums::NAME); ScalarColumn nameCol(_ms->field(), fieldNameColName); vector fieldNames = nameCol.getColumn().tovector(); uInt mysize = 0; vector::const_iterator end = fieldNames.end(); for ( vector::const_iterator name=fieldNames.begin(); name!=end; ++name ) { mysize += name->size(); } if (_cacheUpdated(mysize)) { _fieldNames = fieldNames; } return fieldNames; } vector MSMetaData::getFieldCodes() const { // this method is responsible for setting _fieldCodes if (! _fieldCodes.empty()) { return _fieldCodes; } String fieldCodeColName = MSField::columnName(MSFieldEnums::CODE); ScalarColumn codeCol(_ms->field(), fieldCodeColName); vector fieldCodes = codeCol.getColumn().tovector(); if (_cacheUpdated(_sizeof(fieldCodes))) { _fieldCodes = fieldCodes; } return fieldCodes; } std::set MSMetaData::getFieldIDsForSpw(const uInt spw) { uInt myNSpw = nSpw(True); if (spw >= myNSpw) { throw AipsError(_ORIGIN + "spectral window out of range"); } std::map > myFieldToSpwMap; vector > mySpwToFieldMap; _getFieldsAndSpwMaps(myFieldToSpwMap, mySpwToFieldMap); return mySpwToFieldMap[spw]; } std::set MSMetaData::getFieldNamesForSpw(const uInt spw) { std::set fieldIDs = getFieldIDsForSpw(spw); std::set fieldNames; vector allFieldNames = getFieldNames(); for ( std::set::const_iterator fieldID = fieldIDs.begin(); fieldID!=fieldIDs.end(); ++fieldID ) { fieldNames.insert(allFieldNames[*fieldID]); } return fieldNames; } std::shared_ptr > MSMetaData::_generateScanPropsIfWanted() const { if (_scanProperties) { // we already have it, just return it return _scanProperties; } if (_forceSubScanPropsToCache) { // it hasn't been generated yet, but we will likely // need it later, so just generate it now std::shared_ptr > scanProps; std::shared_ptr > subScanProps; _getScanAndSubScanProperties(scanProps, subScanProps, False); return scanProps; } // we don't have it, and we aren't going to want it later return std::shared_ptr >(); } std::shared_ptr > MSMetaData::_generateSubScanPropsIfWanted() const { if (_subScanProperties) { // we already have it, just return it return _subScanProperties; } if (_forceSubScanPropsToCache) { // it hasn't been generated yet, but we will likely // need it later, so just generate it now std::shared_ptr > scanProps; std::shared_ptr > subScanProps; _getScanAndSubScanProperties(scanProps, subScanProps, False); return subScanProps; } // we don't have it, and we aren't going to want it later return std::shared_ptr >(); } std::set MSMetaData::getScanKeys() const { if (! _scanKeys.empty()) { return _scanKeys; } std::set scanKeys; std::shared_ptr > scanProps = _generateScanPropsIfWanted(); if (scanProps) { map::const_iterator iter = scanProps->begin(); map::const_iterator end = scanProps->end(); for (; iter!=end; ++iter) { scanKeys.insert(iter->first); } } else { // fastest way if we don't have _scanProperties and aren't going to need it later std::set subScanKeys = _getSubScanKeys(); std::set::const_iterator iter = subScanKeys.begin(); std::set::const_iterator end = subScanKeys.end(); for (; iter!=end; ++iter) { scanKeys.insert(scanKey(*iter)); } } if (_cacheUpdated(sizeof(ScanKey)*scanKeys.size())) { _scanKeys = scanKeys; } return scanKeys; } std::set MSMetaData::getScanKeys(const ArrayKey& arrayKey) const { std::set allScanKeys = getScanKeys(); Bool doAllObsIDs = arrayKey.obsID < 0; Bool doAllArrayIDs = arrayKey.arrayID < 0; if (doAllObsIDs && doAllArrayIDs) { return allScanKeys; } std::set scanKeys; std::set::const_iterator iter = allScanKeys.begin(); std::set::const_iterator end = allScanKeys.end(); while (iter != end) { if ( (doAllObsIDs || iter->obsID == arrayKey.obsID) && (doAllArrayIDs || iter->arrayID == arrayKey.arrayID) ) { scanKeys.insert(*iter); } ++iter; } return scanKeys; } std::set MSMetaData::_getScanKeys( const std::set& scanKeys, const ArrayKey& arrayKey ) const { Int obsID = arrayKey.obsID; Int arrayID = arrayKey.arrayID; std::set::const_iterator iter = scanKeys.begin(); std::set::const_iterator end = scanKeys.end(); std::set filteredKeys; while (iter != end) { if (iter->obsID == obsID && iter->arrayID == arrayID) { filteredKeys.insert(*iter); } } return filteredKeys; } std::set MSMetaData::_getScanNumbers(const ArrayKey& arrayKey) const { return scanNumbers(getScanKeys(arrayKey)); } void MSMetaData::_getScansAndDDIDMaps( std::map >& scanToDDIDMap, vector >& ddIDToScanMap ) const { // this method is responsible for setting _scanToDDIDsMap and _ddidToScansMap if (! _scanToDDIDsMap.empty()) { scanToDDIDMap = _scanToDDIDsMap; ddIDToScanMap = _ddidToScansMap; return; } scanToDDIDMap.clear(); ddIDToScanMap.clear(); ddIDToScanMap.resize(nDataDescriptions()); std::map subScanProps = *getSubScanProperties(); std::map::const_iterator iter = subScanProps.begin(); std::map::const_iterator end = subScanProps.end(); ScanKey myScanKey; std::set ddIDs; std::set::const_iterator dIter; std::set::const_iterator dEnd; while (iter != end) { myScanKey = scanKey(iter->first); ddIDs = iter->second.ddIDs; scanToDDIDMap[myScanKey].insert(ddIDs.begin(), ddIDs.end()); dIter = ddIDs.begin(); dEnd = ddIDs.end(); while (dIter != dEnd) { ddIDToScanMap[*dIter].insert(myScanKey); ++dIter; } ++iter; } if (_cacheUpdated(_sizeof(scanToDDIDMap)) + _sizeof(ddIDToScanMap)) { _scanToDDIDsMap = scanToDDIDMap; _ddidToScansMap = ddIDToScanMap; } } std::map > MSMetaData::getScanToSpwsMap() const { std::map > scanToSpwMap; vector > spwToScanMap; _getScansAndSpwMaps(scanToSpwMap, spwToScanMap); return scanToSpwMap; } vector > MSMetaData::getSpwToScansMap() const { std::map > scanToSpwMap; vector > spwToScanMap; _getScansAndSpwMaps(scanToSpwMap, spwToScanMap); return spwToScanMap; } void MSMetaData::_getScansAndSpwMaps( std::map >& scanToSpwMap, vector >& spwToScanMap ) const { // This method is responsible for setting _scanToSpwsMap and _spwToScansMap if (! _scanToSpwsMap.empty() && ! _spwToScansMap.empty()) { scanToSpwMap = _scanToSpwsMap; spwToScanMap = _spwToScansMap; return; } scanToSpwMap.clear(); spwToScanMap.clear(); spwToScanMap.resize(nSpw(True)); std::shared_ptr > subScanProps = _generateSubScanPropsIfWanted(); if (subScanProps) { map::const_iterator iter = subScanProps->begin(); map::const_iterator end = subScanProps->end(); for (; iter!=end; ++iter) { ScanKey sk = scanKey(iter->first); std::set spws = iter->second.spws; scanToSpwMap[sk].insert(spws.begin(), spws.end()); std::set::const_iterator spwIter = spws.begin(); std::set::const_iterator spwEnd = spws.end(); for (; spwIter!=spwEnd; ++spwIter) { spwToScanMap[*spwIter].insert(sk); } } } else { // fastest way to generate what we want if we don't have _subScanProperties std::map > scanToDDIDMap; vector > ddIDToScanMap; _getScansAndDDIDMaps(scanToDDIDMap, ddIDToScanMap); vector ddToSpw = getDataDescIDToSpwMap(); std::map >::const_iterator iter = scanToDDIDMap.begin(); std::map >::const_iterator end = scanToDDIDMap.end(); std::set::const_iterator dIter; std::set::const_iterator dEnd; for (; iter!=end; ++iter) { ScanKey scanKey = iter->first; std::set ddids = scanToDDIDMap[scanKey]; dIter = ddids.begin(); dEnd = ddids.end(); for (; dIter!=dEnd; ++dIter) { uInt spw = ddToSpw[*dIter]; scanToSpwMap[scanKey].insert(spw); spwToScanMap[spw].insert(scanKey); } } } if (_cacheUpdated(_sizeof(scanToSpwMap)) + _sizeof(spwToScanMap)) { _scanToSpwsMap = scanToSpwMap; _spwToScansMap = spwToScanMap; } } template uInt MSMetaData::_sizeof(const vector >& v) { uInt size = 0; typename vector >::const_iterator iter = v.begin(); typename vector >::const_iterator end = v.end(); while (iter != end) { size = iter->size(); ++iter; } size *= sizeof(T); return size; } std::set MSMetaData::getAntennasForScan(const ScanKey& scan) const { _checkScan(scan); return _getScanToAntennasMap()[scan]; } std::set MSMetaData::getSpwsForScan(const ScanKey& scan) const { _checkScan(scan); std::map > scanToSpwMap; vector > spwToScanMap; _getScansAndSpwMaps( scanToSpwMap, spwToScanMap ); return scanToSpwMap[scan]; } std::set MSMetaData::getSpwsForSubScan(const SubScanKey& subScan) const { return getSubScanProperties(subScan).spws; } std::set MSMetaData::getScansForSpw( const uInt spw, Int obsID, Int arrayID ) const { uInt myNSpw = nSpw(True); ThrowIf(spw >= myNSpw, "spectral window out of range"); ArrayKey arrayKey; arrayKey.obsID = obsID; arrayKey.arrayID = arrayID; std::set myScanKeys = getScanKeys(arrayKey); std::map > scanToSpwMap; vector > spwToScanMap; _getScansAndSpwMaps(scanToSpwMap, spwToScanMap); std::set::const_iterator iter = myScanKeys.begin(); std::set::const_iterator end = myScanKeys.end(); std::set scanNumbers; std::set spws; while (iter != end) { spws = scanToSpwMap[*iter]; if (spws.find(spw) != spws.end()) { scanNumbers.insert(iter->scan); } ++iter; } return scanNumbers; } uInt MSMetaData::nAntennas() const { if (_nAntennas > 0) { return _nAntennas; } uInt nAnts = _ms->antenna().nrow(); _nAntennas = nAnts; return nAnts; } uInt MSMetaData::nDataDescriptions() const { if (_nDataDescIDs == 0) { _nDataDescIDs = _ms->dataDescription().nrow(); } return _nDataDescIDs; } vector MSMetaData::_getAntennaNames( std::map >& namesToIDsMap ) const { if (! _antennaNames.empty()) { namesToIDsMap = _antennaNameToIDMap; return _antennaNames; } namesToIDsMap.clear(); std::map mymap; String antNameColName = MSAntenna::columnName(MSAntennaEnums::NAME); ScalarColumn nameCol(_ms->antenna(), antNameColName); Vector names = nameCol.getColumn(); Vector::const_iterator end = names.end(); uInt i = 0; for ( Vector::const_iterator name=names.begin(); name!=end; ++name, ++i ) { namesToIDsMap[*name].insert(i); } uInt mysize = names.size()*sizeof(uInt); for ( Vector::const_iterator name=names.begin(); name!=end; ++name ) { mysize += 2*name->size(); } if (_cacheUpdated(mysize)) { _antennaNames = names.tovector(); _antennaNameToIDMap = namesToIDsMap; } return names.tovector(); } vector MSMetaData::getAntennaNames( std::map& namesToIDsMap, const vector& antennaIDs ) const { namesToIDsMap.clear(); std::map > allMap; vector names = getAntennaNames(allMap, antennaIDs); std::map >::const_iterator iter = allMap.begin(); std::map >::const_iterator end = allMap.end(); for (; iter!=end; ++iter) { namesToIDsMap[iter->first] = *iter->second.rbegin(); } return names; } vector MSMetaData::getAntennaNames( std::map >& namesToIDsMap, const vector& antennaIDs ) const { uInt nAnts = nAntennas(); std::map > allMap; vector allNames = _getAntennaNames(allMap); if (antennaIDs.empty()) { namesToIDsMap = allMap; return allNames; } uInt mymax = max(Vector(antennaIDs)); ThrowIf( mymax >= nAnts, "Antenna ID " + String::toString(mymax) + " out of range." ); vector names; vector::const_iterator end = antennaIDs.end(); for ( vector::const_iterator id=antennaIDs.begin(); id!=end; ++id ) { String antName = allNames[*id]; names.push_back(antName); namesToIDsMap[antName].insert(*id); } return names; } uInt MSMetaData::getAntennaID(const String& antennaName) const { return *getAntennaIDs(antennaName).rbegin(); } std::set MSMetaData::getAntennaIDs( const String& antennaName ) const { return getAntennaIDs(vector(1, antennaName))[0]; } vector > MSMetaData::getAntennaIDs( const vector& antennaNames ) const { std::map > namesToIDsMap; vector names = getAntennaNames(namesToIDsMap); vector::const_iterator end = antennaNames.end(); std::map >::const_iterator mapEnd = namesToIDsMap.end(); vector > ids; for ( vector::const_iterator name=antennaNames.begin(); name!=end; ++name ) { std::map >::const_iterator pair = namesToIDsMap.find(*name); ThrowIf( pair == mapEnd, _ORIGIN + "Unknown antenna " + *name ); ids.push_back(pair->second); } return ids; } map MSMetaData::getScanToFirstExposureTimeMap( Bool showProgress ) const { std::shared_ptr > scanProps = _getScanProperties(showProgress); std::map::const_iterator iter = scanProps->begin(); std::map::const_iterator end = scanProps->end(); map ret; for (; iter!=end; ++iter) { ret[iter->first] = iter->second.firstExposureTime; } return ret; } // deprecated, no longer supported vector > MSMetaData::getFirstExposureTimeMap() { if (! _firstExposureTimeMap.empty()) { return _firstExposureTimeMap; } uInt nDataDescIDs = nDataDescriptions(); std::shared_ptr > scans = _getScans(); std::shared_ptr > dataDescIDs = _getDataDescIDs(); std::shared_ptr > times = _getTimes(); std::shared_ptr > > exposureTimes = _getExposureTimes(); vector > firstExposureTimeMap(nDataDescIDs); vector > tmap(nDataDescIDs); Vector::const_iterator siter = scans->begin(); Vector::const_iterator send = scans->end(); Vector::const_iterator diter = dataDescIDs->begin(); Vector::const_iterator titer = times->begin(); Vector eTimes = exposureTimes->getValue(); String unit = exposureTimes->getUnit(); Vector::const_iterator eiter = eTimes.begin(); while (siter != send) { std::map mymap = firstExposureTimeMap[*diter]; if ( mymap.find(*siter) == mymap.end() || *titer < tmap[*diter][*siter] ) { firstExposureTimeMap[*diter][*siter] = Quantity(*eiter, unit); tmap[*diter][*siter] = *titer; } ++siter; ++diter; ++titer; ++eiter; } if (_cacheUpdated(_sizeof(firstExposureTimeMap))) { _firstExposureTimeMap = firstExposureTimeMap; } return firstExposureTimeMap; } vector MSMetaData::getAntennaStations(const vector& antennaIDs) { vector allStations = _getStationNames(); if (antennaIDs.empty()) { return allStations; } _hasAntennaID(max(Vector(antennaIDs))); vector myStationNames; vector::const_iterator end = antennaIDs.end(); for ( vector::const_iterator iter=antennaIDs.begin(); iter!=end; ++iter ) { myStationNames.push_back(allStations[*iter]); } return myStationNames; } vector > MSMetaData::getAntennaStations(const vector& antennaNames) { vector > ids = getAntennaIDs(antennaNames); vector >::const_iterator iter = ids.begin(); vector >::const_iterator end = ids.end(); vector > stations; for (; iter!=end; ++iter) { std::set::const_iterator siter = iter->begin(); std::set::const_iterator send = iter->end(); vector myStations; for (; siter!=send; ++siter) { myStations.push_back(getAntennaStations(vector(1, *siter))[0]); } stations.push_back(myStations); } return stations; } vector MSMetaData::_getStationNames() { if (! _stationNames.empty()) { return _stationNames; } String antStationColName = MSAntenna::columnName(MSAntennaEnums::STATION); vector stationNames = ScalarColumn( _ms->antenna(), antStationColName ).getColumn().tovector(); if (_cacheUpdated(_sizeof(stationNames))) { _stationNames = stationNames; } return stationNames; } std::set MSMetaData::_getSubScanKeys() const { // responsible for setting _subscans if (! _subscans.empty()) { return _subscans; } std::set mysubscans; if (_subScanProperties) { map::const_iterator iter = _subScanProperties->begin(); map::const_iterator end = _subScanProperties->end(); for (; iter!=end; ++iter) { mysubscans.insert(iter->first); } } else { std::shared_ptr > scans = _getScans(); std::shared_ptr > fields = _getFieldIDs(); std::shared_ptr > arrays = _getArrayIDs(); std::shared_ptr > obs = _getObservationIDs(); Vector::const_iterator scanIter = scans->begin(); Vector::const_iterator scanEnd = scans->end(); Vector::const_iterator fIter = fields->begin(); Vector::const_iterator oIter = obs->begin(); Vector::const_iterator aIter = arrays->begin(); SubScanKey subScanKey; for (; scanIter != scanEnd; ++scanIter, ++fIter, ++oIter, ++aIter) { subScanKey.obsID = *oIter; subScanKey.arrayID = *aIter; subScanKey.scan = *scanIter; subScanKey.fieldID = *fIter; mysubscans.insert(subScanKey); } } if (_cacheUpdated(mysubscans.size()*sizeof(SubScanKey))) { _subscans = mysubscans; } return mysubscans; } std::set MSMetaData::_getSubScanKeys(const ScanKey& scanKey) const { _checkScan(scanKey); return _getScanToSubScansMap()[scanKey]; } std::map > MSMetaData::_getScanToSubScansMap() const { // sets _scanToSubScans; if (! _scanToSubScans.empty()) { return _scanToSubScans; } std::set allSubScans = _getSubScanKeys(); std::set::const_iterator iter = allSubScans.begin(); std::set::const_iterator end = allSubScans.end(); std::map > mymap; ScanKey scanKey; while (iter != end) { scanKey.obsID = iter->obsID; scanKey.arrayID = iter->arrayID; scanKey.scan = iter->scan; mymap[scanKey].insert(*iter); ++iter; } if (_cacheUpdated(_sizeof(mymap))) { _scanToSubScans = mymap; } return mymap; } QVD MSMetaData::getAntennaDiameters() const { if (! _antennaDiameters.getValue().empty()) { return _antennaDiameters; } String antDiamColName = MSAntenna::columnName(MSAntennaEnums::DISH_DIAMETER); ScalarColumn diamCol(_ms->antenna(), antDiamColName); Vector diams = diamCol.getColumn(); String unit = *diamCol.keywordSet().asArrayString("QuantumUnits").begin(); QVD antennaDiameters = QVD(diams, unit); if (_cacheUpdated(_sizeof(antennaDiameters))) { _antennaDiameters = antennaDiameters; } return antennaDiameters; } std::set MSMetaData::getTDMSpw() { if (! _tdmSpw.empty()) { return _tdmSpw; } std::set avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw; _getSpwInfo(avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw); return tdmSpw; } vector MSMetaData::getBandWidths() const { std::set avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw; vector props = _getSpwInfo( avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw ); vector::const_iterator end = props.end(); vector out; for ( vector::const_iterator iter=props.begin(); iter!=end; ++iter ) { out.push_back(iter->bandwidth); } return out; } QVD MSMetaData::_freqWidthToVelWidth( const QVD& v, const Quantity& refFreq ) { QVD dv = v; dv.convert("Hz"); dv = dv/refFreq.getValue("Hz"); dv.scale(C::c/1000); dv.setUnit("km/s"); return dv; } vector MSMetaData::getChanEffectiveBWs(Bool asVelWidths) const { std::set avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw; vector props = _getSpwInfo( avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw ); vector::const_iterator end = props.end(); vector out; for ( vector::const_iterator iter=props.begin(); iter!=end; ++iter ) { if ( asVelWidths && iter->effbw.isConform("Hz") && iter->meanfreq.getValue() > 0 ) { out.push_back( _freqWidthToVelWidth(iter->effbw, iter->meanfreq) ); } else { out.push_back(iter->effbw); } } return out; } vector MSMetaData::getChanFreqs() const { std::set avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw; vector props = _getSpwInfo( avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw ); vector::const_iterator end = props.end(); vector out; for ( vector::const_iterator iter=props.begin(); iter!=end; ++iter ) { out.push_back(iter->chanfreqs); } return out; } vectorMSMetaData::getChanResolutions(Bool asVelWidths) const { std::set avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw; vector props = _getSpwInfo( avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw ); vector::const_iterator end = props.end(); vector out; for ( vector::const_iterator iter=props.begin(); iter!=end; ++iter ) { if ( asVelWidths && iter->resolution.isConform("Hz") && iter->meanfreq.getValue() > 0 ) { out.push_back( _freqWidthToVelWidth(iter->resolution, iter->meanfreq) ); } else { out.push_back(iter->resolution); } } return out; } vector MSMetaData::getChanWidths() const { std::set avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw; vector props = _getSpwInfo( avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw ); vector::const_iterator end = props.end(); vector out; for ( vector::const_iterator iter=props.begin(); iter!=end; ++iter ) { out.push_back(iter->chanwidths); } return out; } vector MSMetaData::getNetSidebands() const { std::set avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw; vector props = _getSpwInfo( avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw ); vector::const_iterator end = props.end(); vector out; for ( vector::const_iterator iter=props.begin(); iter!=end; ++iter ) { out.push_back(iter->netsideband); } return out; } vector MSMetaData::getMeanFreqs() const { std::set avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw; vector props = _getSpwInfo( avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw ); vector::const_iterator end = props.end(); vector out; for ( vector::const_iterator iter=props.begin(); iter!=end; ++iter ) { out.push_back(iter->meanfreq); } return out; } vector MSMetaData::getCenterFreqs() const { std::set avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw; vector props = _getSpwInfo( avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw ); vector::const_iterator end = props.end(); vector out; for ( vector::const_iterator iter=props.begin(); iter!=end; ++iter ) { out.push_back(iter->centerfreq); } return out; } vector MSMetaData::getRefFreqs() const { std::set avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw; vector props = _getSpwInfo( avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw ); vector::const_iterator end = props.end(); vector out; for ( vector::const_iterator iter=props.begin(); iter!=end; ++iter ) { out.push_back(iter->reffreq); } return out; } vector MSMetaData::nChans() const { std::set avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw; vector props = _getSpwInfo( avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw ); vector::const_iterator end = props.end(); vector out; for ( vector::const_iterator iter=props.begin(); iter!=end; ++iter ) { out.push_back(iter->nchans); } return out; } vector > MSMetaData::getEdgeChans() { std::set avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw; vector props = _getSpwInfo( avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw ); vector::const_iterator end = props.end(); vector > out; for ( vector::const_iterator iter=props.begin(); iter!=end; ++iter ) { out.push_back(iter->edgechans); } return out; } vector MSMetaData::getBBCNos() const { if (! hasBBCNo()) { throw AipsError("This MS's SPECTRAL_WINDOW table does not have a BBC_NO column"); } std::set avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw; vector props = _getSpwInfo( avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw ); vector::const_iterator end = props.end(); vector out; for ( vector::const_iterator iter=props.begin(); iter!=end; ++iter ) { out.push_back(iter->bbcno); } return out; } vector MSMetaData::getCorrBits() const { std::set avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw; vector props = _getSpwInfo( avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw ); vector out; for (const auto &element : props) { out.push_back(element.corrbit); } return out; } std::map > MSMetaData::getBBCNosToSpwMap( SQLDSwitch sqldSwitch ) { vector mymap = getBBCNos(); std::map > out; vector::const_iterator end = mymap.end(); std::set sqldSpw; if (sqldSwitch != SQLD_INCLUDE) { std::set avgSpw, tdmSpw, fdmSpw, wvrSpw; _getSpwInfo( avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw ); } uInt i = 0; Bool found = True; for ( vector::const_iterator iter=mymap.begin(); iter!=end; ++iter, ++i ) { if (out.find(*iter) == out.end()) { out[*iter] = std::set(); } if (sqldSwitch != SQLD_INCLUDE) { found = sqldSpw.find(i) != sqldSpw.end(); } if ( sqldSwitch == SQLD_INCLUDE || ( sqldSwitch == SQLD_EXCLUDE && ! found ) || ( sqldSwitch == SQLD_ONLY && found ) ) { out[*iter].insert(i); } } return out; } vector MSMetaData::getSpwNames() const { std::set avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw; vector props = _getSpwInfo( avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw ); vector::const_iterator end = props.end(); vector out; for ( vector::const_iterator iter=props.begin(); iter!=end; ++iter ) { out.push_back(iter->name); } return out; } vector MSMetaData::getSpwReceiverBands() const { std::set avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw; const auto props = _getSpwInfo( avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw ); vector out; for (const auto& spw: props) { out.push_back(spw.rb); } return out; } vector MSMetaData::getSpwSubwindows() const { std::set avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw; const auto props = _getSpwInfo( avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw ); vector out; for (const auto& spw: props) { out.push_back(spw.sw); } return out; } std::set MSMetaData::getSpwIDs() const { const Vector ddIDs = *_getDataDescIDs(); const vector& ddIDToSpw = getDataDescIDToSpwMap(); Vector::const_iterator iter = ddIDs.begin(); Vector::const_iterator end = ddIDs.end(); std::set spws; for ( ; iter!=end; ++iter) { if (*iter >= 0) { spws.insert(ddIDToSpw[*iter]); } } return spws; } std::set MSMetaData::getFDMSpw() { if (! _fdmSpw.empty()) { return _fdmSpw; } std::set avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw; _getSpwInfo(avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw); return fdmSpw; } std::set MSMetaData::getChannelAvgSpw() { if (! _avgSpw.empty()) { return _avgSpw; } std::set avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw; _getSpwInfo(avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw); return avgSpw; } std::set MSMetaData::getWVRSpw() const { if (_spwInfoStored) { return _wvrSpw; } std::set avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw; _getSpwInfo(avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw); return wvrSpw; } std::set MSMetaData::getSQLDSpw() { if (_spwInfoStored) { return _sqldSpw; } std::set avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw; _getSpwInfo(avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw); return sqldSpw; } std::set MSMetaData::getScansForTimes( Double center, Double tol, Int obsID, Int arrayID ) const { _checkTolerance(tol); ArrayKey arrayKey; arrayKey.obsID = obsID; arrayKey.arrayID = arrayID; std::set uniqueScans = getScanKeys(arrayKey); std::shared_ptr > > scanToTimesMap = _getScanToTimesMap(); Double minTime = center - tol; Double maxTime = center + tol; std::set scans; std::set::const_iterator scan = uniqueScans.begin(); std::set::const_iterator end = uniqueScans.end(); while (scan != end) { std::set times = scanToTimesMap->find(*scan)->second; // rbegin() points to the last element in a container. For a std::set, // the last element is the largest, and the first is the smallest. if (*times.rbegin() >= minTime && *times.begin() <= maxTime) { scans.insert(scan->scan); } ++scan; } return scans; } std::shared_ptr > > MSMetaData::_getScanToTimesMap() const { if (_scanToTimesMap && ! _scanToTimesMap->empty()) { return _scanToTimesMap; } std::shared_ptr > scans = _getScans(); std::shared_ptr > obsIDs = _getObservationIDs(); std::shared_ptr > arrayIDs = _getArrayIDs(); Vector::const_iterator curScan = scans->begin(); Vector::const_iterator lastScan = scans->end(); std::shared_ptr > times = _getTimes(); Vector::const_iterator curTime = times->begin(); Vector::const_iterator curObs = obsIDs->begin(); Vector::const_iterator curArray = arrayIDs->begin(); std::shared_ptr > > scanToTimesMap( new std::map >() ); ScanKey scanKey; while (curScan != lastScan) { scanKey.obsID = *curObs; scanKey.arrayID = *curArray; scanKey.scan = *curScan; (*scanToTimesMap)[scanKey].insert(*curTime); ++curScan; ++curTime; ++curObs; ++curArray; } if (_cacheUpdated(_sizeof(*scanToTimesMap))) { _scanToTimesMap = scanToTimesMap; } return scanToTimesMap; } template std::shared_ptr > MSMetaData::_getMainScalarColumn( MSMainEnums::PredefinedColumns col ) const { String name = MeasurementSet::columnName(col); ScalarColumn mycol(*_ms, name); std::shared_ptr > v(new Vector()); mycol.getColumn(*v); return v; } std::shared_ptr > MSMetaData::_getTimes() const { return _getMainScalarColumn(MSMainEnums::TIME); } std::shared_ptr > > MSMetaData::_getExposureTimes() const { ScalarQuantColumn col( *_ms, MeasurementSet::columnName(MSMainEnums::EXPOSURE) ); return col.getColumn(); } std::shared_ptr > MSMetaData::_getFlags() const { String flagColName = MeasurementSet::columnName(MSMainEnums::FLAG); return std::shared_ptr >( new ArrayColumn(*_ms, flagColName) ); } std::set MSMetaData::getTimesForScans( std::set scans ) const { std::set times; if (scans.empty()) { std::shared_ptr > allTimes = _getTimes(); times.insert(allTimes->begin(), allTimes->end()); return times; } std::shared_ptr > > scanToTimesMap = _getScanToTimesMap(); // std::set scanNumbers = getScanNumbers(); std::set::const_iterator scan = scans.begin(); std::set::const_iterator end = scans.end(); std::set scanKeys = getScanKeys(); while (scan != end) { _checkScan(*scan); times.insert( scanToTimesMap->find(*scan)->second.begin(), scanToTimesMap->find(*scan)->second.end() ); ++scan; } return times; } std::set MSMetaData::getTimesForScan(const ScanKey& scan) const { std::set scans; scans.insert(scan); // scan validity check is done in getTimesForScans() return getTimesForScans(scans); } std::map > MSMetaData::getSpwToTimesForScan( const ScanKey& scan ) const { _checkScan(scan); std::shared_ptr > scanProps; std::shared_ptr > subScanProps; _getScanAndSubScanProperties(scanProps, subScanProps, False); return scanProps->find(scan)->second.times; } std::pair MSMetaData::getTimeRangeForScan( const ScanKey& scanKey ) const { _checkScan(scanKey); std::shared_ptr > scanProps; std::shared_ptr > subScanProps; _getScanAndSubScanProperties(scanProps, subScanProps, False); return scanProps->find(scanKey)->second.timeRange; } std::shared_ptr > > MSMetaData::getScanToTimeRangeMap() const { std::shared_ptr > scanProps; std::shared_ptr > subScanProps; _getScanAndSubScanProperties( scanProps, subScanProps, False ); std::shared_ptr > > ret( new map >() ); map::const_iterator iter = scanProps->begin(); map::const_iterator end = scanProps->end(); for (;iter!=end; ++iter) { (*ret)[iter->first] = iter->second.timeRange; } return ret; } pair MSMetaData::getTimeRange(Bool showProgress) const { // can't just use TIME column because that does not take into account // the interval std::shared_ptr > scanProps; std::shared_ptr > subScanProps; _getScanAndSubScanProperties(scanProps, subScanProps, showProgress); map::const_iterator iter = scanProps->begin(); map::const_iterator end = scanProps->end(); pair timerange = iter->second.timeRange; ++iter; for (;iter!=end; ++iter) { const pair& range = iter->second.timeRange; timerange.first = min(timerange.first, range.first); timerange.second = max(timerange.second, range.second); } return timerange; } map MSMetaData::getAverageIntervalsForScan( const ScanKey& scan ) const { _checkScan(scan); std::shared_ptr > scanProps; std::shared_ptr > subScanProps; _getScanAndSubScanProperties(scanProps, subScanProps, False); map meanIntervals = scanProps->find(scan)->second.meanInterval; map ret; map::const_iterator iter = meanIntervals.begin(); map::const_iterator end = meanIntervals.end(); for (; iter!=end; ++iter) { ret[iter->first] = iter->second.getValue(); } return ret; } std::map MSMetaData::getAverageIntervalsForSubScan( const SubScanKey& subScan ) const { return getSubScanProperties(subScan).meanInterval; } std::map > MSMetaData::getIntentToFieldsMap() { vector > fieldToIntentsMap; std::map > intentToFieldsMap; _getFieldsAndIntentsMaps( fieldToIntentsMap, intentToFieldsMap ); return intentToFieldsMap; } std::map > MSMetaData::getIntentToScansMap() { std::map > scanToIntentsMap; std::map > intentToScansMap; _getScansAndIntentsMaps( scanToIntentsMap, intentToScansMap ); return intentToScansMap; } std::map > MSMetaData::getIntentToSpwsMap() { vector > spwToIntentsMap; std::map > intentToSpwsMap; _getSpwsAndIntentsMaps( spwToIntentsMap, intentToSpwsMap ); return intentToSpwsMap; } std::set MSMetaData::getScansForField( const String& field, Int obsID, Int arrayID ) const { std::set fieldIDs = getFieldIDsForField(field); std::set scans; for ( std::set::const_iterator fieldID=fieldIDs.begin(); fieldID!=fieldIDs.end(); ++fieldID ) { std::set myscans = getScansForFieldID(*fieldID, obsID, arrayID); scans.insert(myscans.begin(), myscans.end()); } return scans; } std::set MSMetaData::getScansForFieldID( const Int fieldID, Int obsID, Int arrayID ) const { if (! _hasFieldID(fieldID)) { return std::set(); } vector > fieldToScansMap; std::map > scanToFieldsMap; _getFieldsAndScansMaps( fieldToScansMap, scanToFieldsMap ); ArrayKey arrayKey; arrayKey.obsID = obsID; arrayKey.arrayID = arrayID; std::set scanKeys = getScanKeys(arrayKey); std::set::const_iterator iter = scanKeys.begin(); std::set::const_iterator end = scanKeys.end(); std::set scanNumbers; std::set fields; while (iter != end) { fields = scanToFieldsMap[*iter]; if (fields.find(fieldID) != fields.end()) { scanNumbers.insert(iter->scan); } ++iter; } return scanNumbers; } std::set MSMetaData::getFieldsForIntent(const String& intent) { if (! _hasIntent(intent)) { return std::set(); } vector > fieldToIntentsMap; std::map > intentToFieldsMap; _getFieldsAndIntentsMaps( fieldToIntentsMap, intentToFieldsMap ); return intentToFieldsMap[intent]; } std::set MSMetaData::getFieldsForScan(const ScanKey& scan) const { _checkScan(scan); vector > fieldToScansMap; std::map > scanToFieldsMap; _getFieldsAndScansMaps( fieldToScansMap, scanToFieldsMap ); return scanToFieldsMap[scan]; } std::set MSMetaData::getFieldsForScans( const std::set& scans, Int obsID, Int arrayID ) const { ArrayKey arrayKey; arrayKey.obsID = obsID; arrayKey.arrayID = arrayID; std::set myScanKeys = scanKeys(scans, arrayKey); return getFieldsForScans(myScanKeys); } std::set MSMetaData::getFieldsForScans( const std::set& scanKeys ) const { _checkScans(scanKeys); std::set::const_iterator iter = scanKeys.begin(); std::set::const_iterator end = scanKeys.end(); std::set fields; while (iter != end) { std::set myfields = getFieldsForScan(*iter); fields.insert(myfields.begin(), myfields.end()); ++iter; } return fields; } std::set MSMetaData::getFieldIDsForField( const String& field ) const { std::set fieldIDs; String name = field; vector fieldNames = getFieldNames(); uInt nNames = fieldNames.size(); name.upcase(); for (uInt i=0; i MSMetaData::getScansForIntent( const String& intent, Int obsID, Int arrayID ) const { std::set uniqueIntents = getIntents(); ThrowIf( uniqueIntents.find(intent) == uniqueIntents.end(), "Intent " + intent + " is not present in this dataset" ); std::map > scanToIntentsMap; std::map > intentToScansMap; _getScansAndIntentsMaps( scanToIntentsMap, intentToScansMap ); ArrayKey arrayKey; arrayKey.obsID = obsID; arrayKey.arrayID = arrayID; std::set scanKeys = getScanKeys(arrayKey); std::set::const_iterator iter = scanKeys.begin(); std::set::const_iterator end = scanKeys.end(); std::set foundScans = intentToScansMap[intent]; std::set::const_iterator foundEnd = foundScans.end(); std::set filteredScans; while (iter != end) { if (foundScans.find(*iter) != foundEnd) { filteredScans.insert(*iter); } ++iter; } return scanNumbers(filteredScans); } std::set MSMetaData::getStatesForScan( Int obsID, Int arrayID, Int scan ) const { ArrayKey arrayKey; arrayKey.obsID = obsID; arrayKey.arrayID = arrayID; std::set scanKeys = getScanKeys(arrayKey); std::map > scanToStates = getScanToStatesMap(); std::set states; std::set::const_iterator iter = scanKeys.begin(); std::set::const_iterator end = scanKeys.end(); while (iter != end) { if (iter->scan == scan) { std::set myStates = scanToStates[*iter]; states.insert(myStates.begin(), myStates.end()); } ++iter; } return states; } std::vector > MSMetaData::getTimesForSpws(Bool showProgress) const { std::shared_ptr > scanProps = this->_getScanProperties(showProgress); std::vector > myvec(nSpw(True)); std::map::const_iterator iter = scanProps->begin(); std::map::const_iterator end = scanProps->end(); for (; iter!=end; ++iter) { const std::map >& times = iter->second.times; std::map >::const_iterator titer = times.begin(); std::map >::const_iterator tend = times.end(); for (; titer!=tend; ++titer) { const std::set& spwTimes = titer->second; myvec[titer->first].insert(spwTimes.begin(), spwTimes.end()); } } return myvec; } Record MSMetaData::getSummary() const { Record spectralTable; spectralTable.define("names", Vector(getSpwNames())); Record polTable; polTable.define("n correlations", Vector(getNumCorrs())); Record dataDescTable; vector ddToSpw = getDataDescIDToSpwMap(); vector ddToPolID = getDataDescIDToPolIDMap(); vector::const_iterator siter = ddToSpw.begin(); vector::const_iterator send = ddToSpw.end(); vector::const_iterator piter = ddToPolID.begin(); vector spws(ddToSpw.size()); vector polids(ddToPolID.size()); uInt ddid = 0; while (siter != send) { spws[ddid] = *siter; polids[ddid] = *piter; ++siter; ++piter; ++ddid; } dataDescTable.define("spectral windows", Vector(spws)); dataDescTable.define("polarization ids", Vector(polids)); Record summary; summary.defineRecord("spectral windows", spectralTable); summary.defineRecord("polarizations", polTable); summary.defineRecord("data descriptions", dataDescTable); summary.define("fields", Vector(getFieldNames())); vector > obsToArraysMap = _getObservationIDToArrayIDsMap(); vector >::const_iterator oIter = obsToArraysMap.begin(); vector >::const_iterator oEnd = obsToArraysMap.end(); std::map subScanProps = *getSubScanProperties(); uInt oCount = 0; while (oIter != oEnd) { std::set::const_iterator aIter = oIter->begin(); std::set::const_iterator aEnd = oIter->end(); Record obsRec; while (aIter != aEnd) { Record arrayRec; ArrayKey aKey; aKey.obsID = oCount; aKey.arrayID = *aIter; _createScanRecords( arrayRec, aKey, subScanProps ); obsRec.defineRecord("arrayID=" + String::toString(*aIter), arrayRec); ++aIter; } summary.defineRecord("observationID=" + String::toString(oCount), obsRec); ++oIter; ++oCount; } summary.define("nrows", (Int64)nRows()); std::shared_ptr > times = _getTimes(); summary.define("begin time", min(*times)); summary.define("end time", max(*times)); return summary; } void MSMetaData::_createScanRecords( Record& parent, const ArrayKey& arrayKey, const std::map& subScanProps ) const { std::set scanKeys = getScanKeys(arrayKey); std::set::const_iterator scanIter = scanKeys.begin(); std::set::const_iterator scanEnd = scanKeys.end(); while(scanIter != scanEnd) { ScanKey scanKey = *scanIter; std::set antennasForScan; rownr_t scanNRows = 0; Record scanRec; _createSubScanRecords( scanRec, scanNRows, antennasForScan, scanKey, subScanProps ); scanRec.define("nrows", (Int64)scanNRows); scanRec.define("antennas", Vector(antennasForScan.begin(), antennasForScan.size(), 0)); parent.defineRecord("scan=" + String::toString(scanKey.scan), scanRec); ++scanIter; } } void MSMetaData::_createSubScanRecords( Record& parent, rownr_t& scanNRows, std::set& antennasForScan, const ScanKey& scanKey, const std::map& subScanProps ) const { std::set subScans = _getSubScanKeys(scanKey); std::set::const_iterator subScanIter = subScans.begin(); std::set::const_iterator subScanEnd = subScans.end(); uInt subScanCount = 0; while (subScanIter != subScanEnd) { Record subScanRec; SubScanProperties props = subScanProps.find(*subScanIter)->second; subScanRec.define("data description IDs", Vector(props.ddIDs.begin(), props.ddIDs.size(), 0)); rownr_t nrows = props.acRows + props.xcRows; subScanRec.define("nrows", (Int64)nrows); scanNRows += nrows; subScanRec.define("antennas", Vector(props.antennas.begin(), props.antennas.size(), 0)); antennasForScan.insert(props.antennas.begin(), props.antennas.end()); subScanRec.define("begin time", props.beginTime); subScanRec.define("end time", props.endTime); subScanRec.define("state IDs", Vector(props.stateIDs.begin(), props.stateIDs.size(), 0)); //subScanRec.define("field ID", subScanIter->fieldID); _createTimeStampRecords(subScanRec, props); parent.defineRecord("fieldID=" + String::toString(subScanIter->fieldID), subScanRec); ++subScanCount; ++subScanIter; } } void MSMetaData::_createTimeStampRecords( Record& parent, const SubScanProperties& subScanProps ) { std::map::const_iterator tpIter = subScanProps.timeProps.begin(); std::map::const_iterator tpEnd = subScanProps.timeProps.end(); uInt timeCount = 0; while (tpIter != tpEnd) { Record timeRec; timeRec.define( "data description IDs", Vector(tpIter->second.ddIDs.begin(), tpIter->second.ddIDs.size(), 0) ); timeRec.define("nrows", (Int64)(tpIter->second.nrows)); timeRec.define("time", tpIter->first); parent.defineRecord(String::toString(timeCount), timeRec); ++tpIter; ++timeCount; } } std::set MSMetaData::getTimesForIntent(const String& intent) const { if (! _hasIntent(intent)) { return std::set(); } std::map > mymap = _getIntentsToTimesMap(); if (mymap.find(intent) == mymap.end()) { return std::set(); } else { return mymap[intent]; } } Bool MSMetaData::hasBBCNo() const { return _ms->spectralWindow().isColumn(MSSpectralWindowEnums::BBC_NO); } std::map> MSMetaData::_getIntentsToTimesMap() const { if (! _intentToTimesMap.empty()) { return _intentToTimesMap; } vector > stateToIntentsMap; std::set uniqueIntents; _getStateToIntentsMap( stateToIntentsMap, uniqueIntents ); std::map > mymap; if (uniqueIntents.empty()) { return mymap; } std::shared_ptr > stateIDs = _getStateIDs(); std::shared_ptr > times = _getTimes(); Vector::const_iterator state = stateIDs->begin(); Vector::const_iterator time = times->begin(); Vector::const_iterator end = stateIDs->end(); vector > stateToTimes(nStates()); while(state != end) { stateToTimes[*state].insert(*time); ++state; ++time; } vector >::const_iterator intents = stateToIntentsMap.begin(); vector >::const_iterator endState = stateToIntentsMap.end(); uInt count = 0; while (intents != endState) { std::set::const_iterator intent = intents->begin(); std::set::const_iterator eintent = intents->end(); while (intent != eintent) { if (mymap.find(*intent) == mymap.end()) { mymap[*intent] = std::set(); } std::set times = stateToTimes[count]; mymap[*intent].insert(times.begin(), times.end()); ++intent; } ++count; ++intents; } if (_cacheUpdated(_sizeof(mymap))) { _intentToTimesMap = mymap; } return mymap; } vector > MSMetaData::getFieldToScansMap() const { vector > fieldToScansMap; std::map > scanToFieldsMap; _getFieldsAndScansMaps(fieldToScansMap, scanToFieldsMap); return fieldToScansMap; } void MSMetaData::_getFieldsAndScansMaps( vector >& fieldToScansMap, std::map >& scanToFieldsMap ) const { // This method is responsible for setting _fieldToScansMap and _scanToFieldsMap if (! _fieldToScansMap.empty() && ! _scanToFieldsMap.empty()) { fieldToScansMap = _fieldToScansMap; scanToFieldsMap = _scanToFieldsMap; return; } scanToFieldsMap.clear(); fieldToScansMap = vector >(nFields()); std::map > scanToSubScans = _getScanToSubScansMap(); std::map >::const_iterator iter = scanToSubScans.begin(); std::map >::const_iterator end = scanToSubScans.end(); ScanKey scanKey; std::set subScanKeys; std::set::const_iterator subIter, subEnd; while (iter != end) { scanKey = iter->first; subScanKeys = iter->second; subIter = subScanKeys.begin(); subEnd = subScanKeys.end(); while (subIter != subEnd) { uInt fieldID = subIter->fieldID; scanToFieldsMap[scanKey].insert(fieldID); fieldToScansMap[fieldID].insert(scanKey); ++subIter; } ++iter; } if (_cacheUpdated(_sizeof(fieldToScansMap) + _sizeof(scanToFieldsMap))) { _fieldToScansMap = fieldToScansMap; _scanToFieldsMap = scanToFieldsMap; } } vector > MSMetaData::getCorrProducts() const { // responsible for setting _corrProds if (! _corrProds.empty()) { return _corrProds; } String colName = MSPolarization::columnName(MSPolarizationEnums::CORR_PRODUCT); ArrayColumn col(_ms->polarization(), colName); uInt colSize = col.nrow(); vector > contents(colSize); for (uInt i=0; i > MSMetaData::getCorrTypes() const { // responsible for setting _corrTypes if (! _corrTypes.empty()) { return _corrTypes; } String colName = MSPolarization::columnName(MSPolarizationEnums::CORR_TYPE); ArrayColumn col(_ms->polarization(), colName); uInt colSize = col.nrow(); vector > contents(colSize); for (uInt i=0; i MSMetaData::getNumCorrs() const { // responsible for setting _numCorrs if (! _numCorrs.empty()) { return _numCorrs; } String colName = MSPolarization::columnName(MSPolarization::NUM_CORR); ScalarColumn col(_ms->polarization(), colName); vector myvec = col.getColumn().tovector(); if (_cacheUpdated(sizeof(myvec))) { _numCorrs = myvec; } return myvec; } vector > MSMetaData::_getObservationIDToArrayIDsMap() const { // this method is responsible for setting _obsToArraysMap if (! _obsToArraysMap.empty()) { return _obsToArraysMap; } std::shared_ptr > obsIDs = _getObservationIDs(); std::shared_ptr > arrayIDs = _getArrayIDs(); Vector::const_iterator oIter = obsIDs->begin(); Vector::const_iterator oEnd = obsIDs->end(); Vector::const_iterator aIter = arrayIDs->begin(); vector > mymap(nObservations()); while (oIter != oEnd) { mymap[*oIter].insert(*aIter); ++oIter; ++aIter; } if (_cacheUpdated(_sizeof(mymap))) { _obsToArraysMap = mymap; } return mymap; } vector MSMetaData::getFieldTableSourceIDs() const { // this method is responsible for setting _field_sourceIDs if (! _field_sourceIDs.empty()) { return _field_sourceIDs; } String colName = MSField::columnName(MSField::SOURCE_ID); ScalarColumn col(_ms->field(), colName); vector myvec = col.getColumn().tovector(); if (_cacheUpdated(sizeof(myvec))) { _field_sourceIDs = myvec; } return myvec; } vector MSMetaData::getSourceDirections() const { // this method is responsible for setting _sourceDirs if (! _sourceDirs.empty()) { return _sourceDirs; } String colName = MSSource::columnName(MSSource::DIRECTION); ScalarMeasColumn col(_ms->source(), colName); uInt nrows = _ms->source().nrow(); vector myvec(nrows); MDirection direction; for (uInt i=0; i > > MSMetaData::getSourceTimes() const { if (_sourceTimes) { return _sourceTimes; } String colName = MSSource::columnName(MSSource::TIME); ScalarQuantColumn time(_ms->source(), colName); std::shared_ptr > > col = time.getColumn(); if (_cacheUpdated(_sizeof(*col))) { _sourceTimes = col; } return col; } map MSMetaData::_getSourceInfo() const { // this method is responsible for setting _sourceInfo if (! _sourceInfo.empty()) { return _sourceInfo; } auto colName = MSSource::columnName(MSSource::SOURCE_ID); ScalarColumn id(_ms->source(), colName); colName = MSSource::columnName(MSSource::SPECTRAL_WINDOW_ID); ScalarColumn spw(_ms->source(), colName); colName = MSSource::columnName(MSSource::NAME); ScalarColumn name(_ms->source(), colName); colName = MSSource::columnName(MSSource::REST_FREQUENCY); ArrayMeasColumn restfreq(_ms->source(), colName); colName = MSSource::columnName(MSSource::TRANSITION); ArrayColumn transition(_ms->source(), colName); map mymap; uInt nrows = _ms->source().nrow(); Array rf; SourceKey key; SourceProperties props; static const Unit emptyUnit; static const Unit hz("Hz"); for (uInt i=0; i>(rf.tovector()); } else { props.restfreq.reset(); } if (transition.isDefined(i)) { props.transition = std::make_shared>(transition(i).tovector()); } else { props.transition.reset(); } mymap[key] = props; } ThrowIf( mymap.size() < nrows, "Too few source keys found, there are duplicate SOURCE table keys" ); // this is a reasonable approximation for now auto mysize = nrows*(2*sizeof(uInt) + sizeof(Double) + 30); if (_cacheUpdated(mysize)) { _sourceInfo = mymap; } return mymap; } map>> MSMetaData::getRestFrequencies() const { auto mymap = _getSourceInfo(); map>> ret; for_each( mymap.cbegin(), mymap.cend(), [&ret] (const std::pair& val) { ret[val.first] = val.second.restfreq; } ); return ret; } map > > MSMetaData::getTransitions() const { map mymap = _getSourceInfo(); map > > ret; map::const_iterator iter = mymap.begin(); map::const_iterator end = mymap.end(); while (iter != end) { ret[iter->first] = iter->second.transition; ++iter; } return ret; } vector MSMetaData::getSourceNames() const { // this method is responsible for setting _sourceNames if (! _sourceNames.empty()) { return _sourceNames; } String colName = MSSource::columnName(MSSource::NAME); ScalarColumn col(_ms->source(), colName); vector myvec = col.getColumn().tovector(); if (_cacheUpdated(sizeof(myvec))) { _sourceNames = myvec; } return myvec; } vector MSMetaData::getSourceTableSourceIDs() const { // this method is responsible for setting _source_sourceIDs if (! _source_sourceIDs.empty()) { return _source_sourceIDs; } String colName = MSSource::columnName(MSSource::SOURCE_ID); ScalarColumn col(_ms->source(), colName); vector myvec = col.getColumn().tovector(); if (_cacheUpdated(sizeof(myvec))) { _source_sourceIDs = myvec; } return myvec; } uInt MSMetaData::nUniqueSourceIDsFromSourceTable() const { String colName = MSSource::columnName(MSSource::SOURCE_ID); ScalarColumn col(_ms->source(), colName); Vector myvec = col.getColumn(); std::set myset(myvec.begin(), myvec.end()); return myset.size(); } Bool MSMetaData::_hasIntent(const String& intent) const { std::set uniqueIntents = getIntents(); return uniqueIntents.find(intent) != uniqueIntents.end(); } vector MSMetaData::getFieldNamesForFieldIDs( const vector& fieldIDs ) { if (fieldIDs.size() == 0) { return getFieldNames(); } // Do not use _checkFieldIDs since fieldIDs that may not be in the // main table can be valid. CAS-5168 uInt max = *max_element(fieldIDs.begin(), fieldIDs.end()); uInt nField = nFields(); if (max >= nField) { ostringstream os; os << "MSMetaData::" << __FUNCTION__ << ": This MS only has " << nField << " fields so requested field number " << max << " does not exist"; throw AipsError(os.str()); } vector allNames = getFieldNames(); vector names; vector::const_iterator end = fieldIDs.end(); for ( vector::const_iterator iter=fieldIDs.begin(); iter!=end; ++iter ) { names.push_back(allNames[*iter]); } return names; } std::set MSMetaData::getFieldsForTimes( const Double center, const Double tol ) { _checkTolerance(tol); Double minTime = center - tol; Double maxTime = center + tol; std::shared_ptr > > fieldToTimesMap; std::shared_ptr > > timeToFieldsMap; _getFieldsAndTimesMaps( fieldToTimesMap, timeToFieldsMap ); std::set fields; std::map >::const_iterator end = timeToFieldsMap->end(); // A std::set is always ordered. // FIXME could do a binary search to make this faster for ( std::map >::const_iterator iter=timeToFieldsMap->begin(); iter!=end; ++iter ) { Double curTime = iter->first; if (curTime >= minTime) { std::set curFields = iter->second; fields.insert(curFields.begin(), curFields.end()); } if (curTime > maxTime) { break; } } return fields; } void MSMetaData::_checkTolerance(const Double tol) { ThrowIf( tol < 0, "Tolerance cannot be less than zero" ); } void MSMetaData::_getFieldsAndTimesMaps( std::shared_ptr > >& fieldToTimesMap, std::shared_ptr > >& timeToFieldsMap ) { // This method is responsible for setting _fieldToTimesMap and _timeToFieldMap if ( _fieldToTimesMap && ! _fieldToTimesMap->empty() && _timeToFieldsMap && ! _timeToFieldsMap->empty() ) { fieldToTimesMap = _fieldToTimesMap; timeToFieldsMap = _timeToFieldsMap; return; } fieldToTimesMap = std::make_shared>>(); timeToFieldsMap = std::make_shared>>(); std::shared_ptr > allFields = _getFieldIDs(); std::shared_ptr > allTimes = this->_getTimes(); Vector::const_iterator lastField = allFields->end(); Vector::const_iterator curTime = allTimes->begin(); for ( Vector::const_iterator curField=allFields->begin(); curField!=lastField; ++curField, ++curTime ) { (*fieldToTimesMap)[*curField].insert(*curTime); (*timeToFieldsMap)[*curTime].insert(*curField); } if ( _cacheUpdated(_sizeof(*fieldToTimesMap) + _sizeof(*timeToFieldsMap)) ) { _fieldToTimesMap = fieldToTimesMap; _timeToFieldsMap = timeToFieldsMap; } } std::set MSMetaData::getTimesForField(const Int fieldID) { if (! _hasFieldID(fieldID)) { return std::set(); } std::shared_ptr > > fieldToTimesMap; std::shared_ptr > > timeToFieldsMap; _getFieldsAndTimesMaps( fieldToTimesMap, timeToFieldsMap ); return (*fieldToTimesMap)[fieldID]; } vector MSMetaData::getObservers() const { if (! _observers.empty()) { return _observers; } String colName = MSObservation::columnName(MSObservationEnums::OBSERVER); ScalarColumn col(_ms->observation(), colName); vector contents = col.getColumn().tovector(); if (_cacheUpdated(_sizeof(contents))) { _observers = contents; } return contents; } vector MSMetaData::getObservatoryNames() { if (! _observatoryNames.empty()) { return _observatoryNames; } String tnameColName = MSObservation::columnName(MSObservationEnums::TELESCOPE_NAME); ScalarColumn telescopeNameCol(_ms->observation(), tnameColName); vector names = telescopeNameCol.getColumn().tovector(); if (_cacheUpdated(_sizeof(names))) { _observatoryNames = names; } return names; } vector MSMetaData::getProjects() const { if (! _projects.empty()) { return _projects; } String colName = MSObservation::columnName(MSObservationEnums::PROJECT); ScalarColumn col(_ms->observation(), colName); vector projects = col.getColumn().tovector(); if (_cacheUpdated(_sizeof(projects))) { _projects = projects; } return projects; } vector > MSMetaData::getSchedules() const { // responsible for setting _schedules if (! _schedules.empty()) { return _schedules; } String colName = MSObservation::columnName(MSObservationEnums::SCHEDULE); ArrayColumn col(_ms->observation(), colName); uInt colSize = col.nrow(); vector > contents(colSize); for (uInt i=0; i > MSMetaData::getTimeRangesOfObservations() const { if (! _timeRangesForObs.empty()) { return _timeRangesForObs; } String colName = MSObservation::columnName(MSObservationEnums::TIME_RANGE); ArrayColumn col(_ms->observation(), colName); TableRecord kv = col.keywordSet(); String unit = kv.asArrayString("QuantumUnits").tovector()[0]; MEpoch::Types myRF; MEpoch::getType(myRF, kv.asRecord("MEASINFO").asString("Ref")); uInt n = col.nrow(); vector > contents(n); for (uInt i=0; i row = col.get(i); Quantity begin(row[0], unit); Quantity end(row[1], unit); contents[i] = std::pair(MEpoch(begin, myRF), MEpoch(end, myRF)); } if (_cacheUpdated(_sizeof(contents))) { _timeRangesForObs = contents; } return contents; } MPosition MSMetaData::getObservatoryPosition(uInt which) const { if (which >= _ms->observation().nrow()) { throw AipsError(_ORIGIN + " out of range exception."); } if (! _observatoryPositions.empty()) { return _observatoryPositions[which]; } String tnameColName = MSObservation::columnName(MSObservationEnums::TELESCOPE_NAME); ScalarColumn telescopeNameCol(_ms->observation(), tnameColName); vector names = telescopeNameCol.getColumn().tovector(); vector observatoryPositions(names.size()); for (uInt i=0; i MSMetaData::getPhaseDirs(const MEpoch& ep) const { // this method is responsible for setting _phaseDirs vector myDirs; if (_phaseDirs.empty()) { String name = MSField::columnName(MSFieldEnums::PHASE_DIR); ScalarMeasColumn phaseDirCol(_ms->field(), name); uInt nrows = nFields(); for (uInt i=0; i > ephems = _getEphemFieldIDs(); std::set::const_iterator iter = ephems->begin(); std::set::const_iterator end = ephems->end(); for (; iter!=end; ++iter) { myDirs[*iter] = phaseDirFromFieldIDAndTime(*iter, ep); } return myDirs; } vector MSMetaData::_getAntennaPositions() const { // This method is responsible for setting _antennaPositions if (! _antennaPositions.empty()) { return _antennaPositions; } String antNameColName = MSAntenna::columnName(MSAntennaEnums::NAME); ScalarColumn nameCol(_ms->antenna(), antNameColName); String antPosColName = MSAntenna::columnName(MSAntennaEnums::POSITION); ArrayColumn posCol(_ms->antenna(), antPosColName); Array xyz = posCol.getColumn(); Vector posUnits = posCol.keywordSet().asArrayString("QuantumUnits"); String sFrame = posCol.keywordSet().asRecord("MEASINFO").asString("Ref"); MPosition::Types posType = MPosition::getType(sFrame); Array::const_iterator end = xyz.end(); Quantity x(0, posUnits[0]); Quantity y(0, posUnits[1]); Quantity z(0, posUnits[2]); vector antennaPositions; for (Array::const_iterator iter=xyz.begin(); iter!=end; ++iter) { x.setValue(*iter); Double xm = x.getValue("m"); ++iter; y.setValue(*iter); Double ym = y.getValue("m"); ++iter; z.setValue(*iter); Double zm = z.getValue("m"); MPosition antPos(MVPosition(xm, ym, zm), posType); antennaPositions.push_back(antPos); } if(_cacheUpdated(30*antennaPositions.size())) { _antennaPositions = antennaPositions; } return antennaPositions; } vector MSMetaData::getAntennaPositions( const vector& which ) const { vector allPos = _getAntennaPositions(); if (which.empty()) { return allPos; } ThrowIf( max(Vector(which)) >= nAntennas(), "Antenna ID out of range" ); vector output; vector::const_iterator end = which.end(); for ( vector::const_iterator iter=which.begin(); iter!=end; ++iter ) { output.push_back(allPos[*iter]); } return output; } vector > MSMetaData::getAntennaPositions( const vector& names ) { ThrowIf( names.empty(), _ORIGIN + "names cannot be empty" ); vector > ids = getAntennaIDs(names); vector >::const_iterator iter = ids.begin(); vector >::const_iterator end = ids.end(); vector > pos; for (; iter!=end; ++iter) { std::vector mypos; std::set::const_iterator siter = iter->begin(); std::set::const_iterator send = iter->end(); for (; siter!=send; ++siter) { mypos.push_back(getAntennaPositions(vector(1, *siter))[0]); } pos.push_back(mypos); } return pos; } QVD MSMetaData::getAntennaOffset(uInt which) const { ThrowIf( which >= nAntennas(), "Out of range exception." ); return getAntennaOffsets()[which]; } vector MSMetaData::getAntennaOffsets() const { // This method is responsble for setting _antennaOffsets if (! _antennaOffsets.empty()) { return _antennaOffsets; } MPosition obsPos = getObservatoryPosition(0); if (obsPos.getRef().getType() != MPosition::ITRF) { MeasConvert toItrf(obsPos, MPosition::ITRF); obsPos = toItrf(obsPos); } Vector obsXYZ = obsPos.get("m").getValue(); Double xo = obsXYZ[0]; Double yo = obsXYZ[1]; Double zo = obsXYZ[2]; Double rObs = sqrt(xo*xo + yo*yo + zo*zo); Vector obsLongLat = obsPos.getAngle("rad").getValue(); Double longObs = obsLongLat[0]; Double latObs = obsLongLat[1]; vector antennaPositions = _getAntennaPositions(); vector::const_iterator end = antennaPositions.end(); vector antennaOffsets; for ( vector::const_iterator iter=antennaPositions.begin(); iter!=end; ++iter ) { Vector xyz = iter->get("m").getValue(); Double x = xyz[0]; Double y = xyz[1]; Double z = xyz[2]; Double rAnt = sqrt(x*x + y*y + z*z); Vector antLongLat = iter->getAngle("rad").getValue(); Double longAnt = antLongLat[0]; Double latAnt = antLongLat[1]; Vector offset(3); offset[0] = (longAnt - longObs)*rObs*cos(latObs); offset[1] = (latAnt - latObs)*rObs; offset[2] = rAnt - rObs; QVD qoffset(offset, "m"); antennaOffsets.push_back(qoffset); } if (_cacheUpdated(30*antennaOffsets.size())) { _antennaOffsets = antennaOffsets; } return antennaOffsets; } uInt MSMetaData::nBaselines(Bool includeAutoCorrelation) { Matrix baselines = getUniqueBaselines().copy(); uInt ac = 0; uInt nrows = baselines.nrow(); for (uInt i=0; i MSMetaData::getUniqueBaselines() { if (! _uniqueBaselines.empty()) { return _uniqueBaselines; } std::shared_ptr > ant1, ant2; _getAntennas(ant1, ant2); Vector::const_iterator a1Iter = ant1->begin(); Vector::const_iterator a2Iter = ant2->begin(); Vector::const_iterator end = ant1->end(); uInt nAnts = nAntennas(); Matrix baselines(nAnts, nAnts, False); while (a1Iter != end) { baselines(*a1Iter, *a2Iter) = True; baselines(*a2Iter, *a1Iter) = True; ++a1Iter; ++a2Iter; } if (_cacheUpdated(sizeof(Bool)*baselines.size())) { _uniqueBaselines = baselines; } return baselines; } QVD MSMetaData::getAntennaOffset( const String& name ) const { return getAntennaOffsets(name)[0]; } std::vector MSMetaData::getAntennaOffsets( const String& name ) const { std::set ids = getAntennaIDs(name); std::vector offsets; std::set::const_iterator iter = ids.begin(); std::set::const_iterator end = ids.end(); for(; iter!=end; ++iter) { offsets.push_back(getAntennaOffset(*iter)); } return offsets; } Quantity MSMetaData::getEffectiveTotalExposureTime() { // This method has the responsibility of setting _exposureTime. if (_exposureTime.getValue() > 0) { return _exposureTime; } uInt nAnts = nAntennas(); uInt maxNBaselines = nAnts*(nAnts-1)/2; Double totalExposure = 0; String taql = "select FLAG, DATA_DESC_ID, EXPOSURE, TIME from " + _ms->tableName() + " where ANTENNA1 != ANTENNA2"; Table result(tableCommand(taql).table()); Vector ddIDs = ScalarColumn(result, "DATA_DESC_ID").getColumn(); Vector exposures = ScalarColumn(result, "EXPOSURE").getColumn(); Vector times = ScalarColumn(result, "TIME").getColumn(); // each row represents a unique baseline, data description ID, and time combination uInt nrows = result.nrow(); vector dataDescToSpwIdMap = getDataDescIDToSpwMap(); std::set avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw; vector spwInfo = _getSpwInfo(avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw); std::map timeToBWMap = _getTimeToTotalBWMap( times, ddIDs ); for (uInt i=0; i flagsMatrix(ArrayColumn(result, "FLAG").get(i)); uInt nCorrelations = flagsMatrix.nrow(); Double denom = (timeToBWMap.find(times[i])->second)*maxNBaselines*nCorrelations; for (uInt corr=0; corr goodData = ! flagsMatrix.row(corr); if (anyTrue(goodData)) { MaskedArray flaggedChannelWidths( channelWidths.getValue("Hz"), goodData, True ); Double effectiveBW = sum(flaggedChannelWidths); totalExposure += exposures[i]*effectiveBW/denom; } } } String unit = ScalarColumn(*_ms, "EXPOSURE").keywordSet().asArrayString("QuantumUnits").tovector()[0]; Quantity eTime(totalExposure, unit); if (_cacheUpdated(10)) { _exposureTime = eTime; } return eTime; } MSMetaData::SubScanProperties MSMetaData::getSubScanProperties( const SubScanKey& subScan, Bool showProgress ) const { _checkSubScan(subScan); return getSubScanProperties(showProgress)->find(subScan)->second; } void MSMetaData::_getScalarIntColumn( Vector& v, TableProxy& tp, const String& colname, rownr_t beginRow, rownr_t nrows ) { v = tp.getColumn(colname, beginRow, nrows, 1).asArrayInt(); } void MSMetaData::_getScalarDoubleColumn( Vector& v, TableProxy& tp, const String& colname, rownr_t beginRow, rownr_t nrows ) { v = tp.getColumn(colname, beginRow, nrows, 1).asArrayDouble(); } void MSMetaData::_getScalarQuantDoubleColumn( Quantum >& v, TableProxy& tp, const String& colname, rownr_t beginRow, rownr_t nrows ) { ScalarQuantColumn mycol(tp.table(), colname); v = Quantum >( tp.getColumn(colname, beginRow, nrows, 1).asArrayDouble(), mycol.getUnits() ); } void MSMetaData::_computeScanAndSubScanProperties( std::shared_ptr >& scanProps, std::shared_ptr >& subScanProps, Bool showProgress ) const { std::shared_ptr pm; if (showProgress || _showProgress) { LogIO log; const static String title = "Computing scan and subscan properties..."; log << LogOrigin("MSMetaData", __func__, WHERE) << LogIO::NORMAL << title << LogIO::POST; pm = std::make_shared(0, _ms->nrow(), title); } const static String scanName = MeasurementSet::columnName(MSMainEnums::SCAN_NUMBER); const static String fieldName = MeasurementSet::columnName(MSMainEnums::FIELD_ID); const static String ddidName = MeasurementSet::columnName(MSMainEnums::DATA_DESC_ID); const static String stateName = MeasurementSet::columnName(MSMainEnums::STATE_ID); const static String timeName = MeasurementSet::columnName(MSMainEnums::TIME); const static String arrayName = MeasurementSet::columnName(MSMainEnums::ARRAY_ID); const static String obsName = MeasurementSet::columnName(MSMainEnums::OBSERVATION_ID); const static String ant1Name = MeasurementSet::columnName(MSMainEnums::ANTENNA1); const static String ant2Name = MeasurementSet::columnName(MSMainEnums::ANTENNA2); const static String exposureName = MeasurementSet::columnName(MSMainEnums::EXPOSURE); const static String intervalName = MeasurementSet::columnName(MSMainEnums::INTERVAL); TableProxy tp(*_ms); std::vector< pair, map > > props; std::vector ddIDToSpw = getDataDescIDToSpwMap(); scanProps = std::make_shared>(); subScanProps = std::make_shared>(); rownr_t doneRows = 0; rownr_t msRows = _ms->nrow(); static const rownr_t rowsInChunk = 10000000; for (rownr_t row=0; row scans, fields, ddIDs, states, arrays, observations, ant1, ant2; _getScalarIntColumn(scans, tp, scanName, row, nrows); _getScalarIntColumn(fields, tp, fieldName, row, nrows); _getScalarIntColumn(ddIDs, tp, ddidName, row, nrows); _getScalarIntColumn(states, tp, stateName, row, nrows); _getScalarIntColumn(arrays, tp, arrayName, row, nrows); _getScalarIntColumn(observations, tp, obsName, row, nrows); _getScalarIntColumn(ant1, tp, ant1Name, row, nrows); _getScalarIntColumn(ant2, tp, ant2Name, row, nrows); Vector times; _getScalarDoubleColumn(times, tp, timeName, row, nrows); Quantum > exposureTimes, intervalTimes; _getScalarQuantDoubleColumn( exposureTimes, tp, exposureName, row, nrows ); _getScalarQuantDoubleColumn( intervalTimes, tp, intervalName, row, nrows ); rownr_t nchunks = min((rownr_t)1000, nrows); rownr_t chunkSize = nrows/nchunks; if (nrows % nchunks > 0) { // integer division nchunks = nrows/chunkSize + 1; } pair, map > *fut = new pair, map >[nchunks]; #ifdef _OPENMP #pragma omp parallel for #endif for (uInt i=0; iupdate(doneRows); } } _mergeScanProps(scanProps, subScanProps, props); } void MSMetaData::_mergeScanProps( std::shared_ptr >& scanProps, std::shared_ptr >& subScanProps, const std::vector< pair, map > >& props ) const { scanProps = std::make_shared>(); subScanProps = std::make_shared>(); map > ssSumInterval; uInt nTotChunks = props.size(); for (uInt i=0; i& vScanProps = props[i].first; map::const_iterator viter = vScanProps.begin(); map::const_iterator vend = vScanProps.end(); for (; viter!=vend; ++viter) { // iterate over scans in this chunk const ScanKey& scanKey = viter->first; const std::pair& range = viter->second.timeRange; if (scanProps->find(scanKey) == scanProps->end()) { (*scanProps)[scanKey].timeRange = range; } else { std::pair& tr = (*scanProps)[scanKey].timeRange; tr.first = min(tr.first, range.first); tr.second = max(tr.second, range.second); } std::map::const_iterator spnIter = viter->second.spwNRows.begin(); std::map::const_iterator spnEnd = viter->second.spwNRows.end(); for (; spnIter!=spnEnd; ++spnIter) { const uInt& spw = spnIter->first; const std::set scanTimes = viter->second.times.find(spw)->second; if ((*scanProps)[scanKey].spwNRows.find(spw) == (*scanProps)[scanKey].spwNRows.end()) { (*scanProps)[scanKey].spwNRows[spw] = spnIter->second; (*scanProps)[scanKey].times[spw] = scanTimes; } else { (*scanProps)[scanKey].spwNRows[spw] += spnIter->second; (*scanProps)[scanKey].times[spw].insert(scanTimes.begin(), scanTimes.end()); } const std::set mytimes = vScanProps.find(scanKey)->second.times.find(spw)->second; (*scanProps)[scanKey].times[spw].insert(mytimes.begin(), mytimes.end()); } } map::const_iterator ssIter = props[i].second.begin(); map::const_iterator ssEnd = props[i].second.end(); for (; ssIter!=ssEnd; ++ssIter) { // iterate over subscans in this chunk const SubScanKey& ssKey = ssIter->first; const SubScanProperties& val = ssIter->second; if (subScanProps->find(ssKey) == subScanProps->end()) { // first time this subscan has been seen in any chunks (*subScanProps)[ssKey] = val; map::const_iterator mi = val.meanInterval.begin(); map::const_iterator me = val.meanInterval.end(); for (; mi!=me; ++mi) { const uInt& spw = mi->first; ssSumInterval[ssKey][spw] = mi->second*Quantity(val.spwNRows.find(spw)->second); } } else { SubScanProperties& fp = (*subScanProps)[ssKey]; // the rows increment must come before the mean exposure time // computation fp.acRows += val.acRows; fp.xcRows += val.xcRows; fp.antennas.insert(val.antennas.begin(), val.antennas.end()); fp.beginTime = min(fp.beginTime, val.beginTime); fp.ddIDs.insert(val.ddIDs.begin(), val.ddIDs.end()); fp.endTime = max(fp.endTime, val.endTime); rownr_t nrows = fp.acRows + fp.xcRows; fp.meanExposureTime = (fp.meanExposureTime*Quantity(nrows - 1) + val.meanExposureTime)/nrows; fp.stateIDs.insert(val.stateIDs.begin(), val.stateIDs.end()); fp.spws.insert(val.spws.begin(), val.spws.end()); std::set::const_iterator spwIter = val.spws.begin(); std::set::const_iterator spwEnd = val.spws.end(); for (; spwIter!=spwEnd; ++spwIter) { const uInt& spw = *spwIter; rownr_t vSpwNRows = val.spwNRows.find(spw)->second; fp.spwNRows[spw] += vSpwNRows; const Quantity& vMeanInt = val.meanInterval.find(spw)->second; if (ssSumInterval[ssKey].find(spw) == ssSumInterval[ssKey].end()) { ssSumInterval[ssKey][spw] = vMeanInt * Quantity(vSpwNRows, ""); } else { ssSumInterval[ssKey][spw] += vMeanInt * Quantity(vSpwNRows, ""); } } map::const_iterator tpIter = val.timeProps.begin(); map::const_iterator tpEnd = val.timeProps.end(); for (; tpIter!=tpEnd; ++tpIter) { Double time = tpIter->first; const TimeStampProperties& tprops = tpIter->second; if (fp.timeProps.find(time) == fp.timeProps.end()) { fp.timeProps[time] = tprops; } else { fp.timeProps[time].ddIDs.insert(tprops.ddIDs.begin(), tprops.ddIDs.end()); fp.timeProps[time].nrows += tprops.nrows; } } _modifyFirstExposureTimeIfNecessary(fp.firstExposureTime, val.firstExposureTime); } } } map > scanSumInterval; map::iterator ssIter = subScanProps->begin(); map::iterator ssEnd = subScanProps->end(); for (; ssIter!=ssEnd; ++ssIter) { const SubScanKey& ssKey = ssIter->first; SubScanProperties& props = ssIter->second; map::const_iterator ssSpwNRowsIter = props.spwNRows.begin(); map::const_iterator ssSpwNRowsEnd = props.spwNRows.end(); for (; ssSpwNRowsIter!=ssSpwNRowsEnd; ++ssSpwNRowsIter) { const uInt& spw = ssSpwNRowsIter->first; props.meanInterval[spw] = ssSumInterval[ssKey][spw]/ssSpwNRowsIter->second; } const ScanKey scanKey = casacore::scanKey(ssKey); if (scanSumInterval.find(scanKey) == scanSumInterval.end()) { // first time associated scan key has been seen scanSumInterval[scanKey] = ssSumInterval[ssKey]; (*scanProps)[scanKey].firstExposureTime = props.firstExposureTime; } else { map::const_iterator spwSumIter = ssSumInterval[ssKey].begin(); map::const_iterator spwSumEnd = ssSumInterval[ssKey].end(); for (; spwSumIter!=spwSumEnd; ++spwSumIter) { const uInt& spw = spwSumIter->first; if (scanSumInterval[scanKey].find(spw) == scanSumInterval[scanKey].end()) { scanSumInterval[scanKey][spw] = spwSumIter->second; } else { scanSumInterval[scanKey][spw] += spwSumIter->second; } } _modifyFirstExposureTimeIfNecessary( (*scanProps)[scanKey].firstExposureTime, ssIter->second.firstExposureTime ); } } map::iterator scanPropsIter = scanProps->begin(); map::iterator scanPropsEnd = scanProps->end(); for (; scanPropsIter!=scanPropsEnd; ++scanPropsIter) { const ScanKey& scanKey = scanPropsIter->first; map::const_iterator scanSpwNRowsIter = scanPropsIter->second.spwNRows.begin(); map::const_iterator scanSpwNRowsEnd = scanPropsIter->second.spwNRows.end(); for (; scanSpwNRowsIter!=scanSpwNRowsEnd; ++scanSpwNRowsIter) { const uInt& spw = scanSpwNRowsIter->first; const rownr_t& nrows = scanSpwNRowsIter->second; scanPropsIter->second.meanInterval[spw] = scanSumInterval[scanKey][spw]/nrows; } } } void MSMetaData::_modifyFirstExposureTimeIfNecessary( FirstExposureTimeMap& current, const FirstExposureTimeMap& test ) { // deal with first exposure time maps FirstExposureTimeMap::const_iterator feiter = test.begin(); FirstExposureTimeMap::const_iterator feend = test.end(); for (; feiter!=feend; ++feiter) { Int ddID = feiter->first; Double timestamp = feiter->second.first; // could be implemented in terms of overloaded _modifyFirstExposureTimeIfNecessary, // but would require decomposing the exposure quantity into value and unit. Since // this is called for every row in the main table, that may impact performance so // I've opted to leave it as is. if ( current.find(ddID) == current.end() || timestamp < current[ddID].first ) { // dd ID not yet encountered yet // or the first time stamp for this ddID is // before what has yet been encountered current[ddID] = feiter->second; } } } void MSMetaData::_modifyFirstExposureTimeIfNecessary( FirstExposureTimeMap& current, Int dataDescID, Double time, Double exposure, const Unit& eunit ) { if (current.find(dataDescID) == current.end()) { // the data description ID for this sub scan has not yet been // encountered in this chunk current[dataDescID].first = time; current[dataDescID].second = Quantity(exposure, eunit); } else if (time < current[dataDescID].first) { current[dataDescID].first = time; // unit is already set from first time, so only need to reset value current[dataDescID].second.setValue(exposure); } } pair, map > MSMetaData::_getChunkSubScanProperties( const Vector& scans, const Vector& fields, const Vector& ddIDs, const Vector& states, const Vector& times, const Vector& arrays, const Vector& observations, const Vector& ant1, const Vector& ant2, const Quantum >& exposureTimes, const Quantum >& intervalTimes, const vector& ddIDToSpw, rownr_t beginRow, rownr_t endRow ) const { VectorSTLIterator scanIter(scans); scanIter += beginRow; VectorSTLIterator a1Iter(ant1); a1Iter += beginRow; VectorSTLIterator a2Iter(ant2); a2Iter += beginRow; VectorSTLIterator fIter(fields); fIter += beginRow; VectorSTLIterator dIter(ddIDs); dIter += beginRow; VectorSTLIterator stateIter(states); stateIter += beginRow; VectorSTLIterator oIter(observations); oIter += beginRow; VectorSTLIterator arIter(arrays); arIter += beginRow; VectorSTLIterator tIter(times); tIter += beginRow; VectorSTLIterator eiter(exposureTimes.getValue()); eiter += beginRow; VectorSTLIterator iIter(intervalTimes.getValue()); iIter += beginRow; map scanProps; map mysubscans; map exposureSum; map, Double> intervalSum; ScanKey scanKey; SubScanKey subScanKey; pair subScanSpw; rownr_t row = beginRow; const Unit& eunit = exposureTimes.getFullUnit(); while (row < endRow) { scanKey.obsID = *oIter; scanKey.arrayID = *arIter; scanKey.scan = *scanIter; Double half = *iIter/2; uInt spw = ddIDToSpw[*dIter]; if (scanProps.find(scanKey) == scanProps.end()) { // first time this scan has been encountered in this chunk scanProps[scanKey].timeRange = std::make_pair(*tIter-half, *tIter+half); scanProps[scanKey].times[spw] = std::set(); scanProps[scanKey].spwNRows[spw] = 1; } else { pair& timeRange = scanProps[scanKey].timeRange; timeRange.first = min(timeRange.first, *tIter-half); timeRange.second = max(timeRange.second, *tIter+half); map >& times = scanProps[scanKey].times; if (times.find(spw) == times.end()) { times[spw] = std::set(); scanProps[scanKey].spwNRows[spw] = 1; } else { ++scanProps[scanKey].spwNRows[spw]; } } scanProps[scanKey].times[spw].insert(*tIter); subScanKey.obsID = *oIter; subScanKey.arrayID = *arIter; subScanKey.scan = *scanIter; subScanKey.fieldID = *fIter; Bool autocorr = *a1Iter == *a2Iter; if ( mysubscans.find(subScanKey) == mysubscans.end() ) { // first time this subscan has been encountered in this chunk SubScanProperties props; props.acRows = autocorr ? 1 : 0; props.xcRows = autocorr ? 0 : 1; props.beginTime = *tIter; props.endTime = *tIter; props.firstExposureTime[*dIter].first = *tIter; props.firstExposureTime[*dIter].second = Quantity(*eiter, eunit); mysubscans[subScanKey] = props; exposureSum[subScanKey] = *eiter; } else { if (autocorr) { ++mysubscans[subScanKey].acRows; } else { ++mysubscans[subScanKey].xcRows; } mysubscans[subScanKey].beginTime = min(*tIter, mysubscans[subScanKey].beginTime); mysubscans[subScanKey].endTime = max(*tIter, mysubscans[subScanKey].endTime); _modifyFirstExposureTimeIfNecessary( mysubscans[subScanKey].firstExposureTime, *dIter, *tIter, *eiter, eunit ); exposureSum[subScanKey] += *eiter; } subScanSpw.first = subScanKey; subScanSpw.second = spw; if (intervalSum.find(subScanSpw) == intervalSum.end()) { intervalSum[subScanSpw] = *iIter; mysubscans[subScanKey].spwNRows[spw] = 1; } else { intervalSum[subScanSpw] += *iIter; ++mysubscans[subScanKey].spwNRows[spw]; } mysubscans[subScanKey].antennas.insert(*a1Iter); mysubscans[subScanKey].antennas.insert(*a2Iter); mysubscans[subScanKey].ddIDs.insert(*dIter); mysubscans[subScanKey].spws.insert(spw); mysubscans[subScanKey].stateIDs.insert(*stateIter); std::map& timeProps = mysubscans[subScanKey].timeProps; if (timeProps.find(*tIter) == timeProps.end()) { timeProps[*tIter].nrows = 1; } else { ++timeProps[*tIter].nrows; } timeProps[*tIter].ddIDs.insert(*dIter); ++tIter; ++scanIter; ++fIter; ++dIter; ++stateIter; ++oIter; ++arIter; ++a1Iter; ++a2Iter; ++eiter; ++iIter; ++row; } map::iterator ssIter = mysubscans.begin(); map::iterator ssEnd = mysubscans.end(); for (; ssIter!=ssEnd; ++ssIter) { SubScanProperties& props = ssIter->second; props.meanExposureTime = Quantity( exposureSum[ssIter->first]/(props.acRows + props.xcRows), eunit ); } const Unit& unit = intervalTimes.getFullUnit(); map, Double>::const_iterator intSumIter = intervalSum.begin(); map, Double>::const_iterator intSumEnd = intervalSum.end(); for (; intSumIter!=intSumEnd; ++intSumIter) { const SubScanKey& ssKey = intSumIter->first.first; const uInt& spw = intSumIter->first.second; const Double& sum = intSumIter->second; SubScanProperties& props = mysubscans[ssKey]; props.meanInterval[spw] = Quantity(sum/props.spwNRows[spw], unit); } return make_pair(scanProps, mysubscans); } std::shared_ptr > MSMetaData::getSubScanProperties( Bool showProgress ) const { std::shared_ptr > scanProps; std::shared_ptr > subScanProps; _getScanAndSubScanProperties( scanProps, subScanProps, showProgress ); return subScanProps; } std::shared_ptr > MSMetaData::_getScanProperties( Bool showProgress ) const { std::shared_ptr > scanProps; std::shared_ptr > subScanProps; _getScanAndSubScanProperties( scanProps, subScanProps, showProgress ); return scanProps; } void MSMetaData::_getScanAndSubScanProperties( std::shared_ptr >& scanProps, std::shared_ptr >& subScanProps, Bool showProgress ) const { // responsible for setting _scanProperties and _subScanProperties // a sub scan is defined by a unique combination of scan number and field ID if (_scanProperties && _subScanProperties) { scanProps = _scanProperties; subScanProps = _subScanProperties; return; } std::shared_ptr > myssprops; std::shared_ptr > myscanprops; _computeScanAndSubScanProperties( myscanprops, myssprops, showProgress ); scanProps = myscanprops; subScanProps = myssprops; static const uInt iSize = sizeof(Int); static const uInt dSize = sizeof(Double); static const uInt scanStructSize = 2*dSize; static const uInt scanKeySize = 3*iSize; // fudge of sizeof(Unit) static const uInt unitSize = 16; static const uInt feSize = iSize + 2*dSize + unitSize; uInt scanSize = scanProps->size() * (scanKeySize + scanStructSize); std::map::const_iterator scanIter = scanProps->begin(); std::map::const_iterator scanEnd = scanProps->end(); for (; scanIter!=scanEnd; ++scanIter) { const ScanProperties& props = scanIter->second; scanSize += props.meanInterval.size() * (dSize + 4*iSize); const map >& spwTimes = props.times; map >::const_iterator tIter = spwTimes.begin(); map >::const_iterator tEnd = spwTimes.end(); for (; tIter!=tEnd; ++tIter) { scanSize += dSize * tIter->second.size(); } scanSize += feSize * props.firstExposureTime.size(); } static const uInt ssStructSize = 3*dSize + 2*iSize; static const uInt sskeySize = 4*iSize; uInt subScanSize = subScanProps->size() * (ssStructSize + sskeySize); std::map::const_iterator subIter = subScanProps->begin(); std::map::const_iterator subEnd = subScanProps->end(); for ( ; subIter != subEnd; ++subIter) { const SubScanProperties& props = subIter->second; subScanSize += iSize*( props.antennas.size() + props.ddIDs.size() + props.stateIDs.size() + props.spws.size() ); subScanSize += (dSize + iSize) + props.timeProps.size(); // meanInterval + spwNRows subScanSize += (3*iSize + dSize) * props.meanInterval.size(); subScanSize += feSize * props.firstExposureTime.size(); } uInt mysize = scanSize + subScanSize; if (_cacheUpdated(mysize)) { _scanProperties = scanProps; _subScanProperties = subScanProps; } else if (_forceSubScanPropsToCache) { _cacheMB += mysize/1e6; _scanProperties = scanProps; _subScanProperties = subScanProps; } } std::map MSMetaData::_getTimeToTotalBWMap( const Vector& times, const Vector& ddIDs ) { std::map timeToBWMap; std::map > timeToDDIDMap; Vector::const_iterator end = times.end(); Vector::const_iterator tIter = times.begin(); Vector::const_iterator dIter = ddIDs.begin(); while (tIter!=end) { timeToDDIDMap[*tIter].insert(*dIter); ++tIter; ++dIter; } std::map >::const_iterator end1 = timeToDDIDMap.end(); vector dataDescIDToSpwMap = getDataDescIDToSpwMap(); std::set avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw; vector spwInfo = _getSpwInfo(avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw); for ( std::map >::const_iterator iter=timeToDDIDMap.begin(); iter!=end1; ++iter ) { std::set ddIDs = iter->second; timeToBWMap[iter->first] = 0; std::set::const_iterator end2 = ddIDs.end(); for ( std::set::const_iterator dIter=ddIDs.begin(); dIter!=end2; ++dIter ) { uInt spw = dataDescIDToSpwMap[*dIter]; timeToBWMap[iter->first] += spwInfo[spw].bandwidth; } } return timeToBWMap; } void MSMetaData::_getUnflaggedRowStats( Double& nACRows, Double& nXCRows, std::shared_ptr >& subScanNACRows, std::shared_ptr >& subScanNXCRows, std::shared_ptr >& fieldNACRows, std::shared_ptr >& fieldNXCRows ) const { // This method is responsible for setting _nUnflaggedACRows, _nUnflaggedXCRows, // _unflaggedFieldNACRows, _unflaggedFieldNXCRows, _unflaggedScanNACRows, // _unflaggedScanNXCRows if (_unflaggedFieldNACRows && ! _unflaggedFieldNACRows->empty()) { nACRows = _nUnflaggedACRows; nXCRows = _nUnflaggedXCRows; fieldNACRows = _unflaggedFieldNACRows; fieldNXCRows = _unflaggedFieldNXCRows; subScanNACRows = _unflaggedSubScanNACRows; subScanNXCRows = _unflaggedSubScanNXCRows; return; } std::map *mySubScanNACRows, *mySubScanNXCRows; vector *myFieldNACRows, *myFieldNXCRows; _getUnflaggedRowStats( nACRows, nXCRows, myFieldNACRows, myFieldNXCRows, mySubScanNACRows, mySubScanNXCRows ); fieldNACRows.reset(myFieldNACRows); fieldNXCRows.reset(myFieldNXCRows); subScanNACRows.reset(mySubScanNACRows); subScanNXCRows.reset(mySubScanNXCRows); uInt mysize = 2*( sizeof(Double) + _sizeof(*fieldNACRows) + _sizeof(*subScanNACRows) ); if (_cacheUpdated(mysize)) { _nUnflaggedACRows = nACRows; _nUnflaggedXCRows = nXCRows; _unflaggedFieldNACRows = fieldNACRows; _unflaggedFieldNXCRows = fieldNXCRows; _unflaggedSubScanNACRows = subScanNACRows; _unflaggedSubScanNXCRows = subScanNXCRows; } } void MSMetaData::_getUnflaggedRowStats( Double& nACRows, Double& nXCRows, vector*& fieldNACRows, vector*& fieldNXCRows, std::map *& subScanNACRows, std::map *& subScanNXCRows ) const { nACRows = 0; nXCRows = 0; uInt myNFields = nFields(); fieldNACRows = new vector(myNFields, 0); fieldNXCRows = new vector(myNFields, 0); subScanNACRows = new std::map(); subScanNXCRows = new std::map(); std::set subScanKeys = _getSubScanKeys(); std::set::const_iterator iter = subScanKeys.begin(); std::set::const_iterator end = subScanKeys.end(); while (iter != end) { (*subScanNACRows)[*iter] = 0; (*subScanNXCRows)[*iter] = 0; ++iter; } std::shared_ptr > ant1, ant2; _getAntennas(ant1, ant2); std::shared_ptr > dataDescIDs = _getDataDescIDs(); std::shared_ptr > scans = _getScans(); std::shared_ptr > fieldIDs = _getFieldIDs(); std::shared_ptr > obsIDs = _getObservationIDs(); std::shared_ptr > arrIDs = _getArrayIDs(); Vector::const_iterator aEnd = ant1->end(); Vector::const_iterator a1Iter = ant1->begin(); Vector::const_iterator a2Iter = ant2->begin(); Vector::const_iterator sIter = scans->begin(); Vector::const_iterator fIter = fieldIDs->begin(); Vector::const_iterator oIter = obsIDs->begin(); Vector::const_iterator arIter = arrIDs->begin(); Vector::const_iterator dIter = dataDescIDs->begin(); uInt i = 0; //uInt64 count = 0; // a flag value of True means the datum is bad (flagged), so False => unflagged vector dataDescIDToSpwMap = getDataDescIDToSpwMap(); std::set a, b, c, d, e; vector spwInfo = _getSpwInfo(a, b, c, d, e); std::shared_ptr > flags = _getFlags(); while (a1Iter != aEnd) { uInt spw = dataDescIDToSpwMap[*dIter]; SpwProperties spwProp = spwInfo[spw]; Vector channelWidths( Vector(spwProp.chanwidths.getValue("Hz")) ); const Matrix& flagsMatrix(flags->get(i)); //count += flagsMatrix.size(); Double x = 0; if (! anyTrue(flagsMatrix)) { // all channels are unflagged x = 1; } else if (allTrue(flagsMatrix)) { // do nothing. All channels are flagged for this row // do not put a continue though, because counters still must // incremented below } else { // some channels are flagged, some aren't uInt nCorrelations = flagsMatrix.nrow(); Double denom = spwProp.bandwidth*nCorrelations; Double bwSum = 0; for (uInt corr=0; corr corrRow = ! flagsMatrix.row(corr); if (allTrue(corrRow)) { // all channels for this correlation are unflagged bwSum += spwProp.bandwidth; } else if (! anyTrue(corrRow)) { // do nothing, all channels for this correlation // have been flagged // but allow fall through to iterator increments } else { // some channels are flagged for this correlation, some aren't MaskedArray unFlaggedChannelWidths( channelWidths, corrRow, True ); bwSum += sum(unFlaggedChannelWidths); } } x = bwSum/denom; } SubScanKey subScanKey; subScanKey.obsID = *oIter; subScanKey.arrayID = *arIter; subScanKey.scan = *sIter; subScanKey.fieldID = *fIter; if (*a1Iter == *a2Iter) { (*fieldNACRows)[*fIter] += x; (*subScanNACRows)[subScanKey] += x; } else { (*fieldNXCRows)[*fIter]+= x; (*subScanNXCRows)[subScanKey] += x; } ++a1Iter; ++a2Iter; ++sIter; ++fIter; ++arIter; ++oIter; ++dIter; ++i; } vector::const_iterator faIter = fieldNACRows->begin(); vector::const_iterator faEnd = fieldNACRows->end(); vector::const_iterator fxIter = fieldNXCRows->begin(); while (faIter != faEnd) { nACRows += *faIter; nXCRows += *fxIter; ++faIter; ++fxIter; } } void MSMetaData::_getSpwsAndIntentsMaps( vector >& spwToIntentsMap, std::map >& intentToSpwsMap ) { if (! _spwToIntentsMap.empty() && ! _intentToSpwsMap.empty()) { spwToIntentsMap = _spwToIntentsMap; intentToSpwsMap = _intentToSpwsMap; } spwToIntentsMap.clear(); intentToSpwsMap.clear(); std::set avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw; vector spwInfo = _getSpwInfo( avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw ); std::set emptySet; vector::const_iterator end = spwInfo.end(); for ( vector::const_iterator iter=spwInfo.begin(); iter!=end; ++iter ) { spwToIntentsMap.push_back(emptySet); } vector > stateToIntentsMap; std::set uniqueIntents; _getStateToIntentsMap(stateToIntentsMap, uniqueIntents); if (uniqueIntents.size() == 0) { _spwToIntentsMap = spwToIntentsMap; _intentToSpwsMap = intentToSpwsMap; return; } std::shared_ptr > dataDescIDs = _getDataDescIDs(); Vector::const_iterator curDDID = dataDescIDs->begin(); Vector::const_iterator endDDID = dataDescIDs->end(); std::shared_ptr > states = _getStateIDs(); Vector::const_iterator curState = states->begin(); vector dataDescToSpwMap = getDataDescIDToSpwMap(); while (curDDID!=endDDID) { uInt spw = dataDescToSpwMap[*curDDID]; std::set intents = stateToIntentsMap[*curState]; std::set::const_iterator beginIntent = intents.begin(); std::set::const_iterator endIntent = intents.end(); spwToIntentsMap[spw].insert(beginIntent, endIntent); std::set::const_iterator curIntent = beginIntent; while (curIntent != endIntent) { intentToSpwsMap[*curIntent].insert(spw); ++curIntent; } ++curDDID; ++curState; } if (_cacheUpdated(_sizeof(spwToIntentsMap) + _sizeof(intentToSpwsMap))) { _spwToIntentsMap = spwToIntentsMap; _intentToSpwsMap = intentToSpwsMap; } } vector > MSMetaData::_getSpwToIntentsMap() { vector > spwToIntentsMap; std::map > intentToSpwsMap; _getSpwsAndIntentsMaps( spwToIntentsMap, intentToSpwsMap ); return spwToIntentsMap; } void MSMetaData::_getFieldsAndStatesMaps( std::map >& fieldToStatesMap, std::map >& stateToFieldsMap ) { // This method is responsible for setting _fieldToStatesMap and _stateToFieldMap. if (! _fieldToStatesMap.empty() && ! _stateToFieldsMap.empty()) { fieldToStatesMap = _fieldToStatesMap; stateToFieldsMap = _stateToFieldsMap; return; } std::shared_ptr > allStates = _getStateIDs(); std::shared_ptr > allFields = _getFieldIDs(); Vector::const_iterator endState = allStates->end(); Vector::const_iterator curField = allFields->begin(); fieldToStatesMap.clear(); stateToFieldsMap.clear(); for ( Vector::const_iterator curState=allStates->begin(); curState!=endState; ++curState, ++curField ) { fieldToStatesMap[*curField].insert(*curState); stateToFieldsMap[*curState].insert(*curField); } if ( _cacheUpdated( _sizeof(fieldToStatesMap) + _sizeof(stateToFieldsMap) ) ) { _fieldToStatesMap = fieldToStatesMap; _stateToFieldsMap = stateToFieldsMap; } } map > MSMetaData::getFieldNamesForSourceMap() const { map > idsToSource = getFieldsForSourceMap(); map >::const_iterator iter = idsToSource.begin(); map >::const_iterator end = idsToSource.end(); map > namesMap; vector names = getFieldNames(); while (iter != end) { Int sourceID = iter->first; namesMap[sourceID] = std::set(); std::set fieldIDs = idsToSource[sourceID]; std::set::const_iterator siter = fieldIDs.begin(); std::set::const_iterator send = fieldIDs.end(); while (siter != send) { namesMap[sourceID].insert(names[*siter]); ++siter; } ++iter; } return namesMap; } map > MSMetaData::getFieldsForSourceMap() const { // This method sets _sourceToFieldsMap if (! _sourceToFieldsMap.empty()) { return _sourceToFieldsMap; } String sourceIDName = MSField::columnName(MSFieldEnums::SOURCE_ID); Vector sourceIDs = ScalarColumn(_ms->field(), sourceIDName).getColumn(); map > mymap; std::set uSourceIDs(sourceIDs.begin(), sourceIDs.end()); std::set::const_iterator iter = uSourceIDs.begin(); std::set::const_iterator end = uSourceIDs.end(); while (iter != end) { mymap[*iter] = std::set(); ++iter; } Vector::const_iterator miter = sourceIDs.begin(); Vector::const_iterator mend = sourceIDs.end(); Int rowNumber = 0; while (miter != mend) { mymap[*miter].insert(rowNumber); ++miter; ++rowNumber; } uInt mysize = _sizeof(mymap); if (_cacheUpdated(mysize)) { _sourceToFieldsMap = mymap; } return mymap; } void MSMetaData::_getFieldsAndIntentsMaps( vector >& fieldToIntentsMap, std::map >& intentToFieldsMap ) { // This method is responsible for setting _intentToFieldIDMap and _fieldToIntentsMap if (getIntents().empty()) { fieldToIntentsMap = vector >(nFields()); intentToFieldsMap.clear(); return; } if (! _intentToFieldIDMap.empty() && ! _fieldToIntentsMap.empty()) { fieldToIntentsMap = _fieldToIntentsMap; intentToFieldsMap = _intentToFieldIDMap; return; } fieldToIntentsMap.resize(nFields()); vector > stateToIntentsMap; std::set uniqueIntents; _getStateToIntentsMap( stateToIntentsMap, uniqueIntents ); std::map > fieldToStatesMap; std::map > stateToFieldsMap; _getFieldsAndStatesMaps( fieldToStatesMap, stateToFieldsMap ); std::map >::const_iterator end = stateToFieldsMap.end(); for ( std::map >::const_iterator iter=stateToFieldsMap.begin(); iter!=end; ++iter ) { Int state = iter->first; std::set fields = iter->second; std::set intents = stateToIntentsMap[state]; std::set::const_iterator endField = fields.end(); for ( std::set::const_iterator curField=fields.begin(); curField!=endField; ++curField ) { fieldToIntentsMap[*curField].insert(intents.begin(), intents.end()); } std::set::const_iterator endIntent = intents.end(); for ( std::set::const_iterator curIntent=intents.begin(); curIntent!=endIntent; ++curIntent ) { intentToFieldsMap[*curIntent].insert(fields.begin(), fields.end()); } } if ( _cacheUpdated( _sizeof(fieldToIntentsMap) + _sizeof(intentToFieldsMap) ) ) { _fieldToIntentsMap = fieldToIntentsMap; _intentToFieldIDMap = intentToFieldsMap; } } std::map, uInt> MSMetaData::getSpwIDPolIDToDataDescIDMap() const { if (! _spwPolIDToDataDescIDMap.empty()) { return _spwPolIDToDataDescIDMap; } vector dataDescIDToSpwMap = getDataDescIDToSpwMap(); vector::const_iterator i1 = dataDescIDToSpwMap.begin(); vector::const_iterator end = dataDescIDToSpwMap.end(); std::map, uInt> spwPolIDToDataDescIDMap; vector dataDescIDToPolIDMap = getDataDescIDToPolIDMap(); uInt dataDesc = 0; while (i1 != end) { uInt spw = *i1; uInt polID = dataDescIDToPolIDMap[dataDesc]; spwPolIDToDataDescIDMap[std::make_pair(spw, polID)] = dataDesc; ++i1; ++dataDesc; } uInt mysize = 2*sizeof(Int)*spwPolIDToDataDescIDMap.size(); if (_cacheUpdated(mysize)) { _spwPolIDToDataDescIDMap = spwPolIDToDataDescIDMap; } return spwPolIDToDataDescIDMap; } std::pair MSMetaData::getPointingDirection( Int& antenna1, Int& antenna2, Double& time, rownr_t row, Bool interpolate, Int initialguess ) const { ThrowIf( row >= this->nRows(), "Row number exceeds number of rows in the MS" ); const String& ant1ColName = MeasurementSet::columnName(MSMainEnums::ANTENNA1); const String& ant2ColName = MeasurementSet::columnName(MSMainEnums::ANTENNA2); antenna1 = ScalarColumn(*_ms, ant1ColName).get(row); antenna2 = ScalarColumn(*_ms, ant2ColName).get(row); bool autocorr = (antenna1==antenna2); const String& timeColName = MeasurementSet::columnName(MSMainEnums::TIME); time = ScalarColumn(*_ms, timeColName).get(row); MSPointingColumns pCols(_ms->pointing()); Int pidx1, pidx2; pidx1 = pCols.pointingIndex(antenna1, time, initialguess); if (autocorr) { pidx2 = pidx1; } else pidx2 = pCols.pointingIndex(antenna2, time, initialguess); const String& intervalColName = MeasurementSet::columnName(MSMainEnums::INTERVAL); Double interval = ScalarColumn(*_ms, intervalColName).get(row); MDirection dir1, dir2; if (!interpolate || interval >= pCols.interval()(pidx1)) { dir1 = pCols.directionMeas(pidx1); } else { dir1 = _getInterpolatedDirection(pCols, pidx1, time); } if (autocorr) { dir2 = dir1; } else if (!interpolate || interval >= pCols.interval()(pidx2)) { dir2 = pCols.directionMeas(pidx2); } else { dir2 = _getInterpolatedDirection(pCols, pidx2, time); } return std::make_pair(dir1, dir2); } MDirection MSMetaData::_getInterpolatedDirection( const MSPointingColumns& pCols, const Int& index1, const Double& time ) const { Int antenna = pCols.antennaId()(index1); Double interval1 = pCols.interval()(index1); Double time1 = pCols.time()(index1); Int index2; if (time >= time1) { index2 = pCols.pointingIndex(antenna, time1+interval1,index1+1); } else { index2 = pCols.pointingIndex(antenna, time1-interval1); } if (index2 < 0 || index2==index1) { // look in opposite direction if (time >= time1) { index2 = pCols.pointingIndex(antenna, time1-interval1); } else { index2 = pCols.pointingIndex(antenna, time1+interval1,index1+1); } } ThrowIf( index2 < 0 || index2==index1, "Failed to find pointing index to interpolate direction." ); Double time2 = pCols.time()(index2); ThrowIf( time2 == time1, "Failed to find pointing index with valid timestamp to interpolate direction." ); // Interpolate (time1, time2),(dir1,dir2) Vector dirvec1 = pCols.directionMeas(index1).getAngle("rad").getValue(); Vector dirvec2 = pCols.directionMeas(index2).getAngle("rad").getValue(); MDirection::Ref rf = pCols.directionMeas(index1).getRef(); Vector newdir = dirvec1 + (dirvec2-dirvec1)*(time-time1)/(time2-time1); Quantity qlon(newdir(0), "rad"); Quantity qlat(newdir(1), "rad"); return MDirection(qlon, qlat, rf); } vector MSMetaData::getDataDescIDToSpwMap() const { if (! _dataDescIDToSpwMap.empty()) { return _dataDescIDToSpwMap; } String spwColName = MSDataDescription::columnName(MSDataDescriptionEnums::SPECTRAL_WINDOW_ID); ScalarColumn spwCol(_ms->dataDescription(), spwColName); Vector spws = spwCol.getColumn(); vector dataDescToSpwMap(spws.begin(), spws.end()); uInt mysize = sizeof(Int) * dataDescToSpwMap.size(); if (_cacheUpdated(mysize)) { _dataDescIDToSpwMap = dataDescToSpwMap; } return dataDescToSpwMap; } std::set MSMetaData::getPolarizationIDs( uInt obsID, Int arrayID, Int scan, uInt spwid ) const { ScanKey scanKey; scanKey.obsID = obsID; scanKey.arrayID = arrayID; scanKey.scan = scan; _checkScan(scanKey); if (! _scanSpwToPolIDMap.empty()) { return _scanSpwToPolIDMap.find(std::pair(scanKey, spwid))->second; } vector ddToPolMap = getDataDescIDToPolIDMap(); vector ddToSpwMap = getDataDescIDToSpwMap(); std::map > scanToDDIDMap; vector > ddIDToScanMap; _getScansAndDDIDMaps(scanToDDIDMap, ddIDToScanMap); std::map, std::set > mymap; std::map >::const_iterator iter = scanToDDIDMap.begin(); std::map >::const_iterator end = scanToDDIDMap.end(); while (iter != end) { std::set ddids = iter->second; std::set::const_iterator diter = ddids.begin(); std::set::const_iterator dend = ddids.end(); while (diter != dend) { std::pair key(iter->first, ddToSpwMap[*diter]); mymap[key].insert(ddToPolMap[*diter]); ++diter; } ++iter; } if (_cacheUpdated(_sizeof(mymap))) { _scanSpwToPolIDMap = mymap; } return mymap[std::pair(scanKey, spwid)]; } uInt MSMetaData::_sizeof(const std::map, std::set >& map) { uInt size = 0; uInt uSize = sizeof(uInt); uInt iSize = sizeof(Int); for ( std::map, std::set >::const_iterator iter=map.begin(); iter!=map.end(); ++iter ) { size += iSize + uSize*(iter->second.size() + 1); } return size; } vector MSMetaData::getDataDescIDToPolIDMap() const { if (! _dataDescIDToPolIDMap.empty()) { return _dataDescIDToPolIDMap; } String polColName = MSDataDescription::columnName(MSDataDescriptionEnums::POLARIZATION_ID); ScalarColumn polCol(_ms->dataDescription(), polColName); Vector pols = polCol.getColumn(); vector dataDescToPolIDMap(pols.begin(), pols.end()); uInt mysize = sizeof(Int) * dataDescToPolIDMap.size(); if (_cacheUpdated(mysize)) { _dataDescIDToPolIDMap = dataDescToPolIDMap; } return dataDescToPolIDMap; } vector MSMetaData::_getSpwInfo( std::set& avgSpw, std::set& tdmSpw, std::set& fdmSpw, std::set& wvrSpw, std::set& sqldSpw ) const { if (_spwInfoStored) { avgSpw = _avgSpw; tdmSpw = _tdmSpw; fdmSpw = _fdmSpw; wvrSpw = _wvrSpw; sqldSpw = _sqldSpw; return _spwInfo; } vector spwInfo = _getSpwInfo2( avgSpw, tdmSpw, fdmSpw, wvrSpw, sqldSpw ); uInt mysize = sizeof(uInt)*( avgSpw.size() + tdmSpw.size() + fdmSpw.size() + wvrSpw.size() + sqldSpw.size() ) + 2*sizeof(Int)*spwInfo.size() + 2*sizeof(Double)*spwInfo.size(); vector::const_iterator end = spwInfo.end(); for ( vector::const_iterator iter=spwInfo.begin(); iter!=end; ++iter ) { mysize += 4*(sizeof(Double)*iter->nchans + 20); mysize += sizeof(Double)*iter->edgechans.size(); } if (_cacheUpdated(mysize)) { _avgSpw = avgSpw; _tdmSpw = tdmSpw; _fdmSpw = fdmSpw; _wvrSpw = wvrSpw; _sqldSpw = sqldSpw; _spwInfo = spwInfo; _spwInfoStored = True; } return spwInfo; } void MSMetaData::_checkField(uInt fieldID) const { ThrowIf( fieldID >= nFields(), "Unknown fieldID " + String::toString(fieldID) ); } void MSMetaData::_checkScan(const ScanKey& key) const { std::set allKeys = getScanKeys(); ThrowIf( allKeys.find(key) == allKeys.end(), "Unknown scan " + toString(key) ); } void MSMetaData::_checkScans(const std::set& scanKeys) const { std::set allKeys = getScanKeys(); std::set::const_iterator iter = scanKeys.begin(); std::set::const_iterator end = scanKeys.end(); while (iter != end) { ThrowIf( allKeys.find(*iter) == allKeys.end(), "Unknown scan " + toString(*iter) ); ++iter; } } void MSMetaData::_checkSubScan(const SubScanKey& key) const { std::set allKeys = _getSubScanKeys(); ThrowIf( allKeys.find(key) == allKeys.end(), "Unknown subscan " + toString(key) ); } Bool MSMetaData::_hasFieldID(const Int fieldID) const { ThrowIf ( fieldID >= (Int)nFields(), "Requested field ID " + String::toString(fieldID) + " is greater than or equal to the number of records (" + String::toString(nFields()) + ") in this MS's FIELD table" ); std::set uniqueFields = getUniqueFieldIDs(); return uniqueFields.find(fieldID) != uniqueFields.end(); } const std::set& MSMetaData::getUniqueAntennaIDs() const { // this method is responsible for setting _uniqueAntennas if (_uniqueAntennaIDs.empty()) { if (_subScanProperties) { map::const_iterator iter = _subScanProperties->begin(); map::const_iterator end = _subScanProperties->end(); for (; iter!=end; ++iter) { const std::set& ants = iter->second.antennas; _uniqueAntennaIDs.insert(ants.begin(), ants.end()); } } else { std::shared_ptr > ant1, ant2; _getAntennas(ant1, ant2); _uniqueAntennaIDs.insert(ant1->begin(), ant1->end()); _uniqueAntennaIDs.insert(ant2->begin(), ant2->end()); } } return _uniqueAntennaIDs; } std::set MSMetaData::getUniqueDataDescIDs() const { // this method is responsible for setting _uniqueDataDescIDs if (_uniqueDataDescIDs.empty()) { if (_subScanProperties) { map::const_iterator iter = _subScanProperties->begin(); map::const_iterator end = _subScanProperties->end(); for (; iter!=end; ++iter) { const std::set& ddIDs = iter->second.ddIDs; _uniqueDataDescIDs.insert(ddIDs.begin(), ddIDs.end()); } } else { std::shared_ptr > allDDIDs = _getDataDescIDs(); _uniqueDataDescIDs.insert(allDDIDs->begin(), allDDIDs->end()); } } return _uniqueDataDescIDs; } std::set MSMetaData::getUniqueFieldIDs() const { if (_uniqueFieldIDs.empty()) { if (_subScanProperties) { map::const_iterator iter = _subScanProperties->begin(); map::const_iterator end = _subScanProperties->end(); for (; iter!=end; ++iter) { _uniqueFieldIDs.insert(iter->first.fieldID); } } else { std::shared_ptr > allFieldIDs = _getFieldIDs(); _uniqueFieldIDs.insert(allFieldIDs->begin(), allFieldIDs->end()); } } return _uniqueFieldIDs; } std::set MSMetaData::getUniqueSpwIDs() const { vector ddToSpw = getDataDescIDToSpwMap(); std::set uDDs = getUniqueDataDescIDs(); std::set uSpws; std::set::const_iterator iter = uDDs.begin(); std::set::const_iterator end = uDDs.end(); for (; iter!=end; ++iter) { uSpws.insert(ddToSpw[*iter]); } return uSpws; } Bool MSMetaData::_hasStateID(const Int stateID) const { // This method is responsible for setting _uniqueStateIDs ThrowIf( stateID >= (Int)nStates(), "Requested state ID " + String::toString(stateID) + " is greater than or equal to the number of records (" + String::toString(nStates()) + ") in this MS's STATE table" ); if (_uniqueStateIDs.empty()) { std::shared_ptr > allStateIDs = _getStateIDs(); _uniqueStateIDs.insert(allStateIDs->begin(), allStateIDs->end()); } return _uniqueStateIDs.find(stateID) != _uniqueStateIDs.end(); } void MSMetaData::_hasAntennaID(Int antennaID) { ThrowIf( antennaID >= (Int)nAntennas(), _ORIGIN + "Requested antenna ID " + String::toString(antennaID) + " is greater than or equal to the number of records (" + String::toString(nAntennas()) + ") in this MS's ANTENNA table" ); } std::shared_ptr > > MSMetaData::_getIntervals() const { ScalarQuantColumn col( *_ms, MeasurementSet::columnName(MSMainEnums::INTERVAL) ); std::shared_ptr > > intervals = col.getColumn(); return intervals; } MSMetaData::ColumnStats MSMetaData::getIntervalStatistics() const { std::shared_ptr>> intervals = _getIntervals(); Vector intInSec = intervals->getValue("s"); ColumnStats stats; ClassicalStatistics::const_iterator> cs; cs.setData(intInSec.begin(), intInSec.size()); cs.getMinMax(stats.min, stats.max); stats.median = cs.getMedian(); return stats; } std::shared_ptr> MSMetaData::_almaReceiverBands(uint nspw) const { // for ALMA specific receiver band (RB) info static const std::regex rb("RB_(\\d\\d)"); static const std::regex id("SpectralWindow_(\\d+)"); std::shared_ptr> freqBands(new vector(nspw, -1)); const File asdm_rx(_ms->tableName() + "/ASDM_RECEIVER"); Vector freqBand; if (asdm_rx.exists() && asdm_rx.isDirectory()) { auto spwid = ScalarColumn( Table(asdm_rx.path().originalName()), "spectralWindowId" ).getColumn().tovector(); if (spwid.size() <= nspw) { auto fb = ScalarColumn( Table(asdm_rx.path().originalName()), "frequencyBand" ).getColumn().tovector(); // reverse order so that if the spw is defined multiple times, // the first value for the rx band is used reverse(spwid.begin(), spwid.end()); reverse(fb.begin(), fb.end()); const auto n = fb.size(); // to get type of i auto i = n; std::smatch match; for (i=0; i> MSMetaData::_getSpwToPolMap() const { if (! _spwIDToPolIDMap.empty()) { return _spwIDToPolIDMap; } const auto spwPolToDDID = getSpwIDPolIDToDataDescIDMap(); _spwIDToPolIDMap.resize(nSpw(true)); for (const auto &p : spwPolToDDID) { uInt spwID = p.first.first; uInt polID = p.first.second; _spwIDToPolIDMap[spwID].push_back(polID); } for (auto &v : _spwIDToPolIDMap) { std::sort(v.begin(), v.end()); } return _spwIDToPolIDMap; } vector MSMetaData::_getSpwInfo2( std::set& avgSpw, std::set& tdmSpw, std::set& fdmSpw, std::set& wvrSpw, std::set& sqldSpw ) const { static const Regex rxSqld("BB_[0-9]#SQLD"); static const std::regex rb("RB_(\\d\\d)"); static const std::regex sw("SW-(\\d\\d)"); std::smatch match; MSSpWindowColumns spwCols(_ms->spectralWindow()); Vector bws = spwCols.totalBandwidth().getColumn(); ArrayQuantColumn cfCol( _ms->spectralWindow(), MSSpectralWindow::columnName(MSSpectralWindowEnums::CHAN_FREQ) ); ArrayQuantColumn cwCol( _ms->spectralWindow(), MSSpectralWindow::columnName(MSSpectralWindowEnums::CHAN_WIDTH) ); ScalarMeasColumn reffreqs( _ms->spectralWindow(), MSSpectralWindow::columnName(MSSpectralWindowEnums::REF_FREQUENCY) ); ArrayQuantColumn ebwCol( _ms->spectralWindow(), MSSpectralWindow::columnName(MSSpectralWindowEnums::EFFECTIVE_BW) ); ArrayQuantColumn resCol( _ms->spectralWindow(), MSSpectralWindow::columnName(MSSpectralWindowEnums::RESOLUTION) ); auto nss = spwCols.netSideband().getColumn(); auto name = spwCols.name().getColumn(); auto myHasBBCNo = hasBBCNo(); auto bbcno = myHasBBCNo ? spwCols.bbcNo().getColumn() : Vector(); const auto spwTableName = _ms->spectralWindowTableName(); const Table spwTable(spwTableName); Vector scb; if (spwTable.tableDesc().isColumn("SDM_CORR_BIT")) { // CAS-13749 SPECTRAL_WINDOW::SDM_CORR_BIT // is an adhoc, ALMA-specific column ScalarColumn scbCol(_ms->spectralWindow(), "SDM_CORR_BIT"); scb = scbCol.getColumn(); } vector freqLimits(2); Vector tmp; vector spwInfo(bws.size()); std::set wvrFirst, wvrSecond; const static Unit emptyUnit; const static Unit hz("Hz"); const static String wvr = "WVR"; const static String wvrNominal = "WVR#NOMINAL"; uInt nrows = bws.size(); std::shared_ptr> freqBands; for (uInt i=0; i= 15 && ! ( nchan == 256 || nchan == 128 || nchan == 64 || nchan == 32 || nchan == 16 || nchan == 248 || nchan == 124 || nchan == 62 || nchan == 31 ) ) ) { fdmSpw.insert(i); } else { const auto spwToPol = _getSpwToPolMap(); if ( spwToPol[i].size() == 1 && spwInfo[i].nchans * getNumCorrs()[*(spwToPol[i].begin())] > 256 ) { fdmSpw.insert(i); } else { tdmSpw.insert(i); } } // CAS-13973 for ALMA get subwindow and receiver band spwInfo[i].rb = -1; if (regex_search(name[i], match, rb)) { spwInfo[i].rb = stoi(match.str(1)); } else { if (! freqBands) { freqBands = _almaReceiverBands(nrows); } spwInfo[i].rb = (*freqBands)[i]; } spwInfo[i].sw = -1; if (regex_search(name[i], match, sw)) { spwInfo[i].sw = stoi(match.str(1)); } // CAS-13973 for ALMA get subwindow and receiver band spwInfo[i].rb = -1; if (regex_search(name[i], match, rb)) { spwInfo[i].rb = stoi(match.str(1)); } else { if (! freqBands) { freqBands = _almaReceiverBands(nrows); } spwInfo[i].rb = (*freqBands)[i]; } spwInfo[i].sw = -1; if (regex_search(name[i], match, sw)) { spwInfo[i].sw = stoi(match.str(1)); } } wvrSpw = wvrSecond.empty() ? wvrFirst : wvrSecond; return spwInfo; } std::map MSMetaData::_toUIntMap(const Vector& v) { ThrowIf( anyLT(v, 0), "Column that should contain nonnegative ints has a negative int" ); std::map m; Int count = 0; for (Vector::const_iterator iter=v.begin(); iter!=v.end(); ++iter, ++count) { m[count] = *iter; } return m; } std::set MSMetaData::getSubScanKeys( const ArrayKey& arrayKey ) const { std::map > mymap = _getArrayKeysToSubScanKeys(); std::map >::const_iterator iter = mymap.find(arrayKey); ThrowIf( iter == mymap.end(), "MS does not contain requested ArrayKey" ); return iter->second; } std::map > MSMetaData::_getArrayKeysToSubScanKeys() const { // this method is responsible for setting _arrayToSubScans if (! _arrayToSubScans.empty()) { return _arrayToSubScans; } std::set subScans = _getSubScanKeys(); std::set::const_iterator iter = subScans.begin(); std::set::const_iterator end = subScans.end(); std::map > mymap; ArrayKey akey; for ( ; iter != end; ++iter) { akey.arrayID = iter->arrayID; akey.obsID = iter->obsID; if (mymap.find(akey) == mymap.end()) { mymap[akey] = std::set(); } mymap[akey].insert(*iter); } uInt mysize = 0; std::map >::const_iterator miter = mymap.begin(); std::map >::const_iterator mend = mymap.end(); for ( ; miter != mend; ++miter) { mysize += sizeof(SubScanKey)*miter->second.size(); } mysize += mymap.size() * sizeof(ArrayKey); if (_cacheUpdated(mysize)) { _arrayToSubScans = mymap; } return mymap; } /* map MSMetaData::_getMeanExposureTimes() const { // this method is responsible for setting _meanExposureTimeMap if (! _meanExposureTimeMap.empty()) { return _meanExposureTimeMap; } } */ } casacore-3.7.1/ms/MSOper/MSMetaData.h000066400000000000000000001367471476623553700172230ustar00rootroot00000000000000//# MSMetaData.h //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSMETADATA_H #define MS_MSMETADATA_H #include #include #include #include #include #include #include #include #include namespace casacore { template class ArrayColumn; struct ArrayKey; struct ScanKey; struct SourceKey; struct SubScanKey; // // Class to interrogate an MS for metadata. Interrogation happens on demand // and resulting metadata are stored for use by subsequent queries if the // cache has not exceeded the specified limit. Caching of MS main table columns // has been removed because the cache can be swamped by columns for large // MSes, meaning that smaller data structures, which are more computationally // expensive to create, aren't cached. Also, the column data is usually only // needed temporarily to compute smaller data structures, and the column data // is not particularly expensive to recreate if necessary. // Parallel processing is enabled using openmp. // class MSMetaData { public: // for retrieving stats enum CorrelationType { AUTO, CROSS, BOTH }; enum SQLDSwitch { SQLD_INCLUDE, SQLD_EXCLUDE, SQLD_ONLY }; struct TimeStampProperties { std::set ddIDs; rownr_t nrows; }; struct ColumnStats { Double max; Double median; Double min; }; typedef std::map > FirstExposureTimeMap; struct SubScanProperties { // number of auto-correlation rows rownr_t acRows; // number of cross-correlation rows. rownr_t xcRows; std::set antennas; Double beginTime; std::set ddIDs; Double endTime; // the key is the spwID, the value is the meanInterval for // the subscan and that spwID std::map meanInterval; // The Int represents the data description ID, // The Double represents the time of the first time stamp, // The Quantity represents the exposure time for the corresponding // data description ID and time stamp FirstExposureTimeMap firstExposureTime; Quantity meanExposureTime; std::set spws; // number of rows for each spectral window std::map spwNRows; std::set stateIDs; std::map timeProps; }; // construct an object which stores a pointer to the MS and queries the MS // only as necessary. The MeasurementSet pointer passed in should not go out // of scope in the calling code until the caller has finished with this object, // or else subsequent method calls on this object will result in a segmentation // fault; the pointer is not copied. // maxCacheSizeMB is the maximum cache size in megabytes. <=0 means // do not use a cache, in which case, each method call will have to (re)query // the MS. It is highly recommended to use a cache of reasonable size for the // specified MS if multiple methods are going to be called. MSMetaData(const MeasurementSet *const &ms, const Float maxCacheSizeMB); virtual ~MSMetaData(); // get the antenna diameters QVD getAntennaDiameters() const; // if the antenna name appears multiple times in the antenna table, the *last* ID // that it is associated with is returned. uInt getAntennaID(const String& antennaName) const; // get all the antenna IDs for the antenna with the specified name. std::set getAntennaIDs(const String& antennaName) const; // The returned IDs are ordered in the way they appear in the atenna table vector > getAntennaIDs(const vector& antennaNames) const; // In the first instance of getAntennaNames, namesToID map will have the *last* ID // of the antenna name, if it appears multiple times in the antenna table. In the second // occurrence, namesToIDsMap will have the full set of IDs for antenna names that appear // multiple times. vector getAntennaNames( std::map& namesToIDsMap, const vector& antennaIDs=vector(0) ) const; vector getAntennaNames( std::map >& namesToIDsMap, const vector& antennaIDs=vector(0) ) const; // get the antenna stations for the specified antenna IDs vector getAntennaStations(const vector& antennaIDs=vector()); // get the antenna stations for the specified antenna names. The outer vector is ordered // respective to antennaNames. Because an antenna name can appear more than once in // the antenna table, the inner vector is ordered by row number in which that antenna name // appears. vector > getAntennaStations(const vector& antennaNames); // get the set of antenna IDs for the specified scan. std::set getAntennasForScan(const ScanKey& scan) const; // POLARIZATION.CORR_PRODUCT vector > getCorrProducts() const; // POLARIZATION.CORR_TYPE vector > getCorrTypes() const; vector getDataDescIDToSpwMap() const; vector getDataDescIDToPolIDMap() const; // Get the FIELD.SOURCE_ID column. vector getFieldTableSourceIDs() const; // get the mapping of field ID to scans vector > getFieldToScansMap() const; std::map > getIntentToFieldsMap(); std::map > getIntentToScansMap(); std::map > getIntentToSpwsMap(); std::set getIntentsForScan(const ScanKey& scan) const; std::set getIntentsForSubScan(const SubScanKey& subScan) const; std::shared_ptr > > getSubScanToIntentsMap() const; // get all intents, in no particular (nor guaranteed) order. std::set getIntents() const; // get a set of intents corresponding to a specified field std::set getIntentsForField(Int fieldID); // get a set of intents corresponding to a specified field name std::set getIntentsForField(String field); // get a set of intents corresponding to the specified spectral window std::set getIntentsForSpw(const uInt spw); // number of correlations from the polarization table. vector getNumCorrs() const; //SOURCE.PROPER_MOTION, first value in pair is longitudinal proper motion, // second is latiduninal vector > getProperMotions() const; // get unique scan numbers std::set getScanNumbers(Int obsID, Int arrayID) const; // get a set of scan numbers for the specified stateID, obsID, and arrayID. // If obsID and/or arrayID is negative, all observation IDs and/or array IDs // will be used. std::set getScansForState( Int stateID, Int obsID, Int arrayID ) const; // get the mapping of scans to states std::map > getScanToStatesMap() const; // SOURCE.DIRECTION vector getSourceDirections() const; // SOURCE.NAME vector getSourceNames() const; // Get the SOURCE.SOURCE_ID column. This is a very unfortunate column name, // because generally an "ID" column of the table with the same name refers to // the row number in that table. But not in this case. vector getSourceTableSourceIDs() const; // SOURCE.TIME std::shared_ptr > > getSourceTimes() const; // get a set of spectral windows for which the specified intent // applies. virtual std::set getSpwsForIntent(const String& intent); // get the number of visibilities rownr_t nRows() const; rownr_t nRows(CorrelationType cType); std::shared_ptr > getNRowMap(CorrelationType type) const; rownr_t nRows( CorrelationType cType, Int arrayID, Int observationID, Int scanNumber, Int fieldID ) const; rownr_t nRows(CorrelationType cType, uInt fieldID) const; // get number of spectral windows uInt nSpw(Bool includewvr) const; // number of unique states (number of rows from the STATE table) uInt nStates() const; // get the number of fields. uInt nFields() const; // get a mapping of spectral window ID to data descrption IDs std::vector > getSpwToDataDescriptionIDMap() const; // get a set of spectral windows corresponding to the specified fieldID std::set getSpwsForField(const Int fieldID) const; // get a set of spectral windows corresponding to the specified field name std::set getSpwsForField(const String& fieldName); // get the values of the CODE column from the field table vector getFieldCodes() const; // get the set of field IDs corresponding to the specified spectral window. std::set getFieldIDsForSpw(const uInt spw); // get the set of field names corresponding to the specified spectral window. std::set getFieldNamesForSpw(const uInt spw); // get the mapping of fields to spws std::map > getFieldsToSpwsMap() const; // get rest frequencies from the SOURCE table std::map > > getRestFrequencies() const; // get the set of spectral windows for the specified scan. std::set getSpwsForScan(const ScanKey& scan) const; // get the set of spectral windows for the specified subscan. std::set getSpwsForSubScan(const SubScanKey& subScan) const; // get the set of scan numbers for the specified spectral window. std::set getScansForSpw(uInt spw, Int obsID, Int arrayID) const; // get the complete mapping of scans to spws std::map > getScanToSpwsMap() const; // get the complete mapping of spws to scans std::vector > getSpwToScansMap() const; // get the transitions from the SOURCE table. If there are no transitions // for a particular key, the shared ptr contains the null ptr. std::map > > getTransitions() const; // get the number of antennas in the ANTENNA table uInt nAntennas() const; // ALMA-specific. get set of spectral windows used for TDM. These are windows that have // 64, 128, or 256 channels std::set getTDMSpw(); // ALMA-specific. get set of spectral windows used for FDM. These are windows that do not // have 1, 4, 64, 128, or 256 channels. std::set getFDMSpw(); // ALMA-specific. get spectral windows that have been averaged. These are windows with 1 channel. std::set getChannelAvgSpw(); // ALMA-specific. Get the spectral window set used for WVR measurements. These have 4 channels each. std::set getWVRSpw() const; // ALMA-specific. Get the square law detector (total power) spectral windows. std::set getSQLDSpw(); // Get the scan numbers which fail into the specified time range (center-tol to center+tol), // inclusive. A negative value of obsID and/or arrayID indicates that all observation IDs // and/or all arrayIDs should be used. std::set getScansForTimes( Double center, Double tol, Int obsID, Int arrayID ) const; // Get the times for the specified scans std::set getTimesForScans(std::set scans) const; // get the times for the specified scan. // The return values come from the TIME column. std::set getTimesForScan(const ScanKey& scan) const; std::map > getSpwToTimesForScan(const ScanKey& scan) const; // get the time range for the specified scan. The pair will contain // the start and stop time of the scan, determined from min(TIME(x)-0.5*INTERVAL(x)) and // max(TIME(x)-0.5*INTERVAL(x)) std::pair getTimeRangeForScan(const ScanKey& scanKey) const; // get the map of scans to time ranges. std::shared_ptr > > getScanToTimeRangeMap() const; // get the stateIDs associated with the specified scan. If obsID and/or arrayID // is negative, all observation IDs and/or array IDs will be used. std::set getStatesForScan(Int obsID, Int arrayID, Int scan) const; // get a map of spectral windows to unique timestamps. std::vector > getTimesForSpws(Bool showProgress=True) const; // get the position of the specified antenna relative to the observatory position. // the three vector returned represents the longitudinal, latitudinal, and elevation // offsets (elements 0, 1, and 2 respectively). The longitude and latitude offsets are // measured along the surface of a sphere centered at the earth's center and whose surface // intersects the position of the observatory. QVD getAntennaOffset(uInt which) const; // If the antenna name appears mulitple times, this will return the offset for the first // occurrence of it in the antenna table QVD getAntennaOffset(const String& name) const; // If the antenna name appears mulitple times, this will return all the offsets for it, // in the order they appear in the antenna table std::vector getAntennaOffsets(const String& name) const; vector getAntennaOffsets() const; // get the positions of the specified antennas. If which is empty, return // all antenna positions. vector getAntennaPositions( const vector& which=std::vector(0) ) const; // names cannot be empty. vector > getAntennaPositions(const vector& names); // the first key in the returned map is the spectral window ID, the second is // the average interval for the specified scan for that spw. std::map getAverageIntervalsForScan(const ScanKey& scan) const; // the first key in the returned map is the spectral window ID, the second is // the average interval for the specified sub scan for that spw. std::map getAverageIntervalsForSubScan(const SubScanKey& subScan) const; vector getBBCNos() const; vector getCorrBits() const; std::map > getBBCNosToSpwMap(SQLDSwitch sqldSwitch); vector > getEdgeChans(); //Get the phase direction for a given field id and epoch //interpolate polynomial if it is the field id is such or use ephemerides table //if that is attached to that field id MDirection phaseDirFromFieldIDAndTime(const uInt fieldID, const MEpoch& ep=MEpoch(Quantity(0.0, Unit("s")))) const ; // Get the reference direction for a given field ID and epoch interpolate // polynomial if it is the field ID is such or use ephemerides table // if that is attached to that field ID MDirection getReferenceDirection( const uInt fieldID, const MEpoch& ep=MEpoch(Quantity(0.0, Unit("s"))) ) const; // get the field IDs for the specified field name. Case insensitive. std::set getFieldIDsForField(const String& field) const; // get a list of the field names in the order in which they appear in the // FIELD table. vector getFieldNames() const; // get field IDs associated with the specified scan number. std::set getFieldsForScan(const ScanKey& scan) const; // get the field IDs associated with the specified scans std::set getFieldsForScans( const std::set& scans, Int obsID, Int arrayID ) const; // get the field IDs associated with the specified scans std::set getFieldsForScans(const std::set& scans) const; // get the field IDs associated with the specified intent. std::set getFieldsForIntent(const String& intent); // get the field IDs associated with the specified source. std::set getFieldsForIntent(uInt sourceID) const; std::map > getFieldsForSourceMap() const; std::map > getFieldNamesForSourceMap() const; // get the field names associated with the specified field IDs. If fieldIDs // is empty, a vector of all the field names is returned. vector getFieldNamesForFieldIDs(const vector& fieldIDs); // Get the fields which fail into the specified time range (center-tol to center+tol) std::set getFieldsForTimes(Double center, Double tol); // max cache size in MB Float getMaxCacheSizeMB() const { return _maxCacheMB; } // get telescope names in the order they are listed in the OBSERVATION table. These are // the telescopes (observatories), not the antenna names. vector getObservatoryNames(); // get the position of the specified telescope (observatory). MPosition getObservatoryPosition(uInt which) const; // get the phase directions from the FIELD subtable. The ep parameter // specifies for which epoch to return the directions of any ephemeris objects // in the data set. It is ignored for non-ephemeris objects. vector getPhaseDirs(const MEpoch& ep=MEpoch(Quantity(0.0, Unit("s")))) const; // get all ScanKeys in the dataset std::set getScanKeys() const; // get all ScanKeys in the dataset that have the specified arrayKey. // If negative values for either the obsID and/or arrayID portions of the ArrayKey // indicate that all obsIDs and/or arrayIDs should be used. std::set getScanKeys(const ArrayKey& arrayKey) const; // get the scans associated with the specified intent std::set getScansForIntent( const String& intent, Int obsID, Int arrayID ) const; // get the scan numbers associated with the specified field ID. std::set getScansForFieldID(Int fieldID, Int obsID, Int arrayID) const; // get the scan numbers associated with the specified field. Subclasses should not implement or override. std::set getScansForField(const String& field, Int obsID, Int arrayID) const; // The first value of the pair is spw, the second is polarization ID. std::map, uInt> getSpwIDPolIDToDataDescIDMap() const; // get a map of the spwIDs to spw names from the spw table vector getSpwNames() const; // get all the spws associated with the data description IDs listed in the main table. // This will not correspond to a list of the row numbers in the SPECTRAL_WINDOW table // if there are data description IDs that are not in the main table. std::set getSpwIDs() const; // get all sub scan keys for the specified array key. std::set getSubScanKeys(const ArrayKey& arrayKey) const; // get the sub scan properties for the specified sub scan. SubScanProperties getSubScanProperties( const SubScanKey& subScan, Bool showProgress=False ) const; std::shared_ptr > getSubScanProperties( Bool showProgress=False ) const; // If True, force the subscan properties structure to be // cached regardless of the stipulations on the maximum cache. Normally, // the subscan properties structure is small compared to the size of any // one column that is necessary to create it, and since creating this // structure can be very expensive, especially for large datasets, it // is often a good idea to cache it if it will be accessed many times. void setForceSubScanPropsToCache(Bool b) { _forceSubScanPropsToCache = b; } // get a data structure, consumable by users, representing a summary of the dataset Record getSummary() const; // get the times for which the specified field was observed std::set getTimesForField(Int fieldID); // get the time stamps associated with the specified intent std::set getTimesForIntent(const String& intent) const; Bool hasBBCNo() const; //std::map getExposuresForTimes() const; // get the unique baselines in the MS. These are not necessarily every combination of the // n(n-1)/2 possible antenna pairs, but rather the number of unique baselines represented in // the main MS table, which in theory can be less than n(n-1)/2 (for example if samples for // certain antenna pairs are not recorded. The returned Matrix is nAnts x nAnts in size. Pairs // that are true represent baselines represented in the main MS table. Matrix getUniqueBaselines(); // get the number of unique baselines represented in the main MS table which in theory can be // less than n*(n-1)/2. If includeAutoCorrelation is True, include autocorrelation // "baselines" in the enumeration. virtual uInt nBaselines(Bool includeAutoCorrelation=False); // get the effective total exposure time. This is the effective time spent collecting unflagged data. Quantity getEffectiveTotalExposureTime(); // get the number of scans in the dataset uInt nScans(); // get the number of observations (from the OBSERVATIONS table) in the dataset uInt nObservations() const; // get the contents of the OBSERVER column from the OBSERVATIONS table vector getObservers() const; // get the contents of the PROJECT column from the OBSERVATIONS table vector getProjects() const; // get the contents of the SCHEDULE column from the OBSERVATIONS table // Note that the embedded vectors may have different lengths vector > getSchedules() const; // get the time ranges from the OBSERVATION table vector > getTimeRangesOfObservations() const; // get the number of arrays (from the ARRAY table) in the dataset uInt nArrays(); // get the number of data description IDs (from the DATA_DESCRIPTION table) uInt nDataDescriptions() const; // get the number of unflagged rows Double nUnflaggedRows() const; Double nUnflaggedRows(CorrelationType cType) const; Double nUnflaggedRows( CorrelationType cType, Int arrayID, uInt observationID, Int scanNumber, uInt fieldID ) const; Double nUnflaggedRows(CorrelationType cType, Int fieldID) const; inline Float getCache() const { return _cacheMB;} vector getBandWidths() const; vector getCenterFreqs() const; // get the effective bandwidth for each channel. Each element in // the returned vector represents a separate spectral window, with // ID given by its location in the vector. If asVelWidths is True, // convert the values to velocity widths. vector getChanEffectiveBWs(Bool asVelWidths) const; vector getChanFreqs() const; // get the resolution for each channel. Each element in // the returned vector represents a separate spectral window, with // ID given by its location in the vector. If asVelWidths is True, // convert the values to velocity widths. vector getChanResolutions(Bool asVelWidths) const; vector getChanWidths() const; vector getMeanFreqs() const; vector getNetSidebands() const; vector getRefFreqs() const; vector nChans() const; uInt nPol(); // DEPRECATED // get a map of data desc ID, scan number pair to exposure time for the first time // for that data desc ID, scan number pair std::vector > getFirstExposureTimeMap(); // get map of scans to first exposure times std::map getScanToFirstExposureTimeMap(Bool showProgress) const; // get polarization IDs for the specified scan and spwid std::set getPolarizationIDs(uInt obsID, Int arrayID, Int scan, uInt spwid) const; // get the unique antennas (the union of the ANTENNA_1 and ANTENNA_2 columns) from // the main table const std::set& getUniqueAntennaIDs() const; // get unique data description IDs that exist in the main table std::set getUniqueDataDescIDs() const; // DEPRECATED because of spelling error. Use getUniqueFieldIDs() // instead. inline std::set getUniqueFiedIDs() const { return getUniqueFieldIDs(); } // get unique field IDs that exist in the main table. std::set getUniqueFieldIDs() const; // get the pointing directions associated with antenna1 and antenna2 for // the specified row of the main MS table std::pair getPointingDirection( Int& ant1, Int& ant2, Double& time, rownr_t row, Bool interpolate=false, Int initialguess=0 ) const; // get the time range for the entire dataset. min(TIME(x) - 0.5*INTERVAL(x)) to // max(TIME(x) + 0.5*INTERVAL(x)) std::pair getTimeRange(Bool showProgress=False) const; // Number of unique values from SOURCE.SOURCE_ID uInt nUniqueSourceIDsFromSourceTable() const; // get the unique spectral window IDs represented by the data description // IDs that appear in the main table std::set getUniqueSpwIDs() const; const MeasurementSet* getMS() const { return _ms; } void setShowProgress(Bool b) { _showProgress = b; } // get statistics related to the values of the INTERVAL column. Returned // values are in seconds. All values in this column are used in the computation, // including those which associated row flags may be set. ColumnStats getIntervalStatistics() const; // ALMA specific CAS-13973 get receiver bands for each spw // values of -1 indicate no info found for those spws. vector getSpwReceiverBands() const; // ALMA specific CAS-13973 get subwindows for each spw // values of -1 indicate no info found for those spws. // The SPW subwindow name is established by the ALMA Observing // Tool and is recorded in each project's scheduling blocks. // Here is an example for one spw whose subwindow name is // "SW-1", as indicated by the name field. Currently, these // values can range up to "SW-4", as a maximumn of 4 spws per // baseband are currently offered on the ALMA correlators. // // // 3.0 // 1 // SW-1 // 2.0 // 128 // 0 // true // false // true // // 6 // 115 // // vector getSpwSubwindows() const; private: struct ScanProperties { // The Int represents the data description ID, // The Double represents the time of the first time stamp, // The Quantity represents the exposure time for the corresponding // data description ID and time stamp FirstExposureTimeMap firstExposureTime; // the key is the spwID, the value is the meanInterval for // the subscan and that spwID std::map meanInterval; // number of rows for each spectral window std::map spwNRows; // time range (which takes into account helf of the corresponding // interval, which is not accounted for in the SubScanProperties times std::pair timeRange; // times for each spectral window std::map > times; }; struct SpwProperties { double bandwidth; QVD chanfreqs; QVD chanwidths; int netsideband; // The sum of all channel frequencies divided by the number of channels Quantity meanfreq; // The mean of the low frequency extent of the lowest frequency channel and // the high frequency extend of the highest frequency channel. Often, but not // necessarily, the same as meanfreq Quantity centerfreq; uInt nchans; // The center frequencies of the two channels at the edges of the window vector edgechans; uInt bbcno; // from the REF_FREQUENCY column MFrequency reffreq; String name; // EFFECTIVE_BANDWIDTH QVD effbw; // RESOLUTION QVD resolution; // CAS-13749 value for adhoc ALMA-specific SPECTRAL_WINDOW column String corrbit; // CAS-13973 ALMA specific quantities sw = subwindow, rb = receiver band int rb; int sw; }; // represents non-primary key data for a SOURCE table row struct SourceProperties { String name; std::shared_ptr > restfreq; std::shared_ptr > transition; }; // The general pattern is that a mutable gets set only once, on demand, when its // setter is called for the first time. If this pattern is broken, defective behavior // will occur. const MeasurementSet* _ms; Bool _showProgress; mutable Float _cacheMB; const Float _maxCacheMB; mutable rownr_t _nACRows, _nXCRows; mutable uInt _nStates, _nSpw, _nFields, _nAntennas, _nObservations, _nScans, _nArrays, _nrows, _nPol, _nDataDescIDs; mutable std::map > _scanToSpwsMap, _scanToDDIDsMap; mutable vector _dataDescIDToSpwMap, _dataDescIDToPolIDMap; mutable std::map > _fieldToSpwMap; mutable std::map > _scanToStatesMap, _scanToFieldsMap, _scanToAntennasMap; mutable std::map > _fieldToStatesMap, _stateToFieldsMap, _sourceToFieldsMap; mutable std::map, uInt> _spwPolIDToDataDescIDMap; mutable std::vector> _spwIDToPolIDMap; mutable std::map > _antennaNameToIDMap; mutable std::shared_ptr > _scanProperties; mutable std::shared_ptr > _subScanProperties; mutable std::map > _intentToFieldIDMap; mutable std::map > _intentToScansMap; mutable std::map > _intentToSubScansMap; mutable std::map, std::set > _scanSpwToPolIDMap; mutable std::set _uniqueIntents; mutable std::set _uniqueFieldIDs, _uniqueStateIDs, _uniqueAntennaIDs; mutable std::set _avgSpw, _tdmSpw, _fdmSpw, _wvrSpw, _sqldSpw, _uniqueDataDescIDs; mutable std::shared_ptr > _subScanToNACRowsMap, _subScanToNXCRowsMap; mutable std::shared_ptr > _fieldToNACRowsMap, _fieldToNXCRowsMap; mutable std::map > _scanToIntentsMap; mutable std::shared_ptr > > _subScanToIntentsMap; mutable vector > _stateToIntentsMap, _spwToIntentsMap, _fieldToIntentsMap; mutable vector _spwInfo; mutable vector > _spwToFieldIDsMap, _obsToArraysMap; mutable vector > _spwToScansMap, _ddidToScansMap, _fieldToScansMap; mutable vector _fieldNames, _antennaNames, _observatoryNames, _stationNames, _observers, _projects, _sourceNames, _fieldCodes; mutable vector > _schedules; mutable vector > _corrTypes; mutable vector >_corrProds; mutable std::shared_ptr > > _scanToTimesMap; std::map > _intentToSpwsMap; mutable std::map > _intentToTimesMap; std::shared_ptr > > _fieldToTimesMap; std::shared_ptr > > _timeToFieldsMap; mutable vector _observatoryPositions, _antennaPositions; mutable vector _antennaOffsets; mutable QVD _antennaDiameters; Matrix _uniqueBaselines; Quantity _exposureTime; mutable Double _nUnflaggedACRows, _nUnflaggedXCRows; mutable std::shared_ptr > _unflaggedFieldNACRows, _unflaggedFieldNXCRows; mutable std::shared_ptr > _unflaggedSubScanNACRows, _unflaggedSubScanNXCRows; const String _taqlTableName; const vector _taqlTempTable; mutable Bool _spwInfoStored, _forceSubScanPropsToCache; vector > _firstExposureTimeMap; mutable vector _numCorrs, _source_sourceIDs, _field_sourceIDs; mutable std::set _arrayKeys; mutable std::set _scanKeys; mutable std::set _subscans; mutable std::map > _scanToSubScans; mutable std::map > _arrayToSubScans; mutable vector > _timeRangesForObs; mutable vector _phaseDirs, _sourceDirs; mutable vector > _properMotions; mutable std::map _sourceInfo; mutable std::shared_ptr > _ephemFields; mutable std::shared_ptr > > _sourceTimes; // disallow copy constructor and = operator MSMetaData(const MSMetaData&); MSMetaData operator =(const MSMetaData&); // This comment from thunter in the original ValueMapping python class // # Determine the number of polarizations for the first OBSERVE_TARGET intent. // # Used by plotbandpass for BPOLY plots since the number of pols cannot be inferred // # correctly from the caltable alone. You cannot not simply use the first row, because // # it may be a pointing scan which may have different number of polarizations than what // # the TARGET and BANDPASS calibrator will have. // # -- T. Hunter // uInt _getNumberOfPolarizations(); void _setSpwInfo(const MeasurementSet& ms); // set metadata from OBSERVATION table void _setObservation(const MeasurementSet& ms); Bool _cacheUpdated(const Float incrementInBytes) const; void _checkField(uInt fieldID) const; void _checkScan(const ScanKey& key) const; void _checkScans(const std::set& scanKeys) const; void _checkSubScan(const SubScanKey& key) const; static void _checkTolerance(const Double tol); void _computeScanAndSubScanProperties( std::shared_ptr >& scanProps, std::shared_ptr >& subScanProps, Bool showProgress ) const; static void _getScalarIntColumn( Vector& v, TableProxy& table, const String& colname, rownr_t beginRow, rownr_t nrows ); static void _getScalarDoubleColumn( Vector& v, TableProxy& table, const String& colname, rownr_t beginRow, rownr_t nrows ); static void _getScalarQuantDoubleColumn( Quantum >& v, TableProxy& table, const String& colname, rownr_t beginRow, rownr_t nrows ); void _mergeScanProps( std::shared_ptr >& scanProps, std::shared_ptr >& subScanProps, const std::vector< std::pair, std::map > >& props ) const; void _createScanRecords( Record& parent, const ArrayKey& arrayKey, const std::map& subScanProps ) const; void _createSubScanRecords( Record& parent, rownr_t& scanNRows, std::set& antennasForScan, const ScanKey& scanKey, const std::map& subScanProps ) const; static void _createTimeStampRecords( Record& parent, const SubScanProperties& subScanProps ); // convert a QVD in frequency units to velocity units using // the give reference frequency. No explicit checking is done // for unit correctness of the inputs. static QVD _freqWidthToVelWidth(const QVD& v, const Quantity& refFreq); // if _scanProps has been generated, just return it. If the caller has // configured the object to generate _scanProps at some point, this call will // generate it. Otherwise, the returned object contains a null pointer. std::shared_ptr > _generateScanPropsIfWanted() const; // if _subScanProperties has been generated, just return it. If // the caller has configured the object to generate _subScanPropertiess // at some point, this call will generate it. Otherwise, the returned object // contains a null pointer. std::shared_ptr > _generateSubScanPropsIfWanted() const; vector _getAntennaNames( std::map >& namesToIDsMap ) const; vector _getAntennaPositions() const; void _getAntennas( std::shared_ptr >& ant1, std::shared_ptr >& ant2 ) const; std::shared_ptr > _getArrayIDs() const; std::map > _getArrayKeysToSubScanKeys() const; // Uses openmp for parallel processing std::pair, std::map > _getChunkSubScanProperties( const Vector& scans, const Vector& fields, const Vector& ddIDs, const Vector& states, const Vector& times, const Vector& arrays, const Vector& observations, const Vector& ant1, const Vector& ant2, const Quantum >& exposureTimes, const Quantum >& intervalTimes, const vector& ddIDToSpw, rownr_t beginRow, rownr_t endRow ) const; std::shared_ptr > _getDataDescIDs() const; // get the field IDs of ephemeris objects std::shared_ptr > _getEphemFieldIDs() const; std::shared_ptr > > _getExposureTimes() const; std::shared_ptr > _getFieldIDs() const; // If there are no intents, then fieldToIntentsMap will be of length // nFields() and all of its entries will be the empty set, and // intentToFieldsMap will be empty void _getFieldsAndIntentsMaps( vector >& fieldToIntentsMap, std::map >& intentToFieldsMap ); void _getFieldsAndScansMaps( vector >& fieldToScansMap, std::map >& scanToFieldsMap ) const; void _getFieldsAndSpwMaps( std::map >& fieldToSpwMap, vector >& spwToFieldMap ) const; void _getFieldsAndStatesMaps( std::map >& fieldToStatesMap, std::map >& stateToFieldsMap ); void _getFieldsAndTimesMaps( std::shared_ptr > >& fieldToTimesMap, std::shared_ptr > >& timesToFieldMap ); std::shared_ptr > _getFlags() const; std::map > _getIntentsToTimesMap() const; std::shared_ptr > > _getIntervals() const; std::shared_ptr > _getObservationIDs() const; std::shared_ptr > _getScans() const; vector > _getSpwToIntentsMap(); // polarization ids will be sorted in ascending order in all // member vectors std::vector> _getSpwToPolMap() const; std::shared_ptr > _getStateIDs() const; std::shared_ptr > _getTimes() const; //std::shared_ptr > _getTimeStampProperties() const; Bool _hasIntent(const String& intent) const; Bool _hasFieldID(Int fieldID) const; Bool _hasStateID(Int stateID) const; void _hasAntennaID(Int antennaID); std::map _getTimeToTotalBWMap( const Vector& times, const Vector& ddIDs ); MDirection _getInterpolatedDirection( const MSPointingColumns& pCols, const Int& index, const Double& time ) const; //map _getMeanExposureTimes() const; vector > _getObservationIDToArrayIDsMap() const; vector _getObservatoryPositions(); void _getRowStats( rownr_t& nACRows, rownr_t& nXCRows, std::map*& subScanToNACRowsMap, std::map*& subScanToNXCRowsMap, std::map*& fieldToNACRowsMap, std::map*& fieldToNXCRowsMap ) const; void _getRowStats( rownr_t& nACRows, rownr_t& nXCRows, std::shared_ptr >& scanToNACRowsMap, std::shared_ptr >& scanToNXCRowsMap, std::shared_ptr >& fieldToNACRowsMap, std::shared_ptr >& fieldToNXCRowsMap ) const; // get scan properties std::shared_ptr > _getScanProperties( Bool showProgress ) const; // get the scan keys in the specified set that have the associated arrayKey std::set _getScanKeys( const std::set& scanKeys, const ArrayKey& arrayKey ) const; // get all valid scan numbers associated with the specified arrayKey std::set _getScanNumbers(const ArrayKey& arrayKey) const; void _getScansAndDDIDMaps( std::map >& scanToDDIDMap, vector >& ddIDToScanMap ) const; void _getScansAndIntentsMaps( std::map >& scanToIntentsMap, std::map >& intentToScansMap ) const; void _getScansAndSpwMaps( std::map >& scanToSpwMap, vector >& spwToScanMap ) const; std::map > _getScanToAntennasMap() const; std::map > _getScanToSubScansMap() const; std::shared_ptr > > _getScanToTimesMap() const; std::map _getSourceInfo() const; vector _getSpwInfo( std::set& avgSpw, std::set& tdmSpw, std::set& fdmSpw, std::set& wvrSpw, std::set& sqldSpw ) const; void _getSpwsAndIntentsMaps( vector >& spwToIntentsMap, std::map >& intentToSpwsMap ); vector _getSpwInfo2( std::set& avgSpw, std::set& tdmSpw, std::set& fdmSpw, std::set& wvrSpw, std::set& sqldSpw ) const; void _getStateToIntentsMap( vector >& statesToIntentsMap, std::set& uniqueIntents ) const; vector _getStationNames(); void _getSubScansAndIntentsMaps( std::shared_ptr > >& subScanToIntentsMap, std::map >& intentToSubScansMap ) const; void _getScanAndSubScanProperties( std::shared_ptr >& scanProps, std::shared_ptr >& subScanProps, Bool showProgress ) const; std::set _getSubScanKeys() const; // get subscans related to the given scan std::set _getSubScanKeys(const ScanKey& scanKey) const; void _getUnflaggedRowStats( Double& nACRows, Double& nXCRows, std::shared_ptr >& subScanToNACRowsMap, std::shared_ptr >& subScanToNXCRowsMap, std::shared_ptr >& fieldToNACRowsMap, std::shared_ptr >& fieldToNXCRowsMap ) const; void _getUnflaggedRowStats( Double& nACRows, Double& nXCRows, vector*& fieldNACRows, vector*& fieldNXCRows, std::map*& scanNACRows, std::map*& scanNXCRows ) const; static void _modifyFirstExposureTimeIfNecessary( FirstExposureTimeMap& current, const FirstExposureTimeMap& test ); static void _modifyFirstExposureTimeIfNecessary( FirstExposureTimeMap& current, Int dataDescID, Double time, Double exposure, const Unit& eunit ); static uInt _sizeof(const std::map & m); template static uInt _sizeof(const std::map >& m); template static uInt _sizeof(const std::map >& m); template static uInt _sizeof(const std::map& m); static uInt _sizeof(const vector >& m); static uInt _sizeof(const vector& m); static uInt _sizeof(const vector >& m); template static uInt _sizeof(const vector& v); static uInt _sizeof(const Quantum >& m); template static uInt _sizeof(const vector >& v); template static uInt _sizeof(const std::map >& map); static uInt _sizeof(const vector >& map); static uInt _sizeof(const std::map, std::set >& map); static std::map _toUIntMap(const Vector& v); template std::shared_ptr > _getMainScalarColumn( MSMainEnums::PredefinedColumns col ) const; std::shared_ptr> _almaReceiverBands(uint nspw) const; }; } #endif casacore-3.7.1/ms/MSOper/MSReader.cc000066400000000000000000000440021476623553700170610ustar00rootroot00000000000000//# MSReader.cc: read from an MS, coordinating all of the subtables //# Copyright (C) 2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSReader::MSReader(const MeasurementSet &ms) : itsMS(ms), itsMSCols(ms), itsSecUnit("s"), itsIds(ms), itsMainId(-1), itsAnt1Id(-1), itsAnt2Id(-1), itsDDId(-1), itsDopplerId(-1), itsFeed1Id(-1), itsFeed2Id(-1), itsFieldId(-1), itsFlagCmdId(-1), itsFreqOffsetId(-1), itsObsId(-1), itsPointing1Id(-1), itsPointing2Id(-1), itsPolId(-1), itsProcId(-1), itsSourceId(-1), itsSpwId(-1), itsStateId(-1), itsSyscal1Id(-1), itsSyscal2Id(-1), itsWeather1Id(-1), itsWeather2Id(-1) { // assign indexes to every table to start with TableRecord kwSet(itsMS.keywordSet()); uInt idCount = 0; for (uInt i=0;i=0, AipsError); itsTabId["ANTENNA1"] = itsAnt1Id; // ANTENNA2 gets a new number itsAnt2Id = idCount++; itsTabId["ANTENNA2"] = itsAnt2Id; // FEED1 == FEED // FEED is required itsFeed1Id = itsTabId.at("FEED"); DebugAssert(itsFeed1Id>=0, AipsError); itsTabId["FEED1"] = itsFeed1Id; // FEED2 gets a new number itsFeed2Id = idCount++; itsTabId["FEED2"] = itsFeed2Id; // there are two POINTING lookups, one for each possible antenna // POINTING is a required table itsPointing1Id = itsTabId.at("POINTING"); DebugAssert(itsPointing1Id>=0, AipsError); itsTabId["POINTING1"] = itsPointing1Id; // POINTING2 gets a new number itsPointing2Id = idCount++; itsTabId["POINTING2"] = itsPointing2Id; // there are two SYSCAL lookups, one for each possible antenna // SYSCAL is an optional table itsSyscal1Id = itsTabId.at("SYSCAL"); if (itsSyscal1Id >= 0) { itsTabId["SYSCAL1"] = itsSyscal1Id; // SYSCAL2 gets a new number itsSyscal2Id = idCount++; itsTabId["SYSCAL2"] = itsSyscal2Id; } // there are two WEATHER lookups, one for each possible antenna // WEATHER is an optional table itsWeather1Id = itsTabId.at("WEATHER"); if (itsWeather1Id >= 0) { itsTabId["WEATHER1"] = itsWeather1Id; // WEATHER2 gets a new number itsWeather2Id = idCount++; itsTabId["WEATHER2"] = itsWeather2Id; } // And the MAIN table needs a presence in the various blocks itsMainId = idCount++; itsTabId["MAIN"] = itsMainId; // at this point, we know the size of the things we need == idCount Vector handledTab(idCount, False); itsIndexes.resize(idCount); itsTabRows.resize(idCount); itsRowNumbers.resize(idCount); itsTableNames.resize(idCount); itsRowNumbers = -1; // MAIN table, no index, just the table row itsTabRows[itsMainId] = ROTableRow(itsMS); handledTab(itsMainId) = True; // ANTENNA1 - no index, indexed simply via ANTENNA1 value itsTabRows[itsAnt1Id] = ROTableRow(itsMS.antenna()); handledTab(itsAnt1Id) = True; // ANTENNA2 - no index, indexed simply via ANTENNA2 value itsTabRows[itsAnt2Id] = ROTableRow(itsMS.antenna()); handledTab(itsAnt2Id) = True; // DATA_DESCRIPTION - no index, indexed simply via DATA_DESC_ID value // This table is required. itsDDId = itsTabId.at("DATA_DESCRIPTION"); DebugAssert(itsDDId>=0, AipsError); itsTabRows[itsDDId] = ROTableRow(itsMS.dataDescription()); handledTab(itsDDId) = True; // DOPPLER - has a special index. This table is OPTIONAL itsDopplerId = itsTabId.at("DOPPLER"); if (itsDopplerId >= 0) { itsDopplerIndex.attach(itsMS.doppler()); itsTabRows[itsDopplerId] = ROTableRow(itsMS.doppler()); handledTab(itsDopplerId) = True; } // FEED1 - indexed itsFeed1Index.attach(itsMS.feed()); itsTabRows[itsFeed1Id] = ROTableRow(itsMS.feed()); handledTab(itsFeed1Id) = True; // FEED2 - indexed itsFeed2Index.attach(itsMS.feed()); itsTabRows[itsFeed2Id] = ROTableRow(itsMS.feed()); handledTab(itsFeed2Id) = True; // FIELD - no index, indexed simply via FIELD_ID value // This table is required. itsFieldId = itsTabId.at("FIELD"); DebugAssert(itsFieldId>=0, AipsError); itsTabRows[itsFieldId] = ROTableRow(itsMS.field()); handledTab(itsFieldId) = True; // FLAG_CMD, simple time and interval MSTableIndex // This table is required itsFlagCmdId = itsTabId.at("FLAG_CMD"); DebugAssert(itsFlagCmdId>=0, AipsError); itsIndexes[itsFlagCmdId] = MSTableIndex(itsMS.flagCmd(),Vector()); itsTabRows[itsFlagCmdId] = ROTableRow(itsMS.flagCmd()); handledTab(itsFlagCmdId) = True; // FREQ_OFFSET - indexed // This table is optional itsFreqOffsetId = itsTabId.at("FREQ_OFFSET"); if (itsFreqOffsetId >= 0) { itsFreqOffIndex.attach(itsMS.freqOffset()); itsTabRows[itsFreqOffsetId] = ROTableRow(itsMS.freqOffset()); handledTab(itsFreqOffsetId) = True; } // HISTORY - not handled here, this is a required table // make sure its marked as undefined in the ID map and mark it as handled here Int histId = itsTabId.at("HISTORY"); if (histId >= 0) { itsTabId["HISTORY"] = -1; handledTab(histId) = True; } // OBSERVATION - not indexed, this is a required table itsObsId = itsTabId.at("OBSERVATION"); DebugAssert(itsObsId>=0, AipsError); itsTabRows[itsObsId] = ROTableRow(itsMS.observation()); handledTab(itsObsId) = True; // POINTING1 and POINTING2 - indexed, this is a required table // Allready have itsPointing1Id and itsPointing2Id itsPointing1Index.attach(itsMS.pointing()); itsTabRows[itsPointing1Id] = ROTableRow(itsMS.pointing()); handledTab(itsPointing1Id) = True; itsPointing2Index.attach(itsMS.pointing()); itsTabRows[itsPointing2Id] = ROTableRow(itsMS.pointing()); handledTab(itsPointing2Id) = True; // POLARIZATION - not indexed, this is a required table itsPolId = itsTabId.at("POLARIZATION"); DebugAssert(itsPolId>=0, AipsError); itsTabRows[itsPolId] = ROTableRow(itsMS.polarization()); handledTab(itsPolId) = True; // PROCESSOR - not indexed, this is a required table itsProcId = itsTabId.at("PROCESSOR"); DebugAssert(itsProcId>=0, AipsError); itsTabRows[itsProcId] = ROTableRow(itsMS.processor()); handledTab(itsProcId) = True; // SOURCE - indexed, this is an optional table itsSourceId = itsTabId.at("SOURCE"); if (itsSourceId >= 0) { itsSourceIndex.attach(itsMS.source()); itsTabRows[itsSourceId] = ROTableRow(itsMS.source()); handledTab(itsSourceId) = True; } // SPECTRAL_WINDOW - not indexed, this is a required table itsSpwId = itsTabId.at("SPECTRAL_WINDOW"); DebugAssert(itsSpwId>=0, AipsError); itsTabRows[itsSpwId] = ROTableRow(itsMS.spectralWindow()); handledTab(itsSpwId) = True; // STATE - not indexed, this is an optional table itsStateId = itsTabId.at("STATE"); if (itsStateId >= 0) { itsTabRows[itsStateId] = ROTableRow(itsMS.state()); handledTab(itsStateId) = True; } // SYSCAL1 and SYSCAL2 - indexed, this is an optional table // already know itsSyscal1Id and itsSyscal2Id if (itsSyscal1Id >= 0) { itsSyscal1Index.attach(itsMS.sysCal()); itsTabRows[itsSyscal1Id] = ROTableRow(itsMS.sysCal()); handledTab(itsSyscal1Id) = True; // SYSCAL2 must exist if SYSCAL1 exists itsSyscal2Index.attach(itsMS.sysCal()); itsTabRows[itsSyscal2Id] = ROTableRow(itsMS.sysCal()); handledTab(itsSyscal2Id) = True; } // WEATHER1 and WEATHER2 - indexed, this is an optional table // already know itsWeather1Id and itsWeather2Id if (itsWeather1Id >= 0) { itsWeather1Index.attach(itsMS.weather()); itsTabRows[itsWeather1Id] = ROTableRow(itsMS.weather()); handledTab(itsWeather1Id) = True; // WEATHER2 must exist if WEATHER1 exists itsWeather2Index.attach(itsMS.weather()); itsTabRows[itsWeather2Id] = ROTableRow(itsMS.weather()); handledTab(itsWeather2Id) = True; } // and now, for everything not handled above, also fill in itsTableNames Vector tableNames(idCount); for (const auto& x : itsTabId) { Int tabId = x.second; if (tabId >= 0) { String tabName = x.first; tableNames(tabId) = tabName; if (!handledTab(tabId)) { itsIndexes[tabId].attach(kwSet.asTable(tabName), Vector()); itsTabRows[tabId] = ROTableRow(kwSet.asTable(tabName)); handledTab(tabId) = True; } } } // copy the non-empty values in tableNames to itsTableNames uInt nameCount = 0; for (uInt i=0;i 0) { itsTableNames(nameCount++) = tableNames(i); } } itsTableNames.resize(nameCount, True); } void MSReader::gotoRow(rownr_t which) { // give up if this isn't a valid row. Perhaps this should do something more // obnoxious, like make this a boolean fn and return False? if (which >= itsMS.nrow()) return; // don't do anything if which is the same as the previous call. // This will have problems is the MS has been written to in the meantime. if (itsRowNumbers[itsMainId] >= 0 && itsRowNumbers[itsMainId] == Int64(which)) return; itsRowNumbers = -1; itsTabRows[itsMainId].get(which); itsRowNumbers[itsMainId] = which; // simple indexes first Int ant1Id = itsIds.antenna1(which); if (ant1Id >= 0) { itsTabRows[itsAnt1Id].get(ant1Id); itsRowNumbers[itsAnt1Id] = ant1Id; } Int ant2Id = itsIds.antenna1(which); if (ant2Id >= 0) { itsTabRows[itsAnt2Id].get(ant2Id); itsRowNumbers[itsAnt2Id] = ant2Id; } Int ddId = itsIds.dataDescId(which); if (ddId >= 0) { itsTabRows[itsDDId].get(ddId); itsRowNumbers[itsDDId] = ddId; } Int obsId = itsIds.observationId(which); if (obsId >= 0) { itsTabRows[itsObsId].get(obsId); itsRowNumbers[itsObsId] = obsId; } Int polId = itsIds.polarizationId(which); if (polId >= 0) { itsTabRows[itsPolId].get(polId); itsRowNumbers[itsPolId] = polId; } Int spwId = itsIds.spectralWindowId(which); if (spwId >= 0) { itsTabRows[itsSpwId].get(spwId); itsRowNumbers[itsSpwId] = spwId; } Int fieldId = itsIds.fieldId(which); if (fieldId >= 0) { itsTabRows[itsFieldId].get(fieldId); itsRowNumbers[itsFieldId] = fieldId; } Int procId = itsIds.processorId(which); if (procId >= 0) { itsTabRows[itsProcId].get(procId); itsRowNumbers[itsProcId] = procId; } Int stateId = itsIds.stateId(which); if (stateId >= 0) { itsTabRows[itsStateId].get(stateId); itsRowNumbers[itsStateId] = stateId; } // and the ones with specific indexes // these all need the time and interval const MEpoch time = itsMSCols.timeMeas()(which); const Quantity interval = itsMSCols.intervalQuant()(which); Double stime = time.getValue().getTime().getValue(itsSecUnit); Double sint = interval.getValue(itsSecUnit); // DOPPLER - optional Bool found; if (itsDopplerId >= 0) { itsDopplerIndex.dopplerId() = itsIds.dopplerId(which); itsDopplerIndex.sourceId() = itsIds.sourceId(which); // doppler does not use time or interval as keys Int64 dopRow = itsDopplerIndex.getNearestRow(found); if (found) { itsTabRows[itsDopplerId].get(dopRow); itsRowNumbers[itsDopplerId] = dopRow; } } // FEED1, with ANTENNA1 itsFeed1Index.antennaId() = ant1Id; Int feed1 = itsMSCols.feed1()(which); Int feed2 = itsMSCols.feed2()(which); itsFeed1Index.feedId() = feed1; itsFeed1Index.spectralWindowId() = spwId; itsFeed1Index.time() = stime; itsFeed1Index.interval() = sint; Int64 feedRow = itsFeed1Index.getNearestRow(found); if (found) { itsTabRows[itsFeed1Id].get(feedRow); itsRowNumbers[itsFeed1Id] = feedRow; } // can this just be reused if (ant1Id != ant2Id || feed1 != feed2) { itsFeed2Index.antennaId() = ant2Id; itsFeed2Index.feedId() = feed2; itsFeed2Index.spectralWindowId() = spwId; itsFeed2Index.time() = stime; itsFeed2Index.interval() = sint; feedRow = itsFeed2Index.getNearestRow(found); } if (found) { itsTabRows[itsFeed2Id].get(feedRow); itsRowNumbers[itsFeed2Id] = feedRow; } // FLAG_CMD - handled generically in the itsIndexes block // FREQ_OFFSET - optional if (itsFreqOffsetId >= 0) { itsFreqOffIndex.antenna1Id() = ant1Id; itsFreqOffIndex.antenna2Id() = ant2Id; // I don't really understand why there is only one FEED_ID here itsFreqOffIndex.feedId() = feed1; itsFreqOffIndex.time() = stime; itsFreqOffIndex.interval() = sint; Int64 foffRow = itsFreqOffIndex.getNearestRow(found); if (found) { itsTabRows[itsFreqOffsetId].get(foffRow); itsRowNumbers[itsFreqOffsetId] = foffRow; } } // POINTING1 - required, this depends on ANTENNA1 itsPointing1Index.antennaId() = ant1Id; itsPointing1Index.time() = stime; itsPointing1Index.interval() = sint; Int64 pointRow = itsPointing1Index.getNearestRow(found); if (found) { itsTabRows[itsPointing1Id].get(pointRow); itsRowNumbers[itsPointing1Id] = pointRow; } // can we reuse this for POINTING2? if (ant1Id != ant2Id) { itsPointing2Index.antennaId() = ant2Id; itsPointing2Index.time() = stime; itsPointing2Index.interval() = sint; pointRow = itsPointing2Index.getNearestRow(found); } if (found) { itsTabRows[itsPointing2Id].get(pointRow); itsRowNumbers[itsPointing2Id] = pointRow; } // SOURCE - optional if (itsSourceId >= 0) { itsSourceIndex.sourceId() = itsIds.sourceId(which); itsSourceIndex.spectralWindowId() = itsIds.spectralWindowId(which); itsSourceIndex.time() = stime; itsSourceIndex.interval() = sint; Int64 sourceRow = itsSourceIndex.getNearestRow(found); if (found) { itsTabRows[itsSourceId].get(sourceRow); itsRowNumbers[itsSourceId] = sourceRow; } } // SYSCAL1 - optional if (itsSyscal1Id >= 0) { itsSyscal1Index.antennaId() = ant1Id; itsSyscal1Index.feedId() = feed1; itsSyscal1Index.spectralWindowId() = spwId; itsSyscal1Index.time() = stime; itsSyscal1Index.interval() = sint; Int64 syscalRow = itsSyscal1Index.getNearestRow(found); if (found) { itsTabRows[itsSyscal1Id].get(syscalRow); itsRowNumbers[itsSyscal1Id] = syscalRow; } // if SYSCAL1 exists, SYSCAL2 must exist and SYSCAL2 can't // exist unless SYSCAL1 exists // Can we re-use that row if (ant1Id != ant2Id || feed1 != feed2) { itsSyscal2Index.antennaId() = ant2Id; itsSyscal2Index.feedId() = feed2; itsSyscal2Index.spectralWindowId() = spwId; itsSyscal2Index.time() = stime; itsSyscal2Index.interval() = sint; syscalRow = itsSyscal2Index.getNearestRow(found); } if (found) { itsTabRows[itsSyscal2Id].get(syscalRow); itsRowNumbers[itsSyscal2Id] = syscalRow; } } // WEATHER1- optional if (itsWeather1Id >= 0) { itsWeather1Index.antennaId() = ant1Id; itsWeather1Index.time() = stime; itsWeather1Index.interval() = sint; Int64 weatherRow = itsWeather1Index.getNearestRow(found); if (found) { itsTabRows[itsWeather1Id].get(weatherRow); itsRowNumbers[itsWeather1Id] = weatherRow; } // if WEATHER1 exists, WEATHER2 must exist and WEATHER2 can't // exist unless WEATHER1 exists // Can we re-use that row if (ant1Id != ant2Id) { itsWeather2Index.antennaId() = ant2Id; itsWeather2Index.time() = stime; itsWeather2Index.interval() = sint; weatherRow = itsWeather2Index.getNearestRow(found); } if (found) { itsTabRows[itsWeather2Id].get(weatherRow); itsRowNumbers[itsWeather2Id] = weatherRow; } } // any think in itsIndexes for (uInt i=0;i #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Read from an MS, coordinating all of the subtables in the process // // // // class MSReader { public: // Attach to the indicated MeasurementSet MSReader(const MeasurementSet &ms); ~MSReader() {;} // Go to the indicated row in the MAIN table of the MS and point // at all of the appropriate rows in each of the subtables as // a result of going to this row. void gotoRow(rownr_t which); const Vector &tables() const {return itsTableNames;} // Return the current row in the named table. Use rowNumber to // check to see that the most recent gotoRow actually found a matching // row. const RecordInterface &tableRow(const String &name) const; // Return the current row number in the named table. This returns // -1 if that table has no row as a result of the most recent gotoRow. Int64 rowNumber(const String &name) const; // Return a reference to the MS const MeasurementSet &ms() const {return itsMS;} // Return a reference to the named subtable const Table &table(const String &name) const; // this isn't what we need, right now just return an empty record const Record &units(const String &) const { return emptyRecord;} private: MeasurementSet itsMS; MSColumns itsMSCols; // This possibly saves some time, Units of seconds Unit itsSecUnit; MSValidIds itsIds; // this maps table name to an index used throughout this class std::map itsTabId; // the indexes for the NS subtables Block itsIndexes; // specific indexes MSDopplerIndex itsDopplerIndex; MSFeedIndex itsFeed1Index; MSFeedIndex itsFeed2Index; MSFreqOffIndex itsFreqOffIndex; MSPointingIndex itsPointing1Index; MSPointingIndex itsPointing2Index; MSSourceIndex itsSourceIndex; MSSysCalIndex itsSyscal1Index; MSSysCalIndex itsSyscal2Index; MSWeatherIndex itsWeather1Index; MSWeatherIndex itsWeather2Index; // table IDs for the standard tables Int itsMainId, itsAnt1Id, itsAnt2Id, itsDDId, itsDopplerId, itsFeed1Id, itsFeed2Id, itsFieldId, itsFlagCmdId, itsFreqOffsetId, itsObsId, itsPointing1Id, itsPointing2Id, itsPolId, itsProcId, itsSourceId, itsSpwId, itsStateId, itsSyscal1Id, itsSyscal2Id, itsWeather1Id, itsWeather2Id; // the table rows Block itsTabRows; // What row number for each table is the most recent gotoRow call. Set to // -1 if there was no matching row as a result of that call. Block itsRowNumbers; // this empty record is returned by tableRow when the name argument does not exist Record emptyRecord; // this empty table is returned by table when the name argument does not exist Table emptyTable; Vector itsTableNames; // undefined and unavailable MSReader(); MSReader(const MSReader &); MSReader& operator=(const MSReader &); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSOper/MSSummary.cc000066400000000000000000002046701476623553700173250ustar00rootroot00000000000000//# MSSummary.cc: Helper class for applications listing a MeasurementSet //# Copyright (C) 1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Constructor assigns pointer. If MS goes out of scope you // will get rubbish. Also sets string to separate subtable output. // MSSummary::MSSummary (const MeasurementSet& ms, Float maxCacheMB) : pMS(&ms), _msmd(new MSMetaData(&ms, maxCacheMB)), dashlin1(replicate("-",80)), dashlin2(replicate("=",80)), _listUnflaggedRowCount(False), _cacheSizeMB(maxCacheMB) {} MSSummary::MSSummary (const MeasurementSet* ms, Float maxCacheMB) : pMS(ms), _msmd(new MSMetaData(ms, maxCacheMB)), dashlin1(replicate("-",80)), dashlin2(replicate("=",80)), _listUnflaggedRowCount(False), _cacheSizeMB(maxCacheMB) {} MSSummary::MSSummary (const MeasurementSet* ms, const String msname, Float maxCacheMB) : pMS(ms), _msmd(new MSMetaData(ms, maxCacheMB)), dashlin1(replicate("-",80)), dashlin2(replicate("=",80)), msname_p(msname), _listUnflaggedRowCount(False), _cacheSizeMB(maxCacheMB) {} MSSummary::MSSummary (std::shared_ptr msmd) : pMS(msmd->getMS()), _msmd(msmd), dashlin1(replicate("-",80)), dashlin2(replicate("=",80)), _listUnflaggedRowCount(False), _cacheSizeMB(msmd->getMaxCacheSizeMB()) { } // // Destructor does nothing // MSSummary::~MSSummary () {} // // Retrieve number of rows // Int64 MSSummary::nrow () const { return _msmd->nRows(); } // // Get ms name // String MSSummary::name () const { if (! msname_p.empty()) return msname_p; return pMS->tableName(); } // // Reassign pointer. // Bool MSSummary::setMS (const MeasurementSet& ms, Float maxCacheMB) { const MeasurementSet* pTemp; pTemp = &ms; if (pTemp == 0) { return False; } else { pMS = pTemp; Float cache = maxCacheMB < 0 ? _cacheSizeMB : maxCacheMB; _msmd = std::make_shared(&ms, cache); return True; } } // // List information about an ms to the logger // void MSSummary::list (LogIO& os, Bool verbose, Bool oneBased) const { Record dummy; list(os, dummy, verbose, False, oneBased); } void MSSummary::list (LogIO& os, Record& outRec, Bool verbose, Bool fillRecord, Bool oneBased) const { // List a title for the Summary listTitle (os); // List the main table as well as the subtables in a useful order and format listWhere (os,verbose); listWhat (os,outRec, verbose, fillRecord); listHow (os,verbose, oneBased); // These aren't really useful (yet?) // listSource (os,verbose); // listSysCal (os,verbose); // listWeather (os,verbose); // List a summary of table sizes // os << dashlin1 << endl << endl; // listTables (os,verbose); // os << dashlin2 << endl; // Post it os.post(); } // // List a title for the Summary // void MSSummary::listTitle (LogIO& os) const { // Version number of the MS definition Float vers = 1.0; if (pMS->keywordSet().isDefined("MS_VERSION")) { vers = pMS->keywordSet().asFloat("MS_VERSION"); } // List the MS name and version as the title of the Summary os << LogIO::NORMAL; os << dashlin2 << endl << " MeasurementSet Name: " << this->name() << " MS Version " << vers << endl << dashlin2 << endl; } // // Convenient table groupings // void MSSummary::listWhere (LogIO& os, Bool verbose) const { listObservation (os,verbose); } void MSSummary::listWhat (LogIO& os, Bool verbose) const { Record dummy; listWhat(os, dummy, verbose, False); } void MSSummary::listWhat (LogIO& os, Record& outRec, Bool verbose, Bool fillRecord) const { listMain (os,outRec, verbose, fillRecord); listField (os,outRec, verbose, fillRecord); } void MSSummary::listHow (LogIO& os, Bool verbose, Bool oneBased) const { // listSpectralWindow (os,verbose); // listPolarization (os,verbose); listSpectralAndPolInfo(os, verbose, oneBased); listSource (os,verbose); // listFeed (os,verbose, oneBased)); listAntenna (os,verbose); } // // SUBTABLES // void MSSummary::listMain (LogIO& os, Bool verbose) const { Record dummy; listMain(os, dummy, verbose, False); } // TESTING CAS-2751 void MSSummary::listMain (LogIO& os, Record& outRec, Bool verbose, Bool fillRecord) const { if (nrow()<=0) { os << "The MAIN table is empty: there are no data!!!" << endl << LogIO::POST; return; } _msmd->setForceSubScanPropsToCache(True); std::pair timerange = _msmd->getTimeRange(True); Double startTime = timerange.first; Double stopTime = timerange.second; Double exposTime = stopTime - startTime; // Double exposTime = sum(msc.exposure().getColumn()); MVTime startMVT(startTime/86400.0), stopMVT(stopTime/86400.0); MSMainColumns msmc(*pMS); String timeref=msmc.time().keywordSet().subRecord("MEASINFO").asString("Ref"); // Output info os << "Data records: " << nrow() << " Total elapsed time = " << exposTime << " seconds" << endl << " Observed from " << MVTime(startTime/C::day).string(MVTime::DMY,7) //startMVT.string() << " to " << MVTime(stopTime/C::day).string(MVTime::DMY,7) // stopMVT.string() << " (" << timeref << ")" << endl << endl; os << LogIO::POST; if(fillRecord){ outRec.define("numrecords", nrow()); outRec.define("IntegrationTime", exposTime); outRec.define("BeginTime", startTime/C::day); outRec.define("EndTime", stopTime/C::day); outRec.define("timeref", timeref); } // the selected MS (all of it) as a Table tool: // MS is accessed as a generic table here because // the ms tool hard-wires iteration over SPWID, FLDID, // and TIME and this is not desired in this application. // It would be easier if the ms tool did not automatically // iterate over any indices, or if there were an ms.table() // function. MSSelector mssel; mssel.setMS(const_cast(*pMS)); mssel.initSelection(True); Table mstab(mssel.selectedTable()); // Field names: MSFieldColumns field(pMS->field()); Vector fieldnames(field.name().getColumn()); nVisPerField_.resize(fieldnames.nelements()); nVisPerField_=0; // Observing Mode (State Table) MSStateColumns state(pMS->state()); Vector obsModes(state.obsMode().getColumn()); // Spw Ids MSDataDescColumns dd(pMS->dataDescription()); Vector specwindids(dd.spectralWindowId().getColumn()); // Field widths for printing: Int widthLead = 2; Int widthScan = 4; Int widthbtime = 22; Int widthetime = 10; Int widthFieldId = 5; Int widthField = 20; Int widthnrow = 10; Int widthNUnflaggedRow = 13; //Int widthInttim = 7; // Set up iteration over OBSID and ARRID: Block icols(2); icols[0] = "OBSERVATION_ID"; icols[1] = "ARRAY_ID"; //TableIterator obsarriter(mstab,icols); //Limiting record length Int recLength=0; const Int maxRecLength=10000; //limiting for speed and size sake std::set allArrayKeys = uniqueArrayKeys(_msmd->getScanKeys()); std::set::const_iterator iter = allArrayKeys.begin(); std::set::const_iterator end = allArrayKeys.end(); std::shared_ptr > > scanToTRMap = _msmd->getScanToTimeRangeMap(); std::shared_ptr > ssprops = _msmd->getSubScanProperties(True); std::shared_ptr > > ssToIntents = _msmd->getSubScanToIntentsMap(); std::shared_ptr > nrowMap = _msmd->getNRowMap(MSMetaData::BOTH); for (; iter != end; ++iter) { Int obsid = iter->obsID; Int arrid = iter->arrayID; if (verbose) { // Report OBSID and ARRID, and header for listing: os << endl << " ObservationID = " << obsid; os << " ArrayID = " << arrid << endl; String datetime=" Date Timerange "; datetime.replace(24,1,"("); datetime.replace(25,timeref.length(),timeref); datetime.replace(25+timeref.length(),1,")"); os << datetime; os << "Scan FldId FieldName " << " nRows "; if (_listUnflaggedRowCount) { os << "nUnflRows "; } os << "SpwIds Average Interval(s) ScanIntent" << endl; } std::set subScans = _msmd->getSubScanKeys(*iter); os.output().precision(3); Double lastday = 0; std::set::const_iterator siter = subScans.begin(); std::set::const_iterator send = subScans.end(); uInt subsetscan = 0; Int lastscan = 0; for (; siter != send; ++siter) { const MSMetaData::SubScanProperties& props = ssprops->find(*siter)->second; Int64 nrow = props.acRows + props.xcRows; Int thisscan = siter->scan; std::set ddIDs = props.ddIDs; std::set stateIDs = props.stateIDs; ScanKey scan; scan.arrayID = siter->arrayID; scan.obsID = siter->obsID; scan.scan = siter->scan; const std::pair& timerange = scanToTRMap->find(scan)->second; Double btime = timerange.first; Double etime = timerange.second; Double day=floor(MVTime(btime/C::day).day()); const std::set& spw = props.spws; String name=fieldnames(siter->fieldID); if (verbose) { os.output().setf(ios::right, ios::adjustfield); os.output().width(widthLead); os << " "; os.output().width(widthbtime); if (day!=lastday) { // print date os << MVTime(btime/C::day).string(MVTime::DMY,7); } else { // omit date os << MVTime(btime/C::day).string(MVTime::TIME,7); } os.output().width(3); os << " - "; os.output().width(widthetime); os << MVTime(etime/C::day).string(MVTime::TIME,7); os.output().width(widthLead); os << " "; os.output().setf(ios::right, ios::adjustfield); os.output().width(widthScan); os << siter->scan; os.output().width(widthLead); os << " "; os.output().setf(ios::right, ios::adjustfield); os.output().width(widthFieldId); os << siter->fieldID << " "; os.output().setf(ios::left, ios::adjustfield); if (name.length()>20) { name.replace(19,1,'*'); } os.output().width(widthField); os << name.at(0,20); os.output().width(widthnrow); os.output().setf(ios::right, ios::adjustfield); os << nrowMap->find(*siter)->second; if (_listUnflaggedRowCount) { ostringstream xx; xx << std::fixed << setprecision(2) << _msmd->nUnflaggedRows( MSMetaData::BOTH, arrid, obsid, siter->scan, siter->fieldID ); os.output().width(widthNUnflaggedRow); os << xx.str(); } os.output().width(widthLead); os << " "; os << spw; os.output().width(widthLead); os << " "; const std::map& intToScanMap = ssprops->find(*siter)->second.meanInterval; os << "["; for ( std::set::const_iterator spwiter=spw.begin(); spwiter!=spw.end(); ++spwiter ) { if (spwiter!=spw.begin()) { os << ", "; } os << intToScanMap.find(*spwiter)->second.getValue("s"); } os << "] "; const std::set& intents = ssToIntents->find(*siter)->second; if (! intents.empty()) { os << intents; } os << endl; } if(fillRecord && (recLength < maxRecLength)) { if(lastscan == thisscan && siter != subScans.begin()) { ++subsetscan; } else { subsetscan=0; } Record scanRecord; Record subScanRecord; String scanrecid = String("scan_")+String::toString(siter->scan); if (outRec.isDefined(scanrecid)){ scanRecord = outRec.asrwRecord(scanrecid); outRec.removeField(scanrecid); } subScanRecord.define("BeginTime", btime/C::day); subScanRecord.define("EndTime", etime/C::day); subScanRecord.define("scanId", siter->scan); subScanRecord.define("FieldId", siter->fieldID); subScanRecord.define("FieldName", name); subScanRecord.define("StateId", *stateIDs.begin()); subScanRecord.define("nRow", nrow); subScanRecord.define("IntegrationTime", props.meanExposureTime.getValue("s")); subScanRecord.define("SpwIds", Vector(spw.begin(), spw.size(), 0)); scanRecord.defineRecord(String::toString(subsetscan), subScanRecord); if(!outRec.isDefined(scanrecid)){ outRec.defineRecord(scanrecid, scanRecord); } } // next last day is this day lastday =day; ++recLength; lastscan = thisscan; } if (verbose) { os << LogIO::POST; } } if (verbose){ os << " (nRows = Total number of rows per scan) " << endl; os << LogIO::POST; } os << LogIO::POST; } void MSSummary::getScanSummary (Record& outRec) const { if (nrow()<=0) { return; } // Make objects MSColumns msc(*pMS); Double startTime, stopTime; minMax(startTime, stopTime, msc.time().getColumn()); MVTime startMVT(startTime/86400.0), stopMVT(stopTime/86400.0); MSMainColumns msmc(*pMS); String timeref=msmc.time().keywordSet().subRecord("MEASINFO").asString("Ref"); //outRec.define("numrecords", nrow()); //outRec.define("IntegrationTime", exposTime); //outRec.define("BeginTime", startTime/C::day); //outRec.define("EndTime", stopTime/C::day); //outRec.define("timeref", timeref); // the selected MS (all of it) as a Table tool: // MS is accessed as a generic table here because // the ms tool hard-wires iteration over SPWID, FLDID, // and TIME and this is not desired in this application. // It would be easier if the ms tool did not automatically // iterate over any indices, or if there were an ms.table() // function. MSSelector mssel; mssel.setMS(const_cast(*pMS)); mssel.initSelection(True); Table mstab(mssel.selectedTable()); // Field names: MSFieldColumns field(pMS->field()); Vector fieldnames(field.name().getColumn()); nVisPerField_.resize(fieldnames.nelements()); nVisPerField_=0; // Spw Ids MSDataDescColumns dd(pMS->dataDescription()); Vector specwindids(dd.spectralWindowId().getColumn()); // Set up iteration over OBSID and ARRID: Block icols(2); icols[0] = "OBSERVATION_ID"; icols[1] = "ARRAY_ID"; TableIterator obsarriter(mstab,icols); //Limiting record length Int recLength=0; // Iterate: while (!obsarriter.pastEnd()) { // Table containing this iteration: Table obsarrtab(obsarriter.table()); // Extract (zero-based) OBSID and ARRID for this iteration: TableVector obsidcol(obsarrtab,"OBSERVATION_ID"); TableVector arridcol(obsarrtab,"ARRAY_ID"); // Report OBSID and ARRID, and header for listing: // os << endl << " ObservationID = " << obsid; // os << " ArrayID = " << arrid << endl; String datetime=" Date Timerange "; datetime.replace(24,1,"("); datetime.replace(25,timeref.length(),timeref); datetime.replace(25+timeref.length(),1,")"); // os << datetime; // os << "Scan FldId FieldName nVis Int(s) SpwIds" << endl; // Setup iteration over timestamps within this iteration: Block jcols(2); jcols[0] = "SCAN_NUMBER"; jcols[1] = "TIME"; TableIterator stiter(obsarrtab,jcols); // Vars for keeping track of time, fields, and ddis Int lastscan(-1); Vector lastfldids; Vector lastddids; Vector laststids; Vector fldids(1,0); Vector ddids(1,0); Vector stids(1,0); // State IDs Vector spwids; Int nfld(1); Int nddi(1); Int nst(1); Double btime(0.0), etime(0.0); Bool firsttime(True); Int64 thisnrow(0); Double meanIntTim(0.0); // os.output().precision(3); Int subsetscan=0; // Iterate over timestamps: while (!stiter.pastEnd()) { // ms table at this timestamp Table t(stiter.table()); Int64 nrow=t.nrow(); // relevant columns TableVector timecol(t,"TIME"); TableVector inttim(t,"EXPOSURE"); TableVector scncol(t,"SCAN_NUMBER"); TableVector fldcol(t,"FIELD_ID"); TableVector ddicol(t,"DATA_DESC_ID"); TableVector stidcol(t,"STATE_ID"); // this timestamp Double thistime(timecol(0)); // this scan_number Int thisscan(scncol(0)); // First field and ddi at this timestamp: fldids.resize(1,False); fldids(0)=fldcol(0); nfld=1; ddids.resize(1,False); ddids(0)=ddicol(0); nddi=1; stids.resize(1, False); stids(0) = stidcol(0); nst=1; nVisPerField_(fldids(0))+=nrow; // fill field and ddi lists for this timestamp for (Int64 i=1; i < nrow; i++) { if ( !anyEQ(fldids,fldcol(i)) ) { nfld++; fldids.resize(nfld,True); fldids(nfld-1)=fldcol(i); } if ( !anyEQ(ddids,ddicol(i)) ) { nddi++; ddids.resize(nddi,True); ddids(nddi-1)=ddicol(i); } if ( !anyEQ(stids,stidcol(i)) ) { nst++; stids.resize(nst,True); stids(nst-1)=stidcol(i); } } // If not first timestamp, check if scan changed, etc. if (!firsttime) { // Has state changed? Bool samefld; samefld=fldids.conform(lastfldids) && !anyNE(fldids,lastfldids); Bool sameddi; sameddi=ddids.conform(lastddids) && !anyNE(ddids,lastddids); Bool samest; samest=stids.conform(laststids) && !anyNE(stids,laststids); Bool samescan; samescan=(thisscan==lastscan); samescan = samescan && samefld && sameddi && samest; // If state changed, then print out last scan's info if (!samescan) { if (thisnrow>0){ meanIntTim/=thisnrow; } else { meanIntTim=0.0; } // Spws spwids.resize(lastddids.nelements()); for (uInt iddi=0; iddi0) meanIntTim/=thisnrow; else meanIntTim=0.0; // Spws spwids.resize(lastddids.nelements()); for (uInt iddi=0; iddinAntennas() == 0) { os << "The ANTENNA table is empty" << endl; return; } // Determine antennas present in the main table const std::set& antIds = _msmd->getUniqueAntennaIDs(); uInt nAnt = antIds.size(); std::map > namesToIDsMap; vector names = _msmd->getAntennaNames(namesToIDsMap); vector stations = _msmd->getAntennaStations(); if (verbose) { // Detailed antenna list String title; title="Antennas: " + String::toString(nAnt) + ":"; String indent(" "); uInt indwidth =5; uInt namewidth=6; uInt statwidth=10; uInt diamwidth=5; Int diamprec=1; uInt latwidth=13; uInt longwidth=14; uInt offsetwidth = 14; uInt positionwidth = 16; os.output().setf(ios::fixed, ios::floatfield); os.output().setf(ios::left, ios::adjustfield); // Write the title: os << title << endl; // Write the column headings: os << indent; os.output().width(indwidth); os << "ID"; os.output().width(namewidth); os << "Name"; os.output().width(statwidth); os << "Station"; os.output().width(diamwidth+4); os << "Diam."; os.output().width(longwidth); os << "Long."; os.output().width(latwidth); os << "Lat."; os.output().width(3*offsetwidth); os << " Offset from array center (m)"; os.output().width(3*positionwidth); os << " ITRF Geocentric coordinates (m)"; os << endl; os << indent; os.output().width( indwidth + namewidth + statwidth + diamwidth + 4 + longwidth + latwidth ); os << " "; os.output().setf(ios::right, ios::adjustfield); os.output().width(offsetwidth); os << "East"; os.output().width(offsetwidth); os << "North"; os.output().width(offsetwidth); os << "Elevation"; os.output().width(positionwidth); os << "x"; os.output().width(positionwidth); os << "y"; os.output().width(positionwidth); os << "z"; os << endl; vector antPos = _msmd->getAntennaPositions(); Bool posIsITRF = antPos[0].getRef().getType() != MPosition::ITRF; vector offsets = _msmd->getAntennaOffsets(); QVD diameters = _msmd->getAntennaDiameters(); std::set::const_iterator iter = antIds.begin(); std::set::const_iterator end = antIds.end(); const static Unit diamUnit="m"; for (; iter!=end; ++iter) { os.output().setf(ios::left, ios::adjustfield); Int ant = *iter; // Get diameter const Quantity& diam = diameters[ant]; // Get position const MPosition& mLongLat = antPos[ant]; MVAngle mvLong = mLongLat.getAngle().getValue()(0); MVAngle mvLat = mLongLat.getAngle().getValue()(1); if (posIsITRF) { MeasConvert toItrf(antPos[ant], MPosition::ITRF); antPos[ant] = toItrf(antPos[ant]); } Vector xyz = antPos[ant].get("m").getValue(); // write the row os << indent; os.output().width(indwidth); os << ant; os.output().width(namewidth); os << names[ant]; os.output().width(statwidth); os << stations[ant]; os.output().precision(diamprec); os.output().width(diamwidth); os << diam.getValue(diamUnit)<<"m "; os.output().width(longwidth); os << mvLong.string(MVAngle::ANGLE,7); os.output().width(latwidth); os << mvLat.string(MVAngle::DIG2,7); os.output().setf(ios::right, ios::adjustfield); os.output().precision(4); os.output().width(offsetwidth); Vector antOff = offsets[ant].getValue("m"); os << antOff[0]; os.output().width(offsetwidth); os << antOff[1]; os.output().width(offsetwidth); os << antOff[2]; os.output().precision(6); os.output().width(positionwidth); os << xyz[0]; os.output().width(positionwidth); os << xyz[1]; os.output().width(positionwidth); os << xyz[2]; os << endl; } } else { // Horizontal list of the stations names: os << "Antennas: " << nAnt << " 'name'='station' " << endl; String line, leader; Int last = *antIds.begin() - 1; std::set::const_iterator iter = antIds.begin(); std::set::const_iterator end = antIds.end(); Int maxAnt = *std::max_element(antIds.begin(), antIds.end()); for (; iter!=end; ++iter) { Int ant = *iter; // Build the line line = line + "'" + names[ant] + "'" + "="; line = line + "'" + stations[ant] + "'"; // Add comma if not at the end if (ant != maxAnt) { line = line + ", "; } if (line.length() > 55 || ant == maxAnt) { // This line is finished, dump it after the line leader leader = String::toString(last+1) +"-" +String::toString(ant) +": "; os << " ID="; os.output().setf(ios::right, ios::adjustfield); os.output().width(8); os << leader; os << line << endl; line = ""; last = ant; } } } os << LogIO::POST; } void MSSummary::listFeed (LogIO& os, Bool verbose, Bool oneBased) const { // Do nothing in terse mode if (verbose) { // Make a MS-feed-columns object MSFeedColumns msFC(pMS->feed()); if (msFC.antennaId().nrow()<=0) { os << "The FEED table is empty" << endl; } else { os << "Feeds: " << msFC.antennaId().nrow(); os << ": printing first row only"; // Line is FeedID SpWinID NumRecept PolTypes Int widthLead = 2; Int widthAnt = 10; Int widthSpWinId = 20; Int widthNumRec = 15; Int widthPolType = 10; os << endl; os.output().setf(ios::left, ios::adjustfield); os.output().width(widthLead); os << " "; os.output().width(widthAnt); os << "Antenna"; os.output().width(widthSpWinId); os << "Spectral Window"; os.output().width(widthNumRec); os << "# Receptors"; os.output().width(widthPolType); os << "Polarizations"; os << endl; // loop through rows // for (rownr_t row=0; row= 0) spwId = spwId + 1; os.output().width(widthSpWinId);os << spwId; os.output().width(widthNumRec); os << msFC.numReceptors()(row); os.output().width(widthPolType);os << msFC.polarizationType()(row); os << endl; } } } os << LogIO::POST; } void MSSummary::listField (LogIO& os, Bool verbose) const { Record dummy; listField(os, dummy, verbose, False); } void MSSummary::listField (LogIO& os, Record& outrec, Bool verbose, Bool fillRecord) const { // Is source table present? Bool srcok=!(pMS->source().isNull() || pMS->source().nrow()<1); uInt nfields = _msmd->nFields(); std::set uniqueFields = _msmd->getUniqueFieldIDs(); uInt nFieldsInMain = uniqueFields.size(); if (nfields <= 0) { os << "The FIELD table is empty" << endl; } else if (uniqueFields.empty()) { os << "The MAIN table is empty" << endl; } else { os << "Fields: " << nFieldsInMain << endl; Int widthLead = 2; Int widthField = 5; Int widthCode = 5; Int widthName = 20; Int widthRA = 16; Int widthDec = 16; Int widthType = 8; Int widthSrc = 6; Int widthnVis = 10; Int widthNUnflaggedRows = 13; outrec.define("nfields", Int(nFieldsInMain)); if (verbose) {} // null, always same output // Line is ID Date Time Name RA Dec Type os.output().setf(ios::left, ios::adjustfield); os.output().width(widthLead); os << " "; os.output().width(widthField); os << "ID"; os.output().width(widthCode); os << "Code"; os.output().width(widthName); os << "Name"; os.output().width(widthRA); os << "RA"; os.output().width(widthDec); os << " Decl"; os.output().width(widthType); os << "Epoch"; if (srcok) {os.output().width(widthSrc); os << "SrcId";} if (nVisPerField_.nelements()>0) { os.output().setf(ios::right, ios::adjustfield); os.output().width(widthnVis); os << "nRows"; if (_listUnflaggedRowCount) { os.output().width(widthNUnflaggedRows); os << "nUnflRows"; } } os << endl; // loop through fields vector fieldNames = _msmd->getFieldNames(); vector codes = _msmd->getFieldCodes(); std::set::const_iterator fiter = uniqueFields.begin(); std::set::const_iterator fend = uniqueFields.end(); vector sourceIDs = _msmd->getFieldTableSourceIDs(); static const MEpoch ezero(Quantity(0, "s")); vector phaseDirs = _msmd->getPhaseDirs(ezero); for (; fiter!=fend; ++fiter) { Int fld = *fiter; if (fld >=0 && fld < (Int)nfields) { MDirection mRaDec = phaseDirs[fld]; MVAngle mvRa = mRaDec.getAngle().getValue()(0); MVAngle mvDec = mRaDec.getAngle().getValue()(1); String name = fieldNames[fld]; if (name.length()>20) { name.replace(19,1,"*"); } os.output().setf(ios::left, ios::adjustfield); os.output().width(widthLead); os << " "; os.output().width(widthField); os << (fld); os.output().width(widthCode); os << codes[fld]; os.output().width(widthName); os << name.at(0,20); os.output().width(widthRA); os << mvRa(0.0).string(MVAngle::TIME,12); os.output().width(widthDec); os << mvDec.string(MVAngle::DIG2,11); os.output().width(widthType); os << MDirection::showType(mRaDec.getRef().getType()); if (srcok) { os.output().width(widthSrc); os << sourceIDs[fld]; } if ((Int)nVisPerField_.nelements() > fld) { os.output().setf(ios::right, ios::adjustfield); os.output().width(widthnVis); os << _msmd->nRows(MSMetaData::BOTH, fld); if (_listUnflaggedRowCount) { os.output().width(widthNUnflaggedRows); ostringstream xx; xx << std::fixed << setprecision(2) << std::right << _msmd->nUnflaggedRows(MSMetaData::BOTH, fld); os << xx.str(); } } os << endl; if(fillRecord){ Record fieldrec; fieldrec.define("name", name); fieldrec.define("code", codes[fld]); MeasureHolder mh(mRaDec); Record dirrec; String err; mh.toRecord(err, dirrec); fieldrec.defineRecord("direction", dirrec); String fieldrecid=String("field_")+String::toString(fld); if(!outrec.isDefined(fieldrecid)){ outrec.defineRecord(fieldrecid, fieldrec); } } } else { os << "Field "<0) { os<1) { // for version 2 of the MS // Line is TelName ObsDate Observer Project Int widthLead = 2; Int widthTel = 10; Int widthDate = 20; Int widthObs = 15; Int widthProj = 15; os.output().setf(ios::left, ios::adjustfield); os.output().width(widthLead); os << " "; os.output().width(widthTel); os << "Telescope"; os.output().width(widthDate); os << "Observation Date"; os.output().width(widthObs); os << "Observer"; os.output().width(widthProj); os << "Project"; os << endl; for (rownr_t row=0;rowhistory()); if (msHis.nrow()<=0) { os << "The HISTORY table is empty" << endl; } else { uInt nmessages = msHis.time().nrow(); os << "History table entries: " << nmessages << endl << LogIO::POST; const ScalarColumn &theTimes((msHis.time())); const ScalarColumn &messOrigin((msHis.origin())); const ScalarColumn &messString((msHis.message())); const ScalarColumn &messPriority((msHis.priority())); for (uInt i=0 ; i < nmessages; i++) { Quantity tmpq(theTimes(i), "s"); MVTime mvtime(tmpq); Time messTime(mvtime.getTime()); LogMessage::Priority itsPriority(LogMessage::DEBUGGING); if(messPriority(i) == "DEBUGGING"){ itsPriority = LogMessage::DEBUGGING; } else if(messPriority(i) == "DEBUG2"){ itsPriority = LogMessage::DEBUG2; } else if(messPriority(i) == "DEBUG1"){ itsPriority = LogMessage::DEBUG1; } else if(messPriority(i) == "NORMAL5" || messPriority(i) == "INFO5"){ itsPriority = LogMessage::NORMAL5; } else if(messPriority(i) == "NORMAL4" || messPriority(i) == "INFO4"){ itsPriority = LogMessage::NORMAL4; } else if(messPriority(i) == "NORMAL3" || messPriority(i) == "INFO3"){ itsPriority = LogMessage::NORMAL3; } else if(messPriority(i) == "NORMAL2" || messPriority(i) == "INFO2"){ itsPriority = LogMessage::NORMAL2; } else if(messPriority(i) == "NORMAL1" || messPriority(i) == "INFO1"){ itsPriority = LogMessage::NORMAL1; } else if(messPriority(i) == "NORMAL" || messPriority(i) == "INFO"){ itsPriority = LogMessage::NORMAL; } else if(messPriority(i) == "WARN"){ itsPriority = LogMessage::WARN; } else if(messPriority(i) == "SEVERE"){ itsPriority = LogMessage::SEVERE; } LogOrigin orhist(messOrigin(i)); LogMessage histMessage(messString(i), orhist.taskName("listHistory"), itsPriority); histMessage.messageTime(messTime); os.post(histMessage); } os << LogIO::POST; } } void MSSummary::listSource (LogIO& os, Bool verbose) const { // Check if optional SOURCE table is present: if (pMS->source().isNull()) { os << "The SOURCE table is absent: see the FIELD table" << endl; return; } // Create a MS-source-columns object MSSourceColumns msSC(pMS->source()); // Are restFreq and sysvel present? Bool restFreqOK=pMS->source().tableDesc().isColumn("REST_FREQUENCY"); Bool sysVelOK=pMS->source().tableDesc().isColumn("SYSVEL"); if (msSC.name().nrow()<=0) { os << "The SOURCE table is empty: see the FIELD table" << endl; } else { if (verbose) { // activated: CAS-3180 os << "Sources: " << msSC.name().nrow() << endl; // Line is Time Name RA Dec SysVel Int widthLead = 2; Int widthSrc = 5; // Int widthTime = 15; Int widthName = 20; // Int widthRA = 14; // Int widthDec = 15; Int widthSpw = 6; Int widthRF = 15; Int widthVel = 13; os.output().setf(ios::left, ios::adjustfield); os.output().width(widthLead); os << " "; // os.output().width(widthTime); os << "Time MidPt"; os.output().width(widthSrc); os << "ID"; os.output().width(widthName); os << "Name"; // os.output().width(widthRA); os << "RA"; // os.output().width(widthDec); os << "Decl"; os.output().width(widthSpw); os << "SpwId"; if (restFreqOK) { os.output().width(widthRF); os << "RestFreq(MHz)"; } if (sysVelOK) { os.output().width(widthVel); os << "SysVel(km/s)"; } os << endl; os.output().precision(12); // Loop through rows for (rownr_t row=0; row20) name.replace(19,1,"*"); os.output().setf(ios::left, ios::adjustfield); os.output().width(widthLead); os<< " "; // os.output().width(widthTime); // os<< MVTime(msSC.time()(row)/86400.0).string(); os.output().width(widthSrc); os<< msSC.sourceId()(row); os.output().width(widthName); os<< name.at(0,20); // os.output().width(widthRA); os<< mvRa(0.0).string(MVAngle::TIME,10); // os.output().width(widthDec); os<< mvDec.string(MVAngle::DIG2,10); os.output().width(widthSpw); Int spwid=msSC.spectralWindowId()(row); if (spwid<0) os<< "any"; else os< restfreq=msSC.restFrequency()(row); if (restfreq.nelements()>0) os<< restfreq(0)/1.0e6; else os<< "-"; } else os<<"-"; } if (sysVelOK) { os.output().width(widthVel); if (msSC.sysvel().isDefined(row)) { Vector sysvel=msSC.sysvel()(row); if (sysvel.nelements()>0) os<< sysvel(0)/1.0e3; else os<< "-"; } else os<<"-"; } os << endl; } if (!restFreqOK) os << " NB: No rest frequency information found in SOURCE table." << endl; if (!sysVelOK) os << " NB: No systemic velocity information found in SOURCE table." << endl; } } os << LogIO::POST; } void MSSummary::listSpectralWindow (LogIO& os, Bool verbose) const { // Create a MS-spwin-columns object MSSpWindowColumns msSWC(pMS->spectralWindow()); if (verbose) {} //null; always the same output if (msSWC.refFrequency().nrow()<=0) { os << "The SPECTRAL_WINDOW table is empty: see the FEED table" << endl; } else { os << "Spectral Windows: " << msSWC.refFrequency().nrow() << endl; // The 8 columns below are all in the SpWin subtable of Version 1 of // the MS definition. For Version 2, some info will appear in other // subtables, as indicated. // Line is (V1): RefFreq RestFreq Molecule Trans'n Resol BW Numch Correls // V2 subtable: SOURCE SOURCE POLARIZ'N Int widthLead = 2; Int widthFreq = 12; Int widthFrqNum = 7; Int widthNumChan = 8; os.output().setf(ios::left, ios::adjustfield); os.output().width(widthLead); os << " "; os.output().width(widthFreq); os << "Ref.Freq"; os.output().width(widthNumChan); os << "#Chans"; os.output().width(widthFreq); os << "Resolution"; os.output().width(widthFreq); os << "TotalBW"; // os.output().width(widthCorrTypes);os << "Correlations"; os << endl; // For each row of the SpWin subtable, write the info for (rownr_t row=0; rowspectralWindow()); // Create a MS-data_desc-columns object MSDataDescColumns msDDC(pMS->dataDescription()); // Create a MS-polin-columns object MSPolarizationColumns msPOLC(pMS->polarization()); if (msDDC.nrow()<=0 or msSWC.nrow()<=0) { //The DATA_DESCRIPTION or SPECTRAL_WINDOW table is empty return; } // Determine the data_desc_ids present in the main table MSRange msr(*pMS); Vector ddId = msr.range(MSS::DATA_DESC_ID).asArrayInt(RecordFieldId(0)); Vector uddId(ddId.nelements()); for (uInt i=0; i spwIds = msDDC.spectralWindowId().getColumnCells(uddId); Vector polIds = msDDC.polarizationId().getColumnCells(uddId); //const Int option=Sort::HeapSort | Sort::NoDuplicates; //const Sort::Order order=Sort::Ascending; //Int nSpw=GenSort::sort (spwIds, order, option); if (ddId.nelements()>0) { // For each row of the DataDesc subtable, write the info for (uInt i=0; ipolarization()); rownr_t nRow = pMS->polarization().nrow(); if (nRow<=0) { os << "The POLARIZATION table is empty: see the FEED table" << endl; } else { os << "Polarization setups: " << nRow << endl; // Define the column widths Int widthLead = 2; Int widthCorrTypes = msPolC.corrType()(0).nelements()*4; Int widthCorrType = 4; // Write the column headers os.output().setf(ios::left, ios::adjustfield); os.output().width(widthLead); os << " "; os.output().width(widthCorrTypes); os << "Correlations"; os << endl; // For each row of the Pol subtable, write the info for (rownr_t row=0; rownDataDescriptions() == 0) { os << "The DATA_DESCRIPTION table is empty: see the FEED table" << endl; } if (_msmd->nSpw(True) == 0) { os << "The SPECTRAL_WINDOW table is empty: see the FEED table" << endl; } if (_msmd->nPol() == 0) { os << "The POLARIZATION table is empty: see the FEED table" << endl; } // determine the data_desc_ids present in the main table std::set ddId = _msmd->getUniqueDataDescIDs(); // now get the corresponding spectral windows and pol setups vector polIds = _msmd->getDataDescIDToPolIDMap(); Vector spwIds(_msmd->getDataDescIDToSpwMap()); std::set uniquePolIDs; std::set uniqueSpws; std::set::const_iterator dIter = ddId.begin(); std::set::const_iterator dEnd = ddId.end(); for (; dIter!=dEnd; ++dIter) { uniquePolIDs.insert(polIds[*dIter]); uniqueSpws.insert(spwIds[*dIter]); } const Int option=Sort::HeapSort | Sort::NoDuplicates; const Sort::Order order=Sort::Ascending; GenSort::sort (spwIds, order, option); if (! ddId.empty()) { os << "Spectral Windows: "; os << " (" << uniqueSpws.size() << " unique spectral windows and "; os << uniquePolIDs.size() << " unique polarization setups)"< names = _msmd->getSpwNames(); Int widthName = 5; for ( vector::const_iterator iter=names.begin(); iter!=names.end(); ++iter ) { widthName = max(widthName, (Int)iter->size()); } // Define the column widths Int widthLead = 2; Int widthSpwId = 7; Int widthFrame = 6; Int widthFreq = 12; Int widthFrqNum = 12; Int widthNumChan = 6; vector > corrTypes = _msmd->getCorrTypes(); Int widthCorrTypes = 4*corrTypes[0].size(); Int widthCorrType = 4; uInt widthBBCNo = 8; // Write the column headers os.output().setf(ios::left, ios::adjustfield); os.output().width(widthLead); os << " "; os.output().width(widthSpwId); os << "SpwID "; os.output().width(widthName); os << "Name "; os.output().setf(ios::right, ios::adjustfield); os.output().width(widthNumChan); os << " #Chans" << " "; os.output().setf(ios::left, ios::adjustfield); os.output().width(widthFrame); os << " Frame"; os.output().width(widthFreq); os << " Ch0(MHz)"; os.output().width(widthFreq); os << " ChanWid(kHz) "; os.output().width(widthFreq); os << " TotBW(kHz)"; os.output().width(widthFreq); os << "CtrFreq(MHz) "; Bool hasBBCNo = _msmd->hasBBCNo(); if (hasBBCNo) { os.output().width(widthBBCNo); os << "BBC Num "; } os.output().width(widthCorrTypes); os << " Corrs"; os << endl; vector nChans = _msmd->nChans(); vector chanFreqs = _msmd->getChanFreqs(); vector chanWidths = _msmd->getChanWidths(); vector centerFreqs = _msmd->getCenterFreqs(); vector bandwidths = _msmd->getBandWidths(); vector bbcNo = hasBBCNo ? _msmd->getBBCNos() : vector(); os.output().precision(9); // order by spwid, not ddid, CAS-7376 Vector::const_iterator iter = spwIds.begin(); Vector::const_iterator end = spwIds.end(); std::vector > spwToDDID = _msmd->getSpwToDataDescriptionIDMap(); vector refFreqs = _msmd->getRefFreqs(); for (; iter != end; ++iter) { Int spw = *iter; std::set ddids = spwToDDID[spw]; std::set::const_iterator diter = ddids.begin(); std::set::const_iterator dend = ddids.end(); Bool isSpwInMainTable = False; for (; diter!=dend; ++diter) { uInt dd = *diter; if (ddId.find(dd) == ddId.end()) { // data description ID not in main table, so not reported here continue; } isSpwInMainTable = True; os.output().setf(ios::left, ios::adjustfield); os.output().width(widthLead); os << " "; // 1th column: Spectral Window Id os.output().width(widthSpwId); os << (spw); // 2nd column: SPW name os.output().width(widthName); os << names[spw] << " "; // 3rd column: number of channels in the spectral window os.output().setf(ios::right, ios::adjustfield); os.output().width(widthNumChan); os << nChans[spw] << " "; // 4th column: Reference Frame info // os.output().setf(ios::left, ios::adjustfield); os.output().width(widthFrame); os<< refFreqs[spw].getRefString(); // 5th column: Chan 1 freq (may be at high freq end of band!) os.output().setf(ios::fixed); os.output().precision(3); os.output().width(widthFrqNum); os<< chanFreqs[spw].getValue("MHz")[0]; // 6th column: channel resolution os.output().width(widthFrqNum+2); os << chanWidths[spw].getValue("kHz")[0]; // 7th column: total bandwidth of the spectral window os.output().width(widthFrqNum); os.output().precision(1); os<< bandwidths[spw]/1000; os.output().width(widthFrqNum); os.output().precision(4); os << centerFreqs[spw].getValue("MHz") << " "; if (hasBBCNo) { os.output().width(widthBBCNo); os<< bbcNo[spw]; } // 8th column: reference frequency // os.output().width(widthFrqNum); // os<< msSWC.refFrequency()(spw)/1.0e6; // 9th column: the correlation type(s) Int pol = polIds[dd]; vector::const_iterator cIter = corrTypes[pol].begin(); vector::const_iterator cEnd = corrTypes[pol].end(); for (; cIter!=cEnd; ++cIter) { os.output().width(widthCorrType); os << Stokes::name(Stokes::type(*cIter)); } } // CAS-9072 avoid printing blank lines if there are // spws that are not represented in the main table if (isSpwInMainTable) { os << endl; } } } os << LogIO::POST; } void MSSummary::listSysCal (LogIO& os, Bool verbose) const { // Check for existence of optional SYSCAL table: if (pMS->sysCal().isNull()) { os << "The SYSCAL table is absent" << endl; return; } // Do nothing in terse mode if (verbose) { // Create a MS-syscal-columns object MSSysCalColumns msSCC(pMS->sysCal()); if (msSCC.tsys().nrow()<=0) { os << "The SYSCAL table is empty" << endl; } else { os << "SysCal entries: " << msSCC.tsys().nrow() << endl; os << " The average Tsys for all data is " << sum(msSCC.tsys()(0))/msSCC.tsys()(0).nelements() << " K" << endl; } } os << LogIO::POST; } void MSSummary::listWeather (LogIO& os, Bool verbose) const { // Check for existence of optional WEATHER table: if (pMS->weather().isNull()) { os << "The WEATHER table is absent" << endl; return; } // Do nothing in terse mode if (verbose) { // Create a MS-weather-columns object MSWeatherColumns msWC(pMS->weather()); if (msWC.H2O().nrow()<=0) { os << "The WEATHER table is empty" << endl; } else { os << "Weather entries: " << msWC.H2O().nrow() << endl; os << " Average H2O column density = " << msWC.H2O()(0) << " m**-2 Average air temperature = " << msWC.temperature()(0) << " K" << endl; } } os<< LogIO::POST; } void MSSummary::listTables (LogIO& os, Bool verbose) const { // Get nrows for each table (=-1 if table absent) Vector tableRows(18); tableRows(0) = nrow(); tableRows(1) = pMS->antenna().nrow(); tableRows(2) = pMS->dataDescription().nrow(); tableRows(3) = (pMS->doppler().isNull() ? -1 : (Int64)pMS->doppler().nrow()); tableRows(4) = pMS->feed().nrow(); tableRows(5) = pMS->field().nrow(); tableRows(6) = pMS->flagCmd().nrow(); tableRows(7) = (pMS->freqOffset().isNull() ? -1 : (Int64)pMS->freqOffset().nrow()); tableRows(8) = pMS->history().nrow(); tableRows(9) = pMS->observation().nrow(); tableRows(10) = pMS->pointing().nrow(); tableRows(11) = pMS->polarization().nrow(); tableRows(12) = pMS->processor().nrow(); tableRows(13) = (pMS->source().isNull() ? -1 : (Int64)pMS->source().nrow()); tableRows(14) = pMS->spectralWindow().nrow(); tableRows(15) = pMS->state().nrow(); tableRows(16) = (pMS->sysCal().isNull() ? -1 : (Int64)pMS->sysCal().nrow()); tableRows(17) = (pMS->weather().isNull() ? -1 : (Int64)pMS->weather().nrow()); Vector rowStrings(18), tableStrings(18); rowStrings = " rows"; tableStrings(0) = "MAIN"; tableStrings(1) = "ANTENNA"; tableStrings(2) = "DATA_DESCRIPTION"; tableStrings(3) = "DOPPLER"; tableStrings(4) = "FEED"; tableStrings(5) = "FIELD"; tableStrings(6) = "FLAG_CMD"; tableStrings(7) = "FREQ_OFFSET"; tableStrings(8) = "HISTORY"; tableStrings(9) = "OBSERVATION"; tableStrings(10)= "POINTING"; tableStrings(11)= "POLARIZATION"; tableStrings(12)= "PROCESSOR"; tableStrings(13)= "SOURCE"; tableStrings(14)= "SPECTRAL_WINDOW"; tableStrings(15)= "STATE"; tableStrings(16)= "SYSCAL"; tableStrings(17)= "WEATHER"; // Just to make things read better for (uInt i=0; i<18; i++) { if (tableRows(i)==1) rowStrings(i) = " row"; // if table exists, but empty: if (tableRows(i)==0) { rowStrings(i) = " "; if (tableStrings(i)=="SOURCE") rowStrings(i) += " (see FIELD)"; if (tableStrings(i)=="SPECTRAL_WINDOW") rowStrings(i) += " (see FEED)"; } // if table absent: if (tableRows(i)==-1) { rowStrings(i) = ""; if (tableStrings(i)=="SOURCE") rowStrings(i) += " (see FIELD)"; } } // This bit is a little messy, keeping track of the verbose and terse // forms of the output format, as well as the zero/nonzero tables // Do things on this side // whether verbose or not os << "Tables"; if (!verbose) os << "(rows)"; os << ":"; if (!verbose) os << " (-1 = table absent)"; os << endl; for (uInt i=0; i<18; i++) { if (verbose) { os.output().setf(ios::left, ios::adjustfield); os.output().width(3); } os << " "; if (verbose) { os.output().width(20); } os << tableStrings(i); if (verbose && tableRows(i)>0) { os.output().setf(ios::right, ios::adjustfield); os.output().width(8); } if (!verbose) os << "("; if (!verbose || tableRows(i)>0) os << tableRows(i); if (!verbose) os << ")"; if (verbose) { os.output().setf(ios::left, ios::adjustfield); os.output().width(10); os << rowStrings(i); os << endl; } else if ((i%5)==0) os << endl; } os << LogIO::POST; } // // Clear all the formatting flags // void MSSummary::clearFlags(LogIO& os) const { os.output().unsetf(ios::left); os.output().unsetf(ios::right); os.output().unsetf(ios::internal); os.output().unsetf(ios::dec); os.output().unsetf(ios::oct); os.output().unsetf(ios::hex); os.output().unsetf(ios::showbase | ios::showpos | ios::uppercase | ios::showpoint); os.output().unsetf(ios::scientific); os.output().unsetf(ios::fixed); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSOper/MSSummary.h000066400000000000000000000154351476623553700171660ustar00rootroot00000000000000//# MSSummary.h: Helper class for applications listing an image header //# Copyright (C) 1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef MS_MSSUMMARY_H #define MS_MSSUMMARY_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class MeasurementSet; class LogIO; class MSMetaData; // Provides and lists information about the header of an image // // // // // // //
      • MeasurementSet //
      • Coordinates // // // // This class lists the ancilliary or header information from a // MeasurementSet in a Summary format. // // // // MSs consist of pixels and descriptive information stored in what // is loosely termed the header. This is information describing the // coordinate system, the image units etc. This class enables you to // retrieve the descriptive header information and/or list it. // // // // // PagedMS inMS(fileName); // MSSummary header(inMS); // LogOrigin or("myClass", "myFunction(...)", WHERE); // LogIO os(or); // header.list(os); // // A PagedMS object is constructed and then logged to the // supplied LogIO object. // // // // Note that if the PagedMS goes out of scope, this // class will retrieve rubbish as it just maintains a pointer // to the image. // // // // The viewing of the image header is a basic capability that is // commonly required. // // // // There are various placeholders which will need to be activated for // Version 2 of the MeasurementSet definition. // class MSSummary { public: // Constructor // // maxCacheMB is the maximum cache size in MB to use for the created // MSMetaData object. MSSummary (const MeasurementSet& ms, Float maxCacheMB = 50.0); MSSummary (const MeasurementSet* ms, Float maxCacheMB = 50.0); MSSummary (const MeasurementSet* ms, const String msname, Float maxCacheMB = 50.0); // construct the object using an MSMetaDataObject MSSummary (std::shared_ptr msmd); // Destructor ~MSSummary(); // Retrieve number of rows Int64 nrow() const; // Retrieve image name String name() const; // Set a new MS. maxCacheMB is the maximum cache size of the // created MSMetaData tool. If negative, the cache size used when this object // was created is used. Bool setMS (const MeasurementSet& ms, Float maxCacheMB=-1); // List all header information. void list (LogIO& os, Bool verbose=False, Bool oneBased=True) const; //Return some useful info in a record too along with os void list (LogIO& os, Record& outRec, Bool verbose=False, Bool fillRecord=True, Bool oneBased=True) const; // List a title for the Summary. void listTitle (LogIO& os) const; // List convenient groupings of tables: list where MS obtained // (Observation and Array tables) void listWhere (LogIO& os, Bool verbose=False) const; // List what was observed (Field and Main tables) void listWhat (LogIO& os, Bool verbose=False) const; void listWhat (LogIO& os, Record& outRec, Bool verbose=False, Bool fillRecord=True) const; // List how data were obtained (SpectralWindow, Feed, and Antenna tables) void listHow (LogIO& os, Bool verbose=False, Bool oneBased=True) const; // List main table void listMain (LogIO& os, Bool verbose=False) const; //Return some useful info in a record too along with os void listMain (LogIO& os, Record& outRec, Bool verbose=False, Bool fillRecord=True) const; // Return a Record with information derived from the main table void getScanSummary (Record& outRec) const; // List subtables // void listAntenna (LogIO& os, Bool verbose=False) const; void listFeed (LogIO& os, Bool verbose=False, Bool oneBased=True) const; void listField (LogIO& os, Bool verbose=False) const; void listField (LogIO& os, Record& outRec, Bool verbose=False, Bool fillRecord=True) const; void listObservation (LogIO& os, Bool verbose=False) const; void listHistory (LogIO& os) const; void listPolarization (LogIO& os, Bool verbose=False) const; void listSource (LogIO& os, Bool verbose=False) const; void listSpectralWindow (LogIO& os, Bool verbose=False) const; void getSpectralWindowInfo(Record& outRec) const; void listSpectralAndPolInfo (LogIO& os, Bool verbose=False, Bool oneBased=True) const; void listSysCal (LogIO& os, Bool verbose=False) const; void listWeather (LogIO& os, Bool verbose=False) const; // // List table size summary void listTables (LogIO& os, Bool verbose=False) const; void setListUnflaggedRowCount(Bool v) { _listUnflaggedRowCount = v; } // OBSOLETE. No longer does anything, kept for compilation backward compatibility. void setMetaDataCacheSizeInMB(Float) {} private: // Pointer to MS const MeasurementSet* pMS; std::shared_ptr _msmd; // Formatting strings const String dashlin1, dashlin2; // Clear formatting flags void clearFlags (LogIO& os) const; // For keeping track of the number of vis per field mutable Vector nVisPerField_; // Name of the MS used in the constructor String msname_p; Bool _listUnflaggedRowCount; Float _cacheSizeMB; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSOper/MSValidIds.cc000066400000000000000000000116251476623553700173630ustar00rootroot00000000000000//# MSValidIds.cc: this defines MSValidIds. //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSValidIds::MSValidIds() : romsCols_p(0), hasDoppler_p(False), hasSource_p(False) { ;} MSValidIds::MSValidIds(const MeasurementSet &ms) : romsCols_p(0), hasDoppler_p(False), hasSource_p(False) { attach(ms);} MSValidIds::MSValidIds(const MSValidIds &other) : romsCols_p(0), hasDoppler_p(False), hasSource_p(False) { *this = other;} MSValidIds::~MSValidIds() { clear(); } MSValidIds &MSValidIds::operator=(const MSValidIds &other) { if (this != &other) { attach(other.ms_p); } return *this; } void MSValidIds::attach(const MeasurementSet &ms) { clear(); ms_p = ms; romsCols_p = new MSColumns(ms_p); AlwaysAssert(romsCols_p, AipsError); // check on existance of optional sub-tables hasDoppler_p = ms_p.keywordSet().isDefined("DOPPLER"); hasSource_p = ms_p.keywordSet().isDefined("SOURCE"); } Int MSValidIds::antenna1(rownr_t rownr) const { Int result = -1; if (checkRow(rownr) && romsCols_p) { result = checkResult(romsCols_p->antenna1()(rownr), ms_p.antenna()); } return result; } Int MSValidIds::antenna2(rownr_t rownr) const { Int result = -1; if (checkRow(rownr) && romsCols_p) { result = checkResult(romsCols_p->antenna2()(rownr), ms_p.antenna()); } return result; } Int MSValidIds::dataDescId(rownr_t rownr) const { Int result = -1; if (checkRow(rownr) && romsCols_p) { result = checkResult(romsCols_p->dataDescId()(rownr), ms_p.dataDescription()); } return result; } Int MSValidIds::fieldId(rownr_t rownr) const { Int result = -1; if (checkRow(rownr) && romsCols_p) { result = checkResult(romsCols_p->fieldId()(rownr), ms_p.field()); } return result; } Int MSValidIds::observationId(rownr_t rownr) const { Int result = -1; if (checkRow(rownr) && romsCols_p) { result = checkResult(romsCols_p->observationId()(rownr), ms_p.observation()); } return result; } Int MSValidIds::processorId(rownr_t rownr) const { Int result = -1; if (checkRow(rownr) && romsCols_p) { result = checkResult(romsCols_p->processorId()(rownr), ms_p.processor()); } return result; } Int MSValidIds::stateId(rownr_t rownr) const { Int result = -1; if (checkRow(rownr) && romsCols_p) { result = checkResult(romsCols_p->stateId()(rownr), ms_p.state()); } return result; } Int MSValidIds::polarizationId(rownr_t rownr) const { Int result = dataDescId(rownr); if (result >= 0) { result = checkResult(romsCols_p->dataDescription().polarizationId()(result), ms_p.polarization()); } return result; } Int MSValidIds::spectralWindowId(rownr_t rownr) const { Int result = dataDescId(rownr); if (result >= 0) { result = checkResult(romsCols_p->dataDescription().spectralWindowId()(result), ms_p.spectralWindow()); } return result; } Int MSValidIds::dopplerId(rownr_t rownr) const { Int result = hasDoppler_p ? spectralWindowId(rownr) : -1; if (result >= 0) { result = romsCols_p->spectralWindow().dopplerId().isNull() ? -1 : romsCols_p->spectralWindow().dopplerId()(result); } return result; } Int MSValidIds::sourceId(rownr_t rownr) const { Int result = hasSource_p ? fieldId(rownr) : -1; if (result >= 0) { result = romsCols_p->field().sourceId()(result); } return result; } void MSValidIds::clear() { delete romsCols_p; romsCols_p = 0; hasDoppler_p = hasSource_p = False; } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSOper/MSValidIds.h000066400000000000000000000112621476623553700172220ustar00rootroot00000000000000//# MSValidIds: ensures that required MS Ids are valid or -1 by row number //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSVALIDIDS_H #define MS_MSVALIDIDS_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations class MSColumns; // // // // // // //
      • MeasurementSet // // // // // // // // // // // // // // // //
      • //
      • // class MSValidIds { public: // Construct one unattached to a MeasurementSet. All functions return -1. // Use the attach function to attach this to a MeasurementSet after construction. MSValidIds(); // Construct one attached to the indicated MeasurementSet MSValidIds(const MeasurementSet &ms); // Construct one from another MSValidIds(const MSValidIds &other); // The destructor ~MSValidIds(); // Assignment operator, reference semantics. MSValidIds &operator=(const MSValidIds &other); // Attach this one to a MeasurementSet. This can also be used to // re-attach to the same MeasurementSet when additional optional // subtables have been added since this object was constructed. void attach(const MeasurementSet &ms); // These functions check on the validity of the appropriate value in // the main table or sub-tables in the case of some Ids. The actual // value stored is returned unless the sub-table does not exist (for // optional subtables) or the indicated row number does not exist // in that sub-table where appropriate. // Int antenna1(rownr_t rownr) const; Int antenna2(rownr_t rownr) const; Int dataDescId(rownr_t rownr) const; Int fieldId(rownr_t rownr) const; Int observationId(rownr_t rownr) const; Int processorId(rownr_t rownr) const; Int stateId(rownr_t rownr) const; // The polarizationId comes from the DATA_DESCRIPTION subtable, so dataDescId must // first be valid in order for this to also be valid. Int polarizationId(rownr_t rownr) const; // The spectralWindowId comes from the DATA_DESCRIPTION subtable, so dataDescId must // first be valid in order for this to also be valid. Int spectralWindowId(rownr_t rownr) const; // the dopplerId comes from the SPECTRAL_WINDOW subtable so spectralWindowId must // first be valid in order for this to also be valid. Since the DOPPLER subtable // is not simply indexed by DOPPLER_ID, the DOPPLER subtable exists and a dopplerId // can be found in the SPECTRAL_WINDOW subtable, that value will be returned, whatever // it is. Int dopplerId(rownr_t rownr) const; // The sourceId comes from the FIELD subtable so fieldId must first be valid // in order for this to also be valid. Since the SOURCE table is also // indexed by TIME, the only additional check is that a SOURCE table must // exist in order for this to be valid. Int sourceId(rownr_t rownr) const; // private: MeasurementSet ms_p; MSColumns *romsCols_p; Bool hasDoppler_p, hasSource_p; void clear(); Int checkResult(Int testResult, const Table &mstable) const { return (testResult < 0 || rownr_t(testResult) >= mstable.nrow()) ? -1 : testResult;} Bool checkRow(rownr_t rownr) const {return rownr < ms_p.nrow();} }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSOper/NewMSSimulator.cc000066400000000000000000001725261476623553700203250ustar00rootroot00000000000000 //# NewMSSimulator.cc: this defines NewMSSimulator, which simulates a MeasurementSet //# Copyright (C) 1995-2009 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // temporary to get access to beam_offsets #include // namespace casacore { //# NAMESPACE CASACORE - BEGIN const uInt nCat = 6; // Number of Flag categories const String sigmaCol = "sigmaHyperColumn"; const String dataCol = "dataHyperColumn"; const String scratchDataCol = "scratchDataHyperColumn"; const String flagCol = "flagHyperColumn"; const String sigmaTileId = "SIGMA_HYPERCUBE_ID"; const String dataTileId = "DATA_HYPERCUBE_ID"; const String scratchDataTileId = "SCRATCH_DATA_HYPERCUBE_ID"; const String flagTileId = "FLAG_CATEGORY_HYPERCUBE_ID"; // a but ugly solution to use the feed table parser of MSIter // to extract antennaMounts and BeamOffsets. struct MSFeedParameterExtractor : protected MSIter { MSFeedParameterExtractor(const MeasurementSet &ms) { msc_p.reset (new MSColumns(ms)); msc_p->antenna().mount().getColumn(antennaMounts_p,True); checkFeed_p=True; setFeedInfo(); } // Return a string mount identifier for each antenna using MSIter::antennaMounts; // Return a cube containing pairs of coordinate offset for each receptor // of each feed (values are in radians, coordinate system is fixed with // antenna and is the same as used to define the BEAM_OFFSET parameter // in the feed table). The cube axes are receptor, antenna, feed. using MSIter::getBeamOffsets; // True if all elements of the cube returned by getBeamOffsets are zero using MSIter::allBeamOffsetsZero; }; // void NewMSSimulator::defaults() { fractionBlockageLimit_p=1e-6; elevationLimit_p=Quantity(8.0, "deg"); autoCorrelationWt_p=1.0; telescope_p="Unknown"; qIntegrationTime_p=Quantity(10.0, "s"); useHourAngle_p=True; Quantity today; MVTime::read(today, "today"); mRefTime_p=MEpoch(today, MEpoch::UTC); } NewMSSimulator::NewMSSimulator(const String& MSName) : dataAcc_p(), scratchDataAcc_p(), sigmaAcc_p(), flagAcc_p(), maxData_p(2e9) { LogIO os(LogOrigin("NewMSSimulator", "NewMSSimulator(dataTileId, "Index for Data tiling")); msDesc.addColumn(ScalarColumnDesc(scratchDataTileId, "Index for Scratch Data tiling")); msDesc.addColumn(ScalarColumnDesc(sigmaTileId, "Index for Sigma tiling")); msDesc.addColumn(ScalarColumnDesc(flagTileId, "Index for Flag Category tiling")); */ // setup hypercolumns for the data/flag/flag_catagory/sigma & weight columns. { Vector dataCols(1); dataCols(0) = MeasurementSet::columnName(MeasurementSet::DATA); const Vector coordCols(0); const Vector idCols(1, dataTileId); //msDesc.defineHypercolumn(dataCol, 3, dataCols, coordCols, idCols); msDesc.defineHypercolumn(dataCol, 3, dataCols); } { Vector dataCols(1); dataCols(0) = MeasurementSet::columnName(MeasurementSet::FLAG); const Vector coordCols(0); const Vector idCols(1, dataTileId); //msDesc.defineHypercolumn(dataCol, 3, dataCols, coordCols, idCols); msDesc.defineHypercolumn("FlagColumn", 3, dataCols); } { Vector dataCols(1); dataCols(0) = MeasurementSet::columnName(MeasurementSet::MODEL_DATA); const Vector coordCols(0); const Vector idCols(1, scratchDataTileId); // msDesc.defineHypercolumn(scratchDataCol, 3, dataCols, coordCols, idCols); msDesc.defineHypercolumn("ModelDataColumn", 3, dataCols); } { Vector dataCols(1); dataCols(0) = MeasurementSet::columnName(MeasurementSet::CORRECTED_DATA); const Vector coordCols(0); const Vector idCols(1, scratchDataTileId); //msDesc.defineHypercolumn(scratchDataCol, 3, dataCols, coordCols, idCols); msDesc.defineHypercolumn("CorrectedDataColumn", 3, dataCols); } { Vector dataCols(1); dataCols(0) = MeasurementSet::columnName(MeasurementSet::SIGMA); const Vector coordCols(0); const Vector idCols(1, sigmaTileId); //msDesc.defineHypercolumn(sigmaCol, 2, dataCols, coordCols, idCols); msDesc.defineHypercolumn("SigmaColumn", 2, dataCols); } { Vector dataCols(1); dataCols(0) = MeasurementSet::columnName(MeasurementSet::WEIGHT); const Vector coordCols(0); const Vector idCols(1, sigmaTileId); // msDesc.defineHypercolumn(sigmaCol, 2, dataCols, coordCols, idCols); msDesc.defineHypercolumn("WeightColumn", 2, dataCols); } { Vector dataCols(1); dataCols(0) = MeasurementSet::columnName(MeasurementSet::FLAG_CATEGORY); const Vector coordCols(0); const Vector idCols(1, flagTileId); // msDesc.defineHypercolumn(flagCol, 4, dataCols, coordCols, idCols); msDesc.defineHypercolumn(flagCol, 4, dataCols); } SetupNewTable newMS(MSName, msDesc, Table::New); // Set the default Storage Manager to be the Incr one { IncrementalStMan incrStMan ("ismdata"); newMS.bindAll(incrStMan, True); } // Bind ANTENNA1, and ANTENNA2 to the standardStMan // as they may change sufficiently frequently to make the // incremental storage manager inefficient for these columns. { StandardStMan ssm(32768); newMS.bindColumn(MS::columnName(MS::ANTENNA1), ssm); newMS.bindColumn(MS::columnName(MS::ANTENNA2), ssm); } IPosition tileShape(3, 4, 100, 100); // These columns contain the bulk of the data so save them in a tiled way { TiledShapeStMan dataMan(dataCol, tileShape); newMS.bindColumn(MeasurementSet:: columnName(MeasurementSet::DATA), dataMan); } { TiledShapeStMan dataMan("FlagColumn", tileShape); newMS.bindColumn(MeasurementSet:: columnName(MeasurementSet::FLAG), dataMan); } { TiledShapeStMan dataMan("ModelDataColumn", tileShape); newMS.bindColumn(MeasurementSet:: columnName(MeasurementSet::MODEL_DATA), dataMan); } { TiledShapeStMan dataMan("CorrectedDataColumn", tileShape); newMS.bindColumn(MeasurementSet:: columnName(MeasurementSet::CORRECTED_DATA), dataMan); } { TiledShapeStMan dataMan("SigmaColumn", IPosition(2,tileShape(0), tileShape(2))); newMS.bindColumn(MeasurementSet:: columnName(MeasurementSet::SIGMA), dataMan); } { TiledShapeStMan dataMan("WeightColumn", IPosition(2,tileShape(0), tileShape(2))); newMS.bindColumn(MeasurementSet:: columnName(MeasurementSet::WEIGHT), dataMan); } { TiledShapeStMan dataMan(flagCol, IPosition(4,tileShape(0),tileShape(1), 1, tileShape(2))); newMS.bindColumn(MeasurementSet:: columnName(MeasurementSet::FLAG_CATEGORY), dataMan); // newMS.bindColumn(flagTileId, dataMan); } // Now we can create the MeasurementSet and add the (empty) subtables ms_p.reset (new MeasurementSet(newMS,0)); ms_p->createDefaultSubtables(Table::New); // add the SOURCE table [copied from SimpleSimulator] TableDesc tdesc; // = MSSource::requiredTableDesc(); for (uInt i = 1 ; i<=MSSource::NUMBER_PREDEFINED_COLUMNS; i++) { MSSource::addColumnToDesc(tdesc, MSSource::PredefinedColumns(i)); } MSSource::addColumnToDesc(tdesc, MSSourceEnums::REST_FREQUENCY, 1); SetupNewTable sourceSetup(ms_p->sourceTableName(),tdesc,Table::New); ms_p->rwKeywordSet().defineTable(MS::keywordName(MS::SOURCE), Table(sourceSetup)); // // add the STATE table [copied from SimpleSimulator] // TableDesc tdesc2; // for (uInt i = 1 ; i<=MSState::NUMBER_PREDEFINED_COLUMNS; i++) { // MSState::addColumnToDesc(tdesc2, MSState::PredefinedColumns(i)); // } // SetupNewTable stateSetup(ms_p->stateTableName(),tdesc2,Table::New); // ms_p->rwKeywordSet().defineTable(MS::keywordName(MS::STATE), // Table(stateSetup)); // // intialize the references to the subtables just added ms_p->initRefs(); // ms_p->flush(); // Set the TableInfo { TableInfo& info(ms_p->tableInfo()); info.setType(TableInfo::type(TableInfo::MEASUREMENTSET)); info.setSubType(String("simulator")); info.readmeAddLine ("This is a MeasurementSet Table holding simulated astronomical observations"); } // Now we can make the accessors to be used when adding hypercolumns /* dataAcc_p = TiledDataStManAccessor(*ms_p, dataCol); scratchDataAcc_p = TiledDataStManAccessor(*ms_p, scratchDataCol); sigmaAcc_p = TiledDataStManAccessor(*ms_p, sigmaCol); flagAcc_p = TiledDataStManAccessor(*ms_p, flagCol); */ // We're done - wasn't that easy? dataWritten_p=0.0; hyperCubeID_p=-1; lastSpWID_p=-1; lastNchan_p=-1; hasHyperCubes_p=False; } NewMSSimulator::NewMSSimulator(MeasurementSet& theMS) : dataAcc_p(), scratchDataAcc_p(), sigmaAcc_p(), flagAcc_p(), maxData_p(2e9) { LogIO os(LogOrigin("NewMSSimulator", "NewMSSimulator(MeasurementSet& theMS)", WHERE)); defaults(); ms_p.reset (new MeasurementSet(theMS)); os << "Opening MeasurementSet " << ms_p->tableName() << " with " << ms_p->nrow() << " rows" << LogIO::POST; dataWritten_p=ms_p->nrow(); TableDesc td(ms_p->tableDesc()); if(td.isColumn(dataTileId)) { hasHyperCubes_p=True; // Now we can make the accessors to be used when adding hypercolumns dataAcc_p = TiledDataStManAccessor(*ms_p, dataCol); scratchDataAcc_p = TiledDataStManAccessor(*ms_p, scratchDataCol); sigmaAcc_p = TiledDataStManAccessor(*ms_p, sigmaCol); flagAcc_p = TiledDataStManAccessor(*ms_p, flagCol); ScalarColumn hyperCubeIDColumn(*ms_p, dataTileId); hyperCubeID_p=max(hyperCubeIDColumn.getColumn()); os << " last hyper cube ID = " << hyperCubeID_p << LogIO::POST; } else { hasHyperCubes_p=False; } { MSColumns msc(*ms_p); MSSpWindowColumns& spwc=msc.spectralWindow(); lastSpWID_p=spwc.nrow(); lastNchan_p=spwc.chanFreq()(lastSpWID_p-1).nelements(); os << " last spectral window ID = " << lastSpWID_p << LogIO::POST; } } // Add new hypercubes as the shape changes void NewMSSimulator::addHyperCubes(const Int id, const Int nBase, const Int nChan, const Int nCorr) { Record tileId; const uInt chanTiles=(nChan+7)/8; tileId.define(sigmaTileId, static_cast(10*id)); sigmaAcc_p.addHypercube(IPosition(2, nCorr, 0), IPosition(2, nCorr, nBase), tileId); tileId.define(dataTileId, static_cast(10*id+1)); dataAcc_p.addHypercube(IPosition(3, nCorr, nChan, 0), IPosition(3, nCorr, chanTiles, nBase), tileId); tileId.define(scratchDataTileId, static_cast(10*id+2)); scratchDataAcc_p.addHypercube(IPosition(3, nCorr, nChan, 0), IPosition(3, nCorr, chanTiles, nBase), tileId); tileId.define(flagTileId, static_cast(10*id + 3)); flagAcc_p.addHypercube(IPosition(4, nCorr, nChan, nCat, 0), IPosition(4, nCorr, chanTiles, nCat, nBase), tileId); } NewMSSimulator::NewMSSimulator(const NewMSSimulator & mss) { operator=(mss); } void NewMSSimulator::initAnt(const String& telescope, const Vector& x, const Vector& y, const Vector& z, const Vector& dishDiameter, const Vector&, const Vector& mount, const Vector& name, const Vector& padname, const String& coordsystem, const MPosition& mRefLocation) { LogIO os(LogOrigin("NewMSSimulator", "initAnt()", WHERE)); telescope_p=telescope; Int nAnt = x.nelements(); Vector xx( x.nelements() ); Vector yy( x.nelements() ); Vector zz( x.nelements() ); if (coordsystem == "global") { xx = x; yy = y; zz = z; os << "Using global coordinates for the antennas" << LogIO::POST; } else if (coordsystem == "local") { MVAngle mvLong= mRefLocation.getAngle().getValue()(0); MVAngle mvLat= mRefLocation.getAngle().getValue()(1); os << "Using local coordinates for the antennas" << endl << "Reference position = "; os.output().width(13); os << mvLong.string(MVAngle::ANGLE,7); os.output().width(14); os << mvLat.string(MVAngle::DIG2,7); os << LogIO::POST; local2global( xx, yy, zz, mRefLocation, x, y, z); } else if (coordsystem == "longlat") { os << "Using longitude-latitude coordinates for the antennas" << LogIO::POST; longlat2global( xx, yy, zz, mRefLocation, x, y, z); } else { os << LogIO::SEVERE << "Unknown coordinate system type: " << coordsystem << LogIO::POST; } Vector antId(nAnt); Matrix antXYZ(3,nAnt); for (Int i=0; iantenna(); ant.addRow(nAnt); // make nAnt rows Slicer antSlice(IPosition(1,numOfAnt), IPosition(1, numOfAnt+nAnt-1), IPosition(1,1), Slicer::endIsLast ); antc.dishDiameter().putColumnRange(antSlice,dishDiameter); antc.mount().putColumnRange(antSlice, mount); antc.name().putColumnRange(antSlice,name); // antc.offset().putColumnRange(antSlice,offset); antc.position().putColumnRange(antSlice, antXYZ); //antc.station().fillColumn(""); antc.station().putColumnRange(antSlice,padname); antc.flagRow().fillColumn(False); antc.type().fillColumn("GROUND-BASED"); os << "Added rows to ANTENNA table" << LogIO::POST; } //getAnt(telescope_p, nAnt, xyz_p, diam_p, offset_p, mount_p, antName_p, // coordsystem_p, mRefLocation_p)) { bool NewMSSimulator::getAnt(String& telescope, Int& nAnt, Matrix* antXYZ, Vector& antDiam, Vector& /*offset*/, Vector& mount, Vector& name, Vector& padname, String& coordsystem, MPosition& mRefLocation ) { // return already set config info LogIO os(LogOrigin("NewMSSimulator", "getAnt()", WHERE)); MSColumns msc(*ms_p); MSAntennaColumns& antc=msc.antenna(); if(antc.nrow()==0) { os << "Antenna information not yet defined" << LogIO::WARN; return False; } telescope=telescope_p; nAnt=antc.nrow(); if (!antXYZ) antXYZ = new Matrix(3,nAnt); antXYZ->resize(3,nAnt); antc.position().getColumn(*antXYZ); antc.dishDiameter().getColumn(antDiam); //antc.offset().getColumn(offset); antc.mount().getColumn(mount); antc.name().getColumn(name); antc.station().getColumn(padname); coordsystem="global"; MVPosition mvzero(0.,0.,0.); MPosition mzero(mvzero,MPosition::ITRF); mRefLocation=mzero; return True; } void NewMSSimulator::local2global(Vector& xGeo, Vector& yGeo, Vector& zGeo, const MPosition& mRefLocation, const Vector& xLocal, const Vector& yLocal, const Vector& zLocal) { uInt nn = xLocal.nelements(); xGeo.resize(nn); yGeo.resize(nn); zGeo.resize(nn); MPosition::Convert loc2(mRefLocation, MPosition::ITRF); MPosition locitrf(loc2()); Vector xyz = locitrf.get("m").getValue(); Vector ang = locitrf.getAngle("rad").getValue(); Double d1, d2; d1 = ang(0); d2 = ang(1); Double cosLong = cos(d1); Double sinLong = sin(d1); Double cosLat = cos(d2); Double sinLat = sin(d2); for (uInt i=0; i< nn; i++) { Double xG1 = -sinLat * yLocal(i) + cosLat * zLocal(i); Double yG1 = xLocal(i); xGeo(i) = cosLong * xG1 - sinLong * yG1 + xyz(0); yGeo(i) = sinLong * xG1 + cosLong * yG1 + xyz(1); zGeo(i) = cosLat * yLocal(i) + sinLat * zLocal(i) + xyz(2); } } void NewMSSimulator::longlat2global(Vector&, Vector&, Vector&, const MPosition&, const Vector&, const Vector&, const Vector&) { LogIO os(LogOrigin("NewMSSimulator", "longlat2global()", WHERE)); os << LogIO::SEVERE << "NewMSSimulator::longlat2global not yet implemented" << LogIO::POST; } void NewMSSimulator::initFields(const String& sourceName, const MDirection& sourceDirection, const String& calCode) { LogIO os(LogOrigin("MSsimulator", "initFields()", WHERE)); MSColumns msc(*ms_p); MSFieldColumns& fieldc=msc.field(); Int baseFieldID=fieldc.nrow(); MSSourceColumns& sourcec=msc.source(); Int baseSrcID=sourcec.nrow(); const double forever=1.e30; // os << "Creating new field table row for " << sourceName << ", ID " // << baseFieldID << LogIO::POST; // if(!ms_p->source().isNull()){ // os << "Creating new source table row for " << sourceName << ", ID " // << baseSrcID << LogIO::DEBUG1; // } ms_p->field().addRow(1); //SINGLE DISH CASE fieldc.name().put(baseFieldID, sourceName); fieldc.code().put(baseFieldID, calCode); fieldc.time().put(baseFieldID, 0.0); fieldc.numPoly().put(baseFieldID, 0); fieldc.sourceId().put(baseFieldID, baseSrcID); Vector direction(1); direction(0)=sourceDirection; fieldc.delayDirMeasCol().put(baseFieldID,direction); fieldc.phaseDirMeasCol().put(baseFieldID,direction); fieldc.referenceDirMeasCol().put(baseFieldID,direction); ms_p->source().addRow(1); //SINGLE DISH CASE sourcec.name().put(baseSrcID, sourceName); sourcec.code().put(baseSrcID, calCode); sourcec.timeMeas().put(baseSrcID, mRefTime_p); sourcec.interval().put(baseSrcID, forever); sourcec.sourceId().put(baseSrcID, baseSrcID); sourcec.directionMeas().put(baseSrcID,sourceDirection); sourcec.spectralWindowId().put(baseSrcID,-1); Vector pmV(2,0.); sourcec.properMotion().put(baseSrcID,pmV); sourcec.numLines().put(baseSrcID, 0); sourcec.calibrationGroup().put(baseSrcID, 0); if(!sourcec.sourceModel().isNull()){ // The following commented out code is an example of how to insert component list tables into the sourceModel column // RecordDesc smRecDesc; // smRecDesc.addField("model", TpTable); // TableRecord sm(smRecDesc, RecordInterface::Variable); // // create a dummy table to fill in // TableDesc td("", "1", TableDesc::Scratch); // SetupNewTable newtab("dummyModel", td, Table::New); // Table tab(newtab); // sm.defineTable("model", tab); // // Note: If there actually is a source model, it has to be a Table file on disk. // // A table object for it has to be crated here and put into the TableRecord // // instead of the "tab" above. DP // sourcec.sourceModel().put(baseSrcID, sm); // but since we don't have models at the moment, I am removing this column ms_p->source().removeColumn("SOURCE_MODEL"); } MSSpWindowColumns& spwc=msc.spectralWindow(); if(spwc.nrow()>0) { sourcec.numLines().put(baseSrcID, 1); sourcec.spectralWindowId().put(baseSrcID,0); Vector rfV(1, spwc.refFrequency()(0)); sourcec.restFrequency().put(baseSrcID, rfV); Vector tV(1,"X"); sourcec.transition().put(baseSrcID, tV); Vector svV(1, 0.); sourcec.sysvel().put(baseSrcID, svV); } } bool NewMSSimulator::getFields(Int& nField, Vector& sourceName, Vector& sourceDirection, Vector& calCode) { LogIO os(LogOrigin("MSsimulator", "getFields()", WHERE)); // os << sourceName_p // << " " << formatDirection(sourceDirection_p) // << " " << calCode_p MSColumns msc(*ms_p); MSFieldColumns& fieldc=msc.field(); nField=fieldc.nrow(); sourceName.resize(nField); sourceDirection.resize(nField); calCode.resize(nField); for (Int i=0;i direction; fieldc.referenceDirMeasCol().get(i,direction,True); // and if theres a varying reference direction per row, we'll just all go // merrily off into lala land. sourceDirection[i]=direction[0]; } return (nField>0); } std::shared_ptr NewMSSimulator::getMs () const { return ms_p; } void NewMSSimulator::initSpWindows(const String& spWindowName, const Int& nChan, const Quantity& startFreq, const Quantity& freqInc, const Quantity&, const MFrequency::Types& freqType, const String& stokesString) { LogIO os(LogOrigin("MSsimulator", "initSpWindows()", WHERE)); Vector stokesTypes(4); stokesTypes=Stokes::Undefined; String myStokesString = stokesString; Int nCorr=0; for (Int j=0; j<4; j++) { while (myStokesString.at(0,1) == " ") { myStokesString.del(0,1); } if (myStokesString.length() == 0) break; stokesTypes(j) = Stokes::type( myStokesString.at(0, 2) ); myStokesString.del(0,2); nCorr = j+1; if (stokesTypes(j)==Stokes::Undefined) { os<< " Undefined polarization type in input"<spectralWindow().addRow(1); ms_p->polarization().addRow(1); ms_p->dataDescription().addRow(1); spwc.numChan().put(baseSpWID,nChan); spwc.name().put(baseSpWID,spWindowName); spwc.netSideband().fillColumn(1); spwc.ifConvChain().fillColumn(0); spwc.freqGroup().fillColumn(0); spwc.freqGroupName().fillColumn("Group 1"); spwc.flagRow().fillColumn(False); // spwc.measFreqRef().fillColumn(MFrequency::TOPO); spwc.measFreqRef().fillColumn(freqType); polc.flagRow().fillColumn(False); ddc.flagRow().fillColumn(False); polc.numCorr().put(baseSpWID, nCorr); Vector freqs(nChan), bandwidth(nChan); bandwidth=freqInc.getValue("Hz"); ddc.spectralWindowId().put(baseSpWID,baseSpWID); ddc.polarizationId().put(baseSpWID,baseSpWID); Double vStartFreq(startFreq.getValue("Hz")); Double vFreqInc(freqInc.getValue("Hz")); for (Int chan=0; chan corrProduct(uInt(2),uInt(nCorr)); Fallible fi; stokesTypes.resize(nCorr, True); for (Int j=0; j< nCorr; j++) { fi=Stokes::receptor1(Stokes::type(stokesTypes(j))); corrProduct(0,j)=(fi.isValid() ? fi.value() : 0); fi=Stokes::receptor2(Stokes::type(stokesTypes(j))); corrProduct(1,j)=(fi.isValid() ? fi.value() : 0); } spwc.refFrequency().put(baseSpWID,vStartFreq); spwc.chanFreq().put(baseSpWID,freqs); spwc.chanWidth().put(baseSpWID,bandwidth); spwc.effectiveBW().put(baseSpWID,bandwidth); spwc.resolution().put(baseSpWID,bandwidth); spwc.totalBandwidth().put(baseSpWID,nChan*vFreqInc); polc.corrType().put(baseSpWID,stokesTypes); polc.corrProduct().put(baseSpWID,corrProduct); { MSSpWindowColumns msSpW(ms_p->spectralWindow()); Int nSpw=ms_p->spectralWindow().nrow(); if(nSpw==0) nSpw=1; Matrix selection(2,nSpw); selection.row(0)=0; //start selection.row(1)=msSpW.numChan().getColumn(); ArrayColumn mcd(*ms_p,"MODEL_DATA"); mcd.rwKeywordSet().define("CHANNEL_SELECTION",selection); } } // if known already e.g. from openfromms() bool NewMSSimulator::getSpWindows(Int& nSpw, Vector& spWindowName, Vector& nChan, Vector& startFreq, Vector& freqInc, Vector& stokesString) { LogIO os(LogOrigin("MSsimulator", "getSpWindows()", WHERE)); MSColumns msc(*ms_p); MSSpWindowColumns& spwc=msc.spectralWindow(); //MSDataDescColumns& ddc=msc.dataDescription(); MSPolarizationColumns& polc=msc.polarization(); nSpw=spwc.nrow(); spWindowName.resize(nSpw); nChan.resize(nSpw); startFreq.resize(nSpw); freqInc.resize(nSpw); stokesString.resize(nSpw); Int nPols(polc.nrow()); Vector stokes(4); for (Int i=0;i x; Vector y; Vector pol; initFeeds(mode, x, y, pol); } // NOTE: initAnt and initSpWindows must be called before this one! void NewMSSimulator::initFeeds(const String& mode, const Vector& x, const Vector& y, const Vector& pol) { LogIO os(LogOrigin("MSsimulator", "initFeeds()", WHERE)); MSColumns msc(*ms_p); MSAntennaColumns& antc=msc.antenna(); Int nAnt=antc.nrow(); if (nAnt <= 0) { os << LogIO::SEVERE << "NewMSSimulator::initFeeds: must call initAnt() first" << LogIO::POST; } Int nFeed=x.nelements(); // cout << "nFeed = " << nFeed << endl; String feedPol0="R", feedPol1="L"; Bool isList=False; if(nFeed>1) { isList=True; if(x.nelements()!=y.nelements()) { os << "Feed x and y must be the same length" << LogIO::EXCEPTION; } if(pol.nelements()!=x.nelements()) { os << "Feed polarization list must be same length as the number of positions" << LogIO::EXCEPTION; } os << "Constructing FEED table from list" << LogIO::POST; } else { nFeed=1; // mode == "perfect R L" OR "perfect X Y" if (mode.contains("X", 0)) { feedPol0 = "X"; feedPol1 = "Y"; } } //cout << "Mode in initFeeds = " << mode << endl; //cout << "feedPol0,1 = " << feedPol0 << " " << feedPol1 << endl; Int nRow=nFeed*nAnt; Vector feedAntId(nRow); Vector feedId(nRow); Vector feedSpWId(nRow); Vector feedBeamId(nRow); Vector feedNumRec(nRow); Cube beamOffset(2,2,nRow); Matrix feedPol(2,nRow); Matrix feedXYZ(3,nRow); Matrix feedAngle(2,nRow); Cube polResp(2,2,nRow); rownr_t iRow=0; if(isList) { polResp=Complex(0.0,0.0); for (Int i=0; ifeed().addRow(nRow); feedc.antennaId().putColumnRange(feedSlice,feedAntId); feedc.feedId().putColumnRange(feedSlice,feedId); feedc.spectralWindowId().putColumnRange(feedSlice,feedSpWId); feedc.beamId().putColumnRange(feedSlice,feedBeamId); feedc.numReceptors().putColumnRange(feedSlice, feedNumRec); feedc.position().putColumnRange(feedSlice, feedXYZ); const double forever=1.e30; for (Int i=numFeeds; i<(nRow+numFeeds); i++) { feedc.beamOffset().put(i,beamOffset.xyPlane(i-numFeeds)); feedc.polarizationType().put(i,feedPol.column(i-numFeeds)); feedc.polResponse().put(i,polResp.xyPlane(i-numFeeds)); feedc.receptorAngle().put(i,feedAngle.column(i-numFeeds)); feedc.time().put(i, 0.0); feedc.interval().put(i, forever); } os << "Added rows to FEED table" << LogIO::POST; } bool NewMSSimulator::getFeedMode(String& mode) { LogIO os(LogOrigin("MSsimulator", "getFeedMode()", WHERE)); MSColumns msc(*ms_p); MSAntennaColumns& antc=msc.antenna(); Int nAnt=antc.nrow(); if (nAnt <= 0) { os << LogIO::SEVERE << "NewMSSimulator::getFeeds: must call initAnt() first" << LogIO::POST; } MSFeedColumns& feedc=msc.feed(); Int numFeeds=feedc.nrow(); if (numFeeds>nAnt) mode="list"; else { if (numFeeds<1) return False; // quick and dirty - assume all ants the same kind Vector feedPol(2); feedc.polarizationType().get(0,feedPol,True); // we only support setting perfect feeds in Simulator. Int nF = feedPol.shape()[0]; if (nF<2) mode=feedPol[0]; else mode=feedPol[0]+" "+feedPol[1]; } return True; } NewMSSimulator::~NewMSSimulator() { } NewMSSimulator & NewMSSimulator::operator=(const NewMSSimulator & other) { if (this==&other) return *this; // copy state... return *this; } void NewMSSimulator::settimes(const Quantity& qIntegrationTime, const Bool useHourAngle, const MEpoch& mRefTime) { LogIO os(LogOrigin("NewMSSimulator", "settimes()", WHERE)); qIntegrationTime_p=qIntegrationTime; useHourAngle_p=useHourAngle; mRefTime_p=mRefTime; if(useHourAngle_p) { hourAngleDefined_p=False; } t_offset_p=0.0; } // old interface: void NewMSSimulator::observe(const String& sourceName, const String& spWindowName, const Quantity& qStartTime, const Quantity& qStopTime, const Bool add_observation, const Bool state_sig, const Bool state_ref, const double& state_cal, const double& state_load, const unsigned int state_sub_scan, const String& state_obs_mode, const String& observername, const String& projectname) { Vector sourceNames(1,sourceName); Vector qStartTimes(1,qStartTime); Vector qStopTimes(1,qStopTime); Vector directions; // constructs Array(IPosition(1,0)) NewMSSimulator::observe(sourceNames,spWindowName,qStartTimes,qStopTimes,directions, add_observation,state_sig,state_ref,state_cal,state_load,state_sub_scan,state_obs_mode,observername,projectname); } // new interface: void NewMSSimulator::observe(const Vector& sourceNames, const String& spWindowName, const Vector& qStartTimes, const Vector& qStopTimes, const Vector& directions, const Bool add_observation, const Bool state_sig, const Bool state_ref, const double& state_cal, const double& state_load, const unsigned int state_sub_scan, const String& state_obs_mode, const String& observername, const String& projectname) { // It is assumed that if there are multiple pointings, that they // are in chronological order. There is not (yet) any checking that // e.g. startTimes[2] not be less than stopTimes[1] //LogIO os(LogOrigin("NewMSSimulator", "observe()", WHERE)); LogIO os(LogOrigin("NewMSSimulator", "observe()")); MSColumns msc(*ms_p); // Do we have antenna information? MSAntennaColumns& antc=msc.antenna(); if(antc.nrow()==0) { os << "Antenna information not yet defined" << LogIO::EXCEPTION; } Int nAnt=antc.nrow(); Vector antDiam; antc.dishDiameter().getColumn(antDiam); Matrix antXYZ(3,nAnt); antc.position().getColumn(antXYZ); MSDerivedValues msd; msd.setAntennas(msc.antenna()); // Do we have feed information? MSFeedColumns& feedc=msc.feed(); if(feedc.nrow()==0) { os << "Feed information not yet defined" << LogIO::EXCEPTION; } Int nFeed=feedc.nrow()/nAnt; // Spectral window MSSpWindowColumns& spwc=msc.spectralWindow(); if(spwc.nrow()==0) { os << "Spectral window information not yet defined" << LogIO::EXCEPTION; } Int baseSpWID=spwc.nrow(); Int existingSpWID=-1; // Check for existing spectral window with correct name if(baseSpWID>0) { Vector spWindowNames; spwc.name().getColumn(spWindowNames); for(uInt i=0;i resolution; spwc.refFrequency().get(baseSpWID,startFreq); spwc.resolution().get(baseSpWID,resolution); Int nChan=resolution.nelements(); Matrix corrProduct; polc.corrProduct().get(baseSpWID,corrProduct); Int nCorr=corrProduct.ncolumn(); // { // ostringstream oss; // oss << "Spectral window : "<0) { Vector fieldNames; fieldc.name().getColumn(fieldNames); for(uInt i=0;i fcs(1); fieldc.phaseDirMeasCol().get(existingFieldID,fcs); msd.setFieldCenter(fcs(0)); MDirection fieldCenter=fcs(0); { os << "First source: "<< sourceNames[iSrc] << " @ " << formatDirection(fieldCenter) << endl; } // A bit ugly solution to extract the information about beam offsets Cube > beam_offsets; Vector antenna_mounts; { // to close MSIter, when the job is done MSFeedParameterExtractor msfpe_tmp(*ms_p); beam_offsets=msfpe_tmp.getBeamOffsets(); antenna_mounts=msfpe_tmp.antennaMounts(); } if (beam_offsets.nplane()!=(uInt)nFeed || beam_offsets.ncolumn()!=(uInt)nAnt) os<< "Feed table format is incompatible with existing code of NewMSSimulator::observe"< timeRange(2); timeRange(0)=Tstart; timeRange(1)=Tend; obsc.timeRange().put(nobsrow,timeRange); obsc.observer().put(nobsrow,observername); obsc.project().put(nobsrow,projectname); nobsrow= obsc.nrow(); Vector tmpids(row+1); tmpids=msc.observationId().getColumn(); if (tmpids.nelements()>0) maxObsId=max(tmpids); tmpids=msc.arrayId().getColumn(); if (tmpids.nelements()>0) maxArrayId=max(tmpids); //cout << "OBSERVATION table added to; nobsrow now ="<< nobsrow < state(); MSStateColumns& msstateCol = msc.state(); Int staterow = -1; // the state row to use in the main table //cout << "STATE table has " <0)&&(dataWritten_p>maxData_p)) { needNewHyperCube=True; } } if(needNewHyperCube) { hyperCubeID_p++; // os << "Creating new hypercube " << hyperCubeID_p+1 << LogIO::DEBUG1; addHyperCubes(hyperCubeID_p, nBaselines, nChan, nCorr); dataWritten_p=0; lastSpWID_p=baseSpWID; lastNchan_p=nChan; } // ... Next extend the table // os << "Adding " << nNewRows << " rows" << LogIO::DEBUG1; ms_p->addRow(nNewRows); // ... Finally extend the hypercubes if(hasHyperCubes_p) { Record tileId; tileId.define(sigmaTileId, static_cast(10*hyperCubeID_p)); sigmaAcc_p.extendHypercube(nNewRows, tileId); tileId.define(dataTileId, static_cast(10*hyperCubeID_p + 1)); dataAcc_p.extendHypercube(nNewRows, tileId); tileId.define(scratchDataTileId, static_cast(10*hyperCubeID_p + 2)); scratchDataAcc_p.extendHypercube(nNewRows, tileId); tileId.define(flagTileId, static_cast(10*hyperCubeID_p + 3)); flagAcc_p.extendHypercube(nNewRows, tileId); // Size of scratch columns Double thisChunk=16.0*Double(nChan)*Double(nCorr)*Double(nNewRows); dataWritten_p+=thisChunk; // os << "Written " << thisChunk/(1024.0*1024.0) << " Mbytes to scratch columns" << LogIO::DEBUG1; } Matrix data(nCorr,nChan); data.set(Complex(0.0)); Matrix flag(nCorr,nChan); flag=False; os << "Calculating a total of " << nIntegrations << " integrations" << endl << LogIO::POST; for(Int feed=0; feed beamOffset=beam_offsets(0,0,feed); for(Int pointing=0; pointing0) { Vector fieldNames; fieldc.name().getColumn(fieldNames); for(uInt i=0;i0) { if (not(directions(pointing).getAngle()==northPole.getAngle())) { fcs=directions(pointing); } } msd.setFieldCenter(fcs(0)); fieldCenter=fcs(0); if (pointing<20) { os << LogIO::DEBUG1 << " Field " << pointing << ": " << sourceNames(pointing) << " @ " << formatDirection(fieldCenter) << " for " << nIntegrations << " integrations " << endl; } else { if (pointing==20) { os << LogIO::DEBUG1 << " (continuing without printing to log -- see MS for details) " << endl << LogIO::POST; } } for(Int integration=0; integration isShadowed(nAnt); isShadowed.set(False); Vector isTooLow(nAnt); isTooLow.set(False); Double fractionBlocked1=0.0, fractionBlocked2=0.0; Int64 startingRow = row; Double diamMax2 = square( max(antDiam) ); // fringe stopping center could be different for different feeds MDirection feed_phc=fcs(0); // Do the first row outside the loop msc.scanNumber().put(row+1,scan); msc.fieldId().put(row+1,existingFieldID); msc.dataDescId().put(row+1,baseSpWID); msc.time().put(row+1,Time+Tint/2); msc.timeCentroid().put(row+1,Time+Tint/2); msc.arrayId().put(row+1,maxArrayId); msc.processorId().put(row+1,0); msc.exposure().put(row+1,Tint); msc.interval().put(row+1,Tint); msc.observationId().put(row+1,maxObsId+1); msc.stateId().put(row+1,staterow); // assume also that all mounts are the same and posit. angle is the same if (antenna_mounts[0]=="ALT-AZ" || antenna_mounts[0]=="alt-az") { // parallactic angle rotation is necessary SquareMatrix xform(SquareMatrix::General); // SquareMatrix' default constructor is a bit strange, we probably // need to change it in the future const Double pa=msd.parAngle(); const Double cpa=cos(pa); const Double spa=sin(pa); xform(0,0)=cpa; xform(1,1)=cpa; xform(0,1)=-spa; xform(1,0)=spa; beamOffset*=xform; } // x direction is flipped to convert az-el type frame to ra-dec feed_phc.shift(-beamOffset(0),beamOffset(1),True); ///Below code is replaced with calcUVW that does a baseline conversion ///to J2000 too // Double ra, dec; // current phase center // ra = feed_phc.getAngle().getValue()(0); // dec = feed_phc.getAngle().getValue()(1); // Transformation from antenna position difference (ant2-ant1) to uvw // Double H0 = gmst-ra, sH0=sin(H0), cH0=cos(H0), sd=sin(dec), cd=cos(dec); // Matrix trans(3,3,0); // trans(0,0) = -sH0; trans(0,1) = -cH0; // trans(1,0) = sd*cH0; trans(1,1) = -sd*sH0; trans(1,2) = -cd; // trans(2,0) = -cd*cH0; trans(2,1) = cd*sH0; trans(2,2) = -sd; // Matrix antUVW(3,nAnt); // for (Int ant1=0; ant1 antUVW(3,nAnt); calcAntUVW(ep, feed_phc, antUVW); for(Int ant1=0; ant10.0) startAnt2=ant1; for (Int ant2=startAnt2; ant2 uvwvec(3); uvwvec(0) = x2-x1; uvwvec(1) = y2-y1; uvwvec(2) = z2-z1; msc.uvw().put(row,uvwvec); data.set(Complex(0.,0.)); msc.data().put(row,data); msc.data().put(row,data); msc.flag().put(row,flag); msc.flagRow().put(row,False); msc.correctedData().setShape(row, data.shape()); msc.correctedData().put(row,data); msc.modelData().setShape(row,data.shape()); msc.modelData().put(row, data); if (ant1 != ant2) { blockage(fractionBlocked1, fractionBlocked2, uvwvec, antDiam(ant1), antDiam(ant2) ); if (fractionBlocked1 > fractionBlockageLimit_p) { isShadowed(ant1) = True; } if (fractionBlocked2 > fractionBlockageLimit_p) { isShadowed(ant2) = True; } } // Deal with differing diameter case Float sigma1 = diamMax2/(antDiam(ant1) * antDiam(ant2)); Float wt = 1/square(sigma1); if (ant1 == ant2 ) { wt *= autoCorrelationWt_p; } Vector tmp(nCorr); tmp=wt; msc.weight().put(row, tmp); tmp=sigma1; msc.sigma().put(row,tmp); } } // go back and flag weights based on shadowing // Future option: we could increase sigma based on // fraction shadowed. Matrix trueFlag(nCorr,nChan); trueFlag=True; Int64 reRow = startingRow; for (Int ant1=0; ant10.0) startAnt2=ant1; for (Int ant2=startAnt2; ant2 azel(2); for (Int ant1=0; ant10.0) startAnt2=ant1; for (Int ant2=startAnt2; ant2pointing().addRow(numpointrows); numpointrows += numPointing; Double Tint=qIntegrationTime_p.getValue("s"); Vector direction(1); direction(0)=fieldCenter; for (Int m=numPointing; m < (numPointing+nAnt); m++){ pointingc.numPoly().put(m,0); pointingc.interval().put(m,-1); pointingc.tracking().put(m,True); ///pointingc.time().put(m,Time); pointingc.time().put(m,Time+Tint/2); pointingc.timeOrigin().put(m,Tstart); pointingc.interval().put(m,Tint); pointingc.antennaId().put(m, m-numPointing); pointingc.directionMeasCol().put(m,direction); pointingc.targetMeasCol().put(m,direction); } Time+=Tint; // also reset at the start of each pointing } // time ranges } // feeds } // pointings { msd.setAntenna(0); Vector azel=msd.azel().getAngle("rad").getValue("rad"); // sadly, messages from Core don't get filtered very well by casapy. // Double ha1 = msd.hourAngle() * 180.0/C::pi / 15.0; // os << "Stopping conditions for antenna 1: " << LogIO::DEBUG1; // os << " time = " << formatTime(Time) << LogIO::DEBUG1; // os << " scan = " << scan+1 << LogIO::DEBUG1; // os << " az = " << azel(0) * 180.0/C::pi << " deg" << LogIO::DEBUG1; // os << " el = " << azel(1) * 180.0/C::pi << " deg" << LogIO::DEBUG1; // os << " ha = " << ha1 << " hours" << LogIO::DEBUG1; } // os << (row+1) << " visibilities simulated " << LogIO::DEBUG1; // os << nShadowed << " visibilities flagged due to shadowing " << LogIO::DEBUG1; // os << nSubElevation << " visibilities flagged due to elevation limit of " << // elevationLimit_p.getValue("deg") << " degrees " << endl << LogIO::DEBUG1; } // Calculates the fractional blockage of one antenna by another // We will want to put this somewhere else eventually, but I don't yet know where! // Till then. // Stolen from Fred Schwab void NewMSSimulator::blockage(Double &fraction1, Double &fraction2, const Vector& uvw, const Double diam1, const Double diam2) { Double separation = sqrt( square(uvw(0)) + square(uvw(1)) ); Double rmin = 0.5 * min(abs(diam1),abs(diam2)); Double rmax = 0.5 * max(abs(diam1),abs(diam2)); if (separation >= (rmin+rmax)) { fraction1 = 0.0; fraction2 = 0.0; } else if ( (separation+rmin) <= rmax) { fraction1 = min(1.0, square(abs(diam2)/abs(diam1))); fraction2 = min(1.0, square(abs(diam1)/abs(diam2))); } else { Double c = separation/(0.5 * abs(diam1)); Double s=abs(diam2)/abs(diam1); Double sinb=sqrt(2.0 * (square(c*s)+square(c)+square(s))-pow(c,4.0)-pow(s,4.0)-1.0) /(2.0 * c); Double sina=sinb/s; // Due to roundoff, sina or sinb might be ever so slightly larger than 1 // in the case of unequal radii, with the center of one antenna pattern // inside the other: sinb=min(1.0, sinb); sina=min(1.0, sina); Double b=asin(sinb); Double a=asin(sina); Double area=(square(s)*a+b)-(square(s)*sina*cos(a)+sinb*cos(b)); fraction1 = area/M_PI; fraction2 = fraction1/square(s); } // if antenna1 is in behind, w is > 0, 2 is NOT shadowed if (uvw(2) > 0.0) fraction2 = 0.0; // if antenna1 is in front, w is < 0, 1 is NOT shadowed if (uvw(2) < 0.0) fraction1 = 0.0; return; } String NewMSSimulator::formatDirection(const MDirection& direction) { MVAngle mvRa=direction.getAngle().getValue()(0); MVAngle mvDec=direction.getAngle().getValue()(1); ostringstream oss; oss.setf(ios::left, ios::adjustfield); oss.width(14); oss << mvRa(0.0).string(MVAngle::TIME,8); oss.width(14); oss << mvDec.string(MVAngle::DIG2,8); oss << " " << MDirection::showType(direction.getRef().getType()); return String(oss); } String NewMSSimulator::formatTime(const Double time) { MVTime mvtime(Quantity(time, "s")); return mvtime.string(MVTime::DMY,7); } Bool NewMSSimulator::calcAntUVW(MEpoch& epoch, MDirection& refdir, Matrix& uvwAnt){ MSColumns msc(*ms_p); // Lets define a Measframe with the telescope nominal position MPosition obsPos; if(!MeasTable::Observatory(obsPos, telescope_p)){ //not a known observatory then lets use antenna(0) position...as ref pos //does not matter really as the difference will make the baseline obsPos=msc.antenna().positionMeas()(0); } MVPosition basePos=obsPos.getValue(); MeasFrame measFrame(obsPos); measFrame.set(epoch); measFrame.set(refdir); MVBaseline mvbl; MBaseline basMeas; MBaseline::Ref basref(MBaseline::ITRF, measFrame); basMeas.set(mvbl, basref); basMeas.getRefPtr()->set(measFrame); // going to convert from ITRF vector to J2000 baseline vector I guess ! if(refdir.getRef().getType() != MDirection::J2000) throw(AipsError("Ref direction is not in J2000 ")); Int nAnt=msc.antenna().nrow(); uvwAnt.resize(3,nAnt); MBaseline::Convert elconv(basMeas, MBaseline::Ref(MBaseline::J2000)); Muvw::Convert uvwconv(Muvw(), Muvw::Ref(Muvw::J2000, measFrame)); for(Int k=0; k< nAnt; ++k){ MPosition antpos=msc.antenna().positionMeas()(k); MVBaseline mvblA(obsPos.getValue(), antpos.getValue()); basMeas.set(mvblA, basref); MBaseline bas2000 = elconv(basMeas); MVuvw uvw2000 (bas2000.getValue(), refdir.getValue()); const Vector& xyz = uvw2000.getValue(); uvwAnt.column(k)=xyz; } return True; } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSOper/NewMSSimulator.h000066400000000000000000000230061476623553700201530ustar00rootroot00000000000000//# NewMSSimulator.h: this defines the MeasurementSet Simulator //# Copyright (C) 1995-2009 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_NEWMSSIMULATOR_H #define MS_NEWMSSIMULATOR_H //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MeasurementSet; // // Create an empty MeasurementSet from observation and telescope descriptions. // // // //# Classes you should understand before using this one. //
      • MeasurementSet // // // // MS is from MeasurementSet, Simulator refers to the generation of // 'fake' data from a set of parameters for instrument and sources. // // // // This class creates a MeasurementSet from a set of parameters for instrument // and sources. It does not simulate the data, only the coordinates of a // measurement. The application "simulator" uses this class to create a true // simulated MS with perfect or corrupted data. // // // // To test calibration and imaging programs it is necessary to have flawless // data and data with errors that are known exactly. This class generates // empty MeasurementSets (only coordinates filled in) that can be filled // with predicted data. // // // //# A List of bugs, limitations, extensions or planned refinements. //
      • The amount of information to be specified by the user // could be much larger. For the moment it has been restricted to // what is needed for testing the synthesis imaging code. Already // it is possible to create MeasurementSets that cannot be processed // yet. // class NewMSSimulator { public: // Constructor from name only NewMSSimulator(const String&); // Constructor from existing MS NewMSSimulator(MeasurementSet&); // Copy constructor - for completeness only NewMSSimulator(const NewMSSimulator & mss); //# Destructor ~NewMSSimulator(); //# Operators // Assignment NewMSSimulator & operator=(const NewMSSimulator &); // Set maximum amount of data (bytes) to be written into any one // scratch column hypercube void setMaxData(const Double maxData=2e9) {maxData_p=maxData;} // set the antenna and array data. These are written immediately to the // existing MS. The same model is used for the other init infor. void initAnt(const String& telname, const Vector& x, const Vector& y, const Vector& z, const Vector& dishDiameter, const Vector& offset, const Vector& mount, const Vector& name, const Vector& padname, const String& coordsystem, const MPosition& mRefLocation); // get the info back bool getAnt(String& telescope, Int& nAnt, Matrix* antXYZ, Vector& antDiam, Vector& offset, Vector& mount, Vector& name, Vector& padname, String& coordsystem, MPosition& mRefLocation ); // set the observed fields void initFields(const String& sourceName, const MDirection& sourceDirection, const String& calCode); bool getFields(Int& nField, Vector& sourceName, Vector& sourceDirection, Vector& calCode); // set the Feeds; brain dead version void initFeeds(const String& mode); bool getFeedMode(String& mode); // set the Feeds; Smart version void initFeeds(const String& mode, const Vector& x, const Vector& y, const Vector& pol); // set the spectral windows information void initSpWindows(const String& spWindowName, const Int& nChan, const Quantity& startFreq, const Quantity& freqInc, const Quantity& freqRes, const MFrequency::Types& freqType, const String& stokesString); bool getSpWindows(Int& nSpw, Vector& spWindowName, Vector& nChan, Vector& startFreq, Vector& freqInc, Vector& stokesString); void setFractionBlockageLimit(const Double fraclimit) { fractionBlockageLimit_p = fraclimit; } void setElevationLimit(const Quantity& ellimit) { elevationLimit_p = ellimit; } void setAutoCorrelationWt(const Float autocorrwt) { autoCorrelationWt_p = autocorrwt; } void settimes(const Quantity& qIntegrationTime, const Bool useHourAngles, const MEpoch& mRefTime); void observe(const String& sourceName, const String& spWindowName, const Quantity& qStartTime, const Quantity& qStopTime, const Bool add_observation=True, //# from int ASDM2MSFiller::addUniqueState( //# defaults for ALMA as known on 20100831 const Bool state_sig=True, const Bool state_ref=True, const double& state_cal=0., const double& state_load=0., const unsigned int state_sub_scan=1, const String& state_obs_mode="OBSERVE_TARGET.ON_SOURCE", const String& observername="CASA simulator", const String& projectname="CASA simulation"); void observe(const Vector& sourceNames, const String& spWindowName, const Vector& qStartTimes, const Vector& qStopTimes, const Vector& directions, const Bool add_observation=True, //# from int ASDM2MSFiller::addUniqueState( //# defaults for ALMA as known on 20100831 const Bool state_sig=True, const Bool state_ref=True, const double& state_cal=0., const double& state_load=0., const unsigned int state_sub_scan=1, const String& state_obs_mode="OBSERVE_TARGET.ON_SOURCE", const String& observername="CASA simulator", const String& projectname="CASA simulation"); std::shared_ptr getMs () const; private: // Prevent use of default constructor NewMSSimulator() {} //# Data Members Double fractionBlockageLimit_p; Quantity elevationLimit_p; Float autoCorrelationWt_p; String telescope_p; Quantity qIntegrationTime_p; Bool useHourAngle_p; Bool hourAngleDefined_p; MEpoch mRefTime_p; Double t_offset_p; Double dataWritten_p; Int hyperCubeID_p; Bool hasHyperCubes_p; Int lastSpWID_p; Int lastNchan_p; std::shared_ptr ms_p; TiledDataStManAccessor dataAcc_p, scratchDataAcc_p, sigmaAcc_p, flagAcc_p; Double maxData_p; void local2global(Vector& xReturned, Vector& yReturned, Vector& zReturned, const MPosition& mRefLocation, const Vector& xIn, const Vector& yIn, const Vector& zIn); void longlat2global(Vector& xReturned, Vector& yReturned, Vector& zReturned, const MPosition& mRefLocation, const Vector& xIn, const Vector& yIn, const Vector& zIn); // Returns the fractional blockage of one antenna by another // We will want to put this somewhere else eventually, but I don't yet know where! // Till then. // fraction1: fraction of antenna 1 that is blocked by 2 // fraction2: fraction of antenna 2 that is blocked by 1 // hint: at least one of the two will be 0.0 void blockage(Double &fraction1, Double &fraction2, const Vector& uvw, // uvw in same units as diam! const Double diam1, const Double diam2); String formatDirection(const MDirection&); String formatTime(const Double); void addHyperCubes(const Int id, const Int nBase, const Int nChan, const Int nCorr); void defaults(); Bool calcAntUVW(MEpoch& epoch, MDirection& refdir, Matrix& uvwAnt); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSOper/test/000077500000000000000000000000001476623553700160675ustar00rootroot00000000000000casacore-3.7.1/ms/MSOper/test/CMakeLists.txt000066400000000000000000000005171476623553700206320ustar00rootroot00000000000000set (tests tMSDerivedValues tMSKeys tMSMetaData tMSReader tMSSummary tNewMSSimulator ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_ms) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/ms/MSOper/test/tMSDerivedValues.cc000066400000000000000000000103061476623553700215640ustar00rootroot00000000000000//# tMSDerivedValues: Tests the MSDerivedValues class //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include int main() { try { Quantity longitude; Quantity::read(longitude,"149.33.00.5"); cout << "longitude: "< pos(1); pos(0)=MPosition(Quantity(236.9,"m"),longitude,latitude, MPosition::Ref(MPosition::WGS84)); MSDerivedValues msd; msd.setAntennaPositions(pos); Quantity time; MVTime::read(time,"today"); MEpoch today(time); cout <<" Current time: "< mount(1); mount(0)="alt-az"; msd.setAntennaMount(mount); cout << " Par. angle: "<< MVAngle(msd.parAngle()) << endl; // test the obsVel conversion cout << " observatory velocity in LSR frame: "<< msd.obsVel().get("km/s"). getValue() << "km/s"< #include #include #include #include using namespace std; int main() { try { { cout << "*** test uniqueArrayKeys()" << endl; ScanKey s; s.arrayID = 0; s.obsID = 2; s.scan = 4; set skeys; skeys.insert(s); s.scan = 5; skeys.insert(s); s.arrayID = 1; skeys.insert(s); set aKeys = uniqueArrayKeys(skeys); AlwaysAssert(aKeys.size() == 2, AipsError); set::const_iterator iter = aKeys.begin(); AlwaysAssert(iter->arrayID == 0 && iter->obsID == 2, AipsError); ++iter; AlwaysAssert(iter->arrayID == 1 && iter->obsID == 2, AipsError); } { cout << "*** test ArrayKey==" << endl; ArrayKey first, second; first.arrayID = 4; first.obsID = 6; second.arrayID = 4; second.obsID = 6; AlwaysAssert(first == second, AipsError); second.obsID = 5; AlwaysAssert(first != second, AipsError); second.obsID = 6; second.arrayID = 3; AlwaysAssert(first != second, AipsError); } { cout << "*** test filter" << endl; std::set scanKeys; ScanKey sk; sk.obsID = 0; sk.arrayID = 0; sk.scan = 9; scanKeys.insert(sk); sk.obsID = 1; scanKeys.insert(sk); sk.arrayID = 1; scanKeys.insert(sk); sk.obsID = 0; scanKeys.insert(sk); std::set::const_iterator iter = scanKeys.begin(); std::set::const_iterator end = scanKeys.end(); ArrayKey x; uInt i = 0; ScanKey expec; expec.scan = 9; for (; iter!=end; ++iter, ++i) { x.obsID = i % 2; x.obsID = i/2; x.arrayID = iter->arrayID; std::set filtered = filter(scanKeys, x); AlwaysAssert(filtered.size() == 1, AipsError); expec.obsID = x.obsID; expec.arrayID = x.arrayID; AlwaysAssert(*filtered.begin() == expec, AipsError); } } cout << "OK" << endl; } catch (const std::exception& x) { cerr << "Exception : " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/ms/MSOper/test/tMSMetaData.cc000066400000000000000000003407661476623553700205220ustar00rootroot00000000000000//# tMSMetaData.cc: This program tests the MSMetaData class //# Copyright (C) 2013 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void _printSet(const std::set& set) { const std::set::const_iterator end = set.end(); for ( std::set::const_iterator iter = set.begin(); iter!=end; ++iter ) { if (iter!=set.begin()) { cout << ", "; } cout << *iter; } cout << endl; } void _printSet(const std::set& set) { const std::set::const_iterator end = set.end(); for ( std::set::const_iterator iter = set.begin(); iter!=end; ++iter ) { if (iter!=set.begin()) { cout << ", "; } cout << *iter; } cout << endl; } void testIt(MSMetaData& md) { ArrayKey arrayKey; arrayKey.obsID = 0; arrayKey.arrayID = 0; cout << "*** test nStates()" << endl; AlwaysAssert(md.nStates() == 43, AipsError); cout << "*** cache size " << md.getCache() << endl; cout << "*** test getScansForState()" << endl; for (uInt stateID=0; stateID scans = md.getScansForState(stateID, 0, 0); std::set expec; if (stateID < 5) { uInt myints[]= {1, 5, 8}; expec.insert(myints, myints + 3); } else if (stateID < 7) { expec.insert(2); } else if (stateID < 10) { uInt myints[]= {3, 6, 9, 11, 13, 15, 17, 19, 22, 24, 26, 29, 31}; expec.insert(myints, myints + 13); } else if (stateID < 26) { expec.insert(4); } else if (stateID < 32) { expec.insert(7); } else if (stateID < 33) { uInt myints[] = {10, 14, 18, 21, 25, 28, 32}; expec.insert(myints, myints + 7); } else if (stateID < 37) { uInt myints[] = {12, 16, 20, 23, 27, 30}; expec.insert(myints, myints + 6); } else { uInt myints[] = {12, 16, 20, 23}; expec.insert(myints, myints + 4); } AlwaysAssert(scans == expec, AipsError); } cout << "*** cache size " << md.getCache() << endl; cout << "*** test getIntents()" << endl; cout << "*** size " << md.getIntents().size() << endl; cout << "*** size " << md.getIntents().size() << endl; AlwaysAssert(md.getIntents().size() == 11, AipsError); cout << "*** cache size " << md.getCache() << endl; cout << "*** test getScanNumbers()" << endl; std::set scans = md.getScanNumbers(0, 0); uInt myints[] = { 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 }; { std::set exp; exp.insert(myints, myints+32); AlwaysAssert(scans == exp, AipsError); cout << "*** cache size " << md.getCache() << endl; } std::set uniqueIntents; cout << "*** test getIntentsForScan()" << endl; ScanKey scanKey; scanKey.obsID = 0; scanKey.arrayID = 0; for ( std::set::const_iterator scanNum = scans.begin(); scanNum!=scans.end(); ++scanNum ) { scanKey.scan = *scanNum; std::set intents = md.getIntentsForScan(scanKey); std::set exp; if (*scanNum == 1 || *scanNum == 5 || *scanNum == 8) { String mystr[] = { "CALIBRATE_POINTING#ON_SOURCE", "CALIBRATE_WVR#ON_SOURCE" }; exp.insert(mystr, mystr+2); } else if (*scanNum == 2) { String mystr[] = { "CALIBRATE_SIDEBAND_RATIO#OFF_SOURCE", "CALIBRATE_SIDEBAND_RATIO#ON_SOURCE", "CALIBRATE_WVR#OFF_SOURCE", "CALIBRATE_WVR#ON_SOURCE" }; exp.insert(mystr, mystr+4); } else if ( *scanNum == 3 || *scanNum == 6 || *scanNum == 9 || *scanNum == 11 || *scanNum == 13 || *scanNum == 15 || *scanNum == 17 || *scanNum == 19 || *scanNum == 22 || *scanNum == 24 || *scanNum == 26 || *scanNum == 29 || *scanNum == 31 ) { String mystr[] = { "CALIBRATE_ATMOSPHERE#OFF_SOURCE", "CALIBRATE_ATMOSPHERE#ON_SOURCE", "CALIBRATE_WVR#OFF_SOURCE", "CALIBRATE_WVR#ON_SOURCE" }; exp.insert(mystr, mystr+4); } else if (*scanNum == 4) { String mystr[] = { "CALIBRATE_BANDPASS#ON_SOURCE", "CALIBRATE_PHASE#ON_SOURCE", "CALIBRATE_WVR#ON_SOURCE" }; exp.insert(mystr, mystr+3); } else if (*scanNum == 7) { String mystr[] = { "CALIBRATE_AMPLI#ON_SOURCE", "CALIBRATE_PHASE#ON_SOURCE", "CALIBRATE_WVR#ON_SOURCE" }; exp.insert(mystr, mystr+3); } else if ( *scanNum == 10 || *scanNum == 14 || *scanNum == 18 || *scanNum == 21 || *scanNum == 25 || *scanNum == 28 || *scanNum == 32 ) { String mystr[] = { "CALIBRATE_PHASE#ON_SOURCE", "CALIBRATE_WVR#ON_SOURCE" }; exp.insert(mystr, mystr+2); } else if ( *scanNum == 12 || *scanNum == 16 || *scanNum == 20 || *scanNum == 23 || *scanNum == 27 || *scanNum == 30 ) { exp.insert("OBSERVE_TARGET#ON_SOURCE"); } uniqueIntents.insert(exp.begin(), exp.end()); AlwaysAssert(intents == exp, AipsError); } AlwaysAssert(md.getIntents() == uniqueIntents, AipsError); cout << "*** test getSpwsForIntent()" << endl; for ( auto intent=uniqueIntents.cbegin(); intent!=uniqueIntents.cend(); ++intent ) { std::set exp; if ( *intent == "CALIBRATE_AMPLI#ON_SOURCE" || *intent == "CALIBRATE_BANDPASS#ON_SOURCE" || *intent == "CALIBRATE_PHASE#ON_SOURCE" || *intent == "OBSERVE_TARGET#ON_SOURCE" ) { uInt myints[] = {0, 17, 18, 19, 20, 21, 22, 23, 24}; exp.insert(myints, myints+9); } else if ( *intent == "CALIBRATE_ATMOSPHERE#OFF_SOURCE" || *intent == "CALIBRATE_ATMOSPHERE#ON_SOURCE" || *intent == "CALIBRATE_SIDEBAND_RATIO#OFF_SOURCE" || *intent == "CALIBRATE_SIDEBAND_RATIO#ON_SOURCE" || *intent == "CALIBRATE_WVR#OFF_SOURCE" ) { uInt myints[] = {0, 9, 10, 11, 12, 13, 14, 15, 16}; exp.insert(myints, myints+9); } else if ( *intent == "CALIBRATE_POINTING#ON_SOURCE" ) { uInt myints[] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; exp.insert(myints, myints+9); } else if ( *intent == "CALIBRATE_WVR#ON_SOURCE" ) { uInt myints[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }; exp.insert(myints, myints+25); } AlwaysAssert(md.getSpwsForIntent(*intent) == exp, AipsError); } cout << "*** test nSpw()" << endl; uInt nSpw = md.nSpw(True); AlwaysAssert(nSpw == 40, AipsError); AlwaysAssert(md.nSpw(False) == 40, AipsError); cout << "*** test getIntentsForSpw()" << endl; for (uInt spw=0; spw exp; if (spw == 0) { String mystr[] = { "CALIBRATE_AMPLI#ON_SOURCE", "CALIBRATE_ATMOSPHERE#OFF_SOURCE", "CALIBRATE_ATMOSPHERE#ON_SOURCE", "CALIBRATE_BANDPASS#ON_SOURCE", "CALIBRATE_PHASE#ON_SOURCE", "CALIBRATE_POINTING#ON_SOURCE", "CALIBRATE_SIDEBAND_RATIO#OFF_SOURCE", "CALIBRATE_SIDEBAND_RATIO#ON_SOURCE", "CALIBRATE_WVR#OFF_SOURCE", "CALIBRATE_WVR#ON_SOURCE", "OBSERVE_TARGET#ON_SOURCE" }; exp.insert(mystr, mystr+11); } else if (spw < 9) { String mystr[] = { "CALIBRATE_POINTING#ON_SOURCE", "CALIBRATE_WVR#ON_SOURCE" }; exp.insert(mystr, mystr+2); } else if (spw < 17) { String mystr[] = { "CALIBRATE_ATMOSPHERE#OFF_SOURCE", "CALIBRATE_ATMOSPHERE#ON_SOURCE", "CALIBRATE_SIDEBAND_RATIO#OFF_SOURCE", "CALIBRATE_SIDEBAND_RATIO#ON_SOURCE", "CALIBRATE_WVR#OFF_SOURCE", "CALIBRATE_WVR#ON_SOURCE" }; exp.insert(mystr, mystr+6); } else if (spw < 25) { String mystr[] = { "CALIBRATE_AMPLI#ON_SOURCE", "CALIBRATE_BANDPASS#ON_SOURCE", "CALIBRATE_PHASE#ON_SOURCE", "CALIBRATE_WVR#ON_SOURCE", "OBSERVE_TARGET#ON_SOURCE" }; exp.insert(mystr, mystr+5); } AlwaysAssert(md.getIntentsForSpw(spw) == exp, AipsError); } { cout << "*** test nFields()" << endl; uInt nFields = md.nFields(); AlwaysAssert(nFields == 6, AipsError); cout << "*** test getSpwsForField() and getFieldsToSpwsMap()" << endl; std::map > mymap = md.getFieldsToSpwsMap(); String names[] = { "3C279", "J1337-129", "Titan", "J1625-254", "V866 Sco", "RNO 90" }; for (uInt i=0; i exp; if (i==0 || i==3) { uInt myints[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }; exp.insert(myints, myints+25); } if (i == 1) { uInt myints[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; exp.insert(myints, myints+9); } if (i==2 || i==4 || i==5) { uInt myints[] = { 0, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }; exp.insert(myints, myints+17); } cout << "*** i " << i << " " << md.getSpwsForField(i) << endl; AlwaysAssert(md.getSpwsForField(i) == exp, AipsError); AlwaysAssert(md.getSpwsForField(names[i]) == exp, AipsError); AlwaysAssert(mymap[i] == exp, AipsError); cout << "*** cache size " << md.getCache() << endl; } cout << "*** test phaseDirFromFieldIDAndTime()" << endl; { MDirection phasCen=md.phaseDirFromFieldIDAndTime(2); AlwaysAssert( near(phasCen.getAngle().getValue()[0], -2.72554329 , 5e-7), AipsError ); } cout << "*** test getFieldIDsForSpw()" << endl; for (uInt i=0; i exp; std::set expNames; if (i==0) { uInt myints[] = {0, 1, 2, 3, 4, 5}; exp.insert(myints, myints+6); String mystr[] = { "3C279", "J1337-129", "Titan", "J1625-254", "V866 Sco", "RNO 90" }; expNames.insert(mystr, mystr+6); } else if (i<9) { uInt myints[] = {0, 1, 3}; exp.insert(myints, myints+3); String mystr[] = { "3C279", "J1337-129", "J1625-254" }; expNames.insert(mystr, mystr+3); } else if (i<25) { uInt myints[] = {0, 2, 3, 4, 5}; exp.insert(myints, myints+5); String mystr[] = { "3C279", "Titan", "J1625-254", "V866 Sco", "RNO 90" }; expNames.insert(mystr, mystr+5); } else { // nothing, exp is an empty set } AlwaysAssert(md.getFieldIDsForSpw(i) == exp, AipsError); AlwaysAssert(md.getFieldNamesForSpw(i) == expNames, AipsError); } } { cout << "*** test nScans()" << endl; cout << "nscans " << md.nScans() << endl; AlwaysAssert(md.nScans() == 32, AipsError); std::set scanNumbers = md.getScanNumbers(0, 0); cout << "*** test getSpwsForScan(), getScanToSpwsMap(), getPolarizationIDs()" << endl; std::map > mymap = md.getScanToSpwsMap(); ScanKey scanKey; scanKey.obsID = 0; scanKey.arrayID = 0; for ( std::set::const_iterator scan=scanNumbers.begin(); scan!=scanNumbers.end(); ++scan ) { std::set exp; if (*scan == 1 || *scan==5 || *scan==8) { uInt myints[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; exp.insert(myints, myints+9); } else if ( *scan == 2 || *scan==3 || *scan==6 || *scan==9 || *scan==11 || *scan==13 || *scan==15 || *scan==17 || *scan==19 || *scan==22 || *scan==24 || *scan==26 || *scan==29 || *scan==31 ) { uInt myints[] = { 0, 9, 10, 11, 12, 13, 14, 15, 16 }; exp.insert(myints, myints+9); } else if ( *scan==4 || *scan==7 || *scan==10 || *scan==12 || *scan==14 || *scan==16 || *scan==18 || *scan==20 || *scan==21 || *scan==23 || *scan==25 || *scan==27 || *scan==28 || *scan==30 || *scan==32 ) { uInt myints[] = { 0, 17, 18, 19, 20, 21, 22, 23, 24 }; exp.insert(myints, myints+9); } scanKey.scan = *scan; AlwaysAssert(md.getSpwsForScan(scanKey) == exp, AipsError); AlwaysAssert(mymap[scanKey] == exp, AipsError); for ( std::set::const_iterator spw=exp.begin(); spw!=exp.end(); ++spw ) { std::set exppols; std::set pols = md.getPolarizationIDs(0, 0, *scan, *spw); if (*spw == 0) { exppols.insert(1); } else { exppols.insert(0); } AlwaysAssert(pols == exppols, AipsError); } } { cout << "*** test getScansForSpw() and getSpwToScansMap()" << endl; vector > spwToScans = md.getSpwToScansMap(); ScanKey scanKey; scanKey.obsID = 0; scanKey.arrayID = 0; for (uInt i=0; i exp; if (i==0) { Int myints[] = { 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 }; exp.insert(myints, myints+32); } else if (i<9) { Int myints[] = {1, 5, 8}; exp.insert(myints, myints+3); } else if (i<17) { Int myints[] = { 2, 3, 6, 9, 11, 13, 15, 17, 19, 22, 24, 26, 29, 31 }; exp.insert(myints, myints+14); } else if (i<25) { Int myints[] = { 4, 7, 10, 12, 14, 16, 18, 20, 21, 23, 25, 27, 28, 30, 32 }; exp.insert(myints, myints+15); } else { // empty set } AlwaysAssert(md.getScansForSpw(i, 0, 0) == exp, AipsError); std::set::const_iterator iter = exp.begin(); std::set::const_iterator end = exp.end(); std::set expSet; for (; iter!=end; ++iter) { scanKey.scan = *iter; expSet.insert(scanKey); } AlwaysAssert(spwToScans[i] == expSet, AipsError); } } { cout << "*** test nAntennas()" << endl; AlwaysAssert(md.nAntennas()==15, AipsError); cout << "*** test getAntennaName()" << endl; String name; String expnames[] = { "DA43", "DA44", "DV02", "DV03", "DV05", "DV07", "DV08", "DV10", "DV12", "DV13", "DV14", "DV15", "DV16", "DV17", "DV18" }; for (uInt i=0; i ids(1); ids[0] = i; std::map > mymap; AlwaysAssert( md.getAntennaNames(mymap, ids)[0] == expnames[i], AipsError ); } cout << "*** test getAntennaID()" << endl; std::map > mymap; for (uInt i=0; i ids(1); ids[0] = i; AlwaysAssert( *md.getAntennaIDs(md.getAntennaNames(mymap, ids))[0].begin()==i, AipsError ); } } { cout << "*** test getTDMSpw()" << endl; std::set exp; uInt myints[] = { 0, 1, 3, 5, 7, 9, 11, 13, 15, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39 }; exp.insert(myints, myints+24); AlwaysAssert(md.getTDMSpw() == exp, AipsError); } { cout << "*** test getFDMSpw()" << endl; std::set exp; uInt myints[] = {17, 19, 21, 23}; exp.insert(myints, myints+4); AlwaysAssert(md.getFDMSpw() == exp, AipsError); } { cout << "*** test getChannelAvgSpw()" << endl; std::set exp; uInt myints[] = { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24 }; exp.insert(myints, myints+12); AlwaysAssert(md.getChannelAvgSpw() == exp, AipsError); } { cout << "*** test getWVRSpw()" << endl; std::set exp; /* uInt myints[] = { 0, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39 }; exp.insert(myints, myints+16); cout << "wvr " << md.getWVRSpw() << endl; */ AlwaysAssert(md.getWVRSpw() == exp, AipsError); } { cout << "*** test getScansForTimes()" << endl; std::set exp; exp.insert(27); AlwaysAssert( md.getScansForTimes(4.84282937e+09, 20, 0, 0) == exp, AipsError ); exp.insert(24); exp.insert(25); exp.insert(26); exp.insert(28); AlwaysAssert( md.getScansForTimes(4.84282937e+09, 200, 0, 0) == exp, AipsError ); } { cout << "*** test getTimesForScans()" << endl; std::set expec; Double myd[] = { 4842825928.7, 4842825929.5, 4842825930.0, 4842825930.6, 4842825941.4, 4842825942.2, 4842825942.5, 4842825942.7, 4842825943.2, 4842825954.0, 4842825954.9, 4842825955.2, 4842825955.4, 4842825955.9, 4842825003.6, 4842825004.0, 4842825004.5, 4842825004.8, 4842825005.0, 4842825016.3, 4842825016.6, 4842825017.1, 4842825017.5, 4842825017.6, 4842825029.0, 4842825029.3, 4842825029.8, 4842825030.1, 4842825030.3 }; expec.insert(myd, myd+29); std::set myscans; myscans.insert(3); myscans.insert(6); AlwaysAssert( allNearAbs(md.getTimesForScans(scanKeys(myscans, arrayKey)), expec, 0.1), AipsError ); } { cout << "*** test getTimesForScan()" << endl; std::set expec; Double myd[] = { 4842825003.6, 4842825004.0, 4842825004.5, 4842825004.8, 4842825005.0, 4842825016.3, 4842825016.6, 4842825017.1, 4842825017.5, 4842825017.6, 4842825029.0, 4842825029.3, 4842825029.8, 4842825030.1, 4842825030.3 }; expec.insert(myd, myd+15); std::set myscans; myscans.insert(3); AlwaysAssert( allNearAbs( md.getTimesForScans(scanKeys(myscans, arrayKey)), expec, 0.1 ), AipsError ); } { cout << "*** test getStatesForScan() getScanToStatesMap()" << endl; std::set expec; std::set scanNumbers = md.getScanNumbers(0, 0); std::map > mymap = md.getScanToStatesMap(); AlwaysAssert(scanNumbers.size() == mymap.size(), AipsError); ScanKey scanKey; scanKey.scan = 0; scanKey.arrayID = 0; scanKey.obsID = 0; for ( std::set::const_iterator curScan=scanNumbers.begin(); curScan!=scanNumbers.end(); ++curScan ) { expec.clear(); if (*curScan == 1 || *curScan == 5 || *curScan == 8) { Int mine[] = {0, 1, 2, 3, 4}; expec.insert(mine, mine+5); } else if (*curScan == 2) { Int mine[] = {5, 6}; expec.insert(mine, mine+2); } else if ( *curScan == 3 || *curScan==6 || *curScan==9 || *curScan==11 || *curScan==13 || *curScan==15 || *curScan==17 || *curScan==19 || *curScan==22 || *curScan==24 || *curScan==26 || *curScan==29 || *curScan==31 ) { Int mine[] = {7, 8, 9}; expec.insert(mine, mine+3); } else if (*curScan==4) { Int mine[] = { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 }; expec.insert(mine, mine+16); } else if (*curScan==7) { Int mine[] = {26, 27, 28, 29, 30, 31}; expec.insert(mine, mine+6); } else if ( *curScan==10 || *curScan==14 || *curScan==18 || *curScan==21 || *curScan==25 || *curScan==28 || *curScan==32 ) { expec.insert(32); } else if ( *curScan==12 || *curScan==16 || *curScan==20 || *curScan==23 ) { Int mine[] = { 33, 34, 35, 36, 37, 38, 39, 40, 41, 42 }; expec.insert(mine, mine+10); } else { Int mine[] = {33, 34, 35, 36}; expec.insert(mine, mine+4); } std::set got = md.getStatesForScan(0, 0, *curScan); AlwaysAssert(got == expec, AipsError); scanKey.scan = *curScan; AlwaysAssert(mymap[scanKey] == expec, AipsError); } cout << "*** cache size " << md.getCache() << endl; } { cout << "*** test getScansForIntent()" << endl; std::set intents = md.getIntents(); for ( std::set::const_iterator intent=intents.begin(); intent!=intents.end(); ++intent ) { std::set expec; if (*intent=="CALIBRATE_AMPLI#ON_SOURCE") { expec.insert(7); } else if ( *intent=="CALIBRATE_ATMOSPHERE#OFF_SOURCE" || *intent=="CALIBRATE_ATMOSPHERE#ON_SOURCE" ) { Int mine[] = { 3, 6, 9, 11, 13, 15, 17, 19, 22, 24, 26, 29, 31 }; expec.insert(mine, mine+13); } else if (*intent=="CALIBRATE_BANDPASS#ON_SOURCE") { expec.insert(4); } else if (*intent=="CALIBRATE_PHASE#ON_SOURCE") { Int mine[] = { 4, 7, 10, 14, 18, 21, 25, 28, 32 }; expec.insert(mine, mine+9); } else if (*intent=="CALIBRATE_POINTING#ON_SOURCE") { Int mine[] = {1, 5, 8}; expec.insert(mine, mine+3); } else if ( *intent=="CALIBRATE_SIDEBAND_RATIO#OFF_SOURCE" || *intent=="CALIBRATE_SIDEBAND_RATIO#ON_SOURCE" ) { expec.insert(2); } else if (*intent=="CALIBRATE_WVR#OFF_SOURCE") { Int mine[] = { 2, 3, 6, 9, 11, 13, 15, 17, 19, 22, 24, 26, 29, 31 }; expec.insert(mine, mine+14); } else if (*intent=="CALIBRATE_WVR#ON_SOURCE") { Int mine[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 17, 18, 19, 21, 22, 24, 25, 26, 28, 29, 31, 32 }; expec.insert(mine, mine+26); } else { Int mine[] = {12, 16, 20, 23, 27, 30}; expec.insert(mine, mine+6); } AlwaysAssert(md.getScansForIntent(*intent, 0, 0) == expec, AipsError); AlwaysAssert( casacore::scanNumbers(md.getIntentToScansMap()[*intent]) == expec, AipsError ); } } { cout << "*** test getScansForFieldID() and getFieldToScansMap" << endl; vector > mymap = md.getFieldToScansMap(); std::set expec; ArrayKey aKey; aKey.arrayID = 0; aKey.obsID = 0; for (uInt i=0; i<6; ++i) { expec.clear(); switch(i) { case 0: { Int mine[] = {1, 2, 3, 4}; expec.insert(mine, mine+4); break; } case 1: expec.insert(5); break; case 2: expec.insert(6); expec.insert(7); break; case 3: { Int mine[] = { 8, 9, 10, 13, 14, 17, 18, 21, 24, 25, 28, 31, 32 }; expec.insert(mine, mine+13); break; } case 4: { Int mine[] = {11, 12, 19, 20, 26, 27}; expec.insert(mine, mine+6); break; } case 5: { Int mine[] = {15, 16, 22, 23, 29, 30}; expec.insert(mine, mine+6); break; } default: throw AipsError("bad fieldID"); } AlwaysAssert(md.getScansForFieldID(i, 0, 0) == expec, AipsError); AlwaysAssert(mymap[i] == scanKeys(expec, aKey), AipsError); } } { cout << "*** test getFieldIDsForField()" << endl; for (uInt i=0; i<6; ++i) { std::set expec; expec.insert(i); String name = i == 0 ? "3C279" : i == 1 ? "J1337-129" : i == 2 ? "Titan" : i == 3 ? "J1625-254" : i == 4 ? "V866 Sco" : "RNO 90"; AlwaysAssert( md.getFieldIDsForField(name) == expec, AipsError ); } } { cout << "*** test getScansForField()" << endl; for (uInt i=0; i<6; ++i) { std::set expec; String name; switch(i) { case 0: { name = "3C279"; uInt mine[] = {1, 2, 3, 4}; expec.insert(mine, mine+4); break; } case 1: name = "J1337-129"; expec.insert(5); break; case 2: name = "Titan"; expec.insert(6); expec.insert(7); break; case 3: { name = "J1625-254"; Int mine[] = { 8, 9, 10, 13, 14, 17, 18, 21, 24, 25, 28, 31, 32 }; expec.insert(mine, mine+13); break; } case 4: { name = "V866 Sco"; Int mine[] = { 11, 12, 19, 20, 26, 27 }; expec.insert(mine, mine+6); break; } case 5: { name = "RNO 90"; Int mine[] = { 15, 16, 22, 23, 29, 30 }; expec.insert(mine, mine+6); break; } default: throw AipsError("bad fieldID"); } AlwaysAssert(md.getScansForField(name, 0, 0) == expec, AipsError); } cout << "*** cache size " << md.getCache() << endl; } { cout << "*** test getFieldsForScan() and getFieldsForScans()" << endl; std::set scans = md.getScanNumbers(0, 0); std::set expec2; std::set curScanSet; for ( std::set::const_iterator curScan=scans.begin(); curScan!=scans.end(); ++curScan ) { std::set expec; curScanSet.insert(*curScan); if (*curScan <= 4) { expec.insert(0); expec2.insert(0); } else if (*curScan == 5) { expec.insert(1); expec2.insert(1); } else if (*curScan <= 7) { expec.insert(2); expec2.insert(2); } else if ( *curScan<=10 || *curScan==13 || *curScan==14 || *curScan==17 || *curScan==18 || *curScan==21 || *curScan==24 || *curScan==25 || *curScan==28 || *curScan==31 || *curScan==32 ) { expec.insert(3); expec2.insert(3); } else if ( *curScan==11 || *curScan==12 || *curScan==19 || *curScan==20 || *curScan==26 || *curScan==27 ) { expec.insert(4); expec2.insert(4); } else { expec.insert(5); expec2.insert(5); } ScanKey scanKey; scanKey.obsID = 0; scanKey.arrayID = 0; scanKey.scan = *curScan; AlwaysAssert( md.getFieldsForScan(scanKey) == expec, AipsError ); AlwaysAssert( md.getFieldsForScans(curScanSet, 0, 0) == expec2, AipsError ); } std::set expec3; expec3.insert(3); expec3.insert(4); std::set scanKeys; ScanKey x; x.obsID = 0; x.arrayID = 0; x.scan = 19; scanKeys.insert(x); x.scan = 31; scanKeys.insert(x); AlwaysAssert( md.getFieldsForScans(scanKeys) == expec3, AipsError ); } { cout << "*** test getFieldsForIntent() and getIntentToFieldsMap()" << endl; std::map > mymap = md.getIntentToFieldsMap(); std::set intents = md.getIntents(); for ( std::set::const_iterator intent=intents.begin(); intent!=intents.end(); ++intent ) { std::set expec; if ( *intent=="CALIBRATE_AMPLI#ON_SOURCE" ) { expec.insert(2); } else if ( *intent=="CALIBRATE_BANDPASS#ON_SOURCE" || *intent=="CALIBRATE_SIDEBAND_RATIO#OFF_SOURCE" || *intent=="CALIBRATE_SIDEBAND_RATIO#ON_SOURCE" ) { expec.insert(0); } else if ( *intent=="CALIBRATE_ATMOSPHERE#OFF_SOURCE" || *intent=="CALIBRATE_ATMOSPHERE#ON_SOURCE" || *intent=="CALIBRATE_WVR#OFF_SOURCE" ) { Int mine[] = {0, 2, 3, 4, 5}; expec.insert(mine, mine+5); } else if ( *intent=="CALIBRATE_PHASE#ON_SOURCE" ) { Int mine[] = {0, 2, 3}; expec.insert(mine, mine+3); } else if ( *intent=="CALIBRATE_POINTING#ON_SOURCE" ) { Int mine[] = {0, 1, 3}; expec.insert(mine, mine+3); } else if (*intent=="CALIBRATE_WVR#ON_SOURCE") { Int mine[] = {0, 1, 2, 3, 4, 5}; expec.insert(mine, mine+6); } else { Int mine[] = {4, 5}; expec.insert(mine, mine+2); } AlwaysAssert( md.getFieldsForIntent(*intent) == expec, AipsError ); AlwaysAssert(mymap[*intent] == expec, AipsError); } } { cout << "*** test getFieldNamesForFieldIDs()" << endl; for (uInt i=0; i(1, i))[0]; cout << "*** expec " << name << " got " << got << endl; AlwaysAssert( got == name, AipsError ); } cout << "*** cache size " << md.getCache() << endl; } { cout << "*** test getFieldsForTime()" << endl; std::set expec; expec.insert(0); AlwaysAssert(md.getFieldsForTimes(4842824746.0, 10) == expec, AipsError); uInt mine[] = {1, 2, 3, 4, 5}; expec.insert(mine, mine+5); AlwaysAssert( md.getFieldsForTimes(4842824746.0, 10000) == expec, AipsError ); } { cout << "*** test getTimesForField()" << endl; uInt nfields = md.nFields(); for (uInt i=0; i< nfields; ++i) { std::set times = md.getTimesForField(i); uInt expec = i == 0 ? 818 : i == 1 ? 81 : i == 2 ? 248 : i == 3 ? 402 : i == 4 ? 963 : i == 5 ? 965 : 0; AlwaysAssert(md.getTimesForField(i).size() == expec, AipsError); } } { cout << "*** test getObservatoryNames()" << endl; vector names = md.getObservatoryNames(); AlwaysAssert(names.size() == 1, AipsError); AlwaysAssert(names[0] == "ALMA", AipsError); } { cout << "*** test getObservatoryPosition()" << endl; MPosition tPos = md.getObservatoryPosition(0); Vector angles = tPos.getAngle("deg").getValue(); cout << angles << endl; AlwaysAssert(near(angles[0], -67.7549, 1e-6), AipsError); AlwaysAssert(near(angles[1], -23.0229, 1e-6), AipsError); cout << "*** cache size " << md.getCache() << endl; } { cout << "*** test getAntennaPosition()" << endl; cout << Vector( md.getAntennaPositions(vector(1, 2)) ) << endl; } { cout << "*** test getAntennaOffset()" << endl; cout << md.getAntennaOffset(2) << endl; } { cout << "*** test getAntennaStations()" << endl; vector ids(3); ids[0] = 2; ids[1] = 4; ids[2] = 3; vector stations = md.getAntennaStations(ids); AlwaysAssert( stations[0] == "A077" && stations[1] == "A082" && stations[2] == "A137", AipsError ); vector names(3); names[0] = "DV02"; names[1] = "DV05"; names[2] = "DV03"; vector > stationsByName = md.getAntennaStations(names); AlwaysAssert( stationsByName[0][0] == "A077" && stationsByName[1][0] == "A082" && stationsByName[2][0] == "A137", AipsError ); } { cout << "*** test getAntennaDiameters" << endl; Quantum > antennaDiameters = md.getAntennaDiameters(); AlwaysAssert( allEQ(antennaDiameters.getValue(), 12.0), AipsError ); } { cout << "*** Test getIntentsForField()" << endl; uInt nFields = md.nFields(); const auto fieldNames = md.getFieldNames(); for (uInt i=0; i expec; switch (i) { case 0: { String mine[] = { "CALIBRATE_ATMOSPHERE#OFF_SOURCE", "CALIBRATE_ATMOSPHERE#ON_SOURCE", "CALIBRATE_BANDPASS#ON_SOURCE", "CALIBRATE_PHASE#ON_SOURCE", "CALIBRATE_POINTING#ON_SOURCE", "CALIBRATE_SIDEBAND_RATIO#OFF_SOURCE", "CALIBRATE_SIDEBAND_RATIO#ON_SOURCE", "CALIBRATE_WVR#OFF_SOURCE", "CALIBRATE_WVR#ON_SOURCE" }; expec.insert(mine, mine+9); break; } case 1: { String mine[] = { "CALIBRATE_POINTING#ON_SOURCE", "CALIBRATE_WVR#ON_SOURCE" }; expec.insert(mine, mine+2); break; } case 2: { String mine[] = { "CALIBRATE_AMPLI#ON_SOURCE", "CALIBRATE_ATMOSPHERE#OFF_SOURCE", "CALIBRATE_ATMOSPHERE#ON_SOURCE", "CALIBRATE_PHASE#ON_SOURCE", "CALIBRATE_WVR#OFF_SOURCE", "CALIBRATE_WVR#ON_SOURCE" }; expec.insert(mine, mine+6); break; } case 3: { String mine[] = { "CALIBRATE_ATMOSPHERE#OFF_SOURCE", "CALIBRATE_ATMOSPHERE#ON_SOURCE", "CALIBRATE_PHASE#ON_SOURCE", "CALIBRATE_POINTING#ON_SOURCE", "CALIBRATE_WVR#OFF_SOURCE", "CALIBRATE_WVR#ON_SOURCE" }; expec.insert(mine, mine+6); break; } case 4: { String mine[] = { "CALIBRATE_ATMOSPHERE#OFF_SOURCE", "CALIBRATE_ATMOSPHERE#ON_SOURCE", "CALIBRATE_WVR#OFF_SOURCE", "CALIBRATE_WVR#ON_SOURCE", "OBSERVE_TARGET#ON_SOURCE" }; expec.insert(mine, mine+5); break; } case 5: { String mine[] = { "CALIBRATE_ATMOSPHERE#OFF_SOURCE", "CALIBRATE_ATMOSPHERE#ON_SOURCE", "CALIBRATE_WVR#OFF_SOURCE", "CALIBRATE_WVR#ON_SOURCE", "OBSERVE_TARGET#ON_SOURCE" }; expec.insert(mine, mine+5); break; } default: break; } cout << "*** i " << i << endl; _printSet(md.getIntentsForField(i)); AlwaysAssert(md.getIntentsForField(i) == expec, AipsError); AlwaysAssert( md.getIntentsForField(fieldNames[i]) == expec, AipsError ); } } { cout << "*** test getUniqueBaselines() and nBaselines()" << endl; AlwaysAssert(md.nBaselines(False) == 21, AipsError); AlwaysAssert(md.nBaselines(True) == 25, AipsError); } { cout << "*** test getEffectiveTotalExposureTime()" << endl; cout << "effective exposure time is " << md.getEffectiveTotalExposureTime() << endl; } { cout << "*** test BBCNosToSpwMap()" << endl; for (uInt i=0; i<3; ++i) { MSMetaData::SQLDSwitch sqldSwitch = i == 0 ? MSMetaData::SQLD_INCLUDE : i == 1 ? MSMetaData::SQLD_EXCLUDE : MSMetaData::SQLD_ONLY; std::map > got = md.getBBCNosToSpwMap(sqldSwitch); std::map >::const_iterator end = got.end(); for ( std::map >::const_iterator iter=got.begin(); iter!=end; ++iter ) { std::set expec; switch(iter->first) { case 0: { if (sqldSwitch != MSMetaData::SQLD_ONLY) { uInt mine[] = { 0, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39 }; expec.insert(mine, mine+16); } break; } case 1: { if (sqldSwitch != MSMetaData::SQLD_ONLY) { uInt mine[] = { 1, 2, 9, 10, 17, 18 }; expec.insert(mine, mine+6); } break; } case 2: { if (sqldSwitch == MSMetaData::SQLD_INCLUDE) { uInt mine[] = { 3, 4, 11, 12, 19, 20 }; expec.insert(mine, mine+6); } else if (sqldSwitch == MSMetaData::SQLD_EXCLUDE) { uInt mine[] = { 4, 11, 12, 19, 20 }; expec.insert(mine, mine+5); } else { // SQLD_ONLY uInt mine[] = {3}; expec.insert(mine, mine+1); } break; } case 3: { if (sqldSwitch != MSMetaData::SQLD_ONLY) { uInt mine[] = { 5, 6, 13, 14, 21, 22 }; expec.insert(mine, mine+6); } break; } case 4: { if (sqldSwitch != MSMetaData::SQLD_ONLY) { uInt mine[] = { 7, 8, 15, 16, 23, 24 }; expec.insert(mine, mine+6); } break; } default: throw AipsError(); } AlwaysAssert(iter->second == expec, AipsError); } } { cout << "*** test getSpwIDPolIDToDataDescIDMap()" << endl; std::map, uInt> dataDescToPolID = md.getSpwIDPolIDToDataDescIDMap(); std::map, uInt>::const_iterator iter; std::map, uInt>::const_iterator begin = dataDescToPolID.begin(); std::map, uInt>::const_iterator end = dataDescToPolID.end(); for( iter=begin; iter!=end; ++iter ) { std::pair mypair = iter->first; uInt spw = mypair.first; uInt pol = mypair.second; Int dataDesc = iter->second; AlwaysAssert((Int)spw == dataDesc, AipsError); AlwaysAssert(pol == (spw == 0 ? 1 : 0), AipsError); } } } { cout << "*** test nPol()" << endl; AlwaysAssert(md.nPol() == 2, AipsError); } { cout << "*** test getSQLDSpw()" << endl; std::set res = md.getSQLDSpw(); AlwaysAssert(res.size() == 1 && *res.begin() == 3, AipsError); } { cout << "*** test getFirstExposureTimeMap() (deprecated)" << endl; vector > mymap = md.getFirstExposureTimeMap(); cout << "val " << mymap[0][30].getValue("s") << endl; cout << "val " << mymap[0][30] << endl; AlwaysAssert(near(mymap[0][30].getValue("s"), 1.152), AipsError); AlwaysAssert(near(mymap[10][17].getValue("s"), 1.008), AipsError) cout << "mymap " << mymap[10][17] << endl; } { cout << "*** test getScanToFirstExposureTimeMap()" << endl; std::map mymap = md.getScanToFirstExposureTimeMap(False); ScanKey scan; scan.arrayID = 0; scan.obsID = 0; scan.scan = 30; AlwaysAssert(near(mymap[scan][0].second.getValue("s"), 1.152), AipsError); scan.scan = 17; AlwaysAssert(near(mymap[scan][10].second.getValue("s"), 1.008), AipsError) } { cout << "*** test getUniqueFiedIDs()" << endl; std::set expec; for (Int i=0; i<6; ++i) { expec.insert(i); } AlwaysAssert(md.getUniqueFieldIDs() == expec, AipsError); } { cout << "*** test getCenterFreqs()" << endl; vector centers = md.getCenterFreqs(); Double mine[] = { 187550000000.0, 214250000000.0, 214234375000.0, 216250000000.0, 216234375000.0, 230250000000.0, 230234375000.0, 232250000000.0, 232234375000.0, 231471730000.0, 231456105000.0, 233352270000.0, 233336645000.0, 219465062500.0, 219449437500.0, 218610562500.0, 218594937500.0, 230534230000.0, 230534214741.0, 232414770000.0, 232414754741.0, 220402562500.0, 220402547241.0, 219548062500.0, 219548047241.0, 187550000000.0, 187550000000.0, 187550000000.0, 187550000000.0, 187550000000.0, 187550000000.0, 187550000000.0, 187550000000.0, 187550000000.0, 187550000000.0, 187550000000.0, 187550000000.0, 187550000000.0, 187550000000.0, 187550000000.0 }; vector expec(mine, mine + 39); for (uInt i=0; i<40; ++i) { AlwaysAssert(abs(centers[i].getValue("Hz")/mine[i] - 1) < 1e-8, AipsError); } } { cout << "*** Test getCorrBits" << endl; vector cb = md.getCorrBits(); for ( const auto &el : cb) { AlwaysAssert(el == "UNKNOWN", AipsError); } } { cout << "*** Test getFieldsForSourceMap" << endl; std::map > res = md.getFieldsForSourceMap(); std::map > res2 = md.getFieldNamesForSourceMap(); String names[] = { "3C279", "J1337-129", "Titan", "J1625-254", "V866 Sco", "RNO 90" }; AlwaysAssert(res.size() == 6, AipsError); AlwaysAssert(res2.size() == 6, AipsError); for (Int i=0; i<6; ++i) { AlwaysAssert(res[i].size() == 1 && *(res[i].begin()) == i, AipsError); AlwaysAssert( res2[i].size() == 1 && *(res2[i].begin()) == names[i], AipsError ); } } { cout << "*** Test getPointingDirection" << endl; Int ant1, ant2; Double time; std::pair pDirs = md.getPointingDirection( ant1, ant2, time, 500 ); AlwaysAssert(ant1 == 7, AipsError); AlwaysAssert(ant2 == 11, AipsError); AlwaysAssert(time == 4842824902.632, AipsError); AlwaysAssert( near(pDirs.first.getAngle().getValue()[0], -1.231522504, 2e-10), AipsError ); AlwaysAssert( near(pDirs.first.getAngle().getValue()[1], 0.8713643132, 1e-9), AipsError ); AlwaysAssert( near(pDirs.second.getAngle().getValue()[0], -1.231504278, 4e-10), AipsError ); AlwaysAssert( near(pDirs.second.getAngle().getValue()[1], 0.8713175514, 1e-9), AipsError ); } { cout << "*** Test getTimeRange()" << endl; std::pair timerange = md.getTimeRange(); AlwaysAssert(near(timerange.first, 4842824745.020, 1e-12), AipsError); AlwaysAssert(near(timerange.second, 4842830012.448, 1e-12), AipsError); } { cout << "*** test getTimesForIntent" << endl; std::set intents = md.getIntents(); std::set::const_iterator intent = intents.begin(); std::set::const_iterator end = intents.end(); while (intent != end) { std::set times = md.getTimesForIntent(*intent); uInt nTimes = times.size(); uInt exp = 0; if (*intent == "CALIBRATE_AMPLI#ON_SOURCE") { exp = 234; } else if (*intent == "CALIBRATE_ATMOSPHERE#OFF_SOURCE") { exp = 46; } else if (*intent == "CALIBRATE_ATMOSPHERE#ON_SOURCE") { exp = 93; } else if (*intent == "CALIBRATE_BANDPASS#ON_SOURCE") { exp = 623; } else if (*intent == "CALIBRATE_PHASE#ON_SOURCE") { exp = 1128; } else if (*intent == "CALIBRATE_POINTING#ON_SOURCE") { exp = 244; } else if( *intent == "CALIBRATE_SIDEBAND_RATIO#OFF_SOURCE" || *intent == "CALIBRATE_SIDEBAND_RATIO#ON_SOURCE" ) { exp = 49; } else if (*intent == "CALIBRATE_WVR#OFF_SOURCE") { exp = 95; } else if (*intent == "CALIBRATE_WVR#ON_SOURCE") { exp = 1514; } else if (*intent == "OBSERVE_TARGET#ON_SOURCE") { exp = 1868; } AlwaysAssert(nTimes == exp, AipsError); ++intent; } { cout << "*** test getSummary()" << endl; //cout << "summary " << md.getSummary() << endl; } { cout << "*** test getProjects()" << endl; vector projects = md.getProjects(); AlwaysAssert(projects.size() == 1, AipsError); AlwaysAssert(projects[0] == "T.B.D.", AipsError); } { cout << "*** test getObservers()" << endl; vector observers = md.getObservers(); AlwaysAssert(observers.size() == 1, AipsError); AlwaysAssert(observers[0] == "csalyk", AipsError); } { cout << "*** test getSchedules()" << endl; vector > schedules = md.getSchedules(); AlwaysAssert(schedules.size() == 1, AipsError); AlwaysAssert(schedules[0].size() == 2, AipsError); AlwaysAssert(schedules[0][0] == "SchedulingBlock uid://A002/X391d0b/X5e", AipsError); AlwaysAssert(schedules[0][1] == "ExecBlock uid://A002/X3f6a86/X5da", AipsError); } { // 4842824633.4720001 // 4842830031.632 cout << "*** test getTimeRangesOfObservations()" << endl; vector > timers = md.getTimeRangesOfObservations(); AlwaysAssert(timers.size() == 1, AipsError); AlwaysAssert(timers[0].first.getRefString() == "UTC", AipsError); AlwaysAssert(timers[0].second.getRefString() == "UTC", AipsError); AlwaysAssert(timers[0].first.getUnit() == Unit("s"), AipsError); AlwaysAssert(timers[0].second.getUnit() == Unit("s"), AipsError); AlwaysAssert( near(timers[0].first.get("s").getValue(), 4842824633.472), AipsError ); AlwaysAssert( near(timers[0].second.get("s").getValue(), 4842830031.632), AipsError ); } { cout << "*** test getRefFreqs()" << endl; Double expec[] = { 1.83300000e+11, 2.15250000e+11, 2.15250000e+11, 2.17250000e+11, 2.17250000e+11, 2.29250000e+11, 2.29250000e+11, 2.31250000e+11, 2.31250000e+11, 2.30471730e+11, 2.30471730e+11, 2.32352270e+11, 2.32352270e+11, 2.20465062e+11, 2.20465062e+11, 2.19610562e+11, 2.19610562e+11, 2.30471730e+11, 2.30471730e+11, 2.32352270e+11, 2.32352270e+11, 2.20465062e+11, 2.20465062e+11, 2.19610562e+11, 2.19610562e+11, 1.83310000e+11, 1.83320000e+11, 1.83330000e+11, 1.83340000e+11, 1.83350000e+11, 1.83360000e+11, 1.83370000e+11, 1.83380000e+11, 1.83390000e+11, 1.83400000e+11, 1.83410000e+11, 1.83420000e+11, 1.83430000e+11, 1.83440000e+11, 1.83450000e+11 }; uInt n = md.nSpw(True); vector rf = md.getRefFreqs(); for (uInt i=0; i > corrTypes = md.getCorrTypes(); AlwaysAssert(corrTypes[0].size() == 2, AipsError); AlwaysAssert(corrTypes[0][0] == 9, AipsError); AlwaysAssert(corrTypes[0][1] == 12, AipsError); AlwaysAssert(corrTypes[1].size() == 1, AipsError); AlwaysAssert(corrTypes[1][0] == 1, AipsError); } { cout << "*** test getCorrProducts" << endl; vector > corrProds = md.getCorrProducts(); AlwaysAssert(corrProds[0].size() == 4, AipsError); AlwaysAssert(corrProds[0](IPosition(2, 0, 0)) == 0, AipsError); AlwaysAssert(corrProds[0](IPosition(2, 0, 1)) == 1, AipsError); AlwaysAssert(corrProds[0](IPosition(2, 1, 0)) == 0, AipsError); AlwaysAssert(corrProds[0](IPosition(2, 1, 1)) == 1, AipsError); AlwaysAssert(corrProds[1].size() == 2, AipsError); AlwaysAssert(allTrue(corrProds[1] == 0), AipsError); } { cout << "*** test getSourceTableSourceIDs" << endl; vector sourceIDs = md.getSourceTableSourceIDs(); AlwaysAssert(sourceIDs.size() == 200, AipsError); for (uInt i=0; i<200; ++i) { Int expec = 0; if ( (i >= 40 && i <= 63) || (i >= 80 && i <= 95) ) { expec = 1; } else if ( (i >= 64 && i <= 79) || (i >= 112 && i <= 135) ) { expec = 2; } else if ( (i >= 96 && i <= 111) || (i >= 152 && i <= 167) ) { expec = 3; } else if ( (i >= 136 && i <= 151) || (i >= 184 && i <= 199) ) { expec = 4; } else if (i >= 168 && i <= 183) { expec = 5; } AlwaysAssert(sourceIDs[i] == expec, AipsError); } } { cout << "*** test getPhaseDirs()" << endl; vector phaseDirs = md.getPhaseDirs(); AlwaysAssert(phaseDirs.size() == md.nFields(), AipsError); Double elong[] = { -2.8964345 , -2.71545722, -2.72554329, -1.98190197, -2.04411602, -1.94537525 }; Double elat[] = { -0.10104256, -0.22613985, -0.1219181, -0.44437211, -0.32533384, -0.27584353 }; for (uInt i=0; i angle = phaseDirs[i].getAngle("rad").getBaseValue(); AlwaysAssert(near(angle[0], elong[i], 1e-7), AipsError); AlwaysAssert(near(angle[1], elat[i], 1e-7), AipsError); } } { cout << "*** test getFieldTableSourceIDs" << endl; vector sourceIDs = md.getFieldTableSourceIDs(); AlwaysAssert(sourceIDs.size() == md.nFields(), AipsError); for (uInt i=0; i scans = md.getScanNumbers(0, 0); std::set::const_iterator iter = scans.begin(); std::set::const_iterator end = scans.end(); ScanKey key; key.obsID = 0; key.arrayID = 0; while (iter != end) { key.scan = *iter; std::set ants = md.getAntennasForScan(key); uInt n = *iter == 9 ? 12 : 13; AlwaysAssert(ants.size() == n, AipsError); std::set::const_iterator aIter = ants.begin(); for (Int i=0; i<14; ++i, ++aIter) { if (i == 12 || (*iter == 9 && i == 7)) { ++i; continue; } else { AlwaysAssert(*aIter == i, AipsError); } } ++iter; } } { cout << "*** test getSourceNames()" << endl; vector sourceNames = md.getSourceNames(); String expec[] = { "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "3C279", "J1337-129", "J1337-129", "J1337-129", "J1337-129", "J1337-129", "J1337-129", "J1337-129", "J1337-129", "J1337-129", "J1337-129", "J1337-129", "J1337-129", "J1337-129", "J1337-129", "J1337-129", "J1337-129", "J1337-129", "J1337-129", "J1337-129", "J1337-129", "J1337-129", "J1337-129", "J1337-129", "J1337-129", "Titan", "Titan", "Titan", "Titan", "Titan", "Titan", "Titan", "Titan", "Titan", "Titan", "Titan", "Titan", "Titan", "Titan", "Titan", "Titan", "Titan", "Titan", "Titan", "Titan", "Titan", "Titan", "Titan", "Titan", "Titan", "Titan", "Titan", "Titan", "Titan", "Titan", "Titan", "Titan", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "J1625-254", "V866 Sco", "V866 Sco", "V866 Sco", "V866 Sco", "V866 Sco", "V866 Sco", "V866 Sco", "V866 Sco", "V866 Sco", "V866 Sco", "V866 Sco", "V866 Sco", "V866 Sco", "V866 Sco", "V866 Sco", "V866 Sco", "V866 Sco", "V866 Sco", "V866 Sco", "V866 Sco", "V866 Sco", "V866 Sco", "V866 Sco", "V866 Sco", "V866 Sco", "V866 Sco", "V866 Sco", "V866 Sco", "V866 Sco", "V866 Sco", "V866 Sco", "V866 Sco", "RNO 90", "RNO 90", "RNO 90", "RNO 90", "RNO 90", "RNO 90", "RNO 90", "RNO 90", "RNO 90", "RNO 90", "RNO 90", "RNO 90", "RNO 90", "RNO 90", "RNO 90", "RNO 90", "RNO 90", "RNO 90", "RNO 90", "RNO 90", "RNO 90", "RNO 90", "RNO 90", "RNO 90", "RNO 90", "RNO 90", "RNO 90", "RNO 90", "RNO 90", "RNO 90", "RNO 90", "RNO 90" }; for (uInt i=0; i dirs = md.getSourceDirections(); AlwaysAssert(dirs.size() == 200, AipsError); Double elong[] = { -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.8964345 , -2.71545722, -2.71545722, -2.71545722, -2.71545722, -2.71545722, -2.71545722, -2.71545722, -2.71545722, -2.71545722, -2.71545722, -2.71545722, -2.71545722, -2.71545722, -2.71545722, -2.71545722, -2.71545722, -2.71545722, -2.71545722, -2.71545722, -2.71545722, -2.71545722, -2.71545722, -2.71545722, -2.71545722, -2.72554329, -2.72554329, -2.72554329, -2.72554329, -2.72554329, -2.72554329, -2.72554329, -2.72554329, -2.72554329, -2.72554329, -2.72554329, -2.72554329, -2.72554329, -2.72554329, -2.72554329, -2.72554329, -2.72554329, -2.72554329, -2.72554329, -2.72554329, -2.72554329, -2.72554329, -2.72554329, -2.72554329, -2.72554329, -2.72554329, -2.72554329, -2.72554329, -2.72554329, -2.72554329, -2.72554329, -2.72554329, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -1.98190197, -2.04411602, -2.04411602, -2.04411602, -2.04411602, -2.04411602, -2.04411602, -2.04411602, -2.04411602, -2.04411602, -2.04411602, -2.04411602, -2.04411602, -2.04411602, -2.04411602, -2.04411602, -2.04411602, -2.04411602, -2.04411602, -2.04411602, -2.04411602, -2.04411602, -2.04411602, -2.04411602, -2.04411602, -2.04411602, -2.04411602, -2.04411602, -2.04411602, -2.04411602, -2.04411602, -2.04411602, -2.04411602, -1.94537525, -1.94537525, -1.94537525, -1.94537525, -1.94537525, -1.94537525, -1.94537525, -1.94537525, -1.94537525, -1.94537525, -1.94537525, -1.94537525, -1.94537525, -1.94537525, -1.94537525, -1.94537525, -1.94537525, -1.94537525, -1.94537525, -1.94537525, -1.94537525, -1.94537525, -1.94537525, -1.94537525, -1.94537525, -1.94537525, -1.94537525, -1.94537525, -1.94537525, -1.94537525, -1.94537525, -1.94537525 }; Double elat[] = { -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.10104256, -0.22613985, -0.22613985, -0.22613985, -0.22613985, -0.22613985, -0.22613985, -0.22613985, -0.22613985, -0.22613985, -0.22613985, -0.22613985, -0.22613985, -0.22613985, -0.22613985, -0.22613985, -0.22613985, -0.22613985, -0.22613985, -0.22613985, -0.22613985, -0.22613985, -0.22613985, -0.22613985, -0.22613985, -0.1219181 , -0.1219181 , -0.1219181 , -0.1219181 , -0.1219181 , -0.1219181 , -0.1219181 , -0.1219181 , -0.1219181 , -0.1219181 , -0.1219181 , -0.1219181 , -0.1219181 , -0.1219181 , -0.1219181 , -0.1219181 , -0.1219181 , -0.1219181 , -0.1219181 , -0.1219181 , -0.1219181 , -0.1219181 , -0.1219181 , -0.1219181 , -0.1219181 , -0.1219181 , -0.1219181 , -0.1219181 , -0.1219181 , -0.1219181 , -0.1219181 , -0.1219181 , -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.44437211, -0.32533384, -0.32533384, -0.32533384, -0.32533384, -0.32533384, -0.32533384, -0.32533384, -0.32533384, -0.32533384, -0.32533384, -0.32533384, -0.32533384, -0.32533384, -0.32533384, -0.32533384, -0.32533384, -0.32533384, -0.32533384, -0.32533384, -0.32533384, -0.32533384, -0.32533384, -0.32533384, -0.32533384, -0.32533384, -0.32533384, -0.32533384, -0.32533384, -0.32533384, -0.32533384, -0.32533384, -0.32533384, -0.27584353, -0.27584353, -0.27584353, -0.27584353, -0.27584353, -0.27584353, -0.27584353, -0.27584353, -0.27584353, -0.27584353, -0.27584353, -0.27584353, -0.27584353, -0.27584353, -0.27584353, -0.27584353, -0.27584353, -0.27584353, -0.27584353, -0.27584353, -0.27584353, -0.27584353, -0.27584353, -0.27584353, -0.27584353, -0.27584353, -0.27584353, -0.27584353, -0.27584353, -0.27584353, -0.27584353, -0.27584353 }; for (uInt i=0; i angle = dirs[i].getAngle("rad").getBaseValue(); AlwaysAssert(near(angle[0], elong[i], 1e-7), AipsError); AlwaysAssert(near(angle[1], elat[i], 1e-7), AipsError); } } { cout << "*** test getProperMotions()" << endl; vector > pm = md.getProperMotions(); AlwaysAssert(pm.size() == 200, AipsError); for (uInt i=0; i<200; ++i) { AlwaysAssert(pm[0].first.getValue() == 0, AipsError); AlwaysAssert(pm[0].second.getValue() == 0, AipsError); AlwaysAssert(pm[0].first.getUnit() == "rad/s", AipsError); AlwaysAssert(pm[0].second.getUnit() == "rad/s", AipsError); } } { cout << "*** test getSpwToTimesForScan()" << endl; ScanKey scan; scan.obsID = 0; scan.arrayID = 0; scan.scan = 5; std::map > times = md.getSpwToTimesForScan(scan); AlwaysAssert(times.size() == 9, AipsError); std::set expec; for (uInt i=0; i<9; ++i) { if(i == 0) { Double z[] = { 4842825782.3999996185, 4842825800.8319997787, 4842825807.7440004349, 4842825826.1760005951, 4842825844.6079998016, 4842825861.8879995346, 4842825869.9519996643 }; expec = std::set(z, z+7); } else if (i == 1) { Double z[] = { 4842825778.6560001373, 4842825780.6719999313, 4842825782.6879997253, 4842825784.704000473, 4842825786.720000267, 4842825799.3920001984, 4842825801.4079999924, 4842825803.4239997864, 4842825805.4400005341, 4842825807.4560003281, 4842825820.1280002594, 4842825822.1440000534, 4842825824.1599998474, 4842825826.1760005951, 4842825828.1920003891, 4842825840.8640003204, 4842825842.8800001144, 4842825844.8959999084, 4842825846.9119997025, 4842825848.9279994965, 4842825861.6000003815, 4842825863.6159992218, 4842825865.6319999695, 4842825867.6479997635, 4842825869.6639995575 }; expec = std::set(z, z+25); } else if (i == 2) { Double z[] = { 4842825778.1519994736, 4842825779.1600008011, 4842825780.1679992676, 4842825781.1760005951, 4842825782.1839990616, 4842825783.1920003891, 4842825784.1999998093, 4842825785.2080001831, 4842825786.2159996033, 4842825787.2240009308, 4842825798.8879995346, 4842825799.8960008621, 4842825800.9039993286, 4842825801.9120006561, 4842825802.9199991226, 4842825803.9280004501, 4842825804.9359998703, 4842825805.9440002441, 4842825806.9519996643, 4842825807.9600009918, 4842825819.6239995956, 4842825820.6320009232, 4842825821.6399993896, 4842825822.6480007172, 4842825823.6560001373, 4842825824.6640005112, 4842825825.6719999313, 4842825826.6800003052, 4842825827.6879997253, 4842825828.6960010529, 4842825840.3599996567, 4842825841.3680009842, 4842825842.3759994507, 4842825843.3840007782, 4842825844.3920001984, 4842825845.4000005722, 4842825846.4079990387, 4842825847.4160003662, 4842825848.4239988327, 4842825849.4320001602, 4842825861.0959997177, 4842825862.1040010452, 4842825863.1119995117, 4842825864.1200008392, 4842825865.1279993057, 4842825866.1360006332, 4842825867.1439990997, 4842825868.1520004272, 4842825869.1599998474, 4842825870.1680002213 }; expec = std::set(z, z+50); } else if (i == 3) { Double z[] = { 4842825778.6560001373, 4842825780.6719999313, 4842825782.6879997253, 4842825784.704000473, 4842825786.720000267, 4842825799.3920001984, 4842825801.4079999924, 4842825803.4239997864, 4842825805.4400005341, 4842825807.4560003281, 4842825820.1280002594, 4842825822.1440000534, 4842825824.1599998474, 4842825826.1760005951, 4842825828.1920003891, 4842825840.8640003204, 4842825842.8800001144, 4842825844.8959999084, 4842825846.9119997025, 4842825848.9279994965, 4842825861.6000003815, 4842825863.6159992218, 4842825865.6319999695, 4842825867.6479997635, 4842825869.6639995575 }; expec = std::set(z, z+25); } else if (i == 4) { Double z[] = { 4842825778.1519994736, 4842825779.1600008011, 4842825780.1679992676, 4842825781.1760005951, 4842825782.1839990616, 4842825783.1920003891, 4842825784.1999998093, 4842825785.2080001831, 4842825786.2159996033, 4842825787.2240009308, 4842825798.8879995346, 4842825799.8960008621, 4842825800.9039993286, 4842825801.9120006561, 4842825802.9199991226, 4842825803.9280004501, 4842825804.9359998703, 4842825805.9440002441, 4842825806.9519996643, 4842825807.9600009918, 4842825819.6239995956, 4842825820.6320009232, 4842825821.6399993896, 4842825822.6480007172, 4842825823.6560001373, 4842825824.6640005112, 4842825825.6719999313, 4842825826.6800003052, 4842825827.6879997253, 4842825828.6960010529, 4842825840.3599996567, 4842825841.3680009842, 4842825842.3759994507, 4842825843.3840007782, 4842825844.3920001984, 4842825845.4000005722, 4842825846.4079990387, 4842825847.4160003662, 4842825848.4239988327, 4842825849.4320001602, 4842825861.0959997177, 4842825862.1040010452, 4842825863.1119995117, 4842825864.1200008392, 4842825865.1279993057, 4842825866.1360006332, 4842825867.1439990997, 4842825868.1520004272, 4842825869.1599998474, 4842825870.1680002213 }; expec = std::set(z, z+50); } else if (i == 5) { Double z[] = { 4842825778.6560001373, 4842825780.6719999313, 4842825782.6879997253, 4842825784.704000473, 4842825786.720000267, 4842825799.3920001984, 4842825801.4079999924, 4842825803.4239997864, 4842825805.4400005341, 4842825807.4560003281, 4842825820.1280002594, 4842825822.1440000534, 4842825824.1599998474, 4842825826.1760005951, 4842825828.1920003891, 4842825840.8640003204, 4842825842.8800001144, 4842825844.8959999084, 4842825846.9119997025, 4842825848.9279994965, 4842825861.6000003815, 4842825863.6159992218, 4842825865.6319999695, 4842825867.6479997635, 4842825869.6639995575 }; expec = std::set(z, z+25); } else if (i == 6) { Double z[] = { 4842825778.1519994736, 4842825779.1600008011, 4842825780.1679992676, 4842825781.1760005951, 4842825782.1839990616, 4842825783.1920003891, 4842825784.1999998093, 4842825785.2080001831, 4842825786.2159996033, 4842825787.2240009308, 4842825798.8879995346, 4842825799.8960008621, 4842825800.9039993286, 4842825801.9120006561, 4842825802.9199991226, 4842825803.9280004501, 4842825804.9359998703, 4842825805.9440002441, 4842825806.9519996643, 4842825807.9600009918, 4842825819.6239995956, 4842825820.6320009232, 4842825821.6399993896, 4842825822.6480007172, 4842825823.6560001373, 4842825824.6640005112, 4842825825.6719999313, 4842825826.6800003052, 4842825827.6879997253, 4842825828.6960010529, 4842825840.3599996567, 4842825841.3680009842, 4842825842.3759994507, 4842825843.3840007782, 4842825844.3920001984, 4842825845.4000005722, 4842825846.4079990387, 4842825847.4160003662, 4842825848.4239988327, 4842825849.4320001602, 4842825861.0959997177, 4842825862.1040010452, 4842825863.1119995117, 4842825864.1200008392, 4842825865.1279993057, 4842825866.1360006332, 4842825867.1439990997, 4842825868.1520004272, 4842825869.1599998474, 4842825870.1680002213 }; expec = std::set(z, z+50); } else if (i == 7) { Double z[] = { 4842825778.6560001373, 4842825780.6719999313, 4842825782.6879997253, 4842825784.704000473, 4842825786.720000267, 4842825799.3920001984, 4842825801.4079999924, 4842825803.4239997864, 4842825805.4400005341, 4842825807.4560003281, 4842825820.1280002594, 4842825822.1440000534, 4842825824.1599998474, 4842825826.1760005951, 4842825828.1920003891, 4842825840.8640003204, 4842825842.8800001144, 4842825844.8959999084, 4842825846.9119997025, 4842825848.9279994965, 4842825861.6000003815, 4842825863.6159992218, 4842825865.6319999695, 4842825867.6479997635, 4842825869.6639995575 }; expec = std::set(z, z+25); } else if (i == 8) { Double z[] = { 4842825778.1519994736, 4842825779.1600008011, 4842825780.1679992676, 4842825781.1760005951, 4842825782.1839990616, 4842825783.1920003891, 4842825784.1999998093, 4842825785.2080001831, 4842825786.2159996033, 4842825787.2240009308, 4842825798.8879995346, 4842825799.8960008621, 4842825800.9039993286, 4842825801.9120006561, 4842825802.9199991226, 4842825803.9280004501, 4842825804.9359998703, 4842825805.9440002441, 4842825806.9519996643, 4842825807.9600009918, 4842825819.6239995956, 4842825820.6320009232, 4842825821.6399993896, 4842825822.6480007172, 4842825823.6560001373, 4842825824.6640005112, 4842825825.6719999313, 4842825826.6800003052, 4842825827.6879997253, 4842825828.6960010529, 4842825840.3599996567, 4842825841.3680009842, 4842825842.3759994507, 4842825843.3840007782, 4842825844.3920001984, 4842825845.4000005722, 4842825846.4079990387, 4842825847.4160003662, 4842825848.4239988327, 4842825849.4320001602, 4842825861.0959997177, 4842825862.1040010452, 4842825863.1119995117, 4842825864.1200008392, 4842825865.1279993057, 4842825866.1360006332, 4842825867.1439990997, 4842825868.1520004272, 4842825869.1599998474, 4842825870.1680002213 }; expec = std::set(z, z+50); } else { cout << "found channel " << i << " which shouldn't be in this set" << endl; AlwaysAssert(False, AipsError); } AlwaysAssert(times[i].size() == expec.size(), AipsError); std::set::const_iterator iter = times[i].begin(); std::set::const_iterator end = times[i].end(); std::set::const_iterator eIter = expec.begin(); while (iter != end) { AlwaysAssert(near(*iter, *eIter), AipsError); ++iter; ++eIter; } } } { cout << "*** test nUniqueSourceIDsFromSourceTable()" << endl; uInt n = md.nUniqueSourceIDsFromSourceTable(); AlwaysAssert(n == 6, AipsError); } { cout << "*** test getFieldNames()" << endl; vector fnames = md.getFieldNames(); String z[] = {"3C279", "J1337-129", "Titan", "J1625-254", "V866 Sco", "RNO 90"}; vector expec(z, z+6); vector::const_iterator iter = fnames.begin(); vector::const_iterator end = fnames.end(); vector::const_iterator eIter = expec.begin(); while (iter != end) { AlwaysAssert(*iter == *eIter, AipsError); ++iter; ++eIter; } } { cout << "*** test getNetSidebands()" << endl; vector netsb = md.getNetSidebands(); Int expec[] = { 3, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }; uInt n = netsb.size(); for(uInt i=0; i angle = dir.getAngle().getValue(); switch(i) { case 0: AlwaysAssert(near(angle[0], -2.8964345, 1e-6), AipsError); AlwaysAssert(near(angle[1], -0.10104256, 1e-6), AipsError); break; case 1: AlwaysAssert(near(angle[0], -2.71545722, 1e-6), AipsError); AlwaysAssert(near(angle[1], -0.22613985, 1e-6), AipsError); break; case 2: AlwaysAssert(near(angle[0], -2.72554329, 1e-6), AipsError); AlwaysAssert(near(angle[1], -0.1219181, 1e-6), AipsError); break; case 3: AlwaysAssert(near(angle[0], -1.98190197, 1e-6), AipsError); AlwaysAssert(near(angle[1], -0.44437211, 1e-6), AipsError); break; case 4: AlwaysAssert(near(angle[0], -2.04411602, 1e-6), AipsError); AlwaysAssert(near(angle[1], -0.32533384, 1e-6), AipsError); break; case 5: AlwaysAssert(near(angle[0], -1.94537525, 1e-6), AipsError); AlwaysAssert(near(angle[1], -0.27584353, 1e-6), AipsError); break; default: break; } } } { cout << "*** test getChanEffectiveBWs()" << endl; vector > ebw = md.getChanEffectiveBWs(False); vector >::const_iterator iter = ebw.begin(); vector >::const_iterator end = ebw.end(); Double expec = 0; while (iter != end) { size_t nchans = iter->size(); if (nchans == 1) { ++iter; continue; } else if (nchans == 4) { expec = 7.5e9; } else if (nchans == 128) { expec = 1.5625e7; } else if (nchans == 3840) { expec = 30517.578125; } Vector vals = iter->getValue(); Vector::const_iterator jiter = vals.begin(); Vector::const_iterator jend = vals.end(); while (jiter != jend) { AlwaysAssert(*jiter == expec, AipsError); ++jiter; } ++iter; } vector > ebwv = md.getChanEffectiveBWs(True); AlwaysAssert(near(ebwv[9].getValue()[0], 20.23684342, 1e-8), AipsError); AlwaysAssert(ebwv[9].getUnit() == "km/s", AipsError); } { cout << "*** test getChanResolutions()" << endl; vector > ebw = md.getChanResolutions(False); vector >::const_iterator iter = ebw.begin(); vector >::const_iterator end = ebw.end(); Double expec = 0; while (iter != end) { size_t nchans = iter->size(); if (nchans == 1) { ++iter; continue; } else if (nchans == 4) { expec = 7.5e9; } else if (nchans == 128) { expec = 1.5625e7; } else if (nchans == 3840) { expec = 30517.578125; } Vector vals = iter->getValue(); Vector::const_iterator jiter = vals.begin(); Vector::const_iterator jend = vals.end(); while (jiter != jend) { AlwaysAssert(*jiter == expec, AipsError); ++jiter; } ++iter; } vector > ebwv = md.getChanResolutions(True); AlwaysAssert(near(ebwv[9].getValue()[0], 20.23684342, 1e-8), AipsError); AlwaysAssert(ebwv[9].getUnit() == "km/s", AipsError); } { cout << "test getRestFrequencies()" << endl; std::map > > rfs = md.getRestFrequencies(); std::map > >::const_iterator iter = rfs.begin(); std::map > >::const_iterator end = rfs.end(); while (iter != end) { if (iter->second ) { AlwaysAssert( iter->first.id == 0 && iter->first.spw == 34, AipsError ); AlwaysAssert(iter->second->size() == 2, AipsError); AlwaysAssert((*iter->second)[0].get("Hz").getValue() == 1e10, AipsError); AlwaysAssert((*iter->second)[1].get("Hz").getValue() == 2e10, AipsError); } ++iter; } } { cout << "test getTransitions()" << endl; std::map > > rfs = md.getTransitions(); std::map > >::const_iterator iter = rfs.begin(); std::map > >::const_iterator end = rfs.end(); while (iter != end) { if (iter->second ) { AlwaysAssert( iter->first.id == 0 && iter->first.spw == 34, AipsError ); AlwaysAssert(iter->second->size() == 2, AipsError); AlwaysAssert((*iter->second)[0] == "myline", AipsError); AlwaysAssert((*iter->second)[1] == "yourline", AipsError); } ++iter; } } { cout << "test getSubScanProperties" << endl; SubScanKey sskey; sskey.arrayID = 0; sskey.fieldID = 0; sskey.obsID = 0; sskey.scan = 0; Bool thrown = False; try { md.getSubScanProperties(sskey); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); sskey.scan = 1; MSMetaData::SubScanProperties props = md.getSubScanProperties(sskey); AlwaysAssert(props.acRows + props.xcRows == 367, AipsError); std::shared_ptr > allProps = md.getSubScanProperties(); AlwaysAssert( allProps->find(sskey)->second.acRows + allProps->find(sskey)->second.xcRows == 367, AipsError ); for (uInt i=0; i<9; ++i) { Double expec = 0; if (i == 0) { expec = 1.152; } else if (i == 1 || i == 3 || i == 5 || i == 7) { expec = 2.016; } else { expec = 1.008; } AlwaysAssert(near(props.meanInterval[i].getValue(), expec), AipsError); } } { cout << "*** test getScanKeys()" << endl; std::set keys = md.getScanKeys(); ScanKey expec; expec.arrayID = 0; expec.obsID = 0; for (Int i=1; i<34; ++i) { expec.scan = i; if (i == 33) { AlwaysAssert(keys.find(expec) == keys.end(), AipsError); } else { AlwaysAssert(keys.find(expec) != keys.end(), AipsError); } } } { cout << "*** test getIntentsForSubScan() and getSubScanToIntentsMap()" << endl; ArrayKey arrayKey; arrayKey.obsID = 0; arrayKey.arrayID = 0; std::set sskeys = md.getSubScanKeys(arrayKey); std::set::const_iterator ssiter = sskeys.begin(); std::set::const_iterator ssend = sskeys.end(); std::shared_ptr > > mymap = md.getSubScanToIntentsMap(); for (; ssiter!=ssend; ++ssiter) { std::set intents = md.getIntentsForSubScan(*ssiter); std::set exp; Int scan = ssiter->scan; if (scan == 1 || scan == 5 || scan == 8) { String mystr[] = { "CALIBRATE_POINTING#ON_SOURCE", "CALIBRATE_WVR#ON_SOURCE" }; exp.insert(mystr, mystr+2); } else if (scan == 2) { String mystr[] = { "CALIBRATE_SIDEBAND_RATIO#OFF_SOURCE", "CALIBRATE_SIDEBAND_RATIO#ON_SOURCE", "CALIBRATE_WVR#OFF_SOURCE", "CALIBRATE_WVR#ON_SOURCE" }; exp.insert(mystr, mystr+4); } else if ( scan == 3 || scan == 6 || scan == 9 || scan == 11 || scan == 13 || scan == 15 || scan == 17 || scan == 19 || scan == 22 || scan == 24 || scan == 26 || scan == 29 || scan == 31 ) { String mystr[] = { "CALIBRATE_ATMOSPHERE#OFF_SOURCE", "CALIBRATE_ATMOSPHERE#ON_SOURCE", "CALIBRATE_WVR#OFF_SOURCE", "CALIBRATE_WVR#ON_SOURCE" }; exp.insert(mystr, mystr+4); } else if (scan == 4) { String mystr[] = { "CALIBRATE_BANDPASS#ON_SOURCE", "CALIBRATE_PHASE#ON_SOURCE", "CALIBRATE_WVR#ON_SOURCE" }; exp.insert(mystr, mystr+3); } else if (scan == 7) { String mystr[] = { "CALIBRATE_AMPLI#ON_SOURCE", "CALIBRATE_PHASE#ON_SOURCE", "CALIBRATE_WVR#ON_SOURCE" }; exp.insert(mystr, mystr+3); } else if ( scan == 10 || scan == 14 || scan == 18 || scan == 21 || scan == 25 || scan == 28 || scan == 32 ) { String mystr[] = { "CALIBRATE_PHASE#ON_SOURCE", "CALIBRATE_WVR#ON_SOURCE" }; exp.insert(mystr, mystr+2); } else if ( scan == 12 || scan == 16 || scan == 20 || scan == 23 || scan == 27 || scan == 30 ) { exp.insert("OBSERVE_TARGET#ON_SOURCE"); } uniqueIntents.insert(exp.begin(), exp.end()); AlwaysAssert(intents == exp, AipsError); AlwaysAssert(mymap->find(*ssiter)->second == exp, AipsError); } } { cout << "*** test getSpwsForSubScan()" << endl; ArrayKey arrayKey; arrayKey.obsID = 0; arrayKey.arrayID = 0; std::set sskeys = md.getSubScanKeys(arrayKey); std::set::const_iterator ssiter = sskeys.begin(); std::set::const_iterator ssend = sskeys.end(); for (; ssiter!=ssend; ++ssiter) { std::set exp; Int scan = ssiter->scan; if (scan == 1 || scan==5 || scan==8) { uInt myints[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; exp.insert(myints, myints+9); } else if ( scan == 2 || scan==3 || scan==6 || scan==9 || scan==11 || scan==13 || scan==15 || scan==17 || scan==19 || scan==22 || scan==24 || scan==26 || scan==29 || scan==31 ) { uInt myints[] = { 0, 9, 10, 11, 12, 13, 14, 15, 16 }; exp.insert(myints, myints+9); } else if ( scan==4 || scan==7 || scan==10 || scan==12 || scan==14 || scan==16 || scan==18 || scan==20 || scan==21 || scan==23 || scan==25 || scan==27 || scan==28 || scan==30 || scan==32 ) { uInt myints[] = { 0, 17, 18, 19, 20, 21, 22, 23, 24 }; exp.insert(myints, myints+9); } AlwaysAssert(md.getSpwsForSubScan(*ssiter) == exp, AipsError); } } { cout << "test getAverageIntervalsForSubScan()" << endl; ArrayKey arrayKey; arrayKey.obsID = 0; arrayKey.arrayID = 0; std::set sskeys = md.getSubScanKeys(arrayKey); std::set::const_iterator ssiter = sskeys.begin(); std::set::const_iterator ssend = sskeys.end(); for (; ssiter!=ssend; ++ssiter) { std::map mIntervals = md.getAverageIntervalsForSubScan(*ssiter); std::set spws = md.getSpwsForSubScan(*ssiter); AlwaysAssert(mIntervals.size() == spws.size(), AipsError); std::map::const_iterator miter = mIntervals.begin(); std::map::const_iterator mend = mIntervals.end(); for (; miter!=mend; ++miter) { uInt spw = miter->first; AlwaysAssert(spws.find(spw) != spws.end(), AipsError); Double v = 0; if (spw == 0) { v = 1.152; } else if ( spw == 1 || spw == 3 || spw == 5 || spw == 7 || spw == 9 || spw == 11 || spw == 13 || spw == 15 ) { v = 2.016; } else if ( spw == 2 || spw == 4 || spw == 6 || spw == 8 || spw == 10 || spw == 12 || spw == 14 || spw == 16 || spw == 18 || spw == 20 || spw == 22 || spw == 24 ) { v = 1.008; } else if ( spw == 17 || spw == 19 || spw == 21 || spw == 23 ) { v = 6.048; } Quantity expec(v, "s"); AlwaysAssert(near(miter->second, expec, 1e-1), AipsError); } } } { cout << "*** test getSpwIDs()" << endl; std::set spws = md.getSpwIDs(); AlwaysAssert(spws.size() == 25, AipsError); std::set::const_iterator iter = spws.begin(); std::set::const_iterator end = spws.end(); uInt i = 0; for (; iter!=end; ++iter, ++i) { AlwaysAssert(*iter == i, AipsError); } } { cout << "*** test getScanToTimeRangeMap()" << endl; std::shared_ptr > > mymap = md.getScanToTimeRangeMap(); ScanKey key; key.arrayID = 0; key.obsID = 0; key.scan = 1; AlwaysAssert(near(mymap->find(key)->second.first, 4842824745.0, 1.0), AipsError); AlwaysAssert(near(mymap->find(key)->second.second, 4842824839.0, 1.0), AipsError); } { cout << "*** test getNRowsMap()" << endl; SubScanKey key; key.arrayID = 0; key.obsID = 0; key.scan = 1; key.fieldID = 0; std::shared_ptr > both = md.getNRowMap(MSMetaData::BOTH); AlwaysAssert(both->find(key)->second == 367, AipsError); std::shared_ptr > ac = md.getNRowMap(MSMetaData::AUTO); AlwaysAssert(ac->find(key)->second == 51, AipsError); std::shared_ptr > xc = md.getNRowMap(MSMetaData::CROSS); AlwaysAssert(xc->find(key)->second == 316, AipsError); } { cout << "*** test getFieldCodes()" << endl; Vector codes = Vector(md.getFieldCodes()); AlwaysAssert(allEQ(codes, String("none")), AipsError); } { cout << "*** test getUniqueDataDescIDs()" << endl; std::set ddids = md.getUniqueDataDescIDs(); Vector expec = indgen(25, (uInt)0, (uInt)1); AlwaysAssert( allEQ( Vector(vector(ddids.begin(), ddids.end())), expec ), AipsError ); } { cout << "*** test getUniqueAntennaIDs()" << endl; std::set ants = md.getUniqueAntennaIDs(); Int evals[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13}; Vector expec(vector(evals, evals+13)); AlwaysAssert( allEQ( Vector(vector(ants.begin(), ants.end())), expec ), AipsError ); } { cout << "*** test getFirstExposureTimeMap()" << endl; vector > mymap = md.getFirstExposureTimeMap(); AlwaysAssert(mymap.size() == 25, AipsError); for (Int i=0; i<25; ++i) { uInt expSize = 0; Quantity expExposure(0, "s"); if (i == 0) { expSize = 32; expExposure.setValue(1.152); } else if (i == 1 || i == 3 || i == 5 || i == 7) { expSize = 3; expExposure.setValue(2.016); } else if (i == 2 || i == 4 || i == 6 || i == 8) { expSize = 3; expExposure.setValue(1.008); } else if (i == 9 || i == 11 || i == 13 || i == 15) { expSize = 14; expExposure.setValue(2.016); } else if (i == 10 || i == 12 || i == 14 || i == 16) { expSize = 14; expExposure.setValue(1.008); } else if (i == 17 || i == 19 || i == 21 || i == 23) { expSize = 15; expExposure.setValue(6.048); } else if (i == 18 || i == 20 || i == 22 || i == 24) { expSize = 15; expExposure.setValue(1.008); } AlwaysAssert(mymap[i].size() == expSize, AipsError); AlwaysAssert(mymap[i].begin()->second == expExposure, AipsError); } } { cout << "*** test getUniqueSpwIDs()" << endl; std::set spws = md.getUniqueSpwIDs(); Vector expV = casacore::indgen(25, 0, 1); std::set expec(expV.begin(), expV.end()); AlwaysAssert(spws == expec, AipsError); } { cout << "*** test getSourceTimes()" << endl; std::shared_ptr > > times = md.getSourceTimes(); Vector v = times->getValue(); AlwaysAssert(v.size() == 200, AipsError); AlwaysAssert(times->getUnit() == "s", AipsError); Vector expec(200, 7033098335); AlwaysAssert(allNear(v, expec, 1e-10), AipsError); } { cout << "*** test getIntervalStatistics()" << endl; MSMetaData::ColumnStats stats = md.getIntervalStatistics(); AlwaysAssert(near(stats.min, 1.008), AipsError); AlwaysAssert(near(stats.max, 6.048), AipsError); AlwaysAssert(near(stats.median, 1.008), AipsError); } { cout << "test getTimesForSpws()" << endl; std::vector > vec = md.getTimesForSpws(); uInt n = vec.size(); AlwaysAssert(n == 40, AipsError); uInt evals[] = { 351, 75, 150, 75, 150, 75, 150, 75, 150, 69, 138, 69, 138, 69, 138, 69, 138, 385, 2310, 385, 2310, 385, 2310, 385, 2310 }; std::vector expec(evals, evals+25); for (uInt i=0; i expectimes(etimes, etimes+75); AlwaysAssert(allNearAbs(vec[1], expectimes, 1e-6), AipsError); } { cout << "*** cache size " << md.getCache() << endl; } } } int main() { try { String *parts = new String[2]; split(EnvironmentVariable::get("CASAPATH"), parts, 2, String(" ")); String datadir = parts[0] + "/data/"; delete [] parts; casacore::MeasurementSet ms(datadir + "regression/unittest/MSMetaData/MSMetaData.ms"); cout << "*** test on-demand constructor" << endl; MSMetaData md1(&ms, 100); cout << "*** cache size " << md1.getCache() << endl; testIt(md1); // test after everything is cached testIt(md1); // test using no cache MSMetaData md2(&ms, 0); testIt(md2); AlwaysAssert(md2.getCache() == 0, AipsError); cout << "OK" << endl; } catch (const std::exception& x) { cerr << "Exception : " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/ms/MSOper/test/tMSMetaData.run000077500000000000000000000003331476623553700207230ustar00rootroot00000000000000#!/bin/sh dir=`echo $CASAPATH | awk '{print $1'}` name="${dir}/data/regression/unittest/MSMetaData/MSMetaData.ms" # Exit with skipped status if the MS cannot be found. test -e "$name" || exit 3 ./tMSMetaData "$name" casacore-3.7.1/ms/MSOper/test/tMSReader.cc000066400000000000000000000045001476623553700202230ustar00rootroot00000000000000//# tMSReader.cc: This program tests the MSReader class //# Copyright (C) 2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include int main(int argc, const char* argv[]) { try { if (argc<2) { cout << "Usage: " << argv[0] << " ms_name" << endl; return 1; } String msName(argv[1]); cout << "MS Name = " << msName << endl; // construct the MS MeasurementSet ms(msName); // start up a reader Timer timer; timer.mark(); MSReader reader(ms); timer.show("Construction : "); cout << "Tables : " << reader.tables() << endl; Vector tables(reader.tables()); timer.mark(); for (uInt i=0;i 0) cout << " | "; cout << reader.rowNumber(tables(j)); } cout << endl; } timer.show("read to end : "); cout << "done" << endl; } catch (std::exception& x) { cerr << "Exception : " << x.what() << endl; } return 0; } casacore-3.7.1/ms/MSOper/test/tMSReader.run000066400000000000000000000010461476623553700204440ustar00rootroot00000000000000#!/bin/sh if [ ${#AIPSPATH} = 0 ] then echo "UNTESTED: tMSReader (AIPSPATH not defined)" exit 3 fi DATAFILE=`echo $AIPSPATH | awk '{print $1}'`/data/demo/dishdemo/dishdemo1.fits if [ -f $DATAFILE ] then $casa_checktool ./sdfits2ms $DATAFILE tMSReader_tmp.ms if [ -d tMSReader_tmp.ms ] then $casa_checktool ./tMSReader tMSReader_tmp.ms else echo "FAIL: tMSReader, could not create tMSReader.ms from" $DATAFILE exit 3 fi else echo "UNTESTED: tMSReader, could not find the test data" $DATAFILE exit 3 fi casacore-3.7.1/ms/MSOper/test/tMSSummary.cc000066400000000000000000000047451476623553700204710ustar00rootroot00000000000000//# tMSSummary.cc: This program tests that VPSkyJones works //# Copyright (C) 1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include using namespace std; using namespace casacore; void testSumm() { // Note that class MSSummary uses LogIO. Use that on a stringstream // which will be printed on stdout at the ended. MeasurementSet ms("tMSSummary_tmp.MS", Table::Old); MSSummary mss(ms); ostringstream ostr; LogSink logsink(LogMessage::NORMAL, &ostr, False); LogIO os(logsink); mss.list (os, True); // Remove the extra fields (time, severity) from the output string. String str(ostr.str()); str.gsub (Regex(".*\tINFO\t[+]?\t"), ""); cout << str; } int main(int argc, const char* argv[]) { try { cout << "MSSummary" << endl; cout << "--------------------------------------" << endl; LogIO os(LogOrigin("tMSSummary", "main()")); if (argc < 2) { testSumm(); } else { String MSName(argv[1]); MeasurementSet ms(MSName, Table::Old); MSSummary mss(ms); mss.listHistory(os); } } catch (const std::exception& x) { cout << x.what() << endl; } return 0; } casacore-3.7.1/ms/MSOper/test/tMSSummary.in000066400000000000000000005176651476623553700205240ustar00rootroot00000000000000‹Ò«æVìÝ `Uþðô.…B¹9Â) GzÓLZ¦”zФ”C)i›ÒH›”4ÊYäEnÄUD¡™´9QDP<¸äA]QVDþo’_hÚ4Pýw‹¸ßÏîã%™ùNfÞ̼™L&Õ¯Täæª …iÆÜ¼ñÊž’j'“ÉÂCC¥Ö:ÌVË‚Bl5‘……†‡He!á¡ihõÏŠ³‚|£ÚÀf%S«ÉÓè\ÇFËʺËth9îÔ£ÓúNPÅ&$DWãvPõõ/ –³õ& Æú¯Îë¿o´*:­o¬2&9.I—˜ðÿßþüúÂú¯Îë¿_llßê=üùõ*Ãú¯ •¬ÿ¸ØÁÕ»ü…õŽõ_#*Yÿƒ£û§ÅÄWã&ðŽÿ!èÿk„óú§T%&¿¿ç²0¬ÿšà¼þû(c“‡FWÏ©ŸÕ_Xÿ¡!èÿk„óúOJŒKPÅ%ô¿¿ýxÖM¨lýŽNŽQÀ_XÿaaØÿkD%ë?91&V©LL®¶à/¬ÿà ìÿ5Âyý+S’cb«óàŸ_ÿ¡á!¡Xÿ5¡’õŸ£JŽœ–—Ð715-ðÿ½)T}ýÉ‚e¡âõ¿p|þ«÷^ÿA5¼þÃmëý¸÷ú®ÉõÏA¶õŒõ_*YÿªhUµþÿÊñ?,ÇÿQÉú®Œ‰|ßÏÿ°ÿ×çõoT§çhzdªÕö¬=ÂBB\­ÿ ðð ‡õf»þ$‘ʪmîâ|ý—2IÏØC/•¸æÙwVRYqcÅ/)G­ÕÑ®»Ù+µ¬Oújò3hT‘ìŸÚÖ’5zC¦m*nŸˆS²½â¬ÏŠÝnÂÖq˜§ø-t…×¼¬ßLVx±^…Ó• ƒk;\Í®0ÈÛöA×éUk÷WáUŸÔØhÕ€Øä /ûÅ+Ó†Æ&+m÷¡W*ÞAQ!åkÿ†­â›Ð7/G·_­ðzÇ u†Õºs§bZî§ÄÆS¥Å$N‰OP²çÍXi%N%.IŸ¯5jõân!n n· ‰Ïk÷èÙ£gÙ ôŸÒŠ«e}L+¬¡øÄyEÕ_.¿‚ü¬cÚWŒí™}…XßVo›‰FâK•5·õÝËšÙuh^ñ©C³Øž—kNëkÍh]"{ó¥ˆcD êB…ÒhÐêÆD±<¨Y<©øZ—?-Nœßèääèá¶ÇuîÌ±í¹§*.žMÔç{I¥;JRÙÎãÅJã…yC†>§ W—¦Òæh2ûªj6 9+-*Ù5rЦ M­R²¥SR™­ÎÓt×gjrª4…‡\L!Fo0ü¿f!.7uŒ±JSÛ5¡Òæ–l­4h]§ºLm.«½i"ž™¶Ö®Òþâ•¡·½G•Æv×VyT7ÚòBï¶IºÛCÜôÄZÜ9XÝñn!»ªŒäûÏiMq»Ým™íÅöÏT_#þç4¢¸hÁUiD¿øÄ¾±ƒÓhì¾¶LúgµexUÚ²nLbr2;ÌÆöE{Þc¯Z{ÆÅG÷gçi©±qý¨ª³=%­éy3ëˆ1Öƒ™Ø‚ŠL}kd©Ô¾òmÌÃC5F½A:AkÌ–ŒŸ µ6V§6jò¥µ:i®Æ¨1äwa£X‰¶)*ñj±|Žñ¾Û郛Ëö¢Ùö¬|µO­tµ‹¥ÎµÎX›¢Óó«ºV|ãc£•q ý%U1Ù–µ%[Ö,¶ŒÆlm>ë± MŽZìÅò5Æ‚¼r+ôξ±Ã’Å)²Ç­UÙ©&+‹uÔÚñëV<Æ {<©Q›«¹÷âúVXÜe•/nb¥‹kmþ¿Ò÷ŠÏ»Jªp4t˯¤¥î¹Ix‰WÄNî!±y²4šLÖ2™š‰Öí¡|ð6…Ê4èî ôOXPÇë±Mm\ÛØ3Ëöó<=ÛôÙ6ô@.k½>§Ü²ŠW‘’­×«º%ë'H³rÔc¤Ým•:'G*žÔŠÇ,ë’ØÚ,©ÊPP…½½¢š[øªõlql“Mj½ôÖXÜ¢óÕ¹y9lµÚÏãÕ9÷^Äd‡V×á’¥m'v<ÊéÓóÅÖç­ulópÈHtýÁÛ9*´„_RÊ`etrZŸ8ñZZ‡¤‚œ|µAšÇ*M÷¼lu¾FšÎýNËÐGÒ~±¦±µ™88V›F `Ý qÜ lmQ¶õRÛ¸ÿœµ_[–ßÇúÅHW¥†XǯΑæg¨uR]AnºÆ Íb§ñR½®;ë:XR˜oÔäþ–¾âEü¶Žk??XCL`ëŸþ1§»ôU…¤a¼>S›¥e§8 r´le÷­Êç— ‡·N•/ß}¿¶PõƒGfåKðÿº¢à¥ÉÓgd‹ÃRT1•¬,ç•”•£WºeÛÕª´Dë—oS5Ú1ÙFëÆ©Qgd³Ó3–ÖN²}0±m©UØ%}$åÕøÆét¡¬ÂbףŶ}ј/®êŠËn=[³žŸV}Ýh»qÚÈj¾%*m†=;/cnÿЧžxÎf]^Ûe’Ê/ ÖrüÞ°–ÄÖ ‰3Ù¶²E¶_ÛµÏÙ˜ÿ梻,¼çÂW8]·¯vgáÅõünö«-YÒtÈ·]Ee'´ìˆ$~s(UçKéêô=ÛHògÛè>oö/e\ÿxqûPÄæµ¹j#ëà ¹ùR^ËÎ×Ä%#[­ÓirèJ3ëi ¥éj]fž:Ÿ}ÀÑäçéuùU8ñ‘Tá"³ç}i¢{ÀM­Œµ×›ÄÙ>ÙÚÎgóY£dd»þlkß8…V¾p®CöÖ©úüZoK äΟEÄÏlùF¶¦ïïLW8•ðO%ÒbbTɉÖ9ÿç"œLTÇÉĽ:TÛ]4lóêo»w¦§õâ–xõ#ƒmQcô†ÂnRö@ÅÖ×–;غ<±¸ƒÚö®Ø•7Ì JƺC8Ìp•/Té[»*÷³Þw¾2ù+¬ U2íËï¡TŠ'(U>ÇøëûcUÎÊï—+ÞÊc? Ö+»ÁÆ>ó•ÜöS‹Þ×ý^Kr¼ò%©ü.'qHý˜Ñ lö”±ƒÙ™íö¬U]«âág[«¬×´¯Rû$v"Ž«ö/ÖUniçoèëZ¿Éî[¼žómRUnðjÞt*lçÎ_‘×egcÄcÎëÉwå PÉm[ÿímHl·¶úÚ×½‹®©²³@7¯»»˜Ž¸9í÷ž•7‡Wå/{Wþ²½+Ý`=é¼ëy–}“wüÎÙÞ Ž¯9½^öµ¥ãë¿æ+—qøFÌñõ;ß9½èô®ß/”½ìZ|¹×®T;¾î|¡Öqhù‹—ŽC\\ßs¥Âµ¯rmSáÂã0ûå“róQîÓºãJ>ÐÞÙ oæ³®eñŸ{~±>¦H.uçcÃŽá®'õÖÕâp]n#t8[u|Ýé„°ÜÀŠç!öÖ¡eÛ;Ÿ*¬M[î°f=º³m8õÃÖƒöÁN½š8ØÚAµ¶Ý2oím<â¬Gqq>*ùb§)Ù°ü˜¢Á4h€äΩ€{ÙC±óóê“£ÏëЬ’» øË\ýþ#«|q×ßÊB‚‚*þþ?(,¿ÿ¨ âv(NÅ“ ŠÇ0ëÎbYèVÈá lWéŒ?=WíÛf\¹Ôž¯dôw^,¿ \›–¶ûëE‹zÜy­¨·cp"¥!nô†ö\fŸ°; Kr6š{а<‡aEôØ“†-r¶†{Ѱ­Ã,ôØ›†qv†ûаkÃì;/ p+&¥ÇµìËç0ŒwèªÅèIôÜO\>zìFÃò†Ñcw¶ÈaØ·²v‡mufq+kq؇agÜÊÚEvÍa˜ýœÙ›†¸— “º—µ‹8Læ0Œw/këò9 í^Ö.ÖåsVäp½D¾Èa:kÞo«{ÙòXÜË–ûˆ{Yûœ¡Çââ\sx\Ë>2M7À£lù¥eÓ•y”M—÷(›n’ÃcqZ£¦•çQ6EôXl»Eï±Æá=¶:¼‡Åá±8Ý#Ó=ã0ÝkÓµ_3§àY6]©ÃcqZ2ϲiñžeÓJò,›Öh‡iå9<óEùEù5ù­ÅŒÅ!sÄ!sÆá±8Þ5‡ñìW}iX=· q’Òsq’Ñcqâ[û ‡ñF;<¶öô\j«îœÜzÓûúѸu%¶¡xq¡+M$¶ËGâ©^K‰í–mqíXéÀŠxɱ3+â=ÒâAT!±]³O¯Å‹Sâ ±"±]ßJï?Ž•§YÙÀÊ>Vβr‹•ælc ee+¹¬ÌgåVv³r’•ÿ°Ò˜Í| +ñ¬hY™ÍÊIVº°md+‡Yñg ÍŠ–•Ù¬,g¥„•Ï=mŸ~Eö{j /j‡uáGíkoG±}êQÕ§vjHmÕ˜Ú«)µ™ýâèCÔv­¨ýÚP¶¥vlOmÙ‘ÚóajSñFvñÒé#Ûqº³ÒƒÚZ9úŒ±â îJe¥yÅn5?óÆx¥² 7Wm(L3ææõˆWö4ªÓs4=²dÚj{™L"ëð°Pk- ²=·> “ɤAaᡲ`Y°T$‘ʪmî¢ ß¨6°YÉÔjò4:×ã±Ñ²²î2Û²HïÔq«/ò*ÿ¼lo(êçxŽçxŽçÿç’ûÌåñ?°úÞãÇYHHhÅã?{†ãMÏŸÙÖØ–= Pis4™1úœ‚\Ò¯CÜL;²R+.IŸ¯5jõö=Xñ´=§s†?ëìQëɶ8j*=©EoYË:Z_µQM/ˆÓr+Û)Ü}íI±ˆw²&CoȤWZˆoe{¥¯&?CRv^ïfŸ³{Ͳužª´\ÿt®÷ÿ4•2¾zöÀ»îÿ!ìNçÿá²°Pìÿ5aÇÌýÅs­«Qw¶)aaùµ.9 É Â Å¥Š‚[»¸ïë|)¤û-”PÕ)ÞõEaÄŽ™ýL{n}½¶÷ª ¬¯Ø¼ïSù‡-Ãålò^ åm7ßQ!9ð‰üÚ‡»sÉ%¡ñ¥y;¯\ejy~±iÞMDÔÃÍn‡_FËš¹ÂÚòºñ&6Ž©Çj?¹×«ƒä«ï5Íù¼‹âH;‰buÒnî—ˆmq/ËMkR#>ËÜ¿õ%îPÛ¦æ^Ÿ7V4Z}ÒôýÐ9òÍû†ÊÛžŠø`û$St£¶Š1}e‘Å>æ‘‹…ÍõÜM·ž5­ ™ÁÞãɈ+Û›7¾kzáÀM.?c©=l¦œ-g¯)–gvÞn;ÍôÃr_ÅŒßÞ>Ö71o~üuÓ¥£½LÜîhÒª£M·Çv‘o9ÔÆ<ÓÜŒóæo,ÏýöÔ›&Ÿ=_ ɯå7•ûÍŽT6W´nßH±½ö9›¦œ-·¼ñ%߈ݲLMGïåº]“˜‡gd Kæ{™;œi!¿¸}½©Ë®%ì½vö:qì¤Ðzǯ®…¬y ¬ë'gZ²|ÏyÔŠ–òw¦NåäÍš)ÎmÌéÆû(öŽêoú훑òmqLm GE°é™çÊ[ o¤q#êz™Ç|;›½÷¼â9!*±­#òöP¼øÃvî×÷ ÿnè¯øäæÓó[ߊh»§–œ-£|ýGM„1YmŸ½á¦ø:ü_BS¯çä¬ý"ÞûzYÄcsxS×É[¹ÁµÌqûÿàŽï,<î½R>'äXD‡?ôbÛ€éçõ׸ˆ„/¾÷0=ÕJ80y˜É¿WSS¯¦^ >Š8±è¦ÀÖg¯}g7 SG˜¹AãZ™Ž­ñ”³í¬¸ßÕDùàÙ衘>z¶ wMÛuÁζ¦ÝÑa¦ŸF¦Œ±nÜñi Íâ|t½qˆû©ß­q;âßÚ,ŸÕ4¦˜mWŠckfÉ¿êm>rBŽñõSƒƒM+Æ/.> Ý"÷ÑÚ|îÙ½‚ƒ?’’– Á'º™ZöV˜7õÞÉ^·c“ï•&Šë¾ç¹un’&ÉÙ¶/?;y¶ÉÇß×ô}‘ï¼°®YÚì <‹ÛìZ—ï"Ò-érqþÆŽñS<¿5XðŒ=!4nû.×iÝ [o&ñ=[(’ëPß\w”{ÿ˜D!n׬]䉛v…lj’§ýkˆiaÝï¸#S7 ËŸ}Bønþ/Ö<{Ï#´=åb>áÀ7BÜØc\`”Älºü„ÉïL®‰µÁN6­âðC1¾Âº¯ýQEÜ_ß¾°P\ï½ôÏg˜ØþXv½DþÉ–fæ°çrïûCX:¼(‚½·iÁεb?`*špXh0ã!+êÎkÐZNóðKÅדŸ‘§\PËÅõ´õ'.îé}ÜÙ%ñª—¹œiG‹?-i,o»g>kÃ];»§ùš_»xÊäïqPÈxÙÄö_¹Øop=ÏDLŸiºÝº¡ââØ)ò%[Î Kn<Å™K²­ûeÊ…³;§/-³}Étët€¢KÁ7¦ïæÔ6|Ø n_Å\ÏѦT÷³}Q~CÒÜ|ôúŽíÜ­„flù~é%n'ÍÛm*>­<Äæ¹p|ÚRaCÿ£œØ¿°}£×í±Ä唸ø¾°d‹š;øÕ ¡Oü\Å~ÑÇžüۋܸ?sl?*¯ÿym…¸í8uû‚ká”ç÷=Ooóá'žLk>s¾³÷y!à›—¸Ë'Ü„¦«n9 Ÿ¸ëU!péYNa¡<ª¹›¹âð¤³þŠø`OîÓ’åòQ­Ü‡ï-þÑڇɇ|ÌöÿÙNÓg럛úY*wkû¦I ¼¦?ÅRÏÄúvó;}÷ ?õÜî´ü;:¿`bdzîô>ŽísNÓ±÷s®Â.òÊ-nØúNÃ Þ ÷ó™Üþ}æÒl§é³ý0"õdó‘wΉýŸÓpÝéXaM\’÷ãÜJ¿ëNÓ?ñt€Bì ÙñÇ4C&qjñXc ®kþäfnÛíNÓÙ¿T¸ºéÁ«ý7Ü&<§é÷›U_aN/5ý”ý½ÀŽGNùÃÂá¡‘ç¹È8o󹄱¦ŠÃF>.Ü~£¶bRã¹z™‚¼âpq›aÛ^ÄÄ> Äypš~„|¾ÀÚV`}ªÜÿÖN×_kÔêí/z”Õžât>aÅÏ:{Ô]Bÿ¤Ò¨¾b±Ž’24•Þ^îæF#ˆÃ;Óc±ˆëß;Y“¡7dÒ+-Äw±½ÒW“ŸaûDÄ'm+›[wšãÔ»`_¬ÿ)®÷ÿ4•2¾zöÀ{ìÿ!²ÐŠû¸,,ûMX´ïj½¶g³ø÷w|òzƒ,þb—3,]¬áç¸v?¶a9ÿÐ2ŸÌ‹Ëxù§ãûÇ-㛫Þþ¬Ï:ËЫú5]½Ör}î¶Ùâ×Z¾ø5{mã½™üüDå—ÏgòAϯóÍ¡ þ•ß:ÿè^²”Ÿ0MÖO;t)¯Ú·u¿jéþ›=— }måJaÚ ¦©k,/×í?9ó%ËÀMyÚ|Â*ßÁoNçG½ÕtùÓù o¾ñYÐþå ò³;ô‹ùËnXÜ`1¿·mÇésw­¶4 Ús¼àEËî ò/~Áòôü–燩ù‹»Ÿš¸l4ÿTpïéß.NãÇåkÛ;qÿJ­Ž‡š}†/=ôþ¹A ù O:ëyËñ%×/´;÷œeá£ýoŽYµÊr¹Þ’_Â>LãeOÄ®¸ÒeÿÝ7ïüúLëÇø—Õßwšý4ÿI¿‹±ÒÛ øf¿̹>h´Iï’~~Öâÿî0¿UWZ·Ê_Vº­´˜æçu4u$ꇠân¿àßÉ3ÿ¬V ç'Ÿîœ_{ß|~µr”ºø‡y|L»g¦^ÔÎå'×ÝXëËË-ogϺéÑl¹ÅGúö¨—Z~éߺÊÿÜ×TG3p(?àwoÿþÆ›âê}qõ­9|×-oFrÝgóºæÇ×þ´q.o|IJ@§¥–ekc&ìüj±e\öêâÿ^jQ-ôÛù•:•ïkÙ4Y5q(?lœê£I'UüúܦÜj?‡Wµ»õ¤ÿ£³ùe¦—·=>“ïɳÓ?Ú°ÄR«Ñ™+½—X&Œ.ûѼÈò䌭_͉WñÝs=woNæCÃOœžòú>ñ€ùt’ÿLþûæ3w4˜;ƒ¯?ý³)~ûŠø•¿w_‘Ô|‘e‡*÷ÓŽ/=myvÙ²nº”§,sNz® ­ŸÄw®õPƺOøÎM†ŒVâîýqìØ©Óø÷rrgüc ¿18«–êÇBÞ¯¤ùÏ?ïžo¹É¿6á‚ySŸ¬f~=ÛòÜÊå×jãø‰»nz¼¢èÇç5Z”Ó*5–î³Ë)g3&òíǧx J-à‹Ú§o+ý0ŸRØæ±}ÏϲLægî—û>aùÏ9ÏúÒ"ËÑ·›|P·߯t¢iþ@žW;¨E}9_ü{ÀÜwóøSëŸúõI?ã—'?ùM—ͰæË§L±¬÷ï~úå’B‹>²àæ³+c³öÒÂùëœû¦¤I2þ|žç쟷wáÛÜ<-LÖð?ôëÔª]Á(^qÎå‘3ŒÏí×wÞ| –C‡röìÑZÆ5_â¥]•iíµØðHqsÞóçÝÞØ›Â?ñÒ›Ã&WòçÚ5¼4ì^òâÚ'š¾8‹ ©5ggò|‡C':<¼Je ;[ï‰Á£[t÷olz~¡%{ñÁçú}ŸÄŸÏºr¸`x ?¬Ï Û¼#ù„ÐÂÇøñu–±ÙO6ð§^ÕùÔŸ2–ÿÈëµI‹|XFet>Øøü4Kî–µ™`¹ôsà°ˆÁüûYÆÑž|èO}ä}”|þÓÊñï'§óM'|}fGr2ŸÒ¶‰çç OðútÕ<Ïó:Ëüð~æKçFY4™‡‚_½¸Ð2ãÔêï¾ý<‰O>¬x)«O ÿÓçÖœüZÁ½;ñ×_<‹ø¢ýGo|˜`àç_oØwüXþ›o/ú¤ÖK­úl»8Í2+çt¼G÷ –€ÿl9Ô'˜ï;+ròÂ>íù]]wï[æ?„…iéá„t>óùÀù×’ùÝ“vž“ñ··–ƽ~Qg9ÝcÍÆ‹£,)ƒ?¿eª½À’Yê¥ÜÇÅóþ5Æ¥=:€w_ßmùõ)1üË·bFõÐOæÇH|}ëWãùÿöy­Ã@ß5£(e]¹–°ÞÒ/f™i9³áàñ/.M³<Ë™:?¤ˆä{WN{yJ0ÿx£Ñ¦´ç÷ì=õ›T?–ß7&AÖj`:ÿæ¤iÓÿ—̯QusS –fêÏ^ÒY.¼r3àÔ¥Q–ÕÛÕ­we0ï)ùbGÇæø½{<)ïÃgµ“õ8“7™?qó|´Ç©ñü°ÕݶG2ðÝäÓžbó£í³¥x<›Ÿ§>ºtô6?òŸÏ'®¼¬às’ßô,éÌ'oÜÕib¿ö|ߌé›ÏååÔ5xP:¿>½É€¤AÉü»ê ZÄægP“·?dó3aú#¹‡Øü,ëvðŒ÷°ü¼ÇÞè°ª0†W¬ïÞCÉ_üù‡ó·Îçׄ{ñZ¢×¼r&ûÖ„±|ÚØ¢µêÄ™–Æ­|¯½0ÍRwôÉ›¾Ý&XüÖv4çó½ö›3NnÏ|£Ù[ucxŸM?ýú{b:ÿéú ãN$&ó‰kÆÞ:ÊÚÙüY2—sAgÉÿ¢ãw3/Œ²ÌszÈŒo§YŽæO|t¹‚?º>u³¼n0ÿ¶éŠ4Ü¿=fL9Ò‘-W»— š¯X;/ÉÜüúw“ù×›öŒj×s‚eí±I¾úoÙô4×çç;ÊÒ¹GTÑ "ùýa#æFo æ{l|æ½ÛÚóõÞ“}™3–_³4K=a@:_{ü5ïe’ùþ³Þ;{•µÏ»o܈fÓ©þä{mÙtÂÎ «ÞZÌÿÑúH¯ Úóï¤õ?ÿÈÞö|³«#ŠäÊtÞmzöe2?©íVŸ‘ý’ùîOwküè9EùFö¸|¶_4šý¡OÁåQ–~?2F3z(ÏŸÝøæä=*>ùôþ—Æ~МOž67:»Ól~G¯+­ƒ×ÌäUÙ%/gà÷§&4X}j±%E9ø…ËËY²Œú¼“Ï«,篶šk9 âÍ+F®ŸÕUɯ¹üú#²¬æ|ì¸Ä%í6ÌäÏî|D*o÷WpBòÛ¨¼[ÃÚmz.Yd9œzæÑôÅ -ã4?üîö¢Êr¿ÏSà¿Ãåùpõ½Ç]ÏÿeAAΟÿƒqþ_ÄÏÿ¡¬`%N—aÐäjtFuŽõC¼—uŒ¢²äô!]r›¹/s Õ­e¹gצ¥í¾mææ0ÄþØÓᱯÃã€ÿÒüÔ„R&Õ¾¬Ä)ããt™š‰nì±XZ±âÅJŸ}ÆXñw ¥²Ò¼â@·šŸyø3Æ+•¹¹jCaš17¯G¼²§Qž£é‘¬­¶÷Éda!!R± µÖ² Ûs› ™40(,<0T, –ÊÃB‚Â%RYµÍÁ]äÕ6+™ZMžFçz<6ZVÖ]¦c[éú!îµ÷{&à¾q¹ÿ‡Tß{Ücÿ÷— ûP˜ ûMÿýXíÏŠÒ¨Öeª ™Jc¼Zça̺OIÙÿ6#öÏJpìpÙûþÎTóT£:êyT¯¡z'ÕG¨¾dŸ m ͨ–Q=€êÑTO¤zÕ¯Qm¡úÕרö¥MPJu/ª“¨Î¦ºˆêg©ÞJõAªÏP}ƒêÚ”;SÍS=Œê<ªçQ½†êT¡ú’}—ð´Uͨ–Q=€êÑTO¤zÕ¯Qm¡úÕרöõ²ÕRª{QDu6ÕET?KõVªR}†êTxÛêÎTóT£:êyT¯¡z'ÕG¨¾DµÄÇV5£ZFõªGS=‘êET¿Fµ…êT_£Ú××VK©®.·‰Ø>)±}R:|W«TbÛ¬Å}DÜ_”ÚܼM¢!ScÐdÆ«ó{?qx[I…FâDÚ°Ò›•¡®Fл¸Kj’ëãÿ‡ÈÂÏÿCqü¯)½ö)q ´u9E½ ^Çëx¯ãu¼Ž×ñºþ\žÿ‡Vß{ÜëúxhÓõ¿ðœÿ×ñs/;ë¿ÈÖSis4™ÊlužÆzPbý4à&~ÙçgD¯Z?¡ŠŸ_Sm£Hj±P–î¯ÏÔäÐxXö/Ýèã­;=W±w²&CoȤWZˆog{¥¯&?CRöøÎÇlñ«ÇZqIú|­Q«×9 ©|€øñÞaê-íÎ;V6eq€'=N­ÂHŽKQå‘­_´Z¯ÜiCÛU„JÜ5Q ×ûšJXyèOºëþbÛ_Êïÿá²°0ìÿ5!téúÈ‘Oô)qU_Î|$jô.¾ÔUýƒdC¤ÿÊÂWuŸ-?—t¾6¯ÔUÍ›nDN¨÷ŒËzȺOJæLjÕÛU=µ§èzhx”«zõ€ßK ^ë]⪾Õ`)—{y´ÙU}ãÂÑÒÒ†Š(Wõ¸©G£jÿÚ¿ÔU-뵩TŸw$ÊUmž–,úz­ËúuÝ6Ó–±È]Õ×CÏ–L|sf”«º8íJdƒcÍz»ªÓœ*½µG[êª~o難°ëK\ÕÝ?mÂ]éö¾ÉUýÁû¢Bƽéª^ûѳ%S7¶)uU»_xX‘µb—ÂU½ì§W#LœTâªnôïå&Ù=.ë¨õWJ÷¦­ŠtUg¶ü¥äÔÌ=Å®ê¶[Û”F5)¸ªçmTbôîéªy¢]d-ÿì(Wu—Ü+%¹'oGºªÛËÞ‰Š}¾”«úùÈ–EºªgϽZÒkK3…«zçá0ápØÉWõÙËÏGÝ*<ç²6' -=ïåª~sR~éÁÚc]ÖÚU‰%×oŠtU‡?áQRÿ½'M®ê~ûGF˜1=ÒU½«ïë¥?ìû¸ÔUûÊÕ¨¥éïGºªðe¤ÿù«œ«ÚmËÈ«ú¸RWõ}¯E¶ñmÝÛUý“ÿ%C~ÞVêªö4¼ô É@—uÀGMzwš²&ÒU­œ¼¢DõÇÍWõžh•yVö(—õºãn»vn_åª>síµ¨C·žtY>é?%ãÒ²\ÖƒnÞŠLž=´ÄU½åâ¸Òu§£\Õ» J_ëÖÛUýû/FóÙ‡óK\Õa‹?*m]|¶ÔU=ubii—vJ—õ#ΈjäéªNèûxÔú½]ÕÆ–¦ÒÕAëJ]Õ¿o=\1ñ«WuÂ+u{ÿ17ÊU=|é¾RÉñ%.ëÔ§K?³×ìª~xÊͨǟӕºª¿óJätä\ÕÆü…¥çšùìrUGd^2ûò_D¹ªëŒîQº:>ÆeÝåØªÈi êš]ÕÊ©+Jgóîíªž8²G¤´î§¥®êc3"KK_ã\Ö—?òê½Åm¹Ë:/D(ý×Ì´(WuAæÌÒ…ÂÎU½¤ÿé’ôGöE¹ªgþйtF÷Ç\ÖþÍFöo:ÅeÝÞtSØT÷B‰«ú[Óuóó…g8Wµâppäò_‡E¹ª_{=>2lHWõmÅ—/uUωTGÅšW)\Õ÷ûüàAgŒW* rsÕ†Â4cn^xeO£:=GÓ#+¬úÞC&“……„HÅ:<,ÔZË‚lÏ­ÂC¥Aaᡲ`Y°T"‘ʪo\+È7ª lV2µš<Îõxl´¬¬»LǶ(Ò;õ¢”‘HÜ.°‡õTÚM¦2[§QãÕb[¸±ÿeµŸm½ê.½YIµ"©U>Ý=Fo0Ðô=¬ã»Ñˆn¶¬õ_ñ¹¸†½“5zC&½ÒB|7Û+}5ùöw£¡VÍÅwŒKÒçkZ½ÎaHån3“øSoéFKб²)‹<éqjF p\Š*,.”WŸ}ÆØ;m(‘ u5உʸÞÿÓTÊø@©?ç®ûˆm)¿ÿ‡Ë±ÿׄ3÷Ï}´®bDÝÙ¦„…=ä׺äD,<$/+—( ní⾯ó¥î·P>@U§x×…;fö3í¹õ ÷ÚÞ«‚p°¾bó¾Oå¶ —³iÈ{5H”·y4Þt~|G…äÀ'òkîÌ%—„Æ—æí\¼r•©åùŦyC4Q76»~A-ohæ kËèÆ›Ø8¦«ýä^¯’¯>¼×4çó.Š#í$ŠÕI¸º_"¶Å½,7­Iø,ÿqSüÖ—¸Cm›š{}ÞXÑhõIÓ÷CçÈ7ï*o[x*âƒí“LÑÚ*Æô• Gû˜CF.6×s7ÝJxÖ´&d{'#v®loÞøZ¬é…7¹üŒ¥Bô°™r¶œ½¦XžÙy»í4ÓË}3~{SøXßļùñ×M—Žö2}p»£I«Ž6ÝÛE¾åPóLs3Λ¿!°<÷ÛSoš|ö|U(<$¿–ßTî7;FP}Ø\Ѻ}#ÅöÚ älšr¶ÜòÆ—|#VtË25½—ëvMbž‘%,™ïeîp¦…üâöõ¦.»–°÷ÚÙëı“Bë¿ »:t²æ5°®ŸœiÉòu>cäQ+ZÊß™:•“7k¦h8·1§ï£Ø;ª¿é·oFÊ·Åu0µ-Á¦gž+o%L¼‘ƨëeóílöÞóŠç„¨Ä¶ŽxÈÛCñâÛ¹_ß(ü»¡¿â“›[LÏo}+¢ížZr¶Œòõ5ÆdµU|ö†›âëð M½ž“³ö‹xïëeÍáM]'oågÔ2Çíÿƒ;¾c°ð¸÷JùœcVüЋm¦Ÿ×_ã"vt¾øÞÃ|ôT+áÀäa&ÿþ]MM½Z˜z5ø(âÄ¢›[Ÿ½öÝ$Laæke:¶ÆSζ³â~W僻s‚f£‡búèÙ‚lÜ5Al×;ÛšvG‡™J|™2ƺqǧ54‹óÑõÆ!î§~·"Äíˆk³|VÓ˜b¶])Ž­™%ÿ~¨·ùÈ ÷Æ×cL f 6­¿¸ø€t‹ÜwDkó¹gWô þLHJZ.ŸèfjÙ[aJÜÔ{'{]ÜŽM¾Wš(®ûžçÖ}¸YH>˜$gÛ¾üìäÙ&_Ó÷uF¾óº6fi³+\|ð,Nl³k]¾‹H·¤ËÅù;ÆOñüÖ`Á3ö„иí»\§u3Ll½™Ä÷lq H>®C}sÝQîýc…¸]³v‘'nÚs$Jžö¯!¦…u¿ãŽLÝ(,ö á»ù¿Xóì=wŽÐö”‹ù„ßqcqQ³éò&¿3¹&Ö;Ù´vˆÃÅø ë¾öSFq|}SøvÂBq½÷Ò?ŸabûcqØõù'[š™ÃžkȽ;ìaéð¢öÞ¦;׊ý€©hÂa¡ÁŒ„¬¨o8¯Ak9ÍÃ/_O~FžrA-×ÐÖŸ¸¸§÷qg—Ä «6^ær¦-þ´¤±¼ížù¬ wíìžæk~íâ)“¿ÇA!ãeÛÿ}åb¿Áõ<1e|¦év놊‹c§È—l9+,¹ñg.ɶî—)Î´@Îö%Ó­ÓŠ.ߘ¾›SÛ<ðaƒ¸}s=G›RÝÿ]ÌöEù IsóÑë8¶p·š±åû¥—¸4o·©ø´ò›ç6ÂñiK… ýrbÿÂö^·Çn—S>ââûÂ’-jîàWƒ„>ñ_pûEÿyBðo/rãvþ̱ýX¨8¼þçµâv´ãÔ5î ®…S^œß÷<½Í‡ŸxR0­ùÌiøÎÞç…€o^â.Ÿpš®ºå4|â®W…À¥g9ý……ò¨ænæŠÃ“Îú+âƒ=¹OK–ËGµrWT¾·øGk&ò1Ûÿg;MŸ­nêg©Ü­ ïš&5ðvšþK=ëÛÍïôÝ+üÔs»Óòïèü‚‰̺Óû8¶Ï9MÄÞϹþ {¸È+·¸aë8 O4x+ÜÏgrû÷y˜K³}œ¦ÏöÈԓMÌGÞ9'öNÃu§c…5upIÞs+ý®;MÿÄÓ ±/dÇÓ ™Ä©}Äc1¸®ù“›=¸m·o8MdÿRáê¦o¯ößp›<òœ¦ßoV}…9½ÔôSö÷;9å {„‡Fžç"ã¼ÍçÆš*où¸pûÚŠIäêe òŠÃÅm†m{û,çÁiúòùk[õ©rÿ[8 _òÔ5ÖÎä>Øþ›i]¾‡ÓòǬœ-ˆ}OÈ? ó‘;å_Y´L˜ªú·Pøö-î¼*Äiùß½X[1}U·(þ{kßTqx‡æÇ¸“O˜æ-? œ±Öiú‹ÒcÖ¹ Ëýä4œµ‰ðhÇÃÜs)óå¡fO§ùo×úmÓÇ’z qÙY¶³âðO—)Œõ–0ÓpUØëñ¢Sû ž.,}{#'}þ#näû뜖ïÛ ub_"öž¾ê4\<†±ã€ù†~)âóþùF·¥ÂÁð_„7º5,îtüG§åcçÂàÚï ¯vÝ-°ã½ÓôÏÜX#4Œ¼Ìmµü"°÷pšÿ—û}˽µq%W8\i=VTM˜{mœàsá07Óü¬Óû‹ýù²ðz EèáÖ;œò?¯Ï”ƒ¶s—|}ÌÏ}Ù)ïÕEƶë+â1‰›œÖÒiýl}¾…âËæXßÐÝzPqøyŸÖæÇYaêøë×ÜŽWv8M?|b;…x<™Ò³©SVtj`Ssä•©‚Ø4¹}ÄiþýÖ‡˜¯ 4:½Ž1^©,ÈÍU ÓŒ¹y=â•=êôM¬ðê{™L"ëð°Pk- ²=· “……†ÊB‚eÁRY`X°L&‘ʪo\+È7ª lV2µš<Îõxl´¬¬»LǶ(Ò;õ¢”‘Hܾ`ë©´9šLe¶:O£4ƫŶpcÿOdµŸm½ê.}XIµb}P–î—›:ÆHoàn-n4&+¼ýUñ¹¸Š½“5zC&½ÒB|;Û+}5ùö·£¡VÍY©—¤Ï×µzÃÊÜf&ñ§ÞÒž´­lÊö…H½ÇŽs_¥Åñê“£Ï[Ön’¡®Ü5q®÷ÿ4•2>ðÞ¨‚»ïÿ!Öý¥üþ. Çþ_CŠz£ü· @Õ¸úûZ]–¾ºÞã^ÿ%0Ôáï?†‰ÿ%<0ÿ¡&¨ ó4ÒHi¼F_`ÐäjtF©RcôS¤Ó??U¶6_Êþ¯v‹$U‰[Š4[Ÿ“©Õ‘æ– Ì—fô¹, Òähò3ôy¿û½œP9Wû?ý%™jq¯ý?4$ÐyÿÂþ5f¸íï`IRXñÌ/Ô‰Êþ§Z³âA%TRáÏ,ùJÊÿ¦ÊÊß™óþŸ­›Ü³úÞCÜïCC]ïÿÎÿ)L"‘†Vß,¸ö?¾ÿ»^ÿ¶ã@¦Úxï‰ÜÃ=úÿð ðŠÿ+,8ýM°õ{õ²‡^Öó9‰í¯ÑÕ¥á~I9j­Ž†ø×g¯Ô²>©ð‡òìÄ]¶¶u„?ñGýþR¨6ÕÍ•êµ!FŸS«ÇSısXà ŠMHˆN‹ë+.W´Î¨ÑéÔR]AnºÆÀ^ñWÕºLµáΟ7tzÁ«ÂBj*Ÿß>•ίX–yaåËœXé2‹Cê )P댹):­1Ÿ=oÆJ+I%áÑ­Â_¡ì ¶¸uýHlgŽ' ¹ÝƒîÙ8™úñã—½q|ãØfž<4z°8q¶yh ãÕ9R=k*é„lmF¶”õäjñcœ}&ÏfºJ ä[¡žª±jw·r˯d;ºçöS7.1!Q™;86F•œ˜À^iïzSb­”a4èuùÿø ©B+ù&%Ç*Åý–=n›®¯ ¨¹úü¼lA›!Í3hòÅOú©až®±†i·†qORÿÙv©“;8m@J|\ß8Õp1`o¶C©ÚñivA®6“m>©aþ.{VÇ?Û.µU±ñI±ÉÑ*Û&ÓÉÞ,ÑZƒT¥ÉÍc;˜‘m-Ò,½AªÖ±ÿ[¿t ºgUèœ=UqñbãôÖIãcóô¬;ÎÏÓdh³ Å+gÆl”m9yz-k6ÖõˆÏÚ\k“U_ßíÖºòÖ›Zië¹ÿÕÖóúq ýÄ•"þQb×gqUkkñ3+Ÿó—sîi,̳ž¿ÐÛx$k²$åÏ@¼4âj‡¥¨bÖ§ëuZñh’—Ð7­o\2;šÄY&MìG“ Z]¦4Sk`G[#=¸ƒÚþÖUn?kÃ(“bcÅ“Ðå…mõšªŽþ]$·çsûCbû¨dì´š£òŸB܆ø:œ±;¾Ng»åFu8Çs|ÝùĦ\Êá€îøzÅšã° ºã {_VnœöÇ¡å7ûè’Û³â²*•ñöŶ=Œ–T¸Ðfßw‹¨±Yí^DæXí)Ϋ½Xñ–Ümàè^ײªá"Ì=¯ÿ8ÿý÷à°`\ÿ© ´Š;´SßÅvGï"IÙQÕþ_R°wv¾ô|š¤üá8ÖÏD[w*v¥âÅ ±Ââyd-VÄïpÅN´+þ÷g6ÿyø¬¢­þN¢¯íN›Å×\ /· ‡LbÅ—}ˆÓej&JÊN5¤Û±§3+Jmn^Ž&Ñ©1h2ãÕy’ò§âE©Òÿ’¿«ÿcîuý§:î¿×ýŸâ“rׂÄÿ, ®ÿÔû]ÞŽ7|ßïy‚šs¯ý¿:î¿çýß²Pçý?û?Ô˜Húü)~¥ìxÿ·øÝžø¹Ó~ Üå÷Aå¼ÿ+‡+c¢Wãí¿÷ØÿÂ+~ÿŒïj„m»oµ\Rþþ_Ÿ34¼Üý¿ÍûKþ6÷ÿ¶¸ºá®÷ÿ>×W¼Å†îB’juR£øó65}Ýÿ@Þ&[¥›¥ö›»±…ÎÔLdÿõRëR#­øÂÒûô‹ík[dŸ~Mæ|ûó]îð ½s‡¯x[í&1놜¯±ÞB–§6¨s5lÛ/732 jcÕVñƒ}Ëo…­¡iÙ¾Ÿ–›¤JLV²AA Ö_ˆ­eÐdhòŒzÖXzê²Ä-§sžAŸÎ¼P(e­ÔåAÝ*Þ •4 Z›Ö7®_?±’²Õùi¦6+KcÐè24Òtq‚F£»Ó,Ò Ö_f–= ¬R;ü]oàªÊm}ôú‡­¨^Yƒ¥õ-öËQ±î|åóÞÍRÑ_:ø¹¹žõŠe’xSXôà4ñ&¬ÄT[ÏØ–Åy·uyâî¬+ïÐÓO»‚¼*-Ë}ßÔ›Yײë-ÝScí.»Ä¨s´é¬ ÔŠ;x…›’5jÖyÚ7î*-·uÓ¦®ye[¤Ýߥï¼sÃr•6÷Zb«Ù7tÿ;:µåýßÄ]Ü\ïx5Ý?ý_=6â–ê?uKõ½öVUò0V±÷×hÅ.Uº§ÚoŸ /;nWiåýÃ÷Z_Özö¶NÙNkmÒšÞgïÝ/+‡‹ç`]•…ùl%[Wt¬â{w̬Ù*é˜mùw[É~ ý’£ÓTI‰Ö3£z*½‘cäé'°;±Š'KU^ŸÿÝ%id]’¤Á)ÊäØ~ƒc‡Æ¶-@³¤œ‚|)ëéÄy°f¼&çÁ\¸±É‰n„ƠЮ±uáâã*®ºæñZ݃¿îêY/.A%žÙ潑xe` êªÄ³ l™jÙ» ñg$uËõ Òr4tî4ÄùoZyŸñ.Z¹.úh•÷Ô¢5ª¤Ã ™‹þâZ¸ºåº qÖ:÷ÔÙ¶ÄþÑq 1‰ÉÉñ±ª‰âõ&ýÕZ”Mœ~‚)eŸ÷²õ⇇ÿÅ%³ýjÐçŒÃ¢Vã¯.—ÝÅ/ý+^uXþ:¡ãJ.ˆ9®ü¢“ãö 3wÚS|±Üu‡òc;ÿä>,–›€ãG òyÛr…ws8™.·ØåÎ[Ëe\V2’ó U¹‘\ž˜”«’ã{ùept¶Ìö;ÑÿÅšÖ½”Ö==?~†¶ mkh(¢m`4m<­{ÊEù¯(’ò§(šòg(–ò_SþåÏQþ<å/Pþ"å¿¡ü%ÊKùË”¿Lù+”ÿŽòW)ÿ=åàïì‡Çh{:NÛÓqÚž>¥íé3ÚžNÐöô9mO_Ðöô%å¿¢üW”?IùS”?Mù3”?Kù¯)Žòç(žò(‘òßPþå¿¥üeÊ_¦üÊGù«”ÿžò?Øò68¸Oî÷ñø~לü=Îl¿£]£IÛú‰»­ÿxØú‰§­ÿxQÿáMGÊûPÞ—òµ(ïGùÚ”¯CyÊ×¥|]Ê×£|åëS¾åR¾åS¾1å›P¾)å›Q¾9å[Pþ¡;çÓ®ÙŽÿ‹5‘Òº§§nglµ»ÅV{¬±ÕžE¶Úk´­öæmµåí÷MùR¾åý(_›òu(ïOùº”¯Gùz” |}Ê7 |CÊ7¢|cÊ7¡|Ê7¥|3Ê7§| Ê?Ä;쇴=¹ÑöäF­ãNÛ“mOž´=yÑöäMÛ“å})ïKùZ”÷£|mÊס¼?åëR¾åëQ>€òõ)߀ò )߈ò)ß„òM(ß”òÍ(ßœò-(ÿãù s£aî4̃¦íIÓö¤i{Ñ´½iÚ>”÷¥|-ÊûQ¾6åkS¾åý)_—òõ(@ùú”o@ù”oHùF”oLù&”oJùf”oNùæ”oAù‡(ß’ò­(ßšòm(/¥¼”òm)ߎòí)ßò)߉òSþaÊw¦|Êw¥ü#”ïFùî”ïAù”ïIyå)Dù`ʇP>”ò¡”£|8å{Q>‚òrÊs”WP^AùHÊGQ¾7åyÊGS¾åc(Cù¾”¥|?Ê÷§üÊÇQ~ åR~åS>žò ”O¤|å‡P~å“)¯¤¼Šò)”JùTÊ£ü0ʧüʤü£”Œò£(ŸFù4ʦ¼šòé”Ï |&å5”Ï¢|åÇP>›òZÊ?Nù±”Ï¡|.ås)¯£¼žòy”Gyåó)o¤¼‘ò”Où ”ŸHùBÊO¢üdÊO¦üÊO¥ü4ÊO§|ågPþ Ê?Aù™”ŸEùÙ”ŸCù¹”ŸGùù”ŸOù')¿€òOQþiÊ/¤ü3”_DùE”_Lù%”_Jùe”_Nù”_Iù•”–ò«(ÿ埧ü ”‘ò«)¿šò/Q~ å×R~åÿEù—)ÿ å_¡ü«”_Où×(¿ò)¿‰ò¯SþuÊo¦üÊ¿Aù­”“òÛ(ÿåߢüÛ”ßNùw(¿ƒò;)_LyåM”(o¦| åK)¿‹òÊï¦ünÊ¿Kù=”ßKù}”ßOù”?Hùƒ”òïSþå? üaÊHù(ÿåPþcÊ¥ü'”?Fùã”ÿ”òŸRþ3ÊŸ üç”ÿ‚ò_Rþ+ÊŸ¤üIÊŸ¢üiÊŸ¡üYÊK(ïFywÊ»SÞƒòž”÷¢¼7å}(ïKùZ”¯Ey?Êצ|ÊûS¾.åëQ>€ò”¯Où”oHùF”oLù&”oJù¦”oFùæ”oAù‡(ß’ò­(ßšò­)߆òRÊ·¥|;Ê·§|Êw¤|GÊw¢üÔïLù.”ïJùG(ßòÝ(ßò=(ß“ò2ÊR>ˆòÁ”¦|åC)FùpÊ÷¢|åå”—Sž£¼‚ò‘”¢|oÊó”¦|4åûP>†ò})Kù~”ïOù”@ù8ʤü ʦ|<å(ŸHùDÊ'Q~å“)¯¤¼Šò)”Jù¡”O¥ü0ʧüʤü£”ŒòQ~åÓ(?šòjʧS>ƒò™”Ϥ¼†òY”CùlÊk)ÿ8åÇR~,ås(ŸKyåõ”Ï£ü8Ê(o |>å”/ üxÊO üDÊR¾ò“(?™òS(?•òÓ(?òE”/¢ü Ê?Aù™”ŸEùÙ”ŸCù¹”ŸKùy”ŸOù')¿€òOQþiÊ/¤üBÊ?CùE”_Lù%”_Jùe”_Nùå”_Aù•”–ò«(ÿ埧ü ”ò/R~5å_¢üʯ¥ü:Êÿ‹òÿ¢üË”…ò¯R~=å_£üÊo¤üFÊo¢üë”ßLù-”ƒò[)ÿ&åߤü6Ê¿Eù·)¿òïP~åwR~'å‹)o¢¼@y3åK(_Jù]”ßEy åwSþ]Êï¡ü^Êï£ü~Êï§üʤü{”Ÿò‡(ÿåSþ0å?¤üG”?Bù)”òŸPž¾»“ÐwwúîNBßÝIè»; }w'¡ïî$ôÝ„¾»“ÐwwúîNBßÝIè»; }w'¡ïî$ôÝë l•o«Ý¥¶Ú~9ß㌭ö´Øj¯5¶Ú»ÈVûPÞ—òµ(oÿST~”¯Mù:”÷§|]Ê×£|åëS¾>åP¾!åQ¾1å›P¾)å›Q¾å›S¾å¢|KÊ·¢|kÊ·¡|ÊK)ß–òí(ßžò(ß‘ò(߉òS¾3å»P¾+å¡|7Êw§|wÊ÷ |OÊË(Hù ÊS>„ò!”¥|åÃ)ß‹ò”—Sž£”ïCùÊ÷¥|,åûQ¾?åP>Žòq”HùA”LùxÊ'P>‘ò´™K’(?„òÉ”WR^EùÊ¥|*åS)?ŒòÃ)?‚ò#)ÿ(å£ü(Ê¢|åGS^MùtÊgP>“òÊk(ŸEù1”Ϧ¼–òS~,ås(ŸCù\Êë(¯§|åÇQÞ@ù|ÊçSÞHùʧüÊO¤|!å'Q~å'S~ å§R~å§S¾ˆò3(?ƒòOP~&ågQ~6åçP~.åçQ~åçSþIÊ/ üS”šò )ÿ 埡ü"Ê/¦üÊ/¥ü2Ê/§ü ʯ üJÊ?KùU”ŽòÏSþÊ¿Hù)¿šò/Q~ å×R~åÿEù—)ÿ2å_¡ü«”_Où×(¿ò)¿‰ò›(ÿ:å7S~ åß üVÊ¿Iùm”ßFù·(ÿ6å·SþÊï üNÊS¾˜ò&Ê ”7S¾„ò¥”ßEy]b¡ünzá]Êï¡ü^Êï£ü~Ê üʤü{”Ÿò‡(ÿåSþCÊHù(„òSþ(å?¡<ÝÇ!¡û8$t‡äSÊÓ}ºCB÷qHè> ÝÇ!¡û8$t‡„îãÐ}ºCB÷qHè>Ö!Ø*7ê@Ýé<ÁƒÎ3<è<ÓÎ3¼è<ÛÎ3|(ïKùZ”÷£¼åkS¾åý)_—òõ(@ùú”¯Où”oHùF”oLù&”oJùf”oFùæ”oAù‡(ß’ò­(ßšòm(߆òRÊ·¥|;Ê·§|Êw¤|'Êw¢üÔïLù.”ïJùG(ßòÝ)ßò=(ß“ò2ÊR>ˆòÁ”¡|åC)FùpÊ÷¢|åå”ç(ÏQ^AùHÊGQ¾7åyÊGS¾åûP>†ò})Kù~”ïOù”£|åR~åS>žò ”O¤|å“(?„òÉ”WR^EùÊ¥|*åS)?ŒòÃ)?‚ò#)ÿ(å£ü(Ê¢|åGS^MùtÊgP>“òÊk(ŸEù1”Ϧ¼–òS~,ås(ŸCùÜÿcï>€´ªÒE ÿ߇9Ì6 `VDÅ,ͨ˜#•$Q2(*%4ÁœsV  `ÄœsÎWï~˜kMÕÔ9g¼5œ*ù«œ5=ÓhÿËÝ»Ùk­—?‡ïÀŸËŸÇŸÏwä;òøÎ|¾+ßïÎwç{ð=ù^|oþB¾ß—ïË÷ãûóøü ~0?„Â_Ä_Ìå/á/å/ã‡ñÃøËùáü~$?ŠÍæÇðcùqüxþJ~?‘ŸÈOâ'óåü~*?ŸÎOç¯â¯æ¯á¯å¯ã¯çoàoàoäoâoæoáoåoãoçoçïàïäïâïæïáïåïãïãïçàäâæágð3øGùÇø™üãüü“üSüSü,þiþ~6?‡–ŸËÏåçñóùüB~oMcɚƒ5%kKÖ4–¬i,YÓX²¦±dMcɚƒ5%kKÖ4–¬i,YÓX²¦ñÿîÕúãÍ‹1ëc…²b\ɧ­´´WžQŒ«”ãªüjüêüü²ÝRkòkñkóëðëòùJ|e¾2¿¿>¿¿!_…߈ߘߘ߄ߔߌߜ߂ߒ7”Êøª|5¾:¿_ƒ¯ Öâkñ[óÛðÛòÛñÛó;ð;ò;ò;ñ;óµù]ø:ü®ününüî|]~~O~/~o~~~_~?~¾_ŸoÀ7àòøÆ|þ@þ þ`þ`þþPþ0þp¾)$$ߌ?š?†?–?Ž?ž?ž??‘?‰?™?…?•??oηà[ò­øÖ|~ÙÆ¶¶|;þtþ þL¾=66ß?—??ŸïÈ/;d¸ß™ïÂwå/à»ñÝù|¾'ß‹ïÍ_È÷áûòýø~|~?Äæ‡ðññóCùKøKùËøa¼Ëfér~8?‚É_ÁâGócø1üX~?ž¿’ŸÀOä'ñ“øÉ|9?…ŸÊOã§óWñWñWó×ð×ò×ñ×ó7ð7ò7ò7ñ7ó·ð·ò·ñ·ówðwðwòwñwó÷ð÷ò÷ñ÷ó÷óðòñóð3øGùGùÇø™üãüü“üSü,~ÿ4ÿ ?›ŸÃ?ËÏåçñóøùü~!¿ˆŽž‘_Ì/á_â_æ_á_å_å_ã_ç—òoð%Ÿ>/}^Ÿ·’Ï[ÙﳊßgÙ™¢«ò«ñ«ókðkòkñkóëðëðëòùJ|e~=~}~~~C¾ ¿¿1¿ ¿)¿¿¿9¿¿%_ÆWå«ñÕùêüV| ¾&_‹ßšß†ß–ߖߎߞßߑ߉_¶Ÿ¹6_›ß…¯ÃïÊïÆïÎ×å÷à÷à÷ä÷â÷æ÷á÷å÷ã÷ç÷çëñðõù|C¾ߘoÌ7áäâæáåããç›òGðGòGñÍø£ù£ùcøcùãøãùøù“ø“ø“ùSøSùÓøæ| ¾%ß’oÅ·æÛðmùvüéüüü™|{þ,þlþ¾ïǂҹüyüù|G¾ß™ïÂwå»òðÝøî|¾'ß‹ïÍ÷æ/äûð}ù~|~?ÈâóCø‹ø‹ù¡ü%ü%ü¥üeü0þr~8?‚Éä¯àGñ£ù1üX~?žÏ_ÉOà'ò“øÉ|9?…ŸÂOå§ñÓù«ø«ùkøkùkùëøëùøù›ø›ù[ø[ø[ùÛøÛù;ø;ù»ø»ù»ù{ø{ùûøûùøù‡ø‡ø‡ùGøü£ücüLþqþqþ þIþ)~ÿ4ÿ ?›ŸÍÏáŸåçòóøùü~!¿_Ä?Ç?Ï¿À¿È/æ—ðKø—ø—ùWøWù×ø×y¬´Ôyƒ/ñÁ'__‰_vàôÊü*üªüjüêüüšü²S*Öâ׿×á×å+ò•øÊüzüzüúüü†|~#~c~Ùé1›ð›ò›ñ›ó[ð[òe|U¾*_¯ÎoÅ×àkòµø­ù­ùmømùíøíùøùøøùÚü.|~W~7~w~w¾.¿¿'¿¿7¿¿/¿/¿¿?_?€¯Ï7àò ùF|c¾  0(8ß”?‚?’?Š?ŠoÆÍÃËÇÏŸÀŸÀŸÈŸÄŸÌŸÂŸÊŸÆûŸKÍù|K¾ßÚ'¶áÛòíøvüéüü™|{þ,þl~Ùù:çðøsùóøóùŽ|'¾3ß™ïÂwå/à»ñÝù|O¾'ß‹ïÍ_È÷áûòýøþ|~?Äæ‡ðñóóCùKøKùËøaüåüp~8?‚É_ÁâGócø±üX~?ž¿’ŸÀOä'ñ“ùÉ|9?…ŸÊOã§óWñWóWó×ð×ò×ñ×ó7ð7ò7ñ7ñ7ó·ð·ò·ñ·ówðwòwòwñwó÷ð÷ò÷ñ÷óððòñóð3øGùÇøÇø™üãüü“üSü,þiþiþ~6?‡–ŸËÏãýq}i>¿€_È/âŸãŸç_à_ä_äóKø—ø—ùWøWù×ø×ø×ù¥ü|Éó„ðcé‡ ž'¬äyÄÊžG¬ìyÄ*þWåWãWç×à×ä×â×â׿×á×å+ò•øÊüzüzüúüü†|~#~c~~~S~3~s~ ~K~ÙÑhUùª|5¾:¿_ƒ¯É×â·æ·æ·á·å·ã·çwàwäwâwâwækó»ðuø]ùÝøÝùÝùºüüžü^üÞü>ü¾ü¾ü~üþ|=þ¾>߀oÈ7äñù&üüAüÁü!ü!ü¡üaüá|SþþHþ(þ(¾4 ,<"2 *ßœoηà[ò­øÖ|~Ù±€íøvüéüü™|{þ,þlþþ¾.>ß‘ïÄwæ;ó]ø®ü|7¾;߃ïÉ÷ä{ñ½ù ù>|_¾ߟïÏàòƒøÁüþ"þbþb~( )?Œ¿œÎçGð#ù+øQüh~ ?–ËãÇóWòø‰ü$~2?™/ç§ðSùiütþ*þjþjþþZþ:þzþþFþ&þ&þfþþVþ6þvþþNþNþ.þnþþ^þ>þ~þþþAþ!þaþ~ÿ(ÿÿ?“œ‚’ŠŸÅ?Í?Í?ÃÏæçðÏòsùyü|~>¿€_È/âŸãŸç_à_ä_äóKø—ø—ùWøWù×ø×ø×ù¥üü²…˜Q^Œœ÷úÇ«Bób\©^1®\VŒ«`«,-ÆUùÕøÕù5ø5ùµøeǭͯïËWä+ñ•ùõøõùõù ø ù*üFüÆü&ü¦ü¦üfüæüü–|_•¯ÆWã«ó[ñ5øš|-~k~~~[~;~{~~G~'ÞÛ[Ú™¯ÍïÂ×áwåwãwçëòuù=ø=ù½ø½ù}ø}ùýøýøýùzü|}¾ßoÄ7âóMøùƒøƒùCøCùCùÃøÃù¦üü‘üQ|3¾4 ,<""2 *ßœoÁ·à[ò­øÖ|¾-ߎ???ƒ?“oÏŸÅŸÍŸÃwà;ðçòçñçóùN|g¾ ß…ïÊ_Àwã»ó=øž|/¾ß›¿ïÃ÷åûñýùü~ ?ˆÌá/â/æ‡òCùKøKùËøaüåüp~?‚É_ÁâGócø±ü8~?ž¿’ŸÀOä'ñ“yŸV*ç§ðSýÓøéüUüÕü5ü5üµüuüõü üüMüÍüÍü-ü­ümüíüüü]ü]üÝü=ü½ü}üýüüƒüƒüCüÃü#ü þQþ1~&?“œ‚’ŠŸÅ?Í?Ã?ÃÏæçðÏòsùyü|~¿€_È/âŸãŸç_à_äó‹ù%üKüËü+ü«üküëüëüRþ ~Ùd„û„tŸPÁ}ÆJî3Vöh÷«¸ÏX•___ƒ_“_‹_›_›_‡_—¯ÈWâ+óëñëóëóðòUøøùMøMùMùÍøÍù-ø-ù2¾*_¯ÆWç·âkð5ùZüÖü6ü6ü¶üvüöüüŽüNüÎüÎ|m~¾¿+¿¿;_—¯ËïÁïÉïÅïÍïÃïËïÇïÇïÏ×ãàëó ø†|#¾ߘoÂÈÄÌÂÊÊÆÎ7åàäâ›ñÍø£ùcøcùãøãùøùù“ø“ùSøSùÓøæ| ¾ß’oÅ·æÛðmùvüéüéüü™|{þ,þlþ¾ß?—??ŸïÈwâ;ó]ø.|Wþ¾ßïÁ÷ä{ñ½øÞü…|¾/ßïÏàðùAü`~1?”Ê_Â_Ê_Æã/ç‡ó#øüHþ ~?šÃåÇñãøñü•ü~"?‰ŸÌ—óåü~*?ŸÎ_Å_Í_Ã_Ã_Ë_Ç_ÏßÀßÈßÄßÌßÌßÂßÊ߯ßÎßÁßÉßÅßÅßÍßÃßËßÇßÏ?À?È?È?Ä?Ì?ÂÏàåãgò3ùÇù'ø'ù§øYüÓü3ü3ül~ÿ,?—ŸÇÏçð ø…ü"þ9þyþþE~1¿˜_¿Ŀ̿¿ʿƿοÎ/åßàKõŠ!ÊŠqÙ¦¹´+Ì(ƕʋqå>ŸJób\•____ƒ_“_‹_›_‡_—÷·[ªÈWâ+óëñëóðòUø*üFüÆü&ü¦üfüæüüü–|_•¯ÆWç·âkð5øš|-~k~~[~;~{~{~~G~'~g¾6¿ _‡¯ÃïÊïÆïÎ×å÷à÷ä÷â÷â÷æ÷á÷å÷ã÷çëñððõù|C¾ߘoÂÈÈÄÌÂÊÆÎ7å›òGðGòGñÍø£ùcøcùcùãøãùøù“ø“ùSøSøSùÓøæ| ¾%ߊoÍ·æÛðmùvüéüü™|{¾=6ß?—??Ÿ?ŸïÈwâ;ó]ø®ü|7¾ßïÁ÷ä{ñ½ù yÿw©ß—ïÇ÷÷ øü ~0?˜Â_Ä_Ìå/á/å/ã/ã‡ñ—óÃùüHþ ~?ŠÍáÇòãøñü•ü~?‘ŸÄOæËù)üT~?ŸÎ_Å_Í_Ã_Ë_Ç_Ï_ÏßÀßÈßÄßÌßÂßÊ߯߯ßÎßÁßÉßÅßÍßÃßËßËßÇßÏ?À?È?Ä?Ì?Â?ÂÏàåãgòóOðOòOòOñ³ø§ùgøÙüþYþY~.?ŸÏ/àò‹øçøçøçùøùÅüþ%þeþeþþUþ5þu~)ÿÆþ?Ý+øg÷·~-ï~ÀòK+ú¿•þû¯þÍ¿Vú/þúýµ™›ðÍṵ̈¹Ÿ¶ð3Ö~æ(ó3GU?sTó3Gu¾:¿_ƒ¯É×â·æ·á·å·å·ã·çwàwäwâwækóµù]ø:ü®ünüî|Ýý_±ßßû2ï½7_ZŒ[Ì(Æ-Ë‹±¬O1Vm^ŒÕêcu~Ù¿Ü[ñ5øš|-~k~~[~;~;~{~~G~'~g¾6¿ ¿ _‡ß•ß߯û§~ÁfæÓææÓæ¾:[˜O[šOeæSUó©šùTߊߊ¯Á×äkñ[óÛðÛòÛñÛñÛó;ð;ò;ñ;óµù]ø]ø:ü®ünüî|]þ?þýyd\ÿ×¾–÷÷ãå=–VÜüqúWß§Wþ7ÿZåø×ï¯=\?öpýØÓõc/×½]?öqýØ×õc?ןýùýùzü|}¾ßoÄ7æóMøùƒøƒùCøCùÃøÃøÃù¦üü‘üQ|³÷ÿ¸Ø£Ì{ïã=—ã^3ŠqïòbܧO1îÛ¼÷«WŒûó>,Õãàëó ø†|# 1ß„oÂÈÄÌÂÊÆÎÎ7åàäâ›ýé~`óiOóiO_½Ì§½Í§}̧}ͧý̧ýùz|=þ¾>߀oÈ7âóMø&üüAüÁü!ü¡üaüáüá|SþþHþ(¾Ù²ûÿô÷ÿvë×òþ~¼¼ÇÒŠû?®C˾/¯úoþõï¾–õíúq´ëÇ1®Ǻ~çúq¼ëÇ ®'ºþœÄŸÄŸÌŸÂŸÊŸÆ7ç[ð-ù–|+¾5߆oË·ãOçÏàÏàÏäÛógñgóçðþ×Üü±»byÞû2ï½YZŒÇÎ(ÆãÊ‹ñø>ÅxBób<±^1žÄŸÌŸÌŸÂŸÊŸÆ7ç[ð-ùV|+¾5߆oË·ãOçÏàÏäÏäÛógñgóçðþt?p´ùtŒùtŒùt¬ùtœùt¼¯Ú æÓ‰æÓIüÉüÉü)ü©üi|s¾ß’oÅ·â[ómø¶|;þtþ þLþL¾=6ßaÙýÀúûÿ?»¿õky?^ÞciÅýÀס¿Ú+þ«Ï~ëúq®ëÇy®ç»~ttýèäúÑÙõ£‹ëOW¾+ßïÎ÷à{ò½øÞü²ŸV/äûð}ù~|~?ÈâóCø‹ø‹ù¡ÿ¸Xžß—÷è½/óÞûø¼¥ÅxþŒbìX^ŒúcçæÅØ¥^1vå/à/à»ñÝù|O¾ß›¿¿ïÃ÷åûñýùü@~?ˆÌá/â/æ‡þé~à\óé<óé<óé|ó©£ùÔÉW­³ùÔÅ|êÊ_À_Àwã»ó=øž|/¾7!!߇ïË÷ãûóøü ~?˜Â_Ä_Ì]v?°¼Ö ,së×òþ~¼¼ÇÒŠû?®Cõùÿ_}Þðûë×K\?.uý¸Ìõc˜ëÇå®Ã]?F¸þŒäGòWð£øÑü~,?ŽÏç¯ä'ðùIüd¾œŸÂOá§òÓøéüUüÕü5+îþq?pI™÷ÞÇ—.-ÆËfã°òb¼¼O1o^Œ#êãHþ þ ~?šÃåÇñãù+ù+ù üD~?™/ç§ðSù©ü4~:5ÍŸî.1Ÿ.5Ÿ.5Ÿ.3Ÿ†™O—ûª 7ŸF˜O#ù+ø+øQüh~ ?–Çç¯ä¯ä'ðùIüd¾œŸÂOå§òÓøéüUüÕü5Ëî–׺?»¿íky?^ÞciÅýÀס¿úüÿÿÃó†k]?®uý¸Îõãz×\?ntý¸Éõãfן[ø[ø[ùÛøÛù;ø;ù»ø»ù»ù{ø{ùûøûùøù‡ø‡ø‡ùGøü£ücüÌ÷ÿ¸¸¶Ì{ïãë–ãõ3Šñ†òb¼±O1ÞÔ¼o®WŒ·ð¾ä¥[ùÛøÛù;ø;ù»ø»ù{ø{ø{ùûøûùøù‡ø‡ù‡ùGøü£ücüÌ?Ý\k>]g>]g>]o>ÝàîF_µ›Ì§›Í§[ø[ù[ùÛøÛù;ø;ù»ø»ù{ø{ø{ùûøûùøù‡ø‡ù‡ùGøü£ücüÌe÷¼–ǺeîoýZÞß—÷XZq?ðÛ_àÿÓç ÿêyÃï¯Ç]?wýxÂõãI×§\?f¹~<íúñŒëÏl~6?‡–ŸËÏãçó ø…üB~ÿÿ<ÿÿ"¿˜_Â/á_â_æ_á_å_ã_ÿÇýÀUÅ¿ëè½/óÞûø‰¥ÅøäŒb|ª¼gõ)Ƨ›ã3õŠq6?‡ŸÃ?ËÏåçñóùüB~¿ˆŽž‘_Ì/á_â_â_æ_á_å_ã_ÿÓýÀã¾O˜OO˜OOšOO™O³Ì§§Í§ģÙü~ÿ,?—ŸÇÏçð ùEü"þ9þyþþE~1¿„‰‰™…•}ÙýÀúûÿ?»¿õky?^ÞciÅýÀÿ’ý†K]?–º~¼áúñ¦ëÇ[®o»~¼ãúñ®ëÏ{ü{üûüü‡üGüÇü'ü§ü§ügüçüü—üWü×ü7ü7ü·üwü÷üüüO+îþq?°´Ì{ïã7–ã›3Šñ­òb|»O1¾Ó¼ß­WŒïñïóïóðòñóŸðŸòŸñŸñŸó_ð_ò_ñ_óßðßòßòßñßó?ð?ò?ýé~`©¯ÆæÓæÓ›æÓ[æÓÛæÓ;æÓ»æÓ{üûüûüü‡üGüÇü'ü§ügügüçüü—üWü×ü7ü·ü·üwü÷üüüOËîþøgõßßòk½í×zÛ¯õŽ_ë]¿Ö{~­÷ýZðòññóŸðŸòŸñŸó_ð_ò_ò_ñ_óßðßòßñßó?ð?ð?ò?ñ?ó¿ð¿ò¿>ˆ¥ÂG>²ðQ¡ð±RácåÂÇ*üªüªüjüêüüšüZüÚü:ü:üº|E¾_™__Ÿß€ß€ß¯ÂoÄoÌoÂoÊÛ‹ö¢‡½èa/zØ‹ö¢‡½èa/zØ‹ö¢‡½èa/zØ‹ö¢‡½èa/zØ‹ö¢‡½èa/zØ‹ö¢‡½èa/zØ‹ö¢‡½èa/zØ‹ö¢‡½èQ—·÷.ì½ {ïÂÞ»°÷.ì½ {ïÂÞ»°÷.ì½ {ïÂÞ»°÷.ì½ {ïÂÞ»°÷.ì½ {ïÂÞ»°÷.ì½ {ïÂÞ»°÷.ì½ {ïÂÞ»°÷.ì½ {ï¢o¯AØkö„½a¯AØkö„½a¯AØkö„½a¯AØkö„½a¯AØkö„½a¯AØkö„½a¯AØkö„½a¯AØköDÞÚʰ¶2¬­ k+ÃÚʰ¶2¬­ k+ÃÚʰ¶2¬­ k+ÃÚʰ¶2¬­ k+ÃÚʰ¶2¬­ k+ÃÚʰ¶2¬­ k+ÃÚʰ¶2¬­ k+ÃÚʰ¶2¬­Œ¡¼µ$a-IXKÖ’„µ$a-IXKÖ’„µ$a-IXKÖ’„µ$a-IXKÖ’„µ$a-IXKÖ’„µ$a-IXKÖ’„µ$a-IXKÖ’„µ$a-IXK×ðž…ggáÙYxvž…ggáÙYxvž…ggáÙYxvž…ggáÙYxvž…ggáÙYxvž…ggáÙYxvž…ggáÙYxvž…ggáÙYÌ,|éÍŠÆÂ—Þ*|éí—Þ)|éÝ—Þ+|é}þþþCþ#þcþþSþ3þsþsþ þKþ+þkþþ[þ;þ;þ{þþGþ'þgþþWþWþ7_?ÂBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)zò½øÞ¼'ü¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:Oq/?ÿ¯ó:O¡ó:O¡ó:O¡ó:O±¬óôfY1¾U2.-Æ·gã;åÅønŸb|¯y1¾_¯?à?ä?ä?â?æ?á?å?ã?ç¿à¿à¿ä¿â¿æ¿á¿å¿ã¿ç¿çàäâæáå—=Êý­ð¡óQøÐyŠ …§X¹ðáh—Ðy §Ðy §Ðy §Ðy WBç)tžBç)tžBç)*ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:OQÆWå«ñ:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O±3¯ó:O¡ó:O¡ó:O¡ó:OQ—×y §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §ÐyŠF|c¾ ¯ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡óÇð:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó-y§hÍëò:O¡ó:O¡ó:O¡ó:Oq ¯ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:Oq7¯ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡óË:Ooº¾UÑè>áíâ>£ôNqŸQz·¸Ï(½WÜg”Þ/î3JðòòñóŸðŸòŸñŸó_ð_ð_ò_ñ_óßðßòßñßóßó?ð?ò?ñ?ó¿ð¿ò¿ñ¿¹ÏÒy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §ÐyŠøùMøMy§Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §ÐyŠù‡ø‡ùGx§Ðy §XÖyz³^1¾UVŒo—ŒK‹ñÅøny1¾×§ßo^ŒðòññóŸðŸòŸñŸó_ð_ò_ò_ñ_óßðßòßñßó?ð?ð?ò?ñ?ó¿ð¿ò¿~Ù©‰Q*|Dá#  +>V.|¬Â[&«ò«ñ«ókðkòkñkóëðëðëòùJ|e~=~}~~~C¾ ¿¿1¿ ¿)¿¿¿9¿¿%_ÆWå«ñÕùêüV| ¾&_‹ßšß†ß–ߖߎߞßߑ߉ߙ¯Í׿wáëð»ò»ñ»óuù=ø=ø=ù½ø½ù}ø}ùýøýùýùzü|}¾ßoÄ7æóMøùƒøƒùCøCùÃøÃøÃù¦üü‘üQ|3þhþhþþXþ8þxþþDþ$þ$þdþþTþ4¾9ß‚oÉ·ä[ñ­ù6|[¾:&ßž?‹?›?‡ïÀŸËŸËŸÇŸÏwä;ñù.|W¾+ßïÎ÷à{ò½øÞ|oþB¾ß—ïÇ÷çðùü ~0?„¿ˆ¿˜Ê_Â_Â_Ê_Æã/ç‡ó#ø‘üHþ ~?šÃåÇñãùñü•ü~"?‰ŸÌ—óSø)üT~?¿Š¿š¿†¿–¿–¿Ž¿ž¿¿‘¿‰¿™¿…¿…¿•¿¿¿ƒ¿“¿‹¿›¿›¿‡¿—¿¿Ÿ€ˆˆ˜„ŸÁ?Ê?ÆÏ,|éÍâóJoŸWz»ø¼Ò;Åç•ÞõyÅïSz¿ø}JðòñóŸðŸòŸòŸñŸó_ð_ò_ñ_óßðßðßòßñßó?ð?ò?ñ?ó?ó¿ð¿ò¿>J…(|dáÃöȨPøX©ð±rác~U~5~u~ ~ ~M~-~m~~]¾"_‰¯ÄWæ×ã×ç7à7ä«ðññó›ð›ò›ñ›ó[ð[ò[òe|U¾_ߊ¯Á×äkòµø­ùmømùíøíùøøùøùÚü.|~W~W~7~w¾.¿¿'¿¿7¿7¿¿/¿¿?_?€¯Ï×çð ùF|c¾  0(8ß”?‚?‚?’?ŠoÆÍÃËÇÇÏŸÀŸÈŸÄŸÌŸÂŸÊŸÊŸÆ7ç[ð-ùV|k¾ ߆oË·ãOçÏàÏäÛógñgñgóçðøsùóøóùŽ|G¾ß™ïÂwå/à»ñÝùî|¾'ß‹ïÍ_È÷áûò}ù~|~?Äæ‡ðCø‹ø‹ù¡ü%ü¥üeü0~9?œÁä¯àGñ£ùÑü~,?ŽÏ_ÉOà'òùIüd¾œŸÂOå§ñÓùéüUüÕü5üµüuüõü ü üüMüÍü-ü­ümüíüíüüü]üÝü=ü½ü}ü}üýüüƒüCüÃü#ü ~ÿ(ÿ?³ð¥7 _z«ð¥· _z§d,|é]¿à{…/½ÏÀÈÄÌÌÂÊÆÎÁÉÅÅÍÃËÇÏÿÀÿÈÿÈÿÄÿÌÿÂÿÊÿVø(>üAADá#  +>V.|¬Â¯Ê¯Æ¯Æ¯Î¯Á¯É¯Å¯Í¯Ã¯Ë¯ËWä+ñ•ùõøõù ø ù ù*üFüÆü&ü¦¼ÎY蜅ÎY蜅ÎY”ñUùj|uÞ¦ÂÐ9 ³Ð9 ³Ð9 ³Ð9 ³Ð9 ³Ð9 ³Ð9 ³¨Í뜅ÎY蜅ÎY蜅ÎYÔåu]B×%t]B×%t]B×%t]B×%t]ÜNþ>òº.¡ëº.¡ëº.¡ëº.Ñ„×u ]—Ðu ]—Ðu ]—Ðu ]—Ðu ]—Ðu ]—hÆ;Ç>œcαcyçØ‡sìÃ9öáûpŽ}8Ç>œcαçØ‡sìÃ9öáûpŽ}8Ç>Zñ­ù6|[¾ïûpŽ}8Ç>œcαçØ‡sìÃ9öÑwno8·7œÛÎí çöF'Þ¹½áÜÞpno8·7œÛÎí çö†s{ù½áÜÞpno8·7œÛÎí¾¼s{£?ïÜÞÈ;·7œÛÎí çö†s{ù½1”wNa8§0œSÎ) ç†s Ã9…1‚wNa8§0œSÎ) ç†s Ã9…áœÂpNa8§0œSÎ) ç†s Ã9…QÎOá§òÎ) ç†s Ã9…áœÂ¸†w.S8—)œËÎe ç2…s™Â¹Lá\¦p.“ÇÌ¿¼s™Â¹Lá\¦p.S8—)œËÎeŠ{xç2…s™Â¹Lá\¦p.S8—)œËÎe ç2…s™Â¹Lá\¦˜YøÒ›þ€ì-?@¼]à?ä?â?æ?æ?á?å?ã?ç¿à¿ä¿â¿â¿æ¿á¿å¿ã¿çàääâæáåó<¥äyJxžÇ¤ç1œaΰg؇3ìÃöá ûp†}8Ã>œaΰg؇3ìÃöá ûp†}8Ã>œaΰg؇3ìÃöá ûp†}8Ã>œaΰgö†3{Ù½áÌÞpfo8³7œÙÎì gö†3{Ù½áÌÞpfo8³7œÙÎì gö†3{Ù½áÌÞpfo8³7œÙÎì gö†3{Ù½áÌÞpfo8³7œÙÎì g†3 Ã…áŒÂpFa8£0œQÎ( g†3 Ã…áŒÂpFa8£0œQÎ( g†3 Ã…áŒÂpFa8£0œQÎ( g†3 Ã…áŒÂpFa8£0œQËÎ(|³O1¾Õ¼ß®WŒï”ã»%ãÒb|oF1¾_^ŒðòñóŸðŸðŸòŸñŸó_ð_ò_ñ_ó_óßðßòßñßó?ð?ò?ñ?ñ?ó¿ð¿ò¿>J…(|8¶&²ðQ¡ð±RácåÂÇ*üªüj¼LC¬Î¯Á¯É¯Å¯Í¯Ã¯ËWä+ò•øÊüzüúüü†|¾ ¿¿1¿ ¿)¿¿9¿¿¿%_ÆWå«ñÕù­ø| ¾&_‹ßšß†ß–ߎߞߞßߑ߉ߙ¯ÍïÂ×áëð»ò»ñ»óuù=ø=ù½ø½ø½ù}ø}ùýøýùzüü|}¾ßoÄ7æ›ðòòñó‡ð‡ò‡ñ‡óMù¦üü‘üQ|3þhþþXþXþ8þxþþDþ$þdþþþTþ4¾9ß‚oÉ·â[ó­ù6|[¾:&ßžoÏŸÅŸÍŸÃwàÏåÏãÏçÏç;òøÎ|¾+ßïÆwç{ð=ù^|oþB¾߇ïË÷ãûóøü ~0?˜Â_Ä_Ìå/á/å/ã/ã‡ñ—óÃùüHþ ~?ŠÍáÇòãøñü•ü~?‘ŸÄOæËù)üT~?ŸÎ_Å_Í_Ã_Ë_Ç_Ï_ÏßÀßÈßÄßÌßÂßÊ߯߯ßÎßÁßÉßÅßÍßÃßËßËßÇßÏ?À?È?Ä?Ì?Â?ÂÏàåãg¾ôfqŸPz«¸O(½í>áâ>£ô®oHï÷¥÷ŠûŒÒûÅ}FéþCþ#þcþþþSþ3þsþ þKþ+þkþkþþ[þ;þ{þþGþ'þ'þgþþWþ7÷I%÷Y6°EºÏJ÷YÜg­ä>ke÷Y«ð«ò«ñ«ó«ókðkòkñkóëðëòùŠ|%¾2¿¿>¿¿!_…¯ÂoÄoÌoÂoÊoÆoÎoÁoÁoÉ—ñUùj|u~+¾_ƒ¯É×â·æ·á·å·ã·ç·çwàwäwâwækó»ðuø:ü®ünüî|]~~O~/~/~o~~_~?~¾_ŸoÀ7äñù&üüüAüÁü!ü¡üaüá|S¾)$ߌ?š?†?–?–?Ž?ž??‘?‰?™?…?…?•?oηà[ò­øÖ|k¾ ß–oǟΟÁŸÉ·çÛógñgóçðøsùóøóùóùŽ|'¾3ß…ïÊ_Àwã»ñÝù|O¾ß›¿ïÃ÷áûòýøþü~ ?ˆÌæ‡ðñóCùKøKùËøËøaüåüp~?’¿‚ÅâGócø±ü8~<%?ŸÀOä'ñ“ùr~ ?•ŸÆOã§óWñWó×ð×ò×ñ×ó×ó7ð7ò7ñ7ó·ð·ò·ñ·ñ·ówðwòwñwó÷ð÷ò÷ò÷ñ÷óðòñóðð3øGùÇø™…/½Y2.-Æ·fãÛåÅøNŸb|·y1¾W¯ß/+ÆøøùøùOøOùÏøÏùÏù/ø/ù¯ø¯ùoøoùïøïøïùøùŸøŸù_ø_ù_ùß ¥Â‡ôÈÂG…ÂÇJ…‰¸X¹ð± ¿*¿¿:¿¿&¿¿¿6¿¿._‘¯ÄWæ×ã×ã×ç7à7ä«ðñó›ð›ð›ò›ñ›ó[ð[òe|U¾*_¯ÎoÅ×àkòµø­ù­ùmømùíøíùøùøøùÚü.|~W~7~w~w¾.¿¿'¿¿7¿¿/¿/¿¿?_?€¯Ï7àò ùF|c¾  0(8ß”?‚?’?Š?ŠoÆÍÃËÇÏŸÀŸÀŸÈŸÄŸÌŸÂŸÊŸÆ7ç›ó-ø–|+¾5߆oË·ãÛñ§ógðgòíù³ø³ùsøsøü¹üyüù|G¾ß™ïÌwá»òðÝøî|¾'ß“ïÅ÷æ/äûð}ù~|¾??€ÈâóCø‹ø‹ù‹ù¡ü%ü¥üeü0þr~8?œÁä¯àGñ£ù1üX~,?ŽÏ_ÉOà'ò“øÉüd¾œŸÂOå§ñÓù«ø«ù«ùkøkùëøëùøù›ø›ø›ù[ø[ùÛøÛù;ø;ù;ù»ø»ù{ø{ùûøûùøøù‡ø‡ùGøü£ücücøåÕ9^æþÖ¯åÝXÞciE¿àC°þUWà¿Û+þ«}Äß_?W4º9ÿ¥ø¢ôë­þNý Qò3ˆÍ ‘~©àg ~YÉÏ +ûd~U~5~u~ ~ ~M~-~m~~]¾"_‰¯ÄWæ×ã×ç7à7䫬è_Á?Þû2ï½YjÌ0ÊÍ>æ@ss ž÷¾Ì{_2.5f˜üªüjüêü¼o ±&¿¿6¿¿._‘¯ÄWæ+óëñëóðòUþÔ/øÙWãÿ>üâ߇_ýûð›ùT2ŸlN4Ÿ*˜+™+™+›«ð«ò«ñ«ókðkòkòkñkóëðëòùJ|e¾2¿¿>¿¿!_¥ðË·s¼l§ÿßöµ¼¿/ï±´â~àëÐÿ‡^ñ¿õZÖ;üý?7rýØÈõcc×M\?6uýØÌõcsן-\¶ä·äËøª|5¾:¿_ƒ¯É×äkñ[óÛðÛòÛñÛó;ð;ð;ò;ñ;óµù]ø:ÿ{îæ/¿Ñ{_æ½÷ñÆKÍæ@¹9ÐÇhnÔóÞóe|_•¯ÆWç·âkð5ùZ|-~k~~[~;~{~~G~G~'~g¾6¿ _çÿÝÄFæÓÆæÓÆæÓ&æÓ¦æÓfæÓææÓæÓ–|_ÆWå«ñÕù­ø|M¾_‹ßšß†ß–ߎߞßߑߑ߉ߙ¯ÍïÂ×Yv?ðŸþþÿÏîoýZÞß—÷XZq?ðÇuè¯öŠÿ»ý«þáï¿ê®®»º~ìæú±»ëG]×=\?ötýÙËõgo~o~~_~?~¾_Ÿ¯Ï7àòøÆ|þ@þ þ þ`þþPþ0þp¾éÿ»XŽß—÷è½/óÞûx·¥æÀ s Üèc47êyïù}ø}ø}ùýøýùzü|}¾߀oÈ7âóMøùƒøƒùƒùCøCùÃøÃù¦ºØÕ|ÚÍ|ÚÍ|ÚÝ|ªk>ía>íi>íe>íÍïÃïÃïËïÇïÏ×ãàëó ø|C¾ߘoÂÈÄÌÌÂÊÆÎ7]v?°¼:ÇËÜßúµ¼¿/ï±´â~àëÐ_íÿÕç ¿ÿ›z„ëÇ®Gº~åúÑÌõãh×c\Žuý9Ž?Ž?ž??‘?‰?™?…?•?•?oηà[ò­øÖ|¾ ß–oǟΟÁŸÉ·_q?ðû#ʼ÷>>r©90Ã(7ú˜ÍÍzÞ{þxþxþþDþ$þdþþTþ4þ4¾9ß‚oÉ·â[ómø¶|[¾:&ßþO÷G˜OGšOGšOG™OÍ̧£Í§çcͧãøãùãùøù“ø“ùSøSùÓøÓøæ| ¾%ߊoÍ·áÛòmùvüéüü™|ûe÷ËkÝÀŸÝßöµ¼¿/ï±´â~àëÐ_}þÿן7ÄY®g¹~œíúqŽëG×s]?Îsý9ßõ§#ß‘ïÄwæ»ð]ù øn|w¾;߃ïÉ÷â{óòž~G_¾/ßïÏàòƒøÁ+îþq?pV™÷ÞÇg/5f˜åæ@s ¹9PÏ{Ïwâ;ñù.|Wþ¾ßïÁ÷à{ò½øÞü…|¾/ßïÇ÷çðùAüà?Ýœe>m>m>c>u0ŸÎ5ŸÎ3ŸÎ7Ÿ:òøN|g¾ ß•¿€ïÆwç{ð=øž|/¾7!߇ïË÷ãûñýùü@~?xÙýÀÿý¨´üžü­_ËûûñòK+î~û¯?ë_¾þ§ÏþÕó†ßÿ×!®C\?.rý¸Øõc¨ëÇ%®—ºþ\æú3ŒÆ_ÎçGð#ù+øQüh~4?†ËãÇóWòø‰üD~?™/ç§ðSùiÿ¸ø£ôw½÷eÞ{_´Ô˜a”›}Ìææ@=ï=99?œÁä¯àGñ£ù1ü~,?ŽÏ_ÉOà'ò“øIüd¾œŸÂOå§ýé~`ˆùt‘ùt‘ùt±ù4Ô|ºÄ|ºÔ|ºÌ|Æ_Î_ÎçGð#ù+øQüh~ ?†ËãÇóWòø‰ü$~?™/ç§ðSùiËîþÓßÿÿÙý­_ËûûñòK+î~+ý÷ÿ^ÿ«ç ÿîó†ßÿ îú1Ýõã*׫]?®qý¸Öõã:ןë]nàoàoäoâoæoáoåoãoçoçïàïäïâïæïáïåïãïãïçàäâæYq?ðûéeÞ{_µÔ˜a”›}Ìææ@=ï=##3 +;'7/??ÿÿ ÿÿ0ÿȟOW™OW™OW›OטOךO×™O×›O7ð7ò7ò7ñ7ó·ð·ò·ñ·ówðwðwòwñwó÷ð÷ò÷ñ÷ó÷óðòñó,»X±ßp9¾–÷÷ãå=–VÜüïØo3\?f¸~<êúñ˜ëÇL×Ç]?žpýyÒõç)þ)~ÿ4ÿ ?›ŸÃ?ËÏåçòóøùü~!¿ˆŽžž‘_Ì/á_â_^q?ðûeÞ{?ºÔ˜a”›}Ìææ@=ï=?‹ŸÅ?Í?ÃÏæçðÏòsùyü<~>¿€_È/âŸãŸç_à_à_äóKø—ø—ÿt?0Ã|zÔ|zÔ|zÌ|ši>=n>=a>=i>=ÅÏâgñOóÏð³ù9ü³ü\~?ŸÏ/àò‹øçøçùøøùÅüþ%þåe÷ü³±³O°O²O±³Ø§ýÞÏø½gó³ù9ü³ü\~?Ÿ_À/äò‹øçøçùøùÅü~ ÿÿ2ÿ ÿ*ÿÿ:¿”_Ê¿Á¿É¿Å¿Í¿Ã¿Ë¿Ç¿Ç¿ÏÀÈÄÌÂÊÊÆÎÁÉÅÍÃÃËÇÏÿÀÿÈÿÄÛ{öž‡½çaïyØ{žöž§½çiïyÚ{žöž§½çiïyÚ{žöž§½çiïyÚ{žöž§½çiïyÚ{žöž§½çiïyÚ{žöž§½çiïyÚ{žöž§½çY…·×.íµK{íÒ^»´×.íµK{íÒ^»´×.íµK{íÒ^»´×.íµK{íÒ^»´×.íµK{íÒ^»´×.íµK{íÒ^»´×.íµK{íÒ^»´×.íµK{í²ooAÚ[ö¤½ioAÚ[ö¤½ioAÚ[ö¤½ioAÚ[ö¤½ioAÚ[ö¤½ioAÚ[ö¤½ioAÚ[ö¤½ioAÚ[ödSÞZÊ´–2­¥Lk)ÓZÊ´–2­¥Lk)ÓZÊ´–2­¥Lk)ÓZÊ´–2­¥Lk)ÓZÊ´–2­¥Lk)ÓZÊ´–2­¥Lk)ÓZÊ´–2­¥Lk)ÓZÊ´–2­¥Ìö¼µ#iíHZ;’ÖŽ¤µ#iíHZ;’ÖŽ¤µ#iíHZ;’ÖŽ¤µ#iíHZ;’ÖŽ¤µ#iíHZ;’ÖŽ¤µ#iíHZ;’ÖŽ¤µ#iíHZ;’ÖŽ¤µ#iíHZ;’ƒyÏÊÒ³²ô¬,=+KÏÊÒ³²ô¬,=+KÏÊÒ³²ô¬,=+KÏÊÒ³²ô¬,=+KÏÊÒ³²ô¬,=+KÏÊÒ³²ô¬,=+KÏÊÒ³²ô¬,=+KÏÊÒ³²ô¬,§ñþl0—ýÙàÌÂÇã…' O>ž*|Ì*|<]ÑÈ?ÃÏæçðÏòsùyü|~>¿€_È/âŸãŸç_à_ä_äóKø—ø—ùWøWù×ø×ø×ù¥ü¼ÎSè<…ÎSè<Å;¼ÎS¼Çë<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<¥ÎS *d˜:OYÁüÓyÊ•Í?§\•×yJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ§Ôyʪ¼ÎSVçužRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)[ó:OÙ–×yJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ+"Sç)ûñ:O9€×yÊA¼ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎS–ó:O9•×yJ§\Öyš¹´ŸQŒO”ã“}Šñ©æÅ8«^1>]VŒÏ”Œül~ÿ,?—ŸÇÏçð ø…ü"þ9þyþþE~1¿˜_¿Ŀ̿¿ʿƿοÎ/åßàužâ-^ç)ÞáužBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)¾äužBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tž²TøŒÂ§?nO§ÔyJ§ÔyJ§ÔyJ§´Ý4užRç)užRç)užRç)už²"¯ó”:O©ó”:O©ó”:O©ó”:OY…×yJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ§ÔyÊ|M¾¯ó”:O©ó”:O©ó”:O©ó”:O©ó”:O©ó”:O©ó”:O©ó”»ñ:O©ó”:O©ó”:O©ó”:O©ó”:O©ó”:O©ó”:O©ó”õy§lÈë¼ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<å`^ç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užr4¯ó”:O©ó”:O©ó”:O©ó”:O©ó”:O©ó”:O©ó”:O9Ÿ^ø˜YÜ'ÄãÅ}B<á>áÉâ>#ž*î3bVqŸO÷ñLE#?›ŸÃ?ËÏåçñóùü~!¿ˆŽž‘_Ì/æ—ð/ñ/ó¯ð¯ò¯ñ¯ó¯óKù7ø7y§x›×yŠwy§Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §,¹ÏÔyJßSç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)užRç)už²¯ó”µx§ÔyJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ§ÔyJ§<€×yʼÎS6âuž² ¯ó”:O©ó”:O©ó”:O©ó”:O©ó”:O©ó”:O©ó”:O©ó”:O©ó”:O©ó”:O©ó”:O©ó”:O©ó”:O©ó”:O©ó”-x§lÅë<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSê<¥ÎSN/|<^2.-Æ'fã“åÅøTŸbœÕ¼Ÿ®WŒÏ”ãl~6?‡–ŸËÏãçó ø…üB~ÿÿ<ÿÿ"¿˜_Â/á_â_æ_á_å_ã_ç—òKù7ø7ù·ø·ùwøwù÷ø÷ø÷ùøùøùOøOùOùÏøÏù/ø/ù¯ø¯ùoøoøoùïøïùøùŸøŸùŸù_ø_ùß Ÿ¥Âg>³ðiÛhV(|®Tø\¹ð¹ ¿*¿¿:¿¿¿&¿¿6¿¿._‘¯ÄWâ+óëñëóðòUøøøùMøMùÍøÍù-ø-ù-ù2¾*_¯ÎoÅ×àkò5ùZüÖü6ü¶üvüöüüüŽüNüÎ|m~¾¿+¿+¿¿;_—߃ߓߋߛߛ߇ߗßߟ¯ÇÀ×çëó ø†|#¾1ß„??ˆ?ˆ?˜?„?”?Œ?œoÊÁÁÉÅ7ãæáåããçOàOäOâOæOáOåOåOã›ó-ø–|+¾5߆o÷åÛñ§ógðgòíù³ø³ø³ùsøü¹üyüù|G¾#߉ïÌwá»òðÝøî|w¾ß“ïÅ÷æ/äûð}ù¾|?¾??€ÈâóCø!üEüÅüPþþRþ2~?Œ¿œÎàGòWð£øÑüh~ ?–Çç¯ä'ðù‰ü$~2_ÎOá§òÓøéüôÂÇãÅçÅÅçÅ“ÅçÅS%£Ï›Uü>ñtñûÄ3Åï³ù9ü³ü\~.?ŸÏ/àò‹øçøçùçùøùÅüþ%þeþþþUþ5þu~)ÿÿ&ÿÿÿ6ÿÿ.ÿÿ>ÿÿ!ÿ!ÿÿ1ÿ ÿ)ÿÿ9ÿÿÿ%ÿÿ5ÿ ÿ-ÿÿ=ÿ=ÿÿ#ÿÿ3ÿ ÿ+ok|üVø,>£ðéì²Bás¥ÂçÊ…O©œ\…_•___ƒ_“_‹·]%׿×á×å+ò•øÊüzüúüúüü†|~#~c~~S~S~3~s~ ~K¾Œ¯ÊWã«ñÕù­ø|M¾¿5¿ ¿ ¿-¿¿=¿¿#¿¿3¿3_›ß…¯ÃïÊïÆïÎ×åëò{ð{ò{ñ{óûðûòûñûñûóõøøú|¾!߈oÄ7æ›ðòñó‡ð‡ò‡ò‡ñ‡óMù#ø#ù£øf|3þhþþXþ8þxþþDþDþ$þdþþTþ4¾9ß‚oÁ·ä[ñ­ù6|[¾::&ßž?‹?›?‡ïÀwàÏåÏãÏç;òøÎ|¾ ß•¿€ïÆwç{ð=ù^|/¾7!߇ïË÷ãûóøü@~?˜Â_Ä_Ìå‡ò—ð—ò—ñÃøËùáü~?’¿‚ÅæÇðcùqü8~<%?ŸÈOâ'óå|9?…ŸÊOã§óW>/|ž,|£ð™…Ï …Ïb+ûïcáS×,uÍR×,uÍR×,uÍÒöÓÔ5K]³Ô5K]³Ô5K]³¬Ä뚥®Yꚥ®Yꚥ®YVáu\RÇ%u\RÇ%u\RÇ%u\RÇ%u\üqÉï#¯ã’:.©ã’:.©ã’:.©ã’µx—ÔqI—ÔqI—ÔqI—Ôqù?ìÝWUÚàáæ¼G1ƒbŽ$sÎ6’Ì9ÇF@Ì9Ç6瀆6’sC“AI bj1+ŠŠÙÕºóY_ÕÔÎ~nMÏ®R5{‹Zýfþ§o_úžóþ’ŽKÒqI:.IÇ%mÇ›[ŸÌ­OæÖ§ys듹õÉÜúdn}2·>™[ŸÌ­OæÖ's듹õÉÜúdn}2·>™[Ÿšó-ø–|+¾5on}2·>™[ŸÌ­OæÖ's듹õÉÜút0oNo2§7™Ó›ÌéMæô¦#xsz“9½ÉœÞdNo2§7™Ó›ÌéMæô&sz“9½ÉœÞdNo2§7™Ó›Úðæô¦¶¼9½©=oNo2§7™Ó›ÌéMæô&szÓ™¼¹„É\Âd.a2—0™K˜Ì%Læ¦ xs “¹„É\Âd.a2—0™K˜Ì%Læ&s “¹„É\Âd.a2—0™K˜Ì%Lüuüõ¼¹„É\Âd.a2—0™K˜náÍaJæ0%s˜’9LɦdS2‡)™Ã”ÌaJ÷òæ0%s˜’9LɦdS2‡)™Ã”ÌaJñæ0%s˜’9LɦdS2‡)™Ã”ÌaJæ0%s˜’9Lɦô$oîDZ:wb|ñ‰ZêºïjMô>aRñ>¢Ö‹ÅáZ/ï#jM.~ÐVëe~ ?…ŸÊ¿Â¿ÊOã§ó3ø™üL~ÿÿ:?›ŸÃ¿ÁÏåçòoòoñoóïðïòÕü<~?Ÿ_À¿Ï/ä?à?ä?ä?â?æ?á?å?ã?ç¿à¿àñ_ò_ñ‹ù¯ùoøoùoù%üwü÷üü¼Æg-ÏZŸµ4>ki|&ÏTËû(?hMá}”ÆgÒøLŸIã3i|&Ϥñ™4>“ÆgÒøLŸIã3i|&Ϥñ™4>“ÆgÒøLŸIã3i|&Ϥñ™4Í’¦YÒ4KšfIÓ,iš%M³¤i–4Í’¦YÒ4KšfIÓ,iš%M³¤i–4Í’¦YÒ4KšfIÓ,iš%M³¤i–4Í’¦YÒ4KšfIÓ,iš%M³¤i–4\’†KÒpI.IÃ%i¸$ —¤á’4\’†KÒpI.IÃ%i¸$ —¤á’4\’†KÒpI-x —ÔŠ×pI.IÃ%i¸$ —¤á’4\’†KÒpI.ÉÌúdf}2³>™YŸÌ¬OfÖ'3ë“™õÉÌúdf}2³>™YŸÌ¬OfÖ'3ë“™õÉÌúdf}2³>™YŸÌ¬OfÖ'3ë“™õÉÌúdf}2³>™YŸÌ¬OfÖ'3ë“™õÉŒÞdFo2£7™Ñ›ÌèMfô&3z“½ÉŒÞdFo2£7™Ñ›ÌèMfô&3z“½ÉŒÞdFo2£7™Ñ›ÌèMfô&3zSoFoºž7£7™Ñ›ÌèMfô&3z“½ÉLÂd&a2“0™I˜Ì$Lf&3 “™„ÉLÂd&a2“0™I˜Ì$Lf&3 “™„ÉLÂd&a2“0™I˜Ì$Lf&3 “™„ÉLÂd&a2“0™I˜Ì$Lf&3 “™„É ¦´tÓø²â:¡´¸N,q­.®“ªŠë‹•Åõ¥Šâ:¹¼¸¾ÌOá§òSùWøWùiüt~?“ŸÅÏâ_ã_çgósø7ø¹ü›ü›ü[üÛü;ü»|5?ŸÏÏçßãðïó ùøùøøùOøOùÏøÏù/øEü"þKþ+~1ÿ5ÿ ÿ-¿„_ÂÇÏÿÀÿÈÿÄÿÌÿÂÿÂÿZøTRøT«ð)>EáS.|’­LË>-Ë׿—ã—çWàWäWâWâWæWáëðuùUùÕøz|=~u~ ~M~-~m~~]~]~=~}~~C~#¾”¯Ï×ç7æð ùF|c¾ ¿ ¿ ¿)¿ߔߜ߂ߒߊߊߚ߆ߖߎߞßߑߑ߉ߙ߅ߕßß߃߃ߓߋ/ã›ñ{óÍù| ¾%ߊoÍïÃïËïÇïÏïÏÀÈÄÌÂÊÆÆÎÁÉÅÍÃËËÇÏŸÀŸÈŸÄŸÌ—óå|þ¾-ߎoÏŸÊwà;ð§ñ§ógðgògñgóçðçðçòçñçóðòñóó—ð—ò—ñ—óWðWòWñWñWó×ð×òüuüõü ü üüMüÍü-ü­ümüíüíüüü]üÝü=ü½|G¾#߉¿Ÿ€ˆïÌwæ»ð]ùnüÃü#ü£ücüc|%ÿ8ÿÿ$ÿÿták/žjM(žjMôœ0±øÆTkRñœQëÅâ9£ÖKÅsF­ÉÅsF­—ù)üT~*ÿ ÿ*?ŸÎÏàgò³øYüküëül~ÿ?—““‹›‡—¯æçñóùùü{üþ}~!ÿÿ!ÿÿÿ1ÿ ÿ)ÿÿ9ÿ¿ˆ_ÄÉÅ/æ¿æ¿á¿å—ðKøïøïùøùŸøŸù_ø_ø_=g–xÎ4 <%Ï™á93{Î\Æsê2žS—åkóËñËó+ð+ò+ñ+ñ+ó«ðuøºüªüj|=¾¿:¿¿&¿¿6¿¿.¿.¿¿>¿¿!¿_Ê×çëóó ø†|#¾1߄߄߄ߔߌoÊoÎoÁoÉoÅoÅoÍoÃoËoÇoÏïÀïÈïÈïÄïÌïÂïÊïÆïÎïÁïÁïÉïÅ—ñÍø½ùæ| ¾ß’oÅ·æ÷á÷å÷ã÷ç÷çàäâæáåããçàäâæáååãçOàOäOâOæËùr¾  ß–oÇ·çOå;ðøÓøÓù3ø3ù³ø³ùsøsøsùóøóù ø ù‹ø‹ù‹ùKøKùËøËù+ø+ù«ø«ø«ùkøkù þ:þzþþþFþ&þfþþVþ6þvþvþþNþ.þnþþ^¾#ß‘¿ïÄßÏ?À?È?Äwæ;ó]ø®|7þaþþQþ1þ1¾’œ‚’ŠºðµÆU×ñ•ÅuBEqX^\'•×K‹ëK%®ÕÅu2ÿ2?…ŸÊ¿Â¿ÊOã§ñÓùüL~ÿÿ:?›ŸÍÏáßàçòoòoñoóïðïðïòÕü<~>ÿ¿€ŸŸ_ÈÀÈÄÌÂÊÊÆÎÁ/â¿ä¿âó‹ù¯ùoøoù%üwü÷üüüüOüÏü/ü¯…O%…OµJ\ ŸRáS>å§e Ÿ–åkóËñËñËó+ð+ò+ñ+ó«ðuø:|]~U~5¾¿:¿¿&¿&¿¿6¿¿.¿¿>¿¿¿!¿_Ê×ç7æð†5§†|#¾1߄߄ߔߌoÊ7å7ç·à·ä·â·æ·á·å·å·ã·çwàwäwâwæwáwáwåwãwç÷à÷ä÷âËø2¾¿7ßœoÁ·ä[ñ­ùÖü>ü¾ü~üþüüüAüAüÁü!ü¡üaüáüü‘ü‘üQüÑü1ü±üqüñü ü ü‰üIüÉ|9߆?…oË·åÛñíùSùüiüéüüü™üYüÙü9ü¹üyüùüùüü…üEüÅü%ü¥üeüeüåüü•üUüÕü5üµüµ|=#33 +;'7/ß‘¿ïÄßÏßÏ?À?È?Äwæ»ð]ùn|7þaþþQþ1¾’œ‚‚’êw_“cßXþº¿jºPÓ×’’¿û%ÿ7zÅÿ³_K{‡¿?3×u-þNQëí⡽Ö;Åß)j½[ü¢VuñwŠZóŠ¿SÔšïïïñïñ ø÷ù…üü‡üGüÇüÇü'ü§ügüçüü"þKþKþ+~1ÿ5ÿ ÿ-¿ä?¦_ðûÛúìK}ö~ÿvµ5Pe TZÖ@¹5Pæ³çð ø÷ù…üü‡üGüÇü'ü'ü§ügüçüü"þKþ+þ+~1ÿ5ÿ ÿ-¿äý‚·¬§·­§·­§w¬§w­§jëižõ4ßzz_À/àßçòðòñóŸðŸðŸòŸñŸó_ð‹ø/ù¯ø¯øÅü×ü7ü·ü’Âÿû¿ÿÿw÷—þUÓßkúZò÷óÀï÷¡?Û+þWÿóÏú‡¿ÿ Éýã;÷ïÝ?~pÿøÑýã'÷ŸÝ~qÿù•ÿÕÏ(KüŒ²–ŸQÚ|ÂÏ(³ŸQ.ãgœËúç²|m~9~y~~E~%~e~e~¾_—_•_¯÷çšü~\ÓWŸ}©ÏÞ￯¶ª¬Jk Â(·Ê|ö¥>û×jk Ê¨´*¬rk ÌgÏ{ÄMµùåøåùøù•ø•ùUøUø:|]~U~5¾Þž¾³ž¿·ž¿÷õðƒ¯‡}=üäëág_¿øzøÕz,±K¬G?OÉz ë1[ËXËòµùÚürüòü üŠüJüÊü*ü*|¾.¿*¿_oéó@MuŽ—º¿ô¯šþ~\Ó×’¿Ÿ~¿ýÙ^ñ?ûÏ¿ÚCüí‰ÕÝ?VwÿXÃýcM÷µÜ?ÖvÿXÇýg]÷Ÿõøõøõù ø ùøR¾>¿1¿1߀oÈ7âóMøMøMùMùÍø¦üæüü–üV?,}H«—úìý~jk Ê¨´*¬rk ÌgϯϯÏoÀoÈoÄ—òõùù|¾!߈oÌ7á7á7å7ã7ã›ò›ó[ð[ò[ý×ó@ZÝzZÃzZÃzZÓzZËzZÛzZÇzZ×zZ_Ÿ_Ÿß€ß߈/åëóó ø|C¾ߘoÂoÂoÊoÆoÆ7å7ç·à·ä·Zúü³ïÓÿêûÿ?û¾á·ÿ%ntÿ¸Ñýã&÷›Ý?nqÿ¸Õýã6÷ŸÛÝîàïàïäïâïæïáïå;ò÷ñ÷ñøûùøù‡øÎ|¾ ß•ïÆ?Ì?Â?Ê?ö÷óÀ?žn,õÙûýMÕÖ@•5Pi TXåÖ@™Ïž¿“¿“¿‹¿›¿‡¿—ïÈßÇwâ;ñ÷óðòñù.|W¾+ߘ„”ìÏ7ZO7YO7YO7[O·XO·ZO·YO·[Owðwòwòwñwó÷ð÷òùûøN|'þ~þþAþ!¾3ß…ïÊwå»ñóðò-}øû¼a þªéïÇ5}-©¹ç€ÿ¤çš?o˜*Ý?*Ý?wÿxÂýãI÷§Ü?žvÿyÆý§;ßïÁ÷ä{ñ½ù>|_¾ßïÏàòƒøÁü~(?”ÆçGð#ùQüèÿ˜çßÿ†VSWŸ}©ÏÞﯶª¬Jk Â(·Ê|ö|¾ß“ïÅ÷æûð}ù~|¾??€ÈâóCø¡ü0~?œÁäGñ£ÿðõæûð}ù~|~?€ÈâóCø¡ü0~8?œÁäGñ£ùgùçøçùçùø*~ ?–Çë<%§4×yJ“x§¤ó”tž’ÎSÒyJ:OIç)é<%§¤ó”tž’ÎSÒyJ:OIç)é<%§¤ó”tž’ÎSÒyJ:OIç)é<%§¤ó”tž’ÎSÒyJ:OIç)é<%§¤ó”tž’ÎSÒyJ:OIç)é<%§¤ó”tž’ÎSÒyJ:OIç)é<%§¤ó”tž’ÎSÒyJ:OIç)é<%§¤ó”tž’ÎSÒyJ:OIç)é<…ÎSÔòõ§óáëOç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)JùúüÆ|¾!߈oÌ7áužBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tž¢¯ó­x§Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §¨àužÂ ’Ðy §Ðy §XÚyzº²¸>SQ\»—×eŵgiqíUâZ]\{W×>|_¾ߟÀäòƒøÁü~(?ŒÎàGð#ùQühþYþ9þyþþ¾ŠÃåÇñ:Oi¯ó”tž’ÎSÒyJ:OIç)é<%§¤ó”tž’ÎSÒyJ:OIç)Íàuž’ÎSÒyJ:OIç)é<%§¤ó”tž’ÎSÒyJ:OIç)é<%§TÍÏãçó:OIç)é<%§¤ó”tž’ÎSÒyJ:OIç)é<%§¤ó”tž’ÎSZÄë<%§¤ó”tž’ÎSÒyJ:OIç)-áuž’ÎSÒyJ:OIç)é<%§¤ó”tžBç)tžB&tžBç)ŒßŠe Ëòµy§Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Xƒ×y §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ø˜×yІ¼ÎS4æužBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)¶ãužBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)ÊøfüÞ|s¾¯ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:Oq(¯ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:O¡ó:OQÎë<…ÎSè<…ÎSè<…ÎSè<…ÎStàužBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç).áužBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)nàoäoâo.|zºxNHÏÏ ©»ç„ÅsFêY|_¾ߟÀäòƒøÁü~(?ŒÎàGð#ùQühþYþ9þyþþ¾ŠÃåÇñ:OIç)é<%§¤ó”tž’ÎSÒyJ:OIç)é<%§¤ó”tž’ÎSÒyJ:OIç)é<%§¤ó”tž’ÎSÒyJ:OIç)é<%§¤ó”tž’ÎSÒyJÕ¼ÎSšÏë<%§¤ó”tž’ÎSÒyJ:OIç)é<%§¤ó”tž’ÎSÒyJ:OIç)é<%§¤ó”tž’ÎSÒyJ:OIç)é<%§¤ó”tž’ÎSÒyJ:OIç)é<%§Ðy §°Á:tžBç)tžbÏé:OQ›×y §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §ÐyŠf¼ÎS4çužBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tžBç)tž¢œoß·åÛñíùSù¼ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSÜÈë<ÅÍ…OOW×g*‹k÷ŠâÚ£¼¸ö,+®½J‹kï×êâÚ‡ïË÷ãûóøü ~?˜Âå‡ñÃùüH~$?ŠÍ?Ë?Ç?Ï¿ÀWñUü~,?ŽÏOà'ò“øIü‹üKüdþe~ ?•……•ŸÆOçgð3ùYüküküëül~ÿ?—“‹‹›‡—¯æçñóù÷ø÷øüûüBþþCþ#þcþcþþSþ3þsþ ~ÿ%ÿ%ÿ¿˜ÿšÿ†ÿ–_ÂÇÇÏÿÀÿÈÿÄÿÌÿÂ~-|”>j>Rá# ¹ð±Lá£ØÒÿÛ•¯Í/Ç/ϯÀ¯È¯ÄÛŽ+ó«ðuøºüªüj|=~u~u~ ~M~-~m~~]~=~=~}~~C~#¾”¯ÏoÌoÌ7àòøÆ|~~S~S~3¾)¿9¿¿%¿¿5¿5¿ ¿-¿¿=¿¿#¿¿¿3¿ ¿+¿¿;¿¿'¿'¿_Æ7ã÷æ›ó-ø–|K¾ߚ߇ߗßߟ?€?€??ˆ?˜?„?”?Œ?œ?œ?‚?’?Š?š?†?–?Ž?Ž?ž??‘?‰?™/çÛðmøSø¶|;¾=*ß????ƒ?“?‹?›?‡?—?—??Ÿ¿€¿¿ˆ¿˜¿„¿„¿”¿Œ¿œ¿‚¿’¿Š¿š¿š¿†¿–¯à¯ã¯çoàoäoäoâo.|z¦øs©{‰kñçRâÏ¥žþ\¯âß“zÿžÔ§ø÷¤¾|?¾ߟÀäñƒù!üP~(?ŒÎàGò£øÑü³ü³üsüóü |?†ËãÇñãù üD~ÿ"ÿ?™ŸÌ¿ÌOá§ò¯ð¯òÓøéüt~?“ŸÅ¿Æ¿ÎÏæçðsø7ø¹ü›ü[üÛü;ü»ü»|5?ŸÏ¿Ç/àßçò ùøùøùOøOùÏøÏøÏù/øEü—üWübþkþkþþ[~ ÿÿ=ÿÿ#ÿ#ÿÿ3ÿ ÿkᣤðQ«ðá¸B¤ÂG>rác™ÂDz|m~9ÞøîXž__‘_‰_™_…¯Ã×åëò«ò«ñõøÕù5ø5ùµøµøµùuøuùõøõù ø ù ùøR¾>¿1߀oÈ7âñù&ü&ü¦üf|S~s~s~ ~K~+~k~~[~;~;~{~~G~'~g~~W~W~7~w~~O~/¾ŒoÆ7ã÷æ›ó-ø–|+¾5¿¿¿/¿¿? 00(8$4 ,<""2_ηáOáÛòíøv|{þT¾:&&6.>!1 )99%5 -_ÁWð×ñ×ó7ð7ò7ñ7ó·>=]øôLáS÷§…O= Ÿz>õ.|êÃ÷áûòýøþü~ ?ˆÌæ‡ðCùaüp~?’ÅâGóÏòÏñÏó/ðUü~ ?–Çç'ðùIü‹ü‹üKüdþe~ ?•…••ŸÆOçgð3ùYüküëüëül~ÿ?—“×1K:fIÇ,é˜%³TÍÏãçóïñ x³¤c–tÌ’ŽYÒ1K:fIÇ,é˜%³¤c–tÌ’ŽYÒ1K:féK^Ç,é˜%³¤c–tÌ’ŽYZÂë¶$Ý–¤Û’t[’nKÒmIº-I·%é¶,®º-¡Ûº-á 4t[B·%t[ÂQÁ¨Íë¶„nKè¶„nKè¶„nKè¶„nKè¶„nKè¶„nKÔãÍ©sêÜúX“7§>Ì©sêÜú0§>Ì©sêÜú0§>Ì©sêÜú0§>Ì©|C¾ߘo›SæÔ‡9õaN}˜SæÔ‡9õaN}lÅ›Ëæò†¹¼a.o˜ËÛóæò†¹¼a.o˜Ëæò†¹¼a.o˜Ëæò†¹¼a.o˜Ëæò†¹¼ÑŒ7—7šóæòFKÞ\Þ0—7Ìå syÃ\Þ0—7öçÍ! sÃÂ0‡0Ì! sÃÂ8Œ7‡0Ì! sÃÂ0‡0Ì! sÃÂ0‡0Ì! sÃÂ0‡0Ì! s£œoߛCæ†9„aa˜Cxs—ÂÜ¥0w)Ì] s—ÂÜ¥0w)Ì] s—úíÊ›»æ.…¹KaîR˜»æ.…¹Kq)oîR˜»æ.…¹KaîR˜»æ.…¹KaîR˜»æ.…¹KqoÎD˜3æLÄÍ…OOïÒ3Åû„Ô½xŸzxŸÐ³ø/žzï#Rïân©O]W¾/ßïÏàòƒøÁü`~?”ÆçGð#ùQü(~4ÿ,ÿÿ<ÿ_ÅáÇðcùqüx~?‘ŸÄ¿È¿È¿ÄOæ_æ§ðSùWøWùWùiüt~?“ŸÅ¿Æ¿Î¿ÎÏæçðoðsù7yMϤé™4=“¦gÒôLšži¯é™Þã5=“¦gÒôLšžIÓ3iz&MϤé™4=“¦gÒôLšžIÓ3iz&MϤé™4=“¦gÒôLšžIÓ3iz&MϤa–4Ì’†YÒ0KfIÃ,i˜% ³¤afQRø¨UøH…(|äÂÇ2…eùÚ¼†Yh˜…†Yh˜…†Yh˜…†Yh˜…†Yh˜…†Yh˜…†Yh˜…fKh¶„fKh¶„fKh¶„fKh¶„fKh¶„fKh¶„fKh¶„fKh¶„fKh¶„fKh¶„fKh¶„fKh¶„fKh¶„fKh¶„fKh¶„fKh¶„õaF}˜QfÔ‡õaF}˜QfÔ‡õaF}˜QfÔ‡õaF}˜QfÔ‡õaF}˜QfÔ‡õaF}˜QfÔ‡õaF}˜QfÔ‡õaF}˜QfÔ‡™¼a&o˜Éfò†™¼a&o˜Éfò†™¼a&o˜Éfò†™¼a&o˜Éfò†™¼a&o˜Éfò†™¼a&o˜Éfò†™¼a&o˜Éfò†™¼a&o˜Éfò†„aa˜Af†„aa˜Af†„aa˜Af†„aa˜Af†„aa˜Af†„aa˜Af†„aa˜Af†„aa˜A&M„™KaæR˜¹Kg.=SâZ]\»WוŵgEqíU^\{—×>¥Åµ/ß—ïÇ÷çðùAü`~?„Êã‡ó#ø‘ü(~4?š–Žž¯âÇðcù±ü8~rác™ÂDz|m~9~9~y~~E~%~e~¾_‡¯Ë¯Ê¯Æ×ãWç×à×ä×ä×â׿×á×å×ã×ç7à7à7ä7âKùúüÆ|ÞpªhÈ7âóMøMøMùÍø¦|S~s~ ~K~+~k~~[~[~;~{~~G~'~g~~~W~7~w~~O~/¾Œ/ã›ñ{óÍù|K¾ßšoÍïÃïËïÇïÏÀÈÄÄÌÂÊÆÎÁÉÉÅÍÃËÇÏŸÀŸÀŸÈŸÄŸÌ—ómøSø¶|[¾ßž?•ïÀŸÆŸÎŸÁŸÁŸÉŸÅŸÍŸÃŸËŸÇŸÏŸÏ_À_È_Ä_Ì_Â_Ê_Æ_Æ_Î_Á_É_Å_Í_Ã_Ë_ËWð×ñ×ó7ð7ò7ñ7ó7>=S×µxNHÝ='ô(ž3RÏâ9#õ*ž3Rïâ9#õ)ž3R_¾/ßïÏàòƒøÁü~?”ÆçGð#ùQüh~4ÿ,ÿÿ<ÿ_ÅáÇòcùqüx~?‘ŸÄ¿È¿Ä¿ÄOæ_æ§ðSùWøWùiü4~:?ƒŸÉÏâ_ã_çgó³ù9üü\þMþ-þmþþþ]¾šŸÇÏçßãðïóïó ùøùøùOøOùOùÏøÏù/øEü—üWüb~1ÿ5ÿ ÿ-¿„ÿŽÿžÿÿÿ‘ÿ‰ÿ™ÿ…ÿÕsv‰çìZž³kyÎö€á9={N_Æsú²|m~9~9~y~~E~%~e~¾_‡¯Ë¯Ê¯Æ×ãWç×à×ä×ä×â׿×á×å×ã×ç7à7à7ä7âKùúüÆ|¾!ßoÄ7æ›ð›ð›ò›ñMù¦üæüü–üVüÖü6ü¶ü¶üvüöüüŽüNüÎü.ü.ü®ünüîüüžü^|_Æ7ã÷æ›ó-ø–|+¾5ߚ߇ߗßߟ?€??ˆ?ˆ?˜?„?”?Œ?œ?‚?’?’?Š?š?†?–?Ž?ž???‘?‰?™/çÛð§ðmù¶|;¾=*ß???ƒ?ƒ?“?‹?›?‡?—??Ÿ?Ÿ¿€¿¿ˆ¿˜¿„¿”¿Œ¿Œ¿œ¿‚¿’¿Š¿š¿†¿–¿–¯à¯ã¯çoàoäoâoæo.|zº¢¸>S^\»—ץŵg‰kuqíUU\{W×>|_¾ߟÀàòƒøÁü~(?ŒÎçGð#ùQühþYþ9þyþyþ¾ŠÃåÇñãù ü~"?‰‘‰ŸÌ¿ÌOá§ðSùWøWùiüt~?“ŸÉÏâ_ã_çgósø7ø¹ü\þMþ-þmþþ]¾šŸÇÏãçóïñ ø÷ù…üü‡ü‡üGüÇü'ü§ügüçüüü"þKþ+~1ÿ5ÿ ÿ-ÿ-¿„ÿŽÿžÿÿ‘ÿ‰ÿ™ÿ™ÿ…ÿµðQRø¨UøH…(|É ß®…e Ëòµùåøåùx™X‘_‰_™_…¯Ã×åWåWãWãëñ«ókðkòkñkóëðëðëòëñëóðòñ¥|)_Ÿß˜oÀ7äñù&|~~S~3¾)¿9¿¿%¿%¿¿5¿ ¿-¿¿=¿¿¿#¿¿3¿ ¿+¿¿;¿;¿¿'¿_Æ7ã÷æ›óÍù|K¾ߚ߇ߗßßߟ?€??ˆ?˜?„?”?”?Œ?œ?‚?’?Š?š?†?†?–?Ž?ž??‘?‰?™?™/çÛð§ðmùv|{þTþT¾:&66.>!1 )9%%5 -_Á_Ç_Ï_ÏßÀßÈßÄßü»ÿw÷ þ»ûKÿªé~@M_KJþî”üù^ñ¿úŸÖ?üýº®«¿#J µ ©®ká# ¹ð±LácY¾6¿¿<¿<¿¿"¿¿2¿ _çïç<|]ê³÷ûoª­*k Ò¨°Ê­2Ÿ=ÿÿÿ3ÿ ÿk…5Pn ”ùìK}ö%®ÕÖ@•5Pi ðµùåøåùøøù•ø•ùUø:xøÚzþÆzþÆzþÖz^b=g=ïëá_?ò?ñ?ñ?ó¿ð¿Z%Öc-ë1Yφ‰DXÏÙz^Æz^–¯Í/Ç/ϯÀ¯À¯È¯Ä¯Ì¯Â×Yº ~ÿUã¥î/ý«¦¿×ôµäïç_ÿÿ?}ÏðÏÞ7üö•X×ý£®ûǪÔsÿXÝýc ÷Ÿ5ÝÖâ×â׿×á×å×ã×ç7à7ä7ä7âKùúüÆ|¾!߈oÄ7æ›ð›ð›ò›ñMÿëy`‘ïÁ«Ï¾Ôgï÷«V[UÖ@¥5Pa ”[e>{~m~m~~]~=~}~~C~#~#¾”¯ÏoÌ7àòøÆ|c¾ ¿ ¿)¿ßô¿ž¢®õ´ªõ´ªõ´šõTÏzZÝzZÃzZÓzZ‹_›_›_‡_—__Ÿß€ß߈߈/åëóó ø†|#¾1ߘoÂoÂoÊoÆ7]ú<ðïþþÿßÝ_úWM?®ékÉßÏ¿–üëÿ·þïÞ+üOß7üö§6wÿØÜýc ÷-Ý?¶rÿØÚýc÷Ÿmݶã·ã·çwàwäwâwæwáwåwåwãwç÷à÷ä÷âËøf|3~o¾9ß‚oÉ·â[ÿý<ðçÍK}ö~¿Eµ5Pe TZÖ@¹5Pæ³ç·ç·çwàwäwâwæwáwåwãwãwç÷à÷ä÷âËøfüÞüÞ|s¾ß’oÅ·þÃóÀæÖÓÖÓÖÓ–ÖÓVÖÓÖÖÓ6ÖÓ¶ÖÓvüöüöüüŽüNüÎü.ü®ününüîüüžü^|ߌߛߛoηà[ò­øÖKŸjjßÀR÷—þUÓßkúZò÷óÀï÷¡ö}ú_}ÿÿgß7üö•ºûÇ>îûºìçþ±¿ûÇîºÿäþs00(8$4 ,<""2_ηáOáÛþý<ðç}J}ö~¿oµ5Pe TZÖ@¹5Pæ³ç}k‰CøCùÃøÃù#ø#ù£ø£ù£ùcøcùãøãùøù“ø“ø“ùr¾  ßöÏûXOûZOûZOûYOû[OXOZOYOó‡ð‡ð‡ò‡ñ‡óGðGòGñGóGóÇðÇòÇñÇó'ð'ò'ñ'ñ'óå|þ¾íÒçšÜ7`Ë_÷WM?®ékÉßϿ߇þüûÿÿÙ¯¥ï~ûÊmçþÑÎý£½ûÇ©îÜ?Nsÿ8Ýýç ÷Ÿ3ù3ù³ø³ùsøsùóøóù ø ø ù‹ø‹ùKøKùËøËùËù+ø+ù«ø«ùkøkÿcžn¼·æ®>ûRŸ½ß·¯¶ª¬Jk Â(·Ê|öüYüYüÙü9ü¹üyüùüü…ü…üEüÅü%ü¥üeüåüüü•üUüÕü5üµxhg=µ·žÚ[O§ZO¬§Ó¬§Ó­§3¬§3ù³ø³ø³ùsøsùóøóù ø ù ù‹ø‹ùKøKùËøËù+ø+ø+ù«ø«ùkøk—>ü»¿ÿÿw÷—þUÓßkúZò÷óÀÆyèpÿ¨pÿ¸Îýãz÷Ü?ntÿ¸Éýçf÷Ÿ[ø[ø[ùÛøÛù;ø;ù»ø»ù»ù{ø{ùŽü}|'þ~þþþAþ!¾3ß…ïÊwûÇó@M~?®é«Ï¾Ôgï÷×U[UÖ@¥5Pa ”[e>{þVþVþ6þvþþNþ.þnþþþ^¾#߉¿Ÿ€ˆïÌwá»òÝþðŠ×yÊ:OYç)ë|_¾ßïÏàòƒøÁü~(?”ÆçGð#ùQühþYþYþ9þyþ¾ŠÃåÇñãøñü~"?‰‘‰ŸÌOæ_æ§ðSùWøWùiüt~:?ƒŸÉÏâ_ã_çgósø9üü\þMþ-þmþþ]þ]¾šŸÇÏçßãðïó ù…üü‡üGüÇü'ü§ügügüçüü"þKþ+~1ÿ5ÿ5ÿ ÿ-¿„ÿŽÿžÿÿ‘ÿ‘ÿ‰ÿ™ÿ…ÿµð¹¤ð¹Vá³m›Ù Û…Ϲðy™ÂçeùÚür¼±æyy~~E~%~e~¾_—¯Ë¯Ê¯Æ×ãWç×à×ä×â×â׿×á×å×ã×ç7à7ä7ä7âKùúüÆ|¾!߈oÄ7æ›ð›ð›ò›ñMùÍùÍù-ø-ù­ø­ùmømùíøíøíùøùøù]ø]ù]ùÝøÝù=ø=ù½ø2¾ߌߛoηà[ò­øÖü>ü>ü¾ü~üþüüüAüÁüÁü!ü¡üaüáüü‘üQüQüÑü1ü±üqüñü ü‰ü‰üIüÉ|9߆?…oË·ãÛñíùSùüi…[Š?·.n+þ\Ü^ü¹¸ÃŸ»³ø÷Ä]Å¿'î.qåïáïå;ò÷ñøûùøøù‡øÎ|¾+ߘ˜„”Œ¯äçŸàŸäŸäŸâŸæŸá»ó=øž|/¾ß›ïÃ÷åûñýùü@~ ?ˆÌá‡òÃøáü~?’ÅæŸåŸãŸç_à_à«ø1üX~?žŸÀOä'ò“øù—øÉüËü~*?•…•ŸÆOçgð3ùYü,þ5þu~6?‡ƒŸË¿É¿É¿Å¿Í¿Ã¿ËWóóøùü|þ=~ÿ>¿ÿ€ÿÿˆÿˆÿ˜ÿ„ÿ”ÿŒÿœÿ‚_Ä/â¿ä¿âó_óßðßòKø%üwü÷üüüOüÏü/ü/ü¯…Ï%…ϵ ŸSás>çÂgÇ‹ò2…ÏËòµùåøåùøù•ø•ø•ùUø:|]~U~5¾___ƒ_“_‹_›_‡_—_—__Ÿß€ß߈/åëóõùù|C¾ߘoÂoÂoÂoÊoÆ7å7ç·à·ä·â·â·æ·á·å·ã·çwàwäwäwâwæwáwåwãwç÷à÷à÷ä÷âËøfüÞ|s¾ß‚oÉ·â[óûðûòûñûóûóðòñó‡ð‡ò‡ñ‡ñ‡óGðGòGñGóÇðÇòÇòÇñÇó'ð'ò'ñ'óå|9߆?…oË·ãÛó§òøüi…[ ·>n+|Ü^ø¸£ðqg‰káã.þnþþ^¾#߉ïÄßÏ?À?È?Äwæ»ð]ù®|7þaþþQþ1¾’œœ‚’Šš†ïÎ÷à{ð=ù^|o¾ß—ïÇ÷çûóøü ~0?„Êã‡ñÃùüH~?š×- ݲÐ- ݲÐ-‹*~ ?–ÇçuËB·,tËB·,tËB·,tËB·,tËB·,tËB·,tËB·,¦óºe¡[ºe¡[ºe¡[³y–Ði –Ði –Ði –Ði –¨æuZB§%tZB§%tZB§%tZB§%>àuZB§%tZB§%tZB§%tZB§%tZB§%tZB§%óæÒ‡¹ôa.}|Ë›KæÒ‡¹ôa.}˜KæÒ‡¹ôa.}˜KæÒgsé³¹ôÙ\úl/Ë×æÍ¥ÏæÒgsé³¹ôÙ\úl.}6—>›KŸëðæðfsx³9¼ÙÞlo^7‡7›Ã›ÍáÍæðfsx³9¼ÙÞlo6‡7›Ã›ÍáÍæðfsx³9¼¹>oonÀ›Ã›ñæðfsx³9¼ÙÞlo6‡77åÍÌæfs³¹ƒÙÜÁlî`6w0oË›;˜ÍÌæfs³¹ƒÙÜÁlî`6w0›;˜ÍÌæfs³¹ƒÙÜÁlî`.ã›ñ{óæfs³¹ƒÙÜÁlî`nÍ›³”ÍYÊæ,es–²9KÙœ¥lÎR6g)›³”áÍYÊæ,es–²9KÙœ¥lÎR6g)›³”æÍYÊæ,es–²9KÙœ¥lÎR6g)›³”ÍYÊæ,es–²9K¹-o®D6W"›+‘OåÍ•ÈKçJÜR¼Oˆ[‹÷ q[ñ>!n÷>áŽâ}DÜY×µø‹IÜU¼ˆ»ù{ø{ùŽü}|'¾?ÿÿ ÿß™ïÂwå»òÝø‡ùGøGùÇøJþqþqþ þIþ)þiþ¾;߃ïÁ÷ä{ñ½ù>|_¾ߟïÏàòƒøÁü~(?ŒÆçGð#ùQüh^Ã34›IŸÍ¤ÏfÒg3é³™ôÙLúl&}6“>›IŸÍ¤ÏfÒg3é³™ôÙLúl&}6“>›IŸÍ¤ÏfÒçú¼™ôÙLúl&}nÄ›IŸÍ¤ÏfÒg3é³™ôÙLúl&}6ƒ7›Á›ÍàÍfðf3x³¼Ù Þlo6ƒ7›Á›ÍàÍfðf3x³¼Ù Þlo6ƒ7›Á›ÍàÍfðf3x³¼Ù Þlo6ƒ7›Á›ÍàÍfðf3x³¼Ù Þlo6s0›9˜ÍÌff3³™ƒÙÌÁlæ`6s0›9˜ÍÌff3³™ƒÙÌÁlæ`6s0›9˜ÍÌff3³™ƒÙÌÁlæ`6s0›9˜ÍÌff3³™ƒÙÌÁlæ`6c)›±”ÍXÊf,e3–òÒK·T×[+‹ëmÅõöòâzGYq½³´¸ÞUâZ]\ïæïáïå;ò÷ñøûùûùøù‡øÎ|¾+ßïÆ?Ì?Â?Ê?ÆWòóOðOðOòOñOóÏðÝù|O¾'ß‹ïÍ÷áûòýøþü~?Äæ‡ðCùaüp~8?‚ÉâGóÏòÏñÏóÏó/ðUü~,?ŽÏOà'ðùIü‹üKüdþe~ ?…ŸÊ¿Â¿ÊOã§ó3ø™üL~ÿÿ:?›ŸÃ¿ÁÏåçòoòoñoóïðïòÕü<~?Ÿ_À¿Ï/ä?à?ä?ä?â?æ?á?å?ã?ç¿à¿àñ_ò_ñ‹ù¯ùoøoùoù%üwü÷üüüOüÏüÏü/ü¯…Ï%…ϵ ŸSás>ç×Âçe Ÿ—åkóËñËó+ðÆœçù•ø•ùUø:|]~U~5~5¾¿:¿¿&¿¿6¿¿¿.¿¿>¿¿!¿_Ê—òõùù|C¾ߘoÂ7á7á7å7ã›ò›ó[ð[ò[ò[ñ[óÛðÛòÛñÛó;ð;ð;ò;ñ;ó»ð»ò»ñ»ó»ó{ð{ò{ñe|3~o¾9ßœoÁ·ä[ñ­ù}ø}ùýøýøýùøùƒøƒùCøCùCùÃøÃù#ø#ù£ø£ùcøcøcùãøãùøù“ø“ù“ùr¾  ß–oÇ·çOåOå;ð§>n)žâÖâUÜæ9áöâ9#î(ž3âÎâ9#îªëZ¾??ÿÿ ÿß™ïÂwå»ñÝø‡ùGøGùÇøJþqþ þ þIþ)þiþ¾;߃ïÉ÷ä{ñ½ù>|_¾ߟÀàòƒøÁü~(?ŒÎçGð#ùQühþYþ9þyþyþ¾ŠÃåÇñãù ü~"?‰‘‰ŸÌ¿ÌOá§ðSùWøWùiüt~?“ŸÉÏâ_ã_çgósø7ø¹ü\þMþ-þmþþ]¾šŸÇÏãçÿ/öîJ‹2]ØuïS˜³˜µsÎsQ0‚¡UPÉ9+4‰"f›Œ¢bŽM6ç[Áˆ9gþþ¦.f{öÚûìùdzv{–ôZL «¹€™ï¥ºú«·ž›_Â/åßç?à?ä?â?â?æ?á—ñŸòŸñŸó_ð_ð_ò_ñ_óßðßòßñßóßó?ð?ò?ñ?ó¿ð¿ò¿ñ¿ñ¿óËsŸŠrŸªå>UÏ}ª‘ûdƒ_J¹O‘û”ñ5ùUøUùÕøÕùÕù5ø5ùµøµùuøuùõøõøõù ø ùZüFüÆü&ü&ü¦üfüæüü–üV|1_ÌoÍ׿ëðÛðÛòuùz|=~;~{~~G~'~g~~~W~7~w~~O~/~o~o~~_~?~þþ@þ þ þ`þþP¾>8$4 ,<<߀oÈŸÈ7âOâOæOæóMøSøSùÓøÓù3ø3ø3ù¦|3þ,þlþþ\þ\¾„??Ÿ¿€oηà/ä/ä/â/öïHýü8´8?^VäX‘‡•çÇËËòãðÒüxEI~ÁäGñ£øÑü~,%?Ž¿ŠÏç¯æ¯á¯å¯ã¯çoàoäoäoâËø üD~?™ŸÂOá§òÓøéüÍü þ~&?“ŸÅßÊÏæoãoçïàçðsø;ù»ø»ù{ø{ùûøûùûùøù‡ø‡ùGør~.?—ŸÇÏçð ùEübþQþQþ1þqþ þIþ)þiþþþYþ9þyþþEþ%þeþeþþUþ5þuþ þMþ-þ-þmþ¾‚—_Â/å—òïóðòñóŸðËøeü§ügüçüü—üWü×ü×ü7ü·üwü÷üüüOüOüÏü/ü¯üoüïüòÜûö¿ò˜ûT-÷©zîSÜ'¢Rä>e¼$CªÉ¯Â¯Ê¯Æ¯Î¯Á¯É¯Å¯Å¯Í¯Ã¯Ë¯Ç¯ÏoÀoÈoÈ×â7â7æ7á7å7ã7ç7ç·à·ä·â‹ù­ùÚ|¾¿ ¿-_—¯ÇoÇoÏïÀïÀïÈïÄïÌïÂïÊïÆïÎïÎïÁïÉïÅïÍïÃïËïÇïÇïÏÀÈÄÌÂÊÊ×çãçàäâææáåãçOàð ù†ü‰|#þ$þd¾1ß„?…?…?•???ƒ?“oÊ7ã›ñgñgóçðçò%üyüùüùü|s¾!qÁWUçx…û[Tu? ªE+û…¹é¶WüßýøW{ˆ…kêuóïjÜÏPãFß3Ü”_ä×(Ë¿ç¨1!ÿž£ÆÄü{Ž“øIüd~ ?•ŸÆOçoægð3ø[ø™ü,þV~6;;?‡¿“¿‹¿›¿ge¿À·o•¯}±×ÞÏo¨°Ê­2k Ô(±ê{íùÉüd~ ?•ŸÆOçoægð·ð·ð3ùYü­ülþ6þvþþ~'7Ïú×[O7XO7XO7ZO7YOeÖÓëi¢õ4‰ŸÌOæ§ðSùiütþf~  ?“ŸÅßÊÏæoãoçïàïàçðwòwñwó÷ä¾ê:ÇtÛªþz\ÕÇ¢•×…óПíÿù>b{?îuþ¸Ïùã~çœ?tþxÈùçaçŸGøGør~.?ŸÏ/àò‹øEübþQþ1þqþ þIþ)þ)þiþþYþ9þyþ…•×ÿ¼¸·Økïç÷UXåÖ@™5Pj ”Xõ½ö|9_ÎÏåçñóùüB~¿˜_Ì?Ê?Æ?Î?Á?É?Å?Í?Í?Ã?Ë?Ç?Ï¿ð‡ë{­§û¬§û¬§û­§¬§­§‡¬§‡­§Gør¾œŸËÏãçó ø…ü"~1¿˜”Œœ‚’Ššš†–ŽžaÅõÀYÿøÿbûÊÿ‡·iAë¢üËzåi©Zå±zå—ö•ÿ½ðÞ?ö1üW¿ð_Û Ó\UùƉ•+?V~¬üXù±òcåÇÿ>\5{¸ò£òË{!X°J“& k×¼E¢üë}áfVqåÂ0ðÂŒuš´lÛ¡M‹Fš·èÔ¢yÃ4•ªýá·,\—ôÿ\4~“ÂCáÎP᪢oåB] 0§0ý­ò/Q­òóÕ*?_­òóÕ*?_­òóÕ*?_­òóÕ*?_½òóÕ+?_½òóÕ+?_½òóÕ+?_½òóÕ+?_£òó5*?_£òó5*?_¨&î¦ËÕX^ô§ w¦þqUó_þ ÇÂ;kù_[ø_Rxd¹°¹0ºdÿOƈÔf ßçƇ .lû-Œò*<¦[Ø‚[×±oåÂí´Â(­­ÂXíÂȬÂã°…­®…± *n[FT4Í_¢kÿ‹W£p™VÝÿW_?w­WøÓ6ô§ >ñ/õ·+¼UË' Û–ý¥þv…÷È Ãí 7t ÃWNúKýí ? ÔÆÜRùãä¿Ôß®ð~Ga#a¸^á´Ñø/õ·+¼«RGTx¤¿~å&©¿]á½ØÍ*$( ÷;å/õ·+¼K\ØbQ‡T)pê_êoWxÿºž(|Ÿ]xá´¿Ôß®ðÎzaÜea‹GaÓéÿÿíþwîü¿¹¿õGU¿__ÕÇ¢•÷ –ýë×ÿiŸÁ¿»ÿ òW}éýÅ/½¿ø•÷¿öþâ7Þ_üÖû‹ßyò{ïOþÀÿÀÿÈÿÄÿÌÿÂÿÊÿÆÿÎÿÎ/Ï}*Ê}ª–ûT=÷©FîSÊ} g³È}Êøšü*üªüjüêÿ¼_0h·üýó¿ãÑk_ìµ÷ó¯*¬rk Ì(µJ¬ú^{þGþGþ'þgþþWþ7þw~9¿¼Â(·ʬRk Ä¨ïµ/öÚ9ò5ùUøUùÕøÕÿp¿àKëñ+ëñ+ëñkëùëù[ëù;ëù{ëùþGþGþ'þgþþWþ7þw~9¿Üz.²ž«YÏÕ­çÖ³aI)ü{Èü{Èøšü*üªüjüê+îTÕ>ÂîoýQÕ_«úX´òz pú³ûÿìþƒÊ?m ç5œ?ÖtþXËùcmçuœ?ÖuþYÏùg}~}~~C¾¿¿1¿ ¿)¿)¿¿9¿¿%¿_ÌoÍoÍ׿ëðÛðÛòuùz+¯V\¤5нö~¾f…5Pn ”Y¥Ö@‰5PßkÏoÀoÀoÈ×â7â7æ7á7å7ã7ã7ç·à·ä·â‹ù­ùÚ|m¾¿ ¿-_—¯÷×i ëiMëiMëi-ëimëiëi]ëi=ëi}~~~C¾¿¿1¿ ¿)¿¿¿9¿¿%¿_ÌoÍ׿kóuømømùº|½×U¹ðïVü-?ªúëqU‹V^ÎC~?à¿÷Q­hÅ¿Á´óÇvÎÛ;ìàü±£óÇNÎ;;ÿìâü³+¿+¿¿;¿¿'¿¿7¿¿¿/¿¿? 00(_Ÿ?Œ?œ?â¯s=ðlÕ½öÅ^{?ß¾Â(·ʬRk Ä¨ïµç]`¤ÝøÝù=ø=ù½ø½ù}ø}ù}ùýøýùøùƒøƒùCøCøCùúüaüáü¸ØÎzÚÞzÚÞzÚÁzÚÑzÚÉzÚÙzÚÅzÚ•ßßß߃ߓߋߛ߇ߗߗßߟ?€??ˆ?˜?„?„?”¯ÏÆÎ±âzàûëÿvëªþz\ÕÇ¢•×…óPµÿËÿîÿÖÿî~Bå¿Æ#?Žtþ8Êùãhçcœ?Žuþ8ÎùçxçŸøø|CþD¾2ߘoÌ7áOáOåOãOçÏàÏäÏä›òÍø³ø³ùsøsÿãz  ¿WõÑk_ìµ÷ó£*¬rk Ì(µJ¬ú^{¾߀oÈŸÈ7âOâOæóMø&ü)ü©üiüéüü™|S¾)ߌ?‹?›?‡?÷×GZOGYOGYOG[OÇXOÇZOÇYOÇ[O'ð ø|CþD¾2ߘoÂ7áOáOåOãOçÏàÏä›òMùfüYüÙü9ü¹+®VΨªþz\ÕÇ¢•×ù©Äù£Äùã<çó?.pþhîüÑÂùçB矋ø‹ø‹ù–|+¾5߆oË·ãÛñíù|G¾ß™ïÂwå»òÝøî|¾'ß‹ï½òzàŸ×%Å^{??¯Â(·ʬRk Ä¨ïµç/æ/æ[ò­øÖ|¾-ߎoÏ·ç;ðùN|g¾ ß•ïÆwã»ó=øž|/¾÷®J¬§ó¬§ó¬§ó­§ ¬§æÖS ëéBëé"þbþb¾%ߊoÍ·áÛòíøö|{¾ß‘ïÄwæ»ð]ùn|7¾;߃ïÉ÷â{¯¸(ü]ý^­ü^­ý^­ý^mü^mý^íü^íý^øŽ|'¾ß™ïÂwå»ñÝù|O¾'ß‹ïÍ_Â_Ê÷áûò¥|)ßïÏàòƒøÁü~?”¿ŒÆ_Îç¯àGð#ø‘ü(~4?†Ë_ÉãÇñWñãù«ùkøkùëx³e’Ù2Él™d¶L2[&™-“Ì–IfË$³e’Ù2Él™d¶L2[&™-“Ì–IfË$³e’Ù2Él™d¶L2[&™-“Ì–IfË$³e’Ù2Él™d¶L2[&™-“Ì–I÷ðž¥Ož¥Ož¥Ož¥Ož¥Ož¥Ož¥Ož¥Ož¥Ož¥Ož¥Ož¥Ož¥Ož¥Ož¥Ož¥Ož¥Ož¥Ož¥Ož¥Ož¥Ož¥Ož¥Ož¥Ož¥Ož¥Ož¥Ož¥Ož¥Ož¥Ož¥O/ð/ò/ò/ñ/ó¯ð¯ò¯ñ¯óoðoðoòoñoóïðü»ü{ü{ü~)ÿ>ÿÿ!ÿÿ1ÿ1ÿ ¿Œÿ”ÿŒÿœÿ‚·72Ù™ìLöF&{#“½‘ÉÞÈdod²72Ù™ìLöF&{#“½‘ÉÞÈdod²72Ù™ì {#ÃÞȰ72ì o´‡½‘aodØöF†½‘aodØ«óö‚„½ a/HØ ö‚„½ a/HØ ö‚„½ a/HØ ö‚„½ a/HØ ö‚„½ a/HØ ö‚„½ a/HØ ö‚„½ a/HØ ö‚„½ a/HÔãÝû ÷¾Â½¯pï+Üû ÷¾Â½¯pï+Üû ÷¾Â½¯pï+Üû ÷¾Â½¯pï+Üû ÷¾Â½¯pï+Üû ÷¾Â½¯pï+Üû ÷¾Â½¯pï+Üû ÷¾Â½¯8‚÷^_x¯/¼×Þë ïõ…÷úÂ{}±â½¾–ÖoKë·•õßÚúocý·µþÛYÿíù|¾#߉ïÌwá»òÝøî|w¾ß“ïÅ÷æ/á/åûð}ø¾|)ßïÏàòƒøAü`^÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>†îcè>ÆŠîcËâüتȱ"?¶.ÏmÊòcÛÒüØ®$?¶¯Ÿ;ðùŽ|'¾3ß…ïÊwã»ó=ø|O¾ß›¿„¿”ïÃ÷åûò¥|?¾??€Èâóƒù!¼îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}Lºi ¯û˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>¦2~?‘ŸÈOâ'óSø©ü4^÷1é>&ÝǤû˜t“îcÒ}LºI÷1ÝÆë>&ÝǤû˜t“îcÒ}LºI÷1ÝÃë>&ÝǤû˜t“îcÒ}LºI÷1é>¦r~.?ŸÏ/àò ùEüb^÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}L/ñºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>¦ þ]^÷1½Çë>¦¥¼îcú€×}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜¾áu“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>FQî£Zî£zî£Fî#9æ>tC÷1tC÷1tC÷1<®º¡ûº¡ûº¡ûëòº¡ûº¡ûº¡ûº¡ûº¡ûº¡ûº¡ûº¡ûżîcè>†îcè>†îcè>†îcè>F=^÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tc^÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tãþHþ(þhþ^÷1tcE÷±¥ëÜV®s[¹Nhí:·ëܶ®“Û¹Nnï:¹ß‘ïÈwâ;ó]ø®|7¾;߃ïÁ÷ä{ñ½ùKøKù>|_¾/_Ê÷ãûóø¼'nÒ`~0¯û˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜t“îcÒ}LºI÷1é>&ÝǤû˜tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tC÷1tcE÷±eýüت8?¶.r¬ÈmÊócÛ²üØ®4?¶/ÉøŽ|'¾ß™ïÂwå»ñÝù|O¾'ß‹ïÍ_Â_Ê÷áûò¥|)ßïÏàòƒøÁü~?”¿ŒÆ_Îç¯àGð#ø‘ü(~4?†Ë_ÉãÇñWñãù«ùkøkùëøëùëùøù›ø2~?‘ŸÄOâ'óSø©ü4~:3?ƒŸÁßÂÏägñ·ò³ùÛøÛùÛù;ø9üü]üÝü=ü½ü½ü}üýüüƒüCüÃü#ü#|9?—ŸÇÏçð ùEü"~1ÿ(ÿÿ8ÿÿ$ÿÿÿ4ÿ ÿ,ÿÿ<ÿÿ"ÿ"ÿÿ2ÿ ÿ*ÿÿ:ÿÿÿ&ÿÿ6ÿ_Á¿Ë¿Ç¿Ç/á—òïóðòñóóŸðËøOùÏøÏù/ø/ù/ù¯ø¯ùoøoùïøïùøøùŸøŸù_ø_ùßøßùßùå¹¢ÜGµÜGõÜGÜGÊ}xì*"÷‘ñ5ùUøUùÕøÕù5ø5ø5ùµøµùuøuùõøõùõù ø ùZüFüÆü&ü¦ü¦üfüæüü–üV|1¿5¿5_›¯ÃoÃoË×åëñÛñÛñÛó;ð;ò;ñ;ó»ð»ò»ò»ñ»ó{ð{ò{ñ{óûðûðûòûñûóðòñóó‡ð‡òõùÃøÃù#ø#ù#ù£ø£ùcøcùãøãsŸZæ¿.µÊ]jÿºÔ&ÿu©­_×®È1ÿsRûüÏIøŽ|'¾3ß…ïÊwå»ñÝù|O¾ß›¿„¿„¿”ïÃ÷åKù~|~?€ÈâóCø¡üeü0~9?œ¿‚ÁäGñ£ùÑü~,%?Ž¿ŠÏ_Í_Í_Ã_Ë_Ç_ÏßÀßÈßÄßÄ—ñø‰ü$~2?…ŸÊOå§ñÓù›ùü-üL~?‹¿•ŸÍ߯ßÎßÁÏáïäïäïâïæïáïåïãïçààäâæáËù¹ü<~?Ÿ_À/äñ‹ùGùÇøÇøÇù'ø'ù§ø§ùgøgùgùçøçùøù—ø—ùWøWøWù×ø×ù7ø7ù·ø·ù·ùwø þ]þ=~ ¿”ŸŸÿ€ÿÿˆÿ˜ÿ„_ÆÊÊÆÎÁÉÅÍÃÃËÇÏÿÀÿÈÿÄÿÌÿÌÿÂÿÊÿÆÿÎ/Ï}å>Œ[‰j¹ê¹¹”ûˆÜGÆ×äWáWáWåWãWç×à×ä×â׿׿×á×å×ã×ç7à7äkñµøøùMøMùÍøÍù-ø-ø-ù­øb~k¾6_‡÷PRlÃoË×åëñÛñÛó;ð;ò;ò;ñ;ó»ð»ò»ñ»ó{ð{ð{ò{ñ{óûðûòûñûóûóðòñó‡ð‡òõùúüaüáüü‘üQüÑü1ü1ü±üqüñ¹O-sŸZå>µÎ}jSä˜ûÔ6÷©]îS{¾ß‘ïÄwæ;ó]ø®|7¾;߃ïÉ÷â{ñ½ùKøKù>|_¾”ïÇ÷ãûóøü ~0?„Êå/ã‡ñ—óÃù+øüH~$?ŠÍáÇòWòãø«ø«øñüÕü5üµüu¼niÒ-Mº¥I·4é–¦2~?‘ŸÄOæuK“niÒ-Mº¥I·4é–&ÝÒ¤[štK“niÒ-Mº¥I·4é–¦ÛyÝÒ¤[štK“niÒ-Mº¥é^§-é´%¶¤Ó–tÚ’N[ÒiK:mI§-•ó:mI§-é´%¶¤Ó–tÚ’N[ÒiK‹y¶¤Ó–tÚ’N[ÒiK:mI§-é´%¶¤Ó–tÚ’N[z‘‰‰™…•ƒ““‹›‡¯àßåßã—ðKø¥üûüü‡üGüÇü'ü'ü2þSþ3þsþ ÞÜÝdîn2w7™»›ÌÝMßòæî&sw“¹»ÉÜÝdîn2w7™»›ÌÝMæî&sw“¹»ÉÜÝdîn˜»ÕræîF܇¹»áFz˜»æî†¹»aîn˜»æîÆê¼9ƒá±ï0g0Ì sÜÁ0g0ÖãÍ sÜÁ0g0Ì sÜÁ0g0Ì sÜÁ0g0Ì sÜÁ(æ·ækóæ †9ƒaÎ`˜3æ F=Þ\¥0W)ÌU s•Â\¥0W)ÌU s•Â\%£+¼¹Ja®R˜«æ*…¹Ja®R˜«ûòæ*…¹Ja®R˜«æ*…¹Ja®R˜«æ*…¹Ja®R˜«GðæH„9aŽDÍ›#æH„9±bŽDK÷ÃZ¹ÖÚý°6î'´q?¬­ûaíÜOkï~Z¾#߉ïÌwæ»ð]ùn|w¾ß“ïÅ÷â{ó—ð—ò}ø¾|)ßïÇ÷çðJŸi?˜Âå‡ò—ñÃøËùáüü~$?’ÅæÇðcù+ùqüUüUüxþjþþZþ:^Ã;ix' ï¤á4¼“†wÒðNÞIÃ;ix' ï¤á4¼“†wÒðNÞIÃ;ix' ï¤á4¼“†wÒðNÞIÃ;ix' ï¤á4¼“†wÒðNÞI³4i–&ÍÒ¤Yš4K“fiÒ,Mš¥I³4i–&ÍÒ¤Yš4K“fiÒ,Mš¥I³4i–&ÍÒ¤Yš4K“fiÒ,Mš¥I³4i–&ÍÒ¤Yš4K“fiÒ,Mš¥éEþ%þ%þeþþUþ5þuþ þMþMþ-þmþ¾‚—_Â/á—òïóðòñóŸðŸðËøOùÏøÏù/x3è“ôÉ úd}2ƒ>™AŸÌ OfÐ'3è“ôÉ úd}2ƒ>™AŸÌ OfÐ'3è“ôÉ ú0ƒ>Ì 3èà ú0ƒ>|! 3èà ú0ƒ>Ì 3èà ú0ƒ>ÌÜ 3wÃÌÝ0s7ÌÜ 3wÃÌÝ0s7ÌÜ 3wÃÌÝ0s7ÌÜ 3wÃÌÝ0s7ÌÜ 3wÃÌÝ0s7ÌÜ 3wÃÌÝ0s7ÌÜ 3wÃÌÝ0s7ÌÜ 3wÃÌÝ0s7Ì 3ÃŒÁ0c0Ì 3ÃŒÁ0c0Ì 3ÃŒÁ0c0Ì 3ÃŒÁ0c0Ì 3ÃŒÁ0c0Ì 3ÃŒÁ0c0Ì 3ÃŒÁ0c0Ì 3ÃŒÁ0c0ÌT 3•ÂL¥0S)ÌT 3•ÂL¥X1S©ei~lU’[×ÏmŠócÛ"ÇŠüØ®ÿÿ!ÿÿ1ÿ ¿Œ_ÆÊÆÎÁÉÅÍÍÃËÇÏÿÀÿÈÿÄÿÄÿÌÿÂÿÊÿÆÿÎ/Ï}(ÉFQî£Zî£zî£Fî#å>"÷‘ñ5ùšü*üªüjüêüüšüZüZüÚü:üºüzüúüü†ü†|-~#~c~~S~3~s~s~ ~K~+¾˜ßš¯Í×áëðÛðÛòuùzüvüöüüüŽüNüÎü.ü®ünüîüîüüžü^üÞü>ü¾ü~ü~üþüüüAüÁü!ü¡ü¡|}þ0þpþþHþ(þhþhþþXþ8þxë·e‘cE~lUž[—åÇ6¥ù±mI~lW??¶/Îø|G¾ß™ïÂwå»ñÝùî|¾'ß‹ïÍ_Â_Ê÷áûð}ùR¾ߟÀäñƒøÁü~(?Œ¿œÎç¯àGð#ùQüh~ ?–Ë_Éã¯âÇóWó×ð×ò×ò×ñ×ó7ð7ò7ñeü~?‘ŸÄOæ§ðSùiüt~:3?ƒ¿…ŸÉÏâoågó³ùÛøÛù;ø9üü]üÝüÝü=ü½ü}üýüüƒüCüCüÃü#|9?—ŸÇÏçð ø…ü"~1ÿ(ÿÿ8ÿÿÿ$ÿÿ4ÿ ÿ,ÿÿ<ÿ<ÿÿ"ÿÿ2ÿ ÿ*ÿÿÿ:ÿÿ&ÿÿ6ÿ_ÁWðïòïñKø¥üûüü‡ü‡üGüÇü'ü2þSþ3þsþsþ þKþ+þkþþ[þ;þ;þ{þþGþ'þgþþWþWþ7þw~yî£(÷Q-÷Q=÷‘'$*¹W"r_“_…_•____ƒ_“_‹_›_‡_—_—__Ÿß€ß¯ÅoÄoÌoÌoÂoÊoÆoÎoÁoÉoÅoÅó[óµù:ü6ü¶|]¾._ߎߞßߑ߉ߙߙ߅ߕßß߃ߓߋߋߛ߇ߗßߟ?€???ˆ?˜?„?”¯ÏÆÎÎÁÉÅÍÃËÇWðUÕ9þ£ûÛ~Tu? ªE+û…Mg¶Wüçûˆéß_â{àK}ÜÇ÷À}}\ê{ˆ~¾îï{èü~ ?ˆÌá‡ò—ñÃøaüåüpþ ~?’ÅæGócø±ü•ü8þ*~üÊ~AþÿTáµ/öÚûù¥Ö@¹5Pf ”Z%Ö@}¯=?ÈâóCø¡üeü0þrþr~8?‚ÉâGócø1üXþJ~?þý‚K¬§K­§K­§>ÖS_ë©Ôzêg=õ·žðù¼ÿ—Ó`~?”¿ŒÆ_Î_Îç¯àGð#ùQüh~ ?†Ë_Éã¯âÇç>ÿ¨ŠÎñ ÷·þ¨ê¯ÇU},Zy=°üOðÿÛ.â×G¬ü×{µóÇÕÎ×8\ëüqóÇõÎ78ÿÜèüs_ÆOà'ò“øÉü~*?•ŸÆOçoægð·ð3ùYü,þV~6;?çŸ×…»…×£×¾Økïç×TXåÖ@™5Pj ”Xõ½ö|_ÆOà'ò“øÉü~*?ŸÆOçoægð·ð3ùYü­ü­ülþ6þvþ~ή®¶ž®±ž®±ž®µž®³ž®·žn°žn´žnâËø2~?‘ŸÄOæ§ðSùiü4~:3?ƒ¿…ŸÉÏâoåoågó·ñ·ówðsV\üoýÿÏîoýQÕ_«úX´òz`yÑ¿þwýŸ:ˆÿn±ò?ïtþ¸Óùã.ç»?îqþ¸×ùã>çŸûààäâæáËù¹ü<~?Ÿ_À/äñ‹ùGùÇøÇøÇù'ø'ù§ø§ùgV^üózàÎb¯½ŸßUa ”[eÖ@©5Pb Ô÷Úóòòñóðåü\~?ŸŸÏ/àò‹øÅü£ücüãüãüü“üSüÓü3¸¸ÓzºËzºËzºÛzºÇzº×zºÏzºßzz€ˆ˜„/ççòóøùü|~¿_Ä/æåãççŸàŸäŸâŸæŸYq=PUãîoýQÕ_«úX´òz púï¾Nÿ«½â?{¿¡òO{ÖùãYççœ?žwþxÁùãEç—œ^vþy……•ƒ“‹››‡¯àßåßã—ðKù÷ù÷ùøùøùOøe+¯þy=ðl±×ÞÏŸ«°Ê­2k Ô(±ê{íùWùWù×ø×ù7ø7ù·ø·ùwøwø þ]þ=~ ¿”Ÿÿ€ÿ€ÿÿˆÿ˜ÿ„_ö‡ëg­§ç¬§ç¬§ç­§¬§­§—¬§—­§WøWùWù×ø×ù7ø7ù·ø·ùwøwø þ]þ=~ ¿”Ÿÿ€ÿ€ÿÿˆÿ˜ÿ„_¶âz *÷ Øõ÷ý¨ê¯ÇU},Zy=P8ýùûÿÿÞÇŠû•ÿj?uþøÔùã3çÏ?¾pþøÒùã+矯¾á¿á¿å¿ã¿çàäâææáåãç—ç>ŠrÕÖqÌ}TÏ}ÔÈ}xè7"÷‘ñ5ÿ2×…ÊzU½öÅ^{?ÿ¬Â(·ʬRk Ä¨ïµç¿å¿å¿ã¿çàäâæááåãç——Xõ½öÅ^û"Ç k Ü(³r_óןZŸYŸYŸ[_X_Z_Y_[Ïßðßòßòßñßó?ð?ò?ñ?ó¿ð¿ð¿ò¿ñ¿óË­ç"빚Õý{¨îßC ÿDã"ü{Èøš+®þ·¿þÿg÷·þ¨ê¯ÇU},Zy=P8ýÙûÿö~Cå¿ÄUœ?VqþXÕùc5çÕ?ÖpþXÓùg-矵ùµùuøuùõøõù ø ùZ|-~#~c~~S~3~s~ ~ ~K~+¾˜ßš¯Í×ùçõ@U~=®ê£×¾Økïç«VXåÖ@™5Pj ”Xõ½ö¼ß0Öá×å×ã×ç7à7äkñññó›ð›ò›ñ›ó[ð[ò[ò[ñÅüÖ|m¾Î\Ä*ÖÓªÖÓªÖÓjÖÓêÖÓÖÓšÖÓZÖÓÚü:ü:üºüzüúüü†|-~#~#~c~~S~3~s~ ~K~K~+¾˜ßš¯Í×Yq=PUûV¸¿õGU=®êcÑÊëÂyèÏÞÿÿ³÷*ÿsçmœ?¶uþ¨ëüQÏùc;çívpþّߑ߉ߙ߅ߕßß߃߃ߓߋߛ߇ߗßߟߟ?€??ˆ?˜?„?tåõÀ?¯¶)öÚûù¶Ö@¹5Pf ”Z%Ö@}¯=¿¿¿3¿ ¿+¿¿;¿¿'¿'¿¿7¿¿/¿¿? 0è®¶±ž¶µž¶µžêZOõ¬§í¬§í­§¬§ùøøù]ø]ùÝøÝù=ø=ù=ù½ø½ù}ø}ùýøýùøøùƒøƒùCøCW\¬|Þ° ?ªúëqU‹V^ü5ž7ŒúÎõ?sþ8Üùãç#?Žrþ9ÚùçþþXþ8þxþ¾ß?‘?‘oÄŸÄŸÌ7æ›ð§ð§ò§ò§ñ§ógðgòMùf+¯þy=P¿Økïç‡UXåÖ@™5Pj ”Xõ½öü±ü±üqüñü |¾!"߈oÄŸÄŸÌ7æ›ð§ð§ò§ñ§ñ§ógðgòMùf¸¨o=f=f=n=a=i=e=m=ÃËËÇÏŸÀ7àò'òøFüIüÉ|c¾  *:&ß”o¶âz ðwcO`° ÙÙFìIþì“ýÙùÆ|þþTþ4þtþ þLþL¾)ߌ?‹?›?‡?—/áKøóøóù øæ| þBþ"þ"þb¾%ߊoÍ·áÛòíøv|{¾ß‘ïÄwæ»ð]ù®|7¾;߃ïÉ÷â{óžÏއgÇóãáÙñðìxxv<<;žÏއgÇóãáÙñðìxxv<<;žÏއgÇóãáÙñðìxxv<<;žÏއgÇóãáÙñðìxxv<Æóž• ÏÊ…gå³ráY¹ð¬\xV.<+ž• ÏÊ…gå³ráY¹ð¬\xV.<+ž• ÏÊ…gå³ráY¹ð¬\xV.<+ž• ÏÊ…gå³ráY¹ð¬\xV.æðž Ï„g³áÙ€ðl@x6 <ž Ï„g³áÙ€ðl@x6 <ž Ï„g³áÙ€ðl@x6 <ž Ï„g³áÙ€ðl@x6 žáí… {!Ã^Ȱ2ì… {!Ã^Ȱ2ì… {!Ã^Ȱ2ì… {!Ã^Ȱ2ì… {!Ã^Ȱ2ì… {!Ã^Ȱ2ì… {!Ã^Ȱ2ì… {!Ã^ÈXÆÛûö~„½aïGØûö~„½aïGØûö~„½aïGØûö~„½aïGØûö~„½aïGØûö~„½™½™½™½™½™½™½™½™½YMÞ½®Ì½®Ì½®Ì½®Ì½®Ì½®Ì½®Ì½®Ì½®Ì½®Ì½®Ì½®Ì½®Ì½®Ì½®Ì½®Ì½®Ì½®Ì½®Ì½®Ì½®Ì½®Ì½®Ì½®Ì½®Ì½®Ì½®Ì½®Ì½®Ì½®Ì½®¬ï½½Ì{{™÷ö2ïíeÞÛ˼·—yo/óÞ^æ½½lÅ{{Ç{ýNðú5ðú5ôúèõoäõ?Éë2ߘoŸŸʟƟΟΟÁŸÉ7å›ñgñgóçðçðçò%üyüùü|s¾ß‚¿¿ˆ¿˜×y §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §Ðy §(ççòóøùü~!¿ˆ_Ìë<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSè<…ÎSèK¹Ï"÷YÆ×äuž2§Lç)ÓyÊtž2§Lç)ÓyÊtž2§Lç)ÓyÊtž2§Lç)ÓyÊtž2§Lç)ÓyÊtž2§Lç)ÓyÊtž2§Lç)ÓyÊtž2§Lç)ÓyÊtž2§Lç)ÓyÊtž2§Lç)ÓyÊtž²§Š+òcƒòüذ,?žXš•äÇ“êçÇ“‹ócc¾1ß„?…?•???ƒ?“?“oÊ7ãÏâÏæÏáÏåKøþ<þ|þ¾9ß‚¿¿ˆ¿ˆ¿˜oÉ·â[ómø¶|;¾ßžïÀwä;ñù.|W¾+ßïÎ÷à{ò½øÞü%ü%ü¥|¾/_Ê÷ãûóøü@~?˜Âå/ã‡ñÃøËùáüü~$?ŠÍæÇðcù+ùqüUüxþjþjþþZþ:þzþþFþ&þ&¾ŒŸÀOä'ñ“ù)üT~*?ŸÎßÌÏàoágò³øYü­ülþ6þvþ~''7/?ÿÿÿ ÿÿ0ÿ_ÎÏåçñóøùü~!¿ˆ_Ì?Ê?Æ?Æ?Î?Á?É?Å?Í?Ã?Ë?Ë?Ç?Ï¿À¿È¿Ä¿Ì¿Â¿Â¿Ê¿Æ¿Î¿Á¿É¿Å¿Í¿Í¿ÃWðïòïñKø¥üûüûüü‡üGüÇü'ü2þSþSþ3þsþ þKþ+þkþþþ[þ;þ{þþGþ'þgþgþþWþ7þw~yî³¢ÜgÆÐdÕrŸUÏ}V#÷YÊ}æDŸe|M~~~U~5~u~ ~M~-~m~m~~]~=~}~~C¾_‹ßˆß˜ß„ߔߌߜ߂߂ߒߊ/æ·ækóux›³³mømùº|=~;~{~~G~ÇÜÇ ù¯‹ù¯‹†ù¯‹‹ýºFùŸ'åNœœÿ9јoŸŸʟʟƟΟÁŸÉ7å›ñgñgñgóçðçò%üyüùüü|s¾!1ß’oÅ·â[ómø¶|;¾=ßïÈwä;ñù.|W¾ßïÁ÷à{ò½øÞü%ü¥|¾/ß—/åûñýùü@~?˜Ìá‡ò—ñÃøËùáüüü~$?ŠÍáÇòWòWòãø«øñüÕü5üµüuüuüõü üüM|?ŸÈOä'ñ“ù)üT~?¿™¿™ŸÁßÂÏägñ·ò³ùÛøÛøÛù;ø9üü]üÝü=ü=ü½ü}üýüüƒüCüÃüÃü#|9?—ŸÇÏçð ù…ü"~1ÿ(ÿÿ8ÿÿ$ÿ$ÿÿ4ÿ ÿ,ÿÿ<ÿÿÿ"ÿÿ2ÿ ÿ*ÿÿ:ÿ:ÿÿ&ÿÿ6ÿ_Á¿Ë¿Ë¿Ç/á—òïóðòññóŸðËøOùÏøÏù/ø/ø/ù¯ø¯ùoøoùïøïùïùøùŸøŸù_ø_ùßøßøßùå¹ÏŠrŸUË}V=÷YÜg©È1÷Yä>Ëøšü*üªüj¼ñ·ÙêüüšüZüÚü:üºüzüzüúüü†|-~#~c~~~S~3~s~ ~K~+¾˜/æ·ækóuømømùº|=¾¿¿=¿¿#¿Sîã„ÜGƒ"ÇÜGÃÜlj¹F¹“r'óù&|þþTþ4þtþ þL¾)ß”oƟş͟ß˗ðçñçñçóðÍùü…üEüÅüÅ|K¾ßšo÷åÛñíùö|¾#߉ïÌwá»òÝøn|w¾ß“ïÅ÷æuÉB—,tÉB—,tÉ¢”ïÇ÷çðy]²Ð% ]²Ð% ]²Ð% ]²Ð% ]²Ð% ]²Ð% ]²Ð%‹Ñ¼.Yè’….Yè’….Yè’Åx^‡%tXB‡%tXB‡%tXB‡%tXB‡%Êx–Ða –Ða –Ða –Ða‰i¼Kè°„Kè°„Kè°„Kè°„Kè°„Kè°ÄÞÜù0w>Ì»ysçÃÜù0w>ÌsçÃÜù0w>ÌsçÃÜù0w>ÌsçÃÜù˜Ï/àò‹øÅ¼¹óaî|˜;æÎ‡¹óaî|˜;æÎÇ3¼9»aÎn˜³æì†9»ñ"oÎn˜³æì†9»aÎn˜³æì†9»aÎn˜³æì†9»aÎn˜³ïòæìÆÞœÝxŸ7g7ÌÙ svÜÝ0g7ÌÙe¼¹‚a®`˜+æ †¹‚a®`˜+_óæ †¹‚a®`˜+æ †¹‚a®`˜+æ †¹‚a®`˜+æ †¹‚a®`V”û¬Zî3ÿfæ fæ fæ fæ fæ f5ys”2ãl2s”2s”2s”2s”2s”2s”2s”lS¬<òæ(eæ(eæ(eæ(eæ(eæ(eæ(eñæ(eæ(eæ(eæ(eæ(eæ(eæ(eæ(eæ(eæ(eæ(eæ(euxs#2s#2s#²º¼¹™¹™¹™¹™¹Ùй'¸ÔÀý î5t?áD÷ƒ¹t’ûA'»Ô˜oÂ7áOáOåOãOçÏàÏä›òMùfüYüÙü9ü¹| >ßœoÁ_È_Ä_Ì_Ì·ä[ñ­ù6|[¾ßžoÏwà;òøÎ|¾+ßïÆwç{ð=ù^|o^£34:C£34:C£34:C£34:C£34:C£34:C£34:C£34:C£34:C£34:C£34:C£34:C£34:C£34:C£34:C£34:C“,4ÉB“,4ÉB“,4ÉB“,4ÉB“,4É¢ŒŸÀOä'ñ“ù)üT~¯Išd¡Išd¡Išd¡Išd¡Išd¡Išd¡Išd¡Á,¡Á,¡Á,¡Á,¡Á,¡Á,¡Á,¡Á,¡Á,¡Á,¡Á,¡Á,¡Á,¡Á,¡Á,¡Á,aæ|˜9f·™óaæ|˜9f·™óaæ|˜9f·™óaæ|˜9f·™óaæ|˜9f·™óaæ|˜9f·™óaæ|˜9f·™óaæ|˜9f·™óaÆn˜±f솻aÆn˜±f솻aÆn˜±f솻aÆn˜±f솻aÆn˜±f솻aÆn˜±fìffìffìffìffìffìffìf6 gfìffìff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff(ef(ef(ef(ef(ef(ef(ef(ef(e+f(P??6(Î ‹+òã‰åù±QY~<©4?ž\’óMøSøSøSùÓøÓù3ø3ù¦|3¾6._ŸǟϟÏ_À7ç[ðòñó-ù–|+¾5߆oË·ãÛóø|G¾ß™ïÂwå»ñÝùî|¾'ß‹ïÍ_Â_Ê÷áûð}ùR¾ߟÀäñƒøÁü~(?Œ¿œÎç¯àGð#ùQüh~ ?–Ë_Éã¯âÇóWó×ð×ò×ò×ñ×ó7ð7ò7ñeü~?‘ŸÄOæ§ðSùiüt~:3?ƒ¿…ŸÉÏâoågó³ùÛøÛù;ø9üü]üÝüÝü=ü½ü}üýüüƒüCüCüÃü#|9?—ŸÇÏçð ø…ü"~1ÿ(ÿÿ8ÿÿÿ$ÿÿ4ÿ ÿ,ÿÿ<ÿ<ÿÿ"ÿÿ2ÿ ÿ*ÿÿÿ:ÿÿ&ÿÿ6ÿ_ÁWðïòïñKø¥üûüü‡ü‡üGüÇü'ü2þSþ3þsþsþ þKþ+þkþþ[þ;þ;þ{þþGþ'þgþþWþWþ7þw~yî³¢ÜgÕrŸUÏ}–Ю<æ>K¹Ï"÷YÆ×äWáWåWãWãWç×à×ä×â׿×á×å×å×ã×ç7à7äkññóó›ð›ò›ñ›ó[ð[ò[ñ[ñÅüÖ|m¾¿ ¿-_—¯Ë×ã·ã·çwàwäwÊ}œà:¯ë¼†®ºÎ;Ñu^#×y'¹Î;Ùubc¾   *:&ß”oÆ7ãÏâÏæÏáÏåKøóøóùóù øæ| þBþ"þb¾%ß’oÅ·æÛðmùv|{¾ßïÈwâ;ó]ø®|7¾;ßïÁ÷ä{ñ½ùKøKù>|¾/_Ê÷ãûóøü ~?˜Âå/ã‡ñ—óÃùáüü~$?ŠÍáÇòcù+ùqüUüxþjþþZþZþ:þzþþFþ&¾ŒŸÀOà'ò“øÉü~*?ŸÎOçoægð·ð3ùYü­ül~6;?‡¿“¿‹¿›¿›¿‡¿—¿¿Ÿ€ˆˆ˜„/ççòóøùü~¿_Ä/æåãçŸàŸàŸäŸâŸæŸáŸåŸãŸçŸç_à_ä_â_æ_á_å_ã_ã_çßàßäßâßæßá+ø þ]þ=~ ¿”Ÿÿ€ÿÿÿˆÿ˜ÿ„_ÆÊÆÎÎÁÉÅÍÃËÇÇÏÿÀÿÈÿÄÿÌÿÂÿÊÿÊÿÆÿÎ/Ï}V”û¬Zî³ê¹Ïj¬ã˜û,å>³¡&Ëøšü*üªüjüjüêüüšüZüÚü:üºüºüzüúüü†|-~#~c~c~~S~3~s~ ~K~+~+¾˜ßš¯Í×á·á·åëòuùzüvüöüüŽüN^¿ãËóã eù±Ai~lX’O¬ŸçÇœW+òãÉ|c¾  *::&ß”oƟş͟ßß˗ðçñçóðÍù| þBþ"þb¾%ߊoÍ·áÛðmùv|{¾ß‘ïÄwæ;ó]ø®|7¾;߃ïÉ÷â{ñ½ùKøKù>|_¾”ïÇ÷ãûóøü ~0?„Êå/ã‡ñ—óÃù+øüH~$?ŠÍáÇòWòãø«ø«øñüÕü5üµüuüõü ü üüM|?ŸÈOâ'ó“ù)üT~?¿™ŸÁßÂßÂÏägñ·ò³ùÛøÛù;ø;ø9üü]üÝü=ü½ü}ü}üýüüƒüCüÃü#|9_ÎÏåçñóùüB~¿˜_Ì?Ê?Æ?Î?Á?É?Å?Í?Í?Ã?Ë?Ç?Ï¿À¿È¿Ä¿Ä¿Ì¿Â¿Ê¿Æ¿Î¿Á¿É¿É¿Å¿Í¿ÃWðïòïñKø%üRþ}þþCþ#þcþþ~ÿ)ÿÿ9ÿÿ%ÿÿÿ5ÿ ÿ-ÿÿ=ÿÿ#ÿ#ÿÿ3ÿ ÿ+ÿÿ;¿œ_žû¬(÷YµÜgÕsŸÕÈ}–rŸEî3ƒ ²Œ¯É¯Â¯Ê¯Æ¯Î¯Á“Ÿ­É¯Å¯Í¯Ã¯Ë¯Ç¯ÏoÀoÀoÈ×â7â7æ7á7å7ã7ã7ç·à·ä·â‹ù­ùÚ|m¾¿ ¿-_—¯ÇoÇoÏoÏïÀï˜ûªé¯pëªîTõ±he¿`ùŸàÿ·]Äÿ®Xù/÷,ßÓå{º³}OwŽïéÎõ=a‰ï Ïó=áù¾§¸€¿€oηà/ä/â/æ[ò­øV|k¾ ß–oÇ·ç;ðùŽ|'¾3ß…ïÊwã»ÿG¿à6óüÿ†G¯}±×ÞÏÏ®°Ê­2k Ô(±ê{íùæ|s¾!1ß’oÅ·æ[ómø¶|;¾=ßïÈwâ;ñù.|W¾ßýý‚³¬§³­§³­§s¬§s­§ëé<ëé|ëé¾9ßœoÁ_È_Ä_Ì·ä[ñ­ùÖ|¾-ߎoÏwà;òøN|g¾ ß•ïÆw÷M~•uŽW¸¿õGU=®êcÑÊëåEÿúßõê þ»}ÄÊ‘=œ?z8ôtþèåüÑÛùãçKú8ÿôåûò¥|?¾??€Èâóƒù!üPþ2~9?œ¿‚¿‚ÁäGñ£ù1üØ•×ÿ¼èQìµ÷óžÖ@¹5Pf ”Z%Ö@}¯=_Ê—òýøþü~ ?ˆÌá‡ðCùËøaüåüpþ ~?‚ÉâGócø±¸èa=õ´žzZO½¬§ÞÖÓ%ÖÓ¥ÖSë©/_Ê—òýøþü~ ?ˆÌ«LÅ~(?Œ¿œÎ_ÁàGð#ùQüh~ ?vÅõ@UuŽW¸¿õGU=®êcÑÊëÂyè¿û:ý¯öŠÿl±R]éüq¥óÇ8竜?Æ;\íüqóϵÎ?×ñ×ñ×ó7ð7ò7ñeü~"?‘ŸÄOæ§ðSùiütþfþf~ ?“ŸÅßÊÏ^y=ðÏë+‹½ö~>®Â(·ʬRk Ä¨ïµç¯ç¯çoàoäoâËø üD~?‰ŸÌOá§òÓøéüÍü ~ ?“ŸÅßÊÏþÃõÀ•ÖÓ8ëiœõt•õ4ÞzºÚzºÆzºÖzºŽ¿ž¿ž¿¿‘¿‰/ã'ðùIü$~2?…ŸÊOã§ó7ó3øü-üL~+?{Åõ@UvŽ ¦úû~Tõ×ãª>­¼(œ‡þ|¯øßûXqÿ ò_ìmη9Üîüq‡óÇç;?îrþ¹Ûùçþþ^þ>þ~þþAþ!þaþaþ¾œŸËÏãçó ø…üB~¿˜”Œœâ/s=P¨†VÕÑk_ìµ÷óÿÃÞ}ÀGQíÿÿíC¥PAÔÐB¯;;gè½ !T¥ªz ]z J“*„^ƒ@¨¡…Z(ÒŠŠ…ß®y1ËÝÜù&ë¹—ûÿ_öñØ»wž0çìììîœó^ψb D0¡Œû¿¿¿¿ ¿¿¿¿ ¿¿…ßÿ¿¿ ¿ÆïÁïÅïÃïÇxä|à ÆÓ*ÆÓ*ÆÓjÆS$ãi ãi-ãiãi=~~~#~~3~ ~+~~;~;> ¿ÿ%~'~~7>߃ߋ߇ß?ðpü§ßÿýÝÿôíq¿?îÇ€'çÞãÐßýýÿïþÞày%äøqãG ÇC?sü8Âñã(ÇŸcbñ±øãøø“øSøÓø3ø8|þ,þþ<þþ">  ÿþþº}>ð8ß÷#û>}Ïó˜xÆ@c ‚1Æe ¸Ø÷øãøãøø“øSøÓø3ø8üYüYü9üyüüE|<þþ2þ2þ þ*þ+ü5üõGÎ2žbO1Œ§CŒ§ÃŒ§#Œ§£Œ§cŒ§Xüqüqü üIü)üiü|þ,þ,þþ<þþ"> ÿþþúÃQð¸®xèþ§oûýøq?<9ð‡þîïÿ÷÷ÏÿÞàøqƒãÇMŽ·8~|Íñã6Ç;¾áøó-þ[üwøïñ?àïâÄÿ„¿‡¿‡ÿÿ þWü}üoøßñàÿÀÿ‰à% ÁKŠ/)¼¤zr>`ŸÜdßóüf>'>>>7þy||^ü ø|OξHê@ö=ÏÓÄ3¢Œ0Æ@(cÀžÇgÄgÄgÂ?όςŸŸ Ÿ Ÿÿ,þ9||N|.|n|nüóø<ø¼øðù|ç’šñ”†ñÄÉã)-ã)ã)=ã)ãé)|F|F|&üÓøÌø,øgðYñÙðÙðÙñÏâŸÃçÀçÄçÂçÆçÆ?σϋŸïá(ðÞžÌ7|L·Çý~ü¸žœüwÌ7”@Ž?òsü(ÀñãEŽ/qüx™ãOAŽ?…ð…ð¯à ã_Å¿†„/‚/‚/Š/†/Ž/Ɨė——ƗÁ—ŗ×ÇWð|Çûãÿà#û>}ÏóüñŒ(Æ@c Œ1Êp±ïñ¯à_ÁÆ¿Š ÿ:>____ __Œ/‰/…///ƒ/‹/‡/¯ðÈù@ ã)?ã)?ã©ãéEÆÓKŒ§—OO…ð¯à_ÁÆ¿Š ÿ:>____ __Œ/‰/…///ƒ/‹/‡/¯à;×ù³AüÙ"üÙ¢üÙbü[Åù·Jðo•àß Æ—Ä—Â—Æ—Á—ŗ××ÇWÀWÄWÂWÆWÁ»ð.¼wãM¼Â[øªøjøjøêøøšøZøÚø:øºøºøzøúøø†øFøÆø&ø&ø¦ø7ðÍðÍñ!øxæŠ sÅ…¹âÂ\qa®¸0W\˜+.ÌæŠ sÅ¥=ž¹âÒÏ\qé„g®¸tÆ3W\˜+.ÌæŠ sÅ…¹âÂ\qa®¸0W\˜+.ÌæŠ sÅ…¹âò>ž¹qÂÜ8anœ07N˜'ÌæÆ sㄹqÂÜ8anœ07N˜'ÌæÆ sㄹqÂÜ8anœ07N˜'ÌæÆ sㄹqÂÜ8anœ07N˜'ÌæÆÉ$¾¾!¾¾1¾ ¾ ¾)þ |3|s|¾¾%¾%þM|+|k|(¾ ¾-¾¾¾=¾¾#þ-|'üÛøÎøÎøwð]ð]ñÝðïâßÃwÇwÇ÷À÷Ä÷Â÷Æ÷Á¿ÿÿþC|_üGø~øþøñððaøøAøÁø!ø¡øaøaøáøø‘øQøÑø1øp|8~,~~<~~"~~2~2þüüTü4ütü üLüLü,ülüü\|~þSü§øÏðóñ ð ñ‹ð‹ñŸã?Ç/Á/Å/Ã/ǯÀ¯Äÿ¿ ¿‰_ƒ_‹_‡__߀߈߄ߌ߂ߊ߆߆ߎÂïÀ‰ß‰ß…ß߯ïÁïÅïÃïÇÀÄÄÇàáãàâácñ±øãøø“øSøÓø3ø8|þ,þþ<þþ">  %Á?¹=Þ›&<áy‘ø„Ç¢Q Å"‹‡%<–Mx v%<–ė——ƗÁ—ŗ×ÇWÀWÄWÄWÂWÆWÁ»ðÞ7ñ&^á-|U|5|u| |M|M|-|m||]|=|}|||C|#|c||Süøføføæø| |Kü›øVøÖøÖøP||[|;|{||G|Gü[øNø·ññïà»à»â»â»áßÅ¿‡ïŽïï‰ï…ï…ïïƒÿþC|_üGøðýðýñãàÃðñƒðƒðƒñCðCñÃðÃñ#ð#ñ#ñ£ð£ñcðáø±øqx>ÉxüüDü$üdü'ø)ø©ø©øiøéøø™øYøÙø9ø9ø¹øü<ü§øÏðóñ ð ð ñ‹ð‹ñŸã—à—â—á—á—ãWàWâ¿À¯Â¯ÆGâ#ñkðkñëðëñðñ›ð›ð›ñ[ð[ñÛðÛñQøøø/ñ;ñ»ð»ñÑø=ø½ø½ø}øýøøƒøü!üaüaüüQü1|,þ8þþ$þ$þþ4þ >¿„¿Œ¿‚¿ðäö_p“×モ‹D$< Kx,šðXÜ•ðX"0á1Œ/‰/…//ƒ/‹/‡//¯€¯ˆ¯„¯Œ¯‚wá ¼wãM¼Â[øªøjøêøêøøšøZøÚø:øºøzøzøúøø†øFøÆø&ø¦ø¦ø7ðÍðÍñ!øxrÈ„2!‡LÈ!rÈ$ßßßßO™C&ä 9dB™C&ä 9dB™C&ä 9dB™C&Ýñä 9dB™C&ä 9dò>žÜ!wEÈ]rW„Ü!wEÈ]rW„Ü Ó»"䮹+B"䮹+BîŠ Ç“»"䮹+B"䮹+B"䮹+BîŠL³μ°Î¼°Î¼LÁ³Î¼°Î¼°Î¼°Î¼°Î¼°Î¼°Î¼°Î¼°Î¼°Î¼°Î¼°Î¼°Î¼°Î¼|†Ÿ_€_ˆ_„gyayayayayayayayY‰g]]a]]a]]a]]a]]Yƒg]]a]]a]]a]]a]]a]]a]]a]]a]]a]]a]]a]]a]]a]]Ùg]]Ù‰g]]Ùg]]a]]a]]a]]a]]a]]9€gAaAaAaAaAaAaA9†gAaAaAaAaAaAaAaAaAaAaAaAaAaAaA‰Ç_Â_Æ³Ž °Žà“Ûã½ÉëüžÄï9Eø=§(¿ã÷ âüT‚߃‚ù=(__ _________ __ïÂxïÆ›x…·ðUñÕðÕñÕñ5ð5ñµðµñuðuñõðõðõñ ð ñðñMðMñMñoà›á›ãCð-ðdr ™œB&§É)dr ™œB&§É)dr ™œB&§É)dr ™œB&§É)dr ™œB&§tÁ“É)Ýðdr ™œB&§É)dr ™œB&§É)dr ™œB™A&d dB™A&d dB™A&d dB™A&d dB™A&d dB™A&d dB™A&d dB™A&d dB抹"d®™+B抹"d®™+B抹"d®™+B抹"d®™+B抹"d®™+B抹"d®Èb<™+²O抹"d®™+B抹"¬1/¬1/¬1/¬1/¬1/¬1/¬1/¬1/¬1/¬1/¬1/¬1/¬1/¬1/¬1/¬1/¬1/¬1/¬1/¬1/¬1/¬1/¬1/¬1/¬1/¬1/¬1/¬1/¬1/¬1/¬1/¬1/¬©+¬©+¬©+¬©+¬©+¬©+¬©+¬©+¬©+¬©+¬©+¬©+¬©+¬©+¬©+¬©+¬©+¬©+¬©+¬©+¬©+¬©+¬©+¬©+¬©+¬©+¬©+¬©ûäöxoôð1>á±HTÂcш„Çba ÅCK¸ƒKâKâKáKãËàËâËáËã+à+à+â+á+ã«à]xïÆ»ñ&^á-|U|5|u| | |M|-|m||]|=|}|}||C|#|c||Süø7ðÍðÍñ!øø–ø7ñ­ð­ð­ñ¡ø6ø¶øvøöøøøŽø·ððoã;ãßÁwÁwÁwÅwÿ‹ßßßßß ßßÿ>þü‡ø¾ø¾øðýðýñãàÃðññƒðƒñCðCñÃðÃñ#ð#ð#ñ£ð£ñcðáø±øqøqøñø ø‰øIøÉøOðSðSðSñÓðÓñ3ð3ñ³ð³ñ³ñsðsñøyøOñŸáçãçãàâáã?Ç/Á/Å/Å/Ã/ǯÀ¯Ä_…__įÁ¯Å¯Ã¯ÇoÀoÄoÄoÂoÆoÁoÅoÃoÇGá£ð;ð_âwâwáwã£ñ{ð{ð{ñûðûñðñ1øCøCøÃø#ø£øcøXüqü ü üIü)üiü|þ,þþþ<þþ"> %Á?¹=Þ›qžÄyZÎóŠržWŒó¼âœç•àü~üüA| þþþ0þþ(þ> ‡?‹?‡?‡?¿€¿ˆÇ_Â_Æ_Á_IðOn÷&¯‡%<…&<q%< Lx,ÆŸ+ŸðX<*á±DDÂc0¾$¾¾4¾ ¾ ¾,¾¾<¾¾"¾¾2¾2¾ Þ…7ðn¼‰Wx oá«â«á«ãkàkâkákãkãëàëâëáëãàâááã›à›âßÀ7Ã7LJàCð-ð-ñoâ[á[ãCñmðmðmñíðíñðñoá;á;á߯wÆ¿ƒï‚ïŠï†ÿ.þ=|w||O|/|o||üûøðâûâ?Â÷Ã÷Ç÷ÇŒ€ÃÄÂÆÁÁÅÃÇÀÄÂÆÆÁ‡ãÇâÇáÇã'à'â'â'á'ã?ÁOÁOÅOÃOÇOÇÏÀÏÄÏÂÏÆÏÁÏÅGà#ðóðŸâ?ÃÏÇ/À/Ä/Â/Â/ÆŽ_‚_Š_†_Ž___‰ÿ¿ ¿‰_ƒ_‹_‹_‡_߀߈߄ߌ߂߂ߊ߆ߎÂïÀ‰ß‰ß‰ß…߯ïÁïÅïÃïÇïÇÀÄÇàáãàââácñÇñ'ð'ñ§ð§ñ§ñgðqø³øsøóø ø‹ø‹øxü%üeü•ÿäö8oÿé¼÷?}{Üùû1àI~Áƒ€äokR9ˆÿj>¢çÕX‘ÏhùŒW‰Ïx•ùŒW…Ïx.>ã|ÆsóÏÄ›x…·ðUñÕðÕñ5ð5ñ5ñµðµñuðuñõðõñ ð ð ñðñMðMño<É/xø©\*²ïy^)ž1ň` „1B.ö=^áÞÂWÅWÃWÇ×À×Ä×Â×Âׯ×Á×Å×Ã×Ç7À7Ä7Ä7Â7Æ7Á7Å¿ñH~AEÆS%ÆS%ÆSeÆSÆ“‹ñd0žÜŒ'¯ð oá«â«á«ãkàkâkákákãëàëâëáëãàââáã›à›âßHð/çø¡ûŸ¾=î÷ãÇýðä|À{rzŸNn^ñßÍGôüo3ŽÍ8~4çøÂñ£Ç–?ÞäøÓŠãOk|k|(¾ ¾-¾¾=¾¾#¾#þ-|'üÛøÎøwð]ð]ñ]ñÝðïâßÃwÇ÷À÷|r>`Ÿ4 dßó¼yßßßßßßÿþ-|'üÛøÎøwð]ð]ñÝðÝðïâßÃwÇ÷À÷|ä| ã©9ã©9ã)„ñÔ‚ñÔ’ñô&ã©ã©5>Šoƒo‹o‡oï€ïˆ ÿ¾þm|gü;ø.ø®ønønøwñïá»ã{à{><xœ9Ç)þÇoûýøq?<9ð‡þ~^ñ¿v{˜wèù×{qüèÅñ£7Ç>?ÞçøñÇ9þôåøóþ#|?|üÇøø0ü@ü ü ü`üüPü0üpüüHüHü(ühü|8~,~ÜÍù€7ëq=²ïÙ÷<ïψb D0¡Œûßßßÿ1~> ??????? ???? ? ??Ž‹÷Èù@/ÆSoÆSoÆSÆÓûŒ§O2žú2ž>Â÷Ã÷Ã÷ÇŒ€ÃÄÂÆÆÁÅÃÇ“Z-#ñ£ð£ð£ñcðáø±øqÏþÓïÿþîúö¸ß÷cÀ“óïqèïæÿÝß<¯Þñ?Æsü˜Àñc"ÇI?&süø„ãÏŽ?SñSñÓðÓñ3ð3ñ³ð³ñsðsðsñøyøOñŸáçãààâáã?Ç/Á/µÏçûñã~dß²ïy>!ž1ň` „1B.ö=~~~:~~&~~6~~.~.>?ÿ)þ3ü|üüBüBü"übüçø%ø¥œŒg ¿ÿ%þKüNü.ün|4~ÏÃóÇuÝÀ£îöö¸ß÷cÀ“óïqèïþþÿ÷o½?örüØÇñc?Ç?rüˆáøsˆãÏaüaüüQü1|,þ8þþ$þ$þþ4þ >¿„¿Œ¿òä|À>ØȾçù¾xÆ@c ‚1Æe ¸Ø÷ø#ø#ø£øcøXüqü üIü)ü)üiü|þ,þþ<þþþ"> å‘󽌧}Œ§}Œ§ýŒ§Œ§ƒŒ§ÆÓ!ÆÓaüüüQü1|,þ8þþ$þþþ4þ >¿„¿Œ¿ò—À- ¨ç‰Ûs÷Fíxcm½?Ûæ¹y½Ë}¤kÔ¨Nní;x£d¼ïúžw÷)ùÿ¯xîY½Ýõ½.êõhß¡G‡öuþZïÑ«¼[ç7Hãîòn»wùK<)=§©<‡ÁÔžCSÏ)ƒxNÒzNÒ^t@Säõܽ½{C ¼ ÿzCv¼ËÆÿ×nµ÷,èÏÝ­w¯¿6н 2…ý¼ÂÍ#楧ºöþ†c¥Ü“Z[“N±²Æœµ·ÿ¢Üí>`?¦W%ct÷,Nuíãó›ÓwT 5´õ÷tÁ5.k={ûçu[l>óqއÏ|ûºò— vªkï¯j¦²æÚÅ·uõ§ZýÚÓüÒ°Ÿ»ÇgÿÍ=éÔ»¿í“ƒã¯õuªkï/öFEõÝ­¹ÚúûþT{+}ºµöö^{ÖÝ+¤‹Ýßà;wÖEbêÚûÔ@Õ_:F[Ó÷P=G··eëÓî'oÙÏ_Lk^àT×~|iY¢‘õ|¹Æºú³z/=g½ÖÃïîa/~íôÅè‡Ï˜ßrÜêúÇçÉ]ªÃôfºúS?J€²öÜÃçf©—_7‹¸f÷÷f†(#nG€S]{ËmPÛOhÛªÕÎŽêrª~öö.eºjŸ²ûnå6ÿµNí?­kïoYÿÝê£ÚÚúÛ1A5oØÛ¿|æ(sÜ5ûýÐØ3q˜±óvO§ºöþ ¹¥ݪ’\Ÿd·"«èáöç³|Û3¦«èÏvGV-w§?É©®½¿ã{XÙöå×ÖßOí;Y¯ Ø`o™´×Ì|sí~Œ³Ãû§WÞqªkïoñ…’jB“)Úú;ðZkYÜ>_7÷å¹nŽÉü…ÝßøŒ“ŒýßßuªkïoöíùêØºŸ“ë“ìoM·™ê‡.»íí¯÷¶Ë¬1Ñ>ß4Ɖ32ˆtªkÿ{±[I5±ã(]ýYE¦ U³C‡ØÛ¿êÌsjcûû$ã›)wíC#êÚû+‘õšz0}~r}’ý©u«¬§w*{û?ªø‚9¤FÛ‡ÏÝOl|±q’S]ûø¼]Cͺ¾\[Y‚/© C–ÙÛßüE1‹¦±ÿ®ßwt½R³§S]{­{šŸ×M¯«?Uò…hs]Ï8{û'kŽI=þásczd¸±omG§ºöþV.E}ÝwXr}’ýíYUÚJ3à]{û›˜ÇÌú7íó#£oýLF³l]êÚûÙà†ªùÍfmýÍŠ/¬º¬Úeo¡¨ææ«Ùìÿ~ÆÅÐ:FÌ’§ºöãKNï[9ÞÔÕŸ|ö«Ð†²öö?}ôº™vœýùÒXP2ÆùW`ú?­kßëþ(®F~¸SWêHLw5ç²ýùÕ¬Ø$…Y£¦ýy×(÷|´±t[ŒS]{yÓÕQÓËWÓÖ_¹ç«5YìïSÌ2M¾5+~ÝÛîïÈö8£Ùµ:Nuíã3}ƒ¯ÕˆeÉõIõg劸¡>™¸ÄÞþ?O™2Úï÷Æê%!ÆÊàªNuíûoGª-꓌[uõ§Î}¢¾èeoÿË“Š›ÅW\µû[»ÊeüuúùÏëÚûÛ”ª«zðëŒäú$û;ùV>+ë•}öö¿žý†Yq»¿½#£Í+×9Õµ÷·hcÕcŽäú$ûÛóô•ï•°ý+K42ë¿sÝîoÃ!Ɔ%÷êÚûû²õ=å~r}’ý]¬æRWº¯²·¿ü¹/ÌÊ7_¶ûk´:Øxáå(§ºöãK¶oî©…Ïèûþ:Oðmµº‹ý{—¹æåÌæŽƒößïNÕ$ÞX?ï¾S]{•Ê–W?Ü—\Ÿd ¶.°ÒM·¿O5UnaÎÚlïO#nf¬ýí]§ºöñ¹¥àµlSI]ý© u/©‡WÛÛßt€a–*Xæás×¾ê¸ ý•ËôOëÚû“|¹Íé/ýË_¢þž=f.,ÓÝÞþ°½ƒÌ ÚÇgãÝ7 +ktªkï/üÕTê`ÙcÚú›{¾¦º>ËeoÃÞMóA»¿Šs?[èT×Þ_󀽪༱Úúëóá&åZhoýÍœggÚýM'Ú¸]3Ø©®½¿ÍóZ/w¦«?+à•ûV™+íííOsy­™)KC_­:£sªkïo~¥05î}ç/[JºÔ‚‡ííï6 –Ù¿A¼Ý_‘F‘Æøþ.§ºöþr¼£>˜T][%f\RÏ—¶·¿XïUfñmìþBîDå,wªkïoüª£ªÅa}ç×ó¶Sܳ·}Þ¾æ€ßÙý­=k¬Û5É©®½¿íÞSåo$×'ÙßÙ«õÔ–}öçq3wÀz3óZûû+cY\6ãH…lNuýç×1ÛÕ¹ôúί¿<|Lý>Ñ>^˜5~oVºÕÔîo㰞ƺbyêÚû›±ýŽúxU>mý­ßò­šy$»½ýãó0«}ÿƒÝߤ«åy“‚œêÚû›óf¬Zž>§¶þÖ~ØOEW÷}?QòVW³ú­ßìþô6rœqªk?¿Îñ`¯šÛPßïïùïWVK‹V°·ÿ£jéÌQ[íßãoå6¦§ºöþò/í®Ö6Óöú³J¶™©b2û®? ß7ÞuþŠÝ_ÔÙûÆþ‚NuýÇ—¦ Õç%µ]Ÿ¥v˜FíÝ÷£½ýUoV7ÍŒöù‘kVçXW‘ êºû3/ ÉfN¶®þÌ_Üe.Ü<Î~þÁÏé̾×}×·-XÇØöB¤S]ûþ›’)\LaèêO-9;RÝ]—ÉÞþjo~ao»Ôî/d`Gã…ÚêÚû yçœÊs£“¶þúÏ®JͯkoÅú·Ì=çØýÍYÒרõñ]§ºþë#OYÏ´/˜\ŸTVªí•¬ÀmÏ<|î¾9ñ´ûÁ9ûó®1..Êx·GŒS]{“«ÜTíkÐÕŸZ²)‡·åás³ÁégÌ6‹íß3#ÃÝOÝ^èT×Þß3U'Oi»¾\»ñ¾•â¯ï„íwývÏ,ðªï÷̓ib1•29Õÿ-×·Î ¤«?+m˯Õêšö/óêÊóLþÉŸ»ÓÖ¸ïN»a’S]ûû{@¹urÊDmý=ûfQ+ ,ÐÞþ¢kÇ™¥óØ×§³nr§Ž]èT×¾ÿöüPÑJ[åž®þÔ͵…­<)ÒÙÛ_¬f?3ÇñbvŠ/7Žqªë?yu…“õmýEßê®–µ?ï˜Æ›ïuúÓîï\HGã܆lNuíý;TBo ïóÃOª{¾ëçÍÖûÍÎMíëÍ™3êuÿsªkýŠ~IíÉ–YWVð7%Ô©ÊöõÉæ wz˜½[Ù¿7¸[¿lÜ_1鮽¿ðò[Ù LN®O²¿9‹ÿÃõïl½n¶>e_?çNuÕt§™}ש®}|žjö¼Š«­íøbTÛ`¥L[ØÞþÊ-®šEò®|øÜH;z˜+Ïñ8§ºöþ^3ÔœÝGß÷Ÿå754?_ïû|{)8»yz¦}=±aNAãÏ-uœêÚûÛ|¡•aë~mýŽmi½0?ÌÞþÅV6{]±Ï7Å›Œ;²9Õõ_ÿyྪZ ‘gÑ]WÞNNuíý–ÝSø¸®þTµ€†æ´Þ¾ßWV”jf^›hž7~:_Ð)ër§ºöþ®Ôb¥»RWVêó#­nøÞ¿‡¬f¶©ë›ÿ·úN#cjæ0§ºöþæ^ï¤U»š\ŸTjó¹þjú_kª'lÁoöšå·å±ûû&,Ü=dx°S]ûñ%ß¡ÁÖ†ÓÉõIõg•xo¬õèï›)j¬5ŸKk÷kü°Ü˜3¥ S]ÿ÷ww©‘¥ô}¾}ðÙE5ï—½ý-U6ó”±Ç¿1zkwAÑNuíý© ÔÕñúæo¾×§½õÔ²Möö?ß=ùüêoìþ~É6ɘܧ¯S]{÷ã+¨©¯oJ®Oª?+ËÑjEëJößÿR·æì|¾õ&ÜŽwgx£¿S]ûë/òhFµãÌ­äú$ûÛ?'³ºó¬ñð¹ùR;ËÌúû‡Ïëí#Ýi¿ÝéT×¾ÿ¶~›BÝýæJr}Rý©ËÏmµ2—ôÍ/µá”Yò÷§>w?_±q¤@y§ºöþå(«ÂRêû~éÏ9TÄpß÷+cSW4«}e÷—7EãÈþÜNuíã3õÜÚjýJmë¿Xy …©ÓõÂìí·jt6„Ú×_ºëÏ*h:ZdšZ\Ã÷ýõº´ ÌÍ÷¦ÚýMšx͸ûe¸S]{ öœ·òÍ;¡­¿-'Z%–û~ÿk“Ùù–=¿Âýú‡±Æ/킜êÚ_7[Së›—M®O²¿Ìój« ¯¬··¿òÎ fÑÆÍ>7JW w–p9Õõ¿¿×îj†‰¶ßTèî+æ˜Qííøí sêá6Ÿ»ó¬éëN_»ŽS]ûøL{8ØJñôOºú³ku´žtÉÞþæ¯ï2ÍB×|MéN™§¿1"Ýx§ºöýw SAU놶ókïµYªÏ;Cíí/dühûñ»¿ìµCÜ…Ÿ[îT×¾ÿ* Ëjµï”\ŸTVãÕÖÇG6ØÛŸ©`'÷¯£íÿ~îgb:K> qªkß_5©©F×wüLßc«š_Ü÷ù®O¯Öf£ø>7¶Íîë.âT×ÞßÀ¬ÕíË/éêO͉ÛaeÍhoÿ««ƒÍ¢Ù®œÒ!6ÆXô`¹S]ÿñeÍ 5{èçºú³òv¸£Öï¶_Q©¦^4G\>c÷Wïö]w‘­/:Õµ÷WøÕ*êzÛw“ë“ì¯Æ"뾟±W¼™'ƒ=_Ç·d„»à…åNuíãó»Ú­L{µýþneY×Å*tÖžïf–>5Á4rÚëûºÕÄwöÉ}êúkr[Íÿ+y@O9ÛWǺÙë ›Í7í1‹§°¿ÿs¸²Ž;m³{Nuíã³vXguétpr}’ýõ|È’¬ös³ÉfÑN >7~+’Û¨9ر®½¿¦¿¹ÔІ¶þ:ψSÛ”ýýƒ9âFsr}{þ–;jEWwÚY1Nuíýo¿ÙÊÙ½„¶þ~ð»4Ï7¿xÊ ëÍÑØë[¸Ýu3¹s¿“Û©®½¿§šŒTg/k»>Ù*¼jó?ŒÏR•s™•Ÿ³_FÙ’q®×+¸œêÚ/Á‹›C¦öN®Oª?UÿÝ[æÄŒ¾ß§ûæXe¾wÂþ=Ð] _»x©ªNuíû/ýW¬ïê[?ëÅê¿X•íçf™yyLó®=Äýܘ%Ƭ7‡9յᅨÝ"U«¡{µõ—¶Rc5|Õ@{ûë¶d>sÀ÷þPxJ˜»éG.§ºöý×dØkäÏúæ½'…­ìϓ=î‹=í÷swî÷êÛ®‡8Õµï?Ϲµš¶¸¹¶þžž8G­ë7ÀÞþ¦®OÜ­LûøetÚÕÓøýVS]{•ÍçÕÉÚ¾QmçŨ? ûÎ7g—ªìiØß÷ÓT€1¤_§ºöþ6¤ï¬F÷Õ÷ûíáãTD—½ýCNOp·*n¯×cì 2fåT×ÞßÁÐÓjè½·“ë“ìïë-§ÔœO}×c½_:½{øEû¿ŸñÍÎaî\YâœêÚû»t½‚•ïãÜÉõIõg¥šqÕ*ÓÉžävÌé~±®ïûÝt‡\FlíINuíýí•ôjÐ’@]ý©¢û«ÏVØ×¿¸G<âÛhŸãŒpã|SqªkïoškžŠSúæ?¬øô¢úýŒoafŠúÎ|úªýùÜø¢G˜ëÁí§ºö÷?cÅ}>]ßû{½÷o«©Óíçîevºéc?¯ö]¤q KœS]{CƱRNXš\Ÿd“‹†XYû¼noËéËÝ­GÙóuÜ™nl5Âï™NuíãóÛ´uTÐSÉõIö—é‹Ôj\+{½u÷ì5³Ítû}ë¾X=ÖõZ‡LNuíýYÔ1×öI®Oª?Õ:l…ùå%ûóŸ{|ûsîñ ìïßí;»§¿‹uªkïïøä—´æ?ü0j¹•¾S ½ýƒOUu¿ÛÕ~}ƒt5ÚçíëT×ÞßÐâߨúWw%×'ÙŸ7ÿ¡×ÈÖööGö|Ö½ý)_É ¿•7Ž•ªãT×~|i±'ÄÊ}´{r}RýY½æÌ´^«4ÓÞþa“Ú»o´¯1b¦DíªÎpªëŸúª=´½?¨ݩԾ\?á—ÿr*ÚˆKêT×Þ_…Qjû ÉõIö÷æ×ï«K¿^´·ß?ÿaèÖã°lNuíý-k >J?3¹>ÉþvT^¬Æ†øÖÏòψz%ÈØ÷Q_§ºöþöÿyM-jüLr}’ýÝL¤¢·´·ß?ÿ!öÞ$wúbNuý¯¿5e­lQúÖ/øéØv«p±üööûç?\#ƹŠANuíý-nÙBMHµX[ TT‘]íù¼‰ò&}fÄ4ÍíT×ýnŠ)êØ½³ÉõIö·.ou7£ýù6QþÃÈ·¢ŒŒ/Ôqªkÿ{q]¸š¸Nßû_‘‹Õìžã‹þÃ/[Œz:Õµ÷WüΫêÏ{úÖ¿¶ZÄYOoµÇC¢ü‡Loô4V—Šqªëÿýï«!jvªJÉõIö—%]µá¬ý{W¢ü‡{ǃ]5º†8Õµ÷—ïT/óórú¾ LgnøÉwýªþÃäŸïß wªkïï‹›…Õ×Kµå“¨½EoXibì<‚Dùaæ}£Ù¨lNuíý,µ_ÕÌ¥oý¥Ù®·Õ;ßüfo¿þÃ¥×r‡Ûôtªk?¾(ÑÙÊQï²®þ¬à}{­Bƒíïçå?ÌëÍÑ-Ω®}ÿ­¯µRœ÷™®þÔ‘Á}ÕœìßWå?”Éi,ɑͩ®½¿¼F:5½Œ¾ï'ÊýQE°Ç{âü‡MwV…œêúß|s¨² jr}RýY¹–ª)¾õ?ýóÖ\6V sªkßQãr«ÉZ&×'ÕŸ:wô˜ú¢f”½ýþùk6‡÷>ŽsªkïoãÂMêAsmë3¨W¬¬ßøÞÿüóö/Ž7¶ ¸çT×ÿù!çMÕ¿Sšäú$ûÛ³³¶šõJ {ûýó6½zר2o’S]{;3®VÑÏêû},þÅNêÊXûóV¢ü‡îCÃŒœ£cœêÚ/ÙæMS _Ô÷û{Þ,‡Õê§]öö'ÊXnl[ØÈ©®ÿúä—¢Õi/ië¯Á¬KVºßÚÛŸ(ÿáÛLÆÁ ANuíãs˘jYX‡äú¤úSvtWÛüno¿þÃñfÁ®‚e:Õµ÷—&þª9m½¶ëËÕsïï5Dú®ßI”ÿðÉ5ci‘§ºöþ‡mScµ½¿«ˆÊ½Ôõ‰¾üFÿü‡*©7AC»:Õµ÷×,âUð)}ë÷xóŒWNÚÛïŸÿ05Èe\©àT×Þߑ׬—óWL®Oª?+ {?«ôeßöûç?LÝ´Ü›#©®½¿ùÃÏ«ñÿå¿/ÑþÛR)ZXÅ—”(ÿa]ˆ1ap´S]{9z¬TŒÕ—¿Rb{š”Ú~ž8ÿaK€Qáln§ºöþÆo=­ZfÕ÷ýç¼ã#Ô‡]}ë£øç?lÈ¿ÐØ)Þ©®½¿í%Ò©K_¾èÙŸkª­ií<¥DùKž7NvuªkïoÑ‘¦êì'=´õ÷eûUêçìãa¢ü‡ ‘åõO/tªkïoFñjÀSÚÖQë OP³SØÛïŸÿðIŠžÆìÛÜêÚû›Óh‡Z^0Cr}’ý­ù›Ú}ì½ýþùCvÏ–ÉäT×~~cëX5·Â¬äú¤ú³ ä9¤–\ñ}ÿéŸÿð o°1»L¸S]{ùKÆ©5}òkë¯Ô«UŒø~_ñÏØ=<À8ôÈûÿ=ÿ¡ÀmµØ·éßíOE¤VûÒ¿oo¿þâi¡ÿ0ÿáßÿ¿!9-LÛû»yÿúqsÁO¾ï§ýó†´ö?ïG;Õµï¿)ïÎR'¿ø>¹>©þÔÒú«»ïÚÛïŸÿðÆøÆË½g8Õµ÷ò§©òÔ×—oØoÙYUúU{>}¢ü‡Œ˜n‘Nuíý}³³¦õL}¿ÿ¥®ÚÕÊ_Í>^$ʳr¡Ñ­å}§ºöþ&o[ Úg^Ÿ\ŸT ù‹ì¼¨Äù¿D¹ŸZáT×Þ_–­åµÎ(V;§•â%ßü?ÿü‡“];#OôuªëŸû>P³ ¥ÐÕŸ•îåêjÕ¾ë{üóäh¼;½ûŽS]ûû{ÀkeÔÉíÓ’ë“ìïÙŠž¿ó˜ao¿þÉÞñniæT×¾ÿöœm¥-­ïúù›¥Óy3~íí÷Ï8T¾‘qô[—S]{ §Fß®\Ÿd{J,SË6®°·ß?ÿ!þåFÆåeáNuíýë4KŎΓ\Ÿd?uë®îÝËjo¿þÃäÍ1Fõ¾QNuí¯¿BßôTÑÑ×’ë“êÏ ϧN¾ç;~úç?´¸ëNY0©®½¿ð—½ÛÚú›óÃOÖ+Ê÷ùÏ?ÿ!å–ýny§S]ûø<50XY×B[ErY)ÎÛ¿Ç&Êȼ:ÀõBH&§ºöþ^º{Ôœõ£¾óÏ oÍ3?odŸï%Êؼ=ÜâLO§ºþßjÿ`=õL ¶þÎ|oåkáûüêŸÿ°pP_£M÷»NuíýÍŠSUCOkëo}‰Jêí¾ïÇüóöv w?w¨‘S]ûñ%ë †Õl‹¶õ­üÑ]¬÷j×´·?QþÈ0cJÕ §ºöý÷iýªjXÞèäú¤úS[~š¡fO³×ËH”ÿö•ËeM;§ºöþªŒU±Eô]?ÿVlj+ Øþ<’(ÿ!¶@#"ó0§ºþë['”V ¢'%×'ÕŸ•¡Z µmŸýÉþ–äxÕÊ‘®Š½ýþùYÃG¿·qªkŸW¾ë®vÕ÷ûQº M½,ööûç?<ëzéA¸S]{E®W7Ç껾µZðÛæôÔ¾ã¿þÃÏõî»3ü^Þ©®ÿ]og¥툮þ¬4Ö÷Ö WÊÙÛïŸÿ°þØBcêANuíýÍ Rƒòè›ÿç½^ozœ=:QþÃÕáîA3—;Õµ_ò]˜imȵ:¹>©þþÊ88Ⱦ8qþÃJ1"^ pªkß§ßœ«F<¸“\ŸdÙû©OÓøÖôÏ?$À]ðü.ÿÁ<3Q]y0JWª{ÊcVƬöûu¢ü‡#"?…;Õµ÷w?úªšrYÛõƒV–°µ2—ý}U¢ü‡)ß_s?Õ¯ªS]ûë/²Ë0µãT~mýHÙPÝéjå?Üz*ÈýÔ;•œêÚ÷ßÖyçt^­.Ï´2ô­éŸÿ#cW#¶FG§ºöþå;©Â *mýýÑhƒšWØ—oïŸÿð|ýMƱGòEÿíùçK«õo÷M®Oª?+oþXuº™ïúÿü‡&!‘Fž“לêÚû‹Ú¾BÍr‡iëïhç]jñ`ß÷gþùŸ¤2¾¯ãT×Þß‚ÃS­|]ª$×'ÙßÖœ1V‰ª¾Ïþù¯e*hüQ2Ì©®ýõw3¼‹ZwUÛ÷»Væ°]êBïŒööûç?”/ÑÑ•+S§ºöþT¦ 怪¯&×'ÕŸjóÒ-sô4ßõþùyo»Üú”wªkŸéž)d¥è íû]+pÛ3ÿðù6QþC@¬1²ïsNuíûoÿ™óªÖ–®ÉõIõ§îöͧú¬ö]¿äŸÿðì×Ñî×ÛÅ;Õµï¿J]vY퇇êêÏj|f¦5 }½j¢ü‡,wK‹Æ9յ￯ڿ«F¾•B[雕UŸ}bžL”ÿ°34›»¤o>ó¿=ÿ!lLyu;…¾õ æf«de-lI”ÿþTœ±è—§ºöñ™öÈi5ûüøäú¤ú³ò†LTòØóAç?tw¹ƒÖ:Õµ÷WX¨ë•¾I®O²¿ËW[Oï´Ïgå?ˆÉà~9M¼S]ûøüî³JV¦–7’ë“ì­B/ûæoøç?X禸³wÿ©®ÿ÷±Š?«Ï~Ñ—ï›sÄ]u¬Eœ½ýþù} s§¯ãT×>>k W—¦çN®O²¿ž;ÛXbøŽÿþùßcÔh8鮽¿¿òFéË{ûôíÿ3ÿagÕ@÷S§‡9Õµ÷wþÕõVÎ0}ù”¿-_f Ùno¿þCÕwBÝÏoYâT×ÞßSo]Sçc“ë“ìïÕNË-éàû}Ì?ÿ¡ÌOQ®"9ÂêÚ/%bg˜C"´­Ÿ¥êM 0'–lo¿þÃ+k¹KÜ™äT×¾ÿ2dÖ*Ð.sr}RýY/½×٪ܬˆ½ýþùÏ·0f—rªkßþxEµÊ¦/3mt5|¦÷•(ÿáÕé‘G9Õµï¿&oZ#oê›?ý¬kìωòr¥fD­ºæT×¾ÿîTÛ¥¦ù¾úÛý=^F­›k~u·ªQÁÝ"ÈþýÔý¥Ž; Uy§ºöþ*ÿp\ N¡«?Õ6W„ú³¤‡åž™ï¢{DÄ»¿]fC¶Ä9Õµ÷·!ÿ)5:Ǫäú$û;â>¬æžð­—<2U”»U-{|_†ß5f_ÈæT×ÞßÁ^çÔÐúÞnÍ æ,ñ­Ýãë&îQ}óîÖ›á~Ηßè_×ýK`K+ßó«²v´Ê´µ¯§q/7rjk¯·áNWt¶qhlO§ºþ볂æ«Ái>ÕÕŸúaËPõY-ûüÅ=*û4÷èÔÙ>7Ƨ 0bÚesªkïoÚ¸ùêl&mùqje–«ê·—|ó;^ë>Ï|æ°=_Õ˜ðs¸ëþw3œêÚßÿÜ϶Vá‘?'×'ÕŸU§ß:5u }½ª{MŸîE9íñá®üuˆq a˜S]{C6œ³RnÊž\Ÿd“>«beeþq7»RÒÝ꘽>º;Kµ®ÆXæT×>>¿9qRõÈ­m}L+Ó½ìj\5ûû÷œ÷G˜éøÞ X.Wáû¹êÚû³6.15^ߪZgïg~9Ã>»Ç”ªížü]{»¿ÝÂŒc ²9Õµ÷w¼xõÝÚ~SwS6µÒݵó™Ý#ÞÎãîVß·þÄÇ·ãŒößE:Õµ÷7´yQUQŸäú$û›n~¡zuµç¹#ëÞy¢”ýü…ÈÜFLà5§ºöãKËÔ¯[¹oé;¾ô:òŠõêwöü÷°¡¯¸Ã>³×0b¦‹Ñ©‹c]ÿø|:‡ê qý¥‹¨!+|ß/ùç?´}ÿŽq>UœS]{B?WÛËŒK®O²¿V;;ªKQ…íí÷Ϲ.Äh|b’S]{Ë2Xê£OõÍïØ1v§Û}ƒ½ýþùÛk.4v¾ÙÕ©®½¿¥žV‹ZuÖÖßÍœÔî¯ìωòŽŒrËo±Nuý¯¿MOëͨßÒzåÔ%{ûýóN÷Ž3â*D;Õµ÷·¸Tw5þjHr}’ýíO{V­þ÷¾›þCxÏòÆÁbUêÚû›Óç:Ö½^r}’ý­™ ~hæ;>úç?L>g¤{d~ê¿=ÿaêA5qz¨®þ¬¢eóz×h··ß?ÿá›ÒuŒÍC\Nuíý•ÈÝS=(=V[jÝ*ëiÃþ>,QþÃÓÍKkZÞqªkŸ?¦˜§f^\ŸdY*ïPÓÚ× ùç;{ªº ©§ºöþ¯5ß×·>{Éž§Íu}¿ÿ%ÊX—͈iùÌh¡¾Þ¥o~øž)Ù¬4wšØÛïŸÿ0hâ]ãÓ êÚû™k…ª¹ZßñsvØ|Õ¥‹ïüÌ?ÿálñ(#vNœS]ûñ¥@±ÙÖs§´­Ÿe׳ …Ú柳òümŒìrªkßë6NV#Nè{ýYÜóÑ|ßDùåªDKÞŒrªkï/Ï®?Ô´5úÖ×/·î®ŠläÛþù'~Ëd´¹ëT×>>Ó·RC†7M®Oª?+÷sûÕ”Vö÷í‰òÖv4–ÿØÑ©®}ÿEi¤>É®ïóÑùíÔnÃÞ~ÿü‡õA׌ï9Õµ÷·qznõ ßymý,[õÑï?å?r 3¶½—Á©®½¿E{Ç©þuR%×'ÙßÞ”óÕ¬N¾Ïþù[ËÝ76V pªkïï˶½Ôî_õÍï¸xw°º|˾Þ2QþCëØŽFŽ)uœêÚ/Ù.«…mÊêêÏÊ3··Z]Ö—?âŸÿ:o&ckíh§ºþë“s=¯îîÓ–ßa5غÀJ·Â~¿I”ÿp¦T¬q°p¸S]ûøÜRd—Z¶kCr}Rý© ù‚ÔÁ¥¥íí÷Ïؽ³Ž+pTŒS]{Ò«¡9­]±äú$û{öúdsþ7éìí÷Ïø WOcu·Nuíýù=^øzEr}’ýE´:¯®ô½þüó*N3^äT×Þ_³SùTÁÈÃÉõIö×§Þóʳ¯wN”ÿðÉœŽFüÒ`§ºöþnÌuY/‡j{·<ÿc•þÆ7?Ó?ÿaÆ1ƺ£œêÚû›ß­ŒwNßûû–Éñjás¾óOÿü‡2f2&½âT×Þßs ¦¨÷û軾ÀóùOM“ë“êO-0A ‰õ­ïæŸÿPod6wÆŸcêÚûËòn uòC}ùÅÎu±R˜¾þüó‡ 3FæT×?>½­fµÒöþ`¥}¿ŠZuÖwüðÏH_¦;ul„S]{–ŪS9ô]_ðlµg¬€Ú§íí÷Ï8|¯£;ÍÎÜNuíýíÙ¸ÊJ[¼pr}Rý©e‹[yrØ×+$ʈù¥‘qì‘|®{þÃ÷‡ÕèíúÖWÜ8K-›æûüçŸÿp¡ÂrãÒÍkNuíý›š^ÅÎý@[?Õo©îÝö}~ðϘº·£QûͧºöóëWrÎRÑ¿÷O®Oª?+øG¥NÞõ}?áŸÿÐæå0ãÁÑH§ºöþ³u·²gÒ·þàœ%­­W2ø~÷ÏH5=Îú»X§ºöñy*ÝUuæ}ùÞüÛã}ëÛøç?dø,ØUàô §ºöþ^®»Âœ]Lßñ¥ü/EÌÏoÚë™%ÊXåhærªëÿý¨Ië©tûµõw:¶¥•ï9ßþóÏX4·ªòû§ºöþfOÞ¨ªº4~¿´{–êôA˜½ý‰òn†¹³ŸáT×~|Éšò†Õlï]ýYù×ôΡ¶·ß?ÿay×hcJñ…NuíûïӪϪ¡aÚ>©­­+ªY¿ø~ßôÏô¢;ÓçáNuíýU™TRÛÝ/¹>Éþ:õÔƒ öùB¢ü‡Sƒ³s®ýójS ÖêêÏÊÐo¨Ú6Ú¾"QþC‘B›ŒYŸýßžÿ°-³Z»óJr}’ýyÏ_ÎNó]íŸÿpíóHwÊ—S]ûþÛ÷BõÓmïïêÎ+÷¬Ìõìõ|ç?L[hüårªë¿¾çƒ]ja·|ÉõIöwë—TÌmßõ­þùÜ 2_u9Õµ÷wyÎvµs}y]ýYi Îü?óå-häô­÷ôïÏ7ÕœùúÖϪ»h”Z2Þ¾)qþCáÆýá1Nuíý}R¼µ•ºñ;ÉõIö·¤ÐE+Çõ´ööûç?d©‘Åøþý§ºöñyåz;µ«¥¶|{+Ý€<êVßçwÿü‡Ü—:º^LÛÑ©®½¿"1¹Í1ouI®Oª?U- á£¿g$Êø©X&wFß|®{þÃå«©­tÚòµ­ÔÓwZ/̳·ß?ÿaãKAÆ4CœêÚûû+ÿ¡Œ¶õÏÔ–¨ãjFJ{~{¢ü‡»Ã"Ýa+79Õµ_ò- ·Ö«oþQ‰ÈlÖÁ§|Ç¿|wÊZ1FDÆX§ºþï'6¨¿é[ÿ: l35ï—½ýþùcê6u~)È©®½¿¿òFj{ý©wWg²2–°OH”ÿðËØÆŒÿ¹ü‡ßr)5å°¾ùY~ŽV+~¶÷‰ò&Ž q§¿Õß©®ýõ9ðžÚ¥í÷[kÿ[]Ôí8ßï›þù×›tu?5,ƒS]ûþÛúÆ0u72<¹>©þÔ¥~­Ì íëå?äne)PЩ®½¿˜Ãï«°vúÖ÷þó©`q×çŸÿðBÅpãÈ(§ºöñ™úÐ(µ~ª¶ùïVÞêtEßúKþùõ¾1rìˆtªkïoÇëéÔ¬©ú¾?û+ÿ¡‡¯¿DùRÇøåkNuíý-øì˜•¯³¶õ_¬-·Y%ŽÚóMå?jSßøõóX§ºö×ßÍýÕºÛúòµŸÞÐSÿv€½ýþùU>žázéÃ8§ºþ÷÷ׯšaÍrêêOµÉÑÔ />ÛÞ~ÿü‡ç÷Ä»3•ŒvªkŸi/ß· I®Oª?+°|)ë¹³¾ã§þCêÂáÆè29Õµï¿ý§R«Z³^L®Oª?õãKÿ·3Ë1ûÿÿö(K eI– ÕÄdpŸë~ŸŒ¥±dÍCƒa,ÙÆz#‰d+©´Ù’ÈEŠ„le)k¶,“5[adû}Ì÷1ç2×õ8s]Ïï=Ÿ?o¯Ç»Çy:×~–§¦»²ë…Ìÿ`S®'êy9zÿuðøŽÜ袶^‰öÉMçaßdþ«ô$ÅÛ„—£÷ßïk_âújGæYâó—Ôÿp$=CpõÏàåè|úZx²áW,>ˆ{õšVß-ú¤þ‡*¹ž$q“†—ã__ÒfAì´hµõJ|´Þ¾)®gßSeþï% BËQe¼¯Y—Eð g+µõŠ|]~6¡V‘¢¿Qêpí/8Ùºðrôãó…‹?µì1Um½"_õ†´©Ží'ó?t l}Óx9:ß«Ô<ؘw}©}¥=V¿ÿIýó¶Y æ7¦.µÅùƒRÿi_!Ø}ÍËÑù*¸7þü¾fá:jâ!úc¤þNZm‹¥þ¼ýúâæê® ÜŒ7?¹ÇvÝê*¢?MêhnT"¸[…òrôþ3ï@6Ã[ß߸‰íØÍ'“ùêT%1Í=x9zÿÝ®j CŸ£­£¦“*`ñ--k¿ÔÿÐ4N+ô+æåèý×çƒ]²m}8ࢡÑãÅýç¥þÛvÈ¡+¼½ÿJ5¥9 ïúiåø=¤µ÷÷î·õ©0,rã_¿Éè>^¤âd/GçëØÙ.7¦¶^‰F„·úâÀeí_÷Ç9!4Œýÿ‘¨ÙDÿ« /GçK¿e!!CÔÖ+ò»hñ1âz‡…?C¾=ÄøNÈ'qÚ^ŽÎ—çømÉFã{²µÄ ߇¦¥®Ÿôc|ϧ8 uj—òrüñéš}P¿î@ÛöÄÚï1¼ŽÐØH߬æäF *åórüõ9-aA@g,>(?kué¬ýKfìôeßÈJª!E~N¼/Òa9ÙžR[¯È·c[?øà!>_מk¬3¬ÄüCd“i4©œ^ÄËÑïäü$Ýè­¶^‰þ¸Þ"C™?MØÛý¶pI\ÐiA*)÷‘æè|‹*­§•¶>R[¯ÈöÇjݵ¿÷ªaÄiñûDÍ¥~$d»5/G?>Ÿ-?“üÕÖ+òYÍ1€å—D¿Óú%O„ŠÈÿúM¹h=ãCy9:Ÿ×·æº½7~P[¯ÄÃjtG'2ÿŸÒwÀr’[9…†æórt¾Â´lxÙN‹Æ÷*)Œš7bûÁ AÝ/ƒ.2¾ù[¬‰_ûR^ŽÎ·H?zžtR[¯È•³.0ÿŸ°Ë²«ÙB|Þv|žO.M°ãåè×—A+¯S»<4?:rˇ¶˜!Ž7$,ØÏÆËH¾{ YÝŸ—ãŸW`ä4¼÷÷²ÀºXEß”ùÊ Éíìx9:_{[8Üo|shèð/Î/Ö~©ÿ!xX ñëáÇËÑù¶{ãúÝ€ýÙóˆÌÿpd¨79v²‚—£ó©Q[ÚÆ¨­Wä{Ô%Ž¿åû.­rLV•ñrt¾‹SjPë•x~‹×‡©óÚv¬ýRÿÃʼnäf–/GçKšÔVž˜Æw*ú¤Žg>V™ÿa­w!9•ÀËÑùb÷\‡‚Ýxëß÷õ7‚WæâþúRÿÃò}©¤Š_*/G¿ÿ5þ¦VÆ›ÿòM¿ëÇæcÈüo,ÊȱGa¼Ïõû_ás£ŸÐøèÔ.ÔªŒÍw”ùªuó'{¯†òrôã³,ó)ÄL7V[¯ÈWmlGØ¿Ÿ]ï¥~mÅ3_mÛIE¼¯AŽF—”g¤¶^‰ÜÇëÒÄýÁ¤þ‡µÑaäTP/GçÛyã<Ù¶Im½"߉P jξ/ÉüúïÆ“W¢y9:ßÛþÐ5Ú/Ö¡'ŒÛÈž¯eþ‡kÓýÉ^Ž~}i8i&µ©Ø¥¶^‰ºýz–6ý1µ_êØnjI‚µÙ¼½ÿö=íK&Çâƒóù“ .C|ùœµ$)!——£óÙßñÈuŸÔÖ+ò}w.ö¼®ÇÚ/õ?\(ó?ìéFR_åòrôþ˾pÂ÷¢­Ÿþ?ÿC[#ó?¤-Ï dGórt¾Çö§B´þƒËÝ h«"ŸÔÿp~™59øô&/GçÛR sú!ú–ê!ÆLÜŸHêØ_͉lÍËÑùrF›C®žývópo(óÙÊükû“†æ¡¼ýúbÜëà=Ö«Üö9ÆÚ/ñ;÷³If„/ÇŸŸì3Ê{U[¯È×ó;5»·žµ_ê(zJòÏ–ñrôã3sT"lJ°øàÖºï!/Hœ_.ó?œÈÖ6(¶äåè|&ßGè"ï¡í/ µvµÑmÎæËÊü³·Ù‘=³ô¼oYAcÈ#£ÔÖ+ò%¬X‹þM©ÿ¡S[7Òüš5/Gçð!œrðÖ¿O»ù=)ýYûeþ‡ü\R§çåè|ÛÌ¡M* P[¯È÷9jmkÂæ'Éüá¾d™©/GçÛTu8¬(Àûþy°g'øzÿd©ÿÁ=LOVw1áåè|6áßÃôæh|®g~€ÕE?ÔÿÐë7Ònh6/Gç[Yt OÃz¡ü&°ñM™ÿ!½½–ìïÂËÑù[ÇBB´ñ?¸¾÷6dÕ÷w‘ú’~J%ç¦:ñrüçëüÃpýr²ÚzE¾œz>ðAûk¿Ôÿ°¿a6IÛ—ÀËÑù¢#aîƒîjëùÒ‡¬€¸¿Ôÿ–[D6gðrt¾¸š7!Åd:ß¾Ï!÷œ8¾)õ?̯éKlúùòrôçë?ýýý°ø¨cž l³aûqÉü Ž&±ûJy9:ŸãðlØ{m­ÚzE>÷“ ®ø~+õ?]•JÎñrüëËÿƒÕR,>8k'°ý5dþ‡í–Z—iZ^ŽÍ§+k§‹ô@{ÿÓ½¾L·ù˜ø[êXp¹”dT ãåèýQ/\*P[¯Ä÷eï3xuŸ=ïÉü=h>i<7•—£óù^ öC}ÕÖ+òÍjÛÿüðy…èú®vM!·•òrüù­.Ã`óî µõJ|úƒxý”ú¾ù %/ƒ y9úùW;´ìÛ¯¶^‘ϽJ(\Ã~Ëü¿ëüÉD^ŽÞ'ËÁëMoÕÖ+ñÁÓÜIÔª½oÉüÆãc'O^Ž?¿§æØ|÷!ß“Üd8ÛQü~+õ?|h@Îwâåè|w}ôp4ÎRm½5º¹î7ûGêÈu±#Îû£y9úù×ñÊLˆëþ-ß>Ø6s=k¿Ôÿ°ÀÞ‰<½“ÊËÑùÖ¬ÜKáí_¾õw;jÛék¿ÔÿPsé R.ú^þuÿÃ}j Ç$ ós²y9úõ¥Á¾9t¿Í,>êzÜ“æ Ǥþƒ·­Éfñ{Á¿ï(œÁ/ï¢ñ}ñ?l¸Àö[ùB®ú ;4çåè|ºå#á^.Úþ»0AÐÐÊËÄýù¤þ‡—µýH|;^ŽÎ÷¾F)DÄ¡}ÿ¤Õ† …óØ~”rÿC²“`Ñ"”—£Ÿ©ÍÑYhãGôt-=™ÿáñ Á|†//G↓ó¡,ÛBm½Ü9•K«úŠïwRÿƒ}Äxrµ´/GçË?•óv4Q[¯È÷i&¬Ï×Iý&ÞäÒš ^Ž~|íéí'©­Wâûsÿä«Ñâø¬Ôÿг<•Øt¶äåè|GZƒ˜^óÑø.Ì/…¤ªV¬ýRÿCÄÜTò¦?/GçKl[NÌÄówd̦®Xû¥þ‡ýcÉõ¼ýü{Ô`¤½=©¶^‘¯jßÑpó{_•ù:ôñÓÖýêûÌ¿î0ÓèæLS[¯Ä~±ßè–U÷gúê[•×ùòrôãÓ4¹Œj^ X|Ôaðjsœï2ÿƒuYVàÅËÑûïL·›ÐmP¸Úz%>(ÛP¦éÊÚ/õ?T«*´¬šÁËÑû¯Ãª#täÜBµõJ|´u:7\ô£KýU['“ÄÖ^¼ÿûDV,™Š7ÿÅìP8l'úW¤þ‡c§3wq¼ïß÷?ø¬„']ðüñÖhõl¾±ÜÿÐÖ‡l‰ àåø×—‚¹›~‹Ö;ØÒ2߬ÌÿгžÐ¢¢'/ÇŸ?±¡”Dá}?ëâT@­Zþƒÿá²Npî˜ÀËÑϩԲš?‡Vy‘6fóÅeþ¯¾vB-§¼ïÕíq°ñ™ÚzE¾Úã+AÁRqþ’Ôÿ0{v‘P9à&/G?>»“,¸3oü!`_'j¢wdí—ú^Œð&ODórt¾þáù°c-Þ÷ù±ƒgÁ!Wq|SêÈ,̺eðrt¾›óií}x~í÷á]i+6ßDæ ]„º./x9:_eÏKp}šÿ6Ûv‘šØv`í—úÚ9¹h‡äórôë‹kž¹.p-š?z¬ÖèVbïs2ÿC“çn·ö^¼½ÿ, §Ó†5 °øh£WhÇ¡âûŸÌÿpµlhªáåèýwëw€!w|ÕÖ+ò™Î^Á^ÇYû¥þ‡–öz¡ÿµ|^ŽÞ}6®¢K¶£¯Ð ÁÆ4z ›/$ó?ÔéFŽÛ»ñrôþ{úãÌ/{˜£ñYÕ½øµïRæ2]<[œ¿;¬c.ùøÙ—£óuÔÕ…KyÑjë•ø`xãÇðq6[/)Dì:%,RÌøâîhÉÂi¼/ý¥Õÿpþ˜+$Ô*gí<½QðÓ±ù®äd–?‰YèÅËÑùòúŽ Ë•jëùž¤@Üqÿõ)ß<‚=™o¼| Ô^ÂÍñç¿w§ ãù) ž£mÛD²öß2Zp.бߕ%yqa¼~V»7°à¥”½2„ Ä÷½åæ{…Ù¿±ë) I÷#Å=y9:_dIp5¥¦ÚzE¾kÃû=ìyYWg¨£®j#GÆ·á¥^û9¹„—£ßÿÈæFšŠ6¿Žþègkw±ý(…½'2…MýÅû_'S 9»FËËÑù™¢•¢«­Wä +Ikd±ù!B?«$ax¶ŒPÃv!Yvù2/G?>Ÿ­k“Å{¿ýâwZÑN¼¾l˜£’`äø µõJ|PþÆ>ïRÿÃÀm%äjS_^ŽÎçù0 k@m½"ßà'«¡8Wœ&õ?„\/%½\½x9:ßönÅ0+d£ÚzE¾§°¼»ŸÊü‡©9=¥—ão®€Ä7ïÕÖ+ò= ÔBn”è·ú®lILkŒçåè|+M¢ÖaEjëù^ïÝDw°ë½Ìÿp³K¹jáÉËÑù’ŠÏÂÊ·±jëùÎL{ˆ~T©ÿaùyrêU1/Gç‹q ÒðÖ¯ìM¯à1k¿Ôÿø!ŸXœ,âåè÷¿F;{Áêvx÷¿VÖÕ!¶»ËüåF–äH@./Gçsí¶>CÛšÒAE[$õ?T}êMöÖöãåèÇgÙï³Ï^mÂŽò?J+´Íhy9:ŸƒS3]RE¾Úz%>p×kti½Äû·Ôÿþ› 9íåÄËÑùvkOžn¼ýø4æé{ÕÖ+ñQ;]¬é®eí—ùJíÈîñÖ¼½ÿ²ïŽ‚ðLwµõJ|p³é$Ø5•ÍÇùÒiHÅ\K^ŽÎw`D<|6Áó]ÖØRëZ-Xûeþ‡±Åä°‹/GçÛò.æäØ¡ñýéÈfOæ881”N÷áåè|9.sàxJ/4¾ÛÓ­áÞz6~+ó?ü’àFl}ÿ‡þ‡ËwÑý_ïï&ñ;†½üIN_o^Ž??yÇ[(Ïj§¶^‘¯·ÛNj*>ÊüY¾$OgÍËÑÏÌÎÓ`{:ÚûÜšZò6ˆëW¤þ‡ õ´õÜx9:Ÿqa¾.²ék4>›ævºM!SXû¥þ‡)¡NdïÚ^ŽÎZÙòà}ÿLpj W²ß2ÿC»ÉÙ¤ÉÄ2^ŽÎ7°ÎIpz¼oê“4Ð#¬ýRÿC䨙äñ"^ŽÎ÷p`=ÚD‹7?YSs6mÅÖ3ÉüküÈò_‚x9:ߦœû°â;´õYçlþí6k¿ÔÿðÝ´|²|àQ^ŽÎg“VÓ·áŸn»Âjkæs—ùúõ²$îþ©¼oeLsìVm½"ßzßy0#B<¿¤þ‡ŒÉ’WÊËÑù;N‚ÇHµõŠ|7t2 Äñ©ÿaëË™¤`º%/Ǿ¾}®¯óCãËæ°ùs2ÿCZ`>Ù×ñ /G狾zænÇ;ÿ¾øÖ%5fí—ú"Wd ö¡¼/6ólߪ¶^‘o_Øà¿ùÑ¥þ‡}íˆÕ;^Žþ|m»Úâ¿Ã[¿â˜ ÉCÅþ“ú>>Вزh^Žÿ}pëfØ›{Lm½"ß·óÒ ŠxúŽy‘3«+x9þõÅd6lmƒçw:¾ÓNiØýLæØq¤Tûí[_^ŽÍ§+>m¤‹ü©µÚz%>݇ڽt‰Ñ Ùo©ÿaaƒ ²ÿy/Gï¿÷£p鎵Úz%>HÞ½^åŠãcRÿƒoÿDâààÏËÑù|õÇÀ~f¢ÚzE¾ÙqÓà?ç k¿Ôÿ_§‚œãÆËñç~ñ?tÿAm½5̨BŠõý–ù–>±$“šsst¾ðÓW`Äf¼ý[“ç­‚Àfâó¥ÔÿÐyP…PÃ$—£óU»°.-Á[ðÍFÔ`8?Dê(¬íGBRSy9þñé 1sƒÔÖ+ñQÓݰ§·xÿ–ú̧ø&Cx9úý]S3.Ç ­§µ–WPM÷«¬ýRÿÃ9 ÁÄד—£÷߉%‰Ôäí,>xðØÖ}-ŸRÿÃy=)ü¹ˆ—£ó%fg@HÂz4¾Þ¹°=Iœ?!õ?ÜIK!w–çåè|•/@ះïuÏÁðf»ÞËü‘k5Äû]4/G?ÿœ;9@î€jë•ø¨{ó§_û\dþ‡a+½Éç…¡¼/´[Z³mµõŠ|q9+¨³ •û<ÃÓæÙ¼ýø¼ÒåW¸V¡EãÓL½E+ÕýGRÿCå‡>Zû>¾¼¯ñ-c]¬oüÖ3£·nkz{Ö~™ÿÁ£X0rtàåøãG•ëR‹<´ý³àZÀÚ`¯‘ù6¿±$ƒnZórüï/gç€×D<¾ôÌç0úŒ8þ,õ?œ« ÔÈËѯ/5 Ü耳§±øhÃVµé„Ïâ÷©ÿaGõ™$b\>/Gï¿õÖ¨þ‡Ì§g¿ÌAcí—úæº ÕVÙñrt¾NqeP`b¤¶^‘ïçþVðÙMôÃJýW|H\sk^Ž?¿µ 6ŸÉÀ⣙SàÐqþ€ÔÿЦvy÷òè˜üöí6T[¯Èç^?nÊcí—ú÷Ô’O·‹y9zÿ\° ^âùÕJÔýGÿƒÑ/CÈç¼~ÏŒcØm~<)êg[²ý ¤~As0‚Êçåè|wÊ E›_@m^Àý°Ö~©ÿáäœ2âØ?š—£ŸG»@\ÌqµõŠ|?Ö0‚mmÅýù¤þ‡ òÊQËËÑùÖ,0£F±hþwš`FmƒÙ~2ÿƒõ/aäz…¼ýø¼·æ>ë€çÏ1+<O*‰ûGJýõ+•hùòrt¾V1zݲfÕÖ+ñAçîåº(#ñùSêxùÄ_¨¼0€—ã÷_çÔ, mü-u õý²öKýé-Èš×)¼/~ç)XÐr>dþ QÑl¾±Ìÿðâ~‘0?·Œ—£__$£û°ø¨ëìÍ4o‚øý]êÐØ¥ þ‡þ‡ÅÖõ_ÿ=Yÿ}^16X.cí—ú–4Kïwãåè|P]÷†áÍùõ™­<Ž}o—ùÞz–’¸H-/Gç{ßð8D\k®¶^‰V»óv&°ý—dþ‡5‹Ê³¢T^Ž~þ¥ÒÝp$ä½ÚzE¾ÓâáiOñþ õ?<¨Ì_Yórôþ˪¥‡òêZ,>¸k8›Vïo2ÿCr,¹òá(/GçË_9æ=,S[¯È÷iQ ¬Oýƒµ_ê°ûäD „ñrôãÓ¨ðWHwC[ÿðçú€«=ô¬ýRÿCïR÷ÝÿÎÿ}ã Ä44@ã»ðãgHê5µ_ê;âEžçºñrt¾Äö´AÞõ%«·1u)¾ßJýÍßä’·Çìx9úù÷Èg0¤7Aô?øuøGÿCû1z­c3_^ŽÎ§KtÐÍÙ‚7ÿÚïYŒ.d,›Ï&ó?Øð,:zórôãÓônÕÜÃ{p¨Ñ€ÚÌaë¡eþö?‘Å«Âx9zÿ¾‘Ý~Ú¬¶^‰Ên™ÀÔ#l½Ìÿ`Ó°Bh9Ï“—£÷_‡.™täB;µõJ|´Ïa7:o»^ÉüV!ù$9ÜŸ—£÷ßïƒ|!øøxµõŠ|fe‹þÑÿpÊÚRðXáËËÑùôc¿ƒÇïö`ñAü%7ZÃP\+õ?TîçD²Âx9þõå~ÄîŠÅGë%Û@Zû-ó?ô ·ZEùñrt¾fßþ¼«­WäëR>ŽZ=[ò×o™ÿ¡Ñb­Ð8ÈŽ—£ŸÏßPKï\µõŠ|ÕC*Ó¦•Øx”Üÿð$U°égÈËÑù^}Š„+Fª­Wä«îðþýûÓ‚©ƒ–—£ŸÝÍ›Á¼ûÔþW©ñC6Þ%ó?¼âDºì/ãåè|ý.œ†1x~µ±MªÃ¡¡âýOê8 µtãåè|7Ì¡µ· P[¯È÷ÁºuùQËÚ/õ?&>BoÆórt¾Ê?Þ€ë/«­Wäk6{÷ߎO©ÿÁ­]¡Öés4/G¿¾¸fïÖÞi£¶^‰~<²Z·ª”í+ó?8' ®Ûgòrôþ³°°§ [Çâ£Gž¥ëˆßdþ‡³$6¥”—£÷ß­B_òéªÚzE>ÓI°xCk¿ÔÿÐ|l©0ؼ„—£÷_ßCè’3wÑø&d× Ñ’Ôÿ`›¾‹dÊçåèý÷tÁˆlƒçdz|Sö cû‰ ƒ§¥ ÃΊû‡ŒpË ãfòrt¾Ž#|à²ÞþóÃ3FÃÇæìþ'¬Kr–^gãû$ªÌ„,´tàåè|ûÝ/CȺjëùÎ çþîûVœ.î¯xªƒžÄ=÷çåè|yd,*mª¶^‘ïOÿCŽ8ßeêh"ïoÅø^\×¶¥E¼ïî7þ´AX|ÿç¨"~ðŒ‰œ¾òuTyZœ}“ÂËÑùN¼ú„áíRöÁàoþ‡Eãæ K4âú¿ð_¼É¹–¼oí9W(úo}êŽÏo "A·µÌÑY,®Ïø6Ï&FƉ¼ýþGö<‚еþjë•øèëÍaí6ßXH;5]HìÌžo…FN$on6/Gç[T¯­TX®¶^‘/ìÎlZ#_|^é{½ž0ºäû]£µ ÷׿èÇç³àµ0ùž¿ê‹ßiù!v¿6YnÓY>aó ˆýK/m“&^ŽÎçU«•nß¼ý[‡FßÑåŒdûa KK¯ «×ˆó[sõ&—2íx9:_á¼™ð"o~Ö+}5ûÄæëÁß^&˜±÷ 2o›žŒüf&/Gç[´$z®ÃÛ_1ja˜"°ùzBêûÂá=GØïúÓRÈ…Z^Ž~}´?…ÚmÄÛsʬv´ù6?Y¬sXÐ/c¾tra¤ùùf*/Gï¿‹•&ÁÈ>xþ£ò [¯÷2ÿà%näfJ1/Gçkïꇞ>U[¯È7d^"ÜY)~‘úŸ.$}k¹ðrt¾íkÆÂ¬ãxûÏɯ ˃ÙûŸÌÿ3Ë›]íÅËÑùÎ<[ [nvU[¯È÷¸×YÈÇî2ÿÃ¥ Á¸³†—ãŸóRëÄl4¾×7r©óoìýAæ¸|NK®Ô0áåè|I¥ù°Ê¯ÿNÏýö$Õeí—ú¢KÝÈñ|_^ŽÎ ã À·®ÚzE¾}¿Í†—ûž*ó?,?QB,²ô¼ýþ×hæMXÝemµaÄ:°ù¸2ÿÃÛ3eäXe^ŽÎçJoçkÏÔÖ+òÑõg©U»ÊüÕÆëHš®”—£ŸeŸæCÌ~[µõŠ|ÕVúÀWö<$õ;h+6hÝGñrt>‡j»t[4W[¯Än›‡ëÒýÅù-RÿÚ¶aäøWûþëþ‡1«àIž_íD†–k÷³öKýsæ8‘^'5¼o‰çoÐ57@m½"_¬™~ åúÈ•NÞ¤À×£__¨Íy´õÔÍÕ6íОµ_êØúÍx’ãÇËÑû/í›|Þ†ö~çã=¾öûÊüž?jIr/Gç³Ï«‘íñæ/}÷°;ìyʾ_Ëü‡’á;µ¼ýø4ïy“±øhç3 âWqþ ÔÿÖ´‚ì´Hååèý—žk:¬U[¯Ä7-~‡]kÅõRÿþïJÉ;‹^ŽÎw`| |6Çû¾téÐ*ZãÝAÖ~©ÿ!ÿS49ø²ˆ—£ómÙ±æ4Ö£ñ¸5 Ö½°`í—úÒÁd*æåè|9sÀñS“ÔÖ+òÝvÔÀ½¶âú~©ÿaŒ‹i|8€—£__¬g …ͧО_¨}¸ãßÞÿ¤þƒ˜P’½Ì—ãÏOÞVÊ[ëÑøz= þÚï+ó?\;äEòšðrôã3sÆmØ€7þwkn4ä­ý°Rÿé¡ZÇn¼ÏdÈP]”Úü,°i` Û¼‘ùödþ‡ Ù$¥·/Gç[ågž OC‚ñQx ÷“úH%i‘‘ÍËÑù,,§ßÐÆ7aZvÅõ©2ÿÃkrÿû"^ŽÎ÷°_uÚIJ“Úz%>ª©ŸA=î²ß2ÿCD Y¾|›3`ºãnµõŠ|®cçÃêìïÉü¾¥¾¤-)âåè|«ê†Á;ðöÏÚàÑ~ë©gí—ú2g‘ý½,y9:ßẾ\óÐøn¬Ðý£ÿaG¥"rÉ®˜—ã?_ïÖÂõ4KµõŠ|9wöÀÇJZÖ~©ÿ!#Ë—¤/ðâåè|ÑS}`îƒîjëùÒ7<‚Ö~©ÿ!ü'7²~® /Ǹ»¶ïé­¶^‘o_ [8þŒï˜ß[KìnyórôçkÛèßê“Úz%>ê¸Ï ¶ý ŽOKü‚æ'‰ãÅËÑù‡½QÕÑøþô?¸³ýDeþ‡ÜÅ©äìùD^Ž~|&¾<[}Ðö§ýâ&SÙxºÌÿ°ó[;­ëÓl^ŽÍ§+Þo¬‹‰æåèç_Óepüî,>ê>þ(\þ(®O•úF]Œ4?•ñrt¾Pá-µ~öFm½"_|Û0Úô“øýSê0Õ L³ž¸9 ‹:>|C}ÆÖóÊü»ûkHظ^ŽÞë÷L†E?[¨­W⃬Ñ3Qü~&õ?Ì8œ+T^ŽÎ×iEW¸~Jm½"ŸÿxŸòÅûŸÔÿp騞Ägñrüù­‹‹`óï³°ø¨Åt78T$îÿ,õ?´lïEÞ˜gðrôó¯NeØ™ÆçVpÃBÜ¿Gæˆ×’ŠÛ~¼½ÿNô„×»çcñAéH{j5Yœ?!õ?˜ì²*]ÈËÑùŽÒ“ówÊx9:ß]»ptôc,>jtµ7ܯҎµ_êÈâDZ|Läåèç_G³u×ÏKm½"Ÿ÷’°-W\?&õ?Ž+!ON8ñrt¾5 ë¨QŸ_ÔÖ+ò%÷Ч6‡ÙûŽÜÿéCÞOöãåèÇç}§³p¬šÿèËÜ,x2Ÿ—ÈüõB¼´uŠx9:_«mtËðÆÿ¾ø"7ÿÄÚ/ó?,Ò UJBy9:ß½Fݩټý5Œ&Ö¤õCÅõ+RÿCšÎ‰„]óãåè|ñIî0ÿ1Þü³Ì†{àk‡ÔÿP:£H˜qП—£__œ~AÓolÅ⣮†Ó3ÝÄñg‰ß|Ú™M6Ý÷äåèýwÅaêú‡OO.À† ì}@æX1õÐÔГ—£óé:õ‚{£ñü”k¶¢•óDÿÔÿP6Á‡ÄoÉæåè|ï7Âõ?\±…güØß—úÂïg •§vàåèç_jߣp$ÜGm½"ßé¨ùPê̾7Êü¥¡ÿ¹?Ü~ÌËÑû/Ëù”½n¤¶^‰îzݤU?²ñD¹ÿav¹0-™—£óåǥ¼÷xó³¾øö³ù&2ÿƒ#Ø‘ _­ïø×ýÁó ý×ûjë•øh½ƒàê÷ìyAæè3BKZÝðååè|ÙNAŒÞóç…ðiðµßWæx•KÊí|y9:_¢» m\€Æ÷§ÿ!úk¿Ìÿp6—¼Û›ÀËÑÏ¿G]]!Ýo©ª}Gíâþ RÿCÛâ\m£lK^Žÿ覛w0Mm½øíï­[–tƒµ_ê°–-XÄÏäåèǧiöª¹†7>æ@·Q›âñ)õ?5¶#K&ky9zÿž› ݦ_À⃲Óf0í—E¬ýRÿCÍ­Á¹—ž—£÷_‡!®t¤€7©ÏÚh:wÏ—ùª˜H¶órôþû½F ?_ŠÆgî¬ÿGÿÃÑÁÑ‚ëš ^ŽÎ§7裶^‰â^½¦Õ¡k¿ÔÿPesÙ°6Ÿ—ã__r_C\ÕËX|´^v¤OgÏŸ2ÿƒÏØD¡Å„^ŽÎ׬¥ ”¼G£]ݳ©Ugƒ¿~ËüMvš¸Yòrüùg7®P˺xû³W/¨Bž°õ 2ÿCç¡Ö9¼ïOÿà ¼ùµê  ѵ_ê˜/œ,N¸ðrôã³ÛÉ¥pç§*jëù¦¸Œ¡&~âü©ÿᥙ%éïÍËÑùúÞ€ÐÖ‡Ó1Is ë£øýZê8VZ,;9ðrt¾›Ñ=híÑÓÕÖ+ò}6“ºxˆãcRÿƒWUoÁ. ‚—£óU¹®Ÿ>‹Æ×¼_51ç?JýíK|µß„…òrôë‹›õS]`6ÞþŠ='<Ö­ž!ŽŸHýM† íh!/Gï?ó•O©ãaOµõJ|´QÚcÚq©¸?ŠÔÿ`?>Œl´Èçåèýw«Ü†ÌþAm½"Ÿé“vÜVœ¿+õ?4]ç+ ¸ìÏËÑû¯¯E]²,Hm½"ß„ö-®“ùì×’Ã6¼½ÿžvó‡È¦¯Ñø¬bÇAš!ÛïYð–" v`ëÈȰ"òé×^ŽÎ×1².Cë?ѳ&|\ɾo 1[l„åÓÄëg”“5™—UÂËÑùöO ƒ´ùp~hÄç‹ûC$ 3cßÇÈ©¹$VÜBš£óåu\ ‹>BãûÓÿ°ZœO0Ã¥‰Ô&„ñ•Ó vGMx9:ßë´§Nm½5Ô¿ mGg²ö{ÍšÎîË~[~ëKÎÚÚñrüùY‹üaÁY¼ïƒå‰5`c%6_A-Ù)æ‹óÏ–Uñ%—Þ[órt¾ÈEGáZám4¾?ï†÷:Ö?º§Òtuá ãKîV¢}ÒÒŸ—£ßÿÈzWƒ÷üòãsˆØÎ¾§ {ßö¶Î¿v<íDNþRÆËÑṳ̀•²zª­Wä {DiQ‡Yûûæ Ãz‹>¤º{$ô“ /G?>ŸEšÃäÖcÔÖ+òY9%ÀŠÎYû·ìí¼ý&u²¼µv/²y9:Ÿ×¸<ݾ[Ejë•ø`èd?Ý‘wl¾‰°¤}+!üÅ<Æwro)˜íÉËÑù …1ðâìNµõŠ|¯âÖQ³kì{‡°xËb!à ƧïäE† Jàåè|‹ÆøBÏ!¯ÔÖ+òEt€`÷Sa÷‹!s³øýÓaR"¹äÊËѯ/ƒ–к&xû/M©H¢-v0Ÿ‹ôð°ÀÅê¯ß$zåœÊËÑûïbÈ(ŽçïørlÃÞ'eþ‡¡5¤h_/GçóüüEãÍŸ²o)ç2_”Ìÿ°ÄÆ‹ôª¦áåè|ÛóÖÁ¬xïG9íl`¹‹¸~Eê8¼Ð“ä&ýïüyÍ—@’ ÞúÔǶpÂ!†µ_ê(ȰŒ{çóòãü£Ö³ðüG¯gL§Îâ÷Z™ÿ¡¨~¹^ˉ—£ó%õo+ ç£ñþå-¤Z±ñ ™ÿaErz§'/ÇŸ¿Û»,ŠW[¯È·/¥¼jÃ~Ëü!ßE+G^Ž~ÿkäº V•¢Íÿ¤­¬«Ù£µ_êx¨%Çêpst>×ëÍáSD‰ÚzE>ÚwÞ—oh¬ýRÿƒÕ7z²§w*/G?>ËGm‚˜øªjëùªíûg½gí—úÞÿ”¨mçÆËÑù<ê§Kz⦶^‰ÜBèÒuâþKRÿú¼rªi4/GçÛ9'žLÂ[ßqâÒ(j<ák¿Ôÿ0«Y 8ÆŽ—£ó-ðºŽÂÛ¿<æ7TËÚ/õ?Üvדs™‰¼ýúÒpnmjë‹ÖÔí™+mº†Ï2ÿÃ}( ú¾‚—£÷_ZÈzX2q8œ{žqcØ÷ ™ÿáÛÛedKŠ?/Gç³~"“¢ñµË»{®³ñ|™ÿ¡`£7ñ¦ååøã›ú~°Èl”Úz%>Zçumˆ°eó¡äþ‡ý dG¥\^ŽÞÙ)aÞüÈ›ž³a×H-k¿Ôÿ°go ùœêÇËÑùôƒÏöh~™?ýÖÁ Yû¥þ‡¼Îr`ÕJ^ŽÎ·åÃH˜]f¨¶^‘ïăٰn†–µ_ê88ç?|–¼/Çê ‚æ÷…Û¾Ãàîcq7©ÿaÂ7bÛ°ˆ—£__¬S ±ÉCµõJ|´žñ4Ø]¾žµ_ê0,šIœååøó“£–Ay2ž?§·Ûί¿ïÊüW}<É©@o^Ž~|f¶4†íéajë•øàæ;ÈsgßSeþ‡œ¥EÚºƒ½y9:ŸÉÏîºÈ(¼ïgµ¢-u›2Ùø­ÌÿðÛ Ù•áÆËÑù–E\Âõ?Ô·„Áâý[êètÉ‹4ýÊÿ÷¯ûb Ái…·ÚzE¾iN Mgã52ÿClF1yº)€—£ó=Õƒ6郶¾‘jÚö<[O!÷?´O–µ·æåè|›ß„‘xãï?GÀæâû«ÔÿàÖ~Ç'ŠûóIý z“š­,y9úóµ- ñµÐü+Ôq¢$oåúÈ?§’¸­…¼Ïqm2ì]ï€ÆçþùäÿÂæ¿Èü‡ßÙ‘3g=y9þõŪ$Uào·l' E¿ŠÔÿ°éÖcc/ÇæÓŸ7Ô!~ÔUDÕ%–:³ßRÿCàêT’Q%Œ—£÷_„½.àù™“ [«ûâü ©ÿa@iihÉËÑù|½:½%Úú1˜ÝâWp'î?!ó?Lt#YËy9þüÁðW´zrµõJ|Ôðp‡ô?„ÆY“©mLx9:_øô—0¢Úø&$Gn‚ÀÎâýAêè::@¨zè#/Gç«vý\ ÂóöžÝŠ|Çæ ÈüyýHˆ¸Ñ¿îxöö ÄÖ<‰ÅGMg/ü›_[ê0P$fØñrôû»ÆÙ® Â[?]«ÉïT#D±öKýùv~‚áÛñ¼}@ÃÞÔô›@µõJ|ð°Õ$Z÷¬¸~Xê8oVD.Ž ååøÏ/•“ $ m8jÛOëYû¥þ‡bÇTrÓ4…—£ó42€ÂNzµõŠ|¯)¼>)Îo‘úâÛ•Mõ¼ýükº£ƒöüBÝëçÂå&âþóRÿƒßý2òþ~ /Gç ußKkŽè†Æ—»–:WÏ?©ÿÁèãyÁ¸k /G?>¯ÔŒ‡k¾xû kzýA æ;°öKý–7BµÎ_íŸõ¯ûú–éb‡,Àâƒöú1ºäyl=´Ìÿ°¿© qÖòrüñ#ãxj‘[ïjá`Z?o4k¿Ôÿ°©OñãÉËÑùbw ê‹¶þÒ‡¬€ÑcÅýi¥þ‡3aÙB_Mx9úõ¥FHXíû.mðð¹1k¿Ôÿ4<•¬~eÇËÑûoCAX”¡Åâƒ,ÇHˆÙU“µ_꘺Õ_¨æàÃËÑù:åÚÃ…'ÏÑøüÃáÓö=Zæ¸|VO®&òrt¾²‡› ±:šZdNÃnâþ RÿƒËÓTòâA!/G?ÿê@š‹ÚzE>÷Öáúñø“úž N ïLååøï_ükðüÌ¥óS©Õ¸<Ö~©ÿÁ¨u– ”ÈËñç÷ŒÄ:Æh|O2¯ÀÙoÅï»RÿÇEZRx%‘—£óݵކ5*m ÷Åý…¥þ‡œQÒØ-€—£Ÿ¿qÎxûzéÉ Xû¥þ‡Å;È“¯ö7ÿ×ýëÒ¨Qf¬ÚzE¾ä³Ÿ©m}¶žVîhº‘”MuáåèÇç=ýq8¶ ïúiv(G°õØ2ÿƒí“Dm#šÈËÑù¾©6I·l5š_¼òuº(Ÿ`Ö~©ÿ¡´…ƒ`¹¿‚—ã__rîP³)hë;þôן/¥þ‡Ô)þdÍ6o^ŽÎÿ©%Ìß6ÿ2ÍCTMÑÏ%õ?¼tËfKäåè×—×ÍèþvÔÖ+ñQ×Tkzæ¦8ÿ_æøTL6|,áåøß'Ž\ÀÜ¿>Gm‚ ?‹~M©ÿ!Ô¹žÐä~¼Oç6 î9õP[¯È7¡ÇgZy>Û¯JæxóG‰ÎJáåè|ï$CDykµõJ|´ÚÛ\ØéÈÞdþ‡Õ.Ö‚Ù^O^Ž~þ¥ê áÈ)µõŠ|§í¬àé|ñþ õ?”8” ¦ŸÆòrôþËZÕÕÿp'h­êÉæ;ÊýBIaÏæ¼/?÷:Ì{ˆ7¿îS´-$¼c¿eþ‡úëg’s­³y9úñi4å ¤k…ÅGëulWΈïRÿCß•‰¤îf^ŽÎ—]VÖ}6Eã»°a0$5ç·ÈüuÉË®Ü/Ñ56X£¶^‘/«ÕYê:’í+ó?8g9‘—›Æórôóï‘·Ò«9¢ñU5j7.0_†ÌÿÐqƒ¶¶Æ„—ãßߟÔÍ[>Gm½ 4Ö…l×wÈü Ý‹^Ž~|ší§šãµ±øhƒ÷í¨ÍϬý¿ùô°‚,>¦ãåèýwfØEèV·ºÚz%>(soSƒÅþ‘ú¬ø ÍÞ$ðrüï/vNtä´ý‰hŸUè¼ âþ(RÿCÕóÞdkË^ŽÞ¿KêQ[¯Èg> l çÏKý¹nv‚Ûy-/GçÓ{‰êˆÏ,¥Õ§Š~_©ÿÁÒ«„lÚ‘ÊËñ¯/Cì¼ïgõ«M„ô ì}YæèÙ3Lh^9š—£ó5ë¼dãÍÏêr‘þÍÏ,õ?8ÄØ 'Zórôãó…G*µÔ¡­Ï¡Õݲ¨Óæ¯üÓÿíóF¨¥wàåøãcƒ›ÁÆghë;hí…ƒþÑÿ0§T+?ËËÑÏî-æÃxûÛ\ £Æ[Åýë¤þ‡²­ä‡ËE¼¯ßýɰ#&Xm½"ߘÒpÈ›í§&ó?äš–¼ï拸þ‡¾=hËq~–Ôÿ@Ö8 v }y9:_å¶óàú±b4¾fÓò©ñ[æ“ù¾ùA£mhÀËѯ/nÅKu[ïbñAÝiݪ¯öÇ’ùò÷bO^ŽÞæ)ÔñÞóg£ß íèÄü¯2ÿCßt$z¤ /Gï¿Û†Ž0ÔÏûeþõâ.â÷k©ÿ¡ñR¡_?=/G￾s‡Ó¥h|Ü hôIv¼ËüvîIÖì^ŽÞOoW…Èê9h|V‹Ï}Ùƒµ`¼•àÛŸ­G"£ô¾‚†¸ñrt¾Ž1wàÒQ¼ýÁFÌx¯²óYˆŠÝ,„´×WÅXy‘×½x9:ßþïßCÈ1¼õ·çã= Áƒ­Gv͆ùˆóçÏñ$±- y9þ÷kÃWTmÿdxZÔ⢵¬ýÓ_¶‚´`|φ” µ+ûñrüñéoÇÒÐö—¢†UhÛ6‘¬ýßùÌš~hÇ~[õÍ"ùKËx9þü¬/þ‡ÕÖ+ñÁkMØP!úG „ ¶âüÖÕAãÉÅz©¼oí»:PÔoè·ìàý/ì}RW;¤Î*›íC6nO%ššz^Ž~ÿ#§ƒ!t­¿Úz%>úc¸ ¬b¿…=û\„„ïØü=T%…åÙ¼/°Ø‚VŠ@[DÃÊ_Rë&¢ïaàìtaá ö»êý\²rN)/G?>ŸÝ¶‡IxûKY>ñ†CE¿Ñf›–:ËKßüõ›Ø»åkúórt>¯ÑtûL}ÔÖ+ñÁÐótG°ë…\^(„_`죊  =y9:_áhwx±±ƒÚzE¾WE¿R³£l¿!X³L(×ë+9aýy9:ߢÇ çL´ïµ°øéYûwÏj%ê+ú ʬI¡7G¿¾ 6jwuÚ²mÑ—Í/‚È`A_ÀöÛ gmÈÏÁa¼½ÿ.6œ #窭Wâƒò~†دÖ_¿eþ‡!íȵo^ŽÎçyü1ÚÕXm½"ßÉ­ 8•=oÉüÁ†.¤ß†|^ŽÎ·=Çf½*R[¯È—S)–bûAÊüwëIîo¾¼/ï@ l9:ï±¥rsû³öKýWÆ„ æ¿górüóï_jÝÿ°ÚzE¾×/®Rç{¢¿X긴WO®?·ãåè|[G\‡UUSÕÖ+ò>QRÇ‹û_Ký [ç“3‹y9:_œÐ ~ÀãÛsä¼ ç¯JýËy“*ßky9úý¯Ñ¢4X]w…Úz%>Ú*0b«±õ"2ÿÃÛñ%äИ2^ŽÎçê×>â=_Ó½¿S«ì·Ìÿ`yq)I5Ìæåøã©¡3Ù¯Z¥°,–ùÊò´ž™ù¼ÏaÊ+]R1š_ÜôŸuéÓ—³öKý++üÈ™^Ù¼oW÷ðÄmþä–M§Æó&°öKýÓMý‰OF/Gç[â6ºnÀóÅèMà—ôá¬ýRÿÃýŠ2ri\ /G¿¾4´?Km‡ íFÝ5c¨³›/ó?$ÝÎ% {TðrôþK ò„%1xþ¿óÚ@œ£èGú¾Kõ$ÉJx9:Ÿýè7_¾¢ñ}7î-ì‰÷’ú.Yç“V©¼|3® ,²C{ vº,ˆøÊ'õ?ì)K ©WÊx9zÿizÖàí7ë•Âî)I¬ýRÿCš»7ùØÍ‡—£óؤÏaxã+—í[Rë°¬ý2ÿÑ0r8-”—£ó% -ƒ9¶ÿõß“ñâúåÁÚ/õ?8lMÖÒòrt¾œ>p¸åª…¼â÷©ÿ!w…¿¶æd^ŽÎg¢k¬‹|…·]­ÜºM!SXû¥þ‡Y}CÉîÆ¡¼oY…3ÏðÖ§&Ô*‡1ZÖ~©ÿAâCÿVÄËÑùlíMë¡ÍO†é¦% =/®Ï‘úÖ5Õâ2_^ŽÎ÷Ðym2>Rm½Õ¸µ£m5¬½2ÿÃÚ¥d…¥//GçÛ´V+~_§¶^‰2ݵØI¼?Hýß^Ô’ˆ¼Ï&n3Ls~©¶^‘ÏÍô¬N|ÁÚ/õ?tµÒ¶ý£y9:ߪ¦§aˆÕ}µõŠ| /îÂo{Ù~¬2ÿCÆH'’q¼˜—£ó^³ öâÝnìë Y÷Äï/RÿCòðr6j&/GçÛòÊn:«­WäûÓÿÐMÜ?KêH?3žìû>——£óEÇÚÁÜñ¿¢ñ¥÷¹ë‚Åù­RÿÚW…dýC;^ŽÎ»o(lï˜Æ·¯Q8¾ËüókiIm¯T^Žþ|mûË~ˆo€÷}ÂqþpØfìÇÚ/õ?”™9‘˜Á¼ÏqË5Ø{g«ÚzE>÷M³!¿§èw—úŽo,&§Ã¹9úñ™¸v,$]h«¶^‰Žÿf 'g°õ’2ÿCÒ¾më'E¼›OwG0ÖE62Q[¯Ä§{7I·ùµ¸~CêXÔ²˜ûúj–À¥­x÷¿ä{àU’Ø~™ÿáÁLÒÔÓ„—£óùê½ùBµõŠ|³G>æâó‹Ôÿ°ñ·D’7¿ˆ—ãÏ쥡ÕÛ ùS©á3êØ‚=/Èü+]SÈ”ª¼/<î%ŒˆÄó#lsºCÄóOê蜓 TÛfÈËÑùªu‰‡‹gÐÆßá½5ÐèYû¥þ‡Ó sIȘR^ŽÎ÷¼Îuˆy‡v ¦ý7™CÁÚ/õ?˜uñL›vàåè÷wÍÀ­p¥2Úü:ZksªÙÄö’û´A‚±G/Ç_`vƒšzàO?´{IíMDÿ¦Ôÿp!4ˆ\6JååøÏ/«BÈÀcjëùN$µüGÿÃݼrû  /Gç»P< ëìGã{Ýs0¼±bóådþ‡èE¤ë /G?ÿš^^¹žx÷?·»Vp¹†è7”ú~NЩ>¼oÙ³õÔú&ž/ÞÔ‘:;²ï©rÿû~‚I¥Ö¼ýø¼|÷8\›‹æ÷¥šv¨Áι¬ýRÿƒYÝ­ý'^ŽÎ×dˆ§.Ös$x®Ðm"ú¤þ‡‘NäSL4/Gç;˜RB-¦áùí¯¦l§õ}Ù÷™ÿakT!±½„—ãù¾è¯h|é™Ïaô5Ñ õ?œs× µyñrôëK »Ët@&Úú8Ú°ë:1AÏÚ/õ?lóË'«ÌÆórôþÛà;íØ¥¶^‰ïÿü Xû¥þ‡™5<…j&¼¯Óh'¸Pª¶^‘Ïægø\S|>“ú.Lñ#Ùñrt¾ò.óaóÍ#X|ÔbþU8Ü^œŸ%õ?´\æEÊ«Zórôó¯Î·Öoý¦{ó§p£;ûÿ’ùž×’wWµ¼ÿýáÊ xý¢Úz%>(Í=B­æˆçŸÔÿ`_JýáÚÚBy9:_«UuËú íŸËÜu‘^âù'õ?¼žê'X pàåøýG&Q³åÕÖ+ñQ£üûóÇIýæ–’Õ'}x9:_|Te˜¼Dm½d†Cä>¶?ÌÿPú*_XøK)/G¿¾4XkO÷×Cû~F][Õ¤y‰ëYûeþ¢'ûýïüWÍúÃ’Jhþ#øœ_eí—ú–7ß%4‰­ÆËÑùt¿»WÞª­Wä›p£6­Ü­·‘ù>5¶$±¾‰¼ï}z¬m™¡¶^‰VOË€9l>”ÌÿÞÈR°ÈJãåèç_jbÈií¥¶^‘ïô™x( ¿/Iý¿Ó Á|˜%/G↓…«¡¬ Wm½Üé7VõLJ¤þ‡v äòÁ ^ŽÎ—?±æ]÷Cãûܬ1¬ŸÅ|t2ÿƒ½‹¹tß’—£ŸF¡# ½ ÞþÂöåSáê*qÿY©ÿ¡O¡†4úÀËÑùŽÔ ë.†©­Wä»°÷>$mý+RÿÃêàPòÇ-/GçKô.¦ ÜñüNYnÔ5Q¿”úZuK*Šýy9þüëŠ|Hß×þ¿ùª¦}†›7ØûªÌÿÐ~£¥¶v“^Ž÷¿ª›7¶·Úz%>ðÛß[Ù€µ_ê¨æ-˜¯qãåèǧéRCªùcuhfJm¦‹ãc¿ƒ It#ÁM²y9zÿqu‡nMñæï–ù7†©{ÊYû¥þ[“BÁÅ6›—£÷_‡géH=Úó'íë|›Î[Æõ;Õf’m7gòrôþû=ü,ùÙÏ|È"ØD˜Gæ8r-TðèÍËÑùô+Âã?,>ˆ+:B«×ßJýU–”’»By9úñiVu>Ä>Ý„ÅGëºÞ“ñJý½¯¸ -&Ùðrt¾fs¬àÁÃah|]ÝA­²Ø÷"™ÿ¡QèM¡ñÌñ¼ýø|áåF-ý?£ñUß ´©k¿Ôÿà•P!ØL²æåè|e-`ã½+jëùjïÛÄùIRÿÃÜ53Sóñ¼ýøìVRw:áío°£5ÞÏÖ3ÈüOûDúTËËÑùúÛxÀŽqjëùÆ,+øÛú#©ÿ!7ÜE0{ÊËÑùnÎ7 µ‡e©­WäûìL]ŽÏbí—úàJ¶Pw{/G糸:®ù¯ÿžŒ¯Y´351¿ŸIýî\´¡ù¼ýúâV­‘.0û©Úz%>èѳ@fÄ~ËüÎ5²YN¼½ÿÌc‚iÃnxãnO£ÏÞaí—úìz¥¸Æõx9zÿÝ*w…!z4ÿ5¸‹§²õD2ÿC“ë3…÷Kx9zÿõ N£K JÕÖ+òM0 ÑWÅç©ÿ¡îöÖ$§'/Gï¿§Ù] ÒÏ_e•ZÒŠXû¾n'øÚ³ù{d”w¶ éËËÑù:f›Á¥¼ý1G„·‚Xû£ï»!ãØú;}»,wàåè|ûG|†çÔÖ+ò_óÇ—=XûÔv|5âþóù^Þ$áw^ŽÎ— ÝÆ{º'â Ù~\ÂlÛÁÂR¿†Œïñ6­`³Ú„—£óÝÕo¢ .%cñQÃ5©´í`öü"´ýVhzò;ö»Ê¬"’+š—ãÏÏró†Wâƒr;ØXƒOBÈ…y¢ÜÎý&¡gô䢯—£óEÖ·…kãÖ¡ñí ÷ b{^Ð5œ=IgjÊ|Ô$êž1mŸÍËÑïäj/íß‹zߪ k¿Ú_OÚ*!1“= ÚŠ2r&*•—£ó^J¦•â¢ÐøÂQZ#ˆBÿ¥«…áÙz¡ú jdYµ¥¼ýø|ns&-!h|V$–oaû^×ÙÌñ×oRï…F[÷x*/G磥Ãuû"³±ø`hôÝ‘cìyDXÒå¡°ê6»_“»‹Èå£v¼¯°ßQx¤¶^‘ïÕžPjneÀÚôîaÒl<èËÈðAa¼oÑ”ÐsÞþ­QÑ÷!à[-ì›ØQ8üVô%ÔkïK΋ß'¤9úõePþ&j·q/2ô1mþFìŸÍ >‰þ•‹cŠÈO_=KrüãóZ ŒìS‹^[´……ÏÅùÉRÿÈŽãÉMчùïû=áдïŸ0äæ{¸c.ŽŸHýAWó‰¯/GçÛ¾§7ÌÚ‰w}ùϵ–ûŠë¥þ‡œÎnäD”/Çhš[$©­Wä{¼ NØŠû3Hý—–—F#Ãx9:ßÅàY´¦ ž?çuÉ9êœËöƒ•ùŠ_Y’›M¼x9:_R™V¾À[ߘW»Rç²ýdþ‡Õßy‘Soµ¼/®u0ÖS[¯È·Ï|¼\%Ο—úVYcñzùïû–ôÕmÐæ‡ÐV¹C &² k¿Ìÿð¾cûBy9:Ÿë¤ðY£Eã£gP«[âþìRÿƒu}²§¥†—ãÿ}ÿbBÌÕÖ+òU[çû‡³ù^2ÿÃÛ“NZçL=/Gçs°´×mýïÿžìüs}X—ÖJœß)õ?¬ ÓÓSÇórt¾]më“ÍÐøNDXSã GXû¥þ½cé]=ˆ—£ó-iTºDÛ¿bûÁ/-õ¬ýRÿÃuÈ&…CSy9úõ¥¡÷jó íùŒºýz–6ÄÆÛdþ‡­¿æ“Åýy9zÿ¥…¬‡%C°øà‚Ñ˯ý¾2ÿCû.dËø^ŽÎg¿>"uxþOÍ{Ø3î{Ö~©ÿáÂóD2pC(/ÇßÜtÚ«­Wâ£v•CaÍCñýAæ–OönKàåèýwd”¬q@[ÿ7A»‰ï·RÿCºu)o¨áåè|:QÙý÷d|—¶§Övâü:©ÿáœI(9üƒ5/Ç0j sÌõh|úF‹û·IýûVú“ƒgRy9:_ÎÔ 8ƒæƒ[—ÂÝrñýNê;ÓŽÔŸwÿ}ÿÃΰ¹mÿkjµìi÷†µ_ê0ý6š›îÃËñç'Ïîÿű†Æ×;&š»±ûÌÿp9JKN.uâåèÇgfò°} ž_í‹ÿáÌGö=@æÈ?a§­·)›—£ó™TÐE´ï»`ÓÀ@·yµøýHê˜î@Riy9:ß²Ðg†çŠ¿Ù6÷×—úHކ4{ªçåè|6;Ó ´ýÁ`Ú“i@ì k¿ÌÿPž@¶·äåè|{¼¥Ÿ Q[¯ÄG5ýkÓ¶5Ø|R™ÿ!Ф’6)¼oST!¬Øš¨¶^‰2—ÙÂæ1l¾£ÌÿÐVçIVÝâåè|6Ú ˜öÉÏõÞ,XÝEÜŸHêè=8š|û.——£ó­2L‚ÁÜÔÖ+ò­Ÿ|fÜbóçdþ‡Ö$ã€/Gç;|Z CÑÖÿýŸÿ!„­7•ù’~v!ÒJy9:ß–wùp£f¹ÚzE¾œ.Oáõ%¬ýRÿÃþÓ$mn/Gç‹þ©æëÑøÒu+`ÝIñýOêˆXèMÖ7ñäåè|±#‚`ûæVh|{XÀñ¶Fæj¦'ö–¼ýùÚv¥Äg£¶^‰6¬ÛÆÜfí—ú>Þö%1™e¼ÏqÑnØ{ oÿ¬o5†|/ñü’úŽ6u#çÈàåø×ÃHšk§¶^‰Ž/0Sæl¿#™ÿ!ÙM«uigÇ˱ùtÅ#ëê"ÇWÇâÓU|¨÷þ‡ÀÀDrhL./Gï¿5÷Ã¥9)jë•ø 9r”uý†RÿCßf>¤ÁšB^ŽÎç;Ãì=?ª­W䛕{¾÷k”ù⇓3·By9:ß³chu§ÓX|Ôpou¬ÉÖÿÉýWÝÈÄ©3y9:_xzÑ m}Ø6Ðþoó—¤þ‡îùÞBÕý+y9:_µíp©Å4¾ÖÛ»R‹¬ýRÿÃñD YfáÅËñç·~£‡˜b´ñMjj¶ÿý•;Lž•òrôû»¦E[¸2ȯV©Õä°ùrÿÃ`ì«çåèýwâê)j:ÆVm½<ø¸›ÚwjÏÚ/õ?\|”A ñãåè|‰çÛAÈ‘jëùNÖÌúGÿÃíÂr³™7/Gç»ðÈ GàÍÏ*¿Û^¿¸ÊÚ/õ?ÄíH$ÞÓBy9úù×4|äºÇâ£nýá2aû=Èü£hSM/Gç J­ã­ÿ‹ïœAEŸ„Ìÿ`ì$˜öíÇËÑÏ+ÆoàZ>Þþ/ÿ·3Ê1ýÿø•B‘¬‘²e1ÊX²=×ý|®,#[“-[cCÖ0K²’6KId‹"¢,YT–”%Ëd-KÙZøýÌïÌuÏ÷¾Ï5÷}¾¿Ï÷ûsžó>ŸÎõrÝûµ¼¾}ÿ¬Pu k¿ÔÿPé–…¶Þ×"^ŽÎ×lbS]ø”ujë•øý§Ã£þ^ŽÎw|A­ì ïΓ¯´áqæ[—ùb+øÏ>1¼ü?òýÎ{ ñ%m¿d‰þM©ÿ᪳A°rÈâåè׫÷Ýè°L´ïƒ´±Uo:m±¸ÿ Ôÿ°?$l:ÅËÑûoûWðŒwþ¥¤¶üûó¬Ìÿ°¸×H¡z–3/GçëñC_Ș‚ç7üeësø²ý=™ÿ!{† ‰ºåËËñç·îª1M«­W⣕OÌ„SÀÎ'™ÿÁ1Ö|áÃËÑÏ¿zÍ5Xi ÚzE¾ö½ûÂ]?ñþ.õ?¾t!åÏRy9zÿ]¸q>tÁó‹¶\@-‹Ø|™ÿ¡B©·`båÃËñç÷è!Ú¿ß˹5àZ}¶_ÌÿP~&ˆdšxñrt¾G-ÇÁ™É‰jë•ø¨ñÓÏÿè8WîCê¯Òòrôó¯û¼ˆè…Æçöv%Äy-fí—úüv9ËT^ŽÎ·ñòTjün¢ÚzE¾¸øÕ´ÎñùSê¨Uý1)þÃ…—£Ÿ·À™|´ùKÔ¼n(¼hÅæSËü6å^Úz…^ŽÎ×f;Ýêûh~CpÍ¿¨ y¾·ËüE¶Áü7-/Ç¿¾˦f›ÐüÔx¸–6¬Îöc•ùÏõ$›:Øðrt¾È³`qÐe,>8þ!¶T×ÿIýE]s„ùµ¼ýúboÑ€5MP[¯ÄG-îÑˇÙ÷L™ÿ¡<5ˆìhåÉËñ¿O\Š‚Ÿžª­W⃯¿%Áö9âü$™ÿ¡JŒÐ¼Î^ŽÎ'¼¶¥¡ñMëÿ•V™"®¿•ú>X“p¡„—£ó•¦ …M¹‘jë•ø¨U“1°ª¸?«Ôÿ°¡“½PeÐ^Ž~þ%”~„ÓÎhãÓôÒPp‚­gù^ °Ìn$órôþK1¼†âYxë;%Ï Õ¶æ±öKýõï#™¶¾¼ÏÔô5ñö÷þÒkD=ßÿ¤þ»§©$k/G?>7;@’3Þû­mÐÈvÇo¥þ‡ž¡¤áí@^ŽÎ—Vï#„á­ÿ˳b'±ïE2ÿCÐôPRôC/Gç‹éGíB÷«­WäKÑN¢N¡Yû¥þ‡ï¦­#Úý÷üÏ-l!ñÞþ×Õú—ýIâñ)õ?t¬U¢ýn©3/Gçƒu¿ïV[¯Ä÷§ÿaõQ6Þ-ó?Ôoû¿÷‡aî¼ýø¬´b$5²>¯¶^‰Ú?JkŸ_¤þ‡Š]‡Ýýy9zÿ]¶p€>ùx󳊫Á,_q ©ÿÁÊÏKh{ÈÀËÑû¯[ä8:¾yµõJ|tðˆ.tÑVqý”ÔÿP­{‰[èÅËÑûïÓžàŒ¶þšëŒ`Çmñü“úR„öq>¼O?5 ^DebñAĆvÔj{Þ’û~³!1{\y9úñif‘á÷ÐÆ¨í×QôŒ]eþ‡û<…ïÜ>ðrt¾–£·ÂÓéÑjëùz—äü‹ŸYæ¡é/G?>ßünL-¡ŸßÜ$´yñýGêèé*Ô,Œâåè|ï2‹qýkVBæTq}•Ôÿ0¯¥`zÔ—£Ÿ}«u‚‡ñü3ÊQ“Ýâú©ÿ¡x{q5èy9:ߟþ‡AkÕÖ+òMrz)9lÿn¹ÿaO¾Pao /Gç»—Cë&àùñÊ'Rdž¡¬ý2ÿC+w¡®Ã÷¼¯Jäîꢶ^‘¯å™IÔd {Þ’ùºTpÔ¶;þßó?üﻚnY4Þ÷—þYt–³õR2ÿCóîB»ñö¼½ÿÌ—î¢+ÔÖ+ñÑ&Ñ«i·ÓìyOæ¨7)žlkêÃËÑûï~y 9ÈIm½"_¥9=`ù-6ž!ó?8œÎ†¬Nååèý÷S³µ4 ¹§ÚzE¾iíhèBv¼Ëü6Q“Hª‡7/Gï¿W¿XÃæWçÐø,G„‘6Þ& _8NùÓý¿~“q­ãIÙ-G^ŽÎ×}H8ÜÜ7?dÌÍ(oUÊÚ¿ùÜ!0†ù~ID-o¢ÏËÑùŽzdÃ*4¿ dX¶ƒÈàßYûÝF™‰ûcfTö&Ímx9:ß•°gà_oþYkD\bã5ÂÜ-3ÿM_aÁ&Ć—ãOï¿®i…„pµõŠ|Ác+ÒÙüaPú`Áó‚x=©9!Y]hÁËÑÏ„Û0½íü£–am¶¾_Ø>ì¾ÎòÙ§¿~“†Ã]´M­,x9:M‹Ô ÂÛ_cÔ¢<Ýéñl=Œ°²Ñ-aý,6¾HÎ/1Û£x9:_ÖÄöð¦i}4¾wÁ;©Ù}¶þFXRï€àÓrãó›bOFïóâåè|~F¾0 .Ú÷ Ø’3|ªˆß£Œ„SF?²ß ~É!y^Ž~}Q¯µY…·ÿÄ̀ѴÕñþíW9QXnÍî÷äzU_2Òº€—£÷ß6`|ÉC,>(>P–ž÷‘ú†[Ûõ}x9:ŸËŽ&pr$žß~Ä};xØYôkKýc‚ÈÏßÅórt¾½-Ó`þïŽjëùN ¬IÔ³öËüY$­½ /ÇXñbé¿ý÷d|/r+Cú,q}ŽÔÿ5ÎK¨4Ñž—ãŸIM©õqO4¾÷'÷ÒÙ÷h™ÿ!³’?ÉÝçÂËÑùv{SXhëàR÷°ù5k¿ÔÿüÚœO*âåè|á¯[@æH#4¾#íWÿ£ÿa› 1oÈËÑïM~÷Åõ?„é!ì™è—ú ôäÂIO^ŽÎç4¡¾¬l‚Æe+þeý‘ÔÿPíT94Ý‚—£ŸE™é–‚¶9­>zçJü¤âlmçû ¼Ïno .v/žß©}Ǻ$'ö½OæØz=*Dñrt¾ý›‹àe«44¾óoþeüHêøý‹ÖÄ—£ó´ž }Z¢}?ƒðº?ÂäBö}Wæx°Óƒ\7ÖórôëKcû‰´ŽÙfµõJ|ÔyKÚ¼—x}‘úbò&+½ây9zÿ%n͆€ x~ßëÏÛAÄvÑŸ#õ?tý1•ìJäåè| V\…Íxþ±ÎÅ;á {’ù®vq Þe^¼|ó÷xXv©©Úz%>Zok_Ø<æ.k¿ÌÿOâ—x9zÿ¥¹N‡à ¿`ñÁ=—°ÿ‰¸>\æ(×’¢ýɼ/¹Ñ4øšg„Æwë¡­ñù8k¿ÌÿPèHN\®ÈËÑùb[Ì……ëÐöׇ Ñû!¬§ø}Bê8Þ­€¤ŒŽâåè|§'ô€sqz4¾1¯à1aûíÉüÞݽˆýç"^Ž~}±^²¢ßáí_Þà|8ä'®?’ú*¼Ó’S¼~òÙ P”ˆ6ÿšréAͲÒXû¥þ‡Ü¸tb˜aàåèÇ牺°wÜb,>¸?YWŠÇ§Ôÿpím-Ç^ŽÎgZ7P·¥VsµõŠ|µ‡øêvž8ÁÚ/õ?,øO>wçåè|«·ÁåChóë jÔ=xºÈŒµ_êèY;ž8Ü´çåè|CKBÀA¶¾æÔnÚ ì·Ìÿ–kA…:órt¾§…³i3G¼ý—4†ÑD>©ÿa³¬yÃËÑùvokõÏÕÖ+ñÁ‰¡c úÆ|Ö~™ÿá™Y·Ï›—£óÕžÿæ´5S[¯ÈçÜIœØ|/™ÿaĶPÒ¹<—£ó­‹m#®Ý@ãÛö$æ=XÄÚ/õ?$•¹’£«‚x9:ß©À!à­¶^‘ïnß4H-úÿ¤þ‡èçäæÅx^Žÿ|=x®ÿh |¸?»ÔÿÔ»„i]™—£ó…~Ø‹*LP[¯È—Ú ¶^¯ÉÚ/õ?lxX@¶wáåøã1n°÷ð;µõŠ|‡·„ÂÙRñþ&õ?øµ² 6By9úóuÙ­ ²š–6ºç{Ú²ùøR¿ƒP¡™/Ùv?—£ó5Ú‡}¦«­Wäû¡À 'µ¬ýRÿÃé›öäòg^Ž~|Æ|9±ÕÖ+ñÁ¹ßÌàB˜¸ÿ’Ôÿ°w®—¶ëâP^ŽÍ§ËsuÐ…ø¡ŸºÒNt»z°þùVôö"Éöz^ŽÞw·†›CÑö†¸âðnŠøü"õ? ™bOZYx9:ßÏg/@ýExûÓΧñС†8~"õ?l§%—M-x9þüÁÄBZ=÷®Úz%>ZQÿ†Úýé¯ß2ÿÃÚ^dλ(^ŽÎ|ÆÆ,>Øãp– ©õ×o™ÿ¡÷-wÁbõc^ŽÎWíE ܸ:_m½"_Û™•©ÆýÉüWmRI€s/Gç{Ýé„[è°øh¥ÄùpháPÖ~©ÿÁì'/Á<Ì™—£ßß5d;Ü:·ÿK­5%TóPÜŸOê¸67F0¹¡ååøëš9ÐJ~U[¯ÄÏÖihƒÎâûÔÿ™íN2ºGñrüç—W;aUán4¾ó÷§ÃÞ(qýŸÔÿp/Ë•Ü[’ÎËÑùþô?,X¯¶^‘¯xCUøPý–û²=ˆ[*7G?ÿš{½…s|ÔÖ+ñQçkÙpËœëw&[哲ßõ¼/°ñ#ZSôaÿ¿ù"õ½hówl½Ìÿ`Z§•`ÜÞ–—£Ÿ·-ÓàÎ ¼ûƒÆ÷+5ò÷‘úªŽÌ×Ö¿›ÏËÑùšõœ¨ ;·> K‡šºØ×âþ`RÿñnBÅB^ŽÎw|^.­Lñ¾/eO>E>îÌÚ/õ?ÄÜ- Ãtãåøß_&˜íƒ6¾ IsÖÀ/k±öKýoÅ–ŸZùît8ÕHŸ–úZ³!ïJ‚x9úùW¯ U[¯È×¾Õ«oc¬ýRÿÃËîZRÒ5—£÷ßźéð\Á⃂=µŒý\RÿƒÉ`4~/ÇŸß30bꙨ­Wä{Õ¢®®àúH¹s*É8ÏËÑùý0 ÎL]ŒÅG¥Â'q~Ôÿpie ±{càåèç_÷ï B“¦¶^‘ÏíË ˆ[ÄÞ·dþ‡åQäM^ŽÎ·1n<5^iÆ—@ëü(Δú¬®ß"Ÿ7Ùórôãóñ‡ùpæµ³ÚzE>³3àE!›,ó?4ÌÒ6y«áåè|mÎEêVýòZm½ôÜ« é ®ïú ×$U¶øórüëËÆDj6mÿ—?÷×h؈]¯dþ‡$·@²1!Š—£óE.‹C†añÁ‰ï|!¤LÜŸOê(Kæï/âåè×»ØRšT”„ÅGιÐËá³Yûeþ‡òùÑ•—£÷_¶kX‘ß‹¾¾?ûm k¿Ôÿ°vñ8ÁAðáåè|:»Ãðèg¼ý—¦}jB«4gë äþ‡wnd›]/Gç+}:6ퟤ¶^‰Zu†ýw‡³¿/ó?,´,J|y9úù—°7N›¢ÑKë&@áø´¿~ËüùL…Êï‹x9zÿ¥Ür†¢T4¿6<¼gL«múÛþÿƒíÔxr3,•—£óÖ}+¼÷£/§ßÁ¶U}Xû¥þ‡ó $ó¸/G?>Ï…${¼ýõm›ë!Û‚í§&÷?¬Í'ú„òrt¾4Ç)fæç¢sãàoë]eþ‡Î6¤0"Ÿ—£óÅT¾Mí¶ÝDãKñÈ¢N®â÷%™ÿá„3ù0>ˆ—£Ÿω$ÞÆ[ÿ^mÖ+¸7”Íç”ùº¼+ÑÚ…ùórüû{DGÝï¡xë«<ËÚèV[ÌÚ/õ?Ôm\"T^àÁËÑÏJÛ¬¨f=ÞüA;¯´öì}Uæ0ÙЇ,­ÌËÑûïÒ^7èóŠ'ì—âúT©ÿ¡úõ<¡í¾,^ŽÞÝZ…Ññxû+º_îA˜Ræ¨^Íĺÿý—çB@§Fh|æía‡Ÿ8þ õ?œ|‘,8Ÿ²æåè|ú¾gáE—³X|Ñh:µrfû‘ËüOóÉŽX^Ž~|šib!ÐÖ‡Ó†­Ÿ@Ò-®ßAØÊYø®y9/GçkÙé< ²Cãëýt µ\Îöw“ùšþ”"8LÑórôãóMo7jñój4>«Ìª´¹‡Ø~©ÿ¡×£ ¡öÊǼ|¬æØY™ ñÕ-‡¬Zâ÷O©ÿa¡ÁK0ë°”—£Ÿ?¦ûÁÃ_-ÔÖ+òùL §&å¢_Fê(ï@~lcàåè|CÞ<†}‡ñž?'Å&ÂÉ q|]æh]$TíÈËÑùîùÓºajëùÊ–PÇsì{‡ÌÿàºÚF¨×l/Gç«|Óràù;ZæQ“{âú©ÿ¡Ý¹|m;“P^Ž~}qÊ Õ-ó3W[¯ÄýBýu~fÏ#rÿCû|¡ÝQ{^ŽÞæóÓF—3ÔÖ+ñÑ&[>Ñnë6±öËücKHäË…¼½ÿî¯J†‰xû'Wê6–³aí—úZÕq†ôÉáåèý7øÁ8àƒçŸæcDCÛ±ýùdþ‡zÇRÈ™¡ÿEÿCÉ9ؼÖAm½"Ÿå’‰XS|óøh.xijþ"ã®jÈ×n¼¯»— ÜŒAÛ? Æ|™åÑ]XûCò<…5ÝØü!c ‹²óy9:ßQóI°j×Q4¾ëK}!rgkÿâm5„‘Åù!†ÌTÑ΃—£ó]™Ò ü’µh|¯6v€ˆéìýH˜Þ}Œà¿å3ã{oï,ØÐóòÿÄüj=‹“δãõ2ÖþÇñûþÒy)¹¶?Š—ã¯ï4 –¶Âó;½?v„³ñ`ayö Á§¿8?yƒynbÃËÑùBÚÿÙ/^©­WäÛoæ åîì|ÖÕvž®3ë ®ïù>˜VÍçåè÷?2¾mÅGûyÁæël¼[Ø_… q¾ìú!Ðîä¼W*/Gç[öö'Záv;µõŠ|ÁhÉXûL:%Œô`þ ¡vÓ¥dmk^Ž~|nµƒÿþß“ßßϬ%Þߢ £tæ5Åý5ê¶Ñ¶ºÃËÑù\;ëŽ ÂÛ¿çOÿÃÞº¬ýkc¨°6޽¿’SÇ4$gx/GçËšl o2.ª­Wä{ëiBÍçVeí_òà¡05H\¿²ìªwÀ—£óùµ+„þ'ñÖOoy÷=ø bïçB½*¤ý(úJꇚ’k‘9¼ýú2<­µ9‰è°²§­>ˆÇßÒHcaY˜å_¿IÆ! ñ¼ÊËñÏÎ0~©Úz%>(î[–f°ýdþϵö$·S2/GçsYæ'·Q[¯È72t3ä=×Kýþó|ɘÏ.¼oï‡}¨þ‡´Ï»a­»_Ëüi KÈÙÚZ^ŽÿþPaìÊÁ»?¼èÞÒÅÿ/™ÿáæ¾‚y©?/Gç»q!šZ/ôCãûPOC[Ve>k™ÿáf¡7¹ßÝ‹—£óÅ>žëòñƧ/ Ø Qâø‚Ôÿ°nE¹Ôß‹—£óEŒ½ ™c¯ª­Wä;ü‰ÀÛ¯Xû¥þ‡uC¼ˆEq /G¿ÿ5iö ×ÿ0e/„÷šÆÚ/õ?¼}§!çÝ’y9:Ÿ“P¾|¾‡ÆG[jhµjF¬ýRÿƒåÖ&äðo^Ž?þ—aÙÑjëùª7 †cuÅï×RÿC…ämûn¼Ï.z¢.¶ÍÏ íkDê’ª…°öKý[ô1äüÝd^ŽÎ·ÿHx9Ïoqþ`jòc+Ö~©ÿa~^8*‹—£ó­È™½ïÕGã [a“‚Ø÷™ÿááV¹ùRËËѯ/ëüBk¿ÁÛ¿À9)ˆ6×7JýÑbÈŠEμ½ÿ©X|V[¯Ä׿cØ|™ÿ¡ÓY-ÙýÖÀËÑù$ô‚«Óh|׿ƒC‘+Yû¥þ‡[5Éè®^Ž?¾¹²,;¡¶^‰ÚèR`SÛQ¬ýRÿÑàÓ—£÷_jÆqØX·Hm½Üûþ#k¿ÔÿØ\CJh/GçK®í_ƒz¨­W仵:œZeã]2ÿCF¢ž$ï¿ÎËÑùv½è ÕQ[¯Èwaß33fí—úŽÅû“”í1¼ïth78—…Æ÷ ÜOçKý¿s# Zðrôë‹õÜQ]‚ÖÔvòV8ô=[ï-ó?T2Ï'§lÝx9þüäðéPÜJ‹Æ7(±œšW÷'úr¶ÙƒC/G?>Ȯ½sñÖÿÝßy®ø³ñ|™ÿá|gmÃR/Gç3m8Qï9D¯ÆÚ/õ?tÎu A$—£óÕ^òf¿¦¶^‘Ï9Ð6Ì×§Jýc{¹‘.Ûx9:ߺ‹y0Bÿoÿ=ßö9þ0/ƒÍ/ùŽ­$Ç%órt¾SË Õ¿•ÚzE¾»~g %˜=Éü»;[ÃQ^Žÿ}Þr%ܵ*W[¯ÈwzÑL(ë ®ïú—HRO7^ŽÎš} ~¿“ŠÆ—4r-„Uߤþ‡à¬t²½—£ó…ghPý\GjÔsGXû¥þ_37Òpu/G¾®Ð" V[¯ÄG¹m‡=GØýLêw §HÄ_^ŽÎ×(â.Þ‡¶ÿ.m_†™ÌÇ#ó?œ{™O.OIçåèÇg̽¦N°øàÜ% \ðÒ²öKýq\µmRy96Ÿ.ïVE]ȸïÕÖ+ñéJgÝÒŘˆûgÈüçÒÉ©ž¼½ÿ6^ 7½úª­Wâûæ^¢^â÷i©ÿÁ=74=àÁËÑù<:'BýÈMjëùæ6††‹ûÛHýÛ²µäô%^Ž?P“J­Ìì±øhÅhJí{°ýÆäþ‡Lo2ãT/Gç ^fcâÝÿâÊ`éSq}±Ìÿ ø–Ÿ,x9:_õÞ‘pÃoÿä¶'Œ¨æ7ÑO)ó?t3•y9:ßkë½öm~­‡Öˆû·Jý•:¹fµýy9úý]3¬ܺŒ7¢ÖÜlªÙ-¾Iý¶1‚éM^Ž¿> ¦#­ä‰6ž¥^£ fˆûHý×½Hf=w^Ž?þÐ6VW»„ÆwþŠì}ÌÖÉü–Ú;‹òx9:_&5@Ö-¼ýÞ;õ†÷™"ŸÔÿ°Î`C~ôäåèç_ó»ö.ÌV[¯ÄG[;À­f¬ýRÿƒÇ9Á(Ó——£ózm£5{ÍR[¯Èñ{UÚb½øýLæÈË*äæñrôãóv5p§íûÕD<§FS–³öKýæm¼µõÝÝx9:_3‹ºðàcjë•øÀe^·;© k¿Ôÿx:A0ÚcÏËÑùާդ•gÜGãË °ïí2ÿöDG2Z°áåøß_þ°8ï¡¶^‘/©åzøe÷>Ö~©ÿáºQŽPcº/G¿¾XJ‡Íê­¶^‰6*^@§ 5bí—úl)"›íòy9zÿmi ~]ð¾ï¦¼¯aÙâ÷w©ÿaÞKg¡Z–3/GçëÑr-dÌDãóêоÌgOæÈiäJ¶Eòrüù­wL úš…VþØN97aí—ú¾[—CŠª{ñrôó¯îkc8r oÿ:ç”ÈÍŸŸ¥þ‡‚f¡¤ÄÅËÑûïbý0ø0¦-¶\@-/°ý\dþãýㄊ©÷x9þüžàÝc”©¶^‘ïU•¹p­8~$õ?”>"7Fºðrt¾G§àÌ’šjë•ø¨qÙmxr€ÍW—ù ± Äaµ/G?ÿºu‡qÿ±ÿ7_?ŸÅ°Ç"•µ_êXr͆<µ7ðrt¾MvÔ8ïûDܺ޴vþ#Ö~©ÿ¡Öñ¥¤tH(/G?>¿™gž¡ñ™ôƒ—-Å÷?©ÿ¡A _mý缯͜&ºÕïÓ°øÀ5ÇT·¥)û-ó?¼-sªTwæåø×—™¡Ô,ô,>jÜØˆ6,~ß•úŽýbA6>Ëçåè|‘Aå°xXœX]BΉãÓRÿÃksƒ0ß?‡—£__ìÎ\¢Iï†cñQ§ßÖÐË!¢Hâw4×½Èö±ÿ=ÿÃí“y°âê4¾ÿý¶·Ÿ?¥þ‡Õ>ùBs“^ŽÎ§«c€G#ÐüÅ0µÌ„Vé)®¿•øÍ‰˜›ÅËÑùJ"aÓ}kµõJ|Ôê„ìbã52ÿú¦ö‚Åë^Ž~þ%Ì\i(ßÅ’PØMÜŸ]êxùs P9׆—£÷_Jr ×ôÄâƒGú´Ú q~¹ÔÿÐàr¹5>‡—£ó^µ„Eðο//3`Û|棓ù¶Ñæ¾¼ýø4ÞmIÚ\µõJ|ÔÖ“@öX6ŸLæèï§'u?[ðrt¾Ô²‹6Ê/ù#ÄýGRÿæ©näÓ /Gç‹>XBífãù§Sœ;P§{SYû¥þ‡Öþäý{^Ž~þ=_ÞƒRÔÖ+òýïµîŠó?¤þ‡•c´u“sx9þýý}/Ýï‹|ÕÖ+ñ§×;ݪ6âù'õ?Ôk%˜×ÉáåèÇg¥+©æ<ÿŸÝÒú´övÿ–ù*.Í#þÝÊy9zÿ]òú꯶^‰Š~³‡™ÑâøƒÔÿP§qªà8>”—£÷_·‘Nt|5<Üà’(º¨¹¸ÿ„Ôÿ`•WDv¯qååèý÷Ǽ°âÃ:µõŠ|æË?ÁŽ™—Yû¥þ‡´Bg¡£Ö‘—£óé»>‡-½ÔÖ+ñAÄÊFÔ*1™µ_꨼<”l^ÄËñ¯/;G@øÛ½jë•ø¨mïÕäžßÊüƒ‡ßåãåè|-ò;ÂÓ±ÓÔÖ+òõ©L-Ùz>™ÿÁ~H¹ÐÂ.†—£ŸoÆÖ§ÍñüVÝ£ió)V¬ýRÿƒkÏÇB­få¼|¬ëG\ÿÖ$ÈŒ±gí—ú|§Øf³x9úñÙ·RSxÙ@m½"ŸO 3jêÀ¾÷ÉüE•‰î­/Gçj ûö›¡ñMª0RÊEÿˆÔÿp®~¼`üÑ‹—£óÝËBëÎÇ»~–Å즎[/"ó?|K[^ŽÎW¥s+ÈÝæ¦¶^‘¯eTUjòˆO2ÿC§9öÚ–‰¼ýú┿T·ì“‹ú³×mp÷w“úØíŽ;órôþ3Oö¢{áM_Ðîì{ƒÌÿP·Š-‰Œwæåèýwÿc[Ž·ùŸþÂæ“ÊüMÅÃ;x9zÿýT«* УùSéÔLKºwËü úŽ$çòy9zÿ½|Õ6Oè‡Æg9V‰Ùû0âÃpad-qýј!¤ìj/Gçë^×nŽóÄâƒ1n»¡ì1ó !× ëÖ²ë ÿECôÌçåè|G{Þ†U_;£ñ]ð"³ù€Âüé%¨ì{ɺD"*äñrt¾Ëo'€¿Q™ÚzE¾‚*ã!â{¦ç¼üûŒ`|Åç›§é¼|zNµûÝFm½5®¥ díï4\hnË®—‚åîIäZ»"^Ž¿¾£™,ý!Ym½'×W°ö/~!,ÍcóyɆÜÈí_ x9:_HíƒpçÞ÷³}¯M t ó êêNj¢«x‡/’VzRùoó—$9úýœŽ…Àájë•øh¿`SiÃæs ‡ìº ±#Ù|PAbÈÅd^ŽÎç^´Bž¿*xúVZc6óeƒÖŽ<ÿö}©¦>õàåèÇgá‚®0ýÅ¿ý÷ä÷÷íameÑïU7]gbËöŸ&ö¹zmÛY^ŽÎçjÑIw8m<Œn»Lwz¶x}YU¯‡ÔÝïIzídrãS/GçËšào¢šª­Wä{7,‡š±þü&% Ó2YââB<ãSy9:ߟþ‡@´ë lYU fìf×Cá E´pòûÿµð"™‘^Ž~}~ŽP›xã›3‹ÖÓïzÏ`í_1*RXfÁÞÈ¥2¦Ü›—£÷ß :Æ'ãí¯_TfËLÄõRÿÃø‘¦ä¶†—£ó¹èGÂÉêxûGŽj“’Åý1¥þ‡Uƒ Äãd /GçÛ›Üæ¯ÚÆ÷mÿž5wÅù‘RÿCª“ 9ûK/Gç»Ò»v½3Q[¯È÷bàUHß®eí—ú²ViÓ>¼ÿü {B­/ÝQ[¯È÷>.Œ¶¸ÇÖ‹ÈüÝRÉýÐ^ŽÎ+ü 벪­W仜¾öýumÈEZÄËÑù"vºCæÔ_ÕÖ+ò®^Þ>ÊÚ/õ?¬_RDL_{ñrôû_Ëé°¡Û$µõJ|úÂ.4dí—û|È©±Z^ŽÎç´¼¾”[ª­Wä£S_}{Æfí—úªMÒ#gLy9úñYÜTa÷bÕÖ+òU÷]Ç k¿Ôÿ ÉñÑ~7Ù‹—£óÙ½Y¨‹ÍEó‡ÿéHÔ²ß2ÿÃÖÕ $KÜï÷?ïØ/MО?áü›jZAú–±&ýº8órt¾Å¡O­1h|ážË`R¿m¬ýRÿý Tr)ÓÀËѯ/ëµ§uÆ#®ßœxŠ6ïÖ…µ_æ8–@V¤¥òrôþK²ð†€*Û±øàú»éñë3Ö~©ÿ”û=Ýx9:_ƒQ°ùÊq4>—øâÙŸHêÈyH~ÞgÁËñÇ7‡ß¿Úݰø¨Íìa°©óAÊüšiÉ¡á9¼½ÿÒúj`cí%X|p7#³ý˜eþ‡#–éäãê^ŽÎ—¬9_:¡ñÝúшZϾ§Êü™æ®$õHm^Žÿþ` >.Fã»e a!ì{˜Ìÿòµ„¤ ðâåè|§/îƒs÷¡ñåÍ‹‡ÇÓEÿ¦Ôÿ0yžž4™éÅËѯ/Ök º o~Oƒ´p(Mœ_ õ?Tì–@κðrt¾îš«PüsµõŠ|ƒ–Ì æKØóºÌÿp×Õ‚\ìTÄËÑÏ}ß}ûÆ„Å÷“žÀ•ïØx©ÌÿÙ\£­_ÊËÑùL¶éB®ý¡¶^‘¯ö”xÝÎ/sYû¥þ‡y{ÈÁz^ŽÎ·:x6\ÎY­¶^‘/ªV1< Ó²öKýº™Ò:À‡—£ó k1ŒÐÞßaö¡`Ð~`ãG2ÿC¨“yPêÁËÑùžÍ ¥Íth~ ªéy”v8Ãæ{ÉüÁv¡d­³/GçÛ™êŽê8þ‡¢Ë±öKýmÞE‘õñ^ŽÎW»ÌætõDãs46ŒaÏ{2ÿÀÝÒÅÕ“—£ó­ï7Fœ÷V[¯È·mëZð­,®¿•úŽwÓ’äÙ¼ï”ÛAˆ Ç»»V)©âþ¦2ÿC½,r-*—ÿGž¯s ñÆßO{‡AÙh6ßQæHI/"‰-górt¾ÐYîðû A­Dðž?Ÿ]Ú@ëïíÃÚ/õ?dyû’«+Òy9þó‹m*¬º±ï‚C.쵩ÈÚ/õ?<Ê)"yöñ¼/Ó¦d-ƒÆ÷~õ2ø°œíw)ó?ì¶ BG^Ž~þ5Ÿ é+«­Wâ£í»5ƒ›G:°öKýãº%/‘¼/pÐdZÓs_¤u7Ú¾²öKýö*.>ÀËÑÏÛ5#áN …Úz%>øúÒŸ b>™ÿ¡rmý4=/Gçk¶â±.¼÷tµõŠ|]lêvÏdû=Ëü)µB… ³‹x9:ßñ9¶´rO´õ9p§ÂBÚ0NœŸ$õ?ÄP{2¬±/Gç ŸYà Þõ3iêðZ!ο–ú®ìôjÚ†òrôëK »tØÃµõJ|´qƒ«ô7Sñû§ÌÿpÌlždÃËÑûoû¯gÀ/é œ4=úmk¿ÌÿðÌ Xj‡ðrt¾î'ì!£ Þüë ôP¾_ü>/õ?äŽL%Ñ£rx9:_щW¤¶^‰V1º§ÂÙx¥ÌÿàxŸÎMæåèç_݇¦8q©ÚzE¾ö¦ó!w'k¿Ôÿ¿1™”½ñàåèýw±·Þç­Ä⃂¨e‚8¿Gæ0*NÏçåøó{âJ!Æ ÍŸ/ JáZÓ‡¬ýRÿCYr+š—£ó=ºüÎLNT[¯ÄGM´‚'Ål=…Ìÿ&HW-/ÇŸßÓ6"&ŒFãsë• {ÄùƒRÿƒ¿yî”ÃËÑù6Þò¡Æ‡çª­Wä‹Ë  µ_‰ó[eþ‡Ï¶¤tz /G?>/Lƒ3ÏCÕÖ+ò™;à¥ó0Ö~©ÿ¡öS_ms^ŽÎ׿A·zì>µõJ|àêóI·Å‡Í–ùÞ¼´ªÐðrüëË…|j6+Gm½5îAöûGê8Rl »jy9:_ä¢ °Z`ñÁñB3ÙȾÈý7S…¹­ô¼ýúb—Y‡õÅó9­¹D/ßç'ÉüÉ.ƒ=/Çÿ>±1 ˜ª­W⃯W®ÁŽÁâý[ê $ðrt>ݨ·ð¨5Þüi+Lh•FWYû%~ò¹Ô—D´ræåè|¥oŠ`Ó‹jë•ø¨•Þîýë–U}½x9úù—`4Ò¾àíozñJ —A’=Þóµíà? {²èŸ–úlr v­½x9:_ê+[«²Lm½"_FÇlˆõeû©ÉüJÈ»†¼/r¨ÝT¼ý1SZýJ†aí—ù¼t¤´m /G?ÿžÝ‰Ç‚ÕÖ+òUÛÖî}×KýºÈPm[ž—ãßß7¹é~_ç—Û¼›nu±¸¾Vê°©R"˜­sçåèÇg¥ ªÙ„÷}ÐnÎ^Z«Àžµ_æpt ~Ïðrôþ»4¸ô¹º‹ŠµùGÿCõG6Âw½y9zÿuk£§ãƒÑÖQ÷ŒtÑ4q|Eê°îîOvOtçåèý÷ǪLøá’ÚzE>óóû`Ç>Ñ+õ?œè.üð!—£óég­ç·Ðöß…ð‡µ¨ÕÏl>®Ìÿ`~1‹ìê`ÍËñ¯/§+B„…»Úz%¾?ýGÙü™ÿÁ=È ´Šräåè|-;}€§#<ÔÖ+òõéeI-‡Šû£Hýg÷Z,‹çåèÇçWgj¡¬¶^‘ÏÊo#m¾QÜLæ°±êì1ðrüñ1mmØqÏ/S7زZhYû¥þ‡ù3ò“ëá¼ýøì÷Šëiþß|>ÅÙÔä‚xüIýsC Üwàåè|C¾zÁ¾°jëù~]ùþ‡Óãƒãƒé¼ïÞ•×´îÌjëùÊZÇPÇïç°öKý®þÞ‚m¿<^ŽÎW¥r—¢­o¤­]¥¦CØ÷b™ÿá‡þAZG“^Ž~}qî¶^·ìÚñ ý7htz‹~™ÿ¡Š·Ðþ»t^ŽÞæ ghã†hó[i“èÕ´;eûÊü6?‘ÈW^ŽÞ÷³<`Dk¼÷÷JË«ÂòZì{£Üÿp<]æÈËÑûï§–éJM²ÚzE¾©OÍhè3ñz)ó?t0%ç­rx9zÿ½ÚØ6÷ÄóO[&T…ılüH¥MÆ´]ü×oâyBC>-qäåøó—Žv‚›)¥X|0æf”m`ëÙ…-+G kZ‰ëßÈިˆ—£óí³VíõQ[¯Èwýà!ˆÜ›ÆÚ¿äiuág[`|—£ÜȶµÖ¼ï ð»[¬¶^‘ïU-7ˆ¬/ÎÇšÛªšàL(<#ØŒ ååøãÓDz©/ÞóÙ·õU·°óYpq¯!8¸œ`¿-z·"×2óx9þúŽf°ä1š¿ ŠûV„#´¬ý+‡]æëÄõkO›’G#x9:_Ȇpç4Úûì7ó†r{ñý®Fç:«ãìù’Ä•xhK®éy9úýì²ÀÀGX|´…ݰùgq>ÝÑS„˜f?²ß.çÈ•_y9:ß²OZZa÷s4¾ µ×híì·ðS‚§Û_I¨qª éÏËÑÏÂõ¹0£ö¿ý÷ä÷÷×`m#ö~.ĤP]•Ñì}–Ø(Ð6ßïÆËÑù\œÐ~kP[¯Ä£bÇèNWYÄÚ¿¦ôª°)0Šñ¥^± ×k[ðrt¾¬-±ðfŒÚzE¾·ÇVR³-l?'Á¿÷aæs=ã[ì©'#zðrt>¿öÝ ÿë³jëù¶L3BKXûxç g/³ñ\Á¶¿¹\¨ååè×—áùW©M:Þú±™ó;ÑVGô¬ý‹¯— ~¢Ÿäf³<â¹-Ÿ—£÷ß Óa‚­=¼0–Õ×ÇIý#ל¿­oüû퀓Áxã›#Ç'AÞîm¬ýRÿÃÊH72¬G /GçÛ{ö(ÌŸ…·?ß·ý{ÖD²ï)2ÿÃÉ—rÁÕ‚—ã¿?4Í„]ŸñîÏ?ÇÁyç‡HýÙO ‚ɇ^Žþ}בZ¯Ò¡ñ}°üH[Ìcã•2ÿÃM{r÷r/Gç‹ÝÖe-Fã»xª$¸‹ów¥þ‡-ž>ä¼Þ…—£óEŒ½ ™{†£ñ%þ\oo³ù–2ÿC  'ÕF»ñrôû_“æÉ°¾ø‰Úz%>ÚæûWÞB\ÿ&õ?¼é§%Ry9:ŸÓˆð%m~+¥£iµjF¬ýRÿƒÅõPr ¡ˆ—ãÿý¾ÂRê¡ñU{z ŽþÌöc•ù*xj;ëþ{þ{óºØ]Ñjë•ø ý/ûtIöì}RæØ0Úƒ\jìËËÑùL̓—­ÒÐøÎ»¥SϬýRÿâ¤ÿ#W^ŽÎ`ñz?´Gã ×t…I#æ³öKýwÝ,Èí¼ýúÒx`eZ[ô þù¨óòj´ùMÖ^¹ÿaš)ñ׸ðrôþKŠïd1dø¯…ˆvl=¨ÌÿÐ5Зì½âÎËÑù5À¯i-,>jÓ¯ 6~÷‘ùž‘·ýy9zÿ¥9Nà/xþ¿?ý‘âþóRÿCÒûò1À›—£ó%×ë_ÜCã»u{µ®õk¿Ôÿpëž5InËËÑùvítƒ…ãüÔÖ+ò]ì a=Åým¤þ‡Äñäh`/Gç;} à\œïÁ¸×ðHÏö«’ùôq&öóÇýÇý‡.@ô 4?%µ ܇Š~<©ÿÁ4 ‡œñÊçåøó“‹ZB±m*ß O Ô|Ð Ö~™ÿá}<¹2;Š—£Ÿ'Ú-†½+Ÿ©­Wâû?ÿÃBñþ'õ?\®¯×Úì÷âåè|&_ŸIý+ù’u ¼/º¬=j®¶^‰NøúALO6ßCæÐ6s$ëoÅórt¾Úþ0ǻ߷ý½7èXû¥þ‡!o‹HG+/^ŽÎ·¾•3ŒØ‚7þ°í½ Ìs×wHý)s4äØM^ŽÎwªi{ˆ B›?wO^”0ñùDæxYD2úhy9þóõ¹O[^‘ïô [Pvàk¿Ôÿ+õ?Äs×:öâåØ|º¼»Fºæï±øt¥>èb:ŠÇ§Ôÿà—™J’ϸòrôþÛøn+ÜÜ\¢¶^‰ï›{Þ¥‹~©ÿah†)iüÞ…—£óýÜ¡ê/©¶^‘Ï÷u8´ÿ,®Ÿ–ú¶ms$ç-ô¼þ`² µjWQm½­XJíO2ÿ‘Ìÿ°6HK¦eZórüã³ß!;oü}OÊaXúZ\?-õ?ôš•.Tkµ”—£óU …o©­Wäk›gD5›_ õ?\˜Eª–ðrt¾×ûÆAxåÓX|´ÒúMph&ÛDæ¨4ÏG0ñ×ðrôû»fÔ\¸uyÚzE¾Z3¨FØÂÚ/ó?ø™ ›ØðrüõÝ#i¥öxëWž•N¤õ¾fí—ù†CÇx^Žÿü’š «ñÖ?œ¿â{ý–ùòÇêÉã1Z^ŽÎ—9údu Q[¯È÷at_øpFÜ__êØNIÏ—^¼ýük>çWH?R¦¶^‰¶oš ·{²öKýc²RcqþõÞÿàµÖt²V[¯ÈY¥5mñž—Èü&7W &‰é¼ýø¼UÒî,Ö¢ñi¼æS£è¶¬ýRÿC•VÚ:.¼¯Ù ºð[xûGv©þZ·;†ï8æ`/òrt¾ã3ÇÐÊKÑö/‡ì±ChÃ4#Ö~©ÿaOUk2’äñrt¾ð±þ‹ðÖ7~ó‹þR£k¿Ôÿpq­›P#¿ˆ—£__j4$t˜_¡Úz%>ÚèžækÌÚ/ó?l!›cŠx9zÿm?·üÖ íß'G•B¸öoû—KýË­…jÎY¼¯Gçöñ+žÅ«CøÒ’íÇ#ó?Ü¥¡$ü¡–—£ó[ÚCLÕFX|´JW=œLç—IýmG‘·Ë¼x9úùW÷Q:$º„ª­Wäkß»/ä~¿¿Ký¯ûiIù /Çx”êýÛOv|ö´§–²öKý¦“=„Š•y9þüž±3!¦ÅµõŠ|¯ÆkàZ}Ñ+õ?|‰Ï!W·zðrt¾GsÂàŒíúBMƒ'µÄý‰¤þ‡oë7ë3ðrôó¯û¼ˆˆÉV[¯Èç°âv‹û Ëü_Hþ×^ŽÎ·ñ&Ô8o._\¹Ñ?úªT'E[ÿþ÷?¬Í…³ ŒÐøÌà¼h/Îï”úê:i›ÝŽðÈ7L«\‘V~*Ηú>­Ð“ðç9¼¯tרl‚·þϪ{4ì?ÈÚ+ó?¬[e*Xú¸òrôó/aüBH{…Æw±¤NŸO¤þ‡—c]sg /Gï¿”Ó/ (µ2—£óî]½/ß—Ð:°ýñú!õ?ØvJ%7ºÆðrôãÓøf5Húq¿Úz%>j;¡²g‰ó“eþ‡Û¤áäP^ŽÎ—¦Û aÎz4¾ŒàÙÄæãËüÁ×¼Iq™—£óÅ{Q;×44¾¸F&-fí—úZ½ó"ŸJ³x9úù÷|t0$¾Ç_©6!îÝý©RÿtMÕ6ªÈËñïïiYºßá]_ÆN¨[uC\$õ?Ô?¡Ìiy9úñYiMÕÞÄâ£vOÜií•Xû¥þ£N®$`ò=^ŽÞ—[/†>$Rm½­©³œÙz™ÿ¡x ̓Cy9zÿuÛmOÇ??£¶^‰^šAÕeóUeþ«®î$nµ5/Gï¿ü)cÑüÌÔüéZØi&î¯/õ?œ±ˆ:æGñrt>½þ ¼ hû‡@Di%juWì?©ÿ¡êüT7Ń—£ŸfšXϵP[¯ÄGm‹¾ÀÑùŒOædž ´Y“ÀËÑùZ¶= Oôh|}ŒzÑj5ÅûŸÌÿ°c“Ðlx /G?>ß 3P‹á.jëù¬6~ ÍÛ‰~5©ÿ¡Wh‰P§G9/Gç+ª9vAÛŸÖ}ä ™Ÿÿæ7’øl²Lg†òrôã³ï²Ùðp\UµõŠ|>¥/¨IÂIÖ~©ÿ¡l éÕ?—£ó ¹–ûvÙ¨­Wä›äo 'ÝØýFæH+ÑþýOOѺ“ÑæÏÓ²ר£F\$õ?ÐéBýS/x9:_•ªv;ïý¡e^>5­$îŸ!õ?8 ôÒ:öàåè×§·wuË6ÏR[¯Äý®ë6ļaí—ùj…R‚x9zÿ™ë‡ÐFŸÐö¤MšÑî£ÄûŸÔÿP/´œDÚ=æåèý÷ b#Qïù¥Òâ àÿl k¿Ôÿà É~šçÌËÑûoðë?hÀ×@µõŠ|SóÌih¼ ã‘úlš×&gWYórôþ+€ý°yîWµõŠ|–G} ±M2kÿONû„1›™ÏƒüíK> tçåè|ÝÃÍOˆß'ü×BÙ96ÿS[xJ4ªËø¶,!‹›òrt¾£‡õ°ê-ÞüÏoþ‡(ñz²¶ C´l>¹lïBvŒñáåè|W†ø­F[ôþën¬ýs›' +&±ýeHáfo¡n”/ÇŸŽ8Eíö U[¯ÄG+µ§—cíï2DZ4;Î~WíE Cbx9þü¬:F°äîojë•ø ¨Ì¶¿çËt æåˆóÏÖ]M&»iy9:_ÈD{¸3ô£ÚzE¾oþ‡²;ýÖÕ©r]g±]Ü¿ úD12sàåè÷?ò>—¡­o¤ýž…Ãæ-ì·ph[o!Ös1ûM‘‹K´¼ÏÏÈ—VX¿Gm½"_pý‡´Fñ{àR?aœÐ˜ý¶ ¬MVµÉËÑÏÂô10}ÞþY–ºÂšuâxXô‹L]Íî§ÿúMê÷Ò6¬SÄËÑù\{½Ó ü„Å£ÇèÒž²ùðÂê?‚… •âþDgÖ„’õóx9:_Öo…ß¾1¡ñ½ýÚš×3.kef.òd|KõÖdä3^ŽÎç×a5ôuP[¯È·åôZ˜áÊÞ„#µ; §¥±ß¶{âIÆßî’ýú2¼ôµÙ·ÿÄ̈­´U[ÿ.,‹|#,þ‘½/‘ëÉÄëB /Gï¿í*Ãø0´ñ(¾Ú– aû‰Êüc‡8“{én¼Ï%q&œìÚQm½"ß(ëW÷X|•ú†ø“áÓ‹x9:ßÞ]Wa~x°ÚzE¾Ó¶î°¦ˆí'(ó?¤Ï'g«Äðrü÷Ïlˆ­¯Gã{1­=¤§°õØ2ÿÃív‚I/^Žþí™K­sðÖ}ø±m±½Èü·7ÆÜÔ^ŽÎÛ ¬¯] ¶^‘ïâ—3à-úµ¥þ‡õ—=É™{z^ŽÎq£=d.ÊDã;²µÞÚLeí—úÖ&hHe÷P^Ž~ÿk²a4l˜÷Tm½mse8„÷šÆÚ/õ?³ gxðrt>§°sðå Úþ‘”ƶ¢–)ìz(÷? ö"‡œây9þøß²dÛY]m½"_µÛŸ–ùL£íµÝB|x9:Ÿ]è~\ÿÃκ¤9kXû¥þ‡=ÈÅ-©¼ïÀú)ðr&Úüd¸0Ò‰šøþÀÚ/õ?è-ãÉ ¤ ^ŽÎ÷ÍÿÐÇí ÚzE¾ðê“]k¿Ôÿ}Æ@FÆØðrüñÍ­ÁÏÍKm6Ì…MS›°öKý‡Oy‘}Uþ{þ‡Ô¢j°±.šî}ÿôIeí—úŸ;“Olx9:ß±?¦À×y{ÕÖ+òÝšw—ZÛIJöKý™kLIÊÀP^ŽÎ·+'vУñ]H}aß³ß2ÿá‰r¼i /Gç;}{'œÛ‚ö}÷ÏýõÝfß§eþ‡I¶n¤±i*/G¿¾XŸù1}7añÑÏmáÐ{qý˜Ôÿ`²Ùš“ÊËñç'_êÅq3ÐøíÎþû÷]™ÿ!w©;¹ô‡/G?>O¬ß{õÔÖ+ñÁýûõáJ{q©ÿáJ“@­ÝX^ŽÎgæ¬ 9a§¶^‘¯Öš’ô?Ìr!‡~´çåè|nqpyÁp4¾¨9máé06BæÐE'“VE¼oX7;ph¶oö5cÐ&±õhrÿÃòhk(/Gç{–r†6-ûCm½՜ΡŠû›Iý!Z=YkáÁËÑù¢é+XûÇVµõJ|R]1=Äï×RÿƒË1Gô2†—£óÕöüf¿ïŠÆçh6³ù2ÿƒû â4Ô—£ó­Ÿ±FVF[¿ Ûʵ0÷•žµ_ê8~2Ýjàåè|§~*‡(¼õ+w÷\„”ÑìxùvE{“Ìô@^Žÿ}Þ)î@[§Ç„ò¢ŸKê8šêOñrt¾Ð„…ðûÄjëù’Öo€­©âû»Ôÿ°á©ÙÙ.ž—ã?Œ½ {ÛkÑøŽ 5†swÙ|=™ÿÁÿ¡ ©~DÏËÑŸ¯ë„”BdD]µõJ|´qãõ÷JÜ?_æhJ"L²x9þ÷Ak~‹7¿ü‡UÏÿÑÿpêuI”ÏËñßß[íƒØÉxãçZØÁù‰Yû¥þ‡=ãœµŽ‰¼›O—ZU2î{µõJ|ºÒ1«uÑïÅ÷;™ÿájUÀËÑûoS÷õpsïtµõJ|°Çá.¼ËÇ7¥þ‡þÜIó«Q¼Ï£Úk¨ßÓ[m½"Ÿïª¾ðÃ"ñü“úBÃòȵ!1¼ïuë_©• š–VŒ›M¹mÿë·Üÿð.™ÌvÐðrt¾àbw[÷왡…e=Åýù¤þ‡¾£ò„*5oñrt¾êÑ¿ÂMK¼çëþ‹?@긼ޗ¬ÉÏâåøÇgÊûpDm½­”î }Äõ‹RÿCÕäxÁtX8/G¿¿k:™Âí•xþ•Ú?´ûöÏÚ/õ?ܨ¤ŒGDñrüõýCi%‚÷þþü·C´þѯ&õ?\±'×F§òrt¾˜ÛŸàoû™ü¿ùÎO/€½]ÄïGRÿÃãÝÈݶé¼/sŽ-dYýÛOÆ÷¾ä!¼Ÿ;‡µ_êya ]&øðrôó¯ùû p¾ Þú8çgöpó¼è§”ú<ÇjÈ—¼/PøHkºž@㋸I[4góidþãö¦B¥eɼýø¼Ý{*®ÿ¡o65*ÇǤþ‹ Gm3^ŽÎ×ôÚ"]Ø>´õáàò©.ö¾Ø?RÿÃÑ‹Eäk¬ž—ãõp¡•‡©­Wä»Óðm8˜íG ó?DÇëÉèù¼/¼IІñjëù’L_ÃÄ:¢¿Xê¸jš#X/ÐðrôëKÁ%tXHmì½”NÛÉök”ùö—&;ãy9zÿmß–~‹±øàd£6®¿ïJýóûäÕO_çåè|=Z®…Œ3£Ñø¼¼ |ÕhYû¥þ‡{ýâÉö( ^ŽÎWlœÑ_{¨­Wâ£Uúú鉄µ_êpôô!¯›äðrô󯞽^©­Wäkß±äŽç_Iýo’£×gx9zÿ]´˜ š¡íÏ…Ki5O¶>\æ¨f*¶¼ÇËñç÷Ì*èËÉjëù^º–ÀÕ ¢EæÐ;’ì…ž¼ïQ™-œñx­¶^‰šxÖ€?ڈǧÔÿpî˜3±¯­áåèç_÷T3ˆ;‡Æ×ÏI{Šþ[©ÿÁou y[+”—£ómÌëFw¡­ï§q'ÖYhÏÚ/õ?Ôì8›”ÔOæåèÇçã›Î,©©¶^‘ÏÜÚ ^lýNRÿC½ Z­Ý\ /Gçkk4W·zÞü¥žšAºb曕ù^·p̯§òrüþFÍzë°ø¨±çLj›ñµ_ê8²¹ˆ¬èÃËÑù"ïõÅqxþŽÝÌ ¤šèw’úŠw… óR4¼ýúbw+=jµFm½u*M —ƒÅõ›Rÿƒ‘‰)‰ìäÍËÑû/Û0FF¡ñiÖúÁöî¢?Gæø¨&åðrt>ÝìÝßOX|0­½Ñ7Çk¿ÔÿPvªˆ„Ÿuæåè|¥!uas¼õV«ªÀþ÷ìïKý«ç9óy9úùwxò88m‡wþ]5ðý¯æç ¦×órôþ;Y9Š+=T[¯ÄÊ ÿeZ©ÿÁnŒ?¹ºÕ”—£óÚ¥ƒ¾š_æÛ»;D-×·Kýv·œÉÍDg^Ž~|¿nISÑæ×QÛgá%ŽßÊüsˆÖ‹—£ó¥59ˆë˜æ±;Åý…¥þ‡Í/ôäCU^ŽÎSËÚM5CãKɸDbØx‰ÌÿÐúÑJòÑÇŸ—£ŸϳR ñ5Þ÷Ïj÷¶$±öKý.~AÚ6½õ¼ÿþ~1E·Èµx†éµâü©ÿ¡A5ƒ`¶Ï‘—£Ÿ•ûQMX¹Úz%>j÷:Öú$î¯/õ?˜Ö8@ò†ðrôþ»”ÚútÆ_)Ž©³úïcí—ú¬KBÇ4G^ŽÞÝæÎ ã½Ñ¾ŸÑÁ›C颠ç¬ýRÿƒõ¦Kd÷€^ŽÞù6î´Vm½"_åÇþÑÿ¶ÁGèãÊËÑùôã¼áÅ5°ø b{ju›­–ùª\s!±þ9¼ýø4«½Ñö/ ¶ áhûþ.ó? hí+´Î4ååè|-·ÁS0V[¯È×§f0µÜ.Þ¤þ‡F+ ÍGÆórôãó͹ÑÔb`µõŠ|V1Ãhó9âü©ÿÁ5Ø]¨ìÏËÑùÞ};N£}_¢u—‡L½ø|&õ?,v*Lަòrô㳯{!^D'Î×ÿû†Ž-…}ç¢ñMúî+œ¬+¾Iý§k„J?¹órt¾ûm)­Ûæ_YØBê8X¼ÿIýB®½P¿¯/Gç«Ry&äöš„Æ×rÁAjòŒÇÊü?<7Õ¶ýÛþ`ÿqÿÃí1¨þ‡þºKº ½E?ŽÔÿÐl¤µà|‹—£÷ŸyÌBÚèm¨Úz%¾ÿó?Tç¿Èü CHdJ7^ŽÞ÷Ý-`Ä÷]Ðø*9®†åâúi©ÿ¡ÅÒ…—£ó]Y8üʇ£ñ½|Ò "‡³ùP¬žµ…åŽìù¼] l:ûórüù/»Ò©ÝÝ µõJ|Ô¸]8íøží"¸ô|$´ðcûçÃóÈ•Ûö¼}‡³,¹¶~ Š=+Âö;âõqù…›Â²/âþn¨žÜo^ŽÎÒ:Ü™‚·~e+7(9ÅÖÛèêmÎÕU^lÉø¶çúãº^Ž~ÿ#7Ú/Ð6dë…ýoㄸ¡l?WÆÙó5ÃSw:Tô-{6LXïUñ]âBnýQÀËÑù²º†ÀëçhÏ×P´Ýê_ü~» 3}ÅýßÑ’‘G½x9:Ÿ_ocèÿ+Úü,زá̰ýi×,N­} öç]H†½/G¿¾ Fm~;ŠÅGg¥ÐVçÙ|uÁ¯à7ˆýÿ‘k.dt =/Ç?>×Àx_¼ù×Eï*ÂÒÃ\¿ùy©ÉåÅËÑùº¸õƒ“}ÔÖ+òì}ò¢Ä÷w©ÿaÅ(2Ô!—£óÅ›×…ùWߪ­Wäû¶ÏšD=k¿Ôÿp꥜w°æåè|—Ë2a×ÍejëùžŽƒô"q}ªÔÿp#ÒG0ñ ååè|7]æSë²cjëù>øL -n?dí—ú²Ûç“ûk\x9:_ì)SX·"ߥå} aÖ6Ö~©ÿaͦxraz/Gç‹È= ™ÚûïÉøŽ4éoןbí—ú熒J}Ýx9úý¯©&6tÀ_ikéa²öKýýHêü ^ŽÎç”q¾ä' ñÑá9Ôr¡k¿ÌÿpÓ@{ñrôã³h†Â^·S[¯ÈWíÑD8VM¼¾HýšXomÛ… ¼Ï.ë¡.vî3,>p.tÒýÍ+ó?l¨ïA®5rååè|æéáÅ×,µõŠ|çÝҩɾ¥¬ýRÿÃïuC‰»ÆÀËÑùVä·ÞíÑøÂRm`Ò-k¿Ôÿp3 ‡\™ÃËѯ/µY´ÎÛjë•ø¨ÓëÚ¼ßÖ~©ÿaïè²|S:/Gï¿ÄªI°âí#,>¸^z"‰ëdþ‡"g²×Á…—£ó5¸w6©„Æç2îzÂ~Ëü7ßøŸŸ;ðrüñÍ Áàg©¶^‰Úô ›êˆûcIýûÿ‡½+o¢øÛIï––«X® ÷Ý>énf ÅДS±„6…`›”$9r(( ˆå*ˆåP@)—X@•‚P䨠\"ðG9TTüv“ÍnºÛé®8mAç}ž}f“_&;ï\;÷{9†úð ÊŽ=ývÏÞ æÇ`[Ÿ Î|>ôæ×ÃKõr†R¿¬‡²cç·½_oð×WøôµŸXƒ)a}XÿáØù[ÔÞË7QvìüV-ß&œôÆÆïÀ÷íÁ¿>übý‡Í­£¶ßFÙ±óÛ³í`ßF|ý÷ï¼’Áù«üyUý‡ ÙT£QƒQvìõKðô×@~¾óM®ƒú$býßÏ.Q{¯ÏBÙñ¯O^ÕÜ~5@©Y~ÉßÙ:™¿XÿáÄ–®Ôçï& ìØóçŽK{ÁûŸbÿß.êÆÎäÃ/Ö(išÓxû,”;?ï Gµ WÂÆ¯îöLí»Õ…óÁ$úÚYԆġ(;v~3ÿ8 ¾Ü€/ýòZ¶—ƒùñ0‰þ4\¢ZkU(;v~ƒg€–MOcã7úÇÑ &“ïïHôV|–GýàoGÙ±ó»÷lþ%¾ó[U§_…U|x%ús¯]¢^_;eÇÎoeQðúû¸ø÷tù…õgbý‡ÿk;ŽšsÚeÇίn÷É`ÌI 6~í[‚¹[„þ‘Xÿá™Áƒ©ð÷/¡ìØù͉8†´OQê_–ߊþ—ÀØzùð‹õvÔïJmó²£ìØù}2cÈŠQÿáÆ`°³éB>übý‡÷V–P_½”‹²ãŸÿè8mzàÿ“ðÛûÚð§G ~±þÃÇ Š©­5ú£ìØùåæ.“žIÃÆoëñ»`Ñ¡$Ö˜×)˜Z~j;ÊŽþ¡Ùmð~§þ¿2çöåû[ýûñªîøB”{ûºÞÑÑ`ÙêH¥þåøÁ¦"Á{F~= Dÿá×g¨ÅO¢ìøÇ/›kd(õ/Ë/jêPd;Ƈ_¬ÿ°÷× êØðÁ(;þþûÔn`õ¡%JýËñû›²ð|øÅúë¯fÄ„MŒ²ãæ§=û\#íÂØæ´¿oÜ©Íq—ÿ,Öxùì=jg ÊŽ=ýúñj\üÀÚ·•«ÿøgKªÕì|”;¿Át-Ð@ƒm?÷ö2•x›¿Xÿaå ª¨Eåé?ÜX·Ö ¦?æXŸÜ´¿ÿ[¢ÿ07UGÙÞ BÙ±ó{sÓ0,Ûù6à½yÀ”.Âø™XÿÞ ƒNïDÙ±ó«y3;†OŸ«ÓY5T½)ä?±þçqÔ«G²Pvüë[ß KNTê_Žôm6~͇_¬ÿà7tí7ñO”ûû]5ªœ¨…oøc…‡Ø><~±þC15˜ö ¼…²ãß0ø:ôµcÛ? ~Ò 6´ðçqIôŽfPųPvìüòwµ¯­?¬Ô¿,¿Ï–5ïaçÃ/Öønû~ª$!eÇÎïh÷pôFslüî„Ç;üx­DÿaÁ+—¨nSrQvìå¯Õñåà³6Øô;`ÄÛ=À×nó·bý‡!½Bh•5eÇÎo–í¬#œ‡ôù-Ýõ*l=•?/J¢ÿথ½NÚQvìùóDçbpê^ 6~ªçCõ.¡}&ÑX±=¦‰ÊŽ_ó㻵‹-ØÎ×ר>‹V}_„²cç÷ñ¹{0 '¾þßÉ•1°ñóSùð‹õÞñ°SÏí÷AÙ±ó[|µ#KԿ,¿-Ãæ‚á~?ŸTÿáh]ëY ÊŽ½~©]Í NÄvþ'|"â44Gªùð‹õ>ˆÊ§Üš‡²cO¿RÀTºžRÿrüÀÎw·€Å'…õ;bý‡Ôûé 3CPvìüz„¥#?ø+õ/ËÏðÓZpÿ)¡}-Ö8Þ*—Zqv;ÊŽßíCA>(Qê_Ž 8r|²U˜ßë?´ýè:uÓíýPáúQj°eÑIlü"ÎW§>übý‡¾Ë¥Õ“òPvìé÷ù©#ànííJýËñ7ÒwÃP(býß+i´Ç›±(;þõ=¡±àÝ+5°ñ»¶ú{ph ?Ÿ.ÕØYD}žjGÙ±ó;ïÑìÝÚU©9~ÐëòoàBG·úS¤ÿ°mu!ÕäZ ÊŽ½üõ¨ë –Nš©Ô¿,¿„Éá`íŸÂûO¬ÿ0Mc§®×œ…²cç·À³z}ÙK©Y~k=ëõÞßbý‡Zƒ©»fÊŽ=^lyìýʨԿ,?¿ÏÛ‚«}ùó$ú!óŠb4F;ÊŽ_Ç7´3»íWê_Žˆ-ñÑ.ÜÇ÷G$ú?« èjWº¢ìøë—Uû¡_﫸øA¯nÞ°q+áü!±þÃVï{Ô‚û*”;¿e/D‚—MVê_ŽØQ«X¸Yà'Ö¸yôýÒp+ÊŽ½~ir£n}ëU¥þåøÁðO:À/oäÃ/ÖP5ˆ¡–·@Ù±§ßÉE™`úÀ›JýËòSõý¬x…oïIôfË¥[5¼‡²cç§Ý] ίýB©9~Àük3Xm”°>R¬ÿpçãXj©g”;¿ßIoÁd¥þåøÁZ{•«ÿ0¿µŠj÷ÊŽ½üìï‹UÿáË:sÀîüùý‡ŸÚÑ~nãO®ÿP³¸mi©Ô¿?pa×ÿ`Egùð‹õî¸K•\¼‹²cçW4#¼(Ìwüc~÷§®+Zó_bý‡¦?„PÇŒgQvìùÓëì `ëcøÚ×Û_'c…õYbý‡ä¯C¨FC£ìØùí~ÁRcãwä§–åë?¬í@ýödÊŽ_þÓñ°Éè»JýËòÛùçv#~±þCÛ«¹Ôïm»¢ìØËßU¯õ`ËŸ÷Ooù |û$ÿ>•è?tëm ñ+DÙñ¿ßÿŒÐ¾Ø]ƒ‹Ð8¡}m•ð~è?ì¹DûÏBÙ±çOßQÉP5ý¥þåø9õ®óñ%Õø ˜zY5eÇž~_Þ½½„‹¸Ý\¬&~¾F¢ÿPsÇ-ºí ”{úuŸþ Lo7A©9~0¥ÕD8és;~‰þòPjÍöy(;öôû^ÛL§°¥ôßhïìöljõöý1”Žú8eÇÎÏž9\}[ýÉžýÉž±Ï‡_¢ÿPÐ’z÷ ”{þôÓ¨ÀÒQøÞnÝÛúðóùý‡”B:´QM”;¿6›€ËÏãÓ'é£5º úbý‡'žœG· ´¢ìØóçÍ)]`ÛùD°Ö¢ça«éBý/ÖˆË~ƒ~lâ”;¿Ÿ÷Oï̹¥Ô¿,¿ú§¿Gãøö²DÿáÅ“´Ï±–(;öüŸtœ3WWê_–Ÿ-®zôGÄú?ܳS1#3PvìüÜë7àÓW3þ> ìÜ*ÔŸbý‡OjçÒ^ßœEÙ±óûÖ{¬ÿl¥þeùý~Ö;4Ö—‰õ´¹ÓèÇŸˆ²cçWíÿÚ‚o^ŸªÔ¿,¿6Ÿ'Aï‚]|øÅú×µŒéà¦ORáúßþ¬Rð8.~ i®ª\ý‡6ýcèðå (;öôó?6-Æv¾lvóì¾CX)Ö¨?£„ZöR{ú}›»¯þC3/0í>ÿ¾–è?´hZDˆ.AÙ±§_¿ÞÃé{bãg®ï søõðRý‡ûP»¡ìØÓïz§&à­ƒcãW}/[…ýƒóêÓ©'„ùÍtUuϯeÇÎï)mðõÎßqñú ‘àÃüyÓô’y¯Ós_çÇ7©…oúPcCJPvìü¶•Ì3;ÎQê_–ßWnËVòû™èISRèÁFáüÝ£-ÖQyÂzH±;¿ƒÅ±`šßúºëkÚ€eZ~=.=†ºCÏøŸÀïfR]·Ó~”;¿ #VBM]|íO¯q/ÂγøöݹýRº5ê—‡ ©¢y]Qvüû;:¬“b›¿wÇ€wžœÎ‡ÿ•kÑãc‡º>Ss) u~ŒeÇÎo¡E NÆ·þlC|Gðg®ÏÚië´^^x~Ë«·¤æ% ìØßÔOWÁ¬¹øö?$¾â fðý;zë‚DzÕkzþs·§ò©/¶„ ìØùMm]=>Ð+õ/ËoÞ™…°ö´E|øû­I§-åûwtí -õÚ“#Qvìùóƺ5ÀÒj‘Rÿ²üªG³¿®Ë‡ÕOi½êÚ]Ÿ©Çߌˆ K,BÙ±ó‹µoÓnîÞF©9~à¹Ïj÷Öã÷KÒ¯½ñ=wøxžß¾5VêxÍ ”;¿âÕàæ‹Rÿ²ü~ž±úWWóáŸæS›Îi¸‚ç÷ÒžjHãÁ(;v~SSÞI†NØø½{ŒúIÐçØZý ú³(~} Ýà›Xêó°”{ýòLIòv+¥þåøA[äxض/ÞýòkÁôËmøýTq—*ý@0ÊŽ?>ݤwÀvþ'¸ï &ßÎ'ë?<ûMWêÛKV”;¿®Sº‚]†\¥þeù ñÎý¿F¢ÿ0ã›jй(;v~ëªmãO`[ öì¼ë®$ÑØñâ~j_”ÿaËj°ªÛü-¸ÚvØ¿"†¿Xÿáë'biŸ·‚Pvìü¾nƒOWSê_–ŸCÿáß‘è?œš@•]‡²cç·º]-0lÁÆïËaÊÕ˜¿.ŸúôóB”ÿúÝšÛØ=ÔØøm±–€›äñáë?¼BygÇ ìØßÍ6ôsŸ×ãâ;ÞÔ–Ú'Ö¸fɧö¼_„²cç~ø¸ßþØk?¬¾N8?D¬ÿPC[Bøù ìØóç­½mÁâO°½ÿ`MÐ lÛÆ×÷ýßå±1GµDÙ±ókòASíê;øÞï‘çR´[í±þüÛ1TQjʎߦ³àê|¥þeù}6+z›wóáë?¼ÔÍN%]¼Ž²cç÷ê¡ nßÿŸ„ßU702f~±þÃ7K ©âŒ”{ýòDdX÷ÖN¥þåøÁˆg—ÁVcúÔš­*jZõq(;öôÛÒ!L÷Á¶¾À1~½ä7a±Xÿáÿ"}¨Õ?¡ìØù5,nfÕÂÆïÿööòçíIõÄPC¢U(;þùÍ=`j÷±JýËñƒ!£‚S…þƒXÿaS»Bjþ”{úíóéqJýËñgøƒ>âõ%úÛÞ ¢~ye(ÊŽßöÁ…௳jlüNDƒÁ±Âùóbý‡£µ¦QŸLÔ¢ìøû ì`âGøæÜè^JL¬ÿðÑ‘ jg—"”;¿='V‚}öþJýËòûîì³àB-¡þ”è?Ü¡šþ„²c¯_‚§¤ƒüZqJýËñsê?¸ï&ÖðÙ»ŸÚôõu”ÿúäõ¿€Û§±íß„É;gC¿Ù¿òáë?œ¾yúrIÊŽ=îØÞ5V©9~à»î‚ƒ} ±þÃç½ZÆ´™„²cçç£^®]8u¯Rÿ²üûhßý”ÿ,Ñxá-µ¡_1ÊŽß,Ð |¹ [ùynË„ý·bý‡^m¬T›>PvìüDÄ€ãð??zìvÐã†Ð¿ë?,Û;Ž:·H…²cçwyë»°yÑz\ü j@}%ì?ë?äÆo§æÌ)BÙ±ó[yÀ¼nÿ?°ã¢'Èï!´?Åú]N£Þ¬[€²cçW÷Ôz0úÀA¥þeùE|8Ì}…?O^ªÿp0„ ûÊŽ²cç7'½:ÒìšRÿ²üV¼¡/\Â/ÖØéA}{eÇÎïÓE÷!¾õg§9 vީ͇_¬ÿ°jA0u|Ú~”ûÚºœÎŧÿ¾ç¶à~a~L¬ÿðÑú j›eÊŽ_îž‘`ÒAlú`ë¾Å`q5~±þÃü)yÔòçƒPvìü–vôÂÚ>ÛO¢ÿ°´zÕkB ÊŽ½üµ:sìŸoÿmdçÚàxáü`±þÃàƒhõÁ<”;¿Y/.‚uü>ÅÆoé‚.°µ°ß[ªÿÐ>›öéß eÇž?O lJ±•?¨÷ô¨+¬ë?¬*ˆ©ÿ¿”;¿Q]µK‚´JýËñ]¿3iן¥úkºÒªô,”;¿s·Á€á]°ñ;™Ó6ÍKôòÍYTª°_¬âõnG`Ãw¾÷–;KÀðü~B‰þþ–V:øsÊŽ½~©]ý;8p¾ó_šæœ…æqÂø„Xÿ¡`ÂvjÁ¬b”{ú­xìC05ì†RÿrüÀ®¦ÁâmÂùƒbý‡Ií§ß EÙ±óëQ= Ü¿Œoº€û½øýoRý‡w×Q‹?i‰²cçwÛì òíØöÁ€?l`×}aüH¬ÿМ¥þ7ô:ÊŽ½ü=öØR›þŒ¸ À7ãý#±þÃÕŠiÕö¡(;öôû|ë6pwð\üœú½…ù±þƒ_ÐqÚc¦ÊŽ}OêZðîÕw°ñ»váÏrõþ|ÃJ©—²cçwÞ0ìŠOÌÛ£&¸È·$ú–Ú©æPvìåï©yMÀÒ!ÅJýËòKìñx¯³p¾¢XÿaÊjêÇà”;¿ùûú@¯µ•ú—å·öÅ9°žŸpþ§DÿaÒqê·¥÷Pvìùób§ïÀÞ‹jlüüŠwƒ«Oòáë?4m£‰Œ²cç×qNoíLͳJýËñ= ½µ ߯çÅú·ûZé@a½D…ë?\¨ ýºâ;ŸÏké¯åê?´³Só×ÍBÙ±ó[6‘ö/ qñO ;~ˇ_¬ÿðÓ§ôK›PvìõK“wGÀ­°­á¯î€_ÞŽáÃ/Öð´ï¥–tÎEÙñO\ñÓµ'•ú—å§²kÀòcÂùÞbý‡™ÕÚÒÍë¡ìØùÈ|÷õÄÿ”ŸSÿ¡ýx>übý‡»ç ¨ÜF-Qvìüþм Þú¬H©9~°Ö k`Ãégøÿë?ÌN·Ó3=QvìåoÓ|?°Ç€ï|Å/Ží7úòŸ%úW'\¢½ÌBÙ±§ßÎ{~àö›pñç—Ì„5š ç'‹õ§ç¡ìØùíýØ}ÞTê_–ßýCAž™?_B¢ÿÐ8¼€:¹u;ÊŽ=z}ô,ØÚâ€Rÿrü`£Q½À)á|O‰þì"ªé·Pvìüv·] 7y¿#K¶Õûx½‰þÃÜ¡%Ôë%(;v~ù šÀ&ñ •ú—å·3‡‚á/ ëwÅúmÚFS÷¢o¡ìØËßEËÀÖšM±ñ«Ñoø¶µ°¾G¬ÿ@ÍÓÅ4šƒ²cç§ílÔ¾øÝ%¥þåøýh“væSüyºý‡zç²hß]:”{þôsª><¡Ô¿?Ø$B»,ìë?xÎ,¢¦¹½ÿ*\ÿ!¦ èÝßùR·žmlÂüƒXÿ¡Ö qtÛg/¡ìØÓ¯û¹e0ßùƒ0å­\8écþü%‰þCíùÔÚÑ(;öôû>©xõw|í3ÿW~ï,ê±þÃî¸[tôí”;?»i7¸ªÁ7ÿ·tER¹úþFµ"5eÇž?ýrJÀÒøÖ4º‘¶5äÏë–è?$?SH·/ÈFÙ±ók“.ßæo{SË`õß„õ×bý‡¦ÝGÓ­Ä¢ìø×Ÿ-ñ‡A °_kÙ›ÀV~býØs ýØš”;¿[‹À;çðoS?ËÍ×ðáë?LZ=öé EÙ±çÏø„3àôÆÆÏV?z¿Í¿Ï$ú7ßjIi[ ìØù ¬?…m8Ùs#ØU_ÈŸbý‡]÷òhï%!(;v~ßR­™<…­ÿÿ¨ßvHŒáÃ/Ö€§"èug ìØùU«3|³ÛþFØæ~ôi8Ž¿Xÿ!ìòº˜ŽƒPvìõKD»vÊV|ã/I ´sñí‰þC‹#!txq ÊŽ=ýü·>›žÁ7ÿÞlÙnØý°~I¬ÿPïé*ïì~”{ú}çž­ÿ}[Çiç„ýbý‡6Ó;Ðý-!(;öôKùtœ¾ Ÿþ»™VÁܾÿ*Ѩ¿ÍJ홋²cO¿k÷À[Þ騸±çCož'è# ÎlL§úòãGTzï³Ô½{ëPvìüžJÞ ¾.Ä·>D$øãWá|Ýe_šé7¾xç·`ú-jB—K(;v~Ûþ¼f†þ¿¯&˾×ó៼q&=èô"žß‡B¨åQÓPvìüÓªaëß‚ké*°ìI~1=ééÎôôEx~?|¶Ÿ®÷ñ,”ÿüôª¿ ¦¾ý+^õÁÎg(>üO6ÝF·ÖñóÏtàùYTÑ”y(;þýcºƒÉâânOò/¥ÿ0ívWzJ_ŸR󢂨¯¿=‹²cç·0e(8ù=6}5°!e-¸—ĿϴÕÏÒú>±ç·jW!åš‹²cÿQ…‚Yñ¥Lüf!xë~¿"½ia7zÕ ¾~¦{h/Q_Ä¡ìØùM­ÑzLX«Ô¿,¿7UË`íÞü~:Ü¥SÿªÏMÍûÊŽ=ÞXè,AJýËò«Þ·¼Þ”_F¿;s’6à¬ðþkpn{LëÄ|”;¿Ø7Öh7ýÒ?ðÜ7´{ í³ÙÓs§òõ3µÛœOW[QvìüŠO­7¿Ä7ûspè߈?Žžæõ=꘰~þ¥å÷¨¡ÕòPvìü¦†ÝI`‘Rÿ²üÞ¶v–X~='½eÜ[ôîH~¼…nØHE®_ˆ²c¯_žy¹ ‰Åv> ´^Ú ÛÚøõ€ôKFГ¿æ× RÇSÚLCÙñçOؤÃwþËí‘^`òeaÿ˜Xÿ!5{0õM|”;¿'k¤ƒ]/à;Ÿhȯ?ƒ³;„ý)bý‡™/dQÏööAÙ±ó[×K &4Ç×¾ÞûdðzOþ}-ÑØù^Wjߺ ”ÿú—ÓìEØø]ݹ|Ö_Чë?|u)ˆöÝÑeÇÎïëŽE0ø6ýppçè'°Msa|W¬ÿp~õMXÊŽßêëE`NÏ&ØøxLÖ¿ˆõfÅåRŸ~ž…²cç·tZ8úò6lü6½ ~¢ùõðý‡9oÛ© 6·Pvìï¿fÛÛƒ¹ªßpñƒBÚ‚%~1|øÅúw—ÄPû7[QvìüÂGß÷ÇakBø±–ÕPåÃ/Ö¨Þ¹+µù¢eÇ?ÿ÷õ%°xó¥þeùÕlö&ø¨©pþ’XÿA]’Óëì%”;¿&×~Ю^…m~Dv®­Ý6H8¿G¬ÿ°ÐWG}^/eÇÎïƒÅAàÇçð½ üy ý‡—_,¤’{kPvìü¦w;z?ŽmX’÷y[Ðë?œÔáÝ…(;öú¥éñŰ^Ð!\ü`„ƶêů§–è?¬·äR3š‡ ìØÓoK“E`º×b¥þåø#úÉ`in ~±þCÖ¨÷U±(;v~ ¯Œ ÇŸÃÆ¯ëþ  à¾?"Ñ(~9‚JßX‚²ãŸß¼:LíÐW©9~0${x«šð~ë?ô)¦>¸ÜeÇž~»_°ƒ7ǧŸsfbSðÁ2þ¼‰þÃCs©›§ŠPvìü¶OÞ UqØô}ÁñÚ‡apˆ°ÿA¬ÿphEU8ä&ÊŽ¿ÿÐÅL ~¿/Kê?͇_¬ÿ°% ˆÚ Ï¢ìØùíùý'°o%¾ú廬ÉàBgáü,±þƒy‰Šj²=eÇ^¿ïûäŸQê_ŽlÔ´lœ-èsIô"c©ÂyK¯ÿðÝûà¶–ÂÆ/ùÄ èwJXŸ,Ö(9A2¡ìØóçŽ×Áûƒøÿ$ùóÛW[€ƒ …ñ%±þÑWscjÔW¡ìØùyÿrHûv·ÝØøÕí?N»rär>übý‡‰õƒ¨ 6+ʎ߬'΃ƒÕB”ú—å—we/¸œ#ôÄú±OÞ¢Úíߎ²cç7`ö<Ðâ·}JýËòýÊO æ+~¾TªÿðEuþ‰”;¿K÷ëÀæçðµ_þºó)Œž)ŒŠõæ-ȧfw½…²cç·ò×£àõE¿~Ï‚wçÃ/Öè¼#ƒšùÊŽ_Ý‘ Á˜ˆñJýËòcú`nœÐ¿ë?ô^H…Ãy(;v~sú-CÖcëß:Îã;]8ÿY¬ÿð‘}?µ½Å~”;¿O®ly W`ãwúâ °s=¿þEªÿ±Ž:üã=”ûz÷2pú¯…JýËòÛsn#øsÂ>üý‡O/QÛ¶²cç—{µ˜´ßùK[R•«ÿ0÷íjÙ±ý(;þù‡¦£Àû§ ±ñÛì?ì;ú~±þÃËír©ÇKPvìíëz;gƒeÏá[_×ôÊ]ðÞ ~¿²DÿáZ jÉÄX”ÿøà.`ÓÛØôEad÷ (RПë?ì›6Ž:üXÊŽ=®Šò«ß Rê_ŽØ_Û|®âßgRý‡­³bÂ'e ì¸ùiÏÕWiÂ9JýËñÓþÞ|½6¿³Ð¿ë?LÞLíè­BÙ±§ŸCÿ!ï\üÀÚâöàV ^¢Dÿá™’,ªÕ·(;v~ƒŽ$€~žJýËò›Ð΢h^OP¢ÿoˆ¡¾r eÇ¿~°fGXs¾ó¼üTP3ƒßo.Ñxý|eýt;ÊŽß›;"Á°§±ÏǞͦ„ñû%%ú=_- ƒ~쎲cçWóüZpìÆ<¥þeù…Öô)WÿáÐä[ÔÌèX”;¿ÿ}8,i‰M¿úþ ª ë{Äú~^ƒi¿?CPvìïwö<¯Ï`ã÷Xá!¨~Œo/Hô¾î”Oû=“…²ãß°¶ ô4Q©9~à‡ßÖ†…ö³Xÿ¡ø«,êøÉ\”;¿ü¿‚×>Áw¾âg7§ƒu}y=:‰þÃÙNC©oW FÙ±ó;:D н¯Ô¿,¿;é#ÀÝf|~—è?äî+¡àÞ”{ùkuå6Øÿñ2¥þåøÁH•ï(ìßë?¤..¢î^)AÙ±ó›õT4¬±µ_àÒ-£a«×…ü)Ñð¹Iû$4BÙ±çÏI àÔù娸©²w@õuþ¼u‰þƒ×º˜Æ+ÊŽ_ kvñ2|ú]Ó”«ÿ°©Eíyh?ÊŽþhÒ³0 ÷6~'Wưg0ñá—è?|L ¹5eÇÎoIíXÒ1ê‹j~Ãk ç'‹õŠ–^¢k²c¯_j½ò+ø\†Rÿrü`Ӆàéþ¼‰þƺEÔ‚¤®(;öô[þs˜:þk\üÀ΄%`ñ¯üy©ý‡³¬tM{ÊŽ_ñÛÀÑ^JýËòž~ü•-œ/%Ö8ãa¥ÞÝÙeÇÎïöøWA~;lõ'¬öÒPX3¿Dÿ¡Ú4êÆÉq(;öò÷øºs`Ëhlú”0røzðÍg|}!ÑøñZ}f4ÊŽ=ý>??ÜÓ?p£à¬1בè?x¾×•VŸï²ã_ß³öwßßùŠ?~5º¾œ¿Hßö좡Žô슲cçw~å§`oþ(¥þåøAïâVàâ ¹|øÅúŸÜ-¡>W€²c/=»¥£°íωզ–ÒŸë?Lúñ~ÊŽßüâ¿ ×|ókO< ëÕôqÄúµ/Q¿lGÚ±çÏ Í{ÏakŸA¿[SÁÕüy2ý‡ý¸Íø\”;¿Nu:j_»¯Ô¿?Ð3¨]˜#è‰õîP]éÀ@ÊŽ?ý&î†~;®+õ/Çz]þ 6^*¬Ÿë?lü]E½IwEÙ±ó[6åkð’>}m9ý‡ÿ-Í _ŽT¡ìØë—&}ÇÀ­;°íÿƒáþ'àÁ:BÿH¬ÿ î1’Z²T‡²cO¿“í–éé«•ú—å§ê¹ ¬ðôÄú³æN¤[UËEÙ±ó]–€ó%÷”ú—ã²G§Ãj¯óÏbý‡;ºKÔ¢ÙÛQvìüþ0®o-˜¡Ô¿?X+u(Øp‘o_Kôæ GÍ›…²c/›^_ ö4Ķ>~û1¸1øœë³DÿáÊ `Ú;z2ÊŽ=ývnnßÿI©9~àü§u`$a}Xÿ¡Á²êøÌ"”;¿¢Ãc€=º‘Rÿ²üîê–ïÆo%úãfQÇ.ÝBÙ±çO¯oÞ['˜qñƒ–n' ù÷©Tÿá½YTD­|”;¿Ýc‚EðräÚd°æ,_Jôüv‹ú]ÓeÇÎ/_û:l~ÅÆogÉ~AX¿+Öè0t(uoÕ%”{ùûáW Øê‡oü¥fp/ðmx ~±þCßJb‚^²£ìØùi#FkítJýËñúp»öµîÂú%±þCHRP?eÇž?}OÕ…ªÁJýËñƒMî¼ëöú¯býßK¨i3ß‘è?hjªèfç*Oÿá§!¯Â °éçÀZØ*…¯%ú½[o§ë¶‚²cçw+¦.XYßøîãÑ#@±§ $ÖxqF>íwªõ¦çrðÍÿÙl-J¯/Ö¸e¿D%h Qvìü¬™ Ö´¿‘V‚]…mùð‹õv×,¤}¼‚PvìüÎ\/€õðé±úíïãÃ/ÖèRL7z©&ÊŽ_Àíà›Ó•ú—å×v®?ôy’?/\¢ÿÐq‚5¦Íg³PvìõKD³Ú)'°õÿ@bÆKÚ¹ øùJ‰þCKï®t§¶³PvìéçF ŸXÑ?ØìPø”Šß/)Ѩ?9Ê󻋲cO¿oŠÏ,Ç·>Ò7¡ ˜vTXÿ"ÖhW¼Ÿ~vCÊŽ=ýR|~†Ó[íÀÆÏtÖæFò|Äú 4ÅÔ§ÏÏCÙ±§ßµ‘-Á[ÄÆ¯º%léÈ¿ÓɽߦŸÛÜÔõ™Ê<íCýþéP”;¿îLþ<Þà-¥þåøa·ÞZøö%÷c#zŽŽ¯¦æXH5eÇÎï#°Ìì…­ü¯V[Á²ëB~|ñ÷®ôÀÏm<¿¯ŽØ©£2Pvìü~4LKÃ7?}}p X¶£_XW“žþ…0û?¿|ºÞ{-QvüóÓÓAM>}.¯ì:°‹G>üOÖœM·òàõtèÀs—¨ÃÞE(;þýi™`²ç@¥þåø9Æ—Þy6†ÿ«{öÓ/Gðë/©9íÔ©u±(;v~ mcÁ©vøö7nh›~ï(œÒ¨åv­×çü|µlu.å¿9eÇþþ£snƒYS†(õ/Ç&þOÆÎv}¦·Î˜O¯+àít·–·¨½¯¡ìØù9ôö´Uê_–ß¼=ÏÂÚ~üúVZ·ùú„ üù:jv«ã(;öüycí\`¡ñéVÏ<^Ðÿ{ϧj=šðã«TÝ{Ldð,”;¿Ø^?k7'`; Ùë£Ýûœ0ž;ÃçýºŽo¿SŸý•O•\вcçW¼'Ü<ò¹Rÿ²ünyô‡þ/óëMè)³ÏÒÖQÂþ±Éu‚©çFµDÙ±ó›ÚiH ÅwþüÛk÷€Q?ñó+ôÖE»è£¾ä?‡œÏ£<²c¯_žiéC6?­Ô¿?h4 ¶íÏϯÓ㟠§îÎÿ<¥O RC­(;öô;zü HÇ·>Ü>ÔL>›æú,ÑògõÍK÷Pvìüžì8ì’¬Ô¿,¿!g~g×,çÃ/ÖxePWªÿÚ"”;¿uQÇÀ„mØÖ/=Ïìs×’è?ìÛ{‰Ú»áÊŽ¿ÿ0g4X~R©Y~?¾Pìß?€¿XÿáèÍÚÛ6eÇÎïëίÃ:½ñéÝí\¾þÃôbêÌÏ]QvìüVïÈsz\Tê_–ßÁ¦wAÁuA_F¬ÿ°€Š¥¿§AÙ±ó[jŽNÅ×ßüÃJðó¯BúIôJT”Ÿ GTñú?‚¹3N)õ/ÇvêS, ø‰õ~žC}Ñâ:ÊŽ_xR¸ß³…Rÿ²ü «ßÎwë?Ô¼™A}h‹@ÙñÏÿMš0î/†5Ów}±þƒªý­˜°×í(;v~M¾ñÓ®Þú$.~ â|uí–Âú%±þÃ럥زcç÷ÁmÀ#ð­ÿü,õ$ô.ØÅ‡_¬ÿ0ùé–TßãƒQvìü¦×ù Äåá{¿/Y¸Œ)ìïë?|«)¡¾ôomçÃ/Ö8ÝÞ‡úRヲcÏŸ;zŽïÛ±­ÿß®vÖ‹õ„Æ´m_€²cççó„^»ð lç ƒÇöÔæ×úbý‡ñs ¨u»T(;v~³Âšƒƒö¥þeùåœ.¿+ìë?hWM£ÚÝ FÙ±ópj hñÉ¥þeù~~*ˆY*¬?“è?d ¦~¸gGÙ±ó»s6ÿßú3U °³¿¾Gªÿà=š ì(;v~+ÿ¼ ^?pT©9~àã»GÀ»BÿG¬ÿ•Aͮ扲cçW÷E#S¼N©Y~á'ôÀ}}¤Xÿ¡ßv*tT.ÊŽßœU‹Áí±ñ[Ñÿ!èã‰õ¶-nImý½+ÊŽß'[΀¼ {°ñ;ýºìì&ä?±þÚw­ÔWÂümÅë?ÌN_Å7?½·^ßrõ¶7Gm.¾Š²cç—ûM˜ôç9lüXý‡EW„õñbý‡ù{ÖQ+·„ ìøç¢ÞïoøP©Y~›ýÎôGøþ€DÿaŠgKªÉ¡”{ûºÞî°,ßùÐOÔŠïâ×GJô~;n¥ÞW„²ã|{4Ø\ç6~Q;ƒ¢üx•Dÿaw Uô\1ÊŽ=®êVëgââö½æ¼ ìŸë?¬Z84æ©n-QvÜü´g·yk¦…*õ/ÇO{ïò1íª|zHô^?‹Ú*¬Ï®ý‡ãƒðOûžñ»Ró›bý‡~ჩV»CPvìüí0Ôú—å7>0D ó]ý‡w s©}ïÇ ìØù]ÿ¼+¬Õ6?è¹a Ô,Ϋë?¼vïeK BÙ±ó{óBw0¬>¶ú¬ÝcSžà÷IôâvçÑÕ6% ìØùÕü¨|‚ïüùИ:P¥æ÷Äú_ü•K½êv~VEë?ÜŒÈKFÍWê_Žô½³lü‚ï¯JõºŽ£ý¶ä¡ìØßïªyCÀ‰ |ï÷ÇNÛ¡º†°>B¬ÿP\Ô’öž¨CÙñïøqô=¡Ô¿?pÕs4l¸–/‰þÃñÔÔ×&ÊŽ¿ýÒ¨ÌÔžÇÆïÀâe`Ý~¿†Dÿá\z>U²)eÇ¿þsÂUP¼øu¥þeùÝÙ´Ü9€Ôw ÞJ½EuŸ]yú­{hÀgÍ”ú—ã#k/_áç$úCÃZR~ƒ²cç7+±ÿñ‘Rÿ²ü–ý¶:,œ"Ѩ¶Ÿö}÷+”{þ<ÑÉœð‹Rÿ²ü˜wTŸáçc%ú¾‹b|˜²cç×¢ÉHíâãÍqñ]Ÿ®]®þÖwÓª1A(;þù£è¿`Àelçk°g»ÁÆ÷}t±þê 54·eÇÎoIø·ŒÀ7þ¹5ånúGbý‡CCÓÁËBPvìõK­ùËàÀ™pñƒM7·„æ%üx¦Tÿ¡ù8já¦(;öô[ÜLmþ.~`göI°DÅ﯒è?L FuGÙ±óë–†Uÿ!ãʯàþ³üþ7©þÔB*¯ß<”;¿Û«’AþòJýËñƒÕVøO6ú°bý‡Žó;P׳bQvìåïñ¾^`ËúO±ñ‹\¨+WÿáR^,uꀲcO¿/ò~wOb;ÜXsVÿÍ­þé?x/µÓž?ÎBÙñ¯ïÙÒä÷ÌRê_–ßµ1ëÀá—„úS¬ÿàÑu0U| eÇÎïüÚ/ÀÞØÖ×Aïõ“ÁE;àÃ/ÖØ]˜K5u0ÊŽ½üõð–ÆGaã—Øb2x¯åi>übý‡WïäQ·fÇ ìØùÍŸ­…^]ðÕ/ko¯„õzœãÃ/ÖÉ£~Y£BÙ±çÏ‹‰uÁÞ‹jlüü›E–«ÿÐä¾&&êû”;¿ŽE!Ú™ òpñ=Öæöå÷Jôn6ºNÖÜŽ²cçwaÜNè·û‚Rÿrü ×‹›aã•v>übý‡‚" 5ÿ ¤;¿eé*`ÿ ÛúH§þƒp>ƒDÿá‡?Bè—<Ï¢ìØë—&aàÖ7±­ïacýá—;…õÿ"}ê¯cƒ©Ê޽üýðö»`ËòlüjFœg† ú†bý‡îæY1Mf ìØùiwk_|ëU\ü€þJ;ó)þ<]‰þC£§¦Ñ~KÆ¡ìØó§ïŠ-Pu½«Rÿrü &r<¬ ùóè$úÞ]Þ ^ý! eÇž~Ÿ²‚Þé•ú—ãnek€í[aý€Xÿ¡Ö{tû/T(;öôë^¯ L<?¨û~!|q°0ÿ%Ö¨‘M­¼›‹²cO¿ï_,ÀªçÿÖðN;~=„Dÿá“w¦ÑQê!(;v~ö¼•àª_ÿhéÜ0XËÎë Jô_§Þ¯1eÇž?ý ýÁÒ‘#pñƒ Åà#á¼(‰þCÒÛ¹t§Ù(;v~m¦d‚+-°íƒq·3al»ë³Dÿ¡Ñ®t›NA(;öüùÓÒ"”YM©Y~µr"a«oýF±þCOË=º^Í–(;v~·–lïÜÂ×>«ÿëW ¸u ~±þäKh?ÐeÇž?ã‡ÖçÖ7ÅÆÏÖÁ}š ã›bý‡ŸeP=¢ìØù °€õ¿áÓ¯9²?ØyGèߊõ>ý$öù²eÇÎïÌ1=¬_g³Rÿ²üþ˜› ;4æ÷Äúô«YtýÂã(;v~Õb'ƒo¾Ã׿móÞ1è]ô~±þC§ÉE1nG ìØë—ˆàkÚ)kÎãâíS´sGúbý‡vA×é° ·Pvìéç¿ò|âÉL¥þåøÁæ©jø”o~±þCƒ: ¨Ù7Qvìé÷í[kÀ3Øö¿CßWÁ+ í3±þCË!Cé”a…(;öôÓíõ‚ÓwÄÆ/k÷"˜Û×è?ÔoRHí›teÇž~×N^oõÆw~Oõ°‘`KË:*‚ÿl :]NV–Þ26Õ–•Ý1A×I7H§¥â;ÙôÃ2 ¦ ó?~Fhhhtd¤†u;GG9ÜÐpçg'˜aáÑâB##B#4¡a;‡F¨4¡øÉ"ÇjÓ[˜ ¤ ÙúwÌÏ22Êù' ï>"H›mÐt×èr†¹nª:L•™òŸiN{þ?C®üG…FIË$)ÿ•æý?ähU¢ŠÑ}Æý?æò²Ž5qçÜùže\vÍ=«;Ķ¡Øó<½i®T¨¹Ë³*BŒ e”ÿ*rÅ?]oÃð ™òÑ94Z\þ#ÃHù¯8ó½ïæÖ;…MsUéÎB@ŸL½ÑÄY|ôÌ7þŽÀ`Mý”»^¶šãɆ4³Åu<,[„œßˆ<ªÔ“/ç>®KÓgê-ZsfN–‰ý]·tsóW ÷OæmÆ8ÍF«AÃüØ8Ì¢·Í&Í•m`îs,,ë MoJ×[Òu¶½©¬/üDdg–î¤2ÃÍZûæèM¶œ¬~&£Ý9^Ÿ ¥q}ÌV#&î‡ê¿¸‘}‚MÊbѳó5®3„øË%R¢ró!ÚlÎÔhøhñ‹§z¦&'±Š~Éæ1šŒLýpEÑ Æ%ŸPQúyÅ'Qì²ðšñf}ú,Ít6‹Ñ4\H³$Z—š û÷IìËhÖœeN7´×:ïØ^“›ªëµ)ÉýEP5l©‰d!ÊyžÉ]ƨI±ä4f‹F¯±2 ƒ)Í 1;Xé¹x­úÌ(».®'›ÜÂn5çX€Ǚl÷â®ëG§2 ûDæ¾5ÓÙÓXÓô&)'k˜ÁÒž‰ÿL&ä£ ›ÙÝ ˆˆ76"Lù¹/ú®Ìhc=rµºÚí+÷Íý{WRê·n%Éý{.o–úÊ™ä¥|»E¦ëû \ ò‡]ïÛqËîs(ÕnôuãÉ4.=˜Ës s½Ê\3Ëñ€å·ÿ2°4ÁäÚ¡a’ñŸ°È0Òþ« p¹–-’²Æä_/~Ã5ûcî çÊò~ÜçÑnßm„&m„Þd2djŒ&m„AÃäw½&Ko³Ù…3M܈8Éʦ;±Lºìõ@SP~ ÒÅ%Æ&qÁAǦ+FÛ±¬±ÉÍ;±›ÅÅW½Æ17›Ë¦°ªÌg°k'¼lLãR%Ì6ù÷×[’ L²¹}Éþ#ó%ÛUL“ó£5§;üÔVâÇ‘-\Y€ bã*5ƺòÉ+åEE5îòJ†:VŸÂ+^—ü4ç²ó ^4•<ˆý}OÈF¾WJRÖõíIÅSÚöÖ'¾grR?ölOm͆¦Ÿ)Ýa4ؘô–ÉÄ,;ÕÎÎ/8ƒg²•ÎÆÆ5;!Ãêør™’=3̵î¼ì"#š+ Ô&%'§öINý´,Á'ãLéÆ4¦¨0qœf1cç-†4C¶Íl±jÌš ƒ!]3ÜÌ~o4ÙÌ&8g”Î…9¦•¡ì|G—™ïy¯_b\Š[\²)Ý/¦¦ êEq¨r¿WOþŽxâþ ¦0uF¶9So1Žã–°ý)¡vâD“m1§ç¤ÙÚkôV^£³™Ÿg"×`ÊÉêø(G—ìdP\lª6)±*S%DZy»1gq±LܘF,V6j˜JØø¦+³RÚ~‰ýXÊ,ã‰fl¡±fÒl}¦ë½cýWÑeJ›ÀÝ2òÿ#ËXÔÄr½9úÁD-[ó×cóµ°¦¡Ô«æï®XQ?Y6ßG¨¹ B´xz “LIsAþ5.NQ¹¶bóFOŠï——Ä–ÕŽl*22˜bÊ.09Ö ckŒ1Ý6BÒnTž¸JªîùeGY¬SúéXNU/ŠÉêlÛ¨T™xÂY&¬6¡8hZóu`&Óöi£<å#*µÌˆr4‘Vµ\ß3 &3,Äu…3NüÝIºš„âŒ'_5R’R˜.7M%‚q …=–¨a6³‰Ò™Î6ÂhÕŒ1šÒÍcpÆØCµÊ1ÑZ8ÿÄØdʵ„'8žÉ?˜f„ÍhÊÉÉêdµ²ÅM%¬~…»Ž#Üýa|’ÀØ8&/:k›àþ†LsšÑ6Vãè 7ž+ŸB°ƒÛ’ƒÉ:&ô\Á©Ï3pkÏqùCA##Ó¬· •Ú)É”öé¸ÄžŽa¾­•bѧ=ÏÒÍÑQ|ñò¬Š~-GðARŸ>ñL2ôŠ‹e+ž `ÎÎÎdZ\ÖÆ ÛCr¶ÞÔ¥À>º¸ÁŽ"Ë¿tLéÕèŒã”eœJ¿(ÿ{%R lëè\¯/gm¬1鳚Ì.j•°Ñêv¨!Döp‹9GY]‰o¡§Òˆ®!:Õç¢?Ìq˜SRuq²¯uÇgƒMc5¦Ø÷yÕD¹l Ø1Zêj‚4ÖrC¢¤Å+q06jSâúÃTš]€Ü";Lgø¿‹òCDŽ.œ£Ø;Wb7Ê{‚Aoͱ¸u¬Å\%ÔZÌΚ2#Û­¿¬ªäåüû©„ž™q"ùRÍýºÔä_ ƒ ¢1ìRÆR·¥,’!Jµ›Ñ} Oú½sÄËý{ɸ»±ôXC©@H;ÏîËè*º›Kõ‹Ü ¨Ž‡ûo-{÷Ÿ”ÝjvÿE™ SéDí?÷¸X¥â«T¦T„H[ îfñ[¸ÔŸ–zÝ•Î@¢ ½t6WS¥²bJ™ÃSÌ¥örNÂ:~+Ý:’£-9¨ïüKW`«\O;7ÍÆ¸ÞÌåÃ\¾vÕƒõPEpYyRl‹ŒÝ_ÀúW¹â¹ElZ¸ÇCB2Åe.½P•g ‡òõ¾¨ÜõaaQ‘¡¡®õaQ¡áìúаp²þ£2 ³ÿÇÏ®rNª»š‹~œ[‹óÏ”i_V4¦Fårn¡ÓQ·æÜ78÷W§ë1”s8]ÏJ)¢•ޝŠã«âøª‡r.Ç׃ãë±Ðé>Ê'”BMÎ}sÏ:u/ÎýÀézpÍ;ç^©ø ýë¡–¹*^2W…‚úÙçØ‡[sÈE‡ûøåx‡Û,{“Ãm¯¡Ãž<ÅáÆ^s¸q R*:p•‡œû•ÓQwáÜ<§ëÁÍáydsîI§ëS a«Pü×ÓŸJ«ýÕ?qÿpMàøs÷þn÷µÝî¸Ý7w»ïàvív߃»' (›9ÛµÃÝ«"î+.ä*ÌÙ6cšž]¦ò­ˆûÊgD@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ðŸD/•ʯ5{À†á?¼©jB/Dú¿ìi}bý_öÒ¨œG6)Õÿ-óL&_”Áu Íˈ§oæÜP™§«O÷c7”{ýíP¨ŠPx<¡ð,??xh¸ûŠÉ^Uútï*}ºŠ€€€€€€€€€€€€€€€€€€€€€€€€€€€€€àAPÕúÿIxöá\NßÈ“ÓaóätÇ<9­Ï›N׋Ó+óú·èÏT:<‡r®só8·sÏ:]—œ‹—†sc*2TÿZxË\4Èùcäü1rþØÃtþØC‘þþUET«Ò8,ÿéêšúô *}zõ*ùå?¨*ôé5Uÿ!Øtºœ¬,½elª-+»c‚®“®Ô¦$Sñ©âAÒ€ÔˆN6ý°LCÇŒPã>#444:2Rú££nh¸ó³ã6,Ç&¸N«ýUÅ<§æ‹•òœÇ/ÇWÊsšeoª”ç´¿×°Rž=yJ¥<'&ðZ¥<'nA ú9ÿåÏþÆû?ìAŸ!÷þ•¼ÿCC#Éû¿2Àõ5 sélzSºÞ’®³%èÙ˜`+E»Ê¹ÉUIªUn•%;pÇ8“T¥;Ô2WEÃѪ(qYX­`ü¹BúÜ®ñï*,@ž*ÙñÖŠãOPµPþþ7š2Ìö ™÷¿ã÷þŠŽŒbßÿ‘‘Éû¿226Û é® Ðå sÝTu˜*ÊË?Wg?äÊTX˜´ü“ñ?‚Êŵº3——u¬)Målø©œMv¬ˆ­©DÍ·ëQ„|ùçʺÞö€Ï(¿ü‡uŽãßÿa‘¡ŽòAÊeÀ™ï`n½SØtV9ó²gè“©7š8K;Làïø Ö4•4ÛG1W5Ç’ ifKºÊYJBØr~#ò¨~POsn}ÊbÑÕš3s²LìϺ¥›s˜Òh8»¿¶•˜› û2¢µ“Í`ÑdX £r ¦4£ÁªÉ0[4}ÚMÚ½ÉdÈÔMÛƒ†ÉïzM–Þf1²!Éàˆä GŒqÝI6pþq}ÌV£Íh6©Jõt<ž/›îÄ2é²W`߽ɖ“ÕÏd´YYÊ*gE$y‚š{< ¿Héâc“¸à cÓ£íXVŽØì¦cX›†?¥rvÝíy–îhŽŽ¢àûVzðk9‚’úô‰g’¡W\,[ñsvv&ÓⲎ0fØê³õ¦.öÑÅ váXþµ cJ¯Fg§,ãTBøEùß+‘J`ƒ\Gçz}9kcIŸõÐdvQ«<€îT×HC !²‡[Ì9ÊêJqC¼â#º†èTWœ‹Bþ0Çy`"LIÕÅȾÖŸ 6Õ˜n`ßçUå²-`Ç©« ÒXË‚’¯\ÄÂØX¨M‰ëSéÌç6Ùq`:ÃÿÕX”"rtáÅživ3_4Ê{‚Aoͱ¸u¬Å\%ÔZ´Ùœé>OõLMNb³_2óZÈÈÔWV1<¬#²Ýú˪JŽQ&CÝW93·«ç_fœH¾Ts¿.5ŸÃÇ ˆh »”±ÔÀm)‹dˆRíftÈ“~ïñrÿ^2.än,=ÖP*Òγ»Ç2ºŠîæRý"wªãáþDËÞý'e·šÝQfÃTúQûÏý®V©ø*Õ€)!ÒV‚»Yü.õ§¥^w¥3¨B/MÄÕT©ìàVþÕO¢á)æRs;Ì¿õÔé¸ßs·9*Ñ*ƒúοt•ÇŠM;7³Æ¸ÞÌåÃ\¾vÕƒõPEpj2Ê}ÏMsUg®xnÝ›îñÌFqÙ‡5”g ø›P¾þ#ã—c”»þ#,,*RØÿÚÙ±ÿ#Œ¬ÿ¨Èìÿð³3nm•ÐvôãÜZœ¦€ûÎ`Ü•t|ȹ…NGÝšs9&õ¯N×c(çpºžÿý%ޝŠã«âøª‡r.Ç׃ãë±ÐézVBÐ*®S^àܳNGÝ‹s9.®MàaçÜ+´=Ô2WEÃKæªP¸Îpíßwí¯wíwíOwíwíïvÛýoÀÎåöP«»pnžÓõà&ô<8}<“N×3¦ÂV¡ø¯§¿kßüƒºÿ¸fsü¹{·ûÚn÷ Üî›»Ýwp»v»ïÁÝ”‚ÍœíÚÕîU÷r‚Gæl›1MÏ®SùVÄ}å3" øO¢—Jåך=m‡?ÐðÞT5!‚‡.ýQúŸìùMøõ?>¹§¿ŒxúfÎ •yºÚùt?vC¹×ß…ú¡…ÇC =X w_1ùÁ«JŸî]¥O÷Q<ªZ‚€€€€€€à? Ï>œËéyr:lžœî˜'§uãyÓézqze^ÿý9‚J‡çPεsnçrîY§ë’sñÒpnLE†Š€€€€€€à_ o™‹€€€€€€€€€€€€€€€à‘9Œœ?FÎ{˜Î{(Òß¿Jã ࡈƒjUå?]]³BŸT¥O¯^¥1_£ü§U…>½¦Šà?[‚N—“•¥·ŒMµeewLÐuÒõÚ”d*>u@\"HÞɦ–iè˜j|Àg„††FGFjX·st”à w~v܆…‡j£;‡E…‡F„vÖ0ß„EE«4•²¸7ÇjÓ[˜ ¤ ÙúwÌÏ22Êù' ï>rðâÖ`;[@Ìå¯r¶¹«1W syp——È£û>1É=õ³Ï± îŸÓjU1Ï©9äb¥<çñËñ•òœfÙ›*å9íï5¬”çDOžR)ω ¼V)ω[‚~Îù3Á#„¿ñþ{ÐgȽÿÃC%ïÿÐÐ(òþ¯ p} È\A:›Þ”®·¤ël z6&ØJÑ®r.BrU’j•[eÉÜ1Î$UéNÁ#µÌUÑðpd£*Ê@\V+®>·kü» §Jv¼µâøT-”¿ÿ¦ óƒ=CæýïøÀ½ÿÃ"C£Ø÷dD$yÿWRÆf4Ý5ºœa®Û€ªAåAyùçêì€\ù “–ÿpRþ * ×þéÎ\^Ö±¦4•³uâ§r6YX—m¾4R‰š/n×£ùòÆ•ÿt½íŸQ~ù‹ŽêíÖÿw”ÿðÎa¤üWœù¾Ûî÷NaÓYåÌË~œ= O¦Þhâ,õa¾ñw|kšJší£˜«šãɆ4³%]å,%!ì?9¿yT?¨§Ç9·>e±èÇjÍ™9Y&ögÝÒÍ9Ì?i4œÝ_Û‹JLM†}™ÑZƒÉf°h2,†Q9SšÑ`Õd˜-ƒ>m„&m„Þd2djŒ&m„AÃäw½&Ko³ÙŽdpDò…#Ƹî$8ÿ¸>f«Ñf4›T¥z:êöeÓX&]ö 웣7Ùr²ú™Œ6+KY嬈$OPsgá—)]\bltlºb´ËÊ›Ýt kÓð§TÎΡû`€G/6«˦ðH ^6¦q©r޹³ðL6d¸}t$”+QƲ¿×%?튷2“9ÎdÓ°àìÚ¤ääÔ>ÉI Ÿ–—’éÁÓ˜äMg~l1cèh,†4C¶Íl±jÌš ƒ!]3ÜÌ~o4ÙÌ&ÐC¦ž‹MùäöV˜Ü*CÙqE£ãª_b\Š[ä²)Ù/¦¦ êE‘æž~ŠâÉßOÜ?Á&Ÿg›3õã8s ›Ln%BˆM¶Åœž“fk¯Ñ[5zÎf~ž‰\ƒ)'«ã£]ëÒô }t|ÅŦj“û§2ÕH\"óEc6Îâb™¸16X¬lÔ0Sg0Q1Ì`QUÂY)m¿Ä~ ,e–qƒD3¶ÐX³ i6‹>ÓUWZÿUt™’Á&°@·ŒüÿÈ2½ƒ’a¬ã­Ø&j1_ÔcóµÅa°0U°Aã^+|÷¹…„¼âþæ+ÎíGr š€d¨KŠï——ÄÎŽl²22˜rimИÌF«A3ŒI1ÆtÛIãFyj*©«ç—GIeÆ‘úA“ùo&\9u»(&«31™Rª<á,V›ÿ5­ùJ/Óh2´Qƒò•ZfD±´”–ÿZ®ï™â “âÊÁ'þî$½XÚed<Ùøª‘’”Âô i* ˆ)½˜¯š±f3Û˜Ø)él#ŒVÍ£)Ý<gŒ=Y«ÜsþƒÐÚJŒM¦R’›,ÁñLþéÄ´lFSNNV'«Í­(nª©JãúKå¿Dá®ãwŸ¤06ŽÉ‹ÎÚ&¸¿!Óœf´ešÕF“Qqk¹ò);(°M7˜¬cBÏœú<·Ws?422Íz›Ð2©í ‘’LiŸŽKìéHæÛZ)}Úó,ÝÑEÁ÷­ôà×r$õéÏ$C¯¸X¶â æììL¦‰ea̰=Ô!gëM] 죋ì(±ükAÇ”^Î8NYÆ©„ð‹ò¿W"•À¹ŽÎõúrÖÆ“>ë¡Éì¢fxÝ©=““ú±kQk‘=ÜbÎQVWŠ[ÞÑ5„@§ºâ\ò‡9ÎaJª.@öµîøl°i¬Ætû>¯š(—m;†ô\MÆZnÜŽ´xå".ÆÆBmJ\˜J`>·ÈŽÓûý¯Æ¢ü˜Ûèv¼Øf7óE¡¼'ôÖ‹[OZQÌUB­E›Í™î#±ñTÏÔä$6ø%3¯…ŒLýpEaãÁÃú7"Û5ZÁýEeÆ(“¡î«œ™ÛÕÕ/3N$_ª¹_—šà‹cÔC4h]ÊXj¤¶”E2&©v3ºÜI¿wq¹/r7–k(içÙÝc]Ews©~‘»Õñpÿ ¢eïþ“²[Íî¿(³a*ý¨ýçþW«T|•jÀ”Ši+ÁÝ,~ —úÓR¯»ÒHT¡—Î&âjªTvp+ÿj‰'·âÆ•5·'Úñ[O.û=w›£Í‹×wþ¥« 8ÖÚˆ½×›¹|˜Ë×®zp ªî!cCÅV>*gK-ëâ]"AÌU¹â¹•lZ¸ÇCB2Åe/Pž¡Š¡|þ?ã§ãËÿ ‹Š,µþß1ÿJæÿ+2ëÿýìŒ[[%´Äü8·çŸ).¾3·Fårn¡ÓQ·æ\N‡Gý«Óõʹœ®ç¿E‡ã«âøª8¾ê¡œËñõàøz,tºž•´Jk×ÿ œ{Öé¨{q.§ÃäÁ½a=ìœ{¥âƒö¯‡ZæªhxÉ\ ×þp×þm×þj×þg×þd×þa×þ^·ý·ÿŒà\n­º çæ9]nz̃ÓGó8ét=c*!lŠÿzú»öM?¨û/€knÄŸ»÷w»¯ívßÀí¾¹Û}·ûh·ûÜ=A)ØÌÙ®]Í^q_q!' xTaζÓôìú*•oEÜW>#‚ÿ$z©T~­Ù³køãÿáMU" xxáÒŸT ÿÈž†„_ÿÑá“{úˈ§oæÜP™§«O÷c7”{ýíP¨ŠPx<¡ÑõÐp÷“¼ªôéÞUútÁƒ ªõ'þ“ðìùœ¾‘'§ÃæÉéŽyrZ7ž7®§WæõoÑŸ# ¨txå\;çæqn!çžuº.9/ çÆTd¨þµð–¹ióÇÈùcäü±‡éü±‡"ýý«4Š8¨V¥qXþÓÕ5+ôéAUúôêUó5Ê:PUèÓkªþC°%èt9YYzËØT[VvÇ]']¨MI¦âSÄ%‚¤©alúa™†Ž¡Æ|Fhhhtd¤†u;GG9ÜÐpçgÇmXx¨&,<ºsXTxhDh”†ù&,*B¥©”Ž9V›ÞÂ%ÝhÈ6˜Ð¿c~–‘QÎÿ8¹hx÷‘ƒ·ÛÙb.•³Í]¹™Ëƒ»¼DÝ÷‰I}ŽMpÿœVû«ŠyNÍ!+å9_ޝ”ç4ËÞT)Ïi¯a¥<'zò”JyNLàµJyNÜ‚ôsþËŸ !ü÷؃>Cîý*yÿ3ß’÷e€ëk@æ ÒÙô¦t½%]gKг1ÁVŠv•s’«’T«Ü*KvàŽq&©Jw:)¨e®Š†‡#UQâ²°ZÁøs…ô¹]ãßUX€Ž¤^vüºIéð•%eûŒ´ÌŠ\Ik“\zIÞùÒ¦Q‚Kë¢nà-býÃHƒ6ÒÀäá?Üæ’¢EÐ…›H€ˆEš¨Ý$@šqgvgÉárÉ]Rä’¿@Íhö9ïÙ™o¾/WÉÈid• ¾BôøT"ž §ÂñößPJrYV hAZÚ]B;ŠŠJJNį”ÊR)ÛOI™r‰³˜ i MÃ8}i¦ÃÆÝ¹Ú'Ï(}áEë(~Ô2Šä7ûÁŠX(Wò[Y{Ÿ[š>…~Á¦¢!>ŽmÆ9½Õjž‚F*’÷×R¾¬ñÒš?_÷gÏ:÷5ÃX(9}¢0*H;Ì¿Zîk± §„Mî(k§ƒaUô¼½5(«8ïj™+ð‹( œ^ê^nÒÔóœÐܼp·°¨ÿñ/„ŸÔöewg ÈvýÇï5×ÿ˜ÿqºþC6’×­ÿjêÖFßæfýǘ˜jÐ_aÖ½ˆ ‚1YJíI¨H4>ÊÑ¢i“ÿD¡Š$fö~–*娪4UÉV2åE¢JDɲò´TBR¡’oOyšŒ®*ÙEV‹>îÓƒ[¢kèþp!+gpL²ødUÞ&K ª”‘ŠeEÕÔ¥íHRí*$\.”6IÚŠ·±ZÞ£xe¿)¢A‚äöŸªé†±(Šnkˆ¡‹=SLXÓÅžºjVÍæ‹Xw°Ù›(Fx–öÑæ¬VxîâL=óX-6šôÆX‹“†Gýÿ—€ìÖV| ý¿o¾ÿ]ÁfýG[­ê7gÖ´– ì? Ùÿ'¨ûu¨;Z=ÏR÷šîŽ˜Õ€V¨[¤î+ºS•Ôx„º/P÷{º«Y‚> ŒÙüzŠi ²• *ò.½±{ CˆÃñßvÚŒÿ|>óþ??ÂøÏeF ŸÆwQ7ì$Ã}†ï>À!ÂQû@€Öÿ×ü^hÿÝÖÿ‡Gõÿ€"¬ÿ¯ùÁþ+àÍÖÿÉü–±Ò84ëÿáX*»Ð= 6õmeÍß°ÿäÜA/÷sdM»nýÿ8=^·þìw¸YÿŸæš,€·2èå e©P‘fP…¬qWMÖ‘eþ¢’»RPòĘ\Ðl­p혼°[ò»\'vmWÐgøX*‹ñúFÁ#ó¥³…©¶¹±è#S{mm7ƒ#azí¦„Q# ‹ ¨ :¬îo$ÖÄð@ù"Iͤ¤ÊR )d-ÂÑûºoÃ'Å BDøâ}¸³Û•ÊÝköh fÏ…f¯‰°›êš”§æÂœç!“*`øë`†¿LYtŒdQ:.„/„5ÅZN)ª¼‹ë:dKd–Û™e–L |àøËû#¥Š™§5 º9qE)µ‚ëØ"Òxµ´rM˜Šâg²¹(^ƒ)/ãHý@ÉÃiÇ®Ûù±áµ^¦î¡VV³Lõ¢îîL.áÏÓ¯óç8+É?Rjê¾É§™”ÛǧîS9ìŽãßľnH«é…Äí÷,@µþþ÷yäÿ|ë« ÿÍläÿ&Aþo(0äÿŒ*— ®!GåÞ¸—©ûußÒ½¾*'H¯÷Ðë«rsÆõƒ„1ê'#ÒOϳ N#C.óz8ÑxÜòNpþNw]þ­¯§÷-÷\`ðhCþ™ôE’žkv€¡ÁÁüß-@ÙÉÿ­7ÚZù·ñ|›zŒé>ϵñŸ¾°ù•Ͼýð›3§”ÈÝ?†ã‡âx[ØÖÿ.X€ê@þß· û¿\äÿ‡Ûúß PÈÿû–¡ÿÜ£™ü?Yç1$‡Dþ?¾‘$’ ÝSÿg+ÿ¿¶Þ ÿgyÙõß ¨üÿ—8“ü¿Q¦ëåÿÿŠùÿc\ÙW“€ñÍi\ ã‘P*¤éK'„‚["ðp"@u¬{:U.:“™Ô„ÎȽr’u¼6,㥉JmÅÂ)®&Ö6KDï¶„¡WwãX¿#Që)½f‡ˆNµëc"‘DÜAÊvIR/IêBé´óÈ÷%Žó{Ç’Íä“q-†ä”RfOÊVrm•Â\žLñGCš¦½Ûªò””)#9+ÊòŽœÑÕ –´+GvÛÆ÷x* %ñD(MeQJÊáü5£•òiiw =žR‹è±ÈßYAï¹þŒ&)ð± $·á÷QË¿’ð¯ZS+ât,žbĶbÄNž)7në$5ÅeΈ‹QMonŒP ýÙeÅh$Nćçj/SÚhïú‘¦Æ`VÀ­Ÿ ¥ƒ|Фÿ]nÓÄ’„ðX\B—÷¤ñ‰h?*/•P_-gEvôvA¿õÓ­lj5Ý*0g×Iî³FVDß<Ī»ˆ°×4vÓìÑú¾¯þ%š=¨¡boH[€º Ìuªn×…â?§s/rV»xÎ4@KÙq I’îs­ivƒ:Zɦv'ßÿÝÐúû…l|3ÿ{WáûßläÿIYÓ ¦1'`¸Æ"ö±Â£¢v1Ú»Ûûúà I’ö ¡ßHú“ÎÊXøï]Ó½GÜÒö@ó[8ù?mÚÞ§}9hÓÿÁÀþÀY÷RóóäßKËKÞ%ZÀC5ß9ïê9ÿ2òùÞ¿zßió ¯?ç_;Çd4ÐÜ´½Àµ:¸ Y§Þ¡·…0„\g ƒÆ`ÈÿÛ<}Ÿº½yú¨ÍÓ¯õôéc}ûx_Ÿ>Ñ×§Oö5ß§Z?]Ó}Ñ»§¡¥SÀ gú_fÂVÿûjƒþ¿ô¿¸Íu55/𽏗t»qðFÿ^ è-NêÿAM@t`ÿaÕë…úï`ÿa¸qRÿj¢û«^ÔÀ5šÙ0ô2D-?¦ÌJðõÿ‘p2.vÉöÁ¦þ¯úV×ì¿­¯Aýw½ÜÏ<Å9±ÿ0õn`ì?VYìâGCÉ$¯)–=QvQ^*•ÄÝÎ4W€úûp¡ŒuÃÇ™î:â3ñš¢x$gÑ‚Lf–\@Ì©H«ÝÎ4á›õ² @"˜´eõÁؽ!%ãÜ-‹ù"Q˜ßNFƒRìî*Å6ëÆOá¸N]Äþë¢z¾ ¢*+ª\¾â(ƒº®CÞö'ð _ǰϿT*jFB%+Fø¥wqmÚQ•<º¼'göŒ’Fˆe);(q2µÓñ ¢›[o(nˆÓ×%±ŽoK!Zxû-Bï3ãŸHD­í"eˆ/s†9“‚˜P;Ç‘p:Fù˜–àø_\†ðh«E%陊TÈ ”]»øÌà\H'x&ñ7±™PUœ eI- D„ÚQ‚Ï ¢ý¿›þB: vo°Mý_Y]^5˯/ƒü·+èå~šlY©ÛÿkP·ÿwòun`öÿNqͶŽm(JŽÙó6©m§ i[cSjEBòÚɉ»hO,¡mI* ‘ìc’²¨¬ <ެ¾7–s²ÉÌÁ¶_Ùoe®mý¹nGa—¤Ê½¯ ;M[s§Â±ÙEÁþ{Èö\$Êd{rí(*Ý?YÞ“Kzž`’ÅÆ©íV;¦½½*‰%ÅòÝ`ìT§4ݦwc'+Ù„¬ííE Þ³>ïéIå&ÛÜ}Q9[Tp="»Ü{V`|w÷ÀQm·kßÙ í„Èp á¬ÛŠŸ3 Ý«3tŸ,Ö|Ÿl­#õ°ÁûQµÍ°LÃφWÛ96°Ö~ÔÝ‚©¨l¸ÕÞ[#u°«t@¿ÏYm{% V7Œ7Êþþ÷*>ÿF± »ãØkuÑaÆvüß…  vãÿ5ïJÃþϵeÿ»ÍþÏI\j߸´Ù6êØýÿW߇Óþ²•×¼C@œÞÜ9]‰¹‘3µ•\³@\Çvü߀Öÿ} ÿ×`ý¸±­ÿ]è`ýßçû€{4[ÿ'+Ú,/g±þ˜?e-ê8éââ?gWÿ}««« óË0ÿçz¹Ÿ' öú¿oþmn`ÖÿOR·Ag©iÑìút|#„R¡t0á/¦ƒaŸʪ”ÑT—*;(+åÄ+(#‘µ4´ -í.!_DÁPàôçtílœ>ÑzÉ‹yýÏXÇ7m_M¦CÚ'Œp!´B±€Y»ö¨*’§Mã4¨~ÑñGý¸ÀYî$Io±HÒ´À§ÈÓîIJ¶º•Ù »š/Û¿”~Ê:¥»£ª|'æ¹ÐnyL<Â'C–屸'–$(í—Çj’:-î¦ô–Ç›˜Ä‹‡c©pì-’‹õE’hòFP*%ì¼uÂó!»‚évz`Ù¼¹–„Õ¼¢…ól]ᬅÒé0iok’´Fñ|ئxºŸâ}(Ÿö2=xÜêýÉ¢”‘ÅI-UÌàôKe9SÒÄ|d)—]DZòlàøÅR áûÊÛªXVTG 5øvhNÖÊ“ö©«µŸÚ$‘GÐÐÐŒ9¿c¼&yt2&æµ:¡‹Ú‘H‘|œNÆ·ÜhÙ7MížÞük"Aù^MÙ0䢨LÛFlsàŠ fƒ´c=™´ë£¦Ùö‡œ~°‹%ï@ÔiÒ(ç«ÕˆëdL2ÝãbÈÚ¾eíœyühküuzP3Þ~·El+Š?€#Dˆ%j™¨ñA’÷¤@ Œv½3ÛQ.l¼0m!›ÒÖD…øãÄ/(—Ùäâè]͸`Ϊ®×¬æSËÏ+ÍÜUÃT®ókëî6Ù0[çüÚYÓd•ó+OXÍÆ8¿¼¡%s~éIË/mç×_›FÙÕöH0_[l¸ñ¥Â†ÕíëN¶ØPÑXŒªGrÛ8b6ÕWeZ‚ú(Ôj—þ:]}³Ú¾ñ›œiö$N·3FÚ}"É2‹Ç9]*ùľ~êõؽkB³›·‘u¬´£õúo7¬Ù¬ÿú|~ û_>ï ¬ÿºÍþ"²¯Ùö2ïÿÀ¿™«úÿ#?ãÀ´ßaåÚøO_ØüÊgß~øÍ™SJäî÷û}p¨ÿ0¼@ý€áê? /Ëÿ½kºw ç'Þùß_˜¥á¾0ìößÛ°=Çýk  ôwìÙúé#/Qož>Ò×§ÛX~ëñÓm,¿ŸïéÓm,¿}žëéÓA\úÏuý~€ኪ €>è÷ À0SŽ&“•|^T¯¤ËùâR4yn3ŠÏ•Å휴´ã•»ð ¯×»¶²‚ˆ»¾¶ª¹^¿þ¿†ouùükë¾Uïʲwy}ëþÕu¹¢”µR*‹*~•¬,¥Bóóði;;-î£GUÝÃGŽº#ôç¹6þÓ6¿òÙ·~s改ûÇ]: ­ë¿\ØQþ »úOþ1ÕÿÕe/Ô7H])JèA4“¬lÞ™~¿à­ë?µÚ|@ìêÿªwµ±þû þ®ñ µ†~þ•®2\ÍVùI®6¦±6bߟwî õ?2ªV,wã­ë¿Ïï][nÿ//Cýw½Üßp{ÇS$Ó9½<§Çg9Q.Ð#'>C¦µ‚R)Ã5ýUü;¦ HEÍrz y7¹“bºÐÓéEÇ©{ ¯ªâ•€’«ä ä´²Jß !z|*O†Sáx ûWJI.ËJ);hG’²H•rbY¾$¡²bìHªTÈH¨HÏÅ×Í%Ëb!+ªÙd9*ZL‘ô£O¼$R8Q»Üh'FéÛ/ZÇ÷£–ñ%¿ÙVÄB¹’ß*Èå‰sÓ§¼ƒ¡‰4 ñÉpl3ÎéÍVóä4’”¼×¸–œôe—Öüùº?{Öq¸¯iÆÊxxI.¢¤æ_-«˜$ §„M®ý¹5™s¢Êdt¸PFz K…b1>âÿæÃA’É8Ù¤BADr•÷äiììstÜT²%ëo4ðV,œbb8KòcK¥S!SÌ9“ßQ„§xAà/êÑEZ¶!9»ˆ£š•žÅq‰ÖN@eZ­Bœ'7B|Tò‰pí(*"!(¯d¥Üሣ]ƒuL‹b|s3"¹CóÕÆ—é’TF Ø[zú Ú®”I٦żÖxí¨b^:í(AHÓÅÑvƒ´ Šñîg­SªïÍÖ΢Ù2ÞaT³5ç)ë(¨ÕšÎʪ”¡±Ô{®vk’ ¸ô"=¹Iº 9ë(ßú^cfîzøEã#Ø¿Šc-©—ÄœVg/ïÉ™=½E&¥7ÔE‘”X|J ‘f:“©¨bÙY«5eJˆç­"n™äHGå”üÿ^«’Wý•,Òζ4ÌŶ¢i!%Rq!‰ü±J~[RIáb&Ë N"…vgÚe¡¨*Û8ºWá´õ;«öX|LIq<ñŸÄU£V?îaú6æ Þáy· ¨’wð’4hè J¾˜ÃQ¤Çgñ. ÉD<–$÷;<›˪ŒÓ`IZB9I|ZܕжT¾,I¸L\Vj…ÄQ L“7qÐø»˜4–é’Ä‘.ìV‹Æ œ.¼~’'{ãŽëÚ&iK\®äˆZ‡‰‹ŠÞÚˆhô Ȩ\8¥JE¥u–PÇIB™šàãFlÓ|ìB„ÜîÎÔžÄ|刅]|:i“ÙTs”$Nܧ¬“¤ ò\‹™Žœ÷Ì'“‰P %ð‘ôãáX0þ¸ÞÝ¿,Hzé½Wwý*îÕ.ã¦I¹Lz³JÑQêõ½í±í¾ÇRá(¹ÑƒQ9[Tp£KêVYÎK=ïÂ=§¬£ïCMû?kýæaŽKE%C>¸G·R6GqT~ÍÕ¦B²á C$ö`ã =jîOë^òS©;£±5­;lÝ^°Ï7ª”ö%:íü2Iòd2JO£^ò«›l¦“rÚ”Ì>¾¹#vÇño‚¤#ùí뵉tì3Ø ÿf¹7†š–óÿ;Ý™o=ÿï_YñzMóÿkëkk0ÿï´"RC×›¢9rŽÑƒ=c~eŠú—«e"Œ&4udXE¾ÎŒ!Uu8ÅéÃr2œšëÏkvŸw:¤Ë¯quŸÓêwê³Ô}KwGö¨û†îŽ>AÝ×twìê^ÓÝñ{©û²îN,tï•çwö]u‘]a@êßyê¾¢;:ôö|^wGu_ÔÝQZ_Gi}£—Ñú:Fëë8­¯ãoèîÄ]{ãîa467QÿMŒ‘ño0þ'ñ_eüÆø_füÿÈøÿñÿOÍï™bü§¨ÿ(S¤îuÇóaê~OwG¨}Ê‘oêîèyêÒò:FËë-¯ãˆº´¼NÐò:ñ\÷^ùhÀ¿õõôÕó‰gí^5}Þjs¡¤¢{.B¨M(‡c>‰®~ÿ¦’Éh˜Zr5yÄédþôº¤L„ãjVR¥lT,rõËä[ÛrŸÜ\³@ϱ‘ÿí†ú?;ý?Ëþ³üïºoä]fâ%êiWÆ¥zþÃûÖ'˜ÂĦ÷çÂsá¹ð\x.<è--ÇÝQÿÚ‰þ×èuÐÿ:Ü´¬ÿÝQÿÚ‰þ×èÜ£™þ×ãôgŒe,—¯óvcýò)> %B8A4!\¬Mý÷­/›ô¿ú}>/Ìÿ¸‚^îG¿Ë™ô¿Yêô¿Ž|‡ý¯†ö•F56Š’cÕdnFø i!þ8öÏmæÄ]]iª\木¦1ÓÑË:×9ô®:%$š‘ÛDùޤUVõª­:VìÙy$œÆÃZÊ{˜¨ª“¨æ¤~D†êÏ™bšëÏaŠ«ÒÅ"ÃØÃ­uÂ|ˆv;dE£þ—;8SgCkÛí®¶:Á9ŽÛÿè±iÿý^ŸiþŸh‡ï?W°Ñÿáã˜Ifƒ”Ö²ÒÿAÿÇ‘ •„#ýü6d`É»Hv²ÙèŽûÿÌ·=ÿëÇÿÁü+Àüïpã¸þ`.¸íù_RÿýPÿ×h6ÿK¦X=¿C0ÿK5þvјMý___nXÿ]ó¯Býw½Ü¿‹èÙ±·ÿ5GÔç Èüï,uí4àOTuN/òÏJ%Ã€Ž²ƒòJ¥P&3‘š !´B±@iºÎ¹®[ýZ±Žeßuš“÷šÓ‘ZYxˆ<ë_íXy{O~=±xqñITÜK’+vÝ ‡ö+ 7¤ãÉx$” ¥‡ÍÒÛI&êC`óÍT·ç‚áä#é`˜âȉ½+%_…²²nq‚€¬\Úsc³½¨?±ŽqÌ“Üõ¨ˆÕ_Þ"±Édg<ߊ‘lXˆêÝù˜—–v—˜+Ÿ?²ˆ¤g*bYQe1‡ýåÌ’£´:ÆÕ3¥Ãñ±¯™T8e´ú\$õ¨?áýþEà½Ë‡5¶f›qa#œÒëÿL­×‚»f«÷ËÃï¢f:˜æìL£q/¦-;ŒÖ½lËîd2ÅÓAË»ñÛk‹ú FGV³§µÂì(²XtÍ•Þè=FEÕš¨­¢&| tvƒdº³¼5G·%¶…É MŒ¤jÊeQ’wÚ°ú3QoqÇv«I:¸d)¨¡_gVû36Ðhëëîδˆl¸E Ãfª^ÝLfšI~|ŽÎ½ÄY n;ÔÍÍêW‘´"+šûŒfù}½4W õ4»‰#<œÃ½vó?Ý0ÓzþgÅ¿Ö`ÿe}yÖ\ÁÎþË>§w¤fùCFû=dÁ¼}É#€þÂG׿þècáóóž`n9y3ŸÖ®},úw8||ó‰ìÏùùÑýO}ÿáÖÂ?þ磫¿úõw~ù-~~üÕ/¯Ý²¤…ÿþw¾‰ÃöÕ¥‡ïçç'ñ½_þé¾~A¹‡ÿDyrë÷øù™•wî¾í¯µpþX‡ÿÇÂÒ'Îðó³ÏÜùúÏZøø«#8üûo¬,ìñóÇÿ&|ó¯nÕÂïȉ8ü__üÆ7ðó×ýWî3Oÿ@ ¿ë‹ÿ‰Ã¿õÁÀÂsüüõwýñƒ_&á_ûî÷ŽÃÿ~úÕ?ÆÏß(~õ•7¿¦¯Åwõo?YøïÇùù›~°ð—¯þH;ÿGéOâðW¸›Ÿ¹ÈÏßò¡¿8ù-üߺ ‡_ºôø0?Ïñ§ÎkïóµÝú/¾ýüùÿãçÏüáÇß<7¦…¿ÿ¡k}ÎN 3Œ.}èì¿Ï]w1¾if¨†ñßÀøO1þÆïgü0þMÆŸ`üO2þ,ã/0þgÿ'¨ÿH"¤ðׂîR>#xTHùÿ2ã_aü«Œñ¯3þ{ÿ}ŒŸgüŒ?ÀøƒU»ñ:,ôÛþR¿Ÿ_*ß¹ÉAöw+¾ƒ Y:ž}€äkm•¦ÿ¾ B|+Ô§eÙßïÜàpÐÆÞ_2Õ#û7­ŸîA\mÖ­ûOmýô×Ó§q@‡@ß}À!„´¤Ïsµ½/2þÏ1þ—ÿ«Œÿ›ŒÿuÆÿãÿ ãÿEͯ©73ü×1þÛÿמì5ô ;ý?Ý0ܶýßúú:èÿq°ÿ0ÜØÕÿn˜nÛþ©ÿ`ÿÁEî~®ßoÐgZÙÿ%²†–Ck!Äþ¼3CÎÿg–Ëàcasacore-3.7.1/ms/MSOper/test/tMSSummary.out000066400000000000000000000260231476623553700207040ustar00rootroot00000000000000 *** Test with SPECTRAL_WINDOW_1 MSSummary -------------------------------------- ================================================================================ MeasurementSet Name: tMSSummary_tmp.MS MS Version 2 ================================================================================ Observer: ProcessDCB v3.0.1 (2001/05/23 11:59) Project: 3C286 Observation: WSRT Data records: 87 Total elapsed time = 10 seconds Observed from 28-May-2001/02:26:50.0 to 28-May-2001/02:27:00.0 (UTC) ObservationID = 0 ArrayID = 0 Date Timerange (UTC) Scan FldId FieldName nRows SpwIds Average Interval(s) ScanIntent 28-May-2001/02:26:50.0 - 02:27:00.0 0 0 3C286 87 [0] [10] (nRows = Total number of rows per scan) Fields: 1 ID Code Name RA Decl Epoch nRows 0 3C2863C286 13:31:08.287994 +30.30.32.95981 J2000 87 Spectral Windows: (1 unique spectral windows and 1 unique polarization setups) SpwID Name #Chans Frame Ch0(MHz) ChanWid(kHz) TotBW(kHz) CtrFreq(MHz) Corrs 0 1 LSRK 4839.000 10000.000 10000.0 4839.0000 XX XY YX YY The SOURCE table is empty: see the FIELD table Antennas: 14: ID Name Station Diam. Long. Lat. Offset from array center (m) ITRF Geocentric coordinates (m) East North Elevation x y z 0 RT000 station 25.0 m +006.35.30.6 +52.43.48.1 -830.4792 -176.4150 65.4199 3828763.105447 442449.105665 5064923.007770 1 RT1 station 25.0 m +006.35.38.3 +52.43.48.1 -686.4971 -176.3957 65.4054 3828746.549573 442592.139508 5064923.007920 2 RT2 station 25.0 m +006.35.46.0 +52.43.48.1 -542.5111 -176.3786 65.3929 3828729.990814 442735.176964 5064923.008290 3 RT3 station 25.0 m +006.35.53.7 +52.43.48.1 -398.5275 -176.3674 65.3794 3828713.431099 442878.211893 5064923.004360 4 RT4 station 25.0 m +006.36.01.4 +52.43.48.1 -254.5414 -176.3575 65.3713 3828696.869944 443021.249173 5064923.003970 5 RT5 station 25.0 m +006.36.09.1 +52.43.48.1 -110.5563 -176.3577 65.3670 3828680.313919 443164.285969 5064923.000350 6 RT6 station 25.0 m +006.36.16.8 +52.43.48.1 33.4280 -176.3540 65.3662 3828663.751592 443307.321381 5064923.002040 7 RT7 station 25.0 m +006.36.24.5 +52.43.48.1 177.4112 -176.3586 65.3701 3828647.193428 443450.356046 5064923.002300 8 RT8 station 25.0 m +006.36.32.2 +52.43.48.1 321.3960 -176.3703 65.3730 3828630.634862 443593.392266 5064922.997550 9 RT9 station 25.0 m +006.36.39.9 +52.43.48.1 465.3817 -176.3819 65.3849 3828614.076068 443736.429416 5064923.000000 10 RTA station 25.0 m +006.36.45.0 +52.43.48.1 561.3737 -176.3984 65.3958 3828603.042448 443831.789699 5064922.998680 11 RTB station 25.0 m +006.36.50.2 +52.43.48.1 657.3759 -176.4098 65.4057 3828592.000715 443927.159360 5064922.999630 12 RTC station 25.0 m +006.37.51.2 +52.43.48.1 1797.2449 -176.7243 65.6338 3828460.924187 445059.520539 5064922.990710 13 RTD station 25.0 m +006.37.56.3 +52.43.48.1 1893.2333 -176.7650 65.6613 3828449.887093 445154.876836 5064922.987930 *** Test with SPECTRAL_WINDOW_2 MSSummary -------------------------------------- ================================================================================ MeasurementSet Name: tMSSummary_tmp.MS MS Version 2 ================================================================================ Observer: ProcessDCB v3.0.1 (2001/05/23 11:59) Project: 3C286 Observation: WSRT Data records: 87 Total elapsed time = 10 seconds Observed from 28-May-2001/02:26:50.0 to 28-May-2001/02:27:00.0 (UTC) ObservationID = 0 ArrayID = 0 Date Timerange (UTC) Scan FldId FieldName nRows SpwIds Average Interval(s) ScanIntent 28-May-2001/02:26:50.0 - 02:27:00.0 0 0 3C286 87 [0] [10] (nRows = Total number of rows per scan) Fields: 1 ID Code Name RA Decl Epoch nRows 0 3C2863C286 13:31:08.287994 +30.30.32.95981 J2000 87 Spectral Windows: (1 unique spectral windows and 1 unique polarization setups) SpwID Name #Chans Frame Ch0(MHz) ChanWid(kHz) TotBW(kHz) CtrFreq(MHz) Corrs 0 1 LSRK 4839.000 10000.000 10000.0 4839.0000 XX XY YX YY The SOURCE table is empty: see the FIELD table Antennas: 14: ID Name Station Diam. Long. Lat. Offset from array center (m) ITRF Geocentric coordinates (m) East North Elevation x y z 0 RT000 station 25.0 m +006.35.30.6 +52.43.48.1 -830.4792 -176.4150 65.4199 3828763.105447 442449.105665 5064923.007770 1 RT1 station 25.0 m +006.35.38.3 +52.43.48.1 -686.4971 -176.3957 65.4054 3828746.549573 442592.139508 5064923.007920 2 RT2 station 25.0 m +006.35.46.0 +52.43.48.1 -542.5111 -176.3786 65.3929 3828729.990814 442735.176964 5064923.008290 3 RT3 station 25.0 m +006.35.53.7 +52.43.48.1 -398.5275 -176.3674 65.3794 3828713.431099 442878.211893 5064923.004360 4 RT4 station 25.0 m +006.36.01.4 +52.43.48.1 -254.5414 -176.3575 65.3713 3828696.869944 443021.249173 5064923.003970 5 RT5 station 25.0 m +006.36.09.1 +52.43.48.1 -110.5563 -176.3577 65.3670 3828680.313919 443164.285969 5064923.000350 6 RT6 station 25.0 m +006.36.16.8 +52.43.48.1 33.4280 -176.3540 65.3662 3828663.751592 443307.321381 5064923.002040 7 RT7 station 25.0 m +006.36.24.5 +52.43.48.1 177.4112 -176.3586 65.3701 3828647.193428 443450.356046 5064923.002300 8 RT8 station 25.0 m +006.36.32.2 +52.43.48.1 321.3960 -176.3703 65.3730 3828630.634862 443593.392266 5064922.997550 9 RT9 station 25.0 m +006.36.39.9 +52.43.48.1 465.3817 -176.3819 65.3849 3828614.076068 443736.429416 5064923.000000 10 RTA station 25.0 m +006.36.45.0 +52.43.48.1 561.3737 -176.3984 65.3958 3828603.042448 443831.789699 5064922.998680 11 RTB station 25.0 m +006.36.50.2 +52.43.48.1 657.3759 -176.4098 65.4057 3828592.000715 443927.159360 5064922.999630 12 RTC station 25.0 m +006.37.51.2 +52.43.48.1 1797.2449 -176.7243 65.6338 3828460.924187 445059.520539 5064922.990710 13 RTD station 25.0 m +006.37.56.3 +52.43.48.1 1893.2333 -176.7650 65.6613 3828449.887093 445154.876836 5064922.987930 *** Test with SPECTRAL_WINDOW_3 MSSummary -------------------------------------- ================================================================================ MeasurementSet Name: tMSSummary_tmp.MS MS Version 2 ================================================================================ Observer: ProcessDCB v3.0.1 (2001/05/23 11:59) Project: 3C286 Observation: WSRT Data records: 87 Total elapsed time = 10 seconds Observed from 28-May-2001/02:26:50.0 to 28-May-2001/02:27:00.0 (UTC) ObservationID = 0 ArrayID = 0 Date Timerange (UTC) Scan FldId FieldName nRows SpwIds Average Interval(s) ScanIntent 28-May-2001/02:26:50.0 - 02:27:00.0 0 0 3C286 87 [0] [10] (nRows = Total number of rows per scan) Fields: 1 ID Code Name RA Decl Epoch nRows 0 3C2863C286 13:31:08.287994 +30.30.32.95981 J2000 87 Spectral Windows: (1 unique spectral windows and 1 unique polarization setups) SpwID Name #Chans Frame Ch0(MHz) ChanWid(kHz) TotBW(kHz) CtrFreq(MHz) Corrs 0 1 LSRK 4839.000 10000.000 10000.0 4839.0000 XX XY YX YY The SOURCE table is empty: see the FIELD table Antennas: 14: ID Name Station Diam. Long. Lat. Offset from array center (m) ITRF Geocentric coordinates (m) East North Elevation x y z 0 RT000 station 25.0 m +006.35.30.6 +52.43.48.1 -830.4792 -176.4150 65.4199 3828763.105447 442449.105665 5064923.007770 1 RT1 station 25.0 m +006.35.38.3 +52.43.48.1 -686.4971 -176.3957 65.4054 3828746.549573 442592.139508 5064923.007920 2 RT2 station 25.0 m +006.35.46.0 +52.43.48.1 -542.5111 -176.3786 65.3929 3828729.990814 442735.176964 5064923.008290 3 RT3 station 25.0 m +006.35.53.7 +52.43.48.1 -398.5275 -176.3674 65.3794 3828713.431099 442878.211893 5064923.004360 4 RT4 station 25.0 m +006.36.01.4 +52.43.48.1 -254.5414 -176.3575 65.3713 3828696.869944 443021.249173 5064923.003970 5 RT5 station 25.0 m +006.36.09.1 +52.43.48.1 -110.5563 -176.3577 65.3670 3828680.313919 443164.285969 5064923.000350 6 RT6 station 25.0 m +006.36.16.8 +52.43.48.1 33.4280 -176.3540 65.3662 3828663.751592 443307.321381 5064923.002040 7 RT7 station 25.0 m +006.36.24.5 +52.43.48.1 177.4112 -176.3586 65.3701 3828647.193428 443450.356046 5064923.002300 8 RT8 station 25.0 m +006.36.32.2 +52.43.48.1 321.3960 -176.3703 65.3730 3828630.634862 443593.392266 5064922.997550 9 RT9 station 25.0 m +006.36.39.9 +52.43.48.1 465.3817 -176.3819 65.3849 3828614.076068 443736.429416 5064923.000000 10 RTA station 25.0 m +006.36.45.0 +52.43.48.1 561.3737 -176.3984 65.3958 3828603.042448 443831.789699 5064922.998680 11 RTB station 25.0 m +006.36.50.2 +52.43.48.1 657.3759 -176.4098 65.4057 3828592.000715 443927.159360 5064922.999630 12 RTC station 25.0 m +006.37.51.2 +52.43.48.1 1797.2449 -176.7243 65.6338 3828460.924187 445059.520539 5064922.990710 13 RTD station 25.0 m +006.37.56.3 +52.43.48.1 1893.2333 -176.7650 65.6613 3828449.887093 445154.876836 5064922.987930 casacore-3.7.1/ms/MSOper/test/tMSSummary.run000077500000000000000000000011051476623553700206760ustar00rootroot00000000000000#!/bin/sh # Untar the input file to get the MS. rm -rf tMSSummary_tmp.MS tar zxf tMSSummary.in # Test with various ways of representing MEASINFO in CHAN_FREQ # and REF_FREQUENCY. # 1. no variable refcol # 2. variable refcol with and without uint TabRefCodes/Types # 3. int TabRefCodes with an unknown entry for TabRefCodes/Types for ext in 1 2 3 do echo echo echo "*** Test with SPECTRAL_WINDOW_$ext" rm -rf tMSSummary_tmp.MS/SPECTRAL_WINDOW cp -r tMSSummary_tmp.MS/SPECTRAL_WINDOW_$ext tMSSummary_tmp.MS/SPECTRAL_WINDOW $casa_checktool ./tMSSummary done casacore-3.7.1/ms/MSOper/test/tNewMSSimulator.cc000066400000000000000000000126421476623553700214600ustar00rootroot00000000000000//# tNewMSSimulator.cc: Tests the MS Simulator //# Copyright (C) 1995,1999,2000,2001,2016 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #define _POSIX_C_SOURCE 200809L //For mkdtemp(), stpcpy(), nftw() #include #include #include #include #include #include #include #include #include void test_NewMSSimulator_Constructors(); void test_NewMSSimulator_RandomAntenna(); int removeFile(const char *fpath, const struct stat *sb, int typeflag, struct FTW* ftwbuf); int main() { try { test_NewMSSimulator_Constructors(); test_NewMSSimulator_RandomAntenna(); } catch (const std::exception& x) { std::cerr << "Exception : " << x.what() << std::endl; return 1; } std::cout<<"OK"< simulator_p; std::string msName_p; }; /* * The most simple test on the simulator, just contruct it with defaults */ void test_NewMSSimulator_Constructors() { //Simple constructor NewMSSimulatorTester simulatorTester("newsim"); //This shared pointer gets deleted before the shared pointer inside simulator_p std::shared_ptr ms = simulatorTester.simulator_p->getMs(); //Constructor from existing MS. Not working. Has to be copied first? //NewMSSimulatorTester simulatorTesterFromMS(*ms); //Simple constructor NewMSSimulatorTester simulatorTester2("newsim2"); //This shared pointer gets deleted before the shared pointer inside simulator_p std::shared_ptr ms2 = simulatorTester.simulator_p->getMs(); //simulator is destroyed, but we keep a shared_ptr of the ms, so it should be fine simulatorTester2.simulator_p.reset(0); } /* * Filling random antenna information */ void test_NewMSSimulator_RandomAntenna() { std::mt19937 rng; int nAntMax = 10; for(int nAnt = 2 ; nAnt <= nAntMax; ++nAnt) { casacore::Vector x(nAnt), y(nAnt), z(nAnt), diam(nAnt), offset(nAnt); casacore::Vector mount(nAnt), name(nAnt), pad(nAnt); std::uniform_real_distribution<> dist(0., 100.); std::generate(x.begin(), x.end(), [&] () { return dist(rng); }); std::generate(y.begin(), y.end(), [&] () { return dist(rng); }); std::generate(z.begin(), z.end(), [&] () { return dist(rng); }); std::generate(diam.begin(), diam.end(), [&] () { return dist(rng); }); std::generate(offset.begin(), offset.end(), [&] () { return dist(rng); }); std::fill(mount.begin(), mount.end(), "ALT-AZ"); std::fill(name.begin(), name.end(), "NAME"); std::fill(pad.begin(), pad.end(), "PAD"); casacore::MPosition vlaPosition; casacore::MeasTable::Observatory(vlaPosition, "VLA"); NewMSSimulatorTester simulatorTester("newsim_"+std::to_string(nAnt)); simulatorTester.simulator_p->initAnt("Simulated", x, y, z, diam, offset, mount, name, pad, "global", vlaPosition); //This shared pointer gets deleted before the shared pointer inside simulator_p auto ms = simulatorTester.simulator_p->getMs(); } } casacore-3.7.1/ms/MSSel.h000066400000000000000000000037111476623553700151010ustar00rootroot00000000000000//# MSSel.h: MeasurementSet selection functionality //# Copyright (C) 1996,1997 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSEL_H #define MS_MSSEL_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // // // MeasurementSet selection functionality // // //
      • MeasurementSets modul // // // // // // The classes in this module make it possible to do selection in // MeasurementSets based on the dedicated CASA syntax which translates // to TaQL commands. // // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/000077500000000000000000000000001476623553700147265ustar00rootroot00000000000000casacore-3.7.1/ms/MSSel/MSAntennaGram.cc000066400000000000000000000235001476623553700176700ustar00rootroot00000000000000//# MSAntennaGram.cc: Grammar for antenna expressions //# Copyright (C) 1998,1999,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // MSAntennaGram; grammar for antenna command lines // This file includes the output files of bison and flex for // parsing command lines operating on lattices. #include #include #include #include // routines used by bison actions #include #include //# stdlib.h is needed for bison 1.28 and needs to be included here //# (before the flex/bison files). //# Bison defines WHERE, which is also defined in LogOrigin.h (which //# is included in auto-template mode). //# So undefine WHERE first. #undef WHERE #include //# Let clang and gcc ignore some warnings in bison/flex code. //# Undef YY_NULL because flex can redefine it slightly differently (giving a warning). #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpragmas" #pragma GCC diagnostic ignored "-Wdeprecated-register" #pragma GCC diagnostic ignored "-Wsign-compare" #include "MSAntennaGram.ycc" // bison output #ifdef YY_NULL # undef YY_NULL #endif #include "MSAntennaGram.lcc" // flex output #pragma GCC diagnostic pop // Define the yywrap function for flex. int MSAntennaGramwrap() { return 1; } namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Declare a file global pointer to a char* for the input string. static const char* strpMSAntennaGram = 0; static Int posMSAntennaGram = 0; //# Parse the command. //# Do a yyrestart(yyin) first to make the flex scanner reentrant. TableExprNode baseMSAntennaGramParseCommand(MSAntennaParse* parser, const String& command, Vector& selectedAnts1, Vector& selectedAnts2, Matrix& selectedBaselines) { try { MSAntennaGramrestart (MSAntennaGramin); yy_start = 1; strpMSAntennaGram = command.chars(); // get pointer to command string posMSAntennaGram = 0; // initialize string position parser->setComplexity(); MSAntennaParse::thisMSAParser = parser; // The global pointer to the parser MSAntennaGramparse(); // parse command string selectedAnts1.reference (parser->selectedAnt1()); selectedAnts2.reference (parser->selectedAnt2()); selectedBaselines.reference (parser->selectedBaselines()); return parser->node(); } catch (MSSelectionAntennaError& x) { String newMesgs; newMesgs = constructMessage(msAntennaGramPosition(),command); x.addMessage(newMesgs); throw; } } TableExprNode msAntennaGramParseCommand (MSSelectableTable& msLike, const String& command, Vector& selectedAnts1, Vector& selectedAnts2, Matrix& selectedBaselines) { TableExprNode col1TEN = msLike.col(msLike.columnName(MS::ANTENNA1)), col2TEN = msLike.col(msLike.columnName(MS::ANTENNA2)); TableExprNode antennaTEN; // MSAntennaParse *thisParser = new MSAntennaParse(msLike.antenna(), col1TEN, col2TEN); MSAntennaParse thisParser(msLike.antenna(), col1TEN, col2TEN); try { antennaTEN = baseMSAntennaGramParseCommand(&thisParser, command, selectedAnts1, selectedAnts2, selectedBaselines); } catch(MSSelectionAntennaError &x) { // delete thisParser; throw; } //delete thisParser; return antennaTEN; } TableExprNode msAntennaGramParseCommand (Table& subTable, TableExprNode& col1TEN, TableExprNode& col2TEN, const String& command, Vector& selectedAnts1, Vector& selectedAnts2, Matrix& selectedBaselines) { // TableExprNode col1TEN = msLike.col(msLike.columnName(MS::ANTENNA1)), // col2TEN = msLike.col(msLike.columnName(MS::ANTENNA2)); TableExprNode antennaTEN; // MSAntennaParse *thisParser = new MSAntennaParse(msLike.antenna(), col1TEN, col2TEN); MSAntennaParse thisParser(subTable, col1TEN, col2TEN); try { antennaTEN = baseMSAntennaGramParseCommand(&thisParser, command, selectedAnts1, selectedAnts2, selectedBaselines); } catch(MSSelectionAntennaError &x) { // delete thisParser; throw; } //delete thisParser; return antennaTEN; } TableExprNode msAntennaGramParseCommand (MSAntennaParse* thisParser, const String& command, Vector& selectedAnts1, Vector& selectedAnts2, Matrix& selectedBaselines) { TableExprNode antennaTEN; try { antennaTEN=baseMSAntennaGramParseCommand(thisParser, command, selectedAnts1, selectedAnts2, selectedBaselines); } catch(MSSelectionAntennaError &x) { delete thisParser; throw; } delete thisParser; return antennaTEN; } TableExprNode msAntennaGramParseCommand (const MeasurementSet* ms, const String& command, Vector& selectedAnts1, Vector& selectedAnts2, Matrix& selectedBaselines) { TableExprNode antennaTEN; TableExprNode col1AsTEN = ms->col(ms->columnName(MS::ANTENNA1)), col2AsTEN = ms->col(ms->columnName(MS::ANTENNA2)); MSAntennaParse *thisParser = new MSAntennaParse(ms->antenna(),col1AsTEN, col2AsTEN); try { antennaTEN=baseMSAntennaGramParseCommand(thisParser, command, selectedAnts1, selectedAnts2, selectedBaselines); } catch(MSSelectionAntennaError &x) { delete thisParser; throw; } delete thisParser; return antennaTEN; // try // { // MSAntennaGramrestart (MSAntennaGramin); // yy_start = 1; // strpMSAntennaGram = command.chars(); // get pointer to command string // posMSAntennaGram = 0; // initialize string position // MSAntennaParse parser(ms); // setup measurement set // parser.setComplexity(); // MSAntennaParse::thisMSAParser = &parser; // The global pointer to the parser // MSAntennaGramparse(); // parse command string // selectedAnts1.reference (parser.selectedAnt1()); // selectedAnts2.reference (parser.selectedAnt2()); // selectedBaselines.reference (parser.selectedBaselines()); // return parser.node(); // } // catch (MSSelectionAntennaError& x) // { // String newMesgs; // newMesgs = constructMessage(msAntennaGramPosition(),command); // x.addMessage(newMesgs); // throw; // } } //# Give the string position. Int& msAntennaGramPosition() { return posMSAntennaGram; } //# Get the next input characters for flex. int msAntennaGramInput (char* buf, int max_size) { int nr=0; while (*strpMSAntennaGram != 0) { if (nr >= max_size) { break; // get max. max_size char. } buf[nr++] = *strpMSAntennaGram++; } return nr; } void MSAntennaGramerror (const char*) { throw (MSSelectionAntennaParseError ("Antenna Expression: Parse error at or near '" + String(MSAntennaGramtext) + "'")); } // String msAntennaGramRemoveEscapes (const String& in) // { // String out; // int leng = in.length(); // for (int i=0; i #include #include #include // routines used by bison actions namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MeasurementSet; class TableExprNode; // // Global functions for flex/bison scanner/parser for MSAntennaGram // // // // // //# Classes you should understand before using this one. //
      • MSAntennaGram.l and .y (flex and bison grammar) // // // Global functions are needed to define the input of the flex scanner // and to start the bison parser. // The input is taken from a string. // // // It is necessary to be able to give an image expression in ASCII. // This can be used in glish. // // //# A List of bugs, limitations, extensions or planned refinements. // // // Declare the bison parser (is implemented by bison command). // It returns a TaQL expression tree. TableExprNode msAntennaGramParseCommand (MSSelectableTable& msLike, const String& command, Vector& selectedAnts1, Vector& selectedAnts2, Matrix& selectedBaselines) ; TableExprNode msAntennaGramParseCommand (MSAntennaParse* thisParser, const TableExprNode& col1TEN, const TableExprNode& col2TEN, const String& command, Vector& selectedAnts1, Vector& selectedAnts2, Matrix& selectedBaselines) ; TableExprNode msAntennaGramParseCommand (Table& subTable, TableExprNode& col1TEN, TableExprNode& col2TEN, const String& command, Vector& selectedAnts1, Vector& selectedAnts2, Matrix& selectedBaselines) ; TableExprNode msAntennaGramParseCommand (const MeasurementSet *ms, const String& command, Vector& selectedAnt1, Vector& selectedAnt2, Matrix& selectedBaselines); TableExprNode baseMSAntennaGramParseCommand(MSAntennaParse* parser, const String& command, Vector& selectedAnts1, Vector& selectedAnts2, Matrix& selectedBaselines); // The yyerror function for the parser. // It throws an exception with the current token. void MSAntennaGramerror (const char*); // Give the current position in the string. // This can be used when parse errors occur. Int& msAntennaGramPosition(); // Declare the input routine for flex/bison. int msAntennaGramInput (char* buf, int max_size); // A function to remove escaped characters. //String msAntennaGramRemoveEscapes (const String& in); // A function to remove quotes from a quoted string. //String msAntennaGramRemoveQuotes (const String& in); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSAntennaGram.ll000066400000000000000000000125161476623553700177170ustar00rootroot00000000000000/* -*- C -*- MSAntennaGram.l: Lexical analyzer for ms selection commands Copyright (C) 1994,1995,1996,1997,1998,2001,2003 Associated Universities, Inc. Washington DC, USA. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. Correspondence concerning AIPS++ should be addressed as follows: Internet email: casa-feedback@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA */ /* yy_unput is not used, so let flex not generate it, otherwise picky compilers will issue warnings. */ %option nounput %{ #undef YY_INPUT #define YY_INPUT(buf,result,max_size) result=msAntennaGramInput(buf,max_size) #undef YY_DECL #define YY_DECL int MSAntennaGramlex (YYSTYPE* lvalp) %} WHITE [ \t\n]* DIGIT [0-9] INT {DIGIT}+ EXP [DdEe][+-]?{INT} FLOAT {INT}{EXP}|{INT}"."{DIGIT}*({EXP})?|{DIGIT}*"."{INT}({EXP})? QSTRING \"[^\"\n]*\" ASTRING \'[^\'\n]*\' STRING ({QSTRING}|{ASTRING})+ REGEX "^"?"/"[^/\n]*"/" ALPHA [a-zA-Z] UNIT {ALPHA}+ NAMECHAR [-+a-zA-Z0-9_.] NAMENNUM [a-zA-Z_] COLON ":" NAME1 {NAMENNUM}{NAMECHAR}* NAME2 {NAMECHAR}*{COLON}({NAMECHAR}|{COLON})* NAME {NAME1}|{NAME2} ESCNAME \\[^ \t\n]+ PATTCHAR [][*?^] PATT1 ({NAMENNUM}|{PATTCHAR})({NAMECHAR}|{PATTCHAR})* PATT2 ({NAMECHAR}|{PATTCHAR})*{COLON}({NAMECHAR}|{PATTCHAR}|{COLON})* PATTERN {PATT1}|{PATT2} /* rules */ %% {STRING} { int lenstr = strlen(MSAntennaGramtext) - 2; lvalp->str = (char*)malloc(lenstr+1); strncpy(lvalp->str, MSAntennaGramtext+1, lenstr); lvalp->str[lenstr] = '\0'; return QSTRING; } {REGEX} { int lenstr = strlen(MSAntennaGramtext) - 2; lvalp->str = (char*)malloc(lenstr+1); strncpy(lvalp->str, MSAntennaGramtext+1, lenstr); if (MSAntennaGramtext[0] == '^') lvalp->str[0] = '^'; lvalp->str[lenstr] = '\0'; if (strchr (lvalp->str, '&')) { return BLREGEX; } return REGEX; } {UNIT} { msAntennaGramPosition() += yyleng; lvalp->str = (char*)malloc(strlen(MSAntennaGramtext) + 1); strcpy(lvalp->str, MSAntennaGramtext); return UNIT; } {NAME} { msAntennaGramPosition() += yyleng; lvalp->str = (char *)malloc((strlen(MSAntennaGramtext) + 1)); strcpy(lvalp->str, MSAntennaGramtext); return IDENTIFIER; } {ESCNAME} { msAntennaGramPosition() += yyleng; lvalp->str = (char *)malloc((strlen(MSAntennaGramtext))); strcpy(lvalp->str, MSAntennaGramtext+1); return IDENTIFIER; } {PATTERN} { msAntennaGramPosition() += yyleng; lvalp->str = (char *)malloc((strlen(MSAntennaGramtext) + 1)); strcpy(lvalp->str, MSAntennaGramtext); return QSTRING; } {INT} { msAntennaGramPosition() += yyleng; lvalp->str = (char*)malloc(strlen(MSAntennaGramtext) + 1); strcpy(lvalp->str, MSAntennaGramtext); return INT; } {FLOAT} { msAntennaGramPosition() += yyleng; lvalp->str = (char*)malloc(strlen(MSAntennaGramtext) + 1); strcpy(lvalp->str, MSAntennaGramtext); return FLOAT; } ";" { msAntennaGramPosition() += yyleng; return SEMICOLON; } "@" { msAntennaGramPosition() += yyleng; return AT; } "&" { msAntennaGramPosition() += yyleng; return AMPERSAND; } "~" { msAntennaGramPosition() += yyleng; return DASH; } "," { msAntennaGramPosition() += yyleng; return COMMA;} "!" { msAntennaGramPosition() += yyleng; return NOT;} "(" { msAntennaGramPosition() += yyleng; return LPAREN; } ")" { msAntennaGramPosition() += yyleng; return RPAREN;} "<" { msAntennaGramPosition() += yyleng; return LT;} "<=" { msAntennaGramPosition() += yyleng; return LE;} ">" { msAntennaGramPosition() += yyleng; return GT;} ">=" { msAntennaGramPosition() += yyleng; return GE;} {WHITE} { msAntennaGramPosition() += yyleng;} /* Eat whitespace */ /* An unterminated string is an error */ \'|\" { throw MSSelectionAntennaError ("Unterminated string"); } /* An unterminated regex is an error */ "/" { throw MSSelectionAntennaError ("Unterminated regex"); } /* terminate on EOF */ <> { yyterminate(); } /* Any other character is invalid */ . { return YYERRCODE; } %% casacore-3.7.1/ms/MSSel/MSAntennaGram.yy000066400000000000000000000440121476623553700177450ustar00rootroot00000000000000/*-*- C++ -*- MSAntennaGram.y: Parser for antenna expressions Copyright (C) 2004 Associated Universities, Inc. Washington DC, USA. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. Correspondence concerning AIPS++ should be addressed as follows: Internet email: casa-feedback@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA */ %{ using namespace casacore; %} %pure-parser /* make parser re-entrant */ %union { const TableExprNode* node; char* str; double dval; Vector* iv; std::vector* dv; std::vector* ds; Vector* is; } %token AT %token COMMA %token SEMICOLON %token AMPERSAND %token DASH %token NOT %token LPAREN %token RPAREN %token LT %token LE %token GT %token GE %token INT %token FLOAT %token UNIT %token QSTRING %token REGEX %token BLREGEX %token IDENTIFIER %type antennastatement %type indexcombexpr %type baseline %type gbaseline %type identstr %type flt %type flint %type unit %type flintunit %type flunit %type antlist %type antidrange %type antids %type antid %type stationid %type stationlist %type antatstation %type antcomp %type stationcomp %type blength %type blengthlist %type blregexlist // %destructor {free ($$);} INT FLOAT UNIT QSTRING REGEX IDENTIFIER identstr // %destructor {delete ($$);} antlist antidrange antids antid stationid stationlist antatstation antcomp stationcomp // %destructor {delete ($$);} blength blengthlist %{ #include #include int MSAntennaGramlex (YYSTYPE*); Bool MSAntennaGramNegate=False; void reportError(char *token,String source=String("")) { LogIO logIO; ostringstream Mesg,tok; if (source != "") Mesg << source; else Mesg << "Antenna Expression"; Mesg << ": No match found for token(s) "; tok << "\""; if (MSAntennaGramNegate) tok << "!"; tok << token << "\""; MSAntennaParse::thisMSAErrorHandler->reportError(tok.str().c_str(), Mesg.str()); Mesg << tok.str(); // if (MSAntennaGramNegate) // { // logIO << Mesg.str() // << " (just a helpful message (from your friendly MSAntennaSelection object))" // << LogIO::WARN; // } // else // // throw(MSSelectionAntennaParseError(Mesg.str())); // logIO << Mesg.str() << LogIO::WARN; } // // Keep life from getting too computer-like (encouragement may be a // defining human need/quality). // void kungrachulations(const std::bitset& complexity) { LogIO logIO(LogOrigin("MSAntannaParse","")); Bool level1=(complexity.test(MSAntennaParse::ANTREGEX) & complexity.test(MSAntennaParse::ANTLIST) & complexity.test(MSAntennaParse::BASELINELIST)); Bool level2=(level1 & complexity.test(MSAntennaParse::STATIONLIST)); Bool level3=(level2 & complexity.test(MSAntennaParse::STATIONREGEX) & complexity.test(MSAntennaParse::ANTATSTATIONLIST)); if (level3) logIO << "Oh the brave one!\n " "You successfully passed the deepest abyss of parsing in baseline selection without error.\n " "May The Force (or the CASA User Support Group) be with you. Good luck." << LogIO::POST; else if (level2) logIO << "Many congratulations. You are using an expert level of complexity in baseline selection.\n " // "Tread carefully at this level (easy to go wrong)." << LogIO::POST; else if (level1) logIO << "Congratulations. You are using a respectable level of complextiy in baseline selection." << LogIO::POST; } %} %% antennastatement: indexcombexpr { $$ = $1; kungrachulations(MSAntennaParse::thisMSAParser->getComplexity()); } /* | LPAREN indexcombexpr RPAREN {$$ = $2;}*/ indexcombexpr: gbaseline {$$=$1;} | indexcombexpr SEMICOLON gbaseline { $$ = $1; MSAntennaParse::thisMSAParser->setComplexity(MSAntennaParse::BASELINELIST); } gbaseline: NOT {MSAntennaGramNegate=True;} baseline {$$=$3;} | {MSAntennaGramNegate=False;} baseline {$$=$2;} baseline: antlist AMPERSAND antlist // Two non-identical lists for the '&' operator { // MSAntennaIndex myMSAI(MSAntennaParse::thisMSAParser->ms()->antenna()); MSAntennaIndex myMSAI(MSAntennaParse::thisMSAParser->subTable()); Vector a1 = myMSAI.matchId(*($1)); Vector a2 = myMSAI.matchId(*($3)); $$ = MSAntennaParse::thisMSAParser->selectAntennaIds (a1,a2,MSAntennaParse::CrossOnly, MSAntennaGramNegate); delete $1; delete $3; } | antlist AMPERSAND // Implicit same list on the RHS of '&' operator { // MSAntennaIndex myMSAI(MSAntennaParse::thisMSAParser->ms()->antenna()); // cerr << "### ANTLIST&" << endl; // if (!MSAntennaParse::thisMSAParser->msInterface()->isMS()) reportError("& opertor invalid",""); // else { MSAntennaIndex myMSAI(MSAntennaParse::thisMSAParser->subTable()); Vector a1 = myMSAI.matchId(*($1)); $$ = MSAntennaParse::thisMSAParser->selectAntennaIds (a1,a1,MSAntennaParse::CrossOnly, MSAntennaGramNegate); } delete $1; } | antlist //Match ANTLIST & ALLANTENNAS (implicit "&*") { // MSAntennaIndex myMSAI(MSAntennaParse::thisMSAParser->ms()->antenna()); MSAntennaIndex myMSAI(MSAntennaParse::thisMSAParser->subTable()); Vector a1 = myMSAI.matchId(*($1)); $$ = MSAntennaParse::thisMSAParser->selectAntennaIds (a1,MSAntennaParse::CrossOnly, MSAntennaGramNegate); delete $1; } | antlist AMPERSAND AMPERSAND antlist /*Include self-correlations*/ { // MSAntennaIndex myMSAI(MSAntennaParse::thisMSAParser->ms()->antenna()); MSAntennaIndex myMSAI(MSAntennaParse::thisMSAParser->subTable()); Vector a1 = myMSAI.matchId(*($1)); Vector a2 = myMSAI.matchId(*($4)); $$ = MSAntennaParse::thisMSAParser->selectAntennaIds (a1,a2,MSAntennaParse::AutoCorrAlso, MSAntennaGramNegate); delete $1; delete $4; } | antlist AMPERSAND AMPERSAND // Include self-correlations { // MSAntennaIndex myMSAI(MSAntennaParse::thisMSAParser->ms()->antenna()); MSAntennaIndex myMSAI(MSAntennaParse::thisMSAParser->subTable()); Vector a1 = myMSAI.matchId(*($1)); $$ = MSAntennaParse::thisMSAParser->selectAntennaIds (a1,a1,MSAntennaParse::AutoCorrAlso, MSAntennaGramNegate); delete $1; } | antlist AMPERSAND AMPERSAND AMPERSAND // Only self-correlations :-) { // MSAntennaIndex myMSAI(MSAntennaParse::thisMSAParser->ms()->antenna()); MSAntennaIndex myMSAI(MSAntennaParse::thisMSAParser->subTable()); Vector a1 = myMSAI.matchId(*($1)); $$ = MSAntennaParse::thisMSAParser->selectAntennaIds (a1,MSAntennaParse::AutoCorrOnly, MSAntennaGramNegate); delete $1; } | blregexlist // baseline regex list { $$ = MSAntennaParse::thisMSAParser->selectBLRegex (*$1, MSAntennaGramNegate); delete $1; } | blengthlist // baseline length list { $$ = MSAntennaParse::thisMSAParser->selectLength (*$1, MSAntennaGramNegate); delete $1; } identstr: IDENTIFIER { $$ = $1; } | UNIT { $$ = $1; } // a unit is an aphabetic name, so here it is a name // A single station name (this could be a regex and hence produce a // list of indices) stationid: identstr // IDENTIFIER { // Use the string as-is. This cannot include patterns/regex // which has characters that are part of range or list // syntax (',', '-') (that's all I think). // // Convert name to index // // MSAntennaIndex myMSAI(MSAntennaParse::thisMSAParser->ms()->antenna()); MSAntennaIndex myMSAI(MSAntennaParse::thisMSAParser->subTable()); $$=new Vector(myMSAI.matchStationName($1)); if ((*($$)).nelements() == 0) reportError($1,"Station Expression"); free($1); } | QSTRING { // Quoted string: This is a pattern which will be converted // to regex internally. E.g. "VLA{20,21}*" becomes // "VLA((20)|(21)).*" regex. This can include any character // string. // // Convert name to index // // MSAntennaIndex myMSAI(MSAntennaParse::thisMSAParser->ms()->antenna()); MSAntennaIndex myMSAI(MSAntennaParse::thisMSAParser->subTable()); $$ = new Vector(myMSAI.matchStationRegexOrPattern($1)); if ((*($$)).nelements() == 0) reportError($1,"Station Expression"); free($1); MSAntennaParse::thisMSAParser->setComplexity(MSAntennaParse::STATIONREGEX); } | REGEX { // A string delimited by a pair of '/': This will be treated // as a regular expression internally. // // Convert name to index // // MSAntennaIndex myMSAI(MSAntennaParse::thisMSAParser->ms()->antenna()); MSAntennaIndex myMSAI(MSAntennaParse::thisMSAParser->subTable()); $$ = new Vector(myMSAI.matchStationRegexOrPattern($1,True)); if ((*($$)).nelements() == 0) reportError($1,"Station Expression"); free($1); MSAntennaParse::thisMSAParser->setComplexity(MSAntennaParse::STATIONREGEX); } // A single antenna name (this could be a regex and hence produce a // list of indices) antid: identstr { // Use the string as-is. This cannot include patterns/regex // which has characters that are part of range or list // syntax (',', '-') (that's all I think). // // Convert name to index // // MSAntennaIndex myMSAI(MSAntennaParse::thisMSAParser->ms()->antenna()); MSAntennaIndex myMSAI(MSAntennaParse::thisMSAParser->subTable()); $$=new Vector(myMSAI.matchAntennaName($1)); //$$=new Vector(myMSAI.matchAntennaRegexOrPattern($1)); if ((*($$)).nelements() == 0) reportError($1); free($1); } | QSTRING { // Quoted string: This is a pattern which will be converted // to regex internally. E.g. "VLA{20,21}*" becomes // "VLA((20)|(21)).*" regex. This can include any character // string. // // Convert name to index // // MSAntennaIndex myMSAI(MSAntennaParse::thisMSAParser->ms()->antenna()); MSAntennaIndex myMSAI(MSAntennaParse::thisMSAParser->subTable()); $$ = new Vector(myMSAI.matchAntennaRegexOrPattern($1)); if ((*($$)).nelements() == 0) reportError($1); free($1); MSAntennaParse::thisMSAParser->setComplexity(MSAntennaParse::ANTREGEX); } | REGEX { // A string delimited by a pair of '/': This will be treated // as a regular expression internally. // // Convert name to index // // MSAntennaIndex myMSAI(MSAntennaParse::thisMSAParser->ms()->antenna()); MSAntennaIndex myMSAI(MSAntennaParse::thisMSAParser->subTable()); $$ = new Vector(myMSAI.matchAntennaRegexOrPattern($1,True)); if ((*($$)).nelements() == 0) reportError($1); free($1); MSAntennaParse::thisMSAParser->setComplexity(MSAntennaParse::ANTREGEX); } antidrange: INT // A single antenna index { // // This code is due to VLA specienfic complication // arising due to the fact that VLA antennam "NAMES" // are strings that can be parsed as valid integers! // Believe it or not, VLA antenna NAMES are "1", "2", // "3" and so on..... So (phew). Just for antenna // selection (and this *just* because of silly // convention for VLA antenna naming!), if we get an // INT, treat it as name still and first attempt a // match with the NAME column. If that fails, treat it // as an integer index and do the right thing. // // MSAntennaIndex myMSAI(MSAntennaParse::thisMSAParser->ms()->antenna()); MSAntennaIndex myMSAI(MSAntennaParse::thisMSAParser->subTable()); Vector tmp(myMSAI.matchAntennaName($1)); $$ = new Vector(1); if (tmp.nelements() > 0) (*($$))(0) = tmp[0]; else (*($$))(0) = atoi($1); free($1); } | INT DASH INT // A range of integer antenna indices { Int start = atoi($1); Int end = atoi($3); Int len = end - start + 1; Vector antennaids(len); for(Int i = 0; i < len; i++) antennaids[i] = start + i; $$ = new Vector(len); // MSAntennaIndex myMSAI(MSAntennaParse::thisMSAParser->ms()->antenna()); MSAntennaIndex myMSAI(MSAntennaParse::thisMSAParser->subTable()); for (Int i=0; i tmp(myMSAI.matchAntennaName(vlaName)); if (tmp.nelements() > 0) ((*$$))[i] = tmp[0]; else ((*$$))[i] = antennaids[i]; } free($1); free($3); } stationlist: stationid { $$ = new Vector(*$1); delete $1; } | stationlist COMMA stationid { $$ = $1; Int N0=(*($1)).nelements(), N1 = (*($3)).nelements(); (*($$)).resize(N0+N1,True); // Resize the existing list for(Int i=N0;isetComplexity(MSAntennaParse::STATIONLIST); } antids: antid {$$ = $1;}// A singe antenna ID | antidrange {$$ = $1;} | antatstation {$$ = $1;} antlist: antids { $$ = new Vector(*$1); delete $1; } | antlist COMMA antids // AnetnnaID, AntennaID,... { $$ = $1; Int N0=(*($1)).nelements(), N1 = (*($3)).nelements(); (*($$)).resize(N0+N1,True); // Resize the existing list for(Int i=N0;isetComplexity(MSAntennaParse::ANTLIST); } antcomp: antid {$$=$1;} | antidrange {$$=$1;} | LPAREN antlist RPAREN {$$=$2;MSAntennaParse::thisMSAParser->setComplexity(MSAntennaParse::ANTATSTATIONLIST);} stationcomp: stationid {$$=$1;} | LPAREN stationlist RPAREN {$$=$2;MSAntennaParse::thisMSAParser->setComplexity(MSAntennaParse::ANTATSTATIONLIST);} antatstation: antcomp AT stationcomp { $$ = new Vector(set_intersection(*($1),*($3))); ostringstream token;token << "AntID("<<*($1)<<")@StationID("<<*($3)<<")"; if ((*($$)).nelements() == 0) reportError((char *)token.str().c_str(),"Ant@Station Expression"); delete $1; delete $3; } | AT stationcomp //Implicit ANT. { $$ = new Vector(*($2)); ostringstream token;token << "@StationID("<<*($2)<<")"; if ((*($$)).nelements() == 0) reportError((char *)token.str().c_str(),"Station Expression"); delete $2; } blregexlist: BLREGEX { $$ = new std::vector(); $$->push_back (String($1)); free ($1); } | blregexlist COMMA BLREGEX { $$ = $1; $$->push_back (String($3)); free ($3); } blengthlist: blength { $$ = $1; } | blengthlist COMMA blength { $$ = $1; $$->push_back ((*$3)[0]); $$->push_back ((*$3)[1]); delete $3; } blength: LT flunit { $$ = new std::vector(); $$->push_back (-1e30); $$->push_back ($2 - 0.000000001); } | LE flunit { $$ = new std::vector(); $$->push_back (-1e30); $$->push_back ($2); } | GT flunit { $$ = new std::vector(); $$->push_back ($2 + 0.000000001); $$->push_back (1e30); } | GE flunit { $$ = new std::vector(); $$->push_back ($2); $$->push_back (1e30); } | flt DASH flt { $$ = new std::vector(); $$->push_back ($1); $$->push_back ($3); } | flt DASH flt unit { $$ = new std::vector(); $$->push_back ($1 / $4); $$->push_back ($3 / $4); } | flintunit DASH flintunit { $$ = new std::vector(); $$->push_back ($1); $$->push_back ($3); } flunit: flint { $$ = $1; } | flintunit { $$ = $1; } flintunit: flint unit { $$ = $1/$2; } unit: UNIT { $$ = MSAntennaParse::getUnitFactor ($1); free($1); } flint: flt { $$ = $1; } | INT { $$ = atoi($1); free ($1); } flt: FLOAT { $$ = atof($1); free ($1); } %% casacore-3.7.1/ms/MSSel/MSAntennaIndex.cc000066400000000000000000000261641476623553700200620ustar00rootroot00000000000000//# MSAntennaIndex.cc: implementation of MSAntennaIndex.h //# Copyright (C) 2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //------------------------------------------------------------------------- MSAntennaIndex::MSAntennaIndex(const MSAntenna& antenna) : msAntennaCols_p(antenna) { // Construct from an MS ANTENNA subtable // Input: // antenna const MSAntenna& Input MSAntenna object // Output to private data: // msAntennaCols_p MSAntennaColumns MSAntenna columns accessor // antennaIds_p Vector Antenna id's // nrows_p Int Number of rows // // Generate an array of antenna id's, used in later queries nrows_p = msAntennaCols_p.nrow(); antennaIds_p.resize(nrows_p); stationIds_p.resize(nrows_p); indgen(antennaIds_p); indgen(stationIds_p); } //------------------------------------------------------------------------- Vector MSAntennaIndex::matchId(const Vector& sourceId) { Vector IDs; IDs = set_intersection(sourceId,antennaIds_p); if (IDs.nelements() == 0) { ostringstream mesg; mesg << "No match found for the antenna specificion [ID(s): " << sourceId << "]"; // Use the error handler if defined, otherwise throw. if (MSAntennaParse::thisMSAErrorHandler) { MSAntennaParse::thisMSAErrorHandler->reportError ("", mesg.str()); } else { throw (MSSelectionAntennaParseError(mesg)); } } return IDs; } //------------------------------------------------------------------------- Vector MSAntennaIndex::matchAntennaRegexOrPattern(const String& pattern, const Bool regex) { // Match a regular expression or pattern to a set of antenna id's // Input: // pattern const String& Pattern/regular expression // for Antenna name to match // Output: // matchAntennaName Vector Matching antenna id's // Int pos=0; Bool negate = False; String patt = pattern; if (patt[0] == '^') { negate = True; patt = patt.from(1); } Regex reg; if (regex) { reg=patt; } else { reg=reg.fromPattern(patt); } // cerr << "Pattern = " << pattern << " Regex = " << reg.regexp() << endl; IPosition sh(msAntennaCols_p.name().getColumn().shape()); LogicalArray maskArray(sh,False); IPosition i=sh; for(i(0)=0;i(0)0) != negate ); // && !msAntennaCols_p.flagRow().getColumn()(i)); } MaskedArray maskAntennaID(antennaIds_p,maskArray); return maskAntennaID.getCompressedArray(); } //------------------------------------------------------------------------- Vector MSAntennaIndex::matchAntennaName(const String& name) { // Match a antenna name to a set of antenna id's // Input: // name const String& Antenna name to match // Output: // matchAntennaName Vector Matching antenna id's // /* if(name == "*") { LogicalArray maskArray = (True && (msAntennaCols_p.flagRow().getColumn()== msAntennaCols_p.flagRow().getColumn())); // !msAntennaCols_p.flagRow().getColumn()); MaskedArray maskAntennaId(antennaIds_p, maskArray); return maskAntennaId.getCompressedArray(); } else */ { LogicalArray maskArray = (msAntennaCols_p.name().getColumn()==name); MaskedArray maskAntennaId(antennaIds_p, maskArray); // // If no match with the NAME column, try with the names in the STATION column. // if (maskAntennaId.getCompressedArray().nelements() == 0) { maskArray = (msAntennaCols_p.station().getColumn()==name); maskAntennaId.setData(antennaIds_p, maskArray); } return maskAntennaId.getCompressedArray(); } } //------------------------------------------------------------------------- Vector MSAntennaIndex::matchAntennaName(const Vector& names) { // Match a set of antenna names to a set of antenna id's // Input: // names const Vector& Antenna names to match // Output: // matchAntennaNames Vector Matching antenna id's // Vector matchedAntennaIds; // Match each antenna name individually for (uInt fld=0; fld currentMatch = matchAntennaName(names(fld)); if (currentMatch.nelements() > 0) { Vector temp(matchedAntennaIds); matchedAntennaIds.resize(matchedAntennaIds.nelements() + currentMatch.nelements(), True); matchedAntennaIds = concatenateArray(temp, currentMatch); } } return matchedAntennaIds; } //------------------------------------------------------------------------- Vector MSAntennaIndex::matchStationRegexOrPattern(const String& pattern, const Bool regex) { // Match a regular expression or pattern to a set of antenna id's // Input: // pattern const String& Pattern/regular expression // for Station name to match // Output: // matchStationName Vector Matching station id's // Int pos=0; Bool negate = False; String patt = pattern; if (patt[0] == '^') { negate = True; patt = patt.from(1); } Regex reg; if (regex) { reg=patt; } else { reg=reg.fromPattern(patt); } // cerr << "Pattern = " << pattern << " Regex = " << reg.regexp() << endl; IPosition sh(msAntennaCols_p.station().getColumn().shape()); LogicalArray maskArray(sh,False); IPosition i=sh; for(i(0)=0;i(0)0) != negate ); // && !msAntennaCols_p.flagRow().getColumn()(i)); } MaskedArray maskStationID(stationIds_p,maskArray); return maskStationID.getCompressedArray(); } //------------------------------------------------------------------------- Vector MSAntennaIndex::matchStationName(const String& station) { // Match a antenna station to a set of antenna id's // Input: // station const String& Antenna station to match // Output: // matchAntennaStation Vector Matching antenna id's // // if(station.contains('*')) // { // String subStationName = station.at(0, station.length()-1); // Vector stationNames = msAntennaCols_p.station().getColumn(); // uInt len = stationNames.nelements(); // Vector matchstationnames(len, False); // for(uInt j = 0; j < len; j++) // { // if(stationNames[j].contains(subStationName)) // matchstationnames(j) = True; // } // LogicalArray maskArray( matchstationnames && (msAntennaCols_p.flagRow().getColumn()== // msAntennaCols_p.flagRow().getColumn())); // //!msAntennaCols_p.flagRow().getColumn()); // MaskedArray maskStationId(stationIds_p, maskArray); // return maskAntennaId.getCompressedArray(); // } // else { LogicalArray maskArray = (msAntennaCols_p.station().getColumn()==station); // && !msAntennaCols_p.flagRow().getColumn()); MaskedArray maskStationId(stationIds_p, maskArray); return maskStationId.getCompressedArray(); } } //------------------------------------------------------------------------- Vector MSAntennaIndex::matchStationName(const Vector& names) { // Match a set of station names to a set of antenna id's // Input: // names const Vector& Station names to match // Output: // matchStationNames Vector Matching station id's // Vector matchedStationIds; // Match each antenna name individually for (uInt fld=0; fld currentMatch = matchStationName(names(fld)); if (currentMatch.nelements() > 0) { Vector temp(matchedStationIds); matchedStationIds.resize(matchedStationIds.nelements() + currentMatch.nelements(), True); matchedStationIds = concatenateArray(temp, currentMatch); } } return matchedStationIds; } //------------------------------------------------------------------------- Vector MSAntennaIndex::matchAntennaNameAndStation(const String& name, const String& station) { // Match a antenna and station name pair to a set of antenna id's // Input: // name const String& Antenna name to match // station const String& Station name to match // Output: // matchAntennaNameAndStation Vector Matching antenna id's // LogicalArray maskArray = (msAntennaCols_p.name().getColumn()==name && msAntennaCols_p.station().getColumn()==station); // && !msAntennaCols_p.flagRow().getColumn()); MaskedArray maskAntennaId(antennaIds_p, maskArray); return maskAntennaId.getCompressedArray(); } //------------------------------------------------------------------------- } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSSel/MSAntennaIndex.h000066400000000000000000000072571476623553700177260ustar00rootroot00000000000000//# MSAntennaIndex: index or lookup in a MeasurementSet ANTENNA subtable //# Copyright (C) 2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSANTENNAINDEX_H #define MS_MSANTENNAINDEX_H //# includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# forward declarations // // Class to handle lookup or indexing into a MS ANTENNA subtable // // // // // // //
      • MeasurementSet //
      • MSAntenna // // // // From "MeasurementSet", "ANTENNA subtable" and "index". // // // // This class provides lookup and indexing into an MS ANTENNA // subtable. These services include returning rows numbers // (which for the ANTENNA subtable are ANTENNA_ID's) associated // with specific data in the subtable. // // // // // // // Collect together all subtable indexing and lookup for the // ANTENNA subtable, for encapsulation and efficiency. // // // //
      • //
      • // // class MSAntennaIndex { public: // Construct from an MS ANTENNA subtable MSAntennaIndex(const MSAntenna &antenna); // Null destructor virtual ~MSAntennaIndex() {} // Look up ANTENNA_ID's for a given a regular expression or pattern Vector matchAntennaRegexOrPattern(const String& pattern, const Bool regex=False); // Look up ANTENNA_ID's for a given antenna name, or set of antenna names Vector matchAntennaName(const String& name); Vector matchAntennaName(const Vector& names); // Look up ANTENNA_ID's for a given antenna station Vector matchStationRegexOrPattern(const String& pattern, const Bool regex=False); Vector matchStationName(const String& station); Vector matchStationName(const Vector& station); // Look up ANTENNA_ID's for a given antenna and station name pair Vector matchAntennaNameAndStation(const String& name, const String& station); Vector matchId(const Vector& sourceId); private: // Default constructor MSAntennaIndex(); // ANTENNA subtable column accessor MSAntennaColumns msAntennaCols_p; // Vector cache of antenna id's Vector antennaIds_p, stationIds_p; Int nrows_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSAntennaParse.cc000066400000000000000000000340131476623553700200550ustar00rootroot00000000000000//# MSAntennaParse.cc: Classes to hold results from antenna grammar parser //# Copyright (C) 1994,1995,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Global pointer to the parser object MSAntennaParse* MSAntennaParse::thisMSAParser = 0; TableExprNode MSAntennaParse::column1AsTEN_p,MSAntennaParse::column2AsTEN_p; std::shared_ptr MSAntennaParse::thisMSAErrorHandler; //# Constructor MSAntennaParse::MSAntennaParse () : MSParse(), colName1(MS::columnName(MS::ANTENNA1)), colName2(MS::columnName(MS::ANTENNA2)), ant1List(0),ant2List(0), baselineList(0,2) { } //# Constructor with given ms name. MSAntennaParse::MSAntennaParse (const MSAntenna& antSubTable, const TableExprNode& ant1AsTEN, const TableExprNode& ant2AsTEN) : MSParse(), colName1(MS::columnName(MS::ANTENNA1)), colName2(MS::columnName(MS::ANTENNA2)), ant1List(0),ant2List(0), baselineList(0,2), msSubTable_p(antSubTable) { column1AsTEN_p = ant1AsTEN; column2AsTEN_p = ant2AsTEN; } //# Constructor with given ms name. MSAntennaParse::MSAntennaParse (const MeasurementSet* myms) : MSParse(myms, "Antenna"), colName1(MS::columnName(MS::ANTENNA1)), colName2(MS::columnName(MS::ANTENNA2)), ant1List(0),ant2List(0), baselineList(0,2), msSubTable_p(myms->antenna()) { column1AsTEN_p = myms->col(myms->columnName(MS::ANTENNA1)); column2AsTEN_p = myms->col(myms->columnName(MS::ANTENNA2)); } // Add the current condition to the TableExprNode tree. Mask auto // correlations if baselineType==CrossOnly // const TableExprNode* MSAntennaParse::setTEN(TableExprNode& condition, BaselineListType baselineType, Bool negate) { if (baselineType==CrossOnly) { // TableExprNode noAutoCorr = (ms()->col(colName1) != ms()->col(colName2)); TableExprNode noAutoCorr = (column1AsTEN_p != column2AsTEN_p); condition = noAutoCorr && condition; } // if (negate) cerr << "Generating a negation condition" << endl; if (negate) condition = !condition; if(node_p.isNull()) node_p = condition; else if (negate) node_p = node_p && condition; else node_p = node_p || condition; return &node_p; } const TableExprNode* MSAntennaParse::selectAntennaIds(const Vector& antennaIds, BaselineListType baselineType, Bool negate) { TableExprNode condition; if ((baselineType==AutoCorrAlso) || (baselineType==AutoCorrOnly)) { Int n=antennaIds.nelements(); if (n) { // condition = ((ms()->col(colName1) == antennaIds[0]) && // (ms()->col(colName2) == antennaIds[0])); condition = ((column1AsTEN_p == antennaIds[0]) && (column2AsTEN_p == antennaIds[0])); for (Int i=1;icol(colName1) == antennaIds[i]) && // (ms()->col(colName2) == antennaIds[i])); } } } else { condition = // (ms()->col(colName1).in(antennaIds) || // ms()->col(colName2).in(antennaIds)); //&& ms()->col(colName1) != ms()->col(colName2); (column1AsTEN_p.in(antennaIds) || column2AsTEN_p.in(antennaIds)); //&& ms()->col(colName1) != ms()->col(colName2); } { Int nrows_p = subTable().nrow();//ms()->antenna().nrow(); Vector a2(nrows_p); a2.resize(nrows_p); indgen(a2); makeAntennaList(ant1List, antennaIds,negate); makeAntennaList(ant2List, a2); if (negate) makeBaselineList(-antennaIds,a2,baselineList,baselineType, negate); else makeBaselineList(antennaIds,a2,baselineList,baselineType, negate); } // setTEN(condition, AutoCorrAlso, negate); return setTEN(condition, baselineType, negate); } void MSAntennaParse::makeAntennaList(Vector& antList,const Vector& thisList, Bool negate) { Vector a2; if (negate) a2=-thisList; else a2=thisList; Vector tmp1(set_union(a2,antList)); antList.resize(tmp1.nelements());antList = tmp1; } const TableExprNode* MSAntennaParse::selectAntennaIds(const Vector& antennaIds1, const Vector& antennaIds2, BaselineListType baselineType, Bool negate) { TableExprNode condition; condition = // (ms()->col(colName1).in(antennaIds1) && ms()->col(colName2).in(antennaIds2)) || // (ms()->col(colName1).in(antennaIds2) && ms()->col(colName2).in(antennaIds1)); (column1AsTEN_p.in(antennaIds1) && column2AsTEN_p.in(antennaIds2)) || (column1AsTEN_p.in(antennaIds2) && column2AsTEN_p.in(antennaIds1)); makeAntennaList(ant1List, antennaIds1,negate); makeAntennaList(ant2List, antennaIds2,negate); if (negate) makeBaselineList(-antennaIds1, -antennaIds2,baselineList, baselineType, negate); else makeBaselineList(antennaIds1, antennaIds2,baselineList, baselineType, negate); return setTEN(condition,baselineType,negate); } const TableExprNode* MSAntennaParse::selectNameOrStation(const Vector& antenna, BaselineListType baselineType, Bool negate) { // MSAntennaIndex msAI(ms()->antenna()); MSAntennaIndex msAI(subTable()); Vector ant=msAI.matchAntennaName(antenna); // TableExprNode condition =(ms()->col(colName1).in(ant) || ms()->col(colName2).in(ant)); TableExprNode condition =(column1AsTEN_p.in(ant) || column2AsTEN_p.in(ant)); return setTEN(condition,baselineType,negate); } const TableExprNode* MSAntennaParse::selectNameOrStation(const Vector& antenna1, const Vector& antenna2, BaselineListType baselineType, Bool negate) { // MSAntennaIndex msAI(ms()->antenna()); MSAntennaIndex msAI(subTable()); Vector a1=msAI.matchAntennaName(antenna1), a2 = msAI.matchAntennaName(antenna2); // TableExprNode condition = // (ms()->col(colName1).in(a1) && ms()->col(colName2).in(a2)) || // (ms()->col(colName1).in(a2) && ms()->col(colName2).in(a1)); TableExprNode condition = (column1AsTEN_p.in(a1) && column2AsTEN_p.in(a2)) || (column1AsTEN_p.in(a2) && column2AsTEN_p.in(a1)); return setTEN(condition,baselineType,negate); } const TableExprNode* MSAntennaParse::selectNameOrStation(const String& antenna1, const String& antenna2, BaselineListType baselineType, Bool negate) { // TableExprNode condition = // (ms()->col(colName1) >= antenna1 && ms()->col(colName2) <= antenna2) || // (ms()->col(colName2) >= antenna1 && ms()->col(colName1) <= antenna2); TableExprNode condition = (column1AsTEN_p >= antenna1 && column2AsTEN_p <= antenna2) || (column2AsTEN_p >= antenna1 && column1AsTEN_p <= antenna2); return setTEN(condition,baselineType,negate); } const TableExprNode* MSAntennaParse::selectLength (const std::vector& lengths, Bool negate) { TableExprNode selAnt1, selAnt2; Matrix blength = getBaselineLengths(); Matrix match(blength.shape()); match = False; for (Int j=0; j= lengths[k] && bl <= lengths[k+1]) { match(i,j) = True; } } } } return makeBLNode (match, negate); } const TableExprNode* MSAntennaParse::selectBLRegex (const std::vector& blRegex, Bool negate) { TableExprNode selAnt1, selAnt2; Vector names = ScalarColumn(msSubTable_p, "NAME").getColumn(); Matrix match(names.size(), names.size()); match = False; for (std::vector::const_iterator iter=blRegex.begin(); iter!=blRegex.end(); ++iter) { // Create the Regex object. Take care of a possibly negated one. String str(*iter); Bool neg = (str[0] == '^'); if (neg) { str = str.after(0); } Regex re(str); // Form all possible baseline names and see it they match. for (uInt j=0; j& match, Bool negate) { vector ant1, ant2; for (Int i=0; i 0) { Array arrAnt1(IPosition(1,ant1.size()), &(ant1[0]), SHARE); Array arrAnt2(IPosition(1,ant1.size()), &(ant2[0]), SHARE); // condition = TableExprNode(any((ms()->col(colName1) == arrAnt1 && // ms()->col(colName2) == arrAnt2))); condition = TableExprNode(any((column1AsTEN_p == arrAnt1 && column2AsTEN_p == arrAnt2))); } return setTEN (condition, AutoCorrAlso, negate); } Matrix MSAntennaParse::getBaselineLengths() { // MSAntenna msant(ms()->antenna()); MSAntenna msant(subTable()); MSAntennaColumns antCols(msant); // First get the antenna positions. vector > antVec; antVec.reserve (msant.nrow()); for (uInt i=0; i blength(antVec.size(), antVec.size()); for (uInt j=0; j diff(antVec[i] - antVec[j]); blength(i,j) = sqrt(sum(diff*diff)); } } return blength; } double MSAntennaParse::getUnitFactor (const char* unit) { // Check if conversion is possible. Unit u(unit); Quantity q(1., "m"); if (! q.isConform (u)) { throw MSSelectionAntennaParseError (String("Unit ") + unit + " must be a distance unit (like m)"); } // Get conversion factor. return q.getValue (unit); } Bool MSAntennaParse::addBaseline(const Matrix& baselist, const Int ant1, const Int ant2, BaselineListType baselineType) { Bool doAutoCorr; doAutoCorr = (baselineType==AutoCorrAlso) || (baselineType==AutoCorrOnly); if ((ant1 == ant2) && (!doAutoCorr)) return False; if ((baselineType==AutoCorrOnly) && (ant1!=ant2)) return False; Int n=baselist.shape()(0); for (Int i=0;i& a1, const Vector& a2, Matrix& baselist, BaselineListType baselineType, Bool /*negate*/) { Int n1,n2,nb0; n1=a1.nelements(); n2=a2.nelements(); nb0=baselist.shape()(0); IPosition newSize(2,nb0,2); for (Int i1=0;i1 #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // Class to hold values from antenna grammar parser // // // // // //# Classes you should understand before using this one. // // // MSAntennaParse is the class used to parse a antenna command. // // // MSAntennaParse is used by the parser of antenna sub-expression statements. // The parser is written in Bison and Flex in files MSAntennaGram.y and .l. // The statements in there use the routines in this file to act // upon a reduced rule. // Since multiple tables can be given (with a shorthand), the table // names are stored in a list. The variable names can be qualified // by the table name and will be looked up in the appropriate table. // // The class MSAntennaParse only contains information about a table // used in the table command. Global variables (like a list and a vector) // are used in MSAntennaParse.cc to hold further information. // // Global functions are used to operate on the information. // The main function is the global function msAntennaCommand. // It executes the given STaQL command and returns the resulting ms. // This is, in fact, the only function to be used by a user. // // // It is necessary to be able to give a ms command in ASCII. // This can be used in a CLI or in the table browser to get a subset // of a table or to sort a table. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class MSAntennaParse : public MSParse { public: // Define the operator types (&&&, &&, and &). enum BaselineListType {AutoCorrOnly=0, AutoCorrAlso, CrossOnly}; enum ComplexityLevels {RESET=0,ANTREGEX,ANTLIST,STATIONREGEX, STATIONLIST, ANTATSTATIONLIST, BASELINELIST,HIGHESTLEVEL}; // Default constructor MSAntennaParse(); // Associate the ms. MSAntennaParse (const MeasurementSet* ms); MSAntennaParse (const MSAntenna& antSubTable, const TableExprNode& ant1AsTEN, const TableExprNode& ant2AsTEN); ~MSAntennaParse() {column1AsTEN_p=TableExprNode();column2AsTEN_p=TableExprNode();} // Add the given antennae selection. const TableExprNode* selectAntennaIds(const Vector& antennaIds, BaselineListType baselineType=CrossOnly, Bool negate=False); // Add the given baseline selection. const TableExprNode* selectAntennaIds(const Vector& antennaIds1, const Vector& antennaIds2, BaselineListType baselineType=CrossOnly, Bool negate=False); // Select by name or station number. const TableExprNode* selectNameOrStation(const Vector& antenna, BaselineListType baselineType=CrossOnly, Bool negate=False); const TableExprNode* selectNameOrStation(const Vector& antenna1, const Vector& antenna2, BaselineListType baselineType=CrossOnly, Bool negate=False); const TableExprNode* selectNameOrStation(const String& antenna1, const String& antenna2, BaselineListType baselineType=CrossOnly, Bool negate=False); // Selection on baseline regex const TableExprNode* selectBLRegex(const std::vector& lengths, Bool negate=False); // Selection on baseline length const TableExprNode* selectLength(const std::vector& lengths, Bool negate=False); // Get a pointer to the table expression node object. TableExprNode node() const { return node_p; } const Vector& selectedAnt1() const { return ant1List; } const Vector& selectedAnt2() const { return ant2List; } const Matrix& selectedBaselines() const { return baselineList; } // Get the factor to convert the given unit to m. static double getUnitFactor (const char* unit); void setComplexity(const ComplexityLevels& level=RESET) {if (level==RESET) complexity.reset(); else complexity.set(level,True);} std::bitset getComplexity() {return complexity;} MSAntenna& subTable() {return msSubTable_p;} private: const TableExprNode* makeBLNode (const Matrix& match, Bool negate); const TableExprNode* setTEN(TableExprNode& condition, BaselineListType baselineType=CrossOnly, Bool negate=False); Matrix getBaselineLengths(); void makeBaselineList(const Vector&a1, const Vector&a2, Matrix&b, BaselineListType baselineType=CrossOnly, Bool negate=False); void makeAntennaList(Vector& antList,const Vector& thisList, Bool negate=False); Bool addBaseline(const Matrix& baselist, const Int ant1, const Int ant2, BaselineListType baselineType=CrossOnly); //# Data members. public: static MSAntennaParse* thisMSAParser; static std::shared_ptr thisMSAErrorHandler; static void cleanupErrorHandler() {thisMSAErrorHandler.reset();} std::bitset complexity; private: TableExprNode node_p; const String colName1, colName2; Vector ant1List, ant2List; Matrix baselineList; MSAntenna msSubTable_p; static TableExprNode column1AsTEN_p,column2AsTEN_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSArrayGram.cc000066400000000000000000000126531476623553700173710ustar00rootroot00000000000000//# MSArrayGram.cc: Grammar for scan expressions //# Copyright (C) 1998,1999,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // MSArrayGram; grammar for scan command lines // This file includes the output files of bison and flex for // parsing command lines. #include #include #include #include #include // routines used by bison actions #include // routines used by bison actions #include //# stdlib.h is needed for bison 1.28 and needs to be included here //# (before the flex/bison files). //# Bison defines WHERE, which is also defined in LogOrigin.h (which //# is included in auto-template mode). //# So undefine WHERE first. #undef WHERE #include //# Let clang and gcc ignore some warnings in bison/flex code. //# Undef YY_NULL because flex can redefine it slightly differently (giving a warning). #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpragmas" #pragma GCC diagnostic ignored "-Wdeprecated-register" #pragma GCC diagnostic ignored "-Wsign-compare" #include "MSArrayGram.ycc" // bison output #ifdef YY_NULL # undef YY_NULL #endif #include "MSArrayGram.lcc" // flex output #pragma GCC diagnostic pop // Define the yywrap function for flex. int MSArrayGramwrap() { return 1; } namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Declare a file global pointer to a char* for the input string. static const char* strpMSArrayGram = 0; static Int posMSArrayGram = 0; //# Parse the command. //# Do a yyrestart(yyin) first to make the flex scanner reentrant. TableExprNode msArrayGramParseCommand (const MeasurementSet* ms, const String& command, Vector& selectedIDs, Int maxArrays) { try { MSArrayGramrestart (MSArrayGramin); yy_start = 1; strpMSArrayGram = command.chars(); // get pointer to command string posMSArrayGram = 0; // initialize string position MSArrayParse parser(ms); // setup measurement set MSArrayParse::thisMSAParser = &parser; // The global pointer to the parser parser.reset(); parser.setMaxArray(maxArrays); MSArrayGramparse(); // parse command string selectedIDs=parser.selectedIDs(); return parser.node(); } catch (MSSelectionArrayError &x) { String newMesgs; newMesgs = constructMessage(msArrayGramPosition(), command); x.addMessage(newMesgs); throw; } } //# Give the table expression node // const TableExprNode* msArrayGramParseNode() // { // return MSArrayParse::thisMSAParser->node(); // } // void msArrayGramParseDeleteNode() // { // return MSArrayParse::cleanup(); // } //# Give the string position. Int& msArrayGramPosition() { return posMSArrayGram; } //# Get the next input characters for flex. int msArrayGramInput (char* buf, int max_size) { int nr=0; while (*strpMSArrayGram != 0) { if (nr >= max_size) { break; // get max. max_size char. } buf[nr++] = *strpMSArrayGram++; } return nr; } void MSArrayGramerror (const char*) { throw (MSSelectionArrayError ("Array Expression: Parse error at or near '" + String(MSArrayGramtext) + "'")); } // String msArrayGramRemoveEscapes (const String& in) // { // String out; // int leng = in.length(); // for (int i=0; i #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MeasurementSet; class TableExprNode; // // Global functions for flex/bison scanner/parser for MSArrayGram // // // // // //# Classes you should understand before using this one. //
      • MSArrayGram.l and .y (flex and bison grammar) // // // Global functions are needed to define the input of the flex scanner // and to start the bison parser. // The input is taken from a string. // // // It is necessary to be able to give an image expression in ASCII. // This can be used in glish. // // //# A List of bugs, limitations, extensions or planned refinements. // // // Declare the bison parser (is implemented by bison command). TableExprNode msArrayGramParseCommand (const MeasurementSet *ms, const String& command, Vector& idList, Int maxArrays=1000); // The yyerror function for the parser. // It throws an exception with the current token. void MSArrayGramerror (const char*); // Give the table expression node. // const TableExprNode *msArrayGramParseNode(); // void msArrayGramParseDeleteNode(); // Give the current position in the string. // This can be used when parse errors occur. Int& msArrayGramPosition(); // Declare the input routine for flex/bison. int msArrayGramInput (char* buf, int max_size); // A function to remove escaped characters. //String msArrayGramRemoveEscapes (const String& in); // A function to remove quotes from a quoted string. //String msArrayGramRemoveQuotes (const String& in); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSArrayGram.ll000066400000000000000000000055321476623553700174110ustar00rootroot00000000000000/* -*- C -*- MSArrayGram.l: Lexical analyzer for ms selection commands Copyright (C) 1994,1995,1996,1997,1998,2001,2003 Associated Universities, Inc. Washington DC, USA. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. Correspondence concerning AIPS++ should be addressed as follows: Internet email: casa-feedback@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA */ /* yy_unput is not used, so let flex not generate it, otherwise picky compilers will issue warnings. */ %option nounput %{ #undef YY_INPUT #define YY_INPUT(buf,result,max_size) result=msArrayGramInput(buf,max_size) #undef YY_DECL #define YY_DECL int MSArrayGramlex (YYSTYPE* lvalp) static string qstr; #include %} WHITE [ \t\n]* DIGIT [0-9] INT ({WHITE}{DIGIT}+{WHITE}) /* rules */ %% {INT} { msArrayGramPosition() += yyleng; lvalp->str = (char *)malloc((strlen(MSArrayGramtext) + 1) * sizeof(char)); strcpy(lvalp->str, stripWhite(MSArrayGramtext).c_str()); // cout << "INT = \"" << MSArrayGramtext << "\" \"" << lvalp->str << "\"" << endl; return INT; } "~" { msArrayGramPosition() += yyleng; return DASH; } "," { msArrayGramPosition() += yyleng; return COMMA; } "<" { msArrayGramPosition() += yyleng; return LT; } ">" { msArrayGramPosition() += yyleng; return GT; } "<=" { msArrayGramPosition() += yyleng; return LE; } ">=" { msArrayGramPosition() += yyleng; return GE; } "&" { msArrayGramPosition() += yyleng; return AMPERSAND; } /* Literals */ "(" { msArrayGramPosition() += yyleng; return LPAREN; } ")" { msArrayGramPosition() += yyleng; return RPAREN;} {WHITE} { msArrayGramPosition() += yyleng;} /* Eat white spaces */ . { msArrayGramPosition() += yyleng;return MSArrayGramtext[0];} %% casacore-3.7.1/ms/MSSel/MSArrayGram.yy000066400000000000000000000103601476623553700174360ustar00rootroot00000000000000/* -*- C++ -*- MSArrayGram.y: Parser for scan expressions Copyright (C) 2004 Associated Universities, Inc. Washington DC, USA. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. Correspondence concerning AIPS++ should be addressed as follows: Internet email: casa-feedback@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA */ %{ #include using namespace casacore; %} %pure-parser /* make parser re-entrant */ %union { const TableExprNode* node; Block* exprb; TableExprNodeSetElem* elem; TableExprNodeSet* settp; Int ival[2]; char * str; Double dval; std::vector* iv; // std::vectors have push_back, insert, etc. Vector* is; } %token EQASS %token SQUOTE %token IDENTIFIER %token COMMA %token LBRACKET %token LPAREN %token RBRACKET %token RPAREN %token LBRACE %token RBRACE %token WHITE %token INT %token QSTRING %token REGEX %token COLON %token SEMICOLON %type scanstatement %type compoundexpr %type scanboundsexpr %type scanidbounds %type scanids %nonassoc EQ EQASS GT GE LT LE NE COMMA DASH AMPERSAND %{ int MSArrayGramlex (YYSTYPE*); %} %% scanstatement: compoundexpr {$$ = MSArrayParse::thisMSAParser->selectArrayIds();} ; // Here, for ID-list expressions (INT and INT DASH INT), we only // collect the list of IDs generated (accumulated internally in // MSArrayPrase). The accumulated IDs are used for selection in the // terminal node above. Bounds expressions are however used for // selection as they are parsed. compoundexpr: scanids {/*$$ = MSArrayParse::thisMSAParser->node();*/} | scanboundsexpr {$$=$1;} | compoundexpr COMMA scanids {$$=$1;} | compoundexpr COMMA scanboundsexpr {$$=$1;} ; scanidbounds: LT INT // idv(1,atoi($2)); $$ = MSArrayParse::thisMSAParser->selectArrayIdsLT(idv); free($2); } | GT INT // >ID { const Vector idv(1,atoi($2)); $$ = MSArrayParse::thisMSAParser->selectArrayIdsGT(idv); free($2); } | LE INT // <=ID { const Vector idv(1,atoi($2)); $$ = MSArrayParse::thisMSAParser->selectArrayIdsLTEQ(idv); free($2); } | GE INT // >=ID { const Vector idv(1,atoi($2)); $$ = MSArrayParse::thisMSAParser->selectArrayIdsGTEQ(idv); free($2); } | GE INT AMPERSAND LE INT // >=ID & <=ID { Int n0=atoi($2), n1=atoi($5); $$ = MSArrayParse::thisMSAParser->selectRangeGEAndLE(n0,n1); free($2); free($5); } | GT INT AMPERSAND LT INT // >ID & selectRangeGTAndLT(n0,n1); free($2); free($5); } ; scanboundsexpr: scanidbounds {$$=$1;} // // Build a list of scan IDs. This can be a single ID or a range of // IDs converted to a list. Actual selection is done at the end of // parsing cycle (at the terminal node above). // scanids: INT { $$=&MSArrayParse::thisMSAParser->accumulateIDs(atoi($1)); free($1); } | INT DASH INT { $$=&MSArrayParse::thisMSAParser->accumulateIDs(atoi($1),atoi($3)); free($1); free($3); } ; %% casacore-3.7.1/ms/MSSel/MSArrayParse.cc000066400000000000000000000137131476623553700175530ustar00rootroot00000000000000//# MSArrayParse.cc: Classes to hold results from array grammar parser //# Copyright (C) 1994,1995,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSArrayParse* MSArrayParse::thisMSAParser = 0x0; // Global pointer to the parser object // TableExprNode* MSArrayParse::node_p = 0x0; // std::vector MSArrayParse::parsedIDList_p; // Vector MSArrayParse::idList; //# Constructor MSArrayParse::MSArrayParse () : MSParse(), colName(MS::columnName(MS::ARRAY_ID)), maxArrays_p(1000) { } //# Constructor with given ms name. MSArrayParse::MSArrayParse (const MeasurementSet* ms) : MSParse(ms, "Array"), colName(MS::columnName(MS::ARRAY_ID)), maxArrays_p(1000) { idList.resize(0); parsedIDList_p.resize(0); } std::vector& MSArrayParse::accumulateIDs(const Int id0, const Int id1) { Vector theIDs; if (id1 < 0) { parsedIDList_p.push_back(id0);theIDs.resize(1);theIDs[0]=id0; // Also accumulate IDs in the global ID list which contains IDs // generated from all expressions (INT, INT DASH INT, and bounds // expressions (>ID, & v) { Int currentSize = idList.nelements(); Int n = v.nelements() + currentSize; Int j=0; idList.resize(n, True); for(Int i=currentSize;icol(colName) > n0) && (ms()->col(colName) < n1)); if ((n0 < 0) || (n1 < 0) || (n1 <= n0)) { ostringstream os; os << "Array Expression: Malformed range bounds " << n0 << " (lower bound) and " << n1 << " (upper bound)"; throw(MSSelectionArrayParseError(os.str())); } Vector tmp(n1-n0-1); Int j=n0+1; for(uInt i=0;icol(colName) >= n0) && (ms()->col(colName) <= n1)); if ((n0 < 0) || (n1 < 0) || (n1 <= n0)) { ostringstream os; os << "Array Expression: Malformed range bounds " << n0 << " (lower bound) and " << n1 << " (upper bound)"; throw(MSSelectionArrayParseError(os.str())); } Vector tmp(n1-n0+1); Int j=n0; for(uInt i=0;i& arrayids) { if (arrayids.size() > 0) { TableExprNode condition = TableExprNode(ms()->col(colName).in(arrayids)); appendToIDList(arrayids); addCondition(node_p, condition); } return &node_p; } const TableExprNode *MSArrayParse::selectArrayIdsGT(const Vector& arrayids) { TableExprNode condition = TableExprNode(ms()->col(colName) > arrayids[0]); Int n=maxArrays_p-arrayids[0]+1,j; Vector tmp(n); j=arrayids[0]+1; for(Int i=0;i& arrayids) { TableExprNode condition = TableExprNode(ms()->col(colName) < arrayids[0]); Vector tmp(arrayids[0]); for(Int i=0;i& arrayids) { TableExprNode condition = TableExprNode(ms()->col(colName) >= arrayids[0]); Int n=maxArrays_p-arrayids[0]+1,j; Vector tmp(n); j=arrayids[0]; for(Int i=0;i& arrayids) { TableExprNode condition = TableExprNode(ms()->col(colName) <= arrayids[0]); Vector tmp(arrayids[0]+1); for(Int i=0;i<=arrayids[0];i++) tmp[i] = i; appendToIDList(tmp); addCondition(node_p, condition); return &node_p; } const TableExprNode MSArrayParse::node() { return node_p; } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSSel/MSArrayParse.h000066400000000000000000000106711476623553700174150ustar00rootroot00000000000000//# MSArrayParse.h: Classes to hold results from array grammar parser //# Copyright (C) 1994,1995,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSARRAYPARSE_H #define MS_MSARRAYPARSE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // Class to hold values from array grammar parser // // // // // //# Classes you should understand before using this one. // // // MSArrayParse is the class used to parse a array command. // // // MSArrayParse is used by the parser of array sub-expression statements. // The parser is written in Bison and Flex in files MSArrayGram.y and .l. // The statements in there use the routines in this file to act // upon a reduced rule. // Since multiple tables can be given (with a shorthand), the table // names are stored in a list. The variable names can be qualified // by the table name and will be looked up in the appropriate table. // // The class MSArrayParse only contains information about a table // used in the table command. Global variables (like a list and a vector) // are used in MSArrayParse.cc to hold further information. // // Global functions are used to operate on the information. // The main function is the global function msArrayCommand. // It executes the given STaQL command and returns the resulting ms. // This is, in fact, the only function to be used by a user. // // // It is necessary to be able to give a ms command in ASCII. // This can be used in a CLI or in the table browser to get a subset // of a table or to sort a table. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class MSArrayParse : public MSParse { public: // Default constructor MSArrayParse (); // Associate the ms and the shorthand. MSArrayParse (const MeasurementSet* ms); // ~MSArrayParse() {if (node_p) delete node_p;node_p=0x0;} const TableExprNode *selectRangeGTAndLT(const Int& n0, const Int& n1); const TableExprNode *selectRangeGEAndLE(const Int& n0, const Int& n1); const TableExprNode *selectArrayIds(const Vector& arrayids); inline const TableExprNode *selectArrayIds() {return selectArrayIds(Vector(parsedIDList_p));} const TableExprNode *selectArrayIdsGT(const Vector& arrayids); const TableExprNode *selectArrayIdsLT(const Vector& arrayids); const TableExprNode *selectArrayIdsGTEQ(const Vector& arrayids); const TableExprNode *selectArrayIdsLTEQ(const Vector& arrayids); std::vector& accumulateIDs(const Int id0, const Int id1=-1); // Get table expression node object. const TableExprNode node(); Vector selectedIDs() {return idList;} void reset(){idList.resize(0);parsedIDList_p.resize(0);} void cleanup() {} void setMaxArray(const Int& n) {maxArrays_p=n;} static MSArrayParse* thisMSAParser; private: TableExprNode node_p; Vector idList; std::vector parsedIDList_p; const String colName; void appendToIDList(const Vector& v); Int maxArrays_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSCorrGram.cc000066400000000000000000000114641476623553700172170ustar00rootroot00000000000000//# MSCorrGram.cc: Grammar for corr expressions //# Copyright (C) 1998,1999,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // MSCorrGram; grammar for corr command lines // This file includes the output files of bison and flex for // parsing command lines operating on lattices. #include #include #include #include #include // routines used by bison actions #include // routines used by bison actions #include //# stdlib.h is needed for bison 1.28 and needs to be included here //# (before the flex/bison files). //# Bison defines WHERE, which is also defined in LogOrigin.h (which //# is included in auto-template mode). //# So undefine WHERE first. #undef WHERE #include //# Let clang and gcc ignore some warnings in bison/flex code. //# Undef YY_NULL because flex can redefine it slightly differently (giving a warning). #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpragmas" #pragma GCC diagnostic ignored "-Wdeprecated-register" #pragma GCC diagnostic ignored "-Wsign-compare" #include "MSCorrGram.ycc" // bison output #ifdef YY_NULL # undef YY_NULL #endif #include "MSCorrGram.lcc" // flex output #pragma GCC diagnostic pop // Define the yywrap function for flex. int MSCorrGramwrap() { return 1; } namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Declare a file global pointer to a char* for the input string. static const char* strpMSCorrGram = 0; static Int posMSCorrGram = 0; //# Parse the command. //# Do a yyrestart(yyin) first to make the flex scanner reentrant. int msCorrGramParseCommand (const MeasurementSet* ms, const String& command) { MSCorrGramrestart (MSCorrGramin); yy_start = 1; strpMSCorrGram = command.chars(); // get pointer to command string posMSCorrGram = 0; // initialize string position MSCorrParse parser(ms); // setup measurement set return MSCorrGramparse(); // parse command string } //# Give the table expression node const TableExprNode* msCorrGramParseNode() { return MSCorrParse::node(); } void msCorrGramParseDeleteNode() { return MSCorrParse::cleanup(); } //# Give the string position. Int& msCorrGramPosition() { return posMSCorrGram; } //# Get the next input characters for flex. int msCorrGramInput (char* buf, int max_size) { int nr=0; while (*strpMSCorrGram != 0) { if (nr >= max_size) { break; // get max. max_size char. } buf[nr++] = *strpMSCorrGram++; } return nr; } void MSCorrGramerror (const char*) { throw (AipsError ("Corr Expression: Parse error at or near '" + String(MSCorrGramtext) + "'")); } String msCorrGramRemoveEscapes (const String& in) { String out; int leng = in.length(); for (int i=0; i #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MeasurementSet; class TableExprNode; // // Global functions for flex/bison scanner/parser for MSCorrGram // // // // // //# Classes you should understand before using this one. //
      • MSCorrGram.l and .y (flex and bison grammar) // // // Global functions are needed to define the input of the flex scanner // and to start the bison parser. // The input is taken from a string. // // // It is necessary to be able to give an image expression in ASCII. // This can be used in glish. // // //# A List of bugs, limitations, extensions or planned refinements. // // // Declare the bison parser (is implemented by bison command). int msCorrGramParseCommand (const MeasurementSet *ms, const String& command); // The yyerror function for the parser. // It throws an exception with the current token. void MSCorrGramerror (const char*); // Give the table expression node. const TableExprNode *msCorrGramParseNode(); void msCorrGramParseDeleteNode(); // Give the current position in the string. // This can be used when parse errors occur. Int& msCorrGramPosition(); // Declare the input routine for flex/bison. int msCorrGramInput (char* buf, int max_size); // A function to remove escaped characters. String msCorrGramRemoveEscapes (const String& in); // A function to remove quotes from a quoted string. String msCorrGramRemoveQuotes (const String& in); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSCorrGram.ll000066400000000000000000000077341476623553700172460ustar00rootroot00000000000000/* MSCorrGram.l: Lexical analyzer for ms selection commands Copyright (C) 1994,1995,1996,1997,1998,2001,2003 Associated Universities, Inc. Washington DC, USA. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. Correspondence concerning AIPS++ should be addressed as follows: Internet email: casa-feedback@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA */ /* yy_unput is not used, so let flex not generate it, otherwise picky compilers will issue warnings. */ %option nounput %{ #undef YY_INPUT #define YY_INPUT(buf,result,max_size) result=msCorrGramInput(buf,max_size) #undef YY_DECL #define YY_DECL int MSCorrGramlex (YYSTYPE* lvalp) %} WHITE [ \t\n]* DIGIT [0-9] INT {DIGIT}+ EXP [DdEe][+-]?{INT} FNUMBER {INT}"."{DIGIT}* TRUE T FALSE F QSTRING \"[^\"\n]*\" ASTRING \'[^\'\n]*\' UQSTRING \"[^\"\n]*\n UASTRING \'[^\'\n]*\n STRING ({QSTRING}|{ASTRING})+ USTRING ({UQSTRING}|{UASTRING})+ CORRTYPE [IQUV]{1}|[RL]{2}|[XY]{2} REGEX1 m"/"[^/]+"/" REGEX2 m%[^%]+% REGEX3 m#[^#]+# REGEX {REGEX1}|{REGEX2}|{REGEX3} /* rules */ %% "[" { msCorrGramPosition() += yyleng; return LBRACKET; } "(" { msCorrGramPosition() += yyleng; return LPAREN; } "]" { msCorrGramPosition() += yyleng; return RBRACKET; } ")" { msCorrGramPosition() += yyleng; return RPAREN; } ":" { msCorrGramPosition() += yyleng; return COLON; } "==" { msCorrGramPosition() += yyleng; return EQ; } "=" { msCorrGramPosition() += yyleng; return EQASS; } "!=" { msCorrGramPosition() += yyleng; return NE; } "<>" { msCorrGramPosition() += yyleng; return NE; } ">=" { msCorrGramPosition() += yyleng; return GE; } ">" { msCorrGramPosition() += yyleng; return GT; } "<=" { msCorrGramPosition() += yyleng; return LE; } "<" { msCorrGramPosition() += yyleng; return LT; } "&&" { msCorrGramPosition() += yyleng; return AND; } "||" { msCorrGramPosition() += yyleng; return OR; } "!" { msCorrGramPosition() += yyleng; return NOT; } "^" { msCorrGramPosition() += yyleng; return POWER; } "*" { msCorrGramPosition() += yyleng; return TIMES; } "/" { msCorrGramPosition() += yyleng; return DIVIDE; } "%" { msCorrGramPosition() += yyleng; return PERCENT; } "+" { msCorrGramPosition() += yyleng; return PLUS; } "~" { msCorrGramPosition() += yyleng; return DASH; } "{" { msCorrGramPosition() += yyleng; return LBRACE; } "}" { msCorrGramPosition() += yyleng; return RBRACE; } "'" { msCorrGramPosition() += yyleng; return SQUOTE; } "," { msCorrGramPosition() += yyleng; return COMMA; } /* Literals */ {CORRTYPE} { msCorrGramPosition() += yyleng; lvalp->str = MSCorrGramtext; return CORRTYPE; } %% casacore-3.7.1/ms/MSSel/MSCorrGram.yy000066400000000000000000000045261476623553700172740ustar00rootroot00000000000000/* MSCorrGram.y: Parser for corr expressions Copyright (C) 2004 Associated Universities, Inc. Washington DC, USA. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. Correspondence concerning AIPS++ should be addressed as follows: Internet email: casa-feedback@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA */ %{ using namespace casacore; %} %pure-parser /* make parser re-entrant */ %union { const TableExprNode* node; Block* exprb; TableExprNodeSetElem* elem; TableExprNodeSet* settp; Int ival; char * str; Double dval[2]; } %token EQASS %token SQUOTE %token NUMBER %token CORRTYPE %token FNUMBER %token DASH %token LT %token GT %token COLON %token COMMA %token PERCENT %token LBRACKET %token LPAREN %token RBRACKET %token RPAREN %token LBRACE %token RBRACE %type corrstatement %type stdstokes %left OR %left AND %nonassoc EQ EQASS GT GE LT LE NE %left PLUS MINUS %left TIMES DIVIDE MODULO %nonassoc UNARY %nonassoc NOT %right POWER %{ int MSCorrGramlex (YYSTYPE*); %} %% corrstatement: SQUOTE stdstokes SQUOTE { $$ = $2; } ; stdstokes: CORRTYPE { String identifier = String($1); $$ = MSCorrParse().selectCorrType(identifier); } | stdstokes COMMA CORRTYPE { String identifier = String($3); $$ = MSCorrParse().selectCorrType(identifier); } ; %% casacore-3.7.1/ms/MSSel/MSCorrParse.cc000066400000000000000000000105131476623553700173750ustar00rootroot00000000000000//# MSCorrParse.cc: Classes to hold results from corr grammar parser //# Copyright (C) 1994,1995,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableExprNode* MSCorrParse::node_p = 0x0; //# Constructor MSCorrParse::MSCorrParse () : MSParse() { } //# Constructor with given ms name. MSCorrParse::MSCorrParse (const MeasurementSet* ms) : MSParse(ms, "Corr") { if(node_p) delete node_p; node_p = new TableExprNode(); } // MS selection const TableExprNode *MSCorrParse::selectCorrType(const String& corrType) { MeasurementSet selms= Table(ms()->tableName(), Table::Update); if(!selms.isWritable()) { cout << "Table is not writable " << endl; return NULL; } IPosition rowShape; Slicer slicer; Bool corrTypeExist = False; ArrayColumn data(selms, MS::columnName(MS::DATA)); TableDesc tdSel; String colSel = "SELECTED_DATA"; if(selms.tableDesc().isColumn("SELECTED_DATA")) { selms.removeColumn("SELECTED_DATA"); } ColumnDesc & cdSel = tdSel.addColumn(ArrayColumnDesc(colSel," selected data", 2)); selms.addColumn(cdSel); ArrayColumn selData(selms, "SELECTED_DATA"); MSPolarizationColumns polc(selms.polarization()); Array corrtypeArray = polc.corrType().getColumn().nonDegenerate(); IPosition ip = corrtypeArray.shape(); Vector nCorr(corrtypeArray); for (uInt row=0; row < selms.nrow(); row++) { rowShape=data.shape(row); selData.setShape(row,IPosition(2, 1, rowShape(1)) ); } Vector corrtype(nCorr); if(nCorr.nelements() != 0) { for (uInt i = 0; i < nCorr.nelements(); i ++) { if(nCorr(i) == Stokes::type(corrType)){ slicer = Slicer(IPosition(2, i, 0), IPosition(2, i, rowShape(1)-1 ), IPosition(2, 1, 1), Slicer::endIsLast); corrTypeExist = True; } } } if(!corrTypeExist) { cout << " corrtype " << corrType << " does not exist" << endl; return NULL; } Array datacol = data.getColumn(slicer); selData.putColumn( Slicer(IPosition(2, 0, 0), IPosition(2, 0, rowShape(1)-1 ), IPosition(2, 1, 1), Slicer::endIsLast), datacol); // To tableExprNode MSDataDescIndex msDDI(selms.dataDescription()); String colName = MS::columnName(MS::DATA_DESC_ID); MSPolarizationIndex msPI(selms.polarization()); TableExprNode condition = selms.col(colName).in(msDDI.matchPolId(msPI.matchCorrType(corrtype))); if(node_p->isNull()){ *node_p = condition; } else *node_p = *node_p || condition; return node_p; } const TableExprNode* MSCorrParse::node() { return node_p; } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSSel/MSCorrParse.h000066400000000000000000000070471476623553700172470ustar00rootroot00000000000000//# MSCorrParse.h: Classes to hold results from corr grammar parser //# Copyright (C) 1994,1995,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSUVDISTPARSE_H #define MS_MSUVDISTPARSE_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // Class to hold values from UV dist grammar parser // // // // // //# Classes you should understand before using this one. // // // MSCorrParse is the class used to parse a corr command. // // // MSCorrParse is used by the parser of UV dist sub-expression statements. // The parser is written in Bison and Flex in files MSCorrGram.y and .l. // The statements in there use the routines in this file to act // upon a reduced rule. // Since multiple tables can be given (with a shorthand), the table // names are stored in a list. The variable names can be qualified // by the table name and will be looked up in the appropriate table. // // The class MSCorrParse only contains information about a table // used in the table command. Global variables (like a list and a vector) // are used in MSCorrParse.cc to hold further information. // // Global functions are used to operate on the information. // The main function is the global function msCorrCommand. // It executes the given STaQL command and returns the resulting ms. // This is, in fact, the only function to be used by a user. // // // It is necessary to be able to give a ms command in ASCII. // This can be used in a CLI or in the table browser to get a subset // of a table or to sort a table. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class MSCorrParse : public MSParse { public: // Default constructor MSCorrParse (); // Associate the ms and the shorthand. MSCorrParse (const MeasurementSet* ms); // ~MSCorrParse() {if (node_p) delete node_p;node_p=0x0;} // MS selection const TableExprNode * selectCorrType(const String& corrType); // Get table expression node object. static const TableExprNode* node(); static void cleanup() {if (node_p) delete node_p;node_p=0x0;} private: static TableExprNode *node_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSDataDescIndex.cc000066400000000000000000000141031476623553700201340ustar00rootroot00000000000000//# MSDataDescIndex.cc: implementation of MSDataDescIndex.h //# Copyright (C) 2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //------------------------------------------------------------------------- MSDataDescIndex::MSDataDescIndex(const MSDataDescription& dataDescription) : msDataDescCols_p(dataDescription) { // Construct from an MS DATA_DESC subtable // Input: // dataDescription const MSDataDescription& Input MSDataDescription // sub-table // Output to private data: // msDataDescCols_p MSDataDescColumns MSDataDesc columns accessor // dataDescIds_p Vector Data desc id's // nrows_p Int Number of rows // // Generate an array of data desc id's, used in later queries nrows_p = msDataDescCols_p.nrow(); dataDescIds_p.resize(nrows_p); indgen(dataDescIds_p); } //------------------------------------------------------------------------- Vector MSDataDescIndex::matchSpwId(const Int& spwId) { // Match a spectral window id to a set of data desc id's // Input: // spwId const Int& Spw id to match // Output: // matchSpwId Vector Matching data desc id's // LogicalArray maskArray = (msDataDescCols_p.spectralWindowId().getColumn()==spwId && !msDataDescCols_p.flagRow().getColumn()); MaskedArray maskDataDescId(dataDescIds_p, maskArray); return maskDataDescId.getCompressedArray(); } //------------------------------------------------------------------------- Vector MSDataDescIndex::matchSpwId(const Vector& spwIds) { // Match a set of spectral window id's to a set of data desc id's // Input: // spwIds const Vector& Spw id's to match // Output: // matchSpwId Vector Matching data desc id's // Vector matchedDataDescIds; // Match each spw id individually for (uInt spwid=0; spwid currentMatch = matchSpwId(spwIds(spwid)); if (currentMatch.nelements() > 0) { Vector temp(matchedDataDescIds); matchedDataDescIds.resize(matchedDataDescIds.nelements() + currentMatch.nelements(), True); matchedDataDescIds = concatenateArray(temp, currentMatch); } } return matchedDataDescIds; } //------------------------------------------------------------------------- Vector MSDataDescIndex::matchPolId(const Int& polId) { // Match a polarization id to a set of data desc id's // Input: // polId const Int& pol id to match // Output: // matchPolId Vector Matching data desc id's // LogicalArray maskArray = (msDataDescCols_p.polarizationId().getColumn()==polId && !msDataDescCols_p.flagRow().getColumn()); MaskedArray maskDataDescId(dataDescIds_p, maskArray); return maskDataDescId.getCompressedArray(); } //------------------------------------------------------------------------- Vector MSDataDescIndex::matchPolId(const Vector& polIds) { // Match a set of polarization id's to a set of data desc id's // Input: // polIds const Vector& pol id's to match // Output: // matchPolId Vector Matching data desc id's // Vector matchedDataDescIds; // Match each pol id individually for (uInt polid=0; polid < polIds.nelements(); polid++) { // Add to list of datadesc id's Vector currentMatch = matchPolId(polIds(polid)); if (currentMatch.nelements() > 0) { Vector temp(matchedDataDescIds); matchedDataDescIds.resize(matchedDataDescIds.nelements() + currentMatch.nelements(), True); matchedDataDescIds = concatenateArray(temp, currentMatch); } } return matchedDataDescIds; } //------------------------------------------------------------------------- Vector MSDataDescIndex::matchSpwIdAndPolznId(const Int& spwId, const Int& polznId) { // Match a spw. id. and polzn. id. to a set of data desc id.'s // Input: // spwId const Int& Spw id. to match // polznId const Int& Polzn. id. to match // Output: // matchSpwIdAndPolznId Vector Matching data desc id's // LogicalArray maskArray = (msDataDescCols_p.spectralWindowId().getColumn()==spwId && msDataDescCols_p.polarizationId().getColumn()==polznId && !msDataDescCols_p.flagRow().getColumn()); MaskedArray maskDataDescId(dataDescIds_p, maskArray); return maskDataDescId.getCompressedArray(); } //------------------------------------------------------------------------- } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSSel/MSDataDescIndex.h000066400000000000000000000065761476623553700200150ustar00rootroot00000000000000//# MSDataDescIndex: index or lookup in a MeasurementSet DATA_DESC subtable //# Copyright (C) 2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSDATADESCINDEX_H #define MS_MSDATADESCINDEX_H //# includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# forward declarations // // Class to handle lookup or indexing into a MS DATA_DESC subtable // // // // // // //
      • MeasurementSet //
      • MSDataDescription // // // // From "MeasurementSet", "DATA_DESC subtable" and "index". // // // // This class provides lookup and indexing into an MS DATA_DESC // subtable. These services include returning rows numbers // (which for the DATA_DESC subtable are DATA_DESC_ID's) associated // with specific data in the subtable. // // // // // // // Collect together all subtable indexing and lookup for the // DATA_DESC subtable, for encapsulation and efficiency. // // // //
      • //
      • // // class MSDataDescIndex { public: // Construct from an MS DATA_DESC subtable MSDataDescIndex(const MSDataDescription& dataDescription); // Null destructor virtual ~MSDataDescIndex() {} // Look up DATA_DESC_ID's for a given spectral window id Vector matchSpwId(const Int& spwId); Vector matchSpwId(const Vector& spwIds); // Look up DATA_DESC_ID's for a given polarization id Vector matchPolId(const Int& polId); Vector matchPolId(const Vector& polIds); // Look up DATA_DESC_ID's for a given spectral window and polarization id. Vector matchSpwIdAndPolznId(const Int& spwId, const Int& polznId); private: // Disallow null constructor MSDataDescIndex(); // DATA_DESC subtable column accessor MSDataDescColumns msDataDescCols_p; // Vector cache of DataDesc id's Vector dataDescIds_p; Int nrows_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSDopplerIndex.cc000066400000000000000000000044061476623553700200760ustar00rootroot00000000000000//# MSDopplerIndex.cc: this defined MSDopplerIndex //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSDopplerIndex::MSDopplerIndex() : MSTableIndex() {;} MSDopplerIndex::MSDopplerIndex(const MSDoppler &doppler) : MSTableIndex(doppler, stringToVector("DOPPLER_ID,SOURCE_ID")) { attachIds();} MSDopplerIndex::MSDopplerIndex(const MSDopplerIndex &other) : MSTableIndex(other) { attachIds();} MSDopplerIndex::~MSDopplerIndex() {;} MSDopplerIndex &MSDopplerIndex::operator=(const MSDopplerIndex &other) { if (this != &other) { MSTableIndex::operator=(other); attachIds(); } return *this; } void MSDopplerIndex::attach(const MSDoppler &doppler) { MSTableIndex::attach(doppler, stringToVector("DOPPLER_ID,SOURCE_ID")); attachIds(); } void MSDopplerIndex::attachIds() { dopplerId_p.attachToRecord(accessKey(), "DOPPLER_ID"); sourceId_p.attachToRecord(accessKey(), "SOURCE_ID"); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSSel/MSDopplerIndex.h000066400000000000000000000053041476623553700177360ustar00rootroot00000000000000//# MSDopplerIndex: index into a MeasurementSet DOPPLER subtable //# Copyright (C) 2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSDOPPLERINDEX_H #define MS_MSDOPPLERINDEX_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# forward declarations class MSDoppler; // // // // // // //
      • MeasurementSet //
      • MSTableIndex // // // // // // // // // // // // // // // //
      • //
      • // // class MSDopplerIndex : public MSTableIndex { public: // no index attached, use the attach function or assignment operator to change that MSDopplerIndex(); // construct one using the indicated DOPPLER table MSDopplerIndex(const MSDoppler &doppler); // construct one from another MSDopplerIndex(const MSDopplerIndex &other); virtual ~MSDopplerIndex(); MSDopplerIndex &operator=(const MSDopplerIndex &other); void attach(const MSDoppler &doppler); // access to the doppler ID key, throws an exception if isNull() is False Int &dopplerId() {return *dopplerId_p;} // access to the source ID key, throws an exception if isNull() is False Int &sourceId() {return *sourceId_p;} private: RecordFieldPtr dopplerId_p, sourceId_p; void attachIds(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSFeedGram.cc000066400000000000000000000147461476623553700171630ustar00rootroot00000000000000//# MSFeedGram.cc: Grammar for feed expressions //# Copyright (C) 2015 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // MSFeedGram; grammar for feed command lines based on antenna grammar // This file includes the output files of bison and flex for // parsing command lines operating on lattices. #include #include #include #include // routines used by bison actions #include #include //# stdlib.h is needed for bison 1.28 and needs to be included here //# (before the flex/bison files). //# Bison defines WHERE, which is also defined in LogOrigin.h (which //# is included in auto-template mode). //# So undefine WHERE first. #undef WHERE #include //# Let clang and gcc ignore some warnings in bison/flex code. //# Undef YY_NULL because flex can redefine it slightly differently (giving a warning). #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpragmas" #pragma GCC diagnostic ignored "-Wdeprecated-register" #pragma GCC diagnostic ignored "-Wsign-compare" #include "MSFeedGram.ycc" // bison output #ifdef YY_NULL # undef YY_NULL #endif #include "MSFeedGram.lcc" // flex output #pragma GCC diagnostic pop // Define the yywrap function for flex. int MSFeedGramwrap() { return 1; } namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Declare a file global pointer to a char* for the input string. static const char* strpMSFeedGram = 0; static Int posMSFeedGram = 0; //# Parse the command. //# Do a yyrestart(yyin) first to make the flex scanner reentrant. TableExprNode baseMSFeedGramParseCommand(MSFeedParse* parser, const String& command, Vector& selectedFeeds1, Vector& selectedFeeds2, Matrix& selectedFeedPairs) { try { MSFeedGramrestart (MSFeedGramin); yy_start = 1; strpMSFeedGram = command.chars(); // get pointer to command string posMSFeedGram = 0; // initialize string position MSFeedParse::thisMSFParser = parser; // The global pointer to the parser MSFeedGramparse(); // parse command string selectedFeeds1.reference (parser->selectedFeed1()); selectedFeeds2.reference (parser->selectedFeed2()); selectedFeedPairs.reference (parser->selectedFeedPairs()); return parser->node(); } catch (MSSelectionFeedError& x) { String newMesgs; newMesgs = constructMessage(msFeedGramPosition(),command); x.addMessage(newMesgs); throw; } } TableExprNode msFeedGramParseCommand (Table& subTable, TableExprNode& col1TEN, TableExprNode& col2TEN, const String& command, Vector& selectedFeeds1, Vector& selectedFeeds2, Matrix& selectedFeedPairs) { TableExprNode feedTEN; MSFeedParse thisParser(subTable, col1TEN, col2TEN); try { feedTEN = baseMSFeedGramParseCommand(&thisParser, command, selectedFeeds1, selectedFeeds2, selectedFeedPairs); } catch(MSSelectionFeedError &x) { throw; } return feedTEN; } TableExprNode msFeedGramParseCommand (MSFeedParse* thisParser, const String& command, Vector& selectedFeeds1, Vector& selectedFeeds2, Matrix& selectedFeedPairs) { TableExprNode feedTEN; try { feedTEN=baseMSFeedGramParseCommand(thisParser, command, selectedFeeds1, selectedFeeds2, selectedFeedPairs); } catch(MSSelectionFeedError &x) { delete thisParser; throw; } delete thisParser; return feedTEN; } TableExprNode msFeedGramParseCommand (const MeasurementSet* ms, const String& command, Vector& selectedFeeds1, Vector& selectedFeeds2, Matrix& selectedFeedPairs) { TableExprNode feedTEN; TableExprNode col1AsTEN = ms->col(ms->columnName(MS::FEED1)), col2AsTEN = ms->col(ms->columnName(MS::FEED2)); MSFeedParse *thisParser = new MSFeedParse(ms->feed(),col1AsTEN, col2AsTEN); try { feedTEN=baseMSFeedGramParseCommand(thisParser, command, selectedFeeds1, selectedFeeds2, selectedFeedPairs); } catch(MSSelectionFeedError &x) { delete thisParser; throw; } delete thisParser; return feedTEN; } //# Give the string position. Int& msFeedGramPosition() { return posMSFeedGram; } //# Get the next input characters for flex. int msFeedGramInput (char* buf, int max_size) { int nr=0; while (*strpMSFeedGram != 0) { if (nr >= max_size) { break; // get max. max_size char. } buf[nr++] = *strpMSFeedGram++; } return nr; } void MSFeedGramerror (const char*) { throw (MSSelectionFeedParseError ("Feed Expression: Parse error at or near '" + String(MSFeedGramtext) + "'")); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSSel/MSFeedGram.h000066400000000000000000000104501476623553700170110ustar00rootroot00000000000000//# MSFeedGram.h: Grammar for ms feed sub-expressions //# Copyright (C) 2015 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSFEEDGRAM_H #define MS_MSFEEDGRAM_H //# Includes #include #include #include #include // routines used by bison actions namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MeasurementSet; class TableExprNode; // // Global functions for flex/bison scanner/parser for MSFeedGram // // // // // //# Classes you should understand before using this one. //
      • MSFeedGram.l and .y (flex and bison grammar) // // // Global functions are needed to define the input of the flex scanner // and to start the bison parser. // The input is taken from a string. // // // It is necessary to be able to give an image expression in ASCII. // This can be used in glish. // // //# A List of bugs, limitations, extensions or planned refinements. // // // Declare the bison parser (is implemented by bison command). // It returns a TaQL expression tree. TableExprNode msFeedGramParseCommand (MSFeedParse* thisParser, const TableExprNode& col1TEN, const TableExprNode& col2TEN, const String& command, Vector& selectedFeeds1, Vector& selectedFeeds2, Matrix& selectedFeedPairs) ; TableExprNode msFeedGramParseCommand (Table& subTable, TableExprNode& col1TEN, TableExprNode& col2TEN, const String& command, Vector& selectedFeeds1, Vector& selectedFeeds2, Matrix& selectedFeedPairs) ; TableExprNode msFeedGramParseCommand (const MeasurementSet *ms, const String& command, Vector& selectedFeeds1, Vector& selectedFeeds2, Matrix& selectedFeedPairs); TableExprNode baseMSFeedGramParseCommand(MSFeedParse* parser, const String& command, Vector& selectedFeeds1, Vector& selectedFeeds2, Matrix& selectedFeedPairs); // The yyerror function for the parser. // It throws an exception with the current token. void MSFeedGramerror (const char*); // Give the current position in the string. // This can be used when parse errors occur. Int& msFeedGramPosition(); // Declare the input routine for flex/bison. int msFeedGramInput (char* buf, int max_size); // A function to remove escaped characters. //String msFeedGramRemoveEscapes (const String& in); // A function to remove quotes from a quoted string. //String msFeedGramRemoveQuotes (const String& in); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSFeedGram.ll000066400000000000000000000045661476623553700172040ustar00rootroot00000000000000/* -*- C -*- MSFeedGram.l: Lexical analyzer for ms feed selection commands Copyright (C) 2015 Associated Universities, Inc. Washington DC, USA. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. Correspondence concerning AIPS++ should be addressed as follows: Internet email: casa-feedback@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA */ /* yy_unput is not used, so let flex not generate it, otherwise picky compilers will issue warnings. */ %option nounput %{ #undef YY_INPUT #define YY_INPUT(buf,result,max_size) result=msFeedGramInput(buf,max_size) #undef YY_DECL #define YY_DECL int MSFeedGramlex (YYSTYPE* lvalp) %} WHITE [ \t\n]* DIGIT [0-9] INT {DIGIT}+ /* rules */ %% {INT} { msFeedGramPosition() += yyleng; lvalp->str = (char*)malloc(strlen(MSFeedGramtext) + 1); strcpy(lvalp->str, MSFeedGramtext); return INT; } ";" { msFeedGramPosition() += yyleng; return SEMICOLON; } "&" { msFeedGramPosition() += yyleng; return AMPERSAND; } "~" { msFeedGramPosition() += yyleng; return DASH; } "," { msFeedGramPosition() += yyleng; return COMMA;} "!" { msFeedGramPosition() += yyleng; return NOT;} {WHITE} { msFeedGramPosition() += yyleng;} /* Eat whitespace */ /* An unterminated string is an error */ \'|\" { throw MSSelectionFeedError ("Unterminated string"); } /* terminate on EOF */ <> { yyterminate(); } /* Any other character is invalid */ . { return YYERRCODE; } %% casacore-3.7.1/ms/MSSel/MSFeedGram.yy000066400000000000000000000123131476623553700172230ustar00rootroot00000000000000/*-*- C++ -*- MSFeedGram.y: Parser for feed expressions based on antenna parser Copyright (C) 2015 Associated Universities, Inc. Washington DC, USA. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. Correspondence concerning AIPS++ should be addressed as follows: Internet email: casa-feedback@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA */ %{ using namespace casacore; %} %pure-parser /* make parser re-entrant */ %union { const TableExprNode* node; char* str; Vector* iv; std::vector* sv; } %token COMMA %token SEMICOLON %token AMPERSAND %token DASH %token NOT %token INT %type feedstatement %type indexcombexpr %type feedpairs %type gfeedpairs %type feedlist %type feedids %{ #include #include int MSFeedGramlex (YYSTYPE*); Bool MSFeedGramNegate=False; %} %% feedstatement: indexcombexpr { $$ = $1; } indexcombexpr: gfeedpairs {$$=$1;} | indexcombexpr SEMICOLON gfeedpairs { $$ = $1; } gfeedpairs: NOT {MSFeedGramNegate=True;} feedpairs {$$=$3;} | {MSFeedGramNegate=False;} feedpairs {$$=$2;} feedpairs: feedlist AMPERSAND feedlist // Two non-identical lists for the '&' operator { MSFeedIndex myMSFI(MSFeedParse::thisMSFParser->subTable()); Vector f1 = myMSFI.matchFeedId(*($1)); Vector f2 = myMSFI.matchFeedId(*($3)); $$ = MSFeedParse::thisMSFParser->selectFeedIds (f1,f2, MSFeedParse::CrossOnly, MSFeedGramNegate); delete $1; delete $3; } | feedlist AMPERSAND // Implicit same list on the RHS of '&' operator { MSFeedIndex myMSFI(MSFeedParse::thisMSFParser->subTable()); Vector f1 = myMSFI.matchFeedId(*($1)); $$ = MSFeedParse::thisMSFParser->selectFeedIds (f1,f1, MSFeedParse::CrossOnly, MSFeedGramNegate); delete $1; } | feedlist //Match FEEDLIST & ALLFEEDS (implicit "&*") { MSFeedIndex myMSFI(MSFeedParse::thisMSFParser->subTable()); Vector f1 = myMSFI.matchFeedId(*($1)); $$ = MSFeedParse::thisMSFParser->selectFeedIds (f1, MSFeedParse::CrossOnly, MSFeedGramNegate); delete $1; } | feedlist AMPERSAND AMPERSAND feedlist //Include self-correlations { MSFeedIndex myMSFI(MSFeedParse::thisMSFParser->subTable()); Vector f1 = myMSFI.matchFeedId(*($1)); Vector f2 = myMSFI.matchFeedId(*($4)); $$ = MSFeedParse::thisMSFParser->selectFeedIds (f1, f2, MSFeedParse::AutoCorrAlso, MSFeedGramNegate); delete $1; delete $4; } | feedlist AMPERSAND AMPERSAND // Include self-correlations { MSFeedIndex myMSFI(MSFeedParse::thisMSFParser->subTable()); Vector f1 = myMSFI.matchFeedId(*($1)); $$ = MSFeedParse::thisMSFParser->selectFeedIds (f1, f1, MSFeedParse::AutoCorrAlso, MSFeedGramNegate); delete $1; } | feedlist AMPERSAND AMPERSAND AMPERSAND // Only self-correlations { MSFeedIndex myMSFI(MSFeedParse::thisMSFParser->subTable()); Vector f1 = myMSFI.matchFeedId(*($1)); $$ = MSFeedParse::thisMSFParser->selectFeedIds (f1, MSFeedParse::AutoCorrOnly, MSFeedGramNegate); delete $1; } feedlist: feedids // single feed or range { $$ = new Vector(*$1); delete $1; } | feedlist COMMA feedids // feedID, feedID,... { $$ = $1; Int N0=(*($1)).nelements(), N1 = (*($3)).nelements(); (*($$)).resize(N0+N1,True); // Resize the existing list for(Int i=N0;i(1); (*($$))(0) = atoi($1); free($1); } | INT DASH INT // A range of integer feed indices feedID~feedID { Int start = atoi($1); Int end = atoi($3); Int len = end - start + 1; Vector feedids(len); for(Int i = 0; i < len; i++) feedids[i] = start + i; $$ = new Vector(len); for (Int i=0; i #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSFeedIndex::MSFeedIndex() : MSTableIndex(), msFeedCols_p(0) {;} MSFeedIndex::MSFeedIndex(const MSFeed &feed) : MSTableIndex(feed, stringToVector("ANTENNA_ID,FEED_ID,SPECTRAL_WINDOW_ID"), compare), msFeedCols_p(0) { attachIds();} MSFeedIndex::MSFeedIndex(const MSFeedIndex &other) : MSTableIndex(other), msFeedCols_p(0) { attachIds();} MSFeedIndex::~MSFeedIndex() { if (msFeedCols_p) delete(msFeedCols_p); } MSFeedIndex &MSFeedIndex::operator=(const MSFeedIndex &other) { if (this != &other) { MSTableIndex::operator=(other); attachIds(); } return *this; } void MSFeedIndex::attach(const MSFeed &feed) { MSTableIndex::attach(feed, stringToVector("ANTENNA_ID,FEED_ID,SPECTRAL_WINDOW_ID"), compare); attachIds(); } void MSFeedIndex::attachIds() { antennaId_p.attachToRecord(accessKey(), "ANTENNA_ID"); feedId_p.attachToRecord(accessKey(), "FEED_ID"); spwId_p.attachToRecord(accessKey(), "SPECTRAL_WINDOW_ID"); // Attach the MSFeed columns accessor msFeedCols_p = new MSFeedColumns(static_cast(table())); } Int MSFeedIndex::compare (const Block& fieldPtrs, const Block& dataPtrs, const Block& dataTypes, rownr_t index) { // this implementation has been adapted from the default compare function in // ColumnsIndex.cc. The support for data types other than Integer have been // removed, since, according to the constructor's documentation, the index // columns must be of integer type. At present, this is in practice true in // this case. A consequence of this simplified implementation is that is // supports a -1 value for all IDs, rather than just for SPECTRAL_WINDOW_ID; // since MS2 only allows a -1 value for SPECTRAL_WINDOW_ID, this should not // cause problems for users with valid MS2 datasets. uInt nfield = fieldPtrs.nelements(); for (uInt i=0; i*)(fieldPtrs[i])); const Int right = ((const Int*)(dataPtrs[i]))[index]; if (right != -1) { // consider -1 equal to any requested id if (left < right) { return -1; } else if (left > right) { return 1; } } } else { throw (TableError ("MSFeedIndex: non-Integer index type")); } } return 0; } Vector MSFeedIndex::matchFeedPolznAndAngle (const Int& antennaId, const Vector& polznType, const Vector& receptorAngle, const Float& tol, Vector& rowNumbers) { // Return all matching row numbers for a given antenna id., and set // of feed receptor polarizations and receptor angles. The receptor // angles are matched to within the specified tolerance in deg. // // Do the receptor polarization match per row uInt nReceptors = std::min (polznType.nelements(), receptorAngle.nelements()); uInt nrows = msFeedCols_p->nrow(); Vector receptorMatch(nrows, False); for (uInt row=0; row rowAngle; msFeedCols_p->receptorAngleQuant().get(row, rowAngle); Vector rowType; msFeedCols_p->polarizationType().get(row, rowType); receptorMatch(row) = (rowAngle.nelements() == nReceptors && rowType.nelements() == nReceptors); if (receptorMatch(row)) { for (uInt i=0; iantennaId().getColumn()==antennaId && receptorMatch); Vector rows(nrows); indgen(rows); MaskedArray maskRowNumbers(rows, maskArray); rowNumbers = maskRowNumbers.getCompressedArray(); MaskedArray maskFeedIds(msFeedCols_p->feedId().getColumn(), maskArray); return maskFeedIds.getCompressedArray(); } Vector MSFeedIndex::matchAntennaId (const Int& antennaId, Vector& rowNumbers) { // Return all matching row numbers for a given antenna id. // LogicalArray maskArray = (msFeedCols_p->antennaId().getColumn()==antennaId); uInt nrows = msFeedCols_p->nrow(); Vector rows(nrows); indgen(rows); MaskedArray maskRowNumbers(rows, maskArray); return maskRowNumbers.getCompressedArray(); rowNumbers = maskRowNumbers.getCompressedArray(); MaskedArray maskFeedIds(msFeedCols_p->feedId().getColumn(), maskArray); return maskFeedIds.getCompressedArray(); } Vector MSFeedIndex::matchFeedId(const Vector& sourceId) { Vector feedIds = msFeedCols_p->feedId().getColumn(); Vector IDs = set_intersection(sourceId, feedIds); if (IDs.nelements() == 0) { ostringstream mesg; mesg << "No match found for requested feeds [ID(s): " << sourceId << "]"; // Use the error handler if defined, otherwise throw. if (MSFeedParse::thisMSFErrorHandler) { MSFeedParse::thisMSFErrorHandler->reportError ("", mesg.str()); } else { throw (MSSelectionFeedParseError(mesg)); } } return IDs; } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSSel/MSFeedIndex.h000066400000000000000000000075431476623553700172030ustar00rootroot00000000000000//# MSFeedIndex: index into a MeasurementSet FEED subtable //# Copyright (C) 2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSFEEDINDEX_H #define MS_MSFEEDINDEX_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# forward declarations class MSFeed; // // // // // // //
      • MeasurementSet //
      • MSTableIndex // // // // // // // // // // // // // // // //
      • //
      • // // class MSFeedIndex : public MSTableIndex { public: // no index attached, use the attach function or assignment operator to change that MSFeedIndex(); // construct one using the indicated FEED table MSFeedIndex(const MSFeed &feed); // construct one from another MSFeedIndex(const MSFeedIndex &other); virtual ~MSFeedIndex(); MSFeedIndex &operator=(const MSFeedIndex &other); void attach(const MSFeed &feed); // access to the antenna ID key, throws an exception if isNull() is False Int &antennaId() {return *antennaId_p;} // access to the feed ID key, throws an exception if isNull() is False Int &feedId() {return *feedId_p;} // access to the spectral window ID key, throws an exception if isNull() is False Int &spectralWindowId() {return *spwId_p;} // return feed id.'s (and associated row numbers) for a given antenna id., // polzn type and receptor angle Vector matchFeedPolznAndAngle(const Int& antennaId, const Vector& polznType, const Vector& receptorAngle, const Float& tol, Vector& rowNumbers); // return feed id.'s (and associated row numbers) for a given antenna id. Vector matchAntennaId(const Int& antennaId, Vector& rowNumbers); // return valid feed id.'s for a given list of feed id.'s. Vector matchFeedId(const Vector& sourceId); protected: // the specialized compare function to pass to the // ColumnsIndex object. This supports -1 // values for the SPECTRAL_WINDOW_ID static Int compare (const Block& fieldPtrs, const Block& dataPtrs, const Block& dataTypes, rownr_t index); private: RecordFieldPtr antennaId_p, feedId_p, spwId_p; // Pointer to FEED columns accessor MSFeedColumns* msFeedCols_p; void attachIds(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSFeedParse.cc000066400000000000000000000170011476623553700173320ustar00rootroot00000000000000//# MSFeedParse.cc: Classes to hold results from feed grammar parser //# Copyright (C) 1994,1995,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Global pointer to the parser object MSFeedParse* MSFeedParse::thisMSFParser = 0; TableExprNode MSFeedParse::column1AsTEN_p, MSFeedParse::column2AsTEN_p; std::shared_ptr MSFeedParse::thisMSFErrorHandler; //# Constructor MSFeedParse::MSFeedParse () : MSParse(), colName1(MS::columnName(MS::FEED1)), colName2(MS::columnName(MS::FEED2)), feed1List(0),feed2List(0), feedPairList(0,2) { } //# Constructor with given ms name. MSFeedParse::MSFeedParse (const MSFeed& feedSubTable, const TableExprNode& feed1AsTEN, const TableExprNode& feed2AsTEN) : MSParse(), colName1(MS::columnName(MS::FEED1)), colName2(MS::columnName(MS::FEED2)), feed1List(0),feed2List(0), feedPairList(0,2), msSubTable_p(feedSubTable) { column1AsTEN_p = feed1AsTEN; column2AsTEN_p = feed2AsTEN; } //# Constructor with given ms name. MSFeedParse::MSFeedParse (const MeasurementSet* myms) : MSParse(myms, "Feed"), colName1(MS::columnName(MS::FEED1)), colName2(MS::columnName(MS::FEED2)), feed1List(0),feed2List(0), feedPairList(0,2), msSubTable_p(myms->feed()) { column1AsTEN_p = myms->col(myms->columnName(MS::FEED1)); column2AsTEN_p = myms->col(myms->columnName(MS::FEED2)); } // Add the current condition to the TableExprNode tree. Mask auto // correlations if baselineType==CrossOnly // const TableExprNode* MSFeedParse::setTEN(TableExprNode& condition, BaselineListType baselineType, Bool negate) { if (baselineType==CrossOnly) { TableExprNode noAutoCorr = (column1AsTEN_p != column2AsTEN_p); condition = noAutoCorr && condition; } if (negate) condition = !condition; if(node_p.isNull()) node_p = condition; else if (negate) node_p = node_p && condition; else node_p = node_p || condition; return &node_p; } const TableExprNode* MSFeedParse::selectFeedIds(const Vector& feedIds, BaselineListType baselineType, Bool negate) { TableExprNode condition; if ((baselineType==AutoCorrAlso) || (baselineType==AutoCorrOnly)) { Int n=feedIds.nelements(); if (n) { condition = ((column1AsTEN_p == feedIds[0]) && (column2AsTEN_p == feedIds[0])); for (Int i=1;i f2 = msfc->feedId().getColumn(); delete msfc; /* Int nrows_p = subTable().nrow(); Vector f2(nrows_p); f2.resize(nrows_p); indgen(f2); */ makeFeedList(feed1List, feedIds, negate); makeFeedList(feed2List, f2); if (negate) makeFeedPairList(-feedIds,f2,feedPairList,baselineType, negate); else makeFeedPairList( feedIds,f2,feedPairList,baselineType, negate); } return setTEN(condition, baselineType, negate); } void MSFeedParse::makeFeedList(Vector& feedList,const Vector& thisList, Bool negate) { Vector f2; if (negate) f2=-thisList; else f2=thisList; Vector tmp1(set_union(f2,feedList)); feedList.resize(tmp1.nelements());feedList = tmp1; } const TableExprNode* MSFeedParse::selectFeedIds(const Vector& feedIds1, const Vector& feedIds2, BaselineListType baselineType, Bool negate) { TableExprNode condition; condition = (column1AsTEN_p.in(feedIds1) && column2AsTEN_p.in(feedIds2)) || (column1AsTEN_p.in(feedIds2) && column2AsTEN_p.in(feedIds1)); makeFeedList(feed1List, feedIds1, negate); makeFeedList(feed2List, feedIds2, negate); if (negate) makeFeedPairList(-feedIds1, -feedIds2, feedPairList, baselineType, negate); else makeFeedPairList( feedIds1, feedIds2, feedPairList, baselineType, negate); return setTEN(condition,baselineType,negate); } Bool MSFeedParse::addFeedPair(const Matrix& feedpairlist, const Int feed1, const Int feed2, BaselineListType baselineType) { Bool doAutoCorr; doAutoCorr = (baselineType==AutoCorrAlso) || (baselineType==AutoCorrOnly); if ((feed1 == feed2) && (!doAutoCorr)) return False; if ((baselineType==AutoCorrOnly) && (feed1!=feed2)) return False; Int n=feedpairlist.shape()(0); for (Int i=0;i& f1, const Vector& f2, Matrix& feedpairlist, BaselineListType baselineType, Bool /*negate*/) { Int n1,n2,nb0; n1=f1.nelements(); n2=f2.nelements(); nb0=feedpairlist.shape()(0); IPosition newSize(2,nb0,2); for (Int i1=0;i1 #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // Class to hold values from feed grammar parser // // // // // //# Classes you should understand before using this one. // // // MSFeedParse is the class used to parse a feed command. // // // MSFeedParse is used by the parser of feed sub-expression statements. // The parser is written in Bison and Flex in files MSFeedGram.yy and .ll. // The statements there use the routines in this file to act // upon a reduced rule. // Since multiple tables can be given (with a shorthand), the table // names are stored in a list. The variable names can be qualified // by the table name and will be looked up in the appropriate table. // // The class MSFeedParse only contains information about a table // used in the table command. Global variables (like a list and a vector) // are used in MSFeedParse.cc to hold further information. // // Global functions are used to operate on the information. // The main function is the global function msFeedCommand. // It executes the given STaQL command and returns the resulting ms. // This is, in fact, the only function to be used by a user. // // // It is necessary to be able to give a ms command in ASCII. // This can be used in a CLI or in the table browser to get a subset // of a table or to sort a table. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class MSFeedParse : public MSParse { public: // Define the operator types (&&&, &&, and &). // NB: Keeping the same notation as Antenna parser, even tho not a baseline here! enum BaselineListType {AutoCorrOnly=0, AutoCorrAlso, CrossOnly}; // Default constructor MSFeedParse(); // Associate the ms. MSFeedParse (const MeasurementSet* ms); MSFeedParse (const MSFeed& feedSubTable, const TableExprNode& feed1AsTEN, const TableExprNode& feed2AsTEN); ~MSFeedParse() {column1AsTEN_p=TableExprNode();column2AsTEN_p=TableExprNode();} // Add the given feed selection. const TableExprNode* selectFeedIds(const Vector& feedIds, BaselineListType baselineType=CrossOnly, Bool negate=False); // Add the given "baseline" selection. const TableExprNode* selectFeedIds(const Vector& feedIds1, const Vector& feedIds2, BaselineListType baselineType=CrossOnly, Bool negate=False); // Get a pointer to the table expression node object. TableExprNode node() const { return node_p; } const Vector& selectedFeed1() const { return feed1List; } const Vector& selectedFeed2() const { return feed2List; } const Matrix& selectedFeedPairs() const { return feedPairList; } MSFeed& subTable() {return msSubTable_p;} private: const TableExprNode* setTEN(TableExprNode& condition, BaselineListType baselineType=CrossOnly, Bool negate=False); void makeFeedPairList(const Vector&f1, const Vector&f2, Matrix&fp, BaselineListType baselineType=CrossOnly, Bool negate=False); void makeFeedList(Vector& feedList,const Vector& thisList, Bool negate=False); Bool addFeedPair(const Matrix& feedpairlist, const Int feed1, const Int feed2, BaselineListType baselineType=CrossOnly); //# Data members. public: static MSFeedParse* thisMSFParser; static std::shared_ptr thisMSFErrorHandler; static void cleanupErrorHandler() {thisMSFErrorHandler.reset();} private: TableExprNode node_p; const String colName1, colName2; Vector feed1List, feed2List; Matrix feedPairList; MSFeed msSubTable_p; static TableExprNode column1AsTEN_p,column2AsTEN_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSFieldGram.cc000066400000000000000000000167301476623553700173360ustar00rootroot00000000000000//# MSUvDistGram.cc: Grammar for field expressions //# Copyright (C) 1998,1999,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // MSUvDistGram; grammar for field command lines // This file includes the output files of bison and flex for // parsing command lines operating on lattices. #include #include #include #include #include #include #include #include #include // routines used by bison actions #include //# stdlib.h is needed for bison 1.28 and needs to be included here //# (before the flex/bison files). //# Bison defines WHERE, which is also defined in LogOrigin.h (which //# is included in auto-template mode). //# So undefine WHERE first. #undef WHERE #include //# Let clang and gcc ignore some warnings in bison/flex code. //# Undef YY_NULL because flex can redefine it slightly differently (giving a warning). #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpragmas" #pragma GCC diagnostic ignored "-Wdeprecated-register" #pragma GCC diagnostic ignored "-Wsign-compare" #include "MSFieldGram.ycc" // bison output #ifdef YY_NULL # undef YY_NULL #endif #include "MSFieldGram.lcc" // flex output #pragma GCC diagnostic pop // Define the yywrap function for flex. int MSFieldGramwrap() { return 1; } namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Declare a file global pointer to a char* for the input string. static const char* strpMSFieldGram = 0; static Int posMSFieldGram = 0; // MSFieldGramwrap out of namespace //# Parse the command. //# Do a yyrestart(yyin) first to make the flex scanner reentrant. // TableExprNode msFieldGramParseCommand (const MeasurementSet* ms, const String& command) // { // try // { // MSFieldGramrestart (MSFieldGramin); // yy_start = 1; // strpMSFieldGram = command.chars(); // get pointer to command string // posMSFieldGram = 0; // initialize string position // MSFieldParse parser(ms); // setup measurement set // MSFieldParse::thisMSFParser = &parser; // The global pointer to the parser // MSFieldParse::thisMSFParser->reset(); // // fieldError.reset(); // MSFieldGramparse(); // parse command string // return parser.node(); // } // catch (MSSelectionFieldError &x) // { // String newMesgs; // newMesgs = constructMessage(msFieldGramPosition(), command); // x.addMessage(newMesgs); // throw; // } // } // TableExprNode msFieldGramParseCommand (const MeasurementSet* ms, const String& command, Vector& selectedIDs) // { // // MSFieldParse *thisParser = new MSFieldParse(ms); // TableExprNode dummy,ten; // MSFieldParse *thisParser = new MSFieldParse(ms->field(),dummy); // ten=baseMSFieldGramParseCommand(thisParser, command, selectedIDs); // delete thisParser; // return ten; // } TableExprNode msFieldGramParseCommand (const MSField& msFieldSubTable, const TableExprNode& ten, const String& command, Vector& selectedIDs) { // MSFieldParse *thisParser = new MSFieldParse(ms); TableExprNode fieldTEN; MSFieldParse *thisParser = new MSFieldParse(msFieldSubTable,ten); try { fieldTEN=baseMSFieldGramParseCommand(thisParser, command, selectedIDs); } catch(const MSSelectionFieldError&) { delete thisParser; throw; } delete thisParser; return fieldTEN; } TableExprNode baseMSFieldGramParseCommand (MSFieldParse* parser, const String& command, Vector& selectedIDs) { // MSFieldParse parser(ms); // setup measurement set try { MSFieldGramrestart (MSFieldGramin); yy_start = 1; strpMSFieldGram = command.chars(); // get pointer to command string posMSFieldGram = 0; // initialize string position // MSFieldParse::thisMSFParser = &parser; // The global pointer to the parser MSFieldParse::thisMSFParser = parser; // The global pointer to the parser parser->reset(); MSFieldGramparse(); // parse command string selectedIDs=parser->selectedIDs(); return *(msFieldGramParseNode()); } catch (MSSelectionFieldError &x) { String newMesgs; newMesgs = constructMessage(msFieldGramPosition(), command); x.addMessage(newMesgs); throw; } } //# Give the table expression node const TableExprNode* msFieldGramParseNode() { return MSFieldParse::node(); } void msFieldGramParseDeleteNode() {MSFieldParse::cleanup();} //# Give the string position. Int& msFieldGramPosition() { return posMSFieldGram; } //# Get the next input characters for flex. int msFieldGramInput (char* buf, int max_size) { int nr=0; while (*strpMSFieldGram != 0) { if (nr >= max_size) { break; // get max. max_size char. } buf[nr++] = *strpMSFieldGram++; } return nr; } void MSFieldGramerror (const char*) { throw (MSSelectionFieldParseError ("Field Expression: Parse error at or near '" + String(MSFieldGramtext) + "'")); } // String msFieldGramRemoveEscapes (const String& in) // { // String out; // int leng = in.length(); // for (int i=0; i #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MeasurementSet; class TableExprNode; // // Global functions for flex/bison scanner/parser for MSFieldGram // // // // // //# Classes you should understand before using this one. //
      • MSFieldGram.l and .y (flex and bison grammar) // // // Global functions are needed to define the input of the flex scanner // and to start the bison parser. // The input is taken from a string. // // // It is necessary to be able to give an image expression in ASCII. // This can be used in glish. // // //# A List of bugs, limitations, extensions or planned refinements. // // // Declare the bison parser (is implemented by bison command). // TableExprNode msFieldGramParseCommand (const MeasurementSet *ms, const String& command); // TableExprNode msFieldGramParseCommand (MSSelectableTable *ms, const String& command,Vector&); // TableExprNode msFieldGramParseCommand (const MeasurementSet *ms, const String& command,Vector&); TableExprNode msFieldGramParseCommand (const MSField& fieldSubTable, const TableExprNode& colAsTEN, const String& command,Vector&); TableExprNode baseMSFieldGramParseCommand (MSFieldParse *parser, const String& command,Vector&); // The yyerror function for the parser. // It throws an exception with the current token. void MSFieldGramerror (const char*); // Give the table expression node. const TableExprNode *msFieldGramParseNode(); void msFieldGramParseDeleteNode() ; // Give the current position in the string. // This can be used when parse errors occur. Int& msFieldGramPosition(); // Declare the input routine for flex/bison. int msFieldGramInput (char* buf, int max_size); // A function to remove escaped characters. //String msFieldGramRemoveEscapes (const String& in); // A function to remove quotes from a quoted string. //String msFieldGramRemoveQuotes (const String& in); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSFieldGram.ll000066400000000000000000000106211476623553700173510ustar00rootroot00000000000000/* -*- C -*- MSFieldGram.l: Lexical analyzer for ms selection commands Copyright (C) 1994,1995,1996,1997,1998,2001,2003 Associated Universities, Inc. Washington DC, USA. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. Correspondence concerning AIPS++ should be addressed as follows: Internet email: casa-feedback@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA */ /* yy_unput is not used, so let flex not generate it, otherwise picky compilers will issue warnings. */ %option nounput %{ #undef YY_INPUT #define YY_INPUT(buf,result,max_size) result=msFieldGramInput(buf,max_size) #undef YY_DECL #define YY_DECL int MSFieldGramlex (YYSTYPE* lvalp) static string qstr; #include %} WHITE [ \t\n]* DIGIT [0-9] INT ({WHITE}{DIGIT}+{WHITE}) QSTRING \"[^\"\n]*\" STRING ({QSTRING})+ QUOTE (\") RQUOTE (\/) NQ [^\\\n\"]+ NRQ [^\\\n\/]+ /* NAME ([A-za-z0-0_'{''}''+''-']) */ IDENTIFIER ([A-Za-z0-9_\{\}\+\-\.\=;@#%:! ]+|STRING) SIDENTIFIER ({WHITE}[A-Za-z0-9_\+\-\{\}=;@#$%:!'*''?' ]+{WHITE}) %x QS RS ESC /* rules */ %% {QUOTE} { // Start of a quoted string qstr.resize(0); BEGIN(QS); } "\\" {printf("%s\n",MSFieldGramtext);BEGIN(ESC);} . {BEGIN(INITIAL);} {NQ} {(qstr)+= MSFieldGramtext;} {QUOTE} { /* saw closing quote - all done */ BEGIN(INITIAL); lvalp->str = (char *)malloc((qstr.length() + 1)*sizeof(char)); strcpy(lvalp->str,qstr.c_str()); qstr.resize(0); return QSTRING; } {RQUOTE} { // Start of a regex string qstr.resize(0); BEGIN(RS); } {NRQ} {(qstr)+= MSFieldGramtext;} {RQUOTE} { /* saw closing quote - all done */ BEGIN(INITIAL); lvalp->str = (char *)malloc((qstr.length() + 1)*sizeof(char)); strcpy(lvalp->str,qstr.c_str()); qstr.resize(0); return REGEX; } {INT} { msFieldGramPosition() += yyleng; lvalp->str = (char *)malloc((strlen(MSFieldGramtext) + 1) * sizeof(char)); strcpy(lvalp->str, stripWhite(MSFieldGramtext).c_str()); //cout << "INT = \"" << MSFieldGramtext << "\" \"" << lvalp->str << "\"" << endl; return INT; } "~" { msFieldGramPosition() += yyleng; return DASH;} "," { msFieldGramPosition() += yyleng; return COMMA;} "<" { msFieldGramPosition() += yyleng; return LT;} ">" { msFieldGramPosition() += yyleng; return GT;} "&" { msFieldGramPosition() += yyleng; return AMPERSAND;} /* Literals */ {IDENTIFIER} { msFieldGramPosition() += yyleng; lvalp->str = (char *)malloc((strlen(MSFieldGramtext) + 1) * sizeof(char)); strcpy(lvalp->str, stripWhite(MSFieldGramtext).c_str()); // cout << "ID = \"" << MSFieldGramtext << "\" \"" << lvalp->str << "\"" << endl; return IDENTIFIER; } {SIDENTIFIER} { msFieldGramPosition() += yyleng; lvalp->str = (char *)malloc((strlen(MSFieldGramtext) + 1) * sizeof(char)); strcpy(lvalp->str, stripWhite(MSFieldGramtext).c_str()); // cout << "SID = \"" << MSFieldGramtext << "\" \"" << lvalp->str << "\"" << endl; return QSTRING; } "(" { msFieldGramPosition() += yyleng; return LPAREN;} ")" { msFieldGramPosition() += yyleng; return RPAREN;} . { msFieldGramPosition() += yyleng; return MSFieldGramtext[0];} %% casacore-3.7.1/ms/MSSel/MSFieldGram.yy000066400000000000000000000204141476623553700174040ustar00rootroot00000000000000/* -*- C++ -*- MSFieldGram.y: Parser for field expressions Copyright (C) 2004 Associated Universities, Inc. Washington DC, USA. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. Correspondence concerning AIPS++ should be addressed as follows: Internet email: casa-feedback@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA */ %{ #include using namespace casacore; %} %pure-parser /* make parser re-entrant */ %union { const TableExprNode* node; Block* exprb; TableExprNodeSetElem* elem; TableExprNodeSet* settp; Int ival[2]; char * str; Double dval; Vector* iv; Vector* is; } %token EQASS %token SQUOTE %token IDENTIFIER %token COMMA %token LBRACKET %token LPAREN %token RBRACKET %token RPAREN %token LBRACE %token RBRACE %token WHITE %token INT %token QSTRING %token REGEX %token COLON %token SEMICOLON %type fieldstatement %type indexcombexpr %type indexlist %type fieldidrange %type fieldidlist %type fieldid %type fieldidbounds %nonassoc EQ EQASS GT GE LT LE NE COMMA DASH AMPERSAND %{ #include int MSFieldGramlex (YYSTYPE*); void checkFieldError(Vector& list, ostringstream& msg, Bool force=False, char* = NULL) { if ((list.nelements() == 0) || force) { String errorMesg; ostringstream Mesg; Mesg << "Field Expression: " << msg.str().c_str(); errorMesg = String(Mesg.str().c_str()); throw(MSSelectionFieldParseError(errorMesg)); } } %} %% fieldstatement: indexcombexpr { $$ = $1; } | LPAREN indexcombexpr RPAREN //Parenthesis are syntactically // not useful here { $$ = $2; } ; indexcombexpr : indexlist { ostringstream m; //MSFieldIndex myMSFI(MSFieldParse::thisMSFParser->msInterface()->field()); //MSFieldIndex myMSFI(MSFieldParse::thisMSFParser->ms()->field()); MSFieldIndex myMSFI(MSFieldParse::thisMSFParser->subTable()); myMSFI.matchIdAgainstNames(*($1)); Vector selectedIDs(myMSFI.maskFieldIDs(myMSFI.validateIndices(*($1)))); $$ = MSFieldParse().selectFieldIds(selectedIDs); m << "Partial or no match for Field ID list " << (*($1)); /* Do delete before check to avoid leak when exception */ delete $1; checkFieldError(selectedIDs, m); } ; // // A single field name (this could be a regex and // hence produce a list of indices) // fieldid: IDENTIFIER { // // Use the string as-is. This cannot include patterns/regex // which has characters that are part of range or list // syntax (',', '-') (that's all I think). // // Convert name to index // //MSFieldIndex myMSFI(MSFieldParse::thisMSFParser->msInterface()->field()); //MSFieldIndex myMSFI(MSFieldParse::thisMSFParser->ms()->field()); MSFieldIndex myMSFI(MSFieldParse::thisMSFParser->subTable()); // cerr << "ID: " << $1 << endl; $$=new Vector(myMSFI.matchFieldNameOrCode($1)); //$$=new Vector(myMSAI.matchFieldRegexOrPattern($1)); ostringstream m; m << "No match found for name \"" << $1 << "\""; free($1); checkFieldError(*($$), m); } | QSTRING { // // Quoted string: This is a pattern which will be converted // to regex internally. E.g. "VLA{20,21}*" becomes // "VLA((20)|(21)).*" regex. This can include any character // string. // // Convert name to index // //MSFieldIndex myMSFI(MSFieldParse::thisMSFParser->msInterface()->field()); //MSFieldIndex myMSFI(MSFieldParse::thisMSFParser->ms()->field()); MSFieldIndex myMSFI(MSFieldParse::thisMSFParser->subTable()); // cerr << "QS: " << $1 << endl; $$ = new Vector(myMSFI.matchFieldRegexOrPattern($1)); ostringstream m; m << "No match found for name \"" << $1 << "\""; free($1); checkFieldError(*($$), m); String s(m.str()); } | REGEX { // // A string delimited by a pair of '/': This will be treated // as a regular expression internally. // // Convert name to index // //MSFieldIndex myMSFI(MSFieldParse::thisMSFParser->msInterface()->field()); //MSFieldIndex myMSFI(MSFieldParse::thisMSFParser->ms()->field()); MSFieldIndex myMSFI(MSFieldParse::thisMSFParser->subTable()); $$ = new Vector(myMSFI.matchFieldRegexOrPattern($1,True)); ostringstream m; m << "No match found for \"" << $1 << "\""; free($1); checkFieldError(*($$), m); } ; fieldidrange: INT // A single field index { $$ = new Vector(1); (*($$))(0) = atoi($1); free($1); } | INT DASH INT // A range of integer field indices { Int start = atoi($1); Int end = atoi($3); Int len = end - start + 1; Vector fieldids(len); for(Int i = 0; i < len; i++) fieldids[i] = start + i; $$ = new Vector(fieldids); free($1); free($3); } ; fieldidbounds: LT INT // msInterface()->field()); //MSFieldIndex myMSFI(MSFieldParse::thisMSFParser->ms()->field()); MSFieldIndex myMSFI(MSFieldParse::thisMSFParser->subTable()); Int n=atoi($2); $$ = new Vector(myMSFI.matchFieldIDLT(n)); ostringstream m; m << "No field ID found <" << n; free($2); checkFieldError(*($$), m); } | GT INT // >ID { //MSFieldIndex myMSFI(MSFieldParse::thisMSFParser->msInterface()->field()); //MSFieldIndex myMSFI(MSFieldParse::thisMSFParser->ms()->field()); MSFieldIndex myMSFI(MSFieldParse::thisMSFParser->subTable()); Int n=atoi($2); $$ = new Vector(myMSFI.matchFieldIDGT(n)); ostringstream m; m << "No field ID found >" << n; free($2); checkFieldError(*($$), m); } | GT INT AMPERSAND LT INT // >ID & msInterface()->field()); //MSFieldIndex myMSFI(MSFieldParse::thisMSFParser->ms()->field()); MSFieldIndex myMSFI(MSFieldParse::thisMSFParser->subTable()); Int n0=atoi($2), n1=atoi($5); $$ = new Vector(myMSFI.matchFieldIDGTAndLT(n0,n1)); ostringstream m; m << "No field found in the range [" << n0 << "," << n1 << "]"; free($2); free($5); checkFieldError(*($$), m); } ; fieldidlist: fieldid // A singe field ID { $$ = $1; } | fieldidrange // ID range ( n0-n1 ) { $$ = $1; } | fieldidbounds // >ID, ID & (*$1); delete $1; } | indexlist COMMA fieldidlist { $$ = $1; Int N0=(*($1)).nelements(), N1 = (*($3)).nelements(); (*($$)).resize(N0+N1,True); // Resize the existing list for(Int i=N0;i #include #include #include #include #include #include #include //#include namespace casacore { //# NAMESPACE CASACORE - BEGIN //------------------------------------------------------------------------- MSFieldIndex::MSFieldIndex(const MSField& field) : msFieldCols_p(field) { // Construct from an MS FIELD subtable // Input: // field const MSField& Input MSField object // Output to private data: // msFieldCols_p MSFieldColumns MSField columns accessor // fieldIds_p Vector Field id's // nrows_p Int Number of rows // // Generate an array of field id's, used in later queries nrows_p = msFieldCols_p.nrow(); fieldIds_p.resize(nrows_p); indgen(fieldIds_p); } //------------------------------------------------------------------------- Vector MSFieldIndex::matchFieldRegexOrPattern(const String& pattern, const Bool regex) { Vector IDs; IDs = matchFieldNameRegexOrPattern(pattern, regex); if (IDs.nelements()==0) IDs = matchFieldCodeRegexOrPattern(pattern, regex); return IDs; } //------------------------------------------------------------------------- Vector MSFieldIndex::matchFieldNameRegexOrPattern(const String& pattern, const Bool regex) { // Match a field name to a set of field id's // Input: // name const String& Field name to match // Output: // matchFieldName Vector Matching field id's // Int pos=0; Regex reg; // String strippedPattern = stripWhite(pattern); const String& strippedPattern = pattern; try { if (regex) reg=strippedPattern; else reg=reg.fromPattern(strippedPattern); } catch (...) // Since I (SB) don't know the type of exception Regex throws, catch them all! { ostringstream Mesg; Mesg << "Field Expression: Invalid regular expression \"" << pattern << "\""; throw(MSSelectionFieldParseError(Mesg.str().c_str())); } // cerr << "Pattern = " << strippedPattern << " Regex = " << reg.regexp() << endl; Vector names=msFieldCols_p.name().getColumn(); Vector flagRow=msFieldCols_p.flagRow().getColumn(); IPosition sh(names.shape()); LogicalArray maskArray(sh,False); IPosition i=sh; for(i(0)=0;i(0)0) && !flagRow(i)); } MaskedArray maskFieldID(fieldIds_p,maskArray); return maskFieldID.getCompressedArray(); } Vector MSFieldIndex::maskFieldIDs(const Vector& ids) { Vector tmp = set_intersection(fieldIds_p,ids); return tmp; } //------------------------------------------------------------------------- Vector MSFieldIndex::matchFieldCodeRegexOrPattern(const String& pattern, const Bool regex) { // Match a field name to a set of field id's // Input: // name const String& Field name to match // Output: // matchFieldName Vector Matching field id's // Int pos=0; Regex reg; if (regex) reg=pattern; else reg=reg.fromPattern(pattern); // cerr << "Pattern = " << pattern << " Regex = " << reg.regexp() << endl; Vector flagRow=msFieldCols_p.flagRow().getColumn(); Vector codes=msFieldCols_p.code().getColumn(); IPosition sh(codes.shape()); LogicalArray maskArray(sh,False); IPosition i=sh; for(i(0)=0;i(0)0) && !flagRow(i)); } MaskedArray maskFieldID(fieldIds_p,maskArray); return maskFieldID.getCompressedArray(); } //------------------------------------------------------------------------- Vector MSFieldIndex::matchFieldNameOrCode(const String& name) { Vector IDs; IDs = matchFieldName(name); if (IDs.nelements() == 0) IDs = matchFieldCode(name); return IDs; } //------------------------------------------------------------------------- Vector MSFieldIndex::matchFieldName(const String& name) { // Match a field name to a set of field id's // Input: // name const String& Field name to match // Output: // matchFieldName Vector Matching field id's // Vector strippedNames = msFieldCols_p.name().getColumn(); IPosition sh=strippedNames.shape(); for(Int i=0;i maskFieldId(fieldIds_p, maskArray); return maskFieldId.getCompressedArray(); } //------------------------------------------------------------------------- Vector MSFieldIndex::matchFieldCode(const String& code) { // Match a field code to a set of field id's // Input: // code const String& Field code to match // Output: // matchFieldCode Vector Matching field id's // Vector strippedCodes = msFieldCols_p.code().getColumn(); Int n=strippedCodes.shape()(0); for(Int i=0;i maskFieldId(fieldIds_p, maskArray); return maskFieldId.getCompressedArray(); } //------------------------------------------------------------------------- Vector MSFieldIndex::matchSubFieldName(const String& name) { // Match a field name to a set of field id's // Input: // name const String& Field name to match // Output: // matchFieldName Vector Matching field id's // Vector fieldnames = msFieldCols_p.name().getColumn(); uInt len = fieldnames.nelements(); Vector matchfieldnames(len, False); for(uInt j = 0; j < len; j++) { if(stripWhite(fieldnames[j]).contains(name)) matchfieldnames(j) = True; } LogicalArray maskArray( matchfieldnames && !msFieldCols_p.flagRow().getColumn()); MaskedArray maskFieldId(fieldIds_p, maskArray); return maskFieldId.getCompressedArray(); } //------------------------------------------------------------------------- Vector MSFieldIndex::matchFieldName(const Vector& names) { // Match a set of field names to a set of field id's // Input: // names const Vector& Field names to match // Output: // matchFieldNames Vector Matching field id's // Vector matchedFieldIds; // Match each field name individually for (uInt fld=0; fld currentMatch = matchFieldName(names(fld)); if (currentMatch.nelements() > 0) { Vector temp(matchedFieldIds); matchedFieldIds.resize(matchedFieldIds.nelements() + currentMatch.nelements(), True); matchedFieldIds = concatenateArray(temp, currentMatch); } } return matchedFieldIds; } //------------------------------------------------------------------------- Vector MSFieldIndex::matchSourceId(const Int& sourceId) { // Match a source id to a set of field id's // Input: // sourceId const Int& Source id to match // Output: // matchSourceId Vector Matching field id's // LogicalArray maskArray = (msFieldCols_p.sourceId().getColumn()==sourceId && !msFieldCols_p.flagRow().getColumn()); MaskedArray maskFieldId(fieldIds_p, maskArray); return maskFieldId.getCompressedArray(); } //------------------------------------------------------------------------- Vector MSFieldIndex::matchSourceId(const Vector& sourceIds) { // Match a set of source id's to a set of field id's // Input: // sourceIds const Vector& Source id's to match // Output: // matchSourceIds Vector Matching field id's // Vector matchedFieldIds; // Match each field name individually for (uInt fld=0; fld currentMatch = matchSourceId(sourceIds(fld)); if (currentMatch.nelements() > 0) { Vector temp(matchedFieldIds); matchedFieldIds.resize(matchedFieldIds.nelements() + currentMatch.nelements(), True); matchedFieldIds = concatenateArray(temp, currentMatch); } } return matchedFieldIds; } //------------------------------------------------------------------------- Vector MSFieldIndex::matchFieldIDLT(const Int n) { LogicalArray maskArray = (fieldIds_p < n && !msFieldCols_p.flagRow().getColumn()); MaskedArray maskFieldId(fieldIds_p, maskArray); return maskFieldId.getCompressedArray(); } //------------------------------------------------------------------------- Vector MSFieldIndex::matchFieldIDGT(const Int n) { LogicalArray maskArray = (fieldIds_p > n && !msFieldCols_p.flagRow().getColumn()); MaskedArray maskFieldId(fieldIds_p, maskArray); return maskFieldId.getCompressedArray(); } //------------------------------------------------------------------------- Vector MSFieldIndex::matchFieldIDGTAndLT(const Int n0, const Int n1) { LogicalArray maskArray = (fieldIds_p > n0 && fieldIds_p < n1 && !msFieldCols_p.flagRow().getColumn()); MaskedArray maskFieldId(fieldIds_p, maskArray); return maskFieldId.getCompressedArray(); } //------------------------------------------------------------------------- // Input list modifier. Elements in the list greater than the number // of fields are converted to string and matched against field // names. If a match is found, the element is replaced with the // matched name ID (sub-table row number). Elements less than the // number of fields are left unmodified. void MSFieldIndex::matchIdAgainstNames(Vector& list) { for (unsigned int i=0;i= fieldIds_p.nelements()) { std::stringstream ss; ss << list[i]; Vector id=matchFieldName(ss.str()); if (id.nelements() > 0) list[i]=id[0]; } } //------------------------------------------------------------------------- Vector MSFieldIndex::validateIndices(const Vector& ids) { // // If any of the IDs is out of range, produce a warning message (and // a tip for more reasonable behaviour), and attempt the // integar-as-name parsing (yuck) and produce a warning based on // the result. // Vector modifiedIds(ids); // Make a writeable copy vector outOfRangeIdList, intAsNameIdList; for (uInt i=0;i (Int)fieldIds_p.nelements()-1)) { ostringstream intAsName; outOfRangeIdList.push_back(ids[i]); // throw(MSSelectionFieldParseError(Mesg.str())); // logIO << Mesg.str() << LogIO::WARN << LogIO::POST; // // Integar-as-name parsing // intAsName << ids[i]; Vector intAsNameID=matchFieldNameOrCode(intAsName.str()); if (intAsNameID.nelements() > 0) { modifiedIds[i]=intAsNameID[0]; intAsNameIdList.push_back(ids[i]); } } LogIO logIO; if (outOfRangeIdList.size()) { ostringstream Mesg; Mesg << "Field Expression: Found out-of-range index(s) in the list ("; for (uInt i=0;i #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# forward declarations // // Class to handle lookup or indexing into a MS FIELD subtable // // // // // // //
      • MeasurementSet //
      • MSField // // // // From "MeasurementSet", "FIELD subtable" and "index". // // // // This class provides lookup and indexing into an MS FIELD // subtable. These services include returning rows numbers // (which for the FIELD subtable are FIELD_ID's) associated // with specific data in the subtable. // // // // // // // Collect together all subtable indexing and lookup for the // FIELD subtable, for encapsulation and efficiency. // // // //
      • //
      • // // class MSFieldIndex { public: // Construct from an MS FIELD subtable MSFieldIndex(const MSField &field); // Null destructor virtual ~MSFieldIndex() {} // Look up a single name in FIELD.NAME or FIELD.CODE Vector matchFieldNameOrCode(const String& name); // Look up FIELD_ID's for a given field name, or set of field names Vector matchFieldName(const String& name); Vector matchFieldName(const Vector& names); //ADD for file name wildcard selection Vector matchSubFieldName(const String& name); // Look up FIELD_ID's for a given pattern/regex for source name/code Vector matchFieldRegexOrPattern(const String& pattern, const Bool regex=False); Vector matchFieldNameRegexOrPattern(const String& pattern, const Bool regex=False); Vector matchFieldCodeRegexOrPattern(const String& pattern, const Bool regex=False); // Look up FIELD_ID's for a given source id Vector matchSourceId(const Int& sourceId); Vector matchSourceId(const Vector& sourceIds); void matchIdAgainstNames(Vector& ids); Vector validateIndices(const Vector& sourceIds); // Add for field code selection Vector matchFieldCode(const String& code); Vector maskFieldIDs(const Vector& ids); Vector matchFieldIDLT(const Int n); Vector matchFieldIDGT(const Int n); Vector matchFieldIDGTAndLT(const Int n0, const int n1); private: // Disallow null constructor MSFieldIndex(); // FIELD subtable column accessor MSFieldColumns msFieldCols_p; // Vector cache of field id's Vector fieldIds_p; Int nrows_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSFieldParse.cc000066400000000000000000000162761476623553700175270ustar00rootroot00000000000000//# MSFieldParse.cc: Classes to hold results from field grammar parser //# Copyright (C) 1994,1995,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSFieldParse* MSFieldParse::thisMSFParser = 0x0; // Global pointer to the parser object TableExprNode* MSFieldParse::node_p = 0x0; TableExprNode MSFieldParse::columnAsTEN_p; Vector MSFieldParse::idList; //# Constructor MSFieldParse::MSFieldParse () : MSParse(), colName(MS::columnName(MS::FIELD_ID)) {reset();} //# Constructor with given ms name. MSFieldParse::MSFieldParse (const MeasurementSet* ms) : MSParse(ms, "Field"), colName(MS::columnName(MS::FIELD_ID)), msFieldSubTable_p(ms->field()) {reset();} MSFieldParse::MSFieldParse (const MSField& msFieldSubTable,const TableExprNode& colAsTEN) : MSParse(), colName(MS::columnName(MS::FIELD_ID)), msFieldSubTable_p(msFieldSubTable) { reset(); columnAsTEN_p=colAsTEN; } void MSFieldParse::reset() { if (MSFieldParse::node_p!=0x0) delete MSFieldParse::node_p; MSFieldParse::node_p=0x0; if(node_p) delete node_p; node_p = new TableExprNode(); idList.resize(0); // setMS(ms); } const TableExprNode *MSFieldParse::selectFieldIds(const Vector& fieldIds) { { Vector tmp(set_union(fieldIds,idList)); idList.resize(tmp.nelements()); idList = tmp; } TableExprNode condition; // TableExprNode condition = (msInterface()->asMS()->col(colName).in(fieldIds)); // condition = (msInterface()->col(colName).in(fieldIds)); // condition = ms()->col(colName).in(fieldIds); condition = columnAsTEN_p.in(fieldIds); //condition = ms()->col(colName); addCondition(*node_p, condition); // if(node_p->isNull()) // *node_p = condition.in(fieldIds); // else // *node_p = *node_p || condition.in(fieldIds); return node_p; } const TableExprNode* MSFieldParse::node() { return node_p; } } //# NAMESPACE CASACORE - END // ---------------OLD CODE START (Feb. 2012)----------------------- // #include // #include // #include // #include // #include // namespace casacore { //# NAMESPACE CASACORE - BEGIN // MSFieldParse* MSFieldParse::thisMSFParser = 0x0; // Global pointer to the parser object // TableExprNode* MSFieldParse::node_p = 0x0; // Vector MSFieldParse::idList; // //# Constructor // MSFieldParse::MSFieldParse () // : MSParse(), colName(MS::columnName(MS::FIELD_ID)) {reset();} // //# Constructor with given ms name. // MSFieldParse::MSFieldParse (const MeasurementSet* ms) // : MSParse(ms, "Field"), colName(MS::columnName(MS::FIELD_ID)) {reset();} // void MSFieldParse::reset() // { // if (MSFieldParse::node_p!=0x0) delete MSFieldParse::node_p; // MSFieldParse::node_p=0x0; // if(node_p) delete node_p; // node_p = new TableExprNode(); // idList.resize(0); // // setMS(ms); // } // const TableExprNode *MSFieldParse::selectFieldIds(const Vector& fieldIds) // { // { // Vector tmp(set_union(fieldIds,idList)); // idList.resize(tmp.nelements()); // idList = tmp; // } // // TableExprNode condition = (ms()->col(colName).in(fieldIds)); // // TableExprNode condition = (msInterface()->asMS()->col(colName).in(fieldIds)); // TableExprNode condition = (msInterface()->col(colName).in(fieldIds)); // if(node_p->isNull()) // *node_p = condition; // else // *node_p = *node_p || condition; // return node_p; // } // const TableExprNode* MSFieldParse::node() // { // return node_p; // } // ---------------OLD CODE END (Feb.2012)----------------------- /* const TableExprNode *MSFieldParse::selectFieldOrSource(const String& fieldName) { LogIO os(LogOrigin("MSFieldParse", "selectFieldOrSource", WHERE)); Vector SourceIdsFromSN ; Vector SourceIdsFromSC ; Vector FieldIdsFromFN ; Vector FieldIdsFromFC ; MSFieldColumns msFC(ms()->field()); MSFieldIndex msFI(ms()->field()); String colName = MS::columnName(MS::FIELD_ID); TableExprNode condition = 0; Bool searchField = False; if( !ms()->source().isNull()) { MSSourceIndex msSI(ms()->source()); SourceIdsFromSN = msSI.matchSourceName(fieldName); SourceIdsFromSC = msSI.matchSourceCode(fieldName);; //Source name selection if(SourceIdsFromSN.nelements() > 0) { condition=(ms()->col(colName).in (msFI.matchSourceId(SourceIdsFromSN))); } else if (SourceIdsFromSC.nelements() > 0) { //Source Code selection condition=(ms()->col(colName).in (msFI.matchSourceId(SourceIdsFromSC))); } else { os << " No Souce name(code) matched, search for field " << LogIO::POST; searchField = True; } } if(ms()->source().isNull() ||searchField) { FieldIdsFromFN = msFI.matchFieldName(fieldName); FieldIdsFromFC = msFI.matchFieldCode(fieldName); if (FieldIdsFromFN.nelements() > 0) { //Field name selection condition = (ms()->col(colName).in(FieldIdsFromFN)); } else if (FieldIdsFromFC.nelements() > 0) { //Field code selection condition = (ms()->col(colName).in(FieldIdsFromFC)); } else { os << " No exactly matched field name(code) found! " << LogIO::POST; } } if(fieldName.contains('*')) { String subFieldName = fieldName.at(0, fieldName.length()-1); FieldIdsFromFN = msFI.matchSubFieldName(subFieldName); condition = (ms()->col(colName).in(FieldIdsFromFN)); } if(node_p->isNull()) *node_p = condition; else *node_p = *node_p || condition; return node_p; } */ casacore-3.7.1/ms/MSSel/MSFieldParse.h000066400000000000000000000162501476623553700173610ustar00rootroot00000000000000//# MSFieldParse.h: Classes to hold results from field grammar parser //# Copyright (C) 1994,1995,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSFIELDPARSE_H #define MS_MSFIELDPARSE_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // It is necessary to be able to give a ms command in ASCII. // This can be used in a CLI or in the table browser to get a subset // of a table or to sort a table. // // // Class to hold values from field grammar parser // // // // // //# Classes you should understand before using this one. // // // MSFieldParse is the class used to parse a field command. // // // MSFieldParse is used by the parser of field sub-expression statements. // The parser is written in Bison and Flex in files MSFieldGram.y and .l. // The statements in there use the routines in this file to act // upon a reduced rule. // Since multiple tables can be given (with a shorthand), the table // names are stored in a list. The variable names can be qualified // by the table name and will be looked up in the appropriate table. // // The class MSFieldParse only contains information about a table // used in the table command. Global variables (like a list and a vector) // are used in MSFieldParse.cc to hold further information. // // Global functions are used to operate on the information. // The main function is the global function msFieldCommand. // It executes the given STaQL command and returns the resulting ms. // This is, in fact, the only function to be used by a user. // class MSFieldParse : public MSParse { public: // Default constructor MSFieldParse (); MSFieldParse (const MeasurementSet* ms); MSFieldParse (const MSField& fieldSubTable, const TableExprNode& columnAsTEN); ~MSFieldParse() {columnAsTEN_p=TableExprNode();} const TableExprNode *selectFieldIds(const Vector& fieldIds); // Get table expression node object. static const TableExprNode* node(); static MSFieldParse* thisMSFParser; static Vector selectedIDs() {return idList;} static void reset(); static void cleanup() {if (node_p) delete node_p;node_p=0x0;} MSField& subTable() {return msFieldSubTable_p;} private: static TableExprNode* node_p; const String colName; static Vector idList; MSField msFieldSubTable_p; static TableExprNode columnAsTEN_p; }; } //# NAMESPACE CASACORE - END #endif //---------------------OLD CODE START (Feb. 2012)------------------- // #ifndef MS_MSFIELDPARSE_H // #define MS_MSFIELDPARSE_H // //# Includes // #include // #include // #include // namespace casacore { //# NAMESPACE CASACORE - BEGIN // //# Forward Declarations // // // // Class to hold values from field grammar parser // // // // // // // // // // // //# Classes you should understand before using this one. // // // // // // MSFieldParse is the class used to parse a field command. // // // // // // MSFieldParse is used by the parser of field sub-expression statements. // // The parser is written in Bison and Flex in files MSFieldGram.y and .l. // // The statements in there use the routines in this file to act // // upon a reduced rule. // // Since multiple tables can be given (with a shorthand), the table // // names are stored in a list. The variable names can be qualified // // by the table name and will be looked up in the appropriate table. // // // // The class MSFieldParse only contains information about a table // // used in the table command. Global variables (like a list and a vector) // // are used in MSFieldParse.cc to hold further information. // // // // Global functions are used to operate on the information. // // The main function is the global function msFieldCommand. // // It executes the given STaQL command and returns the resulting ms. // // This is, in fact, the only function to be used by a user. // // // // // // It is necessary to be able to give a ms command in ASCII. // // This can be used in a CLI or in the table browser to get a subset // // of a table or to sort a table. // // // //# // //# A List of bugs, limitations, extensions or planned refinements. // //# // class MSFieldParse : public MSParse // { // public: // // Default constructor // MSFieldParse (); // // ~MSFieldParse() {idList.resize(0);} // // Associate the ms and the shorthand. // MSFieldParse (const MeasurementSet* ms); // MSFieldParse (MSSelectableTable* msLike); // //~MSFieldParse() {if (node_p) delete node_p;node_p=0x0;} // const TableExprNode *selectFieldIds(const Vector& fieldIds); // // const TableExprNode *selectFieldOrSource(const String& fieldName); // // Get table expression node object. // static const TableExprNode* node(); // static MSFieldParse* thisMSFParser; // static Vector selectedIDs() {return idList;} // static void reset();//{idList.resize(0);} // static void cleanup() {if (node_p) delete node_p;node_p=0x0;} // private: // static TableExprNode* node_p; // const String colName; // static Vector idList; // }; // } //# NAMESPACE CASACORE - END // #endif //---------------------OLD CODE END (Feb. 2012)------------------- casacore-3.7.1/ms/MSSel/MSFreqOffIndex.cc000066400000000000000000000046771476623553700200330ustar00rootroot00000000000000//# MSFreqOffIndex.cc: this defined MSFreqOffIndex //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSFreqOffIndex::MSFreqOffIndex() : MSTableIndex() {;} MSFreqOffIndex::MSFreqOffIndex(const MSFreqOffset &freqOffset) : MSTableIndex(freqOffset, stringToVector("ANTENNA1,ANTENNA2,FEED_ID,SPECTRAL_WINDOW_ID")) { attachIds();} MSFreqOffIndex::MSFreqOffIndex(const MSFreqOffIndex &other) : MSTableIndex(other) { attachIds();} MSFreqOffIndex::~MSFreqOffIndex() {;} MSFreqOffIndex &MSFreqOffIndex::operator=(const MSFreqOffIndex &other) { if (this != &other) { MSTableIndex::operator=(other); attachIds(); } return *this; } void MSFreqOffIndex::attach(const MSFreqOffset &freqOffset) { MSTableIndex::attach(freqOffset, stringToVector("ANTENNA1,ANTENNA2,FEED_ID,SPECTRAL_WINDOW_ID")); attachIds(); } void MSFreqOffIndex::attachIds() { antenna1Id_p.attachToRecord(accessKey(), "ANTENNA1"); antenna2Id_p.attachToRecord(accessKey(), "ANTENNA2"); feedId_p.attachToRecord(accessKey(), "FEED_ID"); spwId_p.attachToRecord(accessKey(), "SPECTRAL_WINDOW_ID"); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSSel/MSFreqOffIndex.h000066400000000000000000000057461476623553700176730ustar00rootroot00000000000000//# MSFreqOffIndex: index into a MeasurementSet FREQ_OFFSET subtable //# Copyright (C) 2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSFREQOFFINDEX_H #define MS_MSFREQOFFINDEX_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# forward declarations class MSFreqOffset; // // // // // // //
      • MeasurementSet //
      • MSTableIndex // // // // // // // // // // // // // // // //
      • //
      • // // class MSFreqOffIndex : public MSTableIndex { public: // no index attached, use the attach function or assignment operator to change that MSFreqOffIndex(); // construct one using the indicated FREQOFF table MSFreqOffIndex(const MSFreqOffset &freqOffset); // construct one from another MSFreqOffIndex(const MSFreqOffIndex &other); virtual ~MSFreqOffIndex(); MSFreqOffIndex &operator=(const MSFreqOffIndex &other); void attach(const MSFreqOffset &freqOffset); // access to the antenna1 ID key, throws an exception if isNull() is False Int &antenna1Id() {return *antenna1Id_p;} // access to the antenna2 ID key, throws an exception if isNull() is False Int &antenna2Id() {return *antenna2Id_p;} // access to the feed ID key, throws an exception if isNull() is False Int &feedId() {return *feedId_p;} // access to the spectral window ID key, throws an exception if isNull() is False Int &spectralWindowId() {return *spwId_p;} private: RecordFieldPtr antenna1Id_p, antenna2Id_p, feedId_p, spwId_p; void attachIds(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSObsIndex.cc000066400000000000000000000060111476623553700172060ustar00rootroot00000000000000//# MSObsIndex.cc: implementation of MSObsIndex.h //# Copyright (C) 2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //------------------------------------------------------------------------- MSObservationIndex::MSObservationIndex(const MSObservation& observationTable) : msObservationCols_p(observationTable) { // Construct from an MS OBSERVATION subtable // Input: // observationTable const MSObservation& Input MSObservation // sub-table // Output to private data: // msObservationCols_p MSObservationColumns MSObservation columns // accessor // observationIds_p Vector Observation id.'s // nrows_p Int Number of rows // // Generate an array of observation id's, used in later queries nrows_p = msObservationCols_p.nrow(); observationIds_p.resize(nrows_p); indgen(observationIds_p); } //------------------------------------------------------------------------- Vector MSObservationIndex::matchProjectCode(const String& projectCode) { // Match a project code // Input: // projectCode const String& Project code // Output: // matchProjectCode Vector Matching observation id.'s // // Match the project code // by row and correlation index LogicalArray maskArray(msObservationCols_p.project().getColumn() == projectCode); MaskedArray maskObsIds(observationIds_p, maskArray); return maskObsIds.getCompressedArray(); } //------------------------------------------------------------------------- } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSSel/MSObsIndex.h000066400000000000000000000061151476623553700170550ustar00rootroot00000000000000//# MSObsIndex: index or lookup in an MS OBSERVATION subtable //# Copyright (C) 2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSOBSINDEX_H #define MS_MSOBSINDEX_H //# includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# forward declarations // // Class to handle lookup or indexing into an MS OBSERVATION subtable // // // // // // //
      • MeasurementSet //
      • MSObservation // // // // From "MeasurementSet", "OBSERVATION subtable" and "index". // // // // This class provides lookup and indexing into an MS OBSERVATION // subtable. These services include returning rows numbers (which // for the OBSERVATION subtable are OBSERVATION_ID's) associated // with specific data in the subtable. // // // // // // // Collect together all subtable indexing and lookup for the // OBSERVATION subtable, for encapsulation and efficiency. // // // //
      • //
      • // // class MSObservationIndex { public: // Construct from an MS OBSERVATION subtable MSObservationIndex(const MSObservation& observationTable); // Null destructor virtual ~MSObservationIndex() {} // Look up OBSERVATION_ID's for a given project code Vector matchProjectCode(const String& projectCode); private: // Disallow null constructor MSObservationIndex(); // OBSERVATION subtable column accessor MSObservationColumns msObservationCols_p; // Vector cache of observation id's Vector observationIds_p; Int nrows_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSObservationGram.cc000066400000000000000000000166551476623553700206140ustar00rootroot00000000000000//# MSObservationGram.cc: Grammar for scan expressions //# Copyright (C) 1998,1999,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // MSObservationGram; grammar for scan command lines // This file includes the output files of bison and flex for // parsing command lines. #include #include #include #include #include // routines used by bison actions #include // routines used by bison actions #include //# stdlib.h is needed for bison 1.28 and needs to be included here //# (before the flex/bison files). //# Bison defines WHERE, which is also defined in LogOrigin.h (which //# is included in auto-template mode). //# So undefine WHERE first. #undef WHERE #include //# Let clang and gcc ignore some warnings in bison/flex code. //# Undef YY_NULL because flex can redefine it slightly differently (giving a warning). #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpragmas" #pragma GCC diagnostic ignored "-Wdeprecated-register" #pragma GCC diagnostic ignored "-Wsign-compare" #include "MSObservationGram.ycc" // bison output #ifdef YY_NULL # undef YY_NULL #endif #include "MSObservationGram.lcc" // flex output #pragma GCC diagnostic pop // Define the yywrap function for flex. int MSObservationGramwrap() { return 1; } namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Declare a file global pointer to a char* for the input string. static const char* strpMSObservationGram = 0; static Int posMSObservationGram = 0; //# Parse the command. //# Do a yyrestart(yyin) first to make the flex scanner reentrant. TableExprNode baseMSObservationGramParseCommand (MSObservationParse* parser, const String& command, Vector& selectedIDs) { try { MSObservationGramrestart (MSObservationGramin); yy_start = 1; strpMSObservationGram = command.chars(); // get pointer to command string posMSObservationGram = 0; // initialize string position //MSObservationParse parser(ms,obsSubTable); // setup measurement set MSObservationParse::thisMSObsParser = parser; // The global pointer to the parser parser->reset(); // parser->setMaxObs(maxObsIDs); MSObservationGramparse(); // parse command string selectedIDs=parser->selectedIDs(); return parser->node(); } catch (MSSelectionObservationError &x) { String newMesgs; newMesgs = constructMessage(msObservationGramPosition(), command); x.addMessage(newMesgs); throw; } } TableExprNode msObservationGramParseCommand (const MeasurementSet* ms, const MSObservation& obsSubTable, const TableExprNode& colAsTEN, const String& command, Vector& selectedIDs) { TableExprNode ret; MSObservationParse *thisParser = new MSObservationParse(ms, obsSubTable, colAsTEN); try { ret = baseMSObservationGramParseCommand(thisParser, command, selectedIDs); } catch (MSSelectionObservationError &x) { delete thisParser; throw; } delete thisParser; return ret; } // TableExprNode msObservationGramParseCommand (const MeasurementSet* ms, const MSObservation& obsSubTable, // const String& command, // Vector& selectedIDs, Int maxObsIDs) // { // try // { // MSObservationGramrestart (MSObservationGramin); // yy_start = 1; // strpMSObservationGram = command.chars(); // get pointer to command string // posMSObservationGram = 0; // initialize string position // MSObservationParse parser(ms,obsSubTable); // setup measurement set // MSObservationParse::thisMSObsParser = &parser; // The global pointer to the parser // parser.reset(); // parser.setMaxObs(maxObsIDs); // MSObservationGramparse(); // parse command string // selectedIDs=parser.selectedIDs(); // return parser.node(); // } // catch (MSSelectionObservationError &x) // { // String newMesgs; // newMesgs = constructMessage(msObservationGramPosition(), command); // x.addMessage(newMesgs); // throw; // } // } //# Give the table expression node // const TableExprNode* msObservationGramParseNode() // { // // return MSObservationParse::node(); // return &MSObservationParse::thisMSObsParser->node(); // } void msObservationGramParseDeleteNode() { // return MSObservationParse::cleanup(); return MSObservationParse::thisMSObsParser->cleanup(); } //# Give the string position. Int& msObservationGramPosition() { return posMSObservationGram; } //# Get the next input characters for flex. int msObservationGramInput (char* buf, int max_size) { int nr=0; while (*strpMSObservationGram != 0) { if (nr >= max_size) { break; // get max. max_size char. } buf[nr++] = *strpMSObservationGram++; } return nr; } void MSObservationGramerror (const char*) { throw (MSSelectionObservationError ("Scan Expression: Parse error at or near '" + String(MSObservationGramtext) + "'")); } // String msObservationGramRemoveEscapes (const String& in) // { // String out; // int leng = in.length(); // for (int i=0; i #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MeasurementSet; class TableExprNode; // // Global functions for flex/bison scanner/parser for MSObservationGram // // // // // //# Classes you should understand before using this one. //
      • MSObservationGram.l and .y (flex and bison grammar) // // // Global functions are needed to define the input of the flex scanner // and to start the bison parser. // The input is taken from a string. // // // It is necessary to be able to give an image expression in ASCII. // This can be used in glish. // // //# A List of bugs, limitations, extensions or planned refinements. // // // Declare the bison parser (is implemented by bison command). TableExprNode msObservationGramParseCommand (const MeasurementSet *ms, const MSObservation& obsSubTable, const TableExprNode& colAsTEN, const String& command, Vector& idList); TableExprNode baseMSObservationGramParseCommand (MSObservationParse* parser, const TableExprNode& colAsTEN, const String& command, Vector& selectedIDs); // The yyerror function for the parser. // It throws an exception with the current token. void MSObservationGramerror (const char*); // Give the table expression node. //const TableExprNode *msObservationGramParseNode(); void msObservationGramParseDeleteNode(); // Give the current position in the string. // This can be used when parse errors occur. Int& msObservationGramPosition(); // Declare the input routine for flex/bison. int msObservationGramInput (char* buf, int max_size); // A function to remove escaped characters. //String msObservationGramRemoveEscapes (const String& in); // A function to remove quotes from a quoted string. //String msObservationGramRemoveQuotes (const String& in); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSObservationGram.ll000066400000000000000000000057141476623553700206300ustar00rootroot00000000000000/* -*- C -*- MSObservationGram.l: Lexical analyzer for ms selection commands Copyright (C) 1994,1995,1996,1997,1998,2001,2003 Associated Universities, Inc. Washington DC, USA. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. Correspondence concerning AIPS++ should be addressed as follows: Internet email: casa-feedback@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA */ /* yy_unput is not used, so let flex not generate it, otherwise picky compilers will issue warnings. */ %option nounput %{ #undef YY_INPUT #define YY_INPUT(buf,result,max_size) result=msObservationGramInput(buf,max_size) #undef YY_DECL #define YY_DECL int MSObservationGramlex (YYSTYPE* lvalp) static string qstr; #include %} WHITE [ \t\n]* DIGIT [0-9] INT ({WHITE}{DIGIT}+{WHITE}) /* rules */ %% {INT} { msObservationGramPosition() += yyleng; lvalp->str = (char *)malloc((strlen(MSObservationGramtext) + 1) * sizeof(char)); strcpy(lvalp->str, stripWhite(MSObservationGramtext).c_str()); // cout << "INT = \"" << MSObservationGramtext << "\" \"" << lvalp->str << "\"" << endl; return INT; } "~" { msObservationGramPosition() += yyleng; return DASH; } "," { msObservationGramPosition() += yyleng; return COMMA; } "<" { msObservationGramPosition() += yyleng; return LT; } ">" { msObservationGramPosition() += yyleng; return GT; } "<=" { msObservationGramPosition() += yyleng; return LE; } ">=" { msObservationGramPosition() += yyleng; return GE; } "&" { msObservationGramPosition() += yyleng; return AMPERSAND; } /* Literals */ "(" { msObservationGramPosition() += yyleng; return LPAREN; } ")" { msObservationGramPosition() += yyleng; return RPAREN;} {WHITE} { msObservationGramPosition() += yyleng;} /* Eat white spaces */ . { msObservationGramPosition() += yyleng;return MSObservationGramtext[0];} %% casacore-3.7.1/ms/MSSel/MSObservationGram.yy000066400000000000000000000107301476623553700206540ustar00rootroot00000000000000/* -*- C++ -*- MSObservationGram.y: Parser for scan expressions Copyright (C) 2004 Associated Universities, Inc. Washington DC, USA. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. Correspondence concerning AIPS++ should be addressed as follows: Internet email: casa-feedback@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA */ %{ #include using namespace casacore; %} %pure-parser /* make parser re-entrant */ %union { const TableExprNode* node; Block* exprb; TableExprNodeSetElem* elem; TableExprNodeSet* settp; Int ival[2]; char * str; Double dval; std::vector* iv; // std::vectors have push_back, insert, etc. Vector* is; } %token EQASS %token SQUOTE %token IDENTIFIER %token COMMA %token LBRACKET %token LPAREN %token RBRACKET %token RPAREN %token LBRACE %token RBRACE %token WHITE %token INT %token QSTRING %token REGEX %token COLON %token SEMICOLON %type scanstatement %type compoundexpr %type scanboundsexpr %type scanidbounds %type scanids %nonassoc EQ EQASS GT GE LT LE NE COMMA DASH AMPERSAND %{ int MSObservationGramlex (YYSTYPE*); %} %% scanstatement: compoundexpr { $$ = MSObservationParse::thisMSObsParser ->selectObservationIds(); } ; // Here, for ID-list expressions (INT and INT DASH INT), we only // collect the list of IDs generated (accumulated internally in // MSObservationPrase). The accumulated IDs are used for selection in the // terminal node above. Bounds expressions are however used for // selection as they are parsed. compoundexpr: scanids {/*$$ = &MSObservationParse::thisMSObsParser->node();*/} | scanboundsexpr {$$=$1;} | compoundexpr COMMA scanids {$$=$1;} | compoundexpr COMMA scanboundsexpr {$$=$1;} ; scanidbounds: LT INT // idv(1,atoi($2)); $$ = MSObservationParse::thisMSObsParser->selectObservationIdsLT(idv); free($2); } | GT INT // >ID { const Vector idv(1,atoi($2)); $$ = MSObservationParse::thisMSObsParser->selectObservationIdsGT(idv); free($2); } | LE INT // <=ID { const Vector idv(1,atoi($2)); $$ = MSObservationParse::thisMSObsParser->selectObservationIdsLTEQ(idv); free($2); } | GE INT // >=ID { const Vector idv(1,atoi($2)); $$ = MSObservationParse::thisMSObsParser->selectObservationIdsGTEQ(idv); free($2); } | GE INT AMPERSAND LE INT // >=ID & <=ID { Int n0=atoi($2), n1=atoi($5); $$ = MSObservationParse::thisMSObsParser->selectRangeGEAndLE(n0,n1); free($2); free($5); } | GT INT AMPERSAND LT INT // >ID & selectRangeGTAndLT(n0,n1); free($2); free($5); } ; scanboundsexpr: scanidbounds {$$=$1;} // // Build a list of scan IDs. This can be a single ID or a range of // IDs converted to a list. Actual selection is done at the end of // parsing cycle (at the terminal node above). // scanids: INT { $$=&MSObservationParse::thisMSObsParser->accumulateIDs(atoi($1)); free($1); } | INT DASH INT { $$=&MSObservationParse::thisMSObsParser->accumulateIDs(atoi($1),atoi($3)); free($1); free($3); } ; %% casacore-3.7.1/ms/MSSel/MSObservationParse.cc000066400000000000000000000164001476623553700207640ustar00rootroot00000000000000//# MSObservationParse.cc: Classes to hold results from scan grammar parser //# Copyright (C) 1994,1995,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSObservationParse* MSObservationParse::thisMSObsParser = 0x0; // Global pointer to the parser object TableExprNode MSObservationParse::columnAsTEN_p; // TableExprNode* MSObservationParse::node_p = 0x0; // Vector MSObservationParse::idList; // std::vector MSObservationParse::parsedIDList_p; //# Constructor MSObservationParse::MSObservationParse () : MSParse(), colName(MS::columnName(MS::OBSERVATION_ID)), maxObs_p(1000) { columnAsTEN_p=TableExprNode(); } //# Constructor with given ms name. MSObservationParse::MSObservationParse (const MeasurementSet* ms, const MSObservation& obsSubTable, const TableExprNode& colAsTEN) : MSParse(ms, "Observation"), colName(MS::columnName(MS::OBSERVATION_ID)), maxObs_p(1000) { idList.resize(0); parsedIDList_p.resize(0); Int nrows = obsSubTable.nrow(); obsIDList_p.resize(nrows); indgen(obsIDList_p); columnAsTEN_p=colAsTEN; maxObs_p=nrows; } std::vector& MSObservationParse::accumulateIDs(const Int id0, const Int id1) { Vector theIDs; if (id1 < 0) { parsedIDList_p.push_back(id0);theIDs.resize(1);theIDs[0]=id0; // Also accumulate IDs in the global ID list which contains IDs // generated from all expressions (INT, INT DASH INT, and bounds // expressions (>ID, & v) { Int currentSize = idList.nelements(); Int n = v.nelements() + currentSize; Int j=0; idList.resize(n, True); for(Int i=currentSize;icol(colName) > n0) && // (ms()->col(colName) < n1)); TableExprNode condition = TableExprNode( (columnAsTEN_p > n0) && (columnAsTEN_p < n1)); if ((n0 < 0) || (n1 < 0) || (n1 <= n0)) { ostringstream os; os << "ObservationID Expression: Malformed range bounds " << n0 << " (lower bound) and " << n1 << " (upper bound)"; throw(MSSelectionObservationParseError(os.str())); } Vector tmp(n1-n0-1); Int j=n0+1; for(uInt i=0;icol(colName) >= n0) && // (ms()->col(colName) <= n1)); TableExprNode condition = TableExprNode( (columnAsTEN_p >= n0) && (columnAsTEN_p <= n1)); if ((n0 < 0) || (n1 < 0) || (n1 <= n0)) { ostringstream os; os << "ObservationID Expression: Malformed range bounds " << n0 << " (lower bound) and " << n1 << " (upper bound)"; throw(MSSelectionObservationParseError(os.str())); } Vector tmp(n1-n0+1); Int j=n0; for(uInt i=0;i& scanids) { if (scanids.size() > 0) { // cerr << "Selecting disjoint list: " << scanids << endl; //TableExprNode condition = TableExprNode(ms()->col(colName).in(scanids)); TableExprNode condition = TableExprNode(columnAsTEN_p.in(scanids)); appendToIDList(scanids); addCondition(node_p,condition); } return &node_p; } const TableExprNode* MSObservationParse::selectObservationIdsGT(const Vector& scanids) { //TableExprNode condition = TableExprNode(ms()->col(colName) > scanids[0]); TableExprNode condition = TableExprNode(columnAsTEN_p > scanids[0]); Int n=maxObs_p-scanids[0]+1,j; Vector tmp(n); j=scanids[0]+1; for(Int i=0;i& scanids) { //TableExprNode condition = TableExprNode(ms()->col(colName) < scanids[0]); TableExprNode condition = TableExprNode(columnAsTEN_p < scanids[0]); Vector tmp(scanids[0]); for(Int i=0;i& scanids) { //TableExprNode condition = TableExprNode(ms()->col(colName) >= scanids[0]); TableExprNode condition = TableExprNode(columnAsTEN_p >= scanids[0]); Int n=maxObs_p-scanids[0]+1,j; Vector tmp(n); j=scanids[0]; for(Int i=0;i& scanids) { //TableExprNode condition = TableExprNode(ms()->col(colName) <= scanids[0]); TableExprNode condition = TableExprNode(columnAsTEN_p <= scanids[0]); Vector tmp(scanids[0]+1); for(Int i=0;i<=scanids[0];i++) tmp[i] = i; appendToIDList(tmp); addCondition(node_p,condition); return &node_p; } Vector MSObservationParse::selectedIDs() { return set_intersection(obsIDList_p,idList); } const TableExprNode MSObservationParse::node() { return node_p; } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSSel/MSObservationParse.h000066400000000000000000000111531476623553700206260ustar00rootroot00000000000000//# MSObservationParse.h: Classes to hold results from scan grammar parser //# Copyright (C) 1994,1995,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSOBSERVATIONPARSE_H #define MS_MSOBSERVATIONPARSE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // Class to hold values from scan grammar parser // // // // // //# Classes you should understand before using this one. // // // MSObservationParse is the class used to parse a scan command. // // // MSObservationParse is used by the parser of scan sub-expression statements. // The parser is written in Bison and Flex in files MSObservationGram.y and .l. // The statements in there use the routines in this file to act // upon a reduced rule. // Since multiple tables can be given (with a shorthand), the table // names are stored in a list. The variable names can be qualified // by the table name and will be looked up in the appropriate table. // // The class MSObservationParse only contains information about a table // used in the table command. Global variables (like a list and a vector) // are used in MSObservationParse.cc to hold further information. // // Global functions are used to operate on the information. // The main function is the global function msScanCommand. // It executes the given STaQL command and returns the resulting ms. // This is, in fact, the only function to be used by a user. // // // It is necessary to be able to give a ms command in ASCII. // This can be used in a CLI or in the table browser to get a subset // of a table or to sort a table. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class MSObservationParse : public MSParse { public: // Default constructor MSObservationParse (); // Associate the ms and the shorthand. MSObservationParse (const MeasurementSet* ms, const MSObservation& obsSubtable, const TableExprNode& colAsTEN); ~MSObservationParse() {columnAsTEN_p=TableExprNode();} const TableExprNode *selectRangeGTAndLT(const Int& n0, const Int& n1); const TableExprNode *selectRangeGEAndLE(const Int& n0, const Int& n1); const TableExprNode *selectObservationIds(const Vector& scanids); inline const TableExprNode *selectObservationIds() {return selectObservationIds(Vector(parsedIDList_p));} const TableExprNode *selectObservationIdsGT(const Vector& scanids); const TableExprNode *selectObservationIdsLT(const Vector& scanids); const TableExprNode *selectObservationIdsGTEQ(const Vector& scanids); const TableExprNode *selectObservationIdsLTEQ(const Vector& scanids); std::vector& accumulateIDs(const Int id0, const Int id1=-1); // Get table expression node object. const TableExprNode node(); Vector selectedIDs(); void reset(){idList.resize(0);} void cleanup() {} void setMaxObs(const Int& n) {maxObs_p=n;} static MSObservationParse* thisMSObsParser; private: TableExprNode node_p; Vector idList,obsIDList_p; std::vector parsedIDList_p; const String colName; void appendToIDList(const Vector& v); Int maxObs_p; static TableExprNode columnAsTEN_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSParse.cc000066400000000000000000000071301476623553700165500ustar00rootroot00000000000000//# MSParse.cc: Classes to hold results from an ms grammar parser //# Copyright (C) 1994,1995,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MeasurementSet *MSParse::ms_p = 0; MSSelectableTable *MSParse::msInterface_p = 0; //# Default constructor. MSParse::MSParse():tempMSInterface_p(NULL) { tempMSInterface_p = new MSInterface(); } //# Constructor with given ms name. MSParse::MSParse (const MeasurementSet* ms, const String& shorthand) : shorthand_p (shorthand), tempMSInterface_p(NULL) { ms_p = const_cast(ms); tempMSInterface_p = new MSInterface(*ms); } MSParse::MSParse (const MSSelectableTable* msLike, const String& shorthand) :shorthand_p (shorthand), tempMSInterface_p(NULL) { msInterface_p = const_cast(msLike); } MSParse::~MSParse() { if (tempMSInterface_p != NULL) delete tempMSInterface_p; } MSParse::MSParse (const MSParse& that) : shorthand_p (that.shorthand_p) {} MSParse& MSParse::operator= (const MSParse& that) { if (this != &that) shorthand_p = that.shorthand_p; return *this; } Bool MSParse::test (const String& str) const { return (shorthand_p == str ? True : False); } String& MSParse::shorthand() { return shorthand_p; } MeasurementSet* MSParse::ms() { if (msInterface_p != NULL) return (MeasurementSet *)msInterface()->asMS(); else return ms_p; } MSSelectableTable* MSParse::msInterface() { if (msInterface_p != NULL) return msInterface_p; // If constructed with MSInterface else if (tempMSInterface_p != NULL) return tempMSInterface_p; // If constructed with MS else throw(AipsError("Internal error in MSParse::msInterface()")); } void MSParse::addCondition(TableExprNode& target, TableExprNode& source) { if(target.isNull()) target = source; else target = target || source; } //# The AipsIO functions are needed for the list of MSParse, but //# we do not support it actually. AipsIO& operator<< (AipsIO& ios, const MSParse&) { throw (AipsError ("AipsIO << MSParse& not possible")); return ios; } AipsIO& operator>> (AipsIO& ios, MSParse&) { throw (AipsError ("AipsIO >> MSParse& not possible")); return ios; } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSSel/MSParse.h000066400000000000000000000111241476623553700164100ustar00rootroot00000000000000//# MSParse.h: Classes to hold results from an ms grammar parser //# Copyright (C) 1994,1995,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSPARSE_H #define MS_MSPARSE_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class AipsIO; // // Class to hold values from an ms grammar parser // // // // // //# Classes you should understand before using this one. // // // MSParse is the class used to parse an ms command. // // // MSParse is used by the parser of an ms sub-expression statements. // The parser is written in Bison and Flex in files MSXXXGram.y and .l. // The statements in there use the routines in this file to act // upon a reduced rule. // Since multiple tables can be given (with a shorthand), the table // names are stored in a list. The variable names can be qualified // by the table name and will be looked up in the appropriate table. // // The class MSParse only contains information about an ms // used in the ms command. Global variables (like a list and a vector) // are used in MSParse.cc to hold further information. // // Global functions are used to operate on the information. // The main function is the global function msXXXCommand. // It executes the given STaQL command and returns the resulting ms. // This is, in fact, the only function to be used by a user. // // // It is necessary to be able to give a ms command in ASCII. // This can be used in a CLI or in the table browser to get a subset // of a table or to sort a table. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class MSParse { // Dummy AipsIO routines; they are needed for the List container. // friend AipsIO& operator<< (AipsIO&, const MSParse&); friend AipsIO& operator>> (AipsIO&, MSParse&); // public: // Default constructor for List container class. MSParse (); // Copy constructor (copy semantics). MSParse (const MSParse&); ~MSParse (); // Assignment (copy semantics). MSParse& operator= (const MSParse&); // Associate the ms and the shorthand. MSParse (const MeasurementSet* ms, const String& shorthand); // Associate the ms and the shorthand. MSParse (const MSSelectableTable* ms, const String& shorthand); // Test if shorthand matches. Bool test (const String& shortHand) const; // Get the shorthand. String& shorthand(); // Get ms object. MeasurementSet* ms(); // Get ms object. MSSelectableTable* msInterface(); void setMS(MeasurementSet* ms) {ms_p=ms;} void setMSInterface(MSSelectableTable* msI) {msInterface_p = msI;} static MeasurementSet *ms_p; static MSSelectableTable *msInterface_p; void addCondition(TableExprNode& target, TableExprNode& source); private: String shorthand_p; // The following exists for the period we make the transition from // using MS to using MSSelectableTable. Till then, both interfaces // have to be supported. MSSelectableTable *tempMSInterface_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSPointingIndex.cc000066400000000000000000000043211476623553700202540ustar00rootroot00000000000000//# MSPointingIndex.cc: this defined MSPointingIndex //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSPointingIndex::MSPointingIndex() : MSTableIndex() {;} MSPointingIndex::MSPointingIndex(const MSPointing &pointing) : MSTableIndex(pointing, stringToVector("ANTENNA_ID")) { attachIds();} MSPointingIndex::MSPointingIndex(const MSPointingIndex &other) : MSTableIndex(other) { attachIds();} MSPointingIndex::~MSPointingIndex() {;} MSPointingIndex &MSPointingIndex::operator=(const MSPointingIndex &other) { if (this != &other) { MSTableIndex::operator=(other); attachIds(); } return *this; } void MSPointingIndex::attach(const MSPointing &pointing) { MSTableIndex::attach(pointing, stringToVector("ANTENNA_ID")); attachIds(); } void MSPointingIndex::attachIds() { antennaId_p.attachToRecord(accessKey(), "ANTENNA_ID"); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSSel/MSPointingIndex.h000066400000000000000000000051261476623553700201220ustar00rootroot00000000000000//# MSPointingIndex: index into a MeasurementSet POINTING subtable //# Copyright (C) 2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSPOINTINGINDEX_H #define MS_MSPOINTINGINDEX_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# forward declarations class MSPointing; // // // // // // //
      • MeasurementSet //
      • MSTableIndex // // // // // // // // // // // // // // // //
      • //
      • // // class MSPointingIndex : public MSTableIndex { public: // no index attached, use the attach function or assignment operator to change that MSPointingIndex(); // construct one using the indicated POINTING table MSPointingIndex(const MSPointing &pointing); // construct one from another MSPointingIndex(const MSPointingIndex &other); virtual ~MSPointingIndex(); MSPointingIndex &operator=(const MSPointingIndex &other); void attach(const MSPointing &pointing); // access to the antenna ID key, throws an exception if isNull() is False Int &antennaId() {return *antennaId_p;} private: RecordFieldPtr antennaId_p; void attachIds(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSPolIndex.cc000066400000000000000000000126301476623553700172210ustar00rootroot00000000000000//# MSPolIndex.cc: implementation of MSPolIndex.h //# Copyright (C) 2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //------------------------------------------------------------------------- MSPolarizationIndex::MSPolarizationIndex(const MSPolarization& polarizationTable) : msPolarizationCols_p(polarizationTable) { // Construct from an MS POLARIZATION subtable // Input: // polarizationTable const MSPolarization& Input MSPolarization // sub-table // Output to private data: // msPolarizationCols_p MSPolarizationColumns MSPolarization columns // accessor // polarizationIds_p Vector Polarization id.'s // nrows_p Int Number of rows // // Generate an array of polarization id's, used in later queries nrows_p = msPolarizationCols_p.nrow(); polarizationIds_p.resize(nrows_p); indgen(polarizationIds_p); } //------------------------------------------------------------------------- Vector MSPolarizationIndex::matchCorrTypeAndProduct(const Vector& corrType, const Matrix& corrProduct) { // Match a set of polarization correlation types and receptor cross-products // Input: // corrType const Vector& Set of polarization correlation // types (as defined in Stokes.h) // corrProduct const Matrix& Set of receptor cross-products // Output: // matchCorrTypeAndProduct Vector Matching polarization id.'s // // Match the polarization correlation types and receptor cross-products // by row and correlation index uInt numCorr = std::min(corrType.nelements(), corrProduct.ncolumn()); uInt nrows = msPolarizationCols_p.nrow(); Vector corrMatch(nrows, False); for (uInt row=0; row rowCorrType; msPolarizationCols_p.corrType().get(row, rowCorrType); Matrix rowCorrProduct; msPolarizationCols_p.corrProduct().get(row, rowCorrProduct); corrMatch(row) = (rowCorrType.nelements() == numCorr && rowCorrProduct.ncolumn() == numCorr); if (corrMatch(row)) { for (uInt i=0; i < numCorr; i++) { corrMatch(row) = (corrMatch(row) && rowCorrType(i) == corrType(i) && rowCorrProduct(0,i) == corrProduct(0,i) && rowCorrProduct(1,i) == corrProduct(1,i)); } } } LogicalArray maskArray(corrMatch); MaskedArray maskRowNumbers(polarizationIds_p, maskArray); return maskRowNumbers.getCompressedArray(); } // Add for MS selection Vector MSPolarizationIndex::matchCorrType(const Vector& corrType, Bool exactMatch) { // Match a set of polarization correlation types // Input: // corrType const Vector& Set of polarization correlation // types (as defined in Stokes.h) // Output: // matchCorrType Vector Matching polarization id.'s // // Match the polarization correlation types by row and correlation index uInt numCorr = corrType.nelements(); uInt nrows = msPolarizationCols_p.nrow(); Vector allMatch(numCorr); Vector corrMatch(nrows, False); allMatch=False; for (uInt row=0; row rowCorrType; msPolarizationCols_p.corrType().get(row, rowCorrType); if (exactMatch) for (uInt i=0; i < numCorr; i++) corrMatch(row) = (rowCorrType(i) == corrType(i)); else { for(uInt i=0; i maskRowNumbers(polarizationIds_p, maskArray); return maskRowNumbers.getCompressedArray(); } //------------------------------------------------------------------------- } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSSel/MSPolIndex.h000066400000000000000000000067071476623553700170730ustar00rootroot00000000000000//# MSPolIndex: index or lookup in an MS POLARIZATION subtable //# Copyright (C) 2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSPOLINDEX_H #define MS_MSPOLINDEX_H //# includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# forward declarations // // Class to handle lookup or indexing into an MS POLARIZATION subtable // // // // // // //
      • MeasurementSet //
      • MSPolarization // // // // From "MeasurementSet", "POLARIZATION subtable" and "index". // // // // This class provides lookup and indexing into an MS POLARIZATION // subtable. These services include returning rows numbers // (which for the POLARIZATION subtable are POLARIZATION_ID's) associated // with specific data in the subtable. // // // // // // // Collect together all subtable indexing and lookup for the // POLARIZATION subtable, for encapsulation and efficiency. // // // //
      • //
      • // // class MSPolarizationIndex { public: // Construct from an MS POLARIZATION subtable MSPolarizationIndex(const MSPolarization& polarizationTable); // Null destructor virtual ~MSPolarizationIndex() {} // Look up POLARIZATION_ID's for a given set of polarization correlation // types and receptor cross-products Vector matchCorrTypeAndProduct(const Vector& corrType, const Matrix& corrProduct); // /////////////////// Add for MS selection ////////////////////////////// // Only Look up POLARIZATION_ID's for a given set of polarization correlation // types Vector matchCorrType(const Vector& corrType,Bool exactMatch=True); private: // Disallow null constructor MSPolarizationIndex(); // POLARIZATION subtable column accessor MSPolarizationColumns msPolarizationCols_p; // Vector cache of polarization id's Vector polarizationIds_p; Int nrows_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSPolnGram.cc000066400000000000000000000103141476623553700172130ustar00rootroot00000000000000//# MSPolnGram.cc: Grammar for polarization selection expressions //# Copyright (C) 1998,1999,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include // Define the yywrap function for flex. int MSPolnGramwrap() { return 1; } namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Declare a file global pointer to a char* for the input string. /* static const char* strpMSPolnGram = 0; */ static Int posMSPolnGram = 0; // MSPolnGramwrap out of namespace //------------------------------------------------------------------------------ // //# Parse the command. //# Do a yyrestart(yyin) first to make the flex scanner reentrant. // //------------------------------------------------------------------------------ // int msPolnGramParseCommand (const MeasurementSet* ms, const String& command, TableExprNode& node, Vector& selectedDDIDs, std::map >& selectedPolnMap, std::map > >& selectedSetupMap) { try { Int ret; MSPolnParse parser(ms); parser.reset(); // parse command string ret=parser.theParser(command); // node=(*(parser.node())); node=((parser.node())); selectedDDIDs = parser.selectedDDIDs(); selectedPolnMap = parser.selectedPolnMap(); selectedSetupMap = parser.selectedSetupMap(); return ret; } catch (MSSelectionPolnError &x) { String newMesgs; newMesgs = constructMessage(msPolnGramPosition(), command); x.addMessage(newMesgs); MSPolnGramerror((char *)(x.what())); throw; } } // //------------------------------------------------------------------------------ // //# Give the table expression node const TableExprNode* msPolnGramParseNode() { // return MSPolnParse::node(); return NULL; } // //------------------------------------------------------------------------------ // void msPolnGramParseDeleteNode() { // MSPolnParse::cleanup(); } // //------------------------------------------------------------------------------ // //# Give the string position. int& msPolnGramPosition() { return posMSPolnGram; } // //------------------------------------------------------------------------------ // void MSPolnGramerror (char* msg) { throw(MSSelectionPolnParseError(String("Poln. expression error: ")+msg)); // throw (MSSelectionPolnParseError("Poln Expression: Parse error at or near '" + // String(MSPolnGramtext) + "'")); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSSel/MSPolnGram.h000066400000000000000000000057241476623553700170660ustar00rootroot00000000000000//# MSPolnGram.h: Grammar for ms field sub-expressions //# Copyright (C) 1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSPOLNGRAM_H #define MS_MSPOLNGRAM_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MeasurementSet; class TableExprNode; // // Global functions to drive the MSPolnParse class. These, for // Polarization selection, need not be global functions, but are // done this way to keep the interface uniform for the various // selection expressions. // // // // // //# Classes you should understand before using this one. // // // // // // //# A List of bugs, limitations, extensions or planned refinements. // // // The top level interface to the parser. int msPolnGramParseCommand (const MeasurementSet *ms, const String& command, TableExprNode& node, Vector& selectedDDIDs, std::map >& selectedPolnMap, std::map > >& selectedSetupMap ); // The error handler. // It throws an exception with the current token. void MSPolnGramerror (char*); // Give the table expression node. const TableExprNode *msPolnGramParseNode(); void msPolnGramParseDeleteNode(); // Give the current position in the string. // This can be used when parse errors occur. int& msPolnGramPosition(); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSPolnParse.cc000066400000000000000000000373571476623553700174170ustar00rootroot00000000000000//# MSPolnParse.cc: Classes to hold results from Poln grammar parseing //# Copyright (C) 1994,1995,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // MSPolnParse* MSPolnParse::thisMSSParser = 0x0; // Global pointer to the parser object // TableExprNode* MSPolnParse::node_p = 0x0; // Vector MSPolnParse::ddIDList; // std::map > MSPolnParse::polList(Vector(0)); //# Constructor //------------------------------------------------------------------------------ // extern const char* strpMSPolnGram; MSPolnParse::MSPolnParse () : MSParse(), node_p(), // node_p(0x0), ddIDList_p() { // if (MSPolnParse::node_p!=0x0) delete MSPolnParse::node_p; // MSPolnParse::node_p=0x0; // node_p = new TableExprNode(); } //# Constructor with given ms name. //------------------------------------------------------------------------------ // MSPolnParse::MSPolnParse (const MeasurementSet* ms) : MSParse(ms, "Pol"), node_p(), // node_p(0x0), ddIDList_p() { ddIDList_p.resize(0); // if(MSPolnParse::node_p) delete MSPolnParse::node_p; // node_p = new TableExprNode(); } // //------------------------------------------------------------------------------ // const TableExprNode MSPolnParse::selectFromIDList(const Vector& ddIDs) { TableExprNode condition; if (ddIDs.nelements() > 0) condition = ms()->col(MS::columnName(MS::DATA_DESC_ID)).in(ddIDs); // Int n=ddIDs.nelements(); // const String DATA_DESC_ID = MS::columnName(MS::DATA_DESC_ID); // if (n > 0) // { // for(Int i=0; icol(DATA_DESC_ID)==ddIDs[i])); // else // condition = condition || ((ms()->col(DATA_DESC_ID)==ddIDs[i])); // } if (condition.isNull()) throw(MSSelectionPolnError(String("No match for the [SPW:]POLN specifications "))); // if(node_p->isNull()) *node_p = condition; // else *node_p = *node_p || condition; // return node_p; if(node_p.isNull()) node_p = condition; else node_p = node_p || condition; return node_p; } // //------------------------------------------------------------------------------ // Vector MSPolnParse::getMapToDDIDs(MSDataDescIndex& msDDNdx, MSPolarizationIndex& /*msPolNdx*/, const Vector& spwIDs, Vector& polnIDs, Vector& polnIndices) { Vector ddIDs; Vector thisDDList; Vector validPolIDs, validPolIndices; if (polnIDs.nelements() == 0) { ostringstream mesg; mesg << "No match for polarization ID(s) "; throw(MSSelectionPolnParseError(String(mesg.str()))); } for (uInt p=0; p tmp=msDDNdx.matchSpwIdAndPolznId(spwIDs[s],polnIDs[p]); if (tmp.nelements() > 0) { ddIDs.resize((n=ddIDs.nelements())+1,True); ddIDs[n]=tmp[0]; thisDDList.resize((n=thisDDList.nelements())+1,True); thisDDList[n]=tmp[0]; } } if (thisDDList.nelements() > 0) { uInt n; setIDLists(polnIDs[p], 1, thisDDList); validPolIDs.resize((n=validPolIDs.nelements())+1,True); validPolIDs[n]=polnIDs[p]; validPolIndices.resize((n=validPolIndices.nelements())+1,True); validPolIndices[n]=polnIndices[p]; // cout << "Found DDID for PolID " << polnIDs[p] << endl; } // else // cout << "Not found DDID for PolID " << polnIDs[p] << endl; } polnIDs.resize(0); polnIDs=validPolIDs; polnIndices.resize(0); polnIndices=validPolIndices; return ddIDs; } // //------------------------------------------------------------------------------ // Vector MSPolnParse::getMapToDDIDsV2(const String& polnExpr, const Vector& spwIDs, Vector& polnIDs, Vector& polnIndices) { Vector ddIDs, polTypes; Vector thisDDList; Vector validPolIDs;//, validPolIndices; MSDataDescIndex msDDNdx(ms()->dataDescription()); MSPolarizationIndex msPolNdx(ms()->polarization()); // cout << "SpwIDs = " << spwIDs << endl; polnIDs = getPolnIDsV2(polnExpr, polTypes); // cout << "PolIDs = " << polnIDs << " polTypes = " << polTypes << endl; // if (polnIDs.nelements() == 0) if (polTypes.nelements() == 0) { ostringstream mesg; mesg << "No match for polarization ID(s) "; throw(MSSelectionPolnParseError(String(mesg.str()))); } for (uInt p=0; p tt; tt = getPolnIndices(polnIDs[p],polTypes); // cout << "Poln indices for " << polnIDs[p] << " = " << tt << endl; polnIndices.resize(0); polnIndices=tt; thisDDList.resize(0); for (uInt s=0; s tmp=msDDNdx.matchSpwIdAndPolznId(spwIDs[s],polnIDs[p]); if (tmp.nelements() > 0) { ddIDs.resize((n=ddIDs.nelements())+1,True); ddIDs[n]=tmp[0]; thisDDList.resize((n=thisDDList.nelements())+1,True); thisDDList[n]=tmp[0]; setIDLists((Int)polnIDs[p],0,polnIndices); polMap_p[polnIDs[p]].resize(0); polMap_p[polnIDs[p]]=polnIndices; // cout << "DDIDs for SPW = " << spwIDs[s] << " = " << tmp[0] << endl; } } if (thisDDList.nelements() > 0) { uInt n; setIDLists(polnIDs[p], 1, thisDDList); validPolIDs.resize((n=validPolIDs.nelements())+1,True); validPolIDs[n]=polnIDs[p]; // validPolIndices.resize((n=validPolIndices.nelements())+1,True); // validPolIndices[n]=polnIndices[p]; } // else // cout << "Not found DDID for PolID " << polnIDs[p] << endl; } if (ddIDs.nelements() == 0) { ostringstream mesg; mesg << "No match for polarization ID(s) "; // strpMSPolnGram = polnExpr.c_str(); throw(MSSelectionPolnParseError(String(mesg.str()))); } polnIDs.resize(0); polnIDs=validPolIDs; // polnIndices.resize(0); polnIndices=validPolIndices; return ddIDs; } // //------------------------------------------------------------------------------ // Give a list of pol IDs, return the list of row numbers in the // POLARIZATION sub-table which contains the listed Pol IDs. Pol // IDs are defined as the enumrations Stokes::StokesTypes - // i.e. "RR", "LL" etc. // Vector MSPolnParse::matchPolIDsToPolTableRow(const Vector& polIds, std::map >& /*polIndexMap*/, Vector& polIndices, Bool addToMap) { Vector rowList; MSPolarization mspol(ms()->polarizationTableName()); MSPolarizationColumns mspolC(mspol); // // First extract the corrType column of the Polarization sub-table // row-by-row (since this column can be of variable shape!) // for (uInt row=0; row corrType; mspolC.corrType().get(row,corrType); // // Next - look for match between the supplied polId list in // the extracted corrType. User support: Do not assume the // order of the supplied pol IDs (human free-will was involved // in generating that list!). Also do a max-match. E.g. a // supplied polID list from "RR LL" should match all of the // following corrType lists: "RR LL", "RR LL LR RL", "RR", // "LL". // Bool allFound=False; uInt foundCounter=0; // Vector polIndices(0,-1); for(uInt i=0; i MSPolnParse::getPolnIndices(const Int& polId, const Vector& polnTypes) { MSPolarization mspol(ms()->polarizationTableName()); MSPolarizationColumns mspolC(mspol); Vector polIndices; // for (uInt row=0; row corrType; mspolC.corrType().get(polId,corrType); for(uInt i=0; i MSPolnParse::getPolnIDs(const String& polSpec, Vector& polIndices) { String sep(","); Vector tokens; Vector idList, polIDList; // // Split the given string into ";" separated tokens. Upcase the // string before splitting. // tokenize(polSpec,sep,tokens,True); idList.resize(tokens.nelements()); for(uInt i=0;i MSPolnParse::getPolnIDsV2(const String& polSpec, Vector& polTypes) { String sep(","); Vector tokens; Vector polIDList, polIndices; // // Split the given string into ";" separated tokens. Upcase the // string before splitting. // tokenize(polSpec,sep,tokens,True); polTypes.resize(tokens.nelements()); for(uInt i=0;i DDID map // Int MSPolnParse::theParser(const String& command) { Int ret=0, nSpecList=0; Vector polnSpecList; String sep(";"); nSpecList=tokenize(command,sep,polnSpecList); for(Int i=0;i tokens,tmp; Vector spwIDs, spwDDIDs; Matrix chanIDs; Vector polnIDs; String s(":"), spwExpr, polnExpr; Int nTokens; // // User suppport: Check if they tried [SPW:CHAN:]POLN kind of // specification. Darn - String::freq(...) does not work! // tokenize(polnSpecList[i],s,tokens); tokenize(tokens[0],s,tmp); nTokens = tokens.nelements(); if (nTokens > 2) throw(MSSelectionPolnParseError(String("Too many ':'s. [Tip: Channel " "specification is not useful " "and not allowed.]"))); // // If there were two ":" separate tokens, they were of the form SPW:POLN // if (nTokens == 2) { spwExpr = tokens[0]; polnExpr= tokens[1]; } // // If there was only one token, it was POLN - equivalent of *:POLN // if (nTokens == 1) { spwExpr="*"; polnExpr=tokens[0]; } // // Parse the SPW part. Pass the token to the SPW parser. // try { TableExprNode colAsTEN = ms()->col(ms()->columnName(MS::DATA_DESC_ID)); spwIDs.resize(0); // if (spwExpr_p != "" && // msSpwGramParseCommand(ms, spwExpr_p,spwIDs_p, chanIDs_p) == 0) msSpwGramParseCommand(ms()->spectralWindow(), ms()->dataDescription(), colAsTEN, spwExpr, spwIDs, chanIDs, spwDDIDs); // msSpwGramParseCommand(ms(), spwExpr,spwIDs, chanIDs); } catch (MSSelectionSpwError &x) { throw(MSSelectionPolnParseError(x.what())); } // // Parse the POLN part. // try { Vector polIndices; Vector tddIDList,tt; // polnIDs=getPolnIDs(polnExpr, polIndices); // MSDataDescIndex msDDNdx(ms()->dataDescription()); // MSPolarizationIndex msPolNdx(ms()->polarization()); // cout << "PolIDs = " << polnIDs << endl; // tddIDList=getMapToDDIDs(msDDNdx, msPolNdx, spwIDs, polnIDs, polIndices); // cout << "PolExpr = " << polnExpr << endl; tddIDList=getMapToDDIDsV2(polnExpr, spwIDs, polnIDs, polIndices); // cout << "DDIDs = " << tddIDList << endl; // cout << "-----------------------------------" << endl; tt=set_union(tddIDList, ddIDList_p); ddIDList_p.resize(0); ddIDList_p = tt; } catch (MSSelectionPolnParseError& x) { String mesg("(named " + polnExpr + ")"); // mesg = mesg + polnExpr + ")"; x.addMessage(mesg); throw; } selectFromIDList(ddIDList_p); } { // // Remove entries which did not map to any DD ID(s) // for (const auto& x : setupMap_p) { if (x.second[1].nelements() == 0) { setupMap_p.erase(x.first); } } } return ret; } // //------------------------------------------------------------------------------ // A convenience method to set the vectors of Poln or DD IDs in the setupMap. // void MSPolnParse::setIDLists(const Int key, const Int ndx, Vector& val) { if (ndx>1) throw(MSSelectionError("Internal error in MSPolnParse::setIDLists(): Index > 1")); if (setupMap_p[key].nelements() !=2) setupMap_p[key].resize(2, True); if (val.nelements() > 0) { Vector v0=val; auto elem = setupMap_p.find(key); if (elem != setupMap_p.end()) { Vector t0; v0.resize(0); v0 = elem->second[ndx]; t0=set_union(val,v0); v0.resize(0); v0 = t0; } if (setupMap_p[key][ndx].nelements() > 0) setupMap_p[key][ndx].resize(0); setupMap_p[key][ndx]=v0; } } // //------------------------------------------------------------------------------ // const TableExprNode MSPolnParse::node() { return node_p; } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSSel/MSPolnParse.h000066400000000000000000000117541476623553700172520ustar00rootroot00000000000000//# MSPolnParse.h: Classes to hold results from poln grammar parseing //# Copyright (C) 1994,1995,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSPOLNPARSE_H #define MS_MSPOLNPARSE_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // Class to hold values from field grammar parser // // // // // //# Classes you should understand before using this one. // // // MSPolnParse is the class used to parse a polarization selection command. // // // // MSPolnParse is used by the parser of polarization sub-expression // statements of the type [SPW:]POLN. Since this is a relatively // simple expression to tokenize and parse, this parser is written // without Bison or Flex. The methods of this class take an // expression, and internally generate a list of the Data Description // IDs that should be used to select the rows in the MS main table. // The map of Polarization IDs (row numbers in the POLARIZATION // sub-table) and the list of indices to be used to pick the user // selected polarzation data (in the DATA columns of the MS main // table) is also generated. This map is intended to be used along // with the map of SPW and selected channels to apply the in-row // selection (Slice on the data columns). // // // // It is necessary to be able to give a data selection // command in ASCII. This can be used in a CLI or in the table // browser to get a subset of a table or to sort a table. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class MSPolnParse : public MSParse { public: // Default constructor MSPolnParse (); // ~MSPolnParse() {cleanup();} // Associate the ms and the shorthand. MSPolnParse (const MeasurementSet* ms); const TableExprNode selectFromIDList(const Vector& ddIDs); // Get table expression node object. const TableExprNode node(); // static MSPolnParse* thisMSSParser; void reset() {polMap_p.clear(); ddIDList_p.resize(0);} void cleanup() {/*if (node_p) delete node_p;node_p=0x0;*/} Int theParser(const String& command); // Vector& selectedDDIDs, // Matrix& selectedSpwPolnMap); std::map > selectedPolnMap() {return polMap_p;} std::map > > selectedSetupMap() {return setupMap_p;} Vector selectedDDIDs() {return ddIDList_p;} private: Vector getMapToDDIDs(MSDataDescIndex& msDDNdx, MSPolarizationIndex& msPolNdx, const Vector& spwIDs, Vector& polnIDs, Vector& polIndices); Vector matchPolIDsToPolTableRow(const Vector& polIds, std::map >& polIndexMap, Vector& polIndices, Bool addToMap=False); Vector getPolnIDs(const String& polSpec, Vector& polIndices); Vector getPolnIndices(const Int& polnID, const Vector& polnIDList); // // These are the versions used in the code. Vector getPolnIDsV2(const String& polSpec, Vector& polTypes); Vector getMapToDDIDsV2(const String& polExpr, const Vector& spwIDs, Vector& polnIDs, Vector& polnIndices); TableExprNode node_p; Vector ddIDList_p; std::map > polMap_p; std::map > > setupMap_p; void setIDLists(const Int key, const Int ndx, Vector& val); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSSSpwErrorHandler.cc000066400000000000000000000041511476623553700207020ustar00rootroot00000000000000//# MSSSpwErrorHandler.cc: Error handler for the SPW parser //# Copyright (C) 1994,1995,1996,1997,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN String MSSSpwErrorHandler::constructMessage() { ostringstream Mesg; if (messageList.size() > 0) { Mesg << messageList[0]; if (tokenList.size() > 0) for (uInt i=0;i 0) { String mesg(constructMessage()); mssErrorType.addMessage(mesg); LogIO logIO; logIO << mssErrorType.getMesg() << LogIO::WARN << LogIO::POST; } } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSSel/MSSSpwErrorHandler.h000066400000000000000000000045751476623553700205560ustar00rootroot00000000000000//# MSSpwErrorHandler.h: Error handler class for SPW parser //# Copyright (C) 1994,1995,1996,1997,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSPWERRORHANDLER_H #define MS_MSSPWERRORHANDLER_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# This header file defines the error handler class for the //# SPW parser in MSSelection module // // // // // // // // Specialization of the MSSelectionErrorHandler for SPW parser. // The constructMessage() and handleError() methods are specialized // here. // // class MSSSpwErrorHandler: public MSSelectionErrorHandler { public: // The default constructor generates the message "Table error". MSSSpwErrorHandler():MSSelectionErrorHandler() {}; virtual ~MSSSpwErrorHandler () {}; String constructMessage(); void handleError(MSSelectionError& mssErrorType) ; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSScanGram.cc000066400000000000000000000147251476623553700172010ustar00rootroot00000000000000//# MSScanGram.cc: Grammar for scan expressions //# Copyright (C) 1998,1999,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // MSScanGram; grammar for scan command lines // This file includes the output files of bison and flex for // parsing command lines. #include #include #include #include #include // routines used by bison actions #include // routines used by bison actions #include //# stdlib.h is needed for bison 1.28 and needs to be included here //# (before the flex/bison files). //# Bison defines WHERE, which is also defined in LogOrigin.h (which //# is included in auto-template mode). //# So undefine WHERE first. #undef WHERE #include //# Let clang and gcc ignore some warnings in bison/flex code. //# Undef YY_NULL because flex can redefine it slightly differently (giving a warning). #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpragmas" #pragma GCC diagnostic ignored "-Wdeprecated-register" #pragma GCC diagnostic ignored "-Wsign-compare" #include "MSScanGram.ycc" // bison output #ifdef YY_NULL # undef YY_NULL #endif #include "MSScanGram.lcc" // flex output #pragma GCC diagnostic pop // Define the yywrap function for flex. int MSScanGramwrap() { return 1; } namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Declare a file global pointer to a char* for the input string. static const char* strpMSScanGram = 0; static Int posMSScanGram = 0; //# Parse the command. //# Do a yyrestart(yyin) first to make the flex scanner reentrant. TableExprNode baseMSScanGramParseCommand (MSScanParse* parser, const String& command, Vector& selectedIDs, Int maxScans) { try { MSScanGramrestart (MSScanGramin); yy_start = 1; strpMSScanGram = command.chars(); // get pointer to command string posMSScanGram = 0; // initialize string position //MSScanParse parser(ms); // setup measurement set MSScanParse::thisMSSParser = parser; // The global pointer to the parser parser->reset(); parser->setMaxScan(maxScans); MSScanGramparse(); // parse command string selectedIDs=parser->selectedIDs(); return parser->node(); } catch (MSSelectionScanError &x) { String newMesgs; newMesgs = constructMessage(msScanGramPosition(), command); x.addMessage(newMesgs); throw; } } TableExprNode msScanGramParseCommand (const MeasurementSet* ms, const String& command, Vector& selectedIDs, Int maxScans) { TableExprNode ret; MSScanParse *thisParser = new MSScanParse(ms); try { ret = baseMSScanGramParseCommand(thisParser, command, selectedIDs, maxScans); } catch (MSSelectionScanError &x) { delete thisParser; throw; } delete thisParser; return ret; } TableExprNode msScanGramParseCommand (const MeasurementSet* ms, const TableExprNode& colAsTEN, const String& command, Vector& selectedIDs, Int maxScans) { TableExprNode ret; MSScanParse *thisParser = new MSScanParse(ms,colAsTEN); try { ret = baseMSScanGramParseCommand(thisParser, command, selectedIDs, maxScans); } catch (MSSelectionScanError &x) { delete thisParser; throw; } delete thisParser; return ret; } //# Give the table expression node // const TableExprNode* msScanGramParseNode() // { // // return MSScanParse::node(); // return &MSScanParse::thisMSSParser->node(); // } void msScanGramParseDeleteNode() { // return MSScanParse::cleanup(); return MSScanParse::thisMSSParser->cleanup(); } //# Give the string position. Int& msScanGramPosition() { return posMSScanGram; } //# Get the next input characters for flex. int msScanGramInput (char* buf, int max_size) { int nr=0; while (*strpMSScanGram != 0) { if (nr >= max_size) { break; // get max. max_size char. } buf[nr++] = *strpMSScanGram++; } return nr; } void MSScanGramerror (const char*) { throw (MSSelectionScanError ("Scan Expression: Parse error at or near '" + String(MSScanGramtext) + "'")); } // String msScanGramRemoveEscapes (const String& in) // { // String out; // int leng = in.length(); // for (int i=0; i #include #include // routines used by bison actions namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MeasurementSet; class TableExprNode; // // Global functions for flex/bison scanner/parser for MSScanGram // // // // // //# Classes you should understand before using this one. //
      • MSScanGram.l and .y (flex and bison grammar) // // // Global functions are needed to define the input of the flex scanner // and to start the bison parser. // The input is taken from a string. // // // It is necessary to be able to give an image expression in ASCII. // This can be used in glish. // // //# A List of bugs, limitations, extensions or planned refinements. // // // Declare the bison parser (is implemented by bison command). TableExprNode baseMSScanGramParseCommand (MSScanParse* parser, const String& command, Vector& idList, Int maxScans=1000); TableExprNode msScanGramParseCommand (const MeasurementSet *ms, const String& command, Vector& idList, Int maxScans=1000); TableExprNode msScanGramParseCommand (const MeasurementSet* ms, const TableExprNode& colAsTEN, const String& command, Vector& selectedIDs, Int maxScans) ; // The yyerror function for the parser. // It throws an exception with the current token. void MSScanGramerror (const char*); // Give the table expression node. //const TableExprNode *msScanGramParseNode(); void msScanGramParseDeleteNode(); // Give the current position in the string. // This can be used when parse errors occur. Int& msScanGramPosition(); // Declare the input routine for flex/bison. int msScanGramInput (char* buf, int max_size); // A function to remove escaped characters. //String msScanGramRemoveEscapes (const String& in); // A function to remove quotes from a quoted string. //String msScanGramRemoveQuotes (const String& in); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSScanGram.ll000066400000000000000000000055071476623553700172210ustar00rootroot00000000000000/* -*- C -*- MSScanGram.l: Lexical analyzer for ms selection commands Copyright (C) 1994,1995,1996,1997,1998,2001,2003 Associated Universities, Inc. Washington DC, USA. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. Correspondence concerning AIPS++ should be addressed as follows: Internet email: casa-feedback@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA */ /* yy_unput is not used, so let flex not generate it, otherwise picky compilers will issue warnings. */ %option nounput %{ #undef YY_INPUT #define YY_INPUT(buf,result,max_size) result=msScanGramInput(buf,max_size) #undef YY_DECL #define YY_DECL int MSScanGramlex (YYSTYPE* lvalp) static string qstr; #include %} WHITE [ \t\n]* DIGIT [0-9] INT ({WHITE}{DIGIT}+{WHITE}) /* rules */ %% {INT} { msScanGramPosition() += yyleng; lvalp->str = (char *)malloc((strlen(MSScanGramtext) + 1) * sizeof(char)); strcpy(lvalp->str, stripWhite(MSScanGramtext).c_str()); // cout << "INT = \"" << MSScanGramtext << "\" \"" << lvalp->str << "\"" << endl; return INT; } "~" { msScanGramPosition() += yyleng; return DASH; } "," { msScanGramPosition() += yyleng; return COMMA; } "<" { msScanGramPosition() += yyleng; return LT; } ">" { msScanGramPosition() += yyleng; return GT; } "<=" { msScanGramPosition() += yyleng; return LE; } ">=" { msScanGramPosition() += yyleng; return GE; } "&" { msScanGramPosition() += yyleng; return AMPERSAND; } /* Literals */ "(" { msScanGramPosition() += yyleng; return LPAREN; } ")" { msScanGramPosition() += yyleng; return RPAREN;} {WHITE} { msScanGramPosition() += yyleng;} /* Eat white spaces */ . { msScanGramPosition() += yyleng;return MSScanGramtext[0];} %% casacore-3.7.1/ms/MSSel/MSScanGram.yy000066400000000000000000000103371476623553700172500ustar00rootroot00000000000000/* -*- C++ -*- MSScanGram.y: Parser for scan expressions Copyright (C) 2004 Associated Universities, Inc. Washington DC, USA. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. Correspondence concerning AIPS++ should be addressed as follows: Internet email: casa-feedback@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA */ %{ #include using namespace casacore; %} %pure-parser /* make parser re-entrant */ %union { const TableExprNode* node; Block* exprb; TableExprNodeSetElem* elem; TableExprNodeSet* settp; Int ival[2]; char * str; Double dval; std::vector* iv; // std::vectors have push_back, insert, etc. Vector* is; } %token EQASS %token SQUOTE %token IDENTIFIER %token COMMA %token LBRACKET %token LPAREN %token RBRACKET %token RPAREN %token LBRACE %token RBRACE %token WHITE %token INT %token QSTRING %token REGEX %token COLON %token SEMICOLON %type scanstatement %type compoundexpr %type scanboundsexpr %type scanidbounds %type scanids %nonassoc EQ EQASS GT GE LT LE NE COMMA DASH AMPERSAND %{ int MSScanGramlex (YYSTYPE*); %} %% scanstatement: compoundexpr {$$ = MSScanParse::thisMSSParser->selectScanIds();} ; // Here, for ID-list expressions (INT and INT DASH INT), we only // collect the list of IDs generated (accumulated internally in // MSScanPrase). The accumulated IDs are used for selection in the // terminal node above. Bounds expressions are however used for // selection as they are parsed. compoundexpr: scanids {/*$$ = &MSScanParse::thisMSSParser->node();*/} | scanboundsexpr {$$=$1;} | compoundexpr COMMA scanids {$$=$1;} | compoundexpr COMMA scanboundsexpr {$$=$1;} ; scanidbounds: LT INT // idv(1,atoi($2)); $$ = MSScanParse::thisMSSParser->selectScanIdsLT(idv); free($2); } | GT INT // >ID { const Vector idv(1,atoi($2)); $$ = MSScanParse::thisMSSParser->selectScanIdsGT(idv); free($2); } | LE INT // <=ID { const Vector idv(1,atoi($2)); $$ = MSScanParse::thisMSSParser->selectScanIdsLTEQ(idv); free($2); } | GE INT // >=ID { const Vector idv(1,atoi($2)); $$ = MSScanParse::thisMSSParser->selectScanIdsGTEQ(idv); free($2); } | GE INT AMPERSAND LE INT // >=ID & <=ID { Int n0=atoi($2), n1=atoi($5); $$ = MSScanParse::thisMSSParser->selectRangeGEAndLE(n0,n1); free($2); free($5); } | GT INT AMPERSAND LT INT // >ID & selectRangeGTAndLT(n0,n1); free($2); free($5); } ; scanboundsexpr: scanidbounds {$$=$1;} // // Build a list of scan IDs. This can be a single ID or a range of // IDs converted to a list. Actual selection is done at the end of // parsing cycle (at the terminal node above). // scanids: INT { $$=&MSScanParse::thisMSSParser->accumulateIDs(atoi($1)); free($1); } | INT DASH INT { $$=&MSScanParse::thisMSSParser->accumulateIDs(atoi($1),atoi($3)); free($1); free($3); } ; %% casacore-3.7.1/ms/MSSel/MSScanParse.cc000066400000000000000000000160231476623553700173560ustar00rootroot00000000000000//# MSScanParse.cc: Classes to hold results from scan grammar parser //# Copyright (C) 1994,1995,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSScanParse* MSScanParse::thisMSSParser = 0x0; // Global pointer to the parser object TableExprNode MSScanParse::columnAsTEN_p; // TableExprNode* MSScanParse::node_p = 0x0; // Vector MSScanParse::idList; // std::vector MSScanParse::parsedIDList_p; //# Constructor MSScanParse::MSScanParse () : MSParse(), colName(MS::columnName(MS::SCAN_NUMBER)), // maxScans_p(std::numeric_limits::max()) maxScans_p(1000) { } std::vector& MSScanParse::accumulateIDs(const Int id0, const Int id1) { Vector theIDs; if (id1 < 0) { parsedIDList_p.push_back(id0);theIDs.resize(1);theIDs[0]=id0; // Also accumulate IDs in the global ID list which contains IDs // generated from all expressions (INT, INT DASH INT, and bounds // expressions (>ID, ::max()) maxScans_p(1000) { idList.resize(0); parsedIDList_p.resize(0); } //# Constructor with given ms and main-column of interest supplied //# as a TEN. This allows making the parser generic to work for //# both MS and CalTables. MSScanParse::MSScanParse (const MeasurementSet* ms, const TableExprNode& colAsTEN) : MSParse(ms, "Scan"), colName(MS::columnName(MS::SCAN_NUMBER)), // maxScans_p(std::numeric_limits::max()) maxScans_p(1000) { idList.resize(0); parsedIDList_p.resize(0); columnAsTEN_p = colAsTEN; } void MSScanParse::appendToIDList(const Vector& v) { Int currentSize = idList.nelements(); Int n = v.nelements() + currentSize; Int j=0; idList.resize(n, True); for(Int i=currentSize;icol(colName) > n0) && // (ms()->col(colName) < n1)); TableExprNode condition = TableExprNode( (columnAsTEN_p > n0) && (columnAsTEN_p < n1)); if ((n0 < 0) || (n1 < 0) || (n1 <= n0)) { ostringstream os; os << "Scan Expression: Malformed range bounds " << n0 << " (lower bound) and " << n1 << " (upper bound)"; throw(MSSelectionScanParseError(os.str())); } Vector tmp(n1-n0-1); Int j=n0+1; for(uInt i=0;i= n0) && (columnAsTEN_p <= n1)); if ((n0 < 0) || (n1 < 0) || (n1 <= n0)) { ostringstream os; os << "Scan Expression: Malformed range bounds " << n0 << " (lower bound) and " << n1 << " (upper bound)"; throw(MSSelectionScanParseError(os.str())); } Vector tmp(n1-n0+1); Int j=n0; for(uInt i=0;i& scanids) { if (scanids.size() > 0) { // cerr << "Selecting disjoint list: " << scanids << endl; TableExprNode condition = TableExprNode(columnAsTEN_p.in(scanids)); appendToIDList(scanids); addCondition(node_p,condition); } return &node_p; } const TableExprNode* MSScanParse::selectScanIdsGT(const Vector& scanids) { //TableExprNode condition = TableExprNode(ms()->col(colName) > scanids[0]); TableExprNode condition = TableExprNode(columnAsTEN_p > scanids[0]); Int n=maxScans_p-scanids[0]+1,j; Vector tmp(n); j=scanids[0]+1; for(Int i=0;i& scanids) { //TableExprNode condition = TableExprNode(ms()->col(colName) < scanids[0]); TableExprNode condition = TableExprNode(columnAsTEN_p < scanids[0]); Vector tmp(scanids[0]); for(Int i=0;i& scanids) { //TableExprNode condition = TableExprNode(ms()->col(colName) >= scanids[0]); TableExprNode condition = TableExprNode(columnAsTEN_p >= scanids[0]); Int n=maxScans_p-scanids[0]+1,j; Vector tmp(n); j=scanids[0]; for(Int i=0;i& scanids) { //TableExprNode condition = TableExprNode(ms()->col(colName) <= scanids[0]); TableExprNode condition = TableExprNode(columnAsTEN_p <= scanids[0]); Vector tmp(scanids[0]+1); for(Int i=0;i<=scanids[0];i++) tmp[i] = i; appendToIDList(tmp); addCondition(node_p,condition); return &node_p; } const TableExprNode MSScanParse::node() { return node_p; } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSSel/MSScanParse.h000066400000000000000000000107751476623553700172300ustar00rootroot00000000000000//# MSScanParse.h: Classes to hold results from scan grammar parser //# Copyright (C) 1994,1995,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSCANPARSE_H #define MS_MSSCANPARSE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // Class to hold values from scan grammar parser // // // // // //# Classes you should understand before using this one. // // // MSScanParse is the class used to parse a scan command. // // // MSScanParse is used by the parser of scan sub-expression statements. // The parser is written in Bison and Flex in files MSScanGram.y and .l. // The statements in there use the routines in this file to act // upon a reduced rule. // Since multiple tables can be given (with a shorthand), the table // names are stored in a list. The variable names can be qualified // by the table name and will be looked up in the appropriate table. // // The class MSScanParse only contains information about a table // used in the table command. Global variables (like a list and a vector) // are used in MSScanParse.cc to hold further information. // // Global functions are used to operate on the information. // The main function is the global function msScanCommand. // It executes the given STaQL command and returns the resulting ms. // This is, in fact, the only function to be used by a user. // // // It is necessary to be able to give a ms command in ASCII. // This can be used in a CLI or in the table browser to get a subset // of a table or to sort a table. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class MSScanParse : public MSParse { public: // Default constructor MSScanParse (); // Associate the ms and the shorthand. MSScanParse (const MeasurementSet* ms); MSScanParse (const MeasurementSet* ms, const TableExprNode& colAsTEN); ~MSScanParse() {columnAsTEN_p=TableExprNode();} const TableExprNode *selectRangeGTAndLT(const Int& n0, const Int& n1); const TableExprNode *selectRangeGEAndLE(const Int& n0, const Int& n1); const TableExprNode *selectScanIds(const Vector& scanids); inline const TableExprNode *selectScanIds() {return selectScanIds(Vector(parsedIDList_p));} const TableExprNode *selectScanIdsGT(const Vector& scanids); const TableExprNode *selectScanIdsLT(const Vector& scanids); const TableExprNode *selectScanIdsGTEQ(const Vector& scanids); const TableExprNode *selectScanIdsLTEQ(const Vector& scanids); std::vector& accumulateIDs(const Int id0, const Int id1=-1); // Get table expression node object. const TableExprNode node(); Vector selectedIDs() {return idList;} void reset(){idList.resize(0);parsedIDList_p.resize(0);} void cleanup() {} void setMaxScan(const Int& n) {maxScans_p=n;} static MSScanParse* thisMSSParser; private: TableExprNode node_p; Vector idList; std::vector parsedIDList_p; const String colName; void appendToIDList(const Vector& v); Int maxScans_p; static TableExprNode columnAsTEN_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSSelUtil.h000066400000000000000000000047271476623553700167320ustar00rootroot00000000000000//# MSSelUtil.h: this defines MSSelUtil, a helper class for MSSelector //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSELUTIL_H #define MS_MSSELUTIL_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Helper class for MSFlagger with templated static function // // // // Helper class for MSFlagger/DOms with templated static function to difference // data in one of two directions. // template class MSSelUtil { public: // Compute the absolute difference of the data, subtracting // either the previous value (window==2) or the average over // the window (window>2). If doMedian==True is specified, the // median difference over the window is returned for window>2. // Takes flagging into account. // diffAxis==2,3: row or time, diffAxis==1: channel // Handles 3d and 4d data arrays. static Array diffData(const Array& data, const Array& flag, const Array& flagRow, Int diffAxis, Int window, Bool doMedian=False); }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/ms/MSSel/MSSelUtil.tcc000066400000000000000000000115301476623553700172420ustar00rootroot00000000000000//# MSSelUtil.cc: templated helper function for MSSelector //# Copyright (C) 1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSELUTIL_TCC #define MS_MSSELUTIL_TCC #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template Array MSSelUtil::diffData(const Array& data, const Array& flag, const Array& flagRow, Int diffAxis, Int window, Bool doMedian) { IPosition shape=data.shape(); Array diff(shape); diff.set(0); const Int nCorr=shape(0); const Int nChan=shape(1); const Int nXY=nCorr*nChan; Int nTime=shape(2), nIfr=1; if (data.ndim()==4) { nIfr=shape(2); nTime=shape(3); } const Int nOff=nXY*nIfr; const Int win=max(2,window); Bool deleteData, deleteFlag, deleteFlagRow, deleteDiff; const T* pdata = data.getStorage(deleteData); const Bool* pflag = flag.getStorage(deleteFlag); const Bool* pflagRow = flagRow.getStorage(deleteFlagRow); Float* pdiff = diff.getStorage(deleteDiff); T zero(0.), sum; Block buf(win); // diffAxis == 1: channel, 2: row, 3: time if (diffAxis!=1) { // do row or time difference Int offset=0, rowOffset=0; for (Int i=0; i0 && !pflag[offset-nOff]) { pdiff[offset]=abs(pdata[offset]-pdata[offset-nOff]); } } else if (!doMedian) { Int count=0; sum=zero; for (Int k=st, koff=offset+(st-i)*nOff; k1) sum/=count; if (count>0) pdiff[offset]=abs(pdata[offset]-sum); } else { // use median Int count=0; for (Int k=st, koff=offset+(st-i)*nOff; k0) { pdiff[offset]=median(Vector(buf.begin(),buf.begin()+count)); } } } offset++; } } else { offset+=nXY; } } } } else { // do channel difference Int offset=0, rowOffset=0; for (Int i=0; i0 && !pflag[offset-nCorr]) { pdiff[offset]=abs(pdata[offset]-pdata[offset-nCorr]); } } else if (!doMedian) { Int count=0; sum=zero; for (Int k=st, koff=offset+(st-j)*nCorr; k1) sum/=count; if (count>0) pdiff[offset]=abs(pdata[offset]-sum); } else { // use median Int count=0; for (Int k=st, koff=offset+(st-j)*nCorr; k0) { pdiff[offset]=median(Vector(buf.begin(),buf.begin()+count)); } } } offset++; } } } else { offset+=nXY; } } } } data.freeStorage(pdata,deleteData); flag.freeStorage(pflag,deleteFlag); flagRow.freeStorage(pflagRow,deleteFlagRow); diff.putStorage(pdiff,deleteDiff); return diff; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSSelUtil2.h000066400000000000000000000047201476623553700170050ustar00rootroot00000000000000//# MSSelUtil2.h: templated helper function for MSSelector //# Copyright (C) 1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSELUTIL2_H #define MS_MSSELUTIL2_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template class MSSelUtil2 { public: // reorder data from 3d (corr,chan,row) to 4d (corr,chan,ifr,time) static void reorderData(Array& data, const Vector& ifrSlot, Int nIfr, const Vector& timeSlot, Int nTime, const T& defvalue); // reorder data from 4d (corr,chan,ifr,time) to 3d (corr,chan,row) static void reorderData(Array& data, const Matrix& rowIndex, Int64 nRow); // average data (with flags & weights applied) over it's last axis (time or // row), return in data (overwritten), dataFlag gives new flags. static void timeAverage(Array& dataFlag, Array& data, const Array& flag, const Array& weight); }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/ms/MSSel/MSSelUtil2.tcc000066400000000000000000000121501476623553700173230ustar00rootroot00000000000000//# MSSelUtil2.cc: templated helper function for MSSelector //# Copyright (C) 1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSELUTIL2_TCC #define MS_MSSELUTIL2_TCC #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // reorder from 3d to 4d (adding ifr axis) template void MSSelUtil2::reorderData(Array& data, const Vector& ifrSlot, Int nIfr, const Vector& timeSlot, Int nTime, const T& defvalue) { Int nPol=data.shape()(0); Int nChan=data.shape()(1); Int64 nRow=data.shape()(2); Array data2(IPosition(4,nPol,nChan,nIfr,nTime)); data2.set(defvalue); Bool deleteData,deleteData2; const T* pdata=data.getStorage(deleteData); T* pdata2=data2.getStorage(deleteData2); Int n=nPol*nChan; for (Int64 i=0; i void MSSelUtil2::reorderData(Array& data, const Matrix& rowIndex, Int64 nRow) { Int nPol=data.shape()(0),nChan=data.shape()(1),nIfr=data.shape()(2), nTime=data.shape()(3); if (nIfr!=rowIndex.shape()(0) || nTime!=rowIndex.shape()(1)) { // os<< LogIO::SEVERE << "Data array shape does not match current selection" // << LogIO::POST; return; } Array data2(IPosition(3,nPol,nChan,nRow)); Bool deleteData,deleteData2; const T* pData=data.getStorage(deleteData); T* pData2=data2.getStorage(deleteData2); Int n=nPol*nChan; for (Int i=0; i=0) { Int64 start2=k*n, start1=(j+i*nIfr)*n; for (Int l=0; l void MSSelUtil2::timeAverage(Array& dataFlag, Array& data, const Array& flag, const Array& weight) { Bool delData,delFlag,delWeight; const T* pdata=data.getStorage(delData); const Bool* pflag=flag.getStorage(delFlag); const Float* pweight=weight.getStorage(delWeight); Int nPol=data.shape()(0),nChan=data.shape()(1); Int nIfr=1, nTime=data.shape()(2); Array out; if (data.ndim()==4) { nIfr=nTime; nTime=data.shape()(3); out.resize(IPosition(3,nPol,nChan,nIfr)); } else { out.resize(IPosition(2,nPol,nChan)); } Array wt(IPosition(3,nPol,nChan,nIfr)); dataFlag.resize(IPosition(3,nPol,nChan,nIfr)); dataFlag.set(True); Bool delDataflag, delWt, delOut; Float* pwt=wt.getStorage(delWt); T* pout=out.getStorage(delOut); Bool* pdflags=dataFlag.getStorage(delDataflag); out=0; wt=0; Int offset=0,off1=0,offw=0; for (Int l=0; l0) pout[k]/=pwt[k]; } data.freeStorage(pdata,delData); flag.freeStorage(pflag,delFlag); weight.freeStorage(pweight,delWeight); dataFlag.putStorage(pdflags,delDataflag); wt.putStorage(pwt,delWt); out.putStorage(pout,delOut); data.reference(out); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSSelectableMainColumn.h000066400000000000000000000071351476623553700213730ustar00rootroot00000000000000// -*- C++ -*- //# MSSelectableMainColumn.h: The generic interface for tables that can be used with MSSelection //# Copyright (C) 1996,1997,1998,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSELECTABLEMAINCOLUMN_H #define MS_MSSELECTABLEMAINCOLUMN_H #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class MSSelectableMainColumn { public: MSSelectableMainColumn(const Table& msLikeTable) {init(msLikeTable);} MSSelectableMainColumn() {table_p=NULL;} virtual ~MSSelectableMainColumn() {} virtual void init(const Table& msLikeTable) {table_p=&msLikeTable;} const Table* table() {return table_p;} virtual const ArrayColumn& flag() = 0; virtual Bool flagRow(rownr_t i) = 0; virtual const ScalarQuantColumn& exposureQuant() = 0; virtual const ScalarQuantColumn& timeQuant() = 0; virtual const MeasurementSet *asMS() = 0; protected: const Table *table_p; }; class MSMainColInterface: public MSSelectableMainColumn { public: MSMainColInterface():MSSelectableMainColumn(), msCols_p(NULL) {} MSMainColInterface(const Table& msAsTable): MSSelectableMainColumn(msAsTable) {init(msAsTable);} virtual ~MSMainColInterface() {if (msCols_p) delete msCols_p;} virtual void init(const Table& msAsTable) {MSSelectableMainColumn::init(msAsTable);ms_p = MeasurementSet(msAsTable); msCols_p=new MSMainColumns(ms_p);} virtual const ArrayColumn& flag() {return msCols_p->flag();} // virtual Bool flagRow(const Int& i) {return allTrue(msCols_p->flag()(i));} virtual Bool flagRow(rownr_t i) {return msCols_p->flagRow()(i);} virtual const ScalarQuantColumn& exposureQuant() {return msCols_p->exposureQuant();} virtual const ScalarQuantColumn& timeQuant() {return msCols_p->timeQuant();} virtual const MeasurementSet *asMS(){return static_cast(table());} private: MeasurementSet ms_p; MSMainColumns *msCols_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSSelectableTable.cc000066400000000000000000000033351476623553700205140ustar00rootroot00000000000000// -*- C++ -*- //# MSSelectableTable.cc: Implementation of the the generic MSSeletableTable interface //# Copyright (C) 1994,1995,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSInterface::MSInterface(const Table& table) :MSSelectableTable(table), msMainCols_p(NULL) {} // MSInterface::MSInterface(const MeasurementSet& ms) // :MSSelectableTable(ms) // {} } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSSel/MSSelectableTable.h000066400000000000000000000161271476623553700203610ustar00rootroot00000000000000// -*- C++ -*- //# MSSelectableTable.h: The generic interface for tables that can be used with MSSelection //# Copyright (C) 1996,1997,1998,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSELECTABLETABLE_H #define MS_MSSELECTABLETABLE_H #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // // MSSelectableTable: An interface class used by MSSelection module to // access the sub-tables and main-table columns of MS-like tables. // // // // // // // // // From "msselection" and "table". // // // // // This is a pure virtual base-class to provide a table-type agnostic // interface to the MSSelection module to // access sub-tables and main-table columns of MS-like tables. // // // // // // // // // // // To allow use of the MSSelection module // for selection on any table that follows the general structure of the // MS database. Via this class, minor differences in the database // layout can be hidden from the MSSelection module. This also keeps // MeasurementSet module from depending on other MS-like database // implemention which may use the MSSelection module. Such usage will // need to implement a specialization of MSSelectableTable and // use it to instantiate the MSSelection object. // // // // // class MSSelectableTable { public: enum MSSDataType {BASELINE_BASED=0, PURE_ANTENNA_BASED, REF_ANTENNA_BASED}; MSSelectableTable() {} MSSelectableTable(const Table& table) {table_p = &table;} virtual ~MSSelectableTable() {} virtual void setTable(const Table& table) {table_p = &table;} const Table* table() {return table_p;} TableExprNode col(const String& colName) {return table()->col(colName);} virtual Bool isMS() = 0; virtual MSSDataType dataType() = 0; virtual const MSAntenna& antenna() = 0; virtual const MSField& field() = 0; virtual const MSSpectralWindow& spectralWindow() = 0; virtual const MSDataDescription& dataDescription() = 0; virtual const MSObservation& observation() = 0; virtual String columnName(MSMainEnums::PredefinedColumns nameEnum) = 0; virtual const MeasurementSet* asMS() = 0; virtual MSSelectableMainColumn* mainColumns() = 0; protected: const Table *table_p; }; // // // MSInterface: A specialization of MSSelectableTable for accessing // MS. // // // // // // // // // // From "ms" and "interface". // // // // // // A class that can be passed around as MSSelectableTable, with most of // the methods overloaded to work with the underlaying MS. // // // // // // // // // // Fill in the expression in the various strings that are passed for // // parsing to the MSSelection object later. // // // String fieldStr,timeStr,spwStr,baselineStr, // uvdistStr,taqlStr,scanStr,arrayStr, polnStr,stateObsModeStr, // observationStr; // baselineStr="1&2"; // timeStr="*+0:10:0"; // fieldStr="CygA*"; // // // // Instantiate the MS and the MSInterface objects. // // // MS ms(MSName),selectedMS(ms); // MSInterface msInterface(ms); // // // // Setup the MSSelection thingi // // // MSSelection msSelection; // // msSelection.reset(msInterface,MSSelection::PARSE_NOW, // timeStr,baselineStr,fieldStr,spwStr, // uvdistStr,taqlStr,polnStr,scanStr,arrayStr, // stateObsModeStr,observationStr); // if (msSelection.getSelectedMS(selectedMS)) // cerr << "Got the selected MS!" << endl; // else // cerr << "The set of expressions resulted into null-selection"; // // // // // // To generalize the implementation of the MSSelection parsers. // // // // // class MSInterface: public MSSelectableTable { public: MSInterface():msMainCols_p(NULL) {} MSInterface(const Table& table); virtual ~MSInterface() {if (msMainCols_p) delete msMainCols_p;} virtual const MSAntenna& antenna() {return asMS()->antenna();} virtual const MSField& field() {return asMS()->field();} virtual const MSSpectralWindow& spectralWindow() {return asMS()->spectralWindow();} virtual const MSDataDescription& dataDescription() {return asMS()->dataDescription();} virtual const MSObservation& observation() {return asMS()->observation();} virtual String columnName(MSMainEnums::PredefinedColumns nameEnum) {return MS::columnName(nameEnum);} virtual Bool isMS() {return True;} virtual MSSDataType dataType() {return MSSelectableTable::BASELINE_BASED;} virtual const MeasurementSet *asMS(){return static_cast(table());} virtual MSSelectableMainColumn* mainColumns() {msMainCols_p = new MSMainColInterface(*table_p); return msMainCols_p;} private: MSMainColInterface *msMainCols_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSSelection.cc000066400000000000000000001370711476623553700174330ustar00rootroot00000000000000//# MSSelection.cc: Implementation of MSSelection.h //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //---------------------------------------------------------------------------- #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //---------------------------------------------------------------------------- MSSelection::MSSelection() : fullTEN_p(),ms_p(NULL), antennaExpr_p(""), fieldExpr_p(""), spwExpr_p(""), scanExpr_p(""), arrayExpr_p(""), timeExpr_p(""), uvDistExpr_p(""), polnExpr_p(""), taqlExpr_p(""), stateExpr_p(""), observationExpr_p(""), feedExpr_p(""), exprOrder_p(MAX_EXPR, Int(NO_EXPR)), antenna1IDs_p(), antenna2IDs_p(), fieldIDs_p(), spwIDs_p(), scanIDs_p(), arrayIDs_p(), ddIDs_p(), observationIDs_p(), feed1IDs_p(), feed2IDs_p(), baselineIDs_p(), feedPairIDs_p(), selectedTimesList_p(), selectedUVRange_p(),selectedUVUnits_p(), maxScans_p(1000), maxObs_p(1000), maxArray_p(1000), isMS_p(True), toTENCalled_p(False) { clear(); // Clear the internals of the MSSelection object clearErrorHandlers(); // Clear the static error handlers // Install the default error handlers initErrorHandler(ANTENNA_EXPR); initErrorHandler(STATE_EXPR); initErrorHandler(SPW_EXPR); initErrorHandler(FEED_EXPR); } //---------------------------------------------------------------------------- MSSelection::MSSelection(const MeasurementSet& ms, const MSSMode& mode, const String& timeExpr, const String& antennaExpr, const String& fieldExpr, const String& spwExpr, const String& uvDistExpr, const String& taqlExpr, const String& polnExpr, const String& scanExpr, const String& arrayExpr, const String& stateExpr, const String& observationExpr, const String& feedExpr): fullTEN_p(), ms_p(&ms), antennaExpr_p(""), fieldExpr_p(""), spwExpr_p(""), scanExpr_p(""), arrayExpr_p(""), timeExpr_p(""), uvDistExpr_p(""), polnExpr_p(""),taqlExpr_p(""), stateExpr_p(""), observationExpr_p(""), feedExpr_p(""), exprOrder_p(MAX_EXPR, Int(NO_EXPR)), antenna1IDs_p(), antenna2IDs_p(), fieldIDs_p(), spwIDs_p(), scanIDs_p(),ddIDs_p(),baselineIDs_p(), feedPairIDs_p(), selectedTimesList_p(), selectedUVRange_p(),selectedUVUnits_p(), maxScans_p(1000), maxObs_p(1000), maxArray_p(1000), isMS_p(True), toTENCalled_p(False) { // // Do not initialize the private string variables directly. Instead // using the setExpr* methods to do that keeps that state of the // object consistent. // clear();// Clear the internals of the MSSelection object clearErrorHandlers();// Clear the static error handlers // Install the default error handlers initErrorHandler(ANTENNA_EXPR); initErrorHandler(STATE_EXPR); initErrorHandler(SPW_EXPR); initErrorHandler(FEED_EXPR); reset2(ms,mode, timeExpr, antennaExpr, fieldExpr, spwExpr, uvDistExpr, taqlExpr, polnExpr, scanExpr, arrayExpr, stateExpr, observationExpr, feedExpr); } //---------------------------------------------------------------------------- void MSSelection::reset(MSSelectableTable& msLike, const MSSMode& mode, const String& timeExpr, const String& antennaExpr, const String& fieldExpr, const String& spwExpr, const String& uvDistExpr, const String& taqlExpr, const String& polnExpr, const String& scanExpr, const String& arrayExpr, const String& stateExpr, const String& observationExpr) { reset2(msLike, mode, timeExpr, antennaExpr, fieldExpr, spwExpr, uvDistExpr, taqlExpr, polnExpr, scanExpr, arrayExpr, stateExpr, observationExpr, ""); } void MSSelection::reset2(MSSelectableTable& msLike, const MSSMode& mode, const String& timeExpr, const String& antennaExpr, const String& fieldExpr, const String& spwExpr, const String& uvDistExpr, const String& taqlExpr, const String& polnExpr, const String& scanExpr, const String& arrayExpr, const String& stateExpr, const String& observationExpr, const String& feedExpr) { ms_p=msLike.asMS(); isMS_p=msLike.isMS(); toTENCalled_p=False; // // Do not initialize the private string variables // directly. Instead using the setExpr* methods to do that so that // it keeps that state of the object consistent. // clear(); // Clear everything setAntennaExpr(antennaExpr); setFieldExpr(fieldExpr); setSpwExpr(spwExpr); setScanExpr(scanExpr); setArrayExpr(arrayExpr); setTimeExpr(timeExpr); setUvDistExpr(uvDistExpr); setPolnExpr(polnExpr); setTaQLExpr(taqlExpr); setStateExpr(stateExpr); setObservationExpr(observationExpr); setFeedExpr(feedExpr); //clearErrorHandlers(); if (mode==PARSE_NOW) fullTEN_p = toTableExprNode(&msLike); } void MSSelection::reset(const MeasurementSet& ms, const MSSMode& mode, const String& timeExpr, const String& antennaExpr, const String& fieldExpr, const String& spwExpr, const String& uvDistExpr, const String& taqlExpr, const String& polnExpr, const String& scanExpr, const String& arrayExpr, const String& stateExpr, const String& observationExpr) { reset2(ms, mode, timeExpr, antennaExpr, fieldExpr, spwExpr, uvDistExpr, taqlExpr, polnExpr, scanExpr, arrayExpr, stateExpr, observationExpr, ""); } void MSSelection::reset2(const MeasurementSet& ms, const MSSMode& mode, const String& timeExpr, const String& antennaExpr, const String& fieldExpr, const String& spwExpr, const String& uvDistExpr, const String& taqlExpr, const String& polnExpr, const String& scanExpr, const String& arrayExpr, const String& stateExpr, const String& observationExpr, const String& feedExpr) { // // Do not initialize the private string variables directly. Instead // using the setExpr* methods to do that keeps that state of the // object consistent. // ms_p = &ms; clear(); // Clear the internals of the MSSelection object setAntennaExpr(antennaExpr); setFieldExpr(fieldExpr); setSpwExpr(spwExpr); setScanExpr(scanExpr); setArrayExpr(arrayExpr); setTimeExpr(timeExpr); setUvDistExpr(uvDistExpr); setPolnExpr(polnExpr); setTaQLExpr(taqlExpr); setStateExpr(stateExpr); setObservationExpr(observationExpr); setFeedExpr(feedExpr); if (mode==PARSE_NOW) fullTEN_p = toTableExprNode(ms_p); } //---------------------------------------------------------------------------- MSSelection::~MSSelection() { // If we created the error handler, we also take it out deleteErrorHandlers(); deleteNodes(); } //---------------------------------------------------------------------------- MSSelection::MSSelection(const Record& selectionItem) : antennaExpr_p(""), fieldExpr_p(""), spwExpr_p(""), scanExpr_p(""), arrayExpr_p(""), timeExpr_p(""), uvDistExpr_p(""), polnExpr_p(""),taqlExpr_p(""),stateExpr_p(""), observationExpr_p(""), feedExpr_p(""), antenna1IDs_p(), antenna2IDs_p(), fieldIDs_p(), spwIDs_p(), ddIDs_p(), feed1IDs_p(), feed2IDs_p(), baselineIDs_p(), feedPairIDs_p() { // Construct from a record representing a selection item // Output to private data: // antennaExpr_p String Antenna STaQL expression // polnExpr_p String Polarization STaQL expression // fieldExpr_p String Field STaQL expression // spwExpr_p String SPW STaQL expression // scanExpr_p String Scan STaQL expression // arrayExpr_p String Array_ID STaQL expression // timeExpr_p String Time STaQL expression // uvDistExpr_p String UV Distribution STaQL expression // observation_p String Observation ID STaQL expression // taqlExpr_p String TaQL expression // // Extract fields from the selection item record fromSelectionItem(selectionItem); } //---------------------------------------------------------------------------- MSSelection::MSSelection (const MSSelection& other) { // Copy constructor // Input: // other const MSSelection& Existing MSSelection object // Output to private data: // if(this != &other) { this->antennaExpr_p = other.antennaExpr_p; this->fieldExpr_p = other.fieldExpr_p; this->spwExpr_p = other.spwExpr_p; this->scanExpr_p = other.scanExpr_p; this->observationExpr_p = other.observationExpr_p; this->arrayExpr_p = other.arrayExpr_p; this->timeExpr_p = other.timeExpr_p; this->uvDistExpr_p = other.uvDistExpr_p; this->taqlExpr_p = other.taqlExpr_p; this->polnExpr_p = other.polnExpr_p; this->stateExpr_p = other.stateExpr_p; this->feedExpr_p = other.feedExpr_p; this->exprOrder_p = other.exprOrder_p; } } //---------------------------------------------------------------------------- MSSelection& MSSelection::operator= (const MSSelection& other) { // Assignment operator // Input: // other const MSSelection& RHS MSSelection object // Output to private data: // if(this != &other) { this->antennaExpr_p = other.antennaExpr_p; this->fieldExpr_p = other.fieldExpr_p; this->spwExpr_p = other.spwExpr_p; this->scanExpr_p = other.scanExpr_p; this->observationExpr_p = other.observationExpr_p; this->arrayExpr_p = other.arrayExpr_p; this->timeExpr_p = other.timeExpr_p; this->uvDistExpr_p = other.uvDistExpr_p; this->taqlExpr_p = other.taqlExpr_p; this->polnExpr_p = other.polnExpr_p; this->stateExpr_p = other.stateExpr_p; this->feedExpr_p = other.feedExpr_p; this->exprOrder_p = other.exprOrder_p; this->isMS_p = other.isMS_p; } return *this; } //---------------------------------------------------------------------------- TableExprNode MSSelection::getTEN(const MeasurementSet*ms) { // if (ms!=NULL) {resetTEN();toTableExprNode(ms);} // else if (ms_p==NULL) throw(MSSelectionError("MSSelection::getTEN() called without setting the MS")); // else toTableExprNode(ms_p); if (isMS_p==False) { if (toTENCalled_p==True) return fullTEN_p; else throw(MSSelectionError("MSSelection::getTEN() called before calling MSSelection::toTableExprNode()")); } if (ms==NULL) if (ms_p==NULL) throw(MSSelectionError("MSSelection::getTEN() called without setting the MS")); else toTableExprNode(ms_p); else {resetTEN();toTableExprNode(ms);} return fullTEN_p; } //---------------------------------------------------------------------------- String MSSelection::indexExprStr(Vector index) { String expression; for(uInt i=0; i name) { String expression; // SDJ Removed the placement of quotes around field names. This seems // to be invalid now (Nov. 2006). //expression = "'"; for(uInt i=0; iasMS(); isMS_p=msLike->isMS(); // // When msLike is CTInterface, msLike->asMS() will return NULL. // In this case, *ms should also not be used anyway. // // When msLike is MSInterface, msLike->asMS() must return a usable // *ms. // if (msLike->isMS() && (ms==NULL)) throw(MSSelectionError(String("MSSelection::toTableExprNode(MSSelectableTable*): " "MS pointer from MS-Like object is null"))); if (!msLike->isMS() && ( // (fieldExpr_p != "") || // (antennaExpr_p != "") || // (timeExpr_p != "") || // Will be opened-up for CalTables in the future // (spwExpr_p != "") || // Will be opened-up for CalTables in the future //(scanExpr_p != "") || //(observationExpr_p != "") || (arrayExpr_p != "") || (uvDistExpr_p != "") || //(taqlExpr_p != "") || (polnExpr_p != "") || (stateExpr_p != "") || (feedExpr_p != "") )) throw(MSSelectionError(String("MSSelection::toTableExprNode(MSSelectableTable*): " "Only field-, spw-, scan-, time- and antenna-selection is supported for CalTables"))); return ms; } // //---------------------------------------------------------------------------- // Method to install the defaul error handler or reset existing // error handlers. If the pointer to error handler is NULL, this // method will install the default error handler. If the pointer is // != NULL, this method will reset the existing error handler. // // The operations in this method requires modifying the error // handler pointer in various parsers (which are in C, not C++). // This dual-purpose design of this method (reset or install error // handlers) is to keep the handling of error-handler pointers // localized to this method only. // void MSSelection::initErrorHandler(const MSExprType type) { switch (type) { case ANTENNA_EXPR: { if (! MSAntennaParse::thisMSAErrorHandler) { MSSelectionErrorHandler tt; setErrorHandler(ANTENNA_EXPR, &tt, True); } else MSAntennaParse::thisMSAErrorHandler->reset(); break; } case FEED_EXPR: { if (! MSFeedParse::thisMSFErrorHandler) { MSSelectionErrorHandler tt; setErrorHandler(FEED_EXPR, &tt, True); } else MSFeedParse::thisMSFErrorHandler->reset(); break; } case STATE_EXPR: { if (! MSStateParse::thisMSSErrorHandler) { MSSelectionErrorHandler tt; setErrorHandler(STATE_EXPR, &tt, True); } else MSStateParse::thisMSSErrorHandler->reset(); break; } case SPW_EXPR: { if (! MSSpwParse::thisMSSpwErrorHandler) { MSSSpwErrorHandler tt; setErrorHandler(SPW_EXPR, &tt, True /*overRide*/); } else MSSpwParse::thisMSSpwErrorHandler->reset(); break; } default: throw(MSSelectionError(String("Wrong MSExprType in MSSelection::initErrorHandler()"))); break; }; } //---------------------------------------------------------------------------- TableExprNode MSSelection::toTableExprNode(MSSelectableTable* msLike) { // Convert the MS selection to a TableExprNode object, // representing a TaQL selection in C++. // Input: // msLike const MSSelectableTable& MeasurementSet or CalTable // to bind TaQL // Output: // toTableExprNode TableExprNode Table expression node // // Interpret all expressions and produce a consolidated TEN. // if (fullTEN_p.isNull()==False) return fullTEN_p; const MeasurementSet *ms=getMS(msLike); resetMS(*ms); toTENCalled_p=True; // ms_p = msLike->asMS(); TableExprNode condition; // Reset existing error handlers (EH). // // The default EHs are installed in the constructors or via call // to setErrorHandler() by the client code. The following calls // to initErrorHandler() will resets the existing error handler // before initiating the parsing cycle. initErrorHandler(ANTENNA_EXPR); initErrorHandler(STATE_EXPR); initErrorHandler(SPW_EXPR); initErrorHandler(FEED_EXPR); try { for(uInt i=0; icol(msLike->columnName(MS::ANTENNA1)), // col2AsTEN = msLike->col(msLike->columnName(MS::ANTENNA2)); // antenna1IDs_p.resize(0); // antenna2IDs_p.resize(0); // baselineIDs_p.resize(0,2); // node = msAntennaGramParseCommand(msLike->antenna(), // col1AsTEN, col2AsTEN, antennaExpr_p, // antenna1IDs_p, antenna2IDs_p, baselineIDs_p); // } break; } case FIELD_EXPR: { if(fieldExpr_p != "") { fieldIDs_p.resize(0); TableExprNode colAsTEN = msLike->col(msLike->columnName(MS::FIELD_ID)); node = msFieldGramParseCommand(msLike->field(), colAsTEN, fieldExpr_p,fieldIDs_p); } break; } case SPW_EXPR: { if (spwExpr_p != "") { TableExprNode colAsTEN = msLike->col(msLike->columnName(MS::DATA_DESC_ID)); spwIDs_p.resize(0); if (msSpwGramParseCommand(msLike->spectralWindow(), msLike->dataDescription(), colAsTEN, spwExpr_p, spwIDs_p, chanIDs_p,spwDDIDs_p) == 0) node = *(msSpwGramParseNode()); } break; } case SCAN_EXPR: { TableExprNode colAsTEN = msLike->col(msLike->columnName(MS::SCAN_NUMBER)); scanIDs_p.resize(0); if(scanExpr_p != "") node = msScanGramParseCommand(ms, colAsTEN, scanExpr_p, scanIDs_p, maxScans_p); break; } case OBSERVATION_EXPR: { TableExprNode colAsTEN = msLike->col(msLike->columnName(MS::OBSERVATION_ID)); observationIDs_p.resize(0); if(observationExpr_p != "") node = msObservationGramParseCommand(ms, msLike->observation(), colAsTEN, observationExpr_p, observationIDs_p); break; } case ARRAY_EXPR: { arrayIDs_p.resize(0); if(arrayExpr_p != "") node = msArrayGramParseCommand(ms, arrayExpr_p, arrayIDs_p, maxArray_p); break; } case FEED_EXPR: { if(feedExpr_p != "") { feed1IDs_p.resize(0); feed2IDs_p.resize(0); feedPairIDs_p.resize(0,2); node = msFeedGramParseCommand(ms, feedExpr_p, feed1IDs_p, feed2IDs_p, feedPairIDs_p); } break; } case UVDIST_EXPR: { selectedUVRange_p.resize(2,0); selectedUVUnits_p.resize(0); if(uvDistExpr_p != "" && msUvDistGramParseCommand(ms, uvDistExpr_p, selectedUVRange_p, selectedUVUnits_p) == 0) node = *(msUvDistGramParseNode()); break; } case TAQL_EXPR: { if(taqlExpr_p != "") { node = RecordGram::parse(*msLike->table(),taqlExpr_p); } break; } case POLN_EXPR: { // This expression is a pure in-row selection. No // need to add to the tree of TENs (the condition // variable). if (polnExpr_p != "") { msPolnGramParseCommand(ms, polnExpr_p, node, ddIDs_p, selectedPolMap_p, selectedSetupMap_p); } break; } case STATE_EXPR: { stateObsModeIDs_p.resize(0); if(stateExpr_p != "" && msStateGramParseCommand(ms, stateExpr_p,stateObsModeIDs_p) == 0) { node = *(msStateGramParseNode()); if (stateObsModeIDs_p.nelements()==0) throw(MSSelectionStateError(String("No match found for state expression: ")+stateExpr_p)); } break; } case NO_EXPR:break; default: break; } // Switch condition = condition && node; }//For // // Now parse the time expression. Internally use the condition // generated so far to find the first logical row to use to get // value of the wild-card fields in the time expression. // selectedTimesList_p.resize(3,0); const TableExprNode *timeNode = 0x0; TableExprNode colAsTEN = msLike->col(msLike->columnName(MS::TIME)); MSSelectableMainColumn *mainColInterface=msLike->mainColumns(); // MSMainColInterface msMainColInterface; // msMainColInterface.init(*(msLike->table())); if(timeExpr_p != "" && //msTimeGramParseCommand(ms, timeExpr_p, condition, selectedTimesList_p) == 0) msTimeGramParseCommand(ms, timeExpr_p, colAsTEN, *mainColInterface, condition, selectedTimesList_p) == 0) timeNode = msTimeGramParseNode(); // // Add the time-expression TEN to the condition // if(timeNode && !timeNode->isNull()) { if(condition.isNull()) condition = *timeNode; else condition = condition && *timeNode; } fullTEN_p = condition; } catch(std::exception& x) { runErrorHandler(); deleteNodes(); //deleteErrorHandlers(); throw; } runErrorHandler(); deleteNodes(); //deleteErrorHandlers(); return condition; } TableExprNode MSSelection::toTableExprNode(const MeasurementSet* ms) { // Convert the MS selection to a TableExprNode object, // representing a TaQL selection in C++. // Input: // ms const MeasurementSet& MeasurementSet to bind TaQL // Output: // toTableExprNode TableExprNode Table expression node // // Interpret all expressions in the MS selection // // This method now is purely for backwards compatibility reasons. // Its usage is discouraged. // // The original code using old-styled interface to the various // parsers is available as comments in r19937 in the SVN repos. // if (fullTEN_p.isNull()==False) return fullTEN_p; MSInterface msLike(*ms); return toTableExprNode(&msLike); } //---------------------------------------------------------------------------- void MSSelection::setErrorHandler(const MSExprType type, MSSelectionErrorHandler* mssEH, const Bool overRide) { // // We make a copy (clone) of the supplied error handler pointer // and manage that pointer internally. This means that the // supplied error handler must be set up before being passed here. // switch (type) { case ANTENNA_EXPR: { if (overRide || !MSAntennaParse::thisMSAErrorHandler) { if (mssEH == NULL) { MSAntennaParse::thisMSAErrorHandler.reset(); } else { MSAntennaParse::thisMSAErrorHandler.reset(mssEH->clone()); } } break; } case FEED_EXPR: { if (overRide || !MSFeedParse::thisMSFErrorHandler) { if (mssEH == NULL) { MSFeedParse::thisMSFErrorHandler.reset(); } else { MSFeedParse::thisMSFErrorHandler.reset(mssEH->clone()); } } break; } case STATE_EXPR: { if (overRide || !MSStateParse::thisMSSErrorHandler) { if (mssEH == NULL) { MSStateParse::thisMSSErrorHandler.reset(); } else { MSStateParse::thisMSSErrorHandler.reset(mssEH->clone()); } } break; } case SPW_EXPR: { if (overRide || !MSSpwParse::thisMSSpwErrorHandler) { if (mssEH == NULL) { MSSpwParse::thisMSSpwErrorHandler.reset(); } else { MSSpwParse::thisMSSpwErrorHandler.reset(mssEH->clone()); } } break; } default: throw(MSSelectionError(String("Wrong MSExprType in MSSelection::setErrorHandler()"))); }; } //---------------------------------------------------------------------------- void MSSelection::runErrorHandler() { if (MSAntennaParse::thisMSAErrorHandler->nMessages() > 0) { MSSelectionAntennaParseError msAntException(String("")); MSAntennaParse::thisMSAErrorHandler->handleError(msAntException); } if (MSFeedParse::thisMSFErrorHandler->nMessages() > 0) { MSSelectionFeedParseError msFeedException(String("")); MSFeedParse::thisMSFErrorHandler->handleError(msFeedException); } if (MSStateParse::thisMSSErrorHandler->nMessages() > 0) { MSSelectionStateParseError msStateException(String("")); MSStateParse::thisMSSErrorHandler->handleError(msStateException); } if (MSSpwParse::thisMSSpwErrorHandler->nMessages() > 0) { MSSelectionSpwParseError msSpwException(String("")); MSSpwParse::thisMSSpwErrorHandler->handleError(msSpwException); } } //---------------------------------------------------------------------------- Bool MSSelection::getSelectedMS(MeasurementSet& selectedMS, const String& outMSName) { if (fullTEN_p.isNull()) fullTEN_p=toTableExprNode(ms_p); if ((ms_p == NULL) || ms_p->isNull()) throw(MSSelectionError("MSSelection::getSelectedMS() called without setting the parent MS.\n" "Hint: Need to use MSSelection::resetMS() perhaps?")); // return baseGetSelectedMS_p(selectedMS, *ms_p, fullTEN_p, outMSName); return getSelectedTable(selectedMS, *ms_p, fullTEN_p, outMSName); } //---------------------------------------------------------------------------- Bool MSSelection::exprIsNull(const MSExprType type) { Bool exprIsNull=False; if (type == NO_EXPR) for(uInt i=0; i(MAX_EXPR, int(NO_EXPR)); } else { for(uInt i=0; i MSSelection::getChanList(const MeasurementSet* ms, const Int defaultStep, const Bool sorted) { if (chanIDs_p.nelements() <= 0) getTEN(ms); uInt nrows=chanIDs_p.nrow(), ncols=chanIDs_p.ncolumn(); Matrix chanIDList; if (nrows > 0) { if (sorted) { Vector spwIDList(chanIDs_p.column(0));//Extract the SPW IDs Vector sortedNdx; // // Make a list of indices which will sort the chanID_p Matrix on // SPW ID (the first column of each row). // Bool deleteit; Sort sort(spwIDList.getStorage(deleteit), sizeof(Int)); sort.sortKey((uInt)0, TpInt); sort.sort(sortedNdx, nrows); // // Using the sorted indices, copy from the unsorted private // ChaIDs_p to the output (sorted) Matrix chandIDList. // chanIDList.resize(chanIDs_p.shape()); for(uInt targetRow=0; targetRow MSSelection::getChanFreqList(const MeasurementSet* ms, const Bool sorted) { LogIO log_l(LogOrigin("MSSelection", "getChanFreqList")); if (chanIDs_p.nelements() == 0) getTEN(ms); Matrix chanList_l = getChanList(ms, 1, sorted); Matrix freqList_l; freqList_l.resize(chanList_l.shape()); if (chanList_l.shape()(0) == 0) return freqList_l; const MSSpWindowColumns msSpwSubTable(ms_p->spectralWindow()); if (msSpwSubTable.nrow() <= (uInt)max(chanList_l.column(0))) throw(MSSelectionError(String("MSS::getChanFreqList:: Internal error: Selected list of SPW IDs > " "no. of rows in the SPECTRAL_WINDOW sub-table."))); Int spwID; for (uInt i=0; i < chanList_l.shape()(0); i++) { spwID = chanList_l(i,0); // First column has the SPW ID Array chanFreq(msSpwSubTable.chanFreq()(spwID)); Double avgChanWidth = chanList_l(i,3)*sum(msSpwSubTable.chanWidth()(spwID)) /msSpwSubTable.chanWidth()(spwID).nelements(); Int validStartChan, validEndChan; freqList_l(i,0) = (Double)chanList_l(i,0); // The SPW ID validStartChan = chanList_l(i,1); // chanList is already verified to be within valid limts [0,nchan-1] validEndChan = chanList_l(i,2); freqList_l(i,1) = chanFreq(IPosition(1,validStartChan)); //chanFreq(IPosition(1,chanList_l(i,1))); // The the freq. of start channel in Hz freqList_l(i,2) = chanFreq(IPosition(1,validEndChan)); //chanFreq(IPosition(1,chanList_l(i,2))); // The freq. of stop channel in Hz freqList_l(i,3) = avgChanWidth; // The channel width in Hz } return freqList_l; } //---------------------------------------------------------------------------- void MSSelection::getChanSlices(Vector >& chanslices, const MeasurementSet* ms,const Int defaultChanStep) { // The total number of spws Int nspw=ms->spectralWindow().nrow(); // Nominally empty selection for all spws chanslices.resize(nspw); chanslices.set(Vector()); // Get the chan selection matrix Matrix chanmat=this->getChanList(ms,defaultChanStep); for (uInt i=0;i& currspwsl(chanslices(chanmat(i,0))); // Add a slice element and fill it Int islice=currspwsl.nelements(); currspwsl.resize(islice+1,True); currspwsl(islice)=Slice(chanmat(i,1), (chanmat(i,2)-chanmat(i,1)+chanmat(i,3))/chanmat(i,3), // chanmat(i,2)-chanmat(i,1)+1, chanmat(i,3)); } } //---------------------------------------------------------------------------- void MSSelection::getCorrSlices(Vector >& corrslices, const MeasurementSet* ms) { // The total number of polids Int npol=ms->polarization().nrow(); // Nominally empty selection for all polids corrslices.resize(npol); corrslices.set(Vector()); // Get the corr indices as an ordered map std::map > > corrmap(this->getCorrMap(ms)); // Iterate over the ordered map to fill the slices for (const auto& elem : corrmap) { Int pol=elem.first; Vector corridx=elem.second[0]; Int ncorr=corridx.nelements(); corrslices(pol).resize(ncorr); for (Int i=0;i(MAX_EXPR, Int(NO_EXPR)); // Extract and set all expressions // // Antenna expression if (definedAndSet(selectionItem,"antenna")) { setAntennaExpr(selectionItem.asString("antenna")); // cout << antennaExpr_p << ", antenna" << endl; } // Feed expression if (definedAndSet(selectionItem,"feed")) { setFieldExpr(selectionItem.asString("feed")); // cout << feedExpr_p << ", feed" << endl; } // Field expression if (definedAndSet(selectionItem,"field")) { setFieldExpr(selectionItem.asString("field")); // cout << fieldExpr_p << ", field" << endl; } // SPW expression if (definedAndSet(selectionItem,"spw")) { setSpwExpr(selectionItem.asString("spw")); // cout << spwExpr_p << ", spw" << endl; } // Scan expression if (definedAndSet(selectionItem,"scan")) { setScanExpr(selectionItem.asString("scan")); // cout << scanExpr_p << ", scan" << endl; } // Observation expression if (definedAndSet(selectionItem,"obsrevation")) { setObservationExpr(selectionItem.asString("observation")); } // Array expression if (definedAndSet(selectionItem,"array")) { setArrayExpr(selectionItem.asString("array")); } // Time expression if (definedAndSet(selectionItem,"time")) { setTimeExpr(selectionItem.asString("time")); // cout << timeExpr_p << ", time" << endl; } // UV Distribution expression if (definedAndSet(selectionItem,"uvdist")) { setUvDistExpr(selectionItem.asString("uvdist")); // cout << uvDistExpr_p << ", uvdist" << endl; } // Poln expression if (definedAndSet(selectionItem,"poln")) { setUvDistExpr(selectionItem.asString("poln")); // cout << polnExpr_p << ", poln" << endl; } clearErrorHandlers(); } Bool MSSelection::definedAndSet(const Record& inpRec, const String& fieldName) { // Check if a record field is defined and not unset // Input: // inpRec const Record& Input Record // fieldName const String& Field name // Ouput: // definedAndSet Bool True if field defined and // not unset // Bool retval = False; // Check if record field is defined if (inpRec.isDefined(fieldName)) { retval = True; // Now check if unset // if (inpRec.dataType(fieldName) == TpRecord) { // retval = !Unset::isUnset(inpRec.subRecord(fieldName)); // }; }; return retval; } //---------------------------------------------------------------------------- } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSSel/MSSelection.h000066400000000000000000000635731476623553700173020ustar00rootroot00000000000000//# MSSelection.h: Class to represent a selection on an MS //# Copyright (C) 1996,1997,1998,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSELECTION_H #define MS_MSSELECTION_H #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // MSSelection: Class to represent a selection on an MS // // // // //
      • MeasurementSet module // // // // From "MeasurementSet" and "selection". // // // // The MSSelection class represents a selection on a MeasurementSet (MS). // This class is used in translating MS selections represented as // selection items in the user interface, and for converting between // MS selection and pure TaQL selection. // // The purpose of this class is to provides a simple expression based // selection mechanism to both the end-user and developer wishing to // perform query operations over a measurement set. This is // accomplished by abstracting the TaQL interface through an // adapter/translation interface which converts STaQL (Simple Table // Query Language) expressions into the equivalent table expression // form, reducing the knowledge necessary to perform powerful query // operations directly in TaQL. It is also possible to supply pure // TaQL expression(s) as sub-expressions if required. For a complete // list of the STaQL interface refer to the MeasurementSet Selection Syntax document at: Data // Selection // // The sub-expressions are interpreted in the order which they were // set. The order however in not important - any dependency on the // order in which the expressions are evaluated is handled internally. // The result of parsing the expressions is TableExprNode (TEN). All // TENs from sub-expressions are finally ANDed and the resultant TEN // is used to select the rows of the MS table. // // // // // // // Create a MS and a MS selection // MeasurementSet ms(msName); // MSSelection select; // // Setup any sub-expressions of interest directly // // (or optionally send this information through a Record) // select.setFieldExpr("0,1"); // select.setSpwExpr(">0"); // // Create a table expression over a MS representing the selection // TableExprNode node = select.toTableExprNode(&ms); // // Optionally create a table and new MS based on this node // Table tablesel(ms.tableName(), Table::Update); // MeasurementSet mssel(tablesel(node, node.nrow())); // // // // // This class is used by the MS access classes. // // // // Generalize SpwExpressions and PolnExpressions to optionally include // DataDescription ID specifications. // class MSSelection { public: enum MSExprType {NO_EXPR = 0, ANTENNA_EXPR, CORR_EXPR, FIELD_EXPR, SPW_EXPR, SCAN_EXPR, ARRAY_EXPR, TIME_EXPR, UVDIST_EXPR, POLN_EXPR, STATE_EXPR, OBSERVATION_EXPR, FEED_EXPR, TAQL_EXPR, MAX_EXPR = TAQL_EXPR}; enum MSSMode {PARSE_NOW=0, PARSE_LATE}; // Default null constructor, and destructor MSSelection(); virtual ~MSSelection(); // Construct using an MS and the various selection expressions to // be applied to the given MS. By default, the expressions will // be parsed immediately. With mode=PARSE_LATE, the parsing will // be done with a call to toTableExprNode(). MSSelection(const MeasurementSet& ms, const MSSMode& mode=PARSE_NOW, const String& timeExpr="", const String& antennaExpr="", const String& fieldExpr="", const String& spwExpr="", const String& uvDistExpr="", const String& taqlExpr="", const String& polnExpr="", const String& scanExpr="", const String& arrayExpr="", const String& stateExpr="", const String& observationExpr="", const String& feedExpr=""); // Construct from a record representing a selection item at the // CLI or user interface level. This is functionally same as the // constructor above with mode=PARSE_LATE. MSSelection(const Record& selectionItem); // Copy constructor MSSelection(const MSSelection& other); // Assignment operator MSSelection& operator=(const MSSelection& other); // Helper method for converting index vectors to expression strings static String indexExprStr(Vector index); // Helper method for converting name vectors to expression strings static String nameExprStr(Vector name); // Expression setters. The following set*Expr() methods only set // the expressions. Parsing is done with a call to // toTableExprNode(). Bool setAntennaExpr(const String& antennaExpr); Bool setFieldExpr(const String& fieldExpr); Bool setSpwExpr(const String& spwExpr); Bool setScanExpr(const String& scanExpr); Bool setArrayExpr(const String& ArrayExpr); Bool setTimeExpr(const String& timeExpr); Bool setUvDistExpr(const String& uvDistExpr); Bool setTaQLExpr(const String& taqlExpr); Bool setPolnExpr(const String& polnExpr); Bool setStateExpr(const String& stateExpr); Bool setObservationExpr(const String& observationExpr); Bool setFeedExpr(const String& feedExpr); // Accessor for the various selection expressions as strings. const String getExpr(const MSExprType type=NO_EXPR); // Accessor for result of parsing all of the selection // expressions. The final TableExprNode (TEN) is the result of // ANDing the TENs for the individual expressions. TableExprNode getTEN(const MeasurementSet*ms = NULL); // Accessor for the list of the selected scan IDs. inline Vector getScanList(const MeasurementSet* ms=NULL) {getTEN(ms); return scanIDs_p;} // Accessor for the list of the selected observation IDs. inline Vector getObservationList(const MeasurementSet* ms=NULL) {getTEN(ms); return observationIDs_p;} // Accessor for the list of the selected feed1 IDs. inline Vector getFeed1List(const MeasurementSet* ms=NULL) {getTEN(ms); return feed1IDs_p;} // Accessor for the list of the selected feed2 IDs. inline Vector getFeed2List(const MeasurementSet* ms=NULL) {getTEN(ms); return feed2IDs_p;} // Similar to baselines for antennas inline Matrix getFeedPairList(const MeasurementSet* ms=NULL) {getTEN(ms); return feedPairIDs_p;} // Accessor for the list of selected sub-array IDs. inline Vector getSubArrayList(const MeasurementSet* ms=NULL) {getTEN(ms); return arrayIDs_p;} // Accessor for the list of antenna-1 of the selected baselines. // Antennas affected by the baseline negation operator have the // antenna IDs multiplied by -1. inline Vector getAntenna1List(const MeasurementSet* ms=NULL) {// if (antenna1IDs_p.nelements() <= 0) getTEN(ms); return antenna1IDs_p;} // Accessor for the list of antenna-2 of the selected baselines. // Antennas affected by the baseline negation operator have the // antenna IDs multiplied by -1. inline Vector getAntenna2List(const MeasurementSet* ms=NULL) {// if (antenna2IDs_p.nelements() <= 0) getTEN(ms); return antenna2IDs_p;} // Accessor for the list of selected baselines. The list is a Nx2 // Matrix with one row per baseline containing the antenna IDs of // the two antenna associated with the baseline. // // Baselines affected by the negation operator in the baseline // selection expression are reported with one or both the antenna // IDs multiplied by -1. E.g. a baseline selection expression // "!1" will result in a baseline list // // [-1, 2], // [-1, 3], // [-1, 4], // .... // // The expression "!1&10" will result in a baseline list [-1, // -10]. Etc... // inline Matrix getBaselineList(const MeasurementSet* ms=NULL) {getTEN(ms); return baselineIDs_p;} // Accessor for the list of selected field IDs. inline Vector getFieldList(const MeasurementSet* ms=NULL) {// if (fieldIDs_p.nelements() <= 0) getTEN(ms); return fieldIDs_p;} // Accessor for the list of selected state Obs_Modes. inline Vector getStateObsModeList(const MeasurementSet* ms=NULL) {if (stateObsModeIDs_p.nelements() <= 0) getTEN(ms); return stateObsModeIDs_p;} // Accessor for the list of the specified time range(s) as the // start and end MJD values. The time ranges are stored as columns, // i.e. the output Matrix is 2 x n_ranges. inline Matrix getTimeList(const MeasurementSet* ms=NULL) {getTEN(ms); return selectedTimesList_p;} // Accessor for the list of the specified uv-range(s) as the start // and end values in units used in the MS. inline Matrix getUVList(const MeasurementSet* ms=NULL) {getTEN(ms); return selectedUVRange_p;} // Accessor for the list of user defined units for the // uv-range(s). The uv-range(s) return by getUVList is always in // the units used in the MS. inline Vector getUVUnitsList(const MeasurementSet* ms=NULL) {getTEN(ms); return selectedUVUnits_p;} // Accessor for the list of the selected Spectral Window IDs. inline Vector getSpwList(const MeasurementSet* ms=NULL) {// if (spwIDs_p.nelements() <= 0) getTEN(ms); return spwIDs_p;} // Accessor for the table (as a nx4 Matrix) of the selected // Spectral Windows and associated ranges of selected channels. // Each row of the Matrix has the following elements: // // SpwID StartCh StopCh Step // // where StartCh, StopCh and Step are the first and the last // selected channels and step is the step size. If no step size // was supplied as part of the expression, the value of Step is // replaced with the value of the defaultStep parameter. Multiple // channel specifications for the same Spectral Window selection, // results in multiple rows in the Matrix. If sorted is True, the // rows of the output Matrix will be sorted by the SPW IDs (the // entries in the first column). Matrix getChanList(const MeasurementSet* ms=NULL, const Int defaultStep=1, const Bool sorted=False); // // Same as getChanList, except that the channels and steps are in Hz. // Matrix getChanFreqList(const MeasurementSet* ms=NULL, const Bool sorted=False); // Accessor for the list of the selected Data Description IDs // (DDID) from the polarization expression parsing. The actual // selected DDIDs would be an intersection of the DDIDs selected // from polarization and SPW expressions parsing (see // getSPWDDIDList() below). // Note that there is no guarantee that returned vector // is inmight not be in sorted order. inline Vector getDDIDList(const MeasurementSet* ms=NULL) {if (ddIDs_p.nelements() <= 0) getTEN(ms); return ddIDs_p;} // Accessor for the list of the selected Data Description IDs from // the SPW expression parsing. The actual // selected DDIDs would be an intersection of the DDIDs selected // from polarization and SPW expressions parsing (see // getDDIDList() above). // // The actual DDIDs selected will be an intersection of the lists // from getDDIDList() and getSPWDDIDList() (which can be generated // using the set_intersection(Vector&, Vector&) global // method in MSSelectionTool.{cc,h}). inline Vector getSPWDDIDList(const MeasurementSet* ms=NULL) {if (spwDDIDs_p.nelements() <= 0) getTEN(ms); return spwDDIDs_p;} // // The key in the ordered map returned by getPolMap() is the Data // Description ID (DDID). The value is a vector containing the // list of in-row indices to pick out the selected polarizations // (or equivalently, the list of indices for the vector in the // corrType column of the POLARIZATION sub-table). These are also // what the user intended (i.e., e.g. not all DD IDs due to user // POL expression might be selected due to SPW expressions). // inline std::map > getPolMap(const MeasurementSet* ms=NULL) {getTEN(ms); return selectedPolMap_p;}; // // The key in the ordered map returned by getCorrMap() is the // pol. in the Data Description ID (DDID) sub-table. The value is // a Vector of two Vectors. // // The returned Map has a key that maps to two vectors: // Key ----> Vector1 Vector2 // // Key : Row index in the POLARIZATION sub-table // // Vector1 : List of poln. indices selected from the row pointed // by Key. These are the in-row indices to pick-out the // desired (selected) polarization products from the // selected rows of the MS (or equivalently, the list of // indices for the vector in the corrType column of the // POLARIZATION sub-table). // // Vector2 : List of selected rows from the DATA_DESCRIPTION sub-table // // An example: following are the sub-tables used for the example // explaination below: // // POLARIZATION Sub-table // ====================== // Row Poln // ------------ // 0 RR, LL // 1 RR, LR, RL, LL // // DATA_DESCRIPTION Sub-table // ========================== // Row PolnID SpwID // ------------------------------ // 0 0 0 // 1 1 1 // 2 1 2 // 3 1 3 // 4 1 4 // 5 1 5 // 6 1 6 // 7 1 7 // 8 1 8 // // // E.g., the expression poln='LL' // // returns the Map: // // corrmap = (0, [[1], [0]]) (1, [[3], [0,1,2,3,4,5,6,7,8]] ) // // The rows from the POLARIZATION table selected are 0 and 1, These are // two keys for the two entries in the map. // // 1. The two vectors in map 1 are: [1] and [0]. The this reads as: // From the 0th. row of the POLARIZATION table, use the indices [1]. The // relevant list of associated DD rows are [0] // // 2. The two vectors in map 2 are: [3] and [0,1,2,3,4,5,6,7,8]. This reads as: // From the 1st. row of the POLARIZATION table, use the indices [3]. The // relevant list of associated DD rows are [0,1,2,3,4,5,6,7,8]. // // For a client code: // // o To get a list of the DDIDs selected, iterate over all entries of the // map and collate the second vector from each entry. // // Or, use getDDIDList(). // // o To get the list of the selected poln. *in-row indices*, collate the // first vector from each entry. // // o To get a list of POLARIZATION IDs selected (rows of the POLARIZATION // table), make a list of all the keys of this map. inline std::map > > getCorrMap(const MeasurementSet* ms=NULL) {getTEN(ms); return selectedSetupMap_p;}; // Methods to convert the maps return by getChanList and // getCorrMap to a list of Slice which can be directly used by // Table system for in-row selection of frequency channels and // polarizations. void getChanSlices(Vector >& chanslices, const MeasurementSet* ms=NULL, const Int defaultChanStep=1); void getCorrSlices(Vector >& corrslices, const MeasurementSet* ms=NULL); // Clear sub-expression and reset priority. Default behavior is to // reset all sub-expressions. void clear(const MSExprType type=NO_EXPR); // Set all error handlers to a known state (NULL). void clearErrorHandlers(); Bool exprIsNull(const MSExprType type=NO_EXPR); // Convey to the various parsers to delete the TENs they hold void deleteNodes(); // Delete error handlers (mostly the internally allocated ones). void deleteErrorHandlers(); // Convert to TableExprNode format (C++ interface to TaQL). This // is now for purely backwards compatibility and ease of use. It // internally constructs the MSSelectableTable from the supplied // MS and calls the generic version of toTableExprNode below // (which works with MSSelectableTable object). TableExprNode toTableExprNode(const MeasurementSet* ms); // Convert to TableExprNode format (C++ interface to TaQL). The // MSSelectableTable is a pure-virtual base class which provides a // generic interface both to MeasurementSet and CalTable (in the // synthesis module) services used in MSSelection. The actual // objects used for supplying MeasurementSet or CalTable to // MSSelection are MSInterface and CTInterface classes // respectively. With this, MSSelection module can be used for // selection on MeasurementSet or CalTable. TableExprNode toTableExprNode(MSSelectableTable* msLike); // Return the selected MS. The selected MS reflects only row // selections (as against in-row selections). If outMSName != "", // the selected MS is also written to the disk (a shallow copy). // // For in-row selection, use the appropriate global function // mssSetData() MSSelectionTools.h which also returns the in-row // (corr/chan) slices that can be supplied to the VisIter object // for on-the-fly in-row selection. Bool getSelectedMS(MeasurementSet& selectedMS, const String& outMSName=""); void resetMS(const MeasurementSet& ms) {resetTEN(); ms_p=&ms;}; void resetTEN() {fullTEN_p=TableExprNode();}; // The MSSelection object is designed to be re-usable object. The // following reset() methods set the internal state of the object // to same state as with the equivalent constructor. // // mode can be one of the MSSModes. MSSMode::PARSE_NOW will parse // the given expressions and internally hold the final TEN // (i.e. will also internally call toTableExprNode()). The // internal TEN can be accessed via the getTEN() method. // MSSMode::PARSE_LATER will only set the expression strings. // Parsing will be done later with a call to toTableExprNode(). // // This version, here for backward compatibility reasons, // internally constructs a // MSSelectableTable // object and calls the reset() method below that works with // MSSelectableTable. void reset(const MeasurementSet& ms, const MSSMode& mode = PARSE_NOW, const String& timeExpr = "", const String& antennaExpr = "", const String& fieldExpr = "", const String& spwExpr = "", const String& uvDistExpr = "", const String& taqlExpr = "", const String& polnExpr = "", const String& scanExpr = "", const String& arrayExpr = "", const String& stateExpr = "", const String& observationExpr = ""); // Add feedExpr; keep old signature for backwards compatibility void reset2(const MeasurementSet& ms, const MSSMode& mode = PARSE_NOW, const String& timeExpr = "", const String& antennaExpr = "", const String& fieldExpr = "", const String& spwExpr = "", const String& uvDistExpr = "", const String& taqlExpr = "", const String& polnExpr = "", const String& scanExpr = "", const String& arrayExpr = "", const String& stateExpr = "", const String& observationExpr = "", const String& feedExpr = ""); // This version of reset() works with generic MSSelectableTable // object. Accessing the services of the MSSelection module via // this interface is recommended over the version of reset() that // uses MeasurementSet. void reset(MSSelectableTable& msLike, const MSSMode& mode = PARSE_NOW, const String& timeExpr = "", const String& antennaExpr = "", const String& fieldExpr = "", const String& spwExpr = "", const String& uvDistExpr = "", const String& taqlExpr = "", const String& polnExpr = "", const String& scanExpr = "", const String& arrayExpr = "", const String& stateExpr = "", const String& observationExpr = ""); // Add feedExpr; keep old signature for backwards compatibility void reset2(MSSelectableTable& msLike, const MSSMode& mode = PARSE_NOW, const String& timeExpr = "", const String& antennaExpr = "", const String& fieldExpr = "", const String& spwExpr = "", const String& uvDistExpr = "", const String& taqlExpr = "", const String& polnExpr = "", const String& scanExpr = "", const String& arrayExpr = "", const String& stateExpr = "", const String& observationExpr = "", const String& feedExpr = ""); // Set the maximum value acceptable for SCAN, OBSERVATION or // SUB-ARRAY IDs. The main-table columns for these do not refere // to rows of sub-tables and therefore there is no cheap way to // find a valid range for these which can be used in the parsers // to generate error or warning messages if a value outside the // range is used in the expressions. The default maximum value // for scan, observation and sub-array IDs is 1000. inline void setMaxScans(const Int& n=1000) {maxScans_p=n;}; inline void setMaxObs(const Int& n=1000) {maxObs_p=n;}; inline void setMaxArray(const Int& n=1000) {maxArray_p=n;}; // Set the error handler to be used for reporting errors while // parsing the type of expression give by the first argument. void setErrorHandler(const MSExprType type, MSSelectionErrorHandler* mssEH, const Bool overRide=True); // Initialize the error handler. This is set the error-handler to // the user supplied error handler via setErrorHandler() or to the // default built-in error handler. void initErrorHandler(const MSExprType tye=NO_EXPR); // Execute the handleError() method of the error-handlers. This // is called in the catch code for any exceptions emitted from any // of the parsers. It is also called at the end of the // parsing cycle. void runErrorHandler(); // Return the pointer to the MS used internally. const MeasurementSet* getMS(MSSelectableTable* msLike); private: // Set into the order of the selection expression Bool setOrder(MSSelection::MSExprType type); // Initialize from a Record representing a selection // item from the user interface or CLI void fromSelectionItem(const Record& selectionItem); // Check if record field exists and is not unset Bool definedAndSet(const Record& inpRec, const String& fieldName); // Convert an MS select string to TaQL // const String msToTaQL(const String& msSelect) {}; TableExprNode fullTEN_p; const MeasurementSet *ms_p; // Selection expressions String antennaExpr_p; String fieldExpr_p; String spwExpr_p; String scanExpr_p; String arrayExpr_p; String timeExpr_p; String uvDistExpr_p; String polnExpr_p; String taqlExpr_p; String stateExpr_p; String observationExpr_p; String feedExpr_p; // Priority Vector exprOrder_p; Vector antenna1IDs_p,antenna2IDs_p,fieldIDs_p, spwIDs_p, scanIDs_p, arrayIDs_p, ddIDs_p,stateObsModeIDs_p, observationIDs_p, spwDDIDs_p, feed1IDs_p, feed2IDs_p; Matrix chanIDs_p; Matrix baselineIDs_p; Matrix feedPairIDs_p; Matrix selectedTimesList_p; Matrix selectedUVRange_p; Vector selectedUVUnits_p; std::map > selectedPolMap_p; std::map > > selectedSetupMap_p; Int maxScans_p, maxObs_p, maxArray_p; Bool isMS_p,toTENCalled_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSSelectionError.cc000066400000000000000000000202431476623553700204350ustar00rootroot00000000000000//# MSSelectionError.cc: Error classes for the MSSelection classes //# Copyright (C) 1994,1995,1996,1997,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSSelectionError::MSSelectionError (Category c) : AipsError("MSSelection Error",c), hasMessage(False) {} MSSelectionError::MSSelectionError (const String& str,Category c) : AipsError(str,c), hasMessage(False) {} MSSelectionError::~MSSelectionError () noexcept {} void MSSelectionError::addMessage(String& mesg) { message = message+mesg; hasMessage = True; } void MSSelectionError::changeMessage(String& mesg) { message = mesg; } // //----------------------------------------------------------------------------------- // String constructMessage(const Int pos, const String& command) { ostringstream newMesg; newMesg << endl << "(near char. " << pos << " in string \"" << command << "\")"; // // Make a few guess about user errors and help them out // (did someone say I don't do user support? ;-)) // if ((pos > 0) && (pos < (Int)command.length()) && (command[pos-1] == '-')) newMesg << endl << "[TIP: Did you know we use \"~\" as the range operator (for a good reason)?]"; return String(newMesg.str().c_str()); } // //------------------------Time selection expression parser exceptions---------------- // MSSelectionNullSelection::MSSelectionNullSelection (const String& str,Category c) : MSSelectionError(str,c) {} MSSelectionNullSelection::~MSSelectionNullSelection () noexcept {} MSSelectionNullExpr::MSSelectionNullExpr (const String& str,Category c) : MSSelectionError(str,c) {} MSSelectionNullExpr::~MSSelectionNullExpr () noexcept {} MSSelectionNullTEN::MSSelectionNullTEN (const String& str,Category c) : MSSelectionError(str,c) {} MSSelectionNullTEN::~MSSelectionNullTEN () noexcept {} // //------------------------Time selection expression parser exceptions---------------- // MSSelectionTimeError::MSSelectionTimeError (const String& str,Category c) : MSSelectionError(str,c) {} MSSelectionTimeError::~MSSelectionTimeError () noexcept {} MSSelectionTimeParseError::MSSelectionTimeParseError (const String& str,Category c) : MSSelectionTimeError(str,c) {} MSSelectionTimeParseError::~MSSelectionTimeParseError () noexcept {} // //------------------------Baseline selection expression parser exceptions------------ // MSSelectionAntennaError::MSSelectionAntennaError (const String& str,Category c) : MSSelectionError(str,c) {} MSSelectionAntennaError::~MSSelectionAntennaError () noexcept {} MSSelectionAntennaParseError::MSSelectionAntennaParseError (const String& str,Category c) : MSSelectionAntennaError(str,c) {} MSSelectionAntennaParseError::~MSSelectionAntennaParseError () noexcept {} // //------------------------Field selection expression parser exceptions--------------- // MSSelectionFieldError::MSSelectionFieldError (const String& str,Category c) : MSSelectionError(str,c) {} MSSelectionFieldError::~MSSelectionFieldError () noexcept {} MSSelectionFieldParseError::MSSelectionFieldParseError (const String& str,Category c) : MSSelectionFieldError(str,c) {} MSSelectionFieldParseError::~MSSelectionFieldParseError () noexcept {} MSSelectionFieldWarning::MSSelectionFieldWarning (const String& str,Category c) : MSSelectionFieldError(str,c) {} MSSelectionFieldWarning::~MSSelectionFieldWarning () noexcept {} // //------------------------UVDist selection expression parser exceptions-------------- // MSSelectionUvDistError::MSSelectionUvDistError (const String& str,Category c) : MSSelectionError(str,c) {} MSSelectionUvDistError::~MSSelectionUvDistError () noexcept {} MSSelectionUvDistParseError::MSSelectionUvDistParseError (const String& str,Category c) : MSSelectionUvDistError(str,c) {} MSSelectionUvDistParseError::~MSSelectionUvDistParseError () noexcept {} // //------------------------SPW selection expression parser exceptions----------------- // MSSelectionSpwError::MSSelectionSpwError (const String& str,Category c) : MSSelectionError(str,c) {} MSSelectionSpwError::~MSSelectionSpwError () noexcept {} MSSelectionSpwParseError::MSSelectionSpwParseError (const String& str,Category c) : MSSelectionSpwError(str,c) {} MSSelectionSpwParseError::~MSSelectionSpwParseError () noexcept {} MSSelectionSpwWarning::MSSelectionSpwWarning (const String& str,Category c) : MSSelectionSpwError(str,c) {} MSSelectionSpwWarning::~MSSelectionSpwWarning () noexcept {} // //------------------------Scan selection expression parser exceptions---------------- // MSSelectionScanError::MSSelectionScanError (const String& str,Category c) : MSSelectionError(str,c) {} MSSelectionScanError::~MSSelectionScanError () noexcept {} MSSelectionScanParseError::MSSelectionScanParseError (const String& str,Category c) : MSSelectionScanError(str,c) {} MSSelectionScanParseError::~MSSelectionScanParseError () noexcept {} // //------------------------Sub-array selection expression parser exceptions----------- // MSSelectionArrayError::MSSelectionArrayError (const String& str,Category c) : MSSelectionError(str,c) {} MSSelectionArrayError::~MSSelectionArrayError () noexcept {} MSSelectionArrayParseError::MSSelectionArrayParseError (const String& str,Category c) : MSSelectionArrayError(str,c) {} MSSelectionArrayParseError::~MSSelectionArrayParseError () noexcept {} // //----------------------------------------------------------------------------------- // MSSelectionPolnError::MSSelectionPolnError (const String& str,Category c) : MSSelectionError(str,c) {} MSSelectionPolnError::~MSSelectionPolnError () noexcept {} MSSelectionPolnParseError::MSSelectionPolnParseError (const String& str,Category c) : MSSelectionPolnError(str,c) {} MSSelectionPolnParseError::~MSSelectionPolnParseError () noexcept {} // //----------------------------------------------------------------------------------- // MSSelectionStateError::MSSelectionStateError (const String& str,Category c) : MSSelectionError(str,c) {} MSSelectionStateError::~MSSelectionStateError () noexcept {} MSSelectionStateParseError::MSSelectionStateParseError (const String& str,Category c) : MSSelectionStateError(str,c) {} MSSelectionStateParseError::~MSSelectionStateParseError () noexcept {} // //----------------------------------------------------------------------------------- // MSSelectionObservationError::MSSelectionObservationError (const String& str,Category c) : MSSelectionError(str,c) {} MSSelectionObservationError::~MSSelectionObservationError () noexcept {} MSSelectionObservationParseError::MSSelectionObservationParseError (const String& str,Category c) : MSSelectionObservationError(str,c) {} MSSelectionObservationParseError::~MSSelectionObservationParseError () noexcept {} // //----------------------------------------------------------------------------------- // MSSelectionFeedError::MSSelectionFeedError (const String& str,Category c) : MSSelectionError(str,c) {} MSSelectionFeedError::~MSSelectionFeedError () noexcept {} MSSelectionFeedParseError::MSSelectionFeedParseError (const String& str,Category c) : MSSelectionFeedError(str,c) {} MSSelectionFeedParseError::~MSSelectionFeedParseError () noexcept {} } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSSel/MSSelectionError.h000066400000000000000000000256051476623553700203060ustar00rootroot00000000000000//# MSSelectionError.h: MSSelection error classes //# Copyright (C) 1994,1995,1996,1997,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSELECTIONERROR_H #define MS_MSSELECTIONERROR_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# This header file defines the error classes thrown by the //# MSSelection and related classes. // // // // // // // The top-level generic MSSelection exception class. All // exceptions thrown by the MSSelection and related classes are // derived from this. Catching this class will catch only MSSelection // exceptions (but all exceptions from the MSSelection line of // classes). To catch more specific MSSelection exceptions, catch // the derived classes. Note that you have to catch AipsError to // catch all possible exceptions thrown by all of Casacore modules! // class MSSelectionError : public AipsError { public: // The default constructor generates the message "Table error". MSSelectionError (Category c=GENERAL); // Construct with given message. void changeMessage(String& message); void addMessage(String& message); void reset() {message="";} MSSelectionError (const String& message,Category c=GENERAL); ~MSSelectionError () noexcept; Bool hasMessage; }; // //------------------------------------------------------------------- // class MSSelectionNullSelection : public MSSelectionError { public: MSSelectionNullSelection (const String& message, Category c=GENERAL); ~MSSelectionNullSelection () noexcept; }; // //------------------------------------------------------------------- // class MSSelectionNullExpr : public MSSelectionError { public: MSSelectionNullExpr (const String& message, Category c=GENERAL); ~MSSelectionNullExpr () noexcept; }; // //------------------------------------------------------------------- // class MSSelectionNullTEN : public MSSelectionError { public: MSSelectionNullTEN (const String& message, Category c=GENERAL); ~MSSelectionNullTEN () noexcept; }; // //------------------------------------------------------------------- // class MSSelectionTimeError : public MSSelectionError { public: // Add given message to string "MSSelection time error: ". MSSelectionTimeError (const String& message,Category c=GENERAL); ~MSSelectionTimeError () noexcept; }; class MSSelectionTimeParseError: public MSSelectionTimeError { public: MSSelectionTimeParseError (const String& message,Category c=GENERAL); ~MSSelectionTimeParseError () noexcept; }; // //------------------------------------------------------------------- // class MSSelectionAntennaError : public MSSelectionError { public: // Add given message to string "MSSelection time error: ". MSSelectionAntennaError (const String& message,Category c=GENERAL); ~MSSelectionAntennaError () noexcept; }; // //------------------------------------------------------------------- // class MSSelectionAntennaParseError: public MSSelectionAntennaError { public: MSSelectionAntennaParseError (const String& message,Category c=GENERAL); ~MSSelectionAntennaParseError () noexcept; }; // //------------------------------------------------------------------- // class MSSelectionFieldError : public MSSelectionError { public: // Add given message to string "MSSelection time error: ". MSSelectionFieldError (const String& message,Category c=GENERAL); ~MSSelectionFieldError () noexcept; }; // //------------------------------------------------------------------- // class MSSelectionFieldParseError: public MSSelectionFieldError { public: MSSelectionFieldParseError (const String& message,Category c=GENERAL); ~MSSelectionFieldParseError () noexcept; }; // //------------------------------------------------------------------- // class MSSelectionFieldWarning: public MSSelectionFieldError { public: MSSelectionFieldWarning (const String& message,Category c=GENERAL); ~MSSelectionFieldWarning () noexcept; }; // //------------------------------------------------------------------- // class MSSelectionUvDistError : public MSSelectionError { public: // Add given message to string "MSSelection time error: ". MSSelectionUvDistError (const String& message,Category c=GENERAL); ~MSSelectionUvDistError () noexcept; }; class MSSelectionUvDistParseError: public MSSelectionUvDistError { public: MSSelectionUvDistParseError (const String& message,Category c=GENERAL); ~MSSelectionUvDistParseError () noexcept; }; // //------------------------------------------------------------------- // class MSSelectionSpwError : public MSSelectionError { public: // Add given message to string "MSSelection time error: ". MSSelectionSpwError (const String& message,Category c=GENERAL); ~MSSelectionSpwError () noexcept; }; class MSSelectionSpwParseError: public MSSelectionSpwError { public: MSSelectionSpwParseError (const String& message,Category c=GENERAL); ~MSSelectionSpwParseError () noexcept; }; class MSSelectionSpwWarning: public MSSelectionSpwError { public: MSSelectionSpwWarning (const String& message,Category c=GENERAL); ~MSSelectionSpwWarning () noexcept; }; // //------------------------------------------------------------------- // class MSSelectionScanError : public MSSelectionError { public: // Add given message to string "MSSelection time error: ". MSSelectionScanError (const String& message,Category c=GENERAL); ~MSSelectionScanError () noexcept; }; class MSSelectionScanParseError: public MSSelectionScanError { public: MSSelectionScanParseError (const String& message,Category c=GENERAL); ~MSSelectionScanParseError () noexcept; }; class MSSelectionScanWarning: public MSSelectionScanError { public: MSSelectionScanWarning (const String& message,Category c=GENERAL); ~MSSelectionScanWarning () noexcept; }; // //------------------------------------------------------------------- // class MSSelectionArrayError : public MSSelectionError { public: // Add given message to string "MSSelection time error: ". MSSelectionArrayError (const String& message,Category c=GENERAL); ~MSSelectionArrayError () noexcept; }; class MSSelectionArrayParseError: public MSSelectionArrayError { public: MSSelectionArrayParseError (const String& message,Category c=GENERAL); ~MSSelectionArrayParseError () noexcept; }; class MSSelectionArrayWarning: public MSSelectionArrayError { public: MSSelectionArrayWarning (const String& message,Category c=GENERAL); ~MSSelectionArrayWarning () noexcept; }; // //------------------------------------------------------------------- // class MSSelectionPolnError : public MSSelectionError { public: // Add given message to string "MSSelection time error: ". MSSelectionPolnError (const String& message,Category c=GENERAL); ~MSSelectionPolnError () noexcept; }; class MSSelectionPolnParseError: public MSSelectionPolnError { public: MSSelectionPolnParseError (const String& message,Category c=GENERAL); ~MSSelectionPolnParseError () noexcept; }; class MSSelectionPolnWarning: public MSSelectionPolnError { public: MSSelectionPolnWarning (const String& message,Category c=GENERAL); ~MSSelectionPolnWarning () noexcept; }; // //------------------------------------------------------------------- // class MSSelectionStateError : public MSSelectionError { public: // Add given message to string "MSSelection time error: ". MSSelectionStateError (const String& message,Category c=GENERAL); ~MSSelectionStateError () noexcept; }; class MSSelectionStateParseError: public MSSelectionStateError { public: MSSelectionStateParseError (const String& message,Category c=GENERAL); ~MSSelectionStateParseError () noexcept; }; class MSSelectionStateWarning: public MSSelectionStateError { public: MSSelectionStateWarning (const String& message,Category c=GENERAL); ~MSSelectionStateWarning () noexcept; }; // //------------------------------------------------------------------- // class MSSelectionObservationError : public MSSelectionError { public: // Add given message to string "MSSelection time error: ". MSSelectionObservationError (const String& message,Category c=GENERAL); ~MSSelectionObservationError () noexcept; }; class MSSelectionObservationParseError: public MSSelectionObservationError { public: MSSelectionObservationParseError (const String& message,Category c=GENERAL); ~MSSelectionObservationParseError () noexcept; }; class MSSelectionObservationWarning: public MSSelectionObservationError { public: MSSelectionObservationWarning (const String& message,Category c=GENERAL); ~MSSelectionObservationWarning () noexcept; }; // //------------------------------------------------------------------- // class MSSelectionFeedError : public MSSelectionError { public: // Add given message to string "MSSelection time error: ". MSSelectionFeedError (const String& message,Category c=GENERAL); ~MSSelectionFeedError () noexcept; }; class MSSelectionFeedParseError: public MSSelectionFeedError { public: MSSelectionFeedParseError (const String& message,Category c=GENERAL); ~MSSelectionFeedParseError () noexcept; }; // //------------------------------------------------------------------- // String constructMessage(const Int pos, const String& command); } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSSelectionErrorHandler.cc000066400000000000000000000057201476623553700217360ustar00rootroot00000000000000//# MSSelectionErrorHandler.cc: Error handler for the MSSelection classes //# Copyright (C) 1994,1995,1996,1997,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include using namespace std; namespace casacore { //# NAMESPACE CASACORE - BEGIN MSSelectionErrorHandler::MSSelectionErrorHandler() :tokenList(), messageList() {} MSSelectionErrorHandler::MSSelectionErrorHandler(const MSSelectionErrorHandler& that) :tokenList(), messageList() { operator=(that); } MSSelectionErrorHandler& MSSelectionErrorHandler::operator=(const MSSelectionErrorHandler& that) { if (this != &that) { tokenList = that.tokenList; messageList = that.messageList; } return *this; } MSSelectionErrorHandler::~MSSelectionErrorHandler () {} void MSSelectionErrorHandler::reportError(const char *token,const String message) { if (token!=NULL) tokenList.push_back(token); messageList.push_back(message); } String MSSelectionErrorHandler::constructMessage() { ostringstream Mesg; if (messageList.size() > 0) { Mesg << messageList[0]; if (tokenList.size() > 0) for (uInt i=0;i 0) { String mesg(constructMessage()); mssErrorType.addMessage(mesg); throw(mssErrorType); // LogIO logIO; // logIO << mssErrorType.getMesg() << LogIO::WARN; } } void MSSelectionErrorHandler::reset() { tokenList.resize(0); messageList.resize(0); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSSel/MSSelectionErrorHandler.h000066400000000000000000000073461476623553700216060ustar00rootroot00000000000000//# MSSelectionErrorHandler.h: MSSelection error handler class //# Copyright (C) 1994,1995,1996,1997,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSELECTIONERRORHANDLER_H #define MS_MSSELECTIONERRORHANDLER_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# This header file defines the error handler class for the //# MSSelection module // // // // // // // // The top-level generic MSSelection error handler class. The // handleError() overloadable method takes the action of reporting // the error. The handleError() method of this defualt handler // constructs a message string and throws an exception of the type // supplied. This operation has been factored out into this object // to allow more control on the error handler mechanism from // outside. // // class MSSelectionErrorHandler { public: // The default constructor generates the message "Table error". MSSelectionErrorHandler(); MSSelectionErrorHandler(const MSSelectionErrorHandler& that); MSSelectionErrorHandler &operator=(const MSSelectionErrorHandler& that); virtual MSSelectionErrorHandler* clone() {return new MSSelectionErrorHandler();}; virtual ~MSSelectionErrorHandler (); virtual void reportError(const char *token,const String source=String("")); virtual String constructMessage(); virtual void reset(); virtual void handleError(MSSelectionError&); const std::vector& getMessages() const { return messageList; } Int nMessages() const { return messageList.size(); } protected: std::vector tokenList, messageList; }; // // // The handleError() method is overloaded to send the accumulated // error messages to the LogIO object as warning messages. // // class MSSelectionLogError: public MSSelectionErrorHandler { public: MSSelectionLogError():MSSelectionErrorHandler() {} virtual MSSelectionLogError* clone() {return new MSSelectionLogError();}; virtual ~MSSelectionLogError() {} virtual void handleError(MSSelectionError& mssErrorType) { if (messageList.size() > 0) { String mesg(constructMessage()); mssErrorType.addMessage(mesg); LogIO logIO; logIO << mssErrorType.getMesg() << LogIO::WARN; } } }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSSelectionKeywords.cc000066400000000000000000000152041476623553700211540ustar00rootroot00000000000000//# MSSelectionKeywords.cc: selection keywords for the MS //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN std::map& MSSelectionKeywords::getMap() { static std::map fieldMap(initMap()); return fieldMap; } Block& MSSelectionKeywords::getReverseMap() { static Block reverseMap(initReverseMap()); return reverseMap; } MSSelectionKeywords::Field MSSelectionKeywords::field(const String& itemName) { std::map& fieldMap = getMap(); std::map::iterator iter = fieldMap.find(itemName); return iter==fieldMap.end() ? UNDEFINED : Field(iter->second); } const String& MSSelectionKeywords::keyword(Field fld) { Block& reverseMap = getReverseMap(); return reverseMap[fld]; } std::map MSSelectionKeywords::initMap() { std::map fieldMap; fieldMap.insert (std::make_pair("undefined",UNDEFINED)); fieldMap.insert (std::make_pair("amplitude",AMPLITUDE)); fieldMap.insert (std::make_pair("corrected_amplitude",CORRECTED_AMPLITUDE)); fieldMap.insert (std::make_pair("model_amplitude",MODEL_AMPLITUDE)); fieldMap.insert (std::make_pair("ratio_amplitude",RATIO_AMPLITUDE)); fieldMap.insert (std::make_pair("residual_amplitude",RESIDUAL_AMPLITUDE)); fieldMap.insert (std::make_pair("obs_residual_amplitude",OBS_RESIDUAL_AMPLITUDE)); fieldMap.insert (std::make_pair("antenna1",ANTENNA1)); fieldMap.insert (std::make_pair("antenna2",ANTENNA2)); fieldMap.insert (std::make_pair("antennas",ANTENNAS)); fieldMap.insert (std::make_pair("array_id",ARRAY_ID)); fieldMap.insert (std::make_pair("axis_info",AXIS_INFO)); fieldMap.insert (std::make_pair("chan_freq",CHAN_FREQ)); fieldMap.insert (std::make_pair("corr_names",CORR_NAMES)); fieldMap.insert (std::make_pair("corr_types",CORR_TYPES)); fieldMap.insert (std::make_pair("data",DATA)); fieldMap.insert (std::make_pair("corrected_data",CORRECTED_DATA)); fieldMap.insert (std::make_pair("model_data",MODEL_DATA)); fieldMap.insert (std::make_pair("ratio_data",RATIO_DATA)); fieldMap.insert (std::make_pair("residual_data",RESIDUAL_DATA)); fieldMap.insert (std::make_pair("obs_residual_data",OBS_RESIDUAL_DATA)); fieldMap.insert (std::make_pair("data_desc_id",DATA_DESC_ID)); fieldMap.insert (std::make_pair("feed1",FEED1)); fieldMap.insert (std::make_pair("feed2",FEED2)); fieldMap.insert (std::make_pair("field_id",FIELD_ID)); fieldMap.insert (std::make_pair("fields",FIELDS)); fieldMap.insert (std::make_pair("flag",FLAG)); fieldMap.insert (std::make_pair("flag_row",FLAG_ROW)); fieldMap.insert (std::make_pair("flag_sum",FLAG_SUM)); fieldMap.insert (std::make_pair("float_data",FLOAT_DATA)); fieldMap.insert (std::make_pair("ha",HA)); fieldMap.insert (std::make_pair("ifr_number",IFR_NUMBER)); fieldMap.insert (std::make_pair("imaginary",IMAGINARY)); fieldMap.insert (std::make_pair("corrected_imaginary",CORRECTED_IMAGINARY)); fieldMap.insert (std::make_pair("model_imaginary",MODEL_IMAGINARY)); fieldMap.insert (std::make_pair("ratio_imaginary",RATIO_IMAGINARY)); fieldMap.insert (std::make_pair("residual_imaginary",RESIDUAL_IMAGINARY)); fieldMap.insert (std::make_pair("obs_residual_imaginary",OBS_RESIDUAL_IMAGINARY)); fieldMap.insert (std::make_pair("last",LAST)); fieldMap.insert (std::make_pair("num_corr",NUM_CORR)); fieldMap.insert (std::make_pair("num_chan",NUM_CHAN)); fieldMap.insert (std::make_pair("phase",PHASE)); fieldMap.insert (std::make_pair("corrected_phase",CORRECTED_PHASE)); fieldMap.insert (std::make_pair("model_phase",MODEL_PHASE)); fieldMap.insert (std::make_pair("ratio_phase",RATIO_PHASE)); fieldMap.insert (std::make_pair("residual_phase",RESIDUAL_PHASE)); fieldMap.insert (std::make_pair("obs_residual_phase",OBS_RESIDUAL_PHASE)); fieldMap.insert (std::make_pair("phase_dir",PHASE_DIR)); fieldMap.insert (std::make_pair("real",REAL)); fieldMap.insert (std::make_pair("corrected_real",CORRECTED_REAL)); fieldMap.insert (std::make_pair("model_real",MODEL_REAL)); fieldMap.insert (std::make_pair("ratio_real",RATIO_REAL)); fieldMap.insert (std::make_pair("residual_real",RESIDUAL_REAL)); fieldMap.insert (std::make_pair("obs_residual_real",OBS_RESIDUAL_REAL)); fieldMap.insert (std::make_pair("ref_frequency",REF_FREQUENCY)); fieldMap.insert (std::make_pair("rows",ROWS)); fieldMap.insert (std::make_pair("scan_number",SCAN_NUMBER)); fieldMap.insert (std::make_pair("sigma",SIGMA)); fieldMap.insert (std::make_pair("time",TIME)); fieldMap.insert (std::make_pair("times",TIMES)); fieldMap.insert (std::make_pair("u",U)); fieldMap.insert (std::make_pair("v",V)); fieldMap.insert (std::make_pair("w",W)); fieldMap.insert (std::make_pair("ut",UT)); fieldMap.insert (std::make_pair("uvw",UVW)); fieldMap.insert (std::make_pair("uvdist",UVDIST)); fieldMap.insert (std::make_pair("weight",WEIGHT)); // Assure all fields are defined. AlwaysAssert (fieldMap.size() == NUMBER_KEYWORDS, AipsError); return fieldMap; } Block MSSelectionKeywords::initReverseMap() { std::map& fieldMap = getMap(); Block reverseMap(NUMBER_KEYWORDS); for (const auto& x : fieldMap) { AlwaysAssert (x.second < NUMBER_KEYWORDS, AipsError); reverseMap[x.second] = x.first; } return reverseMap; } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSSel/MSSelectionKeywords.h000066400000000000000000000200601476623553700210120ustar00rootroot00000000000000//# MSSelectionKeywords.h: selection keywords for the MS //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSELECTIONKEYWORDS_H #define MS_MSSELECTIONKEYWORDS_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template class Block; // forward declare the class so we can typedef it class MSSelectionKeywords; class String; // Define a shorthand notation for this class, so enums can be specified // easily. typedef MSSelectionKeywords MSS; // // MSSelectionKeywords specifies selection keywords for the MeasurementSet // // // // // //
      • MeasurementSet //
      • MSSelector // // // // MSSelectionKeywords is a class that defines selection keywords // // // // This class is used to specify selections on a MeasurementSet. // It is a purely static class that just defines a mapping from // Strings to Enums, and provides these for use by classes like // MSSelector and MSRange // // // // // // // Selection keywords are needed for several classes, this class provides // them to all, avoiding duplication in each class. // // // //
      • //
      • // // // //
      • add this feature // class MSSelectionKeywords { public: // The fields in the MS for which selection and range operations are // defined. Some of these directly correspond to columns in the table, // others are derived quantities or columns in subtables. enum Field { // undefined field UNDEFINED=0, // the range of visibility amplitude AMPLITUDE, // the range of corrected vis amplitude CORRECTED_AMPLITUDE, // the range of model vis amplitude MODEL_AMPLITUDE, // the amplitude of the ratio corrected data/model data RATIO_AMPLITUDE, // the residual vis amplitude (corrected-model) RESIDUAL_AMPLITUDE, // the observed residual vis amplitude (observed-model) OBS_RESIDUAL_AMPLITUDE, // the list of antenna1 id values ANTENNA1, // the list of antenna2 id values ANTENNA2, // the list of antenna names ANTENNAS, // the list of array id values ARRAY_ID, // description of the data axes AXIS_INFO, // the channel frequencies, a vector for each selected spectral window CHAN_FREQ, // the list of polarizations present, this gives the String values CORR_NAMES, // the list of polarizations present, this gives the Stokes enum values CORR_TYPES, // the complex data DATA, // the complex corrected data CORRECTED_DATA, //the complex model data MODEL_DATA, // the ratio corrected data/model data RATIO_DATA, // the residual data (corrected - model) RESIDUAL_DATA, // the observed residual data (observed - model) OBS_RESIDUAL_DATA, // the list of dataDescription id values DATA_DESC_ID, // the list of feed1 id values FEED1, // the list of feed2 id values FEED2, // the list of field_id values FIELD_ID, // the list of field names FIELDS, // the flags FLAG, // the row flags FLAG_ROW, // a summary of flags (flag count summed over rows) FLAG_SUM, // the float data (optional single dish column) FLOAT_DATA, // Hour angle HA, // the list of interferometers (= 1000*ant1+ant2) present IFR_NUMBER, // the (range of the) imaginary part of the visibilities IMAGINARY, // the (range of the) imaginary part of the corrected visibilities CORRECTED_IMAGINARY, // the (range of the) imaginary part of the model visibilities MODEL_IMAGINARY, // the imaginary part of the ratio corrected data/model data RATIO_IMAGINARY, // the (range of the) imaginary part of the residual visibilities RESIDUAL_IMAGINARY, // the (range of the) imaginary part of the observed residual visibilities OBS_RESIDUAL_IMAGINARY, // Local Apparent Sidereal Time LAST, // the number of correlation products (polarizations) for selected spectral window NUM_CORR, // the number of spectral channels for selected spectral window NUM_CHAN, // the (range of the) phase of the visibilities PHASE, // the (range of the) phase of the corrected visibilities CORRECTED_PHASE, // the (range of the) phase of the model visibilities MODEL_PHASE, // the phase of the ratio corrected data/model data RATIO_PHASE, // the (range of the) phase of the residual visibilities RESIDUAL_PHASE, // the (range of the) phase of the observed residual visibilities OBS_RESIDUAL_PHASE, // the phase center direction for each field (matrix + epoch) PHASE_DIR, // the (range of the) real part of the visibilities REAL, // the (range of the) real part of the corrected visibilities CORRECTED_REAL, // the (range of the) real part of the model visibilities MODEL_REAL, // the real part of the ratio corrected data/model data RATIO_REAL, // the real part of the residual visibilities (corrected-model) RESIDUAL_REAL, // the real part of the observed residuals (observed-model) OBS_RESIDUAL_REAL, // the reference frequency for selected spectral window (or vector with all) REF_FREQUENCY, // the list of row numbers in the original MS ROWS, // the list of scan_number values SCAN_NUMBER, //# the list of spectral window id values //# SPECTRAL_WINDOW_ID, // the per spectrum sigmas SIGMA, // the range of times TIME, // the list of time values TIMES, // UT time (seconds of current day) UT, // the uvw coordinates UVW, // the (range of the) U coordinate (m) //# Note:order of U, V and W is important, no intervening items allowed //# without changing select() code. U, // the (range of the) V coordinate (m) V, // the (range of the) W coordinate (m) W, // the (range of the) UV-distance (m) UVDIST, // the weights WEIGHT, // Number of keywords NUMBER_KEYWORDS }; // convert a keyword string to the corresponding enum static Field field(const String& keyword); // convert an enum value to the corresponding keyword string static const String& keyword(Field field); private: // This class is purely static, no instances are allowed. MSSelectionKeywords(); MSSelectionKeywords(const MSSelectionKeywords& other); MSSelectionKeywords& operator=(const MSSelectionKeywords& other); // Get the static map. static std::map& getMap(); // Get the static reverse map. static Block& getReverseMap(); // Create an initialized map. static std::map initMap(); // Create an initialized reverse map. static Block initReverseMap(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSSelectionTools.cc000066400000000000000000000267431476623553700204570ustar00rootroot00000000000000//# MSSelectionTools.h: Classes to hold results from antenna grammar parser //# Copyright (C) 1994,1995,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // //---------------------------------------------------------------------------- // Vector set_intersection(const Vector& v1, const Vector& v2) { Vector loc; Bool found=False; Int n1=v1.nelements(), n2=v2.nelements(); for(Int i=0;i set_union(const Vector& v1, const Vector& v2) { Vector loc; Bool found=False; loc = v2; Int n1=v1.nelements(),n2; for(Int i=0;ireset2(ms,MSSelection::PARSE_NOW, timeExpr,antennaExpr,fieldExpr,spwExpr, uvDistExpr,taQLExpr,polnExpr,scanExpr,arrayExpr, stateExpr, obsExpr, feedExpr); // // Apply the internal accumulated TEN to the MS and produce the // selected MS. // // If the accumulated TEN is NULL, this returns False. Else // return True. // rstat = mss->getSelectedMS(selectedMS,outMSName); } catch (std::exception& x) { if (mymss==NULL) delete mss; throw; } if (mymss==NULL) delete mss; return rstat; } // //---------------------------------------------------------------------------- // Bool mssSetData(const MeasurementSet& ms, MeasurementSet& selectedMS, Vector >& chanSlices, Vector >& corrSlices, const String& outMSName, const String& timeExpr, const String& antennaExpr, const String& fieldExpr, const String& spwExpr, const String& uvDistExpr, const String& taQLExpr, const String& polnExpr, const String& scanExpr, const String& arrayExpr, const String& stateExpr, const String& obsExpr, const Int defaultChanStep, MSSelection *mymss ) { return mssSetData2(ms, selectedMS, chanSlices, corrSlices, outMSName, timeExpr, antennaExpr, fieldExpr, spwExpr, uvDistExpr, taQLExpr, polnExpr, scanExpr, arrayExpr, stateExpr, obsExpr, "", defaultChanStep, mymss); } Bool mssSetData2(const MeasurementSet& ms, MeasurementSet& selectedMS, Vector >& chanSlices, Vector >& corrSlices, const String& outMSName, const String& timeExpr, const String& antennaExpr, const String& fieldExpr, const String& spwExpr, const String& uvDistExpr, const String& taQLExpr, const String& polnExpr, const String& scanExpr, const String& arrayExpr, const String& stateExpr, const String& obsExpr, const String& feedExpr, const Int defaultChanStep, MSSelection *mymss ) { // // Parse the various expressions and produce the accmuluated TEN // internally. // MSSelection *mss=mymss; Bool rstat; if (mss == NULL) mss = new MSSelection(); try { mss->reset2(ms,MSSelection::PARSE_NOW, timeExpr,antennaExpr,fieldExpr,spwExpr, uvDistExpr,taQLExpr,polnExpr,scanExpr,arrayExpr, stateExpr, obsExpr, feedExpr); // // Apply the internal accumulated TEN to the MS and produce the // selected MS. // // If the accumulated TEN is NULL, this returns False. Else // return True. // rstat = mss->getSelectedMS(selectedMS,outMSName); // Get in-row selection info mss->getChanSlices(chanSlices,&ms,defaultChanStep); mss->getCorrSlices(corrSlices,&ms); } catch (std::exception& x) { if (mymss == NULL) delete mss; throw; } if (mymss == NULL) delete mss; return rstat; } // //---------------------------------------------------------------------------- // String stripWhite(const String& str, Bool onlyends) { //if ((str == "" ) || (str.length() <=0)) return str; Int j0,j1; j0=0;j1=str.length()-1; if (onlyends) { while((j0 <= j1) && (str[j0] == ' ')) j0++; while((j1 >= j0) && (str[j1] == ' ')) j1--; } return str.substr(j0,j1-j0+1); } // //---------------------------------------------------------------------------- // Record mssSelectedIndices(MSSelection& thisSelection, const MeasurementSet *ms) { Record retval; TableExprNode exprNode=thisSelection.toTableExprNode(ms); Vector fieldlist=thisSelection.getFieldList(); Vector spwlist=thisSelection.getSpwList(); Vector scanlist=thisSelection.getScanList(); Vector antenna1list=thisSelection.getAntenna1List(); Vector antenna2list=thisSelection.getAntenna2List(); Matrix chanlist=thisSelection.getChanList(); Matrix baselinelist=thisSelection.getBaselineList(); Vector ddIDList=thisSelection.getDDIDList(); Vector spwDDIDList=thisSelection.getSPWDDIDList(); Vector stateIDList=thisSelection.getStateObsModeList(); Vector observationIDList=thisSelection.getObservationList(); Vector feed1List=thisSelection.getFeed1List(); Vector feed2List=thisSelection.getFeed2List(); Vector feedPairList=thisSelection.getFeedPairList(); std::map > polMap=thisSelection.getPolMap(); std::map > > corrMap=thisSelection.getCorrMap(); Vector allDDIDList; if (ddIDList.nelements() == 0) allDDIDList = spwDDIDList; else if (spwDDIDList.nelements() == 0) allDDIDList = ddIDList; else allDDIDList = set_intersection(ddIDList, spwDDIDList); retval.define("spw", spwlist); retval.define("field", fieldlist); retval.define("scan",scanlist); retval.define("antenna1", antenna1list); retval.define("antenna2", antenna2list); retval.define("baselines",baselinelist); retval.define("channel", chanlist); retval.define("poldd",ddIDList); retval.define("spwdd",spwDDIDList); retval.define("dd",allDDIDList); retval.define("stateid",stateIDList); retval.define("observationid",observationIDList); retval.define("feed1",feed1List); retval.define("feed2",feed2List); retval.define("feedpairs",feedPairList); return retval; } // //---------------------------------------------------------------------------- // int tokenize(const String& str, const String& sep, Vector& tokens, Bool upcase) { String tmpStr(str); /* String::size_type tokpos,startpos=0; */ if (upcase) tmpStr.upcase(); char *sep_p=(char *)sep.c_str(); char *tok=strtok((char *)tmpStr.c_str(), sep_p); if (tok) { tokens.resize(1); tokens(0)=tok; while((tok=strtok((char*)NULL,sep_p))) { tokens.resize(tokens.nelements()+1,True); tokens(tokens.nelements()-1)=stripWhite(String(tok),True).c_str(); } } else {tokens.resize(1); tokens(0)=tmpStr;} return tokens.nelements(); /* while ((tokpos=tmpStr.index(sep,startpos))) { tokens.resize(tokens.nelements()+1,True); if (tokpos==String::npos) tokens(tokens.nelements()-1)=tmpStr.after(startpos-1); else tokens(tokens.nelements()-1)=tmpStr.before(sep,startpos); if (tokpos==String::npos) break; startpos=tokpos+1; } return (int)(tokens.nelements()); */ } // //---------------------------------------------------------------------------- // Split a give string at delimiter delim and return the restul elems. // Vector &split(const String &s, char delim, Vector &elems) { std::stringstream ss(s); std::string item; vector tmp; while(std::getline(ss, item, delim)) tmp.push_back(item); elems.resize(tmp.size()); for (uInt i=0;i 0)) { selectedTab = Table((baseTab)(fullTEN)); // If the TEN was not NULL and at least one expression was // non-blank, and still resulted in a zero selected rows. if (selectedTab.nrow() == 0) throw(MSSelectionNullSelection("MSSelectionNullSelection : The selected table has zero rows.")); if (outName!="") selectedTab.rename(outName,Table::New); selectedTab.flush(); newRefTab=True; } return newRefTab; } } casacore-3.7.1/ms/MSSel/MSSelectionTools.h000066400000000000000000000107471476623553700203160ustar00rootroot00000000000000//# MSSelectionTools.h: Classes to hold results from antenna grammar parser //# Copyright (C) 1994,1995,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSELECTIONTOOLS_H #define MS_MSSELECTIONTOOLS_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Get the intersection or union of IDs (such as FieldId). Vector set_intersection(const Vector& v1, const Vector& v2); Vector set_union(const Vector& v1, const Vector& v2); // Collective selection returning a selected MS. Bool mssSetData(const MeasurementSet& ms, MeasurementSet& selectedMS, const String& outMSName="", const String& timeExpr="", const String& antennaExpr="", const String& fieldExpr="", const String& spwExpr="", const String& uvDistExpr="", const String& taQLExpr="", const String& polnExpr="", const String& scanExpr="", const String& arrayExpr="", const String& stateExpr="", const String& obsExpr="", MSSelection *mss=NULL ); // Added feedExpr Bool mssSetData2(const MeasurementSet& ms, MeasurementSet& selectedMS, const String& outMSName="", const String& timeExpr="", const String& antennaExpr="", const String& fieldExpr="", const String& spwExpr="", const String& uvDistExpr="", const String& taQLExpr="", const String& polnExpr="", const String& scanExpr="", const String& arrayExpr="", const String& stateExpr="", const String& obsExpr="", const String& feedExpr="", MSSelection *mss=NULL ); // Collective selection also returning in-row (corr/chan) slices Bool mssSetData(const MeasurementSet& ms, MeasurementSet& selectedMS, Vector >& chanSlices, Vector >& corrSlices, const String& outMSName="", const String& timeExpr="", const String& antennaExpr="", const String& fieldExpr="", const String& spwExpr="", const String& uvDistExpr="", const String& taQLExpr="", const String& polnExpr="", const String& scanExpr="", const String& arrayExpr="", const String& stateExpr="", const String& obsExpr="", const Int defaultChanStep=1, MSSelection *mss=NULL ); // Added feedExpr Bool mssSetData2(const MeasurementSet& ms, MeasurementSet& selectedMS, Vector >& chanSlices, Vector >& corrSlices, const String& outMSName="", const String& timeExpr="", const String& antennaExpr="", const String& fieldExpr="", const String& spwExpr="", const String& uvDistExpr="", const String& taQLExpr="", const String& polnExpr="", const String& scanExpr="", const String& arrayExpr="", const String& stateExpr="", const String& obsExpr="", const String& feedExpr="", const Int defaultChanStep=1, MSSelection *mss=NULL ); Bool getSelectedTable(Table& selectedTab, const Table& baseTab, TableExprNode& fullTEN, const String& outName); Record mssSelectedIndices(MSSelection& mss, const MeasurementSet *ms); String stripWhite(const String& str, Bool onlyends=True); int tokenize(const String& str, const String& sep, Vector& tokens,Bool upCase=False); Vector &split(const String &s, char delim, Vector &elems); } #endif casacore-3.7.1/ms/MSSel/MSSelector.cc000066400000000000000000001571061476623553700172670ustar00rootroot00000000000000//# MSSelector.cc: selection and iteration of an MS //# Copyright (C) 1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSSelector::MSSelector():msIter_p(0),initSel_p(False),dataDescId_p(0), lastDataDescId_p(1,-1),useSlicer_p(False), haveSlicer_p(False),wantedOne_p(-1),convert_p(False), useIfrDefault_p(True) { } MSSelector::MSSelector(MeasurementSet& ms):ms_p(ms), selms_p(ms),savems_p(ms), msIter_p(0),initSel_p(False), dataDescId_p(0), lastDataDescId_p(1,-1), useSlicer_p(False), haveSlicer_p(False), wantedOne_p(-1),convert_p(False), useIfrDefault_p(True) { } MSSelector::MSSelector(const MSSelector& other):msIter_p(0) { operator=(other);} MSSelector& MSSelector::operator=(const MSSelector& other) { if (this==&other) return *this; ms_p=other.ms_p; selms_p=other.selms_p; savems_p=other.savems_p; lastDataDescId_p=other.lastDataDescId_p; if (msIter_p) delete msIter_p; msIter_p = 0; if (other.msIter_p) msIter_p=new MSIter(*other.msIter_p); initSel_p=other.initSel_p; dataDescId_p=other.dataDescId_p; useSlicer_p=other.useSlicer_p; haveSlicer_p=other.haveSlicer_p; slicer_p=other.slicer_p; wantedOne_p=other.wantedOne_p; convert_p=other.convert_p; useIfrDefault_p=other.useIfrDefault_p; return *this; } MSSelector::~MSSelector() { if (msIter_p) delete msIter_p; msIter_p=0; } void MSSelector::setMS(MeasurementSet& ms) { ms_p=ms; selms_p=ms; savems_p=ms; if (msIter_p) delete msIter_p; msIter_p=0; initSel_p=False; dataDescId_p=-1; useSlicer_p=False; haveSlicer_p=False; wantedOne_p=-1; convert_p=False; useIfrDefault_p=True; } Bool MSSelector::initSelection(const Vector& dataDescId, Bool reset) { LogIO os; // first check if we want to throw all selections away & return the pristine // MeasurementSet if (reset) { selms_p=ms_p; initSel_p=False; dataDescId_p.resize(0); lastDataDescId_p.resize(0); useSlicer_p=False; polSlice_p=Slice(); chanSlice_p=Slice(); haveSlicer_p=False; wantedOne_p=-1; convert_p=False; useIfrDefault_p=True; return True; } // check if we can reuse the saved selection if (initSel_p && dataDescId.nelements()==lastDataDescId_p.nelements() && allEQ(dataDescId,lastDataDescId_p)) { selms_p=savems_p; return True; } else { // undo all previous selections selms_p=ms_p; ifrSelection_p.resize(0); rowIndex_p.resize(0,0); } // selection on data description is optional Bool constantShape=True; if (ms_p.dataDescription().nrow()<=1) { if (dataDescId.nelements()>1) { os << LogIO::NORMAL << "data desc id selection ignored, " "there is only one" << LogIO::POST; } } else { if (dataDescId.nelements()>0 && dataDescId(0)!=-1) { selms_p=selms_p(selms_p.col(MS::columnName(MS::DATA_DESC_ID)) .in(dataDescId)); } } // check if the data shape is the same for all data // if not: select first data desc id only ScalarColumn dd(selms_p,MS::columnName(MS::DATA_DESC_ID)); MSDataDescColumns ddc(selms_p.dataDescription()); Vector ddId=dd.getColumn(); Int ndd=GenSort::sort(ddId, Sort::Ascending, Sort::HeapSort | Sort::NoDuplicates); ddId.resize(ndd,True); dataDescId_p.resize(ndd); dataDescId_p=ddId; MSSpWindowColumns spwc(ms_p.spectralWindow()); MSPolarizationColumns polc(ms_p.polarization()); spwId_p.resize(ndd); polId_p.resize(ndd); for (Int i=0; i0); if (!initSel_p) { os<< LogIO::WARN << "Selected Table has zero rows"< pols=polc.corrType()(polId_p(0)); Vector polSel(pols.nelements()); for (uInt i=0; i pols2=polc.corrType()(polId_p(i)); for (uInt j=0; j ddIds; return initSelection(ddIds,reset); } Bool MSSelector::selectChannel(Int nChan, Int start, Int width, Int incr) { LogIO os; if (!checkSelection()) return False; if (selms_p.nrow()==0) { os << LogIO::WARN << " Selected Table is empty - use selectinit" << LogIO::POST; return False; } Bool ok = (nChan>0 && start>=0 && width>0 && incr>0); if (!ok) { os << LogIO::SEVERE << "Illegal channel selection"<0 || end<(numChan-1) || incr>1 ) { if (width==1) { // width is one, we can use a stride chanSlice_p=Slice(start,nChan,incr); } else { chanSlice_p=Slice(start,1+(nChan-1)*incr+(width-1)); } } useSlicer_p=(!polSlice_p.all()||!chanSlice_p.all()); if (useSlicer_p) slicer_p=Slicer(polSlice_p,chanSlice_p); Int nSpW=spwId_p.nelements(); Matrix chanFreq = msc.spectralWindow().chanFreq().getColumnCells(RefRows(RowNumbers(spwId_p))); Matrix bandwidth = msc.spectralWindow().resolution().getColumnCells(RefRows(RowNumbers(spwId_p))); for (Int i=0; i1) { chanFreq_p(j,i)/=n; } bandwidth_p(j,i)=bandwidth(start+incr*j,i); if (n>1) { // Not correct if there are gaps between channels bandwidth_p(j,i)=(bandwidth(start,i)+bandwidth(start+incr*j+width-1,i))/2 + abs(chanFreq(start+incr*j+width-1,i)-chanFreq(start+incr*j,i)); } } } os << LogIO::DEBUG1 << "Channel selection: #chan="< wanted(n); for (Int i=0; i inputPol=mspol.corrType()(polId_p(0)); // check if wanted is just a subset or permutation of inputPol subSet_p=True; for (Int j=0; j id = items.asArrayInt(RecordFieldId(i)); if (id.nelements()>0) { if (oneBased) id-=1; if (id.nelements()==1) { selms_p=selms_p(selms_p.col(column) == id(0)); } else { selms_p=selms_p(selms_p.col(column).in(id)); } } else { os<< LogIO::WARN << "Illegal value for item "< ifrNum = items.asArrayInt(RecordFieldId(i)); // if (GlishArray(items.get(i)).get(ifrNum)) { if (ifrNum.nelements()>0) { // check input values for validity, squeeze out illegal values Int nAnt=selms_p.antenna().nrow(); if (oneBased) ifrNum-=1001; for (uInt k=0; k= nAnt || ifrNum(k)%1000<0 || ifrNum(k)%1000>=nAnt) { for (uInt j=k; j rows = items.toArrayInt64(RecordFieldId(i)); if (rows.nelements()>0) { if (oneBased) rows-=Int64(1); Vector uRows(rows.size()); convertArray(uRows,rows); // Select rows from the base table. // This is consistent with the rownumbers returned by range. selms_p=ms_p(uRows); } } break; case MSS::TIME: { Vector range = items.asArrayDouble(RecordFieldId(i)); if (range.nelements()==2) { TableExprNodeSetElem elem(True,range(0),range(1),True); TableExprNodeSet set; set.add(elem); selms_p=selms_p(selms_p.col(column).in(set)); } else { os << LogIO::WARN << "Illegal value for time range: "<< "two element numeric vector required"< time = items.asArrayDouble(RecordFieldId(i)); if (time.nelements()>0) { if (time.nelements()==1) { selms_p=selms_p(selms_p.col(column) == time(0)); } else { selms_p=selms_p(selms_p.col(column).in(time)); } } } break; case MSS::U: case MSS::V: case MSS::W: { Int uvwIndex=fld-MSS::U; Vector range = items.asArrayDouble(RecordFieldId(i)); if (range.nelements()==2) { column=MS::columnName(MS::UVW); TableExprNodeSet interval; interval.add (TableExprNodeSetElem (True, range(0), range(1), True)); IPosition idx(1,uvwIndex); selms_p=selms_p(selms_p.col(column)(idx).in(interval)); } else { os << LogIO::WARN << "Illegal value for u, v, w range: "<< "two element numeric vector required"< range = items.asArrayDouble(RecordFieldId(i)); if (range.nelements()==2) { range*=range; // square ArrayColumn uvwcol(selms_p,MS::columnName(MS::UVW)); Int nrow=selms_p.nrow(); if (nrow>0) { Matrix uvw=uvwcol.getColumn(); Block rowsel(nrow); for (Int k=0; k= range(0) || near(uvdist,range(0)) ) && ( uvdist <= range(1) || near(uvdist,range(1)) ) ); } selms_p=selms_p(rowsel); } } else { os << LogIO::WARN << "Illegal value for uvdist range: "<< "two element numeric vector required"<& vec) { // "average" a vector of integer id's - replace by length 1 vector with // common value if all elements are the same, or -1 otherwise. // Used below to "average" things like antenna id. if (vec.nelements()>1) { Int aver=vec(0); if (!allEQ(vec,aver)) aver=-1; vec.resize(1); vec(0)=aver; } } static void averageDouble(Vector& vec) { // average a vector of doubles Int n=vec.nelements(); if (n>1) { Double aver=vec(0); for (Int i=1; i& items, Bool ifrAxis, Int ifrAxisGap, Int inc, Bool average, Bool oneBased) { LogIO os; Record out(RecordInterface::Variable); if (!checkSelection()) return out; if (selms_p.nrow()==0) { os << LogIO::WARN << " Selected Table is empty - use selectinit" << LogIO::POST; return out; } Matrix want(nFuncType,nDataType,False); Bool wantFlag, wantFlagSum, wantWeight, wantSigma; wantFlag=wantFlagSum=wantWeight=wantSigma=False; Matrix uvw; Int nItems=items.nelements(),nRows=selms_p.nrow(); Table tab; if (inc>1 && inc<=nRows) { Vector rows(nRows/inc); indgen(rows,rownr_t(0),rownr_t(inc)); tab=selms_p(rows); } else { tab=selms_p; } MSColumns msc(tab); Int nIfr = ifrSelection_p.nelements(); Int nSlot = 0; Int nRow = tab.nrow(); if (ifrAxis && (nIfr==0 || useIfrDefault_p)) { // set default MSRange msRange(tab); ifrSelection_p.resize(); ifrSelection_p = msRange.range(MSS::IFR_NUMBER).asArrayInt(0); // GlishArray(msRange.range(MSS::IFR_NUMBER).get(0)).get(ifrSelection_p); nIfr = ifrSelection_p.nelements(); useIfrDefault_p=True; } Vector ifrIndex; // the index no into the ifrSelection Vector Vector timeSlot; // the time for each slot Vector ddSlot; // the dataDescId for each slot Vector slot; // the slot for each row Bool doIfrAxis = (ifrAxis && nIfr>0); if (doIfrAxis) { if (ifrAxisGap>=0) { // add a small gap before each antenna1 change Int gapCount=0; for (Int i=1; iifrSelection_p(i-1)/1000) gapCount++; } ifrAxis_p.resize(nIfr+gapCount*ifrAxisGap); ifrAxis_p(0)=ifrSelection_p(0); for (Int i=1,j=1; iifrSelection_p(i-1)/1000) { for (Int k=0; k time=msc.time().getColumn(); Vector dd=msc.dataDescId().getColumn(); nRow=time.nelements(); timeSlot.resize(nRow); ddSlot.resize(nRow); slot.resize(nRow); // the time/datadescid slot timeSlot(0)=time(0); ddSlot(0)=dd(0); slot(0)=0; for (Int i=1; i=0 && timeSlot(j)==timeSlot(nSlot) && ddSlot(j)!=dd(i)) j--; if (j<0 || (j>=0 && timeSlot(j)!=timeSlot(nSlot))) { // new data_desc_id for current time - add new slot nSlot++; timeSlot(nSlot)=time(i); ddSlot(nSlot)=dd(i); slot(i)=nSlot; } else { // we've seen this one before - reuse it slot(i)=j; } } else { slot(i)=nSlot; } } nSlot++; // resize to true size, copying values timeSlot.resize(nSlot,True); ddSlot.resize(nSlot,True); Vector ifr=msc.antenna1().getColumn(); Int maxAnt=max(ifr); ifr*=1000; { Vector ant2=msc.antenna2().getColumn(); maxAnt=max(maxAnt,max(ant2)); ifr+=ant2; } maxAnt++; ifrIndex.resize(nRow); Matrix ifrSlot(maxAnt,maxAnt,-1); for (Int i=0; i=0 && ant1 ant1(nIfr); ant1=ifrAxis_p; ant1/=1000; for (Int i=0; i ant=msc.antenna1().getColumn(); if (average) averageId(ant); if (oneBased) ant+=1; out.define(item,ant); } break; case MSS::ANTENNA2: if (doIfrAxis) { Vector ant2(nIfr); ant2=ifrAxis_p; ant2-=1000*(ifrAxis_p/1000); for (Int i=0; i ant= msc.antenna2().getColumn(); if (average) averageId(ant); if (oneBased) ant+=1; out.define(item,ant); } break; case MSS::AXIS_INFO: { Record axis_info(RecordInterface::Variable); // add info for the axes of the data array // 1. corr info (polarizations) axis_info.define("corr_axis",polSelection_p); // 2. freq info Record freq_axis(RecordInterface::Variable); freq_axis.define("chan_freq",chanFreq_p); freq_axis.define("resolution",bandwidth_p); axis_info.defineRecord("freq_axis",freq_axis); if (doIfrAxis) { // 3. ifr info Record ifr_axis(RecordInterface::Variable); if (oneBased) { Vector ifr; ifr=ifrAxis_p; ifr+=1001; for (Int i=0; i antName=msc.antenna().name().getColumn(); Vector ifrName(nIfr,""); Vector sName(nIfr,""); // get common prefix in antenna names by comparing first and // last antenna in table String prefix = common_prefix(antName(0),antName(antName.nelements()-1)); Matrix antPos=msc.antenna().position().getColumn(); Vector baseline(nIfr,-1.0); // PROBLEM: antName elements have string size extending beyond \0 // string catenation doesn't work correctly! for (Int k=0; k=0) { Int ant1 = ifrAxis_p(k)/1000; Int ant2 = ifrAxis_p(k)%1000; ifrName(k)=antName(ant1).before('\0'); ifrName(k)+="-"; ifrName(k)+=antName(ant2).before('\0'); if (prefix.length()>1) { sName(k)=String(antName(ant1).after(prefix)).before('\0'); sName(k)+="-"; sName(k)+=String(antName(ant2).after(prefix)).before('\0'); } else { sName(k)=ifrName(k); } baseline(k)=sqrt(square(antPos(0,ant2)-antPos(0,ant1))+ square(antPos(1,ant2)-antPos(1,ant1))+ square(antPos(2,ant2)-antPos(2,ant1))); } } ifr_axis.define("ifr_name",ifrName); ifr_axis.define("ifr_shortname",sName); ifr_axis.define("baseline",baseline); axis_info.defineRecord("ifr_axis",ifr_axis); // 4. time info Record time_axis(RecordInterface::Variable); msd_p.setAntennas(msc.antenna()); Vector time=msc.time().getColumn(); Vector fieldId=msc.fieldId().getColumn(); Int lastFieldId=-1; Double startOfDay=( nSlot>0 ? C::day*int(time(0)/C::day) : 0); Int nT = (average ? 1 : nSlot); Vector times(nT,0.0),ut(nT),ha(nT),last(nT); MEpoch ep=msc.timeMeas()(0); Bool doUT=False, doHA=False, doLAST=False; for (Int k=0; k0) times(0)/=nTimes; } else { times=timeSlot; } for (Int k=0; k0) msd_p.setFieldCenter(msc.field(). phaseDirMeas(curFieldId,times(k))); ep.set(MVEpoch(times(k)/C::day)); msd_p.setEpoch(ep); if (doHA) ha(k)=msd_p.hourAngle()/(2.0*M_PI)*C::day; if (doLAST) last(k)=msd_p.last().getValue().get(); } } time_axis.define("MJDseconds",times); if (doUT) time_axis.define("UT",ut); if (doHA) time_axis.define("HA",ha); if (doLAST) time_axis.define("LAST",last); axis_info.defineRecord("time_axis",time_axis); } out.defineRecord("axis_info",axis_info); } break; case MSS::DATA: case MSS::CORRECTED_DATA: case MSS::MODEL_DATA: case MSS::RATIO_DATA: case MSS::RESIDUAL_DATA: case MSS::OBS_RESIDUAL_DATA: want(Data,fld-MSS::DATA)=True; break; case MSS::DATA_DESC_ID: if (doIfrAxis) { Vector id(nSlot); id=ddSlot; if (average) averageId(id); if (oneBased) id+=1; out.define(item,id); } else { Vector col=msc.dataDescId().getColumn(); if (average) averageId(col); if (oneBased) col+=1; out.define(item,col); } break; case MSS::FIELD_ID: case MSS::SCAN_NUMBER: { Vector col = (fld == MSS::FIELD_ID ? msc.fieldId().getColumn() : msc.scanNumber().getColumn()); if (doIfrAxis) { Vector id(nSlot,-1); for (Int k=0; k col = (fld == MSS::FEED1 ? msc.feed1().getColumn() : msc.feed2().getColumn()); if (doIfrAxis) { Vector id(nSlot,-1); for (Int k=0; k itFlag(nIfr,nSlot); itFlag.set(True); // flag unfilled slots Vector flagRow=msc.flagRow().getColumn(); for (Int k=0; k ifr; ifr=ifrAxis_p; ifr+=1001; for (Int i=0; i ant1=msc.antenna1().getColumn(); Array ant2=msc.antenna2().getColumn(); if (oneBased) { ant1+=1; ant2+=1; } ant1*=1000; ant1+=ant2; if (average) averageId(ant1); out.define(item,ant1); } } break; case MSS::IMAGINARY: case MSS::CORRECTED_IMAGINARY: case MSS::MODEL_IMAGINARY: case MSS::RATIO_IMAGINARY: case MSS::RESIDUAL_IMAGINARY: case MSS::OBS_RESIDUAL_IMAGINARY: want(Imag,fld-MSS::IMAGINARY)=True; break; case MSS::FLOAT_DATA: want(Data,ObsFloat)=True; break; case MSS::PHASE: case MSS::CORRECTED_PHASE: case MSS::MODEL_PHASE: case MSS::RATIO_PHASE: case MSS::RESIDUAL_PHASE: case MSS::OBS_RESIDUAL_PHASE: want(Phase,fld-MSS::PHASE)=True; break; case MSS::REAL: case MSS::CORRECTED_REAL: case MSS::MODEL_REAL: case MSS::RATIO_REAL: case MSS::RESIDUAL_REAL: case MSS::OBS_RESIDUAL_REAL: want(Real,fld-MSS::REAL)=True; break; case MSS::SIGMA: { Matrix sig = getWeight(msc.sigma(),True); Int nCorr=sig.shape()(0); if (doIfrAxis) { IPosition wtsidx(3,nCorr,nIfr,nSlot); Cube wts(wtsidx); wts.set(0); for (Int i=0; i sumsig(sumwtidx); sumsig=0; for (Int i=0; i sumsig(nCorr); sumsig=0.0; for (Int j=0; j times; times=timeSlot; if (average) averageDouble(times); out.define(item,times); } else { Vector time=msc.time().getColumn(); if (average) averageDouble(time); out.define(item,time); } } break; case MSS::UVW: /* CAS-3211: the following if statement seems to be useless here. * It prevents uvw from being filled when nelements()>0 and writes * garbage to the uvw matrix. */ // if (uvw.nelements()==0) uvw=msc.uvw().getColumn(); uvw=msc.uvw().getColumn(); if (doIfrAxis) { Cube uvw2(uvw.shape()(0),nIfr,nSlot); uvw2.set(0); for (Int k=0; k uvw2(nIfr,nSlot); uvw2.set(0); for (Int k=0; k u2(uvw.row(0)),v2(uvw.row(1)); u2*=u2; v2*=v2; u2+=v2; // take square root - could use u2.apply(sqrt) but this can cause // link conflicts with fortran Bool deleteIt; Double* pu2 = u2.getStorage(deleteIt); for (Int i=0; i uvd(nIfr,nSlot); uvd.set(0); for (Int k=0; k flags,dataflags; Array weights; if (wantWeight || average) { Matrix wt = getWeight(msc.weight()); Int nCorr=wt.shape()(0); if (doIfrAxis) { IPosition wtsidx(3,nCorr,nIfr,nSlot); Cube wts(wtsidx); wts.set(0); for (Int i=0; i sumwt(sumwtidx); sumwt=0; for (Int i=0; i sumwt(nCorr); sumwt=0.0; for (Int i=0; i flag; if (wantFlag || wantFlagSum || average || // time averaging (chanSel_p.nelements()>0 && chanSel_p(2)>1) // channel averaging ) { Array avFlag; flag=getAveragedFlag(avFlag,msc.flag()); uInt nPol=avFlag.shape()(0), nChan=avFlag.shape()(1), nRow=avFlag.shape()(2); if (doIfrAxis) { MSSelUtil2::reorderData(avFlag,ifrIndex,nIfr,slot,nSlot,True); } if (average) flags=avFlag; if (wantFlag && !average) { out.define("flag",avFlag); } if (wantFlagSum) { if (doIfrAxis) { Cube flagSum(nPol,nChan,nIfr); flagSum=0; IPosition indx(4); indx(0)=0; for (uInt j=0; j flagSum(nPol,nChan); flagSum=0; Cube flag2(avFlag); for (uInt j=0; j fdata; if (!msc.floatData().isNull()) { getAveragedData(fdata,flag,msc.floatData()); if (doIfrAxis) MSSelUtil2:: reorderData(fdata,ifrIndex,nIfr,slot,nSlot,Float()); if (average) MSSelUtil2:: timeAverage(dataflags,fdata,flags,weights); out.define("float_data",fdata); } else { os << LogIO::WARN << "FLOAT_DATA column doesn't exist"<< LogIO::POST; } } Array observed_data,corrected_data,model_data; Bool keepObs = anyEQ(want.column(ObsResidual),True); Bool keepMod = anyEQ(want.column(Residual),True)|| anyEQ(want.column(Ratio),True)|| keepObs;; Bool keepCor = anyEQ(want.column(Residual),True)|| anyEQ(want.column(Ratio),True); for (Int dataType=Observed; dataType<=ObsResidual; dataType++) { if (anyEQ(want.column(dataType),True)|| (dataType==Observed && keepObs) || (dataType==Model && keepMod) || (dataType==Corrected && keepCor)) { if (convert_p && !subSet_p && dataType==Observed) { os << LogIO::WARN << "Polarization conversion of uncalibrated" << " data may give incorrect results"<< LogIO::POST; } // get the data if this is a data column ArrayColumn colData; Array data; if (dataType<=Model) { colData.reference( dataType == Observed ? msc.data() : (dataType == Corrected ? msc.correctedData() : msc.modelData())); if (colData.isNull()) { os << LogIO::WARN <<"Requested column doesn't exist"<:: reorderData(data,ifrIndex,nIfr,slot,nSlot,Complex()); } } String name; switch (dataType) { case Observed: if (keepObs) observed_data.reference(data); name=""; break; case Model: if (keepMod) model_data.reference(data); name="model_"; break; case Corrected: if (keepCor) corrected_data.reference(data); name="corrected_"; break; case Ratio: { LogicalArray mask(model_data!=Complex(0.)); data = corrected_data; data /= model_data(mask); data(!mask)=1.0; name="ratio_"; } break; case Residual: data = corrected_data; data -= model_data; name="residual_"; break; case ObsResidual: data = observed_data; data -= model_data; name="obs_residual_"; break; default:; } if (average) MSSelUtil2::timeAverage(dataflags,data,flags,weights); if (want(Amp,dataType)) out.define(name+"amplitude",amplitude(data)); if (want(Phase,dataType)) out.define(name+"phase",phase(data)); if (want(Real,dataType)) out.define(name+"real",real(data)); if (want(Imag,dataType)) out.define(name+"imaginary",imag(data)); if (want(Data,dataType)) out.define(name+"data",data); } } // only have averaged flags if some data item was requested as well if (average && wantFlag){ out.define("flag",dataflags); if (dataflags.nelements()==0) { os << LogIO::WARN <<"Flags not calculated because no DATA derived " "item was specified" << LogIO::POST; } } return out; } Bool MSSelector::putData(const Record& items) { LogIO os; if (!checkSelection()) return False; if (selms_p.nrow()==0) { os << LogIO::WARN << " Selected Table is empty - use selectinit" << LogIO::POST; return False; } if (!selms_p.isWritable()) { os << LogIO::SEVERE << "MeasurementSet is not writable"<< LogIO::POST; return False; } MSColumns msc(selms_p); Int n=items.nfields(); for (Int i=0; i& col = (fld==MSS::DATA ? msc.data() : (fld==MSS::CORRECTED_DATA ? msc.correctedData() : msc.modelData())); // averaging not supported if (chanSel_p(2)>1) { os << LogIO::SEVERE << "Averaging not supported when writing data" << LogIO::POST; break; } if (convert_p) { os << LogIO::SEVERE <<"Polarization conversion not supported " << "when writing data" << LogIO::POST; return False; } if (polIndex_p.nelements()>0) { os << LogIO::SEVERE << "Polarization selection must be 1,2 or " << "all correlations,"< data = items.toArrayComplex(RecordFieldId(i)); if (! col.isNull()) { if (data.ndim()==4) { if (data.shape()(2)==Int(rowIndex_p.nrow()) && data.shape()(3)==Int(rowIndex_p.ncolumn())) { MSSelUtil2::reorderData(data, rowIndex_p, selms_p.nrow()); } else { os << LogIO::SEVERE<<"Data shape inconsistent with " "current selection"<< LogIO::POST; break; } } if (data.ndim()==3) { if (useSlicer_p) col.putColumn(slicer_p, data); else col.putColumn(data); } } } break; case MSS::FLOAT_DATA: { // averaging not supported if (chanSel_p(2)>1) { os << LogIO::SEVERE << "Averaging not supported when writing data" << LogIO::POST; break; } if (convert_p) { os << LogIO::SEVERE <<"Polarization conversion not supported " << "when writing data" << LogIO::POST; return False; } Array data = items.toArrayFloat(RecordFieldId(i)); //if (GlishArray(items.get(i)).get(data)) { if (data.ndim()==4) { if (data.shape()(2)==Int(rowIndex_p.nrow()) && data.shape()(3)==Int(rowIndex_p.ncolumn())) { MSSelUtil2::reorderData(data, rowIndex_p, selms_p.nrow()); } else { os << LogIO::SEVERE<<"Data shape inconsistent with " "current selection"<< LogIO::POST; break; } } if (data.ndim()==3) { if (useSlicer_p) msc.floatData().putColumn(slicer_p, data); else msc.floatData().putColumn(data); } //} } break; case MSS::FLAG: { Array flag = items.toArrayBool(RecordFieldId(i)); // if (GlishArray(items.get(i)).get(flag)) { if (flag.ndim()==4) { if (flag.shape()(2)==Int(rowIndex_p.nrow()) && flag.shape()(3)==Int(rowIndex_p.ncolumn())) { MSSelUtil2::reorderData(flag, rowIndex_p, selms_p.nrow()); } else { os << LogIO::SEVERE<<"Flag shape inconsistent with " "current selection"<< LogIO::POST; break; } } if (flag.ndim()==3) { putAveragedFlag(flag, msc.flag()); } //} } break; case MSS::FLAG_ROW: { Array flagRow = items.toArrayBool(RecordFieldId(i)); // if (GlishArray(items.get(i)).get(flagRow)) { if (flagRow.ndim()==2) { reorderFlagRow(flagRow); } if (flagRow.ndim()==1) { msc.flagRow().putColumn(flagRow); } // } } break; case MSS::SIGMA: case MSS::WEIGHT: { Array weight = items.toArrayFloat(RecordFieldId(i)); // if (GlishArray(items.get(i)).get(weight)) { if (weight.ndim()==3) { reorderWeight(weight); } if (weight.ndim()==2) { if (fld == MSS::SIGMA) msc.sigma().putColumn(weight); if (fld == MSS::WEIGHT) msc.weight().putColumn(weight); } // } } break; case MSS::UNDEFINED: default: os << LogIO::WARN << "Unrecognized field in input ignored: "<< item<& columns, Double interval, rownr_t maxRows, Bool addDefaultSortColumns) { LogIO os; if (!checkSelection()) return False; if (selms_p.nrow()==0) { os << LogIO::WARN << " Selected Table is empty - use selectinit" << LogIO::POST; return False; } Int n=columns.nelements(); Block col(n); for (Int i=0; itable().nrow(); if (startRow_p==0 || startRow_p> nIterRow) { (*msIter_p)++; more=msIter_p->more(); if (more) nIterRow=msIter_p->table().nrow(); startRow_p = 0; } if (startRow_p>0 || (more && maxRow_p>0 && nIterRow>maxRow_p)) { rownr_t nRow=min(maxRow_p,nIterRow-startRow_p); selRows_p.resize(nRow); indgen(selRows_p,rownr_t(startRow_p),rownr_t(1)); startRow_p+=maxRow_p; selms_p=msIter_p->table()(selRows_p); more=True; } else { if (more) selms_p=msIter_p->table(); else selms_p=msIter_p->ms(); // put back the original selection at the end } } return more; } Bool MSSelector::iterOrigin() { Bool ok=False; if (msIter_p) { startRow_p=0; msIter_p->origin(); rownr_t nIterRow=msIter_p->table().nrow(); if (maxRow_p==0 || nIterRow<=maxRow_p) { selms_p=msIter_p->table(); } else { selRows_p.resize(maxRow_p); indgen(selRows_p,rownr_t(0),rownr_t(1)); selms_p=msIter_p->table()(selRows_p); startRow_p=maxRow_p; } ok=True; } return ok; } Bool MSSelector::iterEnd() { if (!msIter_p) return False; selms_p=msIter_p->ms(); return True; } void MSSelector::getAveragedData(Array& avData, const Array& flag, const ArrayColumn& col) const { getAveragedData(avData,flag,col,Slicer(Slice())); } void MSSelector::getAveragedData(Array& avData, const Array& flag, const ArrayColumn& col, const Slicer & rowSlicer) const { Array data; if (useSlicer_p) { data=col.getColumnRange(rowSlicer,slicer_p); } else { data=col.getColumnRange(rowSlicer); } Int nPol=data.shape()(0); Vector chanSel(chanSel_p); if (chanSel.nelements()==0) { // not yet initialized, set to default chanSel.resize(4); chanSel(0)=data.shape()(1); chanSel(1)=0; chanSel(2)=1; chanSel(3)=1; } Int nChan=chanSel(0); Int64 nRow=data.shape()(2); avData.resize(IPosition(3,nPol,nChan,nRow)); if (chanSel(2)==1) { // no averaging, just copy the data across avData=data; } else { // Average channel by channel Array mask(!flag); Array wt(flag.shape(),0.0f); wt(mask)=1.0; Array avWt(avData.shape(),0.0f); for (Int i=0; i1, the slice doesn't have an increment, so we take big steps Int chn=i*chanSel(3); IPosition is(3,0,i,0),ie(3,nPol-1,i,nRow-1), cs(3,0,chn,0),ce(3,nPol-1,chn,nRow-1); Array ref(avData(is,ie)); ref=Complex(0.0); Array wtref(avWt(is,ie)); // average over channels for (Int j=0; j mdata(data(cs,ce),mask(cs,ce)); ref+=mdata; wtref+=wt(cs,ce); } ref(wtref>Float(0.0))/=wtref(wtref>Float(0.0)); } } // do the polarization conversion or selection if (convert_p) { Array out; stokesConverter_p.convert(out,avData); avData.reference(out); } else if (polIndex_p.nelements()>0) { Int n=polIndex_p.nelements(); Array out(IPosition(3,n,nChan,nRow)); IPosition sp(3,0,0,0),ep(3,0,nChan-1,nRow-1); IPosition sav(3,0,0,0),eav(3,0,nChan-1,nRow-1); for (Int i=0; i& avData, const Array& flag, const ArrayColumn& col) const { getAveragedData(avData,flag,col,Slicer(Slice())); } void MSSelector::getAveragedData(Array& avData, const Array& flag, const ArrayColumn& col, const Slicer& rowSlicer) const { Array data; if (useSlicer_p) { data=col.getColumnRange(rowSlicer,slicer_p); } else { data=col.getColumnRange(rowSlicer); } Int nPol=data.shape()(0); Vector chanSel(chanSel_p); if (chanSel.nelements()==0) { // not yet initialized, set to default chanSel.resize(4); chanSel(0)=data.shape()(1); chanSel(1)=0; chanSel(2)=1; chanSel(3)=1; } Int nChan=chanSel(0); Int64 nRow=data.shape()(2); avData.resize(IPosition(3,nPol,nChan,nRow)); if (chanSel(2)==1) { // no averaging, just copy the data across avData=data; } else { // Average channel by channel Array mask(!flag); Array wt(flag.shape(),0.0f); wt(mask)=1.0; Array avWt(avData.shape(),0.0f); for (Int i=0; i1, the slice doesn't have an increment, so we take big steps Int chn=i*chanSel(3); IPosition is(3,0,i,0),ie(3,nPol-1,i,nRow-1), cs(3,0,chn,0),ce(3,nPol-1,chn,nRow-1); Array ref(avData(is,ie)); Array wtref(avWt(is,ie)); // average over channels for (Int j=0; j mdata(data(cs,ce),mask(cs,ce)); ref+=mdata; wtref+=wt(cs,ce); } ref(wtref>Float(0.0))/=wtref(wtref>Float(0.0)); } } // do the polarization conversion if (convert_p) { // Array out; // stokesConverter_p.convert(out,avData); // avData.reference(out); LogIO os; os << LogIO::WARN << "Polarization conversion for FLOAT_DATA " "not implemented" << LogIO::POST; } else if (polIndex_p.nelements()>0) { Int n=polIndex_p.nelements(); Array out(IPosition(3,n,nChan,nRow)); IPosition sp(3,0,0,0),ep(3,0,nChan-1,nRow-1); IPosition sav(3,0,0,0),eav(3,0,nChan-1,nRow-1); for (Int i=0; i MSSelector::getAveragedFlag(Array& avFlag, const ArrayColumn& col) const { return getAveragedFlag(avFlag,col,Slicer(Slice())); } Array MSSelector::getAveragedFlag(Array& avFlag, const ArrayColumn& col, const Slicer& rowSlicer) const { Array flag; if (useSlicer_p) { flag=col.getColumnRange(rowSlicer,slicer_p); } else { flag=col.getColumnRange(rowSlicer); } Int nPol=flag.shape()(0); Vector chanSel(chanSel_p); if (chanSel.nelements()==0) { // not yet initialized, set to default chanSel.resize(4); chanSel(0)=flag.shape()(1); chanSel(1)=0; chanSel(2)=1; chanSel(3)=1; } Int nChan=chanSel(0); Int64 nRow=flag.shape()(2); avFlag.resize(IPosition(3,nPol,nChan,nRow)); if (chanSel(2)==1) { // no averaging, just copy flags avFlag=flag; } else { avFlag=True; for (Int i=0; i ref(avFlag(is,ie)); // average over channels for (Int j=0; j out; stokesConverter_p.convert(out,avFlag); avFlag.reference(out); } else if (polIndex_p.nelements()>0) { Int n=polIndex_p.nelements(); Array out(IPosition(3,n,nChan,nRow)); IPosition sp(3,0,0,0),ep(3,0,nChan-1,nRow-1); IPosition sav(3,0,0,0),eav(3,0,nChan-1,nRow-1); for (Int i=0; i& avFlag, ArrayColumn& col) { Array polFlag=avFlag; Array out; Int n=polIndex_p.nelements(); Int64 nRow=avFlag.shape()(2); // check if we need to read the data before writing it back if (convert_p || (n>2 && n1 && chanSel_p(3)>chanSel_p(2))) { if (useSlicer_p) { out=col.getColumn(slicer_p); } else { out=col.getColumn(); } } if (convert_p) { stokesConverter_p.invert(out,polFlag); polFlag.reference(out); } if (chanSel_p(2)>1) { // we need to undo the averaging and distribute the flags IPosition shape=polFlag.shape(); shape(1)=(chanSel_p(0)-1)*chanSel_p(3)+chanSel_p(2); if (chanSel_p(3)<=chanSel_p(2)) { if (out.nelements()==0) out.resize(shape); } Int nChan=chanSel_p(0), st=chanSel_p(1), w=chanSel_p(2), inc=chanSel_p(3), nRow=shape(2); IPosition st1(3,0,st,0),st2(3,0,0,0),end1(3,shape(0)-1,st,nRow-1), end2(3,shape(0)-1,0,nRow-1); for (Int i=0; i0) { for (Int k=0; k0) { // need to rearrange polarizations Int nChan=chanSel_p(0); if (out.nelements()==0) out.resize(IPosition(3,n,nChan,nRow)); IPosition sp(3,0,0,0),ep(3,0,nChan-1,nRow-1); IPosition sav(3,0,0,0),eav(3,0,nChan-1,nRow-1); for (Int i=0; i MSSelector::getWeight(const ArrayColumn& wtCol, Bool sigma) const { Array wt; if (wantedOne_p>=0) { wt = wtCol.getColumn(Slicer(Slice(wantedOne_p,1))); } else { wt = wtCol.getColumn(); } // apply the stokes conversion/selection to the weights if (convert_p) { Matrix outwt; stokesConverter_p.convert(outwt,wt,sigma); wt.reference(outwt); } return wt; } // reorder from 2d to 1d (removing ifr axis) void MSSelector::reorderFlagRow(Array& flagRow) { Int nIfr=flagRow.shape()(0), nSlot=flagRow.shape()(1); rownr_t nRow=selms_p.nrow(); Bool deleteFlag, deleteRow; const Bool* pFlag=flagRow.getStorage(deleteFlag); const Int64* pRow=rowIndex_p.getStorage(deleteRow); Vector rowFlag(nRow); Int offset=0; for (Int i=0; i0) { rowFlag(k)=pFlag[offset+j]; } } } flagRow.freeStorage(pFlag,deleteFlag); rowIndex_p.freeStorage(pRow,deleteRow); flagRow.reference(rowFlag); } // reorder from 3d to 2d (removing ifr axis) void MSSelector::reorderWeight(Array& weight) { Int nCorr=weight.shape()(0), nIfr=weight.shape()(1), nSlot=weight.shape()(2); Int64 nRow=selms_p.nrow(); Bool deleteWeight, deleteRow, deleteRowWeight; const Float* pWeight=weight.getStorage(deleteWeight); const Int64* pRow=rowIndex_p.getStorage(deleteRow); Matrix rowWeight(nCorr, nRow); Float* pRowWeight=rowWeight.getStorage(deleteRowWeight); Int offset=0; for (Int i=0; i0) { Int wOffset = nCorr*offset; Int rwOffset = nCorr*k; for (Int c=0; c #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template class ArrayColumn; class Record; class MSIter; // // MSSelector specifies selections on a MeasurementSet // // // // // //
      • MeasurementSet //
      • Record // // // // MSSelector's main function is selection of data from a MeasurementSet // // // // This class is used to select and retrieve data from a MeasurementSet. // It allows selections on e.g., time, field, spectral window (all row based), // but also on channel and polarization (within a row). It can optionally // do polarization conversion, spectral averaging and time averaging on the // data retrieved and allows modified data to be written back to the Table. // This class also provides the DO interface to the MS Iterator. // The ms DO uses this class to allow these operations to be done from glish. // // // MSSelector msSelector(myMS); // // select data desc Id 1 // msSelector.initSelection(1); // Vector items(3); // // fill in some fields // items(0)="field_id"; // items(1)="time"; // items(2)="num_chan"; // // get the range of values for the items specified // MSRange msRange(msSelector.selectedTable(),msSelector.spectralWindow()); // Record range=msRange.range(items); // //.. change the ranges as needed // // now select with the new range // msSelector.select(range); // Int nchan=10, start=3, width=1, incr=2; // msSelector.selectChannel(nchan,start,width,incr) // // get out some data // Vector dataItems(3); // dataItems(0)="data"; // dataItems(1)="antenna1"; // dataItems(2)="antenna2"; // Record dataRec=msSelector.getData(items); // // // // // Selection from an MS is needed in various places. It makes sense to // provide a uniform interface for MS selection. // // // //
      • // // // //
      • provide access to all other columns in the MS? // class MSSelector { friend class MSRange; public: MSSelector(); // construct from an MS, the MS will supply the range of the various // parameters that can be selected on. explicit MSSelector(MeasurementSet& ms); // Copy constructor, this will initialize the MS with other's MS MSSelector(const MSSelector& other); // Assignment, this will initialize the MS with other's MS MSSelector& operator=(const MSSelector& other); ~MSSelector(); // Change or Set the MS this MSSelector refers to. void setMS(MeasurementSet& ms); // initialize the selection by specifying, optionally, // the DATA_DESC_IDs. // If you don't specify the dataDescIds and the data shape is constant // all data is selected, if the shape does change, only the first // dataDescId is selected. If you specify a number of dataDescIds // and they all have the same shape, they are all selected, otherwise // only the first is selected. The function returns false if // the selection was limited due to changing data shape. // Use the reset argument to return to the completely unselected ms. Bool initSelection(const Vector& dataDescIds, Bool reset=False); // As above without the data desc id argument Bool initSelection(Bool reset=False); // Return the data desc IDs selected Vector dataDescId() const; // Set the mapping from input channels in the DATA column to // output channels. nChan is the number of output channels, // start is the first channel to use, width specifies how wide a // block of channels to average, increment specifies the start of // the next block relative to the start of the current block. // Note: averaging uncalibrated data should be avoided (no bandpass applied) Bool selectChannel(Int nChan, Int start, Int width, Int incr); // Specify the output polarization. // Missing input polarizations are assumed to be zero. // This selection/conversion assumes that parallactic angle rotation // is taken care of elsewhere (i.e., results may only be correct for // CORRECTED_DATA and MODEL_DATA conversions, not for the observed DATA) Bool selectPolarization(const Vector& wantedPol); // Select the MS based on the selections present in the input record. // The format of this record is the same as that returned by range. // Not all possible items can be selected on, some are quietly ignored. // Correct for one-based indexing if oneBased is True. Bool select(const Record& items, Bool oneBased=False); // Select the MS based on the TaQL selection string Bool select(const String& msSelect); // Return the data for the items requested, all returned values // will be arrays, the last dimension of these is the table row number. // The data arrays are normally 3D with axes: polarization, frequency, row. // If ifrAxis is set to True, the data arrays returned will be 4D, with // the data being split out along an extra interferometer axis, the // axes will be: polarization, frequency, interferometer and time. // Missing interferometers will be marked flagged. // The order of the interferometers is that specified by the last // select call. // Add a (flagged) gap in the data at every antenna1 change if ifrAxisGap>0. // Use inc > 1 to return data from every inc'th row. // Use average=True to vector average the data along the row or time axis // taking the weights column into account (use selectChannel to average // channels together as well). Note that different interferometers will be // averaged together if ifrAxis is False. // Correct for one-based indexing if oneBased is True. Record getData(const Vector& items, Bool ifrAxis, Int ifrAxisGap=0, Int inc=1, Bool average=False, Bool oneBased=False); // Put the data for the items provided. Note that only fields corresponding // to actual table columns can be put (i.e., no AMPLITUDEs, IFR_NUMBERs etc) // The data will need to have the correct shape for the column and a last // dimension matching the number of selected rows (or last two dimensions // matching times and interferometers, for data retrieved with ifraxis=T) // Channel selection is supported, but the width parameter has to be 1. Bool putData(const Record& items); // Set up an iterator, iterating over the specified columns, with // optional time interval and maximum number of rows to return at once // (the default of zero returns all rows). To keep MSIter from adding // the default sort columns, specify addDefaultSortColumns=False Bool iterInit(const Vector& columns, Double interval, rownr_t maxRows=0, Bool addDefaultSortColumns=True); // Step the iterator, sets the selection to the current table iteration. // Returns false if there is no more data // and sets the selection back to the state before iteration started. Bool iterNext(); // (Re)Set the iterator to the first iteration, call this after iterInit. Bool iterOrigin(); // End the iteration (before reaching the last iteration) // and set the selection back to the state before iteration started. Bool iterEnd(); // Number of rows in selected table rownr_t nrow() const; // Return the selected table Table selectedTable() const; // Return the selection status of the table Bool selected() const; protected: // average and convert data void getAveragedData(Array& avData, const Array& flag, const ArrayColumn& col) const; // average and convert float data void getAveragedData(Array& avData, const Array& flag, const ArrayColumn& col) const; // average and convert data, with row Slicer void getAveragedData(Array& avData, const Array& flag, const ArrayColumn& col, const Slicer & rowSlicer) const; // average and convert float data, with row Slicer void getAveragedData(Array& avData, const Array& flag, const ArrayColumn& col, const Slicer & rowSlicer) const; // "average" flag, at present all output which has a flagged input is flagged Array getAveragedFlag(Array& avFlag, const ArrayColumn& col) const; // "average" flag, at present all output which has a flagged input is flagged, // with row Slicer Array getAveragedFlag(Array& avFlag, const ArrayColumn& col, const Slicer& rowSlicer) const; // "unaverage" flag, distribute the flags back to the channels that went // into the average void putAveragedFlag(const Array& avFlag, ArrayColumn& col); // get the weight, set sigma=True when retrieving sigma's Array getWeight(const ArrayColumn& wtCol, Bool sigma=False) const; // make the data slicer, pass in the first and the number of correlations // to select void makeSlicer(Int start, Int nCorr) const; // reorder from 2d to 1d (removing ifr axis) void reorderFlagRow(Array& flagRow); // reorder from 2d to 1d (removing ifr axis) void reorderWeight(Array& weight); // time average the input data, return new flags void timeAverage(Array& dataFlags, Array& data, const Array& flags, const Array& weights); // check if the data description selection has been done & do default // selection if not. Return False if the selection fails. Bool checkSelection(); private: // The function types enum {Amp,Phase,Real,Imag,Data,nFuncType}; // The data types enum {Observed,Corrected,Model,Ratio,Residual,ObsResidual,ObsFloat,nDataType}; MeasurementSet ms_p; // the original ms MeasurementSet selms_p; // the selected ms MeasurementSet savems_p; // the saved preselection MSIter* msIter_p; Bool initSel_p; Vector dataDescId_p, lastDataDescId_p; Vector spwId_p, polId_p; Vector chanSel_p; Bool useSlicer_p; mutable Bool haveSlicer_p; mutable Slicer slicer_p; Slice chanSlice_p,polSlice_p; Vector polIndex_p; Int wantedOne_p; Bool convert_p, subSet_p; StokesConverter stokesConverter_p; Vector polSelection_p; Vector ifrSelection_p,ifrAxis_p; Matrix chanFreq_p,bandwidth_p; MSDerivedValues msd_p; Matrix rowIndex_p; // mapping of rows to time and ifr slots RowNumbers selRows_p; // range of rows from selms_p returned by getData rownr_t startRow_p, maxRow_p; // start and length of range of rows Bool useIfrDefault_p; }; inline rownr_t MSSelector::nrow() const { return selms_p.nrow();} inline Vector MSSelector::dataDescId() const { return dataDescId_p;} inline Table MSSelector::selectedTable() const {return selms_p;} inline Bool MSSelector::selected() const {return initSel_p;} } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSSourceIndex.cc000066400000000000000000000142151476623553700177300ustar00rootroot00000000000000//# MSSourceIndex.cc: this defined MSSourceIndex //# Copyright (C) 2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSSourceIndex::MSSourceIndex() : MSTableIndex(), msSourceCols_p(0) {;} MSSourceIndex::MSSourceIndex(const MSSource &source) : MSTableIndex(source, stringToVector("SOURCE_ID,SPECTRAL_WINDOW_ID"), compare) { attachIds(); msSourceCols_p = new MSSourceColumns(source); } MSSourceIndex::MSSourceIndex(const MSSourceIndex &other) : MSTableIndex(other) { attachIds();} MSSourceIndex::~MSSourceIndex() { if (msSourceCols_p) delete msSourceCols_p; } MSSourceIndex &MSSourceIndex::operator=(const MSSourceIndex &other) { if (this != &other) { MSTableIndex::operator=(other); attachIds(); } return *this; } void MSSourceIndex::attach(const MSSource &source) { MSTableIndex::attach(source, stringToVector("SOURCE_ID,SPECTRAL_WINDOW_ID"), compare); attachIds(); } void MSSourceIndex::attachIds() { sourceId_p.attachToRecord(accessKey(), "SOURCE_ID"); spwId_p.attachToRecord(accessKey(), "SPECTRAL_WINDOW_ID"); } Vector MSSourceIndex::matchSourceName(const String& name) { // Match a source name to a set of source id's // Input: // name const String& Source name to match // Output: // matchSourceName Vector Matching source id's // Vector retval; if (!msSourceCols_p->isNull() && msSourceCols_p->nrow() > 0) { LogicalArray maskArray = (msSourceCols_p->name().getColumn()==name); MaskedArray maskSourceId(msSourceCols_p->sourceId().getColumn(), maskArray); retval = maskSourceId.getCompressedArray(); } return retval; } Vector MSSourceIndex::matchSourceCode(const String& code) { // Match a source code to a set of source id's // Input: // code const String& Source code to match // Output: // matchSourceCode Vector Matching source id's // Vector retval; if (!msSourceCols_p->isNull() && msSourceCols_p->nrow() > 0) { LogicalArray maskArray = (msSourceCols_p->code().getColumn()==code); MaskedArray maskSourceId(msSourceCols_p->sourceId().getColumn(), maskArray); retval = maskSourceId.getCompressedArray(); } return retval; } RowNumbers MSSourceIndex::getRowNumbersOfSourceID(const Int sid){ ColumnsIndex sidIndx(table(), MSSource::columnName(MSSource::SOURCE_ID)); RecordFieldPtr sourceId (sidIndx.accessKey(), MSSource::columnName(MSSource::SOURCE_ID)); *sourceId=sid; return sidIndx.getRowNumbers(); } Vector MSSourceIndex::matchSourceName(const Vector& names) { // Match a set of source names to a set of source id's // Input: // names const Vector& Source names to match // Output: // matchSourceNames Vector Matching source id's // Vector matchedSourceIds; // Match each source name individually for (uInt fld=0; fld currentMatch = matchSourceName(names(fld)); if (currentMatch.nelements() > 0) { Vector temp(matchedSourceIds); matchedSourceIds.resize(matchedSourceIds.nelements() + currentMatch.nelements(), True); matchedSourceIds = concatenateArray(temp, currentMatch); } } return matchedSourceIds; } Int MSSourceIndex::compare (const Block& fieldPtrs, const Block& dataPtrs, const Block& dataTypes, rownr_t index) { // this implementation has been adapted from the default compare function in // ColumnsIndex.cc. The support for data types other than Integer have been // removed, since, according to the constructor's documentation, the index // columns must be of integer type. At present, this is in practice true in // this case. A consequence of this simplified implementation is that is // supports a -1 value for all IDs, rather than just for SPECTRAL_WINDOW_ID; // since MS2 only allows a -1 value for SPECTRAL_WINDOW_ID, this should not // cause problems for users with valid MS2 datasets. uInt nfield = fieldPtrs.nelements(); for (uInt i=0; i*)(fieldPtrs[i])); const Int right = ((const Int*)(dataPtrs[i]))[index]; if (right != -1) { // consider -1 equal to any requested id if (left < right) { return -1; } else if (left > right) { return 1; } } } else { throw (TableError ("MSSourceIndex: non-Integer index type")); } } return 0; } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSSel/MSSourceIndex.h000066400000000000000000000070701476623553700175730ustar00rootroot00000000000000//# MSSourceIndex: index into a MeasurementSet SOURCE subtable //# Copyright (C) 2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSOURCEINDEX_H #define MS_MSSOURCEINDEX_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# forward declarations class MSSource; // // // // // // //
      • MeasurementSet //
      • MSTableIndex // // // // // // // // // // // // // // // //
      • //
      • // // class MSSourceIndex : public MSTableIndex { public: // no index attached, use the attach function or assignment operator to change that MSSourceIndex(); // construct one using the indicated SOURCE table MSSourceIndex(const MSSource &source); // construct one from another MSSourceIndex(const MSSourceIndex &other); virtual ~MSSourceIndex(); MSSourceIndex &operator=(const MSSourceIndex &other); void attach(const MSSource &source); // access to the source ID key, throws an exception if isNull() is False Int &sourceId() {return *sourceId_p;} // access to the spectral window ID key, throws an // exception if isNull() is False Int &spectralWindowId() {return *spwId_p;} // Match a source name or list of source names to a set of SOURCE_ID's Vector matchSourceName(const String& name); Vector matchSourceName(const Vector& names); //add for source code selection Vector matchSourceCode(const String& code); //Return rows matching a SourceID RowNumbers getRowNumbersOfSourceID(const Int sid); protected: // the specialized compare function to pass to the // ColumnsIndex object. This supports -1 // values for the SPECTRAL_WINDOW_ID static Int compare (const Block& fieldPtrs, const Block& dataPtrs, const Block& dataTypes, rownr_t index); private: // Pointer to local MSSourceColumns object MSSourceColumns* msSourceCols_p; RecordFieldPtr sourceId_p, spwId_p; void attachIds(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSSpWindowIndex.cc000066400000000000000000000142421476623553700202420ustar00rootroot00000000000000//# MSSpWindowIndex.cc: implementation of MSSpWindowIndex.h //# Copyright (C) 2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //------------------------------------------------------------------------- MSSpWindowIndex::MSSpWindowIndex(const MSSpectralWindow& spectralWindow) : msSpWindowCols_p(spectralWindow) { // Construct from an MS DATA_DESC subtable // Input: // spectralWindow const MSSpectralWindow& Input MSSpectralWindow // sub-table // Output to private data: // msSpWindowCols_p MSSpWindowColumns MSSpWindow columns accessor // spWindowIds_p Vector Data desc id's // nrows_p Int Number of rows // // Generate an array of data desc id's, used in later queries nrows_p = msSpWindowCols_p.nrow(); spWindowIds_p.resize(nrows_p); indgen(spWindowIds_p); } //------------------------------------------------------------------------- Vector MSSpWindowIndex::matchFreqGrp(const Int& freqGrp) { // Match a frequency goup to a set of spectral window id's // Input: // freqGrp const Int& Freq group to match // Output: // matchFreqGrp Vector Matching spw. id.'s // LogicalArray maskArray = (msSpWindowCols_p.freqGroup().getColumn()==freqGrp && !msSpWindowCols_p.flagRow().getColumn()); MaskedArray maskSpWindowId(spWindowIds_p, maskArray); return maskSpWindowId.getCompressedArray(); } //------------------------------------------------------------------------- Vector MSSpWindowIndex::matchFreqGrp(const Vector& freqGrps) { // Match a set of frequency groups to a set of spectral window id's // Input: // freqGrps const Vector& Freq groups to match // Output: // matchFreqGrp Vector Matching spw. id.'s // Vector matchedSpWindowIds; // Match each spw id individually for (uInt freqgrp=0; freqgrp currentMatch = matchFreqGrp(freqGrps(freqgrp)); if (currentMatch.nelements() > 0) { Vector temp(matchedSpWindowIds); matchedSpWindowIds.resize(matchedSpWindowIds.nelements() + currentMatch.nelements(), True); matchedSpWindowIds = concatenateArray(temp, currentMatch); } } return matchedSpWindowIds; } //------------------------------------------------------------------------- Vector MSSpWindowIndex::matchFreqGrpName(const String& freqGrpName) { // Match a frequency goup name to a set of spectral window id's // Input: // freqGrpName const String& Freq group name to match // Output: // matchFreqGrpName Vector Matching spw. id.'s // LogicalArray maskArray = (msSpWindowCols_p.freqGroupName().getColumn()==freqGrpName && !msSpWindowCols_p.flagRow().getColumn()); MaskedArray maskSpWindowId(spWindowIds_p, maskArray); return maskSpWindowId.getCompressedArray(); } //------------------------------------------------------------------------- Vector MSSpWindowIndex::matchFreq(const Vector& chanFreq, const Vector& chanWidth, const Double& tol) { // Match a frequency axis sampling to a set of spectral window id's // Input: // chanFreq const Vector& Channel frequencies // chanWidth const Vector& Channel freq. width // tol const Double& Tolerance for frequency // comparisons // Output: // matchFreq Vector Matching spw id.'s // // Do the match per frequency channel on each row uInt nChan = std::min(chanFreq.nelements(), chanWidth.nelements()); uInt nrows = msSpWindowCols_p.nrow(); Vector freqMatch(nrows, False); for (uInt row=0; row rowChanFreq; msSpWindowCols_p.chanFreqMeas().get(row, rowChanFreq); Vector rowChanWidth; msSpWindowCols_p.chanWidthQuant().get(row, rowChanWidth); freqMatch(row) = (rowChanFreq.nelements() == nChan && rowChanWidth.nelements() == nChan); if (freqMatch(row)) { for (uInt chan=0; chan maskSpwId(spWindowIds_p, maskArray); return maskSpwId.getCompressedArray(); } //------------------------------------------------------------------------- } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSSel/MSSpWindowIndex.h000066400000000000000000000070121476623553700201010ustar00rootroot00000000000000//# MSSpWindowIndex: index/lookup in a MeasurementSet SPECTRAL_WINDOW subtable //# Copyright (C) 2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSPWINDOWINDEX_H #define MS_MSSPWINDOWINDEX_H //# includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# forward declarations // // Class to handle lookup or indexing into a MS SPECTRAL_WINDOW subtable // // // // // // //
      • MeasurementSet //
      • MSSpWindowription // // // // From "MeasurementSet", "SPECTRAL_WINDOW subtable" and "index". // // // // This class provides lookup and indexing into an MS SPECTRAL_WINDOW // subtable. These services include returning rows numbers // (which for the SPECTRAL_WINDOW subtable are SPECTRAL_WINDOW_ID's) // associated with specific data in the subtable. // // // // // // // Collect together all subtable indexing and lookup for the // SPECTRAL_WINDOW subtable, for encapsulation and efficiency. // // // //
      • //
      • // // class MSSpWindowIndex { public: // Construct from an MS SPECTRAL_WINDOW subtable MSSpWindowIndex(const MSSpectralWindow& spectralWindow); // Null destructor virtual ~MSSpWindowIndex() {} // Look up SPECTRAL_WINDOW_ID's for a given frequency group or groups Vector matchFreqGrp(const Int& freqGrp); Vector matchFreqGrp(const Vector& freqGrps); // Look up SPECTRAL_WINDOW_ID's for a given frequency group name Vector matchFreqGrpName(const String& freqGrpName); // Look up SPECTRAL_WINDOW_ID's for a given frequency axis sampling Vector matchFreq(const Vector& chanFreq, const Vector& chanWidth, const Double& freqTol); private: // Disallow null constructor MSSpWindowIndex(); // SPECTRAL_WINDOW subtable column accessor MSSpWindowColumns msSpWindowCols_p; // Vector cache of SpWindow id's Vector spWindowIds_p; Int nrows_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSSpwGram.cc000066400000000000000000000160101476623553700170530ustar00rootroot00000000000000//# MSSpwGram.cc: Grammar for spectral window expressions //# Copyright (C) 1998,1999,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // MSSpwGram; grammar for field command lines // This file includes the output files of bison and flex for parsing // command lines operating on spectral window selection. #include #include #include //#include #include #include #include #include #include // routines used by bison actions #include //# stdlib.h is needed for bison 1.28 and needs to be included here //# (before the flex/bison files). //# Bison defines WHERE, which is also defined in LogOrigin.h (which //# is included in auto-template mode). //# So undefine WHERE first. #undef WHERE #include //# Let clang and gcc ignore some warnings in bison/flex code. //# Undef YY_NULL because flex can redefine it slightly differently (giving a warning). #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpragmas" #pragma GCC diagnostic ignored "-Wdeprecated-register" #pragma GCC diagnostic ignored "-Wsign-compare" #include "MSSpwGram.ycc" // bison output #ifdef YY_NULL # undef YY_NULL #endif #include "MSSpwGram.lcc" // flex output #pragma GCC diagnostic pop // Define the yywrap function for flex. int MSSpwGramwrap() { return 1; } namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Declare a file global pointer to a char* for the input string. static const char* strpMSSpwGram = 0; static Int posMSSpwGram = 0; // MSSpwGramwrap out of namespace //# Parse the command. //# Do a yyrestart(yyin) first to make the flex scanner reentrant. int msSpwGramParseCommand (const MeasurementSet* ms, const String& command) { try { Int ret; MSSpwGramrestart (MSSpwGramin); yy_start = 1; strpMSSpwGram = command.chars(); // get pointer to command string posMSSpwGram = 0; // initialize string position MSSpwParse parser(ms); // setup measurement set MSSpwParse::thisMSSParser = &parser; // The global pointer to the parser parser.reset(); ret=MSSpwGramparse(); // parse command string return ret; } catch (MSSelectionSpwError &x) { String newMesgs; newMesgs = constructMessage(msSpwGramPosition(), command); x.addMessage(newMesgs); throw; } } int baseMSSpwGramParseCommand (MSSpwParse* parser, const String& command, Vector& selectedIDs, Matrix&selectedChans, Vector& selectedDDIDs) { try { Int ret; MSSpwGramrestart (MSSpwGramin); yy_start = 1; strpMSSpwGram = command.chars(); // get pointer to command string posMSSpwGram = 0; // initialize string position MSSpwParse::thisMSSParser = parser; // The global pointer to the parser parser->reset(); ret=MSSpwGramparse(); // parse command string selectedIDs = parser->selectedIDs(); selectedChans = parser->selectedChanIDs(); selectedDDIDs = parser->selectedDDIDs(); if ((selectedIDs.size() == 0) || (selectedChans.size() == 0)) throw(MSSelectionSpwParseError("No valid SPW & Chan combination found")); return ret; } catch (MSSelectionSpwError &x) { String newMesgs; newMesgs = constructMessage(msSpwGramPosition(), command); x.addMessage(newMesgs); throw; } } int msSpwGramParseCommand (const MSSpectralWindow& spwSubTable, const MSDataDescription& ddSubTable, const TableExprNode& colAsTEN, const String& command, Vector& selectedIDs, Matrix& selectedChans, Vector& selectedDDIDs) { MSSpwParse thisParser(spwSubTable, ddSubTable, colAsTEN); return baseMSSpwGramParseCommand(&thisParser, command, selectedIDs, selectedChans, selectedDDIDs); } int msSpwGramParseCommand (const MeasurementSet *ms, const String& command,Vector& selectedIDs, Matrix& selectedChans) { try { Int ret; MSSpwGramrestart (MSSpwGramin); yy_start = 1; strpMSSpwGram = command.chars(); // get pointer to command string posMSSpwGram = 0; // initialize string position MSSpwParse parser(ms); // setup measurement set MSSpwParse::thisMSSParser = &parser; // The global pointer to the parser parser.reset(); ret=MSSpwGramparse(); // parse command string selectedIDs = parser.selectedIDs(); selectedChans = parser.selectedChanIDs(); if ((selectedIDs.size() == 0) || (selectedChans.size() == 0)) throw(MSSelectionSpwParseError("No valie SPW & Chan combination found")); return ret; } catch (MSSelectionSpwError &x) { String newMesgs; newMesgs = constructMessage(msSpwGramPosition(), command); x.addMessage(newMesgs); throw; } } //# Give the table expression node const TableExprNode* msSpwGramParseNode() { return MSSpwParse::node(); } void msSpwGramParseDeleteNode() { MSSpwParse::cleanupNode(); } //# Give the string position. Int& msSpwGramPosition() { return posMSSpwGram; } //# Get the next input characters for flex. int msSpwGramInput (char* buf, int max_size) { int nr=0; while (*strpMSSpwGram != 0) { if (nr >= max_size) { break; // get max. max_size char. } buf[nr++] = *strpMSSpwGram++; } return nr; } void MSSpwGramerror (const char*) { throw (MSSelectionSpwParseError("Spw Expression: Parse error at or near '" + String(MSSpwGramtext) + "'")); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSSel/MSSpwGram.h000066400000000000000000000070751476623553700167300ustar00rootroot00000000000000//# MSSpwGram.h: Grammar for ms field sub-expressions //# Copyright (C) 1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSPWGRAM_H #define MS_MSSPWGRAM_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MeasurementSet; class TableExprNode; // // Global functions for flex/bison scanner/parser for MSSpwGram // // // // // //# Classes you should understand before using this one. //
      • MSSpwGram.l and .y (flex and bison grammar) // // // Global functions are needed to define the input of the flex scanner // and to start the bison parser. // The input is taken from a string. // // // It is necessary to be able to give an image expression in ASCII. // This can be used in glish. // // //# A List of bugs, limitations, extensions or planned refinements. // // // Declare the bison parser (is implemented by bison command). int msSpwGramParseCommand (const MeasurementSet *ms, const String& command); int msSpwGramParseCommand (const MeasurementSet *ms, const String& command, Vector& selectedIDs, Matrix& selectedChanIDs); int msSpwGramParseCommand (const MSSpectralWindow& spwSubTable, const MSDataDescription& ddSubTable, const TableExprNode& colAsTEN, const String& command, Vector& selectedIDs, Matrix& selectedChans, Vector& selectedDDIDs) ; // The yyerror function for the parser. // It throws an exception with the current token. void MSSpwGramerror (const char*); // Give the table expression node. const TableExprNode *msSpwGramParseNode(); void msSpwGramParseDeleteNode(); // Give the current position in the string. // This can be used when parse errors occur. Int& msSpwGramPosition(); // Declare the input routine for flex/bison. int msSpwGramInput (char* buf, int max_size); // A function to remove escaped characters. //String msSpwGramRemoveEscapes (const String& in); // A function to remove quotes from a quoted string. //String msSpwGramRemoveQuotes (const String& in); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSSpwGram.ll000066400000000000000000000127141476623553700171040ustar00rootroot00000000000000/* -*- C -*- MSSpwGram.l: Lexical analyzer for ms selection commands Copyright (C) 1994,1995,1996,1997,1998,2001,2003 Associated Universities, Inc. Washington DC, USA. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. Correspondence concerning AIPS++ should be addressed as follows: Internet email: casa-feedback@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA */ /* yy_unput is not used, so let flex not generate it, otherwise picky compilers will issue warnings. */ %option nounput %{ #undef YY_INPUT #define YY_INPUT(buf,result,max_size) result=msSpwGramInput(buf,max_size) #undef YY_DECL #define YY_DECL int MSSpwGramlex (YYSTYPE* lvalp) static string qstr; %} WHITE [ \t\n]* DIGIT [0-9] SIGN [+-] INT {DIGIT}+ EXP [DdEe]{SIGN}?{INT} DOT \.? NUMBER ({INT}|{INT}?{DOT}{INT}*) FNUMBER {SIGN}?{NUMBER}?{EXP}? KILO ([Kk]) MEGA ([Mm]) GIGA ([Gg]) TERA ([Tt]) HERTZ (([Hh][Zz])) METER (([m])) SECOND ([Ss]|([Ss][Ee][Cc])) VELOCITY (({KILO}|{MEGA})?({METER}[/]{SECOND})) FREQ (({KILO}|{MEGA}|{GIGA}|{TERA})?{HERTZ}) UNIT (({FREQ}|{VELOCITY})) QSTRING \"[^\"\n]*\" STRING ({QSTRING})+ QUOTE (\") RQUOTE (\/) NQ [^\\\n\"]+ NRQ [^\\\n\/]+ NAME ([a-zA-Z_'{''}''+''-'#]+[a-zA-Z0-9_{}#]*) /*NAME ([A-za-z0-9_'{''}''+''-'])*/ /*IDENTIFIER ({NAME}+|STRING)*/ IDENTIFIER ({NAME}+) SIDENTIFIER ([A-Za-z_*?{}'+''-'#][A-Za-z0-9_'{''}''+''-''*''?'#]*) /* SIDENTIFIER ([A-Za-z_*?{}'+''-']+[A-Za-z0-9_'{''}''+''-''*''?':]*) SIDENTIFIER ({NAME}+['{''}''+''-''*''?']+) NAMES ([a-zA-Z_{}]+[a-zA-Z0-9_{}]*) IDENTIFIER ({NAMES}+|STRING) SIDENTIFIER ({NAMES}+"*") */ %x QS RS /* rules */ %% {QUOTE} { // Start of a quoted string qstr.resize(0); BEGIN(QS); } {NQ} { (qstr)+= MSSpwGramtext; } {QUOTE} { /* saw closing quote - all done */ BEGIN(INITIAL); lvalp->str = (char *)malloc((qstr.length() + 1)*sizeof(char)); strcpy(lvalp->str,qstr.c_str()); qstr.resize(0); return QSTRING; } {RQUOTE} { // Start of a regex string qstr.resize(0); BEGIN(RS); } {NRQ} { (qstr)+= MSSpwGramtext; } {RQUOTE} { /* saw closing quote - all done */ BEGIN(INITIAL); lvalp->str = (char *)malloc((qstr.length() + 1)*sizeof(char)); strcpy(lvalp->str,qstr.c_str()); qstr.resize(0); return REGEX; } {FNUMBER} {msSpwGramPosition() += yyleng; lvalp->str = (char *)malloc((strlen(MSSpwGramtext) + 1) * sizeof(char)); strcpy(lvalp->str, MSSpwGramtext); // cout << "FN = " << MSSpwGramtext << endl; return FNUMBER; } {UNIT} { msSpwGramPosition() += yyleng; lvalp->str = (char *)malloc((strlen(MSSpwGramtext) + 1) * sizeof(char)); strcpy(lvalp->str, MSSpwGramtext); return UNIT; } "~" { msSpwGramPosition() += yyleng; return DASH; } "," { msSpwGramPosition() += yyleng; return COMMA; } "<" { msSpwGramPosition() += yyleng; return LT; } ">" { msSpwGramPosition() += yyleng; return GT; } "&" { msSpwGramPosition() += yyleng; return AMPERSAND; } ";" { msSpwGramPosition() += yyleng; return SEMICOLON; } ":" { msSpwGramPosition() += yyleng; return COLON; } "^" { msSpwGramPosition() += yyleng; return CARET; } "<>" { msSpwGramPosition() += yyleng; return GTNLT; } /* Literals */ {IDENTIFIER} { msSpwGramPosition() += yyleng; lvalp->str = (char *)malloc((strlen(MSSpwGramtext) + 1) * sizeof(char)); strcpy(lvalp->str, MSSpwGramtext); // cout << "ID.l = " << MSSpwGramtext << endl; return IDENTIFIER; } {SIDENTIFIER} { msSpwGramPosition() += yyleng; lvalp->str = (char *)malloc((strlen(MSSpwGramtext) + 1) * sizeof(char)); strcpy(lvalp->str, MSSpwGramtext); // cout << "QS.l = " << MSSpwGramtext << endl; return QSTRING; } "(" { msSpwGramPosition() += yyleng; return LPAREN; } ")" { msSpwGramPosition() += yyleng; return RPAREN;} {WHITE} { msSpwGramPosition() += yyleng;} /* Eat white spaces */ . { msSpwGramPosition() += yyleng;return MSSpwGramtext[0];} %% casacore-3.7.1/ms/MSSel/MSSpwGram.yy000066400000000000000000000332121476623553700171320ustar00rootroot00000000000000/* -*-C++-*- MSSpwGram.y: Parser for Spw expressions Copyright (C) 2004 Associated Universities, Inc. Washington DC, USA. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. Correspondence concerning AIPS++ should be addressed as follows: Internet email: casa-feedback@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA */ %{ using namespace casacore; %} %pure-parser /* make parser re-entrant */ %union { const TableExprNode* node; Block* exprb; TableExprNodeSetElem* elem; TableExprNodeSet* settp; Float fval2[2], fval4[4],fval; char * str; Int ival; Vector* fv; Vector* iv; } %token SQUOTE %token IDENTIFIER %token COMMA %token LPAREN %token RPAREN %token WHITE %token COLON %token CARET %token SEMICOLON %token UNIT %token INT %token FNUMBER %token QSTRING %token REGEX %type SpwStatement %type FullSpec %type FullExpr %type OneFreq %type FreqRange %type Physical %type IndexRange %type PhyRange %type Spw %type FListElements %type FreqList %type PhyVal %type UnitCode %nonassoc GT GE LT LE NE GTNLT COMMA DASH AMPERSAND SEMICOLON COLON CARET %right TILDA %{ #include int MSSpwGramlex (YYSTYPE*); void checkSpwError(Vector& list, ostringstream& msg, const char *token) { if (list.nelements() == 0) { ostringstream Mesg, tok; Mesg << "Spw Expression: " << msg.str().c_str(); // String errorMesg; // errorMesg = String(Mesg.str().c_str()); // throw(MSSelectionSpwParseError(errorMesg)); tok << "\"" << token << "\""; MSSpwParse::thisMSSpwErrorHandler->reportError(tok.str().c_str(), Mesg.str()); } } %} %% SpwStatement: FullExpr {$$=MSSpwParse::thisMSSParser->endOfCeremony(*($1));} | LPAREN FullExpr RPAREN {$$ = $2;} ; PhyVal: FNUMBER { Float f; sscanf($1,"%f",&f); $$ = f; free($1); } ; UnitCode: UNIT { String str($1); str.downcase(); if (str.contains("hz")) $$[0]=MSSpwIndex::MSSPW_UNITHZ; else // Only Frequency and velocity units will make to the parser. { $$[0] = MSSpwIndex::MSSPW_UNITVELOCITY; throw(MSSelectionSpwParseError(String("Spw expression: Velocity units " "support temporarily disabled."))); } // MSSpwIndex myMSSI(MSSpwParse::thisMSSParser->ms()->spectralWindow()); MSSpwIndex myMSSI(MSSpwParse::thisMSSParser->subTable()); $$[1] = myMSSI.convertToMKS(1.0,1.0,$1)(0); free($1); } ; Physical: PhyVal UnitCode { $$[0] = $1*$2[1]; // UnitCode[1] has the factor to get to MKS. $$[1] = $2[0]; // UnitCode[0] has the code for the unit. } ; PhyRange: Physical DASH Physical { if ($1[0] > $3[0]) throw(MSSelectionSpwParseError(String("Spw expression: Start of " "range greater than end of range"))); $$[0] = $1[0]; $$[1] = $3[0]; // $$[2] = 0; // The Step $$[2] = 0; // The Step if ($1[1] != $3[1]) throw(MSSelectionSpwParseError(String("Spw expression: Start and stop specification" " not in the same units."))); $$[3] = $3[1]; // The Unit } | PhyVal DASH PhyVal UnitCode { if ($1 > $3) throw(MSSelectionSpwParseError(String("Spw expression: Start of " "range greater than end of range"))); // MSSpwIndex myMSSI(MSSpwParse::thisMSSParser->ms()->spectralWindow()); MSSpwIndex myMSSI(MSSpwParse::thisMSSParser->subTable()); $$[0] = $1*$4[1]; $$[1] = $3*$4[1]; $$[2] = 0; // The Step $$[3] = $4[0]; } | PhyRange CARET Physical { if ($1[3] != $3[1]) throw(MSSelectionSpwParseError(String("Spw expression: Range and step specifications" " not in the same units."))); $$[2] = $3[0]; // Load the step } | CARET Physical { throw(MSSelectionSpwParseError(String("Spw expression: A lone \"^PhyUnits\"" " not yet supported."))); } ; IndexRange: PhyVal DASH PhyVal { if ($1 > $3) throw(MSSelectionSpwParseError(String("Spw expression: Start of " "range greater than end of range"))); $$[0] = (Int)$1; $$[1] = (Int)$3; $$[2] = 0; // The Step $$[3] = MSSpwIndex::MSSPW_INDEX; } | IndexRange CARET PhyVal { $$[2] = (Int)$3; } | CARET PhyVal { $$[0] = -1; $$[1] = -1; $$[2] = $2; $$[3] = MSSpwIndex::MSSPW_INDEX; } ; FreqRange: IndexRange { $$[0] = $1[0];//Start index $$[1] = $1[1];//End index $$[2] = $1[2];//Step $$[3] = MSSpwIndex::MSSPW_INDEXRANGE;//Code } | PhyRange { $$[0] = $1[0];//Start value $$[1] = $1[1];//End value $$[2] = $1[2];//Step $$[3] = $1[3];//Unit code //MSSpwIndex::MSSPW_UNITHZ; } ; OneFreq: PhyVal { // cout << "Index = " << (Int)$1 << endl; $$[0] = (Int)$1; // The Index $$[1] = MSSpwIndex::MSSPW_INDEX; // The index code } | Physical { $$[0] = $1[0]; // The value $$[1] = $1[1]; // The UnitCode // $$[2] = $1[1]; } ; FListElements: FreqRange { $$[0]=$1[0]; //Start of the range value $$[1]=$1[1]; //End of the range value $$[2]=$1[2]; //Step of the range $$[3]=$1[3]; //Unit code } | OneFreq { $$[0]=$1[0]; //Start of the range value $$[1]=$1[0]; //End of the range value $$[2]=0; //Step (set to zero to indicate that this is not a range) $$[3]=$1[1]; //Unit code } ; FreqList: FListElements { $$ = new Vector(0); Int N0=(*($$)).nelements(); Int N1=N0+4; (*($$)).resize(N1,True); // Resize the existing list for(Int i=N0;ims()->spectralWindow()); MSSpwIndex myMSSI(MSSpwParse::thisMSSParser->subTable()); $$=new Vector(myMSSI.matchName($1)); ostringstream m; m << "No match found for "; checkSpwError(*($$), m, $1); free($1); } | QSTRING { // // Quoted string: This is a pattern which will be converted // to regex internally. E.g. "LBAN*" becomes // "LBAN.*" regex. This can include any character // string. // // Convert name to index // // MSSpwIndex myMSSI(MSSpwParse::thisMSSParser->ms()->spectralWindow()); MSSpwIndex myMSSI(MSSpwParse::thisMSSParser->subTable()); $$ = new Vector(myMSSI.matchRegexOrPattern($1)); ostringstream m; m << "No match found for "; checkSpwError(*($$), m, $1); free($1); } | REGEX { // // Quoted string: This is a pattern which will be converted // to regex internally. E.g. "LBAN*" becomes // "LBAN.*" regex. This can include any character // string. // // Convert name to index // // MSSpwIndex myMSSI(MSSpwParse::thisMSSParser->ms()->spectralWindow()); MSSpwIndex myMSSI(MSSpwParse::thisMSSParser->subTable()); $$ = new Vector(myMSSI.matchRegexOrPattern($1)); ostringstream m; m << "No match found for "; checkSpwError(*($$), m, $1); free($1); } | GT OneFreq { // MSSpwIndex myMSSI(MSSpwParse::thisMSSParser->ms()->spectralWindow()); MSSpwIndex myMSSI(MSSpwParse::thisMSSParser->subTable()); ostringstream m,tok; m << "No spw ID found for > "; if ($2[1] == MSSpwIndex::MSSPW_INDEX) { $$ = new Vector(myMSSI.matchGT((Int)$2[0])); // m << (Int)$2[0]; tok << (Int)$2[0]; } else { $$ = new Vector(myMSSI.matchGT($2)); // m << (Double)$2[0] << "Hz"; tok << "Hz"; } checkSpwError(*($$), m,tok.str().c_str()); } | LT OneFreq { // MSSpwIndex myMSSI(MSSpwParse::thisMSSParser->ms()->spectralWindow()); MSSpwIndex myMSSI(MSSpwParse::thisMSSParser->subTable()); ostringstream m, tok; m << "No spw ID found for < "; if ($2[1] == MSSpwIndex::MSSPW_INDEX) { $$ = new Vector(myMSSI.matchLT((Int)$2[0])); // m << (Int)$2[0]; tok << (Int)$2[0]; } else { $$ = new Vector(myMSSI.matchLT($2)); // m << (Double)$2[0] << "Hz"; tok << (Double)$2[0] << "Hz"; } checkSpwError(*($$), m, tok.str().c_str()); } | OneFreq GTNLT OneFreq { // MSSpwIndex myMSSI(MSSpwParse::thisMSSParser->ms()->spectralWindow()); MSSpwIndex myMSSI(MSSpwParse::thisMSSParser->subTable()); ostringstream m,tok; m << "No spw ID found "; if ($1[1] == MSSpwIndex::MSSPW_INDEX) { $$ = new Vector(myMSSI.matchGTAndLT((Int)$1[0],(Int)$3[0])); //m << (Int)$1[0] << "<>" << (Int)$3[0]; tok << (Int)$1[0] << "<>" << (Int)$3[0]; } else { $$ = new Vector(myMSSI.matchGTAndLT($1,$3)); //m << (Double)$1[0] << "<>" << (Double)$3[0] << "Hz"; tok << (Double)$1[0] << "<>" << (Double)$3[0] << "Hz"; } checkSpwError(*($$), m, tok.str().c_str()); } | DASH OneFreq { // MSSpwIndex myMSSI(MSSpwParse::thisMSSParser->ms()->spectralWindow()); MSSpwIndex myMSSI(MSSpwParse::thisMSSParser->subTable()); $$ = new Vector(myMSSI.matchFrequencyRange($2[0],$2[0],True)); ostringstream m,tok; m << "No spw ID found ~= "; tok << (Int)$2[0]; checkSpwError(*($$), m, tok.str().c_str()); } | FreqList { // MSSpwIndex myMSSI(MSSpwParse::thisMSSParser->ms()->spectralWindow()); MSSpwIndex myMSSI(MSSpwParse::thisMSSParser->subTable()); Int nSpec; // cout << (*($1)) << " " << endl; // cout << "FreqList "; // if ((*($1))[3] == MSSpwIndex::MSSPW_INDEX) cout << "Index "; // if ((*($1))[3] == MSSpwIndex::MSSPW_INDEXRANGE) cout << "IndexRange "; // if ((*($1))[3] == MSSpwIndex::MSSPW_UNITHZ) cout << "FreqRange "; $$ = new Vector(myMSSI.convertToSpwIndex($1[0],nSpec)); /* cout << (*($$)) << endl; */ delete $1; myMSSI.matchNameAsIntID(*($$)); } ; FullSpec: Spw { // MSSpwIndex myMSSI(MSSpwParse::thisMSSParser->ms()->spectralWindow()); MSSpwIndex myMSSI(MSSpwParse::thisMSSParser->subTable()); Vector varifiedSpwList=myMSSI.matchId(*($1)); // $$ = MSSpwParse().selectSpwIdsFromIDList(varifiedSpwList); Int nFSpec; Vector dummy(0); Vector chanList = myMSSI.convertToChannelIndex(varifiedSpwList,dummy, nFSpec); MSSpwParse::thisMSSParser->selectChannelsFromIDList(varifiedSpwList, chanList, nFSpec); $$ = MSSpwParse::thisMSSParser->selectSpwIdsFromIDList(varifiedSpwList,False); delete $1; } | Spw COLON FreqList { // MSSpwIndex myMSSI(MSSpwParse::thisMSSParser->ms()->spectralWindow()); MSSpwIndex myMSSI(MSSpwParse::thisMSSParser->subTable()); Vector varifiedSpwList=myMSSI.matchId(*($1)); //Vector varifiedSpwList=(*($1)); Int nFSpecs; Vector chanList = myMSSI.convertToChannelIndex(varifiedSpwList, (*($3)), nFSpecs); // // This just fills in the chan. list structure (to be // returned for MSSelection::getChanList()). The name // selectionChannelsFromIDList is a statement of intent // (i.e. whenever we can figure out a way to select // channels in the VisBuffer, this method is where we // will do it). // // This may modify the varifiedSpwList (eliminate SPWs // that had no channels selected) // MSSpwParse::thisMSSParser->selectChannelsFromIDList(varifiedSpwList, chanList, nFSpecs); // // Just filling the indices in the lists which are // returned from getSpwList() and getChanList() etc. // Since the channel info. is not part of the TEN, do // not add to the TEN tree here. The SPW info. is // converted to a TEN in the resolution of SpwStatement // rule. // // $$ = MSSpwParse::thisMSSParser->selectSpwIdsFromIDList(varifiedSpwList); MSSpwParse::thisMSSParser->selectSpwIdsFromIDList(varifiedSpwList,False); delete $1; delete $3; } ; FullExpr: FullSpec {} | FullExpr COMMA FullSpec {} ; %% casacore-3.7.1/ms/MSSel/MSSpwIndex.cc000066400000000000000000000514471476623553700172510ustar00rootroot00000000000000//# MSSpwIndex.cc: implementation of MSSpwIndex.h //# Copyright (C) 2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // //------------------------------------------------------------------ // MSSpwIndex::MSSpwIndex(const MSSpectralWindow& msSpw): msSpwSubTable_p(msSpw) { Int nrows = msSpwSubTable_p.nrow(); spwIDs.resize(nrows); indgen(spwIDs); } // //------------------------------------------------------------------ // Vector MSSpwIndex::matchRegexOrPattern(const String& pattern, const Bool regex) { Int pos=0; Regex reg; if (regex) reg=pattern; else reg=reg.fromPattern(pattern); // cerr << "Pattern = " << pattern << " Regex = " << reg.regexp() << endl; IPosition sh(msSpwSubTable_p.name().getColumn().shape()); LogicalArray maskArray(sh,False); IPosition i=sh; for(i(0)=0;i(0)0) );//&& !msSpwSubTable_p.flagRow().getColumn()(i)); } MaskedArray maskSpwID(spwIDs,maskArray); return maskSpwID.getCompressedArray(); } // //------------------------------------------------------------------ // Input list modifier. Examine the input list and treat elements // greater than the number of SPWs by matching them as name // strings. Elements for which this match fails or which are less // than the number of SPWs remain unmodified. void MSSpwIndex::matchNameAsIntID(Vector& list) { int nSpw = msSpwSubTable_p.name().getColumn().nelements(); for(unsigned int i=0;i= nSpw) { // Convert to string and attempt a match against the name // column. std::stringstream ss; ss << list[i]; Vector id=matchName(ss.str()); if (id.nelements() > 0) list[i]=id[0]; } } } // //------------------------------------------------------------------ // Vector MSSpwIndex::matchName(const String& name) { LogicalArray maskArray = (msSpwSubTable_p.name().getColumn()==name); // && !msSpwSubTable_p.flagRow().getColumn()); MaskedArray maskSpwId(spwIDs, maskArray); return maskSpwId.getCompressedArray(); } // //------------------------------------------------------------------ // Vector MSSpwIndex::matchId(const Vector& sourceId) { Vector IDs; IDs = set_intersection(sourceId,spwIDs); // // If IDs has less than sourceId, some sourceIds found no match. // So construct a message, and send it off to the SPW parser error // handler. // if (IDs.nelements() != sourceId.nelements()) { vector tt = IDs.tovector(); ostringstream Mesg, tok; Mesg << "Spw Expression: No match found for "; for (uInt i=0;i::iterator ndx = find(tt.begin(), tt.end(), sourceId[i]); if (ndx == tt.end()) tok << sourceId[i] << ","; } MSSpwParse::thisMSSpwErrorHandler->reportError(tok.str().c_str(), Mesg.str()); } return IDs; } // //------------------------------------------------------------------ // Bool MSSpwIndex::matchFrequencyRange(const Double f0, const Double f1, Vector& spw, Vector& start, Vector& nchan){ Int nspw=msSpwSubTable_p.nrow(); Bool found=False; spw.resize(); start.resize(); nchan.resize(); Int nmatch=0; for (Int k=0; k < nspw; ++k){ Bool locfound=False; Bool dum; Vector chanfreq=msSpwSubTable_p.chanFreq()(k); Sort sort( chanfreq.getStorage(dum),sizeof(Double) ); sort.sortKey((uInt)0,TpDouble); Int nch=chanfreq.nelements(); Vector sortIndx; sort.sort(sortIndx, nch); Vectorchanwidth=msSpwSubTable_p.chanWidth()(k); if(f0 > chanfreq(sortIndx[0]) && f0 < chanfreq(sortIndx[nch-1])){ locfound=True; } if(f1 > chanfreq(sortIndx[0]) && f1 < chanfreq(sortIndx[nch-1])){ locfound=True; } if(locfound){ Vector chanIn(chanfreq.nelements()); chanIn=-1; Int numMatched=0; for (uInt kk=0; kk < chanfreq.nelements(); ++kk){ if( ((chanfreq[kk]+0.5*fabs(chanwidth[kk])) > f0) && ((chanfreq[kk]-0.5*fabs(chanwidth[kk])) < f1) ) { chanIn[numMatched]=kk; ++ numMatched; } } if(numMatched >0){ ++nmatch; spw.resize(nmatch, True); spw(nmatch-1)=k; start.resize(nmatch, True); nchan.resize(nmatch, True); found=True; chanIn.resize(numMatched, True); start(nmatch-1)=min(chanIn); nchan(nmatch-1)=max(chanIn)-start(nmatch-1)+1; } } //spw is fully inside region between f0 and f1 else if((f0 < chanfreq(sortIndx[0])) && (f1 > chanfreq(sortIndx[nch-1]))){ ++nmatch; spw.resize(nmatch, True); spw(nmatch-1)=k; start.resize(nmatch, True); start(nmatch-1)=0; nchan.resize(nmatch, True); nchan(nmatch-1)=nch; found=True; } } return found; } // //------------------------------------------------------------------ // Vector MSSpwIndex::matchFrequencyRange(const Float f0, const Float f1, Bool approx, const Float f3) { Int nSpwRows=msSpwSubTable_p.nrow(); Bool Found; Int mode; Vector IDs; Float localStep; mode=RANGE; if ((f1 < 0) || (f0==f1)) mode=EXACT; if (approx) mode=APPROX; ArrayColumn chanWidth(msSpwSubTable_p.chanWidth()); ArrayColumn chanFreq(msSpwSubTable_p.chanFreq()); for(Int n=0;n shouldNotBeRequired; chanWidth.get(n,shouldNotBeRequired,True); maxChanWidth = max(shouldNotBeRequired); if (f3 < 0) localStep=min(shouldNotBeRequired); else localStep = f3; } Found = False; if (approx) totalBandWidth = msSpwSubTable_p.totalBandwidth()(n); else totalBandWidth = 0; // refFreq = msSpwSubTable_p.refFrequency()(n); Vector chanFreqList; chanFreq.get(n,chanFreqList,True); Int nChan=chanFreqList.nelements(); refFreq = (chanFreqList(nChan-1)+chanFreqList(0))/2.0;; //cout << chanFreqList[0] << " " << chanFreqList[nChan-1] << " " << f0 << " " << f1 << " " << f3 << " " << maxChanWidth << endl; switch (mode) { case EXACT: { if (fabs(refFreq - f0) < maxChanWidth) Found = True; break; } case APPROX: { if ((fabs(refFreq-f0) <= totalBandWidth) // && (!msSpwSubTable_p.flagRow()(n)) ) Found = True; break; } case RANGE: { if (f3 == 0) { if ((refFreq >= f0) && (refFreq <= f1) // && (!msSpwSubTable_p.flagRow()(n)) ) Found = True; break; } else { for(Float freq=f0;freq <=f1; freq+=localStep) { if (fabs(freq - refFreq) < maxChanWidth) {Found = True;break;} } break; } } default: { throw(MSSelectionSpwError("Internal error: Unknown mode in MSSpwIndex::matchFrequencyRange()")); } } if (Found) { // // Darn! We don't use standard stuff (STL!) // //IDs.push_back(SpwIds(n)); IDs.resize(IDs.nelements()+1,True); IDs(IDs.nelements()-1) = n; if (mode==EXACT) break; } } if (IDs.nelements()==0) { ostringstream msg; String rangeStr(" frequency range "); if (f0==f1) rangeStr=" frequency "; msg << "No matching SPW found for" << rangeStr << f0; if (f0!=f1) msg << "~" << f1; msg << " Hz."; throw(MSSelectionSpwError(msg.str())); } return IDs; } // //------------------------------------------------------------------ // Vector MSSpwIndex::matchLT(const Float* phyVal) { Vector refFreqs= msSpwSubTable_p.refFrequency().getColumn(); LogicalArray maskArray = (refFreqs < (Double)phyVal[0]); MaskedArray maskSpwId(spwIDs,maskArray); return maskSpwId.getCompressedArray(); } // //------------------------------------------------------------------ // Vector MSSpwIndex::matchGT(const Float* phyVal) { Vector refFreqs= msSpwSubTable_p.refFrequency().getColumn(); LogicalArray maskArray = (refFreqs > (Double)phyVal[0]); MaskedArray maskSpwId(spwIDs,maskArray); return maskSpwId.getCompressedArray(); } // //------------------------------------------------------------------ // Vector MSSpwIndex::matchGTAndLT(const Float* phyValMin, const Float *phyValMax) { Vector refFreqs= msSpwSubTable_p.refFrequency().getColumn(); LogicalArray maskArray = ((refFreqs > (Double)phyValMin[0]) && (refFreqs < (Double)phyValMax[0])); MaskedArray maskSpwId(spwIDs,maskArray); return maskSpwId.getCompressedArray(); } // //------------------------------------------------------------------ // Vector MSSpwIndex::matchLT(const Int n) { LogicalArray maskArray = // ((spwIDs <= n));// && (!msSpwSubTable_p.flagRow().getColumn())); ((spwIDs < n));// && (!msSpwSubTable_p.flagRow().getColumn())); MaskedArray maskSpwId(spwIDs, maskArray); return maskSpwId.getCompressedArray(); } // //------------------------------------------------------------------ // Vector MSSpwIndex::matchGT(const Int n) { LogicalArray maskArray = ((spwIDs > n));// && (!msSpwSubTable_p.flagRow().getColumn())); MaskedArray maskSpwId(spwIDs, maskArray); return maskSpwId.getCompressedArray(); } // //------------------------------------------------------------------ // Vector MSSpwIndex::matchGTAndLT(const Int n0, const Int n1) { LogicalArray maskArray = ((spwIDs > n0) && (spwIDs < n1));// &&(!msSpwSubTable_p.flagRow().getColumn())); MaskedArray maskSpwId(spwIDs, maskArray); return maskSpwId.getCompressedArray(); } // //------------------------------------------------------------------ // Vector MSSpwIndex::convertToMKS(const Float f0, const Float f1, const String& unit) { Vector freqs(2); String units(unit); units.downcase(); Float factor=1.0; if (units[0] == 'k') factor *= 1000; else if (units[0] == 'm') factor *= 1e6; else if (units[0] == 'g') factor *= 1e9; else if (units[0] == 't') factor *= 1e12; freqs(0) = f0*factor; freqs(1) = f1*factor; return freqs; } // //------------------------------------------------------------------ // Vector MSSpwIndex::convertToChannelIndex(const Vector& spw, const Vector& freqList, Int &nFSpec) { LogIO log_l(LogOrigin("MSSpw Expression parser", "MSSpwIndex::convertToChannelIndex", WHERE)); Vector localFreqList; vector localFoundSpwList; Vector numChans = msSpwSubTable_p.numChan().getColumn(); Int nSpw = spw.nelements(), nFList=freqList.nelements(); nFSpec = nFList/4; // 4 integers per channel specification ArrayColumn chanWidth(msSpwSubTable_p.chanWidth()); ArrayColumn chanFreq(msSpwSubTable_p.chanFreq()); Bool someMatchFailed=False; ostringstream Mesg; if (nFList > 0) { localFreqList.resize(nSpw*nFSpec*3); Int pos=0; for(Int i=0;i= numChans(spw(i))) { Mesg << "Spot-channel " << stop << " out of range for SPW " << spw(i) << " (valid range 0~" << numChans(spw(i))-1 << ")." << " Limiting it to be within the available range."; // throw(MSSelectionSpwError(Mesg.str())); log_l << Mesg.str() << LogIO::WARN << LogIO::POST; stop = start = numChans(spw(i))-1; someMatchFailed=True; } } else { if (stop >= numChans(spw(i))) { // ostringstream Mesg; Mesg << "Channel " << stop << " out of range for SPW " << spw(i) << " (valid range 0~" << numChans(spw(i))-1 << ")." << " Limiting it to be within the available range."; // throw(MSSelectionSpwError(Mesg.str())); log_l << Mesg.str() << LogIO::WARN << LogIO::POST; someMatchFailed=True; } start = max(0, min(start,numChans(spw(i))-1)); stop = min(numChans(spw(i))-1, max(stop,0)); } if ((start != -1) && (stop != -1)) localFoundSpwList.push_back(spw(i)); step = (((Int)step <= 0) ? 1 : step); localFreqList(pos++)=start; localFreqList(pos++)=stop; localFreqList(pos++)=step; } else if (freqList(j+3) == MSSpwIndex::MSSPW_UNITHZ) // If the spec is XXHz { Float start=freqList(j),stop=freqList(j+1),step=freqList(j+2); Vector cf,cw; chanFreq.get(spw(i),cf,True); chanWidth.get(spw(i),cw,True); if (abs(cw(0)) == 0) throw(MSSelectionSpwError("Error in the MS SPECTRAL_WINDOW sub-table (channel width==0).")); Int cwDir = (Int)(cw(0)/abs(cw(0))); // // Do a brain-dead linear search for the channel // number (linear search is *probably* OK - unless // there are channels worth GBytes of RAM!) // // Obfuscated code alert (but it was fun :))! someMatchFailed |= ((start = findChanIndex_p(start, cf, True, (cwDir>0)))==-1); someMatchFailed |= ((stop = findChanIndex_p(stop, cf, False, (cwDir>0)))==-1); // Bool found=False; // Int n=cf.nelements(); // { // if (start <= cf(0)) start=0; // else // { // for(Int ii=0;ii= start) {start=ii;found=True;break;} // if (!found) // {someMatchFailed=True;start = -1;} // } // found=False; // if (stop >= cf(n-1)) stop = n-1; // else // { // for(Int ii=n-1;ii>=0;ii--) // if (cf(ii) <= stop) {stop=ii;found=True;break;} // if (!found) // {someMatchFailed=True; stop=-1;} // } // } Double maxCW=max(cw), minCW=min(cw); if (minCW != maxCW) { log_l << "Channel width across the band is not constant. " << "Using the maximum of the channel width range." << LogIO::WARN; } step=fabs(freqList(j+2)/maxCW); // // Enforce start < stop and step > 0. // // In case it is ever required to support the case // where start > step (e.g., when the frequency in // the database is in descending order) the variable // cwDir carries the direction in which // freq. increases with increase channel index. // step = (((Int)step <= 0) ? 1 : step);//*cwDir; if (start > stop) { Float tmp=start; start=stop;stop=tmp; } if ((start != -1) && (stop != -1)) localFoundSpwList.push_back(spw(i)); localFreqList(pos++)=(Int)start; localFreqList(pos++)=(Int)stop; localFreqList(pos++)=(Int)step; } else // If the spec is XXKm/s { // // Now that I (SB) think about this, veloctiy based // selection in MSSelection does not make sense. // //Float start=freqList(j),stop=freqList(j+1),step=freqList(j+2); // // cerr << "Start = " << start << " Stop = " << stop << " Step = " << step << endl; // MRadialVelocity vstart(Quantity(start, "km/s"), MRadialVelocity::LSRK); // MDoppler mdoppler(vstart.getValue().get(), MDoppler::RADIO); // MSDopplerUtil msdoppler(*ms_p); // msdoppler.dopplerInfo(restFreq ,spw(i), fieldid); // cout << MFrequency::fromDoppler(mdoppler, // restFreq).getValue().getValue() << endl; } } } else { Int j=0; nFSpec=1; localFreqList.resize(nSpw*3); for(Int i=0;i(localFoundSpwList) // << " for some sub-expression." // << LogIO::WARN << LogIO::POST; ; } else { ostringstream m; log_l << "Found no matching SPW(s) " << spw << LogIO::WARN << LogIO::POST; // log_l << m.str() << LogIO::WARN << LogIO::POST; } } return localFreqList; } // //------------------------------------------------------------------ // Int MSSpwIndex::findChanIndex_p(const Float& freq, const Vector& chanFreqList, const Bool& greaterThan, const Bool& ascendingOrder) { Int chanIndex=-1, n=chanFreqList.nelements(); if (ascendingOrder) { if (greaterThan) { if (freq <= chanFreqList(0)) chanIndex=0; else for(Int ii=0;ii= freq) {chanIndex=ii;break;} } else { if (freq >= chanFreqList(n-1)) chanIndex=n-1; else for(Int ii=n-1;ii>=0;ii--) if (chanFreqList(ii) <= freq) {chanIndex=ii;break;} } } else { if (greaterThan) { if (freq <= chanFreqList(n-1)) chanIndex=n-1; else for(Int ii=n-1;ii>=0;ii--) if (chanFreqList(ii) >= freq) {chanIndex=ii;break;} } else { if (freq >= chanFreqList(0)) chanIndex=0; else for(Int ii=0;ii MSSpwIndex::convertToSpwIndex(const Vector& freqList, Int &nFSpec) { Vector localFreqList; Int nFList=freqList.nelements(); nFSpec = nFList/4; // 4 integers per channel specification if (nFList > 0) { // localFreqList.resize(nFSpec); Int pos=0; for(Int j=0;j #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# forward declarations // // Class to handle lookup or indexing into a MS Data_Desc_ID // and SpectralWindow subtables // // // // // // //
      • MeasurementSet // // // // From "MeasurementSet", "SpectralWindwo subtable" and "index". // // // // // This class provides lookup and two level indexing into an MS // DataDescription and SpectralWindow subtable. These services include // returning list of integer data description IDs (DDID), given a list // of Spectral window IDs or frequencies. The DDIDs are then used for // selecting rows from the MS (since DDIDs are the primary keys in the // maintable - not the spectral window IDs). // // // // // // // // Collect together all subtable indexing and lookup for the data // description and spectral window subtable, for encapsulation and // efficiency. // // // //
      • //
      • // // class MSSpwIndex { public: enum MSSpwTypes {MSSPW_INDEXRANGE=0,MSSPW_INDEX=2, MSSPW_UNITHZ=4, MSSPW_UNITVELOCITY=5}; // Construct from an MS FIELD subtable MSSpwIndex(const MSSpectralWindow& msSpw); // Null destructor virtual ~MSSpwIndex() {}; // Look up FIELD_ID's for a given field name, or set of field names Vector matchName(const String& name); Vector matchName(const Vector& names); void matchNameAsIntID(Vector& list); Vector matchFrequencyRange(const Float f0,const Float f1,Bool approx, const Float f3=0); // A version of match freq range that does not throw an exception but returns // false if no match...else spw, start, nchan returns the matches // f0 and f1 are in Hz and the match is done in the frame defined in the // SpectralWindow table. Bool matchFrequencyRange(const Double f0, const Double f1, Vector& spw, Vector& start, Vector& nchan); // Look up FIELD_ID's for a given pattern/regex for source name/code Vector matchRegexOrPattern(const String& pattern, const Bool regex=False); // Look up FIELD_ID's for a given source id Vector matchId(const Vector& spwIds); Vector matchLT(const Int n); Vector matchGT(const Int n); Vector matchGTAndLT(const Int n0, const int n1); Vector matchLT(const Float*); Vector matchGT(const Float*); Vector matchGTAndLT(const Float* phyValMin, const Float *phyValMax); Vector convertToMKS(const Float f0, const Float f1, const String& unit); Vector convertToChannelIndex(const Vector& spw, const Vector& freqList, Int& nFSpec); Vector convertToSpwIndex(const Vector& freqList, Int &nFSpec); private: Int findChanIndex_p(const Float& freq, const Vector& chanFreqList, const Bool& greaterThan, const Bool& ascendingOrder); // Construct from an MS FIELD subtable MSSpwIndex(); // FIELD subtable column accessor MSSpWindowColumns msSpwSubTable_p; // MSDataDescColumns msDataDescSubTable_p; enum MODES {EXACT=1, APPROX, RANGE}; Vector spwIDs; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSSpwParse.cc000066400000000000000000000232601476623553700172440ustar00rootroot00000000000000//# MSSpwParse.cc: Classes to hold results from Spw grammar parser //# Copyright (C) 1994,1995,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSSpwParse* MSSpwParse::thisMSSParser = 0x0; // Global pointer to the parser object TableExprNode* MSSpwParse::node_p = 0x0; Vector MSSpwParse::idList; Vector MSSpwParse::ddidList; Matrix MSSpwParse::chanList; TableExprNode MSSpwParse::columnAsTEN_p; std::shared_ptr MSSpwParse::thisMSSpwErrorHandler; //# Constructor // //------------------------------------------------------------------ // MSSpwParse::MSSpwParse () : MSParse() { if (MSSpwParse::node_p!=0x0) delete MSSpwParse::node_p; MSSpwParse::node_p=0x0; node_p = new TableExprNode(); } // //------------------------------------------------------------------ // //# Constructor with given ms name. MSSpwParse::MSSpwParse (const MeasurementSet* ms) : MSParse(ms, "Spw") { idList.resize(0); ddidList.resize(0); if(MSSpwParse::node_p) delete MSSpwParse::node_p; node_p = new TableExprNode(); } // //------------------------------------------------------------------ // MSSpwParse::MSSpwParse (const MSSpectralWindow& spwSubTable, const MSDataDescription& ddSubTable, const TableExprNode& columnAsTEN): MSParse(), spwSubTable_p(spwSubTable), ddSubTable_p(ddSubTable) { idList.resize(0); ddidList.resize(0); if(MSSpwParse::node_p) delete MSSpwParse::node_p; node_p = new TableExprNode(); columnAsTEN_p = columnAsTEN; } // //------------------------------------------------------------------ // const TableExprNode *MSSpwParse::selectSpwIdsFromIDList(const Vector& SpwIds, const Bool addTen, const Bool addIDs) { // MSSpWindowColumns msSpwSubTable(ms()->spectralWindow()); // MSDataDescColumns msDataDescSubTable(ms()->dataDescription()); MSSpWindowColumns msSpwSubTable(spwSubTable_p); MSDataDescColumns msDataDescSubTable(ddSubTable_p); Vector mapDDID2SpwID, notFoundIDs; Int nDDIDRows; Bool Found; TableExprNode condition; const String DATA_DESC_ID = MS::columnName(MS::DATA_DESC_ID), FLAG_COL = MS::columnName(MS::FLAG); nDDIDRows = msDataDescSubTable.nrow(); mapDDID2SpwID.resize(nDDIDRows); for(Int i=0;iSPWID: " << i << " " << mapDDID2SpwID(i) << endl; if ((SpwIds(n) == mapDDID2SpwID(i)) && (!msDataDescSubTable.flagRow()(i)) && (!msSpwSubTable.flagRow()(SpwIds(n))) ) { if (addTen) { // TableExprNode tmp=((ms()->col(DATA_DESC_ID)==i)); TableExprNode tmp=((columnAsTEN_p==i)); addCondition(condition, tmp); } // if (condition.isNull()) // condition = ((ms()->col(DATA_DESC_ID)==i)); // else // condition = condition || ((ms()->col(DATA_DESC_ID)==i)); Found = True; if (addIDs) { idList.resize(idList.nelements()+1,True); idList(idList.nelements()-1) = mapDDID2SpwID(i); ddidList.resize(ddidList.nelements()+1, True); ddidList(ddidList.nelements()-1)=i; } // break; } } if ((!Found) && (addIDs)) { // // Darn! We don't use standard stuff (STL!) // //notFoundIDs.push_back(SpwIds(n)); notFoundIDs.resize(notFoundIDs.nelements()+1,True); notFoundIDs(notFoundIDs.nelements()-1) = SpwIds(n); } } if (addTen) addCondition(*node_p, condition); // cerr << "DDID = " << ddidList << endl; return node_p; } // //------------------------------------------------------------------ // const TableExprNode *MSSpwParse::selectSpwIdsFromFreqList(const Vector& freq, const Float factor) { // MSSpWindowColumns msSpwSubTable(ms()->spectralWindow()); // MSDataDescColumns msDataDescSubTable(ms()->dataDescription()); MSSpWindowColumns msSpwSubTable(spwSubTable_p); MSDataDescColumns msDataDescSubTable(ddSubTable_p); Vector mapFreq2SpwID; Vector mapDDID2SpwID; Int nSpwRows, nDDIDRows; Bool Found; TableExprNode condition; const String DATA_DESC_ID = MS::columnName(MS::DATA_DESC_ID); nSpwRows = msSpwSubTable.nrow(); nDDIDRows = msDataDescSubTable.nrow(); mapDDID2SpwID.resize(nDDIDRows); mapFreq2SpwID.resize(nSpwRows); for(Int i=0;icol(DATA_DESC_ID)==ddid)); // else // condition = condition || ((ms()->col(DATA_DESC_ID)==ddid)); break; } } if (!Found) { ostringstream Mesg; Mesg << "No Spw ID found"; throw(MSSelectionSpwError(Mesg.str())); } } addCondition(*node_p, condition); return node_p; } // //------------------------------------------------------------------ // void MSSpwParse::selectChannelsFromIDList(Vector& spwIds, Vector& chanIDList, Int nFSpec) { Int n=chanList.shape()(0), nSpw = spwIds.nelements(), loc=n,k=0; for (Int i=0;i& spwIds, Vector& chanIDList) { if (spwIds.nelements() != chanIDList.nelements()/3) throw(AipsError("MSSpwParse::selectChannelsFromDefaultList(): SPW and default channel " "lists should be of the same size")); Int n=chanList.shape()(0), nSpw = spwIds.nelements(); Int m=nSpw,loc=n,j=0; chanList.resize(n+m,4,True); for(Int i=0;i vec(idList.nelements()); for (uInt i=0;i uniqueIDList(vec); const TableExprNode *tten= MSSpwParse::thisMSSParser->selectSpwIdsFromIDList(uniqueIDList,True,False); if (tten->isNull()) { ostringstream Mesg; Mesg << "No Spw ID(s) matched specifications "; throw(MSSelectionSpwError(Mesg.str())); } // idList.assign(uniqueIDList); return tten; } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSSel/MSSpwParse.h000066400000000000000000000121441476623553700171050ustar00rootroot00000000000000//# MSSpwParse.h: Classes to hold results from spw grammar parser //# Copyright (C) 1994,1995,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSPWPARSE_H #define MS_MSSPWPARSE_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // Class to hold values from field grammar parser // // // // // //# Classes you should understand before using this one. // // // MSSpwParse is the class used to parse a spectral window selection command. // // MSSpwParse is used by the parser of spectral window // (Spw) sub-expression statements. The parser is written in Bison // and Flex in files MSSpwGram.y and .l. The statements in there use // the routines in this file to act upon a reduced rule. Since // multiple tables can be given (with a shorthand), the table names // are stored in a list. The variable names can be qualified by the // table name and will be looked up in the appropriate table. // // The class MSSpwParse only contains information about a table // used in the table command. Global variables (like a list and a vector) // are used in MSSpwParse.cc to hold further information. // // Global functions are used to operate on the information. // The main function is the global function msSpwCommand. // It executes the given STaQL command and returns the resulting ms. // This is, in fact, the only function to be used by a user. // // // It is necessary to be able to give a ms command in ASCII. // This can be used in a CLI or in the table browser to get a subset // of a table or to sort a table. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class MSSpwParse : public MSParse { public: // Default constructor MSSpwParse (); // ~MSSpwParse() {if (node_p) delete node_p;node_p=0x0;}; // Associate the ms and the shorthand. MSSpwParse (const MeasurementSet* ms); MSSpwParse (const MSSpectralWindow& spwSubTable, const MSDataDescription& ddSubTable, const TableExprNode& columnAsTEN); ~MSSpwParse() {columnAsTEN_p=TableExprNode();}; const TableExprNode *selectSpwIdsFromIDList(const Vector& spwIds, const Bool addTen=True, const Bool addIDs=True); const TableExprNode *selectSpwIdsFromFreqList(const Vector& spwIds, const Float factor); void selectChannelsFromIDList(Vector& spwIds, Vector& chanIDList, Int nFSpec); void selectChannelsFromDefaultList(Vector& spwIds, Vector& chanDefaultList); const TableExprNode* endOfCeremony(const TableExprNode& ten); // const TableExprNode *selectSpwOrSource(const String& fieldName); // Get table expression node object. static const TableExprNode* node(); static MSSpwParse* thisMSSParser; static std::shared_ptr thisMSSpwErrorHandler; static Vector selectedDDIDs() {return ddidList;} static Vector selectedIDs() {return idList;} static Matrix selectedChanIDs() {return chanList;} static void reset() {idList.resize(0);chanList.resize(0,0);ddidList.resize(0);}; static void cleanupNode() {if (node_p) delete node_p;node_p=0x0;} static void cleanupErrorHandler() {thisMSSpwErrorHandler.reset();} static void cleanup() {cleanupNode(); cleanupErrorHandler();} MSSpectralWindow& subTable() {return spwSubTable_p;} private: static TableExprNode* node_p; static Vector idList, ddidList; static Matrix chanList; MSSpectralWindow spwSubTable_p; MSDataDescription ddSubTable_p; static TableExprNode columnAsTEN_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSStateGram.cc000066400000000000000000000127161476623553700173730ustar00rootroot00000000000000//# MSStateGram.cc: Grammar for field expressions //# Copyright (C) 1998,1999,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // MSStateGram; grammar for field command lines // This file includes the output files of bison and flex for // parsing command lines operating on lattices. #include #include #include #include #include #include #include #include #include // routines used by bison actions #include //# stdlib.h is needed for bison 1.28 and needs to be included here //# (before the flex/bison files). //# Bison defines WHERE, which is also defined in LogOrigin.h (which //# is included in auto-template mode). //# So undefine WHERE first. #undef WHERE #include //# Let clang and gcc ignore some warnings in bison/flex code. //# Undef YY_NULL because flex can redefine it slightly differently (giving a warning). #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpragmas" #pragma GCC diagnostic ignored "-Wdeprecated-register" #pragma GCC diagnostic ignored "-Wsign-compare" #include "MSStateGram.ycc" // bison output #ifdef YY_NULL # undef YY_NULL #endif #include "MSStateGram.lcc" // flex output #pragma GCC diagnostic pop // Define the yywrap function for flex. int MSStateGramwrap() { return 1; } namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Declare a file global pointer to a char* for the input string. static const char* strpMSStateGram = 0; static Int posMSStateGram = 0; // MSStateGramwrap out of namespace //# Parse the command. //# Do a yyrestart(yyin) first to make the flex scanner reentrant. int msStateGramParseCommand (const MeasurementSet* ms, const String& command) { try { Int ret; MSStateGramrestart (MSStateGramin); yy_start = 1; strpMSStateGram = command.chars(); // get pointer to command string posMSStateGram = 0; // initialize string position MSStateParse parser(ms); // setup measurement set MSStateParse::thisMSSIParser = &parser; // The global pointer to the parser MSStateParse::thisMSSIParser->reset(); // fieldError.reset(); ret=MSStateGramparse(); // parse command string return ret; } catch (MSSelectionStateError &x) { String newMesgs; newMesgs = constructMessage(msStateGramPosition(), command); x.addMessage(newMesgs); throw; } } int msStateGramParseCommand (const MeasurementSet* ms, const String& command, Vector& selectedIDs) { try { Int ret; MSStateGramrestart (MSStateGramin); yy_start = 1; strpMSStateGram = command.chars(); // get pointer to command string posMSStateGram = 0; // initialize string position MSStateParse parser(ms); // setup measurement set MSStateParse::thisMSSIParser = &parser; // The global pointer to the parser parser.reset(); ret=MSStateGramparse(); // parse command string selectedIDs=parser.selectedIDs(); return ret; } catch (MSSelectionStateError &x) { String newMesgs; newMesgs = constructMessage(msStateGramPosition(), command); x.addMessage(newMesgs); throw; } } //# Give the table expression node const TableExprNode* msStateGramParseNode() { return MSStateParse::node(); } void msStateGramParseDeleteNode() {MSStateParse::cleanupNode();} //# Give the string position. Int& msStateGramPosition() { return posMSStateGram; } //# Get the next input characters for flex. int msStateGramInput (char* buf, int max_size) { int nr=0; while (*strpMSStateGram != 0) { if (nr >= max_size) { break; // get max. max_size char. } buf[nr++] = *strpMSStateGram++; } return nr; } void MSStateGramerror (const char*) { throw (MSSelectionStateParseError ("State Expression: Parse error at or near '" + String(MSStateGramtext) + "'")); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSSel/MSStateGram.h000066400000000000000000000062251476623553700172330ustar00rootroot00000000000000//# MSStateGram.h: Grammar for ms field sub-expressions //# Copyright (C) 1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSTATEGRAM_H #define MS_MSSTATEGRAM_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MeasurementSet; class TableExprNode; // // Global functions for flex/bison scanner/parser for MSStateGram // // // // // //# Classes you should understand before using this one. //
      • MSStateGram.l and .y (flex and bison grammar) // // // Global functions are needed to define the input of the flex scanner // and to start the bison parser. // The input is taken from a string. // // // It is necessary to be able to give an image expression in ASCII. // This can be used in glish. // // //# A List of bugs, limitations, extensions or planned refinements. // // // Declare the bison parser (is implemented by bison command). int msStateGramParseCommand (const MeasurementSet *ms, const String& command); int msStateGramParseCommand (const MeasurementSet *ms, const String& command,Vector&); // The yyerror function for the parser. // It throws an exception with the current token. void MSStateGramerror (const char*); // Give the table expression node. const TableExprNode *msStateGramParseNode(); void msStateGramParseDeleteNode() ; // Give the current position in the string. // This can be used when parse errors occur. Int& msStateGramPosition(); // Declare the input routine for flex/bison. int msStateGramInput (char* buf, int max_size); // A function to remove escaped characters. //String msStateGramRemoveEscapes (const String& in); // A function to remove quotes from a quoted string. //String msStateGramRemoveQuotes (const String& in); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSStateGram.ll000066400000000000000000000106661476623553700174170ustar00rootroot00000000000000/* -*- C -*- MSStateGram.l: Lexical analyzer for ms selection commands Copyright (C) 1994,1995,1996,1997,1998,2001,2003 Associated Universities, Inc. Washington DC, USA. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. Correspondence concerning AIPS++ should be addressed as follows: Internet email: casa-feedback@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA */ /* yy_unput is not used, so let flex not generate it, otherwise picky compilers will issue warnings. */ %option nounput %{ #undef YY_INPUT #define YY_INPUT(buf,result,max_size) result=msStateGramInput(buf,max_size) #undef YY_DECL #define YY_DECL int MSStateGramlex (YYSTYPE* lvalp) static string qstrState; #include %} WHITE [ \t\n]* DIGIT [0-9] INT ({WHITE}{DIGIT}+{WHITE}) QSTRING \"[^\"\n]*\" STRING ({QSTRING})+ QUOTE (\") RQUOTE (\/) NQ [^\\\n\"]+ NRQ [^\\\n\/]+ /* NAME ([A-za-z0-0_'{''}''+''-']) */ IDENTIFIER ([A-Za-z0-9_\{\}\+\-\.\=;@#%:! ]+|STRING) SIDENTIFIER ({WHITE}[A-Za-z0-9_\+\-\{\}=;@#$%:!'*''?' ]+{WHITE}) %x QS RS ESC /* rules */ %% {QUOTE} { // Start of a quoted string qstrState.resize(0); BEGIN(QS); } "\\" {printf("%s\n",MSStateGramtext);BEGIN(ESC);} . {BEGIN(INITIAL);} {NQ} {(qstrState)+= MSStateGramtext;} {QUOTE} { /* saw closing quote - all done */ BEGIN(INITIAL); lvalp->str = (char *)malloc((qstrState.length() + 1)*sizeof(char)); strcpy(lvalp->str,qstrState.c_str()); qstrState.resize(0); return QSTRING; } {RQUOTE} { // Start of a regex string qstrState.resize(0); BEGIN(RS); } {NRQ} {(qstrState)+= MSStateGramtext;} {RQUOTE} { /* saw closing quote - all done */ BEGIN(INITIAL); lvalp->str = (char *)malloc((qstrState.length() + 1)*sizeof(char)); strcpy(lvalp->str,qstrState.c_str()); qstrState.resize(0); return REGEX; } {INT} { msStateGramPosition() += yyleng; lvalp->str = (char *)malloc((strlen(MSStateGramtext) + 1) * sizeof(char)); strcpy(lvalp->str, stripWhite(MSStateGramtext).c_str()); //cout << "INT = \"" << MSStateGramtext << "\" \"" << lvalp->str << "\"" << endl; return INT; } "~" { msStateGramPosition() += yyleng; return DASH;} "," { msStateGramPosition() += yyleng; return COMMA;} "<" { msStateGramPosition() += yyleng; return LT;} ">" { msStateGramPosition() += yyleng; return GT;} "&" { msStateGramPosition() += yyleng; return AMPERSAND;} /* Literals */ {IDENTIFIER} { msStateGramPosition() += yyleng; lvalp->str = (char *)malloc((strlen(MSStateGramtext) + 1) * sizeof(char)); strcpy(lvalp->str, MSStateGramtext); // cout << "ID = \"" << MSStateGramtext << "\" \"" << lvalp->str << "\"" << endl; return IDENTIFIER; } {SIDENTIFIER} { msStateGramPosition() += yyleng; lvalp->str = (char *)malloc((strlen(MSStateGramtext) + 1) * sizeof(char)); strcpy(lvalp->str, stripWhite(MSStateGramtext).c_str()); // cout << "SID = \"" << MSStateGramtext << "\" \"" << lvalp->str << "\"" << endl; return QSTRING; } "(" { msStateGramPosition() += yyleng; return LPAREN;} ")" { msStateGramPosition() += yyleng; return RPAREN;} . { msStateGramPosition() += yyleng; return MSStateGramtext[0];} %% casacore-3.7.1/ms/MSSel/MSStateGram.yy000066400000000000000000000172631476623553700174510ustar00rootroot00000000000000/* -*- C++ -*- MSStateGram.y: Parser for field expressions Copyright (C) 2004 Associated Universities, Inc. Washington DC, USA. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. Correspondence concerning AIPS++ should be addressed as follows: Internet email: casa-feedback@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA */ %{ #include using namespace casacore; %} %pure-parser /* make parser re-entrant */ %union { const TableExprNode* node; char * str; Vector* iv; // Block* exprb; // TableExprNodeSetElem* elem; // TableExprNodeSet* settp; // Int ival[2]; // Double dval; // Vector* is; } %token SQUOTE %token IDENTIFIER %token COMMA %token LBRACKET %token LPAREN %token RBRACKET %token RPAREN %token LBRACE %token RBRACE %token WHITE %token INT %token QSTRING %token REGEX %token COLON %token SEMICOLON %type statestatement %type indexcombexpr %type indexlist %type stateidrange %type stateidlist %type stateid %type stateidbounds %type logicallist %nonassoc EQ EQASS GT GE LT LE NE COMMA DASH AMPERSAND %{ #include int MSStateGramlex (YYSTYPE*); void checkStateError(Vector& list, ostringstream& msg, Bool force=False, char* = NULL) { if ((list.nelements() == 0) || force) { String errorMesg; ostringstream Mesg; Mesg << "State Expression: " << msg.str().c_str(); errorMesg = String(Mesg.str().c_str()); MSStateParse::thisMSSErrorHandler->reportError(NULL,Mesg.str()); //throw(MSSelectionStateParseError(errorMesg)); } } %} %% statestatement: indexcombexpr { $$ = $1; } | LPAREN indexcombexpr RPAREN //Parenthesis are syntactically // not useful here { $$ = $2; } ; indexcombexpr : indexlist { ostringstream m; MSStateIndex myMSSI(MSStateParse::thisMSSIParser->ms()->state()); Vector selectedIDs(myMSSI.maskStateIDs(*($1))); if (selectedIDs.nelements() != set_intersection(selectedIDs,(*($1))).nelements()) { m << "Possible out of range index in the list " << *($1) << " [TIP: Double-quoted strings forces name matching]"; checkStateError(selectedIDs, m , True); } $$ = MSStateParse().selectStateIds(selectedIDs); m << "Partial or no match for State ID list " << (*($1)); checkStateError(selectedIDs, m); delete $1; } ; // // Ampersand separated list of stateid. The result is the logical AND // of list of indices in stateid. // logicallist: stateid AMPERSAND stateid { $$ = new Vector(set_intersection(*$1,*$3)); }; | logicallist AMPERSAND stateid { $$ = new Vector(set_intersection(*$1,*$3)); }; // // A single state name (this could be a regex and // hence produce a list of indices) // stateid: IDENTIFIER { // // Use the string as-is. This cannot include patterns/regex // which has characters that are part of range or list // syntax (',', '-') (that's all I think). // // Convert name to index // MSStateIndex myMSSI(MSStateParse::thisMSSIParser->ms()->state()); $$=new Vector(myMSSI.matchStateObsMode($1)); //$$=new Vector(myMSAI.matchStateRegexOrPattern($1)); ostringstream m; m << "No match found for \"" << $1 << "\""; checkStateError(*($$), m); free($1); } | QSTRING { // // Quoted string: This is a pattern which will be converted // to regex internally. E.g. "VLA{20,21}*" becomes // "VLA((20)|(21)).*" regex. This can include any character // string. // // Convert name to index // MSStateIndex myMSSI(MSStateParse::thisMSSIParser->ms()->state()); $$ = new Vector(myMSSI.matchStateRegexOrPattern($1)); ostringstream m; m << "No match found for \"" << $1 << "\""; checkStateError(*($$), m); // String s(m.str()); free($1); } | REGEX { // // A string delimited by a pair of '/': This will be treated // as a regular expression internally. // // Convert name to index // MSStateIndex myMSSI(MSStateParse::thisMSSIParser->ms()->state()); $$ = new Vector(myMSSI.matchStateRegexOrPattern($1,True)); ostringstream m; m << "No match found for \"" << $1 << "\""; checkStateError(*($$), m); free($1); } ; stateidrange: INT // A single state index { $$ = new Vector(1); (*($$))(0) = atoi($1); free($1); } | INT DASH INT // A range of integer state indices { Int start = atoi($1); Int end = atoi($3); Int len = end - start + 1; Vector stateids(len); for(Int i = 0; i < len; i++) { stateids[i] = start + i; } $$ = new Vector(stateids); free($1); free($3); } ; stateidbounds: LT INT // ms()->state()); Int n=atoi($2); $$ = new Vector(myMSSI.matchStateIDLT(n)); ostringstream m; m << "No state ID found <" << n; checkStateError(*($$), m); free($2); } | GT INT // >ID { MSStateIndex myMSSI(MSStateParse::thisMSSIParser->ms()->state()); Int n=atoi($2); $$ = new Vector(myMSSI.matchStateIDGT(n)); ostringstream m; m << "No state ID found >" << n; checkStateError(*($$), m); free($2); } | GT INT AMPERSAND LT INT // >ID & ms()->state()); Int n0=atoi($2), n1=atoi($5); $$ = new Vector(myMSSI.matchStateIDGTAndLT(n0,n1)); ostringstream m; m << "No state found in the range [" << n0 << "," << n1 << "]"; checkStateError(*($$), m); free($2); free($5); } ; stateidlist: stateid // A singe state ID { $$ = $1; } | logicallist // Ampersand seperated stateid list { $$ = $1; } | stateidrange // ID range ( n0-n1 ) { $$ = $1; } | stateidbounds // >ID, ID & (*$1); delete $1; } | indexlist COMMA stateidlist { $$ = $1; Int N0=(*($1)).nelements(), N1 = (*($3)).nelements(); (*($$)).resize(N0+N1,True); // Resize the existing list for(Int i=N0;i #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //------------------------------------------------------------------------- MSStateIndex::MSStateIndex(const MSState& state) : msStateCols_p(state) { nrows_p = msStateCols_p.nrow(); stateIds_p.resize(nrows_p); indgen(stateIds_p); } //------------------------------------------------------------------------- Vector MSStateIndex::matchStateRegexOrPattern(const String& pattern, const Bool regex) { Vector IDs; IDs = matchStateObsModeRegexOrPattern(pattern, regex); // if (IDs.nelements()==0) // IDs = matchStateCodeRegexOrPattern(pattern, regex); return IDs; } //------------------------------------------------------------------------- Int MSStateIndex::matchAnyRegex(const Vector& strList, const Regex& regex, const Int pos) { Int ret=0; for(uInt i=0;i 0) break; return ret; } //------------------------------------------------------------------------- Vector MSStateIndex::matchStateObsModeRegexOrPattern(const String& pattern, const Bool regex) { // Match a state name to a set of state id's // Input: // name const String& State name to match // Output: // matchStateName Vector Matching state id's // Int pos=0; Regex reg; // String strippedPattern = stripWhite(pattern); const String& strippedPattern = pattern; try { if (regex) reg=strippedPattern; else reg=reg.fromPattern(strippedPattern); } catch (...) // Since I (SB) don't know the type of exception Regex throws, catch them all! { ostringstream Mesg; Mesg << "State Expression: Invalid regular expression \"" << pattern << "\""; throw(MSSelectionStateParseError(Mesg.str().c_str())); } //cerr << "Pattern = " << strippedPattern << " Regex = " << reg.regexp() << endl; IPosition sh(msStateCols_p.obsMode().getColumn().shape()); LogicalArray maskArray(sh,False); IPosition i=sh; for(i(0)=0;i(0) substr; split(sname,',',substr); Int ret=matchAnyRegex(substr,reg,pos); maskArray(i) = ((ret>0) && !msStateCols_p.flagRow().getColumn()(i)); } MaskedArray maskStateID(stateIds_p,maskArray); return maskStateID.getCompressedArray(); } Vector MSStateIndex::maskStateIDs(const Vector& ids) { Vector tmp = set_intersection(stateIds_p,ids); return tmp; } //------------------------------------------------------------------------- Vector MSStateIndex::matchStateObsMode(const String& name) { // Match a state name to a set of state id's // Input: // name const String& State name to match // Output: // matchStateName Vector Matching state id's // IPosition irow(1,nrows_p); LogicalArray tmaskArray(irow,False); for(irow(0)=0;irow(0) substr; split(msStateCols_p.obsMode().getColumn()[irow(0)],',',substr); for (uInt istr=0; istr maskStateId(stateIds_p, tmaskArray); return maskStateId.getCompressedArray(); } //------------------------------------------------------------------------- Vector MSStateIndex::matchStateObsMode(const Vector& names) { // Match a set of state names to a set of state id's // Input: // names const Vector& State names to match // Output: // matchStateNames Vector Matching state id's // Vector matchedStateIds; // Match each state name individually for (uInt fld=0; fld currentMatch = matchStateObsMode(names(fld)); if (currentMatch.nelements() > 0) { Vector temp(matchedStateIds); matchedStateIds.resize(matchedStateIds.nelements() + currentMatch.nelements(), True); matchedStateIds = concatenateArray(temp, currentMatch); } } return matchedStateIds; } //------------------------------------------------------------------------- Vector MSStateIndex::matchStateId(const Int& stateId) { // Match a source id to a set of state id's // Input: // sourceId const Int& Source id to match // Output: // matchSourceId Vector Matching state id's // LogicalArray maskArray = (stateIds_p==stateId && !msStateCols_p.flagRow().getColumn()); MaskedArray maskStateId(stateIds_p, maskArray); return maskStateId.getCompressedArray(); } //------------------------------------------------------------------------- Vector MSStateIndex::matchStateId(const Vector& stateIds) { // Match a set of state id's to a set of state id's // Input: // stateIds const Vector& State id's to match // Output: // matchStateIds Vector Matching state id's // Vector matchedStateIds; // Match each state name individually for (uInt fld=0; fld currentMatch = matchStateId(stateIds(fld)); if (currentMatch.nelements() > 0) { Vector temp(matchedStateIds); matchedStateIds.resize(matchedStateIds.nelements() + currentMatch.nelements(), True); matchedStateIds = concatenateArray(temp, currentMatch); } } return matchedStateIds; } //------------------------------------------------------------------------- Vector MSStateIndex::matchStateIDLT(const Int n) { LogicalArray maskArray = // (msStateCols_p.stateId().getColumn() < n && (stateIds_p < n && !msStateCols_p.flagRow().getColumn()); MaskedArray maskStateId(stateIds_p, maskArray); return maskStateId.getCompressedArray(); } //------------------------------------------------------------------------- Vector MSStateIndex::matchStateIDGT(const Int n) { LogicalArray maskArray = // (msStateCols_p.stateId().getColumn() > n && (stateIds_p > n && !msStateCols_p.flagRow().getColumn()); MaskedArray maskStateId(stateIds_p, maskArray); return maskStateId.getCompressedArray(); } //------------------------------------------------------------------------- Vector MSStateIndex::matchStateIDGTAndLT(const Int n0, const Int n1) { LogicalArray maskArray = // (msStateCols_p.stateId().getColumn() >= n0 && // msStateCols_p.stateId().getColumn() <= n1 && (stateIds_p > n0 && stateIds_p < n1 && !msStateCols_p.flagRow().getColumn()); MaskedArray maskStateId(stateIds_p, maskArray); return maskStateId.getCompressedArray(); } //------------------------------------------------------------------------- } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSSel/MSStateIndex.h000066400000000000000000000075051476623553700174160ustar00rootroot00000000000000//# MSStateIndex: index or lookup in a MeasurementSet FIELD subtable //# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSTATEINDEX_H #define MS_MSSTATEINDEX_H //# includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# forward declarations // // Class to handle lookup or indexing into a MS FIELD subtable // // // // // // //
      • MeasurementSet //
      • MSState // // // // From "MeasurementSet", "FIELD subtable" and "index". // // // // This class provides lookup and indexing into an MS FIELD // subtable. These services include returning rows numbers // (which for the FIELD subtable are FIELD_ID's) associated // with specific data in the subtable. // // // // // // // Collect together all subtable indexing and lookup for the // FIELD subtable, for encapsulation and efficiency. // // // //
      • //
      • // // class MSStateIndex { public: // Construct from an MS FIELD subtable MSStateIndex(const MSState &state); // Null destructor virtual ~MSStateIndex() {} Vector matchStateIntent(const String& name); Vector matchStateIntent(const Vector& names); //ADD for file name wildcard selection Vector matchStateObsMode(const String& name); Vector matchStateObsMode(const Vector& names); // Look up FIELD_ID's for a given pattern/regex for source name/code Vector matchStateRegexOrPattern(const String& pattern, const Bool regex=False); Vector matchStateObsModeRegexOrPattern(const String& pattern, const Bool regex=False); // Look up FIELD_ID's for a given source id Vector matchStateId(const Int& sourceId); Vector matchStateId(const Vector& sourceIds); Vector maskStateIDs(const Vector& ids); Vector matchStateIDLT(const Int n); Vector matchStateIDGT(const Int n); Vector matchStateIDGTAndLT(const Int n0, const int n1); private: // Disallow null constructor MSStateIndex(); // FIELD subtable column accessor MSStateColumns msStateCols_p; // Vector cache of field id's Vector stateIds_p; Int nrows_p; Int matchAnyRegex(const Vector& strList, const Regex& regex, const Int pos=0); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSStateParse.cc000066400000000000000000000063461476623553700175610ustar00rootroot00000000000000//# MSStateParse.cc: Classes to hold results from field grammar parser //# Copyright (C) 1994,1995,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSStateParse* MSStateParse::thisMSSIParser = 0x0; // Global pointer to the parser object TableExprNode* MSStateParse::node_p = 0x0; Vector MSStateParse::idList; std::shared_ptr MSStateParse::thisMSSErrorHandler; //MSSelectionErrorHandler* MSStateParse::thisMSSErrorHandler=NULL; //# Constructor MSStateParse::MSStateParse () : MSParse(), colName(MS::columnName(MS::STATE_ID)) { if (MSStateParse::node_p!=0x0) delete MSStateParse::node_p; MSStateParse::node_p=0x0; // if (MSStateParse::thisMSSErrorHandler!=0x0) delete MSStateParse::thisMSSErrorHandler; //MSStateParse::thisMSSErrorHandler=0x0; node_p = new TableExprNode(); } //# Constructor with given ms name. MSStateParse::MSStateParse (const MeasurementSet* ms) : MSParse(ms, "State"), colName(MS::columnName(MS::STATE_ID)) { if (MSStateParse::node_p!=0x0) delete MSStateParse::node_p; MSStateParse::node_p=0x0; // if (MSStateParse::thisMSSErrorHandler!=0x0) delete MSStateParse::thisMSSErrorHandler; //MSStateParse::thisMSSErrorHandler=0x0; node_p = new TableExprNode(); idList.resize(0); // setMS(ms); } const TableExprNode *MSStateParse::selectStateIds(const Vector& stateIds) { { Vector tmp(set_union(stateIds,idList)); idList.resize(tmp.nelements()); idList = tmp; } TableExprNode condition = (ms()->col(colName).in(stateIds)); if(node_p->isNull()) *node_p = condition; else *node_p = *node_p || condition; return node_p; } const TableExprNode* MSStateParse::node() { return node_p; } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSSel/MSStateParse.h000066400000000000000000000077661476623553700174320ustar00rootroot00000000000000//# MSStateParse.h: Classes to hold results from field grammar parser //# Copyright (C) 1994,1995,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSTATEPARSE_H #define MS_MSSTATEPARSE_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward // // Class to hold values from field grammar parser // // // // // //# Classes you should understand before using this one. // // // MSStateParse is the class used to parse a field command. // // // MSStateParse is used by the parser of field sub-expression statements. // The parser is written in Bison and Flex in files MSStateGram.y and .l. // The statements in there use the routines in this file to act // upon a reduced rule. // Since multiple tables can be given (with a shorthand), the table // names are stored in a list. The variable names can be qualified // by the table name and will be looked up in the appropriate table. // // The class MSStateParse only contains information about a table // used in the table command. Global variables (like a list and a vector) // are used in MSStateParse.cc to hold further information. // // Global functions are used to operate on the information. // The main function is the global function msStateCommand. // It executes the given STaQL command and returns the resulting ms. // This is, in fact, the only function to be used by a user. // // // It is necessary to be able to give a ms command in ASCII. // This can be used in a CLI or in the table browser to get a subset // of a table or to sort a table. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class MSStateParse : public MSParse { public: // Default constructor MSStateParse(); // Associate the ms and the shorthand. MSStateParse (const MeasurementSet* ms); const TableExprNode* selectStateIds(const Vector& stateIDs); // Get table expression node object. static const TableExprNode* node(); static MSStateParse* thisMSSIParser; static std::shared_ptr thisMSSErrorHandler; //static MSSelectionErrorHandler* thisMSSErrorHandler; static Vector selectedIDs() {return idList;}; static void reset(){idList.resize(0);}; static void cleanupNode() {if (node_p) delete node_p;node_p=0x0;} static void cleanupErrorHandler() {thisMSSErrorHandler.reset();} static void cleanup() {cleanupNode(); cleanupErrorHandler();} private: static TableExprNode* node_p; const String colName; static Vector idList; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSSysCalIndex.cc000066400000000000000000000045131476623553700176660ustar00rootroot00000000000000//# MSSysCalIndex.cc: this defined MSSysCalIndex //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSSysCalIndex::MSSysCalIndex() : MSTableIndex() {;} MSSysCalIndex::MSSysCalIndex(const MSSysCal &sysCal) : MSTableIndex(sysCal, stringToVector("ANTENNA_ID,FEED_ID,SPECTRAL_WINDOW_ID")) { attachIds();} MSSysCalIndex::MSSysCalIndex(const MSSysCalIndex &other) : MSTableIndex(other) { attachIds();} MSSysCalIndex::~MSSysCalIndex() {;} MSSysCalIndex &MSSysCalIndex::operator=(const MSSysCalIndex &other) { if (this != &other) { MSTableIndex::operator=(other); attachIds(); } return *this; } void MSSysCalIndex::attach(const MSSysCal &sysCal) { MSTableIndex::attach(sysCal, stringToVector("ANTENNA_ID,FEED_ID,SPECTRAL_WINDOW_ID")); attachIds(); } void MSSysCalIndex::attachIds() { antennaId_p.attachToRecord(accessKey(), "ANTENNA_ID"); feedId_p.attachToRecord(accessKey(), "FEED_ID"); spwId_p.attachToRecord(accessKey(), "SPECTRAL_WINDOW_ID"); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSSel/MSSysCalIndex.h000066400000000000000000000054651476623553700175370ustar00rootroot00000000000000//# MSSysCalIndex: index into a MeasurementSet SYSCAL subtable //# Copyright (C) 2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSYSCALINDEX_H #define MS_MSSYSCALINDEX_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# forward declarations class MSSysCal; // // // // // // //
      • MeasurementSet //
      • MSTableIndex // // // // // // // // // // // // // // // //
      • //
      • // // class MSSysCalIndex : public MSTableIndex { public: // no index attached, use the attach function or assignment operator to change that MSSysCalIndex(); // construct one using the indicated SYSCAL table MSSysCalIndex(const MSSysCal &sysCal); // construct one from another MSSysCalIndex(const MSSysCalIndex &other); virtual ~MSSysCalIndex(); MSSysCalIndex &operator=(const MSSysCalIndex &other); void attach(const MSSysCal &sysCal); // access to the antenna ID key, throws an exception if isNull() is False Int &antennaId() {return *antennaId_p;} // access to the feed ID key, throws an exception if isNull() is False Int &feedId() {return *feedId_p;} // access to the spectral window ID key, throws an exception if isNull() is False Int &spectralWindowId() {return *spwId_p;} private: RecordFieldPtr antennaId_p, feedId_p, spwId_p; void attachIds(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSTableIndex.cc000066400000000000000000000274411476623553700175240ustar00rootroot00000000000000//# MSTableIndex.cc: this defined MSTableIndex //# Copyright (C) 2000, 2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSTableIndex::MSTableIndex() : timeVals_p(0), intervalVals_p(0), key_p(0), time_p(0.0), interval_p(0.0), lastTime_p(0.0), lastInterval_p(0.0), lastNearest_p(0), nearestFound_p(False), nearestReady_p(False), nrows_p(0), hasChanged_p(True), index_p(0), hasTime_p(False), hasInterval_p(False) {;} MSTableIndex::MSTableIndex(const Table &subTable, const Vector &indexCols, ColumnsIndex::Compare* compareFunction) : timeVals_p(0), intervalVals_p(0), key_p(0), time_p(0.0), interval_p(0.0), lastTime_p(0.0), lastInterval_p(0.0), lastNearest_p(0), nearestFound_p(False), nearestReady_p(False), nrows_p(0), hasChanged_p(True), index_p(0), hasTime_p(False), hasInterval_p(False) { attach(subTable, indexCols, compareFunction); } MSTableIndex::MSTableIndex(const MSTableIndex &other) : timeVals_p(0), intervalVals_p(0), key_p(0), time_p(0.0), interval_p(0.0), lastTime_p(0.0), lastInterval_p(0.0), lastNearest_p(0), nearestFound_p(False), nearestReady_p(False), nrows_p(0), hasChanged_p(True), index_p(0), hasTime_p(False), hasInterval_p(False) { *this = other; } MSTableIndex::~MSTableIndex() { clear(); } MSTableIndex &MSTableIndex::operator=(const MSTableIndex &other) { if (this != &other) { clear(); if (!other.tab_p.isNull()) { tab_p = other.tab_p; timeColumn_p.reference(other.timeColumn_p); intervalColumn_p.reference(other.intervalColumn_p); timeVec_p = other.timeVec_p; if (other.timeVals_p) { timeVals_p = timeVec_p.getStorage(deleteItTime_p); } intervalVec_p = other.intervalVec_p; if (other.intervalVals_p) { intervalVals_p = intervalVec_p.getStorage(deleteItInterval_p); } hasTime_p = other.hasTime_p; hasInterval_p = other.hasInterval_p; // there might not be any index columns if (other.key_p) { key_p = new Record(*other.key_p); AlwaysAssert(key_p, AipsError); index_p = new ColumnsIndex(*other.index_p); AlwaysAssert(index_p, AipsError); makeKeys(); lastKeys_p = other.lastKeys_p; } time_p = other.time_p; interval_p = other.interval_p; lastTime_p = other.lastTime_p; lastInterval_p = other.lastInterval_p; lastSearch_p = other.lastSearch_p; lastNearest_p = other.lastNearest_p; nearestFound_p = other.nearestFound_p; nearestReady_p = other.nearestReady_p; nrows_p = other.nrows_p; hasChanged_p = other.hasChanged_p; } } return *this; } void MSTableIndex::attach(const Table &subTable, const Vector &indexCols, ColumnsIndex::Compare* compareFunction) { clear(); tab_p = subTable; // is there a TIME column hasTime_p = tab_p.tableDesc().isColumn("TIME"); // is there an INTERVAL column, there must also be a TIME hasInterval_p = hasTime_p && tab_p.tableDesc().isColumn("INTERVAL"); uInt nkeys = indexCols.nelements(); if (hasTime_p) { timeColumn_p.attach(tab_p, "TIME"); // fish out the values timeVec_p = timeColumn_p.getColumn(); timeVals_p = timeVec_p.getStorage(deleteItTime_p); // interval requires a TIME if (hasInterval_p) { intervalColumn_p.attach(tab_p, "INTERVAL"); // fish out the values intervalVec_p = intervalColumn_p.getColumn(); intervalVals_p = intervalVec_p.getStorage(deleteItInterval_p); } } if (indexCols.nelements() > 0) { index_p = new ColumnsIndex(tab_p, indexCols, compareFunction); AlwaysAssert(index_p, AipsError); RecordDesc keyDesc; for (uInt i=0;isetChanged(); } RowNumbers MSTableIndex::getRowNumbers() { getInternals(); return lastSearch_p; } Int64 MSTableIndex::getNearestRow(Bool &found) { // getInternals ensures that lastSearch_p is the match to the integer keys getInternals(); if (!nearestReady_p) { // search for nearest one nearestFound_p = False; lastNearest_p = 0; if (lastSearch_p.nelements() > 0) { if (!hasTime_p) { // just integer keys, there should be just one value, just return // the first one if there is one nearestFound_p = True; lastNearest_p = lastSearch_p(0); } else { if (hasInterval_p) { if (intervalVals_p[lastSearch_p(0)] == 0) { // no time dependence, should just be one value although // we don't check for that here, return the first one // found lastNearest_p = lastSearch_p(0); nearestFound_p = True; } else { // strict time search nearestTime(); } } else { // strict time search nearestTime(); } } } nearestReady_p = True; } found = nearestFound_p; return lastNearest_p; } void MSTableIndex::nearestTime() { // this is only called when we know it is a strict time search and there // are elements in lastSearch_p, etc, etc. // this should probably be done with a call to binSearch Int thisElem = 0; Int nElem = lastSearch_p.nelements(); Bool deleteIt; const rownr_t *rowPtr = lastSearch_p.getStorage(deleteIt); while (!nearestFound_p && thisElem < nElem) { rownr_t thisRow = rowPtr[thisElem]; // needs column unit conversion here to seconds nearestFound_p = time_p < timeVals_p[thisRow]; thisElem++; } if (nearestFound_p) { thisElem--; // thisElem is the element where time_p became less that the timeColumn at that row // so, is it closer to thisElem or the one before if (thisElem <= 0) { thisElem = 0; } else { Double lowDiff = time_p - timeVals_p[rowPtr[thisElem-1]]; Double highDiff = timeVals_p[rowPtr[thisElem]] - time_p; thisElem = lowDiff > highDiff ? thisElem : thisElem-1; } } else if (nElem > 0) { // just return the last one thisElem = nElem-1; nearestFound_p = True; } lastNearest_p = rowPtr[thisElem]; // okay, we now know where the nearest time is, but is it really the one that // we wanted. if (hasInterval_p && intervalVals_p[lastNearest_p] == -1) { // this is an indeterminate interval if (time_p < timeVals_p[lastNearest_p] && !near(time_p, timeVals_p[lastNearest_p])) { // we actually want the previous one - assumes that they are all indeterminate if (thisElem == 0) { // there is no match possible here nearestFound_p = False; } else { lastNearest_p = rowPtr[thisElem-1]; } } // we have the correct one } else { // final check to make sure the intervals satisfy the criteria Double thisLowTime, thisHighTime; Double searchLowTime, searchHighTime; if (hasInterval_p) { Double width = intervalVals_p[lastNearest_p]; thisLowTime = timeVals_p[lastNearest_p] - width/2.0; thisHighTime = thisLowTime + width; } else { thisLowTime = thisHighTime = timeVals_p[lastNearest_p]; } searchLowTime = time_p - interval_p/2.0; searchHighTime = searchLowTime + interval_p; if (thisHighTime < searchLowTime || thisLowTime > searchHighTime) { // out of range, no match possible nearestFound_p = False; } // If this were in a separate function, some code duplication could // be avoided. if (!nearestFound_p) { // it might belong to a neighboring interval if (hasInterval_p) { nearestFound_p = True; if (time_p= 0 && lastNearest_p < nElem) { // double check Double width = intervalVals_p[lastNearest_p]; thisLowTime = timeVals_p[lastNearest_p] - width/2.0; thisHighTime = thisLowTime + width; searchLowTime = time_p - interval_p/2.0; searchHighTime = searchLowTime + interval_p; if (thisHighTime < searchLowTime || thisLowTime > searchHighTime) { // out of range, no match possible nearestFound_p = False; } } else { // nope, it really isn't there if (lastNearest_p < 0) lastNearest_p = 0; else lastNearest_p = nElem -1; nearestFound_p = False; } } } } lastSearch_p.freeStorage(rowPtr, deleteIt); } void MSTableIndex::makeKeys() { // resize as appropriate uInt nKeys = key_p->nfields(); intKeys_p.resize(nKeys); lastKeys_p.resize(nKeys); indexKeys_p.resize(index_p->accessKey().nfields()); for (uInt i=0;iaccessKey(), i); } lastKeys_p = -1; } void MSTableIndex::clear() { hasTime_p = hasInterval_p = nearestFound_p = nearestReady_p = False; delete index_p; index_p = 0; indexKeys_p.resize(0); delete key_p; key_p = 0; intKeys_p.resize(0); nrows_p = 0; hasChanged_p = True; lastSearch_p.resize(0); lastNearest_p = 0; lastKeys_p.resize(0); time_p = interval_p = 0.0; tab_p = Table(); } void MSTableIndex::getInternals() { if (!isNull() && (hasChanged_p || tab_p.nrow() != nrows_p || keysChanged())) { nrows_p = tab_p.nrow(); if (index_p) { uInt nkeys = intKeys_p.nelements(); lastKeys_p.resize(nkeys); for (uInt i=0;igetRowNumbers(); } else if (hasTime_p) { // all rows match at this point lastSearch_p.resize(nrows_p); indgen(lastSearch_p); } // nothing can match, lastSearch_p should already have zero elements lastTime_p = time_p; lastInterval_p = interval_p; nearestReady_p = False; hasChanged_p = False; } } Bool MSTableIndex::keysChanged() { Bool result = False; for (uInt i=0;i #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations class Record; // class ColumnsIndex; class String; // // // // // // //
      • MeasurementSet //
      • ColumnsIndex // // // // // // // // // // // // // // // //
      • //
      • // // // //
      • Make the searches smarter - for TIME sorted tables, if the time to search is // past the last seach, there's no need to search any earlier times. //
      • Need to handle the INTERVAL=-1 case fully // class MSTableIndex { public: // no index attached, use the attach function or assignment operator to change that MSTableIndex(); // construct one using the indicated subtable which is part of the parent MS // using the indicated index columns. All index columns must be scalar integer // columns. TIME and INTERVAL will be used when present. A compare function // can be provided to over-ride literal matching of column values. MSTableIndex(const Table &subTable, const Vector &indexCols, ColumnsIndex::Compare *compareFunction = 0); // construct one from another MSTableIndex(const MSTableIndex &other); virtual ~MSTableIndex(); // assignment operator, refernce semantics MSTableIndex &operator=(const MSTableIndex &other); // attach this to a subtable using indexCols void attach(const Table &subTable, const Vector &indexCols, ColumnsIndex::Compare *compareFunction = 0); // Call this when an index in an existing row has changed. There is no need to // call this when new rows are added to the table virtual void setChanged(); // access the record of index (integer) keys virtual Record &accessKey() {return *key_p;} // access the TIME to use in the search (seconds) virtual Double &time() {return time_p;} // access the INTERVAL to use in the search (seconds), must be >= 0 virtual Double &interval() {return interval_p;} // get all of the rows in the subTable which have data during the indicated time and // interval values. For now, this code will miss the case where the subtable has // interval = -1 and the start time is outside of the time range implied by the time // and interval. If the table has changed and the time is > virtual RowNumbers getRowNumbers(); // get the row number which falls in the interval and has the time nearest to the // center of the interval (time()). This also has the same problem as the previous function. virtual Int64 getNearestRow(Bool &found); // is this attached to a null table virtual Bool isNull() { return tab_p.isNull();} // return the subtable being indexed virtual Table &table() {return tab_p;} private: // the subtable Table tab_p; ScalarColumn timeColumn_p, intervalColumn_p; Vector timeVec_p, intervalVec_p; const Double *timeVals_p, *intervalVals_p; Bool deleteItTime_p, deleteItInterval_p; // Internal keys - set by user Record *key_p; Block > intKeys_p; Double time_p, interval_p; // last known integer key values Vector lastKeys_p; // last known time and interval Double lastTime_p, lastInterval_p; // last search result - matching integer keys RowNumbers lastSearch_p; // last nearest Int64 lastNearest_p; Bool nearestFound_p, nearestReady_p; // last known sub-table size rownr_t nrows_p; Bool hasChanged_p; ColumnsIndex *index_p; Block > indexKeys_p; Bool hasTime_p, hasInterval_p; void clear(); void makeKeys(); Bool keysChanged(); void getInternals(); void nearestTime(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSTimeDefinitions.h000066400000000000000000000027301476623553700204330ustar00rootroot00000000000000//# MSTimeDefinitions.h: Definitions of data structures used during parsing //# Copyright (C) 1994,1995,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSTIMEDEFINITIONS_H #define MS_MSTIMEDEFINITIONS_H #include namespace casacore{ typedef struct TimeFields{ Int year,month,day,hour,minute,sec,fsec; } TimeFields; } #endif casacore-3.7.1/ms/MSSel/MSTimeGram.cc000066400000000000000000000222101476623553700171770ustar00rootroot00000000000000//# MSTimeGram.cc: Grammar for time expressions //# Copyright (C) 1998,1999,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // MSTimeGram; grammar for time command lines // This file includes the output files of bison and flex for // parsing command lines operating on lattices. #include #include #include #include #include // routines used by bison actions #include // routines used by bison actions #include #include #include //# stdlib.h is needed for bison 1.28 and needs to be included here //# (before the flex/bison files). //# Bison defines WHERE, which is also defined in LogOrigin.h (which //# is included in auto-template mode). //# So undefine WHERE first. #undef WHERE #include //# Let clang and gcc ignore some warnings in bison/flex code. //# Undef YY_NULL because flex can redefine it slightly differently (giving a warning). #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpragmas" #pragma GCC diagnostic ignored "-Wdeprecated-register" #pragma GCC diagnostic ignored "-Wsign-compare" //# stdlib.h is needed for bison 1.28 and needs to be included here //# (before the flex/bison files). #include //# Define register as empty string to avoid warnings in C++11 compilers //# because keyword register is not supported anymore. #include "MSTimeGram.ycc" // bison output #ifdef YY_NULL # undef YY_NULL #endif #define yy_scan_chars yy_scan_chars_MSTimeGram #include "MSTimeGram.lcc" // flex output #pragma GCC diagnostic pop // Define the yywrap function for flex. int MSTimeGramwrap() { return 1; } namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Declare a file global pointer to a char* for the input string. static const char* strpMSTimeGram = 0; static Int posMSTimeGram = 0; extern MSTimeParse *thisMSTParser; //# Parse the command. //# Do a yyrestart(yyin) first to make the flex scanner reentrant. //---------------------------------------------------------------------------- int baseMSTimeGramParseCommand (MSTimeParse* parser, const String& command, Matrix& selectedTimeList) { Int ret; try { MSTimeGramrestart (MSTimeGramin); yy_start = 1; strpMSTimeGram = command.chars(); // get pointer to command string posMSTimeGram = 0; // initialize string position parser->reset(); // global pointer to it MSTimeParse::thisMSTParser = parser; ret=MSTimeGramparse(); // parse command string selectedTimeList = parser->selectedTimes(); } catch (MSSelectionTimeError &x) { String newMesgs; newMesgs = constructMessage(msTimeGramPosition()+1, command); x.addMessage(newMesgs); throw; } return ret; } int msTimeGramParseCommand (const MeasurementSet *ms, const String& command, const TableExprNode& colAsTEN, MSSelectableMainColumn& msMainColInterface, const TableExprNode& otherTens, Matrix& selectedTimeList) { MSTimeParse *thisParser = new MSTimeParse(ms,colAsTEN,msMainColInterface, otherTens); int ret; try { ret = baseMSTimeGramParseCommand(thisParser, command, selectedTimeList); } catch (MSSelectionTimeError &x) { delete thisParser; throw; } delete thisParser; return ret; } int msTimeGramParseCommand (const MeasurementSet* ms, const String& command, const TableExprNode& otherTens, Matrix& selectedTimeList) { MSTimeParse *thisParser = new MSTimeParse(ms,otherTens); int ret; try { ret = baseMSTimeGramParseCommand(thisParser, command, selectedTimeList); } catch (MSSelectionTimeError &x) { delete thisParser; throw; } delete thisParser; return ret; // Int ret; // try // { // MSTimeGramrestart (MSTimeGramin); // yy_start = 1; // strpMSTimeGram = command.chars(); // get pointer to command string // posMSTimeGram = 0; // initialize string position // MSTimeParse parser(ms,otherTens); // setup the parser and // parser.reset(); // global pointer to it // MSTimeParse::thisMSTParser = &parser; // ret=MSTimeGramparse(); // parse command string // selectedTimeList = parser.selectedTimes(); // } // catch (MSSelectionTimeError &x) // { // String newMesgs; // newMesgs = constructMessage(msTimeGramPosition()+1, command); // x.addMessage(newMesgs); // throw; // } // return ret; } int msTimeGramParseCommand (const MeasurementSet* ms, const String& command, const TableExprNode& otherTens) { Matrix timeList; Int ret; try { MSTimeGramrestart (MSTimeGramin); yy_start = 1; strpMSTimeGram = command.chars(); // get pointer to command string posMSTimeGram = 0; // initialize string position MSTimeParse parser(ms,otherTens); // setup the parser and // global pointer to it MSTimeParse::thisMSTParser = &parser; ret=MSTimeGramparse(); // parse command string } catch (MSSelectionTimeError &x) { String newMesgs; newMesgs = constructMessage(msTimeGramPosition()+1, command); x.addMessage(newMesgs); throw; } return ret; } //# Give the table expression node //---------------------------------------------------------------------------- const TableExprNode* msTimeGramParseNode() { return MSTimeParse::node(); } void msTimeGramParseDeleteNode() { return MSTimeParse::cleanup(); } //# Give the string position. //---------------------------------------------------------------------------- Int& msTimeGramPosition() { return posMSTimeGram; } //# Get the next input characters for flex. //---------------------------------------------------------------------------- int msTimeGramInput (char* buf, int max_size) { int nr=0; while (*strpMSTimeGram != 0) { if (nr >= max_size) { break; // get max. max_size char. } buf[nr++] = *strpMSTimeGram++; } return nr; } //---------------------------------------------------------------------------- void MSTimeGramerror (const char*) { throw(MSSelectionTimeParseError("MSSelection time error: Parse error at or near token '" + String(MSTimeGramtext) + "'")); } String msTimeGramRemoveEscapes (const String& in) { String out; int leng = in.length(); for (int i=0; i #include #include #include // routines used by bison actions #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MeasurementSet; class TableExprNode; // // Global functions for flex/bison scanner/parser for MSTimeGram // // // // // //# Classes you should understand before using this one. //
      • MSTimeGram.l and .y (flex and bison grammar) // // // Global functions are needed to define the input of the flex scanner // and to start the bison parser. // The input is taken from a string. // // // It is necessary to be able to give an image expression in ASCII. // This can be used in glish. // // //# A List of bugs, limitations, extensions or planned refinements. // // // Declare the bison parser (is implemented by bison command). int msTimeGramParseCommand (const MeasurementSet *ms, const String& command, const TableExprNode& otherTens); int msTimeGramParseCommand (const MeasurementSet *ms, const String& command, const TableExprNode& otherTens, Matrix& timeList); int msTimeGramParseCommand (const MeasurementSet *ms, const String& command, const TableExprNode& colAsTEN, MSSelectableMainColumn& msMainColInterface, const TableExprNode& otherTens, Matrix& timeList); int baseMSTimeGramParseCommand (MSTimeParse* parser, const String& command, Matrix& selectedTimeList); // The yyerror function for the parser. // It throws an exception with the current token. void MSTimeGramerror (const char*); // Give the table expression node. const TableExprNode *msTimeGramParseNode(); void msTimeGramParseDeleteNode(); // Give the current position in the string. // This can be used when parse errors occur. Int& msTimeGramPosition(); // Declare the input routine for flex/bison. int msTimeGramInput (char* buf, int max_size); // A function to remove escaped characters. String msTimeGramRemoveEscapes (const String& in); // A function to remove quotes from a quoted string. //String msTimeGramRemoveQuotes (const String& in); // A function to set the fields of the TimeFields structure void msTimeGramSetTimeFields (struct TimeFields& tf, Int year, Int month, Int day, Int hour, Int minute, Int sec, Int fsec); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSTimeGram.ll000066400000000000000000000073621476623553700172340ustar00rootroot00000000000000/* -*- C++ -*- MSTimeGram.l: Lexical analyzer for ms selection commands Copyright (C) 1994,1995,1996,1997,1998,2001,2003 Associated Universities, Inc. Washington DC, USA. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. Correspondence concerning AIPS++ should be addressed as follows: Internet email: casa-feedback@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA */ /* yy_unput is not used, so let flex not generate it, otherwise picky compilers will issue warnings. */ %option nounput %{ #undef YY_INPUT #define YY_INPUT(buf,result,max_size) result=msTimeGramInput((char*) buf,max_size) #undef YY_DECL #define YY_DECL int MSTimeGramlex (YYSTYPE* lvalp) %} WHITE [ \t\n]* DIGIT [0-9] INT {DIGIT}+ EXP [DdEe][+-]?{INT} POINT \. FNUMBER ({INT}|{INT}{POINT}|{POINT}{INT}|{INT}{POINT}{INT}) NUMBER {INT} TRUE T FALSE F QSTRING \"[^\"\n]*\" ASTRING \'[^\'\n]*\' UQSTRING \"[^\"\n]*\n UASTRING \'[^\'\n]*\n STRING ({QSTRING}|{ASTRING})+ USTRING ({UQSTRING}|{UASTRING})+ DISTANCEUNIT [Kk][Mm] ML [Mm][Ll] KL [Kk][Ll] L [Ll] WAVELENTHUNIT {ML}|{KL}|{L} REGEX1 m"/"[^/]+"/" REGEX2 m%[^%]+% REGEX3 m#[^#]+# REGEX {REGEX1}|{REGEX2}|{REGEX3} /* rules */ %% {WHITE} {msTimeGramPosition() += yyleng;;} /* Just eat the white spaces */ ":" { msTimeGramPosition() += yyleng; return COLON; } ">" { msTimeGramPosition() += yyleng; return GT; } "<=" { msTimeGramPosition() += yyleng; return LE; } "<" { msTimeGramPosition() += yyleng; return LT; } "&&" { msTimeGramPosition() += yyleng; return AND; } "||" { msTimeGramPosition() += yyleng; return OR; } "!" { msTimeGramPosition() += yyleng; return NOT; } "." { msTimeGramPosition() += yyleng; return DOT; } "/" { msTimeGramPosition() += yyleng; return SLASH; } "%" { msTimeGramPosition() += yyleng; return PERCENT; } "+" { msTimeGramPosition() += yyleng; return PLUS; } "~" { msTimeGramPosition() += yyleng; return DASH; } "'" { msTimeGramPosition() += yyleng; return SQUOTE; } "," { msTimeGramPosition() += yyleng; return COMMA; } "*" { msTimeGramPosition() += yyleng; return STAR; } "[" { msTimeGramPosition() += yyleng; return LSQBRACKET;} "]" { msTimeGramPosition() += yyleng; return RSQBRACKET;} /* Literals */ {NUMBER} { msTimeGramPosition() += yyleng; lvalp->ival = atoi((const char *) MSTimeGramtext); return NUMBER; } {FNUMBER} { msTimeGramPosition() += yyleng; lvalp->dval = atof((const char *) MSTimeGramtext); return FNUMBER; } . {return UNKNOWN;} %% casacore-3.7.1/ms/MSSel/MSTimeGram.yy000066400000000000000000000261731476623553700172670ustar00rootroot00000000000000/* -*-C++-*- MSTimeGram.y: Parser for time expressions Copyright (C) 2004 Associated Universities, Inc. Washington DC, USA. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. Correspondence concerning AIPS++ should be addressed as follows: Internet email: casa-feedback@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA */ %{ using namespace casacore; %} %pure-parser /* make parser re-entrant */ %union { const TableExprNode* node; Block* exprb; TableExprNodeSetElem* elem; TableExprNodeSet* settp; const MEpoch* tval; Int ival; Double dval; Double dval3[3]; Int ival3[3]; TimeFields timeFields; } %token NUMBER %token FNUMBER %token SQUOTE %token DASH %token LT %token GT %token COLON %token COMMA %token SLASH %token DOT %token PERCENT %token STAR %token LSQBRACKET %token RSQBRACKET %token UNKNOWN %type timestatement %type timeexpr %type singletimeexpr %type rangetimeexpr %type brangetimeexpr %type upboundtimeexpr %type lowboundtimeexpr %type yeartimeexpr %type LBRACKET %type wildFloat %type wildNumber %type tFields %type timeObj %type yFields %type yearObj %left OR %left AND %nonassoc GT LT LE COLON SLASH %left PLUS MINUS %left TIMES DIVIDE MODULO %nonassoc UNARY %nonassoc NOT %right POWER %{ #include // extern MSTimeParse *thisMSTParser; Bool MSTimeEdgeInclusiveRange=False; Float MSTimeEdgeBuffer=-1.0; int MSTimeGramlex (YYSTYPE*); inline void MSTGgarbageCollector(const MEpoch* tval){if (tval) delete tval;} void splitSec(const Double& fsec, Int &sec, Int &milliSec) { sec = (Int)(fsec); if (sec < 0) milliSec = -1; else milliSec = (Int)((fsec - sec)*1000); } %} %% timestatement: timeexpr {$$ = $1;} | timestatement COMMA timeexpr { // // The various MSTimeParse::select* methods return the // static member node_p. Each visit to these methods // produces a TEN which is ORed with the existing node_p. // Effectively node_p is a global store of the TENs tree // generated during parsing. Hence here $1 and $3 both // refer to the same TEN tree (node_p) which anyway has // all the ORed TENs. So just return either $1 or $3; We // choose the first-come-first-served principle and // return $1 :-) $$=$1; } ; timeexpr: singletimeexpr | brangetimeexpr | lowboundtimeexpr | upboundtimeexpr ; wildNumber: STAR {$$ = -1;} | NUMBER {$$ = $1;} ; wildFloat: wildNumber {$$ = $1;} | FNUMBER {$$ = $1;} ; singletimeexpr: yeartimeexpr { MSTimeParse::thisMSTParser->setDefaults($1); const MEpoch *t0=MSTimeParse::thisMSTParser->yearTimeConvert($1); // $$ = MSTimeParse().selectTime(t0, false); $$ = MSTimeParse::thisMSTParser->selectTime(t0, false); MSTGgarbageCollector(t0); } ; LBRACKET: LSQBRACKET {$$=-1.0;} | FNUMBER LSQBRACKET {$$=$1;} brangetimeexpr: LBRACKET {MSTimeEdgeInclusiveRange=True;MSTimeEdgeBuffer=$1;} rangetimeexpr RSQBRACKET {$$=$3;MSTimeEdgeInclusiveRange=False;MSTimeEdgeBuffer=-1.0;} | rangetimeexpr {MSTimeEdgeInclusiveRange=False;MSTimeEdgeBuffer=-1.0;$$=$1;} rangetimeexpr: yeartimeexpr DASH yeartimeexpr { MSTimeParse::thisMSTParser->setDefaults($1); MSTimeParse::thisMSTParser->copyDefaults($3,$1); const MEpoch *t0=MSTimeParse::thisMSTParser->yearTimeConvert($1); const MEpoch *t1=MSTimeParse::thisMSTParser->yearTimeConvert($3); $$ = MSTimeParse::thisMSTParser->selectTimeRange(t0,t1,MSTimeEdgeInclusiveRange,MSTimeEdgeBuffer); MSTGgarbageCollector(t0); MSTGgarbageCollector(t1); } | yeartimeexpr PLUS yeartimeexpr { MSTimeParse::thisMSTParser->setDefaults($1); MSTimeParse::thisMSTParser->setDefaults($3,false); MSTimeParse::thisMSTParser->validate($1); MSTimeParse::thisMSTParser->validate($3); Double s0 = Double($1.sec) + Double($1.fsec) / 1000.0; Double s1 = Double($3.sec) + Double($3.fsec) / 1000.0; Time time0($1.year,$1.month,$1.day,$1.hour,$1.minute,s0); Time time1($3.year,$3.month,$3.day,$3.hour,$3.minute,s1); Double mjd0 = time0.modifiedJulianDay(); Double mjd1 = time1.modifiedJulianDay(); MVEpoch mve0(mjd0); MVEpoch mve1(mjd0, mjd1); const MEpoch *t0=new MEpoch(mve0); const MEpoch *t1=new MEpoch(mve1); $$ = MSTimeParse::thisMSTParser->selectTimeRange(t0,t1,MSTimeEdgeInclusiveRange,MSTimeEdgeBuffer); MSTGgarbageCollector(t0); MSTGgarbageCollector(t1); } ; lowboundtimeexpr: GT yeartimeexpr { MSTimeParse::thisMSTParser->setDefaults($2); const MEpoch *t0=MSTimeParse::thisMSTParser->yearTimeConvert($2); // $$ = MSTimeParse().selectTimeGT(t0,false); $$ = MSTimeParse::thisMSTParser->selectTimeGT(t0,false); MSTGgarbageCollector(t0); } ; upboundtimeexpr: LT yeartimeexpr { MSTimeParse::thisMSTParser->setDefaults($2); const MEpoch *t0=MSTimeParse::yearTimeConvert($2); // $$ = MSTimeParse().selectTimeLT(t0,false); $$ = MSTimeParse::thisMSTParser->selectTimeLT(t0,false); MSTGgarbageCollector(t0); } ; tFields: wildNumber COLON {$$ = $1;} | COLON {$$ = 0;} ; yFields: wildNumber SLASH {$$ = $1;} | SLASH {$$ = 0;} ; timeObj: tFields tFields wildFloat {/* HH:MM:SS.FF */ $$[0] = $1; $$[1] = $2; $$[2] = $3;} | tFields tFields {/* HH:MM */ $$[0] = $1; $$[1] = $2; $$[2] = 0;} | tFields wildFloat {/* MM:SS.FF */ $$[0] = 0; $$[1] = $1; $$[2] = $2;} | tFields {/* HH: */ $$[0] = 0; $$[1] = 0; $$[2] = $1;} | wildNumber {/* HH */ $$[0] = -1; $$[1] = -1; $$[2] = $1;} ; yearObj: yFields yFields wildNumber {/* YYYY/MM/DD */ $$[0]=$1; $$[1]=$2; $$[2]=$3;} | yFields yFields {/* MM/DD/ */ $$[0]=0; $$[1]=$1; $$[2]=$2;} /* | yFields wildNumber { // MM/DD $$[0]=0; $$[1]=$1; $$[2]=$2; } */ ; yeartimeexpr: yearObj SLASH timeObj { // YY/MM/DD/HH:MM:SS.FF Int sec,milliSec; splitSec($3[2],sec,milliSec); msTimeGramSetTimeFields($$,$1[0],$1[1],$1[2],(Int)$3[0],(Int)$3[1],sec,milliSec); /* cout << $1[0] << " " */ /* << $1[1] << " " */ /* << $1[2] << " " */ /* << $3[0] << " " */ /* << $3[1] << " " */ /* << sec << " " << milliSec << endl; */ } | yearObj { msTimeGramSetTimeFields($$,$1[0],$1[1],$1[2],-1,-1,-1,-1); /* cout << $1[0] << " " */ /* << $1[1] << " " */ /* << $1[2] << " " */ /* << "-1 -1 -1 -1" */ /* << endl; */ } | timeObj { Int sec,milliSec; splitSec($1[2],sec,milliSec); msTimeGramSetTimeFields($$,-1,-1,-1,(Int)$1[0],(Int)$1[1],sec,milliSec); /* cout << -1 << " " */ /* << -1 << " " */ /* << -1 << " " */ /* << $1[0] << " " << $1[1] << " " << sec << " " << milliSec */ /* << endl; */ } ; %% /* yeartimeexpr: WNUMBER SLASH WNUMBER SLASH WNUMBER SLASH WNUMBER COLON WNUMBER COLON FLOAT { // YY/MM/DD/HH:MM:SS.FF Int sec,milliSec; splitSec($11,sec,milliSec); msTimeGramSetTimeFields($$,$1,$3,$5,$7,$9,sec,milliSec); } | WNUMBER SLASH WNUMBER SLASH WNUMBER SLASH WNUMBER COLON WNUMBER { // YY/MM/DD/HH:MM msTimeGramSetTimeFields($$,$1,$3,$5,$7,$9,-1,-1); } | WNUMBER SLASH WNUMBER SLASH WNUMBER SLASH WNUMBER { // YY/MM/DD/HH msTimeGramSetTimeFields($$,$1,$3,$5,$7,-1,-1,-1); } | WNUMBER SLASH WNUMBER SLASH WNUMBER { // YY/MM/DD msTimeGramSetTimeFields($$,$1,$3,$5,-1,-1,-1,-1); } | WNUMBER SLASH WNUMBER SLASH WNUMBER COLON WNUMBER COLON FLOAT { // MM/DD/HH:MM:SS.FF Int sec,milliSec; splitSec($9,sec,milliSec); msTimeGramSetTimeFields($$,-1,$1,$3,$5,$7,sec,milliSec); } | WNUMBER COLON WNUMBER COLON FLOAT { // HH:MM:SS.FF Int sec,milliSec; splitSec($5,sec,milliSec); msTimeGramSetTimeFields($$,-1,-1,-1,$1,$3,sec,milliSec); } | WNUMBER COLON FLOAT { // MM:SS.FF Int sec,milliSec; splitSec($3,sec,milliSec); msTimeGramSetTimeFields($$,-1,-1,-1,-1,$1,sec,milliSec); } | FLOAT { Int sec,milliSec; splitSec($1,sec,milliSec); msTimeGramSetTimeFields($$,-1,-1,-1,-1,-1,sec,milliSec); } | WNUMBER SLASH WNUMBER COLON WNUMBER COLON FLOAT { // DD/HH:MM:SS Int sec,milliSec; splitSec($7,sec,milliSec); msTimeGramSetTimeFields($$,-1,-1,$1,$3,$5,sec,milliSec); } | WNUMBER SLASH WNUMBER COLON WNUMBER { // DD/HH:MM msTimeGramSetTimeFields($$,-1,-1,$1,$3,$5,-1,-1); } | WNUMBER SLASH WNUMBER { // DD/HH msTimeGramSetTimeFields($$,$1,$3,-1,-1,-1,-1,-1); } ; */ /* daytimeexpr: WNUMBER SLASH WNUMBER COLON WNUMBER COLON WNUMBER DOT WNUMBER { // DD/HH:MM:SS.FF msTimeGramSetTimeFields($$,$1,$3,$5,$7,$9,-1,-1); // $$ = MSTimeParse::dayTimeConvert($1, $3, $5, $7, $9); } | WNUMBER SLASH WNUMBER COLON WNUMBER COLON WNUMBER { // DD/HH:MM:SS msTimeGramSetTimeFields($$,-1,-1,$1,$3,$5,$7,-1); // $$ = MSTimeParse::dayTimeConvert($1, $3, $5, $7, 0); } | WNUMBER SLASH WNUMBER COLON WNUMBER { // DD/HH:MM msTimeGramSetTimeFields($$,-1,-1,$1,$3,$5,-1,-1); // $$ = MSTimeParse::dayTimeConvert($1, $3, $5, 0, 0); } | WNUMBER SLASH WNUMBER { // DD/HH msTimeGramSetTimeFields($$,$1,$3,-1,-1,-1,-1,-1); // $$ = MSTimeParse::dayTimeConvert($1, $3, 0, 0, 0); } ; */ casacore-3.7.1/ms/MSSel/MSTimeParse.cc000066400000000000000000000373211476623553700173740ustar00rootroot00000000000000//# MSTimeParse.cc: Classes to hold results from time grammar parser //# Copyright (C) 1994,1995,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSTimeParse* MSTimeParse::thisMSTParser = 0x0; // Global pointer to the parser object TableExprNode* MSTimeParse::node_p = 0x0; MEpoch* MSTimeParse::yeartime = 0x0; MEpoch* MSTimeParse::daytime = 0x0; // MSTimeParse *thisMSTParser = 0x0; MeasurementSet* MSTimeParse::ms_p = 0x0; TableExprNode* MSTimeParse::otherTens_p = 0x0; Bool MSTimeParse::defaultTimeComputed=False; Matrix MSTimeParse::timeList(3,0); TableExprNode MSTimeParse::columnAsTEN_p; MSSelectableMainColumn *MSTimeParse::mainColumn_p=0x0; //------------------------------------------------------------------- // Constructor // MSTimeParse::MSTimeParse () : MSParse(), colName(MS::columnName(MS::TIME)) { defaultYear = defaultMonth = defaultDay = defaultHour = defaultMinute = defaultSeconds = defaultFractionalSec = -1; defaultExposure = 1.0; if(node_p) delete node_p; node_p = new TableExprNode(); ms_p= 0x0; otherTens_p=0x0; defaultTimeComputed=False; } //------------------------------------------------------------------- // Constructor with given ms name. // MSTimeParse::MSTimeParse (const MeasurementSet* ms, const TableExprNode& otherTens, const Bool honourRowFlags) : MSParse(ms, "Time"), colName(MS::columnName(MS::TIME)), honourRowFlags_p(honourRowFlags) { if(node_p) delete node_p; ms_p= (MeasurementSet*)ms; node_p = new TableExprNode(); otherTens_p=(TableExprNode *)&otherTens; defaultTimeComputed=False; } MSTimeParse::MSTimeParse (const MeasurementSet* ms, const TableExprNode& colAsTEN, MSSelectableMainColumn& msMainColInterface, const TableExprNode& otherTens, const Bool honourRowFlags) : MSParse(ms,"Time"), colName(MS::columnName(MS::TIME)), honourRowFlags_p(honourRowFlags) { // throw(MSSelectionTimeError("THIS INTERFACE IS NOT YET USABLE. THE MS_P POINTER IS NOT SET!!" // " THAT IS REQUIRED in MSTimeParse::getDefaults()")); if(node_p) delete node_p; ms_p= (MeasurementSet*)ms; node_p = new TableExprNode(); otherTens_p=(TableExprNode *)&otherTens; columnAsTEN_p=colAsTEN; mainColumn_p=&msMainColInterface; defaultTimeComputed=False; } // // MSMainColInterface objects is a generalization of // MSMainColumns. This is constructed with a Table, which can be // MS or CalTable (or any other table with interface methods like // those in MSMainColumns. The access methods of MSMainColumns // allowed via MSMacinColInterface are flag(), flagRow(), // exposureQuant() and timeQuant(). // // The MSMainColInterface::flagRow(int &) is slightly slower than // MSMainColumns::flagRow()(int &) interface. However since this // is used only to determine the first unflagged row, the loss in // effciency should not be an issue. // void MSTimeParse::getDefaults() { uInt firstLogicalRow=0; // This is the logical first row //MSMainColumns mainColumns_l(*ms_p); // MSMainColInterface mainColumns_l(*ms_p); if (!defaultTimeComputed) { uInt i=0,nrow=(mainColumn_p->flag()).nrow(); if (!otherTens_p->isNull()) { Bool selected=False; for(i=0;iflagRow(i)) { otherTens_p->get(i,selected); } else { otherTens_p->get(i,selected); } if (selected) {firstLogicalRow=i;break;} } } } else if (honourRowFlags_p) { // // Find the first row which is not flagged. // for (i=0;iflagRow(i)) { firstLogicalRow=i;break; } } } if ( firstLogicalRow >= nrow) { throw(MSSelectionTimeError("MSTimeParse: No logical \"row zero\" found for time selection")); } } // // Extract the values from the first valid timestamp in the MS to // be used for defaults // // // Get the exposure in seconds. // ScalarQuantColumn exposure; exposure.reference(mainColumn_p->exposureQuant()); if (ms_p == NULL) { // This instance is not attached to an MS (which // means, for now, it must be attached to a CalTable) defaultExposure=0.1; // For now, arbitrarily set it a small value // for CalTables } else { defaultExposure=exposure(firstLogicalRow,"s").getValue(); } firstRowTime = mainColumn_p->timeQuant()(firstLogicalRow); // cout << firstRowTime.string(MVTime::DMY,7) << endl; Time t0(firstRowTime.getTime()); defaultYear = firstRowTime.year(); defaultMonth = firstRowTime.month(); defaultDay = firstRowTime.monthday(); defaultHour = t0.hours(); defaultMinute = t0.minutes(); defaultSeconds = t0.seconds(); Time t1(defaultYear,defaultMonth,defaultDay,defaultHour,defaultMinute,defaultSeconds); defaultFractionalSec = (Int)((t0-t1)*1E3); defaultTimeComputed=True; } // //------------------------------------------------------------------- // const TableExprNode *MSTimeParse::addCondition(TableExprNode& condition) { if(node_p->isNull()) { *node_p = condition; } else { *node_p = *node_p || condition; } return node_p; } // //------------------------------------------------------------------- // const TableExprNode *MSTimeParse::selectTime(const MEpoch& time, bool) { Double timeInSec= toTAIInSec(time); Double dT= MSTimeParse::thisMSTParser->defaultExposure/2.0; // TableExprNode condition = (abs(ms()->col(colName) - timeInSec) <= dT); TableExprNode condition = (abs(columnAsTEN_p - timeInSec) <= dT); // TableExprNode condition = (abs(columnAsTEN_p - timeInSec) <= dT); accumulateTimeList(timeInSec,timeInSec,dT); return addCondition(condition); } // //------------------------------------------------------------------- // const TableExprNode *MSTimeParse::selectTimeGT(const MEpoch& lowboundTime, bool) { Double timeInSec = toTAIInSec(lowboundTime); // TableExprNode condition = (ms()->col(colName) >= timeInSec); TableExprNode condition = (columnAsTEN_p >= timeInSec); // TableExprNode condition = (columnAsTEN_p >= timeInSec); accumulateTimeList(timeInSec,std::numeric_limits::max()); return addCondition(condition); } // //------------------------------------------------------------------- // const TableExprNode *MSTimeParse::selectTimeLT(const MEpoch& upboundTime, bool) { Double timeInSec = toTAIInSec(upboundTime); // TableExprNode condition = (ms()->col(colName) <= timeInSec); TableExprNode condition = (columnAsTEN_p <= timeInSec); // TableExprNode condition = (columnAsTEN_p <= timeInSec); accumulateTimeList(0.0, timeInSec); return addCondition(condition); } // //------------------------------------------------------------------- // const TableExprNode *MSTimeParse::selectTimeRange(const MEpoch& lowboundTime, const MEpoch& upboundTime, bool edgeInclusive, Float edgeWidth) { Double upperBound = toTAIInSec(upboundTime); Double lowerBound = toTAIInSec(lowboundTime); if (lowerBound > upperBound) { throw(MSSelectionTimeError("lower bound > upper bound")); } TableExprNode condition; // edgeWidth < 0, edgeInclusive=F ==> T0~T1 syntax // edgeWidth < 0, edgeInclusive=T ==> [T0~T1] syntax // edgeWidth = N, edgeInclusive=T ==> N[T0~T1] syntax Float edgeWidth_l = (edgeWidth < 0.0) ? (edgeInclusive==True ? defaultExposure/2.0 : 0.0) : edgeWidth; if (!edgeInclusive) { condition = (columnAsTEN_p >= lowerBound && (columnAsTEN_p <= upperBound)); } else { condition = (((columnAsTEN_p > lowerBound) || (abs(columnAsTEN_p - lowerBound) < edgeWidth_l)) && ((columnAsTEN_p < upperBound) || (abs(columnAsTEN_p - upperBound) < edgeWidth_l))); } accumulateTimeList(lowerBound, upperBound, edgeWidth_l); return addCondition(condition); } // //------------------------------------------------------------------- // /* const MEpoch *MSTimeParse::dayTimeConvert(Int day, Int hour, Int minute, Int second, Int millisec) { if(daytime) delete daytime; if (day == -1) day=MSTimeParse().day0(); if (hour == -1) hour=MSTimeParse().hour0(); if (minute == -1) minute=MSTimeParse().minute0(); if (second == -1) second=MSTimeParse().second0(); if (millisec == -1) millisec = MSTimeParse().fractionalsec0(); Double s = Double(second) + Double(millisec)/1000.0; Time t(0, 0, day, hour, minute, s); MVEpoch mv(t.modifiedJulianDay()); // return (daytime = new MEpoch(mv, MEpoch::UTC)); return new MEpoch(mv, MEpoch::UTC); } */ // //------------------------------------------------------------------- // const MEpoch *MSTimeParse::yearTimeConvert(Int year, Int month, Int day, Int hour, Int minute, Int second, Int millisec) { if(yeartime) delete yeartime; Double s = Double(second) + Double(millisec)/1000.0; Time t(year, month, day, hour, minute, s); MVEpoch mv(t.modifiedJulianDay()); return new MEpoch(mv, MEpoch::UTC); } // //------------------------------------------------------------------- // const MEpoch *MSTimeParse::yearTimeConvert(const TimeFields& tf) { MSTimeParse::thisMSTParser->validate(tf); if(yeartime) delete yeartime; Double s = Double(tf.sec) + Double(tf.fsec)/1000.0; Time t(tf.year, tf.month, tf.day, tf.hour, tf.minute, s); MVEpoch mv(t.modifiedJulianDay()); // This is the "original" code (from DdeB etc.) // yeartime is a global - don't know what's it's use and where it // is used. It's a pointer, which is returned on the parser stack. // In this method, it's first deleted, and re-assigned. This will // do (and does!) strange things to the YY V-stack. // // return (yeartime = new MEpoch(mv, MEpoch::UTC)); return new MEpoch(mv, MEpoch::UTC); } // //------------------------------------------------------------------- // Double MSTimeParse::toTAIInSec(const MEpoch& whatEver) { // MEpoch tai=MEpoch::Convert(whatEver,MEpoch::Ref(MEpoch::TAI))(); MEpoch tai=whatEver; return Double(MVTime(tai.getValue())*86400); } // //------------------------------------------------------------------- // const TableExprNode* MSTimeParse::node() { return node_p; } // //------------------------------------------------------------------- // void MSTimeParse::setDefaults(TimeFields& tf, Bool dataOrigin) { if (dataOrigin) { MSTimeParse::thisMSTParser->getDefaults(); if (tf.year == -1) tf.year=MSTimeParse::thisMSTParser->year0();//MSTimeParse().year0(); if (tf.month == -1) tf.month=MSTimeParse::thisMSTParser->month0(); if (tf.day == -1) tf.day=MSTimeParse::thisMSTParser->day0(); if (tf.hour == -1) tf.hour=MSTimeParse::thisMSTParser->hour0(); if (tf.minute == -1) tf.minute=MSTimeParse::thisMSTParser->minute0(); if (tf.sec == -1) tf.sec=MSTimeParse::thisMSTParser->second0(); if (tf.fsec == -1) tf.fsec = MSTimeParse::thisMSTParser->fractionalsec0(); } else { // This sets the defaults to MJD reference. This should be got // from one of the time related classes. // MJDref = Wed Nov 17 00:00:00 1858 Time time00(2400000.5); if (tf.year == -1) tf.year=time00.year();//1858; if (tf.month == -1) tf.month=time00.month();//11; if (tf.day == -1) tf.day=time00.dayOfMonth();//17; if (tf.hour == -1) tf.hour=0; if (tf.minute == -1) tf.minute=0; if (tf.sec == -1) tf.sec=0; if (tf.fsec == -1) tf.fsec = 0; } // cout << tf.year << " " << tf.month << " " << tf.day << " " << endl; } // //------------------------------------------------------------------- // void MSTimeParse::validate(const TimeFields& tf) { if (tf.year < 1858) // This is not precise (should be < Wed Nov 17 00:00:00 1858) { ostringstream mesg; mesg << "MSTime Selection error: Year = " << tf.year << " out of range"; // throw(MSSelectionTimeError(mesg.str())); throw(AipsError(mesg.str())); } if ((tf.month <= 0) || (tf.month > 12)) { ostringstream mesg; mesg << "MSTime Selection error: Month = " << tf.month << " out of range"; // throw(MSSelectionTimeError(mesg.str())); throw(AipsError(mesg.str())); } if ((tf.day <= 0) || (tf.day > 31)) { ostringstream mesg; mesg << "MSTime Selection error: Day = " << tf.day << " out of range"; // throw(MSSelectionTimeError(mesg.str())); throw(AipsError(mesg.str())); } } // //------------------------------------------------------------------- // void MSTimeParse::copyDefaults(TimeFields& target, TimeFields& source) { if (target.year == -1) target.year = source.year; if (target.month == -1) target.month = source.month; if (target.day == -1) target.day = source.day; if (target.hour == -1) target.hour = source.hour; if (target.minute == -1) target.minute = source.minute; if (target.sec == -1) target.sec = source.sec; if (target.fsec == -1) target.fsec = source.fsec; } // //------------------------------------------------------------------- // void MSTimeParse::accumulateTimeList(const Double t0, const Double t1, const Double dT) { Int n0=timeList.shape()(1); IPosition newShape(timeList.shape()); newShape(1)++; timeList.resize(newShape,True); timeList(0,n0) = t0;//-4.68193e+09; timeList(1,n0) = t1;//-4.68193e+09; if (dT >= 0) timeList(2,n0) = dT; else timeList(2,n0) = defaultExposure; } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSSel/MSTimeParse.h000066400000000000000000000143271476623553700172370ustar00rootroot00000000000000//# MSTimeParse.h: Classes to hold results from time grammar parser //# Copyright (C) 1994,1995,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSTIMEPARSE_H #define MS_MSTIMEPARSE_H //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // Class to hold values from time grammar parser // // // // // //# Classes you should understand before using this one. // // // MSTimeParse is the class used to parse a time command. // // // MSTimeParse is used by the parser of time sub-expression statements. // The parser is written in Bison and Flex in files MSTimeGram.y and .l. // The statements in there use the routines in this file to act // upon a reduced rule. // Since multiple tables can be given (with a shorthand), the table // names are stored in a list. The variable names can be qualified // by the table name and will be looked up in the appropriate table. // // The class MSTimeParse only contains information about a table // used in the table command. Global variables (like a list and a vector) // are used in MSTimeParse.cc to hold further information. // // Global functions are used to operate on the information. // The main function is the global function msTimeCommand. // It executes the given STaQL command and returns the resulting ms. // This is, in fact, the only function to be used by a user. // // // It is necessary to be able to give a ms command in ASCII. // This can be used in a CLI or in the table browser to get a subset // of a table or to sort a table. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class MSTimeParse : public MSParse { public: // Default constructor MSTimeParse (); // Associate the ms and the shorthand. MSTimeParse (const MeasurementSet* ms,const TableExprNode& otherTens,const Bool honourRowFlags=True); MSTimeParse (const MeasurementSet* ms,const TableExprNode& colAsTEN, MSSelectableMainColumn& msMainColInterface, const TableExprNode& otherTEN, const Bool honourRowFlags=True); ~MSTimeParse() {columnAsTEN_p=TableExprNode();} // ~MSTimeParse() // { // if (node_p) delete node_p;node_p=0x0; // if (otherTens_p) delete otherTens_p;otherTens_p=0x0; // } const TableExprNode *selectTime(const MEpoch& time, bool daytime = false); const TableExprNode *selectTimeGT(const MEpoch& lowboundTime, bool daytime = false); const TableExprNode *selectTimeLT(const MEpoch& upboundTime, bool daytime = false); const TableExprNode *selectTimeRange(const MEpoch& lowboundTime, const MEpoch& upboundTime, bool daytime = false, Float edgeWidth=-1.0); Matrix selectedTimes() {return timeList;} const TableExprNode *addCondition(TableExprNode& condition); /* static const MEpoch *dayTimeConvert(Int day=-1, Int hour = -1, Int minute = -1, Int second = -1, Int millisec = -1); */ static void setDefaults(TimeFields& tf, Bool dataOrigin=True); void getDefaults(); static void copyDefaults(TimeFields& target, TimeFields& source); static const MEpoch *yearTimeConvert(Int year=-1, Int month=-1, Int day=-1, Int hour = -1, Int minute = -1, Int second = -1, Int millisec = -1); static const MEpoch *yearTimeConvert(const TimeFields& tf); // Get table expression node object. static const TableExprNode* node(); Int year0() {return defaultYear;} Int month0() {return defaultMonth;} Int day0() {return defaultDay;} Int hour0() {return defaultHour;} Int minute0() {return defaultMinute;} Int second0() {return defaultSeconds;} Int fractionalsec0() {return defaultFractionalSec;} Double defaultInteg() {return defaultExposure;} static void validate(const TimeFields& tf); static void reset(){timeList.resize(3,0);} static void cleanup() {if (node_p) delete node_p;node_p=0x0;} static TableExprNode* node_p; //private: static TableExprNode *otherTens_p; static Bool defaultTimeComputed; MVTime firstRowTime; static MeasurementSet *ms_p; static Double toTAIInSec(const MEpoch& time); static MEpoch* yeartime; static MEpoch* daytime; Int defaultYear, defaultMonth, defaultDay, defaultHour, defaultMinute, defaultSeconds, defaultFractionalSec; Double defaultExposure; const String colName; Bool honourRowFlags_p; static Matrix timeList; void accumulateTimeList(const Double t0, const Double t1,const Double dT=-1); static MSTimeParse *thisMSTParser; static TableExprNode columnAsTEN_p; static MSSelectableMainColumn *mainColumn_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSUvDistGram.cc000066400000000000000000000144441476623553700175310ustar00rootroot00000000000000//# MSUvDistGram.cc: Grammar for UV distribution expressions //# Copyright (C) 1998,1999,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // MSUvDistGram; grammar for UV distribution command lines // This file includes the output files of bison and flex for // parsing command lines operating on lattices. #include #include #include #include #include // routines used by bison actions #include #include // routines used by bison actions #include #include //# stdlib.h is needed for bison 1.28 and needs to be included here //# (before the flex/bison files). //# Bison defines WHERE, which is also defined in LogOrigin.h (which //# is included in auto-template mode). //# So undefine WHERE first. #undef WHERE #include //# Let clang and gcc ignore some warnings in bison/flex code. //# Undef YY_NULL because flex can redefine it slightly differently (giving a warning). #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpragmas" #pragma GCC diagnostic ignored "-Wdeprecated-register" #pragma GCC diagnostic ignored "-Wsign-compare" #include "MSUvDistGram.ycc" // bison output #ifdef YY_NULL # undef YY_NULL #endif #include "MSUvDistGram.lcc" // flex output #pragma GCC diagnostic pop // Define the yywrap function for flex. int MSUvDistGramwrap() { return 1; } namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Declare a file global pointer to a char* for the input string. static const char* strpMSUvDistGram = 0; static Int posMSUvDistGram = 0; //# Parse the command. //# Do a yyrestart(yyin) first to make the flex scanner reentrant. int msUvDistGramParseCommand (const MeasurementSet* ms, const String& command, Matrix& selectedUVRange, Vector& units) { try { Int ret; MSUvDistGramrestart (MSUvDistGramin); yy_start = 1; strpMSUvDistGram = command.chars(); // get pointer to command string posMSUvDistGram = 0; // initialize string position MSUvDistParse parser(ms); // setup measurement set MSUvDistParse::thisMSUParser = &parser; // The global pointer to the parser parser.reset(); // parse command string ret=MSUvDistGramparse(); selectedUVRange=parser.selectedUV(); units = parser.selectedUnits(); return ret; } catch (MSSelectionUvDistError &x) { String newMesgs; newMesgs = constructMessage(msUvDistGramPosition(),command); x.addMessage(newMesgs); throw; } } int msUvDistGramParseCommand (const MeasurementSet* ms, const String& command) { try { Int ret; MSUvDistGramrestart (MSUvDistGramin); yy_start = 1; strpMSUvDistGram = command.chars(); // get pointer to command string posMSUvDistGram = 0; // initialize string position MSUvDistParse parser(ms); // setup measurement set MSUvDistParse::thisMSUParser = &parser; // The global pointer to the parser ret=MSUvDistGramparse(); // parse command string return ret; } catch (MSSelectionUvDistError &x) { String newMesgs; newMesgs = constructMessage(msUvDistGramPosition(),command); x.addMessage(newMesgs); throw; } } //# Give the table expression node const TableExprNode* msUvDistGramParseNode() { return MSUvDistParse::node(); } void msUvDistGramParseDeleteNode() { return MSUvDistParse::cleanup(); } //# Give the string position. Int& msUvDistGramPosition() { return posMSUvDistGram; } //# Get the next input characters for flex. int msUvDistGramInput (char* buf, int max_size) { int nr=0; while (*strpMSUvDistGram != 0) { if (nr >= max_size) { break; // get max. max_size char. } buf[nr++] = *strpMSUvDistGram++; } return nr; } void MSUvDistGramerror (const char*) { throw (MSSelectionUvDistParseError("UV Distribution Expression: Parse error at or near '"+ String(MSUvDistGramtext) + "'")); } // String msUvDistGramRemoveEscapes (const String& in) // { // String out; // int leng = in.length(); // for (int i=0; i #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MeasurementSet; class TableExprNode; // // Global functions for flex/bison scanner/parser for MSUvDistGram // // // // // //# Classes you should understand before using this one. //
      • MSUvDistGram.l and .y (flex and bison grammar) // // // Global functions are needed to define the input of the flex scanner // and to start the bison parser. // The input is taken from a string. // // // It is necessary to be able to give an image expression in ASCII. // This can be used in glish. // // //# A List of bugs, limitations, extensions or planned refinements. // // // Declare the bison parser (is implemented by bison command). int msUvDistGramParseCommand (const MeasurementSet *ms, const String& command); int msUvDistGramParseCommand (const MeasurementSet* ms, const String& command, Matrix& selectedUV, Vector& units); // The yyerror function for the parser. // It throws an exception with the current token. void MSUvDistGramerror (const char*); // Give the table expression node. const TableExprNode *msUvDistGramParseNode(); void msUvDistGramParseDeleteNode(); // Give the current position in the string. // This can be used when parse errors occur. Int& msUvDistGramPosition(); // Declare the input routine for flex/bison. int msUvDistGramInput (char* buf, int max_size); // A function to remove escaped characters. //String msUvDistGramRemoveEscapes (const String& in); // A function to remove quotes from a quoted string. //String msUvDistGramRemoveQuotes (const String& in); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSUvDistGram.ll000066400000000000000000000055071476623553700175530ustar00rootroot00000000000000/* -*- C -*- MSUvDistGram.l: Lexical analyzer for ms selection commands Copyright (C) 1994,1995,1996,1997,1998,2001,2003 Associated Universities, Inc. Washington DC, USA. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. Correspondence concerning AIPS++ should be addressed as follows: Internet email: casa-feedback@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA */ /* yy_unput is not used, so let flex not generate it, otherwise picky compilers will issue warnings. */ %option nounput %{ #undef YY_INPUT #define YY_INPUT(buf,result,max_size) result=msUvDistGramInput(buf,max_size) #undef YY_DECL #define YY_DECL int MSUvDistGramlex (YYSTYPE* lvalp) %} WHITE [ \t\n]* DIGIT [0-9] INT {DIGIT}+ EXP [DdEe][+-]?{INT} NUMBER {INT}|{INT}"."{DIGIT}* FNUMBER {NUMBER}?{EXP}? KILO ([Kk]?) MEGA (M?) METER (m?) LAMBDA ((("lambda")|("LAMBDA"))?) WAVELENGTHUNIT (({KILO}|{MEGA})?{LAMBDA}) DISTANCEUNIT (({KILO})?{METER}) UNIT ({DISTANCEUNIT}|{WAVELENGTHUNIT}) /* rules */ %% {FNUMBER} { msUvDistGramPosition() += yyleng; lvalp->dval = atof(MSUvDistGramtext); return FNUMBER; } /* Literals */ {UNIT} { msUvDistGramPosition() += yyleng; lvalp->str = MSUvDistGramtext; return UNIT; } "~" { msUvDistGramPosition() += yyleng; return DASH; } ":" { msUvDistGramPosition() += yyleng; return COLON; } "," { msUvDistGramPosition() += yyleng; return COMMA; } ">" { msUvDistGramPosition() += yyleng; return GT; } "<" { msUvDistGramPosition() += yyleng; return LT; } "%" { msUvDistGramPosition() += yyleng; return PERCENT; } {WHITE} { msUvDistGramPosition() += yyleng;} /* Eat white spaces */ . { msUvDistGramPosition() += yyleng;return MSUvDistGramtext[0];} %% casacore-3.7.1/ms/MSSel/MSUvDistGram.yy000066400000000000000000000073401476623553700176020ustar00rootroot00000000000000/* MSUvDistGram.y: Parser for UV distribution expressions Copyright (C) 2004 Associated Universities, Inc. Washington DC, USA. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. Correspondence concerning AIPS++ should be addressed as follows: Internet email: casa-feedback@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA */ %{ using namespace casacore; %} %pure-parser /* make parser re-entrant */ %union { const TableExprNode* node; char * str; Double dval, uvrange[2]; } %token UNIT %token FNUMBER %token COLON %token COMMA %token PERCENT %type fnumwithunits %type uvwdiststatement %type uvwdistexprlist //%type uvwdistexpr %type uvwdistexpr %left UNIT %right PERCENT %nonassoc EQ EQASS GT GE LT LE NE DASH COLON %{ #include int MSUvDistGramlex (YYSTYPE*); String MSUvDistGramlexGlobalUnits="m"; // Its a global - make the name crazy to minimize // namespace conflicts. #define EPS 1E-10 %} %% uvwdiststatement:uvwdistexprlist { $$ = $1; } ; uvwdistexprlist: uvwdistexpr { //$$ = $1; $$ = MSUvDistParse::thisMSUParser->selectUVRange($1[0],$1[1],MSUvDistGramlexGlobalUnits); } | uvwdistexprlist COMMA uvwdistexpr { //$$ = $3; $$ = MSUvDistParse::thisMSUParser->selectUVRange($3[0],$3[1],MSUvDistGramlexGlobalUnits); } ; fnumwithunits: FNUMBER { MSUvDistGramlexGlobalUnits = "m"; $$ = $1; // The default units are meters ("m") } | FNUMBER UNIT { MSUvDistGramlexGlobalUnits = String($2); $$ = $1; } ; uvwdistexpr: fnumwithunits { //$$ = MSUvDistParse::thisMSUParser->selectUVRange($1, $1, MSUvDistGramlexGlobalUnits); $$[0] = $1; } | FNUMBER DASH fnumwithunits { //$$ = MSUvDistParse::thisMSUParser->selectUVRange($1, $3, MSUvDistGramlexGlobalUnits); $$[0] = $1; $$[1] = $3; } | LT fnumwithunits { //$$ = MSUvDistParse::thisMSUParser->selectUVRange(0, $2, MSUvDistGramlexGlobalUnits); $$[0]=0.0; $$[1]=$2; } | GT fnumwithunits { // $$ = MSUvDistParse::thisMSUParser->selectUVRange($2+EPS, std::numeric_limits::max(), // MSUvDistGramlexGlobalUnits); $$[0]=$2+EPS; $$[1]=std::numeric_limits::max(); } | uvwdistexpr COLON FNUMBER PERCENT { // $$ = MSUvDistParse::thisMSUParser->selectUVRange($1*(1-$3*0.01), $1*(1+$3*0.01), // MSUvDistGramlexGlobalUnits); $$[0]=$1[0]*(1-$3*0.01); $$[1]=$1[1]*(1+$3*0.01); } ; %% casacore-3.7.1/ms/MSSel/MSUvDistParse.cc000066400000000000000000000176251476623553700177210ustar00rootroot00000000000000//# MSUvDistParse.cc: Classes to hold results from UV dist grammar parser //# Copyright (C) 1994,1995,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSUvDistParse* MSUvDistParse::thisMSUParser = 0x0; // Global pointer to the parser object TableExprNode* MSUvDistParse::node_p = 0x0; Matrix MSUvDistParse::selectedUV_p(2,0); Vector MSUvDistParse::meterUnits_p(0,False); //# Constructor MSUvDistParse::MSUvDistParse () : MSParse() { } //# Constructor with given ms name. MSUvDistParse::MSUvDistParse (const MeasurementSet* ms) : MSParse(ms, "UvDist") { if(node_p) delete node_p; node_p = new TableExprNode(); } const TableExprNode *MSUvDistParse::selectUVRange(const Double& startUV, const Double& endUV, const String& unit, Bool doSlow) { Bool wavelengthUnit=False, distanceUnit=False; Double startPoint; Double endPoint; // Column accessors MSMainColumns msMainCol(*ms()); MSSpWindowColumns msSpwCol(ms()->spectralWindow()); MSDataDescColumns msDataDescSubTable(ms()->dataDescription()); String units(unit); units.downcase(); if(units == "km") // Kilo meter { startPoint = startUV * 1000; endPoint = endUV * 1000; distanceUnit = True; } else if((units == "m")) // Meter { startPoint = startUV; endPoint = endUV; distanceUnit = True; } else if(units == "mlambda") // Mega Lambda { startPoint = startUV * 1000000; endPoint = endUV * 1000000; wavelengthUnit = True; } else if(units == "klambda") // Kilo lambda { startPoint = startUV * 1000; endPoint = endUV * 1000; wavelengthUnit = True; } else if(units == "lambda") // Lambda { startPoint = startUV; endPoint = endUV; wavelengthUnit = True; } else { String Mesg="Unrecognized units " + units + " found. Possible (case insensitive) units are [KM]LAMBDA for wavelengths or [K]M for distance."; throw(MSSelectionUvDistParseError(Mesg)); } accumulateUVList(startPoint, endPoint, wavelengthUnit, distanceUnit); TableExprNode condition; if (!doSlow) { // // This version of TEN based query is about 60X faster than the // slower code below // Int nDDIDRows; // TableExprNode uvwDist = sqrt(sumSquare(ms()->col(MS::columnName(MS::UVW)))); // // This computes only the 2D uv-distance (projection of the // baseline on the uv-plane). It's certainly more expensive to // compute in TaQL and probably scientifically incorrect too. // // String colName = MS::columnName(MS::UVW); // TableExprNode uvw = ms()->col(colName); // TableExprNodeSet su = TableExprNodeSet(IPosition(1,0)); // TableExprNodeSet sv = TableExprNodeSet(IPosition(1,1)); // TableExprNode uvwDist = (uvw(su)*uvw(su) + uvw(sv)*uvw(sv)); // // Turns out that the above style of building the TEN is about // 2x slower than the one below. It is also simpler to build a // const TEN via the parser below. // TableExprNode uvwDist = RecordGram::parse(*ms_p,"SQUARE(UVW[1]) + SQUARE(UVW[2])"); // // Build a map from DDID (which is a MainTable column) to SpwID // (which then indexes into the SpectralWindow sub-table from // where the ref. freq. info. is picked up. // Vector mapDDID2SpwID; nDDIDRows = msDataDescSubTable.nrow(); mapDDID2SpwID.resize(nDDIDRows); for(Int i=0;icol(DATA_DESC_ID)==i) && ((uvwDist >= scaledStartPoint) && (uvwDist <= scaledEndPoint))); if (condition.isNull()) condition = pickUVWDist; else condition = condition || pickUVWDist; } } else { startPoint *= startPoint; endPoint *= endPoint; if (condition.isNull()) condition = ((uvwDist >= startPoint) && (uvwDist <= endPoint)); else condition = condition || ((uvwDist >= startPoint) && (uvwDist <= endPoint)); } } else { // // The earlier, (much) less efficient code. Its here for // testing - should ultimately be removed. // // Loop over all rows in the MS // Vector rowsel; Int nRowSel = 0; if(wavelengthUnit) { for (uInt row=0; rownrow(); row++) { Int ddid = msMainCol.dataDescId()(row); Int spwid = msDataDescSubTable.spectralWindowId()(ddid); Double refFreq = msSpwCol.refFrequency()(spwid); Vector uvw = msMainCol.uvw()(row); Double uvDist = sqrt(uvw(0)*uvw(0) + uvw(1)*uvw(1) + uvw(2)*uvw(2))*refFreq/C::c; if ((startPoint <= uvDist) && (uvDist <= endPoint)) { nRowSel++; rowsel.resize(nRowSel, True); rowsel(nRowSel-1) = row; } } if(nRowSel == 0) rowsel.resize(nRowSel, True); } if(distanceUnit) { for (uInt row=0; rownrow(); row++) { Vector uvw = msMainCol.uvw()(row); Double uvDist = sqrt(uvw(0)*uvw(0) + uvw(1)*uvw(1) + uvw(2)*uvw(2)); if ((startPoint <= uvDist) && (uvDist <= endPoint)) { nRowSel++; rowsel.resize(nRowSel, True); rowsel(nRowSel-1) = row; } } if(nRowSel == 0) rowsel.resize(nRowSel, True); } condition = (ms()->nodeRownr().in(rowsel)); } if(node_p->isNull()) *node_p = condition; else *node_p = *node_p || condition; return node_p; } const TableExprNode* MSUvDistParse::node() { return node_p; } void MSUvDistParse::accumulateUVList(const Double r0, const Double r1, const Bool wavelengthUnit, const Bool) { Int n0=selectedUV_p.shape()(1); IPosition newShape(selectedUV_p.shape()); newShape(1)++; selectedUV_p.resize(newShape,True); meterUnits_p.resize(newShape(1),True); selectedUV_p(0,n0) = r0; selectedUV_p(1,n0) = r1; meterUnits_p(n0)=True; if (wavelengthUnit) meterUnits_p(n0)=False; } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSSel/MSUvDistParse.h000066400000000000000000000102651476623553700175540ustar00rootroot00000000000000//# MSUvDistParse.h: Classes to hold results from UV dist grammar parser //# Copyright (C) 1994,1995,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSUVDISTPARSE_H #define MS_MSUVDISTPARSE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // Class to hold values from UV dist grammar parser // // // // // //# Classes you should understand before using this one. // // // MSUvDistParse is the class used to parse a UV dist command. // // // MSUvDistParse is used by the parser of UV dist sub-expression statements. // The parser is written in Bison and Flex in files MSUvDistGram.y and .l. // The statements in there use the routines in this file to act // upon a reduced rule. // Since multiple tables can be given (with a shorthand), the table // names are stored in a list. The variable names can be qualified // by the table name and will be looked up in the appropriate table. // // The class MSUvDistParse only contains information about a table // used in the table command. Global variables (like a list and a vector) // are used in MSUvDistParse.cc to hold further information. // // Global functions are used to operate on the information. // The main function is the global function msUvDistCommand. // It executes the given STaQL command and returns the resulting ms. // This is, in fact, the only function to be used by a user. // // // It is necessary to be able to give a ms command in ASCII. // This can be used in a CLI or in the table browser to get a subset // of a table or to sort a table. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class MSUvDistParse : public MSParse { public: // Default constructor MSUvDistParse (); // Associate the ms and the shorthand. MSUvDistParse (const MeasurementSet* ms); // ~MSUvDistParse() {if (node_p) delete node_p;node_p=0x0;} const TableExprNode *selectUVRange(const Double& startUV, const Double& endUV, const String& unit, Bool doSlow=False); Vector selectedUnits() {return meterUnits_p;} Matrix selectedUV() {return selectedUV_p;} static void reset(){selectedUV_p.resize(2,0);meterUnits_p.resize(0);} static void cleanup() {if (node_p) delete node_p;node_p=0x0;} // Get table expression node object. static const TableExprNode* node(); static MSUvDistParse* thisMSUParser; private: static TableExprNode* node_p; static Matrix selectedUV_p; static Vector meterUnits_p; void accumulateUVList(const Double r0, const Double r1, const Bool wavelengthUnits, const Bool meterUnits); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/MSWeatherIndex.cc000066400000000000000000000042711476623553700200700ustar00rootroot00000000000000//# MSWeatherIndex.cc: this defined MSWeatherIndex //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSWeatherIndex::MSWeatherIndex() : MSTableIndex() {;} MSWeatherIndex::MSWeatherIndex(const MSWeather &weather) : MSTableIndex(weather, stringToVector("ANTENNA_ID")) { attachIds();} MSWeatherIndex::MSWeatherIndex(const MSWeatherIndex &other) : MSTableIndex(other) { attachIds();} MSWeatherIndex::~MSWeatherIndex() {;} MSWeatherIndex &MSWeatherIndex::operator=(const MSWeatherIndex &other) { if (this != &other) { MSTableIndex::operator=(other); attachIds(); } return *this; } void MSWeatherIndex::attach(const MSWeather &weather) { MSTableIndex::attach(weather, stringToVector("ANTENNA_ID")); attachIds(); } void MSWeatherIndex::attachIds() { antennaId_p.attachToRecord(accessKey(), "ANTENNA_ID"); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MSSel/MSWeatherIndex.h000066400000000000000000000051041476623553700177260ustar00rootroot00000000000000//# MSWeatherIndex: index into a MeasurementSet WEATHER subtable //# Copyright (C) 2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSWEATHERINDEX_H #define MS_MSWEATHERINDEX_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# forward declarations class MSWeather; // // // // // // //
      • MeasurementSet //
      • MSTableIndex // // // // // // // // // // // // // // // //
      • //
      • // // class MSWeatherIndex : public MSTableIndex { public: // no index attached, use the attach function or assignment operator to change that MSWeatherIndex(); // construct one using the indicated WEATHER table MSWeatherIndex(const MSWeather &weather); // construct one from another MSWeatherIndex(const MSWeatherIndex &other); virtual ~MSWeatherIndex(); MSWeatherIndex &operator=(const MSWeatherIndex &other); void attach(const MSWeather &weather); // access to the antenna ID key, throws an exception if isNull() is False Int &antennaId() {return *antennaId_p;} private: RecordFieldPtr antennaId_p; void attachIds(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MSSel/test/000077500000000000000000000000001476623553700157055ustar00rootroot00000000000000casacore-3.7.1/ms/MSSel/test/CMakeLists.txt000066400000000000000000000011301476623553700204400ustar00rootroot00000000000000set (tests tMSAntennaGram tMSAntennaGram2 tMSAntennaGram3 tMSCorrGram tMSFeedGram tMSFieldGram tMSScanGram tMSSpwGram tMSTimeGram tMSUvDistGram tMSSelection ) # Only test scripts, no test programs. set (testscripts tMSSelection2 ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_ms) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) foreach (test ${testscripts}) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) endforeach (test) casacore-3.7.1/ms/MSSel/test/mssel_test_small.ms.tgz000066400000000000000000022445041476623553700224360ustar00rootroot00000000000000‹‘ßVìÝ |g}îñYÝ%çâ'$Àا4NbäÝÕî* ‘-9Qˆ,£Ur(ˆÑîHZ²7fFvœ6‰I z(…ž–¤¥.¥´¥\Bs)…(§”JKÒ@€p @J(´§ ÐRÜ÷™Õü%¯.#˲B~ß_¿ÏÎý¾ï®vw*®k—'<Ûõ&ÜŠU.÷VÜÆK*ýÙ¬ªû²Ù\ʯûR)¿{ÈH¥sÉd6›Êå2F2•Mö§ 3»Ö Ò̬ëYŽiîäŒåU­éņ«VÔ€SSë±Hë©Òdÿ{ÖdÙîJõ¯Ñ<ôÎe2‹íÿTŸšÓÜþOö«ýŸËôç 3¹Fó_ÒS|ÿß§FËý*ž>^*ÛÅüŒU·óÞˆUUêßwTÝô »¶èÏ0Œ¶mÁ F·.þ ƒãá”[ý’I¦1÷ ÅØ<7ˆ~¬wtǘ]¨9ŰËyz¦A—AÛ-4föõ«g:¼¿æ–¼R­*ú4ïqT“ˆ5ËD¸¬Ïi6eÝ£M•+Ôg¬d ¹'fQ+Y•bÒ‹œFðîRE_–Ûw—k…žºã *w¨òaU¾­:Ÿ©Š:ZîVãn]l¤D8ûÅêE礋–·¥esPËR×ÿÌÍcÉë:™š÷üŸÌù×5×ÿu ¯ÿ{U}ª*yϪ-'¸Î·ú½'bã¡/¢KWÛÁã[ qQÆ“ÌÑ>ôÕT_aóù‘ájѾqn¯šÁÞ®Š¾ÂæK•zÙuжcG¬ºÜûºÿYª´«â_¹=›v ú»ÿ7Ç~÷ßÿ–¾ï»ÿúÇwæ}Í_—­m‹ôàw߯o KkXÚÂÒ–ްt†¥+,Ý¢ô¨²‰i3m¦Í´™6ÓfÚL›i3m¦Í´™6ÓfÚL{‘i°U\×.Ox¶ëM¸«\î­¸;=k²l÷N¥Ók4¤’ËdTÝ—ÍæR~Ý—JùÝ•T_ªßH¥sÉd6›Ê%ûd*—éOfr濤Y׳Ó4ÜÉË«ZÓ‹ W­¨§¦Öc‘ÖÓ}Ša´Ü¯âé㥲]ÌÏXu;ïXUÿËcâ;ªî z…]õ=³ £m[0ˆ¯Ûdoyn w…lÜ+2¡ÇŠnÙ27ˆ~¬wtǘ]¨9ŰËyz¦A—AÛ-4f*ægœ«g:¼¿æ–¼R­*ú4ïqT“ˆ5ËD¸¬Ïi6åÆ/¯P#œ±‚6˵81‹’XÉ¢$üÿ£I/:p"áˆHß¾3«Jûîr­pCØSwTåU>¬Ê·Uç3UP¿[»u±‘7]¬^tNº8ay{PZ6±,qýOe×hK^ÿÓÉ”ú_\ÿsþõ?Çõ]èëÿ^UŸªJÞ³ªEË ®ó­~ï#Ñ]~5}m\ºÚ®ßbˆ‹2ždކôq ¯¦ú ›Ï W‹ös{Õ öðv#xöÊ—*õ²=êmÇ.ŽXu¹÷uÿ³ŒàþÏþ•»Ñ³iGР¿û“qìwÿýoé›ñ¾û¯|gÞ×üuÙÚ¶H~­[•MkP÷„õ)¢fÚL›i3m¦Í´™6ÓfÚL›i3m¦Í´™6Ó–Ó`#«¸®]žðl×›p+V¹Ü[qwzÖdÙîJgÖhI%—ɨº/›Í¥üº/•ò»ký}I#•Î%“Ùl*—ì7’©\¦?m˜É5šÿ’f]ÏrLÓp'g,¯jM/6\µ¢œšZEZO÷)†‘xPÅÓÇKe»˜Ÿ±êvÞ±ªªSBýVuOÐ+ìÚ¢G<Ç0Ú¶ƒÑ ¥éŠNºÅ/‰p˜„aÎuÕõÎí³ 5§v9OO%è2h»…ƌ¾¾sUéÞ_sK^©V}š÷8ªˆIÄše"|°µÙ”u¶p,:@"œXcW4 ^‘öÝåZᆹíf´m]¤ÇÒc¬Èâç8Å5°ÜùŸË¤œÿýIuIàüÇz¹+¸¿¡J›{¸ª/‰ðWWº¹«Ö¬±àdkô<‘åDZâù_¡kcÉó?•L¥ÒiqþçüçÿÏÿëB÷YUoVe¸ZpìŠ]õ¬²ÿTßîq$zªiÏ«xrÛb»¶U½¿uS®-ì÷zUUew¸ïu÷.ƒûu~öܧìSu—*Ãù‘ájѾ1¡².ç«Ò®Êîr­pƒîÐŽ´­Í0Î]Ø3q–Ç«âºvy³]o­XåroÅݹgàšÁ¡Ã{†v®Í<’J6«ê¾l6—òë¾TÊï2Ré\2™Í¦rIÕ=•ëëë7ÌìÚÌ~i³®g9¦i¸“3–Wµ¦®ZQNM­Ç"­§¥÷¿gM–íÞ©äñÍCïà\&³ØþÏfŽÙÿý¹lÆ0s¶+óßÿúú¿WÕ§ª’÷¬jÑrŠyoζú½»ƒëzãÂTi\ç»ÂÇ ®ýO^GCú8pŒ`¯æE;@3UÙ¤ÊvU6ëþ¥J½l:EÛ±‹#V]î}Ýÿ,cAÓÀX¬#ÖÛJ^ÿéÛñÌc™×Él2;ÿõ_:Ù§^òúëe—~ý£êKUisW Fô¢V½ôI´„ýw=w‡'Dø®g¢¥é$ŸVrþ-ï¸æ±Üû?¹LzáùŸJ÷sþ¯‡à¸ßô6ÛÇõÞ6¢w{ôñݳ¿l•ªaŸî[U—nÿÁ íÄ Ú¥U³T©ØÅ’åÙ¦çXU·à”ê^©V5kS¦7c›ùÁ‘ \sU´– j(ÿS#ë÷z7ùS³ 5§Îþ<½A—sL¬v¤Ma}n¾`•-gO­<[©êáž7\õL­±êûƇöí˜T¶ T=»ZµÔâ—ŠvÕ+M•lý†É©óÞ5kÖ¡}Á&_ÕR'D^vÉ;÷ ‹}Ê^Û.êeÖoèlÐÅ}Z~ÿОñ±k&®Þ78z]°äÏÈ×í‚:ŠÊæ!µðµCb³÷n”)ÖfÕäæV¤m|xdHÕ甊õZI­¥>îK۬ؖ;ëØµ+[ø®µ\ø¯@×°:äÇ \£ò9j/ÙÎAµýÕJlÔå_p$²ïÚ‘ õô5qÍè€>†ž¹o¶2i;z Ô˜¥IÇò¯FåšUtOÒAt΀ãX‡Å*ä=§Tž[…Ó‹?±o`d(¯º<}Ï‚E7«VÅ^áòë«ÿ&¯4^º‡÷×Ü’žÔ‚í»ú[ùžÚ3´|tL=ÞíÇ.Øu¯æl˜=2¥6²-z÷¾Ñáü>¬Ôƒ]C¯œ-©“B¦gWê¶Ú/êÄpÏpÕZɵM·6ëTÇíãj,sªæ˜C®¸pe+ع>»l¹ÕîÔâÐ^ýÇ¡¬<í©©R¡dWUQ+X«Ú¦Ús;Ñ Ò ¾² .|›Ç‡Fö Œ_;6Ô¸xtïŸ9ì–ÔqãŠw×DMù§ Ví˜e0±æ7]¢hÈÎÍŸ‚åç6ÙM>]Èî /ò߱׷¹m37ª¸NÈQçˆóÆÇê¼îÍvëÜ¿¾æ{§*­ùüH8Î1uÀXðšoS8#ážPu‹*­ª´éaUÝa‡¶P,: `%¯ÿJÕ©ÚñÌc¹÷’éÌÂ×é~^ÿ­‹ñÃuÛÜeöäg'±çd/ÖÏÊ>ÿS:®y,þ'žÿÉTŽó½l>Ù €“¦ÙùŸ½vlÍ>üiÄûüg¶?«?ÿ™ìOóùÏõ°Äþ_›Ë]ÿ3ýó?ÿ™ÓŸÿ̤øûߺXæóŸ›‚—1Í>ÿùêöàeÎ--Ǿ'ƒ' ùùÏ›Œc?ÿ©‹©Š~Q°ÒÏ6ýjHûb=tYjî/Oõ‰™{K8÷Û™û?…ur™¹'Âþ?ê ¦o)ZOê6h;©sçÚ§6®½\{¹öëïƒ[î:c›ññÿ˜ØþÜ+~ãñ?[îñ7v÷ç·}üò¯>mËK[þñÉ^?WµVÕ¹ôÛÇ“±:¸ó¬¡Ÿvÿ``¥õÉ^ÞµÖÖ=aîy‹ÈÏy»Èé0ãÉO¾*\ïù¶4Éëá _ÿäûιîßVZ¯ã¢@3Ï3Œž{ôï@\Ê¥úŸÛߟêož2é¾çö_’¾´Y:Ù+`iËþþãñÞü͈{ÿ·œÿû¿i~ÿëg±û¿é¯¸Ÿf?ͤ¿–ÜìþoÇÜëæÉdÙóÿxoþf,wþ§Ò©Lÿ‚ßÿíÏåøý×u÷[ôñ>ïþoc{ÞýßÎø ±Äýß´õ»ÛiÆÊîìÒ=8<6´g|xtŸzpî`ɱ þml¶Û½Ó½æØÀsphÏ oV£OôöpºçMnñ"~½ ±ÈZÝÜt­ü[Ÿ¼hÖªz³•k«%ÏÕk¶è\»Ìh]#Cùá}{Gàâ´øFkl¸>½fþF o=õ|#ø¼„X–VÇ*FÕ+š¯Ê¥‹®J›w¸®¤ÆNZÇì)ñÐß/ÅÆ®Ðõê´þð7ì9uÿØèþ¡±‰‘Ñpמºß©é{Ujá4×vww6ߣM·Abµ»S?î]jÍýrŸÚ3;]¹Å–½×{®Þ=6 7ØÄ•c£×îW/ŽîÃ5íÔfëúVdú~UòviõY§^sí s×½wLkÛ3:¨ïLt¹¾a`É*›…˱ ží”\¯TðoÍÜk‡éŸô»Õ×-×[ÉÚÊnä¸ð¶='þ†‚òK»äýøü› êýth¦T˜1½™’kº¶¿ÁºZùŠ­uMÕÑ*fÕÚ+>!¤ÿ¹nÇüE+9æsÇ{¬ÃCßgÊÐGºÚ,ѱ`Z®9]:hWÍâ¬?|mÒÕ›W/¤»Q‡gp·¾9Ö5ÃûüÛf¹nãN™åRÕ^Ù¯ÿ]>»ƒfepg±î|°JÅ º´Íoˆ¶uxÐ?ë‚ómÁýIÕù7[ß(«³ÈIŸÌIã\DVuëÏÄÏ7_»“ÞZù5§Ø| Ž«ýÓn×k…ÝïÚñ=bw6ú/×êÚ?šÛ>4¶KتÕûÔ­©Ât»ž]Q×¶W¨cueW†•ÞÇ0‘Ù û5³Ô~mmäʼÿfNÀîªGkÕ6<>6ww¦»wÁ³Vºöì›ÛÁ[®Qvs\ßR;ØÏú°+Ú›ëtÿ×åŽÖÓÆ†òã{dž^tíо=׫.gú«äØ®gN9ö+gíjáðÚŸ;6èñyñRÇçÜËŽ«ô/‚—NÀaÙ-7wÛ5ù±ÆÙ‘ùëó†t{ôYyÿÚR*˜ír­Pò›–§vè”í¨‰Çhp®d_¦7è¾Ü±’}ÙZñ_©UOÀÎjÓW£ ±H—u²Ûb7œm´ñe·yåy=dCTöXåMpÅñÜ!ëß„wÞ%|^¯c/‡ózG'Ù\çûÂ7¨?b,}³ZÝcÞÔ§‰¥×券9­ÊÝzƒYä7\›ØŠ4vv«Á/o\˾ÿ¼7ÿ5âÝÿ×ÿ?­†Hóþÿzàþ¿Om+¸ÿãñÝü׈wÿßðï*qþ¯îÿûÔÕìüOütjæ±ÜùŸÏÿ¹d¿¾ÿo&×Ïù¿‚— } Ø<^*ÛÅà¥ãå„nÄo5š¼” [öø‡ª~ÀÐ/vôÈÑü—Ég©vÿ¶ppýB¿ËäÚ×…³öÿh½“}úÑÑËD½ï;VúNTc–ZRQ–]•§œ%ÎÿÔZ}¡gùÏÿåŽ9ÿû9ÿ×ExþïVñôàž±ê¶<ýõ-ÌšœÝgг[¿™\=®›öòþŸÅf+á o†­îÌn,æ¢o6ï!ÞÓ{N³ ÕfG³P—«ùovÄëø$µÔózbeŸª»TÎ W‹ö •u9_•vUv—k…t‡–p¤mm†q“°ô8^׵˞íznÅ*—{+îNÏš,Û½S©¾5šGRÉe2ªîËfs)¿îK¥üîÉT2•J§T:—Lf³©\2g$S¹L.c˜É5šÿ’f]ÏrLÓp'g,¯jM/6\µ¢œšZEZOúüϪz³*ÃÕ‚cWìªg•óÞˆUm÷‡8œÚòô>ª¬ÿ¢âðÔ~}Ó÷~òkçŽýëÀ7ßýŒ7é:û²Ëüz±î›_|د'7ßÙôñó{¾è×oüäãM¿ÿÊoü¦®Õìߢë±{Ÿå׿[ßå×Olÿ¿¾ò7ùõ]wÝå×½è^¿ÞÕóÅ·„ÓóëoÜ´åN]÷õõùõk{‘_ñ]u¿¾ðÞè׿¸åýwn„ù÷†Ûþéªè<¤Ê/¨âªòzUޡʽª|^•GUùOU¶¨ýt±*»U¹^•º*¯UåmªüoU>«Ê7UùwUÎPÏÓÛUy*T©¨òjU~[•©òU¾®ÊU9­Õ0ž£ÊóTÉ«ò U^¥Êª|@•¿PåUþM•MmÁrë¶‚¾^œ£Š©ÊvUôµúU®På*Uö«òbU^®ÊŒ*uUn4ü«‰ñ:U~M•;Uy»*¿¯ÊÃuþ¸*©Êߨò%U¾®ÊwTù*?Ò3Wë×¥ÊfUÎQÅTe»*IU.Qå U®Re¿*/VååªÌÐ4°÷)ûŒàžîÃù‘ájѾQß.]—óUiWew¹V¸Awh GÚÖfç.ìÉMÖŸŒ*®k—'<Ûõ&ÜŠU.÷VÜW çÇGǮ߹VóH*ýÙ¬ªû²Ù\ʯûR)¿{ÈH¥sÉd6›ÊögŒd*—îKfv­`)³®g9¦i¸“3–Wµ¦®ZQNM­Ç"­§¥ö¿gM–íÞ©äñÎCïà\&³ÈþÏfåþÏ¥’jÿ÷gûú ó¸g¼Oñý¯¯ÿ{U}ª*yϪ-§˜÷F¬j«ß{SðÌиðU×ùW««‡ªÓàÚÿäu4¤ƒ7ÁÞ΋v€z¦7LUºUÙ®ÊfÝ¿T©—íQ§h;vqĪ˽¯û?ÇXÐ4Ðy¶*/PeB•›¨ÑÈÐ_ç‰^u8éÖõÚ{ËbqíðÔÒ֛üKäwŠü/"ïKDùWDþ”ȉ–(?_ä›Eþ‘Ÿ¹¯5ÊU‘ïù1‘··E¹$ò{Eþ®ÈµGÙù"Mäs:¢<.ò›Dþk‘7uFùJ‘oùs"oÛvD䉜Ûöe"ÿ¶ÈŠ|†ØžûD~ƒÈŸ¹ClÏ="ß"ò'Dþ‘/Ûö°È*òOD¾LlÛY‘ïùŸEÞ.¶mQäwˆü5‘Ÿ!¶íu"ÿºÈ„¹â¾òIÉ6Þ™a~©*—‡ù“FÐTógul÷…¹šˆ†¯Ê§‡ù[¢ûÖ–(¿Lå³Âü;*?-Ì‹aÎnr^å3Âü«­Á[ý:ÿ­8æ7µEÃ_¥þ{z˜ïPÿæûE÷SÔñ¼%ÌW·GÓM{´<Ÿh¦ùSqü_Ò ãuDÃÜ-ò?‹|qg4ü´Èïê ¶µÎßë –çd;z’ùÏ7ªôÁv¹Säy0åW‰ü‘(r¦%Ê®Èù»"_Øå¢Èïù"?«-Ê"¿Cä¿ÙlòKD~‹ÈŠ|ZG”¯ùµ"JäDg”w‰|Xä?9Ñ忹j ïÛ;j„ÝÈëŸï·¿úÈùcÿ:ÐýG_óë ݯåïýúO‡¿å×[?øhøø;~ýº³óëŸ>ø]¿~ûyß÷ëËîù_Û—ÿ¿þêÿͯw¿ü‡áp?öëÎWý‡_åÇ? ûõë{Ÿ•øš®Ý™V¿~NG»_ÿí¶N¿¾á£]~ýŠöøuÛSÂú4¿ÞôÅÓýúmß?ïûï8Ó¯?ý³üúÌÛÏöëí©¿øã{jj<µ N銮µŽÈŠüôî(_+ò[D~Hä§õˆ6Èoùþ0ë·"žaDù€Èoù³"w&¢¼[ä›E¾W䊜k‰²+ò‹ü/"ïlrIä÷ˆüm‘/h‹ò¤Èoù¡¶h;¥;ÊW‹ü?D¾_äΞ(ïù6‘?f½ô>jäŠüË"B䟈ܟˆ²#òûDþ®È·DyFäßùQ‘®5Ê/ù­"Yäg¶Eù:‘ß$òý"··ÓþÝùó?ü´ß¼ùŸñëo¾;¨¯>å¯üúƒNP?ýá ¾å÷ûõ·Þ}8ÜgÃá>Ô•øœ_?0ò×~=voP¿ý¼Ïûõ³_óùp¸ ~ë§þ&œÞüúnç áô¾.ßá|‡{0œoPŸýðƒáp÷wáp…Ó{(œÞCáp_ôkÿ}8lŸ>¶OÛ±_‡ ê{Ÿõ•p¸¯„Ã}%î«áp_ §÷ˆ_w¾ê‘p¸ ¾ìïŠWÖÿuà‰íýúï ê7/¨3}‡üúc^PO|$¨ ãF¿~ý@P÷õ‡ ê—}äp8ÜM~}Ç@P_üÚ þÜgƒúÀi¿§õ<¦{}=0‚‚É/ôÌ{ìYî U«bï*Uê5G=*V¢´ÜÈmÇ-Õª—™™Þ¾Þ¤é9³ÕLÇ>Økö¥’©~s|Ö6ÓÉTfgòRõÏL¦.ëË^Ö×g^;¾gå3ÑØ¡]æ¶ÙRqbbb ™LO¼ø’‚ÕŸ,L¼8»-Æ2—\s¹ÉõVÜ^«^/ž*[ÓnŒ‰»¥êtÙ.–Ü=ñ½VÙµc¬hÕ³«U+\²äÊG,Ôg¢R+ÚÁ*YårœEv<¹=âì•*ö„kUêeµâñG¯‰%W#¬ãj•ºc»úø‹¿©ËÖM‡£•Ž»ŸÜèRK}QŒ…>tЙÐël<»8Q´ä”<{5#»3µCá“Rü‘g]»1®ÞXûbl®ÉâT¸ão®C%ofî n\VµsOæ×sÔebªæTD\Á~kÛVìÕŸRzìè”Ú–ŒÕNÑ#»õàÐŒ¹ÖëÆÙƒ‡Š%u|VýWcê%Mœ+U.M:–l¨Æ[Þ8@÷ÕªqFVˬÉB·±Êe»¼Š økœ•±Gö¬W–£s+æÈ•ZµVVíýRa"|5¼]0›µé—K4èRmúþÌ%æuê¸Ñ¦O÷™É¬nÓgü·èâÌe~›>úÝìŽ1-w/îøMší1Æ^¤Ùc Íší1F?¦Ùc\Cêþ-5ÀÝþ]Øš³9æ·æc:¿!gÔ öíƒ.Ú18pÑ…WlH]´cÿE;ò]øóº«ê²ÿ¢ c ô±öÌü}ŒQ6èãl‹ ú£.lÐÇuaƒ>ƨ ô1FmÖ 1z³}ŒÑmÐÇÙÖÍô1ÆoÒ 3ö± ú˜c‹}Œ1›4ècí¯cô1Æ^¤Ac ô1F]Р1æ‚}œÅß 1æb ú“XØ µ›ç7ècŒº°AcÔ¦ úã7iÐÇ{~ƒ>æÈ²AcÔf ú£7iÐÇ[4èã]ù›6çãLà˜æ|œ ~Ls>Î&_¢9¿Üd9æiÿWûW'ß}.ìWEþœÈ§´FyXä;Dþ´ÈímQ~È7‹ü§"ÿ§È—´GÙùD~Tä ;¢\ù‰üe‘ÏëŒò‹Eþ ‘ÿFäÓ»¢<"òëDþŒÈÝQÞ#òë»ýÌ?ŒØÎ·ˆü1‘"ò¥b;ùÃ"?.òN±m+"¿Wä]lÛI‘ß*òC"Ÿ+¶í¸Èoù¯DîÛö*‘_#òÇE>*ò.±oùOD~Bä>±ýoj²ý7Âg`ŸjYò÷‘8¶«"ÿAø1¿#ò­Ágrýó½5øŒ­Î¿~Lç¿Ù ¿ë¥óo‹ºß)òCá÷ÇüÏæ‹c[¢1îDþ´È‰ŽàóË:ŠãüV‘?"òãÑ2g;£ep;£õú 8æëŒÖý¢®høqœ¿«+øü²Îßꊦ³µ;Ê/ë>¿¼‘œÌÏþêùû×»–ès©‘ß&ò#"?³5Ê׋|—È_ù¬¶(Šü‘ïùÔö(_%òm"Tä'Dn|6YgOäˆüO"_Ôå‘ß-ò×E~vW”_*ò"?,òÙÝQ·YÚŸ}ªeéGù_ ?OûKáçd)üœì/…Ÿ“½Ù¯”ê»î êÿö• þ”y‹_OOõ÷ßsKø9Þ[ÂÏñÞ~>÷Öðs¼·†Ÿ»=â×[?x$ìþª°ûmáòÜ.ÏmáòÜ.ÏíáòõoÝu{¸÷«Ÿ—–ûýoý‘ÆqYú÷¿“Él2;ÿ÷¿Óúàùýo¬›]ú7XU}©*mîájÁ^1Þj?¿š6‚¦›׋Ýá)ÑxY™>9˼&–?ÿ‹–wœóXæüÏ&3òþ9}þ'SÎÿõ÷=þ×úÆõþ6‚]?Yêc»gÙ*UÃ>]ãªK·ÿ`Ðv Ʊ/gô-;6ùŒÙ…šS §ržžRÐeÁˆ‰ÕŽÔÖç è?zíñÿ¤{^Þsü?34V``ÿþ‰ýc#yõèìz½\*ïZêw¯*¶g;úµÙ©óî~ЬÃ&1w}èÞ_sKzJFôvÓª7ÂÜ{|˭Ц=× OìØ7¨nQÍB­RQ kºö+gíjÁÞHësn¾`•-g‰R{èšá=ããúND›å.ÒŸdˆµ2Ç¿Ì+]ìΑ¡|~àÊ!=£kjÓfÅv]kzÃ,ípø7äp€îÑÝWíŸö™Q§4]ªZþ·"F'_a<¿ûò Þ¾î ~ÚèîüÐØÿè–þâQñçRÑÜ^Ò?˜n–ª¦ÔôŸ¼.Ü(+µàØé¾rXíéíùÚ¬S°Õ)\´/4kþŽ1§œZÅ<4S*Ì4«°åÙÅr„-X§®ýcÃjµÆ¯WyóH¸Ôu§¤Ü;¼Q–¹X›Ôßo hÑ'ð–qýg4ϪÔÍÚT¬3¹kþ"'~¾ù2ßÜt™u9åE³VÕ›­\[-yúyH¿‘¦¿@ÌÕ8ÑxËFÏudh ¾¤=wÉMÒØé…÷ŸYÂýö|#ø©ù÷‘„^€bó5¸tÑ5hÓ;»¯uÌž2æ?½´ÛõZaF÷‹>'æS«ôS#jm$šmâÿbï܃ã8ò:>kI¶´~H~ÛyÎ%Äç Žkiå‹mîF»+y‰V»ÞYYv ¨V»#yã}(;«Ø$qŠÀUÜÁqÉ…ãÈãr—p I€¢(êxþEÿ¤ ŠüqEŠ‚ª£ÂÕqÔ%p˜_Ïôìôüú¿0Ê ¬Æ5®ãïF£ÃøûŸí`¥ö_¿º¼ýe7û\ö?2ûß–ÿûŸÛÌI¥µš`Kkºy³)gP,äIÜXï0=¨KÞ¿AÇ¥kûtÖžoÍ?/'µX[˜õJòZædéÚ¬¿ñˆ\ ½Á7#¬?¿çØfÙ»„õÝ‘µß ·Öê~=ì‡};ÂêŸÕý¹µÎÈmLùBUc±Ýï‚€öÀú@I2¿á Ý(“)cÞãþ>´ÔÿºàYIÚºÄ_4óû’~÷©…óZq‰mãnÜΑ—JŇؾøgF†¢ùÂÀÙhžbÞ›¸¤¼q̽óÎŽøžxlmJ˜`ïÞØײÿÿ3ÅûŸ m´ÚÿÁúNÊúÖÍwÿ÷Gð·Áúÿí±Òþ#ƒžý†‡ÑÿÛ‚©÷[OH®ý,ÝvìÿÐÃ>¼\ïû?¸>ñ ³ï,g³Ê”ñ™ç]ª±ñ+Û+A#W›—…ÝË¥ üvñ´ý¿Óµ3Нƒ d닎Éô˹¹ )×µÚ:Ù»£[Jħ'™*î²K¡›7Ó×ù¶cµZYؘ¡{|R™˜Í¦gØq¶vQfûá*››¼¿H·9ž%²t¼{ŠíÞìöZý°lˆ5Ø_$“M³ÏÐéðîL½Æ6éKE­Ú(Í[»£èÆë%ÿ.û-›˜$»”˜+9¦ïdµ²–×5™&š|ñ¼V•?h;GW«hº¼Hg— ƒMnhÓ… ¸Ý²V³¹sÖ„û½&KæùXèÊÿŽi Kg³SŠ±Ù‚œ#Ô µEM6 Áa­¡_žQ³¹#ò™É1¥Ý¶€o!ªCëí/™¦ù[vÂ4›­olJ!ŒCÎÁ¨;v±l¨(Œ”(v÷}1Ì£Ub ·É¬ÐÝèŠ+æÜmξùy*ù=äÂÏ/sâÆ&ØúïÆö€¸Žý¢‘(Öíû?ÜÞøõþÍ$2›SS«°¹é²ýعÿCtpTDqÿ§-Üìýå·]×ʳ MoÌê•|¹Ü_Ñ2éI%›|TÉ%ÓS«pAbtd„ü£##Ñ!Ã?:4dÈ9ÒP$:882242:, KòÈ*\{E–ôF¾.Ë’>w>ߨæZÅ«V(âü|;²ÔNVlÿF~®¬õÏÞÀ5XG‡‡[µÿÑá!»ý£ƒQi02xtˆÚÿF®˜Û¼ýÿŒ';9µ‘¯óõ¢ÚHå«,ôùM’Dÿ ǸJ„øñxÈüý ‡¼ ƒ[‚«¦uúÝMNUSÉjQ»dµªL®“Üar},¼TY,kézQ«kÅT~Ql}¾—\¹±r­pÁ ônXí=%yk9Yº¶Ú;(¹*Êr-nuú¸vMs±væþù5ÍÅÚÂzÈFÐå[€ó?fr®û+ÌÿGGÜó¿Ñá!Ìÿ@Û8ÉFpò?K®S¿\-H¦9bßNî7Çà1ÞBB¤[•€ý¿˜o\ÿ5Vèÿ‘á£çú?28¢ÿ·Sï»öÑaWŽ5µdë3Óíp¦œ/UyHÇ¤Çø×ô‚äU}vËf«!«jõ"Oå–’)qºÞ“¬ Ã¥^Ï_ŽÕÊK•*‹v"YmÈ ÞKg³³¹s™ýHäÎkòb­œ¯—žÊ7JµªÜ¸¼¨É󵺬å çeºV]+›!‹õZq©Ð8"çu9/«ÚM—µêR¥ŸÚîX+û º„¼2«Ñ“ÌÔôKYrÎt®«ô!ë`¥âo3ŠŸÉ¦ãÓ±ý>Nk“RJR¤ÈõÒ\©º ×µ‚¶Ø¨Õu¹6/ÏkZQ^¨1y©Ú¨‰UrMåÞtsË}P-ä©…‚Õje¡àÝã“ÊÄl6=ÃŽ³µ‹ò|ÙèÜ+—ÀÍjgÔÕBÝSÓ©YÖJt|ÏÔReN«³vðQEýšàƳõêÕÿsÉ|+‹èèfÍfÞ䣎@±•B¢\¨K^åcôãä:h•Í£z²ô€ä£;í27³:—‰t»pü/Uçk×}•æÿ4pÿ£Ã¸ÿÛrlü;)‡Õ¥9ë0¼Öyí#ðóŸÒõ_c¥þ?4:ì^ÿbýß6~A8¶†ÂrÌl%·M²Ÿ¹‡Iñ´ßqÿm{|ë ¿þ¿ K~Ë÷ÿ£Qê÷Bÿ‘‡¢Ñ¬ÿÛ‚9§>ñ)Èú˜µÈrëÿ¾ã’ßb£ç’ä·Ø`“ûpJ=“Ȫ4ÔЯ-äîJ©ò“Z]g Ÿª±:"—úµþ#r¤Ÿ]‹2•KLM)’i˜š—Ž+9e6žPcÙd&g&'†wŽ'q—Ì\}ÄRny×x21én9•Tséì9—xkzLMdÏ(>—Ü&¤®°ZŪšÎºzÕL"–Ë*“³3É©¸±0rdMÍ)¹„K¸YMOgcniw&œÊ%§&<±Ï©1eÒ]¼™„’;•pg§›"gÒ3y¥OœIz®Ú£¨ñÔlJIºK¼ÛP&SJV‰'Ó©DΓh¯ej*ó­Oì×üÛ0Ê”’ÊL&]}ÍÀL&sJQÝyÞÕ Ï¥Òj†jÁ£™Â˜2Ï(ªÚêò±éì÷ÉÛ¬@¦¢­NŒ'&·ní°}jº™ìøäôÙVÉŽ§cÓîÌîr¦ÒtéVIOxÛ±yö©ôdz"«dN¹³Ý¼¸_]7k²…vîu‡ûåOHDMúhÊîfx6Iúvn,¡¤\QvZQ² vc›F³òÕD›ͭVèÌ·ï3ƒÒSãɉÖöˆg1ÍRÓ]aÅtEÙcDaJÓ:³ËÊÓº!ãéLfÒÓÛÌ&HÝS‰lÒ­$<ôl"66™Ž=â 5{¹I ›>ö“ŸB×7,Àk‰yr>JÜk‡øœg§Óããj"'ùµ>Ón2µ±G¼íkfÊßä›i·TS9[ f÷h¡þ»~%ÛÉc´ZÌ–k5¾˜WÏ&b‰ä™*¡Ž©Ó)Öm$¿Æ`c‡¿Ñ§w^Ìn²Lò¹Ì¾a „¾ã ÙÉÔÓÓ ë?3qMb9OayŽ}†ÌmÍVãŒ:=æS ³qÕ™d.v*v.æ© ^*¿v»ä7žš:•£oÒ/ØLvæLÊ›¬™[ÿÑ;ôy~zÿ@ÿ€=pîa?}æKÝLnujãØžõ¿y¿6Ò³;G/ûéTø>&r)évC&èån&ðÎwŒKY6~4µÄL¶ÙuÌP«¶l 5Á­ 5®/ަ†@œ¯ìo <ó”Ýv8?éÄVõîlÊ„ùȇО‡ìsÈÅù‡ã aÞáHÞšoô‰B>ÏpD´æ»œB^ŽÓù|Âqºe‚÷y„–r$Áç ŽØƒéHܪ G‰…V>è'·®ë:©9Øï;æ{Å qüwTNÓjõŠRs¼¿ÃùŒóÂ¥Ýãûfw\·UÌ1žÛkãvFÅñ[ ã¶­å¼kï°¼? QÌñ¹Ï!0-€pW†ÝN‰•aA,Ž¿v­»Æ]ûb¶I±Óp¨ÝØNsc«“ 6û¿ñOÈ7¡}‰ÛnÙãœ]¹ÎñMÈeiwŠ"˾ÚmæÇìÓ›ã—}õ¦µÞÆî6yol2nDzn°m÷žº¼¨Õ ƃÕÙ\©¬¹Ýc[?ÞdÜí9u¼œ_ˆåÚB­~9Pwy’˜Yh¨‹Z¡Q_ªJáO ji¡’¿¦4öxÒ˜>3èLoí›ȯç¢Tô@gîó/r sIBã¾:"½ë{"»UÝY-–XMnæ‰tó v¹L $Ÿ·Bìex¹«P3¯(ö¦Rà¨ì7û¾â!‰Í Ù 'ÔF½T]ø) ¾ÝÝɵúå"[‰JoœZd¹‹©ÅíæT– â„9À¬^užÞ8ÕÉŠv,HuöÎ$’§r|lšN­j…f6V…Ž©Ðjr"¥ÜœúLlœúd9<¤>;Ìqpõ*ñ¶zøj`õjñÔÆ©E–à µ¸Ù´•«Z«ûƒÔc—a"W³L²ßž·h‹µ%ªÛæ;šÜš|ú MÍkuùb©q^^zò¢lÔK©Jë]>\ªÊ­¡Õuö0µÏ˜Ýš)Z/&v t¶€éâ©ûWÏ.;ðoí§}[ÛxçòôR¾ÚXªLWKÆ[££;•PÔäÔ8›šŽKÃËÕq‡u\qüWð/Ëg[–¥“½¢-™o¢0:²Ú¼ðÓ¸µ‹™ÌeÇ­ömV×;–}‹²×ÂY‡0^ÖÈyv{ wŽNÐÍöÖóMÖÏç5öF8ï>½æ"†I­VîS7ùmè• ê™ý°³Âʾè="¯Ü*9³ìw⩤ÆbÔ§¨;ý–Î+ù þE~Ä·È,¤[(@`óÈ´®Èùr-ß°+R°å3Ziá|î'Çgº½ˆ÷©·naѼEÈB;5ÄU°¦q=‘Ð¥ 5gQ®Wt¹Z+éæ…óùjU+sãG†å²<—¯óº.×5}±VÕ5ÿâ†+ý6xå÷îù û!֘ɸñíC©®7d²œZµš—Éž—è°>¯Õk†a—Úûúýµ$BLĴè”pµx –$›UÎÍ&ÙÝæýfI¸%®ËúÒœq¼N³¾­ù8Í̾ÜRÌmͯX¨-ŠÚ¥õR×L§;q6“V§P÷°2hóódÊJOj† -ÔùgS¥Š¨Ý®|Ù¿iß{=SöûA)Àœ2¤ ¸±»Øƒf7î4†QöŔѦ†Ìʺh`¿¼G–Ï{dæ½Ûx^ev°;I!žX"­,9oœ/é4“ŽRK¯—ì/óIÚë“4ù!Ó˗˦½ cm¦NJór®¾¬§¹YíáÓm(’¤+Ù3Æãœ½L•h~¼XæŸ jõ'óå@¹Þöa‡ðpÒÔÖ£4š5Õ´6§³ by;Â;5ºpŽ9Nª¿5éšOFÍ¢E’fœË.h4éX¬× š®×êBÙš§¬ë’meÏAgi4f<·|PÕÈÀAÉ—e½·ÞF–çi%ת‘²“Ö_ÖZe¨ÛxÌí¥WYgÕ4Õ_/Ùw™˜Î\2Åæ!»Sµbi¾D#ÕO/•KÔñ€Ó@—q òÏòšßG nŠŠþ%¸¡»']Úb­Àöê˜ÎÅ„6 ÜNÛY;ÍÆS¹lÚÐ54Øš6˜g­«±-Ç.Yíe=Qîm®QÌ'òÒ2÷³ø)âç\kx·Âçñãîû1FÁŒ¡¹ú|^Ú°îM¬qñ¼?Ýê®L ¢îò}»äfVØ>€%Ϻb_²Z¨kTóe+s¡òM-ä-ä-älÜòšÍ-"oi!ïn!ïi!·om!ßÖB¾Ý/ó;ü„½~Â>}ØÙBMüÅ»ýÅ{$ÿ§{ý£ïóï÷à À¼0ÙËþ[öQ‰a>ù-ý¿¤¹E„ûöwˆ—Öµïé†x† qóŽhˆçÇ|ñLXÞ‡x[ˆò—÷6åö--+¯Æ@åº_â­eœ#܃±â9²n;ˆn®çC’½U•¸Pñ>à·ÝÆfK.,æB¼0¹w!âÁ(ƒk%â‚…¹æÒ!Þ'Œ‹ ³ÒïFÓñé^H²?9öL-B¼S'ðA)ÄUÚú !®Äf‘<¶5Ä•Yº—o0r'«Þ¤±ÁH‡Ñ„ÌZ›ùmEh±»x”Î(ÍÆ[.<²Ì%„&=Í£aöN(bëúnT&-Ð꺂j<À£|ÊÅ«%÷óˆ÷:#ºæ>íg4—î´Ê˜¸¸áQö;£Xõw»úSs/ñ`ö‡ŠíK¿.ú©¹÷¿âò_tù/‰~zî_sù_}J÷—ÿšÓ—_wùßýôÜà›¢Oòo‹~j.ó¶ËGôÓsï¾'ú£/…ã5rÿJ†ãÒ‹áø?¾Žÿìo…ãôûä×Âñ?¡°#ÿ]òw’›¥°çè÷Éÿù‘lóo‡ãOÓïߤß1rMîè÷Óä¿Inß×ÃñÇÉ™ÒAa?"÷rý/Sú${Œüè÷?“ÛFîÈý*¹·)ìcrŸ¼Ž~-ÿâ7ÂñãäFèø*Éÿ–ä¿B®Ÿ~’ÿ¿–¦ãéø1 ¿ƒÒ}”\¥U&ÿY ûˆä÷}Ã<¯çuªrÿþÍpüÞ7ÂñÉÿæ·Âñ¿ ããäÿ¹¿¡ã_&÷?Nþ³äÿù›áø¿Žÿ×Hö)rߥs? _¥°¿§°wȯ|;Ÿ§ãýoQ9ɽG¿?&÷¥·Ãñ*ýþ9…Žæpü«¿Ž™ügÞ¥|‘¹É½N¿ï'ÿùß'÷käÞ"÷8¹/½Ž'É­µÝ`=ð_ßM)ÚƒÏ)åÃOžSþû…´’Ïý¢rnÿ˜ÒýÖÓÊöS9WQŠ(?R”¹¾cʽ¿tAù£ç+ß;TQ^ýÃÏ)šÕ•çTÒïÔ•é¿<¡üPYT^œžë°rï ‰vÛÉ Ë#Qð¹`B ²Ä°; Zq‰àFT†AD7$’Û ®¸áŽqC\à®qßùUUÇÎ% Š&|zûûú«{»ªNó?Kªävu9Ñ^{Îxû© 'ÚÓ7޳›õH·oY8ζ(ûšLûöUSí-×g٣οDø~]©½ý™¾ö¶ÐtûáèiÂË »[‹ÁöÀ)3íå[ΰCƒ.³×ßÝß¾¥ã {Ùê‚‹kç¯`Ÿ4ص/¿ôl»û׳ìC»س]moýËp{Ĺsì{W ·7÷™+²ÚÍn›#ôϱ‹J¯¶OxàL»óûWØ'?–ýÁ»³í©÷fÛ­Ÿ¿Ì>`CŽýí?gÚå2îúC/·s»gÛg½Íþû¿fØO}ðû¹c§ÛÛô³‡}:þ½ÿiö>+gØ=JûÚǦM·×ŒÊ²w´›nß±)Ó~ñÚRû”Ù™ö”/K¥c·|mªýÑXGô4Ín= Ý|h©È9ÈÞpâ¥vcÛ]âJ\{ÃUô'×y¯“ë¬ní:Ú¹Nß–®³}Ç §³ã:O†]稡®Óô|×étŽëlî:Çœå:¹rw—ç7¸Î›ç¹Î‚\×Y2ÌuzJ»™Å®³O‰ë,à:®Ü£äÞ6ÉuæÊ³lù\*4ÚŒ”1…Þ;C\gÖ…®3ç×¹eŠë´—{Ûd×¹~ªëĤíþ¹Î=#\§ãh×™.mžÏ;¤ýc\ç ù~z‘ë\Qè:ʽVžm“ï#¥¾Ý(¡'cL“ïß Ÿ…çºÎÛyB÷l×!´“çÿvWHŸ•ò}á(ÍÙBçvùÞ[îd¬÷äù:á»éDc¼ë´9N.cMsÎù®sõ\§¼«ë<³Ÿ`ÒÜuRúM„þÑ®³¯à9ÿ$‘·§ë\(˜Mûo‘G°²„¾B+GÆ|ýj×Ys¥ëxWIÿk\'k±ëœ3ßuª–¹ÎE÷ƺΆ уç:-w.«\gÒJ×I~Òu¬qŸv?ÈýÖ+®óÆK®óÙ&×ÙüŽë´úÔu¶n|7‹J{yžû¾ð¶Qú¼ë:mw‰+q%®Ä•¸Wâj¼«w¿«¬Íî¶š—ÌUåök®³.«þ›õöÿÜhÍŸv«Õ&gŽ5åõÖ çf+ýß‹­—'Ýdõ•Õ¤ÉuÖGÏßkõüæjkî­ó­­G\o˜¹Ðzÿ™¬U§.WeêUw(z­',µºÌºFуÎ]¹·Xw*·þ¹ìE¿ààÛÔwèC§Ï†ÛUžSO¿šÍ7ZÙ,Rí†.ºÍ:íWZÅ_ÿÝj÷øµV×Ãî´¶¤ß úóýªãÿf Y0Ûºÿ¥›­^\­úRÂô+_]®øúºWÕÏ®=MµÕŸ¯”¾÷[Œ¯išq ””j<Ú}ß~)-E[ ½ÞêNII1tBZKTŸg×Þbžu6¼Í2´{+™à‘6¾Låó<¹7+þà…±SRUßËçÍ3òž©ø(Ÿ•~Ç+¾ Ï­e/¬ÅÁ—Iór¥Â•g³É =†¦Oºï™ãuÓá›qÑ%²iÜ78gÙB—Îfœ¡™¸Wââ?™ŽH”¿»roº"ßXü=Æz衜‡¥,ýà““N ¾tý¨,sžZhÌ„‚’ÐÄáEþ‰W0²`B®:÷ã÷rÖÚuª=:Ô/“ƒqÔá»E…“9ÞjÜð‚Ш¢|u6 ljò³+9;å—C2qNÑ/t°TÜ98-ÊÌöU°GåˆsÇ3¶xxAIѰPqIÁرãKvï³æIu¯ëëùW0ÿãvG û Ì `T?>ñÇzûø 5§žtâ,nu.PýÒÇIÆ©zÿA™§ ísz¿¬S²r8¡¥½¯nñË\uüìðñâÈEãvï ±x]ïm¡®ÓOUõA||uwÜIÝ?­Öu«œÌ~3Ù9Ú³;ûª¶‹JB9£Š Jrlj™jÁFû§Xÿ&ôÞ÷§ê=9€•¯ö.;©ý@®m`¿S28@òõ¥`bíùe¿C%·®ÃWq›Zçÿ$hX‹­gõ?eè)Yƒ2ÿ‘'‡Ø’¤æž_šX4:?”_TÂáácêåu—z½¡Áôzüîèuß’\Ÿ…ÝÖìAuòÕûµêý05°Ž[*²ff²~jSG¿’iäÿ&t;ªKpõ±[ºýã÷Àøz=¨®^wž_[§qkâäþÙCÊ ZµgvõTQ>çÉ:sÜÿÛäÐxuÔúÈÉ¡ÜÐÄ‚\YC•¨sƒw×`ñõóNçgÆ­ ©+ÞÀÙY&<©Cï‹Áø Ÿ"K #O“4?c]{ èwúàÌ }Ök0ôª»»Ñ$P¦ŸÿeÖÕ›®Äå€ÁªúRž`}Ô!X±ó4Z§v§¹*Xû:ؤn ÖÔ êT·ëØ\k=úGMíU³yýRRÃàv²”Tgóú@ÃD©|-Múù×ö¯&†%u(_’ö þÂϬ€Gñ7gþã…ÝþûƒÿÁh½ %®Ý½v½ÿ_4ºpÌž±«¿ÿuíÞ£îþ÷®ÝRº'öÿâÊ™\\ʵ̟çlÙØ<%®†»êósî_·ðМì~Ýö|Œñÿnñç?¦v ó÷¿î©á„ÿ7ÄÕfÔúêc'íýŸ…^󭪆-úsÆ5ÿvÒROni_¶BÚD…^ä«ç¨ñäsxäòEB¿yTxðš~ø1ãÃcäÑ»¾’ñË*¥'¼JýuÞà·Fçý×h C*c¤;Õ^¿Ö}£r{‚ã"7| ±Þس<ølc'w]@ß0ŸÑ‰`ïA÷³cnŒ ±×.\EæâE#"´géã}|Â$á«^¨ õ¹Ͻ =|WŠ,Qø:|þ»¡Ani§ø\ï)ôG¿p”:ULð"í©SäìÞê¾£‰ç>?LbBúj\p¦-üÃß„kÏ”}è6ÈèIžœžÁbÔ!¯Ð‹¡0p€0’[Úõˆ myÁ9ÀúØ£”a¹=¹#èœÞV0®È†øo#%š~©bÓrBOtÕ¶¢0Põ¡òRì<•ü¢d!ö%Ø©þà#²G}¹Ñ!X‹M{Ø«øžèáÃ(vªuPÆ×Ä'<ôÅsüGžÇ >С?öV÷~z«²#xÁ‡àûÃDvO|Pá -ƯxcŸ(ö ÿâƒÞ´û¦ásFalThá•à†ýj½*ü_uxKá@ô ]°¤?r\2æúù |e•ÈÎ2füU¼1=DÅ–”_L#·¬ÂžÀ›Rnô†URŸux³*c«Ê§ÀÙ…eß Ú®SãRï>é/6åÉØÊ~ÅVU\À†ˆ/ÈAÂ>ÁuÍâ9Øß lÑÇ)ëðK~âcøjT몷‡@3¶öIóyŠÂ_왡lRâŒ×óŒW=Á6‚üÄ7â­‰a •þèOüÅ–àAû²êîàÈ o³à1Üþž›Åf«¸Ž}h»¬QõÈG|ßTþO|Ä‘KÛPr„øÉøÐE>0_êÁ—ø‡¡уòìüðK±iÀ‰ñá:c‚ÅgñVúzÒFÅ?ë"èþüxL…wnê_dº‡+½™ñ•ßa`L|»Uó`¡êu\PsG%q™‰o‹ÁÞ"fü~ÿÄið‡M¯ ›«D.â qŽø­cGr%:ðçtŽþ°CãW¾üêñ_ü9ŒïKˆY•Ì›ŒwË‘W«y’8îÏ/àL|b¾•ö&~—)»%^¡ìZøñ=áƒ~ü'#ïA=Z‰õM­èõÈm-Ò×§–f ûôÎô^ž³òéœËWuî˜YžÅv™ëÓVßß7}Ù)n¯ùÛªzÔJæQ|?ẻ+«d¾„·h«—_LkóPj•¶«ŽÈ›Çæ¢ðm-{ç=5ßj?­Á·Ð'y@”y€ÿµ´ Æ*_‡¦ÖÿqÔ)Èû®ÉzÌÓ¹YY*~CœFNlýa¿:§([Á¸ÈHlC§Ð'O¾Ñ+tá:ä®Äm쾌ޙÃTƒî±/òAS¯â¾…<ÌMØ7ö‹.TB]ê˜[!vþŒ²Yƒ c„ùOÌ“äi× •_Ò[‚?ìûeF~ê­acJ~ðÅð[ø69ºZSHŽªæWôƒM›ü4bòxc•‡`7ôC×È oÔkÿm®Ögz~MVkl¿FàcrúˆŽ#ÚFÁHû諒î Ç«©Ô¹ÅßÀÝ1¶±…ÚþØ?õ`âç è lÈ{ˆOØŠÉKU>F=8²®$Æ™çynâsLÈëñú™uƒÊ¿Á1±?üÇä.“C)þ‰_ÄO_Ô¼bæ5—/øc_:g¢ò+dÄFL_“ªØ TøB?Ä@âë#þÐùVEDó; êÇ/?÷‡u`ˆM¢Cô‡lè!~!1ÛrgF†òݵo§m¾ar¯E¹5+Û5ž1¾ÓcÕ‚·ò¸ÓÁTòýè‡uK»)äV÷ÒÁéÛ×,©‚ÎÝÅÏU᳌ÏZOl;MäªÂ®ÍšÕ3¹=vN~.¾½.ª×É‘77„«8þØ">¦å¢òø`nòç ì†99±/£S?wRC;"Ï3±A­õh‹=?ùëDä79’š7±3¡¡Æ‡wøÄg±ê˜çÈ{À¾é§mS¯¡‰¯¢ϬˆO1£Sµ>–^_®Që(3ç†}G׌Ïsòh=o¬‰øq>ÈyÎfn ›ñÕMÞŠLÚÿGøëº˜É+Á ZÐ!ï0ó“êKöwà{ðÇ[Yg«5>f| F\Æ7È?ynÖvèOÍ£øtÈsýµ#9¨¹ þÉø¤4À ?EÆï*Ùç Æhìv˜9¶7ñ›ý0`þöÌþ‚¯}æ&⇟“™þ*>²~¤zB>½®Û¡b(¼.ýòßÊæà[Ò{W_©5†±xbïÊ31‡¸Qiæzü\T³îßTqÀ¬Á]ñÇülðWóû/Ø·‰_ ä Ø ú±ŽCô]xÇÆÀÏÇ1t¾=¤RÏ[T¾ÎøÐ×søˆˆÞã«a%B^/俬Á° rW»¬‰¬Yܦʷ/ƽ¨ö¨T=úÕ2Õ(¾ÌÚlq¡Ó~‡POV6cÚ;¾äUèý_ôÚ¥£Ÿ»ªçОª¹È9|X÷@ûe lìOÍMàsç´*~‘›5dmŽbò{%“Á.æ¯ ÌžqÀ32)ý轜¥13‡áùÈü=Eï‘q´ê}Htor;e§`€MòŒu,ö­ç˜d¥cÆ71QÍßðHÜ4|ªu">È—˜ƒk9+¬›À?×¹Z÷ª½1â üû\èA¯íkŒ|5ª-òšý͘¿7௱Xc_´ã¦?Ѿ°…ãÛœ²_hù~ŸÒFáƒ~¨‡G?ÇbLì;@xü¾>YÅqpá³Ùçð´o¨ø¨ö˜·±iðc™õ9ø©ý³÷¦òU³OÓyXk ¥7J#OÌÏÁôe…¢kôèùþïçêÄOüÝ0—ÑOç~?â2xÒ]³˜ÏÁ ÿ5úcÉšÇÓv1$¢s€…j_À·©Óã/ôçHàwî³3°÷·.ê¸êë™W ëuĪÓ?Y’ñØÑftn?l寥³Ó‰UÛ¶~[}^ÿ>+¿;r{õ¦ŠÙé']ü×ôü>ŸW­[$ûU•ý'§Í:ô‰4±×´KÖu¨>º×ÿU‘/°¶¦?²†Ê“ªÀU|/MâCëSÖçÄ òY“§G°]âæåg¿£Ö]`áûtðsòGòsÖŸèû6ë;?NW’³ݪ=™=*…ë3ìû‚¦™w•³–¼dÌ7ªž5„ŸCàg&бoÍú~Ñ}ñÿçêMà,ËÇóq&h æb~Ec‚j3f¦1®”:çÜ[Õö¶…F„!†A4¢éÄ’TYÆÐÌX 4©ÄD4æúWçœ[UÃX wÂŒ%ºlÃD,éØb"±t’=Û©Êßç3Ÿn}ï=Ëwy¿ïû¼Ïû¼X Ú×rÏ)FëÑÇ>ÆçÆ(ã÷ŽÏ ÛÆb›ã5À³à×4Æ Ì#â?Ì#æWø'×Ny øɽø|¤}ÔºûýKø0x¯ìs¬ìù<8—éKxmׯTù Ø{°uúlŠ1 ö9žñ!~‹=î3`Z˜üÏòÍ5Jÿ{E˜ÌÑÚO•ëï`ƒ9øW8[?â~wÛXæ@`#¸>pkíìnÎ'ð1Œr]GãƒòL n Û‡|SÎ%¬}¯ܷµp ù…ÄéhÇd{.Mþ€>|r²&q60~š¼ >Ã:Çú~€ßÁÖÇ.ûœö@\k÷Ç|ØgŒ= üë Ÿãß„ýÊ/#FÂÞ•¼ALPgQcøïÏ£/“Á<#~Å󃪅}ì Ï÷ÇþÇüaa~p_ØiŒ1ÖÖÖÞ߸WÛä|B%ûÔ1HîqÜó‡qÂïñ›ì/åiŽÃ>Àü ƒÚàÆoð,›ë˜ìP¿(8æ¿·ÏÏýåüÏl|ëÚ¤cÿ°±ïS9§Á6øžÏœÓÜ¿X¿þ¬ðüñ¹’€düc9yJIJx¬3ØŒ½b›ÂX:ñ=ï•­ðã)ÚO¯óçž{óÏ.·ãø©tN\g­ýzÇY=ÞgÆG¹0ÆýbižŦŠgh„Áio;Ïd;µÎë{ÿ6Â6—ÿÀžbüa«qÿæP~m²ÂüaL0Êq-äGï‡ý.q¾ÀöÃôº™¶;mŸq¨Î¸ bx¼+>~‚³×g í–÷þ´öËZù‡çìZónîñÑ-¯¿oeßÕwŸýÖ˜Ùüíè k5|úÑ߬l®‘ÖÈæ³ŒþöÃß¾àÚ¿bl¾×à›ß©ø×€mù§}ÁçÆÞÆüçÀ8·ª•Ë^¬œ›Û\w ¿‰õ€g–¯¢8qgeL>p¶ä_0þØ;8C€Qbcœ+·=WŸÁá˜åägŽˆwÀÿ×_tŽú\~ù%ÍÓ7‘=ÝWa~°¦„ÃõˆÀöalðŸó+Á& ¯?^bþs>›£AüŸañ.ø­óŠÜX»Xß8#ñÆ8‚¡"oVÙ¾ñ>XømøÆì9>8§1gñ¯ðçÆ+¬%ŒoâFŒ‘°Ðõcƒ9°OÞ ôÞ–]™»QåLǾÀøaÞð[ìñ'=.ò3/g]áÏœÏÆ ™ÛÀaÌñ°)¸'žㄜ²ò?çp~kûç…oÄùÑw‰ –^Ä1®g¬5ç®ðmÖ7ü7¼#ì´0}âPÏêJïB ·²ý)}Fq`ŒµmÚ Êö­L.ßÁ{áùáÇ*§#lx CxseŽ ¾SšcÄ÷€dÿßû{ƒ¹œgø=¾ƒµ1ÀupþcÊýí_VÊï _496Žžq‹G 3ï‚çÆ»á;Æ×i·câ7Øç¸¿øNgІÃÿÀÇ=q‹s—|nÄqKºþç"ö^üL¬ Œ+æü*øÙ±aØåÏϨğ˜k“ãÃú4g¨úάá7¯xöMgßýÄß›=õË/=ûFÕìäÍ_4üîó.]{å£®ÂÆ]zÿ/?ýƒ³gžôÜ}3‡Nºí âü¿ýåãVß÷Êà,yÿË^3ú—ßsæ×F¯:û#ŒÝõ¦?6˜yÀpþÖ ÖørøØ?Ö?Ö |\øIÀ{°Ö`s1–سâõ åxÈ ï< 7®ÓÈo?£Å™ƒ9‡=€Àº´}bl›±OÎá÷;~ ÆBñÓc åjn8PþKøÆs§8ýã4?ü1ÆÜ×VÉë+Gº×~ìÎÊÃÖü>Ì]Ž–m71dø¹[9b„%|W|nŒ·ŠÏÿ6_¸Ù"msði¿aŸ×âß°ÿðá/`cNä×f¾q”s/´o˜7ÄÖxn< Öö¨ß¶Â¾¯ý1>FîžkÁœŽë§O~ß9ƒS÷Üh`Äù{áÒÆKcCŒ[a‡qOü‰ßbŒñNÀ6°Ÿq>â= »!FÎñÃ3ÿÁsâŽ3¿†¸6Åûø11ù!½Ê¸0ô*¹éøæZrOÂ$ÿ.yB܃g-l¥Ï†2ñÖ‡pCå²ãw8ö¦ lïƒgu|E~ö<ì,bçìŸplð°°_ÿ¾¸¬´Cø¾ýÃRØÁU<‹0¿Ø;8_ñ[¬yØ>ìYÌÛ‘Ÿÿw%\Q<¼‡8äì[óSé_æLÆýç,c›œw«§×8ç1î¸'¹fqþ*ÿ³øLßV¹’^n«ß™þ‘8tâÉ(®¨ÚıŽKqX—ùÊõ ûüÎqLiîq"ì/àZ;î{Ҽß\ó¶á«_öÌ‹o×®m¼â§«›×mÎûêæ;Îà·kÃ'êGþjðôüÕ_¼ýƒ+x¯.ŒGoºóW0â‹>1¸ÿ=ï¼€~èGŸô;ûVËX7È»aÞ`œÛ Ï×øS¡3ä×4pdãµWÅÎÌëÓ?„σ<Ÿü„õ.7èöEã©Ü£ñE®a¼?0FŒ_Ö½ýûZùÃÇrýˆ¿½?¼®e¯Sà?Ķ‘Âï0¶ÂÔNM®‰Ü!p!ñwqŽˆÑ“£Žù5‡€q‡qõÚ¶“Ÿ#÷š= Ûeþìó°/cñÑ—õõ&97ÄmÂÝör_aïÈo9Q›;Áó;×76üŽë_>åŽäó¬ ƒûh0Fò«§š`Ëx<¿ý£`÷Ü[ñC`31ÀW±ž•ÏØ_£ƒOŒg7?œ6Gœ7~ùsú$Àní›OãÌ3¿›Ø%l°kœ¡æ]–ÆaÉ}Äùûã:X‰ðŒx>å]ö·ZG;bHø.îññš§íÅzÅ߃ ÷ÿ%ï1fí ×â4ö<ìü]ó]Êä0^ZÄÐÛÔœׯûÀNbÍâY0Žø7Œ¡ðÓÅeñ O4‰;3æF–Z¿;[a†6[ë;àâÍs}ë^Œ—™;IìŒ3cçü óJ~Nú©?Œ¿Þ¿W;käcînÇ:Žøäá>`¿$÷"H¯2§"gc)n&¹ëmò øwåHÿÀ<žç¾ï†w”ÿr]ãÜãrx5ŽŸ—§}NÜ5&Gu±1? ˜2Ï)/ý"a´=æï`ÿÍenOd ìXrèÂ}ÆÆ—ŠÅaÇþÃûƒßâ=_ÅÄsãù±wCxO&÷Q&w†Ü|´øÉßcþáßanp†¤ ûLÜšk‰ ˜;E*¼¼æëKkîÜÖ1+}³Ì>WœUµÊGÌÑ7g’kë/|m¬!¬ÑÔà¬ÅÚH]†s M¸FÆgºÜ8žÏk?‡ew€½5~ËÏq/øoxWç³Ûàš°ó°Í°˜Ø&­ýIúÂvV©¹¾/ÞÂTÎvúx<«ø;¿eïÁÂïFûñ¾Sî³vçG“׬ÝôÀ™—ïí¸ü…»ÇÚôoßiöÓßìí9íßËYÌßséêo¯ž´vŸÙ﮾íÜ;¬^uÛ ?ó±+fŠW|nóðâSî8ÜŒû‡;ž=ܼÞà‘¯»Ýhþc¿»òã}{;òüÉÁ9†ÍñÇŸ<ó638>zÅHµTØpà[a_½òÇa¿Ú§'OëqHjePGd®Ø´ëmÊÔ}`n1žÎÏr­à¼4׸‘O5×ÂÓÙµlˆu~¸¯×4Ï øVΡ`dÝ“Ÿcì…ûÀÏÀ¼žóñÇ5ÃßLŽóë8¬tÞùQÅ®®×Ý^Úèð~ß,Ì ¤}À8ÉîôÂñ~ɳÑcÛ†û’š6¬ë|&@yAÜ[1ûNžÝØCÊïöÂ_-'£”ïè8§†¨˜”yŽ«¹R¥± îá=Ž]¸Ž©ÏÂÞĸà;ñW•â%( ßc þl¬1Ó:<0ï;>;òÛáFàùl¿›p³œÿ.|¾ó 1…s„3^ë£Î¿oÄ×3V2—3*gÏ#ÜkLó ~cbx|bsœO°9ØC˜¯ŸŽ›uŠûãlŃ_¡œ»8 Án<–ë¶ã§JÕ+*þÝÛ†ÓêÜ¿ƒyøØWo+7ÉáÇ'Å÷Ìaªí‡p}àß‹t¼SÈ·åŸÜ [cÀœz—ŸQþé4>?®“yK æÁØß_ÜìžÏÝC|¿<·ùe­ñ×"wüV¼¿c¦ä¥éÿc_šGäwØÙæüdz†·†ßãÄ¿˜lÂßQÝ‘øÝ®‘j¼¿jùäìò90ΘS׎Øš£_†=lµ ~€gçr'ŸçU8EXoâ Ï“wˆó \J<¯ó„ô½îÉg”Í›'Æe¾ø9|gù™\_ƨç¸719Gâÿgž0¿8Ï0.öÓJ×½sür~¥žk_œÑÅ®Æ6þ+Ï˰’/>àô•ÍsråF?èÚÊžñ*Æëó÷}çÊM~3X…O­ù<8ú³Wœ´ú„ù×®`<à[ÿtÿeÜìêãôgœë8瀋#þÅ~õÞ,ã/Âÿ€?¾˜ìûd™šnü;p¬à¶CÆ·ªÙTÝ>by¡ÂÐñwø·Î¿ÔÊ1îÏúÀ^hÍ]¢ä:ྡcÐùäåÁ%®‚Ý;N"Çõ}°È(¯¤&ဳò6©s|W¸Î›õ-ðyp=Ä:ðwaC±¶p<›pÑãáiÑ綯Àü+bÛ×mø¯xЏ~üÕí€bé«èƒâ\Âý“?ËÆówľÀX¹>>>‡=C.¾ž#Žë#§õ.—2æ1ßÈÿag:ó_ˆ“0–Økæ)k0fÆü¾cþ’s4=òp׋T÷Rû_˜áñK•òúò=„ÕN‘;€÷F.Nü â:ü˜=òxÜÇØuüø(Æïƒû˜7š-Ç㮽Ñ—(}Î×àûã÷Â'{…m_™øFŸ­?]öyV„~ö‘êÞw·áßizXŒEÅù=Ñd#^ÝœSðKK߇g§kd8wx?<'°4ç&øüÎÃVWìüÎØA)_KùY¯‡äö™ÿÇ÷°žð\ªSœåþ ÷ÂZ|ÜÓÏïÚ}Úyr/°†2Îá/&Gˆ10_¥B®(µÑŽ©KÕïÎq_á=a¿ÄÆñǸÂN™ÖÅ¿òq‘ŸåºÜ*¹q¼Þ 븴×jN¡k¶‰eaý)Þ#'ʱÇd¥u¼Áëc>±Oñ=ìp#ð}ój ­^ÿÏsØ¿7>Ln‰yü=rcñÞ«ÔV«C¬ÿÔz{(’ûÂs"VÃ÷d³YûȼxæöëE\”ÓÚÄ6cà°‹Ø“Ázq]×Â$/c…ù ÿëWõ¿'WÊ«¦:üœ)8p?è[ƒé|{Î7ö-lŠx„;¶ã”´ÈQÂá>8Ï{¬ñ{ØkÜ×u®º‹Ó1˜gçͨâÚ™By¼£ö1Ékãõëì _kõãUÎÛ0×lÛH{…ßbm-~æÖÌÛ„Ÿ¢:JqS°æ0~æ wk×gç ãt»Çn¬a.oùê?Ÿ}ÉÇÖf?ô»¯]ûÊ[¯=î›gÎlúU³ßÜwÙ*æÿûçÝ~fñ©gÞñ­?]}ñþ{åFç­Þù˜§Î¼àÚ;1ÖoýÅâ覧÷Ÿ Žüü¥x.ìMr‹ö¾tÒÍGð§1f8ßS`i_ï­Tß|c8à/8T/Ï3gÆë.ùSÅ3‡œ#›cNó g‰ñkôôjåL÷ÒfÂÎØç(åcÊö*½—úÁÈœSDÝ çσõâsºÓÁ5SûîÖ|KsCñs÷ñ|smm >Çóû}+aËó]þX{a15¹ÁÎOðüÃ3 ?d>hëýÛ›Çu¤I3Elü˜ÄQ´Y¯Kî7öìLüvÄ%°ÇÎÙ3±0ˆ±æSœ ò ÚðãÃáûÈã™0ÆNq7÷“CÛ¢úHæX+éâ¯`Í£6#qxvØW ØÆpÎŒ…qüűbLŠØ0ÜãR\£sìÿÈ÷Áõ°þl'ÝÛN“ÛÄú¼cbÛOclS®½½´r]–õ›„iôÉñØKßNÚ,§ÅWMmys#ó‚[Õ³6ˆ>Žý7ÎÏu=öVZ7ªkV-îUUbGÛ‡Òg ×"|O|Ž÷ÇzÁ^ ‡·¯ZQæ§’76W&Æ?ㇿÛ? ÆÅu²}Íâ^ÆKŸs¨­R«âñiìòŸ—ªÄwx÷ä Tc³QÃ>‰ÿ1ŸõS:ÿUÇçÃøà¹aKíß—±¯˜?ÛÆ/Þ¥bc©˜ŸŒeê§àß°1ÞÊ%ˆdŒi6±kôkì?âš¶áºÊŸ‘†ke}âsÅ9Ò®IŽIµY‰¡™_ɵ™\œlæ"ãüݶ*yNúäÈÍbÿâþÁ¾d/{Üã?ºû/'c/â~»¶¸$>Gœå¸±yä/`ü?{0Ç~±´&@kÞ/¯µïæaË1Þ?cj¥sL¬]Pí75¥ì¿Î9†ZœÆœ†ÿ"®ÉRr,µãœJí!æZì÷û‚ÍÃï»-ÍEëâ'Ì)öœ1rE‚ÃßÄ;G›ßuüZG6æQ¹.¡uüNÛŒ1^aþh»ö¬ÙYüÚ>gÿtx÷S>°öÕ{ÇÚ-¯¿oxÓÑä꽞ó›!Î6Œx ?¸ü©«½éã¡ûSMÞü?GÐ7 ·ãyÞŽÁæÜ € `¯|êÌ R7¥u“j–­OÖ:p~½WÄçæ:_ÊGV½Ž}8þ‡ZÂä©`°fS„±Ç=0f^ˆ·\Ò#‚1ÀÚ†m0_g;¹ ÌûçÖ¬ë>¦Gò\Ãü‰{6éõQªàg¶yü=ÖG8L¸Öl3||ØaoÔ8,R—õ…sV8kïyF`þ1>øwØyç¡ý¹4j\3×{HýËtjl‘{Æþ…‡Ÿe\£[¿xÕØ_T9GZ;çLhÝ ÖH©~Gñ·¹ÐÔ‚Í3¶üz9u´¨ ·¯ÏÚ™£¶…ñDÞ  [ ßDügµÇù³ŸÇüAbGÛ9žm8§…ñMWKrBÄŽ®å-’ƒÃ{`ýªN@5t°Ý9£aP÷‰y6?½w‰Øc¼¿qæ*õñÉ-â]s€­Å>ÞEMBÙuí_Õ½ã}1ÿ°Ö*(£Ìcižkp¾Òx¦k’Y£ŠµæÜß2®ƒùŹî}Ïñ¹†º6¦|_ž=ˆá¤ßAÎx—ßÂüÈ¿û~g§p¿Ôñ˜C?<ùùJÌq×ο‡áü±‡Ä’Ê]_Gß~»F÷8aÉéEc¥Žk.áHû’µ"ÆgO„§R8ᙆ½¿•wZ'Ž€ºdŒ#Î>ìŸ?•ù5êÿ{¬±u}/s®Îa•ÑAli ‘ZÜ é¯aOaüáÛD«$Ú4°%?Ì)Ö§ó_Ì·8¿L}Å|—U®_eì1T=ø âêÂÎ.nSk|s™ÜR­Üß8Ö`kºÛ¿¸>ìeßúâGÎw(xÆä&á–jí’CÎs8º,ÑÆtíiS‘¼*°ë{”Öcd këÚyúVú,Ä• ­Yæu¶K—Ê0'×\Ò>9Cß3ú˜Ø_ðMƒñÁ¿÷÷¦ñ{ÔD ão黈o<Ùq0ä;ïpì%î/ÆØ>@“3á‰÷¾pãrÖÍî´öã{\ûú7µö„s¿»öÔ‡suåwî¹çí+O›Á?îËë+ÕÿûíÕ Ÿú«¯üñçV¿ú‘ÑÊá“~>³ùç¾ø¦lÎÅ`s/ ßñýÇ 7Çl°çöYF0|mØPäA¿ñÔ?½þÊ?=ó§Œ.ÿõ§ÎËGÃþÁy\e—ô›Óo21N°Î5Ì›õÀ9€¯Áú_Äþ‰w`ìðwìOóO[玛ä„;¥N'Ü=óSÁE§_ ?1šV΃qŸâú°Í°O°iãÚ~:sðáUÿ»rž·Î$¾:êÎv bWä{(¿/­»K*çJ㇕s@ÄñTŸlŸÜùø ÌCàì †?aí» ¬Sk{CºÔäÁ'Wº-ØÀ¾å÷êlõ³æV¹¶‹çþv~/Ö#ë°T¯±7ÚˆáXÃÁóÛ+Ä¿Qm9Ö&0<£ëÌjÇÉcMãÚ#Ä_ɹÿrM¿9‚Œƒ+Åõ¬‹çŠ=€Ï¥axj»Šµ u5ð_¶| åWãö¨‰ç®xÔü¾^—'†mVí†ÆÌy¸ÆëƒU¬u<Ÿñßp<\/F’¸×–îácøŸ\_|·“«­±íÑg@ï`LsYØÕ\Îh®?ioS3×<•ž×!|ÔŸ27s@šÁñQ:<ÆÆµ%e| h¼Ézlj_ãE{ˆ<ÄÝ?þ»ç¿tíy ľu'ÍŸqôqrøŒ}7[\i_z¦¶‘~ÆÔœîØn¼ƒt ™o¬Ñ݈_ñÓJÜöÉÒøJm‘çÖojoÃÍ‹k¦ÒùD]Ù&{À8Gã³±ÓM½‘ÎPÎkà„iR›$Ü‚Æ\–Jµm¬ûãþsÞ§Èûa:gÍŸSóÐ@æú÷N¾k¯ôùJÜÞç ë®ýl¥seX‡©Ž_ш!®Fj§s®†Kj®9¢_×êš{@qÞ c캜Rú*¾1µï:ØÒúVæ¨,r­á3¬_óÁÌëÜÝåê…a]Öa_˜si¯™O±hÇs*룘۠*ØWÅæKÌw˜›P‡+…Ï¢‹`]ÅðƒRGÖå•Tî# ÆÅ<Æ2ÚÎѸ”Cž«&¬ÙíàØ’Ô;ö±v£ô“¤íÞV×]vêŒï]öáá9?¸ïÌ_Ç™O¾øoÖîþ_߸ÉÉ+WüÍÍc¸Ã;§ Qð™— þgMâO?õÂÕ±§ÃþjQˆ˜ç+òðßqèÏÿâíw#çãîF´´\ÿZ ¯8Q`ýa}šÃÛ˜CU‡Çêü65¼T²ÎÆõT´¿ª¢.a+¼m=:RÌáüŽz”¼|çYè«âsü^Ò^}WóŸ˜7‡o„XÍgo#=ê\±F ¾ÞßµÁO/àl5îÜŽ­Ýšøs†¼öˆô¦šÿ]_}´€o€±RZ¯´Žgú о;ß뜹Qä`Yƒµr Úº^ºãá7GÜsü¥/~san7ÏFóO¦söy/âɱÀ¸º.¦–oEÛÌ²ÎøÝ†©ù…¥êŽWê-±ŸþµŸ­N=μƒë$£Y’Qh°Ñ>…g“ç3^EŒó‹õcŒª1üÌ4\.måMn”ßvbyÙPæºZ`AcjU~Â\8åß<…jH©Ü:Æ)ÇjŽñn8?Ä'âºêt0o°_Ñ .©øŒøv#ÝÕIù|jl»9Vˆmñ{qY÷‡¿_”¸<ð ç0Â/áY£’%æð]“—ú:âŒØ_Ì¿j+6ÊøÅΣNKŸû`eþoaÜÅØ¿Ñ¸OíJl-ÖÖ§s±áè—â¶Rk„üø†æb6ÙÉ—Œ}T[çÖqÛxÙgì_Õ~m¤¶…~›±ôþžë?¸€Ïˆ29ªÔAD{À51ý™J:)ä.±†"8(®Ø×O’Ï¥F¾Óaæ pÆá÷ˆGãcÇÚ6ϼ5”±wj]0öø=Ö—kPšø×öᘇš v÷ÞðrKå¸{Ÿ9vk2„‡_ˆ<Ì/öŸýî:ï†gÃ=°>Ÿ7áÞ(F:Dœß9çÖ¤­›Xó¬g›Oï&çúBׇd1Ú!ẶáaøÜèücEÑâ ÖU‰ƒÁ|D}œAØò¿÷wšYcê/+׋ñ—ÿ¶Ø$ÿ)»·—σóM¹ar@Â+.ô<ÔþãþC|bÌRöqžg´#–*ëo·ÖÒàØÊ._Y…ƒä÷«±±V|ÖU?šþƒ=Ø'¸Í‰µ§ýö}/¿²}ÖÌ+>ÿ³µÁÿóÚ™_zÚlÿôçÎâ¹þçã_\}ãcÎX;ýoþløÏ'ý`õ²Þe«»®™œùZñ‹œ¯'¾=7|ó{>0¾¿6ÿÂ!òçN¼yôµ‡VCÜks¬FõͯYÓz„3ëû]»n1òÙˆ³l€ùÁ™ÿÁD\\üòµ/ ¼~€k}ˆ+;íbïåµXŒÇo^uËyá<ÛÅÃÝÙJ;†g³ó˜Ô °ÎÊ^ž-wèÊoVÜîœrc»Ç<°ky>ìRϘäKÔÖÂOƾÎj|§7m¾qž’g›ê½Äϲ¾kï•wHsŒ•jª'ËhdØn1†·þØÀ׬T;©ýkÚ49ã•û8QG‡ÿ~{MPÏæÚ&qÝXs®k?áÜ ñ×Úñ±?WÔâÅìVǸûÏ×+½/9ªï¼¶4G©Lî¢ïü¥kê£ä¹…ÍQû›ù/ìüi»[`í3÷(ÜÅFþó¹U| pçr»N}Ù8Fixjz«m¶Ñ.¬Å¡o%í„«œ—fo*Ú¶ð°G›,XÛqj¸_ê¡0ÙXÿÖçpø/qð(. Þ2c÷:=à»àï8c0¿©KÅs˜ÓÌçWü&<Æyâ̰;Îy1ÿæú:ëïoÔÙ7êMD}Í&5ÊâY®Wòƒ3¿ LKysöå—[ÆÁG‚ÏÙ(3nⱞ(ìÓ§ÐùuQ6óvšð‹íÿ1®Ã:4~Ô„' {k]©ÏZÚÊ‘™Z»Ž¹ '×c@ßdÂ:î—QŽ­máø†öK:`ªIG¼XsRsÔV5öS8ÖàgŒ©I~ð±fÁM< ì›kTªô|‹ßd "ži8ÃSWolƒØD´5­±W¦¾Â“BºKû»ÞTø½÷)ë@ð¹×dx>elðSölb~2Ø<¾çuÖi/Z;¦‰–P°Bi/-°…̆CÉñŠæ Ö?ƾ»p¥ãôoÅÿš¢}Oç(èc¹g]¸½ÛðGå0ÄßY#÷E1Âz“úi|Ÿ[S¨’–ÆÞ&×€ ´vRóÆò“³Û³~ýÄáwÿøÄðµw?ymõšÛ¯}ç‹ÿ5¸lç›VÞñËwÌÀ¶ }åÿ~pÏKß¿òþç~jûosí&&N[qk2oP*u÷~ÜãÖÚ>ϼ´õPñîÌq8¿Ü÷Mü‡b®‹})ûÖÅxAC±4èûÒ ¨Ìù-¤eÕõ©EÅ^aø•êÿyŽ8rU±ÝáYྸ¿uz[ó Ÿq܃P¹¸¹26Ìþ\iîM©\½t™q/ªvW¹LsòJqsÎÈÚ?ÄõSêÿ‚qÏ|‰~ºs¡Ñ†jl7K×-–¨µæý|iÌ,•ֽķúŽ6øXÆöàøèú£è[+—*}NüŸ/~æÖ#ù¸ŠóþÈa Û1òÙ½ì:…iÛr^7}¤‘ý÷ÄVÜß7Øä•û»ÖÚ8Ôü(‘C2ו9Áœ?©ÇëÜ:•ãRãÑSàç²rq¨ïŽlù!ž'Çeá¹±Gö–4¶ô½£ŒyƒõÞßÒŸ)¤O°ƒ½a¬ƒNv¿®ÛŠëˆßAÛÍ„Á}¬¯Öжž/ùÕ˜wøžÞ_‰ßyÎH‹¦%ÿßóYÅóÙ\?÷ÚÏFÑ]€5#ú"œ§ØÇÕõOy^àú Ò|®cç]B®)>÷üOçoâ’Vœ­ã]ýØØ= ¬kÈßk}Pߣ¾G}dêˆã¬O}¯bçN‡ûSÏÏž£…kS[—p­u«øŽõg¥q–Ò½-ȃ³8m?‘±*î¯Zå÷£ÿm_·´öCé^©+äyŠÇþÀwÏqÍbn\C¼ÀºÝÒ=  ÛØhMΫ“‹÷êKg”k|{ØÛÞè³·Ö'Æ/º[®aã}±þ±?p¬9ë3ÑÅ}‹fÿ$§œëïu¬÷[ïò÷'Ø Øq£/¦snÕÏOߎÜ7Œsz‰s }àðÃðÁÖðØ_Ñ/ÂÙêcé!É>£x>럥ÇYi€Î×2®Û]B<Áé¹®à»Öº4vPË$.m?2õ²Ÿ_=gpÚj{¿éÕã£W¬¼{fvæƒç¿úÿíÔÛ~2øò>v¸9^ƒ>飈= ôþêu_B=‹~ž7:of„¹FÿÝÏßaä€u¦æ8Ò÷ gìâx÷=¡–‚bÈãMøƒøþ„jé—EË¿Çú‡Ÿªý=ϸ߹¦Æ} Àަ6$y(¬í­ø„믞èú(Lb¾¸fá§Yö=ø/l&ž Ú^Æö9ÿî H cB•1ÁhV—Ã=±¯áÇáùԳء5_ù~°ÏX'‡Äˆ©Çsb}e¦vݵ=mb!ìoÕ¨µŽÙûþBt]_ã> Ôcjõ>9þ#¶î mžÙü¢&öÙ|iÍùÔ¡¤·%±ÃøN®¹´v¯´·sö[wœg™µ7êà# ª)"?Ó{¸OÓ~?öÍÁ¸Âw/Wú'Òvß߸¿H«:é ,X»Ù9<òP¢î¾ùµôv´ŠßÄÉ‘&ýuÖN'Š:q泤¿B|Ä&µèæ¦z\ˆñì2†ïMëĈùÅû…_e\±t.ÇuÁ³é1fßqqñÖŸ×\律ö€ÃìvßÉCŒ“ò¬ÂNÉaàú:æÞ²¶«…±Úqœùx×Þ´Æv£ÁYÃgÁúC•¸Î8t´}ù~ÒÉ>ÎuáZÎ?ö7Ö´;ÍŸå¼,¸V>g¯ÿÅØ¸±=á‡è¹€ë:‡ÒñïX›ë÷C)þã\-ŒNí¹uÆ—®Ñ(íã@‡®Ì™*}ëS­¯@ߎ8&Þßë¾rN!Ø;±œ‰oí“7©QÆžõþoŒÃÑn¿.Íó¡ýÃù¤|äb)~”4 Ó?F½»÷7Ûü òëaÿðoˆû®9Îó;7ÒFwl^¦};bP¸~jø’o¿š½ÑÅñÀçxŸ;h+4¶Ê ÏŒ½omT󶤋ž³u} Å÷Œ©Y•9½µy¬Ñ¶ïØF;tÁ5Õ~ò/±®ÔÛX}Sƒá*NúJ«:Ç‹3­÷?×®ÿüã›ÜÇzÙýóÊà‹ØC[5|ê)ÒWÍeeÌ49GÚ÷Oo^ãÞ`âõ¸·`Ëa7~öévö)?{ô† ÿpø×sÿ1|çn¶çïþõ-+¿<çÈð‡3÷š¹É#ß6ƒïÂþOÞüü+g  ˆ³ú–׿j°ÿ…?ùü¥½»áû»‚ùÅyÿÓ³n3Ú]¼e`íXòæ¯÷tpöÈ¿·¾Kiþ8ç¿Ãû‰ŠžÂ·A¥kp–Rʘ÷ƒ}ÈYÓw)åN/¾b}Ú9LúWî{UDg÷löžžì|™¿Ùn‹]¨g☇‰-­ rHiûaáWwÁçáïôÍ•Ç3ã?ä7´?ö¥Ö%6œŸcþU›¸;=djí'Ѿ~¼0òà—³?0>ð5à_ÃFYŸ,¯‡õ›¸û=ý±¾±n¢ýþzßúdêoO^yÞFx ¸?Æçì¯k’›`§ŽÕx>$?ëüFiMXê¿à½q>HTõÕÉŸÂþiœH~ºØÆîžïK'Ý| Z ß›Mî?iÈ|‹çgôߢñ‰çÇu­Ÿ´"ñS_}'Ûôv¿Hü?ã4|>p RGÔ·>4ÞkköÑúJexÎc÷Ÿ3—)üsÚ°äò1¿¸?>—nþ!sÏ{…y6´x~õ÷¤.[®~}ûîuOÿ89L¬бÞë˸^ŒA¾Þnòó1Î3•ÎÁÒ‡„MÃãs÷÷dl•"æ1>wÿÇð¯¹?ðòäØÁU!Q's†ÿœçvR‘}€ñO|÷·ïÂušó×;g>#çÖ*SG´ 7ÏGØwño¦:Fì/ÞyÀk¨áü†pbÈpÏÄúßÛ±gßÃÏ}mÏ{ÖÞuù×>×{äžW|â«+¿ºÝoÖz¨ù¿¯Ÿ<Ëxâqó«W,_±zû[ßwvóÜ­n]í;iíýšÄ›ã0øÉ‘zò+ÀDÿrú¬á÷~ùÆáÜ‘o 07›ï2:ò󗎾÷&C¼ÃàÓÏᾪ!QÜŽµøÆ‡>ç?óJï{å³â,©ùJ/™pæ\VŠvÿ`¡Ü'ð ñ}cxÝù)­îyÆSâWìO+c‡§q;Ñq›Ìï@~µ;ÉÏõ]—á9æžFl±Õ‡@ÚÂv3ÃÞv|˜Ü§ûøv}oÉßfíõM{ ¶ë'šâ ®_ĺ‚í7à89hÉ[/¨*Öm7ÆÈ½¸v­ÑAþŸõ]ÈQ±>™ÏohfŸÁºç ¨Í?g}$aû®å¡ïk~Œcf]?|Øfðß·éòÊÿ©ÖOºØ;®¯írÇŽ¦1þðp}ó²:îYbp°€{`½¼ë5—áÛ!>÷PÜA<—ó Œ³¬A-zÇ1µüµ½•Ï×*yññVocZK䘆{á98òm:¿GÆ÷ìãóÍõ‰Uðßäoó²> ºú}Õɵ°ý*¬]ÇÆÊúß²…÷hûqðÌé/c›`/aß1>¶Ÿ©/i„ÇI· g;žëw—ú—Ï5¾µ4Pvs}Ã_M}œç‚õ/®é)ì7Eÿ†ì±Ô+8·gÌh7ëÊñŸsvEj«é|>ÎúŸÓ¥ùñYÄUxÑ‚0w¯R­â¥Ü_ˆ3~ø¶k,-Læ×“ß/épö mžjÿ÷Uá¬.¸÷#Ö;>ÇÑowìÉÜì|¿hƒ­¿gœ‚>&ö?ü'çÍ‹#O†û$=z¼7Ø“":„ØcØÀO“oõø›#VýU¯ìl×vpˆFï~IåõÌ{èl›,SЗfK×›:|&Œ ö°|¦Åh·¹†woj³Á¥R#‘=ìØmý%ŸŸô°ö«F|ºn<ÆËœÊ\¿†u]G£sH¬O‰O3Þªÿb½9öx[ºðÒivMß«Ÿæ´6µcæ>³v×½o:Þ²},öX ?µomýÔ€+î},}gó´lWç£Mþ.Þé]—?wûýô?ø‹™~ê¢YØ„Û=ç•£/ÿáW†£‡½µICت÷59|ÚOΜñdzÄS¹ÓÈüh¬·ž c‰µ%ŽgE ñÒ.®\3ÚÕõ Ga­r­zxrÉ©i…çtŸôÖû: öäIoRÇ2¥5–¹6á—F_SùCå×úêÏD^|XìøÂ®)+³7d{壿0ŽOüÈ=çZ¿ÇR±…ê Åy™åo¥?¼”ø:þiâžVZ ½&ühê9V÷çÒV,¹Îñ`ß_hx^ü©ã`¹Žº±ö4}¥äåSg=dëßÖŽy>c®`#cøù‚Í&~¯¬ýÑ‚`ûS§N@¼0ÕÉšÇ`‡}ƒó òö9Ù’©®ÿ Öƒzy«÷BÎhì üœÖß,­“Lî®õ}Úôo6IßZò°;Àˆí£·ö¹½FØ¢Šö·zqO&ö¦ rÝ} ûâÆ.Éýãó¹o-ÏJ×8rínãíP#ßKï¡Ä¯é}Œë;ÿÔ?ޝ=þg¸ÿªz—$‹õ‹ç±FBk¡‘6¦4Ÿð>êMºÔ&öÅw°¿a[°®ó´®!+ƒsâ÷x~Ìþ´††×ìQò“0÷®nu¯“ªÔH,°gç"ÇÇüˆÛÂÃá½ÒŸØy®ö û ë>6CçÓáh„–ÒQi[ÕF°n´¶Ý²y¢Loaë¼l×,†65ÇcƒßGÙ9üÇõiFã³¹è»ög#l_4®Ç®;4·€ö÷Çü!×æÞ Óg»ƒx{‡ÚhÐg|Ææàâ÷î¡ÌõëúobÒwm™#€  ¾ßXü~÷‡ºÞó}páÇŠõq¶BŸyØãZ¥ü0r¨{œUõ¶ª¹ðþæó»7ç×37Ò“Ä]aƒÃC2/'\WÆPøÎÄ–¾»}lîbøã¨ñcmR­\ýùØï­{µªýœ‹~Ÿç®m­ÚfÇØéÙ&lîղ릺þlÀ‘ñ›èî²NKŸý‹Ï¶|ë¼[é}LŽWzWK…:Ü¥ó@X Îñ¬·âO¨?g4ÜÅžÏÙÙZ“‚ùqؤ‰Š3Êy³*øyBôGŸõÎãŒïü¨×¬˜½jíßÿõ5Ã7|ÿv«–ï± M þýOÎà·÷½áÝW?v³ WŸüµ?¼ï•ÿ‚¼þcþòÿó;+Ø«ðÞóû?¢f›ú1æúxÅóþ|?çøæ:blŒt‚ºâÇMô*ŸYÓáÈãÜùÅÛï6HÅØIûóµù1•{-rmªÎRq’úŸ­‡Ú¸¥ÝA~ìxô‘¢qçø˜¹¼‹k4ëù%ÈÞø½¹&æR›ç,´±auçÿ‚j ˜CC~I>Þb}Ù¢Ãä5Á‡¶¼8-íÎõ&±8ømˆµ÷”s~ ;½aa‚ܼvz‡¿wx,½9`;¹†e:5t°1®=hÕ{t65Òµóϵ¹Ù­õIª…®÷õ¬ñ¼¹Â%cÜ]î[­ciú~Òêl»š5Ì•9´k?ŒÿÕî¿g-Ô"rïàœÇüõÅÍn¶4i{ø}È¿¦vSó:Å÷ÁÞÀøëÙ¤¹e|‡{ç ~ï>zî{(þŸãè³Òîa~ÒÃG±­z|!‡©\;1áö˜µ'qH'ì§œ¼cê«â'ÈÖn0¿}°àˆŠ…¨ëÚJaÎëƒ*/‡ñ³~’kz¥û@2–‡„õ ß}ì¾"[X0kZׯ›^j2÷xضðýwúÙ¹±ÿÏ‹ Žˆsx_×´Vñ«•ëÕÆ/ýéöÕ÷¨µs$°óÁgÂÏñ^£ö;Æ×úÌ«¨Æ\¹BØ`Šø}Ÿ½q_¨öx71<ÌG8Gcö»¶ ÏÃA•´ä® vž>‘…üËóSã¸]ǦÖZ¿”1\ú‡ã2–†,j7ˆ±¸¿k1áþTx6¼'Î_|†õåsyŸ¥µõxÖÇG NŠy‚ýÆø™ã˜ÜótbiäȰÿÍÁr <—va¿£’ÖkRÏõgÞ!Ç߸Sžg_\àV5ª×Y7ºây@Œp»6¿v}Fã½Øº?G¸õÁÕˆ¹ïAn}ôÁƒß»ïP«Þäò ÑfëUìkáÞòµµå¹JîÏÛÝá$Ö¿¦oŠÏǬż®Ì§Öç:Î.¯í:ü ùa½FºN×£îh8œErPÎÿ”ÁF°’Geoºk¯w9zô=ñ/›áÓþó¦«Ïÿ³·®lÜõ̬ÍÍëþü·¾¸ ýÿ6ï?:ï!Fwøö¯ éÇø÷´'=t~}¸HØ{âN|Ÿkf椿ÁϼÚý9ÃQn+NÂ%À˜ZÕWîæ>ÁØ#¾ï‹WÂkÕu2 ë‡.‹‹Mÿp9ü ¬?é:±Ö«U.ÿxW‚sÐóÆ3%=üŒo1ÎtÝEcúWщ˜°v„ãÉ&þ£m2c0œùÎ;7îjþqð&y)üÆ}vÊpDú¦¤êlÓ¿ÂgT™8Ýv¾‹ùÕÊyâÂç,ñFø æ7V©ÃÇû™K\˜¿K~ŒñyÆ÷Â|—JûМ¿äŒ{Ñ!~“ÖÄz4Pšà¶ˆ¹PgÑWÖ&ø!pEçBÀÑìøå¶ct“ð}å–­ÏÖsoGi'ø‰FpýCÍëÑà+…µÀêÄ@áÿF/XütõÞËŸðp]Ìô'ÔÿÎ÷(£Y›:ôïVl4IÝå"Së-úÖÆ¶qÏîs÷p›Æz0G¼QÎBõx8Ç”Ÿb|Ë?áh­‹ 뺄Úg#릜om¬¥Ù„¿à³=g¹xÆK“ãl¬^šeü‹5ÜÄÉ‘×Âý°†ðþ:CÔcû¸<ÞÇû¥v {6 ûÛZ;¥ç¯T¯¤3x­øœ}Ö²=/Üsôçji|p-•¶Uuù¾]—Ș|s­p|Ž=Èçýè%_ÃßãÝÜ·ö¿ñií7ÆqØ¿ø˜{Î!>vmZò#ÔÆ\›#ÓÕ_@ Éø¯Û«cçE´ƒnþè;‚‡_“«ø¶fsÜ©MŒ½`o52ÿ¤ã/ƒ0fŸ÷ÓZsªhû¢ý$\¸WªÌ÷i?Í;ðú8·J=’×Ç:9|øMæs}»~0kŽu•ÑêvýuœbÝc´ÆÌµeþaA}‚»º?Ü+øFâ_Ì…tEeËpÎa=„CƒùpÉÒyúéö3˜ÛÅç>é'aßø .„\R©ö‹Vy¾ÂñwéýWŠO¾aþЖŽÆ7ºl¨rÜ˵‹}õ‰ñ‚¯ ®xò#ÁÕ“àkGÒ‡ãæ\0ë ûêå·mý©ïžyð}Üw¶í»/—}ÂÞ÷»/›Á|Ýnýa«?»Á;VôÝvækïþüÚ­GW­xÁíg7ϱ!ÆãÌ÷žº²ëž¼r“G¾m¸y­nŽóêü7×ξ‹|=Ç¡ðú½kçð¼õ†ÿí§€Ñ™D¿Þ¸8êÁè‹¢O ó‹ur¨ØûˆIawc·¦ï‚½½Vü‰8uqÖ! ~Ææ­óÛµ{LÔÆ‰9öX;Â-ÉMsüÞû»RÂÅÄPÒNØô/ᆃýEæÖ‚ßH³–šWËŠc²nX´Ë#„ÿ¬º¡Ó’÷m¥Sy®÷~êÔ¯u}èR°éJœ¶É2çÞ ûk71¹k>§S¯Šgƒoáø¸pÍli.Q%Özz×uú¦x†è¢boªwÑþŽÀ=­ñnø|ËvMÙO?ýdâ_xFë1-‹cÇ>1Ögß°®v ±¿m&ÏAÌxÞ©÷¬Ìñ€ÝãüC¢ïšYç¹q|þ›WÝrZ±5µ¸\ÿ~.üÐQôáÇÖží³Nà"Î9ÆGºó]þüë¿#~±Îh§Ï|Üœæ§:½º^=Î1¬/kušmÀxÄEÝÏõ+ 6=}ÖÑPŒ®’±åpŸ…pƒÌ©Fâ„u§–’ÃåüÀ÷I/Úm5ðäcí»õã[5²ÐžªÈ{ST7ne~¯žé»9¿Éï÷¥ßBü'ý·q6ØwþÏÿ& í+[ó[¢Âø˜ÆŸã]ñüéa`þTåú\Ú‡­¸WùUã ÌM¤O˜qÁZ~Ð^¾?ìöRrÎÑÑ3†ÄþßÙ3?Ì/þÄøÁLnÍ9ÑŽÃæzr¿ßF´ÿ+ïoò£`÷ñ|öã²}&Ú'õÏNì5Ÿ‡ª?EÜ¡8˜v£ŽÎ¥û­:ÿÅ߇·Ïy<[}ó˜1Oº4ç£4FÀõ‰½oŒgÚõ®óé1ÖÂøás¬9ûEü ö@ºäÈ´:·¥aéhôñüÊÝuqϲ¹6äÔ nÁïSÛ/¿g'ù(ö[i; Ó1ÿ#>T)ŽêR+M é3lÓ–"¾/ü¤ÔŒc]Šg¸›õÍ#Ü gŽ9’å.÷„PßήF)¼…"uOŠ{Ž3°M6ÿl/}Lä=­íí¾;È£×:Û+ÎýÐ_ ï©<:5ìpÑ+8{9zöÝý©7šyìϱúµÛ}öÀMïzù·›/ ¯Z=8ó€o=` gÕ ß÷•Á/Þ~·•÷¼îC3ƒ}7Y»õËÞ=øéþs†oýî?Pßë1ï_NŸµ²ðÅÅá„5 7}çÕ Û.è°.ÜuøAÖ.á:ÅyôœÞ=ƒKÄó×{úHï5oí‘9Ô„´é³ ÝŸ6Üa`ØÏÆYã­újò¿Âƒ£>—ruOjs.™CYå Pîç¨û®ªöâ˜ú»1nvò®Æ­¼Ö[^`],×Ô–ª5Xjbƒ]ŸNÿN:®SôC€í©FöÒ6yaçЛä°ûê¿’ú.Ú—pìc›ïÖcß‹ë»6кWª-M}0|äøƦèGZK…üœñá,°oëä×Ùò÷Î_R{Ã8ø*Ä€áª1sn¥|Äëº3¾µ5KùóêC2f=à¹w;ØPb¾>5 £‹{4uÝep÷Xj·êÿèƒGF÷Ççð„=K7s!ØÊ-}tñ¿’+:›}i.nSßås¾8EþæÏý@SWVš§D}aœÁöùYÖž1:žñ¿z©3¢—ÆíR¥Þ©ç¦Wîtrc÷PûÉfߌF:ÌådžçÇïÓ¾¯þ@Ä'ÃÁN»ºù«½ø»Uò“ò¡–šôDLŽñÅ=¢ ì\I§ä¼KëÜji^]©š£+É}«Ïr ûCµs[ëƒëJnï‹ùsn»MÝnrix`âI/&žø½6VOÅü•ŽÿeÀôméò» N–b@\_µUoNü]öÝûã+ýê}äPlq©ÍOýmäŰþÂoñ¹Ë1˜IÛk7Ϩ̙ùßôíqVá÷Â/c±ï3ßçö+|°ÔFƒØ5ƒÄ°ÒËa¾Y§vÈgÇËñYJ[em>îÛHóüÉ!¦Žµç^%MÖ^òÚêû¶—1Þ–6 u™ …߀ç¾ü#¦pê]Cn^iŽíŒkçkóÏ:l§ï<æ¶cµ_Æu‚1 Æ>T£<–ú“¦&FÅwp½ôÎÀùƒýšÞ?Ì£r,ëÆqKcL­çè¾CÒ ÃúeÛǸ:5QØÿé=ñã}{c;ý?/|ühõ¿ú³úò‹fpï¿^{ëðzÓÁWNý÷ô!8zÞó©Ñ%c##s9ÿêc²ÝÒ&zîX³8¿°÷•¿ítÜð.ø¹!ë ™#¬¸ëãç¼¹6Ww}çUÓ ¿ q'ÖŸz8޶:õMÄÉ›äø$?áþ¸¿5WÊÔà·ÎY/Î5¡šÔ$ä 4ΙÞ@u>³w™Kж~¯þ Ônp-yò­üý9®sÇe…ÏuæNñþÑñçcGj½X¿%_ø(uÊûæÇªËâ4Ö¬ãòÚý…ûË6ÂÁ§ZÕò±¶°²®óø{0[åfÕ.v~4ö,~¯õ—¸Fþ„uî¦q3öòyqەʱxí=ÅóO5ÊëÔxH|éú+Æ8ðsâÓ¦Ö¸€}ýÖv¦«ßÅ^‚o<¦ÇU4™Ó.¬5Ñ@û ÷Ã9%­GÖxÔŽ#[û%´™®Á/·ò \3­ãÌÆ\×2u@æNÓWÇw¥ÑÙ³n…0𪮡>bÅõïÖ<ßð,ŠÔ“~LêÆ`ÆgNŽpÚ6±X—óÂù§þgªýÏø'.‡ýwÿ²®v\8Ã%­¹¶ãÄ·ñ_•œ•óî­°Ò{_$›g¼ë#º8ŸÔWó|úôéÑîgðûôÔ§v¾5>ÞèwW¶æÈ°ÆÔÀ&\½ÄÕ8'Ôã‚5†­ù …1éBÌqú3Òá·–$ÇŸX³s‰Ä¥Ò§F>/çªIÿFìÛôÇ3†É93¼ÓÿHýÖeúû%÷n߸t\ZÙ7ê8üÑ€Ï`ÿµQ:k÷ ÷6(Í»å\_tì®uŸÞë>û’vÙý˜“Ž¢æwì8q ןkÝ/‡q¡±øÔ3öÎGr‘®(SŸˆ1Ãõ­u—ü ùûÀÌíë¶æò¦ÿê#ȭ³ÂNHƒáhǯë«F›ëWçˆ46£!|t¸&Öì{êÃSk"í³£´ê¤øgеÞèà¼U}ÒT¸‹Ì À>j?(Þ /Í{—öó˜t~Zõ·éEŸ›gÎrŒ‰jŸQ…›à§Å3 örLnNûr-b]›êzW¤ŽOýU¥Qg¼£¶n­ôyÑ`ìoë+áMåþ.ô/Y×2œœù>?[×À-'·wô|Vªó>Ùvœ¼r¾§ëmþ(ϬYŒ‘u4œóeš2öÁ}kùüxo÷v öHŸöót{GnÏïcÝI£ðÜ.o6vm.ÎisÇ2—ÁŽŒ==ƒµ°uΫ§˜ãô}U°]|î¼}§oc–ãëþWÑö Κ§ÖZ«Á§hŸ°þ¼‡;¾§÷-í¼}ö.ñZ0>+ýbÇÎ¥jÀE£§´Îü>öŒûôUÁ~ÂñÁب÷„ægÁõÑ’·îñKç%yö›OÂõí>ûD}¾Ÿy(v]ê¬ÃíXòª±?0æ©Ö«ó ó‹8Óþ ¹y¾û8½òff1zêÞüóWÿöÃß\½óüŸÌÞíM__ÁÜþò'·šÝÎ8€›ßY9ýc·Ÿ¹ÿóêg=òóÙ_ä¹½ûÙ+.aÜ’ëì»g^ö!Î ç ɽپ6˲wI›81Øn°)\µ)8/€í©ó°¹Å¬…ÿàþ´g¸6®Ó$Œyë+W½M»³ÇõŒçHM’µ!­Í) I<Ÿîß+£óÝ7å לûX‹xŽq§_²Øõ¨Æï'á~òæ·åï¿Å÷öŽ#®cV{^:;цÀ=ÌckeS–RßC»~Î ó¹XŸÓ7—²¯Ú¢F½­{ô³ƒ=9·Íyކ³5B»œ€m+ãÓÄỬIáþÔìQƒ¼þÜÐkz[C°6Ï’ï‡õëØ¤ sõßwŒÌC`œÖ7Àyð^˜SPþQ>³õ©3h­êFû#Ú£SMÎ`|ÿ\óÄÜÔrÎ\óƒu`jçhù<ãÁKÁûaãý?O5ædÕášÇj}×)röðoÀöq.F0fœÛxëX2 ‡Ù¾Fj%YãÝ”p/ä·îdþ^úSâ °f;Î%¬ëôAÝÒ¨Pn_¼ƶ̯¸v§Ó¨ÆºÃ:¾§ükÞ—S+!¿v£oÕ:7ÖD'χwÆõñŸü‹]¦4YOû*}Ãyóóç¸>q®¸æÚõW|þðIéï±ÿwæ—¾­ë4i&÷ˆ­GwòéfÓþcß»á8$n•~•±§6¹Ã±õÝÌ5#'ã‹÷7¦íý3Éu‰ucÅ6 Iã;'i—Q³;Úμ–ì k«¸1?xFçº:»†u‚ù7Ç¥³˜5ü~GƦoý+ñ£®l£÷ _0q¨{¾qü€¡¨×#s-]œw€:À—ÿ—𩃬…ÏÓÏåúÚåžfŽ£Jçd¨ƒ}Ÿh¤öÄ>Wë¼3Ç5~‚k~›Ø©¾{9ÇÔéO¾˜SS }ÀõS߉÷RÝÔ‘ðOlW‰wµÑêê³'Ý9´{8ÔÓ‹¹Î%Æsñ>gatõkñÑÙÓ·~F"í÷–~ÇVü>×ÀcÀˆ·°Óùíë þ›óga7¹~»µß¾és×~vƒÛ­]ºö¾Ùæ³KÓO_½é_1ƒg{íg¯\tâ>«gÿ}åy÷xÍÌó×ÿbæïïtbôÝÏßùýÉ›¿h°O à# ξÙʦ¿0zÊãÏH‰Ú&ÌSb-ŒvÛrïˆaPc…1±.*ã é‘O÷ÃgÎÏw±iê‡áÿ‿+GI;ËýŽëáìs.Áøšìƒùa¼æÐµÛ<›­ g ‚ù6uÖÎEÒùì$¿%ÌøaÁºÆ2ÔÞi¤ó½#}ž Å™½ôß#G*þC°uõ²=HüË9úÒu-Ñ;mÜ‹ŸÛ÷á}lƒ¨#ŠgvzãØ$ùoúטcœ©Ï ™ùqì/Ø0Õ-5îK?XÚ2º¿k-ÒC„x8¨°!ÑÏOÿ… ØÏ…Z欖¥q ž˜ŸÅÏÜz šæ‹Ú±õgT{(5$æŠq¾d'´?1ÎðÑÅo~ãNìcqËZ›sÞõŽÄ6ebÄ/Î¥¶¯Hþ×CüŸÆ¹ˆ26èØ¶þÊüÞ±m-S~ŽøQÚnÒÌÈùÒ·>ð¬MŸ9ó—Ìa=#ï%üü´ø ¥ë'KçYÉaƒ}Hï`²Së¥{Ì´±ÕâìN,Æö]3æ yF„##æk/:ZÁWD§2xc8ïÆØ™ÿ1‡©Ní…ÏÂ2û_¾¨ætË×ß` gnw«œÅT4N¹þaó’Kè[Ÿ/ñ!¾‹ñÅ^´^Ún[Ã8 óoêÙÕWÖထä“â?8Ϭ~ÀÑÈH®¶"X,Ö?ðØÖpÔ¢±[$mØã<#Öæg4ÖŸ¯Ä=׺ÌÛ”^«Ô?ÃÙgîŸ}B½Š:}¼>·¦Ör8žÆz¢M»§ºÔãéã{0?§ó•ü„î=+™_r1÷7Æ/ÜÌSzÂÇ0'ªŒÇ:•ÆÄi­|!­©ðüðwŒÆÞë/5x…cEòC0÷ªÃç0>.æY=rÎÍú)UÏÐé¼Õá_`ý;çÒÕ^ÚŽV®!u BŽ}åOâ‹XÁ]ƒÃãûm~VaŽW[ üÒzF¾m8*ØË齉5žu?¶> ümÕpìç³™;R§vcÇ×ÎZøüÏ Ÿµò´ÇW³;îv£Ùç¾àu£“ÏøððíwºÁ*ì { Œnðý ¿ñ§b ^ò…Wþå7‡Ò˜¦îEù/.Œnþè;“߇9tN¾òÚ#‡ÕÜ^rÜ­3RIß™óÏý‡±N€ïНÂÚ¼2>Þ}3† 7ïsÿqgÌ"·-¯Üx¢®‡øjcÉ‹GüÛià Æã~ÂÖÆÇk+ý«­ñ”}ÅûàÝÌaFSØŸ#¿àþ…ÄÙ¶zC7©Ot¯FÌS¥qö«þ*ô ¨_ž˜ÏiÕ©ˆ+|U=R¯ŠMLR¨—ÕÅ•yÚUßÚ%™'\ÛÚôüN8…±ø žß‘o)ÞZ¸Oxn<Þa[~±ñÂ9Pÿ¯Üï(çö´ãvæWÔÿCõ ¬á vµì¾„¬w-Nø¡µÇ8º•{™ÕæoÄ?§ýÄç®UÍüÇ`M6ÞyŒh;(^¬1vªë>·’ªz6cí½½5õÖÖŠñKÚv̯ô„ûù¦S»{åZ‹J‹«Ü·ïúTcÉ•9¤ÑP÷ø5NÁÞÔ¼G|çðSÌÕ¯Ü'ŒëcÁ=J}þÑ?Àw<>È«6®ƒHÿŽ5æ§³¶û®ÕÀøáŒ4¿¢6FÇ:2ذ¬O½Ÿê¥ ¨Ì æÖ›ôyÓÁž¶ÆR4:ƒ['¬qý]·þ‚ëJ#™´7Ï8Â\×a°O<ßÝ9@®sëKòsؼ?l|Öpro}ëŠ#~ÀŸîXÚGdœý£ÞõäB1ÿäk”Æ ùܬÕ̦×gmnNå^ÈUøgÆ:yÖamÀG‹þF¸!˜'ü{0b¯ÅôÿuŸòó+ihMºfRý!cg~źÜÿ®ÿ÷gþ ×Nfù_;+k8N‡§ßZ›–Ïkv>5Ø?»¨ÁÎN´Ž¼®æ+ÓO„/gߌcnþg-ÿuð;ñøUî=Q¸×^9¶.ÎûT¼>Æv,š+8ìãWÒϬxðžÆì³Í<ç×\ÊÊ>I‘\´sóUbž±5ìÕÚ#‡*÷:Iþˆý;RWˆ9ÂuÌ_öw–1îª9Ú›¼båÜNãí‡n6{ÞÝ~öO>üòÙkOÌ^8õÂámžtdí{oyÞ*ö÷×ÿã›Ã¿»ÛÝf~ñ GÍì¹ûÄÌÇ_ü—ƒï>ÿÉ«¯¿òßèÑÞß‹OyÏèÚÅï6×ü{úCç,^|ÊGàóãßµëä0ÞÓ‹›'#åù? ~bà)ÖÖÄz$OÅþ•ñÕBº«ìû‡:ÀÔzG;ŠùK廈ÁTÉ):~*ÍW¦ß§yØiޏtè°þ¬ÑúáÊz Äþ0Oc`·˜óº=5"ƒo-°.ZÜV¬iœ^3á‰êkr19fî»ÑšßGß1}éœÌÞå(·4ǽûè~}uxX¶k­¹‚®õŸ FÄúà°X§î{W:Íó¾ÿêÑ_šÚþègLcý¨¯+Æ{)üËà/eêç0'ê²áÚ}޵ ßÜœ½œèR«öïRrG±¿ÔcgªÜZ=úˆ»±·çñ¬o룟KÝ^ëPk`Áu—¸¾ëÝ—î(ãèðJú®ÝÂù«ºlb•ûrìas1˜côî᪾vר§[帼ËÎ~lÓ­R³ ÿÁµÃ¥ó¬ Á™ŽßÁO²¾@éMdܬ8j)gÆ‘þGúšaå|ï›ñun‰=‚•7ÛZÌÚö›óþ¨së]-¯5g[Ûûð+ÉY’ýŸëÅyòZyé,c="6‚}s¯×:ÜäÔÁ¤öÕü*öh·v Ϭ_œ/°ÓxwkûÓ÷Àù¼L3Ž“™û‹1ÇøÄÇ<àþÔ°ÃÈÁ˜wÁùÇ`£p¾ˆ¹)Øs‰ã¹ÔÜÄó›ÄœÙÏ·¾A;õåët\ØùÈîëÍÏ1WæÄ®ïaír~GKÙÊô §†.óËÖMÈøº>Xç;ƸsjÕU[~8ü8â ஄ç¼…h͹GO+å^jSJûê¥ûЧ˜ü[ßZµ˜wìsáã¿ÔñÍñØß˜'i/9þYìryø÷„ý´ï_¸>¨VÎúxìa¼åªÖçQú2àýÌ‹ä)Ï~®kW¨³S™wUŠ#³ƒë¸¦ó¡UbÊ×@Y׉þ¬õÝêŒò-‡ù9Î!õïUìbî ù[À0'êQ¼aܘñ+±!ؼüsûmâ`õ­hù¹5/*c[ýÀþ¾ÓÞïÁýæoõwà n;;óæ«®X;Zÿzõ~yÔè>ß½ýês'¾€üýϯ»`ð’/Ühøõé—¯Þö'æ_sæ×FW>kE˜Ä!êÜÜæÀ? '$ÍïõréÍ»ÉI`®ñö»A+ÖXåÞö•?~è(ygŒ«bÞ“é ãü è}¬þ1컵Óª? Öü"¬oó¹K󣈫ã90Ø¿é­kÌ3±oiþsgw}ç©ÑÃ<žãbë©Osn²”Á=×J#v©I]ÔØ:»øÏ¿…ÍîoRC5¶¾r?ª£á>¯}öÊE]ÄóØ¢42O®¯¡ š6èúû6¸-l¼óõ´xì­Ý©NÛ ç¹ãmê•­?ÕX»Šþ~oÜÒ5’ä Ñg°.okÜtÙÜËR×£¾F«Þ:êÍ’Ü}ì—8´G™Ûp^®ˆ »?&lø.k¾¤ø ïw6{Ÿ²wêï¹?à?bmH›D}ç]P'÷ƒ8\Ú7ê ”w÷8"vg Ã&˜¡Ö×@¥õvëC¸ˆÖÆHç×­óç´-Ñ€Ãß1¿Ç ¨½{‚ñœpjzDº‘×+·ô­Ÿ‰ë[§ºUìn™ú ³š«“ÛÁ^ ~ŠkÀ'4oƒ¿7:ø!lšÎÄY×!-Ò;Ò¾ùWÎç:ÇÎèâ ÔÙ`¹^¶ŒFò.ö;£ýmÔƒ—¼…ÂÜEb`˜?øìØɹ„#™ÜüO|n¿ TØy®õèwc|¬IÞ·näX½ƒSJ[™º!\÷ØKÎqý¹NÀÏ|mz׿×eýš»½X›ûÚ¨'5ͺÞa⮲—m6öµz-N§Çh8¬á~õÝ{8¹»ì1üöñ´íײ¹¡ÓŽ!*÷ElÅV's‹¹᧘ŸmÞ™úç¨V•1=ký>|Où»«è£»nÜCªtŽ¿æ9•Ú¾es$º¿ÇüŸpŒ}û¤onœŒy,ì-äÉ“»I^N¾È¹œç›äG¼×§1ÿ8³T{Ùk¥E1Õäs×´ ‡È14#:4¥úÑM6јÆûK_‚9Òh¢"Ñnq¸˜£,Å_§ü÷Fk’£Ã¾ÆùõæÜX£:›SÛøx|_¹ñyrÊS¿)ûÌ:µ§µµÞãÑÃFÞå ³{pÞ>ígßY»èg_þä¯Þèòÿ¾õ=Ö^píg/ùì­ö<ùnÅ,æçìKÞ¿zûÓo°ööï|gu3Þ]}ôy?üÃëjfs~Øÿ÷Èãï8|î ^7Üý¥ ïò°nsÎïŽÐ ð÷f>;07wô­÷¼gô§þñȺ¨àÈCç?s1kï>@ζs{úM&FƸ†eϹf›øúð¤S9YI ò`¥ø¾Ö÷)]_ÙDŸa«~‰¶á÷g¶¶±”äm±–¬á¾ Ž!¹¦ap-ÏQiÿ”¹Büg™õç å…;äv-ó7Îm–Ñ‹…]ÆçáçH§–}®Ëø\Ö'‰þö´bÌYâl8[ñ™|ÆMÎjiãPWŽg˜¹;8ÿø.Öàù…3ÖüØÚýÁšpTú¬íÝ Ž¤är­ý€Øžéh-áÚð…S[/}V7Ö&mÌÏäÞrþªIO<®ÍœÄÑÅÁ÷¬Ûdm;ùá—bÞ1ª•YÚ¦I.mËÒÜk­ý×Õh¨¾ôJÎMúÃ…ã-Þݹ<[2ÎÍQß÷Áo`Ÿóþ×óÿ‚a`­ãÝÕƒâü6ghÎBkQ·®OìúKNPc|£0ç¡MþÙø ÷¼ý rZ°~0–~·f[.‡ï…1Œ u$<[®aÇ$á×¥oOcŽÇ÷ðùZ ƒ^o\Bß7úôöíxù±ìë׆ë}"c¦¬WÅÚ„ ¾Œ?g ®½ §2¾Å‡ü>>³‘ë³9GéÑÈX:¼´m5h¥{³µ™‡-i*þÇm‹Ÿ îoô±ö`7¢oiL®QœBüœcyT~[u!™çôüÅùášÞ2u:xfkÒ6”6©Ï–"š¦{ñ—¥ËŽÐØÚÑ9?£gn9}êÍ)·¢¾™j[ŸdMêxqŸ[Ù¥è¶i‡y¦áÃ’Ïð'Ö§ç£èw:P½F=¶5¶uòFZÇëû2ÓÎþ²è=½Ã=öÎ7N'\dÌZúƒá$´ÁµÄ`M7û7z,Âl¬{Büì÷¯ÄZßÂØÂÞ&öÉc@þùSÖ•ÙÝúŒ§íD\5ï~÷?Ö·¶p®ôµW±¯žÚäÑbœlS*sÀøÎ°{gþÅÛÖ0.§œU®<ìEÿ°r‹oÞí9øÄ*ü§ßºÓ;V®7}ŸUÄäà9OÞüE£Ÿ~ê?Vz¯|b~œqíÎ[eÿµqÏ ¨ñÃÚÖ<|=ç­ÈO•Ný‰ú>x&ਙ?ÝSîSð뼯»úŽØk¢1‘úM×W§Ïó+˜Oç/Kk(všâʱv£’¦ˆ´Q•G›r^NZn^“¥ê+÷&Žä¿»‡ZmlgΫÄ0ð5[5©ÿA|Œÿ®þ'|>öRGW— !µMÆ ËàΈu0Žø»uù]ðã‚åý»òsÌæã˜ütjœpàbðÿÉ7R"Œîó$Z¶ÑN1ø;ðC£ÕŒ9 f™ÏÇXšvMz`âwªkÝ(^|Êöjó{9ÇŽq ŒK³vŸáÿ;oÄ^CƾYߨµìEðó>óÊê¡èçcLšiŽqù§k›ékŸÏ˜ªF\+` ø½x ìÁV¦ÆA<ÎýÑ¥àuðÌ®eeüç¼_°†¼ß c‡¼gxDÛûëâsÌ p{<bŸ]µ}qúÛXx.|¾`],ã°Û]ï8?gb(ŽÖ?pÛ±tâ»þ¸¶õæKiiœçŽ®D£†õÒ¼òð‚á‰[rRë8‰ó$ý½NŸ V kQˆ«JÛu­ëÿŽ ò:>ç+ŸA´ýÉãnÆÔ Çüâ,PHÖùrÝ"oç}oN׺¹á÷¯\KUŸK¿‹cc^"óÃç_ ï¡Âz›Ìëa^¡c^©k‰¹W“7Æ>¶Gjñ kÃòy‘ŸÄóšÏŸ<8îü.öaòß®¡f94ìi|Ž÷†CâN'³ ÆUÜó•ð‘Ó˜ßÆ½À#ôšqls„ùÿì+ØYŸ;Mrxxå5õ9æ<þïû Ÿ[™ ­ë¯nõBŒ_×Qÿ/|FÓ¶ã=±lÛR‡ÃüžkV¸Î”ž‹vo™ 1«Êþi™9Æ50þ¸®ï½WÚ6ÿr®ÒýKÒ7#“æM9?!nvø½¸¶{§–Ò¢f?“Ô0‹‰®Bêp]£>Ϻ×m.› Ï=cü¨F]Î7üðKk°É÷zÓ‹gßyñÚìÇZûÑY7^Cß“~{iö_O;²Š¹º÷ßnæ’?=83ú›?Z}ÝýËÊ;ßwùàQOÜ7- `qïÙkF“7ÿÏÑmÎù‡ÁàÓÏF¯€jêm?ýòµ/Yÿ§Âyó¥“nNí_¯]r‡Ìã`?ØìCü´[áÛáß¶ò×ÂÚp}íÙ#MòçØ£ö¿PW×q pÇ~æ¸l¸Ò ®Ỹj*˜+`~ ¶Õ<-òø½ß¹‡ak°'`{àgã|sýE-Ìv'û2ãïò“WÂ(—O¨nä:âk ê!í‹66 > |GÄ'é?’wF¹AüÓ¾?ý¬EŒækÒ|ÛF½ ˆ”Ö¢Í cµ‹ÿ/;º£Š6U_šŒÄ‚ÃO†í4·~±õ]˜Gq}2׬ën¸¿1¶æœ…ßU™wKû»kKÿš1¼s¾ü=ìÈ„µQìñžñÛ­B\ßurΤ`§È¥—.mÛ û6ÄEpN—_Ž¥{η®¹­ä»ˆ¶ }B×Jo@ks›7qnÖSïÄs&Ÿ;'Êœ´¹ÕÌÿ;§Q›#â³O}’ÿÞ†±ÑÆu­ïS'Æß­‡Þ·áú¨å_ìççªýf/øÆuÊÄFSŸ†óó‹ñ×y&ÜsìÇh³÷ þý¦Ô—‚»¬ûu¶·g˜»¢*çžð¹©ÒÜ:Æ0æwNÂ: oõ{g-mÆóÑÇ1§« æq@=~»þÄXcÕ2ÿ¿Ëy® ×b‘ã¾KÌ#º–§ëaÿ¥NÝ…z,UÛjØ*ã‚Ì`lÒßë͹޹Í7f¬‡õ½—ú6&Ø÷¬[ø;×9í<í|ç4Zó_“_áúÄÙ­ñ;Úˆÿ þæGTÒüZäš”ÜKŽ«ToÆ—ó÷ÎV}k¿·zQW©MÅx8¯Ž/}n×°§¯kc |'Œ Ï÷ÿæÑÀ÷ë'ÝîºÁï=òuÃK>ö×v>í¯ÖÎ9ó Ão_qÏÕ?¸×)èùK=‹+ÿÏ™ƒ/]òœÕwÿËSVa_îðí_€ ô»ÇXþøG߆­„[ó—Óg ”›àÜñLæL öÅ}yx&©˜5 ´}_ŸÝ©‹`~ ØŒuZsâ×u5ô¶Ù¥ëc[X«nZ53Öa™·þ0|Ûä 훸çx/÷Ó[×~U­x6wücä­„A©·ÝùýâàcM{à:ßp»ºº#ìqéêî…ŽÀ ùóꇒæúüEÔ—ÓvH³’zeôuT7mÁøÆÔÇ3¯2þžããÖ©E^{Ógc4 cƒ<Ç\“P¹†š÷_p«1{sÆ>²áGô­½çؘy ùÎ]}[c-iÖáþx>asážÕŽ­Z¥K•öó˜…÷îÕºÿi´‡ZããÄY0¾Ñètþ» 7ÏÛìR}TÄkè[C÷sÏ™6(}óN̨ñ9μt¶zK.c¨x½2ÖoÿS}(¥_±³Uý‡ÞÍõŸ´È;c `ý¦gv|GŒQ¸Ò!:-±S껨Ÿ§º¼ó«¬ÍphñÿÍ«ðçâfaŒ|-ìÖý—;n–x]¬CÿäÖÈsÄþu—.ªl"ì£ãêΆHÓ•ý]Ïî¾ÎWõ5>ÚH'è„mÏrh­«Ï³+}©«ÒO„}JQ°“Ôø`ŸÀ>a½aýÝóNù}æW¨_ég´ï"ˆ¹SÔÆw]~5vß)Ç^쥖^“Úk9‡³»ŠæƒjŸæ­Ë¬Üªëž˜Ÿ‚ï”óÁõŒ‰a?¬ ÎëÛ/%Çó žEÇÕ,†‹ÂÜ‚µù|ðý×4žO®¬s¼÷f<‹åÒ¾€køÉ¿iÁôû2þP?CáïÁè`+¢ÿ1ÖÿßÇ8R¸ÍŽ6جæà8ó,¨¥7f]ؾ,ÛW¥Íÿμ‰2Üè½aNÿõVošÅ÷õÃÝkw}îùkw|ç/Ö^øëo¯=áÕßY}çmú{>òðçÍ`]Ü|meó,_ýeýÅÕß;÷Ó«=ùŠ•Snóï3žö¥b‰¯^÷˜ÁoÖÿnp¿wn®ñï´ °–1?ø<øPbL\ 빜Ûî/SÇÏÆ»Á?æÆ7ø Þccl¤tî½±eœõX,}õ`?°~¢/€ý‹ØU|·µÒ¹¾Rñºò¨ X`OíäW§š|Žÿ{/|³ë/™:Û*}Î\Ã]¦>ÕyÜ®vÑyÂÒÂÂøp©¹9D~8ÌÈ-š_èü‘ü„è®»O-÷Ob?Œ%Î{|ž­ó‡îã´Ñ·S¿¾õôÎ G¨Ó¯MšÛÑ.È:ÁÜÙ–Vᘿ]Š¥úiå]öâÇC8ýë·Ûƒ}øãû¬OÿÐãgþûIgÎüòœ#k—œý«á3xÊÊ‘á fñ,|Ý;:gzxí/n8<ëÿœ¿ºù<ƒ õYÁóZç±…_¸6ÿø†8û~rä3ð?èÿºŽ¾F@Œ1þ?ü<ñ »¿ÅšÂxaìîÏ; ÄÑQ] Þ'½ ]A-.¯ñÚµ¨g¬¼â¬5ÕÄ^eœ‹}`¸7QI£v7Çó‡GÞ{ ¶/ø_òÏø;öæŸ{\§…Ïq?5® ¯¬ÑÄs-~8ž1g£9Ã…÷µûxõºúècÒ‡(“¿“=ØMÛŒñHÕ6÷;Æþ âØœß‰q]ÃÃÏ?öÌ“Nq8Þ»¨Ñ-9;é³m7¸>Ç,üx×1‚±ÇúÆ;áùÒ=\e<ŸêÜwõnÎ1‡ÁÞµ¾@ë\oáø“óã…*ýcððk0>òCÄE,ì“×6–CÛˆñuQj·›äïqvãt–N%÷ؘ'ż·zG«VÉN¨ÿxpcÕ<jì_ÑŽ`ü0ð?°w1¶Žýjù-¬£f~ï`¾>°¥ÏËÿ€-¹·E›ükzÀáýëfýÛ66é`.m¯|ÕlÕw_G}ø[òW7jëVóžòÅIJ£=uìüKm ‰þ©çÌùaa O¬¿û³¯äûGÛB¨<ãÛmu¦5ò‰˜C÷I4Ç|)Ú5¸V -ÕîH=ýœXC;Õeª6ÑŸ×Ê/VÒ8£Ãxa@#’þ™ûf0fpœG[¦úpÖæ¦¨v¯.~ޱÿêùoÃ?r®ƒØÎàmy¾hÜSKÆúɾ8¶öGß½â‘wtlQø;µ1bè¤@»Òu‘[½ÁÌ×'9ÊàÄ©oÄó;W]àìÃøªç—ðûÔÂ`aÿÒÛÁü¨NÏñwíú´y´  Ôc<9rKr(\¯xw¬±`áøôÝk¶W¹³^‡{ØÏ!¿öË ^7¹£>kÊOîrÔø»0 æ!–³?1?ˆ/p­Ô®ºÏwütú8 ;yžˆ3ÇQu‚;Z×0¤F§ÑÞü>óTÎ%Ù¿®]€í¬Ü‹®±H¡œÝuM4j°æ]—ÜXoˆùZ¼ÿ9÷züÌãË–þk­Z¸ßå×î}öÌñ/ýxíÞûþÚž?zúìߌÿùò†ã/®žÚ»ëÚ‘Ÿ¿tøÔoþóêÓþæÃ«øþé3àbM§gÿäÃCô¼æWÏxÚc†›ës´:yÎk1ÿc¾3Âúý‡¯¼q´¹wGX¿Ð 2÷dÓWxð>tßk°†!1Û Öµ€Ñ .ƒkÊÇœMMr•šØMþY§]eÞ,ë“íVÉ]àÿ»70µ±ð;sdšè¶`Œq^®S¿’*çâ5ÖÖÆ}ÔÛMøOjûæ¾ÃN!öÀo¼¾cæµq&ê(cá{Á¦­ÛÇü¿ãJÚˆhnÛO-ä/UÁÿÜ»5º`¬ÿÁócެџt}héþáŽÑdú¬‹¡Öm3žo·Žþ‹ûp$§U ÿ§Çü úAî½—>óEê[¤©zÿ*œ\?ø<Þ;ì kæù9ÑI¸€½f£'æQŒFþçÇÜáŽ; øc/ÇqIß:ÞÎÔš[õAr\Y87_‹¿q48e­õÓëÞ/ûßíÿ޽nˆò+óæÍ¥F6>Wo)ê~ؾK§ç¦9ÓÑhŸu5á¸orɉ}6”ê©u„ÜÜGùSñUa+üï¨a§ˆgÂXI“ý‰+açWÿ»É\§­¦|)9uµj)÷’#Œûiý,rô*ÔÿožÏ‡F_¿ñW\cتðíÈ9¤þ´ù‡‡ RƒœÿŽùDŽ 82ò´î`.<÷!â²Ä/ ìyÍÜû²ü¿½¥}éÒ:+Ë>‡–SÇ…Ï_9Wۤǎqrì#Ž5t> T/wjY“_ƒuŸóØCÂ;“»lÝTÖ±;î­åN«~ö­ÞùüÄ÷ÓÊ- ãs k4|.-'¾Äû¡¶ÿ¶yuõ!Žoh7Ñc ×”N5MÚè¿âÜ“¾ÍTéú;ó¢ƒ9omLb‹^[ì;KŸþräîgÝŠw¬ç'â§gÝ8N N‡ûª2®%ÛNp=©ÕßÛuf¥øïÇ™Ãp¯6®QçÒ9~˜7Œ?â?üNçû35©ïí³÷‹Îãï<'q.ˆß·Þá;æiÐÖáÜÂ{ÁgÄøÀn¦¶Ê¾çï\²}¸ðP˜kÂøÂ^XŸ”g€sÌœ_`aXÓ^ÓŽÿkûéÓ¨ÿ’^s8?­Î÷AŽY=Øã¾T@ñDpFƒ£€¹²NK~IxËê¯^¿ÝêÜá@}éÐîëÆ?vðç©YwŠýeý„FçããR¸ën›¤sÎ+ìoØCkl×LX\Ïú7ì•gMÃÚ>9`àØŸlÃÿŸcÜ•éu=D޹º1Pþ^9Ã¥Ôžðy}®£ü^¶sõ†¿¼çêñÑ+Vf~uß™Íç_Áù=ÿôÿiß#†›Ï5@ß?ÌÁ;Ï»tôàï|ñw Þ?rÿX³ð›6ãò‘ð7ö±! ù:QGC:u;ö¡*õU\gü?ÜïÍgúÏ౿>ƒŽQOíÜÊ=®Âý!g’õ̬§š£õ÷a]ÆQøñ”®>ϯžª 2ûÚ\³B}Ùù9öÏVŒ@íÂ&=/Òc!µ°¸žûKc´ŸÇ±umkÚq]ÄO8¿£© ¹±ÜªóßÉçqm-mÆöÏ‚=þÈ.õçóY®µ–A0šZ±ý~êx„‡íùoœ‹êjœ—¨…£Š_æ¾óËŠæ³ÿ»þ}ê+3W`þa'S7žà‚ú?QûSv{*yö%š`¿¡‹iŸÂ-Ký‘1Ñ¡3.VÆpÿ\âHðç]w¸Ñ3+‡¤̯–9ãÆìû®ÞÁÖ¨£ó ÛŠg ûn,ú¥˜ŸmÚ <礱–úW`ïÌé ¾ÊXê ‘Æöñaø•¶ÁæN‰óªü0û§”Æo‰!YWiYµc‡³&ÒÛ’ºM¸Ž{·ÆÿáºÀ»¾æ¿ÝJ}'ë &Ô“¤µ†š¿‘×CµM㢑Þå~öÚÅþR/NœÙè©_ÂZªàá_šGÆçMÏ×Ðw–ƾoݙҽÏÍËQ3ìMlbt¢úÖÖ[Áº9­q ïáùôìpïqò4KÛ]YÁs~ÚÇ`žëÁÜýíyÅ`xâ`âÇÍwµÅXßà·âOÜ?ùäð^ƪe_ÔèlwYP=:|1ú>ÊO®unåh)þguŒæ÷vãì/k®¤Îª±n=ŸŸçüÆú o-ÀÏ}Uwë›ïpß§[Œp_ØÇp—r®áwÖ hŸü…Ï~êÌàÜž}厙ú·Ü³9ö+oúÓzxåÍöÎ\q“wÏÀŽà;ûïýƒ Ÿú¨hÕoÆ/ÃMû¼rL}WØ“ýÁßyÄ Ö,Ö'ÞzÖµ ÞsúMÞJ¾ŠúöÑæ2gï Ÿgl.ìðO弩ßYš³Æœ¿ó²õ…™ìbAÅ,ÔoãúŠöÁxK#–öûWubÔ•­ÅU?Qlã%T±ý:#¥Ÿ û 3¦MR0´ÆþùíðÿìßÖÙ»xO|ŽùwŸÞ6Ø’1døä×Z?|{ü@ÿ.Úç|ÚväÇâ¾XŸÂm'Ãa¢‡Ï‚Ï‘Éþ Æ×·ˆ1#~¯üéFW›‡}ŒµáZƒj{N6D`+©P&y•ËÉ5³Às¹Ïmk}ØÆä…è_$¾ §)81ìömòÛcj|«FªoÜ$Ïü‰1M „õGy>H•}½6uú)kØWç&Kùäå5ñ/¡ÅùÛêÝʃlüReìÊÜ[úä¥ý“ô®§í6—¯pŠv 1‰ç†¾ÝîSk̰Qù\ßç€tºZõýfM_•¸Ó{„¶óƒÇü`-…Wm?¦¦†ûàüŒ>füú>õ±¾ß†céøÝÚ‡sîQ°Þjß\VùÜæ¾‹ž‡táNjUû=•úêÒ†_޹Rm>}¥&üácì_Ø(öþ4g8WÔuÃs«Ž°Ó¶.£õƒõ¿§6Rvöx|kû­Ã_èwý/˜[¦¶î.÷Q0&U_o„I>Ïûd<–v3ØìpöáX˜×Ä\ÆA½ÃR?Ñ„ÿŽçÆÙñIïŽÄÕéÕŽúØh\?÷ŽíÁ5ð~ÖØ±-Ìïívϥǽ…;ý5ab‡ZiÌ0A#ý]1¿ˆA‘ÿî•!°;˜øàˆEŸÈ½Ñ¸Gñkw¬–¹£ÿ£ž—"NàwÉŸãœ'à(ÆuÚ\ï!êÌÄ@þ¸O cõæÒ™rZõÙ{Ö/½Õ¨÷­îYzôEƒß”ïZ{ì{þmíßnýð=÷úØãWvøÅÚµ¿?uù›N½É,ÖÀŸþퟮ>à[Ÿ\}Ômff7÷ÓèÒÿwõžuõ?úÀ«W°ÞÞò«\¶ãÃÇ}y}œšwÿæ.ëžý†áæø ðlˆ÷¡õ÷øý‚Øt6׿H±k~ø(Ð[ÿ|s ÀÀºNí²{™7ÑLR.e¶R‘ç[¸…¸§G+÷¿NÝ*LÄŠ°IÆëijœÔFç¹÷È*s¯±µ‡`SÔ×})û/¦ò‹ÔÖ¥†ƒã!â©áKcα÷p>mËIÐÀ:uíx¥<ßbpOë$²|n§ñŽûðœÀ˜ ~ÅzHé[F;"?q¨'¤¡Óö­ÝçÞSÄ„1†cõ j¬;SÆ~á=œ‡¬¶öó«¨/'öàü1sî-çÖ¾6=î£m“<êµr|°ÿ¥Ñ4Õ$wÄ.õ®„ÛP£îwüqiDKûC5=xôÉÂ^CléüjzK®=拹ƒÿ)ß þTð?œ9•{“”^{MòXá?Âþ;.®’ãïùy´}ÒžaÜØé÷,¨Î“¾•룊¾{›KLœËÜwÚÇäwŃVc£ý¿~|3sοþ¶¾"ÂNm탆?Zê¬=n #r$ˆÙá îk$Ž÷ ú8Xÿ˜Ÿð,\ÛUöÝK½êVæ7,oá·â?àsŒÖ—yÕe쿹@<\äÜf°yr„鯧þ·6¿¯ OSµAk•üjií¢68ëpˆ@çÃ{ÒÚ+ªï6û|`Ÿ¯›Ã~-Φï~þƒpÎlw迆#Þ“k?+ãÒŒÿ­ÍhðëÔØW³~û”s|2ÇXï©«VŽo½Ý¶þ±ÇÛà:ð#ñþ}éoІâZ[ãîéN~aj3a[òîæ”:Ÿ™“àÚà <ß›POû.ÿo\Ž1ضü}kŒ§´¦ã`%©!޹›vc‹Ï៛çºTÝ õýèã_@téW™kBýq<·±÷Ök«ôVªgÐÞè{tÜá{ꉂßâúø½u`ÊðD&ÔS£u›6¹‹¾{Ìoë/ÄùÙÂ?Õ?ç²bäuæ*;F¶-yW¬S¿{§ïh~q{Ì_8½ ®µuÿ7®=kƒOÛ?kŒ£ÂüçKÍy„XîWáþÂKÑf G†ãƒgÄü˜·Uº7 uHñ»è©Ÿ(îÑ„tüªh<·~Q/\êJ}eg[çY›ðúî }l ±ÏÔ2ܬ_`øyPÛ¼2~ðÙì}|c$¬Oãî]í2æŸE¿Ûgg!^ÏIñ-8÷g«‹q7êõо?®#\àpcüޏ©|’Yjȇ^ñf'9>ÖxmrŽàsœ ¸>ö8öwêìW’÷…yÅõëÆ¦cŸ‹'1Ïý9vÞ Œ6%Æ×ë¶uûƼ°ÄÿÌÛàÙ°R# _ó€˜×ÇÄ;œí¾1Ö¼äüc~ñ`a©³Áºq¦*ºßÊ÷kR;¤æ’h´´ýN“Y½»áÛaß"·¬+܇øŠöÙ97¸OÎä>5\ŽÏ•¾CÛšßEìuìdøÍXWX_8'Œ_•á(Â/F°¹y ùGëex²8?ÀÂÚ ̾ã@Ì?Öö§õ¡Ëð“Ä£<ȼap,÷ŸF†Ø‘öÀýÝ'Lü \ ñ+îg·&Wr(|œsØ¿x¶ôÙÊÙÔWŸæÀñ{ü—Np"`LxŸ]­4aX‡]»ßjCGxãìÓXÁj£?÷ŸWÿùÏúÑÖÞü?k7ûÞ__ð¶»®îúÇû®~òYß\sÒçf°Ö¾ý¢éÕGÜ髯:ûÐè_Ù¼7â÷öfo®V0–}ÒGnròg.Ö(ö#rþÅ[^?˜p/—G¾îv£±ë£0÷é/ÕW’¾¦}Ir!0¯Ž¿««Ý{Ë>ëÏŒ±UѱΩstKÔØÁ9·ê4öwõyð ¶Ç0¸wry¹¿ûDTŽëÒ;й2ìkŸCá`–Þ‡ŒE±7a'Õÿn.ñkaþÎ ò[±g³. Î¦bç`‰_l_›öCk°TÊ(ö7ÿ†þ)r0øÌG-Ž«zÄã>°û³m5•ˆ‘¯6©‘Á^•û‰Â bdœSXw~7c$òå`?0÷Þ;ÉO¦ŽÏÂ0úcõ¼¤€} »íÞ£Ußü¬Ô*ºÑ:¿\…Ûn œ2ºòðC¬ÇZ:‡Ð¨ÆºmÄ÷’? ¾ŠwpïÖ@›×Uš[Ä5|-k7؉yÌ…âùU㹑³µI…×Öpkþ+û§s}„¿ÞujôOHBüob½öq»Èù¯Ãæmζ©3ÃÀw7w=5®MÞÓµÉ;:7t´ÎúïK;¨•}’9öÔ©¢ÿ Ÿ‡Ç?¶ö~ø™Ø±/À‚±ÛÏãóãÜÅÚÆ3й¯5ŽÅ¹4¯45Ñî/×ëð¬/kóѯ‹¾ž{ç~r>Xc:C•ÃÐ9@ ¯J`ê<Ñÿ?F}kê’r}‰sr¼Lõœ×°/Ëñ*TÎ!&‡KŒ}óþÛ‡Úü þ™¾Îo¹HÞŽ3ZÛ˜h;¦¯ó¬_¬oÔŸÆ'qª1Àõ|ÃõQEpdœMæq3F÷çËÙßÖ)lÜsv;f}áé«Ý«m‚z$¬ ¥èamMªV}Ê'SÝŒ;þ ¹cß>õiTŸˆù’*Ï9õ—oŽÏàªÛ~dø°/ßtu3ö_yòÝŠû¶£g¾û««ÀÔÿiß#¯ýú‡GŸ:ó£ç¾àfCì[ø5›÷‚çíÕJ|³Ë¸¯°–Ñó\£è+Ggݚͩchs®Ä?ĸÇDŒ ;}m~—µ ®b½ìè¶ú®ô}ëp1÷ ®ÇÖnÀ^‘vùë¶qN†C’¾ê}Õ vØuy]v÷êã>a~NéqmÜÓ²„ŠëÚ§Ò¶¦2W©±Ž-µÍ­áþ^ìùævê;Š}‡{Z›ˆu%ÒsTn9uÐÎ; †o­u:–>}ØæôßÊg`ò²Ìå.°^=yºús–iÍõ ÷*•'¾¸Rœv¢?|±I0æZv™}4ÝCm#)çõGØãðÍ1¾‹ÔñØw©Ä;Yo„SІuµÀÀýûÃõÍ=2Lj\à*½Ǫ+-ƒ_àù1¾Ê‘[E»hÛOŠsÏœó&ø„°ÍÖg0Ûô»2Ǫôþ¯ìCÖâŸîORiÿµ&¸#ýG*õ†“Ÿ„ç3Wçh´ëSg© Õ©Oò¤ó¬ª#UÞC˜Îù`–Æ·çMX‹ç·}«ÝW¼´~6ý1œÃ¶=MrýÛ4·ñ>)g-æ×~áò¶úˆ:8;Ö‡up÷á.Õ÷PÚÿÈ{¥^q|nœUæj±ö}Áõ|È=â¾æoóðûømØßÆÅ û½ÔV€ßçF %î"ÏÚéê°f‚ŸcíY»‚¾9°N×ãRK¶AÔojú=6Œa`Û‘;Ùún‰ý¬†5÷ÃÙ?JøŽzá|Á¹„ù7¶–ú®o÷žrn“>ÝöÚvž¡x>øf[ú;G»ú6¬W¬=ØgkM1®ÁÚýÆžÂüáýÌùÈûñY±g°öá[)Ff^¬Jû(p}Ø7¶¿p4Z ´ÿX¿cj¬uý«ù®ƒ`ì”Þ[áïõ­‡÷‹o%ü²ÛA¿×õéomªðù]ºw:1ªm¼=Ž¥ñ<æwÝ£0þXì²Þ”qýÖ E|4ç©9Á0í;—¶óê"¿Âû»§a£ü¡´ ñ|ØëÏüDqù ¾ÿ£Á½ë×ÃgÝú«GòÕÙÉï~ùíÖÇÃ\øÌ™‹î<»_õ†ïûÊàç±òÏÕûgwÓ¯=óoÜóÒ3‡×üî'‡cÖolðÌxÆ«îºòˆ;½>ïqÇoÜvãì§~äK_ýÈh„gDø Î@¶â¶IëöTy%jݦwt—CƲuךÔ~ªNîH›Ü›ñ Ö_a~ÃI]ƒzÌÎÓ6*wp~mtœkF›Ä8·ÝßûÕú‹Æ˜Éj­ ߺ×Eø‹]ýÎSçüÚh0cšOŽ­\þ«\×íJŒÁèŸ>göioÞ?Ÿä.ûðpóFÒ™OìâækÃÙ+n:ÄûƒŸoîV•¼èOŽ^—›ÇœH«JºYÐQÜåÞ æÇ'fe~ š;ˆá0¿°Óò­‰§’ƒùÔù¤=}×õøìcÿqñüOêì ¿cnEÎ ãçè™[Ó¸ç=ãA¬ckEÿ€¹'<3ìŒÎ Å'Ѳ€ßŠÏ±¦àߪ†Šú$<;Õ¾œ™ï'Nó“ܧÖD˜1 û[óå>ƒßkìˆØ®/Î}¹6}ˆ;çùÎ%Ö¿t<÷'Gߟã÷8¥%×3·}‹iÓ”Ã}8ǧ¯>¦exÎÂĉE´î Åõc<úƒ­ë_øùÕÖgwî©ñ™V)y$z6…s×ÔÜÀzÃúGgÜæÛ»þ„ëolý’Äjx¯èk¨1°R5µ²+ÒÒ\¬£±l ëÏŸ ï÷Â\‚‘u®:Ž};ú؈p~`ýšŸ“ߟvãÜyüÚþ ÷ì·{‡Ó2w­‘Náò¡'ØKhg+®±jÜ‚¢й ¬1Æ^3O‹ñÅæZE»X|rcÒ,Elz+ÔN‘Ê\©ý‹2yFàñocÿO;çÚ齬Ónº‘Y§×ßPXjS¬SÊ}‡1‡ÿ.~Ô|ö>}ĵÀmß`ÊnÎYûˆÚÆÄmH£t_rœ+ë«Xcí|î_k$ Åïqna~¥7¦¼~ø©7œa¸?ì°0Щ2½¿Í½&Ç/ý]´·÷VÖ-`ÌŠ95Cx~ë™×Ö/™–Üêë‹ß`ÿa\ð9ö1Æ!Ü ûð±Oj¯ùù¹·ÍÿbN÷Åýƒ³¡ï[žºçÕƒúæ×>òÃͼÿe¯n>ÛÊ}ßy5ºáé¡uËë_ÅÞ>ð½_ùãÏ €¿›³GŽÖêÖD„=G|™ºº±tŸ8ž8“á_bÄ©› Š{ä€úÖ§§CÇ«€½–V½xc˜ŸØ‘ô6·£RÝà%­û×’› ý8ù¿XXŸî'ÍsÇuÐMêàß`ÍbNÃíMR´up}ü»´¥ïÛ»c,ƒöÙœ‚ÚgµÁaGœ{,Ïeý~Ïa§Çª-vp->Õ>à?\7â€t½ ˆ-©/‹´­u~ôÌÁ:ÔˆÓw°²ßÖ*g -ãƒÌ1‰x¸UMøÑ2×\qÎV,js˜ŽTéøŒœ>ìeü>ºÞî å:ÃùJÚ`GÓcŽø™5W»¨· ¹õÖ¤L~GúÉÛüç%çSC…wq_“^¸(Äðnx~×~Ñÿ4ÖÚõÔ¹q˜¼'œ£X‡áU“€­ÀŸ°ëx>÷hÃïÉÿ¿=Ú™°ƒŽêèâLŠÛO~nlã`/Cø1â(*¥ÿ~R´*qXö“#z€šŒ×&F*R›–:Õðr]ÿ\»o’Çgw¸ æ`«¿-Æ(ªMyl ÏU÷lúî/‚ßc|Ì!á³l;ÿécHß|©ríºóNëÆX¦¬ñy4qczûql±‡¤{u ±]Ø.ß¹o2÷áçÎO-;Æ!þ—BìIuÙ³é½ÈÏS#ãzK¾¿pj:-Gß/}5­dí®ÅÔv6Ðû2ØqÕX lOõÙT³xÞß¼ê–Ã+þù½ÃO¾acõœçƒµ‰:â}?ðë¯^yÛ‘‰™ÛœóäÌüÞÌgGýN[–ÕôWÞ‡kó˜WÉØk{[˜Ï$ý`æJ†}‹yÎúËV6BÚkæ”ÑÓû±už<öìžõ>¬£Bü£Î€û{¯´êÂüPcŒü±ÖýK[cšÅVžr±0ç‰óèã$6Û†O6ÂçNdý¿$þ®œ«j4ºú“¹hÔµÒ»8—ï/ÿAuý˜\s†ë«^!ú˜'¨sˆ}ïºÏ°¹—KÉ‹p_`ü…³À&¯§Æ>:¼.Ö±5_yi§)¥­²HÙ@ÍûµW¨+× KØÇýcûU*OÁüóôÈO`׋†7µaƒ‰ºÞ½wïƒç²þÇ?}iíàœl%´Ñ ñùDZþ¡Z(êf}1ƇdN ãçïŒßKãßÚÓGõb±i󬨹l^I¸u©Ï¨…ƒŸÏñG’5ZjŸ¯©hÍYh…õ~ãJáÚÇÑg.7×ÜÅ~Ò—p}yíóìv¬Äûc|Ý[2¶¹LþÈvµdc÷M ¡ý¡>Ù°Aæ~ò{xì[¿ùs©qIžÀškòÿÊ;·Å3ƽiß]¾ìú;žE°sø—[uóܤiì•X¹ý†àÀÀ©Úœ#æ™ öí8Ÿ˜o÷?oa+_”É;®k¥C{0ù×eiR—™öníůžÅ9¹zÍí×>ðág¯ýÙ%§­Í¾åÈìÒï}röï®~î꧇Pó¿üÀÿøùʾjÏêWßû+ý _?sëÛ½tfóì^yô®+x§³~ýÄÁözœàªgßbå…_úÁè9½ËØ90RìCÄz°›ï?ؼ't‘ªM¿c„ù“Îø©ôi°¦¯qO ×Ü´©ëSw'}Käù±>ÛjO‡w…ñ±ê«'©1xÌ:\°Åˆ à'`üqMÅŒëù3üs¬±OŸ rvÙ»û6Vùoù¯Ñ£ÂóG«-¸kâ¼'pKiÛêL€]²Î0ý[ä=°\#ÙDŸÅóH} Œ'lI|@ŸÃäº,í‡÷F³à>±Â®ZÆ8§­ß{C÷Ç>×^N#æÊ5Jeò‹Z¿ŠÏÅ_ÎIé_g•k¸w2~užV³Fq¡:–±²´–çËüÖ½‰8¾çY’úh·ÆÞ»&7ù‘óÎÕc§ëkS{lšà8X¿âÇê|†¶ëäÓ¤Ïyp¡>5gÕÍ1~8Z©ª+(¢í+¶Ÿ@-røa°þ<Æî/’\û#·éE¿í·<ËÌѶÎèQëãJßÈçIeÏŽŸâø"=ÄiËàÃ…?"Í0­oÝŸ<‚¶o&sP¦±?€‘`?ˆß-þÄ‚ù§Î¥ïrr’笕°$ü3÷dì>Ï~ŸJùµÎKöRƒY„ï÷k|&17›[…õgß—¹?Íûâ´÷;ë–m›8¾Ûjtè c}âÝÌñóÚ9X™cÉ÷Ãó9V©½§Ë-ÛÅFÞ[ø£xX°xþä"U7Õõ7Ž}Éùíq±ž«¯b©öa©[º>ŸÂ_W/˜{ط֚ϛNßæ€{œgý¤‡ý'ã''’ÿ©ŒAøœŸäD˜†{Œ‘‹c\®ëqƒ=ŠçÇ<sÝûØ}¯q$Œ×ú)»Öà7<év×>v³ WþêËÃÙW,õËÞ=úþcŽ O¼îúûp=r0zØ]~6¼ï ?Î_ñ”Ç`°c×ß°Çvð‡/þÅècíÝùóùñ®ðÄQç¹A\výdz`Ô©Uë«÷UjذȃVçó¥F5Ï<ì¯ëœ¢fÙ‰Úüp®¥Í·Ã„½S߯Š&®4R©RÊ^'Vƒkãsü>ÜPì÷X% ,Â½Æ ç¦Ûqr Í»ªâ›;F.`[±±Þ݃®vmŸÜ@üø(؇Á¶\sDürL}ë3*õ":ÍOé‡Zó0½R+é°wFÇ¿6æA9×G7©Mî»æØ¸5ÖºÞ7XïxoÌ9l<ž%Ú€ÇÔŸ„gÖ§zܶίR£ƒó‹±yäën7ÀvïRú¤ÌçË=ì¶ô¯†?4Vý/?ÇúNž¨Ïž´çǬµ÷–˜Aþ4sp`ºÂ?RãÍü±&5;˜³hàœ †i ÆúDœÿä³dç\ÍœPež2jìiÛJî ë”æ-t=Rûoüßó"±ä?²Ï¢ñµ`­µcîž§ Òu ÿ±×Æ úÆaºþiÚ?s|GiH¨ZŸýÉwò½1ÎÀ±Í1.ÓFLã‡ûkmªn*¾žþý’nEÒ¾'ï±s-i°âÅîgÇõ%žÞ÷ìæï‡5Œ±Å«çÂT¸'¥j¯„ñÃÖ¤.À>V) Tãü?Ú©¯TÂI÷"¾Ñw0®-^ÀFiŠì}êv`Îð¹1·Ê¹Rñï^ædPƒçœcŸyºg0g‰9»Ã=¶­¿„ί}´Ÿø½4ú6ºúP× rßb}˜g×Õ/öYß´vŸã=Íy5÷uq÷ÇÞÄ3¹z¶òPçWöOùœæ‡–á°ºïi×C=ܱ±yr° àY_½¶¤gÀàw™[ØûKáVÓ¶›÷ó¡´í\ŽÆüâ»îõj}«ô’ 父~ªã†:¾˜û"UÎGwã7V¦Fùƒ\×è‘Ý(¼ÖæÏ{ê±è¸sé‹Áøõ¬ç®±f£¸ñìß}mßìß®š}ÿs8ûèË_<|ïã.[;ûÏ]… ~Ê ¾0|Ü7Ïœùñþh欛ÝiæøèƒÏ>çé«è€\"t{á3 ÿý@¼Û9?øøàµ_¿Çç Æöÿá}±w?ú¤ßáÚÐhT_Qö¥lʼn!Ÿœ˜¥×qØûÇÌ :×›ø¾²Ç>½º9?îÓKn»rz»[çk+ỽ"qöüJÌøGÎÄ¿Nß ýËð|ñÏÒC û_=f§­ñÝÄ›[YÜë6×ܼ&ö1¸ÎÑ Üûo‹?}F•ž’ØGöYKÕ‘ël·_é÷¿jl \¬×Ý3N™渖göe¸ÎõÑ73†:bðÍñÿa°ïãC)_ô$b˜+i™©n0ùü±§l{ .'®Yv ¹;ö׫œ}öc`Ã*ñªòïÔÃ7:õX8kð>ÐÙL~2ü ¬ ÇUU´®qÿ\Ký‡YwÁïš—\§/0¿Šýç |Wã;gÐ+ÃU,{)?77©L|k\t:󇱨:çÙã—ûv,±á„{8.¨Öšóc|iŽ⟎#'m˜¶rÝAi?1ëCñoæÇ…×Y©c¯RŽl¾Ìþ޵ÀÞ&êáhí±Ò\›:}â’ë”vý)ûßÒ0Ã8Dwïâõ縗õÖÔ_Æß[«ÀKÆË×çǽšŸS'¥‘Jl{fÜÆîÁ×w¯{¼p$Ÿólµõ<¸&°ÿÍÿk’§Ç¼dþį¥íMpõ =—ë7½–Í!£}wÏù6ìŒvOú…É›G|µà¾1Ö>f¬‹ý™>¿²þX#ƒìVø=xœ×klj{«œÖ=!mÝ’¬2{`‚Zë¥ö1µê˜{Ê û“βpöó­ÄÛXjr¾Û_çøãúª+šLí×ì‡ç9û»ëß)[!ݘÔy£Äøá=Ä:WÓÉßÓ‡Ñþý×ΗõÆš:ô0þÖ¯Â=i¿½/J­•)sŽD+"ý{ê-ž u%éõÝÜW΃±¯h„§7¢õÃX‹Ñ¦—oò‚ößñˆv·ÞÇUpWçH¹?¿,ŒC%v¨âŸàßnôàî͹Aõ¹á‘ß;ƒ5öÒÙw 6Ÿyø©‰—­^uÛ¬o÷['­Ì_ïé+:û6XÛúðÅß ¦¿rú@œ´¹ yóÒ¸çÁ„]†ÿ‡ñÚ´G£ä]ð®îKßÙ5 óW]lçèS£ÆuL XÕlhý¢Ÿó‘øÔÄ÷ìÛ™¿¾·TÌMåÔçu¾[ø[ªÙmUöž>¶Õ‹1™sæ°ôº5Š{ü½ú¨.sö`Í™Ä< 0N¯3ôÖ¢O ßÓ5’M´ÕûîÁ'óÒJÚOJ~Ûøcö‡Ä}Í_l ‰S­Çü½jêÕRÏv}sû¢­â¾®aì5æ³Tá[×h£Gß g"æ)ÚŸ²¿ä2öW'i˜ù:‘Îyt•®/ÆÇõ‰7x¶(þœj\;F|>ý3Ì1ãóoåëplàÇI¿a9VçŠØ ÷Q¨Ì jÇÖM2ž1Æ¿OžÜqk'|Žñ^¢ü"ó7áârþñ\X_îµW„_¥\Ül¥¼ u8›àNcëƒSîÆÜ—Æñu;,ÍSibÿ¦?–ç‰ü@qÉ?ŽïÝ8OC[€µï¸Ö®ó¼ïÊp_pmcßô­õ—ü·c¾Ê¼IçîzU´­oVšCOÿç»xUǸoëéÂøë0(Ægƒÿ17rÀÚ[ÖušöùÍ}¬šü úÛû³šsAþ£žg>ó7Øÿá|RƒÖm” ÆsFÿ öÁÚqÖÜT%|n]Lœým¸5á>8¿Åý}É™÷ÛCÚc®Yûò±3/?ï›'_þ‚Óï½Öûääì_ÜàÔ=g9œÅ>ºâ—®îþÂIkŸü×Õ›Ž&WÏûƒ£ÃÿzìúÌ™o¸zŸýÆwþû¿¾fø®ËŸ;Ü|†Íÿþ£Sßþˆ•KïÿÅìÙÓ×ïC\àK'½~„ñÂØÝï#_`„wÀy-ÇûßóN¨™g^ùŧܑ5.®%bž¶f¡ë¥ºÁéÒÇŸg}Áöüžøs4®§:Û¥u&+×™qÜìK–Š­Ø;/µõÔæK}¦ç‡y'õÉwT¾Áqú°éOˆ÷°F@úwÎ!·®³æ5²î¢³¢ºá“£Ýžþ|9'Ƀs<óÛÞä–8Ïŵmm„Ö½¥°Ãã<§ÁÇÀ3â=¶ð³¹®‡:8šÆ;›äû[}ì×Öµ©ÙtÞ¿HŽ ãŽ÷7ÿ!¼±r+—3ךŸì~3êOÚ'm÷>Þ9úÖ›sµ>Ÿ5kl;Ù×¼‰Ý!‡Uû'¹ec8®ÓÞ™ó/½SpoÛΓ­>ÕnÅÖšgŒ-¾ƒÏñ,æ,{‘—iýRÚðpËûêÛ¸'ŸÝ½ƒËp3¤-î'ü#ñë„l]‡\MÖæÛ\¶íMÆ~˜[÷±«оâžxç*Zû/ÖS"|¾#Þã˜Ú–>kÑgã7rýºÇiÞ±p5g†¶Ó:×\Ÿ˜_ñ/Äo~­>DøÌX|ë~Æñ­YÇ›­ÚtÖÓGÓ·ŒÆ¼í¯ŸüþXÚÄd0'Ê™Ì:NnE.mêÕ0ê•¢þ<öÇK×ͱ6?¸‡1ò(ÌYàXKÙÕös¦šp›77ñcƒñ«¶ý±9Ÿƒ>_[sq¸½o¦“ó®÷‡u¹¾¢³ íÜ‹Ûð‹Ì_dl­Z®Îë1õwMÎ;:©û6wëËXH£çWZì_bæî¬yÀk`ŒÍOê8Âx¶h_Š? ø%~Ö”kuÚäóµGæÝŸý:⨚Oêmpì¢Å’þ¡˜Ìõ.8ÿ9×a{qo÷iRoýeàî3ßÚ¯¬]F¾Þ+Ú-©;Ƶ³•~…Öž/ySÙõ‚޶„ýßÒµ¥Ï/ú¾®Ï$ˆkn½;ëÊÉMÉüÛÎ-û `ð¯¿y ¾úïÿ‡+NýÇÊ)Ï}øÚã>ü¹UøÇ÷Ýû镯=´ZE¬ð”ÇŸ=zéì»F³'ßpõ ó¯]ÁØŒóGyÏÿ Ï&ÿcªúÁåOÏÆs¾ÿe7 F¾bûL¾¼ûò—†ŒüÆõUgŒåœ5l~_;×]cMtâ¢øÓ¹˜Ò¸±¨àž}öê¹6½åÛð`g€«+žd.Óº,áy͉ááKõ¥KÉyW¸!bIà_òÿÅoöYÏüš4d~麟ñ¿Ä/˜gþ'ùgçqø=øÐÑqì³Æb«?WòÖ±ªì7—Æ&‰o!±WÕwÿ:ÇÌ»áw¤;Zåï«ÂĸXÀ5—Ô¸µNÿ"ß_=lÙû°LœgÿŒ¸ÆÉÜâÂ;}OäÝ1/Ð1‰îFÆsÜóè^GÌoF£Û±/?·þUô³jûkóÂ̵öÍ»³ÒÅ(á M¸·ºâÓΓ0ÿJ|Óü½àë…9ÈÌ›bž‹”9ãúæ¿a-âýúÔÙ¨·k¡©>q–yJóækײ2Þ’VÝæO]ݤÍÚ•âµ½Á?—soá?—r~Ã/ÈÙq5”w"ÿÃýãìé ·,l’æÔü–Ú{¶ÏŒÏïÐMrD¸–k§‰c}¸'NcýÀ±tñËèuÅ¿î[£Aú‹Ð¯&OJs$ý×Ã0VÓ(È^ ̺÷ ókæ§4Ρ–ÎoTªÓ½–ûÇxW“<Ö­µ÷¸ÿ£].!b|îü^iü9=qkåA~ÚÕ·Nî̃ÿ=1*Ø'k)¹¾“~w©üÎ>Ú7iÍ~Åë{.õmÓØ»ÆH¸>}c÷Ø€mPoûyîä|ÆQ×uçÌï)ö]o\'j öv&ÃñeÆ—çùi¥zÌY›S~®k}eœöOJcÔŒå`ñ÷诤¿(¾'îõ¯Šü>1€k«ä=ƒ¥¸ggm,ˆy—¾ôŸÆãƒdŸUâìxoL³¦ÆIºžzüY ¸HÞIXè%ÌÛc9W[CëRú KexÂᲨ7µh¸Ï‡Õ©UÁùh1çC³}}âüšM­½Ÿ{ß¼íê«_øÇ5üÿŸ?ñÏf7CôÙ÷xóÚýþå–kúåé3úó¿›}Äm>´Šu0zØmg.­ž7óòßzÞêÕ/øÕÊæ5ÿñ÷OœùôÎâ G\?õ¶ŸŒ6Ÿg€~ÂXSŸÛøÙè;_\aa/¿k×-KoÞ] â‹Xƒ©õ…†gr 9× j ƒƒü‰ÏÀJX\¯ ~g~l§ÿc{Bü0z<ʟ̹¯ÌÚsNXïYs_1ûPôC¼ßÓC¡«_Ä:0ï‰X‚í_øÙ쟋9IšûѧR}ÙË™–VÓeUöDðÉðtV-VÙ»á°Á6›—ÏsÊØBí:äië,µ²[ÄB³vJå§¢'Ïõ3–nTÓ7Ïã ÿï;vý»bqæÀjiíhÁVÖf16-îšÇŠùk]0‚kãL’†Ôuû'ÕÉõàÙ°&ðþ¸Fø9™Ÿø,Ö–i”Uܯ¸XÚb[9Û-~Ç„{º¯ ççùeÆc澑ù‹[Úœ‰à%÷­Eß·~¯yàîp~ûÕ·>H|§pã'Ô³³²ïQ(‡0Oþb‘c®5výXáóžö Ø9lžôA¥[8¶¾8Ö4°ð^ɵE,üëú7‹©ðŠè;™¿ÖŠ÷¶Dû%œv—ËÆpÄ}v÷&_[g­Œö_ø_ñŸ<•4þf©cŒø€õáÌûã\b|Çîß‹çsMMj3ØËÑñw—ß_PoÞÖŸýØS­å½îþmà,–Š_Ö­E/Ÿ {Ö}ÝyÖJ3Mü~q½Ž2¿‹çÅX;n-ü=Ú4¬¼¿yjÑ~,ƒUÒÍ롲’ßÃÜIi­2öWÃVFÿÀ~ù3À'ñÿÿ ¯£ÏçÒ¾à7öë][Ú¦ô}ýÂù±xNãžš.Mz…a­ wäÜŒõyg™7޾ñÿÆú-¥ð0ú†å‚û•`L쟔ƒ2ëÓüªi÷‚,'Ü£þ5Å[çe*c…ëKí©ý­ðžóùüÆæ8fŽKèÓãþ¹ïî‰âpk±ò˜•ÁîT w ïõo “>Ö½5Ù‰Û`üÌŸ3v|4ù?sÊÙ[©É¹M¬³ç|àþ³X×Ï|à)Ã?ú×wß6þàÚ?<ú]kÓw=øËo>ü¾ëÀG<ëãÏ\}þιÕÍëVGïpûؼô„ßpá7èmtô J/¼7t© Í…÷¾Œý=çK˜;ÆÚÂúPŸƒ®¯z:¬ Œ/æÎ[ò!'Ñ@]±uUé›FÓ4vì]`@ ͘³MýW\3ØÊ‡ö†ryø/¸'|X×w}–ß!¶ dAu¾¥ß£PŽ—ºOé/Õ„ß)­îÝÔ­Æeºˆ~²ëG+kNRkÌøß²õÀYâûÁ÷TíŠüfÇáu¯ZÔXSÉÇú…ï‚ï"ƒu‰û[[™6ÖõõðYà;Ó_G6¶cŠu“þ`ÆóX³€³¸)b—änWHã s‹ùÃøHÏéºÔ,Eç‹yì c”¥ÏX÷ v?Î&ë¸þ:øN«Ü6ûð,GãÁüP|—uuÎó—á]¿Û-íõhÃL;ÏÎ9Jîë#¹%é\P#°F^ö%µÆ­ÊœŸáÁÊ÷Û7¨Ôy¥á7Y&·hžf!.êT+%õöb_Œëû7~¯çÞþ+ui£¡ÝõýX°þö>ÆÐÜUÚfk|âFì§}¶¶km.çÂñ÷/üHï‰&ý†°W°?ð{k;v5;KüÞ}/`÷웪&ÜsHß ãû۟ع¯5׿±¡Ò¹¶:Ú»T¯ÙœõÙ×ÍâÞŸ»ôÎk7ýÌŸ®=ç¿Z{ÝÛÿií;wùúêo=ô{þýÕ<ƒõó‹]½21qÚê ß÷•ÕO_óÙÕŸ}u´ròø§3›ó8ê«6~€À›ï4<õíþåôYƒ.< ÚœWÖyĘ6mÆuû›c;À»JïDi°*Ü$i#œ6>럛ÔsÃöýâíwÀ>!~±ÿl)ý Æ%ÀS7Ž„9ð‡X_†óÈsß{ ‡Ïc.P4—sVcm§ŽG¼çÕ5îS9‡£’>ãdç[Ćbïaÿ£~ú7˜ßÏ<.lÎ?ÅÞÌMOX‡ó‹¹wì°ìú«ÚõËÔ:Äó¥š0÷^í:b°ö;+ç¹îÇ-.×þl4ýUÆîÛc-ÞÜûècŠ÷»£ºÚ=š°ö€Qó¶æô|¸us+éo’ÿ]'ßk¼­rΡ2¶OýGØ0áY<»S§V÷ézk:nM~ Žv‰ùÁÄðÆÇç8; g‡ÖúÄ™7í>:Ú{ðÍå͹Å3МÓÊû¯²îK?±%ÖÖ/üsÈ?b?àÿ[ÿ¯ŸXõ!x6ÅN'Ó¶ÂþF×ÌÚþµ8P'Ìç¸T^YÚìÞ5âfq¯÷'·<í%})ضô±4翱¯á<ûbÖ8×m´­úÖ8³ïL"þµd¨ƒƒµëÜ`™VñÁs6ƒÇsÓ5ÿƒç•ãvŸOó™›*ãŒõÿm—{ '’^|ßÔXwÆóIËz;ðoŒÕXçâ©Ç-ò Âî³c^­+_ÚVQÃ÷ ®k-6“ׇqŒ­Ç±2Æ^ë\ÚŸúêcì”ã;ÑqÂÇÛz(wRÇ©ïoéH@C›µ‘Î=v_¹öâ êY5ñOÇêïÍùp¶ð¼†ŸLï9pü`~ƒz,Â>× ®°`íE¼?Ö8Î>ïÍÔ6©ï€çwíU¥çQÿí`1ÀMÜg´rm }°hè(¦UŽ,ü ç5‰…€ãìëw¡ëD®}þ/ÖŽ¯]•š3ú‚x~ç­=2ë<ó„´ÏXCé;üWùcr§i_. ^©âÆp„á{[œÏ=¶îÇ‚j¼ËptG”âh/¸ÖïV¯Ýf5¨†ïþÎKg6¯;s—WþíÚ?{Ë™»<ì+½»þ,ûN qàðßþÕàÇß]X½Ñy«œw|á‹W¢Áƒ3~Óæ¼0ïýýÕë¾4€9ÞÛ61FúqCÇyîç"Ÿç æ¿GnF¾¼ê×lW›àêÁ‘°¶°o…ƒ;ͼ~o¦—œ|/Úç嬡Ëþ*Ä›ÒK ÷ÇE+Ï1b{ŒÏ1ÆŸÄáp!f€}%N"µýŽ¹èˆ°ÿPjÏœ[jUª/Ö¾³M¿¤Ÿ‹ÿ`›`#£•èÜkêÈ©µ‚3½úêyÛxŸ0ƇŸû—X`;?ëÑui•9ÒÜŸ©}‚½ÁÞÃýÍŸmÜãg®ß kïõv5û^[fFƒ4øx&åHÔë?Þæ8ðùRË Ûß+ú+9¿Ã‘¶„÷‡/­qWnãðÄÍÍ‘h­=°-Fí·ÃZ0« Náó“¸+ü`Ñp}E­£ë•Ç  ï vãõ ›þʰGÕaÛq®¥JÚçÄ™KsfJå'NãùjUëËDë ¶Ï'w1¶¹öyUªÒù®“ìz'5æ§2¿ýÄ–6œu|N#bý¼8’GmŸT_޹qŸé®.Ðu<ë0?ŠÉÍäú‹@_5xÉæ|Nåµß¼•B ¯”†ØqëŽP¸±ÝmÜk”:ÇÎ/óþï`è}ñ»c¾Å3=ì®Q,°ý­j[ܽ¼U¡º1÷ˆvÙ´ã?Œ#cCìÔ°Gá ;…³ûËö¹ÊøÚ©ñ¸@}¦£; _âTx6çø¹.ãCŠg¼N^öGjÆâgqícMX?ºÓÅsn©ÓxÀÞv-1mÖz–áAY—ñ‚{TÁðw¼Ÿõ=Xg ã÷øÆØ‰{3ߤA_}ΚôsײúÜ2·þ#{(»÷E¿Òy$žãXÑF7²ŽN?lÖ®kËðWä#³¦”±Ujo½' בp,hþ–ÌkrŽªÝÊk÷:ß(k ñ§û|6ý-þrájæ]'Þáô¢ó…w³ßB›`ô&õŸ°wÿùª'î¸þ·~³ö—Ýçòüå³fÞwúukÊ?¯ÝæÀy³¯¾Ùófaßr×/®Þêñg¬í|È_ ‹ß;¾ú‘¿ýÐêkþd× 8ƒxþƒ¿~Ôðä3><¶ÿÉ^ñ¦wìú›ÑÞ^ qîÞ¢÷ÚÑOŽ|†º@›Ï:BÝö&´‚`çn~o ÜÊU›qíãØ[è1 O¡Öê‘Ó׉¯­X÷áUú?ã3à^ðÐK0}}¼kãÃ]͵ã¹RzöÔ€%'×uáiŸ µøŸ»ÙSkÌþ¹k3µ‡Ì¥ îlnV/LE|Sœ•˜Ådë>WÉ3,`ÛÝ÷͹ùäïYäœR£>懣P÷ݧÌç¾Ïü°#ã¥Mò Ø·Â?ի̶›ñcÞ ×·ýãÞw¬ØXs¾ŽÞ|_œåRý—Òœ?Jîtá'ÃÏe>Ê=ƒÂ­jŒ=”РޔЋxN,ìÛpÎw1·ÍìÉøøÖ”"~f.w×¹ïú*ØUØå…–|v©÷¡ÞÏ’ø¬N~VñÏ>Ú¬ƒèSãÁçÖF Æaßp*µ"zÚô¬1·£µÝ ç>œ7a§µ±«˜_ø–ÖG'>›²…m½ÑŒé0w ¬TZÚ{Ëð Äï<©ë¤ùÛáx‚y|¾7|˜­ÚOõESâJÎ;|€äÕ¼¶8˜?kVŠ…O¶ÖùŽÖuU•ëmßzü\œnôò`ï&žqר·Tô%øwc>9†µÏMÔ_ð¬ÃøâýÌmlä;ö\C9KN.æ㌱Ás]$c=<zaüSO‚s.„>îûlã¯ÓFc¿aÜa¯Îf8梣=Úv÷¦cï‰&5„'¼/Î]¼+öQ¸¡é¡}sVºüƒsÈ|OœxhÜøì.ú®_Æ:ÃÙƒë[;«6î^º~šœé°©|^5öc¨æ]ù×F7æON;ÿS¹ö¨²¦@×ûÐ\IúÍö/ƒM ÜŸN[I|‹%~ÿ˽®Kÿ{“úÕø¤ñW´.Nmƒ`/^±ðÙYäzN¿É[‡w¹øÌ™Ù}·];÷Þg®ýtÿ9ÃÁm?»ò„µ¿šOas?~üèë ïóõvåôÿüâ âêÍ=<úéY·!@5èw¼çõ`A½íÚo~§€ÇÜ ÷ĸàùW#Oû¥“n>¶»H|9?œˆKûÒîi“c5ÍgÌ-Fæ©¶á'a,ì³ã}Á5´†#¹±céßf,¬spŒ/$OÔ(½’ç<>OþÁ{¤Fô.öèk_F[5<Úi鉞5û'\±÷Môðà*X·é…×g½l8Ö1x”˜_à+®h¥x~x½äÙ¨9Mú¥ëÜ*ðŸoöS÷/CÜœ5âKãÓœiæÐ±61¾>÷›Ô9àâ™ì&OmÂ5ØÎAs|€K`¾ÁC:&Íeîe< æ=¥ GŸÆý†Jç(Á»nm¯þ—þ1Æö98Ä Ñ_5¦_Ç®Â>㜗ëoÙ±3qø¥˜'­Ã^kÞõŸ¬1ÀϯQ/ÅVÚHÑXØ(ÕûA<)ã]ïhÙ“µÄýY_…ñeÆpˆë1טó·—a/`^¥ŸÄ>MmßúIÎY—éÍä/=äKÇ^äŸÇ>±ÕÛ…>£ë¹7¶÷P+¶Õᓇõ‰÷·~ðôÂVm@ãšưɛï²þöB´]½žºÜïØQé±Ü.cïÚNÚtäØ­ÛžçEzQàûê;=Ik|Ï\ÏÆgßçLô×sŽ`®¤éw¨ë1×ߪ$>€ùÇùen ×O¯«qÁ¼A[ãäÜka쮉,ö%žï·MŸ.\ Âõµ\ÿ[ý‡zEbÄGØËÈe»Þ¦Ãv]¿ÑZ´Mþ ïfå2=âpð.Ì¡­Í£á¸:©mßšp qn¸wN48Šq×›º?ôÿlgŠ­w䵸nŒ!¹>V½y¤Owq;¬p™ìƒ”æCG¿7½7»ÚSû?Üg°ÖlcWR'j>21jñ(5®Žµ—¥ºÄçÇû™/WÆŽÂnÁî;o…™I{/œPÙß‹ªp9\;Å}Œ}ÿH=Ô÷Å÷,ÃïHovhû¹ÆÚš8ô-ûaÜ^|Ê®Ñ% Ü”k—úJÈaôÅ]«ãm¸Ž]zÏû×ÞœÝw¸âÒµ¥gœXû½—?qmïe×Îþ½¯Þæoß7ûî7Ÿ:‹õÓû·W¬nÎÃê3^u×Õÿz÷}WOÝóꕼå¡3>x3´€Ú#;†zßCóüÏ“öÕŒFsG¾5>Œ³ÖúàìE~2}b°ŽñüîoV¦®:þ„ß{—ºXõmÑžÀ:ÂØŠK8Iî°|‘ì1C\#½{1wØ“æ½TéAèü4ãSøV7óZÛ¤ÖýEñkØk r…R¹—¥®0Ö)ÎqëœaÏ·ÉÁam»vÁþ¥XK®à9è>¡Üß>;ÉOÃþq^ 4œ{ÆÜx?Øã#ì=KŒÜ:7Ïá3‰~ˆ{‰.‡;ìÜvë÷¨Ä‹¾ª ~/ݘ¯´É¡X‡¦ ¶„çR_§ÅeçÛq׿—¹öÕ­ýÒü‹;8vŸCíõSÿi/°7ÜÉ­mn÷{Õl°–’6ÒÚ†ôÃ{—â'Iãë+8ÖDøoÚ£êk…õ ;ý sxÆáýñyîó$8Î[ÝWÑV`¯¹&qWÌ0Gžcö÷gé§¾Òö£t½rŸ<1,ž ŸIGòäØoÚ<άYÌo8ÒÆ¦&š5û{ÄðÓÿÊ}‚jkbñšÀÁœ_ìê¬7EŸIùýYÎ…ùÄÈÝ—Šùqcã…ñ7®Äx_ÌìfôCðwŒúççpÿ¬RóÍ\F‰Ú“¾u4a«\kÓ—N}7ãù•{Å7ÙÃxœ©>Ì¥n,=~¾Öz¡õ­Í‚ûàó耥UߺüXgÿÔqÀ‡×èûsÿ:‹ÿV×Ç:Äšy°Š~OjçMyÿœìÏ{EÖŽuÒ[ùøY—‹á&ñù±þ°Ž°¾ƒK-P¿rgÖ8Ç?½³ØSU5˜œ«:³˜¿h¢i)Í•CÍÝ_»2ûƒËŸ:úáß0|úÎ;ÌÜåONÙóùû¾s¥¾ùµÃ—Üñ93OüäÛÐÿ§…ŽßyùÙàå7ûâÌ“¿ö£ór`ðÒÙ; ÏËI++ÜsvÞŽ¹k-pÆ|é¤×âÿcŒ Œ¹Äœ¥^WZkÆöØïügŒûÓ×ï3°¾1*kåã\hÕÛ†YKutÌí&ÿgAöG8 Ø_œwÖ'dNÊ{‘ö6Z5ºŠ½Ì]¥ßs„ÚâôÝuþ…×8uÏFðaÖ/½UjKkû¨´Ó›ûœüRø§Öb\éú3\ƒºÀ ?–+\Cýçö¶Ò!&8ý˜ËvO¨Ö5òÑ7¤ýÆu`ÿñ~îÀ5FùüßjݧÐùÇ^áÚòÝpü~uPª OY9ÊEÎpÂèó䌳În–üy\?üq$û`-a|°¾qýpË‚!¶Àºs†ZšRäV1?û‚q±Ž`^¡1gÆFØ£øÜö›{G¾Ü¤û£ÍµæƾLûŒYgæ\Æ·ö çÇiŸpo×ì˜û·QƆàZXÿÖÏ«|îUŽÁhCpì÷mõ ¥Î~æ`+??ýñùnñ°ÿ`ß1ßx>Õ¾ï¶ÿ«}Ìkïü;¶-çïX<ñ¬Ïô&l´Î'&‚ø)¶4¹oÇ¥¬H©Ô½_åùªyYªÜ_°ôw.ç|>oÎOÌé–¾ ú>ª·úZ%ý™©Ž›£üuYß0Á4ê;±` œŸÉïÉ?•®²íl­ÚÊÜÿ® kµ¿1?Ûƒxã«ã'¬‰{r«ÞE¯jíïóü‹Øìï.ì×ÇübŸÀÉÙá\ m*ò¢ç~ÅÿMÜ$ ®·óë¸Vì'üá6ÒNþg*l jk—|fJKz~ÒæsŸêô•!n„ÏÓ·YáXã€þÞ8·5ŒŠ`·XKØŸ«Ô¨ïro ñìýCûó¥“n>HNÌ6œ9F¬1ì?pðÂ- OÚšâ´ŸàWYS·^†Cé‘û[ëƒ=)+ñ‚¤ý„˜ï¨ýGÌ0ÚnÄà_ÂÇ…ýJÞ?½e£+„5ÿkºe÷áþÀC|ŸÚüh°iŽö²o!ðk÷òk¤i@îÕ´|²ƒ­´–¸Ÿ‚‹c¼¥§±³N$Ö¨Ö?ëpZõMk/úõðÿF¯¸ì’µ—>ï_×üÕ}{~ûs_Y9Zÿzíeï_þã'Ïâ,|È^´úö[||õ r¿Ùéÿsùèã[^½ÕNZÛ¸ç…+›ú‘¿œóƒû¸0Fßàê܉[ ¿÷Ë77ýHÌýÍç½êìëñ¼×?ú†zÅU½*kÍF8?àwöîÿÌz&?†çÅz²&ÅÿâœÉYÕ¹˜\3ôÅ¿Ýëœü¹­ùAÜÏêßÃlé«^ÛW¢ïõÌž2À{õ»¸¶·=²™£C~¹o~¨íOôMiûñNÆÖ«pŸð¬ˆUït<º]žKþõ‹ù{剻ÞnXcx^ë‚’åÚƯ^g´M؈ÿÛ5ÉñŠ¿Äµií‚"µúÁùCTÉá|Á˜E#öÀgoÕßâ{TÏ£møÁœmƒˆOÀ¶A7Cñ¿psçÑ–*\Ã~n°µ"ytäÞ§ê«/Ic?€ç8ü ìmkó,›ÏÎúŒ1>Çøàó]Ò6·øí<¸éÒ‘ï~L}¶Ê`åx~ÔG“ÉX1ÏÆß¼ê–ßÒT´#ÆŽÜ%>ï«6ºÏJµ°°ñx>`hÁõSÃ[t€=–P»r½ëÍ:í°¾k•á[J{~Î9͓ݿXúê?kïתay€®Ñâú‹Ý ?*û{X=ìf+ãµ1‚V|œKÈ‘Lßûðe´Žp6I;þHüfÆ·Þ[Ð0¬¤í²×ŸO5©Su¶±R>ãÞ$†Çõ±çÓC/¼¿s¸¶ð7­-Øfs¨èËÿšú±íØ\0\ì]ŒOêã•×:\ªy–ýWpÇ$…¸óôó`O»‡ˆ=ãœùxüqû•O߆haâل郞?…¾µ*Ì]©¶ÖÜ¢s“ŒÔŸçÊNû#uf²aÔh‘ýRmâù!ê¿ûLßZöá9Öù½µŸÝŒe¸°Öû06ÏZJÖp`ÿª¾@~‡×©ke§ìÏmõ}…}çŽÃ€ýíãOæPõ:]&ó›j÷kL!×:Æ_øõÍ£‹PFëÏÜž÷˜sc?Eß=ØÅÏk+ç$êð\<Æ5~o™y;~sPØ›°ýmÜÞ1u¬„1âùÐûD±©´Oü.ôÀùÄ¿?w†¶ôqó3ߺå›g‘»_^3š¿ýÉ3?¸üŸg¾÷Ô!|Õ÷59<ù›¿3Ìy•Ÿù ëúàÌÅyôØÑÛFYKƒôÞ…/ œö0üøÍð±à[à\ÖjÃÁî…¼>WÿĹà3ÌÃ`ï#®„–ïÖ³anñ÷Q?~–ù¡‰óù{ĸwj…ûÖ€ÇXãþ¸/Æ)ñò¾â—Ávà¾Ñ57%ÜfâbÀÐÜ;Õü¹¥œO5Þ_¸üùÑ ¦}²ž¾ôÁÿ·nK|LÆ€æ(µwñ|Ão~'r@Îo­'‡Ø¸þ0½ÁÁ´.ëOˆ»bü1vøžõ@Œ1öØûÏ96?‰±“ú¦œ(¬gÜŠ»¦z=óÉë0UÉ VQìºQg}`þàcí ÏŽœs§Mz'Ž„m_ þõ\l¥Á³n~ãŸF<ó‹øü˜ë{r\Â?ƾG~ãƒkD?Îü~ŒÄ~Zxémê‡WcmÂ׆/u4Õ“ùñ6:HÁ—\_Íßcÿ`}yíÐϺÀýðÞâFL…Pº¶ˆµ´ÈobpbN\—AŒëW¸ØRôÉKã÷EkÝÃÇÜUqÐpkzñÞêA}¢ñìÖAþßwÏïÆüÑpØÜi©Ñþê¹·i/õõ•ë…Ýç}*ú1(lôüíŸ:×½Þš›ÙõQw=²—P¿ŒylÕÄ/vµS8‘#Æz¦ö,ü,ìĘ;äOÌ‹0¾Hr›ñ¬»ÜkÓö/ýw9_\ÏfÿŸkÀ:äÔÀÁ>ô3´Òýœ‹¾-sðÖça=4ÖרúÚ»Ôç†ø¢¹¡Œù¼Îé«ÿ‹ñÀ¼ scþ¬6OÚúS•õ(Í3ZlŒ-C'ßܧ2õ5Xûˆ‰ ÷Ç6²†ß~~ßÚâÆg`ÿÁG5«‰ÿ#~òIìM€ã/ o©‹!aŸ³¸À}o_ÃE %­9õ¥Çµ ãý_JëQyØÕ¢Ïro‹?{ÈrªÂ%¾f;æ¸f®«­ÄÆücmÄî:†âúP]í"ßOzGõçã\2Oƒóógn[kmÚ2sûëú°ÂøuWGo>sëú Äàö›Ô› vÁ=°]»ÔK¯jÚ&Œµ YS–e¬Ø-Ø)ì×ð&ÿáyp~cí;çfÿ ë¯K¿ç´û¼7ñ‹ñlÅúˆ6Vbرëxñ÷&.Å=áŸñe¹Ÿ`[Ü·“ç»|£µ¹ÆÄ×ûÔý ·)k¿Ç¸¾sü•ùŸÔÆÀþ€}Æç#Œ»ó¥çhöCòï5V\+Ãs¶ö3$Uºp+mâžqgr˦S«Œç°Æk¹M˲H¤zWtú ‰1RÉ$ÖN,/¥÷xU)Ÿk=å¶Ø„㌹Áú6¿­ÜÒGöƒ{ï°65íòv4<ÎMàƒÖÏã¸Kk¯õIô¹ÏJ>c|¯mb\x^û;]ŒgÃþÇzÕJãvÄ:χ:}¦è/ôɇ8‡÷6!ì£bûùŒ=}@óu;ûþ æ(úØæÑólna,ªÂ5eßýC¿,¨ù¹[º ©cRý1bO¼¿êNûŒ]4fŸ.æì}÷u‹ÌØ:ôâ/ßd`¿´°n@)ÚnÚŸ›?úÎå Twé)ú¹X#Õ__ÿòo<õî WwßrõÐe++'¾=7ƒØuó÷£ÇßçK«˜¯µá“¡u0ÚQ{Ñ=‡áeÞï#*þ$/1Qâ2bÀ~ëHüà^™ÞÙéKŽwýC]Ÿâù:² ¤Ä<¹ôÈ[ö¸T鉋wAÖwÍnâ[éò¨ÓçB6ùFî3k¶òÿ‹®¾~Ѫ¾4W–}ì/u5ÒX—Á}ðŒ¶u´ëñ|‰'ÌÓçüàý±‡RwæõÎ߇£€û»Z°Ž„ûŒúìþíÅrs…âáwØgدú\¾€óDÑî¦6¯{+”昤ÏMr¬¬².µØÂ1˜°Ž¿òâLÀß–›Ž–¼Y×£Nçëq爥Kä|j\Ä\j#DwócîF.‚yéÜwÉu‹¿Ë>áMj]Í«j½/û7G”ÚÆÀ![÷‰?MzØ:s-ïaŽ1j{£y"-4é¿ÿ¢æ<|\\Ã:­±£ÆþÇ6ï³<çç˜uüW²S‡€32ÔoÝpòˆ³¸—_W"Îñzc?¨ÑçÂQa—Œ¹7æ·6Æo¬1z>}JøâoìGÔµCì_G ×ùuÜJŒt;UË[ì&ú2XßÞ¿eâ³cìý³›‡] g¶h—{ÒY·ªt^ƒ±âì7Yû,¾da] îiqq®MïˆÒçHe>%°2ÆáôÍ[\PÏfìEž½ÑåÁóñšÃ|%þF\ížÂôë­M[`[i]#úÜÒ¢cn½ãÂï6NÝVz1O{f¸çW!.§´ƒœ—iŒpáý£¿ü@zƒ,¨ÿE«Ø•uãu4¦|.‘‹|ÝâŸñ“Tg"¿Õ~µW¬uÝXÙ<¯Y~R¸ù|´露¶ »¾µÍMìùù¯7p,ШNJ9˜`¨È;óAœ»j;CÍhýž=ÍGæ 6âøm”ê™-=*|nîiÕ-*×…ýeŸ¬è[G̼ñƵ‚¬C±KlÖy]Ä3ÀÚpömŸ\ÿ¬˜Ýý½Xé5›WO©Íí08Õ'\ƺAøCéádœëw3~gŸ½~òà÷-ÿpáa«§Ü뫯ÿV3óî'~yí+ƒ/­=ìçwš}ë/‡cÖCŸ¼rßëþx¥þèááf¬¿ú;ÿïå«§þëß!7ž5ˆ‹××kï6¼Eï&ÃÁ§Ÿ=r¾&Ü%Æ[¯ýú=FÀÆê“îqt h›Ð+㊽á|Né³§LOYi(~«•¿°³ŠöÔµ«N%¯Z÷™«œ“)“{Áz†Í…o¢þA;Œ±¨†5‹ˆa·iÛ’·‰õ'‘yöЃ߳@Ýñ8áË"&¹‹ß»F9õ¹…õù{¼îo»]]+Š1îÛ—¼Sb<ŒìÎJØ®œëÖ¶"Çç[0„±´JÛGŽûá¶Â@Öã7–É!àÌÏk¥uAþ[r(Ô¿VïºëÚôr_°¦jò êá÷ËôFnÓ»=¼jħ?Œöfö 9Äßqö.P÷Úv¥lîz)½‡#Ä£_¯ša°ÀŸ0ÿªMÜ_…×um?ÇïÍodïwÅøâ·°½ø<êë=I]õx”&ÅØõ¥æ>²~~|×àîêpÄqìA⯈RW°`]¹ì_ø®XëÇÜÛô˜õ­agá7ã3ûÖÄ;”?\ÆHUæxG¸'×tšíK­ücõŒÅ<:~eÝüØîpĬNŒÃ¹y¾_4ì{sŸIWóRî?øûö Ýó¢ÎëY·Îæï”»8¿u-V4N¸ÖÅ{ýDkß«LÞ*X!Æ~rŒ© ûaߺï#yãÒé=‘ØÜ5jêûc`~œ·íxÖÐáó%'é8«N{sýHssˆûìáüº†¥Œ pŽÆÛ‹9‰áE[ÿ†µin?ýZk„3ŽÀ:„mMÍÑ1j)_çÚ;ò¤Ë¬ïp+mGègcž5?©-&Ü7MûsÌÄÈcóNïÑÀÞ… €¯®XcÃõÏÄ`à1·¡^ÇKïÏŽ Ìc`­‹Ô„ƒO¿ŸÃ>à~á$×âV{v©‹ úÔ_T~L¼'ák>gÂf ¤Í➌/k|܃èÞ%çXZK„ü ìØÕ–LEc„óÿ ~ æÀ:]ôT´Ÿög›rÐm§uã8ŠöÕܹ6½¦Ã/[Ck säÞD¶ÑwóÍ?Gþ¹áå›ó0øÞýo:ó7þþê÷_ô•Ùgû.—_ýé¾÷ŠÍ<úó÷[ƒ­®ÿæ;¬l^sæ}7ß±vúMÞŠºÃá ªÏ •bî½zäçïµ²qÏ ‡©S¾ã7n»’=Œ±¾Ñy3ƒëMS3k:=îJÈ5ãÐþaŒ~›s~Ú¼6üùÇ—ÑwpŒ žçX=ïß ù/ë“O¥‚ŸãÞâ7P7ÎûBz.ðaqÕï«ÆÍü3â?à[à÷ªƒ^ê4tÆîŸ‰÷Çþ… ðùÚÙ8àwîÀ<‚5Ök‚ð,Ãøº¡.ß<<ÄøîáÐá ñÑÒÛï‚Ï­cº¸ »‹TXí¡6Ú‹»¤åÚ˜ÿ;k¼6ÿ¡qίòûìwÝé—aÍ}â‚5³¥½5×ñs„'®·®[ïx¦_Õ¾³vX®uµ‰_À'6>w ñ(×c¸¶d©‘ÆÆæç£5WÃñšo½¿K]ƒü;q¦’çpgÝ瘸׻ԣ'˜Šu`7ØÜ}ñ’æ¹WåŸÖZÛEηwùCÄpÁƒñþ3×4ÆÏ›hQ[Ϙ°ùóÑ©#Ž”Ú2ÌQzô:fåÁïSs›ÁâŠNÓ ÏvpÁ=Õ°vÄ5SmÖ ö€òIÔ4Hÿ™ä^ÕŸ)ŸVn#ÊÉ­ô?zÝþô»ñ Áüâœ5®lçRý>Œ§õå™zÖJÚÙ¦6ÏŸÇ5ê eì—}eÚíúæ6¹¾ô µ)‘‡Éÿódí\ž!ößi£t^íh·´ÿÃ\»>(¹¼ëßÂO5ŽÜªJ=&hÊØç·¸þñ9Ö_ßÚ?ò·SÛá¾îc7ÖqgOb\±g|>²>ïÌÃ}Èq}B§O`ÞŸßú/á¿Ö©OvÏö}Ú,ã÷O ßŠï^ྺX·°}ê­ò ç~tøÅ„tšœCâ|¢uޡީþåì+Ûº~±Lœ»‹=R.íüjÕ²ô:¼×@\5¡¾DðUìCí÷ÞY¬aÔ·"ff^O\8õÚû››Þl{ë§ß4sæ=&g×/½Õ ôû7ï=¸öðƒgtãzHNåô}†7žùòpsÎÁ§†öouäï¿?xþä`„{â~›c;RÝ&y8˰ñæ‹Ðgtoªéœ¥æ“Õá|§¦ÒXö²tDÈI«\Ž^W˓ھk÷  zã¿er_ÊìèrÞ¸‡Ï¤BuFœ¿ÒX²ãæäœc›Kþ;µ´µøÄÂÎðÙv_b¦_Ï-£ÕdÌ•÷Âþr½imž^4'‰{!1¦]8ŸØˆ³Þ3·“}~×·c¼jÜ;ù§Âµ3æ9©&Uñ؉Ä>…ëáë ¬;íÜk™> ¸o°A×4óLw-sbœÂ¶šŸ».3˜^-±Ó½¼Ä}oq€–“ ^ÎXz–ÉÏ;—±Íób§¢UžDa=‚ZšO;sÆ[ÇEš[©Ëp/çÆ\à"ZùëÐë!ZÛÑ æý«ä\sTÛæ·êq²”ú†2õÂÑžŠî¼}ÈÒ¿KÏÅÆöµ\pú1ë(¯MìÀÏÉs¨0_º»wvÞ#\bØRÏc!þ‚¸gÁ1=NÔvÛºëç6‚ÖÁ;Íïn‚ŸûwÓæ=7ÝŸùP®?Ù·>Vâh¯«ô,õ]Ãf«Ö÷•KíEr<Î÷q½¹¶=þgpÚf§ßsí¶Ï¢©Ìizã·Æò'ÿWú|‹Å1Ș&÷Ðwÿ6Q­×²$7\ «Z̺«]sJ}ªðƒƒWmñ„Jæxòxyiݹƒ5-ë¾4ØcjåM¹ ÏOæ¦è›ÿc?aZù’µÔÜó»oþä1ëõ;,ö®¯v/$ï£r[îø€ãür«&X±M´ë.p?ª­g¯Âk,u&ßø†ñëÄÓÚp?ñA»ï«>?’Æ6¼»oGîa¾…çŠ9¹ôoi½‡Ìãš­åˆûwm‡ƒ¸.'±[#­ÈµŒWí| ׫ñÃ.Ç=vŒäNs½cÖâLO)Ÿ©á§Çmðÿíµ«¸Ïý>rõèç±²»xËÊÆäô¿þÊ?UœYÁçðÀÇ;ç÷…ó‹gAŒàý]Ã2ߣQ~n>5·Áµêh%ª¦ýP“$÷cœ¦ôÙV'ç–{DCfÁ½t¶p7ÕÙõU²Í¾Ò^rÝb샡gϘ{åúÔý]qìè–&–´£³F¤™uýSÛÌ^¸N…±§Ò~W®FlÎÕ]Ô” Ÿ vÀçëÖÚì[çykÜÅ‹ ®Ñ·ŽYÖ¤9¥}ç.Î@ß=£’_Øæ•Ùæž,gy|jïùÒcßd¬G×Ù«öK§Ã/«v¼š¾µ󛬓ñ–^U“Úè ×@{œ»¼Kj#ÌÊý†fz´—ynG£+½$¢i’±™PíeëZÔæ•±‘ÛìE“q3>Ì1Jÿ¢œW}ñ&ϯŸ<ŠmײqÇnãý²NãOg]畟«|î…Ï’+ò™Ñ¤ö;g}>›Ïoÿµ±Îhx"VÊéº3n Æè3lÚ¾y&®ËY¹l³ÜwîÛ~] ‡m¶«è»sâ<Ÿ«´!^?eì,Æ/œ)s/ùŸE°|óKR7˜¾ïµã÷œÅ娼Y㘸6¯¡ó-„«¿­yÆo}-6;ö¨0/µLn^¾îF‘X=vMq…Öé6¬ˆ/”ØÉ8|“x?¸™Ï·î»>«ËèŽ$þ¼`[‰Ä ñ˼O›mõœµójùç÷ ç6×ò{dïÓ/ÁüxÿÄ›Îw¶ùí}ÁøQuž{,Oò-å.÷ÂÓ˜‹W·àš¯i׉tçE‡‹ÄÞf¯Z‡'>cíëÉeôÍe_ÜóYç9›2#ìK5dŽÙÊ`5η¶ÛΆ:÷Œ.‘¹%ÄŽH?µ4×µQÞX±¥Ÿóƒ{º÷žjܯÍÙ^¨oæáô8/¥‹Å\,÷,~{ÞC¬à^óO¿áÊä Þ¶r‡oÿjtÓ—\8ü“‹VVð}õ?N<âƒ9ROÝÙÁÉ_~4ÿ]½6öwëÇvˆõ­ÀÑŽQ|ã‘Ñ;húîÃê}Ôåº\ß]ÆÞ˜+ÔW0)¶µp|Þ„«â³>=TysAjcêMâcä+x½4æšoë!Ö#n™\’ý³rÛœÉÙ–•[ï-ý2×CµÖÄPN2õé=sÒ¾¸>º«ÑuŽ RuêJ‚ám󯧃i§/›kLi'‚7Õ/ ø¼1Ž®uìžÞOMßøÊ6¿)Üþ=¾ð™-¿gGc¿¯ÉÙîýÞQëx˾ûT×ÛTñýÎàÄ]qß±<ÆA8Ã!ûk]_SÙ8q‹í›7 v´É {ˆ¿¿Gûd{þwl¯ý&×î«¿`ß}€S'<–TÎÆ{“ëm»Ýß–ÿÏyÝØ‡£MJ]í¸ëѲèZKáéÞgö—‰¥4±ßÆb+»³Õk°‹O2'± [ØÄ\ÎŒ:Ÿûw˱¹x–ø%Æ—rO«ï×ⶸfÃXìFâ«pè;óè:>€}õ"µÛ[g•ðtÇï‰kK½§ò¹¶#´CÑŸòóþ-“Kì»ç™y1Þ£‰‰[–©añ^)â;É/Qÿî`;Ù³‰…„·ˆ«Š÷&™}›ý”úpcÛ<«·Õ-„÷×Är¾Õö¸—}ÛÙ|ó¢ SGwÁ~.ßïú˜ºÞÐk‡œ„Íû ~°ë· éSE?5óØ·n¸µ=§…'‰ÓtÌš;É?n='ïW†ûÛ>…1¡2ú Ǻ5ºtw]óú¶]5øŽsg…c&Øí‚ë]Í' –Äý†=j-Ò¾}éºkØÊî{íðŸÑUÎòà¾ñ#qôØu|™ÏêØ,û–Á—·ðÞÅ"çbÕü]ž]Ö2 FaÌk-øêÉéc¶z<¶²‹×ãc,Uïy$µ¨Ù3Eæ!8ŽŸ9ý›ð|V%ÆHlTË7>Q*{]W|2>]߸±yˆbSù|Ûöã8’sêþA1ñøeÆ*u‡®S)“R\O[\7 ®âØ¿‹“¶öÛ–]p¬^fú”[6h15Á½¬sùë|c;ï.Ÿõh‡Ý8G”ëOo;ûÏݲ+ö¶i¾Ð6Û0«é÷ñ5N˜8£þïzü\¯Iyu\:ï¯ §³ űRéû5öÙÚì+ã[ub}ŸmÞ–m ¾*œÜ:ƒÁÇ’+*ËoƧÛ<2˜Þ&–žéÄ£â7‘¯“ó5{aË~.¦^¶ˆ.DÎ Ç Æâ‰›ïIï N§9v=Ú9o÷vZ¿ÏÚ+ì÷ó¼ˆ}³­*“—2ÖÖhϱøvþmúQ–ö_9&Ž#xoÅ;/úÙU3•\ÿïŽgAWGèµÒùd»Ü÷Çv£öød?²ýìÆ1 &({¯yµ½Ô¸–Ñ»N,L;˜wx¸És#v|Z ëšj’2» Ï92¯ËyÖdÄï–œ8&gîv<+þX|vÍ]¯³µÑóˆ±?_zlŠì©øõ‰Å’7ÈzO @ßbò*ò;¨»Åq¿³O ŒqbÍëÑ7Ì`®?s뙕7]:3ó«ûÎ<î{7A_ß_þäÍ3ß~Ñô÷úáÌG{nÿ˜áæüfýïOyüÙ£W~°-?âàBG%g"ø—ÑÕZÛ‘\AWço½óFûS{s¼Õ#b&ÖÕg–>‹»ütߘ¬|ÈÉø¯]/ÓÄqñ=¯J¬‹ï%§‡±vþ›÷sÝ2þ½ >nîda¾xëø¸í[?&¸Už§Om“Ýmö—ù ±ËÓ‰’'é3Ï,À1÷‹òúáܺßVjÂëóyLîaâ£"çߨ¹´~§#?ßù˜x~k6·:‹ç»˜ÂxR£=²–x‘úuxÿè…bí¹v­6¦ü²ÊyŸ¸¨/L´óõóÜŽÂÏëðSÛÝ.÷—³(ë ɶ¼Í´ñ‚ø"Égcó4Ó "~Iß9M¬qoc§¦·ãÒ.NÌ.ìýxÎöb ¿ß(wÚÖ–>ÓŠð¬áàó± NåßÖ—d¶…--Æ/O~¢tn3˜Lü¢ç“sž:°Äâ‰3Ão:&ý·VvòP°Ž:|ˆkØ[DøYb£à­Ž›K_§IOÆpgïŒÍUÄx9¿ÀÏÃ1ˆ¿Û—vbö"u8Ûxé÷L;žt¸EY_Æ”JcIñ—êpq¬Ñ×iÅåü ¶™þÐÉŽÍóó5ÖŒ¨â§õÍ9ÀøYûªˆ_ÌÜÿö½Â˳ŧ/·âúø¼½ø>µs^µç18)± ¿Ã´÷ik;‘ügxܱW_/þ¸Ÿ£ÜnsÄëiÿ㸜œæÜ8s“: Çç,ÏasZùl¶Û…ózµýò` àü/žS°‹øÊ îumß¶L¼cÛZÃrŒÜåƒé•É3ƒÙÎóèòYþ“Ï—^†:æ:žØöXÈvÃÏ,MóÕëø^»MÎÄàÎ[’«ŒqHÍ¿m~ç§z5ÛmOü‹ØúmzwMr°á¸aqýþøæ«Ç©ôûꧬ­<েó/^|Ê{FãÇ}<š~gÔôóg?©ÍYžœ¾}Ÿé½œ¹¥Ö ê“úæˆälOÌ8¶~Up%ç{}k¯˜G\¨È™øgB˜®©íN—{Þòÿ9Þ™«·Ù¤"}hñ<Ö+hìu8AΕí8¹¦äöÇÎSÆ7æ;¹¶†×H ”5.¸ïŒ!—Áß·¸(s‡Òãòÿ‘õ.p’eU™o·hˆB Lµp…* ­ÙqvdfÉ€R6¢´Œ"é (H‰(…ŒŠšBW B*`Óø€›#¨HNœ‘ÉãªxAÉäa/IJûB©7÷^ßí]}ùýúGwUfÄ9û±ßúÖ·°œ·žº…ðFñÚ N:QŒØÃ±Ó˱¼Üú±ßÈ]›è®uн;ÎTÅ_¬“:³òÿdø®ñì*Ž è6-ÿã} äQÔ§Í®(Ö:ç:¯Êù¹»)¨Ö$á5tò˜}Íòn°†¹Î§ë¸j/XorQq'/(Ýhó²^6˜X)ÍÔïClA>NO0]üŽòÎ2Ç`ÚæÂ1e›7Z$Ì2ÄV\Ø8U¹ö' <¯Š;[\Kµ×h8 ÎÃ7nxK-nTγêÅ‘ø¹Þ·—Ô…á$ä=nø|]Ë{Âoñ ÔOjÜo|îvËÿmÏYƒ$æ`Q§£V¬~Xá ¦3Bާ˜L¾àä̇\5« š‘!l<·#¿Vª»ió¨ƒÝÏ)§Oô<)J`Mcãã÷è¦`9ãÔ{uFœëXÏà\¢ò<ªÍÂiŽòäáÔ”Z¼T¶¼ð÷<Þ•5Ö7Ü8¯û¾¸õMœêœ5i”{¡¼­—ˆØÆŠiõ=ë/þ'Ÿ;t@Á‡¨9«Þ\î&˜^p¼Õâè&é¨Áלc„Ï&–'ÏG“4†Løðß#pãl$4uÖà$Ó½;æñPs’âµ)9{\ûÙŽy}< Ç*ÙŸ‘C…·r6³Üç/¾ð'÷òwßóU—íÝíG9ÇÌ!(3ê_öø¯ÛÍgpòg¯]þá÷Ü?kþõ²É>{í7i©Ûúè~E¸äœÑ :9ëKÞ'LP{mNü„¿û$×'O"†SOΪs…Íèx?òtê”`?ì©òúßþßb4ú‚Õ5Ëž SLÔ8GÊ)Ê^j ošüøLôqôîå~hž'÷EyÿEÅ.•ßO¼ŸÞðÃÉ ›ò¸¡G÷‚Ø}fÜ1«“›©¾’š<²¬·8ÍÛ"ûÕƒŸ[îtA9^áëô²·IßÉ}b¶'¶Ï}ò ¾06€œO½paù³Ä¹ï£ØÅë=÷ì ‡^fÎÜYÍgi¹Ô°±ðƒâs½.C,…m$G$'¤î¢e¦¬ØŸšŸÇiüœâ9q,¶‰£egFàåQ~±c.98Ep®Ù–¾w¥àsù™ÉѵQ¼0â(zߢÅÔÛŽ¿*IÍs–Ÿ%¦ÔcC^†\Óú®ÁÌuÄAœ å[®«­Ã"žÁ&Si4iRpíú Í©1ÍZÅ·Q¾H¾øÀcó©Û^c$.hì˜bÓH$QÜq±Põn8oÊ!ÜW ÷Úƒr†™]ÙÔÙÿî›çžƒƒÐã¯V Qü,ñ¶˜ú–b¦)|ŠÁ¹Q[`ÕZÊ¿kÓ¥¸Ï&±hÂ?Ϥ |^3¸ÁWà¶“_„†C=˜Fï‚ø°‰/F«­93®ŽïÆ&)÷p ia±‰-Á8Ê?ù¹ë™)õ£Kpªï§&A<©'±Ô¤õT)§íÉ!ñ?ÊÏéå/ï—ï°°™bS 0ß‘óJÍíêÀšØWb`rMü|gOÌó3ßú…µüÙo}Ü·¬>ö3/Ï}÷ ÎK^›~ìwWá7h–+ø¤ß1ÎqÕ :¢žqŠmbo¨ÕÕñ Ê)zì±8ó^ßÐÝ…÷¹Çµ¾¸ãÜ^jCWi¶DhpopVr¡™©жÍÈÉüçòse#ªúÐwD6wSê¦ãè¥'PÄ>‹`+‰{kŽa9S»‡º‘}ý+.h0 mî"øa'®`#VœÞëËù!Ï3ÛqÛ6e[>?ö‘^7ræ ¼ØpØMú:í5AÙL«ó ê©AxdS±&Z/ǰCáÿW.l•ü—8³™94ÁßÈï’Ó‘ƒzSï³'U,'l™ÍC::¥×4¯‘x8ŽES7¶ÜÍ8õûêsÇ KTŸæÜ·oõãÈW˜sI\wH¿Ó`Ô–{×¾ « Çp\ŽéLýcø±}Ó÷GîÁžx|&\ â ¡»~Ä×äÛÙ(¸<ö¶ü¿t‘Á‘æœ5jyº7eݨ¯ÀOãßÁË'»fç‘ü^>XõÃñØ’ãqÂüÊ]'ßÌZÓ MòMŽS--+ª^á6[gÒùøA½8C™óù¶H ›÷ƒsÎq~»Á{1Gð&p"Èd¯çè5bsàˆç¥õt¹OùÔå5{Ö‹œH³?”C#„ƒóG­ß†&û+îÇyxhWm>½&V£§/ìF÷Þó æ4Ö[Ø÷ÁøÍ)xOlÁî’r²ÎÞåˆÇúM]|òÑ—½u=?ÃÑßž®ÿø?pý6÷¼r/þÇ+wóܽ§þÃÛ»íú÷®ÉŽ­îô?·–µ{>ñ©n™ÏúŸßtûÝq™Ón=Vùßsî™ëdšû1•OËÏ\î†4òËΈò+‰–ø–Øìœ•5Eß[XДv^ƒÜ›®3êx±ì0z´ø_8ÆðñΟiÖ2ÜΚ|¥×—¹ë7%°¦guž4þlª|º|¶¦:ü¼úŠ•TíLj³ÇŸƒûzm“Y0ô… ÎÁ¶üFgŸœ¡#¾i°šÈŸëìuŠ•ÁžtÇóIäÊ¡h»›NÍL}€A8£r`¸©½xdà¦Ähä6em-·ßÂŽ ·¸_379O<¿÷U)~klé¨gÿ‚Í£IØqõNΉ¡Ðh‚3$^*XUOÎG98ßø^Âd¢pOùŸѳîùœ£½Šÿf}å×ëÒÝïšçˆp†°ù²Eeïèäçð%Ú;ü”í‹ÕŸ#¯˜ˆKK|K s!¼³Ä yÈ‘òß›žéHZků:ÞBÍOþÀÏ.œ>° îXpLÅû-ãn/Ñ Goyqî{‡R¿/u¸÷—ì¿éþ»6VO,@_…â¸6ö"?€«Sž/·¸DÂÍ'ª¿|¸ð‡áEé¬ú߃M‚*?&Öuüˆº6 ÔÚPñ¹“î•û@lçð¼æ"‘ósF+s£ì³âœdñö1rlõ1_,Ü:—Úü° ÇÌ„Kp÷æšOù}árâäZÌüœøIö;ãxê ÞWêÅŠk:jKð*:¢F൸✒wº‡žÏÕb¹×Cåô&åÅ~—d#zÙ%õÍWªÎ¯øÈ{œšŸè¸Êžò,—¼ym^—Ó/{ù³ŸûôŸ[^ñ¤½åçîwãÚ/Ý4^Ëúƒ¿qü%«oü‹¥âÛ|7§Î=xÕ´‹Ge6bÖ’'¯ÂŸƒ(¾¢Nœä×ÐÀ²û‘½• ™hÝÓg ª÷ {púš²kÐ ž‰ôáâPë]Ý_Ã^d#‡6Ûbs?Ñ<ÃË?öVSù@áþè.±æ%^°Ùæ+%¶¼zÅkª1/ˆ¥Å»‡3•¸—8ð}#®¯öû\¬~a+’w£ù@ £{Ë™Œ5ï0˜Y­e—½R>Ø3“¦ÆBÖ‡KüÄûˆÛ‚N1]Á½r.K?¨0Æ^ï×Ó“!l£“íÇ>MÈ÷~E±uíÅVŽ»ÑÁÇÄÎâW”W—÷Ô¹i¹¸Ò+ª½ à2²QÑêï6g”žrúŒò³H{> ?nûØ{0õ¡êÔÐÚÔ­_N'õWí‹ÙTò!a `µYî89Få¾›^œr¹H§:l;iù6øz_©Iñª8㺓`ýQÏ6èõ .œz&.ÉmÇÒ–ÓÍÆ+LøÂ³š˜}¤.JÍõèTڜ΂ÇÂZ‹©ÕÍgR“QLÖ)¶ð3Hì"ΉúÃ7Ü UGá’ØŠÚk^c¸ÙÃŽî[ µ¯HlØâ-Ô{áŒ5\xåÆçÌXÃLÚÕ>Ÿt=!ñÀo¯Ôý˜Ãcâ÷ƒ¸÷àeðB‰û‚k&x¿DÉoڰΊç·ÌËÐ>Š“±#î5\Mø)†añ½äÁj4ÄâoÅäŽÉ µŸ¨áDlxÞCî<³ɲø¦™tˆ!廿<Ã~3ÇÛS¹Îišpž«/B×#¿ã‚\zæ½#Åî¦%,Èãü}ÓÞŽÔ›õª75×*ÖYÖ;*,1*Ž’ÛA¯¬S¬[; ¾örNé_Õ}íj޳ѡJ£}—ý³>\ÅP;ñ»ƒæ+o‰‡Ð ÷Þ@ùÄbÓŸø¯*ùÿ5ßÿýË;?ç·–ýþß(<ë{{Bžq²Äï  ®žúòÝYÃ[ûW|µð*×£Æßç5²Ëlv‹»Âï«7üN‹ê¡ÅÇHG<¶v$\ʳÄÖ”XÎ3q”l‡r±mðÏÏÝÒ× Žzy÷LNw¬œ‘ª'âXbî”ÄgHà÷äðøŒ ÌQzàJÅ27õ¸æø¡Û9vm$›:–¾¹œ^Åhåü¡Ñ+m›\Sè¤ûgÙ)lxv”,ë \°*ò l·pÖ²n²õÄüåߥà WÃ¥mf>ÏÑ-Äf¡C¬õKh`É6æs7 ¼°5û®…ku„z6+Ø Ž£Î‰cÂä<ë5o©ö]›VÌ×í)z1 bφ»¥šÑŠó0gâHa˜ò ¶I9cjóex'øj;GÒÉ(ù¬ã<²Ež—ƒ‰‰Ào£GÝø.ñ¬4¤œ÷ƒ_Äwåx”üƒš’Ýç6α>HËø™¼|€¹,àôÆguÚ©ÿ| ?F±×”€uô-ö¬»ÚéãÈ®—½•F =q>«»Áj]ÛA}”Q9¾Þâ„}\ d£#žÔ½Ÿp¤‡ã˜/%j[Ö›»â6½úÚ[VÞ“s4ò¬¡©Åé¼—ZA¼!¸«A¼Lü:uØ ^¾îu'Œ߇Aßþ^׃ÓYéàž‰KŸ²ªMÍœCz®ù{×tn%÷]kÐ ÷¦÷\£¤3ší z ¬œÙÒHÜ1ÔÍòÝä µºE¬ÍFzw+™Ÿ?ŠŸ,g’ØJvÛí0kP±Ô}gÕN†Þ?Š_Åz7½iÛŽ¹±Fú®ÎâÚ ì˳~/îßÝéÁ{¯ùÜGö2ÿïuýÐêÍøâòÿº{XýÚ7þXæüw?ÿ[þà_°›ß¹‡ßQæ¤ …ã›{c/ ï>gæšj}åYó—oÁg‘ÏùL©A}6óp>V$nêã!‡# ö”ã»ülì‡ð×$=œrör,RyG›ùéVOÒ<åÉ®Á3ôŠUä;÷&üËk ÔŒÊÑ‚ÄöqÁØ™•¾¯8›ŠåTا Ì Þq_ýãnàGãXóX4§Ž}Ë&¶/÷ßo9üIâ[ønÅ>h?#õ,Ù _'b Õˆs’qE6…·f LÂëõMN9â*æ'òÙÒ ôÜ®á]â¦`•ÏÆÜ ×d‰m/K­“™&½b™¾`P/qSk,5îñ)µÝÅb'*~N¾±WlÿöeŸ[Ë~˜Ó¯=îO¾výÕ˯Ù;uôî{wýÊ®½ê/þûÞßù¦ÕüŒ÷¾ÝxõÍß4_Íõ€·é]yÀêM/¾ã.œ‘ü~9ÿÉ|À;hÞë›=Ó\Oõ#•÷Íw˜lfún FåÉíhžlíÄÃyGEq±É„XXëXÎÞ ^±Ù%½³®AÝcÏážq6ÑàìÆçÀF»=m¸·s{÷í¨ÞúZzì¥0R¥7Uç{NÞÖÄékê×Ztù½ÿ§‡ÇóTs;›…4®³¼62“¶4¹)=»œë9u)ò‰®‰ÇS½O–+Ñ/©çq NDÕ±>^ð}Öl‹ü¿3ˆ§šz)=@?nËÏÙ~Z\tÔçÕ+Fžh½™=ÿ-WÇ'à.À+ â£è¬ôêÿKäócçaÛ~’_ÌjNßÖO›:ˆëîKcœº­|‹óþ‡F[0Õ@Šfµ2ÝÓòyÌ襖ÙR½Ÿàóæ°Y G"ÁsËÏÁ¬« ý aâZo1,šWÔÉуjoÊ Ó&þ̯õUnfš@äƒCí%Ÿs>ıuCþQÏëõ8Å6Ûž×6øI_m”÷x.ÖA_öKvÈß]m"æ²µµ¾&Õ× ÅøeøsüÁéîCR¿¬væ;nóF£àœÞÝæ´ç¦òvú¦N¬8'Â[äÃM›Bv.qõð?ÒàB+¬¯:¢>‡uañÄ‘©õI-1½oôxê<‘;.Z­fo/öÔšàNÉ6â×»V·Sñ$µÈ>TŽgùYü‚|COj(k’ØrÌÆžp;jN<ý¼hÈ&¹£üS4WŒz“ê{;k·!ûSÎüBžQ]ò&é^¹~2ñÇNÿs»ùÏóŒêç>ýK‹«ÞûüµçÅ+×òÌâ7~þÙ«¯˜Üo)NVù¼­wßyõ½w½ïjÆÆ°#ä²äÆW¶Ú…´XÀ"#vŠXÞltÔ9,ö£rT›Ÿˆ ->-5mïùjÎQGϪòÞr.³þ±å!ì3X¤á)šAÅû†o¤ý÷>õIÎsu&}ógæøPW®½-šw_±ÜËb Ð¥ª¸³.=ë&âÝ»y¨ºg:g†I¯Usm¢<¾ãÞÉvtpñÓD|&¸:s,Ïøì±QYcÃ)«n=˜“|CÁ*f¼%›k¸³ø™žSr7eÁ»»&–è™bg{‡ú¡¯OCŠFoažµ2ø‰um®íàZ+}æ¨p8/ éZ'[•¸'Ú§ž¼›ÚñLsZÍ’¾õ±Äû6›ZöeЬáÒ$4îdÞ7ön0íÙ]ç–\Z}"ľh†Da#‘| E¨|çc)¦uN ð+°'÷ac×Ë%ú¬À× Ùp:©cëõiÏvÙ7õÇY3þùX}]ÆÍ¯3㪆5µŽMz…:b]åµh,”ÚæPôuÏ]Âã쨬|.±ù_Í37ºŠ«ù(f!—.v“û{á^Koú’baîØ~lðéfvfáa[º&—‰A½Ø!ÖR¾dÚ¼CT>챫t,ü®¶ñ­øç`=¸©îQŽ_˜„>×\u´âS˜©G=(Hã!LjâE9_Ž×Lsœ™’×\þ¹Ä…pÀÐ`'6c–˜†å#Îzö!^/Îk“»8W‚šì™¢ër56fÎZ+gˆŠ¹¨õÐ3ÞÁÅs íA=Áúw榊'o5ûYgIR;cßge‡aó¹}¿õüÜ_ñ„?X»îsß¶žçû¯ïËgŸøeÖûÏçïãOü±ÕçÿæÿYµÙf¤K²U8ïºgé0÷WïÃÇ­GìÄ´úšÚm}µ›Ü+ü¼Á¢ï€oºJÚݺ·Îý&?ž‰Û‰œUÞDâï÷DÃLJ~2Ç?’CK;/aÓt¦¾G1)~»R|1ZœŠç}¶;µ¦äÏ%&$Æ´üꜟïžòž¡jÅAzí¹楟IvgM[5ß!0‘æŽ;ß ;\cÝø sì,µÈÁ´¶àæøLYr ñdÅá(óÌÀÜèÿ™“{ ª•sè &§ µ®«g©}:Äè0ʶ46Ðz©¨ãÉ~ÑGÓ5½QÂ"w YÂ.öœ ì%wE1¼ò‚¸¹]ƒëÒSbú"è¤ÏÄÕÈ6lî¼i{Œ #ï%3LøYñàç M™¼Ÿô÷“׆Fs.¸ŽÂˆÜ,’ëSÛ{ §˜õ˼¸ƒ¾éEŽpW\’™Uåçs¼®º.ù?8·qËóô}ëC‹èMµÖy(>´â 6åðì’ì¡fžÖ™ÝÕ?Z<\sjçID¸ †K™~7Üz¿‰)ôL Ž­8†`Þษâd†{ÌÄ1&.;mûM#ôm œ0*OA'X1AÕ^Õ3:6†ÿãă铛Ú]°ú.¾<4Ú_ÄÖêg-ÏO>„錫X± âóÃaG#Á±üýÛà"cÓÈ^Ðã {ÙÔlOÒ{àú Ê+[ÝaŽ¥¾Ð…ªkàñ6}7ØEÖ<˜PYoñ`bhjTØ%Λlï„Ùõ^ŽàÕ*Ú¹$†#ìÐbû3š)І[0 gé'nîÜ´öŒRS³è¤?ûïÿcWµå_]÷ÝËß¹|1­ÜœQºé>O_†¢STê«ô9ßÜHùe§ß#^‹Äôp¥Í3ÉŸ‡Ž°jTSÕH~,¬5…ZƒéãGø`Ú“FÀýVêg›s ŽZû¥j¼ÜÄÖŽÕêÌò.å3Ñ5!ÇϯMpct¯ÝÖáSÀgÎø ãHS ´8Ïæ…‰ó’À e‹±os08r9ÖÂïko’îR©_e[¸ïúX+ œ&ÿw3Ïz‡Ž–8 û"ÍÕfæŽåSÒ¸ŽœÕ >¥üRŽEÝ W-¿Ïlò&p®PçéQ—S=Üêy`C¦=eû.L¶àAä è#Ê?2“(éòÙVg:ͼÏbÇ…K;Vj~ÐzñýøT80ʹ:ê4`ªYF¸ ðÞùYÅ e”+Eb{>ÓjÊ¿'Œ×c̦.Þ©¦3Uÿn¹/hÌ`·óß3¯ÌKçýÇÈ9Ä?ƒË“¿ÞÏáý>p<9ƒ‘^ ld­)vMìÂܦÁôƒJŽÖøÏ²æàÛòUÞ“#¬GwjûÐK«)Vß5ß.gî û¤3^©â{Ãúóþ*ÿ*gžÿ¬ß;ç!13ÐÎr~'øçgj/}j8¦åþHË¥p™•WðùÊI­F”ÿŽº±¸y`ß}ÅvVT7ÞêÁ ò»f{€­ _94\÷i¶ ê¿_à×Ðù ÖCÕ'â=êˆÌÔûÎ;ë‹Áùˆ[-OÉßEØz~®òÌüý×µYÙ^Áy ê+i⯹za´'åîDbdõ,øùœw5ùR?SŽÎ{§y= ¹¿Käñäe‡Ïµ7'r¶ÇÞ³¼UpMêb³Âñ.3¤Ü~õòvAýüû6[Àgqéî÷à2ðh8çMŽ77;¸é\ü¾¿}‡ÍõüN¯ýÌíÖþíŸï¾þôïýªõ_~ô¯î½ëüµY  hžñÆé‡¾íµ«·¼òM«w}Ì5»/O͸@Öü/˜ú~™!ð ŠñÏå¼fA˜{„½æggöc>ßʇÓP{þÐãèÀ#É5;÷’û oö*Í4ƒ›‰ %ÆÕZÌ[|š¼õÍŠgz8¨ø>Õ ¾NœúŒýïñÔt×#±Ê¥qÏï9;Íïy?;q0ïÆÞ'êì÷ê ‰Šè‡HªÅ¥Æ.ª~´ãy‰rÛ(_UâHðTù¼9:¿øŸ†ÇÚõÚÂ]£ß ¼¡á”ó ­dx:²ÃV›ª_ëÝÂã¿#Cß§k[Jý¹ÅOÁÕó™ÄWͪ.Æ„ýn1ê z?ý m,4¨7¯›â&4×{lkÝÄMa&­òæüÅ&'ó<ò8ûE‹7Á{ Âé7Þ;ó,ö㾇R¿z#¸Nߨɮ‰=ßÏkHß yj¦@\†ïÔA,$].ú¡"½äÞØáFŸ™µ‹¡bÓ“ Ü–X¾,qB¨}ý}hj̼›°Ê^vÏ×½×¼^šÑG¶¼_ÆŸyÖÌ?.ëëÔøÁŽºz[²mšksÀž÷ôÚ5ywÕ÷}¸±+W48ÄÄÂŒßl(9†¸R± OfÔ‰»0c ·X¹•çîÔ¸5'|®<Ó㜦ÅœÖÝë´zvÅkÆÙÛ¬w¬ôùx8¿ó ¢W™ÿwÄ:äËܧŽû>«šàÆœó96i,}PS×Ï¿ÉǰóÊÙÔs6òYØ.åIýyðcü´†sa=9µ¤6†Ñ=‹à0³gû ù†c_õVÏ43Íð³ùçó³Ò/ny´øÄ8z¹.e>ÞçßÌCålz®Gþ >¢jÖ€_ {?`/Ëú‰»@þO?G$G ê—GG*î5ÕyŒôúXLzAùíqeƒgi¨Ü!ñÿj­„{v6=vª˜©ÕüÆ>Š3aö³™Ãëy¿â6Ïo©Õ´8¾üøPØ?q Þ•áLosìš9.Ò8)ßEƒâÔb7µwÜ=çOÃ¥ VÌ9lå,nuØÂÖŸ)_åÎÍɽñÇä:ƒ×kZÌÚ4¸3ÔùeŸ;ð| :Äìè³é,ÍÁÀ#°ßhIáWá‚SÐÃlù«ÍÉB|°z|±+ùù¨PSÃFÂ-µßg'_…³Å±ëÛ­¸&>q¾t&ÔaÙ#î2>®jÓÛœK­…×ßÕw&¬œÙ >;¯ÓÌ8ÿÞóÒà æÓS½Ö«£>L­;\g”ø|›†ÛgÜ ­ ÷¡Ä-ù»„KÁ¯w[ =)ÏÞ¤$<‹\>6º=½¨µ'ÁµËÙ@Q¹Qƒu{Üv.ÎPû‹ËÙ&¶æÁà68d©*gÆ´‘Ø»±Íö*ûüÃr÷÷|Õe«_ó·wÝû§ë¿þÏxýúƒV&o{Ädeýù•õî½?ÏŠß÷åß[~ù†;­íüô×îú°Þ=y¯O¯~âSÝÚgÖ¿y™ÏÛaî?Í\ìøÒ‡ûjþ÷¬ xù-¯CWÞu\ÿ¶Ý¾„œ'ŸI8`’ô åßaþ Ú?pa©éÎÔ3ÎUí‚Åâ²UQq-ûÙã_Û³‚ŽÛ z9þ¿ònßÖô™.œ ïSÎß#MÖȬ<ÝG—[;Tkè³U¾‹j.`ŽSùØXqÔXñäïÓÖz8‰u ƩخáhÜi­S N÷L68 #‚·?ì‰-ij‰ÌsÂ'sæ³PÍaN#›>g޳²•»Exrm®ÄÃn“O _7ã¨óªÍØtÍ¢ü~Ô«’‹ o*ùÖp#Õù7… ÈŸ{_plxQþþÚ_p€yÍÌf+ŸGû­¬‰rŠò÷äœùçÐÛÜš8¨w͉™¸¿ÂYbÃ7t¼[¹v±9Ä: ÂÏH&ŸÏ‘½†“ÀgŠSÃ3ež2nç”ê;\o0ßQÎfƒ«Ìé­›Ö¦ç ØJâüdp®ƒÅ°Ø½«ç—ܵÁjG‘|”™»¯ó[Œa¬þ­Cb‚ÅŸÇ¢ÍFÙ“¦?±ÄEO—s.uòCúkO¸Ù7ùèbGëbšðµ6·ÕæØi¦Y%m Øß ^Õ ½¨óÒÿ'·P,JNÛK÷%W_j«LÓ­æzÏG˜ƒßÈÆ(ö:ðžÞ‡z^«†‡·Áù×*vœZ`8µåq/’¸ðÄ$Ž“Ë*þ`ï#Ø 3»°ëØtââƒÆö:/Ø%Ôy}3÷€˜j®™3‘z©âG0{âžûÆ Æ×n´­¼ô\ˉëáÔæ½ßùøó¦Ù÷ü×Ýüy¦Ïï¾ù†ås.^¿ü©g½fíƒgO,ú ×L³ßßW/wöéyúW®[¥§2„·'D9s9„»•{Ìl.q?œS€¢^¦3KÜ܇Êe›€£`“e™ VÙë¿]‡š>7~FuMžÙ{ÑuW{lncÏÊü’A¼1êeCåçwÌ¥¢®ÄÃ'Ž4žŠõ 1cˆõ«¹ÎŽkýåg7ŽðÅ0ar‰³£ß§eÇ›÷¹Q¯có`XƒêËè!­kpfajn”uàhÊê»@ÇÜÎÈ9ÏQƒz4ÆÎµ-\‹$¬½;[õKîÏP/Oþ >€þh¸Çô]OÏÔ“2Ô^ ü‹&ÖKÊQ’âšÌµ\ÐãB-c_sðöŸáš©wS{°ßÖú÷¼ó dîuÅGêy;Õ;î«pºüÝâ(4|a‹Íà¨Q _ì‹8W~0‚5ÖZ=Cåe¶s-Gð¸ƒÒaØNp£È9äÿÁ’zâ’î´÷” ^Ë7|³bÁ•ËAϽê2Ô‚#¹6ýbàØŠe±­%NÎçMk¶*,÷ ,ådsÕHàyvƒf2çš9XXH~oÕç=¾Ô~s¦|f;9K0N§súéc–?w>ŒýüŸ{‰ ndþ¨Ïöáî–;Ù`±Q¹†Îß߉-Àaììpöæº^Ïhcpzcó3ÂQÅ~åû›×ƒÞŠ}œÛ•¸–A5HbRúGƒz¤µ>²ù^S7Ö¹)ï œ—½vÎ í²ØOtÏð;ŽÑ5ú—å3™i¢,R—{ßžÏNnêÐ-ÇàÀ±8øÊÄ“yÍò¾Á S^Ú‘_ÇôwÛYj$ü=÷„g2,ûÄT¾Æã®]÷{ùw¿|ï¬}ê?¶û˜›oYþÙÇŽ¯å<~ñ¢ßÏ5€ÝÁô)Wß÷g/]Ú¯Ü×6µœr3çlSìüñœîü9âǰ² Gó8¿cþ÷™xSœ!8ÞÇ«ö¦ã¬ÒãëÉmút;ö¿áCFÕÛú¦—ÈkØœ£P¹0 pNÝû‚¹ˆO³—?ÊŽL…;ëŽ<Ÿ¼ÔC/¿‚)gI-F4I¹W¯ü™†ÎÃ#ï ÒÀÂ÷¡ H¾O#.J?®Ô³“o-Ú5Ô“TË)ÏLoDÖvÌÏýA›íé5|8bÂçàdŠ{\™X.¿gÃ×ûòš×^6´NwZ®|¿‰3hŽ"yñ?ñœÅ}ïg2\ ¾>¢ÑŸc¯ë3˜µ}Î6¾ÌóÄêïGœšø‡gæ³Úü£jî­Àé%¯îÉýð_ô8€o75Ÿ×qÇ®5ê5-9_±â2†9à‡ÁÕ±ÄôÊç4|ÃsDåèÕP‰Êg’jÙ òæZ×(s›zlu Œ+ÝÑ<[tGXç§ð}òyÆžä3 O瀻îø>¹¾å×õ)þ™ £Ž•ÿ¾©FÌg¶Öpz´)ñC¡ôД~78ehõØ(›%w 0o⸒#°/œUå½8 X8Å/þÚלÈ÷ñ×>ø²õûÞîÞ'þö‹w\ÅÏšfÓYéK?ƒºøõžÂwEG#ˆO˜÷Ø4óñgUwo&]÷ª)y Ü·jdq÷×ë^Ðß8rì4h^1{‘×ÿa>’y #¯›VãŽfÊÚÚ`_j<¼í½Hä3XœÞvGîfññ ç~ÞÔHzâ4Õ{…Ñìx#›ÓQŸ–Íqàx·¾£œµü=ð]‰a+Çì´Ç ô¤«.[b¹Š›XO°üS‰áêT ¦TãÊçÔqÛ‰MÁ5À8åË=¤ž>Üè3òü>×þ2ëo<^u€&ä ÔŠ¨Ñ¡wƒý«GG4ó@qfâ3 rÿ¹OÎnôY«ÑlMÑ1ˆÄÉììhñëpÏkùì†׽åd×·¼zÓ“u/¿ó¿|ôÛwßð/ÛýÄ·¼|wí쇗7Ýçs‹Ë&ïXþÃ]¾«\|ñÂ=pyø}ÈxEŽqNÞëÓÙ./àbð›Üsq¸¿ä äÇå>¶óã+–iñ¤âlêB=¸"ëüUçMs±v¼Öÿ\8:Ï6Ž8 gîD^Wú,9ŸÍÙ·T.à_ä°ä>IýT?®ñKþžVÏDó,bĄ̊£•";‡æ¹×â$cà[áêõòà[½|”Ÿ 8*Š%•‡ÚóüÞ*,êÅ™ICŽšñPë2ž³€“ƒ[S3"0›Q5÷ˆöM«îp½æ¿ƒ°fò¸ÙÄɗꃬ8.Pëwôc8'“Þyæšu›á‰“ººžØ×výóó}Vý7û+^–ÝØr¬¼3XÏU$ÏË?›}9õ¤Zoßò>á¼–ù~ﻦ“aÕÌ+lñ6ñ±x 8yñ3y$}£u|&u êlâ»õðˆ}ŽKã [ŒM$æ®VlÄâïD/qçN="åï £3®(qe>K蓨/ šÄ÷#‡n9%ÞWÎÝàŠ®Mßä9'Ê·'ã¼ø\ʨX®j ┃Íò¹`ÜA˜OÃ{vþè¬ÎÝ´h_=¦ÔÀ_†ªMM¿Å Wï8@ç`>Áy•ßU ½hÏ7¢ì¢÷më|t·Š;&äÔºCœ³瀹‘ÓÐk¯?›7ö6±Ô*Þ¹åØ'šÔ^/•µÔÌ•)x,qñÌx}í•Üò;Ÿ÷6ÿ}Žp¿¸WvN/”8¶òhFôO³úŒuü¼fDÍsì1kd¹Õ…+§j4å³§3IÌ™Àâá±q§ÿæ>OÚÍû´rã?.õó¿¸û];·ÙÍ€‡?›û27'óûº¬í“sýÃ?_üõûï¾úÀ§û‡|¼â7|›çzä¾MŸñܘ‰>ûÁ9Æ>*ÕºéNê¹½~JŽ­¼·ƒ»Hߌü:õná÷p‚ÜÆ:§A÷«'—ß  ¯‡¸V5pHŸÇ1HkJü)\ü$Ø—Ö"ât‹}²~Ë d›¹OÅ~À'Ä6ä?“Ö:Ó©;l¸Ø¾ ÿü<ÄJ|§ì±ó»è‰ÑýûÏðù"ð‘"| XÝ:a3ó¿ #â߇ʇ÷Ùµh S/¢=)­²9v•øLþLõ¼K48&Êã¹³ø\3×¼Œ³N|Töþ²öÍŸ¦¦w Ž×vƶgî¸iGEòu·‰îGg¶¬ðkˆK{| ˜³r âzpÁÈŸ+÷wLp,Íì–§EŽE¾¿Oó¨ègMŽSzé¹Ãn OIä§¿‡cœ,Þ5-ñõŒÆ{ÞÿjÕžõÜôæ|qzåÓ®_=xÐWïúÓÕloºÏÓ§¦?XfþÆ“÷úÑlË 7 /’7µ²yÅ&¶ÈÝñù^?ït= °ÃP8ƒçmÓO•à%ÈvbËÙ">W Þ¹r-Æ«+á›yž¼ÆôœpoƒôàXÊNwÄ¡j‹x¬W?Ãg¯°¯ƒzI²ÿ`Ž4kHoYc_ô÷ÆåQOAª†8VW{T6”ßÖYßA=mľMÝ,‘=JM¼QÎèw¬¿x=?Ï·?öÎëwû×®ÿýç»õ#÷¿býË÷|Æî]>ð’õwýÝUkò嫯¹Ë÷­ßùÕWLÞ°šõóßež ÖbqêÜ;67Öt&rÿ¥Òþ˜ŠûÂù˜ §MÔŒ^1X™Ï[´7ÐÎ §\S™œÖu¸Û:ƒädìk‰3‰›éãÓ~º¿7›xÎó]êìÊí{°Aì.q¤|_37Ýq–IP_çžÞèZ?¼ØÌ,|Ò¾b|;=ç’x©™µÛÆOhn%ô§›<Îã\ùb?²ã®ë¡óÜác„ƒõøÙæŒNN¾WU7}“úO¯3.^Î*¸1áÃÆÿÕã•|.Ñ{‰/€ æ’Û€qåÏ€c`÷·ôX$ð… ~´Át£à‘?ÉoYM;ÞÁÝWM„Y%åJGˆ¾Šò÷™ß?÷JχÅy'¦m½ÞºwùóÕG_î³–mo-¯a2ÜBðdâPÍ ð•ôçe­èM•O-ïƒ3Öž åq²ÿ#êkسÎJ¦DN^~*ßê9î\±å膦ޢ|Ð{Ö‰‰™¬»›”£õÖ‡çÇð ¦^Ø£aœó6¢ÎÛÃ×Ò I?7ÔíéG³x°è4~|.ÓŽ×jˆßáls&ÙÙ°qjð®eÖÔ&"ýÆp*àd Ö¯PÖ‡X(ÛâŒ]s¦qRà¤gÎYÎoÔûF,«û0Z˜F×Eùw×@,Ïez7Ûò%@+`¸ñzÇ ÞaGkñDé)¡¿©W®ü=}YŠÁ´_ÆA%fÀVÑ«6BŸ.±±-yبž£¯x‡ñÁíòºÑû×àœ²#ðìDßàxŠ_k½P¿ï1?8‹ú¹±ÁQñõ˜i;2â¬rç ÇÞ âÄnP/iµ5#×HÊŸñ?¾Ë^~‡ë?û'Ë_~Ð]wýµoß½Ãè…»ùü¼zù5»wû¦÷e~_¶½‹C¿¿øÔ‡fË~ÌU¥'ðæ»r•# ¸ØØ"=G¤eõ¬ªF£31Ïqö¬Î9#—(¾I19Cé·gÍkŒ¶/šK?Ÿ9«ôq¨†GO­ùHΕb³é¬™y™ŸµÕt¬µ=ç¹–×a¤Á\l¸ IÚ,®j:í£Dý:­aÑ‚œy¿õV€+/ß§zÚ…„¬ØºqI¨ÿ+-ûN$}Ê^u&þ|¢;ÜøxÌ—oޯϵò³O¥K,69éº3–‡n“‹'úp,G¿'ÉsA+°ùÕ½a…n|ÝÓØÖË3ÐÓûJŸh°ºL9ïpùø{ù(­+ùùQŸ×b¹MÉßÉÅ '½Î(X Ø­i2ª×.)?,8Híçß)óuÀ‹Ž~jüH±¥ªéF‹I–úGöO–£O Üœ+?_Å*¶ü^ê;a?ôʦà\ïVÇËæÌêýºPµ=úAý-šˆ¿Nða´Ÿ%'¤!AŽ£_¯üÒïµ×ã®ãú5`­^êØÁV÷¤ÿ©Ìÿ›¿ûë/Ûë~bk/÷üS÷ËgïÓOúúÝü™;wÿõÌýÛ=ôùS٥⓲͔ý›f®¿õŽ6v\3œÀÉòû»‚WÏêKâlz½[±±s•¸`?Ãþp'‰ßÈ™‚÷Bz_½ûø<ŠÛÊù/¤Öšï=XÓ;©­èîvÂxé çtl´ï:sžüAâgéE¤·Yï•ôÜ®­–ûó¸ûð©ŒKkxZŽçÍ纶LáGÁ‹…“ úŠê'¼O•~T|)gS~¿ÏºvöEŒ;À:MÇtèÂͬ?‹8oJ<Ž~>9¯80äoà“—Ä(ÒÉ‹Áù#¿/zÇï…$a êï؈êÒ îÿØ4{tòyNã%ZÜ$›+Ê ¦1a¨}¥ŽñåÿƾÂ}KÔóº6g:Ë'†irpγt ¾Ì, 8§Ê ¯]¥üÝS;ðj:ÔX¸#ùïŒófz ØCüy“rµ¤žeÙU´êŒÃ̶üNíÌÕÖ½Óþ.¾l^ÊL5,Å:ù½Ý§ë<§ü>òSsl“l¸ÛðcŶ=ø°bô$¼"Âí#ÿ’ÆXj㇠EŽ3m¶ŽÅàïØçÚÃ3r=|åmø™†7WçWÀ½Êÿ6ÿÌz¯’r>¹ zÁ}¼¦0ó^³Sä7ÔÀÄKèŠJG£ÇoÕ\ÆfòR× M]”¼™YðÁ„Y’³ÅæY’á›Âë '$*¦¡ÿÀ¹?zNçÐS—U¿[Oî,þ ÏåœtýLÉ´v|D~±äš™åÏNV±¶ëÂÁnó ðÅL~ˆ3u–ºF³ŠÜ¹«6ÑðÙFðTüóN¿r¯9 oìT÷p®$Xw îU“ûÏ‚íQÀ$¦‡»Ãçóû`‡ùž4ø’÷f©nØ;̤› Î 'G6(1ßžþ8«p¨åìk¶ìd^f‰'|tJm¬Ðr*Ëýµ.Øö2#‹úûÙúƒ?à¿áš)GKG§ÄËhÙÉgI'Êò’¦&ÔÔK|>è$?³r¯iÍ­ g«k¹”9Ï&ïÒ3Sƒî­W~›ï †í ‡¸ÖáÍ ,¸S5üBywt±Ù·üœšsž†Ú?Nm(sê¦ÒvâsùÇýzÎ/eO'pÑðß9WÊö}"pë ºþáŸ-§€ÅÃû.öCœuçÀ‚mÁ¡NùœZ4dàÕ°ï`zäŒm#݇ vª©Û–ûökï~Ëzþî}ÔúúüïõÿôÑÞûþ쥻wùÖ+Vÿù‹Ø»øÉµü³OºöŸV—û3k×¾ú-ïöMßµÌk}øî»™ ˜¿WXÆR=b弨¦¤3rŽ»‰EÐÍÊû¯óPîˬö˜:NIŒw0®E â-sO-wµÜº©%’'Ìuá“;V',¥èY •ßÛa‡”WEá¾Ü© y6ý=â %ÅÖóy¸?ì!\€ì/Å1#À¶+:6©­'Ï¢v„ÏÎx/ù3<°Lãá\Mžæ='ð,/;:%ŸâsÇ,:PžUçŒñN¬?52å IÏé˜s÷àå3õÚh­tæMŸ>4\ b0j¸àJà”ƒ÷hš?¤Í§8,ê 6Œ£l¸rrï,k†mU~æöUk©E‚ï ·rÌY÷Á1ꆃ–_ðPÍKJ`1ú»®±y…ksÆ´£°]%N8^çšÌ+¸áóZÀר§ã»UÃ!ÎFÏ…Üjo†/JBÍÍVˆW|Î#6|ßfrÀ¯ÑY4}!bÉãš)2ö]xò§ãËwXëçÚ£Š"\gñ"woVû\¼¦£uÑ]EçÁxðûê«%ï#6 M=Lp•­[ß‹ò}Ò8‰Ø ½÷ÁíÏLzÇ«vP×ÄþøÊ(›fŸˆeà=œ­óædSV”§T_*¼pRs¡zü{ÙÊò¹¬[+KiÛ Ï~3‹Ý>Sõ”{î>k‚ýĆóù`}è}À?Tñ:'eþq¶JÝj' ýõ"y®*·ŸžØÅ5Ö‚Ï0ŽëUš÷ÄŦ4v;ö|õ䃹Y¾#ÿ9ºuç®{ô3ßÓíåïûÀÿ¸<Œuv?òU7®}þwÍú¿ÓSçÞ±zè÷Ký?Ç*/}øçû¿zønS«±Ÿ˜*î¯÷dÄ®“¨^¡˜hK8¦å2Â|Ñ—êÁcÐŽ âIP×c­©k(?-çZu ÄL¯Ù³™ÓcºÂz÷ûÄLù̘V‹ÍnÕYT Íý¶ûÝUj™ †…¶Î¨'.?ã:c¶Ä¡ö#ûš?¯sýyŸFOuQq,·¶øÝòpbîæØgßî¾Õ€PÿêBû>ÝÑ7F\"_QÞ—º«0¤t¶jÏGîØqZdä9Äù÷r .›áf<~0½iJÌ’ïxþ~ÅhÔÞK óÄÉK”'ó6Ë>ŸU06‹ÓO£ý¡ÜÜÌm¦&oí°»Š³Ôƒf9y^3q»Á wC¾“Ÿw eÿ˜š€Þ ?[òáÔºÐà ¶êƒê°œÓôø›óϳÓǬü¥c]òwIc-‘c ÒE…ë%Ϋ÷*jŸÙë‰âØN˜ ³‹AÌ«ïQïôiŸÍŒËÇšä<^ÏϺLÎÔž|ïOCÄæ²†5~=è4×ý9¶¾}¨:/ ªÇVŒæÀµÄš¾º‚ýÀÔ;/¾XÏšTL­Ø½l§pØBé_¿À}/}äù,Ó’?;ÇûÊSÛ/ ŸYØüIjÿļùè Ê#/ª~âõ¤Iõp‡{ôg‚ks4½#ǹáòq¨å ªñi¼€wêÞ»cš d GcnžÍ$~~®½F†ÍR+W1gQ¼M¿'Š {j>C\Îç-Š7ÁÉ/‰—á$3OiŽæ•Õ6¬¦¢µ‹`3úŒ~ÃPµN{öU{ÑË”õƒ7§õ‡¨nñiùÏÁZ Sñºeï¿ýµ¿¼ž÷ä¯ßeô;ë¹Æÿ€;^µ{øY«/{ü¯îÞüáeþ·ùŽ÷­~ǧ¾;ëÿ•?ïX¼9ÛÛåoÉBñuö½Ìù"îÎŽ)µ„ŒQÃÆwÕ<ñ>UOlÂÚq£°«àÌp2´Ž®¢Ü×±OòÎzµýv.„é:O0¸¶Äˆ¾EïïŸÕµŒBíIÅçbç\ \<4 r2‹I Ë£–=¸ÆÎ3JgœNÏVÎBæ é;CR¾×7}ísp>j&z¾H Rç_ù禯ûXÚÊ`nôõë|‚3 wÉ1ýuSÕÑÙƒyÅk^UÄÖÕåÉɰSêÅàw<çnâêk »+ìD8«­û7E¹¿æËˆÕ;¸Ÿœ­Pu&úPkˆEKŽœÝ<°Epvêxµ7ÝxâEÎ11‹°ÍHí–ì[On—Îâ çDNäSÊ™¥N0h§î{Ï9QÿXlóˆ&gõllÀ/ïÕÛéÃ#6§_š¹IÔM´ fÙ“Û˜o´Þž±ôè³Èß•ß<¨×¥Bé²Þçæn¸æxÅNWÜÏ Â—kŽPõˆ‹èËmk,؈`5ë„_TŒ^^ôlà`«×)»Hý•ý(kGÉÎÑóé%)µð&öòZ0g9x޽ƒ_vmEò˜ õ‰&î­b¯å±æ9V¨z ¥'¿äô9È×M —ðù¿hˆ¥Ágoã3S/Hqf9;µF¹%­À#Sò¹ã>ßaäX9¶„û@§šàTŠKWÀÒ\“™@ÄÊ9=Ÿ …'Y¸k½­óI¿`ëœãAÚM¡r¿;þipײЍ#÷²9½Íœ4Üå¸ôr”ÇEá3 |ÀlÒ6ý¦`*Iùa >óz«£×€ÜLqißÜËX×ôXú¿î}çÂÿ¿ÓåX>ûÄÝw?wú÷VwîþëY cy«ŸøT¿F½È¿ynPŽ?õ¹…S]ù¢[à„±¹S=q0ñ2öOAd?È Èó›;Ýc§»¡ù׫g?èµQåÔ »êW¬§÷Œæn¨†mŒ²Ô1äO¼wœ| sðdo]O;HKJï ²·Ú°Å±Â»¨‘EâÑÁxnð^ÊgUÇbp7ÅéõOì¢bª¾Ú*ï9éäËÛþ÷Ný¶Z‡ç®Eu‘ƒ¨ºb"nlìž íóî{οìœ×x›ý鉛…Dò~°3âiÕwÈËShø•< øyL£û’Àu?|†+3 8ö>›Ä ê!Ϭ1"<ÛYÕµ@»Ÿ3•=ʳ³É÷ˆéÁÚ…;'í=މ3IVJÿ¢íÓ9â`ïëâŽé¾»-$. â'aKÀ~”ã‘‹ ç<fq¤pHTÃHÄ8ûÒ²0{Sæ*öøFjØú¯µI1KÇ#—ÊŸËžp/øž|/é¯Q®³P_@9;ª¿ûþKÏ0j]œs0v-Zãfvd¾EX†çëAúÒò>÷KÏ×`:ÖžCN:®Ú¿øÚKS7›Iÿb0þ3³ác›?+&-ïÜržq•ôœäÂåïó~©»ìUÕß4œ_õ©v´î¹ÍéAÏ ÅÙªK™‚úTO€-rng¦D_½ësÀ›—íïà>Ê—¢EBƒ‘«äuÌgŸ.kßÑ|Ó%µröU?+qFÆKÀ~†™é°o4 ŒÄnêYXçÕœgÃù.øÚƒïÀ¯ñº¶ïVu°´Îò‹Ÿ.Ís?®YKôtuÄäùý2nL>¡\·äŽè}ÖËʼ¡^ëÝ}ø¿,òüŸøo¿õ_öžû›ÝËgâò—¬þÀéw-Ÿsñ6«o|Ëîæ¼ýæ|qùúç¼`×Îßu9(<”¬ÿ›Ÿ+ûÃ×?ç¶«Ô;Í3ÖØ½sÔ¯¤§ˆÝP¼Zîx†âÜDŸ^åªnuàQÜ=ù¨DŽGž¢<®±[â¼Ð—¶ãª .ÓLºgf«F®ûÜàÎ!SíÚÏ„žÜßùÝÙŽ¨÷¤Ç/+?!gŠÖËh#=ßä Á{–Í7±ÇÄ^ð ˆGÀ†ù]jàRÍyŠØkñà.vø™IP=[˜9DTn@^Sbª¦Þ3w£N‚b8ì/Ø¥¿#›¶—ŠÜGØ=ke³5´†œÇžpMñSÄË|7u¨à¸ïÈñIpyù*ÖH{^0^ï…&fÛR.5Áï(GJÄE³2‡yÈ[ìO'ßCï´×Çô>s¾©bMâ/b Ž˜ZÚÁå~'HÔœÁLá3ç^žÙ§s`xIolÇû4ƒ¸“yÿÉa[nH/‚«Gâ™æÎ;ê®DzâòwÉ¿Dp5aYä†îˆŸÝ|òsZýÀyàQñøD>Mìèy,÷ ü‡XÌ€üûG^j­–Øba¾ºhŸZ±õ-ü£çêªUĦ֑¥šMI~!^«l©Þ°áÖ.\zÌ:p íCyW»ßÛ~W¨“{ëñéº h³:Æ Ï#X $ÁKC¿3š¯<¹ öÐ,ËNvX9ÉÆbìýÞûÜ+WšÇs6t^º6‡ FÓöÈ.'éÛsöMnÙÎâ™Hó!éܲ6å. J²¹þÌGžI{˜ýDš y5ª=òüÒ{¿¹‡ø|­›tÏIsiÇùP²Ž%<üö)øÿ®øÇÕÿûÃkkùçÞú¸oÉsÿJdÿÈ¥'`úܧi G•ZFÖPþ–èqaíóÚJûTÜ¢ëОàsÁuާh÷^}ÃË”W9V†_ÃÇ€oÑ×¹o:*ªéÛ<×3šõ);æú«Uû©h1 ,¦œg¸äwA<0ö¸Á:È{bfÅ3žÃæ=7 ïø!uÝÂÑTŸ]¤ª†ŽÇŽ3ã“øý—̺íïóx’<¡æ´†‘\¥=²osÎ:\{îÌ •A}ûù¬ïמÔ­«%¿P›ÀtÀLòçÙì¯ õü”¹/Qy¸@Ùkl^Îý˜™†Í”¾ZÁ‘ÎW­¶4S=7ç¤ðâ+v´`?Õ£•ïx9w|S¹Gjûøóój~µ”dþ¹œ[lx¤§ŒGCÕKëX_åòØÂ>MŸ? wLqye$%O!>ƒ[ [E“°x‹GˆÁUqÈåj=m<Üû¹¨°îòœòîZçî€wÃ1–]l‡æ/vÜ-â°OÝѾ©ÙEj@5&.®ü`¬/Ñ{Í{RÛçÏLWÁqçËŒÀ7{ñ‰zñ¹’|¦êE£Âý¿®)ã%³ºùù(<‚œ·;[?¤x-+‰=AýÆàÚú®{·;D@NÖà®Ôá}Žvƒk=­wù2ýO<(=iZosçÊ}Õÿ]ÅóšÃ+œfªsɳ±â'N©/ä˜Dw°S Î;—÷Ïv¦é$ï+û`ZµÏ'V÷sª}¥Æî81uM|ã ¾2¿+|$ሷˆlî-³IVœ›Xk@#f²ò¾{‰Û ϳ“í­¬31dK4Îâ òñJ1²0ܶ·«Ø*òp–?ñ3™ßß¶ùÓk—ÿûm×_ø€Ûïýõûï¾÷„3]ûôÃjïï¿|ÓªÍA¼zõû¾ü{«Ww/[~ä–Ëc_û3«+ï°«¾Ï‚MæÞÖÌÃÊ~>?KÆ ˜M7SoøXÞ¯ü<ÄãÕú û^X0|-ò ×SLËÜø$_†ö¢Ç—Æ <±P.WÖ‡û¢œ™¹(ÍL3û9â}ðp!rƒÖ×ë3‹Íóçü©/~T¯¹Bå3Ùã_EãÐùgÄÂà­ÔðÀD¿ôŠ¥:‚¡Ö‚#ù-ÜâhòOÝbÇ*–my™ø_©æßv¾áYY=Àû+µønªeD|v!5jÅÄ'ä߃zyð;YiÖÍù—ôEüL“÷—x <“8«ù¯›T¼Ù¸ÑÔÏCÕWÐŒ«a;èç¥Ñw21Çž~ì(ÛWê4cq É/¬&Uæ@9¾5ktÙTCäD²û>6Ò¹K ˼ÖH¬&;¶ꨙë};õ2.ðµÛê ð@Õ>o¥ÇC¿ï8½ÕÕÊý[Séûç{늑ÄÜa¯KL ß9ˆO³ïšÌܸØÄ\ô\˜Æ{w½è|4θ„—ÔÑ•#'râªà˜²ÕÖèÏ0¬àzÌøåžžƒ¿¡w7¯9˜%¼el*ú)³ª-FªìïÃïqÔ˜ƒšIÍ?œ/ž¨I̼æÅ<Í-Ç«­®3G›ZD9èW Âî u/Ý|õ*ÖlÍã[}w± WÕ9ß ­úûÐæ$&Ù£¥Œ=ßú^ñ”éç²xƒ:Ü,|ƒbßb‡Ò¿utŠÍVÝ$)î-ë)ý½¾b?¶¦ŠÝ&ïQ±Ú9úXí¨¸€Õy„Ãåuä}ËZïï‹7meùÈþÈì©yþÏü蛿~9zè“—³m­9þÛk‡¶fù°'þûê¹×}Ý2ÿ>}/üØW¯ÿìÃWOÞëGù®JÏ/ͤ-LlŒ&¬â}î ³d{i§xÚ¾úaèÓWT)¶$I«,‚ û¢ ¶ozÞå9àæsöZ>u8åœe© )—NÂkœ –ã™Á5@˜÷g³…™ÇvNµîuª†#”>,Ó÷œçÈߣ»8ûàÞÿ£)-{ÐhŒœÛœÿŸþT0Iì]79ï!|jô©èû}εîÈBºýŠ·¤ñlX@Þ[0iôC±·òûô)ô ÞÝ#æ÷¥·Sg5ñgúîLùúþóÏæZTþwx£Vç¾ Ì­Ô8ðô’+Gw]¬*ÇÁu›5Ew2¢Ps›XCj;â«÷޹\Ôf^‡¯e˜~Ÿ\@¾ÝòäFC*ùsÆò³ë wÄÆìøc0îVùNx'àFo"'3®îM"Nc¦¦my“Ïæ`M…ÕKàäèõ Éž»Ý —‘˜®~—ù0éÍPsMôØ|Bm¤ÎoÙ"îdfœ0¿¯CWœÏÖ«î ½gÌ®s÷Ú^[žA}±àƒmü^î6œkå†QXx"·¬ïâ}´ì-¹C‡ý³Qîçv,Ÿ?tçÁ‘‰á;qžÅ[›ƒß˜.¹yåNÊæ6<‹û•ã–ûFl¯½¿Úö/¹î™ëšˆ—ã5ÛlC¨ÛJÃü&Êo€öÄÄ›Š#zìüé\·µ]ê7AýYÒKæÞDl9<=J²Óš ¶yWò`Å~Š¥M?ƒx9T.€âÐÖ'|·äZôòÂùk>§o|:±$OPÌÇÝhÎ’i ?v<2ÿÜ—Fߘûÿ§ßûÒß^»ðúë§Î=x)[¼8}Í£—¯úôG2þ¯xÒúêŸüÓªÞ­ÁSG å¡e°ž«'¿#âרÚr&Õ|ç0läœ÷EÍ4ֲ߮]ÇŒþÁ‚ë¡9g½Ñ×IÂóãùffuOl©ò.¯£á÷±Ï¬'uTrtâúG+ìqÙóœ7½:ŸŸ‡–V~NÓÛ¤Îq<Ù¸²Þª[¸~ŒljjñÍ6¾Tl·Ô¿áúóùØ–Asœ„Ùw WÃuWd§:íÁö@?ÇMîÄú #çjþ ÷Èö؃Ú/±åø³|dñW)8—×ÏósªöïúÎÊ ðÑÍŒ-„Ï`“…u:Þv _Æy`êŸBo6q6ØkÙÌ<|+X¥Ù ĥôV{ ýbNðÁ™éO•µ¬ØgÕ…—o¦~ŒÍž7ö¨Änh«Ÿ$_§~âüfÇ€ñ¥µ¿dð9œ#¯ë*÷Že5RêAA˜~“w{m5¯•ðÑ‚©7}zÌÌ虣E¾ìøÈ\¶·ÄMz°û>ÜÖ铃3–¼’Yaø:¸+Mݹp®¨…ëE"¶[iÁ™¯Ø×l`êäƒtÏi'À¹¶éubJ|?P“Ç;ö¨X|ÊÅ'æØ¼Î.±Ê\: ·#9‚î™×/É固ÿŽ?wV³ÀêúUŒ“ýG£ ß§3 ÞÓë>´µ ï·þªw·Ú|w’Ýïqêȸÿä-hYæÏ!ÿ;Èï‰Æ s)™ÓˆŸUí5 Ò¯BŠøà=°®a¼bj×ʧÀî«~â`µÒHL«^¢†,›ù]õ©‚€•v6çÚž1¨¿26äÅ÷|ñµ9ÿïŸtí™å7~çï.·Þ}çÕ±¸œ‡ÿLoÿáG--¾8—9eœ?Ï$ÒÞðìÅžV¾æ±ubþÆ mWÕú&œ?üA£]DŒÉ¨áã·Z¾'±ƒrý¨>3Çì#;—zæs̯‘/~t5ýÎnCí,ùþYijVÌÍrÙŒ±Öœ+j£ÄGpbñAüùLµiÎYÓ+âw þ¦YÕ×£ 'n$_ _V6+ê}“ühB'`pÛ sÇRÕŽaöØ(r΃xÌòÙŽõiÍ}oš}dŽo'^sÔ~ŠG²éurÆYíùL`Äz÷bljIÀðÀ È!kwðß‚°òÚ£cœâ±Í¦óšë™:ë-‰‹Upr;³{eýT¡_ §°ø8Å_ÄD%F!ΰz¢}¿0‹žX[œÍ[ÓÖrÜH¿ƒÎ¯ôZì®Jç, =>œHùwbBè¾é‘E­â{µæð2Šÿ"îmk0Ù¾Ò—°oóýœÇ¼ÇÀµt-–Æv3_*8¦½"»füit¶Àûá õà‹š9‹ÎFRÞ§á ͆KØÕné&ßœ©ÇCú’QºnzÎ×TÈï ßB¬‰õÀDT«·x³]opø9e½ÎJ;^7·ƒ·B æ†bGfƱLÔ¸Žk¦6±¬êIþ‡x ÜŒ4¨—žÜRë¸`OñµAZ•SUµ¦ü \¿ÆÜÛ™ø/Ä<ø·As­Ô[(þ„õÆ6<‘¹tDb¨µuÝ5tä¶”§Zl¦Sî|±¹Ú0 îLŽ?sîA‹%X­¬åÁ€)³ÆÊCzᮎ€Cq–Ù0TÙZçrV‡Fû#¯Õe×^¿ž÷ÿeÿºµoZ½Çú=¿çŠõß|éM{Où“‡¯?ý;¯ýÖ—¿q-æwßá½Ó+ž´·úëOzÝê¿é!»›—ýíjzß{2ö¿ÀVåýÍ|ÀSçÞ1Ul Mûd³6¤G;mø}ŽÁæu'÷ÁÎaK¬Ž}íZM&p]ü=5 îb“ǹ} —æî¢a–í°âoî>¯ô0Ð#T±Ó °n×2krŸ¾úy‹ÿ°Gpµ±mä…ÊÁЦµÍ÷\³évnòåî½rDÅÅ~ßà¿„Z×$ž÷9¡yý©Ûr'ó:©…s|IO¡âÄcSk‡ÿ)»ÞÓ«T«íðçìE­µ®Dúwò>0‡µÁ;åH©jò»¶ñ?cßOXe=¸›Þ7‹-Ö÷M”g•8½Ö³ÓIÄ´`X vì³B8#òý¼d8”ä]`ø?j«àMÇ="úøj><òØI8¾ÇÄoä&k?¢žîÍÇ2Û.æh1ãLÏ9Q]›^DòäbcÅA#îrÝ…±k¿øì«_]{ìsà…cß…OÃÏñž*bùï×]uœ–gÈv§âMÖãnùùÛ|¶Ç`ZœQõ«òÿÄzú>x?ôÇ”#$î6üÅ6Ô–èµ .ÁwÄs-æ$_Ûé;Á-ð·Š©×l;>nØÁ‘5FåÖÎ)” DS@5Ó½¬ïó ?€ý%_gîWƒÇ̱µÜ)f_“(>î«ßÜ€ëNw_OÖ:á¬I+€~ÔòÜàwįƒt ‰àÍU’Ï)Ÿi1 Ç{É«jݰàU‘Øžü’çGÄî×;`÷¤©áï£Ùá\,ÝÝ ù jj`1h;uàÉûU/.Y\ü¢rrOcþÞÌð]3ìæÏ~ÌÍ?µ|ëãÞºüè/=jwõ]ÿ°öGw¼ËîÍøâôºGņ÷N,Ü+¦Ž¼3vIw-ÂÏ /Ôž—|‘YCàbâú¦¦^Þ‰üQv²cŽ”a.– U.°MŸ—Ç¡™¹Õ`”ÑxjGÔNˆkäC‹ÏÉg\993Z›Ú½Â#åZSõ©èÞ[,\'Ö¸Î`ÄD3ë})kÅÞj¨»DõÊtÂH¨“4µ/Ó” ªåSÛSœÝ+×ÃÇLÉo²$ÏÁgn‹°$ÙK¯ñÊmErþrfÝ ç|[ëR|½4eàËÀåwŸO}0TžE§{-ÜíÀë[  ûèÚ᪹Dê œãGŸ:4ÎÄ·§°â{£ÄYj/h§x5U¯ÐßQxö9Zouãiñ÷Öt ýTcýšûd{Zë¿#|ªpìŠOSÃAG‚Os5ã‡ßs3üfчÈ9E^›ÃwËgj!¬&V{`óQdg§ä~ê+†Ï2×'¿‘G[¨ÌME§©ÁŨs™WrÓíw8µEßn‹Ã(n?p>ñp¨ýÞslÆŸb»Ï°—ä¦ÜSjíØ%î?ZXòÝ1žrYj->7‘æVõ ðf0ï#ž›È+äsw»j8©‘ü ‰1'䫪‡‘S Ǹv«ú¼Í£°ÙÊÛÄ…^“ g’}U¾Õõ`•£VH\RyÀÖûH=…:¦ÅK#Ç\°òa±Éë&AÜä†H7yN"ÁOÁÁ«¾Ý÷žœ{RÏÈÈ1,ü%Ü‚ö\75pxèä›å|¡“÷\»÷¾¢ \¨ò_|6èDX«÷0O€ÐWÉ=ipÈ^ù¦kÕóÁù’Ÿ`–@"Ç»v |dÇÜsÖf&øÊ=5yâ¤ÁµÀ7TC¡ºÎü ·OAš_·þãü™™ÖÓòüራÿÞÏéL<‰ìá°f<ŒÜF÷-׿YãJ×Vê©ÓPÓ¤ï A3":ÔÛÀ§è-º•æKñ=Ä™ÄÓºå¼(×JÄr`mÌåœ[¹þCþVîH3SÅg„56N/ÜìwXzK®ÃHmK󡱫äÝ=ÚõÄ¥àxŠÃJÍmwN|Ãþ‘ß‹#Ø.Õè‰i|žöZ1I¹ãà‚Ä볪{©ä5É{Lü¦{ÛÕ¼ÁtW¯U_¤Iîê¶^z^'µØçȾw©©S|WZêžï*ì±µŠñÝ–åÏ»áSWdýÿôÈ÷ÿÁò/oùªÝÏÝïÆå…'¿vmr×·/÷h5ÿ¹áÿÈ~½Ô.ÿñß±ª^¯—ñ9gÎç8ïñͦÀ?È®à[ß%…ëH cÎ ½&­NˆîcYÛó®Iï>®Ÿ¹Î¯Û\ç)é3T7uÄYò- ^UûâÈ{É¡¤·\îz(µjä1è¸ôz^½Ð$jêC£k•{±©º'ùì-ð¯Ù6(÷÷fÛ±ŽÆ·FíA±aÊ]• ÚL܆'âõSõ+ÌÕç¢úò1a †›ë9"{•ÿ>ß öÒb!;ŸøXá„-ÖÓÇÐ+Ìù7|õEÓ¶^Gn.ëJðsçò1Ž{qQ¸›ìC;·Œ¹ç`ÒK2ÓpKž%ǜƇ×c¸·ŠI•\=mcÑÊͱX&óñgâEp¦À¬òú W&£7J¾k³íMê™CMîK.BþÄk U×^iKk욀à˜ÎÇ þÖLssÁùƒ÷YÞ@ß xäìV󇉳Éñá3É^RK¾‚¶Žù^­SŸKïÊcÙýÞÎ¥×4eG¨©1ç€>H›SiûŽ™×I¸°kÎÄ Þ/e¿ËþžQ¿-g\g§­ÀüÁíûZwò¸‡YÔ œ[I^Æmr#j‰`:û=çø§ÆõƒÙŒ>‹‰f•?SäϘ‚Oq.ñ·ù=ÑÑ'Öå¬Fla¶qÆS9!ž‰ñ!™Ë­™;ÄîsüCÞjLÄOÁriöü¸OœñÔ³žË?$ìHÞ£¼Ì=²úa«ªóWì¥Î4˜†bƒÿS+?–\¢Ô ‰¿”CM¥ý¶ÆìøÞòâ¿”M:×ò@ˆ­¨ë&òpÔ C6Öµ¥Ì¬ø î&±ò¾4»þíw¶÷ßgñ}Ë×®ûô_îžù‘ÿº{x6ÖÞøùg/ïûš»ì¾à(ÿwúħºÕíG½hùš»üY™”íÆ¹×ýê"ó,Æ:R´[QúUÅk(3K½fA}?¤û©|Œ¾î‚y$85`·Mï 8¬óQt/±)œið7Ö¦ñ-ä-p{É-ºÁ9¹[`£‰ÇÈm¹«àös>“ œYõ3Š»pÑs²Á8Ke½°Ñz¾ûÈ߃I)–5N×¾4Múµ£q­˜¡±Á/‚Í‘v„Ÿßò²kIzJè`uÕ[|—ñ 04|¹¼}ð×ü?ÍÀâYJ¼ÖÔ¥coþ]y'}DÛY{¾ŒóÈÜpŠyèÃ?[8'ø<1œ9¸ÍÄç±GÁ¡ƒ÷Ës1ñàbÛ[ŸŽ¿ÇFëŽöª½%þ~P3ubpvåËn ” NÛ<‹X5Ÿ¼Ù¦³ EµÚ‹Ò\5­„6s”©15õÔÞîÓ9ðOéåOnÚf„‹ŒÿÎû€Ö}S`éWI_>»ñ­­v$›ïus°€¼>Ô±wÜkö£å•é³Ú\?WΟéþ”8<Âmo0i’Ž˜©#àxN„¿l¹ÖEæšÃcžòþ:;Q•âÏ¥…FÓÎT4Š£4¤iƒQZñ>dx`gëÜÚnWR¶ŽÞKx3ž—«ŽÖN]Çc)ñ{˜­Ô5z´Ìæ@É®t­?Öùð|®©…ðó^'çóeg?»å¾\¹›×Ôfâëž.ØwÅÏ`å,q;3C¨_Ò÷*|@¼Œ ~L;À{ çàCµò|îŠê©¾£×ß"5êŒÄ€øõ]ê´ràV¢fºäÛ°úµXrŠ'q?™õĬ{¾üóo{kw"¯ëãï߯ÿù‰ï>ñàç?ný;>ÿ+kŸ½ù“«¯û­—®?ù“ÿ˜ùÝ“?ùkïZ=²>ºþ?VOü?_½ú´·þö4ósß@þ¼7~þß|Ê3–‡1ÀͯÌÈ8˜ÅZ'¦V ùÙÍkñ7­Uæh­‹\£Èë‚qªlv’Î\Ôyq\gß´,=ˈˆéæ`tÚwÕÿœÓÚ5>¾ŸIklPvsN.6’:ÁqõëÔœu§õ½®Ÿ&ü'Vûd÷ OZçÞyKÁkñöÙŠ;£8ŒÿËŸã¿”ëuÒJHCåvƒw{?]8µÕÜÑÏ%òºfŒ“\tVù@½jï%®&ŸÔ¾€ÅL,¾¸ààóU7“ž%Ÿ!н×:/„c%tÆÈ÷ó;æü Åô9 ëÚ ^_μx°™Æb‹ÊçRÿ&‡[ÍÏmï º#`ËÊ K]š¾zxá½aÞÛO(÷1Ûr~®ÍÎX?_Dg­ÁZSëóè¥=î:ÉeËå8MùD§Ÿ‰ÂÙ½/ KÜM<‡Ž•¾'ÂË©yö¥óâ¹Om}£±×å\S£kúõ%ùÏ ûÓæ Â¨=%6†ޝßP.^>Ouè®ú Óë¬úa[þˆº²01çÄ€œ-óq®›ÞóU¿0•æG?kz4„“N›|M6Àgw–½•…'‚.GÏgŠÃѩֺ‘˵u›…|ö%=ʹK~L¬É¹Ó³¢ÓYlØiPM“ùÒ?&·Àë;0S@|Ïñˆ˜ïÔÔÝ:41rLÌÜÈAcäÐø4ÙíNZžVÃå(ûÐÄÙäåÌ}.xÜdúvÄ^päj­´ê#é ÂÍ ¦{ÆÚÀ ‘¯Ë^£lykþ3Åã bê ÌîR½a1§Pß#Þœá³ð=àŽ‘~sfÒe¢ï‘|Nþtž}vþœ7ýìƒö^ñÂöŽkŽÉékþ|ù¿õÞ]ó©Ý›Îüéò«—ÇvùþÌQ¸ß—»ÄodÁáç—˜ ^‹¡Mߘi¨Ü$éˆn¸_"††ÿg1²Í·mb«æN3‡fS9رØj&]§Åó‹|›?#¾˜[}ú˜kÌ»-ëƒóãM‹HþX}uÏ{:šîжpeúÿ.r~µEz®jíh„Žxdâ{”'åu£·£® F˜ÏJ®×4|iÇ'ù‡xžeõ3f7¨C—pFø4ß7êlò)åyò;è.EÍÅfÍ:jtÔ'òMŸÕN¡—|œDõË:; œŸXŽúY~¯êŽa ñáQxžâ–ö†Ú\Ô Ò¨—žò•N˜¨×ÞCí íùgls‡ŸKŒI­Ng×ëÇäã5w­:,Üi|q“O%p—¶ÿ±æ(†{áËôY`IfÊÈyúnç‰ o¡?_ñŸéš©î#ÛRîËBš—Ôôáú©'Íu‰…ñÏápÁ¹hs/å„=÷tðYÅ·LuçÔåµÿI±V©KÚë©€ëÔó»¯ì ñˆp ïÏkcõ¤ ‘:8±víÕAÃuËqÉñ[þs æìšrê½Qn_çC¢•¥û ÜÞ{ÎU ¯x™­³¶ÀÞúŠÁZÝ™5γXVdO¾¼—Ì´UààʧP ”>†å1äÄMí-gZç­®bGåü4ÏÃ\Ö ÝÝtiül:LäTµoÑzQmmL‘þ|B¨GÄÆh{ƒÏz-z¸•ÂØ´ØÊßS_1ýËCS/E‡g»üýø->¿E˜³k(ušIÞ33øoùtæïõoùä,ûŸ¼|·äÏîÞð©+Ö_~ûÉ^æÒåÏkzD‡ç~y˜Ç¯ßù¥Ý‰2×Ì£¼Oþ½#cƒ83òä.ÅÖÐ+­>÷`ÃðxÉ_ ß)öºÛ'ÿ O ™9%u@]¤Ø_b%ö\´ê^x?ªÏ†ká¹uŸ:ôÆÄ©ïª}‘[ôè¶ŸÕï°ÜŒs ¿’tÁŽ:4Àý9‹Üwü-5žà<¨wÂï=þF~¡è¸€‡ã+ɉö¥MI~"ü?ÛÎ –g|$0­o„¬tÊ>‚‰ί\QOÙMéü–y&Š©Ñˆ(wдyà|T-¡j'6‡ž£®À +ßoä=2øFê>ôºµ½iÍÚôp(˜¡Šó`úfiðž;×ÒBu!ÞE"V‹÷7.s Ž¿%^XBcGë z½ñ¿¨3à»±}ÍyKÍõyÛ`?Ü)Î AÎù7z"Úk?Øõâ•ïoøUàÄjÍ÷oDr@ò}õÜ꬧ÁµZÎQ¹kÒFïjÈgEõ3›ÓÌï¹ѦFGlÞëLˆˆ½Ô¯)˜X›}×Hkµ˜Zye¿Ô;µ¥<Ìt‰ÿÒç3Æ.pßÁÞ8ò äu‰|ð-8!ôh€1ÄŠWF23>Ü"Ô>æÌ©‹ä û¦ÕXÖ|7ÏuÀG‚9ƒqåçC†:4Ï™ÿ½ÁNß…G\Ã5‰zê^Äð 7Â9vܽšó»l‡ þ1‰brl06zö®x5׫pÇÖ#6•?+u“Œ5ëÐ]Ái!UlTì¦bÝÛ"{[ÿ£ü>‚mµ6"Çîð­„ z 5¿§ê!Ϋû U÷DùëÝP]Ò÷ Ÿ6# O®ìƒê_Q±Ž×±ð/ºÓ—p-4ë@Ÿ¹‰–~‘úKÄ WŽ}àµÅô’ÑOM7 ¼h%q^gÎå7œ Žóš!«:_‰ÃóZ[mvÇy†àqþgRû’ˆïÚ? ­‰ .“ðób;TßW~¹£^ŽsøñHÜ>®ó˜‡å} CÓÿ/Þý‚ÚÏ¥:†[çw•N=u&·ù¡eÏ­/vßõ™WˆÉúPùœÅOûÉæL8Ÿ²YÎGºK«ÄlïÕS¸!ùüçßw=óÞ¨ úCªëuÕžØ9Í{œý/6ÿ4ˆw®˜6qo*¯o3‚)TüÜzûfêKÉû#~H“s˜‚/&¬>íWýxYÓU 6€S.ü£çóà¡—?‹ƒÁ5W¹Zy`Z`ôá<ø©¸ž¿ûcÿñ­ë¿ÿ̃õÞ÷9ë_õ»“õ£oþõÝåÿÙ]öÁϯå÷:öµÿgõÏozÑÚô©ß¸ö‰{}põYwü†å·ýÒ“×só-¥›ù7ÝçseÆo¶CÙ¶ä^ÀüÞh­æ½Ìÿoó•KLÈ£¨˜&™Fï¶ø~Å.ICȵé›~^ZoÕ'2CÍç¦uÔ`5ghN<—G}aÂ]Žú\Œ|vàd‚SÕÝàŽtäƒ:¯Å~ªC‘çƒÙR/4¿x”9Õø¬9±“ÙÇ Ê3*ïÈîçeÄÉåYÑ#¢žA¬-_ÜÐ1vbråúçUBþ­oq åѽzÒÉáÊyNª¸Ãž}1zvóûêï{|F:§õÏN¯9¦¼ÿŒc>‡1Ã'×?ÞhâèϸKê{½‰»A-(ÂE…מŚ*Gð»Öýˆþž%‚e´<µà½ã–€!â‹À„Úú„Ö=U¬½Ä±ÄXŠòá$m]áT'Ët|¡ö2)ïyn}ñ—ãßA¼5ìÕL½]è‘£7ÖYÀàj¬~Põêf£Xóžto"õ]âj†Â+óï”ÿ(þa—ã0Ûës ü;..W’//ï…qS kô@Œ#tÜûîŽyŽ«Ú›s„´¯ .?7˜þq¯ËߟÏ1üå†eÝÐ-°µ{œ?­±œKÓ0},in¸†ÆLzøfà‰ +ÞÜÅ—…z—ìRªvÏôsµ>ŽCáK”³` S¨ß+ÚÚä Ò“Jà²á Å*=<áìóñÃ`xÄÔ%ðÕŠA<¾UÜÈy%v'NmqÚ‰žŒRñ·Å™øØ6gá^Pדís\TvÒù¿Ák§;Ø«ü™Ì0»dƒ0 ulq²ü¨ôu óôú}$N“VE¯æøù¬Á' ¿NâõxNùs~J=à÷^ì¾áš;ìýî?þfñá‡ß?ýòn¿G~xø™Ëëßô–%k‘íχ¾íµKüÝX\ê¡Öç(÷J­%œu!ÞIÂvÀ‹S¿|‰]àJR_†÷26Ý|ÇÉ3—¡Ö“Í'à£ó^äF|ÿPW2<Àx2ô:ÏÔ/Ël a'žJ ªìõ‚ïÂ6¡¡=«ºÕ®YF v•¾„™ø~Â"8qþ=p}j¸Aó]š\–˜•{ãñyºÎàD|±%ª9·’uƒ'ÛúÙÙ³mÞõg­ ޼ѣE-ÿ«x}Çc+êCºemÀ±*dËõqÇÖ—ëºÑ`Æðµ”ï,ˆË¨3TüÛ´Ï„ølà+˜qOA9–ÏFiç3ã|<wÊ^/àSpè ïÐï¦ýf>˜­ÑvÒ»ƒo”?“Ÿœb¯9ïäûù9“¶ÒâáÄÑ©ÆæC`×}V¹ µ‘&‡¦W¶øå>=9`PÿzQM¾ÜߺߛØJuAÙHfNF ö!ü¤«œ(‹·—Q³s"Î5FbÎ62Ì£¬uß¼¬-ñI·6ÉKzp–¦Óë<“'ðÎQp9‡âüÄ ®ÍXs ŽY Íl Õ‘¨õˆ[kð•|ÞÀËÁfcñ‚å§#ǯ•Sø\=ücÕ°³«Ï*v@wLvû4xj©9ãQ¯WŒ]ž_ó÷±H8Uç~Õæ¨9 +éÔ£ƒÏ-x‡lNyßü=Ì[ž¹É1n©ûQ?Ç&ͤ?ÕÄÙ‰9cÂ];GwºØ%õõ¹ÝTÝEßsQ­MáìV§‚ÓPqyëÓTDâÌ‚WÉ>µxJÁÑ6ßœ‚EÈv”hib”µÊw­áå³Tð¼ü½¹¦rÓ}ž>=óæïÚË÷íÕ¯Ïî¯ÿ×'îý·ýÚîSÿeoù‰OuË·é]»W>í6»ù™VßõÔåÿä×ìf½á,‹_~ï_ŠkÓYܸY4ÛXü<9¾fçI­¸mÉ¿ƒï? [Ckæ‹”ø+çìSã ¦©íñzåU _j±q??ÜÈl?øƒ[I³ç;¸ ºà¯à4îoÃÓçv²õ张׌†Kç'rÒü,Øœàz6èÝ>Ž|äˆZjùwöÛxÊeF!š‰}¡ÆBîSë×®±îÙäÝXœîO>ÇôÙêwÛú ¸zÁ Tw+9‰l õ‘¨ÑÂFáw:4<ñA¹¬ðöEsß'pè4Û¬tŽ= ¾­½ëˆój}w„V»0ÞÕñO&êE<'g6Ôtê6å.kŸ‘p9Ó¾¿‚믌Øl½þ™ùbü :€ÔÜLbaã½âEjh#bcon, êlŠÕ:ò—¼ÎÙ×™ç¨Åƒo•ó%l7 CƒwÀ»$ËJ=!î9{=áþY½~“z'xK²|~›óÂ}Æ_•X[§ø±TÔDŽ٣‹¥Z¤÷ P7`fl> úÙ’7‚!(/wwŸv™^Øf߯ˆƒ¸½ØD¸¼õqãUÿ%xa^–QcžÈ=ë€ÿJlιÖ÷÷Î4>¯#&SÞÓô//”>5a:؇²VÔðÄ»Pn¼ÒÔFpT½æB/$ïky¦ûCâÙpÆ>Vsô®­&A‰ÎÿD±¤þÞú¡”{»/'‡WÃ)tWG^¯L³|¾Î¯Î„áí,oê&Ü÷à\[׆#ÞÁcÇù7ˆ«'•Ãañ~ö£èÊ6qku_S¨sñzøîĘğÔBé—~QÁˆ¾ùÙ^û…{éZNèáÿ¯>é̘ͣŠ×2}Ú[¿qu\{–ûk¾ÿ#S呜݄ÍWx‰Ï“ROe‰µ±Såw¸?Ô=Éq•ç.†¦ÈZÊŸ(×ÜòØžX}öì:;iölf!X°å5ç’j8)4}œŠá,ë\–ýð»& °×¾§öœï×9ðÃy¾žš@Sßö8€¼ÉÖÅ´…ùwjô‰“÷ØûY\—¥áÈS/ê¨Ñ+NõzAPÙPú/û§síy3¹ÕàýbVgÂ&á?©e4{]ì58焜!4õX¸Ä¡öùyüs½ÄD¾Ïy»:Cn_À óß[O¹Ù)qüÈ:ò$b l´ò7Ïc„5ºKP¬†íuò8Âò5^S Ò*ÕÞù½hkýÒ|†«Òq¹T§*yµÖ¯øo¸™ð&ÌšÞo£“‘ÎH÷qp*Ú.¦!­ÑŽšvœW}ÖâË›ÂUŸ˜düž¼M¶.‚)+o=¨ºÒ•W°6‘ÀKZAÞ3±$| â2bDÙ—²ŽäÖ𴉥÷çõGò~bÄÁy>Lº°›ŽÉÓçÐ@·ü½ÁN=÷–=œ?Ù^té1umb4ÓÛkîÐ, #†¡KÜZ{2ÌOåç´úÅeâv&õ¨tðˆðÅ:·ô$QŠÊsÑ™ã.tàùû¥I¤;¾%üh“{Ž#|Éj}Â9ȵ„Ý_2/eRùÏægˆOÐ!×äõ…/—saiåYUç/ôœ@¹„s’B­ùx_¼<âêoMïf$×CCÎî×¹Kl8£´ÆæôŒ*,g¡Õ,Ä5ë-ç6ß&Þ¤Çpmîø‘¯çŸÿ†çü·õ»Þó_Ö>êq{o[;¿›5þ¾ùÿ}åÞe×^¿–ÏmÖþ½|ç%kŸ}Ôe»·<æå¹'wýÖÝ>åŸK~ÿ…+·˜üÙ½soÀT¾$½ôáœùPSðê^õ,l¶|í|§ ÷Ñ0ÄÚÿ5Hã³bË›þÎü9¹"wh‡þÌ¥ýÅqðúCÕô&vÁñ͈Qgš³‰ž©wŽXE±UÄ'R—çýÂ)ÓÊ6Wæ 1«®'AÛè±ø#<6t¨3J:ò3~Fñ õ?ï¡[ / †·<*ÏÍÏ›ffäÙU[K"÷Œ`¹ê‘›žQ¸ ÄË]ƒN/³5á‚ûïâ÷M{<Ég3ù“í(¿¯<ŽeG._Öƒù‚ûè÷ÀºñC©™q[ì(ŸoïU¢'Ðãï ]0»³oLÒ)Jzgïo‡ÛE-]ê¡ 'Ì%b¿Á·àé»\£M5¨žsŠ_¦oe&=³™t±°Òoó¡d˜ üæmåb[øã 9/9-"ã0æ5ÌÏf©þx š`}Þ`¬ÔˆÛóClȺñÖ±7Úó¢ÃeÜuëÁ‚/Cmƒ>ƒû—ˆÑÛx½e–h8Õêuÿ;'3¨»[cÔ‘¸7Ì ç÷sÙSk ÇP¾C £9ñvgu¾z°%êpAÜÔ:ëÈ{i‡ ¦÷?ßg¡ìzQê…,ç¢á~Rçò˜M÷6U¾ÓõÎŽœ_þ 帪—^,39áub“òïË7öŸ»ßpLJÿÞþ÷î+~áøÞPfÚ>?¦÷]»ú„3]kؼìÔònßô¾eÛyÏWýÂ*¹°æ7µ­Ÿ“)ÁóúLr¥º^p‹o†?Z,Øæ¢A= ù9႞qMLz¬Ž'ÞcÒåYô÷Öφ/o0ÔùL:®Ä|ÚËž)ˆçÐâo-»‡^EP=tPß<1 ÷I1¹o §.Äá Þo;¢[Ö6¦úe\/œuÏw/ß 4´…½M`ÉcÈ+Y>¡»YþžØ\¸O¬_ìñwpZÞgŒÂ«7´Ô/(=É*N2·µÜƒÃs¸ŸÜk4/Ø(Ú¤3›U— _÷9&äìhæiÉ>v`fµ·ãÜïçŒRÅŽÊv0›/âOÌ®¼/g_6*²Ö†¹`Kw褩à:Ù¿¯ŽäHÄù3lçŽçwpY.(¹ñŸÎAÕFÑsã'°ÝÞ«F¿˜s¥:y 5NrWö;Xνa™èîì8Ï×tn.Àµœ€Ño–×@šÅ·Ñãœ6¢_Y5Óöã»à$د:«àæêW‡Î± ç»¶}.ðÓà‡cã¹ëôûÈVwìsÓØö®ú~ˆqžñ•ì¹£êåÎÉ7FêŒè/Ž«VÆeö«hôÇ š 1Íà½q;pB=N›¶`¤Iæ÷ÄôÍÇÓÃ×b½ƒø:ãµݾ$ߨ±öøa­¡'w°Ú%|DG¼÷™ŠŸ›”wwB¾Hn)Þm¹Oô)Ó³$nXܯšJè#”¸ ýYå/¨?Êç^¤ö\èüÀËí½èw°r™.z¯ÀLý ïx¿`gÔGÀ_¤S[â”Ñ™sëù¬^9zØúþ‘¿XÖ¿}l÷…_ÿîÝGßå‡Ö¾÷aWí}îz-¯ÉsŸþ¥Õ¼ó Öžsñúåá»/Ÿsñ6«ß»þžåáÙ+³œòÜßÃó²Ì\#æd~}SÊ•}¨÷ÃÇÍë”ãôKáËg9F¨sÁy#Çsý8q×<¿ §Ðâ/9à_„Έá}Ô Ls›çSmbÄq“'.ž„SÌ®µštíSwÜ.š.aÁ4Ÿ©yuʃ<äl—TQ}£4G›³+¼²áÓìiJœ*¬Øÿ^1Šg<‘ü 8+õƒ²NŽ*žgÝ=ÿÔÏ—¸Ú8^Ö¤z¶ãiyMÁx¢ô‚ÈÇvªÉøÜêüÙ–oGjÞŠ/Á€á0÷œqrmÅgšgLã;Ñ“#©o€ù³®O,Lıì¦ê8\³OÔ6ᤔÿÎq6Øh8Uõ¥eg¼iâ†Ù­f¥)6p| }7Õ©ËzÉ×z<‡¢®S{Nm.ñXœ;ŧÌH€ç]òSâWÅ£¥¦NO¶žfå‰X}¼r¥FâÂWWÝeÜ$‰ ·âÕúÔiŸk£»Ó7q99=µSå§üýèVœŠ?7Äl¦ý÷|ê>ÞkG|vŸ×žÚ¸0ü¦OÌùß}Æð„És÷çÒ¬„/¤µe6íF¯F¾ xolæ”ÏÈgâ¬ôÓÁ°¨ah-ÕÆîÐK¯¸ÝçN;&î«Ç¸äq²…‘<»bÏ%ÆZ(FU}9d7À§8[çsâ/ñIw·'VRºWŽ˜Â)ŸëÖáwˆ«à#ÍĽBÛø7¨®¤X‘YŽŠ1¥×KvÅÏB^?› tÑëGþêE ÞË´Ym6£ô÷Ræ×“ ¢ÝšÿŽ|=÷Î÷Ö:Þ^ü]¯‰ŠÔžc¸¸à) —Ìü$õž«lÎcé™ü§,w÷®¾ÓžlpüáÇ\µ|ÚÏ]îÛÚò¥·}Ó’ZYŽá—ßùŸVɳ±;Ãb³ à|õ[snƒz´ƒÃn sI?ç±ñA¬99R­°<Ã8ÔðŽ Ë4[Ãšæ¸Ø4ííÞ ûŠƒ¸ÁØÖ9kê©Î‰Ûk®Tl÷‚œAg’óÉÙÈß‚ðå&>%Ÿó× ^´ðŽÄ1NЉŸÂù(w¥êXß0ýLp+vVçkˆCÊ3ˆ3àDò¥ãÒ;ÎÏŸc<éʺï²Ú—髳1Gø÷ 6*TŽwR>áK’oÊ.6¼5«¯:²sá7žŸ©úÉøVbíOa¨Úv­^,˜06¬yέ^87±HÃ+q÷GXD±-ò½ Î#þMuÖ²–è4Àÿ£žIþ:Øgåôæsgª»ß«nÔS‹·ÏsxQâÊ$ùÇÍ+ô5¦2þE[ÃΓ55¾ÂÉ£ž¦úuGÏ/±g•ód©# ðvfûægÌ9%q«lµÇ©äçp ðÇœ áRô…5=ŠæoÈÕÀ²Î»†±ñogµ¦ÓYvþ8ŽâBÙ×"gæ¦|{ÕË¿·Lqæµ½Žº û¦>ò×9"wí™j—lî8Q¨¼h jñæot¾ülgñÞƒiˆÉÆÚ=PÞï¼0b@ù5¸³^ƒÕšO¸ÏðàÇ®±Ìüç'Άr:ÇŸÈá®8EgþpÛëè ËŸyÝF÷%ª—´Ó»/¸{ŠY«$þÀb&- jš`¬g(.ø§âŒ7†\_ujbXê{ýà}ˆ¦Y9TÛî§ ‡«ZÂ-JŽÌ^g]^êf;üþ^áø¼ç{ÓïÚ;î[vO_óèµÃœ~ùÁw=z·àžï;¿¼û'vwlsŽOþö;®f\ñ17ß2µXæ™ÓϬÿÁtVµÊÿ¦7ÿú„bòþ0 ƒ="~'‡6ŸxTó‘ ÅR'%¦#/”¿å¼Èß_D×&¯o]$·ö>~òëmØ‘Ï1|²ù°<Ç´àÿé»Ù'îµ»4xOÇJ$~#v¡>U÷t û­™š› {Vó°‘|é€ûrnknb<[j…ä-ÄÜå åw«¯Mo•çDü¹5¾sfⱤš§îÚö¦“˜_sXzê-A5ÖC3áѨ§ýmÄädp“ö—œ‰Ïªø¶Å¥`ŽC3#¡ñfD¾}Ô»wCå#wAüÕ†Wì=UäžÄ¯:ý¾ú*…)$ì^SSïèÐgЃ˜‚×hV„™x«r“:gvh4 à¶å;ÉÊøŒqâ¶ š¨Î®sZÑ"sœ©—ƒØxþ'¬¥õ_ ßÍZÿï…=ék5¨/¨Î\g§9‡Çï©ÕHLვoÍ<¶^xz×ônyŽ]cµ2O ‰mmrÞCYkœ°õÔ”ãöösƒÃ6<ò$9ó\÷«ÖDùø<ð×,ï^ñú&–búüSxŽäÁŠ+± ý~ûµUøµ­%>F8€×úe#~D˜½ìàÈór—Áf].ð ášh6£Fó“Å$§¨3ÍF¶;kÚ ÔÈñ<3ú›º)8î¶á=Là%MLЉä¿S>mèk”=Îï—1xx¹ÎÿÑÙQÝÈt£?þÄÏ”ž½o9ñ;kïþúËÖñƒ|ñ«–Gßüõ«ârÆ[^yÿÕï[Þ¸JA±9©Áf“~³ÃgžÊL‰Á›ø®ÔéöMëÔuç8cÓt¬%Ûžª)fv—÷†Ç¥¸›ú伩Ӕ³ÑhÕ”ºA® ®ëcÏDÜ~F:0ù9²}^âvjßôHñã‘<3ź\ÊW±ÜYùt¶ÙS~¾áæ÷)³Eòúñn©‡š[†ïeëTµ Èe‰)Â)Ÿ“HÃyÛJœÐ`˜¦½®œÙ1ðzÖ ÷Ò¹l¹[žw‡ÚWÑ…SèÑQw)µa¯3*f%§(ùa>ϡє o¢‰Ó"þJïëØÈñÚCßåÿV½ šrÇñIÄJÁ1ÂcžÿÈwàMCáJ.'üÀçK¿ëZ©¦¬â Óa-Á‰QšZžhý°Û‘µTn›¸?Ô|tæ|.yiþlÓTÛlýÜòœ9?€ë-ûTbê|ÆÑ VÞôI$é°&å}åyè Êÿ/]°Ü)Xow‚k/nÏ>ç¡Ù[s«›Ñ3Çœ°÷¡é‘îiY/°&ίñ¶‰Ù&¬üRÅ'ô­9Ÿ|>(y•pzGŠm¦O“Ï µ€ºq8±^Òguh[aêm œò¹ÈØùu&;“6+~‚¹òuGŠaê =[§ ì¼ô®Ñ€ç¼’ë÷Š­q&s‹°CÃ?‚¿EŽÁ rtbSa¥£Õûö†µ—øŒž04Dó%ß‘À‹8×Ä.à`‹ð]Xß:˸Rê/놪5-_ÓjÎSÂOÇËž¢½Y>Gï߃±Í¤á„O$¶À¯ƒ×âÏXS|šÕ¶/8^B †Ï¥ïSá)çú?øCëù篸ӧÖ>ý?·þŠ|ÇÞ‹¾ð¸½7üÃ×ÿôM¯ÛûÊG|íZ^ƒ­w¿lõç_wµÍËþvù];·ÙýáÇüÏÕô¾kw¥Töí¦û<=kÿM3ïú©sï˜ÚZLtbzVíèÅ*èTÓœà+eÓ§Ø´Š±Žè,~\ñ.À)Týº^÷«œlä¸Îbðº»êÑð›‹ø9¯}Ùúš¶ˆø•½8Ô 8\Ø‚Áûô›Py±ôµ[´¹Ó~½ñ]êMŽÈÚÓEbág‰œ-?‹ô^èå+µ_­r^Y× þUS#éø4ÃßðÑëvŸuÇWïJ§ez·?ÿÊÝ¿ÿµ]#w?´#ËÃ8{‰ }èƒ^³¸òiׯΤ9M X¹ìÿ¢ñIõUâ’yÃê“+Eâ_æ ÛP=øúêi§Wv îÝlóïk~k¹ãMœÐ ÒÖÆéz ÄÚøÁìGÈ‚÷ÌlR/ït\sY‚ch!ÚšWn×VW×ÅçyÓ÷Vö‹ûONv¤×3Gr å²èØõÆ….s9=¾+2[js]‰5Á4¤…ìµ/bp°eÓ“­:Cä²ÉúL›At•fiqÅZnt{'Ÿ(‡ñºkèœ&‡-x5úZó8éØ"ýá¡ê`t+Ÿߥgұ垽âFïéà ÐdùÔzíâÏß‘ó–/fƒ3Ûi~¬ü¡åBø}å 41’ìeTžÅÃ(v«£º•Ïâ£B@l§­©'&p[ëÙ¦7ø”| Ä:Ô]‚óLÑ>v®ñ~9ʧ}ÊðªÐ7nd^®Í•€[Pµ™6ˆ×›ül”²>zP-!Ô:õ¦d³^>ˆàÈTýOë5_Ÿšÿµ~Yå%'°á¶KqD§{ÒÓƒ«ß+牸bÿÌž6Ÿ œ'Z/o©Õ¦/k›ÏI«iDÍ¢ò8¼t._Ô³¿ð÷•ϸýŸÃ&îÊ—ûVŸÕzÁ¦‰÷}ÖÉŠÎüÚª9BÈí|lü©ü‹Ûê°`@Ø‹û埧Ñç`Š?¦VD. ®08OÍ8üð38£pªt˜UßõcXsº9<a«ïæçASZg™¹±ÑòIœñ—Ïû¨¹*LÜ”˜)?SÃ}p>wD>§oS|Üà™ÍŠ?óß Mïlý“Ò¿4- x>ÊÃáΪ~·÷-Ò’R-ÞDù;óù›Ž èËZK_H˜€Õ“× ­óøxOÎ26ü¬f/5O û+Û«½§65"‡3ŠNœß?ü.6›¾õãš3 îQßßêä¹`,ò=µÂ œ¨Á=_™Ï|R·×{}wFä÷’;@-Q>.ÍÔK¢ßOðšÞ¸Ô ð³ÔÔËçS¨¸-Z«•÷©:›çËà}ôëÇ…GûµŸ<*[#~é(5÷¬GÔb%;÷`‹:Oø¹ò÷5f¹­c­”'Ä&w¡7Æë›ä%ø~ÎwþãÙl+ß«š¢3ééž%ÅŠ¼#y²çÇùý„ËFéþ'r¿¼&ÒÑZ[ŸR«½‚¯îJÒZ·X5w)*oŽŠ{ˆïË Y©|´×¬«Žº€åÂfÀm᳃?Á"O»õÙQO`$× ~…ý³žã{hÉEçĶæ…ûÚôqGýÚ¹—i æV1Ê»‰ÇtµësΞÍÜ‘çê-óêtß©ýú »ós.,®h“‡oº/<_çW¸Žb-åf6ŠZ4pƒj`Äp½òZå3¢ûØâ¤si„GÅ»åÿ¯ò9Š#jóÜÉâ/ògÁ©§ª\&âG‚´䋼†FÞÈý¬·`ö,¿QîTÓSë=¦œ]j‰ciñœÑÜ¢ó®hóÂ…‡s—ÑÒ챟ƒqGË÷Ó;ªxi¢»ÖiÿéÁW\9ò> úÙÀÕZL œ·‰×d'Ñž,óÂWã@Òeb?шž×ã“Éïu:°Å;=ñ öH¿Û>‡YíµIªóFj-¬‘ÞÇ}¶¬t¨ubhá9#Ù7ãõ —™Õ¸î Ïùjö}pmJ×¢êlƎǘ‰ør_`æAœ„ÜÿŸÿë^²ûø~lwV8G/š>ñy÷Ø;}ÍŸ¯â_²¶ïòv¿çý,úëÞ9_Çßì<ËÓvbÍÓ-f®¡¾7â8çÜ °;s›‰ÙPäjœòqÕ‚©Ç ìù¸ë?m¸^zxäÂÁ oY59f2NˆÁ•gdýõ…tä¨[S;ËŸ­‡ù¬w®Ø:ÁÅ¡ÂïpvË%òyto”Ÿ–µÒÌ©ˆ×9éठqÀ9 âó?ÞZ/¤ÚªGÔk5Óœj”AÚøv°…ü»ÄRð6x.}.\Žr—ägSP½SœKþ^Ø&óÒê|]Þ^.u xXì½øÌðšÚ s™[­_ï«+ß%¼jÑÞó³Òž–ÍbÆÃ ’>8ü(y^Ó'â<Íbò<©rˆßÏý1 Þx:`hÄêì-y5vŸó¨}ïÁŸL×â"1•s¹ÀO›Þ¤ù­jI¾NÔCÁ¬,hçPÀ]§_}˵†t—‰«ˆ#ÁFc=£ÆÍ–°àbᓟ—.KŽ]ò;É—%z*²-_eFç}–ÛEŸ{s¾Î·éÉ“èMêI êѺU_Ùœ5ös'$0N}¯sõ¨A¯óXL )›Úñç°“ç¥{HFvv³®i¦ÑU4±'ö M¿(þÒ¾s36õlá”Z9ñE>“yŸÐå>“#Ió…Ú;vÅí±ð9ñÚ >°<¯›`÷ƒº6?\¢àµÍ‚adÛ•ý§þ¬œ{ðAá‡SÖVkË)ˆÓ…ž¬0Cåʾ—yŸ*þè¹Yßô%-²–Þo¯~#DzÉþxéá×ÈWùîžœNg.—ƧaÇáßÄá`wð¤ûŒ…sÞƒ–þG¾yýDÆ)ï|ÿ‡îÝkïWÖñî½õwß÷¿¾íA/ÒúéÙYÿµ­Êúÿñ‹/üüòÁ›®íô?·vÓGn·wxîÖ¾påãŠFÀ¬pµæu]Æ‹¿{û³.Ðâ.ßzE™÷&ïI^Û“÷úÑé®ü f•G8ËA-ÙMÕ¯^X®bµÕ]‡»rE]SLý4Ìp²Ø[ì(6„X ïAþLñz6Át¨K/zFûÒðç.Н…›ËuÔáË çì8Óø ¿oÄ:ưÞj| 1-} `PÄ—ª“&°!ΆlÿD<<4ûzæœ[Ú+N„§ƒï(ñ€ìþµãçY©óϤ——ÿ‘æ¤4¬=>é¤ÑkNjü qnŠmȾÿðœ¡Qr¿ü3ù,51Jѳ¦–¡Üjjû²‰o¥Æ®ýÚŒu~L%Ä:ÉÖÌe£¼­()Fuî†ìpGÞDî ÷‰žšÔ™ãâ$x^Qy-#‘ÇÒr Ò"„Ǫ˜-)'Âw“?©ntÌ{"òûæ³Þø— y)>®!˜ËX?8žƒë˜?!ÿÄ;'@gUvmŽž)g}#MŒìx+9´rÔD]?Ÿ-áŸ,§­£‰KŽPžµrï ^¨ß!öôÙ3Óñ™QÄq5ž8P×9óŽ*7èoµnÂöL{X ŸD<¬øL-ç®ÝZÿƒ÷žçÙ:`$:¾¯gmf§Ï…Æë9™–íór”ÿ;þ-›â½4p¦†ª«Úƒ¥ Es^³Z…‰•mr£mÓÃåäL3«‰ŠšÂPûz5¯óÂo ¦]â±–ì¶ßó`=H‹&Áv÷²‰Y!º¯ÍšŽÐM굎ºÇ;ü”ò†¨<ÇÏ¿p©v:áBÄm[“½Läºp¬óß+·ž2g*ëêðÎæSK.YìCö»h©TaӉϟù•Ÿ¾¹` 7nïì~燿zOï8ý«ëÞ¿{hO×TŸL÷ûÒc—¯çk–¼ß›Î¹ÅÞÖúr¥¥¼ øÕŠz|bØ1ÿç…Úç…{XûÁ\«Ûs¯AuApò%xíë÷zkG}PµÛžÞ‹³šé¡Ü3‰ KOZÿÞëþòx.ð‹ÈÏ•_‰gvdš{™Á÷oÕ3k³CMUSí„í»®¨KbBÅ‘#ü_„³:Ôž_×lÃ¶Š“oÒk¯ƒóª­§{Y19ô6”O௜ã¨ï6yÐÖ>#ó¼[ž¥Öà9} Ä‹¡é £vÄ%U?ƒÛм7phj,UãùüóU‹Úpø`ìùùék ÎsÁ'híL¿¼R^“CS–|ì‡üÈôíln)xR~>a?QxkÉ ‡Ò×ü|lJ§øû? Ò2Ÿs@žà¬¢g ºn9ƒŠßJ~–íÚ[ãF{Ž;NwJ¬&KŽ"Þ¶iÞÄå9é]³†ÓÏD}ž¿/)Ç$‹ v/›cç–šû“5- âj/ʱ­Çå¹hÖ¯¸-ÇÞRß!/&ň ¿)‘/ê3;êšMÿÜŽH >B¾Þ9ô½TNè¬,r¡ü76¿¯é\9{Ù>f»Tq:Óg0-ÆmjŒSðvr#jehx ³—¹RÔ¿áHH“"µüÊPô¯¦WüüqÎÜKbí¼ÿpŒót.¡O¼ÌÜŒ'åuSÝ®#Öã,÷^¤Î-ØjG ‰);å _0ÜXçT¦# ¸{Î#õ£ò9zÏ󸯃õ ‹}€} ¶VÂ8D<³Î·cæðÀ{j¯ØÉøë¯ýÖe¶e¯˜¼aš{únÜ^Ùû˾`=\vf÷3/½Ã^^c«O¯cí쇗_ùâ{¯®Ûò'ÿòÿÎñü‡hÜ„ë²ÿZäç}Í]¾o*]§òÎÔ¾÷ò·e?2Ï¿iü}Ó—ª†ê¤ÆÓƇ!®– ÅßΩñÁ³Ä¯ÏŒãÂÌχBíKWÏtáFGúBˆÁ-–+úz}ÃéwÝ‘¶ÆÖ`ŽòyhkÒŸ»MJs®Û™@`Kι²;oßOlÙðÄzÙΨ»á¸ãp£kÒ+G¬5– ¾*Õf{ìŸÕÖC©³5ü˜ < Å¿}Í‹ŒG¥\«9u*bøš9¯¹Lÿ ®Kb÷‹:¯ü±¶Ûwñcƒ½âCËûfŽ|^¿\«¤F$ÿž*oétáO‹SÍEùwá”Ñ7ãy+q?¾|8œbΛåö5.Q_ >H¬ë¶¢Ñª,v¸S~Pk¾~Ñ™ÖÁÉHMkpN¿éñd;‡¿Pn‰Ï+wĸ…Æ9‘ïŠìoþ޼?M?:…ÏžïîÌt/âÌ¹ÖÆ‘^`g•gçg_€‘5¸.3|Þ} ÂpyæN¶B8™ûÖh༿çâMcKÂ%|zã7:ŸÞ#‚†å5˜±öÿ\hëÛs=ÛDø¹@â.è;}†›âÑò÷Ä3äŒ`óà… V”Àýô>9¿ÎçÑœSêMÄbðžö-sáN^ŸâÌ4úÞ“ç>Ûœx)n×Bí—ŽvFORsÔO8GÂ*£úP3ªïÄ<àª-€= Òu©5G«ñ^åseŠm¡VàZ1²× ñTÊÞ·çB¸­î×IòbrÇ =¥™õÁðÜ® Ø:)>w ;TžüWúù½H\@¬ ŸlQq™l»ÅŠìƒê¤ðHʹÐr¦ÀÓºû‡kNäÌ7ù½õÝùŽ_>ýÔõö{í WþÅêkñŠõ¾çBÑ=ôÉkO»âòõ3·»ýÚý¾ôØÕŒë?òýß¼–çæ5øø?³xø=nXÒ3’ïÅáç.žùš–<9cè™ hü²#¥Ö—ÿÌæ‡¯DôS‡ª§Ôƒ­æ˜ ߬ơ~¸ü8ø”÷œÒ{K?.úŧU“ßp.°b&záàéáω}ݨÑ›AeÛÜçRw¦Ÿ ˜ÎþÄÛŒÛ'Ž®ù rŸ|.é¡´ØíÈÂôÎFhkªæf±Pþ=zÖ©±Á]‡WDˆCռߟ®úü¡ôaÝ'AšWÌËß'MönšÏ³6ÍfŸn’rYñ17áz×éÎ6#í…&׬ùµçêãwàGÅ;5Hé_y\G.¾‰|ÅŽZà3±§Äq²U>S/¨75hùg~oqè:ÕW’é(‹Øw4qÀjTWéØË™tI‚÷vluÔ5àTñ\ª{íP1’sæ™u¯›!ÍôHŽ ½™ÈÔ§šŸC³¤b³îIqøB=ŠeFÎSû‡cGÝ)ˆë_ë¦ÕßP·œ·nµÞ1ÿ¬z]’ù]›Ìù9îZ!vŽÈÁ¤/߯qq3=Â)ŸÏƒÏD2. µêF`\`Rpèóïÿ*Æ!ït}Dû¬ ß+»­Yè+Š­OLu_¸3±59S£Ø,·–oÛò±¡:¯“ʯa>¨i<ƒ3(Îìè»—ʱ7ñĉy‹Ñú'ËáÑ/G¯y«CS“š,ürŸ“,ë˜Ï­ö¹ºå¾ÃªµŸ/Ulê«>ƒS5ôÔÖ@ðÛª…$îú¬ö/ã»áìõœ9r4rüqÓ¿Ì÷âÿÛØTµ»DέZœj#Å.ÀÈ1Å]†µ ç ‚Îký5cï7íEr6fWƒåª³Ã…¤/'g(5÷tbÏ~tJL*ÿ×÷?yùnþû Ëç.é¦ñîk¯Êî¯ü÷µ“/¸Q~æÌÛ»°¼|™ç|Ó}?ºä‰UÄËÕ<òg–¢¶žõAÍôÅ6õô§tŽwºÉq±}Þëû!W·>,Å>ò%î}Ã;¶šlPí˜V5xÙ$׺(ï„þ0~Xg"a=9–ÑýŒäóùçÄFZ~ÎüñÌù,Æ ý æNÌÄ­Öçè~^$¶ž€'(N%FŒœUòVüñ$|GêÞÔRCåâ _­ñ8²â/p’®î{Õ—®Ùj’f÷UßIà†Al³Î…§6Ì9Ì?¯z¼ëO4ç¥éÁ2«ø<¯±ëE âUþʶsÑs¨œÒ¯Ùã3e‡ãÃwȾKÏkp«¹oU‹á¼ú(Á‰½Áhõ¹œH.§;ä8P¨ýôQþÍ}…üijrjŸ6˜îi‚_¤:§×?Æš?Œï·“Ÿîu_º†§ÇžkÖÂEqéÐMt¾X$—ÏüRâôŒ=æßC[y(ú×Más«Ž}ÎñoÎ:ø@´ÎX÷ Ì›#ÛÞ7ö²ØÅŸ þSƒDÝybzçiÂ)×Þ—NÚ YfƒzÉ«þnÙŸœ35<¼N1H¯K5$îa±‡<Ÿúˆ<'¯8B½IlŒ^Œp·ò3y*×¹Ìkj;å~:¿^XZÏïªæàÝåý††¯és†;z̲Ÿo`-ˆ+Äð¿×»'øŠq¶¿5v}0Izȶú†ÛÂËûµ:¿aWòŸ‹Ó žã,P7Ë1i®Ç‡Su¦Ü®ü;yŽß¡ \ͱLîá?ñƒ§×ùâ+36ÐÑWIn‘×,Ï ¢F*ì:‚Ëá9‹²³Üyýý±wÞí}#m¯¶…Ï!o’„_Ú‘OS’Ó šœmštŸ<2âea–ÃPŸ ^š|»t.Ñ*<èjŽo)ï@n£}¾Dÿ93+Ñ£Á/ûbãÁKÈ·¨ŸAÂqf}Û¡ÚÊM| ñfO_ ùåÌú¨#¦®:ÔÞ#æY¸'ç7xÏÅÙÔ¨ugñÊzlpÆ ÅM/Ÿ—Ï,œá¡Iï˜êy·Z‘õ¶33ظMµÜòô³š×Ä4彤év˜ØO¶&ÆŸ.ˆ_?»,ò-æ{y?4±Ê¾ÏýÙÐwŸïšù='zÛŒݤz 6«¬Ø?ø‡î™¯S©7ÉÀ„ÄÁ¸Ø“‡ÓPG£ž¬¸4)ÄßDø·Ä!Ê9|®éLÜÜãUÓ{â|l8,øuñ£ð‘ Ì$ˆÏgid[™ëAß÷Š× šZG©÷a©mžoõa÷{åÎkî ÷§Ãnc+jßð¬Bí+¾ ™1®ÜƒÏ5|¾©É¤Š«ÃÏÒ+ßW(¦.ë%¬{Äþ枀…t#gnu°:Hþ.ÕÅÀ¤L7õV9dynðmi\'bSåiÎ3£–Ùø4¿›²ë ]Jxg¤•˜÷„Ú’zWÊï}Ðf€Òµ‰ýoø$ž7γ8­>×1Þ*NMÎ’ÿ|Š$¦Ùç¹ñ°ò}pÉòŽÄ=æŽ,θƠÅ|:û±b˜èd’3•~-j~ò}{gÁÀZ.—b@òLÇZˆ9dÐê¿kûëùÿ¯}ôÚúâqZ_ÿ»ÍõøÿNÖ?ü¯Ü}ê·öE8Ÿ…Ç]ù…ÕýëÙµÏÝïnk÷ö¿Z}Ú[¿qyßzÂÚ5ßÿ‘ònÏ|Í ¯Îm—yP¾cù;oÿá•YCÒf†÷_'ý´Q‰íòÚÑW›s°P5¥¸‡®øWþ;q½^#¬%IË£|ÖL¼PÙNÕ4Mc&×JB£9`øtÅßµ—îe>WêÕ,~ .ùܤPuÑá%¯+q?8!~j÷o&@¸ÎÂØ¨évÄÛº][ÿÿ ŽD îÄÏ÷k¶OG\&ÁRÐ%bMƵÏËëœùÙЭE›WþÂ5ÚÕ¿ƒ¿ëÅCŸ«Þ lÓðsjXÔ¶¸£5¦ô¸¸ š›q¾j‚é®Ö\‰ºØ™|VYgá4p~¼þCí~8-Ø ¶9ˆ{=«|Œç*àà”Äñù¿Oµáüu°¯¡ö’E>\ÍìÇŸ•Ôk ¬—hÑòr /¨¶#;’ô>žÀÒþ:îÕæî`Žê³/÷¦ÑÖuVŽ®§¾«>-ÎT‡Ý δT§h¹ó½||T]$Á‰"ÿÖ÷\üB>:’—îvb¡»ëZŠíÞ>÷ÍuyçªÙêü¹®#˜ðDq2ü{ò—9g}– NG“Cº}€s¼>å:Ñøþžøá’Ú®r¤Xñ±ƒFcÎûR´6S,×pó]Ã"¶á³Ø/ø@CÕE"W-vŽÏÕsÇAõ)½„'£gƒ_oÞ9Wh¿(6VîØ–Ïb#®÷Ðçw¥øP¹sí1nìvd.-jÏÁ…McÃ'̉`Ÿµ>äŽGUëºV}{®ðYp/ò;ˆJÞRÖÇúxà“uM ¾ ñ™Ü&R_Íïfö~Çc‹ýªË?QÜ™®|ÚõYÓ·{ô“wKÿßµ¯¾f™1þ/ùÃéß>ô«÷াñóÏ^~ß—ϸê!û‹W¿zÉüÆ ~~‹EÒÓ-_Ô+žŸÐÌ^U¼ÛzBྴÜyî|µ=†ó‰ÆÏtŠ›‰­ËϵxØÛ:kÀqà µEüo“ÇM4Û(RO ŽT¶Gï îmþsõ%uä¸pRÈÅÈAòïäïÓ âO¢ymßwÁÞâGë« g§RsÛ‘óª°©ø²3Ò&ä¾Á ÒÌ9ï§ ®±QÎù„8^†øùÏmì>å3›¾\#Ðj“--ïF¾œ9Ô~š|¹×¬‹EÅmÍBŞŃ]‰Mp®§4‚;b7ò´c‰EÓ,˜Â“-â¾+þ.uµ¬;2UÏ[9{ç¥-AÝ9ÿ{Û{­¾˜¸ïý䮵u–óVÞZþœÏ ÒƒaMÁGé‡7:bþw/¿D¿X&yE2{‚Ö»÷¿_ò÷•p ¢Ãþ“³‚`ãì^aŽë\ù˜GÄO Or§½+ë§žgïU'Ô{-óÚ2îMJh¨½1]åþmup»ÀNCåòwÜ%õásæ#|=|¨üZáS’“ ŸÀ¥Ä §\_: àCa÷²KöÁk(º«Ä‰¥Þ¨?/ïHNÍ=N æ–ñ¶©øq=\ý Úç¬êãÀ‰à¡ÑL&ÎVÒIÿ8é¬iŽÍóOOlÔäèpSåg\‡Ð´%àBÔ>ö‘ë äÏ{’o/qíYŸ³jš•«ÐâºÆWÎ?º¬'¹#˜S[㕦‡fm^Hð¢+FòŽNáá‹£<´Wmæ’ZIÐìXù.ùÀªUÌÓÃ1ó£g‰QY3jz9R,]øZ¸ã÷ìå¿ûųïÙý?O~ÂÞ±Ÿ¼q÷{×ß³|ï]ï»|Ë7¿s÷-ßþ•»ù]sóO-ŸôæÑî-¯¼ÿ2sÿò;ßþÃZšö¼õº kwÿyøÏœ;ÿwÎ;òs™–²é®ÕÃøG5J½‡Ù pEtK”û÷œ b@­KONNÙL Ý9x€®c¦{ëýø,p?pj_Á9ph7˜mÓÕ{ê=x®Ý\ã3æXOéXZ*ÜQÙ¢Xµæ·àâ¸~ñ‹0Q~®G–X2¸Nψú[y^0¿Áu¿¶<¿Î¶Uç½3 ˆ;I]KµÑuæõ4åÇI1­çig›Ù¢Aøfö·z6¯Û‡Zû™×¼Ú°H³sGé?§2ç¥ì} ãÚרSCcmÀSÓNsÍ5‹M·ÔöÕ˜ckùÔˆ~rbÎvù»¦þ×aŸ‡ÚË6ç Š‘ÈÝTWIÍZt3õñâLF݅ȹãLKêèÕà3ÛÙëáTQ*¿M/ˆpÔ‹}Ã{šó̊ͨ€9Iþ"¹šÇŠ;Í1èñyà¹A=kÜEý<þ,0ÁËŸ¸?⾄ÊwhûŠZ›9ë.2H‚øk&½]êZòIŽóÁÑ7VCÁþEôðÈ3¤íä˜0a|8kÛÁ³Ô¾OÐá™UMszgZ=âH^+œ)VL®jøo})îõ\i¨ºˆ=¾òr…„ËQ?_ƒ™—³ƒnro‹Ø›”í›ñÅ]ã ÇÓÑÄa¾oð&Ú| Î0ý5ø9°¦»šCÒpCG©}Çý¢ù²B­ØjïÙÎÙ ÙåCgÑ;ãGæÄgœ}êÁÄþ–ë.3Vßqæ,¶mõãÉI‰«Z=j}—Îø68&E‘Ø.¸ù€ךÊët˜ë¯æ}}û—¶ö‘¯ºqMg4.¿ó?­>ïe_±Þrûhzí«¯Y5¦ÅÒy6#þ‡'ÛEáf…"ŸDŽÖî>Â$ðÖü$?;Z¢`§`zMýd Þ7œ”s¯|Õöžú\Ù#ò¼¢¯#›é¹"ù}þoô.ò;ÒsNsÈÏgõ‰£ bû&ÏÊÏÁ—Ȼθ ó°Í¶òÞÂ’Ù6´¬„˜í7òíýª5­»m³_5—®Ø7lu†`x[¹'ôâƒǦǞé)ÑsBÝ‘ØYñP“£X]ìL÷Èýkõ­Þ»½=OÔ±oußlî xÖ ùÌâ¯Äϧ5“1Ç †aÇKÐéëÉq‰#d/z«÷]”ï9p{ ZE‰ÃÕû1§§8Gëuà÷ß-ßZ>[=¾Ã–×ø™}›×Q\vï·Bë„÷£—“XByg´¼²`©=¸/˜¤¸Úe­ó]Ußì”~Í߉Ê/yGb×TuÙ,&m櫸Ô“ #)¾ð= GPüå =Ž…¯@îL,M.ALßðéˆSqñ0qþƒg!†¶™u¦Tñ’Ç_Ñ냓ÖF.™1™¼×y­àуŸUå>…ÇBpwSp§'øÄARù÷ùC­&ððY3k ¬®á¶8Çþ{P¿p?´Õ¢ò·á”Ïxïë”ÏÅg‹'_¾C1_lû¥¨‹î»¦qÕþ¯|ø‹žÃ(ïòz¬4wR¿%¨®IÍO¹=Áåb,¬¯õ# ¸Zà­Íòó6¨v5HÇݬ Ú0óÁ‰5š™bGó3‚€^xÎýî šºp–¼ÍçüØ7}r=÷¨üüË`ý_ŸtËúñç=ioôпÛý«ë¾{õþŸ}åÞwí\¿–qüÃ8aíóï|ÅÚaL°{Ëc^¾ÌÏô°Ûv»Ÿ»ßÝ–Ù>~éÈ.îùªË–uÝû§ÊËúô¾÷Lk´Ïˆô¿ª¿`2óþ˜ c1‹uht]Ð$w&›ÐWŸ©@±c¯¸×ïv~F‹}úžÔÇA,LnFî>GExq±“²ßpqéÝwN¦~7iϹ+1Òyë»òüüˆ„=ç>8¿Ã8xà`Ä{Ã>÷Ox½ëû;Žª¶`"!?Ÿ­¯=>Ô×½6ç]«__ñ:…¸<ŽoûÍFGÜ»ÉÅË]†SZ±NëÅe®Ç¾ú7É]äû¨}¢W¨¸Ãú}È;Ï4ú/à‹ôô‡Sè¾Ò#TÞsš5*¨=Àý‘=Aº¼ƒ¸Pªq×^cüS“§Ñ3Hþß⺑š¦ŽC}6Áuib›¨ûßÃM'–˜c®»žÀQÆêWaOÅòø\ñNjtѼ֮ø2Î4;ì5UŽC€zn*? Žê¸0š–ƒkcxoú|/$p{øRèõ€yßCÀ¯ã‡+ǦjÈ…¦[5€Dü­5#?wŸªó6ÎïtzÏŒ ªsgã3F4é„ý÷ GfNŸCÆ _¾z²=ÔÜWÓ÷¬3Ãèó=ˆÖ×j\P~Ÿ3ކ¹˜øÌå¿Ñ¯ †EDÞ·Ös,ß…c?ØŒo÷wªï&´°ãMÄYåõr:ðaêðÄb:“sðüG£íåû ®CþNŽÙÖ2ÇU§ÛKÿ39wË㸦Næv˜=@£8ãâØåÖÎi-'†ûÃk°˜?awäœÇ’zVç ï‹ëK=„oG¯¤¼Íc‡¬—àXú›oXæÏý×k?¶ûÚûÛËx@ÖZyúÕ{^ó©5âëñ¼å-¯|Ó’9Yy¿WßõÔÕÃï(v"n¶‡Š¡ÊY:÷º_]ä¾_¹Ì|\û. OPµ“ÐY›•²8SõøºŠµ|ÎnM˜¢ýS)ìÑA–o“mõÞZjÀ>ßA>ܘC6Är2«…s*¿I±vGþ‰Ÿ âLU»c}eä´ð¾…õ;?:Ól¶¼â-§Auh½ãôŒ4;À ƒr#»ãG§Â“ùÁ‚§uý¥Ÿ¦<¿0«(¾¤ÇZSâìNúº3›n¿Á`ÀÿÉQÅ— éÄ±Ï Ù½Üò9Æù9™íRk‚–÷’Ï4œeÅV…<%#&!®1;}TúðÔ œï@¬äñšö¶_ŽˆÝšXÏW©ƒIW±ôvâƒ;å;ô„{}‡¸ã¼zÐdïËçj^po¿cwE8}ÄÏržUo‰ƒz÷›Þ+8 Néúñƒtx8OAýáœgËÏÌÖ£é®{àþßd}\žx¯§îa‡}G[X~ ÜŸï|kÙ2òFÍzÞ!v-Ï”müŠã¦Ó°¢^ÐÄð£aEG\ÒæG0™¾» ÏM™ÿÑö.Ôø›~– êžã #s}Pr9ÎÜ`šD ¬74ü%ÅÏI½ŽÜY|Vò”¶gÑPoHØÌ±´$ñ`á”ÏÓ¹·xØâqÓ5!Îcv'û­5P=gD¬ž„éEņe¯›8®ÇÁ½VÛO`u仜æý:Õ¨«q'øi°­ -,ü/þ5¨Šw'É{wf°¾¬HnÊz‚Sò;pbàB‚“RGÑ3’CqGûZÇeRïKª¶à:¸éX)Ö·ºñ ¿±ôA‚zõ´æ©Ñ3¢Ilk¿5ÿÇ_Öó;?kï¡ëwüÈÇ×?õÛ=ü¬Ý}áqk›_w¿½ŸȬðÿsó-«¿úù_\;üýå#¶~~ùœ‹·Yýî;¼7ó¥ãrtñÌ×Ün™5þÀ‰vúð{Ü }æcS›yD¼b¼šÁù°h¯PSKÕw›-5ªäнáG F©;Ãçä¿O‡œÑ쪼7°ÇMç_éN—߯#ÊÆõäRü½pL°pá-¥¦äp7‡ª«—×AZµ§#ÜÂê+J¼ššÜco¨ÙÓ¤»ÙëLÁU¯ê@<¹“š!á~Í߯í)Ïx‹b—rŽÀoå{߈FâóŽ{ßþ{_ÎÚ.ør² .™âúbй»Êf\+ß þžÚyU¶CÔWÕûtÿ´䇿«íçúê³à×Þp˜AZ0Ôü±IÂU¼¾-ÑØâ“àÂË˳[<å¥ÇôŠ?Ú½å1wÙ»ü–×MsþŸy~ϹxýÚ_‡¹þòw._,ÉImîÊ*ñ—ìbT  ?7Ê‚wÌ…izí¾b«6ÿT5ÁެêZ». ü zô9‰ÕžVŽú¨·z¬ñσע ƒ={ Mç ènO¬Ömu|rW°mó_W{­Í·†SÚ¡ ÒpÔº±k…eŸ}„¸n^ùN^‡*{KÕò©ØâÈû&ôù:ÓÛøá®Ö—¦3A¼Þ Sq… üPñk²Xo“ø®ÄFÌI;ÒçtÆ­6 þç ^ó-‰[|†:+ø+uM~W|FŸÕ¦œ\°õ_Ë߸£||Òycçì,i£Dá‹~8SÌtÚ/ú¾G4'Å8ëA:‘䇆ón(?9 ßNï·Aψr%ÃQôü›5ÚXy=Ñ\³h8âÍm:ŽJÞEovPýS\ÿ| Û#ì?5g&6½À‹Ç p9“¦±½A íCrš¾Õû›Òé›sëgôÖÚ?Š—õ®«´Ű®gªÏ#¦QìºÓúéþçL­]m4½ü;=µqòxÐÕûìbÇ eƒ;ðTîÀìÙ;]ã£z4*Á8Ái™Ö¤Å Úl‰³/S­Ñ9–ú}Å®Ó)]í}´XÎæJåÏ˺ÃcÍû’_ŽÅ‘ËgL>GÞsjOŠÝÁ×£b¸)ÚÁôTË8ÉfšA£¼Lº†V/VÜèÜŸËot5FBßzȸóƒtÛàc«(s|¤Í«ò¿¿úÅoÞË?ûÞ»Þwï§žþö½“÷úôô‡^ùÐÝ+üÈÚß¼øìò7gÎú€¹>°üÀ?±›cÍÃï\<ð)ÿ,½˜sô/dmχrXÆlú…Ã;ø¼“j2Øel¹tô¬ÏY܈$¬5)wV=z…Ü5¢ÏG®»àq±l¢ŸŸ±8¡âz[ý²O›~æ”[Eá†ìoßð˜àÒc€ÏNAý»è-ïÈî÷äÚhÇ ¿ö<?„õ2£&¨§}M¸ èçõ\€Qs†ƒ¸•©|Vå6T}ð¼fÆ{7Û¤\•>›ò|-?†ú­rï ï€=!χUŒTlµUêKøéàúQ#ÞúdÂ_ÉÇõÜo¾®Ø ­’6Çæ.Œ}Vàhî ëžÏƒzXÑÊêL¯Ð4XßÚÏ`zS`rºsÜÝbG؇AZwMM:QÎýû–³^=%'Ös¯•{"¾ u«(ŸA-¹Ó3–ú«ð‘’Ôšã¦ï õ/õ.À#€ŸU>»Îõ¹H­Úyò€*§ÿBŽDœC Âó¨>Sl4<é™f Ñk=(çd+¶Ò¸éiQlÜj¡ Á6—˜Û´á 8Ž{`Z)#ò†…Ö—Y_“Ág²8¾«ø =Súm£CÓ×ï½HzÆ98$q/µÛ –rÚ4\ñIRΩ½9íæØôsz¶6Ÿr>³­çéX÷qƒs”Àã÷Ÿqé\õÁù…Šû-·ÔÂWùŒjÛ¿_޹Ãz£áÝhÁ¢Gbs4íÝVŸQlT>œ„žžP5nº >z¶‰àU€M)Žë¨Í²^hÄù^õ٠A‘ü4x¢û™ª1µÅ»%ñ7<þ^ÇþôÄHäcéŠaÿóúð®àXĘùyóßïÀz[~i9'–ë®LÁÅ„U®æŸýÎáõk_|±Ìúɾ<ë<òÅW®’sg>Àßù¦UåјZ1€çDM?säLƒ!Èžõä¢`¸ðQ¨ËÈÇozo¥ø]‘{M.‡³bø;~Æ-^:—ˆ‘ô÷ĉÅþä¼Fµ¿h:ë;ðûJ™íqÆÀÀèãA»­áÒýÐ¥¸“~SÃôÁn”3C÷äýù÷ ‡·žÎþ®’ü<åD¦}&Öѽ9Hì‹ì—°t×<öÙ%r€®‰±‰!¢Îœbaó¯´~ú³­Ÿ¸é½(þ|¾–r—$N—çTV-Xsb/ðqMß@ñsôh 'êk~f1%µhÙØbƒÕGOîã| zÛ¨knØuï8ï¹òòÖ~—˜È0 à}Å^€µcó°¡jë©/x;55úâÇÔëØÁÝ"Ž[dVïÔCÁLrpizÒô|¦5©sCÓslùÅMÞK;.¼ôÏE‰»Þ qï¾ó¹áóì Öã}+Ä·A8FçsÍUÅ#ÄMÄ=¸¸ÊqÓÑL3i‚ÓjF‚sy‰=ëÜ¥?cø zî¨[Âu¡æd>|#Áá :Sµ¨zå~øˆ¨;)?2òœYõêHÞIԇϕ:¼rS0xÏòûÔ^´bššLÕž¢vL>¿OÜÇH ‹"8¾k<”«|¾ñ–ã¡ÖS]×Zq=Ø|ƒË&Qϸ÷Bö¡Özé'mj;#é·Œ°§õ!ÝáÈ’†[+¸òPdó¼Föf–pˆžsE/"Ÿo3ßéŠgl·8yo8/E†(èfê¨x5|í‘ø*…{2åîk/ÊÝ@÷PÚÊ©Á$ÊsNïø£ëùÙÞ¶v~í·ï°¹þ/ýö½'þçÚûŽÏßiý¿{ï–ÇÜe-ÿ~÷²_^}Ö¿aí?øÉåÿä×ìþÎå‹Õ·éa»Ò6,|‚WLÞ±ÍR?Ê<©gNs ëJ’3azuÆÙÌ6ì<€ýŸYÿõ‚^9ù­„n`^Í;/ûDŸêÿ â2ì1¹¼6ÌžÂG7ØW$§ ÖC‰Ï£>朦ÖîÊîôäŠÄÐ5Ç*óT ÎöÌ\õÀŒ£Àí‹M¬Tluá}i<(›ö¼Sç¨ScäÎå;!ß–$ßî=ÜW#§fÆ|Õ±rNU‡_FÜãCfêµTÿ#n¯œ¡ƒ.ùµì~yŽêc;Y¾=ÁtÙˆ©zå=n’â(ÅW’õcÊî_sG;|-9#ù0ÏRñ‰­žøOk•/ôÊçN@¾Ô9óÏáÛ¨7g5¤™ÕûÐëƒf^’Óî×™çeè®mSÿs».Pz0XÙ‘‹ànIqS9GǽwظRú®(|%R;â>ä»k¾Ïf·y.kš¿;?ƒÝŸ1þVž;J Êúí»®í%z~=ùCs†;öÞ>³ =C$–"·¯¹lÙûˆ¦ø f_:ÇC5p­9ùCS2¼ˆ^E0;Ï[ðGÚmØmÅÜÔ%gÁ†É÷u< ˜90¶„x .~»Î‰!CÅVzô¨ïãÙrÿÕ>–}V<$›pÐo(·ˆÂí#ZwâT sØ¢¿6÷‘O…;?NÂqïñ®=?ôLÐ Éý'ɾ$÷Á)։܉q™U]0Û<ónª| ³ú7õ~Ó¹Dó :;ƒøWØ ù­½÷zõÔѰ[`^ŠC»VƒþQþ.iô–×›NŠö{0‡[?Ný„%î}ëã¾%Ïð‹¿r¯ÇîÞåÞ°ûܧiš¹}¿ÇWí~þ±·¬1áË7Üiùøþ·¥pììÇϹxý* <Ù¶rßõìóàz–u¦Ò¸rÍ"œTî„pBø/îçà}i”zÍë<aGÄ<CKŸ­<Ø3÷[¾‚ºŒ÷¨81ËÃjA›>‡\x¥ûŸ ½ùÌ v~Üà<âMÞÎ6úâIw²Ø7üxzÃ['â³ÀK5Ó|ä8ˆ|©ÏÜKó[ 5`ô&ô«ñ÷äµp¨…Ž«Ö:û­5\!Jòo-öç]Z?³¾ð°/áTÅ¿Y?ëŠ÷p)Ï)š¹ÿ¾jH츶ð Þê\äU{Úq ·3ʧhàÓYÓKýì1,þ‚çMŽG>D 2‡'ÿ=Û9z•ƒ'r­P{ø¹#åÌå÷ilb‰‰ÏøŒ–ƒ¾Þ—-i\š|Q\⟩ÓΩ®qÂV']|f¦Ôè¯ ÖÛ"ÄæU'ìüœÌÛ~Ã>•»‰Þ„ô¢ò Õ¬W3ŸOø8ƒzcÁ¦À¹ñV«¶¾|â@åŽï ïB/+é|Ej¾²‰}Ä7Ö(ßÕö Â×ÄW((ûÛÔô;Õ À?˜aîê2Â@™q‚?.vIñSŸ$oÈï“¿/û3bbÙ×â+Œ js›‡ª©˜Â©:{b@Å"ŧTÙΚìw9çh£4úd}¨x•´/.z½š˜¼ÍñåÛèmÒÜ✠ú¥z°*Ùö¢'öÒ ÷ÔänÎ5ÊÏœÿ¾ö¿nGá>gHw0Y¾ru!Üšïì¯<öÂõüùW>í6ë¿ÿ_[ÿ›Ÿ]ËŸùñwßvíêîek÷ùËû¯åuûõ'½nõN?líÜë¾nU¸ä4½ïÚÕw,Þ\Î}Ž‡Ö¿mwÁ3€¹Á›oGÞ §ß"üƒØÅµ~Á„Ä›ô |œØ“Ø œ~ñ :¯ÝUx¥|JÎ*ñ?ŸU1ô-z+t¶Ì'ªÖ˜8w†;ŸXÀ…Ñ,ÆŽÖöä™ ûî2ó"boY#é%u`òM¬.~ä†×MàX êW¤nDœM7SouNÚÔHº î­üAOYveB¿h¦Á]—}ò~ò|ظ°ú©ltÙwr1î€áV[ž§á[uÿæä‹`ðà>Š-À"Ëg‘'³Þ²-¹âqé@©Væ6K¸‰ú‘vTÛ»è±\ÞÃtÏ%r¼ã®slþ_ºO"‡èÁáñ‹ùù¿påã¦ÊÃ;{ÎM¸½|l¤6¤:[¬¾¿¼…}RýÕj5Ä(Ô‚µ×àñ%gjì¶Ü{N|åù<¶•œ¸ÞsëY ¡—CÏÖ“»·x¼bò ~O}¬ªÆ^3óœ,Ç’m€Ü7u•n¿êmtÄcíùƒï×J8K²}8É—ò^HÏ×ý–°Öræ²Q¢úÀÈkôøÎþ ݸ\úol–Ÿ­ {ÁòÃ)ŸK_ö‡™l` fVν´+ØÇˆå=üŽPqcç™ C¢w<ÁyŠciöˆ×—àbà ~L7é,&åM‘ú­â<å#W/T‡‹ðÕ‚ÕêÊ;¶z¥Ôƒs07R]Ï:w;xÝçœRû×°ÙãôgPÛ>ks¦ã'F ^[¬Ú Íù¦†ù¹à\;¿µGÃâç™÷•ºÚóâßXZq¨Ï©†Û¶’×üä½~taø¿áxª?øz>ôA÷\æŸûÓÿxÞî‰ß;·;'yçãÏ[~âSýò¸úŸüíw\ú¯%mþœ\7‡_Á9äLÈ.–wFK=ÿ¿|V„ÔÓ&!|.³hž™û}zLà¼ÙgZìC pëz—îh²8s›ø¸‡ 6œ δç—ZqG_ïÑßu°Áµ!˜=hÚ?Aœƒ™4 Á+Vg½dÍïôœ#ðçügWÙ¬÷¼¶[zª…M½±ù~·+‚[¨8ÁkI¸¢0ÛȽây«¾áp®ùîzæG²e#ÎZ¯g.¿I¬¯»Økõ°%°Ñýª× ÓÔuøBö€™ŽòŽ “·h¶BÄþÒåÏà°'ê¯é+ÞíšÄ±ú2³ÙpCFÕÄcÅ͎Ȧ‘Ça“ÅÁÃVlõÊE©{_KOïX:g¬CŽçèO¤}”C)o¶äóŸe“m¸ìNÇŒaž=ç[÷*RãÎë‡ýW–UÞ©­Ïð;Á5c|ÎÄ|'žú9õÓªSˆžAÁF¥1·þ& i§Á«f6@x(ül£pX´åìg3ÖN|f±±q 3~”†¦öžÿ;Çûâ\8'®[þŒì‹‡mþq ùšY:g)œòy%>¦Ñ·Ûôܵj8æ ŽêIÇO¹CÔà ˆDpåãê}+ ª×‘³RÏÎόֽÆÄíä\‡?3eî—pè²OÄyù4.™.›ÍÈÞ’‚j#Áuy™édgˆý§¸át‚Q‘C$õ΂Ó7›BÃA®ÚÌ;ÄíÄ#žu"΢|*8)ù6~°M_·xe » çó*Í…¤·„Þ°np7´oTöµÔGInO-D÷7a ˜wS1«ßüÏG¾m=ÿügoþ¡µ¿8û³ë_q·¸>{Õöæÿö²õ\ñk_ùé§­ ¦µúÎgÝuíµ×ÿýê•O»~÷Á7Ýkí7Žßaõ-ŸüëâŸó3äZúïþão.rý ëèÎO©ÅÐïÈý¡.Ã$wÝ/s? ÿàÜÈ ö¸æ°+ lOÑ·ãÕƒ3÷p}toÈ¡£ú#½^np\³¼±uð~”a+{âê š²j¦àe Û…/‡›GÂrÈ gâbf­Õ™#×å7 ^×µ>ÝÿÂTΛjìexǸj`•Ù‰y ¸ÿòÁÞ'?·Uç=ZÅÁ–K ®ÛUøÄÊžèá`‰½bofꣀ_7O÷{ÙűÁ*æŠåÊ»*–éi ÍÔC;x?j¹‡>˘ؒ8MçKöÓ9 ÒÃ/n¼wj‡Ú|¯øŒš½çS಻ð}“Îr‹9ÇK6Ë9™pŠ«ÌáªW©k¿ê;¶¹Gå³ùþ±kÑæ ~Pï•ųôØžîøÏƒ¡ç½ÒyŠÊý#ÿ^döø¢÷|Ê®WöƹàšÓA½¨5/<Öà‹ÄÔYšþŠ-pw É“ÇÎM«ü„pŠ9 Ö†‚¶©ðð¹ò¹äaäŠàÜö'8ŸÈ0—¼FÄŒâiqÿ;é´$æ#åµáÆGÑQ§§[¸öÕÎܱ \XšŒeýÀ…eKÊ~5¯w×R¹‚ïï*Û[*gfX–l\wFšºÔWâ¸öÌ;?;wö!3ñ§Û|ˆü=sÆ”Ç{ªÕm“I Ò‹‡ÜàPÕ/ òú(5Íl×Õ·ŠîÈÎHËP¶ßñ[išGÞWþº­¯úßYí« 7®Äˆâ/–÷ ;:ˆÓJM÷Ûõ‰à„a{ÔSöš÷ßW¿ñ±lHüÓËýÿ“ÏÝïÆÝé|j÷»ïðÞÅk¯Ê굿ÇÞËÓGWÅ«Ëß¿\Þî–ùÜe ó­{kžõ ¿gbœ´^çÎ1›8Êá¼O²Ø=êÈ{ÝqÎ g‰º øqv€Zlð)úÀìwˆUu'±Ì(t-ó}ãû¢õÐcãÐp•ï‰ 3N ñapœz$׿1(,ëc5ÍÇkò3‚‡(Þ‰ÂþÑÇè-Wó>‚“-K#Ùñ&â<ì)øÖÍ=q¬"g±r6ÌÇ2¿ã¬ëMn«¦æ»åuOìg¾ÆÅǼÖΜ—¸AŒ^âAÅî|GOldzS3T“¡˜Ÿ­™Øã ±ÿÖ¯{äÖú“pj\s.×àó‘Ðèe^d±±àF¸?ùgÞgÅ–ŽSïð\|¯Íaå»6œó ö¥?/µhæ0)+µ7if—û“ÿ½Ñ¤.ß)¼º“],kŽ1ÿ{ùÇ倓¢=îcœ„-æSsF¸‡ô±¹Ï¨8¨éi«Rßa7={Áûë6 –A½gls…És‹QLË\Vö<¶=DÌ ¦I—„3v`ºÔ¯«¹_ì¥MÅÜš’¿[ìáÚ9V˜’ÓÕ!ùLrY½ÙCÕÇ †¦Ú™ê=æ='C³µ‰…ÁôˆÅ:âê}×ÿ4]>4 d£:Í]w +áQ¶?缂AlüLjCû—háÎÙÔ,ÁùXïòïø)r jü~POô°¸7ƒœƒÛO·ÁW^äÿ.îqù9Å“ôåÅÆ—÷vZcb¯«†SU7X|°ismynK|Ln9Hÿ¢Ú[«i {¢žéúPÜUÙõ^±NB{…8ºÍ•切Ëw¡ë•ÿÖçS·}ð‰|¯þå¥ß¹÷ܽqýñG–ë_¿ù=oûÞ¿êú£_œÖ±õå¬?û¨ËvkýÄÚÑk~í]«Gö¾ø_·ö7÷yÒZž˜ïEÖùýø?³ÈxÀa\°šù¯Îm—§Î½cJM2¿Sþ÷Ã?[SÿeŽN®÷л¥c6¿Ür(㌺z6íüÀׯVæóªº«snqµ©#àß…£öÁ¹®Îa‰†¥^p[ÑÔ²“ì\lð ßgt¾Æµ/|N}“¾l†bnÏ[ý }W$.Tœ\Þ»£ï× ´|ú†™äc-×ß%ŒÒ¹»hPWn5&àïÁ;¡>¥˜Ýʾɋ< E'â/•{V 2øœ^«{ì«ç¦É­ø˜´CÏ8ÿ.°3ij 7ˆÄø8«<k â=}åÝ ƒ?†Tp4°í}Ï߆k“÷ðé76Ì|Ãû†áÅÐç-û‚­¡§’™ò=þŠøš„âΨ¼#R/Ó;°>²•#êwe?+ïq¤šÅÑ…ú±ùÒ–'N8pÞ$³ÕìÙ7ÀèŠÊOþ‰ßÇ_‘ƒ‚ÃfÛD\BNÔ¯AŸ \xÕý"¿#½çz)öëÐdÉ6^ˆüV4¿b|Lz3ƒê`h¡é«‚C3¨o™;/¿ÊÝŽylO÷ Î{ß鈫kœ¶íþ•õ ®«û…¢)ÔI_{Áy êÉ©¶p+ŸÆjÝv7‰!|¾!øIåZ™~¹ÅhGyàó ˆûé÷•͆'uàT'ŶžÁçÁG …ïZfu—} QÍDïcøZåÄ;`RCÕz(ë Ì-q_šž¸-->E<ã³íÁ·x_Ϋå¶§Ô>ÞH§øÛ †íwNö¡œ|¯ˆÅàbÕåk:f³ƒqÉö¸Þ€ìËN{þž?ñÇŠþßïŸÛÙýçïûê½üç?ü˜«Vïtùv¿áãÿi-ûÚü9'ø‹W¿z™¿'çÿY7ðè›Ýç¢ççÌç­%ú]tߺAz¼9¾µ½}£ê_¦¢sëúv¼¿ðÛÔb#êe›Âhb4°»¹jY‰z*wÓ|—yÙ“9§¼ÏóÞß _ '8†fuqhÊgg¼ }VòxF:ÏeOðýAëS4Ž*¾ƒx¤ácGù…«ž×쬙ÍXjq9?ë¡Ñ*—}Xè®DÕ¯ËçÎ*wQqiã7çì8¹ÅÖìû¶‰;\P3ÿŠ@—U¾§¬-<»üÒaQÔðñk³ïPèêÙff“û¦×uÍ÷u5wØnô£,—È{ïššÛ -“ÐpuCÕMª9Î:kt(Ô‡P0Ÿ£mh…ñã¿&úBõN ð:¸ž¼q=¾œóN\M_,¸ ñe~Næ\¥¹6SÝófÒÔnTÜèêü¬RoÛ'ÿäçJ ¨Þ!l[O|ƒ¦ë,¬³ZòœDß¹8ûUµ-î UïÿØj¿&ð âupyùõ6–Ž`Nƒõ©«uâ(GÖœ~¬YØù lEñËÌn¨Z¬pýFYsh˜‰{‚o0û}!áàé4ùÝ%g\“ºø4¾^ZÌ›œ¯×“·k=ˆáJ{ˆåšZd¤¾¢:~¤~©˜Ëï7uÃþLko5SÞÇø.ì±°#ü¹sÆÀ” c:F¬Uæ~u ·¶#—VUîŒ8‹ÄÇhDÃ÷ö™ŽÊÉ˰+äñš'µQ6ÚûÃJ쯙%¬-w³#æàN µo£orÙ‚õÉ·:†2H? L,T¾”¾”iÍÍÐI·Ø ï×Ï?äßrÿ_÷åî´z˜Ã/âϯÙûó›^´Þ?ògwùçî¸wø¹%ÏʸWÖô;ó#ÿ{ùÒÛÞ-ósßßIqeªñåÈs!Ù/ù/뙞Yÿdþ;´>ðÉî·¤¹Eì¦3¸åy‰l=xN/_š8WÊG­p/¨ß{ï<{Ñî ¸yPÿ)x~½àX>»ÆïÛp£i±&ðiØ‹¶Ž¤{É-ÈÛŽ}œT(¯ËÁëv"xZçR›lb‚|>šÍL8Gá¬g~Öti£áá¦wÖ3óZ¶qzòÏå=¦ÿFgÐuï‰ù„+?ßñ½­ù÷±&^·~.î<Þ;ª˜‚šw¤÷Mw a—›š~$wÒúvè‚E·5ûü»êŸMüŽòla²[Ôš\¯G9ºk>'þa*ÎÙÁi2û²‘¸ÓâÁ-*ϦYÜø7LN6ñ¾ç~ËúŸœY{ö‰ßXýê'Ÿ\¿áªË ?°ÿÚ'¬}øv_\û×k?¶zïÛËÜ¿»ù›ÕO}h¶ÌkòÀ§üóâØ×þÌ’yZ¡ð§žP¸Êïr  p›é¥Ïg=ã h~΂º¸ÙM‹Ÿìü›VuI4BƒpÅr©™ÍÐ`fèâ™ßW'ߟk#G§âwb+»™æŽ²ÇõŒà˽—ßÇ9i0Ÿ>4=lƒëtÔ^½ýÒ×t*ü^ØÙ üVpjpI‹Ðôâ)¦‹ôM#VìŸÿò÷p·‰¿cpm´cÔ <îRŒŸèµ!ßÞ¯}1Å6™&ß9o§¸*Ï]˜Öu4ü_ØP¯}l|ñ¦¶òYḖCroÛs¶$[]püù\©/S9ÒE¿§â@î s Áß[¼Dø¹ï™âç²`Ïç|z}½×]¥þ=!'ÀææïÆAÂk)«²?5æ¦ç«¿“W oP|ˆbðieo)—)û^þ\6>V~úilz;'*Ÿ®¼3˜u8…®êûÝqω›„±/ÄÝá{²ñG§Ú󞜗:Aþïüžô¼ÒÛ4~‹Ïçp¬ùŒklX½fßtµÀßé=p­•–WÏF›ûæÓƒS‡Ú”È‘Á;Û—;K^¢žöãä¿WmMŒ<4ßO­—óÄYOã`…#Ü“§‹ëãö¬Öav¤²â÷Oyv'LÖóêüý¹NEŸ%aÓcMü>üzáÒW5S-Wx&±9¹ž'á½.ºlmT¾Ç*`¬Äæ·/¢¯§˜ÜêªÒÒéj÷ˆ~rfŠ8G,Ž!ìåÛ*Öá9À¶;ð%x‡õ96¤+¶Á7á2ìÛ\7rbør2¦•ßt@Vn½çêû9èÐãÚ—†ýŸâ½S³ˆ¼ö®ØÉ¸ÑóÎVÞ‡ûcñªù-ÕmÊž‚oØ]1n€ð òö{ÙïK[™<Áö¸øà–—ê]±žjùT]Ç¦ÅÆgõävâ¼+v:ð88Þ2ûŸŸ/Û´¼æÔÍñ)c›=Yòz«%æ:H'*Vþ]rõãôʉ†I˜/™]¢7rDü„Méçøñ/ù¡0á²6ÔÄTÁ/uÇ»†·Ç¦ÄÐôuç?}Í£sÏÿÒÛ¾iù/ýöÝÃïÜ]yì½çê÷ïæï½ù_dî{žù³¸ý‡?”ù«_|áO.›û-Ü—¹ß+®›¥\²Ò©ÒÙŽÄæôqM*Ælñ-…é/§O[ï V.zKë£Ëx 8àLkqb˺ö¼·ð¼bSvÞw@ù÷TZ„¹;y¦°Û¤Þ‘KjºhLº–|å*=¬¤Ü£#÷jû%t~&Øî|/xÞÁ¼…Š“žsŒŸü‘}hî3~µøÆãÒ‡3¬5ðÚ#©Z÷ÇUH¢¬½ á’ê¶ÞkÄ,k°.â²9\ê6pßÈÅ”ïxí­yö õ]ì’òáÌ%gvÚø^̳x[5ôr–òÛ¡žšÿN¶e!›ÖéžFî¿ÖAfØ6š\{\s8[_q´ûRm ?Žþ“9ý‰è +÷IèËXŒµBK>˜¤·J í3q¹rìÄÚ+ ê‘W®¡Y¥çª§žgyØÑÅLz”•â:©pA÷cŠ•:θ,5dåŸêŸèäwu`kÊ3œ?êwØò2lðp#3&7œÏ-œˆœvBl§¾=ÎA±ê+-¹¼á-Q¸hd&Ò†ÓÖéÜGø4è|§\¥Í3ñ£‚fq€Ó…,¿ƒÆÎ\RŒ—ÎûL-ëYjêçä·¼Žî½dà>Cª¹WÂq6þ›¨û4¿6Õ*'ˆä%àáÊï#˜aŸD¹™l²×PÊg×y¹•o¤ï(Ÿƒíœ_ã<‹N¸E †çw-4<€}õ)Þ‹f±.ñ±ú†™yãýHØtÅV .R¾ôÑ“á«T›UoÐib‡2³Œ9íy²?±{–¿ë“û­æ½ºæû?2=ÌûWïùªËV/,Ÿ»üó•XÍ>¦™†ØÃÃ~áÊÇ-û™—OáZ0߇~è|¶ñòmI9E?H¯…˜¸ÁÛ¿#› ß÷ÿãê]à,ͪònnÚ"‚¥2M$´~À "¯çݧªZµÃE * 2&$4ÎDí#·Á%Zñ6 ´JÔhëݧªñÒÄãmD´ /MDÒß(F *¶úÕZëù¯½[~?~3Ó]uÎûî˺<ëYÏJü¿ÕÌsõÒìá¶ ±vÖæ`žp̨«‹c4'ÎÀïq‡¸kÜkÎ?k·ž3+ÖdÎV´„Àˆ±ýÚ×ñée]̦šÏÀo ó?Åõ¨Á°Ð%æžß2gqÞ!zR:{àÉäïñ'ËÅqûšo½uÿ_=í·?ûýß¾åõ¶÷|hó{7^´õê~þÖw~é?nšÀ÷ï3·nÀ{—¡#õìå÷<óýVï_Ú±;ø´»>¾4Œ±óãsùÛ9Ä`㪠{<®IŒJïŸ}†Ýeìu6|=vH±æH.Öúˆ×ræ´lz²Ô}JÜ­c®ƒÓk6 ê—ëØ’œ†Z7~ Û¶ê8[`°Ê§ õ+´v°E6ºê®NÌtÔwuÂãK´,N¼² ÷.Š æâ3–3êá7QN5ijuVîÜ ¦ÏÃl…˜ÃÜóŽñŸ-O`VRÎâJÿD¶Õ[ãŽbÏW·æV8ŽÛuLŽ™aü}¯'KN,›Lþîñ‰¸üþ~v®x6ö6jŠ—Sï«õ.ˆF·Oä"Á ¿¢|ÍuxÒfÉ`ï2ǤÿHíNöŸ|¼)ç°/ˤ^gF¾~&»=v|ú¬—ÚYéë×Ôo;-€ ¶"-4à*x‰ýN̈Ž}Æp–ÝL7­ƒŸ½K©×¶F,;á{ô;»Ý:Lâ–ó\ËUj÷D¿©xyuHŽHÜ5³QgL½kqnɺÚéá‹È9uçü\ý îïεãñ~+|û]â-|Ò ¾"zØ`ÅœYa´ÇÐw˪CÓQ(`žÊ'ðÅ\E1ƒòâìKŸÛP+¹º¹E8È.v®¹ÂBÚ³ìQ—‹e ÈöDç=÷›wY4—J *̧hNÆN„bÝ/ÁžSµô±›=>qFÙ³…z9çírî óç8oØÛIµÏùJ},à 9ü|9g£Û1\U/\Ëšð`ñ8£ŽÁ¹PŒx¾9‡@ú|;hãÎeÏGâÎFŠ?ó÷ÐA Ÿv|N§0ˉZ^‹ÃO=åÄmûvþîUߺÿßþßiÿ‡ëÓ÷¿ëägï=ëüõŸ±Ú_{ì·ìÙÙyñ§ÎÞþš=Ó>ZcÏgŸû¸3{ÜA­iéx©y8ÿåŠÆö®«µ: l b_qÙÏQ>ÜÁùÌe3£b-g7€ƒ*¶*ƒôGÑÅ_Úg5ûzè+õ¯6à:¡çÁWw…½PóJÎtßcC¾)~s]W•ÞdáÌÞ]êlMÖ¤ä<œ²Ú{xñÙ !žª|Ćð`¿Ÿõ)4hÞ_“ÚwèÕ ­¿ :óœ¨7癄£¨8³ðlÍ_%?§.¤¹ª÷.öNü¬ui¯Rûë8>1k2¨_›?ã,ŸÍ»‹-!O[E½Ý×ÿ Gƒ|Å| üJû3ñ†ËJü®¦ë¼ÆšjOr¶Ç®ê8-ù.°‘ª:eéðá*{5Jg›û¨{]ù§=?¾IvŽ˜jÄ–uqÞå‡+Ðùaïl½Ä§(1»Æyä3ùÜ ö¿î}Þ>; kÌVËÆ,¢·Þϲá¢aóoê4]ÏWâ]z_™´.íbzüyO8{ð;¹#äôj ,`7øSÕ#ç¢N øÐc¨˜Áz˜˜°ðþ´CÌžZ‰·´'l=¸iËñSœG´(††éãQLùõOù]i»y-Ûÿ)\_6Æ~'L¦èLù>’ÿÈ÷Túém›@[flHí~n"ÖÒ^¤¦;±Þ3Ò«Rï$æ\[Üpˆ˜ÃNOkÄüÇçÊ&½cURa¾Šì.µ¨I¹¡ó,¨=hfÔǰ‡KÝæñê™C[u-‹N?±ýÌÙB~¹ƒ¢žLÝ—ó×󉻸“Ô5x¦˜”½fUq9ñLÁ¦Ùéw ˜Ó“~ë ­Çovë³_¿¿ñÑG\¸iëyû?÷Woس\þiwý—ý£üÞôªéüÜùk¿¿w´Æ{wÿø[µXàèïæv§e;æÂ|ÀÝÖBvVygΫt›!ܶïì9q~î¢Þ<’>w¼ÅjmkK8Áò%ª¥»Y‚KÉ·WrCú”±'×JWŸþ4ürÓÿEo5tÊÖ¥%M.¶ÊZ¬s×ø™¬‡ñ÷à0ŠÝÜ—ãÿÀ¢ôîè_Lì}×WϺ’GÁ Ãð3Òè6p˜½½øôo;=åôŽ1ÛþÁ%¥·WßÛiâ\V­'{ÇÜåƒé0×}Bô”ÕòšCÒÍŒJŸªw˜YŸ)xÚmÄ„¶÷æ³ç½Õ¹mÝ©³ÒçE¾?H׎¼ƒžâû|ÍåJN5wryùàÄ™Á-Áóo^DäÉÇÔ;¹“5,~מ®Æ FÎ w…?T“_…®qiùèáØâ½Óé÷#¦î$¼qÙß]þ\‰Øÿäÿ7¼{]Œ1Î`Ì·ä~êwO¤ŽÍi·Y­vÓj¶v¦#ÖawcØþSU½5Eü±´ÍZ+úŠ‰Ù 1Úx`Eêg¢Ÿ:-WÇ…‡Ô3‡Ÿ^usÁ†äªïìFà¬â†Ì¥ÅiK}ÅK¡Cä1…¸©§@~Ç»§Â4Èy3¯ÀÇ[,¾ ”q½R`ðäšÄÖØŸ!k$™ûø(åŠþN``à ÌIYSÿçFÆÚM²¢¯)\{nÛÐ4>|¯:ݧ¿@Ü¢œ¬—çÂs$§‹ÑçO¡¹¹fhV¸ëä«Âcèë#Ž»X¹;ôäÀkj9[<‹îVíÎp…¿*|©ðîÔeÈSÑ7íÏ}ðBæ»D7¿2¯ý^Ù“9¼ø[ôaS‹P.Wy.jCèÛÙ¹×ï»egçYïûÃÍÇ>êÁ[ï¹Ç}7þnþ‰c¿<ÿ_¿»Ø4ý¾EôJÌÏÿÑËçÂø\hžÁDíLNÛ!ÞVýo.<Ÿ>ªÞ¿ÚÚÍŹà½Òj&ÿæse›S7,´Ã›‹pÍ…ëÈ€#ëËúžÙ‡{§³¡õ¸2a/Ô™óȇÔnÜû›°×Ø6âz°rûÙèc<Ì^žáæÚG¿Bô&ìk}†ÔÎÂA,ÁñÉ$›æº|Òç«Ônáü ó¿èÀRÑ çWLï¸u<òx¨ÌO‰X?8³ÜCxÂ|UÇ8—±„ÎGjfé;dŸ"žP-Ð׋úO‡ßNàNô&qîˆgÀ2°o«œYúŽè°ÿ¬5e8Ÿ`M—B¯ªÑ­‚ûP…yªÆy–\žÒ¸R¯ÂÐñÈÙÿ¹–¹¥r¿‡Ç{˜õâq1àgŠ Ë¬ì+y†ˆSd{8§…çãù#H}¶œí!í·eœû%~hÈ:ÓἌ³]Œ„îSðž°[öwÌ„OOøkðáš O©ä÷ô (>ÈYk7EïNĽ`Ī›qæˆýTc»’|+ü¹0q7<@a$¥ó¥ŽW¡ š\p8ÕhÒ€i—{¾ðŸ·ý¬~Õ³¶÷þâo·¿òúoºðœ?þóýWüåã7¿ì#·\ø¯ú‘-ûÙן¸ßÖÛ~á¶þüÕ÷ÚƇxÏò¯c'>wßx–kÙ»Þ÷)Ù;ú~ÛÏoÀ{=F0ŸañLpsvÆ8HGk‘³ÆÁWÑë?ß[‰× øºðvÇ[|xÆK-Ìï‹úñrþï þ=y,þœ™ë iÿp?e‰½3okµÔ°SpÆìl©žyv²!;Ä×5j×ÁaT>?Á3‚g£Ü¸(Çá'¨šø*õZ°.ž!xt¡ã/th³Z ¾¤_øÔEpª¼CÂñ¹;E|£)š¬œ3bÁØu™®–9ÑO‰F_‹Çv„O¯Q{Å6%ßSø÷DÞbkžØ8å×$O ¡žùfLçüòB]D1Œ¿_hÎʹø'lzÍGð†…t"í3íX®JÏ¥ÙNê‰àÙè#ØÂÛ³À¹\Ha6d|÷Eb£iPï v[yJAËQþŠ|zÑØÝGpñQ{ ö_d·}?¹ëÄ›]} ¨Ï.ëÄ×ð÷Ÿ»Ž9¾Ô;nbä§øLîþTv¡P_¡×ˆ31mØÜKM—“³œýhÛÓ›¬Ï.zÿÉ5×4O˜=µçæ=°]`*ö¡åpM ñ•Îf,KÝG¼©qèZrk‰Ä5»œñ÷ g9$WÊxn“xëpÎ'qôr‡­ghäæ®ýÍ/_7óØEp/ðy;ÓâEírvñب…to–>±êEðEgÄÿ̰Õ{ÍÄA˜87ÑÇ{°\Ç–ÄðÄø…!ðÓ%=PŠR‡e¥> ùîB•:®rdñÓÎçæWèy;M=GZªQO´»‹½Q¬žº\+õŽ*Þv{ý©ßrqßÖê¾OùïûŸþYÛ7m¿ç¿í'7O¾ëS7ùÛÿmë•×¾lO3a÷Æú¯{_ý??§Vúü·}î¦üÙÜ|\è|_·DO5bè“Gÿ}ýœ:üÅì`ÂsÖñLë¹J\œXl7½—c-¶WøFê\ƒêÃÔ…dséMIÏym8iÔ–e_fš“R¨…ÆýuîO¥ßoÿ. U}› ów‡¦‡¶±æb^)pYé³Eo•ž.ì«øó°1ÃSk¨Xö”ÏTÐy+`~­'2¸dô-qv‰ííÙTwûžÀ`âç®CóÝß~/8ÌkÌȾ#|5< ðb°í6»j'çfQ¯"Ñ=¥N0uœÐIk2 §*`üÄQüÌJ:p~Ö¥¥µPßûªãc[éE_h=ô@’ÿÖëéž«G÷šBOˆÎp]‰³5g§f±žü·ÀȇZÝ1ûð3¾›ØœW¶Ou¯ópã|ßB¿?ò&ðPüÏÄ:¡q€ßÄ?0sˆïÒ³â#RW n*zRC«?1c _äDêVƬ-;«ƒÀ‰„Oó²eü~æ67çlÃ]f…¬O[8_è5G}?ú¾ˆã©ʧUê̾– ž{àãxža¾íDjj¹]¡æ>¥«ÍÂ×Éþaà1žÇØï¨.æ?§Z«¯iô…6õ5l˜úž*¸k‹ãC]x†ôcFñÔüµˆUÏ„–Û¨{—ºE­ÎpzNO.OãÃl¤­Tï8¯Ÿgðùû%õ)ÅS#X€=¿´‰Ç®¾*¦Í ÿsé F/¼-s lø¹Ñ¢imŒñçólp¿ÉÃWâØ«îM>PÈñl/©ý*ßšˆéíÝC?-œ_Ý÷™fÛ•›Œzß#ðÀÑnö¹W¥õNc¯ï~ç»¶íó^½ÿÐíÇ~åþö_½ýûö×^ñ’ýé[ï¾õ‰c¿¼ÿ÷ݰeïþǹsóÛŸ÷Í[?2{øÞ‡·¿ð(ç¿c~Çß|מåüöÎÐ+—¦ ˜l¤=“?-äñZë™ðÖôkÂâK·‡þ³Óö9§ò§]°ñnÎVÖ›•7'ÖI^ ÿ¾“j©ðoü~¡—GÌ8#@[„ÜK¿³ÄŸ Âöß…ü¹_UÏùÛDŸshdÇ'rÇUέoúü:oä„à´•¸v='µåÁïòmt°FÅî#¾‹|Jü…‘¸}„!µ`7²wRq%\3|XçOƒj혭uš{:6Ô½õ<9—"üŒ3C-:núî¸j”e!½åY§šÆT桪=zõÌÂЫ»¼{õW奯õpN9ëëÑ#8R;„;ÈæÙ³Ï…YÉæ3ˆ¯x:y@+Ÿ›Õ0kòfúZ…1—˜Iá}̱a=Ê¢éýø}1¼`¡þü3®ÙtÝRù˜«Ÿ70'ÃäǧÖ×Èœ’kèÓÁ@Í—ÂQ…ÇOþ"­é¢ ’v~WœØ ÛÐx‡©™ªx­‚ËHçw¾R/2µkòbrcå ø ðùæÐ)böŒ| ñØëty{‘o…ïFîìq\Ó…;ÏAGƒØºPw Ï€“.ILË™C¿ ­Uö1»¢]¨³ÍMÄŸ²·ä,ƒôÛèóÑ}ÉÞEêä/ƒ0?fÊOR/Ïù‘ögÔÿ4O@úÞ<3}”~o#ÿ¾¬<,øðÀÀ*T‹¡¾®þlÖÁWâ˶;êudïvçyî‹©ÉÀš‚yrFô<¹ªôk¡5ágB¶ÈÏ >›¼’þ–Þ+ûµþEu̹îæÔÙב~ó™1Ã+ôÚéûb.¸½×~|gß>û–7|Ûþ¼uoÿ†‹_²ùÂk67oºýUË;í÷7÷oïxÓí÷Þ{ô×~­õäüyq|çøÃ¦ß³ƒ³l*{1´^Åçó®+ÆgxhÃtÏ&N.‘xb—ÚÍzÓîò8ýfùä]üK«ÙÿL©»œX¥ìK®Ÿø=5jÿ§Gò=í©âÒµ Gg‡ûŸýë¡5ç ›ÝVÍt"¦Ö}ÍùÑä!Cp2²§‡˜Bعøˆß­Úje–µêÔþ»Ñ#u™ºLú ò)Í@Qí1zwÕWIÎàw¯iòF]ü“óÁÙ%Î˿Ѧÿž>ÉÈIº(¸¢¡W÷C9QUœìßa÷ÞöJ5(Ùç)fßÅœõ¨ÿ5wötðùg‹î!õ?¸ è¬'ÌzÞ ¾U}Eü²äBÚw™f8±J‡%¸MÒ<4”¸Wࣰûqh¼å]|Þ×ת¯9ÁŸâœGüúh`§á܈·S•¯'ϰk8aÜ/üxÄšQ·/é°N{Å—eO¾‘=«ÍŒWNnƒÛzƤµ>º>þ /F\¯±£ Ÿ¡^üˆ»Ts^;÷ZÂñ¶Ÿåœ¨þ, éFâ‚û*8EpAJÔr[?!Ýl0öî@}ò«y uɧ?ég/Ø3~î¿_»ð‰µŸ¼pôÙóó/zÎþ=ç?lÅ{7}âÉûöùÿñcö~ñÑOØ7Üáóî½¾÷Ñþ‰ÅìKûg芜?ÿm?é<žõè;_bçÎHËÙ¸S#ÛÏÞ×¥³ ôðÍå_Y‹ |°Þ…æSáC¤APÑäÜ©vï\ÛGÍÊx/êEÇ—èÉ‚ÛÁñ¤fÏÒŸ—¡i^N‹ìéˆÞÓ!y'kÔƒ»šÐaö¨€qÁ›Vœ>g/Á ‰¡‡Ö’Ç¡ü×6æ-ǼsjSô‡,Ô Çìp^|:~]±Á®8[“ñ=”Wø÷¡ßÀðkÄg-fŸF¬sú;Љ.c§SŸ¨Õ×ܯÑs±ßeÕêA`W+‡-‘GEyÔ‰}Ö \jiÝ’yÒ?þÂÍ[ö}÷¸ß×n½à ?±uÄë6ÿüÕ7ï™àË^ð‰¹ò¦ù}ŸòÍEh4Ìá6ùmiA^c5¯®:bí.wIî’ö檘|“œD6·´uÊÙ­É£[IÏAi/Þîlø4¨¯¬óÊ£¢Ì1j*‘µsÕzÉ­¦& ÏŸ]ú8+ŠOªâœëaç,BùïLü€%1¶bÑ¢¼Ž>Åš‘ƒ…ˇQWʹUòõŒ´–.å?se8 3дç…<4⊡ãi¯‰&úéìý„ ¦Îà*{‡£·ƒ: =,²qŸâ ÿÙúН„·¶R} ŒÍÎ÷Dý…θûkÏJm›˜[¾0s7žañ]9¾€Èÿ¦Vºå¤ð&m Èá¶Â^„ŽP·¢+ÿ?CëQëéç\õ†YäI×0WÆý6ïÝ ¯Õt¼DqšùnëÁ;‘±‡r*õ•\–›õnüùAÌ3È~àË\ç-{ËèCàþ¢ó„ ÓL½.¿Œ¸Ž³EDÆqÌ Å÷êÌÛgÉ6ÿj‘z°;øùÄÓåW&°8õ“-á.¶ÜZ³ŠÑÆ>LJsANOÍie6›>pl˜ô6AkþvêÄ,éÿg^‹?ÑÍ!êtýÙˆÁÄYñ)âGºÿRþáñ„lø¤š¨÷S’KDm,æëçÝß.Ôó…o\©×/¾÷¶ŒA‡N‹G\ ±‹ÑÀ!v…í½æ¬Ñ·T´y~ˆ!é«[¢Ž¬µ·IÞ qZ‹_wÈsÊBýÚhZÊç%üßü|ßÔÍÁšƒ91cОÝüpÄsÙ õÁ]ísm¶ß¢ãÿƒQ#ÿ¾~ý¶=çÝfïÜzÒw|íöÑw\ø¡¿á…Ík?mûÞ÷zÃ…£ïÝ’=ܼéö{oÅ{×÷ßܳް¹û±aßò|°8³Eâ½Ì ·¼×ïšÓk-®ÛͺÂöŽz>å7‡‰[/ìla]±‰ò‡ÌRw?¡º¹ÿ=¼]­_¿9„vÑýGCxRÝ*ñÅ´i³©GÂ9‡¾[Ö‹©Is‰ÏÊÜ.öö6xH=÷kâÌñg`hƒ45È{dsÆVÛjz«äÃmêä|ZïìŸrÞbð2Z,œ€^‹Lw¯®R+kM31£Î0)ažçõéÚúDïhàvš_E¬XPð6 WñÒÉñÔ—˜qëÁ:Ý÷‹%ßGñBAcžmàCÑë ¼ÞtÒüXÁá·ƒê ÂäF|I«'E|h÷íG|þݾÍ í× ³®y5ÔBWÒKbV\ òàUô‰»-%FBŸTï?ŸÞÎè²ÙÔПã°µ0lLµkݯ[øÙÝàv\Ìü‰ .'\âýÆÁ_óº!=Öƒp7øIzß9±œüï9î±eëídŽ.¹FÄgƒótŽ-‰éˆÛÏ·™ŠÝY"ïEǼª’ƒ…ð¹Mô¼r“ó9„ßÇV±çÜÙ²ô‹ZÇ"\<ß[Aæºo`Ï™[µx5ë =FI¯³¯wÜý¨CÂSL6ƽ9ŽÆ·çEƒúy™}"Íòþô81\¥ž/?VÚ»æ®îª–±›Ö½b÷‹ÕUÓaS]7µŸ“Ë#[ž6Ö>»ÅµmžØçœõêxð—'ñÇ+=º·583רÿr…“€Mçç°§øqA’WJG{2“Þ øì¼ãÓ΄Ïf<ß÷uØ/±Ë÷ØÎxÓ îšòªÄìï>þšGØÌŸò›ïýôý‡âû³÷~ÞÖ§ýà|kö¯Þ±÷¶¯Û–qúì{mîß‘ï_š.ÀÑ÷ÎPóq›º›uèz5Áe¤‹cÿ§þ6F/çZÒÓîuòÑȳ6R‹V±‘sòäã“Ki¿«Ø ]íËit¡™ûæ¶«aýW¤ùüÓÆÜAoÕã/GâMÓïBŸGùš³èå¬íN5<“¸›»Í¿9—ø0ë䄨“u×Y‹œ.1„îbêô}ݲ‹‰qgõ»ö3Kð³Õ­m¦t]—ô5*6*ýœU³ÌM4_ ¾}—ý½õ€ÈßeÝL1Ç©òÕ%ÎïÅÔ+Š<¢ñ‡ÍáUóšÓÇ©X8õ2ã3NÍ…?(¯\“c.÷οðoÍFâ牨ÛÁ±3\ˆ'B Ÿâ€5x=³AxòÐæRq7Ý4ŽœáCøƒªa&þÄ>Q&–ÀwëžežsF}çŠ \å0ð¨gØ;ü;뾊yì~Ömm™õ¸Ç±y;ß;Âv¼ŸÁã0íÅ Ûïë@ý`]ìšó’×GÍ5êüŽ/°;z4A¶³·iÐnüZx1µq~ÏòÞÜ‘IñoÖ"ÀAuŸfâ£êg6æ² #\ê"¶`_]Çœ5p ®ê~eI?4« q>kÑæ*ŠÃ¨íH·yß‘x¼ê—ÙãDO ¹ü åè•:!ÚbÂdüÏmÈÏ•wÈvÏ^ó€—çg•Kßò”•ê…`“Òæ.Ê«Çøç#%'Oy^!'X¨_s]ÚOômƒµwØÚi&Ÿ—˜C¬Ã¹ì5XÝšsåyþ‚%~Œž÷·s_ò®ƒ)‘›Ò¯|ºTÁ3œB=Ñ‹ 7ÅßQñ4yB¡¶˜yrêwåO®ªñp>µ/¾]µåJ 3H« L‘8MpÎýBÒõ‘ݱg²•ý\æ*¿¨óóÌ£¡~DmMz‹|‰¸ßOÝ,0Çëà#eyx4¶Wk5“§ª–3E<š3‹ö”:mv,>môþ),ÄîCëp›w¦ÍLL~Úhzoõö_™ÐPÀo SñØWq ¶if]YƒÎ{ì¾h¯•ý¶TÐªÅÆëϼÌù'F@—óÚ˜ 9gž€KÒ{>Ñ4¾¦Ay´°?Cp,?ÒY)œò ÙŠÊ™¢æAŽ9+oêx…£|?=è~Ʃσå,ž¾î=ó£áþx-%uá‹‘säS¦V; l“8•¸œ™Z±ñxoËë½ÿ ÂÌõUÃóç‰ZEÔûˆaÀš†„ŸÉ6Ëk ØjW©ã.Ú4ª¨]';mk¢ØwÔùR \æ;Û¼ç:ÌâKhv²†G)ê#×ͱÓvö¾é¯æó7®ÜoÿÎ_‹YÀëâüÚZßpñ¾GÏÛO._råž{ÊMõ~^§ÆV$ÿC3Û¨áTù­ôͪ¿jÏNU8áäfêoÖL ÅÉÁŸišˆœŸ%ø©rÞÏ…fÊŒÒ*±µ¯PÏKh¡Ã½·Ÿ‡{’]ÕeÁ€À^¨->ì¾i¹R_¡|¾¸)à$Ésv)汉S—ºq{J-Pç,7yÙÄðÊ-'b z•ƒÖæK… ¬Q/è1 ñKržÍØÅÄaEÚ~Ek¬gÞÙ+xpÂJ&òСñ&þµÓ/WÛ]jô®§{QˆùñÌí;ì®â3t'3fŒºÏõKôÀÍXWû¿ÅÎÄ_pCÁ‰ÖßÂ\úX븯·—Ï´^ð[Šì¤f6ŸNšiØ Í‚¯äFì ˜‘‘.Ý)ÝwùzÚ™‚î|Ö×­DÕ'«ò¥Úúk".±ûEMAÏ·+=7>_ziÓzÓ,ƒ´ê€[£Ï¯\žCqˆÏ^ÇÖ^’fQW#ÅoÉ}×&FÂ×âK`ïý­`CéȃˆuS[D1–ÇùŠ=3ÇãiëÃÛo5<~©°š€êBæŸM ` ^-ûì>0fJ\7—.NÎý³ü¢aÇæêm*±çè•·ýµÄ9w ~/u;ª°8Yóvi¦àuçüØ^*> Oxf"G$êjE¥«î¶;uºóõ×àï‹ÓÏŽâÙ:?ó¼Ÿìá\üN姇º3Êã<Ÿ M5€©Å:Ñ»`>Nç–ºWƒ‹<.tÇð#zþäÕÃ-à|+.^É‹?›ñ5=«/ã¯Wó)Ïúlljš)NÁ§–7ØîøëâºssÕÁ—äРͽêü®b”¦— :1‘UÔHÐù’}оÀî>û=_eÞå'…gÚñefä(=kQnñ<÷oÑtCÏ _¶§ºge¿tÏÍçøÚØy–…×ï9^ãÖn>ň-h}T‡ðÉÜVR§´3l÷¯Õz+·¸f5C‹Šõ#^$WSŽØñúÉ—ý3ªúy*ub[;SÂÀláýùφŽÈéÆ+;‘3¡Nt1qx|õ³Øß˜7Õ×Yo–î»§îÞýwj6‘»6Üoz»>+{/+9žîR#C^šêºÁ©SœO•„N_…8,ó{Î?ÜãÆÊYÜž‡)nƒÇÏsã£à›OÔ Â^nPÇNξ<†{ÓuxßÌ×J|ï‘Î> ÙGò–{®¼~cmZ牙úzxYÝzú* ¹õ‡L¼òð[—ÿö³‡M› (®‚ç¬öÃJ” ¢é6qÖˆ?é]Æ×.TWE‹•^ëªõÉù•¹ÍÕÌñ ×jZZªyýnçû\æ4Ò×ög@Gµãòs'R³œž>­.N£ÇÈ}’êqY‹\w¸ãKqf=è8º^Ï£·<ÎeØSr°31·w Ççb¿Wâ¹£Ymg…¾åuõÔ€³ •¨\"ÏJøÏãóî^Läˆ`XšýJo­âüSü÷ÔðËÓàìpÜÔW÷ì¹p¹.¯tí?ïâ‚j„¶Vò•?fç »Š¶µ¹•ë=[ÚâQ§Ž1Òk!¾±ßI¸ˆÄç¶6f$öNë—3ìáucOüjî•:ØBš017/fç¨vK¬_˜ÔÕsý³àN§‚iÚ{à"†»œgeˆzB%Ÿ½àïGù 0ª½Îþnû|jhí 6r1ŸIgdFÝ|uý-mN-ùµÎ¡j1kʹß_›ÄK&Ÿ·ÎÈÛ+8> ›êÈÄ…#8¶ÐîE¯w¥<\À5I‰5À_Y߆áçÚÔ ½ÀÕ­èÕ‚)í¤]`Þ¹n—ÏåþéÑç?b¶€ê]µ« Ãÿd†ÉŽÂYR=¦ «ÍßÄŒ»ŸêmÙ ¿è³²Óç ÁåKà©;è•ÍÀo…¡º-”õód¶’œI{í{l{"m£œ³¼ž½ýQ›S\)Ü‹y‰W,Ùcõîßc¤¶F>7δr¾BìBŒþ">8\ŒŒ‹b®_tZ¬êsÎXÂö.z²!dž«<8Í5Œ³7¨¿1ò ë–ý<±Þ®’«ÚßË“Á¯‹µ¼f~ ~€+E[Ÿ'ã`'õ3ãýÎ?áäuãm~ê[sá ¿÷½Û_ñgËíï;xìÛoùù­ígüØk·×þÏÝ·íýïõÁ»ö~ðǯݺù“¿}ëç^õûw}þßm~Ãkëö<–[Œðà×Þmïšû~Û¦áGï¹´™A²íÌl˜ÿÀãuIïðѺ,ÁêѦír$?óÒ}õw2[%ÊÏ‘åí¼›âü²Þú@R£LwW9ÍE¸{»à[âøŠYÅ¿Pý<ò8àýØÄ…z¨…у2Dÿe]×ìbrò4ìÏA*¼³êÌd?¼}¸º~¾t§‰©“£Fï6ñËi§ÀнP<Üú*¨áÈצފí°5bø]0^å‚Ì^,ØcøÒòA`y%ìùå¬!óÌÂvyÇ®·÷—:Œ|Ü!<ÍÔEZ50åÿ¡iÀžà“ÁÕdk»œ1ûŦõûf½¶ßè(¨¾NBb®üyàs‘§nnsÈææ~ó| ñ¶Áô…ÛNœÓ¡ÃŒáלIݪ8‹ä¨žƒcSÁ5Àš[Ý{M¹mhÀs´s‡Mm= 9Çœg ¬ì¸Òñ?Gâ(4¡áÓcã‰Ç©}+Oåì4¸½à:ZÓ‘üˆK¶™zòØÛdáŽ>·Ø8õŠ YKÎê çÓf€»ržuÎÉÕ§¶ïp-Ñn£j(iOTëöÏ”^þ=±>½Zêéqì­ïÇZ×<#j¸Âé¨ ãß—<ÎwÊã³ôŒqƒ¿qy•z/ʾE=ÑÎκ4EèíTgÐÞaosj2ݾøg¡Å'›–:#h_˜¯„£jïlýùƒ´]ìÏí³?î;­‡¯üÌû®ß/ÿüšý<ü³öžrâøÖS߸ÿ ¯ÿõÍãoþ7{öwüÍ?-÷ãÞ[¸ŽûËçGñÀò±º}.Hl¬:ón_,]®Ü+æj€Ý4ÎÒ³‚ý¿Ù»ÈUsÞ÷§rv…1'>fg9´Õ.SŸ‡ß ¾ÀÜXò˜äP²/œ p^øezL<’k’»‰¯NA6Ìará䈅3ë,ðvŽ®Lª‰øùƒû9HÿÇ~씜{ã :,ZY`d¶.`ÔX‡¦}âçF| ®}Is1ñZøÌÖÿïgqÞ|KÌ9#nQ =c2í{=PoϺæAÚgÚ=4ûÙÕ‰e”WÆÜ⟎›æX«ÎLƲÓäe#v]GPØŽø61_vèzï”Ã,‰Çt¦:®;|ï5ð‘‰Üq¡ù0ä!p…Èñ¨÷*ÿ©]ÊvOìŸâ7Õt®kð|p¡gý=S µâûÁ=û~&Ý9ö¾o¢‰÷^ÊšJþøoøáxuP€òPÿ9ËO,Ö¶=T\®\åßÎíw®Ó½ñ^)iQÅ…hÇù{X@¸^Q.6ïúŠ4³Æ¸ËQOäŒ(æ,~ }øwúˆ;•7ùÞØßÿÖ;ƒºø‚¢=LíxÓ¶&Š×û™;ä„à-œ‹äN„¹H]Ók2²C¸ë#®¸MÔO'|nx%Æþ®y V;9šEò›ä5ÄðÄ'½{ör6CË—œo'uÝÀ^š6br2'öH>}>ÚMhz«s_KßxÄ¥W¤!ÿáçXoêªõû$Þï}¡œâÅA<{Gf¸ól|‡bá‚æ1ñ¼-åº#þþà*ý?÷=…¸´õ¬±§þ³¶6²¡|FßSã>TZ§~æì¾¡áMOŠr£I}PË8ÁgÆNaùÎ~‘?®œ{^æ ªIG᪾ÛÒùª™ùijÁÚÇ.—O $Ÿu¨ÏÉ98ðáí³_âû—â–¿'׆gGQ9óÔê9§¹çÃUg9ý‰Ñ{ØjB§áaUÅùy^Z^qQç&0Q´óá€WpßÄJûÖâÎÓyf´“â™ÔS]IC[øÃ ÛÂy5îµï'ÿÔãO~ô²|éæ/lê[sò·þò‹·îU‹­ýWoÛ|Êg|ÍöŸlþó–½ÿÆ­ÿykkøÛ­ýû›Vÿ·XçígÿïæŸýÖë÷l½ Ó¿¼÷²½£³ãßkùÚ3>üÃKê™Òó\'8.Q;2i@©&ún±n€érVÕ9ªtÝý´ÚÚaâºâÍLäVÍŸ‹® ç[y:5¶z0g±É¹ÞÔÙ¨YÀõ]HšúJË9¯ÈG]„<Ǩnà¿#< cUbñƒ ñ¯üJÆÒò¿…³Ó†óÙ œÝEãd]rˆÞ"¿—f[é›×݄ӗ3èÓ î¨Î«ã.qïoT^r6±,pQâûAõ6îîܨüwR;±Ê¡ÓáZ5ŽÛúZ†¦Y–¶M¼;¸O‰e‚£Ù=±û}¨ýr,>sî~­WšID¯„¾7gµý^ò+d»kÄl'ýÜÈÎô5•‰sEN¶H$\ÑÓèù:¡mÈŒ:j<ƒúU´w9_•š ßGœÀÚ¶Xw-5à™ë; ¼pý¶¿è]JðÂ×–o¸öï÷í{¾ÿ§îqáAÿãË.ÜöÐøL¿éIß¾g5€™ýÌžíý}Ÿòß÷>ôŽìóë–QÏ=y”þœfcÏ+unÝ ?Lnùù¹ NÒ÷«Ù™6,ó iðêÈ®¥^aãÊg=;q+ñŠâ!Öˆüaì°(âÊQ¾‘Ú¡¯Cœsœ»‚ >Iô±Ëfeo?6=¸ÀÙ㇠ÖuÉ"¦¶íßi˜¿=ï«þÍ»÷ž}æéû†ûÿö›Þ¸uÿGüò¾}ï³Þ÷‡¾®g?í®/ïúü/mFàõO|ùžÎlY‰ï‚6ïN.Î]7Âm$yꤙãù™ß&GÁ_Ùú™¶Ä<ÎSø·-ÂñüþÛè;v©›ÀÐ]^âwàNëÌøý !ÿ€«9dßuð?ékUþ[„ tšV±&ƒt¸ÉyûÜ—\ *î=¢àaâ¦ÌáS×å\Ëï¹~ß ÎÛªÓñA—’ `Ÿä6à ÔKS¸&¸Ä„N\T÷ÇSõ™óô].O¨•uBIsœ\çWÏà>­/xUô–óºßØ“«æv(VŸÌù/] ì¨ÐÆjzîø0úÌ•Û,©ëÀÿÛ¸AnJ;¿è¼0Ÿ1xŒÿ¢§j6÷2¾}·qÝB÷@|Dô]“vz£Û“W]êf2ƒ½ âúH†Ô©ûR‘KØcôméýj` wðyEùÕ| è͆?2ˆëoû‡¦ìiÅ'®:ý tþ×U08嵋ýÄsqmIì<3â;Nz­ŠÞHpjjpùVê­&‡ÝdÖòˆÿ—‰:mÆ´ô³ CK‘îιÊÏÒc¢œw©:a§±6]HŸM[éK ÷&ÎDh]ÀÁăጷø y?Ô §®æ_øò åþîÃBƒäú:•Eî†/á|DL<û9¢7ÇÏer~"&pÖÌ‘¤¥7­ÔÝìEðPàïógÊ9&bUêÜÊëjã4…†Y§_ çü>8ºxhÌeü6ˆ3Éó‰sÅß%qÀ¹j¾HúáYŸïkWÑŽr»Ù…ðãœAx-ݹÌÙB`·ØW8VáûÓÿ…>ý9ú]&î"5IâW[«“ïúÔM{Þ—\¹çæ|ô©›?ðø_ۜ߷þØol¢çˆ†ŠáLŠÉ½~ªï›ºüŠüºP7´gVonj˜‚c‚itüÖÔ=ÀŽõAþ‘YçµÅq&N¨çJ=—KÕ-ÇÈñ\¯¸Æ¬Õë}®¹å—ØX0FÅ™ø&>lï½åk©+¸ÃefL‹ùÿOÄüô9}¼àiÜëÀoŽKg$𺨯\NŒM<á*¼5ûç©™Ï-úüÕ{Ÿu9¸'ŠØ¿ìE$×äL¶”OUê9¡™õ7ù ?ßôºÙù‡ó~Œ9É©ê¾ îÐeÅX牦»Q÷ +Ü©õ¦EO ,}¦:U…«Î;ƒÙçÓ±í]_ÃÀ'¥UOJßí߯.'8¹ü~Ðϸ|‚¯#ñmçŸG­YæaŠá¼÷WqIÖÒeÅWfžKp[W­'a—³ÀuWO祓u~¢‚eß ß½ 7ûìߨûG­(ç]T0$xºGI±~èlÙŸýøuÓÿ-ÿþ+®ÝþÏßüŽíÏ}ñÝ>yÃWnÿÍ/ýÈþ}þømÛGyþ–ù±Ç|ôÛ׽ç[·^ñ¦aë•×Þ}ëâ?sïú>{ËúÄ]õÞ¿‹oüÁ%ü+Ó ”v“ö¥Ç>W)a§¾ÎôL›ÁaRí11É…8ò7·É¾Ov¥¨ÖRˆ‘¨®qáøh¡>¬\L¹Ih¶Ü©ù¤ö÷]œ³¹¼k‰Žðê©aê®P§wß`ï g¶Û«¢\¨ÓñÝÔÃV­ßx†QžUð¯øMpå8nkìýÔ÷Z5ŠßŠú¦‰ëf̱ǬK$¸ÎkÔ§ zaÛv ¼VÙjyp™½&EoÞÐt«ì;œù*]Ö¯Öbt»ßñŠü~ÜœZHÙsŒÝVýÒß#gÜ­œWúìœ×ÐõYd^¤zÔ.¹÷˜3¬ç)äÃà€ðâ,\”&âEêÔÓªõfÞ¤5öuÓ9"Ïöï¶=³Ï²Øó(>™ë¼’K»ß‰Þôë}þ×Ðõÿƒ±ÓãBý¸÷1ŠÛŠî™ûIáuàtÎÉ ŒÇcÆ‚TíµÜõù/Öþ^îA¬n~n„ìaý‚š:×jnòJý":Óɹ•f©ÎOë«%>‚Ó¨˜(¹òþžà&¬ñг«ø{æ-ÉFì_¢]Àâìß¿¢­_‰£Tó)Ì“Y¨ïP1ÅØë?(G$’>{ÄWaÛCãûÕñ]Æ.n¤g³€wÙ~b[ØS°M8„Šÿ¸ëµÏ‡Ó¦]éâýÊÁŸaugrl}àŒÐ'«óâ6Àìµ#l3Úǃê·â*N¥w3ô¢­~ÙaÇyÈ5Ùµ¿'w'W8ªç›Ó‹r^™ŸƒÄo†ÚVðý‰o†äB¸^šêû§™C­:nÓÐlÍBÎr ^ZÕ’²gþ9\bòÆù ÌžY¾v¿…oíÂñ„3Îw<ëO– gnÅù?zù¾Þkÿñ÷y×þ|Ê#ö_þC÷Ø·Úý›Îüöþ®ÿ*«ÿ;ï¾ÿžaþŸùK»fƒ–Q{¸qŸÓ´©bž£ƒ¿ö¥ó¾.Ѹ+±—º?ÜkÕŸÏUp­èGs›'ûx–;§Ñs?ño+µKb åªð¡È)GÙ£Òá´~>ÈÛ©³QëS¯B×ÿš|?ùÀAë£.1/øº¿…Ú»bÕ¹æÛ8w—Ú%±Ý ;h(‚uÃ"G!ŽYÿU}K¸×+×Mö:ÝL±}ðÔ\òùÙ+ÝH¹E]e-k-cô.÷š†¬›œC_ú–=8h›®¹íGãüDê†Î9„lW…?ÖÍ[©Âòó8cÔì˜C=S>¡çÄ—Ð ¾Oð Å<•ùÖCj.ìBܾ×j úý«Ö0¾ÿ,>§P{ç¹5ë£ÈOÀ­¬ÂïœÛ"?ã~IºŠ5bçs…¹Vƒ¸œ õvªf—s•á‹×ÊVûyÞÛfYìxŸŽÞÏÏ 3‰-e¼G[C-îèBý˜Š–âBQ3D»v.¬•ÙcnkmoT”füIfƒM+q®U/Оo$Þ¥šh!o\ WkäÌé¬ÂƒâÜsõkœ»JWÇþ[±8MÇù @Õª ïÒÛÝOÅ ‡™çë¼ꧪÕe?a«]®9'Úþ[sêpCÎð{Ä pÙ“Äiù§l¨ßçÏÛ_°}ú//¾}ÿïþâQn|ô×ìÿÜ›_é~þçwÃþ}~ïwM篾åýß´g½€Ö+`xcÄÁÇ÷Œó7ˆjg.5ùx£4R¥å} ±è럺tÔÕgŠ]l]*ûÙðÇ[è-ò÷¥ŽÚoË ƒ_ÑahÛ©æuøpCp?‰í47fÿ(ß•‹Ú ½ ò©É¿ϽN<ÌÀàËQ i5òú‡|-u¾Õ§½±D‹¤ñ„Â?Fn}™‡~³IwâLÒ« öÿWÁÎsêÉÓO&ãõ‹Gˆ]ì÷˜‘bûö &¦Çuù-ÏHãB}f%°› ×í íøàpÜÜt>Ьª/ÌÈÑ´7ŽÍv=Ê~ÒÖƒ>kbˆ»xlà|<ú¨VÒ\TÞâw¬RLr|š†ÅZΣÒóLÊ1Š|£sÙ4!±éu 7‚;AÎ^Á?ÁÕ•tñkôü¯·ù~†©!êïgô°þüœâhaGÇ™E´¤ ¬}•=›·åüµ[®@ô¡ScRüÈû’Ûa+Ñ5c0 M£7?Wþ®Â×^×ó)"¾Z÷Yf7ŠƒsjIÏ Ø2ùǵ1G“r¦˜»ÈÖׯ-cVRæ&~?ѤÔ>å¼búý©»M×÷ˆþYøAÂA¨{ÕUjز¶…zÑúÜ­‚#OLEˆ¿éÏ0¹3:S,nȺôÈÒîÞ•ï–Î!9Y~VäOJø»Ssåßò·¿»íæ&%o]s<ˆ3›îèù´™Í¯„Ž|}>\-·}f[ý&|ØÝsû~fÞó‚— n–ÿvg¸!´ê7ùŢɑN{Á}DãF1õÄ=!¦£?jh}íê÷]«G¹þ–­ïs÷×›ïÿ¶ÙÖÑߌØâûŽÞï¦Íw.ß,±c±ˆÙëOü³½®Þj°^p¢…ú©¯ê~úÞ€«±àKú̉¸yѸ/£ÎyÑwpYî’ï!UzsÀ ±Ñ†ªV[…Û,‰%V1 ÀÏ=ùù°Ù/ìÊJúVà/¶^†®KÛÁÞ'ú¤ÖÐEMn±ò飬uØ[ÔЄ«•A}¬¬§ðRrÞ«€YÙùjXKÜðâÆUp4æÄ#ØÅÕ…Zß íêößæ#ÀàãI[ BÎæ.:ÄÉè_¨žÀßç ó¨ËPˆœ×Þ§qÎBïJ1F>·xUìYrÎhrÐ+ßÇ™ GMï·«˜Ï1ÀˆŸÏUpLñ%TW:‹ÆJ%OT¿Uœ;ãrÀ‹uî°;“bÜÄ”KϤɟ8 =ª+q›;ÞkÕž/…… 7$;…³ÛfzLÀGÕ÷Pd£“G2¸¦ëq÷ÃV+!o½§Sm¥¹ì¹Þ#ù*êïX§®¼qŽ úY\”:B`ÙÇ–mþŽû4v¤½xlɬL{OÓ=[µùP»ÁSu \f,±]Šßôù~7&ü’êÞS‰æ9q¼rú%sg¿ŒŠus^Žö¨‚OH»ÕòÜ!1‹¬íæ04žqìÓ ƒuÏö<þ¶‡~ĸ3Þb~X˜”ülà¨ëÒŸ LóøR=5𶤱—³%3ÿ;!=yÙÜ¿ï¾ÙËà%*ö³/–»cïG<¶îZw$¯`%­@°ÅTød¸ù\` ÚK ˆçxÎEèh”†Ûž¦¾3’K¢¡{$¬ö0ïù¬ì‡âÕè .LmZ{0isý"Þ|¾o‹…ºØ.5Ïȳ홈c‰7ôss¸à³ÄAö½²å¼{!w£[iV¤ò]ôqvõ¤O£“#é|{ž×xõÑ£&¨5ÍØ£ÃaÓ§K·~”˜G„ýµý7›O ß{¥œaù/Ö@ÚtçцO^Èŧ+¹äB:CòIÔ®ü̱ØyñBCŠýY©ÞOü}=SEÏQÄQÉûÒÙ0zò”cåœ(øcè>L]\4S®Rf—}ßà®è‚Â=J,R9tÖÜÀ ä#è›Á°<õæn6x­êþ‰+~L;|ôlsùN¿›ðbý£.¶}KOzkš.L Ûë$]èÝ3Á=i-¤"Œ ÎäÜO~$y—4?ß:¤FÞ• ]Õñ¥ÀGdçÇi¦°ý>¼!z¨‰qûš ¸i…‹ ’ÜÜ„Y·öÔdÉ-µŽU«üfp`Wâ|Ò ×øIÙÏékev©ç:ë]faW2ŸÈº,<Õ=+û´ê8ƒ4”VÑßËÙ”~ô9Ù0ÇÚ*1p>bl.<«þópoá>Á}YuÂ&X¨½Òû!ï„cV>¨þF~¡8Š9(»fDíÏíxøZ?ïC[ cÏðï5»b¸¿‘÷E?»ÿï¹Û…wßë?}ë«7/¾ñ37Ÿôê×nY/€=ó—¾õÉ{æ÷Ÿõ¾?œ[LfŸqön7lêû}ÍU3uû@^^+}¤Ì’èâÍžs‘zDz‹£ø©ó ÞsFsºÉ?„µÎùâq]jË™ƒ÷ƒmSMi›×êEÌé>¬0Sömâ9„ý÷sõüÌÛZÓHNøüEv¬0ã°ÏÁ¾‡è1/äØOò,8iæ—ÄQs3¦oIOîJ½Eð•çÂà—hIàWêϰóFï£r?×âÍ»Ý;PáB}à ñjÀk¤ß3ëâð)~ö"Ÿá+qüBzžòU%bšëçp†ñ+Ã\$D³®RÔ³å1sb`â7æÄ×Xƒ³wü®£aU‡%yÏŽ[5áz{¨<™Ú6ܶì}¦Î(Ÿ‘y‘l@]WŸ/x¸}~póÖ&l§üø;öZ…ç°sp7âêÓŠ1ç ?ú“îöïø×÷ýêÈ%®ð=ãÐfp1áËuéRØz*/»œ†œy’¿íôháRŽð§Úºî íN¬—:"ä–ð-¨³Ë¿¹Ÿì|%¼—"ݰ¯Ÿr9òNÕrO'ev&Z5ÌìXe½&þo¶Ž¾^qs™ Uˆ÷ÖÕ‡CÜ¥Ú£Ÿ­«ëckh[¤þÓ Mª•zׄ£Po˜©FÒkÑ&NqÂEjåS“Õ\’›Nž"^ƒÒ™â>øúRO”~ÇH?{&néD°ìà¶ù°*æšðgÜÛM·œ¸ …¾Î;T£Q½4g[tb¯%1 Ø£ÖŽ\ŸúŒŸ8’²Uprfä«Ô"»X Åm&õ”Kš™+·Wg®m{k±ò·Ì ÉC†èNýnùNi˜…Ÿ'~’O)à ©§îöÃzÈ#o&®!É\c^ ºA‰ËDï˜tl’-D ¸“v˜s!îW'“cÞ¬0÷S[[?q»g½îˆj%n—¤ã2Š¿‡æ|}øŠcÚÂg‚ÕÈ&޲M“0•qÈZväå›p‡º:ß.Xeè ¬e¿j’cœ§†9S“ïâ÷ŸØ²3Òȡ֤Ù-©ÃšËFi}"'¤'ç@½œ%{&Õ@s†ü´>÷¬l¥ÏF÷ý¡Œ\EöªÊÆïpö;$nÑæš”|"ì½aÅ¥éºþ pSùqøÏĉ‘i/*6vˆÞ·+Òð,Ô^ˆáÐ%=ú,Ù¿ï;D-Gó»ðŸ£r´7ÃìŠâ—\›VëDg-¹¹…ù¿‰zuà^¡ÍJ?”p£¬…°ÔõáÐ â"“Ï,\àÙsÖG1wòYÉ­Èȇ‡ìg?†&cæ˜mÆÌù|þuqp¯Tõñz"=§Q~å*ÙÍ¥‹Ú4xõiô´ÁûÓ'þÓj2×p?ðßeÑ4zÆ•ê6àj:Ëõ„æ’*ÿ©¥ÈŽVì ù—ž'ã#°2úX‰Åñº{`ïÞn„YÄŒ®Ê3ª6ávKõÓĤ”fœÃ\ø7ƒ´>é×àþRÓ‰ºòÅìë”v9Ø¿¯IÇQHž ¹ôrTÿ'nsû¡{î¶ÏluÏU <.f“ïÞì—x_~/”³øçi*¶ißþý©xéþù_Çþ¯>ìË6_ü音iº‡oߺ´yô36߯Ø\ ëXx¯QôQÒ¿G‡ý÷Bý0¶¿–;Úýn<'ôo‚çbv½é»ÅLÝø-àdàäâi],:?É^©>Ž?E#1lΚzðÏ&×R}Éí´Ÿ¥nF\ VÏ×lïBûyñƒá6ùÿun¼þ{g]ušºùÇ Gqßþ¤ÞçÛ³¾tòåE³Uò׌c°„Û§³KÎægF«¬÷„¾ï i'vs*þ™¡ôÂñ犃ý>ÙßK¿§ËÿÖàÒΚÆÎÚ¦`¸#Ø öšÊóîØŸÑ ÿ*ú=Ætû&¹øÍ®{v}r×”Ãc׎O«~ª˜‚†Qц³w„c©³âg×~V=~/Ä¥+è(õy¤â"öÙm¼ Å›`ˆe• ©=25>ÊEa›g©7{nD\|Fº×Ì~£Vÿ'7o³sÞü8·YölÒ†+ Í’€¿^dëD=Û'>²ŸSî5Záþüʵ*~]ë/|c§Šë[áˆRKÑü¯u¹îîJÚyéÌøûÂÝér·Ì™5§F®wÈ}s효ù\Cž„úç‹‹ü[¿J_1º7Äõài`÷}lkÿ“^ñ¨º:9àå„fKÛ÷ÙÏJ{mÄ"ßé6ÙxâN`/yŠØ|§’ǨžçÏzÈʵþ#œT»JJñ¸{Ë v©ïW>@Ï{ðnèQþó²õ­k¥ÏßËs·=>¡Ž³´F™Ï“ïN…^ÝØr¨ËÂKRíf ^îE}OpN•Ç¡Ï8 ê³Â.[< þFÍm\ú´…-ŒÊ]›1/klù«a¿æà§€7*žPoÆ—Âɰm,[ß?õólmßþ§ë¾þ/~úÂÑÏÏßúcOÜú_±õw¯úÖ½7yÒ¾=óî÷‹{_úÖ'›V`}ô×þÁòÇ~Ùg¦åüî¯,_ùÈÃoããàÄK#ÊcCq‡v[¬}…žôŒ÷µ~»Ä½ q‰WWë_L¶BÎ?Ó‰ûñIÄÍöóäJįÔ¥/7‚  ˃™ù‰8Áœ]°Y¿Kêñô}Á.®wú‘àÞŠ¥ X1(Ÿ\øõ܈+ðÌဠk;Ÿ=_Cãéú¾[.,¼Ô}¸zJ”Çü©Åsê|Š1ª°?·`é7â‹É ÁN†Ð“¿ÝPz;9µX-xêYÊšmÃfÖRCNñÕ\±}úì8™|"ÜÜùvÔr!0T|訙mÌûÚ­0÷IkUè ‡H¯“êëþþšoJÎëöAÜÝB]€¸ îí*{¡#>_±v1¶ø‡kô·VqæÔWø¿|Þ.üÖ!yI§ˆW³ÏV‹­²½­*ŸÑRb6©÷(ÖŽBÜ6†Vñ匩ßVú„%dFÜ{þ%õ¸˜Ó’’p©ãèØTÖ›ü‰š>9“ÝÕàüŒ£±©ØÉ}›ú®½öañ¢òh¿ødåäEùjÆ‘¼˜övF¯RW£u,Pq팘«Íbu¼ÝŸ:‘ÝKÍ'ÉÚ µ41¨Y‚óñgðñÔkÀBÑž‚ŸŽÍál*ÿè¸kyOÑ;ÑÞÁK=7‹‘Tc… ZÊö Ù“¸wg4Gt¥zL;œí¤´ÁĪ=xGýbw8ã=—€s«g)ð‘‰-ØgbÝÁq©—S«H>-k¡»¡¸ßcœés…¹¬¬%õûÎŽ[ùQ³a]^öŽýœChr%Œç±óF(;º«û?⓸7h+Ÿ‘æ€øèåï÷u[ö3¯ÏÝúŸßõ3[ö¹¦h½þ÷úà]ó—½à¾çOzõ7¿àCÙTnaû’Ünå"£0F¯ÃI;Ûìø ø3y¢+ú¬É?„€ë§OƒÜôz™ï8ŵ±cOrf¯ò×+¥áèhí\ÕoÎþÊÿWò]ð•!je¡~1Ùœ´Sƒêö¬…=¶U3€?´¡Ú°[„•0'w-ó4›éŒz ‰íb /¦V¢ö@5´«4}Ó?§a{Ô“!Œð¬þü:œÛd0cÙÿN{n³ãŠËÈÁ'ü€ýža8¦Ÿp¿+æ ça“6 ½F­Fgx½ë[j5Ês‰ÉOroìÿÊ™Üîb‰Ÿíß™I3ˆ§õ¸ãÔçO¡ \Ò±ðN×|"öCã…8[v&}Ú$àÆpTm}º^gåq÷ì;gÄÏF p™¼|7|ö)z{ œ½«ß¨cÝX¥O†/Êyuº½[0élË&ž×½ÃHlÇ{ò{Ýï—ù˘ƒ:ŸÜ£…4\‚o½ƒªC+‰ûmq ½y¡UqGÆ®ÒÐN.?öÕ.36„×H}¯éÓä¼ñB\,›™˜Ä:ö¡ § x!m¶Àf/‚S’ǹ}RŒçç{kq>¶’sekIý#æÈ†÷‡ozŽéÿwÜëW¶ž{ìôö£~ø¹ÎOßqáÚ—ÝwûŽ/íg7çoÚÞÿžg¾ßûÁ,/1[g¹äãôJï+½Ïïý®óÛô¼E±/ ß;èTGÉYHðOmÿÄ„ A}Ìf·5ûEOŽlEbŠZwEìg[­,þ¾aÒ;à§5r¼‹Â+/ênŸE›Ê5Ð’*²þsð^VªÑ àó‡œ›Ê ÉCb_ú&bbubfá!‰ 9s=Î.ë%¡s|H®4r‡ÍiT“m< ˜·1UhIÀšÃ3]eO÷yðý‚¾:—Ô¯ÀÉtR»Av†¸){³õgSè÷{y^ìýŠk“+ª8iNŽE-Œzaä¡Çæ]<¤ÚïeîóÄÞÝsUÃÒûrÏÀ†µ§…­œW®·QÑ›¦Q±@…ÏO\ßÅ$þ.v§õ|3rzôVÎ+>>'e‚ÿ/ž16nMÖŠ»<ÉyfÔŸ•7U¥äG § Gž&Ïñ;Bþ{3=Áø“û‰V¿Ùà*¤ÎõÔâøð¿âª‚gåÌzj„ð”?Kú=Ä?ÖJMw% 1zrÀÓø÷A½‘kgÞÙ×¼«¸~¼—p”Èm¨ˆ s¼þ4é&¢{ÞÛi°²Ô ë» ÷eþù8yÔÐÍ_P¥’¯àkÉ-©ÿÈN¦šÅŸ‹Ðâï;Þç!u8—ð…'°H;KÔÅ×›6Em}¥Ñ+mÏÁñAýlÄ©=¦¡9›p ç;š‰ÎõL<Ðþ)¬›óTÉ홥‡í¹¤îxæ Ø#;Ç­&ëºRͯãšòîâ554°7bb}69¹å+Õw©3ÛûOôxê¶=?§þŸÔpÿgwDz‘'HS´sÙ¸ëÿõ¾ýsãÊýöÿò®oØÿñ½OÛ:þæ³õðå¯ìýÈ·½kë^ù{ö^6ÿç_üÉ{†óôr”·|ŸÕ6­Ÿ0ø?'—ÔgT\bKäg˜ ì1°~¿¨†S™ß<ˆ·ÙófÌFEÍÇëµÊ}vúUÜŽªØn¢¶ÏÞ6^[p)á?+F­`ð}ÐÓ{1¨žI<£s0Ñ£,_K_*¹ØoÌŸ¦Ùçþþê‚ó3?l½€Ñ[jßüĵÄs¨±ßæt¨™=ç“c gÅâAt¿Tw¯ Ͱ\©?µÍwÛ.}L81#ú´ª5ùìÿRQÀ@t?üâ~&Æ‚ÝʵÿÈŠÏQmu®8 ,ÝßS³¡±ßŽ;ŸHÁ‹]øKp]r?åf¿ØŸaçê}Vn™~Ÿ Þ‡£q®vûçHþß×]¶Yz%gÁwÓ_Ãå[WOƒð¬¥į̀³îçRõÃ9~.ôJÜñøæðê^¶Úõžæƒ•ï¦óŽiPÒþÖ•¸ ÄD‹6?ÌŒu%§t»©yˆe!n¨øaYCSý<çP,²—*ê/pš6ïFž“A½×jþù2ý}Z¯¬ù#3@Ï\aåpÑ ]KΡ¬ÄÂÔÀ¾G”Íž?¾\Ô«K¿mßS2Esl.ždíðG—5ŒúŽðìsÉíK”M=bá=Ï|çîØ’8Yv¤ô¶@z ¾>~½â“¬SËGúvþì/ßiÑÍbµg‹>fÇDѤ”ON.GQÌé{À<üœ0ÝÁ¹¤|Ó¤ür"·ªs¥zsp™‰ƒ´†]´AÜ_ˆybM³æ“þ¿Kí8â§Èí©9Ùû“¨¯[5?²-óUÎ AmGXZô}¯²n{©39çŽþp}ú¶}ÞË^öŽ­Ûõ”íG>ïÿ EgÚý{w?¿-ûןøþÍOýöOÝê°gûçòGf?³$VSm’œr$ç‘ „“üjLƒ8?ܳA:8‹Ô >Ï<ð_ù"Ížµ5 i¸Vá'ÐE°u3Þ¥|«ê-]µ»tŒ^µªtâ´±åXÁáfÄ CÕ ¢î+^lçšæ¾]Æx¾Ã¶×¨‹d¯2½Ú‹Ö/_d§ÅqÌk9ÒEpzîZA;˜3>H»…3l?C "âôãKê¯ØØîýÅéŒùka¯.ðHl—öÊ×Ù|ÏmÖí®Ö› +¿ƒp?¨+0gÀοù1ùa Q'ƒã.çú\U„õOû.üQ¼ùClÁóœ+0iû{ÍH^ ±kÃÜñÜÀçŠøQ%Þá |«‰È Í’ézù„!¶v{?õšì ñu_@M¶ !=ÌçÔØZ¤ø£Aýúä¬ÂkÄs¡dvõLÓ»˜ ¢éŽVí.>Oö=çE+S/þ‘#bgÉA¹3Cð…Ëfn<㌠9ã±ià®Äßn9å÷35ÓȹñÍÔ¹è%¦‚…FØiâ¼Ä†ØOú¿ÉÿˆçàYQ·QÍ {8#¶&GÎCû̹Ây‚¿Æ –ûÚh¡5,Œ sò{…q?±Á:·»œcÝ«e`džK8N+ž•?:°Z?ð?Î|ísr¤ÈG›¿HN×È%î—Í·û8í•¡iãQ£ñ5ùÓ’éÿ[¿ÿ¢ÿvý~pvR{Äð}Õ¿–õ7ÿ§Ÿ?jêÂ̘o‹+à:¿µå§Kµ?L.î‚fþŒÌäQŽ›sÙV>£îÞÇÿ^:ÇÄD“xø%â„ЭŽÜõtâ•Âg‹f*9Žu XÖØþ\¾s¤\Œ>|oÔœˆ£‡«yðÌd›Zl°FOrAú#1†öÕþ{¹’~ðÿ>'ê0*FêíŠý ±Qðl.«¾á3à«ìiEï¨×9³;Ê~Øßu3L=v»éöWùsR›߸Ê·ž–ô Û•ÿ>·¦ŸÙÈ»¸®¹\º·é÷ä ëB½éƒpâ¶öý PÇ ~Fµ¿ p,V·2—áü§±áàkÔ6&óœ7ÅÙk¶$´ºÒJ÷‹¹æUõåa¡'aç€ú€Î"ýæEq&ý5ÙË.ß3÷—ž'ÖuHݓӕž—¡q3¤OMjå*YcM-OagEõFò&î\æ¨Üa‚à˜YŸÆGÊoWúå²?]¹x•þG‘­«œ­˜Ûp6ý7X}X²ëZ“ˆwí}¥é›97çR˜üÎoR—Ãñ‡ä„êÌÁ5Åù­ß¡ÛëÙÖ¸k#ó~íÏ„UVjïâvMÔOå§èAIÎ ±?ù4ù$¸?q›â£<¿zÞIÏ›ø8YݧBî:Q£¾3>è5dθç·à1ôÑ[Û^b¯|]V©µ¸Q¨³éìÕ[àü*Ö÷'ÿYIg†ï·g±ïÓœN÷µÒöÇ®øZ¢Q74 ÷7Ú·¢:@‘÷{@Ñþ^gÂíóÈimƒs m6noáõûü3øÏÊóFr ø17œ5]rïð]×-ã}ãÞÂkÚ%|ϱå{ñÛÆùßýذõöWžÞ>²7Û¿û_ü—½÷ÙÛŸ8öØ­Ÿ?öU[ö}ân~Ú.7Ÿðm_¹¿wï»m=ãÃ?<úÀiÕ-Í<öQ·ÏKI¾¹.VòX«%À¥bv´f¬*¾_C#´ö¶_ºÒÌ~g%­¥Ž÷ÉÜבšUÇᢕø>½¢Cã"úgžÐì jFįʧÐÜ÷ÿ ×ÏgFXµŸþŒÐ+—Ý]˜¤)¯£hv@ ?¾3öèp†ôîË8§©Iç{lg_wv©Žø™^kéöÞ„ÆËØåèÉ–vËD/îQôx($7g} Üm4 à)(ë~JW»Sß±¯->iŒÂ5Î.|ÜôùŠsÄáÎ’8¡¿#£ð¯ÄSTWõw /$WQœ<­ÄÅæƒOÂC‰‡¦Ã¾1âSɹ8ÓÜã!ô‡ªbÚ2ˆÇ&­zyů+_Çv‘7¹O¿›þ'[Hì’3ŽÛÎn<jñz?p}Ǘ軓]Ù%vY‰KÖõg¢[8’ƒÃqÆW» 7êæÜ®/;¾!ˆß:Ȭ1ç.ŸpWÏ‹íÏ-–s:ó¹ýL2åE1³Ç?]¾/ž=棶þÂÀºB5òaáÇ•œ8z#/Wj=ØøœÒ‘«aÿ#ÿÓ,·Àä×'j5`2ƒsiÃFÂkX|WÎBvÿI RÜß9upzáñ‹ /ÎÞ‹àpx ‘:h×¶88”çt÷¢^¹ñ4ˆï«¸Ëm³ô &ô”'VùÐ ü¼×‡_ÅÒ“­ºhíêâ“úé–Ô%ås'îµAûni«×î¼>_ùð2lù)ê~wáæÚ—Ðêy¶`PKˆ§Ý0lpÙöÿpj³íâ ›N+¸/vUxƒç8ðàp]’&©ÖÞÿm­›5gJ÷Ìs([ßCã ‚—þþ›­§oÜyÎMû§ÿi¹ÿ‰c¿¼÷ýßûÌ­ß~íö?òð[7/ïÝ}ÏžÕæý~Ï3¿aϾÏb#¾ggȰujæÊͳd˜(=<«˜Fýi$Ç"9púÔÔÛ]4 MÅ>׿î‚CÂëâc0>pPjŠpÒË'Úñåà…:¼âƒ"Œ˜~±u œëº¬#ÇÏߘx >ÿºkæ]sq[cïH?€ùS´QÿE\Q K®¡¼-xhòÇY—ä~µ5ËYŽ©IÌûkoÌFÁçÉú˜ê_ɽ$~@×N¶[Ï< k󴛉ž÷B=©µÃj³„^VøyÄ~ƒô ôïâ‘þõ“œÅ° >v YœÂØè7uÖ 5çƒÖ›éq¸¬%ô:BïF}‰ÿi½ ÷>5qÙFæN+ÕÜñQûMýá™â¸ÌµÍ.¶¾ó5ìÓÄÙÐý¦¹ä\œ‘nlªÛ»o·_SÈ;ÚýÚé½ ¯+î£û½F­:¹]ôQ×úgÜ1q\üóÅëðsÐÕNx'4Ó*ûݧ̞zòùoûÉùöß?æÂ“þá¿m?ò;qû?ãqoÿл¿|ûÝãë¶?ü¾OÙöZåo^ÚûÂïú’­_?<³õeó{\X{Å?oþÀã¿|kïÞ/Ý‹ë sÃ#_ö‚O,bƒMË9­^`ع¡éY-íèû–æ“¢G覥z[2®EGymb`Ä@Äv',æ·3cûLÍHXÍ ÌráÜB·q|ÕiÈÉ÷ûš¡ %“<{W÷$c pÉ«9\ç ýWÔ#ñ[äo`Uø2z¦ÈÉ®•æ<ñ,9þµõ•_5oœp„»ˆÍe¦9œ åÿ^¤þžþÿÝAÚ˜ƒj‹ôJ_ê´‰¥˜.Õäsž…éï ®Åq¹jFpæO¶`näoäZ]½þ—zU3_)§ñ{¤>±%ù½ü{W‰ÃÖÕÛ¶¶RÏ*xžÄ=Ô¿„-{ì…V üRíqFâ|ᣂÛO¯Q`¶—‰/«ø{ÉgÔ³¯˜ÔÏ+1½ÅYäŽà¿ô,®R/°nbh> ßÏHy=šíã4¤Á¨#¶Eo<°&é”ÌäO™YãO{O¬7¨ ¬‰H¾$µ»ÑõìôÔ<Æ8нæÂþJãò]“ý*š¯MŸˆ¯?ùò ‚A}"àAª!9ÂìXÎzøèw´{‰–:š1{úP¹ó©Ôõ½Ç9ÇÍÚ¤}ІphÜ:>:×=°ïõxÅîFàÙ‡"zriv:rúÒõ»2{Œøv¤Ïaè´xà?(w·ÔsÛœÁ ¾34Î%¾Uws"nTó#†Å —í¯C›M–ù³âÎ ß||hÔyÿ֦¦çàkUèßc¦žâ¢BÞN]|-ÞÆ.u#pcÝß1´ZÝÖ÷6P¸spmá²Ê/Á‚¥õžó´w‰…÷À©¡7Àã+z #Ï Nòzê@'P\ê¬çÙûÉîfÎÓÝ•]zÑg1êÆöùï¯OÚ·÷ü¤ŸúêýÇ=ë ûGkº´™€/¹õ›ö_÷SïØ@ÇÇÞ™…ÿoz­ø%öx¥9-Zß‘|Zk®¾)Çi5çn:ÐÌòUöÔ…îšÙqå'ÙŸ¡qƱƒêÀøÆEhQpfÉq…óó¸MézI&a˜³SN¬z¥ëâž+v±ÜÅ1™Š“\iÅúp&…©6m0»¿Ô 9Ó¶¾àÐqç/*¶?›¹Ÿ´}JàÔ^;É9Ìâ2¡ã<Ó| z7gøÕUhÃ0[wRÁ\œl?‹¶Ÿ—4¿w!.žæ£ #—õ ­]Ñ=Û•óÚ ˜¸ìõÖ98i÷n3ÎvRõó:ܰ–õ(ôK¸ÛÄX ñWÈ¡[mc-g\õõ4|Ìzêp¶‚3©Ú@>œÜ!¸/óêÙQG󎼷z.[šö‚; ÞL½ch|ô V¦Ü({7yf­ ø>…ºv\eT£›Ðþ]¨gOþŽI¯é@–³áŸ…n–î‰øÄ×-™QAM5ª"ûW•ç/“ÕVo9L.ñR³ÕÁï¶|ýÈ×_°÷ÿÊëÿlóèüíׇ»p¿óOØžÞv|ÿÍ_°»ÿŠ7½e/jK×Ï­7`û?±wôO› ¼üõ°=ÅÁé§íÜ™MT4‡g¡ºÆ¤z¯c¦è;(¦,ììÚ@n”+N`£#NýAyûUfÜ*7Qîq£liì™jtu•š°×°¯®‡Ê|ŽNoü,ý¼üØ6ñÇÈ>pÞ¨_!s/ˆ‡„Ý])èÑÿ®ÔÛgÿ¢ón.IKmÑõDPb\WþlêôüÕ3˜)ëƒfÄJ\[õßTz—¡1ŸÜ õ$úúÚŸÙç“{*Ö›G万r˵Ôf¦‡Aûîþ]50Åß¡¸PßÂ"fÅ.ƒï~#üsê#½Ô[Ù­}s'¼§ÏvÉdæºYÇ»×z¨Æà=‡nO”#3ïÜãWxëC«±ûŒô²‰+1¢OAuú ;9kpåZ/*¶~-µ®©D¼|í¬B=“8o]ÚÆŠÉ‹òDöœ¾ ÚE÷ŒØNØLj2)Î.-^YC‡ÎïÚ½&øî5ù'çÝð§à…çOàèh}P‡@¯o% ¸näðƒjÖ¬½ý79 ö,B{0]|ãÎÏäÌ0æ®7ýRÓB› ˜y\´ù^[ASž¸p]ú¸¶fçÿèåsü«{ÏÅþ>fD‡‡x£½[ÔÀ"å{+1:9v)xî¿é‰©mv÷Yb4½G`÷è@Â!mXiÌþ5ûiøì"z· ¾¼ËΟñ‹ñ¹O]»‘øKv ô‹úÿkø“À¤dÇwqç+9\Õ›C[î‘0µˆÉÑ€«g6ÇfÁ?§óÚ;Ù~ÓC2hÓú[rN7g`ÂÓ›¡+ä6Š¢#¸Vó;Âô£®/%nšÎzrB{¸½ço?ý '>ù-ë¿°ý¾‡|éÉ{Í¿ý€/þî­Üøó›øŸ¹ýüO¾û¶½óáoÕfƒÅÚ|Î¥åQ õðo€¿®]ÄÈ>ZÖ«‹%'Í™S«—ÞÛRu£Šž>}á ÍØö?éžÍ…©”.†Û¥g ¿‹ìÎ;ú'ž*F!v•;ÅxÂÖÐí.ì㋺ ‘g†>¤ò·¢øG¾:ê«Ôl¥¿ >~¨\Ëg(ÌÁ:ÖÕ[©Xkyd£–ðßí. _)äb¦M[¹ ¿«–¿Fý<çPg 8ðpçW¦Ö>>n½v»Û½À?ãèe‡`ÞüOr÷ªj“ÔpFê†Ø0ú»È—e·'xô§àëVâoØ:¬Ä›³û^ †*›ê¸¸rÎ^åÎG?¤û”I9[Q®â>Òrqê+ k§ÿÙmñ2zóO/•óŒÒ%đԳÉ}ýµŒSGáO»ø¥uÍUëQN“:hNˆ‘’ÎÏãïFÓòRŒëÞÕ‰ón£ï'?7~s95Ø¿!ù!;™W Oaö&º0ÔnÀüùOÄÃ3_¤,<€èŸ޳”›j/QkF7¼M9×DŽ¢:c]—><¹5ˆàÝžC¯_Q|èµKå`n¯ì ÷¼0ç;V 2â‰%9³‘«5½&r|;ÿÔq¨êûÿÅ¿J®ã HÕ ˜Õ0’ףɾá‡Ä£þÜÄŒÌ'g"ö_O=n8…;É?€g°Ò Gx³[s¢TF+f"ÿêpÚÉrÕMS·3ªoÀ¥Ú…H_¬}fàƒ“SﵘOùÍñJ •¼¾§lÑ%Í7nG¼<êï+|OÕ²UñÌùÄi—®êsS'8ž<¿=×]Ÿÿwû~ïö×û/|þÆ…§Ýõñ½£=Ù³ºþþšýûÙoÙ³ßyÉ•WìýñCîÜ‹~¯Ó“æT,£W4j[Æ#èl­tw¨Ñ‡ô<ôµÔz±}Ôu/àfL·_’Û3Î> ñ)ü»”Çù]m½_ÅÈwQkðõ¦WR˜Å²ã´”AõÙA¸;5O ý{pјÃ,î¾÷b,²_-f Pƒ³ý³˜€XD5Ø*¼Ûñ˜SpQØÙ•ÔŤ¥hñÝs¼Š¾'õó‚wyÓì~\Fz8ü{ìû•¯,å‡ = õð÷¶vÄ7ÂÇwôcφ3^WÒØY…^¼¯¿ýþ™«g;0ÄÿLÜ„Ôã:£yáä÷p€ï 1NŸALŸ Üø¬uR0wÅ“⹌áNQÿ FEß%Xè N†=+Ï vï%ê1W4w2ùVاõX¸е y#5zÅ´9×WuzÙ"ï_ëgf­žœëA:ž-ô\ÎÏ‹êŒ~^âì·Þþû¹r^ñÉ%9¨0,ðP4¨“sBÞ¬øˆ^•‰œ¿Íü ÜŽ¼nãªëÙ°½a†¶tÈA3×S_(˜ûf‚a#ár Wõ•ž«åó—áƒÿÄyîë2Ć›´úÛeø.E±Ú>n7ÍÞ©–¸KŒH $¸÷×Í¥!‘±µè3Ös¤ºµQíh#ùÆà€`+ò“såG¬¯öéTÚ«¾ZwÁÍAW¼k¥º§æpŽŠ 9ZìÍuKüAWãÿgîñH¸¨åç ¸¤òTê`cÓ XÃW“ oßÃOQï 7¦Ç—_”3ÝÛ„½ƒÃ§3o¥Õb7äΡå1ƒ“ͦÇLý¦×Ïÿø!OÞ·ïúº‡¿yï >ô˜ýoú«‡íÿÊ…iëÂï¼Çgý¼øÓ?Çrz÷¯?q¿½ão~ÝòìÝnØ<|Ô÷îÑß wÄöM:óø_åÉkš‘uf½›Û­àˆú {Ç“Ð&'ÿ·Ï[³ËÀ2…Â5f F´äŒ(ø¯ªG&lPíTµoùíµÒÙóìÙÌWËžàÃÓ^«v…–tÝás_Éú-Úkf×ZÍ{ îý#E1ñ(\“ó€vL 6ZÑû]Äìw0Cé ´ùdØ ­™Û0SÕÉ<.3¿(nE¥ç_:êŠcŽ-Cã2ÚŠõE9Ó,bíÖ{’Ú°p9ð‰Uyƒ8:R%Ö„5ꟓî`ÖPÀ²ééØÏbÃö^®¬SÓˆ5çnªHµœCÍåõá©Ý1ìus0ýŸxt¦>ô¹ððÚž{ÚqæsòñEgbFµö¯®: [KzT[IÞ̉ÐÍXß _~%>êüøæ×ÅEħê*òï~™J¼-ûç+kÿz®Qç£è¯è&ª"ü.fÛ6w|Ùrƒsò¡K!Ÿµ$·€Co3t3®Òöñz}¦z4rMÈÝÔ#Ä>ø£Õ–ÚótÎÅ/[K£ò·]l.ؼüचx‘övgÄ7*@SË÷Žîk—·€yº-;Ð ÛKM—³ó·ç|ý°5è\-Ä7[‰÷=hnbÓõ@C1â+öI\ÆúÈ—(w?ŸØ×Èžßì‹úš©ΨOÏ›‹÷ëw‹Ÿ‘ÝœÀÓ¨·kmÑ®ªUTq%*wl¥>JxØEýþ.‰-ÅfÐ7¤ÞúÈKàËÑCo*ò·ãsrppI|×*æUxŽªþ™Œ}¸Ó]Üž˜ z!‹àj°fU9)ëívÈî—xFÊÝŽIGÈã=î9w…S;¨o„ø‰|”½_‰gOÞ-¼æ*lÞAµð´¼ËøúZ­çsä•¿ÎÁ€ä‹à`:ÆowšÜnx¥æ@Ïõ碙v¡3]“c»—N\!_›9wô³œžÎ=pµmûðíßyíö_¿ú—¶¿ûG_½ý˜+OØ~ÓËpÿ)Ï}ËöÚ+^²¥Üsós^ôâ-Ó zØÖÇ6û¨ïýõ+žµuÓí÷6|À{¥o{èG–æË£fáµ ïæÐÀµØÁìôëO|?ü–9~y•z};©!r"æ|Ý{Œ±>‡%f\7ïòǪ< “‚Öñ™¦­ìõQëR>•>§Ù*çáÏÕgEN “']ŸEΟªÄAò·3ÙF9ø0à© ñôâþÇœ&zˆ‰G»xßý€b0ÇÑÈ…¸Ë䓪¹Ìû»=¦ÎÆY§BÇ’nå½–Q/ÃV;á¼ {ÀñÖ¨}fͲéyC_ |uI|‡¿‚û ?h•üçT•·PÇ™-T¯ç^óÁÑÖ÷R{¨¬yÂJ³JìÕ#“}lØö|?¢: =P…ÜŠœsåœësæ·ìbr„‡è›š£M#|ͽÂA»ãfé¬Ûÿ…éUôTe¦ÀÑΩnp|üÔ°SàÆ²Í‰sGM×ã,íaôýaƒð; ï>—5/´E1[ÁÏEÄ1hŸ×Íb7ó7ÑSu6cêU§j¼ª!zCKèÒœÍu…§ªúöÞÏ1u\áæäsÉáŽó…f3=éëÞSzwö{]®‰ŸzòmNPrxÐc•ž\ÔU©íƒ +ëóŠbÕ V‡Ñ~ŒQÏ÷žl}«P<âØ€tr |\Õ_yòöIvžaìžjÔž_7K…Œ#ZŒsñ8‡â”ˆÑ£§J¹<è‰k8žÄŸàèÚgr‡)jÕ~ýûíy¸ ôò-ÄûÑóÍUËeôÜ;è!éù6üLq–uöùYu;‰o4;Ö­ñŸ ásrB»Ôìì¼À x#¸BÔ ´iè7†bÏDïä*µ*î5–µ%xšÖ`«Ä‰Š‡&é—òßÙ×!,£¨¯ œÔÞ÷{7^´¯|hÿýß¶»ÿª?|äþ?MÙ3Î×Ñgíü50ÿ>3à·o=kϸ‚–Ú3­ág]±Úœ»FZ9“0áóÄ?hHÛSWœø{éÁ€§T0Pâ39ï|âú–‡JO¤°O:k¼zˆ~S4ÕëJZHƒ¸V éKÓÚùÅœ"Œ­èŽÛÝcåÏqöЄXh&Ÿ%Íu(=›5·“‡¨|5µ5´–]î¾*rÿÀâé÷„‹_'þ Fÿ•œg•ý|ÌÍ=›ÜSÙ7ñM×JWûâ÷ûŠ^h_W´Ð5ÂÏÈÎd¯ýzÓcáÁµ_cM}­ºÚ4ØÓ¤ºõ|ÈÞèà6\«™ö|³Øg\’Þ¬r"8+#ýp!bÿ¢NÑxç§;[µ“¸moŒâr3¿bRî7bkÑ>¹ÓÝcV…}nšUÂXGòüA5Fx_]œRþžgæ{ Fæñ‘ýŒê/3År#öž1ÃBóWÙ«ótà: à~±qHRÿoWúúÙÛCÌÔÛrl¤l"ó»ü˜Öû&ŽnkŠn1˜5qjd}žž8­‹êïW®Yyá×ð+{mØ?î™Þ‹ÒlȺNhëÀ—T. ?OqiÔ?TÛÓ¬âózÖ¸ Íâ;ÐL‡•4¯Y£6çVQ+T½}Æí±fðwU¯rqÓ僧Ƒ>–µüVWæ®F./œ3x(Z“Ý®.-ã<ºH™;ðYêSr»®\½0û\:8MÓE½eÚ÷5¸ÌVðØèfiÙ»úÒpš©Ïéw&x%ƒfN¯«?-:t:N×¶¹/`ÕëMgÓmtÿ÷âvÀõ¾7z–„kÔÀxbæÇ"{²áR·w©òõ²ñ‡©÷bçmù¥³ ö9_ñîÛÿwÿû .|Ãk»¿ùkÿqïÑ_ûË×~ðk÷þi>¾þÝ«¾uïÆGÿ¾ÿ¹üGýé—|Šq=Þ±z‰Ûz5lô—“¸;H_‰hsSƒ!‡¤ÿœ{ÆïÛùëÔZNZk?ãpE¯ŸÂÌõ<·Ùe9_gWñï?ߎ…üF«A­Z˜§´‘Oq‡§àPßQ[\¸–uHl(yÜG0-ÅŸKâç•4RìüÁ¯cVE·÷<^lw×k%^÷Z…«BŽD¾¥¹DE}!³Ïõ>òÈbîŸîBÎZ°}³¾…4tºz’×,w † õºE¼f¿«YÓÄpöLÌÂðwÏë0Tx7#w =û>Sb‡Äïx6ñ‰óï×¥QªÕ}¥úÑ&ú ¤=ƒ?¿^õAÜWñ?²§¾ÏæËh¿À§snº´Â¼JôÐ1#ô¥±ƒŠ5—²?•ºõfXuÚr3z?ecýŸè@€5Éž1§t¦}#ÞšðAÔƒV­¯eêžoRϼVÅÆÝÚŸ5=òóh~§Æ ¶-ò8æ84m>­Û¨®ÿO5÷¨?ÚÙ‰ú/üˆáBÙ߇F“c8•.ìÒ*úm‹Î[Å^W £¦æL¯ëHíLŸ%Îô¹Ä‰‰êA!—aþ ë´Pý•øØÖÄrWaãnût?“*m+ÿwû{úˆù|ûç÷ïoÚ³Üëƒwm~ÝÃ?ë>¿÷»s=óü{žù ›¦ëgßûáí·ÎoÀ{57ÐýÅõ¬ÐÖTç#§ |0úÌxOùí .%÷„œ“Z‰î}æUø«êÝL{hÚ´üd—ž;i—–#G É7r’ƒ¾Oœz Z>ÄÅÜox…W÷…†¶ƒ}N7¯˜¹½UùdÑù¢ßWk·–vŒç³sÙ¸PÁ¡[-¬¢óžó††®v"¿èÿ„#ç\“ç¨'ó=û{é܉cs^5÷èíWà÷C½:#±žÞ¡ÂOTïV.?¡©¬çÏþúÆ9r<žZ~^[Û¸ç]Ïðˆ_f:Ì8k/ÒF©ôµê^ôESK¢6Ã|Õæ¸Ÿ,¥¯ƒÁ#Ó3‚÷°Ù@°x??h‘>ö7þyÛÞás^òuÛÿû}tûGo{ê…OÙþ½ý£3°y˾íÂ=üÚ-óúñOÚzäoÿ·­kîû·{w?ÿý{¶?GþiÿŸví^ðDžmŸmµ‚yœ©³Åúá½F­40‹ðÁS—ô[©Þ›Ú”} ,^u¶äOSgWÞ|ˉï;Uø9pobò"øSôTèÄ ‹¬ _¦'±Êþd¼1H770ñ‹}¯ qÃ(\~>zépR'Ŭph¥õ·–}ÙCÃ+˜˜åpCÎ7)䤊9ê þG™Â:ר9Å.œžÕœz ú=œ¿>†ày¨QDÜCÂËwyì1õ9aU»ð~ÖS|gâý¨As'ÁEñ#ö<šË vÊ!õüT½ã%fœ¿¸åZ¡ù£Xµ†^Û1êõkÈ®æ]#àìÀƒgÿY5Éœ_·×W1˜÷_ [œs³ØsZ “Ÿ[5~>óŒ#2'—…[2¨ÇÑÞ!úךf«ì²ærN`ÿÂrñ/#8r)bðͱÙñˆ1š—®>öú‰rÏÔ¦RŒFÏïß×?i\ïÛ*g_ÿ¢˜[™µ4ì÷/0ÆCùà¶éßsDÿ >ÍAÓ=ö˜ ¾˜ì{Ñ™Æo(oð8`êêÔÉg—]*`ðÚ´V»Ô‹ÀïàÅ’?+¶ &(`W`Mì‡úyüù°Š­ýy-Æ÷°µ>:ÉO¥½£·ˆGGm%-_úú|ÑògpEjØÀÁõ£ŸÚž:W~¨_ì}¿jùÿô¦_¹vûLïØþ//¾}ÿ¿>êGösúÞ[·üõïíO÷}ö–½ÇSÿkóä3oÜúðöîÙ–½ÿq÷åüW~ö&›ý³·’Ç]ŸÿwËÆ§¿\B 5pcŻ󮦵lv.5Æg+iŒÃ“”¿DgÏé0=ÇÑÌ?ª~9R×_àõGbÏAúɪ9_tÜ-ñnáôÊŽ:VP”oøÙ”&—ŸÅ^ŸêDêA¯-5O>^ƒ£óàç»õk…Òw«DlŒEà$êç'öûD¾þU•ΉjÁà“ªïL¼»°£ž€üõH jÖ‰ÍໆÐÍ™ W¨p*¹C§·$\%ýïñöõp„ ù=LÓ\§¿?9Zàk©Ó<êßy¾Isëà„S;ß%Vµ÷ëb=Ï]ÑIˆúËå®Vï±¾%5€!4 æø±A½oäÈ<½‹Ä h©Fî¶31ÿ“z®òiö„Ùê\òK†à`û†öFôþƒq‘ )öCÏtÄjíð1Êi7J{ŽÈIé­Mæü‰Ów.ûd{‹|Ît)µ¹Zyãàý·×37_æ¾Bz.øî¾oÔmLh;·Þdí¥Tã:WÃPlçÉó¥…4š?Ýa†Å |ú·„éÁ×õZnE×cP/0ë?œ^îʉÖC7ƒ t³f ž ýž §•³Žæá ‹™»šöœô¨…ó39 wfn öwìlbÆŠ ý;ûu® úY£SÝ®¨7p"^'oIÞO®Ç\ê™CêD^ÚÑcoÇñ à ÆÈùÒ#µRbQxU:Oôv;¶`k¾‹ïGsì}¡w¶õâïdþk÷^¡¹8ÒUø7}%j²>³ÔãÕ óÛ]?a¿üÖCò[qÄ/“`Ã3¶ŒÏ‹=3ý5°8Ý·JÌdÿäó^hóÆ¿yÆÙÿŽýâþ ¿dó¾OyȦù°~ÑÁæÑÏïÙs=ÿmŸ»÷¦3O2?/ºÐGÔp….Æ©hϬ§ÆþyåØqNÑåFßG럽pô: [{¢õdûù ö_YË5¥ž«öùunEÔ§åÿ¢Ž†þ 5_é„e­ñŒôOáó¨îWÁÒçLpð[½ÃL@f•ìŸió‘jh?^„ß›ù9 š1ƒ¸xÔ‰G†Ô¦ßàŽWì4qdWo›Xpmbdj‹â*_åÈoÕ¥©â+bqzpòéQG+RÜŽFKU|]uý-`ßë>6Örõ/z'©µ®¤³HžÁóé|¸ò<ç"ç„Ï.‚ÖVÿ»8©l?ë–3„‘è]£ï…€‡÷A±ñn•¶â¨ %~ölê‰éÌÂu?–ôtÀQT3‡¥¸°¢ÅE½EX<û0Iã·ƒáÆF̧}œz\¬^¼Ï]°‰V£ L ¾(÷|•ÚæÑë@¯ k·ÿŠœÝ*t#å<[|7\AL°uzÁ.:ÜÞ÷Ý¿¡él0¯/ù#X í;~¼€<ÚY`¢Î‘?‹9}ä4úì*ÛXª‘ âldŒ ¼vzÉ~îÀÅK˜¸ÛÔLb}£æ†Ñ¾ç<ê²kƒúï3Û}wiÍV>«ÅÑnÛˆï+ý¿Ä$²-Îe V?Œüðß~ö[.Xœû÷?ù¯/œ¬?}Á4úÿò®oØà±oÚ2¼ÿÌ7=ußÎÇ;>ñk{ÿé7ÿý¾Å’Gïn}€sÃ^ö`s]|Ø£÷@ãi4<@kYË-Ὠ6ºŠ­&Ö`Ÿ_µQîØD-»*¿’š9äߊ#—Ì·Ï®ènûß™í¤=tEÁ3±qË®YxT㯶y€à~k…ù-œ-îeKkíGÞƒØm•\ã+¹b“„®SË °Š[\¿Ü“X/|ÐE=óuê…̼j’Ÿówa&Ë"{Qs~XúfêÒÌgèpéY§W7'‹»q1ÿžZ[ÃUãû4ïZ¿;êyŒ#ì°C+ä¡`¯nQÀŠ„KúßɯËlWCƒë%öŽñ£òýBí @‡%Tqœ+þÞ5(ƒ‘ÜŽï6½$7OYþ)ΕzaárÍÉùxêC›YÇ|¥·¿$¬Y× æà#è™/S?!~ñý£×ŽÜfhu¸¼—QߌýYçí’Š?Y§Lü™I¸„¯]hˆ\NýšA}LÜqïÁ‚É/ëB:DÂ_8·ùÙM;25¥ºZùqéFû³eýœshuŸuéâw5žsÄdðƒ9ÓàBChkgÏ…Öb?^¨ÇLÝ|Åp»ªž»€¹ WC¿ç\Æ õöÁƒäýÏÐëçXþ)ùfCÔ,„]ÄìëÖ×9}ƒqïÁßGâAÚÒœKÅô‰ÃÓ¿ Æ þ5·Ièt½Ê²ß¿ÓÇéÄAu/¼]u¦+àM‰ßÛyW¾2êì‘k•Þ¯„®›}F…oIl9¨ˆN ØÞ5•Ó õ˜I•5º¦‘yº×7ÈýêæÉ/ЇF ºâ'ð¹Š)FðՃȳ®Ô80çÊ·üØ×nÙŸá;Ÿ¶uÿ—üØ–=çK®Üsó^|þÞå½»o>òyÿÇyþ/{Á'æoûú/Ú´Ï ;r™¾ó:§ZC?»š ‘3¦Å+-`&+qÛÿ“+æò:5$l*µÊÔ2ÂŽó³hr¨®’őԴ ³Àñ‰ø0ù÷ì1VX…C^µòu`®ßOÍ G1>399 iPË·œðºÙ#£:×Ä; ]¥;ˆN˜û*[ae£ðwæÎO²u©¯„QÌU…ãÌ…·UÅLÄ2»ñù>óÎï3„»ôþ·¯Áï¶ÌäÌæIy+½3pgxš|V÷™……ýŸh\¨ü^ß?øŸèÁ¬šþAoé¹<Þ¾0€à¶ºÝä‘®[ /ÇïÄožr…]˜W'»©3|Nëvž|0ï5Úx—ì۠ƈN½¿¿ú³ÎÌYÒßg/µòÆØ-÷,²Ëø<_ú‘ˆáâa2ÿÀ¸I`²êõ-ÄþÄA†ñI±j¯’C ~¹ÞéJ©·!óÓNËíêËèÙâçƒÖ—6â×À¤ˆI°Ó²Û…úÚ%i¸(UMõbbØá°±^s©èc£Ø½~1»r.ëσ÷.\ï58iR0ç‰ç6nÏœs~´ñ5e¿æÄiœ­¾ÞÜéyïÂÇ”k%Ìå´ò¢ Åšmü‚•ôþÐ5ã^ÂÙáùÁÚ9_ƒx#ŠÙt†bžj*Ì=¦'s„áÙr:Ç;œëQ¼#ï!Qý¡RoÑ=vÅö‡ùCà ÄNÄ%â`çzf÷ÕèëýÐtM R=Ô®ÿ@}ø õNåûa›"F?iñ73ÁÊÐêsâ7]7·™nÂS¸“c;;îsÔ“Ÿ=ãöŒÒæŸÈ™íûÿÓú“¶Íßõ{¿°õÛOÂökö¿é£þíË/<ñKÖ·¿ì>?qáožññMÍUÙ¼ëóÿnó-¾²÷‡ŸòýýWoÛü¶'ûÇ>ð}ˆà2YîM|ÃZƒ‡-¤mkÕô-ÁV–»ß íX?Ç'Ô‡³’.¯1À[R«fX%¸%\mjH‰aÒk$ jBãÎ>G¿ƒÆerLyw|µj…z„ƒì*î+ôH»&¿s%Ž·=‹å¶ÞÌ5{µ G–èâè¹}E#B¿—~“ ¿C,=díµqžï:tް«Ô°eè¿é,ô:…VWèj’Óawı›S ¾¦‡V —Ã|EðÊÎå]u<-Õ-_Oý;ÚÍ´–6ÅIô)‰õéÝr{\<ø®p®C¯D5Nb³ßÚ-×ÏÅÍäïµ1wMóeû:£ç˜ªíú\ÅA|!l<>Ý'rIz§nÍ”ÿyo£ß´‚¢îuKò˜9±žƒøŒôUÙ•®¯Ìgx‘Ëk¾fäIøcxÚèÙuv~Fì²ðºÝ±¥úØ+û6†îñôîíp';¼Õ?{NÍy]<hðóé‚¢cÞì ý—‡pì2ãs•§Kb'ûæZ °¡~ãC¸Ð»]>7ÆðÚ\öh¢WÝqë±%~þ•¢/U»ØÒã0õ¶Á+[¨`žŠ%©SŽôÖ¬ÔKÜ´È¢·BNêSnkT/É„ŸTâ5|-¹”ýûŸ~ü“öm=¶õ±½^|Âþ浟¶õÞ?<±õðå¯ìýÒç-·Žös‰xÀ²Íú¿åýæsågoÚT¼\×ÚI~¦ÖL¹×yñvîȹ¡âïf*L *9òC×ã¥3:).ž8C­¾gXy‘pÜÓ#{LŽeç4ìCözÕì3b®nè`±†íŒlx¯\ÔÀB–¾ÔE§õE?54×UgÖ›>É<Öȹmî÷ˆgëùÙ´Üá#¿u¶àœrÊÐ7;zñ39w#¢Ï9/Ôõ1©ÁÓáÚìñA5cð¸|:;è#M|>ë$»«¼µÒóI-Nypöv9\ê‘Üõä™é陡†I®çñ/¹åtâ¹oÔ+ˆ™É‹Á|é“£~¬³8éëºzø3êá=çSïÇÌ›¢ú¡ÇŸÂ`³gï(îËH¼;¤~pÓ#ïâØŒÏ´vZ8²×Ü?'ðÈðYúsò¹Ôå[%ðbׄ¾‘â±*^sÁÈgøþkFA¡Fµ.]døipÒõ?ˆëÏ<0r:Ýñ´‰ð”À+~ÏÌf©fŸèÄσs‰c˜Ïºfxw±_Ä…9P/ý Ì[µ~l.xñTÁæàgTCZÒCÁ¾É[V°$[ÿàH5=D0ÚÀPœã›³Q„+á&~…ÉU°x½'\Ë9µ ö¾ãóù^ÂqÞ_eO¹ ÌïLr!ÙÌ)æs¦Ÿm¾=p¨à?'I¬´È÷.…7LØjì': Ü)ÅóÂr±ã§;žkÌeæŒÛÚQ‡_å³µÞÉÍl}¯q_À©†ÖçR[½ÆuÕæ]¬›5é.iÖì5øóNPà/1 Lsn7šßÌJèjU»²[þÜÚïJü|)u^™—Îìø˜ ܹ¾þSþö­Ý®ÿ­­/¸ô¬í?z·ýÌXLóÎå›mÞÏV`äß·yò]Ÿº~ë|ÆLG>H5« ŸL.9xÍüÞQû|1cKæIá 9ÂbSKZ¹b‰Öñ9gFõxùÔü.¬#ÜÞ®¾I <ã,zÉÉÁoÅÙn àûÈÞûý„·~LŒL-™òÕsV"VžìûÌL%Ùñ¼Jòyøxè [\bßÙžÿ4ø“ßIÍâ?çŧ‰«™iT8'‹¦Ó2ƒ‡˜ÌDg£óÛ3Ù3,š&-¸÷ŒáJ³l/í³àLêÓ0ž¤†]‡M¶3kq–>¿ÿ ïô{L}t%|tÛW97j'ùAÂÀ ³udÿÔ'1 q¤røJýO¼ÝüÙ;s¶aÓ„’Ëäuë¥êæU¹¤fºÑ/¼¶ ¬îT¡WUþBøC|?~P¶(ãué+±æCÓgñ»$Þþ²¢‘¢ØmJl©ó3Š_â5½à7ŽyöŽváu­·þté¡›ÓA=O—žÕ¹ÎyÅŸ)‡†wz=œ8žC¾Ó×»ÍhÌ„™Ià~ö÷ðmÿô~‹™î\!§Q]ŠoöÈéŽË—&v¾ÐŒòžcÈÞ6ŽùÎØ8çkãÿäl@b6?çâ‹ón…Mìý)á‡9j$ß¿5"ñh’w¼.ínùÉì_„«Ë¾ómÁ¤xL±}ôÃÃ/–/§pn¸ŸØ^8 Í¥£žGÃÞÃòUõAIóàš9ýIº«ÔƒG¸!ôÁ¨Ž,}…y{`k~ŸûÍTÒýÀNïfÍž*øG«´á')nõóì¡f?8ë9cŒ„:Ye=Ñ·¯¢Oí4ë’ó.°Sôr“7Ɔ ³®|64_éñW?­ê´·¥—>¶ýþ¾}ÇÅ7~æþg}ÁWî+'œ”/Ÿõ¾?\Ú}=Š–÷ù½ßM>pœŸ˜å¨9`©±@­ü|o¥³Ôð9‹Š³weçd»rÌ.8ÓB½˜ô’÷õD| ¸]U±ÃʆO)_ÉS\xnY/ú¾€™ðßð¬W­g¾(!ßêî{ØÎ=só~ x™ý·t]³——ù*Ê7|.8óç•ïÖȉ#G]Ó“â€gí9uUìó˜§ÌÝs zB5°‚?ÍÜÄþž™`üŒý<ØÇÐÕ£·âtöé×è¢Þ-ï/\ aÇwèß™_q™þqçx1¸¹‹£+óA¹GÊoFáØÉËW„½©<÷B}ƒ¸7Âts& ŽÞ Úæ ½Å£ŽAêæÙi\_÷èÚUa`Eœ¿ÔXÏ2gèÊß‚‰égBn]:aàl]ÎCÞ\‰Ãà^›¹'ŸŸ}âFOÜù¡ibUÎtÇ]Ôw缂‰}'·o<ÁÐÏb¦ò"µ›£Ž¬º¬p\zŸ"þ‹˜$úþm•û ÷VV­xWù`!6:Ð\ŠU×ÿ†ß³w³ à‰Ô÷Ù¡æ †š3ˆuƳþfë£:o%†F]õ  »®ƒúÀö´vÄ®‰K Q+'^£Î¼ÛcqäÊŠ£ªîᜅ<™œvPOuÚ3Ý{­ç¤;ã>·ñ‚£.‹}&‹Ä—šýépBù ó`yÔÀÛœ3ÕêqQ ´õ–~Cm~³õõ¬n™Ðýz £ÃùK àfiÎËÔ!ùçkÙ¿@½Eq]Æ„Ä<àë¡ÁËÞÓ#[B‡Øë˜²yg»X9ç9Í”Æ)ÛÉxTÜ™åÐt*|MÉÇÐ Bo†ú¥žÕÏ'û,Œ„œ¯µñ÷_ÿ%?³mþøßo?nëñ¶ýäglÿ÷éö ÷ø¤ÿwû×ÕÖ—¾õÉÆ â€ùŸ¿ú^[oþ‚ÝÍ›>ñäý#»µuÛC?2É•{î­¢Õ룂 Gçu~×ç¿8{Pé¡RÛë3γóDó·{0áp䕸M±3³Š0MßOÍwñ;H øIz£ÇæªU7¦DŸ–ºÃ¥¦Céñ}p‚ìÝU7¤†:‘ߨsÁÕ'G 6zÛC_à½ßö÷ðéè²3uüͯ[‚acGmílÿ®•þ;x†zÆAý57knÜ|ôJ<~3°wú\ *ç.ôПL\£úM¤ŠýBS »°Jm™+𥲶l1ļKêc…? ]bÃÀ.¦ÖƒìJi±YrÄ»ÚÐý£'0æy`'ìþ*]’é{xfç«h†~—»3ˆƒÄS½4Ÿ ŠÜ¨aÚÌß@[Ìù·ðDtÖµ·¡ÜìzgÌCˆƒžqêû¯ˆ¡é­é1Ñ6‹!bWñÝvkM;ŽFà½p{팠 s&ÿÇ åB=XóD&ð lÔ Ÿp°]a}hJÍøÙè‹ùƒô Á]4ãÌLXü£aÉŸÁvû³ÚÓ‹Ôq(+û¥[°¦MI[z ĹN8Á :±Ùê*-ÚÔ3^Ê/Tî¦ðøÞK|?¼t´g•¸-a†Ä¦òaá'ëûß‹¢Üic6¡™Kþ{ð™È Å+·¹ðÚcîÒ iˆØââGÝN÷“ú‚ßùíª¿G#¿ÈîWåYWÖ1W|;²®ªSù>ŤA$ëui±»MC®ëëì¸wÁ;¢oÀ÷Èö@ú~ðÄ8¯þßøx°6òá %í¯ŒiR(Æ-Ò6ƒ[:/Cgv\Ì=G¿¥Ÿ?æÿ £@Cwâ=Ô;ëÿ$Ç%/Œüó¸÷XžÑsöí;^ùk߸ÿ„ÕOï?ö…uïÎ_û×þ9zëísŸ|ôÝK›hwéóî½¾¹wï—î ãžÇ=[ç…›ìøü’ÜìKùÇD]ž•rú›&Íç©ÔòV³“XPص˜ o/ækP?à®jMR[MövŽïk6ò|æ~íK™ùÈóLŠ'䳃çikcÏ׸H®ƒÏ)‘‹GnbÏgœ[?x`ÊÁƽ6hŸ'-š¬ÂDÿÄjùôO“g‚?+÷˜ #dVZú{/æd(&ž£Ó#ÌJ±5zß}Í…3y,‡Ï”-CÃês®žoÂØ3I‡¶6‹Yí‘Oi>çˆ/<Ú=ýÄ(²ý£úa ÜfpîÛÑ»/Wâ3/[eëÌ\â냦a2 ?§öMókàýÌE6Àý³Î¤¿'ýÀôÝÉ7)V|ºáKQoá§ m–¥ò•5xpäOU8p¥o pý‰xƒÏQ¬=£®Cý`¡þbê[ÄÃÊkæ+Í @çi¦Í³3Šü‹û’Æ[¤'<ì:ýšÌ0ç ¦+ÇVh˜ÙĆo¦ &ö$w²ß·õǯ‹ â`g”§;Ç!r@ò±ó#øíºzéÀÈÀkuoz]Â_㘚ø€Y/TÏDWý!ú ÉG©_ã#UG›:û†>Ϥ8Ê}š¸$U1 qõˆ¢† ¦Èy †*=p0çäÕtü÷%öXß)-¿XWa©7BÎÏ<-ÙäB]“x,ÝžÙÖ,|PhìØZv"Ô–sfŽò_Åsçä{"§Y¥¦kðÌ…eäìîá´¯Os;//ð´Ë™+ˆ““ÚÊY#“Þ` ¿|{á÷[˜˜jì¡“£wV앳'»þŒ¨]è=²§¼CX`j“ŸÙ3ÿÌù§ž|ø'ž±÷%;_rá‹?ÿG·Ÿ÷ž;¶ïú­Ç¿ýÆ—\¿ý'¿úúí·~âÞÛÆA}ÌG?¼wÏß:¹u/lMßz÷ ÷[»÷Öwßü8ã˜þ_½éöWÙ:,¿ê~¿±4®€ù¾w.ßlöwμ6[Kã¬gÀüPìÕuÆS]Òwa?§~¡ä<©Î…M˜)wŸ¨j ŽƒÎ"=MøAìž|µ®™úð„醽ÅW°gm~ …«î“j|^‘ï™7 òr¥GTØVò¼O1b±¾G.¤¸j ß,×l‚êóˆõRs§(çñú½iA­çÜ•à^XÜI«;QuÇËq©NSn—ŸI;ƒ¡;ÏÜ’µj¡ÞA¯ø0÷ \>µ´³ÿájmÛF_q¥9šä˜ò#]]ùF¸ÓŠé\Ç<ëÿª›aªâ68ïð¾*˜WW¯)ô^t¸}ê1)w/Ԃ஡{@íŸ q›röŒIÍ"r^ÊÚ‹µ©õCŸã¾×°C®­©˜é|†xbÞ•ü+|3w|¸a'ûžè+\„Vw6ùð ©ûö8ô {jø=‡mè¸Jä5/úùE±4<êð»ÄsÄ ª/`ú¼ËJüzâMêfÄ‹ö\蘨GšÄ <0´`.Š—y×;À:žJöÃèýœËÆ`ÿ<®B{-ìáFÕü¿Eÿ`ëŸÝHà5_eúÿãýn|òþ~à¶ýí/Û_žúîOÞú–÷ÿçýÏü¥ÝÍxågìÙï<øµwÛ³áo›ÿÑs> æ€<&¿C1¿ëJÚØ¡iÅú³ÁTΓøxÚpš…^IÝöÙ)àˆä|:«skNaåÎë¬ûg€Y¯R7ê0¹,Ò,Wëw£ç]Ò ~ÙIÍä[“V)³Yœ#e=\ËKÒ½×ï£õ¤\ßµ²¨Åf/:½/:`òêÅß)Ô`Ñ“=e†ßL¸qr$eÓˆ™·ìú­f­>€~äZö÷(>õßûíé÷gî ®GÌñk˜< fp…Ö[1#o!6;_µþ·¥àÝv·ˆ‡õ “î ó ågob~õáª=uþ‚0–2¤fLàË~EM f½i9 7Œy‡šFl”¼ýL‘vÿLö³Ò£ºÏB:ˆŠ›Jè™lÀ£,¬×ÐõR©¿!÷H¹Þ’ykŠË–`rÁÁ¸n‰?”®šQïµ'4º¤§G\ñêE|GÖkx>ô;À8°ÿ‹ïÊ™:ÌêšÔ«sõ1G¾Ãâ’O§ûY¨‰6ÎÅ!ß~b í¢Àa"Ͻ ¼›š3ûV‰Ö]O/4'•3ÏÛiy`ÿGréÆ7Ø)íyã\h.uÆ®ØFa»¶NK¸:p÷ÉÖ5ÓBvW}ÏWñ9ÿUq˜x·1îµC|>|¢«¬Áu@'£‚ MCÆcËkáÀ/…,¼Ú|Öoé/h:kÌ|†ƒAΜ³WàgÊîl-<•Ej¤Žÿ÷Yÿ¼¯|bó÷{ôþÓîúøþéó_²}ô.û÷{û›öÿà­{®ÿk½ÈÆ<ÊÝ÷Þ¾uiÓ¸€GvÁæýí Kó;oç౺})›>~VôÂÊ·H?¶á”ŠOï´|‡Ø™||'Fwl¤ˆ¼œ:8š–ú ÎÂ.¼ é’‘›VaÄ%jlÌ#nŒ>DaEsÅ$ÊWÖè- Þ;_I#@q¸pÞˆ‹Å{ÏÙî£58 î^•UÅZ‰ó1£E1u< âO{Žè_ ¹ødUñ•ú³ŽÏũ˺“45eŸ=®Ëx{ªX#gSbk‰-‹‹~±îÜfL§8ØßŸ¾húZ/ ÜW|j¤AO„›âd&ú·d— ºuz'æ|ùùê9ØbÅ ~&ÄXÂç%׋|•™êa„AVÕçsF1¹œy°HýwÎu¹Yš×à :«U1 Û+aùíœ|Ëyì~\⦅*XqMä¹Ñ*ÐÌá„'ûúH+SœÔç¢öÞ顳SɡșM¬‹rª8'{BìL„îÆê·;Ô©*õ<ì-zÖG‚O¡§ÿ48Wà&ò!Ý%ÇËüßíÜ0“øL§ñÔìÕœýG-P1e¥¶![\Ïeÿ°zlàhTÙ ¯—2ÏYë›xƒòõ¹¸Ka‰Øóš}’Ý_võ1ÍÔvm6 Ñx Ö&2ÒCD+pK¯q&¯ÌÖÀðÇæOÃm%†š˜ŠýTœ0ArÇy7a=Žó™ØÔ›¥A½ñ¹êåðVtn×<>±û\ø`®:8áØR¶ ýù¢i„º?EëAçW}“Ç¥gã½£© §3èù±ú6Àm—`Y«[égõ¼ ®§â¤\zm‰Ù•ßÁœBßc UÔ÷åñ€}§é¦à›Nhλ4øüþH\×ã b|¸Þ?þŠ'žnÉÙiò©i'Ñ.!>–ªØz-…]ú¹Äæ»Û¹a~Á"5ŒbÖµ=\´Á7ÁžXÛÍC'f¾SípÁ Ö~žZØàçós7·Ù)£úç =ÌÉ®?ôÂ… \-ùæ30oåˆy/Oh& 50‹Aõ™K­g¿ŸÙÂÙ`­ y'ñ¦luúßᆜE`Rþ©û™|ä]ñY¦À¨Ž©ï.c€Qº-hŠf-^8æ.Z Š˜¯œ4u[àH ÌÀÄm ÑŒÑýLÝJÅÓ3âö†³in­1× ùÕô9è>:AyðU8v4æ¡»†xQŸ®ÛÒ¦©šÿrÝõ7¸ßôžîŠcTçàú­R_Ó¾Ë΄­cã5E즽òw3ž3u#øçvð™è4ÓÃfœ®k”ïù¬æÆîØ œ/Þc‘¼ÜФ…ƹZµÚÿ6ñb‡û’»xÜNCmâŒt u/®šï-?¹«šIí8‰±ƒÅ ƒ©o“*ÖÜ«œ èç^‰ò‡Ä†É«„iÀßb®¤¸6§‰ÐÉÝU~˜ºk` ÔXt&«°º‘Y“à*àÜ]Å´©å>üÙ‰•5ßqMÖ÷ _T.ºDŸ^50?ŸÒšè%&’Ïç~N‰_ä>”Aµêþ<÷þ€:€pÀ]zmán®T«zÐ=ïqÁÎÑS?p· ïú£/¾ðû·Ýgÿ3i¶÷so~åÞ±ÅÜÜÿ¢iþÔÇ=ë ¦ù¿g€Ý;ã˜ÐJó-t?¨yÃó˜ Ÿ«Q£Þa׎ƒ¢–±jÚ#ó·‰áˆ+áä¢Çjÿm6 }Ù¯ƒšcžYWKKÍPÝÏ î‚}–=-1cçT¾SWªQ÷CeîoWgðø×>_ü?åOŠ×ï5’VKm3 ù\üñ51:}OÊÏuVoI¿ssÌ +m.Dè¿F­=4ÐÔ‡êùq®ú°Ï—n_»¼’¶5:úøúä»áøâw²?Cš…Þ zZÀŹ‹=¯L_y|ÆèϬwÚéŠßÒµÞ×CÕÓ?ôÙÖ'þ™|,´8Çp¦°­ªãÎáÕ‘7 º…Cùó(å>S372ñ*õG=WëˆÖ^U®8æÆìAl³‹T®K¬çX>±¸-|¦°'—Ī͎·ÒÎZ¬Ýñ%š&­ÎµOï¥òÄS•÷•°YÇtü'âJÅ$è®g}ŸøiÕ4dç‚!Œ̑ث+pÀÛ©)4,úîxž—…tÀäd“ ¼­Ù(_SÁ™ÉùáYÇ?í¥à\ÎýWìŠÏ·dm»ÔÄ•ög «ÕÌš¸~¼Ú|SpU„‚¿ÄgChg‚¯ÏµÎ`!à±þsÂãàEŒØMøiö ¶÷p—ñð©'ÂÕ¤† }™à)oò³¯y$ã TìCÇ·¬<ƒö{ >Îaö° â)Nš‡æJÞÛÖszÁ½8®ð'_/«ÃEÎ|lÉLCò?0 ík…#+ [ØÑyfù^+Ö"}Ép:‡Æ£/͘[uµÔ¦•/÷³JìØÕJ'l~¼î»|çÔßþ°'íÛxñ§ÿøÞϾfÜÝsÿŸý'ÎÎo=ìûï´Y?uçÝ÷ß³=6;`¾ß°‚?øèS7ï·öª=Å>c^Z~žgXL£Ø¶ª7n©û%.GÎþñ³OÝIk5usäkàWŸÊê~z;‹üÉgò•…´ÁÕ‰{äg|ÏU7…óæ¶ÂÖ„ó¯ÞM÷1ØËA\gÅ'ÉwUÜ¡ó™õñ‘=#Ôc§<È÷K³ôF}ÿR÷ø¯Â­ZlôÑØZàÛeL§Iµ–‹µ›wµ«¸vRÝ/‰» x?Zâ:-ºyçð+^ûPY¥IÄüÄYãüE]M¼ÇŠ}ZdùήòŠ%¸l.84¼xð¯Èm2‡½ÔfêâC'á>ø”¼ü·­ |3ðhbnðuéùMè?*&­Ø!;óöÖƒI S9ßãuØ÷Y'½òᓎù¼Qÿ£¦šó?ž&/ößþ8£æB]ýtÎhN¦~~ÃR=·ûuLW¸Z¥gÜöQ¼,|¥âÉàcè bÁîÿnS/I£­ÅÈÙ;‹mß%–v V5¢§"ΤÖ3úï©k‡>óÅœwµÿ¨c ÓÆï©ÿõ–ÿ›Úüô!ØŒø/‡œõÊÝì¸dØ—¼ß†w¢U¶Ê^”Ôqôx®°rÔ>L¯5»èÁÔgy¢Óå’fr\ål‡ë®QW„3 o¨èÊ/€e¸†œ°pjOØföæ³F 3’ÿÙƒó…~ ?§ß‰ ¤#œ6ˆ¸ZqTUþ¥|*¸S`ÃÚŸû­¸Oz[7ê…†.z+|OÇûÿñÃðré…ìøÉë×ýªä CÇ\匩àBjËÐxEñ::.ËÆy (â4ûlõ–xáŠ)fÔßÄ»)øvñ„-Æ~À·fÍáÀó™-°¾~[#›ùóë¿jÓ0ú§.¾qïrç¦iø›-ÓZcÊ¡æäeÊgÄʼnz5uFÙcϥɃq:£žwú*ޱ·½‡øÖ6b Åg‰Sgⳃû'‡›~ü¯ðÔB¾ÞãæÄùwjFšÎžÿ=XuÏA=4+é£D’µ/õÿF\»ê8|àSħöù:n‹ußgô­ªVM̺+cRNÎÜ…‰XHþÙÏ®ù¥ÀLcÞ(¼4áâä+óÖ7~z¤^†'qb2‹à-å|ÏOh®$üÃEÓ,LŸ@.Å.ÃIÄç$'î{àÄ“»(þ.üÜJ5Cîg‘øQy(SÑM½(î}Ë%O“óíRsc}•ƒTjð'šîÛLœ›žHoê¤ØuËRÓhÐlî¼°Ò¾ÿ`RþœǺzT—ƒË™³RŸòŠz'wFb8ðoâ§!ûséQóû­³Àaa •çMh+£[€oX—Ö—üF!F’íJ~‚ô¦æ+è Ž±ÄröÝқïVþŠ6¯z¥¯‡ üNþÑçýÐ''.%öI{~N›cšÌKˆZnòê’‡«xy”FZmŸ½æhƒ›/étÂSŸŽ9‚êDà13Å\¥íÝüp»¢=€¯_¥WÌ<8îÛ´–2½ÔEZ½/jƒœÖzÌŒwäÙÃF_ÔúndŽ9¨ïhÈþsU³R*>ƒ>oò%Û Ûcûltóæ`± ×t+ñ8ÙËM‰üûOŸ•zW—â׌`¹è7R{Ä;foÉè/;“³ÄN.•ûýˆ^&¯mŽâc7ʾãw¶í3>üÝØþı_Þþ¡Ÿ~õöá{Ÿ¸ým?ñCû÷¹û[¶ïX¿eËÞãìÝþbóë^ñÿn=ôO±õÏù§Í?û­ÏÞ;ò1¦ $¾Ù†qˆ÷Ž>ÉLÓ †cóÐå{ö\:”þΚÛWCsë”jǘÓCÍ ­Š <¡q;ÝVð(øÍÔ·ô9œÁì}Z¨¿H>km)fÎ6îPèX…¿sn–œsÛK>Ÿ^¼àÞ]N¾vÏ WM&s*Õ,Uw]£Ÿ‘3*þsàTôHÃ? ¿zLšPk#3Há (ƒk1*N*âÃ%Î$ÎÌ$;Sàøé;fšÝ8£‡‡zŸ4‚‹bƒ¢žäÊ]þ×uì}¶ìJ‰:Qhd(‚‡ŸÉ¸“´Ë×f‹ÆN\æLÓÁC ùbßÛçï.¿•<ÈÈ‹™·½jª/€ó•Vÿˆ9tœà©btGÎÙz’‰qáü)/±ç§™œ,t`¤ÕXÈÕäSÅ‘¾B-¸ãÈ~Ô›s.ôFÎ?P{Ôn®™‡òü>ßs=9}§sNŒù-i,óu~{ãÕ±ÏÔ ºs彨â•ç3‚®B;“³“Z¿šçG]=Ï­òÛ‘šºüé¨|ÄcDîµ4ŠKw_Fqß哜ϯ}B×7ìFùÃiÆ’bìð“ôLÆÙO~ZÆyôØ®k‚êÙ©ÿµwK÷lw½iûïÙºÙú£…²êzÕmÿ¯¨ºŒj°gÑ•ÀÆzü'îD®18§°ÎäYbk†ä„n„âŠP9S5}}ô'l›ðsúÕ'°åp>ÖBÿ›]Ÿ•ä-äYŽœÐÜ0æ$«G‹¿/ÔKTߥ;Aí¿´ó:™hƒôv°ÏßŃ)1ߊ¾§à')>÷3(Þi‡BŒÈ:[\—²Ù’ÓùNÄÔŠƒ°]…þÏUj„MÔÿ©ü°vñŸ¾7ò¥àÝ^‚ÙÁçmœÖ³åC'^±oÏñÀç¿bíG~eÿ‰³ý¿önÖ8ÿÇÿêþ_Ýñï¬Æ?>éÕÜûžg~Þõý½þÄ÷»_¿ý_½Ç>ʺ¡7UwjFÜNì…f‘ù¨ÎMÖbtªl¿âÙì-)ŠõñóÓJœä¨%\.Äúà-¿X+pçñDXËEô_–^ãv¡™1ìÃõMÏOùþui+?¯vì}hzP4“« {¦7—zA¡F ÆÑÕ]'r—àŽÏxE˜Á’zÙÅØÌõØIœ)» óvŸn·£Þxc‘n¼ ÌáÈ“‰Áâ®EýžÜ$üÍEêV¾ÇÂÐôûi/+<ƒ1뵈÷S‡6£±ÆðµŸw˜›Ÿá[ê­9[èµ wCØsj0ÙZÈÆ`¬p‰èijn@ìˆfî¨Ü£Â9^—6•ò!z?Œ+ƒ¾”c|¬¿r!ç9+ב?™M!ÿ@w@9ßê\Âòæƒô’”;U8Ÿš2×>ùù·÷5›<šãªÕ&Ndëgß^JN„NŽüpê­¢UŠÍ†IoJ7G%z8Oxïò1z¡¦ˆñß®ýy̺ƒ5z«OqVæÄ0â$.8âEâ‰‰Þ òAžu§B½ó+nõ³Dx¢·F>&{z”Ï¡—H­»›R¬}[Ö…#7çÙÒjYÙ¿îïš]á—¨›èižòÉ9ý`I=«ÁÝö?Ǿ¡ʺ⇸?ðð8×ð6øûáf»îÀYÑ"Ž´Ûõ Ù]„³îk‚¶#8‰=—òúz|—© \U××<¼‚/¦GEØFÑìéÄ„_ÔA54Κø'Â{Ϧ î¸Fž_ácÄ=sÿ þYjÈŽ¸½ çrñÌ, íXðÕ²…[žR?ŽkárF¸!äÌôŠSÇÔÏ%ž}§—\ÂÎñÚûO^°wzæWß¾ÿ›/û¢ ¦xò]ß»÷ü·ýäòëxÓþoùàžÙÈ§ßø{ðÑ÷ì=ëžêÐåòÞÝ÷lNœq ì ›S<wF|`ï ˜àû6¬ÊcB|Ä(¿J͇¼6Îõ ‰¼nˆ^ûe«£Å"QÌZ»³[Wê £¦·êø ø;³¹ÄØMúñ´–™û ³Ê™uä¾—4ƒÎÞ›;;$WàœâÄì$GÏ; fÑçKÁ÷9?…&žcèÇs£ùYä3dCsîפ:ßH]4òÁÈIÕOVÈ1¨­‹§W;<25ǧˆCpúªü‘^ é‰Ëþ]6„œ’Ü…Úd—Ó­•.ÇÉñ¥©Ç\4tWS^÷<9æç¸.½à×ô¸|{N= ¬lhýOøŒ¼GðñÖ¥qÚì w2bnð;ú7ÈyÀ™E!”óìó…Ìù=°MêØÓAܯ†ýÄüêžÃ 9«µË¿ˆóNS¿Ç6å}€› þŽfá Úýðª»xÎt"f/U_)f!ýÕ¬óƒÛ.wsHÝô¨A 70W ó„:ÜóF¦Æ3rþX‘]ž?púÿN¸þohÑ)×pûiwÐÖM\œJ좘+qK|<®C÷5¡Ï€X[ñÖ¸èzÑtFݯœ…LL~ïƒýULI z¤nÏ=mñê!ó Å>Áž©_Ùï13l=frt•÷rgbÙ3Òµ —&§ÂÖ ê)ÕÞOäêÒ/à0çájWk\…Croxdœ©Jܧø'û̆þºkÊ.©Ë“SØ9”>ð¤:Ÿq»‹ÄÂËÈWÇk5»œE1%u!tY¨Gl“ø:9ëœóIMœXZ÷̇¼k.Oáü‚“ö¼3xæ-68Ï]Ï9:Ä5ìÇï|ò_mÚß}èØüú_ºnëõ'î·iñ¢éôýÑsþÃæÏýÕÄ‘<åó.~ºP¿_©GXu©ÕÇ‚û¦ wAqØÔåÑ#wk4ùÇÒû«MÛ½%ç΃=1ß ^2¹‘Îä.¸ç†ø ?!{¢ýŽ”º<ñ¯½+ÚÂF™a¡8ë´xkñ³Â‰©›úz¢!% [¢kùål>Ñì<ÚÏÛòŠƒ˜×S¬¼Q{S¢ºLúœÉØ8÷ÏœHMùàˆé,òü£ê8Ùw&ç˜ÑkÄÜ ÷¹lâÙ¦¯G‡3òñ+è\ŒØ~fjôlÙ;p˜‰~@øR„&rfØRßÇ·+.šÀ}t&F01êoª[€](¯ ¿¨U’®¥›aýö=Æ6"»¶|}Õ§øÜËé›±¯²‡Š!¯[+ðkÁ’Àp‰«ùü…æYâ»Åµb­t¶Ñ÷ºÓ ¿iÏGCŸH\Hݪ’´¹&»ä_ØØÆc¾ÈŸWrn4ëÍöK×](a˜¡¢Ü|9ˆ{¶HýŒ+šUúmÂõ:Žnνö_Ìx†§;½T>Y©ë°†Âß—ƒø6äÊﶸ‘‡¼Ÿúš0Í.;©hCZÌkÎ –êøEØ¡^û‚¼‰XÞ܈ßÅέ¤¢õOû#ŒMÿ8«4:&Õ„|m˜ù¹ÞúX&ÍÚôøœêêx>æ\šO!§°?7ûÝáÕÔv©ë›¿Ñ>pÿ‹°·3öóÄÇp&àØÚÒc¹P/÷ ^Û…f­©éqSÂÀ Øž|ü*êíäû¥aƒÁ·×™T'Ë^î98½ìiúù·ô£ðÅÝQm!4&šŠ~–˜sp*kV²‘®λ&†îù8$z‡­~ËîÝOÚwíýÕÓ·¿ô>¶ýÎo~Ê…§}É{ö_õ‡Üü”/zá…£5ß²ïzË—ßkëý«ܺñE¹÷²|‡÷ýýÓg¯í?öQÞ³ßÿÌ_ÚõúÿÑùœGß5/ç¶vfíÙn{è ÇT¾GÓÏ¢8µ#6}ð'Å.ÌØµwšM /¹¨Q³Œœ˜8¼Õ â9„ƒLh©ˆ —},Ämà 9ß»€5Ä÷&£>vj|îg¤ã6¶8öšì-X5íUêêʱÐðŒØ’Z±‹ì+óæ˜µw02agCpŸÙ{j§7kÖ»r9Ùæjy]Q5áòÅ"ŒÌqmòÙ•›mçÜöOý“ÎÛ®ugÌ^/mv|ö%áãÑ7+ÚOö2÷–õÓóS[)C«=áÛ+>ƒ8ËþLõËÂ;Òe]ÝzZüf×ô5²¸TxwÚ6Å:Ô+Y÷ܹã¦Á›*Ä”äêò±ÜSûsË¡áK°÷ä¶6ÌEæ>‘·±æ²un×Õã!,àä’<ù’Ï÷Þ¥‘½•4v”«z}A=áÉuT-±ßéú91¦ê¾V–ëØó2_@5uñÂbÖžýYÔšÚìy°ÀÐ˸E÷·Ë̱S¼{cåŒaoš/¹2‚ëè;'ò*æÏ‚wr¨›Óç¨ßIþ¾ÝKòA½«ïa×?¯zBä¤pÁ#¤c9q×á(Ϋæ/iGNr<{ím¯LߦÅÊç²§Kö½Â!Ñûaˆ²×…¼á„´ŽuW¯ÒpOQNçï!nNeMÈÛ•›WðÝÑÌx‡Xÿ󹯊&bîö}~Îß_“{ßáÎvè?§v_”ëùÝŒ|©7 ¶UÁÿÅ¥LŸ·.}7fÒÐW³’n±ÎÓd6ÍÞ=¨ÆW û1êuêæÖóvù匉¨=ÝÜÍ{\5ÍZû}͈ùð‡ìA‰{eëh±<5R°]ù€¥|€0ЇÁ¿QKQŒ;…ŽÁmà#¼¬À×\uÆ^±–Ê݈98/þ½ö¾Ê¡´g Ú«V³È<ìH>ÓÝÉžrA9&49ôÙigÍ÷hÆfxìâû!úØæÖŽ÷S‰ÍìŒvÚî~þõYY_IgAH½Gè­r&ïœS4Û³Ö®}\rnÍO‹7 š1ÑÕÖÀmr¾JôzðŒ3Vm|í¨EjNt!.D+h¸¹‡k© ¿në´äPËâ\Ïà¤ÎÞixOU8LƘøjðÓvŽZmup~nÌÜAÇ ||X1=KÌñg—.IÎàÆ€ÎV‰<88,š1/\!jËÍþ„.}´«[™‹¸–Ü—¡iÆ€‘$^IŸjË1sþÈ.}4ØÔ…¸ÅðôáËÁ£bˆ£ ˜l¿À›Ð÷Vr$|+ºŠ‘søÌV4.ä B¯*løeÅuYGâ>±Ç<¿Þ±,¤§&M4å‡ç*yÄ ~4x#Ü:¸Ç J«×îŒØeb.Õ>¨åŒä ødòÍæ¿ÎÓ?2ÑKùîr7æUq7Á¶FxÎÂ5ÓítνŠbÜ'´âØ;¿v'•[T= œ™Úé—`ofpªtàÄNÄ}ðë>ÿ“ÿç¶½ûg½üaÛxí…íÝ÷ýû_yþû×?ñ[Ïù‚wíÿÓô‚-û¼­›oóÏúÜ­Ÿ~ɧì}Þ½×÷¾ú~~~ä;÷Žöz»k³€O„fã\±ìœ¹Ìª…Qû"7!¾™à*áWìý”›òÒ=g ÿLNL=ÄöÀž íéô¡pGµfU×ÈÞÑ£0D­…Z¼çæð£˜/höÃì…pÅvQ[·?³>Nø0ƒø³Ê_Æ.¾`NÇ®pKÎ<ý›#ÚØØ¡ñ v¥[!¿¶>ã@çUyÆab𲕩O —`¡>ö?îà“ìÿøÂä3¨Ï|—ü ®†|y¯c6’›ÙïP‡Bî!Û·o{7òÓÛ’{K®5ŒkÈÕ*ó6¨›³¨V•ܲA:£hÀ7SsT=ÍŸþxºe+×Iz;9[ÖœVÒgVŽÖ¿Ÿá7’ëw¼¸™òѢ؋œhN]$ð´ëæì¸ ‰ŸÄÚ^?WNÎúWõÄø:‹Ù3Oì­<ºªöÒÛ ~³?ØzîšbÇ¥+•v>–}–åiëÒÒgÅ«cè]Ïžyø÷ÂÁ=Ï'#~ ÎÜˉ]<ÖÔÓ¡ÇÜc´V÷ ~lä±F+ÍF[‰³«~— ]±Û ìà LSç¦êçeÎÕΗëá_µÆ.y¨p¬úÏù®}[‡ÏyÑ‹÷?úŸ–ûõ7·iØÿM·¿jyûÞkZ¿>ç÷ä»>uOÿ>WݵÒ³ Í-8¿L2òˆ+µÑáœØ?-¶îøYqë"æ’Ùµk˜wÔŤÎÃaÎ73gÀìàÎX}NkAÜë0ø­´Wº¾ÁCqˆá/ÑùRÇ<êëƒpî…fä®Tï²ß÷2ø,3±àŠ ±6PëgýÐîAû…ü N´Å‹¦ÃºËù猠-ŽV¬|þDMC8o =µÔõKþ Þ'˜uøñktžÜn,÷{Œªžô‰ø†=`ŸWÒ»$¦Óçw[ ¾ oRzWU9b ZÿôwQ;´µ·¿§>"¾4uÜ?l›ú%TÃòù?©w,ÿ<¨§O:?pIÐauìPu¨%yê*usNÓO–y“Öi”=Wm¦IjRðýä‡h6ã«Áû• &0üup¨b¦×æèÕ̼ z \Y9Åñ¥ÝIâÙ®Ô{°g±;ÑÞÍî`§[êý2Ê£•—øìGÿwâ.a˜ö 9*+Í$P|ã1'=¹`ñ:g“bª#ñ}KÕ‰'Μ­»ÙGÎ>VöÂãݱQÏUØ#tÔ¾ŸÊÞÓÍuö]ÑǤ¦Áù³Ÿ³ý3HMR8Ø/Öì¢é›âº(wKÜ]5¶¥z§T/Ž8’~ÛÏ^Û'Öù\æ¶ê»Q›54jäÜ N!ß?ªþâw‘s¨‚OŽp³æ½‰ ]•CÍTãIL¢ñù¨}¬a…õf:4=ŽœÙåE{³ŒÞÝãâ}ÆÙ ž³˜*z^/±o«NGD÷¦ BÌ ¾˜Á!ýöø–y—G̈ù¤›Tcþð9ù‘³…:õS;3p„à~-Ô‡8ˆ› νɣ@ÜŒÎ/û¤w›”3Á/äŸû‚;.Øyü‡<à»ÞùŽ¿ùuó/ž°¿û±aëï^õ­{Oþ©ÇïÛ=úÜ'üÜÞûë“öí¬Ðf7˜ÿ}Àòæ5ÕùÌ~fŽ/‹{w»6)_&/²w_*ŸòÜ\|"iïÄlbz’un„ eœIŒ1WŸ.4¶awP}+lyôvÝ,=0>k]}óÂ&’ßwcP¾{ôgªò®²Ro±‡âȉó•æÍÙŸKÿi®¼710ÕôŠÎŸòÄœKÿ 8-gN=Ö;²mg3ç:óŽ™;œøœ~4ÄËTìØ%Í$î„g•ö’ÜA ÿ™Ðú>OMÔ÷PýcU6 Üí*u pëAZ°ôœ“Sc‚øÏÚ\Ò_-\v„¿ÐL úbàÛt<éB d%Þõ;ì¾b¸´ßÎ,ýì–¯±ÅÂ÷çašÖ¹p€%=Bô)“ßE u™œ¨Çž?}§Q÷“f–0õšN _Î95$ÎO¿žÔ‘‰1U³Ôû'ö2ê>Ua§äÊ#w[ø¯Ç›ÂJÇÆ‹ˆÜ%|«kå,×Õ·1^øváx9[îÙÐ4ÖÒ'*ÖD#¤tö®Ð'9uê9LƒjFšÉ1'ç¤'.‘øWsÅjÄaø£6å¸êxÁß‘=ÑÌrÇdÉ éy³ÿ¦Ö!Ü)Îõt͵É91ƒæ)4þ3XŒt BÇŒ3'”¾‘Uë?Ccƒ;\„¥Õà)æ.¿ã÷Ñâúù÷K{³ðÌŠË ³«ÁùûÙ94[TXWâJÊ_Ï{P«Ì÷S¼4_ïú†#®>n3æg¤Ç$?œ¹‡öÆï=é õD€Qé\z| 8·zÿ[ÞÿgóÇ?è“6í¹vÞ}ÿÍ§ßø›-Ï Û-||¹Ói nÚ‰6[yŒÆ¾›6ïu®àÐG MœiÐ|Wê`‰é»Å•.+Õˆ{~•øÿ<¶œ¾^??­77xàÔÑ8ÿðª„Pc«²'_t³æù¨žQâX7ì+ðC{~;?w¶Â#XµjîÞó¥tbኑý=ì÷,n ìï:é$žCçkYÄ×Å·%÷ÐzèKùþÝÙæ%û>¾–½ â$­Ôp ïQì ž0®ne^ÝÎnÇ[ t>õmèkÔ÷hÿ®~›Q5ÏùéÁsF¢¼M×Ó•šúJ3Þ‡{£ú§[‘¸^&|›V¯\CŸ:{lé3^W+>‰ÏUmÚï5ú$²?Ž€KóÔÿ^½,ÔŽ*kHL`ï§|>µÇÀTô|s¸vCêT¦¶”ßxå²9Sà‡œk_Wú×›ž[Î3Ä×iZ;©i%ÿ:£SOÖgªç <Ÿ…ôïCŒ`©QÔ¸ªÊ;'ŢРÑÍÛ vïçd/ë6¨ŽM¬H.‹F¼æ[ò¹èE¬«ÇN>"ó|üÔìù™Y¦í—GßGøo!fƱorlµèX·8×—á¬dþ¬{Q9;êXgô³ÔƈMcÆ ¹°0F­ÉËÅ;ˆ»jñ¸²Î“ã<Ò«&0_µŒXyxx 3ûÑÿPˆãˆ­ÐR‘¾ÿ»´ØÓPüÆ:¨Lï ¿¢öóZŸnô[4þîy?wúYz«“Ë¿VõD꬙ˆÛVºø³¶5¼:5?õHzá<à‡… ÌsžgH~¥k.ÌÿâõOÙ¶?ÿ‰ú¹­/ÚøŠíºûtÏ—_øÈMŸ±}ßGüä…W^{÷-{¶£gó‰;ÿ°yî)÷Ù__ÐþQ®¿ùÂk6÷í,©Îoq¼s¥MÌüÓË^ð‰¹´ÐÓ+.ÿúÞÞ|¥^;Ùçj×­K3©ùø+Y—·g$Æ[‡s`ë oœ˜¥aú‘Ÿ¨/|$Ö¤o)Ö0tB±áÂö¨%–æK7^©ÁÅž^7oþíÆÌ«e ¼.jç¨é;—.ÿ~á"=AÜC=Üi®R_Ξäà»h!‰ß ÞPé5ûF¼$ÿB}ƒ¾!ø¥å-k9 –¼š¼ïZÍd:#=p1ð+a¾sxØÂ çÄ~AÙnzfÊ ^2³Ýg¤,»HÎ฼e´TVªõSB»}2ê~ôhÆóž­ÍïœæîcÏñ“ÊÙ{Î~jˆîŨ˜$q‰!ú´–âApb|/˜|`§Çé_)CòŽ©ưæÁ§)Æ¡wF~çb%–ºÙ'‹Æçœˆ“‰…tÞdïr¦À^¥ž•Ãq„Ó$½.ÿNáìUçC=î¡'N‰­´³"œ´ê^|¡bèÔ¥?hi»ð_V]P:9{|ã æÓq‡Ä—eF•÷Ù/é­£–ö`kcë«>ÿ]êÌ]¾5…ý:–ú `çpˆÂÏ]³Œ¸0øáÝlJ_ fÇë÷ 5íuq^U›â"s–á§7,ļß{G|<‚³ÿà‰à ø{^»cøúãpX˜+Aþ ö¡ºÚÜøÔÏÔÏG­ÝϳÙå =ñȵS³HME¸‰ÑOrý¼ñÙÎÃJ·%q\Õ nììVh € QO·ÙË·t™˜ª¶^fϵ¥³KjFôÏØÃñw–Ùò~—ìlë˜×Èû;÷\Ï.Fg·qÅñ×בã)vžKƒÝ‰o0»­R¯Ým_!v ¿²VÞô-Ïß6›ôü­Ów|ýö3>üÃÙÏaÿnñ€ý÷—}ä–Íßû´­®^í8ÂÅ7þàR8ÍÒ¸4ðØwHlÐ× ¼lukÌeÆëGÍ þ²å@š ÞëóczìËßu|d? êlg÷<ëQÈÑ÷„N’ñ|4'CqóháŒèŠËWtO'rYt i3áÓÈŽÖe'§Åq§’çgëbïIÍGñQ!7&æ8!meòmùeòix¾þÌ™ |ìŠÖä\bÃv÷ÁŽÔ‡ïXå º~ÿ8ëpÇÄ—îæB­e/ËA›y¶FÜ6ëê—`fîg¨ã*OƧj®Çi¸ÍpÔ[z…¹J©­5·ÊÿêÜ=æÿÉOúþ£Ó8¨ïˆúœ%æÂ7å|qÿ{ró˜ñzrÎ]¼³›—ÈÜË?¤¬ÚQ_!{4sz1ë(Øaû^q¿ìl9ý°Ê£OÓKd{^ÐŽšVŒ¯3yGÃMs6¯ÇÂäÃÔˆÀ®ˆ•[/hh#Èvæ|ü |mQ 4É_¸îš}®ôÏ{µ÷·ó©ÞÆŒiÑR€gK>Û¸ìÑ«¢{öf³eáGÌtF0gÅÑØæ‹£Å<Õµô݆{ÿ‹žç1Îö±~N#ç1{ÛÕ‡´ÔêbðÈ•ÀmѦ@_ÜFñúHŽÑñŒÆAœ Ù´d|}è‰àlÛH£Û±}ù_ð+z¡9‹­TásÌ]šw¶—f¶ ŽF}jȺÐ|ý _ š^ÎVx‘æ5)V)ÒE«ZO·«†*§ö³§Y9uÐ\k‹˜MºR/3½Š©ˆ]'æ”(ŽÝeî‘ùnáÂ%ƒß‚³³ ÜeDKe·ÞþÜÎ/qœü{Ñw«ÎüƒUÓGG³H|ÓìcˆÏ^VÂ>SåÊHŽ=nZ)¡íFÜÆºVs2wÞ}›ó[îxÚçìÿõ+žµ/?~ê!tm³/¾ñ3÷VÒ.PŒvy‘ú|ÙóTêŸ ¦¦~¢;«<ét#=!}åäfg—ðÎð]âpp‡+}»ð5¨…a錃7OĆô¾7ÞÙs>+µ6òþA°vè¯çˆÜ `f}›óþâG¨O%úC„ÃûjŽ8ºà[ƒ½ë|ðzêzª—ï ãÃ{.ÐñÍeÏÃ}b-ËÐðhæÙŸ‚="æÿi·]q‰ãÎòÅ]lܸ㠃Š/|ýÜzµ;Œ}WqVר“û¯[¹kÆ`òÇ~öÁ¥t×sÎçºfÐp^´v3ήjïÔçÇ–žÏ^0prò!Ë4×Üót/TS,_6þQä°Â…Íœ?,б2v@Ÿ®Ðk2[Â^ëÝü{Ð\]ˆÃºÐLx*Š“ëBü?ê ò™‰ÓÛÞÛzš­–=ë·õ ¦7œÜG÷I1®çôG‘Ë$矴7Ñ$à¬ÇšÆ|ð+ÞAçÚÎâ2pÆœ öç?c}N~íKçÄ’CöpÕò[ñÊ*¹' Ù„JfŸG\}ü^ý¾úäzÍGâL0¼uÿS£‘^™EðãÔ“ÕæÍŠƒã¶Jš•ÚŽ­IÌ;ÌùrØuÅä]ý=4Æ”÷NäWƒ¸Eq§£7<›>çàõdz‚—Û9P®Ç¼R³¦c¦ƒÞÛÿžü½Ó~W­ýRœ)Ÿ‹©ÜÙ}–|ýλm¾ùùÄÿÖÅ£V|íý-pÖÕ{‚6»ô¤*ÜUrVp8#`™îßÁª”£,Ñ"ÔwS÷N-û®>î÷Ž8GñòìŽûþÔöÓîúøòÒßóÖï~è›·¿hçžÛßùÆ7\¸ø¥ßºý”ç[ïûÞ§lÙgœûÁ›ÆøÆ§ýìæ/>ú ûWî·õÈçýŸùóOKÍ£u¼Àø¦hçS~Õç÷ª7Å¿3úÈšÏR½¿W×'´K ›>+þ=úÕbæà í½£‚sWr–èï;ëM莅-ìò&Ç]Î:vw b¨ÌÙÑÃP›.Iãç Ò?ésFá«ê!ydëš‹±’6'X 8©}¾æ³Žâlêbî¥}~è»zí ­£*î³lä-òqgÁ@à¤&:ž¸nJÔaoÌû,Û? ÃòçÂçQ§&§µõéø`ð« 1Xy/qv«qïdý®:¸çD>µ*oU޳4Lã ;š —+q…ìé’Ü´›;ã9¥ïî÷ìΜE‹zßà}‘¯\GÊÞk°h×¥ì@Æ^ÔRC»æÙàÓ³Aü*åX…»I5wGçoËŠºßõKl x)=»aS6üž€¹,B vŽmíuZ°pÒí]ã¼EØßEÕ©O΃ìÂÃkññÙì)P®³¤gk3ÀBó]ýhKjÊ3˜Û‡Î÷®ÉšuÖòíÔõ¡&Ÿ¸5ë—›<Ã|3õ¸vv©ËŸÈùÁoà;ˆ9wVð=ù f‘LÊGª0 åQÁ‡;3¨f([K&¿¿ó›ðƒGø…ÊT{Œœ5Ö'qš‚‚ú‘÷Õßâ@ê«Â´ÜïIÿ9m g°Å“¢O>¾PÏ$û£:b•~ºûiëKcø<óÁ*ýú^°arQ£é|÷Øé0&Çy]ºÔáTë¨ÔhdËÕÓ²3±'CÖç6R#|=ô˜òüëNû™€ Hãs·Ëç—ŸÓ¯ 2rÍ“ø p6ßõíõäR+©`sÔd9k~Ãÿ¾÷"57À 7*µmz0Ö5¿°Ù6çûÀûôÞœNsÇïMÏqTÌ1¿$]eb½åù‰§ÚüÁ ÿþÉJ^² “8Ôn&0qr‹êO¤GJϲì?¬‚ý&¶"vW-ÛïüŽe»›Ìû½&ç±ÁÏ\u³æ˜Í28çî:qHÑïß§&o•Î8šÜœG1Ñ’úÏ®XÇ÷‘bу2Ã:rñÔ÷3UÉ©†n&ÅÍ©mÌœ†ðÔw‡¨}Ìé‡m1Õš0ïrÞÒ¾ói¦ÿWàñ_¾ÿïþ±}«Œ?tß­ÿõ»‹ý¯{ø›7ßsûšŸ>qì——¯øËÇ[ý²Ù€GÏ$;¶£ùxÁ/Ž|ÑuÂü|)FËÙYÒq¤n‘:QwÚPìûÿ³õ/`š¥Uy? ´å_žJ!‘n 0£àÖ¢öóVU‹rÁÓˆ!ÐF4£†Æñl¿`È0XâG@I+xà Hï~Þªæ`ÔÖlEáR¥’Q1"i ¨ˆ"´úÕZëþ­ç¾ëšk†îª÷Ýû9¬Ã½îu¯sœid{ÞWäC«°¹¼hoȇN³zó±5Øðeø¯Š¿©”à‰Þ¢;ü±oOTñR}þ—øÛÊ£”y:p‚éÛ+w¡Ö^ä§™:¡÷¶7ú ÿÕ›f•Ûdßÿóm‘sããޏ}® 0„"®‘ÿ7:¾p 膸 ¯¥ù2KAuÓ‚Í¢Ö†ÉÛtŽÝÞÚŸ‘·Ãß(Eß3¹ãâ8fýìNg¿ÄÚ´ù·Kéw)Çð?Wè÷<ú寂æ 1­A‰Ù:§RlSzçäZ`VCê®äìVÅÑÂï*¦tß ÿ—ú!3 ñÉÇà< ¡ïK¹9}Uÿ-›³Qº:cUìQàRßÀ^Ø;‚é… q 0ÄÈ‘yv0$0ñÀš#7Õš0˜A}ÔkuæÑŒ-Â~|áöÿ¨.LÍ•X<ŸgVoËRýBØ5ûfs`„üŽø¨;ݦSô¤ËŽŒŠ_‰FêªðÿˆMT³­1‹ÄïUêS†žbô£j#1—Û/×ãlÇר7EÞFý»Ã''8zçħÚÜ«©¯º©Þ¸Äªoº­Ck6â‘ÓÄj#øWð‘¯ÃçVôCèímx>õÉýŠ?°¿þ{ô¸/B5 úû'é·»ýkº+ÄÊ®o(º¢þ¢^2O8¤±^žSûþ¢ÇBL‡¯&îVîP•×(NùeêAйÁp°j<³ô¶¨ sfgõ_r÷ù3|ïÏ™ØÔ|M=_ÖwæÆ‘®Ñ{ñ{ô©—ûb¥~µ™s…÷ÁÍêGÄ&®ú×?uËößÿóø¯vñôrxã›ïvyçÓ³÷goºß៿쵇¦ûgßùˆÏ}éâÖ—~ìÁÿùð~Ê?í¼æÿÌöí@øÈ]òÛsxAQ[ßOͱ#étÀ a6·â+a…û•ž/óÁª)ßm³'ªçʇ_c-Á‰/W­oò܈ïXªŸoœ_¾ÏkøäHÿb­"^ö±’‡K #GEŸÁëÔдߕx™\Û¾Ÿþ³AüIqÖ`¬\ž¨{¸ÍI×jüÑC¾„¡ÎF¤ê+nÃæqnêë1Cî8‚AÏê+QÜåw<™šš=‹Ù4бì±ì|G¥OÞS\þPþc ¿’<‰oŽùï Ïžzóï=æ.ìþÆ_üâÎ=_öø½·ýÙ=÷ìÙòMOß}ü¿ýûÝ3¿sç·Ÿý ;ö9{ýw{ÿgØY¹÷[ß²~äûn?0Ì_õçµqk­¾¡3»Ð?.í©é[Ÿ€ÓÒÇÍ:vøqáïe3RU1HbŒwJCq–nœb6x4Üu÷9ÔL;ìw'ËVÍþNƒj…†oð|`Bƒt4¢æ|ØRj (R,ãñ˜Z3ð~'0{{Fé%·.$g?jÁAf¦šz,F°pò?ð^ü|äž Vbgb+|zÛš]QÑ&/#¥\kX©K Ã3j}Áù»U·.õL7ÿqh:.n‹àñŒà4œ-{&ÅOÔ%„E_Iœ{Vß1õÏA8"þ•î§ìð¥IüÚ*,,{gé÷‡ý¹¦Úùã¬=uó#Ár§Y<#ÅdÔô±n³À„•k'/Ny•cÊ`îQ¿Bû b¹Ø«8{ð¿õT Òj  •xFsfªÔA½¥Cë«Ø.œýÄúÞÄïÁÉÄÈ>Îgûù-Ö¯ª”ÜBí‰b‰èÙ×}ßîbì Þb³È[¯Rp»tö°Áä›6ȦzG4“ÌN¼þ©³C£0,Á™;*ì2ŸKû?ƒ›‚£âËéAPþ€þàvë‰9§:ÁµÄ¦×⳨ͅ&»r^ø­k¸)mþÒ~žkêòÂ$«æµúúk GrgÝÅÒìDä šIâï ON$vuSÚ ä¿Øpáòkl þ‚:”­¿é®Oi·ç]ÑW…ˆbäª^≩ϥt¦×p°ðßÄÈÊ¿Ý&&$L¸F½ "ú\ ß]†.¼¯ü<ørÌý#·£Ow©¾ ~O¾@˜Ahýƒùá3UÃòüI<­zÛ½þáÐðÝ›?úòw_~Äå wûóƒ“w?xÆÓ¾÷à5üõ‡ÿ¸ÿ~ šýÁ§žÿß'¿ë=Mv¾ßßÿµŽ¹®·k9<,÷¿ß1«9>˜œ^¾q"—°ÈO$9Ó9÷…ô…‚Q‰ûBe!Þu/ÅÁÝT,¦ów34Ö”óf|±ŒùPØ´_ÁÓ°ôÔTrÜejNE¬»ÔŒN;ăúaé!G°}G¿Åì°zÈ yðš#^B?ðÐô"F­Õ.®õÿ†¥Ø:Oe™Zרی=n¨k…ý¤6 ¦f&N ~Kœýè÷k¢O0uÓ75#@ñ›ÿläo×ÈëKÔb¦eœã ´¤eã¯ðq£ÿÄ{ç×ø¸àÜä:sî?ŹówRßZ…Sew½pYü£ìL¥~H¿n—WdNv+ ]ïxgó£ßN¶Fº/—ˆ÷œ?+íúõݼÝAõ¶ç[nç°o`^Òt*+Ü@aɵµS,uñ†EMŠNûè\Cò¶epU+½ÝËìGÚÈsv}@hˆRkµ/k4~íó»{N…â¢sêq;µèöu7«RÏROóJíp*øPšM³44§HÏ1¹sàýס–\aa­cø†c±®:;…8¯‹™™¡šÜeÕê,].ü;q¾ùˆÀ£6£s3ñŒç¥£\½TÉ˰ÄúÃÍ1û‡º«püä:ªÖ0*'£Ÿ|R{iƒúÁ(œ$±=87-/;'›±}güAü¢º#¸dÎ"ý´÷~þ¡ð‰ƒüôõ6÷çð~ýºÝuyÛ¡úÿÍœìÕÚjO}Ìùýßü‘΃úÜ.سá_ÄÁÑ|ÞÖi?Çü³×·½t{#seb'Öí¼Ï½‹rªk#±ï‘æXÈ/.XcûâLåÊ%4"/Ñîµeûo;h2õý5Ä5â¦ÓŽØájpì«pQv#µoäÇÁ·Üþ[Leq!<çV›»$ÝéÝnc[¸Q-Ñ[¸I¹Ü=Å0Çå®6‡œ3fKß*ó}«¼Ô3èE°ßiv2pöÌ>—>ZìþQèyÖ¥øKšÃUˆ%Z?ÀùµM¾¿P´? Ž é®y/ ýî²Ímn-xôò®=ôÔ ØÜzÍ$¦.ÊaK胺¾587FÿöF_#ØÖú]µü/þ;ê#ªu¥MS¥O_çGgÈÏ‘ÛUéøÞ…CÓÖ#¹]µ ý` ⌜C=ƒŸ;å’ý³“Ïa“Ê,=妲ÃÑ//B¡6BœMï†lo—¥w|>ÓRüÕ!ˆo…éo¥=ÕÝ/à⊠dÿoO;u³VR«,òÄ ZûÈSmMÛü®s²_Çù÷³´Y"¼» þTÀ¤ØGá9p¾ Ø÷ŽD‡ÿTzä{„ûÖp® ŠÑé·:Ç|îcêbû''ÎyFsìsÄãI_¡˜¯(¿Go¦eOî…ä‚vgt- æ+N¯rÖ×ø€.¦I‹zž×ÊÉÑ6›Zü”ýå+p 8ÚÊ!'Ö]qâ¤yÄ?Säß§Á|t§¯&ÿ^ê¼³ú]Àg'nGbG¾C¹5¿v®õŠâ»hÑœ¨ð ©‘tõäm8nŽzÝ6š¨Šk|=ì3¿ü>¿íµ¨Ÿ»ûÚj1†ë/ö„'<ó!ïÜ/|:í 8§Þù<Í4»(~BÓý†«_JëÇÞ­Äq¬ýì´aϧêñ?³ÚàbTöN~y;|päNØOi êÌžR_|äˆÄKâ׎ӾÒYq|Z}#äÔ¬ÖÔ5•Ë`/'ü¢øÏ^—O_Å^T8¡zîµâ5âÙU‡-NQk?—1!X­æ ;V(¹Þy?ë§Z÷eÝÌì27/8_~o7]ôbƤCô8¡ÃãO#ÿ½h$¦ žhï·^<_8@a#ûù qo©—¡«8z„‹=4ÍœJNNîCÎŽ”ÎoâNÒ†GYgõȪî {b^–l[bFsô2¬uŸ³ŽÝÝK×'~§3x¬ø&¥åìñÞp­U×¢—eâÝæÐƒMÝ;òÌAýƒôË9?ØZjŸosj±6ó5ß*®ÁÍ>US`¯è3éç³0ÿA¶öìZ¸Wö7Ê'ÜÝ¥éŒæ¿Iw޼Y\àä«W,SØÏq[úÒ.gшTÿVö¤ƒ/*·s3¡„Y¦>‹ú# 6v™zg9GœÚ¬Ÿå~ª+ž“M =šà„?áïqI™ugÏIüÝ´G߬jsÎ~Ì9»êC»£2o@ýFÂå¢ÝUþçT¯¸‡³,³7Ããöß¿¬kM½žŒ’ý;5kU1Ý8HJ1“ꚯ#—˜ÀõŽ|nÆóÌÖƒ ÈÏàC9÷Ki¿¶œ xËÔÔœ¹Zh êƒ$îÞtÝäàqck-Öoõ’[2þëRoˆŸÎå ùä@Ä“äÛ³´Gt&éÜ&–¼¢óš÷Š­Ô??Hoá‰ÿ÷ìs·ÞÿÙ{—~öW÷îýÐý½w<à+÷Þñù?zxù_³wù;ž·kwàO÷ÿ|çO޲ܵ¹Àß÷Eÿ¸sòÌ?ü€¯ñù–Ú;¼ó¦7­mvÔàZ< Ó 4)LÐëÓÑwÑ4”í} 'Š58öóm¶Mwú!:ÜÓý‡åÑóz¡pnˆ9ñÿô[P· ló31§à&ì+÷öZ‚ÇwÒö¶ÙÛÌTSLzßèþ%tY£SÀÎí÷ÁƒC_:fëÞ§.=à•ƒjFªÕî°°ÄwЛBÛ@xP!Ç·\Xó*T³ìcSšÃv–á&3Sµ¿Kä’ò á{·aË"<›œiù1r9ÿû}q ¨å¬ÈsÅýqŽ1y˜|` |æ&fVz­È]moCs9æÀ¼Û‚Ž·ô1“V>ƒïL 'åT•ØIµ½œ)ƒí#wXFO}a¡\3µ$t®¸“Ì]OP{¥F|˜ˆÿ•+ÁµóX3ôÒƒ×5ÜÜæª»ÁÇ·»nEŸ}ÐÂj ± v?CŒ¥\®w;ªçÏš¤xóc`BW™ÛYÀžáèžf/ƒâ”JAœbk8¨”­žÕm•Í'T7k¶Ï³ú±°öwZvsÅïK¿=§î¹XÔ¥é¥_’|‘œº‹mGá•\§«»ú]#—¤ßÕ®FðXaò§Ñ(üŸ¶¢-yxó²3²¡É ó1éùR‹÷H=uúÜmâ³9¸(Ùóî{צ˯“!Î%BSýܬë³Üÿ0çDwinŽ.´æwNò¿êk{.ð"ÞÁîþµ“õÀÍÉKÌnoJÓ‰^òaûÿªYû{W¿Ùq‚yõ28¥Ê›‚Ç$Ìxj}º§R'|VOÒõÖù&û¢Øï5¹îž»O§¦"‰ ž"5¤YºÁü¾zmËVaàäfì0°Åwÿâ÷ÚßÿßÇýçÃïÿÍÃçüáC´û·V˯¿ó[‡_}Ë£­ÿo¼zp÷ƒ>çÛþè)ï^Û, {—“};h¶÷¨ •îõwøôoÖÌ(é,•Èá¨ýœ£.‡ˆ<—؇<Óï-½TŠ¡ê¬¾zÅ´ðŒüÜ_kß ösS}ËꫜÀ²ñûµ‹.î7GM¼¬[ÐrŸd7àésáð®Á#•û†ÓEìÇÏbC[í>8ÉàŸââ¬t_GÙãB.×,TõŠ´ÃÍ̸$;|5ëóäbøëMiÈgeÜC.oÏÇrÅßé°ñꉂÛ=I+¾$ZM:ÏÑ+=¨Ö%Ží‘ózOŽð ÷åG1âpNÁÏ„ LôÀÛ„Q//A?Ó»ÐßOð±epƒ >9ø§ÖnFÌáµ#´k°sÄAê­«hKÎâûëÝVp7ºø~ÈŒ³ˆU‰ëÕÀ9È~zñhŠlÞ‚^A<åiþ½ÄóŠÍ²7œ>¬¹ñä¶gÕ‚ùyêB²#ëJŸœÅ!µœc~aWßÌqÙFbšüTx^rÓˆ)„{,C-‘¸\`­pbØô_ò5~>áL€›Ó§B^ÞÇœL÷¤`ól­ÎþÒ—Íæ¾è©/?üáo/ÿŸÿžÃoûÓÿ~þUÏ>ü‘øëƒ“õ[¿ýý_u`Ü@Ó <äBý³7ÝÏ4½Ç¬r¸9ç‚»ÿÑŒŸµò0>´Ejhœæ ´Š¤ÙØ]`Èp ¨RËõ»š1)ö¿ÅnÁ]ÔÛ­z×Â~ª5έW2sb0 4&ÈwǧÏÒŸs(tæýܙdžR#ïîª~û˜%µùƒõ‘æ¡j-ÑWñû:Å¡Z®á¨¥ Ÿ_ 73+v?c |ãœÚPô¼Óç^±Eƒxá²knOé«luáÄíFî€þâ«-¾ùÀmö5zïoÏ\áºW½h N.,»tqŒ¯9}¦ô*Ö¨ân”ë5·gP¤îÆ>'uPê™g\c"¸ôä+:oegÃz<¨áÂsÚR` pÁÁ€‰½—ŸšZ1L§«S¨¯‰oö Ù°¾«ä¯~ŽÁЖêymyÿ†púÀyíûÅû‚]èo  n_šØ,nÕÝô?ø"ð tžýì‹€U|DŽé± y³r_k;Cô" c°°œ®³´C"þ·³¥ù€äþ úƒ°…Ý,H¿Wpèfé|)6£Xɱ[m#ì°jc¬áHoFW¦¶ægˆþb8?óÇLolÜiÈ©ÇÀö½«É ?ÈdŠ%4_¹£gÚ,ñ‰ÏR'–³múÁfq”ìyÕ›1rŸô½ØªÂŒ@ÙT¿ßÔ.Å+ —§(¿$ÿó‘>ò18TŒùìy®åÿ u‚ß»×{wìg¬—ïã^°Øý¡Ç>jÇð•“s»¸aü‘Û»Sù±í_0MU·³¡±±µh¶ÿ\æ·Ôð•ÇŒºÌ!ôóK?æ Ì“¾Ýmq °ihKžó©}Û~vþ ~¦Åº úX©½J×rÒù…ãû!aÅW`Ä(+Åþµ­upɇ„!f?“öý¬è¥»’gZüQÌÜ!&±éÒ2ðº1côäE¿…âr¿ßÌεu°wž<’ š½c®¼ü.6^ õ‹?¾åw—˜\{ãöIšTÚ/¸×Ç}úB³¸Å-Šõî¯Q ãë¯\29Càá•ïŠ È…—lêú²1•\O9ÄÔföœƒ·µRœWÚ9gÎꢮ¡ýKMRºÖÇSãéÄ\4¢íçÕ;–š:Â}RSÆ~ÌS8ÿ;-Û6jæ¤Ûxæj5;»¿mçyéYÑ3¡J`”ÁQߌÞÅÚêô‘ûu<._W°Xa¡És_M<‡¼pp}‹Së®^=ûµáÇ™QÇB‡;¤™[U±+¾Ø?Cýè+Nà·øO;#ôháÃÏkvöðHs±úxE1‚¯cÇíÓ™wíÈõ,N!±˜}&y©4½¨•%/OµÃÊÜRz#µÂw/V΃ò;¸#zü+…–ÖíU:Ägëž/hk.È´ôÒê.ŒÔ½–â£/¦32ò=䯖‹P;“‚CUt×ÊÜo[m޽œÕÓlk½5–s?Y6e?ígƒ9ÇÒAuÆö¹i%ìÓû=Röÿ™e!ßiDï‡pÿJ, ׄ\*üQÌ¿ ¶¼™3¿Bû 0@f:*Þƒâþ^|œÔ[’O½Šë«î\‘‰\^ ~‡>Ï¥ú±ÅÉ«pŒºÚï@Ô‚/„†”4‰<ÖþœÛ?ú¬½ã«ÞñÕ{ßøc»÷ G=áò§½èèðǶ¼ó/ßøm—7ÞýÓ»ö^?pí£w?øÞì¾æQ÷8üšwÿ¨Õzé'¾çs>èñÎÉ3¬M+Ðì%ñÕK?ùm Õð<·¼ôGÏ\ÀÙÇ×wüݬ5QeüÐj÷þ^ÚCÝÅ;2'$å.SS¢¿°´ï6£Rù*XÕØÎypńӥFˆ¸ðҵ˹nà%).¥.8Á-R0Ï£êÑ`Ãe>Ÿ&_Q° ­7Uù™Ÿ=Õk·ÉU•kd^Û×H†˜Ï ^? ƒ£N2Iß#±7~ÔÖ×| x²üštn³é©­Éç_¶yY£0›Ä‚kpÜg¼9gY¦>:­øƒúÍÉÁ¢.u o9õᘠΌ!ë%Ñ+¯z³–°-£x´•‡ý&üŽ<®’¯H7ŽçsûòÙ?óS‡ö_ð3‡ß÷EÿxhxÀôß³sâïwN½óy»O?½c\€r󕇼ñ»ìÀ8sê›.†4ÛVö€§Ë—Ô¡Ó7Ó^VáEð0Àè#¹XiF <üAÚZÊýµ^Ç~¾ð Þžó¢¶p•»‘9<´›†ÔjŠš“¾sì¹,·IWS}¡…ž¥¨§ø9ð '\»B|» –TÞO|S”·,ˆõ‰Èiéó…ûK ŒXn Ú-Øòx@ÔSÔ[á\.ͪ þº î«<§0³<n1q~àˆ®«¾P>‡Ï97ì1{°éý§Á«e©áDžj àôÊ!L}~§ÔßÃY™ˆÑUË\ íJΨ󷖯,ïÕæ™¡£O=Sý)âîžUóÊ„þ&0ÊY=ëößÔk„Ñ1ÿr›™È¶&¸\Ô¹ÐCnn3éÿowïX½ÀטYwlç?uYT·¿’6Îl“l"õûŒ±m_á{⇻µQu‘̧°³p^ÉçµÏyÿ£_*ç"fÿ'5[÷8KØË j9þ3ׇ^2|9ÿ.j‹KõêŸmý­ÜJŒx¤Hг?:—$ñ™>_oXwèZÁ_ÔþnŸQo‘ê·•š=]Ý{lµúÐ.…2DM8óbMî¿°"j6ôÅ6ëÖƒ¾^xbQžàwMRwÃvGn:,ÄÛÔXàþ·Ü#ð{âùeêN‡æµr÷\ œ[ç1{íµE9uß—šúP-L}± û'LŸ:}• שĄØTåù…>!øì‘0³>Cl@¯:õMŬ²ã¿’GP/š; gÅ:ðCFÕpTƒ ÎZ®âåšÊÖøCsÓ?X êŠXîyÔ®Us¶þüç¹ö¿‹|ü•=ïÅyÈ¿Þû¬Õz¯þ«—þÍìð÷¾ù>»û„ß?¼øž[vgçyýöÎ¥ï|Êî_¼þ)'ùÐÕÞó9ß}ðî½Ï3}`Ë‘Ö?ÿsÐðuŸ«°à™_y³tÆ¥i¥üõ}ª…:]‡?r—ÈKȉ»™5Ñç=HW—xL}°ôß›À{á}Ñ“NŒmç%4†nÄnŽ`¢ögë¯ÎùTäö>p–ÔÛ´T»Ö}Á'­úZIÔc΃j¹%ìÔçáQrf«ÓÇ5‚³ÎÙg¶ás˜éËÕ©ØxûǰJõ Þ%‡èëÁà ÄšËïgNîqbL:ëœ@µÿÒÅE{S7»ùnôÆcuµ¸r‰£PËÆ&+×ÔßÇ]†‡4«ÞÏýmØüµì)'ÆÆ–¨N%¤ûG?—påÕ©¿±g=OnÈù6 r‡®O¹ ×V¾8£¸^ô„û}×þ’ƒeŸlÌD<çü10Sí­ÛyÃf”/û’›µ>ºˆÑ„ípëéYÈG0xöYÚ Ì3Ø‹Axƒ4 K‹{®º¯ Œýhœûå­äDÌJ+Ê#ȱø{?ë]¼ºšÅ?æó7£lT¡ÇdœóÆû 63¥ì½b®iè@âë„Ñù:Ó»Âüü#øÖ?uù»úm‰ý¦EÇIŒŒwF/X>k×£ã"·WÎã24Ÿ¤Ëåµ}ÍìÉž…YT?lÕÖï³o³¡×äÇpÇé}îëÙ5ue°V›9‡Åb©:;ñÝÜñ£î~ŦŽùRƒèžmÑa¥éKnÁq`Í…ºÍ•fÊqj•“o)ŽBÇÛï§î¼‹•ì[ö!P;Ò9ñØ‘º-Øœ­yæ²Uw©-’Ÿ°öÉÏ‹dß4èÃHTƒˆç†c ç}æHÍ =Õ³¨íùÙ”6X}Åk¾áÐöðƒÏù¶Ãƒ¿?<|ï+¾xǰ›»`XÀÙ_ýösù¦¿öù?MãÀg-àÑr‡ˆµçòÑÛGWØñ¤:5y˜ûÕ²s U»¹¿:¿ÞfmÒp%} \åÀÉo§6$nþ"bÆÀ{„!Ðdõ'Ëe ºhÄÆªõ—¸¬;˜^ãt„‚» Þ#žšêŽáOè‡ØÔœcðüYº¦â¥dߤÖo~|÷K™ß5õ ÏM~ßÿ^¿ãkdïÛz…¶„cˆh`¼Y3V°}žú•W˜óãvY~dЏüÉÙ÷=ˆ;J|gÏúeèf%¶¹ÒÚòÙ“r+p°Íx÷R¬ô2]ßÍ—j>ØuT‹rO·ÍŠUGøÿKé/ÛÞ IIüMÎ¥|º MLÎ@ýAqäB˜8æÁ“ ¦¦({¿Ž3}£4×"·•IÅ\ŸsgÚ Ûky]³ÌÒ`UŒƒ†Éž ÷Që…~Væ<ª³òCÖ‚{Oþ6¨ïLNSvÜmÔâOÄp'ÏHÓ•^æ!êB>«A1:šŽÌÌ͆Ùïk~ú`Þ›b÷Ç|&<òш‡œ·ÁܬG/Ö=ê±É ÁnÀY§‘m©ð¨5ÀŸ€«Mî*\t’o÷$° aƒµç9ñhÚói}™¥1£XÑöØù|þÇŠÄÒ©Ä9‘“_{;•/ùݳŸ§·CqüöQꪇŽTÉ‚ 1‹kÀÙd>–=Û¯ýâ­k¸¨Ô$¨ï¨ÌïǬVaBÄBÖ3@­]¹'3(áTÕ[ÐLJ~–æ÷æ¬.Ù»Ì7cœ8.äÖÊ™‰àuC°Bé:ŸK> uª»öó©˜D9ÿ1w:m öCyÖ6|›¡õÍNøê¹ÊÇ'½ƒt®C?.sö u\ƒzߥ<£ 73[.}à ›o¡‹”¶€XÌ%¸h+ñîMjÑUu2lQQ –µ±MŸñˆVÞ9p8Ÿ+­uMœ™õWZÞÏüƒØ~H|ÖßS5Á;2.ŠÁol³!àEGÌyêrp2Í—ÀK§Â¿›ü+üå©þD?ã{×ñïáØ)Ö¹â®+qÅûý›;5Wƒ˜í|Î*Œ\Ûí!ë7ª‰)ÿ+}ÝŸ¼O6Å?úw«çÓà1´÷¿ëÌûïŠKPéCäùæà¹-4ûA?"³ÄÍn*³ûáñ X‹½‹æ™¹ï¿SU­Þžõ*lÕ¶,çzN],ƒ $®+pïà@ƒA’c˜Ÿ—VþDþÌZÎÁ•¯ÌÊc;W¡¯DX3íý9b‚·†9Ïœ«õdïKǶp–eG+“FxÇâ•E`HÞrhª²Nôû!Þ-›©÷²±Püµ gSÜBoéB}!à¯h˜øßÛw†¹ØãŸ‰Pß±ß!æ¤ÎJ¼H¿Ž4Ps®*ú'1°áûåùÏ}Òîþ/îêþ,¬ÞoÚ€ô”w»O±?øC_³cßiçÁ>Cv±ÊŽfÛûµŸ']àMDí´éØú™TÏs3ýóøï¾v<¨WX{¬Ïqn‡¯‡¸”Egp \þ†…8_Ô W`ù¶ö p"Ù#í©ïCô<\¨à)ŠËŠx4páÇA½‚-¦>. GŽž^û{pmý>3GÕP‰÷'8²œã©_£™Š'?_†FJQ~þ—¾f3´¨ ü¹¨™Ý¸oÆã°]¯Ë™»ŠËéqÏ:ü‹žÃg1f®ZŒ§øjj\%â‹ÐYÔyÈü:ÌÐú3®%?TW9MðG¸Š/QÞ{­¯„m¾*L%úáç©*–Hþ™ò…©Ù›àÉÛZ‹ Uɧá»;‚ÓŠw^ÀWä{’›§:1~%çóuù)â‡V9IÁ¿ã××~ô ú“úT NòNòal8nrÂu7ë,]=øh­N›½ê©… ¦X±ãaý¬Ïœ!~“Ûlrfx‘¾Ÿ8¬j†‘ãºöo4ÄaÄšs° Ûø,¡½ôÇ–‡Ð¯\qz˜À»û8L±õJ}èR/p,^#¹‡t*’+£sVá¨Ã ¡M¹˜¥å¡\1ã¼!0øÌ%É•7;múŽŸ4ÒÓ:§f쥳~“•ZDh (~ÙWųâÔ9ÖŒþT%vÓy,Ä%äI`kÄ÷ÔOíÿkVìDï;8êyiØ1+®q¸˜_uD;?C«%®º}˲üÆJ瑼tA<ÓåEô;ÙyYt\ìJlÀžÞFÆ>›ê«b ˜yNŽa¿¯³Y„{q‡î’ŸâhPßP|! ŽKp-+y]Ü…Ô¬ò³¬gò{"L]¼óð§¯Ûýº=û®¿zûÁîOÝWì}Ê£Ÿ~yçÛÿËå‡ýÊ'ï=ð†Ÿ»üe—>z×îéßó;¯ø›Úyõ«îyøá+>üÜ—ÿüÎç}ÿÃß½÷+žÇÚ}û–×þ¬ÍüóÔfÿœ¹XUä4kÔ-nXÃ)›[OÈ„}ÔýHý1âyÊÑ„—_)pgă÷µ"Zj¶Þ þ,ùâ¿SØ4pH}ù†¿cÄ7¬¥&ük‹žx/³÷·ê±lö,g>f?äÜt‹¦˜‘œ8”ìjohòmo¦váé÷P¸ùÔá#þ=ÔOõ·q™ï'|n¥XŽ>’ zÈ6¸Ó‘´í¥Óž1†â,õ‹_“~ÌÕä_R,ܱ£œ©3§(3B® W»nž3R A¾ÕÏ·jí9§ˆ<þuzJÁéÁqÅéutyŸb5{7³'æÿ©Ÿ.¥ßªœµRkîçx‘ý±›pÛ8±Ô üØò!Þch½ ÔÍ ±þ¦ãË ï ¿Š}¥î¥w+ƒjØaÖˆž=ÅpÉQhèWõÿ¡QÄE,hì€ý +÷¼§õ$7½ro„ù»/a=u–ý{éÓá;fÍ€/lßoÏ«{6gÅCÄÿøNòЉÉ¥z—… wìFöõ¼ÙÍœq)k2 5NÝïIAj SwÇ$ó@òSj"hyâ#À ћǎΡ±Eý¡ÌªýR[Ö»‚³%^ ž¶lš…œjP­SñHmüް¹âÎø¨ÞT†›s.Nêú+~&¦Êz4qLÌWf?sV÷¨{;~éþ‡­—oúéƒ;<ùìû_ºÏîÛþðÌîÞðöƒ¯|ÙÿÜýòûü¶cö¦cf½~/>óüÅ3žö!³wë—=ë›v¸—ƒ°×Nëw¬a·¶Tƒ¸–=ì?u1€4_£WÍ~í%8ìsÌ_ƒ³yŽ8i{6󵓟ÁÌËI~‡0s@&Ö¦åYÞÓ¾¦ÿ]¥Æ¹ Ì»ùÎ+ù®ƒjªäpG»çÁŒŽBgº¢׸û#üF¸ßäkÒÍöš(5B´Pd÷·TÍe’í­ÔU„gãטwÃ3éNÈ¿ž®ÄÕ`}ª,¢ßu‹š±âÓ­5ñ‚å]­f½Š¿Fpnf °/ìÃÜôñÒã{EÆ>ƒæ¥»l†æ-9Jåù…u8ž§ûNßÕÔ8ˆÌîDK;jꉞˆeµ÷ð &ñ@2fPþ¸àûè kwøxTïd·®GŽgîbÖd¿üçц3ËïÑ-šC¿~-üÃÏ.wÄþŽùêà÷ó ÏUz•»—¾ÿÅþ[ùšûî!új5ËâZ~†úéÑÒVÏù•^ e¢~ÇÙåâïgo<¹ñ¡Þ‚žÑÄBÐd¤f¸lº^ºë×Är\t„c§ˆkKu\v?'ØÎ:91âá*íÞ*ü¢«_ÏùQÂò][]/µŸü¼7Â>ú‡ÓRua­‹¯¥òqåO¯ãü_Íš¸îK%NÖúÖA}•ÂbÜÁgçùáÃ*+šÉuH|¯t<×kÞøR³h¹s†5ÒÿÖ°øKøË§Xߥ˜îŽ‚ý›ÅO=ÓÍŸØ ÞLþ°bOÀWüï¦tÝõ¬£üw ͤ¬aÑÛ ë¸gE÷_º¬Qû`¦ëü07æÄem¹åèÑÏ6«ßš¥Þ=ÓuèiÇÌlêÔÄiÐólL-_ ÌfÐÌIìæïo}óÞ¯ýâ­‹Å+/ïþë¯ÿÚ=óñÊ;Oò¯õú~ºï®ÅøÆýßü¤Ý–]·ý™ž˜1_ÔÝßOéG¯äÏÝ¡UiïÐô©/¹¦ƒ}6y\ëd|Õ9zÄöàg‘óžöÀptõx:W¨õñúlΘm›y±€·ÂÓ&ìøºhýeÝŒ¼xPªê¤ÙwÏ=ÐÞÕ+Ô xi¤gm{pìÍ6'§ Sj÷ ý [_­ÿÐ\`8§†¯EÝáöö?K§úN鉳¶Mi¸™¹—’Ï=‹‡Gn¾ñ©÷³A} îEÏÅ#®¥vÇZʸ\ôÁ#§÷šÄ̦§‚ZÇ2ô?¨ LCêÑm,C Íb´~“Mè¿·ƒ€ J Äßû¯x —Þû•ž\½pNÁœ_&od–&Îyi5Ñ¿¬xn»Ë-ŠêWEšƒ•ûGl`õOÖ.eÄ7P;Ìžæ;¥÷gŸÛ8›©8öµ­!zŠòlúV«ü”ßûl‹u„•Yó¶äßÖp€°³è ¨¿tÍúœ—&yò18]nW§Í=!w¥vŸ ì_:Z…ü\þ¢ªîè÷ ®oÛÉM̃…Ÿp]ºBÏ=Ü·I_‹z%¼$æ”Ìšƒ’6ÁÔq1;-¤à-É®'OBç°Îê¡65Ø«ÙK‡O1ûñdø)a[丞µújpظ_à™›m&ÜÏì9ànJ³ ¬Û FJßx+5+Ö’: ¾œüó¹ï6âóÆ)L6çVàìšZá‹j6«¿¿ý½z Šì»ýœã×–›Ùˆú ºØ® ævQZ@¸%9‘j´^(8SìCä1×vÄ~¢é5wzõöç`7ÂY°ûUØÌ_vgëðâ3÷ñù¿O}ÕÆ¡õà•¯­þë“þØñ˽G®Oüÿø;ü‚.ÿJý žxÑ~›D]ÇÖHxÏ8Ü¥÷èu©QëfRိ(vu[|§æÔ+w/7âÑÛ¤¹]ª×˜š³ðÜ¿ÊYî4’ƒjm xxr}ð¾•ò€ÔÌߤޫ¸M} éƒÒ¿ 7^’<+aωy·:ô~§ót ¹!ºŠšñZ*vþ‰wqšÀ×ìÏ­†ÃgÌMƒpâž¿:ðÊÈUewàiŒÑû”}ÇEö»h?4S/æËPw }pîî“©qg¦Èe·ßŸ¥ù€ýG `–®-þ«¯ÅI/«Š"NÛ%fF'¸ÿT]±ðôJêÌ2·n…¤±ÒãóŠ%`ðçî ÍFñhâï¹—ƒt4‚óƒ8h¡MúD1ŸÝÖ‰9™ðH4›¸ãcŸoN-ÍÆðg%¾SŽ8‹ƒM§æà2³z)½@r[Å-ŠGÝ¿¨öt Ì‚º‡ÿŒæoŒô õýf·ðupsáåá߃+FΈ”нrl-9ؾ#€Ï–:aÍ?&N•:aÜ •œµ[Rÿž:xÞ,ÝZòUî½xœó¡ãxâ}ƒÒ˱5FW>28Ÿ)ÉŒ9øÇSÙ=X:5qâwptmTCR}q‹³áù3¯9GËäæ4]ï;C ÎMbÕåó5ð•×%ïC÷ÄßlÙΟùIÕRÁ>ü½à^ŸÒþ¼étEü€v†ì,±f2á©‹Øý5üN(üMáô~ŽC{=lvŸú‘bZîéDÝPñս酚¯ìó_ Ž*ÎÊDÝs©Z:=qÊ ƒpâÜ9¸Þkúšd[ˆü9¾õQ¯Ú³ù„³÷ovïÿøoÜ;‰ö~ý /ãÿ§½?Ùçïžÿú¯rNàx×âO?pÏÝ_Яíüå'?öа›¿uòŠaªÅ“²™ Õ±<ÿÆnÓdû`6 ½:ÙA´À§¨‹Kw;glŒàKòWëÀ ó,Û¶ý .ì1ý>ãWy‰÷ýÄÏ á°?keGÒÕD'Òþ,´ÍnTïë¬ÀOå“Ñöqýw´…˜Àìà8·7¬áSÉæzÎ$ŒÔöi®¶äß§š'~µÈ?ø¼ù&v^qÆ„Mä5?q)÷G¼#¯›rGå³'ÅD``•šñ-qà3àäK×O¼Cµô⣠ÍqêLü£ž×…/qôôœîx±õ ¬`銧Å-ò~û‘xWþ¬€õÃõìû¬ào€ÇÙ¿éqû™;Iý{Ýê§—ð©­¾Ùú<®°Ï‰»…~Nð¸9{ö真øÎ}x÷îCâ'³³‡ ½²« Ŷk|1Âà5ÇÐv$F'ßš"Öwý|ü0Ø^•ݯ§fœÜÙ  {6¤^ø~“"ñ~°tûs¸ÔÊsÞvà ·%n%lÈÏ•ž7{ ÏŠïµ?©–HŒEÿqê‹’?ˆ§‘œ~úÎô~6ÀôÞ³¿bf‹âËÆCîÁÙ‰¹—7|’Îê4‹ókçl]Ú=„ïºSú|›]Ï;u5YÍ=¾P„®¨iˆ_P©ÿ+&a¯&å íÁù…̇¿þç\ ­wUìG¤µŒ™œ` 2bæxr‘çÖóSàIOvØ9ñGÑsf®B ocÙqX±9ç&NJ}fáEÔ÷ò ¨ÖVÉ%—Ò)äœëçsÿkLäÔšZ\Àl»¦A=·þ¥Iö›|ÚíºÁsö—…†”­?ë'¿3ŠS§:tÌ5·ÿÕ³¾îÐÞñÄßþò•K‡/zÙë^²ü·»GÿþÎÃ'o¾`çd<æÿð³?áàÄ'<öþ϶žÙ{¼ë[ŽÄß§%žšxJ[Òä‹þìR-€“ª^$òÔA}º·…¶!8Ϥ;8‚YÚwØ}E§ƒÚý#âyrðBí™ßIò.þ¿jäU8ª?Ë]y5Á!¿ßR~CWGWì[Ÿ¢&4À¾gïg>«¼èZòª»>6¿7º_%Ž´c Ôa× ŽdÔÍœ“_áF€mß=ìSè— Ÿ÷» 6¦Ñq…VÄ”òõã,ÝáAYã$U1¡lJûOk[TWGo¶Ò®ü1æâÜÖ´¯ýüÑyõUõ#_ÉühQ‹eÑûût&ç“„Ž;Ø)˜(–¸¿þ9æ‹»~Hzº=ç¶ý±úT§¹šÜéֺϣçzHNãÿ•³Hî5tZVºK9{™˜C÷~´?K—ƒi&Òx­Aù!z;+øÓ¬~1ûox_ì'œ0xô6)¶ÀϮȯÔk _xÓš¼Px¼ŸªœJ½üûèyNÜKxAÍw_Ó9óy©o1WÏÎÜÜE¹´cGš}ÿˆ7‘Çn›Ÿ7[s+‡izÌDZ~–àëú9€÷5‹WÇ9šÕK .g¥µ˜ˆbMÎ.:¾[öÁàs„_ûç3§aþwO1óR yÚ(p9©UÊoeL ¾­¼`D×OlÐâÏýÖâ6,š]Œ˜V±´ø1G¦ÃA¨]Wí§úDÎ%ÏW¹ƒ¿+5[Õʬ¹ -î<ö¾ôú#wˆE>xïì>TÚd9K3Ÿ]š+ØàÄ}g¯É…†Läzhkm€½¸.‘bʼ£pÓ–ÒMR½[¼G—ÇøhÂbSsôd—RØÞƒù%â~9.¥ú·?ïý·sö-|ÙÎ}¯|Éå÷ÝúÃ{?w÷õÞ½îûˆ×=æßîî=æY?¹÷âoý˜=;Ÿ·¿äïÞúé{»_{ß[wöÑ:ü“·üã΃×Ü=ù×:‰ Æ>â9òÌ¡«7*¦÷sÖ±J.Üáô™ŒôLƒ»Ë6«Þ±¤4 ˆïÐjO|ÅÎ_è»môgçe-Ÿ¾¦fÔÕQÔwŸEžõj{‹A<iä¨Þñ¹ö¿Tá¶+ÎK}"îðu„ªz÷-×u–VŒ~Îumñ¼Î­Ì•ϱ åÀþ÷ì1œDzè?‘×jü}î>®*þÉ x·jaÔ²7Iµh|?Z¡cÌo=µ¦fO×î®áªôÜ.¥¬ûëçŒp–Þª|`jGSÚTï[ØP8»—’C.<£(&gÙîc{ê=øÂAµè9t¬ µ,å+á╜‰»?Kï’{HŽ ö»z@ì9’zÌÃwªÎ>êú:ÛùÞÔL-83Š/ÁþmO7Öœ+Íô˜Û%ž€´1#=;ú m­B?z#µxNøOÒ#(Î~i‰ú³*&¬‘+œõ< -jd`r‘¿†~õÐñŽÅ­ê8û•šÍ]ó„¨ï¢ GŒ¨øÚñÍ>“mE·ûªÛû=éc÷ÚØ`­ á¡kj·sjÞ§Â~_…œ§:w=ÿöÙŠ%+º]½x÷>1^Õ>²O¬«{Ð[ÄéÏ:8||_·×v¶ågGì±L³…7Âÿ7ÕûG}Rç‰þ"פQoaU]¨»ÙÙ‰žÇ«âPÐËq±Çc|½áçËோpÔ‘-?;«¯ l4j+7l#ñç›y«Â…³‡M½££Îߤ¤€ÿ0ÃÈΰbç2‹S©{œšò[ƒŠõõÙÚÛç›&9¼?òËu¸‹Þ†bËÓ•š´òȼ{Äðžtfk§z|·âírFóœÑ*èì—ø¥Qߤ>8H' lýèé9E1oð ‡n~u°YsdxNÅkô^MÄå]Mf ¾Ú ÅœŠ;ïë^zè“×ð‚asZkõDz-µ’OèL¸‡2ûFý˜¸ÆÖœù¡Ìç¾Ã«·97Z3ø¶£fùˆ‡õ¯N—¶ˆO•üjÅ*Éy—€˜Ñý ù¢îPòUÀæÀP¨‡-?]ZŸÊlåWgg¼Ï©«œ¥èœY.¼Þ~àG]¶u|Ý…¿ÛyÎ>äð>{ù^úè½÷¼àÓÿÓóëïüÖÁà5ì—[¯×úÃW~áàö—üýÎ=ö×]ÿ Ö¡YkÙR_¿àú_å9·mý°ÙÍ_î' ;`ìZÖ ¨)Ú»GööÌÿ¾ÇÂì8ÿäËè©qÇàR C‡›svÕJX¿ûꯊí'l½òqpÌi–陦oV—ɉdÆ}›‡þ>9­þa÷u‹eBKÿhïj¿o±x~a–.º&à§³jT¶ÎŠ×Âê Ü,8ùö;/>óüµü$y•ïú&ÆÁ®ÙWº¯™jÎ;É\ÿO¶õ„-!ϣ惿ëg—<Ôk¥CéØÙnhìí3pž…Ò+éßc¿?4>fögEÜÁìñä»U4d»¥ïZ©Ý©¼ÐßMý¥äÄwÁTßHOꬲÅ58H7ä|mâ.ÅÊ<KÎ>ùQóQÁç²÷•­MŽ눿²ß‘ßDœÐÅmSƒÃª›+G65O‘Ú›¸i™oâSðÓ ëºÈ\)·¯1/ívÅaðñ(ö¹vÌZ¿É6ýsò~¹{h/Œ`ÃIÝ.ÿ{ ¹öÜ|ðñ¾†I½Ðö_µ_iÈìOÔ7Ûܸ”cã,†¯ý`?™W7*v ®"ÿP¯EÌða~Œø]U¹Á<ˆ·®ÞÕÌ«ÁÒl-Æ£·5|6¸KpˆÌþR#{ñû[¹sü¹Ï=XÇtýŒ§}ïzØ]S;ø€·.¢fqÝÂjª9ï(8¡ÃA./¼£.Æ|ôìMƒH¼K_+seG¿ÂšÓ[½¤‹†]9=.…¸jH}$ǼG¦Ÿ#)L¯€ù‰;X>¢·´êN¥¦{oß}³ÁmÒÞMü^³KQãWlïFϨmÕ¨ ܯA3vðç`ð]ÐH襟ÅÜçgÀÏW^ý‘h—[|£ü¥*§r;,\l‚c!Ÿ´ÍÏÈgL`xˆ&ðsáð—We]D÷«Ìâ¸ÁY†>V©³à6õeô &<Ø~< ÌRñ3xðØæ»ô1]¬ÝQÓÜÁÞ•ßÕ5ÈÕGrá¥zEˆCšJ¬Í}-òáÇäˆ+a ›F> ^/{*<ÿów+ö˜Y÷Ĺ|ÆÝô¿¥÷ÝÇwiÿWœÝAµWÙÀô`;Gê½±Ÿ·õÂè;V-žÚßGÛã!ìXÈ‘fp4|®Ö>õ(b2÷·ÄpMk¡Ù;|ŒöZµ’œ¥:Ò3£õ«`Ø1Õmä;®fM, q¯M`…Ô.ÕY•Ð7ãç2zlŽÁc²NÖ½oUþRÐ^êü¡Þ3çJ¦&œmÎ&6@5¨ÄôÁæÐd!þ…w žÂgãà REb/䊑»dnû ž+¶2ôå.©ÇÈ1 ìÝØÕT'jm½6· &€Ýéû-n²¿'ÿÅÿ+¯Ìšç˜ªÇcdüçèQ‡Ž<ñZ%öÇÞÏÒ›Ô»_¼÷[ßbøÿú­ûÁÃïø¬G]žŸðÆãúäFŸ÷†'^yù ,Ç÷>êgý”:îígóÕï}ÉZ9¿›a&ämöN¦h?¯þ’ô›Þ#yÊóV»ª×Ö°{§U}OõVå°øŠB®o믻*|1zæÏļéãFíŒsr¤^ë苾ž]¨icç¨2ŸE¹NÆ·ô+[Ÿ9†|±çHðR¤ñÆÝÍy`@Ć:›Ü¿ >1K×|žSðÎåü50½!õB®Qãðõ=$ïyC=®¡´–V~íŒIö$ó¿eëŸ.-§:¦î–º]~•ulåÅè¿É6ÀI Î<} ²y¼>ή|ÎB>É1„N¯pϨ€’ÍÙÓýÖè{п¦Ø²Â¿Ó ã5}O%§¬ x>@ßa‡Ïeÿ5ýA|r7ü=øøöÎÖGÅDÛê˜È‘úþR{—àz/Áß {ëç°Óâ_Y«>îx¼M›üî5{Åþø:Õ™âþRßÅóÎÍÖmižÂ¥ÄÈà·ÿÛwS?Õý‚Cœs„±OäüÌ‘´P#w9=*‘ œà.ܦ™À䬊-³V… ‚{O¬°=Ö¡W¶ù‡-¯ _µ³—õŠuŽû©úà&ÌYYj®Õ nƙԠ‰ø|êQ:[áj‚§ÊF­:Lljõ8ˆ9[šs’±zWOÚV^žÜb¼!9ÏôsÁQÈ{ݰs5„À½Ž“+Jbh:D…~ ÕØªö/¹@Ý}Y+V؞ŻÂï½?f8R›FÀ¬­5÷þá`ì¹Ûg[GøõÊ}ÏÍÏk‘×jm›‹ZDò]è Éõí9º^Pò;ø…=D©ÓŠ•ÎUaIç ¹ü"{ŽÿçÁåÐ>ëñ/<ø›»?äÐjoýôË»Oºû¯ûŸã£þŸófcLàä÷×ù¦§ïœ<çø§r˜ }‚–ÇlõøÙM+á]‰Å.¥³æ ¶>HSœXµß²T]žû@ý@wÈÏ 6ø¬DøçíÌDˆzôUiÆ·w²³…PnQˆÉUcÂ^Û“ƒ¯ÀIÉ ÈÙé…TË-Ë: µ\j®`òà8ì?X„î3½]+Íå-½_ÎY⾺f_æÏø|,úÁ[ˆÚ¤ö°Ð_#̽Çݪf=—¥fW*n›à7IÏÌjTmª¨¦V¥%&ÞjpÃÄ;Ÿ+õ æèÕ ¦:ÒÛÑ;ó߉}7ç\sð>zþ”ƒ\pœªõ%E/’>ÇŸ'xE2‡ë°¸º”Χ0†J=wwhh}ƒ©•§ü¡‹qR/w"F³Bk¨a¡ ­ìŠÆ5&嫵íÓ9úßü³É…ÍR^¦šL›¿`kEÌàœ¾­Ä-Ä}ÈÞ[üpÏ ~IÜ­çF«¡¯þÔÁÅÙÒì®[è¹ìm&1\êojÖ×Ðõ}±ïäÄUaÿÃÏSËçÚLÒÐUmŸ8±ööyÂÂW¡}*ð”qÄÍÆFŸÁEøÙ>?—9ÐÄΑãÆä,q¦qY•Wsoà D/ý ì4ñÜÖþ#ØOà‹Çì[‰™ÇÒ 0½1KqL£V: âj9Æfs}l_>ã'ï¶sò,;øÜ—.>ñÛÿóø¯vŒ¿zén³èY‡=¸u¡3_dÇÑ?Y â! ¥®ÌFâ¦ŠÕ {­^°J£¼‚š‡ŸuxΪ­VúQ°môS÷ ç)èBØ÷h–ö·Ìªï*ŸªŠO;}þˆƒgõŽ×Ò¯CMÏÎ…z ±®l©Û%Å!úÓo¼q-W•ß{|nïl8ŠÅ'êí`žAÚßèý=»–Þ˜Fçg¼'mnŽý·|È|KãOÞ´¦çÌx6sǓսõÎSø€Kâ%YþYÇœ·–¼Ìðüž%óIý9ãÏrOFí‰ëÃõ§¾ëx•~Ƶbf÷)Ôøˆó6c”ùäé ­¿}bVÏgüä^4Žê¹Ä‡áúX Û¢ÜbÂ&Ú?M¿-jZŽnî*¶oEž©õ¬MϪùužc™uç­uóÕ¡·…o¶õ?™Zþ®ÎmÁŽQ{ Î _H¼axZe–n <¦¦‹™µûÄñÀ;„¡ƒ4rèE›ÅG^ªg‘ZldÑz¯:}mÛ+tåûÐ瘨m,Û,4z+:5g¤ËAï†xdî[/¦v¦}ŽÍ•ÑÙ)ðÔÔÏèkA<ö¢aø‘'Ò×BÜ6«M¼gåÆïec~á1ši•ß nyÌÃs¢ü[½Šå§"žÐ>øY‚ëv¾ëË×#Çñ1`%ÄKø#ÖsÙôDÃíÓÏ8ÒW+?Æ¢yéX6xÏ 8<Ê!õ"ß²{*óZë©»³AßÛzgxvl-¹ïAì7hn‡ñÑ™$/w¤µ@ 5«ÿP\·ì›íøg£r°ª³ŽÍŸŸójò‰¨•ªÏ·b?šßŒÏ7¦â3Æä¹—û8~³fåjO×`IèmÁû¶ïþú?¿²gßõ¶Ïü—{çø5{ŸþÚÞûƒç>n﩯Ú?žñн§|ѳwm¾øOÞ´óe7}ýî÷þÅÃvŸ}ýÝw?ðŸ}`šA#¨§týÄ÷|`­˜ÁkJw<ð}öÿ½'oÓûÏ^oÁlÎ3ê…#÷¥î>(êÙð~Ð&Ž Ö6R?„Åj}³î/ 8Îä™E1Îö2{¦ö3?R\ãϧÜLlÔY¶Ï$_€M{Ø~ñ½ÒQËZޏ[Ò/¾ˆ=›‚pA¶Ï5ôs~”j×ÒN ŽŒñÔ襧ÇͶY}rpà—â§£Ó³êç¾(œûΆç†–>XêÆòoî@ôD,FÞÎ;7[¹—>³4œ%úÁ*#N¸XÈ9ð/pÊølò~å~wÄGMõfòæCWÔÖv~‹ìNþ¬ýc:ØJùBÇS¥&q(ÇXÕËÁ|щ|08^ò:dw¦ÃOhA?ˆp5îËÈúÀ'¤TkÁÝ÷JîÏNlß׿œmP§B7A˜Ùi´Èá ³Š6¥Bž¡´ð3ݬƒ1Á-©Igïßr24o. e¶~A\BÍö“>Â;ø´…b‘ÌÙìçlÏloíD;iõ7Ç,00iÝ1?3ÄPG>÷ëJÛ=8ÎzоDÍ8ÝÛ©mõ »cn²òÕCb“8] by°Þ‘õ×Å/µ|´é%‘S,ÕÃ9«¦Üq³Nƒ1xôë?ôèC{‡¿ùšï8|ø®ÿæŸrxá ¿sð¡Sÿcñ–¿ú­Ãg<íCÎS0íß“g8xìýïy`ñ”aý÷~ëãðÃô&¤ÕV”—8—<Ó8Y:ÓfÎHÞ×{—¦wçÎÔ ¿O›ØHuµŒpHmÞ}rñEãÍ=ì9thŠ‹3î´}’>„znI{¼×ЧQŒZÀmé/¸SZËÐ6/ô^‰Û®vÞ\÷¢@[“ù´âP{‡…æ&¤OQ­Aü•[½GU<<øèðwÜwDîz-í>Šû²Ô<¿fC¢öǼöAur'ù˜mí™ãÓ†cˆs" (¸|ðUT'ðÏC£˜ø`húˆ â}8LcxŸ…ô[¶˜—Å}±S1‡:óIøX«AœCpÝÕ•04f¨Ã”¸¿§U_¹6Á‘ÄöÒ·†=ÅÇ©?²çMlko}Ïe j‡‹¬ÐœGS§Ãï«ðe·ÅÒ?Nv:9“Ä@Ô£äï2¾ïÚëïÏM<?Sk²&L‡?&¬ƒÜKz”…8O8í?E{ºëmZA—”Óœ&g-ÄsÊýßÄ¢p€‰i"ÆÜÊuÝ“À á€{Ú÷ÇLÓp~èõuÖáhæýS<6µ8)ð(pzrkÅ`úÝÚÛf0ÊA=à¡z{Îû¢ÖÁžˆ÷UÛL”-öÅm„ΚIÔÚ«Þ%±ùlz®*9§ìFæ)ú;Ö4y¿ø7¸vÊÍÆAõMj×ú9ÅmÖ‹|CþCl ?“ø.™ýÛþ^q˜º»¾'š¹;*gt{Cžgkh¾yhM÷#âjÕþ<†»ºþÐzüN·Úbì½?áÇÌ®ª=¤Ž–ž\\ûö›|ˆž°&â r˜.V_ûE¼qG~VÓ ËyO%´Æ#&Qc œ4rDãüŽëB0«âG¾ì+.Ûçì}ÔÏ>âéåòøo=üð³øÀæþý·ßþöÃé+þáÀžiï‘»_vÓŸÄ×Öš‰gýQï{ð =Þ±;f6‰»O”<Òö„wøÓ¤·¸«I.”;Véy¹A—qSz–öìö<ä­ö÷v¶Ñ@küÉs`¬…Ø[6°RÓ=v»c±‹â>ï'︤~_ìù9¶/hçÃ3€O+ãýl ÜÇ2›û¥³PåÛ=NÀFžQ´rG¿§Êgû™]“°¡Úþþ\圻Q} … A½{ôšÚwË& ›þ˜úà+_dü8´žzfv•Àì/hîShBÀ½W¼?­úûªº­°ïÐþÐlhÍæ¼šZOä×Ì…w*›Y»Ü\¡4ޤ÷z0˜˜ÚãqM<&Ã7¨F¾€Ÿ¢X«ÏÃÔ¸AŽÆZƒû¡6‚‰’›w}ìe¸¹ÍÌ/¯À‡?‚޹6ÿ–Ý«Îy›©c~ìÂý8øvWþ­*F«ÄhP¡q@ìEø(õ|ïD^ÇT Så‡ý<ÛŸ‹‹á>„¹MäŽ]ø³mÆœ\Fá[Ì·ô» 'S˜°ß{éÈkÞÂs§sϬvdßÝæXœ×ožÅBuZê3ª D/ߦôCàt÷þVëãµzxì÷å7èaç'^šˆY:-ÖŽƒHŸPôÜêÌMag/‚Ç&.„ߦÎ=÷®w¾Ôk)L­j†E᳈ÿχ–Ý ~‚/ÄÆF,xZl“ôk‰ñ<¥¿€»@}˜Z•ΊÛOéË(Ïj¼8ímæ êEG7B~7k#ôÌøçÈv£Wà¸ýûM»‘¹¥žÇe_w‰ø”ž.Î…lde‰#šm­8&ôO.³?Ô52+z‘ïݰ€g~f ŠìÇÕòùÃ[ÿùšìÜmûá»æÏŇ^Ïïä9ýÅY?^‹©.‰ÏÒzÜ–ý Vßù¶kÉmìb¯ÄÕ©ß q2d?Ý…BÞ¢s³`}Ô+˜=y±w×-z¼I¶Íí»z`W¡¾¡¼å8kàæãà¦Ìª¡Óeç°Ub0,ÛòkiÁcBOl.kÔ:?ÎãO±ÿ~s›‘;…?j:6àoðÜØ7íKQ~‡vÖJøuv?­þ¹-wÓm‚ÙÅ3šklïÁ¹žÕ¿I®KŸªÙ%Ùtõ¸^e~dUüOÏ¥ç%`…èäÑG¡½X«_t”ýȽ7£JTørô§è›eL*þSmõ‚kSÌqˆØž]<\ñéœMÅ1àkp°e½6òÆ‚Ø[ñßÄ̃£Ð&VCuä¬ ¡­BÌ6:5Ì4 †UoOò«áR鳓»K^6„ÎÅB±šÔÜ©)ìã©ÔzV¼‹îcUÌ[ÑÐ_†¾Xß7ágXqSº ̱;#m;äcöù:óÉá¬Ùy1®=G £»§ÕuˆZÏ@Ôj–ê“‹uŽ&bDÙð6ðpçúÙYÄo£oùC_·³öÿ?æ¿|ùÞÇ¿ùÏ÷õÏ¿üQ÷yûáWüà}w¾ðýÿñòϼîÇmþOùÔß¿ÇîÏÿÀÝ5<àäßÖXlfàÉçØ°5°Ú€i®hæïh±ºý Ü´ Î©ž@Í`9oÎÌ(< )r‰ÌQ,þ³¹!~ü\µ¹…ÎWßa¯íç锯…OKŸÏ 3q*½ÒôÜ«N€nñZQ¬¦ÜÃó@ôrˆIä»Ý†ƒ)iÿÈ-Íú:J»h®ág9b‡«Ò( ¾ÚÖp)ézÆ™|°:§Nƾæ3ç,ÙÅ pÍ"ýèÄw•óŽø#;öäàúNv«¡ß&Ýæ8ƒû½úg1ÜŒ^ú5âèmú[ˆàT €1ƒ¯næ¬÷­»ð;ñ{Ä3ØVúÈGZnsŽŽ¢gŠ|ü´ÌÙ籿>QÓÔ³ùÏ0Ûê6Ím±=Ž|)°’6sÓk`Øã…x½î»r’‹•}…7 D¯mÏï€Ï1‡Öjêøµxâš|uä'àä_†…u¼)â0b_æØOŠaKÛ‹ÐNÑù]Á…áücÏ7¥cÔ±U—[ºOE#CœëÚ}=¥ qi˜9IOË ¯!æ2F ¹Ÿ=½äögš[Ü×¶ÉUmŸì\«®,<<ÖfSzáöûвgçlÂåá<µóy}Ã{C—wVÏ/øýfhs&¾­\¡*.Õl_j%® —°“ðõ¿^³(Ðíë•ÔhÑ”±u´³«;Vðç]üæ{A/—îm¡~:´~¿Qk[5kªhý<öÕY_ÀÝg-É1Ô‡•çPŸ;u± ñ’¯¥ÖÃ×’¿#›Èw{nÅTî’Z¡pÁÈÏí9é#€G¢³UUC^£yÇuɯ¦~2¨–F.;„.<ÌŒ„qå¹ãg¨ïoˆ|˜<˜;Çñ“îûý‡ö÷ÏñKß}ý{ö„·ïœ|ÎÎÉóî¼úUÏÞ=ù<Ãÿ§§ŸÞ9øµ_¼õÀúìÞ˜_¸îUŸ¾#?àg§ñØí9/“îìDÏCËѼFQôsÙSK½N:-ÉmÖùŸ˜‹bq}'`K ŠòÂúµ…Q¸‘ŸCâíŽÃåkNŸ‹Õ¯å/JÕõᦎ sÌ›RGD>b‚=`^·@3Œ^¿‘3K½䎩þ&W¯“'xçqf®Ó=9‰ªÁeÜnÜjqÖÊÃ5O8â(ãlÈî¹­™UK˜Å]VÝÃm#½^ÒÎ ?M\¶ÇßÅ]CcÄ.é.¸¿·Ï€ªåÁï=Åq)¬¶gSz4è·u¼¬U{– Õ#c^V×—ævî5ö6Îßµ«ü² —*è[€o7þØFæÌzWŽx¤ø—šçþîŒfÃÁ3Q 914ìË2ôLñw#vLAõ®ìÕ¥¢ý(ðÉÑ·A3¼…ÜbP¢|®ßWêKÍ1íbÙ•ÖXu£KÉ?o³ˆb®"Øg\÷¤Ë+Zñò‰£üŽüÄU¯³€Ÿbfõ„Úï«–¶æ÷Mz–—Yà9.¥Ÿ„îjl+rš¡i˧^/k, }*â'°°…bƒµ~~%î‘céÁcÛs£Ë ­ÖV"¿w­æ=PWH­õRŒƒzˆƒW-:­ÂM¶©(#n^gppìï >cåt•ÙÆüWaÂkr[âÇ#õú â½Im˜Ð×°wjó#ž/fWÜì†tN“6÷šÇæ+¥<²fÔšéÕ´ï‚ÒõƒÉ>ÇšÃM‚3wºbZbè1p+üŒ0× âÂþÈWú³¼»CèASË(òY®ÎTriÀdÿ¸¼é“Õøÿåÿ|øº½­÷¿rï)Ï|ÉáÝ/=ÿðäÙvßûŠß<¼ôOÙµ÷{û¯ì¼üÛ5] ‹êïüÖÂp€7¬?ç b½[צ <ˆ‡ý%¾ îÿM© L¿ðàºWðÝ>Vy¶ûjª9º¿€)¿›½.p~ìË>tõÏ—Ó|Tßÿð¥Á¥²óy“ëãä|*x;pßÕk –ïvÂþ[š½ŠÛ3ÇN¢=‘ÿ ^0µ+[+q¬¨—ŽCÖÀ6F4NÁš9ûpÒ4C©Ó—?Ø‹Îà¿ßó7eý.)ï]ƒC€Çø¸å[š ç$5ni¨ºíç,‚yáãŽ4ÿ@؉ÿy¹|–Ïæ¿~|³8~¿…‘)&8]Ä»¯ñóòlP¯"Þ£†3KˆõÁNâýÂ[j}LÇà‡¾ßòÅäîÿu\ùÜk9Ëšü TÏUቜ‰™uSWûZQ»±Ïñ™çÓ³2¡W+lgÛ#^J»^:P½AºLý‚kh5Nä/ËÔºš):.ŠµÐ¢œú˜•üÄ~–ºØ1±6Põi7áìh´zoy³bœ‰ÈÎ.ÚU휃á1@ôæ3x#ëRwJgjPïx8÷”Þ©¹q2¤…#?—|wÎïLŒJ¼O¬Ä¹…7Ùÿ}Ãtš~)½Ö›êÂw+ÿ+mvÖFÆ›1›°6-føuü¶‘ø]9æëFªêD‰Í€C°×ó]zvŽåbV.ý:äYÝÝqŒº×”¢ÎEÌ&Äìú¦WáÿÚåeë-Â'*ØË¬:£5{?ôå:¾¢bZ|ÆEË•.}”Þâ]xàÈø6b³AZ›IxF§Cs5ë ÄèªãùïØ=’-Pôbb^ć̉#ÿ’k½­·‰YÀVã^\¥nPÀGìÜüí<êоsë…ÿéð'¯=|í×~þŽÍ÷=ù³õëvïÜ1ýqðÞyÓ—ÿ¯ÐWŠÆ;={ÊSˆóªåŽsã¼z¿yf_‰écn¸ð¾ç?)à3øhaþS¼opªìs›ÏH\ºvzÎÉ¥§•;ï\Ïe÷ Æí7K›^x€f¥2wk#ëHp0Â7C›ý£>H¾Ð÷.ž0d÷hÖ2{ L9´—ÜfÑçíñ.˜/< N»<µ^štýjϽà¬SÇ=#=ŠÙuÏ.‚7M`g·i>‰¾Çûæ–Ò6Q^S„ÿ®5#¸bçðoöß¡u ŽðÄ׃™(Y¯i1Þ5¸ð ˆÍWÎìv4úú"ÞËCoGu†5¹9)˜|!ê´;z vXÁ \ü_qN!6¦B.x9¸÷Sßçùïý5ãÎ!õMq:§ˆçOeOµ“ÔïŽ÷¿N%ö‚ßmçÌ®Ùñèó¦; þ€5‹ÐĹB,1¡« .îdóRZ\ê\Óü{[?Ó  ÷¥ñjRsºª _NÎï÷ޏ…rõvnƒEØÞ(ææ¿×ÄrCΛ¿8µ,à!12çÌl 5Cõo©¾ç¾×÷ÜS5«‰Ø_ï0ÂWš5«v¾>º!hUÁeö8/´û¶Ô‡ÅÌpo_+·ÏpÚäër–—4vsî5<°9x3sããe¼nŸ¥»•¼Pbõ»ú‡Ô÷ëy…½Âçˆw¤¦Ü§F,Ýæbh}d /¢!4µóý ‹ˆÏW¯“ŸÍœˆÛÑ? Æ—™?‡ FCOÏsþðÕ—í^öî½üóS—¿åµ?»ø‰7=ððmxæ$ÿÿâƒ?xîãm]Ÿ~úàà¿Üö‹ŠõøŸzçóÜ?>ä›þÞ§Íÿ]Pã´½“ñwæRÁå‡ä`ndà .8ñƒö<õ%[øý>Ÿ3įƒŸd?© Wå÷ aœU¶ÌÏyKô€¶™ïòÓè UýlÆòÏnGÈùá\€‡F|íš6Ù_ÿh5ÍÐ׉YRÑFß êU:Q‹†£ßþÖ±sËfwšúQ3¦? îÚ ¹¢äKqÁÙ#Mð1•P“ȼÛõ©Kâ3l¤–ÃÜæ½eî¸[hX,Å=×ù¨ò­ð±óÓ¦æ0 MßÑç¡àƒËò쫾§¬åYè](äÁ]î3Κa~½k~^å®e”|Ó4K³“Z›=§æßÁäïà¯ég£¶·ŒþïkÑŒ42ÏS åq¤lÚÈù›£'}öÔ±Kñ+w¿Óç<ܺïlÑñ£Š|¿ñúÖZ£2«LþÚýHè|_Ë\TœåŠÞ¸ÝerYá“Ô?ý¹¨³÷\j{šÍG.•=wôèZ¿÷¦4!ÏÇl9Öv;üpØ»ÛÊ u °ù.ß3û9xBÊ™œŸz¥WMÿgAL&m÷Â÷(¾[SQ >Ä =ì pêÐô¨ÛßÃÓ±¸…\ ÈþÜÖ¾7gO9†ñ‹ýNbg„ã¯ÉÏõùí@qÖð?àúuõüÇM¾Ž¥ûêmìŸtòYm¨’stœCê§kx3èÀ€q·ºJ`à‡Ô Þ;¯ò}Ø„®Vç±ZÎ-‘®À´lz]«–Çm$•s W_6Ëßy¸9çýJã*¹ßJ~f\¶¾ª Ÿ _O _‰;Å p<“^}Ý1÷ö,¡{qj=Â9_£ãgz_ÌÚp1ï©ó˜X±ÓôŽ?wí^œøùÝûÿòËŒëW÷y¸0ßo=ÆT?øâ×ôÈò8ëÊëJ×k%>óš³7b÷»¢‡ ?‰9ZäÉèN(®Âê+XØz_vþº{¿ˆ:LèT‰Ãµ€?FŽ¢¿÷ÿ¯ùy©Ÿæ'›1IÓ¡Š—ï±¢­ ýóôÊ â²F㟿8R_7ý7pôíÜ›íW\ž=ì8<ö!ù ÷QnYàuèÌú ÿˆs@¼Š¿T–y±¯Þ;g‰Š›à¾•:klŸ ¶cßiö“»†Ï†‡eçÖ4 8óø^Ùÿ+/AÅVøÒƒ8Æàû²Ë¾VÛ‡&KbÉ[žCÿHõ¾¨+¶p•ر ªWÃ]±uD'^ Ú|êÿaCêó†/9‡ý•¾ý«éÏRGî¨õ‡ÙQE¡/’ï7#ëóƒ4$ÉeÀ·‚íÕ¡ñ°ˆÑ\ǤãKŽÔ«›rÑmÈÙîô­o‡g¦§ý›œ‰uƒ+©†~B‹%£'ÆÖUùA×´?Ñs5ˆ7«ø æ{á?ÌÈ3ßâ‘­Ô‚U­…úò¸Ùfy€æ±æ80å‹ð&8üäÍ`:?«ðA®+Ÿ˜š¢¬1xºùWõÓøºE=<°B³7ö^âÔóÒý%÷àÜÎâÙû)–(òK¥×Z—†YÅ÷(wX°îØÛYºB|†êüðú} ðWöÌ—~N>µÂd™7š³ßÁ©¨¡¿Î:µâتþȾ˜œFþ–ZmY¦~Að’À±tö²>;gSp/[³nT¨[5m§8ÿ}+wXk:©f—=2ÔÀ•ÁŒøy½jú~d·ð%ÂCœCÆ:‹ •}e1û#.ab™hPètŽS×ä³øßíÙû}à'^¹û¤»?jïÜ+¾öò·í÷^þß?x½ùY?bØ€qËÛßÿU;[/|ïÎ{>烯yöý/ÝïE;oûÃ3‡æt¿<ç0[nçÆò‹“ï^txóHÍÜ|‹4³ ñÓRó›À‰BÓÐçó¤8ù}ã—f/aòP°OíßMN“ýžtŠÁ%Ga/Y¼^:¢Ô@»~±Ã³üL‰ïqŽœ%ùð¬ýÐ[¨8pA+~s{ü®(G¤÷°(îÞžÅ?¥Wvhüdðä¼W8Jæ?Ôܺwò~VûNêšøclX„|§ûÛóÏü<ý6–OÀ_³2Öž)X&vd–öñ?÷ŸiPï‹líDÍ})íïÀ¡áôž£&¸àޚÜßÅã%jÀâŠÔ®¿ÌsUæÜ ÙÕîª|„ïsô„ïs–éUks=-w¼„:_ã\_S.l¶ô¬8$Á§^fÇ9éÆzm¯Àœs¾ÏµÃœ›ÛÎéqÖPdÓÑÒ¬ô3oj~ÃRú=Ä â†Uå ÔÖƒ´HéËÐù®ð¥‡¦7b«ñyMïqCwàš¾wKøj›»¼4×I» Ï]QKÙ ~àísôUé{°;½|WUë»–3eÄBëmÝê×ûÉS”íò|Rü²Ì­çV»þ bÏ5yž—šöüoS:Š™èiß?³à•dN`Ÿ¯:óKKaG\{6篡—:´é Þ­0%ÕÛÓ–è¾Hý3ã*ð4pŰoûÄ¢eîú¿Cæ&æ¯nË¿®Éÿà@PK¢öJÌH À½Õ ƒ¯£†¥øÔÏytø¨ Ô5|¿ìÏÍ.EoâòˆªØ¬±±7ù^ÉNÝ_KÖ5ê“©}%¦¬º'ØÜæih­ÂN6N’cÜ•žòëóš¥Æ«µvû÷¯§Ï÷ûžß½×á}nùÊÃ÷ß÷íÜþ’¿ßyÕCW?¹xíî#>÷¥ŽÚ¬?ëï3Üß|¼aîtõ°•ò:¿ß`†Ê/¹[Y‹Ã'é™ÙÏIœjtVÄ?sëÃÎXz|­ÇH³VfÏ9§ÕFædœ±Àn3î]éáiЛ w—3·­xËóÏÛ¤©VŒŠ\õjO♔ѧ {~ß}çŒþpAòð¹è ]¢¥ ƒ"¾oí±oøìö½¡‹»‡z„ß}FZàŠ9L›t-ß—ø¼|¼û&õèV|Ê<ì"\SyRËQåûJhã\Êß#΄Ÿ·¸Í¢W½¢PW®¯ß‰¨Ÿ•OäÁhqé;ºÞɘéƒþXh…½;QÅ›¯}­XÖúÍšm/ÝnpCâù*[áïªó5‘ÏÒâ·lÅ‹ÓïP¿º¤u¾aA .Çœ\(b‚˜ƒoüÖ¸M¶¯êß(à§Šs™£hß±;gj½"îk‹µ|ŽÉ‚¼N< a¼ ÎI«/…þ×Qè”qgü. qß4kæÆ þ bwñ›ýüRÛ¸^º‡Ìe  îo;8®W’³¦:!1†â“ÔçK¾'{B¬CœÕùŒWÈù”çUÎ0û;¨×ÖÎ£áø›Ò¤F§`×HØAÎ5Hüõ||«ðµÐì&~í¸3ª÷mô½è…ZÖl¹í%å¶Ágüù‰éÙ/øMìuÕŽ¯5©öèØ–r8wäÈôLHß2k}cÓ  ­43±IÄÚsh6ÀõáTi³þÙi$Tê…ÆW öÜãµKÍW¤¿IûYÚ熆!X@_?àü÷uÕB:¾€ßúiFøØaïÐuÜÏüßÐáðu·P±‡ÿ=|ùJ-šøÀÖø óS÷ìó>í½¯Ý}ǾrïeÏú¦{Óý5®ÿ•—⮥;²óÈ÷9°†—¥¹ëÐÜ»umüÛ Ãfá™ »Sz¨Ô¿™G`¿Ûs¦Õ²‡Ð•µ˜0ç~H ³ðžöìè*˜-0?dß#|^úðÑaq°Ý³ÀÛ‚[&Ÿº–ÏM- ¸è¸ž‚o‰Í-MÛ"ùŠžç.Ø*ì ±Hëwh=ËÔž´gÜ=aQ§Rw…úÀó·=6û8£™m`¶òÛ•Ø[wA¯+ºRÔØ¨Av}WÄè£úõÝך?”-®ƒô§À¦Ñ÷6 öæÝq<<Ö£ÆØõ‡?-H°WzÓýïMw<•xQ8œÛ:óu¶úŒmðfå¢ý`b‹AøÚ¬^iú{6s.\p#Tç¾KM¡ÝG÷kÅÔgêp3s^Ss›\G¶'óTåv›Fî‚p¤‹éÏ·™¹uBúí·‚+Õ>¾¡n¡:‰0ú Ê‹È)¶”_lŒÄ&àbä@Ú§"»È½$ÿ›ø{ø(h’ïƒq¿‚y Ò]ëÿ›ú ü jÀÄ»à//ŠÏ‘œS‹5lÉæ®‡ÀÖ¤Óö¯Ä9CôÃy¼(Í?ÙöÐkøò&·OÒiÜnqÂ1DÕ³=s┲}ípÅï*gž¨µÂ?"B-ˆ~Ñ*þlâ:â[¸§ÿ×™¿šq¹ÙOaàò ´¿…Õw¡AS–÷sv—÷&_Zœ‰Ä1ÅKϺln]Jå(úv*¼øÃo¦¦P%6Äωù3qîˆåfñï†àRc(|‡°ÇJlÑõ $¦,Ÿã±ˆz1•ë_SNûDŽF;KÇØþ\uÞihy2u¾ùõ¸VoC{a¾5ޱÕj6Àd&òÊ¡ãöÁ3lyeèÜ–s>ŽÇǽþƒ–ÿoà‰Ÿ|xæw¾îðäw¤Gš¾'?ã>Ýzû^úÉo³˜`¡<‚Xˆš ÷ÔqV;º.S÷Ìp4¸EùÍþ¨éÜ êͧÏ«»:R›"†Ý .mæà#Ú?ÿlúáé@³ -nâáF`Uöž¾õÄÑÁ€—ÎMwýNâ°Â½Udßxrrà‡WÕéªîªŸ¹¨¸Ur|±wÄPݬü"=6Ž¥‘+ŠwRè…èqÇYýuÔtÕ^ºÝm½ÂØãMésg…E°&Ôf*E‹oÐF&„ûýÅwf…RÃ%_“ÃqšÀFm¿¨÷SÃ?R ºµÁ;K=l´¯GÌݾ1{aiŒµÙI¾NôÚƒM GÉZŠæKmc_9Çôk3ϰ޺sبĈƒ[sÎö+ú¿fé– Ñ; Ï»”8œO¸8:ŸÛèWÉvŽðЩ½c çÕ¬•+Ä_9Ç ³ n¤ìÂØiUWñø%{î›p“ÊùZ†î„¸üQ_2X’°ý  }HÎù0Ÿ f¢¸…y`U1÷ÛÏZù²…ØŸÜP˜ì¨û@¿æodâ¦t äß×`‚·¹ŽÍê ~gnk3~/$÷¤RWÄË™Õ+OóLô¯É•±EÊ*uê”]ì /æ.çX8àÈù'_yóqeþ‹üÖíï¥ï627Mg€¹„üý¤~úȲG‚Pùƒl¨â•´iϘ-Ml·LmÊÐ Õ='¶F_Ägòœ³ø9Ì|³Ï¹û›aÏžãK¾ëËv7ñ{‡o¸çÞϼîÇ/èÒÍ{ŸþìÏÛýçï|⮽ǽßú–Å'Á½vOÖ~ç^OÝ;¼Ç—~¼ý÷âëþà-®´Ø±jþÀÚ´ÄߪêÛYÏêATÜ´–Ž¿xö—ÈsÓvɧF-)5ãî*À—MŠG8ÓµÅA1/zhøìŠû¬Ø{R<[åß§»vt½fz£ÃŒïD«òziâÈžåLú:Û8ŠoTÅ­¯àXàdËà8€q¥æ,¾ûΘ9Q²W Ü)ÖZ5Æ vŒ½ }ßËY/äÉÝŒµ¢¹¶Êÿ¶²Gœ{÷DwÍkÜúã+VÕp|¿dŸsým=±‰ÊÕ«´!Ì4ô9À•k§vˆì óÀýgÀޤ;ç¬Ós úˆ4³‚#´©™~ô)ʦ҇˜$X&þóKM~úHžÊýÑEYª/J<èJNÉ ï¥ô£ñòU©YFíXøëvÄŒRëÕöWµô*N*µf?[ªøÙ…÷–=K¿Žœ´›*s:fâùÊe3&°wP|Ë/ºopf<Ÿ”ÿ¤Nˆ¸žaÇ¥¿nþÏx,í°-âô‰Úº~gA<+®^wGb&Ð2ûÊeW²!õ÷¹ÇÂàªx¼J<.<õN¶.—¢Þ&àõNø†ÄÕ`NÄ+ÄtƒêþÊqÇF„Ö ˜ <ì©­ëûïûµh±VðôÓ¸ ºK#u=õLáËC/ŸMüþ± {óüH¹õ$žÔHšíƒú‰ Éã¼æa5áð Öƒê1èg2¯ÂÖdS:5¶f/éZ¶¹¡÷Iœ¦B.§`Tž-ÿœS´3èEÎ^e¯… ¦þI­x2ª+vD''´›x¾6gó=‚UùŒ?ïý5+’¼“ç’²¥{ÙÎÜ,rë!ôíЦ_É.€·‘8†Â].¡Ñw}ÞîŸ<_æh3ã%¸@ÂBß– á²ë½ý÷ùüæÇ=ﵟÏú1ä,½û†qñ|Mÿ ®ÚRºˆÂ}‰'5Óúå>ù ÏŽìšr·Åœ3pã\sÅ1ŠÅ®Ëþ)Å0£b¨μyhú6c¬Wà³³¸£Š›ü|iNeé×OûPñeÌ R½<5+»ýéÏhÖ|cŒìÁÜ´%4#1õÔp ¦Ší{—â€sŽ—Ò¬é8Tºôî»Æ3ï¦5œÄù!w’öx'RŒ!ì+u/‹ì˜þ}-ë³fΚC&ï¤n¯3 ›œñ0úèèƒá“¬¨£%®3Rþç½¾ò¬ýÙ÷oí\þÏÿí÷>þÔÏí½õ ¯;‰ ö>õyÏÝûÓÜsÏöë5ügïÞû¼ÝgÿÆØ}絿:|Ðîßî,¾ù3w÷óGLhm|Óø¹»¯×?Z¿zGº{V/ý‹žÛX­æ[^û³kzCNÞÍsi;Gp°böýZ…üSw°(÷+Šù'bp?û,éÝÔ.ÎΑA=‰à†±Ú©''ãSÅè¼$F;dÍ>xmÆó>é)Â^2{åv¸õëYsÉàF.¥1'›â¹ u´áffÖžNÌÚâï†{ld\>h¦®ø‚œçì•¿_†XrD—êE¤yF޹}Žkݹ‰8yNòÊõòû™ùÈ\pÇäÖà+Ø5i¦¸½ë´Œr¦ìŽðÅøüeê@{¯^ζ²ßÇ'Pï²{I+rÙsΚÛh¸…¶Ÿ:ˉÅÎâ’njNí³8yòiJ“¦€%×\ ¸üÂÞÈÕáшór® g¨x:1qI8°Ä)~þƒ~;µf¸±Yƒ%žVCŒÌìÆíþ¬mªÇWçhR=fÁÈ·¢cãwÇî7ýyô<‰X±ñÔUCÉ{ZObèÊ[„»K>_î”.cëñÈ:RÕ\#°6ÿ{üýY§+1êžf|*L¨p¯â^GîJAÿÙºûp'8Mü~Wû_éé²Oà"üNâƒÄaðÿv6Û|¡°½ñg¶5WìN<ãŸ) ™”ŸêÅÔ(T~>RÏ!ÏîÂý‹žbî>Â>Ërî‰ê0Y‹³ç7ÎÁ z=¦Â!'Õ£p?á u\ÀÔŸü§¯}iú¬¾àÓ¿êðOüéÓg=ñåïÛùÚûÞzø¾[~içïz¿»áüÒÿ«¦trÞ³.bϬþBMP8Ëu¦ëÈÁÀRêmÖ3²ôÊsƒ›ö1‹æÕ$ Û9dßXÖý´/ðë·’ï«{Y4WxMÞ­\6s|9c`\“ª· +ëCòQ#½½ö ²Ùä-ž·ŠãVõ3ô¥ £¦µ_ß&LÒÏEÌ% €rœJüj÷QÜâwúbÞ¯!°Wiã_%®O{¦˜>Z¹8ùÙJ\Cf½Mº'~Çì;¤#Äý«à!ØfÝÁس4?¨~'ÖétÎÐÃþ*ÆòøL³{=ïç»åkz¬mÕÍ#ƒØÞ× ~.µÅBªå^¤~>»ë]ŠêpUö…º/ï$:jhKd—Ú|Ë7ï}Áo}`×ÞãíïÿªÝ÷ýúß™VàΕ—âŽÙþºßÆîÿþÁÛ H|àÆ|ÃZ\xq—}F”ÛꓘaïT ™šZoÁùvlüÅ˲ÓÇ©óºŒ2· èwõ|1Ùå ÓopŽŽ|®ˆŸ•5ß÷ð¡Ïù·ô¦ÜÀWV΂žž!Ù‰‘ûÚf3…‡®—‚Æ1<~ÙÂí[_ú_õ\‚!Uì³§á-õ˜}—Ø­ÀàB¿Ì–^¤.&ÕŽXY¶gDcŠ„Ø¼×þ^< 8õkåD/ß©9´ÄÂâîÑÃP¢ßqrê ×Y6Sã>¾=ySØô/á4*Nq›§zØ‘ò©ìÃrÛ˽âg˜½¥¸Töq#óhžap½ªW¤ ö*™ØµÎF!.£^Îö×r¬Ð¶}“ãlØȤs³V~,³Oã¨>@ßS¸·Âü}îlšùÔë ]´áæ6gN€=ú&=”¸w–惰ÏJœÙlOôû» êUPNÞqÓVXêó+ž”“’?d.§ºÍÔ𬠙ã­÷ŽÞ¾wu½ªã¢}MlRï”yÅmÝü°+°°¡õþ“ãú{acÀ%„±ã +vtÙ¸Ó -ÛŽìç‡z#ñ˜îh!žÕG8tüK=3³‰ü÷ô޳Ÿ;¬°‹Ø,íÑÈL øfò¥Ø–I¯ï/ó/Ù_iÛq®=>„ãfnkû¨¿û›C[¾ãw¯¾æ_]¾ï·|´Í:ø£§üǃ_:õ凯~ïKlý^|æù¿þ _;x߃_èx’Õí¿£¿ßgèJ³#æðÙóžøï…jïkÙûÄB5“ÏóÃÖû|8xjš›îëCÏKïc,¤&‹Ÿ3µµÒüîª:õþæäGÜAnö®ï>\> þ8\ ·ëä`…ÂÕ»íÚUœ‡5|=ÝCf"züsÑNWò_âcåvv36Ë=]†{Î$·6^¤Ñ¢ZCæ·³´$íï‰EáqÂ?¼FýäçLz jµöLªQ¹ÆŒrÙôãüÜ ^t¿½Rèêû_È1ZÝ3ðY|Îf·ŒNuÆÚÇL½î bp?_h°×ª/)=ÆŒìsÄJé‡àöÐ$ k#slÅ “xr#\AÙ¶ FßåG¾§Msdƒ9î‰u‚¥ØŸ©–ÓõLìwñƒc`9k%x7À ·©äŸQ÷¾)µðWCp<ïÃ_É>j]áíN·­‹VœmÅyèÂ8SÂû}M­ÆBƒîSÅ5ÎOêÖ¦æ˜iŸ[òîÊωý˜DüX{_,Œ| ~MÚ~Fý3EÜ¡m02p¬A=Ò©`탸‡ZOòÇÚs½»þ§ü|ÅY¥‹¹©¹VÝÏÊùÇO ÿJ¯å™n>­ò€zI7ª·ô| ÂüÕµªâHÕ†\[ŽÊ6|?¸!œyq4*uI[ ò-ÙûÜaòôPòÜÔp§“GL­—Tcð;…¿ ¦‰Í¯ ¨Yýðĉ:CÄc+}nÁÏÒ…„ joJÄHçWŸKîñ/±Œb%ôe“/ .7?,uëÉuˆÍ×^»ŸudxqÔä¤o¸ ¿Åþ¬nSú‡ó¾ÐêÿÓ»÷~ÅüüáŸlÿËÃ>ô»¿têM‡ölO|ÏàÀ¯ÿè)ï¶½[›ÐßóŠ[Vðo–1·¤ÀïçºNýæ©+Å3¢ý·b›œÎÐ2úéÕÜÖQ»“]J•1òÌS êJÄܪÁMìýx:S+Å%é;Å_XGLÛtÀ—ÙÓ|œœ8`ŒòõùÝö9Ä(ÊKx_Õ8zkñ_*wS¶n"²ué´²/¼ã¾%§°Ã·R/˜˜Åî=VÄêG9w)r*zº¨ynJ»!ú/&ÀbrÅÖÔ+*ß¡˜§´Ø:lx³þÎÄØ#ï/Œ£2Öý¾Ùr«§nºMeHþ͘ªû%éÛŽ‘ûž‚8i½‹f¦MØ?ö§Ë ºÞË‹•8“¼Ôp0ú ƒÞlºm“|›Ûu³áèœÈo⇋Cø69=sÖØGñSÈ©ãå÷eW|-4/Ýë‰segÕrºMie«>ŸÂ÷; LÉ>׿8­ï¦Òß@o =Åîá#„^ÓFbààp²_nScízÀÝÉ.¹î Øñ µNý}rtÎhn“â³ä:*vù=xÄS]Gtl“ü ƒµ±Ï¥7„ºõlyµò?_òˆþ,Ð7 çL¼â »ý·Ù7EnpÜÛmª>¿†ÿwžXIQ žu¡‡]ñahjmF/aŸ]5¾Gûç÷Xv£ÿµº‘s¯±¯`£¿(¿é÷ Þf×o€¾¶ŸÏЯòy1ëŽg4ªŸ’¿ö{hÿ¿ÕºBëqÎ>“¦ÇCLA?y¿ðÜ¢üj-Ÿ³ÞLíÁÁZj|ïUblßW¾›3`ïg$ö­[l•ÎúšÚï‘æ@€} ÿF£|ÿE¯‰}ï¦fæ€óÚïîÿæ'íÈo-ú_¸sãÞµxäûn?xÃúU;vÆY ùž…ú;ÖèwjÍÒo Q¯+- ̹ñký½àñFþv]ÆÐƒ0q> ,yhú‘+°kñ­,¶_ã•+⩱2¼_)ô¡©Sug?Ô-•›¸Ÿ³çÆ€a eŸ÷„ü“:Ô Ž¼Î Z²¥Ïï‚—{ö.O¼Oýì¼f¡Ðk0¤–IðáÕè܃ ŒQwo}­ØÌY—Ÿ¥3]¨Ë4ûw"bâ+pG2Â~‰Û™ òíà'©»«Ü7kªâ¦Ínfîpôýászm }fÏs¯ðÁ$fÕ ¸¯q¦\‚ßc/øFòE\šŠMã^ 7ÇÜEÅÔä••ñ ~޽Ÿ02b?ñö¯ïéù[]LK.ŽÜÏ…“ƒO×̰Bn£=MÍ&鲣æKÎ]>’Æò¸$œ ìˆz `P¯v Ny›fg¥Î;>®(þ¥6·½1tFòVjðävª ûž‚g‚3â‹1¯ìÆÞCZ/ªŸo¨?=g}gª€‘Ü–ºcá;”ïtúžqâgCÛ€ÚBó©1kI'ôíŒøkz–íïѺޥ–Ǹ}.êÁWÊ>a]cã«¶YYÄyÃÍ¡ËF¿ µTê(ö{MŸkCv8ðvøBp³•Ÿ8BË6—!ç_¿Êv¹oï…Â+8ÖÊã'éVñ¹Ñþñ |–p?»Üqã·ë 1³¥(§ôõ3|Ì‚: þÀî{Ç™©ÎÒmÕœ†‘üŽ™oËÐåT?[Ôpuæ y®¸1‹è» ;+\ÀçÛû [ÂψÙU`»aæM3inoë‡Ñ ^*Å~.á7#ͪ)Hl"7b_Õ”™D¬ÊÙóŒù,ü=±®â5Ç¢þľ„F)g@ºFzÿs ° A:—a‹r®3Â{¹Âóws<µ 6µåœÁõä> g¥nWÀìˆ=©­‹£›1)ù¶•ý‘o©Qs¹T»Üc¤¿ÊþÞÎgp¸Sß~6?Gü½êÚYÛÖ™wžôzs^E9@δÄï±AjCbMŽÍzÝTSOý~ÙñÔ½­Íž®àVý-Qï¼!{Ö—&w;zÏš?¹Êœ\«uT¸S:oܧÂgr^5÷Zß·Ï6cGfÁ9h±KÎ>æg¤?w‘ÞäÄæ¬gxó·?ü²½ÿýùe‡›¿{ùÏþã…ßãKÍçÜgã9‡ºÿçvoNÎØÁW-ÿäà³>vóÀΪá©ÿ¸|âW>§ÇÖÃì ÷!8¤þŽô7E¯và}›Ò#Õc%»_Áþ¢¿ø’âfzcŽ©5n˾­d[‹rUjHºŸQÏ!”ãÞb98-äçÄ,ܱ°áY0†˜‘œWö/ós— ݳ‘ü¼åäÇøŽ‚-#NE3dhý·Ââ.[|L}qη–Ä ±³©ë~(?²h¼Ø‹ò9¡¡N¿Çúp7£¯ˆ\aÒûûóÁ¡Os"wQþˆ>i÷LpêBã_ë6ÑŸE-FñãÔíg®ÝR|XôL‡ÆéG‹ €ÅÒÏל ÝY´sêø¡‘=½¦½´ fBïNß¡øÖ1³KŠ%+öš¸^1ú4Ì“š"ŽŠú¼Mõkq¦íÏÖ²e#ü(x”ðÞàÚÏ£Ä9ŠïܪEßçqÎÙé|¡æ‚œ—É^bzÿÌ/5.NrðUû»Ê|ñS/ök‚–¬8-ч2'ÏÇïz#éGñ›úéj^ÈØl/p§°ÁgZ¿4ä¶z;à~ƒú¯âžI{šqÜõšªß­ gºJ_;fœlë5Ô‹™ó¨åöE1qæòãÙ›HÏ5mÇíáÊÀ._Øû^_ÍHM ”øYæ6D±¹‡}/:ÕÚñD’»3Y…SÂqçŸuÅìu@C;ìÏSâ¼û÷ CðóAoâ,mÂà\ȳ¸T ˜#.ñ; i žPÌž·¦4~Ǿ|jãÚ‰ D­N¸ï%Å྆ ãÍ©sˆ,æÐÌ­Ù9Åá‘“¨žWÙ“A½Ù9/79îà¿¡‰åçW:F[µÛ¿ãš5 P옾]we$o¤+£ø3óMbüÈR~{/ÕŒ <Îm—‡+ˆYàœ9Ùã ©ý°Xbâ:ÜÌÌã˜Ë ßÃìgë}ʦÛÔûºûËÏwýô¡¡ÇYÀNNýZ¿‰Zͦæ½S ´œ‡šˆlUÅó¤6O¾õb‚«RË/¶PTî[–­Ÿ*kÌpÅ]œoó ³–µ”+šå}!9z]¼’5âŸ#õ Ï­ß¾ M߇óÄÝAg }»pb_zGä¿Khæ¿<ž µUì|Ëw™Á3F‰Áe—àdLw›kÞ¬­¶©aq2– >ÏiÅ7/àgz·äZÀƒàltÚ‚cä¦Ç:¿W³öEŸ+øŸ>7{í¿cí#^m˜G¼79¸ðHç0tõ¼1>çŠìö¥I5ŸJì«ï)<ÿõšLþØFÄ9`9ös1Ã2jËàÒzWÿ.â[|¼ a÷cãÌÄ".ÂÎÒ¯)îgúžè©¹XÑÙ:’4ÃáçÓs©íð ëϱ>Ûƒµq,^€³q÷¼|~ÿø³÷èàWâ|qŒ‹tð+ùñy×Ùò¹­n›…ç©Î5Æ®¾ f„/B# Oà%óLh†[áç–Ò†&÷WowjNÀ-²ÿ?Çžíͨ˜”ÀÏ.1»2p¯ˆ‹S»Ÿ3=¢Ñ<¨®Ó|}ävöì{ñgœ9 bÛ —~„ýÿ;ø44xÕ‡²N‹Ÿet˜Àaïê_®”Æ›+C ,FbðrfØ&gµ›¹Øñ¥ã, UÃÆï€-R_¡fÍ !ñ€²öÇùVMœœ&ï¨r‡^|6ÝéÅ‘fµ4̺ÅóÒü™i êÙQNR". =惃ɥ7zMvÇUï÷Zcô­^(øM‹‘©kP÷ŇÛï›oYÆÜÌÛÄ9œ"~¼YK,ç(4º*÷‚ÿ¦×ƒœö¶Ð-ÂU‰t~/–课Rè‡á=©ÃƒEà ³?k=X§ûºÄ¤šºË¾¶VgBË”ÙK=kvˆùÈ>>Äaaoá·,ÅœSÓaŒ8g‚ ׯ͆oƒK2òõAu4忾wÇÎkð+®Vø$pžÙ?{oÙ…âäß«xËÎöû4Ç \€ïõ]vÊ©“ÈqÁ|d—ÖsëOË^Áæ+7Ð̼{$;Jï1A¡G÷,1aø(sÓ±@1k]៯¦ÞèòûÛüd[w{fÙq6Èñ‰EˆçUWªôÐèþâÜ£ÐXwçœÎýƒ­?ÜÕϳ†d÷ÌîßmVWžSbîÐù¹Eý1¡&}æSá.›NimØnèP—&ÇS\è¹¿Îæˆ½£&ç[j¶5öò¡;/9´µýÝ~æáßþÁ¯¾æ3®YoßÎkþøÏßúñß²û©ý²[ó¯øÁû<ì O80ÿN½çä½v„ÊV\· ƒ©>˜ª\¡Î÷猴‡›söJ±W%•ö‘p¾èùf/¹Ý šŸlSÅ6Éç».ƒSîÿЋÌY |ì:8ö~Äí£®^ñ¶ÎÆ£¤v~×Þ"ÏóÆ#étøé¾/¸«×§¶Îpô¿¿ë|6fm¸5së¡®ÓzZ-Èg¿V곸ÁÄγzØfi· êËØì´^Å?In>[vÎÏm³9 œ–‹A\cøi I½Ç•ÎæïR9 ³*Çej=G^€¦ñ تýc¸—0‰ÔDëמÙjÒ`ëÌ‚ˆ^äÀa›/£ÿ;|¾ò´mñ~˜õˆÞ5ŒÄç£ÿ©Í€ßÑ“/åùæ|ès§Kǹ'zÇ+È©‰w–ßó«ÐÏ¥†¡\zKÕý™àÑ(?)ò É7BcÆüZVCjï\ʾ$¸nØxjüz®:¨_¹Ïj£gã¶6çs;85îûÖðáî³´ý¢Ns¥Ó§‰ZþRœUl¤p¸Ô÷ \ãºE‹íÐ:l6–ÚŽ0~8 øàU‹áb^fwÇGù³¢Iípjúy ñÉœ\çsÂN.’[[O‘ë„/!÷¿›Õ‚Þz‹AŽåócFÓœ=g×*kÓ0ˆè«nNÍåÖ³”Þ$5Ø»I~Tûû ì_÷Œ±îàóà9]l2épþ ¤óÝÌ-­=’ÄŽh=€‡dÎÇ Ûðø i?•K‘Ãûš©WeÔ»À~P_QS‰=ö9!Ì}§¶4 ªE ;[ÃïCS~]EêPÄ‘£úojà’1ÿÍ øÿÿÜ´F; û#UUþ“šUfß/%úê÷¾dMœrùøÒžýÙë^x¿½—~ã/ï½àoþËáýþø{l&àÎÇ~øàðMyâ®}çÍWÞ°óÔSçvOÖåà[^û™¦û»øê[}pöWÿ…Å“éÔ½ÿ¾ÿ‹9'Ê….,£5Iòÿ"<d}ˆýæÐÅZ€ƒÙsÇ,„À.Äñ(}L߸„û>Xk=‚·Û¿™1dv {¼ÙÍV±Ïc †oögnH­^yD¡¶EÜMM:æúÂIÝÈ^EΙíẻ٤v[­)|"ø¸òÙ«Ô?Ñ/§W’^JÝçQ5ž‰Ù©Ë¦µR.¾Ã¤ö·™:_·äÈe]Sïµ@_ ‚·5Rí<‡¼ÑŸMý¿ÄÔ¬=¤NN_ ˜]؇˜úà·Ð#<*ʽVo•ý™ú”/e·) ƒÀjnXÇúÞ‘5£¹ë1Y×ÿ^¹h Ü.Ηâ“ìÁ‹u:}¾è$4ÎôiòÒäWñ÷`?øWx>ª3VâΦìN¡÷ L‚347­âmzïå''b­)q´ï51„bÅäJg“ÇÛwÛ6:Rïà.)>„G?aK\c?êj-NBsc_=0[‰ÏÚYíîeU>áöÐ~¶é|í§Þ“žÓ?Óì ujiög‘GܸM„У༩XíQüÞÂÙk=Ô®Owò\ 겊·ááŒç5 á¨Í„ÝÏíbŠÄ»ÞsÅÛ§úÚga®6}0KÍ¢¤î>¨_´«5­•SׇΘ‰øO~k[u#ê^ØúÍRûPû0r®Ì.£'ÁŸE}èã«NïÏI½bS3ð:»SÁ6…ô˜ þUqØFÆûÂ…ŠÎoÖa„wež3D :5t_dÍÜì±#ù,6¦Å+ûp ¼s»â׎ÂQ˜IPõý>ßÇ>#44öKãÏD"öÙçü°¿©ß \Ÿ¶&—Uâ¹\ÃÔŽÓ–Ñ`¹Ù»·î~Ó¡­ÇþÊ£ßñù?zøŽ?w^|æ>;–×î}ÔÏììÿæ'¾_Ÿñ´­¾äÓÔS4É__yŒhØ€¸rU9¤a k»#Ìä›…{ÏÒÛ: }Ÿ5uøA=Ô_¥Ó2q·´þä±h¬–©WþžÙuƒ8cð „W|0ÏÜÙ¢{ æ­<<æ1µ~ÅÐ×VMmMìØúÐ}½ Oy¤o]¹-v{¢Ö§¸ŸØ]<–°›äI²ã,ž3yûõš}‡FJ‹¿÷óYu¶×:ÇÙÿDŒØé kð­oÖPìîè{|=Ù?îZ ¼6™ºÀfrä‚/Mþc¿¯(n––AôN…®HUìçƒY´³æ;‰×çwŠ:­Å.øxgNÎ$ê9Ü iÀ³…Á¢Ë­Øü˜Üblù˜ßùä_0·½†Ð7ÝO­ü7<Á®®4Æç_ ÞÈù„ÔØì.(žóøÛš"Á#˜Õ£.—4%ò,KL~%ߢwqÍîJo✽e¡ ÐúþÿQ/¥oµ[0Ímý 5ßÚjÛÄBUuíä·èüØ;ú\T'MÍ«öghÿFcÂÇKŽÈúm¦fÞifþUáïwáU¢[t§´®Éll½Èƒ¸?1%gæÛ«&³Î›oìY?SÁw„»±wãRÚQ€×ðáNÓ´k{Då7â'\š¤6ý8‹ÌýVÞm?³nñ s 6ð‰•ú!±;±¥zÒ¶¢@¶Ð¾o­x¤òûÔ2ì¼ÛÌYî±òmÝ%›«m¼uð£mrbz¿Ñ.Ó^2O±À±;!Æ7¥Fj릚všø{?¡ ÎZ(&ÅÓ¨ÄÕøÃ^3Óò|ì‡8µÃü~ÊþWö=æþGÜ}:mä íAaºõõ_~ñ²§/Þû¸Ë¿ù‰?uùd=?ñ¦ZŸÿÉsÜ|åáLø‰WlÊØ—úkÓò·ç9‰ „mܲxé'¿žKoËÙ³ôÔÖYsŽÑ”éx#ytà Ñ»v^=ãòAS³Ôí ÄIû€¾ÙÈÅ_Ç߫νºäMØ?å îË‚gœLåc•¸ßöž¹]ÊÝÊÐæ£a¶Ô†Ç‚ätŠyØsø’‹À’n$FÌ9dKq-UsOÍÐ.ÊÃ3¿¤k F½‘Gh}I9ÿ?\YuzgÈ!m¯_®À¨Ñ“—o‡×U„Óf>Ï!òç;).ÝègnŒƒ8ÓKqX¢n|K!œ_ÈíĘá|Ëï†åuÔ£Wßó;Õ¥ÏÁG3)`Ô®ÉÝà=ØŸ¡Å²Ù´<Ñ?…S'—z ¸BꃵlFo;¸HÆž’ƒ¡ÿ@ý®zóð“E1¡ÿÛþÎrWÕÝJÔê¢W μÕÓMŸPÚ+ ´Ãà5‡¾ã“×êÑðœéüGh¦,5§J{‘ç?=¨okh:-#84ŸË™ïñóù.34œã§šù~çR|R+zɉ5ý9…%l‹+V6Å©÷P\ìøŸÎuÖÅTϨÔÑ•7”ÛºúÄQ›û—ó Ä#… [©w–w޹²7‘/ÂÑkõ§Ö r[7^¾h›z?ïƒÖ˜¦ ™=NÄ#÷}ýpoçПr»(Ü ì}ƒ*^Gæqv¿¤‡\[íÜë5“rØœÇöC^:x]䆼‹ep³²û{F:W³zÜúXî®úe+ÜhåCîûLð¤ß•µ8›Cêügž(»Ÿ9ä :ò!ú`–ë¿”v!<²£Ð@«á›C_Tçt~I¯…â"â#Õpo4³›ª0£´³ösâP¥?$îNá÷¾Ž}_ù¿ßµwzàŸ~¶ýÛ±þ“wÛ±Þ¾Ûþ……aQË:^'PøvQ/Ÿîô1k†mœÈóñÛºþ¥¢çYI?ÏŸ3:tQ †4R·#veÖ0k ÏNyIá÷æ˜÷œŽXÑÐiÄåÜ¿ 0Èìõ?Â/P3§\ú¨Í¸(øΆöÚ×ýbíV'‹³½'ýdÁ™oóg©ûØóÈ~úÿ`ž#X}̪v®ï·­ÔÜì{ड{ŒqgèÌÇ\‰!ô¸jçóªg¢Í\À;èµ³Ó=©ø|é¡ÃI½@¼vïg`¾ê6ùýóŠ‹×ÄÏÄiðÂVõr.û^­§ï õýð;§‰u©?Lø%q\'õ0q§àvÀ—Ì™ðƒ8Àsê]šèǦ§=´6²7ƒ¸…<…¤Ê¯àGTåKн7ÈýèËñ=Ÿõ÷3þý«·ìžÕ—í}ó_þ»Ë_ùUßzùoÿòcö¾ÿì‹/¿ìÝ»kgü©9¿óUï¼ÛîÉ?‡ç?öÞ‡ßòÚŸÝy÷ÞçZ÷ëÉk›×gµ…[_úœÅcïÿl›€•?·tÿ…¯oTðõ¡iö¬ÄA‚k·Rœ¼’@swÄ'Åùºˆšà¢Øþ¿E½’˜AšÈɋ֙"–ʾ ;ÄØ`ú·™š›[É3†Ô`ÙG·ˆÇTˆ9ÃÎF¾¯xÖu9—ªIàÔyÚR:äÔDìþty&8$¸¹ý³h1ü¦;RJ÷£Çïü»í\Û=°ïÕ¼ìÛDÆž;tð°ÏYó÷Œx44„ñLŠáÁ‹+š×òSœortXÑ4Y‡}tÍÙi)þ?õqlíÖŠM*šC]½sD³[5ŒÌÿÑÕßÝÉB?:uPôÏuF ¾€¼^öšºÁÄhœ;ùDçF ß*ô®Z]M¸®j&‘;Ú¹4?kë Ö#_æ(óa´>EìB, ^üòà‹u99þLú6[É#“mÊy¤Ô4À_±}á;÷sœÅQŸ`‚ã$VΜO¢N³¦f—®™=udìç,.¿ò}tÞýó„Ùm7\}¨Çœp…Ðã²çcÖ*1±0•uÄ€¯#·¨ƒf8ñ< ="á³òáþ¼÷öœZ<7¬9—w}Oçq¢ëüÂ6rö8 \Í".˜¨_Q[ ÓóP{€+HÝ ^+s«ÖQ8wè,¯þ®JÄ{…ïn} Ì7šþ‡63l´Ïë†ÄŒWô)ìœÚZhζf^K]`ì9ZHÔø±³8ÔÊýË,]S‹ª¼©×:¬p“¥#ö»TßvÁw_E,sŽ{¾ÐÚ÷ÄûÄÔdàqã#áó‘“¢ ±ÔÜ7üXÇ©C§5`gÌÖïÊË?Ñ4þʾÿÝ—O?ôÐfú~à‰Ÿ¼{é~/:xÒã_º{õàîöùÖ?vï·¾eýî½_Y<þÿdvký†õçì€ÉÄ:y,ãë)ì–|mž<Øpô·ß°Pßç°†_¹ZU?,è6M¿ÚåZ~>ñ#u_ཌÜ5Ý7´‘ÄaIÎËŠý—o¬:¯ÛêÓGŒ>î¡òŒsÌÏ!7vL^“=?6]~»PçGC?üš¾.ájÉûЏ.uo ù-XYpæsy‘¯Ü1óð“:Ìvó¢ç îVð_8ŸŠÝõ;lj}’³Q?Q\º&ßµó*MJ½GÎHtûMÜ;H+Y¹ºŸ[x¼<¾„þ›ð+1Oþ¶6»Åç¯Áµº«VxÖ¤R;DzgUºéÙ¿F5TŸ]±S`ÚªëñL£°ÝÒõMO`ª³ø‚gRû95ûáÍ;Ô,Ýëhý2ßsÃ…Ä­ÊWÂ(ü¬Í{¦Öóê„-GŽ>0z„¡Ê¶Dÿ ~iîô™g`ö«éÅz‚UÏÁ}”?ßâÎLô#Ó‹«ùßu–MõÜÁ—²³F šoÄ슅ÙOt3³J­|G½Ù¿¨“ÇóÈþÊNଃúðˆ}dÓ=>Ç« Æ¬¼¼†ö™?Cö!Qã§jqÃ3k 1‹âÍÕ >6ÂÖËÎür[Å3£ê¬µÙ/js~/&´ktÿªê9U˜f9j³6àʬ¨ ]̓Øþ8‡~g.F§»—s¤Ä5ñs+þ}Îêï¥?*zw¨·*ªâù¸<âÓ#Xáû*M~NgÓ!FêpÌÕ¦zzggO9VÏ>õhÏCrN’ÎôD=™TØ/8/—ä‹6RMx§žacÓWS ŸÀb7S+æð“ª)ã›ìgÌ|ß—üû=ç þÚw?î9OÜÛ~Ûgy?¿Ù½[_ú±_÷Ÿ´k¾øK÷¿oçÂÍ÷ܵµ–¦„Ϧ4¿Ž¼û^¾îô^Kè–ù¬æ¬Ÿ,ïÊ'^ƒKÛ9W:ùÖÒr£nPÉïd#ÈÕ’Kv¼Ëœ‡&<&1JùaŒÞ#”=˜Ø9­¹T¥6Äý¶wSŸ`Q|H—ÛúëÀÎfõuûÏÒÄ"Þ¼kœzoÜ­×DŽ>ÕGFðÃ€Ž¤iMn-{%’ÄàÔR±£Šé¿(Ú;j­|•<;ƒïìmú >²†mƬaqùÈ +~fPXý³òñѯ%ÿ«zäMè À‘ÍÞ¹ˆ=NIÿtƒzLòœ”¸ shÅj¢?î!6^kì5.°kð:áVn›À@‰­zÝâ'Õ$=ÿ£Ϻ¯÷ ýsôXÄÜÕñ¨¯º¯·ïCW“s‡¶³ÛúÔ£Ç,ɽ‹&n†G²Gkh:ÇSp^Gì #{yu§26µõ‡AÝ®‹ù'ü6˜ù°Ý_ñ¢KW[÷r®LjYë^h4©ŒZ{Å,ä)䣸ù«Â:Èÿ'¯¯‹ókÔ ¸?m/‰oíÙÐRV/Wö·wxŒ×Öe[á1dœ<«Î­™—õŒæo¢ñÀÙm}-ÑÏ¢˜B5—ÀHš6öeTL{NyZç%pùó…¸cÉ‹±wæÜ=9HÇð×Åù¿F®–½¸†Y6¢³'>úé̳­?L4øöq€]D} úåɵ„iLZkaxMK½RU>¹“âoT“Ô™¼˜µö#iÂáÿñ/˘Ý<ÁeTŒ©DŸÓíÌXt[)L¯P»†gÕ¸×yÝVº>càw·§¦ÀÜõý(ήÿ”Ðð¦Ó#wdܬ^áÓßö©‡ö¹¯»ðw?µwö0´À³ß´˜OWŒ¼þ¯Oúã5õCâWå–¾½Žý‘úálu?ÁË´>§©ñTÙÎ\ö‘;ÁƒÑ;®xêiàƒ³8®ºŸ+âÅn«Ko ÷¨ûñy¡>{b~©¾þ!ÿ_jM/¾ Ž,Ü…!ñ½ ü¶ûŸÐ$ºÄÌðþáìZ9erà„‚—Vr Ýí5utñÈ·‰E„“­U#HùG®óšk«ýȹ#ôÝ«¦›ý%ƒjLsprª|‡ãXŠU'bLÕ-௘ÀxÂ_l­ƒãŒÞÚVeÁÀ„à‡ÒÖûȈ{††YüþQ7ë°âlêKq|™6k,øÄôæpñµ:Ý<¯˜ÁÊ>¨žZÀ`ô#¿ž;ç‹Á™‡W‚¯Ž¾‹ÀGUÇöçÅï‚CSÏå÷ x†|IQìúäªð_ô½än׌‹l↓ËW:Ïkü„0²‰ÞdñU*ºªâäU0L{ùF4õ*3]Å1WÜx¡Â1¢‚—tkˆo^‰CJ¿»ú•6²Æ%.Ö$ì¶ðüà4â¶Qg¿š9"¶CÚ#ü3b…Y<|ì±ý[uÙ‰\¥³YÙÛB<.Ç^Ú‹¶®qöŽëë¿í§÷ì»Þô¿`÷à½_½÷=·}ÜÞǽèE—ëô7ï=å¡_´{ß+_âœ@› üw·ÝO{ïkw¿þ‹ï³ñ±»'gdñuð‡Þƒ øÜûe÷yè3ÏuV‰cí½ïê#lÒº÷½WiT¯S¥¦±}¬A›ù\Ki $Æ ŒSð…lå¤<ÏÏ—zx‹ê³Üï\?¸yûÙ³EÍM½HSøâ˜Å(?ê1¯zcávŒä»öžækÑk¾/üë™6 ÿÐÏ5ÕçU·GŒ ŽFlÆÔ³6èW[ü{uôõ×yK-*p<ðuõXÓó™=Ý­î´åñÜ_>Ÿx™}ëpºmf¸R{†ÝꘞOœ!ÕÜ*¹gû3K™xRç‰z.ؤê¡É%¾-9·ÀöÇÎEë„Ëz#{RÄ}YS“&î›6£çtôõ“ÌM Xñ]ÎÑ~Œ&±¼¿pp½w®€ìbì“Ý-zê#Ǻ€}ôyÑô‹Ìâí뽘—SÀjZï÷iü+~›ý«ËÔŒš¹á¬Zç¦ú×éÇ„ëK ‰ŽÃRšÄdâíùm˜[ô“HgcMÝM r8ûn*ø˜j “8f•˜Z=.hmËï¶8¸»»•zŽjá¥ëõñµå»°KŠ+yŒ°Á‘Zͬ¾±Î·ÊŸ&6ôïçR}Âýz«¡eNã±Ð9£útƒâûZüémb¼mêˆÄRKõÿ)žÛÒlÙ«n_cŽlô»±n䬺¿9{ÜU9:_þLªkËÖG™sÅ…iq& X·Ý=ñ©¨å¢o!þ÷%v‹oŸ¥µ„/Ô½ªÊ tÆ.·TíiUlÇùöç´çãþëÜÃÝUô }`qÔ‡‡¦ALï)ñûü«è%Ç•>x2¼ÃÔR=Óúrˆ§Aó½ØWðˆ“øÂõÿ¯Ü;|Ö?ÿ·ÃW=tuðÏ}Üî_ðêÃð ;w<ð}ŽÁ¿çs>è3€ŒðÄ÷|`ñˆÏýŒ;ãæèIg"&}Þ,nSú²ÄHÔyïlˆl´rä˜& Üî½°•ÙRì)±½ð½ÄçþO-æŽ9NJ®†48 ½vðéø^[á[_1_Ào«ÎgƒZRÆïà¹p‘Éùã\8g³è^€Û-×Äù^©=Ïà³M/6rå‹YËg+¶?žß û,á'i^ZÎSm‹(ðñ³Þƙ徑?SOeÅÛÕlÁ%ˆÇÁï™-H¾²Ì¹˜[ôê®ÑÐ:}wìÜŸ£nN©lO¥wcP-AûCße%¾³Í˜qM=y–¸ÓÇÀ½æžÝ&M!ÛW³op«©Ï€SºV|$Ì;ú‰eó :`f/t÷{Aÿvhz+Â(•»Ý´>R_¨jOUuâ²)­wÝãB]w–VWØâ›˜Cö·Ö»)‰9œà^›M›½»Ôà –›;Ý´ÆÞÈs¨-Ù%¶J-P3‰nƒ Ì1f2r—°ƒ½Kœ)þN\öɰaÿ#¶¼Á~&°ÑÐRœëkƒ¾ï›5Kë(õ âþµºÌz2üìÑì0…ªzebȃj<›âºªT”ÛgÝ”=‡·”¶ Xþ >=ר j,ôÁ]h8Àœ¾Œ[… Oì›jÀ‹Î§ »”ÜAjJÂûð)ÛCWçi>Äýé‚ܵŽ~¨÷Â“Žøà”´oð¼lüà<§+Øßmθ\øþãÜ/âCÅçÂúöáÉlƒW¡#Ìï“Õsæ9]Ó9‰9\öÝèìlfoø~ò{w}ËÁÖŸsùŸ¾á¹{/ø??»÷²?âuÏþÝGìýÂÆïýÝ×ýó®ùwÞô¦ƒwÞça»Ïî“vÿä-ÿxø¿ð»¿|ek÷¾ßò¬ Û§uýßZ›~°=«i3œ¬'óò\Ÿ¬šŽÀi²w4¼ƒ\fgÖ¦æ:gXñÕ%âOùßs©c>ÐÏlè8A…¾iðe [îÕk@ YÇÙ`Ϧ‡(êȸ¥ÃüóÈ)„ñÙÐÚ0Õc4{d Ñ Mì;±(­SwöÀàn$^\µ3pmê>Ÿy“r쉉u²_õ‘y¢ð6ôOKh`m ;Ré¤Ycë‹mRüD `¥ßçPé—ïjñhxûy¶¿'þU^ïçoN‹ëx^÷j)-ÝùqÔ©9õ€Æ¹jýpŠë,íSöIX®ŸQá´ü!Åþ÷ô¨(¯š¢fpK%¿³ï±÷;’ þj©Ù{ªû¾Èp çžríÒòí°Qì šÔŠé uFÕê¤v-{I ãÃ>Óú›å¯ ŸyòšžKâ׈"fQ¯6~"¯‚«|køƒÑ+FÍ‹:‚b_jPÔ •ƒËîM×#èÏK✽¢Q3÷Y:nYÃRFæÊÿ“Ç(|OØÝÖ]0›à9·Þ{ë»Ü>ûÉ¢7$fäa;‡eÍ—9šÝìï¦ØÖc”Û¤mtäv—ºúð†¸“§st‡&Õ5½*-Rç4žvÆlp&F³ÿøz[Kå„YS ë‚b§˜ç†=˜°x] 'æBìÞöoí{ûÿe—žuxw½gm¾ý—N}ùáûü“3}`wóäw]ÿÏžó±÷¶åÊâAG΢œ ?ôÙêRºyÂÚ›ºÙô?ØSÎtrÊé÷l˜Â½×ävÚOô=FňÉçž_—Äoƒ4÷áÁ-5§‚ý²Ï·ýŽœåÔºÃÐࡳ¶6–ç`Oˆ³ì9¢ñWÄÍó~XÕûá™ms_™?§F¸È$Lb-»3‚±Q¿0[C,ØÙ¨íV7}bú—„ÿg½ÁžW6´â[•_øZ˜ýnº¶SÓœGýÀ…»G-o¿IöN19¸¯cíÄøÔ È]ìg쌃»*N£>™ölAùÃÔ|E›8ÜE[,jŠª‰å̆A˜¥fŽÔ®âçŽ3­göý ×”^ƒjð¡Ç¡÷öç³|ÝΨÝIò)r'å¼’;ƒ(Î2ÿ²w™¼Y âºÄ±à ÚÏŠ&Qd㥅r ÜD÷ çª u;6Ç-æKLÜoݽÌí©÷`¯´‡hw+æ|ž¥÷e$ßQ-¥Pc×£¾¬X`ä®›ÓÿCLF—ØEëDÞN=yMÍßÉ3ÙËMÏ빉xLxKÿçœÿu°ë;Mhá“êï:óÙ»ÿ»R³wRܼ"®„·½¢½çÏp³AÚ Ô”9Çò9~Æt&Fi8[àÝN“sÊ~g•0±•ôk'žƒú¸Y¯ã&,iÄWb“î2Ã/êhÜQêIî”1vóÇœ;x=•ÙfMûZr1iFÙ>›}Vþ3òŽ]äq‰8wÁkÁŸõ\þ™Kõ'w÷½]M#yýsÔTÁKªðï ߢsÓa9¯+u³å¿*5fzÆT nF߆ÇCð¨Õ€…“ƒ˜¸S:¹¶/æ—‰‡bÆCäÖhCñóNbëç‹Z° ?]Ï9þ˜>­BmvnµçmìÓ¦æ'3é|3‹Ý9¾ªëp¾Éw•«Gÿ6”â’*&×¶`>¶Ý»AºïÁE÷ŒÚð¦úá"‡X \#bŒs‘ÃìÃõ§Öá?×8žQçCótwÔÖL3*¶{ŸiëÜ‘ShŒ¬Â§Gê®?x«ÿ=z ·I#0öù‚sŒ“ôóa|¿-®4û¤ºBbèð¼¢®¬98tp+ÐÎ~ná™ ø¢Ê•ŠpƒÔ1ÓÞJƒ3r4åâÞœr‰>9¼R|—|Í„­?’æÿ -®îã1Sè5Ïldn%¿„œæÔ”Ê;;’c“ð™úÙmñëᾸ]¦ž$@ü[Áž›^˜÷!ûûã'…GøÚ€ùq.ñ{àû:'…agèfÅž£ª†_%ö(ÁK ‡F’l…êØÇ‰»F¼F:õ‚8|Qvϵ!d?Vì­¸—‰‚û+¦IÛAÍŒB1tÁÎËÇæÃƒ;¨ÎÇ€\eêyž²{µ;O+rxŽwk8ûõî’«(çL[ÉÙëøÌJýHÅÙš‹]‡MWõ!(žûoÿ_8,w$ý-=¬-·93M}Wa•þŒ3v¾úvÕ#š3!S Çî ÅðÍâîmÀÿM7~=ì§ýœëãýÂÎY›õ÷¤‡þÌÞ >f÷ì3þÇö¾ëg~d÷·?åçwÿo¿kï©ù+Ãÿ6è¿ûþÝ¿8óÑ»¦xéž¹°Þ@‹ìl[,`³¥ËRÓ¬ÃÖ‡¾”iØÙ¦WjpŽÝ ký)êJÉqø¬ejlm‘£OÌ«ÝÔ,‹3šíA¬9¨žÊŒ8ò?8p‰È{WjwÆý ÷c§Šq qªüf¡6e|iòüx»=‡ÅÍŠÉ*6“ü€˜„ZªðOòóž üSøƒ4n‡íìšz\ÞUöß?Ã>Oø\r¢ñçÆ÷ >WÞ“|›À”nÊYR­V¹Tà$ÎÏžàD©?LX·æ,ðA5l³a9‘›¥žgÖÍçׄ.fìYpù ³ Gh5¼W¤NjÂþŽá_¯òÿÝ_€Í«ž©øÆùî}Fâor,ÕŠ2~÷j|Åär/¸[M:î&Ú±² `T•8ÔWáÓ;K­ÞQh£Õ–Gl€Ùñâá3ÿ4uëÀž¤¿×hjØGÌà‘RâÉØUz]¤_ ÷aË×-ÔWâ÷ÜDvß}¼³¸¿ñ=¡EuÊý¦ôžVŠÙàø {º!ª¡Õ.Çôïû¼~à÷XŸ V‚~‰xQ÷bݨP#PZeþÍ Ù]lê¿#}Ä;ÁØ”G03ÖÏ4#ìw\á}EÚ™kðð!{ãÌ}gÌ@¯`ª©“Ãú;K)ó![cj«†“*7á«*föw¤¿S¹µìÍÅ‚„ƒ§»Vtö3®YFo"=geVöŸ}­bÿûy”ös̱÷³ó‹í¹>çc.¥aÙÔ ±Àà<¯6KOù:ú§¥å@Ç#ó)áh(o˜¨ƒÙß÷1*ø68“æCNŽÆˆßþÕ1uO|7x¨x›ñóò…8D@ŸO‘¯†¦¹úêîõq—íŒÜ÷Ê|xúA÷¿ü®§~úá‰o:8ýñßuðÙ?üïì»~õÀÎàW-ÿÃÁ½žzùàÊË_°>‰+Lnýsw_3‡×m¯Å–'YN#Û—z{vþ4+Áëà2ÊëÉÁ·•‡¯Ðýâ÷9[ ç‰3 žG~ÔòШ£Wv'¿ïq¶òŸÄ”·UÖ]º!8ykÿ^{Ë_éšsölÄ›²­ pyâ[ù‘JßÖÐõ÷ Â×èÌ©*v©Ÿ(7Z` X°¬ÐäŒõÜ!ì%ö€ú/ø%<ç£Ð!žˆóù=a·…\I¿s7–¼Éú~ôêLðekü÷eçýχN«@çñ.|ŠÆõ<&‡©ô¿ßÈg‚Á£²ûæ¶êØí§üd•e¡ö®;_õ ž‘^¸½<ª“bkl9½÷ø.ÕI‰×ý3{Žÿœ½go/ƒÛ”³üT%÷¼ßçì|†üÁ$û¸¦&5g?FèáÚŸÑ¡þ3_ðž¹i lcëÀ°í÷ìïß{O´â¬)î™Ç…prüÞPÓ:#\âóAÜ&á­ØTòßÓxNêôhãxOâd0Μpb4EKç ¸«f_™}Üó.GbvÖÞr r:¸ßçcvØ\É+TªÂ(R?kV/|Ó‹»äÜIa"Ä>v×`¦Õû/üzçÿêCçà_ý×~ú³?ïðê»õ{Þsvÿº;'øÜ—Zu`>ÿ¿>éßïü«?ú‰õ-Š ³!\õŠó%ØCÝ{>¨w{FœLM‘¾Ua%rÝcakî'<·„K¢;3†í¾B^ævý¶œç_8}ßZñß(L ,Ksv®`G0 å¦5r¤Û¹ãò¥8‹êÑwþ‰ÇËœ°/íñºŸ344-MjXÞÿ‰m'ŸÅÝTŒ\e•W oHÛZ陣ïB²Û+}Sý ErLÅݶrÖ¶B¼ø³kñm ö/îE̮ճ–®÷Mù]h‚YSsÐ~ù¹P}+í®lbÇ!~žTпŸ¥ún#¥Ùutx ³ýþÚ}Á·‰ë~%äLÎ`Ù(ØRê²GÔa¦«gN·ûœóšK¢{ ¸g_úWQßäl×ÀW™;.y³%Ñ÷Hì78¦OïOèÔp7©ãŸÉY0×8Ƕf ârÒ8 ©E-x‚ £{æß×Õ<·¥ÁC¬?‘K Ëö>”èŽ3£^ÉBoùy31ØüÝuøëR wP/é m°çlUú÷ç¦á±š¥Ï$|D3¶"†hqË%ÙæÈÁfq§,>„O¤õ™È‘•­…Í–èSkº¶vêOŒ·ÕõÃNéNŽäÂ=……«|&õ¤3šÑ€nq¡ðÁÚcï`7Qû¾ˆð=“>ˆ[bÏ©x­è9ü®sZ\CÝmPÏÙQô¯WjiÔÏËmw=¶9 l‘\@5 _ƒÆ9Î3m?OÜJ ü‰Z†=:H]Dw©Š×—g7ÖýŽÒê ¡ï¨ó•\öþGöÛד^ÝA¼ÑA|dðTj9Ô°k¯Î7pWq oÔ}óXõ.Z7ÄGmþAW¥t£~K½©Ù®Ðiµ5x÷ÞçíØ¾üü|Ì΋^ö;÷x×{L»ßkü–çS¶X§Õ¯Tfž,¥K¢µK-[ù둽Òä:£K=ﯢe)üÂmú¦Â¨¦ð§‹àºÝ)šÅ[©R'‡3k?ÏX¾¿ÈÞ×°³§Ö]¿Ò¨8¦à+‰‡à‰úF?\(b9×¥Ý&kùNô\‚Wô}¢êcL›®67ñм®¸ø0b«o†ïX𠊣Rƒ×žÍð ž'êŸÌbmTáwѤQÜSà¤uØFÚÖ;¥‡–6‹ë*¿Ze'øù`R䍨†Æiú)Â"©ÿ¯¯Y7áB`·#5=°ð¹è7ß§³ÎþYyC!WQ ;ži3Ÿ&ÕäÆYsù 8>⃃mMÔá5ÐÛ…??JÝökYS„;Èz+n[(†Z/ã£ã|Dß’Ù€!ë¬×uóãƒëKŸCn(ÿÖæqF wubМíÐFWdP/¯|܈vgãÁõæ~útðòIa U9?= #úƒx&Â3‰Gj¬à)øbrc°TáI™sÑ›LÅîøpkâ0íA¥®kw¼ÇüIÃEÛ?Ÿë¾ÖÞÂO)ÊsÒ7SßVŒ9¡j±Æ1u‡•pñԢʚ5žsè:KÁ±g„#Aí-rp 4ÓçÙ‡I%ò“[Sà ìT6±DnpkjØ).c(ƒô˜ÈiecW:##sÑçèí¬ðú¥+6Qs·ßaVqœ‘ë²—hV=:ŸµÝ÷˜uë}\.g$ ¹K>qõ¥ŒÅ¹ò;«¾£ðÊ7‰ƒžAyÙÔ5½œ³zÌ„+ëpîèëá•Ø§'þcj%Ò?.ÄwÂ`VZ7Ç€jïu{öÞûν÷ßßz¸wŸ¿|ÞÞßþЗì=tç%‡ÿÃõÞƒv¿}×ÎøO¾ëí;öß6'ð©ù«{=uïàâ{nÙµ¾€àIÜjó~NòÙ_ò¾?û̽G®Å›*á/Û¬\­™ûòÐZõ¼´¢‘ ï¼D6i5deƒøxÛx³tnÈm„ƒJ›ýÂâ;5/œ~Ñ£2_汌°“qH¾dÌîžÉ­ìg!î9 ­ôB/fÓŠgS¤³¢ÙsÛ+æÈþaŒð™¤ñ£îJQn2’ÓØ:˜n.y°‹Y}dŠ«™¿”:ƒâLЉRwQö§“ê}ü»™Ç¯d) èc9î4Ñ×ÀPWnÎY_E:E5Þ7|‘p°ÄêõèΪ>±Ž€‘ù¶›Òs‹æùÐÒjybÓYfF ;¢Û‚³s \Ék–¶>Ì{_»ëê#âÅÑúæs£34’{ƒCÎ.ôfö0£Ù÷gSÚÕúÿ=š”ÚHà¥ÌB÷ îÐJùMQ|ÂcfV%¾ædkl,ÜÄÖŸïýਃ´ ô|•ØìÎÔ}¾”¹ø‘÷Sùùc¶cÓJ´÷5|œÍ>|Š=Ÿý}Ó£. gLˆÙŠO‰-› ýÎXßn ]ù½vfBcšúœ´ZÕƒöB!Î¥&;¨ß_û¾"ŸÅGɧVêø}| ~}öžßÐì¤dÎy£Ð‹§ØM)/t_͹¿Ž©/1ù­òÏ`oÊÏðïÁ#°çgM'iø–6¿1gÃI.æ_Š; Ö ž<¡óLî2÷E¶…œämê©5ß8î3˜ó¹æ˜ƒ°€÷?4~N ?v­ Ù€[£;å\NôÒÀtV u΂ö¢ræärFmó6iéâ+ÄIùû3fz¢Ë®÷¯ÚF ÇžýÇ?°ï8ÿ}nùÊç<ó%‡ïxÀW>îÌu‡'ϸ¸÷¿~õáWßòèåˆ/zÙØ< óñ7>à÷~ëãˆÕ™Wt&g‘\¡O±Ò“áUúóv`U¯öËkP²)Uõæ¹¶ýî€òzKFùâò~~œ­0>Ö¼ÒG tÇ®‚­ÁÍYâÂ9ü÷á/ñŽÚ×ʬ‰ÈUnÉøÓ¾žk¥z!÷ÞcGiï¡õJ¿êŠz×}Ù›jŸ F涯tîú’u>+6 ¿(0 Ç­ºoð YÓmù4¿[ѳ´1‚SË®Á÷"/÷Üj©YäÕªË6w+´¼öéÕW~aÌÊáž)W]o ?"”­¿š1ZÓÎ Íao…»¤:!vu14uo´P-gf.'½ÕÛ²}ô*.Àõè$ÎÀ^gÌc/ð ñK²Íóˆë0FÏý…‘;‘kÉn‚Iu<‹Ð{\$û4D°‰ð;UK…^Ã/Ñîøü¤Ú¤ó(8‹æ—ZßxhðXL@^´t-ÃèÇöÚ÷^3º×ó¤Î€|^hIhtå®Ïš/ŸS¹})ð¡'qè4/¶ÝasÜW´D*908‘=kÄY—²® X,ȱRëiÈ~ôЉù<×-ší:NÎ0vUŸ¥˜Ð/9tÄoÂüÿßÖæY¸° Œ lbÔÑÀÕÕŸSÛœ«šªï^‹rjŽeÙ4Ë2QlˆÆÆKt=ÀwèF° åþ~îèi¢ïmn󉽙ûY°øquýçY_rqœéªÜåAuôw„…0´â¾´:®ë¸æ|FrFr)å¶5|Õ…J^y›Ïð¹a&¢ø)5õç¨ïp&ü=t–±‰³éÞ/ˆ}áÇ å¹ýÐïUÙApeÿÙé?œ½lßó©ÏüùÃ_¾°sùÁúšÃ'o¾àÀt}OrúÃßøËƒ!øêÏ|È;žñ´9/[|jÓþ _X>õÄS9/W=¬Ù§AÌO-»áð~÷Ò¿’‡Âólù͆´=¶À9³ÇQû71‡Ú°¸NâÄFœ…&ÜRú!öpxX_æ>£/"[—ºÕââûsIË<û.auú];{™óç+oq¿ÊœŵԤ2/ëê*øÈI=< ÷ˆÞÖ2t:¢à¤ƒ°þMÍÆnÚçg,z§Ñ‚Sßñ²p>ÿ®‚߇  Oùo•½/ŠmÔw…XW3{ƒK¥sÖÏŠ¸ª©‘0‡Hž3Íõ( OÝ*pQ;ü}ìc‹_ÑlÂ碟¢|3q+°Å!0iâî× ì7Žü¹õÈ¿S~ÀžÓñê<ö¹èˆCN/ZòÏà1U­ÖîwÎ5•–gDxëW |gS³¹·Oøglˆ°HlIêÞ â"oJ nÈ:©³¯¸-t zœŠÏQ¥Xê*œž fïè¹]œ…-ðàÄ‹ðMë¼2gL5£Ð‰Vn'ÞZì|Wö£±çÔv:ß5agà[Ã5ƒ[IþIÏþ Ü .q)5†¾ƒ}FïÂöíjäv6з»õ°ˆÃçEþ,,cÝi 8ûFÁÄûʧӛBœéëB\IÏÓ²ëûQnÆçç \3òùër–ójì³ìϘ«Œí¡¿æ¼æÚbßçœ0ÈÀÛÌÎÓòѧ•Óûlk˯üLPãTçW<¬*×'^_ui‹4 Wo4¡Ý¿öZĬ5±Ž3ùÎû„þ‹\™8Âf|Ro§žD>oϨuLÿ¨pö˜3’'¨_i¡ž÷•ÿê>m×þ}á ¿³ó¾êî;oz“ëå~âW‹WžÿŠÃì<˜æývxsŒG¸©òòMGi†‡ÿ9}ë¶þê -ƒxàMZW?·f»Á1ÁÁT¯Ú–î2w›ŸÞÖ ^r8ëÜy0ûYlC«{£Ÿ–üa³`DÞ³ãvJ=æ °1å%£89^·±˜ž3u™A|ì{×ÛíχMG«Îj*Ê*q€b8¿“âï(ï >ªêî•øGq䤸Ñ× ½W¸/ŠM¯Ë_Ì_´;B]‘š³î_…ƒE,t§æ­…mq]¿œÙxFzê ):‡Ô¥Ð8J»/Ñ‚¸œÜ½å¡“Ä9û!g?’6é ^½¡ióFÍŒìB IÞ0¨Oœ9ê !vHlCõ&_ÝËÒÅ šÍpj!~¤°Ïœ‘\å× ü~8oŠc*½ðdáµë.P#ئ‡òHzõ²›Ü%Ö=kj`Ýâ7.Àgd?ó.¶h<×-úDÑÝ-`Bœßãñ³¬ºOÕþøý5N+˜I«Eç çtÒâ38º#q$÷³åw¦N}ëû:V -b^zÁ8[ò EÚ/þÌôÃÏ„ëϲ¾OçwolPM[§}/¥Çr¤®cÿ¨nR0&î¡8â³<ðÓŠU¶ÉÄÙ+ât’ŒÄæªñu×ýB”âÎÅ(Œ-9ª¿òLSŸûS»îúCÈQÆ¡ñ^ þ¹Åßĉ9÷ ÜVÏév²‹Å·ÑyQì—x?™ÕSq^óyYk|âD߯WEqØÿ(5ñ2ñÓÔS쎯UÇ÷Á†'ÛÖ“33dnjÕsdÌ5äüÐæoÐt±ÑÁŒ:åUx‰yÖ>çµ¹g÷õÇïøª½Ë¹÷Áw|ÅåWžÿÝÃÜv.Mß{ù3_òÜ]ûÜOýý{ìžýÕçîþÓô¾ƒ›¾ô™Žûß0~Ò¡iÖiwå$~°þ@ŸMjöëîx¹ç7†E= ¸ ͯûl©uÓÑñšØî#Ä:ˆ« ;Ÿ{7ˆ§Ö–š zfÐ|¦ÏjäLÑ뇯—$Ž“OGú¾x›û߀ÙëÝÞnÓ·hÏÑÛ[êz¯¼wì¼,îÿÐjaôfm/Uçnjþè6˜§xˆàÔ>kSõO¿Oèø‚;"fúΌÿ¶÷–+Ó㣹K“òvô‡’AÌöÑâWòŸN ¶('ôÏ…÷"áÏ‹=§>ÝzcƒûßÔÎ>š—ºÀ༯›|.5².v÷“üNæFʉévZåp ÕGXŒ¡¯ÓfÐ —¾xØäÐu¾¤þµS q+ÈÏK`Ñ‹v|S0ðÂAzšÌ9“3û²–¾Ùf7Ëx£pÓ2¨VŒ¦~@ %¿ë¶ÿ;5'KyçRøâå÷oe­ÀÖí$p.aÉ`´â$†ÞRpw}ÏÐþkNVû3éÕñzÀT\» gXLC¹_â‘û_ËYÍG]¯>¹ 8³íiô‹œNM¸¡õûšÙù_¦Öræ¥:¬K‡ýÇÞÔÎg­ôþ|͵íêxÑ?6îÆäTꮎ]ZÉﬤÏ›¸äPgb>Rö##nfo“Ûoÿ~ü“Ö{GþÏfô,—AuMbÉЩ½ƒ˜;ç•(Kþ01õžQùÚH}l¢Ëó‰i‰iFù ê7y.†äÆf3o!¶¯Ê±ä[7˜OS¶Hbð7d‡üLFµ?h[ñL¥·[±'1ÁŠ\KF,±­½¿?Ϊf…‚x‚œ°³ù•à=?øË‡v§žù#Ï<üÕþõÃÇ|Ýýw¿ñQ?´c3~Œß÷°'<ÁôÍnœÄí÷~ë[rÂ\ûèÅS¾ðšUë¦fÇ;£™åõ0û aLþ,†Ùw!ô\¤í±^ðŒÎf¸Šøƒ`§õü4bÜ!j…Î wÞ9bÀK‰› [a;¿²‘:äöpMÎHO^|Ö¢3Jn–¡AskÀFtÎüÙ¨ À¹Rýd„GlÏ©Zpâ_ö÷pý#w»nÑÇzƒpHÎ µ±;w`Å9Äþ“ƒr÷sOÚ‡ÌÕ6SGÿ81_l¡j)uP¿Ãqp²¿ç±Q΄±w§<–»â?«õ‘ßCë)æzê.סõ!û}¤æ¯Z?8 ºE÷¬’GÐç˜Hàtœñ9¸Æ5ž+ø^öÙÔN¨õב×s?‰)eÿG°)psjÙËn~]g3èµ®pÃä§ë ýèA˜gÄ“álªÏæ(ôQÐ¥öëgO1ƨØ|j¶p?cŒ†EŹYv3ÏðAö ñ]Ҭ߫hú»Ø +xa¶îÂy*±ÕÉÏ-Tç ·œô^î[»ÞÆQ¹e!Ö¦†8K;§ãÁ9Ïø6é½O]¼côÁ’ÿp¾Í%W^ÉwUl÷\—‰8 Γ8ú‰Ûvñ÷¶øŠ…yGGÒþÀǧ6^³þ|ŠX q7Jö޵ß/æ< L½¹Í˜›\U‹õ}4{ÝjŒ[Äç}/8Óƒj%Ä3¶/¶ê-ʺ–°wb[¯¡Õå\جü­7uÿ"}!_#i"n‹+’xÔ¦Ï%wÂ:ø{#À Á&c_ĉ O-èÛÀM›»9¥ÄVÚÓzFsZÁ·„…­ÀØÖ\CXƒtZŽC]‚ƒÃ tz,ŠjhσÑÒcI,ýاËÿÅ«÷lÍžñ¶ûîýø{^½wŸç~ÿñ÷>ú)ÿ´óØ{ÿêá½ßú¸]ûþ½ìõ;'wÒ°€ƒŸ¹ÏÁu¯zÑÂæ`\jýê÷¾ÄuÎÁ¤…=o §éwNÔÉs®ÁˆÈOUÓ]€-bé§ {|Ý^qöi†ŒÎyÚxbÂÓ_Ëß‚÷ôú7p•WðýbVHó¥ª1LÔŸús0Gß[šnÈÄy"^%ç Ë?n¶¹YùKéY¨Þœúšð“fqMôÝ}œ¹¢ß™ùîôè0 ¸Ž¿µ@üÄ,.MWK§~⬭Ro!·Ô;ÈZp÷à^Æþ®÷‘ƒúHdGônô\òýûªÏ„ƶâ®Â#·šæ·ß'¿ª~MêìU÷Ñ÷¥ë‡Ã~ç{r§ôGSÕrFð½Au$Þþ›lYšN¥¾ý=µ.8ïäVäÛÂ]×ÔkéÍ‹´õ´žjö}ª¯É—…Ÿ‰çÚP­&æÛ,¿¿Í4Þl=2¹ñ]Ãö‰ ù½êe––#v\¹ç{‚«Š?¤Ïû1¨ÿ•õ‚«¦»#œ7ýÝHßÛ9iäfàÊÿ©ÉRÛ_©—Üz\·ËÙ²—åèé9G¢Ì]}Pñir†á+:›ÞK¿ëΜe±ŸQjuà}pЧ8rSøúò}ÎYç¤ïI1ûÔÅÜïŞ̭7alvi?ÿÿÞÍ_œ¢‡.ç-ø³R›æYû85f"¦NèZü¼çð:¸ô£øF¦Â½ñ^îÖ ~¤½«zbáu%Œ¯±ßd/ìxí…",u¥ó_e³³ïÍ®e›ɽñ|†¸„øþ-Üv°¢95¶¯Ë>øî º}°5Žä¯ ×ás(g)àdœŸˆ©nZkÞŒòìyèÜ­_L¿ûÏÏ<´¿ûÞ¿xØážø£‡ùé‡íüËû ;ÖCósw_ï|ѯ|å­»éþ}üãp\ã'û;š !Ÿ Žô­nÓ±ÝçC{–üßkµØ/ðþ–ËlÔØŸàªéSû Ô÷]k‰-‰)ͦ±µR­%1|¾•ž)fÉÉÇP{Y³>'Q~75,§i‰Rÿž[Ø$ ‹÷‰ féñ^ÊWáoŒ±G1M3x;;µöc íé'S[ö÷fÎ,ü«±ƒé‹¯¼ëã,)NS_s&‚³H¯ÌR}±?gÛ´“Ø?a#®C~ÚE‘lJ ÝzÑÉ?C/ã\…'";4õ÷{Á1ëj‹ÛÄ7ô$lÞµ?gìbOÍCŒ|UõöÄŽá¤h&ð|¡öº¹y%"FÀöÙú¡Û³¤"^Sœæû+}ƒ53ñi²~~ĉ÷Äõ*䙃¸oefb™›.œ?5(îÁ2¹1ÞË\ÀÁ±šÿó~è¾ç…Ü¥«Ã_È>I4ñµ]®Íl‚ª_ÕöýlUaÊô?{lÖæ\l`ƒ+š•KiPD^ucÞÑî dœKßo³#Ÿ©¾ŠÄœ‰/´Ç#ñ‘|øÒŠ:ÙY­G!ÞY†NA²2z†àÝUbwr‰/pÔÍ–oyùÉåÖ}¯²e~^ä/Gj@ðâ8ÃðfÕÛ¼DxÅ¡]£÷wŸ)Irpj«°ù®¡aww-îÀˆß>/]“ÀonZ7âbbNÂó+_Õ:…ŸvÁdµ)f¿`GtŽõœ[kôÛ‰sƒ¸*Äëä œ3{Ù§œ‘Fü/,ÍÎ뚺“ò+|tg'. [sN›~Fó9uwÁNÑCóϳðzÍS›¥×4¨G~zÂÔo¼Kœlïl¿«™àôPHƒ'ì”éöÇå¼­5xáâÿóS—íLýͿظ¼ù×?}Ùx¦ø÷ç¹û{÷úO'þþì¡ÙƧŸ>8xý‡}ëö¿ì÷ëÅßüÓBqÊâñþ%È¥‡à&•àDM\ ÎyÔf‚ƒÖñß wDõ#å×röV[‡-z@+ø¹ Z7œï؇KÌðo›ý÷â«ù÷55Uê\‹“7KŸaÄæólƒæ`ͩ̚>IJ7¨‹WüNªvÎò\Ž'¹¹ŸÉÙ ú­ä¯píyìÜ[|&=ñÙÆm4ù‚‡åuë´íÂH§AuúýÀ¾WO:û…}Ö9óóK?hÔÙ®öVz£[È®Žš•Xàdi]á`h–sè|H§¼aÄ–ÍÒÒ§ÖóeŸ Ø çEé ÙnòÈQ>›Zk£q¸ ºÈ]ÍßD ·¦C× ú)q¹l°jñ.pÕ쬱÷ÄnÊÁªâ•µòÕÆZýRùká8ºúÝoÀ•îV‰5h\s݇51.¹ ±öùøM¸˜Š=j«¹nÿ¤9F©û r¢Þ£@zu'bMp [ëˆåÉ¢Ž.±”Æ$uOl¨|½ï½ø!Ε¤6ªºuýÌ¿”3o«®´=HO…ïÓyðû?¨'ÝŠ†Ïì3Û{”–ߟ1ª°’•â´24ŽYÆb¼“­}þ†e{¤YáÔ3ð]©ŸÝmší;ˆCFìFqhƃœyê®së+˸NwE8MÎIã.zÌK<Ùslس®v—>•Xƒ\,œºâuÇšýÝuçw¬›5¤®ò¥‰¼\eè¸-¼ƒ¾—ÚÞ¨<ܺ‚Gu}Œ“áªÏ» !þ·»hç+ì¯ã(ŒMyÇÔ8ÕW‹tŒ±CþlÒRLuŒAØÕÒåÓ_ˆÑ¨×Ú¼øõߺkŸóŸ?åów_öîç¾oçjQç1ÆxÄç~ÆŽ½ÿ3žö!ëïÛ¯b¶Ãúýt¯éˆ!in(]P#×'¶ßt^ÛÕôg½N5w†u´þÎùzØÆ«ºçôÑnQãó³Æ,Y±B]s<è©ðõÐlåª8ÌíŽÙ0úçT·¡/Ømsg¨¯ˆ+3-Õ›cÏEn‰}!6TlʽËŠ<{–>6ù\aaèÂÄ7À²²ÇDXÞØ×u‰sTW©QOYÍmS}ƒôwÕÃÑ&…gw~‘°ŒIu­1ðÆöçàõ`“Ø–®‡šBò{gh¿ m,»6#ÖóÌn[ï±®âËÑfUÁùÂ׫6¤9“·.:€Áv¹nÚf ‚kÐGòóFÝG18˜©ôÿÎñ}Sg 3¿‚›CÜÙâÓàqëµý>ñÞ›ZXƘĻsríö9ÛµÙîà•R'äï[>éäÕø ûûNç*99ÄChà‘_)O¼'g¢§T{Çm#ÕJ¼·)q7¸pœs4@Áè‡Ö ï{Ððâìà!êNl'üûAúZÒM]`oî &éQù¬¦ªZþ8wÚ ú~õ_‚KœïÒžõZ¥µÔL<ê!›Ò ÖÙŸˆguf<ÏÀ'³¿·i¦üu‡ç5\†&øÅ÷QÄg‡Îš×ð±»eèzVÉ?tÐu¬M/o#caα-xü1â>p 9u/øQh¼‘ãé|§¾…jÓĸ5òÓ¼·£i¿ÄšÃ0¿2h^ ½éœiÙpæ3äÌkåoèO®t×ÔzQïuU±îñ¸÷_èõÿÿ½øù݇ï?|ï•ç¿âòïo}óåë?no÷é?yù÷îõÞùÕßøËèþçÙO8üù7¾tç+Þôy‡¦lq€=‡Í‚ù±í_𹥆KÿÐc}!žAÖíѯMÝ;52ƒŸZlÆŸÄýþ³mbæ%Öjѧ©¼BZ3ûÄØ•¼¼Òâgâ«À^Qÿã\òº‰?箯BñO‰Ü0û &¸ð`Û1+"~V{SÕßTÑâ…ËAlCÞŸR±Á¨˜ÕŸÅþMÉ™T^UYñô½¯¾n®ÇJy óŒý¿éïÄmQß3dw&là)×ÜíšуpµJ §'.Åa¾áñË ~‡øRSÃrŽ“_ÍÝ$>ÒùæçÅk¾Ãù¦s¤;×tÕàr©'e 6CoTÄÊÿÁïSN‘þؾÇâQ´«á4¼jcŒó¹þ_‡¬ Ÿ^´Zë‹ûyU/RÖ¯ç¦Ñ‚Æ/û9‹‹`ÿeh½^¾þvþ©¿ÊîV¸®ò›ðŸ…¿\ªàkGÒI®’Ÿ­ýë{&r¿ÆÑh½ÌØT|u¿¡ÓÒ4§F¹UAg)°34}.É6†®ûD¼hÏü¬kyfÁµìì®ËŸ€ñÐ=‚q‰æÏwúò~.‰]õZ*GJ­I´Ê:,aBCF¾mÑás:1 O>>\§ì ߤ“AGØõâ1°ç6Çx~aÓ‰‡‡1‹û ‡Hõ´Úìâ>–õvŸ¿l›‡|B~1Ï7ëO¼Z8ÄZë“üø9âo8‹>•~žuôsÕÍS)šËæzéèè>n“þ¾¹Š8|äø£t@аp™Ê«.!-°~x÷äKé{Rs‚O6KãtÈJ|¦ùjá(9zÙôÔÒŽ«N˜y*¾ÄÖRøÛCj¸š{â>¶Ë¹kâ¿{oãÿO¯ùã?³Ù½‡§¿íSwÿ݃?g÷dOÞ~Ï¯Öfmš@_óî]Üø€w¹/³Ù-ÉXª*>ó³A}{'Bñ›ïgä2çQ\»b=k¦O%çÐÚù:2žßÓù|  86ú÷á«ÑÓtÿYs•Æ÷Ýç‰YGqÐ>á6é}ý^лK ÁÎ7}‹Â{r†½Å·ÔˆÑET3 ~TVBÎ×R~ãgW½RUg*c{ìÌRz*Z_K—‚­•†õ(LQñÔ5,xÒÚ Ì*üÈé¬Ï)6cîØB5¯u×;=Àpµ…;Tz‰{3æFØ÷¨·ªªæTõëî­·PóÄWLÂÑѯåœd¯NÄ=[þ.g¤O7¨O—¼’µˆÞW÷ý~¦[>w_ø¼ãÞÂý»¨Õ‹ÛQ,®VÍî@ÚuñCºÙ2ÂÕÏ%æµ)íñMõ¨sÖÉóÅË®ð ðϪg&oØÎ_Ç]ô3z}êLJ"$·ÙøÈ¸|R †˜?ƒÆ ø zÏ}m¿=HGØaÄéàlÀ_ŒºÃ Ìw­ª%O-Þ|óˆíÖâ-ÀV[wèu{§ +ÄÊ÷Ý·Š#Ÿq<Ï®¾á18ªw9Cªyå¼§Ÿß(`æ`¦ƒjbØH4 è7‚ïWãsãÙeKðÇ©'Á †>þ#’³t©†à»ÉŸ‡æ:5ÕAøï‘Ï©¸ƒ^}7b!ò?ôÈ{гÆ>Êþ€aŒsrA6FÎ2>ƒÜ .,þ¸×ˆÑ˜†®ßUkžvZ9FÁ>.£wa-^hú1îÏGÄêÛúÇ—¨µPC"WAû£ñ rhé°ôíüJ mç×3UqÕJÌ•?&&gß&0UÍw+ÔCîÕOÚ³ïýÁ_ùÝû½ûköN¾k=Ë;ù]ûï]ËËîñ®oÙyÝŸnîr—U{Z¿øÌó×Ò±ð»Nl1‰ñ>ÐaКy>Œ^¸¸ô%rçˆeÄŸ&o`Ý’k,Œ¸èÞTð9å'^“ë4lüŒÑëÐÿþ¦ôþñÁåås®}†ô³Î <ÅWèVŪcÇ­‚óœ9³´(Ñ|ã×Ytýpi.XÎâüµj©u3¨6œØ+•ØH=äÉ é{¾õRçóï·ß#w‚OWT×­¬Ñ¬Þllù¦´ià˜â;¨% 7çìßk°Ó¥ôo‡ä‡_ësJ0¢ÜŽÑ6œãè‡ý8äÖGÿ_²ÞÌÒ¬*ï¼´¨h‰¤t"vk„¾ØÔ·OUµ7dÇ b©€‚NŒ¢­(bÒ'  B‹ !­Q‘ö|ûTµ‚·V?4 (UDm Q -BD‰DZý×Zëý­½‡?Ï3Ï ]]ç|—½×^ë}ßõ®œrÓ‚œ@ug»ç@¶mÝ¢‰§B3nŸ¯ù µ;ß¶¨ŸèͲW9<Ä)¯›n1Î iý{4óskBÚÿ7ûîD¥3ýFa–ë~š^«¦¹9Ÿ=‚öâWÑuÒ³¦ÞÎîO½”‡ñÕ>?ô0wÔðý<_é ßÞô¹€žiVØ¥ô‘†Ûù,z÷Я(ÈÜL^ß#ù ˜9ûÐpRtCó`÷½jï4æ7Þ®çh?Ó›„¯Ãâ§P„M8~¨ù‰à!sç7ëù„´Côq…w˜[ä{yÆëy¨Gÿ¼°Ð¨ÏX ìj‘¥´…ª[ƘùZ úÀ¨=æ˜M•˜¢pû±Ó+U>\|ÞŽö”ÿ&^ÁÖðìÓ³šÈqÝÒ.S¯¤öˆw¯ÞfÉ1ç*ýðd ýÊM©½Ñz!çâl)ä`­·{£âƒÅ¹;«¯ =Рj­•< 5´Ö@¡™ÚëªæR«ÿzÒuÕÖ¿’¾X¨Ϩ§{£j†O¥W‚µ&–ŠFä‘§,S½TU©¾Ôää«êt;©ûV.G`òDËìÝÚàÜ.±¾œ¿.膥—W~ç{W3j׃4%ôŽèÜ!—μ‘5€îZzw mºbéèV¾¿fXØ^æ8݈N{¾¥ëò†3cÖlKið±¯Ôý]_,¥ÓïÐï£çÐü#¼#jÿTS2ë"s,õæÔAÚkò;pNÝsÛ&i@«rL÷Žn8c`œ–{ ëÆÔÎêÙø;Œ0® NíÝ ^áM1³fþƒ^Óû;WÜã-|>÷¿”–Tçvjø©û„I­ìý—ymsó(®ª‘Y³®CßU…}ÖAš•Ã𖜘›¥µ¹FÓƒÇRWoÕȉ7F4lËæ£8‰†çðyrpÎpÇÇ,ŽS‹+G·²êê‹z(ÿÑØ+¼Ü{Ð!ù»·µîÃÖ¿zÕr_Y\½Ûɬ%èmc-Eê‰õ²y)–ÇÌ7»7ÍŽ]Eý~E½­÷??|µé5Ñ늫Ôoè^Ñ#¢×`æ+Z#j£A3ÄmêýM}Œo§~› i÷ ŸEMgÿ?ò›‹½}ÖÆöÑç¸@iõíú1·¤?JÝ ý |ž^³úíšÄÿù3"ÿ"wèq£Á½î@C⸿½/»ofç²?t6á[ìs…x­ÁY(ïï]ÂŽ\@˜b³nsòrf<= ~¾ÊoÊcšªMÍQlL½jßWfB^ËZh3Ç^•X#XqMñ*ÏZp"òõ¹ŒÂSÀŒi8Áý…zÖž¥¼ž¥¡G m½/¯ð6ëk5iìçv^¸k¿¼ŽwÞöØ]}ÉÇíî}ö—¿êÅß¾k={àîD ü‹/zçö=_¿ÚþÑ_~èÁGÿÀGŸÍRL®†œxûØs¶ý»f†õ›ÚŒgÏÙmmðNf{ÝŸ#ûE¹[EÏcŸoûN½`仜=+½{ÍnŒ~¯˜Kêø'}啚ç0tÑkôä¶6›WàÁ¶¶šŽ*g3o ê7í=Zôž#è/¡w„ç6Šû\úUÑ[t‘÷Xû\Z2ü°[”¾ ²¿#ÞÆü/â0`Xš§“zà𩹒}ƒfdnÊWšzR¼øH>1u¯‚íE}ÿmïJÞr9ßuÓ½©oHþÏÞåaxÅø;²wÖ~~k¶÷p“˜¤GH̘Mù±¶‚×t\5K“zÑ2™Õ·¦üBÜE¼þµ¹Å8;¿ðEìj‘ÄTå]QÁ@•+'&"=Ýšœì êsêŸE»'üÈs Õä©'U\¦Z·Õ—Áý=‚!R0ãü‡øÇŒQõ}åUëÌ} Êpç^óù5~àø«<êÖƒ<¬š¦34^ò*nOÏl`6ÌEØôùZ·“Ó–8»#Šêæ5õ(1„š7xÚkù~ì{¤XSïu}Ž%4g¿Êù˜ÚúIÉ‘µJ‡ÕTÕã‹î=ƒ×O` èD‰C½ž(bæ•ä¦å‡Ÿòð(áë|‘~™ì§ÛAÃÍúZ¦ÿφêük`TÒhßZºë-â~È_+gw½Ïë)Ï­oÝý'~ßÎ?ä—Ž÷ÎöþÕýöõ{ö=vMv¾lÒ¿bŸ=‹†Yu„ïIñSàt­„0ºJ|9¾7õR„Ø®ÞÏò¿nvWeÞ‹ý=û¹¯à `-»^–yc¹ØÐñÐâVÁ§ÆZ×:DÃ? ßêy^bƒpuë׿ÎRrâIz*´&Ô œ·Ž›ÑG.~8õ’ÔØ'ü\.á Þ-òñ'×ôõ%v%&SëŸÎYÞ{êSö¼¤°Õ¥k$G%v%⳩¿¨áÑÂ^w \†:€û/ä:¿ñ„šæœó<¸Ò -oÄØ+p“+úui!ä“ëë.¯ëo«½ŠöZ ÝØyòœ1ž¥ŸÍEý–ªÝŽÒW@ñ·ˆ³Á‚õq¢ýG'%Ü Ã(½Yç1«ñ%©'[Fÿ‘.¹žxØÔNIîkM¸Wú(ѳb1 ÏÙeú‰ƒ Dß»¸‹:HÿDO7øbʺùE즽VŽ–¹\ÿüÛ»ËÙ³#³Â¨3‡ôýŒsV–ŸeôQû+®éë ëQO}¹GÜ×H.ÉœÎO”|%Ÿ¯ž‹L`ÔqÊ3VežY ´:¾Á½ Ä’­ˆ«äN<Ãséùã=F`¾ÿîÌÉ…ç;X69ûX\AÖ›1—„˜^ÁÒzå$Yc¡AÖ?…œ9uÚÕðKŸÆÀ“ΧŸ™Å;ówÔG/óÔ8• #³ ¨'õŽ#gæß)ž£ãöî„éZž×Cþ-M‰ðôÀp£wß =Á§4§Cý]•¸lëM³ò,¯M¤Ïóó[÷f?†¶àï­ùN†–>üån\‹ó$7œà37½O-z„éy€wÑs/ŠuÌ϶îãë‡: ¼]çá:ô`×Ò§ÏEà)™íF룯 ~S9GÀ—Åû/ȳ©w‡®–žNê«MŸÁþÒäŠöß¶öì÷íïÛ³TýÞ¤ß8Ï÷áXSÝÔìLxyÅûÞ­¯ü|%/Ã?¬ç™É§U'³·¼Î‚3CïfxD—göôGm8¶rS´@Uçb•&S9NhÀžáüð™Û|YégˆŸ®×[.p-ãÌÐxl×£Í׺"§Zƒ+ .מµ.gq=âBð¤ZçàáÚŸ·Vü€Të¤÷+؛兜õzÏ æh*¦Ô¡ù °FÎtê!þéú 5ˆÎÚ‰w„®cþ<•ŸG”³¤ß Ls³…MD»ð]Ö¡r˜ëòr ŒnmnZ4ìM¯¯ÏÉß5û­ ØëïŸú½·þêåŸ{pó—?åàøYïßðêOÝYüüîÁŸ¿ô¥ÛÇŸ»oŸ}Çg½wý¤ÿ´ý8/„\=Ïò$»6ûÿ¦ Òsªè4g¢>¢®8%O:ÕÂ^+Ë›Fkýxê ^|]žÎóIg?ê³'é‹ÁeðÑŸš„%Ó³TT»v:븾ÍôÁ7ºט¹õÝaóÕ?8‰,hnÀªÉ/èëà,kãsˆó=ñ©'Ñ”<…™ù^üNoA^'®°™ÞzQ'õ5uÚ²ÍÍPp…z´4nk|¾p :ŸÐÂWb\8Šj©‰õ ºµVÙ¯ªFqnh–…†÷–p åÍé÷Zè×Wͽ5¨¿|ŸذrÖí„&H:ºÚ¾/=ž&0!µ)¹áOK;[ð6hŸ4v¿k´!Ëì™ÝH,×ø6ôœ†ë™xrjÿ.aœ~® ·šþŒz]<:}ÙgÄÄlZÕ‰ëÌÒYƒÏ ›ñû§¦…„ÏPl¯Íw²”¿*>bäÂ^jÔ±Á(¯Ìº‹üCZ„Ê3£W \FXxzµÃ‡ôû|–~Mñ|‹¼¼¥ã;¶X×ö\À¯¶¾î |\µ?½]¼—¼wÕÓêÁ½^;}-Ûó¤·Fu‰×<¶»½œ½Åp ›Ñ›[giU/wÞ™çÁ6}Ïòß´k$¿–T™×D]#¾f‚{!~Í­ßMùLj§pÛ ­‚®aŠ3ÉIŒ`^ðVxÃ[|âY´õ¸OÇу§Ž`7›òzEÓ¬Ú¬jÿym$±´‚Ù³ŽÙZ¯Â¸hþ;1{E/œúÇ!çY¡'ÅëúøïóháR›¤º2Ž9øÞð>Ül>®ž‹[Ï}ðý×’Óâû»¾#¿ŽX“ù)?ÿÕÏ}Âe«…ÿâÿiûîzóÁqð¹·úîþÂÚÿÞ}ÞÁ¯ýãoïÛZzïýŸ¿0ßÿßü¹'ìßçÑ¿·mœÉ•WÜÓòú]{6vî‡uÎÍ®`Oqƒ´Õö<_&02ÕxÄë‚Ï‘tŒ~†“—éŒM¿,øÊMù1ß³ù®äºŸåo"Mnö=Ï­ÇÚulä‘ [Ù 'P~=h‡e^ÇÓFÏ1Ã5:eúG‡ôØÚ(áËîgÖBýIŽ ¯AšVáªþÿñßQC~Rµ– ˜fW“ƒ4´ÔbðŸªxž©;wäñLyùm°þ~êÍ”¯S’ïS¶B·^8H Ç|o8AÖ÷‰FXØHzœ’û÷¹7óæÀ܉sCóY¡ÿ}"ï!n†×}røz—œkþ®…­›¦æŒxß|œoèÛÉûЈª—%üðtÂ…£2ï#OîÔïÏ,ɽJ®6´þÌÇFý6D/䨴çS‹4·y`Ì#˜(yXV§=I ݳ^tvÓ¯“šQø:´ó³f âTϊǹ„ßÔÏ%,¨¢³†‹b}Á âáyèwAï3É"†EܼYxvÔ°½ŸÜ˜<‚õ*~uB‡©|ÈŸ—ýNàUgÀ×*ß5HC©kÈít’W&ÌùF­Ã`_p†ÂÉp¦¯ÈcÀ¾ãùĵJ{ì¶b }þ\ßú\ö¾ßɯ|I¹åÅ G¦ÜÞ× bqb×Cxd¬[OÃ^žY<úßš– /Ëä]N“8¬¸"¿_©gg-óüшƒß“× ê ^VgyÊ‚ã ò4׺Eï1ÁñØ3²øÙê™6»ÝÖvh[nX°ŸŸLoÈ™ýßÉ™çüôMÍ~³u:HgÚp¯=ùº†PñÙcñ„¾Öž§Þùô{ÁÙÿv×õâAoxùîµWž={ú»¶ûÈ¿z¦õÿo?þëÿÝîí/ùŸÿû¬g>jç~;¿óáïx÷öã_ûÛ–3ªÌSÂyáÒ~N¯¥ÇöøÂ3î¸D<Ì·:¿¡¾>øÂíŸ0Èœ1b¿oŸƒ¯|'£žksnš ø†ä®,.ÁIÎòVÜœÐôÀwÙ½ sD÷TÈá®Ñ‘“Ûž°ýss¦Y>œïChíÀ*üýƒ‡+¶Vfs)שpìKÍ颤Õùœ¸~´¡ÌÿYª7Tk$uA6ƒáCzï}}ó#'¼µ1©·âçô,)và’øMhîö”ŸO,†üD5,ïXuåurÁ¼ð¡zOááÅq©FÜK8eEû©Ü%sÿY:†r¶?¤£kþJxާ€öüMØ!\ç°4Š#x ׇÿÆÐ|×ßU†Ô:„žì­Þ³ÂÔoþZÆÛ5Y#ìÈãÚÍJ® 2rÁÀÐË b]w³7„7î‰^Aµçü {M¼œê¦Âû6>¼ú.| éY3Ô%œYsxte.¡3˜új¤®eýÉÿlŸ|Ï# ½^Kô–-Ó7é̺ŸÇ³ô^»³ê¾ $º(ûLz«ÁÎi®Šjíµ°>ÔQ³Ï8ãZøðg¹¢¸g#±AžOÙÇ84ÂØ×§÷¿ù¿±/ Ç ÃçÀ²×Á9ܘœìÒ=F}îü𽍳jKëxÒ «œÓÍ;é:žsþþ—ò¢˜“ƒ9ò>8ƒA\ç–}f WaŸ ½çÄAÃ?øtólú¼ô`^ȃùÁ¾VÁ2˜O¯ï; R3XbÆñaz;S‡)s]œY¡áúNyæˆß#u…>s‹ëvæÀQïêáç\¡>%7=”W*yˆ­Wûå<#˜Z64– ÷ë}I çµ­G[Ûê½)¼'ð»^[[–3rmè9Ã?Îèèk8™xºÖ‰çûÒŠHG¹¸ž_nÉ™ßâ¨Îsž/fõ/„¾äî‡z|Ç(ÞVõ„cˆÞ÷>4ÝúH¼±gj×­5GMQððn‰YÍ/+úNµ×Ó/6xúÈٙǣ\%ùV­õIúÓiO §øqë—Bgšuk7£‰ºfB£˜ÎÖÔƒø3»z{˜n‡vƒÞ=‹Ÿ:ßý¬G_Úü7ÂËmÁi‚M ËÎ8Ln ¾ÛÒòÕÐÿR3i=é#ñ“ÊždÕɾ¾8ëÁåOÉûÝlóo½Ózßô-e®€„³®×ªty ˜Ô Rwæ–¡ùZø^µ¾>7èá‰û¾'Ã×!ðií½Õ ùÊ §Fã75<èBê¿Ð‡ë\/\+±Î¯I9–rþÐÉwN½‚×{_»-0E8ç#~Ô7ÂFÓ·\7×FñCÔ—KÞ>ÂÃ*n¬„QäÏÑT(/.KÍéÏ/æã]C»H,³3,5©ø%(¿ÊFª)ƒúñfi¯å)FË{Žœü s…óÅÜ_¾“œF÷–^^:Óн­¤].œ+¬_t¨ºÆ…r/¯Ã¥…_7M\`±ø ·äL²B]¡zÊ÷Ä ¯þÔm{nï~À÷}á]lÖÏâk–ß²ü¹ÛV—Y9ÞYøiϵ¾èA/ó~B»/û{¶?™1>Dok‘¶cêuªª y~/OxÙ3ˆ!#¸©02§–£Ú¿£?<úº©ÇìýR¿·w3Ñï´·ÈÑÀ÷ÁTØ“Ô ³zêg×߸À· Œ:3´}î{/ ~÷îû¾Oï~„¿7VrðƒøªAó%º™Žâ8£ÞSïˆÖuø<·ÑgÝáÝ£ð–±Ë¹¶èkšøð¥€ S\„So +D#<)%çe1¾õ÷.%BÿýÆ!œns‡ø•Љ¡¿Úü±«0¶B_¹žáN{soÑùnÕ.¿t¬ÕðÞ¯úäó\%º¯è<®H  Î8ËÞ‡ãE§çXÔ]xm£ƒ¦vÐ^Èc¤]R>üE`hW„G4-dWçy~ï0§ÇìÚë‘õÃ9Ž®,‚ùtúþ‘~,´q³´±í ýΈ²©™äÂê·©ôPò,,Êçs1¨?óª<„?ÌH?¯²5k¦žfFò$q¿yîÃñÂQÃol¦ÿÝùÚÖÎQêmûçªw5 ›XPïÉ>ÞÕ +Î?f”k¿QëG ‹Æë/µðå¹'Èćѻ—ž:¿Á?ü}“—O“ïµÂ?µ³&¸påƒÊ7Úbrcâ¹ðE| ï¡‹7`ƒePŠÎGÇèõ\¦Aý6ŠQž[2ÿ¼=¿ÄT :VÎ }ÖHþ'?‰"މ÷ÃlÙœ¡×>ê`¡ºŽÒxÔ|Tê4ð™³ú¨1õûÔØcôûà,WÿÝ­èÜF°Â®'5½¼–mžãš½«ç]fù:’#kÍ—.÷ʾmå´™ÿëìqÜò¿ùõ»ößúžûíþøé_ݽןµû%ñðÝ~à…O~Cݽ÷Û~`Çîók–±ýcoøÞË'hó·ÿì/Æý¿û”GïØL€àmÎ®Í èž¯_9‡bgõ«Þ÷ÏkaÄÞï! º˜Ç 0ÇÁЄëMu ¯:¹@Aßä -„­{öy»|oá+Œ/œå!?‹~µYšv½Öd¥}hZËBÝÆÚä¹Ú÷“S¨Lþ@õ®æ®…¦îœ¼mÍÙ5+~Œw~oð² í_´wEB+¥&Q®ž¬¥f¡Q¶D<¨ƒ<ÔØŸÁÅùFžOýa?×ú®àO:‡Gé1ÒT{.7}/Ž|L5Ÿ¯{ù’¹f4µ¬0ÚôÖ”¯roœ§ø”P×.å§ÇÙܼ)wöëb&6¸…ÎÐ}æ'ÈÑFú=´¾F4 ª KS×a›y¾¹~Üޟ΢24O¼ÌÍÑHïï¬OÏXïÕ¹’Ú4]‡¾?æo©•íûíÚ¤«Vmp&k­×!o¨­ígá‘þFj:ðñ¢^F!чz1ëi}][\³k‹ž0ïKï[sè£mþ%9²°Á©¯¿„;NhíéÛ—&>>1ê-ö׬~þ\çhÆq°D½§ Ö:Èσz˜$tø¼¯˜ãv†ºž<_ ü¿©-á„Æ˜Eyã"æSo¸×¢×ÌÆiëûúˆ>BuùL]ʬžNå ô ÿ>™x7~51ßüDî1傯=ÀÉ-ºX¾Þ©ùøÓN±Ws¶1=ò,ÎÙË…ú=}êõøzûãüÐÝ›yúýæÇ?÷à¡5lŸþXó^|ðÊÏÜråó÷íÚìœük?cßúûvŸ0ìñàG>r²ú‰}=à/OžÕùaTÎ[b=ú;ö2µ"ܵ¯âP‡µ7\uéþ1¯–w,œsŒº%âÔ¬>æAø7ç ù29çYËwO0iå®âr_¥5wD½.:g™KË)s ú¤É:~w$î¢óÑý§[}ýy.1‹F±iì¸|ÏKäZøÙmš)#<.õ¢Äab!y®¾‰ ô…KÏàÏEº‹ì'yâæv¿¹"õeç;Èzõ˜M.Ûa@«ð¸èÚm­ÇQ{ÓßfÕO‘s\ÇÚó·xÿ­dÈžÞœYƒ~‘¾cÕ¯ã2çÓìÉ3ãL>7ñ“ð{©9§Ux¿ÂþVá½qžX–¼ zí[î¿PÃYNÁ™¬ØŒwîçøYp ø† ovarþN¢ÔϬôÙ|MÎÖ¨òŒA‡NFž0©'i®ˆó„x-wâ@`#ÚWä›è÷µ¯Î“ãŒâ‚X/•½¹òŸë LŽ|@=–…œHXE mEÛ«ŠÙÓˆ&xïÌ•êÁŠ6Úý´žñªÕ?q>ýÒùíËöNÿæŸúàmß¿uùƒ÷ýnëûßÿÇ¿q|Î?íÀæZ=ýÎ_{Üþ ßñ–ýϼûæ¾õZì6 ^ö¬Þý€'É':ê_jåàZ.VfŽ _ôµhû€ØF¯¼‹øÃ9Gåõ}Jœi›Í—zd9X ªs†ç„AcƬSù{ø÷Ò¯$Þ•u]¨Áé•á,Ò¹µ"o–¢ê³ÖY¦Äžú숳+aÇxe¹5Ÿ-l>ùeü„¼^¸õ¿¡Ç>ßi˜Ò7=©ÇžÞ›Æ~Þzn7”ϺÏeA»žËÜÅ04N¾O4ûšú˜Y`8SÌÔñ>óŠŸõaøßSãWÓS8ëšš?^†Î‡#|Få1ž=Bʧü~Á·áp•ÏïFê&f)õçØQÄhòó #ÿ¦~ ¼ê¢Þ[bÉëùq„…òǨÂ[²O˜ÏŽ{O_„õ,F[Çô¾.;ßRî)´³xZ¶™0|·íQ‹ñô(çYQ†OK`Oº.òõQýÒÊ/¹–K5v…Ü5kb)-t·7¶fõQëyõý¹ÛÉù tœ{ء߻X‰ç:[Jœ×øOåìèBž>ÿ¶ˆõAþÈìÛÐÉÛÏå·A}F¥êç…'€Ç¤ ˆú&<:M8“_G×w­Ø3É'iÔ6s.nä ô&vý¾“òÖ9Bœ7 |-З¬s°*·P?hôj€‰*ßK¯/ÎÍCù5¢£B–û>|Ñ=îék·ÄådµüÀŠÖ0þ2œµ%p\t”®³^S‹v=)é© ×2h¾úPÞùR½jƒ´Ë‡ò“¢4ÚéÝð‡‚ßõ}i? OúÛýÜC «Ø‚WÇ\æÏÛ÷=EÊs}ÿ‰†÷ø`ú?Ãoµ^ÏðEߺᅮ÷ø‡mÇùúíû½ì^;öã¶iáÿþâ1oýÆmãù™á~| yÄúÚµýoïBØM–©‹Gì4LîŒ8J<€¯W^’¹•8ü]ê™A:*Þ/ù¿úL¸–ªzNúô3Ì¡ƒ{šè‡³×:-p<³4øÔÈÊùGõàÛú_£¤'H¼—¿é y„r…Ô¸)ÿt-õ¿í;f-ÓÛõæ5Z|íÄmLœEƒ|Á¨mÈC©OÇìçJm̳T~+¾rc"÷¬-}ZĽžï5Tš§Òô¹¼×Ãæï†o°j”ØgöÌ­G¨yø´>_x{ôñ³ºœÝßgÌØ€s¬ªÃ3çÆÃ[çwæñè*ÔïkP¿_Tgx^ÀŸ òÅCDž³µÏïµ×ûçS©áÁBó‹ÙEœÏsëçYÙ޲確G9ýur­*ü›{žà ™IEoe«WñÜŠžNê3bñIºµœ/f¹)_¯A>Úè¸Ó‹ r{­Ÿi™Tð.~ýèx^]\ð<-‰]‹Övö}w±,µ4ƒüzùÇ‚ÕáGWî}ž$± ÕÙäEÏ î_³¡rƨÿüjÌ.õYàU Ï~ý¼Šôõ²_2ö¹è > öŒö#e&•¸Jö8e‰ë™ºüÜúVëÜþõ¿yÊåÕ—<Ëøþéæ/¿Ûοþ®ÛùÁëvpô gîÛçÞ~·O>8^‹ûö,Ìû÷[¾þ´q q~®)D³§3†œ¯{ùz sô})í¾óoáíz“ÒpCæ²ÆüCÅ ¾®ÑƒIý"Ü<:}H–Í«E{äqxc9l^ç`™EZáe{èX¶„ßà—<1¢Õ•Äæ¬»â³½æ@ƒ\—M÷Go9ëš:’½ë±ãNG(Î4¼ É 93ðA’wÁ4K«v¢½UÀcôgUóékç«PTÇdÑy\Mpc:ª¼/T«oL臮xªž;°®BŽúŽô3è¹q<’¦ðÇØH>H˜fU­¥þ©=êpÔàHðîèÀE©±È‡ÑÈŠ9J©¾s]Ø3Û;^·tš#{ýk¼ÇKœq£z×ñ]~>Á‘­|MrþïªåŠ`ÙG#uÄ ^Fẅþ {ŽôÓ€; ò(ÖºÁãÛÏö?¹^¯sÌ6-p˜`áÊqýçÒûù`xb‹a1ã®zZöÄ„Õö÷'ÎÏy‚œ}ó`¨7Ž˜‡Ö{öTpm4DäM`'œuêo+äÖsô±Tzöá•L`ÒdÏ¿aáhÈ…èE•Ö|Až{ïÖþ3D‹´·Âù—ž¤úÃ?—3­ ½BޏÁô'{ÍÅ{¤6݈j¶I9Pé¾ÓŽÖNLg´¯óðxÞ(äËÔ_ÂÈ‹bN›8¥v~ü‚½¯ú$}™õsÈW½j‘fƒõª3gC~%Ì¿Ü3Ÿ8“ˆ·³xøN?ZõVnjŽ&=Ø:SÊ nmî´ ìË5UG uõç¼­eá M‹êãÈm—Í«.õÓä`ôÖ)† sñœáâs~Úúüʇ½íÉ¿ð¢_?¸qüÄ_xõÓ¶¿sñò§~ÇÎñÚÜ×lŽýãýºoñäøú·CŸž ÒSTfR é³Áºçõ|ÝÎÏ9<€žÛŠsqVy”4êÙ÷I¾kï”Þoõ„0‡Azô¦Í;W:²fÍŸÄ©FÊ:ÉÐ"ž O{æ´Ùäï:cˆ+«¥¼.Åwë<^lWøïV©³‚^šqh~“Î¥‚§9Z=ûÊSì«àA`ùzneP.ùÏZ–ªó¯+…ú;‡oýê¨%So0‡ožÿ}0”V3âyÞr–{ƒù†nâÄ‚³]ZÇÂ¥E̹GÑmúì7çsµ×/(~‡×Zp°YqÙéï?goð%´}ä†õP¤â=fÁßÛóCÛËõ +¨·i†‹òؽFÓAÝÚ½“=j@ÖBi9žëB u뛜N¸ï<.õó |­ª'éN¾ ân|­à5 óh—\kÔAg›6’«†³<>UžÇ5íàzcR—mû_„fÙWiÔ2'ëxIø° ^ù£?Kqhðt[]N>Íòe×~š8ç¥Y\hNh^¹ÎAý{zŸ+zÿùðnU ó¾+X¼ÎiçÀõw²/Í¾×ø7<íÐ1 çØó¶Kܟ؛Ҟ“7û½(~غXôý>hdUŸ¡Ëtß“ªùˆ…˜§\©„–%æPwZ©\¼Ó?Ÿ³êÎ\ð­¬ý‰}C`guäƒçÉñ&>ÃîÁžOè.n¥¾Z)ÏÏzY9obRðÅ-_ßKhÿþm¿<1æÈVú*Ÿ:Ÿù\y·â_ï•¶V¾“g0õÅ,Ï&r{_`]>;ò½ä¥ª³²G€ú¯[[UyŠßó[Ðä|0õóå|ÁvŽ„7‹Õôx±ØçÚ÷Ûúµ½‡Ï4zô3±~bö:~/Ý>,€ÑUŽð³ð$:#×MoãëxK1ÀÏ\| ð¤Öy72;.8®Ìs~Â,½õ,ÎDùcÖÝðÔKéUàÍ ¾¶Ø~}$/„_×¹µ54} Zg'äâsøÐT妫àÉÝŽ²N%ߘ¥‘ÒÓ-gwg= ׾ Ÿ'ta7zlÖ3ô8©Œ~—õ²yIÇ^èrék ¶Ìê?ßeîcÆA0½3ÿócÐÆ/1ö–¸'y×ÜžœE ÍÁ-U°öA\zÆÀ|^ÅüSí™ðÇìÞÏJµce6m›«=fèk¬þd6Á Qòû3ËÓ·X÷–“ 6ÌÒïw‚+â¾fÍ¢Ö£Ÿi–vA9ô𵫸¬=àùD‘wq+¦”ùšÝß|ïÏ\¶XÿQ/ø„Ë¿þ—/»lý_¾uæà-¿¼½ó¸ÜƧ~‰åå[¾þçÌøÀ®ãá÷û^ÏMžþ¨·9Îö„—=cñ²{ýñBq×÷­­ ûf:,ÛœQë`Š=Oþß{m\é÷Žka¤÷ø€o>ظö XWѳè'Àç6ß„<Uugìü-À™¨¹É?”ÇÕÃ`5žŸ‚ ÏÒ R|&*ppËИâ›?K >¢×¯;^o¥é¥÷r6…öGU¯BA›‹¿¿}·åÝÒ8_ 5¡ýÜÖ–ÎáÒÞSè; RUoóØ|ýßí]z½÷i)öÞŠ†½/s®(XjèäâëW¸†þœ£#*ô$ÍÍK5÷ë^ß3Q'ræFîí©&§#µ½ÉW´xøóÒ§/¼¼r¨ß†ä€íù¨7¢âiGÿX_ƒ“'7:ja¼•}¬ø¿î{è\ÌfZƒíÀQ+ógþb‰+àû’ùów„åxQÃ3‰KôçBoå _„8ß}– ë›5>áŸlë£iè+\KrCj¬Ñ2©ž«Ê—«²|;x…jp´ë 0Àþ¬Ÿcv»úc/ÐÇ:åÌ£—i¤‰µw¥‚cK'Q”ûÚÆß‹÷Ûå´£t[kú ™³5g¿GÔÔß›ÒZÒø(¿LÞXqfŠšÂ=JÌd÷½;uºµ‚Æv̓¯+Öt`,ásAÏax‰$>G?eð\ÑS?¨ySšså Ù“¯šSg"¾dð®ñò ÇS=’y/uÐWÒƒ€³[|ê(¬* dÑ×ÀCèb*š鄯¶OS;>‚ŸÒì0ôˆƒp{rÝÍ6³-÷–¸NÕz¿Zðh'>5ýõæß­IÍÅ‹{BOËš>ÞšméšßËÓ•ÛØzI_ÚÍœ^Ãò÷}úæG=ÂfýÔã3çq_ø´»æ³¿þÑÛ¿u¿‡ìÛ\ ûï9øÖÅ—¾ôÁÛµŠéýä?íg=`˜vÄ\©âÏBA=¥~ÏÔ‡ÍG߬^Å;õw^ªh\7å gÈò½Ì9v#<­Oó¿ćéŒ#g×Yp_`²ø¨©î)ªm&êxðU«à‰Á×áæ:ÿf?ÀHtëÝo¬™5Dï¨_¿½óˆ ѳnØaM•õœƒúzá†è±"§ÔÚOžŒüD{œ`K˜8½¾ÿÈyTçNôR)È>bò¹CùÏðÜá×ônýù€c '±gÒã~Ò—1+täÜíç™kmeþHÞLîÑ4MÊ®€ë{Ÿ)µWhXÂPçbó$˜åC-ŒGõtèè%SÜL_'pqÙßCʽMeŽƒÜNù¦çx vØšû>~½]º×扡¤ôïrÎ>Ëè—:Ôœ0òHÕ\©73þ‚½‚w$ùæÜ|ã ç/œ²j p<çÄuŸDë ýajU³ži¹gÎÉ(à×Cr±¾‡T“ºîR#sá£oW˜‘Ç‹ ÂK§åZµZßç«­ææ =µ™—ÞGµ`ÏQ{Kë>P††ûážœœâ…ðÒèGá3¿ÿ¼èÙ¬=›jù×tFž¡£v`ÞõR}¯ƒ´›Ô¢Š{y†Â=ª&E?A}”X'5žxZW^¯ã‰ƒbóÏLÝðÇ8ïO,äÐc7þÈîÝ℞E‰us¾Ò¿;û¼ˆ›åö—ºŸ!´A a¤ ¸z8ǸÏ3 ¸ŽMÍ–kÚëÐñ~–ò·÷gë—÷¢ùiägÓ 8Ÿz#üZÑ÷8pô\šºw36üü‚°€X¿Š[[ì%8žA:dêþä \9r¿›Ði»–öÑÓÃví]}ûßþ²ñü»×ïÿµ—ß³÷.ÿ»gÝ}÷Ïÿ÷—ÿäÌ_o›¾Þ<ÿ¿ü·?ú^×öo?ñà‹ô²í~ôýüÈ·xoq[žï>Ù<}?üïÆï¾pžÛþ6_µÆàÜå¯w^ñòŠŸèËx†ºŸ•b-ü|‰¼ú¦ÂùOOõ,O ~®sddæçºÏœ1Æú°<üfÑÅ܉Ù&`µê]žàË¥cª³< Ø“ât‹j§<×Ñ;)7ZÀEËGŽþ ¢§;sO¤VZx÷°â÷ɕϮ†¦QŒðgΞYÁá*ÿut¼kè-×äœ#Ñ5&žþätxðeî æ¾)}8½âàˆÉ㬪ÿùݳ!ϳn8‡³¦‹ó?|ÍÙ—ÂÖxlpæÏâ,èc¥G€>¥ô›Ä7í7ßçÄú :^×ßGÓÚܺ@¢X>qžˆÃæÌK œa)=&œ ÷¯½îßiñ Î×bƒý\ZLǨoun•Æ]ÈçÛa½/èj;MRUï5k=udèÃÀøÈ”Cò^aÜÌUëMöÌòEŠ<ó±Þ ÉÚ¥¾ ßÎރݳ|Ç<×l¼"mJa^&3z{Ÿs>Vpˆœ#äÌè×ìwX+³<é8o´6Àê<ˆÜ=âðý Ì üÅÞ9Ü>u4Z6Z—šaùÿíøò‹#:Yàðô9%æÿì¥v~ÈF—r^†ê˜ŸG=ÃÞ–Ö¹ ãÚLOªÈ»©ÛÉ”ãŒKyÚïá9«s{Bs¨ýkœûš\C–xÁiyÊ Ù·tCêHãïúú­ÒDWfï ê›VDŸ>èªá‹¸JÅb_£Uï»i.c'âÈ Yġͺ¢Üà†5º¸sýŽ?CÍ/_qéÃì~êïÿîþ~Å}þèn÷Øùò÷êÎ¥{¿hÿ›ÞõÜ—Ýë=ÖX¼×î/¯ñÁ_\<üãÞàÜÇ7ÜúÐí¡ó·‚sfmÐ÷ÃÏ[Gê&áþþ˜•Ü祼WåY¿è¹FÜ/Máèþ®,.žÒ\·˜MóêÈÑÝ“|£†>Ë{ƒÐMŠ #ûxNí=Úº8gõq êeŸc=éevÀŠØiù¯Þqæûìc{¿vo'ÎØ4ì¡e¦oµ)hcCwdÏ„~’MyåˆËªÒr‘›»v¯6òkøWfs+ʉ}oØÏñZÔ§pgÖëÞ5yÆ}2'{®›ùB~ŸÔœÊÑ2¿#&Çžºæ×f×b??\Ê_]uqiÞ3­ ¢ã2Öäip4ì»ñNÙ Á¾Εó+á\c œ½øVô#ž³‘ é=åì˜9=†/`ša\#“ïU|š˜“Ý4­ÏÈ~nW}ÉsÍâ®fylNv·.Î÷œXð1Mƒ}iœ]àdxáå0„pöÀƒÁHƒP:ܹ×#—ŸˆÉ`ÄÚΨGÿ‚4ah"/·w¢9` Gï^>:×ðPÁ…†æÝèºÕ¥yÖélË3pöù'û™µÊ—Ï+'½˜Ï;g+þÛƒ´(è.¼@Ͻ¨&]«Ö·¿¯œëÌ|W9ð´)? ÁýWnP,ˆün©¸³f·JSVÀ«à¿y™ Á8‡XGQ{DmONGîÏ÷ÿ¦ÞAª÷6¡Cë­=µRÌDøïÊ›—5A>mŸ!ŒF³3.‘'höAà¬ä¤x-hï8ïÌ{>Ôœ–A~‚§ÔïÒzj.Œxψß7À `T®2’+ ³(`èu>ŒÑC|>¬94Ηëx$úsž¥ÏåL£VÕϲ/Ö>ëjó‹M,Òþþó>ã–][oø7ìü·_xÔî׿ûý~½ÆŸǃý+¯¸çŽ=«û¾ð‡¶_|êãvT»S¯ß{ÿç¯ÑZ‡ß žÃhi¤uJžFë¬]QŠ_RÕPehÜ^•®­ð +Š™9Êݳ™Õc«çÎiKçüHßË öçy(ÏQ:щ™QWÅœ {Æò”÷=ûDÍkýmô” [¾”gòcù{qø½03‘þÃez†ãÅ}Dä ÊIW‡šC{:|WÙ9ãÐêSa©Qä#­Ïò¸Χ¹:Òí¥g>víöœ¤•Á}‹3½"_…ò°‘‡¡ùþ¢sÑ ‰ðט5CKu”ׇúÙk÷a~V‘cÄ-¸QbŽ|ð½‡Cû?1uûïðWq ~>çrýœQàœó±n‡OšÀWÅÃñ±y†tzx4B£Ö8iQ·5F~‘hmÑ.uŸM>5Âñ£¡Ç£5ú÷-½‡B}¬s£jÿû;°¼Aõ0yéD}¶©^:Í^OýÌ ­¿=ÓRiަ¿³óï²÷«úiR¸¦îµw£ó s7]?XÑ"ÖÕ á¬gRNý§3hKõ™×³~wš¥5AûÙ¼Ý. É&Í\D¿;ÂÁA×*Ì “nÞªR{Ó[·} ª¥n¯pßögÌŽå,ã:‰¶¯6ÝcáÔ]µ'ý¤ÊUô~™ý^œõÒÐ4-ªik«Aã=·Ü"p$úK•gûzlØÆEùÖœGÏNþ>ñ\Å]úýžŽ$`W¾öí2ãRsÆs£í#'çÖ™Rya)÷õ¬|½+_.p v?á…µ§ð#é⬊çsž½›õBÓ¼^ö¥]>‡Kù³N†[bž7½§œCº÷-i£”ÛÝA.±…ž‰|=.ëKøœçlÌÇ8^ƒ>ÿ÷Ìóß³ÿÃyÐï"ΰÛ}þïà:Ú1­Ÿj©#æ(U8S¸¦™»ÂÙè÷(üpœnP¹#|¶êºJŸ¨ÖÚw{Jó7ñ9äÏ9Ÿyª¹G¸0]ôê¡ñvü^žÃÁ’Oáϰy§~ŽÀz•;Wü›ÑŽ(¿¯»ÏÓÖx4Åþ‹9?§å¿gùZyqû}´Ë.‰}xcž'½qøøÚ6ì¾2òìUÝõ’­?p"æMê9®¨#u^¬š>z&´¶+3 Ø?ì1ÎIb¦ø'r²ìq·¿cÏ—z–Ç ßkßaç7¼¸ðô¶ÔÞ̚ȡÉoù.ÉœwW„€Á—¡õ ­8£ä9è×AÿÃàœºcjëð8:ÿÿû¹r®u`Ì—*ÜîÁÄ™éçÖ–pdxdUÂj²®R­GÍCßxöU+Æ{~)¶ î´”ï§Î._Ôš¬Nóçs€º>š¬SÈÛU7yÿ…r?çÛ”ûU0èâÚétΦÎpH¯ãà:;¿ÂþüsºhnûH-K]ÁgÄÚ¾ƒ}“ÚFÅÿqh¾¤ÉËoæl¾ ÍÓ‹ÞRê8#â˜òÌœj±ÂÇž¤¶¶{ä:àÀ§í^ù¹´syvúYéz¢ó¥oðTøÏx„ŽG5u_»Uj}¾ôWp7GÊÕ®¡O¨à:ÿ&âgä'Ò´=¯˜¨|¦ k¯ìõ­§IkÞó$8¾_ãçð|Â+zøÅ Ÿ?~:ýð/Œ` Zïö®×ÒJúÏá(ÁüëÝ5ý‚-Ov·ÓZN@na筇݇,Ì9øÂÛ‰‹hê|ïêš'ykžxÎ+IÔÞ£´ÒâܳϽ>|óÚ>£Õ$G©¥4áÝÕ4+¼Ó3{<}"&ë\GH¹&ƒxhrA´sÌi.Šsä*«Àþoã³ÑF)'ìõpEÜëØõLTé'˜ƒ³–Ÿ¶0×ëUt-äÍW5{ŽxÉu´µr”ú ¥fǨæ­hÁ‚9?tÝkr:|'»~ÓÄÚµÿ=?“Æ(}þšØyä5õ7†÷ü>פßMoïÆ/†Ghì³ì/‹šŸYÕÏ«°äô`;Ã’c }ý&Ä©“J¬Xõغõ9…oÛaó—žø™®,©‚«êœKžPu$ëDÏÀ×´0³‘<ÑÖÞ¬Ê+દÎSlMŒ"ÃÓëÅúSû¬*_ÙGÊù²&WŒöóò´fEصá 4‡Æ.±P¼¥ c„³ÿCŽ7§7xÆÛ ï\Ì"çR ŸõФ¾VÞ?s<'…ÓTõhViÇëÁs[ùA¥¿qŽžÝì%±Ÿwsº'04í}Ï¿Ôó0¢õ_œþää'O,"g ?õú)6] ï© +IoeÉíéoRR…YÈCù|ö@k.÷Z¯¼â9ëÛÒ?÷­_cý}ãS~8øÁëO=øþ¯šößV¿rçý/øùƒçÖoØ6}Ÿ=÷×¼íùü?ÅãÅ­þ:Ÿ hÏ<ðàð=GniþëøصÚÚ÷\™q ~}.gIE æ†8?ÐâGäJhÌ„·RWøs¢v§V×Z×çnL`1p“ÔœKÔäæ]}+oÿð™dE`NÙ3œýò¶·ÁÒåAvG?=st¨?™õgÊýòjXáV¾fS¿õoÇß‚ÏÃ݉÷ÊùO™SÊ“Ðpè¼Í™ÇòÛä!„'×¹˜]A C<œqëpSŠi¹>ºX‘µºez’fqÄôŽâyÚâwäºÔ«p»Ä@{gö»x ×é}±ÏB‡ØÒyé2ã}?ªèŒÑé¬ÕšºØÏFÄóNÞ÷7%Ï@L \<ø\0Î Û}|FGE}Nç,ÿKÎPáÔ„“j4ÅÈÔ.Ó“£z&×£Îÿ…¼ÓÅ'emùäYÇöuNfm ×¶ž•Ó ­Õ9yLã¿ŠŽ‰ü;\Þ\üúk®6½Sä3ši܈bÁÈ>Žs:¹pô!‰£hÖ›>ýß­… ÁÛ¤ö(4~7¯#'iü3µòR½„ÑÓ‘¾Ò¾9ã4EX/º´£ ¿+¾IÅ%<ìÉ3üýh¶dúKQw+wkñkþþØ_|‡4ªôßjž/ìÓðà >ˆûÖ3_“‡::âYó†Ð¿ð§ÀGñ¶eFàµäòæs”ëwFGû-yñ«šLŽ-ÜïJ_KhµnàÓ§.G¢~“EÌTTß¼^A‡!l¸†·ô†ò‹ðw¤nÓ(ªÁ©üš´kW\̹·ší&ü=ü-•¯¬™¦³Ë?W¹¾ÍàòœEøÉÄs@g¹ÕƢŖÔö»‡‘âÃBÏÈc™rÊ1pŒ3ëïº÷Ÿ}È{oß¾÷N¹ü€Çýðîs?fµûeÿûó~õÝÏØÚ]Þþ“»_ùYÿ¸cŸý%ñû¿ýÎÓ;¿ùsOعñÞwðÕ¿öíÏÿ‹Ûxƾ]§Í¼ëû_aõÿúèAÏܶë¼ã³Þë=‚ŠòàØ[¶2¤¶þ‚åwkÖ¤Ö¥?3t̺ϱ¯ ôl§ÐM_K=Ë ý¹Ö[=Õ<¿ Ï^¿“uÜà¦æÁƒò3A:5ü³þ°ë‡77¹Šy$ùþ¢Z-gLòrïýÖ¢ŽtQs´.)½ÿ5†Ü­ÔbÚ3ñÚ]¹Ñõ‚ÅŸ†ûœDÿQÐÛtÖm¡wF{ºÂwè Y¨'l:ÕÍ+P=¨Zz#½¨oà£Øv¯Ê)È ÐYWÎëe›q™Þ(v?öò6™Ä£‚e8î‚Wö¡<(ÐŒªÆ®<劵Õ÷Ñ›Ì9 Dß)Ü•ê—ήáÍ“>£ËßÐÕ¡óÆDû¯^ž•øþ ò­J­{Us†ìók>)ÂdŠê¹:«Ÿ@÷TÑÐOºÙõÊK£9‚ÿØçÛ>ìúðe§°šn½l3» ýûÊ ÈQôgçµæ¼${´îG06a$ ø;‹š AŸHΰçnרÜlc‘à ‰ø ¿ÆÎ_pÇÂõƒ_Ùú=”Ïóï¨T[eýÙkºç£ìC"VÃwÂÿl­ÃÃô|:8Uäjѯ(l¹nªw_½AþóÍÎO`Vßë þ<¸ðׇÙ#|¬iOoüšã±òã¯pÀƒtŸòJq=5z¹}Ïs ÐRúÚÇGß!û‹Ã›#fÜ_`¯à›Yy?œÝ䔇š++MRG¢¾#_÷w¼µ9¯ù^ÙÔ©yè¬þÞ®—OÎÁɵ‡GØ9ÍÓ¤áÔ^öÏ–&¦ áãüž„FÑÿ®a`W53®ó¸)âtJø}†~}$sGºçïküO~W£¾O|xø‰Cðšâî÷•¶V^ó´{<ä™ß~ð1oþÃõCÞ{bç–+Ÿ0þÄm_û¾}¦íæû3»¸áÕ/’ï­×50¯µ9øôÀ92FõŒfžâûýölå+°FçÞ£ïI~÷È} +«:¿_–¶Ù¯ñœ¼Ï•·Rkº–n_hžY›æJMº€“侞«çó%×­O&Î ríûüÔcýÜWݯ˜|AýÙ—üÜ&®=âGV›˜¯<´æú^}ÏMM®¼ ý+çðʨÄÎXòòJ½§Ê»éùvâ‡îÑãKkr¾ œõ ˜Ê2f¡A¬ô{óL©Y©ƒÁCÁˆu¯é5©3„¼¿Ãè#oå÷驟'^vŸS»†ûœ³Çs/µˆð¶p„Ä´Á¹Ÿë\[MZ:ðþ!8›µê0Ö¿Çñk854ïäZÄ^ûîÀ®©þa¶õÕîíXZì ý·]›zcœ›Ä X/}—侇mžýQ#5&ýbÌÊ!':%C°ø«ÍôºÞ&zÐx·ÂögS>ƒ×©<‘Î'.F>nß÷ßÿån—·ãG¾gûø™|æ?üáÁ/½qs÷Ïxßþc7Ÿsð W¼r_µ‰Å«õñžÛÿó—¾Ôf¯?ø´OØ·ùÀ`àMÆi(¶±ž³¶îâq¿ w œ)zèЉ ;·šxWÞ:­â_BÓãœ@µÒD›â‚úi.i¾é†z²onz »¢A+½-fø¬YÓÒÒW|/õÞé{­|·bÌ–bÈš<`Ù<ÅG½/ç¨Ï´GWñ{®¹õ5F~IíÓñÈ>Oœœº;rúH6Ö-·ÀWèöН›âr›S­ïïÖÎ'ôäöYʉ¤?bÆ{Ä<~‡3tˆÞ´÷}N}}Ê¿#ξIg0½7¬­©Û{ägÓܿũÏìWs6¹0˜6>röûòo¬Â° ¼©pé´f ÑpÏì!=cÎDx¸k߫ҲP×yÌÇwœœK\œëž%u·i>Àfó}.zÆ[QÀD_Ø­öjÖ–‡òÿ#ÇhJô/‚Ÿ3Ó ÎZ˜CÕ>àÞ+µûZŸYb‡&ùàšÙCóB*xb(·gõæ.›ñŸAxe}ÏDN=HSJ®Áz¤…£6P=…Ž»è<ªÊ5Ò/%ÎÉèIíð®ÑßaÌ‹Y²h^ùÙýhâúo)r€N;'Øš3¿å‘ñž”Wˆï8SÈ¡ÔR¨w[M‡ÏÝÏÂ÷"¸µþNâè¿ûÂ/>ûš·ý¯ÅÿÊ+v/|ÅçŸ}Æ~Åîÿäî¼èÛ^±}ê)ß¶û[›ÿ²£ž´ûøîlÿö_o¿k÷ßn[>ð{wß9®C÷#×?±ÿ²{=bÿñ¯ýçìm­¿v|¯ | Ìo‹µ¶™"ðošìï7é ïÍs1<ФiVî¿‘ºd­7Ç]?~óšÏ!§Õ9èÏJÚîuÌ…^h¾sˆÞTã4ÖhçX: ]—(~¿rfôõ}6˜=z Õ-¾6˜÷*}^‰XZeåÛ©=äï´)/“A¸lóŽ¹ÄŒÛÊÙ«ûÇÃE¹ÑEê8;è ì3ë«òE韽Çz®ˆ¸6h.ɹ˜¿QU—Wõ(¸û7ž¨‘–Ùƒ¶‘ç–rt8jxoÇ#®Æ¬‹Šî\ïjâ ãì¶?‡7$¢^dƆ¼º*gÂ}º,Tx9Z ¿64àhsÎ6 }@ðÙ׈ɓô,Ù¿`:|EàuÁAf×Oß¼n˜%þ¦q]OÔÌñQç؆òUÕ ÅÑߊž¿ë™šN-üXÀå–áU¤s∵¼Æó€ø6) ®Æ~-k ¼àr†ìyˆ5&’wDޤz´’»ëùfž4¨§fÐlÝUy~ÖoÂnÀ’*úÙMé€á‚æÐ)ùïÃÿS;àO<û<ÇRÛ©¼͈ú£çÝ©öLj6`h~ÓÔ<ŠcÁyÀ.Óƒ(b?9±Î·2«‡Æ®WͨI±ÿÇ:çÜ‚ÐT’3¢=gÀ¿C×™ç£zª<öÄü ¨•STüƒçÖsg¤ï3έ]4Z¡Ï+G=_áµà6Øßö;Ò…gÏR³:‰¿¿T3WúÐË /èæþEž¤¹9èD+½iÂ^ÇÀ®Tô…:›+µ—Î;¯í¥aã»WôÞê¼_sôQ£Àëðð!ävƒp.ûÃÁÄcLä6ìmû~j„á–œ{—xO«/•û}ûs|Ä+ß|`ïåÙû­ƒ_ÞpùM¿ý'ûßþÅ¿ÿ˜·~ã¾qÇŸcýÓŸ>îßïï>ä`ßðº!<%ÖÒ&zÜ ôY êõ³?³ün^¤åiÑ'i÷' ²æB»G! ýž´Ù‰S_xcöõJÿ¨¿ÓzÜ›V$gag'_Â+N~uyÎ ÂéÿµÿæXƒâ¾¿q>ÂTc^„ýc8±ÌÖÚéІìáM¼ïðÆyõÜB1Tºâ3‹¾ŽíòÔv--IÖÒá+†1cæÈ÷?1.®Œ|™=l^Ï‚_­¨[ôçã,Ý^Ÿ éùNËôj Œ—ó½5®r!zŠpó ížÆâ„àþªÎ¤¼Í†8£àŸ¨Äön¥ÙHíÞ)ùµ£ÙP;õù>s’Ô¿2©Î¡›š¸îqt­5Ë ´œád×ö4ÜMpÛ1û .äÝxg¾NÕƒêu¤]ÖÓ7¤<_\e`:ö|„/$Þ5‡VL1ñdíKÇä´®©Vä2px|ŽÎ©BŸjëü­ÜpKθe_®z¯Fpò˨ÂD}Æ#úEê+~¿ïs›¥÷ß÷-—„3Ä﵀ǔrŠÕ¬¹ã}=z(ßAòqÕÁ[ ?9JO|=Û*x GÓÞOh›ŸB/B­A/ð Þez¤†o¦êë5¾ôÚÄ\õëÙã% £öi\?ÚgðAmÂD.‚ág^<„GŽreçï‚콦i‡ô%9Ã{È|{S³Ä¿ '^ÑW½JáË &×bYè¸#ÿØË5Ì»„oáÐõcVž£ÞuÔ´ìú}ТK³·î0'Î*ÇHõÞèÙó{O‹ÿz•¾,õ[œÑ±æ¼¿~¢âIüëz y¡úHs/ûåÓ¸%Nˆ!Ï'à?†ì1‹>›_ûLJšÿßôEß]÷ÿíùüƒÅøŒƒO}Ú/ïüÁçØÙ`uýà>cÏu? {¾~µ>®Ó·ôÌ}q‹î÷§+l¯ø`{† ´zËæ÷™¾ñKydÞnsë˜Ý ÇÚã‚~ÞQ£è\Úš“¿¼¦³8æAÏÒÞëúñÀ×Ãc0ð#Å$ßÓ¦}Q_ez¸è»×ƒü×"\Ìï¤mÒ“.ðùd}§X ǺgdÏ€pâw¦ß'sH4ëê›&GšØsÔÔWå˽)ð2¼ׇš1HÇÂs——þV½> M„s½Ô•œ :[ýL°üC³‚Ö¬;ᨩ}¼ô§Oñ³\’u`ïÃbŠáBhÿy/ž /³š›õâ¨/ûõÙQ«›µÞrÍØú´Ïµ÷K/=Ø_Ôïh¹¯pþv9\ë¿b?â >£çœûÇb:i°^aGÔ°>§d³ùOây*Þ¸»ÏÓ¼ð |$ùë÷9óÀ7R"¿9å±_ÉÈ•¨ßÀ ¸Néñ<. ¿ZGlpè“ða?¢ñeoà ‰™À¡–é±w]Xàuaªg…ó%>[ù{ÂÖ=~΄öFî%Þ7xŠò‰µúh¶8óÑ ეµJ‡3M1#ñÕÏ7}HÝ{Q=bÙãU;Àÿ¿ú—©}Ó¿BÏ’z³¢oR³½åªàU<.[l§çºó2À{Þ5Ú;µ7žW`ÒŠu[êÝË‹¦Jg3³èY˜ð5%æSÏÙçÃgã°Ôüj;|eT_¡§Å›ÝŸC?ŸCÏ]ºÿÌIùQž¯Ò'¯‚/j‡)ŽÂ/Á¬RS;§WpäÍ`øô‰)'SLó|¸KQS©*uðp=¯£øÈL”I:–Ôé’?pÆמ♆v˜~!4«ö;o¿ùœc2Mß…ßùÄí[®¼ÎôúûŸüž×n[­ÏÞ‹xvvAý|Î=Í/¡µóúF1nRÏA¡ÖÀ ;ôïVÓð|ô.ñy.Áñß¼h1'òµ8¯ãç›]Ÿ€Þ³óÙÔ”ƒGæ,_€Y½ÊŠK~½vê×e Œà@`Dªo q·÷éP¾½~¬€‘I³]çæ£™s^à~ÅULàø ,åC…>¸Öøà¨KÀ8µ3¿Ç#’µ7ÜÂÜäÆoã¿®xFî¦ýᾓk0õâuj;—£¯ž<õÖ°s¢uG øMéï©þëQxÜÄ:ÓZC+µ‚ ³k§âÝñ>ìçÖob?£¯{p­ò¯2—Òr£5ûMv?Ÿ ,ÿp~Ÿži«åi•yÎQ£Þ¸¼È7C?\ˆs¨Ê#§A½wàá³4„`¸`w›>ÿ*r½Y}.Í'&Î?Î0ö:øðRiçÒ'úáÑ”-Ãß ,¥p®âc(봯 l­e÷ôir¯ò¤-&TÀ¿Àš'~Ì"—a³c®ZOÂIÿSçîH<^fpœ¡z©•Æ>B}$ž{¨ŸÆï™>Ò†‘‡w,z!ê7´‰è2ÈiÀ§:Ÿ·¬aõOQþ_U{¤¦ÝÈ ,æ0áAõƒb{¡¾¢¦ã<°ÏãG¿EÌ™‰ûŽ>¤ð9ôç¹ ¦Æ™ð霛ïfG8”—X™j?¿_b=ðà<úù–ò…XÙžÚY×Hß0¢ïÓþîÇ뙂‹~ZùƒgŸ¥rÀ•¸ þÙzÕCÏ£>9|géW/Ýù ÷^¨‡[˜³t±°~Å»ælZ0 Ý‹û_jn¬0¦“äӦ慮:rF¸5zèñ/iÚÇ j´”«A>WËœ¥#œé³®ìÚ==þ;NïþøG¼~÷m÷ú±Ý—>æ+w?rzÁÁw?þÒîô•?°cçôß}ÊŸo?ù»þÓÎó¶î¿ó°û|ÄÎñ;Ýàý¾agÿË>ÙðŸ÷s|=VPïMÇ¿³&'²ëÞµk ©ÿ©ÁŒ”§oIçïk+øÈð‘ìí á—ªR¯\º3±(—*Ôk ¹Î fæbl©_¦€JßþíùŽæ dÞrþWÑKŒ÷äØç¿xEÑKsJ=yÊ­¸F¼ÃJœûG呇 g*pÚã•\Uç—¸Ç Ÿ“ŠŸK¯Ãî\tUô Ó Ý-ŸMœ=§žl0CÕ‚yvƒ7(Þ€¥¢«D#œxƒîq ~±áÓ¼Û>@CËy-\Ó3;Ñ!RßS7ɢåûŒßÜc.ôŒƒ{"ÄL‘ÆG†/(ç¦Î'xDòÒ:½¦kÞH=à,/ûA½¤ÒŒf«ÞùIóµŠó¾×íL²ëÃ#A¹ibãàC¡S\mõÅÿ9ZÿÑ™lõܘ=g®s¯h¿vÛz?ÔÖ#øx±´3éÁ εDíqã¤t¶ÍŠr_Ía¼žúSá þNåÕ[X/â`Y+¬E‡ý°FÅÓ…ª«ý=f)®Oü.ù$çªæw êeŽœá&rD?«ÔS¾€ƒ yz{½žñŸÛÙ¤}÷–˜.œäRst~N_¡7Í÷çëŠ_ž'¾y`&ê1 —ª3Xƒ;êám¾VgJà89+-çc¡™Õ Àœgò³Ö‡à³|}Â'°nÑQÀÏpÌ~hëmAÝiŸ/NU³…Ã#™8­õ˜çIœµ×óœ 7V-½@›¤œšxN÷P`®n™›‡ 9m·!oT0ªG‚3j¾¯Zh§¤ ’>cMîÆ/ Ñ9f«7ÉUfizħTz„q,^v¯G˜ÿ¯ù%|âþÔÁíwûäƒçÖ·ÚìŸÅ|ÄŃãïsÿßWþàGí?‡}›d½¶ç^ûèÏÝÇ÷3¸¢àï'æZñṫñçÜÉya#žGÌ“›';ïEçð…‘µhÎRóŸÀÝ… Œ\KœOgÁŽ'a5cäÏÞ+6êø~DÒ 9G?Kÿ«¼P{,¼õÙ©Ô/Jý/Ï0æñηTWp^Vðê|ñjÌð®hfáe-æSûÿ¦Ï5ó¾Ýµzö½.ЏãºÚjþê¡ ½¾zNåoúËȃ/T]¼°¼>¸³wÖ¾‡ý6G¿îÜžºY¿Nâç ü\_Ë5öÄž×YúæØ êÇ’Ž àŸMÞ6gïläu‡Þ'Üö¬Þ£eëE¨äuÂFb kEgAÎGÄa£·ÜŒ™J¬Þ*ýÝÊ̱†yûKýs«ã|Z6O›<Ãq%vŠ•ôžƒü+*3Uíú„qäš²}ÉLê¥à.&«snšå¡kï—<{¸…y9ÿOñØ5íYSÀwèüȳ8ã†àúž©ß;s7}V?!Ï|ÖÜ ¾ÌOCzOùB ËSm}>µ”ÊáVäŒô‘(ÏM®:¿ïQ'wäkc×§½F-Y…¢ƒªô›ò5»ä'¦óx$7#þ’=Õz—5®ÃkãI{p¡gVWä8Ëðî€wû^TžêYúÊ“g‘6~Ö)yöJºfúq ™Ô_/½ê´'þszIl¾¹=zRzSlݪ7iB‹½©OÚ³£ò&zßó×óMm§ø‹Jþ†š½ÖúO˜SqšÜßM{¯.Å‘Ñ+£šŽYà:^·‘Ãoa/MCóôkÐìˆäº©—ð‹ˆ\3f{ C…s®ensĪêIb?pûùÛ~ýA—ížßúâçüÉ…û_Þ{Õ£^ÿ¤çí›Ï]oþ΃wþÚÛ÷í{ïñÕŸ¾|ûOþ®´ºÐÏ=ó÷_²ë~÷ž´PþäñÏÖ0<gZ—ŸzÖœ—õ9«¯Vù\ó‚rî’ð(aõ¡ &'&æê®‚G>É­:ÒgýföwZ_Wèt—1Ç65ÔÔ¹ö=ÒØñvàϼë1òÖÐËéìáÒì¿å½6[˜'O ÎÖ÷ u 8õ9úâîþ2˜[e&Q¨¥ Y󌵷í³3承K, ÌAÚ'ÕRÌ´¼žg½­Q¸b;89#ü©­ ËéÐuHU”ÿ‚¦öRÿ,ÀÉ«`¸v]·Ñéûªžkmó´}ý¬Uã’£Qóy<µ0´×ô6é<¨Ô{ƒ´ðª&:Ý£öA›í®¾5q[ç~¡Sì5Ÿw¼yÞR½ÛôSP›ß[mcχõ34ÿdtpÙÔð–ÈcæzÛóÊ™ãlç¨þù¨±áhZù7=EÊÅ4ÿ/ëÏ ·n–¾r ÿ|ÕihP\ÃÂL‰¥æ¯M“^ä_ZÀféQ¤¥gvlÎ/Sm®z"xåV/Óóå׬X˜ù…Ö×ßp>± =Ÿ<çæÓî¹z8ºž§=tU£f{Uêk¸»ÆÍx7¢'a…¿ÇùÚ§ÈíÐÍ¡­cžrU¼d¤.ä.úúfžë¬HíY‡ÿüé6«r í qž\ŠþÎÊN <ê,ð¿ÿÛ (×ÕfµÄì\ô<í¿C >$l¾Rë*N¡¡¨x¶‘Ê7¼E>cyöÍmŽXQn̳XµµgçxÄ»Ð^‘#µ³-Õ†ê©èGä9PÏ‚ ©¯÷8_¨c»^ðB>¦|#{º7ÓÏ:âl;c5:°ýƒY“CPŸ*VxŒ5œL=qì¿ ^:jÃÖäô|?Ïoh:Ñ1ÀÞ¯pâuêSÉ­¤±òç¥ßWüæ|Ôùù9ï~¥ïöøÊ¾"Ž}é1|Â'G¼z?Dn\9cíZ¦:KŸƒÞ2Gz»JœÖÈæ4¨/Jkhç±}9˜rú‰ÙƒæÄYÆ}¨n¨\Ÿ­Ûßü¹'àÓ¦¿êïÒú ôÞJËGb†–}žqIƒúŠÀ¬Õûæø—ðñÕ odêÎoaš#g;<—ÎK|/S£0gï]œÂj¶Èε9–îOLN6µTÏ7AýÌ ÇÞ@;êÏês0ЌԣԗÄå•e©Ùç³ú:›~=p1þÑ/½Ø‰œ ºoÆ¡óŸ½Ö§3ã:ëÞ¯ÏÖ1½>ªÅéŸîø®3¾¿íçè éOUç,éZs©ë-Ö:O«‘¶%sdåaþÿͧ%rÌÈQÁÏ´¶Ýû-ùÜ|þÓcËÖ§ÖO¥T9Ma® \çß¡üYáEðÏßTOˆj|ð‘)t©aÖ9±n™Í!‘^ó·EãV¨ÛCçr3Z7òTz¢å•‰§]hGÁÀQñ2ªíGåY•úBùËš8¾¥8ªsþˆÚ8½G¤Köï<Žñ6ÿgõÐÇý—ƒw~ÂëþqoØ~à;¿À0Åú7í|Ò¿Âý}Žóý·ßüð}ãjNi~ö•WÜs›ÚØÖLœ7«C'+}@ö柖g©] «Ì‰QüIÏ¿A3L´u^ozCñŠ&€'…ëÖ™_Ñ}£±¢'gî<´³øßGЦúÈu^xüézVGx‹µÌeWn†¿–×aêØO¢NT=’ñx~4ríkÒ 1süh¢fÐó)ôü [.àÄañþìÛv_<â;g+óý¿{}ûRžCp¼#¼ý _7¼Ú» |a³õ1{އ¿Š¸SÅõÔ:6 ö†fœØ2DÏd¥.]ʳd–¶hv.¾9ñõ¥xöAØ£ò,ô;“Î-ð× ïLò+ùd]«s³ëI|®ió5Ì·¹P™õ2·9ôëúuË#ÃúÚS*«¨Fò˜Ì¼jjop1Ö-õžÀ/:ç&òÄMïs Y¾h?,ÄÇêœN«>’CÓlƒY}mÌÜÖs…£÷ÏS}³˜wÔ½à âŽÊ56°øN~.NsA>¢÷4)&WÞŸð©\cZ¿àòcx\ݰhÚ•½ÄÀ„)/à§8‹gõ–F_nò¶Š¾·»øWT;,ç•S~.ìÊþ®zÆ/•–o$·…f]øÿ½™pb<{ÖíiÍ}žœ¹´pRò Ä†x:cê¡<##¯öu8rFpÆS×Ú3²¿gu=}Ÿê¿î÷¿ÏìÄ…[…ÉUbѦ<Á^¤O¯à*Ô®Kù??¶9ä4÷MLÒ?ô$¤þÒpqzE´<>–xÑu¶ZS«A|Õ,oÆÛ4»‚||É>Í‚ÎgÓ¦UôGä¶³ëb~±žƒÇ8ÎÎíM÷Ü À ]#œ°b|9L/û Ÿ&ž¯Åðy¸Ü»ß·îÚŸ½êë?m÷û_øšÝ‡ß÷ÙÇ5üÁÆSÿe{ñó—®¿mÏæÿNç_÷ûÛ/|Ç#wLówáw>qÿøÏæ t¯ÏûHËVöœ¾þÝï_+žñNR÷Ó+ò›ÜK-½ô¥« Áðr–xÓ4.ƒÞ—ø‘ŠF?òÞ^_`÷7 —ÆŒ,Ö=5É2ü–RoÆ~ÝlºiôÒàSx¾ªà­?‚FŽóÿ·n>‰ã9h¿»ÙÔ‰SÇ’‡Î$|ó…û’KÑW›˜ªå¶pÔ5ŠÕUÏlA}:ˆ£¾Ru]E¹oö™ÊçƒX;C?A}¼l^P+åZ£4ƒ#˜$=œÚÿèÇzÿö©Åïœ œgB‡­Ïå ¨9„£aoké4ÑãØyï|“­?ÃràêÀÉUgxŒ@‹9hÖ~þ.4;­‚¹Àêyº¾¥yól¤_rõ…þ®°Oüï¢NÐþñžÁÀ˜‚‡x’j¢ .w­=°¥“:o=îwyO⚊½9 q–w9yÁ,_YúDfõ>a‹ÃHÏøžé ~V®á‰9¿f#}©†NóÜ|Z¨Mc?€wªßµ#gÞ PrÃÀÞ®ÛøýŸk^3Þ{/)9#úÖ—r›ÔA(þ%_þÅ»£#ÆÇBœÍ¨V6¡Eæ7*õ7=÷¼Sáý<¹òÊIytö jVkžð‘âbW‡Í{›ÆÏç7«×…µ îÃXžY§ËÃu>úº²Ï°ŸãU¨³µ É‹†'"žÞCúV†ÞŸÉèc™GMÿ.<1 =®:K9«¶X÷¬å†ûe“âŠ?Gzý¥·ôs°çÐ[½s-2½)äDôÕ1K¶´¼²õKÑ?©žð*ü›ü2gQÏÏߣÏQçVð¢ÆóëÝà ÔLÂí ÙƒGœ´N²EçÒ8Ëçjg«Õß/sh5w-ýÿðÆ0ÞÅ~¯#Å4|›*sGµ?øý‚”5E÷÷ÀŽ­ûüÁg휼Ç÷ïØ³´û5-ßëÖ¯^<ù»þѱ ãÜ`ñí_üñÛâß<·T3 _TŽä¼ÛºÇ£5Ë™Wœ~xé+—Y€1µxu¦¢ÑÔYôîFúŒ‰sX¼Úy0@®¯H/5Fmý2ª/Ò_RýÅ#ž hm?w<(fðwä¤Óýz¡í„Sï½Ò…rÖä™5„×|º‚•Öœs‹ÔÃöΤ[7™ä^^Öf…éôç¬zc 3ù¢|lÀ8Ò/ªáY1Ï×>7ú&ƒ_ÑþZµ\<ž›üq{àìèÏúX?¡unÁg>xâ {ÑÖ—Î#ÿ;1o"=$¨iá) Õŵ­y?ïÔ/F÷>ãùŒbcUŒg.à ]—â­ç¸öy:ÇW¡5º=ïmnÊ#9ÈR½ÄM_r11Yzá¹W}Ϥ¼5õàiƒóJoTÌ¿$®Æq}æ6ù3ÐYäZtn¬Ã¿4óbùˆ—?Âøÿò±ÏYï|É_<|÷{ÞøèËßøÔï½ü _üèÝ×|ËK/Ÿ¿å#vì9¼ñ#¿sû§>lç)?q·ƒÇnÞóàøÝnçìØìàðM ®ÌôÇß¿0O÷ãkõ÷Ž—¼±’Z\v íÞW*ëŠ<ƒ8Œ¬X¾wEø’ðnÇ­š“–ïiO«s¸?\Ø…íEòsïè–‰}`êàWY—°g©9…3q–dÝG.9H;$ý<ù~%ÿÿm<ùœÇõfeý¨¸Bƒ¿K_aã….lŸÈ¡™( ñéH˜ãK¾3ª븘àà·Ð‘»Š{-]=?ñþ–škK;¤'Ûþjäy9ÃŽ~epõ{OÔÏöNUgôÂ8+9ýösñXSÓžÇÛf…odìÆY8ó"mZ­Aý†pS`XÚŸÔ`.£áLG˜£ÞiEN쀓i}ö{|ï¤5g^ˆz—`?Ó,oâ£rßôÍ¡Ög³çþjÑ'ÀÓûÊëœy˜G`•ÛðZ€Ÿ“¿\1 õ_C󛬬}z!ÅÏ”À9C[¢<0ÏG¸”A¼+ïœf‡à­¢3t<”Ö½ónгÝ+ì{ò…Ê<µ÷x¢†n9æ‘f^]ɸD ÞiW¶âïÏÖ|A¸Çå~î¥Ã ੠ئ?óçCUòÂÐܰ€PT©¿´ßà†mm8.÷ æNø(aÓ‹è—H}Ržïò¡+ò^.Ô8òÐ)äÏ1?$´¼pìðéö~ñ£ÎbÚóE£§= /êkßtÛm>Ì%|‘ŠêÉI1¾€ïÛýéÌbÿ8ÖŽ´L¯Ÿ=áƒ7ÐÊY¨qFÛg©‡|B¯÷Ê\¡‘}£Z’µ=njþ“z7²/M`arÆ’ÓÏòMµÏÿü”ǘãû/êÿÞöG}öÁWÜü¿¶ï·ó÷Û~ç'ö±ÿüǼõ¬•Ë­ ÷·œÇò>Ó­=ýQß¼MÜWͧz 8½Èß®d^wJ}¦Ô—öYô4~¨W9'~[szŸ„NL@ó[øL_[§Û,v궉µ1GÏž_½¾¤h}¢Ë-¼θ¦ÃÙæ3r”g:/‰G ñùjú®ì¹?%¾^ö]ä·äl›ê½WŒ*òàŒ6Ó÷êÈûæ9kšöö|…_£¶¡¿)°EŸXxÞÁœ]£´s•þtrQ´ŠâÄr6–t þýhSˆ]sÎBˆó¦á^­‡¯ü9{r7„Ûô˱îçÎOÌœõßð´Ñ'N·´<;êÇ7i¦­bBæ ð#ʵNbV;\Z‘Óš¢õ(ÿž[ 3RØpECê†Ñ…‡ñL–œaͽ†fW¤§RûÌèÉžv”ªˆk¬ðõô-ëïá õP³æƒßŸÎ\=sOÆñÑ+17ÿ•¼Ín’¾#fïÀçÛgÙœlö3yŽö£ïOé÷ÐËël3ËǺç1–N3ùo¶È…†æû=Íêu%W²'&rÎRž…jöÚ®-jߥ4ò¿\q®ê^sŸÈýÁ¨ü1}¨:î…µ°ŠX|’ëØŸ`^ ³TØ»œSÊ«<¶ ë©2Ïä=ÈãŽQµ¶÷h ±2lʾS¸9 ³9?¡OmÎ>Kõqöm»ÆüÓO½~ç?Ù7ïF¿ßÆdu½yþþ؃þ•ÕÿãÓN?yûÓþô“wÀ®•ÇXÏ_öÍò]`*Ch[ü]©¯‹×ïŽ~{cë¹H´ÆœÝM3s½ìçh²•CæÈsÖ{K/2[ƒ¦ÛWÅñ²f'ð+Å#z¥2ß'OÑõ‘ ”6+/ðÔzáý‚i‘—÷'Ú;å^r'xþ*¿Ë3Óžs;ÑÉ#µÁ~Šþ»ÂeoJËßßšå>•<œÝ8Ö «¥t(vÒëúýQïõ.}Wâ,ªÞ‘ò±œmRÃÏ1tÏÔàâöÊ¡<…è¥R~VÈmlo·Š‹g}zÿˆpQoªA“˜¥õ¡Ñþ,êÿ˜§Bù˜ð>4 “âgÓ°÷fëkÙ4´,E¹²ÏµoŽ>âg+LaÁº¡¿Äî!êŸU¸@ njï]õÖš¾øoteš{8E‰>×óþQ¸)gGúÖP“ëïLÊ™ð¤õ³ßþ[=­#¸Ú r6Óщï«ècº:eK¹ƒŸeš?T;üq ½‚êíÂ:Ö“=Â&ƹõ¨ø;¡¿âkÞ~ëÿŸþôqïÚ¿|ò Ó |ìüE÷ªü¢½Ìþ½ÖÎÌã\ºQþòÔá»®:ËÏož>Iœghz„ÍÊ÷ é“à£ÐµP·ØÏä?nJçØÅ×±ÚÏ×è,è"‹<â/\çÐùhh-{N/17mGUÞì?×ìVrñ1jcŸéÎl1pbô†¾ÎÅ-¡g;ë>gã©W(}¢éðÉ;Õs^ðÚŸå³gµ#x+u¨øÁu|þ4Û6{·¸çÐ¶ÜØÏxsĦ¿æªfsªžîÞlƒús¨Y;.(u+`ñÏÕïg¨ÅWÊ'F0>ñÌEyÝ„‹0(æù¦O§<¼“÷é¼%ü}¡ûÓ¿üôúàJ]©gŽË}éOç=öõ¹f+ûž#&KëêïAs_ýû>¤×Õywæ™Åkª÷ëšÿ¼i.ÐÃ9)†Uâ[ÃMOR#zÜÐ>SFß”¼c¿Ÿ”klqv*¯ÍYÃÊQ²¶Gï¥xÏ#¼ÿŒÎ…£œ+>L`ÕaBEë&qÖ!:{–ôðèú ½üCxº8Þ…ŽnIš8ÏÓ¢¦¸B̬`PZ÷•\·ÓwLÄ8æè—ʼfRƒ›ù!ã•?øQÛÓ=þçö]ßÿŠíßyÝLßs×—Ýëõ÷wvê½÷þÚ¼0Ÿ‘÷ Òfà]®|!1ûYº7jºCÍŒï8¿±«;·ÈÁ6ÛìÓªZÄŸ#¸¹)3U…±§Wó(ì}+>Lì?[øÁÁ‰R#kÝr6yüS¿£âþ9uHç=Âý7m¡ë\Öä<~­ï žô7iæÛ¬Cjp%­Q±d×”üñ(¾ƒ}îç š ~^$ÜÞ}6ð™7®ÑºVÑìÆlÎʹ÷øT` éíZ£üÿ3ÛS˜FEÓÚræ ]W¥Ÿ­•°ŽýI®ÉJ|XñØ?Ï~nÿ€ƒtzÎüŠ{o¯5§~V?û¡zlB+w­?§ˆÿ:άYÄ„A¾tÁÍ=v­^¤¬Ñ…)»þÎò/á>éq‰^\ÈšzÎSµŠô{áÏû>|“콡Áž!熓[c¶ûƒ$Æ x…¾}ÍYG\V¾á84X¸™b\9¥ù ̯²˜.@ݱ©ÙpáŸpr@Õ'G~>  lØthJgiÈ©©8Oá{¬=âÆ |FÏevŠî±JO‰AÞW­ï.pbõçóg]p†¢åÕ3¬øß¶Ü=¸Oa99ïSßGhOpþliÍøÄ ®o>»¦¿ ¿(­WÇuä ï÷«9„ù®<óØ«úåEº{énÓãBŽ:I7U•_ûÏáÇ–îçš]´E#gèÕè Ï>Æn^UUžWC?zlÆã¤Î…•Å?ð²æã˜ó™üý*?ò¸A?x‡ý>¸‘jåL{©[ûû"ø›-ím~ʵGù}Rk¢‰OOc= pÍì1oãž9Kt?‰»(çÈ“b‚žýuav'‰ö夰ÿð~ü«—®ûÿ}ò{>÷à“þä¶ëó;þïçmýìÁËŸúÛ~ä[¼Æ°áÚþ]÷á=/Y¿ýæ?0½å~ç·åZʦC¾(NþØ ¹/y¾{˜YŽ ýÎzÐ~ßâŸj‹ÅÐù²«¶\ÅÌ”ôãÌ9_È][mæÔàdÂÖÔ1ô ¶k4Ž<´'v¶–”€¥È{.<ÛÐ;°Îù0 ž‘êø"\ˆwã˜>õËUy>ÛúÇ™÷¦xNmNDÑÞtžB×\cï_ìòeøìÛÉñYïâ%ü .ì/½³§&L‚sd"ÎIŸ&RfùÄÚõ[\¡?`~~›§˜æ=”Q‡ÿ£ðakçÕ?}’Yz~Ig¹ÇGi—CŸÐYÁyíy¿j(Í€YÁïÎßz¤Ã<äþ´EyËJ>Ež×RSnÊ»Q×¢¼99 »ÍÙ€`ç`vª©h>ÈÐøˆó_pŽ¢÷éêtü. ˜œ8µB¬ZIùö™ÄóñNá÷Å sßÈÙïz#믙ÃèëÍúŠ8´L·6GˆúëPóó×~bwãoïºkŸñôG½mÿ©??ìüÑK¿óŸ¿ðŸ¬/à¡5î=è™û›îýtÖb’Ï~ÍÛ¾uÛžÏëÖ¯^Û<}·ã0æðáïx÷Úz mZ_Ç2u‘÷‘gÃwË€¡Ùg‰“šˆ±ìròðQ½Ry–›ò žåK5Úy€¤žÜƒe"÷D«©kYs^X>ë(ýW=ÖàizŸßP„}”!ývN7s~W×ç&í8rëåÉw—õ„íeéý*={ð–ª ×è=è•æÌÓ9·&“µ¬8ÏÙÀ¹ñ#ŠÞ¸›ÅõVª=]éÃÕ\ ýl„Aœó ®‘X:4É éåJ—ö(ë-¹µe§¡C.¾?­>Ξ+jÝæÿîçbm=ßGò¼;#~뢴7/àZïà%Þ™sêð#Š¥\>Μ3é‡C>¥úÉã8­©B}Ü4ê1Ͻ¢Ü§lJwÏ/ý¨«Ññ™ç4ã\Zþ"Œ'9IiAÀeü8ÿÄé+ï>bÿlq&†X!Ö ÒütØJCâÌ¢Æo8²gT¯ä¹FªÏ㙥¶üJØð¨5[áOBÛã}Jy¦¢iå¼ ÿÛ3Ôéç¦ý]iîN©÷“µÝÿNà{úyx hH×ub­œÜ>ÓõŠoEZ1ô€~fÙŸI[]T»g~"/¢äy¬âí¥˜YÈÇ„=ÑëPtÆrMøÄ¥4lÒÚdŽ´”¶=pèXG½Ö²ã®'â*x :å¦Ûè±·ô.U'Çž¤c,à$Ñ#š½-#µ(ë\‘(qwò>:Á½PÀt'á²ò"‰³8°µëŽ[Ÿ˜_&nbÒgdïÀR3(¢>»¾`Îl¼’|mK³”çtFKi+è-ÔOùKô¿r~Ÿ´Ô˜Ê=9^OÎÿÿ¯?¸÷Á½ßö¦ñßø¢wn/~~÷à3ïþ“Û¦´¿ÿMïzîúÛ¿øãm.p5á·ßüy¶«ö+ƒ8–.—ñÌ÷YÊ éPºHõuzNŒ7)œUœ-O@£µ>Tßµð3j"ÇRð emÒÛ!Œ†žÝ‚·õÆ]Ë<_çEwý‘cΚ)eïù\ëOöÏ7l“Þ`zŒìý[Mƒ[×ôR (F/ȇìkž)VªC×Ägø¿™M#¼3Ÿxzü<½3§œ=ß–½ÐO‚–B:µ.—¹ÖyŬfØÙg·>¡ õ´nLÑ—{]q:¼ÿ‚ëºy!mçš½)Mˆÿ<ô1SˆMç4ÿ¸í‡VÓÓ?íÙ{O^æœvÃÜRù=¶EœJΦ¤^:Ææ#t“p¾Àùég9ô>J0ôK#½”h£{±Æ‰]Y_`ƒ¼Q´ö§˜#y­Ò_4¤·QôA îõp†µ Æ ¾U¨W—â™Á©EÑm(çÞTñ<èVÁŒÈÄc‚ù¥§‡‰ ¯Øâš”Sˆ/kó•¤oI? ÎwáœÉ¹7Ü„þ2ϩʜó}6JûÞ˜'HüÔÙB ?гæœåÙâï³ÞíJþLpVŸñfxLTtÁº·¬£óֻƒ´¨Žñ=jûGkh­ú—ÜwTN?Àý€Ö[Î×nÜ¢×àhœ©m}Ï[ÝMÝ©{+àxñ^cÚmá·èŸC_Ãaøºû9Î×k´´ïÓ F5:x‡û[Y|èú¯´6#ÞÚsQŸrEsl±€sQ:»5ëhÎâÀ„¯Uòlad½~$uÁêM«äËàröóÓš¯Às—>{¢Ðºvú„‘\ý¹`ô˜G¿5µgŸžA!W§nEçØÇpzÞÁqÑl AdÖ{‹ž'ô×`Oþ·˜ÿoµ9À_þþO=xß/üáÁîWÜg÷÷ŽþÏþ×~åœýõ¨ï ‡yÍÛþ×úÛ¾ôÜþ»–ó¶q›Çß±OÎ9¨ÇIÞÀUñ¿†{EùÓÞÔ´¯{Ò«Áß¿¤Ô«&\ƒGIã°±úÆ5sn“O(9ža/ŠmÔ*päÌ‘¯÷üE9‰×{ªTÓoPÿ« ÎGõÜØ Â#‚Mü xˆÓ7HìMݱíOy UaSøª­¢ïô¬fÃߊænѼ6*ý)àz›º™~/ªçÈ¥³®Ü”nRkäV¬˜ª½X{Bã{Æë£Sšÿ–ÁÞé°fkprWû»òÊs Oqk„OÑù›µ ù6ç“òÿ ÿ€·"ïh.BÀ{˜Ú°íߘï#N­‚«ãyÏJÌX¾ˆöd"פFQOŒÎð~ï~SÓµœïð‚æáúÒ#1jø Ÿ‹¹S^?Rß’ûŠ)ª›•F),(}qõÉ4ņ|2Â?«q ´†‚ N]dÎÆ«Â¬Ftd]Í=?|)5µœïàûV¯,Õ›ÛΨÇÉÀ/:L³’#0‹]y\ \fúA3ÉëÕŸég}•xzÍÏg>¸çHh8'Õìx™niVøý§®›§„Ƨªæªp«ä²Ô8< 8ø`| ȃF´„ö¼®jÎ0k<ÑÖLh°ÃÃJX¼SU_:Cä vFØ™«XX»ÜÈ×FøvÝļsèȵOÇè1¸¤óT'»¾¦ëåÇ~å ÎÚßÛúã—ïþü‰Ï;û¯>üvË¿¼`çß=í¿màß³ûó/;¶ÖüÈGîüïŸü;ïû¦÷oßúà¯Û¶w·}úcwn¹òù>à¦OÇÚæN@Ÿ”ý6cÖ~®Y´‹Aº¾¥|_„-{LÅ3mxµjŽäxávÀNålðçÅóôÚ€ùpsÎ9»•çäqI>è¶ù P)¦çÜ3ê¾ØÛ‘s ¿„÷ñ˜ªú£ÂMƒØ¿•[rvO`¥ÁÛÞÚ¼ejgÁ o÷™ã½§³40ôM´ œkÔLè,–šÛÂr]¿QàWgiì9[è oNg*>Êà`jâ%Ñgkó»ØH ‘÷(ͦ|q.¬:nÅãBäJפå¾È:)è)ðPæ·¢Tçþ‰ÓŒròCÅ»-q‘ë8“O$G'ퟪüRüjè³8o”—Uj4¾äŸvàxI{æuƒtokpÙY¾¡·…þúœzñw#o ÏA8IÖäp…Âh¦ðGŒXÉ‚ãè¹Ö†¼Ä…ÏDÝÔëÂ#Ç»4ò½ðΦFåtø ¦î^g•òèÐÓï#M®ç§søUíýNô¬àË(þ½ëö‚æ ‚5‰O½b8À¤Þ‹ÄÀáÈ»<±ÎÒšêÜ!Nùµò½âþ²çÏ®70Ÿðeç¿Ñá(FUaÏÌ%H^›^DË;ŵ(¿ŒŸ^Upû9ùÌ ÞÐÖKÑ ¿Çzoz0ýšá¸¸Ž!{øNjmºV3=fùAiMr®¥FAó«'>\XñU½M{p.É_D,fqZ˜ò¨Åï ¬ _XåÚYkÃ)(ŽM]ʺ{mŤùp[ª›å;póÅñeøÚ¹iÀF´Ï†/ƒééY­ô®½.áœâÆé9Š]÷ñ™îõÿ>ì/N¼õþ—o¿Û'ÖþÍ_þ”ý'ì|ÇÁ¯üì¾}çŸöãû¿u¿ßÜ7 Àþ¿áòÿ¸7¬»ÚÌj=çð2Ž?tjpïñè:F8z—ž»¨6\ÃwÐG!ßÏó{œ}éºÿéq ü*ã²ýœ³ÄNÕ‹à€µé½"%|ÎÅYÍo‰¾VýØŽæ[§Æå³ þVäö¿õ=̹»^À‘õN&0Ù¥<ÌèsÔw†Ö‰s¼ËGýšÑâÌò /î½0~k/vpz¼ÔÀÅè3¤ÿ†O?ž½.ʇJ`”ÎsŽ1ƒå(ó]ŸÇ0zx£Ÿ 0$8`»Žèi ¿Ç9ü×Ô–ª—3ïÆ&Ü(´Ýz›ÀUµþ'Σ¿…Ï“.{”ŽA½1OiV5˜]?y…xÞìݤ!Õú²gÏZ™Ä½hFLøÐ·ßÐŒ£cÓŸû:Ñ™êÏ"8¯œ½ïá [yg¶ûu¾w3O³¿(-.=ôÇ,]ËþתÐ¥'3ç±ê‘ôçÔÁ¥{êÁ·¾ç•;}¯ß;°}û9ßñ·ž‡š¶ïøþmîõømÿÅ.÷…‰{\oœðê8Œ¹xžïˆß-¬wašôêV4üÒŽeghk¢ÓÞb°Fü¹µÜõ‚°ã‹oü…ÄóufLhWé›®N „ã,rnp wǹ¦µTy/ý‡ÔX¡ »Qs]/eíf«wMÝ*,“Zøá0#ùLÎö•8~ˆ`dvßäÏZÌK欬àX`šÁ?x-¸–H qôÐÉkMÓš~"4XÝýù¼¢®GvœÿR8×X›¦è뚇sÝF<·ÔÑMõµsóæZm¾¦ÍÊã ®Ô ±g¯%ï×aOÔŠƒÅ×Ô˜W xôÕô#{ÏÙ‡`àô$orÍ÷Q®xýO/oX«æ¬à¯ÁiÄ~´¸xùcñÈʺŒgm÷e>lö<ÐDp†à }À(Î`‹:‚g& ="õñ4«×ú bhsAR‡¡{fílY­¶”NZxëĺ·çaçºÆ!fSÁçuýõoÊ =®pû ͦÞÇŠú'jÁ“뉫«A<¡Î!‹[øš¢?'NTa¨ž?KGͼ =ÿ¨¿Å?rsôú{ð¢îÿÞ¼¢¿ŸÞDõP£ßß_æ¼®ü(òs”ßl…ÆÎ×פûÊ88È7O¾˜“öüx›ümN5¯õµêÇËU³¬”g'Nýáïx·ë­—ÿÊ+î¹móü^·~ÀþÏŸûïÛÊYÒƒv¾ÄŸ±f;,šŽ/¤6V¾íÉ¥0Çœìø¿öœéÓ—æA¹›¿ƒúqó©ûä,^†7=hn·X¯öl„ êódå¼[zDˆZo[ÚK•}FÏ,ZáäøEQã“{L„½Ÿõsû\úr5›÷"(WGß{+^²n*÷~:|³=&ÙYÈã¼i14ô/ÝŒ]ùÆ»¤æU,!Ρ[ žf^0?ŸFqâØÔ´†V…“úúÆ£T¸:\œ¯aøúµÉßô½Gª?BcÑq‡#˜XñïTΆ¹¤5ýáôe#+ë Ô|gQÅ•ƒÅò®ákTÿ®Ð*Ïç|O¯}åN˜\CmêkZ1GØÄ kx=K¯=§»>‹œSNž„¾uDëÏ\=fˆÃÃè]–à§7r‰Î‘^Þz©núŸïñ>‹rJ4¦Ä²ôŒA¯¦8¾EO<µæ¹ìÇŽüG\—Ç´¢à âO’m}kѧªÏYÁ?€ñÂßz½Z í±ª\ºRß›¯Ê«‚s x¸›“«&÷ÂÜþŽÍL%†°žˆå“Ô"ôw¡kß”GÞÕæC|'u,=àh\[ŸJԮ̯¢ÿ`èü‰”P§€KrÞMpͬwÖ9ºbSW‡Ä=2×/ùèAK=ש^¿ô WN­:Ö×\ú‚eR'€kõ¹¸¨öðt˜óû.Mè{Z¼Ž{P<Ï#? v)p‰èVÑ¥ _JÏÎ9}IbM‰gòõ¬œG<ÇÉŠo‘ž¯ÞÙ¥Ìá”ûú½Êßp­Žfcõ!s”Khžœçä&åÒïýÚ®½Ë‡¼ã3wÿýSvþÐÝý¨~øî½¿ø…/»Ïj÷¶üsî÷ÿÆíÛ_òý;ígؼÀ÷¼êKö¯}Ýcwn¹òºµá/>õ,÷ü»Oùs[kž÷™àÐ{·ÃÅò¦1»°õ¼­ŸuØ9õÛU“`JÂþ'ð¶¥ôT=ž Øù£Î‹:2­‹ÔÆêã›WÈOõXv|uñŸƒ<§ÀÎÑIÑ3t˜¾ÂÈÉØ§¾ŽèáÓØl:õ^{3F>sõœ ú^]ó9Ès=~¦xb!,†8³Rý•sQ…-úž£g¥×éâ‰Ý´MgèãU¾´±À³M±%y û½èåsü3/ùÞAó…¨ÝÀr¨ 65‡ºï·Ï¶U?wŸ|ÇS¥/æ±ÇÐ|×ÞjòzÕœ1ò>ÿ¸ÍÎK„xoÞ1ÏO}@µ½Ë ¿?<À:…¸FÙp¬gå•ð¡ÔÇc÷.9ß…³ìMôEÂÍ‚àÙÖÙqPÔ³þ^ñ  ÷„šŠ¯#uº48#¾¢èZ=ù1¾–'=gi\ï¼ GY×_Ð)W-Z?Â(B;¾™~œhÆÃwmšj‘µÖrÆPêÅàE²ž@K<³–ê‹Ä9qà§uw°x÷žt`ÿý5Ëo9ø²ù•¯~àÖÁ}_x—ƒyó.N~ÚÁÁÅçœÚ·<öÊ+î¹ÿóç¾rß<€Žïß°™ÅþòWíË߯ð\¨y7ÕÒ½ûàêÒ¶„Ç sâ‡â’ׯêu¡MµkxÉ[}yZ³'Ni“ôeì}úºyY{ˆOýr‡…ªþ¹½ç”§XCG`ÔØÑ»‰gcÏcWk÷Eoª®yœåÿŠ—g#ú2êÚÀýŸBOòëùä k÷ÓõNpÖ€Éù3ÐÌ>°RÖ/ºDj—*^£P?ÂÇ“[)—¿“þ¿×àRkÿÀ悼H]Êù´%]ÉO3¾H àQEcL­¢: ~mšÈ赋~ÀÖôu¡#oA ­ ÍçÞ{ÆìZ„Q®"™…ä šM4¢ƒ=¾ë‰Ñ÷bž§4‚ÙBpÊKyŽ¢ùV¦ãÌ?àl²gÚ¶W‘ÉO*x ìÿ•êîJ ßzÃNêL>“óUÛ¦Oº]KÌÇÈÞ)bbâT`gø·°ïñ/¦ÖлWŽ^N~âpÙ¾ï«OÝq𜗞¾|öQ·˜Öï¸6_ÿåÞvðÎ_{»÷ÿíÝÿÚÝßÝØ}>bŸ98t·{ì«Aºó‹A^$œcôÀÒ3ö¥ü××»øÊÒrZÏiäXÏüü½ÔÏP“Ѓ4¤~"ÎUa¡©3¢Zg`/œ:áNÓ Þnï›4™öçkö~³=ÎLòáôŸÓxSà§òÉ–òYª§¡Ó0d_(uµàþGΉ4mn`õâ²'0p@=«ÊY@ÜâŒkD›5ÃýÛÞfÎ u¨ÅüÀZc߯~ÉþóJ]"Ì==ëÚyt²«áðO~IµéÝq#{Í5u ³ÈxÆäÆÌq"Ÿætz>G ÏA,Rº'$üH8#T·“c¥æºÛò&æéùqÖ³¯ékn¹ÓH÷0¡}„ÂLõx»òœÛSÛ§3xZ_—p–ð2D_©uÏ> ˤ.ò½¨>9墮£]w*ä,ÊñÁ‘—ß”Úfõ-‚ߥEu߈§¨½Cô§ƒzóçô=™½£äÙ¶WÁׄ½xnÆŒFb“= r²žƒT¼í}ˆ!_œåýbqVØœêú Õ9û¸â‚~„Ø¢ÏcnOr—é/†ÆÍ%ÜX†bÀ$ÜpBËÑåÙh§=‡0þÜÀ…0µàn´Fåˆ ­Ÿœ‰K>dÿ_õ`!¿QÌÆ“f»¤‰ãù+OÏýE_‚0º üÁ>—÷÷5˿ض}ôgŸþ¦mÃö[ºÃÅÆ}û¶iú­—͸üǼõ,¨ß8#„A1ËfÒL5rxÍV.õjz.µùJvn€›Ø=3ÇOq´Rg "Võµ4Ï}"¢‡˜g×*“=çS¤eEãçë]±x-Œ´r…?NèÞí¾í9ŸvúKõÌ©`׃ü<9σßûUêƒÄS»wn÷¼&7ä—‡†G÷Oo›ç³ö™œùZ§5j­“¼ÿ-ñry®ö:¼å¨9~5Ò°¡Y¨¯–q]×µ‘¾™ FÍu^µwÌÈh<²×~î“÷Ý9oÍâOƱ«_òfó öµ‚Ž—:‹çN"ü—•uÎZ¤Æ@ËG:ˆ»v鱞Þz!Cs£ó$¹ZpÚÃÎö0{ Ï$6®Ø_s*ïÞ3òÖ×j®›Ó›Hœc“ƒYwkFs¦šï½áÄ.átSãôs&z¥>!Ñ#`ë€uûboº3öz„·i‘æVÜðÖëÔ<›„[o¬»<’:TÞG¡­…û‘³µè|àþýy«*änø¼1ÏbÓ{½NªvMþÒ±4zLСÀû·xr¥PŠãÎ~YjcòXb=k;0ºö¾É[¨¤ËÎ÷ &B˺èòÄÔLÍš9¡½»Ò^©z÷Ò°G?@`¦'š—. û5ꢑýy¨Yxª·xô¿ü~Ä«8vo0(z9ì=£ÁF;?¤.Ê}&¶À¯Bó{]yeë VÛ½}òsÞ¿kgȺúµ»¯{ô{wûê—^þÖýþÁîCv¶èþëòŸ}îswl¾æ¾×·Ï\ÖÎývþ~ÿ /{†×ýݼçÁñs²yÁ‹+¯xŽé ö cÁWÄzÿ;°geßqü{ÔÓY+ƒ§ƒ»sKùqòÜO¸I¹OøgüæÏ=aAºûŒÎCkÁó›ÝðVé$nRÞy½ØCóù¡¿‰\ÿ¥gîx›­zuV²Þ8óðb™AJýf÷Ï|Lø9Å–ocXùù.g.g¬¾oEN½úå\Ìyâ¶ú*n"Ç¡Ï;±yôòÂÐ7©îÚÈ^ ò@é{ y:Wa½u6+Îìó5frà)“6x¯®G.ê`åïôë—!ýQ6ÄÁ\¤Æ÷Ÿáÿ®<(½ón“OÜ*Ÿqô(½jà¨w›xLxug±î)<”t®S¯)ž¤^}T‘ßÒÿ#Gï¡y¢[àh_ÀVoÜÀÔÏyûúÀKHgg%Öâ÷ÎþSì!/.ª¯KxIFß#y&y‚xÌn3øQ¸ÀóŽ_—rŽSï á©î:®¨"gUþ=‚yé<ðz=µÖÕ8µ=\;zca©è1‰÷EXGÎöwRRàÌáa9ÇÀmÄ{Г4¶œ>rSû^zÖ{¾‘ ¯{a´Òí\ªà—ŠOW艇~@^5â%Ùñä^ŸË¯Ákj_b$ÿ–Ÿ3ó)Kx÷\Ó~Û»†îRsq'°iÛ?Ò'kŽ˜YòŒ¥›¹TÛ>Š3ܾ“þ4ßêE,çäÝC-}CQÃ(G®:Ûלùà)]Þ<áµc× &GÝÆõ¢ƒ¶5‰‰øGôBª‡¢g5Ö™û2–ÐA^—f:tY¶öñºâšì^áå¤5u-ïÀþ-Þ?ã—Ö-½²#Z‡¥¼eO gÒ³ÕòXY‰kvíOÿΟ>°ïû¶W_8øø·üéÁÉ{üÃö½>_|êã¶_úußùšå·ìëìÙ¿øÕ?²üý óÿµ³õÝø3¢·¨Oo÷™£G >Ù,d{¾ªaS‡%<Ãë1¸Õ£ô4^ <½$¹1˜xy˜³P²®“9õm+í¹:§–áLÎr 3´÷É™'ÜΦðsйLœ/ä}¼[ø¦^ß*o–…fÄfLf° »”ÆN­Ã½ƒ_Q ³EÞE«þΊQ‡òei=ú¡»$gÛ Íqž¬!öÿ ^ŸÎ/£ -^j&Á)y‚ëšVô9/å;‡‰¦_h=Ÿ›ò9Åy¸‘^˜À’ó é³±‘8$Üy¯KUíUuÝŽŸqf¢9ï„ßÕx½jXΤô÷bö5zê´¨‰‹ª‘Wh©Õ7ÔÎ ‹#Dt³ðx¬¢#öovžFWåMÒùÅêÛ¡yýÐ åÖ÷ì /ƒz7¥±“¾|П1þ±ÚãÝ =´ü\:˜ÔL·s2½À2W&'†]JsÈ}R‚3µ<üŸ£ªºôNú_զ껔Z ÎtjWðJ»üz‡À´Grf= x6õé\—êÆ2ˆÀ‹Bß[çôôr|%1”A=µª*û]üZÅ —Ày.ªs#×AW§ÖA½K`w³ú•w3¿~ÆqGúuæ.ÕsILE§ƒ>Dœ}E¿Öñá1¿qé ÚþYúkþ.5‚êî̵áXÁÍ„÷*ïËùy¶ƒ×)ŸX‡œíÔˆûS7L}å1žEŸ«A¹¿óœÐÞT"Ú¹ø9X¥q]ü[ñn÷«<õò3‡ìe‰>л߷îÚóÿÜÛ?}÷ú¿ý•Ý×?éy¿ñÓO?øßó±;ÿüÑ¿pßËŽ4=ÛÿóÂ-;[ü™û6ûïønÎw|÷þŸ>î]ò•8¹¶y?ï~À“ƒt§¡k¸–±Ñþð·Q³ÂÀ«œ ¯‡Bmdq“þ°Æ_ÏšqßÍ=JX£ÞcEG-KòD­Ö‰y4ìuíËB ¯÷ÛÕ¤Ì n¿½!zm˜{STxýq›ü‚¤á§¢çN¼ÿEño{ÉË[.$?òEëóÚÿ«`nœ{hôg-ï ÎÜm3ú°G8ŒÆ»nôç[AÏÔÅœì3_°¦.C`6Œ½;ù[­àºàvíú¤¿©:çü;¤Õá"~†§ºrGááWÀn¶††_ãc”ûr³yCøù„– ÜG5=|Ê„fƒ=¦Zi/¢ç`HOÖKÌh.´ÉÐù³¶?#g!7ÝT?êд e³õ¾d ÒšŸÐO.m]¦7>¹øŠœ.‚ø¡ºÞ zà ˜ï ^6aosñYá¬cÃ;K\¤ú_3G*³| uß~¶ÒS¢<Ç}7å¿8ÒË;P®+¼ó<ʬó¡ùÎtä׺¯ é‘ûV¾“ýªš_àû›Þ*0ÃïΙ]ôkyý¢ó³èì)z§ÔàgcÏ…k­k=¯~¹7/tæ1ï¡Ðkƒµw×¾à¬C³šÞ€Ïœ ×‹õáûîAxPÔ“ßôHÑÃü+bˆöoA£IþÛù*¬¤]õeqÞ>“z,S¹óAøw<ï÷ÀNN,Niö·ÖÓÄ,!b:ùw¯Ç«Y*ôAê½h(=ÀáGLŽ+í">‡` œû9ÿJ½ Ó¼!èUWœJ­±°©Q{n ¼õÆuË6t×-^h­µ5ß2z…JpÐîŸù.þ>Ú3wêüé<{Õþg|Ïý^ù›GüÍ?ÿôÁq®±}Ë•Ïß>®ñ׿`u¿=[› ôö›¾?ˆ'²çb¸4×&†Fžë´üWuŽd¯zR+5Që¿LNÖc¡ÞÁ¨gèïCyÛB3—ëRÞUÔnKyF|ÿ8ôÐô€Ó m‚ SŒ](ïË%äÈölå»–¼)Ü=5ø,ÎQõ¸¡ó9ð~Š7kq§5ê~÷ ù3¸ÄAžÂ{>ß?‡X4Ë îQ¾}ÔUYÏŠ¯›4ÓXŸú¸°áiþwtv®ˆ]ÊG×à(äÚÔ‘‡ò7:oz%ù|¸å5ýûÇ5u?`@çZOøš:EÿÖŸ‡>[Þ™Y«röØÚ±{1l…¸O}kßaçÑ,]4¦™£‚ÅÝ&O$4Ïœ¹¡×Šºq©ù2h,ÐõðŒð=$äý)6€Cö5Šòã=b1^ÀYKFœ¸OüÒ3Ág$ŽÙó/Xá•ùï†OÝñLáɳÙùë òO¦¶TüKßI|ÉMgÍÿÚ¼BúVÔœKõ½¡ <®'Öä5ÂJû ?«ÖÌëp§Iµ^> ¾i/¯A{¦Ó6æœf|<Äeø ñ-a”E{^ϼnÖàŠÌl"¯¶ë¶çôïhí&Î`g™­1/UXõ¤çzú‡Ô“K]¯˜äùÌï!¬:'üï/ª–U¬¼ž=öÔœh¸ùè¡Ëй7ÅVv±àÝžk9Žîœ¾sÒ¼é»ý:ÑòÙôø‰ÏÒ\ï³k~ŽFN5Þš³qNÒá^GÄtŸ X•ïÖ†á1“Á{â×â”9„i^¢Ï’ùE™'u8ËÄü‹ÝhlÉax®±ö×vPŸø›ôïÒPEŽ0Ë!êõÔÞTjÕyþÎ[ü® ÞE¹v•O—ïKñáž»ÙïÙ3ù¹ó/¾l{è£óÃ/¿ú_tÙúþþú^;ø­û=dçxíî¿å—·ìóžôñ/Ýÿ¯oÞ±Yå½÷ÿ¤}ó ’w—jŸ³ö~³Ï<|[.  œÞÖ*þæ­Gô¦Å©ìc _,ÕԞˣoSü¨\7=$¡3»ŽîgE=ÇçoÊLq¡’£ÈWÞµ(Ƭ¨µ¢®¾µh]£íšSWø/)¯Æwk ÞŽ™»â‡ÅÄÜGû7zIη¹ÓÞS++Ö¬ˆ‹±Ïäzî´ [`<ª±+>BËì/8ß÷ H÷{>D•ÚOg…ãâš&j?Õfëȉs^Úã|ø™Uyæ$ÖAÎ;È—Šu+Þ:uä6ì%Ãên“'iüîMœ×ô’Ü©ŽéãïÜtÔò`®“OxSά´wÎ\2ð­ëQXºÿ°}Õþ½ð#MÓ:䎟Øjë-zÚ௉Czï?ûnw/ Ï$îu D1ýDzuó¬9GÀ}À•˜KpZ³!v>ßö»¶>ô=©žÃ#y¡üÓ„ïq<ökøb(¯(ÒÙä¹þn1«CüŸç’p`ö~:L¶j :WÑîä Zí«Bؿ‡<ÇîEë“~³Q0‚‡wšªI|xzøœ–×>½•}ÿ ÜÑÞ-³}uvl‘³“ïÀŸqnJÃ*½Ð´7\H±Œ|£€q šá¬:”g]T“žóþ±Q;“Ï-՗߭ǼVmºïJök+§§„D­¯3pÔƒo—b9^©?Þl½¿[ÊeŠöÅ Þ‹3£×#žk> ¾†ä/¾àzñ…Ù”WÇñw.Ô›Z4_?ì-i™è/(Cö÷áÛz!±lÃBT?Œáç}~`Ävª¶ðs|–VMú׳‚]É•7ë'Ï?"g¢v=lúVIñòGìØwãý°óÊ|ºyýÔWþàGmÆÿö›ÿ`±ÿeŸ¼mëá]»¿lúm»ÿà+Â_‹Ü8®ûJ§Ç8JO õÃÚµ–^+ϵQ‹‰›YÃÕ““Y Fs'ï­ôD›Õß .CO'9çm§wŸðÀµµ ž+½Dj±Ä}Û{]´¾Ÿð(FË RzŒžkýÜÊó/åžT’ÞNðŒƒüœùÿ^[À‰‰Í̱‰3ãU¹ŸuFápSzwÐ#dï L>yŽÊžÑ=_¿bƒs•ÒLfõñß]Wá™né;Ñ5.ˆãCóÍ+ªEÑ_uçÛï©Y˜·¦®K5¬x¶h‰ùÔÈúwiuö™^“éûÌö´8ÆÔ<‘WÛýñ9¯Áoá‚ЬRÓr~·¼<¸žýRÞhÁ‰Ü¡<-jx8c¸zjRõq¦o 3â§„8­ 5µYìÕsþ` ¯½âº:x˜Às'Ao‘Xיּ:çêÐ< 2‡£~éu*¬s=ã‰Øý1‡œ¡å®çýºô] Îùi{Œ€ó!>.åWyªy÷ã½¼8ÔÌY~Flä9SWQ÷‚‹ÏòŠQ\g¾oz¹ïÏx}UóAì÷m¿©f.Ô|CÎ=Ø°Ùæ‹N3‘9ð¡üT3©o=æãj¿—ð—_fFÏ䜥¹ÀgtÕù&€2½\¤MÔKøÝ1×O¼ì>Wfßs¶Ú,1‹)èÌíý)®O§4O(râðU‹™%ײŽŽàñC=È‹A8;ܾ®c¾m°@Ÿïa8g ÞÚÄ'a+ ´ªðôì·^bmÃ.ù|fîЃÌÙÚ4èç kmgnè`¤3†¿áºÑ‰°_F¸ rej½£Þ×yJï)­Ÿçèµ¢'ðLödÃñ˜/:ô ³´°öóŸ{ö×íÚ{8>vîxÐWïþÔç~Óåw?àI—󃟸û5—þëå‹_ý1;öÙ¯yÛ·n?ä½'vnÉ?ìà=÷<¸ËÖë¶ÿÓ;|`ëÂöæ¤8—g:[+¦!Œ8gôÇè©ô á»ö…úÜ÷À¬žÿ͘ÙYàtgl´Ï·w„vh–ϬzÖÁ5] §*ìyø%p åÔB£pàiHoyúœ÷ÄÝ…Ÿ„¸³Qü23æÐ­Á9OÇÅÄwÌò饚#ϘÈinÐ:Žšš?×;¯òQëfwáE· Ô«Jn+ßeß—¶O´oVÂP}^ˆö¸ûy3—Eœµ×O]?pá ‘z°éAœÐ¬ÄX|ž†[˜ý¶Gÿ/gNY¶¹6Ï’Y¾~puƒ´¡Ý9S"G¸žz›eÎø»þpµ§Ô#Ž O¡š¤×ëÁ³ð~œuÆ{´çK-87¯~Íì=SðÔá·3QWH'¾Åy‡&}ïk²~Võ”æ¸Ùž2¯n=Ÿº”/ö¬þqOÎwôëÏ>CùŸ%Á/ß°–®J5è™…0Ä àâŒ[¶ƒ¥ã]¿©^<«i‰³1oH=Þ³OÜ ~ÍÛrÆ÷%qôy>èÁÉsN} Âá¦YýVŠûhûÒ¿žÝ¹ƒ>ƒ³z¥9#é易Ћ‹nB³ƒõ^7ÀQÄáÇY,Ýü3™Ø÷C›Ù"\ë¦E`Û¯ªàNç4Ÿt3|óÒ ¤‡}Ū=G>I=H®¼B!î_%ÿœO£žÏú܈Ülns^_Ñ:Ø"¾QG¢É&—˜¦Ådòtáäk¸&p‹ÿKÎúÙ'^x„“h9èEébÎ6óň`ƒM'’ó‹üÞÀ”•cW8zteê¿ 'Rz1sá¡ÍX/íœ#–\H,OõšdæH{¾Ó°aæ^ƒéœI3ê(j1{V÷}ááO¶Õå—õÂï|¢Íÿ›žòw;ø°?8{ðÑ?ðÑ;ïÿú{íÜõý¯Øç›^±sü;®>~ÎÆ³­Ÿý°ß²sÖŸóoÝï!ÛpÅà(ªc©A'ãÇT÷tøRx]Øó‹~°ëªéöè-"7C?¶ÕéLôÝMMísÓ¥4§Ë¦µ˜¨£9G¤Q£g&ù™Y½PÄkÛG±&t«KyëšþáªzlÁ”tæP‹/ÐRu5‚Þ¼ÊÍSk¦gwÔ׉Áe ÷™Bß{Ÿ ×± ®V~M_Û¤ºgnÿQ0›ÀHÎ.”'uür›WÈ6zC×p=y]ù°WáüM˜-^ùþfyS¢ G¦wyVO÷L^šat­GÙ&ƒ±õW\HÜ…˜Øi©+Vp `½hCæ6KÐó<Õ»þ÷™AÜasy=šsš.a¨E±oR]TYkÔÐà Ô˃´_:g¶¢Ž¼˜Z»F;S›‡Ú…ô*1)†v¸÷uúCF8I‹·:Ç;\vNl¡~:§Nôº¢w#ŽK=2¯·Ó¶ùuÙÌqiëjäÀ'Å·¦O.¾x§ˆ#?™¸š°×J^:k&žá öÿ›N0½ß}/Ê·U=nñ,àû”#rÎe妉ç‡'œ÷X&¡µdëj7Órü“ÊŸÏ|iTËo-¥#ïæ|'M$!XØPŸS+×­|¹}Ԋ㕜~z­µïsê““SÞT©ÁÀð”K¯ù,EÌþUøzò‹uÔ`ÖÚw©Çì¹°¼à]›ndO\çÅÚò¿j©é”æ<‚ù Už[ßô(­ßgó~»™2^óéýäú´ë²sK9„ôç‘W ·rþ ÈŽÿ_Ä5bçNAGËs§B½Ò%₟-Ä!z¾‰¯Ù³%mU¡¦ê¸ò‚>Z{~zú=oÙµwó}Ÿù[;ßwïÇí~ù…ÿ¼okÞfûÞþïý„»æãüaÛòÓTIWæú*›hße1Ð<xîÊýý¹[N½‹þ Mº]›bAe-ÆŒ“kÄ÷"Ž?¹9ù]ùÙÕùÎà¤<³SêMî°žwÖVà$äcÒ ¢Eô½OžÛxèðtÂKMHä¿×?´ûPNÇÜ× Ð×iœúêJüìú¨ç—Ò7~âv0Þª˜WÈó”?®¥Ÿ]Ã×i}´.¶W™%?!L^Ãs‚QÓ3΀>Šönï¿‹žO(ÿòï|ñ©g-ä×éùƒý³•íùá¿Ùù“Û£Ûbž˜<¤“»¹yÓ96fbïLZe°!xŒ¢ØWä˜\¬ðuðž¢kõßµïêñ.³pÓRú7åbþüì½èþ&¸xqpWš^58Vké/ÌûÄe3¡i…Ÿ)ð%ÇÙ{Ç6ˆ{ãzíú4 þk¥õ2}ÈY3Êß{Ky~â9Kù0i¬÷á}k¾ŽÏkxLô¾)·œ´þ't´Í£bœf!޼òè¢^'?þAÎïŸoÏOq™Sµ×lEµ= U~µ«qØ#³éué°ˆ…b!ºýŒ¡Ò8Ãÿ£/Ô4öüà/už)ÿ ͪøv¼<KÍFÿºtÞ<|ø¹ò f˜®Ñ®).g.d9™>?ûZ¤ý¡æYkOã-QXG¨֑ߞEC™>6}Þ1ú‰÷€MŸÖ,M8cå"u7©kS.Úzk–ês—îiÁ;¤ó¢.Wœ\ºô–‰uÚ÷Ùù€?0\¸ÿ9sr+VœŸÂ‘jÌÛ½•úÊŸ¡<©}Ñ‹Ûí‘bs‡™—`õWã=#_3\—5(}ZÑ:N} 9ù‘ž¡°Ý¬ÝåŸÆ3¬ÄOø&|”ÐNƒð{£Wp)M®jiþƒß”W\æìÖ§p› ·#Ý~ýuF9ý‘Ï5Q.^ÛY}òn –IŽÜIµhÝÔþµ?³6àãþÍ7ï~ÇwÝm÷3ÿò—‡»œÛý¥+gvî¿~ÈŽðÇí¿üÑßyÞÖÏnÿÙÁÝí;Ç÷µø˜7ÿ!ø¾å[ŽuÚzµÚ[gIQí¾ø…÷¼d?±á(äŒäÎpPí =AWŸV=à:`ñNþ¬šorÌ8G !ìü¸‚Ã4LÞ0<:›fñkŠ÷éqVï[qôÒHN»™=ÝxºFþÓðá#Õa—2` Û„CØž^K¯·Fo2HÏn¹–Î[Õà{ôüõ¹FMü0u·qÆö\£–¾5kÖ‘}¾zSä¡z>¯-1uKÝ‚#Ì]?š-øéÒó=Z×¾O\m¦/Í‘zlÐ:_L¾SuØÄ^ĿÛ°×í÷ÉNi~Pë{`ÞäyžS!•¾¿¶œ>tqòT]èÏ*u½Â(”›Ÿ\3ó•ÚA¼´çv¾Âmñl›¦ûº4G¾N¯ùŸý°ßZï>dgÿ½÷þÚt}ÇÏ~Ÿú_{’¹Û“pi»f•pèñLcÞNôø¤Þyv@Ík׎vj^úU8WÖ±†ƒ*§J/⣭yñÁéŸØyLá­w>çäê¼A¿:u<îUg«¯#Þ‹üM<ž[õß«…ºZëpÔ~^£ýwÕºóuÉÜ’ÛÒ?q "kKaJ`‰ÉÍòJæ3ñƒ¯ÐZex²‡t•…¸$| ïfÖyžÏèoÏ…¯{Îx‚ÏÁ­];¾ÀG™{«Wf‚B§5„æÍ÷k«›ŽàÞ#‰üþvõ,„·+g¼8Ö~×÷™½G[p¯v}ô5¡áAMŸÏÏgÎÒùôü¡Ÿp¶)MµzH½ŸˆY|~f‹G‚¿òØhõ9—jž",Îkq«¢÷î¦|.i;ïºËVýWgi…íóÄçúgÛû Nö nâ(¹¡Y@ü§â>ÏÔí¯ ¶²T¿­r)ሱ†Îi¦ðfôCÀÅ¡)Ò”êçNK^“qbîŠýÅYÒ0È#Å· ©Ÿ¦fÒ¿ýýGr-³f6Ÿ ‚5g<^còËíÌóo4Žà[¾þô¾Ý«yì>ä`-Œ*µcÊñòˆ<{”Ï壾óÄúg÷HqÝõnW†ºåQsÎ.Á»9æL/£÷=öHµðƒª¹²oåcEnZEž{\k§µHk%=A7g0üÛèao«Uœe†ý«¹m?¨—©èùóPsó ººzs މýxµùÚÐ{Íù‹7ÊÔöuêMFþÍÙDÏuÎ }CàmQcêýzìÕº)ŠçºžóùÁkà—ÔƒT[œ½ÏÕô´ºæ™º'´IÒB£/¸rÖ3zÑa/sVðÒd‚5˜¶º›gy}â3Eã>aÐÌ-¸cpXía_CÄ*žý„pþúÞîÙÆL¢Aš>4ôKõ,ñoÕ!^Ïê÷VªOª¾ËÖ—çŠg¾§ÑƒÚuZ] ¯¥5Å9¼Eì Jã]ÑÍé/|»ÖÊ…ôäàlWO§úQO '¾8¨w4Î×KêY¾Ug}äåK÷—|š½Öü07È¥­7—^ÎìÅA˯gƒGkê?ÙsZãkê@p í‹Qµ gCigÀÞ'ÿ}O¾Ûe[ë÷ÛùûíÇ=ð þÏS¯|Ë=´û9ÿýžõâ—¼÷Ö_Ü—‡Éâ¾/¼Ëþ§<þ©û/|Ç[¶ÍWàý/øì}ù¤¬õñ c^+ï÷øÍ|t-Ôâëp´¢<~‚ß±=ª^ΩñÀ¡ç þéæE‡!V>ŸÈjr½»¢\ÏÔìR³jd,¤Dæ)<Àß¼<$®Ç+Ñ|gLدêN¿ŸÐM„ÿî¹ðÀ[ˆŸLýŠâ©¿¯˜vãº;{F‡þyâ©ÉÁÉÉ“Á'f(q¾?¥û¥Ç.s jÕ žódbÔ¡¾FM1ÍãµÀ¨G ÄY{…Ú¯ QA³fß¡kÈ><å[cÓ¥:þ–ý”èÕ¨¡¨™áÐ4k}Vâ88œž4ä9Ÿ5žãyß?}ï8¾ CèЊÖçBx8{z!U±ó¤tx)Džv%óŠˆÝÛÑãD™Î4_ºúè˪¼Ÿ!ø¾‰œQõ¯¿SåÆô4)LjyÜZ‹ŽMhŸWø}Íí˜À&ä\õ¦kmi]9FÝðóxγæ›Ê;m‰â˜³°ŠÛ+}ÏÔ¸¬ËMy8RCH«EN­wêùšï?½×.oHÏüÔßm6OúñP^¬ÛÌ™N~Ÿ{O C·¨sxZ¤rÌÒ#Zž â6b6 u"¹4Ï›ñÃæ=3ioøY ít!>npÍkxòOÅCvèA´>RSCìãÙ5¾ï¡_xÖâÚÑg½r÷Óž¾{öýüºÝú÷ÏÞùà•ŸÝþé½'î~Þï¾ÇîûÄò?ì|ì½çs¾ão·?Ë<Ç[ÿô;ÇùËþ}_øC Ãúì ßk³\smùiéˆ^Ô'¤_“bº­!çÀ¸yÎä]ÔjªO øû’|PDäË¢Z_,ßKª…}rê‘1<˜¸D^¢œ¿Òï;4 ó4ZòWáøÌD¿¯:ÛÏ4Õ8ÙG"®¯ êá¤S}SZaö±ÿÈ×ðgöY⇊â˨z5×£pŒìG?Ê“˜ç›~±Ò踮•³íP½‹KùáI³9Ö-î^RìͨcÌ=y£ÎÊ q!Ì8¢÷bÖHÿøõ«ÿ¯ž–O!ü‹ÎÅ¿Î<íõôç²,j›Öô4Œ%æeJÃ_ÃKåVΰñ1êò‹µa›·&—´L_ƒ#âVæ¼åêU˜!š/¿VÝç$L[½.—8Ï} hO§÷=z‡!°?ŸXz¦‰m)&33¾ÀÞç†o”·ÌwÐ>šÈßÔWáû—@u«¯wùÖß)ŸSN!¼åö¿ö”ã†ô:§£<±èLñuªÜÓοôÝoœÅò;üølñ:cÑ/¾¾bœ©à ¼×{IO‘|øÜfb ¸)5Yðé§äßøèu¸KÚc éÒÀ µÃ ïʨ/pvi¦ÞM‰E ·Ê¼ lÞðênû¹Î-òpž¹ÇIéyÇÚ/Ñ'¨³Åß31"´¤;˜v.½<½/ìJµß%õ¶Æ™$mS®qƒÌ«hæ„×Q·ä_øÀª‡OüFêÿªâ:zj™Š\PÏ]ûó µÕzòx™$,i¡üC9øÞ(ê:K‚ž\{ÂÞ½òϼ6úØA}Gðœ=`/¬Gjü¿CCvBýJ×Ödûa­=Aÿgiýçéã-Úo©¯O82hÙ¼ÊT³l¬c-\T¼kBs¿Ÿ\vÞÜmîÏcXÓW¢ÓÄÓ‚§¿qM."N¦R[‡FàzjX ô˜ï…öÙÆ>¨~­úÝŸ3\Çàš“+ò?“þFhdàæŸVäP]üwà›òý€ÿŠxx=ÂÜÂ{lÈb³âóJkªÆ¼àÓlK=*†G¼×A8Vá¹à“A¯ù–jòI¹üKæ2݈g‰¿wä”þ.Ó‚û]†V¹Ð{¢ÿŸØ¶}·f—ä–ðÚKß?ïÁgØ÷þÀÿaãöÿá3žððWïÜø™o´Y€ëWþàGíÛ´ß½çëWë·ßüæ3¸ý§û÷û‡î½‹©Ÿ‘®¹Ñ™µ O'ÂS‹\{hS‚'øê9ûi÷ð ­Ô pXô:+oó½)¼*ç¬0W Rñ<{IfÍô‘û­°bþ=|¾rÊô³#z9àóì{ÁÏÑÆð¬X[й¾¨Ã¨wàAy«mID½áß¹éýØ1_…<´ÇÆ…/ä̹ËyµøÌ¡ãî«ÍE Ý®p€¬cuNЛàÚ-ž•rSr2©öûZ÷+Þ:öeø’f?AÇ]Žä ôr(ï/5K±ç'F0wa÷ùsÝÓ*òâÐU¢ÝÇŸL’Ù ZOYÇ2tS¾nïÅ 2ò/å¾FÐçÇŒ³ àà @ì™[]ÿ2K%]h>ë«î—{5àÄs±uÀ¿‰Ã›òrUÞS„σ  û,ôsNîXM¾W×kMêÛòõbϬœÞKç@z¶²ç­÷l(ž5=@èܘ[ºR}X´îüÙKSê±=8OzÞs.6ï™ÚdO„=/fwxo™ŸÏ¼O|ÊBw¦{+`*Ò™Nô¹Øµ«>] ÷÷5 ZÍ÷´]ã›b†Šç¢ößxœ´³?pDa_ÓfÎAÝÈ:2°µóʱÎÔ–{L}‡žwûÃ[ñ(óq…U=âþ÷BWžèÅ…1NWå˪X3*ßbnä ìÜcîü´õƒv5´ïwpT4.Ì‹éïsVïÚ¦ü Z¿Wœ››9·'Îlô[s›ÿ8á79È‹ZÚKô«ôÄ®x®öÒ1Lð¡³<UÇ2' Ø×)y²åh6”Çøs ÍôEÇÑÐuRoÉ…Þ8é[÷8#RAm¥g5qVðLèÇÒ>ÉÜãª|¶µ·ÝÓÓ|}?ø´OؾåÊë¯[?ÀÎümÃ~­ž~ÄQ¾‰1›Þ>×zügéœ ë§Ç,G<¡ç9výM§gŠpšøOìMÕ³äFÒFíI ý¦˜±^#–çZœxçC‡k©¯.ñcÕ&|_¿FÑíd¥Z~!ëê'iñªp¡ô'Ûßgv°dêijÜ9¡çôp€ȃ-–ØúãýÚ÷’G+Ÿe˜Þ?Þ(qžD©?‹Ä¤¨½í;¸~ÅíªXZ»ª0ï¯aZ¡«bŽS¯¯#¡ tY®}­-î†5gÜR3A™å°™ž´9 /ûû#Ƈ†…³V¹º_£0Žô£&ÀO‡×î]Üç"³MjW âýî9Ì ®„lyjñiêFæñƒ|NkF{÷žu¶\HOçÆ=îeMÿÙ4Àïð`¼Úæ[–ÓmþVz0ScΚG„îcðù+QFÿúB¹Ò‡lÏ’>Y»_ùUU8IfDo6]sÆ&Î]Å…;Å]aXœéýNöÌ,6’+vØŸïé‚gä34®¹ïàcÜûÈ{‡l1!g…e Rm^«>žÐWG1;ÔÖ0˜ÏmÍËpìÞ3¶¨9†[rvžŸ1Ìï}4àÇíºñSÙô>—3ê­‹ùZápcúÐ*õçƒ×¬ô àÁž7Ùõ)ïŸÛD®¥_ˆÐª©¦¡®«ÔãÜ+søt†y|D£EÝÇ{Ñ÷g`ÏM¿˜ø\ÀÜt—+0 Å;yG {ý³ÌîÉž1}šqî„ÿ@ç·»@çEÿ ¿ÖðŠK¹6gõ±Ü&oÓ¹ùÝLÄ&û=0Nj[<ÕX“ÂáÇÀZÑRGn¢:Y}Ž1D5X˺*Ï´«mƼTziÎÙ“ççaè¬s†#ç5¼ÖÞ øè‡_gWcù»9%Ÿ áhµ6ëM8ªêxwø¬Ìò^ëÖçJzÜÄ|ÁùùûIJSš Îs"š³_Ü}S*>MK¼üfw®øŸw|ò‚8Ä{‡G¤uäßÚÏ¿?¤ÎÂt 72KfK9󄡇Ï®E½³Ù9»•Ÿ0Ç­I»'&Û{´ó—< xûóœ<Þàyà;¤»«ñœO 1f­ùu ·j8sð“ôýr6+ñ3°ëìz™Wèqä Z†Ôé^ÐïÆ'œ§4<&ø{´ÂØJĨ‹ùYÊù}-§K§["~¾ ¬«´=u”ŸÅºÅ“¥éäâ~9?ìžLûî<éÀþûSŸöožýMO?xôëo<øÀ3Þ·oÚØ·¾øy_úÒ›Ïqšë/zÐ}÷Ÿþ¨·YO¿{}÷Éí}ûþ˜…áçYY¶Y %r<÷ç*ìmù›“©ÉFb5žÖªi’ os÷ðM>žwXvôEÂÁà¿:ˆW9”¶ž~4Ū¬íá&„©Ž`ñû7eß³°Zð_âøš÷¨ ÈÉU©Y7åC¬¿š}àmÞ¸ÎãQ|zÆÐŽƒËZšÏ¤žDŸÏþQn£wrDÌI/íQåÓ‹Hûu¤/¾?³Ñl*V’c°–!û‚¢G¾ËAGÎ<Îæƒßƒuðsé$¶æÎÛhVgP¯»]ü«b~¡Eõzb¡Š›Ù—¬Z¿ÒO¦ó0×G›…ýªâ ÐRy ø;3¬.êTø^ã+àq—IåŽÍãÍÄïéçÿªOâ1ê¾³î3rmQ;N¢yEgž~îù¡ýú`ÚC`ô¥¯¹…Tíí•xš¢Ùmép*fµ*O:›ïÓ³iÐ, Ù)ðã`Ú7é{ >sTøy÷#1ôPóY9;¤ññ÷Ýf|ìÑßî¹63vY¯ÔOò¿Ë^wb?z»V«#ɵÐÂwÚ¤qØ5}®÷‰¹á>·y ÌÞž&âÈhl”'2/‘Ú¢/õÁ,[¯9Ìt(¯‡YÁ.¯†M>¹1âQçÚ翊ÓS̨¼Â¾òƒy‡CëË×{¹GÈ,‘Ô#ã/2„~~¡z}¯¶œ-?k lO5 ½Éïƒ7F,k>ÊâîÊÐf”úûQ H­…çj¥G€='yˆò7?7ì;YãKi8„ŽÂ±5¿ñ¨À*nöĬ~7rÑ«9w#x>úÅMÄ·Æ©E§Ø1±„¹i¬ÊÜüœ8êÿùsÙùÐKÏ:øæ¿þ×—ëýƒãßµž¿õß>â™·>øOÜßçIÿiû»9Ø·|@|Õh€r¯·ÌÜ‹jô‘ƒøã¡õûû·ZNø¶çv’OØgo±Ì´ÂQ3¤^Ÿ¥m¹Uê6ÆðR¿¥Íè¿.äÒèçµv=^¨.Nž‹\šZלþD`û¡½ˆYªµ+{…úÿ؈ˎ9ãñ¿Þ~!Máv0äLû Õ@ÌóïwùqLà ÌlBן®œÊΊµzÛØ/‰#Áu¨fgžxª÷Ñà—”gýì}w$n¾Ùù`*?¡§}aúT)ëýîGâŸaׄ‡hpB³Ñ4(Ÿ¡7L36䯒úûI¹ ½GEXA=l3„GâWÄ›Û+œüÕèƒ/ƒ|pˆw`p›ÙÃÚ)t ŠIÙ§%|{¡µ^ñe#?ÁYœ‚¸ÂÀ´Ñ“+ÁßP M[¾RI/C…»ë0MÌ5XjV…ý»ùw„&€¾ÞA=ºªË×ô\ök\µ±cÐÊa \Vwî”¶þbí |ûÖä`•3í+Î0çÐ)G–¾øVÕb«ô¼ää©äìuh¾~]¾"ýl¢Ç\1þ&=ÆìùØþ¦ÿ…3AqÛn냺[µå´Tï¹í_ៜc:_â|2"½!›òñ¡¦Ò:È>+}רùkÔ“‰ËoÊ Ë¾O3'p ÎVtqª‹ïÝTÌ !¯]v}‘¡IqÜ¡¶ŸgÄ^ÜëúÈ:ËêßiÔ˜—²–{BëWÃZN®ûÁ¾Û´ëáaÚ°³éý'úÛƒGß½Û;ßÌ+ä“êÒäìUÛ+ ]”þAÏ2 ã’ÁÂmrFÐ/G”ë`hý(uSz”ãóÝzù¶žyÇOn¿é·ÿdûÁ|‹¯Këé;¾îí_xÏKZà ÍDšà)ÀÑ3ƒ4œó ëÜŽ<üÈ,o"ôÕs× {.ý-ÃÛªÅÕ8?,þ ñç@¼Ä×hvaß÷u=9õƒÝ«z”àz\˜µ< +°k€‹Á™‡à…ü¾Ûº¼V:Üœy„Þ-Þÿy|,'á •:•ü‚^ÀSš(Ü êýìÎW­¿À?yæ]^ÀÏWÔļ'òKxièÃQJ¯#û}fø¨ïœ8ZÅyó{éóaq@þH©×lúÁÀ2ÔÃVÈéáðçèe+Zoð$[Ú+xïgî® ú§2$÷¤=[S»‰¼r«q0®EåÙëï%¸Eçú›@ßd÷©yZ#ÑC¡7Ý”ÇØ·ÞŸQ˜ƒØ|!r& ý¾åØò¶*Kõ¯Íšï¬˜±Æ›Tš5á»/£W(ç¹úÙ?¢}>ØmN€Ç•g±Ö^:+áã\‹çÄ̘Õ#¨<ÏóÛY:ub#ûbïð yaÓJH,z?NóƒÙ¨V;hÛOô pž ê g…¹OÁÿ†VUÜÃt[Îÿ ¼¹ÈxŠòƒkÉoÀGJ#ì÷eX<»ððqHÍÓÅœïÉl98š¡y€Yî³jœ ‰¯Ãmà©1j gÍ/>{ mËyéO®¤O¹ö¿°ŒKøJ³žó¥&ö0"Õ ¤Å °ê=êÚA}€pHx u³‹ñKíæ˜m”=H`r¿¯¦jmOì1 ¾¯äc=O¯Ê—×þŽ8ŧkœÈžŸ49óÉþLÚœµ<‡*šö!ôz`øUïZïtC9øþ•úˆ|¾õ›[^ÈñNÉ/ÉǸq¡ÜKýaZ;7»ÿåí/ù‡]ûóŸyìÞî¹oü?»÷üßxù±çÞzðºõ¶ê+Ÿ|ùŸï}aG³TwÎÝýGvnþò»œxûXŸyïý?éàø÷í™×ÿ¼­ûï›.Ðñ5-Äã.äOì8Uh¯ÒoŸ-]+x;kmâ<ç}6gÔ£o=¥Ö±./¢5I>Á8âæÓº—øÇ9͹òŠç,ÔëTÄ™Wõ¡ËKýG<Æ1«dPßúYfV°×—òth½ÊÑsørzC O$—cv‹Çvx&ô ƒôµð+Ú+SwV±·'å@:~.½}Ø#Ø ù>*ƒôÕvò4U?„×Öehþ^%f4;ÆìqLtH½1µfx^ôrÒ$ùu€ƒÂ…Ü&ŸÓ¥zñ¸ë£>…sך*Cë±'׳ët­º¼\käÍW¤ ôÜtÖC>jÌGñ3Aú_/ògç^¡¹ƒORþ¾E‹«^Ï…ñkPŸù¸iº/åÌ °å† ž/Â@ yŸôÔë#û^yÚëÌ+8_#w:VŸ=³4Çärªs³ŽÒyçï8xßÀ·í³ñ¢¤&l÷Étü×êðˆyQçƒo(÷úúT›w6oyÄLõ(8¿‹?¡ß?ø;ZVöƒæ6£?UûúAÌCXçgÙÄ=mæ\ê|wä[çäï·”‡.šÙYºIiž»‹ÿ­Š¹…aˆ=–d†ç>ß#ú òᘠ{|jéÎZ|@r€Ct½=œ‡+Öã ÎäÆÝEÜŒ\â&y{Ê#b?p_á»zvMÞÇŒ&e!nÆ®}q(_žç,½-<’ÅUrêOå)þl­ö@ó,qžKöœŒO…+oú3ÏýÅ)qÖNÊŠž3g)k—\.ûÐx§Ô/ôãw^6Ì\ôü =—jÞ±[|RÖ6ÿnN¬ÄžO‡Éú>ý¦w=fÿ8g>3cKü×6/œ ß{y6Ǿ'™ÚÝçš³Òþ§ÚZgvôA*Gž"'¾yž1§?Í«ïW/=9#9ØŽÎ Œ…™Ïò¬¦O™~϶) ¿Fº—zc{nšw„†CóKSS1¡I€Ëkµ_ÓDZÞáNáÙµçÇ–o„^õ ^(œ#ýsQïÌ‚½Èþ£®>”Ÿùó JÎ+áJÂ7Цøú…ËÝŒ™ “f¡û9†ßÝ,mŒÖ€°å8ÏuÖeÞD|^vóWÐ-Òw™‚~º× òÝM‡uR|OÓ£Eêêû<'[Ž•Úü‰žCõtjRÅ›d–Îý‚熼O=^Sã³×ƒ§¸BìǬp#øvzɃ³)ÿZpΠÙAs'®§PK6þœ~ð·ð(¦H9ûH~:„ֳ౎(\yŒçrãB¾°#5/®ÙöÈ=ÉÕвªçq7O=­ž¿.¸…sêeÇ¿@uúg÷±é0;Ï+¤õ(]ïõ‹{#´™WÌØõuéñÔb—½;½ÓŠÿ6ž™8°OÔõƒú(áîsçÑ~kßA½&=toâ׉†D|YrrÄyêJøœ[6òLÛlž‘U˜IpEuê‰Å]¾éò®ýükŸý»ßöài÷³ú§>ø´?ø¶WoìÜö rð°úm;öŽßµœ·ïóSݱ€õ÷¿ô¸ÎÇâa÷yÚþÃîóû¡7¼aýwŸòçë8knBA¤cš¡2ÂuÅü²Ðz·u×<§^;òN=×Qu–¯ônh£Éûù93PÐYÃñò=ñ^¯Ur íɱáð^Û§nXõoæºèúz<~áigïÈr[yxÁùªO)ÖŠô|Y‡Q»)G‚Þ¢Æ$w4ï|ŽŒšN15çï°‡è—b¿ÑûB¬àl`Þ"3¸Ä¥dŽ ®­õmX¯Çåb9«C8›çwÒ‚Tø0Î:Åêuœï9/ÕŸ×ou·aÄú–{þÅ÷–eógòçhÏŸù)ª‹®¬c!œ ×ç¦í·^ý¢Œ?ÔÇä’ÊÿË)Í,A ëCá{Zk`…žå´û|^ä:+ó#À5˰‚Ÿâ8à˜_ Fh×N0ǼoâC,ìè¼t†7£‹âFzÖ)'¬CΓŒ™€Òp äôãÀ¹vØzêÚáºèÛ’#÷š|¥3&5„`ä÷Ìò ·B_¸fxÔu:k_v–“¨?…yun\ñŠþûs|¦Øô¹Ó“b?ǃIuΚüd‡0ÚQ³æ×h>é?Ñú˹­ÊÐøß•n óòyå¾é‘ fBÝÌu,sîÂ¥‘ó•¼|½®¢VS^áuÚ¹VWƹÏú˜Õ“07_ô”ôrT´ |g_‹±/yŒÙwЧ§V™Xp˪»+{·iI™sõ—ò’©ãþÒ›¢åèÁ µ„k׳7›o›ï9ôýäš–7tý=]?¯Ï—X £­¬)òEæŸÀý‘sÂe ã¯]ý³¢ž×3†£ÄÛPs¿SC=‰³MOKûÝðüiÞªàXëOü¿ï¶úzÐ+îwðao{²éý·?cÛ®û½÷þö¿¾÷à>?ÇgýúÊ+î¹o×d±=ÉUùÃ1ÔÚÏÈàcæ1:=pciÒÖ`)Ê_ µ˜Ö žmeh'™·Þ÷…?´F¯‰f²ÓZmòÿ±õ/`–¦Uy7žÆÁòT¡"vƒÈŒÞ¿Å~Ÿ]U­°ŒD„ö„¢Ž‰£‘Q>Q’Þ 2È1RAP±?uDE:õ>»ªñlk^4€«Äâ‘D¥“|µÖºëyšü½®¹¦ªö~ßç°÷º×½s4gë$¬.í³a–Եў&î¼Õë‘´ˆ‰!l¯¿TÎ(6–˜­=uSê·aÿC{ƒ>þUê²ÆÞ÷? OH]òE°U=;º¢`Ép‰“Kº’fyÈ ¾oÌP¿+ûαÃÂK¼/Y¸‹â‘cxPûª¬‡Æ÷E˯âA©ßþ^Y5ÝÚj‰Q·T 3öìú5Ú€ 6 ßLR0raÌÀ˜ÀÞ7¥{cs“ôÓ„‘a#Šb¼‰~aÙÿôCÇûŠxõ*ñcr$ì]Œ'3¨ÿm³é…2§r]š#Ÿ%ZS<·rây­ÍgA¶ëBÎ’"æ˜C» c ç5'+Íp;Òœxú,ÑqÕùöØXÈþÎÖBñMƉ` v¾íw¤áD/=ïÑñI·Öì5|bò}ÍÀß™À:ámÌšÕÁ9$o޼Éã„B ›{A ¬“Ú øºF+Íu×™Tœý(Ä$ûð=ÇÂ% gÖléÓØÞÄ™ˆÿ©Gþåù_¿óĩŶƒ’O ]gœÇè]WžüKú 8¥;ýêŸg­áƒèÚSNuBåþûpó‰íÀzñ›pó‰I:>HÁoª^FÏg•”zu.æþQgéøm‰ë÷\tÖñ‘vžÕóöš³'áÝ(ŽAïª2# ž5¶C<ÅJoéÜ´š'·ŽÃ‚»Cÿý@ʵàI8?Lš ©M"ÛZÀB¯t#ÿ^Ï ^p¢Ÿ‘Òé¶÷ÞÎ9ü=ûݽä§.Ûzl]»ÏåÏý¦Ÿ¼|¯·¿mù¬ŸOòü“ï?xÔ£oñù€ßø„Ÿ9ø¤—¹i/ï|È_­'h÷í;Þø“g_ð$FÐÜÏc´ úhö>Ccô¸Š­‡Åä3áûo^7{r, ï ™Ãiß‹ö•øv ~sW|†ßAL2â{gÍè’§~ í«v^£œý9Ô-<öÀïðÎq—¶ y5ïG.Aìª<…ú\[ÎM…Ÿ^>¿”X<æ(ž«‰Õu–Ä{N ¤sòÇê§qí{ÍžoZgÔ/JŸÚ³|íÑÁGŸƒj`¿ð©˜³©X;{geï•»le“8Qg¸ÊâÚ!kßq—ÀžWÒæÓga|­_u懖Šë¬y‡GÞçsIú“–ª=ÕŽËF±×âœz›§xâÎÁùŽ™ä—è½J¬øn¢6²’>’x…”xj#ù¤½»í#µQ­¯Ïe°õS¿÷Ž¡øÑµ>U—L¾-yÝíê„EÍêí¸’p¦É;ÉG„“eŽƒ%ñyMë©ØXÂÖ šC,Cß 9œ¶†ÉlÓûþw9Žço­GgCÚE¡±Ýµìk¡–@| {όԬ·ꣵw¥Ž(³l>"ô–Á!u–¹gONbσp?ñ5õriÝqåzQW þLèàcáÁéÀŒèñÀ> âñÚùQì#ó?É©y`kϨwZXBékÑÝ™#>ænJS!úÅ£QͽÕöÌ–ÙÏÅ1ÉÚüÚ=v÷aóWâôÏ껜¥2ˆ—%Û² Ε-öumܸð¡}¬1ˆ«O\'¿21s‡ó2ˆÛ†z¦›?£œg->kÚcâKÙóµfÀÅ©`!`uàBsè€âÿ¨‹ÛþúúYÌ þ>¾dëGÜa¿ËÅ`_íLÝòž'íØ:>æ…÷ÛyÒ¾kÇþæŸnø5Ë•Mëyð%÷ݶ3ô ¿²oýÛ›¡½¸„›©x‹ó½ Ï]usö¤CÀ^øzJÏ6kIg:­^üŸüö~(NVcT _öèøAU@‘}žTËœˆñØcÛƒ¨-eÖ§ægÄiλü£¦ý¼Å)q77$òXæ ÁƒoÐ,ˆý3mø2ì®s4¥íEoÕVµƒ|Æ’xCXˆz#ÂI?¨‚»‚q*§ò8ûðó‡Æð³ÓsoÀž¨÷ݳâ¨ÍL­&¸Q‚sé§€$~M™Õ‹ ¯Ÿ\z–.€rfWq²ß•õâïÈ È×5ç¥pöäo7TŸ§¾¿;#] 阧$¿ŸÅË"nġޔN5 ú}À7…u%¼’ zŸ™¥Ç™O-Ö qtéçHžPQlÚk q&½WI÷Å{ÂU'/Áéº}ži¥>{á0©¦½0›¢w­?­œãvaרQûÈž»ßéf&ÁIJLKu'¯KPKíò~i9‡ý>r#Ç-ÀEŠr­+î½ä;ÛYÝ(bûóh^U8ûâ²ÁߟVâTqÆÌÎjs¹[ú%ºÓî— ãá\ØóYlŒŽ \$jvæ,߀S Ø¢Ð'×ú2BcñަÿU°ºûEçÝïjÌ$ÈYµÔ—ÀÒúø}IüíÐÝò¿3û'ìúemùÒ^ÕýðsË;©6ëgIõ9ú|Ô£uLÿ&sNéšx×ÃÜYØ{Ùß'rpOü­âLt ñ=˜á,ŽzÒ×!ÿÁ¦ØS8çƒúÄ홄R·êmŠ?›ùWÕ ¸›`ÓãMÒíöKµµœÝ’:¡¶fÒ´ñßm\ŠcÅÞw»Ôº»­¤Œœý VR‡ 0öžæíêw3¿ø¯Þû»vwÎÿïõÎí¯ÿ²Ý×ýÃã/ÑÑ·_~Ñ×~ôîwÝüªË'¹þ¶}ïG¼ç;¶ðá¶ó÷ÞƒûlÜxøÝúÛï{ûßð¾x­3¹¶˜Á4„­Çî#Þó¾%ú8Äö–’on9c×󖤡ãÏ Û+ÄQâ`Lò;×ÝõžM`èÔóŒŠK*¶˜8þžê¥ØuÌ&÷%V†/ÐÅ·û-7Ýò{A½Gç¾(†ççIäEû:ÿ…æàš5gáZNºûûyN.\Åe•^5jª¹êä…à¯ävÄß÷ô˜k@žn~- ü)µ`Õy–³´œn³ÇOø=ÕwácÀϾ³ßh™rù{ø“pqðôjÚg¡Ã5d_úqAëeWƒø.ìœ÷là¢ßH.eW37¥y5H¯A½†œƒ"Û²PþTUköü ·J²œUÄ}û}$¶ ·X8rj‚Ÿ“GΡá±$ž˜5ËÃÖÉxøa÷¯fìJÝk6¸Š^!8`9ÿNØkAOˆøM5‘xƒ¿‘?§îó§m½Bç91×8çLwŸ™52Ã<‡ìk> ÖUÉÕ,fÅ›Ÿu‘Úî84-‘2$”ZRÌ#KêÎózÊи} ê~ì£ü|ÚWîï¬9lƒf®¬âHp§ìkÁo‰[Ž´¯ÏÖ|ácâÌì»$^'_·wˆù:Ák†»Ã>Ûs¨—¬RãWÎ}]\ØÙqø™µž¤à6©þôqM/Îsªä>ö¹Ìœü¬ä“Öf“/em•~­õè'@½†ŠGÛœ žGëÍë‘ZŽj¡Kü¸)š¨Ch±‹“uL¹Äœè Š}í÷©gÚg?û¦{Úgþâ½ùàþºßáS^}ãÎÉ9ß9}ïï=xØ?Þ±cú¾‘:çlmüÿ§¼úy^GÌ ïgÚSh%ÝL›z¥’Ÿ:Ò¦ñ}°Ÿ‡-~êSÄ”1{Üû×ÂÓÒö ÃYFe¬#üSÅpÜ«ù­È›[œóå©;㡦P÷¤gùŒë¥;ÎFÄÄ=¤õ`û‰O˜›þkžŸUj9'Wû,»Ž¾$ÿ[Ïþ·ÅÑã¥Ï‰Ÿ‡è·öÿÎ=¥VÑrßÈ©‰Ÿ°)³zDæ—å<Ò‰çRMo‚3ì÷,…œ™{Òf)EÝùŒå”Ò½)Ñë}’Ê/FiÃ즬ñh®/‰K“< åOÁ¹¤£=ž¨ƒ/±¾ÂMÑ¿÷¿ÇfÄçÆ¼<ÖaVy„jE˜¶ýý"ý¾ŸuxlÌ¿‚*=ÜÔ» Î­³ïïl5rE|±öÖ÷Ñê†ü|ŸEïïyÊMMgÏÏ'ýJCÓ¿©Qß¡ù«¦Ùã6$ú#B† þwø‹4/440t+{¬ùgÙ;ïy!mBó̃hºAø1|ö}­á»VÎ_;ëë7W|ž‰:tøW?sžïÉ>{>ay/¼oðjçGÒ^ÆɶdM™žCì1#÷ŸÞ °6â±ÆŸÈžNÿ9ój¸˜Ø·Îü( ýÀ©E(NÊy²àpYK‰ô±j§S,Ö(´‚•Ï”As£‡¦¯Ë™˜Ú¾%·¨cªø¾ÛÞËb÷Ys~ɇ#§:WáÉf¡»|]_01–b¹µêVÙ[Çùý1kï¥>•ñ+?÷[wíùžü?¯ìüõ³ž¸ûIŸ÷ƒö¬ü¬ï÷Ï:øÔwÞwÇþ÷O=ã¹Û¯:sŸqÞ|ßmîÏÊg§x=¤š.p«±F+Z|Ô˜»ØB+E¼»Ô‡ †Uÿ÷ÿO ƒµ-ê)]ÏÔH=»ãà&êáCÓɬòeÔ„`Cê­Þ–ø—Î_r®å<g ^±þµ‰: ÷\9gÆØ+ijêÞ:ž#,}"o—ÏšðÏÔÉø¬Á{ôï¬àûªYÊw¢E³U{ …n~¹ÌÔé²ym-"­Ñuw4¼côÒT_©ÔpÕùÍ–ØÿW~-\.ŒÇJ©ÓH¹“0¾-bíeäÄ©%¶­óç²<‚=Z© LÂþÝâY°êÔ†­ÐOEÿ¡íw›×½±ÏdŽ)|3åÄ~Ôƒµ$Nì8r•˜Äð—A½Ö‡z ã"盎ý£6H}iV¯ù ï 7ôö¦{´h1”å§2¯Ðïò{ÎyÃV â-‘Ú^‹?˜ç—³y¶ç@ǪõW»mÓžÃwËyàœf[»¸kì|mëÖ„özQMÞã…ÀúÜxq­v)õ6€;ÀáÜÇÞÇi~zQB½IµÆãQ3u 8õJÜyê†ò3Ä0œJÛ7fîIG­ âè êO?Zi~ñ¢rŸÚâ˜;é'éûzèy˜¤wƒMM:bø9´ë =#Å“ž£Äÿ~)q{ÖqׂËd¾\Ø¥Ç.5““YE½OÛð:“½^3Ø'!î§CûEyF¥n1ÏVÕÌÊJŒÂ™ ÚEjîi÷Z^íÏ_#ÇÚKöÿ®ÙPÉÅUͲÀaf¦ÀQö _ít´=ßdv—ßoq|òïÑ4Ï#^§?éÌ röuåîÁ§ŽK—ø}Q¬àgîħÚg}äk~ÿà5=}Hí†ÚÐÉ{8Ža¿>kèÅÜŒkðmÐÿ¨²ûàºßÉ?¦iSü+ùÅ87=aóàÃÄûÙx¤Q{$'ßíz0kãÙœÓyßµàè·¹NÈg\­]^áþ‘Þûo¶ïêtm÷ƒþ1ÅkæŒ(ÿ –:Áw·ß·4DJáì*×Ⱦ`æ´ÉæLà2äCǃÄ×VÞ®:Æ÷µ|@½Lã ^"ê +éÅãÃáZÀ€CEÜ©µô¿WÎ7‰/5ƒ]ïÿCÇ;»’¾Ù ™‚²ÍÙS®ÚK!ßc޼üª×Äø9—†Þ(;8jfdÖz¹Ì~„¿å¼™àÌë« õGuhúåŒK¸Dî‹ý~ã8ìe@»C§ûÅûÁfÆþ[wÞý!uùA}§³xìgÔDÞMî·sÌ„%m¬Ég©uësª|@%~âλ˜ØzChWr#q{áÌ$ÇÈÎwãužêµ¦#õ5sVƒïºUà2 ­b <)tWÁÜV©é½g±~7$Gj>Mr&œ;çy:š_›X(¹’Î98Y ÃЬ>TœÕ]»ZHäy‘—yÜÅ\Bzó¯ëµ7®ùõqnÌ<°Ÿ…ÆüJ#6ë— á˜þ~–ÛÊ—e¯*¸Xà‘§ÖÈYÂ#¶ÅÎAÓ M_Ç ˆºÞ¬¾PÕHÖCjôÄYU¼è¾PX¹oUå1NÜ‘½ÔÛ£'?ìêûØÒ!êÏ•3Býû)ûâgXCøR%Ʋ÷²¿·\Aw~Œÿý&ì çO)ç|/æ‘û{ ª`õÊ!u/QG¯âۢߚ¹€zöá£R÷DÿG}“ ŽU¾Iõ×»óŸÜq“bvûµJÌ6HßΑt³õ‘°v]¼êXpàJ~î‰ËÁoFøØ^åi“ü&¼ÿ¬ë ÒÅÔwúú¯O Fuá5|àŸ~Ç-‡ößßöÈ>멇¯»çÚô}v{æÎÃW¾æó¶­×O½šëŸzÆG˜=>ùŒå¿þsìy蟃£t‡ô[¹p;e;ÙgxâðD^¥˜s„—!ß¾8“3Jók·ssönH­½Mõ¥„ý¼†f]Qm{”=ÒÓ\2ëYõ´}õ×û>£A®ú·ógÀúŸáÁÚy8qƒ´R65«Žø†½Š¸/zˆ+8Ó³fá Mkgì¸p+8ŸŽUµZgäúÍÎÌÎ':œ½AüT£iîø­v®àDôšh±–®¯I¬^Ãø^Vx®KêÈÈ·`W` hSPž¥¯0Üš3ÝÝÆÙþ£§0¿,æ»Ódïڭߦ×#;#ÓIÿ´j:Ì*ÌßìïéÝ©Ùul£¨îZVêµÔC¤8­ÊÞ‘Mh•±›ÑKìçÖÞƒ¿;×þzîšø+º–p‚È…—¨§íï–¹Ê .ÐÐê*àKYÛ|C›+ þ3t½ÔÑßúZà¯h Ðo\ùOf,°ãCôú/gñRàøê3ݖ£G£‹xž¾ú€Fb78KšŸ>ó*ÎqÿGT>l¡çX‹Ë÷0uv©CKÙ{r¯R[QÍ\gdcDWU±Ï>÷Y9>sœ«>cBcï(æ ÓO‡þ¯ÛLðMûNæ ´X¥FóSŸ“µap89àþØ]âUùúxè9¬Ü/Õ’#.@ß×[é&/œÕ˨ø1uˆúWëz3©½ÂiWm„Þ'· ÔêC®<]Ùrîïú%·<ú¬Ù™ûÿÍÙËï~Òؽð—wíþíõ¦·ýõï~âO¾r÷?þÃwm~ü«Þwðí¿»ó_ò-;|Å=.Ÿ<ÏÎó>å‹vþäm+›\ ƒ4ÌÄæžä{®ôËëׯ #Ná=3òÕkø5è[XU¦Ôó£®tzØ7Ï…[Çokb#ê¾ÿi´¾;úÕñ}ô%Cʾ1[œ7Ï#ñ¢ì]mù¥sÚ©q$ÏcSšmö»ƒ-€ÂA‰ûJæŒà|ôT)† _s»^oöKó%ü}ƒ3± þ4îjÔ7x÷YZƃ8ç->3`;}0ø» Mw'u2À¯ˆ¹° ‰X˜.ͱ«ÊY©‹°r;â–3©ÓÉgGO2ñ&½°ª©r Ýz#ýnà †»64M±wÉëyÔW¥£=DôþEsTíe<x@ü~Ìãؽï0Ñ©ã×-¢–³çw¥åñÙ²¿eSÚ¾ð‰ÁªÈÑ¢…ƒ)Ný.u%b~й\{´Ñí]íüŠKR§®íîŠó`ë@Ø¿CñKž×xõz®^aZ!ªQ8ICh›gÏ&f{·¨ùÐØWÄ C«H›ÊûWÖp[‡†ëOÂ7'Õ’Ë ç„¹ Ô“Ž®›µpIyìm=Ž´ ŽÜxFšA….ÁUâÔ‰Ú!¹ï ~ר|§UÜr4±ì|ÙÏd_FaIŽŒò³µòðûÓôï6ƒ‡¦cÄIô`€‘_뮀ßWù˜þ^L]?ܤú¹×ë4¿Õži nWT½~ka×p–’WDœ†&¶©ùvúîJ­Vû91AØI’?µ£¨5ø]\wõ Ü/êÅq7KPß±Çݪ o–Žß“qü³9ëMïZüòªX°bóà„€·Å3]©p:ÜͶBq©8¢ ~–O÷;ø³ýÇ¿ýœñÿë?¼ÿmÀ­W~y}ÿwýãöåÓŸsh~ÜfúØúί·¿Íó »ûW^ûbqýb}±·`޶¾¶v·‰ÝÕ X©?Õ¥öNKφâ³ÄÕÐ1‰óøõ¸¦Òê}ûÔÅõÒb7êÌÔ—É™‡˜ÝWª;ãú= }¨œíàç%æ û=È^gx5sÌ“\FÕµ¨×®=Qžâ±‘}>š«Ô ˸ðòj‹ßã^Áç±wì8±kÃ÷pþíÙcÆÛö¯°&òm5ê}—2'3ÿ`ŸmwE¼ýuË#£×‘:Å—v‡•GŽ­~uüˆ.dœ|Fzà­à¡·Kg20Ó°á}!˜„8[9“k~Y›Ïbç³ë•ŸŽ-‡ØÈ3†/Û± îòæªü$ûjdÿÙ¿rGÓó.ÄáÂ~ìý—ÔúfÕ¥±Yå{SƒfNÞ²÷¹«qŒ–d[£ö£>¯*lbR®‘3 Ñá¿å;ÄiðX[±ž0É‹¼û"úÁoÁ÷ú¹mþsCõ™kôPõwhç‡ÜšßE; <|*‘ôå ýa¬q*œ Åu ÅVÂ5§‰çÅJ^ ö;sÕÐ7¤~øøJ½Èöýœ_Ùl·Kfo/øÏ¤{¬»¾§ÿŸKídÈYŒëd»‹û³OÐîÖ \±íaðTç[÷Øþ*f±éžEœ¤ÚÆ$;WàUÊ^§Œò­Ì§*òeîÁ‰À鿦!8iöc!/GÇLñšæÂ'Ž£X ®pQœ,>~è¨i\Öƒ©ÕÐoÈýáÙô{£r-iÜ챓žÍÏ{`wÙ~vòkòÝ÷Ô¯,¶¥.ÑcL¿þ3OYÂG³ï‹@¯ú¯ú2úC¼fN®Fœ\:ÏÉ,Ö^‹¦õxîœîÆu€ |FWåžÌê-›uÍûÿžøŽ?Ü~ÈŸ~æá+–o<|ï‹î³ûïyßÁon?çð'Þ~x`wÐìÍ:Y÷ƒ}ñÚ4Ö¿÷a÷>Ðw%oN\.Ëéá½Ñç%\ÏÏ2¸OÔ‘ƒÿ —q}Ã¥òÅÊç\‚½­šÖ…Ÿáëè:.åGÊ ~ Mi)æ^’ Ñ/×ï5g oø÷ˆ¯-îf®úê }¨à=Ÿò]ïû¬ï[£GIGø—×àѧ\u:ŠR ^œ„¦Î ]îH¥¾ÆÜˆ›BƒÛ÷Bµ¨EÄÉWñ¥p ª°íÅp+s=ƒ—M„bXÕ­<¨Î£³Sñ£ƒznûšÍX7.PöýOp¤ÍœF½ÛDÌ;x-8rÜίïÓŸx&fcxÏqns=³@?á6=îÌ© ùþ1KqŸ?¼uppøµÔ’ˆÂÖ_í5ýꬹ7ô§´Ùè1·åwÔ\ÒWéð Ǥ«?‚º»p6ÿgÉ<3Õhè‹®ÄZCh>®{mðšÿÙÔÉ_ÒZN±nÁíÆ.bˆ÷ñÃ˺’ñ6Lq×4h. 18¿?‹‡IL=§Í~hº‹`ö ÿŽß8´;÷³¿þ3‡ÿøÿuøÖßü/'þ|ãàqüœƒ¿øà©ÃgßôLóïåÿ<õ æë-ï·µF´†'§XŽ³â´½<£CÃO÷UãÜW¼•€x?Eð _¯ø¾è±d™Å_ÔèT·.›Æy“ÆDr8á5 ë8ûMá¿Á· —&â_ÏÑ“[¬]ñ[«p&uW.%OƒÚý ˜•úðb>ÓxkÔ}ݨßÑæ‹g L"ÿ³O.ÉÙ‰ûvÖóC>[± ¶Êó34©ÅGd/˜Tt¦Êp+ó¬s"Ï‚O¬Â‡‰å‰1«òfÅ¥wâß²o™Ú!ëp”³J6¤”šÉð{'ü}à8‰ßRßÛ'¦ßŒ”5vS1K!¿íð%猫žžgÌU÷,ý{‹e²¶˜³–x¶¦?<-°{ì(¼Uð¸¹é$M•—ôéðn 9ÍŒKÕ˜g™Z9îèaR!p4Q¸›‘«2ÿä ºn6YøY%?²Ì­gr‚O[ú9Ìvõtfy^Pvj f„žÕü2æ]ž'V¯pSZ}'ç½*ÿA£&>¿q0""†'§϶õ£¶×ý/ÊQ–à3èBp6†àÏz †Ù€ä¯p)íßy?}.µéõ =@Õm´7Á ëÈ笟¢?qßþùß5êh7ÉOïöT>?ï–Îyjî°¶oÕŒ]z xü¶Q6vo¶6\0ú‘u— ø›bNÿ]xhÄzì<b84_õÞ•{NüÜùSïe”†Zž u3J37†©>ÅŒâÞ¯xüî†jÐeN}ÕÀÜUß&sÛcÏ&{j>w­¸!mÉpkÎàa^+‡¸#ç–Ñc°A¯|i¿‹ö÷Äô âʨæ˜þ±¿;Â#ËßÞó³ƒ7ø’ƒ¯ÿ•›ÿ÷ý7/Ýÿ•;/¿Ï¯ØÜ{¯õ ]¡«÷X³÷PIkJ1å>þ%jáÑ/ÛÖÖÞÏ~fþ˜~Š9ySki5= äú``+õ~û§Ôùßɺ?_m¿'NS•(ø Ý»¢9'^—B£ivkøb¼Ì‰tîý W]³WôöÎêËŠ¼òÚz‰‘;Ëžgl™Ü×~—™Ý¤Ïž^a'ÓúÙ{ô\,©u ªG §K¢A½ÿ²Ù#q¶=¾ë4ùá„Å~cˇìýŠZÅ`Ec[·->»ÌÒp_RîMNSš&ÚuX€ßñªÐ%à‰“G¨Ž©6œë8u’”øw+·G‡&yJ``kÂÊ ~Ýfr\“XéŽ;4¥ks¾6+~Wöq ‡™^ÜÓõ‹©vSÌ©×ϲÚSŽr1µÚìì0ï8P{êwP=R\ŸfÑ\ÃÞGýe–eåÙ¿«Öíþ9p•à4cSW›ËÛ3çÏí.âýG äܾµüJ‰Øèî‚sà‡‚sþˆ³ã3~}×>ÿ nyÈîì~ùmÏÛý›GœÛýùSÿáðÎ'þÂî_üÈÓv„%nÿÈÞí;/þñ›vþÓñ_o?ãÚ‡üú·}íŽéÚçX¼iZ†§Ùbò÷îþâZÚdEù^AcFøD??‰þêÚó©-ǾCs?à‘çÆyh< ù¤µî…ïµQj6ø)ðoùTñ$öÀˆ ø)þ¿„>çC:Ÿídf¡ÝxKøbêÍÊy•³_kã{÷‰;bÞfÌŠ¤ˆ#¯zû-ô¿îƒïÚÙ3ÿ¢Ú'ø"†Qy§¿Ö9gh ™±É1çþ6ìè‚|vÓ5cÞ'5é{èÿ"'ôúÄ2¸øÑ—/_MN˜½ôWãèåÇk=©åN-ï8Om|ì¸[~wØô½dó|í­6bß«ù˜‡àSÂÝâ¶.xÄ1›KÜÖ‘ØRq?gqG/çÝÉÆ:þ¦Ü ¬º®¤uÇ}—¤ÂI%®7`jyièðÀ?ÓLCú†Â]ÇVØãlxl ŸIŒ‰®IÎX4¿€:"9 u–AšôY’‡ÃƒÄsPL 3zØÔ ZîÕ¼äÊp"X_iŠ“özG²›Wé…ãY…©ÔÀJ"ÏÓ÷Ti®ÈÇžG\.¹Á]<ëq¹ì ]q*&ùSß[a pÖŠúC9ªeGÿ–pÈB†X'öA5ŸQ9KæSvþÁ´Éûá[)çñù*ÊŘ-^Wê)wz‰}ë"_ˆœïæ%q¸òÔ¼£àÓÊ;ˆGˆ Ü_ýŽÇéâ”ÔMi½JÉ|ôR1cåLça[ðÿäÚĦÜ#òMå##gNëCm>ã(âøäl·§Ö`ôZÆY¾HN;ê{Šrª/ÚÀFÒc˜ýBðÀ3ùýkÚóhgF<Å=ÅÆ‹×Èÿ¯ìÇÇ=´F uóáÆ³žqxû÷:ügŸó;ÖŸûæ}Çá£ð‘ö_~Ÿ7¯¯¼ö¼þòÖ7±4þ¿ü²G§i¡-ü«Ù_ú¨‚#µ•w\=^Eyuâ_›êW ?}™ð«ÀÁ>‡Ô`HíŒ[io‚¯âhâï9Op†ä"‡–5b‚•ú U#`Îä$Ÿ“½ÂÕxt¯e0«E=^£#§Oz+7g6965,{>êkÜM廾?ö|ÜSâùþ‘³ÂyTÿ¥Vs:í™› ¿LÏ ÝÅNu?–Ül]É&©š›û„VŒX»-^5ÅÒÙ:øX‰›Â[ l-ãß \ÍŠÏ8BuÖ.§ºxÝgÓ >yÀ ÌJÁ'Ø;Ý#ÿLðphô…Xëv?Ï“ƒø;ˆÏ »¼Vƒ¼­†~ôp‰…ö²ÀÛæýVÙ¿~Õï÷C6›ùléïÀÀ;ÞPÎÕRmaÑæh¥öz¾¶^6t$çoõ¸àìÉ.jNû9ö0ëù}¯ÿ =¶A½µ`|àœÍø»˜ë‚¦7ö ,Uûgϱîòˆ}b4ôà‰™;ž£s˜™ù¥ZHæcCèh¬×=´˜ád…ÿ¸Rƒwbjº+~^¹?-–mœ‰áÖ æ¯¯Á5eã;?Îçó|Â£àæŒÔûT+p[Ü—À½¤Á¤Yô¡Ç¦üc_û‰ßßT“ ½™¦)¢5šàŠ)o¥öAOý„ÄuzÚ¿»é²}ÿ£¾ö‡Ÿö›ºüë?ó”ÃǼðæó×ßð3?xø’úŽ{Ö“|þàöoþ½ã€s~ÇäÜ@°(áç=ýo¸™ûÁ5޶ªyžµ¦­ÌålݤC®¯;¸5ú9ð#ÀÍÀðÃ'žõþ´˜[|ú¢æÓgU°vbNêÏØüàEO9>xÞŠ»ýsà« ñ‘üœâà»­¿&bz è?ßš›Ž@¾ƒ°Ì5¾üÍnÅp2~Ç¡ˆG¶nö1òÅ#:mªAg}5j€çÇ]¬‘ƒµÙy«¦Ë3a¯ÀPÐ}W mÒ£Y4†ÎÕÍ̳׺ï¹WUšƒÄvùÂ&ðÕ*ø>~±bž*‰>È}áû¾§p T3Ïy²¾~·K“€Üž1¤ââGQN< |˜q?S»;H«dý]¥ã ¯‰©…#(W¹BÜK­—ø¼DmÙχê§ç‰­üIÓü¡‚CËn$š3aß¹9úÐæ8ajã:Äìò'øDØ0å—뎻ík§œa\©€s60³RCCøffˆ»iùdÌl’MrŒÔÎÔªéO¸½¤WfÈ9ÔøÐÙ×9³Þ¹bâŒÉ °oŠÝf«î±O=Ï|\žAz$]ÌÊ,ØœH¼B.Hm`SzÃuõ1øyѧ€Æ,ø¤ò Å"ÉA‡¦?\áëôëÑëÑt\»}ú§ázƒyj¶lYIŸw–v 1´z\ýçÑ÷rͬ±Âa<ã³æüœ,©= ¿BÇÒÿ&øR~ï*óðçshz/©Y(F[Ã[µ÷Ì-ëñU¸”ŸIî‘0‘Q6Þ0šQÂRéÅeNWöNø ÕOàÂ×ÇùsrˆÄŸƒ£“úƒ™+wôýÿç÷ö}Ω׿rûÏéÝÛ|Å=¶mí^ºøéå^þ™Û†yJ¿ayò;KjDÊ hL;Û÷â‡áUKf„+mõ&¦\ø¨pÑÁHÕÃ7Ñaï¥Z~m5æ˜K¡uôø Œa3æQ[Üo=3Ñ3n\z tÖu^·47ôê*¼ŒMqoÄ FÃ,ƒ´ML»þ8ØM™ùîÓ-曥)'Ni LÉ1Sø÷Àéüwο+–U¹‡‘\O¨Ü¡£˜—\áÐFNZ}Òù+­º{ÄGä‚äû}|ž¢|:ó+žSçߟ“þá‹cWϤo~R\¶÷6ËÖÂY(†ô8ÒÖ^Ü¡ërßA|ߎÓ_‰ä#º~Ð úµÈ£ 7LÍæœ]«wÔ³D,¦©8XÖˆáøÑ÷Iž§ˆu×3T°ýYº™Ò€ÌØÄ> -ؘ±î3p<ÎßGKV>­ŠcÐë¼7ÝÍŠNö„ç'ö4_J~¹Ô¥ä})&étB#®¿0‡äà¯.Ê#U÷EóíX5óX´òà“(Æšœ¹9sxqí6›fq¬ró‰ ñLò“Ô_ÜŽEmótÆA†‰Ùï«öUëUú î|È“=§Žùìè~îMÁK»-u¡éç…KE]žX¹Ý‚¼[ó$ˆ%Øêªèç1Áþ2Î!µylŒîw…w ï/šqíç…=³ýA_½ŸeF\ç.âa¸ pà"Ùšk¾Câ"­wìÜñJM»¡ú+ÚOyg„?Œäà¶.ý³ìÚ÷|Âô¸Ýçý¿¹û§{·^¾å=zø}÷©Û_ùìËô¹/Ù±ß=Éûw¾ò1/ܱú@hþ—/~âO>ÓsÓA:Ùsë Xïiå=ôo^ÚlÕAZ£ôÛÌYÛß#H-ÖVcÙC7zŒZÓ%°pª"[¨ÿ’œÑÜ,û‡Ù°ÌSÄö ¿-·º(¿Ù—½˜BÇ/õ¯ÔÄðþ&jª²;ž›ˆoP›&é†z³{‹YgSW ýÝÍìëÝ(ê;õÏ"Ø”Ö59C¼×“–Ä̳ú÷õ,#¸­txà§p'•_Ź'~™£ÿ5yߺ· |¤tm–²k“p[ò3ÇΤEí~›ÚÖ :îÓÿŒ­Õ÷Wí3æð5½x¸ñˆ?ß~î×}ÃöÃÿËïü—î¼ù“?Ã|ýxâÇ×öïÆçS@9û«³=ˆ dkh÷¬a%ÄhSFÜ-Ÿ—6<8z9ƒ¬ã¢žç õ(Ñ31R'˜5jσđf&*K^îí(¼V}¹€Õ‘ó‰Ç5­š6OjÏ ¿%ÆJ­ZÙ²ÚãF«§ç<½IŸ 64i/Ó&ªö5)–˜¨ ïÀ9cf(6îSÆÁ·‹ÚvŸSƒßÒ×"Þ;õFåÒøT­÷¥ŒYÄ«ô¿–“ëNÌÑâ54?‰ÁdzVV}‡fB˜OæÎ¢«<ÉãIómÂ×òôË_È¿“$O³} vµw•ÿUþ°Çì·Ñf_ ÷èp‡kü¾Ýùµò´%õ7âªY:o]Ž_«Ê)™н“OºúÈ’^ÍAýóòåŠSoH|¶á×r6Ï,þAz‚òZß ÔW&j5zæÑf4)®eNcAûÎ\ ž\‚Xdè¸làkÄÜp2¨ïŸÐߢ¸}ëzͶZ>'q_ò›!5s“—›è,þ4ë6¹’¶u4qøKÔð=Ι"F N*3ÏUYêyƒ#Kî¤\¤Ìâìˆ -ª-úsFb^x4Ø\tÒo|•îÛ>ºÿŠY¦A|p¡—¼§îÚøð?øÔÝû¾ÿ»ÿú^qxû_pxñ±÷ÚyÇóßvø+¿÷u;ö=ßþ÷—·¿ö_·sïÇ~ÚÁ¿zÔÇ?`ù„÷}ÏÁ_qçïßz‚#½åûñgÌ3&[IoEö< 1#u ¶Òid¢7ÉÌÀ¥r§2$‡.ì&¸—Î[jmaÿ‡¨oûúÉþUúÍÁ•àÅ0ó™óLÝAkFçÔùдÕäÓG©/¸…Cã6µ+ 7SMË}AÄ ¡W£8iÍ|oú9¹ƒ`Wô-«¶èk­ž¯}êYë >Hq}‹|Š×>ÍÿÁkïók{[ùä‚­¦>­ùy®ìãiò¾1ÖUçòÙÏ™S&1íupäìâÅ*«œµ×æ‡ÎªÏ5f`]²ëkxºäzøWù…ä?ÙUÁUlJð[úïò~Á¾B¾;„›LO öy޾­ÌQ6ÕOEþO¼GÎÚê{àK¥Ã‰Šú‘òL’/*~ãïB‹_8J >¥ÇãÔ܈FÅ?þ¼7…®-yÙ¾|žÇðüÐ\@ÇhPPñ’ã\ªû¯µf0M¸×ƒ´RT cßSwn%mÃáÖ6ë@}ë`ÿÄg ŸÚñ0=ÿ“û«8'ùñöœ¦c ö¢ó0R#²gýôÓ_èú?¿ýô‡NyÚá?¿ÿ°}rv¶­¶úàÄþÝóÿg>ùŸ<ÿ¹÷WÑ×Q~q.ñ=¸ÃÌV1ÿ‚F¼ð°#pPïä}ÈYm]ÍçXLN?w‡½DÏ›~›!{lƒÇa?ÙY9·“30©(ÿ˜xëØæL9.çÏßùõ‘>—¨-<§j}³ö';1ᛂxݬ´•šRÃG_+ÜAðð04á’£4ÖòÕ¾·Ýœ îdò„IQ?giÌà «™¥I)´VÝÏŸWuLz”'¸‰Ô.ÉÍôûU~2ëêWy’mIµðJÌwñ(õó®ÓCO æG’7×ë"º·¾øMõÖYýEM‡õ¸ËKÎs˜ƒ‚ONí"zœ5g‹=/pE…çºv¨úÙdk½Nˆ½ó¿çýé; ^ùaô߃l¦}pñÐæW—¸8ïÛJºkîàk!m|ñ‰®b‹*}mƒ8±GÒÑ&'ö^çNs 6‹sÐꦡ¶{whû¡^˜ÎUìp%f´gD§ï&é¼ {ËøÛ¯ûá÷)z6.÷Žƒð–à÷yoãºÇ;a÷Ä¥›¡ý”ún=F.ŒpN687g8ÎñÙ5q½|ͨܣ*nÄ¿$·àŽ˜ß Xƒ¿¨÷ÃÏ‹ð`êî£>k_÷Ûm[‹)CÇ—ü586Ormý·…°‘BOÝÉg¬é9!Ÿ–SZ-÷XñK`ýðó°WŠí'zEû¸•:+}÷ªKKùÝ3ûÉâ=ˆwÔg¶Fó¸yÎÊM®«ë}Y~†À2;›’ñ>qlÀçÞ‚0Ì=7tû‚;æõÕWήá5ÎÒù±ïžÇ…ÒüÞã©Í¨ñþ†®‚ šU#†ó6_•Ë÷W½‡™TØ-çmÁïù8wÖoû½».ÛúîýÖ'^~À/¼æ²áj'ûø'oû_>ûïiŸñ«Ô_ü±7¼ð¿ðОÿkßû’µîC5¾`à w–'¾ãÑ[´ç_ãcÅU©âÊ]ï‰ðF´t·¡ßT9SØ?q•…iS“0z±¾ßnG6½¿àNt8sFUÌí»ZÁfìY¨!¬Ä]³œDØ49Ná<Q÷ðg¥†1¨çî®äêÏkç§:åZ6²ðýà½öî|Å÷ûÝToò™kªm»¦³Û|ëmI-dd®aÄħ–Áeo1¸–0ÑŸ÷²÷© Nl6>H[ÍÎTÇׯ.£é3Q›#FÄckYSÙ ´¬ÀçFzmßC“8ê*pÌìçô×(Ö+kú™K1D½¥Òò¿åw«p’¾ì"?OpÎÑÐjï¼Ë8¨ç¿áM ñCàŸè˜ÅÏém’m˵æÀƒqŠG1‚%P¢‡Ûbë)}ÚQ\@q+GW¯C¸½ÓŸÜTOƒ°ͰgsðBª4l§È+áiìiþ̽ÚþÎÔ‡u¾ˆ#2·~•µ4õ:må³îE/ˆŸ9ûßÒ‡£>7ÑŸB F\ø‰˜Kv½bg¨GÌc‘ú¤ð²·ÀÃós ŸM&ò^Ýyn[›˜[y11&ìÕ >ëÐÖÞçeå¬ üŽ>£^u1ý–ì`öñ4ßr^9ãEò‘ä Óû…ÿ#–¤©¦»¸h3›.ÄþÜî°Î2>~yÿÍôÈM—žb_Gü|ûÿÑ—*|\I7 dvnë[+Ÿ2{ ÏhnºnÿWMç]"­àoÆžWœ¾Qg”ºëBX \k½¿Ïïï*ºûh<í? EoÚp+ó.TæÑ{‡v1|€ÎÇ»/µ{JÿŸ°ÏÄj‚·sjÉ™”íLJ,À8»ÔŽ\ãd«RT®YîûþÏݱýºyüÄVß¾c÷Öfž<ãqË®ÜÓúÆ+¯}±ý·mméWFÝC` [×Õc©W¯Ô›¥;ùÀØê¬¡F8_§i~\zRlëïÇ .¬êË`T¬VþÆu¤¥k_‰“øL=oæ «ÔyßÐy;_°¯sjý‡/w»(g˜À?Õ£0 ë)-n ˜‡âÒœi)üC1ǵ4PèËM»"]¶ìÿiüåè…&í¼Wbœ!gŒï-´þêɾR†Ä0/Á¥¹Ú›Ôúãý…û¹ÍÕ[²¶â;,ÐJ´™ù®+é[5ûºÜT?¥°Úäƒvš•304ý|íD.A¾)- bhåIêÄŽälå of§J¹ ý‰t±ø:LÙ¥x_öëÔ²Ã_³_ZRpDró>±ù85ýh 3èï+s«ïçþ¯Ô¿&Íy´ý¿=çåDϘ=4Q“LÛXðݪ¿ø™#vå½W©£}ì.ûq˜Àï©7 ©ÁpºÀ¢ÆÜðœç¦=­Ã¶o©×ì÷Nv81¡A¸‰ìVm<ãKÂs‚kçRáR¾sÂo·³ÉSâÿ/;eÿƒêë¾GhpðÌÂ.*3ƒTïÍZ~xiH ¡óèeM=|RhWÀC=½·¥ÕN-ƒ[wjIU‹\oÅϵîäDþ¹ê4°ÌD/úv”ú¾ÛIÍNàÌÃÍμ›|pþÔ |Xuð2‹' ˜w$‘M^ÂóV®—yƒíWð³Ïãê >Žb,úîá"Tâ¢ÆÏû­ûéë2GïåZxß¾ŸEž!|m©ýà{&áä …:¡ì=+/ÀÁ¢ïÀã+4Òˆ'ºœ’óßqßã\ÙçÛ{àóTÿ\¾ñqݵÏyɽöw.üò—íþÌ‹¾êòSþæ;/ÿÈçßk÷ŸñÊË?÷úܱõ1=à“ÏÛ¹ó!u0þð½_ºøéí}Ég@Ãò{; Æÿ·g5!ù9.˜²|©âýÓ¼ïZyÔ¾jI³*^šÐ"—޼3øÖ¶‰þ›j¾` þwÄN=¿ÿˆOQ¼½”mrŸE_÷ŽºŽê6ª+Do6¾ »ÖÅ)̧¨ÊAé3¥N渭+ój±›ÒZÃޫצ*¯,zæ5±´í³Ö ]˜}pÊ98vø_8J…»1›ÇªcnP7¦†æ~¦Óù†ãP¥ÁPÁS¨©Ö¼f»ëôÚ}“&ޝÝT-˜Ð*û}rfK…[![±d-éõ<ÓæHTáyþwÒÀ¦1]þ>júCÇC‹¹¾>g¾:} `Šu°åc¿ÎÔÐuöeçƒL/}Þ`å¶nôÀ*v©-†ÝЬrú ÎÎ1YøŸ7eoUã´7xøå¡û}®‚‘(þqŽŸø7~¦ˆcé7ÚþFo¯ñuðMN-™K nKÎ`6Ž©0!ø‰¾ÎÝÜÿo`¥ÔDé]%ÿÄõwÖô+k½ö•·úþóß…-¡ÁPé+W7ëí¦‘&íƒ%ýÃôôœî7áÿ â} "¾­à›ÑWS6SgéRâ§ävÊРé—=¨³êõ ÃH ªuww¨›Lm.fžª˜;瀑÷ŸÝùàgh/û?ĉá'+¼ôA¼sêHƒz‚íïÉùµ®•úÈÍ¿"±ç"®U2mJ_ØÖ™Ï¾Ý5oÎe~Öq!½©¸¼€}RŸZ5ÍÜFdçW=t#\ÚKðuÌEç0‘“Uå“ìycÖûà%(†.¸EÝŽ~Šäl€Q7Cn9w[õÞ 6KÏÒpkj`ï«6VÞ´s÷­Ñ»ïóžòêèù´ýÙßö7Û?÷úg<ø'¿gç~e_\´­µùzëç»=´!Ý¿ë}¦¹õžM+͆ ¾ÈxEv½_€i5^SjU“ÿî¦Ðâr4™±mе*º³tWê3bÎ>E÷ {| ù$ö|¢Ïƒ«i-àž°® ~ÖòË¥z &Å«X{Ø«k9™Úù!tÀBÇ俥b»Œç´^E¶s‰]ÄwžŸê~Þì?·}2œ*zÄSì÷Írâ‹V1Ó‘8Ü×íi¸¡ùë±³°?óâ)g]’|TñŒi¨1‹Æ 00Í [µ’ÖAÔ.f}˜WkŸš{ò?‹‰–Ê™‰Õƒá¾ {B ÖßM¹Üiá…Žóó|Ev¼Ž¼´Â(ÛÚìâFìŽföà{ :Ç›M»=575¿ÍcÙrrÒÚ¸@ç8·©EB.$Œ¾(Ž‘FEÄŒª‚±ùÏE‰‹SWD½Éyw|ž*úêªÔÛ›žð¾£¼l z2 R½˜º÷qlZ8[ääª÷Çü¿ñ£‡¿Ž-|xkM¼Jrh<Í…4×—à àÌ+¤Æ6$ÿ:k4É1å™e'&úEâ \aí°1ûähö·Á»¥O)Î!ãMê”Âö‰wtoØ?fgŠ£èß³çFúŠÁŸTÌS¿ý¿éÿOßþ÷—wþÛ«»û¥{ÿîÀÖÀ´üŸùä{ð{vïûNËÿï³qãŽæQz}êÔë_¹¶{v['ãÂÙZS[¥wK½tý–pPØo³·äþ³´Üè=¿‰~…‘»M,³™ý6{|îþ,ž¬r¼QqdW‡ò\£r?ƒ+3jæ—1¯2r-zåÇý»éKáÃÛá®êëÀ—€=°öÜ;ûßÂÁ¸wÕ»Lè«¢Q€i‹=6à‚«?äJââçŒpþŧ+piÄ›ÙËþ°.6¾îÞ·Ÿ¬{SÈgÖyDCCØÉ^ö3ö9õ¢€óJ;Š|¤^uÖ{Hn’üÐñ„¢ï¬j›©1^ˆ×‡è}MM7rbâYºó¼'œ@ê>`ž`.ä Š'è3“g*¦§ÇgÃû(ð¶¨}ƒµ9wþg_¹à’{!œ?Qר'f <ÁÞÓüî>µö¾r‡m_ˆKÑd >4Ñ‘32ˆ“ÿvSºà<:Ûð-TS¸„½süCöbŸžÝ=åû߯ÃOF]Œº6QÜ@ê"=hàV:+ì%ØGéüc‰éâ‡Ôx›–½hà¼ø—3Ò½Ô«O-K~I>êÛŒ]Ø' N3¹{£~¦œ¢Â߯ÇGÓï%í0üLò^ÄMqŒ Þ¢Þ›Ø,ûío,?P}ŽÁ¨ÜkR®ºD7@û\”/ù9Qþ@½öNÎ!’Vá"få^Ìü\ºUE>KºCÑסgKîãརWS/hÎ*8 µ¨•ô”cim÷ ZéhÈQæ^€Ùöøœ2õñgoKØÔsÙ»}¤^L«¬{næì<Æ¥æ¥Ïq$î°˜ ®wiÕñ7ÁÛæè¬øvêŽÊ•S£¢ÅçØðQy•ÛÏñ‹o=ðõü¯œüüÌ…î¯ú2ׂ·þÿ£Ðþ/ÄÔ*°_ƒfåÀ¡ïãQò¤AúK«œƒzÜwH¯zÕô&×èØç“KöÜWáY!k³ðC#Û º>X8'}ø3i“€Á©{L4løÎ nˆö©À ߌù;w»‡Ô¾_¶-¹ öÜö ïœZ¢e®¸G½ûF/ûØÜ¢]ÕÝEÞÁ1Áè1LÍ;j¨éÛàU´: s‰Cú.yg˜³hÏBÎJ3bì¹ÐYµõ`æŽÖßömM­to¼]¯Aõ‹3¡=R‡¦©¸PÏ]C {Xt–²E©9­g¢þ¸„jm#y-û/.XÁ?êY²>©ÚTѹo¼ÀÚ/8+õ\ÁÕ€;$Ÿ?¡‰ ¿ Nñ#{£|É?Ÿ:)ññ¤¸ÇŽÀ*ûïÌ:¤pß´ýâó¯±ûÚﺿJºU¼¢"ÜÙŸÃ~®¼Ë{§Ä½˜¨})ðý±³Þt¤B¯WÜ?Å¢ý<¡ánß‹žvW7t˜jœÜ?p9¸ý j Šûàz޳z£±%³j´ª])ßʺ°x†•g ­ƒ‹™?ÓWcßm|Aa9à(Uµv·{Æ…‰û°E¼_[c#B?*î›b«)bËà4ŽÜ|hpkÑjEó ;8$÷?cdÿ^t¡—P­Ô{­`35´.ÑRÉñböÜF‡;óþlhnà3ˆAÀõ„ý€5P[(o«Â‹»¾ò˜·BýÌ`SóìùÑ.ÔóÀ¸;~×Ò;ÇÙŸ‡}‘îw…ï 3·/¼ãígü6Ó·ž«¸/pÅõY錃ê‹Ìz ]«ÕÖ–z3¹NÔë"ï·¼lTø™ç à:âzÕ}êYþ?ÞøÁí|ð«vþ›ïµ{ÿG½âòÃ^ò-»¯»çr罫/1~`}Ý=×Ë­—½ûäÿo?þíç/Üú‘;Æü»ûý±ï±ÝƒÞýǾö½/YZlog.;H+ËöžڮðŸQg’XgÂσÇ(†WÎ‰ÎØ5îÙ~WS¨ÄûQ¿3qc}Ï~ÄI[©MGÍW9±ÇûÒ/‘ÚÈÙ¡/}•ܲÊ>¡Ç Ç*íÝC[íƒïk—Û»¦j`P[ÙïBM.ü¿Û‡5œúù†ÀiÝ^FÜÕX ¾QÏ2Ù=…ÿº©Y…ºón³í×zÅå[ÊJúËìÑ,>©°¨J<¥û“qÝyf”ÇÁF­Ä>‡½»qÚ²z¶ÔŸÅæd nâ ¬ìûÜ׺mmÑöRkk£Â1ÔÍàÁ¨½€Av1tåNpF±ÔêRÂû8Ù ¯ƒÚ1õ@qj<óµŒMáÎâ^ÏÒµiÜ„ÀïéARŒ“ÏxGÌá¬ÌMXe/Õ5òþQ˜=¸+y|~ž8É °Ð[{é? œï9Ä3ûŠ Ý^Ø9éÖÔ׎15á#õ‘3K lŸ<¾˜j~ÔGˆÓÉ ÐÂß7®5üü=ñ¹}f§%œüNan£4 ÿ”õ.·.ÔŽW®ßxŠ^ÄIg;ÿ›¸ÌyÇàŒ©ÔaàáïÑG‘_«Â1iµ ?“®ç[çÊϨx"ª©Eýs³érìSŸ£®±Ö…¾PæÏ*¶º ³¥Áååã+ØÈQhsŸFõ÷ð¿'qµ©,ð ²¾æác«£Ž¦:’Ÿk»ßô¤(¯WNz%Š?“w&lJßu«¾.!Ž Zââk¢ƒVRàà‡Á';E¶‰ûëöLù­ï 1ªz›““+,²övQý‡SëãÞÂ^-?JMB8Ev¼üà#vhïõô³÷?üëg=ñðw~ÿE÷~ì§íüà¼ð𥋇nßó¯] k³z¿úή,|ãæA«}…v:_ÂêÁc„wnuvò|ΉÕÙŸ¥Û¢g#O]p—Ú™ÍOïÏU­^ùÇ¥<'ô7ÏâRß ¹¡\%fsßw½dÏüM‘ Åÿǧrá.(nðÚ›ð!ž&hŸi1ñü ~ ý¢«]…û¦º/xJpÖVê[n3ЃÇæE B³¤„½Âûß ?Œ}Ä— ÂkÐv½I34¥µºr†yôYPCA¹ÊHî7¨÷”<°Íº9ƒ0à6'uƒºÍZš›wÔ4{\œØD]1r”³‰lºŽWh:kÚ½FŸÆÞÛÖBõÿçH}Ü«ÐóóÏìz³üL wwü4¼åœU:DM<õ7ÕKIn%ÌY³öòl‚õˆ‹˜¸;øpÓ¼C;jϳ40c}Þ_©PÓ¥¾ Ï/…³†Vgbù³j‰]^†ßqß, #b«JŽ8ï4.0= ÔÐ7›ÅcWîv%»ŠŽ5Ò{ÂkX¯+|î*û0®õùÐ>*ôö”÷g®G®‹†°í?Ÿ;‡Ægæoƒ8õ²ïy&øÿÔ!WªÏËWøÏ¯lœñeÜw¯:Î'Ág²ék‘\eñ‰¨¿àû’gªú< b5ôó§" žGÚx@`·øî±MÇCg5wP/õy¸½Ú“ÂlqÖŒž¬YšVä½O×ó-õL¬šfµç™öÌèÝЪ Füäÿyî¬ÕöoY~ÞåÿîóvïxÎ¥Ý/ù‹Ï{Óæ½wv?ï£ïÜ}à+î±k¶æ©¿÷çßø„›v~çÉOÞyûÿp8=æ·ß×íÜøý6pßj@ûàϯ?â=ï[¿å£¾Óû¿ã?¹¶¾ÁŽß\¤å¸FûÈzƒÂ3šÅ¡¤EÆÙ2Ín»·mÖñp½˜5cçD9X`öRsëpËʾ„m¼þ\q©Áe"iêæGéÓ³Ö8§Á!@‚3ako½)Ø‹áæÔž é÷ªÏ%æ¯QëWø8õŸð+Š9¦þŽ¡ñÑõÉ—¹MuüÌZçD±ÎEêh“jò#¡i'[8[ôM½¦Î¶/¸FŽçì]ŠÛ7«‚Ï&Æ'–íìªìVÔ¾i–m^7'†ÙumÉóÔ;-®môÖa¯ñÑÔ­Zyšjκ Ç ÷øšóÙ8YÌ=;ͼܑü>ÂJ¼Šöù6ßål+¨Ör…¸«ÇÐ4Ûæ‚87ŽÇgÝhÓ{n«ê½t¿³–É=cmh+:ÆEü¡xÖ¹•úá¦f´œ-t.å ïÜêeá“í=Ðä>o+îOn,wqSœ]ù®*Θœÿ<ÕTCuì*±gêÛâ¦a[èAPÝý¶ºÆšœ[ƒ›ÎÉã½’v4pŒ=ù§‹Ô"}½m½,?žË9¸¥~š>å:z¢Ýs(Ä=¢—žŽ3s7*3&tN‹l`Ö˜ˆ½µp ©óWt†àÆU0çÐ:¸­ÓI þ¨­¿êÔYÇU ÎÏ‚å'ØêÈÙ.wæËwWtæÔ\ Û!»1“#(V¤†‘Ø­®Óf~F y´Â÷„¿ŸZÓÛŸ¿}ކ1€aÈÏz,§xÉñÓMõ«)Fƒ X°{sÓý¨Ê×¥!ìu€J F nt Ü5,&ò íQöòaÐsD,,{°žU“êú'z,^ù-Ÿ~hïøŸÎ~üáËn9hœ¾øìwoÿ»×>àð$vß¾ëoÿ÷ZñàúUgîã½v– Ó£f¶IOø¦ký¼¥t:¹gä°GÑ—›{ÓìxhNè~ç\áÓÐõv¶\lCk½—3½B$ôÙàuà÷‰¤aÇÎÆžèž;v6Cª\Íóuݼv÷5—oneöÝùì-Sþ",Ác¸µð\âÄ}ŘnïÌSÿS¾áçXš.û1›7øêÔüÄý+p”à?'ÜsÍÁïkד—Úã õ˜%ö¾‹Þ‹è+>’î§îL6¼€¸šÏÐ]C/YÐ¥aѳÊY‹%½ÔìÚ£+i,ÙùÁ+è|ªv´µŽÜæ4¶»¨[àXÏÜuî\ˆÕ^°îg±ØºJ·×ï·=ÕCÀ‘¥çSgß÷K±!³¯=•ÀŽï´ËÏÇÎÛ3ÐÑñ_=Æã™„ÏÃ5ô³'œýš¶ÇÒÛãSÜé}â286kÙö»Qã½Ê3ô˜4¹ û”¹,ù´0‰´ŸÊOün~Äò?Ú½þsö·üàcOþÿá™?ÿˆÝ¯Üýíƒ;>êi‡Ï¸ö¬lpýß^àåŸyð¢þÙí“ÏZ[ mù9ÛÏå \ÝÛA3°B{ýÒ( aj#6,öLh[Æ\¥}ÕϰwÏ -ªwÕ÷£E_±¥Ê_Ó^›ß ÿÎl‡Tú›ÔÓæwˆ³ÓéÚ:v æ‰Ý |žœ.ÃØâÝÐðmzT^KtŸÚö)1ܪZ¡·¾oÄCWÑÀ!w÷3@Ï–òî_åžÚûƒ/+gp_§™¤žCG;ëGûø0³3ö<öó.ÉÍo—ÎdgC‹ì¬ûò޳¿@@ö´p^Ñ… Ý>zñ8»¡§ƒ°Ÿ«×¼ª¬ -²Yµñ7˜Ë°TŽ;!ñ,ð ÅG)h®Ô/yúý•¸¼3°‘˜¡¼)­-Åÿô{íCý»ê¼¶3éŒù›Œûìû4’ÚÊuü‰àŸZó.òSgÛÉ F͆\ƒËÌšÇbu!iÒ)Fks¦À%ÏD¯®ï?vH>Üw„KÛÅ@£¾‡yrU1)çäžR7£})Ÿ·ˆàÍ×=T´Œ65ÆÜN™ü3fFâáVôyÔ™Y¨Váër·æÄèé3=7_SÏD¯€X4ˆ›-s¸ì™Ì¶ƒ_ËfÝÏþ›ìæ$Laç%ã-åœ#X:|õ½&oÆþæïî÷õiCChÌÒszõè)Š~8êáð߇Ôö>·¤ÿc–¾ÍQÓ¢p^â[5«‹üK¹R•o^â[WâÄ«b³ºÞöªó<’K÷µ_l*ùïQÎŽ íÊ!û®j'àŽð‡”{.>z( 硈»#¬æ*>wGÞí@ç—“gðTÍŒŽØ#Î?õp•¶ÖñìäÛšKZ¹Š¹áïæ÷êî•¡ÕOÀ¸RÇéŸã£Îš=ùøÇݵû’W}ÞÙß—»[/ûÎO|Æmo½ú+vÿõ—þ“óÿÎ}Ú·îØ¿¿â=°ýw÷ûcï¿ëoÿ÷öO=ã¹Þ?`±Á«?éqa›BÙ|ƒÖÈùtŸð+û®×ßÕ~ÏøƒâŒÿ$Ž$=Wõ…."öEñCv>µœ"bìšúÐG-ÄÂhmÑöÓžÓþ¡~MÜÞÙâ™è¹Ó>sfÖ;-àñÎ1‡"1w4IÀ}Ív‰åXfÇÛ5C|MþºߌšbÄæOY7~YØô®&ßéŒ^ÀþŽô Ò ×ûU¸ÖÒøp~1Ø×Ðj“•º 5³'³j¥äYQ‡:–ß»Åk”âB%þŠ/ñ5wxÆIèvGÝþÍhSìTTkõs&}ï5¸‚°æâYoóÜæ¢½ îœrU4ácãÅ׺3ãyí |ô…Á5ŒXXüyj€é«géÙijÝVá=C€ öÃ:S{¥v"ŒlMƒlÚjÄ|¾6¶s¥ÙõÊ›ÖÌ—œ¥MÒrý½Š–-ØAøaç|Wz½ÐãV¼kßí}Àz—ªúr¥®&A>öêtÎscïû!ð½Ä/àÊä8à`‘ôC“»Îâ9ÃÎÉ¿Tá·Ia$6Ñg¤èöígf\Ð'³P±Pî>¶ï™öÊ9³îkëkû'{€6yö¤|¿ÎâÒ'&{èöSÏ2vµ……zŠà×LÂ4|¢ï´üy¼ 1žâ0Lêj‹.O¢N’ZYÊá’ÿ××Í”£ntó¾Àp+:ÇaËÄëðXŸººâjaç§á%¸M’Þsâ+ƒxä ²#XôÐi j¯²§@y˜b‘ó9SWçÑ× E°ÜÆSñù$KòqH§wQ]lÜ95´>cË×ësyp5êä«Â³ ØyË lOv¯Ä] _ùÇ?þã‡vßõ½û‡ßúC7^6þ¾ÏúëY9¸øâ3‡O|Ç×X_àø†w}óÁƒ¾äç,Ʊ½³óeÜ@ÅÑ}ÌÄ¿ðéfq,…£'–¤:k‘-LÒÅÆïawÈeíV½)ÄÎÒHº*n¾sªjDnUo-–ԓÐcÑï`_—ªÍbS¯ã\©ïh 7ì(ç=FmI ñ¶ŸEqŽÓÏ™ ßx>quj3ÚgÙˆó¹ñª­ü*6vÔgqàUXØÌ¨~T?Åÿ™ûéËG wN-¢Æxâ4=WàRc³5{Ù‡L¼%®DbSØ?á¨;«X~?êà¡)nwçCž ‡`$.¡28o+î±üyèþ‘t±ÀÁ9?=^«’G‰C8tëçܨø×è§OˆxœÐžÕêüöù¦÷÷ÎßúèÃÏ¿óÓßrß_Üù…§Û¯Vÿ7?a\¾“ï3>ßú#ÞóÛñÁ;°shÅ%nH<>‹/Õ¸¢á@<Èûsêƒç<èNæŒL0=âQx Š¡Šòœk?‡¯¢½XS?Š™ÎKÖßÃZ¿pö â_ÎâÃÇå¾Ù^Pg-ª‘ÇHZ¬U1g•vê:îGÓKïð¯IÚ3¥ã„°NÎO”ß+²Í=!¼·ÝÅ›Rßs¯’[k]ˬÉÔ?:Ž ßkÅ$cÄ^‘G ê/™ÅÐg$÷F9K6ç¼IjüªËgÜwu~yuib:Õ×3¿'§ÀæwõwÅï{ðÖÑSë<Ù%õâKé_y~/Σ}µ çרžì~>±­}œ¥;Å Œ¼Œù̲÷yæí¿óžŠ•¤Gp†(DŽGí-uuÈmá±TøêÄäCòÞý>®©³Y ÊÏÁ±éy²øÅÎÒ=‘wëÎUrª9jΕûǹV,:›€E´|$ðÒ£˜µÅ9ÒÜÚ˜×õ$ìÌF}õÌ`”ω™DØÇ“=Xk]Fî)öÿ9k&ÜÐj8ûhI·<ý’òƒ‚_ÓÆŸ‹Kåy¹ó=Ÿ`b¾ç“£½ÄåhÞ2L|1éîlß%×µÎK‘Î;>Ìcƾâû}=lÖω·zïò×>üàq/Û¶¾\p‹ÆOÞBû`)>FêikɪÁUa´âó‚³:ÚþÛ:0äãä3ØÏoŠ‹^?²ø_µ·ŠæÒ,ýUÅØØ©³·“â@b6tT+8_ü.5°á¶v¿ÅqËþ qý—:Kkíû1J¥h†õÕìó¥G…aÙ6â¦5µ4ûúWÅÑœS¾9N–9›sYoOÕÀå…ËùYÃè]3?Â7Úw£{bëßê‰ôÙ\„?éù±°yßlséˆÉdç«f‡fn@04}œ"½¶Té­Q¿ƒj·Ñ!}…‘¼_84<…ju’AÚT+õ‹¸”-’Nß•Œ1VÒ¹Q¥¯Ϩü@¶"ü³ì`%— ‡£7y5žup õ»ØpfæŽÂ —ì¡j •¹wfçtþ¤ë±·sÕµ>œž%ü¶Îš¿#sþàD*w)èóÄùÚ€/ZÀÎxèàÑÉ[‰iÿÞñ{*˜Îýç¡aÌzỈ³753›,|HwÄg0{þv=râ[¸€èJ‘Svk=Q F£Av.¹&ø»¡i>øzÒ« Þ‰V Þ1ˆ›5–›á/yÜO„jEô#‘ÿO«ÆqÆÁPïGäĺoØ»ý3ÒßRž3’g’o ê-G¯kP?õñ´Wƒ$Nèê-ÒéŠg¦ç°ËÇüžwÚeN}Œð vÞ˜]-ß-{qYô N-Ž‹þ;‹ÌïÒ;Lì+1nðGrvµú†.å]VßsÎ\×$V0Ž5¶­ÕSo­ào‡¦ 5Ê?.‡¬™æ ¼5k_ššwŸšÔ5æðSƒzÍ"§~˜°f_ígÕ¿ƒE-°_ôzIÉ}Ç÷]þµ];ÏzÉgìþä¾i÷½ïxîîK?ë‹v?îÂKôà7ìþåc¿ßçÿÜõ†÷l¿ï³¾oç†3Ú±ºÿ½ûi_vË7ï¼îžkå‘·¬_ôèßXßzå—סMwµ÷ÓúZ”+{}n¤í­/¾êȵÙÏ&>½õùÙïG.6¹á¤^³„H~$Ÿ÷*t~ŠîÏì~huóñKÝ»}ŸœÉf9Vô*]àü¥Ö¾ò§%ÿÜF8ôruýÜÈäÏ€MSG—ÝñÚœXì̽)έķ‰Ï]˜wúÖ6_©ÓëØ¨à¬ÿíÒÀÏhøIôîëï´¾§sŸˆ‘ù;ù#|rÞlÎ Î víŒæ â3µ9oÑç½RŸ(ÿÎ ÿ}=·?sÇadþ²bKt^·ŸGß3OŠé©õQ×÷K é’p9æÚŸ—àÍÎÔ½Ì5?ä|ÈÆûwÁõ§9ßÍ~´·ûê±òÈ>lBñwö9ªP˜ÃÍKá÷ô¾OÝšŽ1¿Žyµ‘_у Žx™Å•Ð{ìÁ›ÆŽMíè¿G¿…î)ú7ß­xÇ÷˜Qga‚3ü²-ôŸ3¦Ò÷˜]Æu1q.ñ\Áæý޵¾4ú8#Ï ]Ï+¹àqĹº×;ëêE<íÌ5º5#¦¼JNîûJß\Hú¼è›¥ÎÑöûZj?«¬Ä¿Wï^!WŒ3 É<„g FO²ß3¬¿›oÇ4}”­ZSð‹‡Ð*Ü%ôÛ…ƒLØ$õ¸,ÈC…ó8ÈÞ™ú7¾WñhÎüb¶·Åâw žÀ3q™º^ aá©é¦£˜»ñ9•Wó9 õæÿy¬(.]öPËcÿ†ì¹ýÞƒ´ãÅG¨ÝÚ×vO¢ÞG,,›ç¾]u‘¦Æ÷€Ðä¼öwS­ôRk®{ã¬X/ç5IŸÈ׿ÓÅ` VÛ;´µ|ÖχoxÔ‡ï¿ë·nøÀzüÿßžhóìo­¯ÿì¯~ÌåR¶Z³aÄKâmbù4kà>v<á?©%ÁCk±ÑEðÕ ž&}ÅØiµ 7=µÜT/²ú <6ÕàËJZ=âõÂÎÅq ¼ñîДWnr‰þï <{S:(›â—¹¶øA{Ëçæì“xÆÛ–¶‹bZâçÓIÛtÁ]?ÒÌRÕâ ËÊ9ØçÆ5؀׳c4Ò·€­'C£þ‘BµùÇ]Á ²ÖßœÅ=Ô[JŠì86KZkÁc ý›‹êAÈ=¯#téá“ÿ§> ÞQà3+gž°}Ê]?¤Ý¬|pÛ¥×aV¿%=£Ä²ÄièI)~S~sŸíl‘| õ‡& ö/üªr曽9' þ8kfÔygõ<ÀEÞ”žÂ*ç¿íÉ–„ÿJ½ªS$—‰^I¸6-örN8<µ+ÁÈÿðvÅ8ÓÂÜg$gÀñ€gÜêÝ{ø‡Jm8sÞ\æŸØ âe­A‘ϯª-•¡ÓpU]cû”˜èÝÂ~`×äËËд°Fár9³U¹½âÅ-´Á& 3¿Àþá1«æ’±t`YÎëN­u¸sƒ4í®*RŸUÌXîPÅÑ=ØS-—zçÆZ}h#õJ8DZ‡u§×½O=lnüqa´~wl ÀžÔSàÔ â©0Ÿ*0™èÉP]´ ªCÙž¼ìâÖe{æ§Ÿ}Õá»ÏÎå?ýÀ³ßüÉ?upòìëw?é?¾á]fü?óƒ'ÏrðæOþ çøÛß[/àS5³Ÿ:ˆ#{FºðÔé£Ao„½#'§Üφøt…³;§.OËÓÓWùì„óƒMÃïÑœ…JŽ*ì>–ùÀ¥8£¾šiíX„ânS»ÞÔ¢üœÌý<£ž“ ~Œíf×û ZØ1Ä4kVKǯîxÆ{Þ«•[Ö-®ÌÜ{„›ô¾Ïú¾µjô#ù%ë7xÿÅ×{/ÜúÐÀëÌÆØïP÷i˜hÄ®ªŸ®ÁÈÅádë¹2æ@+`Ξ†K`ÄEÿ>Áië%F$G…ßÈÿŽøýJéÖÒ×OþÔí-ºs÷t, ¾Þç¸ :•ü™\g3æG¬W/zqÜûè_7g ל`×\<ˆ5ZÄYƒøgý>ô7“o4y‹Oëub%[k󑯻®íyœG^Àè;ަ8ë˜Ux™õ«,µwè\KÇÖ§ K9÷ç ñ8QÖ(gq°çÆ·û†ë’šêÌ~‡Y‰?%\®R;Þt~^ä̺sh5g^¸á42´ÉÛœ"ö½y |er]êÑâÁÀÙWŽù›êÜë¢8DyÆ%ðªª8ž|_qѼª5¹;ùSŸS›anjÔÂQ3SÏ3x!ÏËÙ€Ó”ühÕÄ ÷¾¯îÜ‚~Rj2æóîp}ÔÐÀÕ,âÒâþ³ëˆÎ¹/¡—HÜûªyåLfòÅý»6Q·ˆž‚óɃ’kúFëAZ†ÄX+ñæWÑï}•OøÝ7=u…„«å2#ý½Ä¹ÄÍSÏ@ÛŒnMp»:œ»ÎxWöi~é™Ösë|fm¬¤Ñ©3šq>ú@|W³•ÓI®¿mÏó°×þÔögìüýöÉÚgÚÎþòsþü ¶…Áù}°ÞþÀt¶rŽ}‹]¿çoänؾ„Öý˜Ãç~ öf©ó—a¢. þ/››\tx1]ÐÌöÔþ ·4mË Ú«ê•…žs1;$ì’úÀSwžýæE>•³F¿Ççʹ2¾£ÏeSºsÄzªëÝ;õ‹'ïs$sƒ‹ ¾¸Ä¯šþíù´‡òÍ9Ÿg%]ë†5Ÿ‡ûJoÍÄ¿Ëv¬áá€s Wª² ]Ÿeôt Ž>u(rÜÖ›}>q°|û{i1g~*ŸÁ¹ÕœæÀ :^WUŽF<4ÍâRè<âj•Ô☛¨©*þ®ÜUá1Ê3™·•=8yŠêzµá×ùmü»ðg·Som³Ú¹‹¸[WÁ‘™ÉÜa›yÞgiCÌâ'³N}ßÀM>+ñM©% ˜W}þ¡A \®òŽÊßÀ±ñ±‰Ã™=Õ:Ý{·µ`¬Á ¿NÿfÌã œ~XÙÌ)jà¼ù~É™³û%Lugõ<÷–Þ¿…ò¶5çP¾]\êä,“o&F,)Û‘wÂFze¤•o©¬¤?ÁY°‡½wÌŠ¹ÚiÇÜp|`hÕ^ä>*fnÄù¬kÓÞ4H‡šø_˜5ž:§æaÄÊ]Á‰ÐœöÏ0» ¿zz O3ts".ì°eøº“°"·U-†Û›ˆãÀÖsȰmº£ŠÕŽGbZå à©ßõ*Lê+çìAAw-¸ýšC+ÎTÌU6T:œj— \2âBb40PÎg¸œx-pÆSKbEr;î09íÜø(Êž“}ŽCèqWÖ®Åãׄo‘øŸ*\kìò,l¼?ƒÙj0á_øÂܵçyãÇ©ý#F!×ëçs‰ßX5gèuPÇÃGNàú~KîwËõN+Þ»ˆ=Æædì­ü½€=W«ÂAÂnèùèI?-žQrS†¦ƒä>`V"¬rú‘Z¶aWš2ÊnWr\êØäd-®¹Âçè¸P3ÃOËŽ{,±žÎH¯3¨¼ÿüö:›F, –€ ¤ÏdÿÍî±â²ÜÇøž³Ò°ü%8÷;òÖàJOwŸØA±ke®Ý Ú958òN›=@œ<ˆã. ÞŸá}Ÿõ}Kåð‰×«¾K^ºÄ_kÿ¹Ÿþïp ÉËy÷Y} ö¹ÒØ+‘SþjÓy¿÷qí¦r¤þ3xá:7à¥ûÞÔâÖþºÏeæ¶$bÔà•‚×€‘ ÇaÎ5ø£ÎA™Å«Gѽf.…ßÅ´žÓ_·)=jÙ5gLû Nqj»¯Ñ˱ó οŸLçîÜRµ²<1Öë"5´äÖ ·2gæX¹ÉÍô„1_qäüô9AptRSØó<õøûÝž£sÜáV’ú™°\Tu‰¬gò®cÏeWOMÙT_„­¹}o§ç¹P=2êŽWêEðgæääÐû±–=÷‹ý¨CÛŸç~Ý7ÞçM?{øˆ‡½zû1/¼ŸÍù[~ü=oÙùà³?Þj®ïóÐúÚj!öϽÞþ¶%=M¬iø¡ìŸÔ׆6„Ÿ õ†ŽÌ†çžtú§¾÷]¼ÃýHnë jê:#kÝÄn,†%Ÿ$voÒíúAûâOæ˜ &PjëI7Eö>õ÷Òq…$ÏmÍÌx”™¡ƒ4Loï4±n—Ö¶ÙžX×É©ÝÁ‡…#!ü0yÎØ'åMô³'¶¦€/™½î6êié{„Ù.àKtñ&±Ã¤¡Þ†éâ6üu+ûwÑO Ýâ©óï“bšìÕÒ½&Û'F Æ’ÿŽ]ÎnĤ{Ì n³/wÄ' „óîTÆNš™Êú-©Ÿ3ÓŸEJ ƒÃÇHßÀϲü úup*x&Ÿ‚öaM]Žu`­•ûUâuå†Ô‚ŠüX“ƒ§O}¸{ü.õ?'ì~È¿ƒ©ŽuNuU°8¥Q»¿yI¿Ô, Djc-— ÞuÇB_Uw°Ðs!{Xú*õNnC¿DÎ ¯,lê%êgòÿnï§™Wnëìíù™…Œö⾬iá÷„a,àyûë«—x?p«-r+?󪤟šÅËŠ|$j;ŠY'j۲ò/ÇÊg.fNaþFXÄ>3•æ6;°€×¯‚/J­‡~÷¬/é×éñâ'ùQî]Á§I«a_ûçxüüœ4z?¤®¼&jìpçµF;·~ÛÄ‹fiú謥ÞÀýÆÂC6²—RùwÕçœÀ$¤ï>ié]ñm]úBÏ踃°Rò5l›ÿÜxfpïá.¯ÿ˜õ®µûÂOݽ×½qw}÷ ¿é_<ûðÑøÈÿúoåðêW=iÇžõËïóæíOÿæ¯ß1àûïú¢ƒg>ùŸ–ñžï88Yo›è6ó$XÇð=[J·ÞϺ=Úp¾·½˜õHi-ÀÑG"7&÷¶ŸkOKÔ®£Ö?˜œÝîýÛÜqðÓYs6æÐIMñŸ‚7Ûf”)Çzº uFÉy–¯)‘» '½<ØE=ÇRþ˜Y¯vàŽÐBöÞM;ÒÉ)Š%üo[Wì ù¶°Lê‹Ö³Ž~æqQoï|‚ø_µ¹eô,ÆLÀ3¡o™Ø 1—8‰yTÓ«Äö³4!†ÖC±DZ8ðL×û®Y/Ù[9Hÿa%~—qQgÕ Õt»Ã›`¼·‰õoøãÕÚðó·À·¯ƒx€ÔäWýçô«©oo´Ù×öwvfíû;ŽO âówÜ?ýœlg`vh «öO-Š}”M­ÔÀWÄq¡WD:¾¾fûð…Õt/|ÑHÃΑÀ}'‹¼kcÜm»4¾ƒW}ÿ2Zþ¾'µ™à¸v˜ue!~¡ËË'x'Ï»ÿ49ÅØzúL©7ˆ“^e§.¿Ù'OSLL<½¡6}–ŽŠâÊô Ìüˆ\÷,ý·œ¨âеpVß—3ÙŸxšÕ¥}ŸZ ÔO£^vÃ~©aéž§SNìï~)x^òù×£4'¾Ütήc0Ë»sNuÔ¤‰Ï…Ýå^µù»£ôo|]UKrì ±}.÷š8 l-w}¥ß»SãñÁ*m bÕ ¼öÊÌVÇQ[üõ$j²–).IŽQä€[nÅÍI|e%>—ù)iIïÿ4\É ‡Ð¸UŠß—‘]àþ = áûÄk‘ã†O±ÏÒþ  ²–6"µð5}ùíLż'[3³/ÌŒ"~=R¯9ù€bÙ"ßéqüðY\ê&­†}·w¾ðãí¼ýöÓzxõà™Æ÷[žäú®ûþ{öï·Í§‹a:t 3÷y”ìü¨ë÷¶w´çb~„êŽïSkT­&Þåÿ•ú«ýo{q•љߧ¾AíYÚð¹½´ðO©Òg"›(îÖÖRï$ìøZÎ=³ó…¯ –b-í¹©1*þ®ÜÿAµŽÐË™önûcŸ£.ÁŒÏA¼’3M?¡ª¾¾†fmkˆ¶là…ÁmÓ½r ô=/V8h¤Û™WLå¶F÷ÊÏJã1û{ÙïJ/`áϦý–fà¥Ì­b^UÄvø¼AÜÀˆ.‘ŽàÀÒÙ¦'©+ïYà쟾>4´žk毤Ÿ¤~Ml? ;ªxݵЄ“®•·Ur °Nûw8†èánæì‘œÙëöUõ†*Nrú¦a5H¿œ‡œFØß$ß/œ*tð†ÔÙ9u¾Às\÷.9‹ÄÀœ7ûó¹ÊÈIÔ¿=Ú¦oA\mÏB\O‹ü•¼wS¼Lû¹m|ìQ7犟£á<ˆ§¤3Ÿ}ùs›KˆÝöü‰~ÅAüe­‘ß;#‘Ÿgžàp¯Ô4HŸå(æUýïÒòÆè«V½®«Yn÷grVaÙâÍ—óLÌ·–w…½3Á-Ï¿ Ü'ëHà4ØYzÀÃÀ>ÑW’îCo=saîápkÎ*+pèɇÂgP/ñÁÄxŠ™ŠîpkQœXÙ§nLÌéÜâÆ©÷zØZ¶l- °B­ñŽÔÔ @´âf/jÖΩënﺅs«ñ5—Mhÿï‡Ã7~ýçî¼iç‰Öxhïô¼?ü¹ƒG=ú–C[ÿ÷îþ¢õú{Ìúܯ{—jH['±àKШT!{DüÞÄ,™=Å„Ñû޽¡6ˆ&wVÂU`žmÇO¯¿õE¨ZÁ÷ˆ‘8Óv/Ð1ã.£‘`Ÿg¥>¯2‡…{"ìxI,Å,ò9fQËgÂѾÐéVmÀm,Ü‘Æ=½ìøM`ËUu‘³Šcn3<ô[M×ãÊìùgÆ4gZx><Ǭ÷Ùý ¾»²¾ö,­ŽœiÙú1ôÃÃ^&8Hç œ\û.õ;NĬÌ3ÄGwrëÚ}ßÿÔwÞwgüì³£8b¹øýüÓ ¿¶´,7Ÿƒ/¹ï¶â°ÄÍמ’Γc@hîg=\±Ùú?g;> Å7n»'l%÷Î‘í Øvg2çLYüüŒmÁš+J\BÜùÓä†:ðÈ›ˆƒƒ»ºœòU‰ƒC‡3t%e§÷kÍ[êZhözm5ÎtØî¦+á¹Ô˜Y]¯1áïü¯ó”áVf¡3ƒÇs¸Ò=›×ð¹àQŠy—œÕÂüo5½ð¶Äg-Ð&Ÿwˆð¦ôü»ý=šª¹3ËÐm›jC•:ç³p•¯]ÎwW…§ ¦%Œ¨¬¤ÃµYu´œ±ÖÍÙ`Æ’ÛòÈÑšÈÞàÔ*ì0.?ÂÄGðKêÀ²Ñë<4½£ñƒbÊ"{Ì»Mèÿ4l6õ9ü»¤?DÿlŒÌ%ïÕ™…g±ˆ÷ŠY¸è÷ÈïÓg3q&5?Ùïl‡A¹ïµ÷aÎq$}¶Q m®ˆ7¯ŠçsIü€+œÙ¢¾ˆBŒnŸALH/£|~ê¹Á‹[åœÆ«è´%ø¾kx¦X‰‘í¾Ëgœi+®bjpˆ³Tï¡ùáù°ù5rk>;j«Õ€4»¯àC…¿xüöª3?ä¶Ð>G¶ŸòJL <¨§HxySf—þ½FÙÁÄé±w}¿=¹=¼—.)sÎE >5 Õü”+>§‹+ý|–†ÏwÞéJ½øÑ'{-9¼ö~̆5û¢^x¸òô|‘»p>*ó~ÐqYI·Iyg¯PÃþëýÉóF¸¹CëµÄ¾úÙUC¬SðI}¬‹]¡ž,“3ÕfEÏÄžÜÅÞO:–U{ F†Yåžvöqd_†[Û\ò¶¾ÇÒZ Ü©Ë#“s%l|”?‘}º³€…Ϫ˿ë;·kõ×ïýŠiçoî±»Ïü¾¯»ü¥ø=—ä»ÏýáW_þó3¾£Zøöý¾ãÃwþò±÷8|Í{o<¼øâÿwû“ŸððÓüq ×ÍzÍÍ?¼tñÓKë7ŽÀªÍ_õX=f–ÝÉ}*ĦàVƒzYí܇ߡ'ܹÀ êô³8 Â=ÒY#÷êtoRÈíføÒ6?[çr)^)}o~—Ãþùü¢¯Jö[óª\ŸÕ¿GX’ú:÷’‹££ÈgÓ7’ü:p'Ùì*Í›¼³mþ[ôÃpí»å¿³G쎦“_´'lQv7ÿ~Ь!øÍěڞ{TNRæÖ§_‰7„ÍÖUò-£U^@Ï‹òÚàßkmG|ø‘ækÏS ¾ ¯«¾9 YÜT6.yGÊEýßÍþÚ¿Ï\\U†à¥P±Má\ÍҢƗ(9'X&úoâøåY|?ö›>ªA¼ïÛl³jžÌ¥äº*ïžðÇêõÎLvvÑôî·àô’#/ÁM°¦•L­MÄ–o^šäïÆvW£†N<ŽV*ú1zƪ³ç÷ªÃñËÐÕÙàèçÉãÔ;¬º©ú"ž²íÏ+Y›ÇÿØsÚ™µÏ±¸¾ ¾V5ùÚòÒ¨“€÷®º9u7i¾Q_ÛÝ”–ú¹â€ˆ‡¾GŸŒÅœKÙøJ}ÿ>Y¬Ã19½|âÅŒùZqÍ™¦¿[Y+êÔuöÆà7ù9#ÿß§gQ¾Ý÷¯ã±gnÙj·â*õúŸÉ?õxÞ/âVzÅ©3`_á¶(Ùî#Ì'â|Ý ÎB®±‘üШ¼˜xVñ÷V!v\Iw™øEøÖ„˜Å“íNìË~NŽ>ÜŠŽäpÄ#p~Ħtq»^½ÙæÇ…Îpâèò+Ôè±Ä!ÔýÝ™·¨à‘Úó5½N]½Òï–0Y8í…}–_p_.MžÔüÁv‚¹‚J 2cG05ŇK8Ù›oȹ~VÅYÎÒMš53 =jü¶ð´Ž4ßì}ñ‘ëĬ*Íp)qϯTî¶r&è¯Ä{V̇í/CjS§nØDÞ-L ý¸lóu3]õ{/¯¤±ÿF8Ò’Ü¢ÅhOY«v”¾qÖ¨­µa—Cè/ùý‚;´‹czê«Jí!á„`Ç®¯ ˜t’/C7V½ˆ_†"NÁõ܈YÚµ²Ý…ü28¡'Æý·³`ç@gk¢†ÈÌYÃw©a’ÿw|ª¥zeѼóÜVe¿/Š¥ïGo/~}hó‡ÅáØÊ™ö¹ô™ â¦Sƒ i1zh@pçÛ&G±ÇÆÂ†£±“Ÿð¤³Æ£:~Æ¡ààØîVÜ¿KºãQ'“=ÐýÜ“÷8©ÓQ˜ZžqÞ÷Ì/åg&îC§ ~ Ž‘ö@È<€u ÏT¿ræ§Í¾_Ìzq¹¿½³jËÅï¿Æµ|^ôèß8xâ3>ñí—Û5ÆøÁö}vWl¾ò…ý˜Õ³fÊç&¾d±´®…o¡£èÿÛøC¡…|E؈×â×}G¯ ø²¸1Äy ´5á ËvÓ´7‰ÿ‰VÒ6×läÄ·Á©+®Q³Ïþ«.6x¿ÇÑ &¢&ѳQgÝÆÌ¿É­tÆÅMu[¶~P¨u™oÓýG_¥ööaÖì]åZnk­aœÜOfrÀO´¼Dõï‰<@ëä~L|Ö:tø¦âFrø­U}¿¬Yò1è•ÑùN¾\éì,Èu”›yD­“ü·ÃiÁaüÙÑ$GFöj¢V{»æ&ÑÃÒÇ Ô£ÑÒÆOÒÙRLáy1ØùS§a²ÀFqOàßÜ?ÀyŽŸ>ÒÖÌì³l)¹uéë!pÌÀŨŸ®’3w³zUCÛ&|»ó‡È?ˆ Ý6 ÎÉÏíïáÜ‘s!cšüäz–^=>à ö.Â"+xwFñá>=tøw¸tÄ#CÇ›ÐçÖA½z—Œ{©Ó£NAMØ=Óˆ­"·V¬éºöñgiSšs؇֯Ü"aÒöYÎoG;îâÜô«tÅ¿Äý’î·ã©prbm¶œGmv9z®vXÔq¥gÀö‚ü •òbÕ:«÷ÀÞ™àâ`‹Ê¡±gªÅoUbUá}¶¶»:DØVáR×õº€ëÛ’³SÃé9 ©«â«ÁåÑJ®ähÂ72¶¡ßR±×D=€š)¹›â»‰žò~´tµV,ü7Ϲé8úÞ†Pj†dß.½Kääœ}ü¹ò“ìíè¸@ÔAªüþB®*¿âk¡:C¥¦jÏó»ÿãÕ»öÙÿëǾÀøþ»ï}Ñ}voùW?zù·å‹,ÿwžÿÁ÷عÇâ—·¿ù‹ïyé>;Vßÿ»ûýñšž3ÃÊ"þ½ºŒ™¾©¹–œúž•Ã?Mû=bÄ— ÇIÞ&½¸½MîsvÝýœß¤sFÔ? NŸÖݰ"{¿N‘µ|òå¿™£Íâ{†ömh’õ¼aÞI¶¦‚%uZ^Š™Áá&¸ˆfÓ¥÷¤9Þ Æ;TÕÿ«p*þžï†R—¾QUK={òjùŽ:¨v†MTâôìÉÑFâ¾÷ýÅÂR ñ0ÐJK1Ü(.“Û©¾¿ ,Ù~ÏâD;#Ì#_5Õ’cv3©8û¶Nö7ÂBñ+ðž¹çka5üVô¯Ãçâ~ÑËAn—œÁî,ˆOŒÀî³æÏUr?iw—xEâVlRÔC.¦–ß‘4˜šØ<>×0èà3‹n ¾²Ûû¹|ã~œƒÐŸ –’Íš”§‚VÅÑþsaúÊ+ή»œX=[²ÅÀ!åðž÷Ýý¥Øþ£Ï}‰õn?âa¯öû}òœë‹/>s`õ2ó=§^ÿ)Âc’f1hNZ…Â~êÚ¥çÑŒ¤NWÔç™1} à Ô¿à‰Ã¹¢ \¹=>Þ¹·Ô¬É+åG+½-̘¥­õÕ™lêHúØ9ÅÇkêXô½Ù¨æë½­ZÿÏ?®þbú]¨+æÏÁÑBŽ‘œObpòKùŠÒz¢\7{Tí:í$ujq/’{ÉÝ”†k<†M;_ÚÝ›içlOS‡µ›þëM3áÔÌ~¬Š×«zq ¸9º\ŠCúüÒk Ò »þMâ–èa /¤ÎVÁçàbݽâZ'ÇXRK…Øf1ìQã­g¼>ˆ«ùnÖáŠêAÓÐÕò8ÿÆ_ÕÝ€_ÊóOŠU–àÝ`ÃâBÃß*ö<†¯<«ýÙÈš \PrG|‡räžáÿâ®yM¤tñ“lÊÅ”[ÌD+ú.4ªÐâ/Cö%7mÎMÍÒ{WboÝÁ5½l›My_ñú‚:¶bì<÷øVraêžøoÝÿB¼¦H=ÑSÏwR'§?Qíe! j &ÚíéDL¢såüÎ>µü¸a Ô±”Ódœ.Ý3?û­§Îc(LJÁÜìÙ­×€û[Ë·´Ï‰¯R±u>÷iߺÄ‹¢&ÕÕê‹ð=b3ô²ªîü÷30ˆW †%ߪzó%jèŒÄ~DQ$Œ+´gì,I{"õšå¿“kb¾†÷‘oÈ9ëZ£µ0x–œÁIu`çfPßb ޤ±y$­†Íœ»5hziØÿÀߢÇáŒfï´yµ^ëõý#^Ôç {,m‰èѦGšóCÎCljgXý_»Q.ã9Ù¾^·F¹L=#*ztˆ9nqÙÓôKÎúZ¾zûò{_ôC»ÏûßÛ=|î£Þô /Äî+ðòÝ?üøßµ}ù–Žþà·w^ó¬oÛùº¿ú?‡ò¶ÿµ}òÝ;_º÷ïì<[Ÿ¹Ç/¿Ï›×ÿrõÛfOžm½ƒø@öý†U?îƒ?¯9zŽÅ­Å%PíÆc,ÍvÜȺܠÞhìoä§”Ä^zÄtæ—ƒ='m@Ç‹Ñd‚B¼më,;#ÜðjQª\)j¡âÎû¾ /:;ª¥G­Gs}jó¹Á'PL01;Dç±ÀÛÒœÁœ¡:æÍ9]DÏPëÃÕ}[ QßV>äwÖÎØµ ®6q>úúùò‡Â»è>|*Öc5G­ù u?ëÆùX¥žÉ9PUî¡|“9­ÑgÓò›ÐÚR>@½GùCpÀ¬‰°1Ýý£w-u¢Ævƒ4†6ЬO{(ÿ‘½Iä¹ÜwxR-Ÿ‰>£¸«ÏQÜs¤¨5Î/cÆ^WÏvý´Ä’¸ÓԽ穧÷˜z"ømò|ÀìçÒ›ž¸WÜ-Ùã‰^ÞMÍÊR>–>ÿHó¼á-nJÏVÚ†ä[…<˜:‚Ù}ócºß“bZñì‚džfy¢ž£*Þ,ì¹ö:gu3g¼Ž~Na>5\åV`ª ~nq5Kâ4{þƯ?»nýïW¥r›¿‹½ûÆ´ýÜ8â=`–æ%<ìŽs4R'âó…Åp àI—¹õüúîe£²NaöG¼Ž¢x‘Ø>v¾w‡÷…sÁ=RN4 ƒSŒæŸ«šûÔ…î‡âÓÐ~ ü6f£Í©Y¾OÀ×€ZõáȉW w,à‹ÔìfqËᔨ mvçH ö¦ã™ùç?ð÷8´3ú»ÿÙá‰ß>|Ê«Ÿ·Þ}äáöçž}Èáý¾ãYÛ¦û?»NÁ›×W^û ΰ½µ¾U«‡–¡iŒùsu]#½y·kžŸl :q¥Ûæ–>ž"ÆUq%¿µuSœ¤Y­>gÞ¹¯‹ùnËÏì›­·»!>™ïOpÍoYŠïÏÝ çÐmuÌnïA®dÏÄœ ÙÕĈm­C¿ð·½Z‡ÌņàËùÏ…—2?î :$ªIgÞ×á4NëD-‹í¦øÍhš*—¡»&ÓœÕÓ8§FÚÞØlç¥ÔuÛt-·[ÖÃõúnêEgnÙùµ¸750ú®’Ÿ³†/,³„mt޳@=fe(¾ýNâ­VØPmÑñËJŸìƒÛ*4èàÙ† SçOyô1XðŽ(û4ªnF"|Õ9r‰ÇšífTÀª³øóP{"¾¡oxOÏ%.kà¸êG(â½M+õ Ìš_Áýo¼€KUýt•{‰O›ƒûßqW[Oîº÷#*ì³ý78ƒ°ŸYúBÌN²=êø¸ô&{ÞØžÙû àîªèNŸÍñVÍö¦Ž)û¶/]‹5ó †¦Ñ–õÅlÌ£Ï6{L¯ÁfTs(³´rìüˆcJm¹ÐkiöÄî†0{l»ÎÀR¶™Ùh û;Û3Qhgú8ןÞÜA}k:~—Áïe?¸3YÿÐ~¸ƒåœ€*®ð½…·CÝ{P-kSzÏèû“v¶N¾-ò[òáÈy£/“¹ :ÛEXÜ4ÕUŽG0)¸ˆÒ Ê~‹!ðÈä¢Ò;O ÉçÏâé¹þ|ôƒïðÜôÏe>Úì¼DZc“lA{‹~÷þ”:ü¬¥ðÙJL æ#ük –4ˆ@ .¹ðÙ…rzdÈ-°Ã‹•x>ösù)b’õfÓ5¤Ö…ïòsúÝú¦ïgglûãïù–ƒçŸ;8ü¢ÝÝýƒ¿û½ƒgßôÌÃ'¼ï{¬?Ð뿼~ýú¶‡ÕÁ‹>úg· µYØ-xö¬hŠÚ^ɧfÜ®a«=Eý•œZ9Mrqt昅Ìì¥*ßV£ASúZ×#¶OñÉ> ;£úcøR8†Òg¨Šaàj¸Ž#z‡ÁdÍCqWÇ1:_T?/‘«]ÄÆÃvÛ©ùTÔȈåì%^CŽ ·‹uìëø|ÕF´RÀGUb-ì,¹îŸîÐ19éô!yUrPYoÅ£ê79WÈóõ¿ Ò¶äw‰ì3é# î^/<9t¾…ÕS/úZoôH¹¾(s5r~ßì÷ªÓ" f… ×õ Mƒx÷}ï<ÅAû²Íï§ð¿U»+`¢C«û£¬¹`…Cj 8N±VM¡Æ÷æ\TÇzÐýîð.åýÇÙÃ<„sÉÝ —'¾“-Î7º@`××È/$n üFz²nOr^ÃÜzhrn°fÛ±F5 ÅËÝüõ˜ÿ;‹·Ùê&é©ðúÐù§j6‡I×è˜ô½Ùw‹®µø`ô.™癕OëMƒx•pÁ¨ƒŠO\•w•ëõò#f¦/ÑžÏî¡°Q4œ&ù ÇŽÄ«`}›ÒtÑ»—x8ÎÙ³WèË¡¦­ûSVÝœ@x¥ÔàZQ#RßvgßÃN€qêGR ^Áo°Á‘“—Žƒjçpf„)T0EÕùÐÚ@-u´Õ'æõI{nÕ ÑéòÄQö nTÍ>оãg£†Ê:È>R¬à…èÇØÏ©ƒŠÞ‘w¯µ¦†=r&W©ÅùΙßWê¨h‡Ë뜔șŽñÙ í1\Ôä9RG€KíÀxøCöVO?Ãxö~ÄYÓøÿòöúÝG<äóÎ~ÙÅÏßó´óñtôU»·^úàŽ}ÿOÝï_ï|Øá?îüù/½{û“>-þ:}ïÿ¹ýë?ó›ÿ3>üñ°þìoû®ƒÐMô¸äd^²6,;‡†×æµ5ê'›¡¥è9Iä{Ádž;FÎ(0û^5gŒÚÀVÒbŒˆ}è§ŽD~N\®o$Þ]‰Al…Î.Kîé"¨+ç[(oñ=¶u@£ÐŒcΡÙÍËq^5Ùÿ‰yöôøÈæŽÊO«ìÐ~Ì O½Qv5klš@¾8’ËbLò9îšÎ™Ÿµ¦Ë²Á¼´©sÇ“]Ãáì:ê†7¬…KÏNä#š¸'gÖ»ýnèÄF,:wZ²¹Lǯc*CΔ¸/ÀsyñTÀ÷&â5æë.ÅÅø1»‹kõ_]X¿žÝm¨îæÞaZ<ÊUɇ {ïr‹Ûz…:lÀóÛûŒ_xʱáÿuû¾¿}øÆoýäË·=ü¿|É}}Æß}ßÿ¹‡÷~ìOؽ¹ëoŸ~ðW·ýü=—|¦Ýñ55F[oÓYëë%öœ1dC~¢iŸŸâPgSm²È¹Ô¿Óéû§ùk5æøžÏ< a»°<εçí´U–Ôü_Ùõç‹ÇMñõ͸4j`{Á]9“ó¢oÌ> Ž1·bÚ5x¹4“ Újª…®m¯ì3ÐR–óø΃ì2<’2«FK>±).¹x‹üޝ/ù¿ºÝ²7¡×)—ÍG÷•sN¦<ë¸ty¶Ÿ\`ÒÌø;/;¶PL³î1¸ö{¡«é>~Ÿõ'³óú¤—к"îÁ~¯Ém°ãf»àHÙº¿S‹§ikiqÎ*uwÎp«‡à@Ó×Y:Þ;¾>ø¤¹ŽÙ»Ðq=&ò›!fa*>÷PlVÉ È­c=|nY¯ÁBÇÝ*ä=CǽÓgæ»r~Áì½™oKï‹â³Ô‰X%ü<ñÎ?LCWŸVìåy´nÜÃ]¤ÍÔqpí2«OívÍÌ„ÿߊCÇÜô¢‹›fNÇšºÓÛ%çã½Ò¾â‹8CÓ¨bÜî*¦|Ò½ «`¾â>|ä¦4ÛWê¤ïF¦°vßûðµ§+¹Z Ü-Ù¸1lvëqëïÍ îõGõöP/#æHŒuÕtA+3¢¸§ðmSˆ»2¨¶F>öK}Žkú¶˜›gvÑbY³ïæÇ˜g‚o¦gEù™¯Þ…ù&‰i˜©Â9™¤Ìõž~Þ¸<øSæåC«¯a~¾…M¢;ÓïG¥¯‹óKܯ¿­â‘ç>ƒvX‹ê‡Þó’õò=ðMaïø‰v&ìnT÷PoúRñ"¼ÁBÝHqBQ½šùd©§½öù<ö÷ÆíR-s‰¶“l¯ófC#ÇçЦNvØb%}SxÔsð'À§øyo›ï¢÷.óEö˜{ Œ®?õ¿–ék£z 1Qiö÷‚´ª¯¢EµÆÎKDGÔƒUo?yÖŠÁC“#ø»R/ÜÞA÷Þ׃š¥îkžòIj’ö ø6tÚí®1ÿMv± S3‰#¹žºœ;Y[w,}Ÿ«þ¼ÂuÄw¹Z…ƒÒl}ôlHǃYµ¹Æ/ÔO,8©w"Ÿ[©×]1nÎ~ ¢»3©¿Mõll¥î9¨^w½ÜH£¡ë‹gÒï {R¥Ï[û>¬¹Õd+ñ X˜í : «¬Ñ_SŸ`ÌEGAkà6Hõ¢"^B¯±2ÒO1KÇMÚ£uP?Œâï.¤úëNP«QçÆÖŰbðYðejë] Ð÷ÞQ`'Q—$Ówl¸qùl mžßG¼ç}ËŸ{ÿ.ïõöÇüý;~c[y‡îÏéœÃe:7¼ûKaµþ Ôí{ÓÊwý½4c©†{J3q£ofÕôNÁÒýlâ—ÐnÁç÷õå¦sšº”#õ;Ù³%vÚÃ[ÄÉenõÖQ¼9ùåÓàÒ-0b‘o/ G¥7ì4`®i'•ë+>N¬¤« á7ÑÒ!þ[#sÞy7bRâ0´ºÀÚ:¼>{kТ=¦†ë÷®-1Ã,ÞXìCê¼ðØ7áêpÐÄÇHžÀˆÖ = Û¶AýÞ÷KüˆiN^ú¿·ªÔÈáœËæN]ib/釽õ›¯&§I¹Ir©àžEOXœ3jrø<õ ­•ÇRó­ƒj¦Úg¿+WQ§âþ÷}†/è½&ô§{Ý:æÆG-d3u–Î '?¥ÞÚÌá×ûjæ˜êkQ#×LÝÏMðMn€×PVR:ŸÏûUp·!û¼ëUçëz3˜qÿŸÚǦøçòëS—·Oĸs7ç¥ã´U0(}æ±­†¿nZ!~W© ©ÜyæÁ¥!ÿϹtô/ÀiS À,Úý¶þà.{É)ˆXü\æÒ‰ß'ƒXŒ5‡E.[4¢mÖ±Î>ö·ã׌Ê!²æs·´üáýCؾ ¾7_¯ÑëqŒá¹Câ7ýt3*lo—œK›¥XÏßÞ 0Arpô6„õVqÉ+|ܨíwhÇghL¢yDm W~£P³Ó™)]n®^"fÂ\*ðùá)¢ƒ~iÏFîšz1+`NŽèmkêVqFB£}³ƒŠ|r×з†ï>Ê;Só'ÎgÖçë>g¯,ºLÊ7˜Fü‘½­ŠÏüf,Ñ;÷.Ò þˆ=?=-ÜAa\©»©<\ùÛ5Å&‘§5èVòTñw⺪v^yþµº©~—è ŽØ]>a]#ñü [ 8]ïGò:®ƒê‰9G¦ S­5{ôˆèÉLJQ£Šz³û[kM Ð÷êuëßß}%óïY<Õ»¥)/í7¯+‰'^á. —¿»¤¾t“fÛª¯ƒ¼®P7²µd¶Ì‡ò7ˆ/©·ð,«¦±–Ÿ ˜º¬zLéc¢Þ vR°ûöÙÂ#—äȳúªå—=ö°³Û°µKœq×Ì£¿Iç‚=”ýÜ ŸÜÀ÷M;öWÜ |-: Uxl‡s†^¶õû óÕŒ†ð­Â²}îiô]U°E߆çŽh¤€Õ6ÿ½”ö½ÔÁdÓ&¸3g¢¯f©Ø³›eµ—È‚£Ázv6³ÊàÓ!\Ò÷~â°Ð–»Å×]ûØÞQÌCŸ`)̲(žá¾,"½…(Ï!Ï|ÚÐt²ïˆžÆcáÊ7°î:KÞã’9˜î8½S˘#›³ÙØBûlæø êy'vV~C1gkÑ׫wÊ8GvÛë€Ä-g ÝJê9ØšÍÐa'G#†ö;§¾&û›%5,ò8îàÝÒz"ŸæL(öL½ÝõS5=aî£ìaéæ!°ŽÌëêê6çõsrû¹³Ù미E¶34Ë™ ˆ}¶u@†uíúʳ†&û¹O®1¨wß¾Ùúb³&¬3k}Øë>Ç¢wGùäÅò¯y´iþVûÿï{ñËMøÄ×ؽ¾tÿWš?·=6ß~ïÇ~ÚÁ©×¿Òr$Ç„#`:ë²Ûªg^Éœ\Dyà¾ÎTòY‰ £Öxx¼vyõ87­ö‚Žÿ‘f®ZÌ&¼a‰ÎŒíÚp÷ІQL9‰'ºŒx+fý€Ñ¢'&‘âÀ|YóO|6›}£ƒôÍàÒËÖÁ³«¿Ÿ¨u ⩉OMÏ&ø/ûê±ËíÒ²Áÿѯ€¯R\!• 8FEøºêÔ{ÇŽO8Éø~ [ÕÙ:ôÛŠlJǹ9ŸõPñh'0Yá‰Øjµä&#¶¿@n¢zŸr¯*Lºp7íwÐëœ[¶U¨³à÷ÑTA{‹=“­wåÚ;À÷Qü¸wŸLý¡ã™§ÿ&¤!,gŒ}¾a [ >{-ÖØø'ºCþ¬sã/†NCOÏ™½tCë ㌠¥Áß \¯"_WØŸ?`ösMñÖ˜½±äì0Û ŒÆþ›å™–«q¿ÃÓÒ’Þ-b.¾KñUÖæèC£V„}¹;gߢ›}\‰uÈ=ƒƒq*ã2{~0Tü]Ø‹à[P§š¶°ß3ú,ˆë´O²­§u6˜2êý„ƒS[(~ ?‘‰ïQÇ„å+>¥2Ï—påŸ[KÝ3zBGÙ.ëf2*ÏL}ÉY\G|Tä(ƒ÷Ãɽ[úôÄšø}úA"þºš85œ#Íw±g†ï øP8ø%ÇúTãa&‰ã€f÷¥wYÑ=ÁÈMbCg—ü†¼ŽElõnõ}¦Ïæçà´âìQóZ_Ï©ØPm=b#{/4ÐT®œ‹¥ÉgÁ=©žq ŸéQ¤i>þS§ß0‰«Þå >Ç{©»6æ?Ǫv2ѤY·#¸FãÄnx‹ÚìioÚóZÇù=…~h«´Zã=ÝU9.¸O¥6£|iž¢î8Rï‘}Á°¥«#ÿåwa9«Õð< 7°M4p_uP]›-º[sô6Wå»ÓœÖ̲>OMÖ­ ßK½Dò5òßÀÈ¡Éu”'ä|xxDCèKúi°mì›xšK΀zƒ¦~_-®öyÌ4'_;´´ŒÕï½þ©áw¶œ_ îß~Ïø_ÖÄräRp´‚;q{Ýýí^QMÂí¢üÙZï°•y„pÁ5±º8ÀŠñ=ÆÑAbm°õ«Ð÷>+úlÐSgîy&t-‰·Jøàð7p…õɇºßîëÖaWž´”¥V¾ž[oå¨^ºÔ¢èt–©_Np äýÙàæqõói³ÓM¥n@ =‡ÎÁrSšFƒ8Úðì9švSèÞ{(Oªß/··>÷Ô6àn’tZôÂ8\H÷µ +jg=4ªB~³é,è’4L25ê*ñ—ð‹ü~å“ä UøK_cÕ“QÀ0áMê _È|ª³_Ø0·}ª¹åáîâ›ÈûÄÇÀn-ÀÔÔ [eWÐ~£—Ð{t—qï2þußnñ{†vbä§×ö³À„CglJ1F%…“ª½7œ8ìÏq&õ›#ž“ÖþéüäYB?—|}Ba$]½={ üÙ©µS{Qﹿ³ý=g}ˆ:ºÛ:ñsÈS+÷E<¥œMÆùÚ”3¸ßMš­D­œ ç˜o¶ù}ÌœKÎËŠ]U›Ý+bNû:?œÑ|´.‚#Üz¸Á—ºZÐP¬7Ò'¡|Åÿ·jÂàâ“âê\;óÝÁÈ~qñ—s†dÑþ `fñqáóˆÍîüÊûÿ箉×ýÃãwÿáy»û_ü5—_ÿGï8|Ñ£µ½ñ7ϼü¤ÍïØï~á7ýïíý/ú¡ŸzÆG>úÏ>°<ûû>îS­&`ïixÁ#öÀƒ“ß]ªÐ4—ÒÛ«Ü}ůԹ´ŸÁ·{.ß7ÊV/CçÓs·5šà}+ûºdNŒêcÄjKå 9ï©Ãg|,ö©{µ¶˜‡zŸâA¿›' —C?n›±ó)ÝÀ}΃ìˆÛDå ðFáÂð’Méñt¹Ð¤¸½R›“s×^o¥nœ¤AúbÄÑØ‡¸©éVˆÓ£·æ6õ‘ïas˜¡QkV°aÛ#xxä)Ì^$ç»á€ãÛåßü<œiÚ±“ðô".DQìŸ5{?Í•ÁÚù^ùÐ)0“ó~N”UúLì99[h‘š®ïÓsG½rmƒehâÄ áðGèù|Ö5½Î†ûIÏ }л«*S]LŽ4m;ÿ³×ÊÏ.ûüÞÞ-bŠœ)@?AÄË£‡£¨<²Š_„ïΞMl‰8'•; ®/.œê|ú—!t–Š«ò°î¬0­äÑQ;Úhà-ôSk¦^úEÙ ÿyôwD<®8uœÅÁ&ßâî(¶™áõÌt¦—Cñé„Í×,TêNã¬4`a¶ßÄÜÅA5Χx8‰0£¢xF\ðÓøfÕþßBÎŒ}˘‡{7DÂR½òEÏïkbvIwaIÜ8KK×Îö‡8³õ~FĽÞà÷ΞOñfbp`¼Ú/â]qL¢~²zz›¨gŸ´®ýTÄ«âÞMôçÛ«^œºÆòSà4W+ù?¼_8§Ì顆 úë‘8&ï -žl¡þ'P>s Ü>´ à,röVšËøØ“–ð îÖ,jô™lÍ©röÐ8¿=gØ€×ëî8&ƒ¶œ0i>cì¸%Uq©Ÿ¦½UÉ3©¨g:¿Gó´ç{Ÿß_üÈÓçÇÿÊáSïÏ·Orÿí“ß]~ú7ýΛ?ù3lÏMàáüõØ}°³ðÎozïRXžòŒSK°êô¡±fCô$gýˆ¾|ÞAÚUà©£ú 9\ˆà}Üæv.¸1wfm^ |ŽYš@ú›œûHß‹l¼¹ªZö‰ó<u½Eò úC[zMÄg\b‹ÏZhß×á—îô[¿›úâ›­Ÿ»È®Õ SÓ·õE_:µ+ò5q?«lY œö6Î(>ì­*×èó<øKâ+·_~ Ì9/fƒxbR\\ÑÔŽy¾©Ñ0£¨[‚[å9L§O~Av?âxáSã—ïéŽ2#>0BpŸYÜ:zxx6åÞYŸ ¦×´sb>¢ê U5¹Ž1º”ƒzôÄEÜWNWÈ{t®÷5“y"V7&\ªDî{ó‡è­$Öï6ÕÞ»0dÝà Ë_ÿ™§,õnp*¼.Õ0<ÞS<éÚúÛ"Ü«GásòkØ=ðß¿w'×gBŠb4°\ÏÕoúóÈûƒ'ê"Ê臅ß'‡€Š:2ó/á-ƒz´WÌÂ×;ßê9ÒÚìÜ“UÓÛ[0×s¿eh5xp”5ü$qPò¼¯œC³ÂˆÙ„uúÞKã%qõYºÂ¤‹>¿Ÿ+7ñgeõMüÁJjù“ o´ï¥eÿÙ£†›n¬y&Ñìqóç0øGpg»>iǬt7‰½Ñž¬š³’>_6£J‡kT.XˆoÑ·œºæCÔT:Môcz y®ªš.¹S¥–Ïú€_R;åYÁÑ©ïÎÒi³ó!Û²àþÐ \Z÷} ;áü#Û[ŸàÿŸP¸CÕýµ;·VžTU‡¬ÂŽ’c4¨˜gpæÐèÍç›S×7tIõîï ë!1xr½È+À R/Ù§w|~.91…ð¥Qù ¹;ØuÁBz3gWÅ= wQÌ‘u4âZÙP¿sÜIüéfôÀ}ö»döÎŒ¸¦ôO¢ÏåOí¿>¨;¨&{‹¶5âì̹‰à1s[ÚePÍ?iç.ð‡Ô¹#Ùc..ÑDM}~Y›û3ÇŒ_éxŸK®(8zÒâ0ùs*—*ÊE+1°°Ü*[€Í_(ªe'~9g@ô‚‰_PñAò;<ªÃ]&ÅÖºC7PÃÊÜ&ôú‚kÔôU6&òpŠY|1¸¹`7òÄD…{ÿM8v–g«Ìb‚¤½Ï¼eúYÏ &NÔZÓW‘5=b-êv7if[ÇQu~‰87ªC 挿–M°ó’úŠ? ÿÌÁ§ZƒÁËçÞ ¡Õ²VKúx0ÄA½¥}ìOžÒÖgcM^Ï=‡Cײ6¨<Íß™¹“ªåý‹gõevbhÎ*ëìw÷>·}Å¡=é}åc^xø]§··ß»ûÿÛ6ÝãÜõ·ÿÛ¹/ÖËwbK"o¹ÆÙI9'~o%îýÜ;gm8gËñKhXÚ1œé)îüÙ?7ˆkaþ‡Ú÷GøŽÇzšÑJ/ºÛ7ÅÐ:¡»ÅŒx/ÚËÊ~)–ÌØo׵Ù¦Y¼.bxa¾µ‹ýŠâ¼œ!©Z÷HLDíw% ô‹´·œƒ±á\[pÈ „±o¿¯é_T †rSÌG&žÎþ_aYÛ¦Ž ^!µ§Ô`-©5’èîÂyCs'c¸Äz—‘µ7ãw¸³ä÷¶ß¬þ¼©…—ù‡¶fp…‹í“ûpñ«ò´Y`ÞÁ{¼„Æ\Rç-ê{ʨ‹ƒâwR› åæ×ø|ñý#ig÷¶wŸ˜ Ýâ jÔæß3”àj/ô¹Ž‡À=£DO~Mñ/yÑÔpªœ}"£IñIíîb%žÕã>ŒôÒRÖ»kÞ¼kMÊ·*9ŸâƒýÀ"Ž#¶ûOœN®LÌØá¡âmŸTx¡ƒjÄôüMø{·Mä¯ûî¼æÅ܇£Ð‡XR€³$ý÷OâdNô µ^ÿ¨eÜžú<¡ œ3'¿à6ê¨õù'÷5>#-Ž!{Aéç½J~˜ûcϾæ=ÿ œÃÞÃÎêp¦ýùÈL?ìLèÅð޾^Òqí¾Uê;xßhr]Lÿ-p:Ξb޵8BSC,„1iïOi~Ç ËV¿Ìö¨Íö;‚æ¼kGçoOS‡®P…›¬;l ö1¿rßpôAÚœCëgô»†+ñ%±>þO¶í ÿ=tBˆcÁ!ñ•ܱ3Ò­ì8}plRÞÅ}¾ªe\Q,zÜõYmŒ/þ×]6-Ÿ߸yùÇxñò¥wþÀòOÿüp÷ÃÎìüÆg<òàÏì¡÷®æƒÅ'Þ¡jY¶§^Ó³¿%ž>±Û:®Ù°’ŽyW3Lô«n¡XëJ‡S†¿OõÝb^o²‡5x÷·KgŽºxùð 8[7ª¯æ O|YðÊbžË*ô!È/j¿0»N¡ÎFÚgÎ2ë?ZïÚb–žƒüãØjKx¬kŸ¢ÿl_¾~ÙizTêÇÊʳáŸ^g§yÍÐus_=[ñ%kpéÖ{º‘½SŠÓ öRç¬t<^øcäÑ1{l˜\>˜8è9Û„¼¶ãöÀNw¡óÁÁs‘ïÔså `Ç ƒ÷üpüwëAÌžEpûô½hÂP‹Õ(ƒxîä%ôKë„8Ë?^uf|šâ<´Pý3¹³àÔèî€ë= ø˜4ã*ymä0¹_Ô\²žªµ§F«¹}áÕS“öü›˜Ôê Ša™o¡ü|‹Ü««'\ã|dŸöa?UñP9£yÈôê÷š­³5ÌÖDót>³pß#Ãåƒç3šÌðIüŒwýÇ`Yðä÷õ\ð…íœO;ÑŸsú"¿ŠZ1)õ½¹ÍÖÃ'¦æ}~ÁUº­PÛ‚¯»ŸõRøÐœòžuÓ{®¢6ˆ²ÖµÁ\ÐŒù”¿P¿‹r€f=~+4oCC··¡ bâ9qlGÙx‰Ê.[˦º»“fƒuñsÍz…AdR˜oQ¯"õ1Åæ¡Ýxƒ=žQ ó 8+¬·ò¼"]??3öBñ ç݆3´¼®h¼£­Ma•Y¿Ô»zÜ®ÜAç÷®œ'w$Í®UÎqº’:¹1SÖµ ¼Ií?ï V[d{¥Ó:SƒøWöÙoÿ”]Ó÷)ô'ãŽiý¶ò£Ë?ûÝû<úÏ^Þù¿ò^±³¿ú1Ûoxןå{¬4kå(fi¨.ö¤õÚW¡±ª8™ší„ž$wb?e–– \nó ø°iº€Y-™qªøvd_éœ`³”sŒâ~“§Mð$èÓ ‡~³àÕ“ÿ[ØUðÿO¸!ùmŠ7åÓn^rŽÅÁÆpƒï»ø¬Ä’ÔDÄs8µÄ®Š· Ôû¼;†6ù¦î”¿‡}úeCöHÒÇsžY‹–ÛiÊpz°eúªàmѧí KêÇÜOÕ8GtiílfÖqά_õ}ó—…3]÷¤ÝGÇêÍ{V\k$œaÉZƒÊæŽGÞ WojܤÔQ›èSÁ^RCk½ì9«l’¯yò5Ùºìý„S4ö:† I|/Ì]q}kM“"êÿg—Ää/sÃÛéÊœdЬ7b¢ŽSPe½Þ¨x¦´5aZpÛàB‚+ÄšENÓp˜ÆMŽø÷:|›Ø8{~xž#ŸŸ˜œ4 TG7¥NJh Ò"œ5°\¾oNNŸãjôA1¬Ê^(' þ¯p¹Iû™±ÆÐiOÂËá½ô;ìÙ¨}ȼŽz{0«§T¸Nû2WJÜâÀîm : 7éEÌ þuÚ&E¼ÉNKîŒV ½Ìª•OàdƒúÚÁL65 [v<ë8Ô;º™K~ÆYV µÀ•ˆ˜0x£›Ñˆ>J¡æ w«ªÉdþE]Y½8Šo•[æD­FxZò»¸c_÷iÙzŠ7RÿänÍû1ÛtHýœÍ=¿öÔ> ¼ r 9fÎdܯ3&ÎlÄåèÞ;„¼Zèñ ^n‚ÃA Zñ#wnìæÆÒ›Ø©gÛß~Í¿t×bÿöš7î¼òkµ»üêo¼üÚ¿ú.ÿösîµû}WâòÉgmÛ¾üËÕ7nŸÄÛßòÅ}ð'|ÒáÛùšíO~ÂÃï’IöÜðþå•×¾ØõMmNà™ì‡ØàLÓ?îÏmwzëíšý'-rû9¼è‚¶³|÷ùûÕ¹"_KÓ|¹¢zºòéãô…ØU[j1âŸÕ¸C^›•:Ï<€}rñfÀɯFÅâ¶oàô-MÜaiÞѶ/^Ä„ÿ´ÿݸޮaîç@=)9‡G6Ü‘øÂë†p¡c¿3†-ÁgsûÀý”NÑùäAŠæ1Ç _ÍLwæÒ)'BC××ÜîMôQlUzHõ¹hûý¡æIþ f²Ê~“°iœñ3êe ¾!—"¿¥9 9›ÔO&ðRÅ´hZR“õ³¿ŠZµÎ¡éÏ ÐíÓ1=÷àCûÔàóïA;hÔY3Éâ|º5ªYŒÊ7S#^M§?3òý­¦èº¬ó®ì¹pR;ìlã7MßMX~ÎW4ûÁÿÆr²†+DÂκê¥Ew1íÃp+zÝÁ×nœ—½¬Ë’ÏÄ>ÇŒViÔ9õ{‚SxQðPT¯¥æ´ãWïï.¾¼cÆvîãßWÇn>CùìRü·…â ±‡ëŽÿ“¸11~Ó>G9‚°Ð•Vý±„ÖÐY´ë=†dnrDZ.Ä̓úñ…éû÷’s 1üÓéÂÅ®×jÒÚ;/‹b^ÿ[åwkõTñ„&æ‰v³È¯³ãÊÍ=.¸‡þïäýl¾_yÿ]¿u0½ñÔáÁ÷Ø1ãöíýÖïX GÏeÿßfYLé9dý/Þæ Ó÷°RŸiIë·‚_ê>øù±œDñ¼âž½Ôÿ ÚådQÏ‚»¸ ÎEmç£i¡ à÷•û½íj‰÷ƒk¯Úü´Q>1ó;ñ:²N »µ¦n¿Õv‚[sô®eÞßÅCž£\¯1åžõÀà#%gØcmû|õLM²ËUr'oöØþÚš÷>„&Ce½…=ì“wè½øJÕiüfqä[]âNê›ùsÕ4‰s³Æ¦÷N>•âª:'_*tpÀwé Šÿà÷†˜F6ҹŚËLÈ:cT~]é…cN#‹v•¯÷MgÍÏJħçr&OÏ/\…–ÆÛËóƒíc«mmíÿ«×\\™Ô,uû±Ò¬Så+òQðóR#ο½£8`n+9°îDѹª]nµP<2ªŽ—ñ q¯ÖvŸ<]çEùI›g¡xN øÒBuyñìb;8ò :<þ,ÿ¼<;|ð kÎáÝš EÊ žBa‰•úò¦fN+NJ>/Ÿ?ÄÌá‘w †¤ÞÁª¾nÕ)³'…”*Z9饉[5²24^\✠ƒßÓ÷^)œÑ.¯Ù—#&÷|þVCsÔ®gq¥õl¬í’ú0|rwìõiú°¯öÓ®¤²jsKƒìin =ÑÑWÉwÛ÷Úßk_©ƒ»oyjœ´®½OùÌQã »ý0N]ô£dŸb±ÌqrFÍZ=þ]–‡â§¨(G£§šçôw V³ÏÛ¸ƒhBø&˜Û|ûüˆ޳ß_uÎÔq;“mÍØ .ŠtetFs>bÆ@š]ž0é^Âi•§MФy zä^ÃwA§J¸+3ÑÀç‰õüódŠâ0‰ [«\EqËùìak¶+òOj®Í÷7Nµø‘µh66rýž7wòwËUÓ„Æ&f½:j¨[¥Åøç‰Ïú<˜<Þ?‡§ÞªÜŸøµ’ƒ5=rz+ý<» Ó3z,¤ø `sáçÍ©E¼‘½ò7Ät©½-ßE j\ÌôƒÂ!jïƒÃs%±aÙy÷gp,gÕܕ㹟³?£¹)Q¾ ?Pµ6Ô¬A« í‰Ë¶‡öôˆ-QžÃN`<⃌ýs >¯íŠxág >þ&p»ðEr‰‰U‹ƒ?’qšÎ±çðÄÒòž'„¶OèÐc‘›á‚\°`ùhÿ.ÙîØ ï,Ü‚š9±}‹¾¯QÝÊXAûÇ[›½ýƒx÷œiq'’“«Ä‘˜…‡Z,¸=ËS5²a‹®áçíýÌÌ?_º2ÔN+ù 5Æ3êáÖ0ît›Û°.†ÏZíÐjÊþ÷wk†0µ'a´1Å‘xÿ›Ñ+X:·^9×EåªÇÂÏWò%r†¡ñaÁ) Ϧ»œ½äððè×&¥¿“9Kª0;¸@_¡‹ *ó¥áÝcê¹Ì†¬ù=ÅŽ`CÊ_+¸upZÅØ;4$¯a¹¦Š¿ÆÌÙÝtƒåç3žR ¼ÂÅP®â{$Mj#èÊÊžå¼~NŸtw—£—…þÕ£¦Y&â®Â=Òß êm$–6¸ þ) ©à³•7JSãXë1†ðúî\Q‡ lAøó’þbãÔô1—âý%<“¸‡8OèÝq.'Mx ôuøïtö¡èl£ó2¡…ÐúòCË¿¡‡Ýf}ecɧœ'‡¿Ñ\õ¬YÒHáÅFÙß‹Ö ù½Yê§_F¼ëX9ZȪGmx „˜W¹8˜?X‰øk0W¹‹ù&õËt=é_…íLð&é§þ.>ߥ©³…UÚEKö[6 \MµÏóâ¿Äbœ¿MͺÔ»ÕéÙ¨ßu£²îª›Uáƒé à/Æ9ˆ>u%[Óà&†¦¼,j–#À¯Sü]Ô“_‡þfÙÆèÁ‚Ëf9°îSÎ Ä®Úzœ÷í¾îžëåǾx¹3ùwŸÿg»_õÒW\~ÔÿøîÝ>üw>çÏ¿`Ç>û)¯¾qÛ¸ùÝ_ÜþôÓ_xøà7wóÂûm?óÉÿ´¦§Åb¿zèËÖïÝýÅ%qâ*ôk«bô%5=ó¹ôÝ®¾îYúLªd¼Ì¼ï¨#_·\i†²x,^{÷D^5¨.£<œ™hY°.‰J#ÙmEÌPʺ®ûËYˆµOòg·=Ì<ÒLŠ9¸%j–W’×ßUXýÍú¦œãzžðˆ”Õ¨­D>MO‘¸ë8 aÅYÉžuõH{œJ¬Dÿw šz©ð¾rçCž¼;çÜaêû•üyp¼'ûNëpkÎFÀ.‰ÛÁÝ…ïúcörDZ^‚­©þ¶æûu¿áÍû Ðg?ûœV¹oÅ(6)Ê×À!«b[0 Qñz¤©é:·9£îoöé·¼øRâÁGÑ/¶VQöbØïš=ØLý\ïË«è3Düþ¤åMÒíã{µŸ…xnñ(ý/Ñc–½Ê•|eP/ë‡Æb³ú?dÙÇ>®È>z0bz‹ìsš¾î%âæ"Gï¢û Å4 åžê=¯øüMhŽø rÅ=U{š9X«åÇìáUª9ùYf_}eÆY<)úï–~7ýeò|)-»:O¾œ>–ºc—c»·ú»ìõs¿ð,¯’¶”b Ô‘pž¯êÄ5p¨›—ÒVª³f‡GÿùmЇï…˜Àž>8ù3j#U3¬+8,ç ìMdú(gÍpÑù¦_^XOh%Ë–AµHÅ—+k2žWÝ} ï—x˜ìïѪTíLxäyŬQSìêIãfêÄmTú©±€/c?‰{ŽýϺ_…sqgÔììÙ™37´þûb÷Cö­€‘£H•ýµ|½G‡ç_tÞ†ñ†}ƒûÊ| ´ÁXŹòølÈþÊc8B fƒ‡ÏöûŸpÓ៼muøô³¯:X¿àa;ËoÿÉÃwßòåÛ'ŸãÏþËëןïÀ0È«÷Üþ®ÓÛ9¤îÈ84'ðbOÝ=°ö ßÏÞMöu‰ Ú”Ž=›ùsá¯Ôì ñ‹bù¬yAÌùSß3uG¿WôÃÈúÏÑ€]‰Ÿ\ÿÈ-ì™›aß'ðŠÙuAïD¿?k íŽv žß¢\ªÂÁ¿«NÊ=©ƒø[›ÒßnŒžÓY§³¿5œ]¹fæ€Ô'ì½ãçÑ÷ƒœDõ=æ\å;…~ð9t°"v FñÖ\û .…8˜=–A=BÁJ \Ö¾t÷Vü¼ã‘~—®_ÞíQÔ—²NÞîSoaÖȼ³s_0ÜÊ\ìÈ7£Ç¡hïs-ØBà; SØo¸Öyê¨àôTöIñgåNÒóÙñGFt&àÛmvš†:Ü1âÿÆO¸î.ót93¼÷ó¸,ùƒ¸HU9Ai8Ö³»ðc ðz홨A­rÎzhS»PÍ«všµ\swL|˜µrÃ̉‡õ»þŒÂH}­ßóS3ë(ûò£¿W¼í Þ!ž1 ZùÜíÜõò™Ý¥V ¦x¸'sí+í=:zûêéòøÏΦæë®áË«†àõ i×Ò¿a"F"·NXÁxàúƒEØz¨žègT6*ùƒcѧÖ]¼Ï3|èLÈ‘ïv¡¹¾ç±i#˜½l¿çâ×dîÍϨ™lª··‹“8ƒÉuQLS±çæ[…µ¦.:®Cð[ÖäèhéR;nµˆkôc§Ï:]hé8éY"Þ¥žh¿ÇìAÅ†É Ã?¨æD -1R­'˜UúCjLhaß—VÕ1À8×`ýp8ìÓþÍ]aÁ#È_/Ô3š£>H+ÀbÓGÖsgïõöǼäWw.Æ«_²ûço}íî^÷È7íýÇ[vßô²WíÜx]Û‡¿|ß»^~Ÿ/عôÔoÚùâ¼vøŒk¾ó¬¿|ôÎIÌp ØÓ|øÚâ„g>ùßn_zç8`½„èÅÛ=0,Þfµ¹Ø®±ßµüfÖ\ˆA<¯¡ë¹°µ—Í\ˆëS©¯ÏÒðQ­¯vöÜu¤çľ g§.7éÜJ3>ê¸sêל—m{NréÀ‘Åq£¾ÐI¶Û… ÞWœáµ&Ãh¨gªn™œýYú¹:7àÚÉwÄg‰û¼•ù±}>xlÄQÃ'V°¿ÇŸbű/`ΆÈÖ$×;îhp^ ë¾Ý籜£æ1iÎuEW€÷ÛÝl½D‹V³Œ¿#îÄ"6\©îjÏAÏÆÊµNN]§q%\®@jhÒÓ;KËNø€ÅKä­‹D“^ò!å}ÂK“³•ú•äràÓÂpèÛÆÞúñ ›ÉïŒ9à!~À³×:_ð<3×>8Ä‹ÃÀŸH£Ø”^ì ,ÀÖ‹™½öœš÷•yî¦f ·ˆ¹ÿ„ÁÈÞ*Ί>MùIÿÜv¦˜ß5rø§¬!¸7é‘ô(ÉÃÈÁ‡¦ FO…Mþ^²Ã׳oÌY{ooœ³'õœxíÍ7OÌ×ìïµ}ó ·,ábQKÑÙôïįt¸ÊDí 2ø²í/ü4fC`gˆÃ7Õˤ˜MÏŒeÈÃÙç/‰exΡÕ)Æà¼g1]T[·sßÞ0ht¦À[|~e̯›]#È9Kx^ÔmíÏâZæõèI€Òc¨ž ž)ó0°­©ÛRø{ÄCë}+šùœõJü0qCÌÜ.HhÔÃkñ{'ÐÎáÃgÔ“¢Ÿƒ3zÌ«zèD­'rÏSK4]…#øžª7Ók*Ì+ƒ—¾ìZb产è’x¬À̾›øQ6³Æ¬Þ½ò—æ¡=Çÿø§n½ì;þø?Xo<ëÿlï>rçðß|÷ ·Ï}Ú{˜md{z`û”W?où¸þüš³mk®ò4ûìNó¸ñ½Êçò¼àcÄ׊ìçšÐ"iÜþ蟿³l¾!gÀÈŸ\=¦Í®OÄxÄ–»1™ôº”‹÷Œˆ™›©­ßbUᛂ-ÿÐxÎùµ9ûÍ7ÖèÜÎWT=G9ãK~·ÂýýXCxBä<‹ìñˆÆ|âzáûô)§.Å‘æ*«¶S:_ÕÞçè)u­X ~’m—ÌÜ„6ü¹ü¾0{àà7ªn<’+7›úÚê?¢î?rÆÉ«-öÙLýÇcr¦qP8ý C7[X¸”çç\ÜCÿ×j°w¥n}^§¥×»W»ùíYk•=cŽìö5åWÈ1«fWr j†ú †ALaïeÚ vn¥íUáÆÒ_5Ç\ò©_ÿ!û™¿ãõoŸmMìcŸ)n¤ð‹ÊUß8통>ëC¸þÑSBL,Ž¢ë×mŠžÑáoûâJL-çÚC모Ε±“âè*LÚï§ùfýŽ×)8'›½¥úÀ%Õ/‚OÆ%_½Ì²£r4æÓÑ#-W¨±kþtÅG.ÌU\‰—N 3HѺuñ£Nî¿zñ&0“•ôÕÀoÐ'E AÏS8¿`4ºW¶Sñ[%—Q¾12ø gƒ{ŠæVØÖ[ÖÜeáài›mŸÔ›ãy<˜›ö`Zo6Ÿú»ý㱌ý\çq ï!üÇ kÎãÍ ‹…Û¢ý'¦+Ò“›ØGùË}âp0lãJ3눽Ÿ·žfbÁ•:Ò< pòA³«é‡Õúg]eˆ¼õÿûw¾úÖßü/Û/ÿ݇~ÃËíðÏû”Ýs?øQ‡|ö8<ÉÕ-¿w¿'¾ãíÞ¼ÿ®ßÚ>y–õ©×ÊAØÀ+™#)g­ª%š¾ õ¯Q¾Ùc;[¬>q¥þê4ƒt‰ÛÈÈaçÿKOu‹š€æƒžS/hã¶âSáƒ[ò}â¬_I‡‘ºQÃÓÞìõ×fçs©sè°è‰¿­»y­rhçK`¢Þ)sæˆkoÐ|8ŸqX[oã5æ]hÆ53Q©]¢ƒàØ"v@ºµçÑ¥eörúqj ¹_Ѻqp³U<7_#ú?U¨ø-aµÂ!nïN\S¼—eàŒÑ‹íÃv£Ñq|+õ-ðz­÷~Ÿ¼u%N :Oô2sG8wZƒÚzSèûÛÂof Ÿ:휺»ÞWÈ]v;J.Œ?g£Ã–#Û]…v]å¿ñÀ³lzµàcäÐþØŸ|)5©;7nÈúPô`ÃUÇ.‡À>Q#ÏÒ<›À8•¿ÉÞžkïü2úÇ7tþ¯ê=Ω‡&baöËbFñœ¤õÜö—>lú<ÕsÚ<ϨÓEÏsUWÑ]ÌAêX¬‰ìäz«õ¨µ~·3š³Çn0žîhrðѰÒÚ.Á,Áú¨¯î¾µ„›NMžœFtì~ÀÛÕþœÃ9tÏÁEéÕ/ø|Ù£}Í"%~'ëF R£SÞª8ýõìÙ¾0{\[B½@œ°Eð²~=öX¾¡[_rÂ}ù‘¾¸vXmö– ª™H3AvÂû²6´<À59"õ+Ë+[Wê¬`8ö¬6çœjS¼4æDMçÏyºÚdÚ)õ)Âù®-·9&Wð;m{û“û°hµ¨ Íd ^<¾éŒ÷œ¢'O¹ÕGçà}îW?â¬ÍíùÊÛ^·ûþGŒg¿÷+ÆÝ¯yÖÿ³óÇ?þãÛOûî/Þý«ßø;¶oÿøÅ_³sóïý­ÍÚ>Ù£mÛ/ÝûàöñÞ`ïÿSÏøèƒwßòåpí,7“_ev¬Ù­ä§ªº¶©Y0~Ïé¿Ô£Ë\LjbŠ-R¿Ôøêç4zÀѱÕy™ÈAÅ£™ÈeïðéQ;9¥1gKçmÔº]g}WÎÆKݯ3Mç}’²7b;uî}?Èìùèçëð½ºÙ4¨ƒÜüìÖš{mŸÅ|]ú Ô×Ö³Ö¨-Qw¶çƒ:DmÚ}ßÐÍ,`¾¡p·OÂ|?„H74æ´µÚý†Ïw#¯ƒ¿8ÙKÖèi³Ö©ó¿¤­ûJý-äuËM7’³3gYútö {Ëqÿ¸{f À©‡ànä{€+o ë§Ž;Uæì•;÷ŸüÚƸßÀ>ßò†!ëùç•¿n$VËüN|¦p§Ô1€Àþ0ËOvt“m>üRö1·˜–÷‰Þ߬å(îõs–›º™$Ÿ/3û5³W²ëï+ÚO?¿Ï²AÞ»–q“æ7ÓÓs:¨NxwÓg'&*³z‹VtU‡N£šº¾LN5¿7'ÿ81ÄYaýNŸÀcmÞ ?§ø.k6:©åÀ^è™eS.ªµOÅî™c‘—€{ØÿWïEæ‚¶_¬[pÚì,êCôóEm/ÖjÕéÐ7Kíih|“´‹â®¬[Ýï˜~°0pPxgûðòà/wœQz•«oÉbG4:W­×ÖclûN»?âWÔ>B#Œ{ÓSk.Ì_À>yAΨïr’cf%<ˆËE>7°ã÷M¡!áσN8÷a„³®3çýêü¹ý5»,ß=q?ߪY ʳ}_¨•)Þœ„£±ÿ¹öÌ›‡a¾Öl÷ þî-^þúó–ÃK÷ÿ”Ë_}ÛoجŸ÷ßõEï{ûÃOìó=Ó3ŸüoN¾ûàÊk_œ¸¿õÆóD]Hö„\Äì~W>ã¸vgdA½S>q¿³½£ÖUµÊãäÆ(þwÊõÆ<ëúR7ıÄnJçE÷pPלSáçÄcö¿—†®’ÿ¹Ä÷LFUw ï‹'N¾Ùóå°ÝãÝ9ƒ·çóù¡c6¥×Ó?«uð·8 þ.ô…p.¸cvçPéº9‡7¥Öpô·HãbšÕó<¤ øìiùõ=r…I6˜»]dǪðk°=ç­Z~ǽTb,~#öV^”úvÚg´0ˆUäÓ©KÇŒGž ¦{TÐñ‘¹ Ýkf¥fNŽA †Ôj${•œÆ>Ïì>þäŸ5µëÞïk-*¸¼îÐyÏjžµÃôœjÄôºÿ ÕT 9¸Ù“jUêÏØnšó ÖÄ?äpö½ösÅ[•œRg.ù㊧’c_»«5ðý·‰üE8̨úJ]%Ÿ„šKð˜‘ß¾Oó\*u`æ·æ$ô;€‰£GL ©qq‚@,Šn|Zð0/zT«Ä®Tj"áç/¤†=—ƒ´˜Œcdç!18<Éà x eâN«ÆG*çL ªÝ·(Ö´¿ÓL…K}ßÑ’ïR œø½ÎùHÑŒª‚ 5loïÄœGå²h4Rc+â–¬'Wu%]KÙjìÞˆ/¢¡˜šúÿ„Ƈb[ßíz¤¹y³¸â7Iß¾K:™köDÑRçÈóNé–¢9ëþAú¶©[ÅÝ%g~ç]~ ëxMÿï6ÙÆÔ]ľ^ÒþF}3ê8‘‡ƒ Ò#¤Þ+ç¶¿êÌ}LÃ÷$·|ÊÁôÝ÷<üܳ9|÷}þãŽÅ¶ßñÆYÍÿ$ß~iÿ­ëïüöúYùèíñ‡ÿý=S㣧=¦î4Q¢Çß²ÐÜ_tBÜÏè‹wís©Eú»³oÕœû;«EŠ“6s 㑎nÌ=PÏ'=©+B\|€&ÿŸqÕÿ¿ÅRÆù—ŽÓñ@âÙI³öÁŽÀQçÔ¥Ú žD&½3^‡W6?¥rà*»Ž8'´<aÓŠ+ì÷…‰¯'°µ‹i¯ÅA”g*ä“¶&ªMªSÞðû`ãpǰéÊir¾ûO.ÉÌpba|¬bk·QšS½ÖóVÕ1œ3œi«Á>‰5Ù§ üÔŸ_¶¬Ž?µÎ58xu–.×íÒvÙT¯¥òÇäF½òæµj¡•œNv¼F/Ï-ëîL“óð¤èm–ÿÞ׳ì}ÿà_z˜1¹=+ñºuè¡ÏbêíŠÖ¶„Žhè-k–™çþKø½¬ôj½ýRÓ>dy]Õ,ÔŠ¶”⣺jzB\ÞöLõõ,~Ð×&Õ¤„¡Ó³ë>™8–¿U­§‹£oµó·cÃgcPl‡ Ÿ“ÜQp ð-ÏÑe;Èù'åÈ…þðÙ§Þ§÷ÓÖ~>êóäðL;ûQáÓrg•Vü(<Ï#Í ÞÀYÇY{üÝû¼÷}Ö÷­é5Å¿Ã;ƒ?1«—Ò~Ÿú"w—X\œhò³ìS16užX2}²b´‚>¸Pwë¦zÈqVêé?†ºWæ~䂊ÃëÒ¾”¶Ÿ®™N. ·¢ð~Âð u0ݹ Ÿ@nND-ÖÞŒyn³ ¸°òXκÛ&ãÌÒ4ULU©+Q¿¢x$­ Å#U5³"—3!˜çÞa˜nvyèµ²S¯åI,ðCË—.~zùÅ?þðƒoÿûËÛæÛ‰‘6s.Ûi°iÄœÏyT|ŸlØ÷‚<Œ˜ ü -‹Aõ±9¸œ¹‡­Ž×°2ò>Û3Óe8êt ÁøÍNØ3üÕC_¶l1ΆA#g4ãÐzÖ÷±›äÁÒåçœjM#v“ÞE‰º’s‚|}íN‹›îßÉÏutÞáiìË6ç<`zäõ¥‰+kØšÞÉŽá6ŒkâVåÀ÷üŠ-> ­ëÚ´B—̄ڞü½¯멜ÿåzˆÄàîàfÜMá™/ÎÅ êMiÉ 1K7æ;®4[H±r_g$÷&®e«}ÿÀ{ôà»(n‚öþf´ÑÑLÉøa_°ã0-ée¿P-AÁ¤Ü° Ë$>õ 4‹³v¢|«P léîC¿¹ª>8ÎÙ'¶WáÈPC#.R}¦ˆ{“9`ßË¥ó72× ÍÏMéÁÅΡƒO,¾Ï÷Îâ´‹GF,MŽI,Z»x³‚3w%¾Cq}¤)'<‰U^o%>oÏøúDŠúŲžygÌQo^j\À‘ ,=jè?G¼~<‘Èÿ9¿“¹NC«£8ß^ù¿ëKÓkÓjþÑ FŒ¦ÞŽÞ8ãâ V2òhrhÕ“¹ 8cøð´9•8^v‰oÂ~û™¿†9 ý&íG(¸!·éÂ.ÀFÀà8ßCèyªæŽfYôk©;껯a/Â秉žt[ÈÍíïà€a“Ùs¢ 6‹“¦Üº€ÑÒ·ª˜Ù?='jýÊyˆ¿ uô¢ZmÀ߉»ŽfVbJØÐAµÅP…z'ïÅ=Q]1ùmⲤO·ÜFDÖ¯Ž4§ý&nýÃ?üÕ]{ž[ßòi»ý…ÿq÷¥ßòÜÝ?ÿ­Gí¾ñ^tøOø™Ýzê÷[Í¿|Îg¼cûißö­;Ÿç§íÜõ†÷l[}ඇÕŽõ¬¼ñ)kÓ2½€M×´8wrî~ßþ?Ú‰¾f›Ý/o8ôxuîÑÛ&V^'‚Ãâ6Wöwb.SÜ%æ9G. vj‰Ž 8\É£˜Y°Åw­Ú¬Œeô.4 jSÄ‹àÉàh²Y“´Òƒì~æ>Wº~…ØdN~î} ËnvæçŽ\ýöЧ_Š«0‚cçÍÙ]ÔØ:Žm }Gf’ne\ žaß+~$ú‰ k[Úýfeç§V£¸æ8!ÜõÈ;Cz¦½<›š%ùñ ô‚¢ÇXúù¹ãæ Ï)*qn¸mp‘åo2Ä~ÓÖág韵öÂÑC¤»OýœÃ¹RàºäløR8ŽØe|°·‚ë£YŽ^×°ï„,»Fî4â˜wºÌG÷Çí˜õÜ\ooBsÀ>Ïž›YSp©´¸ýûƒcÐ3\è!ѳåLVqg—ÒnI?ž3DòÎk=¿ìÏ&g^rzùS_÷ˆ×‚G¬^á±ÝÅsÊÇ/Ò³ÁeìʳÓÿNGâ4TØ/ž_ÌÑ@ó¶Ë#ʦfÈêÙ¤Ýs: §<üñ?´}ü¼Où—‡?ú¢;Ÿ~öþ‡'ŸešÀËÿøÜ×îýÖ'ú<Óþ1\àägëWù!ÇôðcO:ÐàççE, T½®ØŒÃ5»J¾zƒ¾ÅE~Ç*y)|\|vW÷…<`ŒZÆßI¿ï<¼³%8 Ö¦2ûA±¡îÒµ‰ó%œÔswrjaehõ3ôç—{ä>»#OaG1ë‡ä[ø•;šŽÝ´)]#ìyäQèb§”pû|$M—x¾çPMßGE÷qTœž>‚¸2žù¸hÎñ„]§¯½é)‡»D/µáÁg¸³5zo¯”†³o€±UÅàKUçˆøGyCêþUú‘‡¨áà—§§æ|OúIöà¤Ñ_U„1&·BïNo†&ýL`*«Ô® }ÌôLÊ1÷Ty3Õ6x–xý+ÔØç¦_¹$’ †$ Á}ÿ¢ÅgW•£_šÔÏOn¯ø ò*aÐpÑ“+~v+{gõï“ë*‡ç=’o§¾¨Jü)üzÄwÙýJ–ÏQçFR¸¥¿?5EÙ–µüz×DZžú“ÓöGÞuU÷ç:}¬ÌÁíÌS×m±_ÎcYÐ;H›ºaÔÉ®€ #•ÍÆ©'æÈÞtð\øYʱªÏì=ýÇ:»»1hî†êü…µTŽëï*npÅQZÿ|jS·ÿ¼r•àâ5 €iH­ÉJ,±Roϳ#|_8÷vư³âP¬á¼Î¡_‚žxƸð#vzRj°Á£¾"ÌyIO«CÆllÝ“‘†à¥=©hÇo6 LEÓVòz¼Yûõv¤f\WÙtŽàÖƒyT>×¾Gýï•ØVqœ÷-Ƽ«Ç3«ž3ç‚PÛR¬[eS©'”Âûtµðëú3gÕÚì÷ïó¦Ï¼lïuç _tøèO:sù ïúæŸÿÃÆõ[ßõ†o=ü²[þÌ|þø—æÁ+_óK'wüNÜ3®}ø>w©ÚæÌœÃ|¨b(ÇÏà1ÒûW-ô¹ÍY-_^Ñû„m¦ž«xÔß‘xVçî.šÿ=×®À£±¿‡ ©uô{wÂî?\-øƒ`¦ìÿ Ý’›š†¦òĦ½ÎïYÞ«ºþqIžw÷fx¶5â¿[–ŠEøâIp|ø)ú¾©ýïôœÏYµ1ùœ}æMÒû¤Þø ûäðˆ©ÕÁ—R½£túáðÝ”OºÍWÌqL½xLkÐÜíÆ#‡ÿ·‘óÿ?öî>Ê*küx¨!„B½[A„¡d&€eNE¥HQÀ‚‚ 4i*¬½ «® =3 ö,X»RE±WDýÏóÜsžuwßý¿ïîºïÿÿû~>w³2)Sžrï¹çžëâï Kõšå6Þu±æ(é8«Ôb`ÞÏyóÖåš“e룼ǼcVïþ8Ôâ±Þç¦ñ³RíÓǬþ;§÷ZÑâÃyV·Ðrw,ÂÎqï5èžQqw½K/µóÏòëmO ›ÿ±sYÇà~_Ê»nj ó¸å¼GW‹QïŸ6çà¦ÇÔ>C$¹¾µsïfýðÔ:‡vŒ„‚ØoVØòíø%׋ù÷Sëóh®²?þIÙ?/bñ<—ÇÖ°Tû)Á>Þïе‘‘dý:wüÚüˆ® —{'¸½ãgë¾ß©1ôrkÔøS¤µÖƒK9'ƒ3‹ãYŒDccAýJÍm°uSÖw Û9«±ŽxyÊz`ÍGìXÓx@\Ï«ËnóÌñbÝ÷Ñ~FcXaËm¶×Å-Îdó¨ÞkÖúäAßÖ}6.æâÛº_„Ý?ã6Çíý›æÿ§öt¼äçnû54¦'÷éû’¯õ½üß©k ‚yÍ;Í·¿©ý {n1‹lIÖÙ˳<Þò ®ˆ[¿ëòêÜœ†Õ0µ¾ŠöñJÝë[Ü×,~¢s8~.‹­Ò~i8Ìgµ²µ0Áܼ#÷‰éÏÅuÌe×QÿX/×zq·ÒóÏõ§mÍ­®9‰ÛÞû‰hßÖLïƒù6Öø[‰»ÿøóÇ^ì4_óbû³~µv­² #ëgåÆÖoñ^_^½ ]½÷aÖÒ•]íÉèæíàõ·¼=ÝßZ×-º·¡ÖàÉ·X®÷Yº~_+«³{A±î#«s’W‡aMÐ?Òñ\p¯öŽŸ-º‡iq°/Õ‚`îP¯ŸqË-°9ÂrÍÃÖ¾]^2Öëç¯iMˆ½Á\’wýrïŸ[Gâ½Þý$9èjZØ\±÷Ùyç–å?Ù<µví·5ÞÉûc–­×¶>½Æ6“ëˆõ˜‹Ùý$ÄGÝXÞë¤ÜßuŽ9ú:ϱ±‰œ[>¤ÝÛ­O£ñ²Ô¼a]ïêêÛº\ëØÜ¥÷ù¥\Û‚\ríãÛ5Óê¦Ùz¼°¥,†bãÃæii>kp_rù´~ÿÝæ4m^&nkXÊ]í—àþ¦×f?F‘²Ïµõ[ò,öa¹!Íëµ¹½Æ†5nfsþë°¾´öe#z\Ålí¾Æÿüû…­)ÕXa©Æ4-f³÷Ю!å.¿(nãÍCÖ•¤îAîýNW/É‹E¬ÿcÛ͉¸\HÛó¢<¥°ÎÓ†u^4b×ÝP‘íGk}^6Wac ûo]׿bézÎüêyX<ÊûûzM‹h|ÂúØÁžŒ#/×zÎtóÖ.Fû66ÿ³óWcøÁ5OÇpö·üëå/è8Êöp°k¾å‡m¼¬ý‚Rg霚Å-‚\—;7$·êu'n} ;>ì~ª÷>ÿ±µ-ÉX’ÛÒŽ5íõfB®fa\×{Zm ë/ÆCºP÷}÷ÏëbW9nãtë[Úç¦ñÛãGcöîÜMÍ›KÍÐ×·ë›å&ã…6GæÖ¹¸8ðq»ÏhÜ)¸îÙõÃbNöz´OdÇ‹]ŸJ4öcóÁq‹ hÿ*˜ŸµÏ2%öK¹~ý [;®÷½ˆÝw½÷ÙbCš“¶±™÷7laÇ—õM4¾ç‡zï êö[Z÷޵<ÇÍݰû[Ø>{‹ùXýN­]·= í}Ö±³î'ºWÇ©­lŸ—¼ÔšŸÓç«÷O7f´~šËóë³Äçïúº»÷n\qb÷C¾ÞÙ½O•±ë ®ÙííØuQÇÙëkì¼¥›÷®lq ë[Ï\ÕíéÅÙëÚ†ÿìíÿÀ£-Öyk½÷Lï£e^Åëj¬<ß‹í¸8ÑÓ»æ¥ælXLÞ;f¼óÑÖ9¸ú*{#_´¾šwí[Gl¨³V ÞÿŒm]ýÜü{£Õ ÖÏÇCè¹f×';ùý\·öË…RÖ¨»Xx«ü”1qph|-¢¹û‘®½Kä%¯ÅOÛ¼F°ÿ‘Æ#5Þîbi6gïÍ«ëyŸ¯×§˜ë ùuòýZŒš¿®ëfcr»‡jlÊê^kýÄ9Áu'¤sÃ޽ϋEÌÖzÒåÁ\¢MŒ¸\ý¼l½ßæ¹=–èÜlçR›»¶ØYÊ~VþÏ{cí7…mþÀúÞ½Õ>G‹ã¦¬åðÏ]ïïÛßÖÏ"¢÷ß|=Çm.;¸‡oIîÑì? ïyÜæ$õý [­áÎki=¦5Š‚¼ãbW;Øjn÷¾®guû@õJYs¬'÷ÿŽÅ 4Àr­ƒ¾Å‡ôس¹´¸W›+õ××Ìkh-ê°Å¿Ê5Èû÷äxÑßóÍrcüëV²®¦Ë5Õu]ALGc±Aîî!gù©Öµ¹Ì˜Õ°¼_;Fl—ë´/·ÖÚg Þ›PQ°'U¾¯:ÆŒÛk i~‘s-Æe±½çD옳>Í›êÚ=Ö‡èÜj–î¹:Dû—{ƒzŽ!͵Mæõº}l>Ãb·ÉØ»ë—hž{Ð'°1}2Þîß‚úf¡`ºsê¹êŸÇ¶ßåÂYîw|Ûñbkû¶ø{ƒ»šMZ 6žrÎÚ:`›ƒ°u„6®Öò…’{ ¤æZ~Q¾ÎoÇlͤޗ½çoû´üzŽÒåw¦Ô®Š¥¾‡z~Çô\´ùšËóÔñ_\Ç'‹}ÛøK²®yÜrïrý:].÷À{M–î®ÇnM_òÚäòè-'\Ǿ‘P2/Òâ,:Fôc7%V+Üêsص(9Îrëd4WÁÖVx¿³Ôòõ¼×¢µ^ì^³ûé·J;mçŸÅÞ¬¿lc »m¬qÍ:ïýÞwò¥ë¾›ôÄ:o¿ÛØ5q_Ïo>ý‚nñç÷êûDö–UðâþekÞº<ßåÌ­‰,˜Ûº«Þëìó ú¼›·z¬11¹PÊpºN,X[aûX~¹å&k^s?×ø9)6æÒcAûæ.>kç…ÎÉù/6·~†æˆú'ûk,ÖeëSÃ6_Ò5%›+×õ_)5¦,?:nkÜxâ_…õ>l¿#ßê¼Ù8öÝ`“ུ1\ÜjWé5ÍâÒAmÛrW77b÷"MçÎÕæ§Ô‹Ye2våæÜB)óv¾hÞ³Åã;°÷Ø ûù¹ƒn­Ÿ_+=Xû^î¯cÜ«}"?Þä¶hîOÜõw]mS›GÔñ]püZ|¥Xó‡ÊSjËYÌ:Tdû%ëY>d®Ö1ÔÚÄ6OäG®ëÖ8Z= [äÞ7·6ÅöZ´{²‹!6 j‡Û}Éõ[±]×§³ZŒA]rÍáôëêùh!·ïnÜæ½sM÷ÕX‰«“X®k€f»Ú¥Þãù–ó©×˜°}V.W¼­í»¥}×g±ó95þd¹Ö6®µü÷ÚÖÑj¬Ô§yç¦æÇj?5¹6¹Ø­ñ÷û€C êÅê5Ëò€l¼è¿ÿ–Ól}{»j^¶om ]DÏçxrÝoVÄò¬dc›G´8ŽÆWc.“Äq윳Ú›ÕÏQçv¬ˆ«1þÛ\q7·43xs“5*lþÐêù×=›Ó³Ø¶÷þÙþ!úz‚žW´{«Æ­"¶NÍj?{ïOòµw¶yí°]¿u//›ûŒéu7ßÎC}Ïíóðß+y'ë{êñiã°åÿXÜÑŽ5»ŽY½_ÇBɺdþ5ÌÖ}ëý¿$e¼×y%{}¶ž8lç¾þî¸åÛyauFì¿õ=³½ -Ö³÷^ósò­ ±›ÛŽèó ëûjÇRØâŠ+ȳ¿¥±>ÛO:lÇtyPoÄM-&`õõ>o×ÿoÚ|ž›vþ¥Î;{ß_jûO?x]wïß'ÜÜ´ûŒÐŠîÞý˺.¹jÝ‚“3»÷þ ë¥ôöÿÌÜø|×èªþݼ@¯îïí‡~–ߪæùeSæg”¹ók¯_PǹË!Öç±±«Ål­~±›—õÇkÁú‹Ojž^ë£× ÛÛÂ-V3y­uãDíÇ­?oÇBÊ¸ÒæZ¯Ð9 ÿóѱc\ã$–‡/×ý˜ll©ûWØ5Aºíe§¹`A-h½÷ù»:?aãHÿ\÷Žq¯hñF]Ô‘ÊõëºûùV¿Þæ¡"¶ÞÜòYtþ<ˆYX\!äÏé?Ì›Ûz7/·ÀrKcö~Z|µ8¹‡nÄöÊõþ[Çóþ}N×pZ ÀîÿA|OÇ;þßsçÚ‚`Ü¡Ï!X'æâv ¬†JØÆ†£Ñ¾M¾å0é±ay¶þgâÝ+íùèØ.ˆèúîˆ?,¿ÑûL’18·Ÿi(eþDó™Ã:IyÃn®ûöˆÅØìþd×ríú×<Û›È^¯þ¬óv_·þLX÷*²þ­í‹Ù\{¨(Ø#.lã ë/XÞ¡ÅÚ“µký½–ƒµ vžxcû|BÛ¶Z“š ×~“ÿúÜÚlwßÑqh£îÆõC–—ª÷¯ˆÝ/“çÄLÿsÑþeØþ–7Alâûlí¯æÈº¹/_{9á–ãdñ‹Szÿîöàv×WÛ[FûnÁú½÷zûì½÷GsMƒ|^‹SÚšQíßýwù÷­b] ÷†¸Öìñ¯iÖßÌ ò;ýü«¹¶9V‹ †\M܈ŽÕu¼™Ä6Bš/bñSËið®/6/mµy56çç¸u[Cüû­wÎØºUWó¾WÄâ™!]G«ûû×l½êž®¦¡Öh‹„tžÖγrÝSÅâÉ8ŒËm±ñ³«mõl:ý}Ë1ßâr°ƒñ¾^gÃV›:7Y3X«×d;m´=¶ãÃë'çj=LË?Iνϱ>sLá`þÝ®Ïv=´¹”Œ˜îaqcH·žÓê0äúõ öÚý0ØóÌbºšãâÿ}­‡©Ÿ³['¯×ò¸Å½tî£Dk“ûý•Ïg¹Î{îËÓûxµþ×½ý~¸kÍ“[võö´ïSkS×í/4ñÖÄ_©X³,qN–yï•ÆÐcºO_Øúf'±>¼]ÿôñ .”RGÃŽ¹Ôñ§»A]$§ý{=¹Áþ$.Öañ½¿{kœJ­~°Å©RÆþy–¯l÷ÕrÍÑÑ9Â`nÌ®)Éü¿ ~ž/¶µÖ?×k]PsSs‚¼•b·vÙ¿÷¸ºæ ‚šbz ¸º¥mƒÆcÆë.†çêÚþm!Í´~È$Ý+ÏÎm»¯jüÄÿü½Ü=Ÿ­ö”®9êjÙÞX–KÄɵo¦kQ“ã =/J,¶7ÉíµPjã/›³u÷nLìúnÅþšL«Å™ì-bsùº¯UPó4¤õT4O-f×u‹KY¼Òæ¾ìºkŸŸõ·m½CÈÍMÛšïàóÐkbP/ÐŽiE’×÷{õ3ð¿ÏÖº…4ÿÖÆ­n_G?®j1î`§\] évý°}#ÊuÍÏtÝ;\û´~¾”A옶ùaaÅÊSê[¿I‰wûÇš7ïlõ±BnŽ­Ôò^-¾V¬yošËwcç!z<.ðûrº'PÜ®3Ån­ODûöþ|Õ¥ÌÕº$–¦yÄA»Ú½UßcËÕóûg6/QÔæu}yíWD4¶j5Úâvoµúvm²x°ÆîR·ñšßwÖr¥¶îÚžSHç‘tm½åæë5QûƒA-öR;Û9h15S(Õq¹ÎI¹‡öº‚\0½®ØzKÿµjì?˜ ´~°Þ·âÞ¸©ûKåj½"í7•xµ—’{©Í bÉûÍQº»Û3Fç:ƒšavÍ+×ù8­‹ iÍ 7þ³ÜÕ 6tjß%ÜiÄ}뽘þ¬_j®ôÈ;×{9~󺵮öá‘n9G_QþøuÞµ&ûÑ’² ×vóÖƼuý^m­í§k;çØï…|Íó²Ü˜<Ëó´óÆî[6Vôú^_z‹«Õ¬a³œe—ýC[3¤q.Û'(Ø×Eó‘Ã)ó¹–³Næ›ûµ÷#¹º&Àõe>HYKîú¬Ãèe5Šì\·xo(¨Áç«"¶ ¤ëRµ_iuoólý»õOìáõ;í}Öqq\?ÈÅÿ­?º^2ä:&ëùëý,¦}|ˉ¥äHÇìØ-Ö}rí9ëx¡Äâ_V ÍÆÑÖŸ¶8¬õm ¦ûuø×ÛÚÆà°\(yÏÕrô¼Š¸z§çX>‘Gnܪc0›+÷s!Rò üû¾­¡Õqd\ëÇhœñéxÊgqýVM»·ÚSÆl6ßjëßânï·Û-Ÿ3^®¹!]+R¬5W,?Ëbvݬ{òzÏEç÷‚|>½N”ºض~Ñ»¶éçìÏE¹ñXçRwÝMÏ׸¢¯Öº1«EhñeëwY<ÃûLõXÕszAÜr ­¿êr/Ös¾ºVÄû\J-ÏÝòµ,N©Ç˜ÿÙÚ\‡Žƒ\¡-šn±6½fXnµß¬Ï§×ô<Ý/4Èk±qŽÍZ³í§§c³’w“5»4žéî:ï×|š¸7;‹ÙxXç ýëšÅËìx³ßmëm}C¹®ýL‰‰EBZOBï³6÷ iކ]/ÊSj+æ®´ýNý{{žÍ „´¦sH׌ë˜0nyѶ6Úr‘RÎ)}½þ(l1‰b]£iÏÁÆùÖ—°ë²ö‰,Þìÿ¬ÎåE’ý´äw]ê—oñËuÔ¸¬[ù6ÿm綾¹¸sjžQ±Û{,_Ï¿ Þbù zýÊ·±šw,ØøÉÆeÚòß¿'+èæ=ï~¿ãºÙÝBþÞ{9|e‰¿ÿÀ%ÕºêX5¿ì„]íýÑ\o»”èù××cóéþõÇŽß-ºÞÃÍE-ú¡ú>”¤Îy{Ù8&åޣdžËÓkAØbŸÚ·}@#zm¶õíyVÿ²8¥N§ö;ýùZ‹¡Ø¸§Xsü\¬½³ÞW‚¼r]K2$âö²ú¤v,¹x¼÷ÿ½µ!—+’oó@s÷ž‹Å 5†Ó1½Õ:XÿÖŽí§çÛ|nÊuÃj_XžÕ [Ž…Í ¤ó‹Ý$Çõõ­ [¾¤åxï¡Cµù¯£Æq#ouë[Ø|åÜúyl:7ä&é}R÷ƒ<*%†çχSâ‚1í‹ØsÔ9÷š4¶ÄÊ\Nb°FÙ?þížjï½åª»ù¡ –sÌb'îÞäÇZu-fVXëF4‡Ñþ:>µú¡A_ÓâA¶ª8¨SáòLSÆËzŒM\ßÐïókß2+œ’ë’§Ç|¾ö¿‚>Þ‚:'XœåAýwþÚ¸Ýò8ôÚ±~tÈåA9x:Œ¤¬¶úvÚµÙÏ…·~™ö¡¬Oâ6Öw´9.«/Q®{Ý”kF›‹Òc$¢sØþç§ýƒ°¾>ËêAXüÁbW: æ”ì>kù)¶ßnr.×]Z¿Úßj¿Öæ½Â§Ôã5ž’+æŸîC×<â žòkÔ鷾Ɠ"6®ÑûŽßq›ã´~¬õË’±&×§×q¸}¯Ý›ýk‚í¹—úùk_Ü?‡4-ø¬l½Ÿåи:µn:™_¹5ˆCëû«yÂ.^ouB)µƒ4?ÊÖXÄìøMæR>­Çà¬ÍÇ—»¼wÿúh÷C=í¼¸Zk‚ø«Žñ‚ñ‘w|èXÄbþÖo¶u>A\ÙjYÇþ®Å‹,WD¯mþów÷kwoÔ~XXûÉþç¯ù@özƒ›å8[Ü\û½a{꼡?ΰ^Èϯõç»ý~šÕâiý7~) êÙwëÀîÞ‡]ÙmÓã'v²äŒõ—Mœ±¾Î5»?¾s}ÁgéݼçWy׸®jS¡Û€ÓÖ5«Áº— umði§u+õûS¾Y\Ú»éŸò½{‚W+Ü‹Ø< ^ÃâöÙZ^‰ËÁr{VX®¥wíÖ}Atþøv˳»xCË·8_pëüad³ÖÊucœ¶ùÖ¿µ1§×Ñ›¸ÖZ j™h¬Ñî™þïI®qq±}ÿJܽ¬m¾Åóô|µ:y~?Éò“÷^?îÇ”´N|ØÖýY1ý¬ýuJ:oÑ9}ÍeͲxsÜr¡¬b±8;Þ5-nç¶Þïý·¼C;/l½‚wüx翳º)¹X×öí¶52:¾je±ž°å*Ú\ˆÆ1ƒçoyÅ!·¶xbH×NèØÒ?í¸±xŠ®AŽ—kM1Mø×=wFRò€´Æ]VØr¸B~ÝŠ™Ëy°ÜYËÝÔy¿°¿Û^"nÌ¥{ÔØœºå0×G[3çÅȬOnc -A¸Û» X׋…´–ŒÍ§„´¦KHëûÙ1ek‹u“Æg#:÷£ýý½ñdÜ)YwÝbì:ú¼ú™èšê9VgÖú|AüUëfùõ>m¾Ì®uåZZû1þø%y-ôcvï¶uLç[ôõ’׉`_¤R÷Úž¶¼‚¸ÞcÚ·*µs^ïûÁ:a­!á÷«­o`c<'k7¸žX­*ëS뜛Õ×¼Ä,[¬rŸƒ[acÍd^ÛßËÎ+ëg{çŸíóëî ClÊ VÛß:Ø_Ô?þôýôç{RçNõþeë<üÏM×@k_ÂÕ‘±ymÛSIã –/Ìïi^sƒúÎ)q‹[y¢åºF.ù}Á¼³õÁƒX‰æEì^Wž¬‹%k…ر\Ïl~oKPc9Øo:l±Y[kåbAVÛÚíYfý—I)uÔõ}±xU0çÒy»ÿë=;l5~õqûLòRrÕµÂˉ„’µã6æ²þ«w‹ƒ5·ë8ÿJ[ëkùÁxI×Åm-¼ÖŒðû{¶¶»XóZõŸvïL<æÕ÷‹ìûä½²fMBë>i÷}×W*Öìvç¨Åe‡×˜ß­O­M~ŒÍ»Ÿ/ùògo¾ÍëúóOo|5 «æÇúã=üyuÛàX÷o³{­Ë+ rÆb)¹\6¯±þ¶ŽwôúîbùvØXÓ®©Ç„åRëµÛÖÅë|¬»GÚØJçgò¬ž›¬3qýW“:õ8°Ï¾8¹·gÜâp.'nÕΰ8Åêã¡`®Ê­°VksýÝ^V ÏÎ)}ý_Õ#ÕëYpOÖã6ný„bÍ3HΑ؜¯Ÿg®çÕœ •õKݽ<=ßb6§S®96—hÇwr^'Ø'ÞÖ®ùç´ËQsëÿõ½Õ÷2ØSÂjê[ÑÏ­ï«ëUÃö¼SæeýÇ5nä*†Üšõ`žÎšå‘xß§ó*ÁšRË»Ô~‚Ÿ¦ñüàzœœûwµu aÜæ]l¼b¯M¯“q;wu!?5îh}2{ò×Z¦äûc$=oýù>«Å¦1|ÿs·ý¡Rêm–j͵`Ì­ë6‚œ }ýZ“uk0_hk‰¼ŸµØ¿Åµô ôZã_-—Yû¢»Ûº3ïšàruýµœA_JþgÜÖýX~ƒõµ“ùZ6OtNpŒ'ãàn=X±Ö é\™Î÷ÙõZûºs¬/ïçØü˜^—u=»‹1$û„Aýø˜Ý;tž!l}*»žéyì£g1:½nýªmýWŸXü@Ïç`_5‹ï&ãTnÍ©Ëá ê†5ïÄb÷±”<¦ˆåÙ¸¿»×æ,ÖÒz¼.OÖåÉè{žgëâBº†.™³ãò÷-&lñ›ßæ¯kfÜ®ßnýŠÿ»‚|‹•»5m7æuszðǺƳX~ ~¯ÿÙZ½¢r]3¯ÇH‰®¿Ûûb¹/z­ »ùþA>‡‡OÕÏ%8ÖtÌé¿NÛƒÍú<ɾ@g› ‹è÷‡-~¦cÿÐü”Ô¹–û¬ÝØÇj®&ç;õ:·x“¿–/–Œ… òÑ4÷ÏêÛÆ¯ýêŒîÞ8ï‰õ±nSö÷ï¾tÒ‹^ø¼V]Ëö——œ‘ë¯ÿà’«ºÎ,ªÒMkføóÞž€6~/wudƒ} B:cy.Dzs¾Å‰¼ãMנǬ_is n_†¶GU{¤ý‚°åXOûoA!¤ó¸.—w¯Î˺´ÅQìÞ:ÛíåĈìó´$ë jÔ|lÜr¯ò\ì­—Õ¸öïÞN› Æü¸­·²û¬7öËõ×èûù£Ö÷L½X.mÄâbº–>¬± ¿­c~­kêæ]Së/‡´Ž„Þý{ åéú“°ö=ƒ½’×Ѷú7]ìB_Cpmõ~¾8È{tu“¼¾ÅL’óÁümÜê„Z?%9÷<'ØoÐ{\cÁ¹añWßÄC ·œP«µ¡ç¹õmòí¸±óAûòþû§}UïµåÛ¼‰ÎOù±ïõé^2 Ĭ†‚ía`ý9=†ü>¢Ö/ŠXÜÈÖ¿»ÏÓÿ|ýØ€­å²9ËsÓþJLã×a„-fa1žßÜKbö;Sr×J,¾©s;Aއ]Sôß#v éš.»gyï©þÎHr^̯û¬;Õ}ƒý9,ï3÷Æ®n>è%ë›êú¿56†÷Ï#o/{Ùñ³µ?.îì_µo›¬i¦{ÞÙ½µÄÞ?ëÿ»ºa{ƒ5ïn¼æö¼ iŽ­ëS»ãÛâÅZkÈÆÃ.ã@0ïâýËŠëÞ °8„îÝp ˆÍéØTç-\?ÖËïÒºjV_&ˆUy_½ÚcÉáAíœ`Í ÷þÚ€å†éçÔÀéhﺭ¯?–ÌgÔ´8‹ÅÛt‡Å¹KŠ“û“ûÇ¡î]Ì¿YLMÇ«¥›³1¡ÆÔcVWY×3XÉæøc¶_ŠÞ£-Nfñï Ï¯}-;.4ß§U$åÞq9&Aη›‚ù=Ëõ°œ[}ÜÆ{ù©ñ½^ã/­Y¬ÇêˆÞ«#ÖGÕ¹\ëÛEtœŒìxןµc.®¹J‘bÍ!Ö9¾ˆŽ[cö<,Vdûý…t®7µf5,oWã©¥–cï½–çüXæ=Ÿ¯½Svi½Nëô¸ð÷TðÆú™¯¾ìïgçÍùz±€î1asª­ýÚ›{5öµ{•‹©»5šåZÂÖÕx?ok3’çïÖàüÕ9î`¼®ýq]CìÙ¦}«ÎAÉâÈÚÇ÷ëi½ëö°z£A_²\k Z-˜d±‹•jßÚæw,4®×¡ÔµŸ;ç4Þwy¾¶ÿ’Ë ·yŒIºÏ\¹Ö½ó^›ë÷uŽØ\’Æe-G&¸þ[ÝqËý÷žæhî}g»^”jí}›WÏKžßCbºÏ]\ãù6ç¥ïyÜÎw]Ôÿ¥äq•këæjlÁòLÂÖw}hÛÿaí×·ZŒS-vë^ƒÜj‹[¬cs°ˆ‹EXž¾ånº¼àä>Ë!W—Ïß'2â_7Ý:§,½oþj*'X, âö‘öû–ùî³È²¼Ý˜»FåÇIµF¢æ7Ø^#l,í¿§6>´µu¶÷§÷Z]LÿËÕ}ßÕš&Þ÷XßÛÆ¡[t_t‹9ê9a}Ýxò½ æð#šßnuDƒõzîXMD½&Z]à WF÷ÐÔ‘°ãVÏg‹ ÄSÆz%–seõû,V¡ý ÞCÊXÐÖƒèüÑ^»5~mÝœöóÝøc‰Í…µæ¨ý[п³1´7ï–’‹•¯1ư֞‰»uYnͦÅ,?á]]ƒ›¼7{[Úý=ØŸÎj*é=Ec~®®…wîx}s7Þ¹=bý!]“d¹l~®‰ž{¥Éñb/;Ïòtl¯÷R/ÕÏÔjÙZù›ÿ×8–þ\‚^·mÞ§ÔæD¶èz}­‹à÷­¼ãßÕmmä,k!¦yþøËŽ!››·ë åZ&c¼[ÃÉ×åÇíKmŸËP0ßcy%þËmÙ¸ÃjK„´6_q²ægÐ÷+NÖŸÑœØ`ÞÀÆ„1?æ&÷ÙæÜCºÏ‹­mÑþ¼åTEì|óŽ?]„í÷ÛqìýŒí_œ’‹f}&ã¶ãÏæ_‚<8ýþ¸ÙÖéóóß{ëCyŸÑÓ‹ç–ÚuOŸoLû°þõñ¡™wu÷~wö£yÝî+Ò}èÓ•º?¿|îúÏgÙ}Æî.Ý»©‡Ÿèíœ5ë—®y¯-êšø÷u>Lë–8&¼Üý2›Äí˜XùÞö|ÝsÒ?g¼ÓnM×ÓA_ÙÞSï9hmû 6‘§¥—Z?@ã×1ÛëúJÛ?ÛòYË“ù”þXËÕ—uyC¹š£ýŒ°õåÜ:&[ÛäÖê¥änx}µR› Õ|g£¸=Nt<®ã[³è_Om—õõØêüæúëøý¸‘öÓž¶>bÌÖt–'÷îе 6ïåjzY^€½o!Í£ÌÕ}ÿìZ2î-±ùëÛ1;ÿ½ç«¹Sq[[¤ñ£ˆÆƒç­ó·AÝ)½5÷ÞRo¹²‹uXÌÈ{\ó‚"!]§e}ÁÙní~©»-ÐqN¯Ôyp¿—rëF°’­÷ŠÍöîHæ€xŸýìd]Π¯©çO$W×°x}9ÍW[ÜØjÖy¯Û;¾õZdõîü{–õe´†”-°9iïu꼆֣ñ¯“–Ë×µ vý‰ÙýÜÝ£\ŸVkºhîä#zOr놬£÷‚ ¯é=_ïﺜ}ePwÐâzò¯£v^Zܶ8™fs_þ%Íã·¸tÄÖÊY]jiDô-qcÏ ¦µ?ð⫲¹kßôÏ «Ò|µÖZ÷_ï ¶G@°†Øbž!­MnÏ_¯«APã§6§×¾@D¯™aûû–o‘ÌpµW’ykîozŸ‰7Þ°úÂv\&焆XÍ‘RÃNÍÁðûLÖ×-×C6n¶÷߯ä+rt_ ÿ\ÒøkPßÌû>ïýµñÌ»º7°®ˆè¹ãßV§ÎÆ“°Xœåþè5Íúʶ{ÄÕ,qï[rÿ÷¹†´~‚­c ùÚn^w’ÖÇv±åVËoѹ‹<sZíµbÍÔû›>»9k·¦Ñ®£¶¾)enÝ}ï¦ì¬ÇR¼µÖpòÎíæ´Úüžö©üÏÁÛ Ïêÿjß3–Ú÷òÖj­á ÿÀâZ­T×ÑY¬F×Òîµ>nPßæmþkK²Æ»õñ,ÿ?bý›? ¹úžA}Ë?’ó"ë¼çtEA‡u~˜½î{î);yT¨Û›Õî]WuT÷®7ö~ÒãÏ?[zäØ/¼yÅÒÌW_Î÷öÿõ~^óÔýóÊ›3Õ€æmµ¸VXïq–Ï«ót{­ÎZDÇ“që_yŸ·ÕŽ+×|äb¿~¼»öÙõ¹XëÛ[ŽÕ.ÖÜ æZÜA¯¡v¯‰Û=¥\sYRŽÿsÒ˜³N$ï…nœ cº°­Ë¶Ï!u¬¬1©ˆƒ,Žh×ËQ±÷Ðú'¡dNŒÕ¦‰ë½5/¤y„c´sÎâ1[»â]㵟Œ‹,æá®Y3#zŽZ|Þ¿'&çë×Xœ'x/Z»½üÇ´ï¤ûôº5Ͷv@ÿ~iqJíd×[ÌVs¶-?*œÚg²ø±4Žb1ã˜Æt‚X¥=l/ ˶ֽ-gÒûyë‹ÙüˆÝƒ“ùY¶ç¶?Ƴyã¸ßí=®ñï<»N•ëÚ;Ë=°˜€Žo".±5Xoæ=­Á뿇Þ뵜^‹aéÚ¶ˆíqç}µ:y6_ãýNÛSVsO‚q½ž‡woÚä)Y?Zã·¶j°Ŷ¼¿g{%Yì1¤kO½×1Ýß[qMÄâÀ!‡°ü‹µ'ëjDBºÞË®“ÖwÖ¹ñ`½­1Õk±ݶqš÷ÕCÍvkÅóíØ(wóÑñÖZ›Úå ¦ç[_]kt÷=·#Öo°}ؽ>lq=®‚q†õmœgóY6-êÂ[-[O™±52¹ºVq‹îõj1ÁIA-Þ½q­»cñ2«C¬ÇíϬ}¤R}ïl¼mµÂV“!Tdµîý5“A­]‹yYŒÄ»ïØÚDwÏpk欯â}¿Õj+Öõ+zÜGôضý]µ53¸¿z×!Ëñ¶< »¾%ÇaþýÝŸk±Øö~jÅÿü¼øž­ñ²çÒÜcwH×ù—W¦Ÿ£ß/K­½—œËqsﮟÄþ‚¼FãYŽanr¯›C ŽC½¾{xê: [ŸœÇ:Ÿôí4Ö¤±—¡c,ÿw[þ¹¾ï{oíš«ã¢`í¤å.$¾Ï×êÇK_*8ÊC=wôú oê~Ê! »¨ydï™Ý{æÝÚ½¼ÇnÞó«?iGÙ²öyÝ.ÐmÆë_¬K¼§ÝŸI·~'^îå&>ËÅùÙ–”z{&þfW¯ß¿§û*ïüÌ×÷!XË“x^þü÷™é5Ô?~­žH®îÉh¹Ãv~ؼ‚å~§\×ã֯ױºõ+ƒu‰6¯Q¬ù#:—·±“Õ”°üW…Râ¬Öï+OîáÇbt=œþÜ]—5GcÁ<ˆ÷6±ç«{ÓElœP¬õ¾t½¢ŸK½EטzßïbŠéÁq›¬ç¯ öÏ©”š‡þùd¹oV§Ð懭î€×¯Ò뙟¯§ùcþ±éæÚÚ{­ë“Ô_³ý SæÝu<ïö0uו¬˜õ¼ÏÛëÙ¼šËûrëÄmn`Êü«óm}±~þ‹Qëy”¿%¨gîò¹RÆà–_àÿ¼÷ù¸s¦•Å‚â.Þéê Ø^ ֿךLËi°l¶^3ÙËÒó}«]Â6˜\#çÖ:knSÄæðuü×ü¾`F»~»õöœ‚}šìï$ž‹îýàr&ôš¶9[ãcñ/ýlµ¶î̸ÍOZ_Îr6[k#›w´q„åIÚ: ­oç¨3VCÞÿû/JîwìúVÞ{áê[ÎŒØ<œåfØ<­­™¶óTÇœq“뜽ۿÖb'^ÿÊûYïZšÜk-+®{a„·{÷¸8¤Å‹57ßòÙ,^k}8É…­†‡ÍÚ<”ÕÖ¬ÍÑú¹Ö_/µ8…›ÿÛë×y·yëÓ†´>”÷¾¸ú‡VOÊí-f±-ׯì—osèöï)cxÿ~cóÈz_²øUXsó"ÖGÑ8tØ®£.†Ü/ßâÞó³üH²ú‰~<Ï‹Ñé<ˆ÷;ü¼RëÓëÜDÜ×9ºH(ˆº9 ‹¿¶IÙÿdºîÛmóµ–Óž’;«küq[+æý»Öšëµ¡Ôâ¹ÖÕ~wÜõŸÖx‘ˉðþžËÝvó¹)5Æ­/¤k'5§)X›àÿÎ ;~BZÇÇÖ½k<Ê¿.ZŸ×Æbv±q{¨(ØÛD÷bù ZÄr-\×Û$äê<Ú¦chÍ-u× Í³X°ßÿ¶:zðÏáäùضÔ> Ëã¶ø‡žŸAVçõ˜ž£ï¹·´¹…¸ÎsÚœÖÿ˜™Rk½—­³Ì³xŠ÷>Úž6޵k¢õ-¶gã|›°y£Ö]ÍÕ=jm\ký)7–tc}Ë}´ÏC߃ ¾¥óâAŒåàXß$”²îÇb“Úw·kD\çG#ÅZVûÌñ”kž}Ó}‡ãz_×k÷½Î¹µ?×þ—_"z=³÷ß—xï¿­'ÔyonK÷y [LVïåþy spÚ7Yc5õòtO¥ ÿá®…éºÏÙ9q­{eõ ãzMµznq­^jµJmÞÖæe]}í5A-5çhN•]Ï-¿¬DÇÉAÌÔúºÚ³‹°Ëv¹u|¶VÓí¡¥ó€ÖkÜ#f±ŒrW÷ÏjòÇ-F`ǾÝw‹µ^½õ­¯§10?d{¿Ùœ„3ºÏY©Å+¼ÏÔòl>ÁûYíßX à ¿œ2g×1‘õ+m­uØŽ­£Ì›[ Ã;6tßç¸}n“²s#cry¤Ú'óÇ*^¿Áæ,uN0n÷:ïz¦k#á`íîœàZ¬ý¿R››Ò¿¶ñŽw-°µïåAˬ –£ëîJõ~Ä/ô:áŸåÈ„´¾Åô\ÏÓkS°× öŠvsŒ6¶±}píX°œ‹óZ-omøï‹Þ‚™‹9wöÏIÛ»ÜÖÑh­÷÷ï!cÕx³Õ+ÙXíÄí[Öy¯cÕ¼M]Û†ë®ëSkÓº«âM»|–¾.úäÍëv\7ÛÏûã±ÄÏ”öj9ºlúã]ý¸Ü_èöüò‹<˳wçµ_ïÐÿŒ’{»";Nõ}·Ö=È‹“u*"6ÞÕó'Jæ0[ýÿ ®¬}s»µntKÜòêl­žÞïÑýím …æòuÖù³ÕX¬Ö›ðstýÏVãf¥6î·9 »‡O÷÷ŠÞkcŠÔñ`ÌæI‹uß‚Ö÷ ¹¸°fÏú7ï‰[ŒÏâ~6'hsÅÞóÓy˜‰B£¶ãXíS5Ôœ3Û =+8_ì¾çrzÜܪåmÙydc$«¥“R§-OÇ‹q»§XN½%mÍŒ©tˆ­ óÇ@–'P®õ”B./]ï‘þºn«èïa5\LÚ¿kN·Ÿ–Í–ëžnMê”R½Vû×!½·Øu fý˜wu]J=‡Hê¸ÖŽgýw«/a× ƒ¹œ ý|"ÖÓ8©^Cý¾‘Õò ~&åÖøC<¤5™t ¡Ç¾;N-^cy+!­S`}§P‘ÕL|Úâg6?`ëvã)9y‘ä=¶WÄb z\è<¹Õ¿ž£ýIˆˆåeÛ<€åó„‚5®žµÅ+õ÷Å‹5ËÆè­ƒz‰Cbvß´x¬£{WëJé±bógV .ÈÇMÆ ü1¼åúÅí~è=æ›,ÇQk¦Æã öö¶ë©['ôtPƒ ¤96§cu ,çØò˜t¼·˜‹÷ï^Œ,äêÇ”ÚXÉžS±[×ѱ–õ_ük¼®‘±œ7»YžCXsœüŒ¥vê5·Äí%XèÏ¡»øæX²_à®7n ÷VËqÕë߯ÒûÔý܃ڧaëëõÆ¿/ê>ÿÙ}W¥.úø úø}|˜>>òŸþ”ÿg´Ð˜ÒYÕÝ×ùÙîëûôñÖúx[}¼“>ÞM¤ÒÇO×Ç‹þùÏøE‡Êîë$íX®Èq_?o¢¬·ÓÇ5(÷ùQúx/}¼¯>~Š>~¦>þ7*Ž­ê¾^ZË}}4Wh®ªk8þѰý ~9^ï¯ÑÇ õñ±ÿÜç €ÿÝ~iþG?àÿ_œÀ‡óøãpþà߬6ëXÿÀú‡?†;÷Òjý½÷£Eš{ÿš÷Ãkï¯OJûW~ÿп^éýë•ÿп^åýëUÿпžžò×Î?Î?ο?îüû£ü—ëÿ½§ý?üý‡]:tùõúÿŽ:tìÌúüÛ副ު¾ÊS.œà­Ú³Î²WÁ»P{· ÿl='R¯âÿ[ý—çÿ¨Â©ÿÓ¿ñ÷ÏÿPÇP8ü›ú/á¼¼Žœÿÿî¸Ï¹(ñ« ò>ð4w {¾wlWï?®pì}¤ÎìÄ¿døÿÑ£hÊÈ´dE(ã­Êô¿a@Ñȉ“GéoñóUwÿò›¬ðßý¡:úµáÀ‘…ã 'L7müïûº÷ž0µ…Ç^A´ï ž}ûF‡÷ö– 6îÝ£ÅÄÑ- 'L-š0¡°ÅØ -¦Ž;¥EáäÉ…z/ûW5PþÚ?Tùg¼â )ÿÿ¿|é½zöìáž}z¯¢¢Q-ÆŽúOy¦£&NKüºà™Vëx«œõÖbvI¼Š¢ÉÓ Çµ=qr‹óÇŒ9ƽÕSЦzŸÀ¤ÂÉ…ã‹ß2¥…÷þ9mráÔ¢è…UûÍ »ù¯¿°~õ…ùý²“§%€iãO;uJ⿽µÞ^±³ŒÞý'N;uìÄ öê5uìø¢ùáPá¿þâŠÿê‹«øß=ªõ騻o/ï(køwß»ÿûƒgÔ_ùóTžzá$ïÍÊÔ?Wi@Ñè”ÿô‡[E“&Žã=6xPÁ_9@øÏ,å=nbáÔäñYsP¢O0ܤƒûxÇPAḱ#ŸRâýi1µhü¤¢ÄÿŸ6Ù}ÀE…‰Ïwä˜Â ŠÆ%.¶£ZL.Y4iêÄÉÿÐ'ê ,2Øð¯}æ?íÌ?Ñžß?òŽÖ4`HêÚ1ñŒ‹ÆN/šÌ»ùßx7kxâÐÔ·³ÝÀs/äüsC¦¾“^8%ñFòfþ·ÞÌD÷3õÍ EµëÉ»ùÿnfûïæoÏ‚ÞMÈëEØ{{H‹)è!ûßï_ýõΕLœ8.¥¯˜áß‹{=Î{–½Æží¿8ï_ÿ¡×ñ[ÿâ§[Í»Ñé³­‘|¶†ü'>Ù ÿ>òû÷6ñ¯ÿ¡O7q2ü•§;tàæÓõNàß?ÝÄ¿þ'>ÝZÉë>çì_=çáÿî÷9qíù9-©ð·þ”÷S¿|THy$%¢úÏ©Ã÷ÔÿëCÓÔï°Ñ^ê¿ýnÀ\ ý1×o:¿¿zðw}¹ß>úëëÿoýÍ ÷Wþõȯ¾åW×_½=)—± ¿ú”+ÆoH8¿~ å,H}à÷Ç›=ú†¨_I´Jö±WûÛß»Hý*@]GÁ¥î?ýã¥iÿ÷þæ/þ-û+ÿ?V³üß翌ÿ0zâÿðoüWóÞd߯âÿß"þÿï0èÂIE-ŽjQ}à´ö«ÿÑÏ ÿ>ÿ@ýÿÿq©¿þç%ÎúοÿëîÄùÿïrRJ‚‡E”M´^/ðÚ3\«s¡k_ŸïÚÓ]‹Msí/S];Škƒ'»Öé<×*NríÝ ®ÅÇ»6wœkçœëÚ±ç¸Vw¬kŸžíÚó£]»¿ÈµKF¹vÊH×ÚŒpíÀY®mîÚ­gº6ì ×rNwíåa®-êÚC\ë|ªkovíìA®­àÚÂþ®mëëÚÚh»¬kñ“\Û|¢k¯žàÚ³½][~¼kWçZ¿^®µëéZ¤‡k¡ךˆk?ëÚ¶c\{áh×bG¹vgw×fvsmHW×Bù®U‹¸¶+ϵ’.®ÍêìÚI\KR‡©µæÊó]žÎ×PÚ½­ÓúÛèÈVoF¯|þçè´VïE{ôý)ZxÞÞè©WW¼Ü}Ñ›jT–‚wvEù ²|xþöè•*KɨO¢ƒW’h¥¯£ÙT’7|=£s%9¹Ú'Ñ®ÏVŽ'½ëÉŠÒ¼Ï÷Ñ'U–÷-™RI.?íÛèYS+Èô?}íùSEYyúÏÑSf§K‡kªÊG­ªÉi§V“ø}•ä‹’J²|rEivFEyqU5‰\WMNêW[J›Ö–©oÖ’~3jÉk±ê’~d†l]CZÝ“.#¢õ$úb ¹þ¡Æòèóu¥k¬‘TšVOnÙØP«–%ÚL<\CÆäµ’z÷Ô‘§V·’aiõeÔ²V²âÛz²õ¹ÖrÂ%u¤Â­eòÚ:²txKùbh=™TÒ\^¨ZW–Žj!‘£jɬ›ZJôã:²þœ–rØ3 䢚˭“êÉÒåM$w{ ™=¬±Lø1SÖÍh"Ê–ëßn$g=SO²î¨'•žÍ–Ùësdјr^´‘T9$CÎÞÐT²§Ô’†¤×вãÙ\yðƲ`T#¹÷ýZrxŸæ2õâLyê‘f²¯ût5 ÿðQô¡hº<0õãè¯ÒäÉ7¾Ž¶ì§è§ñ/£æT–G÷îˆÚ”)/|ôftçYÒéŠw¢¯Ý”#×ܼ+úÛãaq·:ß•<,ç.\ú~zŸTÿ°´ºPúe¯”¬»ÊQÕWÈg/”OX!wž¶PFݹ\*õ[(÷NX.±¼…Rý»‡å‚´…òqìay,ñõÜþËi'-”3¿X&2{¡\1g™,’ø¾PâkâñÑk–ÊÅëH~Keã½ äѯ—ȵ.wî\"»X £ —È„á å•;–H¬ë}Ò3o©Œ+X$ƒ^\*í^]$ [&_~¶HîX±L®¬¼XŽyèa¹¡ýb9ç£åR:p±uÈJ‰žµXº Z%§[,ç­–o–/–³¯‘s‡Ý/—ï]+x@vµ‹IiÊÁÃã2渇ä±Kå®íɲRÙ=z‰<õR©Lzj‰ y¬Tnœ»DFÝZ*SË’ÞK¥ÚŽeПã’~òƒ²ûݘ4|ëùñ˜¼ÅÒ¾ Dö|@¶¶V¶½|¿\ûæj)þi±ä·J¾ÿh‘TOæ¶Z&½w·¹u™ÜÖïn÷Þ2itÚ]²»Ý2y¼Ê¿»>L¹u|×úÞâÚ¡7»öó\×^»Éµç¸V|£k'ßàZëë]ûæZ×â׸vÕÕ®r•k]éÚçr­l¶k—Ír­ß宵ºÌµÏ/qí‘‹]»²Øµ~3]«w‘k[/tíÖ \ë{¾k?MsmÙT×fLq­Ûd×>™äÚU]«:Áµ~ã\ksŽkǸ¶él×fhÛ2Úµ4m5Š\«4ʵG¸öt¡ksÏrí¤á®5;Óµ¦g¸–qºk»‡¹êÚuC\›rªkýOqí°Á®èÚó\»ýd×Fôw­s?×¾ëãšõw¦èÚo‡´î[ä–·DëÿÐ]f,¨*]6Ô’–»«É+Ûräçc3¤Ñ«9òâõ²®}}yM†´ÉÊ•’ÉÕ¥Ò°\Ùóf i~w™µ¾¶<¼£±ü¸¹Ž¼o*ƒ ëÊÎp3ùòðz²vI3Y‘U_¢?·?}Ò@}µµ {¨‘ þþ ¹¦VY0ö ùóSMä W–5•n.í†4•í»ÚÊÊew¼­¼T³¡´û¾­<ø\™2ª,ÖPÆ/è ¯ÜV_Ž©ÒY¦˜#ó?î$Ÿý¥®”­ÉÄsä¶{Còæ_êËQÓ;ÉÜÄýô†>]ä‡öõ¤Ú¼<¹þÚº2饰Ä_È‘zy]åË!¹ÒõÑ£¥bâûy]dè› ¤õmÒð–†R<<¦¡ü¼öy领‰{gTÞØÚH>ÛÛC66•;*''|×\6¼ÞKŽù ¥l{»‡\Ù¶µÈ¸;µµ\ßáxyüÍVÒý¾evÆAR’{¼Wn(m*;ö%Þûì&2û®lslcÙTZ«±ÄZÔ‘³_n$ósëH+É‹»k˶)¤<Ñ÷ûhn#9øôZ²î¹Ä=Pmiøa#iýT¢_xP# WCæÎHô?Úeʶ'ŸÃë™òÑÝ eԳ﨟è?dJõõdî·òÖý9òø®tI¿¸žÔ¾»šŒ^VOþ”“)Ó;$úÛ2å†ëÈÛïT—«_È’¥ã2äèG³å†*ÕäˆôÙV#]^ZWVU:ïLôëþ\9Ñ?­+£&W’=êJ•N•å¹§ëʈ&U¥íÀ9tHºô¾-Gæ$îß“?Ëkå¥ÙòéI¤´N¢?9¿‚캸¶™éZÉE®]7õº–wkç»öæ4×îŸêÚÌ)®Y<£íy®ý<ѵ—&¸6o¼kçŽs­ó¹®íëZ|ŒkÎvíàÑ®ý0ʵÇFºvÁ׺6¸kŸŸáÚ+§¹Vq˜k— u-OÛC\ÛtªkïœâÚ[ƒ]+äÚÒ®]:ÀµèÉ®5êïZó~®ÕíëÚ÷'¹öÒ‰®=t‚k·÷v­øx×N?εp/×jõtíƒ×V‰k×F]|¬kMq탣\ëú›ãal•{¥Êk;£k&Ö“>© Û>Ý=-q½*Ëý<:xYuY»z_´óÊÚßóY4»e¶Ôë÷m´yn]ipCšŒÎ¬'·~&Çœ¸î==sZ39õ—ÑÓŽi*G<“¸žžØH¦·«,Ó>nœ¸×W”×7—›^¬,Ÿh.¯í¬&—¯n,{öV“.ýJµaUeØWeÖܪ‰1K9÷Å ©xWk™Ô´†<5±… ™‘)ï÷l*£îÍ1%Í$ïªêòõ—­å@ÓZÿå¹1«¶lúì`YqK iÕú`™¹¶¦4,;DÖÜ”èƒ.ï®Mô‡Ž;"q>Õ•W7¶—³údËIw†¤ù½uem“´TOÊú†¤á¢zÒñáŽòcâqzv'{L¶¼ÿBH>¨•%‹{uóo«-çvì OÝZG–Ù^>žRG®í„¤w©-k·“aû²äÊŒ2*#GЧ·“ ^Ζý=—{·Õ–¥¹mäìYudæÓíäÙX=9~ø‘2wf=©qp[阙#;9TFîÏ•ën9T>|¯±lÙs„{]3ùº¼ƒõpsiye{9?³•ì?²­lxï`9®ÅáRPÔF^ÑN6l8Ržþ²“Ì«ÜQÎi—'OÕI²?é(.ê$ÓÇ´—./tw^DŽ?¶»ŒìÐX¼¨,ÊJôõ­å‘DŸiü¦–Òa@c©9 ¹\PÞXf~ÝL&j(;g7—Kï«'O4o.ƒ¤ž\øyc¹1T_V4l(ûGäÊî6¤ûÔl¹é°&²óæ,éuJ#¹ÿ‡,‰lÍ•3zÔ’ë}‘còªË3/7SNÍ36’'çW—Çï­/K«T—‚šuå¾sÒåò»êÈ©OV‘ÃräÛ«Ê1ÇחძI~I®¬Ù.m©+M¯"£¨#k#UdÌÙòñÒt)»¡®,ËK—ÃÖ•F¥•äâ—²¥ÛŒŠrß ÙÒc|eYΖQªÊòžÙòÙ×Ueã—ÙRéàt)ü)[ªÎM—}}³¥Â-Õä‰uäõFեŋudðÊLY0&[ž[˜)Ÿž–-ãFgÈ•‰ß{îmÕ$R!G¾!?UGßUUʻՒ6Ã+É»kÉã=+Iÿ³dášÊ²0½¶ìXXQ.l•)÷þ)º¤$C6ýå@ô@f¦ ÛÿStö“5¤ÎÌï£Û;eJÏ×>‹þЮª´doô‘qeb‹=Ñ=÷%Ω‡·G§ô®"ï­'zraUyè£-ÑØ[¥_ü•èý~Œ¦íy1Úî¡ï¢Òá…¨”ˆV<ý¥è¥õ+È_¼Í:¢‚<¸sktÕ»iÒMÞŽJ›J¿¿_Är ª<ùˆœ/‘+¯^+n™/•nX#36ß'¯lX+ã/ºOª¯-‘>W,’ÚïŤùe‹ä™ã²fí"9ã©Ryà¶E2÷ˆ2¹ù†EòÃÈ29¡Þ"éqC™LœqŸ¼1¹LîÚ¼PÒv—J¿ÐBù¸k©\>t¼ý\\®¿á^¹áò¸ ûp¾|vP\î9_6-IµÌù²î„˜¬ÚyôˆIô›{dû˜¼Ñ}¾´i—ÓfÍ—NsâòàšùÒ}}\J·Ï—%{ãrfô^y»G©L,»W®z¦T®º@Z\U&[[,‚…ëä³Ñ äŽÐ#r]•…Òäû ò`Åû䣦ʩ½Ië‘ÊsãËcW?*C¿Z,Ï?û¨<¿±”\÷¨\ùÝb9cëF)úb±Œ>o£Ô¿w±ì¶QšT],¡–eSßEòä¹äà›î“KÛ>"›\(wn]'% ¤ýeòÆ'÷Ê}鉾Aÿ{¥g¢¯sâÒùRüCL¥Ï—oÚÄä¾;ï‘3O+‘ÏûÞ#3Ƭ•©ΓŸF&ú!…ódþ”Õ2þ»»å¬Õ«¤xÞÝRí UrûqwKí²•òy绥ÉÙ+eàÜ»eÖJÉ®;OÞ+[!“gΓ;ç®´OçÉM«VHƒ‹î‘6¯¯J[î‘¢ëVH³[\N‰Ï“Iu–ÂöS S‡Í[ÆxíÆ7\;éu×Ò_sí±W\›ñ²k7»öÑ‹®ÍÁµSžwí§r×V<çÚˆg]«ûŒkO<åÚyOºÖø ×^x̵YºÖe£k;qíÏë];fk{J]»!îZ‡˜k›ÖºvÆ×¾XåÚ‚•®õZáÚ¶‡]ºÌµuK\Ûó k‹ïwmÕ"×jj›wŸkï,tí£®m¹×µó]»è×"ó\Ûs—k³ït-ÿ×ÝîÚž¿¸öà­®~‹kÙ7»öö\×îºÉµSæ¸VãF×6\ïÚ„ë\kt­k/^íÚ”«\˽ҵ’?¹Vø›ëô³“¾ïgȸ»:É—V‘ÎoÕ’/*§KÏM9òãy™RÞ6G†L©)ï=”+½É”›vÕ—a÷W—‡&6’;gÖ7{7‘oÖ’?wh"ç]•%•7–£¿Ê’o,&Æ ·¶h,Çj ÷…šÈÄ›Èm“šÈîò&òÚMeå²fR2·±üå‰Ö²¶z3©r÷!R6¹…Ìê}°´®ÓB*¶’Ñ÷5“ºï¶”ã¶5—N­[Ë¢Ç[ˉíZË-Ë–]¶”šw$?ÙBîí{°|5³¥8(q~¹¥LÖAÜÖBúÝÒYf?×\Úœ–Š?¶”f¥]åÏ÷,«äXiÔó0™xnOùòò6²nøq²5­ìÿ¹§,:§½Ì«P ¾ÕAÒ Drvw&ŸÈØP{‰_ Ç|„Œz"*Gü|„lªrŒÜ¿üHɪv´ìëp¤œÛùh9ê“¶òÅ)GËcÙmå›+Ž’ÍÕ7kw“%·!{ #rwq[9¡Cžw÷árÝ;eÙ•‡ÉM]:Kß]‡KÑ7å…EGHæ£!9¹¼ü%­“ŒÜÔ^Îܘ'/ï,§çËUºJÏ…©ðñÑÒàû°œ¾¾@Ú5é&?Ö[ +§DúËÓÕ ¤Ç¥ä³Qi|xâë‚î²ýÜ“åùó;Iɽ}å«k•A—ÿ»ûź±wÈw«¯“e‡N—›ËÆÉ•¹¿ÊžJçÈí•Î’økgK•—ÎÕmF˦¯Ï”ô¶£ä”ÑÃåöw åØŸÏ_† —›²O—{ÏC&F†Idîé}cˆD¦Ÿ–èw’W¾*w<ÖO.9T¯ýqˆ¬ï)výSyð“ïeÑŸÈ)×~#oÄöÊéE_ÊÔO÷Èý—|&KB»¥öþOää§wÊǯî•V쉹ÉŸn—šïí–?-Ù.Sv˦o¶Ë±›wËÖ•;d߸=²åÊrcâwLk³KÆœù‘xb—4,ýH6MÝ-ǬüHv¤í‘ïÙ#ñš{äë³vÉ£Cwÿîxø>qÏòÚ+·ºöÐ-®]v³k§üÙµ¶s]ûyŽk/ßèÚÂ\›z½káë\«r­k¯^íÚü«\;÷J׺\áÚ³]{n–kw\îÚ˜Ë\ë|©k?_ìÚSÅ®]3Óµ“.r­ú ׿Àµ‰ç»Ö|ºkŸLumé×N›ìÚw“\›6ѵGÆ»vÛ¹®=:Öµ°¶c\‹j;çlצvíÜ"׎r­ÝH×~(tmÕY®ÍîÚegº6þ ×N8ݵF§¹¶o¨k¯ qmÅ©®]uŠkûÖfkßpmãÉ®ÍéïÚ€~®e÷uí¥“\ûíñpÏæãä–dQ‡nrË1õ¤ýÀnR2¿¡<[áX¹ºG#ù¼¨‡|[½©œÝÿ8ùfas™µàxy£UKi±áiúV3©øùIÒù³Fò@³¾rî‹ epý~2í´&R©v9aO3y,z²ôlØL¾yc œ×XŠGŸ*Y‰>Ä7•ofµô¿ •›rZÉîaCe@¨™Ä “ðþ†²æ™Óeù†rÄ%gJôÌÆrRèLy®¸©ômºTx¸‰ùè0ydeétÊ0©|f=ylú0ùzw]9#ÿ4y'£®œøÄéi™#3ª —-5êK§h¡´~-W–•Ž”eoeË‘÷-÷©+oõ/?¼×HÚ¯"C¯h)s².”‡´ÁýfÊœ}M嘲bézG9úÛ‹eÍ…M¥w­Kå•—šËàC/•Õ»[Híc.‘/W6“AÛŠå¹ åÔUÅòØž†Ò`ÞÅòôÌrØ®‹eÉ‚úÒ¬g±ÔþºžœÕà"É:¦®¤‡gÈäÇêÈæµJ×–udÀûçK‡ŸjÉ [¦É¹K2eÄÙÓeÎØ é;_F̯&½ÛL—h4]ŠM‘­ªJQt²´U•:SääÇ«J÷}Sde×tùèÛ©’Ù7]JOŸ.éé²õÍiReX¦ìO›*½îOôÿöO’;ßM—5ëÏ•-îÿÝ|w´¨…¬ÖIêÏh$#Þ«';ª4”nß×–G:6yWdÉ[sse΃µäðKëÉó+jȆÒüKuy÷£lÓ †|÷N–\x}-ÙU!Kú «-›6Ö–Ì_jÊÐgjËY5eì²Ú2tS-9yf-y·¬ŽŒº¤†,:«Ž4›)[ûÕ’‡ëÔ)_Ö±kHÓÙµåÛ5dz­l µÏ”]å‰ï;¶ºÜ¾¶¶<{cu9°¼¦ ßž»o¯)ÏÌ©!²0K §Õv5ëJïãjH‹uåíœòÙ59Òu}¦t98Gj$Æöë²åð^™rÍ{u¥éÝ™ÒþðòmÇL¹úˆ†2ñµ é´:G¶ß]MŽmQG ¬&W|–-£ï¯&áWëɆ2ÿš\‘Ì y·¸žÔQM~Z\W>:¬š¼òl¶lÚQM^>1GŽü8C²vÖ“ë7gÈWÖ“¹Ó«Éó9õäÐné2ૺ2`HºÜ´¢®”¿‘.?ÖÌ‘üÄ¿ÿÒ¹®Œ®XUö®¯#÷̬"®#å­ªHÙ‚:ÒïíŠòj³:ï—è´ZY2îÌŸ£cŸ¯-þôKtîÂÚÒ¼Ï/Ñ ™µåþ¦?Ekµ«%§lÛ=ö¬šòÜ…ßG{å×”£ý6úò÷™’½íÛhffuykà÷Ñ‚}Õ¤ïìï£}U“¶µ~ˆÎTýw÷‹m3¶É‘Û6ÈÞ—my«dÇÍ›$§Â ùqé rGbÜÚhÏ‹rÿ·+ä“Ò—dÄ®rÕ¶—ä½±+ä ^’c¾Y.ùM_’¹÷.— õ^’çïZ.S&½$×WY!çfl–gžZ!¯<°YÎXµR2xY¦ÎZ%=nyY¾Û¼J–TyEî^·Jú]þŠŒ¹n•,ÿò©sê*™ÒçUYûÑJ¹cÁ«’6p¥¼¶íUÉym…¼ùÙ«òÒy+äâ_•F-WHíy¯J^Ú ™pü«2ñ§å2³ó«²õƒå"3_•î¯,—'¼&×¼B¯òº|ž—?4ä ¹íÊ•R½þV9sÙ*Y—ñ¦4|fµä„Þ–úw¯‘w½+çÜ»VŽ(ÿ@>ÚT"÷ü°]æž“GzÉWïl.‘‹¾ß)óV”ÈÑ;vÊ1óKäö=;eÎ %rò;¥O³9õ…rKåµRÚf§Ì¾|µœV°C¶Ô]%+Çl—Ùï¬As>”·®_.M®ø@^Ü¿Lµ{_äÜ¥’õÀ»òÙûIǶïÈú†Jú oÉuCï—Úol“ÕËY}¶Ê¹•IæÁ[dq×û$}ÂrXùB¹õÛ×¥pâB ]ùºÔÏY(g´z]V¤%¾~ÿš|Tu¡<ÿÝkòò» dåìפaÛòñ¶Wä¹²{äš›eÕö;¤°}ßi©ÇÃŒçÆxíèg]«ðŒk'˜õ¤kò„ki»öÈ£®ÍØèZÞ×¶¯wí®u® *s­F©kÇ\›^âÚ!k]{wµkw¬rmàJ×2V¸öÈîMXæZ³¥®•?äÚä]ËyÀµ¥‹]ëµÈµŸºvÿ×¢÷ºöÌ=®µŸçÚˆ»\;ô×òos펿¸ÖQÛù·ºvõ-®ßìÚvíȹ®}9ǵntíÄ\«z½kû®uíñk\»új× ®rí—+\Ûü'×níÚ)³\«{¹kÏ_êÚe—¸Öùb×>éÚ¹Öc†k{/pí·×‡Ê®>ý΂èW#?ˆžyôÆhï_EÍ.^¼ù«è olŽ.ûüÇè=‹_¶ú¹‚ÜôóËÑwW‘«‡¼½ê jRáœw¢ƒ>ÌOÛ½¢Q¦d®Ý=ëԚҾ˞è7¹YrYíO£ùfIäŽï£¯ßU[Æ­þ)ºæÇš2ퟢ=ÖÔ”>'ýÍøº¶|yi™zA¶|1¿²|~R¶¼ÿuU™².Kš¼õ|–Ôß‘!ƒÓêJ£eÕÿ{w•ÕÖÿýžîni E”ÁÀZëCˆ(ˆ‚…¨(‚Š ’"""ÝÝÝ .[ì@ÅDLDQTÀ3}Ö}Ÿó{î}âóÏgŒë;ö{¸÷._¬9—N¹°Ç@Ž¢ð”‡&Äñ§[·¦K ö“<æÝ@{ "‚gHbq”2ÖòHaê;Uø‘ëí'U”ì–·05¬K’FJµ&º•eñé‘“ë½V»FÞÊaï=-\•ƒ§¹6 æÊ¡_,eÉþD9æÒPpÕÅjF ’ϵ1œ,õûZ'î‡ãa# Ëš°È“Æõu¬°“W¨*Tôe±e®2FoÊb×uE<4—Aâzè¼—ÿJhÞ‘Ÿ‰n*Ë£M_âåu0/NÊ;tp?Jå÷È‹_2†®Œ6?U¼î‡Æ *H\¢mue„˜ÉBÈ~¼ÂȺ®ˆóäq ™$é$)Ä‹ÈA唎®–C+Ùìý-‡õWEPÚ ÊCZ%2¸"„ìz)ˆñ㎀4"–ðᜟ ¾ùðâÞ]dÜâAÞm²Ææò@NL“y`ºVß·sËø1×âÆ“&æ»Ë`B37Z–Há³&D"$ ^(^—€\®O–Ä$!~ÔšJ`ó~Hl”ÀÄ3üðz'‰Xwð”Haº¦ ÎðH£¿Sgg÷£%ŒIƒRÐ…übiÜÝ+ ëßÒdÍF8ù¸.í‚Îi¸» ¡jH ·×ã÷6)hj cÜ[Iv @=“¬ù_øˆMI¤äCz«|/ó¢O['vp£ó¦(Ò.ý¢÷;‰âùø¯ôŒ·ùHwE â^j­ìŵ/èÇøfý„®<̇Ó9èçxá*t‡ŽàS{“Žùò‡Þ½«“îîüC×”]¥gsá<}…oÂ…kç:hEÙ?tRù}úë¼1ÚêÐúïwWû¯ŒöUãFÖ5|ûV†]éÙèž_é¾d81eMÁÉrÜONÁ¶ÅåðïHAÞörÌ.HAbE9übSÀÿ¢;S:XŽó…)xq¿Kž¤@öD9”øSñX¹*?Èë'•!¥/ºe˜û>içK±ø[ ¸v–"®'wœKQØš‚§!¥øNÞOSŠÉb©H—+Çթøzº ëJR‘m^Ž‚o©ÿRŽ¥sÒ šYƒSiH§*!ĕޖêJð$§#ùV%ê×g`ò•JÌ]—‰]*¡r, iUØ• í¶jœ=™A½ZhÔäBheyyøÄS¾¯yxt´olòÑ9© —ò‘q­J0jĘNœë@äcÒzLhÍÇCòv÷œóñíb-(®|ŒÍ­AXZÎVaó‹\tËU‚›¬÷W–£9$›Éçµaj®]*Á­7ÈÊ,†]q:–æÁ8: fm…° Ÿ¯ÆpV §¢gonžMÁ ƒÔ¥ Dº{ž§ È¨ÙV©8q¤Š×Rqå ùq}„bá÷4Œn+„á¶txñ»Óáí—9õi°ãÊÅ¢ù©øÏ?ïŽ!køß\ëØ&ײ}«fk­b;\ɶ°‚M²œív)[B ›]1›DÛ¶È|¶ÅylB¹lg³ÙB²Ø¬2Ù2Ø.¤±§²™§° &±$²­L`ãŽgËŠe³ˆaãf«‹b[yŠíõI¶ål§ÃÙÖ…±íe{q”íпê:Âöþ0[ß!¶®¶¢`¶½Alÿ^ÏŸù³åd‹öcÛq€mþ~6î}l—}Ù2÷°mßÍ6cÛȶv6ÿíl3¼Ù~mc+÷dsÝÊ&¹…í?× ©e:‡ÅZðhéF,°4ÅÓ§ãÐôL³M´àºÜ×ÔDž#¹ŸkÕÆnyõÒÆÉÍŠÈ^¥ %dsM¢íJ¨X>§õÇ᩺>2·*C’žŒgpŸ6ƲîdY™aÊ‹˜b1yùÄ/FPÆC9¥ŒS¯ŒaqKÛfNƒõ>UH‡O…F¶*¼} h¢Žu"F8Õ;[©©è?7¬/Fø}DcÁ“±)PÛŒô1ò^ s/àõ(Yë–LÅ»¼Iئcˆ~ÓÉØ2 ¹…zhæš‚ùκ°œ†?§tàÿØ µÿÇŸ˜OžŒÍgMpë›> 2gÀ‚¼þ6Ê#Ÿ& sl&j’&`oÑ,ôfOÀñ}óPa  ý÷4ú¢Æƒ¿|è›ê fá÷8 Ìø8 >ÖxdOcQ’FE­ð¼Ow{æã¢´zf¢'C.Å3‘  ÞóÀ»M;Ôçã…î,þ5 kõ¦ cp&Y‹'Ãäý<8¶Oʰ%`5w-qú‘)r­ W7 “7,‚ñ]3&8àp0Ù?,ÅÏÓàQ¾ãGf"Ðο:gãIÁBL+÷¿i< 4üÇþá³¢ ϵŸ³ßÇŒ|%Œ+Q…Ý„q¸Ó£± PmWC„°<2UñÇOÉžaJµ,~¼RÂTZçS”àì.…Ý­ŠpxOöj›pû±4.gÈãæ2 8­’‡ÀœÛ)÷Ã"øÖ'Ïa1¤ýG’8^ΗGà.²¶Ëᇶô/É"ñÙ¯N”ƒ`°>ÈÂo·0ZwÉàÅFA,½#‰¬uB¼*Þõ¢‰’B€†îµ¼›G/ª%qó ¹Ï…‘¨–U ÃÞQe "X¨+Œ}{ÄàUÁ Óf18wqÃÑ]·ÏóaHV oøQ‘-„›^¼°NÁö <ÿãÏõDsø0=D®§øc%Œ5 Éz%LîûÆhÕ‹¢øÊÛÕâˆÈ'ëýN1² bQ¾(šóàÔA1Ø%ÑþÝb¸Àƒæ¢à¹*%ò93ŠüH…NÉ(­3N Oœ~ÒÅ¥Bx0ë];‹þ²\x +ˆw«ÐÙçÈc:@ûÂ…}_轼ܰ˜0HìþI?]ÿ‘NÿC/ÜØOÇDðà¹àÚþîÇ Ò†ißßéñƒô¬·ßiɬ!ÚcúúÙ±ü~¿U˜EîS‹Ä–7(üuýó°öj&å¶‚ç]#äš ÙF»BLv6aÝh=ÄG›ðìz=Žë4#Ò¹/»š'S‹Î-𨫃â¯üÊ®ÃÎi­( «Cµj+í­ƒk` †ìëÝØŒqãëÐó¤ åk±jF¤›jq©¦O/×ÂÉ¡ïŒëPp§jŸÉë/m÷[rŸ?Pù øy·î6àÜz¤7À1·~| x;£IAõ0¼Y‡„÷u˜ZP‡4Ë:&ÖÁ-¹·më.]‹”Q²o¸]Q­:ÐWjð¼»5øVsËZXù6àé‘Zäìi„€a-žÏi=«uQMx2¿nõMHͪ‚h3ºVÁô~3Ô¾Tb°½6Vâ÷ÌV?¨@„Q+òŽUÀâc Nlª€kp *6V ‰iƆ’ ¬ÞÖ„Òy•ˆjh€ÿóJ˜o¨ü«Pp¾væÕx|¼²3k°ç@Ujñ§¶펵xàQ½w5˜º»»kP_ܧE5˜&ÕŒC&5XÞÜ‚†ÏÕP™Õ†-!÷Jm`nWáb@ ÜÓ*ácÜM²çûOi¯¼-þ¶ò%›Ä ¶ ½lž±MyÊö¬‡íôc6ÝaK¾Ífu‹íc[ôM¶y7Ø>w²¥^c³î`ût…-ö2ÛÌKl/°ùžg=ǖȰM8ÃÖÝÆ¶£•m¬™mÛ¶‘:¶º¶–*6©•ZÉv·‚íq9Ûù2¶„R6—6¹b¶öB6§6Þ|¶§¹l…9l³Ù¤²Ø˜ ¶£élóÓØ¾¦°å&³9$±ýJ`ËŒgû÷ß«ùÃv"šM÷4ÛzÐ¥"±ØÙN û®ôðÛgô)›`zKûZ,ë ]óò5mLùÒëV½¥/~ÞO/1ë§ß¡u¦~¢?µGÒy²ßhsx:’ˆ¾¿6Ûù…®ØšHk¥û²è±1úhs)-Ƀi]¥tË nL5ͧ7í¡'.ȧkïÐ!¿Jè©[xPUPIÛŽòáˆF-}Àœ>Uuôù^^[TÓâ‚Ø÷¤”^½[ã´JhÉ~ql-¥eD¥P· ”~m#‡uå¹ô´JÐÚ“JW}U†¨z*ª &‹¾ì©Œõs è…÷TàÑSHçû«£äxo­‰æËEôÁ dMª />րʉJÚ§j㱑/Ÿ U‡È«\º r·åÑŸ×*£³8›\Z”!ÿ+…ŽV†‚K,}oæ8Üu’NPTÁÚÂô„ê˜ûýIöõcéœRUÈÄÑ·–+cáÕ8úøeì*L¢÷nUòÆlúìuX?+£ß´+c-ÕHOÙ6RúÍô̽Êx>\IW.RE´B=%S 6Q‰´…˰éœa;߯¶¦•í]3ÛÁ&¶IlõlkêØî×°M¯fs­dS-gÓ-e (aýWÖÅlElæ…lÒlòØbrÙæç°=Èb ÏdÛ’Á6-íK*[V ›S2›JÛƒ¶SñlˆcûÖͶà4ÛSlÉ‘læ'Ùî`ûOûg㊱ted =K‚w‡h!· Ð×áÂ3{}üùʯ”|_ð!¢Ñï¸!Qa€@a{aŒòja v:nl•Ÿ9¦»‹£Vd¶l–†LõÅ·×Á/h9º³ÑÔås#8@Å~ Dv,Fà}œ.·E÷)}Ä-ZùN}Ä$.‚)ל¸¸3Ècts½ Іø(ºi܆õ_¯VCt\_Œ Æp~i‹GçŒqá” *a$·ŽæÆxVm -q#ô¯µÅˆ°ÔÙàu¹ tÂmàòÁ › aÅt^·‡G y f/ř۳ÑWå„óù¨™¿sOÏÇt§-˜=6s¶ƒw;…}s½Èû™ —ƒ›0êiù9.ÿ¸>Ø5:!ªk%Ö-W€Ù~ø$Ã]æý\RA_^ÐG6 "s[=ë:?""zhóc¼xù˜Ž~ÍnÓû4µˆë*nÐ[^sÁéçUº§u„Ö›r•Zô^ñùýó×ý`C'LüM¯ôn¥gòÿ¦«ÏÒ~E_i/¯«ôB“ô¯—éÅ‹ßÓ|ª Ýwò}Òî ÓøŽþrõ2ýíñ{ÚGâí)ôžŽ‘ºKç ÷ÓÎ}Ýteq]‘x“žþšþp³‹ÞN>æ5·è,Ý>zÂõ´Óh?Ýxä mñø-w‘¶´zEKJ§_/ì¥e…ÛhñOé¸=Mt‡v/=A¢•î(}AÏ0o§·-Igmj¡+¾¤yn5Ð~¡/éٻ頯i»umô7—~úŦst–Í;zÁ© ô…½ýtµûyúØû>ºçËYúeÌ+º6ð­âØKk1géíO躜6Ú|zý¨ žžœû€žmPEWÆÜ¦·-®¤íp“vO© _¯è¤ÅöÓ?;h^½\ºÁ®ƒ>VžEûx_¡—Îʦw=chEÏ,zÏ„fzžNÙHoL¤ùFêh‘zµx%m­G«ï(¡ßsEоÉ%´ÒÊ@:i¸„.ªÙM¶Ñ+>ÒV‹i±âô³´*úLWÝ“"bù_=,(U´¸âô†Å•0¢Ëá{''Ÿf@Ï"§J³à;?•O2aלuÃ,l6Mó6¼ÓS°0Ñb)pù’‰>í²ßÈÂÓ/ɘ1…9×’q=.3Ò“qÿG62ƒ’±Ð,º|ÉxaŸƒ¡ÕI¨4ÏÁ÷+‰©ÊFUd"Î}È¡Ӊxk—…/‘,•…UîIx,—…ê×Ið3ÍÂQ›d¤zgAµ2ËßfÁ„|<«³±äs Þyä §12¹ð߆Æ\H³ðäy.ÎÏÌ‚Xfxm2±ji6^ÏÀÕO™¨|™¥Mк”ŠUWÒPþ ×צ"L6›lR0q}2FO'ãbj’qo Wä’á~‚<^®ÉhöLă<9œˆ«®©xQ—ˆ iJ¿|¾(&aùªL˜œM„ºmDÃpìpÂãÐß”ù?¿˜DÖ¤¿ñ&²=Žg«Šc e[Ã6%šíßç(»N±eE²¹d›Á6ÎÖqœ-.ŒmÝ16P¶/GØÎf‹:Ķ&„M'˜í} [eÛN6Ãl¯°EïgÃ>6‰½l×÷°ýûï‹*îb‹ÝÁöd;[ƒÛSO¶åÿŠ÷_­Ûʺ…-j3[¨›·;Û‚Ml2nl×6°¥º²%­g;²ŽmÃZ6c6Þ5lý«Ù.®bK]ɶ}ÛÜålBNlÝËØ²–²¹9°i-a{jÇF;ÒNÿÕCI=½:Œî[ÐM dwÓ«JÑ.½ ÏWõТ/_Ò¦•Ïé;çúèøC/é­?ßÒš¯hQå÷tö¡7ôf—¯´rß{šgûÈÿ‰n”þEÇI¿£Ãç~§Õëúh-¿1z÷‹÷´¯r—¢7GòBðé;Z9‘ _„ßÑF‹y1÷ÄgúS¨0ºû‡è™]b8}z˜Þ$‚Œgßé¡Nœ)¢¿õ @™”.î·5Ô;%P˜ÉY_ ŒÏáAéô›r£„_ /ü‡ÅðùžÅJBMAǤ¥Ñé,‘oÒøô˜¼}¾ ö]€ñ Y ŸÓø”ƼÄqu±<4kÄ1~²Ú¤%P|L_ŽIàþ Uˆc°N_¥Ä¸\{å%ðô•<ª{%!´EV%à?&ç5¢àŠÿt1ò9È@!X¡áJäß%PV¦Ï¢°˜¯ãâ˜â& ±yâè½!9 Üù"Ëëı&OÅbpZ/ë-î”’¥4ü×ËâPƒ R7Ëc°QÓÑBîk…þ(bâDy²GPÂE<—'{+cExLQƒZ–"ŽÐÀ‘¹J0sÒ@`¢˜2e¨ñÈ Š¼ÿÿÜ?݈†P{,Ÿ? û+¦!SÑ|‚ì}¦"vŽv_4‚IÝdë0BÑŠÉàw2ÆŒ¯ú3Â> =$0¸P¬ÕÉS0±JšES ”8ƒYS`÷HGµõ¡;i<Þ†èÁ{ª‚NOB¸.^Ýž„© Ú°ˆd1 Lý¤ƒ]T1ÉJ’êXöNÓÍÇãHÜDÔo«Iºˆ“WÇíÃãáuJ»Š4¡«Œ ]mlòUôù:H\¬ŽUZd¿¨Š–íˆR†I¹:úÇa½¾&ŽƒÞÑñX¨ŒÛ©šà“VÁ‚u8«€?^ zûTð Y ó6*¯ÕP¬¦ ×êè?¤‚ªh HßP—š&t¿ŒCá H¬RÄ`º>&Éã$­ŽÏv (ߪ‰»g”pó—:Öy(a†‹ ‚%ä 裌=ÄÓuTŸ“Ãxe˜(bV‚"úä±õ1Ù?Ÿ”‚‹<Ž˜I ‰X˜e'‡w¤b)Óyð0‡m¹^nÅCA1Lû#Œ¥±¢ØW&Œ?]Âph†þB!Tu áåt!¸ÀŸ_‚¸ôŽŸ|Ý B1@Ë4…àœ'ŒId(8_òŸß/¨ã*l+Ü\‹¯ò•øx«9†•¸”X…0‹JLh®B‹~% ì«‘h^‰U_«1p²<›k°¡®þ“jàW‰ÔÕˆo¬DcY”u«°ýS%¼]ªpA­vÛÉ=wPªÎWÁÈ©'«!º¨Ý«Q+Fþÿ¹jðÝ)ÇÚcÕW¨@Ad5* ð±RÕX®]ñˆ*|ì¨ÆÞœ*ðÏ$÷ôgªpO°繫1ß¡š<æÕp­Æ¦«UˆK®ÆÔSU8SUã U˜"VGrNçÖ@‘«¹jqn[5û×áìŒ|ÝS¦75ÐyVCFµ8XÓ'jQü²Œë ¿·"õu˜ÍU‡Su`¶×‚7¹&·jÈ=PNØÖ`U> Ô`7O~Uã“S-&½¬†þ’\¨©ÆÂÉÕ8•W Å•ð9TÑ;,¬BHO9ÆITâÂ`„Êq}¸ï–âQ v.ÁÑçÅàO+† o1|ƃZT„¦{E°¼\ˆYEH‰(„W1bvÂÒ¾õë ¡q¢qk Qü¬ ; A¹•@|w!úΖàú`–œ,AiG>Žžþç÷XDî¥ÿ¦VÌö¡­¥€-,ŸÍ)M#—íßo±&‹í`&Ûô ¶‘4¶³©l¡)l “ÙD’Ø.'°Eij­ˆcS‰e{Í–qšm]›Ê)¶;'ÙÂ#Øf`{zœíhÛÂclÜ¡l…GØff« aû÷÷­:Àöü ›Ã¿òc³ùW[°yîg[µmú^6!_¶k»Ùîb³ÜÉ6k›ªÛ€7Û¿÷/‡¶±¹z²MÛÊÆ³…íß߯3Úmù&67¶§Ø\Ùì׳ñ¬cÛô?{¸±ÿ+½C/Žn½–B7÷ÖÒ=#hžqõô$ÕÚö^-ÝK»Ni¡³®ÅÑ#|çéû± ôT¹«ôù])ôpÞ5ZÙ8ƒæ»FûË¦Ë ®Ñ‚Mù4vÓ¾Üe´‰Úcúhf%Ý´¤‡ØVN¿»G;Ù–Ò~<÷è–ÞrÚúåSú­K }¸ë%½W£–KzNoX[M׉¼¦;WÕÐçÜßÓÚÖ4÷;zyY}lK?]ö¾æ{Cö,z­ô€Øº)¡ŸG¾£Ó¦^¡«‹?ÐÛŸ_£Ëùèæ”NzËÑúw@½äø¢m~ ÒÛžÐÎ/¾ÑÏ"^ÒÍ£´Ð“·ô?”­yG÷æ‚lÌ{:õñ0½Pâ#­5c”~fÙO¯òæ…¥ô :‹çUzé†óôõ‚¾7D»ô?¡»DFè<æí©Â…D'ôáÉÜØpè-ü’ ½ô»×|0q|BoäEð^úü^<ØüšÖ2ä‡ßØKš—W_¼¤¯u ãAJ?­¨-†¨†zöoQ؆ ÑwDñfá:3Q‡GhÁ4²*åŹ# èØ$û%å%{e¼ÏçÁà"5¸+ Â5MåeBõÇùctK9Y/>¢'‹þãú°ÑsnZÀpÁ4Ô{jC J›Wi :}æ­ÐÄJI=|º©†Ï&âÌ;edéM„J„ê•&`i‰"~“u?¨BYß´ÁüPDìY-üè‘Ǥ šðš)ÏL „IÊÀ4@»9DüÔ„f“|wj€‚Œ¶.%Iàq¤*OÄZ ú⟠†ön¤« å±¹7‡ÙsD1ÛLÕdí[n¬‚ׄ‘S<›¢E°GXŸ ㎔\. ¡Þì ¿ aB¸+ƒ€t!´Í${¹üQ‚‘(\¥p!Rú6RÎÁgkIŒ_$ {A)8kó#g© ìOñã{¥ ò\…Qà/…ÓDöQë bÞq9ìÑâÇ31<åEè˜2k¹0ã<ù±qŒ¶µ”ÄEo.dQâ¸Âü¡— ŠàLåozTNã·ü µE0ÿø:ÕZû¯~ ïŸäÇŸõhÕg¼P~ýŽ÷àEâ™·ôþln,,í¥¹{ÑW©Ç4ŸãZMæ!]ýr„^8ý>cö‡æ;s›NŒ¦ï½¹AÏ^:D+^í¤÷\å‚àÖ;tñ1<+ìûÇù‡ºñ÷ÚYŒir|˜„€µ1xý#«NÆÂß5§›cpAæÿŒÁüU ˆŒÁ“… ¸o‹k°Å?¢p¸#É ÈŽÃ¦Ì,Y¾ª4œˆÃèû²·‹ƒþ”DÙÇÁC;gžÅbÓÍÜòˆ…þÎO‰E׌Ä›ÄbÙH<æo‰Ef[<ôËcÑ”¹?cqÂ/Û-âàm±8͋Ǒ‚8<$ÿ}þ~×ÅÃмìH<îåÇcéx8é$ÀG"!Y h6MÀxãDmO€åõDP·°ß3 Í˱x\2&bàB2¤®&¡C<ÌÉdеÉpéHÆÙ)ÉHrKFǽ$X¿HÂψ$ì9™„;s“@! å“ý*ý]‰X¹;¿&%bôYdÖ'àÜ|òøÅãùxö‹Çáñ˜}$6sã!¥©î8”_"÷ü8 í…jv,-bm‹P**_b°yù÷³1 ÆÂ7$Ÿc±gu ärbñ!"=±x6w‡8ô,‰Eoy<’bñ®-{Ëba¹-'VÅ¢-"ÅëbprJ ¤¾ŸþÇõ¡ì²·Åßv_b›~‘mø<[Ó9¶gÙf2lßÛÙjÚØþ}®@½…íAÛéF¶… l\õluµl5lÕl=•lql‹ÊÙ¸ÊØjJØÜŠÙ¤ŠØš Ø\òÙ¾å²Êa[Í6˜É‘Á&›ÎæŸÊV™Ìæ›ÈÏö.Žíð¿ºËÖÃÖÍVušíp›Í)¶?'Ù²#ØÖ`³gS=Îöê[z(›ËQ6ƒ#lß±Õ…°í f›Äö"€-ÞŸméA¶±l…ûÙ–ìcûOú·àzQ„ìOg@y²8­÷S1 ÄÊaÜ{1ج‘G¹ÞÿJrqbhü¢ˆ°…’¸Ð£ñ$Yj*“¯3yhy¨àÕBy¸\VA£þˆ«"hµ¹n«bž Y¯&ª#¶Cï\4° D n¼ê𛢞A5rM×ÂÜZ0Ÿ§ƒ´åºXpGmkt`᪃æ­:p³ÑEò§IàÒ…S´NyêBÀÛíêQccˆ“.zN6ÁY=¼vœ…t‡‰xÑ4b¾Q9dãI˜"1‘×'cƒÇ|4O0€iþpr¯|žÂam#L_D!rŠ øÒhp=ž%^KT=1sÑ ¶{¦¢ÐÀ †Œ±0Õ+ꌱ¶Ê ßÿáØY+$@ä›̺&!ep6m"ö™YâžÌ$ØyÛ`͇ÉnXˆ^«)°¾c›%úX¿Fhä$(’÷?Ú;^Š–Ø;ÃíVèeL gl«™]ÆSæ`«óœÎš~j œ¬Ì±ö×2t4ÎÁsÍ•Ð'oóúµ+®ú.B‹æVs²Á‰轤ᑹ‡)ìù¾'õÉÇøÁÇ,Wï¬p(dÖ?®Ò2[±xÉ,èЂÍMCDÆÈc×ÏñðI“‡‘6îÈ`²ˆ6šò¥ÑKæ—¥ î …-’ð{¯¾q¬xIîûˆO@ÞÇEaê1ÃŽd=<«‰ sQœ V‡Ý6„¯Pƒ‚ ö…«a UZ²jÈŸ+c]LÆSe¤ ·RÃçq~£2ôæ !òÜ8Ø5ò᪔2æÌæ†å«qä׎ñiJÐ¿Æ ÞаðãÆ«SŠ·ÿMW‹(?å}|‚FkÆè´"ä¿ÿ¡¥£äÑm”ŽÍ’ÃúäQz쨎%q!áŒ,Ĺ1e¢ ÖÏ䯓*i8s!ú™ tÂÿПÌd¡ÚÎ…#­ÒðØÁ½¢Ræ†Ù¸]ŽƒôÍMBزñ-=ÑE’ÉOè+ÓÈË›ÑÑò¢ˆÌ~@ ÔóáXï úu$fÆ\¦³>ó€+ã&÷[÷¦=£gOF¥F-Ý,…EýtH8?V ÐáÝBÿðÐg±ÚÂA„ÏÂ?¿gìÎa]†£ÔÄU!Ö¡»žT |¸ÖË*ávº=¹ˆùÜÏ•˜Ö¡£åˆçiÆ ñrÜËiF_YÂmZVZ†×-Èï(Ãþõ­8oPŽÜ¾VÌ)‡]HfêU O­gŽV@îH;š}+`w¦*ëÉû{ÛŽ_øÕŽÊ•ˆûÓŽ•‡*Ñ­|ÜUˆ1>ƒS¥UHÑ>ƒIöÕHÿÝúG5xî´Ãðf /µ#í]-ú¿¶#ZµgçœÁvª™Îà×ÒFˆo9ƒ¡=M¨8ƒÔƒÍ˜òè ¶ªµ@Ü‘Ál¿`P&ÔŠ[ËÏbrC+|.3 'µaÎàœ{Öµ!¼¦ ™­”kGïï˜nhÇÑŒĬj׺,xÖ†aùìRjúkÍ0ÞЊÏÍH¸ÒÝúfLhÁ¸×Í8ô¸ óôZpn^# rZp6´.äñÚo_‡¦mÊ­Å\½vt¸ÖbÍÌ3Ì©Eƒ=™uP«fpN¯Á- Ž‹;ÚðlíYDv¶ÂñƒéÍx±ñ ¾~¯ÇøÁÖxˆ¾Gî[IKï²IÞa»v‹íh7Ûü.¶7Ø*®³yt²©^c»p•mß6ýËl=Ù".°Í>Ïöú,Û¿Ï;ÚŸaãng«nesmakf«od[ÛÀö§Ž-¥–Í †ílÛîJ6¥ ¶Ò2¶)¥l‘Ål­…lóÙŽå²½ÌaÛó¯j³Ù˜,¶òL¶ãlNél2ilçRØÖ'³É%± %°]Œg c›Ë6ÍÖrší`ÛôSlN²eD°-9Á6rœ­2ŒmÅ1¶_GÙ’°ý§ªä¢µoÓ¶Ït}iˆ{ÿöê¥íWŽÐýÒ¿ésGè ú_ôa† •z£´Ë9nèŽþ¡OòáŒ7»ùѶW× €ë56›Bb+F· cvN*M*+”þ^ :  Úàª(P1´m—‡ï yl T€’,~MQ„´¬ Âä!ñJ³VÉ`ÈO§¦KcöY¸-‘Ź)œ­GÅ^x—GæÆ«ÈãúE¤ÅÈ¡ˆ¼ž€¨NË2€º4‚Ud ¬%‡µd±~ž<ÎO•ƒ%dœ"ƒMxâ-‰{µàÚ(ÝAilT•Âû4Y|íÆ"i\þ-Ú~I\#¿~nnRx(#{ä×\„<öCÉÒ¸Í'…ÓfÒØúX†iRI•‚ ¼îŸCàK P1bx”(†ƒäëá F—K"úˆ.Û‰¡Ç_æüÂXP(ŒÃ=Bxÿ•oC_†î¶xyWW¸xñ™OK½y¡ð&|Ðß.Åø„"Ôê”A®­S¯”â¸v1z^•`À£—•KðûIrÊ‹aZZˆ_;‹QT€ënäc¨Í‡ã‰b<3ÊG¾@ ~¶æÁª½nëòàPŠ¥Kò°Îª »óðls9öä£Ä¹;M °Æ¨ÒvPœWê|˜,ÃÒûyØ£P†^µÅûöÝœç¦üožÿ믓©ëíþ¾ÿï¾þ§Í ó?}ýÏøûõ?ÍÄŒóõÿ¿bî~;-Ƚ+¨ Ú¥±Ê‡YÛXäu·†º]ÿjV!¥T»ù–ž¨õI+ÔüÖXÕ»ëûû©yT§Ì´éο3·_Ù-ZJÝSòkÙTHé?£îÍœOÙþ6l¤Ì%O¼z~'”ªä™ÅôÞÝËpéD~ëX3—9~~‡ÔÌ_QTfô]i•§ 5ÞüzM1— æ«årw§\'lIíq1£‚Wj=š³ì5¼ÿ|€è`Ês†ù‡Çü(ÍQkþTÏ. óF³xjŸª›B°êaÆ!wMw‡¶/u¥ÜvUÐÅêŽ`ž\£„'SºðÕË‘ïùTþ÷y«ºk(Þ€Ãg/É$3vªÙ­Ѝû?ƒlÍê¨s§Þ4®½Ã4îÿû׆(û†.ïÓŠ©ÁámŸ„WÆ0{>XÕ.Ë ,_[¼¿á^FmPL¿Fˇ3å+ï}­¢Úã.Ì£¸‡¦PÞ;O0–^¡?"º3©ÚwQ)¯| ©1Ó7k§W05™”Pt55ñáû×7 ©ê¶‘¡‘Ì“5{Ûó(µ}ܾâK(ã0©€Ú†&81Aûzm-µnyâŸäÔ"¦~¡‡ÕúšjŽëdî?¡ELåçÒýªó÷R|u»µr§ ´î6?¼,—ª/´¸æâYÁ¸?U 牥6˜nÛ©ŸÏ8}-‹ ÜTJ™úL1=©ÌT(ï²:û¡žZ‘3Å@s\có‚›wÖú“”EÁþ‚yuŒŒ¡ÁŠùÄÉ!ë‚ ›|˜W¡=~Äɾ±•6›RÔM™­§F7RÕ7½6'éÁs_'ÆÃWvn;Ì)8X@œð.°s/¤š…®ÕßLðfní>Õô‚8q²nè>@œ$kÎ@¥vßø@œp½Þ?™8‰ÝþýÈ̱Xª‡ßëCqÒ û¦³ž8ùtý‘d˜z0#Õ"T’KœL{(-­OœL(¸w”wÓIªu›Úî£ÄISl}Zqr%óé˜Á¦f¾¶…;qrÅAÄõ=qòÖÁ¢~èóFJòëÍ}YÄÉÙ{†+?'ku¸*¶paò_´÷©']чƭ%N.d<Êóf*t"ô:ˆ“7374H'®‘ßLe'n_F6ÏeNË­r}Eœ0Ž/§Œ6€<¼þrqÒå½»l€8 vžù졃ÅuÎþº4qRy­Ät÷Á`*8ðÈ~£~Ôð<ÞœfâÄ)–w­qò^"=>Eã0EUÌÛ¤åKí+y>JœŒ,\‘g«îÉtØ}¹@œÜ^ùê¨"qr*𻙣R23Ÿ¯ïŒ*qòÈLb³=q²šZvƾ3†ñ7ËŒµ%N$Ãf'\ùÓÚÎ;Ç0¶·ªž,Í þ\{üóqwSýd Z8óió¸Øâ¤b(åÙUâäqa]{À¾LëÆÏ©fÄI{Õç¬ÄÉÓU²ûÛ˜æM:¼÷‰“™Vá’‹ˆ“)™+#GåõÕÄÉî§Ýĉfl›ÎŽ3!L(åGœ˜›øÌ!NvY–öžhŽ 24w6$'û{Ód¶[ì¥ G=’&N=VoÝOœl/^¡úkkóMÞÙWeÀ}ÊŽ8±ùtŽû3q"pÅíë!­`¦ÔÂ]*Œ8™7mùçiĉÅ6ÑRƒ#‰κÃYw8ëgÝá¬;œuçåºfÛ3m”8I ô’¹æÃ$º¨ýžIœ,à–Kž]H1ÍÖËeïl¤:f>õó!NìRlg ¦åQî¡§¿¤ 'ÖéñçDJ)±e‡²x< ©™½ÑëNžõfŽþ‘¼–@œär5NŸs;”ÚxRï÷ÊâÄ‹ÏYs÷\¦ËûZ_ìï(ÊÕ/ìb¨ˆŠëí¡Ä‰æÿCƒ›Ý)Í;åUÇ-ͨ¨È—ů— .ˆÎzL ÿv£í5]ÿimq¢¹xWv˜iœækx€ÚÔòÇ‹8Ù$×ßKœØXú‘/Žú6uAñµIùŒ}©ÚF÷R*?óü…ý`&{ü/;â$Ú=JɆ8±ÔÉÞ•¹ë$5Å÷•“*q²tî]÷Ù»ó(/ÃñwžÆú0r?\µ‰“êòM/Î!×Éð½çö_ÚHͱ,R\Iœd¿¹/CœäÏm™–'r„¹yª/8y*»×É‘8ÉŸ»|æÜ›ÞŒÔ©YAĉKÏAþ;·B©G; Rž'«çó¼Ü?—èêùýŽ8áZ«ð0Tzɺ_âd wñÜ[Ⱥ³uö½cǫ́ÞŵZ]Ä ó)Š[=(˜Rˆ©°‰ ñ£>Ô4íÌ"N\|¾%Í%Næo>{z•Îaf‡feMœ¨/“i'N„^yWŠL÷dÖŽKó!NœfÝΨ%NVØ&X^ÕLfîK* è'|ƒÊOˆ“Ž–®»ÃÈ¥µ^!N.ŒÜI-¦Ò y w¹Æ0«‚,~Æ9dP3[÷Ë'\nöJè‡3c{xÌV'yBÖG|‰Ê7l >ÁTp'l®$NüîrÕ'"zÜÑ7n0\ ïN'=u G¯“ÇmNš‰ôÆHæsÕ*eâä’Õ¥¹ÄÉVû‹g_ a4?­½@'å×|šV'Ï;Ÿæù_Œ J©C¦¿Ž1B'¾âµÝKùõ,ºVbp€rÐwóÞLœxœjµw#N„*Uvk ÆQÅ<öĉÔÜ?fÉÄIîÓ™L¾a0X7ØeLœlâʽ”8¹³ó4×¶}'©ÑÜê'?çr®'œë çz¹žü÷]Oºã/Ýl&N¶d,=VæÃ´ßÚ¨øãN µnÚžwas ©à]ºñ±Å)ÍK{u‰“–§ô‰×ñûƒIa.%TÏ%NlŽO.&Nâwo-^üÞ›±¢¢.&N†=z—u‡R­Qv¦ÓŸ'‡ÝÚªŽÌeâ½¹¦Œ}ìYŸ–ó+@µk¥.#N¨§&ovn%×Á3ÓM̨kýÑœ¿×“I¯bC‚)Ç”L‚ü¨l£«AQÄɺÁšªœiñT–jJû±‰‡©m‹Ån©ûRû'KöÍ¿œB¥ºes[{2ö”ÑÓ‰“Òþ"¾]ÄɽÍíb&$39ÛÄdÞ+¢¾6pɬ&N’ŽÞÜàû8†1”x|q²Ê}‹q¢¬ÁWf±)†!óÇMK2¨WÜîM%NZèó›„3ƒSz÷ª'Gf¨”Î!Nü7ô:} =ÁTî]AœHß¹rIdg!ÅgÎ?öðy£óC}ÇjâänúŸWˆ.õ:Aã­‘ÌL¥ç‘5òyÔÆÙe_㉓ÐÕ÷«ôn‡0A‡. 'Ò/šFæ'›N†+Ý>šV¬ˆ"N¶¼òº»Ã~/õ-¬¥Db y¼Þhs¹'Lò[G·ëÉ”-›³Eã¨Îã™ÛDˆ“ôÀÍŠâ¥Ô»+¥çnNfœÜ:ùûûzª¦¯è½;q¢;¸nÈIÊfæ ³âD‘×àÃ0q"WÑò!ªÉ‡Yºò ÿsâÄÿñ:e±y…Ôª¦+Ú?S7RŽU‹'ÆØ¨08)ó»ü\ö³õöMMâ$N9ÖráæBjq³»øè°73O5ÞÚ”8©Î;+Oœx.<Ò~ãqÒµEaOø\&ç°%¹ó!ëΊÏvÕ vÅ¡ÿÞï`ï‰ÏÄI/ïìPÓ)f¥¿ww$qb3˜›råP0µçUžƒP å¬Í<Œ8áü½Š8Ù'è"?é0S™¿ä–:qÒ‘íø98qú˜´þš'óvu¾µ2q²ø¾\ÀRâÄkÛ[½ù“’™½†Oü“Àórkˆ“õOï¿ñ~Ã\X˜vÅŠ8Qã]á3…8YÒã§éAîƒîÕøÛ'‡K›Oœ„½MØ™>=œÑÓ[swl¤Šz¤¿Ñ$N†Åø?Áˆi›N&NÔ%gJœ4÷U|¨yÀHZZwQĉÄâ·3*ˆ“gN½S‹¼"™‚MJÇCˆ“Ý:Ê爓 ægÊî‡0=›š½¯©¥Úó¿šJœœß"îìÝAá‰wµ q¢w>rg’Ã^jº)„¬& dÜ]¼V'[¿hv'þS’Þ£:I]ÓËg´‚ d­ˆ“Bûw;̃ýñî§'}>§ |ˆ“§ód%…BORŸŠ}Ú£8×Îõ„s=á\Oþ¯'ï‚ÖFl–Χ,½6¼5›È\w:`:£¥šºÿ2|Ý„RŠ?B÷ðé8fŽü´2Y­”ûÝWÊé'r(jÍõ7)iL!rN(¦ðÁs\\fÅ}°E³ås2s~Éçsúë).ýÙ)E\Ô—mšr²O0\jý«~_vḃ¹)yÆPoV,5ò b¾Ï(]µO7‡Ú±¿×ñºëVJŠœwj²StqJ®ü~*ê€ÔÉÒHª×¢>O3Á™ºc,Ö´íF>õÉAâTÖh$õÖi®„Ùp4¼fñ~W/jꊘ í…)TÑn)‘ÅQL¼õ°yÀé<ªùËÌ3‹*¨ðcÞWS·–3.º ´û¾|jÑ‘gëÔ*«)Ï„%¶ÇdË5Ç£æ‘ãë¨gâBf›s©ïÞÌЛ™Ç¼HsŠÍ}‘LµêDNÓñ/¦îí¼Ò(ó"‡yÛw£ýHz%åóIð©ó¡,jÿó%žzd2ÙâÃ_¦Q‹ù#{–&PU/ܪ£Ç§3‰S3 Û×UQb”Jäà†\jÎR­f‹“ÙL“é\Ç”÷ÙTÿЬˆþ½Å”ê¾Ù†3²˜NçUEkj(?ïËá_Îä1HË“ÎMΦbó§VúÏ)`ö¬QúõØ1Œæ~öeåwêiðîi ×r¨˜ß\F.o ÓW£ÖÓdíºä¹ƒ³òˆÝñ·wJ(­²cÑ‘–YLSÌ­/ãŽÔQ;dŽ8Ž–3òßZ¤mßçS{vláË­aÜ~œ©›GœD^ëvn^"s`Îk9mâ$~|X›;qâ°P?&ŽÉë2žø3³•›ž½Ä†8á]´VÜ:-9dXù"8™ïê¹Ò‡8ùˆB&ìK2ã]’sX—8¨º¦÷ÇŸù©²7Ž8¡ø½Kn^qb´ÎžÄ_'­ÝnMfAŒ!3»À8aöuS=ÄIæÒ£Þ« ˜Þ‰Ÿ$NZ–Ý&N£kký#© ÞN—Ö'z̹¹«G"©Ê01o†8É´W¹°Æ‹ò«ÝYîGœ$¯Ô䵋bðD{qâ”?}ÜÛ Jk¶ªk½g9“¼ä€ÂâÄrKÔÙŠjJ±álÓ}¹2æçì05/â¤Í"ñV¨G.U÷kMç3âDýS4¯3q2àíú¨ì`1U¼—{÷˦v‰qŒq"x^èÄ‹,j{Ý÷­þ›3™ðoC.¶Ä‰ùÇ®haâäÒÆ™Çfk§3öÒÇ‹ ‰“}fï5·';¶Eéÿ&Nªô–ÛKœ¯¯Ñ‹#N<úÌŽ¨ÎÌbŽ~S?Mœ4Ægí!N.óŠÜÛœ’MÑÁO'⤳PS#Õ)Œª¤ Ýß­u§Vé%è”'¿ËÇäõ2—œÖÌG!eíxÞU›8yÏl=–Bœ´U·Êb"ò?Dr'†ûtŽ”3-÷4Å7|ȧnÎ-;×—SÃ<}þä¾îͧ^ïnÙDœHÝ­×ÒS(c½œÊmKœ¶íÜÚëžK¥ßvN˜•Ǥ«Nñ•$N£ýöÏ N”2¤O}&N lv"NÖyX›x'ûåÜ~ŒÛ’É´ŸÞ"Bœ˜Ì^BŸ/ ^ù±â•N:³w F'†8 ½Ì_ÖïšK¹N^_™Ít0‡Yß±Æßž8 6×ê8lÅlé]4¥ïpåtCé–qrH±ƒwÿÇ|J:b×ÁkÄI×â†ÁRùÔ¤ßõOèDæþ:ý{¿š«©KŽö'ooîüǤ¼’x‘8)ùp#/•8i•zÀŸ™Æ„Å%‡¯$N²¾*4k'~÷†µ‡’™°®™W¾¿­§\/Üš8âOIþìÐMþëdw£Ñþ'†ïÌùŠÄ‰òrß륲Ȧ«-AœdÏàþEœp-ɾùΘ¬;ó„ºÖ'ÔØ±nçòHJJ·WoZ€3µNöÁ3š8á{ïåg$ÕñÀeÜ}â„Ú•Ú¸Ú‹ÚîÿØc° … ¯º3kÈ>Š)5¡#§'Z»ìÊÛTP–ñƒ{ú½Êótƒ‹MÄID«í!â$÷áÑýŠeÌs§C¯''=U±?g'•­/íÍó˜7´fÿ…çÉÔ­Æñ³~ÅTEêð×'¯r˜yñÓæÎ!ND¹ÎÞþœEµwÄ7'3éî=/Ò(nAá·û‰“9۳ƟœÎÈÜÓÈ N:Æo3q'NŠöŽn=•Íx /*œLœ0z5Ö|‹)aºZü'qâ'"}ß•8‘«yìèGœ$¦ç,ýLœTŽä'N¨h.U]F ™Ó#+Ý©0ÑûüñÄÉ|ÅN®™Ä‰õöƒ¯ÿ'›ÛN¸]!NDšµ·p'ÃáÏ6.ÈbR\½Nœ˜©ËÉŸù]Îä97 E~ʧöoª2­ N ­ \ N„VÈž÷´HdÎåó6¼!Nö~ïV;Hœ,þlœñ=1Žá±ZJœ(œwÙš8ÁõµîYiŒ¶ÞþgÓˆ“ד/<Í(¢BwŒúò=™YÖRçùŒ8á²ý¤“ÿËŸ¢w«ö’ýÉö  ‹¯91ªß¬ÿ:‰:½kå… Fʱ/Ú˜8ÑŒö¿+´a+Åœ¤xjM®3ÜU ˆ¦Ï7F½"’âzúzvägÊ5TöÃ4â¤rcƒXðp$u(é‰ÇÆ_dÝ™±æëÝU^Ô7™ÍfåÄÉ%“ßø´$ŠùÅ=i¹q²Eksæ‚ *çȸ¯<ÛË÷Òƒ‰“”­{¹JË«)«¥zϯ(•1+Stð'ñ+¹ÇlÊ¥ö:xÌÎc÷ú¬÷#N6 K¶%N”Æ×;u¾ÎazÜ•Ñ!N2öû Næžà_¾5“9¶øp8qâ½e,Ô˜81aÚg¦—Î̿ک²…8éöQ©ê]ŸK‰•|\?>*›É“0Ú)@œdhf]I NÊ÷Øõ™g1ц§klˆ“ó§¶å1úËÕ¤eS§C™‡Of03Ï+¬ë!NzÍd5²Ý©`û¯Cáĉ½åŠé!¯ ™Î}¿=³,¤L—õ¾I&Nµ­½]Buª÷}ßo“Å„½{¸¹–8Qzl7“8ÙnùöEÞç|ʹkë§hâ„jN“I!N:¨P“aËD&òwâ-âä¹ïºÉ‡ˆ“Þ”üë_’ãWO£‰“À[Ô‹ã9Ôóþ¤ð‹ÙiÌý}_L¥ˆHîß÷‰“–ͺg†“™CŒ“øâ¤P(ÒH÷§?¥o¹­9ý¯“ò•ašNŒ×ÏM”~[ù\{\ÓºÈ÷¹:qÒ‹ÑLEâĵå©Y€©3àùñ´1q2g>½ N¨¥þmÎTxýÐ]â„ǯ¤Æ1îñõ/÷'Jo}O&N.wÜvÖÍMcr„ùO|à/¦ ]—¨&Nœ÷­jŒø•ÌtöÉUV'ºqîÊå}Â/ÍçdÝá¶ÍýFœìèT›ó× ßql{¦ļóå1'N¨YZ›&'ÔíSžÖÓc»Ž]ãþÞïè çU’ý‰›n›…›3•*È«Dœ,Þ.ñy(’Zí1°ÿ7ÙŸìk{+EœXŸ0hÐ%N>(IÛ÷.b<Ò*_¾&N”£Û¨›VÔ>‰û÷Ìv”3[Vů” Nî®q™òµ¬šb6%ݪRÆZ–8Y¡ÕbMœTË}pö“Ç|ïøº7™z³ !i-q2âY.\Ô—Ã|Ïu>ó5­’j©ÎŽ Ê¢6Å^à‰òÌdÔ\óDˆ“EÒÊ2ˆ—»ânÇ&§3û<ßÉÎ%NZ¶¹&ß_Gö±Ü½ Q§³™7‹.ó·'‹N¯©þ´§˜j^™}ñüœ,æ“Ð^o%â„öíá!NÈŠyšgdSßfÝÛIœPñÖ»¬ £¶¤<’½¸Øú~Qçîâ$+ÿa˜,qòÆ~W¦5ÙÇmû2Ÿ8éŽù6Ž8Ñò¼¡´Ý6‹ È !N¦vÞÉÔ'NŒóÄ+nɧҵlÜœ‰“ïvÊ/$NÑ„dmÅmÎÅÄɵsyúQÄI÷RÍ)oÓ㘦ŸÇæ¬'N¢U‡ø¬ˆ“Æ`^Ý£yiLLiÍó+ÄÉŽozÊÄɼӋlœG’™§ß—%'R¿'èi÷§æ*r›æ'TÒÒº+טàí;¿)'Ñ;´†’Ճי§ëäPÆïÕ͈©½ 3%g:0Áïyˆˉá©[«ˆ“³ÉéÎÎTò‹R â¤öíXPþ·HªS­m ßYwJ7î4\áE)èlö|žŸB-3äwx¸,ŠùöyII'qbòyÈåe•ü}ל%;ËÓm«§ßõͧÂ^Ê5Å'=c 7šT‰“Ä…ÄÉÁs©éy ;§ÌÍcöŒ†t%'i#æb}û‹©ýe×&¼Ía7ÌLœ0U2šRÄ “%|Ð`[&Ó5óGcÇó4*ý¸µÙJâDÀiÍ8cƒtæj¥sÔDâ¤&yÏ‘8Yä»nÛ‚èlæh—_w2ßÁ8ùñGxïâ¤\{´ìh“ž}Âû¦ý‰8ù–˜d)?I-šÞ£‡‘›µE-†8)\ô 5? ‹ú·½ƒß;“¡z¿Š'ï§'¿eóÕôMÒ%éi‚}ÄIé]ª]r©açÞÅÞ±ÙÌåf打¾–j©Ä ÏMËÓþT㵤ëUqR­bþPœ8yÑWüÃ&;›2ÌK«þl^Àð}YÀë²&Œr=ÓØ$:ß’ªö—8¹ x+ÚÆ>‘ñŸ1Ô»š8±ýù¾©„8™cùðÓü8FªàhÆpF+åÀ~8–CQ^,ÍW’Æ „9q0hòõkz5c£wo ÙxóÙlâ$¬Z¡@}ÀŸj¹=r·ä¯“bó¥n'fDª3«TO bŸi¬%N¤6úÖû'Á¯}BLç;0Ô=«{yÄÉÌñÅ÷Õ’ûbGµvþ™ÎÔ@RÿŒ[Ä Ïãݽ‡?GRʼç^‘ëÉÌq_r—yQ—R=7óR¨h£«óÛVD1…»L:7'Ó-¿{€® 6-á ©õ-g®Ë??;°'Ÿ2iñ/×!N2_óWü_Æøý ÖP$NÖkç'NL[)Bå17’Ÿì N,Në‰'íÑ3Ž:|Èa”´ ù'o&QR†Äɵ¶óSsˆ“OoÖ)]ëM£d\,̈‰ÂÈ3·¦¥3“ t'|3L½(â¤Ö©9R'.›™µ³H˜çﺳuÆÆÄÉÀ»¶VO:‹1Ös?ŸAœ(%jÎc" d¢Är²)礣|Wˆ“Ú+bT\Â(*õ’FÚlwJï™fÀ\âäUë¹ËÁ½…Œ¦Õ¾Ò?ÄÉŸ­Ù.ĉյPÿâ¤ã¼|û²%Y =ç6ÃCœÈ|¾ØvìG9ãòØï‘Õ|*bâEÉëÄÉ99—´ƒÄÉÇ ¥Ow—$2¯í¾ž³"NœöF†V'õ­š]…q̲1“á§ÄIÄÏIï㉓‡&hs+Mc ÍÍRˆõ5.w®'ÙÜo>¯åOaÎÚ9Ö!Ny}Ò>ùSårÑ ^‘u'¤Ðãqr댮á_'Á›jò,§1ª¶‚9ÄIeÙÎï‹}µŽ•\±×ë#‰“Ú/yÓg×ERR¹ûÆ­7r¦œeWÓˆ“7‡U5fФ ]äx[‰f‰}á¹¥^”"ÿ× 'ˆ““ÍêWF1÷¬øâ쉓H™¶%«© *qAEÛõ½å̹¤ç… ĉTó>Õ+%ÕÔ¨!÷}[í2Æ> RxH£ŽÚcñªg¹oÝ躸ä,q‚Ï=&'y —þþ¾úåéæ}ÌaæMùrc#qÂçö& Î?‹ºâÙKmÏdÔ×4=AœÄ_PÚü!¦€rÎuq9h–ÎÔ[ÇÓJœŒX¢®eM.eœôîÚcâd¦œø£žþl*2Oúà®b*6xÎëUÈbŽ›[g#Nºúò¦È'^ߤuºˆ“+3‰›Ð«¹‰“ÆãÏ= gºS\ƒ·%Nº»ÍµsÈbÜ.÷¹öª£vÌÎå"NbåZ³\†‰‹€E×+‰Õ°pŸeÄÉÊ ‡¬Ý–&2µeòyS‰“õÂï6'W´¯Ç1L釋ÄI¥Qí-Š8±0q;_–Æäí ý>ÈWL VÊ•'}ßnûLLažš Lÿ{=y½É@õ£?õ<Ë}¬‚8¡Ngn91og+3rÄÉ„KŒ¸Œ‚˜±ãw–Ÿ&N(™®ˆ¿÷;—̆/ÁhµNØGœôœM#N\šæ6Np¦êïn·­'NæIœvüIYËç¨.øÍhzÞÜÒëàEå?þÔš8y6ÎrRŪ(¦ù›Ì:3âÄ´×ÌpÇü JÙ}êà›}åÌ5®c§‰“VãEG¶'}!ÃÉ:eÌ® Ðo·ˆ“w¦Ÿ&5’û‘/«NþÜOç1û7-|ü4™2¨·çÓ Næ=~ògʧFÿÁòB[âdèø•×sˆ“ù—[¾>!N^ü´]¶ˆ8™):‰É!N”b”¾iÏHgŽ´-Î'NF$ý˜NœÜ»¿‡/&>›qRp-i N¨¿ºÓˆmç‹" ,²˜é"»C|ˆ½Cé×âšò˜½“Ëkbr³©ìø‘âýĉÿ¡­)kè™$Ÿ1u§”ÆŽ®œHœœœW Cœt}]{fq!5ËBê 7q²÷OÞHW u¾l¬iÁÒ,f­-ÍÝEœt5 …ø^Î(U[lÚý3Ÿ*Ô\t"–8yñÅ×|qÒÓ5–2°,‘雵ö˜q¢êÁÍÇ'z—‡^*cîÜÞWTBœ,7¼Uó.4‡*º%Á«]‘Æè>Þ½ô&qr*{m@q2tEèã°P |ìNÇP_=Õjû"ù½?uôÌN!“×G˜àÕçÅÜvbô‹ýub¾êZcËÔ FbÜúÜ ¿÷Å‚+Rþþ~ì9e³(Kâdyõ>׿דe¿ÎÖGRÁ¬&K«;S‰“ ‰“ÄágEï#)=Ñ-Æ·ˆ&(ßdl‰żXÆCœìáÞ¨\¸š\ORÖOT%N¾yò›WAm¿:×rFãæíƒVĉžéï×ÄIäܲïuËÿ¾ÞJâ¤6bÁiâäP÷'ÚyÌ=_ÃàÓÄÉ'®Û³ }‹©ÂÎ÷ÓU>“¯Ï™'ÅŸß/¸p0‹šþÒÙþ O&£ÙµS”8yå¶Oz qÒØZÚ93ñm‹‹"N„õéiÕιÔЈÃLÛ„lFE%7 š8±Hï±!NT†í Ì,³¿‚®+‰‰Ï»Ü”ˆ“†iOç­Ì˦&ÇDüq NÞvLX³.ŒÊ´3®t˜J®'ÓÌ,U‰‘÷3O¬yZÈ|Ø.daWHmܺ|É9ŒÛØ” âä‰áãiX–ÅzÙ›UOœX®¿à48Dö÷žüZá¿ò©tû‹ ÷'*jc$ò©ØÛ±Ÿœ™áW· '¾ݵ—ˆá2«¦³åqLÐgIžhâd†ù¡ÈXâ¤íCOê¡Ê4&«l¤´„89½mºÕâÄäÒÉÍ—DR˜M-¶?%NÊNŽ{çOmm­ýëÄj[íæ;NŒsáí.Yâ¤Øß ûLƒ˜_Ûû½ÿîOZç^)"N4xw¯¶&ëÎ[ÉU ‰/ÓÞÅG"©¨îOÏ·Ê;S.E®I&NÞåñ¤»¾‹¤ªD<æ­åŠa\yoò«'Eu[[ÛrS¨ ¯„³œ£—¦þâäVÚÊδ¹Tæ±8Œó+g:Ÿ^Ú,FœÄ÷¥«‹«)ço'ΞXÆü¼=©3’89<ð¡Â“܈ùí9ÒGœpåzK- NžŒÏò˜Jœ\~Ó¹Ux ‡‰¹¾ÁE™81›hŸ»ˆ8‘íoÍUÚA®']"1מ¥Q§Vñ¬‘!N’Kôå|ÍÓ™N%ဃÄI‘‘ÙâDªñÐfîÄlFÃíƒ7q»¡_ýÛÎbêšÎãÅÚVYŒV|55Ÿ8ùQ¨ù-¹1™9’wQ9?›š·òÓ$âdËÅ®GÊëèU¦ŠŸ¸S®FK'¥(óÈ}RÈLßV>h_Hi¿éÍ'N–>ºS¼8Ü2V<Ç1‹1‘º„4âD`ϵmÄÉ—û3çSËö6 ¹'œó'œó'œó'œó'œó'œó'œó'œó'œó'œó'œó'œó'œó'œó'œó'œó'œó'œó'œó'œó'œó'œó'œó'œó'œó'œó'œó'œó'œó'œó'œó'œó'œó'œó'ÿoΟüütœá g8ÃÎp†3œá g8ÃÎp†3œá g8ÃÎp†3œá g8ÃÎp†3œá g8ÃÎp†3œá g8ÃÎp†3œá g8ÃÎp†3œá g8ÃÎp†3œá g8ÃÎp†3œá g8ÃÎp†3œá g8ÃÎp†3œá g8ÃÎp†3œá g8ÃÎp†3œá g8ÃÎp†3ÿ{w,Uy®}¿×³Ö³08Œ¢AT@A·³bĨ8Å‘DPÁ0‰²UPÁ ˆ¢AƒsÄ( œÐ€ Q<€ˆŠŠ0à€"jÄ|ܽŸû\×÷ÕûžúªÞ}Šë_•:¿zγÛÝe/ïîÞ«W+¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”ú?Tï~ý.êu~íEýjÏï×»K¯^ûöî·_m—®½.Ú·[ëšÿMÿŒVë;°M›õÿ÷€¶ml]ý¿´n]]·Ú¶> Òzÿ×£më[TiÕúÀ6µ©ìÜêÓ?ÿ¿ìò~µ].ÛyçJ¿®w©íÓ¥ûÿl_ŸÞë7vëöãWú¿Ù´õU*Ùqë¹å¿îuÑ…§_Ü¥ïE§×žÔ¥Ïú¥lýÿ¦¯ÿ_úÿWZ öƒ?®TŠæi‹µuuKû^]ºÓ¥ö¢î—\vUú¾)Kÿ³­å/.ºà’Ë.L+ÛÛ?¢någõ»Àÿtë•&ëÿ÷£ãO½¤ß¯k}IúÿüÿëÖ—6ìù?ÚPü¿ÿï þÙfýÿb»^—\Гþÿÿÿûõ_ÿÿïúgü—ÇëV­[ï¿?ÿVÿõÿoÿÿ²ã¿íúÿ»Õúÿßç‚Ë.ê}QŸÚ.½ª‡z¬î¸G®GG—úïÝ&éÿþ÷ý/˜RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)¥”RJ)õ¿Þ´õ¼þÿn²þÇŸ~Òñ}.¼¨¶Þö¿¦ëÿ×ÿ¯]¯K.èi !ýPó¢Riòÿýfà·Wÿ«õî×ï¢^ç×^Ô¯öü~½»ôêµoï~ûµïpôqçsÒÏöûßôÏhµ¾ƒÚ¶]ÿhÛöÀÖÕÿ{@ëÖÕõT¥õþ¶jÕ¶më¶µ©´j}àþ¶­ìÜöÓ?ÿ¿ìò~µ].ÛyçJ¿®w©íÓ¥ûÿl_ŸÞë7vëöãWú¿Ùùï¿¶K×^íÛ­Õÿâ?ÃþئÍÿðßÿm÷o½Û6ôïýã¤õAî@eçÿÕîÿ¯6òÿößÿöëÿïæëÿwzm—>v¹ìÂÓkOêÒ'·ÿïoT*ÃòºÿðûßÍCó:ß¼þÿ^×PÿíÿïÛº”=.«Ô=8žX;§õ=Öÿo+ûÿÿºwß^rÙ…]vÑ…'uéËÿöíÿ¿MåÿóÔ ò?[TJ)¥6Þlþ¶K#ñ¤õÿ3&7Ìà䯾‹¼c_GÎ øfò·äk"\)á>ä“¡ßùú¡ßÙÜŸ¼†<(À+É—çðÛäc øUòIž@ÞŸ~Ïgȇ5¨óÏÖûÀô;« [»ô¸¨I~“|vHîàeäžë3’gçX?µ€ß^ïfÉÇD¬¿J>©ÄžçÉ{6¨Ûs ýÎÇÐï|Lú›%¿œaý„¿°çðë/¦ßßܼÀž‘ölá»#öOë×—p‘~g{ÌÛSñfÉ7&+µ1g3ØŽ‡?V0öÌàß“c€/"N>!‡Ÿ%ï^ÀãÈM"<„ü ¹W ÿ3ùXšÁæ>äUdŸÁÇÒ 6÷ ðJòy9<‡|H¿@þY„ÿH.è÷¼…¼EšÁíÓ5ƒ7|ÕÇUs⊠sâïä_„ºycþsÀþƒr¬ÿ>ÇþXÀý ìÿŽÖ»Ex~Äí´)± ùˆuûMe÷óé÷7•Á/“OØÿlÀúá9ÖÇ‘›ðû7‹ðïÒïlþ'ù´{ÞInŸŽY÷àd¥6ælÛñ0µ‚ÙÐ2ƒ'‘· ðäísø>òæ|ù+ò•~—|n ÿ…¼WšaÇ­÷¡øäeð•äwÉøar£H. ¸–<ŸÜ.“ÈmKø9ò‘ 4ƒëCö¸j˜fmûô¼Íç„=ok”Ü)`} ¹{‘ãv~Y`ý£·sdÄú_#Öw¥Yõh‰õ¥¹kû#`MòÍ•º=æO+ØsVÿ5Þ]ü›€=_‘;çðÜÿ¬Ó ¬Ï(p;Í"ÖG×Düì n^ÿ|½·«h+e3ØŽ‡S+uóÀ<‰¼UßAÞ>À÷‘wÍáßTÀW’—’OðrM &7M3ìøôß÷@ò rm/!wðä_æðGä øMòÙ~™¼[ ßNÞN3¸^d¥{*u!ór§ 3c ¹{€¿ _šãgßȱ~Všaæ¿’÷ðýûw+q;ƒÉKÓ<³ÇO%ýžæÎ•ºÛ1Ï%–aÿŒ ëû¬?I>8ÇžGr¬ï]ÀOسSÄú#Ö\âw»¬ÄúßÒïBúý5ƒÕÆžÍà2¼/jžJö¿•™g’[xºdðëi†™÷ ðˆ€=kÈ=sÌ›•9ÖÏ+°þ1ùâÿ#bÿ¯Jø?h†Ü ½ç“Ë5Éo’ÏÎ°ç™ ë‡¥ßÓüXÀú~9üdŽ=~N–ydÛ\I>5bÏ´ˆÛ9¦Äž‡Èyz_ýätüj«=›Á'gx|2Íã“iŸLóØÜ À·’¿ û{¹æWÉðä"·?#û9ÒæUɧÐ<>%ÃkâS2¼&6ŸžÁ Èí<–Ü8‡ï"7(àîä/È"üùHúï+5ƒëC'§çp>V‘}ŸœæqMò~/Íf³¿—k~„¼MßX`ÿÇ´~ZÄm¾±çÀëÏ“Jóø” ¯‰ÍþšØ|T†=÷gXß-`ÿí{VÑz—û—åX?£Àþ' ì98bý®ˆý J칵Ğ/J­”Í`;6O¯ÉN¥yfîLžNÞ7ƒ‡“x4¹i#oUÀ}É‹ÈGDx©€ý|.µa³Çv˳á®ócuò/Òó¹šdÝl^Eö×Íæ×ɇx¹IŽÛ¿*Çí¯¥õó xyLjýEÜæÖ%|=Ùÿ.{:Ý—Ó3ü-ÜÜ0=·0÷Ȱ¾"Ýs§Ï&û{æGÈÛ¸»É;i+UÁv<1^&P·L3ìŒ ç™G×}›ý}lswòÛäcrøU²Ï0óóä=#| ù3òñ%<—¼oº/g®÷öø>òæ| ù3rŸHî’Ã3Èí5ƒëEöxþmÄløq‰ùqùËä3Ò s÷!HösÏÈð>¶ùŒôšÒüdÀþæ9<2Ç~Ÿag¤æ{6‰ð·ùùÀ¾³Äíì^+Ûãp=ÿÓvÈšä/Ò}1ÿ2ƒ_%°ÿù€õ=s¬?žãö·(°~{z>¡ÔÆœÍ`;Ƙ #\K^B>µ„§‘÷NóÌ>£¸{¾‰ü5¹kÏ#àñd?ïÚÜ‹<‡|Z?AÞ)Â$ÿ¸„/#ÿ-ùìôß\÷`ò*²Ÿƒm^Cöó¿ÌKȧæšÁõ!{Ü®*0?ºDÌŒëû•ð=äMÓyFö˜±Ë±ºûUênÇü­ÁÉM<Œìç]›;æðr“4ÛÌ÷X_[àvzEìùgÄúù%ö¿Pb}ŸtöÙÞÓ6¿™î‹ÙÏÁ6¿L>!ýÎf?ÿ˼_ŽŸ½'×9YJÙ ¶ãÁÏ6û9ÀæWÉ~]Fóòþ4«ž!ûuÏÉê®5íž@ö×—ækÈŸ‘ð£äåð•äwÉÇðdr³$¯ ×–ðr»t¿:Ú}¬À‘·Ëà{ÈkÈ>›Õ†Í«~°Ù¯Ëhöë2šýºŒæïiݯËhöë2ÚãÄ®ËØ(ùò Ö¿§u}i~•|`Àž;Éß’Ïʱÿ¯9Öw-°~¹á>ûçÒúa%~Ï%Ö÷K¯›íq»C¥nÝ| ù_ä‹Ò<6žág}6wL³Y¯ƒÕÆžÍ`;¦ÌÿŒ¬ù.òjòuìŸÏ1_@~ƒìç›ï#ûùSöß"ËÝ™<—ìç‚™‡“ý\0s?ò[ä£sø²Ÿ?ežBöëhšï%oF¿s/òûÉ¿¢ûbL^Eî’iׇìqØ0ÇüèO^–cf´/0Wf“OØ3)âgw,a?Øü¹Cz ýËôXh”lÏS}}‡ ë×ý\°êñ°>¼sŽÛžc£ë ¬—?Û-b}yÄþŽ%öL)±Þ"Ý—_exÝlö×Íæ£2øñL¯ƒ•²lÇò ³áŒ?I>8‡§‘ýu³ù òÖ¾žüòe4·ü½ßsi†KóØ<ìóØ<™ìç3›’“OÉá‰dŸÇæQd?ŸÙÜ—<|ýþ÷’×&w¢ûÕ‰î—Ú°ÙcÙð@šaæ,Ç,¹‰¼i,ð³k üìyësȇ”øYï×¼OšaçÒ<>7ùÙf?7ûÜ4k’+ë~>³y.yß{ÆäXÿ!ÇíøùÌÕã¥À~?ŸÙ<5bOÓëÃJ¬/'Ÿ˜îW'º_ÒýÒ V{6ƒíxðÏê˜ýºædïÔ<•Ü.‡'‘ýÚRæ[É_ýºæoÉ×ÐÜú,ù<šaæ>äÉ'dð³äíÜŸ¼ŒÜ>‡Ç’ýzÑænäùä6žBnB¿ÿUä÷ôùàz‘=ÆöOóÌü Ù¯kaö÷NÍ sì‘cÏrÏ~¯ÀÏñ³ÓÈÇ”ð«%~öÀô>ðyé±Ü,ùyòžü{r øÙ‹È/ì9 ÇúíäUäÓ xrŸm±>0Ý—êñRb½ùòî t^´R6ƒÏ§ÙfL^Jöïj1O&ûëNóòrÏ^I>µ€§‘wŒð­ä/È¿,áWɦ¿×v^ïæxy%ÙÿÛfžFÞ1Àב‘ÈáñäÍ øwäuäÞú|p½èü ïšýs´ç§Ùì¾-Ͷêñà>äÉ'äð³9nÿð·yO=›F¬÷ŒXÖ.±þy›4ÏìqµeúýÍ—_¯`Ïá<.ÞMÜ“<›Ü2‡‡å¸Õ´Þ1½OP=^ ìéáçôÙ$¥ª3ØŽ‡"fá%<=¹ Íisgò\²¿?lNþ’|r€'o‘Ã×’?'ŸSÀÏ’ð=äæôû?BnætWº_]é~™§“wÎàádÿ;´¹ù;ò99ü Ùß÷V6{œïQb~Ü\b~lžÎ±ò¿Ý7J¶÷Q›%ûßkÍ×? à?ÿMþUŽÛüòAüpý1âw±þ9­_RÂ_—ØsFzmÝ•îW× ï{›7ÏàÞifwÍðwèêñ°gzÀú±9ÖÎñ³þwh³¿ï­ÔÆœÍ`;üoŸf?É|/ÙÏE2÷"¿O>$Ͷ 2|6É<ŠÜ$ƒï%¯%÷ ð²_ÇÃüÙ¯ãa¾žüÙÏë6O û÷+˜¯!@>4ݯ ×»m~ˆœgðä7ÈGØ¿oJmØìq»¸Àüðs‘Ì~.’ÙÏE2û¹Hæiž]f–Û>›Ô,yq¥î6ÍÝ2xy†ý'쟰î×ñ0ûu<ªÇ ­ŸWàgçXß;bÝÏë6Oë—X•¼Gº_öXmœî‹yye{NÍàIöìà[ö|¡ó¢•ªÎ`;ÖÑl8?‡_#ZÀ’ááäF%ܼ8Ù>¿`¹;“§“ý5¨y8Ù_ƒš’W;åðr»~ìç^™»’_$·¢ß$yer7º_Ýè~u£ûeö÷ Ԇ͓r̆ñ9fÆö|3ùÛûÏJ3Ûü¹s‰=ÓKüì¾é}ã‹2¼5O ûkP³¿5ûkÐêñ°>—|X?–cÿ~nsyù”O$oYâ6”X_H><ݯnt¿ºÑýê–áœ2³ŸS¦ÔÆœÍ`;üï»fÿû®yy¹6‡ç“}¶u£ÙfnáñäÍhn]Mþ4¹;ͳî4ϺÓ<ëNóÌ<†Ü0ÀýÉ È~–y,¹q×’ç“ý<,ó(r ß¿+ùErs}oR½¨úœ)`6ô!0?KóÌ<#Çz³û}¶uK³Í÷×Fدùlök>›g’ۦנÝižu§yÖæY÷4ÏÜŸ‘ÏðË?»[Žu?˼ŠÖ»XŸAöó°ÌÉ‹#~ö”ëÉ[¦ûuq:®›ýŸü«Ôƒl_œŽY;ÖÍÉ‹ÉþüÖ<Ÿìßch~r¸+ùòqüy›_N~›Ü¶„ï oŸfÛ¯3\ Ú<–\fp7òrrßÏ#ûyXæñd?Ë|5ù}ò!¾[çdÕ‹ì1cçØûl˜KÞ7=†.NÏS›%7 ðˆ€ý+ȧäðÄ·Ó¦Àú(ò×n³kÄú‹äæ%<ˆ¼$ýÝ÷×i¶¹ýºÖæUé~™íÚ›îÉö×xjÀž¦9ngÙÏÃ2w,°>³Àz‹!¢s²”ªÎ`;6)1® ÿ=¹G:¶ÝÉsÉþzÔ<œìï›’“ý3»æùdmmžDÞ*Âwsú/ ¿DÞ%ÍéžôüÃ<‚¼†ìŸm4O%ûóóLr‹Bþ„ÜKçdÕ‹ìq~f‰ùñ¹u:'Ë?¯×(Ù^6Kös²Ì×ý}ãêñ°ß?³[=^r¬É±ß_[›G×øÙNû—Dì9µÄžIä­ÒkОéXn–܇ü!ù„ û#7LsºgzþánœcO-y>¹MýS Í`¥lÛñà¯ÕÌ?ðŸÈ>§Í“ß!ûç‚z­÷qxÙ¿SÍüy§_Oþˆ|dßG^G>¿ Û'ûõ¢Í$ûõ¢Í7ýš!½éù‡y0¹a÷ Ï"ïà¡äÏuNV½¨gz­æ³çŽˆ™ñ ùÌ~ªÄþ]Òl³ÇŒ}.¨&y¹Iß›ž÷™7 øÙ^ä9ä½søVòä<ž¼Y¤ÛøüzÑÕ㥄_+±ß¯Ò;=–k’ß$Ÿf³ùò¶¾’ü.ù \3X)›Áv<øçgÌÏ’·+à›È_“ýÜ%ó<ò4Ã^"ûkÐ>é˜u÷#¿E> ƒo'¯"w ð2²ÿ­×<›ìŸK6O"ûç’Íב‘ý~™Ç“[§ûuÉz7­À’·Êàë2Íàú=&Î1b_T`½E>:ÂÉ~Ý(ó°³Ê_ƒöIÏçÜÉÓÉ;gð ä¿‘J¯­Í§×£Õã%ÇžÛs¬oWÀ÷س¦ÀíôŒXŸMnYÂÃJì_î£=ž¦ßßÜ£‚õ•ºýæN<;Ó VÊf°_ÐlèàñdÿÎ"óÕäud j~ì¯AÍד?"û5¥ÍÈû§ÙÖ×fd~ˆìŸ 2_@~ƒÜ:ÀO“’ÃWß!ÿ¬€ÿLÞ9ÂÃÉ?{”ð,ò^:'«^d±ƒæÇ]äÕäŽ9æÇÌ3Æ_ƒV²¿5÷"Ï!ï]·–¸ýïÓl³Çɦ•º=枬¯¤uÿ\Ù?T=^Ö¯ øÙ<Çúä—È»¸ß‘ב{Gøòñ%üL‰ÛÜ6=¹Ô~‡Š>›¤”Í`;~R©›æ+ÈïÌà;ÉE€/$F>;‡_&ŸPÀ¯“÷‹ðòr§öï5º,«»_î>ä¹äÃ2x ÙÏ£6÷ / ûkkóX²_ÏÒ<Œì·6_Gö×ÖjÃfŸ *˜ /‘wÉàɧ9g>-`ý‰4çªÇKŽ=$ÿ¸Àžß“q;×’?ŒøÙJ¬?Vâg÷Kï_–ËîçÓ}1ïaý– ëŸÑúÙëϬûyÔf?Ú¼Š|zº/Õã…Ü)âgýµµÙ_[+µ1g3ØŽ‡#h†'û9Æý2œ»dAnœÁw‘W“;x&¹m?DÞ£€o&oáÞä×Èû”ðoÉÿN®¥9mL^Jöksš&Ç÷#/&ûùeæ©ä¦<@çdÕ‹ªÏ™J̉ÕÉýÒãÄ݇lç.Õ$Ûu§ÜËÒl3·ðØ€ŸmœÃƒÈ+sÜÎå~öí뿌X޼} _M~ŸîËÏÓœ®Måšd¿6§Ù¯Íi¾üù·3ìç—™ÇøÙäYºN–RÕlÇÃBš ~ÝGó8ršaCÈß$_N³ÍÜ™<ì³íršmf?wúršmæïÈþwkó+äÝ ø&ò2rû?InL¿-y>¹Mz.rÅzï\o ÿ|T?NÞ"À}tNV½ÈÃ{Ĕ¡óã;r·žOn—fÛåéßs£ä äÍÓœ¾œfÛåi¶ùÏúu/ÏpÝÇêñ’cݯûX=^ ¬÷#¿E> ·GülVb½ y¹Yzߨ«›WàÞä×ÈûdðoÓý2ÿ›Öàçƒþ¬”Í`;>¤ÙÐ%‡_'ïWÀO’Žð4ò1%ü9OóìJšÙæ>äÉþ¾±ù1²’ùòò ^Iös²ÌÓÈ~N–ùVòä%üÙÏÉê¿Þ-+ð]äì×_P6{Lî™^ƒVŸ3å˜?-03®-°'‹ðMäMKxd‰Ÿ]I>5Ͷ+3¼ol~ž¼g†=·dXÿù¢4§ÍŸì?!Çúë9öûwET—ëþ]fÿ®ó{´~p ßUâöwL÷ÅÏ ÓïoîO^F>#Ãþ'3½VÊf°3i6´ð²Ÿ“e¾š¼Žìçd™_#ïáß’ýsAæËÈþ¹ «hN_Esú*šÓæ.ü:ùð¿Hn•Ã#ÉkÉçð4òÞ¾›ü=ùzúý?J¾šî‹¹sE3¸>d·Æ³¡–Ù¯³ažDÞ1‡¯#AîPÀãÉ­#|yó¾­Ô ®ÙcɯûhöëWŸ3¬÷!H>!Ç\yŒÜ°ÀžþnÓ¯³Q=^"<5bO»?ë×Ù¨/éuç5éù\³d{nZ“¼ª‚=~=¬k2\çëš ×ùº&Ëî5äž9~vvŽõƒ ¬ßE^]àw¸.bÿä_–Øó‘ÎÉRª:ƒíxø*̓kÓ±íîLžK>=ƒ'“k<•Ü4‡’/à{È›F¸'y6¹%ýþ“È[¥9=€î×€ ¯§Í’ý}oóòd?_Ú<‹\“ÃSÉ~–yy¹ÎɪÙãÿÈ4Û®¥×j×f¸–y‡4›Í÷gØS XLn˜cOò,ò^<”üyî9ëÏ’·+áþ%þ¹kh½Sº_2¼ž6?Oö÷½Í·?#°ÿ™€õms¬Nï ÈpVõx)°g­×DÍ`¥lÛñpbÄl˜BnQÂCÈ~–]gb=ÿÓÝÈóÉí2xyÇ_Gþ‚|iD>²€ï#¯#ŸáÈûÐïÿgòÎif_ŸþûãîG~‹ì×1?@Þ2À£È~ msß\3¸>dçÑs¢,áÚ3c>­·KïÛcæôJÝóäJÝs³ A^Cîð³³ÖÎqûÓr¬ï]`ÿ­ä/È"<ž¼Y‰Û¹ºÄí¯Këö˜¬¤ûbîLžNö뇘oÈð³_’ÏMÏEÌ‹ÖOÉ᩹f°R6ƒíx˜G³áˆ¾—¼–Ü+ÂsÈ{—ðäÒl³cÖ¿GÁ<šüùœ ~…|b€ç‘ÏÌá§ÈÛðåä·É¿ŒðsäíKøjòºäé¿Yî>äUäþ¼Œ|†Î‹®Ùã³iÙ0¬ÀŒYN>1bÏ”ˆõ&%|U‰Ù³6ͶÒã¤&Ù> ×(y)ùØ ~8ÃÏ6 øÙÑä¦9ü`ŽŸ Ü•ü"¹yÄÏ>±Þ „»“g–Øß!½¶˜áµµùùtÍGeðËöà‚¾»P)›Áv<¼0ÎáGÈ>ÏÒ<3L>&ÂO·¦¹u)ÙÏWDólP†÷ÀeøÌ®Ùß7Ï ïàä5dÿ[µy6ùà~„Ü ÂÝÉ3É-è÷Bö÷ n¤ûuc†Ï"ßH÷KmØìq¸{޹rSŽ9ñ5ÙçÙÀ4Ï|½UÄúȈõµ´~^ O+±ÇÏW”á=p³½^“¼†=÷“·ðµ?û!­ûߪÍåXß®€o"]`ÿ~’ܸ„kÉþ>¹]šÓ7¦ûU“ìŸE¾‘î—Rs6ƒo¤ÙfžLöÏìšû‘“ýzæ‰ä- xùkòU~|Ͱ§È‡¤×Ó¿Yï+ðr‹ ¾ƒœøòKä]røFòÇäc ø!réöÉ/‘w)áß霬zÑþ¾[}ÎàÎd¿…Ù¯Ga“cÏäs ÌžÅÖŽX%b}÷·9ŠÒûÞ¿I¿£ÛÞCj–\fp-y ùÔt_Ì“È[åp_ò5Òí“·*éöKü>‹JÍ`¥lÛñàï¯ÎêÖÝ}È«È>³ÓÌ6û¹ÓæäÅä£sø²_gc0ÍìÁ4³§çæî'ÉÍé÷D^™|Ózgø&ò2rû ~’Ü<À·ÉáÉ“}fßD3û&šÙjÃfÿéõèà ﯚýýUóQi¦™=8Ílßß'`}.yßNþ2Çí÷+p;>³§™í~€œ•ØI‰õ×iýð4¿íñvr¶s%Á·§™]=^|IÀÏ~Më]sØgöM4³o¢™}Íì›ÒÌÖ V{6ƒíx¸4b6|D>‰fØ›dÿ…!¾GÁ|Ù¿GÁ|)ù ò‘þ+yÿ¾üù<ìߣ`Nþ<°„“N÷ëf»ý ügòÎ|ùoä£|?¹’ÃuNV½ÈÏÓ"fÉÞ%|w‰Ù³SzÕ'þ= æžäÙ•ºýæSÓ<3O˰gÇ€õ[öocýúû?"YÀ÷Ø¿yĺ‚ùòñ%þYsKìß7Ý/{LnŸ~ó}¬¯«ÔÝŽ¹w†õ×Èûø·äOÊqûoêœ,¥ª3ØŽ‡¥4Ž-àÉäFîG^L>¥„'’›¦ÙæŸ}p&GöÏòšç“ÛøAòV9Ü—<ì皙Ǔ·ðÍäOÉ?/á§É?I÷kh†s͆ÒýJ÷Ëìçš™ŸÍ4ƒëCöøÜ¿Àœ¸­À¼©D¬w&Oسo )±ç‡´n«QrçJÝcż”ÖͰ2¹YÀžë+Èrì™JösÍÌÃÈ«ÉÝ#üvÄ϶-á;è¾äifM÷«&ÙÏ5J÷kh†sÍÌ“cÐ VÊf°~ÝGóMäeä3rø=òqüy›ßHþ'ù´~‚ìçQ£Ù6ŒfÛ0šmfÿü£y9¹o€‘/Èá—È»ðä’ýóNæ×Èþy'óoÉ?N÷åVº/·Ò}Q6{¼]”æ™ù­€9q@ŽõrÌ›Ý ¬"‡ˆŸí±>|D‰=÷’ý<êa4Û†ÑlF³mXšm¾gV†Ÿ­ XŸ°Þ2Çú¤·³U÷%Ï+ð³GDx<Ù¿+Â|5ùýôšûVº/·Ò}¹•î‹Rs6ƒo¥yv+Í3óB²_÷Ñ|yÓîI~ìsúVšÓæ]"|#ùc²_wÚüÙ¯;ý[šmæ©ä¦<Œ¼œìsú·4§Írx'ö ðPòçi¶™ÏɱþlŽuŸÓ·Òœ¾5Íiw߈ŸGnUÂ#É~ÝéßÒlûmšmî†öôH³Ù¼€Ö}Nÿ–æôoÓœvß•cÿjZï^ÀoØÓ6bý¡ˆõœ~ç Jìy£Ôg“”²lÇÃÒ<žŽm÷`òR²Ÿ“ežAöÏ §9=œæ´y$y-Ù¯ÏlžC>-Âï,á?’÷L÷Å®‹°M¾œü6¹m?GÞ#Àw’wÈáûÉ•L^J>6Â뜬zQõ9Szuxú÷_“쟥1ûgiÌ÷“ý3BækÉŸ“ÏÉq›~}f³_ŸÙ<®Àz“ˆý÷’w)±ÿÆûÿ™æ™=®BúÍ]É/’›gð  ?Û ýÎÕã…üqÀž‹sø9nç¤ëoXß?·‘¿Ò9YJUg°J̆~äÅd?_iäzïSÿ@þ7ùWüytVwÝ}ÈsÉþ}Éæ1ä†îAžEö÷ÃÍCÉ~Nórˆð“U/²ÇÌÖ•ºy`¾³R7'ÌE_–æ™ùMZ?*àg_X÷Ï5™Èñ³YõKÈ Éþýfÿ~s“ëCJ¬B>.]óktz,»íš_’wÈ`ÿþmógä³ö¡ÙpͰGÈ{§9}×zoWû“k2x4ù;ò€û÷ú™»’ç‘ý:æ{Éþ'óŸÈþY óäwȦûu·='¨ÀƒÈ+ɧfðÛä_ø9ò9|'ù[]'«^dÍ6%fÉ(ò×é5œ=NÖ?¬ªëæ‹È/“wËàÁä¥äcnsù”{ü}c³_gÃìßéd^^àgûFø›ˆý”Øÿy—ô¾±=·¬À—_¯ÔÝŽùð ë÷duÿ¬êñàGö4Èñ³—“?αç˜BçE+e3ØŽ‡ ̆ÏÈÇGøòn%|;9KóìYÝí»;“ç’ý5¨y Ù_ƒš{g‘ý5¨y(Ù_ƒš/!/$áqä&%|/ÙÏÉC÷k Ý/ót²Ÿ“e~˜¬s²êGö¸}¢À,90bNÜ1?оŒü7òÉi¶ý!ý;wO¨Ôí1ûkP³¿5Fö× ægÈþÔ|%Ù_ƒšÏ-°þ—·¹WÄúЈõïÈÝJÜÎòûOL¯­ÇÐý“áœ,³Ÿ“eös²Ì_‘p;þJmÌÙ ¶ãa1Íÿ$óTrÓ@^H>1ÂóÈ­h†$û9YþÜÚ=•ì×Ù0 /'û÷ ›Ç‘ýsÉæžä÷Èþ¹dó².Ùüùg%üg²_ë^º/æÑäïÈ~=Nµa³Ç¡’y ¹aùу<‹¼WÄþÑs¨i‰=È Óœ»'=Ÿs&ûu6Ì=È~=¬êñ°>šüÙ¯bö뇘w/ðÏòÏ%›ýsÉæ{#Ö7£ßùw%Ö×¥ûu/Ý—{3œ_f^Jöëqš'gú{°R6ƒï¥yfîàEä#rø^òZr¯žCÞ;·’ý{…Íw’¿M¾æÙ}4Ïî£Ù|Íæûè¾ÜG³ù>šÍ÷Ñl¾f³y6¹e#/'ûsŽêí“7¥ß¿'y6¹eÍàúнižùl0KZæ˜ÃÈËÉ'øÙ)Ö›Dø*ò{·^ œæÙ}4Ïî£yv_šg¾î³ù>šÍ÷Ñl¾fó}4›ï£Ù|_šÍîí ¸?y¹&Òí“¿#ŸC÷åYòvé}»ÆÉú›Ôë`µÑg3ØŽ‡Ý*uóÀ|;y¹KÏ ûµ±Ì#È+Èrx ùÔžF>&¯’÷(á;ÉEšgÐÌ~€f¶ù;²ÆÉü y÷ßD^FöóËÌï‘.à»È«É#/ÂsÈ~î´ùOdÿ¾£‡hf›Ç’gp-y5¹{€ß&“Ñ÷(`¿Ö´Ú°Ùã´¶Ä\YfÛƒi¶5JîLž^Áž}3¬'™aϹë Xß+‡G“Ëô^±ÙÏÃ2/'Ÿáq?Û„~ÿ!äo’¢™ýPšÙ¾¾ŠÜ%ÝóŒ ûÛìy’Ü<‡GæøÙ•9~öòëoëœ,¥ª3ØŽ‡oi6\á¿’w-áÛÈ•4ÏþHóÌ<š\fp7ò+d¿Î†ùA²Ÿ/mîKžGö9ýGšÓæÍ"|5ùS²/áiN›7I÷ëáôß,wgò\²_CÛ<™\àÑäRçE׋ªÏ™"æÇsbë¾”fØGɤyöÇ ¯AÍöÔ÷œža}2Ù¯³aH^°ç”ëþ½„f?_ú4§ÿ˜æ´¯wŒðL²/¡ùò7ä3ÓûÆ~N¡{Bº_æÒ1o¾?ÞJÀúà€õ¥?{z®¬”Í`;js̆ùäv<“Ü"Âw¿!ŸYÂ/‘[§Ù6v½[Và»È«É3x¹C€Ç“[çðïÈ?)à+È'ÿ,Â$ïY·ÿ•üÍéGhN›§“ýÚœf¿6§Ú°ÙcrFŽ9ѬÀœQ`–4ŽØS±g ­·+áKìÙ*Wl™†¸?yY¥îgÍí3xlšsÕã%`ÿ]{Vì¹.Çú¢ëX©Àí´ŽXÿùŸ?{E‰õwJüìÏÒýz„æô#Η6ûµ9Í~mN³_›S©9›Áv<øõ™Í'ø/ä£sørVÀ]È3Èí#ü$¹1ͰAä•ÉvÝ{oî.ääý2xy ¹S€§’[æð$òŽ|+ù r‡?GÞƒ~ÿ äÒóG3¼g 6löx;4`Nü!`Nü(Ͱês¦{¾$Ÿ\À“·ˆøÙÛ#n3+±§K‰õ×ɇ§kHÙcæäJÝ~óãä-Ò<3_›aýCZ?!`ý1rÃ{úçøg­¡õžü^Ÿ=8ÂwEìiP——¸ÍïÓº=æ­šdÏ@©9›Áv<øûÆf?ߨ<€¼ìç›ç‘[åðSä] øFòÇd?Ëüy§¾žüäÇÒ±íîLžNö× æÈ_’ýùÇcôüÃüÓ¾–ü!ÙŸ˜_'ïá'ÉëZ•õ"{,ùùÆf?ߨ<‹¼WÀ,°¿iŽ=r¬‡ûûØ3Ü*Â##ö¯%÷*±gͳCÒœ~,ÃkPó²¿5÷&¿–æôcôüã1zþñXzþáë¿&ÿyÏ¿Ãã~ö§{®Ø“•ðM:'K©ê ¶ãá.šg«“ÇÑlG³mͶq4ÛÌÃÉ_’ý»ÆÑlG³mÍ6³¿¶G³mÍ6³?„ùEòqôû?BöÏ"Ïp~™y y1ÙÏ/3¿Bös§ÍãÈþy'óUä÷ÈþYd³vKmØì1¿¬ÄÌhŸÞ_G³mͶq4ÛÆexÕü­ûw™ X÷Ù6ŽfÛ¸4Ûj’ýµõ8šmã2|÷‘Ù¿ûȼe‰u¿Î—Ù¿b|:–%w®`}.­ûùeãÓ±\“ìçN›û¬Gëçä¸ÍWr¬ûg‘«ÇK=M¢þ¬”Í`;ü;‚Ì"oR“ýZhN›û?$–ÁcÈÍ<‚¼‚ì× 1O$·)àQä¯É]#ü"¹9ýþ#É~=¬Çé~=N÷ëñ ça=žá:_Óóód¿Î—y¹FçdÕ‹ìñ<$b6|1?Î,á§hží’æô„ôïÙýþùG4·~Mþòžéµµ½NÞ¦ßHþ'Ù¿KØüyë_Jþˆ|RO ïPÀ×?#û÷^˜%o«s²êEöXúi†ÙpmšgæÏ3ÌKÖ’OÌá)9öQ`}"Þ{ÉkÉçxZÀÏîÃwçØó=­_\`ýÕëþ½æ;ÉßFì¹°Ô VÊf°ƒK̆UÉOfu{Ü}ÈsÉþ7Qó²ÿ­×<‚¼†<(‡—ýsÉæÉ[E¸/y¹ýþ#Éþzô)º_OÑý2¯"ûûáædÿŒ“y*¹i#/'w,`ÿ¼µÚ°Ùcþͳá¨4çžÌð¾±Ùÿ&jö¿‰šý»Ìþ·Þêñ°þ!ù„{^Ïq;ûðòŠ?Û)b}*Ù¿Â<€¼|xzýݯ§2¼nö÷ÃÍ÷gXß-Àþ'sÃî‘ã6ÐzMõ±dÿ¼µRs6ƒíxðkn˜‡?!G3ìrƒôzôišmæÁäUä.<ƒÜ>ÀcÉþù]ó$òVÜ—ü ùÌ¿Oökc™_#šîË3ëÝ¡?GÞ#ƒï$¾Œü&yÿ¾_çdÕ‹ì1\13æ“ý{…Í£È_“ÏH3ûéôX¨I~³R7ožÎpÝi³_wÚ¼EÀþÛÖWѺ~×¼&ÇžN<µÀžvëF¬·(ñ;ÜQb}û4›í1Ù8ýþæ»*XoÁ—“?&ŸàÒ}1ï”Ã×çØó\ß›¤”Í`;v+0“—’ðdr£Mþ.ùYšÍæ>ä¹d=mNþ’ìß5Ï"û9ÆæÑä²€»‘_!ïáqä&ôûßKös§§d¸Î†y4Ù¯ÍiîF~…쟱V¶ês¦sâMòþ¾-b~TJ¬w.1·–Òú±i6?›áu§Ù_O›ýõ´ùòdÿû®ùQ²ŸclösŒÍKiýôë“É"Ü/Íi³—põx)±îß%<%Ë5É~ ³_›Óì׿4O&7 øYÿN'¥6ælÛñàŸs57Éá!äOÈþ…æ9dÿÎ]ó ähn]Oþˆ|dz ú\†kXš‡‘W“»gðLr‹'o–ÃW“ß'RÀw“7‰ðä¿“ýïÖæw“Ÿ§çæÎdþ¡6löØû.`~tËáù9æG›óÿ£°z¼D¬ûwîš7£¹Õ‹<‡¼wšÓö˜Ù®÷'/¨ÔݦÙΫw?™aOãצ9W=^h½cŽŸ™c½E!R`ÿ™~‰ÜºÄ?ëé뻦×Öϧã²Qò„ Öýù‡Rs6ƒíxð× æáäÈþÔ¼˜ìŸß5O%ûßtÍ3É~}GósäíinÝLþ4ù/4ÏÌ}È«Èþw\ó ²ÿ}Ú<‚¼†ìß%lžMöï6#¯&wðäôû'ûçµÔ†ÍcþÔìŸs5°þhšmÕã%Çž1ä†öŒ 7ް_ßÑÜ Äíw/±þ6­·Msú/^O›ýï¸fÿ;®ùþ {¶X÷¿O›?$ŸÃþ]Âfÿ.asò‚ûÛGøÉˆßçàûï"û給¦cYïE«=›Áv<ø{ªæ±dOÕÜ<Ÿìßwd~ìßwdîJþ„쯧ÍsÈ{GøVòäKKØ_OO£9mîLžNöÏ5™o ÿì×­4ÿ…ì×ö2%oYÀÈËÉ'FxJÔ ®ÙãÇÞSm–lÏç|Nø{ªæÉö4 X÷ï;2¯ õSrxbúÛgõx)`=mná«Èï‘.ñ³ÓJüs÷NŸÿ™–áu§yÙ?×dîM~¼Oº/æ?ýÚ^æ_çX7Ç?÷Üë³È5?;:b½,5ƒ•²lÇÃ4ÃÆ“ýûƒ_ 9mMö9ýÍéhN¿@súšÓæ9<„ü ùÌ~‰¼K„Gþ'ù´~‚¼Sº_ÓÓÜ£É~¿Ìµäùd¿_æQdþ1ž˜ç‘[é¼èz‘=ÎýÚÓ2|ð ôZí… û4ûß>_ 9ýÍéhN¿@sú…4§ý6ks왟cO»ëXß*b½oÄú"Z?¢„ï%¯M÷kzº_’;§û8î×ôt¿Ü32ì÷û5=Ý/÷â€ýþüczzþáëM ¬(ô^´R6ƒíxY`6¬$ŸáiäKøVòÉ3ÒqîîCžKökXÎÈð¾ñŒ ïÏHǹۿs×Üü Ù¯³a¾‰¼Œì×Ã2?InN¿ÿH²_kúEšÙæ~ä·Èçdð³äÃ|ÎɪÙãv!͆Ã#fƸˆõMK¸'ͳ÷hýàôwP,»ý<,óÖí–5Éþ¾qõx!Ï ØszŽõÉd¿Î†¹ù-²_Ëì×Ã2ûõ°Ì—¦×©/ÒÌ~1Íl÷tòÎö?œa=¬ Xÿ\çd)UÁv<¬¡Ùà5¯$ŸZÀ“Èm#|9/á Èo$¿Dsú¥ ï¿”ž»ÍàÉdŸÓæääN9<•ì«6?HöÏ›û’ç‘ ß<Ùß3øk†÷ Ì£ÉþÚÚìï¨ ›=&ýo¢ÕçL9fÆáfÆ=ä5ö ŠX_q;§–XŸTb}Çô¾ñKÞ76Ûûƾ¾†õÛÒÌ®/îCökM›˱þXŽuÿ[uõx)°îŸ?®/ëSÉMKìFö÷ þšŽåšdÏà¯^[›ý=ƒ¿¦cYïE«=›Áv<,§ÙÐ1À3Émsø!òÖ|=ù#òIž@Þ¡„o!–ü2Íé—3|æøešÓ/g8Ë<ƒìsÚ<¼‚ì甽LsÚÜ´€$‡w%¿HnN¿ÿ#dÿ,õ+t¿Ô†­úœ)`NŒ%7ÎáAä•äó xN³wÄúÝëßÓúÅ4ÃÞ)±çÀtÖËé±\“lŸ9öõýÓl6ßOö×Ó/gx=mž°Ç¿Âì甽œæ´¯÷(ðÏ]Aë§Dx"Ù¯[i¾¤ÄÏ~MëþYjÿŒž^«=›Áv4½olžð³ûæØ3œü%Ù¿oÑü8y‹ˆÛ¼¼]©¬”Í`;ZÒ F^žü*ͶWi¶½J³Íì¯GÍ3ÈþzÔ<¼‚ìï›§’ýsAæadߨ|yùúýÇ“ý}ãÿÈð¾±y,Ùß76×’—O ð$òV9Ü—¼ˆ|„ÎɪÙ㼉™±€\“^·½J³íUšm¯¦ÙV“ì¯GÍþzÔ܇ì¯GÍþ¾±Ùß767,°îŸ 2/(ðÏê±gvÄÿ§W3¼olö÷ÿ#Ë5É~N¸ÙÏ ÿ çdýG:–Ýûøž€ýkÈrxjŽý- ¬Ó9YJUg°÷˜ kɽ"<‡|H ÿ‰¼Išm³hf›ûç’}fÏ¢™möëVšŸ$œÃÓÈÇðä­#|=ùäËJøMòþé¾Ì^ï+ð­ä­3øRò·ä³üWò®ºVe½È·Ëi6œ13¦D¬7¡6„üM‰=g¦™=+ýûo–ìçdÍ¢™=‹fö¬ ×­4ß°žåX¿‰¼i,°mÖyësȇ”øÙJìß)ýþöøÜ´÷L÷Åü^¥îgÍçeØ3-Ãú1ëüìÖ9Ö/Í5ƒ•²lÇÃor̆ð•äwÉEø/ä½Jx(yË4ÏædxÝižJn™ÁÃÈËÉ~}Góò9<ž¼Y_M~Ÿ|H„ï&oR¿%ÿ;ù5zžaîLžKös¿Í‘·ÓyÑõ"{|¾‘c~œU`füµÀÌØ5¿‰Øÿ£û¯,±çÝ4çæ¤Ç‰{0¹ašÁæþiž™k<:`™ãv†åX_cÇë3 ¬·ˆðò'ûϤßÿýë?Oï{¿–á½³¿7ðZ†s¿Í·ÿE¾HçE+UÁv<ô˜ È~fóh²Ÿ¯d@^HöëQ˜§›Ð »Šü^ò\šms3œ‡e^Jöó¥Í³È§x"yË@^N>±€§ˆðxòfô;_M^—ü:Ý—×iN¿N÷EmØì±÷rÀ,Ù-‡“—¦™gîQ`}y¯ˆ3š\–ØÓü y÷4Ûæ¦ÇrM²Ÿ‡eöó¥Ív¾´ïÙ6Àcöü@>7ÇíÏ"ר3ºÀzq›Ã"ÖWGìïXb}f‰ýÒ}yæôëÎý~î‹Rs6ƒ_§yfžLnàäd?¯Ø<‘ܦ€G‘ý¼bsWò<ò4ÃÆ“ýúŽóh¶Í£Ù6fÛ<º_æ‡É{x(Ùçô<šÓæ…dŸÓóhN››Dxùr¯žSjׇ^ÏðùŸês¦€™Ñ9`}.ÙÏ+6!ÿcÆ ,°¾¸ÀÏúyÅÕã%bÝ?ÿcFöë;š;¦÷ŠçÑl›G³mͶyé~¹¿Ê°çÊ€õwÖ}NÏKsÚ÷ìU`}4¹ŒØÓ-b}>­·)qûSJ¬7i ¬”Í`;öN³í¬îÚñî{ÈkÈ2x*¹e€ï"ï˜Ãב‘;ðxòfîEžCö¿O›ï&Ÿü&Íì7if›§“ý<ð7if›c€/"¿LÞ-‡“pò“U/²ÇÉú‡Ru˜/ªÔÍ óç´~B?Fn˜f³¹?yYÀíô̱6¹eýwX_Mîá)dÿû´yH‰õOÈÇ¥×£oÒÌ~3Ãyàf?ÜÜ;Íé7ÓÌöõ_øÏäçðeä7sÜÎÙÖŸ)ðûì5ƒ•²lÇCMÄlMþŽ|ͳWÈ»§ùýVVw|¹’Wýcór»O"û9Ææ;Èy_Jþˆ|a„?#_ÂsÉû¦ûbףܾßLþ–|V?MÞ5À·‘¿"_©s²êEÕçLsb)ùØ~¸Äüh”^ý•žÛ5K¶s}}.­–f›ÙÏ16ï°DÀúZï”ãg—äØsjO#ï±ÿ ò%nóÎë;¤ûbɸ{úýÍo“ɰç!r°çRòG{ŽL÷Åü×t_”Ú˜³lÇÃÒ³áØ~˜¼W„‡’·,áä…äÃÓl{{½ªÀ¿'ÿ4ƒ‡’?'ŸàgÉ»çð(ò×ä®ü ùÌ?Eö×ÖæÈ~­éwÒÜɋɧdðTrS“U/²ÇçþæÄmä¯ ÌŒ+#ÖßX?·Ä¼™UbÏ^i¶Ùcæ'•º=æßT°þ÷JÝí˜aÏ»öàßì‰9ܼ8Çþ£ ¬O,ðÏj±þ`ÄþPbÏòfé¾¼“žs¸;§ûõNzÎáëûfXCn°§GÐ VÊf°ÃfÃrò‰9<…Ü¢€ï oá›Éß’/,á7ÉþY ûïÏÞøVò÷ä‹3øò¯ü7òQ9|?¹RÀƒÉKÉ~órM %7N÷eA†÷Ì#È3Íàú=vÐl¨ÉáÑ9æGYÀµä%i¶™»G¬¿M>¦„Ÿ ûgì1cŸj–|UëïÑúqüy—ô¾·ùOä}rÜÎoÉÿ ŸT`ÿ›Ö÷X÷넘w+±gp‰õUÉ Òœv÷!X©»Ms—LתTÊf°we˜ «É~ÝJóL²ß‘ùò7dÿ,yùü¿@Þ©„¯'ÿ#ù½tl»ûç’ýXï¥çàîÈ~~™yÙ¯bžJöóÀÍ’ýü2sWò‹äãè÷„ìß¡6lö[–ÕÍsûûu+«Ï™r¬×æX_Bëí ̘™ä{Æ“7+á^ä94ÏIsú½ôøm–ìŸ96û5°Ì×dØó­Ÿfsõx Øã× 1ûuBÌ~ÝJ³_·²z¼ÐºŸ_fžHÞ²ÄþQ%Ö¿N÷ëýt,ëïÁjcÏfðûÞS5$/&û{ªæùdÿ;®ùA²ÿ÷}šÓïÓœ6_PÀŸ’ÏŠðÓä]KøQòÒ<û€æô4§? 9ýÍéhN›¸yÙç´y6Ù¯çeF^NîᙺVe½È?+˜ þžêûÞS}?=Nj’›¬Ø¿‚Öýï¸ïÓœ~?Íiß3©Àž¶ëEìÏin]Zbÿ·%öœ•æô4§? 9ýÍéhNæ´ûì€=Ϥ™ýÍéÒœvoW`òrMÄþ±äÆ¥Þ‹VÊf°mK̆‡È{¤Ùö¡3x¹A_N~›üË?GÞ>‡ï#¯#ÿ¦€ÿNþE„ÿLþq _FþWòBšÓ 3œ#m^Jökx™'“k<šüÙ¿WѼœìŸk2ÏÓyÑõ"{œ*1V¦9g{¨4JîR©Ûc~½‚=gdX÷ï¶47ØóHÀzƒîžcÏ´Þ¡ÀúÖ[Gü³î‹X_G>¿„_ ûò³tŽôÂôXn”dCÿn>óð3ãZ?·€ÿR`Ï^ëC#Ö¿‹˜UJìYXbωéï¸þ9y·¿ž^’áýaóýdÿ޳ǃ9Ëq;]È3rìÙ¯€ï!oq›þzÚ¼’|j O*ñ³;¦ûb톸G¥ny¹}¦¬”Í`;:f˜ SÈM|y-¹WÏ!ï]Àw“wŠðoÉÿ ÿª„ÿF>*Ͷ¿gøü’ùnò÷ä_fð«ä“<¼C_CöÏ/™;“ç’÷ðpr£îG^¬kUÖ‹ìq56ÃÌ(ÜüJšyæsÌ’)9ö4)à« ìù¤ÀÏöŠX?bÿ!%ÖÿTbÿ>i¶Ùc¦I¶Ï/5Kþ„ÖËàG2ìÙ&ýþÕã%`Ï÷{.αþjŽý'XŸP`ÿë×?ˆøÙÎ%Ö§—øÙ}è³IJÙ ^šÕÙæ¹3y)Ùß_5O&ûû«æä2‡»‘_!û5¥Í£È~íeó²ÿ}ÚÜ‹üÏäet_–Ñ}1O'û{àæáä/ÉçxÙß7O$ûûË辘ýóKæ®dÿü’Ú°-ÍðÔì¯AÍþþªÙß_­/ö÷WÍs~öôë“É ü¬_SÚ¼¸ÀÏúµ—Íó#Ö;–Ø?¥ÄúiN/£û²,Ã{àfÜ|M†õȇìy”ìL³Ùü­Ÿ[`ݯmöÏ/Uò–¥^+e3ØŽ‡æ4ÑW&B³Í<˜¼ŠÜ%ƒg÷ ðò²æÇ<•ìŸÍ5#¯&ûg~Ì‹ÈGÐï?žìßýði†ó°Ì#È+Èþ¹,óT²Ÿ‡e¾‹¼šÜ=‡ß&·Õ9Yõ"{œ_Rb6¼^b~žfÛ'éßyM²ý´YòQüx†ý[¬_°þ!­Ÿcý1²6×Ü£Àþäö{ÆFün-K¬ûw?T—´þi:–›%÷!ûyXæÃ2دueöó°ÌýÉËö·Ï±þ$¹y=ƒ Í`¥lÛñà׺2ç¾”ü-ù¬~šü“4Ûüü÷@ò ²Ï6óDr›"M>#‡Ÿ$7.àZòjòuö¿ïšo&ožî‹]+Ó¿óØ<š\fp7òrrßÏ#·Òõ¢ëEö¸]R`Nœáii¶™¡ö9Osú3šmŸe¸v´y.­ûlû,Í6÷éqV=^Ö“ÎáÈYw!Ï(p›í#<›|j ¿üyº/îÁ龘—¦ûb>=Ãúä ûk<5`OÓk+e3ØŽ‡‘9fÃZòy<¼c„¯#A¾´„ß ™f›Í©–xy« ¾Ž¼ˆ|D€Ç“7Ëá«Éï“)à»É›Døbò?È'•ð²Æé zþñ=ÿ0/&ûÌV6{L.Ì1WN,0'Æ‘7pOò숟=˜fØ´{vLçKÛãľ—¬QrÿJÝ~óZï”aÿì ë-<,`ÏjZï˜c}fŽõ<„üIýgF¬?ñ»Rbýnò÷éuöôüã zþñE†kž˜÷Msú‹4³›ýŸûתÔ‹lÛñà×½2ûkPóƒdÿœ«¹/yùˆ¾—ìß?hîEžCöïH0ÿ‰¼OšmÿÈêÎIq_E~|pO#ïà[É[çðõäÈð_ÉûGø~r¥„û?Lþ2ý7K3xÃW}Î0'Ì’äN9öLͱ޲Àú0òò{üûÍSÈMh† !“f¹=fÖ³ºßÜü y÷ ¾)ÃÏnš~ÿêñ°ç½€=çåØ3'Çž½ ìy¢ÀúÖû¯'ÿ#bÏI%ÖŸ'‹øyz¬6ölÛñй‚Ù0—ìç.™Çý<¬/3œ‡õe†k:šý³@æ%dÿ,yy«÷%/"w öÙ? ´"ÃgÌÃÈËÉ~¸y ¹E€ï ûõ¼ÌW“?%ŸUÀO“á+Èï蜬z‘=~&T0üšŽfÿü®ù³4ÛÌ~Võx ØsXŽ=þY ³È|OŸ]Cîá©û[–ð]%nß? d%ÿ,Ù? d^@ë5ÖýN3oezœÔ$ûßDÍþ7Ñ•þ&º2=NÜÜ/àv¾#Èq;Ësì÷÷ëÇKý­"ÖG’W’ýï»æ·é¾“fö?Óc¹&ÙÏ/3•aÏýäJ€;ìŸKökJ›#oWàŸåŸE6û{ûJmÌÙ ¶ãÁÿ¾k¾•ü=ùbša¯’ýšÒ_e¸¦´yy%ùÔ žF>&À‘ó¾€ü¹ußG^Göïk2¿F>´„%o›î×*zþažJnšÁÃÈËÉ'xYçdÕì1Ü3b6¼G>®„Ÿ*1‡¶IóÌ'6¢Ü]*uûͯ“Ï°çž ë›¦yl°g%ùÔžDÞªÀí\Gþ¢Àÿ¾&óøˆ=Û—ðÍäo“WÑóUéù‡»aº/æö/ õš&ûóUéù‡>¬6ölÛñðTŽÙ°M_Nþ˜|Z„Ÿ o]—’ß û9Y_g8'Ë|Ù¿ÂÜ<“ìï›ï ûÌþšf¶ùSrïþ€|h„%o[ƒÉK“¿IÿÍrw&/%ûu³Í³2Íàú=&C9Ñ•übÓ*Â÷FìYK>¯„§‘ýœ,{Ìø9Yf;'Ë×—‘ÏȰçIrãûûÆæ%dŸÙ_§™]“ܶ€Ÿ+°ˆý7G¬K¾°„ß,±ÿt¿¾IåFÉöÞ~M²_7ÛüL†=ÛêZ•JUg°{̆¡äÏÉ'äð³äí ¸?y¹&£É%ͰZòüäoi¶™û?$–Á‘ýsÉæä5dߨ<›|pßEnáîä™äôûßAÎÓóÑýR6{¼]0Þ%”ÿÏ1obõ‹È/“w‹ð`òÒˆÛ9½„g”ØÓ,]CêÛôXn–ü¶€N¯MÍ~ÝióPòçäJÜþ³%Ö·K³ÍWE¾ü×JÝ?Ë||†õG3¬ïàÉ{åp?ò[9öŸS`ýYòv{î!7×9YJUg°þýÁæçÈÛ§y¶:³î~äïÈ2x!ùðßC^Cöï(4¯$û9YæÉþ~¸ù òN%ü[ò?’¿Ïêî»{0yÙÏÉ2Ï ûkkó“äæ9üÙ¿oÑ|9ù{“U/²Çù ³¡Ašg«ÓãÄÝ™<½R·ß|lÏʰg¯%N>!ÇϾžæwõx)`?'ËìýýpóZZïUÂï—ØsHº_ß§ÇrM²Ÿ“eös²Ìþýf¿æ—ùÚ€ŸÍr¬_’cýë·sF=/ØsœÎÉRª:ƒíxøeÄlx•¼Í­;É~¾ô¿ižý;ÃßAÿáúÌæ3øò—ä“ü8ù§9ü9+àKÈ ÉþzÚü"Ù¿ÇÉ<’ìßãôÝ/sò‡ä2øYòvîO^Cö÷ÀÕ†­úœ)bNlSb~øwîš?&Ÿ–þúïôï¼QòòæÜ›üZ†Ÿ=4`ýäìùušmÕã%Çž“ ìù Ù_O›‡Füì–%öø÷8UZ?1Ý/ŸËý<‡Ç‘[ðHòZ²_Ë<‡¼w ßMþ>yÍló`òR²¿W¼Žf¶Ù?¿»Žf¶yÙ_ƒ®£çfÿ~óòBò‰žBnB¿ÿUä÷ȧùmÿ= ¢\²ÇÞ_fÃOsÌ’¡äïÒœ[›¾a='Fد‡µ6=6ÜW•Øó ­—æÜº ï›í½b_÷ï6ûçw×¥™íöÏï®Ëðù]³~×ì¯A×eø~sò,ò^?;š\–ØÓü y÷t¿ìwüq%‡µÞ7T4ƒ•²lÇÃí̆í2øòr§O%7ÍáäPÀ]ÉóÈ­"üÙ¯ai¾›ì3; u÷ÅÝ™<ì‡“¿$ûûáæ¿Îá‰dŸÙæaäÕdÿN'ó"²_óKmàÖÿ»ø[%}Vu½/ÊàÏ3Ìü¹a÷ ÏÊq;§XŸX`5X±?”ð{>!ûÌÎ^g›'ýýpsï4¿Íк¿nþCÀž¥ß¿z¼äXÿìßéd^@öït2ûw:™ýš_æ»J­”Í`;ž£yæça…€kS˜G“¿#Ÿ“ÁÏ’ýo¢æ›ÈËÈísx6Ù¯'ežDöëYš¯#/"û÷šï%û÷æ4³sšÙæ¥dÿ,¯y¹6ÀóÉírøA²?ÿÈéù‡ù“U/²Ç¼Ÿ‡Òc£&Ù¯ÒcÃ}l?Lö¿‰š/JóÌü­÷çøgùõ¤Ì~=Kór§ˆÛœ±îßWhF^^bÇtsšÙyÀ{ãfÿ,¯ùòažð³ÍÒ<6ȱ¾‚ìÏ?òôüÃo§MÔ VÊf°gF̆÷É?/á§É»¦yVüMÔ<¼˜|t?@öçðæ.ääf9<‚¼‚|JO$oáä…ä®%ü"¹UºößÕ£+ðDò–<€¼ì×É2O!·Ð9Yõ¢ês¦ˆÙТÄ\¹ƒœ§ó•Š4§Ýþ7Qó\ò¾<œü%ùä?NÞ"‡ûäøg}Hë‡ðòäs#~vVÄú^éuvõx)±¾eºöøüQ¥nyxë?¤ûh>7úŸnÞ+`}4¹ÌáZ“¥TuÛñ0$Çlج€{‘ß'ÿ<ÂO“R¿!ÿ=¹Lÿýr&¯"ûßzÍ“Éþ·^ó@òb²¿ol~€¼e_B~¼_„Ÿ$7¦ßyerº_ Σ6Hök‰˜ýóÖjÃfÏù9æMdzdJ™Ñ"Âwóû/(±þ­·N³­LÓšdÿ\Ù?d¾Ÿìë5ûßzÍsi}ßNþ2Çþs ¬ÿ…üÓ_ñ»e%Ö»”¸×ɇ§×Ö Òýj–ü<Ù¯%b¾…쟷VjcÎf°?ÐlðÙÖ€f›ÙÏ=6O%·,àId?Ú|+ykš[w’ý;6¡y¶ ÍióR²ŸGm~˜|ùs²_óËü:y¿AnáAäô;_N~›Ü6½žþ‘Ý~QÑ ®ÙcÏ®¹Q“ì³­AÀ¹ÇÕçLä†Ü¿ÀÏ®¡u¿µù½ˆ=ç•ðÇ%þYþ ›ÐœÞ$àœ,³ŸGm¾üùþsÀíø5¿Ì¿Ï±þÓë×’?,°§Kº/Õã%býŒë/–øÙæé¾Øã|‹JÝóµ¬XÑ{ÑJÙ ¶ãÁ¾?ÜgCÏ ~|^€?&“ÃO·.àëÉÿ Ÿá äÍKø6r%ͳ†4§ÍÉÓÉ~VCšÓf=mîG~‹|@?@Þ½€o"oážäÙäƒKø.“U/²ÇÕ æÄ³äÝÓ 3#·Ê1KFæØ¿–ÖÏ+°>‡|HÄž»Éß“YâŸõQ‰Ÿ=)Ͷ†éqêž@öó°Ì½Óßt¦9Ý,¹sÀžéäsø†û¿Ìq;ý ìy«Àú9ëÏ’·+q;7•X_¦s²”ªÎ`;¤ÙæßùíKn™ÁÃÈ«ÉÝ<“ìû4!B>®€Ÿ"ïáÉ›”ðÅd¿Öf4§7£9m^Jös¯Ì È5M.s¸–<ŸìŸ±6O"·°ߢڰmš'Í’íï5É«ÈýÓlÛ4=N|û?°§q×’ç“Ûð¨·"Ü7bϼˆßçÌëO•X÷ëamFsz³€s¯Ì~î•ù™t_Ì»ìL^°çôž‘cO³ë#Èk ü³E¬û÷-*µ1g3ØŽ‡œæÖ¥ä’7§y¶yÀß;ÍsÉþ÷Nóòd¯Û¼‚ìïu›'’ýó?æä…dÿÌ®yySúý{’g“ýúÒv=¾Ý*ð`òR²Ïì-hfo‘þ[ìKnœÃƒÈKtNV½Èç§–˜ ÓÈ{§¿ãnžþ}ºýúÒæ2¬ûß;ÍŸ‘ý½îêñ°ÿ°{Æýó?fÿÌ®yÙ?³k±þùœ~–ì×—¶Ça‘î—ù² Öß$ûÌÞ‚föif»oØ¿ŠÖ»äðë9öìWh+e3x zÝf~ìŸ2_Gö™m¾€ü¹ušmv=(ÿŽBó]dÿŽBswòÛä¶~޼}ßGÞµ€o#EþE„ÿLþq ß@þù¨t¿¶²ÿFWà»É›dðä¿“4ƒëC[¤×m>V˜þY óìˆ9tj‰ý“JìÙ1Í6{œ4¬Ô­›û“—‘ý; ÍOfXo°>(Íïêñ’c½{Žõ/È—¸ ì?2Â÷‘בÏ/ñ³¯•Xß'Ý/{Ü6©ÀW¥ûbþ„|f¿Dnî‹ùé ÷¢•²lÇû³á þ ù§<”ü9ù’¿N>¼„_$7Oólëôß÷@r™Áµäùä6~ìï›Ç“·/àûÈëȽ#üùÐ~”ü£t_~ð~¾yyÙ¯a©6lö8Ü5Ç\ùMŽ9ñ£ë¿.0KÞ¥õƒ"öÿ%bý§%ö-±gË4Ïì1c¹;Wêö›í½¥šäÓ3¬ÏȰ¿Y€ìYð³µ9¼:ÇžîÖ¿(p;"öä+Øã×°4É4ƒ•²lÇÃÔ ³¡i€’ý–æ{Èþý}æžä•äË#ü1Ùÿ¦k~•¼GšmvUó <’¼’|jO#ïà[É[çð¥ä7ÈGð}äudŸÙæ¯È¿(á?“Ü@3¸>d±†³¡yy¯Jþ<Ǽñïï3?›æ\õx‰ð‹û[•XŠæÙ6iNÛãdýC¸ºß| ya{Ïà{2ìñó;ÌWìy/Íióy9Ö§‘w,ð³·Xÿ‚ì3»QšÙ~›G–Øsy]rã4³kþOþ‹Uê¿A6ƒ\Ë<šì¯A͵ä%äv~¼U÷%/"w(àçÈ{Døfò§d¿~ˆùOä}ÒlÛ&Ô½o澊¼–Ü+ƒç}f›ï&o’ÃWß!XÀw’¿%_£s²êEö˜ñëa™íoÿÍ’ý5¨yy¿€ý#È+ötʱ>5Çz˾‹Ü —GüìÛä¶4Ãî “f¹=®Êt_ÌÝȯT°çÄt_ÌS2ìià«ö|B>3‡_ʱ—¾‘ü1ù˜ˆßáÕ¨¬”Í`;*%fCò\ò¾i¶ýÄfd¾™ü)ùçüùÐO'ïœÃ7ÿF¾¨€ß"áÈ»—ð(ò×ÉÛ¦çãîÎäédÿŒ“ùa²Ù|ùs²Ùüy;]«²^dí“JÌ•çKÌŒÒëQ{̬guùò Öß&·Íà;ÒyLæí~öæ€õÍsìïM~¼Oý.p;;Gì¹!býˈýýJ¬/.±ÿèt¿¶MS÷²ÆÉìŸq2EëþYd³ÙìŸE6ÿžü¯4¿·MDzދV{6ƒíxðϹš¿&û÷ú™_$7§6ˆìŸqÚ.ào¢æäÅd¿†¥ù²¯Ÿy9äðò7ä3 ø%rëÿŽì×ö2_Aök{5¡9Ý„ætšÓMhN›‡“¿$ûg‘ÍþYdµaÛ6=ŸóÙpt„'FÌ-Køòëé5èvéqÒ,Ù¯aižKökXnp ËíÒãÄöÜ€Û\°~JŽõù9ÖÛ¸ ¬oáë"ö,Џ J¬¿Tb¿_Û« Íé&4§›ÐœnðæhýÐt_ªÇKÀúιf°R6ƒíxðë;š g| ùu²_ßÑ|y ¹'ͳÙdÿ\½NðÏ™“W‘ý–æÉd¿æ†y4ù;²“y!ùð~‘ìßãd¾—¼–Ü«„ß'’î×ë½{GÞTçdÕ‹ªÏ™r̆/É'˜+~}G³_ßÑ|mÄžÏiý„ëþ¹ ³.È'þ¹ ³.Èü&­û5,Í÷“ý–æÎiΙ—Òú±9ÖgåXß«€‡سeÄ?wyyÄþK¬O!·H÷k‡ôÏw÷«Ôݾù;Z?Gçd)UÁv<\•a6¼G>.ÀsÈ{çðÝäïÉ¿,àWÉFøä¢„o!o‘æÙOÓë wgòR²_ÃÒü0y¯%N>'‡Ÿ%ûûÆæþääN^BnW“È[霬z‘=Æ^É0ìÚXÍ’GÌ&9ö\•cÏ'´~\?R`Ï6¾‘üψý§•øg½CþUšm? x=j¶×£5É~ Kómä¯2ì¹2`ý]òAé¾T—ë±€/"¿LÞ-âö#ïWbψ·¿&­7MDzf°ÚسÜ4à}cóò ²–Æ<‘ìŸ 2?HöÏ™‡ý;!̽ÈsȇDøn²_Ë|ùßÉ;Òœ6÷!Hî’ÁËÈ=<›Ü2‡'‘w,àëÈ‹È"<ž¼™®UY/jðYš¦Ÿ¥1ûgiÌcÈ?¤ÙföÏU—€u¿6–Ù¿Âìß ažBnq;C"Ö?¡õ3Kø¥·ùóô¾ñŽé1ë~ž¼gúÍ“ð³i6›·Ëáþ9ö¬Éq›= ì™Mn±ç®ˆõÕäŽi+µ1g3ØŽ‡^4ÃÞ'û{ª;Ù¬­ÀSÈGdð½äµä^žC>$‡ï&oRÀ“_%ûµ¦ÍÈ~­isoòkäCÓýÚÙž+Tàñäí3øjò§äŸøOd?wÚ|ùòÏtNV½ÈçþžªÙßSÝ)Í£fÉvNCMr™ÁÃ2ì_N>1àg§¬7ɱ>$Çú'´~fõ§ÈÛDüsýZÓæïÉ¿,áçÈÛ§9m½Æ•ºusmº_æÕìéžaÏÌ ëmÖï Cös§Í~î´y—ë¿Ó9YJUg°.0~áÈ#U“šf›_?Àݼ˜Ü-ƒ—“ý;w›|çn³ôß/÷½dmmîE~Ÿìß·h~šìçd™Cös²ìšE• ܇<—¼o!ûõõÍ=ȳ‚fp}È·ë ̉ó#æÄkëû”ðoKìùwr³ô8i”Ü™<½‚=û¦ùmžL® Øïß¹k.s¬w˱yŽ=þÚºz¼X÷ï[4ßAöï[4_@¿ÿ´Þ:=iîÇ_²_§Ó¼C_“aÏgä³ö‚fؽäÍÒœÞ%à=póXrã ®%/!û÷C˜g’ý=ð]Þ7C¾ €_"û÷C˜o$ÿ“|Z ¿¼+Íé]þV½kÀ¹Ófÿ[µÙÿV­6lÕçL9fFÃs¢G:_ɼ‚ÖO‰Ø?5b½i‰õa%Ö—§õ]ÒãÄíßa^Eë]Òl3ûç’Íö¹dßïßQ=^r¸–ìçN›Ûð¤{¶Šp_ò¼ˆýG”X¿—î‹Ëöû욎¿FÉÈ›gØã«6û÷8)µ1g3ØŽ?¯ØÜüÙÏW2?Köó•ÌýÉkÈþ]~æÙä–4Ã&‘wLsÚ¿ÇÅ=ì×Ùæ´y>¹c€g’Ûæðd¿†¥ùfò·ä³"üWòþ%|?Ù¿oqwšÓ»ÓœV¶ês¦€Ù0¼sŽùáç+™ý|%óEä— ì?!Ͱêñ±g»î_bÿš4ÛvK“FÉ+X÷kXšýú!»\òú {ÆìiœÃƒrì_’c÷{Þ&±ÿ¡ˆõ­éw¾žüûOJïïNsz÷4§5ƒÕÆžÍàÝÞS5û{ªæ1äÈþÝGæÅdÿŒ“ùò–<€¼|x„Ç‘ý»Ì=Éï‘Ns®EÀgœÌƒÉ 3xy¹S€§’ýœ,ó0òrrÇžIná!äOt¬z‘=~ü=U³¿§jþŒÖý»ªÇKÀú¾9<œüeŽÛ9·€ý»Ì{ExhÄžïhÝ¿ûÈül‰=»§÷Ší±ôã |Ù>ãT“|v˜aÏaiN› Xo˜c½Žõäš{Æ’Gì©%Ï'·Ñ9YJUg°Ç•˜ sÈ{§Ù¶‡ÍÈ |y ¹SÏ&· ð$²_ÏÒÜ—<ì×Ü0?EÞ&Âw“¿'û5¨÷ø^á=Óë wgòt²_sÃ<œìï˜G“¿#wËáWÈ»ð(“U/²Çù¨ó©Išmö˜YÿPª®›‡V°þ9ù„ ~,Íoóvëýng ­ûõ,ÍSÉM ì°Àzˆp׈=ŸÐúq%~ÿ^aó!éõ螯GÍÈ~Í ³_sÃüAšßÕã%ÀþYdó±9öOαި€ûسXçd)UÁ{ü}wÏ€kcíI¯ÕÌgÒ {‰ìßQhŸ¿mZ‡‘·ÊàëÈ‹È<žìßQh¾™ü)ù¬~š¼k„o#ûµ9Í}È’K÷Ëž?XŸ'ï™Á·?#÷ ð\ò¾¹fp}ÈçD̉‰3¦M‰™ñ y«4ÏìqbßQØ(¹Gë *u·³Wznêë³3ì·Ç¯ß°¾šÖ»çðÛ9ö´-°þPõ%‡§æ˜%í xf¹Ò"ÂC"~öZ÷ël˜ý:f¿Îƾé1[“ì×­4•Á~ óiž™oXß.Çþ›rìY–cOÏ^Y`Ï©?;-b}Çë·–øÙ­ÓïoíM+°}_Y£ä÷ÈgšÁJÙ ¶ã¡C†Ù0ž¼Y€{‘ß'ŸŸÃ/w*à?’‹ßBþŒ|| ?CÞ-ͶVŸk2$¯ ûùÒæÕä뼈|D'ûùÒæûÈëÈçGøòN%ü[²?çP6{\Ý•a6¬&w 𔀹Ò"ͰýÒãÄ÷lVÀ½ ìù'­Ÿ±þNÄú%|g‰V‘f[«ôÜÔíß ažK뇥ÙlžAnàÙ?Û2‡‡å¸Õäî~Ö¿?ØÜ!ÂãÉ›•p¯·ó>ùç 4ƒ•²Ü:Û6ÌÉsɇeðòd¿…y¹}%—\KžOnáIä¶%üyë4§÷§ûµ?ݯýé~™ý}oópò—ä“ü8ù€~–ìßaGná«t^t½¨uÀûÃf{¸Yò|K†=Ÿ‘Øó y·¾=ÇþUäÓ ì™Q`½YÄúòš4¿ÍƒJ¬¯$Ÿ—fÛþt¿ö§ûµÀyàækÈ ðÈÿN³Ü|CÇZàŸõ¹[Äí¼5ƒ•²lÇÃ{4üióSdÿÛ§¿ïåMöÏìš»‘—“ýXæyd j¾—¼–|^O#ïá[Éß“ýió;ÉmhN· ¸–ùC²?ÿhCÏ?ÚÐó6ôüÃ<‹\“ãÉþüCmØì±½{‰91Šìû< =NÜ+uóÆlßÒ,ùô {üXÕë1¬Xošãg‡åسœ|b#oáž·óÙÏ‘> Ëî]Òkk¿Ö»ûyòžé¾´¡çmèùGzþÑ&àXæms¬α¾”Öýù‡Rs6ƒíxð× æùäŽ^Dî@3ì9òöiNû5eÝÉ‹ÉGgð+dŸÓmiN›[åð½d¿V¥ùjòûd¿V¥ùiò®%üòWÉÒœ>0àõ´y:Ù¯e~˜ìß×dJöïkR6{ÜúkPs³ôZÓ<–ܲÄü¸‹Ü Íé¶4§Ûœ£džKëþýÁæáiNW—&7ͱ@ŽÛ\NîXÀþ÷i³_«Ò|9/áKKìƒÖL÷ëÀ€×Óæ d¿–Ù¯eþŠÖýûšÌï’ýûš”Ú˜³lǃ_Ëü,ÙßS5ßCÞ4ÂW‘×’{Ñ óï8ˆæ™y0Ù¯¡aîA^@ökh˜—ÛåðLr‹OÞ,ÂW“ß'ÿœ~ç×Èþ}öÚÆ¿ïÁ<„¼Y÷"Ï!û9²jÃV}Δc6ø{ªÕçLÖ?Os®z¼Dø•ˆýþݺæ)äéuçAéßyMò›ä³Ó 3?“aÿn{#ï—cÿˆë ¬×X_Më#nfÄz‹ëw”øYÿ¾{Üú÷=˜kÉóÓ}1w̰>…Ü$ÀWÍ`¥lÛñpwÀlØ)‡¯'ÿƒ|Y¿I>*Â/“w+áÁä¥ÉÓl>8àµæÁ»=8ào·üíÖü%ùÜÏ"ï•ÃCÉß‘Ï)àgÉ»Gxùkòt_^$7O3û€óÈÌþÞ€Ú°Ùãð“€ùÑ+Çœ˜“cöR`Ï öì±ç·{~\bÏeä7Éû§Ùv0½Ö<8ào·fÿÛ­ùòäCö?JÞ6Çž+ÉïæØslõ‡É1bO?òâˆ=G—ð%öl™^C’ŽëfÉ~Ù!ç‘)µ1g3ØŽ?—Øì׺2O%7 ðòBòá9ü"¹U$¯$ŸáIäKø:ò"òiÎÙ‹öªÀCÉŸ“ÏÉàWÈþ¸yyÓ¾Šü ù¸~Šìßa¾‘ü±ÎɪÙãç° sâ± s¢a€{g‘÷Êá¡éõ¨yË·9 Àž…äÃ#|yMÄÏö,±>›Ü2Í9{,m[¯$¿K>(ÝGóÃdÜ<:`ÿwäsrì…ܦÀžQÖC„ý{ ÌóÈ­J}o’R6ƒíxðó•̯’÷H³Í®)Û¼$o“Á7’ÿI>?À/÷Éá?“\À7ÿF>9Â!ÿ´„ g龞ž›»;“§“ý5·y8ùK²¿æ><à5÷᯹xÍmþœ|‚ÎɪUŸ3•˜ Û¤ÙV}ÌTêf‰ùòÂJÝü0wͰ>|D€ÇÜæf9Ö¯Îq;ërì9¿Àž× ¬ï±þòJìùu‰Ûü’ÖON÷ëð€×Üæ d¿NˆÙ_s›? ûknó£dÍm¾’ü.ù þ½ÎÉRª:ƒíxð÷]ÍÛE¸?y¹S O%·KsÎþûsXCþì÷5¯ ûß}ÍSÉþÝ æaäÕdÿîóò~‰ì÷5?MþIº/G¼‡lH^AöÏ"›—Ûx&Ù¿cQmØìq#fÃEä—É»¥Ùf~¬ÄzÃôý}ö˜Ù¡R÷X1ßR©ÛcþŒ||†=ÏdøYÿî³w‚Ù¿;Áìß`^@ëí ¬%—·?Œ¼U ßQâvò4§LÏS›%û{Èæ¹´îŸE6ÏȰ¾_Àþ{ü;«Ç‹ÎÉRª:ƒíxðïï3ç|ùS²ŸùrçžKÞ7ͳ£Ö{û |5ùSòYüW²O©ù7ä¯Èsx:yçNþ|n„“.áÈ[¦ûutúïŒ{4¹i /Ì4ƒëCÕçL9æÇ©æÄ¤ó£mÄž‡"Ö÷(á %öìæ™=NÖó?Ý<³R·ßÜ6ƒ"o°ÿRò{ŽÌá 9öl^À½ ìù€||ÄžGÓý2ï[b}8ùËtNsº&ypº_楴Þ#Ãú,ò^AŸVÊf°þþ”ùEró~„Ü €»“ß&ÿ2ÂÏ‘·/á›Éß&·KǶ»ùC²f×üy¿ßCöûbDöûÒŽîK;º/íÒÝ‘·¦ßùz²GÓ1t_ÌÉ~®™Ú°ÙcohÀÌØ2Çœ¸$Çú×´~F?Y`&5ØÿHÄž%ÜfØÛäcÒùYþ¾‰ûy²f×|K†õ¥×£ækÖ?X¿$ÇϾžcÝïK;º/íÒ}ñõAë+Éç•ðò!é½cÞ÷6OH÷Åìçš)µ1g3ØŽÿÛ§y ¹a€{ýæÑäïÈ~¾±ùY²g°ùòr'šgSÉþ¾÷ÏÞ÷6?FöÏ;ý,à}oórM€G“ËîF~…ìßEhGöÏn™{’ß#¬kUÖ‹ì±äßlþŒ|vÀüðk`™w˱>˜¼”ìç›&Ljù44býsò %üÙß÷¶Ç¿ïm¾…ü¯JÝ~³ÞÉìŸw2ï°>˜¼4`Ïé9Ö'“ý»Íý ü>ßÑú9~6â6w/±~S©¬”Í`;î*1V'K¯áŽ¥×pæédÿž]óÃäà‹Èo‘ÎáWÈ>ÛŽ¥Ùfök=š‡7£ßùjòûäCÒÌ¶ëæ¶©À£È_“»fð<ò~‰¼KßHþ˜|Lû÷C¨ ›=æ—ÑlhŸ^K¯áŽ 8wÉìß³kî^ƒš¿¢õ_øÏ·¹sÏñ³>ÛŽ¥ÙvlÀµÍÝ"›¤”Í`;æç˜ m x9D¸/ùò4Ã>M>žæÙñ4ÏÌ’OÈàÇÈþ~øñ4ÏÌkÈþ÷]óJò©<¼w„o%oM¿ó¥ä7È­ÓsŽÖ»ežDÞ*ƒû’ç‘[Íàú=V›˜ É‹ Ì•S"<5bµ+±>©ÄzÛôôxšgÇü}×¼gÿ>Ãþ‘ÏNÏmOóÌ÷Ÿcýõë‡øÙ{ ¬oá«"ö¼q;ç•XŸFÞ1Ý{Ü6L¿¿¹¥îgÍk*ØÓ)ƒ§’›x@Ð{ÑJÙ ¶ãadÀlØ&‡/'¿Mö÷TÍ‘÷ˆðÍäÍK¸7ùƒäiN›;“—’ýu§y2¹Q€G“ýoºæZò|²–É<…ìŸe2ßKÞŒ~çß‘×%w ûÒžst çf?§LmØìq¸0`–tÍ1'^$7/0{ü=UóÊ?{yÄúÛë¿,±þ\‰ÛÙ#½îôkÃ4J¶÷½k’÷ϰçþ {*î°ßÿ¦[=^rxFŽýÍ ì÷Ï2U—ˆõaëË#n§c‰=‹J¬wH÷¥=çè@Ï9:œSföïPjcÎf°~–Ù_wšû“ý<,óTrÓ@^Höó°ÌãÈMh† !o–^wžð9]ó@òbò)<Ÿìßc~¼U÷%Ï#QÀ÷’×’{Eø}òù%üy§šÁõ!{ŒùyXæ³ürÀÌðó°ÌƒÓkÐêñR`½yy¯¸ýïÈÝJx~šs'¥çsÍ’ýsºæ¹ä}3ì“ág›ì°g¹SŽ=SÉM ìV`}9ùĈ=SÈ-è÷_bÿféõôÉt¿”Ú˜³|2Ͷ“i¶™W}¶™'’· ð%ä×Éûåð=äM x$y%Ù¯aižDÞª„û’ç‘ý–§\ÃÒ<‘Ü&ƒ$‡w%¿Höïq2$¯%û÷-š§‘ýýpµa;™fÛÉ× 0–a} ùò¹þ ù§9|mŽÛüœ|Nz=zrÀ5,O¸†eõx!¯!w*á©ä¦éõ¨=Æü–f»†eMòäö¯È°ÿ”O$o™cÿ€ë iÝ¿oÑ<Žì¯ŠšÁJÙ ¶ãáîˆÙàç+™/&¿J>0ͶS®aiD^B>5ƒß&ûçͯ’OÊá ä øòäã#<—¼o !7K÷å4z.r=1/&ÁÉþ:Û<…ì×Ã2!ûµ¦Õ†ÍÏŸÐlðó•ÌO‘·I³Í3[VêÖÍ—_'ï—aÿ=éµiõx X·¿ùú69|wŽÛù>Çþ‹ ¬¿JÞ#bÏ·³C‰=×”ØóYzjC«Y²?9-àu¶Ù¿oÑ<<Ãþ2ÜÎÀ—9öÔæøÙùd¿Ö´Rs6ƒíxðï0?Bö×j§Ñœ6ïTÂד?"™f›}FÃÏ]2#¯&wÌà)äOÞ>‡o&J>«€Ÿ&ÿ$¿!ÿü³þ3ùÇé~žþûåîLžNöï6O&7 šÁõ!{¬Ž*0¾.0KüµÚiiNûz¯ûç÷NïµÚãd» ÜŸ¼ Rw›æöÖÇ’Ë€=µi¶UZïžÃoçøÙ¶Ö*°žGø‚ˆ=oÐzëþ]‰=ëhýütOO_÷²¯°ù¶4³«ÇK€ûÍ`¥lŸžžÃúlXLöÏϘ§’ýï¾æaäÕ䎞InQÂãÉ~¾ñvûøArÈà¾äyd?ߨüÙ?¿kþùŸä+ øò~ž¼g ÿžÓ}9“æô™4§Ï ºNV}É{sæÇ¾9æÄ²ÿÝ×Ü£Àúrûˆ=c#Ö—X¯-1ŸV'ÛcÆÎ7n”Ü£‚õ´~Jšgf?߸z¼x@Àφûû’åØDý/Xß%Â7FìÙ¤Äí\Aþ;ùiNŸIsúÌ€ëd™u,¥êf°~,ópò—ä~~‹ì×Ù0?@öël˜o"/#û5¤Ì³É~ )ó4òŽi¶•þ›ãîG^Lö¿a›'’ý{ÍÃÈËÉ~~ÖYé¿Yn¿†¶ùSrï@>´„ÿ@ÞYçdÕ‹ìqåßdþ€|hšgæéë~êñ’cýËûûX‹|@Äžg#Ö·+q›7•سišgg¥çsîÎä镺Ÿ=+àü,³ÿ Ûü­Û5ïÜ öÔ¤ßß<5Çž–Ö'‘ÛFø¹ˆý{”¸Í›ÉŸ¦ûu6Ý¥6ælûõølœMóìlšgfÿ;¨ùò¼Œ|F¿Hn^ÀƒÈ+É~N–y¹m ßAþ&ùœtœ»;“§“ý:_çÐó³Þézþa^LökXšç“Ûðƒä­"쟱V¶³ižMóì쀿ƒžî/3Ì’~ëo‘Èq;·,°çòëöø9Yæ{Èk"þ¹ƒJ¬/!·K·>'à5¨yÙ¯óeîaÿé~U—€=Óöì›cÏäëÍ xDŸ]Aë"Ö§FÍ`¥lÛñ0fC+šaO‘wI¯Û:œ»dH^Lö× æ©ä–FöÏ™û’ç‘[ðHòJòyžC>¤„ÿDöï(üeÀu¾ÌcÉ3xy Ù¯µo~r¸+Ùï—Ú°Ùc»i‰Ù0 ÄŒ ižuLÏçÜ~î’y.Ù_ƒšÇ¸Àþé1göÏUrÓ@^H><âöÇ‘›”ðò7É¿L÷«&ypë«È]2øõ4¿Íþ…æääSrxbŽÛñÏ>)µ1g3ØŽÿü«ù^òZ²þÕü>Ùg›ù²þõW4ÛÌ£É~ Ks7òrrÇÏ$ûùÆæ!äOÈ~®™ù)ò6¾œü1ù˜~ˆ¼uº_çÒs‘sé¹È¹t¿Ìµäùd?ZmØì±êŸ5/'ûç_ÍSÈ-JìRb†ùç_E³íW×°4û5,ͧgXŸœaMÀž±dÿþ]s-y>ÙÏ76*ð³!b½+ùň=­J¬$¯L3û\z.r.=97=©IöksšgdØãŸW®/A¯ƒ•²lÇè€ÙÐ$‡¯"¯%÷*à9d?Ú|+ù ²_ÒìßWØ)Ûî>äÉ]2x¹}€Ç’ý:Yæaä­ ¸/yÙ¯“eOÞŒ~ç^dÿ~ˆó辜G÷å<º/f]'«~dÃÅó£[Ž9ñJŽõ Ì•)ö4‰ðUä÷È—¸iäcÒÌ͒Ÿ'ï™f³ùqò?{;yùôû;Ø3µÀ?«eÄú°ˆõÕ´Þ±„§”ØÓ"½gpÝ—ó辜p,³÷¢Rs6ƒíxðë3›· pòr§žJnYÀw‘W“¯‹ð"ò4Ãî%¯M>Ÿf›¹3y)Ù¿£Ð¼€ìßQhMö9}~ÀßwÏø§ó®¡}~À5´Ïø§óÓÝï‘£ßù“U/²ÇØ¿2̉‹æÄËi6›Oȱþ¹aŸíO^V`OûˆÛ™±Þ²„‡‘—“OLsúüô˜uO¨Ôݦyÿ öï(4ûwš“ýz–ç|G¡y2¹Q=~ móâÿ¬në¯w/ñ³£Jìo’æ´ÿ­YßÙ 6ölÛñpb¥îø6O!·Èà!äoÈWøSrïþ€|h?JþQ„MþòA%ü{²¨KÀûÞæ±dØ\K^B>5À“È;æðuäEd?_Úüy—ÿ޼NçdÕ‹ìñcñÙPfpmzÝfžOëíÒãÌ<3`½mŽõçr¬ïQàvn.°þ-­Ÿá§É?)±ÿ7%Öý³@]ÒsÓšdÿ›®Ùþ¦Û(Ùß6Ï ïà{ö¯¡õž9Ög“ý|éêñR`}«÷¸Eä¥Þ‹VÊf°~Mcó dÿ›n×€¿}šGWý<,óD²_ÏÒ<Šü5ÙÏ6?Iös§ÍƒÈKÈ~î´ùm²Ÿ;m~޼}º_ÐÌ6O%7Íàä…d¿Ö¾y ÙßÏ7_E~|p¡\²Ç¹_ÓØìÓíšf[³ä>dû›‚ïñëYšÇ Ÿ›gæÅëGçðä¬À?ËÏ6¿NÞ/bÏ=·ß¼ÄžA%Ö¤ûuÍì ÒÌv7̰§yy¯ôßóhr™cO7ò+äÝ ø&]'K©ê ¶ãÁß767ˆpwòÛd?_ÉüÙgö…Ÿw2÷#/&ûç.¤™}!Íló²ŸclBþ„ìça™!ûyXæÉ“ýÚœfÿ.á‹Ò³ÜÉÓÉþ'ópòäöïÀP6{Ü.+0KΈ˜OF¬7/á‘äµÉ¦ÇI£äÎäéìñÏ;]pÝÇ if_p¾ŸyVšßæSrØÏÃ2ûyXæQä¯ ÜN×Ï‹ØÓªÄm>E>$Íì‹Þ7O ûgœÌþ'óäãü y·\VÊf°ís̆±ä²€‡‘W“ýzXæ)ä4ÃÆ“ý5h7šÓædߨÜ<ŸìŸ2?HöÏ8™û’ç‘ý3Næ{Éþ¾q7šÓÝhN›O+áw’»ÓœîNsÚ<—ì×B1ûwU© ›=&oÏ1KV‘OOïµVŸ3˜~=¬êñB.KìñëaU—´Þæt·ô8ñu?¯ØìçK›'gØÓ,ÀþY ó Z÷Ï8™§’ý3Næö//ðÏí±g¹ýþ÷–Ø¿KúûnwšÓÝ®‡eöëa™¯É°þY¦×ÁJÙ îN¯Õº§çàî¿÷Êá¡äïÈþ}…ÝÓ±íö9mžInK3ìržæôÅï›Ç’ý3»æZòj²ÿ·Á<“Ü!‡Ç“7+à^ä÷É?ðŸÈþ]æ‹Éþzú×4§MsZmغ§×j>'%ÿ(Ç̸2Çú»´~lšaæYÖk"<–ܸ„ý:æ%iýâô8©Iö¿ïšW‘»¤fž‘aû€=OìiœcÏ]9ÖW“;ð”·Ó"ÂwDìù†ìßuaö×ÓæCÒœþ5Íé_¼žVjcÎf°Ó+˜ þžªùòßÈ'øqòOsørVÀ]È3ÈÍ"<–\Ò «%Ï'·I3ÛÞëݹß@þù¨ ¾Ÿ\ ð`òRò±9<™Ü¨€’Wk#¼ZçdÕ‹ìñã署{“_#ï0Kþ°þoZÿušsæ/sì9¹€'oá>?»ŠÖO/qû3J¬7KïÛció Ü›üyŸ þ-ùä“Òs^ó›ëûçðméùGõx)°Þ¹Àú\òa·9ƒÜ¾Ô{ÑJÙ ¶ã¡{‰Ù0“Ü"Ͷž¯GÍcÉþzÔ\KžOöël˜g’ý:æ;Èßý|cóKdߨ|#Ù_š/&ûûƽÒë wò*²¹WzáöÏ"›Ç’ý3NænäWÈþ'³ÆImØìqþd‰YÒ8ͶžéqR“ì¯G{¼íðz´gÀõ(ÌÍ~Ö¯³Q=^rì©Í±¾„ÖÛX÷óÍ~¾±¹/y^Ä?Ë_šŸ*q;þ¾q¯ô˜u?Ÿîc¯ôÜÔ×O÷żEÀÿ,rõx!ûgœz|Æ©WÀgœz|ÆÉ¼X×ÉRª:ƒíx³¡+y¹Ͱ‘ä•ɽi¶õNÿÍr¯"ûl3¿NÞ/ÀO’›çð#ä½ ø òÖ¾”ü-ùBúß$sØ÷ªíXo%AîÁãÉÛøjò:òùºVe½ÈçD̆‰3ïai@^H><ÍìÞéßmMò›•ºÛéM³­wšmîŸüìµ?›åØsIŽõ¯ÉWðÚ·s^„§Eì9¦Äm>AÞ)ýþö˜Ü´÷L¿¿ù=Z?8ƒï"¯Î°¿{ÀúÌt¿ªÇKŽõñ¹Þ‹VÊf°/ä˜ ;ðõäáçÉþ]~æß“ÿ•| Íisò\²Ÿ»džLöïܽ$à5¨Ù_ƒš»‘_!û÷8™Ç‘ý:æ{É›Ñï|5y]r_º/æÎäéd¿/æádÿ¼Ú°Ùãs³³¡yN¹rH„ÿ±Ç¿ËÏ|ùï%öü,½½$à5¨Ù^ƒ6Jös—Ìפ¿ïV—€ý}Öý5¨Ù_ƒVr£îWàg¿+p›Ý"Ö—G¬w,á™%ötH3»oº/î äÍÓ<îðTæhÝÿ¯ÔÆœÍ`;ü<,ó,rM&ûùÒæZòr»?H4Æ?I¾”f›y0y)ùØ ~˜<”üÙç´y9ùÄžBö9})ÍéKiN_JsÚüiòet_.£9}YÐ÷×—ì±÷hÀlØ6‡ç˜KiýôžAÞ/bÿò ò)iž™ç—Xo“æô¥¯§ÍözºQòþ|ù+ò/~öÝ€Û<6‡'çØSSàgGØSF¬ûœ¾”æô¥4§/MsÚÝ6ÍéËhN_p¾ôeé9‡Þ‹V{6ƒíxðk›#ûûÃæä5d¿n¥y6Ù¯[i¾‹¼šìçK›g’[Ð »ƒìçK÷ øû´y,¹eO"oà¾äEä#rx<ÙÏ—6_Mþ”ìçK›Ÿ&ÿ¤„¯ ÿ»Ô ®Ùãʯ]lþWz j¾6`~|°Ç¯[Y=^r¬oW`ݯ[i^Fëþ¹&³Ÿ/mn\b½¶ÄºŸ/Ý/=Ÿ«Iö¿O›WÑzÿ ^“f³¹S€§ülËëÃr¬¯¦õŽÖg’ÛFì¹#b=/±~ù¥¿ÛÏèu°R6ƒkéµZmÀëNóR²¿î4O&ûu˜ÍcÉs¸–¼„ìß³[›Žmw‹'o_ÂW“ýû.§ûbîLžKöç—Óssà ¯!ûsŽË龘O-àiäc"üykÍàzQmÀëN³¿î4ûëNómiNW—€ýƒÖW‘»äøÙ9Ö÷+°>¢Àí4ŽX¯X_ñ³ÝKì™IöïK¸<àu§Ù_wšýuçåôœãòôœÃ÷Ÿî×åôœãrzÎqyzÎá{ö+°çò¦{FFüìʈ=畺^´R6ƒíx¸“f˜—Ñ4Ï® Ù|Íf³G‚yù”O$·ÉáQäPÀ]ÉŸÏŒðKä]èwþÙ¿øÊôß÷X²_Ä\K^MöÏ›ß&ÿ2‡Ÿ#o_ÀW“בÏ×g“êEö˜ÿ¸Äœðï2º‚fó4›¯øŽó3Ö· X°þ­̱¾8ÍæêñR`}bým"öò:²¿5¿@Þ§„ÿ@ö¿ã^“þûåL^EöÏb]ð9ãkÎñV6{¬(0˜+~e³_»±z¼”X¿·ÄúÚôÔ'[Vêö˜Têö˜’Ïà{2ìß4àvFìYI>5Çþi9Öw,àë ìù‚Öýõ¨yAVÊfð5é9¬Ï†dÿÛç5ç[™ýoŸæIä#|yùšaoL3Û΃9¸ßE^MîžÁ3É-<„ü ùÌ~‰¼Kÿ‰¼I„¯ ¿C>°„ï$é~ 8Ììï¨ ›=öæÌŒÃrø1òvæÍ=fÉš{zF¬Ï&·,ñ³“Jìß1ý×'ÛUà›*u?k^FnŸaϓָ–<ŸÜ&ÇÏ>HÞªÀž¾é>š¿¡õ3#ö¿±¾K ßHþ8ÝÇéyjM²ŸG6 àý¥6ælÛñàï!›kÉKÈþݺæ·Émsø!òÖ|)ù rëßGÞµ„%ï›æÙuë½}¾™ü)¹w¿F>4À’÷ÍáÉäšMþŽ|N„Ÿ%ï^Â7‘—霬z‘=®ü=dóŒ s¿[×|OÀ,ižcÿ {V’Ï+°gyÇ_±ÿ‹ˆÛ¿´„¿M¶ÇÌú‡õgÍÝ+u?k~›ÖÛfØÿ\†õíößLþ6`ÿ59\)àÁng)ùØ?LŽ%n¿_‰õ·È4Ð VÊfðõ鹪Ísò\òé<™Ü(À£Éß‘äðrrßžGná‘ämJønò÷É7Ð}¹!àýaóR²ŸãmžEöóÈÌÉMsxy!ù𾇼i„¯"¿§s²êE×§ÏÍ’ýºæÒ<3ߟaÝ΋vwس”Öͱ>+Çí×XŸZ`Ó /ŒØßµ„?)±ç¸ôþð ï›ý<2³Ÿãm¾-Ã?Ììç‘™HsÚÜ#Çú,ò^<”üyŸ='¯DìÙ½Ô{ÑJÙ ¶ãá`ša÷N¯;í½j{¯Ì}y¹}?InàZò|òÿÃÞ»Ç]>•ÿÿ÷~ŸœO 9E 1…n÷aß÷½ïqŠŠBQ¨Èa†Œó E5däðA¡ŠB9Dá“CäPôAƒŒrB¡PÈá·¯}¯Ëëõ{ïý^ïµÖ¾g?ïõÇÌó±kï{¿÷^k½ÖºÖu]kÇ|ñ„| ñSÄê/-| ±úK EüOÃG’f O"žM¬±LÂgë™®ðL∷‰ÁׯVæ‹^(ŠôóYtâe£sò{Ö»RC'„÷ìi#|/Õ÷WÀ§TЦ¡~2ñMÄãcü­ b¼vùm¦Ï!ViáóˆÕ_Zø â»3´ÙØìA4ýtœáK»P¯±LÂÇVÐæi£ßÂz¦Û/Úo£þ¬õ‹'¨ß7)5¸,e –ñ w ?K|P ¾›¸‡4ì*â5feƦòéÄ $|<±æÐ>‚ø9bõ½~”øÓ ø:âUSð±ÄO!ÿšx-ó,ߎà/-<“xñ¦ð¹ÄËDà‰ïŠJ ^Šô[½sP¸–Bc®O¡š;º1^2´‰ŒžeÖsã Oêi#,6m³]õ÷ï¡ÍÄÃ1ÚÜ£~½|&ñ¢)>ÃÞ)êÿLõƒêš¡>1Ïòmó,ÊSÍs}Û¬M•»+à‰Ÿ¯àµ;G¨ÿ ñûÊ|Ñe)KCƒe<¬CÎ&^"ïGü"ñ!)ø>âeà?Èh›ä6X£ |!ñ ð!Ä÷DàÓˆ_!Þ#ßC¼IþñSðω»3ðYÄ‹›g™E:=‹tzéô,ÒéY¤ÓeY°Eúä7bhÆ3Ä_L W²ðF)øæmÖÈðÚ“3´YÁØŠ¥ÏÔöFá»FÚ¿Lõ»WÐþfâ5"ð‘Äoã}.'^=Ÿàï.—âµ§¨ÿ7ñá^û´áY¤Ó³H§g‘NÏ"žE:=Ëèt™££,ïö",ãaíÚ0‹øIâícð•ÄË'à3ˆÕGZøâçˆwÊÀªÓǘ±­<‰øFb½çXø[Ä%Ö{Ž1kpåubðqÄÏ1_M¼Q >›x úüG?F¼™Ñlñ‰Ù®«Ôà…¡Hß›Aî%î¡1çÆ¨¯$àÉFÏã…X}¤…ïHñ>äa‹NËkÕ—œáK‰õžcáˆÿH¬÷ ÿx±üµíÿB<” Íù êÓŸí¸õÏPýêï"^߬?¤Ï¯Ú5Ò^øÇ]å>¸,e –ñpE´a|<“8‹ÀSˆo%^;ŸJ¬9°„w'¾›xã|ñ[Äz¦+¬gºÇ’fÁ§ìØgºÇF¸¿Hø bõ)žIXó| ŸFü ñ ø–Ò'k¡(Òç_6ÚpœYÏ7¬öÕã"ØW3ë9m£y£„ß Þ9B›Û#Ô¯ƒ‹ÑæU£sñ’ Í\âRðÙ)^»D†×žœ¡þ-ó\ÇÓs!–°æÃÞÐè´°Æh ¯¡ÍIÄ/Dh³KŒ×>£Íf Ú\ž ~Ù2WeYÊÒÐ`§+ü#â¥2ðÄ$þˆÑ¶ê<¡ |ñ+Ä;TÀWOŒÀg¿N¬ù™…ÿ@¬>Y—«O–ð‰—ÎÀS‰gk,òÿÔùC]àK‰W­€L¼tžJü0ñ–e®Ê…¢HÖ8]áçˆw" »–x³•~R¯n´žNüXÚlVŸGE#º%|L„ú§ˆ·ˆÁÆxÿ¼öÌm^OÐfïmþ‘¢þKÚ_G¼ªy.韋˜g>¤kä}„_§ú½+¨ÿGõ_Šð>×E¨ÿp þAé“U–²44XÆÃE1´añ¼/ñíĵ|:q–§Ï!Ö3ÑeÝݾ‰Xcœ„/ V{¸ðâ[‰Õ.|*ñËÄÛ'à›‰5.Yøâ·ˆwÍÀw>Éì3”'Ï&Ö'aq*Ë‚-Ò'ÿC3¾€M¼b ý8:EýT¿]†ú+2Ô7þÆÒgäLTùØ® ^º>Åè´ðKÄj¾‚XíáÂjžGõ›&às´Y&Ï V{¸ðVøâ mV6gØzÇØ8ךg<)BŒ“°Æ8 ?])5¸,e –ñðiÃÎø7Äï‹ÁÇ?C¬~LÂW¯‚O%ŽHÃv'¾›¸Çh¶äÍ}_ø\âµ+àYÄ/ Þ5ß@ü±üâåRð÷‰ÿM¬÷Cÿ/±ÞãôÝy+˲`‹ô±OEІŸ/ƒ÷‰¡%¡zõcV?&á4Eý4âyÄÛdhY†úÕÌ~TúÌb]à}ºFúðóÄÓ*hs¯Ñ<áM#´¹5B›µcÔ_£~‰õ''¨1Á{Íã)Ú|,ÿ„86Ïò]³N­Ö¼•e)Ë»¹ˆËxÐÜÅ¿+|<±Æï Dü ñ×cðƒÄðψ5³ðW‰ÿL¬y˜…¯#þ°Ñ³ïÕyÍ.ðw‰ÿIüÙ ø·Ä‰À?$þ/ñ—bðÿ%àÿ‡ø)øaâ-KŸ¬…¢H_z© :¡ñ»Â÷ï¡Íõê7‹Á·Åh3!c4¬1^¨^ó0 kfaÍÃ,|T†÷_Ôì§¥ÿˆ¤)Düˆyá+às*h³dþ:ñß#´ÿd þYŒ6ïMÀßNÐæoÄ7:Ý/)ê?œáµ?(}²ÊR–†Ëx¸:ƒ6¬m´í”¾KÂ3‰çO©€o%Öx'áS‰_&Öx'á+‰5ÞIx:ñcÄ›¥àóˆuo-|ñ#†O5ëqåIÄ7k>Káó‰Ó¼'ñ½Äý1øâJž\úd-Eú|j´íÓ7”Õ'Kx6q·Ñfá+*¨§ExŸyT¿i >—XãN1}Cù¦ï³~Šú“ˆ_ Þ&Cûë‰×5ϨcqœáK‰—2Ï%ü½ êÿEü¹ü«ïùõß"þkŒ6ŸIPÿ‹2OVYÊÒÐàSÍ8WmŸ‚/ ^ž4ì âÕfŸfƦò4âW‰U³…Ÿ%Þ1_Caö §ÍÖúZ„ú "¼6‹Q?=Fýâj‚6ç/›âýO#^…>ó2´_Êœé~ßôÓñ†5ÆIXcœ„'~ˆxƒüCâÿ)Æûÿ_\æè(KYDƒ¿Oû³ïÓþìû¤Í›§à ˆ—' ›N¬~X? mûi›ðÃÄ[VÀW¯%¾Ÿ¸ƒO'~•XϪïO¼R >›øMâ#éY^4|:=—ðÑÄOë~Zø|âu¢Rƒ†ò}ÚŸ}Ÿögß7Ú¬útJŠzÕæïmÖú›ˆÇmûiÛŒ¶)¸þqjtKxOâ߯ƒ&~‚ø ø|â4¥÷Oñ9Ÿ¡ú-3Ôß•á³mdÎwõ~–ša½BXï‡þñ¿*hÿµ¨Ü—¥,¢Á2Ž‹  ¯1_M¼R>”øIbÍM!|ñº¤agë~úŒûiá™ÄzV-<…øYâ­"ðÝÄ=1ødâ·ˆ÷OÀÿ$þl þ-ñÇ3ðO‰ßcžEb¬Ô÷JøtâÕJ¿è…¢HßûK4¢15S Í8Ÿ8MÀ{ÿ>ÁkûShÌÕ)Ú¬”ÍÐ^÷Óg˜õÜ8ÓºFÚ‹ FÛlWAÝO ×"ÔŸ¡~µõ3ˆçÆxÿ­ð5 ^»qŠöç¤h³$}æïfhóOó\gšg©>Ú<×™fmªõûVÊóಔE4Xƃú^ ¿F¬öaáGˆ7ŽÁç/™€÷'¾“x½|ñëÄšÃRøÄškZl]ktO&~‹x× øNâõ"ð™Ä‹Æàÿ!þ/ñ> øyâSðoˆß—#^f‘Rƒ†"¿ÿýèÇæ4゚±nŒú㉟ñÚÔ_C¼r >Œø¯Ý"Cý…¤gškZúLýc6ê…$žK¼Uí/® ~åõ‡E¨Šxc~0Fý'ðÿ&h³AŠúŸ§xÿÅ2ð>Úü%Ãkw^¤Ü—¥,¢Ág™u«èÁY|”„o$ÖsPá‰ß Ö¸&áßk\“ð7ˆŸ!>0Ï%Þ*_C¼r>†ø)ÃgÓsMÏu6=—°æþñ_‰õ|÷lz®³é¹„#^&H<—x÷´Ôà…¡œe~Ïq†/%^Êh˜°žƒ ?Dõ×$üsbkÞ‡øÿb¼vÈèYc¼$¨_'ŸNœexŸ)êçP}Õø^MÏu6=×ÙòN @üGbÍ;-üCb}®³#Äk ÿÅè·ðÎ ÚèsmžKÛ\––\–²ˆËx¸9…6¬Av$ñcÄšgãÑæ.ðYÄ‹WÀ‡?I¼}¾™XíÆÂ'¿E¬ûlá;‰'¦à3‰ßŸ¿Iü¨á‘f O%~‰x» ø&âõ#ðIÄ/OÁsJŸ¬…¢4ÖL4ã@⻈õîé3’gCùð®~šø ðï‰û#ð¹ÄËÄ`µ Ï¥zµ ë>[xåõǤ¨Êh^c¼d¨¿“x=£Ù?2ýTYέk†?j´YøÇ´Y:#B›‡‰7Œñ>7Ũ_úE—¥, –ñPM  çOHÁÇ/Iºµ?ñƒ†Lz&<‰ø bõW>ŸXã„#^&ŸJ¬wBïNüñ)øBâõè3ŸIüºa™'õLWøzâu+àã‰_#Þ+ßF<¡Ôà…¢H¿™@K^H ÓShÆœõ;f¨¿&Ãk'=ûq„=¨°ìAk†Õ_Iø{Äÿ"þ\„ö!Þ9ëÂz'„ðe ÚTSÔŸJürŠ6‡exŸ§ÌskÆoÍðÑÄ‹ >´‚ö÷SýæøÊm–ÁÓK¿è²”¥¡Á2.‰¡ z¦+¼?ñÄz¦+|&ñ¢¤[{ÿ™xÐì¡å¾ƒ5ºÀ/RïE|±Þ,üâÅàïÿ›øó ø!âO¥àŸ¯˜&~ɰüý¤ |,ñÓÄŸª€^úd-¥±f"mÐ3]ákˆõLWø°óñ¤aWe¨_Óì¡¥Ï,ÓÖûƒ…_¦úí+à+¶5ÆK„ú##Ô¿á}‰QÿxŒöKÀ?IÐþC)Ú?E›Sý—3ðŸˆ?j|¿¥?¶ ügó,ƒð÷‰ÿ]úd•¥, –ñ°bmøñÄÛÅà+ˆÇ'à™Ä/o“‚¯'^4lñ\âŒfÿ´Îîÿ€ø?Ä_¨€&Þ0ßD¼~ >›øMâ#ð‹Äzw“ð}Äzw“ð/‰ßožåü¨ÌU¹°éc_Ž  ¿3Ú&܃£MW‚ú© êgSý†)êÏJQ¿x†ú}‰o'^Çh¶ô™÷v¿Ú5ò>£úWÀ?5þÒñ¡þØõKÇxŸo?£ý– Úß• ÞÝÔ/)Ú¬‘¡ÍÉÄo–>/¥f¸ÌUY–²Œh°ŒÍU)¬öUá㉟%ÖøዉWŽÁ‡?@¼E¾Šx…|ñãĪmç“¶ 뽄˜q®<•ø%âÉðMÄz¦+|ñ Ä»Äà;ˆ×MÀg¿F¼W ¾­ôÉZ(Êù¦o¨6¨}Uø~bÿ>øÕ3%Fý­Äk'hs*q”¢Íî)êï¦úž |2éÙ[T¿«Ñì L?oXÏt…?ZAý/*h¯gºÂz¦+ü0ñ†1Ú\D¼R‚6‡&¨’ê7OQeŠúå³ò<¸,e –ñ0!ƒ6œFm»´MxñÄz¾+|>±žï O#¾—Xó; ŸK¬ùC„g/‘‚÷#¾ƒx8_H¬w$ÿŒžëgô\³‰Õ'\øDâqx&ñ Äê‡%<‡x³2WåBQ¤ŸOÏ ok´íBó{*_J¬ù…õ|Wø_ÄŸ3:×/ê5¿£ð·b´yžê?“àýï%þbŠ6W¯”á}fëÉÂÛ›çú=×Ï"Üw$¬¹±„'~ÈYe)KCƒe<¨–ð²)ø âGˆ·ÎÀ÷÷m“u»ÀÇ?K¼U|1ñø0⧈·ˆÁ/’€!~œXsnÿ’X}§…ÿ‡ø¿†/"Í>šø%bõ¾‚x|VÍ.Ë‚-5iÃ.)4ãúõëfà32hÌêFϤŸ,Þ5ÒFøÐ.ÔßO\«€O'~•ø‹ÞçVâjŒ6§¿L¼}‚ö7÷¤àsR´‹ê÷ÏÀÒøš]dúlÍðŸºP¯¾ÓÂ?&îŠÐfj„zÑìÒ'«,ïö",ã!‹¡ Ó‰#Ö}›ðåÄ«§àŸǤ[«f_Lz&<•ø%bµ ?I¼_¾ƒxÝ|9ñê øâ爷NÁ—¯BŸùëÄo¾„žåÒfaÑ>‰ø…Ò'k¡(5S ¸‰xýšqR‚ú7‰÷K¡I/¦h¿m†67d¨_Ýø1éÎxÃj7¾8‚ÝXøÔ÷G૞ ¯ƒÑæÍï³_~ Aûá|ñk)Úï•¡þ¶ ï¿µÑæKH›/1Ú¬ü… øa£ÍÂFå>¸,e –ñ°Mm¸ŒXc„gk,ðîÄw"^tëâÇ _Jzv)i³ðlbÝk _A¬{Mᓈß$Þ/ßA¬gºÂÇ¿F¬gºÂ·O Ïñ*Æ6ð 3ç(Oë*5xa(Ò÷Ί  oïCcn¡% Ô/ Ú¬–¢Íy)ÚDxwâ›3¼¶ÇèÙ¥¤Í—F¸;AxU£gº×lŒ—í§?L¼e ¾:ÆkõLWøPâû´×3]a=ÓÖ3]aµç ?ftúf,3<‰øÆ®r\–²ˆËxÐ{„5³ðâj>•øeb½‹Pøfâž|ñ’)øëÄ'þdþñ{¶]FÚvi›ð½Ä›VÀçë‹Â/ƒ÷#~‘XmÝÂ÷¤àk‰7ÉÀ¿ËJ ^Šô%½×èf=§<>‚fÌ$žG¬w ŸãµË$àÄÏo#Èðž§¿btî2Ò¶ËHÛ.3Ú¦ü ÚŸXAýóFËã%¿¡ÍcðÕ1Úl”€oNÐf|dŠ6‹dàˆ—[¤ô‹.KYDƒe<ô=»¼ÎëuO ~Žxë øZâEàK‰ûbð÷ˆÿEü¹ü+â÷¤à¯ÿ‰¸/ÿšxEó\¿4ûå ˆõˆ_Òúã—´þø%­?„Ï#Žbð1ÄO«¸ðUÄê^–[¤Ÿ,Ñ5Ò?„ëÑሇ+à3*h¿H>Äè™ðëÄߌñÚG‰7IÀ?"~›x·ü[â÷gàofø[‰9Ÿþ¥Ñéñ†ÅžT3üRÚè=¿¤õÇ/iýñK³þÐú¨~›ï©÷*6ÆKV?ðÆxIQ¯~àe)Ë»¹ˆËxÐX á6ÿ–ø#FÛäï ]àcˆ_!Þ¡¾Šx…|ñ}Ä1ø'ÄJÀß'þ7ñá)ø!â 2ð‰Õ÷êŠñ»Â§«f_Aš-|+±j¶ð5Ä»%¬±[eY°EúóÝ)tbã :qN†ú%;MúI½úžÞ5Ò^xÕoVAýyÄQÞøfâ5bð‘1ÞóE£yñ’ þñí?–¢ÍRÔ(‡øï†¯0š]3¬÷ ?ažë Òì+Œf+‹Ðff„÷Éb´™B|k\jpYÊ"|íÛ„57–ðƒÄš‡ùŠ÷÷ œtë§Äzß•fl+O%žM¼a|ñâx_∷‰Áׯ–€g?K¼c ~„xã | ±ú~_%ïßžAü,±ú~ _L¬¾ßeY°å ³oS͸&–LH¡Ǥh£÷÷ —4LïïÞÍìG¯4¿óxÃz¾+¼jõÇVPÿ4Õ!Bý¯¶5ÆKŒú³b´_Cý²fý!ývqóù…÷%¾½ mÔ÷[øtâW‰¿•>Ye)‹h°Œõ1¾ƒx8ŸAüñ^ ø9bõ1¾–XÏD…D¼”Ѷÿ`7>‰øâ]*àë‰7‹Àç/ƒ ~Žxë| ñ*)ø;Äÿ&þrþñÍsýšžKXíáeY°Eúžú ¯CKfŨ’xóm®4úÝ/)Úœ‘¢ž‰ Ÿ¡ÍsF³ÿ׬S•§?Ü5ÒþÍ:Uë/ª ~q£Íñ¡þ…íw‰Áw'hñk Úì•‚ï#þ}þ_fxírfýñkz®_G°‡ «=¼,ey7Ñ`j_žBü,±æÆ¾˜X}—„÷#¾ƒxÛ|9±Æ" ŸFü ±Æ ÿÝðÕfl+O%~˜X÷ÖÂgëÞZx_âû‰k1øtâ,O'žC\MÁ§¯LŸÿ°Ò'k¡(Ò¯Ô¾*|E:¡¹±„O'~•X}—„¯&^)Áûœ þMâ]R´y,EýfêoË aFÛ®Ž°·¾ŽøÃ´9–X÷Öº·Öµ¡ðZ1ê&~"F›íðM ÚŒOÁ3‰ç™gž’¡þÖ2OVYÊÒÐ`=¸†´Mxñlb ¾Ÿ¸O'~•Xcœ„Ÿ%Þ1_C¬w7 Cüñþôùï4|-=˵¤Ó×Ò³\K:}­Ù[(¯%¾ŸXóV^KkŽkÍÞBùBâER°Æ8•eÁéÿk›˜]=»gøÒ®½Ö¸ ᯡýÑÄO"FûÛc¼g-_ }–¢^ïnžC\Íðž×d¨_Ù<˵¤Ó×F°_Á~­Ñiåÿï¡ýï‰×Šñþš·òZ³æPžEür‚×nŸ‚¯,}²ÊR–†Ëx¸´a€4ì4bÍ[yiÛu¤mב¶]GÚviÛu¤m³ˆu?-¼ñĪmב¶ «±ð™Ä¯%ÞÄ<×oäý»Àg¯^AüñÆøâ%cðw‰ß.}²Š"}{ Úp$±æ­Ö¼•ב¶]GÚviÛu¤mב¶]g´MYrµÖ ë~ZøêmÖNP¯ÚvÑ6åÝS¼ÏS)Úo‘/$^Äè´ôÕ̳Ï2Ï"üdÚìWAýÄëFàã#¼ÏkT¿cŒ÷y$F›­Ë\•e)KCƒe<ì–@~Küþü?Äÿ þt¾ŽxU£m×›ñ¨<ø^âþ øâJžL|±Þ}$|%ñò x:ñbµ ŸG¬97®§µÈõ´¹Ö"“ˆo$ÖÚÂçkí²,ØÒX3%ÐŒ%Sðþ)4ãAªŸ˜ÏÌÐfQ£Ó×›¾¡<‰øFâTÀß"þ«ÑláÏD¨ÿE„ú÷Ũÿ†Ñ¹ÆxIP?™ø&bµ ÏLñž/¤x͹q=­E®§µÈ l—kmaÍ¡-ü/b½#¹,ey7Ñ`ÇEÐÍ%|*ñËÄÛ'à›‰{RðÉÄo«ÝXøNâõŒ¶ý͕֬g¿L¼{|7±îG…Ï!Öý¨ðþÄOLÀ¿%ÖµˆðO‰“ üeâßkìµäê_½ |DW©Á C‘¾÷—š±s žC36MÀçš=hc¼¤à)ÚÌ¥ú­2°Ú…W6z&ý¤ÞüÞ³k¤ýoÍ:UyÓ ø2âÕ"ðñÞçYªß1Fý51ê'$¨?Æœaÿ–Ö"¿5k­ÿ'Õ6ÿ’XóoH?_Â<—ð~Äwt•\–²ˆËxˆ+І=ˆo!îÀ?"~›ø€üGâ$à ŸHü<ñÎøvâšÑ6¹ øƒ]àoÿ‹xR|#ñ"𷈟'þL þñÒ øâ•Rð,â'‰÷+}²Š"}iÛ ´árâe#hÉÄÏEh³uŒúkcÔ¯’€¿ž ÍßÎ5ÆKŠú‡R´ß CýωW4Ú&ýg¹®‘öÂßÓ…6›TÀ—VÐf©|ñ#´ß Fý‰ÿKü%ó,Â%Þ3ÅûÜ›¢}†6Wg¥—¥,¢Á2^4z y¤(O%~‰xr|±Þ¿+|ñ›ÄêÇ$|=±Æò Ï ~–x«|1ñÊôù#~€xØhö-uÞ² |±Þ¯ |(ñýÄ›Gà+‰—ÁÓ‰_#Ö8.aÍmY–[¤ÿodö£7›ßs¼a½GAXïQþñû"¼öÄš«YxËõk,¯ð¾ÄË+\KQ:ñ«)Ú¨¿±ð­Äk›ç’þöó,Â?èBýˆ¿`žKø÷Ô¯¡þâJŒ6“‰oŠñ·4Ž«1^ˆ5·eYÊòn.¢Á24¿±ðOˆõî#áïkŒÐïH³…&~‚xß ø~bճߑž ¯ƒO&~‹ø¨ü8ñÞ)øÏăô™Jüa£Íâû¹Bøâ׉wª€ÿ@üé|)ñª1øXâ¥K ^(Šôç#ShƒÞ}$¼m-¹/C½ÆýÎüÎ5Ãr_Ö÷UPÿk£m¿#=ûéÙ)H<—x«ïyw‚¿Õ“¢þªí×ÌÐæ¨ õÿ4Ï%ý02ŸYxwâ›»Ðf‹ ê/4ÏÒ/Úœ¡ÍëÚìƒÿ£ý—JŸ¬²”¥¡Á2¦&І‡‰7LÁ7ÏÀ3‰çoj´Mî?ýHøˆÿKü­ øyâixñ”|+ñÚ xñËÄ»§à›‰{2ðÉÄo¾ÖÂS‰gwWÀ'?Oü™¨Ôà…¡H_½.N|8…–›B3–ÎP?•x6q·Ñfé3«t¿Nü`×Èßþ¤Ñiá?VP¿A„ú#ÔwǨ¿"Æ{ŽKÀÓˆïMðÚMSðe)Ú,“¡~F†ú¹T¿•y®Û"Ø „Õ'\xÕ øp⇈7ˆÀ?ŒÊؤ²”E4XÆÃo"hæ1ø\âeðÄwo”‚Ï&~“x?Ò3µÿ´MxñÄêc,|;±Þ½(|ñj1xñ³Ä[%àkˆWNÁÇ¿B¼}þ«ˆW0ëÿ‹à.ø9bÍÓ)|-±æé¾´ôÉZ(Šô«¶5ÖLXsc ¿J¬±¼ÂWkž áY êŸ$Þ<k®fáu3ðåÞgu£mÒg4W³ð¾Äš«Y¸V_PA›,O!¾•xí<ˬK„—Hðž‡%hó±æéÖ<š§³1^2Ô¿^úd•¥, –ñ ñ3’+Zï.>x¹ ø`â{ˆ?"þT þ9ñŠ øhâ—ˆ·KÁWËÀÓˆ_5|§Y+O"¾‘ø𷈟'Þ9ßN¬ù,…O'Vÿ2á㉟%>¨Ì“µPé3zw¡°Þ](,wÖ ïZAý Ä«GhóâÅxÏïÇhÿoªÿrþñGS´ÿ1qWžD|c†¿û ã;}g„½µð¥ÄKUÀ«=\XìÆÊ?'^1k>Ká'Œf ï› Íý ÚÔR´¹¾Ì“U–²44XƃîA…÷ »‡XóIIüº]àã‰_#Ö|RÂq>˜øQâOÇàK‰—JÀ‡w¥à©Ä³‰·ËÀW7Ï2ÛÌ-Ê3‰çoS_O¬÷C_N¬÷CDüñÆ¥OÖBQ¤oëTX÷ Â«=“>#1ëã J|ÚlnöÓÂwo¡Í ÄëÅà3c¼çëÄ;%hó‡ïùéõ×¥h¿j†6?ÎPßeže¶Y›*O5Ï"<›¸»‚6gëýÂ'E¨—Ø}í.1øúmÖMP|é“U–²44XÆÃ9 ´aÉü]âë½Â¿%Ö{…%‡•Þ+,| ñSÄz¯°ðƒÄ#ðψÁ_%þ3ñ`¾ŽxÕ|,ñÓÄ_ÈÀ¿&^Ë<×ÝÎw…§¿J<£žK¼Q>;*5xa(ÒoŸM ;¦Ð˜GRh†Þ+Ü/Úë½ÂÒOêÕï°Þ+,<‡¸ZA›óŒN7ÆK„6ÇDhó Õï£þ–õk&ࣼç¢)ê÷NñÚ?Sý`†úŸf¨OÌsÝm4{œáIÄ7vü-áOTÀ·WÐf|ñ3Q©Áe)‹hðÝâw…ŒÁ/ëÝG—ëÝGÂGk\“ðÄ·ëùî=Îw…'~XÏw…o#ž!~…x| ñš ø»ÄïMÁ_%þ3ñ—2ðuÄš§óO¤Ù"Í.}²Ž"ýpËúqW íÙ(fœMü&ñ.)Úß‘â}¶ÍÐærb½‹Wú‰žï Ëù®ÖßO¼ym.¨ ~ùõÓ‰çDh³Y ¾¤h³u¾6C›UŒ¶IŸ‘}vͰ¬Sµ^üÇÞ±‚úkˆWŽÐæ˜õOoƒ/Œñ·ÖKðÚ3‰MÁ_MÑþ¿)ÞçKøº m>ºH¹.KYDƒç˜u«èÁœ¾Ks"ø. kÌ«ðYÄo«ï’ðoˆ×‰Á—k¼“ð âg‰57–ð5Äz§‚ðiį¾žë>z.á'ˆõFáû‰w‰Àw¯ƒ'~–Xïa¾†XïaÖû’˲`ËóÛŽ3|)ñRF…'~º‚6ŸŠÀ?'^,Fû¯=kŒ—mvNÐævbÍ%|zŠú,O'~,CûÍÌ>û>z®û"Ä; ÷UÐFïa–ü!ÊEh³RŒ÷9”ø~b½‡QXïaÖ{…§ë}Ée)Ë»¹ˆËxx…´aÒ°«ˆ5æõÏì«Â3‰³ øxâg‰wŒÀ·ÄàÓˆ_!þzþ;ñ'Sð‰7ÈÀ7À<‹æêW¾€xù x:ñcÄ{EàÛˆ·ŽÁ—/™€÷/}²Š"}[c^…Ï#Ö˜×?›õœò¤®-?¦šáí*àû+h_‹Ðþâåcð‘1Ú?F¼Y‚÷¼-AûmNKÑf• m¾“¡~)ó,1ÏR3|´yá—ˆ'WÐþ¦ ê×ðÚ+#´Y>Fý1ê_#Þ1_Súd•¥, –ñp'iÃz)øâ׉¿™5|¿ÛÊS‰&Ö{……Ï"~ƒX÷Ó·ë~Zøtâ,O!žC¬±ÈÂׯLŸÿâ%N?@:-|:ñ«ÄS*à[‰×ŽÀ³ˆ_ŽJ ^Šôá•ShÃaĤО-2hÌÚ¬gö÷›ßSù:â=>–øibÝO k,ðŠ1ê¿£þ âí´¹"Aýx£Í¤h“eà)ÚÏ1ÏøéôF§Ç~‚øF›…¯¨ ~\žF|o„ö›Æ¥—¥,¢Á2¶¡ 7¯‘€$~Œx³|9ñ²øb½÷AÒ6áë‰W«€'~–X÷Ó·ëTøâUð׉$Öý´ðψ?’ÿ‡ø†¢õÇCòf?á® aµ{?dæ¬Rƒ|‘>yn mX&fH|ñú)ø¤¯}“êwÉP‡Ñ¶IÛ4ýDyq£aÂûm¾Ÿêu?-|±îA…u*üZŒ6{%¨¿xBŠ6§¥¨%Å{~=CýƒÄÍÞZsÔ KÞìñ†?ZA›wE¥OVYÊ",ãa|m˜IówK¿è…¢H_R¥¹´W›köjÊ“ˆõ…¹F§µþDâç‰?“€A¼´Ñ6áSˆWÊÐæPâû3üÝͶ=Á.¬÷- ë}‹Â¿0Ï%¼t–õ‡¶‰øÐüfŒ÷Ù/k®JaÍU)|ñk)Þçˆ õ”¹*ËR–†Ëxx¯Ùw>Á>,|±ú[ O'~ŒxÛ|ñÇbðOˆã|0ñ=ÄŸOÁ¿#þ`þñ¿ ÿUÞ¿ |0ñ=ÄŸ¯€ÿ—ø½ø‡Ä‹Åà¯?Aü‰|>ñ¸<­ô‹^(Ê£f W3¬>Jš5Ü8ÃÛUPñúøìï³FŒú“c¼Ï‹ÄÛ&hsC‚úÕS¼ÏOR´Y.C›ƒ3Ô?Jõ’ÏN^+}l[ó,Â7˜g^½‚6?© >ŽÀ{Dhó÷ïóùü;⾯ýñ¿´™”¢þÆÒ/º,eih°Œ‡{I6% »Œx£ÓóH§…O'~•ø‹ðÕÄ+EàC‰Ÿ$Öóiá+‰—OÀÓ‰#ÖóiáˉWÏÀG?bø13+O"žM¬v‚Ç"ì§3s±òIÄoïƒõ~‹²,Ø"}ûtâDâ7Œ¶Í#žÁGIø âOTÀç§xOâßmîÁçÆhSIÀ“‰oJÐ^ϧ…õ|ZøMâýèYî ^×ì§‹`+[±Ö¯j4[øÇÔ/¿áµSý–1ê/ŠQ¿Ré]–²44XÆÃº ´á âERð^Ä÷†ý„X}¯'m{œ´íqÒ6a?¾‚¸O'Ö{ …gG xw⛉×HÁ'¿H¼m¾œxYó\£çžJ<›XŸëoô\Âj/Ë‚-ÒWM  OoŸB?®LQ¿F†ú#3Ô¿HõÛm{œ´íqÒ¶Ç#ܹ+|¸Ñ¹Æx‰ÀGGhóÕ뽄·Ǩß&AýeÄˤàS|†¹T¿Q>›øMâ]̹õß"ØÀ…õ.á¿Ñsýžëoæ¹´ýÔ¨ôÉ*KYDƒÿFû6áåcðtâ9ÄÕ|q”‚!~…Xã…o!Ö¼•2ÏèÂÇ?K¬y+…¯!Ö¼•§¿Büõü ñÄü3âESðW‰ÿK¬y+…¯#Ö¼•Oš¹TyfW©Á Cù›Ù·©NLŽ¡7OÀ3hÉ T¿MŠ÷Ñ;w…5þ¸1^2ÔkÞJé'’·RYŸXï%Ö{ …³õÓ‰‹Ðf3£Ó·ŨŸ€IðÚW¨~‡õ·¤xŸOfhó3bÍ[ù¤Y§*O2Ïød„û!ÊR–ws –ñ0¯ Ú°i|+±Þ%,|1±Þ%,¼ñÄ»&àˆ5YøLâ׉wÊÀׯb´í©¶qá ˆõ [x ñ³Äã$ü±Æ8 _B¬y6„¿Nü ñÄ|&ñ¢xïÒ'k¡(Ò—º+Ð ½K¸±fŠÀz—°ð«Äz—°ðÕ14fí¯½8A›%R´9Œø©m¶ÈÀ/b´í)£m5Ãz†-¬gØÂÛUÀWTÐ^cœã%Býº1ê5ÆIø5â´Ñ'á )øâ§R´ß!CýUY¹.KYDƒe<ü™´aÐh›øa¬Ñ>’øEâC*àlj÷ŽÀ þP þ>q’€¿BüWâϤà_¿/G¬w ?M:-|:±Æ" Oü±æÃ¾XsJ _K¼IþñSð·KŸ¬…¢HŸ_Óh›ô™eºÀßÕ5¢Â=¾¹‚úžõWExí 1øí'þlþ-ñGRðS¼Ï©~Ÿ üÃO“N?á [ø ª×Xdáû‰7ÀW¯ãµÆxÏEÔŸ@¼\Š6ßSúd•¥, –ñð7Ò†“ný”ø=F›Ÿ1c[yñÄzúL„sPa=žI–@~I¼\ þ>qBºõâ?÷Í~®Î«wO ^®>˜øâÞü#â·‰w‹Á$þHþâÿï“‚ÿø3øÄK›çú­EþAkáÙĺ>‹¸ôÉZ8ŠôÕ“hÃ[ 4c×zóxŠ6ŸÍÀ¿ÍÐþýF§¥Ÿ,ÑÞ¯k¤½ð]#ï)¼kmn ^=áµÏQýÖ1ø’mVIPÿuâ´ùd þßmÞ›á³ý0Cý‰¿džñ´ùG_3aõÉúG„».„Ÿ6úýZ‹”¥,ïæ",ãA÷£Âóˆu?*|.±æÜžL|ñø|ñòøHâLj73:÷¼hXøDâç‰w®€ço¯'ÖüÂÇ¿F¼W¾x ŸF¬ù¿„D¼”y–"ØÃ…õܺ, ¶4ÖL4C÷£Â'?O¬97„A¼t žšBo^J¡C“3ð]Ú¯o´Mú̪]àÉ"ÞÀh›ð϶5ÆK„ú³ˆÁz÷‘ðýT¿y¾’xí$~,ÅßÝ+?gžñ³N­Öská—ºÊóಔE4XƃÞ$|$ñ‹Ä»Fà;‰×‹Á¿$~þ&ñ?ˆ?‚/%^*@¬ù7^ŒCø`âG‰¿\ÿŽøƒøÛÄÿ"þ\ þñð‰ÄÏë¹µð½Ä›Òçרë²,Ø"ýjrÚpñFøb╆ CcÞŠÑfÿmîLP?1Eý™Ä¯ÍðþšCúæß¾x½ ÚüÒìG…—‹ÀßᵛĨÿñÛ1Ú€JÐfƒüßAc¯ã…Xc¯ËR–ws –ñ ù7þiÖ­Ê“ˆg«}Uø,â7ˆõî#áß¿/ƒøabÝ[ ßE¬g½Âg¿I¼K¾ƒx]󌢳vo"_ŸDüñô<‡¸ƒ¯!ž€O#ŽKŸ¬…¢HÿßÙìAÿÁ¾*¬öUaµ¯ «}Uøiª×»„N¼X Þ‡øÿˆ?lü˜„‘àýß—‚õ¬Wøâ-3ðEÚ¯džKú[ý-ï/|,ñÒ´™jžKøaªß0Bû›"ÔÁ3c´É¼ÏtâLj·MË}pYÊ",ãáàÚpñ&øwÄ4zö’YC+Ï$žG¬qAÂsˆ«øTâ(D|7ñÆ øâ·ˆ÷OÁÿ$Þ-ÿ–ø#æ¹$¿ýÊ]àÈ Þ¢¾“x½üKâåbð7KŸ¬…¢H¾!…–¬žA'NÈ +Ë={ɬSÇ–Ü,Z?›¸ÛìG…¯¨ ýømfÏ‹Ðf›|}Œ6«%¨?>Aý³Ä[¥hsMŠÏ°q†úK2´_ÒÄ(KŸÌÌçžB|+ñÚ´?Õ_I¼}þ#‰Õ¼, ¶Hûyš±XøZ„ú¿Åhs¾ÑìÆxIÐæ¸õˤh?#E›¹Äeh3ñÆVüiökîßî« þ{ĺmŒ—õ7 +Fûçc¼ÿ´õó´ß4ŸK\ÉÐþÀ õw«xYÊòn.¢Á24èõþJÂ/^ïK|;±jöë¤Ù¯«f _M¬:'<‹øIb½cøuÒ¹×Iç„O&ÖüÿCxñËÄšCø)â-"ðUÄkÆà£ˆÿIüÕügâ/•>Y E‘~"±@ã KüŸ®þBükâ#ð׈ÿá}>£þ|â4A›i êï%îOÁ§¤h¯:÷ºÑ9å¹Úkþ éošCø^óŒÂšCXóoWͳŸ¡MƒŠÑænªß8ÁûÜ’ Íš)êVæÉ*KY,ãáºÚðaÒ­ÿÇðÑÈk•'?A¬ûQáó‰U§ß ~ƒtú Òiá[‰U§…O%Ö»"„w'~Šx úüW¯`ÖzÖ¦|:±æ³>8ŠÀ»ßL܃¯"^!)5xa(Ò·Í _%þ[ýø¸Ù¾a?*¬ûQaÝ ø_´Q~ƒtú Òé7ŒNký¸õºžG¼MŠ6—¥ø[Õ õ§fh™=è›f,× k>Ë7#ä³Þ·~¡‚6ÛDàˈ—1Ÿ_xFŒ×F Úì^æÉ*KY,ãáÚð:ñN)øZâeàïëYï[¤Óo‘N ßH¬g½Âg¿A¼o¾Ÿxó|%±Þ1,|ñkÄz£ð}ÄôùB¼œÑé·I§ß&Vÿ2á)ÄsˆÕüí~àeY°¥±fJ [¤Ð‰ ‰É +‡d¨¿xÀèô[¤ÓoEð]Ö|–‡ëY¯ð§"Ôÿšx­| ±Þ1,<Ù<‹ð“T¿yŠöWë=ŒÂz£ð‹†ß&~›túm£ÓÚFý˄տLXýÀ…gÏ‹J .KYDƒe<¼LÚ°} ¾™x|2ñ‹Ä‡¤à׉¿™ÕsÐxä3(O%žM¬:-|±žï ŸNü*±î§…çëYµðÙÄoï’‚ï ¦Ïñ"F§+1üÀ…O"~xº>KYh‘>¹i m87†~,“ þÀõs©~£út3ñøÎ í×3:Ýeú£²Þ¿+¬>Yšwº1^"ÔOŠPÿÕë~ZXϪ…õ¬Zø8âgˆ·LÁ¥øl+eàYÚƒø5âð5Ä+§àsˆß"Þ•4ìâõÌ4‘5{øPâ'‰7¯€/ Î"ðtâLj·Á—¯ž€ Ö|XÂ{ßRúd-EúîAc£mã [A½Þ7Ð/Ú<¡~r ~2Æk7OÀg)xJŠ×>Kõ[eà‹‰—0:-})5Ÿ_xOâßw¡Mõ§¿D¼]„ö7¯ƒÏŽÑþMªß/AýÆÞ/)ê/'^6+5¸,e –ñЛA~Dü¶á4Æ9¨ðéįë9¨ð­Äz*|ñ„| ñ+Ä{$à[ˆ{Sðÿ>óωW4:™9Dyñ<âm*àˈ—‰À3ˆçoƒ/&^"«=¿, ¶4ÖL´ä9£sÒg¤Ô ë9¨ðÄŸ0š-|Eõã"ÔÏ$Ö»;„§Çh?'F›Íðå Ú,›¢þˆïg¨?8Cý¿M}fžeœáIÄ7v¡Mw|VmÞ Þ9B›Û#Ô¯£þôõ¯Ñhsc¼”>Ye)KCƒe<¼HÚ gºÂ¯ï”õLw3¶•'Ï&V[±ð‰ÄoëÝMÂY žB<‡Xs†ŸJü2±Æì ßL¼}þ#‰5–iQz.á©Ä/oWßO¼y¾’x|r\jðÂP¤¯é™nãLWx‹ úqa†z=Ó]$†­XXmÅÂj+>œø¡ Ú|Êèqc¼o£ý1ÚOP?“x±Æì Ÿ›âµËd¨?XïÞÈØŠa+;AÍðG+hóã ê׊Ðþbõ>xn\jpYÊ",ãáEÒõ‘~œøc)ø—ÄË‘n}“Xuz1Ò³ÅH§‹á#½XŒX&áó‰Ó<ø^b͇%|±žé Ï žK¬gº‹‘N/F:½é´°æÖXœžkqz®Å鹄?PŸHü|铵Pé«%Ð’›èDO ½99Eý[T¿k†zõ½ZÔè´¼çb¤Ó‹Åð‘ÖüŽÂË$ü/âÏ=kŒ—õšÛKøDb=ÓÞ9Aýí ê×IÁÇ¥ø ªÓ‹‘N/#·†ðúfý±8=×âô\‹›çR> ‚öQý¥_tYÊÒÐ`Ÿ‰  ¿!Ö;Ž„¿Aü ñðÕÄ+¥àC‰Ÿ$Þžôìâac7^¢Îý]ૉ׮€/&î‰À'¿H¼k ¾XÏt… ~Žxë|-±ææþ±Þo±d {¾°æÍ.Ë‚-Òß~A'ôŽ#á}ˆÿ/F›¡|~‚6i Þ“ø÷)Ú÷gàsÍÞZxm³•>󞮑6Âßêi#œVP?­‚úW‰gDxŸ¹ÄÅxíÅ1ê—HÀûß‘ ýpŠú3RÔ/’¡^ïnŒÃKšuªòÑæ¹„ÅžTjpYÞíE4Xƃƶ O!¾•xí|*ñÊ1ø0⧈÷OÀëÝ ÂÿK¼Aþ9ñbFÏ–2c[y*ñlbµ _A\‹ÀׯƒÏ#ŽðîÄ7kNᓉ_$Þ6ßWæª\(Šô+m]2Æ™®ð¸<-‚~Ì‹ +Sb´¹5F›j‚6×$¨×»„O#ŽI·¾“¡þßÞÿóF§—Ša+VŸ2aµ k\Sc¼DࣉÑ~ßõ/Pý6 ø2bÍÁ)¬98…ço”ÏÎðþk”¹*ËR–†Ëx0Ú¶t7ïßA¼n|<ñ³Ä;FàGˆ7ŽÁç/™€¿NüwâÏ§à‡ˆU§—&Vß«ebø^ O#¾—xÓ øVbõ#[†ÖÂ/"| ñSÄ;$ૈWHÁg–~Ñ E‘>³t׈6ŸB¼’Ñ3áC‰ï'®m¾ Âk×Ñæxâgc´ß1ßF<‚’â=?”á}T§—Žá{µŒY›Ž3<‰øFâTÐþD£ÓËÄð#Ö|ËĸWQx›íçÄhSMÐæ¼õQ Þ=ÅkŸ*sU–¥, –ñð:iƒú^ _K¼ŠÑ¶eIÛ„gë~Zx ñb½‹Pø)B›ˆõÎ`áë‰×MÐæøõ¯QýŽ)êo#ÈÐæ´ Ÿsó\Ò³.ðó\Âs¨¾ZAýyÄËFhsP„úGÌZDxmn!^3)c“ÊRÑ`O  ?%NR𗉟&þTþ5ñ†FÛÞ#ë÷.ð÷‰ÿMüù ø‰?¿Mü7âÏÅà_ ŸHü<ñÎ)ø7Ä›fàs‰+æ¹Æ™yYy*ñlb±¾ˆx¥¨Ôà…¡4ÖL ´áŸÄŸMÁ¿L¡+ƒ꿟A{ã‡%ýd…®‘6‡t´~œê?Vÿ„8ŽÐþàõ÷÷Æhó£õoSý ø¡m6HQÿóõ‹eøü'f¨žø3F¿Çۇ «=\Xc¬…%þñžQ©Áe)‹h°Œ‡Y´áIbÍÉ,|%±ædžNüñŽ)ø6bÍÉ<ά͕_1¼ŸXs2 O#žG¬¹…¯'^-ŸG·‚öËD¨?ø®mÔwZøbâ•´?,AýS >Ûþ)ÚÜI¼^>!Ãk_×¼Óe)Ë»¸ˆËxXÊhÛŠ´o[‘ömÂO«f¯Hš-¬±¼Â{ßK¬±¼Â—kÎI³…õ^Bá݉o&ÖüÌ+’f ¯ižk%3Ÿ(ŸDü±æÆ¾žXϰ…Ï#Ö<_—÷&àï¿M¼[铵Péÿ;™=芴o[1ÆTXï%\‘4{ű¼ÂŸ‹À¿2Ú&¬±¼Â'K,¯¾ÿÌõ/$x½—°1^ˆ—ÉÐ~F†÷‰Ìt%3–•§ë= ÂVÀgUÐfq³ŸnŒ—m^ úé1ø5â#´$Á{n¢þ’Ò'«,eih°Œ‡ß¦Ð†÷gàŸ¿ÇèÙʤg+“ž­Lz&¼Kü±ê™ðåÄz'„ðÄÏë—«O–ðw‰ÿIüÕ ügâAó\«ˆ–wO&~‹x× øâõ"ð™Ä¯ïƒKŸ¬…£Hß^2ƒ6ìŸAKþitneÒ³•IÏV&=[Ùè™Ö_dl­ÂëGhsR„6oRý.1êï NÐæ â׈wLñ·IQ¿q†ú[2Ô¯iÖÒ—1Ï"<£ õs©~« ê/&^"B›ÃˆŸŠÐf‹õW—>Ye)ˈËxPŸ,á_ ‹ø¯ÄŸÉÀ¿ ~ŸÑ¶UI³…gÏ#V?já[‰5FHøTâ—‰5.Yøâá|!ñ")øâ׉5ç—°æüzŸ™‹•§Ï&Ö¸dá+ˆÕ¶/¬¶ý²,Ø"}õ» ´ámâRhɉ?’¡Í3Ôÿ×Ô¯Jš½ªé'ã Ï&î6:½jŒ¸äUM?Ñ×jŒPc¼oãµ—¬õk'àY Þçeªß>E½ææÖû…5ç—°ææÔœ=Êê‡%¬qÉ—Ü/ê'«m¿,ey7Ñà÷Å8ß}_ [±ðeÄš÷QøTb½ïHø0âˆw% ÓûŽV#=[ôLø%âC+àû‰5φðÄêÓ"|ñkÄzw“ð#Äz¯‘ð߉?IŸùgÄ×´zŒ¸&ácˆ_!Þ£Ö¸¦²,ØÒX3Åе ¿Aõ;'ЕyÄSR´¹•xí mô¾#á%Ìù®æƒoXs2 ´þ½Ñ6áµ"ðÑ^û±ÞÝ$¬w7 ëÝMñ’ Íº)ørbk>øó\Ò‡5®IXãš„çoV_^A{k*KYÞÍE4Xƃƶ ÿŒXs· ïMüâ/%àÿ#JÁ? þñ2ð¯‰×2Úö~3‡(Ï$~XíÞÂïo#Ös\áÓˆ_!Öœ!ÂOLÁg¿N¼wþ±Ú½å|PíÞeY°EúÛAtå•:±C ¾*F›‰ tågÄïMÁßNñÚ¿<ÿ”81{Í÷›µé8ÓºP?›XíÞÂj÷^?_á}–ÁÓc¼ö1ªß,ÁkoKP?!CüñÞóª õ+˜ç’>¯voaµ{—¥,ïæ",ãAÏ>…ß"Ö³Oálj?Kü‘ü?ÄïIÀß"þ+ñGSð‰—ÎÀß ~Øðx3g)O"žM¬±LÂ7¯Ï&~“øÈ¼H>„ø>âRðµÄ«ÐgÖœÒeY°Eú’ž} ëÙ§ðÅFÛk¦mÎ!^2=ÆkŒñÚÝ´ùc‚6IÁÿCüí¿”ÿ/C››ýôxÓ7ǾÔ<‹ðª´ÑX&á¥#ð7ˆŸ‰ð>[Æxí]ÄÛ'hs‚ök¤hsaŠzÍ)-¬9¥…ŸËJ[tYÊ",ãAý¢× =[ƒôLøFb=ï>‹ø â#ðoˆõLIøÄo˜Ðû¿A¬¹5„ço“¯'^Í<ãš1öÖÂ3‰_ ž^Ï!ÖóiáóˆÕ§LøâçˆÕ§LøÚ¤Ôà…¡4ÖLÆGIï–T¾”x)£a‡?]A›OEàŸë=Âûÿñ‡ð±ÄO*¥÷7š×>ÏYÄ‹›çZÓŒkåI]#mÖŒ±·ÞÐhöšfªíÇG`½ ¹1^ˆÕ§Løbõ)>#Aý"¥_tYÊÒÐ`«¤Ð†ïÿøóø‰70ÚöÁ:OèŸFü ±æz¾…XíáÂ?#V{¸ðÞÄ&LÀ?%Ö<_Â_&þq_þñ¿ ¯Eë’µh]²­KÖ¢u‰ðÄš3Dø$âJŸ¬…¢HÞ+…6Ü—B32Ôÿ$C}lö ÒO–艹Þ5Ò^ø1ªß¬Ö\Âz&*¬öðÆx¡zµ‡ «_qc¼$¨?*Aý?©^ó| ÿ’x¹ í¿™¡þQâMŒ~¯Eë’µh]²­KÖŠá‡Õ/ê§?áµÆ¥—¥,¢Á24φðbÍ%|±æ>8&ÝÚƒøÃk“ž Mü±Ú×&=V¿báiÄóˆÕ§Lø2bõ)>ø.âõSðÙÄoIÏò¢á ô\ÂS‰g«N ë]ÂeY°Eú§äÙPýмQ5S‚úåSÔ™BK£úm3Ô_N¼¬ÑìµMTþS×Èk…?jtZøÇÄêW,<‰XýŠ…Õ§LX}ʄߠúðoˆß—‚¿‘âµÏPý–>ó]ÚldÎzÕ¿BYý¥…Õ_z‚Ñiå‡*ež¬²”E4xi›ðLbÍï(|<ñ³Ä[%à‹‰Õ_Zø0∇IÃ.$^Äì­?#®Ix&ñ Äš[CøzbE¾x _K¼Jþ:ñƒÄê“%|&ñëÄê“%ügbõÉúp×è*5xa(Œ¶©~Ì6ûQáíbðý1ô£–€O'~5Áû¨¿´ð­ÄkgàYÚ¿lê?dÖsã k\Ó‡bØ…5·†°æÖÖÜÂ'/£Í‘1êIð>{ßF¬>YÂÇ?E¬>YÂWk,²ôùeºÀv•ûಔE4XÆÃ‘]ІLj·­€/'^=Ÿ@üñÖ1øbÕ9áï/•‚¿GÜ•&^ÜèÙ:fÎRžD|#±æö¾‚XmÅëİ ¿I¼_ ~€Xã…/$Ö; …!~Xï§*Ë‚-Ò—îê‚6¬oôLøì êߤúý"ðÚ Çà3ˆ_‹Ñ~¯|ŸÙg MÑæÓêÿDü£mëİ _J¬9™…¿g>c¼Dà©Äoƒ¯&Öøca?~™x÷m4þx÷S5ÆKé“U–²44XƃÆÏÈh›Ü*g]ÊG¿H¬:-|ñNøQâMbð¥Ä} ø{Äÿ"þ\ ¾‘¸;ŸHü¼áõH§×#^tZXmÅë‘N «@x&ñ“XsM ÿ”XsM¯oæ/åIij‰u?*|ñÄû$Y E‘þùl ýØ1뙨°ž‰ Cü”±µ k®iaÍ5-¼›Ñ¹õcìG…e?:Þ°ú. kì“ðÓT¯±OÂ?7ûìÆx‰Ñþ,b½ÇIx_âÛ´©¥à R¼çºêÏÈðÚ×Ì3J^Ü<‹ð¾]¨Á<—ð.´¹žxµ2OVYÊÒÐ`=´áâ5cðw‰ÿI¼[þ-ñGRð¯ˆß“¿E¬vã^Òiá©Ä³‰7¬€Ï"^<Jü$±žï ßA¬ç»Âg¿F¼W ¾xkúü—¯btº/†™°ÞuQ–[¤ï͈ ÏFÐ’ƒbÔ?C?6NPI‚ú%SÔ=Åû¼¢ÍnÚü‘xãcÜkú£²ú÷Öúc+¨šê¿O,÷"× _ãµ+%h3+Aý“Ä›§hseŠúå3¼§êt¯ÑiißgƲòTb½ë¢,ey7Ñà¾>ÆÂêc,|±æ¦>•øeâícð•Äk$à ‰õî„>Ò6áçˆUÛ„ï1ÜO:-<‰øFâTÀ'ëù®ð4âyÄSbð­ÄzÇ¢ð5Äz/²ð1ÄOï@Ÿ_ïX,Ë‚-}1|ŒûÌzNù c7Ö»úbÜ ¼i >—¸’@oLð>zwBc¼¤à+S´ÎPF†úÕMŒSŒý´ð¥ÄKUÀmÖX Æx‰ÐFcœ„»c´¹"F›q ÚÌLÐ&KQ?=Eû9ÄÕ mô^dáh‘RƒËRÑ`+˜½Z5Æù®ð4âyĪÓÂׯgÏ%Þ(ßL¬:-|2ñ[ÄG¥àlj?›Kü~ó\1ö Â3‰çës Ðs Ðs ŸG¬¹±„"~…x|ñ&¥OÖBQªf=7Îð$â»Fúpw|ñâÚïK|;ñ:1ø8£sÂË$¨?0Á{Î%Þ*Eû»S´ïÉÐæâ%¶ ˜çoXcœ„gës Ðs Ðs ˜çªÖÜX»Ähs}Œ6›%hs9ñê)ø„2OVYÊÒÐ`?J¡ o5oÔ ÛÊS‰g«­x0†­x0Æ™î` ãÁ{ÐÁy6„o%^;ŸJü2ñö)øfâúüWO4:=$ïß¾˜xå ø⧈÷Àÿ$þl þ%ñre®Ê…¢Hß~.…–lA'®Í +›[ñ` [±°ÚŠ…ÕV,¬¶bᧉ?eô¬1^"Ôkž ác´— ~Z‚÷ŸGõ›¦àsS´Y&ÏÈðž‘ÙO™¿9ΰ¬¹µþUóŒÂS*à9´¯Fh ñÆ1ÚœCüñ®Ié]–²ˆËxøfmx”X÷jC¤ÓÂKeàˆÿh¸F:]‹a+®Å° «­XøDâ7ˆÕ÷Jø7Ä)|±æÃªÅȇU3{ åRðÙÄoïGÏrñºF³%tÃ.ðYÄ‹WÀ‡ßO¼y™«r¡(ÒoïL  ë¥àˆŸK¡C;e¨¿–x£mµ¶báK‰ÕV,¬¶bᇈÕ÷JøçÄ’wZÛ|-ÆßÒ|Xñ’€“ ý:)ê#~†xË í¯ÎP¿’Ù[K¿MºFþ®ð±]¨ºkäµÂ_¨€õîEáµ"ÔŸRúd•¥, –ñ ÷ï ëý»Âg¿F¬÷ï _C¬¹±„Ï!~‹xÿ ü ±îG'ÆØ ŸJUÀ»ßL¼F>™øEâ]cð Ä«'à#ˆãüâk>mᇠo@k‘ h-"¬6ƒ²,Ø"}ï%Ò½WXïßm¬™ðÄY ž’âµÏRýVÞÿâ FÛ&ÆØ Ë~t¼áyÄÛTÐæ2âe"ðÚÏ¥úbÔ_£~‰¼ñæÜWxÛ|_Š÷ùX†ö?ÉÐæCÆf°­E6ˆá.¬6ƒ²”åÝ\Dƒe<¨ÝXø,bõ1Þ—øb¾ŒX÷£Â3ˆŸ%ÖX ákˆW& ;†ø)Ã’¶mHÚ¶!i›°Æ8 ŸHü<±î³…ç«=|Cz® 鹄$žK¼{ ~Šx úüš›³, ¶H¿R»±°ú !Bý¯­µ1^bÔŸEüñÎ Þçvb¾ Eû,OÉÐfÕWÍ>{CÒ¶ IÛ6Œã$|8ñCÄDh¯1Nñ£æÞžkÃûlaÝg ¯“â}.#®fh*±ææ,KYÞÍE4XƃægÞˆ´m#Ò6á‰UÛ6"m~ƒXÏz7"mÛˆ4[øzbß݈4{#Ò6aµ! _L¬g½Â'ë Ós Mü±æ³¾‚XóYnLϵ1­E„w‰Á×kž,áóˆ—-}²Š"ý{³UAåK‰5ŸÔF¤mŰ! ëY¯°žõn#~w£ñ»Â¿+¼/ñíÄjC>.Åk_Mñ·fdh3—x#ó\š¦føO]¨ï«€5Ÿec¼Dà©Ú̦ú cðE1Ú,žàoiž¬ÆxIÐ~—2OVYÊÒÐ`G¤Ð†Gˆ·& »„Xc^7‰áo,<“xñ” øYâ­"ðÅÄKÄàýˆï ÖX^áˆWOÁG?G¬Ïµ =—ð’æ¹>JÏõQz.á¬>žø5â#ð#Ä: ß—¼0ùMîH¡š›BXc^…5æu³žSVcáÙIJN­¾‚¸¡Íéį1_M¬±¼Â³¼ç)ÚìG|GŠöï´ =×&湄?JÏõQz®šçªÞ®¾¿‚ö›GhA„6š«1^bÔ/›”ûಔE4XÆÃš ´á(âï–‚ÿHü¹ ü+â!£gbÓ]³ ü]âµþñç"ð¯ˆßƒ¿EüWâ=ðï‰×JÁ/ž÷%~ÁðffžRžD|#±Ú „Ï"VÛ¾°Úö˲`‹ôÕƒèÄÝ teã| ñ*´äG¤ao›zé3ËvÔ Ô5òZáG¨~ã ÚßRA›Þü£íß&Þ-F›?ƨÿH‚÷üU‚ú÷¤à¯¤hóªÿBþ5=ˆÆ¾Y ;ð¥Äj'>œøibµí ÿºôÉ*KY,ãávÒõé¾€xÝ|9±î;… ~Žx'Ò°?ë]~‹q—Ÿð‘Ä/RßGü±üâåbð7‰%Þ$ÿˆXïq>€ø!â 2ðω3Ïõñûiá™Äš¥, ¶H?\1†NMüR í94¿™ î;…uß)<œ/$^Áø+I?Ñ»ü„$Öü̾¹‚6kDhsr„ú©~×|gŒ6ë%àˆŸKÐ~§õצ¨ÿP†úïd¨ÿ·©ÿ¸Y§Ž7¬ûiáÙT¯yQÊR–ws –ñ ¹)„/#ÖÜXÂׯƒ#~€XmÅÂkn,Ὀo#ž!~Åð'âÆ•†ïðâ9ÄÕ ø<âe#ðiįïƒ$ž˜€Ï$~x§|-ñ*ôù¿Súd-Eú•æÆÖÜX5SÎb´™B|+±Þë'¬÷ú ¿LõšKøJâåIæÏÉð>›™ý¨ôŸíºFÚ_Ñ5ÒFx|õ3+¨x—ïóX„ö›Åàób´™ þ⧈·HÁ/’÷Êðî3ûì²”åÝ\Dƒe<,eöm››5¬ò$â'ˆÕwIø|â4ïIü ñ–1øjbÍ¿!|1ñ)ød≷ÍÀ7Þ‚že‹¹D¶ˆá;-¬vc቟'VßiáÛ‰Õf |q–€§ßJ¼v铵P”ÆšÉø.mÃÖ*,¶Öšaõ]þñ¿ˆ?ᵚ Cøñˆ5ÆKž– ý« Ú1EýÜõeà³3¼ÏF§·0Ï2Þ°æVßiáÉ"Vßi៫Í@øhâ—b¼v»|ñ¸<--5¸,e –ñ >ÆÂ+“†C¬qAŸ$m>šø âí*à+ˆkøâucðÄ‹$àˆ—KÁ?JüiúÌׯjÖŸŠa+>‰øbµ_F¼LžA<—x«| ñ„2OÖBQ¤o¿šBK4.HXã‚„«Æ†üIóÖ «ñ'Nk›WPß¡ýÑÚ¼Dõ‡Æ¨’xûm Þ5ÅûߢýzøLâEÍ:ãSfüŽ7<•XïQV[±ðYÄoïᵚ›Sxõ§g xzé“U–²44XÆÃ1 ´áâRðƒÄ»e`Íϼ%ió–1öÂ/O®€o"Öøᓈß$Viá;ˆõ¬Zørbÿ>ˆønâèó_E¬w$‹†ÖºÀ§¿J<£žK¬¾ßÂ×OˆK ^Šôá9¤ ›¥Ð’óÌ^­±fÊÀ—dh³¤Ùwª}g¼aÙwjýG+¨ÿ…Ñ0á¥#ðÔm&Þ2«´ðJ XϪã…êwIÁׯ–¡½Þ£ ™uÆVf,× Ë:{œá'ˆ?QA›Û+¨_'ÂûœNœÅàéq›T–²ˆËx8-†6Ä xâ[ˆ{Sðÿ¿7›XsMoM:-<‰øFbÍa)|"ñÄêW¼u ûðÖ1rX G¬¹5„O%ŽRðîÄw÷Ðç?™ø-ߦçú4=ק鹄ÕîýéþÒeY°Eúçc¤ Û&Вˉ—MÑæˆÚgh³G†6÷obö [ǰu _J¬9,…5‡¥ðCÄŸŠÀêW,¼b þñ_ÌúQxçÃ_ZøpbÍ¡]–²¼›‹hð§IÛ>MÚ&ü±æÖ¾ŒXµMxñ\â­RðÅÄK†Fü€á϶ O%žM¬y¾>CÚ&¬±@‡?I¬g€ÂW«O™ð…ÄêS&¼ñsÄ[Óç¿–X㪷‰a/Ë‚-Ÿ&mûtŒ|Xš«±f"~ƒXóa kn áuRÔŸž¢þUâ/fhsk†úµ¶}&ÆÞZXÏt…5×£°æùúLŒX a½KXø÷Äý1ÚŸ£¾btZX}ʄէLX}Êã%E{?>#Ãk1ëmÌXoXÆry\–w{ –ñpA´aù x:ñbõ—>8ŠÁ»?E¼C¾…xÍ|ñãÄzG¡ð/‰—3Ú¶-i¶ð$â‰Õ§Løvb=«>øUb½WXøVb½WXøTbÍ¡-|±æÐ.Ë‚-Òä¬Dµaršqñø<3Bû¨~›,ëÔšáj‚öç/›¢ýAÄwë…Âçd¨‹xW£sÛÆØ _J¼”± xEó,ÂGGhÿñ'b´Ñ{……õ^aá™ êç%xÿ))êoMQ¯9´ËR–ws –ñ ÷(ßI¬÷(Èý{z‚ð©Äz‚ðîÄ7÷Dૈ׌Áß%~oþ6ñ߈?ž‚Eüž ü-â¿Ô<×çê¼^øâ爷®€¯%^%ÿˆøƒ1øÛÄ#þx铵Péç×dЉ•Íô³FkÆžÖ…úyÄz‚ðeÄËDh3Ãì­?kÖ©Zñ#1Úì‘ þžïÙ›‚¿›¢ÍÛT¿[†ú?f¨ÿˆÑié{+w#~€x¸>Ãì­…‰P¿—y®Æx!>8F›{ˆ{ðwKŸ¬²”¥¡Á2~š@Þ“‚@üâ=3ð3†·‹GÞSyñĺ¾‚X}²„/ Ö{œ„$ViὈo#HÁ§Çô™÷ þ»áíéY„&~‰X}¿…o"Ög>‰XŸExz\jðÂP¤ÿ3Æì–BKþ–B3>ž¡Í¯2´2Ú¶]Œ=¨ð¥ÄºÖœÌñ§FhóÕOŽÁwÅh³}‚÷¿’xùíLQÿÕo›¡þò ï9`ü˶7Ï¢,¾ßã Ôh³ð+¨Wÿ²ícø— ?á}&ÇhsS\jpYÊ",ãaiƒÞ1 |9±æÃ>‚øâIÃn!Vãbø _O¼Y|qw'¾™x|!±êô¤Ó;N OHÁÇ?E¬¾Ó«àó1ì§¿L¼}¬v‚²,ØÒX3%ÐŒ“ˆß$Ö|XÂw¯›Ï 7ËmÞÁ¬çj†&^¼>©‚ö/o/#^&ã}^¦zÕéH§w0:­·‚6zßbYÊòn.¢Á2ôÎ]á#‰#Ö³&áˆ×KÀ'?G¼u ¾–XóF ‡X÷£_0ó—òÑÄOë~Tø~âÍ#ð•ĪÙÂ'¿E¬y³…MÁ{ÿx>óuÄ6:-yø×ìÕUjðÂP¤¿A'î"^?†fœM¼D>,Aûˆ‡Sð)ÚkÞ(aÍ%|Õ{øbìG…5YXc‘…\A›µ"ÔŸB\‰ÁÏ%Þ*ÁûÜM¼CŠ¿{ñ ^{T†ö‹šÏ/ý¼þÞáƒÌç¾»«ÔಔE4XÆÃã]І½+à?)_Güá|,ñÓÄ_HÀ¿'Ö»„O!~‰xr¾‰x}£m;ÖyÕ.ð±ÄOO­€gwGà³ˆß Þ7¿@¼K¾ƒx8_H¼B>³ÌU¹PéK=Fįª@3ÖŒPÿ³ú±hŒ6_QÿgâÁm~J¬w'%Eý_©þ£ê‘¡~i£mÒí©Þ» õ¦úAó\Â×UP¿j><ÂkŸ¦zÍ_#üëï³a‚ú‹ˆWJÁ³ˆ_&Þ=Ãû¿‚6úŒ_¢gü=ã—Ì3Ö k.aÍ%"|(ñý¥OVYÊÒÐ`Ï’6h,¯ð5ÄšÃRø0bÍa¹³™Ë”&~‚xß øvb ¾žXsX ŸAüñŽ øb >Œø)âýéóëYï.ô,“ˆgkΠ቟'þL¾—ø‹q©Á C‘>\K¡ §§Ðž,Cýâ[3´©šóÑcØ…Õn,ÜWAý¯+¨×¸ Æx‰Ðfñõ‡?ãµ›'à ˆ³<…øÖïSÍð·®ÉP?ÁødíbžeœáK»FÚk®iáÉ"Þ ÿ0Â{~ Ÿ—\–²ˆïãîa½;AxñËÄšOJøfâ-HÃ.$^ÄìAõ.5å™ÄºÏžN<‡Xã’…O%~™Xã’…Ÿ"Þ"_H¬97„O ^.L|áÝH§w#ÞtZXϪ…¯ •¼0éŸzw‚ð´úq/Õoš‚/KÑf™ ºrj†ú— ïjúÉxÃz—ß®¦ŸÔ oWAKnŒ—<“xñ¦1^«qÉñ’ Í©Ä/oŸ¢ýÄ»Òç¿xu£Ó»‘NïF:½›Ñim£y³ã%OŠJ[tYÊ"¼›Ûª /ëzXø1bõ>Xó> D|7qiØÉÄš÷qiÛ¤¹±„&ÖÜX¯g/ƒ÷#¾ƒxÛ|±ú„O"žD:=‰tZøß†'Ó³L&žL:]–[k¦ú±a ¾(†–¬Ÿ@?N"~x›¯ÕüÌš÷Qx±æ}Ö¼“LT¾ŽøÃFÏ„­ þ?Ä{Fà{ÍTXì.úÚ«‰WJÐæì¯]"E›ýRÔ«NO"ždtZë?fže2éôdÒéÉ1ò|•¥,ïæ"<™ôLø,â7ˆõþ>áßk~fá㈟!þb¾•XÏw…¯!^™4ìbµ{ïNÚ¶;i›ðÄ‹,|>±îA…§¿J¬ù°„Ÿ%Þ*_L¬w, FüñQôùõ ¾, ¶LŽ‘ëQøiâOEàŸ/CK4?³ð_ˆ‡´9Ÿx\ žiôLXíÞÂzw“ðª×»›ÔÖ3ΰh[Ͱޱ(ü=âUðÚIêoŒðZ͇%|EŒúZ‚ö§¿JüůÕ|Xñ’ï6¼‡¿e®Ê²¼Û‹hð1ü°„/ Ö¼•Âg/÷"~Žx§ü(ñ& øRâUSðáÄ]øhâ— ™ôXx*ñKÄ“+à›ˆ×Àg¿I¬öá;ˆÕÇJørb½¯Iø ⻉5–º, ¶4úU׈~ì#oå¦Ï¨f‘øybõ·þ ñ¦1ø2b½ßBXï·(Ë‚-ÒçŸÍ [«=cì5…/%Ö;Ž„ þ£Ñ<á "ðωWŒÁGÇxíÄŸHÀçÖ{ ã%E›W‰¿˜¡ýÜ õ™çšBÏ5…žk =×”w7 ?Dõêo%üÃm3kŽÆx‰Q¯÷[ëý·'å>¸,e –ñ ç¸Â»ßL¼iØ‘Äofì«{Õy».ðýĵ øzâu#ðåÄ«Çà#ˆŸ#Þ:ßC¬w, ÿˆømâ2ðCĘgٻκÀ§¿B¼G| qoþ.ñÛ¥OÖBQ¤ßêý}5ñ2ø@⻈×7Ú&}FâÔk†L¼VmŽ® ~q£a‡F¨“ê÷‹ñÚ;bÔ'¨?#ÁkWOQDŠöÏQýÖê¯%þyéŸËw§w´~Œê7« þò ê—PD„úG¨~ë¸<.KYDƒe<CþH¼Aþ!ñ‰¿•‚Ó ¼'ñï‰×2Ú¶™[”O'~•Xãt…o%ViáS‰õî&á݉o&^#ŸLüñþ)øNâõ2ð ÄÏ–|]]àIÄ7ëYuYl‘>ym X%N|'Aý߉?ilÅñBü¹ m~Eü£mû˜¾¡<©käµÂOP½Æé kœnc¼D`õ‘žGõzw“ðeÄË$à´ŸK¼U ¾&Eû•3ðaÄ›gÔ|6Ê—kÞla=«.KYÞÍE4xßþJÂižF…ø%âÉø.âõcðÙÄoï—€_$Þ6ß@¼z>X5», ¶HûW:ñ9£g5qw >‹xñ¯Ý7Ƽ ž6Æ ±ž ëù¨ð³Æž¼iö~F³•o$þ@ü-â¿Ô<‹ð/ˆß£Í7bÔ?Cõ[&¨¿:ÁgÛ(E›³S´Y"Cý~ê zÕì²”åÝ\Dƒ÷§}›ðTâÙÄç*|±úï#ÆIøbÕìýcœîã|t³6W~™X}Œ… ÎÀk,ò1b‘…/ ^·¾œxÙ|qƒ&¾‡¸7ÿˆx©|ñÓRƒ†²¿ù ÇŸ¦q†5ÎUXã\ã%kœkc¼o£ÍYÄo“àomš‚oMÑfí <+ÃßÒXäÌXoXÆrÍðKćVÀoVðÚ]"Ô?á}¶Á7Äh¿z>"A›çˆwJÑæZâUJŸ¬²”¥¡Á2>’A~Hü£gScøNO%=Vßiá#‰#VßiáÛˆbðiįï€o!^3E¼hÞ›øÏăæ¹$wý]à ‰©€!¾x§ü(ñ&1øGÄK%àÃKŸ¬…¢H?ÿzÍø»ÙƒN5z¦¬¾ÓScøN O®€ï2z&¼~„×^¡~ùíŒQÿñf ø¼í—MQñÝ)þÖê¯ÊðÚ5;SúÞ2湄åŽ/­™x{ó\Â7WP¿F„×^H¼^Œ6'?ã}vJÀHÊóಔE4XÆÃÓ¤ _HÁ¿'Þ2_D¼¸Ñ³ƒÌœ¥<‰ø bÝO _A¬ûiáiÄ÷÷ÇàSˆ+ ø@â¹Ä¥à³‰ß$Þ/?`ø`z®ƒé¹„gkÞJ᳈À‡kìSYl‘><˜B~Jüž óƒ ºòâ/m;ÈôGåK»F^+¬w ë~º1^"ð$â‰?ƒ¿Eü×ïù™üâuR´?Žøâ-3´¿šxmût0=×Á1òlkÞJáÃ+¨šø ø÷F³…·ŒK .KYDƒeƒø5â½Rð}Äôù¯%^Ŭ?¦Å8«žFÃ"fÍ1ÍŒeåIÄ7šçšfÖ¦Z"ñó•26©,e –ñ w _L¼r >†ø)bµO#žF:-|±ÚŠ…÷&þƒáé¤ÓÓcì;…gë~Zø b½ûAøtâW‰õ¾„é1re ¯”€%~’xû|3q}þ«ˆW0:}é´ðÌ®Rƒ†ÒX3™ýÙ4ÓOT?¦Äà91ô£š þ¼õªÓÓŒNkµ7ÆK†6W¯`ò0O±ïÖ}§°žO ë~º1^"Ô¡þ âOÄàó‰Ó¼'ñïüÝþ|.ñ2xF†Ï>„tú3–ÇžÝUž—¥,¢Á‡Ð^MXï%¾•XuZøTb>ˆønâž|2ñ‹ÄêW,|9ñ²½?ñ#†¿Jš-<•x6±æã¾ˆX÷ÖÂgk~iáýˆ V;ð…Äz„ð^Ä·O(ý¢ŠrˆÙ«Õ 뽄5SžA?æoƒ¯Ñ~µ<ƒx.±ú ŸMü&ñ.øú k]³·þjŒ½µ°ú”}5FNfác‰uo-¬{kágˆ5¿ôWcØ „ÕN ¬÷@¿L¼} ¾’xù <½ôÉ*KY,ãáÒ³W ´ík1ö£_#mûi›ðYÄo«¿ô×bÄ8 k>)á[‰õî&áYÄOož‚¯$^ƒ>ÿ‘Äk.¹®» |ñø ø$â7‰w‰ÀoƒÏ#ŽðA¥OÖBQ¤ÏÏÉ +›™=Ü×bìG…u?ú5Ò¶¯ÅÈ5-ü4ñ§ÌÞ´1^"ÔwÇ`É'¥mÆ%¨ŸF|/q >%Åg¨d`Í%"|±æ‘¾·jøpó\ÂOw|á©Ô?L¼e„×^¡ýú1êO"~x›ïs}é“U–²44XÆÃ#¤ [§àKˆWÉÀ_'~x¢Ñ¶ÃDG»À³ˆ_&Þ½~Šx‹|!ñ 1øâ׉wJÀ9ÿ‰ø£øÇÄ]æY¾Ã'\øtâÕ*àÄQÞøfâ5âRƒ†"ývÝÚpF Íxê÷ÊPñ£m‡­QžF|o×ÈûoZ_fö£ÂÕíO%~9BûÝcÔßãµ[$¨¿0Aýz)ø—)Þçýø2¼ö†¥J©>º õOPý¾Ôß^Aý6ê/#^&‹?[©Áey·Ñ`'ÇІ‰IÀ¯ï‚ÿL<˜¯#^ÕèÙá1bv…gÏ#Þ¦¾Œx™<ƒXíáÂÇ?E¼þ'ñWSð‰÷ÉÀÏþ†™›”'ßH¬>ÒÂ'?O¬¾ßeY°Eúç\Ò†hÉÍÄ[ ¾ŠxMÒ­£2´_Ôhóá¦ÏŒ7¬÷ Ï&î® ÍYÔ¿A¼s„6·›=hc¼Ä`=·®&¨¿†xã| ñ'3¼ÿÿf¨ßÀ<Ë7bØ„/%^Ê|~áÃ+¨ˆxó™ã%*5¸,e –ñ öaaÍéþ 3ž•õÎ`áóˆ5–ðAÄoLv±Þ1ƒ´miÛ Ò¶¤m3HÛ„5®iiÛ z.aµ{ _F¬¹½„'~XÏt…o#ž@Ÿÿ4âØ¬9Ž ç:‚žëˆñZeY°¥±fŠ¡ WÄÐ’ñ Xï ~ê5–ðõ)ê×ÍP<ñ³Äzgð Ò¶¤m3HÛf¶Í0ÚV3¬qM3HÛfİ{7ÆKŒú7ˆ÷MÀ÷«¿´°žé ë™®ðô m#ÞÖhöô\GĈ×Ö;ËR–ws –ñ ¹‹…o'^'ŸNœÅà㉟%Ö{v…/&Ö{v…÷#~€x‹ |!ñzFÛ¾Y畺Àg¿I¬9C„_$Þ6ßGü±üâ8Lü(ñ&)øGÄoï–[úd-EúÕ÷*І#hÆ×Œž5ÖLÄÛÅhŒözÏnc¼¿JüÅ|uŠ÷\;Ÿš¡ÍËf?*}¦Þ¼Q/¼g꟡ú-+¨¿Ëè·ðFÚœ¡Í1Úœ£Í‹ÄÛ&hsñz)Úœ@üñÖøâ%)c“ÊRÑàoÑ^í[´WžM¬¾WÂg¿A¬w ßN¼N >ŽøbÍ­!|±æÖ¾™Xc„Ï!Ö½õLz®™ô\ÂOkŒ“ðýÄ›Gà+‰õwf ¾ð[Ä»&à;‰×KÁW]–[¾E{µoÅð½Vß+aõ½~šøSXï(^1Fý׈ÿBšxñ x_âû‰Õ/ZøzâÕbð âg‰ÕNP–[¤ŸïAn!î5zv¤ùÝ”¯#þ°Ñ0á_TPÿ>£ÍÂÿ#ü Õkn á‹b´Y)ÏJÐæIâÍSðÄYžB|k†÷¬[ñQæ¹j†ÿDü… ÚüÚ<×QF§µÍEÚ,ƒ÷%¾=ÆkÕN ¬v‚²”åÝ\DƒŠa+V[ñQ1îK~€x˜4ìBb½/áÛ¤mß&m~‰X5[ø&âñx&ñ—XïžA¬w$ ïN|3±ÚÀ¿MÏõmz®£é¹„'Ï&Ö½µðÄú\Â'•>Y E‘~û*iÃSh†Þ— ¬÷%ë} Â/SýöFÛ¾MÚöm£mã ´þEí—ŽÀS‰gëù®ð‰ÄÏÇxÏðí >ƒÞ‘,|ñ2Ú¨ üÛô\ߦç::ÆÞZXmš?DXó‡4ÆK„6S‰ŽJ[tYÊ",ãaùÚpñê øâGˆ5–IøâÞ ü]âžEz&<•x6±ÚÀ…Ï"Ö<̇ßO¼y ¾€xù|ñkÄG¤à˜>óÄ·÷¿ìÕºÀ3ˆçk\VYl‘>99†~÷ùžQ£Ïú›ìî¨NØöÀ¯íù•Ýößó«{î?m·É_Ùs·Ý÷Ügê^»xPý=÷ùúž_þyØê{¬6ÿÞ«ýA†Ù UïëmÒÃûº{újcò!êßï@wÿ¨oÂ|ïýžw¿_yp¨È_ÛtÐnFjþ+ »®Ö<}äÿÊÇù7°›êúš|NéýaäÏnÒ¯4ŸªA&ýѳ@óÞØLþŠ÷Æ«X­Ò¹‰#¶ùÐøÍfú‡èñ½7QüBã²6Xh\V‡ìgÔJ«ÑÕúzÂL|£¾÷BÒ^Ÿ}ÛJû¦ƒ+9éÊÍÆSý+­´ÜÓv!Ñ_ ±Zú|oÞïj–lM>„tåÁ «…Du¨»¯¿/çw­Öì6L£~ׯ7=Øæ›Î_¿È7ÝÛbÒèë 1‚ªv»¶þæÛ„žj˜ésÔÞæç6¼[ýŸdßc1 „û‚G/ÖŠ}Á}ž_p!õ®õä\r¿;³Di!œ!–(VKC™ÇG}ˆáìé Ñ‹F›ž‚œvü¥ú?+Ïg‹ý½tt²·³½þFÆò`±œkù6«ÉÀ0ý1w]afµæ–ïž ÂFƒ­+ò×®mL.½ÝµZµëhër'—R=–_p« i±‰¼gLÇrîšÃ쇚ÿœÃAN,òÍí'”¡›²&f¼ÀÛ_÷ìîÉh›íuË_RŒâ!l¡ù6¹¶¶Ðžji°<j~2[­oFò4XmÑ[„X”Ú}Úf‘þ[›ü‘3¡[ÛLß²›uB9ÒK†:d ¼`Î7,6„²I/lœM„ÙñØí›G[7G !Ì6†úÆô>jÛe¦÷0gµ–{¿f¦s‡ã:;ƒS‹J«òèSš ŠfsßPœ&¿«,‡Ât.‡´0Êä?dÛÔ3dm2zñ/™3ý÷[áZÚà›ÍŒõ5ç|üÉ0kPƒ<«~”ûÐ9›ˆ‚ó¥)1È_kâ.ÖúëaÞ°³.{âr®È?‚ úÛØ¸¤;Mµ±´[ztÈÌÛÑŸ³³†×M- Û­ þ€‡ûÅ­V^+²hölýÝõ0âjçG2Üìð½¾Pcijðdj œ&N›Á¼‰ó÷eæC a4XÂù[tSßˆÞ ¼Ñv°ž¤ö6+»…µ¼±l¶J–}`˜ƒËã·&[Ò½ja>„]hné¨Ö‚X¨r?ÄXØ<ެl6K 9'k¶ž‡ÐtRJrƒFdrôêv¶;¦Ïf³whmMè­9öý!ÂîAG[mî£,ópó°ˆZ˜µ†‡·yÈQ,éß— Α!?ò‚‹bÓ“Ë´•©µ:Ü̹.±a¬mcép•ïOÓÖ8ТÕBÐÛù„ù>çv.2cwT9z*6Ü0Îþùî÷…T ·ƒþ z`zìTÜ&Ú²Ý9u_NË3„0ìá?fgÙË5Œ•CìèÍ}žû¸nåšE¸¶ðB¬Ëos Þ¼þO·hðXÙ™­Z‹óà0Gè6çÁ²KjÈÚ&²Ñ"®þ!›ym÷wù£c<½XùSU‡›Ìå ëS˜6©Æfímá'ט½[„$÷1$yø¨wbMj:o ;C˜esÃZwoOA_2ÏA±À-›p»¿6PìÙ<¾ÉâÎÜ6Žê­;OoïØÄµ¶ßUy¬î4ø+õ¢ÿ×/ÚgòîŒ/e›½ÁÀÀX%ãhcæâvÔd¾l³÷Ó«|›½e¨V˜37›¸ÉÒ"€DG­ˆv_yScHáœ=>‘žvîY^q±}}Õ»ǎþ6–ÚÆQÏ"¬R¦ÿfáT’!HòŸd]ú_4ßò¾E0~§Ü†›Ën‹ì}vX–žGML}õ™¯w ˆQÛ&§L»þ^,|ÐÆ,.O‘EhCZš}Áâ¡äWnog¬Ï{êVȸÖZƒ®ÿçîƒçûSCû{ŹeþШïëTÀMó1êÇhÔ÷9€Ïq%nÿcØh¡E¬Ë@­»ZmòM÷ôHªÀ1YVÖÿXãP¬É‡è«/7‹ ²\C\ã÷ëmÜé\aræz’šÎ5JTõÕ çƒù‡Ãæ{µœùÞû î¡Û.‹Útå‘¿6TÐ)¯cMìë뮎NàѨÈ3¹ä rý‡êi¶jÔ×7‹^îEùßV±й#c¢§ù\W ’ë*Çf`®³øÉzަÉCV»ûûr#(]?ÿPcíV+dá·±5fîf­§{xxp4x»ú?ãç®a̤ÒdfkL*…b³rüÿÛöÇFÏAYÌ>Ÿ«Í#ýµÖä¯É¤?ŒFä{ÍÌÐâC„É[’»æo;rÅè˜33´µ8äþ®ƒ׶ΆÁ§ö±’ü-bÛ_ªVòrÊÿ¥Š}w9*sl“¥¢Ì±}c“ë ½l ææ/ͳ,¼ÿp‹¥h5ˆQ×n„÷ŽÎD?2Í„‰©ÍÝì»t^[ >@þ™ÿÎ×yÉ)ç…¯¨ôöÑNT#OƉª}†£õ¡˜S²éR_·‡±Þ·w• ¬7¶† × $ÿ+ÍYñ³ÈYXB?`³¿Vÿ«a~@÷^ÄÄ_hLäúôåžéšÉ·Å¯7Ì% mýâsw¹îÃ+“F/m²aoôÒ‚–ÜücòÆ\Úbš ’é?'O@ÞWš›ÂËfbik?*8ð­v"ͧ19”nú4ÿ¯¿“/Úú°Éß*ᬶý¾m Uþú¿æi•;UY8ù5Vä-Ö(anr°‘n îææ&—¤6 ™áj˜Àà1ÕA‹…Fà Òb“æ{oïËçÝÃó›G&ÕÞQ^^#㵯Øáv®/_Ûµa¾/„ÇìYü¨>'¶Îÿ·±Ø¥†ûmlm)µa.Œi‡ë(í4xÇú?«Î¯Áùçâ-gýúÂu0L,tÛ€Yÿiû$ ù›ÂbBí|¾äâ?k3©ùL3¶Ç©î[Üo¡sÚåXÙƒ-LýíöñE•Çê¾ÕWZÍ;@õÙ¢‡¹ö)è·ÕÞg1wrè/æéìuâfŸËÓ—œ¼v(~ëCÿüjÆ\ßÂ}exhB¾¿›´ mn‘hùgümm‘ÿšçÞºàÙšûêÖ!:ÌÂ["Ü9¾Í!W8S’‚בZî¹a{9r—Ï Ïî$ÅõÛ6¨ÍëG̵^zùVÃÜ;í¾½*žæ('2!ø±…«Ò:ÍŽÙ½eØÌ…­§§{hhl®µ5F¹fñ ¬Aƒw®ÿ³R ¶H8%k´{cÖíé s²ëaI°ZYšKZ,Œ‚äŒô±Ï`à3»¤ˆpµå9guºËjÇó »˜×¢¥£I­¹ËM_K©l‚MdRÇhŽ(Ç KcþÞ¯#Öî^÷ýAB¹Ü7 NÁ”í³È?¡±ŒähµŒ/x¶gDÁЄ Ö;v#o±\du&`Û‰+ŒK²­åp¸I'“Q ò!,½&]ƒ l$"àöÛ"à®íÁ–¯“@ói¸ÅJ-ÌK–¢¶{ô0:ùßD¸ïÝ] :³!ó4ð;›ÄíÂøm½ œDW(´q.¯õ+KÛ‡´ð>rÿçÓàÏÛ9³ûyÅ vhQÛÁ£!‡‡LÙ£:à±—]È^oO³(K‰KìЭɌױ¸’¨1-47!…É"êx`Šiàßܵ¥/Ll½e|«ÙG :‘°Ø/ Ù':·ø9žÍ¿í4SÐÆït½è9žŸ X ›MºƒÖßäüw6˜ÀÃz7;;M¹DHù—Å4×`ݱˆ²Í¹n°qkq¨Ò©fÎÙŽv¬ÖÂø[ s±ŠeRŒ"Ðå ~>Ϲél,²ýµq¨¨õuè&ñN®,®Vmì½[è`˜‡°¸‡Ðœ·Ð©0É-2Ñ7>D‹€º07mz8GÙ%· \<Ù®ñ7³¸Ù¤bHíXLŸÆHÛ웨ÏhýaâÍ‹X¦çÓà‹[ƒ,§ëÞÞš1&—Œ_ 4ó{Ø û1>µËÙi§á–1RC¹Ëg¿T¢ùG=žËó‚&;¯ÀÕ|ã­³¹Ó)%˜…ñ6ä©Ê˜¦ÂpNšg”뙬 ï‚Ý¡Ápó©þá ÷Õx$öp¹×kb²È›i×AæÏ“êÜd,3HÚ¸ µu+˜3oS›Ø#·îŽÚÂÕ$#i cÚ«t½ ×ÿ^w¿{ÜtÞÜ9òÕQsgMRã×rì†9ƒ­ýç·É¢ÓÞ8ÝöýüAÛëZ›¿æÚi×›ÜõæÈûvéàcä 8ò!F§%i|ˆÞ@iIÚúæŽ2‡Ä;ÅúMA•mk«Ïýk].r |{i‡g¨ö޹ߖ™ÿÿÕàOÖÿYoþ\•y r¿‡Ì9Ë& žô¶?ÑË›ôóAí}¼E13i”ÔéÅÈœ4äVæöžðÞs’{¿r‘S~— ;addÔÎbä'«…¹ Óý'sÉ8›wH:òÈC£¬û#ò] ã­ÙÙÙ?ßcÒü®£¾Róa ®ó›‡S§Ïïi fCio`²ôqÐàÏÔÿY{þ{“ì–Û£ãÛF¦ ùÊsÂD󦢷³µO/z?c¹×l%«=AR²9ËRg®†ñË–ëÆÑÁ@#ÓãP‡eŠÍÑa.³ü-:×ðDéçw._kjà]«Ÿm¥ «­ó®Õ-dÝÎöÑ¢»ô„é.? ËÙÕxeŸ]¡ï_ÿgÑù487lÌ×Ùé÷wßTuâfá¶ê@™=mgnI:m¯¶Ûú» ö˜ÚæœíÕ¶†»Yt°ù´7ˆ—[ŽËWîC:Ü}PdLMöê¼;s:ôx6«ã%ç÷w šîìa™í‰»±Ãs¯lñmͧÁŸªÿ³î|¶hçM¡åx²ÔøŽžøä;M𳝿›ýžj˜õŸØ µ0Zö…1ZzžMù¦« ¬¯vK¢ÁVâ0÷N»Ú;pGað%…gw){–Â>6‡‹£Sò˜cƒ ×Yçz㙹¨…!?HþrÛ…õ`sg€¾Þ YW)]ÎÉmmÅ:Wq Þ¢þÏGæ·EûÍ EïTË¿æs°$s!ƒz±¨c[Ew—·Îž¬û쥂§_ °û°³ »«‡ûaãë^‰í^3ìîÉò(º•5,ÌñR{‡Øc‡•?xg‘|,R¶‰P\4Xc“&µ5l6û*†{šo%zƒä¡÷¼Uyh × ÈÎýÒ}Šô\Ñ?ÊXm¼œ2f—«@<7•¾aE¬1oíÆ»»>9¯vܼˆ:ü4Îk·§ñÊ66ØS<ô²£ë^Ïï.äMÁO™‹?›ÑàOÔÿY_öÁÅœ:½`ñÜù:Ówô·òØ_X]îç§ûþ^íaÎ{ü_ù徊3–Þ>'—v[K e‹EÂ`˜{¡ÆÒ@âã£?–²ä–ÄÎ+þ ÿŠIKWŸ¡æíÞ¡±IaÎZì!ûÝ]#Ý4x¯ú?KÍ›Ôi»GPe}mW+–ÍУ“Û]ìÖY›£m@„»…Ùïô?ߦég Ë¿YÖëû¯æ¯ \m¾cíÑ2¶öø±õGµ2eZž·òï ³^ÈïŽýƒÍK×뇋o•“÷¯6ñc®×WÃÄTäïÁäCÔF­“G>Do? K¦×7ísZáåõߦêåÚ·o¹o]N7Û:ÝÖO÷œ}peÛ¿xía7c7Ë¥Ó?ÔÝÓfßk7Õ 4éî²ðr“‹íîÃyL´w2É/ÉóÔ*‹XDƒw­ÿóÞùc“:»¬¶9áô›äÖ½•[úU»¦Ág/A,îk’@;¡NŽÈönM¡W–¿Ú$—VãQ±—°…–I=¹§Aî?™£C‘Ï_Ë»lÄ+)»Åæ8LJ!¯KN2èõ4C!òIøyZt-&€¶ì˜/ÚoÃÞY·wk­S ·EP›ålÖêÌi“Yä5¦µ ¹ù§|¾ÛIKSo‹Ed_cà\oX3!6·7»_!SÜ ¶ óæqŠ5Óä§™ó3c¹+¾[c;áha”ü !:¸Ý²cÔW:Ÿdû憎™Q=*qóÈC™?ÛÇ­†¶8¯ÿòŠ·÷« =£ø%ý´0ÜyfƒdkðœßÛ'_Ìïi¼Žo†Âxÿ8o ]­;mKÚå|RÕ÷#ýƒ¹"ÖÖ}.À1o^d¦ï~Äëä­°™ÜNÿZ¬ç‡zB¬2]­ÛóiðÁö?¡ÝjyÂ$©G²½ú8èõKÀˆë*MnûÎÎî¢mOûG=ÍÈn«?ÌáE§÷µ^>ù®œWÔ»t-?¿£«÷S .×Ñ•¤§wOÈ;Ûÿµâw¯:›=ÝryZÉ f~q?º¯÷ùü»Â-Í@D£ÁÛÖÿù œçÛH7ÜÖls®•ßš—å»úøeÙÎ:çïÇÍ‹Ðã$Ù&gU®Eo´Ëö`ÇgÏc· g,eÉÖÔÞÊ>&…ªÝâl …¥°7H ¼ß­RÓË””ïBÔ> %øâÉožjigê}÷ÜÈ9Tg#gë–ýP, ÁïäÉ á ꮲòÌé—û4Ŭ‰¶¦ú+•Þ<§Owm ”«2ìÞÌÙçm¬§— ý:¬I4×{4ø)ksãF ¯…qË€F· ÁñîMÈhÀÝa·q£—à{¿Ù½X® [˜ùkÍ4øö0nvâOÿ»ãõ}Í­÷ÍÃÑéb »V±32çi·”!v_p³Äò¹Ér•âa›é„ ™µ7Ù[žJ…t:+d[+v®eXM_³KÆÑ a/vîL!]ã›”žDµ0I-ÏY:kðÛÇöä¦êð òð^°›âÆÔðë±³s{ó3̽{Ünµ×bOV áDfãäø8·£ ö¨òË”?};9»]¸àó>̯Íj·7/ɰ»O¬Ówg7núš]E伋1|€ü#ûàñu«uºÇÚ#â¿xD»’¹Ecxük¶’x¸£wl’ïëmî#&¤Ó'ÄÛÅk®Ãáýv6Ýìð*ÃÃÿÀÎ'ÈcbµÊ çqVY|á™çPí™Ø;ÐÝÓ?v‘úÞá%UäGîØ;8±§·»*¡¥Ç¾x { ¾øø5xÿú?‹ŠûmÇå­v5û¦kõ KqgÆ"Õ—ëV‘wìåùùsV m:Ûö±íf8ð_³ýî†ç_²þ0WµßÑæ²€)óÿš÷†Âúç àüi<Öhõ*÷>:Ÿ5àÈ è#aùƒ«`jQÅ@÷àpñ£ÀwÎfØHð¨t€frsmi{Ãmàn«…Ã͹¯§Â|¼[ýŸDƒC|9{ ßßÛÇŽ<Ð]«åY.mú“—P·]`‡Ö‘¼£3:zæ2]¥/ÌU9?™ÑùÏ©uˆ1Ïæ¹šÑÛü›èâà§j!^Éøl¤)ÏоW±Vq¶yþîÚ æ?¹4Ã!ˆ³‚—‰ÀæGj¶“»šËO"ï±Z ‘ÑC¤ù Â:]„pkÉq_u\·ÓàfñÁícÞ‚oê,Vj¢ÍGo2Ï$ÙþÇï«…YεµÎçktÈ$¡7Óyë}ã&«@S`ŽNù.Û[1C¯Ç pn?`‡·ŸíÏ™ƒÛv:»=t«wüfÇG6ÿmë‹Ì\ϱÎn®Ü÷±NHÎVDûQZLƒw¨ÿóþùü¢Û»~°YÎÇÍ7ó½AÌæ‡¼öÞ®ÁÍžóYÈ@$ïŒÏû±óÖi?ÃpÑàu÷QñŸm’Ëý^QyœøØ'z¼¿ÕοÓfh÷YÆ&Ožû&ÁmKâ÷k77!Øi°úE»€ÅD5²ù`ßp¢Ókí5¦ën=‹†…¹Õk>êìû»ë¬ÛÒÝÚärwagUÝËGÉj‡êe ì ’<ÉÃjc²e!è­MÕ½µ N>aªõ‘ŸòÀ}s$ñj'Þl,â6`¤ŠV‡òó5WNÓ‹æ÷‹žßÆ[íP"Nþêí³œ…Þm:[ÏÜüÇö0¹³{ «ƒŸ°¶À°§¯6ËÔN~þð+ÐÀ§Ó^§ßƒ=¹yÜ­—v'vÖ¶cu:íemñZ²ä^¦æóýñ -6÷ù†¯W‘ÿWƒu<«í𰨇ð=—l¾€iqRX sá—‡ùÝÊÙÀÃYÂæý-ën÷]{¹– Usgp/ç;×Wû÷¯v×\KÂZ=l¬°âB1 ²ºÛ…1Tå4œëÙN´†w‚wì8ÚΛ©Ã†a縷͎ߨsfz·D,vÇÝÃͨÀÙWe> >ơ÷ò¾“ýÄãw¶ºÄk+PÍM¨äá^çp(ë<í»¹ç[š'[ˆ@˜4Áký±´±¸]œ“WÍWr|’ÿÕWQÃѼ‹x õ䟽xxiu úÝFX|Î"]¡ü&¦¢Ir-zrkgÌj5H?ŸÄ5!‚Z¼ÿ|üûø<×m«£M;3åpóp–¾ž 󻳿KËË´ZÍX‡]žc¼X»[º…Ë]óCwÃC Û¸‚î ¼RZEåuÚÍϘXÌÍÊ?ìAJW×>²&.Ç’Ö&–Þ¡mb±»×dÌOšÏ€Fƒ÷­ÿ³¸œ· ¹ƒ¤æþ¦XSrgòÜû6ú­rò/æí¬…Á2Í»Ç÷Ói4KŸÄ‘Õ®lípò}½]<臤*^³jŸCê~𾉽½ûkÝCî¡"m—Œmß?xŽŸ6­#éë¿Aµa›ýÛ ôtôÏ€ÞÁï.g?Ó¾'ŒLž£4xú?ËåæÉ²Y(Ë÷Xk²<ª×‡™–,œsý~̶ڗûc5Ò´Ýä„‚A*~ŽÓù[m¿§)êØì>%:\´Ö~Yž;ìòW^¿M®€¸[.Áª9v²ÜQTÌé<Ç_:·ß©>Ý{9Fæ/,,ÃüæÓ±ý¶¼‚˜-F‘•Öš¿‰Ö{Z+ ~'WåÜ_1}¦åûÛ\íšßç}údαOîb:_ý´"?`Ïc.w6pÁv»u÷oËåf’ü±ÑXÄ6é»2‚ƒÄûå¸(^öåÄãu~oVPÚB‹}{+EÞwWØû ßxï'Æùòè5±¶÷d^@ûúf2T­ItÔ„ù48d®JƒŠsqg»Šû@c¥Óä S¾ÜÁ0®h®3—›s•ݲ£Öâ‘IÃØ®—­¬)µÞ&þõl¼l~yšòíNs~³jgMR9ìÁÍ*^˱°q³ù-lBjïžÖᥲEž®Í²ÝVGžÆf¸6æŒ&Ý]>Doï„v¼mýŸ†Ñ`÷åˆíMtÞ§ZdÍ”÷næÝØ«tèN¯IÃŽ‹ü~$Ï,|Òm/ר®àýörEÕijôän¾î­9(²s¿™YÂÜÄÔV¨sÊü+'Úž ä ê·³»ÿ5» ®›|£Ÿé£vq#]b(H”¡Ç\bg"èì¸õéÁÃhsu¤èÕ mÍ7¹sR®ÍÇ#ªþÛä^&è<§º€»Ù°ìi±¬ rqWιa~)檘»œ7+¹ùÿÚHýpµ:¡¥«OÖi >´5ïJ;ÛU»û®£Ó»š¶'sÇIN NàÓvÝã.¢y¶ßÍ’ûº°xb¢ƒ}þôQ0é’åôÑbM:æÝÃÆjaŸ7ùYhñaR¯¸ŽðpŽdmfØAÏ´ØùsFàô!¾V¯ åZ¾sߎ¹6ÿ½Iß÷õ•ôœc=z¯KÔ¦—²o07ˆØjB•ÜÅìă„Ý:‹°›Ù×ÃYÆâ+ÍñÌô6žä0_²xl»ÜNÍ;¹Üó9FííîÏõ1vþíݯyùÌàm¾ã ’æÀ'L¤·»wÐïä5·»Íêíee°puµ3£Ž@Ù¤öqVhïÉßì!çÓàZyRÖ»H_îûû™nòWÍ~]°èå¾]ð¹o°ÝÜíî[^?+~®Ý»}—¼ !7‹K¾kßÈ÷3Ôbï 1!ä{ÏøþHg€VñQÝQæxA„¶t;ÛÕö—¹Á#¿ý躑@-Lì¬û&ÑåàÂÎLÖò¬©C"ÜÜ󎻨Ñà½ëÿ,)çÁóíÄ‚üή.ùãrœóÿZ±<–'ŒîÆÑ15 ä:úšîÓ|ã>Ü&ºÙõ¼Ñm»ãw2PðÞ*¿<«C>÷´n»Ÿl»ÝÄäçÇW,Ÿ•Ãv&ì7ö ßùiœ&EŸžV yÆ6_ëµ§`YLºM4X÷Ág„37owé “ºÔówsžýÆÚˆÙ1iöØÀ{üö.ëö2’¶ Ðwíæœ–'¹ab ÆÒ± ÿ¯í1ÎPT,ùTf'ÙâCô9\öØÁ¼ ¡ƒAžïoy<:8*J1¨Ÿ¥ÏÜ||¦…×ãÈ,Lª,_µôr¾È5 ·Ï½Ð û\`û¢Agtè­™ñÃ$¶ë- iC ÃMÕÕÜܾçcAŽB:ºn[8Fñpó^:4¦—Ú}ˆV"L¼Ó˜š{-ÉÝW?‡üÆÜ£7÷ó ž\ÿg\#OV%”Ï­Ýú,0¼®ú±Z°Ùù[6÷äêíЀmþ!Z„‚†9œðZçûÂ9«M ,GTRÏ«b,¼<½”ºà}ʶ.-ペ[î¹Ý§eÏîè{i±¿V̈îìDë’~Í'tÌåt¹›Ö¼ýŸEçËÑ‘{vå{Ô9¶.¨=CpšìÀõso {Šéäl•é34œ› Ð˽ËâóÛ9ŒÎL?²z«†ˆ*²õwqŸü´£àÁ¼¥Ç\‹§©æêz'QX³[¦`;aja£ sNàeo•®ØÃ×Áò+Oƒ›äè°½7ªÅÌ5˜›eÔ3ãYn±…Õd ¿»pTž¯FýP˜ò6c±oýžž‰=½rÕ`'2[[¼®»»ÑÝþýÒgµYUç=‹YÍõ· Ìæ¯…»Å*·_Xå†ñ0éU»óïuý¶œCl;ÙÓ, ç25ó‘¯×öuè&—¦¢¯‰á¼þÈ=þ^ >ïoˆ!ï?ÐÄfR¯ßnáƒ+¢Údý!¢Z›ÐRƒw«ÿ³B¨ûƒó—I憎ÍU>æàØÜÖ~Õž›´Ú'Pºš ¨Ïêr¹]÷v>–sóð· ôþ^‡›ÃÅÆŠýÚï´´›ŒGß=2@ûƒ€Ûl{­Ù÷Æãæëñæ"8&Ø×ù‘]vú63`ã‘›ôÙ`ÜλÛÏr›SÝ*c ‰=J–Rõi¦¬þOo öôÆÈ—¿Î F»ÓÄN,‘ížf¨ùPìë´a'üŽÐƪ٘š¸›Ê#÷1­Z¸ÓøÉ¶ÝJÔ}Yãn2°óŒt]„ŽŠb.²ÜC¹Ï~†ú¼9ÏfØnû5䊋В†µ0,ö„¹6´Ãêb¹i1 OpÕ`õ‹þqˆÙÍnæ<ûXÚT†šbË7ÑÄÆìl£uvßë¨åÒ/OsþÒY«íB¦“‚êgõè¬`·s³±ìæÙI„ûoí0b´ùfÏÉß@uvJ´UG÷þß:…kˆ%’Í÷ïg'ëôû»Ž¯Ælm4ø³õÖ”}páË2Æò¯Ï_S"ÍÅØgšr¸ä±“:î€bó{W[€Ëµ,¶ ÕV›á {Š6~„èîm2tbYi7í4_©ô†9³3^6ë³Óà çÀž¹Òz<îÁÿ»¶óFíÄíw®Ú34Á^ƒ÷ªÿ³ÔØžwÒîã·¨ñ°òY ½Çd"c]Ð-•óŒÇ²Ë-ÿE1›k±ƒwËÿ@ é ¢ ¶gîÔo¥œŸeÔ#DµÚÝ›çæn{’º v•ƒX ž.›wéæ'KAµÝ7gNYmÒÕXvðf¼GýŸåDƒÓs„9„r·ðÙ‡yšÜŒÁ>;+»Œü~~½Ã`=ƒ»òù½¸ES—NqÎö§Ä‡––íçvƒa® ±‹J(ôaO|B±ªù·§» q Ã¦…Ô\érKòØz.Ø-iZDÛ ÖŸ­•ºþÏ„0û`w›S ·ÿúWzåðªvWóÖìvšå,h¸+ÉŽçqzè’tÂOŸ z‹:;jº©¡‡×@ û–ÎÍ„N<œC¤}ñJeP­w»}lºc±»}|¬`aoGî[IàƒzÏm@°{X;±E³ûîFý53÷ N(¦ÁïÜÙ0v ÜZ¬P‡zsMEž!«§ÑG½y¬Xìl­.Ï…óê»Ï)nÉ:lfs¶S»í<ŒLVwåÓØ®&º!urö÷ÐÊ 7ê.¤ÊÖÙÓçÓ°À‘NM4Ñ`õ‹¾ LŒŽÝ"ÞË#d Ú߉c‘j Çúj'p9»£f»îÒâ¸q0Pô@‡s8ïTܬxcºööðèlJ³F*xÓ‘ÝÁóP‹X޾ Y<Ó«çNÑÎÓ˜›K’‡ç£ÍY¸ç•DùßVÎ 8P•OûlÆò¯¹œ8åÎî­þšÑà=ëÿ,#û`§ÍbîáFûo¶ —AîDÒæ¯Yå†ÊÍ(Ñ7±¯>áõŽ2BÔ뇻ò=póÆÑÈûŽššõýaâc¿D§ÙØÏ1d°ZüžŒ¦_iuÔû|¥Ãarq{…ç¦ôÌŸM¿µÂjÔW{ÃøRçéªßàÈ•´œ©¤àÊ<ÏÎÝn*±º_Å]r=›þ£í¥úžå)Ò%zò#ryºí›Â<ø*¡¹ë>øgÅBgZN)Cݽù÷ å¬ù=û¿—¥²þþ½¹NDîKÛž×ÂÃ"DÒËæ>Ô_<Ë@1Íl 5sSóeÎ@ƒ€Çh¶óÉó2’ æÄúE-[,œìÖÎkÝ\#¥é£KFÖ,=ANËóüúóf‰°)J=5?×*vÎ˳ …3rˆ¦»Œ2˜îâ¼5šÿ<8\² ‹iÙcŒÕ÷ƒÅ/ùë¤ÝÃrwë¼AÈ;¶ ,ùƒÙæ(wcì»ËR~4ËÈÓ ŠÏÙ†÷…¹ÃÔu(¼£³±W–ÜÜòžç'¹Ë wC¡›ÁÚuªèLÚ»öæ™ yùÝnyííf¯6f·&ü•ú?Q øàÜå³çò6×Kjäýû›/oûkA–·ÎÝÜÿÚŽ èl$s›ì¬ûÕQ Ì3H„ò}ÍRüƒ;rúÙI!Î Bñ5µæ†!øž uz§ïÚå:’¾"ð"Ì+ÊÜâ»óØw&Í5ššÁÛÂfÒç.*ïhð;9:Š>´ø’‡seÆÒ^Øb€Yø|y­mûóíûl³­ì>®GüŽùÎ;|bë<];EÑçN®¾“·ÝÆ×Ýèá!>µîáü4vïßßÜVß7&ÿpŽÃlØ­´‡ÏJ˜|‹f;Ø|oT ’~%×tÙÖž*yÆ.;˜³QÁò!ÇÞ%Á~-ÓLƒ›Þ]è]0Ô]í¨Ï÷PwO^¼Ïy­Õ¾ÇÃ5Ì!Œ6/ÍCøóàbÆœbnu–{Fw7¯Ezµ§¸zØ_#×½Ëhdsãvµ?Lœ¸WxQþY”óy[ [^Ò‘åEµù²c0Ì%ÌÎ^nG$––™›»0½(×/ÝS1=,†Å=¶ò¤Ú.ŽkÕÚ„–üñú?=y²<Ï$ mÍò|7V£²Õû÷æ^MoùùG‡f˜S ·‡YÎæî‹F»åÿèTuÆÌ&)SgO|Ü[‹çðò™¨Ã\"dvl-Nœ^çE+‡5Óæ%‚ ¬}cºÁ÷»´–ïÛí1¸¬\ =D´p¢v¯5ÛKa5xÿú?‹Î·vžØ:öPÝà fw6èqPf% v¶Ö¾QÑÎf…Û!ÃWGÏŸ:ÞÔi/1w'&»­³²¶0/ÚôxœŸØùåvÖ é«4¶¸¶¶-.×ÑYÎ%Åû:Êé¯9Ûíƒ{ó5Ø}Åà’ŽØóú³ÞÁâ^õ§ØN{®ß ž_DE±ü€>»›ÄÝ>vüN8Iûž¬wÚQÉÓµ'lnáœß¦hân»Q:4*¬1Mõ±ñÛ- û›õöׂÚZÞ“ëÜKÓcŒµåÁñžcn> ÖøàËCœ§:¥ùÈõ 2³ù訆¹Ø2D¬¥ÁÓM,xDdà”nnC’æÚÿF/ˆ+—h5»;ÐÂKg0LÐF‡ƒp-¯Í 8—ñçï‹gmx”E©bOo÷àP¿×Ú¹þ%ÈW!×ñiïxÖæ¯¹Ù.ÛN7¹­`Ö`¿pÆjîJÝbUï ÃÍ|0ähÒy}f4øsõÖ˜/>ØfëèÕ=-¦yèÑw¨6º?LÄŠÅy©ßl{ÚÑv †IÛï÷ùmÍåGêm"$õ©o(È¢½½Q-"ó5ÉØÿdn‰ô;9QZœþÊØ×dq[ÿ{†ÂlýÜÀVÑÀJg£‰«ÉIŒ‰ ×[~ÙÙî![ô›Þ0Þ,~Dºë:Ô퇢µë>øWÅ\Àý~d?ëí`ñMg¡©4ÿLÖuè¼éì ð¸Ï’n'aî‡þžîž<¿e›LmV½CA<Å,|òäC4K$(Sõ@tɆÏÀ£8¬º¶÷ɶµiæïì FtxÞÞ›*Àʧ³ËŸÅpg’,5†kWE™3zÍVóiðáü’k=¹.®[D§(l?;ð`nêÊöG¨EÌÆ)«á¤¯:Éiòg,ßü“EFdÁLÂ9fëàk+“Óp3ïrù9ƒx—çÍæ?rÁnW¹ dÛè´M¯£¶ ¿_ª {’‡«K}Áœ{9¥ÏfÃÑ5èfÉ}æ7K£ÁÛÕÿ/¶è\Gr?£OÁË<Û»Y>p°Iâ5N=¶òpçDÊèË5ŒäÄä„–tç¿Ö) hgTË †oïH•¯cöÝ9ö´N>Ç-b”Î}¯Õ®AÕÇÄËÁþ¯%ÊIÂúÁ/–¹Å/e«Á»Öÿyïü~Ñ~r[ÐcÑãDÇ.WTCþ@hï•/ ;‹ÿ@³; Änä¾÷µ®× mg°è.#¿æ_pßp˜ i;³òé´ûÇûûÍà6L[ n’«ÒÝ£/P¬\sãjóóÞþ0®#î‡Oއ[…,ÈAÒfÙß8+m!/½aœ-¼’IZLhù>É fÀvàTÿéÓÙÁulWS.O<Ó×[ô´;EzÁ8wzsv$:[€lçl¯…p‹ï¿™ëy㓟¸7ËA/•µ¹-wÒíìÚïv²WÐÙà`î6ÁNñ{›{6ö †ÈÚàãSc7…XŒ·ðÛã}e¥øÃÍë‚­­<ã†ó'S‹P«À˚榇ޏÕ0óM‡Ï-}¬& ÿ!\ªs’„?†±óuj1B{ÂŒP“Rg¢<"Fƒ¿Xÿç}²ݵƒ|΋9'C¢ó1²Óm ±  Šý5ßeBh}~7»kx<|xmÖÛÕÎdnhV ?ÎNùqÞú-áòóvº¯º­ÞßãówÆ\ø×h®-ÍW½ýArn{ײž|+¯­Q‹µ¬…oUÿçÃóåªôpñqI†=æÁÈaãü²èYD²uöûqärsˆò;{ñvììè³uX–œ- nY];½1ì°#¤ÇšÕîv3Ëðëæ²Ô3âÆJwY2]¢à=v¦§V‡Yƒa³:í j·Âmvß…ý½ÝC£úR½¾þþ=an[n;¶ÿ­vm7ÐmÞ?ÐÍ©ÖOãàš»øjü€ƒµQ&Úz}µþ)rs«å‰ìH©5é€õúáá ©Çs%Ú>d5oX¾ÿPm”#ÆÈ(tƒP[·Óöý¦¸“k®ƒMû¯4ï¤+ga›;¦s3ÙøxtôÕÅ17"ª­ÄNÊ=Ê}çû7ü‰ú?ë7b“Fµèîë-¾ +Tß—Ã9­sïr {õØhô9Ýg?-~{Ë÷ªŽÚŒÌáaî„kÖá?!8w@·ào“b_wOqâ ]Â+…S_w_^b ]®˜´Ð9/ðÜ®9Ë¿–”`Ö&£¬wµIoãéöF±ÜÁUÌ(–ãaæ vÒàOÖÿYoþ{“òö³~ ©ëpðÍKžÍËSŠÚšû/«Ú[ / rθsUùQ+–Â=ÊOcd_2СÍW'§rçɵ3+»‘/x¨Ùî¶/”‰ÔnªJFk¶Øcã/—ÓÕ›»[Ó:¦¬k,?Ñ,‡igÉsŸ”ÌõcsC“ƒïXÿgÕB÷&åkdàLafÒhf3í—Ó ‡ yߟÛs‡2‘·º/;:(øjÙïÙBß/|š ÛüŽŸroâvÞ)¹9òŽÁN©ÀòÈÄ78êtudâ “½³– ¯¬ü}Ýý¹ÐÝ2âèZ×\š Ðú4Ô7!_ƒ¿\ÿgYÑàŽ¸÷éÂ…/<÷ˆ ïë®æ]uäåjhÕ;ý¤¥h‚@¿Cƒüû¸=·–¾²ÜiÛFЙ#?'ç(vl.V2ÓK ’›ßËãÐâðÏ+HÕf·Ï¶î¿ÈñIáWŸåòRyå¿[›tO˜ë¿Ä@kúB›>ËhZhpÓ‡úV‹óÎŽ Ë~åþù}â%mƵ³ŒÇA®û¼Q<ÿrN’Ó\u.z#±‡-Ýj]¼n“‡šõñ«ëÐ…³Ü‹¶÷ôÍÿ‚ yúúxÙuÖbÔ™%#¨ÍVUâ€ÆoÙõ+u¹ Àý\ÓÍPbåO14ØìŒE¾à Í4xÿú?‹†Ùç'ŽñÕ>Ï£ïÕoEfsß|Á‡§GÜŒÕ~ÀÓA¹ 6Ù…QÍ,e‚L6v¶Ä°Tr»ÖËhXͽЪ}xðÓ÷…ŸÝöÔS[ †hø9iøÆúöd¿…q®ã¤GôE!.ºÌŸOƒwªÿ³Šh°gÒe³hæß gA]œmÿÈx˜ù¸ Ye‘ð¹vÁmïxšðóã+v·œWŠ–ÞmQ;(H^7åY9à{ÄuÔ™ÂÖOpLÜ|rYquÖµÅÝPáv™B‡m²g ¶ò«Á»×ÿyÏü~Ñ~Öâg{—p_$¹ížíÜR[ì †I(ã%¸ƒywŽYg†æÅ©‚œTzÌ56YyòÝ™ÚFÔrý<¬tvaƒ[M«+”:Zç“QÅíÌ¿£fŒ1õ¿öˆžèDHcõjnîí ãàî½Ðl©ÁŸ¯ÿ³z€}°çÆÀçRh·GÓŸ±Ú"Xb Le“Oµ{(?úÓÎX1ú.ZszØ¡Û/oŠü¬Œùî±\²Yì ·p»Ò/í¶rn q9€ßÎÏ2«E¬k_˜ŒU^GW.†¬b²WÌäá`—\plÏÉ-³Ñ4ÛÉšÞ½ƒXipŸ¬G‚þ‰Õž‰=5ŸÛóV U9¯2l7ê‡zBx…æ¯|ˆÑÖzýP÷ÀЀW6™6_b®¹ÏýkÞ!×ükù¯~¿TΆ1øw×ö@¥ýàr;©löm ô¶\CA°=ÓÃäç˜sŸ¡Ήœ‡²íqfÞųƒ;W·ãÒ¶‹…À-ÇÚzðæÍàµËiÔ‹Ã`3 þTýŸuÅ&yüÖÎmBOý¹¶*O‘Î5üž£o™,=ga¡xöÅœƒ½ÐóýZ~¡™zÜ&ƒ¶Ö€Üg+š,ÁsàôŒöX¥XåôûîBµù®õ]—#2Àtt výk¶A^4CƒM—ŠÅ5¸Ù>8×f:¢=£¨Ì>5„V¾[Œ¯z¨C®¤¶);?C1zKš"Ï{Ø<CLp?G³óÒÑ)l>÷Ô%ø¦ª¹kÔ¥M#“C ìS?™[6·ÀÆ ?qÉßYçÙ!|…ßnÿÐÓÊ’ÄE×>U3èËuØpßзþ‘šið¶õ>0>Øf ÷ËZÛSõKb;‚¼QŠ|þÁüÛ\—ˆ° ðmuXœ­Q¸†ÞûÛÊ?Ûô]Þx%æêÍÍ[äe·Í¿_.×SËwiîiª£ž[Z»½U‹½UðaË5âÀè»Íõƒî6÷³`ÛÄ%Ðà&wÚ.ugÍöq,A¾ÂN¶-37ûµ]ÏîôšÓccc7ë7™µ §óÎ;_·f¯Ëz,ì~‹Àü¤d¶sø„·X¼¿ûhë¿dw&Þb›V› –ØŒrQ¼ ᜶®7î àÎZÚÝOAls¨¸Ú'ÛÙÀŠjpÓ$7í«¯aÍo¿–}˜Ûy¤Ñàëÿ¬$û`#€Žî¡¬Î¦[ùÔúƒœªød¶:³\&4ýA,n¹‡€¾60Ï5r±eˆå D@“oX/»5ø@os#ì`˜|k±¸q`ÁYéí –ËÀæ?ÙÐP˜Qì)à…l³ù®Ém²¾Ü[ìü9zZÄ…9аslµ‰ê˜£ÁûÔÿY"”-ÚoûÕ"O“G¹CÌ‘ÇfÏ뤳 DÏD¡ù.ÞÎ ¶@fÖB&0!מ&‚ah‡nîYü4Õú›LìéïîXÆ…Q?T}î®6q¦ªöt×r/Žðs!ªåY~=ý's5)Ç²Üæ×p»Ù=ÿiäו߬Q?Øæf÷¶3là˜ãšÿ[ ÞѼEýŸÌï“•ožh3š‡.[ÇÞ´ÿZ‹'†hjŸûµÍößv ße þµ¶•üg+¸·ôÚ¨ yžë¶ýîìÎ]˜í¹q¾EÊgÜØìsb“5ncN ’‹Ô/nߢä/ä½¾ÄöG–ùC¦Ð‘eŽM>ÿ¯…<ŽÍëà)ÎÝ'7«°f¿|&ýžg AÖEƒæÏUi7 F_îݨ¯†1ªxµc˜|¡¹$?I®Õ„ÜßÌ£³^_ß«qǶÛWŒ¾+~äCô9%µ”޳êP^r|Ï„fõNååÆ¿Ð¬Ä;å¥xÙPäiŠÆX,W‹EJˆ˜¤¿àÎÿ€§I×÷ïLР™¯šxuÈ|&w óƦðUJóßÌê²ßi¾žobå—oº7ÌÝ®k¢ X¹·˜c›«S@£¾Ö]Í7‚y¬ç § qøn9-ýzB¾£m¾Kƒ—YÉâè»ÍB«ZêÌp :g¸Û—œ¬°~Þ¹6ëœ#šà~?K wlU±g+t˜“3'Ààí„Ùg> V¿è{x Ì7 ´™4ú†lAåý‡›kpux¬Bèýl^^Ãx0?ý”Ý4Ø$ßLc!3–ûì¦_b¯EŠ…›N£§4ñÈ“žR “5Õ}˜Ï5é• §ÖÝ“wBï>E:з$ <ýÛ,ÚY\ò/&ó;-,úÝy=Í`ÞuS6ëöÆÚ­Å R â¾ãÿSÍ¿fÕÃ<ïvEJóüüñÁN‰o‹œG ô9£·´7uR;Û¼ÿp5oû9ÔæœO œ–¡íƒMóg¥‚s¬Ýºi¨…e(ŒÓŸ‡•¨Ö¸Í4§ƒxù%YtpÏŸ¬˜_J’áܤ¾ÎG|–N‘ÎÞ$¶™©Ý-×Vû8g#SNBËA‹5y~Ä®Çäf—ÚÝÕ#Å>„´‰O“Mðüw6xínúýoçö”¡ö!þãÒÒ{³Öü¼| Ö¡-h‘‡Ì î÷ºÛÑÆºmá¢Ø÷¦Ð."ïFjÏ¥ÆW«õF³à$1„±sø%D³p’ËeNC'ô:p—ðêÒù‡~Qºµ|û§§Þ÷ÆÉ°Ö\gÒwç³EÏ#ç_«4ÿ΃¥ƒ¬þžŽƒa®3ö2xvüqw•±ZO:oÝüR<æV«|«5WëâJ^ŸSk—”î.êÅ“E¹ÿ57Ç»Õ_+ÇŸá0î(vNµ&Z˜ÈW@›yÜrܹw°/Hz$¹¨Ó·¬Ì§Á÷Y&9ÏÝnLî¤.YžR·2Bç§¾õ³šuÎŽât¥¬ó!©ÛE¢~^‚¹Av-Ø‚ä–òè I~ãã­n{”ïlç°Üæ´ðîØÁX`K²Å­Y®ûFpEÕŸ‡9¾cI™œˆm]¯Ýo ìOç÷‹ ™ÇÓ6G''©mVU}ÊØQë™_@^~D«ßB8ßc¡ónþa\ÝÇ6*Ä!¥ ¥M+ËZîуóÀÍÝÈR@[¸r‡É,êŽ^ü´½œ#ôìÖTý=-Jƒ,#m˜Z­‚¬,ãÜ}Ù¼NåHc> Ö}ð_:Ùî4áø\A]ëÈó‹ò9rIe<––uÛÀ¯‡ù¾×›ȧîîî%tyO30±·§»§Ôµ^ß_ïSo¿÷Ï1ä}[}ªsf‡À¿MN$s›¿æÖ¯í~©Q^¼ú¾@÷¶]wå?²olûŸ³p.£œõ~àŸ3gR ý×reÙs˜··w·|£Á“ëÿŒkä‹vsUÍ]ÑŸ$ ýV÷íw¡¦½%+ô$–{ª9òK^£ŽLbAÖ¨ùF_3“Ž:kÔ÷÷…É¢à>·YåãÊ]Bö°c.÷ÏüH£v©#?Ro‡îz/ô#å;VæÙÊGrô:d8 ñq~H'¯åÜ­²g¿loUË=‹:Œé\ÝÞ oó×Zkð§ëÿL “ä’þ1ÿ0p¤ë µ ·ÑäûBç Ù‰òÏ2‚O¾Eæ½ü\wíí_üÚ?•3†ë”ãt–ó_³õƒ´öáûÌjáµ\½ÔšuðºúÊëÖá%Nûˆ†¼Qfq¬ÐÖiÇ{‰™opö]bºÎBn xí&öbOSLƒw¯ÿóžùsUºî=Ýîœô–¾îÁ\rËU½»Âs=VÌaÕyïìì’Þ|l¾Öès•Y{g¼Ð‚ö.éÁpy»¾3˜Ç o•8ÈîóJ›bd2ˆM87êØwGç·|Ë=ÐñØ1Ú½¿å‚ª…¹©&Ûpg×2>?’Ãy~n¬`îÓ´ÔàwîMò …¾ñ÷0º]÷Xìi f>²ûõZl¡û¥ÚËu•Ï1¨c<@GeÓÒl9Ê×ü€A®cÎ=ç÷ݹîž:y@×B$ª=AÌJí²BOâŽg†Ö«gí¯Kjn«îØÛÛâ¼"ŒqÒÃ@l§K±–ç!£5X}²±J älötÎãŒ×Ñ4ÓÉkçóq§8wYÊñ·i®n-l}AΨ;ˆÛÝ6~®/E=—Û†+ìû{xÚͲžvÏ‚yÐ=ÿÚíd\r¼»ïJ;se§YõX¹XØÖM°…w¸·Ïhðžõ–‘}p§1\® °õ:åh?ò÷tèRûŽîý]ÏJmï°\¸Žò—9ï #wΖŽ6¹ Á7‹…†~§úe؇´ôou(ä8ÂÇðfç’åiÈ,æÒâ,¾¶—þÙõKw³og½¹ò#ï̸j19õ†qóX³X®škðçëÿ¬^4W¥ï ·tŽmnœ “†ÏÇ4ünã¬øôµøJûÂuZ<¶¯{(ï’BŸeP'® lkŽí r…íä×éÉ{°Åéq˜kPŸ›“Ÿ…»X\Š{D‘­¿×˜(Æé³Vq ®ë¼Xið;w6wDvžë,w+-H{ƒlÉl½ûZ8U«a2~x®ÆÇÌŽm5^=ŽOг»ÛÉÝ.‡ôœ|Ï›¯R[œ¶ñ·ý Â{,x—С½í'öõOì;DäÛb«õO]m+R­ÿ6=A\'Û»Û<¤“±ÛB5ªØÚ¦Ü3änáö½¨ý#ô¢&ü¹ú?kMj¿‡Ìý$EoËÉ_KË—ßìø¿:ÐÝ›ÿj[›sÛñDÃ>óoh_džîϽeÊ"r 1×4Ù,Ö^o5Œn¾_>Ä@«Ñ?¾âù£?d*ÒÀÍÆÀá3&lœý½ÆœóÌl{Žd÷ý4‹Î”QfIfaåòú‘Ú¯ró'®IƒL“Ö¼WýŸ¥æÛ·ßµïRV^Lv«ó¯Ù~‡”;Ü!µ÷) =]µ?´È_þ„ÌYâÝ|ÆU@²ß©JoÞ…²6çp~ËC»íNÀ[¡%T^ŽôïNoqʱûzo7s,G¡G›Å]c­×|ÁÙ306Ws_¯4ßÎ6érm˜Kçì6BÍŽîdQã‘+µ™ïZÿç½óÛ¢íf–Á&Qbèè ²ë÷‹Ç ²Ãòzÿö‡Pù ­‚Ì~û–¢—ýml×ýîÒÖY[„ÏÒÅŲb7u\„Ýåµ? ÝwýÒZ,;\70rôQ´Þ )Íl­Äc¿Cvr^·p^-þ4m5Xc“·öIÌÝýäû$Z.…š8j4V!anqÝPº$cÍqÃÌŸ• ïÙ™Z-óÙ®Ü÷(™ë?çOO€bj ÉlÓá{ÃÜÒà“(Þf]ã<#ºø"¬‰È²ö7ë€õ9ª?Lþ¨¶—èöØ:AzŸ]6%ìd«ÕÉ`_˜ØÙΞw¸;Õº€¡ùÁ{ SI¯{Ä\S þlýŸ5ÿßó`Ï”¶ùÇ}–nŠý-|‚¤œõ±†XM4΋-·+QÆòÒ3lîÂÎvÄ·CsXá3-8ú£6ЏîP;p]ŒÒµÜ‡‰Lñ3‘»«Æ6d°Iâ ™&;ÂÖ¨n%ßNƒÄ¸{$´[YÛkð¶õ>8ŽŽÎî}‚¬¼ÍÿnçO³ÕÖ¨ÓKбµdø{ kë°Ž…_qc5Ù|é©Inéi°ƒ4}ÿ¾æZÐ[ƒ¼YÁO°ýuB„÷zõKK‡aϳ[Ÿz¿˪◬޳€ïXÿgÕùc“ìu³/˜Ç3ñåP¾-×uÒ™KÄ´Ø‚Iò‘k‰ôírÞ[ØÎ$ù˜ÿΆf2ß\žk2B2ʃÕ,_Ãøîymdû†:6*:é8ï¾£Ì{ÏY{âÐßVîé–çÓ8›p]rôØîÜ L®ÆvËcŸŒIn&¥NN¦¹ÖSß‘bi‚n±íî ’›Ò/ݪŒ¢Ã `ç.hÉRl:¨Mh¢ÁºþÇØeÇÌÛ¸¸ŸýXÊPîT1––œÌNÁ•Ýn±ä>ü7 ÞØæl£p‹»éìÑKލÍÀÚ:½Æó|ÿöQPÁ']»©Â]‡;üþnÁó¯—vsZË5L˜»TÜ7hU£Á[ÖÿYGöÁA\êƒOä;d§…ÈnSÖªS „éc»õ²üåï|r}ôÛž‡¹8©}0]è¯Ôcqfºg7ŠûG¥Š1?ö‡ðÊ_p;~—®ß ´X^V»œc¢{%-|'¡'Cñ¼Eîfiû˜Â¬ûàB¬Ëmc×)Í%ÔÝÇ~ecŽw_ ¢j:w ¶Ð¤Á0qcRVGåžËébÑH–rÐ×Lê_i_“ ÇWÜy>pŸõ9èø=lVѪ'ØþѰ¡-ìíC»|ÛÚó[¸¹…9¬ôL‡3”ÿ£ ÍŸ'Ëzí±Hr¸<Ï}iÕýÛ³u¾CvÚÆÞA£¦»³¾[OÈ;·2JÙÂ…-Œóˆå!F@ï®…R‰Ý‚£¼”²ÚëíãØÞ+$w­íèpvUÕÞÙ7°Vøè¸‹º+“]H9ÏjÕ௒›ßÞ&ÈÈo–óZõÌ»h9ÈÓ Þ®þÏø&9:,ŸÎyM讃b#'dÎØj·ÈàëÓó:«¯.! îOã6Gwvícã Ðí&é‰dMD¶m¼tkæ&‡é ‹IL{Û|è5m{—ûIû ŒÜ^Z,•_Î Qþ>°`ü€ë|å’®&ç,ÒßÕ>x-È|b«ÁMîMò Óî-žwªù¦¾ÅØï ²_¶ˆåm|ˆ&Nó²ƒ r5¶û ²õ%ô:±¸ ÉëZ¼;ç·6ÜÝÓS< uà¾ë¢qä“°øµ=†»«ÕÜ­v‡0|¼,ë½iÀûJ,ßíÜXšà|6︒<øV¸étß×ÄÇ©1Ý»{ækð;÷&…9lsq‰q7¤8DHø\ ðÿ±÷'pw]W}0,IìÄIâ@ R‚‰’<¹ó=W‚ã(Äà YN“¢ØJ,b[ŽìB ¢†¼eþJM¡oE€ÊPÀzµM´ m C' 5ýR†Nö£èÛ×ß^ûìûøÑ½çìµöZk_)ðÿÿ¼ï#ßGº÷œ}ö^ã­½Ø-XÇ–~)LQ‚D™(èܥ݂py{ŠÎ¢SéIŒ&J¯–ó´ïÈ‘ÁïSpé q*QLÔ`²jÚ9ðn›Z0FL&QþÒ°/7kè³f.&C ´O/óÁì‘X×Yï•—Š­9ÖTÑ›ïÛ‹&aëXÌ u:½B ³{ò¾²1b+xaW²¤µ¯íúEŸüåî¼¹ËÙHgØù&]c¨’¼¿É2P‡nT-¿ ™¯z›bÐÕ³­¤ƒß^žN~° 3oRf|ˆZêŠüšîèðtV©‹³ïtyÈÄágNÇxó¾^œéŽn…ñ"|fºrôÉ L”Dç8¼(2Û·æÃJé9'Kl Jþóe¢Awrîr.˜¤Z*s³]„ɸ–%&Ãnvä|è’«»\M% mµKß^’6wB¶ÅeLOp6wii& Þô‘lçƒÍÄ[ß1%Ådl#O&÷Æ“†#iêÒ@¯ú4µr &$´B{èhC—ù¦Nì-—–,›Îä7Œl©²C?Ôs§ê$+óÌ ¦º­2¤§5G‘>^®\ádɨ⓮c‹I Í=,p}bU“”¶|›ìÐ5Ûº-<úXT5“EÇúëæ|Î 6.kšµ;ßã£N\}22€ÁZÛle©)Þ¨n\ղ؃Aào2˧/÷’ 5›˜)ÌYB2.y%wº«ƒï /׬è`[W\ -¥¶iãì*xã‹­IS-Ã\Køå²ŒÆÂ58dÛ“I·7›øxq¦ÄpEÞ˜®5ï&kuM6WfR~´bQ0mèažéiŒºfl¦†6ü!õ[ÂåÔhwy²¢ƒwûE‹ÂÅ¡ÁŒæn¼‘c2ߦ+úÍ®öÞJõ[V?ð÷VHääâóC£ÁÖt²f{„÷‡Áî-φˆïFY²×u£Ñšwߟ4>Þ%g€¶“5k:.bdkyëù$êL’Îëoº®?L¢ñI“îùo+$΋V©~ä-pg Åó_Ú=·Î:ˆïg.¬6ÿf›R>?–nrÍ.j÷ÄL/XD:øÁðrõŠ6ì Ñ $]Ab¢[WŒŸnÿ†`g«¬”‰é%a¼íWòd=Qß_t«nV§úà+÷ÚÌ´±»öT°›|'–"¡áÖŒk¦ÁÐ#¼}!Öëv¶”èa¾ïež=è.˜L^½/ýâ2 œz•OdöÍ€ b©ýiTAÔÀ Ðú€»Ã¾Éˆ Ÿ‹¶Î->Ň D¡ÑÖ”«~5õ¥–ÄïJ½­U?xïÓWtó5oXÉ4dS…c§T¡`‰EºÞ_·µM›aÃZ½v½¶õ:>Öfø‹dx_Е·v [u=ßÑ ëú)pèRLʲ&­9O›÷TZ{eÊ­9´ÍhÙ°Ë{¢dƒGnM*šµ"äq¹!Aíj°ŒÈÚŸŸOPv×ü³:x·G‡¸(¡FpÓ9”-Ë7;>©ŠÁM½á.í5!0o[“Ë‚grå+Ü]Q¦p4êæLF>‡©x…‡UÚ|­ÂêPuŒB×kóG«\߀ ñ=¤Ã3À"O4‘ý͆´LÁSAH(]º ñ|x°KïÖ&m¢º.JóÙz/:©‹&Ó“nʧ@[\®ŒGiO+°\zâ…}ÙÁ)22cßÓpGìššþ‘@4w~LŽIÀ]ø\Ù$vF†tÈô¥n;ã$ë^)ã­†k;c¡¿(šn˾ë q„Da Eï¡) —¢°@“:脾 q¯V_ÑÁï /W­öè°Q®$¢nÁÐM$/<ˆ$Ò m_ìÆçô8Y̯7ŠåÒàM6 z\Çϱϕcj‡Ju¸¸¥sôHPÅ%aºöU¡¹X»ÙëØû|n"µdÇÕhôÎÈ›JÖPʼn>žª bÃA…ºÙÌÜ{B/9YO²ÑÓœ [=·T ¯LF´ô…m9‡]XÔg`xÕþ|ƒc4ÚÍYS¿®ŠÒ—–¨ èÙ&*î…8ÎÉ ì†Ë«¯[¥¡w¼tCm‰@Ö|“pÞú •h5ytµ«ô·D]˜ôÝæÕš'•tð‘ðòòƒ“…éÒŽßÕt¾1ÔþˆÎÞ0VJY3`E!Xžn¼kz¶L~•µO0TM›­á ðI™Xœ‚–%BÏ©§Û†Ky‚éL\Am‹…ЯYŽ›ôÌôxMêÜÔ}PDZ6š"…>´!¯&2EŒåÊ‚• ÖÁw„—V9Y¢0íhÖœœ]gùáË£CƒùÖÄ+Á‡Ç§ã­ÅÚ·Ñû³­f¼ðiKÒ£Yùcçl©ìñØÔp2÷4T=îm)+Áq“ül™žv6˜Ÿ-Yâ?gk0{£¸æAbæ;ï¢ÙâS_6².K”rN‰ Ð5?ÊñÔé—îû²ö/JË޵ߦþ¹°+³{¸ÜÙ«2g›ò3PȽQÏ€Swä¢ùæÅ»©ªƒW‚Ë^“Ø_92uágÉ”)ç»/¹ltQ ¾¬gÊ6¯s‹‰v9Î:z€ÑrUjŸ]6ÁÖ†Ó'Xª[gÜš`ŸQôHºˆ¦[( Ç>Œí¦MVQæ%‰^惯-Ó#Qwo¬±Ïñ²Ù`Ô ¥‡£/ä«,%Ö·É;Ƀ¼AÎ9 —޼íkŸ¢J[ï_ÅÄ'âNêàE 겓²ùR»W®XIsA‚I +eÞA\$q4ªt®xE=/]ë2š6•º$•ÖÖúÎò¸nì+O ë™­¤ƒß^žK~°Þ0´pF¦[CžÒªw]5dôÍ~Û&·¶$bíÙ£Ú… .©`‹ò¥£™.bîÉ­_5è=Eÿnƒ‚ÜoT©Ãžº4ª‰ñ6gc3Ì"û³l»$É E3²{»Ž çøÜ¤¢¿¶íØ–Ë<£ƒ†—«µI¦–g wœ‘Ö+H÷ÕuA%Ô¸,»UÙØ¥˜Ç¯Ý¤\”f8M-‡›î5ÙmåÈÒÊê-F[ªÛM.|̘Ê$aÖ¥«w ÝäÔ‡’V;še˺(b%ë²´$OÄ”uÌÕÏ–. `û¶ùü Pß^–žÙ ô"µn´=&>ÁjÙö˜w0Çb¼Æ¥ÊXèÔ] vËá‡òo+ç ³L5SÛ¦øŒ€ëã,ŸçÇÐÕsº5fO§R{Ϻþ]¶ßT½7ݱ d$k‹ÌüîLÍhP)båLËFÜ{È3—Gí5Ëw™R—k7ýæ|~xc4ØTgŠxÝ*|<ðQá‚k¹agëc6bëLlrFÜKz£ÞÿdrþÛ ;’Jb+¦à¸–^¡9:NŸÐÑ©_ÁV&— '{êâ1ë)=NT-ßt¶ˆË{«Y¢TÞcù<©2¼äd=#[¨SSúH—Ä•TÉCçíê"'a!Jf8wé°hà㕈¦g{4 °/ 'J7ÝË/@CîYé|m¶æÑ7Ö,ñ$L¦›!º¬ëßâü´ë2%ô’*®¦üÌâ"Ÿº\c;>>i,áÉ‘Üc<;¬nS6ž 3ÜÍM!ìR·5Ƴ±À¨pîjHÛ6ovÔõê-f“&EcTÛÖ£4»½úž0ôÄ¥ÜXŠºqÄ~`)É-?e[(Ñg”³µ¢ƒŸ¥ì‚jM…•–FβõÈ3gÓì,÷ØXUMÀ£èè‰íO¿94šu]x±5åÈ׬-?›JOíb¬Ìçk,PFA[ï†Ës3ŸoëÁµ÷ó“>^žDù`-!”Q‹½÷£Ëüh×Z•Œ óme1yý:¯sÜw+ÖOõ ïÏ· Z‰ÊoíBÖ&Ï&î„ãÁÖ·©8ÝØ~þ´KÐΈz²’`VÚÏgl.D+¿t>¡!O5çÏ’ÎÖúç·»Àå°f‹‚o¬ÞeNG'” –Jç3–¬›1¿ ô6U%{uðnŸ,[WCˆ¤ç¸Ži¥ÙØ#£×÷š¤¤)+>ßš*Î r^•Ú¢£¤ÈÔÝzqN»Ñ'›90 ðµê€$À]̵Ϡ²ÜØQ{Ë‹5< X6¥±w&O¹°™Þ¥M¥µNŒ®ùGm;t£Ž„¥uΟÄ{ø»éàe,úãl÷^²nìŠÔÇí˜e°Cu¡jr ß&c9è­1KC/“”×ï+…,=«Õ2?zÈ0O›ïþ!³¶;í˜`mO]¬m6{.b-ÃŒ©JŒ²çZØÑÔðšwSq¢$ÊÁÖiض:K†d·bÒÁÇÂËóÈö;ªÈßë±ÅïY ×fÈ(hزÛÜl)Zì7³µ«žóÜÉ¢>‹5žvòx7)¤õzÛÚª´Àët4Š>–§aü|µ•£ë¸- -¬5üI¡€-GÎç«ù|»£G¬‚òÃnÝ{Ù’ë ̆‡ÔLætðÒ~^•®Ú¾.GÉâ´Ë†²Á:ޭѦ*cz cŠz ±öçÛx¥Õsuí ½=]…œW;z]‘/¢w&Îqw–ü&?qÄ6³´´—ÈkKeD#Ht ™në ÉV3Ž}²ùzNËê™ Ï×7ãìÈ>ýŠ4©§°XXyå´yiPH?¥u?_ïu;>jÊXƒ_nY¬0ÔÒÖkΟ/+uèôv—)É—ËÝëI7Ç`8ð9_C(ûî.“ꕨ‹rÊØ¡Žë¦¶„¯ËÕ~ÑO±¶Ô0 $à‹Š·<Ù5Í7PDâìÎË\ƒõSÎÚ-âŒññ‚ C:Kt ›‘‹Tè€ËÌiOüÚ)m Ù;bmõ%lô®î棥:¸„(ÁÆ–Ç”]³úlµ—G‘dX!¶ñ9vÏ&UØ©åPp¢‘š¿“>‹ÿ’·ûn¶üJ—QŽôÑJw¿,Ñ«ç5hÚ‰³íÅœK16úmÂC7ô4SS•…àó VDc•iˆyph|º a›ƒB|$¼¼¤ðÌóYlѱIþóKÈP ³Š 9ÉçHd5^åݦ±‹¶4ø¶)Ë4M{8.Í-|ý:=(mdƒW?§U©š¤µ”úpªŒr4í®¹Î<4š ·ÆNAµiÀUyJø4†×V?AÛu<›Ö?[ÎÝûŒw“·•ø»)³•„cÛ³©ûìUÉÍÖŠ~¡½†øÛeŠ:›lÎ|¾®B k™ïÆH<˜±„cºÄƒpCkyÞ‘ç ïÏç>õ„†%':\Av“]ùûð~3u¡ÚÂ׬F’h<›Tâ“Íôù“Ž\%MâÄ#WÉ„YERZŒ¥5Yê4¦0.|ÏùnN'¯ò­Þ¯³SöùI?^žL±èòï“mæYÇZ ±ìÓ¶ÖÖq·¡ódLç%"©¬4ÉÇ| Û}Áóݸ@:Xî$g¥Ƙˆ`ØLÅRù«ý6u„ÊÙ%ÕJÜ -¨x3¾¬.*_Vìí4ž”³¨èüüqG6†¤»Kݤ„2£bša%{Îa¦/ÒÁ_^®_ád ê®LÏW¸~¦ Fz¾.G÷J´÷&±>Ê"ûüÏ´™»äˆ þ†ì¤F}PTV™kó`Jƒ-u÷•¤ÎͶ¤+uó¥–lœƒ¯'•]ÿ¤#­JrÇåœa©«Ô•Û–©Oý寅SѾ*={[v ßðRb~0¯ƒO†—§•r²ÔAÁšÍõÁ>úg39lqnE®î]ÝQªE‹Ë‰EùšBç¢4)ÖUËé&MZ€Ãs•™ªã+˧­›D‘ÆÿõzݘÚvæ;[£.›¼}ŒÇé´¤n§·g¹ûÏ &é-¯ëàe>x¿=ª8Î&6‚{þAAŸöH>EðÂK· !ÖæfxŽQÄ÷$î>ª»vvÒæH{ÅåÔ3‰>ê<£ô²‰ðJÁÔ'L$½8,KÂå$h_ƒ­ãï èy²HT¶#’÷ÚÊ<½¸)sNWtð‹î®¸K±”àÖUÍ…‘ iG˜O­ë9äI«Ü®ãI«BÒËæ#‚:›¨ò®PGËw‹üüz%:DSµKÕÃÍÖqt—ðÙ2ŠsU»Ó5Ý»ÅÈå`+[wëÑÄ©¡º#EË™"` 8ˆŽ>Ö‹ãꡃÕXt¹ &$™u—Õ®OZWÔÙQu™ú© òº¸‚ŒâÎK4öÉ䷯Р½Ii aYJÁ6[Æ»),Ð5r…«ÔsëcÀ6K®´?¹! j¿cò-7Ê7XrågÌZ²I©®ëà×…—{~°ñ¨†k®wUÏ„¤³q9cá“N”9í}¤¿¹ËEØšü *aL¿ÒS.mç |Ùºé#N7ÐäÑ™ík_©ƒß^^¸Ú«r£9†øšO×iw¶hM™¨.dÖ‘pùKù4ÜLÜ­žìÿħÿF+ jF–®•-‰^ï|æB.¶t"ˆ’U”wåßo¾‘„¦i˜¥âW~5´rP´Û1/Óæ-ðƒ+$ñæ=LܹOÞÕ¦ ã¶y+pth2=4 »[›åeìžìçk ‘¹¸84¦ÝݱëÃû³Æ‡™fšR>$–Rå?ßN⮟'å]ÄÌçëÊ[³¦¤÷d5y»k$¼? Û©üX»¢µ¥8¶Ãùó³Ž ÷4*$þ{€ñÖÌØöÃymñÆvm ׬ĸþt¤§&)uð;Â˵ûÁ<¿*»AflàÙÆÃ™tŸQ‘ÕÎ×?êºþðùó tû5kÆä×uá1°}#ªÃ1-‰†7q¸@]2:Ör¶ûÒåL8>”ÝW‹yùy³¾F‡¥…̘?ÇŒ{·ó3Y£T¦‡äÓÔe£¶Hac¥•)ÌË™¯­ÀR2²ûjæ“ÕËûùîþ‹!æ?W1;¸ªƒwûdYZ$³6yiùƒLX {ž¬OgDõòu:«ÖÕîáë0óSêÃU[÷º@úFC yFd Ϩ[Ú­‘|[ÃÒçÀACèBV×;³ ¶‘á7µ5ùæåI!åe³a íVÓpÖŒ™ïyÃrIµëN.¦.ÒÁËÚ¤ƒb.Ýé1|~9ß”©µqyfsGù'V^&ôÔºZè«Ö ^“¨¯ÔýÂ7cüû4dÊ^ÿ„RoÒº`Ê»x‘d%<8£¦fe|ÄÅ nÊ›+ ¼:²µÄdgË_Ó¸°¦}É¢Š}Œü²ÒÔxžñºèdR=Ž[ãB§ak‘ÒE¬q?Ú蘋F’Æ{ú¤Ò°¼Æ¬LÆJt“ËË@µ ëáf¢ÓÌøêO“s&ÐFçÌy­úldù‡ž}3äõ“IÿñúÉvÀ€àó+‡Þ„ÖÔzã«V‚/yWÎñÖpÆz`²Iœvª©¤ƒ¿ ¼|<Å¢·¦NM{LI/S2©S¶¨ÕÖçY™´…mJiÔ2KFŸ‚”)kw“ö™Sk“ëÄߤÐÓ»fú|–²+v·%ÙMR™}„¡,–®¶$Õá()ÿBf ¯ª•Vz%Š}‰pZ°1=µ[ºÛÙµ?AÊÿwóv¦N}…ÔçtðÒ~…‡¹ª&¬:»B†ÏpGô©¿:Ý5“]+JuôÅy›øÕdÚ4Ãn™0ò9ÞV-uÎl¶²‹Û<¥½ád6ÆpÍfkߟŒ| G½¼p99.Ýä´{OM¦>çë rÞ C²O)Gf\®›U‡#M騅ÊÃEÉ–Òa•¢‘d¥S´º²™¥9K6Êd¾­²Ö!S¦=&åÈ¥®ÅÚRdÐóµÞoK:ø4)`òƒ•bG&i ËGä#è]bcÙ6WêĿ׬ó£|úrËÞöù M]fk–j‰„Ýpâ·.œƒ“-k£È™Ðmïö˜},&Ëo÷¹¢»¥WÖ­H&33Y­;ÕÍ’.ݯ:ü3Oîâ¾­-ÌuYÁvÁ®Þ­ök„Ænž{mô^ »FÈóZÇ¥WêÂLâÊ’—Ñ#Ø\ªØBÒt“Õ’‰¤¢|öræ›IDè?_RÑ•E˜¢Ÿ…m™„Ïf8èvÄÇ3ŸØFe¯1OW±'‰ò}QÌæƒ^;H.ª¯õˆ-iÚZâ¡ýsÇéàw†—«VÏl0ÕD4¶:ΘÛólµÇ/™Âãg Y^‘yR[ƒè7°â”Ð|Ñ ÷J0„Š55¦µ#8¢Œ‘Á‰ÑÚ:Qkk&ÃÇ£´+M×̘I-“9\=Ÿ;œÌ]NASûeªöúj¡®ápZ›u/Î'tp'/ºöîÞ¬®ÌÃ0,Y—£ÚZ×Døcà³°©D§>Ê¢ý}IÒ±Ï9Œ2ÜpÒä^Ì7ç ×ÎJ31q9RÔ°œÜ D=Ïø3‡h*dž ‰%÷SL¼8Y4Ó“‚Zúù½:x÷ü`E0öl|ýùó,܉uuŸˆ1œô莩Ïqz•¥ŠÌYÎzÈŽC—8¿Zô¨()\­šY\âÏÏýóaùùAòëŸo5¬ÍÇõóg¨Ôô¨ i¿Íq‹´þ»1]\–ÀUG_åÏM©ƒß^žÎž\µ2Ãø$ £Dêªè@MCØb¼2Û¹ZɪMF ÙÊBÇ¢f€ÌP­%{6Fzkan³N“¡¬yŒ|¶³¦H3Ç«ç%'§фÌ<®¶›.Â¥V—R¡h·=k¨õ¥ÚÍ™Æ.|$¼¼DP¼Qª·Œ”èɺp6•…áÑžò‘OPPF_ôløOèB£í‰ŸL|ÊÜjsÇ7©-”!M_.[‰`YßB‹=.£ÙÒˆe'3ä¥ }êp¸5ÜÛ!ÿm ¶ƒ©f2)ç¥ ï¦JŸ¥üÝôônëà7„—W^ìÙüé‚Ðýxæ±#o5nMfÐðù‹.›wV£OŠ­‚‘]¤ÊÝä°cÃMŽ\¢¼’4hï“lÍ8öž1É#ØéYíì¼ÓŒïé‚+dOèi,º—ÄÀ§_‘4ȯû|˜}dæ pE5*¡ìÓlº×ÀEp1Ô`÷=ašàÒãòY)—Ç©ÐÁËM6ãÑ-Þ;œÒa>â]@µ‰¥£]ÄÂ']j’š’µË½Š€³CÏuÐáÇçêãó1Ž(g:HœtNÄ!ý^”±×ùu­Àžä$LMÌ|Ì¥]á]ºÉµÃÒMútš°*,ö‡ŽWŽlæ¹F¼î);¦OZ ºãéÏyÍÙÔ¥]Ë,0uÌȲÔ@ÃüHÍ©>^žÄÆ¢]…Œ^¯©Xêà•°öFè°÷,Ɔëqk¬Mr§!3•büÓ(äBj³V*–”Û.ˆeÔ±…åí·ØÚ& f˰S$9¨¶¬‡–~¯Ôj#bälRSŸÿu󦾅‹­á¬Üu«fJO½ƒêo“¦)´ÔV]‰£1œã[JuÉ“_㟘ÔéƒÆE82ÎÞj‰ðá¹Åùs! <éàãáåã(íX]í-,̨èZôÐxf>)«MºúlŘ볰Åà6 ¶ö„v}€XÀ ’Å c£Óât:rÍÆ1«—=Ru0µuôž² DVŬ ôjµïJyE‹®Žá¤¼g¼N!‡Áx©áŠ÷ëà¥üÊj¿œC;Α!œY~¡¤£v, ¼v̓U¥’¯êLŠS>Ø×0ä…d_ sQ`Þ–äϰ6®­Â †|¶$("h+`“‹u³˜Ò||Ó]1ðéSb 8Ȩß2›¸ëh͘ òñðeaŒ¤ƒï/Ï&?Ø©½µ ÚùÒ¤Š¥éH[n¥ô¬xaEg7Ïw°ðiåh IŸLb<ŒR`€k+ÁÚÙýª‰])‹¯fÖ¿ê…ƒPÉô8i.ç~ %àbØM4\*…¬¦[ Ç.-ÔpÚŠÖÜ›³½mê¸B'”õéà¥ü™¶>Vη»Ñ‚ayPOe¼Óz5$ç\Žkt}€BgÝ sKö@¶J7ÙàƒÙÓCãðÁc¿F•Úo“:àœŽ!e¹ß_L}ê³Æ';¥…曂‰·6[7@âûÍ܇ŸÁíE,Ö\ávÞ}úÖåþìâªÑÑ«½åéZ&3¼?Ž B$ÌþÏO:ø ÃË È–|ˆ·Ý“Í?ï܉ó·1ñ ^ˆZù_Î+¯òÊÎó×ÜUŽvÝé¤1g¡XŸ jpÿüš³•l¸ïRƒñ!:eM$±çk}±éýáÖ`àÁma‚}–)ho=Ùy ·Ó|°¬LltÑÁ÷„—g‘®7¯_>Ê“&c-ÔÔqCŸ¾+z3AVûn‹×ÍÙ`»Þð”Kóû/YÚÝæþbâÒ €Í mE¶Ú¤]‰ë,Áv[]2kRF¿ÉÒ*VºM•›+»ÝÐå7ò8]’É¿³Rq⊯õ•÷òʯÐȆ[Ý=2ý³‘rztði D¯ôèP‹iME†Æêþ\õqYA†Þqé ß­ðºL4êr©­’ò&D|9\2ÄÖ2])й™fàV/‹2¬7jm†!£ÕJu°-ÔQ*fêúfL~ÎÛ]7(s ‘òc%¡i*ór×Óf­<™éåÉ®^æƒ_Wìo$ŒMÇj]¶NÖjn2¢ÍT‰ðsWH ’9›Ÿ» ¬ {œÑÆkð+¯daCî,W„ó³X£”µ©Ï‰Ü¶Ö#¿n"ÃÄ2m lÃ,Sœ¯a{\²EÁíü¬³ÑZóuìRÈ*ôàWÏö)þ´È9…SÄ·ÖÉõ„±J%­ñ¬Ëzjõ·ºŠÔy¶l»´7ueûÃÖ}Pà˜ž¶(î)S³.zw}U©l–°üb£€É“mÝ—´ÉHœÍØ'$–£¨+´™'*S7„Íu]³=S“YÁül6dÛ®¥<\“õ+ðöl)Ecgão£Å3²pØ|ý\±Ä?Yduð„—wâEËè2†èQ—l¨–HÓÛDT›wå²(áSÙ¿QÉf(D“uÌ1r;œKe¨Ò›ÌC9òn}ƒ/jÓIçÖš …ÙU&¯K`èˆñ~ƒé"«Ó宺é•TôS©iµ»”àtðÂË+WyÑ6íSØ+JV®_=§Ûé—Ç´ÒÙ›ÕrQ¦è|U—ê$eYÔ+²7úl²ÔË|ý(ó¤+‘œ/fƒôì;£¨ÜsBC–5,ö4Îp釯¼èJä…ê·Çbõé².ؘ“°ö'Á~²(§éq…^æƒ?[)i¥ÍœÖ{þág¾MWñŸµ/¼ïÍR¯7Ùq$@Ë“R}Ó¹J×;ÌÆ÷ç ”C£çHa‹}þ‘Ñ-O;bOáýfèJ·í‰ÂˆOžGÉ®"¾®?›FåïÆêÝVý6Cìo"hï)àzÇ娑u§8ð)k6Ú'[C.Êdò PrÄ»€ŸQ®lÅá›þëO:øæðò©äë≅÷YÒ>Ge­V»&²ô„šGÆÜf<ï£LXhr$Ç ‡Î–%.©•‹–@GÌ*¼?oæ 5ãMÚ¼ýwàFöVMyG’“ª‚ µLªöɉ먵-„I{ë-®ûŽJ\÷ú×X"X—F‡Ï3B˜ÿ6a›Ìr|?½\‹6UütŽ„Wœ‘©‹LexÎî·ô–Õ”¯8¤úã#ëè~ÅKw#UùV*–nç“­oà]†7ìÑsSŸ¸¡2.& YZÞHâ’¶ÄuÃöjUÛÞ.´2ïxC>M΋jë™gU…,ò4í òÐvú ›% ¯ƒï /×Þˆ‹ MCšö9¯(Õé6))sAzBJó¦RCIçPª-¬PXÿ¤6U5Ag,°á8S7²ªv¡¦™±úÊeH% &ÛJÞ¤ÕçâU=ÇÄhHOtÛ…ý%Yî¶ì¬>Þ,«½ÒçY¥hE/9Y·Û\WçYܤ@—F°ôwc ~‹ÒÉJ¨w¿íóù´—m~Øî=yÞ¶CpÐc*îÿÁ4Mà¿­¬O„Í2ëöEšÑ¦Nàp¦Ô8'Šê&¢,XÝðù¢bõÊó£Vʪ2my7+:ø§»ëú¾i×1 1[àÓÊE˜«ìÉù4…PÇié='!ÄÑÈò5K RÃý L»dŸ*š|©ý@©IW7"cÉm2[£ëc à÷]ª«{-¥¨ï’Áy¨Ö à"™–tðáåɱOV‘ëU×r´œ‹¬d"ovŸv[*=„¦©¯×4 ‹î\NÇŒ vŒÐ¾^t—Í>ad‹–²ÑÜ(kÐnH(ã|%³5ž²%Æ›õ€•î(BϺœñ%¤SêY/SÁ’YpÓnŽØ|èÂç5D“ú¸H/ýà#ЇÕ&ɺ>˜Ü0¶l×Rï¦É7ËvzOï¬qiÜ+ŒÀô]ÄÄ'±IÓÊxŒÑœwë#$¥­6CÚÆQ„Ë8;KN¿Õ5\}é#›õÄ}4íX=¶ÔQš´Ö u« ¥þGøüÕóƒ•AE[®MÁ¬iå 86O\’fV7Y*M£õ–»x¸™–È©¢Ï·ZɫڅͲfžÍŸKzõ›«¶h5–¬>{NÛŽ c{ŠÕ» ï/¶ÆÓ¡u¶lŸÏˆ¢ù¡ash4Ùšz•£s¢(s7ÍÖbÆžJÊ©UÍlíêà¥|´Ä±>Ÿ¬*Ê<µÝyý맪Å÷g#"rV\³‹Ðœ O©&õa"Q4§ }ŸÏaÊDTÙÙ*£U3QïgÃVt7oÞêv¾&ÕîýmyA^0‰ldíÝèSí»ávi³k6x|:ÛLëœtk±éö"æ.ÁmÓˤƒO„—gÆóƒeò‘‡´7½~¸@|âRiÃç¿Vbž-À+PφËf›DoHY6M>-‰5•Ù.‰‘OŽFkØUhˆèüm%TKM"uŠÖ;f·pîÒÀƒ5¼¬žŸa—‰ ƒe2iÚ½ ¦N¬ýVר{6[¶´w†ÞuÓc`ª–éÙsÝ:øáåÚÕ>Yú©­­4Æ­Þ”5#e“Ë—ÊžÚ(‹³¬—/·²Ã§k™­|cÁ¶ÓÌS‹ù%WV?”Ï 8z8’_ûÈ&k™Ðö‘ \ì`–ëct¸Ùf Öhœ,Ú7íY÷SŸ¦Zu…‡^o9;w‹’n[nÚøœNR7$uB3Ÿ¿ªƒw{UZøv©’ï.ã®–³­¹¼W¦04®ßgÚ¹s:¸ìI•Õ§æÛSšãiÒ »Î/iÓNç·mR«;Ñíôv‡g#¶8çÙç ¿Æƒ1Ì7HqÌnSfâÇÌиç¬dó®wj;$2YÚ¬1ÍÛG6öY7¦TÙxÖ¼Xß^žÍê`S‹ùÖ°)ïcQ5†bjµÃÇö,=LDEó²4éZéDDò9«Ö-ñ(SÉ<$AõàfSèY~UþÛ„Íâ}B’²SL ©FÑ9Å›4+M•–¢Ù2DâDŒÌý\ï~ÕŠ—Ã1gš8ä@³Ÿ?a;ùäËÊ]ÄAŸ~0¼\Íž윙Ñ;½ìΓÚ9ÓÕ#cfÝ ³‰‹«fQ!2…kc«”žO(£QŽ{¬é…KݦچÑ%°m©»ÒóHµ&)ÈÈ×°òÓåò°ÙZp-²-ˆç ¤jŠ£0+1î‰'¸Ô%êÓýºh 2›ìòƒm¼XßEc;eHà§n”m™/1q63@Uj“{žÔœë©À牓ÇßMt/|ØBumRcA¤`¹×ͬ긴—¡Áf…•6JõV“7Ñôi´Ø^åçïX|9M]¢0ÙÝC7ûtðî™ áÓZ.ì¤fIQkvÎ&÷)o¹^*ª†4–hÐ*¢è^]#ÍØA˜ÿ|õÚÕѪmßVx £!¨§¤Žøî õõkN¸Ð»3:“Ù°ïuçÝQñÙȧwq]á)Íy÷&gÃ+:ø‹ÃËó}t°±ìljë)àíPÚz¼9Èù,¹u6ð©l3¤)7ÊÖt\©ìþYŽ˜oM¸SöŒçË ºUØ–úÔò§-MÙ8J^ßÒ&u1Šª[ ‘]»ÓQù³/ ßsM3ô‘LU:Pýì/òz{uðÃË‹ cÑÆ.z¬²•²÷z N§>ÔMµÂÐåœLTn5¼êM!¬pª§ð²¼“ɰÒYâÎf™Ì‚ï)1¸˜ÎœæÐpL79ò2˲ë>ûm"¯9¿‹íŸŸ(±Ÿo<(/ÿù"f"ŸË,¶£jDxÜøpÃyyO1éØÜt“ÍÊÚ‹tøðÓaßéâ"ü†ðòÊ•ú`úո+ú]X§P\s3Þ}¼O^¡Ä‹è¨ˆ¡‹m€fe—yS•“šºì’Ïd!e‚aÜó\Žã’˜Nt]-ßH:Í}‚{¼SS.Ä. +ÌÑ_×»aøöÞòÌVE=5¬˜¥„^ú뎩Õ[4Rf¢BïÆ¢Åv^JûÈÚ¾­,#"‰Ûv Mž”Özj¿Í©4¸d¿7¬VðOéÙt%ZHë ©ÎÕËCmò©VßÍhAt}£Ùø X;Ö&1DHgqb<ÝkÈ÷mÈ—–w6²ˆ—¬ø%Vï¶œ•VðëlÝÊ8ClÄ«¶g¯œXó(}ú$W2z^[C—Æ´iŸBÁªv]NrXಔ–c«Tx®èà·‡—§ÛyÑEkÂÌ /ŠÐxu¨W6k—§NÈUN²hv§Æ#—­6Þõˆ?.Ñ%õ*²iV¾"{£V•í¼R;§ht©ªf=ì¾™KkÙ|g§ °ó&Ç=‘ç± §ßÀ‡ój/íמ›œŒvé`ÏÂ|ãÚ $a=ðihÒØqIœÚ>’ÞüRGP5Q[É9ƒ2leÙa26ÏÚ¼y»³=!öÉÜ%ìi+Å3÷a-Ýs]:øuáeÀŸ]¨Ž`j2ÀÒ[íI Œ‡›*͘ñƒ!Û"«‡óC㑟þÑ~[ ðøÐh¸5_¬ù‘áý)ïÂ1®97…è w3á{Œ™:ôŒâYhÆŠÇüÓ˜ØìÓ(=iýñn‚È鸛Qpí|%ðˆo@Ã&³ÂÁ>à,|øÞ>²ÁZ3®øÈ†Ss®þ-+uðáå†ò³ sß´('ù”íYçPm^‚ò'eSžùg«8ÖàýäµþÙlÀ{雬+à-\Ø^V»ñÜü¼/í¬Ýx&T+*×Óñý… cšaðÐZžVs‚óµBîš/ëÕóßV–ßcƒ›Ù­0მÆmmÞØ¹¦k±d5Lèà//ïT›”¯¯ñöìÔEGØ7鎢³c·©¸æú·Öý̧oÉf˜ÁÔÓ™ÝÓŸ;©—†=!+aÕGS$6“±ÈgÎúÐyê¿‹‹ìq&ré¾À¹h¾ÑB³DFG[6Rg) “lP6˜ÝnÐõ¾Á­¢›û´Iª¯´8¼ì“õ%e$Ÿ¬ÇͦE8zfÞdjÊóµ¾¾–…Ò5Úš²ž5ëëÚö.SÜÈ-»âs7M‘PÁÝÍ}çªôä+®‘YZ[iáV3„^d­¾õ¶˜O‰GvI„eª-0>|$:-‚®ë'ëÎçŒS¾G°¯ŒùžB§Ý­”Ü!¼";µQ³¤÷ê`òƒåKÜT‚ Š'štЈ‹…3Ô‹MmѾ럎ÙL²Þš÷)õK"¦Ç'w![ðUáùMɱj…9ªK`è8ÕÑ8kM­· rW ‘òC„A3ê6«†>ä3¶,-cÛ‘Z÷¹£9kí<"W#ã­±bͺ† „Ñ÷îÐexdÓùÁUNV•> U=­eS¡yŒYșΦøÖB^Q3ìNp„·ê—Ð{gߨ*'«im”Xe™K™Þw7l§Ë/'ë)‚ÒíT7 Ìw °…ò„h3îf1 \j‡,*Yn±™Z…*?O+vÑNJÜÁÉÒ'ó5]d„œ©ÞLèÀ§k¯ñ–­­½]·0Õ ×Ú¹´–½K·ta¶Ç3ºTbµ¢K¦ëgO‰2õ‚’dÛÔ Zg6䜎[llãqfjd¢7š—6¤ YCC19IFH_ïó˜¼^—å”o6åÅ4V 9À`M<霪¤ƒO„—g’lˆšV­45l/ÝIó%ÛkÁ7#2y;•åºDU ‰ãɬ/n˜ÓÁw…—ý+=:,­ÝEM`øHÞt²5wÒ§ó­ñÄähŒ‡†ÃCƒiWu“7¤óß&Š;d÷s7å=@Y\˳ɇáùgãyF£û· hÓºÎÒ…ù»)Ëðæ³d‚¹+똵ûsßæt ó“Ò¯;áƒ÷+l”·¯ÂçÏÆiŽðþ¼ñ9{^/ødñ"í’“—hÊtðÂË+Wûdñn=aG¶+¼O‡ÔæÐ:¨¶lÄ‹Ân‚m›èE¤¯‹AWÅFx¿ùô—Ó>$'"uM-°ùi,zäÜÂ'˜ —C²LªVŽªœ¶lè¾§%ñŸ(;\ÚC—C²ebþV¬Éf.°i…šŠ ,¨!-Ñ ÜÕ«2›aöPZ+Qw¬Ö{p:ì¸ìÞ éV¢ˆÂlÔQÂ@r|îÌæ=ä¸V²ªørY’§}ñ눮#_ûj k¨E…j¹gË«&ßÙÊ‘³UÖæ\@ì·9û›|öŸ©N+žèHt‘:,ÐÁž½*ÕqF§‡ÒíGt-± å\¼užB•`·eì¤ešØ3~ì,È¢©ÐúÕ[ö"“!›‡‹2–œÐ›Ö?›Mæ,朋 u] z“Ag×ðê<|iÎM$eúü‰ÅÈçôùÑKE,ZÂNF@;•´›U^@'·Ù¤•“wY†¢¿ð’%pºêƒh»œº«[9Ñ$«Æä*g³ -¼.ÌõìQÐV”¯œumD¤qYŽcHÖJ[dþ̺˜‰Ñþ®Ä…{XEÐÕÞæZWvÝÕL!¹„Rèà7†—­ò¢ ¹­;ÿ"Ksó—Õ¶Àii³[RØ~ÊQ+f<Řsa¨#xºz•KYPa6ì ßÌ\Â7jPÕzR­ U¹'ßj6è /Ó»Ô)Hú_ÚÂÆÄ©s–4¥ÝÔ…fêBÑ20E²NF€)|d"|wxyöª,Z@ýÔŸó, IiQ«¡zé–…‹OƒV›©îÑù9+|Ž2å‹ù67B:ò¢›BØ |z„Ôæú_†B×p72?¹'îØ¸œ˜£ç£9{¼Yâ·o˜Ý˜V´Øm®þˆƒO˜ÚHä˜dýà „²^J§GÔ‹)iutЩoLwô®‡†<ö0 ,d¡ƒ—ý¢¿ÜFÆ”O§¢U ãðZ§“5¾BcÕ3×o?f½½þÅZê ¾?Ÿø´÷æÌ2ã$r&/³ƒKO 0qC¦Çîä¤+ÛÁ”‹›qò®°/ç}»o ÎÏŸw/ði­âƒ¢)5q™È¤*…÷V ÎÆ¸ÚÙjÖb\íY¸´¸UhýÕÚ$ƒÍaë´`Ï˵ùˆìçó4™ì‚³ŸÏ…x9ýá\yžUÉSŸ“Hm¬vþ‘YOd½Ý'í¡S6ØŒR/pŽâÅ-ðÂÓ…+‹_ n ×ßlÍøãÝõO„²Ù&fsÏן1ìû~¹Âéà¥ü—Ê£aMÇ– ïÏÆ>f…I Ÿ-¤ ]8DQ{——¬$>mÇRO³2}°¨f´h‚dúà©®¬Ý,m<Ä’=㓚¬ÖÆH†.[™KIšAúå®anøj ßß^ÖêÛ\sÿü$Ý{ltŸ>”†%¡h»ªº;Ùˆ}ß–tð熗O&?ØÅ&ª›Ê2ÖôNx Rë]9•løÎ–!U&ëøT7ÉlŸ¯¶y«”Èùz'l9³õÙˆR“Q·nŸùœ›¨Ž¸KK'Œ.^™8°ujD åRÍÖ€on[7Åòœ7Èš ֨حñ:˜”ëà¥ü•`Iù†%Ô<]›<“k)hÈh\a޶ñ¡ÖB–‘P™ö¤kf.é¾5æúŸ¸çþmñ'k‘ds”yejËGÚŽÀòwÄe¶ïzUJ»&•Ùî‹X+l/Âé„#aÚ­ÇœølõænÒÁGÃËòƒ•æoEM¡gÒ cXÂK™¯°š9•µB&…þú $†ª\]M;j¾ÆÛÝï-yÚÓ{z_}Ž»P~xù„Õ>Yzµ 8-OÈzÔ×bpÉ×d,­R%cɧ1‘©ûÒ|Q~—ó$ÊÌÞ¦GÛ-\Ú—Z:Ù4Í>ò7Ù¸T§ª­˜:‡øeCn³OÓÊÒÜà9ˆÌ\}Á§“r’`=üwO*ašî•2ui?jê‰ÝNÕ5Tkõ{}¬^Æ¢ÿry±N,šO|ÊëRLÝäš­ñÈ|ÎY~ã¸MÇ7Ën/÷Èô$öÁfy 6’~Úç¦BtöóM')167÷Ÿ³*T»ð45ÿ¾â1hú’5K;æ¦fÌÍ©QoIsêB– º—{ÒÁÇÂËóÈVy”U‹Ê*»vÂŒ†þó7¶Ðme-cL‘!>hèì <±;bÜ“wð‰ò.í+J$.[cÉ;Èú!Öv/m‰µiaÛšMÆ7ŒÇi ¶«©6I©íê¹â¼RKÒ3îKÁ·åtðƒáåê•óƒ Œ¡ÖÐâCQ´ÙN:&7†ím# L†=\Ÿ^ÞùdÅðÐtth2µÔ³f`öó5”±ìÉß}¹g>_Wž•'ö»Ék^öóùÊì¬æåž½ûÑÓCƒÁÖh½9f|2séºk,àBrÆDÕTqFc×d çkœËvG8_€_7V¥RúÈöèàÓˆ&l|ŒíZ]ïN™¦Ù‡x¢ßþ5ØéíÊZ?Ÿ8¾?ö!ü°©²tkϺ÷ZÌ¿²y/$áf£-¬Êòhµ<ë^ØÓ‘Ë©j›Ã)ÀTq1“óâaˆ6vëP-‚t‹3ë ²wÃ{›Æ»±6úìÜj“5ò┾m1ŸšJKœŸ S(#ÙEãÕ^•_»¡]¬ôƒ\ 7&ö`^ÛÆÊÙ—s³xn›Od©÷“áã<+ÁA†”Í0Ø1åaú²§QØNZ;[šv&jözùAÖÆœN}ÚPrNMr0ÖòÉÁð9¡Ó[EÞµú\ÝåÙ6ísmzâ >GгΪUé\±esÒÁo /ŸD~p “f½×ŠãªeHlÎQ+KŒL¶Gd½¦«» õî«kBW°Ó{Pº^:Fï°0¤“Í|òB¡Þª´¾ØOhÒÆö`— [(•€ã¹¿@Äé O\ÎôË·…ò«¨B]bÇ2¢–µ|kx9Xzn’pª cx^ÞÖ½~¡2èSÑõ÷î.òÖŠ^ÊÙš®ñY2­QåÔ¢ÒwI°¡ã–d+“ÎYã!¤Ä©OÓKŽl‘.bíØáö"†.:Ç`ú˺Ø ¦Râ÷F¸yšÝ¥ à¦}θ¾*¤L/cÑßPÂ+²Ê ™ù¦WáµU`eygè¼"Ò>êP¶Nûè“Zš#E÷Væò Ù(zÍWWÞ[ÈuŠNÂÀ³~¶D¦éxÒ—Ûwé¹#cÎì{¡ÒçûºÜ¤­éœ Yhë–ÂÛ÷¶d$Oi¸Õ~Ñónãq£œ˜^"|þÜK™3ïN%Œ§•Z®Õtle°Mw1‹\ïöªôˆðëÈu½J)= rp4èáO]Ãj†£Ph>¿ZÃtO3xù”,#e:ÞÀ·vGx“z³Zd¶FkýeRàÙGÔuÝõ†\ F÷UÍÕÕ1“M|A¾FYÍ¢Ø=ÛI¨ƒ¿8¼<¿4l;•¥Çn(Ïû8ÊȲ•o­—pV•gËàP–÷2†q–¾á]õÚr ½w*ªqŸÓâRZeé³,ðü¬'òðô1_Ô߯FI6ÐUd»¸´•µ°¼Ýh uB2>¨‘Ó8?˜ÕÁË|ð7úÃæM1*¦_S}Dw†Å'X¯/pj¸èk¬«H->ž¦èTë«ð9¨ÏØ’=¶VM—Ù49GÖIĘ(ØP ´ê°u _¦4Ø”²:Nƒ¨Óz¯è¡ŒùNýÌÓ“3;4šn5+mESyš%OíÉ{…»²s)8^ÓuoM§l47+²³¥ì°vã Þ&æïxL÷U'þéºà.s~%ñÕ¼ÇÂ>$Và®_QN'oqItÐ Â’ï¹,‰¬ ç_ý7Çø)-ìoÂ/ð8¥ôð0¥$a«;™ü-—1ò,Góž`ØfÁ( CÒ#u=² 3sSOâÌõÏWÏløfM*›W[twóŽ´‰}ÿ€±¨³SàÒ¹¦ó–g>3ݲS[%í®ÐœÌsMk8èñ~€y³žŸ»B‘m`èùó×$†iï³™nÍøÎ¨2ÃNûì%}LŸo0jô©5ÞM>äêîôÈ,8ý³Ér2½÷½Árïõ·ûm«¼èÍ1¬èÉuÕ’¡8ñéð(Û M·0l\Ž+$6mk¸öŽ7¤±Ã“²½#£‘ñE"6ûW»«uLål¸Óa¶ô¾•èóÕ¶Seèe)ßuß&sô뺮ÝhYÉ5ŽogkÚÁ˜%U0Õë#¹^úÁßšõ³JžÂlP~¬_Õ§`óK›¢"7¢ŠÛç+ÎB-Éä6|dHÌÓ{Õ¦ô›R°<_…/"؉Î~hÅˆŠ©>Xôl6© õaÝÜÙBî…ÄHK`èàõ£éµ£-ÂÏO:øîðòìèk#Ý.fw0vq®G·Š¶ºÖϬ4Z3ù, +GŒµÛf9Å‹Œó£®sŒÏ—=ŠÅØkÊŸäd˜]§Ñn9¾¬kåøÀ%šO5;Ü$÷Ú›\?¤u"nœL@ËÛošleûª@wåƒYY›·.é6)m8•??ŸvøB°ÆâLŸ‡Äù —nÓ:/òAõ¶§ÌÖ7é ÖÖ—ŠO½ f°aD¼8mÄÄéè»îˆC—” È'¹h£ê ‚€µ—„ÉÉ(>¡€ ÙeYPLÓMõ…`Ùª>^žÇ惙*v{˜›ÂtÏõÚm&#À§+³ö–½$rR¢]7dÀÌã&ù ­uÊžä¸+´JVµO×PSœkÀržó&{¶Cíß©Ø7ù΋ޱw¶Ø,»Õ‡µø“Îé$›[W˜yg“3í݌ֈ&­ôûx²µ](›B*õ”…Iõå?ÌéàeŽïÞ\û*mfQš¦š÷„I6q`‚sxYoû¨’>–Ô’( ¦Îzl«¹‹t`íXk²Ód'óÞn¾‡³9¬ÿ6ioTÃl…‹e#®_ÂFò®zVÓdÂ>m£ETæhæ{ó™ƒwzûÎIsúÚÆêÏ_vAN:øTxyùÁ›yVN<·2©S–ÃeÃCVmН“wë»b5ûlJ’ÖF‹: t› =ù¿™Ïù#%Hª]zM™®t«uæS(¶èÓ MÏÜx7,Að¹rvH½ßu]d^×úé›­×5ñ£û®ö<²½:ø®ð²µ>¸Æ—–ÇŸ™Ä5=¶ÆÈ¨³Õ,uÍi{L·AÎf{5žÙ á?úæ, 5"JŒ:ÌRsíκ"KA }ŽÃÝl†ÑÊ(ÜNÆ8–•pê,X ôÙr§Ü1jÔToùJéàe,úo–XVßI–Z×'!7.'™H&hÂÆÏÔü`œß,7ÜF5vDvOEÈ•½~€K¥’|)¨^Š"LEŒû¦Ô…×#d½:fK/oX”¡0ÔJΰ¤ø”•VŸŸtð­áå`÷ùÁÒ“Gqi«å^Sª%®SúÍòBË4qQ£*½û¡9{Mº¼B˜lVçk£„Hì¼%—”¼yf½nO™¢HšæxEÁûZÕQ¾áta)ïæm>9C®L¿!¼¼r5m`}” &¬ í³õ.DTf¨l2éd)„­á¬e™\C¶ó í°?¾¬T˜Bë!=‡.I'ƒ"ãñº.|þdÑAPŸ ·¦ì±™0åøÕƒpa²ÂuzÁ?²p7Ó.rÍ¢;Ób>,gHñ&;øÍt“s—l¹…Á¸VÛùZ»nT' gt£×*UèàÏ /¯Z­6°’ó.)=¢Å5î°æÂûsŸpP¦àƒ{ø²€5?‰¡·`û´„³DI&æÆ„$zH!Ivë§Ç÷1$°Ëé"ÖVleáħT¿\5]ôE;´{ñŽ$¬Ñæ˜ÎÖŠlŸë°Ëu‡æž¬„Ñl»'±Ãv¤u9ó9×´ßy­K·ójSª‰QMé°9¨ÑÁË|ð÷n¦Å]¼ƒ6ÞAm‡B²(Ê‚žÆÊ†OÝj—¸÷Qc¢os>‘2kE ¸&œ¹˜“ÄÊRгº—{Çõ“•2¬tÀ´«))4@ÔÚXèìõ)‰½k>°Éžìžáúë \8MäÇYO*™»_—¦Ý c'UWòêl­èàïÛ#ÞØóš%ý%2v,å/q¸D†gBò.2\½¡¼ù .w£VªªAÑBt;6-ÀêmkH†N¤‚å.ôô2J ѳ̅_–!xÔpÛ DBýÉôEJÌãí¿ÜsTÎ ›K Kb¬«;àÖLt]Wtð÷¯º“¢@Ítý¬ª”Œ¨´cJ ±) µÉç£QêU+=¤q£^²Ìðê›-¶‚%v§iBor`JÛ`fÈ:’•ÀÖY>_S£·•gÊ×u9!%CòuÙ¥™2ºK–5U©K,0ÕWIЂ¢g™ßýIß^®¡|°&£Þ‹5¤Ò?ÊŽ€ìéO»Z¾SìÒ¥ w®ËÅ·ètÒC$qé=¨©ë" Æ”Xá.0ÈX]ñUwˆ«‡¬áÒ˜$W%]Á]µôÙ‘«d„‚yOÔp~i ºã†mA¿¦’{37 êqÝîé<øÌ+:øáå*ÒÁU×Ü9‚Cr±­© %Aö<ôäYÎÑÀì2آϯ›„чƒU=ãe„ÃÝØô+Ï4F 7¨ÞÜ+?~M»”–cN…‘†ºŒQS3@·Y3Õki¶Ø t+ß÷Oèà»Ã˳WyÑFþ¬7¤, ÷I(È.¢¯‚q|ÉOPù‹µ‹ÀŒÔãÂôFY 2^¦^8³ý6R—^r²þŽyPÒ3XO¾qê•è›æ5¸Q² ˜°f­§FvèCÞ$‡ÛRXçø¼˜ßéiøàÔÞd²ñÂ^m²9Šîœ´Ö†¹uݯLŒtÖœ¶ÌÖ&7—¦?ždÓO8ö(à36ž± ­žtðÉðò4òƒuí-}ÅDmTl,?¾owÝ60BcÞÀ³Óɱî˜ùM~G§e…ÿme(ØpÏüÐpH>ðêÜ…÷'[ IÈ »ìÝÈ p Ûõ3>TæúU‘¼)Ë›gTÛûÛØlq¥åóüü¾)œ;í·©Ì¹|T˜R…Æ£m]Hîm]¿1¼¼èâX4o6Ä%3­ÅrãûSŸc^óq ÷u•µd8y)èiÙÒÙ]8°º>ë§ò+µ0…¯]©ÒÈ.kmgk±Æ ïOž©E…*Pù@ƒ·`cI,V¥ _ ¾­Y7¢‚:¥ãpÛJÇ™O'".Ýf´¿òŒçå(œRƒ5)ShƒžGfè& ÒÁ÷ÓËŠΗ˜¹Ë‘;2^wåZ볦 Z¥œ_Ëg™Ò-¯ð´·ìÒ –´1î±´—(‘z©Ik3 Ÿ/±ج2W×’–ñýÙÈ%Q§å÷«¢œÖ昔Ú*+dPm 'ÖÚ±)ËÒSe´–¢´0œ &™Ôm|Ï\ä2iõw±þÜðòÉ+œ,¶«Wš‰n­8WjDZ¤"Æ>θè"F‹ne9s9OÂÈÛàåaëÜc²µÅ¨¯ÏWäzû²óxÔ#†F>-èê:&úƒ*'ÉRÌÚ)ôˆ’E¥®ãEê‹ïS§*û¦8/ ­!©c5÷´ t"×Á]¼è|9·wZNöð:1&éÑ5Öî@Ÿ–û–&øº:ߊá8¦ªÂe{8^ªy¼›†|»ˆï` Ëâ —kNZW7fËt–f‰e¾>!`ÐÊvj­ól £ïãÑW?¹ÈGe™zN·b¼KïÖ[È¿fYÁs»62ÃMAçK+ë[‰3=Q`àœ"6µÇ˜±ÍÁ v§¬)‚‘TØN[o׺\Ròm…m?-é…òî Û;/§6nëd¶6¶FÔš´6®k«ÞR_±O/k“~ÌÒ‘,œ|Ç×§ÌtÕ÷úoS–ftç“{²3—ÒI™ 4¯•ô¤d qJ-"¥\ºÚ¾Š,ÝÙ“ì˜N=*qùö”‰²Òý$g—'i0IeV¾-•ÉÇ‚…Ì€¾íàRSÍŽçÉG.½…¹­ñúYŸŽ¡¦½·Í«TIß^žE~°òaÆôR¤ dý iQ« _Æ£z³V{·)кœ]Ž–婯£~[ã£ÅDF‘Þâ—14 !ߺ©KñŒòXgÊ©ë1JM1GÑyýÈÅ= ¢IãÓ ¿î"°Xq)HÑ£ƒ—~ð»­§)ïŽOÇ>í%MéAoŒÚݼl–Z©¾¬\¢¨Nlê’C6º3ß‚Q–ö85.'aK+´ô2Å~ç£5¶€xa§YA¥!b¡¸”w¿¶]øµM¼¢»)<ÁÁPk¡at˜I¿!¼¼’ü`EŠÑ”NàStjvL%uéXSÊν%ûhÈ6óÉ'4G‡F³CÃÁÖ\0ÍW‡9|>+œL¶³§=˜l“ÅÔÔ.-{ý.‡¹wÞMÓA4 ï&ü &ÆÃßr!•ÕDÿ¢º3½,Ni×™lŸ~tY)ÁîãÒ² $aQÛçË’z ¼ËðùîBÙðþd8=¨ÐÁ§É v:7)Ÿµ/ ¼¯å¾çMn¿çónˆÃvÓÎV­’š¸x;ñCÔN!&µt¤ƒ??¼|ùÁÒ=h@É:"è}…/`ëOÁÊ_&‹dž-kÏ`þÄY­D”Õ2y/sZ:©sŽ©q5 XÚ6ñiÓ¥ òDñôá¨c¶H#Í=µYüpeð·Dœ•Öm ŠqJ{lÚ°]=¦T¦„ãÅ5÷´Ú©ÚíÌêàw†—«Ho$¤£³y+ÃÙp+Ù&¥}*ó…´v­#›dlí‘Ky‚¤/Šé‘å9Yîqv›³Tªš6™X–¸Í¶4¹!= bNW‚g[yg']¿ÊUõ¯†^½Dˆ^¨Ë›7Wwgë ¼Ëɲ-Žns«£ù…œÌ-Só4GC¿d­†…“Ø“¶UâÞºŠHS½@o"Å#é-­6ÅI¦“òs¯œÓf";ì*ýiz*¡ Ú#½Õ": Jæ ‡Ý¸ñ¬RtÕYÜÉâäÃî¨ÄdèÓØæt–¦mDÙûÂu©ÐÁ_^ž¿zfÃ&óÁzB©Ê©66/æKõ+˃Ðv9ÍÇyãS¥eÇã ªÉ6ˆIgÛ¾ª§bù)N‚¨=wuÃO–è…'¡yÑíöŒ.nŒræÞº$´Kœ‘1ÖObVwœÙ`l´`su8Vs$¤äd”Œ;ÙX­gÆ™Ì|NJ2ù §×Ðé_Äàª75mÑ‘KÃT ƒ@Q‡j(e(ï'ä$ TíÜé’&1)àòUfá Ù4’GžG*“d žhãx¤g}Êuðn¿hŸªM'ãºSO‘SEnîjšÌc…œïÊá0CþËëôŽŒ/1š¸œ‚e:š²ÉT¶…ÁW¯FÚQGùªu]sx”ðˆØ^>{ò+mä6ž]i¤IX;¾8äˆútð²6ézè` ÏABM¶E¡lÑ[×&-î"ÖømeN“NV§y­‹yOè`ìC›­ìëz2h¦ÔÖ‘ɻÑ›Ê땉©¾ø‘S|íÜíÌJEÏç@ˆð!u×E‡>2#›Ê6C¢®Iß^®#?ØE%Ôé|k¢èœµò9Í¡á,XˆkSÞŸ‡©÷9Ô`~ n’_§íMNÖ6{x±µ˜Ù˜ª{—ˆÎ¼Ï[!™Ï×± üʰ$ØrU6‰Ð>õ†Tí’s9™‚ d™ÆÈšZÜáϼ³P±ç[Eó× Ä¾ü÷<'4ÿ4DgMJæ‘_k±Ë¼Å¥†F²¶,c+ü;t°ðìÂ|¦…ÜüÓ̽5ƒòæÏE+™?áZfè§v%ëjÎ9sØ*Ųä"÷}ixö¢m‘Ì_¬çú£šÍ\NÌavë#ˆTAeË“ÍC?_- uN¹ÌÌXt/¹ÁÂç̲l€ÑÙ2d™ü—¥Y]®Y.}:x‹¾ ™÷›‘Þ°§°æä»¬5ák}—Æå<Öb²jåÊÖ>ëî·Ÿ?_+÷o·ùÜç2™džu_ÄÐéFÎoKËi-œ–“KÅužOW#z!ß“ÍÖ`ÀVâé£/å½*˜6¼z(,ù7pº%á?µúQeÓò‰WVÙ•¦©ëF>¼Ùù2µÍiVtðO á5Üæ@ í>ÒµC1ìÚr‚¨Z¢V8ÑÐÛFÏwªaÁÝMiŸ­Ë.½c€ÃzÈK‰á6å‰92ÃpÖmýŽæ.E¦ò|œÒe—Mâ:µ¯µÉ&.IGK ¤(í'3<õq ½¼U§jî+eeÝŠþi©—i:C’eã'lÐ:`.}ÏMMUƒJf{3XÈàó­ÙÌvJ“»c‹-Ï ëiLùDø1Å…±R‰®’Öý|}–§Nsù“X;ÁSÿ©•|­Sº3Ë}ʵ¹Ñ{VN Û¢xH‹”©RñÖÄŠäƒï/7P>¸ÌÎàSùêìé#}.ßæ¦®œ¶%Xýù§Q¶O ž¨h§äkãìTanP¿Ejo1XÓ­oëÓüTÆP轟ÓòÅJÞn–£¼ãl¾V9Ûš3s—Cà…¢pÖmSF•Ê‹ŒúnZZ^úÁÿØƒŠ§#þ‰üÞÅzƒãv-L+eo+*aK¸Vq`›:Á#kå'|€zOK­ÄTÞ–>¢µ4»¬Ïãë…,x^ùúÕä/]9ݵkQÛ"sU-·4íŽøä­õÙÛÒXUÏEЄô•e–bµ_ôJHשS¬m òQÿÊ^µ²ÔE*GùÕ¼ÝÝè3©š²¸ºjKTKéûwoßnGo8r!Wª§—ªn\º•\&¶ƒînl4»¶JŽQ­Õ'îÀfYÝsÒ A…^úÁ?[Léí+¶ðyÏTÙw¥$º5œºð~Õôþ ç°xg~Ô.€Ó)ÛU³ ƨ¤•IUÑ5”–×.ºãR>u/ФOUlÊ+÷°þ—N†úÍ*ÚxõWÍ"vY¾m1ì&Æ f>Ä“ŸÌWtðÏ™ûc ¡Ñ³è¦b } JÞ”~{Mª”,tÔ[¸:ô)\­¿2¤[Ë+§-þ‡‹ª/ñFSuhÈÎ+Û¬i«YNÕl¶¦|bYai/£ÔC7HÔíd±5wô2šŽ‚1¬— ¶ÆS+rrh8‰Y2}>Eï`‡ëòíy'6Îÿê¹IÊçaiîg4åÔVž2“yNGÅvÎÞ¤£-Rx1q9¦,/õ²KPÑR Œ·Ü!èÃûÍÀ' j{Êf§¹ó–çF4=å¹Ož†·ä-‚Ò²ŠdºS(h´‚>o¯z/‰<ÏßsÎýy­Bs“w#šê•÷°ù»)dnËäaÙÝduð./Zlì{«=CK‰QÅT¢åe‘Ä·U RºÞ‘”ìWI‹/ÛÊFRyYZH²áƒŒt7‹ŽY®bzxd®áSNân—fiìç]Ǻ"\/Ît¥¢¼8³™&zõ*"&äVÎkK*|z|žÆ'ñ‘Oå+ÝîüÖðòœU¬ÝâÒÓëer²‹BïÔö¹n¼F’qÊyÔS÷RBû¤‹hzfz\©Á¶kd‰aðJÞ³ó¥ƒÉe”…™ánD[]«¿u„Ê®y>UåîeKiÝçN_0I®Ð€íˆ`*ÈlxÂ)Ï"3ÉsAè‚ùü^¼Ìÿ‚¹þÄÙº+Ys¶"$_VïnÝÉÂÓÕçÓAX¸hzbÌ Ò™„àc³tLë†Ú‹‚†yr—ƒF×i\ZÛù>í<“ÚnÍ V“M‡ËôļgK:ÆÇÕ¢]ÞãŒ]ÒmjÛOE34ÔŠvqž~Ú7+:øÅEúr ‘Ô3Xæ ŽaW»7Rq¸z;]Ö¢r€ÄàgÈæ§®×'i—el— l †g ÀªjÛvΪ+»7qfËÄh3aµX˜Ñøï`„‘ñ?õ :XB»š*-ÙìêJ&ŧˇN°'ü¶ðò ŠEkØÑL„bSèH­Ÿ"›¢©è@k·¿ §­É¼9!Ó¬‡v5q©ö‹ºz Óf™¹ôåÑZ¬ºVGêÜšKëlVÀ»¶Îv—­ZI®ëÌb°.%ç5z„¾àÅd "QYÔÙ“ì–Pý²ÈzøˆyJÒr·S/ýàîÃC“4V²tÖ ÉÓ j³˜lî¦â¤ëªžÌfÍV£—VXëjðBÊ[?°•lKú² ƒJGYÕ¤+ŠZÔNå­Wº³^É~§ŠÈ& „¼ßYGýw´…}ê¿õ^ ½æ·É»H:øðòäX›¤ó°L™|É^ïf—·ø–ò´&Ë3Zc>µ²¤Ú´Çów©êµpaGͼͱå¢J[±Êƒ5sŒ&¶Áà,ï«*É¿g"‹ÍbsœÃ –C5$Ti YWKfšÒ¹Oíae?Ý…+?ÞQŸÓÚu’.ÒÁK?ø—ü:ØójÂ´ÆæËeœºä•|[a@I‚.ï»ù`‹¹O…ó&'XzËzJdeªŽºÜdÓqa¤rí«¡Ö°< -5šv4ÔŠÌF—¿áX™'eà©T ìé˜Æ,FaðËpoIŸ&L~p9wÞ ÉDuƒÂRO½è3>«BE!«„›u»· §†Àu§ÔÒà¦üf©Ó¤w1äM÷Ó´*’Ч‡A`ƒÖP'§9‡|qh4Úš¬7>‰ïÏF>OL«¯­²ÞGv5ƒ/2Þ°ÂçõGænœj}`>vÇ=›ÂØãf°wüÐ]¼ôƒÙÒª$ÿ.gï´x²æ½µØ%Å/ùö"Ö£6ñýƇÖj‰èöëç›æL¸“‘y[¦}HõҊðþtkÌeyŸ£ýüñš­ߟO\Ž¯Î§`Ü…£!P(ZrÜçgÙ|ÎZÙ@¤Y¸3NVñ– Ëfk=Õ.À¡K·Kv[™Vû õq>XÂn RÖ‚òÛ’¾7¼\G~°˜¨¬½;•«“¯TâíÎÂJ%½•+³Ô²1ç•aªé–ˆV‹S-ÑæyöÿìË‚Žê]« q,•±à5iÇÚžgÓ°1*½à‘º4B°Ð—#Œ-ÅÏã­aלqœ7.'æ&ÉÃX»þv‡.uKB7G¯Ûõ»Ø§‚…½þ¼ôƒÕ…;ÞãÝŒgì9˜&™T1ê¢épc±dùr“eRšaVË[]•ˆÖtéZá¬=˜p¶»®’‰ñÉZyh+f'..Tžåë~Ë2¡¾.8Z¯qì»2D E’_눫Â"ê(k…ãÕØýΘbÆ¢ª3áž[+Vï'|gxyqäE;µß7ö¼æS2†ÈS9Q“¡˜UŸôÎV£…­õ!/‹ˆå<ƒÅ(6åL¾ˆ%Ý'2žØê¥¤1º³s§¦¶,`i4Fk…Tè½ém/ ]×õ:ŠdóT:áÅÕµdƒv%)”€ãî4ä|ìÑ7€ï  ™D©¾;¼<{¥_´09«~ª¶¶®Í„{ªêä¼S§ Í:VE:›+Eaê2ìn(ÛAÓº¶´.‰†)}ë}‰Z!<õH[<{ÙJ™PÔ|ÊÅ²ÆØ¨ÚÖX zŸOUT¥VÎ:ÒB]SC[”ßMŸî8³Áx*K«áÛ‹%eË…û¤Ïͪډ±|Í$¬»3Z>E¿Rý×G7òië¦VòªÞN–À¢‡áÐ)—Ï·æÏ6+75žÓ!.µÈSº0&®{/n«»k°ä\~Ùg?å²™¦óªÂÚ–wv/Ýé½:ø~z!léìžä‘)‹Y·{1Ÿmæ|×l i>q‰‘æ]–YÞèsU3&4m«˜Ú3oi9X _,™ee¯ñn+±Û@›¹œ3b‰{º˜ÎjG¦6×k‹SæÁ§}øf«©d¡Y·¯1ßPCÚqÜt“=ÑóáüàÅ:øÖðrtp•²šš1`õr¢8‡B6I™––Ô-Òi£KÝá–®LM¸É¹O—lCQ¤¤#†°”ÊQÞø^¿å`-yßVc?‡Õºˆ¹•B×^&åÆ= îÆ%B)Tqê%­¶f墶L¿9¼¼Eo¶½lJ ÓMu°…²¬<ºÈYfÕ•)ú칎â)Kl>û¬:PVíØ;õ q&¬Ú¸•æÃx»­În6Ü̇ Ç&ùÜãɾÂM•ÓœÔ--+ï©H›6.‡¼Y‹r1>¸öÞš¾#¼Üàå×­õ—vâÑ'Ç6Éå²TþËÂÌ¦ä›ÆÃ(ù|¾^2OšLè©N½¨@¦ø{Ä8cifîFci21¢ìÜÉšðþÒ42×gk:ßš³k+OoÎ_¿Ä{ÍSÃÙùQœ¸X²v‹½Ã݈J£²rÑûn^­­®Ý”®Â¥IŽ€¬M1êP½áýÉxr°@;Æ¢%ñÚxáµz4{sŸZ=Þp±ˆÆ|çd¡ëÃ'¥/ÍÝ蘔ÚÍìTçØ9wãŽæ‰´ ZÞS椽à 2½¶rOèÛ¥½ }`ÒíêëW£e+xiØÒTF³5ìH¶¼ºœ€$5Ù™^ÞÊ ×­ç!•éàeŽß(ß Ó>KduïîÂ…wƒlºÎ…²u/ËÏVq÷2éæWï»eãÕ'Õ–Ô{¾uz:ÚÌGÃÓ°÷ßã>Ÿï¿g#^,ØÎ´¶C’\¶Z»j£‡ê£óe¡µQw*dZë_ÇÒ­t8õÐݪ×RPäßÖ¥ƒ;údY åíë¥ …ÞŽK/ñ| ?ûž”S»Œúz/Óa]…ôÈ•š¬t˸AëYƒ†=Ü‘Ï)K›5`LèÂfåÆ:A@Àðùa+Žá²mEÙçzrÂcŸS–êR%¤öÈì艣¸”LI:eX“`˜öéàe>ø·ªŸÜîíZ óiޱýšÁG·Ñ…êUÄ9.;jSÒ°zPoe©g[ªDh‚ZÍéiÜ›¥zÙhЊI·ÍØ#Ã]úX\R—À›¯ZÌ×{ß­¥(¾nnG—¡AOµôͤƒ¿0¼¼€ü`7ËÆåÌ@çH§ÐXì1a\Ž7vJvÉÏ[((Òî¤'^±péf`I*ì02—ó 7«÷|é5B±¦7‘mtŽÒŒ¶):ã{r×å„ZнòÜ@­R¸l0dQEY±O{̧|Kxy™Sm’¡¢Ó3$¡OãU®z´5ÿÄÕ*³ä„µgŽ!îš,9u ZÓpÀ¢`E{Ãxè:ÛÏJ˜ÝÜ<¡A÷ì+ êx½îÔ˜Mš#Ò˜¬^ Ûv O¦0’+³U¨ƒß^®Zád „Ž9ŒªÉW¡ÿÖWiµF¸õIØUEu“z;׉‘|™´‡P LaÃÍW‘é*¾mN6e¤IW‚1ÙUX¬\·¬HÏqÑ­ëºM\Ø•6šŒ·Æ ú„~0¼\½Ê‹Îj­á¡ÁàÐh¾µh.ÖZñýñpkŸÒÊ<®ÞTòiñÞš­†;¡—ñ>ÛÙj¶+…Ë÷Ç—óëò¯Ý‹X!a.ßM]¨æù€Z~Ý ¶3[Ja÷&ÇŸO7é ç´NfwÊJ³²r÷&Óîå4õÉ›0"Îx“l]`îóEP¼ˆfk6°Ó VØÚ†K‰g‹ ¶Úå½%-!x¢-cº~ÆlfvCw"b^r²~»ÀâtdY—@ÎÜ´ 5.9aþü,=æ ÍÐô¨?Ÿ<©v™èül½ˆÖqeöŰ{‚ÇSê§ÁÙo¶‹òCz‹äÔÔÃRô•³U mOóýº²ñÖÝÙZ,º÷´ ñß"¸Ê³ª¦dƒÄláæœR±V–I(ßÖ°}ß–tð½áå:òƒ•ô;ƒÕUBÊWÆBÒ‚Z«MTÌ.an6hmšxSw£Kziƒ'Ò|²Æ:ÎN\jJ¹vgVÝ" —41›rq5Θw™)ê–ªÆëÞ(@æÝ¦æÀeJBM&SÍdÊòšž‹ïç¥ßÖÂ[Ä&ù:x·G‡­bn9–7ZµtŽ”/,E{¢P•Ðswo§™OÕhÖ³zbOºmyŸa-¹â†Žÿ2‘Á”Š@3›Ò261ïz'·$Æ>!IÜÈ2¥†8º¦¤Cëÿ;Îà,¸*«`ŽÌn´1Ù6aV''_rø„¡ÞOg»tð2ý{èà¼+g1,tÑz—É¢E·Ý;šzœákñÌ5‡Éº£ž%X¢Ë,A‰æ§H@Ÿâp9ÔYMR¬ÂÁÏo°ÒÆ€jzœêŒn5ÿ_'æeùÞ«cäÂ76Up7±aSiÛíî›ì£¯màØsv›N6v&ͧ÷H•Ìñ’Ä£ s팀ÉKDl„µ²f=–ŒC¿OûêÕ³ ÿK!=å17\NЮMêNþöÉ=—˜²:d¨i>iaŠåÔd IØrä— ,eIKib6uÀ½ŒNj™h9^NÄϵ…»ùàH픪)x$˜Y¾h¥ÞAA J­!tþ¯Œ 7î¦ïŒôÕ¥+:ø¿Êë¹yŠžÏs0g7FðvÏ´p$\ÃSw©gRi ËYßž]>}ï`8Ö-iµ|~9•ÈÔNT²i Yn¬VNFõ±é¥©²0—¹¹,ãõºl ÁÏñ*{åyëw;%LºøÉ‹6»ªkAæeŽ{šO¸h+ã9Dñð“ú£DâŽøÿú’ò+‹Ï;tɨÁš¯ÙàAB7‰ŸÊåN† /\MªV•#å}òù¡é˜îa^¯ù=êµúlâû‹¹Ñ… Ä/›ÏÖâ„á¿ñÖ´1ó쬟ÏéoÍç_¤ƒ—~ðï—˜}¶»b<ÜìÒÓhq“ 4f»+äã&ÎwÃçÜÛg³XK¶ÑÁTwÙVy:/;D~—¬2n+¶±ŽuÛšè±Sý¶hþy+Ä”Œb·Œ±ÿù¶x0¿åÙds»~š5¿¸Ý„NjåŽîhݬî.å²^¸Ý81ܨZ!UÝýü¤ƒ??¼|ùÁêìk>ŽÀÎßÃÇÞr!¯L­±+Pl¿Íè ´ÉFÍUƒm¥ÈNä}NþÛ y+»c¬Ýbý|Ùõ7ÝÆ÷pìRÛÍ2­ZÇzû’k—ZžîîÂqØtË=ó>œduðÒþ襜]-¡Q<ÚzËÌýôOþÛ ÏI×Þ›Ó¹+eßV¥Þ@ïq¡Þ´E×ø­Tš¹t5T›©º"©ºÁ?©áÕžªF¨±$æb ÃUŽ©·«Wôd[ôl …G®oª”l4ÊèbÒÁ§ÂË“ÖÎfrnœ(­‰É3;ù]WÖÝ“mÛŠÍõŽv‡. õ-K;Þf“Žæ­Ã³Á­¢KýŠÚ[ï;[7øm‰E•Óâõߦ‹‚â`¢¸dݸ§ôÙ¯÷žnÅÍÀ#p­—çºèÖ=ÒtV;s¥wˆúEõ^¼ôƒÿG6Hä+1 »ª«Ë$_mÚ¢ðZ[ öbÄÖ%Šü:O.@…ÇvέÃÚ­ë6»²ç ݾf¸VòàM´ÙÜæÒù·)úìÖ¦ÙRsƺ@‡®—ªÔŸ­1ª !›­f­À%X«ç'©F¡ lF={¼ñiG(âÉFƒ·†œ Ìe™\Ͱ{ G>…R¦Î£K©nHMH#kküývÝûî¤vé*^äî[éGš3±m†uiÝ^í ’!µU\·ÀW§­Ð#O|ºåË.âø‘»*à ¼ôƒÿЯGƒƒ@3R%á!™£ß ºríÁÔãˆ}ÚˉiX¢ã¦cóÙx—.-å^éN…2°E¬ÃlSp6R]ØÜÖ!‰·¦lGû¬5#©Ï¹ë}+ŠÆƒn…5vI‡hÈŠÚtÃr”%´Ÿïd‰Ê_ÒÁo /ŸÏ.”ÄA ©QGS ²á(ãz&¦®úO¦–šî¼ópì¢é u`²êw™ÿ×Û$'ÚG†˜ji|cB£ÇWÈ2=2Áùk¦Ša6a1)4ÌF[2‰ÛÏ%©D î,=õ‰ !й7ƒã¡Ö©ÞÎ59fæƒD //!ìJaI‚·›F6n ã¿=Ú`à&Ë# A<;þóæGaÇã·9ûøÙ¤ŒSÙ†íh^AM­(óÔç0 |&uQ›°ÎQr†®- ßÓu΂±†˜7ž·?ã·gßVD 63)|Rbü†ðòJÒÁåLm}\±2­@Öc¤7Á9ôéca#&+úçíýü&|HÛŽÚ)?ÆSµÆáÑO;¬®ñlk¶(?øK¾ä&[cÖ7ËGpœgKBŠ ÙZt$‡ÃûÍħ`)+Å2·ìt$FÉ—6,Dpãw,—ðþ|ábâM¿x-ºˆ‰;šµ*اlî)é,²'w¡&{€ew£ÐÁË|ð—…,3ÍxIÞ3m#úñëF-ëTuÊ®Á$ºC–¼kön¼jšhm5ÝêiîãÎYÈÍA´WàÙDU á‘u°œ€^aËÚÉ™úúNŠéóäõ¨Ý;œ²á¦>ìÚº‹€ñÄx¹YØ¢ÅP8‘´Õh¤ƒï /×E^tA0ëâ±³WZctaÊž•Á•¤çIÎìç×sŽVtFZ+_Z»©}ÚuŽOL¢µk¶‚h]øTŠÊä»^¿Ê–Ĭƒ @79wÑù(í%{Xä»Ôë×›² ×¼;ªÐ4.©]™ã7ëÊo’q>sÙêu#oTÑ¡ƒ—~ðŸ§Ûû<ÇÆÞN·î.Zr4ó|Bu†€hãè %­l¹”%xmeó‰OÙjßÓ˜ }˜ðz3XíÙ9µ­¸òÊ-/»5$dÛ©®ibj$!7j¯RG7­ˆ‘ägmOàá&‚Cl„žÖŠþß^MKlk¡´hÐ$º+»îç›'­›!À7š3ø©ön_üj*[»Ú“sÓÙ«7fEÊj,E"³F7އ õ]ÿ|ZÞD²f¦Ö1Rt·58å]ìö³9u!iÅãÇKÅКәtð‰ðòLŠE—‡g]¼óðþbêÑhC¿u®E¶¨Œå”x×P¦ î‰JúD ™Ú ZÑ ô†{ø±nÆ‘)Šp'§Èò›óîý>wižej|<¡"˜â¼Cw& [ÒΕˆrÎÑ7£ äÙúÝ}•ÖM|é ¹ÂéÖÁo /Ϲ˜-¥uêoVFY™÷Ä¢õvGá9*ÎOôR|þlÐw[ l6j",çž÷¤u\jÖ¥5ÙMwN¾{ÔtIùÈ‹ž‹˜øœ¸c FðDÏú…ke:øhx9°Ú'Ëæ”VMf¥ÓâÐdthÜlMÔûyË€ý|ûù £Cƒà!N׸ñý©Ë1!|¥U{ã5K-¼¿Øòü*NLµŸ¿®ãû³ÙfÒ¶›äkòŒŸÏÚi×|É´R<­L•Ãv˦6¶Ýô‰3_¬Ÿ¯ŸEC±¼¾á¾Í~0Œó³×~¾S ܲ¹ê`]¿è¬ çüdyŠ˜qW°iöókfp«_&>^­èÒUá¿ÍÊp‘oM]m§«Záã°VG+(å<”¤SŒ¼ìÞ û”ˬ´õcRZ+gâS8ÍE}Ë… rX­™):푇C<Ï-rPçÆu)·bÜ‘–ÚëŽþFEs†é…Äjþn²%8Þwc£s/f寫¾ä¹ÉàY»þvƒ/\ŽûPNÇÓùš ù¼¢Ý±1üÙ%ǷǰŠýü+Jgó9kîÛ <  »y×ÜͨõUý*Ro—Ì–gá—£¥é¼ïܬN=:®ºÚÃ.wêœíkñ´Š&ïÊ5”2Á,#:é°µº®øþ|²ú¨³Í·xL!Üî[žút?dKÒEt »Ùȇû\9ã`#còâMèaéµmeçA§îI;ͦ.ÉK7ÿùÖgFÔ ;J%˜>¬©õ¹4q[=cÒÁ·„——Å©r*F·RU”ê"Ï`É(?¿h/ØFc:ijÜß=½±É{“&;6/uêœb•2·¸é1¹Æ>‘EOàû›hì ×›UÑeNq%gz©55$Û®} fq°Pß^žåÄ‹¶³²A7S/AÞÆ0D­7ÞöeÎ<¯×¡:gÕ–_(݆29½Þe>y¥>ÇÆ™bÀlE®>†­iÈl¡.–7Š36žò''m”½#$ó­·àj¥¶O‚%_‘éœ`‘†dzâq3Ÿ®cê[V5g¦Ñ2º¿GïÖ&»„¸ó¢j&Uó]%Œ· DG…k0ßÂtÕ„'êÆC™:Fw§Ñ`hÍ·F^§Üõ|þŒ Ó÷kGM&QëR¶c;EO°$dŽÕúyšíMŽ+uŽíú²ÉúA6)ÎíRÄ6J±ŠyÙLO»3:ó¡ËÉ¥þ„T”éà;ÂË «½*mQTþIhMnݹ¦;/-uÄó­ñ„­¼Õ*:³•º.‚Ò"¤i›;ÜL¹×¥b¿é*†ë2-.‰²ÃTÉ.›ŽëžÔ3QdA«îâÊÌMu‡´YŠÑIr< tðn,Ú§–Ä%7×-p{¨ðSŸîÑuý|i½Âzñ»£jÓ»ÿº´l܈Âè´º>Iwo¶Ð@á Ú†|¯¨M¤°¸|Ú]·òqÁt>X.«lGìñö‚8Df¶èà:CÎg½K™hk×”1ÍØ£ rÆHþúv–VÆ›wªß¶/…Æ‹†}6¹e>ì“yå‡}fÓ¢ü¾,íÒÀK±qt ÖŸMxìÓÔÆjšsiÃ|t¥cJ“~')`òƒ½CS<î(‡÷'ŸòIíQ"èdB·Ü•´¤U5óiðʯ*ºˆIÏÒöi $0ý-ZÜRö9X»™z[ ÆúK ÐKrYHÁT¤Ì^¿þi¨Ú Գv‡ Ó ^ÞVŽ$ÌÚ{7í[;,¨í¥ÏïŠ0’¸ùœÁ,s±&Ý‚w2ñ9µUv}3lý'tðÉðò4ŸÚ¤<ËÄ}½ u ^òë¦ ù'¬Ç#È™üOÑÒ4?YÞ„ài[»u[<9KÚx—ö†Bß°$|1ã”–ñõwíæŽŠšèšøDMm:ª4Õ¨A:UÖü6AÝ6=ÎYGš“vÈÐãqækÂüW°)°2æˆzÆ®£Í¤3𱮃—±ègd ª+UßY*6>2ml2Ÿ,–Û늺DY„ ·«ƒ]„ ­F53Ññ$I L}ÍšBë|"Bèôw5ÌŠáJçG_’5­¶ß ÚIÔO½çu =CîN”Â6xÁ»åºñ+’øÈ† råB·ý<öq\¬[[ÊMöù㽎ñ*/ÚÝ”4ggÞ¥«¢ð"j:››X¾zgß‚*ç* xd¹ðýÔe3f S›Ñ_寳´VÍ=rž¥n kXvo…gÐÛ¾­ðô ¡ÓÖg¡.|Z;›‚c¬ç˜g¼¹ËƒñSÞ£ÃÒi59‹=:øíáåélËÜŠN<RzÖÆ``jå’ÙDÓî°õm¯ÝDÆ›9&57ùÖålew3[ãe¦¸ž³¾vâØ¨ðϲ§©-AmÙçˬáºÉCþGv<‡Ö¾ó:©Ùš¯Ös eG›ó{]:x‹~vi¸¢fòO‡Ï˜ŒÓKÃ-+ŸgÉ”–°bà kàÆsÑ%êi0g™4ÆX¦ãA¥î¾¦µ![×F¾m™/«æa µ’!jâRè[5 h0[tÇývJyO‚̇Áe«6æ-YËÁ¦+:ø9"ËÙèZYI~¾KÄ`™»4c©ø2Ø{3wSÞËXA›v“'SŸÍls´lGìyHDÛ4»½¾Iƒ¶’•ùV¾~!ƒ¹‡V0û¸¦"NAŽt£&…0m>î‘ãM%?ŸX7I/G±hM²r£6¢ñ£_0z&š¬ÛžÁ7nÍù£X jQáMU~ÂPˆ>‘o¬c+ÔHuëy,1D—TËf}Cgmnš»éd\¶LgÏ t»Ì÷œõøžCsUZ©ê(ª'Ñä;÷„Ùúuð]áeÿjŸ¬ÍÆ_*wUP§²uG¨#qE¢žÁž¾­Œ” Žg×<6¸ÁÅ›íI²}“A#K-ƒ,Â\Ûæ’}þ¬‡®7òéæoH‡ŠœëìM‡ N„ߦëÁ™JãCÃHÒ^}€áý‰àô|¾?;wXà=õ2ÜÙ/ºÆå”|~û¸Ö iÂû£­‘ =–14¼÷CÞIe÷C)½ÔT:-¨Ï‘=ÀuÙ>À‘Kë7=kl??ï‰óKÂñlIïåÎÔñ÷VÈoæâºí“Z_îi9ú4ª1Np¡9¬×HŠglõ‡ûVè–'kQäön¢´ÙED÷èàe>øù%W«t³™4ö3VÚë®5íjéÜeOæ3¦vÛFmˆ›š¬ënÀ+SWTXBíú¹Ïg›"1Eî6§,&§Üï£×—ºX?^®^ɳ™8ë"Z±=&‹K«!¾TÍx“™>~c[^žG:x#[@ç¦È²zmi‹¢0Ú ¡jœÇ…0a:†¾Ð_qt:?ýˆö&›Ít¿K3݆=R¼©”Í» çž¡±õ¹€@b² Š‹Û+G} ™ûY“Ÿ/Œ »](Ÿ`„>ý®ê@¤6ªwK1s:x™þ¿Âxw­"³óÉ75- aa²Ö‚©]ÁNFc匿šoTÇϺìC®Ÿ®>ž[YïM·±uþ™±ìs¶E‚uóV Cãîí:{tL“:šµ'qÔmÈQ"ÑóåÏxC®6Ë©\æ­ž›dÈ>ÌTY›8›’,«À‘²¶õÏÊ9õƒÓ> ]?a™ª°ÎÖ Û•5Ü—¥¶ÕÕ±óÐ…Á+[ž¸¬%%6]ÞsNZ OAÖÍê嵎)d’Œ·S¶Õ’‘‘àÙrUâ¯ëà[ÃËÁâXtí³i ¬—v)ЏÖÏ9qá}ˆÓµY`æ”ņȿʆâÎÜ"™•ÜÃöú°}„™]½SW9 ªfÏ눇¦NÇi“Ü"¾ž0ÅÇzŠ9\Žtjÿ‡xÔ¸Fa¥’~Ø¢¸ó°Lß^†+õÁjÆ€ÊVm>Ô,äo°IZ·=Áå±ô®ò38Q2çÝèZEX–j¨äÒÔž›x®óD–Üï[àóJ…@ÑÛSÀå(8¡ž¼f³h— ™5£®ˆÏ“ÊKǘp Mc8ÔwÕêàe>øEbCathé›ÓMµùšL¶æ]Ïd2u.›8u¶½ˆaÇê ï/f•zŠç]æþó1´ ­òŽÖ“&öP·MøuSXv›Ut™o«QkȸÃO¦²ð9I‰/¶˜µ„ò Ðåt´4‰lž8‰›i’œ.¢ƒFÞoE$p8£Ðë‘7ƒÍ´”ÌjÃß?%få™"GÙ9‰ëGû¤åä¢9òõù›L:øtx¹’ü`Q¬)ë 9‹=&ÕÈ[!ók£÷–'ÚÙ—§zî„ο ^eH—J †“KËy&>í 2L{c? -c}º â||ÚÙàfâ»Þß–OsßVz4Y>½Ï-G¾EBžh_î¢f¯…7çÚ›Š%¦[ãaùé’r ®8GFä:æìá]¼ôƒT“é êMžbæíâä[4p÷VÚ¢APV“óŸ¼Lúw…I…Ž|CÕK¯ŠŒLó2k³á"CÄ)!Šƒ`á²$òœ8ó’“MK^mºÕLú§Z®ŸÉÛ]F&’ÃIdüÝ”1õ^ÿê¹IÕãtž+»ª‹jà°ë̵ný×£„ç.JX½ÙtMj+ýÖvjIæq¤ÏâréŽZ6.9<½nÔ¥ëE·lÐ4ãL$¯ôF½¬]¨ÖáVñéÕ \Uˆ*3 é™÷1ìÉ|õúuðn¯J‘)ëB³QkKÀæÈäc—†àj“L'ÄŒquÏ£ÀÙo+,C±nˆL$QÒ|ÞUJ›tâe4YËæ,}šØëCýå½Å$ÉIç\÷¥Hªš‹WBºÊ„~\¥¼0öl§NüÚué%uX—ëà/ /O]ÕÁú_X)lb@¦=mÍrÊʾ­Ð¥¼ô^ºéó-G°H>?Ïé7'Úõ”i“#C"_ ‘ô>¦Ì„1|~åh¬!a-[?°F—›e¹Ž‰W5 ¦ß2Š¥,=ˆ§[£ay¸¾T€­éàe>ø†j·òsm˜6Îá!a>é ³ÇL˜ÇÑ¥’¸I¶XŠîª"T-’ ^M[x=ÁìèWUv? ^Mâreþ;õp)ñj§#ÛqÞá2Ë)A9ÎØ£"„¬ž¤K £°Â?éà#áå%ä_ü÷ÂÅ6l`b³‚Ðp‚À¾±v¥féêÂ#Õq±–f”³­ýÄë ³Œ"¶>æ¼)²Ç2`LÇ’O·æ‹Š>µF?™øB|TºÖµ|µ±·xŠZ]›UHÍ4ØÜFÛ§ì`EuQ‰´ ©vô¦£I¨ƒß^^¹‹6Xâ²RñÊ„X‰_q$,3t%š‚:š¹$Õ¬YqÂPȬ±¾ àÌl]Ù虲Òu®-—&.z†¦ß«¤] -õø$ ŸD,Q9­ÂÃàzlø`­šºä8§¼LG¹Râ—µáõ” åHÛì¤^M¶C=ñU¡ƒ¿8¼<¿<o £cY¼ŸãmÑïÂ5'KÓx¦™j‡üj§áœë™7ä35ü­œ”váûºl†–=’ófMM[§[SžHd¨Û•¸j“_§ëM³5˜yø“CÃ`Ù® ÿøþÜç ®ü#ÂýT<(KømòNV /VtpÞÍqžÞ͉w2_c)L[ÍÐÜÝöùŒ¹-Ÿ¨X{ý‹5Ÿ¢Ý‹(htY¦%ºì-Ï|Ü(6µÓ £ua×λKÿ6^¿7›6~¾A˜Êl7£øt>Õ0kéŽøò_íÝHåemCVT{8y«NÖU4 ö<•][Á¶êà%'ëå~T`îY²aô;WÅɲu<p–%ãG\“fºï7NuEªcº~¾o²*j+Ÿ÷bšŸRJ·¼vH{Ë.íî-KMâ²³˜‚ÃZL ±Á¢¼²ÖÙä0±¶×ošŸQSÞ¼¦Íç¬ùo+ÌY‹6¨Á×+7@Vk“ŠÂ/ƒGV¡QÛ<·™8¥%f¶@1—*ÒKÞ çÄq;§øœ8½TðIÖZ£$Ú‚4J¢um*žóR³ðQQ8d:îŽD4Kw¶*Åfת&ZÛʇµéNÑáR5ÉŒ_kDÛÎûÐ癹´è1ã>6­‰g?˜tîÞ5¼ôƒ?µŒEn±v†¿CHDæ…­W’§mãӕØҰ¶*+û¶2š!ü®ñì³Ìï™Ì×N:›gúT”®¸\$\ ®€Ñ´-,ݱš ÍCTEUfzó/.ñ+?˜õãõ D%8l$ÁÝèS»$’¾-¼¼”üàzß§(ÎS€Ü…1îuX­•¢b4’Êx¨WYÖ¢AðZëá“¢3•f+yd“îl®OýP«TVÏ®¥Äti²M–ÂX‚‡²‚q›«H%•Ù¡Öƒ‚œ³,uÍ=‹K¯,—ñuâÕl _¿vÒ'%ëT;æ¼ñr™î˜[Mò½4æÖôè×±G\ÇÂ)*çTK‹%Ëžë®^úÁƒ’Pà¥ãŠ–ÈbaÀ×.ºÓœacMp¶…ÜåEÌr.ç>œKC¨Tqb´!nU£jÒǯl"üY÷Wxƒ¦ì@™`íÏÝWâ]Éo¹ø¨{ã9‘¦€¶ÞÓ=aæSů$%ü…áå±6IzÓ2½è±‚>¤c#´lêò1fùØúæ X?_t2º-TÚÀƒSšõŽôjx6sñòìa=̨°‘„aæÛ„š¼™šý|M¡fÖgï†ï[¥}6ºŠÞ&‰Ë±ƒÞŸN}h†&ÿ›?@½@TË=Ï]`¿Íõps—octðÛÃËÓ/æd1ž÷[õð~ªö‰êÊ?li#önòyKþn ÉQZq©KsÛö^a³€y„䨋(NÂsìA—ú¦‘zßq#þÀWÆXå…§gà:¯]EA”¬ gÖÞŒî<[V²7/øxÖH\àåÕqû¸ˆzƒHÖ©Âf”ê묷!‘¾]:øáåE¤ƒýêCÝתLXn^N ù`ùt­Ù‹‘ÔÚĽÖc‰;)“º7) ¿X“‰´‹wA—Ó"VÛͺÊK¡óס>(1ðh&jä‰.Ø6†)•ñ–Dö׸+ŒJ“èÒ–_ovø®ûùZŒ¯N#—›¸y9kÄp¬¡H¿5¼<‡=7ÉÙ@ãùj6yZW^3|çЄçå]÷*žùdPe`ý#Ë7`âÍÙB÷^dÕõ©:%`3*Ķ Œ‚s9Y’µÝS:q¡êë7¦SÍYm¯ßׯ0D-\ŽvöõD,Ç ·&ƒò†t%Ÿ?MöêàûÂË5¤ƒKº—/Zžàµx¦›]}å=¤6Ÿ£hwvèê^¿Ðû0ÌÌZYtðnH-\Ê‚…4Î7Y5#¢QÔ9w19MÍ)é:…pmaEœºä²õæ ŽgWÛ¸5êªùÁü–ðòÜÂX´$éiºKaªÎQÎWL<2Tcwû¢.GRtžn5ù”G¦´ø8SEË?.Njv4=AÉZ™éšrR½uV°QjZyð%ËeÊõa“4õ²…ù6»dò¤«‰ É“‰ Ç7_.í@ÉèàemÒÜÃÓñ\ Y{Q{ŽMfí-ÂEtتAœxaUl>piêdª N7X²2nÌF]ô¤¦.}þ…”½!»QçÆRy;’œ¯—4K!ýço–ë[ëÁhCvÔF5Z©#LYÊÎE—Ëò†»{.éà#áå%±6©xŠÂÓã®óUcRÍåT¥Àý°g÷d+ÓæiN²¬è°ûNf>Ä‘<ôDþœÔ•a¦^Ÿ®™0­a­áËThcìxÅï«ÊÙuWåTä«ÛíÆ‰åˆ¼áÖhèPK›!*LGz¢‚X/ýàEVÕ8§ä*/Rƒ…."K¨ÓÉš¾T–MPã¬볯Ì6ø3Šþ/²2îjº@ܧVY]N)õêmÍŸx ¯M,hšFX2¡Ž‡¶õÍÖlbkºw ««5]K,¥–²VÖ¶Ž2 ë´$’¾¢ƒM'.ÔD!£Á‘ó\¤‘ÊÎoÒë?]+†Ê|qC«ÅÉ¿Ænkõh%º „Ì.]âm>ÇÈÖ6Ž õ·ºjœ’Ï(zø.9a&¶‡®5ûDŽL7ÉÓ¥Õ‹@WxjìÇQØ&Û`®:{ßáß^>µ+ÍžN”—èÓCƒÅ¡É`Cçq±ßæ}ŠõôÐh¼5˜¬m¶ð>iú¹µáŸõó ä߉¹óVæiÔé{b›-†ŽÄ¯­Â.@•ï&8Î>YWnm×.kh´Ÿ¿XÓ1ñýñ¢Ò¹81]kàÒ^„ÓäYmÈï²2›ÙBoÍfåì]S7\OØÆ÷G3—˼SÅO©@”ëàÝÚ¤_Ö*%² wíM;›‰Ò-XÉ _ÎeùHYàN&ϧ}ûÒûd8JËà½N-¾ó4@î!ñ4À<Óž_r…, Чœò®–Éó‹§ÉYkeá£ÜN¦wѾTä@:ŸÆlJÖÚY#½Q°¢ƒ»zUZr‚ãx,¸¥³±ljYEö(õKïs*ýšª™Zt;£‰Oõeå0‡A+j¸^GʸÌ'T/ð<½ÂÛ¬d…[’¼k­•¼ÃJå¿EnöÐÇö4Øn¢ÖvÛS F¤:TL4zæ§Kß^nXé“¥ŽÍI7µÉ1Yï*ËËá6uñ¹L•E”Zëh"Ït¸Îvo¥ƒÏ‘j³@ÍÅ,šÒÂÆÔY§Ñ1,*õÓûB²â8C¨{ºÕ°^=ë+ZCé›LÑñ¤‹dÒöX ÒòÒŠœe5àhh Ü!g„õ~~îòƒ…ÙÊõŸíƒoª^$÷`VÜKÑaBÆ€»µ7LUgÖóórÞ¦#KÍc„­ì.KÙîxk:b#lÂÍÛmDMF>=êÚ¥z+»Âiæîù›Æ1öKà™i(Ë“d™ &§¦¡H–*Yw]:øÍáå…Å}²LÁjAÒÓdXñŸÏ&m“³·VRÒÊ)—~ÁB95¬uÃH§ k ‡M·æ¼‰iÚ‚ÓkIÙ¼>űŒÎñ4›WÉ·Ì׿Ô4m¥1þYOˆuîÓ§³n¢~ÖDÆFµ »A­³eK”ðTøºL2 Y$= ^/k“ntîa£ÐÉâE³õäÔ=BvÓž”½ËE0Õ¼ -«ÖS›°v„µ­Òsí±}f>2º®Ôrêûl+ú|›¬K¼7e&‘†•¨`ÿ2ê.=H6ËèÞsÝ4óÑÌå|KÇ ‰)˦žSð§[V|"·åÁ™½:˜üàe…\:°úæ…´ªžu:º¨[CVBq“šñ¬;bÆ$²5'÷•|þc裑uj‘Ý#¹ÝÖe-É…d`b9œJ`M¹õCSÂÎ'–V#“Yî‡ÔŽ)ô$NÆ.‰)¡§é±RÆúb¸?ø¦™{ ßYÆØª‘\«Ìyù\V]ª]º]uW‚0)àYkºY‚L‘UÖ?FZgÜ,UâÖv_D×$uîRÌ«¦vè(vuë£õ¥`ºju#…ÌÚõ»f@]ÜäÔ£›]I¿1¼¼(öè°i9çxžÞˆ-?ZKh דM™Y7XVÂ~ Qè"`¦l… ºÉ2i}RHeÔYÔ>Î5)¶b:coËüÓ(îmi!dÉ:Ú ·Â“MóñËI“á"¬-mô!O‘Ê~¾FÊð±ˆiäʯËÌi³5žÙV2{7áÙˆtðÒ>¼¹D~ïˬ³çý$LË“d Šüºuä 0ºþ®Zó0[ƒùfNéi/bÒ‘ê 1œTÊ‚IRpÓžÏ9ÌÒèÂM¸öi ‡Û·Å”[SÃ1önÌän6š]WîH½¶Ï¯=ÿ6 XÆ Éën2ÕŸLÔ³¾Ù ü6Uü[Ÿ¤¹w+ùáÂGÉô“ SÂZJ|÷x™ÍÞô)†¬S–êtÙìYV/–e4r½!szùˆ›M¦fƒ]Üü³¤J‹;¡‰OÊŒˆõsµÓ–q¡æÚÚ?Lº»Ü]¬ƒo /ÃÕ>Y2™«ß©2IÐt”0ã6öiUSYÙÚg±­Õ%ÌW›÷n*ñs'‘KÅQW:“6ØÌçàzÛcÃ| “·•Ä^¡/œ‹îsAõ¶‘Ù:TÓÛ»û½C¿.¼ Šûd“Ÿî´eÛ°äü¹6µ¯ßõ®È´q ¨–ÜÍ”;rSï¬ê8ÎZÛKu>½:«+Ū¼®ÕA Ýguà RÖúÔ‰´èrtŸ€Lͪâ‘K{icù»€Ë%K#wu{ˆ.¤>¬ÔÁ†—«Wý`›‹Xº•eFæ¬ÇŸº»9š£:mÔž:Q9L©öêUzÌÀÖ”7K¬í±$dL3™žtË»ÁÄ%dfFˆ²M†ƒì̃0Jƒ{H nÿÆ%H&¥è#Ëú” ¦Å‘Liuº•Öèà|$¼¼„tð&ÎϨÏåÊÑ„¸nKR2ÿ´'?õ!`Ô&ÄmÒ«ÕW Uh’ÁîÃÒ&úüBùI·Ên6µ“Í™­ÅR°õn®ÈKVóñJ<˜[0%LÃtvsÞ.¼a”¶'ñ>˜»tVÓTnŽaÏÉè6 RÊL4ÜhØâ¬¾P»¸*yk¨ï’–%¯èàÏ /¯Ò]Ø#üY¶ØеQÊz×郗ªNyBæ½>me Ê»ŸVuL9éÓ^DX†¨÷ùL|ƒw¼ªE=ÉÀÌçO›C“ÅÖÀ§óŸG™5[³ÁÚ’˜·F v†ðnœI%w3Ù™gãp´®ól™? – Âef˜»‘˜>Üq~0ËêÏoºÂöi¬Ý‘_Füa=\¶ˆ¹sTC¾Œ[³¡Õ³ó½.MÎ}[¡Wl[w¥¡dC¸u!±µâˉ2Ý)ìçky”øþÂÇcV?ÀsÂ$І^lùþv2ý<]‹`Ç÷›©O-ƒÉsmØZÄü.(ìêÁzw9]b?D²À»tð;©Qe)/šuÆÒêY«\oWÏÀ£1!1wÖ­¦jÉ)Û™#™W·×l9›-âV§µ¥rÑlñ,ÃÓ4åbÓ­íÓ˜¬uKâ×§i–ÖÀÒy„ú)u)Õ.ÑŸìA]<—θ¸Ê£8`ÃÓ΢þÛd©j&f6vÄ´;Rö„^ö‹þÜкÀ¸ bü|6•$\OàTØ(Û¥ãµT^+f—ʆ­¬9b’£¦o¬`µ~¾-ðfÎÒ¹ºqlV{öîç-:ßMç³_ïYÞwér¼(ß³À¸…‚wÔ-3c—ú%µý©Ë­zK‚\®ÚÙØ(û¶|¤Ø”R{nš&zÏGÕÆÒ×U"Q,-`Da4cŒÀJGî67ºmžÆ¥²Þ×m¶ÍadFåxm¹¤ ö‰ZuºóáQÖ¸ÍÑä;Co2‘ÊÛs—j¶T-ؘ£ÕÔ1¸Í¢Ct¤vEÒÁŸ^^A±èN7zÆþaÉz)*ú|{CX+iB+¬8}Î Ù^_ïÅÚ*ƒ¡Oo*™;Ü£òÍ¥ ä:qAûJš?vÌM8¯D›M^¨Ø–aÙÔíbàS|¤§KÈÚ:o2[ †Žºó™N>¿å “E<¢cJ‹uðî¹IåÅú¸’,¦ßŸ2¿l¾Ö8 …}B:6~J!9[rѱa„éÌ‘4õ˜` ÿ¢B/hg/PÕŸ®•%Â…Kßõ –‚¤ŸR™+jtÞœ9½—Š­sEM‰öüo¾^ÞŽ»¤ …¹³õ3)g©÷(.ÖÁ²>YÝÔC¹ªÕWå’ºÙ570×p"¼¨¼Ñt&ÔÂᘠ׻‘ÒÉÖï¦]ŽóJ À’)²½Œ œp™B2ÆcÊÚí«ÙûºÍkª( …'r§Y¦uÒl=öãÂC½ZÊG”Û©(ýÖŒîÑÁË|ð«ÔÑË#•Ä›ðáBÉ’ }„ÏÁˆÂ™pd'9—Ú>Ÿoqhªàr;ÓF+ǤNOo  Ò‰ÊEfÁÈÜ–._©Ë÷!7”º»(3+õÓæÇ+b×MZ´¼ðPJ¨œu—,æ›é—mÒ²-™tðÃË‹È^YâÍ€-vßhúÀÒ›BÄŸ3N"­cXðŠÆ†•ÊÂü˜ÞÆÐWŠÂ †J° ðœÊ›LKHéÿ=+a¼`ËN*¯4C…£hß«µêØ ¡ºyµ]åÈ¥KVÒ$?PE¤ƒ—~ðç•Àë}“S6Ê…ç*,0r4Yjç„Ô¯QO€fä Q“:’t.“jn¶æän&<>?4P3ª…—tÈæ¢3ߦ£¯d—*{o¥‘]n÷‡OnMÆkK5¾?óIձʤ½ˆÉZ4«½ˆ…Ë‘…y/#û”%UâŒÈ¾ý¬ çUÊ;ºé‘­µƒ‹ïO>-MõLsË6¡3_ÑÁovn2˜Ñ#É9†`îhkÄ;ñ\Ä:-˜µÈH|îÓ$A½Gt}§9£&+[çc—‚e–.¿,¸G1ÙNË{ÕœRµâ—œÀ‘ Ëe¾äÄY¹3å…Þò‘ ;ÚVš^ªN¥4–ëùqüJ°ò׊ö  A6Œæ¾®Õ¡ ¤ƒï /û)M»–mõÌx¼Œ+¤vhe„Wf½UKõ´^«–\Úói+«Z2Yó‚ê.ãö-Œ}Ö-&vÈ+ŠBò§dÛ8è6g>í Õîž®½z6ÃO°•ß[5`‘ ýxKÇ|X‹ßx…ß&3áדIVúu7í’yö£nÏ~>š”éà¥üÕÛ$¸/xÎü°ºu iK¤HÆ«2ñš9[^`0D²"ýTñùfLÞ\eC9Æõ£v&ê«dWÓ„wÉ »A—­²`xívwÁDPÆ­/ÛV•ÏçÁ¼ =õ·­ž›¤ÊÂ|¬ž[×IeËÿQð²,]:ÆÄýé0ý>’¬D«i2ìI9}bóOÀª¨ ªs[<›¶·Œ!ñ.ë›,Š„æ?KõädiÙ”¥ádKªc¼Ò#õŸ¯8àÁB\ÍÌV¹>^ž·Ú£Cèƒw‡hgKå…Á”‘§ÄÓÈ“Ìf. he3mF{йc^;¥Ó®»¡ÈªOe 1BgíVRÓµ5ä^ðÕHz!K[œQǪMµÅaHT(-‚KïÒôñÞæ—º{ujB•a7Pú=œ˜™žê—ÓÁGÃË•óƒ )YŸØŸJb ɸR}ùÿ‘‡j©LØdý‰úäKç ¡¡”L™®méTÌ ¹ “õf¹‰£îBdcqÖ%acTÚÏ÷݈ñÛk·9b$‹-õ°ö¦3mZ×ÿ×›[Òd½Ìý÷po'ú')ÒÁ÷…—kV{UÂÀ¢ãôAë9ôÝ!—µÁÚ0ØŒfTQ² ‹Á*2¤²‹{ê襷¦RÂB§¡{›Ö&òú$ûÔåÄ !ãÓÀ7¬Ìܰœ~*‰Ý‚æš2*sc<-8ÛǵIFû¸ìVuBFYÑÁ÷„—g­ê`-b³eƒsÅ©„Æpӆݽ6^´Ú)d…ZëŠomác7Äò8}úºsšº'±'6òéµm”¡…&Ue.¢!bU­Ì¹98ºT¯Éâ’ÚŠÐMêëÖàB@2ûK«{tðn}°Ó#«šÉúÁÍ¡IshD†S”7«§¸o+Î>b¾Ó­Acë퟽YĶÝX6² *1žÐZÿüñ|k²`?߸¶ íGSø¡™°O[¿vŽâg«ÌþÍ×e¾MgÛkwJ­ºõ°®GÃŽZÒðþdXé m×ÍŸKì”z»KÙ»XcJ+*äåRzœ·ò;ßåŒãv½ŽÖza¶ëu4ÚHÇû¬øœÎÌ R›xf %~Y gÕ/a=Kb:÷éå©Õx*! UD©Ý±@h¸dúô«HZmb SÎ99j*žJέ0)…T-‘ »´íš€l]®zӮ˱‡OfDyÐ3NA0~ñƙ蹈àOÉtðñðòq¤ƒ«óÌYæzd®»+31A=Z™ "¿í–Kõ®)iÈ;y‚¤dœÒn%4uªÈ–$rB¶°$Q’9‹B¶ƒlH·ìY”¹lzo)_ÁÃë­ÂVc6çÚœ )²YøÆ4B-Ü•ZØÇ0“ŪY ºˆOhظŠ<À»|[¿þâðòü•|°ÚéÑù|ô³ºvÓåT, ™¥Èšó}³ ²Kã+è5ˆ­ë„Àò°ÅóJ7—!¤% f Š`Hg z6ï`‡ÅÚoÒÖH°$lA&ʼn¾UMw‘ «njOÏ/J› ­ »CY|Gx¹­vÎÙ´ӭ1Ÿo6ˆÑ©dM›r¦3î@ ¦ŸV~Ï·ƒ×›ª{W¾2ÒYa3}ܼÍuºL­ð\eµ±i—Æü(ŠíJD<º¤´(ífÈÐšê³øŒ—€ÞYOš»©ôÈä“8Ûj¸Ö+–XX¿V/ÐÁË~Ño³³.&7pÀê0Ù^¿Ô§z~gÉh²(ømlXMåíú$öQt̺ãh“¡‹ÐP«~9Û£˜ûlX ƨŠ{$f{ ÓÙuÃõzvš”õjRÍl“A¦\Àn Ò²†du?_Bƒ E¼¢ƒïí M›ê‹›±§ÆÇá’ÆªE– "4zGƒ£qêr“jbð\Ñ0\(¬Â{=±ÈJŽš\haîD%2Ä%¥Øu“þ’Úš˜iê΀O>üËʹ5OÃ)Üâìb˜Œf{)YÅÍkyR²EîYÒÁ§ÃË•‹V„®6»òêµXŠ~Ê;5Xʳd¡í>RÖl‹Æ³rW½jÛ(ÊŠÙ Ì[%3¹êÜ™Ö5ï€Ù3øü2>î°‡Ÿ?óqX„¤à»Å% q¥mœ™qÖ`æË¸k§¥ ‰¡=¼ƒ»:ø–ðò2ÒÁ浟1gÇ‹JÜ _5lõ²óÁl!ÊÒþä6šYi7²ºÞ!ÓÁÊT—…cÉS‹L0Yõ_Ÿ€[°©ÕOɆW3´¯v-4ôLaŽ^И5ÁêRg.&¬*­›ð–g”ÍO¡~0¼\½Ú'«¶Ëi(Jõ”U¸õØ‘>ŦÂÄä%PJR2¥1¦mm×·Y[¤B 7PÇöœbÎê]–pï©a õGÏx°ÓæŠÓ)„C†b®ÊÅbêåž`™ c; N 5¹ÙjÂGm ÖË·âûã‘K7”ü\šL Ùþyå~°- Æ>”¯Ëß“äÔ4–éPpfk©Úðþbk>š–¹ä™˜Ë#LóŸ!f¯_sèµ~»‡á¼v³ZÏýnL¹Mön,+YÑl•¥h´uBß¹t)äu Q8¨¶Êb°•7#[7^ön 5:+ølÏÆØ·¯g;êàŽ³ Ö?Ë…n‚AýêNˆè|Š‹žm>ñ©87Ù0ÍÜé&ÕbÄð\N6-ú¶ÂܼÁ"yŽl¸ÎjÿÕ}öÆRÅŸí笊V¯‡­Ú ês®¤P7kGë´Ñx¸÷ cˆ]‰<]ºîJd脼±álº«çÒ»êàûé¥ðìB¾`+Y¯kTëøþÐ…Ìg‡“níÞ5ÉKPŒM'¤­»&ŸÚÀÈăö) G'¿äq|˜õù çýͤîÙo+KÝ‹&ØW6ú~~>oQ?PñÛDžºÉÂɧšõ.SHiwqÔzWÇ`”ÉÛõ@wú>§Ž Œ^І÷¨¿ÙüàÅ:xY›tªÊ¡Í5ýgõ·é6¾VÌèJ>¹IíLÒª=9Ÿó6æ"#6H7EÊ<ÿØîè­‚]ˆ…_àM1øHŠÆ3¦®`mU_Krg´Aך6'/Ç¥«!A%!¹ë:¢„)9^°ÝÔd2u¸Ç'L:øKÂËSÉÞ+cg>‘·|I".Ç;¹GV¸ª?«¯"["ë§"¤ˆŸÚ^D_ØÑ£œØØÂ…ϨªµÔó0æŸË ˆóŒ2w¯Sk7éÒ`Æ™´ÒQömE6!_EÜÊ“ywépe¹eâ¨=ÄêmP^ÓÁŸ^^Ut~°}æ„¡±ùZ+œVû´ÂQçv»³Ü·qÂøÏ¯ë˜KcúŽŸïëå©Ã$êù¯ê2ID…A× ôpQ[Ýë˜-¦ê>½-­·‰DìUukGžæp?cc<ð9†V¦¬Ö[§µ '§•7„Ø, °.¯È"°4çüV6G Ö€¬ðF¹ß²؞R©áÂ¥ ÒÒf» ê¶ü|;çÐ…°Þ¬›D=ëYQe:ø­áå9«±hÓ[‚X´-[سQBaÌq€}â+µûDÈj,‡ÝËmìb…Jµ²¾×Ae¥d è(ÉTÎA6ýç[^fò®Î8\ç¤ÃꚌ·†Mùçï™ÿQÜÿAöN½ÔœöÛTnh–l&ø6Ç£ øoó/Ô¥u±¾/úl º®0ŒÞ¿¹ö¦ŸìâïÕÁ²>Yy–†à¡ZKsÄ›QÆ á…ÍgÓáG†÷Ëi?YNs“"roJÛ$¦ö!ùW¢¹ËçlØÇ_†éul¹óV¨e%H·“~¥Éd¼úó¶£½ç±Í6’„ÅlÏ—çi˜>?ì퀌Qn öv¡¾+¼ì¿ØÖ[Qþ,#^ñœ)­<ÕIïœ/xÂ…ÉQ™ô[tEÈõiõ—Ò¹ÛgY²‚àÛÊT¿"ŸjÓBçÙgaç‘lXÁ¾±õ~×1çÙg?±å!†ùõçÕ3´ek·®i”§î\>fwnµÊtð²GÇ—ùÄ„dÍûl^†g/LïGiaaÑÆåûI꺜mÒ®Ïo,/Òê.-ù-Â$x³¬²t:&³ž8ËÜ£6S2‰Îqç•Xùú³íx)Ç&ÕÞŠªh0H\ʸ=o}s2Ù­#”¦óWtð{ÔY¢,-ÆÿéËÂòónÏoи$>³üIÁ-ÖxhíAÉ¢çˬgÙìuÝ]j< -–½2§™ÏŸrgZ"U26‹V?éƒjs¥š†fùRz~ó–5#·£\iU3B&ãx¢8ô×5+£÷þÔĽ¬n_åEo2T £Jo6†¬ )šØÈžUÓEl&K‡'¦Ž!{—Ì.x¶»¢>¡î”´ÝxR2úÎ]Ÿ–ÄÜ…£`0.EÝ ¬´¾A¼IŸÓéE3Ý :8ÇdŽ\¢YN°{TïS5 54Ü‘Ùešî¢H¿¼mk÷*í`ñÒ* ‚«Lß^®#ìý2µ7Ÿ-œ¸³žÍ3ðp¡,F»¬C&ï‡FG¿'ìÓ•\À°uNõlôí`dñP[8ªôpCÀ@ıq;øÄÖ&“ R:÷¢»Ða0õQ¢|W3è(õŠár&—!hêÕ4ÞdÏLûT™Ëê:gšTïÔÃä”8õ/B >úbcñ"|z©ot¯ Mñ®ØoB›)³MŠtð}áåš’>Yµ–”,jS±¥éÚº9¡€ðä¦tÛ¯}…Z.,,ƒ¡ØûBºOÓaãÅXƒÏ‘Ò­ʶñg&¢…"ÎŽ‰gYÜbÚÀn ¹ÒS \stD]énid7èÞ2Ö¨ÁŸ×sõ¤Ý€WtðÝáåÙ%g6xÔ+šHÿš|Á%ææH¼|M¦0ºHL¤#}‚ÓÂ#Î{nr>(ït_¦ÊÒñ†‚Då¡$e Ðñd#÷À¸¡¢Fso›,B×ß›ŠŠf8Õ9ì)ž/ak #èZa«,=cÛömþM†Ë}Ÿ^ò¢ÏøØÌ² ˜0ÜØ§U6ÕÔÉfNÉ̵®Fiésé6'ÍÑè}h#-ªˆëÁØVÃCãñ¡Q¸¯†¦Ü\޶¦ë ÊøþÜ¥7#ç2·,äµÙŠL§É&q´–$ ïO·&SVÏh—„ŒDfÜ@£éð½üõ{÷Zl BâiÖ¢É?Q8•s¸ÛµµÀk7èØ£Y.Ï?Ë-ð¤ƒ_^äó ^º¡¦kj%¾ß8e,8ëζ«ù\tz²kįöÉ.<(ËLÅ»YJ+²±{‡Í"{d‹nmÖ¸L)C·ñ–v65+»yz¢U·éåg¹·«×¤*SÎf}XÂ^>•gÞNÆ®‰»€›ãÚÊ'a8»IP ĹxIÜô¨©‰>¹¦ÔÁ]ù`[1n¹²ÆJyj‹·>c}|£‰À…ŸÏVMYM£p/|Ù 0+ùVMµgKë¸H¯_fpöøúÍÜÇà¬mUë'±F“žönf]w3ÚZøÿ§¶ªUÝúòÕêÞœ§¸²*œ? žo¸élÚ N¦Ýœ5~{xyúj,Z¦§Ö{a´~êÌ£pPöÄt\ZIYÒb²Mo\tÎæÒ”vGŸ2HSÃëIcîToÔ¶6Ì#¶ €ZN먕sêÀžWîÏ]ë\ÒD–õnlÄþÕKâpÎaØ,²îÙ’ê`¡[/n§.}û™s,ÜýcpÑý`‡Ì^›Ù[Öæ­Y •“êBF¥^O×½~ «¥¼Ñ¡…{¬)_•?×+jZá3sé—eˆTÊ”•‰q3mØ"“µn‹í$ºt”R1ëQW¬^¹Œ,Qvý]:øsÃË'¯ê`©œÇi³ºN2‡z2îöê]*¡,éJYEªÍ-mø°É\º”ÜÇT÷9"\H­–a}ð¼hV:°D.úFTŽêS˜ 7`ŒYNç ·¬¢˜Û[Û¦uÙ#Ý\Ž@‘^„þ‘U®p3,‰þh\/냿Þ#,_GÄÍuJL“Ý6öö/ØÚjk¯¸ûº%”¨(Qª½álc‰¶(U³*ÔýÓjº§B>~²ñÉOÕ®ú‘E7ûÌõ…‹¹.¬¬hz ­…Kße!‡T¿+ouCf_)b«Åzî&éàáå™í¹IÂ8›!J.:6­2¯€mu–Ê1z–øx§¦Ô}û²ù…Kpý<ÉVt\D—­… >nÚÕ…í¥G·èïf“ P?wºwmfRåú\a&cÖm Pµô¥¬¡ØmŒ¼‘ª],Yd´/Ýaˆiuêà+ìÛ÷䣫µI65Ë2‘ ©^Q.¡š-3² ƒ¡ƒ¡ƒ¡ƒ¡ƒ¡ƒ½tðã ûö}uÐÅ_Æ{ÃøÚ0¾.Œ¯ãÂx_ÿW%Œo ã›Âøæ0¾%Œo ãÛÂøö0þ?aüÕ0¾#Œ¿Æ#a|g=Œï ã»ÃøaüßaüÍ0Άñ=a¼?Œï ãûÂøþ0þV;ŒãÃø;a| Œ ãï†ñÃaüH?Æ…ñ÷Âøñ0~"ŒŸ ãÑ0Î…±ÆNçÃøûaüƒ0þa ã…q!ŒŸ ã§Ãø™0þqÿ$ŒŸ ãçÂøP?Æ? ãÂøÅ0þYÿ<ŒÆ/…ñá0~9Œ_ ãWÃøµ0þeÿ*ŒÆ¿ ã߆ñëaü»0~#Œß ã߇ñX¿ÆãÿÆGÂøaüv¿Æï†ñ{aü§0þsÿ%ŒÿÆ ã÷Ãøƒ0>Æã„ñÿ ãÃøŸaüQÿ+Œ?ãOÂøÓ0þwÿOÿ'Œÿ7Œð؃þ ãÊ0® ãê0žÆ“ÃxJׄqmO ãia\ÆÓÃxFÏ ãYa<;Œç„ñqa<7Œç…ñü0®ãa||ŸÆ ÃøÄ0>)Œ…±?Œa¼8Œ—„ñÉaÜƧ„ñÒ0†ñ²0^Æ+ÂøÔ0^Æ«ÂØ ãÕa †1 cÆ$Œi³0æa4a,Â8Ƨ…ñéa¼&Œ¿Æg„ñ™aÜxÅ>€K‚«ÞGã|ùSƒÄ ïÜyç­7?pω/#ÁDÒéÀ¾öý—…ñ,úýÉû¼ïÄí§ï9qúÄ=·p¯£ßü“‚?_{ß©»ß±üýáçg„q,Œ3ûZõK*Ô©‚ýI$ ãLgøÆcaŒcaœ ãlÂx,ŒÇÃØ]ø}ÇÂ8ÆÙ0.„ñX‡±ÿéá÷a ãLgøÆca<Æþg„߇q,Œ3aœ ãB…ñxûŸ~Ʊ0΄q6Œ a<Æãaì+ã3Â8Æ™0Άq!ŒÇÂx<ŒýÏ¿ãXgÂ8Æ…0 ãñ0ö?'ü>Œcaœ ãlÂx,ŒÇÃØÿqá÷a ãLgøÆca<Æþç†ß‡q,Œ3aœ ãB…ñxûŸ~Ʊ0΄q6Œ a<Æãaì~ø}ÇÂ8ÆÙ0.„ñX‡±ÿúðû0Ž…q&Œ³a\ã±0cÿ ÂïÃ8Æ™0Άq!ŒÇÂx<Œý~Ʊ0΄q6Œ a<Æãaìÿ„ðû0Ž…q&Œ³a\ã±0cÿ ÃïÃ8Æ™0Άq!ŒÇÂx<ŒýŸ~Ʊ0΄q6Œ a<Æãaìÿ¤ðû0Ž…q&Œ³a\ã±0cÿ‹ÂïÃ8Æ™0Άq!ŒÇÂx<ŒýaƒFÇÂ8ÆÙ0.„ñX‡±?H‘ÏãXgÂ8Æ…0 ãñ0ö¿8ü>Œcaœ ãlÂx,ŒÇÃØÿ’ðû0Ž…q&Œ³a\ã±0cÿ'‡ß‡q,Œ3aœ ãB…ñxûo¿ãXgÂ8Æ…0 ãñ0öJø}ÇÂ8ÆÙ0.„ñX‡±ÿ¥á÷a ãLgøÆca<Æþƒá÷a ãLgøÆca<Æ3_Ö#éç•$‡Ãr(þ… Oö=%Iݰ÷÷…ý½/ìá}aŸî {q_Øoûž™¤mØûÂúßÖø¾°Ž÷…µº/¬Ç}aÍí{Iã0ÂúØÖÀ¾ðœ÷…g¹ïEûZL’=Ìí¾0ûÂí ó°/Üë¾p?û&‰ÿò0^Ƨ†ñÊ0^ÆVtÔÀ Œa£0ÆaL ŠÑ,ŒyM‹0(Üúia|z¯ ã/$eð™44!aܵL>Æëà–oãæ0>;ŒÏ ã–0n ƒŽÚ½=Œ; :1µ˜º3Œ£aÜÆÃxSŸÆ›Ãøü0¾ Œ/ ã‹Âøâ¤ˆÞÆñ0Þ[tO”–}[T*to'Ãø’0Þµ“¼?ŒÂ8ƃa¼3ŒÓa<ѪÞÆ»ÃøÒ0¾,Œ÷„ñåaüÅ0þR_ÆWîk•àW…ñ—÷þ¸ÿ¡‡NÜwìá=|ì¡ûßwßÖý½úõ·ÜøYÇnºõu¯~øø[ï;±E›Ðôƒ€Ùd~ާÓÙ0þ‡ñ}Ât0Ý7ͦÃé|¶o0ŒÆƒ}N÷˜Å»zøøéö=ôÖ{?üÀñ·÷ý½îñmoÛÄ%ÇkÈ®ß× á«zÏ$ä(®AŠƒ”Ë•i’xÒkÓŽ¸bßZéc‚ýÏñ‡mßÁìÿÉt6_Ýÿáïcÿoíº¿–”ò“ŽÒãÞ×s\ø´¶ŸzÇ}ÇO>~óŠ{^ÿçu'º{ßúÒ'£æiñ/9q÷©Ó÷¤O!»ê©í;+ÿð í?º&ýüø;ï>~ßñÓ7ºï]÷?@ïÓ_{êÔ}Ò_xÊwÜqË͇ÉdúÔ£§ßuâÀÉ·xÛ}Çß~àÞãxë‰8þàƒ÷uÿýáªD×û4·ëí½ä{N½+|Üî%_sómGyãd‘¾âèÉûÃô?ðð‰Óï>~ß·:}àKï=y÷½¾÷äCí3 ?ïNÊîæš•»ù«Ýws{çÝÐo®ûÜwàáwÝ×'¦@n™ý×Þ|GJÅ,o¹M¿ìÞ6ÙùO¿ñôéãïIˆ õ«öÌO홬þ »ù‡ìY®OºåðÓl}"=á1UuàUNŸx÷ɇÂ¥7Dóó¤úO{e>ùÈáï¼¼Ž§Å«?}âøC§:¯íR,Í•™¾æÎ0ÓGn>JŽÏóî ÓzúäÃï Ûéž^6xÕpðòËf–WöÔÕGo¾•¼¿á­'ïyðTØON½­Þ¶ºâ¥Ý7ô7DCµ­®¹5,›o{=íÖ¥+Þ=_Ëy’oÂ{ºï`Ñ{W?üžI,åUGN¼mÏÿÆ}zâÁSw“ß{Õ]GoÚ'Ùè+{åê£o¾ƒžâ' ßEO0>©—‘ñu <»n£?½\ôÄüöNxÿïíE×·íKÿpb½bïÛO(©½oïU{ßß•w{ß|BŽ\ô{6ìÞ÷—ûáâ÷ÚÙ]¾÷ÍÉÀ§ ùUwÞykúkk7GSw‘¿Ü W‡ÿ}oøGa\þ|uøù¤ðóÉWçþÑåýò·2}çÿF“5ÿ4‡ý¿ DáóšO½ó]o]þñ©—úš€Í¡kÿ·Ûþm£±Ówpû>šíîÿÙ`¾o0œM‚ÀþßZµx£žyôä}'î¹óÞãžXjCJ»P~á©í¯Ò»Ñj¸>èÀ—ìK~i×Ä¿ò¦·/cE­)õ„)v`÷]úz´O.0N"È,\³!û±â³}å2¬÷â®OŽöEºýÞ¿pEú°å5ŠþbWxñê÷ü"ÿ/„èÔÿ‡¿îÕò`÷øtÚ¯ÿƒ=ú?ȉá”~˜:^C/þœïÿÞ矔€‡ ÎËÿÑxïóŸ ‚ý7œCÈÿM€ä?e‘)[~‘/tUüõu­G½”‹$R—m™¡íû€M,¹å‘×¶o×FãÀ¾–Q!æµí[¡o,Uç/°v. ûøó„g¥Ÿß°|#™$_–~~4ý¼7…@~+ýü¼6>²ïÃéç®nþTúÙ$ZÈ¥Ÿ/{rûó{ÒÏ<¥ýù­éç5)eüUéç§Ÿ^Ûþü½ôó-);÷oÒÏ;RîÿçW9B,#;ÏO~þž?¿rÏŸ_»çÏŸ·çÏìùó{÷üù»öüùÇöüùçöüù7öüùìù3U3/ÿü¢=žìùógïùó[öüùÝ{þü{þü½{þ¼³çÏÿbÏŸÿãž?ÿéž??ëÊ'þüÒôg/|fú¹“~ÒzûÁôó@z@ß™~>+­·oH?÷¥õöeéçGÓÏ{Óºû­ôóóÒºûpúù†´î~*ýlÒzû±ôóei½}Oúù‚´Þ¾5ý¼&­·¯J?ÿX¹þL?/ý|Kºï“~Þ‘îûçÓÏÏL÷½“~Òýþ`úy Ýïw¦ŸÏJ÷û éç¾t¿_–~~4ý¼7Ý÷o¥ŸŸ—îûÃéçÒ}ÿTúÙ¤ûý±ôóe×enR€¥7´d ]µï‰J§eµÓ²âiYõ´¬|ZV?-+ –UPËJ¨Ü¥=¾!Øf§~ô;Ÿwøÿ½ö£7þYùYzÿWnhôá‰,hÝŸ}ø•ßú™~Á›þôÆ?+?óO»W~z?™öéÞ©»‚týoÆ»xWô.p™ƒáÿ™{?Jú?$þ_ø=øÀÆÐÓÿ!RÄ)|¶$Avò°¯êþÌ 0ûßÜûßÿÃÁx´·ÿÔöÿx†ý¿´ëþYÔwoÿ‡¸ðim_ÔÿáÔ¿é2éÿp]úù‚Xœi@pÇíwÞ|ôæXL:Y–_ÄrÛ'î9púÄ}Ç>ùîÔû!½ñ¶§OQª!«™~RúÆîRç}»râŠI÷ý^ò‚jº®Þ‚êeBâŠû/z¹·û^L¥Õ×ì™ú«o>zäõé}Ñó~Úkßxë±Û_ÿú;S—³O{í‰ã÷ï>ËðÜßöЉ‡¼,üñ¡w¼çÀ[ßõð“HM¿Ÿxöo;}üþ²Úëe©ïR;t(Ñ{Vù%êãÜSßMG>~Ï?¾¤ÂC¿öž“Ô½½«'}öˆŠBr}¥šþÙwÜ~ËGnþüi«KÅßóeiýƒ§î;~úä—ÇNì´ÕÛþǼ=lý9|Óá;ŽÞ~$lþ‡<õÀ=‰üÞ‹Ï=xcS˜µ¿é10¿lyã×…?väðwÜ~ÛtÏŸúºWÝ<ÌÍ—8¹ubëÀ}'Ž¿ãøÛOx뉇¿”zÝ<ü¥§ÂmÞ}âÁ‡O–ÝçµÂî}Ÿ+ûú˧tìÆÛ>ëºÓŽÞ{b¼>þÀÛÃ_§î{Ÿ¶è¯ÙsAÙ[ükÝ·X¡Ã q/Øæi;îÎ!Ûòä©7Þvôðm·ÝxìfjÏð›_G›#I?„±cÈqúVѼm õÉÊ <%J÷xõϾùžø´é÷ŸºçÄ}—ë5“=Ý^óS^O6†°ËæËìíÑ1½¹§¡ éO©ÇIG†¿òu—9~÷Ýï:}üaY׬?“}›ž~Û]·[Ê(ê±:ºí]÷¿õÄiš«]{àTÚcÑÖ|Ùƒ§O½5Üö{ ©ãËHfpl~?çÎ;ßtôÈ·{ÓÍ·½îö7µkùÅAzÐÒhŃAwŸ‹åKO>pÏ©/¥Eò®/—ÛéiŸôš½í“è”—ê+­”ÊZ)µÝˆv}Ñ+º¦x_úž½~Þ®B̺bô¯V¼…Ýo¦ßuš”ýUÛë¢_®,ýúbU|ÅžßìÑq½ý„Ùûv__¥5y´÷—Ý;zïßXí©ô#)@F'üäú'Ñ¿¹(@–Èg»]»¿jß¾'‡ñ”¯Jܵ¯ÚÃ[ûª´.ú>pÿ3÷~"”õš¶ñÿ)â›ú?ýùÛÿã¤ý;˜ý?^Ôÿ%îÿâÿ›ÃgîQ¯Îî^”üý/=óDPñ®¿ú»Ÿý‚7ýÜ…öç}ÆŸÅï¥Ó<.Å÷î»Dß»—omù^jgý±p¿ëßKûâÏÓýâ{7ó½Š(ýûøÞï½TrãÏÛ÷ÚWö½ëëªô~½ìºÒûýXÿÞ€EÉß_M»¾WöuÀåŠ.ÿÿΣ7=ìØ¶¬ÿ+ùÿáu„þ¯›@ÿó÷kËÄæƒÉJÿÿá|„þß›A¾ÿëuW÷÷}ê•íûèÿú±ŒeZÄî]íH8°¯í5!íH'ë^Ôêo_ß›Àå® dÉ èlöø”¾_`5À奦oÒŸ›=&B×,ýùoïùóoìùós®xâÏo¸¢e—ÓŸÿbú3ñkèdö½>7Ýò‹}›”—WüHøöÛÈ>¸éÆ[n~í‘à»ñè­·ßyÇ9üÉ·¿þõÇî¼ý®#7~å¿Óìù…ï¿»­çŸÝö1ø¯^wø–ßÜùîxÃw6ý“->†ÁÅ<”Ôÿ§øÏ|ˆóßÍ¡¯þŸœaò[¯Ü÷DuØZý¿4'x9‚Ûÿ ˜ý?žOÖøÓ1ÎÝÚuÿrá.ªÿ'ÐÚ¾¨þÿÉïÜwÙÔÿ?eŸ°Ü窛bmÄ'ÝvêäC'„¿|ò­§S!ð‰û<þü®Ó6êÖ>gŸ¤níµ§NÝ·§¸ëšxÚó‘ÛßD>rêKã1ä¢éX…­þ—/Ûºåö©JåY·œ:~ÏŸÓg·Rô~Íí¯½óØ­·¿Žü½—ÜþÖ‡Nœ~7ýšŠa_yàÄÖÛ·^y zÔ±Øç®[Eåw–|ï]¬¬À«Ž¦ Ô£§ßÕ–àßSÕ}*Þ•¼”{Þ‹rõÚï¼™ÎÇý¤=×þЩw¾ /|¥¦óš;ïzí±;oº‘Êå^vç»Þzà¡»?pàX²úʋڢìù…èFü :SùßÞ÷zËÿ’”¿¨2nd»¨´- ’‹þîž´÷ý´6/z«}äýë=“¹|ÿë’EùÕûòsÃ}+åSöÜñÕáQ ö½a|Cߘù:pöŸG˜¢þk>ÁþÛPÿõçõ_7¾ÅópÅùß1þþW}ô?ÿMñ¿ÆÓÁx²Úÿs4Cþoyþ×Õ×<þ"ìå=ùêö$šw_¹nó+ò¿¨Gø_ðg÷¯d÷÷Rüz‘ûö+®lÖùö+/é·_uI¿ýêKúí°.,Ï@}0û·þìbég|ë%½ŠK‡;ÒÏ»¤WqéðÀ©(‰%âÞ?›ÃòxÖ§¦?/Ï1þüàRz…÷~ÛûÖ?ò?nüÚÿòË~^ÂK¹$¸ÜÏé@çîÛ÷”ß"^ÀggÃù«æóáü³‡“ÑøUóf´¸Ô”ƒ«ÿÞtÿÇTÿ=›âüW`sèëÿ[öì{¢ ÷Ÿ‹þ{÷ýþÃÉx0ZÝÿ“9öÿFЮûÐÚ¿¨ÿã²íÝEýŸOï.“þOO?_[ëõ· ¼66M?öº›„ÿù ¯;yúÄݱùã©·¸çÄ}Çßsàî<|âô—Q¿¼Gn|å×¾éåÇ:ðà©ûÞóÀ©ûO¿ïÀÉ<|òþ[ûJ: .ÅÅZƒÀ„«þu÷mEçmÓPµ ¼æÖÃ7Þyóm¯¿=]Nÿ¬.g–ŽièmX¸{Áéã÷ìþ¸êºî[ùÎï¢Òº«~σ´Ô–ݯ}ãñÓGN¼-<Æ=oÒ'†7©/•øvÓ¿¹éÔ=ñß|œäßÄerÏreÐD¿ŽVFX+ÇÂG…ßenF>9'}öˆÑ”ßÿpôÈ]ÔQîªï Ë'½v¸˜Ò߸6þáØo¹1¾»üë¯MýšÏºñ–o:zóMôæn ë‘fìÆÏ?LîžL?ï|ÓòO·Ñ?x ýé³Ó#¾¶ýõžÿ¹ípû?Wöm7¥O?|Ó-7ßÑ~úµ·îýŸ£{þçš;ïºãð‘ÏŠMõ®¾ùèêsõÑÛtóMG½õð‘›î:òfºÌ7¾í.zïê[ol÷ÙwÝqóÑôéž|çGï:Bwøä»ŽÜØþµ§ÜvøŽ£wŋҷÜu”>öª;ïº-~Âí·Çé¸éö[Óÿ(=ÖvößuówÍýò).OÊ %FæTIHýkhORƒ§¦ÕE?iÍ>=ý|FÏÜ×VB?;Œç¤¥óÜ}mã’a¼8Œ—¤ï»!ŒO ã¥aÜ×ÖÈ¿|_Ûës·#+™â¡ I2½ü"Éôà½Ç©Wm—d‚úó"„î E!!´¯¢zú‘ï?|äðm7-ÑM ¢Ýßö£MØIÿrߟõÔî[ùXQášßµÏ  Ø&çWßÔ¶e>tçƒ'î&)s÷½ÇO¿;£“=|òî‡HL½íä‰ûîi[žxm3è¡ÝVÿ§d}«7ßø|­õþë/—Öû«à¶o¥GðœÛŽß‚æûá{O>ÔNúå2µ«íÍo»ëÖcwÜ~ íºw<¡¡NQû*ºÒvîŽÿþ!Ñ=øµ5—ÞõíYnÇn~]üŸ¶­üIÙŒoàjWg8zs\#/9z’ÖÈé“oÖuÄßUÂ%ûQu`Ã/í¾ƒKnÈŽw  ¸§û½w°ªü¯Šzv÷cµžºû^úÝ]GoÚóÈÄ‹ìºÃw¼ápÐP7ßÙ®³—~ðÞ÷“l Kí•Á¸;;‡OØý‹NqÂËb®ÞÏÅ^SÄey‡˜.Ûë¼ÈÄÛøU¦ ®Üó^ï zw÷AüÍÞ@ËE¿Yó~.úíÒò¸bÏ{½‡J$yÑßÝ£„ö¾‘`¿èC’ ÝûÞê½èw+‹ý¢ß­,°½¿»ø¡.s.åÀ~|_þÄŠ›÷­äÀžþÄSÛ5-¿*Œ÷¦&ÑáçSºú©õ}‹U#PƒËÿmúüÝüÿù¿MçüùþÃIówpû<\ÛÿãùûSøg{þ¼7÷ÿ«ï~î_öKöýÔÿ>ö²W}æwüÏ Þ¿ÿ¯¹çû¿÷“®þ©Oûç<÷‹þ‹Ïï2tíÿ´ó‡ócGï¼udÿŽÜþŸ¬œÿ3Ìãù/sœÿ²¼pû†ó¿úÃg7üÈ#çÂØþôßØÙž}íG¦ï¿ò‡Ïßò§Ï\üûßøÁówý÷+á½Goø‘ëÏ}éïü­ùÿõÎþÏpkñ—Ïâ¶Ñß>ÿÑ_xÆùðÞ¹_~×µ;áï<úËïzßü“¾þ'Îÿâ‹&;÷¼lxþröüï}ßÎ…ÿŸËs?+|þÝ3ú¼G¾ïÇßþ“÷,þÓÓ~œ>öÚñ7‡Ï~ÍüèS?þsÜ¿ø†û޻߷ýžs?¹xÑ›~fû;Þñ‰ }>}×ç|û7,Þòþ/9ÿÌk¿üüO|ô9á{y4ü»í·ì\½ýýöí?ý«_0ü çßýüëÏÕÏ}e¼¾¯ùŠÙΓ_þþíøÝŸö©ç_ÿ}‡Î¿èM¯m~í+~dA÷>{þýü×?¥9ü«ŸºxÍë~jq÷ç½äü¾üÛh~¦û~åÃïßKÿ~þìÏûÀ"\ÏùÿqåßZüÖߟïÑû³ßû¾WÌÃØŸwþ^û{;ßüw/,ž÷–_jþôËÿhFßþÎ9º¾?øÈ‰0Ç‹wüî7-Æ¿}ø<ÍûÏ~èßo‡9ÚþÍÿyfûûîßþÞ/ùÉÅ?ã/,îkþ÷ü_ô“ÛŸüG·ÃgÓœû—¿ýèâ[žû¡yx~‹Ïý’ﮑžÍõÿúõÓ|Òüýó÷¼ìï-¾ñá—Ÿ§ßѽ‡kš‡k™‡>Ì÷ÎË_ôO·ý‹ÎÓgѵÓÜ„çsŽžÍÏüµÅ½éçZáߣO#¬óáó_ræëÎÏèoÒ½†{7}þ6ý½g}Ãw‡ëþ‹qîþÙï¿vqú[îØÏg‡Ö=cZxè„ëº~‡®Ÿþýéoùðü}üïóïxÇߨ~üŠÆ9?õ¦¿OsÛ<ùå/Þ£ s@ßsî#·ÜµkàüUïûšóᙄû =¿ýûpÿ³·ýñ…gô{/ýÊ—žÿµçý}Ý÷9Zcô,Þöèùù}×-Âß9ÍŸ¼:Ö²ÑõÓÜ…5°óŒÅ;¿þŸ¸ÿïÆõ®7ÞÍ!]XçÃsÚÙ÷+Ÿsþw|-}ÿvXÛa.›/ýOÙ {¡yÎOý½ÅwÿЛ¿yÝwÅÏÿÓ/7­ï­ÑWüþ‘ůþÎ]ï{ûƒ´þÃó»~æiþÚñ3¶¿â¥Ç‹Oÿîóïú‘o9ÿ¾¾s‡¾›öÏû~íi;ô9¯|Í×,~æàç¼pãùþÄÃçií„Ï>G¿£?Ó³ ÿ|ø¼&¬­}6}7­«°›ß¾êé‹Ã¿úC´†vÂë?ÌM\ô.|ý÷-¾èŠ[Ï?ðõ?Dëk§ZwÏ>rË¿œ¿ùO~à<­Ú;GŸúëM¸æ9Ý#ýz¶ï¸ý;v^¸ýý‹¿ómß¹ CsäÂ6ícZ t=´†¿ïWþÁ‚ö;ɺ7ZÛ´—~èŸþÈ"ÜËÉ™OyþÙóá;hoÆGÏðµŸø‡ÍK燣ì:?¸-Ìѧû²‹žo¸®ó´ÏHv|ÏèEçéžÃwÏi~iÜþ]?|þko=|þ—^úo/Ø÷ßæô½ôÙô÷hݽåýÿyçÿ9~vñŒo<îÖ`ø;ÌhÒýöUß´øù¯ükçéÙ~õŒò1¬mÚ'$‡¾æ+Îí|ÚÃï|X;oxèŸÒs gŽÖ&É ãþ=ñ­;qÒšƒäËŒ®?ÜSøÜ8Õé“çßôò¿~>É]ZÛ´·~ý5ç¿é7~lqÝÉ/¤ß‡ýõazöÛ4Oô=aMÉ¿¿fñ¤7ûyú¬ð¹ôoçíüàüÞúõ‹7}àKAŸæçÒšÑg?秆;aÞ¢üûfç“?ôÀù÷)ï_Ðg“Œ¢Ï&9yÿþ¿óŽ¿zþwþëiÒ/çúŒë>ÜÇ,ìóùŸ~Û·’ÜÛùò¯»é|Q¾‘\¥ýGs9|ÞüüË_ôºóáï5AF,hí’Œ µD÷x~ð/Zß´Gi Ò>£Ï¦y¢ç|ׯ߻hî:}þ †ÿhAû=Í˹ðûí°~›ðìχµwþ_ÝúÁ Ÿ¶¡ß“ 9Lz2ÌñÎsÿøC‹ 'ÃÞy Íß9º~’M¤'h=ÓþŸ¹sµC×Në?é™sao‡¹ù÷;×¾8®oZ´¾èïÑüþ»OÚ‚dß'¿þ[4G4oô½t}´Î}ÿ÷ž§ýFúåM?ýlú÷´?·é>i-þ•ë¾—dÖùü¿~‰ÖÆŒ¾7ÌçvØc ý½ 7w^ÿi?±øíïþÚó?øÓï‹ò‹d$én’£¯ýć¿ùðûÏãÃgñs?òz¾Q¿Ó¤õó'?}ÅùOøÝïrûÞí?Þ&Y“l‡»g¿ù?'ùuú5ò£çƒ¥=¾Mk˜Ö!Í#É\’­?zò–óÁŽ8ßî?²A®zîÙ}ÉùÁá¤u3#¹GßOkt)ÇhŽèyÓº£µCò“t7ý}ú»¤Ëéšè™´kÿÛ˽C¶­’…dO,÷Dú¼ 2Ù)™¥Ï~”þLßEψ¾›>‡æt¹§éù$ô(É@ZIn§½í ú7ô9a®æ­-rwÜtíô÷éïü¥k¦9#yF‡æŽ®?Ú!$Ûè']}]?½G÷K‡¾—¾ƒ®‡®îæ–Þ§y^ê3z^tÝ$?ioп§=DóLÏ‘®½ý·Ü÷Ò~ÇõÛéz鹜Kû"^+éÁö9}p{)ÏhmÑwÒú¢ù¤ù {sžö=Íë”ä=kÚ?´É¢ë§û Ù’lÔGéóIÓwÓgÓþmçð½QÿÑ\§û£ï=G×ßî»G¶é÷´¾imÐÜÒ}™¶ýo¾²¡ï¦ç@ßAßÝÚ©wÇ¿ßÎÛõM«ŸžX_ô;úœtÿ¶zü‘stýiÞâ½Ò}Òÿ“ ˆvJÐutÍ$ËÚ9¹{ÖÎõû£B÷MÿŸîs–þ?®‘´hÞã³$½Öî‰o¦Ï&e ];éºgúž èþéïDˆ®›d^û}cúwô“Ö,Í'É|úü¤ãf­={=ér²Α~l}Œ÷‘~‰ŸOû—æ¶µ]žÑÐÒ½·Ï˜Öø‡æÉ®"›v›Öo«c£Aût;=7Z¿sºöåÞ¦9޾@øŽ¥\§çÕ>×÷o'Yå*]­!ºZ»i}D;®?Ù–s²]é÷i§g|u|6ôlIîÑ|Ñs¤çN÷K{æ¼ÝkqýGYC뛾ƒÖ½ß~ÆÕó¥|¤Ï¡ë ù¤C{öu’!qnÂßo–òŒæ‡æ–~.åÍU»ï®ßnåãû—û'®™vÆ=OkîÜR>µ2óîY°ƒ’a´†È‡!@ŸGû†ôP’÷ñß´zûεÏþ5ÑG¢kNò|Öê§xMqÿÐÜÐÜ·÷e{¼NZ´Ÿ’Ÿ6¥ÏY®mZ×Kû•æ“þŸ¾Ÿ~’þLÏ$Êz?ù[qN’}ý'úÌdÃ’¯× ùŒ47­zÅRœ[úS´¾Úù¹>Ê&ºú»ôoZ9týR¦Ä5—öSÜ¿ôoH'ÐuÒ÷Òú õÔúÄ#ú»Ë½G÷™äW\ ô÷hÒ|ÐÞXÎéjš³$çôYt?­<»;úÀ4­?þëôÝñÙÓ³¥û û¢uHóŸöÆ4=“¸OHvÐzo¿çÛK¿öÍoZ«q¥k˜µÏàŽø÷è;’:G÷Er±}®W'=ó‘(Ûé½$ε6Ðâÿ/×y°±Òžkõ@²?mçi7žqŽ>ŸþŸdiòßgIìÐz]Úi­FY’ä\ü¾v~þhžärÜ?´æi½'Ÿwö§ßö¬fùüÒ\ÓüÆõ@÷lˆ¨ IN.mÒwiíl·rpéG\×]O«óÛý·ôµè³Òú:—d^´KèûèÿÛŸïÝnm«V^/×_ÒÓi½´rªÝ/r®µW^‘l¯(Ï£lnuòõÒ¿¡}Ò>¿‘ìö?=ë´×gÉXúLóèDýõHÔ‹t ôÜèš“ :ƒæî%í£hO¶ºäÝIî¶zŽæž®•=~r9_ÒsZú+t-ížÿp|Öô=é^¢|M¶ÅŒbUttd³$;'ÙíþOk=êŽVÿD›þí|©ó“LŒ&yCÏ”î>³•a™ÒýÑ£5Eó–ôPÜót­t­òÁh'´ûô#´—âõÒ¼ÓõÒ܆½¾C?iÿÐç/íbºŽÛ~áE[‹kî‹>—Ö[ë ¼7îåä3Å{¡çAÏŒžïç¼â/7d7„ûÜYê”eÜ‘ä(·®û„´¾Én'üæ›Z££{nŸÙw÷Í ù×?ñÑç4ËØßR6¼ãöç5Kû%üû¥ßÖÞ×#Ò¼%Ý:o}‚ë£Þ¡¹$=Gs98ü²º?’]ɵz¿Šñßz'=ãGIf&bûM?ým Í1í]Zßí¼ýúv»_=Ú’ôÜÿõ£¯Û ×Jó¿”‹ôè³é:ií“}Jk¤]S ¶Â«wcžÉÎ'2ou×#ñÙQ¼Œ>—â¦íZ}ÍRÒõï,§Øiø¼ídg-åêŒæôÞüÕMŒµÛŠæfùü’ühè¹]øúOÞ¡kK24>Sº/ZCä#“œ¤x­eZó)vŽüTzô=ô|é³éY·6xô1h^)îtŽæ!é¶í$[¶[yýÈŒžQ²ËâJq°ÿZ3Ûaþv(Frjéã´¾ìû¢^£øWò÷âþˆr:\ã+~ÿWâü†ßíÐúIri¶Ü¯ä÷þá¡æà»v‚¯¼Ó>ç(¢ {濺C:•î'ü¤¸VŒÓ¿O6ËÎ |EÜ»à·ôé™ÒŸÿÏðGš7<ôOÉç¦ßÏ–±sz†‹Oÿî†ìÚã_ uCû†¾/íù&Ù_á3nÞIk"Ú+´_Ã5Ò¼ïœ{ÿ•?c^É&Œ÷GsvÝÉ/lÂ<ï,}SZKŸ/Ü_ÓÚáï]ʇG[¾o|Æ_úW íOú÷´——öɺ—·ýñgP\ˆâñÁ6ü‹Mûø@ôH¾þá¡OÛ¡ýK1‹«Þ÷5Më3<2Ov×vÚ§Mø·;ßýCoj–sO{…îóýW¾jç+^z¼ùù¯ükñûéwÉö‰r;<¿†îöÌß|ë·4KŸö­áŸø·_Fïí„¿C£¡õLëŒ~G÷ñ¾_û+ôüvhý=ç§þ^Œ‹¤ø}œƒ·¼ÿKš0v~ûªojîþ¼ïm–º‰dWkDŸw‡æšb…´&HΣ{ ï7ÉgØI¶Í¹Ö.håTØÿ$w^pý×Çë ÷Z9ðÈvkŸÝ=»îäo4ßþ“ÿ‘âyM²¯æ­]øÈöÿ®ÿ;ÊnÿÉ’ÉŸIëøjº/ºï¸¾S|êÑVNÜMŸ³C2ˆ¾‹>£Í;´±_Ú?¤7¿Ï5Üîߜ|ͨ«¾ÿ{¡ s¿ÓÆ\£{®µ‰ÿèÜ?üݯ²‹æ‰öØ26Fû‡ÖÅ«þÖÿÕ„ëjþà#'z­ŸeWŒÃ†ï¢OÏe‡þ.ÍsÔ¿­ÿEòoNÿ>è¨d“,}æè“ÓšH¶d\_)vs® |äQÚ$›i¥¸u´ihþ’>ÿÀŸœÝ¡=ø¾¾³I±¢í¥œ9xۣѮ$ùHë8Ý÷lic‘Ïî;Ê—wÜþd7ÆÜ =?úw$?~ý74AÏÐÜG™ÓÆY®zšâþ£6k3I¾GyAzŠæ„äÅØIVÒºMº->Ú?´·Ú\Æûâó%™IßO÷®“ô­Í9ÅGS¬b¶ŒaÑ}„µOkŒâ°$gæ¤oiïÒõ‡5ãñ´?“Þ§ÜU´SIgQ,šä,Ý}>Éw’çiÍÂÚŽ2„>—®>§•¡1÷&í­Ür×N«‰q’4$_é÷)–·ôÏ¥¸ÀéHúwßøðß¡}c8Ëü }f¸¨›¿â¥ÿìœèg¤ucg$#iý¤µ´ô#—1²&ÅsÏ%y.ùèQ¾Ì#û‡t8íÁÖWø`ôHî’üz7®!’5É.q’QôþµÿôÅù¡K:>ùmQ6L õKk€ü”äÇ%¿øÛ$³hÐ:m}ÃG’~ü`|¤³é¹’áë¿/Ú&d‘½Lφæ?æÜÂó ¯iýƒo^úP¤÷wøƒo~ù‹OE9Më(阸OèÙÑúNúo;مѶ¦5þÝR~7”Ÿ¤ëOú1úG¤ÿH¶ÐuºŒ¦Á6­oúnÒ3·ýÂߌñyz¾ÉO¥çÛ½Hk~’-q®•m´Ýú×Ò³rûÇÿŽtÍMô9h/‘l£9 ×Fëh™WMñò ÿbóŠß?Bs@¶ÅNÊ›ÌRü‘Öê£tÿ´ÛxQôGȯ¦gLÏr§Íy¼?îQ²Ⱦiíä÷F›šö?Én²1Óº™µ±’WNÚicàÿ®5Ú€éã<Ó¿¡ïO{²I¾Ä2Ž×ú¿øG¿ç˜Ö#½Ÿ|‹è3ÑóY泃|ØYÚžI~N)—D{<详µ!¢~¢û‹ù ’}$Oi-Ò>£µD÷˜b[‘?@{„dÙŸôý4/Ëü}.­ ’Ÿ´FÉw[Ú¦¤‹iíѳÏh§Ý_l§õ÷hò‰špÿô|›°>šä7ïÆ„éÙ…9nR^#^+Ý[ëë·¹xÚ£ÁΡg¯Ÿž;ÙXQ?Û€ìkzŽä/о¥û^ƹhm‡÷£ NùÕÄ 9u"é¤ ¿hÿÓómÚ8ïÍ–{˜ä&ÙaoÅuNºl)gÉ"Û•d!Ýç¯=ޠg™rÓ6oõm’tŸ)¿ch´'ÒsصíÛøz¯¥µGstî[,úPiNfK9@{ìË¿î7ôÙt )Îp.ùäÓÁá$¹´ý3:^ÿÒjãç­?>?Ù¶×G;„Öé8ºfZßdßmÿ¼·üÒNZ÷Û)öFûp‡l ’áï4ËýOò¼Í˜Öåηéï%ûc¾ë«'ÛdñÚ½Õr\hH瓎¥}žÕNk´÷–ì³8?m ìýM›þà®ýCól̆®}cX®=Z‹ôüɾ&Û‹¾+ùô¹QŽL¤{ýÙ}q\#$ëé÷Ë/é« ¢~¦ýAk›öNÊ]Íëïn“\¢kÙ&ùÝú1vEñž¨§Èo¡gCü–”û/c„dwÑü‘|£9£ïMöw\ ¤ÿ–ñ2ÊÕ¦Ü ùŸQ‡¹ÜMò…lõÖG}÷2‡zŽø34¯4÷´ÇbÎ>\cÊéPˆæ„ÖfC6xë+].ÙPQV…çåÉèÖ·¾{™ÃŠüZ«ßù´¿õÉŠ6¶ÙÚÁ~Žkl²%è™&þRÔ¯¤S?÷Kþ!­²¯£o¶´ÏH~’l!?–l$²¿Ûçíªí6–ô);¤ƒÉÎ ý°Ì)&ûoN¹yŠÁÐ|ýí€eܘž%­-’Át=A‡6)&:[ÆÚiÐ瓌Ï:ÙÇ­ïIkŒtzòá÷úxçRltNòi߯|N”¤ã“n˜§\ߣ´g’®Ü6D|¾m~ýŽÈñ‰yœ°~hýÒ³i}ÏVÆÒçО ¹&ÿ‡ì(ÚÓ$ãIÖÐ^ ¶u“ä@œÇ¯Š{¬c=£im쟜“¬_ú—t}ô}a_/÷ý¹ióAˆ±Vz~ô]´~iÿ˜âc³eŒ™t=Ý#É©6þÖÆqéR®šdJ´KÈÖZæàéÞèþé÷d›I~ÅNRÜo+§ù!Û'Åàf$—–±(ÒŸ´¶HÎÒsnspïµ±¹÷E{8èŽxmÁò›î•Ö ½G÷Lû‰æ!q“¶—ùZƒäWQ\ÖÙJ4gtÿ4×m¾îýópïá»ÿ ú^tý)_uéZãäãÒZ¤Ï"™FßCŸAŸ×H¸Çàì´²õŽx´ÎÈ/h÷5qrÎÅ8}/]­²OÂ߉6ì+~ÿWše~w¢{!»|ã¥|O¾EŒß&™Nòf›bˆd/µßÑæ7Éæ |Xâ_¥|ä¬Í˽†ø´fâ¿![¥µÿ^ó·mþï‘GéúHÏ_Nòe™_¢¹¤5EkžâDk÷þ3æËœÙ1®Ÿ|ˆ6^}è(+H¾ýå7ùbô¹)νMv Ù$ÏIÑsjç5êè”oŠr.^íe’ù)öãô´ÇèIþ¶Ïü#¿Ún9:wPü±iíšë£|IyÖYÊ}G?–žmð“éù7íúûà¹%ŽìnÒ?äc’|Kù¯È1 _‹žÙ´ÎÉ¢ï§9Hyõ "ß—déº÷6ý“QG“ÍDú†¸kKÝ@?—qr²i ‘!YK6­-Ú¯´>È'$¹CsüǤÍsÅØÁø›ãs!=IþQŒÅ·±í6ú²©É~ØIqÑhS¤ø3ñkSÜõÝóŸø·_¶ÓÆ.¢n˜¦üïéÿ°–›4¿¦¼Õ4åÅšÏù¸ßmˆSJ{u™'I2ž®ƒžÍ2¹Óæù[;“Ö!Éðo‚oóªÃNqà¯N1û(›[žÍû£.¤y[æñHoÛ.Æ h¿·¼¡G¢¢ù Ï.Å­¿z'Ùeç’}ó1´f£}Öùùô\’¼Ì‘6aÆÉïÖvº;­ñG¦dûR\”äpÐÑËØør£Ž¥ë£õD±º.’1)/G{„Ö]ôƒÈÿ¤gÖú/¯‰yGÒmអÝ@ö Íë’cF{›Ö=#zvÉOйž¥D:™ÖöU§ÿÓNkçEþìŒ|Úãä?ÓÞ"9Bû¾·õ[îÝ+é–h³üÊ/î$Èt™û'ßžž#ù÷d_·\ˆÖ· õO÷Fví±ÄM¦øTÊ;¿7Þ#é7zÆKîéë–_÷áŸ"9Jþé©6fy´Îh ÷µ!ýD¾ÆßëÑ6ÿþ3§÷iÑ5§\Ö¹ögäÀÅ'ùô¹”«m×Ïûã玤ýIqÈöÙE¿8ÚÀ´~hÿ¦XÛN;|ï|)Gé™$;’dPâ…ž[rÉèùѺ¿¡å¼ÇüE›ýpôñŸ!þÛ%O‚Ö];=c²‰i½Ñþ§ùXæ¯Rn>þ[ò‘IwÒʽ?š¥|eŒÜ¢88ÍãÒ— {H¶h¼§6Çò}Mk\ŸòÜí:¦y£gL¿oíŽ(×òðï(E{ˆæž¾—ž-­6¿öHCv5ÍU_MâýÇœ9=Ç07dDÛƒ|`ú÷47)þEß¿>‡|¸ZÎZô…¶[æÕ¤—㵑BsC²ƒ>›öÉjÒëm-ÁDÿŸìdšGºoòh¯·“ì~͹ᙓÿI×C{Œl‰å³'ÙÓr(¿yN>.ÙH˸Ғ+Bûƒd"Ý3=_š£"?þÅç8eÌYÑú$ÿ‚dÍË’ßLzò‹®øç1çCº˜d Í}k¿7rWHg’@ö Ù¹-)òøÚXmx?­ëè_µ6ì#Óe~Ÿbæô^[7pn'q²âþŽqذ¶‰kN²ƒžÑ2–øçh.ioÒç‘|låöGÏ£å ‘K¶å:ÒzŸ%<þŽ®™t,éú÷7$ÎVâŠG›™ÖpʇGۤ凴sD²…â¤cH¤Øl´áInÑ\ÓwRü8]WÌgÒþ¢øÍ Å8Iþ’íÛrÇ?|Žæ9åjÈïÜYæ&[.NË¢ï¥9&=æ°i9ÏH¹ã Ãý–âÿä§Q,ÝœGºoz®dÇÆ\OxŸæ²µaÿèé ’‹$HO,ï1陡ØÍÙ)ÿè’ôuŸôk1§@u tM-gòâEE¿›|Ò‹´iµþS¬˜/yŠa]4mlþîèw¥Øâ,ñ vR¼-Ú'dLH6PÌc^$;»ÍahÉ+™'žÌNŠõý²³ä¢ÞÐr¿¢¿Eö!­Ã0/Írnk;Å7HoR^*®Ú£ÉÞ&Ù³Ô7ázvyÿÉ‹¹ÊPœƒb Évƒæ’xåäG¹žs+ã^¼òô1ŽF:’d/É/Ú3´_–yzæ1ìß6?óšYâTD ÅoiRü…äÁ2vJÿfi“Cñ#Ò1Kÿ6ñØb\‘Ö7É»ÿ¯ÇšÄ©ˆ>ÖÒXò#Hδ1´–oC÷B1²{R}š¸¦Ûm~ëÛß$¿þîÏüéhŸ<‘ú@Üÿ´nÉ®#ÿ;Å?m÷oŒ ĸ­o²UÚüì#1G×@1НÐÜ/B¿£5J׸7s’”§O6vâ}½;ÊOŠ-Ð÷|÷ýë$#·—ö1ý=òÈv¥øéø(Âç/¹¡ô÷ÂÞ uíÇeìÿ >îb^ŸìzÞ‰û2[r,éYü§klc~Œ|Ì–/ýy›×~ïŒü{²s’}µkc‘ž¤{ Üä2?GóBóKŸG~éИ›zÿn(òH(×L:ŽæžbCÿúѲ“¸ZçÚøD+óiO¶|³DuÉÅN±ÒÏ1®HÏ—æ5qdÏ%]ÿ(Ùo4‡´Ï’oõ7­ƒ6¦A6Ë];©öe»­A¸6¾O9EZK¤[(VGóFχþÞ’[M¼Ú§d l'}™ôÓ6­­%—•äÀ ‘ÜÆ>hMPŒƒüNò­é'é2š—ÃÙNöC|¾m^²åóÐýÓ¼PLƒö-Ŷé9.}Ëw‹öí²h}/c/´Îè'é>Òá$ãÉH²w™?¢um²SZ?â5Û©†-ƯÉÞ¡ü-íCòC“þŽùqºWŠ™Ñ:&=¶ä¦Ñܵ~þÑh9"Ÿó ËüÙ4K>q$’ù6‰óíGòKèßR|‘bøËÚ’á)A±õ†ìûÄ)4Å­âZ'ÿ’d3Ý#Ùg˼=’KtÍôÜèP,d©SR |F¾/ù^É–iRîr–ü\úž¿¦k#_x™³^ÆáÈwM¾b”etoËæÒ†¥µAÏdíì‰úÒç%yIvÉ~ò•H‘ Ôr­y”ìJz&´wIÎßjHÆ<Å'þaŒ9 Bk*ñ^â3¤ýEÿŽd4é@Òåd›,¹GQ?¹Br‚æ)ÙÙ16ì«GSî¨i9b‘çpnY“’üŒmŠÿ“l‹6`âK/yƒ4ç´GHŽS,s–jbéÐg“ŸH ÒËäK·<êÄ}v›Û|ÉÍhŸ,}—d'ÏHf'ŽÉÀ†~Òg,ýWz×üW$˜x·1.Üæ™?ó¿É¾kG"ÚÕôoéžéù‘|!û$Ú“±~1æ #¯d­oÚ¤HÖ¥}0£÷)Fº‡t\ËaÞ¯ÏÉö!ÝJö][?ý¿sKŽíIZ‡1ŸßÚçR)q¶?2¥9hmðȯŠ{tÉ£¤ØÍ1É0Н¤¸ÌRÆõ é²ÇIï´:úú¸6éÙµñëÏ[îÂݳ6>ùšÄak9Žd÷PÞƒ®ìœÖ?oë!hîÈ6 ù¢gK¶Ô -çu—ƒGö+}6ù!‰³9oùÊÑ·ßN{n‡ì$²Oï•lÈȱ"™@Ï%Êý0?´÷Rì6ÚtMdoD~Jâ ,9º‘Ck€ì‡–ýÞs‰ßåå½h~HÒs\rŒ—µ$ïèÙÓ|Óú¦x^Wˆqµõ$ï_æHãú¦gGq*ºGÚã)/}n)‡hžhm“}F±2ҡɇŠú;úöÁ†¦9'G~×uŒüË軇µKkœb)Ëú|šcÒ míÈõÑZÆGnHµc)¾m/Z?©>jJ¹DºGZÇdãÑ

        ñ_y”t:éšûÄÁŽóžjqbŒ&Úæ×}WôRî$r¿È^$½ÔÚW¯¥ûh–µËÚ5ò×h Ñ:£õCÏÖJª?ySÒ䧺 Ú;‰û¶b~‘gÚòÃÚøéÖx-a’Œ¢½LóÐÆˆb}}ô_)¯H±Ñ¨Ÿ[ÿÑtýñóI'ÐÞ"9Eö× ±~®åh$.å”ö&ýŠñ‘^I¼ÂëÃþþãhG'qËcOzÛô™dûÑ=Qަ½oéLâå~{|®ùîüÿÛ»𸲻@ðW~t»ívâ¼HÒ¡ÒBçeWIªRÂv“–l¹»KrTrw’aQ®¥+[tUÉ©*¥@Ö3Û¾ì,!óÈcgfa†™/Ùe“ÉÇÎ@ï|Ðé†åµ ›o ìd³Ð“†Â#hˆ÷ÜzX¥·,«Kvüûõ÷ïsëÞ[uνçœûÒ½×Àõ¡R¯'åéFRoL×+q¹|¬R?ވϕ“csý;•G6( †t Ÿ/äšé@.ןÍes¹þþ(×_Èfóù\![ˆ²¹Â`¶?ÊdwªY¬7âZ&ÕÏ]ˆÕøüzóU+aƹ¹^©—ž ò!=¢X©%•¤ÚˆË¥ÆX\ÝßœãRõE­è¸ô¾¨<nñÄ+ßûo}âëoéÛtnøÆôd0Ò!Š¥±bu6y¤/ §qGˆý!FÊ 3¥#ö´¿tç¾(:ºrbß.”žkU©×“òt#©7¦ë•¸\>V©oÄçÊɱ¹þÁé©ÒXîÚóÈ…ÁÁäó…\3È¥i6—m‰rý…l6ŸÏ²CQ67Ô_ÈG™ìµg½¹Åz#®e2QýÜ…¸Qϯ7_µfœ›ëE‘ziæ]Ÿz“XÏ-‰å±Ûur³Õån¯7ë[!Ä»½ÿB±½Øíë ÀöͼëSoËãÙ£Å!±V©oÄçÊɱ¹\a‡òÈ…ÁÁäó…\3Èåšã³ýÙ\ø”ë/d³ù|®-DÙ\a°0e²;”ÿ†븖ÉDõsâF5>¿Þ|ÕJ˜qn®EꥴÿŸ éá¥F\k³¥ÆX\ÝÛœ|©Õé;ÿrÐéçûîm}~O¤ï߸.·¥í µö¥®ý@S¦UÃw…8’NŸ¯\,'µÙ¤–ÌŽÅ»k?þ’hÅ®!Zo$Б>ûÿX´úÙÿæSú™«{öÍ×¼j½÷Åx¤úĪØ#VÅn×ÉÍV—»½Þ¬o!„7BìöþC!Äö®g•z=)O7’zcº^‰Ëåc•úñF|®œ›ËíTÙ 08Ò|¾k¦¹\s|6—Íåúû£\!›Íçs…l!Êæ ƒÙ\”ÉîT6²XoĵL&ªŸ»7ªñùõæ«VÂŒss½(R/=äCz$D±:SK*Iµ—K±¸º¿9Ç¥¥d·ãrÐû¢ò8ÐNû6œ ¾±=Œ‡ô@ˆbi¬XMé ÃiÜbˆ‘òÂÌCéˆ=í/ݹ/ŠŽ®œØ· ¥çZUêõ¤<ÝHêéz%.—UêÇÏLNœ-•&&ïLÙ`(Ÿé@>_È5Ó\®9¾-Êõ²Ù|>—Œ²¹Â`>eò;“ýÆ븖ÉDõsâF5>¿Þ|ÕJ˜qn®Eê¥ë¿Ÿ+'Çæ²×–GZÁ…ÁÁõê(›ìªÿB¨ÿ¡¡¡þ(sÙnÍM^ÿéöÿTH‡(5âêl\›-5ÆâêÞtê¾°¡O:þËAg;ËžÖø÷D¶ý7®Ëmi;¨E­ã€R×q@*µvõw…8’NŸ¯\,'µÙ¤–ÌŽÅ»k?þ’hÅ¡A´ÞH®ií?­®ý42ÑÕÕþš†}Màú°wóîìa¶‡_Ù¦7:ûÝÊ?­ó´¾_ÜN·6Ïß­Âܼ†ÂVàgÓvpbbrrôôðÔÄäðé±áé¥Óc'G'‡O'ÆF§F[S—>îvùØ¢­\ÿIׯ%M®ÿdóÙüòë?ýÙÁÜ€ë?ôÌ=épHßb_ýÑêLÔ:5ME÷·Ó+ç°#íÑ×5Ój+ý6n\S›ôÿþüÊëÿýÙþþ‚þß ­v¿ÿî0¸*­íh©=§mûà™r<_mOI/G·5?œLê3ÑꦟþÉæPs†Édf¡6Ûþ•—§¿Ô³â‹}ÛýÒþvz´4—ãÚ‰…òb¥šÎw÷ÈÂB9´g8pêôð}Ó“¦Ã“ gæÊÍZ>¼ìr÷Z#VÚV9û:« Z¬62]½5=³š.ž ƒ/8S[˜Iêõ…Z¦²0›dæg·Tàý;VàuË\jÔæ«ç¯”yßÔÛÏŒ†ôyKnkÞfxûz쉀›ÑáŸÿí½á¯ïþ»{Ÿü‹'§ž~ÝGÿÖÿû?yú÷ï×~üö0þÇæ>ÿgýÔÔÓøÑWÿÂkÿxäéïþó7œùóË÷¿øÿy껿󩩧ßÿ÷¿ÿÛþóÈÓ_q×7½¢ox¸ÿã#…ÿ7ÌÿG‡~µöëaüOU¾ó̇¿~ïð[þÙ/O]óÿÉ×þλ¾ÆßÈþô¡¯Ý;üÄçÿðŸý÷aü÷¼õ ÿáùá÷?ûÐG>ôÉ¿ºwxêÈ«îý0þ“ÿÃGþuò_Fžþ_ž÷½Õ¿|öÞá ?ø}ÿ Æçþâ—~ Œ¯¼ýïýZ6Œýüü‰;Ãøò'~wáŸüÑÈÓç¿ôëß^ åyÍK~ã_‡òïÿ‰×?/ä{ü·¼wO˜ÿgÎÿÛø«aü·ÿÅÙŸÿŸ¿2òôK>ó‚7ìÝ3<ü³ÿíãgÿ$üÎoŒüñÇGwäéwŸ}Ñ‘K™æ[øiùÿáÜO¾5Ì¿ÛõÆÎè\ݼ½=|{×ð˺†ÿV×p¶køî®áû»†§º†ßÙ5\î~¤køñ®áiß<7éõiøôÔ‡ßE·\‡Ã7ƒ“ù4Ý×Ζ† Ù®áÁ®áü•á²oZÎe»†s]Ãý]Ã]Ã]¿“{óÒpÿ•ñ7ƒÒ@öʺÎ \>3˜ìîïîž k8¿4Üß5On ë»]óäÞ´4~©îÂï\É÷&òOÃ^çmé߃8;~ò#åѓ×Ûðn¯$€Ñæ÷ÿ^ëÓßWûüw>}þ#—rÿ/=³ÞóßémÑéÍG{ÚqS<ÿ½¼ÿ_ëÓß›öÿü`¶°òþÿ¡0»þß­v¨ùžƒîç¿Ó†¿êùïÛÒË©×ÉóßÚéˆkµøÑ®çggÃ/]y~ö–‰S§J£éŸâÞ0üHRÏ,ÌÍÕ“FH2•…Åj#ÓXÈœ=™™=5:9:~b4sqa¾š6ùÍŸ«MËÐy=}YömÅ3 õùÆüB5ZÚ:´ßª×7¸öR~ÿšKÙü3ä[ãjc±r¶:ߨG­?E®K׋ÛŒ—Šã§&¢ÖÆjý•ØY‘i¹7WbûÑãïhºSøæpeÙÿ.¬½,o^wYöµŸUî<‰¼w2™ëúجЋKKµ¯85y*êzx~³Z>pf¢Tœ*N¤¯2Ípµ‘T«qæmoxûÞ‘¹x!®'™Z2—Ô’êL’éÊF _?5¼åW |K§z›Ïíß•;,S:3|b´õÇ×n©^{ðî-ôðÉbéþé“ÅáÎ{òŽž¹ðh}>|+3;W’FRK7J³óõ [n™ÝþÑÚK0±æôm·ñ¥Ÿ_·Qsº•®´þJÚà]/8UŽÏgæÒ4\˜¯gj oi½¬´Ó/íXÑ*÷MœO÷.wµv'i£l¶É¸ÜxcüØ2É»ãÆBm>.‡áÆÌ±--EÚçÊî5ZfH_Ùé^ÕÐ(ßÐZ”N÷÷¿!sb8;p–þÖÒÔp{óÿòPš´õfîŠÛKr1ž}msqz\øöË2w[÷eK‡)WÚÞ†û›æk0ºvz[ÿÖZïÙXµqZö¶uÞãq¥é/{‹G»-{‹ÇRåtFÿpû|ë}ÑÆoëHWþ²ó­öf/]˜ôÖ”[/…Qi<jc_¨¨Ç7ø«m~þw­oÿ¸Ú÷´¯ÿxÿWOxÿÇÍmÍ÷ÿLǧŠã÷íÔ @®îýéõŸÂ@Îû?zaÃúß¡€l¸ýÏ…m}ÿÊë…×ÿz"=KûYúäl±:SK*Iµ—›Ç_­ž]Zý ìnþCì¨W­ø¼ê~וçð™:÷w é~q×ð+®¹˜ðœz2éÅÒX±:›<Ò†Ó¸#Äþ#å…™‡Ò{º¾xtåľ—œP©×“òt#©7¦ë•¸\>V©?3QŸ*Žßw¼Ÿ+'ÇÒ ¾¦<²Aap0¤ù|!×Lr¹æøT> çú a Ÿ+d Q¶?ÛŸï2ÙZÆ -Öq-“‰êç.Äj|~½ùª•0ãÜ\/ŠDÏ §ÛÁÞb_ýÑêL´´A»-jmùÒhnGÚ=¢³I¼‘·~[èÿ³qãÚòؤÿò…ìÊþŸËêÿ½Ðj÷‡ËapÿTZÝÑÒn>mÓÏ”ãùj{Ê¡ÆÜÖüp2©ÏDËRù‡š3L&3 µÙö¯¼<ý¥Ö˜_ìÛî—nk§/®ÕâGO,”+Õt¶»gÃ/…mÏw²89zbª8‘åd‡«¤Z3æ«ùêùÌì|-™iÌ/T3q=Œ-?Z]¨ÌÇåÌ|5Ó˜¯¤kãp©WgãÚl©1W×q ]¬´xéaÑmÅ3 õùôG£e†¾uôû×\Ð4nëb\m,VÎVçõta£Ö&hU}—ƒv6ÆF‡KÅñSí⬿;ër ]ªæz¼»Ô¨…ÕòaÄÞhi —ÆÞZ<»”|ÏÚ‹òæue_ãÑ‹éê<ÔÎwïd2×õ±YUW*#|Øÿ·ûö¡³Óÿ-ÍÄå¸ÖUÕÅj#“jÏppx|jt||xºx²ù©]×ÅÙ-Õâþh¹mµÊ¾®áÕå]Ñ4„ÍìèäçÓâL…ÖZ]#©½;._U³ëøGkxbÍ÷m·u¥Ÿ_·Q{¹õhYë_g´~àÊ:Ù7><6Òo:Óé¡ÛeÉTã-öÇCÑrÏAM®hyÆÏŽMŸ™8ýöt­–’Ú|RÏ„ŸMj[*¼Í¶‰·L OÞ7:†^v~ç“ÆÎmÛ-Åf°‡›Áµ»oªØêBË6)™Êüls¯·õºìZ;¯¹N«lë[¢Ùç ¦ö'f.¤ÓÎNˆ¢-líVTÕ¡´ª¦'&‹÷ÓÓ£Í[¨ÍŸýnn¡–én *m·*mda¡Ü½±Ÿš>ñáü( ¿zªÏ<”î¦æÊñùÌ3SµÅÐçæ2aKzqim^w+msªåëíUr¤X©%•¤ÚˆËݹ­*Bg+·ìˆùÊ®%²ü«¯+£îC™îñÝù²y»v–Ýã—öJË2ílÎ:#÷D«úͲ_ïªÎø;Ûg÷¯Lk½X‹ZÍíPìf„³í¹Þ™N.5çjæUZ6׫¢Wö¶òxÝÇ¢èì™ f¸>máü¾:·pMylvý/Û?¸úúŸóÿž˜ ÛËÌ=™ƒ¥ÅsÁƒ»]&zg ý.wylØÿsá¿ìÒõ¿üP>Êæ† Yý¿Ò}Þ©±ì€ µ×º´´ÏM¥‡}êöç÷D7îÕo.·¥í౨U«¥®¿§ÎîoĤýñH:}¾r±œL¤×7’Ù±øb÷¥‡tú_ü7­ßyI´âOÄÑz#`wUêõ¤<ÝHêéz%.—UêÇÏLǧŠã÷oÄçÊɱ¹Üüµå‘ ƒƒ!Èç ¹f:Ë5Ç7õg£\!›Íçsù¡|>g³¹(“Ý™EÜØb½×2™¨~îBܨÆç×›¯Z 3ÎÍõ¢H½vd· À®Y«ÿ·»ýÐNå±aÿÏes¹þþ+ý¿-DÙ\a0;¤ÿ÷“A>jmŠÕ™ZRIª¸\jŒÅÕýÍ9.EQ_ÔŠŽËAï‹ÊsàöO¼ò½ðÖ'¾þ–¾Mç€oLOã!=¢X+Vg“GúÂpw„Øb¤¼0óP:bOûKwî‹¢£+'öíBé¹V•z=)O7’zcº^‰Ëåc•úñF|®œ›ë˜ž*å®=lP é@>_È5Ó\šfsÙ–(×_Èfóù\!;esCý…Á(“½ö¬7·XoĵL&ªŸ»7ªñùõæ«VÂŒss½(R/=õÌ—N‹åñÁò§'ÄòØí:¹Ùêr·×›õ-„âFˆÝÞ!„Ø^ìöu`ûžzæK§Åòø`ùÓbyìvÜlu¹ÛëÍúBq#Änï?„Bl/vû:°}O=ó¥Óby|°üé ±Xþô„X»]'7[]îöz³¾…BܱÛû!„ۋݾl_¥^OÊÓ¤Þ˜®WârùX¥~üäðÔðôÉÑÒ‰É♩âÄøñkÌ# åó!Èç ¹f:Ë5Ç·E¹þB6›ÏçòCƒQ6748˜2ùYÂM,Öq-“‰êç.Äj|~½ùª•0ãÜ\/ŠÔK[ªÿF|®œ›Ën3´‚ ƒƒëÕ6·Tÿ…lh'ýÙ¡Á¡(³Ýü®ÊM^ÿO§Bz8D©WgãÚl©1W÷¦S÷õEÑž¨©ËA_{øÚŸßÒ¾U¿Ëár[Újáó¥ÒX±:›<Ò©ÕLˆ´9ÜâH:}¾r±œLÔf“Z2;_ì®ýtúKBì1R^˜y¨3qÍ‘iî­‘{ß6r?º2£4ö¬7¡óíë¡w ½rûÿ´Ûl+MöÿÙ|Øç/ßÿç³9ûzæžtÒ7‡ØW´:µ6Ç{Û±§ý¹¹i÷„îËê*úÿlÜØ^›ôÿp¶—_~þןëÈëÿ½Ðj÷{Ÿ ƒû§ÒjŽ–ÚsÚ¶ž)ÇóÕö”=Ÿ cnk~8™Ôg¢ÕM?=e?Ôœa2™Y¨Í¶ååé/µÆ¬øbßv¿´·-ÍÄå¸vb¡¼X©¦óÝ=²°PÎíœ:=|ßôäăaøð©r|>Ó¸0_ÏÔNG,;çYkÄJÛ*l_g`ui‹ÕF¦«´Ï?3qzx²øŽá´ãMO†QwœY˜¯6’Z¦±¹¸¾=ÿXܘ_¨fíúÚ|!öïØBlu9^X:3zbjrøôôƒÅñ“¶å[º¥~1™iÔâòÃóÕÙ…‡weaÂyÏ×WŒ[³þ÷¬hF}]ãר°îÉk¯‡ÎsíÝι{ÉOûK« ñªhÅngoûö…x|£6sÛÿùêÜÂu¹ÍŽÿ²ýƒ+·ÿƒYÛÿž˜zôb’¹'s°´x®3xp·ËDï¬ÕÿÛ—{ûwªÿmzþ·ìúïP”Í ýú/´v@}éNéùSóåd¶t!¾˜tö;éžäé[“Úc›ûчÏíYn ñÂÖ,óç+q©¹g_¬´³è©]ÙǦõzËUì«›Ò3¯ÛŠgêóé±O×”µ'¤×4Û3|ëZ3t v —:Y4/QvïD¯nä iƒþ¿c~Û°ÿç²¹\Wÿ/4û¿ýo¤ý?­èôÊy±:SK*Iµ—›]½u }iùѪ«sqc»½v_Çi×õ¾](ì†'ƒñQ,«³É#}a8;Bì1R^˜y(±§ý¥;÷EÑÑ•ûv¡ô\«J½ž”§I½1]¯Äåò±Jýx#>WNŽÍå²;”G6( †t Ÿ/äšé@.ןÍes¹þþ(×_Èfóù\![ˆ²¹Â`˜-³Sùoh±Þˆk™LT?w!nTãóëÍW­„çæzQ¤^Jû>¤GB«3µ¤’Tq¹Ô‹«û›s\juíîî}9è}Qyܵªvo´´yi×õ¾Ý)ôÜ“ÁxH„(–ÆŠÕÙ䑾0œÆ!ö‡)/Ì<”ŽØÓþÒû¢èèʉ}»Pz®U¥^OÊÓ¤Þ˜®WârùX¥~¼Ÿ+'Çæ ;•G6( †t Ÿ/äšé@.×ŸÍæÂÿ¢\!›Íçs…l!Êæ ƒÙÁ(“Ý©ld±Þˆk™LT?w!nTãóëÍW­„çæzQ¤^Jûÿ©QjÄÕÙ¸6[jŒÅÕ½ÍÉ™V§ïtüËA§Ÿï;ÒúüžHß¿q]nKÛA-jíJ]û–¾f ßâH:}¾r±œLÔf“Z2;_ì®ýtúK¢»†h½‘@¯¤çþE«Ïý[géWwî¿æeáW­w½Øuè…Ë[w'ë}Us×fƒ÷äúw( ßÿ‘Ëærýý«ÞÿQðþ^HŸýÈG­g7ŠÕ™ZRIª¸Ü|Èþæ—V?ª‘Þ Òû¢òh„zýÀÿ÷×?rtòO†¿ø¯¾ùišÿîoo¦ë?ò¶G›é¹#^óówü\3}ÿÏ}uÍÏŸ¸ï™¦iÈþCi:ùÄ+›é¿¼xO3ý󻾫™Þ÷ÌcÍô#ùH3ýƒ·>ÑLï9ø¹µ¯™>óØ‹?œ¦Íô½ðÖfú¹¿ØL_û]ïo¦ßûâO|øzÈÿX{Ý¿4D:<â»BÔC¼/Ä?ñDˆO‡ørˆ¿ ñâPO¯1âí!Ò¯~0Äÿâ߆øÕ_ ñW!^°'Šî ñ–„¨„x<Ä? ñ“!~%Äï„øZˆçí¢o qwˆRˆï ñwC|8Ä¿ñ‹!¾âÏBÚ×*wúœXº½xYÔ|APóÙ°t[ý¦÷†¸?Ä™o ñÎB\ ñHÔÜšD?âGB|8Ä…øXˆO¶—ù©¿â3!~+Äï„øO!¾â/ÒÌÃòq$ÄËBdBÜ"âM!î qˆ3!Þâ!.¸ Ø‚'ƒñ¨õoºKcÅêlòHúÏ¥§qGˆý!FÊ 3¥#ö´¿tç¾(:ºr¢dýFT©×“òt#©7¦ë•¸\>V©/=159|zúÁâøÉ‰_kÙ`(Ÿé@>_È5Ó\®9¾-Êõ²Ù|>— ó円 Q&¿ ¸™Åz#®e2QýÜ…¸Qϯ7_µfœ›ëE‘zi+õ߈ϕ“csÙíæ‘VpappúXªÿB¶eû³C¹(³í ¯ÆM^ÿéöÿTH‡(5âêl\›-5ÆâêÞtê㇢è–hiÃ9èlçqkp LÜ×û‚³#.·¥í µŽJ]Ç©Lˆç…¸+Ä‘tú|åb9™¨Í&µdv,¾Ø½çO§¿$Zqh­7€k“n½‹Vo½ÓÈDW·õ^óÄnÏzú6Éý™=­ô¹É}O;÷¿·Nîéϧiv“ÜûÚÓ/jË\])önRНFWWŠï;Ôú«+…c0¸íïŠÝp¤>ÕN¿r`— ²k.íûß™ûòóŸý¯^6Ò¯ÝþÓ/ÙíRõÊ;ÛégÚ»#·íVIvǵÓߺ¥•fn²åS;ýåÎež›lùa·uöý‡ÚÃßâ•íᱯإrÝ,Òs¸Ýü+fºë½wó>ùÃ_yv8H~í iúC¯ÞóùáÝ+Nïù+6ôÎ'Ûéï´¯?g]‡€çTú÷¦ôoP/nßµîI‡ç£¥{E€õh|6}6óÁ&_=>1V>-=råN•áÓcÃÓ“#ÓÙWŒLç^]zðÙÜ«O=}zzr´-Ý×Ò·þÜ'îŸ~ྕóßP¶þþ§ôYóíå±ñûŸ²Ù|6¿âýO¹~ï¢gîIßßÒ7‡ØW´:-½ÚáQë„ûÊ›FÚ]¡û• 7ª­÷ÿÙ¸±Í<6îÿ¡¯V¾ÿ-—Ëçõÿ^hµûoúÃ0¸*­ç¨Õž;ÏÃÒä{Ù仿ßlÿp°¹x°xrêþðé•'Ú;‡çgVí¢ÜDÿxí&5±f“J§l«W\];_Údl¶ân=u*ëy0|~íèÜ\2Ó˜w’©.Ìד̹°ZZ«qaÎZ\¯ù…5qúìTq"}“ü±t£›¬»oææ¸z6²°Pî:V=pêôð}Ó“i[<0¹ðpf®Ü<#Ù| ­´ýcÒ-Tlnu;›Ãç/QŸ¯-,^ÜR‰wü(zu¡[s¥ÐÏ_*ôôøðØhõâ%ÏTãÊÖNEË=Å_y"S<5}bbüé°µ/¦=í•iO+žÊÌ,TßÔê¡­¦*úW+ç’ÚuZ û:«¾t1l"jqºŸªÎ†¶~¯úÛÇG§¦KÅ“£#Ãã'›Ÿ“F¦>?›¤Ûµëe=¯(óñ³ciSI[Ê77[Dº«wÖz{û[¿^Š¿b§òü©‰©áÓÓé ïؼ:mï…F(üòýIãÂ|½Ýж´4+ßoxýî5®¦Âo91=Þp®ø7¬ï›*6ïñ¼cl~öâÂ|XÊ…¹Lc¾’d*­§Ì+a ¶Vø•÷§_Sá·¼Š¡ÉO>Ð|ÉäËŠé3æïë?,ÄõQþU·ZÏ•âF×ã1¥‹S'îÿdñTzI󵥇ç3’ÙÌÅ…‡“ZØÍuž¾k&]²jæañæ^»µ…JÐÜ;ÊW,æíW³t6½)÷ÎKY_¬\Y¼×ßx‹w$½ß“á1Êpx³&¨®¯§ê}\»Ìò“ñ‹G^l{¥5œ›¨Î樋‚t°.È(.„¿ÃŠ`rs uLφr÷§¤5By^ÍâE^óžœÐÏÈ}òhÝôk âeëzi1–X™Å³­Ùê#ÛàÔ’íÄÌH_òǪÏFkϘ¡ c°íˆÕ¸¬VÒ5ßâDw5¶óíqlþuu¶Á‚SÚÈ5n 1Ú`Àµ¾Ó@hxÄ/*=(Í ³Éµø*öóÖLP…»ø’söûsz êH¡~$Ü$¾¬£UTR¢™wÛwz,‰¶žÅ~&œÅS^E‚Žÿs’PÄ ëÂxÁ©/Í7÷#[0Œ­*é‹ÝþL{&6ïX¶&ÓmóÄ™ œ@6QöU`Þò­¿ø˾˜Îl‡ŒI…ÏtÖ"+Ë|wÑ‹«¬á”ÒÚ1±©¹Eì5NsÖÞ“TtíW\=³³ºŸ‘’6[ñPª×ȱoF‰Øå† ¿òƒ>“!.&‰>x&"¢\/rÕ‘Õ/ÛK?ê 5µ«ŸJ‡xB˜Y{ÝWà¸6h2|¸Å±§UÐØÁZsˆ{aéÎn<ÔÂ-×< ÃNoéÎÂñLà+D!ë£Ó2©!Š€½¿1ÅÖo´¾gzÛÚ‚ö¾*AÆ º‚S&öBŒµ©%±;Xå:M˜ç “µdð‹i™âJ}¤\áaI&›@GI›Ä+5ÐÝ™ÅÔ‰0¿Ê~\ùu¨$dò½Ñã‘ux!ØŽÚŒCàÊ {°•ºN3–X²ùŰQo -°/­Ò$ôüðñ¦É;¡¹zrþ’;öLgYú ¸§û5úhÄŽûíßFÐçàÁúŽÍ>üÞÅžÃúý;9é’5°^~ÓÙa‹sÓ˜V®1d¿ï£7œ ðÇ_Ëqš‚?ˆçë‚°T ËÉ…‹ÁÁ¤eœ#—ãœËʾ]ïМ¼ã1ºòå8-lû‹]­#èÊ›É'ˆ#ÛÚ“ÁIv9‹@óñ‘$(¦‹ó»‘]9‰=ðvg¦Â”®Æ Ùô•šž¹Mœ&¶– ޝ¢M=¡` 5)H¡©¶L,qx_k£~KñQyEÈx7bŒ¦ƒOR º¾\lRiÒÊŸ‚¸àU̳m3qüÞN$L%ø¸{¢Lk¶ ¤Ë8%©ÃÄËñÖ<¹ ŠÆý¡íWâᙩ&n®ð¤5ægèptv®ù‹¼ÏcÙ üû¢|ºØçºLf:"ø¯¸6Y:È0åøz‹&ôìp1SŒ´ò\˜FÈ'ªÚ>––ÌžÇL:À¯ÆÂ;£ÑÛA(´ØDUÜÝ ^5ËLÿ†;.n`VmtååÏÔÐp³ë<ˆ?¦…Âm‰þÎ\g6“ª4ëã~ß­P÷[òÆeÐDsö×2¢ZF³QBIf›žŠ¤Ì¦Á%Û±ôø…0˜‘ŠB .c]»@¦'=Í»ð•Úiʱˆ¹çè+#/¨ûôžº¬½EæJ aÅ¢© ;À¨5«EÞl¾])öÑpeÏ4–à <n~…é„W¸cùTœ;® o!emù³‡Ûá‰|ðŸÔéÞšÚ´)I™¾uzJ÷ßÿŽsôthòd¾ê6³B~ߥ¿óVÂÓ×òÔqø<·¢J•©Ä—£…#Ç@uÄNô½ +éÍ„¸îßXáB7m±I§MáEäq{ “?=Š+Ì ;?m%µrsN£ÕÊ,Þæ5•ú AD–œbõ7HdUa/q€B:HÌ]‡Û·ƒLëZüe&œÈTcÅ­[pŠÐ“U,¸M¶  ÎÓ.~í!¡*¡DfÌò6g ù¡ž úYãàÓäýÔþ%b—Ó@oi½ùÔŠ…_AŠᦨ8Í>çOÞM\Ãòâ"Ø!¹ÝôGZB9Êlbþ8æ­Gg÷»0‹c˜XÍd¾ÛIzšzPÓ.iˆY3êgû°ä‘=dçB–ýB$ä¿ÐØñÊ`}[›§là$ƱªX¨ûŽÆMEN¥<‘…¿\à¦þyti•Uh5‚žô¸ýÝ/NåÃka¨÷<øAïJ=Özµ~ìg\ί ˆˆ'Òé—@÷Ù4XÖ¦Â'¿’ƒà“ö x* Ë”ŒY×ì™T¢­™‡‡³¶-îGþöB;㻈’Ê8¶½D‹J]àCT³†#[àALž×ò áPîžâ¿/fý÷wƒê1 Vý °B±«ÿ}„Î߯‰GôÁQ¾)=m@ òÕQZÂl‡yÿö\CÀЙw—F½sa)$ $–èQÿ`]&VžDêOÚBþ¯\šÕ›KIùZ¸ùÃe˜Hb³í,GΒɧҎî ð‰m¡æýéüÚÙñªJ}áûlæ²å¾"¨-N‰iÌò†Õæl¯vâéçôÇA÷øñØ¥|8þæ›<Þq›_²éݳpÓÚl¢øá>I«2e?¨A´ìÚùå‚på)Ø×7oȱ/ wÃîmä87“˜,ÝF_Ÿ_‹7/Õ‡Ä[Z8zMö_b ƒÙxìÖ|i E—×â”è,`è&•ó5¢u©9$û Ižq•¯qUeU‰“X¹túdÎÄØ*¨:ã¶Oöâ¾ÓG`jÜ $©·¬'›1Ë©»àá³,~äR\òb5s É„’“áãÙ?X:TkÉø#Å+ñ¾Kò\ß½0`66ÐyM’Y&ÃnnÜ åKUHÛãɬÉRŒ,é6†w¿ˆ£ý2èŽÄØ1|xɲ¸mø©ûkÚp:œ™:_$¹ªЕ÷—*ϲ  ûÞãB¤zçm¨·räš)2ɇ¾Ô½Ø™E†>Ç! 2ØÏ‚7Øåb Ϊ®ÙãÁ0´ ï:VÎý;8r¿¹9íbÇ-åIRåb/D7¾Š± ¨þ©‚‹n¢ë—Ùá˜Ø34U‡ElJ‚ÛéÔze:²­‹šÏº‰‚kT T–Ç-Ïþb·S³`ÑÂÇ8eÎ$¦{Q»ŽÀ+*”Ù³Å_iöÒcóˆÓÝ+[´R‘«Ó8Ö¢<Ÿ…Ï8ƒ=*ªØðÚüŸÞ²œæ)‡]&3°ÊØ0°5cå~aù”©WŒwƒ–]Ïæm6‚ªe0¾n)lŠt£núü§C‘¨Ýk-|.ŸÖü ž¯'³ºš5w<ƒ¶ï›É‚f¾ ²rSÀp?™Ý“æ³ñíµn½é õ°LÅÁujl3Ñeâóëéþ¯  ãOläiIÚV[8n$Ý ½Fš5¼žmM½0{•6„ufLb/”ž#±¤|èéhˆ;§y\+í™Y{nŽ¡Ý+³P­¢“µÍ$®å{IEøF¡²¢:‹]' ÉK’8CiüØø^¯4JçcR‹÷Áî’xç¿ÿ4¬ÅÚgxâÙÈ ò"â¼Ü¸ñ¨8Z@iàjï{Œhî‚lÂY‹Æ<:ÜɇßÝÀß*çqùxôÐH›ù½«¢¾ß×Å0V>Ñ,•Á°jþv yæÿ›ß³ÀaæLZ³½¤‚‚™ÏÖjjwLœAþzä»+üø:6µ¾ã#nñÏvsÄ|Ý;niò jt]š¹s©à2÷9Š2Ͳ‹P%ÍÇØpöÒ¤’"ý«8¯}AqÿK³© '©ÔÓÇþÎÌ 逤xTb~Y‹èa£øÅc€ÓVцÁ\PØ+˜Bt¯+ܼ’4»w@˜o²Ãž#Zø÷è5,b¼' ¯$Áy²/÷6ݽC6û ¹¡ßHÀV“ÀcaÄe–?v-(e™³¿Ÿ« ™ÚNm*¼Þ€ýª{p#óǷ练¿U¶_jÌ¢…ëv¸âH©ìÊ%=vF5ßdç‰ë­÷¨|"‹Ö«±„¡Ôà›Û9Í”u·?Ç·¿{C x3î @&m\ûrXHÙ7l¡±…=»1‚Ùˆ5!û%ñLl¦1œ—ßåËhdí\öÜ¥¥lC›.‡ÃéªjJ/|g7ÿ  Sé\Îy¿$Mo@óünj|xÜèßI•f÷áx__vþ”;4ûHïHObO·¸Ñ¼U:ðîéÖ¥®‰´áw_Àve8Oí À¥†§?šŸbûï²LzP âUß=§îë ÿàÕºv°âæ=|M5$~s$o‹.­ü;…XËÒܡÅذgS&ÀðÈPÈöpP**Gó®¢ôÁ=Hä´ ™Ó‡{æøQzLŠ!µéP{mkÍÖbµ+ÚIOéh[ÑDûz·C«¬6µªÄ‰O6°¸¨‘8`Ð’=˜¨Çü  9Kà˜“óÛû„«3x‡7¯•†¿"© YwÞŒgÕ /°wÕj;v%Ó–ÛYÜ3 qXTl :æ}ô" 7À‹@IšÈ"ÉÍqsÙé+h÷Š÷\HÕ}núêR:qÔ|°8A¾-Íçl@ו„Â`ª{^ '$ñc÷$K³ò}ÎDj®:4Æñ8½\ §Üðƒ5ñábÌÙ· -:`‰ÞÍ•gûíY£ÍG.×â\Šœ¯F›ª'1©–´zѳ¸*Æt A§ƒTK.›—¤äس—cá±VëúW/µ˜‰ªýØœÄGí–öÛ%y.‚`­-T©:W=R¯o: WÁ³Å=x¸½›tš˜3a±µ3aŠ~Ì;á ±¶Y‘ÿù»0Ù@ï«“ÒˆDf´Ü‡ê¿Òa?MÚ©³âT&2y& ç4à¶ÖŠ”†ÖeŒ™D ìGB³…S–‘Eï«Ôú¥~2{üJhD»(@+²…þ è.L%?"3á|ªéþFjœíÙßó*Tq¾M2I Ç³ÿ›‰¶ÅNAÛØ/…Ö,Õk¨äòVúÿ;„Ù÷Ïw›d©êÈÝä§²>»¯ÖÃǪÀs¹O±sHMª XY_Ãe­Tãf ßt?ðÂ¥¥¼¥u¹AD¿È„Îs9„¦*@?†¾Pé¬k4ì­P#Oæ¾1þÖà³ûŠ©«q#¬~¨O/­›Î]ilg®4n´tgo‡Žà]?Ñ×r?©Òõ›xªa´6Ô“äOÚØ°ÌeœïâK˜ˆûë‘è1j~Fí*4†‚ ,o½:‹ð—DeA ¦úPAë (Ö Á…õ£ ïÄö¤v °ËšgI°õR"L¡*^œ}|ôQ×X÷²£ñGÖÁí«ÍÔù?µ^ª§ß}Äz×h„õ#\º2•}šbÝ«î¬"6ÚÏÎå{ŠQÄ&ø0΄yjðsE!-´>޳ Qò»Lä:›©û $[Ž8•V¡L÷ ÔÌYö^’¥mw-ðÓÅ: û¯oÖ1wfŸ I†$Åj; ¾K%÷œD'Ö뙿™Æ^Äýk@¶\’/| N%sðÓŸ£¨ícÚ{·ÑFû¸gÆòÁÀ¬VÛD¼°eÈà#P<Ï[_ÚÏfŒ‚°.ðJM£7'ŸÅŽï4g¶|k,`%kMqDû"&¨HÚg¥Ñ‰Ÿw0ï°[œ¾î\ø½¹ ݾSMüŠù©þI¤tþ$(^d*5Z|D°ó¹Aa•6sÿ¢Æ„“RUm¸÷/CE΃{f£ÙNX->ž¹ÎyEÎNWdjÑcÿYôVÚQõ-ìç¯CBÿƒwñ²ŸÓ xP»Éð)Áˆuvbk«Ï4¾-†F­§}‡îáðyð Ž1®àµkõÝ:íÄÑò¸ +{»WU„2<”QÊäïdXf-?U Á‰ÁùDòãh¼9Û×ÙóŒCU@ž. XÔÇ/ÙªÆîËY½»v°~Ó¬Úrç,7†¤;ðÄÚO´á® Þ},—ì+³e—b¾Ñå‰RÃVþAv-ªÐì'F×ÔYò¿)3-†N JbÉcLa Ù\Çmç•sJAÐÝF Þ¯„*º˜y®¿Ž»_,`Ëæà/i¢ÌçÑf¶ÏB46ÆB‡ðˆÐ½åš<ÅÄš|_$†˜30í ¢RûÌkc¨î:OËÿùÈÁ7D~5ßï£éÏÙ`»¿]û%¾œÙ’˜¥8wÿÚð\…å¡ëB@¹ŽcÛ¦S‘ÂÛ˜‚,ÛJ{Ò–ëfŽm:wwÏ7bJÊÿQøi:¬±ƒ´à_ö”«²Y0éZm©†VÕÄö°1¹8ÆŸOùKdôg’a»Éüß­etIèX/Å¡OUˆ–ð îŸG/eúAF–' ¾“@ìYÁÞ´ñ­8¤œ)Àužð^Ç´ o¨ß²gâ[éñYù´Ó_ƒÅ½’dWÄŠŠnñÿ¦Ž;æœ%$Xã¶ËTÂ*™zåÏYÏç·°ø’:Ú%²¦Ó{Ö ž¬-b6U98öR‹ô¶¢™îõôÐw†œjpTrsóHíè.aWìzÔa¡¾Ûó¡lìÜdïLûF ØÐr8ä<–‰K{0¿3íh×…§F¡${\6ÊØ>oyˆK}˜|K4d¨Ë:ŽH0¯K7hRàdØÿ÷%•9Íåœ;‹}öŒ…EþtÞ„1àМ¼®çáödW–è U[畱àÔèíS“‰£A+ÕšZ¬ß çÅüQ[F8éÃr>zÑ=£©™„&¶zWKãÝ¡é±.±žá‡dš*ðÇÏQ Y§Ê^Í„xÑTóé'ªÛ~ƒ~‹L$ëåm@bð86‹vÆ/Òí Se4ÈÆvÑ©¿#ð@‚9aÆ®»G÷{‹áC¿£ðo¿ITÌ$Âx/&a6;wcrk?ëVç¨ l_1H=¦fAVÃgÞ†_ MÒ³˜_­ôa26 ¤çÒz"û_æx‡EG.ÕÖ,Ò´à4yj³‚(¹’kF9P5e48\ ¥'>j°{­¡ë¦‘™–ìÐ×åBÁ“ÑTÝ4 Œ†ëˆEæW¼\ševmÀNÛ4Xc Óì«£F¥Ñ`Ø.ÃnŒJ%¶ð—"u¸ü4•ZÈj‚Yò¦3¡y.™Á?Ȳ3¦W¸7øéª·˜hÀ‚¢ç¸ýqu•; Û? šø $êŒóV›Jƒ.*PéX;º+E’5ש—ÄNrŠˆÂÌl\Á¯ý-ìc‡#ìݺ¼ºQÄkp“Ø”æpº²@*_gáa÷ýÍeŸ–Àµ%/ñï j÷^Š.JH'÷…þlð…8Æ85ñ·H5ÓUDoæö7;²À<–{|7¶ŠØÆ¾}›ê›˜¿ø¾ž€Ïi¡†38ßßÁ|.UáEYkYÍŽêÝÝKO˜Ìr$àšKTÜx%ŒäÏÔÇVøÓÞ|”ù(Šï€ôŠ}T!9„ù †¿»í˜Öd¦ÑbˆŽ·9€Þ@£ÍRÀ©~>«qŸ gª%±”º*ë<«ÀÌÊ;éæÀõ°ÖIHƒþŒ&×¾ŠA¢± <À˜ÎmUµæáQŒ}„3[3È\ÞM§+hbÕ÷Ò’b¥ÍÞ0ìÐÞ¤ø ,G-ešÜ2Uv"³4é:’Ÿ“ÄH«[}ôG^ÏyL”æ:¡Ž1>øÞy_èý«ÅVß ›_yÈœ«âü‘gæ7o‘zNú…SpíåoÓ¦Îò»yOð ÁMÚÿ@Èlà>¸3c«+4¨dîu"BUkœP•ÉGµ}§Ù5–¬Kð·×²SXùt2ÄfGáB‰cЩ´›ØDƒqûæ÷Õ†3—±¡¯ö¤Qá2ueÏKqÌp(*%!oÇdÒÕØ…"·U‘ü€UÐ}ZÔçΣ‚”%¬s¯?ì¼0‰ͱgó¦k¡<ìI½OxÕk%`pd* çp@_8“鈖Íó‡©üÞ6zû7¶Ë¿P¡„? nÃ%.ã bôRU¾šr“XkenßkÏ>xÆB]ïJšÛÓ‚¥ž`Ú3~JØBP-Ñú’‚úX;ñ/J<ÎÂ|h…N¯?;‰'ÿ$Ýej4}Š\‰o|ÎÇ•£ `‚“<›-•%¿× E’Éi¼xJU2®áy;$ G\wŒú‰¥îr1á Žˆ~CWéᎧj¦0aõÚÛ8‡Ú„´4Gß­#®Ãñv`~c3½ã‡I÷›9lÙÎñ °2 >ž­&ñ'÷ƒå:½ßR&æ2£øVRÀV­ÒÇÙÃmt^Š >~ ¿X6NÝ=L⡜×é0ÅþÒŽDç¶ t肸]öË••.^Þ]¬9Ö”©øê Ô/[@—Z.MtÞ -‡¹ðÏpA/Sø”@Ä Ü˜ý{S–d‘Hã_,‚¹þÃô©D&ROƒjceÐ’÷¡y“AýØ ¦ÙSqñt–T8ö{ÑíºÇñ·N3hÛ þ”Àä•6u™_.`ƒVè^¼K"$óQ(Š3A!¿µÕ aô!a³É ÌÐ$6/ŒÎ-bŠz  ±‰ BfÝ?é£\ì÷ÄôkSp¶úQ¼ËCèd`©ë"Ì7U‡rÔªa {|d+Ýê"}D5ˆÔ ,sÎ]y4‹ý5VcJý&øÃ“¥¬"ÄLjä@)(•uÙ œ¾Q™¶i !˜]½Oس*€éð ‘~¨‡,ÙÙëÇP†o 䨫G›“P…&zÕwšÄ*ï†M-þ¡‚å³°ú·ª`c³œõ6Ù±Ù?ìñÊ#NÐ QÀ—ÿZ §Ü'ÐãáZøÇè$öíÎ-"gò…þì|Iãv[0ÓÎpÛÖ†éO7Çõ~âÚx}ó:#Œ’XHlî|ö™Še¬†6(Êl,nN…,?øµà1v¨M¥evUpÏum.0Âg?Îf÷öçaÆr´ó4‡D«F’ß(<ˆn_¥j¾Ûq¨®d ë펀Ê èãDÍVϼt %£ rL.¬–ÅsHœòë?ºAOÏL£¹åØQ9žéeùÒñ§w#qm{Ò³«Cø ÈeÈ4Ò¡쾿+d$mb?«SÀÞ]mK¡›¼ÂïÛiþ g8½‰E®^H¶¯ófÆ×ðÚ•ó雿§r\è*–G[Ž¡ìžh£R™u#—zψ«Ú—8õ¸z`4•Å]šGnŒeF×YÙ¶¼×4?Hè µ‚áNãmœ&1„ õ³,™woùA}p3±Aš=ý• »ècɨ:ê'îø—)t—l§Vö"0w¸˜==1³†±¬íX4XÇíD½!xþI0?dÙRÓÁ&BŒ]²Þ müE„Æ£wû±Ï´ð‰=¨cæ4|*žÆ,•'ÎåÈÊ;W0…¦pÿX.‹\¹:ø|‡Ž¼[KSVî4nG[@¦f#NS9.pÞ¸ÊvŒÕW”>NžƒÅÊ5¨ò³aj:ÖŽÜìÆzߺ³ [`R`9SP2¹Æpôw55ˆ)dþ‡NÜ  2ww3,úê_=„°²Jkì7ú6§µÉžý´~ÆoJW6-DûÖ3Ÿ2¤¿уÒ$ð’»„oß°„íÎz¶´`‰sÙ¡>uVµÕû‹ZƒË䯤pu"„çO ŠÆQ;×u¬IÝŸú=Óeœ¦¢ÇDZ·—Üà¡¡8ÓTŸ‰¦ŸèDZŠÚ¶õ=ù´÷<zRÎ"W<ÅA­jÌú”|ÄEøGóZ¼«j:øâð¡J šÊ©Ê¦íp§Äfû_ºÙï`ÒWálÙå$B»ùäž@R£B£‘ Û£M÷iVàÙ1Ô¾ØÀîf€äiž,¹…Â~Ïd›£ñ‹qràµÈ_R#äçCU:`·à1õlV¥úщø´ÜöªyÕÉä¬Á Ýn ~Õ§vWÕ˜ûD ö-ÎúwlgmÒ«@¡÷^hN‡OeB Ó¢õÓ'€ö½m,⇠mNgåwûpËØGt®e ÊWÄn+S@¥ä/ûôÉ>bÏmT¿A’¦@õ%øÚ6…Ô½tdþ:º°`„ëø4› KE"EÖ t«á_¬€<MüAb7úxω•íO ªw¦°Õ“[è@º‹² †øŸ*LÖ åÙº%j7¶]'ÆÇ\qPg&ËÏpÇ9Ʊ†¤(ø¬•‰6Í9‹µeLÁ`E$8æ¿Äo•ÙLéí‘mJT–#Ï„,öíà lúi&ñ~dޤæÓ}£màÛçd\¢…%´< &L«<™FGÄTk¡1Rä¦J/­/ü—+&jÎÎÕ¸äùQ²ò¼Ø7(ÃoůÔWÍ3FRó€LôRmŸŠVÚü …uKPB´=.Ú?›Ý¸±”²{ˆÙÃýL˜§^§Ô!zãzLy/Ü?©@°L´ÙÇJí%°£[;á/Š XÜÚ5v¸ó)4Ô§úó)Žc©úË&ô³ÂnnÆšf1ÔÂÛŒh]û:›y³oí^dbÍHp|LFBðüàÇ<åórí£U©;’jÈvéb¹[!L-æD•#]µA¬0j'“¤b_×àÁñlõÞس_E„€ ¦’ØÙ´`ëyû§Á`»t »è+ß-²-œl¯‡cï¹±ì‚Brsø§3W‘X4ì£Å&eo[ý%E»qö 3½Äšî9CÇ¢ƒ¤ìƒ^ua4Þuøp‡Ôö,ÄOC­sàUðg,|y®Ž<È}æôÆ™ILR3’~Ð\L\u~˜ñËb’fsO¶Ë{ ‹ðiÇÞE‘,ÊD Þ+«ƒ²ýg¢ÿ²úÒ˨æy𣬔EJ)aßÉ¥ìùìSdÙƒÔTNŠ‘°R#‘Óê97_¢wï)‰;›ˆ2Áø”)\êM§®Ì#uœf¢¬p¬SéU<Û1€—Í´¢Ì7ò¬EòIb`v»?ï*—µÂ–YÜÐaS3÷SÑgÑPvJ’™_ YpðgU¨Ý`FϨƒâìDqµUµdÒͳ!u¤}Xq éçC´ÏSܬ­¯_7RIé}àþ%—88¢©µsÙ½¾:K­¤Ù¨ò’r©e°>$LùÊ·TŽà‹eNá2ˆëmc÷¾¸Aùªä¨Ü’Þ)Íì“/“‹¥ñìÏ{qö&'¡„p!ˆ˜Œ€¤è«•¥DÎ1ÝÉ3c^unŒ8#W…éÔôOÙõø"q}I%bÓ Sl_Æ» !ïè<zAé¥'áÊñ-´ïl8 J¬g‹öà”;Y׿u1¤¿´”uL¾‡¿nƒß9¤¥ó{Vè¼ÝHãËaèš ¼‹h¦FûìÊ®åÐaxðßüÂìÅ®²ò”xm8Ãwô%AòŒz»i=¶_Áú‹çáÀ™VìúzmÖoßGá¾9È;h±£/’J)U°µU†ý¾4wT<¬¯šÆ¼'½À'´ÜXŒ¤=Ä~ßJɺ Ü•!€ïuàž5L³›óŒÛÛà ©×´ï›SX“Š<¥?ræZ‰üöã[i½ô$Ø[MC–¢ðù&ôé—4h¯þWÏcÃ!ÿöm¾Su·85ž¹x‘=½X¥¥ëì0Æ­Ò¶à[þ-ì«Ü¨ŸÂz²¨b$û¦Æ~=<*²›Ès©NËñF¯~È0»EÓX¯~=v=ÖÄPŽM”€>=A§Ü?ms1s/³gQv20ûTKêNg1H›Ü ƒñýÜ×_*V¿»bL7í'šðk7ÉŠ³;ÀÃÖ¬¼.Òû¦ ؾŋXÌ„š?3Û®Qƒ·ñÎðBD•fm«œ}Ó fû®ŠÐc]Ö5/çWþ¡—ǰÀ;°˜!ź9)¬‰~à#Ì`ÓéjòAD ÄôJYÇ¥ø«Œû1% r½E¸Ê냸»lô'À¾Ç#!ã‚ëÙP Ï¢nµ äy±{«¼+,8!8Û: ݾt˜ÜÑ€gÏ5ð³áë\þ¿ "Ⳟچ1ãÌ *KÊßýà žÑó5y8àÊx(]EpÕo ÷ÆÞÀ¥å?èùŠ´hš=±5c~“ÏÐÍ>ÃR–öqŽ„}EÂ×Ù°gï͘njWX©l;ûÔ Q?p©Fí;S ƒ%«¨¾„;(/^Ã֜كÆneùʵøë‹ë´+~K¶ã"yp>5Dº|B2G.‘%³¦Ò«›rÁÔË´ˆÝY¯ÆÔï.‡ò.Mö±Õí ES\À*ˆLaÔ5fàИNì:¸ŠÝX,˱´Û´?þ}|_—j‚T½ ,J¡’Tå?Çq’Ïpꣴä¦+N½‹í|.À$~&+üíµóæSŸˆ]8M} Ìžo §¼ÅO’DZn;”=2¦ßVkÓøy,àÐ2®ïÇ|¢q•—ÈÜG+MšûŒ©ñ±ýÜþ€ëxèì$PIXÍJ§»ƒèÙEè¢CªÈÕcí…ÈoÚ,×; ê'Qðëò_|°u$3ý+G<Â’‘UGS¹`Ï^í–ÀsctP¼¸ö^¡AÛw|4 zñ/{ÊrÍ<á~„9|¹+B Ç•Áù/Çè‚ z'ÓØ•ÄgȸwÛ™¼•|>®À¢Ö31ñ(œtn üâDвGÒ>4­_Rƒžgxƒç%gÀ·¯KFLÚß^$ÔSšc†R[IôM#øÛ%F[#VBp°m\Õ€?w­dÇu¤±ÖqG&«£Ã>ÝQdåé®â1žd’1¸§h I{Ï‘œl}°Ppe q/ñ•g2LR$–l$¤ù¯ X¿´†þ¹†:o$HûE£dñC㕌ó|n¡ŽÍkYŠá MåJ$.$â`O¨¬E"«Ï“ŽÂWh…GþYÍÂ6€Êé?ü]»m|âW™=2.P€˜OñŠïžØ4Ò „ï±úü‘LCP€ÄGñ•8æÑ&Þ4FÙÛÆ`ùá|ÒQ?;‡ÅŠI°áßÍ\Îõ£¬=i>xZ?ÀíJZ/; ÁŸðPRTôä14c l^’ÇÜ·É`ÇÞ‘,ÝEŠõœ zÏVÁàO+îPáAÔѳRðWy .4L†MAéÑy?Ó9Gâ›]´éÍ–r0Š|бã±#ißö]?júûPdzk–þø|€ ‹rÑgf׳iÍè0˜>Þ•éL›ÇŸÊùÏÏÀÆÛ[‰ÄŠ)àø4ˆ•ʤ eß,ØÛ¥Ëà׎)lÙ–F¼^ÿ ö5씟©ÆtuF%ôÝ‘ç^…¨Ó¯ÑA¬w„ FïLiEMWSâölzÆP´Ý’e£“PØ?ÿÞ¹ ÒRŸÑ_)ÛÁÊü1gІNÜþ@OŽÏÃ@dÛìþ-Ú4˜ÔMUdºü‘+ ¸`| “{E®Ü´ŸFÑCÁhɽ$.às+~~Ç€%=õÅŠ“ ðÌ>°±q„ïnE%“xX>[7yí€òLfVŸ_O£q§Ì+â6¯RWàÍûRÀH3ŸÔÅ–¢ˆ¢}»5…Äïér»&meGiWÄt¶@ ƒ|úTöSÜ—i¬žŒ#Íý¡ñíSb*ïš•íøŠ“)è|GL'³‚ÿ-­ÇÜÊ™lÝ{ºðal¼ñ‰¶XMæ?ÝÓ—“Áãívª¥˜ƒ~/ØÍä{öÐýºlã¨eàAßU !Ûg’L¸p9„^ÞŽ{ߥß_îìl¾Ýñ4± ëþ#–€Ú¼SävçX¢!-ÏÜ­à¼C‡Hü@? µ †éñAÝEÀ¬1°ãGJò±!Q±P/©„ÿÄ郩x‘Q)Ä CŽpqú;ÜUÿj|€m·Í¥ÏÆ]Ÿ/FÀ³Û†ôè"¾ûŒêX²½õYÉÄf9±¢âxùÓtÖ4?u½Æ1JGéàÚãPÝ©ÊLƒ!ëa+ ¼Ío|ÍÈA Ee´éävåM>^!µ¬Î#¥†Û‘×’%ÜW×H „xåyx·’¬-Z¾3Í'–씄»”+ƒŽŸŸ¿ZÛÈïÉKÿÕ³+®Òå°Ò¶H±Wo(Ta:AØrPgÆ@&[…ýÚ`ˆDÅ^àÕµF yy9õ=ú¿ÍIea?Í0 tgFÿ²ó@ÓMþ²xØyÁ¡‰ÖñxÄ™¬¤«>C÷7+\ûuœpõÃmL’Ò‰ïQí•oDÿÍÀ2tïo>‹tNÄ  áG"ë Nãx—ô5>—`µã‹ñß<¨z|g‰Ãy³@ýßlk¤Î­§HùÒT<&z+;º+›F® ¼ùš#¾!{_|ûŠ7ËT8LBœ9Û•ì`›Ðë‹!ð\5mÊÇ}é‡E#!s}[v?Wfy±4±ðæ•d®é&¦ÜZ€±ã˜o¸ Û7ˆáî[šøSò"ôîA(L8© ÓiÅS&é¼SºâÀîú)Ì=ó»O¯¥›S²!=åü½[|ÚÀ&ª<$VÎÆ /•f³Ë*éÍ£‹à”‚»—{XÈH‚Jœ2”çÒ„‘Sª†²·¾Fø™›>ë—Y9E§Ñ+Ó!”QcnÁe·?¸[Ü 9Í3í(7%ËŸÏôa’;ÄXè/š¸X“&TDÒ©çÍaØû×z¶Ž›ž>„žœË€ÎS˜C‘ I*]H7Ó·´>j$žxb$,Ê·ðýšAp¯­ Û\Í–ØUc‡Ò(ò€ëXèÀÔ»8,80Éœ™Á™[;sÒx"ûôÒ‹u!ªŒ£XÒR+ ³ãD ô±·bžQÉR/p_¿ƒ5µ7áç¢,ü¯UÞJÃ~…³ÅyEøùÕThœ4+v&Åù³¢2|ôî O Ƶ¥ÐUØ fhÿdV‹YBþÒæ>•ùp¼u/•vñƒ¿ÏqЊYÐ3ÇŒÁ$kDTÒK¼˜¼Üº9! Oˆ±ªÝ¹D"ANŸ–…œ({ êZ&ÓY›[öÚ É*Ä×CÀÝïÄji^ï2‚¬TQL| ”Né¥)«wƒW–;µ)­nN [ÊŽÌ_ÌdË ~yk”ccÂôÁfn §^§ÏËœü,P=–¶ç4Ù‰×óáé_J^UpÁNÊL?Ü K.~‰§À­Ï³±ësOºô­qd‘ƒ9ïS~\¹¼)Ûì2•ha5çÓ\„ˆ¶›ÌmvažF,ðz8‰Y€¹6„éløl]o~)‚¶QÎÔ<6žëűŸò†5»„é5¿B».l¥O½“™Í¤ ±"žµ?âÄŽ¥s6!‚æâŠhÔp9 r¾ƒ_nÑ![;Æ "V´Ð5&âø’‘•‡ÎÇ£ˆbö>ܾ”~ÅHb »)×ÃÅŒÀJllspMy; jNLeqšôëøQÐæt~­¥†œ@)<”ú¢‰LrdáF“ èáZ{S ˾Z7Ìüá O,8[É*H56W†’‘Pv1@ŠêOѧõ¯8üë•’‘ç€zR1~>o@¶oÜ5_ ™uBÔþtB~‡¸Ë2,ÀV;oþˆ­¦/ÀRC±ÛWH Ÿ…7–b—BzQ·ëUòkd‹o¸EãZ.¡Â^ òzÚA2ýªyw`óÿnÍäj†éÊ‹«Ù‹*üÚr?,x%BŽ(ƒN©vú®r´ü‰fŸòdàR%bëSôXtê|ѵ…½èÁá¡+@!O‰š¹4¡O7Öó ÖÄaKÔ0%8òA’¹:é²[aýýÓôxì$ö«"éiéBX°/}2ºv…MÝŠõ·x²º+±ïú LŒ›ÌNß ¦–+@ó­‰lð&!=}8pÜx ’7d²ÀõüHöT²¯¸`ÎöOý•j¬§Ô'8Xþ(Tôsóh¬tµtzñ]ìYr¨Ä˜Ù¥¶¬ÖÅ3àSvÑw+x¡sŒl:bkSßep,OŸ †ÁÛ>¼Aî.b¶D’ý|ÆÛõ7àÖ™°û|W|óìbhè–„†̱ւªÎÍ¥O*i³¶Û¼i'±÷%…û Ñë»›IÛp kcÈJø[h–G ³óZ}ÿæl݉I|BQ3Xß’£v‰+À+2ƒ…­­ÄoÿeCûv´ËÇ€‰”oeWR0$µü¹I´§ò:-Fƒ¯; mÕYpç¢d‰—е “˜¯uôWWп—&±Ê[ædß {¸8g>…¯{ K•´ U«íYп_ûÕŠå;/c=OhÅ—8pêàÝiM“õⲫÆpL½Œ”wÿâÀžÝZ„§¢•Œ}ž2yTÏ& :CÖ48ç :R„«B‹ §y’ìëD v'³Ù&‘yÛEA¸_†ž¥ŽËíá­ÀõëÜã•Ob‡|HÛ¦Âê,Ó ¼qÝ÷ÇsÓF³G]ãPq‚ìÞ„óW®Âýç`ÊÆ}ØÏq€ZŸÜ…CzZÐÿÄj:žã£Ó×£ÊZ .Þ„ü^çΖ¡Ì]r;¬uK7º¨€u<‚þÞñDbaTOÓvÛ<¸÷5ˆ™–(ƒÑÚ]4,k6,ZdÉ´KÇãQ(‘—züQ¾ëRêèHMqå  ÁT¾Õ÷ÓÆñNÌæc,¬ßv:ýëÁ®éǹÚ@#HÐ>F-ò Q´Œ„5L„w<™õ¥\`áÀnþ™ÅZæÒ¨ ¡P˜dÃ4sw’·'/â¾Vàû8;l‡VcöjÙ9|v»¸m5|ØLÕ~yøøZêîñ»L³€òðíÂα0è{¹¾‰í*Èà}46Š }®üBªª ´E€¡1®R(3ZwümLM‚FÏÝL­a ®ì”–Iáé>jdS¼!<œ*G\Û0V8…¿ÙÍ€øÎ'X6p$«¶éG&Ïa‡ `öv²<$Ù%’ ÕÛ¸)Í0±JåĤXJ¯[£²ƒþ1=Èzƒ~âö¯–ðmÙZ\•uÂôÙ%òßqÞʺrqÙí|¶É^›{¹ã³Óã™'‹Í*ÝÌ^€wfÞBnág›W¢§r¡Ðì&ÉÉ~têf'f¶Æ^!]æY=EŒ àKÇ€ÊÔ+ä¼YvZ³–ÕHOaïÅØ®!Ì=×eÝ?T¬ª¡Eó[hÁÍdVnz‹¬+sã™Q?}™?‚³x3ÁÊ‹‚ µL™VY;ÑþËðîŠDôÚÒÿ Ãeà4ãÉÈýþî±Î|ŠGo…PÉ{äø°Š:?¤·<γÊÉç£#é¼uqœÆâXž0‡<Í€)u¤í@z°Ú‘¹&– Ž&›YþVÙŠ«NÁ³Ó'p¿µó’#âuö¨`y,‹×õgFcðo¥¼zŸ–°rBû’˜Šlõ«]E"®0–wVøå­ …‚xh<‘ɸе¯bÑNØü€'›'ÑFˆ« ÿ²OÚ€þ87j_7 Ó~Ô{=ãYJ*  èBæf#ânäÂ6iÂÀ£¶Qe‹^WCâ½¥@Ûò -°Ú V—´¨SÁAD<}Y׫¤´C9Žbv¬“¦¬„ˆ‘sYñ¦Zd1ê-®ÞäºV3h}à|Ðì6ffë”Á!u,kž3|g¥Ó•>™ÔÀÞ<AuÄpö‹&nã¾IÈ%IÎsÙβ†ÊQÊÕ|¤zEŒ¿EæÊ@y¡ l/#úCóp~Öè \Èð˜MPU(ŠÕæôòΦ±þ%å¸ZäþVv«×ÉàZpüemf¶=‰žÏLÇQ©–¬sš"S®ôæKõFqø…ý©ýY\÷” ,t¢;ÿjŠÕÁϱ€õ.Ф5 õÌÜ öQÍ£@úé–¿x!ÛRØqn ÕiØÄ³ç³.ëX+2tD7âC<öm£6¡#ЋsQ• #’]!¬ë›#¼ _ÈÎÏucZïÓîB}ðRo£Wï&A\ûQÛ­ÙÌãp1QŸ¦Å<鳦Òïô“äJ8rؘýfcù¸œ\5|üÌø£SWBÍ÷Y¬zÌ5\Ñ"Åf,‚Ók¤iýLJÞ>\¿·Ï\c?#Ïò¡Zû mÞij«!Nòv‡°óoZQBüE$ CNH¡5¡ºÐ}z$îµ2¢aÛ‘•L2D×±¹ŸƒàõÓLtÿФ½W‡e/2á&Τð^<œ)}¿XBªÛ-l§8‚ŹÏCÛ¢”põ¿Z.ž8›E˜ˆs/¶ùòë§ QCòHw©«žt®µn¤&_‹Ù€­"àÉsÁX%JT? ¯«r©WÝtÒØÊR®ŠÀƆB–JÍP>?€z;×1Õ«óño¾Pd:Žúï{@ôrŒÑãEBG©U`"€ïâ©r¼9»xÝ|6fSù€Y¬~š~0EF?O¢ëB´[6 Þ|Ÿ}Ì"–ÜG›Ë'±Rçilgu³Ηû³µVâd—Ha3ÐØx›4I`Þƒ9ìuæ, ¾6ìÙžiàßÒ@ñvi$ ÀSÂ|w/—ñ—‘lþi(ãô0•0¤Ñ7ÓÙŽÍ#èï9øæâèm˺ä< =FΞTÂɧף0ÝXø(£ËM‚Š gœÑ_×Ùl~Nž|õbEØóa8~¤1® ½ÂÛXÇ©`;s ö¸¬Ç˜¨/³ÔR  ã°{Bÿ7Û”6~´biÍŸ)IßGƒg¬dÙcd ­Aö="kPåÜ ¾góO)ÀÓ߇²èJS¶`ÄXb=~.·~°ì¬<ö8ÙLQ” ¶p7Z‘ xØ"ó4l”>+Gá° ºŸöÝY£e:Ñzi ¢rƒäSèVè³›jJ+Ô-˘ß6À¥®P‹ûÖ¬vãQ:ÑÄ"œ\Yh¥u-“‹5ð›<’¶vFÁ™ËRÌ,ù>>òJƒí¿® Ÿ¦Ý¢ÑßuqÅS}íU2A`(*Š»Þ„ ÿ8rãTyW‹mˆg†1ÚBQsòm¡÷·x@ŸŸÖŸ*|»l z¿„kÆ3SÞôV\AÃÃÈüÙ4Ö7å)-þ…å^ÜÁFfçñÏ…k t† &Ë²Žøb4ñÈ)ú^Ó ß“ØÛJ róúlp^uüÓ5£™øBCF¶§ñíÛYÙ{Xÿf”*ñ·EKàñÍNz|c´/a¥ë¿bíª6×͑ڙrT¢Øœi™ØCcýbèX1Ç•Þ$‡¾òó¥qálã#Ô¥HšÙ»¯ç÷é’“˜YŠ‘‡ ö ä8š•Õ€éˆL¢¨|7$¯a:= °ÿêI̵c® |Œµ,4…zÑÐ`Þ@ÜÿÖ@Í%5¦©gÌi7«ÁÔiz" 7‚äÖ›ÓœYéìó$ÿcøÍTT"I¯=YÌ·Ko‚©…®!²Œ6~ýK#ílÁªÁ™µ¯¶cŠ™I|<}Ç2éÃ’hߦKÏ_tÃö)̃E'«2‡Ì'tpXƒÙ·†ã¾3YTïV®èìs®c jCµÄ7Ëü@ìG/¾yF‡±Ò?Š*à’/úðµÒ:²|a뛽hœD.dL\Jø7Zhó˜MlµÎµr'ѹ>Y0Õ:„Mð™ÛcÉà ËHŒ×FT ê ¹o2ûpØÔ[O¼JŽC{ýØúIñøÈ5ðò8‹5§¼B™Is ÆëÞg8†ÁiÔmŠpÇRs:òuŒT!fµIÅ‹zÁzõý¨¢÷ÍÏêô¡M2”…e€Øú³8Xv÷³ç(󥛣¡êZ>;ç+ËGO[ÆîIšC¬1f¹þOi‘Y+Ò» õ Gøü úøs.rL  ·­ 2Ö VÍ#QÉöìs€°,K¦qÚmÌÊAGÜ'€ÇÊ4’ºâ·íÅU:Õ¸Oj1{º_ ù9O`¢Ù"L?íý¡œqÿj!{Ѓ4îQ€¸ë0‘¡ÁF"ðmG<{÷i-Ö•›Ê’×,†ga»©@èŠe¸»S…OÜ@bÚWÚLö€¤o[ÃÅš{3¥ØÃøõçO|ÎÚ—»ÒsôìVˆSVÁâÒµäô›Q :Ñ-ǦîÎèwGíî#ˆ¾C;5úJÊjç€ãÃ?H+¿™·›@ö?ŸW~ð •ùHë÷|4•Êè/¡æ´—¨'¿ÙÌô´4ã¯?§!wf²°wŸqñ¶Í,ãÌbˆÐzŠíP̓¥Ð¿ó#z:„+D²?ãÀÅÏœµ¯OŒj¸ôÑå]£ ÎÇLïOÂûjg’ƒÑ$ ñ+?qr4xoê¿\³AœYO2‚¶…Խ˖‘3ÛÈö¿ÎpÞ|>£˜7¶mÅSü8(4Ðal½!¾ôM…i%j³§éwè†Mû ]S…ú ƒè€+øÈé1%¶>°~ƒ§ÎOcû7.F¥íúL$ÇÎgZ³^YKˆ^†ÅôÁæ&ãúì9ø 5ù ÷›ÔþŽeƒG¦ã@û‘ôe~IšúžÊž…øtC,=ÓŒŠêˆƒÆ^#¹Šµ¹…7ço×%òv¬·°§ëJÂíÓ©8S°ã~à7åöبÃv¶æ•|~ §? L:ÂKt? Ñ«yõ±µ\‘Ÿ2Î^çÃÎF*2õÆMdXsc²>PÿÕ𣠍¿ ª_¥scä@åµ {¯€œ¿#V§YMl¸Ð´W‰lÿà?0"l‡÷c1ɤG¦õ¦ £pH‰´…‚øq´$Ù—Eî˜ C’úÌê… ¼^€˜Ãæs®ÔûÝb±G ½š2Òl¥Êm¢z‰:o4aí©'h‡Ünx¤2†®]ò™Øs×ðÑ?}8îÜ Í=ìÉW­Y›R.®žË’º¼áq­`èñëLpM÷à¦6¹€ßÂ%lSæn¨èÑピ“HÕ¯FÚÈŒ×Oû»È3Ì‘)FéÃël#$°)#?p?©¨@h>°çVƒá‚ôÎ+‰$dVSµ¯³ùêyˆ¾Q‡ ›p­¸%\|P‹+vRí£{Ðïg­Èº }SPa¹ -8‡Ü ]PÍÆtMhÁ†”ÔØÙéA(31•)I:ÁùFU81³•Ö[¥ÁW=¦£cdT+®  å~Ì<Ó…˜þéÀ^~Álºè3>pßú56š>üÐÎõ:-Gb2ΤS!äSՠⵦP:CÀ|ÔZñ͵N,8|"_¤‚‡ï8@„©ñK*€–Deîv^àNÎ6Ö¬çHµ?ߦúíWiÑM56±.{F±£3ŒðÚÖ»xi—&üº—n_n y&3Xf¦t¬Òfý-©PþJ“¢+¤Å× Ôí&BJŽ+¨7Ÿ¢7ï‚ÂŧˆBÉ+aUÑ&2þMn@^Í+Hbé’–hi®¨á$OJA ïÛ ÉÞ뙵ÈVQ|CÈ\áÛÓEØ”mJxÿ¥‰:%K¿ß‹¾=>žã”7ìsÕ*bÐy‚æ61óŠLç×GrvÒ>bÙ#KëuCIì‡ÉM±†ýõføø nöaq_Î"§&Jìÿ¬çŒ¥µèà&p½žHJfDÏ»°Ÿ—?l©Îeu×ä@û¦(îpÏ»™2'†ôgÉÙQ#›>µƒ_op÷$ ö`ä þ¢ž-î«ôb}ÖmH7o.OßC¯g’º¨ÑÌ}ò ª$©ƒ#YÅü8PàÄÀa½"k8º’ߎaÊ+Œá’Û18›}ذ•Åf'Ba n2ˆ`_ZЦßÛ hù#^½q ®Þ AûÊ& HµLPÝóÿ­'hÿGÖª¶k¿ÌbûF±ˆÁ£¨ù¢)œZ=Ú¶ì”b†2g¦Ev0)¥$*æ=H¼.S*aË^z&ƒÿ9¶Ù²×TŸÁQ)FpúªÝt`:õ&•iœÜÅm˜¸Ÿ ˆMø‚[®ö¡8û1ðõ_/ØÔWˆê±¶¥+ íÍn±²$5 gÏìò¹¸ãóy­Ó{ø†–¨þ¯xv\$Êöcp÷E%xç³.íb稀˜¾'Ê4D¼/l Øp–<&jzÞh ƒ£²2~ ª9B•G3]/ÚZñ•OÄ@Ï‘ïDzL5 מÇ Ò©œÓròÜmŒO6)9ƒõc’ÌDH> ®Z é ·1£ß¦0°¦h·¼âoþó ¯÷@‡ÔjqjÄH(•¸ŽJ¬!Çê2Ný%à _«åÙvÝzISŸµìMpËD*ËlIp˜=þq1kø~‰ªZ•a©@Sîã Ñ»`=:ï@ |¹EƒM…€¾X8ú>?%c«?h‰f¯¤¢µ¦ÌEö$n[ŠÇQš{“˜úƒ¨Äi“µðÙh.˜tÚÑÞ6¦ž- ÍÉjÌÿ¶3-ˆE]…ãÀ±fðÜ ¦u¸¯[¦àõþ‰Ìó],ÙìªËìëÆ±‹äØt±%°äÂ9X‘‚ºF´ã…Kõ Id)W¦*§ýXOÜ>¬(fÃÞ>€€ÿrHÆbÇV f´¶„åFêm;óþÕ×aÍÀb¶v_Î 0FhŒ;ªÖˆ¤¯ŒVAqà1.*o2žl½ ÇÇÀ‹3ά}¼+˜ôW¢€%êôAÁuš¬+ƒ·„‹üËhðÔ¬é¸ô:¿û8Êçmš9-ºI–éìbjgè¡·ûÑ3›$$V×] È|¾K…Ó Þ{ O&³j› lÃ(¸WK¾–@î´BúÂu4öìcvç"ûÔ(æÐ~µ¤VR9©¥,i™*ö²[+^L¥¯Vž$ƒcÖsrÉ'PÆŠ`äý§.P§Uo}YZø<Ú?Ž}³gî5\ò¦Ñ0b®Yl´ Ò.¡\ûe8£Éƒm.ªD júìÕ ¦ÃF²K`¬,»wΔL¹Ú…§M öÃ(ê×€O-*ffæ8t’ ‹öñ8{úøå~žf‘KMA½Ã:·Â©¦D-\Ê'Ý}{IîŸfôŠH/Cž-œ+ÀköÐT=g¨_òÝú= w¸ˆ@ö1wPl1aÑ ÝïŸóÑÇvrIF2±''p]’´Ôúás£Œ™“áÒ¨qpYW‘Uh‹s÷¼¤;?Ú²KW=ñ¾e†`Ÿ¬žg‚H× Rù+ÇýZÍÜMpíúˆ¨³ée4Åz6Pp`Z髨•Ýü÷ñx°•PiuødáÏv.×Âj*FluxÏ¥JûÏàC¯ÔáÈÞéXóÇXП!»¡/Ç oÉ‘e¬#eÖ´3¢ÄMKݸHCËÆ€ÿÔô® åí)ÀuáõHoýw,ð6¦¤›‰ç3_QÍ\]‰£ù#:_B?öãž1©ü޵ØÉÌÙ„­d™—F³#–í„Ú×¢öÏkH‚£9j¼3šÙ&‹±›b¬U¸—¹AêOlògùlq Ê œè};Mð8\Âf¼(Ä -–ìUÕç~ü½yP~“8žÌÁR‚m Ÿ”¾@×´s¤xz;gñH„){­À!Ú¼+÷W¡ðä e/l› »}kÖ“ÜMŽÒ8Ö2M‹J½>EbwsDÊ~üj •ÙoÊ:´Vzƒcæ3Å=_-ëpÆ´6tªð›Þmþ°¬º²ŒÊ0ür9+ºÿ»^Åt^³ìOX01 Iùpv%Ïä눔mx2bE£!33оPscw^eÀ‘'ùôE›6ÛdVÌ»}ÕÀB$R 'éâ‹ü0>5C—ux,ÂUš¶Lj«+¢g¨×•`| ³kSþ™:]H¡:£-¨íysø©œÉÌäU©ÙRm¶óÑ*ØÎ²h{ÙM|Â\𝍀ÿ S? “±üÃx|êÎû­ÈÙK75ÓwÁ*P”œ†ã>C³ö%áÎy‹@nñ$¶b£#¨û."ôåT‹Ó‰·bß³šP3[?[ ÕgùCÌ>ì:I’ù}Êéljæ*;³ÍýÏhX‘Îÿù­žXŒòŽŸCKŠþpå÷;ɘûÁÄ}Ìhêw0•8rd©ÈZrpÇbáõç„ÈíEYÉhæ)nÉ£«H$+ïs˜$;.%®Uð%Nïr½=á—š×p’."ÑÙ­‚ékY³ }ʯÞ3 ÌJ,¢ÉeWxßw'9Ÿ–îOB3~L"q;¢‰ TäÉÐÛ«DZfÊ;âÀ½±²#§œÕÒizuµ)QŸ§‚‚ûÞqïÇÝçd8ðsÔÓ¹½%NXoxRJõEyÜ7;izåK ™<^žø¬Ü Øët‰oÏA“–L%ß/˜‘Öךôª³í8͹·ô›ó/‚¸]KÇ ¤æYäIít’C™Tçjã8rèA:š7~-WÛcKæOä …wÜøã&»I‰w1j~\ì ÊI‘'3GéàÅ{ɽڃܸvb(ùël…K] ÷µDIsåN´¦ñ3¿QÖ‰N“Æ£r³†o%‹ñ'¿\AªSÉÙ?šÏfîãÕÝ,ѧ–0lZŽ÷îóGSϽ'ÏTÖúÌÙäuu.J¾þ‰S[‘KôÒ{Qú ‚±iuÜ.÷Tru›ŽÛ¾šðÕ)$uV{H\x,ç²m%"ô¿\©œ÷yb#XQhñÏ“õpÝdJ{QQ[GÊ·Z“šÓIõ¡9Ü|¡5!p%ør’¸]A&,ÏAâ…©dˆàyñ£¹57“]UgÐŗܾŒ¹¨Ooú¤1$¸~õ$ x^M2²ßpñJ¸Ôe7÷­i+ Ü$Ç«§¡éó*¸Ö Û‰G†<];Èx·ôà`1±9UË))ä.I¤»óR+c¹‹ë³È­dæëZ´­ÀMuP'HŒ›<.BçH`I —e„iãE}2G»%ËŠÒûbRè²Ê>T7n‘¹ù„ìž·Ö”BvÎm$æÀVòøØ¼Hå)Y1ÖŸ¨ì8ˆ¾É@ᾓx½;ùäl5TQÓF‚E"ˆ”M&9›OVûµðZ¡ÉÆÚ’á†Ã·Æ’àJÒਂ®•D·OD¯o–’Y¿œH\áò ˆÚ¢Mºt²µkö¢CWýHåœtK3—\ôˆìzä^Uj“¸둟ô,2õŽ3²¯Ý+õF§¶“ɃßQû!?>’ a ã¥;‰…æ^yÑXüØ·¥Èí@©«ªPœñQRßÃ9W’¯ýçˆà`aWS8=Ñ ä$ã‘”hr3ö$±§«…#â5ÐÈòSÄüòl²¨«YÆ>G“ú®  ÂÉÙ¦•d@Æ iÏñ!£ŽØcÉBÁ­|DÙ=’¾cjƒ⟚_N¿è°j­ð´IJ7’7:qä×ó\Og‘‰ÁoVêL"ÝŽùÚuox³L1gÛÈ™-‹Òóó¸µ#þ4ϧÈÉ?S"æNj'¶ó§ (²2Oá"Opu{5Ès1Z®Ï££y{Ñ}åS¼cëùüÖ¹èðÁd½m+2nÝÍ©ýÕ#º ÄPßK Ùó;Œû9ë…`Ïø$’“Vu„Îsão”G·‡SQ×âmd›±Ò39OÊbDш¥Ë¬}wsìp …P®Ü1”¨²eÆëóƒ·ÉCmoÁ÷µdÖñÃhÚchls(¿²p2šâ´…4·þ›'a’ô^Œ ®/UC§í婽ÓUÁƒ÷樿Œ7w¡ 7“£Ñ ô™D±÷4ÆM~آˆ”ˆE¡›vI$õQ™.{†¼³#[º—‘«ƒúË»äñ3{,Ó·•7˜P¶? æ{nqb“(²{ÐT$¹•ÌX1&1þÜÉCdô§=Dë°²A…$õíY>oE ·èØoÿörÌ´Á} ù»p"Þ÷H™ï¾ñèfœàOÔ¿ä{ž ËôøOÊx“I~èÐ¥Áib†R¸Ý|)øÅ-,{ÊíàúøN•(rîs^uj5É{Ÿä¸›˜Üê%×Kÿõn½n¦E9{«WOD3á`†!W»z$)Y­ÀÇ$˓¸±¼åcþ†¦)yYNdºÐ÷}ãñä·‡‘4Ñãg°±È1Po×C7`%Éy´{&EÚÎIÓua.¤ÕGŒ®XìKOuÌCCÇÖ7ï ­"÷; ×iWÑ &þ»=±;8‚È]jæ·[>$·ÞüEÞ¯v¢»ôè Í rE|—¾Š\?sÀ¡K«„¯ÕAÙó¾rß.‰O…¤¦ø(¡³ûÇã©Ù×¾³ Ìä—×4)”'£’ÝŠtã‡ûÍTŽ’7ã?öe%×}Ü~ ”´a?_6jojº .ÖqÃ7ãø®tòÉp%ê’;HtæååÖäë…)Âe.bø÷æ\’uõ2ñ–|†ÊÏ' JöìGþ+ºˆGq:òÉræ¿L&«´¡ãÆ’/AâôÛÐvþHñYÚ]ü‰»&Üç}rèîde^Äort{E6Î%¯»_ C\ɉ?½‚²â‹¨{— –ŽF7Up÷o6 ÑEî¤îüa"¨N[¡îÉi‚™ašXum6º65ÿæ~‹ÌàTÈz[ ª²8˜¹£ ùJÜú÷PÚA+ÁëìÍä´ø¢aàÁk}¸C>uvsÕß¹®Õ»l5YÕÄŽG?–x‘ô°MÈÝÿ3r_óY6ò sšGÖæü¤ujä¬ÎwÞ!{ ¯îPËçŠ×q7gjñП0 ŸKü„ÒnÚ“¢ômZHò¦øM1]~ð鵦—M}B­OÛ‘÷чÈ-³ÉÜù‹.þ0"/C%é¹ÈQX°÷#õ¢ÑÙ×€n¿º‚\Ž% ”s‘ F/ÙC®ÏT¦íÖg‘ú| {&¡oËE¹£Š„„Ïéç¾%íáìîIñòÓÄF#‰ ·ÉÉmîœJS*rÿþ•Ÿkí+\dGžtí ‰-~\Ê 7ô¡|¿àˆœ'÷bÕDzüïIôaPÓaôÑýü^ƒ*”ÜwŠðK2Ì(GW^; ïÆÖ¨qB*šö±Û4zÞ5ßtE ¦’KÄ=Ì_GêãO#3j¾ó¿^ݶ/L÷á;]ò‰$qjðÕÍËùMuA‹—O¦Î6/8%ÁVò¹ðYüø9ùì†NÔMâÅÙWþºŽ)ž/Æg mµg¸×v“žŸ‡nLÛEÒçës2ßg£à'jü¡¿¹]~ä½WZ³VÜÌÖ%Ï8—ïŸÈN„PæÝWΪ&…)oøgtr„ññ‡µÉ¬orô²K1?m?lîŸÏÏšÙ‚ZWá4UÓ‰ùÄ$1ù ÷#q·!kqôóE§–Ï'ægÃЧÚ.ì[Šì … (vÚhRhº}."† 8gº‰Ó”H@Ï– Ô%ôµ£rs #³úwlövdµî$§Xø›HîQ¢AGî’*ÏQdGþuaÿr/.Çj-Á2Ûúcd°!dÏ%=ÓËQaû{½œôf,Dêw#ÿ'äµb©Ã‚Rr=z?'ýógv²‰×ƒ h×^yüj•9 XËY.|ƒ´ö¨!÷퇈rh=Dˆ¢¨YÍR½‹Ì“ň©q)ñV•A›® Oè’áÐv{¬z™ï”é&‡}<ˆ×ñ¨Dƒ éiÆhädÿšg-Ê ÞÏ#6³Éci5d1æ/çZJ<ޣЉãñ´¢ò±Ù”d…¢²=¼Ë¦(ôK3rl$'c‹Èj‹^þ…"²ßtx™–ÉädK¹´¸•‹ú*¿ÍÞKfñ|¦£?‘í$È-n+)?w -Þ£Á©­ÓE¶óï _©`‹Œ2îGq*4”äsþî ûªSHSû2²ã›;ùñ=½ß^ƒ §w¡ðð0²¬Z ëäL"Ûf$’M›9…Ùn<ç_ŽF]†‚GÁÎkU¤eÆu^¥!¡&x¯©Ž=ß6r…u’´ðÎ:¤`¿Ž?3û ÙPõƒo µA¢Û¸S†ùܺwÛ‰×ÛDNvŠ -°›E¬ÈªªdaÖªhÞåN^Ù&W w@}ý€_&«ÂÉׯBKÃ{9¹“dë*edûíIÓÑal¹Ð³°YÆù%¼…'VŸœÉ+]½Š^‹ý«óÌ0nKW2Š»Ež{ÆO7àШ&=üq{9t|ïšbJ<%.!G‡&´öBé½FZ¦šÎ‰#Èæûòäˆb)Ò>4Ýò'ÍÇ+¹_#碡0iò¹¡5I$ê³êšçüÚ„¤Ç/çsÛ’yÝ~(ßM–<Í—Á+ qoVw¢‹|Š™2‰X:%«:¦ò‘RHev'?9Cˆ6ÿÝCn½˜AD¥KøõׯQ“„6¤WäBw]BÄç¸4Ö(Ñ@f_Æ“ù5lj‹J6{àWìQ@@§upËÄäjE:¡þFä{(On'Ì!!ÏÊzR‘¼Î ^Pv˜¼¶Fva'ÉÒ£Áø0"¼éÍŠë ï¸'ZÇ¢š²œŽØ=áß/³Ð‡sɨ-jªõ\L*\RÉœFžÔ¯t#[ös~)å§võHE¼]Ðñ÷¼PX4»þv@²Ë¾ò‹p×_ý‰ÔU Å?ÇÈ¢½ßÐuÕ¥(5_Žø¦2áKœÐvÅMÞ}ùyThzÍBɤ´®­1‰Œ1¸ER54ïÎåÜoÌà¯ï:Œn|û|Ç­ç3rs V‘‹óÑÌÒ=ü¤Î(‘}œ‡±¸tu*1¼LÔtrÝ3Âöw¡ëù+ÜïÅ5¤óè(œû7Š”–DÁ^Åž‹ÑR¯¤bA1eÊ”EÈàÏõÞÂÝÚH}¾?Z;}«€(l\î{O¦'mà')þËÜ›‰¾éTÒZ€ªEPÕ´Ïܤ™»Pí¦¶¦ðyªÄæÏn¢Ü¤-Û,—•'S¸Btpï>ˆâïñÒ÷ʼn¹?;uQ·:ÅßéûÀ\æý¾¹öý !òaSPôn²_ Þ%=AsS¯“Çç–£©ÛQKÛJnZÔMÎá©9é üÈÝy9…ÌW#ÜÆ1aäüŽáóÙFG/’nkSnRÌmôàåQ4AlÉÛщvªk¢TInÝy¢°õP£‡ãÜ¥hED®0ð‚#¨,–Ž—¢âÿf®IŒ96f¹­W…¬m2¹Þhqé,â 6m&–½éÖ_B–H§ð§4¤ø.13nÑ,týe*ùô}¸¾Ê'S.Ü#gFº¸ë:ú„7IOe'>"'3É‹CýÜõ/ Âq;·¥zEr¥·Rà?ë$l©"[×øòO6oãseÄȽ«ÃÈ~ GG?%.’ÄþÌnJÏm²UU…ÄoŠE›$ éûŽrÁ©Cü©‹+yQÞÀa‚U'*( ›'¼å—êáÏÊ^dGý Ò“œCL?Bã%äø;ûSQç4-.AþÿµY޼[\^?R£å‹:¸ùbç‘%C¶ç³ÈƒÛ*Xgs7qÏ$«åE®d¨QÅÏé(8yß•·…ļÉ#´v£¸õf¨(ô?A¾ÙÌ^Á…Èl@Zã"Ñú<'2ëÌþàݥܻï·ÈÌŠèºW™À%ÔŠÜjá÷@ÏJѽ^Îû+^%FQh“×A>¿Wu%ñg"5é¤ýlº–YÉëž/T¹÷„´®ê&—$õѯ‰HÂeR‰¤Ù æ£5ü®¹>ÜZ‡hTà<†³Ú€ö/$¾ë2ÉÆÝ'¹PKW¤<¹ˆ„I]!k%rß®!ØïJcž +&(gÖîvv9ª[b‹n+åò· òH¡®<78…~žÿ×küFãï÷—´>d~íŒ`zz ѽÍzÒÎ?ž<Ç¡tz.Ïê„"ÎÛÐÖfM¼©€ ^îÈè¿°RÑ8«G¿/Žl–ÞÊø“&Ÿ4#evMÜþÒøaËr°x;šïÚËgÌþÆßÚ;‚Ú•ËâÇUçHžØ¢¤ö}üÝΫe— cnEö_¾óå×!«ŸûxkÈ'ÔsªŒßÏGÕX}|ªä4ú.+A|oXòF*¨iŽ6É}»BP6ï2y÷=ž—.¨$“÷¤£=s®’iùÖËÕ¤æÜe¡ä7rÝÈsŽi6¾šB.»v’#FÊ(0CŸêdGF£÷¢£Z%$2¨ÑÛm¨aÅ4òdK;oz¢Ý4þÌOÀã‘úY†n?ßÊ û ÉÐU-\Ó»›¼Í[@Ò~¯ån³>>Q®E€ö]C=·N†E÷ù›f— 1$rŸòÈçölT#s’œŸ;eî<€®-(A_?­"WÉõn~ËC=òzÅsaÖº1($Gˆv;]$ô‹ám³nñÒeŒTü”§}…ÙܦtÒ>Å÷Œ@Ýu‘µé_Þ~(†¸ç§"‹¥DD\ŸÌž#FΚŒ‚pš}x-‰°9ÌÕŸ¼€ZJ¢„Ò¡‰dô‰n´ÃcnÄk´þA1o¤“E†Wår“l•Hò­J^eø‘‘ÄS‹ë‰YYh¹Ýè"ÖÛø¦ä«$°+Iîv â£äÈ™q©Âš„èÒ¥¹äRNZKž ÊUÝPüTÎ?±…À—¼z%E aü×x·”óÙoHÖ5“AÓ` ûEO£2ÁyâÙ¼„¬3+!âwšÉ„Ñ¿H³¸ ©ÿŽ;óY†j‡f’×IOyêùœ>?ÐNnÞ¢$Jnô·¢õm'šê'LD.cÐï{ äÁ=Mt\î%wÉS“äš¡òÑÒÈj` ¾:i':×oF¾ž×ÇŸç7ñ½õRt²Ô~rle-2î}Oòçõr\I:šlîH0ýç­ïù´]MÄõúud]Ò‚f>Oâ×n#+s³¹5‰zôºUw&܆KP9KvâŸÜ—Ñ•\R`7îg=Ù½æ_¦IŒlFÒÇøGA¹HôH:ùæÚ.Þãÿ&‹oè“äL$¥¹Œ[YdFÖ^ÿÖSˆ¶h¢¸™¿ø¾6´¸ò%Yù^X¬FGnÎàì«¢»ùÜçm¶Â¯'f£\Ý{dŒE)º2NŽ3x³›{¶ƒ?âÛÌ=B¤ïV ×o’p{Çuš;Þ ™íE&hÿë=³¹,K´Ú&Mp4ÓƒT¿&Í¿ŒºJÉóµ¤$\}c÷H­½ùûª™ËÓÒÂ?—ï ÚÇ·¯ÚMaõ¥$]a$ ý±šÄOH"×zÓкlu‚мñÞ§Aäé1ü´=›Èؼ6þÆ¡6ä¸/žâH>Œ÷Æ"ÇS™{†àiï4ziÂvtØ]þ=éÉýVË ‚`O^î¯-:*Þ€†\(7ÛFÿJ›FâlÕɬs3ø­²¦dG’%3x‰ö¼ÑÁÞÇ& ×Ÿ„çñ rdqy1V‹œÕÿ$ ^w ms&Ió§“ÓÑq$x©Ù?1»‡pSç/ Æw£YË&¢»ç[Ñê›çˆÏÇXÎ÷FšsnˆÈD†±ç«É†]™ÜÜuŸÖÏz"–Îäb¶Ž ËF"[2ýÔ¿¾Èùµåýçþæß;ôý;·–óRá+‘Ã=ÉÙÆäÁtSªbxY,}MWÔ’U ºèqù~׈ß\ÚðQÒy`P`fÙLF¨E/. ‡Q¢üÒ¿uB›¡fÞy~:9õÑ„(‰ŽãôHi›‚®Æ^%ûV¶p] –$HDT`0ÎÍý‘„¢}dIºÓk~œ¼•zûÙºÆ+%DgqŠ­9¨q…1~¼Šì­ßI´ïýËQ—N–Í“ÃCwÎ!S1νf5™S{‡d½žC¼W HÙ6QÞnÏß›0ŒÈ—9Èû{>ä”ì‹FÇfUî€û$òÜHœËh*DÏ]¶ðÅŸtH˜2C¡ª…D¬µ˜ä•Mã½ ¸?QrE&êXÉ{Þ &çšÇrqîE$tÌîàÍWd~ïþÂÙ)äÊ?tuH‘º†Yã³S2ÑØ¹Äo»9¨°†xù¯ MKÑþ,z“ó•NÕÅ7kk¹ž–¢{ü ¹·m˜ÏŸçÏ•”®.þÙIÚl< òÍälÜž4‡Fw–³¬5E±éÈÐŒèÝg[´#¤µiLÆOξ݊–XÚq;®säþïi¼Šï<ôûƒ4vȳ&Oïç£)´ ÅÔéáG[ª"m€Æ|iEé‰W¸}/5¹ô ¾¸1»eþøF>ǯ@~üËkoˆué¢yH=ë/#ó®qO‡O¢ùh[Õ>¥Ê–T d Z÷¬,–Ag¼ä¤áŠ“0H´ît e‹9\Wº˜fáÏ GÏÆ’z »4…ónãcÖp]ÓeIXO+i­¯j:°e¿åËY¢ì↤4s…Å.á¼Ã—Ehí£2bfr›<Ý›*$Ïä~fr·îÙÛ[Œ×!"§àxKÂÑÍÑÈí£Ñùƒ~üj«wèFÃNtyöU>4#˜³Üs’<+Eë¥Rã‹ÉlAŸü^Nî¨!“Q$oë´éìþ×@ÿuÒÒø¡ûaSOª.l æU±èâ¾ ´âÞ,rçB/QŸœF 'råÊcQZx¯^;}Æw·HÒ¼˜ò§“[š!O.é–p‰›N US·›ÒŸ¨,´‚þ˜Æ-²é%g0§_ÌïkxÌ|üŽÄ·†çü0þXñ$tÒË}n½DÞ¼åÐοãˆåüc|Ÿåot‹!‰ ¢ttd'qZ!ÎGzOVßÇäHNr0L"÷GJ`‘e¨üþ7´es_7½j>Y[MÊÿêñÉ|ζð¿Oþ 5Cš¦ËÇpt¯:W´ ¯íôA·x¡Ñ•g8K§×ójéq|Âü‘4iy>±ýpŽ»×±Øý*t¸÷éGð]žç°¢H¨%¹™ÌrICÍ­Èšõ]­Žƒ\Øä^æÄ(r– h}âÎ<Þî d°Â±HnÑ¡ÉKTv[]^5‡ä •¢ÁnÓÙt‚ÓjîwA. <ªK®ÌGW:8c‡jòv:I{¤Gï„Øö½“/µ »^;dôr?#žóÊßÃÑøÍ-Â䟙o?¥ò“óPúL9Ú"ºõŸÒ@_Çüâí=F¦©õ‘ƒ5åü¦”ÜÙO“QJÇ ”öã"ј³Ù¹_ÊÇÐô~ž\ÛßÄù”·11°] B7ŠŽó ?¯ ©*ºèöÃoܦÅN\×2q2;Þ½û{‘3ü<ÜLôåm³Ñ7ûf´ú\#¹pÈ Mþ·_þZjósSùC];PŽÞr~›>m7Æùh@‘á©hªç5Ô-Z$Ü\u­[v}-<(˜ÃöãÄbAÃ÷fáÔ0@oGÙë¡?>£IŻб}±¤28ýTNE¿réÚÇÑÉ¡íÈ_¹l‹:€Öt?çoâÆ^­B—Sø•;ÐzAwkº1ó_M¾zå¡ Û+üìÑgIÛ*¤<¸’¿rÜ›ì8°LJÙöžð'zJ;Ðç·-DGkX˜ïºýºI>¹¦¡gêwÉ4LnÞ+äƒnõ±#QöðôÍåñ<þÏt… ¤øÃ\iÃ^döµMÉOþ›ƒ^õè¡»ßÊ®›méûRcÁLñ‹¤"~€»v÷¬ô zÿÇ‘¬;·¬I=Jfí‡UÈf¥vôîôL°-•¹F–Z¹¡ôM3?-e±ßH|嫇ðÃÇF”5&X»L"ÏT×’ÏgVå/$TV_2óCŸÜ½ÉóêË\Ès$:Žô}U$7ŸDR‡'¡îøÂM¢}§šÜ•*jf>ï5ñY/(ýÛ³»÷’—[ŠHNéÎÕø—Ù«ÅË šêJ®@‘G³ÿrñjÉ™I»8FEnú„+$MW-u¾Ám­?OÆ® EV*±ÜI¾’dÎhãö~I!yš¨øwUlz·m O‡'¡7¯šÈ„שHê@­p~± '’“Éß?>Š:Çß!SšG`õ}ƒB‰ƒ#HË›•¼ÅÂHrt÷lzø‡ oòx }°Spö~J®òZè¤ p[Á‚L}V@Fßkæ[DFRåï=ÄÅ´€K˜?ɼ‚š²ïØïº¨‰zëvéÔM¼å¥.îü‰Ñøå–…覤11™ˆBt·qù‹«\i)1k]OLçËàß*åÄÍÈ€F;§£¸U…¤F³šÇ-!gˆÙû«ž[çQÍã;ÑrýLNǵËáD¶:ÞÐ݇´«´±YÈQ>ß÷;··Ó™ûÀ ‰âؼê!3®óݯéöÑö4`ÝZâ`¼ý©šË/É"Û_AÅE¹$pVjûÕÉEî_B&Ü(&çZùÌOÈú¯Å(í¯$6¼~ìÛ›M– Ÿá¶NþÄ÷ÕÆ“oƒKÉéqÜ´½dåYm”7­Žï*æý~Oª?àŒ]ѦÛß¹ßÙhæãe¤µGD0QÍýðÓ@›C=5oøx…ùð9±5ÝŒ&oqFU+Pàütf¦=Yü½Þ9OX4Ÿ̖ɧ\ÐŒ–&²!BŒzyø#£“âܹeÞ$FAu\ÿÄ?ñ9OÆdóœt¡9šzìßÜ{qýÉJ%ßk¶ ïÄœlü{‡™ÒÛÈ|5²@ý<Ò­|Ðlüê0ùGúGúGúGúGúGúGúGúGúGúGúGúGúGúGúGúGúGúGúGúGúGúGúGúGúGúGúGúGúGúGúGúGúGúGúGú‹GùOÿé?ý§ÿôŸþÓÿÆÞ™À7Q¦|ÒƒrŸ«‚ âÁ%ô®ô}(G[Û"àª1´)Û¦&)‡¨×ûBQ7+â*ž¬««’ÄÅkÕoDݨ‹×zŸ<ó¦ÍÐéK&Í„ÉóyŸ_gr¼óüÞyßßä™™Š!BDÊ£ÁãqÔÛ¼׿i°××Ûšë½Î:§£¾ÖæiZ2¶Á3nÊLëTÛ¤Y“Ç%ûÙE°Ì+((ÌiYæåä´< )'/¿(7;??;/[ÊÎÍÉ+Ì—,Fn¨V4{¼v·Å"yæ/´{í ´Þרo¬«ÃhfèÑßkŸ_ï[ïª9KßwÈæçkê_]À蟗—[ Y²S³Éíã ×_DKLØË€ ϲÆXš¢¯u®Ë 2'Fw“Š5ôïÿÎÆ:—¾ïèhÿÏÎÍßwÿÏû?FT/krX&XºW5ÏWV»ón“¼Ð¿ÿ×éÞ-;Øÿó ³Ùý?·(»Pìÿ÷J€^U^{c­Ý][åeo„'Ò$)ë"©u~K“ßTcôñbéÀžæˆD#ÚÜ@תªY¥µŽ¥R›¬©ÕúV9šêåîZ‡ÛQ;ËÞ$µ—¿/pˆÄø$IëI"Dˆ!Bzè÷ÿµv¯ÎïèÈÿ±Çÿ9ð6áÿ¢Õ÷uóÀjfµ¬¯õúÑè^Qow6F_ÉÚ Ïtky0Ùá©aÞ*‡ü“}–7T:j\îZ©Õè –?©õæš’ýGʯrƒªjìõv÷$W}sC£ü¾ñ]®z Dô YÖŠŠ™¥%“autµ»ÙaqÖYêêí , íË|‡£Ñbojªw:j-^—¥6ÖâæaŸ¢}ž`#©-1)+ûnJ•×íl\ж)“ÊgͲ–É›Òw lÂùÅWC´*¡öö0¬½šM®u5ÃÇímr×Ò²ê’ÊS¬3a}Tµ³Òßèu¸Ûë-u.·eÉBgÍB‹w¡ÓÓª ,á%gb[Ó•ÙšëboMyÌ­‘_éyr³½ÑÛÜ0»Ñé•÷€ÀP [i…Ëãô:]Ê&Gt•Í%7ÇêvÛ—E%*†'Ò¥ö? ›Yµ>[µ^§Z_¢Z_­Z¿)º.¢-”Ÿ:£ýK ¶.LÑþeŠö/S´™Þm]¦ˆ.›¢Ë-©kbJcZtyUt¹³ua²D— £ËMÑåžÖe.WG—;RÓ¼”‡RF”j•V•O*ëdI­'“tKÕGŒT}?ï —þwÕá+Œ[€aJ”ïþgýªææÝ7.«6j™ªv¦8î–ÏØU•wö2·3?!B„"Dˆ!B„]¡ïü?ý×~Ëÿü¿œì¼\öúïÜü‚"qþF´Öþû¾"1×+'·»þ»·|zX'¹þ»gt9°å À8 W”W•V—¶\L–¯\Ør¹ÃQkq;êí^çb‡|íwô‰:‡ÛÑXã°4µ]«˜Ø5“™ÑoŒ}©£Ôúãž¼š{{¹_P)·Kó‚Ête½¡ÝÿÆÞ–ýº´²«*õ¥Õ•S¢Ï'¤w‰%ÖY¶ò)SªJäcNšè°7ìÕt¯ó8¼–°ê9k™e~³×âl´@rö6íëÜöGb×^*—ú¥EÓ¼&ʆiôrîªçÅS]iKºÛ^Û¶X”Ñ»Õ:ÝŽšèVeNÏ•O ';s5m¿Šò™ÖÊÒS­ò®n‹^üY¤\ZÛ䪷»çØ[úìê­×GÛ- `×o´T–L*©¨.¯„ßÓäj¬õ$$¼ºññ„ßÏ›Bì³á“\òXK• ï n«,©ª(/«’·yôäã웥çXÇXK½Ã~–}Ã2ßá]"ßë»Ä›YãhòºÜ‰m§Rz訃½Ì~Ý[QÉf-›:SÞÒ£ª:Tãµ½q¼]¾ú]­vB›ØUÕ ¸›xCìMLÁ&ÆH \ÜÝ÷æ°Ã[t·–U—”•Ym¥r÷ðÒÉòÎýä°åŽvù[Ê­˜ ÈjÝ[Z߯´¶EmùKƒ«ÖQßYÛ,èÖ6gM‘=F‚÷aÀ¿±„úý‚RJÈó§<¤Úå9Þâ‘ï.a¯©ivÛ½‰Ý5çwyß–^e³gÙ”1ª žÈ-kn˜ïp˹Ú;àZ\Ñ}¬ÅkŽhr»æÃf/³äÈw|ÈMÌpàwàþU%“ª+­3msJË&—ÏiíËÃ`ô»Fk§h‚¹Û e‰³±ÖµDî$ÍMes4nŸ2A}û¯|k¢T÷tq+}·R‰Þ¤»ªšw#QçíãŠÉÿŠ9ZØûÍ-çAŲ”íÞÁz¯v/îkXÚ½Ü~*6©^QÍqížn›FÔOkÝWeŸñHýbì=Zýöž*÷G/¸[Šÿùß´;Ç¿gTv)zbË Iêd­hÝ;º®hu˜²ÆÝWDû…Ö‡ˆ0<ô^ÿëLâ;:¸þ?'/Ÿëó ³Å︑ö®|íÔ6r›ØwÈ×þ¨^3‘í^61ÿFósT¯§äsvèÖ™Ú#>‡ë爈 ŒÿÑ¡?'éïˆ;þäÄøûoð¤ÿ1B¶uòïë½€v^®¥X±ö£wMmÇ òA†ìЦ¶ÇGô»Û¦˽?üKmÞ]½ëq¦ÔþÄu‰ùÖe5î·°šñìz «v½…ÕŒw`×[XͰë-¬f¼»ÞÂj†]oa5ãØõ–XÞ‡§åØõõçI~½…ÕŒw`×[ؼb×[XÍxz‹Z3ìz «Ygž»ÞÂ.;Cðô ØõV3Þ]oQ‡ v½…ÕŒw`×[$æ½ØõV3Þ]o‘˜Ïî·°šu†àéA±ë-¬f¼»ÞÂj†]oa5ãØõV3ìz «ïÀ®·°ša×[XÍxv½…Õ »ÞÂjÆ;°ë-¬fØõV3Þ]oa5î·°šñìz «v½…ÕŒw`×[XͰë-¬f!xzPìzKgûý»Þ¢UáåAyv½…]b×[XÍxv½EyMùnìz «Ygž»ÞÂjÆ;°ë-¬fØõV3Þ]oa5î·°šñìz‹²®®¿ðô ¼»ÞÂ>Æ®·°šñìz «v½…ÕŒw`×[XͰë-¬f¼»ÞÂj†]oa5ë ÁÓƒb×[XÍxv½¥³yPÞ]oa5î·°šñìz «v½…ÕŒw`×[Ôï5IøõV³Î<=(v½…Õ¬3OŠ]oa5ë,Á˃b×[XÍxv½Ey¬h†]oa5ãØõV3ìz «ïÀ®·°yÅ®·°šñìc]V3ìz «ïÀ®·°ša×[XÍxv½…Õ »ÞÂjÆ;°ë-¬fØõV3Þ]o‰å}xzPÞ]oQž$á×[XÍxv½…Í+v½…ÕŒw𨷨5î·°šu†àéA±ë-ì²3OŠ]oa5ãØõõwÈ`×[XÍxv½EbÞ‹]oa5ãØõ‰ù<ìz «Ygž»ÞÂjÆ;°ë-¬fØõV3Þ]oa5î·°šñìz «v½…ÕŒw`×[XͰë-¬f¼»ÞÂj†]oa5ãØõV3ìz «ïÀ®·°ša×[XÍxv½…Õ »ÞÂjÖ‚§Å®·t¶ß?±ë-Zõ^4™0v½…]b×[XÍŒÌe2`×[ÔùW¾—§å%¼<(v½…ÕŒwþ±Æ-Ͱë-¬f¼s]oa5î·°šñÎ?v½EYWc×[XÍxç»ÞÂ>Æ®·°šñÎ?v½…Õ »ÞÂjÆ;ÿX㌖fØõV³Î{%<<(v½…ÕŒ7ê|Jªç±<(v½…ÕŒwþ±ë-±4ãéAyç»ÞÂj†]oa5ãìz «v½…ÕŒwþ±ë-ê÷š$üz «ïü«sÌÃb×[XÍ:CþyzPìzKgó J¤zœQ»ÿóô ¼ó]oQ+ša×[XÍxç»ÞÂj†]oa5ã¬qFK3ìz «ïÜc벚a×[XÍxçkœÑÒ »ÞÂjÆ;÷X㌖fØõV3޹Ǯ·°ša×[XÍxç»ÞËûðô ¼ó]oQž$á3ZšñÎ?Ö8£¥v½…ÕŒwÞyÔ[Ôša×[XÍxç_b–Ø»ÞÂ.;SþyxPìz «ïüc×[Ôß‘&á3ZšñÎ?v½EbÞ‹]oa5ãìz‹Ä|z‹Z3Þù—Të<<(v½…ÕŒwþ±ë-¬fØõV3ž¹W¾_b–˜»ÞÂj–Æìz «v½…ÕLo¾Ò »ÞÂj†]o‰ÕRMF0uÕùWÞ‹]oa5‹— °Æ-Ͱë-¬fZyaÏSæ±n,Ͱë-±432—zÁ<Ö¥æ±n<Íx¡ÎŠ5Îhi†5k]oac×[XÍŒö3zÁ<Ö¥ö8“Šêɳ֜Á˃b3Zšaù-”|ðò ØõV3£ýŒ^0uci†]oa53:Ÿû3Ÿðð ØõV3Þþ»Þ¢¬+±ë-¬f<æb5Øõö1ö±.«™Ñ~F/¼=(c]µf˜^'ÞüË˃b3ZšaxœŽPòÁÃb×[XÍRágô Î§²ŽéA±ë-¬f©È©žyä`÷ <½’_ž»ÞÂjf´ŸIfNæéAyÔ[Ôšígô‚9ÎÄÒŒW½EÑ ÛëÄ›S9Îhi†5Îhi–j“ˆÿáéA±ë-¬fFû½(Á˃b×[XÍRék·Å®·ÄÒŒ'˜ÇºêÏSëÀÓƒígô‚]oaóŠ]oa53ÚÏìÏñ2Š]o‰¥6êy»ÞÂj†5Îhi–Jo“ØõV3ÌcÝXšígô‚]oa5î·°š¥ÚÛ$2ðô ØõV3ž'¼<(v½…Õ,~FØõ6¯ØõV3£ýŒ^x{Pìc]V3^¾Gi«:g<<(Ö8Ó‘f¼P矇Åg´43ÚÏè»Þ¢þŽ4 oœÑÒ,Õ¾FÏÂÃb3¬fØ~‡kœÑÒ »ÞÂjf´ŸÑ‹’ ^»ÞÂjf´ŸÑ oÊ£Þ¢ÖŒ§÷I“ðÆeÕ óX7–f©ž;»ÞÂj†]oa5K•ŸÑ3ÿòô <ê-lH5ñæ ¬qFK3ǺjÍ0½N,°ë-¬fØõV3£ýL2s°’ »ÞK3#s©¬qFK3¬q¦#Íx¡ÎŠ5Îhi†5k]oac×[XÍŒö3zÁgÔïU/±Ç™TäPOžµæ ^kœÑÒ Ëçh¡äƒ—Å®·°šígô‚]oa5î·°šÏý™OxxPìz «oÿƒ]oQÖÕõž”Ç\¬»ÞÂ>Æ>Öe53ÚÏè…·åq¬«Ö ÓëÄ›yyP¬qFK3 ÓJ>xxPìz «Y*üŒÔùTÖ1=(v½…Õ,9Õ3ì”§÷QòËÓƒb×[XÍŒö3ÉÌÉ<=(z‹Z3£ýŒ^0Ç™Xšñª·(ša{xóo*Ç-ͰÆ-ÍRíoñ?<=(v½…ÕÌh?£%xyPìz «Y*}M"ðö Øõ–XšñóXWýyjxzP£ýŒ^°ë-l^±ë-¬fFû™ý9^æáA±ë-±4ÃF=O`×[XͰÆ-ÍRém»ÞÂj†y¬K3£ýŒ^°ë-¬fØõV³T{›Dæž»ÞÂjÆÃóÄ‚—Å®·°š¥ÂÏè»ÞÂæ»ÞÂjf´ŸÑ oŠ}¬ËjÆË÷(mU猇Åg:ÒŒêüóð X㌖fFû½`×[Ôß‘&á3Zš¥Ú×è™CxxPìq†Õ Ûï°`3Zša×[XÍŒö3zQrÁ˃b×[XÍŒö3záíAyÔ[Ôšñô>iÞ8£¬³šaëÆÒ,ÕóoG`×[XͰë-¬f©ò3zæ_ž”G½…í©&Þ¼5ÎhiÆãXW­¦×‰v½…Õ »ÞÂjf´ŸIfVrÁÃb×[bifd.õ‚5Îhi†5Ît¤/Ô¹âáA±Æ-Ͱæ`-°ë-ìcìz «™Ñ~F/XãŒú½ê%ö8“Šêɳ֜Á˃b3Zšaù-”|ðò ØõV3£ýŒ^°ë-¬fØõV3£ó¹?ó Š]oa5ãí°ë-ʺºþÂÓƒò˜‹Õ`×[ØÇØÇº¬fFû½ðö <ŽuÕšazxó//Š5Îhi†áq:BÉŠ]oa5K…ŸÑƒ:ŸÊ:¦Å®·°š¥"§z摃݃òô>J~yzPìz «™Ñ~&™9™§åQoQkf´ŸÑ æ8K3^õE3l¯oþMå8£¥Ö8£¥YªýM"þ‡§Å®·°šígô¢/Š]oa5K¥¯IÞ»ÞK3ž`ëª?O­Oj´ŸÑ v½…Í+v½…ÕÌh?³?ÇË<<(v½%–fبç ìz «Ö8£¥Y*½M"`×[XÍ0ucif´ŸÑ v½…Õ »ÞÂj–jo“ÈüÁÓƒb×[XÍxxžXðò ØõV³Tø=`×[ؼb×[XÍŒö3záíA±uYÍxù¥­êœñð XãLGšñBkœÑÒÌh?£ìz‹ú;Ò$¼qFK³Tû=sŠ=ΰšaû¬qFK3ìz «™Ñ~F/J.xyPìz «™Ñ~F/¼=(z‹Z3žÞ'MÂg”uV3ÌcÝXš¥zþíìz «v½…Õ,U~FÏüËÓƒò¨·°ý!ÕÄ›7°Æ-Íxëª5Ãô:±À®·°ša×[XÍŒö3ÉÌÁJ.xxPìzK,̥͌^°Æ-ͰƙŽ4ã…:W<<(Ö8£¥Ö¬v½…}Œ]oa53ÚÏèkœQ¿W½ÄgR‘C=yÖš3xyP¬qFK3,Ÿ£…’^»ÞÂjf´ŸÑ v½…Õ »ÞÂjft>÷g>ááA±ë-¬f¼ýv½EYW×_xzPs±ìz ûûX—ÕÌh?£Þ”DZ®Z3L¯cÒ€§Åg´4ÓÊ ˜Çº±4î·°šñFOIõ<–Å®·°šñÎÿÁîAyçóX7–fØõV3ÞùÇ<Ö¥z‹Z3ÞùÇgbiƫޢhÆ;ÿêóð ˜Çº±4ë ùçéA±ë-̓*‘êqFýìþÏÓƒòÎ?æ±n,Ͱë-¬f¼óy¬«þ<µ<=(ïüc3Zša×[XÍxçûX—Õ »ÞÂjÆ;ÿX㌖fØõV3Þ¹Çg´4Ã<Ö¥ïÜc×[XͰë-¬f¼ó]o‰å}xzPÞùÇ®·¨?O’ðÆ-ÍxçkœÑÒ »ÞÂjÆ;ï<ê-jͰë-¬f¼ó/1KlŠ]oa—)ÿ<<(v½…ÕŒwþ±ë-êïH“ðÆ-Íxç»Þ"1ïÅ®·°šñÎ?v½Eb>G½E­ïüKªu»ÞÂjÆ;ÿØõV3ìz «ïüc3Ê:«v½…ÕŒwî±ë-¬fØõV3ÞùÇ®·°ša×[XÍxç»ÞÂj†]oa5ã¬qFK3ìz «ïÜc×[XͰë-¬f¼ó]oa5î·°šñ櫓Äò Øõ–Îöû'v½E«þÂ˃&“³4Á®·°Kìz «™‘¹ŒGº˜Çº±4î·°šiå %¼<(v½…Õ,^n2Àg´4î·°šO¶®×˜Çº±4î·°šéÍ—Ñ`×[”uå1v½…ÕLÝx€]oac×[XÍŒö3zÁ<ÖU¿WYb×[´úñæ_^óX7–f'X㌖fØõV3£ýŒ^ÔùTÖ1=(v½%–fF“ÌÖe53ÚÏ$ /Š5ÎhiÆÛÿ¨sÅÃb3ZšaÌ¿‰z»ÞÂj– ?£Þ»ÞÂj†åsâÍ¿<=(æ±n,Ͱ|ŽJ>xyPìz «YªüL¢`×[XÍxÔ[ÔšO½s Ö8£Öe53ÚÏ$ /Š5ÎhiÆÛÿ¨sÅÃb3ZšaÌ¿‰z»ÞÂj– ?£Þ»ÞÂj†åsâÍ¿<=(æ±n,Ͱ|ŽJ>xyPìz «YªüL¢`×[XÍxÔ[ÔšO½s Ö8£Öe53ÚÏ$ /Š5ÎhiÆÛÿ¨sÅÃb3ZšaÌ¿‰z»ÞÂj– ?£Þ»ÞÂj†åsâÍ¿<=(æ±n,Ͱ|ŽJ>xyPìz «YªüL¢`×[XÍxÔ[ÔšO½s Ö8£àV+ € ‹€UÀÅÀ%Àj` p)ð'à2àrà àJà*àjààZà:àzà`-p#p°¸¸¸ø3àþܬn6wîî6w÷÷÷÷›€¿6[€­€éõV~-"-‚À@d™@ è tº=€ž@/ 7Ðè ôú€?‡‡‡AÀ`àp`080`p$08 888ŒF£1ÀqÀX` ä¹@…@pà~à`ð7àAàïÀCÀÃÀ#Àf` °øð(ðð8° A <üØ< << << ü xxxø7ð"°x xxx€ÝZ‚®%ÁæI;7]À[ÀÛÀ;À€0ð.ðð>ð_`7ðð!ðð1ð ð?àSà3àsà àKà+àkàà[à;à{ààÿ€=ÀÀOÀÏÀ/À¯rwßÙÊDXŸLJ€)ÀT`P Lf3Y@PT'•@P ÌNæsyÀ©ÀÓ€Ó3p&`æ5@-àê€ÀBÀ ,Îê pMÀÙ€ð^ X ,–Ë€s€åÀ¹ÀyÀù€X¬...V—«5À¥ÀŸ€Ë€Ë+€+«€«k€kë€ë€µÀÀMÀ:àfààVàÏ€_Îÿ›-„‹`øÌÇÃnØà@ˆæàuÀø?Â@0Ÿ¯6À¬œY­Fbb½«Fž*¤×äÿImÎTqŸj7«¸Ÿî@ 'Ð è ôúý€þÀàÀ!À¡ÀaÀ@`08 ŽÌ€ ŽŽŽŽF#QÀh` p0d9@.ä@!y” WäC‚í—NÆ€b€°Ê9ØÕ‚>Ÿ6Àø"€¾›6Àø"€ÚEàü@À m&€ ð~ „`†í!€ ð~ „`†m%€ ð~ „`†<Àø?Â@0CŽ`|€a ˜!°>À€0Ì[Øà@ˆfÈ;l€ð D3hBàü@@ÖÉôV 6è>À€0ÌÐG`|€a ˜¡ÿÀø?Â@0Cß"€ ð~ „`†~Gàü@À }’6Àø"€ú+l€ð D3ôeØà@ˆfèç°>À€0̰Àø?Â@0ÃþAàü@À ûl€ð DäýÊôv ~Ø@ˆfØ— `|€a ˜a?'€ ð~ „`†1€6Àø"€ÆØà@ˆf;`|€a ˜a\!€ ð~ „`†1‡6Àø"€Æ#Øà@ˆf«`|€a ˜a#€ ð~ „`†1Ž6Àø"€ú6‘Ç@Ó;-„a¼fs `|€a ˜a<&€ ð~ „`†±š6Àø"€ÆqØà@ˆfã `|€a ˜aü'€ ð~ „`†¹6Àø"€æ Øà@ˆf˜S`|€a ˜a¾!€ ð~ „`†¹ˆ6Àø"€ú6l€Ož³Lÿ‘Ù1¸Oë¬WU5«´±Ö±T™)-ÑrDtÖ«r64Õ;Êݵ·£v–½):™¶„üzÌéÖvdËã‹Ný²í”­Ÿ|ØaŽÚoùÐO>ü’äÃï0‘ÿ=ÌÓ°>À€0ÌÐBØà@ˆf˜ß `|€a ˜¡Å°>À€0Ìà `|€a ˜a£ `|€a ˜!³°>ÀºJ £Þæux¼6Oƒ½¾ÞÖÐ\ïuÖ9õµ6OÓ’± žq^ûüzÇØºB[uÕ¬)‰È†(Ìχe^AAaNË2/'§åùìœ"ùÿRN^~Qnv~~v^¶”››—S(Y²“ù2½ÑìñÚ݋䙿Ðîm´/Ðz_c¼±®£I˜ñ÷Ï·¿Ywsñ̲É×ÿîCîúWyrí!äÒ9ÏoºòÕâáM&ß—Î&þs:Iû¼”ÜõÔt2­g%¹ÿ‹J2ÿ™É€sÆ“wzO&[fL$ÖÕsɼI¥¤`Æ4röèÉì3ºaK&Ÿ “KÌo¿18› =¥y8ÿHòæ=Ïû7×’©C¦“À ÕäÉ{Ɠ櫑'K§’qo<[|몱dzÏÞäDïTòÜò‹OÉßU|ä5äšû*‰h³h³h³h³h³h³h³h³h³h³h³h³h³h³h³h³h³h³h³h³h³h³h³h³h³h³h³h³h³h³h³h³h³h³h³hóÁÜæÜÛ_.>üýÅŸt™@&6„vÌ%k_šCòïšKÞÚ5 ywIÿ¥”ô[Ù²¸Œl|f™æüªøo y£ê²í‚‘dá»ÿ)î·ÛIÞh>•|ùê\rÒ+ÈÔÌ¡ä¡Ïf’¿NýOñ»‹IõGCIîÉÓÈùþ¢øìnïºÏ!Ut®h³h³h³h³h³hóAÝæ{l’7×n$÷:†Î~o(}y\ ž>о~î²óÇwɰ¦“éýW§sΙAç]QFï‰L¥t÷ôÅs¨g亮÷Iôo–үͦWí¬¦÷öŸJOÊIoû¹˜6È¢µCFӛޤC_¼ÓÍBH­Dé—Ï¿–M¤/)}çÑzó–lzsæ:¶zM{çU2ðÒ‘ô¦œatøîJúãåÿ"Õï½C®¸oݱŠRÑfÑfÑfÑfÑfÑfÑfÑfÑfÑfÑfÑfÑfÑfÑfÑfÑfü6?{ä Bž«%D¾#ÿþÇ.ròŸ{ЯgüH2ï^A²î¸†ÜÓ“^wßÏäÑú>´ÜŸE˶ô¤äÚÁôÏó-ôÁÒéö-ß“§²zÒ¿žr(ýû±‡ÓggÑ®J§Óé'¯¾IÕ“žvÒOäíœkÉŠG¾#îe»È¸'ÃäÚ W¥?ô¡¯èIÏœÿ3ÙÞ7‹>3êkÒ玟ÈY?®!}NO§ë®ø¹ÙÕö½íBòÒÛ—“îôí¯»ÐðGkÈ =KÈa½Òé+>%ÏoÏ ‹ý‚Ô|½ŽÐo!;GBÇÙûPëÄ~ô—í‡Ò[ÏíCwmH{œÑ~º¨uíèGïÝ:€.(éCŸõ§;Ë¡ÿx±ý¥GOºáÑ—Èȳ"ä¢õ~½¼ÿ´‰ö;íâ_üÙÝtÉÞÚƒ^6°7µÿÚ‹~›Ý…’é_“Ù'FÈÊ×ëc?“ç+ß'§ý»/íñÞ ä¹;ÿLîÞÑŸÖ<ÜŽê{ÙØåt²ÚNû7¾H~ÚÓ•;f'ù×y×’ð¦›È†ñ}éžô=äÌãMÔï•è_ÏDû2)ùÊL÷›è÷üDÞÛ’F·Þ×…ºšzÓç_ÿ˜¼Xч^[ò?rçÏÛÉÖP?Jû ¼ìv2hÒdÕo’+_ÛJ~¸gyse7švxúÑSï’·ö|KÞ÷ÉÔ‹?%7¯!c–}O†>"o½ÿ%yæÈ‹H¯wW“/F~H|×}Dî}d.9±d#W¾ôyþòŤrÕ÷äÿf¼F ÿº~¡TÜó#ùvcúü&ºvåä³¼ÉÝž·È°‚&rùŽIŪgHãØ ú޽–Ì,\JÆÌû™ -þ–ˆþ,ú³èÏ¢?‹þ,ú³èÏ¢?‹þ,ú³èÏ¢?‹þ,ú³èÏ¢?‹þ,ú³èÏ¢?‹þ,ú³èÏ¢?‹þ,ú³èÏ¢?‹þ,ú³èÏ¢?‹þÌ·?e»„ìp¯$k˺Ñ3ÒéªÇ¡î±}¨/û&²¼÷äæ‘ôã?¢/O8–~;ôXºLEÿV\HûÕž@ïpFwÝÓ—6mJ¥£¦Cß,¤Ë×N¿Üt4Ý&õ¢}{LêN:”>ygÚí–;Éö¿EÈ„ßßZ½gýdõsÇRóô#è¡Ïõ¦OÜ?€>Tԋκ8‹Vv=)}%“öú2B†¿w$mÈ[K>úçrã7GÑK¡Åå—’3¯ _œÞ¾_Õ‹Þïˤ‡ÌëFW¾·•\rÇCdî¢rÁ!ýú©tzáô.ôù“ï#«‚ÃéXèÉW§‡{{Ò+ ï^’F×Ý·‘>û9.ÍD¿.C'þe öÞFÆœžG¯f¡#—^EŽþÏ2òŸº¾tBðGR³¦=κ‡ yÿ2÷ÕûÉY±4Ç3ž±æ0ºpü‘tá''Ðk& £“ž?žžøæ`úÄ®ôçÿëO·~:ˆv»z4=ÊÝ•No<)¾rÔ ä³‡ÒzgÐ{#[ɶÝß {¾L^¼v'YüÙµd[Ñ0zLJÐ1EõN̤¯ó!±ŸýyòÓuäðù¿‘ῼE¼ö¦žU7’×ÊדèI_žÚ….Ì9Ìþ`ù:ë=rʆ·Èqß¿GvÿémòQ¯‹ÈЛ€»ÐÏv¥S ÷s{JÓïèK/¹ûPê›ÐæÔ÷ &iýqÝ:&Òƒv»|]9ö(zêCé°+Ò{¾ÝLFOû†¼pÎgdñqWì ë«¿"Ëo “Ñç_E–—  áÉGІ{¡%³&gU|OŽ:âÒÔc ùqõ'䤯ÿK~Ø|8}Ýs1Ƀ1á«'Òùßõ¢¢?‹þ,ú³èÏ¢?‹þ,ú³èÏ¢?‹þ,ú³èÏ¢?‹þ,ú³èÏ¢?‹þ,ú³èÏ¢?‹þ,ú³èÏ¢?‹þ,ú³èÏ¢?‹þ,ú³èÏ¢?‹þ,ú³èÏFõçñe¢Í¢Í¢Í¢Í¢Í¢Í¢Í¢Í¢Í¢Í¢Í¢Í¢Í¢Í¢Í¢Í¢Íâ¾ñâºcqݱ¸Ž^ôgÑŸEýYôgÑŸEýYôgÑŸEýYôgÑŸEýYôgÑŸEýYôgÑŸEýYôgÑŸEýYôgÑŸEýùÀíÏâ:Mqæïé:MÑŸEýYôgÑŸEýYôgÑŸEýYôgÑŸEýYôgÑŸEýYôgÑŸEýYôgÑŸEýYôgÑŸEýYôgÑŸEýYôg£ú³$B„"~wÑàñ8êm^‡Çkó4Øëëm Íõ^gÓQ_kó4-ÛàWQ>ÓZYzªµº´¼l\2ß‘ QTP˼‚‚œ–e^NNËóÑròò‹r³óó³ó²¥ìÜÜœì<ÉR`ôÆÆŠf×î¶X$Ïü…vo£}ÖûàuuM ½ú{íóëcë]5g%þ²À…ùùšúd0úçÚdÉNÝf·ÅA®¿ˆ–˜° –'že5°4EÉҢ냀̉Ñ!O› ‰äögc+ñïèhÿ‡ý}ßý?OìÿQ½¬Éa™`é^Õ<_YíλM"ð"¹ý¿N×®ÙÁþŸ—ŸÃú?xªHìÿ÷J€^U^{c­Ý][åeo”Zæµ´‹Z—­s\BjóäÅ”èãóÛžq€E$Ñ~àºVUÍ*m¬u,•ÚdµH­>hзÊÙÐTï(w×:ÜŽÚYö&©½ü}C$Æ'IZOþ."š½s¤}³'c‘ôe/¦Ë”´^ø½d²/ïp ïpŽßK?> "9ÿWk÷êøŽü_n~^.ëÿ „ÿÈ֙+óPXͬ–µ•Ú~ì£{E½ÝÙ}%ýkx¦[˃ÉO´ïOòO¶=ZÞPé¨q¹k£Ÿ4Xþ¤Ög˜hJöeD—­n·}Ù$W}sC£ü¶ñ¥^‹Ñ×»M*¯¬´UÏ«-nIõB‡¥ÉUow;ϱ{®F‹W>è­s¹-{ÍB |—ÛQßúJ“ÛUÛ\ãc±{,vK•×u–Ãcq467Œ•bxå}žÈTµUž¶»•V¸yö¤jx|xg lI-¼Ùíœïl\`q;jM^—ÛcqÕYêŽZË—ü¼³ÑëR§D×v§¥v»UÕØAFÕ†Ot¹êUÞuÊLëT[eùy½ÒµÄRWß²sw¼lÝPF¡®e³gÙd•`ýˆ²æ†ù·¬CŒ®èÑ%Àþ7 ~“Úû̘ɒÿa»Ýl¯Ìi1z`»Õ*™ÔÏ«’¢<ßuÙ‹€tpÙÑ·îÓ¤£%Æ$g´mWË¡lFœ7l‘ìï?NßÑáï¿…ìï¿yE¹bþGŽ|ùÈUjÛ×{J­Ç$ÊÏ?Ÿÿ#ý¿ª|v夒¤*ÿ­¡¿þŸ“S”-êÿ‘¸þÉTþ[Cý?/'/GŒÿ"ÐB«þ/›iyTŽ6cþ{ [G½û¿¾Êkè¯ÿÃþŸ-öŒõÿƒ;ôîÿú*ÿ­ÿÏuþg~Q¡Øÿ1¢ƒúIÒ¬ÿg]$êÿ|0õÿxlÙíW;MëÑyDˆ!BÄAso†Ö bî!B„"Dˆ8¨‚v?ÆR²ìõÈú9æÃüÖž´ÇÚ§ÞÎxm2y¿ï½®<ê*Úcȯÿnþ!ì<þ­Æ¡Wfuô:ïí!BijËf”•Ï)“W³ô®ójóï)_%¶ü½Fޤܢ¼ÖÕÌœ¼œìÑy¹òîeS'åŸ[(?Ÿ¥õ<¯6ÿNÃÄ,±¿[.O¥KȧóDbæ÷[¿ª¹ùÙ‡¾;–· }çÆLyyR÷ÚWÙǘmâʵ§£KKt©œ‚p¤Nsý_OIã¸ZW3|RÛåUåU¥ò¥+°~¬r)še„c삱-×ýyä«-že¯£Á⚿ȑèuV]£Y’¿d¨ãB7™ôhƒócoây17±åª©“›íÞæ†ÙΖö ÔüÕŒÑuV‰µª´lJ¹ÔZìÑΠ’E¹]½Z28¾Êëv6.(Ž6Zi|ËzC»ÿ-Œ½-'hnK†|‘¥ü¢_œ^é¨S=lQ©©m«2J«+§Hñ®qd$î6¹´²dRTãA“nаMäJëËä’I#»l3!U£Y4it\îªæÅSUiKºÛ^Û¶X”Q»Õ*RÈI.Ÿè§GÖ^•å%•¶YåQi{U¸]M·¥Á•ðÕ¨zä”nŒƒò˜90%+§üxl<2A™quÆ:¼–´ß$ëÌÒ‰•-—êÙ¦V–Ï®€'G·]TºÀíjn’/í•G>ø,ç|wô Óf7ì}—6+‘ü¦šÛÕš•½Û•1©|²|ŠâIUMާ½ÞR³Ðî¶×xn§Çë¬i¹pÙãjv×8ÆXZvú‰Ðâ&»Ç³w#]î„6­‡Ô>R°iìüTZV]RyŠu&¬O9îŰ}°=^gCë•éK:kZ¼ ‹Çá•_j‚opÀ[=xÒ^SÓ [èHx‡PÇuh}~T"}Þ´·¿ëêeÖYr÷]iië ò•û œ‹–Úæ–÷»æ{äôÊLlŽGèìí ä Ÿg––•TɹmÛs=Ðñ½nèõÎFö…à 7¾õPÁV:¹åA«ÎÚNÚÚþUà*­3msJË&—Ïimö°ÒÉ-{]ëþ¦d}‰³±ÖµDÞÿš›:Ëæ0IFuiËNP<ËYÛär6z“DwIª0{븡ÄÇœÚØ[°_þ'ÓÑ䪑ÝrúìêI*9•×;2@½+KªªmS*KNž]R6i<3`&ìý7­[êÜŽ³›5Ët f’âß•Ã4¦“*9:%Ó¦ÉçÐ9S e7uº3fVUÎhy!z‡ŒLU4ï¡>Ýëèâ8¶ÜWC}lÓñ?S6iïœø?é!•.Ò’®¨S?§v1íÞ™Õϵ›âÚ½ ž>Ô/Ä©Û}OtðS?·ïî³·£ß=åòn)þ­DälwÆdÏh‰Š.—²V@Ëa)_6×cEë[z²—Ü)·tŠÐý—ž;´FG׿ìsÿ×ÜÂ|ñûvLSÝÿCb–¦‰'¾þàçñŸqFûtÇÏOú;:Øÿsä ,sÿ‚qÿ?”hÓ6ÃjŸjg½£¶j¡½É¡Ì¦¨ëÞúRÛu¡-UÒ×Hm£A¿–·L©·/˜‡ \îeÑärjºÚ¦e®h³igJR›“ï¢ãø¨%4ílì˜_%t}¥)º9#c}²r‹ˆ¨—úÃÕ‰¼QI°zL5¾I´õ_½§£7ZTMR¾"á u[µ>`‘üI’޽ ºÌj7íµ ÊRë“úÿ ¹½ ^ÿ?IÄþG"÷«,ŸTRUU^™ì- ’¸ÿSìÿÄýŸRºôOòPIÜÿ© GÜÿE^ÄûûOòþAý÷Ÿ˜ý_÷- ’¸ÿSA¶8þG qÿ§ƒ;’Øÿuߪƒý¿(;gŸý?»Pìÿ(ÑÁýŸ2ZÞcÞÿ)­Kôyqÿ§7tüý'Ù‰¿ÿô»Œî@¢GýƒóïW‰Ø¿P£„ÿ<cbé,ktõ@¾ò°ï¥ÿOöòß^)™i­.¯äÝ$"Dˆ‘ŠHâ÷Ý—€vô÷ ö©ÿåååŠó?P"ú÷ÇK‰üýÇ–_ƒ:ÉõŸÊ9·¿Ÿ?˜5«|rôä×~nWÃãqÉ»Õv¦k(Ø+o¢ñ¯w[ƒ£'SwÆ‹k²äÖî“á–?Úi3ܵjöDåï*öok´§y>Lëù“ZÛQÕÏÛŸÂÝþG¦µ¬ÝÙæªœ(Ï/Vý‚ï”nùt¦v?A)’ÊcÛE­2í¢xoü]Eâç&ýÕ‹r ÷9ÿ'Wüý/”hÝkL/KZ皦J1Ïÿì¢:ÿS¾ ¢kË[æ,P¼aëå$ªâ+åÙåLOùÁ°XŸ݈–Í÷† uëzc¬ßÇ[N™Ôüá\ó_$‰ïÿE‰(îÿyûžÿ'ÎÿƉèþ/ßßIcÿ—/ ‰±ÿg1ûô-Î öèG‹àßÿóýÈ}¢Ãë?òŠöÝÿóÅþÑë?n–4÷ÿ¥˜û:sýG·½×DŸh½{ª§¦•0*d+o9Pƹ­GÇúdÕEÈ-×|tô¦ý¿Þ£Ã¦Ðèµqßda®óHèÍâßY$0þO¶V[m“Kª&U–VÈ…ë¾ Dÿõ¹0]ˆë?"ýõ^¢ÿú|ù)1ÿ‹À ­ë?äS\÷ÞQÒpáé|ÚlH$¿ÿ'~ˆþë?`ÿþ%Äõw$¿ÿ'~HGõÿì}îÿ’Ÿ]'öŒèàúS†Ôvg(æú©«øûß|èøûß²ÍÙ¯+2µ^0a|»¤ùí†$Ò˜P~QþÅï²æ,¢óDòóâ§vôûÑ>ÿ!?/Oø”hyÓß“˜óÿºD_owþ_š|š@'9ÿo﹞óÿzÉõ‰Ö»àº]K¤Nx`ŸŠò™ÖÊÒS[o¿ÙræÓÐ ùn¾·Åë²4É­ÂyNëMÛ½Q½:Åik Ý\Ù¬ÚåÞÊÑ[+óؘèÙl]TÏé>›-†`ê—ãߺ´.j|æKñOZ“KÇí|‹ò“Sô¤5í7t‰×smÕU³rüØvgüÏÍ‹ýûž¸ÿj¬ð4ï >=¡8Ôí‰tj÷H¡ñ¿n£—ï .½á}þêÅ }e÷uàu!{æOôʧçÑ‘ËWZ-ó‡èËè® °>ÿñæàÅÛî í^Ó=tåœóCu§Î±z½×ZËÞûþÐà´÷3žø|Éa¡Ïm[‚ÓMµþóÄgÈ=ŸüﲿPZü5<öF«g蹡n×_úœì~|Ô`«+gEhìSk¬ãï+ ޽ÌZ´ydða×XÚõÓô‡_^4½¼îö•¡ç§m N_põõ?¡wþáƒÀQÿ‘^¹ùKrÞÆ'¬çõSð°»o³Þ4hà¶¹5„Ýeõ:üúÒ“Ûዯ³*Û¿ê×"ëK²­K?{=´ü°îAßë'„>\SH¿(7×.¹Ìúùk¯Ðw—¿xì±ÃBÏö ]9ÆzpÌ1ôŒ³o=pÓÖàƒ'?ºtՌЕ?¿zN^ ý63yêúõdêô[BO]$°¨ghëÆw·mÚ6×zØÕßÓÉ3ïÝæ]3?Ttæ¢à†­ÝƒS~:óËiÖ§×—#.Ÿm½`ðÄ‘~ýÌ·ÖZ~Ï «¿ñšÐî¿ ýë³­w:n¤§~û1}øÎCå¶’Q•[ë8:øÌ¼{ƒ÷–M]ó­Ó&î¡K‡zC‡e Éï?¯ñÓ`ãôC7g?NNßd͸s5[òMðöOþ:é†%ô©õ™¡Ã¶åÒ!ÅÖ—.¹(0裟‚ÊöŸ8öÈàGì œqýæ`è´5ÖÙ»£çöŒÞýMv`ÆO Ü5öîàSN÷l8°sÔAËMÁœg¦÷¡HFz°ÿOõÖ {Z­Ww¿54ñ³ã¬3é#¯ÐÝý~ ÚÒ#Ûn^u›upùàÀ§µUÖ©¯W¼ã¬þ¡WïØE•ígÔÚÀ£·¾K«ÊÿKšVl.*ÿ.8ãR‰v> 8áÊutçý§?œ´18öLgð×—Ž \~ÁåÁû“3æÒ­A7œZüõþ,:¾W?ë蛟¾×ýË@Qá¹Åg>¾‚VeœCW­õX—¿Cæ|sàú—‡ÑKÍ=­Ýošüúº›‚/¾´‚¬_´ÂëÛj"ç…–_ n9c ô¿OèÃ;®ÝvÕÕ¿’žçÓGîÝ|·ìŠÀò7ÛF¯ .¶/ ¼6¦0Øï´5ÑÞÛ­Ç>ù)™~ì­AsñkôÍR“õÖoo¥Sg¿IG¿<™\”¹”xªÐz=„~¿½Wèê ž¥Ã&~P¶áy'“Ý><øaŸ‚ë–êÕ¡Ÿò\t^ÿÉ—kýÖž¹=pÈ¢ÿÒÐõ­Žâ Áîü)˜÷F1}}vwkþ+§Ë/ÛEßíòfð·ü"ºuc)ý²ëãÖ‚«FO¾d=ÙÐãez͆Pñ±Ov·nÚpCð‰þN™q¸uð§†t;?¸i ]7{…õÃ/n ëL¿<ÞeØ&šö‡qÖ¢Õ×˯Üð|¶–þíʬ¿ÞñLp÷ç¿ÿT[k=ÁzAàPßûÞGÒÐÌoß->íŸãƒ÷MO×ÿ}]`Èæ«‹G%Y[w”uÖ—³IÑÛýèàë7ß=‚þzüÄÀŠçýÁÏ7]MÏ>ró^ýoÿ^zÔ =‚kîú†^¿cysíÜÐ7[ƒÁE3¬ »é•ï^\8ºÊ0•<½ù7ÚÇ\c}ûˆ‹B ‡N}ÿ–PÙº Öà+'në]yVðî÷ÿ½.ú2}{è“÷7Ò¡/|@Ïáhë-ß­³¾ºcOðÿÆ]Hü6oIè´Gn Çß2"4îáUtÄ—2¤6øôšw]_Úrq–õ›ízÜ–~!çåU¡ÓûC÷{¦õ® G“¿]p~hÁe¿Ðôs3ƒ«vŸMIð¶`VæR6we¨éµ¿„Æ :)ôkx]ê?Ã*-¿Ÿž±² Ý~þVëëoÝp®ý¢¾<®¦ï{¿yÒD©4Ò¤T4Pݳ62”H…DJ¦’Œ!B£&Íó$EšÔ=kWЍ¹ˆ™2ýzÿ^¯oŸÿî¹÷œ}ÎsÖZÏZÏ~­½¯ùh²å^šOðŸí¿Ì’†t´ljFU»3ÌÒž(]µš6œN¡]­¿p:M£cyRlÜ…XÜ)§K¼ÌÍqæµ;ŒžJ}%S‹‡Ûh#Å5Gþ@æ†m°Âß…îbQ)áSä3 ¹ùÈ]ƒ<òGK ÿ]à¡÷þu¡à9ôeš©›Uµ›½š&Ÿ —ÔÊÑaD›Z>éÁZ?ø¹Œ…¼ÏÙÈãŸ@î<Êh(ZB¤ê«™B‘ô'óõ—Áo”Fü•$ sé>û¸Äç‡ûoÛA§³Ùª8zxV£ô¤µlÅîàóúLŸß;NÎÑ"»cÌÿá?´ç8=…O·’"AL_Z„Çšñúdjþ¬Iî&{E×ÎÙœŒ?³ï§ q¼¶–^x_‰è9î©ùŽE'^«y1+O\Æ‘¨LföáSð;B—æ}(b]¢E]7cé+‰î÷˜;ФH õç>àȇtƒÊ©?xøZ0ãt¸„Ñ[9Áÿ±Áyp»ÏÌVÍÇaÑròÅa ÍëqŸ©3‰úÀCsfÀqë6PÚ.HñÕvàÝ&G?¿"9k¸GMÂAºg=ð*Š}uu3JoÚIñô㌂‡pãÉs\ëw›üÈYÖƒˆòS³°jŒ¾êOæ$DQY’“tÍÙöÍzþ€¾»½ãørl{Ùg'bˆ“MŒÝjÏ|ôKDÙ÷ËŒ>øºWUÒC]îQáPrÜÍŸs;$/òSËtqk× ßsާù_×Aá¦;}?)5 8ãš=ý,% Žžû؉øïht„æápØ™ü‹‰~^wš^®N U3»¸ÑàsOÉœéÔ,þ<د¥*zOñÙH0õÕºÞkg„)ædNn$…ðÙd²Á{<؉ÇÛ•Éí19ö]ú[vŠÃ;Ì¿–Gïû‘%’EøçHª$/ ÆgÛØPÏ0éìÁ7†ÐðÈ&’UDÕw] KèÀ O¸|Ä‘4ç™Ó3Kèˆ×+–óÚŸKª‘þ„vÆ5 7D<.‰ÂÇß6ï U”|¯A«7L¡\‡7²9ÔS#™\Šzú¥Yøm«6“{åšµˆ´EþžÀÿXy¤îŠcvò‘Ò&e˜&ÿ…tJ¸r3dèˆÖj²e’ñ·ºn®Ñp5ú+Û¥Xv¦á°Ø“‡özîÂçy“ÉÚèã¬á¿Ét’õIÖî~ÎðYMÝöß`×ÏT'­¤¦·‘9˜œOìwâÆ<ÈšFÚni#7 š;7h éÓö™¯™nžTá"½,S'ÝõhKÜn¼ZN4NL£I'Eé¾@eŽÿÒÃxå} ´»è²¦Áš¨‚cnçF—oÉ&Ÿ– 12¯-`Wrºæ—ã%‘×àkµ´FÛѲ$wd3X5䱯¬'øÏ÷åtit ›zóÓ%æe¨Ng·šÂ.~»Þ‡tx>ó{UÌ?ªI4´ðÎÏ(Z‡ ï|Å|aŒýO9"ïÒ 3L@Z½“ô/à܈ÃÔ{¯#+µ7‰îxŠç/‡ÁŽÞHœtI„ÕÐùƒ™í$G#Æó‚†ÕÏJÇ’ðŸEF…,²½ÉÈôáeÙoì»8>ùõ· eÄÕÈ)+I´ýâÒ «0åö}ôö“!§6Ÿc¾c=„jmB¾Ù²xbö O¢ÏÁíø$lLšÅ‘ùªAêç¬å‹–aèÊÿÙ_¼?jÄK˜ÇŠîðçßböfù´xôv„â‰Ò6шjøx®G&rº;™ûÄ0"ì»õ.çO¬_\%{äöj|`0›HýÄUcZ,¯ž;°¿LÈ1“Y˜'×Q:¶q]¤üã÷Îæ¸/k†ŽÞå0gêþñ±ªA¦C‰U •es/ãÂÕ6°Å*uü»H¦Ïe.Ý8¥˜3—³”Ü*ç#ºIϧ-à4‡P ä£^û+˜ FVÀ¹µë»ø¡»5;zo³.yÁTã(”œnNwÄm¥S“Y÷e^Vë#øH#½ ŠÖÚTªèÔ~ÁM\î^_#0_²ýêæ@d­'u¨û ÕÉŽ””µ—qŸ¼Ïib2g^wàÕ⥨Ýw½OqΆphlh2ä-Ãá 9;RdXåk9+ó_£Û7¬ZcÃÞ=øå .¢J­M앪¼†¦m}lÝ>.ãîà¬^›zM8p„{´áÜ© ’YüT¹M¢ãLAw²[?)²A5MÆN¿äNjjA¥îÛlª¨5d؈.Q«Ø ·ÛpªGƒ¹Ü±2ª_7|œÞÇ|ë)d ÷Ê‚ú^[ªÄ+?cøˆá—#TíBÃ÷¯¥ÿ‡ÿyx.öè$Ô·þkÕ~ÀŽ¥AéË üú$Lë‹¢“*çrÊ®tfpî~–?X¾[üþç x2Þ<,<öc êAÐÕ‹eœ¬Øü$>òd}6è$¢IØoŽ ×aŸQŽ«aj¥ÿÜ+Qxw[Ü=0íëEH´h€'Ž% VJ7 [»ƒsø-;wá-Lù“ >ožÁ{“©(%ºvW uOɶ™"äéÇ^ðÛ߀™×?³¼ÜVðh×`­H÷ïÙÄÑ1šŽ7Cù¼<®zËSÐøÌaEèÆ×®T# j"ÿó÷<Àȵyà2º›Zý.„_=ARSˆ~7»ÏL¹p„®l|ÊæüÛKwÅÑŸ™ÍŒ£ª kW9È9«&@‚RøèÂm8oòü§” °Á̶ ÓË5kïþ­pïæ\Üöy2­‹–Fÿ ã8§Yt3MêVôYÄTà̇٢Çi(­f~Œ<† >hrJ‚[yˆß ünËnõ¨…À«†dºqÞ©E"ÚHä–ûô†1y¢ÃˈÍÒ$Ïõ5ðÉJ5Ür%n7ÝCÆÇÃÚ’¬ü~c¼÷¸ûc'<³Ù¡gÁ`«þ +‹'ò¿™-XóîeGÞ_câià³ÍS™HÇ} b. ˇg¢H›!¨éF–ZBÆÞÕD¼×‹^V!ºN&x©’ÏÏáóXF„gR}‹È•^ĽläE4ì¢Ñ¾ï>&$ˆ,ã~ܸ*—¬øÞ×´3ÀíŽ4}¼%‰'†3 ~Tˆ:£A„Ä.C†8á[´ -Ά1ÏñÐo}háš‹k®AïÜÞÅT5s*¨ÀYû¹ôŠg)× ë¬qAI"=õl¶ý&æ›ÒdP7a2öó‘çâ¼ðòÛcŒ/MZ-æÂ‰ß/¸Žk¬0t’hjIJÁ†ÃLZ¤ Ýw}1Tÿ'¶y½‰y”Îçi°™ñ)ƒ z”Æôîd±fés¦gF=³ØWƒ~ŒÂàUòÄM*„œÝÍG¡f.¤‡^€_Vy¨ð"—«·/ÿìÓ†Í~yøg|t…ïCúà­)†>sî‚qj ãÓÇ‹S7jSõwã¸y¾öz©`´ ý»MÊîKÇä—Ù¨À2Ö—e@J;&Í…;= Ø×6ßþ£mµ$ \M§åP¸¶ˆ “ùÙƒÚö8Ôoõšß‚ÞñnVñÓ a­¨¼ëгZH\gÀéÓàÍO z7Rœ,5b:§KPÑ‹‰Ü‘è¶ÊŒ¶§-Á©ñí¸­c/cp§T÷2qyg¨Ñ#üzø·)"œn캆ÓKÿo¥2–s’Y³„¹ôØÊ©äRþEt´fÄâ'ðÛÿÎÎÅìie>bòS›åðÕecÜ÷ò¦V‚{P¦m Á=X¸4î¶L§hð,Î÷²#Ý[9³Dy âü¬Züž©½´):jCÕ„D‰uh?l”ýƒ ùütçò08¶ãcc&Iׯ%V«O’œ¹¿w얇ܒˠö*†]çDÒ%œðf† I^Ñi¡”KÄC/sÚõ û%ÄŠ¸95³A»ˆ®—(íXLs6s÷Ö¤Óùh‡ÖV{K‘~]Q†ET¿(y®@'ðs´¿…¾Â眪yœ‡ù¶d½ÏþßâÓ¡aì1ûŠg*5ÝÆÃÕ¹¹Œ ý†¬ÄGuæ{ùEHhÂÐÇ¢8vü6>ÞþþŸ¶I“ðÁžCÌÛú©ô™o-®ÕžCö„4àƒS|Èã%GšÝaÁK!*<Ôx©‚+|$ñÙgìiíé‚dì8/g¿€óMèÎ À ÍWñ܃E(òR›J,x€¦1Uî[¡“ן¸gÀ±8>ðŸÌŒÜÔ¥çÝ\hLê¯^Gÿ]÷‹îÜ«Fªô}Aບ‡‰£Íý&6çÒeLX} ~VJ’É®HsâQ­ …ÁÛ-ó|‘Sa­Ð|ÎêiÚÄKü#(t 3<8m.Á †̽¤¨=E¹vwÐÛÚcõÕí=rI†êZá£ï[éü²ÓøõL NÕ{ ×:“ šÓÆUº3ãÖÉe5•¶Á°ÛlªÖÍ ,˜†le-”³€oS<:óá§´kh¤ ö[^0¶×‡a½§9îÔËñ£ˆf¯…Hä‹7`¾÷6öj“]OÀ•0ñšJBߨô)‹’_«ÞBÓ›:ÆÀlîØyHj Ž‹P†µëó÷8‡W8á€;w×A\»y}¨^¹MäÿØàSl¡+C–ŸõjÈG+íœHÈütÉôz¦Yüî?ÛÄq½IhY»ª}+ÀRobÍ?JÔçÓ¹?æRM ò ¤Šª, EÛaµª=°_JmfßÅU«­ÈÊ[Yí:„9Œé…µ[QèáU˜ñ~è}ƒÄT1J!KMɧɉ§í9î6Gf£-õ;4ƒÛ~¯˜Ï¦'Tk ¨MƒlR•¥é\ŽÕÊpê3o =p ƴƸò)²äíEmÒe½‡,Kƒ_*@즵é$³*Úó÷²yãh_ÊÄ~ÃúÆyõßâl¬ 8ž¸Ÿ‡*³x8¦ñSákLÝQºÃg›ÙÄ&þIC=¹Ø3õämùÈŽyuKr}¸>Én¥¥‹õYqã9„ïÖCή‹»ðõ“ŒIµ.¦Ï‘¡ëÜCáú  awQ{‘(u¯ÿc;­dUŒ°Üëpýí ß¿ÏjufØéS¾ÀâoÏ@éI$FÑS¬šg6öûz€W˾¾3~ߟâáøÕ°ô ê?̾ê•'§«·±¹A˜$p 6. Mcšx|ÐO®‰DùßIaªI>IÓ¹J&亜݄ý‹êw²q7™Ïú!ƒ«JÙ:±‹y’@P\Tg(±ç.îÀ[®gÀ.:kºõ,È¥¢!3ÿ1‘óJž¬¨GË~aZc¾OhgãÎnIú¸WŸõÄÖãR ¼!U—Y³Ã‰¬îûÊÿ<Æ:ë!.™JŒ?LÁ»ë7Pué`ÌyÎì\äLk“Ãqfl6 D1FÖ%Ð#œÂmhb–G­†¿ªÆäÊüC¸H»uEAá¡2©ZŽð-UÔžà…xƨøµ ‹Óç±÷u'Ñ `aª4unYI·rÛñrži~'F8™Ât •Ã×{kAg#/­º’N“'ìåë“!bª-l-È­ëPîY&Éß K% ²ŽÖoÛz1²bÚs˜ -¬†4J”§Ò/å[9g~¨ÂŠîšxň<òYï¨2g}zñ^ñ,:?Bœ^ÿ ù_¦z¬ î1©4¦¥CO JRë%y¬É&oÎAéºÆ]žt½¹ÂeúÒ¼õ`6]‰¡ÙÔíbãöq˜-ÿ„C~#˜äú :à%“ÿÐ ¾/]Dy5NØß-+’ívçµþ&áÅÚâ+²‰žßþʽÃp~´(ÞWéCÿQÄÎunDú#âþ‹óAfž½«ßÁUžC¿šjh:Ž£Ú똊 =xp¢vpÚSO²ma‘ŒZïQ\œRÏ|È—%B3á³Û a|ŸD„ï„ Wµx[UÈõ\E*d¹St§Ð¯*<%1…8>n•ið`’>îºp ž«ç¸ÝÛç/h“€áôú7Ÿ0ËÛüAìÝf¦Ï0©~vžýR!Mξ3Ä­º°vIx‹þ`ˆ¨?Žùh翺`ß% âG¤'ð»Ì“'Gg:Ã$ñ4|ä¶oR0“–äD\J[vLa‚vÉS™‹°¦z-fÝä¤Q+N‚ýöVÖ(ú·Z²Â0Ûb ÷ÅôGðªÔ*§¯¥ŽúÉ8yû36lÉ í›JW|±¦b{ÁÝ!Y†£ù‰ñLà26ꔄœVÅ=Ç è*ŸT™Bxgq(£Jc;E©â¤Ì‘£\§ÙÕz$cL‰eJV§\v/—-ÄÛ ©\ÿ;÷‘Yd,7¦û¢kØ©–ñRÌ(~(Çö:Ä#î ܹÂÀÿÐký“?]™à¿÷ÛôÉz¯T=¨ ›´½i|ÀRb¾ƒnq³À—Á‚4Õó7k²·n¥_Kr6oîfJ?V°wê˜MEè¾¹oå Ñö0Q¢?Ú2Ë6 Ü‘<&Oç+ݰ‡ò̶ ]z»ðÆù.(ùÛÏîÿ“«//…c[Æ—ýÄŸ+vc;åv1§áœ¨•fpMœ/[À zG£@Wkòåð£øJ7vmƒò~¹Æ6Èœ£r7à"±ÇLÖŸ â_ÃV™á«ïë¨P5!3“£=Ô£o §ÓÍ>Âdßriò0܆–ºç0Ï‘‡¨\·žÈÿµ±sm6,d%O“C«…ÿ«-pÝÁí()\5ÒÈ»ÐÝðÍ}ýb rÅ ìô"ø×åŠNVf¨_ø—ë-ÂG--$©Ä¾¸'é†Î­Ûó#.4171Ž1° ;çÕ¡ § .,ãâùå$ÎJìô^cìê7tåþÙdAêO¬Ð4$NVW™š\?v¹¼0Ù¶j&7Î}:+pååóà•µd¯½ƒWë=h™[+g9yÏå¼Ãõ¾MQ4Ê_œÅqYÊÀ—_<3ÂANE Ì¡™1e÷+áÆƒHÊù2Àm?ú ½óÉ„ÿÿÒØÍ U,å¾\lUÞoàEžV¼ªƒµN@ä©8u¥£ü4 úƒåæï8c‰R:¤DÈ4­@øç ìeOº®Óš>ôý…rúÙLL [ÿgãsX0ºV.¾Á<ÓèÊÃN¤óë_˜):Ÿ$_ˆm{àb¡-¶¾A>¥2ä@ü$Ÿ_…U±—îˆ&²vÉ´FÉÎD¬Âc߆™JUÔáaÁ»&• Á+6a&?ÞµIÁ áYôÕð|<™|Œk†jÞhª:¥§o sç©á%›è6)š§ÜÀ,VŸ¬”t' +»'ð—dZBý§éø3=.¥và˜ëaˆ·˜MeÌüHј&ñ¿ƒÇ–d¼ïüC\¼œÞ• ÂÜÓo˜C]iÐm’EræôBM† jóŒ×‹ôÔFspQ ÄR%ˆÑ”ZRe®.ÇêÉÕé3迤BZ|c9sïþ2’ýeÖo'š‘+inn&ýnUL “–Rÿ M°wE5ÿþœý±7ö^I¤ÆË{a¯ ƒjFTDq>c zÇ“©¦åY`–®§+Ô8ûù;ûèÆmë3(âBÙ¯‰X8”53CQ0#’µ®”YQªY'°–ÑñBÜ®‰êÆä³D*çÍü$ª?û|ð)”í° §V3háB ¶L‡Ç‰ºôÖSoVîlkƒpo¾Z”‡ñúC¸©ø9ž¯ØŒ\MIZôm )EG"¦ÇezoìU”á\½Q€uÝè‚3˜Ó å&òÿc•…Ì)Ü1ççîn9:â£D¿‡ªÓW›µ ÷ÃE\[{“}ãg(†Š>ÈÆàäÌg±CxIm)]6^ƒD<õ¢ÿªÝáwÙ:tý~ºÿ…CÍ‹èpÑ~²_¯“ž>„£7ÙÎ6vêíak­ØïXüuÜÝÎ]tŽœaÞtòa꜌Z¶?2",Ü3 Mc¬‚o0¸8wB T!´œ<Òâ§‘iÀWÎÁœ®§Ð[= BÜuˆé´²rà ¦ú\KñVÙ¶ƒýS¼”<Ü*Jî.¶¢#±† ºÐ‰ÌVDaÝM0Ü5áÿ¾VAƒõ“i4®î8dj‰‚ÖþJèÿ5“3«ßI} Ôz3.®éÜ•×/` A z˜¡fTcpþ¼¢NØ¿—€y8›´ nÀ›Çœ©^Î ¦¼^^¼Á]$ FM„ý%ýŒ³_ð>c*‰ñž"ôïØ*Ô]oðl]$?Œ÷mÚ v·,]©7 2õ“óÿ6%/Çut˜}.ûëŸ6ae¾0gû]"J#ƒyIxvªÕ…ÇoAò÷KV±þŒ׉N}ÎÈø×†³XåØã°oÛ&ÈŽ} ¸ªŽY"þÂì•ÁnvÄÿâ` r9IØ>úu½5å]ªI”-ï‚Cœ.þøõ—ݧó–Ý©\ç«èæð@…ýYHÖ:\d»¨/Gb‹.Ñ~)‹Wj“ŸÚñTOò ù>KrÏ"'OÝeªìžÐñßqoV ™ª'NŽ—.„g@AZ2„6á¶£f ~4ù4a¾)²± ­wÎPïcß@QâjÙúÓ›S·±ëTvÓ”õ³çWQ&?Wsè©[8·™CTí‚©¡² ¹ÙȤj´Ã–þû4¡í&|œM½ÂB©ýYê>…Æ1è6îÓA‘t"ÿy†žÇ+3·áè?Ó£¢ÏçÑ*]/¢m¶RnóÂ…¦%ìæ¥´öŸ.íPð ‰ þBuqÄ$œ¼4;xRÑï ¤yA ŽòÅÁ\_ºYÈ‚±…³;ôáþçë잙ɰlÉfµ}3 -7¦ çsq¹û–^¹N2t¥kÕRè—?ó±›C¾¥^%øú/$Å|ǤäËÄøJ-“Ü>ƒÞv“¤Æ}K诒¸cã$¶-q´ZEÃ#EfjÄ9&cá([aJRã iø©³`ýÛ¾2¥Ú^vèáçCóÇ|É)Ýzò®5µpëœÀ?ëw&V¨¿äXåËPƒGÀµ#ƒQlacŸ@ý‚QƦ. µò®ãÎo{Ð}¶?lZó3ŠðûµK„¤§ú¤dÏ!¼©Nv‚ cqg6;ffŠ£©X=w; >s€ÀI'zvqãa¸ˆŒ>÷¦ GïçITÒž†•'áshMÊdÏÎûÁþÜÛƒzÜÃô™{>5Œz2ùœ¹Ø?PA+º@kț歹HøÌ¦Ðžä8z<ñ[œL4fiaÿŠBæ­NvNaò—QÕ¢!<»8ǽ{Y©ú)4çç ˆÜNæ¯+Ÿ¨ÿìfzc×/vÒuzLl]óÏ ¢7Ï\ˆXlO’ºö‚ o=»ý®­JP¢ï1w£1ær:Ø?Ý1½¥Œ¢LÓ;VŽå&Bôá²Ûì!IÒ“acðÞ ö€‚=³ÀA«ŸÙ«fÈÚt‹Óˆ[¾¸‘{º§'Û =²fg5ö›C®‡X{èb}zÓs ½sé!êßueúv ÑÖÁ^ÜèB˜ ƒ ,¯E4ûä— Mu4ƒs§|HøìR|wê"ðІSþù/ñòÕå0gr0²+Ž@bPô-h†"ñ:ýgl]š;‘ÿ:÷7@qQ* Uó±4i}¥7 ]UÉìuáXëéÃ×Ù)/´è›<†X¿ö¿Ÿ>út%kL‰ë:;šJžŽ¬õßAé-F¼“#åßn@h—=qñL)O„ćM ÿT€*}›Æ(•êУæ´¤ÛJ”Ì#7 ›™¾@=ê¿"›¿@ÿs4ëçv×õ¨— N"üLЕ9.ŸkG~y'S¶;ÓöÇôé’v(hZ ¯BÒ°éÜšª\ Ë?>¤Gí+P>î$º$™n¼¾žr‹ ‰Ñ\ztñ'Ⱥ2™¤ÆN¡ ï^LØÿþ•8¼M‰jG“e9(_M˧ _„ÈÔ†²UøÙ°ÿ(‘ð7@ÃSëhZq8zŠ%Õu¡$@þ+üñ+DÑÔ*Ú\›‹œiÆÌ¢˜dõ©zº§m N**dƒ8_^ÛÔ«ÌŸl¥Oò.²…„/ßUr¾õ;Žp`Ùc2°A¼zµŒdµî¤ïF@Þª–rÞ$ӎ̳ð÷ð"ü,»œ)°o€þ\š³o õŠ¥‚sbqƒC‡TZ‰˜ªìõ$úAr4ã 8±žóœ´m…$ùÈJòÔÛ‚h0^ðü1?y¸eö„ýG¿1V¼xØ©•±·0À-ëMè¬ö¿X[Îbù/Ò%y.ù½½aÃ… ˜Ð€2VˆBo-XGYàýÈ9zI ØË,æ.¥RÀÖÖ‡uâ-` ŽÕÓ@©oð•w–ôÞmðrâüÒc¸«rõh]|4ÚÄ@¿Æïa3°’–•Ÿ`÷·aÎømÂß÷Ñb½ (l»Æ±×#&“I‰Ö>z²º þ‹¥ÝÊÛÀ¦M ?~:Nâ&Óo¹ŒvàVpæ®ÑtÆp šÑ1Ô¡ fÈ’…NFœæ`6\†)£‡™Ç öâËB9 úS"|_°©áÌc—$à“ÅŠh¢âÍZ½'-i™°²PJ÷Ï¡ áýØnÊO—<“F7®÷„lÙÖmMˆk…>GúÑ÷E|K§o¿/$ß>x“«{°Ûq[¶!%&}áLUø¯vpniÐ÷‡}Ððã&jðp?>OiD¸'D6…›S»þAbÝ?¸É½rj\8v÷4ÞmР&íÍ”g»<‰.ágÍkã{ßTL(>L¥ïÅËÅ¢4È´ö-´†uûW Æw)ªsÒ,Í'c·îãüE4>óÌDþ7ÔvÛ‹î0ÂäGåñd¼þ(£bàõ΋ qÓNÍ3fy]Ÿ@À1úçþAúýÑ ôûÊ:>/@É‘§ â@ÔÚ0[â ìò_LÎÞ= u.³Çw8’ŽÙQèæ“Æ¦M͇̰ç`°‰M™£Çjä…pÍNVÁ¹Ms¨¡Ë{tË4ÄWá³ \«¯âdë3¸Ê[‰Ú›c„$Ì|Þá~÷×è/ôÊE?±†o²Gw•±ñNÝðåŠ;ýQ ý[J™æH7{·†›P{a(Ý•;Ï®>Ä(ýÊ¢¿Gmð‰írkcóþöiQx¯ë>Þ_¢A'és™Â#/ñæÑr¼óan;\àôs]CUåP¬8}‚]/NÏ5 ?~aEÌ=0u¤'YeÓ)[¥‰Q¤ õ|Âúî1¦üç öÞbxÎw›c<¢@ÿë£.î¾ Sw~=ñ­tMØA¼óe=ýµð%Þ—¸Á5 Ó§…ßæQšÖ•#²tF}3¼óL%³T5郇R$¡ïór¬šs ì^Xƒ³¾°o:ãHÛ©L\ê}VލÀ{Bh’ÿ‹¾ðÐSÁXÝD5ýßr%NhIÇñzxÉJR¤DMøÿFK7Ø(u\ß,†±J'"ápœÉ%O’ö«ÓÃQÕÔðâýŠwÙßÓÃQÑì/³¤¸™yÈ“šžzÎüÍ)¤vE–d¹ˆ,Û)Ë›ÈuãÇ5ôÓ„…²&D£:,([O=™DÏ&'“3‡S¡b–¹sø6y€ª'ëñßkm¢8ŽõèVépQ§BºQ´êm ŽœœÉ·6a*S³"—Xä'ÀÏûMD§Y–\~»…D %‘è)[)?©fç~ó›Óþ±Ñ‡;am¬ æ´òBÝ#7T=ÍqÃèÃ3™ªÔŸ'C!<´ÉRqÂþ…&wXþlë¢Pý¯›5gÒá«{ªßv¦/ß+’Ö¥áèMx²¬‘á¿âΤ,塯/æ2K#»ÜD1º6ýO3ÅÔ–oü¥a)Ëìâ„~>‰Z1\@Sœf=Ä=Ç9ƒƒ½Ç¨ƒÏEúv±!mRƒb£ôõæé°Ù"„hîI€„¦|"gÿ§lˆgã¿o‚¸ ±pE€Ÿ@Ñ HÜxŒ–ÕÀ`»?Þº²„¼7ÈÝçõñ[…U—î ýþaœ±à0Íæ‹‚˽wqÓŽs¸vÈLþfàâ¨h4·"Z±Qxz#¼ç»2Áÿ'Þ0ðB9wOV¦wGÓØ[íém?6G¹€ü7…€”›ïqƒ¨úGÿ=ܧ:_° à¶åýPU5‡î»)GgíøÀ”¨¬1ñ$4ü: ãÀŽ“‰pí«b_rž—TŒŠ¢@ÐZ´SjÙ9!3O†^ìô†i¿?3ÍüÓÉÅ5‹Èû =önô.ºòCîhÛƒŸ’²èv±ù°.q‘P†¯àÕ“įãïèÝŒçÄnRúIPΠúÌ›‹®•¼i<r ˆøÎnΦô—(™'‹;¥óñSK^- ÿŸþ{.Cyws ê_¹öš]°"ÞØ¥C|b*Ô?37lÏ€áÀ¶ÖÏË}œ~=f„ 6Þæ˜6ÆÓ±‹¡âÈcæ¶‹s;âvWœåZ ,&F‡ý=¬EâÖ­Ä7Õä•E?Þ.}Àö7soÛf7Àq¹zö¶Àr|÷;gÆJÆúþÖº·ÍÊæ‚¡tZ—l!7<Öôœj,sâ£Ç~ébêÆ—pcÍWöÆ®bl~·•î;dM R RE€®z£I×­äzw">›-Cì%xR•ƒÙõN‚”y¤AÛb·Rσ&ð_û5Òð¹u#ùÄib7€INB3q«I*¤¾ v°âÏõ±m¯"s*ä$ˆ¨ANçqÆÑ8ŒÞæ¹CþÛ!ásÌüIájl$-YMŠç ¡'Eœ§àët%Z·®6íxsŽ%x¶šõ0‹µ¶ÿºË®lÂ÷6åð³‹óßgjÐçÈeÔùAXƒNûŒHê=Ìú‰ó…»áàÔ Û»†€¤Ñ=xZ ÈÒ'—1ö5CךØ3%nühú'gÌ£‹˜Fn[@ó•dr_®a ž½g‚ÉþwD\þ_ÿCDjÞ`O­ ƒ¼'íx×­Ê"ó¦ëÓšŠ àñüÂBX9ögÁ’‚%¸Z}E-QÔü&íTg¾Ñ¿f®£?sõŽ2¾Û’ˆŽl‡nË-x(âˆÎU!ƒF©¸zè!<+¾…µv£¸²F‡† hR«•<$Ðé0ÍKƒÝSþâ4Æ€úU¾²îü|€Ù³ÿ%¾T½Ê4·¶á¯–jèQÚÉêo_ß îAàS3öýYLÕfe=[±çÁ,ŽØ†áswÃs×íÌb¥R¡“°t¨–]±ª_¼œÂê9bç¤%¤ÔOŽœ\´ëß³V)¡ÏÂjÒÛÇTý‚u×5©‘QΔŠ•™ÁÜ.š îTãŒWhÌUœ'iH¸®ŸÑAÓ‘ªÿlÆëâÇ`H»”1 ó ìéb«RkR%½f‚tÅ%yl¼úäìÏQx*(7xÑ÷ cØö5il«3GmŸ>†ë‘wÙYfÒãóÿ±sZp2cÁ~Ÿ2›.Ù#L Üô©ÊLžzÕòÔÌ‹\p¤²fµxÝì8ÚÌ61ñÀ’Ct™ã-¸á²„Š6³­gÐÎdÇ„ÿß’Ùñ{§Â–k³È± ’H¶ßø™:áx¿ÕÌTèÕEÏaÅÎ*æÝKøZV ¡n^øy0—U/yÄÔˆ°OŸÝdærJàW­2U}ÇüõêjøçÕŠ/E= eh˜ïåuÖ(Ó¡âý…ÌåàNFIí¥Žà’ .{Öû[š¤" Ýø)œŸ.Î÷>):”vϺâPšûXñ ³HbwÚøóž`ÌDɪ±U42,…IÎé?ž‰KƒÔ13f¬v¿[òvÂÛ´ÎñÏ«éÅ5}øßs˜^þËDò‹ÓC"Cøbõ¹Sjg‚ðÐ"¶yÙSèégOú^j0Ý—ÍôHmqÑL<{¸Måøi‚H×Äp)u‰n¥кu‡Ù´¯û®fáN¹AÔûüýÇ»öì÷†â0Ëï0kµôsÖä2õx&¢cTAî5|hÕ!{%å¹×òd™»/}>ÀüÄ4²¨¯óû¤¶³w¸PµXnHùá±}Pµîü ³8å›l™»¿yà·€ÑJœ ÏEk˜­—l¡Ê=¥¡"¡˜}q7¶»ÐÑøëìÖP¼“·†ö€ÎKØ?ù3\ðxÀ˜g_m½?ØFs%Bzÿr>]9…'v)ÁcÛaÎ?φ-cY¸ ö4gÇt~f¯ mê¯`Sy¼éE^O,:ªK&-n€É&¡èË7Þ?JÆ}0Œu›s`ÃÈ|ÜÝìŽÌx úÀFMµ¨þ,!Ô°§‡â°£:‘ÿ„s˜;¢˜gæÁÓÃø'°Ü¢ùHua4ÌŠŠ€Ÿ_G˜MmŸ1èD*ó}R|þúˆMQf÷6q{oø0³LŸ°/™—gŒ™ Ùi°¯YK¢­!pòShùÑ‹Ž´â§âñÜÚ‘ŽÝ­¼Æž*"—À ˆâÎ ˜FæOaìÞYbkÔ=ØòérƒÍ›'¸NN‚D%Hše&ó.ý**Ÿ@ç¢6*MÄLHÀx®Ùò ìîšÑEeÀ-{Ê¿2Ãù¡kÖŽâÎ&YøÞiËA`úEtQã[ÀøäÿÖÞü“âeÅXõ ÏæžŽ'ûÉú*®"“l'“[ºŒa€ n€V‰P¨¾³gf&Áõ\ìë…ýù·¸Î³£Ép?ίZ>úÖL\Åãh»F ”¾½^Ù +€~w;†Ws0eåinò[à{7Äz•ÂÜ|uþ,'m ixúD2šY–±:âÁÄfì5òÆn·áïå¡åæ±b-ïñ¾ÿTûfÎÎà×%ƒO5áöîyPõæû:ë<û¥P‘¾×yˆ#uÓÁsN>ªžnGÒ >YXð¦y¶Ó¨a{.œ*Êââ•uŒl·Õÿ¥ÄÃR.Ëq»¹ —†Œàò'¯9G¦EÀUs‚!¯å‰ˆâc”ÙkÅÙšu˜”Ä”rø¹x.ñ3”}vbÔS6ci›!ÞÊF“LΩÝpE׊ñ[ûdfÑ™[Jwã öÌýtFDHš5Yn͸_§G¬Aæòtè]œ íƒòø, öõ=Œ±:­M‚ð=˜BVn¸ ¢MüäД~ØûÁŸ+(ãy=Q_oÒ6‰¸Y©‡Ó¿¦•Ù½6¦Ô Áëfà~.*Kf.Ê­¢¿SîÇ€tÜíœÆ†ìà¼P«¹šäÑ‹ÙÔ¼JxBÿƒæ"ÌHJ‚€­%(NdyNc‹“9[¾œîhÇ©6Rtò­µ´/üì½&Áã¨{ëE6})?sè“ Z~žƒê2¿@æÏ~¼ž¡ MUqúb76ò[<ìJ(bMК¨> 3 “€Êtäž·†0ã™DâC ÎÌ•€ùf;˜+álÿ9öql*Ìò™FÞʨ}§qðÀY¼œùºûWÛ ¬]$ÆYûP K·˜Nr2¼ÜztŽªÓ_B¦donÖ|EMgYp £{nA\[³z›4=|²#3hAaøx\Ãýþ£.'q½³ð -¢ë­[1ù­ü×Ó,v³Þ¦`Ã?¼Ì?Ÿ®±ƒ#û~Àì¯Øo)òôÜÚ£ofÁÀâb¬ Ž +ÿ”‘¥~m°+"ÏÃSâ`àICdGá˽0Æ%þY)cŸ£1©*yD‡}²©‹D)ÁìÝÌ’xí*&o-ÅŸ«.àû3ÐØ_—&_cÙSE°y¶ Ý ò¸Ét$}ùRð`íFšþ-.]Àƒ‘ÐXáJ8+“m×W`Ž~å«þͽó´€l¶¢Å)ÉØ± þë«Æ›ONNøm½+ÃßF™ûºWA^ÿƒÖŸÌž%ÂtÁû˜–óÚ#‹Y-ò¢ï U墥u÷£¥ÑL€†wþ²™~M‘ý˜RŒ¿ž®¥Ÿ$•YNÒô•E|·ØÐF³û Ó\€í=Waý¿<”ÉŸlãë  ¼7‡¹ªó¶€c0ÿg“½c#]ú)ØæØ M&}@ÝL<¨ë×RXùç#X¦ Âß0;³)™™6s>ãÿu+¤“¿÷{Ø“aº¤í·%­¶R¤ù-ÒÔª<Žï´a~Ü„=]'hï¸_ô2Øÿ™ .ï§Ó)§‹¹ú.1þodm€Gí÷ÂÙ­à{*KD(Â,”.»Ç½þ Â-[PHߦ¥œ…?¶õÈÕ*€ ëÐøç1öʽI4[¿yäz˜ä×9ËE¹ß&wâµw«ÉZ)uú”÷¦h:Q­&Œò‹Õìýô`x}\€>­KÆ%½*ªA6o” ²ûhî?·©—y\¯DÄݾr’ÿ²"kŸ ßúøÈ5›ΧŽÏzZ¬[BæÑ[‰šÌ¯4úï@ ÜèXJ®žÄÝÞfÐ6ÄKTo'ãâVZo1ÑŸ[žÎ ÌO¨¥Ÿß…á …õpæñ!H³´šÀÏhgöHüa柰 ¦V+ðRcSa!­^k˜çÏV0;8÷üÈsH謇–µeø[P«P; ˜H/â¡ '¸éÖëhŠz.µR¡úk\éi!ñ!Âì£ ŽD=W€feÎÆö÷áwüG<ó+Ö­¹À _!GÌyiàÍÓôœ=&lŽdÕtâÀ0¥ Ú~ƒÇ î@e…0¥1¥(ÃïKŸ¿ƒx¹8æÒó»8CWvúóü”‹]ËIÍYqBËÔˆéÈ ‹Ñh¸žµ¤ï0ÏžkkS¼ã‰`_{$eÿ7ÿåyi5C?=ãÄ<­Ÿ…q¶/‘'Ñ–1;Å}öív*aÎ!!¢¿p9MyaEÆ*¯!ÿZò„ç#Ó§£@ý=,ˆâÖf8û|+Äðsö¥ÜdúÏ”á†ç1ðèPƒ `8ÉZûSl‹„yÚ8*;Ö_Êæè(Zgž… …ÒTÁñ"ØNƒyI+É ¯}twA8ÖͫǶBð_Ov@I2»òª.³°Æœ¬;tã‡4P7©¡ú#4¦«C–­^A»#¢Am«SãʾÊÔ¦š¯O£Ií$òfÐŽXdk y¿çáÄúGmL›®…"¦Ç¸±¼_ wXEî>ƒSjâ´ÁãžµP]f%²,ÉènÂ+3à·Ô»†À'ê0Å!gŸ´ ÚöÁpÄŠV~Ò³2š¥Y6ÂdKg v. #‚ž/AïÙI¬ÉÅoNànóv¨¾DùO×ðAÖbZõü+&Mb@NW‰LÛÒ}²KIËÇ"Ü®/@ {¾ƒ^Š9X0ÄÄ1ó‰HÈ.º}¼Ö½êVOöÑÁÒïðé°yZ²›¢×%†¹V¹jµ‡¸Í ÓäÖã¢ÖŸhQÌdóIX¹LŠZÞµ˜à?¯+‘œá «6Û²åÉ<æ3«¹t7D®5¤§TmÈ ~;è(— «yTd£+|œ>þÎ- é¶™üÛžÙ*@þ¼¼ —²ž O”)|‹: _¢;è©+@TÚéL=úe‡ãúNj¥ÓŸÙn췂ߌƒËLX{F‹1ÔÀ´º~ɣϾæ8ü~¶˜Ù JÀâÙtv¹nŸOmubi…ÊI(¶DEµ9ø(qœlÓlðŒ/½_Þ]—ˆ•Á úÜ]‰n«ÜG¿T SÌý°½šxïì‚-vFĺʟ.ʹ€m½;ÿ'³µhô9°âE}w9:¸Ž8d­Œß]rMÇùî«÷!¶eŠá‰:é¯(w˜ù;Öڎè•Æ  ¹¨÷Hƒ,òßKŽÍ©fÖWD°#ª‘P¾õ%Ü®ZG/è`R/ +÷®c÷ˆª7cbÇ]@vx†âîœKÈóû»!Â’Ôì1!^™\qA7®æî øT‹(pͨÉOšUp‘ˆŸÃY Ž¢ìŠ4Òmß ÅR–è¶þ ýЊÝãðá#ov£y.¯Î¾r«`t™ D7 =~Ÿ¤ýŒiû_œ8= iÙÊÔÿ­éŽÁWFðFº9n3ÎzÓÔ÷GÁ«Ú,Ú] ƒÛÔØâ¬ \° ˜è÷¸ÐÅ#*ä¡Ã$’}.ƒ 9à¸;NcË4Éõ¥ÛˆÅùBt‹5¢J©0$ºn^òŠÿÔ1מâÂkN”çà\VÝY6,~…•1mŒÅBGúÌB Çøq¾›(D fL¨W¼ ­ˆþ¯§šíèR&Õy!ð}UIÕkÀ>Ýâ`qêB÷a«”&u+ýÉXØ*Ò¯¤éÝØœwɳ¯¨rWe¾/ù åPU·Ù8Ü ÓÚb‰C€/l{pk"þ¿fõ3¸è4ëúî ³£õûáùR¨¨ÅS=Ȥ-àfþœ•¶ÁŸ=—1l(Q|>Ëêàž†Ô¯ÃfÝ ÜöC‘{€1ƒ­Í혶ýÙ©Ž=ʈ’›7ÀBÉ!&'ã4¬™Š1›Aúk4c0®íè¼Å Ú …óÈ…SwKز<ë<©»ªÞ©âãt\×'W/¿ÃŒ@{ŽFG:ÜÎrƒÀÒUxf¹VªêSƒæÐ dñß! Ò]w7'Ã-%Z_nÎÇáÖ€^¬ìaçFð·s¬èÖç×`§ÌI6÷Í» ü7†åqNåiHž"GÕ¾@Nx)ä} e{^G°MUé Õ|˜/mK>&šƒÒhG…øò“M²}œpí: |0›tïka¼µ"æ³zðmí/î:! :ø~j½¼ÏV®8A¬ÖÞ§2…ÈÔüZJ>–¯ε™í žÛÏ¢ÙÅ.0Xõ‚÷hÒg+(úÝ4;ƒ'À9ß Z"Ã!PÃY|è.|ܪÃÊyTp_^ZÈ:¾9BOEÀÓâÿÀà±}HBSåÔy›zñ’'5ÞpYö4$8sá f.T;kP´xŒ?Vd“˜ONüŒš¨ß=ÆÝ?Jdñ¯ýÆ<öû4㆜L•þ>€Ç“ÎáO— ”sS§.¡3éõ*G¸|/ïX‡jNÍœÉéŠäà;²¶±|óg&•µ´>d2}sP„ õ²paÏ´}~г¼Þ…)Þ8ÀD.ËfÏ=Ì"‹;€\iÁó¦Cø^õ^gÝN®·‘íÈ„es"#Ocöf‘U;èï™ÂðfÞ-F,]˜ÙS”õß¹L×õE4‘;Êì8·™Y½üŠO[Nƒæc½ž9î›™ wó¿±"p–d¼Äiº ™ $ØäcâÚÀ¼Ö¾5¿.8ºBFæ)Ãç´nPX>J½ó{.ó§y9>]ö™sw ›¼‡_HÇðuædS£µzý­†±Ót"!rÖ|”©@ÝQE|`)ª¾| 3†Tl6–cê(ĘœÝ·×Дw÷þ»†1ï|î#„]÷ ‘õßñ+´ãŠs3é[ÞÌ¥O1WÄœ¶m²ùA:þ Lò3 &‰\»ª£¸ñk.h8&BO’ŽiŸÂûÒñäŒQV7®mÆu÷ÿáX=‘Éáã®Ü#K·¿Ä´´vÃ7}'¹ÍKœ1b³-Pøe,è<ÌùöôfÚâ­#lÀQ}ús‹#y’ †›êôˆû7ø~ 2MäIQg9$Fb’3Ó}ÎÜÊùÊåù`ŠØI‘=9tÁtibÇ+Õ ðÅ_“­Æ±ŽYŠÊ¥t‘¤5Ó‹¼ô f:Ûœ„Ôàp»&Î|éÓÁ÷ÛöòíÍåà¹Y„ÔŽÖ0ë¼ÙÒ¼´Hæ5{Ùv\Ú„S5ö‘è™\Ðþ² ŸŸ †ß÷8';Ôé É ¼Êš<ÁÿumvÍ)cÓ|°ÄO‚Z­¢•¯ÕÉÍô©dy‘/ò)%–&I$ÙË„®uÁcR ¸ß~:Õ"òtÓÕÉäG»)»$%‰v‡ýk'¸—Ù^d4vSuÝTN¥ÍÖºÓ”æ‹ÛcM« ¹¦³€ü{~ ë:Œlô½/€ÈóßLÌq9š3#o?Ê%&WÓêãœÌMáûëÌ|rJX“–Šá¾øTfáZ¾û=€áÓup(^—¸ Çaÿís̪7;H‚½¹s¬¦ M¦ûj¨ù*¯ s/iDãï“ÊÐd!H\Å&ü_e>Î{ »Ûx‰ÁÇ3è[,ÂH¨%Âú6¯äNà_þÌ™­ø¬Œì– àj%M´î.¦"w“^“ ìVáÇ<’Ð\ðÏÏÄÆ—B˜(¬AÎDg±§/Ïñ±j0:&H+¤ö² ¥q°äã²OáîTÇ (©Âáãç`—t Ù9w –ŽV k’ÆtjžyXoË4\´±… êù§bQÊþ16[7pÝ[‚ôÛeÈhHàÀáyøäs8‘Ñ6úo<òß½Ÿ÷ Sׯ,d%¬&ϲ¬Ä†}à¼Ù„lh,w §´ A@ŽæyÍ$÷vA|{7l†ù©ÄT­ÆV)BYíÅ üÁ“çS§v}Xûù”FÈ“®úôÚ맬]oé•Ýû|”ˆŽóTúïA0–½ìÁLÛ¨Ÿ–ìÓtNeN¾oG¢6.­W™ÇÓËñ…Š3)‘íAÂÍ´}7Q5[I´Y\ §ÅÄË,¥&oûÀÃ-ŸÞ-BÕepè‡$DÜD»Ãô¸z-×”±µÞôø±…èr³„Qp‘ K|p‹ä~âÕf ¾v”ëA÷;ýÁàœ$šSp–_;7Ή^Äå¦ꆤ1ÛR¯Â¤y:ôʧGœ# ¤¶³;ÍõÉêÂÈg÷Ú\›ˆÿ†¥óÙÅòj¨PŸsVv‚Óòp8hÚÅ~Ä‹Y³žšZ-£C+„ØË{•hr¯}_m„åNÓ1Wy´kA´{fGSp+}û!”uØ$Cʳ^'ȃK¡8ûÁܺbix’J{ô­9u‘ÎÜdß ¬«&;ÜÅþ©ó Ÿnÿ ÛÍ*èí)A¤EP‘<\xÞ}ÀG¥È! ¨vóÖµ¯ëÍ4=F—~,»‰G¤¹ì4 m¼õq7e‘äi<óŸ^ȤkZ:Áe™ž6v8ÝcOlðcH‘®WÁù¦’Ì=§pòukÈ„þטUÈV¼ÚɆ|4£ÏF‚õ·PO1¨v¾ÝTÍ:XÒ“ŸÅ‰˜èS›œ9—˜“pÆG?8»m'%¡æLU'bí5Œ°€Kó¢ýߘF›;¸áHl’Z…þ/Cˆ«uØÕÌ<‰ DYãy4Q$sÌþ¢ô¨54½ŸEþñ¥³NjÏðFÁ!n}O³õF8ôûÄÃCÌä¹<Äb¸ û{võ> 4ž×¼ô§EÙi´ôÖUTª/ƒGi 0ÛRަ,ÈF×-ýŒ„ä+´vrýÿ½Üý²Ù]t{49ž+Rä´HÌÿ­ÿJ–&ß6o‚µfNÔ#ß–®T:ò_Ï0Ë2Íå)<ä%[ÔãÃãüQvV¨{º¼PÉ'I–ÿùwduh¢ÚAöä›)¸õŸî'ÝúÓŸàÜ” lü ƒnõy㜿“*}·iàzy]6yb–ú’SèÆàݨêzó]Úà²9ÜÒ ¡SÕñÏÌçÈcïÇü•'©ï^Ãç—LNßO±“%–ça¶ö+¶SÑ}"ï0ùÉáXÒ2F„3? ÙT#KTnáÅéÚ,ÞØ›‘Ú¢Dâє۷ãjÿ±eVPv¯êDýÓd­KNH>ÀòÛ¢¤){.[ðhÆxí¤HŒŽí ÚÞì p6}‡Å.q`=?–ÈO‰¢/o,&Ö>}°Uulô{Æh{‰ÓÎbGztÅš®û‰ÝªkX ëñ¸åæ>Ñ#-ÔUÉIµ½óÛuÅãÚg ‰¸!A›ÞŸ¦£Þž™X¼þ á:b{c>œ8Å5ú^Jë‚°ï\í>½‡l—W¢3‹Hép=tÚL¥zçïcùòfØXŵØq;?Í'%qö ôa:BÞ&Ž¿V.Ý¥«„'¦¤ÃŸž$áÝ hÝ7üçæüoÿç=±UW ÷åo }”Ÿ;i_äüS£Ùg³¨Ó·Mtå¬3ôzŽÙÊÅ;; ÉûSiö…}¤<Òšä*Ph‹ª%&ÙéäÊCIŸ¯†ÅGe‰Ú®>»úñ¿=œÑie\¦6Y[ˆ|ÈI2ù7/”2*%wAüÃFfaô ÜZ)Nçö]ïö¸ç¬%¤ì¾G¸M×°äö ÊpV€?Ujdî¦ã,™sh©u—Ŷq­ùo}Hw¯}í»*Hî¹äS[©$œÖ§ÅÝ7WvÞ”€ê-óµÊ.1c.ÃìÙèdö?l³dtñÍ¥± ÿ×]µû’±œó,í£pÃ]¶æÂÐÓ&HðÈ„áÎp¨-g`ºm/r´’ù%”S2`‹¨ü¢íg5Ï'p脹»Ð„ÕL÷e'ëÒÝir¤ª'†‘øÕÆ‚´}á%¶H@™öÓÍ$1í 6x\„Õ3NR½ö@óR¥_=–b‘Ũ]5—Ü(_5OK`N[òFåÀüU<´Õ5eÂÿ×µI¢û+3"áåKw N"ršwØÓûf[®#埫GgÎý Y²rèÆ-áï@Gë,|\˜À\ø\ŽÖ3@–_ˆ‰ú‚ìÕ=¦0hz—Æó’«§©;?YQcºà§4qü怅éç(7JV–íA«ôP¯ÄC%DC Õ0§d‰Ð Þ$4iÔ†ÍÉLìLGÆUè-¼˜Ê™²ìYtt'¦ÆÇžÝè-7™¾o²ǦAlþMB¡IËÈt­z¼Í·œÔ«N¦´²Ž)<†ZÓ† ¤Ð¢òMHÏþId{D9ê|¯‚ áÙ\¿eRö?ô<Š­n·†ÝîzìºÝèø¼.¦ ™Eªy uªªñQÓiqԧªÏ8ª?xˆàØæý y:?‰S´Ó‚ß8&Ýw‚¸Š3Ì«3Ædëvì˜I=^…ÆGÓaÍí{è´>3Ôÿ²ò/y‰Ž† ݺ+—Y.¼EÏ(Óý†ÞÛû7|Ÿ‹’ÇZѼA‘®\¥ oáü‡ãš^­ûü§c£Âa£'˜Ê• Œ—f} ›òd>¬uÏd—ÔXÃûTs­åɾ ‚iö ºšNŽ< -Ø‚VÙVàQÁ‡N vNÌÿï­A$-àºêühQ‰µÌeG©rA2ŒYŸÄÞ–— òâgyJGô˜-SáÂÓ|op<]Šî««ØqªÂ×GLüÍ(§eI|Ë aØyóëÞ\úxŽ"ÕÒN¤9oöàÝwù»–;r.…ü²L‡]ìÀñÐ5Õ£‡ eo|IÈgCÒ¼i™¾u‹]E¼¾-¡ž6ëÉÊßpD?‹«dR 5E|é‘Hó|u¨Øõ<Ùö–•÷຋Ú$âÊ~}h6•Ž4¦ïK>°Qk^²=ë°¼ï5 & ×ϯSëp§ û‹n«ç[ø¾$ ÙÒ´;*D~0‹qÒÊ ÊÏ£Ùo¿2ÿíÛ\oŬüæDSMiÓ—£ÌÔúp2êEë/‹„@b« CúÌJ¨ž=wKÊ;fx_Y1W–<]H8î*¤`¥0ýÚ Ùî$PB„´G•Â[i{!–Oí÷§Àº²eÄCNŠ|wŸÎÔ]>¦{ø„|˜à»Éîàv¸ì‘‘%Ôcò9û(lºŠ>_Eð Ÿ “Ò@oÐeX]êETºKÐÂW—ôíHG_•¯°ûø¸®ã{ªf¿(†¨ž´& r'ìäp+v¥«`pTt¥:cè¤fNç6šzø1Ó¾½dx‘,]új™·´EõÞ²Á¡4lÆ"¼&1ÖÇòa g%Ä×Ç¢éÊø zlÍiúeèÇWë>g[‚Ëçú ›÷lÇA÷Ð%d6~5XIªÐäŸY¤ïÃc?y-Ö«ã.éѧé¿qèà¶I~*¹P;zôÛ» ý§xÓk=5Ü·&î o/òI£‰m»Ñî÷!œX“?z*tÿ§g¨¡Ú ÷UÀÀÎP˜å¸Œ$~‘"»î³VßdïöïÁ–¸F§ýlû4‰vGÚ‚_åf›ó9SˆzåRr3È—ØþÉ&÷̶—†ÅГvbŽÕxà©ÓòäVÕiø—4•ú5=À"¿;àHRþ˜‘Õüz$Îå~Šä„«àQÜ›Uïû€—ó>4‡Ž“ ÄSBŽ\ÙN>ÿ«Á⋘r‡fÝÈCм› JêXyVc*nàÒ¬"Ý÷?üÛr$ tD¤O¿Aó´ï8ë± —ŠÁ3*©P£Xg©F¬? /àå§D¸sLÜf§±óÿ$ÃÍã³iè²~&lÚ"æqGóÁÛ ØÁD“ä{÷ 1$˜ywoì)“_œ™¿“üI–¯‰v*Àã–E >§›¹,¦Äˆ(>„w[¼h“Î1ÃpÖú`?ÛX7 ó­|áÖ-oâÅø{LÏ<`ÎCÇÏá5³Ê¢ôŠX/ã/1®akqÅ¥ 4`U€Í͘•dáÙG¸ÖwÓçMž½¬ãM¤+HÒ”ÚÙÚ©Vñ?hêÏúÞ8Áˆ÷ÇÀÞiGö SÛZäÓ+8©î”푘ëXˆ¸µL®£!Ýã,:oP}Iûòlžo“§á Éìß"6}¡ u3ƽU"tKÞx¿n?<{&"WS`àÎ[NÝ®(to?J×EƒKF5H c¢åj3¨FsX»M˜Þ|“]ð%>Ðeµö´â ‡çìÒNXù2Z"CȧáÄ/® ‚L&ƒÚ’gãã/¤—4èý-<ä˜Éi¼¿ä s›'JMIOü)vfSƒlù*¹u+ñÚ?îÓÑìòzü¯~ c2YÛϳ¢Ñ´¯±ç,° ï§eá¡ù¯°p‰Ä¶Tã8 ½A'Þ¦ì$1ÓXÈ´‡ýf>…N$ßW4QçÐý>BÐiI2YÚ„ÊŽ½ä£<ýá1“v»‘På협’Š«­¦¡/XŒ×þŽ:M_ÅÏc¢î^ŸËædG÷%h—¦¦ÏÞê‚€2_¬ñ×ÂÖÖ\9϶¾lÄûMü9ò#ÉÔ‹âÄ…*ÁÐÁ4å•B«Ð ÎéÃgPlÛxΈyÞ°{F#TÊ Cٞ˛NœÍ~rEù@XðÿÖÿ¿:ŒîÛ˜âªû0½FårÈ"mUr4ö3Ës5š]á"H'ŒAÓ¤zöI@õhÏçü×g¼gÖYæ{'.n®rG»l“`KŠ—ߺqÛ'ÆšŠÜ€€M†´ÿ×r®ËŠéYÅŸ2½Q(<η•&'8&É&¤T¨Œ…×Ŭƞ£ðÁè5~,=€oW<À œ¦p°§Á 2hï€ßv~bí;˜ÑG&Œó`»§`?¨;'bùAMò (nÒ&c}å¾jO4EÕ—û8Ó^s÷2ªä·4õN7bKäð[>9Æþ¯ÿgK‘1§á}0ô‹Ÿc¾CÌ~ù£xCï °ÆÖP#aFÛd33vÿ*bÈpÏ¡áEw´}ÆÔ*“GñÀù¸:{`ÀöÄ•¢ fne="ÙÓY‹ñ]°1Ø,‘§cdpß=ÊÖ>?b˱iÚa*×YDƒ-ÃÑ).ði9Þ…£ÜcqÞW/B/èá#n ü®›V?(š,/ÜE#qÒCQ|epö¯<ÁjÜì„ü½’$ºò£.¾Ž$ŠØs"ñXÝ5›ÖJ[äÆé‡®g°}å r|”¡]’¸fÁM8< ‹³í&ð‡¿ÚŸÒ ªý2ëi] â~Õ~·%dE‰·°]ò²FÕÎ2S·`L®ÀìdArÖl9N õ9ðÒ÷$:XÏÃmoàaZ4©µ¬eíŸÕcÜõrN•» W'ŸËµòV£{3W¾¶ƒ`a¥BîÌþ€«M¯¬>ÁtΨdÝoòÓçqw¹µzÈAý¼3Ü+½ïY‰hirkx ®³ k%ëƒù©\€]þò¾`®quÎù’ÓŽ3È%½hÚ,k÷#¶ƒÄ¤2«úîó‹âí‹OùÛÃ4ìofÍ•NàÒ¼·°°uy¤½n"þÅÂía‹ÔANË÷Ùä´X+S\[Îf(¹ak¼i5—„ù¿mHÓ®Pý\ÆÎëd7\ÚŒ2Z–(îóí4ô°ÁÒNzJ¤Èítu¦úG%ÃØN¢šm^8wã]àÁÓ›¡6n mƒva[N¦Ìz¬Y‹?*‘Y÷¨ › IÚ gfÓá˜rÏî7Ì•.b3¾ðÑS>Ùœˆ¸lžÒ.|’¥J–)ø™ï™e/øHtý6mV7LÕ¥Ëæ¸Q çp&€‡“ú¡L'×Îs%PaÂ8ò 6<*›†½ÓRÁy^>ô›àÿ´àFx¸`;{ÿuë{Pt<Î⣣_á’ÚnÒwô|JzÏœ¼‡"'÷á…“Â8“)fòzðƒT5š.XŽÍï¢A³ð< n']ް¥#§?á¥ëäXœg¬IóÅžáî:ofF9bË‹'¬û•fëwéf0Ç? V%¶‚ŸŠ1õðZ†¯]}Èüèé$;Ýç¢ L™Ë¡ÓÅÎÃáX;ªj›ÍÞ¸ú¬æ·À†ëÞpÐè ö\™A­"Ù«¹M8èœ Wf@‘möx»!\Ï–1ñªŽôFÔ|êðc>ø|÷úŒÀU{=,nYÏÞs>9aÿîue$šÊÅŬüíýx­ÞäXóò“UÉ"8¼ùÔé¼eÞ„>AÞØ!æóœA\Ão† ÿj·É—J…Ï¢Õ-† ãë?ô¸d©(Ûyá«eÝTzñpifí ìÜ[Ï0¶ÒúæÙfqnHˆk³’ŽÓ©‘¦0Zk…ƒz%Rê?n>*ñ…>aøª¬6$½ÝSHȱIÐt›~œ+ŒËŽûã³wÓ°¨b}ùdgÚa9ú^g î}¹´á˜Û%œâ·:þ  ×ßžÁ.GEºp8ŒÈ7 ó_žDöˆÿGÆÇ@I/žûÀ¹ ÃU&aÙþ%tþøuå“òï°%*%Ù͑µèxì3;χ%£Ì²ø0¬H âëBAy¡;SP«‰eŸ»8u0ZÿŸh]e…–±ñ´“5róÃ…{æ1vÂFôüæÃPô“mèc¦î6&¦ÛxKÁíì©…’(úaŽîLƒïå™­U?Q~´ÅõÆpð„6]Qí Ѳ8~YØX0¿›ø3ÊŽWp¾üWˆ°mź`hä>Ö;?6Öü4Üá› 'ËØN‡ÙttçTrr¡Øë¾a þ·þÓâÂY¦j*XN¥gG·â¬«Ñ´jj(í×GmÑÉá5×þœùï¸æŒ*^m~ ?Àô´üþ5…ºWP²}5?mR#¡—ðù3L“¯Ö°¿BÔ©TöL"0“Åÿz¬W‰^Ä«?[˜%¢¯!ô“"h½Œ¡úÏ'Cò g– ÓËçÒµÏÜÇë4ðrÁL‹§Ì¡KZÌn—û8¦FR6—áƒý¡ ¿ä&ò©x1}êA q<fëDb¯ËE97 "xþÖ?YhNCbÐþ¦É¿õ|ÿÓ×oÁKØ ÎiLÂþe 2+˜Iøðžºâ‰-90;£…ÃÏ ´‡ Y=͘.9e3e~3“o²Æâ6¢Ócê¼4ïª6±=‰bÄ&G•¬ß\[b qcùqg›‹eëOâWIòY*O5׳¯sÈﳂ&¿|‡=5Êp?Ú†¾©¹`Û>“Î ±vîËÐ}AÛöZ NÌbðöÉp0Ný†<!Ÿ©€¦pZ[ H%®iR¯LɃ£°c¹Z¬+WÐvóiÜÏ]ç‡,œÈkªàѳßð¹ñ »ô²½ˆ]_|=>Âí]›@gY¿ïEù°€‡ø[Óú ùÔ§o vyO|/£"> ­„—FËñ³ÙSôÊÆµÌŸ›¯™‘_›R:¡yy-âçvtËzͨ4.C×âJ,¯|ÕH¾¤° ÕaJ­N¡uì%Nì%|·‰&Éσoc]L¥ó5NðGa8à6•ÈZnAÅå«ÙÁI`x¨ ’)Ä©V’%Ñ09T˜,ØÛÌ„>á#¶¶Âì±ðR«tˆÜË~§Ç^3g&/€—rž°7Õ ÕÌLØù;=6ëÔtp]4ƒ-?N¨²H:¦‹Y•Î*u× -FuªÄ¢žÂÞ$̹~“¤wD³ýqˆÌ†ËôAÎJÜeä@¥oáåGšäÙ`&yµ2ñœä=(3ø­iæšñ™9Ó÷ƒËð4nËÅã5Û©¤q<1©‡Ñµ„òLRÃéGÿ×ÓLô¸þdZØQÓîç糄mngìg^ÃÙ·þ±iÚçÈÆìË´º< DB>2ìŒíŒÿ0T¸¦âm²0‹?„H‰­¤«ÓYåÚéäòÐþMº»nŒ¹²]“¹ûí$–*kýÕ;èCø‰ëˆ¡bÁ ¹’7צÍj„¯'ï8Râƒ6ÉQD÷zÖÚÙup¦ÛŠ6Ò7ùANg5=›òÖdÌ wìHÇ *¼_ V¿ãhÍ£=vkèìèûÀ+ЦA–_@ƒoÜñ‡±¿2¦§ï'|ÝŒXd3Îýêˆ)¢°Ø=†žòÒfW2ÜøÎ ¨““ȹQ8üD„lþ²‚&¤?Åñß&æ Ž]ÓÊÏ`$–€¶wëŠ ;Éêþgð#õ9Ó¥”ÄlÚ±3³¢i -]”›ÊŒé@ÐðòÏó™ò`lWŠë±± ÛI.ú:!iÄþ2?~xM D¾ÂJ\`PNþëK¾"N‰‘=çñÑ*&!½œ„úfQ——wYEѤ•ðêÛìÍò¤³¦ßF‰@røÑ+;†­#E€£Ká‰.‹¹)þ¤OjÝyÜŠ0÷–ÂfT#Î=«©ï¼"´ M¦¡ž§ØÙ¯í˜ÂÚäÓÑ{D(Y€j>äkl•àrÖþâ§›ãy'êŸË¡tpÅFbø˜MßF·=_ ÙÉѨbfNêÌc1ûé<ð/Îbxeè³ñºbtæIv[{ë¾g‡K`dÎ äÑã0Ûwv#ìY²" è¶#Ù±SgQqW 3Gi5wÌ›Ÿ\Ã[Ï®ã6¾AŽì !:˜}g‹Ë£YŒ f0šy›i‡Á)Îb›à?Bd¡!û¾Ï̧t÷ðï¯g¶cÙý˜SÅ9zSjTyãʱAØÞ G+®A‡º·Nd=dž¡sšNZoã7]γˆƒÁµ¥Xrs•)ÉBÓë»±e½'°ž°?¿ü[6cóiT \hªHyñ—]r㤠äjÉßùô›Êt¹¯pÝ{@ƒå#ØdÑÞ4£¤Õ}ÂGÊ+3 3}û¡ë›,ªo/Æ(æ±Ð4<,þ Áäð5ÈØ„ñ?—M¶G·0ëWÊϵ_˜~±ï ÷ß<ylÛv⬹jLµƒ#‰y@ÈÑ{Vÿ)9'ð‘†9½ºŠ¡±òý¸Ã£—«Ÿf^Nº†ܦCE¨Q½¦ ÞÅ4ù‡®4;mÓß|oU0æÏm†gë¬<¬B÷X1Œbw6lÞ¼jÂþM¦Å8ÍÁ㿾af SÜAµ‘ d;w«bVãlŽˆÇ¯‚r—$ŒR“l8º<ƒ|’_‰]ÕW›ŽÜ»œ×Ç…‡ÈÚÖUÄýqnñœ­K…<õqzO|=û±šDn¿<‹»®•pSVkPÝÑlvtC5x¿»>ôTøC?_Ù˜(ÒGµ>ä¼Ý(²n¼F»ª£à7ëæ_ýHN*Ð|ÿD?Ê’<½ý"È%ꨄo¶,¦»º^á‘á\nÅïè1ÆåÎþ¹”J«¬…é ððd7ÈYÞ…'ſ鼥gàFøÌ‰ùƒŸÝQÍ*—£9ç(8G63çnØQ·ÍMÌ×]柽LJwÓ*³·XsÖ÷¿bé…³°³àîùÅ`‰Ãt¢¹ù*ˆñÑ2CI ’&«lüÈ š íXûæêd'Àá3S<·*E®Ð¬ÛX/y’ë•y‚õÖ+£ Œd"lü:—saáq%ªØÝϵUèǹâñ`~sC×  ÊÆó ñFˆz(èAuÏd"ŸúëÒñëIvV£ß…Éô´Þ\ Ž¢ûE«€Wn1MZ¢ÏíÝ?Mºùiá¼oxÁ<¬®ýßÿŸÖÀ­;7áºû–TŒf0³/}e‚4ÄHíý—쀹:q?úƒ&Ó=úφCø³È©âôVZƒÒKýñVôjE“¬ÃшÉt“¾M½ÿëg>t+’ö,ßAíá²·óHî{ȸ}àÿ¤[dhˉ¤9ÿ*þÚþ• ˆûÂuwZÖ¯Å1^ìšþ‡†öâU"°+bŠösìÿMè?Ÿùr¬}æGh=iÆtl•E:8‰žÖ¢NÏ1s°Ý>Ä0/Õ¦Ðÿö‡žÇ“9[ùhÜúñxÞý¸•‡ö€iÍ.ràa3&ú>d¶äÔ°avz¸Á`6Jë†Ñcå˜6fF/§ðÏ!)¼çm@*ª’éñ4A6®÷,­½yœæì™ «B4øSëf†rÍÄ®“úDYΓ~ÚÍý>/œj-ÛB"D#XÑ/Ù §^ĪL€›éëÈÁCC˜7]žü}TZ<dÍ”‹x¿ŒÃ°¶l”u)~™SÝ –c\8™.s²ÇìåË&üßÙó²§R0ô—­iÁ7µèš“jT2V‚4¬;Íþ®z5IŸÑOàù.nü~{h«„Àß·ˆŠ§ˆâ=Ì_³’¨öÍ6œn<Æ+2Èd>'Ìûï9hæ‰ÝYþ¨»LÖð\ÇÐv X>òJqŸÛe6YâÉ?AQ¨Iª»OÛ5be\6ü·ß³ëW“σ—çiz"háÓIab§i⎘nH‘ñk1Ëö(éLSÆé{ ¬ÉJǾ;» ëÓu®ÎîÍtGD ãT§ÍD$$à±Ûpaw.3~Ì)ûßü׎{Õh³T½%1cØþ(„â÷Ý83)’ð¿á£yk:QD·ËžGRv^>6'mæÒë/˜œÚ˜‹¬Â‰ >ƒcʹnŽôÍëoLÅ7vzž ù Z µo6PþÎP*æ$ó; ð"³ò ó¿>åúKNY0yH”|fa¨v;U/–úê¾×ª¡ZúaÄÈ«^oØOÜ”gÑñçƒ%Êä¤t0½§ò·øÌ¡1ïZ kh9ô¼.~C~¦É¾ü Èèþõ¤L:ÓÔýˆF]ùRt wªÅ;ÛôèôvLzzkÿW³i|}^¶,†eKñQoÖw÷¢Á¬™àP°Ï CC¹6- ïe¤¤ˆÏ+MòÝ)’ÒŽŒ¤8>Š”¤Š%qP³Çœ¨ÌÙ‚¡w©æ¿w  KOŸõcy…¤ÈÃ\°‹]FLÕÉ®!/²[*“¼[›C þVÃpÒf4Nþî'ä¶ÌTšó|Ýó¯%K)yo¼ã'Ý­<\ÎòFÚ{a5kö’I*1Ø4¿²KáL-?™ÖdMEú¦» I#à¡q–}S‰jÚn·p×ìŸ!]˜mމ þ2­£RpOþð„ÿÏø;ê'¾r}t×SÿJW#²ˆ:EòV¡§(|ÀM’ZXͪk«DÉêÖdÜÐ:]' Åo}ð Ñ 7~o£‡BwBPM,r¤o£ØÝ÷ɼ«±p'`&±Ø"Ç}›ä9~ÍnrÀLšh-|ˆÒš4W&Æm«…MRÝLT µ(dÇÇ†ÑÆzü÷*\-·q¤_ÅA¨³6íùÂט˜ÉsQ3u!Y5ý5ºŽ×¿SU¾àñË™ðëõBRÑ,Í>oODÑTIrv Ú4Z«€Ëþã´Ñ^‘¬¼ Eºë<©Ó—rOêÓÃýßúïo–•ÌS×|8xŒ\K“§¯Iœài¼0 õÍÎ5 Ë~ÂåÐÓ ·ÒmK.bÝ!Æ5Z—H(¥cùL%¢=ç"~r]KŽZ$cë[^ê,4ŒWk`ÊÂ!üvþ½a0Šòã5 ¨®UÒ™ïþ*®* ÒOÁèºv;‰˜‘@•LCqzæ}ôŸ›¶ ŠT¯í(IÅàn%Aÿ}ò Î¥J4ôG ‘˜T J=gˆ¿ÏmÜwtÔw£ÞŠ0*`F-*å Q8”,÷9Êì22¦¿"$9ëŸõá°s`TÌæe§>qyþÒeë.ÂßäFbß½™2SÇ•‡Ñõó‚ßÌu˜ð'‚T<ü‰o_ Ñ}’‘dîùL(Êk¦çxkéEþLâêòWÁ›f—™s$è*þ\ú›¹Fåè0cIS12½<ßõ×Þ¾ Æ—“ÉpQSÉ&ƒ÷±©÷<ßeÈŒsÒ4"Óð—¶Ÿƒ½l/ì³-$~ÏEéäË<Ð{±Ð’>¾†÷ÊcaÃTZx%~OR¥™}‹Yv¸†åÖúaákÈâÀ÷—mï¥$]+¦ó¿õ/™±lGinN?E’ž*÷OîÔ&|;2—‚á_° me7÷^A©—n4åÖ(ˆÎÅmÿ°Öùÿ`ç´^̤Oup$Ëß=åø ÷*ºWruû4°ë÷<ª® E»U­ðSnsþ»"­Kd±xM œ›7^«$HÀÊ[󉮊þÿcêÍñú¾÷qSæyžgB†Š¢ðœµ%E!RHs©4(cÆHŠ$SƒY†’ñ9k+i¤HšIš“"M¿^Ÿëúzÿþð×9{ïûÜk¯u¯s–µ!Áÿ6²“X¾3“ðøš!ø¬Ò Ísó™#õÑ´ìÎB*f«J\Üw0mYsqÝuKý= ÒPö¯³a÷ È™êJ+ÇbiÖÚ*Ÿ¿´G±¡£ äº6y ú3j5UˆÚL$ @ªÉÿêŸß„ë€â¶0&,}ÑýmEŒspÆôµx(}:Yq¡§pQ®¡Ur·‘µÅ)dõŒD¥:žÖe`éô-ÛEÏŸÀ– •º~˜sǽžÎ¸W®W’~}yb#BêùÓ¨¶ða"錾zŠø÷O!­ÈžDŽ® %óžYÒêÑ8¿R’îtU"ñ$ÁÍt®߯wyl@û,škwœˆÝ°!²KuHΰ,±×u§©³[غʹ4THƒžu–ÇçWÿÓ³ÐIIüZ”¯à9~ó~ )©YÄ=%˜Äɽ…Û_ÔHñ¦Z,¹å=ÿ[·ßĦåÕ¬ ™K^&ù±?–UÐ?‰_¡Qí}ïHŸÅ`ÒÒa”Q™Ošå°ä™"ôˆ£\,•ÿu—½³'™.¤š‡”P½Uõ©UþiM®»Èá;ÊÝ_,ðPÐw“iBn c_ù!ê0Å ×ܪ…Ôuóéªyqt~Ã:Úúl®sϺ„3~ž¡Ô}Ðûí°Óê9Ç×%†˜lìÛCòÄ«5yHÐY£›è: TÑkdu&•Bj8¡îëpy6yö1mžøÐë:SHMH6éS\D[:…Àõ— 2’¦=¢«&ðOKÌýÊGŠ‚æâ½½o8'S`¡OÜäîa?|ÕÔ!æçÔafÄʇQÊÛE.o;àòóó“ÑÝ*¤­æ6{Qþ;.Ë´¨ìÁ/åájæoFÑr›ýöm‘à õnÞ‡¹÷^2므»¥&=u\Þß‘ } ¦4ú™³j™"}>½7½±¤ð3‡½×ý¿©f3ßÙ“ dkÏèþLC^¿.¨íêdÏ4„νJ¢æ°¸5‘ ñ…ûèd)±ÿõÿQLÏUåp)!v!r´ýÂa/ÓÅ:§ll ±"Êï~0" ÓPí ‡¶3Oô\ià‰JV¹ì;šùô ò‚nú.u:šÍéG‘Oì¤Îõ´íøL,‘±aÿC°f¨›-P¤"ÅûiÌKœ`Kš-Ñì„ ú_Çô± RdCºT°Àû*ø*T ’‹9•Aμî9w²±Ý¿ÌyÖ@ÝÖ4ûäIÇ{• ]Üo0!Ÿ‚êiŒè:¦Ä¤ 3¿é©÷¹˜$UÃ-0s ¬®)'éP.êʧUGŸ€Ä§}tÇ=KZå²sÂÿ>ò‚q¼›©¢#àâwolu¥-“Ü ýæ,jY“‚¶û0ç”%É0Éă.Çq\âš<óµ®ÌH›6Ù~)=T©ï”¶xè­ˆçhšÜ•g°jA"®ÕÚDïªn¥¿w¢ÏÑàÕ7‚ËwµÉîϼôŽÞª(Ý–æóÿë.aè\ZØÁlº™¯ÜSP¸5òNümŽ™·F¶C÷›ËÙEc3Xç&LÞîé«ådãäKtD6¬¥°ùÎs¸þPžÜïÆÄÕ†"¿q5ÿ0•H(ü‹ï½¨W>‘ÿmLêe—ü9¹¦T{X­Q@Õo+èjñ­dÉáa&éË/¬è·|ÓÐWiý—‹áó]è}—&j¯ðøÎ<ô1s";2{áC§9Y8-‡ä NæHE¼6õ*·†Åç¤0Þm-Þ¾®¯§Ë®r××LÅyÑýLá×)Xõw ½nLUD²¡³Ž°ó ý¢ëA<Ù›~Ÿ[ÌÛFоbKž'$Á®/«1W× -|29ì 5Ü1óräÓ¨ÚÕ`*¼~±2´ãÍ]ø4°Ÿ7^NþÖÃ@8ó@bQ;%f •š²jÂÿÛÄ6¡ÛȈw>Ëtî^Ó0æµ´cëöŠûîF&£·.zWa§u$©:Cm÷6^_êÔAqJ#<)ÂS{õIL䪿Ñ_RL}rwõcÄí øGʪè€Í‘i Þß7ç6"éTÂ’Ë« /(€ú´¬Bã£^Ô3:Žk.µ¼ÃÙR3Kú{Q0ŽOëÀº.=2T¢†'Ö ’z[ÖyæB’­M?%EÂ5Ð+äÑ"¡›!|i8é¿Êþ0YOw`HéF’åeDc- hÚ¯ ì…à#P-¿ìÕ€¸ Ýÿþÿë\ #?µ×î:Í*ëaýØ>kúoÁG®+Z_‚‘›útgèJø.]޳8Âø‘{ïæêRææiÜV;È®\Ø€×rÈéðç¯1L¶V¢“B,iÖ‡7ì$ϰh‹)ms½ÃÖ!~±Ih²:”ñ¸ç‚÷Yɳ·Y­a:_= åî\ÀåC® 5ÿ!&Ä'’ìEz°p®y%ò ‰4C¢– C‡]›àœësì@nên†ýy“}ðÏ¿´?Ò¡¶òp{w6÷ùœ_õát]—)¡]HWžù·.-vÕ…«ÿÕc³)]Åûß¾³F$Uáòe}H[º—šÅ÷‚}®‘øƒÁÓaVá ”9'âñäÜäꀓ  ‹ë†…[Áþɦ8¾ RK5!¡£ý²c™¡ì[Ìøk+ø{Z@vr4¤e;ãC!ô¸O'oa˜Ì»_æhýîì^e ¼!W0sÑ1Œ0OÀõžx\E š¬ù7O;ë^#ÊÎõó›ä{ÐÏ/Jߤ ¼,É ]GÈßq 3‹Ç¬›©¬Qa4(IHQ!¯ ØÌËLý‘ÍHÎñ&ÓVÃzƒSŒÈ YzsF;4½/=O™Àÿì{øÄ`§Ž#]wåèµoƒþ=<ä´Á\q@]­èzizZ\—dxêýJUZ;0ˆgh‰³`ûTvqÀ™3þâ߈/ÜÍ6õø}$è°N6à§I×Ú0ȵ‘Ýú`' Ðæ‡-/åˆu¼Þv¿ÇüÜõgíT'çCyìM3Ñà g¯Öú2Ág¾cëCgÒ0ÖÈåI®Á)3½ØYÁ(o°´¹KÃ(ÌÄ)·x¨@üCšÖ‰¶ ãØÇü©J½þ$âW…­äˆ¦ õQ<%Bppçz²Lï)ª²ï¾¸Ð–ê»ø¹ÑcLù>6Gð˜öFÑŠßrÿõqÍ’5ôL?°®üäÌôCËo 5¾Ç>ýÒKO­ÁrS˜i~޼|#I×Oc°eÅ~öÇ =ûªPbî+NTP.M÷±‹c°ØÛ.òÌÄuÊ*”˜£rÙÌ,{–óLFÛÀz+}ºOz ut󦂈ÿ õ4† ¿€ìœ'øfzn ¾ûpÓ©Rxܰ‘.¹w̾ßE˜޼@K‘&N ïJg6AžB;Ö·h2•ÞÌ…M‡%w¬Én 1B{ÎÀ÷…÷ðö}ÎþÛ phG+.µ²"öÕþ(ÁçuæÊdîžÜ;RýzöDrþ <–‡K¹2gü™ªÝD¿žŸ8kQøÑ…6Ÿ¯£žš$Ùz× d,€bø ̉‰×R_îßÇ)ðs– ¼™÷ Yð v3¥Ç}Ñåš,qû`ŠE©Ä ³hœ;=Ž®Ýý³«»ØsÊ&(­¬ATÔïÅε¥q¸ÃZ24ü¡ùxÖYN¬Ý4\líJCg|…?­šÜqsÝLVOÄÕN>ÄLó#£ï<>ñþççÀãvå~ˆ¹¯î ND<Ócp”“»ù$8DÈД<1ƒãì^Ä|Ÿ‚wÆvpæ†}Å'y'­\àý*ÀzWœ…W2eÙbIº„¯R@ǹ¸×ÄÞŸýƾ¹šÆL½gÅÎ5BÎA! ÿ0È^ö¢{ ÒD»Ý ¯b‰ËoÄ1 1ס^ühçÎF?E[²t}*žõrdVý±äž• wn#ï5˜¹Óƒðõu'ºH:–áÍNÇ5údCÐ<Èqdä”O™ö'<Åÿ¦ øÀ_âŽ[¹`¹Ü. <ÞŽh”ûŦN5ÇÈ+sÙO²yøø[¦õËáÜ/IÌæüÃÁ T ï÷~a•xMa®7?yš1 (ÜPýÈúf4Áÿ'cCÆâN 8'.¢×J¡hg£ž ÷,§­¹ìÚ›µ¬wéÁËN:«å+ªXq%³>ƒ”DqŠ'¤ûñvƽçX¬Ð­ÎÓðR…9ÞóØÜ\8š {45èթΟ$ŒæÿE÷+¨ut2g™ï' ýcI^K=`—i™>KQt †_ƒ‡oqÿéfý #­úôâ6;7õÃ$ÛYdæ· ¸‘¥ÆüoY×?“ÈÒP>²WÃ$±bè F”Ì€âÃf8Gì®É• ý¯ã9‚Ardý£qÆÙCŒX—œÇÒ£üßá_†ƒ'ã0,Z†¬Û¼Ž”LÞAþQÑ—ÞTÝÝ„¬óŒ¥Î÷ªÐÿõŽÇ–Ê7IÍ*"v¦ìM¢qRáëfMªë–Â=ä¤÷Ø2˜/æ[…æ!¯ÇQß½‹´6À7Ëí(k”²©;Aøä$Ú0Uæ¶«“MagašY4˜J ‘%BÐÒ¯ ŸŒÁ¢Ó Vv-½ cËy^J³,^±«çci£+KÛÔ©ÀXsˆ×;J ‡Ò¿a·ù!¾’Ô}Z@,1ßvÜ÷«’¹+mC/¡¢²"´ï©¹Ìéin7™ÀÿgºvôƳÑÇ@×o˜­ëƒWïs¾x¼ÃˆyBÄiô³„ÆÍšxoí/fË<2XkôÛaÆò(¼S©GZ~Ô1«Þƒ¿»¿13ÿ=ËÏMgÀ£ë ò¾›A3âœàíÎf‰B>;`v‹}°î5»r`'èoù›Ü¥i‹g­£Úw^‹ÀÒFÄëJâܻϗ13Ó°âm%Ã×s‚ù,15XaÍŽ½í…©¼Qè•Üžå È‹Gðö§uäÁ¶spÅã'ÚüÛ/ÙÛz›Ë‡œØóÈÏg¾Ì;%g´ýÙ„Ió¼É‹ƒú?bªž;™ »¼ÐK‚XÝR˜÷à/êýÌÔÆÉ£‰K93S&¯oÁ¦t‘XjC/,åß×&Ct£01> sm{ áÚóª/@xmXEjIž÷8qÍo>bW1+àî]AÚÜ`„Ž-†oý‘ ¸8Õ¾J ÁÞmì.Ô*ΡNV«Auê'h2ºÓËíqÑÖ~p¼O/¬Ù„¿Õ¤ÿdÉC¬-…)C·ášI(n©uE¾y왤‹îᨸ)®\]“ûÕøIÅø ­(ƯUÄq’-ÓªQ_Nk[•fû¿k$‚+yg2Í÷Ô¢–3èò¼ÏŒ]º=±bÿÒfÁ‰ÌNãoƒ²*cLö½ ‚Ö¥ðÀ´‘É&C'ß0³|¾rO|Ù7/#¹áTeŽ’Í©æD.ó3úÝ8=CW¨°ÿUÍK`å[wIr N×ÐYЛ±Î}ícô£])ï—WÚw‰•ÑƬ ÅteÂ*Ú6NÄ%•àL” I2#Eý³éì®2|ì+Oã|ˆÔZ3QºŠ³G: *2ÀM…—†ý AÉDSÝ~‚Ï]Z4d‘X_¸ƒ/Ö-$ê¢Jøí'ÝØ÷hÝ Ïô‰ÙQXž-Àf~ÌxÜ.¾½ ô“_6Þ,©Ã¸·Qôžó'Èo9ýOwÇ0œ7ÖDáÄ\fÞÕ”~yÎ\ÚØŒû9{è¨ þ»3q®™ yÀõ°¿úõhšž…’¨h۔ȌÌAû§™ú—~ÉÓ%Î3VÎH>TÔ§Rmо6–f˜•ïÇs ³ >ÿ|ÅÎ+D§òÃ_É÷lG˜æ¯ ¥U2Už ÊvO¨.Z.Ñ ‰¶ŽÄœ= Åãjüç;ÍÄqOqÏU;Ð[\„[ŠU ¬¤AôÑEך1ˆ­kÞ_ò³"Äaµí_©GMÆëÙž—Y (rÓø•`]X©¾-Jƒ·BON9zÝ1§£ƒ¡ã…8Ø-¥NouÉ”A 0Ž”ç>, "cò1ŸÏîù7»þ J%Iëßå‰@·ýý‹&×1/cŽ(]Ó+ÆéjPOa]Ò¬DTÆJY‰ º²û+ÃWÌ–E1Ý f­/9E˜©í¥äóS3í8¶­‡þwþûÜ“‡ðæTkvó_Œ’B¥—£Ð¹aÞY°ÓûÄàø€:Q *Á¸6u’u*”Ó¼ÒŽüv¡.sï´)0wÛ+áö·ÉÄdzþÄþŸïÊÁ²ÉLvÑL ù‘Œÿ¾£ðBË´w;s~·îfuüWÓ1dÙI!VÉeÝ~_œ‰&îK0­\HÎO`ž–A€´<^ž£D¢Îöü×»™­œ{ G¦œÅ›sžƒjšå¤ZíÝ‘8Ûª•¯° ù5©OÌ"2Ú qVUøRqyÌå÷MPÜ0kü&Sæ4€ëeé»HkR5k˜ñ—~ ÕKUáí–“èÿp™Qr´Vé‘÷LÑ b¿Æ9‚ÃmŒË}oÐÿ§Ã×e{£0(L¤¹©ÍàÔ"O òL)^±°ÅUUÀiïÃ[DºqXKÊZ±òŸ~BÞh&^Þ; _;iÀ—”%°y~4ßâ£Ç^6ã×3lJŸ:êxCá²ä­2.öO¯¼ÏdO»Š³RÃѧ“¡<\ª¸‘ÚÑf´Ô™ye›@ Xí$Üèý…1*$wÿŽ#îªÅXã»tµ¥X lÇÞä (¨/EœîMæë‡~±j0xëAì4ýPÀæØœMlÇs¨ü¹(>t— Òÿê›ï‹½ƒ—{‰qEOÍÝì`Ö4ºmµ($_s†ÃM²0銟à_28ˆ©ÛÈâýßžd·ˆÓ˜Ê¡™¡á5›˜]O®a¤†(m?„o#FJG@éëNØ|ò2³î>µ1T[`oü&†Ýx¾v/¡¦ÆæÌܹ¨±æ :‹Áë·î6çI–p…/ Ð×Ñ‚8¢v¿I¡œ1}| 2+ÒÉ“×Îï?\$sáëßdöx`LÚºÿ® §¦Å[È ïH¨²N£âßP)n;¹s'µ¢â©cê&r"ræÿ"vYû¼ÐmþS^³»¾|fl‡‚`v‡ }2³ãŠÍɪC÷1me#> ±&•÷ÐÒ_‰è;Óéõ~(üD­­áâwúac$ÜǼ¡$²‘ÿù&¡ŒæÉrt}âiŒwÞ*®Ïphë#0݃¼«úвå$*ZM¤§†ÑÿÆ·°8JôÖ’#ê©XÔ_YƒñÈ¡Ã%0åO†½æšGŒz‹1f†Í÷þÕük†ÝÅg\ˆí;Æ^ÜàÎãr´n–Iw€¾Wzd—ò;\—œC†¿!ÙÜ üj† ÊÒý'þå.Û>bŠÖt*áËУ*·pGÉsîâ\†ÔÜǡ“ظðÆxò8ëªFDIt±2{5“•þÒ‡ßÂ/1–¼•¸JpF÷+ƹ\¨¿¢€EÀ¾>2Ä®ä-®•Çhï¤Ó@ëC2_?tœje²n3oc!«i¼RÃÆÐ#XíõW7äa[Ž$=ÎóŽså×ôÜõ>H`ÒKM\DôààJØ-- óKëÐ[ÄÜ•]Ád­  "RY¬E!þ›ƒëz²˜ôõuÃSß1—· MØ¿ÌÄÁùó£uúuøæ }ÆåUA$»q;åhºiU!!Ì–}¦äŒÚ?Ûµ ¦š_ȦÍð_m—ä7P¡:ŸSÎBðm;²·"íŠVE½fðiö(gñÉo¬-“—,]é–¯\&)Ђ¸ÏŽb"?‚¾P<&ˆW ²¯)eáàŠ¹‘ÈŒt¥7­áŒÚ)„{¤%kió•Ž$P“&þ鿥•YôrW1Léóe›`‡V ´ÌYJu¾ïG½<>…ýŸõÏð&ôÝu<ú«ê§Mé¹°!rÛÿ. ©ì­V _K°áÉ(cÙÚÀòëkCOÔ[NTõM\½ñëó›ÿÜMÞFAš»<;©A…ä.ÁšÐgl%î#Æ»€l¯þæ]å `wDQ¾®BÌ/PdÖñ©’—Ëq8É’ö­™Kj8LAá%ð>­EvT5¡¨wÄ %1ýÍJ=GèÕ'Úd™KŠÈ&Öö&ŽY…<ŸïâÇ÷Š4Tí0»ñ–ˆ;;§BEjîºë"@"õL]ww±~éN¤5ó5û~© ¥žm mªKÕ¿Zâ”Å+™Qñ8Ü8¶ïÙ:ñþãÖÏAö@#‹«äeèÂýÔ/åÿó mé)€/ÓU‰àö²5D…ı—è5ç—'p4Ê®húÃߣU䲓‰)sÄY69P¶Õ‡H}ÓNh}èO¼&ߦžqïÈ*¿*ö™î0,w‘·§÷0ß¶eÓ;EÿõdAŸh˜™DÓ7¡‹à6TĬr¬Â¢i¹ëõ–PiòýR9«Ý š3±²ï$>–”¢m “ˆn¾,þvË#ùbi0üá«yàmZ $5X„Ž×Ä«ëúÙg³†9¬ësq…O!'ϘOðÚñì8'Ò†ò(ÖЈoí°ôœ.yºT•^P8>5`–E"B”hܧ\NÅEòÉ0 x{ÓêØäý÷ai\v)Ÿ;ä‘Í¿2‰ÉëÔà>¸*ŠôÆXÒ{–‘5îËȃ\Ï@Iìo߀R ÒóêqÍ«228‹—ìÙnH„óò9.¯KHܺ8Òó;¼õº‹góÁ…ap Ñ%a²'qÅ&}ª< ׯ:H…œ‹ÝMƒ¯M%.O9¢-ß8¦·Á·Óœš%rwí¬ðyêš«¡ÑP‘¤‡ú“Ͻ9¸3æ,ýOg÷/¸…—EöЩ 3ɉ“×±DŽAízAzeº$T¦ã'gm² ÆŠöÛ¢I§Q¬U¦ZÒÍ8k¯´§}åÈeþóa{ÁÉo5ÍÛ*Äz©KîlýÎðèwƒ-QûDŽ€Wu³H…õfº4¡ èÇ«$Æ_‡Úáùq5bXý‹lþ³ ¯ÖÐ//{ß­¸íå^ê:bHC>o'¼Ê¸(4¾Lþ šïÐ;¦ÖòS{gt*´QçÂËôÁ€±Kñ aÇc&ð3 "ðá‹ÝäBÁ$â½|óvo£w«ß ¤¹S£Èâ¦Dw÷ƒÅò;1æóSÞKÌ .‘m±´-ç/ì<¥N~„¬@¾Ú š¯˜Eb¥hÐKjKuÈC¥"|Â}‘CyLÛò*°¨oޝýÊþpnÁþî4vÓˆ]È¢›©P©­I~ý©ÄøÚ ½º\¸Î]ÉD_Y~;fNZ#&Ó2ásäÎà%”1…¦Ìä!Ë‚–ƒ ±%_¹ÍÔeêòµß‡zðSûÙ׈ç.+ºÜkº•ï€8@‘Eéí™’gŽ©²­ø-woÆEç÷à›;yœÈS ˆª¨ ÑÑâ‡éµO˜â]’84•Ì\{’¬4_C~@¿¤Ýtcú_¸œú}×Ðw `×»wX<äD¶öbñÍL˜ê}—ç´âåi@¬o ãV>MZÞg-uÚd®gÞ¯æA³s'qߟ+°åQ~:ÔÁ~›þ ø¼H # Ï`&¥oCß²Ðo>‰-ìÛ10\ÄâÕ©dôÜ%ÜWŒòf«iÎ8ú‚ƒ3üY£;\k™ŠŽ2ÀÙ^”Òy5˜£‡øhj q\íÑéa‡j÷„ÿ³7ÁèKîø+® µßÐI±ÅöÁ\δŽzöˆŠ ñ³ž,@Ë«FÐX;bÒ-Éâ}¶ðõ¶/<—a®Èæ‘°ôW8ýe#ù;mõÚG!×óß3ýÄîŸYˆoÂfÀí¼JШ¨„ØÐi¸¯6€ã]NˆËÑͨº÷( õGìO §M°´É˜þ9 ‚wϧR6ÌŒÚ{ྕÌÕ‡7“ͨ‰°uH‹¥n¦<`ùz ¾ZS…¢·Ìp~ b¡5ÄJÆÄOß×>Y‘ŒÜÌoœâøÇX‡Ë¹O¾–cm9ÒtBâWËÓ†—ŠäÚüiÔ[sm^D,M `òŸRöÍÉŒ–íù ûßáv„m ‚§C¶Äwï œª,bù40±¤/[yà§ÑÉ¸Ì ùìcâ5õÈu=Ö·*žLóßžCˆkÉdñêÜï äK¤$pßI“ãå×ÙÂ¥.$!kuÉKË`ý =<·²~¾ÃãÍVÄÁSÒš}°®3†yèUHF$›YÂy´fîºëÂiø7z¾×ž¶glé7gë¯GFþ(І½¯`0h]v$wHŸÀo{:±dÈ·+çàõŧÐt¾IZ(¿»Y¢2`3Ï9¢É÷`sñNºà…î„ÿËË)c2+þù ýæuÆ&6*³Îk’Ó×ÒdŠÿ#H•}„Ÿ’¡Š2 ¡@ú§1æoꀭŽå[káÝ’zA€âí|0è> â†ä£K£ä †³ÌgÑM“é¾DõÑT<ïûk\|9£ÐãŠDcü"ã#D?Ò#aŸF‘7b¨N{ B‹·‚ìÉÏàü³Žæcw|›õZÙO›‘Ì%[/ÃoÛG¥hÑÀ¡‡U ÿ«ÍÐÑ{? ·a¼ævøo»Ï¦5%òôãGc\X| æC%<š8Á¿Éº3ÍÞæáí³«©ZŒ$k!'E+1f† ‰ XDƒôM©Þ#L4?†æ lå:šøaâUŸíImòïw<Ý+A,5èFÁjº £Ž•ð¾îñ±Ó]’¾ªæ#kðtïÔ:Žv< BÉYÝ0TjBæ Æîk°ø"˜*-ã#É¿*Ð`ž<‰¸ÑÎò©®aÿ[›×œãpl-JÏ™†#Óº±f²»–‰‡¿{í`EB%ì©& Z·™Ò¶\àíd4gL% F“èE'sµ “éì#òàÕ$:|–²’T‰ùøå þ}çÊý3оúDd „Ó*ºÔÿ óAÎ’¦q:¼±"=²aêÎ|´_ Í‘÷®ÕBísãpù¤&Ò5VnÞ ;78q5Nï¡kfs/~>A½Àø+xÿð´›Dë¯|€}_1gAƒ˜ªA_ô<²²kYð§ æo…à¦ÉlüÝzÐ8Œ9åÇ1iþq¦ùÉn(Šý…_¦ÐؼaæÚóÔ~ö6ÜØó Iã,´H罨ӌŽlàÄ÷/¾+ø}q3³Eä ›V^„ó&ÅÒ鎘¹ ÏYéâcÁ—QúÞiõœ¯0'Æ—ßC¡ #hÙÍœý´¾=O~üI†¢ãØU+™t•Ýv±æ(š€èÃ÷·à]sÁ†›uxœe3M9ùGNAšú9VîE<ðåä@Z–"1ÌÁ¯oµé—µô¯‘<†ßÿ+ÚŒYûwß9_¼?(‡³DÁóˆqŒOùÁnHM£V/1¸`a½7a˜B42%¶ñáX´ÜšÄÆ¿Ãû 1¸]3˜ c|[ëpumÜÿÝ{Ö†°ÿL·ûœºYKñ¤àEì –Å•ËTHÚÙ6¼ëÇËyßÿ€ó¨òh›5€ÎÀ)ìœSïN:áX-4ó•ðÉ=e8yP‚*²DTÔ·/ÞK†~0/V›À¾ªhÜ©—’!.êä;'£QL®ŠËÃíþ?˜0ù—¬ÍKi4˜¿…}«;²iïñûˆ?”3å†ÛøRNš>tÊ%ïKLXËöô”s¸hš:Z¥§ãì1 Ê­åµ²3©è¥§Ìû jü+°›=ÙªIê+ÑQYY\ìü‚í¶”#<‹/³wÔî³9?¥±ô'©Ì“4ÃíöÄz7¾Û ŠjÝxK‹íÔa‡9ÐX3ƒ¿‰Ñe'’q¸@ˆ•9¾ÒL¶ÃÛShiÒ"âQ¦N–5©“3í๠/WÁX…™ô¯òkˆ”± µ´ÃzöàÛîÝ×þ€#¸T‘“îaǦä¼/kÃHßN1ßÀN)s‡Õ¥fdÅâ³°j y–¢B~…uËÀmŽ0®¹p„tvâÑ]w8»¾ƒ%­…ðèÅ4ºö<¾àe»¿Üùµnàà+³£Õq§Z+ÊDÐí^‹ã¡ õ>f7ƒ|ÉJX°_žjg.%â…áøµWqœ-Ù¥¥¤í{ ŒL£â‰ŽÍ~—t™h 3œ¾€¤_ˆÃWYò>æH쇨…‰8[$žú4Ý‚ªùyøa‰09£1>>î—²«Èαlh‹úÄ‚-¨{_48g1¿s>bÚ>Xå‚·í ‚'#ó_ÛÁ\/™ÎxeL[Å“¦ Jb)Wå1Ò[„IÈ{Æ¡7€¼z4ª+êq×u~ÌøþZkøI¢ƒ‰™ë ‡qM ¼½Çî>E[‡§6Ç*:ÍÊÆ–’j==¦ïU+ÔeÈ“Gµ/9uë<'👖ƒ§ežp¢s2yñ;‘r~Ï owþóG² Åì0&æïFÑ7Ô½&…^vF¨{HŒ5QþŠó2:Ùþ“{èqÕ›(à% 5Α G2Ä`ýa¼õâh vùî´µLƒþÊ-…Þk¡Éu^ ª±Úì•ßíL˜ž;Íl3 kçKÁA=9âÿx%tj)ô¯“0Üñ),x#³2ïaÄ2 ­öøSsºÜ­ÃZw é§•aSv1ö~·ƒ«Ûœ°§y6 ZÌt?_û̹-¡HÅ IAö){>ý7¬ËAzx“4Ŧ“ôÍÿûþuIÊ…k¼3 r~ÅÙ÷ÂÑ6æìèJÄI«£þ² P:6†!Ùœ¼‹˜“c8©Wˆ¾}§A6¥ö°ÝŠë9z·5IÕ¢˜òWŒ¦]—Æ_%ÝÈŸSˆe_¶áËJ8§‚²¶à«óo›4¢Õá¢ü·æ=D‹ºlý÷§Kàg½Z×0is³˜éÁ$³yÞøÛŠKl´ik¸½X F\¯Í€.# ^W³É2³H—ìjt‰3¥ÓjñØK]¦>¶÷ª‘5÷ÿ×ÿ5øg24Œj“HÒ§¤Þ”åbBç|µ/­Æ~{ðR÷…ñƒ,~ïS“k¡¨Þ¯qζŸÇ^û<*¸U›ÎoïaÐF(îªeøÎ¾Åµ¿ï¡ÄÜ " D ür˜(Þ Àµl7 ’Æ»ÙÄO“ÈüvÐY ÑNq?Å8¨x²f¥Â¢RÏR5gÀ(jü³©w#Ï44è]¹gÌçþóÌE¡®ð /œgý>ãb8Æ~‡´¤vœç·ˆÑz juð=¿•Ë·“©›¯1 ™OpÃ*ĽŸ8ä*÷ÿá_>¸ Än›3o²ÜpíúAìÏü© `Uf,ë»})ð¹$B¥§;åí6û}’q0™m¸¶CˆˆËÕqÔÇS¡1ìüÖ”&µ«ú˜ä{Ê8…û‚Dª1©¶‰NNÀ‚ÌØ,& b_¥ÉÃ2pÄ6 él£|höû§ç¦ƒWÖQ1\ ±w|þ·Z~EâôÜ0º“^w²_|¹0e·4ü޾ËHïN†–½f°vò¿<`¦%:iùáÐÔ¤æ ºЖ̬wÕÌ–?L<9ÏÔ%¡òÆäÁuµ{¸˜ÿß>}mmÍÓŠ7Õî¡#Âä^å`Æícš²1‡ÈÊÊܵ^ƒt Š :4xd yÁVM΄—mÃèwû'l²ÖƒKâbxå’*ÝZ³ŠöLèß_§ÂÂÝLyWÍ —¦åÂF„—ë1ÝŸèØñÑ’60üûÝ‚ÐhnPÃ~ì­*Ãß[C6¸“ßâ’Ôûº%û>”Âm7mê¡«Î(mÌÀöU]ÜæÌÖÞ[žë6<ޝø59ö|’täÖS PV‚)3“àŠT½ì†·ìžDÐ8Z=Uõoµ8úQ<žµä:H²+A>R˜4¾3£“TÛ±ðÆ\nŠwì?üº­ÒÙð'î被®V±ƒåüœ+—ƒ9FÑÆyp2½ÿÌŠ¬UtᆠšÁA‹oà0™!*jÙÄMÎÿëqÎ:©5 ”1ú^³¨gO l ÚÅüù½ # Š2ý¬¢”i¹ô ;¶Çb–Ñ{äòZ@˜æ::ZÜïGò©ÎÝVØãLáȽ ÿü–ü¼¿†èOë`kL²S¾®Á°Ö¿¨¹þ.÷Ñ‹•ìì[õ¨£‹cå"d»¶9}kûn<<Ê~Ñ×·—ÙmÃ^\Ëåç¥N²£0wÙuFûÆRLâóÃÛ»˜4çB\iMãÛ»ÁCyäÝëòz`vH«P'K'G†afQ ô[‰³Û-íiãÃØØcBßL6d›»ÿWÿ÷2Ö³ªew0wU-ú&C–Ÿ9‡]Ópk¤\ÿÑHdÆÁ‡¼iðøÇwœQ™!#ìW?M²`÷jØ¿+Ž„éϧ¥rõLyl> [R ‡OhÃÕ8r¥¸ŒyQÖß•¶Ñ›þ'ÁåG\¼ó“{÷1Jña4f´‹{ñþy´ÊäA›7[ U]…ÙN%ÃE–ÐÜ©BJåf±ó.0;…ØK2ñÉØ_KbÉÅ›j$£n& 8âE'µ6À×]ŽtÌÊ–ö <ž?6T°XŠ$¾2…ðÅf´êo S«gOG¥³º)Ûà„þ)r«èçþùFx©ø!×ò¬%꯰$O޽æ{úžq¾mň<º€ó# =ýk)%ñÓGã˜EuÙå˜û2Gȳ[ÝðŠÆ„“³1G©Æ£Ïìò²cœK9§aÕ3qš>ë$3²‡— ¨•´¡/f^ÛyRÔ¨þl ä”V¾_{Ī¿cv]‰ Ï„­ˆCÖ"ÑÎÕƒéTÑŠŸé of»6éÐÓ‚mLRb4Ì“Ñ#—ϱû£†™•á¹X~qò «r,ê¯-…J›RP=êFšß]Åܽìö›g¡pÉB20òÏ\7§¹T]LÞþÿá/: >‰Ÿ§•²) £)WêZ$fÒðnþ³1¦ »ŽÉχÙÎÔB>´øiù·$òÚSœ˜T•âïC?…ÑUÑ$Ël©Â&7BÞÎ…ølƒ Mœ[çerÐZX™Ôd3«ƒp Î\oÜIDDRèÍF?”Ä{óèáš~ÜÖ¯ârÜÓú&âße¶ ¬{±pœÅ†‹Æ5›nà%%ÜsM2äÖ”$úó¤jJ”¶íhæNZ.%Ilˆ*Ué9S ^aþ^Cxßé@†¾ûh±#èy•ÞÇ⦠®4g ½X'ÊWæ†ßt èYEºl*îTÁ‡[ѦâÍÔr"2‚‰úz;¢u!벎pÂVêR,x„“—'‚ìå(¶+5‹,ºvîýù]Âqß]qòR5I6gÿý «ˆÑž` ¶²ÖÖ¥3ZõRp É”(VÊ’;‡øh¥÷[Pùø—ðX² N”aŸ~ôÄ÷ï—ý0“/†¹_¦‰n2­Œå†¼ØèNÕ ºÉÞõ©ä«åIÒw`yº¦•öïÍ&³´É£èÃ4ó7A—ågÐö–*YÊ{ŒÌ¨EÕɨì©Ëð¢0€‰³Ð#‘ÉLzõShÛ¦O(ñ´üÜ;…=;t‚ñ &+ÖòÓõ+}0§²Ä“¼ÉƒRL>£M>պЅïcŠ—/Ù|hùÔ(Aæ®à5H+8H®9¶’­ujdoc's:õ5||µpȺâ™t› ñ¨”pÊý±v)¯Cõ Gì~nA4.|± þ8Ÿ%º;F—mUZ?¿¨+GEáèO1Vº³ë¸Ó¨ku¤¼S¥{nТ#ºör,ÕÙh…qãÊ´ï‹''þwþá­ùîØÿ:OÞSR—ÓÇÜQøÃ9hŸ¹ÒƒJQhë ¼-íÎÝ6ïwìy¶önaÌÞÄ-Çã`ÓYvËâÿÕ•kx£RY)ØG‘U) _=…úx½ÆøMëAÉÒˆiWÕ¥mƒ{ðö"!b¤¹‹!ê¿Ø£j¤yZ$ç±í¸uü üÃEƒÀUìÄïÉ¿1}kþÿÁ_Xk#ã‘!‚k–S™Qºò’?ÊÅÖa; gû법÷¾áŒ›¿9gGÁU•k¸Î=žÓ/8Ÿx0ÂøÛÅžT^²a‹Ÿà‘$Ú)x€ŠX‡sDå3—i0oªâ¥o3ˆû¬Vp­]G÷/[G¶_òE«Ë²Ø#`ƒÏŽ‹RÍd%pÍ_M=³Úqùš„-ÒtËiΞ˜ ]Q:x'ó(@`s ?ƒyÇ>Á¨§™ó^—,tö ›ŒèM›ËìvY¬‹W"c¡:ôå´»3,tÈ€’áGغù>¨,ò€þMITåì ý?½º–ñÐìûgqûª40ÓfÜ«=ˆhŠ‘»îHV¬Ã¨¼x"tñòšg'sßIJá±yä`Þ&šYòlí4ɲŽð\a3uÍæ'}µ!x9¬ù¿kà’d Éßq û2¤H³ž#}¿Â›”¾5£Ù±ÿôÏb/Úb—®»…ÈíØí8M_‡‡cˆÃv.ù¡£‹ãñÓ°dM&nX¢Ï¦üd g;Ð@ó.Xÿ—…3ׯ¡IåW˜ùΈ6Ž˜Óå}ôO3øü"i®?Olö¾@ÕÜXøzH†i7›KøêÒȱSi¿ƒ8Yeö¿þGû_-Ä:aÀÆù¯!§1ˆMýî‰Oy¶²Gv%ÙΜq2Jqnã`fÝä¾Û—Šî _˜)õÙ°iÞGvs–¼S'fÓ8xqú03wôsÕG¯}…Ëye@ ÍÄžObLg¿ÂÅþ¢´h¡?;s¢Ù+bÑÜÀÿÜ}÷F8äÎå+0Þ:È8¸n™+^ààe¥(ؘ¥ UßTaæFy¼EÞé]ÁøîÞ~n`Ù[h¶te7ÄòÂøõ™Ìä 8’ ény²mûHür§hä² <Ž¡vRæêJáoà»™åþƒS_t<Á8>’Rd@s~ÇàèBMšâ†ƒí/™õÂ:ÜSo¿ˆÃã{6içbñtx.†=(DzqoÆdϽTrïRÚy;Û¾ŸëÕ%D'?YÌÆ$*ÀÅYFDéuz¦_ó{˜sãÅlK’ ½Sš‰Kwïe,x± žÁ4¯¿GfÒw¿Z®ùSÐ;O®)`]#ϘNí—q¬õ2'›êÙ©’WQ‚ý—[pï3ßÅW“s.°7„'öf‚,Ñ+aÖ¤qŒ6¦‚„ßæ†æ~î>êJ52³Z)äßÀeºõ°­e}L¼ÐVºä…Ù³âÊ¿œè®é Ä¡i5ØÎz¾×ÂÐÝø„‡ÝéôŒýuˆÿ:Id/èAÝ.Øümc¿½ÆOå‰$£²³8‡çöGk~÷‰Ú,vßõ ¸Å6˜‘3Èù7|˜IÌ¿°–]’tNÐb˜íþBFøÆx%ä£"} ûŒál9„Qý™˜³p-T˜ÍLîÆÎD)ÚŒ·1oÒ6Ö¬¢Ù?&ŒAÊòè±ãûw‘|Ϙ1ÿêFiZ½¦Þ=½Š3…IÆä¥ôËázµC’„ð ·8nÝ@R·P×à­¬PŸ ½÷X<ŠyÜç47í"É[¿s¢Ò'QgÌg®è\Ǹ8u¶w×Iò¢ý þœD• äölrôâR}‡Å¹:¬éœGßh«Áuu´ØGµ©ÓMfýÄoü]æ1yƒ÷P…eï`…±)yòݔߓuXs+B ºqAÌb6¾µ=TDÝW¸ÐMËŸ±–›ZÉÍ‚ÙÌšG@~ácôu1ƒ¸ÒeTí> ÑÌçí²ô³¯\ñ ¢v 0v&"NÕÇlè¡à8œ¿f'ÞzvŸ¸Ê^¡U:ZD=¡Ž[]x}íɼ‹HÝ)²y­ i´Ç’uIä÷£ïœ ·~²« Ì“~NgJ’éIº¤Ð‘Î(º†E–¸´nuÑkoO’¦ŽO gN.6ñr€w¡%ñêT"ª[¢ÈÖ2IºA±ŠþþõdßöÃþÀ}ôÜ‘ÐùM¨eDñÞdªLí4ÍhƒKžêtæ¯0ª¼r&¨û¼…Á]ä¿ùœÂ((bë_Ð<ô“£®ÉO$¯äboïÚߎÿø ßrü}ÑžÙO/fÃ6ÀÀž~Ê…å³hÆêh8»BƒZæÜæLŸŒÓ†VB¹Ð”‹¹ª§¿rÿ%—¦8“M ‘!}ºÒ–_8 ó†ŽÑSæ2÷lÊð§b4µ˜¤O­Å\ 0AÆ+c [ºˆ´Q ÃC¨Â°‚3¿{å6Fêo¼]àŽw÷“a#YR0GSg~†—Õܯä_Œæ›c=,SqnMœaF'1»Ð%EÈ/ûÉc!ðçv*­í᫓ȹ=:bšigq¶ÜM&hî'pÝ|äœHR…}œ¬IŽTG²Ó?˜’ÜdOÚ["Hlƒ[q_/3ýö4n| ¦Í;FH žXÛâùFâ.B Íš4î$‘<ŸØGî9Ðþî,V%ôS,Å-Ÿ@'“ôÔn"Û rñƒ./j¿±¢¢®BËE©é-a²lý l €ÝfUX©þxÂÿë:£ëpÈtt1y:…$÷j<çxàu÷~ ó·fB Ž¿f˜µ/ÖÝŸ *¼×`øs:;2‡ñ,šVv…º³`è˜ÊäƒÒòü§qýZEL¿ÊÐûï7Ò°ñtÚ÷²["ë°zÑ»ü¸í|C²m2œ:3ê—ç‚CãdÉ‘IôüŸtþœÓxd%‹áWÃØf¡T”=D9jñTÈ^ òôáJƒ˜‰üÇuš ð9ËÖ]!ã|<¸Kêì蹿‡q?Û†ÖÎn”÷$ÃÞG©ôѵ¤Ù”9‹ýA#è$]½ÈŒúH¡ÎG%ðAÔsèu± -,N{2!›EˆÅµó v—¸‡ ôÙÉ :”i‰Þ†^¤P± îf “Ñ”U°n»$î;“>áÿ#_†° ú»ØA‰£¡û¥~éQß…óàó…}0YºƒÑŽûµ¥0øˆ¹ªÉ_#èŠÑ\G‚œ“>„,‹£Æ:Lú?ý;(!CÑ~-µ|s‡=üéøä"¸íZ/ö:1f#¶$¿Õ×98Ї#|˜’ue,@c¡á3;3‹íYÁ ™§lD`ýé8¢4&gM5éÛöbÈ—wÅÇßÓ‘Ï@™|  5ˆÖ m®nÓoHÿ¨Iû{öÁa¼ôEšùiø”5Ù~ƒ]z`:®ÌmçŽ}/Áùý'Pak“¨<¡ëgÍÆÕøó± [ZÿØ^úþP_FpI²͈ ¢¡ŸëQuÎ9<èF’üCi‡ßnÎçYUtÆïm¨Q ºV«©š¹ŸT‰Z»ÒÐô×Úà„5ÊnÔŒ+¶Û‘Âü$ü¼ Š/î€g»û™ÚóGÙúÑpJ÷Ó,>,2‰j˜.%•#k1åx-‰2$MZZðûA »Ð4 ŒF˜‡Z¾TÀâ \»8þ–L¦s= ]{ ž?ØŒwFráæË7øÂé9Æ?Õ…ckxðêS*Û~×C«¶.­ÛîCßFj0m½Vt¿Ç® þ¿œÎÚœbl“Ï;ð¹ÁîI½G S£NJ%žB×akД§Z&oquðHümÉžWÃ$…u0ªùÏõÈÐåL,ŠÊ½gt$Ì麯"ô­ÌMõç§µ{$`áyßóàðvÕÈzïÒ\¶q7x‰rèÔ¦üœdF÷ž9È[Ž»¤ŽÛ“KiÐö²ÆYWü9@uk£~²Pv ìÈÄâ*†^lÓcN½oE®¥ì¼>¶©Ì¶î@´88ûe† ®ºü2ÖAlãVnòìxØ»–¨V³ ]uö•&&öÿóµ* ¯ÎÔ=ƒß384£iSiÓ…Óÿ05p6ÝüC1   \jAOõM¢÷³„¨\‹!©~¶‡1oü\ŸÆNÝS‘Jêlò-úëš!.{™²Œ<@^#L²´¡ª_£¸¥“p물ô&’}¹Ô íËÈÃÑ\üQþ ¿hX‘²:ä.?ùª’êÛRTÄk&iʵ#Òëbhå:5z¿_†ŒÕ72íûû zÊ\zÉB‘JÔêà©exUä6EÈÁyÇ8YÚagcÕŠræ>7Bê™{WÅé‡ÚoÀœéÂ&ʼnøwxt„çtÁØúƒµWä2WãÒîù4¶×‚¾IÐv¸±ñ<=Ð×FÂ66á^—T"¸'¥UÈ¡mT&˘–/s Eã8%”§eýý<áùO©ùЗ¯µA5:´u-ß³e¯U£ã’jêrž&þZEU?M!3ö­gÆŽáM“È"³`P|­GÃy±Ój¢Ñ?Š ê÷C}Å>¼fÊ|îh£–®+ÈuQ–®’:Ewµûaþ| |âGà·ú.ZtÙ ›“m)|¢?߯£<.<$JY—JÎB )ê·DzΔ‚°ÜÿÎÿ;'žŠ³%¿²SˆKØX^•‡á2Yî!;#´¼ïÑd×B&þü˜ õ®W9Œ~Þ1x.úÛhL† #µå–çêÒÊ;¢„_¼•n\/…;Åe Ù&Mî~‹dú i»mǵô¾z¸îÙCרºã¡”t´<ÊGÏ QbÙÐ×öyVR“·¤6ðô\¶¡x¹ž>’…“­QTÆf+ý±X…<ñà%sê‹Iæã©tö¹Çx-\Ú>¾r—‹Qyé|<ÞL4]“üý¯™ŠP]ò**š¬–úó?˜Ó´ž»àœÚ€þ¯Ó²›-|Àe³óFàè²öX-,ÐàZì“dj _ƒüß`*šµŽ¬?rV­¤ógL¢Q´=~6 ¡¿w¤±Wö¦‘[ðþ¼‹L“OÏšMT†®ó™Dj““ù îä£E/{nN*f–¸@0Ÿ.tõf Dƒ4Í-`t·Â!å6ìÓ(Ã)àƒk›¹¬qî 7lçnwy…7·/£qÁ<ôÖÊ0:¦ÏgÑàaùŽó¨ù;3÷™±ºFhEyÙaG|rbа- Í-‘½Áû_4#×îA«¹ï?öT`Ï+8WË;ÿàn²»>—u›œO¿7êÑ5n:TlëSδ+SÁï– ªú¼Áœ˜ý~¨”+S«÷ÁFŬ΂'Ï芟@ÖLu ƒÛbAÌó ±q:Ǭ‰[My=Ù 3¢®Å9ò1v</dçéÏFá§§pI½,ù³å%ñ=…)j „…¯ÃLJjìï‹Çq‹K%¨TÇ`dÆFÜËB+kww’=V¢4s~­õô¢çïÂôƒz”Ë ÀïKÏ&æ²°½»£N±Bá.~â~L¦ûÂJ!lE(ºÞ€®q4å¾ ™I$òOà_,ïUØ?…hºÐ0Ý :+CHÂÝJ>a^Qx;lJ÷ŠCÇ+ðîÄ8;ôüçuêwf´ÿøÅÒ'¾eÀËjQƒ3÷»/ŠëÒ;ç/ ›¦E.t$°vb=0ëT5„ÈzE:ç²'y‚äƒo6æ}ïÄ˯õ1œ’í†.ôJ‚•޶¡½ï!Œ'y~…¡Ë5²\¬úú4Ç x=€JøK|rø!ª¾(}z¿àcC3ì‹uD§U“ÉÎ ×8޾©„ÍÁþC>8—Ÿ—Ý~Ôãíæx¿$®>Å%2[føJ+î«€+ìøöCÀ+qq÷‘%õ7`…ˆ: K('Õ{7p85`æ×>îûçõŒþž™Ä’Þ@>£õ°ø¥ôÈf>%£[·œ`}ˆ&‡WÔ°õ›=Qñ…nóS¦ÑÊõéô ÔŤ‰¹¾7µÓƹZêÆx,—%'½/£û÷ìêy#è£GÖ‡}æl+§Ï|Dæ CWúLèÿR§ÀBC­L§âØ®í0nÞÄÕ*…}¯ÒQqï_œGÖ°+ ^áŸqÜŸk Qmúd\`à/—œD#sÚ/h„×Jx™gµRX#¾œ:ð»áø·yê÷öê K²Üû3ª·¸C Â+NxøÛð½ÂÊÝ @£ct;¸›Ky )µ˜£ÍÄ“':‹˜+Ϧ³íkÿ¢ê'kúNÖœl»^¦À»×o gÅSÜ÷ž5¾þÓ_Ûƒ'‡ôØÙGîÀOn?d$ì`6WœEÃGa¨ä'Ar/¯ú—“}â¤rÒayx*½ä'LOÑÕÀÚâcë#þ¿}p tÞpÆCæ<Ôn– Q~:|%ðgÁ4RŸ[€/+|™ÈOž¤ûe}õâãHz—@Цë@»?CÉÀ!à䚣è<öeW ›º`*óæïTVO0ñßîófz/:£HæMa—¼U†,5{8[؈F‹ÍÉyÇ.5Ÿ s~à  ²ÈŠn'Z˜¡£Aó:Aù;IºJ1_‚(cqµ³yvŽØ2˜†ÓÞea^}<4ñž‚‡ù`Å£Icf¾aE”ó邵—±VMÜ{›„CIläbIº'‘#ÖÿrŽ…XïnH£T¦LðŸä.ÄÞ÷¸ÊE¤3'Ve6¯.Ñ%ßö]r]^bþ% .Ö‡û—S C9Å*áN°6Pnc"çÜbEÞœÇ ®ú$e ßþzƒ8íøÅ…L7OVÐÐCÕÞ1Þ¹d ÿ8ns…›O2‚2˜}uö2<ÃïÏ$É–®A¦×Òza9.zK´d‰_0l{ÍðÝã ¿*ñõ—a6ï3ÎÊÏÙ¦C0q[ÝÞpìõ0‘ÃþÅ$úùGFBS…šJÂÁh O’ª!¹› c>zä Ÿ*\0ž‹+ø…èïc2ú÷¸gª½øëP¦ÿà3œt\ð»M›¦ƒ­É8 y‹»î|2Ó™)Œ~ƒØÏY¸åùqœ.n#)–ÅÑd-÷:™¹—ÖCjTOs1e´ŸcAÐ5ÉÔËîº0¼ÔKçu\OksÓñ¶}9rÒ¢‡*MÎLõº¥éLØÀZD®ƒŒïÆØülªOj@á6IÚ¨H”~  } ØXsƒ- H‚uM×Àüþ{ð[ó~.eç¦NCñŠr¬Shäd—‚C¿5â¥<.ÂŒ ™¸«J„¶™ ‚@Dëþ"©,,J΃ժ&$#D*7kÀÝ*MÒàp43!zr.ݘ§¯ÅBüÍ^_¯ÀøÍ²¢þ,>÷[qd2ŒL“%‚ó‘}mF6ˆÓЦ2òl¥" ’߃G qޙ묵Z4†ÏP¤‹Lš0€›‚Òq’äÉn8š}§ï¹ÂŒ^¾+Êúè-‰•¤Åæ7 l”§½žö|3”‘Uyw¢A¥0Œù¡'ʹ C‡†qÑÜ‚Ã0sø †×¬'óÄ^Àä¹Ùàg”ŒâöR³ÕÑÈ™«MìUâ®z.£®ÛÎÌéÐGQùÁ üËþåf‡xd‡Tg’¡*IçœßÀh¤h2³TÌhv˜-ž Caíkðuc·ôó%.»±×)zë‚ÄÎoûµr3*íd ZŠá‡ï+ì­´avõžƒ¬ T¸þ›8¶ é¬JìÒƒ§IdÒwUê§ €ºÇX…ÇÖ ù_ðçI`#Ù9¦ ÿò€ì̱cKN²s˜™$¡¢—áåLEƒŸÎ¾¹x§-¿ý«AJy_KÜãÚÑÛç ©ÛÒ@,î%‚ù+ಠjÞÆIÝPǸEƒÖDE+Ò5Ì“ú™¸äŒíDü;™ê ©ý¦l~è æìS²êÐIÈšJÊÖ±_ûÐø\þë<ì_uÛCÅðÛ Wš“9¸jËñKà PŸmNw–uÂ7+ˆ“êb·LGï2 2ïR Ú úÁv=àÊîƒ*Y}2¼ÒJõĹk'‘m±£ì§£— @¸ž¹å“À\Üù5ö=Á?ƒ”·Y‚¾]‚âӠНçªÓóW3Ù´Uvœ¥ÁgÁ~ƒ58œþŠÕå‡+ i¥ô|œ_™úü¾¨­Ž&¿- Îg zµ¬ý#㘥û³ØKqs¢óxß„ÿ‹›ta~2sÖ[‘ɯDÈòé¿Y‘rôwNkrBý·há⇠¸îl÷ôCml®Ãû•]pÕñ=.·íÅÏÍ _×Î)–ý vƒØKmðýq'sÎÑ´ŸDÝ…Óhxæ~Ø¿N „ûMØÿ’^pÙϦþ¼w¥~ἺÒ-—‡HôpxT°Àz̓c#X–s™sÃBŽf×wàÍ©¬IþYè;{ØÒÅáËhŸR6÷Cm&N¡ÿ®Åµ;H›ý#fü­<|ì Ìó£}ŸÀñUzþô…Šòä üóX!rÿCé·Ì*Þzô8s”xžA­©+(©=ƒï˜qÆ#ÊôÄE¿àÙþ &ìB ëæ2ýï^ãóÉÖÀã”?šƒàrÔ4šÄœí€)#U¸eŠ×ô¹1=”$‚ÛËѸE g˜@ǾOàuþ Ýõ¦7n1e«î1¬•'Ö 7áÏRMú¾Hf¹èC›ûl¢xZŸÑãÆ27‚𧼿%>eÂ4iW"§/Ë?ÍUƒÑšNö(Ý%}Àïþ†9úx–ÔMçzî¸åd&î[}Û«:ØÃ/+ù¿â§ã Èïˆn3&òŸ§]g~%vFO-žz‰Í{ÀûË^^5€ÏÍ|ðÙ6'PÞ{ˆoYÉz/ÓAo ªçu øŸû‹É$áûQ&ëÛ6‚]ÎdhH✋(R¸¾ËÐUùºäuÁ7&ên-”û«“ÐÎPós*{÷ym¾¾Ï ®Úäý![ÜrÁ î**pT63kux™·r›P'¸šÞ0©ƒPŸ‡nú ^ë#²C¡Ð'œH²‹`Á1ÂîP æÛyÉÉV†8A-ï$nß ¥`#¬u#é}ç ¶1™è…änâ.:%NqÂÿí÷Ÿ„†O{8!AG`E”Íú­N*sØ©ÓõÙˆÚ(P:âïÚ3F×°ùß‚s)äÞZÍ™Cv'×±*驵UèÃ%[×ã™[רŸ_B0ZX¯À¨” nEÍÙ— ,~:å²âªYP£!Ÿ:yiœ÷sTI—FÕº¸b“?à t"Ù£ƒÐß­G‹Z÷°Ÿô]áÅâïP¡©Lì½ÓÙâ¬Qø`H¯o…ÌÓ9_C¤&¹k5ÅOIÐmÇÃgBÔ}î ™ôþoŒ!¦ôC‰0Q¿kC.-ç!C¥ bšûÄû¯¨Ê‹p}c.{ð«ü]¬ÃÚ=;gïÜŠ~tï:†êÌpƈ¨BÜh½›ëûÝï-ZŒ½cEœïö}Ð?E›~¶ë‡ÙãshàÞHìrEÓU°mËlí£ÙÚ‡1Ù]“ÑßjÅfëÙÒÊy©8u»>ÎŽ ÀÐö`Œ›ÛÀÍ͸àž* ̤à>è¾V :áôÎRî q18 hbK2yÎYìáÂó§Ì]ºØ/ž#ð'㕦/SÍÐõ¨aLÏäLèXÕ|]°§Ÿ=Îòù±ÃKAbL‰l,‡±n)Ú8îEfé‡×iÆÄòâøýÅÆïFÒšècäoÒ\ûøÃÀcíeb¦Ð—T‰º¥hS­Wñüà!ü¡ö¿þ·K$°T%]2Réˆv4÷Þ€#\hÇr×°k¦$¶’cx‚›ä¶*Q®`—šÝÀçgMI÷ª 6¬²wô·¢‰ófðž·‘íìÍätFë³À,'oºÖЫ+&‘Œ£(Êg»«²ñyT ú&ÀÙÕ¸¦6ôÄGÙ¨f7b}+˜(9Ðyß`BŽ;_á;l¯°e¯•I`a ¼è¿ KØÛºéðFç<ìú­vb¡ãO±RβXü. vßÚÿÕgó.Œ¡ñ¼Gð–‚³æl¬û²—2ó »Jè,Чk~›àÈÜêJµAͼ >,8}[.2N\yܤSÆjó¬!kð<«ôðç=ˆ}T˜4O^ËÞÞ•@íz›ï‚ôââ,Œþ˜ R;ÁMÍTõ`É;mÂw±‚¹óî*WV+Ov1X4hÏÝWÅ®ÜÂAuõÇÀwá£6¦ÅI‘ç`©úGÌø›‹fÉAÔúƒÊ.id»,TÈû/z¬jõƲÈ;6—‚[kM›/­/[)·çŒôs"{­ìÿÖº%¶5öDzæµO¸!Ïâ‡#Ê”9}‚«3šM»òCˆS÷^vžò{f{B!D½ªÀ_¯Šãw¤iÀQA²m-œÊCªÿxss–ˆÒ/É\,Cd ç§D°kŒ‹Ck`åNc´¾-Emt÷P×pA¯4Ÿ¼ù¹b¹Ølõ_/Ê(:vBžŠÜ‚¯ËU¨{Â2ôe5t³uËFÀÕ¦£—ÔaÞŠú#v?Þ'HµW- [®JSÎÓ›œáÕÏpèhûYÓ™¤ÿe/Óí(È9ï$‚éb Ðõr\édw®•%'ž `cLNëyÊVcÓcôJ¤6ݘ“ÊNÿ†sõÜA%U†æ„{SO³/tÝÜç0¯ÙjÊDÉçŸÝüg6Çr5·<`ß<¸€vØbBÆf|½z ^0Åî“°÷˜==¿ÝR¦Žåu“%§bf|ΙÊpq¿ ˜—Ê#ËðŒÁs¶èùBì]ªƒ²ž§šyEÃ賿ypŸh’®C(2Õ>7ÂÊùkÑÈà:z_±#ž±'Áÿ@“<­Ë ¿Ò¯c²f ý(þ/–™-Á5×~°ÓEèëí¾”ïÑ}Vr•éé©ÄÝ^špTpZ\!°º%‰yl?ÂõÞîMn­ÇgÅ`QÔ^<¦hB‚ÑÏÇKðc}4̧al}V7þcèõŠ ý§m߇N²Ýöô¾Ç Ì#ëÛ‚åN3ô QÂc/ÙŸoDðæøææãN{0ïóCÆîþè™ÕjÓAÑãBøZ]ŒÇ§•‚ʳg`2š‹Â-à;}±ûSÈR¯Bf?o%³Þf Ôo$\ïˆ\?M@«Z–q«eç†YáÓ‡Ù ÿw}ÜÃ&Æâ“> ±ÃÍÚÏY‰ãWP‹¯ rÖ³{<Ÿãˆó?|c›nO"¥+&ÃÑ¡7ÈûÚ‹¹}¢ÿ¼Ûù2ë!eÿ^®âlyØÒû ž‹ndNî4¡Ão¡®î8jªLð¯ä¸·ðŠ2ç$Ií¦¹xDt lÇX :ù÷$šUæƘ‘/'méóƒb¤]m”mƒmá™(cBJÒ§½¯'© ,vº9äM9I¥÷jmå{¸EÝ„¬é)cž™ÙaER ˆlœòm×XAßTßL«s6C¦ÏÐÑJ!‡•Ö‘˜6t«ªÄ+±q¿ç“Å3`’K§”L‚¹~5ðpa |1%Ù­!òP4}™{ŒŽ™—síGΣÜi Ø›þ¬.–íZçðÎ_ >nvÄ\%‚ý!±„4mR%Ö;c!öøÿοüò(‘e6uÂåvž’¢d£® v<çrËÿ`™E;“{»‰YÇï ÑÇäÈ©J´þÈT»õ˜õ·‚*«]d^æÃ÷ê§°è4>wT³Äéßn³×• ¨™°©ÜÆMÐbã×±Ïâ>€RÃY<œåŽfñQœ×჊ áDÏgJç\ezÅÀ䫈gÆ$ráÖA¸½Œ8Û=ðñøEXu@ë—ðPËŽgà4/LvyÆìQM†Â[<Ðò§ ³õeìꋼ`²Ó î ølõ奦8²E5IŒ‘áöt|¸Ný†¿—K ð8ï„ýÛ ƒd›:´uoþÔqW¹.%"»!~» é:²özÕsÎf쌷dÊüœ©ÀÖIœ/‡»G—ìp;Îæ:]Bám©XŸ¸‡Š:n¦ÊÓ͉ˆ~c²•²+o¦ŠsI[Ó5è­,Çí»ðƒ^2ˆäœÆ-Âè—©÷0O½ã>F±qE®ÐÚ'Å-²£¹#­Ìø¹t üÛÅIm†Œê©ÓÀšvÐï`ÿ«Õ؃«×á5¹,ú¤ì4¾¹’ݸ`%å9ü½ùO¢³B¬»;‰Šl $·"0€Ï”ŒŸp¦i“Mˆ×ÿZ—O33Ӣر‘œ†¹øtÉ *²ö.ôíHÀƒ¶lÎ9kúà“8 ©\ aÍè!_‹£¯¶O=º+>ß}5¥.m¶äÉv®­¤ó0’‹àõÓf«õ½›¨ÝÅNþ%CÌZ ×<HKÖuÓ˜DLŸ‹ßᬗ[i*âÔ®óã^jHžÙ”ãðB&qû[¶‘2ºû+avE<ö4õÇþkó:…[œÊ”Λc!ÒWž~J# Jg¹ì;y2³0–Uöñ¦;×_åÆEî†3fÒälí3°ÊEÊèåCØ4à1Á¿€:û$;wþí­$)f¬Že_UXýjYRå#Á<]” s ÙŸö³ÈÒzða×­æE%i{]:†I Fœii,…™…!hÿí8§Ÿ#eùô¶q™íbÈíë˜I\?Km—{ÐÄ–eDu§<½ü|)uFOúaQ\Ø©A¥ýX⾡ì_¾y¸Ë*Éß) (ùõì®ó$q×¾CŒ«[î‹â·rÈ³×øþB&•o°£µs©‚´Îÿ×½EÑó¤?ž¨‚ØŸ™²”9àÑ[;~UÀíµÑŒö È;¬3ÑÐgpµ^„Ü{· ~Ÿí…kAìIÃé$$¹¶z¾ay–Œâ–éKpþuzõe*v´£Âý}TºD‚~<|‰kQW _¸cyóh“Ývœ)"ElÍïÚG:‡QCGTÌX •Š`ñY7|¨À—ù2-üÄv4à} 5<£ŸÊh–Gïõɸ|ò~"öd&.”î½ÇgÀ%¥ŸsÇ·Œ[$®‰&]Çí~ëzV8ñéDw9v¢Žùõ.sâûŸÙWNzî8q#÷òî‚å?¢©}ÞX¢—ÅòÄ£ýžén툽y~di¼?æËð½–,Züì†É߬AöÜ%èØíC,ßÛE@Ù“Aüð|}~q-˳a û†ïöõ@ËaI<Õ8ÇEFÙ#—%˜ƒkÉ~å˜np‘ͽáDüeÁðŸ;÷ÈòºÏÞDÀù1;c9?©Ð*­O÷r,¥<©Æ!ŒØšÏUð9LÓ‚¾±¹‚‘ÿÕcãÊQ)º>!› {¼âT¶Æ’f•Ï °‚(¿J´>K^?è…’¶ŒP!ÿ„ýJ³ìó¹g˜ö€ Üÿ¹«é1åéjÔ½a5H—Å­Æî4lI»t}sã-ªYßf]·—³¶ ¯ØYKâ¯J;˜TûÍ_­a£É_öÔw5bû8šý_ãŠ>liôVøÚmÈ)x•W±“–m¦b/·ƒ¤Ö{„§Ö°áÎ? ¤C#Z¢p«Ï ræÁN¼rZÛüÏl‚ (7ZÂ\˜­FÇ»™}Šž°ÎʈÊÎd5m•ÉB‹Öæm›MHýRæ[óî”õ‡é¯”3xj‡>MúÜ BÁ È1(†ù^ù°’_‚ÎÜYÆÜžï?ñþ{ÿ"+p¶¬d çÄQî•0¨ý—†WÄ;ZÑtú•šûç f}i‚\:Ûs& ¬Ë$eÆRT༠Q ìÀd±1¦ïürrðµl™Efî¸L$²7çÐ(ZU¥E%²?ИS‘”sö ÑÖO…ÿj–En”²šŽ´’#C~æ,†£Ÿ¦Ð- ,9½ôÙÿî‘xZNB{z1ð€üàhƒW‹:ÿ&¹Ç5§BMqï?úãQ>öب qÚdLÔ|ÀÚzÈîÓ¡r"2fˆI ¾tÉßÉÄxôúdÉÁHÕ2²q–5ˆ]¸Î¸= ‡w1üûÿÞoN_RF3Í4c‹*Ô=«‚$Okªó)•&E“Éã÷¡YÖOî2a—,)@©“rä£Ç5ˆ¹îOwVÖ1=‹n4µ%0ô¶|Qi†5µ·ì½dÛ±×û‹ãu´Àõ‰Þi‰#ñÌR£æØÓÔ†ñ¤c°”˜œê$DàÓOØ5<3 >Ä~JWe¤‡ç’çÑëMÑÞu€8mY>ò‚ÔuO&qL89ý‡&{ÂüÞKdfH"YöjÒ=ûdé¯ΤÕò/.mžK¿µ‚C&ÎDüw&ËGà·ØÍtݽ¶ û?k•Îj>”…Òü·L=Ej憔­´î– L½5žÁØòÈZwòRj ø}(þŠœK¦n€¤I*ï( œ†…àªåI\x[á|®.‰¨k挈\ffýež®tcíþªbÂà{ó³$þÍÐHvÁñÄ(›·øh¹ 9º/K|uÐaO ¹ªKxž:ašj>ÎÙ°š|TOÆ÷µ)¬ó‘]ÔŠ7wqñâø$Ê\æ(þÊ@énQrk Ä<(‚Ü›6´ÜÍ€}³büË5îÞ7“ÙËÉð‹Õô`ÀaòÌ~  ƒ×tÿ¯”lì»ÊÌ5ôdg½ŒÇäYç¹_®?†»íÊÄ}ñ,¥¶u Ú¦'!ß‘íä7˜<×ËÂé³FPç` ¾ÿ€òJô(¿;¯à§ëO0gö73· ÁÜ^Q²I]»gWÍÿê'á?W\¼F‡ô¾âé™oÁZ^‹Þ¢Ýw!—$—9¬ÍüÛpÌÿ(¬|q6±ÞðY_îVžEá8 òÉò=†3P>Ä×ÌÕQ§EQØ6¿#*îÀ÷ʽèöÚ”-X£D´ÆwbÖtKÜÑp½&ù¯a&>ŠÅ~¤*.g"ÿý.IÅãô ûwO6s ˆ±Ñ[€r"±WWÓãP:ØcŽÕÕ¼¤l‡8.mtFWz­øÀŒæí€š{É‚Œxº$m3YvV“ÞýéKï ÂZ;9¢jÆK?>n iN¹ð Z‡”\þ‰‘Òc8gúj~(\løèµÎÊsšù(¾´{R›¾'œá‘é´ön}63žËC“€óœ#k4ið=#zöQ9i[7€†‡ ØCƒ‡ÁÕ€e:f¤à¶—c°*æ»uá=Ø•> ž"·@qŸ2u‹¢Áw-Ù“ùð×~Õˆú_üw·iýá`xåø¬æ´‘±•—q±þ7è[º†îÕŽ£ÞgæÑk§—‘¿qˆé@óæôdòæa18 ÇO'?W°„Z¦ÑÖ‡þ×›¹~õÑf'¹Õ°L=‚~ …ÑØïCœ8dëÑÃtSÁ!tÒ{»,:þùº€‡Q¦ òì§©ù^C‹:€\e=OóHÈÍîäê Ïs‘îdÛÈæ¹I/u8z‹–\£ø0ËŠX–ä7Ÿ6üµ$žòŸ5—"$Î0d¤vYÐç†0Gæ9”ŸÅ"hž²5Ywqù„ÿ‡C¢p^„‡ì¼ùצU°UCÈä!]ºÈ'÷3ÍxØ©Ämň1¹î[Uèé%˜ï$OÄVûÃïÙ‡PÙˆ‹9/B¹aU5¸=þ s.f¤¿çjäÿè{à ¦u©‘âÞtüöµÿë=µu`®mê¿ÀT‡hÒï“4‰[Ìw¹ªOlÇ·Ñ«‡_âû¼8XtíÃÛT„/DLñŠJl›?†1z$í´1>’Ÿ~›Âaeœ&Y»@”¼ºuî>…ºêbSÔ#‡‚•i_0Õ—Ì‘x¥M’o‰ÑÁÒ%L¬À8\°s›à¿=ÿ#æ +?L%þÒ3‰lÝjýå/³:^ƒ`­ m’¶&ÏërÑU-ƒÞ¬ªâŠß¢ìw™¹áÁÍZÖ±X—ªþ ‰CÍô‹Ž‘X¹”˜Nó£åSùa™  ¹™fÎhÂyXÓx îñwÁO ^Ò?öNè›röÑÓŤI1âÖ’sÙgAU-:EÉ!+?½&IÒ%Qa²=Ô¯7ïú2 6¿áß}8Gô)^Ðâ#÷ƒÆPn5‹Esgr~¶cè ºkt#IZõÿõÝñ\½ïÿöLFFöe‘xë&d •J%í¥H";3É*+$%%+Åë\·TÒÒJ{¡RZôóþ=_Ÿ?^ó:÷¹oÏ×µž×9×}H|nKâÔìé7lm³À\»ÿõÿÒðbîüÛÇ®\*J³åtYIÿ:Ø»@›,js!Oš'¡ò·ïÜ]¶Æøâš"Dæä€aðx~Ÿ qnv¤Òy&Ùý»Ÿ*7Þ;{Žšƒf¶#üõt¢‹3˜Ïï°>G*Àfþ(óóð~ò”#þnßÀãZ7ΰ×LÏeUÖ•À’„ÃpGüN;’‡MÚ0¯ZÄ …Pöô~Èùý ²ßª•í3´±WE—ütmò4öóûh¨¨Oæßÿ—µÝbILMBÿ}3éµu:yàeµ4ó€Ìa'•ÁhA/|\'sSd‰¢jT¹à„þ‹Î’€e=ሬ'-1T=‘Y_šš`è˜:-:ÆÂÀ[˜Ò÷Ã}› ûx1k{~ßóf7^c8òÿHÄEVq˜ù±ÎX—CAÁ,x(<™”ؼªŽ­¿o‚ÄêßìòU¶‡N2ÍV3ÈßèZ&ý2/éƒÞ#ÒT»yyqÑîSl.?LÕ^åÁ”E:ãÛ«r#*UDØU¹&¬*ßSÜhYÌ4)3ظ\¬u¨…Ú@05? Í~'ÏÀˆ(}Ø@çý˜BOn%1‹¡SþÄ*þa8L5Y¨˜8¡ÿÜïŸq’k)FÑXbþ÷ùxì+a:‹ù:úÙ¼…d»-=è/:mËE-vç3º“ég=}rsÍ2:Vƒw*W'±kœ[øéÔòzX¡LšÉ^F_fcÒzpAœƒ#ižz=¾I³a]÷ +A3 W¥RÝ‘µÔªp#[<•=~볂íiĵC“ì° ›—Ï¥c#pðcîHšøÃIÖÖÛhzì%öÚ¯f|Ðÿ&íîƒõO/²Ã .0 \Á¾Þy˜#ObFÑk¢Î¤¨è1vKÑêâÝñáß ÏŸƒŽ÷·ÒmÌ…'ï`FÉyhw:LÇùSt»=m.á-[(H/$’±¸­y ÑÖ’%ÙÛ?BïãHêñ«_<ý2‚·HÇûx øm‚‘¢9té¿3˜ÝÒñ_í4>È~ÅÞ¾Šõ ØÆˆª¶¢Ã“"Ø×Bež ‡yn3»—Ós<'ˆd]i©| úåHI²ÿ|”Ew|‹"N2•ĤÇ÷5I(-)ºø€ªºÙµs2[9?‹<:º”zm‘§ë»·BºõdqZ„¬Ü†ö·½á¥d4yÛ‚Ü(kâ¸ÌtšˆÿÍMnW²¾‰ Å¼èª&_~š‘’Šo ã*N™†|Ý-þ?w ¾ÎMao¿+EŽa+3ƒ“#în4ÍÊ€<“jÀ? 3‰É.ýq¬ðè¬$õ®<‚" H~9ûÁû;¨Ï¨…7Ÿxh¨Z6“Õ9y5Ç ÉÃ?Ó…äd…Žy“p•.ªæ̇×\[k€µj÷1ÙÑžŒèuƒà—9 ðu/Ç0m!ä•…Ó§k)¯õt¾8ΡD›p¯çáŸvÐÏZ‹çU*àÒªY0U߇ŒòbÛ¢³œiƒ›iaƒ=xð< ëþ`Bž:Nèÿ¾Ãž ñêÓk˜Ë ™1§Oš/&d“Ýï6Êò;kÉT’W§ÇÖ ¤›…dí§Ýhÿd }ÑËÐ[*ÇèÒD¢-L®5¿BϬJ¶[£ÚCÚ°ö·Lõ`•™gØ-?CnŸ…—¸ï¯ì'·N ’?ϲ6‰sÛz®1N+ sÞINäs>ü1Ÿ‡¹´H”:»Of·ßO!:öúTrGná•$¡Vúäk„çÕ]zPÛŽ£aÃGCýù©g–9Gí׿à|ò‘5¥Y|÷6Æ|‡ÿúBŸ²|ONà‚:ìÖ͆•)¹úoÔ&Aê“ó™Ï“ÊpIk73´<‡lÛã@Å…#H=öAq÷Æ,—ï’†ñ¨À/xÚ+„gîN¦ñ"1ÌN \ˆ×¤ƲáÀÒ°~zž"÷œ5ˆáèBfÿexvVÞßp&¹„úüYÙ’%tçÖwׄf/ßECLÛàÂY²ny)ÞºÁ(¼eHþ +ºí^OÝu¶Ñõך0ƪ«âµ†µs2E§÷SßËûHÒN"fêÓV^Úþ):DfÓ “5lacw•Ân§ß x2€=¥ —¸Û‰™³uO! KB&ô?=ŠÚ´x0{¨ƒx° $áº\Æv‘¤Y=em“@KŸ³|®ôäÜSÀÌ_JCµ©4˜%ŒMçÖ“?3àãíg0:#Œû‚½?5é¡ËÓ :ö³¦~ΦÔž7‹6Þ} 曼è‚ìI¨÷é)¼¬ßľX|œª\5Mýh«$ç+÷4`On=ü »ŽáåÖHª°à^ýöœ3ݾ½¦ÆF,[CŒ'½Œ7 oéE/4c•ÊY²È#óüC,ü]pŒTåæZÓa¯ÃôÊÛHøi§]⇿[%ñ¾€ß„üïèº2†2Uóo£]öwLX»Ž®ê7¥²—œñ€{ ~ &¢ëÅp±j$ۤᡤ˜20—æ842iö pn-®£æa×¹Z‚ÑÖYäÞUÅ‘p/¬wbøS¦¡OX¡Ýˆ÷û!sÃ&®ÇKlI¿Æž79ÉÃ?>›©ßR Oq¦†M“©íųµ@´ý­ Ÿ² ¦å?‚éN!Èù©K{>£•Ç«Ê_Üþ{Ê\78O¦r#u°µ{?ÃMÉÁЏtë×׋4véî þžf:åÐa*†¾e¤ÝwNàmO…QŸ|ŒŒÃOËééý¹kϲó\˜ÝƯÀ¸x€9ñ=‹•+Šß4)òÊý5®‹ik¸q2µ^p]’JØ«è™ã[‘Œ?ìÔÏ%³/†]3VŬ ó­©´:ã>(¤—u=A(-/(Æc#ö“Ê@cãÒ«`ØÏî¿]S.â {Ø9‡‰ïóÐ<´½…OòÒ¹áÝ܃Va·µ8ÞžMÆ/…CÙó`fùgöˆëbÊÛ7… ûê`«r3¦+Kw3]Z]Í ©ºÐø@>Ô#—`Ÿ`* ©>†?…ÿÛÿrcÔ„Ýìô„™Õý¦h:‰ç‘ðoŠ){_ ûÈ`ÌZð¿cšgåû•w³é’g§!úâr”(p`^}`´Œ¿ƒæ›ð/W”4852|ys™Tñ`à{ˆ.Gªá ¦ëÏá‘Zkf9ˇϒd±P2‚ÑqÓŸ~(ÍE‹öiœkQªY#Ì„?¹ ¼¦4’j€[—>¶Ýú'Þm!øžYì¿=åµ1¤ïž<‡Æ/ø •‹Y°šRû¾[x7 ˜̟2Èø'´ÂQÎ_ÜT63Vg5œÛ6Ìø\;‰›7Ošà¿§× Bp}<#ãèÀN‰†M_o2ª³pÃNfŽz ½ÓõånHÓ½dyÇS€óî‰0¦_³ppï¢÷l“ﺄdALØÃÕ[Ó‰½@£‹D0¾r3sãýq¼¨fMD'AÕ®–Æ¿²«A°¡Ç¢;Àç¡3l-9 kÜ¯Õ à| ÿ­%«ý‹iüei™ÀÓø™»ÈeûÅe9+¶!­‹Â°æÈ,ª/ÊY£Ï Q XÛú«kÚlè€à‹ñ‰ÞjÐýs‹ù;*@¢óä ë /yqD§î2Ìö¾ËVGä€]}1HUNÇ=‘t•:óg¿ÆyŸµ zùi¼«3«,ÍG#—=—ä¸ð£9ž»¦+=ÅpQ,Â*“Y(rg ò’y"²D§ÆRº¶Ý‹‡ñwÜ jÕ™ ³€ï[k·i ñ d6¿"Q—WåòÍ܇¶‡äÉâ.mìÈÅÉ{ÛàÉ|aÜx<º_uƒ’¶VÜ·Ž^05¡Ö4ÆÞ»0˜*Χ´*}©EË86D×ápxÈ—Ç*Ôl‰²Ã8/~€¹Û…˜Ð—·©áGb6³õœM :a->×”#û:ºÁ©áw8µŽ¹ìÖ„ý§ÖøB1f†¹}"-G>±ÏñÀH+çKu ¨è 1Ö¾§ayi…Ýœ  ðõšC£¹W!@× äŸU¡ay$È}}É„e…`ìÅV{ÍôóQÙŽq>ûÈ“¤w9ÂÍIPsYŒ“sòÑá¡ßû /Úæ’½~˜1ã8Q-wolÆ‹›2ðEÜ1Æ{vûëéG0®†Ù>iP&ƶ¼Ó$"OŽp¾tþdFsÚñÕ‡E¸ÿ¢%«ÜÂþ¥ð=ëÛzì D„ó3Ní&Òsæ³÷SáT¾ ]RŒÉÑ;2”}Ã& ãi[VAâ²Ùä»^ ¬µ;,`É…È×ŸŠŽ=7Ѻk*µÞ¥K~:ë0_O'WÓƒê¶Sà𘇪v.ÆØzºìÎgæðî9°&有áų§‘[?cYÃÓò4ô…õU£_²&[ê õ€mëM@ßoY-N)†)tÅç-äERCÁ5X’´•´ÀÈá4Œ)÷§1mup«| ~vIõ{ï@öþWøù÷p³n®åþy·öúèàZ U³[Î|¼?È´‹rÙßO³ÐJkܦæÀ¬¸í óæã¤å„©d_î(Ç¥—ñœ¢Ö¤„¹+ê0zÙ âÝ…Ɇ̦)ƒ¸ûßÈ8u Žw|‚;a§ñ|A?8©É´úDXÈ/Îl¿_ƒþ ¼ÄHI‚,«aõ>~àö—œbnÍ23Åj¶óE8>Ɉ…«Œ;kR+ȼW9žÓ¡Gþ÷Qú›>þ×szæí æQž?–Ÿ‚ǧ˜3W-­…¸ôÙ|軇ߦè²O2Là_ÇÑõ¹8E‚Ÿ€:\¡•_ÒP¤×ÝåY žÑÿBŒ,-HCÉç’4¡=ƒqß®ŽC–b e¨‹óÝñŠÇø;p':Ô¼dFû„é°ü*ÈÝGÈ®(Aòö¥ñÚˆë.·£Ôº:¦3H/këÁíÅlÿJ:ÿ/q;dñÞNxzÅeX")Cú”ÂÆ oúgÅAb¥–DŸý<…r7Ÿav±*]w½‡é}Å–^v$*û„ ÈK7ؼÁ›DŒLW7ÆÖŠÀ)´¤oµ¿ÃFÇ«L÷%úªá!hú/ Úÿ¿f[Fõ!çÿðóš˜bÉ…ß,ï•äÌ&âQÿ­ÆÆc†=l‚žM]õ—Y³Ã" ×ÎíæŽÚÑþ'œ:ƒÙLñž©8ËþçšÌ0³poã¸î,ŸïG>¾¬eÖ¼cÑM˜ƒ…óâ©ÑüP|°iŒó«ÊÌ=Î.¬Ö!S_ÞB¥âúï¡}û¦ýYYŠbÊðܯgšo¢÷Ò#@#p6Í]zƒž`wœÉ†‡®®h\Ü œE0³NDNªéÄ"¸¯œDä]õ‰õÞtœ}bï–‰§òDwÍLöúz.¶HO³¨'¬¦øräÙm‹ÁÕÏÑ”ï&ZŠ_nÿDû2õ‰ø¯ªêÒ 5 ®p!Âù°Q…ÚÍÜ'Ïú„®fE–‹“÷ÖûPöp6x+©“‚)šäòá-°IAЬ·ÄE“ø [s½¥…ŸÂðÈÄö쓌«dê'ô2íiÆ$zz4÷×(9•†VÊCØç=‹h~3%Ý?Îa@]#†‡^µ’äÉOÇÇ2šø %¥°oÿ"Ð8¨4çí@ÞØ¾€4ßÅcn²±IâD|“­¬âƒû‘ã\2™[y=Ó– ZÂ×2IoÏ‘cϬh]/4\‡ §$‰)p0U‚~à„YcÏÿÇxðK–*x±oðbtM…û¨Ý®>QáôŽ~SYêFÏEkÃô;/pH>—ÛS`CG—`l€äŒ@ÂxÎ!‹x7ÎYõ”9kêÃǹ×2OïÎ%GÒ䉨ޣôKjY£L/ǧáód|1:¿}wQ™›h´0+w³~÷°¤jqrüÇš˜™Ò »"È>ë/°(ßv6±­ öÈè’Òå˜'ÌBLs,IZ˜Fµ,!·»Þƒk>‹Ÿx?¢m1?õ(€²Q²;¦ü&Ñ>‡‘³=…¹õ©+o(Mø¿1×he'ƒCÒ’$OÊ ¿2Rè2m7|›¤Cÿ=¯3©ÄAgR<òl7~aªkÈvz¹F‡è_¸Ž;Ï%àâ¦ÍŒÚçÌ+£ëÌ’g¤¤L Ž®{ï3M©çÌLbÁy\/q~¬þ‡'‚$¡öíTj|í×ô‡!_×Ç=Ÿ-NfÇdZÝRaøºýÞUcŸ+™Ù…zt‘Û$²ME•> †HÉETã–;ÕUŸ Dñ ñïE­£=¨7y1ýt²šQû˜\Ñãxvp7=P5—&¯C 1EfaßF$Óº`ìÜsÈ^Y‡¾¹ç&ô5aÆUüíiBßOË‚¢yzx—£ӗΆ֕Zp"+´öÐÕ«„鳡JòÎ/ž|] ¾kùàv]0¬”4æõY°?n>ú—¿R¦{û™‡ºE ²ªà#Ó}}Ñx\ç&„­m€òåÞdG:wïf¬¿‚åûÉý„X:ç%ó_/g•;Ñ”9±… “±ãÁ¹’l•} ¼6!7ÒÏbݼrê³#ú–{Pç Lxúá¼1ì…™ÛÕ(GÓ“®O±¥£õ!t{â~Üâ3d_1§±é ï¦!„6Y“(çð ü‰R®ï‚Ö›çáþ¿húy +=ÈU~I2ùÄa²?[ŠÄ_ÏlCR}ÀšœËâ£òÊ“èf£ÆsÚVCrõCÏà­YŸÁlH &éŸnÖXüú4î%qc_YÏù¼ôE†= _±&Ÿ{UM2 Ãgš¯ün›Òk+LhØö˜#$N$|aÖŸ€0ï³äQ«c&ñ)qÁòìm&™çá‚§2 ˆlž!êL[ŒÓ†g“¯)þ4¿+šæÎ®‚Ú€#ôîò«0·8§D ‘%•R¸+r?kø˜X3‘ÿVâ±[fÜÊùYŒ†þA8â]EöUqçäbSàà츾ͭàuF‘ZV¦Ð?¹¾´öM}4üÕ· ‘å–Ÿã|ÝØ›|E ëh òÚæ¤M+ 6,<ÇoJ2ʪ(÷c&P òʸžSwý0Ýú¸‰Õ5>2Û¦`ì52g×v&7i¹ïÚËýz¤†ÝZš|£{ÈÍ"Ix£×κº‘;†ñ`°ì3+x.üm´¦çgh:޲–@–)-"̧xsä¼;B·5á“ëòàsŘj¦ÊƒÉÌ \ÃwŽü¼W VÒ³È§á‰ø÷2ï £1Gˆf»Bån@Þ¢FÌÐkjÿ”ó²#YÀ¹¤¯NíÙ ²Jæ$9¨šõž.Lš¤Ì¹ݰó/?9ÈCè‚»hvø.SNZa‡h8ŒÓ²Äqƒfòþ¿nuìÚgÞк 8éq¼êN óps>غ»sÕHæ×¬ðѹ\µG˜r£oÈ·`Füµ#¦xê hV\D¹÷aܬ9·`ü/sûË_èTífïW=Ç€-ÿÀWC‚Íè†?8Æ&Rë“ÂLÖf~Ÿ?JßMïeoß»ÈlÉãXí ¿nY‘ßD–?³$û¯F‰B!|•z›»xæTjvQ’f³³Éøl^Ê8ôk3”qÐÀ¯ŸMìåŠN®ÂåÖ¸ôëi¼¬ûv†öë¾ÌíèUìÎsìíwÁ´’Ù‹ÿ«ÿó™~—™²ö]»èbn4_EÙr=R_ÎX¯!dá±2®BL4º8VBÙ…páýúF¾aÆ©iÒ¢k71°ü ««¶—Ùzê<>ÑæÁæ»ðà‡üW—lå¨LÇÆcÉR™¹h˜Ù©ƒpæj'\·%ëÖ4±KêÓæé $óÄéÍùÌ’Ò³dÓ¹S°Íñ9ø_¬!o×8R‹¦ÇçpÉД¾¸÷ŶŽ”9jªLÚ¿M¦±••(ö®ÙeA|B*3ëánø¸g¹}blËØu©Æôë¿~|ñ2‰ù¸~d LðßOý<„¼T¡ùã´rÓ¡ÅÄúÓBR±eû‡á€v c.J r¿ƒ{Fµ‰ŽÃI©µlt· *Þb^þÛ‰bDxÎ3¤© Jp°÷œ4H§)ÓQU(ŒöµM&ÏïAyù}¬ZÀ‹75É#CeRul%¹'¥ÏüW÷ì}½ìzל™ÊKüÖiQš«Ê0K_0ìñÕ$JH ±J-òà⮩W>#ún~‹­†4zæñÚQØ«åLÆÌŠÆmµwÍ8ˆâ—µð€¾n>žˆ7š&Ñíüí äAçðUߟÿlmBþ+÷1õ9K¡+é•ô~ˆ!>ndì^sìôb<^¶7›Í%ƒþ²¤rÌ–mœJÚ¼R…â6PÙ®¡36G‘töAvº.‹ÅœLEšç±2†kaJ×/ÆÿÒ5lOvgDKìˆÌc?ôÞ2ÈFߟEO*_ý8òÂ0‡ Þ‡Îø„á3yߺ—£`çjXÊ+ƒ6Z’O{R7[žé9ÁX;¿B¯ +ò"y¹açJª¯ç·ÊÙ„ ³Ñšp˜¹Ä´Ý„v¶K`Ȥ¿ø*ñ1Ü]y Ý}³9kj½ ËÆ‡¨ïš Ý©oðÁïÿ=ÿKÐG½'Évûg_â÷7q¿ØJ_Ï]ÉN·Dÿ³óÉS†Cî]HajÙ…5á´ïõ+vv‹ Ž™mÆõKùÉÕ½"°ªà5#ªõ/©§ƒýkxÌ#A[xzðÈÊÄ®î ¾89—QKR ™‹cì¢Çõ>1¼Šy|_üÒ"5 ˜Õ×γÒn3È•ï+q^0Úˆ¤âöE èíµvLî† <«ZާLÐ)Ðã›n£Ô7-àgN¶é¡œæ"¼l:4;âA¿Óà~õ>ê¶]¯üjÎ]*N]›£ðMNð}tçìÞ6ÁÿboÞÂ^[WFëøXk¯Žùß̉ØÝr˜s#ý3Íè•çIèÐ`W@ŸÚv¼À·^ŽÏö`ÈI•&ÏS¿q7ôüµy˜õÔÞMÊ>a»â΃áp9ÌèÔ¦·­¦“3¯Óiã˜qoÀ9cçñKÅÈ÷Q®V<9ìeHûÚÓ1¬käîà„º›Ð¥Ix»"½a®È4æR}5Gâ”x³ñt>/™¾Þ“^mˆ¥vÊSY¥mn4kw)¨x_e§œ9 ХޤzÞj*¿!¿ô µ©s_é•t´¤…}•èC:—^gŒ¦ß ³7—Ó‚õüÿ7?/&ÿÛÇêþL€ÿz*:ëƒùŠ覛 ¾Ædëóã°Åy›ÝGÞÛ “YͼV‚§ŽqnØ, ëÛ¦R©ÒTøò×¼–êÇIð§©§'gî^‘Î;1¶ãàchÝyæÉfÙÁ¥¸&Uv¾ùÂÆu&~üö¤«(š¼x:=m…# ЬaE·¯ ‡õëÔ‰×ܶ¿ƒígØÇ"|ð~Þ{ÌÑï嘳¸ ðð¬ þ§Ó¸¾ÿüÊ>¾“ÆL½Dw.ˆàXºf1že*äã$uŒË’£!+OÀÃE¨žþY3t üx‘AKYb©KèLo!¼U€¾Ma74î$…wn1a'0vv0.I]EGdŸ1ÊÝj´Z¯ú¿>dì–@¿e,ÓAž¼= ÇâßC"6—!GñÄpvŠä°¡×ž¤¥;þWÍZÏ-GÍsÛñäãéä¯îKü¯^úÂe$Eç •’d„oÙ¤”=QÿR°m;3{;¾2ß‘ü5ŒÖ«Ý¸Œió£UÐÔ%„] ²û<þõŠõ®s¥/D΂þ/ FÿW5Þ´f®˜EVøæ TÞrRÏÁ™ëOcú›'øcÍVÖ}¥58ÝŠ&K½_²³ÒŸÁ¹?sH÷vWØÝ§K~~´cÙža]Lø™n†°ã½¿Ë“kr¼Ç "‘»e*}`;W½€«¯Œè¯cFxäµ:+&3ãóaŸîb4.ø'­>ÃËBÑÆdJx¬ÿªÆÀÏCÓéÍ¥nh_"FZ¯«’‚MF0WUJËîÂê«øï>ú Y7q{€9°ªOÊÈuÓÂÉû {épWõÎaè_Ç4¨“MßµIÝ5Ÿb/Ü-DŸ9M$ÿ&¾]Ï@•†€\æ©™1ž;ŒÛj ¼ú´Œ.ør™æ6’VáÙŒ˜å%¼ÅcÎ2‹©…ÁIÞš‡»,€´÷<"¹oˆHX*>* §UÏæaÍõK–%¯ýwçD&y_q scUž=LjY%ø¢)”JìùÍñtecË„Áo¡/µëÀ5'‹¡æo1•Å¡'%V¢¨¾ºŠÛÑ U[â‚…x÷i>Üüo[¢9i1™æé_¢*sÃPy/)=nEí•‚¨Œ¡³{m ßÔø1w=£Ü…Ñ|ô[ùEä5ñ‚ï݈Ý1ò*z‘ÛBu K̉{æ°¿M”Ö;áŽär[¹OÂtvé^î͵‰ñrkòcžù!KèìÛä¿~Ïy/£QçÜn²²A¤›ëiì›Rj¼ºŠœö‚½g£Èé­öq˜½=ÎôOþÊiT#ÕÏ„iÆ[Ezuš<å }L4ÇDÉ—ƒóHVQ-mê¬#‚V?PÉ~=nS‡kxyiÃÛgLuE5¬îžLT²Ë&ü™ã,[Ù vÇoÜC…^¦ø¡mõ |ºx5¹—$L¯LyÆòGá|dOíŒËצœdMj|lû“ÿÚœdÈ`œ'ns_gföi`ϵAèÙFœ6žù4 ÚQYU"0|š-Hî ê1/÷ÄŸmÈõ/U8ærí¾?€ïÍpf^'nØû’ãÂl!NcÒÎðdŠÛiº˜,ÔÊëÀ”qÞi¸R“°½ûhzu(°!²¤\Úœ\MxL¶¾¦;ÞǾ{õhý˃<ÞE4^UÃkmX: ?,!›»³&âÿ«Ï ð²dý9´ŒðZ¡“ý{ðˆë`…/?`»2¨÷,ǰæùôèù-µtíBÈ€çn&més°óÑ¡òkÚ™¤@ÝÏ)’þÝòÔx_ ³ñ+æ®(VGÔ,Üãg»8êãïYZd¡T"»*å"ÛalNÇý®ôTr4-škÊ0‘ðõ^ ^xù³?‡õÆyð[9}ãZ‘Ñ:ÅŠþÉg8› ÷—1³ï4[riŽ–´ãßb0¸rª#2î!ØÂÎuç¥WœvÒº•ÿ¸ò±˜¸½š+êÛüÖž6|ù_þ¡M·{)„Ò~;i~²Ž/úôm¡Ý!³A{„Ô´«Ó´óYv¯nž—’HeãBMÏMmÚØ¯OëcÊØ`…yô± ‡Z;t€ÞÁØ#IyÈÐÌ£:tô 5‰WH¦  êiÉýÍ–á"3;z~a}6Ѭ†0\¬?‰¹Æ’Nªo—Éæ¤eåÊ„ŒsÕ"¬Í]„‡†€»E4еż(ºiwÊýa¥7=÷½OÀã»2JÙvrñÖùËwÖüx7Æî݈y¯ÿrm•¿aÜ6è™t\cxYå“‘·á Wn*?8ŽPÀ‡l=ÁÅCˆÞß ÓO5â_‰ÃDyŽ<û¶ó3ÊÈ>ǣǙùÞp¸ê:³ Kœ#K°S—‡ZÝaæ>"{J¤)ÝWŠ¥¼‰ü†gÌÖ¥–tûånl\œ‹<9Ï9JŸ X¥ÙD°Nˆj/àAû£’ÿõ³F¾D ZT[Çù”ò¿ý/{µí˜»ëžÛ‰¸ÃÒGŽx¿ÿ2šTªSeCøÑkölŽàú•:P=Ͻtü’Jïát„¢Xx9Ù€&i5ãŒÐƒ¨¿Ì¯ÖÀ#1äÁÃ\p½z7k JWœv`Ä®×ì'{ŠËõàý§Dܪ¡IÔv®„lÍ ¬°iÃ:Ö‰ TáVE2<å7ŒšYà6 uV‘™Ú™u§Ÿ£Äª'8ÃÈøVo¡÷§Š#OINÎq ƒ¿¹Eb0°æ8™9ù.H£ ³8¨YíÆšñ$’#¼»n%‰ üÍÈ)ycù ù«­F·ýx±©†¹6â€ù®_˜’;Ò¿âˆTÆâË»²ÄüÊsŽYæËF¥gNÈwv=ù¼HHÑ™ÉDo¥.õï\Ŭ/›M—ØàùûftZ‘&duÞ‡ïo9Xù!Å‹'Ñ>ƒg5žH³ ¿ÃˆZ–ˆi­WΛé„ÌZWÓ§“¸T´]Fª{diúÅBò¡*s÷ƒçJµ8`®þ7\7lVå§sÆý»Ód—Á%‡V#ÿ›¥Ì݃+°ð÷SXü´™5¾ƒy’ÿƒnL¥,‹·¢RÑqƒ%îÜc×>NØ?´3iñ7Q88`\ÑrékøöÙm½ b4äºíðƒÂLsS6‚U¾¢lrã4¥FtUncÑÿ­[¢X‘ý„“Gã¾n¡©©m vm*þ[‚|»¯@ls™šigì´ücOÙSñ›h·î6s"à0îãQJ$ o*þG üøAà 8_à"Ò‡óé§õ835¶V\€y¡CìÝhU²·ÊºmKöðœÇáÕJägý ˆÆ{1¸¢,Æ3á±Íð÷ âu¶Øúø1:ò‹Szõ0}n@èÓì ü)ßD°ÜN†¾Þy˜»iáxv³˜Ùõù2.6y ¥Òè¶UÎ|®D¥ÝÔ9­}Žôƒ”$ñÞÒŽ·Z“È£W:-Qƒó{gJ,à,M—¶ŸƒJ èÂeôµý¾c)-ôN ÷Sps`Ù§!CG†^¿ž¶nGH–ûWuG (ö.¦í[ ¹®/ý5`„WR 7R/Õ«R+Ù:Bh‹ƒ«Üæâ‚6Q¼&êL׬¤%™ ¤ÏW”.Nõ¤?u$¡Üm¼ñÙ§¡´Ò|ŽWÝ1µ<}ù3¿ZÑ>ÕpôöÁ û_y' šƒ¨Ñ Òÿ¯~n'mZpÊ%ñB¿œ»¡Ç3¶£î·ûo;ºÈWˆF —Ò)RJ„ål:xå$ˆšï%EWKY“éÔS2~Ý$ñiâxoƒóV|*gRHó.Ê»µ‘Ü‹V#{ “¹Üx qò1¸þ{ Í[±Ø/JÞ]c7¿ôÀ¯‹™ŒC#¨®¿|ôW‡é¢wèŽq_"1*C]uv¢_Àe¿ŠJ`÷žìÅ÷ðeè kO‡’I© r~= }®‚_…ò£4=èzÎKÌææ‘V°'ËÏ Nà—?û‚ÕÙ ýnôtB)”–4#ÇЕc­MjöP…ÅÀ¢W [$ ræË)|ª£H¢NØ3cs¿àÔ_ÝŒ}à ²¯[ŠéçUÝhqƒ]åOL烅蒮H–¾¬avvN%_¯hÀ¾à8Ïû©Qj¥!¹²_˜èØâþc±Qiá}'‹ f©P~Z…´îZN¶ˆøÂCõCx‰\º qÙAçÆn‡¢s Á³Ê›¬æMC½-ðÀR‹Ì*f.l6‚C5&l}^·"‚¥î³¬Öç}ä¨ ÓÞ#AL–fäDþã¤îGÆó†—ÛòikäLÆýÔ©ÓèµC×»{Ñ1ß9VÖá™O ÈßÁÀ·ëIÕZ_¯×#ÿ±Ûçó‘Mî‹~_&…OL L¶oÔœ‡³ƒ“I ;ŽçÝCî]k-èÓ¥ÿü½é›-¨ök'•â΢çÏÂᬯσ¾Ák°·z?Ç»æ70ßw7dlæÇ¾ïà”‹'|^a>Áÿ>º'c¬¶ßoe½m„AŸýuŠáë~'ÈR’a¸~KèŽÆ Ü«Á!GóVÃäEß0G^ŒˆªÁm»tfÏœxé 2îóÜèö ÛU¸pÇßÔ*·ÑßYdáñdœÆëK,‹š¸×:øÈ¥.yRwŸæsyìo:êÑÚxœ,ÊK£¯‚‘?!æ“/Ì=®F"fS ~"õÀ¹ÙØ“‰Çl•µŽÄäöâ:Á>y2Þ·€Æ)²ý¯å¦1Çr:€=e…-‚œŠIh5éTîbß.ʃõÝ=œW®2ð*3zHö9æSícØôg½«N;.׃\ëSX¹¾‰£#5γ¶?Á®áØúþ5Lž…ñÇž3êÓuidƒ )»˜Ã¼ø±oÀY8=iwΡõûPµà0YR ŠAt÷¢ÕäW1a êÑÂs2õy{“œ×ãÎ{ð¦ûvü} {GÞs½ýÑÖºû"S«CÙâóñW1eß§Œsîòù¬òÏ3줢n8;ÎÂÕãááç™DêÂná³…ŽêS8õB&*ä£zi·ñ†±±5Þo…ðÅï2«è«R§@¼æƒ%A-=‹%ÿ&üv¯6“³ØŽ?A»û/¦à¡2azµ€`¼säìѧgêðèÙhÓìËùlÿÖá» I Þè´£Mf\@×÷Xóå'»ü¨5ºg¤â)¹?¨¶Iwß1¥Ó¼–áâ€{è|ÔŒXxÎ&fgD駦 ¹R†ThÛÁž‚ÇCK½›?Ù%³HŽó¿KìNÞ58UL™$kxsº•¦CCîtùÎèn]mûæÉ+èù¤ÉÞUÚ Ñý,söý4¶ìørÒ7*À–ÉNð‡³°7ÓÖ_Ù ¦* þLn?±¹“ <£û øwüÄý?+ï=sÍ“};áù§üzoKD—$]¬Ü%GF-ú;úðâ4¬2É£_×'`¶ö<û6×®Ô%Oո쇩tuI6ËŒö³Ø²ò5žŒxˆª…Ãì‘s ¤H ‹e”ñ]É.ªÛõ]‚þ^:ô CÜö“MÍÇ!Ð26^SÂÄð«¦ZM#áÝ@+Ü‘yŽ¿4ŽÃ®ˆ`:bÔƒóŸáñhBÖœ@ã+±äЗãðäc›ã/‡cbg»¯0®Êñ›èÞ¯X}§W'>¯+9WïD°å«Î`íÑÿ½ÿ`è Äm>×ø4R…\~5 e>Öâ­÷«ÁAzóc(Í‚Œ 1ý äÍÀ¤ý2x³8…å8ƒyV®H´½£79/F¶£B™½âÃíoEéÀ¶ ªàÆŸ]0wæ.äŸþ8õ vE.æ>$qæùÿþG˜¢[Ói€ ´¹òÿz@÷)¨@á[‚[w- Á¾ŽT¬æ;ט^øeºÊ“Qd« hú5²J O@Ó6 ÷ÂòiEض‚—º?TÀ_˜%—”á^D.üѾÇ`¹:\b\“s Úh=J.ÂØûÒ ýßx6yoú–nb®kÒÞÌ”Í^ˆ¯=BõÚÝŒÁŸãª½°ÑzJ1¼I)ÅYžsB‡ÅÙÕìMÄÅ)Ôf•ý!ë@ ž¿Ã€Ç*äXû6x—"½ûajöN"O·¿„*ð%Z€L­‘ů)¸5o¹úÓâqG?)Ó·¤¯yÉæà`zu)BâÝrwd`ê´ xŠe¦l¿Î­’ä¯r2>5ÍCéݵøû¬×c½tYÜÑle<p”-ºÁ¼ZÖ„Ÿ5Ïaö+zðÝcFÖì.š?ù†nÛê0ôX¨—`¤í– þ/"µãß;0±†5þtʉTæß©L\‘ÊCù§ÈÓœÁ:V#·¿]°£i/7ƒýéjTRÅy^¨¨ÔÃ8†,…sWîÂØ63ÒÙ‚¦_&Éaò²®Ób¦ÒëR"=t¾Ná¢ØÚ#04­b€f¬8&V‰I~é첃Ÿ™âè+lý¼^Cg-ÿé¶´k¥$fÎw¤· EphZš´œ„#“«ÙÀ«ò»p!²üp‘ÿºh,Àb£°¥'¯tgþt3z¸}„• :_~±O\;ðü×;8>Ãf˜ˆÿW]Ð)Èz¾3l™ãQÜ .JÎÿÂi›W È°(õÏBEUb«eÈ,`g@óN'ȼ¾ f÷%‹ ‘ݧN4-è—×øyéVPlÝ ‡"Ĉòo°Ý{Œé‘%[bÄiì‹PFtç¼ø˜ƒêû’˜C=W±ù옲k+v}>Œ/ŒMp}›9ñÓÞCëí5ñ‘ð5¬˜÷Jw#3÷æ»5 ˜Ä¤ð‚Éhð' tÿ¸àeÙ ŽC q3fß«cÜj‡ð? Ó˜|xÑ­D7n” ÎÉJôËá´5•ÄE抴D,¢dÃ'øŸ]é"6ˆGž=[Gî¼.ÅÛbLÜgˆÊeÊ$wƒ6X=ˆ¹{˜JQŠ©ýš4R+útª™gù¹µR¸u®(9_ ~õðÌE&Ç-š52°¦ÕÖ¼¬PÙ3Δçzì4ÙBàË­›QÈÛƒcc¾ƒ6ÜÉŽ_º3ÓªMA9s1òýµÅO ˆyœ"¹(¼ï=RähKo„Ó·Õ‰È_ ÏÔe0¶'’œ5¨ã.‘•ǸËNð*8ž§‰b ïg\ú·æ9Ý·KÜCÎH©œ)fFS‰ÐÆøXÜ«‡V‰BÕíá û÷í\Ë„¡“v@¾tþd׸ـ˜ÔI|²'‘í:Ž1*žô<—ñ;&A_W Wä0O¸Gè†>5rc¤¬\ ø¶ µRåe½•îãÛÓMaaÍÑ×ÿ ¶Þ¾xòØ%vÞ:KXƒÚM.beû Ùr+6Bøž;YŒw“d`ïÇ@ ÛÓŠ£?/³—Nf“ã–4cŠ£ÐË•xÑÉTÌïgBcØ·æƒ}E,ËÙùxÄȃCÿ8ήÚd$p2 ‹P'6¶î¤öý<ªªIœíHy³*3è1øŽ?†p»ã¨>Ç’ú}Ö'‰ò'ždžU€ÅóìòùØ<¼°!øú¥èüÛ‚ OÑ 4-W GÕÓ«_⽺/ì4»$óì/ø; ïŸÄ°ýSw’r«óbOV3k3 ÎI‘GÒWïAgûÙ¡‚·èÁˇ¢“}ëŒ÷þÃ'ýÖä’‡‰äm‚™ }¨«Ðnø\;hÛ˜Ì|U¡ðPSv¬+Fµw<6+¸úD<ïÞõ*äØ|Àº @0ßËZRc£Ôniå\ž;‹~ŠQÆeC93%Yë49Ò[¼Œóæ'ç7q˜½—.Áûÿñ¿\Cã¦!XV€à1ŽƒÞvN³â+øÎŽ)M†MwÞ£~fÂs77¨Æic¹¥ :ÞèE·Š(¦•"U z/¾"NJÀu~¬ÿŽÛXhâ…æZ0sJ/¨‡«ÓHOAz«2O-×¥ss™±a(»*†Ö41ëôAºv¼X¥iòP0eïÿï%ݵpˆûF¤ÍAÙåŸìÞ‡àk²Y‘|Œj€…ެ©6„’Ù%ÜæÕ•(QØÊÙš³óN Ó¯Ñiè!]ÕX¯4ybÍ V—xWðS)µI×{nYý°û?üºÙŒ^€nX»ß•Šâ½ ×ð™©,½²ãÞyÉÙ>û²RM öâ¾…ÉðàÉ—µ¦ÇÅÆðÎÝsØûõ Ãzðáô°apâ9…¾±Œ|°±?‹³Ûª0t™Í;º‘³fì ´ ì!ÖËk°þåo<>‹w¶/Ç_>jî"Jø .ü×ÓªœnرA*ôìöµ¸3¸s…Âßå~ä¿ÚåU·ÅI᜴7M‘V’ Ó@Høº%:àí*AzÇH‰ŒeIÒ›o”IJâLL ‡ 7vãZÝa¦tù¸ÝoK›Ró û_,ØÅ^Öan\x`t´ usÈ’éǨÂÍ&0]§H—Ø+ᙈU4"ú”j@Ü(ãžÝß|3xyÌŽ<Ø }ÕX8%’x‰¼d½›wà—«iuÓ])±Nºä—[«³ö*ìÑyÓ…øÉå¥7™™…L€Í4¼ö0›_ïa=¹ËþµÑ¥Ô1—¾¿&‚íqQtUV$;eÞ+3 ìë羃ù´\•¬®Á;.Ó¸oô˜Í'ÓÁ¥ˆÅ¥©øO= o÷&rž·î§ÉÆG¼E©HßdløèüÔ9¤jEo˜D·ëÅLÄ¡œ›¬›WoÏäÎ\Q„Dï!ó¤û†]õEEÚc$ż2K㬟šÉ¨Õ-ǯÚ7ÐcOÓá(³ñ+0w û‘Š’ò Í™&†q"|D5ù´Ö¹`æýµ™A¸"G­¸˜aP‡TN‚¹ÂzÀ<ï(úÀÜ>²½îÊÑc<ºP¡<¾D‘if;ñ¿¾Ð÷Vnc´ld+•ñOHÛîãZ™‰²G2|i“-ƒ;ß׳a¾¾Ü™‚Ù9™:äà]aºlê.˜ÝYÈüãÅíﺠE: ®d¿|müýµÇöEÁ…¬^VÝVr‚ÿæÍÆÌ;ÜøC‹4ä»yô[ìY\V:sƒ÷A©/³í€ô¯¼,sÏZùÕ‰Å^EÒÇ„)v7`ÁÓÃŒ=3…R•w°]| täbzP5¤Ç¤°‹¢dIXl!³ìI0ã<4…¬XÂG³t"¸’\Q•±A®«I")…Xet¿/ÑæNÙ<ŸÒQ Ø‘)ʨN)Á;V'°ÉA”V,ŸûÇ(3ׇz=È¡Âv¶fÁVLòö¦ÉR³¹óß|„憤û¨fH]kaÚÃÍìiDý¡=uñX‹™büÔa„à ð ýg.+á¾G6hyÈ tÊç?Yâô³˜4Û¦&gÙ³¶SiX¢íº0™nV Ç]ÛPrÝ"LWAçi‹`ÿ»ðÈ6z×ê"ʦ[BJÎTrÊ@Œ,Õ½À,”Ê¡N_£¨¦ÄNœö^Ÿh Ò·õ°»ø [õáÎõœú±Ï™~ i[MIë¼eYMã5Êáä >Úæ`K<ûoõÂB˜¹:Í‹¬'N\jsÈ„FŠÿ@ÅÉZd8z9ýU;‡Î„[“y=w8ê´˜¬ì7#¦épÝ[˜p=%‰·#|Ô»ÎÔTzÓƒ{þ÷þ¯cd“Îær7 äÉ]@ùÓŸ¸Ë8’ÄùG0 lBáÕð«k.LQäžÝDžI¨Ó?'ÑX›«8xQö> ä*í¹_…ßr»(¡äÕ`Pëéļ2´zË1²rýMöë¢ÙXTËÎ-ÊÏ¥^pgÍ<… Ëq¯Ä|âEå(Â0[ùö,è‡H3.(nb4ê•q_¯+ðvÉ¡Ëý°À9!q¸âû4“sĶöcððR'ú–žÀ+.²š]ÁÝ« ¦OG_§9ëˆ}kØ­úôêd÷/Ñ|ñ}ÂþK„œ™ç‰^Lê‚ýXþHœ÷ŠR‹~7R¹%´žÍ¦¥;*Ð r -–W`ä™×à>]œz¬K¥¬¨nÔ'8~°gý6ÇÉÖG° hÜ~3gÓë›k¹êJb°¯§M¢g7³é¶7àðó,8ë¾… öaÓ3H—&?yAè ¹š¨À­vó¦ÖíGpÄ»Œ³ÜÄ ³ýï±~!¿ÙuQáPÅJ¶ŒÂÎÜ­pY¡ „‡k¸?oY“ÒÚxéöqº¥'œ*Z·Óœk/Ø]гHxã3Iª:I‡özi½æy´#h:ýLu'ô‡UþŠ5"7}е—é,p¦[ì€vn6åž{Aú6Ý‚ˆ77á* œýð•ÎHKgoÍÚJƒ™;ØWõ ÚµÜqæºtºñªEB£ì\RÃ' ‚ÿ4aáºÛ|9òM¾†™Á| œOfo’Às—óIȇzÐÕY‰w?ç‘gÚÁìÐFK+•ªD¬!þï h´æ&°ŠtÅ,"FyQvç-ò²|;ÙÕ9—&~» ¯½èžù¨HÚØzm]õR‹¦óü³¾Ó /VÀ÷Λ¸ãÆJê¿\Œ*mÆÒàrèÜ|û6âÜ®ò˜¿U¸•9w ï̪ÆK«c`Á‰—`¼ Ç:½hȾå4QÞ &«õÂpœ,­ðªg ëïàÜÅ6t‘áÒ¹Z,§*‘”ç¯Ð/Ì”\[`Iù·E3ßî$CßJú95†L•>ÿ†jÁÒš%NÉZZ ŽïÁÓsyø"ž˜ ÎÄ«­Éw>×ô¾¾W‚¹Ã lÖÑ=Ä}ê4ìxeJ«äoAi@Û}3È¡°kàV€½·òH㶃à3xà. ˜­:éTu"·„>aÐůÔ/ì $zÓ6¥$ºÕi?Y"ŸDÕ¾´bs@gG¸éÿðo-–b–‰3ì³¼"|÷¹~ég•á<ö}p6ã±z¬+Q$carXXß‘­Ž(»!˜ó»Ç÷ñP2”™²‹Éjánùélêp1\ø~ÂbÖži³ÆÏµ3çïb&_ô ¡ã>sÒË7®›¢˜õöÛˆáœ#׃^.ͨý—4b÷G~ƒr_Bet­È¯|}Ziøªæ‚€¹:=0ª Û™‡éz¨%ÈBƒº)ܽ—ˆ­õh{¬—x.¡–Õx4§·ùÖ æ)èXªJÄN‡°á§sáZK;.gsÑ£ôû쥯à3OfÞnÌ1SÆK¥Amâ§ó.ôV£ ½Ä»§-¸€^S h÷‘¤þc1tðó‚OzغºB°ôV¸¶d³çØ|ô²8ÃÞ“Ïìï= R×ÿ1b¥ºwÎýÅó×ßrT’¨Ø»jÈU«g­â²œÇÌeÜ?&MF¢nBª´<»÷ M0TñxLÅþ²ÌYÛ,æ›Z 1ìµØ_›6€X‹†yô³A—aÒïãhøý7¬$5¨fS nîÓ˜]g7üñâÊ 0—b›Ax¦:lP&9‚Ð/é Ø',È‹km8lâàôã~P0L û5si‹l#»=tQ˜ÕPŽ/ŒjiÖîk¸àÑ;ìQ}ÁÎi؉±Rô²üL²ø¸%YÌ›òßaÞ/ErNõ&›öЕâù‹ìßsshrd!„;]A]©µ°k×Qô¨™AvŽ$£â€=qÿùtnÇÁ¯UjäfbÔs1òê'/éø;ˆó5QânÛY.‡éËÉÞד0åüÓñ9¤±sf>³wy$Jox»Ã‹aNL%ü49ßhæ|’ näÐ]_} OÜifW‡-uh£‡D¾£ÌŸæó?ze纉ø÷( „Q® ™çPoÛ]˜³~*µ3®FÞ×­ìÎ¥ŸØ>GY²IÊc<ŸO¯œl¼úò'Ìy¢Kò(»è©6èÆPà?ÿŽ3óÁ*öÞ1]rgž8øly JžËIM~6ÖÈh€NF= Ý…)e ìÙjr@Œ|‹¶„eöƒlzjê/ð…u©·±¯Uš\²ð ïÚÅh¹þC®D(I£æ}`<ŒFàøòAé .u'ÎÛ3X¿õ…œ&#’¢'Aš ðY´)Ø[Ì UÞ<ðóWˆ]¹åm<λšù¯¶ûÝ{><ÿÎk0%»ÝgLà1°Ä ŸŸgKÃð¿þËÛOz‘ð¬éºIã|PœV:-äf€£¬ù.ÚƒÉÛåµpf÷áGíZÿÅIV:ŽÁ«6ƒ¤Oîçÿ¢p}]7D«…‘|MT(É”HF“ÿ`6ŸÙwé5ˆOñ&¡\¼óÞWñÈ£ˆÐdª9hB ,HyZ³uE+Ó—òó_©ƒñýaÜ4¦ÊìºÄ,‰ØCøLVbbY'ÎÓ™MÏVÓçã±yM­šœÑ¤rÂ$³¤˜4œX‡2-¥8ÅAô®9† Ç3 /C“›…õ•öŸ­ùôT²m©øÁ…V…ÇS±Ôeôd¶︇&² ä{Oãµ3f䉖ý¯ŽyÙ%A¶ÕÚŠ12aúµÈOÑaÌ»*D§0Ó 3í óq~&˜Þ/ÆÅÒúLÐÊ]h£:…Hû_f‹ÆlH›Ù2’¤r滑ÐÍ6O ^ܘîéÁª'ý[s]+iAŽä‰à¶]¬ÌÙ\ÛŸ‘¤yÜïtÓ…`hšÁû/Âý÷ ð´l ¹?ܨz´ü­"C+ÇsÏdF‰×§]ØÈ\ÝÇI}mKŽÅ¨‘óéàà ¿Ï™ûÚ³0kœëweèï}1@È Ì}›NO¹ >ò'h&½R<…^û{€ä´\†¢aqzO" ?ÏŸûOAb03[1cÁz@d/£=kòmm‡'‹´hhÃ_ÖAè“Í[Å%ÍÑøz”ùQ3Å•´íÊAÚ‘7Lo›¯!ÏÕ½éîÍYô`ÅWõ“É•´'’¥VÌ;é´~N%½ÿû~~Žï/kCæ»P=^ôÑÐÖ‰ƒt_=äNµ¥Æ™KTÙ‘Ô…ëÓ·kY¼/#B­óžÃð.QºÐ%™ò¥…½×¥ÌÐÒÿÝÿãU8Í.7cxݲ! ²àåªhò¹á5#ûIøóÄàÏ IôáûMÙ:¬"ùr™Ø7É‘ÙâуGß½ƒËw¿sãâ“¡l¦é± ­6½‰½ýæ¬s˜,DzÝg¸éÅø4ó(~8Íõ·ÐR²´¦üÁœ¾zîÈ×D¼Û¿¿Žóéãco¶K‡Ýóȯ S Y³ 61×°çè"fKe2Üs=ÉLK"?S›ü÷]¯æ ;ÁŽiÑßÖçÙ´r ìýjH;%^0öSÑ÷–<ûž ñ¼èã·•íü†tCC&›Ä2'îÿ~6Ìd£ aË“!v¶ÖRà.bwpÈE{¼4žÿØj¾c7ÿãÂY)=±©bßvÝàdß/aÍ.‰Ð›[íÑÝö0]ƒO¡ì\ Õ½»’”öQ4z¶y|üLXÔ.L~Žý„?÷¸¯gAîs=²ë{H éS.D¯uÌù¯í ö]ŠÍ1Æ`O"u`Yqú)ð…*ÒŽ+ÕÐh}§¶g¢¿œ0Íy¢Nºê8´Fj9éˆûÄ 9ʬQA«YÖ`¹DŒ®¸n ZøßÊEÀMn¿"]û¹œQ'Ý%¯ ?÷»ÝéÜè ùß‘Óã4T•sûâ¥HM·4=µ4 ƒÜQ?SŠ&ƒ¤I¦Dw[6œ[*GÝÿ qŸ~¹Â]+˜L‹ë°Û½ƒñ¯¤j£¿ACé:Ì™Nº¶‰Ùq£²å\ûæÁ>ÿ<1ç`(Ñšš/Ñ@Û-9lÊÌ‘J" N*ËéÚ¸Ù¤ƒ_…Iö3ù¾“06ª æìЦ¶ »ýðÀèB4 å²jqδCÛ Ž((AiáOÜ6Ý2&bTžÞ¼¥‡C½Òä`—2ýÓ!L¤yþ±ÜSühÿÍ¥ÎÍÔ>ΦêNn›ùTYÝ6Áÿ×KÁþ–$ÿz”Ǧ“X;^bt_…î-0³½²¿ßJŒ’ºñ}ð.bý@dV×Ò!iÇÈ–ýCó„R!}ˆˆ_çÿ¹ƒœq\AU Älm¨Ê³•ÐÉ—éó|hlD>±«ßI3ðÒeyøgÆ]œ|ö º…¹1‰£·èG$Ãò/yµ2€ (Óž°£à!Lâê2a¶S0XaëgÁà/`¼.8’-täa1‘´Œ¡æ· íPþ÷#,·Crñ{æ¾q>iHkÄ‘d›RyYŸ¾ô$¢· Qö£ï„ÿËQ”!/ í˜e‹¼pHIƒôtþâ†9ÉÓ«s·ÓýÌp³Ü+Nv|»dj Ýÿ[…õVºÀ~rÐcÇ:¢ÈN5Òòw)U½›Èy%—±kT¤Ôç<)·sIzX>œÄeÌF­„Pv²j³"éË™~¿MJ ¿dwrþrŸfM¥z…äHüJbôV›êš ãÓ5÷iÿ”ϰSјÚÇÅÁ÷þôÇ%á¦Û]j½[Û/aŸ¹â#Ítk$yÑwõa¿À²ýpÓRÓ—°g`õ|WËD©¦Òÿê¥YcÄÿjª½MNPÝ^wîÖ¹-ò¿±ò;wàôa,« ù}É ,6•ìàBó¬Ã´mõ FÜò %:s$Ý{ŒÓšÖ¡ÔÍøzú-´{v X&žw„°[ÉÐ#‚šÿƒ¸†a\l5 ?]EÉï<±ñ|p6UËâ$Um$GÔÛ˜ƒæG!!Ó…¼uŠ>ÜÂI.œš²`¢ÔÄL_ Þÿêpï7ašÔc3Z ¨d\´Ã˜ÕLjê6)EO¹lú5”¹É´Ÿ¹»žŸ‡×‡Fñ}N¼î¬Eµðˆó÷â“cÆÔVÑ•Þz+G}9Q? %*#1þ?,˜ˆÿµOÀßâ[ìíÒÓPCšá¾|ŽÏË*‘íK‚¼^(¥žß Ôº|l%Λ4˜AÂK7EÍ%c4”ž ‚ÂÁìSV¦g^ P VÀ­–Ît§„ õ¡“qYçBóð!<É×Ç!_˜/"a×ÛHZ}aqÛð £JÙÿæûí´žýKÇ{Bqà±µ­qñ…ä°x&»è~;ã_Må#EÇ›¡iÑàçýŽüÂ÷q²å ÎÈ\_”šˆ‹>n'û_Ägm¬´¾Ø^JÖdžBîÖ·P}é þnV£.yˈªìŽ ÿÿ= ºš™¯ÞŽäÊÎ'%dÀ‘›2·â0¢ ƶŸ&ÑD„¾üR6}(¾¬/ñÐO×À¢£KéØýcÄ»6ãçgr½tˆw”5]œ¡GÿΡ÷V@öãqÛˆ7"‰jèS>º¹q<~IÎ¥³SÈè•©¤wÛi8¶Îòãéƒoê܂ݾäçô}¸ Þ„šÏ‡Q?Sú9dœ~ÈÏZM.ä6:…«9dw¨/.:)‡Ç.ñÚ|²&’—_æ'ÁªDåÐnò®à.Ό֦á;£À·æ þ‰ÜŠoä&äÿgl ãšPÀîöøÄ¤)ƒ€¨&×4ž¬‡ßê@eq;ŠiàŠH6ÂÇŽQ¬¬d‚¦BðµÔ÷»5jô^e÷‹FC€t>vp£Ï?1†<-ÉoEîFÁ%ãÃl¶á|(¯SÆàÑ(¼N«ð©ÌiBƒ±«Ú Í^ÅES ¸í1têPKãÍÄ.l*ÊFsÈZ^†ïÝ¡©H‰qy?b~H‘ËúÊÈ(kâz¿`ø|+–5Êõ¹Ë„þ€n{†xÿdÃøóØØ¶Y$d¦s0èz²Ôä[¡­ÂŠàQqE¾ÏÛmÿÿn÷“0s·q?êþ‘^Ô7i•^Ÿ£…9#ŒuØcÌ|„K¾¿†¶““èõ î«"$ðëCf »7Œ%g2ïs‚+‚péS¾í@|âÞ‚±ms…ÿ 꽡¨)dOš¬ ðeÐ`ÞǧݙYc-Q%ªQÇOF½@}0ÆÎÞ6Šò‘‡ºÕ 10ÙO]6¦1¶ôÔÑ—ài 6ê=GªOt¼[Àeø-f®‘'Ã’¸³g=±Ýt’Ýí®Â®èÆg™Ë¹ëÚÒ©Jeû4ÿ.dŸX(³ésPæKêÄóŸò`îÑøFS$JÔyȾÌ:½ÄarjØÇ1-°wðô…úu5C¢ÒiE‹^}‡Ê2SòmÒKܨÞÂrB—¢v¬æéC¿žÈß•DFã©gÙ<ÖÇñ‰ž³’ åÏ¥Éê\xö¡’íªYƒþ£¸Âe9u|׋Ë#[1OW€~5|Í‘{;™tu”Ó1¶’*HÙ9uúÃd”³|ÕYªúö Ñ“B÷œ¼†~YÕäí’> ú¿ñÒvÖô=cŸí[ Ë¿ƒhK–|wô¡‘«eG[¾î‚W§ƒq(e^ÙU“È—žDüÞʉøn† l‡%ûÅéÃ÷rD.ç3gµ 次k{K£R-Ò»™v |cTN[“6¸ ¡µîDPü!‡'e Ý:Ž}…Éêv°ø¨!Ù`¹˜” ÀÞ¡épN`3±Õx IÚ牰î^ûfí~ÉÛm !­è.fAn 'Š—·ÁZï×Ó­5ké;¥[èº~ ªìen7ͤz±Yäý“F<7£}í^ ÷ò#Ö4ƒ}=*O4žÊÃGñìÃé¤Aä<†ï÷eno°GY"äÄÕ·ŒÇ…£DÒÕˆ¬ž––ËéZžFvû¨ìDþC*Ì™··°µ¿2pFÒ-Ø?M‡|¶;€FmA8×årÝh¹ôGXw|8>e˜‹õo™æ„&ìt:^ã“à${9^”´¼ÄEû2Уؖäí«ç9*t#ûn¨0YÂøíl0!ø"WšÑtkcµìÙŸêy¤öOEÏo3iÀÊY KãÕ÷·ßb„ÂX”ÿTÈ” 0Eæë`g±v,éÇäÎo¬Úê–Z€‘oµH¹}Þ‚•ÎÀ_rÑHÛ@xã%Q`º]'ž æ"cœE[ãðrb#'æpNNœðÿ¯Œ÷±Ês†º ‘ÕüÁ|<ŠÜvõ&Z@·X’óý§Ù‹©‹éF¯%ðp`+I¯Ê†OTŽæë“v~-ü™é ÓºEé#a!"—*K/ÌT£+¹fŒçÔ[Üî+i¾¸òâßãè¬ðýUH—ÀÏwNÁï©\öæé"àóåâöŽPÒ\§H…ç+’õ2²dËãbÖLg#LùVÈmÊe„Œ±®¶4m‹½újºÉ‚EçwÛ.o°¾çH¾ßÝ ûnìfo÷ÁfGev8òáí¯$_Ó2)»í\¶ ÒÙ‹a²ôtú)V•^Ò.üßþÿ™VìÅqÿ¶Ôh.™²ŒŒ•nÆ8we|Z5—~Z1ÌœBê§N¢Ÿýë!'A‘¾ÿpdÂÉóS[è±)š\.…± ©ø;:;~'o¾ˆµžSñòŠ­øöã.Écƒ¸@6*¼€v¾.ñm…êbñA‰<˜¿üïÐ×Þ2ì—Z”÷™LçM“C±~92?hç—†ŠÏðÕÔ— wà¼VDîÕ7ŒOé~üt†yúõ î½ÄÎs˜L» œ°ÈIòÿÇãuLÌ”\;iª-ÝåÝpÚ,ÅLé"É笲èÿúŸ„mÄwê' KS‰^…+ëxÉb˳ŒÛð ¢dÄC}R™þj`è ì$;Öo.N–±ÆYôù®¼ÁDÏp¿I$ƒcx>‹ bѨ|už¿îþúÅhËÅÒkvO`ñÆQø¬ºN“Â#³¶Ãî:Ûµ„[Lo¶þp„€¯‡°9By"s¸¶~°#!SUˆA—"iN¿:j=pv4Š}Àã|Œ‹ºÝ*g2?=,–½ñAŽ9hlÇìjKk´š´óSÊp¥Jç07¿,@ùk>ìÒÕ38;aCèffÅ©œ3/ìéïÍxþH*  ù¦c}§8QÝT¶?f“_æ¯Ùz[²ym”¸í£|¶ì~•¢²ž{éQÊþaÓÐSO†TÝOD"?Ê¿áÙÚD´95^dÃø:hAα§£ÉŒ}íìÌI÷CzjÇRè\ªYÃÙ¶ºæß€IãÑ0aýûZˆºÂÖ¼‚o§HÓÒgýìð+q:( ŸÏu£ÈeZîûa"þMÛ; SN4£ÒÁå¤4áT™ÊÒEI!œå‹´¸Nøk+¥èµd†_0÷ä»jØv„îïÌb”¿ZÓ†Oúl'¶]{ އ^¢WÄ0®g%É€n¶#4ÂGÞ3+›náIp]1CcyÈ«] Œ3ß0«óeNøÅ±­ñçæá,9ö¹ ‘Û‹\Pøî|Œˆ©†*zÛ/’qsŠåòŽtá$'^ªÊc²>þ…#ͳX‘;uÌ èm6bæÖŸÄ`bz—áFÌ§Žªñ†ÿ[æNa^œ› §Zü`[Ù;¨Ú9žRÿßþ§y.NÿÎÍ ã\ˆç3ehô&·Æ•žï1åeçÖùòZÈ[÷µãƺr=‹#kÇ fÃ?½×1 C]YÔXÔƒ»˜M4bÅ*¶hypO›–¿S¦t•&]vyåE´ÏÚ–¹ºB†¬7á ®Å‹ÛÉÜ5¯±ò¾õY)È+¾Æ¥m> î¢Lb> ¢ãþ:ŽÄà5&Ô^‡Üä¾ÃœÕ—ÁºH‚ô; “,‘º ýß¹6ûNƒžœ$¸þ±ˆ®ûxªÿe^˜å`Ú¹©ð=íØÅ4ê>ƒìéz”c C¶x~„½ÃÁÈ~ª<ü9ÂWW^…÷ Bá–™ö*’¶µû“ºi®Ív@ÚùiŒ\ÎJú}ÁðŸ~…¡ßN2¼×›à¤0½oÚƒ2Ùpl¨²Å*9^Ë÷âÐX1Lo`¶e i„‘[±IKSØÝ½71X°_—€r Ž,SFûg^è3ã!ʸ/ää¿ÈÖÏ9Wo`òVh¿­ ¬aÊ̦΃plf%³vÜ6Vg¤MÜÿ¨lûÇ–m … ñù«b¼ZuŸ¯eÊôðh‹ ¤yÜ3xl‡ ;¸‘5y7Î[-ÂØïÒÿ äs#úïG€cŽ_¼ƒC^TI-¦þ¿¶bûÎ+ÈDê’ê¨z¨1#9MLAA2H¥A™¶N¿ÎÎsÓĵo2>J?ññM Â]õ™‘Ü FW_£ièa¶eh&+ûL’]^ϲ‘Ûé•<”´Ž#‰ŽÎX{u7z&…sÜ4 8)V,ëÖŸ Ïó2b¹ñ`þÙ„ÖžBÆF¢ÑïI£@FêwAK“0ži‚ºâ þgoÔÍæÝ|E?©Á¾ ãÜc.jXƒ…ã¹Àú ­ÆW À­KW05T”>“š„ªÞ?pü{ÎÏ5ȩʃ’V.wVŠ>ÝX¸70Ýl[ÌWÎá¸#ŒoýKhuºµ6K øúú+‘MÏP)`9)4Õ  ¢ÞY^òE~>øwÆpÖ|a5ö¿ç'‡}bÞ¦õ¡ÉËp¨ö/lÞ§ÆYîTi…0¸ÌBì2º uF@ÃÝ`Q…þ<߀‡,”A<÷&zòeeð\&Ûú:¾}ôc™ÆŽ-ã±û} †Õû ø^54×á™ÀÁç÷jób¼yyœë¼LÁ@½Öt°Wëhï4KzC}?ͯ|Ûd§QÕ•¶4ò£*t.˜GoFXßà¦1&š™¥XâëªÎw°MU ÏîY‰ý'Ȉ£:µÎÿzUAö& öZ‚Ä• X‘e._4Z?z‰i=gW˜‘ÛØ¶f©˜ÚCê õpþò·Œ¸6Iÿ&‰÷6·À£MSH„J0Õ›+À ˆ!±z²ÄCw ¤}^†¾—ý€+F¥G·²^¯y©!Ÿrão® Ü¿T†íš›éI HøÇFZ¬ÕÊFÔ 1ðÿ»8í̬Q¦ð×Ì~)Š:M+É;á2ÔÞ¾žIó9ŒeÏ›¸‰é¢ÀÆÃÌ Æè´):|߇b0u#có9Úäâ`ãŽ|ªo@J‚•©ßÎ ÞŸ›þ1²s“Á-x:¿¯g?í^ W¬¾aäž ÌW¿ìžv䢯Œí‰Bݧ’¤¿í0V=d?<] l¥.3ð}¶µ¥pOÏøÅ86ûÁu뻬HÓ'&C•à±)iðLØ•„³6S§‘¯_ýÀ)›ÐSÒEÜ0e)’ò=Œµ‰ ý¾Ÿœ¼À±zÕþÈÓïâʬÜ×Ü û¿(©Ã~Ð*çìÞÕŽnêÐò㥫cÆ ÝÓ™îséÛÚàÍ'EKO›£›ÍQlÐ#Zƒæô¤ÝY8öµ˜õ„<¬? \C­3Ñ„å`€:Ø6´ ò¿ê„(¾˜þY†É¶QÄUO èåD´wS!Ij³è6ÍÏœo Eèa¥Î÷3¬>(Kýu²gú%ï¦ÏvâäCíì´uALŸ÷9ürI–é~ È„¯ŽÀÏ dÏ3âÇmÁ\5À^‘CçØ³÷ð÷Tq´   §Î~=ä@¹Æ1mÛŽàãâÿ½ÿÚxš>ûùÓh›÷mÞ‡²ˆÐ°§‰d?äbÖ‹,Ên¨…j‡­ôÕ‚‹¬Ü2òiÝ7ÈÜfIãõavüBÖbÒNØ|,–,ß²—®2EUÍ þ—2Îä€ñf8ÿ“õwÞÜ?°åü4 ¯s=¨ºgÁî'ñpQî<>>þ‰I;Ÿ z¦ÿÀÒÖ*ONAÇÏÉô% ³ÿ-ã~}qÛä ôÛÃÈ6Ñ_{ý˜˜5­Ð®ò½îóΈ*U~üç LG&í*^80|´Ñbû.çÛ‚ëúZ¹§¢…IŽs1ÎÏø êWšþ×ÿ2>µ ³ò¿ûìŠÏ¹Àþ{ùh ]îO6qDHغçÌ‚ÎRæD`JÄeàg× «¯?H°KŽ~Ám7£qß?¼úi®Ã*·{„twaȺp,è^#CBtí½) •— ó¼*`ÚZsHx°Ÿeªi!vaÚc®Õ}ÊÔ4‚÷àm !Tâ~ Äôí#ý9  ®eDÜ=E±d0‹c-ׯ¶ßa:Ræ#ÿþçÌ “v!ƒü”J:‚ßÇ«ð؆/ëshèÂ,Œi¸ ¿¾…á‡_à~*côƒ‡„ÖÿR'OO^„.µã:¼r:i;éL*¡'p!T—#~G‘ùŠáS?sNæëÉøö”%Ýf:ʆ®y‡ÊŸ‚ÈÇðÀh)²£uˆ¬)# +ac#Œø_ù¤9õ ^ÔïgcÈt¬rçggÅ<ƒ’gè¡sׄÑ…ìI£#0Hƒý¬¡¹Mœlv5W×Q9Aí=¥È<1j7þü~…íêx® ü”ÜÀ¼k›D‹ÕhIÇEtª8ƒ»f]†vjJz¦¹Áædü˜=ož ÷C7Qe.s8ÆrCÊÑÜd’‹ÆÔ÷ìQ<çueé™Ì4æ‰%´ÿ8‹+Ì7¢ŸP NÙ§GýÓëXy>Ò¶Ð/¿¢Ïϱ귄Iti>U¬²EíMŒ÷í¹DRæÞ\J¢þ½ÂŸ´¨Ð¼(ÈÑŽš/V£ '†~”¿y¹ÿ¡Œ¸¾¸ ¿ÔràÌŸ˜¼#‰ÙjÙ–Þi°ñc Æw´¢ÿ˜1³sím®ÚO{~+ѯ[F&üÿåAæØ{Q|(DŽɖCZ“!¬Ûq Ö·åÁü)ŸÐ ˆ6*©jû¬‹ïú|!úz/'eGÔ5_‡ ŠÏagª9.äø³½t›B=ÍܦÅ-|´‹kH¬Æù­tƒQËÛËTùˆ}ÀYÒéöø ma'›¦A¡î\H[¯?÷‘I¤3u%~Q.g‰/Í_•ï¢wÂpùTãƒYE›HÅ9O·£ëï}dë¹s’q*Σ_J˜êp„ÚÎmD4H‘ŽcÜÚ†ŸÖ’N&¾¶¨Ñ7S˜úé#ì¡ì  ù÷×pÖLj2’ˆoÊ¡‹Œ®còÒw‡bñ|AÖŽUؾý”i–JtÛ&e2:ЪèÌËÃh™3”ÙßÑÅ>ŠY÷N Œ_âõ ó­6ްšu騠·‚Í¿ËOÓŠŽàɳ ˜Y½k‹‡ ë¹ žå@²¾à_î²MŒý¹ "æĵ JÄÃÅ/ "Ò™JlºbEÕ“òÁïï!rl7ÅÇÀÑ•$;•ãÐ[€ô)bæ{æLZC„§Œ²¦ËP.ªâÙ+*s} ˜ öáÖÛˆ[ ý¤)<_Ýh&ívÞGUëÌÈ¿yø4ur¿Š±-ÚÔÉW•º(\©n»ñËÇH< CËS¯° MédXHŒ®1%uÏï’´ÄÛX,¯@³vÇ’ýhäÈÒ¡ë®\‹ËÈš¬z"ªF¹g¬Òð0³˜Tö®eo×e£½!Âô2œñë=œÒJ!'¼ÌiÕ,%rÈe)Ѻ3“*L¹îÖ5óB›»ì¸XBVnô%ï™;dúó ¤ž¯ÔüÓ&æ–Qxl[(› «Š¶¾°ò™>ժ̤=ªAdÎ=c¼ûeӡΑXj›kŸ!;:ÿ·ÿƒT.AWM¼ó¾!åü.d›ΆÅ4‰×?S”Yúži!©r4±  Õ­DáýÌôí™$P#ä†[™ÇµqÑ3²ÿý$Ù ÙrçPæô;œü;^ ¶ÄÏ߾‘·EXºÍZ5”™›YÆÕŸœŽêg²ÞǽۖãmaIª½éš‹&Ñ@¹ållgÎišLúÉ*ˆ[WÂBS<õJ3›—jBãs?t´ _zîr%ö<‚‘ž›ì6ç-Ôó´>n­XKcÌ ±øÕsqÎ`·jübɰS¯aD³m5&ä?C]ºŽšá¼}ôQã{ôú¸GWnþ')`z§nD#v6Ö°\¼Ö–Ì\ ñ€ºž3°Üª5jx‰Nïë0íákŒá·¶óÔU úç˜æˆ¿Ý=‹DJÃüºÏpojýx,ףŅié‚eD-£¦žZOÒ\ªžÅÂ,÷ÅH¨¤¶×Ýi¸B73$|GÖÐ öªdvi>àa´ ³´Õð,ûS.bŽA ,w®ûÙ™íÐ6žóevÐþoØ[¿^± U‹©Fµ1Ô(Ã[ùA,‰Ü$èáßîÉ­fïüïý/CÉlöQ]zÀî£Í“do‰,iê!ô^A5±½òöÉ'Í>‘pq.ì9¸ª/%ªjžpGé"yi:.ÿó°CÙ­—¦ùVpzËB85——Z{Œ@MÈ8mÉC•æ†ÃÚÔ?ŒÎ5Gìq¢·WeÓ|.…õ—ŒÓ£ÿz%/¿+k¹Š§nzO†µ×ÛØÜËÖ¬kÐû|>þ¸ÇžŸó‘‘†WÊäþó+ì¤WŽd©²«…$ÐM\Hè`%–ƒÅòç°Qì$Ø`z5—qåO†åê9°[4íæÿoÿoçù6öÏsŒHâBw]&X"|qÙiªôh§<²¿ìô uûÙKbÀ韈Nâ`ãzW(Ž$E»¥ÈÁ[óÈÍniìÈ.†¶'س1‘ÈH\c§'‚´È0|¡³—Þcµ•hõ¶gÌÞþ@³…ˆ3Óp|}nÞµxb“Ž!’›@ÿë ÆGF–*ÞZîÌû;-L~nÜç¥W6q)ª „f„ÃÃÊ ¯ú{¦&ç=ÂN&&Yb;Û’gWÒ?9|ûû} ?ÆÐ£°l½7‰ZïGS>²?7Z7ùLèÿú®(4SÁì­.±rÒŽ}¼$Óô š®;‡ñ*ö¥£g “‹GŒÙ'¯ÞrŽ”ì¦¥ôlö1¼táëõå§ÛÇ5¨|=~èË`LÅéú :ìøµ$àæW8óûnù)Ž§Óž‚¯]"®p"¿‡7#SóÆsÆs©­èù‡U™Çð×Õ±ÓfR\Ò·£ò7Ùíp`vó±7é6? ê2=RW±mºÖZ¼Û1°UNžÉ.Ïf‚®2¶\eWn?ÒAÌ/Hc †Ö¡êÖy{9> ×&ÍkqûÔ=øµŸóawF%n³7!Ÿ´J ðC\Ñ˼eôµkñ³—4&ÏßFf]›âšæÔÒõ"«Ó:f¿»Ÿ%é¦ãĶÑÈ´NîhZËá[A/…nÀaæT1²ˆÝ© C .~èâ%|ÙD×<š='ìÌìXAªÎ†ù“åÐãJth”9tÛøm,btÅÊÉÒOlî*!²}‹)ëüO”<ô®†ug+€—‰†É T©ƒßLŒ¶HÂK>mÜ7Ë"á (˜µëMÇã¥Yì(Kž>¦ŸV<ÅžoÖ²¿Jv¤SuÛMä’Ãÿúÿ$9A«ËF½1#«>Ùï²ìÕ¯a7]»Ãá¡ÇÒƒh$¸—Yýù$Tò«‘Ðd{…?XZœÇU?¯Gî0vr=Ó,|”vq¥§~¬!ç·š3óœóèÊÖhô¤¼Õâ4Ï{ Ý}$ŒÄn¬á®;Ù€f!†3f»À03ƒ›½ñ1Ø­RÁ*HÀöKpRî˜?ñ§{²C19ù:vˆæ@dX,µ]À{MÚLiD1;é„6U Þ…k2ÆE׌ÓïFŒº|;WÛÀô[QVa5ùkp ¯\õ¿Î”>~ˆf®Gèä[2òÏ//‡]Gãðq<ÑP$–µ2¬pÿoXLJvl |ìibv_ßJtOçvúŒp´¬T™©ƒÊZˆŸ—’iS¾°5;7ôï÷&cæCKJ7•q6“jŽœ"ËFߟÊ\yÖî•“cºà͆A¸Vó’Õ”¡¹bõ°”¿Rþáò$'&ïÅ9Ƹ[™ñX$L¾gÔ UD·RØŸ-f`݃Z8èìÊìZPÄ~Ü*„1³þ×ÿÍ0ÜcØ­xéÞ2}U*žZ¼€Ú?ÿl7WÜ /(¿cù-X¼^î˺G5ƒáõMd Ÿ¬¾®Bó ¤Dðì‹Y0ÏŠ‹‚‘±Øé°ïê6z·3Ã!|à” â’s¶„àñÇ·pv–3Œþñ$ –RµØBþ_ã×sèÆ¶” µ3Îï7°–¾¡aæ}f£‘ ÌM¤e>ç€~x 'wd¡¿+tk Çdøœ7¹ƒr_@¶‘ ê[@Mò¶O‘Þ‚M˜ôý´¿Ÿ4>B›_0z¼ætÒÙFÆ Ó‚éýßûŸ{ÿ½g.|Íg¶÷ ƒ‚ß\Â=^‰ÂƒìšGi˜ë¬…÷Ó[ËôÆÂ©ÜF&éq]4— ²ú^·ê `þdz¿’»¨ô'3>’õ^CMÊǹnI,=×ËÈ«šSý±j•ü~³Çý}s¸]uÌ–…°áŽ×¸“Ã'á1#Úå½ 6K?y“&æÕ˜-mh…¶ÑtÇ­LP …~ù03›UÓľIò”wÏ Hð?ÒaX$~ž9ûó=Ÿ Yµh)Ÿø n ¯áY¦Ö÷(\»u?~Ëš¿°ßRh7Y@R¯LãdlœBŽ]Åï>GOg܇sD@ȼ­]³ÉÌmy(U”Iû*ÓÇ»•è©GÑÔ μG=§ÚPõ¶%Ôa‹;ÉTx‹S÷’·zéA%qR÷d ½–LÅuT‰ÍOij^ÇrIš´{u¡÷mÙ¦°ÅVD©Â—Ì»ÊCßt)ÃR.›Ò#Hlnûбöœ o'ȵw» ó']B«ßípu$¤7Íüï<÷K*X}€¼ ÓIòyºìò$*4£Jv…Á|í¦ N÷®!W†©³.gBþOf”aÈ ²¥±*(%à íØ0ZZtÑÆ®¸ÓŸMWɦ5öëIN¹·ªšuyä ]ÁËIÇ–.]º‚âtÎä38``OÚf¥Ð‘|Y’¬ùd¾òÛcv<“h|Ÿ›C_^ªwá2º×%kä"àK¯¸/ q!»¨pû1tf;´ê+QõïÛèƒË5Ê©Ô>ÿ»Å؆V¼…÷R"ÜÉCÔióE5ÔàòQãeŒ…•]v‚‚¿Ó'œó%äŠë±µÄîœ!›ÐJqLÂ…ìÙ£Hù{)×\OKWOø¿’HWöÞ´(ÌùÕÙévߛ Áüe"ÔÝ­2^rUn5@» #Zꆕ ‚m¦“.GàèÛFÐÛõcâùßЛe`ýÆN 8Ø&C]^‚™Q¶¦­€?c[”¯0i½nDj® 5PÇéD£‚½hpI;S·{&7Xà$>lD—ð ‘g„Yær “Ö.Ã{Bù•¦¼dfßQº3 hÚ/6uè1ðÕ§³BLTìcPX3 gOª-í§¸é@0gì9‘ël`Ìç‰X¯&%šE *õ¿Õàµ÷GÐ[„ÅÿæL™âOÿœc¿‰FÏÁ•˜R>®¶ÏÃGÖ¢”¾mÀí?jaÞxÜÉb®X°ÃñõÌ;º†zÆì‡cœ ûË_—“ÔYY‰4žÚÇt/4¢µ½\ûY´©ª Ö}”`f˜MjBNæ@µà¸ð6•°ªzž>ýŒÈÍ ÓsËÈ5Úð1BBÉ]Ùôq®M—ÃéÃmâôÍp¢ifL«³éëI›IÀkK ÿ(EþØBÍfð²syP5Ó=hþÚÜ|öy-¯HÞéñRÍ=èùq ‹V"ýÕ7῾žéTøö©€×!3NKšõ½œLÿ«›æ*d—ÓÞt¹×(œiŒ†³¦…xåHz^±°÷éû†¿šŒq†Â'Òe÷¿ú@2Í¿i1Ýë¦Q?/Qª‘âFäÎï&œEâÈØÃ¦ IW† «Ô²”zd峯ÊÙ.ÓtìÿÖ‰Y›Œ©ôàØõ»Q¬}ÉhœÚM·¬-‚ ³Mæ%fzYÁŒhâɦgqîÃ×Tá}%}4k uOT'A®qØ[½•Š]?FšO%oõÀäÃa\Gz69ƒ¤DM„ø$HãŒÜ Âµ–u%M¾‰Ì¢›øC2t…Xŵ’ó á¸Zi—ÿ:Lm/™Ð‚l{i~GdãO§£3~Û¬O“È“€ÄZP|ô¿úηú¬Ù§¹Èb*½o€÷üGŒWX£á ÏåâáÂÍOl¼ÿ~\êti_B¥•ÃðÈþ"+±nÌo'-Ÿt˜m¦ô½(1]-@©o8‰º*Kó—{1"q¯àíª_è{> g±Ññ©X{¹·©(Ñ÷ÿ¨/«éûÞoTižKóHsѨº{í"i"™R2ež§FI"!I¤R4‘ºgm2V’B‰Hæ!C¿Þß×ëçó×>çÞ}ïÙ묵žõ¬}×]ÇjÛÏKÒyÊ3ÝÉN휆|ëÆué)´c ¡?SóaÛ±élíªI\ix*ï9Û·}#b"/ÀsÀ ßN4‚³ú²LjR9Tœ®Ä¦è–QÊ“ËèÁŸ“RÈ’23¶O]‹ÎH8ï}¡Íu=vF¾Š­”õÿvU§ úûx¼­±ŒI­Á}yÌýàÎçµ"›5Öˆu‡d‘ÉçÞc¯•34° ×'7›˜MlìÝÕLè°\<ï}Ù°bs%çìЉÎÍ=¸åþ|ð•丽ôi¢)¼/K7\ÞN¾gp+–¾Ã9íÉø`\'ÄÙM„«ûèÒAújG&úó'À5ÏØùè-jîŸÏ’žªP…ŠRü8‡ZÓ”X«Oh^ïÂwë: ½'õy™ y*† pdee`ó# ^-Å”^Šî7Iù""¦(KCüW]%hÅ(í¦ß/M…ÿ/ÿ¯[™(üUŸ3Sù„^óhŸƒ53 »ÙèYÏ1ö=å›<ÙH}êÒé‹[ôû¼fN(ð/X{=F¯³¨À}FµÙl–€“¾‹2V„­yÇ©›¥2 ¾4¾Ô€»¾û†uX{]Ö•Céâé˜yg"ŸœD›—ØÓQöqÔëô ö5¾‹›=äï3÷2{Çc|ÿ ¶ðÇ|ô¼›L”dN/o°øÕ—°öâîî‚çLØ)ý••`·õCï%œµ’VW]à,/à çqAž5˜x–?»ËÙvùáú›Î ‰_#ÔØ Ûƒÿäž=‚ {ðEš?{qù ПšøìŽ:Èlz„¾åÞ4¾óžmŸÀ&È·aU¯¥r•4R›_µêðä5#6owq n*g T>æ8ÃiáòŸ…šIÁæ°þ’ ‡Žæ²„Òuà^µŸB{2s±áœÃǃúŸôfªü‰–cðæ˜˜q6›ÔÞGìå G6™åx-"‘Z"ôpÈ,¶ÉY´lßà¡™Lù{)ó“ñ`–V$ú¥%{~±ç¨eMŽy­TŒíÿôŒ¬4W€Uý6“7ÎútîóXØoû¿þ7A;vÂ"•lê®Åã[S †wØE× ÐA¹ûëS€rf½œ¹ÿrn»æ=÷üì/ìG#±%†š]Ñ@‹ÙÂpÈ4ÿöýâLV‰3¯0kïnô-=Â-™&G—G/£];çòoO¤egühß–Uxf£|pKÁ§KŠ˜OÏšvû0·¬Ï•ç~Å—Ì_‡Š)³é©]ÜÛ³§~ÕAN®¤¶î~¾/§±7¿¸<ÇèåAZ†%èwM?6æÀ4œæ| ¶\¦+¶Mƒç.ç¹y½âôáôJ¨ê¿Á¹csŠõw¼…rTíǶýïTìqÝicvZNt„[¡³ú‚ÙöeâìׂBøda†â]E,C2 ÎùÀ矹áy(,âÏ"æ‡Â˜û©w°*«ÛúQ„„¼Á3SWC£×=˜zþTk±Ÿb]\ì:héj÷ô2¹[ø ßj7ãågÌg^J|?‹Oš>ð6ÿÔ„ÄŒD`ÛmÙƒo²lv·*÷lêj1íf6›Ò¨Ÿ¯QKÅO‹ß%?ß>„bØ•U'Ñú.Ëh‹áý>³»œ3÷oœÃè¼(:ñÞ!^Μat½F§™”`%7¯Ú:Òï7Õ xmÔ?ûŸX‰]ÎlŽz ¡·AìÎ@îâM>hDBA÷XþÒr©)z,ºwZT˜KÂA4ó° [9·AÓÂ’Îìàdå3È.ßIx# ½Y7ü<öš|Q9Dœ{7ÃuÿTmÚKjDñþt®îc;º ªÓ­ù²ôAñxZðrÇ¿{· îâ.Q.áÊž ÿ\°`Ò;¥è ÕjœÛü÷ÝN·ü½p-µ†îîM­ ¡h}=çý5¤u˜.Ë„Œe[XüçËäÑëÂÿÖqž³ Zø*¤üV&£Îî‚—+Ôÿ÷û§ÉgÜ+ôt„fð•ìm™¤èb¬ŸIØûïò¸|í%ÞŒì2î„ê:οp$®?ÄÒ¬!¯­m¤–·Ä:Ðûöƒk‰(€5‰ë¡åÛG2î‘'ÞgÀK““àø¨‹³‘<ÄVMòÇ™ñ°¥E–.t…á ²¬î=M=KÝíèg7W*/åÓù<®+´˜^›Åk‰0þÿêÊsñ˜…¿¡{oåo rÿ–[¹ý l=§…§Ö²•³©üZj¾ï†zÒ¨¹SØ×Ø0pŒ$ÏiO£?°V°¿ëNe£¼Xbe~£  £ÅÿìS <®2‰‹Î¢=÷r³æŽg g±Ÿgíi”—[ReÅF98fá$Þ|­ÛpRü!>ž@¯ßXÎÉ-œ]xÍa~Ñåxnàí  \Jæ.wèἇ}äùÉÇp’L…SnsÍçc‡y6ñ •e¡L Ž?må,;>!ù·¨¬jD ¿•ᵌ"\ wI¨ÎÿøÿuÊ[ÓÑ÷B ‰Á|ÏE¤£½g-ÆÓËBq“ÇRKpÃd:7V·×¸°òæv\k=†Ü2 %.ýüã§È¼²~®üLðÎÿào­?ƒ>ë/pAlm‚ÝXÎù_ä¢Ç+Ô¶¨%­º<÷Ýc¸Š(gV·8ÇÄøñ.Kå¡»I"C\ù±÷;ÐÞ0Š5oä¯<Ž ¹éúÇñLùð2ø ŸGlíŒ ŸÄuÍ6§ªâLnÇlp.ÙC·Ÿè#§¤˜õÓ·˜ôy&|ÑÚEGæá¦õ*¬A³ûrâ1®>p‰[÷`û?ýû I£~Ù6pŸÄçjÞAß«<–õNƒŒ^Upã‹ þÎÎ…/)'`WŸ7ÞŒ’cóB&@ט<'Àã‘/×wÞž¦·?Ôn^ ®O 躚ÌèëXÔPœDÍË”aš£ Ä£[÷ásWÂJ O£çH]¸MKj¸ö oUéâé%Íä—^ƺkáD­ÝÄG9XÅ^¾Å‚¬´™Óù30Jf=ô ùíaK•­“eõf¼S‰ãYcô%X&ªƒIñ ´µÒ“hÌvÀÇ­\Ùüy4|Æ"æd÷¥B•þÅ¿yY#Ø<3&Ø¿$b¥|EÏh°4É­Ÿuc /UÂÒ"zñ¦m*®¾_ÿœ“¢YÙeð²éÝ‹#§gpÁ7ÐíŽ/Ûœ{“×O]ÔS꾌šœõOÂ{žù•”Fy>‡¨?RT5ú5~ê‘€ãÖs¹»]sQäë~=Ù‡6± •èÂÙ.<Ï£Á³¸ßè‚®ü9mna»ˆ›øã ªÆo÷󱵯„u^M9³1Ìø¹9Oh÷u˜²"ŠŒoW†V'Yju3 nÝZg:ΓÏÊùz"Ü¿x€ìŒôga’-àuá8Þ ZHÔ‹÷`zâ1ÍÔä;˜˜±~‘'¼ÿêªÿœÀæ¹ê´>î v¬:ÂÝê/ƒè" œc”CK~—eà$¹&±)$)½õDÏÎê‡ ~·`½¥%´>ÿSÿLà^¬üâ‚ë7&!#¯?…ÃÜ@ÎØòŸš’Íi©‰áŽ1»áW¤±6K?s ðÄßWP~ÐŽÓfÇvhó{‚øSÒ³è¥ôÉ0«ÇË Ol33-÷dñ•/`‘y ãL@xÍ5ìúîËN½ZJ n^ƒ†Q%ph…ì}´mH“;‰9œû±èp™d%:D:³s\,F¤‡ù*?ã.rW¯5‹ØŸÍáþÊ‹ž=†\² û]6‚½N*Ô½êŪÖÉ ¿høì2ˆ¼åN®£ŸìódsçÒ\·bؾ±ëæÒUr"Ÿø†±?PGÁF‡¿ýgÿŽ~çðþ¾%XõJ™išàÖ7¦9Ùhñx£¾§jNF`vé5Ñ­ÙÞ&2àø:Ÿlݰ¼jlÙÕÍ{ ·+ÖU]'wGM¥»ûÏã²;¸¯^FÏïĦñâ4q@“Õïe:cw F/Ä%æqRk}!Ø]”V8%??K`«ï¨°†qÇ0þ^0ýkeÅâOe‘)œú+YT}ÝɆ4͉m»$F{¦\@å¯qzÏJô×<€‡_îAèìÀ†ßK‘‘ÅÝ_’Ï÷Òìo;ef.,…‚÷YøŒŸ —§ðósè¿ø?_|ÿKÛQ<×/ «ˆú´kðâˆ#›S¶¶Í‹A‡ži¸ó´'u+W„«0“Yâho‹øbĿȀ­B­èÁ’ð4¦´|Û1ÔÝÆ6O{Y÷…ÐJ»…·dK Þ°c^KƳwɵ¼^.µeã/üE|®žÁò9Þ¨ÝpœHK¬Å3_ûùÉ&ó Õq1–×ôà‘åQ°9…*«ìØÖU×Éà!SjØÄ’—ü…¨¹íõ×/UMmØr2‚}ÙøƒSýàƒM5Að¼Y®Þw[´ ‡; ¨€R5¢`ìs: ºI_5þÓÿÛ ûÈà©z°6„¥ ³¸’ÄÏ µªÚêá“uo ï°j?Ëþ”ÊÐs]ùÃÚ¥œÐÔ\ 0®÷í?+áW¡ 5ëÕžÕ˜1ƒ®ÿ‹Wwôð7÷@Ñò¼Çó•ÜáòßäíæT\&°snp¿KVáØ{©äöÙ-àZ˜…!Í{àX‹&[¢6 yœBã•h9ìW§ð­í’ÈEûIÐ]zË%CPžnÓ§‚A²ØÙ.@Oó幑Ïs‘—Ôèv%cøã’HÊ4”êÓªO¸#Íûd¹ e›aHM’Ú¯KÂ[vBÿüZ{xâáçÜ‘ËÇA¯-‚øÆt³¶ Ž×…E&v{œ…¿2ã^Ý;7;•Àþæ œ/èÁæŸ3e:Ñtã+ “ÖåNxL››ÐßX¢W+㧦xÊ5”Îßf<>àlŸ\ôn¼„—+ñ‰®“ø½ˆ, ˆ¡½Ê0iâ1ßMÆÈc‹¿f˜KZŒþHn6k1aíÚz­Á4nRýx(¹šGÞì¥'o¹À¨20 ý†eRÃõŽ?°;íÜí“ÛÙ(S?¶Ê$Šº êÆÈ?]› Á>q0AhYÿ €«küÛÿ[w¨¿ú‡sK/$À6o5N\U—ÞÖAþ®W¼Ù§dë¾O{Jþ7vXƒsÓn~eáL oNqw¤hðHv Ãf[Cæ³þ.É;½#?d2Þ­VLtÔ¡nÄ’=9rˆ‰ª:²°yS0[i=K§‹΀çGQv\,ÁÄä8LÝv 3J.á“^UÖøòFÑXè!¢ñøsïT´Þ©EÉ­G1éj —ìó¿ú×Ô¿¤õ{,xëzÐ{JQôæd+àbQ²n6> dß|%Ù’jk–°> —ù`âEmj 釯px_p¥ž ´½!Ìnë°fIî"{XC—é†ÓÔà‹¸@¡ _†Ø²wb.d¶ê˜Ã›Ì–õ°«ã½hy˜þj.n¬Ëå˜HÓSS(Û ɾœËáæÏûF|dgcâ²SÌs·.qqˆÃð+†ìyòL®ºw;d÷”´Û¾GÉðè«CœÄEamÏ,ÖbµK"¯¢ÅEöäj=¯v XJÈ Ñ’?u8µÞéßþGѼÅx¶ì(®S”¦r)ç1(Ý}»cM' ñw!ÿ$q›¦Ãþh6rÅ.±oq¦½Ž‰гà²×Ü„Î>ØÔ“N<ÎJÑîÚd\û“|\— ŽèGë$Ö‚ì“\\°Á‚‰®îƒÙwƒQUòùˆ6;+LM¥ì¸°©Ñô¡0 ÑŸ÷–5ðZ¼ä.ö~åïŒxÄYD%Ñ7e(ö]—¬ÖòçB oÑßR²¤Í†åôñ‰Ê Yvbe2Ž–¢2¼mø6÷"^‹¤‡œÄAã<\½?[ç!¶¢{muÂS¦ýÓÿ¸7q¤V剓ޅµ–™Ì¸Rס´™,È"’h®Žô'ÃP–ù ­3Àl­ú+Ð3 ˜ØÓ]ä§ÌxúÍU”N¨œ]?ê›Õ3¥RÒæ8vü ú”ÈÍ~;ÖXöÓNÜãÔ„o¥Ÿ6®¡í¡]œÉÎeVyûà+wLûÞÞr‚ïP÷\¶¡O’M®z 67PGÒ˜m8)HEž/¦wƒ}éá×k¹ÑÑ.ôì-Iv¶å×K¦²¬fZ]¯EåÅÓaëðZV9`ÊTdŠP§«Âu?ÿ ù•Hv±%‹õ'0±óÕ<þ¡C\f»%»¾MUµ_’²›é[iS+[q”õÞчcØVb†I»”ÑGÙ˜ÿbÏf¶zúJ6ñòs®¨õ6H×H3”¡éž0mæE¼3¸Ë÷¹°¬y\L|‘Ë:Ÿæj^6y³¨ÌKr4ißÉaìnп—f†Q ÚX‡íÓ.sû»xúë^sQc‹¶<Åd÷¯Üþ‡ªT­ç)Z¬Í¿„Uÿð¯äúgâõ7—müƒc+¯‚ȯ»d2Ž¢Bü­l—ÔTʳ ÇOëÊ–íØé4~Î+~tÌ£äpÔqöH(›{u?’寥£a¦+®ñN‡âg³¸yÉÊ´{ÃÓz·›° A„%ÍJ¡{G¡{UoÑŽÀRÐL‹'ò—ÞqÀŠ% wØ‹`ND¹o½“'0!ós nùaØý+ƒMxÆÃkÂÍpÞ-W]Á6©N¢­Ç—ÑÝ_òðþÐunQa¡Ÿ;±zû[ìL}מq´+¨M|A•Éô™¡/3,a­Qèfu§òß¿‘€iÑWxÆÙçÈ—'peÊa,:QkG[cŸ™.ó¶"rÝR`mgÍæœŸmà=\E¸€÷N{v | Š|Äáê"Ü1Úˆ¨/ÞšVw–äÖáüÖÉÓ_"ª¬Ä~;ofôO0Ì.'§nÔÀ+ÚK²ÝðÆ þžóÒ”½þà ·©ÄàHm&`>¿‚áÁ¯Á/{ºøZÕßV¸@3ë h¿™6VE­v8 ùZ[ht‡}L£1Bm,»:ã"|?= ?žò¿íáªêâq]¸ñ16¥©›# üÉ ˜j\ é›{þÙÿ´ˆÅðûA4ïOkÎRé—4üT:îÐ?Â.?¦Š@–fÙüEEòÆr©­wpþ’F¨Ï‡7ÚWQ.t%däGcêÃ8Nÿu lwÚÉ+ØeEK—Žƒ}»Ö³¶×¹n½n¹ãzXÙü ¾âQw ›[Pgg‹…¿ãrñ6œ0F„vë³Îáµðë|2oªavwL¡Ÿ>KãÏ-vé[!ˆ× ‘º1ÉÐw2?¯KÀm/špuw8¯<е§ø['¹À1ñ2"\Pqr~N;‡»C•¹ÍŸ.ÂÁ•W¹³Š¤ñëÃòÔprÚlê„•P“®âE¶œ£Üg²÷WwA’MÜeH>ûÐwΉœ¸ÝQp¯.€SqlÌcköAf{¨@Åât^Çö½Á܇‡ièûB&ñâœð šöh€Ó^Ä=žìÇ>FàÓé‡9á.)¿q=ýã‚b^«ááé÷p¤fÄÎúÑîe¢.8…íY–FûrÿÀÂb1ˆŸƒ«"åéùW,Õo›¾+™;<éÒôï t/ŽnxŽŸÕÍØ’EŒß‹½Böܾ«„®?¨Bw6Ý„9ËÀBZ‘ÞH¥£ZŒð‘E÷?ûx$×(â¹ÀŸò„»úx~ý«ã ´l5Ƈ˜¹cI´u™‘‰b«‡àu’º{×b“P†-±¥ÜÛ$šX^?ÎBÅ]ø¦{Ϩó¾}È{dQÌÅ‹§Â·ìÑôè f²q$Fð“áåÅcuêUùÄk$/I?æL?ùû¥[ø9ÃÓ!ÛaŸ50ßjzA6€}|}fÉ¥Ëᢟ ;öÛ…!ú\í靿FÛ6¸ÊÝ4OñæægbÙô×ÿÉÀum«&³>¤pÚ rÇ×N {ŽèAk<ù§ÿyñ7€ªB— µŠØ÷½7F¼ÆÎÇ9»½âÌáÖ1œqƒPÍ–§Ä(æ&ŽV–e3_Pnú‹ãÔ=£ %¿x¢ùwM&V,€-!¬ôö\ØõFŒÅ>Ç31*|1„úVŽ‚™Î{ñt¹"·øŒ,ݽí ÉX—‰ÆOÂBåbüüqÈ×¢6»§áaçiôœü-Ô Ö¡û¦yÑÝûÇsj‰Y¤í-,{ OïA´ÿD¯‰4sëÿ¤±F)lÿçÅܤã[8­¥£™þ«&8™âÉm?rç„èŒQýš[×+?rdó¿ÊÃɧÏñ¶â‰ò?ŽSÀ¥=Xüiúù·+íx”FZÎp©ö½ð©È/¯öá¥Éû/õ ¹ë•‡kn¹Éû]|¶”¡ß·@ö߇å9þ¬£ã6&H®Õ¡6ö*% Þ)Í„Cªfôfæqð¿‡:]±` 5–Žd²ã¨y­;ŠÚÎ埙ïvÑï)l‹/kZ÷„s81ÁŸPôçL{ù>Tï!Â…¤<"— 0¤1e]p~3­Cû—û-!ßùG Mh׳0¸ý‡#±ï·"ý±Ÿ÷(_€æ¯Uek7$Â5ë–¾°ÏîðÏþ£’y¨]=ƾJ%x$ˆ.‰Îƒjºñ¿zc<·j<¬ ¥+³¹5FÏÀÔW™ºy™€Äúa~Zêlªói¾ôe.†2¼3P¾ó7.ð8þ5)5s¤òKMßÄÁÛÉÜÅm[h\ÖDº\° 寢Ìí$úbE,5 §ö Æ2¯õi¸s”*>âîŽ9/œx¸bõw¸ Çõ "¯¥“ÈN¦¯+¥é‡^Ýßøñ´Ú`+ Of©ÑOÁeμñv o¡[v¿Æ‡›Þïwø¼è(z^=xij.Ù~« òþéßgSöêÚâgÝpû#‚‘“ò!ån “¹Æ£"j¡Èâ=6ú €R¸,oˆƒÆŒiT<Æ#¦äÀþ¬K„2­T*qÁ¤lÒx9UÞ\änIåøßàŒg–[b[Îq“$Yó…Ü…li–±=_/Ò Â,Æ5w×~2ð²‹,.2Cæ5Þv×åÞn,e~+_óš..Å€UÒt–G$Æ8%èyóààœb¾«›³™ªŠ¡> h¿dqT*Bͦ¿ÄZ§g¦Õã™ë §7‡ç¨áKízt ¥­=Óô±kª=ÊüÍ\JÚ¿ýŸ[§Ê`kÌKþ'‡%(PîF{éóÎC“]CœH€-R.EÿRâGU–¢d"ª\ÞÆ{&†ãÖðµŽuÛ'N`ÒÙ‹`6ï —$KÂô6ƒM`4>Øs…;—oS„ØŽÍ0u`[†ë–AOæ "ÓÑù¤•>ÖEb¼ 2–˜,È…“%i?•„‘uò[ f±¹ü?_$ QªOvÏlÎçó1ü²?`ûú}ÝÁ’Î S§¹Ftã€1zˆm;/¡Rˆz7þV?Æí|Œ«{·~5v†o¡okØÄU‹ñ³s#ÄóýØ›4œx5mÞà‰H%:ë`|jN7ÍœCŧúÑëíi¹’¼˜|¿ßS¥N~×`àºäG¥ÀS×Pjö>“ulæ¼ÚgC”LVT¡s;à¨61Ú»ºS mîÎûxÜÆ—]÷„ÊÃKh¹Q·±Ï™i%éc— ߕς¾ñu®³G\2ýˆìÉsÿ~ÿ²·«ê´w{8¬Æ$k<~i3Šn¬€Ô)v­M {QϦtýÃFjs¸™íË:A].(àô'ºp@í3 î‚´§¨Ë|¼?@Ôú±2Ñš ¯§SBÞ[(ú¥B“óó™aˆ;®$Çnµ€ü”§è*ôù#W6Êe ó¾q ‹‰¹±ì-›Dô”FòÈÑ_À¶²Üu™XK\¾šEžI…¹ ÍüäT4õ¢0o.ûu>†Q} Ç”ÇÓ+f¥tÔxv8/š=ƲR­Ì(¹ z.-šÓFøo )-ø‡ç`®ðVNìä Ù¶Ú{p1ÖžÍô„\*PdzaWÑ{ nÕÕçέõ‡ƒ%"xvK(ó{–¶7ãðçY¶îš2Šë’Ù’ÃdQö%dC‡P:£w¡²¦ø'Iž;uè<óôÖ‚&ÞD|»ˆÏû¤w’ŽÙ &¦“YÈ›& ß9’NOƒ““&Rë@^§? +ÒÔèøIOÁjþ:È«wDããéõDz@h„7•’ñ¢·±p´Í©ü„%—VÂåÒ4,r=‰¿%XÓ®øo]»+þ÷ü›Þ5_‰Cg>¹±A…æ­/f´Á'ÖwŸ‰qîÜú as™Ñ)AŒo¼ÁË=ö]âmi塃4Sן&î?‘îK(OOµžïÀ|µï¼[©b¼-“£q·[¬íYÏ­âp¼´Ó…ßô ï¡ýf2³pÌ‹¢öúà‡-'ðY¢*{û™d~vf0£‚·w±“)ðaYÁô £©øêPñWæÖ<@ëØ{ãpd† ÛÏÛ7º^ñ›ÏGOÖ˜B-ùs.$âÕ‰ÛéÏ›öL¤¿†ÚlZF_ÿÅ7…¨È²=ÿüß<Ò—T,Ú€ã2‹¡ÿ¡s ·g/?{rn*å˜ro:1ýæL礟ŸõÜ ÎrgeÂë’l6fU=vêgWÌl'ÛÓÙaGê–NMcleå•°•Õ0FâõëgûòÕ™QÓC*ÜÒ‰Q¡o9£?Û™ñve6€~‰,›—ÿ€›Q-Œi‡¹Àkاљ8vŒ÷SÿVzü€Ë­Ð4g+vZ)Ñ "ÙGOvãã}úJêTŨùa5T}Ù³YŒÝrî]n$µX–MÌŸHR‰$_z©C`h»ŸšÁZš|þÉ¿AÄ>?¼Å5}ÍÇéz½¼ ñ¸úâ¦%2„dW\Ë¡ÒËxëÔ1-ú¢÷- §ï×:®(4>„QÉ* ûè^vájEIkzúÅ Éê¸ Õ H²[ÁÎë× õa6¾AæÍ7“g6a¿£×öò*‘=6Ô.'ÃhÓW\óðxîiàAœùÉ‹<Ÿ¾ˆ^ZÝŽ’÷5ØL±}˜.$Â’¯cÒÓkpÙW€(שâ+Ña\ÕûŽ›öe/‡×ÞãÊêî+lPó·4}çg„Õ[˜Î« ôd¡uέ#ß2¢«évòyË“øga»¢â»Îó`¡~ìÔ¢µì¿ZdøØÅmO[É“öP¡cbJ¸U-zÍÿ÷ùÃSÞÃÓR¸ÓHž%[93µ®Ýи9¾ Þáô¿tª0ÉÕ†Â_æ°{Á´ó¡\ÁQºÿš ]QdH³Ž8qcáL•!¦è¥ËO9˜¡¾‘Œ ѧ’©ßQi†$Sò¶`¾-V4·ÿ<.Ÿs‡•$¤±‡c^à˨Aœc yœ2}^}v¤™²U/-iLµ:Ý'&9;ð@õaj\ÆÝŒ8ƒá/öÏýÜüØŽß®N‡ý'˜úrùüÇ&sÿxÇõȨ} 4Ñ€&<šƒõ3¾€¥O&i• !ž—êÌ}©Ò /ð\ z…½ƒyAoIžt©‘£t_°= 6r¢%?+¹ÈÕ]¯í< …?³ñR Ö¤¦ƒ}È xé!ERŽBÌk-úèPjx€t•¢Ë»´Q[í< 8¿¸ÚÜŸ™„>‡¸ò«¼ãN }ïàÙn)j¤%æ^ôÓê?ð̯ƒì 5xùû\ôÁ ŠãÆ1™§`Hï<)ºÔ‡iºîPïó…kßæÃ3,¸‡ͪñ‡äþzç¥ôÔê*Þ£„ø¢ÆŠÇçÃU5šPý¬–ó·È±²•BäqÈwØø&ŠºÜ êçÉÀœÃÜÍÆh¹0š;lœ‚îY2Ù›0Jý8Ze]‚„|rN&v;ÔC$é†9íÌX>Œuh:Qã©åÀ{„[£ÂÕË ðÄvsNBD…ŽQ‘¥olö[û[1e—73\° üiNîÊ}΢ì¤pZ1ïýô}Ó_øì{¶ÿ0£¦ŸáJ*ˆÓ–kÍë³Ñ5<ûMg±1]XEGøæpá%>´uA,ÏcŠ’íðAÐ KêÒðÑö¬ò[æ§©!$~Áíõ¾ pÈü÷ƒò¸m¬£UæsÝ(8ê lI &[îÇÁ¯ûvµ?¨cbc=UªÙÏM3: ‹=Ö ï´Àç/%¦ëXP~<¼ŸÚ:2 c§N8uûàÉÖŸ6¥YSáyênLM¯ø‡KbnƒÒY˜}‹½:°”Òš“2h?ÀÍ BO~M¦3™¹¦-J#]ƒOpÛ¯Üpè Œ6›ÃFFr¯Å‘ÚíùÆã\Â9FÒ¡iGERÉž—½³÷ èÚ‹£‚î>¼õ¡ƒŒS `SÆßÀ3œÈÔ…°(Ï‹Iñ¦ÓûЖ76ËF²UÃZ´ôÃIÐÕ5ƒ¾lü"D·Ê'À›4¸Í%dPË1L&ÝíÛD¢Jù.¿ÿ!ßlé eæ0Ù†Ÿ‘ÉÃ8”aGU4hýv>Z}Îb5[ðäIf}=ìŸÿ¯µóÉU¢´õF ¢g¶Ý‡W<»óPXçFŽÏ‚9)¬ðÁUî}Åy0–wÃ6Ãè,§šF× ž.Â#7¯#.ˆ«{sØVlú‰Ëß /Ú,ð‚§+Nžõ£` Œ~Ÿ–‹×’­)ÛaÊ»Ox.o5;&³ç*Àñï«àxŒm3XÎí·¾ï‡aèª:™%; ˬ½Ùþ*ò{Äל£“HÇ}vå›"u˜‹±åÜŠŸÑÜ=µRØ–F'Ì¥O2_¡9Ì·ø*¿?—NPŠç(^]õü‘¸SE%$Ø5Ùÿô^¸\V&£¶£ ^m8‹y…S0âdW<÷"ð/ïÃóõÎ83n9Mcî½>°ü׈ŽÿBXº*˻Ї>»Èœ±±p´èoŒ}+MÎç9R_±Hpúò ÞÏX‰k»I¯Ý¶ü× üùP‚g%€“!v–*ò·äY1Û¯qLúýj6q™#³‘)$oçŠ3ñsWðê}l<>f|ýƒÛwΤùNcPN¨€g±"œõ%òqF¸é-¡Î' 9ß=¨äûQœâSA¦ûl'U[ÕNü{þw„+äJ¯@¹MÖ{½«_=…qËŒhf­´?ÞÏ…¯'Ì …=?pÚôxÞG\×d±ÒÝ{ù‹É$œÍRÁFê·ðf0ojÖ&ôH¡“•¦ÐOƒã£gÐFYIvýU"´…·ƒ“ãr6ÊΑþ•B¹Š8ÚaÈ­O7íãÇ1xž9ª²q;Pw«,ûà”‹gwÎEáÍY„7q?‰ž}Fµ`NMKh-C•Z?(¹ìIëÓ'Ó´Î,ªgQŒ×竃®ÑA3Õ†Ž\jEvcèP-ûv‹«ßA _ =&úOÿ?KPùl4˜’Ï3Ù$ij˜ÚÛ…Ÿ¢‹ð~HRÁ‚ã9¨@™”WׄeÕÇAûþKü‘-@—Ÿ&6ãäszôïÏ|±½nô™‘:³-7aßäÑè[¦=ö=?«æËí×ð®¼0;|h÷ÒɃÕï€Kkܘÿ×.”é³å¤õŸãÛÞ·Ù£˜ü%/N´é\Ýð\ÁßýŠ˜ÒG™ÙÙ2l¨=C6ÖíËxÜ#§t­#5kž ‹Ïì'¦=y¼¯_§¡ïΈ`»àGà 2áf/.O,CĈó“ýÿ† dp©Š-¹«/EYwàZr øvôñ¡ãïÇÄÑ·¹„y*Xc|Ís„É£ñ%wæn+çfdXÃãæ‹d[sù-©AŽú Ó<û%€OÁâE›˜”_-œ·x ¶wˆÊ¼Ç˜zæ†^‘eFÛ\þ^µ÷iý$u9ü)3Ë{,M»-5驸 ÇêSÓï@éÙQ°PÒçôîÁ‘8»Y`HžÜÉ©{ôX‘âˆj‚SëŽcÃ=øÕR†-ìÌåjKÔ‰&„qS_ƒCíÑÀKÞ‚C_ª™Ï¸Ç—.AÚÙÎø·³PÊçJà³kÒVÚ%é‡h]™9ËÎ á²iÌý; –ÅÀ%)#|bˆaÁT¦aÓ뿟÷kôM0iU 2oçÒßIR8ñ¶ºÛ‹³[{cøå…x|ÂîšR·\zª…‘s´þ‹šÓ¶ÃÒÅq•eÇ ÿÚœˆ•'ÞrYOO‘]î÷xm;ïòçœTc5©$vM÷jG?T¹ûÂÇbØòÙ’Ù~°b·£¹yJÈÆ¶6ØÓq '¯Ámxz©)—¡¿ oLsÄîB §ZÏß'g€áiO!}âf­±üŸþ_Åqœ¿Íàf¸8púëþp?ÿNaÛ²¯b™=á¬O ñfÄ[~¨G+ÎÞâL™F17T÷ž„_{€Ê-ÊôU~"z|BÆþÍÂ]…lÙ€=}Í9Ÿ›˜ìÏf×m§¶Ñè¼w þLˆu€3ð)‡ MõÈu›s“„.âeƒ ÜèT€®¯·2/õ%ÐØPŠ™‹PH(fzÍÄ…«; äàcøò«4ÿ×ÿç‚Ý+b:Ge䣙C/I0Ñ®dk}êl¬ÎðÜKàùƒo⾓³\s›Z¶™¾|RF~?FeImnßöö&¯GÊ…NؼOmžMâÈ=ù¶aû@Sî$jÝá-kÿ¢ºº$wÙQÝFϧy6™P ?‹âÅ%d㼫Ð5¬ÉÚÇ›`¢œ »¼ì&ÿÆÍj()0w¢Q[/pÿ²Þ¢hvE}2=:í(wi]$]oÛ [÷$ð*œpk¼¬ý"°C5wÇëÂr¿>rõ]ÖÈÜŒÚö Ž©Ÿƒ•E™û¼›àègH“2`ç\öfA*È ÊÇšì¨t.j9¯†/ž½øm¦›¸aÙ\‹ù­¹‡Ù¸oÑqŒ´>„i¶¬Kß<ΤqKð3qv^šßÊ1@Èœ­ÿú“›ú•«‹Ñ¦ïÌnü³ÿe“·q*!#8™‰ Z¤Uq•‹÷„/`UîWázI˜^´§{|ùGUgb³¾ µ׿.=l±Øó°0ðq[ô‹ âµÎò£öãÒ]Ÿ9÷{[`šæÎfg)JL÷€ëP!wÿ6Áp\ ú4‚œi:Ù óú½«áû•n]Áݰ9CÊ­iÓL9¬¾‘á§ !T”¦^O×.[ÌZå'F]ÛŠøG_\G¿0WL?CÔ#ÿp?R<ÐOá0OWev7å gõrš+äEGm_GkÚO€Jˆ¶„/…E¿þËÿc‚.q§ºÊpáø=°|] ¬‘¢óLë¡:Î^äCÊ‹ÌØ[4,½ Ô¬oÖÑP\¶^Œ>_7ŽZvN`§oóxùñˆ•>õ¹—‡šÆt·Ò]0 ï¸Og™‰`&­¹¶cþúìv9Ìt âJ²Þ⎥¡“­H÷o>‡ò³%ØåÚnüôö¢í ½_èO犯‡Vôƒ˜9 (Æ(TÚžkEïŠS2~Š=Ý}Ó¢ ¬áø³S\ÝaãLð™„ÝfU ›Åg²³UšÔÎfÔ¯ý[›0B1’×Lëûõµ•.àjIpBC¼;(Å›ì‡Ù‹(¨ÇìbwÆ$€ñ¯GP²åNOmsSnÓû>7 »Ì-ѱ§Ž>/K%?’Os¹+Á¥]Kh£ˆµ¿–Æ»Áä÷"¼/r––”ÁVÝa,ÿ:ƯdÞ{³ð‘ )¬çV™ERïöú¢ÚžT,Þ€%³ÿ=ÿæçÕN.sÔW©”ÙÔmK¬ømD2æ› ¯Â““úžÏÙ¶Ý@ý/aDÀtËY_‡ŸµÁq(ÉíКªÃÞ¾öd!DdÎ1Þ5È£gJs«¸‹Î xéAðTM¨TA_=¶‚G8%ouU‹-ð|¢ï¥˜Ùž2”‰Í$×"µ!L¿ù‹Ûñ jl'1‰Sù0ûO*ˆ qÇ”Gr·‰-øËR֜׮{.xd­ŒÐ½ÖS]»“Ù–OI|•‡FtW|4Þµ^‰Û _ãxÚØà‹óÅ'±›ÐÝÌ?ÿ7ý$ƒS.Àg-c*îBvËÿå[\õB›®vܽ þ’U%ëÎYñj&—ã.óGpDÑ‘MµÁ—OÂ`å|w^"ï ^ð†£–ÅäÐ@øÕ0;=õ6ˆ £ð"9ªf¨ñ½CøK"¶§ãú¢.¬{»¬¨ÀŠ^ÇCt]nÑc 'Ä‹Ò/è;΄Ý4åï­åÞ‹ì ëÔ)³À;˜ï,I÷µƒC´,<ôê…üo.ôçåøxË Î¾ë/ñ.¹Åj†úñúf-–*<™ºímÂGÇ+¸Ý ëÈk±»ðÞaí.l垉d≃£ÿáÿŸ+‘0xZÝýÙ¢’scSI,î«Ó*IW¦ëS³“áæœåðKלYÕ6BÉ×/¤Ôd%$øu“WIÚlEz"ÿˆÿ5¾ê;#™K·ì|€Ó®¦·jDAÂï.)ü—v¡Àü¾ÀL‰¹l‡¶'I%ƒÄLÅ׈ìc†â¶pnùQLó4@[3Z r {5>r—ÕË¡lîRb ^ÊÚlõ1i¿1Žûk…÷4¹á±<|¶íÏï׉õo‚î½eø.A–éûÌÀ_WnÁë8Mâ>@>^*DÕicé‰Ï&,Ùw7ž®ŠùçÿY·êqAK%ÎWíçµ,I¤*?ŠñfÙ+®FÙ~ŸY ã§ôÀ¬ú1Œ»]E6¬ ¡Ãc$˜Ö‚ ÷ל,=vØv©¹áãÛd—e6ê!þ™ÁOQýKtÇæ3µ§±øùK*ê@ŽÊZ1§:Ó}“Mï¯_ëߺ ¯×¨P»-DQ6x7ZˆÌ‚ª?)÷`Æ éR:sö´Âw¸ë^9ù0E…~¯§÷5*IÆ1à ñõP/²Ãýò™ØtP#WÿDçÜœìÒDزlÒ %Èù<~ŸÎC“}ü?ü¶àb›ëÒºLÔq yÔ˜Î.úLÊ´t°ñï\Xrá:ž‘}¼ŒÙËý€»z~£êç”\r.Z‹±ªñØÅ%|ò6¢«”Çÿ¨ƒsß^XÝ.*'ðøt?ÎOþ8˜zr[\¦â‡üql ìs½<š}ˆäîwãcA¼ÜðfN!wRYZM¹þÇSɧœm,î«n Y Ç«HëÜý&š†ta+óåO\(ÉÔת øšX$L³={¡vÝc<û¸ó%%(Xi… —ò·—·À·Ò$ÞÁ#šLüqRçKÿø_Á2y,Ûvš¿n6#ãS˜ôâ:N3ÂöêùÑß[ÈøKÇÐî¸ ‹µKãíiŒ'í«º|)§©ÌC=>©SÅYMÇaÝÂÅìFk vXþ5ît‹b:©­Ü…ƒ¶%˜÷ú rƒ–q0*•Ô#}=ŠŽðêò.qzóÆr¦:•gœ»bvsýÇÙ窫<û)Ûibz y×Éû¯GtQ@ Ͻ}]Ù3mg‹QQóTy­Mç½ýKj×ãM;£'žcIJ/à¿Ôcað<Ö…©‚¶Ìø¯ouÈú2ÜQÆ Ýý_ÿ7ó Œ_¦’W›Ž!í4¤ÅËÞâ„5y´Å+ÅÇ™ÑÁ¯”Þø´ Þ(ôß·™Ì4%Œ©Mf¤Tê=OTè,'!brD— *ÝC…¨QìëëznGm/üÔ«Fý@TWê0“Å)è9V‚=Ñ8L kôh¶½<};0t–»rÞ¯W]ÄŒæ¯èo‹ BðhßböÔz" O™F}-ƒ™MÃ/4iÏC+¶lš%êÇkQýYàþ¸ Ð' ]¢U):žææ×q;͘Y\ýòô>4XÒJ¯ب» þÄ$£ÃªÿâŸÜ¨Xè¼ÑË5O6au¯´qP)ö¿ÞÏhšö=l†@z½}øð86 _€ âÜŽ13ðC€#q™µ˜‹ë™Œ*»8‰}Ô‡=§'äXPV"™™yÖ—‹ÐŒI/¸ØÛqlWújúǘý­¿@ŽHK°*ûåô¢_"}s,”¹Ní·Aªl£H ¬ A§ZÑ.d5Vˆ”qów±â/J.ùÓç3ÝmS˜áÅ•˜ø¡a~F:ŠacÏL\vr‚þp!'›`¡€ëh¡¸Äû¬ëf³ýðÑïëÿ¼…±ãÁìàbJž6; Ê]!JÚÏüÃ?E;¸Ù¿’|¦YYÒsjp§‹h@8ÙÒŽ>ª`¾ø è­h ñ_ãqM×tzÔ_¹9S)ã‡øùÃ}ˆvqa’ç|1~¦M0ŸI7Ý™ŸWjÃó•j¬zc óîY Ÿìœéü¤çœÀÝTDT‡yÈN£^VæLõìbºNhM²ÿËŽºÒÕꙞÏ^ªÐ¤bûbé£iÙíÍ&0Ó÷¼‹¦¶Å wÏj¯UcK§ò Åc½ÞÎaiVÓ„¥ât@ðò¬Æ1±ñìЊÐüМî<¸’¦u7@ø»X¶UMïŸþo>À“x-Nóm*p^²2õqãýQ¦µRÕ¬ZÓŽÚŒuŰ"Õõfÿb̾Ð…:[Hw²­ÒŒ=9™MK7 ãÏ>bšgb]÷/Š¡ îä²cî—úÿ¯¾y‚ÿC˜åö{½+‰‹ˆ° s—‹ *߹ǯ€ *ÛSŸ’t½—Eß¹«³SÓ„Ø(ÇLÅLöÇܘ·<…±íI°¼Ð€ÅI䀨õÜ4þ¸çÅP»K¦4Üÿ9^˜³ŠS/È”­ ù̉KÈ_ÏÚl\Ù‚î6Lùr½CÔxî ìË=‡Ý%±t‰²œ›ãÞðò‹Ì=‚’¢b´¦NK§”–:¸g˃nM4ëªáè}Ådh¯Ì©¦û3+ÐÔ—‘î0Bõ‚ßq˜)ÌnF‚¥6¥xE9†K!y´ºkÌæbõµ7 f>;«àHÊ\¦m¯—»W “‚o’‰Å"Ô]c?Þ|„ü½ƒüº+¹IÁžõíýÁÓ÷ðíßÀÕ 45<×™à„þÃVüU6…øõè¨Î„޽]œZÁ\|wo/ ›Á9ej4zX÷§Á¥{W0&¼”ŒP4fîKXáÙgð{Ùh u›á‘®$¼Sý'ÿåÓ¯øóEتjiL^ܺWŽ‘Ñ5«Yå“xî×ô£d÷[š {ZvÊÑð»3ñ–AqÑN¢à¥þï¸uû÷ÁO7 4‹,…¿MÜÕc}àŸzå<3H˸Ř®«L?Äë@øëÅõ!ñ§PéÓa˜2ø”bÿb=x£ÑÇü¥¹vU-ºy tЄÈë \ß±&ܲEïïžXC™^Ë'rå™8ÝrÚŒÍÈ:@¥gþóÿº’nì±@h×úƒi¦þ`"t Ÿî2çß=¸y 4`R:mV9¯)ȋܗbÙžŽìRf¥ù¹ïÃäH„/^‹-Óïãô©ñ8ù¾8Ëøsž¹ÄÐàO?ðÁâqlEÒ4xfNÙÖƒ#ÜùÏ#Œ<+BCºô¨ I€Èž‡|Õ/=Ðÿf1æhàJšaLb­oµáÑýf®¢h.÷=q=æšG‰±(me¬ Í_‘ª¸ ³”òNRD‹'Ü…ÿj¦EþˆS¡m{hþ˽Ô}’7ýñ÷ d§§àé¤ÿF"j-L¿ üÓ¿gú9¨îû ;ŒiÊ‚¶ðÐMóä7QÏoO2™âŒZ.dc¬«õ°û1Xñ‹ÅÜOH<:‡·*÷< ºy2Œ~‰NF•|µ}§pRòg2¯W[¡W‡R…‚´öb:®\u òÏßDásÝð¸N†VÿÀ«÷nsþñý°¾Û€åŒàõaEaú½ç/î÷…2´³;›¤Í|ƒ–G‘üV™Áz—ÈÒ»Wˆãc<£,M{DNÒ#ncØÂ¤B\£¶טÅcÑ>ª|Oâ>›¡Ó×(œ2Éå[FÓ·ýØ~Y N-öÀ̽ÿòŸŸ*øÙ¿r°utK3݉“r‡Ì4àXÙF¸öÌ ûÑheú½ ¡‡@’^»¶­~cU6ÄìÄ%¾ü»BIÌb¸š<UƒÅ¥$bU ¦Z9s'ò-…'±»òû@¿s 6Z…ç£YØÔ ìF#"wPùÑE¼n 6É,Ÿ FtÃùt¨ê0¡ßkΔ#µÌ„òÅRÉïúxpTRb-_Eÿ»>öìI ¯J<¸±Å›IåÞTŠÍØõÀ‰…ï‰!*Ý×Ðj“½©éƺƯ`ÅwõÙš«3@;Ì04‡©%ª¢ä¯žòç?ÝJô “x®¹ùwªv²æÎ(`±ø¯&xù¦9ó8gE/dÜD~ª:Ñ>0+ï™ÑˉËàKà zñÉ+¾ æÚòA¥z7–x\¯GYWá8Vý¶ÊuIñ®ð¬ Û¡¼ŠB¡CýÔשh¯ð Û›á,{B/|~×ŸÞÆ!¥ïx²Vƒe^°É¿à+—ü-êŽwFó0ŒÓ†ßËQü¦;¾*F±;SiFÖ"Ò¾ŒŽ»°×° Šj!%sûvj0… cøüò,âo9hŒÀN!|PÀý|³«ÜTØÛ$íüo÷R%ôymËmrÙ>Â%Ç×w—‡8ÄØÙñÌÿy{ýΆ~é*æy£u¬NË|Ã;5*e¿Ç’ÁN1¶)t3õÖ"¡§³éœã3èåGW@Ðwž¥€Ö€)”õ<ç$ÄUðÖˆá]»xž{Ýl þåA~šxîé γ„Ô€+œñ <š4‘>ن歆TQå ùq3™ÊLÞB·¦Ü ;ý`NétúIUùK¸¢ãg¡4ˇ×Ï\ç£ýÞ.ÒǪQ_y'¦šÓWç¦Ánß4;{÷âënßK¯Ï—û§©h?xOðï•@ZÛîNWjgƒÎa}¼îzçŒ2æ¾uyPkïxÉp<ä5§&ÝE´'Ï­WʉDËÅä¡b=8ºK¾hCÖl#&ÿ!ަΉ‰Óö·Yøzß8¬ú Íë`PYŒnxuÁÅî©1Tê×%¾‡¹øeû±(ô¹‰9娤˜•»‘âQm`¿kí,t#pš¸ºN­Ô*\ ƒ{µÁÈ9®è€ÐêPˆç½†_im0·î>,ªg“ûp5oeõ܉1TW[ž šEu—hÒ†øùtjgGÝÿ—ßÇr?wÝr2nÉÏÝRXÖË^oªa9«_⬠tÆ$=5ÏŸFÄ€ê|)š?‚ßécïÃ)¼¥'‡¹6±±ô›K83©Ïÿjý×`yi+U8·M<-NFQÇÈ“ìuòXlr¼q×t¨ÙÄ8l]} ÒŸ©3sU†ÑÃsëoúãàüçøèÆGœ¾\}‰g>ÈÒ—÷ ©œs4š³à¥{Ðr ÛÆOµžlÍyCØÂëCÓŽÖÿj¨ëÜþè ëK_¶±ö \¹0„3û1Û†*øf>„Ô—šd<èË£M›ãÿÙÿ$½…è9½Œg ‘Ë¿l¡pÅx*hfŠ‘›ç ìýËÓbϽFÓ­™éäñÕœ"fþ_M2¢•%þž~› øè²ñ¬;  ›Îc^ÁºÜ±¼Ë@¾·aM䦛¡ÊdO¦JPV{)Üù´ŒfÙŽar¯Ö@xΞ´‚óG”µú#l~kÃ4Åõaqª Ý?M›–¼HƒY§b}súÝ×C‘‰uØ5OÚŽfI¸7ׇ¹oý×ÃÇâÐ[*², uˆ7ÝôGÆÒçR`3`Æ_ñÆù™ô%ߟšw ·T.ûgÿ¼ì°‡ûp´õþW#¬pNŽ9ìêï ·è(›JŽ.w¢·¯ïÁºå†ìʲqÐ.wˆæ½ÜÏ{ä4®½ÛÌ|]^Â*¥× ™ÂŒÉôËXi¶"w Óh`Õ+NÔ_ú°šíÐ@.§/ {3A°]šÿvå6÷ª›7“eÜÞРµÉ´¹¹Ÿÿý‹¸ü6”æ%y/åQrƒ¤·Š±ùøpºh]ÔåÖiy³i#cñšYÙÈ**RÁF˘¶Oì ›€æ¹•¬jÔJæ’#ø0Õ¨oÏ8Ǿ„1q;Ù*íGÜØ¤„üç­w]rHŽ èP­¼dií MW¥ûÆ* ^J‡þƒŒS?ÎU ÇÑý³DÙˈúFv?ã"*홎Š#1[dÆ"Æ+âcå[Mý¸n/ªj¡.h”‰`6ñ  øÖÏ‹ãT½´Pò£2Z;›¬4ŽêD…°»—K¹sëòp8@„ö¼™A­_€²·8Œ§¬Øë<ÿñ6šØ0f"1™÷)êý´»w}©ÆM¢K¨—7Õu6eÁ3)¿­-'رMÒÏXÛ<Ñ+“®¨2‹f¡ŠÍ† øu§#-,C¿\;FT“&þÛÿëq*¶‚:¨ËãXÏHŽ4>/.m-ÅÁ—ËhÁ"ax7]Ÿ5I5òϨ‰°´¾DVyá>|û¸•–ÛiÑ•V¹ñI‰÷;ýúÐ × ¥á9ü÷ZæYšÎ‰«E|w«Ü8È…oR«vÝ8„³Û(ŸêÒÏj_`»Ä(·ËÁ?—ÃhÒ,CöCè4ÿŒ½U–÷f»/‹À&ˆYãÝÖe$uŠ>[<‹V'@…í.ªs:‡Ë“;IÚk“™¤ÇSñwð±ê"í¯ÍƒÏãAÒ^z½³$Öl°¯@NN9úÿUJ¯…ûÖGê·Ø+3¡„=ô© dßúq—MYúާ¥³“[l ìð9˜]Š¡MKH{@ºu÷ð~xJÒ)VªìÃfgP  ýgcÇäC\ù–Fœø,–V®ßÌM{2ö±­Qé¸Ld*ι% J¿qUÛX\ôèøO›D~8±EK€5D=ç˽òÃ2…ƒï;Qwos˜«YÃ~5[q-œ:Û4íà’Y4yG4¶U5é 6ëÖ°ÿîÀxõ¡üÍþ‡þ…Ø$=§6À7«p<‘wØ…38m†8Ó=¿ˆÓJE‡$çþ“¿Ó}oã‘Ë0££mfëä^á}Ç Zµ/…i†³ëÎö˜æ¯Ç½þkÄÆÊQS_y:£VŽj'±÷,ní&µšl§X1ÝmÄaÚîǰITD “sb×aÅi´ó9³ L€â ÐáüaÊi¼‚%'ج¸¿ØYr‰<êS'“vкˆIL@×*Ä©0O¡íйe'²5§òý¶›X1 gýó¹„õÆLêçôúšË•üJ“-lHþàk% _s4Aãæ- Â\/jÚÈ£šoßÃt+5f´z ³J?Ë9 gÛ³ý“Ê/]" ì:IDº2“~$ÊJ2m@M$‡“ŽAÄT}¶ñÕj:ý¼6O=€¯K4àþÈ h dÑÑú íé'Ló’Fð‹§›Œ˜éØ…ÌÊ*—~>ëC¿ú߃¾Ã~lZ÷x\÷[ 6­7ÆŽû‡ÿ‡ wÀÒ†é¸üİ©r¢Fé`ïtêÿW _\Ž`‘P‚+”~À:n4.8íÆ­ô•c5Íø'•ÒO9¹µ«Ù»C<ψ»xö]ki8.«8Ò®Xž‚tmV1jÉÙÐÏÖâƒXg:sq½Üé‡}GqõavHð"×uû:9ø»²ëWi+âmŸýxwïìl äí¾«O[˜KZ“Dµú(;ðVuFûv°rC/žOÇ< ú6+€fZWqãD¯@Éù;àÚ|–ó1)† OÆ`ßœ0P)ö_ßg"³ G%'ÿÃÿ±?BŒµ…¦]7ñD—3œõ%fÊ“kz»±±F–ÉýàÍÞ ÖÄ|¸òbÎ\äŠz²™djB?Fsø%Í™ŒOs#㘣6'>8•nH6bQu¨pl(+r2³/ÿp5{8CÓˆckJa½é¨IÞÆí‹Ž³¯!ð!V ß&ë°­¯‘í·…®y†žne×KSýŒB|îŽãÖÝç‡<&;h ¬¹µÛ5ÌÆÞNMŸû­„üÍÒœÜO?|úûÄm4ÁØ2AXg’…ÉÞ…ÐÜ0™¹æÏ¦²ïöC[³>ü·ÿ¿mÝþú¥GÍðçÛ,¨ð`šò²Æâ½·9ßëŠô`Ó¸q 5×Xñ›L ¡×t˜ªô)<ýd1Ü>"M}æx`ûü¸jÌ>ý5ч0ÛQ%ÂËêísI}(ò‘eÅÛÆ±Ò4i;D%lDŸ*'€(%<úu¯‹·lß_×¹´WdŸŽ]²æ;îWÌAÿ…óØöQÚô½¸7ÛqÜ÷·½†X½. u—ó눷"[&hC¨¶"•_p ÖL4Ã-r“ ûćõŽC ôÄž F\¶o<—T>‡fÝ÷S0ty Ê.œúÏÿÇvcߦD.¢ḏºH–^­ÁƒÎWalé!þÜŠÕx«E„¾™ð{àì¦!ø<)ŠåÝH[ÍGÑO¿.Áj=–¯Hë–ê2Á5ê´`R;†Tª°‰=£©ê^ Ì29€Ë'õ£Ép ü1¡‰=~ô€ÂE,÷_ —‚¯çG±ìÌwÜdµE¨e1†Yv䃎¯+¬lô¿|Cöõá%ô7¢*»ÖC¤_É‹ Äi¡Ûéá­!tÒ}¨yJ€˜´æW²tTÁGüu¸•¸„ÁÆYÌ0á-N·¬EÅiªì‡O#ÿð[«+Çâ#ÿüßÓo<áüûÑÇÈŸ•6ýå™gÁ®›Èй­/Qÿm3¾H”€…»kØõMìÎp%÷©e«¼áD'¯‡z­laÖtw»pgV—¡È=ªYîG·ŸÜŠs›ýÙKý4¶cÑFjø¨ Ó¿ÒM=Õð­_–åZ³Ö Wªqö|m+Åñv¤ºj<þø6š /¼H÷„s óÁnƒ.Ní-‡_ó”¨m—‹÷Ú˦gDӼݿ þÛŒöÀ {e™œ{ëÕšH T3Ù³,XïlÂòYuô–£–LêægÓ¾íshZéÿú?¯4Ï §ÍÙØU±ì fïT¨…o-,Æ7†’ô‘£ë‹àaÞ“ ̬]…¾:ð¿woã,u=àg|(õÉ: ž›MQo´Ó±s¢™Ëô©–è úe~J©jQ!íYÔ®ºó+Á6³nzm’uj?vJgQÆ0/mqø˜t¯†ÉE;2²à¢@2‰õ ÅE™8¸ÕÕ‚cأǮ̾ϞI½Ö¡É I“¤¥qæ¦绚_ ·ØÃúæ´ûùqüý{;™™ÉÆ$²é+déª~ Ñt¶äq¸V»å_þÓ/›Â-i­&nNv¤½=Åk*Ò#·b8MC%,hïà¹f ;ÏàŸ.IZËw”7§Z¥æ4pàש¡çwCП äI’¾m³IÙx{ÔFUzMî5'ð[6­€¦Æ¬òäZÚ3Y†å\cÍ‹˜p^,dìa–‘nôƒèd62‡ôKÚB}'ø·¨Ãò·ŠdÍ8}æÎó¦bºªÎë˜76¾H†Š1ÊLøT8¸¬iÇ÷[ “‘Èo¼û‹—æã“CÄô¢4´õ{Qr°÷4|1{sÈ‘\ï!Ô…ÖlЦVi5X,¿ 6é§¶&ô«¦sÑü‰GšUÑË샜ê8\9Sƒ?›5Á éÁlX-jDµ{{Ç&r×@ñ© x\žÁJ7•ÃôI›ØÔŸ3ÉøwQœ˜És¼Ûá@ÝÏÄrÑ¿…1pl]ý;©:-½¸û>Q{pªeÚ»í±«I ?P²S+ )³]‰ðsá3Ü/¦c] -R‚«Žán:æóûBìi‡Èeü|"‰]|ÓsžZRá1g°^_OÄs9¡¸…_·Â±¤Ê{\ÿ¨ ^ꯦNß®pF¯ä™â³tºÉ\˜~NØG"ê‚xTDp›º‰/ÇŒ†ŽaMšµé®ÝÕ‚…cõiÚ_­¿qG• ë ÿï&®ä^ ¹@‹ÇSâÝèʹçyë—Z1Ùª"X쾺ÿö‚µ‚) Ç÷¥ÅðW`fçtº–~ÊEòvÆ/Ëæz´î¡¿«8ýä]Krª9m§ZPZsw?Ú tÝ„ê»z Ÿ…¡uòp¨iÏ)üŒŽSân޲@Ý¢Ëè¶Ùž´[™Òö¦ÝðôûlÎ窛S•31$h"niôà²Æª’-g7CP»Â®@EÚ'2?8]ERÿé~øë#)ì˜þŸsRìý²¦+3'fKÀÇÙ=ï/éÒÄof_ð³F|5îú}îîí`Z5ÁêôR4”´"C)é õçÑá¾[vŽÜó~#«z±ÉI!ÊŒÙ?;炤™Û_{°ÏM†ß…9äÒGOva«íÔÃ|Žå¡2tïÔLPÈÄþ–äoV*ÖÌS`r»>áéÓÓIFéX"ó‰;h±½¬¥þëÅÌŸ©þß]àC¿<’UqêÌÏi´]Œš#ñ;E\Pì‡Å¿’8͘+Xõ`YqU} 4Fk}6²>úàÝuðµ•më'nÄÒE8 r·˜°•§ðSštn—¥‰ìj:ÃM ½WÕ ¿å**µóxgâþoçÐ~`ö}¯áþ;ùqšÞl‹ã™œøÈv½_†­Þ´÷-rS:…-{)ÆÖæ}#¿Ž¥Ñ}ËŠp×v vRpõйÎÁß@|ð'‡.¸¥ÉTô¾ãºV[vþÆ <`ß„Øä>ªáwTß§s©¾Ü@þYÔ8ú÷MSbC‘¸OYŠ•ÏHÀ¥;7P]±£¸`ì9VX‘NçØê£—€L–@Ö-ʤø°`„;ú¨1£ºTcí^Ú´M“JHâû±û`é÷ë0|ü{”O_ŸüêºÀuÄ/¦fQ=#°ôúI䄘À aÎ…=€}3PÝ­gÐåþzÐÝjJ7^‹5E—áÇ@&·‡ãÛmÍìÙů\ðgw8üª>®ãÑ=žåìÓÅíÔíÍcX>À@ø '§³÷‡/¢¡Ï¶åù ƒvltµ.ÓØœÎª$üñâ<}0Uˆ†{‘ÇÙγTeS;z…&÷—C°ús6»;§¿ÍÍcÎ[\_´ž Ãx%} Ë*2ÁâSèÝNûVOdñ¦²“ƒl’O‰ëü^l¢Ú7Ü14=½žÅ²­]ëÚõžBÔŽLÖ±)އ+·iÝ–kð~ïN0{ñ™ž‚®+a´ˆ7N›îLǯñ} º]6îùuÝ•ò8»?çáø¨i˜ôë.i¾ÂË34Ç•>ôLÆ#´«5WGÖœÃïA¨°fìƒw/‘S'yÔë£ë cm»ù»cŸã“¹åh¶%Õ}às_Üfüº|Ù«‡S©Ø«¤Ó ƒodÁüãò8*v; Þë-Iæ’yÆâ0p£Úaïi*[*IyÇS!ßpN(‚M?ËqÂNÖx¿_©e’çg,ÙŸjTÏï×lߌígàï1˜>e1ž²ÉäJ_DÏH™¶—Ì?MÝÏæÉc†¶´øè,œÜÏ&¬Ë‘ÒD˜¡ÅÂ'Ú±“O›qr‰Ð•Ù“ÂX4ߘNñ4ý½sy+ftÐâfôkX+ó*ºìg_Å·]yUWmf%f°¼âHº½·óÐD6ñF=sˆ¤X2lÏ8³”np ¥kú*਑'MžêÏý>7á¼Ù…Ú…£±É@—ú.š SOAô•dZåÕƒ Z–rÛÌb¹—t艀½ìé º"á1?Ù? ޳ŸKòµ=§6Ì÷Ú×ìi’m,ÈßC·q;˜ÙîTØ9CžîxeŠ'ŒXjê"zuT$KÝ{ ;ç‘¥-PGVÒ£/'¡l] ”Ç`þÎíìQøVöå³%Ô&%¯8Ejãê†÷ï?C‡• V{‹TÉM“Ë=èvø /ÄÞçz´à€„Œøú¯Œ,zÕÁ–zÝ7¥¡û·Ñƒµ›aãáy¸¬`4wLM”ežŸÄ ¸•ï›Oýjç2‘!pEïÙ7V‘u|²ÂMg!µKÏ´&m,õeà„†1´7å-Ùýä Ñ•þß_/’/`^ÕaT-±¦áÝlþo–”rƒL¹bßÝßABÌ1NEm"KÏ?ÕYaŠÞ쌬¢¾ÉuÄ>Ñ‹[g¼‘“xkIåzà¤ß1ÞЉ©¦æ@§xx°ù:@® —;_«CúîÄf›3»ß5ƒJ(gª@f—?È2…l 1ÎkCn˜Y ƽ)pRQŠ^¼õKÍÔAË;?øç;Æieö:ÑÙ|óc;²:ÐîÙ üÙ§«!û°²#þæñ0§ QuTö£÷CÊ`å† 4Ûņ®Û œƒµ4^…Ùž{@õ”+˜ ´ßq/uÔ³¤ßÖò¨£ÉAjÑZŒ_:'¢`Ìތ¾{€}÷rsÜ|šO¸BæÿQ¡Û=ûa‚wYÃC5ev/Ý{ì'µ«¹_WsQ£MŠÆ Ï‚RG3ºfš5Ö¸ ³w qwï—ÃßãNló›T2¦BÏîbÌÖP•ý¼üjE­~»%š p ¾ ÛsmS^¡Á®å… “,Y‘aƒ|ÏäÇ®óôÛ níVï#…éWc’þG¨Z¬Ìæ‡á=ý¯ìBmê}i>}_©Nk:½èÃUiÐ(Q e*,0â.·bK Y¼sÐÀäYç+"õüŒkïFS¸îz×_t†•Rk©ó¶S#kz˜l…³Ïp´NWÂ…}/†=Ç+ð|ØSNÃ- ãËñì|øpµ2Uî€ù¥7®A3 ·h3n‹æ­<„LÌÙšHo»Í5i¤;o}>Â¥*ðS’0K\Ëm¥<ð7ZE3~û¡ªÐ_Øia€—xªÿé6ÿÐ e&þî8~G2n§ókü ¿OŠÀ„´`vôÏx|}X‹åämcÛ/(p¼Tj^ò×%ãÚ%Õø©[•˜Mé†Wrà9O‚-“:Á-½…f 0½ÆÛsP%îvDi±\…ñ8sŠ<úa?«ylO~ ÜþòÌ÷Æ…úcؼüðù»†Kˬå,E%Ñ@í9”uêÐà#‹ ý@•kÔŠØe=‰^ºÚ€w⥱ìŠßÅ–áDVì^óÕCô©pêTìr¢!¾0ªê79­¯Dÿ ?°O«‹oòm/ŒááÜþz§¼ l0ÂßY#iÍM€æ¨k6½\Äj?ަwsm ÿ¤/|ñúÛ–e²Í§eADJZ¿[Bר¯ëop’[¢¦Ð$¶Áí HÑux-©‰„Ä$²}Ž3`Ã<[–àªCkW¦ñ/JéAíJ3üê•aMÞt± Ûèv3d¡Ÿd;‘ìÞŽïyÛ™Qû!h‹(â-Y¤Ìwäuî—|-]F[qÉÍ {¶‹wÕÐ_._–©¦Äò±=z¿x†?¯KR£Uâx·?ž‹¬Ŷ+#9|ÎÈ·Ã3æ§!ªä±þ©‰sOqùטý0› þ^ KM1©9y{¬¨ñÏ|ÿ¦Þ¸‰ðùi¸§Ð“3çîÂX™‡p,MŸVk' O·²©'‹Qó÷GôÚò—.C»Ð=Ĭ;† J1B•Öøü½FÝ—IûÈËz¬¼¿ë¹³ŒS”J&%± úk3[ê‹«ªÆ2ëæÐ)ZÀÿ];Šu6ÍCƒ­EXøö¯/ª—•&FËÛð¼K|ñÏ&K®;áº9!œçÖݸüÄq‘…\ÝÃOù€FÍ Ø:0›VÚH‚žíf câŸf%öLEEN»¢»ÇiàÞÆ[wìVe¦¼f•¯›CkJÞå…¡ Ù.C'ÞõèA¨á~ õ t”énºO_"Âðg +Ø7œÅš‹hE¯ ;b'KÞÞ´Á½K†¡&H„ÅY À¢÷K!Ÿ í`"žÓéåv¿Gʲx Vpå[øD„uÝMðh¿î}ðšÌÅbÛHr:¢”&[Ó%r58®,ž¸*>Ç7Í}6˜nkmÐó†M^ËzǶãìçµ³ ×ml&Ës“HWÇ8æ´:ï¦ÿÄO¸Ù4ÌÚ˜†äÊ<ðåÄÎw ™cô.H„ó¯ ›í§GƉǯ«àa¯/ó^ %1"¬¥ªö0nÀŒnÍS¦,­š .à¾OžX¥|˜-ºs*Z°ëII$êt®·¿ÀM7–R%*·Ç|æÖÃT•ð1ïÃ*x"΋W±U ;’zÀùŽ ÷Dç~»3WNb×2>ñå%8-6—N‹Q`–3'ƒµc¡ØÛû?À"ëwøÒ-|;Þ~žs»µÒ¨}èuÏçÜFf]Ä#>T£5“z÷”›t÷È)sÃ/­Ù£AÈ7Ê m Ãï{#˜ã»½ðmÈ›­~· bûmáj»gª® ò&oÑ@â§¥•ĥͿEãdè,W9ø¹ê éVdú/÷@¶j ñ§ÅÆÀA!UvïFNÛ!Ë©°ß>ðþšæ9Â/qK«0iT3DÑ ꛸gOEè³-£à]—mø{íVÅðŽTEÃÈ|ÞT‰éDã~#(•ʃÉ(5ªHF¥$Ãàƒ\u·”;úÐ ¼?]C—Œ{de‚.ðL‚%òf˜ÓíÀó~ÔNÆî…Ruwè?dGòÓ™ OÂä^Òb ‚´µ]~½Ð&rÇãaû±ËÄr$÷0PŒ$†òàüa}òÊ<y1ãñÒ>'H’⢂ÙÃßðlwv¬WKÇDhžæ &¡}f9 ž¾j»ö¡½Í<ú3…S/–û­H߃ªrè¾ëêôø-Uìš9™:®;¼ÖnØÓÊ…ßW|›‡îoAæ£b^ê»WÄBE˜©˜+³Y£ ¨Ÿª==¼@ ¿(‹³Øõ«ðq`åS}@Ë_€û•6ˆƒ÷_`ïA úÔr•;`:neXzS²3.CÒ”Ü^鯄öÕBÍP9øûž¹wçXµpŽöù…ïrƒÙóµ£X­­öÚÌݰÅwÕ‘û• h±‚'S¼ ÅYX·3E˜mÿK® þ6ƾlru=QÁ_žÜ én`–ÀE0¸±šë±¥óÄuqy€='/5 âLbîlØVc‰Š­’ãº<Àg…ö¢Ks!œ·Óç^ˆI¢[émx*<Š6¸\ç´›þÂ]Ñßüºê,?ÌÝZÕµ<š¬ÐÊ…çÛÄXáÉø%p¼¶²çÆÍ˜I† Ú±Õè4Ñ”ŽÇÛãv‘««çЧ·6AÅå`Ž7_ÖZçø·Åiö7 ÜXwŽ[êýÆüÅ=•$º1K±^F”ž|“ Eƒ²µ¡,q:¬|Þm׳3 âôê©4¶Qá;wûÛZì6ÚOÉ$n~ 2Óóñ´“>[­|¬.‡ˆ·õuë´iÝ KTj:M~¾•ÃÍ%:t~l¨òW’ö4M¶Ó+Œ%NÖ‡[TššžÀÿŠ{» Mn?ÁÑŸáJw†ÕÁÁ¥÷±rLh/¦QGqéÇytƒ~™cÒWW?æ‡.úD¤Ëj`åÑÇh«5@<¥bqáâÏœ—ƪ4«A›h;d?vƒU1§qtiºóßñ&DÇ”ù!ö¡¿ˆ5>~ˆ»œÖà—cy¦DxÚ;×Ò¿1º¼·gßqzÏȉéL|ù.¾ÈñIDöj\9t™”íTd“Œ¶•û 套ñZB.¬\[ƒQӣ»çåcK‹}`®ÍN¾Ë!I3ì!{j¶ ª³¥“d[§&Ö‹ycsË2P›¶kÍŠˆÐ1,ሌ”.{xb xËœÇÔÌ>®3è OS~!º}šݳ<¡wÝrÒוKÖ‚yÈÍdc˜ÒæSÆì°ëT¬_pŽHÖÃQkwê߯r6,…Í÷€HmLÀ7o qü%EÎæ|!$(‡q%ÖãÉ3É*œ8a ýð*„ý»rš¸Ó®ûÀé:¼+›'5ëÈ¢ËwQ£¥–7WT›Ý´b‘=‚´rÆZÙ,gž\Àîª'°.HŒu5ÙóΨf¯cصҞ² æ2—vSg  Ó»„g:eXúá]ýdûªœg;‰‰…„ÃØ5‚T£|=~Þ|ø /ºyî…W¡Ñà-VNéâ=i?‰ú»Uqô÷›è¼:ˆ]{™ÄS•¾ƒM *tò8”˜Κ#Ȥ;wÈ5­ÑìÂcº|{>>¾õ ¨ÑCì½Ò Çäq}¾ S®]ÆÖ¶àþ»{ñ¡ùOŒì.ÄY•ù8zž ‹Z%L][£05{r-j,¢òK=lz⯨RÞt,=/Ë8'ªCÓØ¯Æ4úfv6[â¨HûHÒ@ù”[¹—?Máig¸À®Èpœ06 ¥WU_t~uú“¬íq“€Ó0ø^åŒ0¼P|¨…>BÔûV†¿Ñci7Ê  8Ž•×ïe?“´˜Mù5Pä Ö‹)ôÙXÜþp.ä©~äž&yÂw¾[ìµ´Ö´ðNB6¶¿-ƒßÏÜq¸GÌ® ™÷!Ëô‰¾˜K %Dèu=ó ]ó¢XÀTm¸ýw-®Ò·¦}zó1eV+ìNÁ*£z¬Pu²ˆKÀ0¼‹šåjxÏ“ u?ƒÁo“y²/Åé ÆºÙ¥›ìyâ×j¶Á÷Òû01 U óñ¸þ Amœìéô÷)>¾¬<m“¸÷Æ1q'$á < ÝY`í?–-=»Ú:0©`\häHŸíþ„¡f4ßx.ý|29ý$wKúO\*Çá´mçyŠî–” ºÉÅÜüQgU µ‰;è‘v1vnh*ë½ùR%†ðHq3OfXbd¼¡-Qƒ[¡Ö&b+¹×æ®ØpÄœFøGbŠÁU.òÅh›x'}½ÅNz š¯D˜Äa–,ìHÈXÑÔ¶ö§Â…ÜÉ”Ï`5ÇôÆr¹ë8Xv®¦.€ê¬sÜ’)œõ6ÒõEõxúÑ÷´ª½Æ ‡TÀ£}$wÜdD÷.„³ŸBðsYÎYÉ`¶ï<˜(hËõ´|À…:ºÔ£HÌ¿Ç³Ó­ÒØ²n: Ø_Ÿ¢JÀ¾zG7 4ŠXƒž†LõE&œ™»XhêÂõü#ìïxcN¥¹Ô_s+NT±·Ý·Ùø¤t:…ˆÑë7wãF« øðƒÕ¦eÇÖcDÙdúWÒ„ˆM‡gáèŠT_ò„ÇA"׉ŸØcÜð*¸Ug2Kê kAïj|Âu‘SàÑæ ¶¶x€d©½Ã•"tfëu~èuQ¼u>Ÿ}¿‘D?¶¦Š Ør^ÿ˜ÒQ:c£Û4Áž¦n9ÌY´ápŒ.wUowHˆe©-'mÁiðlÚYr]Œgþrù qd½0rÌ[“ mÁÒœjâ{8áñ›Ë–»„>A.¨ýÛ’z–kÑk{àõñcÜËç ØêL=Ì™µ“¿븊GúLJò3Ïék´ù¤ãó'w!,à/ÎV…gÉ©¯±d¦!ñß I§È%³Ä¦üä·æK,®h¨ÆµÁ=¸Î…G#¦¿„[ßéø1ö`ÖËÓ†s‚ßAb|6òW£è>]ê&jÀ7æ–]—GåOлÙ—ÙF#÷|ȾgDÇRGu#zàr*ŒNH€ã¿]ùL×·jç-bÙ¤µC忥ãìñ¥ w ‹cTiÓMYúc¥ÔDE€b%÷÷+‘­nqÿ·î!ãan6/ŸLÙ‡­@¬\æ Me7ÆÀÊUÿƒëŽËéÿâ•öÞ[¥ÒÒ–ÖsÏ!‰”"4‘Ñ@{*¥Ò.£¢B!-õÜóIf‹dd„BFv"#¿¾¿¿žç®sÎ}îçœ÷û}_ç9µ¸%a ‰´9Êð,‡kMVW•a<ô¢ò_¿A«Ÿ“ùè/»©u?º9•bòT¶ ê4”]Ф„/ŠÕ›ûõ;æF$®óЯSüñoe:_'x³u>fzâs°|-Jv·.&îA3p~Ó Ü âÈî^ Mõqô”œy”2Lq–MQ£ÿóse2³x¯2Ù¿kŒާŸá)|ÞQ‚CÚ¾“~K!aîqÈùàh7þž=/þdÊJ¡Ü'ƒÞ{ÆåÖ¬I¥¬ Üø—À  ÕyÌ.ÀK–à†± |1KB”—`×öp¾Û‚‰†ÃƈDúõc9ìž@ýÓ ið¶ïÌüÆÍØ¿w63ö>þº“ PåÓGOðÂçÕ]œ[ÃŒ\»ðÏ#ýÕ“Ï¢ô(„çÑ=|s˜~H£Œ€’‘#áNñ§á1rì×>pú˜A½3ÌiÇþ— ¯M6È8æq34f’Æ}øãI­¿x}°v‰]zÜwnl`kU›'é[,p$µÚr¯¨£ëÿg–€aë#˜´¬‡Ñ̨§rÏ!øß6õ–-õ5 ­n¹õ`É(ßM¤âg3I´ä28—ÆÜý«&dé^öÛ¶·ðõ¤9–®c¾µ~ÆÖW3hv)Ëð‡ªQÿUàS¾ö™ÇC¿ër²/Ï#ê¼¾ÎꢒZ h…rpÝã(<Ö v+'ñ~ æ„3/› íÒ[vôíiŒ6ðfy{¾p3N!¨®ù¯u m›³Í¢gJµh¨ØIõš$qŽ?¾üD„Þî#Žgz‘ØÞ‡K*NtØl›ØJ€^t» [ÚUn ûúu)ï¡¿\‰Oñ°ùB´Í–¢¢ò‰íÉâØ|Œ¬T'ª wÙá±p6ôY8Ñ”ÑgY)vµHrTYQF»ÊŠH”ŽaÝ>ôÑù„QBíìÂqSºbZ LÚaPBC«Ø¢ÅžtôÛzèZ{ˆþý•]™ ¯/£7¥â¸Òf5lÙÓ ê¸kômš††aîØ°÷X­ äÞÑmÅ¥ÿÚpüûf¶ž9f­k™½¶å0ÔUÅÚ‹½àè\z‰'Œ­é^·ûY&®X»†ªP€{œ–ÈДÀzLWHGÕœé,o|0'öÝaؽ±+ÝãL—¸,£ ¶ìÍ h”²óoF‘½«±nöCPÝú”XíDøÍéù‹aÍå_ì„í/ˆµ̶X¨“ɸ9“±pǪF™“ÎÓ+—ãà?QÒ²ç{2ölkÆ©ç iJwg^F¶¯†hœDCÅWlæ†8äZŒ²+À@û>*ÕCÏ€fxuåê~Ï„$òq:+Þ¢ˆGܨÑF^²åšS§]a–”ž†%-P9ɸ]iPkcÂlòa!­âä¤^´AßÉç£ÙziÝXù¹ j¬N°^#'!ÑíG¢[¯ë1?£†°ãÀ Zê„þN’àà1³º§™_äÃ•Ø ÏN)3媠ˆÿ.§b| ¢:â`eÓK¬ ÕafKÂ…µsÈk©…´,Öœ|ÖàÃd³0<.@NÝgÍ®ŸÆß+q®R<>ÜŒa½Åpðõl|_ûNNƒgïaÙx 3Þ$LôFDkÑ^{š£‡K‹ È‹ª¼f#Fš,ãY2ïÒÞߨÒX S¯¶p2•OÀça n\ùâ -—JP~ ”6uT±Uí:$ëú)üz™»¤Á 7ÓEÑ}xyÝAb°:…¾ø«F2O ɽ#é (üWŠ˜¿)‰l’ ‰Ú KDâ×cÊO”ÊÑÞÇ}Ø@b©D/x4ŽÓåàíõe¬éÚCøñ÷'ÎÇÜäÙq†ÚýNb!u }½Î¦žî‚Uиc?ûñx>ŒÍ tI&¦Ïv ^a DèH’Ûk(sø$FÉt0ÒÅ“üéT {>ü-þ¬íã„)ðæÙt,Ç6ý6{l=÷Ï×鴣̕¨²ç±>}¤\‚7ïÁŠ;—PV þ®šJnû‹Mߨ5¯ÌHsd<+ãnBMÊ +W—.¼¿ˆ$T]ÛX²wu%pÅxY–»¦<ïB&¤,¯¬Ä*atÞ‚¥DÞv=›ÖTHÌ~Fs!#ø7Ö…†Äåvx~çsë«åNØØ¿–Ž EEÃ󜭺ŠäwU1Ê,’#uªìB¥n¼ÿ7–¤óKÆpMf±½ë„bPÙlfÆÑ|•ËàWÐ Ç$â#§ìÍ­10n¼ –¼oqeN˜ÒxSöeÒ¥)ßþ›Hæà,£ã`ø}9pjr¡ãÓŒ–¾÷Ø õvTÒµ±µ+Åá÷ÑÀw )²=JnðPS8êÙx¦NhÊ‹¡]½+¹ ™J¬{ó7@ãwgbÀáuŒ‘ž }ôõc2p%aœU;É•eªp­ï ܸÙoêˆÏ5wÚ5u6u‘ƒeá³¼72'g†'y²ÛRÚV]…¿—@Ì[€çïO€ÿt5ÁÕd‹+à’hST ¼ZʘÉ\俈$©YlÕê#°»=žÍßý¶[‹ Ž4uê½mÓ:iªú2z¦1„ü+”&kÿ%ƒ[©~>[vÐUÑý ðZƒ´¢yËuZ¿^”æôä“8ÇøV=’ÆN,£Lo¡aô*ŽÍ£=ùpiþqŠlìù×’h;Â軼Å÷?âv¿•$íf;#“F¾¥Fàî=KÉøÈ;Ô¯¼ ©ÏŒðÿaÒãú£ã¨®u,ÿÎ}Ÿ—ì 4;òÌ6ª­Ù9,.¹gA6\@ÇðD¸þó¢ø"õg«Éñs?áÜmabõê5#g/JÜl¡òð?˜¨¾È¾}]šwç³ÝÀKÂ0ÅõóÑÓK3òãÿ‰1 yAp¯ó6”ëiÂs1"þ9ß]]MeÒáÚ¶½èP´¢¼ѼêÔÉE`X8àQŽZ„i ”Ë>+ÅÛµyì^Nþ(ß³˜»’‰à·«{Ãpí~úùv3X¼Æ+!4¹¼ ^6ÿÁÓ[¨ñ‘v´ŸWº¢EìhA{È©KPË#ÚD7šà®keŒeäAØ#- ÷Ü̉øú(ww}Vö ‚sò¡EË’¡Ý¦P²Ðç åîÚ6´*}\LfVÅO%¢í&Ô|šÇOy*ƒ7ž¿!ôIW¹d;lßÀ¶Lë›Áɶü×Ò繇L=jOÜo¡w¸(ØkÀþ—Áø4u9¹w2g™T`¹ãðð‡±ñň¯ÚtžŽ®°OÆ3ÃÓpÝ Y䘱"?×ÁT±8ö<¹…Íå3˜¡¢q…8¾Ò]Êa~¡7y] xU”6Ë­%²§‘”ßÂÐÂJÙºì1ÐLîÁÙ‰Jøê×'ΰå)p¾rc/*’‡1tÅ=¨”{;l9Ò‚‡hÖÛ~<ðGä&¶0‹]yi¸}Ã)žÀOöR˜.˜ 5ënøyØhBõþ.®ð® 4D$>¿ÈÄ=Ë;¾ ]Î}#,„$¶AéÚ6Lp²†*m;âxO7ÍŒå4E/ᛳp›R'Û!y^ÒošÒAK7cw—áðæ£yø5ü;ö~XÂN Þöð$£~s/äi’ÂÇ«ÁRˆÏá÷(ö”Åê>úg©%j_×#g-Ø~« lÔ&˜éÔ¶%@¢”sHåÄ'â]-Ôüu=®{q¼î€ý Xuz|?`Ï$òDÒ¼>iøò-bEî±]—o1翪CƒŠ/j¯9 ¾ñÓí«l‰ø†\Z÷ОXmUÇžªÔÈ}˜ž|$Kåü Ûß73©ì‰ü°iœ+۔­|ëG³¶ÑÏý¿¸oâ㉵ž# : OÇ}©~¹ë\HfÞÉg7d[Ñ« ËðÍ«=lÖ÷-äfbH;‹ÐMk­ ×3€ä´-ÆY'“ŠˆÝtŠâ䯾`_ßÙ ³ãI˜Æ?¼uIvl¿N_Ο2Çgw-œå(“gûÍéióû´»æ4¾‹I [[¢—®+ é²Å²õË!ظ G®7ÀÌ;*°oÝE±9d[LÆ ¹°½Þ[—Ã¥NqôÖõ4L·‘ŒÏ]ª[lAøvi(‹ÇåëˆßÆRxi0Ÿ¾|ÂGÌBÀ©{ü‘Ǭžðs©És—‚;^ÉØ­Uó êÙ8 [úØU•™5 ­Ÿ†˜ÝŠÈ‹0]šÊ¨ÀË'1p‰œ"qmQD_ô:\(`w{Q|X{ž±=ĺ‰{Ò†úåô×¢?QvÐŽ¿•êžõU5°ÛÐê›KÏÕ©Ðöö(§ÜÏzý‹"âº5lŠXÿ/ë†#°é½¼}¿„ŒÎ0ÀÓÕQTmíhÛ<’ß×rVŠ…`Ëð4&qælÜü— µç멌Íx± ‡ì/â»û:tÆŽç0zt/œja+²ê`¥r2ó#?‚g¡‚=s²döFÄOjú4U2‚}9tõç£è:lÌ´%!Œ~ä嬮;‹ÂZàVo&´„¼g ¯9ÓÂ’ÅlΧ@t{m’ ]rbäà5aˆõÑ#/Z³ðFÍv‘ÃQкÿÞz3…–5ð{&íàEVž®»ÈÇÜí=É´îƒæÍž´­Növ aQÀ5f̱ >Ä“ëˆn»Y\ÉóóU0)CÏï ©ÔÊXØlçMsª°Ì<5‘Ì¿p3ú`q7ãsQž¨ñH‘kѸ?|•¾¸Rƒž™š7àí>:«:‰€únì%cß¡I¶?Jaú[öb`Ö ÌÚ‘f_™K v£¯ )¾–ƒ:daSâRàoLBy± õÒJEý…ëà¸÷KèSÅÈÍ‚`;Ž ·¶ãÑä¯þ ¨s(d\¦N¥A몰öˆ!*·Ã§Yîô ª¯5³a»¤™ÎeéDD6•5J£÷ÛãIÀIâ;á b?¨­fÇ\¯rî· P'#Ä™ ùÎ*Ó _þœÂKÛÏ1ÏιÓíÁ®äF²4Õ*µÀGJôÌ¿z*ô>³ù½:ÑÚìz¿‘¦ÞV¦Â½ƒxG&δŠÀ©™ÔÃk”…iöÉh¤mFmÜhöN¢´ß¿Ì–&¦ÕhŒWÑU dMÞB’ÐuL#•È—t³ Âìth'%âeĉÉÂ{õ!Äbe5Ì6²Ä¡¼ ƒ;1Á^ƒÜ{4%ZWH~ÿ'6Cߦ¯l€“§Úþ…. /™;‹âÃ3 ª~òS±{H4Þðé…ôÏr>ò[¯€è?&‚Ìr}EkkJ\C—âÜÍú¤BýÈ]'‡4lH]DÎWå”5¥°æòL«Í7òBâ81óyMÅj“{{Äè5Ù“Äy|€5ì>G¾U(ÐÔŠdߥfŒIºFUÐyg¾ ! 3‡~J½‹mpƒÝs><„ѽ<™xp¹*ÑY`KܤÉo%oÇØiÎìÙGpãX-\º-Ç §íDű*œµmë“í°œ‚œî®ÇÒ!Nèí"x<f-1ìc‰ÍäHå^¸f¢„¤¾”‡ˆ;u`ñ¬ÇvØMÆÖÂ#È¿M OÞèc…|ð¾‚2œ–žË¤h¿Àz1Cø{NˆÑ?q›Y²6ïpf9ãÏó|$Ÿ;†®+d`ŽObë­Ã ˆo=lÙô÷)7…Ê>°%Μðî 8T™Ð}g8ýu©n´;¾.(À™D,wMš–Íp ı½a-®÷Á?oܰØ÷>Šl3%_˜™lGY0ý*F9ý`‹¿?bÿë5zEIô•Ðq,ñ2 ’ï?cy>myR޶ØÑ[Kõé&U:aÙÀ°î)À^àî,[NUÙS\éÇÆôÖ‘Tv¤å›Ë`9ø¬""ÃñÄûûlÂÓ_Fm°$¾K´hcì}pü¥1™oµ`naK%oã¸ÏÈ`ozÌ¢{øÓO·à™ëVHµ¾‚çÿÂ÷ljh¸£DH“.‘ÂáA êDNôäÐú(gúáîúH8õŸs~ø”q×uLj|ëgãõi;ÉÕ-p¶$ Õ2³É¾)j“=-Ž«ÑHW‚´ì3AK¢ð1‰ØÚóÜ©Z•"ýµÜ„ZÚÜD3/e™Aìkmº  œ®³\DÚ¬ìiÉ"hÑÍgmÑGú&Ê“©{RØ:®éxù±u·4¡¯/š£‚ÒÜI¬±ÆDmM²oâ “øz1i8?÷•ÐôýSˆpé)tóŸ“>˜ýÖätáj~Ü–Ä|]Jç¼»‘!‡Ð¸5LDw0«4ú ȃv¥œÁ‚¡_ìEMroÙI&Õe…“ï`ÅÚn<¶q­ž\×éuÔ¢˜\?îtíf_6/ò0æä Ç«½hØX¿­U4RZ-Áë?|PoÛâ®$M…¿±.»?@æ…MtWÏMÚ}ø\h!ìK-€™7ðjj2„IöböLã!-¢ õÎçtS?ÿG©ÒP6Wj>ûP¼ÒqúŒ™Ôÿh%žnB­7à D•é{…‡V¬Àÿâ\×ô#q4,q­gSuÌàöìÓT{»yö[›Þo‚׊4â³þŒ›Åpzn`ò ]ô‰,ÃôÓ‡`ïéX´p¦ '‡àªå|be±$Í¥é¯xºr°›=zÌ‘ý3£/æ´ÎWYug7püû`l].tN9Å©\ÐÀ´=Ó„Þí¡Ðô!¦¾aàôŽ\Tvà×7§ð¾YœáŸÀÒpg\tÏfž­AÉ8|_´nT˜ÂØ_o˜y<Œ>ÕYˆŸHÇýÓsÙ3­Ât9oÊ ;Ô3ç?°áßW ã‚4\sP–|»°–ˆ-¥Z×ÁÈÖ‹û;ëZ=6e] z¶½ìýò.tìGå©€mK8"O›p¾1j‰‹‰Èú \©AD+×Aê²Y4¸T™¾ãì sïÀ\‡4\'ÁâÌšÝt‡$à‘Ížhºå<;ïB•ªÖ³";cÁ³r >æµÄÝôò+$ÓOnÒpÅd*™ÅèŸ~ÄZE‚üãÛL:ÝÁÂG5~e_wý‘©*¤ÖðتԱÃ|—`×Óã“uún¿íƒs¯ìg”{¾r/ô…ò¿IàÕ’¦7áZ/ú¼6…Ô©y’§ët ÿ ùÓ³‚Nñ åÈ#æÁ ­ç@Ù¥dº6ÿ+ŠJ]Çõ¼[ û½AÇ5°¿l^œmO×hÃlÍ\ÆÁjŸ&JW„®ÂKcM`=e>YjrUzð„IÌ.¤Q])Lä_tâ'Ô*…/£UqêætÁ³Ei ÿ—¦l½+ÿŰk ²¢Óéö-­àòŒ³XO–ª|¾Åì18J·>§¿Ã¥'õМÕÃGÝÕ$pÖJ?Œ0=ÊüçßÙŠ\ šÄ!%cvüë18ðü›³ëfÓ³”cdD•§-Àü«Àúêf´ Ca~¢C:!ò’™ÕÃð:»7òsÚŸJPŽ"ž[¶=òJŽ|ÈÅn;srp,—¯R|“ú„Ë%0.|¯ÏÙG¿ÂšÎý¸ÀÞ˜Ù–ØÃŠ\ Àï¹SððÓY0ý±8u£¿Y®ã¬|­sº\‡†ËûˆhšúÅ”>}}ŸÌ™Ãˆ4•³ëÞñ‘v™¿˜; È´VUAôé/Ìn¿F<ÄI¤¥†KȳYøa[œ½ÿ›{¼„1úl„ñò×agn-Þ¨è‚ß±Wáá3,Ów£[†t™HI!<ðØoc¾·>“?Qw¤¨ÕÈ~Ø{V¿X×á'™™Húrqy® ³wÎIÈÓ?ÞÉLf“^{cúN‹à¼ûùœªÈ¡ìqH†¡ 9úcW0{ftwªø2os`ÖÄgæO‘#+áÉÄ>Ò¡zÄúñAüw/FE³µ`ô7 ¹yrd‰Ø)p·ì€ƒÙ[¨Æ«k¬•‚Y`‹wJ‹ñìèT°çïƒKC¼Ôó—Y4¸]|Už6ŠW³=-ŽäAúz˜tÇúÊ'“qãH0Yvç #è>•ÈuqÈ«­Ï˜¡ŠdX•ÐÅxþra펯„x­7x»´Ü¿²á¸Þþ4Ìx®IwÖçbÄ–Ff~S4{Þá8«ýÍf €â±"fÚßÝxçº4U{2ŸÎÙ|Š©Rà|VÕßʳP×ÚXÐÁu7q`8÷pÔȈx:Ì;ˆ.‰PïùÐP•2W6€ËÄjê4ï /:ÇÞúô† I”d­7(Ðÿ>dz2‚·gr•Ò0_Z¹²œ[(ÿN„&eãÚKqGñ'Ìè4¦|'?1þ Ú˜~f QμMAáb,ºž/ň/;¹¦ Â…"ab걎«õù7¼‚ºS¢±1TÒû’9üãjôÌ4ÐMìÆÔ_e¸ãxsÃ0ž‰Pý}ÌÀ£6T(ˆ\g΢ˆu=;KœüÖ4§e»±h|-{½éWòm/„/Q€ú?y¸0E’ÖuÖ¢%Ï P¸ým÷ˆÁ¾+ÚÐ:ê‹fŸ@¯]hÅMgg^H…ïáþYZ€wíil€6öäÒ¨êK¸Ì_”˜ÚÖ2_îj’îﳡ†#CÎ’åq\¹Ò ‹v°|™œ™#δÙpf]½‡Cq®È4ÂhœDð«ej:ã0]Çh¬±¥á¦ö¤/¼no€¥SÑú,ïd¼9\áö(Ô/£ÞOU‰ýÜ©¤¦‡™ ¿¬èž¯]`s’™ÅL•P¢¶6ß¡¹!Çö|j=7tžæ/@ΔcP8rV(á¥GgØ aG*œl@‚ šÙ¡èýœçá~åXn,CUno䆔t°f¿nãì| ¿8Cׄç÷0ä°Ž~Ù xý5äú’–ŸÙrÃ=Ðv –=¾…f|"°ÍË’¬ŸMø¦'°Ñ_4É“ÝiôñÙ,8+ᆠ9QtZUøþ T<É£OÓ© DüÝ}‚~dP{Ëv¢¯Hû6üFGÇpz_Eõm“`×]š»…óoÌ£7SNáPêIô›~ +­ÄÛÕ7·ìl-P`gÊԱ݆®Ì—‹¥ s¬î5¢kM–@Š·sã¨1IXÈËt¿f'®jÀØdíy¾NqN9¿õD¨^îTúR=râ,Уé-Ö4„)ÞUÐV­M‡ØàŽ-f襶$Y9jÆ;ƒF¤¼qtqCÝS.äçõ\৪;¨ªümXP êAgðĉ¸ß;ÀL=¬J/^5B³O_aÁÍjüRlJì’ÿ2CòÛ‘…ÝBw`CG Yµz ’ÜÕÇ`ÇËí`T2#u/Ž;îÿõËf‘6=Y²5™›æ‰àkÈz1”ü|­»ç!ßÝtø–+D ¨ü¿ùðrEZ”)þ'Z çÉAVÆ: ÍwØ =ÉÌ9…åÚ+ òl ×j0­”pbÛ_t;'óï|cÍ?Šÿg‡"£M?<íÄ“ou‘;> +»Ü©î”wìnÏ%xº67=áÅÛ´wõ$¶ehÄíÁâImz¢ô'®ºÌC×qÀW²é&VäÃðÙ‘„=¯ƒ¼\ <=Ò…5hw¶š-2ðèOò:.-|+ž= _¦ðá4íÔ²•©%dßã(hßp–©áuÆÎ•;Ø‚1ºº?«gg0s?)°9)LöQ17¢Q:7Ÿ• Â:€ažmž‡Z ùo.™Å_Ϻy©QY?q+ÆÖ¼[E2µ³™mþÎK”ºâV€Ø¹Ôƒ°Ý%ä,U¡Iy¿)ÎàhǶšê® ÖÞ÷XŸ?ÌÙ$ß{áØºÍD<¯õOÝÆ[Ó½áo˜ Ù£®„¦k·³cJ¥ ¡£ÇzÏÍ„#õq°.èΤô.fÿê†ãÜ(/xæ2ŽNÕ£Ö¡p¦"˜ÆÎUFu±»§Òdæù/ÂÿC‰$Z‰ÒœÙÊÙŠ’2ó¡×ã¹:ÚÊ$© ÖѯK¸r>ìb|_Ž㇉ýtgèCˆÅx^=b©ÍýS)ƒ^êàûQŸ š­†&7˜ÈQ*8m<ë'Wo¯aVéË fÅÅ s˜íÓ!ûÜ2бòd*-ÛQjè#",EŒ }ÑÁX‰ü52Ãq©0+\‚®ÿû'ª³ " ˆí'wtj9H~,ÛÈÕÌÿÂÕÝÅýpz”i[úò*w‘è±Û-UÓ¯_$coåˆök!e0 dšwêAt–8’ÿ÷Xïx‚³ßÃE ¼‘» t’ù1õ¨møâO,oÚ³Å÷õQ9,‰,Žôe*Ô¸!/Ò¹UciXµå/ÞeÿÁqAOØ&µ˜Ä'ÃI­ó³ «ÙèO­ áôùs!–ˆÒb¤ön¢‘‹£‘ÿÝâ"yžu´kf~¶ªCn«‘50&çv–Ã§ŠøýóNlOÝW§Ê÷®2Diu'óûanÍ;ƒg¦É™yYh¶ËÛ-¨mcÇs\‘º9rW1 ”ЊFO±ÉY ìØ“ pØÞ‹¸›Zû¡ è|0„”Ë¡p(¢Z=áÄXA…vk¼ƒ—¾«ZÎía¢ [ØÅ—³p_»Mt¯GÝ<_Ç…M8¬Õ;Š‹ßèЂ–S:VΜúÊ¿gÓ'˜ª{æ|±(-ºuã²~y³‰Yœ; ;\—⚀Ǭü:g(ΩaÃù/ª,€)Ú&±â»øÀ-´éï„i7š`ãN üÑúw^Â~ê£Xw;£ß9˜—^¿"EC¯ÄbEÅo6t·2¹œ§L¶[ ×2Ƴô“¡Þ½0ÔzS€2GÅÆYhI«ŸÆÁõ*}xjÃCÍWN¥%â/q³+iøì‡ÓžÎ ~©tö´ÇÐàü_\‚ ÏÑÆARýæ$Vo‘¥³}F³68ñ³ìF/Cj Õ÷´ õâÚt^w7çÍ`.wδ~Ôæåà ߣðª¦YEýÛ ‰Í:u<öGNÌÕ¦ñÁQý4-­C—ÕÀºå1Å;¿ƒÊÃrRÂ/‰j éøÕŸ,¸Ã®Ó&u7¥¨Êisò!:›ÜwZ#'Rxó/²=˜¤ï;ÉX¬na4åþMÖæòÙáø­Ðû&L¦Ë¾„åzqœ/»U9R¥¸ë˶‡ÞùEÊdÆ [¢÷E׊ì‡`5<ã e u}fñÇ@æÈL:ãÙ xˆßÚÓ ÿ¯TÀsëVv5gÞµïæ¯•ÐÛ#Œãã±Ì>«*°_è@W‰¥po9Í®œ«H‡–ïÂÑãäÍÌdæÊÈ“Öëî=Y¸S*“©Ö0źÓäÕ‘Aü> 15R0ÎCsŒÚH÷y Z—kH‰YÞfw³Ò{þj/ÂT3<¦b…d–²#{q×üZÛÈÙ£Bà g°i£iè’x×ü+ípA"¹J•±VñaÞêÔqo,x ñÁ­LΕ¯*J/évŒüôß9p¬a˜¹ìçÇ,ñþ3¦™Ï.´òdöRgîq§ß ïƒ½²+qE–u?Å ^$áû§¾}`hëÀÊ^Ç—ÿÁ{kzKýn¶W¡Nʱ&ÚŸ ÜœiÀ }ñ¶tc0Œž$2[,©Ôe¢×±ˆîjxÅ&š¦ÒûWºÙ¢Sé£?C8z7â”3'Œ@COY÷»>¨¿äÜ͈ º!Óè¥áb|Ó4Îø]yödñ^Úpº”Ñ>p| O§7‚ 0ñ»Hù€;| –Ùk¬Gîöˆ© 9|d‚#üh! ÚºCÇ»hL`wq¿¯U"¯÷M0 mW’µZïÀæYp°îº\†ºP>zdtŒ£ye-¬ Ô Çg9© Ì7…]XÑÌ6'a|ܧÖákÜÒÄÑbgØ=„?ýQrT—ÞÄÆ¹.…¼`?=0¸ â~€¬z"]ø[„¹y¼˜<±øÄèÏ8BºÊ¥±™FgOI¤ §}çpòpsᩞAÞÉ‘©m˜rá+ÚËá« ‹l$¿ Ù CîÙ5÷u'óÕ6êë¸Áú_çQe]26$" six:Éë ¦MŸ§3Çô ‘©¿‚ó^òb©îEFýºå¾"6aËakm .vý5m9xòÀz0™çIýê3Xϯú(†EÌq~P]LÝ0)ï)dð̦72'gˆ€jJ%n98©ÝN¹asã”÷±ãý‡Pçs5ÓÏ_ 鮺äš[ÈÕh’ÆkWÙH+MÖgÁø‡4ꓺÖΠôtÐæPÌSŠåܽÏb]E,Ïa]¶¯Æ‘F+ˆ›UÏ„†bD$­ñýιpxu¸1«Plï4òíQ1®«-æü6¥&Ͼá«Ô9Žçüç’/Þó ÓF„ˆí=Ž}GxhÙú84Ú;Šf·Êໜ ;¯Ñ-·XÐV˜NO7°'‹CÔ¾.jîBE~:žÐÀÉ_Ú­â—å3™Q¬ò}&ù²çŘß&–À~ÑPc?æá™;¬|{6­'½2³‰¥àU´ý8ŒG—Ð|;±vt+XÖA·ð²o÷&ÂÞÁV¿ÛæµrøC”$µý¸~o£åîâÔ½©"FfÓ´sÛ¨¡ÿâ«"G.ÿòf#$÷3{åLÑ~ø5žÞ@üf’¾[6tõí$عZ„l¿#Íö,Kc­O¦»ã‘U‘’Žj Šô¡c%ž— ©"ë´åÇ×Ç‹´ÝN¥ÖF}x¶Ï~¸z‡Õ<¤zð¬Æ {t‘ûw&ËQŸfå%O/~b{^GÒá'XÇxZ«¿nßBY<÷@œ¤kS"æÕÛ¹ ËLÈGVœ2Žyœ€¥#LïÜ“lÏͼ÷Ô„™©²­Dkñ¦à +ð‘~†aÓšàûaÒwö [ò [c5©}ë[P—  IŠ»jB¡ÀN†<¿¤îN ¨dd‡·Ò‹aöѧl_ô!h{í„]†ä`¤3ù$œ…[‹ è¼óhÚÜ2ÜïDÍË¢*YÃ060À¦4Ü‚(³˜´Õ‚l´k¦·E6’ÓýŸ`z@" Sn‘î’mìG' ò[·³ùVÁ¹Ö tê0©ñÆäëºv²éÜ-Pì.Åê±#¸`F¦oâÙ«Ò4â´¶ö:Ð ×qÌ©j¿Ö5œsü²$·C×  LÍÑƒŠ”4nŒÑvé!ÔÝ®NöE•‡¼Ú+à/+K‡î%À©·Þ$ns(zÊ¢ñÏW`'SL4òfÐûLÝø{1µò€GÒ­îàÅV»µtCÝ{îõÏ1U#Æ>UÉÅGQsàÑ«"5âD»öÝv ·#¼B~ì?ò¤æ;ÕÆÜC ¬Eº[2]+.sxjôÈçýÍP»8 ªf% jëvr’õ_™[”©Rj2Ô-œÆ¼§-iÊtƒÛ ǵ|3¼ã ļâ#§¿‘G ¯ðoïSt”~ªëB±ËZ³…|Ѹjµ<ø ?ÈÆ´™. tc-WÉÑ.£{Ì1Ç(ŒÞ³œº9›ÐS²ÿØÐ„8f§›ù!ãM£2˜ ƒj ”|ËMiWúçØ/1¸ø¯†2rJÖŒÞ~úý™vÄ p?ë¶F‹¡ N CÃ+å×\Ï)¯Á\ÆLúÃÔÏâe·Ìð™Ô\{ð÷Óí­‡Ï<€Íò¸ù×Rz%3? ‰’à ·^_Æ{>¼­¯­$á™d8¾|™ «XŽjÏ-T’ǯ¼Iÿjl`üOø\ˆé}ø9aºþ¤pÁÙ±»¶·»’C_¸mvi`Ó¶ß`pp X1¢äf§NkÖ£9ŽKÉ·“Å(t˘$œ^„ KÞ0™çë¡"æGìÙIv³Ý”û𙉾ZÏ]ØK7ŠT#ÿÀ.Ùx¯Ôg7QΉðJNÇb†ðL©Ä­ Òa`ð×~Z_¤ /ŽÐÜ_…Ì5UºÁù¼&3¹cß>"oÀqFÓ} tÖ…ÚÌðý”qxÄL•[V¿Úׂu»?Ä‘¨¢¼‡?³wtP%ï ¾|½†Vò1M·›8:ïLpÚÖ»ÜlÁŒJ>Ô€û[KÊ)–FIÇŽƒ«.uü¾GÇï½sFäØe²è@«3ÚÁ¬UQ£÷6›RÑ£l./^7²¾ñäM~JKž 6ò}œí×Üyçþa¬S4é6á¡~I²tr?3K *rN)“9wLÁ^^º2Ö•ž³ÛJ5Ýçá´EžôGöÔÖÍI4`{6'óÐë÷>2»ÝƒÉ[‘R`ö]€ºOó`ù 'xfƒó–+s§˜¢dƒ(y·L¾†¸Vë;pQ­—–Šª€ò]V___¢jÇJ2¯w>¦êÑzdŠŠ •« bíßn⸬²¤šÞ ¦¶-Bá=Ýø·Q'œ»›U™Ú·sñ:Ÿá| âücÏÀœ÷>‹yÿJ‡„=LÃõ§‹èßëÌ*×xXµL–‰ØU ©m·ð“! -"_&aöYbž‹sÿ]cùû*­éÅZ9Iú !=tuU(RF»q>Z±`¿äÒ‘Ï—àJc3.9ÓÄîõ v’è£ÎÌ炽ðú^=%Í&A"¹øbf.˜ŽìN(åÊ‘;CDZìr£7ÎYsžÖbÜjL:¾ª® ¡Cb ¸´‘øŒ:(R£öû‰ô zíx;»òü °ö#s\޳eŸ¦ô¦#_tkÜ@ »G­‰¾MÌÂMnاÁKwÖ¯&yЉS‚åá™BYèHåö6Bæ4âÓyt÷+ÐÙGà5ôW³8µ4PÂT%O½Ñ{af¯÷$o¼.±3¾eâ™’ƒw’Ï•¨°ë}FSHûõ;(·å;'í®>*G´˜ Y4]—-gÞ„ Ó*nô›Žðœ1&S}b÷ß!ò'WCbüº>y'™‘4ÏžŸd«ÇRã5øþWŸLì¶Çxe’£Ia•½&‰l©Á+3‰]ìŒÌoõµÍbžfÆ0÷ÜWÐDIÊÞÌeFb­Hæ’Ì ‘UtÒ~1òCs“B’w‘ŸM>ÇØŒêÔ :Kû0yæJ’¥·ÇõÈ U©žIÚ§’Ù{]èìÓrDîi³8¤Ÿ+´ò ›heš„R1ëC.üG‚£ø6"ç¿¿efÔ¢Ã+qRajEæìK&Ì’rüE)ʺEÒo|Ð}†³*é*~ÔA¯D^*ÕóÛfñÓÝ[Té<é/0È×j®îðÁ×ä7}böLÕ¦ñ#L‚ÅrÖÛo;^þWÂÍjýŒ+#F0g˜UÙà@Nܘ »w¦Bš² ¯µ %¿Œ¨Gîm5­gò~dS.êƒò§!VÍc> ™ Õ›ŸBçlA6MäKšçRóWæÔ@A]¤óÀO'5ûiD[ç¨À¸DÊ)·¦M—ûØ#çMQGŒÌÿŵ¾ËØ©L£ê±v¸õÚ_l~j‹1S[ñyãv"w&œÚÞ̨'³ËχâÓijdôE ÿVDåÏpcZ)<}3ÉoäNÑÃ{ƒ¤ë\¼vÍâ6Tâùw79»i ä™Èqܵ <úÙ¨{nî*f^´.€/xè›Ü¶`Ëb’·Ô»ÉCý‰Xª#»ž˜}Ï€ÚÄ`Žïæ¿ÐgËÓ‡Òô YŽŸôÆÇ×ŨÀÃXýßy\§Ó…7@úR)¨DWÀ‹c×ÁÒ“x– ‘{ë0oƒ6y‰É2bÁ‘'34ç¾c9¸O{s4¸žŠMgj"ø>$î&—ì!7DißY³lR¿$å±{]®b² í]ÖŠéZø)t/f'XQà݈£‡ïÁÎÆÙdOÀ_Öëüb¢}½+fžïf £˜ÇðPK!¾–[7ÛÀ£aÔ÷kÒ—=Êð%Qij’™ŠÅ;(çÓ[NÆychº¢À³Y_Fièº~HN_ûÌ3Q±k òYÎã$ØMŽ”(ÐöEz¥d1c)WJ:1Ä_™iÖ¿ƒ'‹jÙå>Vt­·.–ñl%½ Ýȶ;VÔ˜w|XRÖl›j»^¹„|÷ô!¤^œ ’Åä÷¥Åäaì;æLø*0·ù—ÓŽR«ž}`Ûô‘5™‡±U³ivãÖYd7Ô¹xà\ai½]ËãQ´B‹ÎlÎ'’y$ü° {àK.-ÞOZˆ?×HÔ†þHl¥¦y y/¾ÍŠ„ºj$r×aº¿™°)é$Sò1ÞÖÆÞ8^N²¾¹ÀÌ7ùIJ¸^¦Ò“_dióO:žXæÁ˜vòd/£ß‹eôŒ±I±ñ¥SÙý§aí+!R¯æCsßÏ 'Ý®¢÷w5º~ƒS;Ø3¼.ìT#{Êü ëˆ>¸!²Dm|<þvÒ­,{S:#½þT\š¬‘ÃtäÌ2%ËTH"ï'ªØì€†×§0/ ®á[wªZ-Aûm"ÇK.çƒ×iäÔ¹$æR8½ÃGÂGbý "*¼•Ÿð’®~-ZxX‡ø½¸çœ„þT9®kéØ¿ˆkcÜ—›4Æá¾­“¤—U÷Ñ»ÕxÔÚ×8)îçNx§ò þ¼‹k6ƒynVü)e^ÅCÙöX|¹>‹æi…Œ¼X\·W’q:PƒŒGÀ2à ÜÝÔÈ4Äb+É`òdjaQß¼{E}XSæõÆWÉN&oßw³(M”N£Ý× ð0CÞ¶–3œëàßÒ-Ü´œAXûbÄ?“"¾ ñ_?4º ø9×þ1s*Ž3£ ]Ý”¡-|7îR ³³ÙÕÞ1 /#IÿxÁœ5ƒ80ÄΚ±ŸÄ×AÊ»ÐúžE<߃é§P¢>‚z+NAßZQÂZÒ¬Jâ^|ÃóZCŽŸ^Âvßj¨8¹bl¦BðÛ{°ê¢74¼'+ ‚ÐDá7s ×&Îe£Ñ5=Ô¹”jvEðxŒÅ‚[‚´X~ÞX\Éòy r%š¶Ðõ£<ôèD.(ÖçÁ¾[׸iMÿ@åP6¼>Ä9QÇ‹OKEXU9Avÿ#3n¿_ȼ3ÄÝgšá@ÙZ5ʘ¤ E°öH%v…ŒÁ×o™™3gÒÆ“Íèù–Ñ6½‰v+n¡ÍÉ ràž"aøÖ±AÁkð¿¸ÈRúk°œ nLCž8 x©Y„朗U¿çQÞn±9.-Añ-,s°Æ×j&¡J˜}¤¾%4e0¡idóÙå”h‚âßm¸ècsÈ÷ß}Ϊ-Q$1_!t…¹ôþ"¬œ*Aÿ}JaO=iÁ-“¼ãÞ…Ë8tþ<ÚŠ/ªðÆeòèâ|3Q AÁ¼àhBïqfp‹ÃÁéúòlžYT×D$®¨€l­4®%²ªtgSmS‡ó[©“Ò"Ö#Cˆ¤cç¢djh¨JÿÕ â~¹d²¦n “ý¼›¹³ ²ÿ’!Íy´:­íÝD׸Õctd8NÆ×O¶³J&gòÕˆý”¦ë…ÉI2]Tš ƒ/Ù¿k;¼] Ù÷ù©½C<óú^>ýÑI³^³Ñc8ÿ‡3È®\rˆ®ø@Ð@Àƒx ÚÝ…Y†¯YÉ-q¸üÖ Ì» K/®LÅ%sÉÌKm𬛞üØÏØD±¼Ê¬l®¹S‹åu³Icö Z˜¬A­ª|©þâôöÎ,÷ãjò® RÈœ&ØL~x.Æéñ ùq¼ hIÏ›N›ÊWÒ{k`”b[û9¨|R€Œª©Ø¬Ã1ßuŠ~¸'CÑ÷7çêÌ8<¦MÎ/ Uó•i4“ŸÒ%H›D*Y鬃ë¾Q™Oè0ÚE¤zÆÜÞ‰°UóŒIÆ^9çÀš,ßJ¯ûoD1¥^<ºn*xœj:jF/‡½ÒØ%M–æ‡àüdAšš?ƒÙ.«M–‹˜àØŽæÖŒ¦øÅ/K—V¤á£8E:GáÚ„ýF¯ûŽòÀ›”ùè /ErcX—twºBjy>¹üÜöØ S¥j‚ªu¬0‚ž´'ÅAìË?ŠÄzÛžÚó£»mÙ ã)¤Ý9zºÄ¨á‘HüÌjÐMOŸ@ôX"èßÒ ™çÄI\œ1¥©¸þ©Äl8ˆêÅB4yJ1&m²Áêï®øuH|Î$%>,y 8ma‚þq¨zC&ûpÏN*ýyyþ¥ÿÿó–Y'²÷ ÁW¡§Pÿ¦ žÊBÅôi„1 Ás¥«É«[Øõò$X[²ÎWaÇ7yr¶ÖM²¸–÷D¾yÑK×ÀaÓ§°ÑГ]3ó Æ.º÷}ÙÃL¹dÁø¼5£‰!àó}uš£¨F£_à½Åó Ãeo2ÚA·f5^J4§y5tæðfœÓn†»æ•¢1YíìŠgxˆýÁÍYRÞR$cHœ¬EàȵL²A9D —Â’¬+ð½i)½hM×m5!v³·R¿´©8Oú(;ÑFEïГ'~`¥ùܕڷŒ)köo-ïb#o›“g_ÉË«ÈÄà5Pˆpç}¥dã*vĕڛN§_7êàsÛç´ä«,1ˆYAò×\„Ñ¿3H[§ Í(ÜB²ÿ]#koD ÚJg/&‡Ç“É<•jøÎ“ ¼oxè‹‘;l|üm¦;y IP؉¿ô¥É3½› uóÌ/[Oy÷,@±£µ4÷Ÿ míúÆ?ТȬðrcµ6ºØ²ÏÔãK/eš˜*‰ÏRî`žd7)$•dœÕ%Ñþ˜ñøø‡+a}œ1‰ G}“8xÀçÆÖ.oEÍBiðÛº†JµØ°¥ú§Ñl'†iÑí‹Üè®Ûð³”ñþ©J¿¯ä´XÉ“5GÉmÃ>¤AýŒÚénV¯ã>)s= #¹ù$ËD›_îaßË1ãLhÞJËéâl_ñÓŠŠItK5۵݂]^1Á´k·ãާî$ºô ŠóÎbw-R'¯ ñPº)Õ[®L×$}dUfIÑž± x."K雓ÿfQ_œ¢AšhÇ.ì]ÁKHýk=Š×ség僴AJŠxŽ[@® >¯±£³H´MÛ÷ôFkrL¤£¡Jý)’ճȮqGô/“Áã-tà²>ù—ã@o>nbÿ®ô½—fpÖñ+®­q@iâáæ‚¥1…­ŒÃâ~”¹ÆKê$@¬Hƒ„úHÐ4Kü0ãÈùOÂo²¹pæ¶ógxQ§ŸÏó€ó»ÆÄf-Íæ§´u œ–Â,*%…µ‹©çÊ,&³ç‡ãJÛ7à?ð†m8>ð-Z‚³n¾fÔ[XxpYçì¼ËIÍ9 Õ*ÑêÉФ—@ç‚•Ý1âw)ª»£lÞlbžý˜Åªlváª4Þr’¤Ê©Ñû;*A,Ö š–fæNÏ&#ILånaê kCx; håÜc°ñ%¦-ÝÇf×M'#kÒíÃ%øíf0*­Ö!‡”òYü<—¼š‰OV‰Ó‰Oh¥6ŠJ÷¬Á!çfJÁ󞩘„b´ª ¨K’;éþ=î<„UövŒÈ%Tºç3ÔŸõg˜´Ú«é‰E†Ô§S™J¥î ~·î s ¼î pè…㤮P!Ö6Wiøá*7JNˆéQïizöâ4Z÷I’\D—CWñ@ë0¼o®#îkþÀO™dš¯Gâ¢ò‚u¸?>ÉQé÷U°ÚõLLEÓñ¡zEŽ’/Ç®µæ7í î ª¬@‡’uÑÔzúUürKˆžW¥¯èêTi˜—+GìzÝžÞÅøžpgí Ö>Ršdé¡c<$ƒÿ!&fïg}<¾À«£jdnµ{×ê\ÑïÂÍ×`ÚêZväÑ/¢Lˆq©µ&ÑÇ6@ã®Qöì¼!(âøS‡ø.ì+?D}#JÏ?Uà®v‡®8/LEs¥¹%wŒél?œí,C·ït&¾Ÿ›©l烙LÙA•òÞ©Pvã6 âÂd¬Œæïn OódzW$éwIÒ÷¥ùòáÁ•œàÞ…Àã®O]wÚ26nrôÊú ¶¥-GæáªsRôæŽRzEV…n›!Ìý︻:[‚’¬ó¢V-ä#ì½ L¦…®¦ñ†2ÄBä{Ìú.s¦m̱oÕ'ÌŸx òFMpIî'óT[ž¸éäÓ újô¦\®K¤MùËéÁ&|ÕÒöG£ùò›ðÊ­~ʆ÷1K0ëÛ}(\eHZø‰¾êžöÊ¡i¢Äë8-ò;«û ërVž Þ>@ä̈áê;XÛžŒ#5ºÄ`], ’‰Å‹J¹”iÛG»¸¤‘W¿G“ýå<ÏÄè»Ç¥º‰ Ç„°FûØ®ófà©q”]–>7o´`žDʨby“¹Ðxßý™ Í ã8SW6:[“ñ´Af…F³a§,(õœb×U¥ ËòI¼ qTTž8©w³œ7³`â„öl&ž‹Íh‰z0Ì &*?â·¼š}¸8\O –I[ÒæÁ)Äës=è¶Ä¥dNŸ5ž+©À /4QcÕdy÷sµCGÑûEI«0W€`÷Kvãf"q׃‰`ÆOK"uîrhôÙ´¤çªôu˜-bF”‹3F0œ…µmÏð€=ÿRp5° OU™™#.(³9¼  Dq= +χñû÷0%¹€¾%Mn¸Ú¢ÂL%ânéÇ&Äþ`Üë\ÉV£Lô]lÇ.<ÈåØó{C/ßaŠ¿ùsËÜ-/€B Óûš0?å ~‰‹…Ò£D!«¾Ä5snCÝMp‡Ï“¬ÒQƒ‰À8òxI=3 r¦%#ÿ¯àœÎþ ,„×=Ñz_ 穪 +? b~ˆ¾oK…'¸ÂGbßàe÷ ò(»ˆ:þC›Çì¢AEÚ¹§>¬VÃÝÏv2A%Ùí‹b9ÂÊž(!…ùæCŒ‚|8ãëXÎj®ÞIÿ†/l™ÿ÷ÜŸAá}¡k¼ë}õ¹‹`¶b–Ì­Ä%•ðPî(¾_£J§‡37™ï°'6¾ée™2'R¹ˆc1Sz¿W¢Úß>Ô[öiþx æ¤ïC‰‚Ç ¾«N¨W±kðÕÇiPfÇZvsëtdÇ‹ªÔq× Þ5¦'CðSôôYð‡ŸD²ÇýªpÕºéd_¿þnÃ*ƒ8ov”™Œ¹u…% {†ªGwöb1ñ2•dRw Ñü‡<äý.¬ &ôÜOr}µ'LÌëE! `çLÁÞ s ªÊ0Ö2’>ÿ@y÷A|” .pÜÅNÃZž,ŒGnÜ…}ë>3«tPò³[þl¹®éA>±˜Wákçñ¹ïYIßröxPÝ^°–TDò°ÁËA°ôÎË:Ψ— ßn2EŽÓÈ«;¦$¯Z’„,à‡ÑCŒÞûéûüÞ°ŽéIø q^,È»,m·’ik¡›ö,§ÆË±÷ôF¼üÜÖ$Š‘ÉxXéØ(úÊÔÛõà¿þé_« {¾­gå1b|9¬ÇGäïꇧ)” ñ¤åPìµß>Ñ…u Ëà_çÛô'ze%@Fc=:®xw‚œY;‘ßàd³ 8z¹äÒ—JŒî[ ûgñPµ¬JØì ÛBIÏ/í«íÆ^‚FIòͼ•Ž +rÈ(}!W†oT^²®ü Ì䵘¹±x½ ¶-  b=Ë@ou0sg“)Mî©Á«G>3gçˆÑå`.qxÐ÷:“&@£EňIqh7j»p&=cDTÓüȦ@o:ÃPˆfbrâî"oÔ’wû|hÏÐû— ³5‡Z¼ëó’'\½lʻυÌ.R¦LÇAz¥˜Ì¶nvõº*l;mŠ XQ¶ÃTgûm…ŽQ¶·ã>û¾¦—¸Ï@·ËŽT¨Zl&EÿxçÜÞt¶¯¾h³`ròieCZÿWä=N‹àEÊaXe)EÒµTkš*<‚{Åáü,üMöM¢‡^sÓ¢‡‹É‚9#ÜÙ¯õárA #òu+•\ L>¹F£ä¤†Ý±¥†+­y‹ûèPRëËcòÉGyÏw¡RÑ8Ü ó$“~°/=…eˆq‚?¹œ9‡<¶‘£68’wŽx£Ö‹~û'çTxÈÝqvÝR)È}¹‰Õ’Ô ­ë/󬂻!дùýt Û˜¢kÔ0™ã ñ…å샧iè>?m´Ç˜“¶hý¤›l¤0ñöCä UUâ0ÌGBÍ\)}x‡É1­‚šUßÿM1f ßfô“‹Ðµç«,>•h¦g[Aóã9¨]ôƒå=ËŽZˆ¡Å9:ïÅvøf/‚ë¥ör<»‹ØsAŸaJ\=ã>LàÇZyRʨҗ,(_ÎSð’<íÞì|ý©¸{¹½´ÉÛQÔr:õï€áÐL6“ k¦ ãuÄP˽™wy ÐÄéÜ?&ûpÀ,vîÜI ä3k4,8÷ÅŠwÏá¿ ›qÙŠþÚP‡g•(–`N\åŽ1ïã2áļxúŽD¤~nÕu(]mÍ]Ä=ý_¿°¥«ë™5yé`½¿Ÿ søJcIÿkf™¡>«éä@é`ljz­dЧmÇ`H¢Lî=*NsH¾1³f&¿“Tƒ˜g6œt–o·iKãp6EþbÖtËÃ?ƒ|Æ»†Å…G} çD!{÷ã-È+U³žsY…Ü|>å4ÇÚí øV-Ç€1†ŽºNaœ†/¢ýL zZ$oÁܱˆ&ý“'¢æÃ\5ùãØæ/ÈòV§ ¾x)–˜’Ø)dÉËPøm GÛHÙ­¥”Ê>áÖÄßFß– çVˆ+°K¹Æ_zÿ•?×AG òÇÃÒEGÐØ¾?6‚ýO1ʸay¯Ë2>áÔþ¾1ß)GªdO^| •&`Æá&–÷Ðf8dAcÆd™OOæ8ªøNï›@õ÷'!XÝëCt‰ÿv!ºüM0ˆû+à*÷v¶Òë$nhR¡Âž~4=¯òÍMAdÃVr¾ã&ûgïAìJ¦ŒbA.(;fCù“'pü†½¨\Â|=ùŽêâ"“™Ä±þ-¼¯Y féx¶d*9~m ´‹6¢À{5NºÙQH6¡Üî¥N>ŸˆÜIþnÇ_WpïÞl¿ËK¸¹ e|<å–°Z‹¨Ñy'˜³b€½2å>öÌc£ªUÈžsÉú€3pßî &ù•±Q—Tö|ŠÃ=øBü甂2V?܃OË~±<9™pIª¯æmaçr·âHæ%äð@ùh#(ºIÒ™§Úᬘ±^¾˜ÄèÜÁ‚ž>6üaYP£LºclÁ#è=¾«êã¨óÂÑ(Wæc¯5Êwís«}ŒÅJ!’•?Ía_!Óå/ó%M“¶Þ̤Ûö‰’çÕ'ðàJ^zàë8ãpiq™OÓæÁç– Û‰¢#†„ï‚‘1±¡aq÷§î àû8Ý¿ 7ß͆û•w™Ó½ÙÆh%òàžMŸà«ŸB§µNò§0yrËÃ+u݈RÝ\ZÑ|®õ †Ë³B×µdzÎUüh§MéÓëæâd›îjšðæ*ž“W‚äËç u?uúCÞ«AžéŒèóXæ¢B]÷ÌÀKƒ ×þ)®¢|´h…2ùo–óVßü —7L¬YB¡B-$k¸G%6Åüå,æë“•¹Ý¨»OÔTßã8o¡šr!|ÿ.X÷æUèÂè&-l›?•fgF“‹]i ;%•ÉZw–l,¢= ßcåø~Z—ë‡uá± ñì&<ó£lq9:oéÄÝ0À™;¬‡^µÌÌõ ÿOÛ?â q±MpsïlbÐôÝ#ÒØå«lAœ"¸tì%ç®XG¬šÇ‡"iL…8ñåÉÅêil˜|'îµåà z?HÍR¦’Ñ ‡˜ÏVóÐQ¢½÷Üi‡/é¥ÎL—ÂeÜëã@Û¤³÷+¿±"rA)ù2£{Y¾çîcmù§”^êíú‰ ½ž‚©Ëñt­$%âõ‹‚ô¸›i¸ù¡¬›gÏ"÷ÿõÌ|ÅgZBO#EÝËì)ƒ«Xœ}ÖÚÍ€~×upý¸v£I RçÉÌ)|ÎØÇÁkS(ŸðeæHtÑ‹‹Ü&‚×?ç,£}ܸŽz, áÊ}-µË,üél†a¡4ÓlƒŒE ÔÐégè±;Gß°ÿíi„c õpñïl*œ8› 6·‚J–³17ÎóÛ!s“ŸðZ±Eo×BÚú™ìW^l’jƒq8uÆ{ò¨ìó Æ¡f~¶-‚SÀÚqæ xþɨx.=£G2=Wà'ác$âÞ%4PzÔrO/ê.«Ð#oEÙ7_4¨Öu´9w=ôÙ‘Ü/ØBެ8Cê©q~T)Ë·†ÐPâekd‘4hxp³öt+5®ä'mÒ˜ý>øì_/n›G¯uÁ9g®O|H9¦”·›ã¨@Ÿ µ—-gƲå`8y;œ • UmÈßgÈßK:B.Á†Vüp© ÿ™|eöaÆL#ÛŸ¯Å§§ƒð¼·Ç#ÿ©cx÷ìÞ@Ÿó³ ඈh>Ñ¢# ¯@È® ÕßmÇà§÷ðüó¸¹[Šª{w0ï;¬h•¬>m>†¯õr!]Q‚í¶¶¢éÞâÞÕ ôÚþ"öþ]-T Íàvû\€ÿl…ìŸAç.°££Zs©žA«%hLj¥3À€mÇù×ÎatÀ|ò½©4æ6ƒrF+X’-°´Ò&}° ÕòèƒC ÔëKÐͦ¾:P¯ïVT[Ú*Ÿ\{k*ÐÊP“¸»¬e%kæÑ.v;šŒëùadÇ ¼[±ŽlH"Y lÞ¼³LÇ«R||l&©¶Ì -ðדa ‡åÀåo.gNs:$T’ƒa¶dj•/û¥1{¡\f.òxÀ3§Ý¸Ÿþó\Ä,‘mÃùkˆÚYÖø/ùg‡f§fÀþÄ$\}[‘öžZŠ K¨Ú1]W‰ÓS%Y8èw”ýS°¥øT©~I1⣶ð˜W÷tî¼RβvJñ€C½žEgUcÀy…”‹aƒx!ºžXž$‹å @Îv[9OëmC/ÎMØø­ƒ¹¸\¼s”HórNb†0±`αK]‰ñWüº~?µ¸u¹#X÷ÿYÉO¼“À¶U òvî¥z×—ßa(  3ÆÊqþãÝè [ÿh`HþK´Õ¬„—KSQ¼À-Ç­IWdr¨Æp1¾ŒA(Ójž@F÷~Aa rÀHxoãºì1\Ôã!fü dŽƒ Ÿ†Ê.çÁeÑ{&)?LjŽ„:/j¼Ó<:Ž‚Etx-[³…üh®À¹ÃóÉCxbû–ît]:™Ú4¾ ³BfÓÝâ\<õýÛ žôò{ŠŠ+>0‹¦$£lôs>š»âÀ_Íÿf#ÓÀ•Ê” \ë’„Êàõ¤Îø<Úσ»/ó³rÂ~dAö%2ßĉR­tÚÙ€ÇìapÇ <“"&RGém;A¢>ÇäJúгåhBHÞ¹Æ0Ñ_Ø;›[°ÅÁƒZ,u¢šy¾Äã;³ß'„6DºPI˜8OvÓËÙd‰»-êðÞð^bp5 KÕúÉJ/m:v¡*èצ¡²•)±jÇ÷/TˆEB)ö£Ë·D#·¤„ ì^J_‹ª“ÿ|Îý1ŸŠ'ä¢j÷T7'·Ä³Iî¶ìÅ8žKÅ~=C¿Ë™®¼¯AŠè–ñØÆk„F®O™4¯,r*Gˆl¼yŽMNQ§¼SôÐuO!yÙ÷6ô瑾өŸ9¡GÈïðCänw:åd$†²›6“[ã­µ§¦ƒ‚Õ¸Â6ºLk;Ù9û§øá)O<&ꨧº5ÛOÝguçÍpqÎ<¬lzËz1ó¤¿> bYª}-t˜n.P€ÇÆïón%£$ìë#hµ²`Úc’©`ÇNr6¢• ‰êÀ‹[?8ìPgæÅ’L¹ °hbÔR¤¨ûk 4ä9Óé &··å©àñù»€÷µ3[ìU –æ/±àH2U Jb§}ô„˜ÍÓ膠f}},MT‹gСetÃ?Ü?w*YÐ8ø®pÈYSUì ΂*_mZ°þ$s¼ê¦©ìÂ>ÝLl¸y-õ|  Ä®>Ö"®+¡kK%nøÈ®³Œ ©;ì6•øÀ¤]ذzÛLä ÎÁ þ|†—ê×pt´„”è¨8 ;æâ²¨ÛH´'Pµ¸ú”_NÍÀµ¯@u®ËÿðÌ >BÏ.ãRM¡I~1»Õ¸Zœüzᆖ›îÏíËÌc~HìÈ©D/U'rb²A&„š¾Æ9¦¤hãBDñ¶Ëk¯jÂ’2DüîX™tå]âp `Œq~8 | &¯3‡"K÷Y(€mB»UD½Af«{£@·ÎiÁðöDNöØ>F?Tì44„k‹®àÙqšohBçÍèÇ3{8G¾ØÃÞ'׃CGù«¾(ðó>¸Ç*ªŸÁ =]4m”ýpÅÖ—ðÝú‹ãoP„µE+÷ð-%\šàÐÔ'Bÿ…m$»VìÆ4!ºIï^‰8CngM! · ´Hrà~òû×\ð*€eI¢xSG†\|kK®Í&Lu^ câÖF¹Ûæt«õ'è0Î'=VsÚžÊýûìª4în-o˜NÎ\ ÀGÄ`Va¹„¾øör9y÷u59fy€„Ïý+fûÁÈõݤNl ™Ñ¿œ.ùb8×c·ÿ¥;8ÆTˆ¾cv‘+÷l1±EóÛ³iا5ö·„ý÷ù}ëðaL| b©RÄMõ9söž¾øÛ€›Î{Ð=N•÷XFÝÈH¢!MÿgˆÓ˜˜•¸ôLi>°Š>¸?“>(Ê"1-x¼O ÎÜ6&ç:“Èœíùè¤ÒÅþújZê)¨•y—,yÏÔ)ö‚FU$n½RÝ–<~ ˆBÏuÜô£†½{@‚n‰³a½õi/ 'lDHâ²Jx¡ú†»^(]Óƒï_o8²B ÖvðP¾òipÿÚ0w(^žd8»ÒlÏ<Ú:R@êëH¸â(óu)ýœâL7.4¢S¾KÐ ¯‚4 é¯óð&æ0]½ïZ<·b^þ‘`,ݵH¾\^Ôú èoIÑ,ˆžvobhu03‘Ï@ɧTvîõ nåË¥°fß4šã=©V2ó—5‚åa´;iBOpѯܗÈ×ÁÍ•¹+"Ã̰mDxu¼\é©3™ˆ×<|˜?—j`íBZ7$Î&Ð?øu’óÉœèÁÔæ™ä²Ä |qó>útt\xzž}¿ †?d‘:—ðË—)¨øD̳Ô!)  2f)S³Î‰)^lN§¯/ÅtÔ¢¿7æ˜Áîr×NæOêz*)ÖŒ¡JdFîƒÿfN³›k:ÀÅà6d\dfÍ…só…@3¸ŒÜÓ=@ë#hŒ³&y$)S W~<Ë'N7Z‡ÂéÊhÆ'~”± Ó#bÉÉ$hÈ›ž[”Î~÷ ÿ¢tŠš ™+êYÇ®tTtùÔJêÁ‰7Â(i¸Œz¥H.Ê. ÌO'ý­ü'¸/¬٪¡øGЕvéÁ¼•rDÞŠצMÅÿÍ–ævOædú ’úC³èŸ÷†ôøneXÕ¸†Èš&Ó¡„ÓlÃ7K"dnFzM’HbL,¤ í?’‰^Z7#Ae΄c]H*ˆ]ZƒûŽxÙçÁRTŒ\×yÇxsê|c@-dݾö(ÄÖyOršmÄa]11~#Gs'“°Ò`Ìsˆ#U‹HaT-2ó i—á+Ncû’~òOg"Ù·†Ô,‘#s %Á4t5-^#A>-×$‰C p{V'‰mT"Î/3YÃ.s|*ÿrvÑßù¾Dá(m7ºˆ9:$r«p[ìëðüÎWÔùý‘œÑQ ‹nòÑÍzS©'«ÛEø˜õa÷é6;k”qåLoZ* ÚɵL–“ÿæUë„DçÕÙ°-± JÚ¸÷žÉRÁõwèwo5xße_×âQyúÕa˜Û̪ö—â«mËAñr!Æ‹kÀÐ¥jàâ'8ŽQÔæç‰ÝdU“¡^d3;³FnËð¬ÑTÞåpûÔßœ£gî`\aþffó5³@Y —­†¢Ÿ·ç­8cœå×#¼Qµž {fÄá‹þý0ýÄ}î°¥Ig™"Ï¡ñ@ªËE1‡L¼, Ûg`¢ínXÿ²mÒsÀ2ÙÛWq³ì Lí+§9¿¬˜·ÒM~Gé&#”{ryo61OB'9V«‰½šyr!‡ÎØ›ÎñZéNbîÄ‚W¨Þäʨvvà‘"äæšÂvý³´Q«Ò+6RÁÌ–uJ$±ë;Lˆ<…Ù•'&ÒdU ;òŽk3Ÿì½IyH*í7%óšÞ!ý>É1µ¡¯C®k_CûÉdžk…€âUÔ»N”²ã„~ ý½NƒP¡Mõo×â£Ú©l¨ÕÜÙÄÄ+ýÛ3Q?¿‰Ê½§VssØÕ[½h¦|:ž*«¥ÏîÍG«þgLÁÁYl^_KݯCþÍTì!”ßühï;R.OزoÍ0&-C-[OÃÆ‰g;r ‡ï±–B|¤$è³:_~Î¥…þùú3Ô'Ö1넚˜×³wA¡QÓfDûІÙ=ËÃÎe¢”.áбˆ…4"å:ëýTc]Ša­×à¨żWÛFõl§'JOŸú@ú¼¢»µFb>ÑYG—}åÎ4Ô'ZyŸ Ÿ‰k¶7¥H³CŸžƒp5wN–®??d7+P^mH+{«aŠíd;$°R.k Ü¢·Ì2#ö_æ«f+úpON4\ó_ÙWÿÿN"÷¦ƒ¶gœDå-tÔ©…î4£¼ÎÚ”W}-œsÂ#kü“M™Ö»4¸Ò¤µÎf P“©Þtט*É]LÔ8Iá›äÕÌïpºåÝ°ß ÂüÈKÕåäoÐõßqÚÂ]4·†Þº(HoCËJOJñêDÚÁœ¢)óÈ·=—1±Ò‰}W·— N½Kžçìdè´¡ÖTŸÚÐêƒ=íÂä¨ÀY²é´5ñzjÌ< ÙŽzbD#,ƒá£I®XêÐ#SHvÎZ-— RjÉÌТQ¸P­JFžýdFy÷sJ6£yó»ÝȆ®yoMÏý@ÅS+hMX±)˜N"Ÿ“¿ÓæÒÀª¿Øk³É©Þ×eXž:§†mh¼_/¬§y,·Ê‰=4Ÿ =Á³Ùõw7ÛmõTR1‡L‹óÅ‹ÿ~ jª‰×œ€GD*ÿ Ùlv‡Fks /, zÄI­ˆXG©‘ÝÍu4¾Â….PÒdC#Åh™EŸù°`­çç_b£Br‰œ5§íÿõRsfý‘€mu»@ccî){†ÏQ#!æ¿ÙÑnâ÷À° Ϭ¾MoŒW7S"E ãÓéå» ,4î —¾Î¡É+-è/¥F|øK¥eȳš‚{`ÖÔ‹äüêéøP_ÃDúpòp˜™FêTdƒ=örõ¯ )ÖÛ©žó¸›Å„”“”s+ˆóÜ=äÓ\ûb†£‚s#öHJQ›Ù¥Ì»*_òÏ,Ù~?‹£ë›Då#}HøÚ6¶åò||1™fDnCcIìºéBêÛUˆMöÆÛ?Œ·:£ÞJo"©H¼›IV5 þ*]0ú1šS{@z?~AvtuQãŽ%páïI6¦Ç®ÝÆ¡;9àºl³òˆ'ã1h‹s^†¡Ý—(½ò‘mº,@,Íè×L˜®@Ú®Á=~ƒZZu'·iöª (Ýë˦¸^‡À<{z¯ùj}†UëÏ¢ÅÆz¶ßuSpô 3¸H‡Ì™äg=¥ØÌËt®àŽíK¢jFFè ÖèÇG¨h’t)wƒñàR*—¥E<Ÿ‰aM(Ý–™Œå§­ˆÄ [èš¿OÛ[³¼CéÞ²«°Ü}#1Hä²;b«þÈ~†¨Ñ¸:b!ˆþèg*>àÁ)iì¶Õ'@²]—<©Á"•º‘È oy?v¦BNG3éë`ËS+’üA’cšñˆ,Îf—Ë3Ûb¨QË‘ƒ~.¨ =å|û¾žÍœ“„dp+9º¾ŠF­£–y_ñö#»Sk) ZÞútÉ!xhiL†o'1ýé²§ ã])4mã'‹K<‰‘Ï**-¡ƒÉ·Ãðà’ý8ß] ?ž{É\ÿÙ†'ž¯‰Ù"d¾å§ÿö±–³*èÍõâØ{ˉ.Ÿ“PÏ&ш€|p0~NÞsïÐNõ$æ9h£Œæ-ÂÛfC˯ð1ìì p¥r׸ãÞÎ$\3†lØäDnõèAÙÆ 8m?Fê¬"éšä»4ùö8ûS± 3ËÂ)“z D}Çp¯§Á$&åáÉY–ôÈŠdz3ЇdDîe. ¤]IƒÌðÚr²­qnþ=ˆÖf>¤Me/8'^ƒ½SIͶó`rd=½¾C|%á/«ŽÝëßãð›!öÒàWÖ"¼”Ë÷>¤Þf®´ÊàqC+ f/X[€hv'š_¤wrŒX±Ô\njÇ_惻,Ñþ}ÌWÅáŒÉõ:ÝjR3åŠáèSH]$=½Ûéé˲à!›J•¹¢ù¿hú)§ùWð“‹&Ø1ÇétÉT HæûÃJ­ò§{–¬ƒ@ÍЊ‡ô׋©ït1ºY´ Ïn'{ÿ©Ã‘dÜÐKöY‘1µr>þ}º-ƒˆ 'š»ç)n/îÌ m|ѵ°Ek‚»_ŠB[ÚIl:t“Éë†W— `¼—ƒ·ÝÞrÄÉ >Ƭ|À2»ÌI¡)Ã;> ÁJ·ØäCŽô¿{{eC¤®‹“ºË¸ÆfamM2“.ݤé¹ZÊ:+ãmm~ê v»."[˜1¶‰“‹µ} Y~ö9Þv)²œâyö ÷W©?ås’%ì’zlžA+>åCÖ1uì?ø’ݵ‰§Ál\xSKp/62ÆÙõÜ'sæ@¥C0ª½¬`ÚÚ_CâŠ\npÈÞ†ìveúxñ3¸^aJ¦·Œàçk8xá6s;¬ýör÷ŸÇ_çY¬|÷ƒY(»k\ƒ¿$EÓŠã¤D<†ÊYYÜ&{zq…,Ý:©e^ìá¡É¼7|³‘.ŠÕ…Ä«rtSa7ÐR™ Šì†b‹d%6rß«dD#NÃÙÑ,h`Om| ŽìYïbn¹€)Ó¶¡rÔ#÷fÎ>Ĭ·ŰrL9ó‘{è°ù¶È *'ëÒÎ8µ±uŠ‚èbñŒÉ•=è]–ÝÃÌ©Äe\Ÿ§­ìÚ¦4XNÕÀãT<6™ks®'f0_î¢÷Û6ný\;ÊÙs5Nœ†ìÛ»áu‰íq»Ï„}€ò Lhó™8 j<äÓ pá“¡ø—=^hAýJA0÷$âJtÖå'õO¹ÏgN%ó 3Á5Rš2d¶ u·“ûw› I”Dù/ið¢Î“Ù¥ûE7dƒKæ‡Ðƒ÷"Èó™ÃXmÑ„/ÄVڱ砠2ÌÕŸ¯…d‚\ËN\”{?Å0Ù‚ËQ×Z–¾¬‹›ÌÒ£ð^bIö~ÌhÑJm¿ƒnï/¬}·î–£}ýVxì³€¤Ïó†ï ëX-ÑbGVMݽÆÂ@0ð¼ïÃõñ„²#95`-‡éžlcøîÆO¢/a@í|·ëË£A–:áYÿçïIÃÙáì\‘ã(#/@Í6;¡oö]¿9Ȇ±¦é|šð1‹!³ËÕH¬ò î¹µÓp×BvÛh6×GƒóH#êð‘ËžÚG…9ÉÈ(½C'™<(—gœõh¸7s3|=ÂÓ.ô¡g— l­çÇ@÷op&‘v¶Ö@4ŸzÄ‘ùZÄÍ‘9wüÿx9fˆið[J¿ÜÕd¶HNeŒ¿õA~UW>93𥛣Äe Ô›q‡ÏL²‡}É‘ÔR&#ÊÔvëL'ð –î7!Ê̸ª\Æ€/¸oC'pÌUq·$šHtlzÏ´»ÐzNÔ7F‹Èþ<o,/{ma®%&‚Z8‡&ne˜Éã oh2Ýò}’Ï$˜á 9¨®zÆxŸšIÅŸdüˆ=1zæË%`˜j%ÆZb|ŽzHgýRç1ßâʰ­f5YTÕ„½ ÙÜåP 7Ñîò+ˆûõV¯|©±OqOK$–%Þd4}ª™Çf‘”ï~ É“îdFNå“Z5:Um%8Ø £bΔýÈi/›mé¸ô[6¨ÖܤƒÎT¾á«˜^ WüDh'§ÅÄhÝ{]ââ9å¿^9ç$jAJ%‘n:!F‹¾±WuH_R>˜TÍØ/L#Û•%鬱p2¾SKËÕ˜]uŒ{’­¼Õ—Ï=gRVüÑé˜OÄœ$nÌ¢+Vc£ƒ¯âK=s3,GËE#hQñp¤]^óÂéJK¢øóf;üåN¬[ŽCžfðC*`'7«@âç-ì2ÐÍr=¦Ò{¹Ï™«›&8Æj²ÔÛWžŸäB¦v&c—€<ßêÀ6Ë„ÎÆ‡Ýý£½¬¹\ÃkS¨ÄË4–K¤ˆ¼ø_LâœÅ+RéÌS/büí|ãmF‹«Å°Ïôó´¡Ô†EÉçƒz”<âw§¡ã³Nñû7Pt]Št·¦ÁÈNsZø·‚û¤(>°§7Ù´Zxxµ‰ë( yGÉPº/u‹5§/ôC Ö ŽcÑ’ Qk÷sµ.%3o÷î ³.€Fó*Ö›¡³Ã:šÖÅGŒ¾-&’Âuhíí‰!Kò8®¯“ÿÉèãÙŽÃSyiíd­ÞÎ×wã¦sU»£»g93øØ€ «dŸÄd‡:€Z†±y½’$%ŸŒ§»œ’ç˜ôK»<•¨ÙÉ2täÌ¥þ7C•¥ ¹ñþ+éVÆŒ+N'zïÈYõBoñÓr—%°¡cûe–-»5“Žz% G<<ÐM—¿1ãšØ’BBœìuéíò4ñR ­žÏ<ÏÃ7G¤‰sJ6£³!,“Ù€be=L/ Þã‡`qC§lÝ=î³ß¢ðÑ=½|ßë„-Ú¥dµÐ6úðÞ'æN™&+,¥ZÿLˆ™ãMÆ{‹1™žíÉzò äže¬õ3nSòðÊ­%p¿óúý¼Å›¦À O~*¼5çÜW†{#uøÃKeäãzÏû €U`øY¯ÍúÎr?dáJí­¨lp‡='^ÃÀƒŠÉ{ó?¥O  Ÿ‹‡&¦“–kÎܰ•=a8Sš>iCFÁ ÷éjÂzKu¶w‰ Šr§‡^T€Ð`miÞE‚í2©ß¼$Py#¢§ŠPSó îqÎF9ÑKþ5T~s¢/ÛS›;^Lþ¬õ(D™¼…ð>£˜Ýôô Üâ§ÛfÕC²`Ü—øªWv²šÕÒ@7B} ]Uc åÛ„ñzƒ+K¸ÚvÆømæ˜.Fšá¸(ý@«³ýLÏ)ƯV„,NšJ"6Ú±Ÿ,¯âAMaº$õ'|;È Ågµ¼Ð-æLD?]¶s7˜Ù o‰:É^l¶÷óÙ°^€®ë&L­ú*Õ:ƒn½&Io|½ë ¢ÿÇÑyÇsõqÜ![f²³·¬Ï=ïTF¨hh—II{Û›QVFÉ •ñ¹ç-)•hP¤¡½”´•úùþþ¸÷çÞÏ{}î}¿Ïy=Ÿó8ƒßcÿaM"ÿÕ_/††‡ˆÕÞ~f÷¸UòªÀè”jˆzeýü)Tãø^TÈ4'Z±¦Ô+2;L]ÿË1MB¹×™¢&~âUö ®š“Ç™áèœb½]“û†ŸpüÒ¡ 'ˆíƒŸa®b{Ï‘ Âý;¡óÂk¸§xœ15ã#!NVdÀË–<üÍ*-ÕeçÁa›«xàác75 N ÿ`êlè©XKzÚ“fƶã¿öÝø€»„4oZDÅÊt¨oè/ü¿ž@6÷83˜Ç…¿_Ù”Çñì˜ÖQ<9ã}Ûð…#l”ChØX“ã*|tJ“Ýõµ†Z†ÊÃÒfVkƒÉ«ˆ ¦HQò©|ÕeHwƒ(9R‘ô-žå{ŒJ CÌŸˆç¬ä¡cð1œQj#/ÅCððݧxyÖUPÿƒGËWâÎ(x$Å@“Ñö·uߤ?½R{pé6ª¬¹§5^º¨’UîjtÇ^º³^;˜–èyÀ™¶%\AÉ“ŽŒy$ ‡ß‚qn²ˆJo=^ñÀŽflï°Í=Fµ6‹A[î,ò/º¥r±´úz T eâß3Ɔ\;Áž¿;—ѼD]® €÷£¸ðºÙµm;þ|7ˆ” 'ã‹ÁãÜt’rç TÙ‘ûh ©,•±úIJCºäˆçV\°o÷óâ`f‰Ð^Ö³Q—¼ÐÉAj¬Nè#)xb$BÿpÎ0‘û¸L²Û–ÝÇžá±€èW;`Ëž©ø_ÿ¢}Øéƒ3?)@ÉQu¸¿ã jDû³¢·CÉà:ÔÓùÊžx^ŒÑQw¬gX5¶­Þ~œõKxšk üÞwØ·ê;ÃÊŠnÇ… °½ƒóü„ lÖ O¿¬ ÷¥¬;ÎÁ}7@iõòrõLök8/IÒ%‡yrZ%¢¨š¸Ã”ÍžX-Î.0 Jñèq,ûê×°Âéƒ?ºq峉jஸÆdð9ÓÐq)Ø7¸1¯‘©ÕÓ&å±ò$Ho1q ±Å ssŠ5‘ßZfƒøZM–+ÿ宎ïœC¬þ„àcßD`"Ö;*wh@ð— Ã‡;ýwÒ³ÿJ±ñ©á¿$ÂmQeÀøÏnºj„Œž”!MþIİv!(µ‹r?-VD»¸zìг ¾.¹4coÆ& ´ß0šJö¼Í‡¡'ˆ¬õT˜mîJyJß²ÇE uJ #äÖ0ð&5Ûá†{'#¥þyŽgÓä7êdIsiT›m­Dñ˜+Ʋj›–÷?À Âíê%ɗʽdꉰl½)•y>D¶Ä¬¥‹ç9c´@%–\•§ó›ÎÐe·;¦ €ɨ!{ M鹘Ë}D縈±ü³Ô%†!g `sQ$;@¤3ðÚ™vÓ¥Rö­\7cæŽ7´àð[gØ8uÚ¯aÁóãn»>ƒTdž£àb~’^ô=õ©×äM§rÙW÷F¢šº™.€IBD2xRŸ¡Ó `ÔàÅâ«Ðx/hi$ˆÔ-Å}Ùxua>,æleúÒ3³_øãd:nþüÖ·âWÃ8Hع?cXÑÎ÷ìòûiØú8•ëZ+oG•ƒƒ \ÝÄä½>ÆØ¼W‡ªq0ñm*M»}o={ˆæwï3fêâÿ¯eI@¬]3 ~”†{G~1Ö§Ú™¥Óàâ’H|dµ”&_žB·^ndšvÞÃm{ÉÜÂT|ïQìü¤E¿Üá§Y[ÒýÓ›ì )ež$Tص=ˆT>M.EÐ{~‰ô´ñÛx‘b§Ê³áê~|ïÓ 1I%¨´Hžé8†ß´>Àåá¬y?Æ~ÚUŽÿéQŠwŠ"Ñþ® «òf7,ÚØ—Á§Y\ù²µ(Þ$LS1ÿè6 ~!Föûͦç&àæ4#ocAþ¾Í¶ÃÝ¥ÜÏÔp~⯓!øáÞ”jÃfort® ÛÎGÌLE™ñpLÕ‹¤¥®ðõúnœp8Ž=6ÀŹ»ˆœñ ”;8ÁŒÝÓ§.£©O¥ ½xÒäï>—7&d÷z9È”C‚^"^zŸÁ¿ºÚãÙ‚‰h* =ã ²È<5b`~Y= M26 ¡m¸ 9ëAïMmÅðXÅ1/r¾å"zvCw[ uïŠ%ùOifÈòyý!&åa•³Ÿ ”g½4#{uÈd‰µæ‘9÷¸ìʃìÚ–Y˜n[K$¿k‘ƇiÌ$K­ä]ްåf4ýþý;~•*Ä;7üè w J!ÿúüè@ô&bΜÇ÷[ˆÔŸ5„Æ@«áÌ ›R„f{V’Ww•érÇdú_¬ðUÃL¼cLÆC©ÂñûŒ¯!Ù½%œ>©Pc´ŒWR©íëßB.•C¥©Ùï1!6 䬵)î[‰©gi¾—J­ %*ßr˜ü²è­¨Í7Á7;AzÛï-¼ˆ H>¹¸¯ ^ÚóQ1áZꤔMw/oe²´Þ1æ+‰È Zù¥ÃùçðoϦòéqôÁsòú[<©%hôæ¯8’{ªñ}l“4騮ÇwUw Zi„l­RÑ+Øê¦E‡C6’ðÏLnÛC°Ó_F¯œ`ÎMéÁ{&Ó©‡Oûõèë•eNlÎ?Åî£ålï`öiÐ=ÇuˆÝ©ÐuÏc¢µBÈœ.E’²:-uu9ãóµÀr, ¸™Â ­úï½Ëçv »$ì Þ?}»‡œÇ!@)U{}iw® 7 P…fC¨@´þ ´´JR¿ì«02óºVÉ7Žz+?Áë]‚01µˆ¹Ü~!Œ}³äñæàQt¾kŽ|ӎÈd<^QÀÒoOaKó*²·Èެñ‹!‡„[XmypáŒÕ)hf¾}Þ¢Û1¹úÎ4vð,9$I6þP"ÛºáøŸ2œn6Ù'Æ=Ç•‘Üäá ´}Z­ä2‹ªrÀÈ!nKhœ½8cðVˆ¡Eìh}fAz dÞ`>ovä¬hù„v¥¥0·¨ŒS°ãÏjì^/LwD<Ëfg˜êŸœ([2míg¼p¯ ³Gm¨MÔ)ETìÁ_EØ.Mc¹ðù%ö˜ÛAoõ@ldO±‡ý-ðKoFŸæg',®ÀèëólÏÞfµ¾ VãsÛÖ2/P¯q;˜ªñÑÕY#(üt†VÌi›Ù¸×eF²ósH˜Y1÷Z€± qdx¸ÓH©¦>4¼ÔLÞX`G•éšuçÁôPnÍÛDìRfpl/$ÃÍ[9{+ìÁ¿g-)ø{ç4}‡ðÃôU`8åXÁñy&Â-m^Vw/àЙÄHd®Þ!Mã/⥕¬ê•VÖî³3EµÎš¶XÚÚ=ÏÂÄŸ,{ÿ~Lž£Î¿³ðKnæ áG6 }|{›ûw¬|ÄÂËw¬ËÃuØY]ÏØßÒ$ÿåqŽ@*R_K2w>ÁZ¿+Ü ƒi8³¿”ÂXÕçJNŸQÿ0÷ó"H!¿,]pÚBÿÒ÷=‰ ׫R5·"¬\´€£ìEÂNÇÕÖ˜Ñ]sd‰£/¡÷²r6ÊnÑy<8¢ÂD+ÇÝ/]ÑðH7hÄû‘G1.w{ÊÒ}×áý×±x²XhMZ¸%¿±äv°*îz#É!p6Zšt-¶ œO+0Ôl*q•¦æ€R]&ÍÖ§µ÷«!¶Ì§_¢mõ‹ÍË~/\Ï#}oA5Vi€i÷ûº*b×Úñ_®gh<ïÄ,]êŠKGXÑÃÐóò ò„Þ‡Íjºôè÷¯SQ F¼œØˆé¦žèsø™ã›S÷¡×•—tt»PŸš\øe8ˆß¥wCÄîE?±î<ˆ²wnª½‘D¶s-Ê·ì$/?ÃÛìEÆJ¶DF¼?–gÃlõg ‹G“‘ Q¢¬ %ÜH²£hk–ôª¸‘v·XR?'—¦ÿ ‡S¦[ùHºä¬6”íö¤¾²é¢oïáõ;VSkfÒ­Ž?Œþ`TL(©¸‘ÆI9™YD.± ¯<;Áœ\œ‹W‹ã çÍ2úÓÉ óów9R‘ÈfE-þCäÜ–0%î ÎÍ CæÃü{<=ºÒØ@î5L,Ëb£û^r®\guGÒ¡üÀA|ûä FM2Mƒì&4(ƒ”Èð=C²wSóùóÂÿçYžñj«È@[ÞÝ`4æ]İœ³øÉl-8-LT—©ÒsÜäýúø´åANó"¦Îvǯd5Ê,s`; <@#B˜ŒìP@û‹Üßžr8Õésìµ<þžŽùéѰö×+uîÆüx~úÒ2ßÊÝbO”=‚ùßãÀd™u<³àÔuÜÝ9œ¹Daô#ªúFãK…Qõ©¦½­üTOôd[q„õIì\‡Qùå>å<?d÷úZEŸ°dPÎTÎ!òf]Ì׿è?ûóYé4íôZÁÆ¥à“°÷Œ›ýqЦ¶­~E¢d±Šgëà#Œ×Ä–ÅH0¸Áö©ò“¿+ƘlN=ÌéßÒv54ëóõh@F«nj@_ôgd´1%P‰˜Í;FMúOùcáø#<€î©ÞBjWGÓ¡âÉ»Õ,;6g%GAì#<ðÕÞ\‹ë%¥Á›íÀëÎ\²íÉA¢ä"Í9»2t¶y‚Sç5p´Ô=Ñ›¬õ¯øø!»­z „ýoëiôãô}cøÆþ°òuq X9“ùtf5’žA<’¢r‘!ÔRÖb’ÜI(Û•¡*Ìɯ8Ö‚Ú$p®)yз¾¶‡›õg°{ø Ó5+–üŠ9Â^˜^€‚;:àøî:\y‡Ädu°ZìÖœB¯&j“ùß´Àxïq0øœÍ>ýL©Óx9q'g¸™Ñ´% Ä©áüïYbÄâ˜h-Z»t?1ðÈä¿6íæ/•@=µ˜>ßð•Óùö¼XD¦k“lßd0€Ëœ©sŒï‡m;ánù ºï VøÓœ¾dºèâ,º¿CÞ]o"¦ï.Ñ !6˜ß‡$í„7 rí œ¡ó¯ž‚™kJˆ¡ú˜r.†»…÷"Éæ&cÓÑ5ÔnÑúRqàÂbºï¿s(³\J^Jæ0§ggÓuû·’ )ºÂxœ¼Bmc«4½# O5™`ñÚ¶Z—ìV›|î=ÏÐÆmˆ¶¾árQÛ›öt?%ó$N™".›ÿ-G-sPÿV9}•‹3ÿêÐØØaj”V˽Káôå¶Ã¼[nкa;žM¥ç*0«âæª/Ø~ï+Óø‰Ÿl_AÜÌ·RÍ%á .‡u%û$ rvÑ_×µÈÛ‘ôû~Æ7m¯ƒ–€Å¤b `ò¼Â”{uL`e ÖžSðˆ so®ñR"¼ßOд¹²ä:ß Üo£rÂéE?3ª{=Œxlëh3ûrç¥>†´—ðìóótù½=´ÿàÊw³ŒÞ5ÌÀ‡·£ÈÛ¢Gl¿™}qŽ]5•^ö;NlüåÙ}6Üw×càÓHªs,+ö¥@œyQ]ñ Ÿ7tã‡Ñ»ðáΜ{&Š£·ë¨åö0’çdˆ‡zÃá½ÚÜý—Ëü–v :çäðŽ{(ÊÅT£ÚÓÇp®ñ-ÁrjÒgÛ›™ÓXÈ<Ïš _?g.(‡âs7SÔÁ ||1•y¦HM–ÅáµßV°W\Î,oÃkKµˆª‡,ý¹[ƒúèÆ²rí^ôß<(}»œ;1Áz?»fŸÊÆQqE :}ÛRú§^þUÍÆ ¸nö-Ú·§äO~c݃]IÕØ[üã¢Ê&”J’ãë7’Õ#3Ù;ºtz’\ˆpd,„‰ùð[6n< igPƒO‰1Ÿ >žâ›ÔŠô“«:(ö$¡Ó§3x@Õ™zÄ!]#G÷ô¬£ßg[âU˵èb?"®¢øÃ7h[ =•\œQÜ‚[Ò”5»(¯œók] ©|y‰zôÇ©pÜY{ë]Ø[”Ì:¼XNF 1se|{ £97qÎ,+¥>ÎôK)ÒYõÂôù5}b²Ð I•Ñ$?üáZdÚéÛËhjçYTùœC\ö&à»osÑäÙEæW6ÿ·¹Ðøo¼™Aã½ ]Å? jµ£¸Šï¬ðGÑñÙT§ÞœÜ«³aBþnׇ5°ÒÞž¬‹îÇòyòàê<¨>}‚.à+¦w„HjfªbLÃbpüK>É !ÂѰïe3-¿ƒRAqtöÐ&Ï©u”Š1ót(ÛÜð V>£éM®Ô×A†&•o!³w¯ ÏV„±ïùp0?’CÄü°tw<|ÜáNrtob~èkPõ›-²ð$‘ØA##‰üülªËCÛ£5`øÛ0¾« ãüœÿžØW ÓY¦rXÀûœ­‰èbJ­ ‹ùfÑ5cÙL¸Ý ²eü íšÔàuƒ0TR0?…=úÀ®Eßaœ/œîNbû0‘\Éð ¡qtÇ?mžFöù.£9‹œÈ¿ÐÓ ×¢¶3"f!ø>°Ž61ô¾³Xû&o3òç78éšbqx#ø›‹¢öÈjÔH¹AÔÀ ²þî%&ɲ˜ý½ˆôça¶»È`ªÜ\ÐbB=vôÁl±óÔØˆ‡òÿYK’5q‰å9‘Íõ[H¶eóé~Ô»5Ó´Dà_r§qÝæÐ×ùèù§Is÷ šï±¡×Û$À¨M†þ¾ÊÆ£}¬Pa;žýSzæ¹p0´nZºÅ œ-×N€óŽ ptäíªäÁØVˆÝ}ÏV ± 2ä‹Ùä³’%*'6ƒ1ÿGêÞ¡ño@DòLÛÑÊŠÜ–†ÇîÔØ¿‚ùé?Tmá§~™p¶Á”šh_š\ËnôÕT.ÒQiòch Iì=KœzÞ£õƒ×htE Î$Ì€3ûJ1.e˜L·e~—%qÿ¦ÿ¹rÔ8ó.Á_cZtåÍW¸}öÖ˶®•_`±þùž”½ÆâÝñ(õíLÖQÓ l˜&¿ˆÔçSºKÓ‘þô V/iÚêé0-’bn’¸[ ¾ˆ®Íép­àk9$²Nß4Ï:‹a¿y³«sÒ÷iÊS:ºßžF2ÿU[zzôT¯Š#¼|1m~-šµ¿ƒ"סáá~ÂU˜ª>zÔ1Þš¼©Ùs“áE7¼[rTf‰2ϕҩ§ˆ yjúƒ=èqwŸ;„ZsõèïqYºwZY§ý–ÉôšNÞóÒÙrÉÀ©H´@/sNy<™JR ˜Az*¶…voù‘×qû·hfûý›à4ý×öw"½óª-;T kK™ò‹,•[³sg’Ü ‘pH}*Ñ{ºŸÞÓ¬b·pY¿ Ð÷=ÆîÖ¥æ²Dû¶8½x3² `Ïx ÓŠôüë@¦þˆ¯23R8u*©2@µiÇ)§X…Êý»w’;ðØJaªuÀŠÊLp\VJL¶ LU§_ÌŠÜÏ º-ðÔëLÕé騥$J$j’33¿@¹ ‹».Û {Q"çÒ_|Ð} Ò‰Æ–Œ€©Ã8ÛSš‰m(fýHþ4Tއ`É[wVl†]ÖGß¡Âõ/8&»Ë£38¯9)S…陦ÖbXvÉ ùµ”HzåkTÍ-cË£¥iÆœXøo&UsŒ9?ÁLTJÀÆÆi$XW‚NP“åpAOœ¬àçv’en¬…³¡¸­ý{ø!X$_¢¼÷0ü¿î²›‡©%ç:Deo#¢o§B™ESfO¥|A‹©¹³ yê CV4hÓÝÚ‚èjÙŽ—ñÓç÷Ã>½#àR²´b’@h­5V ekæÃ7ò¼y-M2â´FÚ²ïg(DÙ—qtsÿ´ßv†S<€x?Óßápz^)nOê†=bô]~3|nZ‰šÄ°þØ*˜,IÌòd<ü‰áIÅ ìzÚ•òŒ¨ ’Þµ ±Š¥" ™ Lcrc\}R²§Èts'¶ÆPê 4"ù½ºa˜Ž4ã×KÊT“ÿŠÜ×ÄßÏdiê?u"ñË›uõ]ŠV·#IõðÈäxÉDövªGá×[á4¢Ý“¸¦ŠíÌ}ã'øÇfMaîÀaWúßܼÊÿBµx+¹”§B%Gì‰ÑÉ=ô¿qÌ…ž3^¾±D‰',»ïOf\à$TeCüõiÄÑX»n¦ÓýÑ£dÝv!Úù¸‘ù~x&½>¦k/1 vgýø x?ù\îÐdƒEC˜¿Ûös¦”O# ³¨åV>²6Uv]å'‡9ÕìÚ×’à»J’kñtg~§ ‘xIm&Ò‹†o‘ÚÔ?õ“x6ŠÎ=¯ VaWê2/M“܈Š!éï+Ø)‹¶S…;aßuC¶øüÎÓÐæÃ‹(Ó9Z—ᢆ6¼U; Fs…Ñø¹- 5דsIwY$ñÉ“ósñèO‹oücEMÑÑÃÚ:Úp&¶©Ö~T–ÿ1вU1ðem5< §t޵»5©ßN¢¤H;8GÆÁië°.ìsK¥®NlaŒ|DI„ÂF¸·Šø {á­òÜÒÆ($@¥=Ç?%¨B¨&L½‚eö‚dlÖøl; t¢D×L²¾Y Ü^•Ž»›ýá)µ"C|Ð’|7¬‰Â gÒ' ‹žÓgNãl›Ë°â£ 9·1>'UÂ>B˜ìgúK¸Ó /w<…¼¡ã»÷ëQâ§ DýÏÀý_RhÑî+Ì’Õqx Ï˜¾Zc¶Óä"ø»£NÒpnÑo†XÇ ±‹CÏÀ¯zZÇŒ¦üС–A`ƒ«"l¨³¤-;z±%ÎE~Ÿ%o b³˜.—ÂO5†$I²•cR!ÿlqkúƒîŸ 'YäæO|á–KìÌ‹@Q3›~ø 7•3š§B÷iØñ¾;ë€u¿wš©6/a|Gø š%³» ~‘JîÇWÛØ¦S—9úJá±sîßX€O¶ºÁ÷Žó0šMmNƒW…1U¹3š>7©6c¸#ÞÌþ c­T¼)†d…HÑŸh€'—úqWõ®gƒÈð/ͱ%lÌ,ØQ“ #b³èÉ)ÛñU•*†œ/a>Ít¦꘬Ú]Ì&›Pðíò$z×b8kl² þ‘ôr©µ˜­Iïü*ƒg/aõq{TûèŠñÝ5 Zý-kîááŽø@ÇìïDÀÇMƦ¬Swj’°;7¡Ã9]„: $AÕ²•àÁÓŽ $[uØÒ·?üÙ²yötÚ—£peëŽSŸq s ªÆwáÚºepDÒW ªWNL¹]0̲“€Ç)låe^Zz¸‘Þ¹Ž»¥¶Í|`pÖ&K&ì¼.9£? ÒT°ºbÒ¸«HšÄÌQÃŒšìIÕ>ƒ.{ƼÞ>ȵizÆ>^Q +÷ÞÇ_îõ ×“ÃñÕâ§jËI7—úÞ›‰3ͨ²K"l©]Ê”XL¢ÃøÔ•‚#¥ìäþ1Æ•„×ë®ñŸÃ©¸rœª+èÂÖ—GÀne*¶m·‡E¶°´¢sº39‰Ëžÿ¿üånŽÖ‡yˆeH!›‘≫r`4iÑ”ßL—ùW²®ʰ10¯Ê«c»c Ó¾á(äï ­ûŽ¿‹ fZÊô·ì¢0öé:žz#@Þª›ÌÁÙ´y—þé ÇóÒ°z†=5\¾š¼ œ‡Á¦ðöI;J„êÍ;`~_;WàËw6^JOî&?”ØëÏóÅÇxÝ#…s S“;Q‹Ÿü7ã=Å(üÍõs”ùÒȤ.Ë¥ûÀ <ÊåðÔl_º.ΆI¶ØO~cTø¹l™ ƒe5­Èçƒõ^¾ä{Fýâ­É2˜)S—£ßW_…÷¢;pûq¨ew0sGcðBl$:Èû£Äš´«ø^ãÝBû8°wÛÇ%vaÞû6Är]ë½%¼êÙ‹£ °à`¹öñ \-@ÁÃSØh×qä»kC®]Ò¢®LʎÊ•èÂÃBˆ?SåP̾Yð egë“Ó%Û!af%NôIR·ü\Ü÷6ÌçØƒ“ƒ';3‡”ót3÷w„€Eª ¸}nÊ$:þ6—¢í¿~´ÝuÁå·©9øƒ«ÂÜ:нùðZHl¥xw]$&­ÆÃ¨HÎ8p–-ƒ¥’m¨Ô×õ›Î€ÍÂhHM¯”J¸OÐíi\p:†ó3àÄ%E&!ÁÉñ™†YÃspÁ€.ëö&n$ýCá s&¤`df^Ær³}ÜØ¾4®ÒÈBÆëï]6y¡0¶ü®e^Nw†<Û ¬ã‡ËÌ&ƒؼ{3¤ßŸ†¾ƒÛoNZr$Wö”,šbMÝSû ,‡‘~݆=ixÝ`}…í¹¸ KoƒMÇs¦¡Ÿ}ÏJþ 4MÄ?Xc¡·X®¢IvÞkÅ—sc!ÂíÇüy ËÔ¨¢o´žúzȾç…%LAÇÍŠpÏÉ>Â3(8ËåÎCàr¦Ïã,ûéU¯“¤eOx±/M‹VhıAê-h$ ôŠþãvÞ,Í=ߎãy—Q]ü"¨n1=ÉJkÁòç^ô÷5 Ç´hM¹w…àËæ1Lk¸Á L˜Ñ{ÁnÄwÀÕ'žcÉÊfäŸD£Ÿ AlÐì¶ß¡ç’c\ÃåÓ­c.3áûu©s°?ýºd7Q½ !ï¶ã[ /Üsó/þ©IÿyÄ;:hNaž7Ýã`Én^›D Vª±6Ï>Àã¯eÌfVÄÎü…§+æà·tQêª µ;>áæ‘LÂ#ÊÚ™a™£0Çký¾pœʰ7·³ÓÎN%B'nbÝû¶üÛlêë%%-(5Æ`åà HŸµ‰{þË]fçüD|¡¢KÒ,gâ ÂÈ'ÇôË/ 2öÈ©Ré’Äþ´ÜN~Ïz¾~ÇyQ¡@ÿÚ¹ÂÇÃdnÇŽC¸)óÞÆmZÑî\B×$ÑÚ ¯øÜÝÝ—ùb¬T¶ÊfÓó{qã¯n™[ ´?™…æN«©aZ:ª$Ñrº˜Æ—”³±ëÌ‰ÚØ_Λg=ÅåàÙýàýâ/d> -«w€~ÙT²Rq’£dó1pïôÝüI¿q¦F=j6Ã\y<“ÝŽy.ÌÊUñdÅŠÅh—rŠÝÂdu¹7„H¸“­{,'wËã6†–¢Ð±4òwòqÝÛÌXîBš>%ƒÌõU®ª¤zyN¹-Œ·x.â'ÌÚ« çøê¹ËD{`ñ{ NÆŸïÅ3›cQÔ)Õo·²î·®@«Od3Q\»»å  ^w×à®`zÑ_)YÁYÝçøè’5øÏ"êrpüžr]«ax– :¶¯EÍ“ÏYò ø&U‚e"öÕ˜ÿæ2¶ ¥–Æã³JMæöêBŽø|Gý%\ֻРÚ6AÜÞ Î߆0>¬‚/¿”8. íaNüšÉæ7ÜbšÃ£@oŸ#¾û"M«®Ï$×nX¢gqÜû ºoòÙÍÜf|g>æ}¦m¾„³AÔÙW–öÂÆù1P·_”Æd^AçÓˆÐÊ9ÄÍ>¼+Q¨h/TŸ¯ƒ˜÷wQæÆSLY~ŸÝ/@ÙÕÃÈ»º¯óÒ] ‚ðî ͺ>Œ>™‹`åÌ2XûÒ JÏbZA<¼YNQâuÇæ¶_‘§¡µ5l€{*,”a#×m!³§Ì…s²<Œúܯlbî-¬j¾À>˜â†tè½L9¨9ø ¿;1gßt#XۛáV/ðŠú1Á»JØš=Âðê»%S±tÞÐ"m„Ÿì4á6>q np&G‚ØérW˜CN_ao¢hSñ>Æ ìð÷ÍïìâJo¦óÇR³JŽê%¨ÁËÕÌú øï ý–DAÏ8 kIGòGØöï4.ÎÆ‡«5hêæxÚ•hcißáÅZ„Ê⋌ʹ§Øï¾ÿU•Ào“­fYŠÜ°)ì¦lg¢¤ø;žpFãĈCápo(€á]©xõ¾;{|ËRÚ%w¼çKÂ~ÖËyˆu<ËC*Î=Ç{ŸÄWxžÊÉÃ>ƒD¼{\ ä’w°†ÓáÖ¿XO—Þç-Ì…™Bdßû‡¨ðæ)|Íà(ô•ãÓ¥ð\³üw¹ÓR‹7¨$ò”5KðÆ5JYøÇµÕO*’‘parrC==’¢Ï—Í'¼ûâÑÝw=S›ÁµoV‘‡¯-ˆûWW¢ü°‚y¼­ ¶?‚O+£ðíš~|ÛŠ-Où‰ÏTç΄?ßq6xs°ïC$Õ]íóαIë‡Ðñ¾·ã"³~ötµ Ñ‘ 9 „g75èøž½XüFMäoÀÒÕ— µØœv¨¬dº" àÊs!šj “^f­Ù—EЏ$oÍjm;w7Xê4€;»ÒAÖ`Œ5;3Ÿ­Ó×"'NÍ¢Ÿª¨N°Ìz"Ñ‹¸œ®$làNǪ““ü¸g9#–D%M—.aJk§v‚õ›*ðÙ—?rK™½?SQp±5õY¾ŸsW8·5„㓯 x=›à˜u_èBÞž’7‘ËÊ …c¬Ì fybGßtäÌŽƒúÕç1¼à=Êr¡Z*bšu±öÐyHÞ« gWl$UÑWq,ö!~8]Â.<´ÅVRö¨­ˆÃ„ÏY[® «¿í¨Þ½óh[›¿W7+þ«×náhÖV`3•òYÍó¡‡Ps?YÕÅÎ.PÁ•O—³”M“¿µmR{_Beï ¼' LÕ´$è—»d`beÛŒÛrløÂ3?ÈÅ5›rÅ®N®ç ߘÓéÉxÙYˆ}pí:Þÿœ9B¢˜ÚAQ¾B‰<ƒ“ËPØa€}èØú¾²pصKN)Ã&9€á >©Áž;Î!}ß/3Vm\0þ#C~ìúØËxñw9~ÛÌæÙø°Ì¥bøàS‰CÔŒ\ø8©÷P‚v~>ŠõÇg’¤¾lFðèb¶Y] ¬Ñ¦·<Q+qG’/¤5·Êq鸹!ˆ|ÇœèöºŒCH6‰‘XMÍ&΃ˆÄ¦îSöøÃ¿›ZÓ~˜cyÝêýõ0ù¥S@ [í`¹ÿ7öjš*\»Cv¼å%éç;ÿMSMˆpË]æÅv~úåcSŸ~ ~^¡$K‡¼8¸œpÛ%Éx \݈G½õ¨kd‹‰“!¤äÍiÌ£[Ã>2|ÌJW—¦0Ó2ö`BŒ.¦énÇ9ËÂ1è­=þÝ>1«ËYfYrÓ> ÎL£K'5‘Q¼Ù¤6êGÎR#væ ]ÈY¢€Ÿut™E¥Ó0êì8¸}†K¾LJÔë¹Ü'ÑpikæêX“‚D9uS¼¯U3Ž¢ãeúþÏUx<ÆG¯HÏ ›¥]X­Sú`ÈOúéŠë%ñ~B.VÌ0d2t_p4Ö½b®_p¥-#à…±œøc‚>=òºo=ua¾½«cr ÜÈ ‚Hf½/5úé QâTpÓ6ße'z_ o›%õÉNêÚû¡ôüQ*Ù¨ š+6ã »æÇ‰Ì¢ÑyÌ´/¿Ùúaú”OœÙ0ÅŽzÜf¦n¹Ç9d6í_¯A¼P;øSÓz¯ÛâÇ˹˜œòm—j‘uÖâìG=5ÊÙGyîjÐÁ϶TÿsóN¹qß…JIÿ(Šî2hPx gùÓú`3Læ{ˆ©-iìôÃ3èZ‘pöGâ òüä&F?†ý¸` y×Ñ#ùزØ >þdC¥{q¿ùlª\a‚›r“˜QyUªNùá¶·µÖW€!ç*ÜWØÉq¼ÅK/fSÇÑQ¦}ÑT´(‘¤_4*`q—•iÕ9?¬Yd‚þÎ!yHHå»*O cë`ûŒb˜ëiÎ\ØyàÙ2zDß“¨wø£ý5[R×Í©4ñÃØÖUäÆN*¿Ž•»¯€~‡7¬xÛ¶eÒóé:¬ë -IS9órÚp¹x*ª¯«e™«ð_µX©n„²¹†ÄØï½y"šíôU¥ÇšìÀ¥YœìÑYFÙ…[HNÜ\? Âs-ÀalÝeLZ¥¿/s "›×!=x“S^Áøöáø˜ ‹zÀô&ô°µíËÓ¡d‹-›÷¨ÓYo†ëR„IøÂë¨Y{K”'@Ï^œò C9õRFàz36 7Ãã3ó©ÌiY8|Š ã™Mª?ßfZg{µ^øÂv½¬¤™N" ?Ò„W ¦P—s ?o+ñÇi>æ¾ÞÔ=½…¶o!ëög“ûër8óÞà|ψ7{SQ¥ù>긮çjþ膟l)¬Kíe÷ÚSޗ̨ÎElÑ CY){âPÓÆœÜϨ=: 5m*Ìñ¼Õ´ï²µÅ@Òc/ˆÓˆIwOÀCÛ9änçEöG=d—Ñá×›èÇîa&}¯0>LãüΨco‹×B]³:¼ìG­KÅŒêqMz2ç,žüEvœÏC|לq¼”¾‡í¾Î§‚‰DGåZÐËRèþ×O@}ç&ã3GŸß[oœ² b¡{àéãKºß‡tÝ’á¬L•åÛðùv9&LvܶMêáä x’CjRU‰æ^c¶/ï6„WtãòRMrsñ´{‹o«“˜âû\#)ð3‹ ÿ(*Ÿ^€{ýDøS†÷ÒÆAeW7cRš¿Ãáúë=¬ÃVºJe8§Êã÷Y°÷´0çlàlpr¤|"P»a%žã_„ZóX­Y}ìóÑAØ}u#j8ŸÆ 'cánŒ'½½/OM¤SÛ¦Ïé_áBZ¿ì$+zQð’#v ¡\¬`Ö¯we®m{†+¢}à‹Z$¹•u68ËfÈ ûìîáãÝÚÔDK…l¿ð +_…Q¡of4M:n3¥ÃZ>äÌŠi“×αÏœ²‡ú\¦!·xHVµž|¨‡¯l¹½¿T‚-aÞ· 4>¶ªíÎ?*œ?‡¤éq:(ºê‚ ²«¡¢ò0 0ºù)Ý̯-”ûðJŠgßÛKÔ+u™–°[ÌÃÜLÔ þGvLGaM]”ä\‹O•áÖeL(<Ï«£Ät^rð/ŒW›aßÄY®Wª ö87’œˆÑ9vMAhaµ„Øû¥‘}˜Õi£Ìc׺ÏF’˜¼ %Ý#»aÑqbV:‡(éÖ’Î×" |'ŒLŽ{Ë“[ZÚôÊáQ´3Œ¥¯(S›]ÉX`æMÿúu-]9øµûÙ³ï7ó(<•„}lïÙ>‹>r¦v-†¸Ï Ó™úæª+ÓÝÄIzÿ|²Ýkæ§R‘‹[ß!—Z‹í —cîÁÐ×°~ßÌߟ†N[’¹ûôû¡AÔ,<’„›¯¸Û·œœþjE“¯ñzäÇqlÝ+öŽÙ¥šDt¦Ä1º¹ÂôWd è&:ÂÇ£8‘ ÿŃ֣B ²úMR2°0Ëþè}€ÅÛiõEotÙº‹èh3f+…Øa%†¾«:ʸÍH¦ñ;®0g7İ‘&- 9š@ÆxñÔQEz0$€³]²…Mø²•åÚ¸÷Ç™Yxï]oÁsOòqñûT”ŽOÀ˜#¬uÎ,üÃ&@xëî­Öpzž2ö:)Ñ ÇbÚD¤MiðŒK8ñ7‚¦óÓ”qàOÿOž áþÄAôÝfÀ.° dßZ©À†óNTzQ(G;Ì“ä?ƒí«h×ÀZÈ]¬ï3ñ–N]Š{®3°E7ÓQì©QZ]•=h2¢F–—ijv…n`­8ù䕚Áð¥#^œ®M´­`›Å5bWcDŽÕVÁq£.XsÒs¿,§W7dk-i³=cOÌfÊ2çÓ€ýG˜ò·g ëÆcLÜÔŠs5Äàìï2ºÇ¹wŠ—¾`µ^¯!ßÞ÷C€ã(nzƒqzá(^‹¶'ZØyÿ–£Ý!{˜µË­w³‚ «µþÕóè­3|­V-+árߦS¯—U;3´'8ÍN±à,þʧæ³Kâ4ðVí!½½…¾Kô N•Áú늌ú“«G˜QÅÑŒæ¾KLA(³=é4TÔd£6?g4MŒéY)„ŸÉüäTÅYàl-fIc}Öˆ.AQ¸¡–B÷ihÒÓOw`Ô½ñ1Q?-Mx*£ÉJÙEøÓ؈Ú ç\àõ–<|¡s;›¥©Çå+LÏgÔç™N…)Gü¦&Y™·‚}ÍS+ókÇûlX“ìã™G[Âþw wÊ_Ũÿð¿ùš85Ã<uZ¹OŠžÛiIß|Œù²—ûÇü îV›J ÷ß«K)ÕP¦f"—9§7щõS©ùoWôþ „&ÎZÄKI*ÄŠ ›¿|ar7ô;k+&|tŠSbû.3¼¼­Åš¢i”V1|áMf~Oà0k‚\€ކ†ã¯lcZùµ cûÍ0ëó+PpÔć!ö8s—(Ýýs‘è» S,¢åyÓAvÁJ¢`Qξ 楿¶Âü?Gq?·Šwâwû•@LǘF‰vËSýöÊÏd°•» ðèìÖ(œd6"±º^ºHÒ“Û¬ƒS4 è‚Ð>(7§›¦ÀF©Ì·…‹`åYe&¸QŽ¢™+þÁÔíûÉOÚ‘p„z[ÛÓGCx3'œÎS|…_ûìPà–æòþÆÆÑ>F&`9]¯G{5¨øà7¸»4#çOA£Ì3˜:VÎésè òâ1œ<$JßlϤÏ^m…§1–ÔÂ*ŠÊÊ‹€›ðtÔØj@+.Å?«J–ö'a¤p9š×Mù ûQL×*Ö-'ñ6hã¯ÁåßÁ®<›×ö#w9­iÀ}Y@ñã#nÕ;}uu>y?ü‚yÛpTÃ1Ž ì›/¨Úis79CER⌜×÷Ôã{’»é? tí¡ʕ軺ªÂ‚î|  NÑPµËžê²;@\•—øóÐùc¸6ê„4󹿛pÕ¶2¯^ÄeèÈëRx7«½Ës Â_˜1®~€²Ã!yN"µ?cLO ä&Œ¨b¹ÌŒ ÇRÍÔv؉º©„CH[3Xè1d§¾3Ýô÷nüsŸ¾ öAöWðîÔ pom‚üXCì‹/ÃËr—áàºj6%€ôùV²lµUŸkîÚ‰~ßîÂý®W°Ò(„Ê6$‘1…ZÎd}Ùµ€ûúžnŒ§k4‹È¹u¿ðÕüZ,ùŽ —Ô€ßÿëß4£¶Ï䙣½ÔhUõt§;Z?Rµ¿üÔpÍ4š‘©C—oûň¶ð¢ñ>к?kTŽd½––1– öäü·iȧÕÏEä2øÃÅàù€ÓîÖâÒŪŒÍKazä£$é ^I/%ÌáÚɶ¡S7*3Ǥ5ÅôÑËPT÷àZýoÞ,œ»ñf‡Œ1ü¦“…¼ôh7ý™/O=2i¾[ ¸ûÃͫϠ×jRèpYŒ¢³…óà×ÀLX{ʈ|.ÑàFÎ,£ zåD8Õ ÔwÐ¥½†dïBšTïŽS§Q+kCʉzN•K…ÛÞHÇ©BØ.3›h{ê“ÐíˆØ@º;§4Ȱ:«íè\wÁv7ÔXš·}áñÉuÙZûJFHÐ7Mâc,N¿XN_ØÉÒb¿“0¥áwÁD1}ÿÒ^}Ï|¶é!™gÉìÛ9Ô>À=írHú»Ý¤Ö4‚´œ%Î’ñ´2ßõ›Øå“:åy=õŽ…ºAÄNZ– †M÷êu)þ=IWÓ€pCÓfïø¥<é§šwawîWöê‘4z$׃nÙžªÝbôíM)j´ÎôUiM€5I ¤z4‡Ö¥k²•º×г| Cxsh^Z',¢èÒ‹g7r+7&ó÷˜5pM¸7«ù ;ÝÉ÷¹Èú‰UÅÊp]=ƒX í¤&¡¶ÔÿÃeªQ$wÅSˆ„ªóâÌAâ™Doq%ÍÿÊñvÏyªRž‹ƒû›°çA7Î=‘ÊÙÿÜ<®ÿGw p}ùRÀ‚•Ø,®€¼‘ÁÔbÓ[É RYÑêùäËîg]Z w6¥•¯.r3u_Bæp"­œ´¹¯?2—•GÛö‹­a² Êµ÷ÈV-‚Û kÌoE®_íeŒW•µðßîÅlUzˆ«»NãOžJè]7Ÿª§jà„ìVŒ-ÍÎIÿù3M~Âýã\NÔ d¾®ê‚øGnì„I8–ñ›1 …Höõ~z'.Ÿ-z¬É]îâȆ5hÑGÓIù©óx+­œþâ¦/ÕÁ÷?€T†”Úù°?9‹ÉO}ÎÛ‘&tϽŽ@M$÷‘‹>³`¶í ( na•ŸX“躗(*;Ÿˆ§çaåe2­‹pnýu'èX‹f·OÀ[«l(ØÂ²^«é¦¬ŸL¦1?õ›s†ÂrpÙÚ`ºº£k.{ÃVž< q¥áp²à´ã ³Î¶\9‡.~w°v ?õðr…_w°Çô*pÑÍsð$1ovêP[ª‡ãcÛÉtCo\.'E“¦¦rïÀúÐ×ø6YUÍâò¼èÃg•;I¢yQBÍ%$PÙ5 v©g·iõ™uÜÌ=k(ÿgÈK¬Aé+ÿ°o´•Iæ]¨?]Êní†l}Š=G&™ñ'\VrÃWa;á¶Á!6Iri²zŽÔ"G™ý’d6 ¡õj>xòÇTânÏLM”ÀΓ“ZØk Ñö¼I6|®ÚEƒ‹$a•ÐB¸UìOÆÇª èÅ?\%àßš_òzyQ¬Ðp%üŠp—ߢêiň» †Ú3È…ß*¤)h Ñ0s'IÙDµ±c;žBÕ4â27ƒY³Þ‰¶Hc^(¯#ÌÓLÊ ¼§ÂðŠ„q©GÉ­¹0²/‘$oÕ$?sgÒÅbúè*æ /d³Ša„kãŽÒ¡¤[›Ä¾ßŒ½ÉšÐ~˜îͪ#1éný‡°¶¢„xßÑ&kd88{Ñyr·p+¹Éøo4'Ç·ì¦Û?ÛÑ펱܂Úc0}è‹%¥=4jñ â™ùê½ÞÃu[Ü¡|‡89ºÁÐÅùÌ¡  tù²öv™@åÑpêÖ˜Dãré©’lzÀq=•¸ Ûo Ó‚ï}l’J~²’ÝHuø) žöZ¬è‡}ý`A}˜+ôÝrQ»Uƒ|_JEU~âfŒùó¥” \Õ tã€o¿%ÄW½“ÛçÑçÔç ÷Ì Â‹Uã7ðsñ"ÒµåN· ¥{º;Yé¹·!1A„vw»ÒGP õ5c =ŒË ‰:ò*DÜÞµúž¹²ø_ ´ýj#vÚÙºr¾ •ÿìáH”4Y?z™îM ¶šæ$øÊÜç± ?§ABEÕ•ìu¦ líêù eTLô»Ð/Kx‰ðÙ°M÷„úìDáÂÄë8¦ÆÿÅ`)|—Óül5vÌâ"CQ­rWÌ~*-Ó«¯×£PôMBQ$3Æ×­‡d¨S¸+<:ý§¨ùbÍ–vزv=1hCwŸ`¬Æ!6 ›,…/wt‰—J='cA¾»±Œøû´Ùòº½×‚$^˜MǾ\Æ›bä¯ÖyúRë1 X@öW=F©qÒhœ¹„Ó8ñô°ÃØ¿´„ã¼ól6}PkE6veáN’B‚½“ɱ ´ôè½+MdÏ ¬ˆj…ˆÝÙдŸÅg–â ¹ ·BÉå°4²ät _D‰€{rb8s÷<–Ÿ³ˆ„¿PÇ&DáÆuØþ,€jk&3Ñíj¨8àÍóEàG¨)ù–“Üá#0Ñ6ÞšÊn j梳ÉAŽóçoÌýìpæóbX³=_ÿ=ƒ^ÞvÔÁœâÀùÕèí®NJéú™¡ñ¬Üßç¸úK9<±Ft¤®ÁÉN0ŒI‡ŒëÍì1e¢ZSƒ¿·]ÀÑŸg ÷^6Fé µÅíƸkO¡aµ„¸N¯F§[Xò‰ý¦G§Ïk`b…=¡jG§Rœ3¾ .óزZsFùË.¶¹?>G½ç®©W"»¦e9rŒØ(ÝXƨ§“9ºù5óØÚ˜™ÞŽRóÏàÂîä}{~Õò  OïÂ9q†^ÚÇœ—™3XÎæÅ 2[òðÓ#ÐÐ~¦¡åŽõûç/§å—*ZÁ†.%·è'f‰Í¸æaƒ–ýÙ zô%û§ª„> ±ê÷Ú!¡óø2Jö¨Rß"›ö̳“Ù²oú4÷©=SUÄb#¯2–(œïG,î›á…-³hÔUMzfì<}6¡«¥8X ;¥Ž ~ ÆÔë¶(Ä4€›m¾nÈÆ’Bâyi^Tê#—oµ9.˜'l£¿¶4CÛÕ©dãÄ9œA?«x’„quâè°…xýÉùн¨@ùSΒج¯ìßý`íËE¤Ût¹—Ç•Óéƒ^“I–Ú?›nþšE3M+aãöƒD.9;:_Èžœh¸Âøšˆ'«%´ál þhh>*€wnüfx–ôŸ¹1xú‡‡Ó«u©Ç[2®Ñ +ªm <ÕŠù)Rˆ—Þ•Q¦x:”ùûÒçâž4…¿‹Öj2޾50/ë;4™¿aîì,#Þ–ÓÑÕh ôEéüôÁŸºä­È ååÀÛ¥LWϰ§e²”N&z ±:AC’Eᄜ:~†j/ž_÷èì9¦¾Lˆ8^V¡þC#¸ñÉ tQ¹ÃŒôÊ/·@"õä^>sýÀÏņô¹ ‘·üºÄ§l6Ù ­úò3È“êxoí9Œžÿ £›¯#¯ð8c‘@o¬!/毥ú…ædpJ1è´)B›ÐñÓ%gÍGë,U¾l å6D·¹FЕáOã+íàO$ûùÊjäQº‹ÃÍ&¸iA8U^‰Ko~1kãtKYSë~ص`ùê#‰ÑY5êêü™}’ÿ”ÁúbIjñtùU@ð™ÑÚÛ©ðûT‰Uá#'Dž@$ýýp)ð5)a«þcøªµžv чo™g {i†§6©ñ»ÒºgŸª/R‚ÖÃÎTCâ2æ‘õ¬jî/”¯‚“ƒ¹´sÜÎŽ§ƒAiØæžÀì\ï@Ù.^(g7 í¡°z+Е‰zlûi ªÈÊàÉ Ì1u\ÉcGvÝjg_¤ÒiŸ´ˆQ‹/½*Ê¢7pJiV›ý¨ër‡¹ó‡¹•ú¥-´ôÒdÉ ô;™ ŽãµøºD‰öG.Åír‚ô…0Ô^-gB÷ @‹ H«ÃÂOc¸3¶Š»yíQlÄ]o…à­‚ \ƒtë:T«LÓ4<‰×þÈ”¡¶¿ƒP·"BBr šg¬na¬¥Ã‘r^úSÄœ)ì,¥Õ¯2 ‰¯A»ŸŸ<í5ÇÏš jÐÆÜ}øÊÓK|n€ŒTs²UbâmpÇp ÎJ¿É„4·Ã®âÎ…qØ-´f £cK“èVhÁ »©»È^*à,Lgð»cnö4êÐõGLâŸv<%9V8rq9Dg8ÃÅkMhuš‡<–`E¯Ë“‚³79ÑUjÐ’:BžäóÁk0òŠ‘0ÄEœ}È»ê ÊLjÉÿâ³3·Éá…Ž/`×¾ÄLÉÜ·ylæ¶,pÖþ <ç’˜ãî¹O>%¢WÌ/4-à×8‘^_RaùXµ=êÚ÷`îÂõ\c aJŸÉÒ71~h°}zÀ©Ùøâ¢9fÉÅcó™ÄÞ:x§Ñ •Ìl²‚Ÿ^¾¹TLŒÈþº4Hš:üGc0KA¯²£ù17ê,wN81¿ô ºÅ\ñêžPˆùóã‹‚¡+õ(Vi¥Â»Cpk¼¯·zÃõÖ»›²î£}CðSÕ†XnØ~ ³0 ë°RæqQÙD÷hwpšH:Ô.½ß‚аp¹þ! .¼î亞*†ã1ÃÿCöoº‹²çW³2\Cò“ìc# ÃP…½½·â€T„£´ žÆé‰Ô ‘+ũɡŒ=Žü'ÁÆc8¤{ÛÞFª¨ƒ±ÒÕè¯jÁþÐèÆÖ_17S‰f}‘$W¶Þc>nàÈ]„Mñ ¹6†¤Ó9›SQ"âûOÿ$N©4¤×SîÀìèb|º÷jž^?t^À§.qfý²¹t¨£k9e }Èݶ-…%™Ž&mOqʸ8¾/B¥g‹’­)¡è˜¹©ïÁE¯|©Øï`fK¿o‡û<ÅÌŠ%Qp^±•ùi£DåD©6zAn ¬š±ž¦ÎbF.;àòiÇðÌðvv!ëâ.`æ‰$ØñØ’v+CÈ~yrò°!}´è¸HïÅn2•Œž=ÌÖµ¨“Ë ñÍ»bXï+J7ìtÈVëãܺ)A³… èÈ'^âX\‡ZŸìAªfQ»• ãÆ¨–¤J¿”ABN,æ(ŸC™Yó˜^Ó1œ­Q Ó*ؽ¯fCÜc:ç“ÿb¤ó[¦ŸË´=úŽå_ª¡Dîm»´…í¥&w,\òÆYñU ˜9ÿ8Ëó8¯êfr –Á©q¬T»™ÛÊAL~-Û£'Nš¿Ïa~¯™ÍdöÇ…T¿&’i¼Ÿ =½ í•/'ô§Cì/<ùÄßîƒzÕ†?v±ˆuÅÊZ;Hkš†;¾Îb)¬B,Æ‘iïØ‚O¹nµÕxñQ L{/D¦µIA´œ–el¢‡÷üU9ÜTÆz“•{߀eã\ü}«ýôô18ãÆé¼¸Á¢/÷4aÝ…KŒüëüjŠ—ê-™’Ú]4a¦>óxé5vZoéÿ㬽¿oC½Gâx£¼ã6^…â Z÷8פ?Ç¥WýððÒfÆhš'»×µË¦0&ZÙx%2—¶íÃð2]öÜ{5‚}žÐ'ùºZ—Àü»]Ðdò*ÞbrþLâ¥&A'æõc­?1ÇšOaòc?”µ9Be{^asÀF]¿ßÇã•×1Ìb ú9½Z‰3Ú•!•$œ³¡÷_ý†æFq2<Éë•zØ/YÒäM!Á×bu×ü˜›É4è®'^špQ†(;øK‚¨ùÏ jÿ2(ï7Ÿè1,·Ü—#LGå+Ήw"¤iµ6½]5Ö’µxM`Ïr‡Ø¥&¬û™ ºBŸî~)Ïa.œcç'?Gã;²T@"b$—áDÿø%zÚñ¡–) R˜£‰['í¾Ç³P¦é‹+Öÿì*dï…&[Mx®¦LÙ¶Ljd܇UNj`ÑTMS Áó´Up53™•:5»öÞFÿÅ)ø`ó(¹ŠvãÁ oî£ðl"÷FåeíèÚTdf( E¸Q;«þ’µ…Ð;aAœ7Í"%§Û™9Ûò­VK²zñÌYÖÇêQ}þ"8ØÏÛÁx¢”yÁO “Œœ•Ÿˆu?Ʊ•/HœXTǾs›IîŠô³{NäÁ{é­ð$q®—4}¦®@g,ø #¢$öÞ³6¯«ñ‹ó<­îš#øû®yt¿Ô%Á*™éðÛ‚7 áè ×ÿ{b9ƒ1¸÷“UÏü§â®ãÞ 5à/¥‚=›¦öˆ~P»iL«·ˆ!Ïu#z?ä+ç7uqÌß-N6Tý ?(•Ie~][ˆé'ÉPS0†nêÀ OWX ÷sÌÊ3ÆöOv°ÖY¸n©/Ü£%°©¯ ø‡’ØÎÌÇŒzšõ65e/o‰ã„Æð¡ÍÖiÔÈÆ +MÁ ßcè)¢7Us8WliƼ\œyø¶¦ºÐ{%/Àd‹:m5õǘSÊ·ÿ ÔäÙ€hG/ãÝŸÅôÌcè9?>Ô°^ý™_0®' :â±Ôs3ÚÇw²ýð©Ì34SÅ%Oz1üôaÐçëÃq82aƒùRžÄTâƒcøgW(ÏŽ ïg7Á‚u DÎð¤æû° m·ð^flxÈÁ¹7›q‘ê2,µ“š”ÄNø÷àá'(æ0‡hí&t†Cªø{°ãsA‹õj’‡YØÞ¡»¨;ûó@Ë›™P£Á?*qºÁÁp`šv÷ÀÊý/À¨*34òÊàÖÈ5ðÈY€ìýÄaC…xÉôìû̵§Çhs² ݧŽ^%pèä]8AyçgÓaw8Á{9Ö¼H†¬X;²ˆ#Æsÿ‹ŸfßúÂ(”„ãûGÙPvãGgB‹ ¿ç§+n†0レ¡Øº0Ȳ_ ³ñeýy|úíÌy"Dºp¹2;CÆ¡β圇LiÜVÿm'NÉ2‚T‡¾6…É_–ÌF=Z¼Út¿§3ÖsŸmaܡθ ¬i‚õøظI‚Y­e;i;·Â‘Iö¼u5k—c„Ec©}„>p™+ÛOp§$Ûr¬ã¬a¡VμvÆŒÒÁGc€­°$ÆgÓ1$¾î¿Ù°×¹† ª9'æN€Ny¼£äøÌxÁþ×0Ù»¬Ï†#¢DÇY†D )À'ËÓ(0'ŽÉ~(CXbH4³êPAW®ùunïäìZ~Š •å.ïzÍÜ^ ­¿¡«û êÍŸä(¦R_VÖ8F—3tßu:IùÌŠt5ºÇ[‚Uü€ Eoq{WÎSíD¥ïŸà2½À®“¸‰™]{è±"ZèÞ‰»–‰ÁûM¼ô`å Ô©aRŽš±9n¯Ùyug týÆ%è0<}Ñ“63^Ÿø¿ á7øéìp÷Mƣɠ|`?^’‘#·^*иeÈz½0LÛFM,î³ÍaCè;œy<h“–€ÇÖcÖ] ¼ /ƒoçìENq#ó`Í+ÆjÚ¬3)„'_×Ó¢Cx7×lËÃ9åî¸Ra>†®?—Þç ÃÁ& R†è•šôöp,·yÃÖÏHÁâ{{¨÷Ð(=掟{Š"^ââ,H„l G€µðÚû<|þÉLI&I§cˆæ4Á®fÞú‰ÓˆûqÐ՚ΓúÌ–^Œ…£ûè¯ûóðérQÊÊíokZ¢øÆ˜œ;¯Œ…ºžDøÄjêù6„V¬&§ü!¨³š©_®ƒ1ab4@v*ÑT*ÕòtS³ ¹GzsaΕ¬ôÕ'…›Qõ |9%ÆÉö ćܼYŽM[™M÷‘<ø¾\Þm´GÛ">\$,En_šIƒã›pT>Žå`FÇe©w©ê~O‡³ÎBäòU8ñ›‡Ýèr§õÜûÞ ö͘ž*:û¤—ÐÏé]aŸFµ÷܃[«n‚Fݺï!lÆ\¼Kb¢k»Û|L‡è±ã(;>Çéɽ´¿b¥ÌÉìåçqãñn†G,§­mó:@ÊÔ™ŒÄM§KÎÎa'’; eà ¢}‡‡ŒS^ºÜW†Ü;òÝv/<ŸÂè ]’gâ„%[.±Ì©•JÛÉúe[¨@`4³ÎaÆÅ¹Ðáv¥Þ¿ázp¸Þ´däåIp»2ã»·<Õcq´D…˜i.!¾iz`E…ˆ¾Ï%vçÆeäo=¶5Šœõ´¤á1λÍÉùw#+RËì7¸2÷»ÁpŒ¯%/zµ™?ÌjtŒ D­1d7Ï î%Ñs8³O™J®[BÎR}òfîQ:y=fÇÀ@˜ í½Ù‚×6_gÏx³GþÃÕÏá䉙Æz;ö³rŽŸµ¢ ½ËŒ¦´/e#›ÛÑM^™|¶?KºìÜá™ËLTKW‚|ÖfíUSj¼˜>ðë‚v[9º{»‰Ñ¿³/z1‹kôhÛGk:¼*5d_²sE“}šÏ©b¨H ѱ@»Üu­gbIÈ>K¢¡°•¨Ì? ;N9OÎÿ"s} yç“ § IÙÈF”*c ~/ÅâñðUì Ī8Aޱãd¿•ôÆÏLÒ$ãõZÑ˦Ûð|Q7z¥Ì!sî¯ *¨’tdßô±O%ª0·ø,(:~‡=‡liÌã*¾ÜŒVšõÀ—ºä|b,9YAÒÂp±˜ ýèn@zþÅÐ ôQ‘9>èû$g]˜‘F8¯;®Sò';‹"HŒÎyÜ9ÑUèTKôW<åþL½J\°Äv׫Ü;SéŒÊšï§Ñ›ep[ÞÐØ×o.%Ô/çPó|jŸ /ƒO½e¶ôÄžl|ËZtW/"Œçbˆú«†˜éÖãÚ™mô} ý§¯H"¶XÂÍ^zí©6®8\ÉÜ,¯³°N:fÎ#¢'ñŒù{vê.;¤@.:ëP¥ªJx°ð]5ý<ª»î‡sn}Ø|+¦Î•§wgÒrcb&êAÞ‰ÁÏ ˆ¯I"Û>µ{ì~ÃítA<Æ懜¤žg·R”{ÛJn„ÂØ&7,^°†Y«L›ÃÒÚSæÐWaìêSdõ7s(Ö¾‹9 ¨Ò$Ì,•/ÿgèá[mp 6ºú?C¤À8¼Zÿò:dÑYò¶íS#Uc¢¨(Ó§Ìš½ÂNA/¨oiÇê<[6ZËŠ•×× µêRäùz5\Ói€GÇ®ù¤Óã,”V[›G®³;³2˜¥™ÁG•^ŽAÏ6Bž¥ Ýè%^Jm`!=¹æ˜“™E%8h°ž^‡M¯`aÖK¶èHã—; »HƒÒnpÓYNß.ÎI/9¼°º6>fbÒÝÉýBc|z“«(ñ ŧ S÷wFóÞ-ôµÝŽž«–±¡:|0À jY²gK1;ѯ˲ëY¾íWؽk9oÅÅ©ðw_š1Ënh¾Œ•¦}¬jÖ!øP‘Ç,¸“óË®±µ­c¸oúLõoI\ù fàZJ½Ÿâþo[Ø«?Ò‘‘ïàÑëÏHñQ©_1pÊN”Üiá0*qIÄ1÷ òV\acã§Qµ©ðað5(K®P×\ã=¬³ ãÓ_†º«aæó.¦+Ž$Ö8áÖ£½¸úN<þ¯Ä…ª¡dÌR’¾Ú|”ûa™"—[Ž'~aæé÷ìħxç-ÖVa`À&üâÇKªs.Ã6–ðôãû·0±ð3\çãO¿AûÜ ¹Y”ÜqeëW*Á¢ƒîDOÑNµæ%'áì¯ê1òÒHŠqÈ`V[1ñ,r„¯b¢Ë^<ïÎüu(hÕŸ‹Ý'O‚ûí ¿B9ݱ ¡pNòµ­n—W¨1š Ëc=¨—h2³Û<7kŘÇFø-A•Û°(Ó‡=XÇOoÉkJÎ7V«A¯ÝÿScÛÑÅ©´jŒØ·×zÑ#P™ê©íbMµa¡Þ?earÇér6àýª€a}±÷Þ\,ªY€·+WÀÄ’t°ÊgGõ|éYczuä+7¤Ó†‚Ú°ÓVŠZv=Í™ÔVKvÁ£„9P꾂ø]z·˜§Ž‡Ý ˜‘ް1S|‹Z€Ïõ qôÜ=Xwj-ùÅNÖôB}¸o í9àIV´ÑðU¨ jGÔBÔÁEe/ðõ6àú¶íÐ9ÛŒy£•€7’¹ËÈù±!,[ ?ÄÆñbÃúcI9’u`ýê4†*‹ZgMDçH’XÍôüûOh{Þ†<Ù̆c0Ä[\ÛǸX_ö¤F‘ëAô\˜‰¬ù)ps¸~‹ÉË9¹´Ðõ4¸¸¬!Kî Îú¦mýuUÁ߆‹@-EMaH>gŠÃõH÷›ŒK‰t´™'®ÁÊŸ×`~ÝOÎøŽ…LÙq4ø8Š #ªTÃí<×0†ßõ šóeôuðÀ5Bܧ§#n?ËòŒªáúálÒ1Ïœþö†™.*aÒ™”ÊKÀ·¢þO_é(:þ€#ë¾àØëÄ_Ö€|:Ãü; ÷eO›ýÂh>žYDL¡èl$l•™ÿ^?ÄÌu ™oæIt[÷}z‰? ÝŸ`¨jý~§–­!wÖOÀ¸ù8¦¥ñýÄ=¸d‰µw½ËUñ{…kO¢ž‹ùàFÍþÎ@|ŠË,LHÖÑ 8¿p ÿ¼?JSnΧ•¢Ã \xöKUÁQFºT™6¥§2A­ë±?аÏ¡Sê •.¦QS醿þTl¥?É}ZME#ãàï¼0žpúí²aqjDøé‘¦©t`wää½9@v¿ÈÊ~ì9ÛC°¡A†Ýá'KåßÞFþ?ç¹uFòX\‰ëÍ¿âi±rÎåI[úf!8?qfíΊЧµéà:yôY©HoMσēöâÝc,“{L_ÌÂÞc¯ño^#{Ê­·×e²c[ñ«xëu2’J”ÁÏéJÌÙÅ·°˜w5ÕÜSŒ›*1¶kÔQ4f=™ ¯­÷ ¨Ú}èמÏÞ\ Yʯ@ét><1̃òÃ=hî«@ï.ƒ\ B÷IArÄjjÍïÄíVšìÓw+“­›{Ùùk#€ßn {9[— ^<‚—xn¦ÌÈ•^Eóå¸#ë+T_Á¬ˆU'Ï_²âóþö~øµL™;<¢ÃÖªÔã%Áïðp°6ÜžËÚlâÁ‡á.Û*;r™Ù5¯ÚÔ7“] 䉶¤4Ùø¦ò‡ @÷z;FŽáOS'ð>c‰a×’IøšF¨Ð!Û>=ƒÚ™•pãŒ,}'~:öꤌYè×ù”i™ùÎ ¶£ô17º`I(ô6eC_ƒ1˜È´‚´W]u¾›¦$¶ W—­z}Ì»€ ¯D³ŸnÀÏ…Å8£è38ÿõÿâC=Cfüñ a<¢µ™GÞ¡‘®ŠýoÓ^úÑR†ãÜû§¿Åedçz¾Ú&¶Ö9×òEwH“ö”gcœø:’°EÜ—Àí‡@5s'•3&Rœ|ºçØTÐN½†Ã*Ðe[ ܾŒ­*¸å†]±îˆåCIÙi´_~šñ¸%C3k§3o§Ê¡§¾+y}þl9ÍÈ+"^ùv’ð/×Ôó»ÃH™æÔ ²®¿Œè1NW,‚÷ßp‘e¾6þÅÕNùðWü$v'¹ã¹‹Ùx0ÒŒ|npÞy¨#m7¦a[åççìð §–ý¼ôEB7¸ÿbÇÖW1ì‚@XS]¢à!1†w<å _K߈†7Jä«¢³gÍefóC„¾]T’ö‰«¯ûöçæì a›h®.B‡½‰»PBœí<ÉSßäÑõ#)äHßhÅ_œ"r÷ÔÒwü'˜lä¡ ž»ñ);™7-*402‹q7öÇäU¹/®³ï_Þd3†Óè“ç‹éÄÓx2?æ®$I8Gn~]ƒ5Îð§z#Í]ãDý¿`Š@(““È‹#;`z}¨Hm ]™•Ì#5uª)öêJ%0ƒn£ícØCcðúƒ6&Äò{À^ˆÀú\˜–z•ÎÔœ“ŒCðYY‹,þ°—v¥”0UÞ}¸9°eœxHËÔF=õ„Çãüäzfª½Y=¼‰üSYÀt5BëõôÃäý M­nS_7iO=Ç Ù ïe@÷CCeA|³íòÇnMlq·1µpÂëòù䶘î­üÀ굪—aÇ,ïBú_|³íc}53¾.:Nòró‘U\2©oSÐÎ4 æ^ÀêпF›Á~ ²n8#¢PÑ[ZDrÆ ’r«†·IÓø™v´û«}zbÿž¶&[‹¢°Oi19TbLý«g>çéóo 4å‘ ½¬ú£gý„õGéPà:”à>ƒèÃ¥°ûE2Dˆ¾‡§Ç¡º±Õ?;Úרà¡‹œ8óPr¢…Þ1ˆZzôs(ö .jN#ß*mq³O¯—'#¤ñº-YâÆl6t .®V ®Q€ÞFwPD/žy•ÎÇ÷e‚þ ÚjoƘæ¸ÓŽ?P4P>Ž 'bRK¸¶ÁQx>¦.hÔ¥ v÷‡ÁŒ3‡X °ï%¿Àß%ˆÌÓu˜ü:ƒåilp4êožO="O‘—ùhÅlÂ{B‰½‘<ƒt‡iO{fp‘>;÷¶‘òš`×xlÅ•¦=`±Þ„Þû–i/nÁÇ ýìØ¿o ï‰\àÍ6"²Ù÷aÝ„JVl! §ò¨x¹æ|0»ÁñŽpÓôNŽA‚ÝûpäͽºÏœÀ*×÷P¨°‘¤_ˆ#¿d“Ý2Ùìg0„;µàŸš9†ŸI2vø±ßrYÕRD‹yå0p¿›³ðôn(Ú{M½©4o â>88é3SANÍÞ½„µŸÅaFù,¢ÈÛÙ¦d‰-f/Û˜8-Ÿt}±£ÕŽUTÉì-K˜MT[%‰¹!B¤^s–_ØÅùó~/ xoÎVÉ ÎXrM9qˆL¸b\ŸÅ.[Ò C,é—øQ|G»ÙïpL§Mà¶ØXÌùê Yù Ù™k…yÓ 1Ûo'-ÙÇTsŠ@Þ=9h%B¹yݰjö£•¢O#+¿À*›×Èy;ôú&æ×/hݨ›ûd Žì®)žÔéo}ùØçTøûè0µy!Ȇ¾¼Ï™ûKŒ6 :±»s1âP-3Ó&íêÈæ;QXÂ$³ƒhls;zzÓó* ÉN¦šaŽ^ ^}êäxSàÓBG S‰Ú|Àé§‹áN—4]wAˆZÙKÁßËEØèiFt’ÎPÃÑ4¶äw=ÏK¨‹b*‘àÓ¢B×\i'ý#Â!êïâ‰_‚Üø\€}>‹¡iuõ™UE3Α5š„¯)ˆJÍæÎÏ[Oø<Ãi¨Â2Rñ¹ºú–’e_…ˆÆçN´2¡KoõƒÖüHªu»…êMˆéVÑ9#mŽ3Ú‰¯ä,šÃ•˜3T0›ø4„0Kf‘£"Æ*ì¶WI1óúc0äÌ}v}Ä,zæE¹¾]œ*¥¹ð&Øäu1«°oϸsÎÅ`ïÕmAY«µøä§ŸÄ~ÞgÜi;Z¹dl± ¿¶¥v¢ÁãÈùYÝ8¦/]¸Gž™bÔ‹v[ydsNàÜ#ÿÀvÍf-Ø­DÐC¡UÄÇ1 bm’Ày£‘Êt$&K6ÂÎ?çÚ*3‘wårVõSiäΓ˜!6—$¹ô0ËC01(+Ý.â†ð'0çé9¨?š ÿ*5QþÀõ¶TüÓ¹ NM­&Ò‰ÇȬz[œ:&Cëã»mS™ë×G˜e‡K©â~}ò9:–-Uœ¶ˆ7ÝM…!êÓùÃ5*F5Ð!–{д­7¡µ+›ÌÒCÇÜwŒ¤p +°§²uû˜ƒ3?ƒÞµ@“Èç–4±8·e×ÌŸlºŸCÎ.Œ…¶¿|ôÛuÚ³j7§ù—q×=Ž_65ÁÃߎ´äÚb= ±y–LÒT=ºK©5Gg“E2ì©Ó\Ó¡Ù1¸3Hz~ “œ }²ïˆLõ “ó‡e·Ö€û1Ò›™Ct}a¹AyvÉ :Fn1ÁEë@¯þþÙBµ>Î =ú ]ü®š]ü>ÚÁ¤Qy‘óø^ŸSÇ9Ñ2ߋРðNÊtò˜—ÈÚÁG7`õÀb4){ ¦4F¾ŸU­˜ÄªÉ°:\–]:ØÁTJÔãgÉå8èóƒÝ¦gô¤ÃÆ/ xQì&†m=g–t3{ײá“~Íxç0×+$QìÝÃ~Èb O/c5K¶°ö<-p¨GOÀ;/\1ò _]ò£ÍŸŽ2%·cFhjø\ྮ2¥sûßàŠ'õÓ¾°ò;˜ºx¦™nɶcÁu.õª9Élü"BLÄT`ݽH5àEu«K(ÒÕĈ§È3«}nãà¿Pyì ŽØ`Ë´f칦F̧fm¹¨ê „;´š»(ìSÂËJZ ‹Þ üäÒše°?×å<Xž«4ÖC‡Ör.ã#ÍJ¶A —ÝÖ}‚mÈØ ø4YQqbž°Ü´ó¢%<ø¥.œ–UtŸ¬T:/á5ks¥™FÙãÁ%Bdd›o[có<ªñq?hþÂ¥Áà¶²KŸœÆˆ8Eª¶ã8»hn5œdŒiåìß5)`îïƒaž] øC·ŠáÏšqƼÁŠWM!¬ê7¨Û7Ó}§¿ÛË, NaC%f™ûM8ì°7Äê}Ó¸ØÛˆàKòîý\Ò^œÉ£Þ|+‰ó¤}Þ}m+åµê'[.ž¡ßŽ$Åë<¾2Ä Ù’“~µ$á5ÃÊ¥sÿmnjϱTeQú5Ž&vBäŽ] šÁÖù*ä¤Â,œ=(JZv#ó °–}‰¶¸œ>Hs#«.La4§ÄÀÃô8Úµ©üøë®‡: “ÁRVÙ”H^ÞÎN”W’À÷. µe9óÆa:}]vŒž“œ|b´Ân1Ìp=„d€î; Ç­*éÝ£^x·1XDÛ¼k¹.36Ò§23þË q¡–Ô{ž4öØC¦˜jÒç7SÏã£mJõH`õ4òʸ·µ¾ÀrGX3m Ú}Ÿàš¢.ºû¥8ó|ô$ýyîU÷Âçl[Ú00¦l¡«gÓT¡$VÿÑA2¯n;7ݶ‹ º¶‚šÍ,„…`bù°š¢TÉMosê&xàŒ!ÝóÙ”®Ï°§ûñûE9 ½±TvÓtÙ¼¿m¡ãÆmÅçã9ÏaØöTònÔ.®/É>ê1§žs¯ày±ñ¹±Ûš[1}Îqw$Yöе?l¨öôD¢Å¢ÓÑýñ(«¨·‡óúw¬Ó¸…•¿1Ž£Oº“¼©FôÎzåÚ¶Ò%lûzúPlŽ´…:óiÜé.Ј¾Çüþ®HvÜ5"N°gígFµ±ïó¬_k £‡}npD×&ýаëN°]¼˜žÜ ,×i°{Kæ’‡’ôç?z;b‚±ù&¦‹ÀzÁ<ÜâfΑóå&Å;âsV;ý-H÷ü!Få‡,qvÿÇ®3¸Vò~°üÁ-اF\¶êÃÀºè^?dÞ(€’ŸK©ý¨F wÃî¾ &éýqæÎ¦VjW.ÅpÔgÖºxºü5TÕs“ë­Œô%ÑPšÛÇ=Q"î€ëdðÖ,m¨ÙCÙ\ý8\e£DD¡vÉ ê‘/íÉR±:óƒMÒ*¤Å² T;ö!ðÎYD*n»‘#ìN¨]Lìæ‹Òà…[ArHŽŽ<Ô$¡ºÆð˜¾ç4½¿È.þVL„¯¬ƒwKÒ˜*M ó˜SSBÀþó Ô[áJjz‘O6Á™(2––Cn:7ÃÏTC*À»u³4HÏTØyi)=°ƒ‡ü+’¤#7)óôgl[m„2$Î9ŽßÌè÷ñwì9ÅãÔ{Ã4šÌìLÀM^ ¤÷û)ÖQq Y£„Å[¢ðÔ¬|°Ì2&çfëc‰wPêqlLeÈ-O}ZzÌ‘ž} Rî¸ï6+opW31ø«Q¯­[†‹R½áÇ€8Ô•Œ³µq/Q6 Û8‹¢XzM©‚L§}±: QP+ćA;êw¢áFpÙŒµÌ)!’ñûëþR”^öË!9”’ K%•ÐÖa+»W¦‡cÒiMÏtª·s®Ì%ÿb¯@þËQ<#á.L£Š Þlñ¤‘nûi†ïM`ö%£)ýÁ®÷̆—¯/@ýÞýPÈI9ìC=Íÿ¡OózÖçŸ2©š5´FD³^òiÖ…6Þ¼´ìùQüø³šU _ºŸ™§5†Iò`Ñ©(8pc÷gO¯¶£ “¿-º;÷ ²Ëg­Á¨ ýx'á«•Ѫ£`6ñª×êq相1òâ°ð­#˜7 jý‰ÕWjw7 þËAmg-M4…¦R Õfèö]AÊ ÀVÑ,Hñ±tè4’¦ëõ¾q¶ X‘]A–„Çí#ö ÿ3CžžÊ’2¤«ž—`Ç©kp¿7’ŽJR§‡'ˆõÂn³—"sϱ®)ÚŒšù^T Ö&fò¤­ï,ÿ K>mW¥  Îë> ™`.²Úv¼OPsâ)X®òý;•0Bª>p&ÆVó‘Ï}î}î÷Üó¾çÜû¾çóyžÏ}/-_¿†Ý¿ñ8`,Ó·M¡×öëӈ̩ÔÿröíQ£Y“ÏRÐ*u8woÈ]áÜNZÉ|æ!Žq«˜§T·ðÒt-gò#¯Ö'Ô£Ë(«?ƒ­]œ…vW®BȾ(¼øÏ—>öîaîÂ^m´%c»iN 1Úº¬]à¶l6ƒå~'pôŸ?ýù±Wå•’«|r4xÚN8ùÀ ù7…S¿rwLܼœ^ƒüóâÙ4†fF¬¦–ŸKAòÐm¼/ÀC×ô¼€u[„°Ó}Õ¼i@Z0ƒ¬MºÏ lؾO‚Ôú©2¹Ë$Ó•uüh~ÍíŒãñ¸V2Ô–Á “j”]6ˆ0Ô½íP“‰vN|DõÌ3׃}k—Â+ùmpô^.áïÄÊPd´™cSÃt¯~ ïÀî‘ì–-ߘ±Šn¼+¤Ç´‰Ï¡BE^\óü‹ðÁù)¼I1ÆÌ®#è÷Ä*è “ hUXwûãCV%ÑŽ4ú]Àeä̽šYD›þéL¥MÑDç’ ý}VŒ0‹3˜Šqœ‡çÝaÓ˜>¹$º‰K:1:ç)óXù<&W¨qwEaá™*&!Ìw¼„Ó .Ḣnu "œ}œûŽãt?9øì•Ë9Çññ«4ô×zÆÎ{6—RYžv`” °ëþ}|­Â sö+û¡6Ï~ÿöl¦¯ÕÇ·Ä2â¯aÍ,cºõ¯%¹ä™os•I½Â °2x‘b= *´Éámìy§­Ü¸ók°c±.¹»W ž~¾] `¥+Ac/Zà3Ž&ŒH㛥©pǃ£~žà„ýå³Ë“•„ÕÖÇ0sïLL)ç%ƒ.i(,R #NEL‡á6”Ní‡G3¯À»]&u-™q®[+d‰õ"{ºb®‹‹FcKa˜½ÍÜ8ØÍì 7aþyå’¿7ÅH¼ÁÜ–´}Ý›È:‹ÞïÞ* çkÇɶðaƒ2å%žôšð,˜“#Mný„ÚÌÇ…R¸YGn•ÂY1¢}4)?U" eIÌ­±Ø`[ƒKFÂùõ Yˆ7D›á•‚ ç¡zö.#óc·Ó€ÐOh:oQÝÈÌ|%C÷Ü×ÀœŸ; a€‡YôÞ܍ޢ«ÄÊÙÚ'8kÁšd±‚¤­˜@·×÷Zs7œaǃvå‚õTpÞ!²PSŽvl œ+8þõHá‰LŒîT&þñòßÕÌý­‰'þ0 t*(ÉJÅæŸ˜ïªïûÿØäø¯Pc!…˯=ã4œÐ€þ~>àè†H^æå?Â6€p^´ä³oîöâð?~º1ê9T)®@ÇZC&Ú§ [ÖI‘þþ“Øn[Å|•ÅÿjDwÙd€ 7å+!Kû3ü½¦KXkkl½f…CóUãS9ìšÕ¸‡iø¯Z?"K—iÁÎýî r÷¦­ûæÚ"x6îM;³"£À;?ù¯×áÜoñèƒÎØÜІ*SDÉE!:S~Úz'sa:h86p41÷®º¶I1¬Ö¶dfמTæÒÐYŒ¬ǽ¼ è7?Zt5þ<¤*×w®!Áç$ñcªíŸ8Çd&ne<ÄÌð4<Ùàv”SŠÑëz+ÐßÖüѯOþ>E{ý¬pB]ÍÞ„2a·ÔQŽþg’¿f@|Áp°[LµÁ¥A•µy5îi`¯dÙ0C†;Q^¬þ]ógÀd:}›¸ïû{BƒSØ´÷aƒ´)½“†Až:¸Ze›$ÍQ¨]™< ’Í}ÿ ÚÍ: wmã`ìÖNÔÎí@O…Û¸ã ñ[†ï…åé–nE˜6'‚±ð £oyqû [ ÷71@ûY5NÏð‚7Èx¾ þ^\ÏšUO%µoÀÙË7ìmEø`Ûñ1(_°ò÷)2iKÏs”ç£x‡‰ ´Åò[Îh°Pþ8%ʹ$EO˜yü§YƵól!eo0k7í,Ö®$d(1•y?=–ŠKœ©[’…'ÇU‰ÙI1NøAbxL—ζ$;ßybïÈ’ªmKfHÉÓ3÷‘•“X¯Ìè+Ù T·r é>Z…³¼_±ª?âO—¿”¥Ó ãp²/öÀ{k1’EåŽ áÁÍ´îÂ3fʱ6ì}t£^`ÇåCX0¡å.PoÇ¢ØÎOLÍçø­Îe¦o­bÅÏ<‚ G5qHï ôëzÁø÷øs_"Êî€Î†eŽÝd{íIGY=ŸÂ¹~[”’¿7 B¹˜½Ö¤sÌáÚ° SæøSuækЮ;ÃÚÝëé5ê…Öå“ñqÕ'+ÀœŠ9¬z é™5üø“„O+VÀ¦Ô÷°0Hý¶š“Ÿû¶@»é&zÚ+¢ÊõX»!Ò[R„¯ù­Iäœ+°gªîê×À¯êÇñ‘ºå.¡ËòTð|»&~Ö¥lÜÓ{¬OâQ¼¤yz<˜H³“Ì&A!­¹qSçrV}­ƒF—S¤ëÐ[lm1 kòaàœ0½;ýwýÊZP‘ÚAIßYqð@|„A‚ÎgïÁ†]Buû³ÓèÌ×Pͨ€v Ýc»&±P]ÝJìÝÛ‰^K#鈣'õ| J{&$È&äIy1‰u³‘h¯CnÊæI{3$®ŒÃ·™t“âvœè:V=§aíÔÁ'HIÇmö`‚}ÛUKN›Ýœ§A…lÒ †Z¢µ)ñ'+NzÃæOz\›~ â:Ýÿ®z²Œc=§àV£åøÖ#’zo8É<ãhѸ¬ØþGˆn”Ö«;ѵ Ï^ÜKì-¤·Cïˆ[Tûp}ÞHq׺.HÝZÉe¶l§2ϳ¡DowÑ,k’u ÜzQ!koÃD—‰w»ˆ6ǦÑNWGò{©6ý¸Ì–ÒIÞa,5Hy‡G^cyâ.Ù "«³œò/Æ4áDÊXIÕ¿:¤@Ï9 æ’Dþf Ù¢‰®›\cVÞ·£’_½Wãwzûc4Èþ`ʾéÐô³;iÁ–UÔùèuréâyP,V ¼zýÌÙ?œŠ›Ï°Hµ7ÔϤ6îšD<,BoÛ×m·‚;gÙEc"꜉rb£Õ\pj®c¿}…Ùd °Ùr¨dÊè ÂEjϱÇ5•N#rDqì5<3!ú ' Ñ´¹át`K :ÖÍ¥ôÏC¬ùZŽÝÍì×?¾uñG~Ù;¦€KíѸü¤(³)ð+Ò øb LBdË0OȲu•è;›8ðÞ×óO0]Â<ì“Pî1}‡°¬¦m7+šñŽÝ«™ MËÈü)0CÝí©åššJ‘Ç$›=¼cœ­"BëtI{^6,^2ŽM]Ç9üd¶Ç}B‹K’¨^”39ŽÕÄ]ó6¾‹ÃÂþ¼-LpjÓqÆ’Ô²JjXñºv­Ý%¶ÀÁ î;/ òƒŒøF¬ËGç*o²ÐÄ—·ì áFä›ki]ƒã¦MÌã_Ï1f6À azì÷K?ó,vÚÑ2ã=L›ÛTø,ü ó4Ñ„ìV0Zæ¶÷@mkɹZ/Öu@•e8T¿¸¹ƒ#èñŠúf B™Æk4/¸†§.âéòìÎgoÙ¿ºšqKš‘Ù°8øïµ¡¹½mQgêf°¨ZŠÚ#¶PVkF¬Çåécç hÖ|Ș+í¥‹êàÊÏ‹˜93–‰­"áßq>4³w[RýYtOàòFVîLbþÀLT~:›ª¸¨?cù×K’ëB—˜—‘W@ 9lŽÆâáe•¸ ô4¼Ü]Õw˜èm} s²é6òÂVðYòVð%À®>Mözb6vY=cFûòi+Ä´ WÕ9/KkGé‡÷¡xæÅöð;}zírêûÊ’ê77 úcÕÙúàË\ö)/YÒÖc§à”ɺ?Ô§&Ómñ–^z+íM‰è7ALÖ»?›åé2>ÒÏ<…Ü/¨JIîÚ\•tÇî·Ž­,„Ÿ²Chs| ÝÃþǯ°4+t]ènbŒöÝk¶tc}b»øg;;Û$ƒ¦\5¦ízkˆûj)âº}M\R&ÊmŒÆ¬qXl~|OèÓ=b¾ÌµA;rz¹+Nâd&yžÓòî*“?TŽE¿wãÜ«¡ðÈ{’¯'Û¢3©ÄtqX¸OƒWM°:óAâïCpï¡k}Ö×jZdž­ýÎ{‹S‹½ïØÝ—oâO†1¸Ýq^ mõ™h·ùà%}E"%ENŸØB7 ƒŠ=Å·¼¹æL.ËæF°%DmŒ›YWC%÷ý§Çfôë˜7á×°ÊБNŽsfõ/’°LÃ’èæ½[9. à¯ãuÌíÛ’dOØtZh{‚=lRÄÕÜŽK6dcÖàvŸo#Ó“Fk³$Ù`O²cõx¸~3˱㼩‘ º[.ƒ|²þ Ѧ/>^Á“‡Nîä¬>³; ïÅîAÂ2eZ hÁ®&ÖïN¿•*Ô²cìÜ“Hw‰,BÕ¯eX¸‹2ë!_ÁOÐ<úÕÆÏôæ§Šñnt¦fIá'ä&Øq|äæ>**TÀT_ÿ€«uô&,¤"åõ°å©:æÎNH¬Áø ÖÔ3L›x‹ÉÊ &ç®RqyQºmÔ“œþ÷ž¬3§³ÄØOYRøÅÎjJ`/вïvÉÒo:"øÄþ «×¹šütý OSíØL*ÌjN?‹E¯uIXž7ùšÚ e-_pQÒUø’ÿ›u‚£Ø–õ’Ýu…E\¬ªÕ,çôÛΡ‡¥í¯=#z%!ñÆ ´2^„3ù…ˆÌÆåc鉅7Ï¢^a!ºGžƒ+o“hŸo"…Hr^#úÆë¢±›Y±i;(†g_Ïgð.³žÕ,m­ýĝާ Ó†õžlØÁŒ½zÒÿ®¤‹£%Aê²5îgŽ^‚^¿=XÞBÈ»³a¤Ld-Þ[÷_ èÅ:⬜K³4¾ *OÃŒÌ/LƒÙ0¾8µ›žÜt%¼ðÛÒpÜ:ØÈÆÇ±ðu¾•Oocôޱ¦ÖÙ\Ù¸åtÝÅ^0Z s5;XcGAÒûØéîBœÙòœrE ÜAŒmÓ¹DE/$³BG¡³#<Ûô郓‹@¾+%nC­ý¸8ƒ‡¬ÿzöðzÑBñ\&!²éç=É¢ínô^ìl̸Üî{öÑ OÁdV)Êçú4¡bÿûPE1×/,ã£KÊ ýžô£|^>‘˜6½U‹ÿ³%ƒæBd®Ô'æëðS*o@úõ#ÁóB7,}ÜÍ,Ywƒ½w·¤;ßàÒ™ÏàÈÜS8,ò€u!{ë*™Ñºuf)ãåÚ, Úû×ÈðÂï,;cs•øð_MgxÕá Ñ“Žk,ÛN4xpÏ"¨.`ÞûÇ2U¶ Õç úvªpÄÀ‰;çügx®üC_Ícží‘¦}s€¾'J0¡ÄàÊ[yp„ó •Ö¨Á89¯ ²ÿB›ðɺóh ÿ«7.¦™Óµ¹ÕÃïðdìlÒ¹R 6¨xÒ¤™Í\?))Z¬‡\N3óøÃ £•#Bóµ5Èâmßg±"­öçÃJÛudT¨VérrG˜Œ‡näéýBbêS‡aÞ@áI_úkü55Lù«Ï%éé°÷Iùº~xšJÐ{Ýåý‘ò-£zzUZa…yu‹ÈÇç–™Á¶£ ª—‹÷¸“3SªèØîp²­"𳣟ּrA‡ꯇCÍTø`Û*i@?ÍN¥«’é½Zaš?çÎçÐg~•ä´×zÆH¼„8"™Ù\Œ¿ÂiÈÒ3O+‰Hx:çý_ÚÊccWâÜ'¿KSE‘óè7\O—${“£íÀn–uýÈPZ¬Ï"rC æÂ5vÝ'uÓÉ’ÏVø\Þœ$ê©à¯SSó}ýWº€ä¿Œ§;È•zìmšWêK«Ê[ðÝ`›ûù"¦\zßÕŒ´l “Ãä…d;½:K7d,cì¡^9ÊôÎѨ3Kœúó¯Ã ì·µTÅñ9iâÁXÞFû3ˆœ•1ý"€—¦R£-³I¡óTò´ÁŒ noÄÓ »a£™ÆañBNé$~Т®!ôëÞG ZH;ë¶’“ØîT½-­QËÆŸ<§¸o ÀêšÈIfÒ±G¾x17™|Ûà˦ª­'J•éŒñ•$€×ªïuƒ3‹sÑDõ-2y@|ÇFˆ--„sJÓIŽ•?„¬z„·Ï`yÀ:zVÒ‡„¦,gÎmoE½GÛIÁybŸu–æzEõ¬#S×騷aHRÜ mà ¥µ»É­FáA0Þ͹kX#|eYààCƒ·Zú¾l‚§¼äË»gœ?ú±°ç…qŒ¿ç×Q­Ÿ÷irA'ÜÞu’ê½ %;3/‘ÛYלÆTDͱ'ùû±oì$xæšCôMøö­™˜nI;ó¡—)¤ÆÎöôË\~ЉzÄ|ôêöaà±nߢD÷ñØÀƒ*â,#HšïÇ÷ýítšâWfN÷&q™+Q›ªHÞn4&q†¦|¯Òož¢ïmÉüÕýxx1ƒE.a´ÇÁ*7èÓ„˜Fª±-—º¾ñ£×wW uñCœ]™ŠKyë××AñXÈò¬€ýFǨ°}Ž„Êá½OzŠ™ŒGÞGlÝ”tTfN¬=ï9†cªŒÇÞF°ër§%³.ê¡äo: <\¿\ª°ë *”­²!b“|¾Uš·þwøOF¥š—$·žE3¨bûSTޤ§rdÉÉÁô‘òfâÀ_IL³¢-¡D½híK GâÔÈçþ,²`‡5õ¾öŠq0Q„ŸM ”éW&[¿ЄÄõ'Á%%ßjjSî¢|òc]˜ázì5ß“Ÿƒ4}S?Ý>󴿘@£Áó¤2Ó’Z,ÈäÎÔ‰bg ÄÑÎgJº×½½¹od‡Yï÷yÜ››ëpË>Qæ’³.Y¾=œLz™³ßBÙW×=I²d”_®aæÆ<€…¦_`˾h®ÝEm¢lH–å0ʶg°ÚAyqUþb#uç³2AZ°„NÌÀwS91mÞ„mü)ÄñUÓ3&¦Ùïª\†ïKøÉœ®E”¡„É÷3rëÁÈìuù‚^ºÚôÏÓ>üª¸1d挩µÂ-†ëSYt|$Bi/@ÞHt‹8Žçæ¼Á#ÜǸhod÷h+ÅGvë¡Äæj†›|Û$˜s¼–¸Èz:,‰¢Ã;¶Žñ’üî+6½à%OÛf€ëxvhÅ}6¢0”¬äS 5¢–T¹ÝwLb _j¥WB„ ª@\Ý•.KÒ"eœpj¦@Wÿ)CåŸaÄîŸ'Íøˆ:)ö‡Ÿncöeáº;†„'Y2ÌHc¾<$æÚkIÍx/ô^¾ ×u])W[Ä”û˜_ƒdybá*X“¼¼Ä"_•¾þ29ÿCÅaOÕf+cbqwæ®VÈy;c<ã¼bæÞ̦Yî.$õÐyæy—:M åæú†ÃÇ÷0µ¸£gw¡üã øš' ÞžclrM%$„ÂλS0.3çî([^ÛˆW5£™2¶làâëìéU_ñôÂDˆ…ïnæ%CÛ㫸]IW/̆)IZÔ¨P ‹Lˆµì8,‰¸Š+yƒ@ûNÞŠö‚e½œòmù¶ô4vݸ‹/Ë´pë†~f°(ŒŸ]Ĉ‘'»]©Ä¹ètð.ôßIaû/×1²?±w+­O¶‚S÷Œ`ç{Iz·x/äKüŵJñNù¶ºï7–IÿÂß‘¼$ëXwÍ+7²ëÄ$Ÿ­ì`îN-Ä•ŽÔ¥¨‚±³D1¡Ó̹M|À ‘§wÓùÈbÚùo<}~®.Òg~ú8“d}l” Ãð¥lä'iã[“Ø»—!–ìo{îŽö¨Ý ar͵Šu²]Œ¬Kn<Ÿ œ &3"‹1¾ùd¼$92ëÃɽq-rÄTŠNÒâe÷ÉÊÝŸì#»ô‰Ó‚ÓܧWæÐcKâÉò”•Œ…”¼õ°>ý%¨Çµkuˆ*PÁ„Ñ+3¸ŒKe+}åD¨óm¤c7¡AÁˆxoŸI.ÝùÌ ä.Á¾Ñ,›är[Ãx©ÔpT\ÄáØ œ¦0Šý?eÞ¢]ÚéÔqÖt¢H²9§îÎÃÕš÷¨Þ‰e(k9ÊX©HþŒ|gÜñzM1þvU$mVË/G¡*ʦnJýÕe¹„ó` ¬Î ìÑ4Â{DšžÏ×Á7Íç¹Ï‰Õ*râiÊÞJŠëöaËñSpA| 4¶ªÂ¿'fT' šŠÍ˜~CÇIýÂCäàµSäSJ1¬?˜Du«+`[™Ư¨ïv“¦ü§fÒÐG$¾k¥¡Xm%QîƒÓ½(>´”øH$\‡˜À¯@­ËÕDÙ÷› õýL«ž¦7·Ð¥›$I§…>õ]çN,nMÇÐc©dÛÆTÜ;žHΠЩÑ,'ÌI¼I1ûdÙZÚ­¸¡.’äÑ'Çþ¡Õ@4å³Å×£Ý8¼$“mr;ICRvàâ© ÈXj)}]À2ëc™SßÏÁqeCººC‚Z€ ÝšÃÁå_’ô;ºê÷Õó`ÌiòÞ>lK4Õ\Œ.û$FTWsæ'ä‘/Í’tÚÔ Ô9ôˆ*yH“¹ ‚Dø)õØ+MS$¡eo?ˆ6¾`=< ¾`:\ˆFÏ3äÞM";¯’‰Ç5,ßZ_¢ÎÏK{̯9=Išþð5Œ|w$Ÿ~}‚W¶°Af¬8LUƒ idzk¤-‘r›i7×NnbÖ42ΗoÁŠ¿²ÍÁrq£¼ùfIÏ ëBAÔ\†I\B;SÐ)ñšèAÿ5ÄÎÉ”(—Tóym’¤fã·ëf­ûƹ¸»–ó‘±Ë/9÷¥‚ðù+Ìϲ©4Œp¯ÅìÐmT³@‹Ô+xЇî³0ÍOt¦‰áÝ%;q\ïô,[É*,Ø@Ç"wÑ¿<9P26“È\¨FŽÁ"&ý»Ndû0Å;Ü!7ÿÔØì£Aü+¡©¥@v&aéwÐÓlg—žµ ÊæLcý½[‘zFäÆâ &jg0á± !Ê’ILcY*îö0¢ÓáÁÔeÖŽVr?Y õ:ž}o%.¢y29Ë73s®â÷Òƒ´ð~¼yªÆ9”„ímí¸öÀ[P3œÚEˆ7W ×Âþ¦ñkÌ4ýoÃ]ÛŠÑÂu'§þ]'³pŠ´ ,$ù*ÏÙMM'y{ó¢é|ìÊ#¢XË•4ÉVŒ>-A]ØMdoÿÀYš](²ˆ!eëÛðÇŸèQ»d¸!$fö(|s÷ëÌ£ßç¿‚QoA:e¿/‰ùyÛº·CbÜ*øpÀJë¤P#M‚ļgî;Tâî _ºæÞîÛJkÒÂÓûšÀé»õ\k½ÒÕìƒÃxÐCš :Tƒû4ë9öEì³ÆÇÀæZ‘!³÷L›Ô=ö3kEcÀS¿Ód£HžÑ$þ#êa…¨7×B«eÈíŽpè=Ë~u; zY‹À3îÈDs¨ðUajš ÈíS'gÃMÈ•7Ùÿj2ïÉA>³ÖºÝÓk¹ž9çHsd8¶ ¥“C—% Ïø_ ¾šŸf¯#ÉwΡGîyNßH\Ü´&о>„ ;¡ýÛy:;Î~)’FŸWÛ ëvxTš“Kv ¹¢ JŽH‘Â'éX¥†3¢æà+7yzFLŠ=YT‚»ŸæqU¤²Jo‹aª×žxÌ{d(^"õÁËÑ.ü8“VïÁkBÉkMryã)¸´×…¾!<ÜWllå7rZ;†ž%[á³\ìÙ6Ö ‰óÔiMZ5¨¹k°ïUÂPd¹*˜ÕUÂÚoª89Τ$ÛùSzñ*úvÄ`£¤ÛЪB¹8þµ>oÏcjÞ³¿^óB]…£] V±þØš‘£ÙóFÇA¦î›ÚXÃÆ÷«ÑU–.w/!ëzD!Ð×g<'¦Þ;ó“ø°‚жù¾äœ®îÏßNw½Áðå\6zÖ ]·Œ¥vbLS'÷AaÆçcÄ< Ð]ñ8š–Ãõ¡熡”Á)Æ&{9¨V°3n·cøÕÓ˜uAš*§©1< 7pÞŽÊ+'O.13¬‹ÉâAÒëÇžöT‚e]¼¤Š§ŸíW%ŽQñ¾+ò>ÍúÛqÎök1Šó\HÛp:÷Âé2ŽSQ&º…†'·¢µöÈkdN.‹VËðý-šzÛ™á»,KçN}Rµhñ¯$w.Æ>§(šËCÿÜ,†®ëƒ ÿ÷öì–f¾õ0énBÌü-F4f“(=m»ïKÐGIƒÑŠ…—3áÃé|<žŠºÒn]GpUÄ+V{’'Kž½ÅÌ8tšcrhyYU÷!6±¯D~¼õÁxzY$šÏnÁi%ɰî=úl½hé~BuLø‹4ºÿ µffƒ«ˆ4´¶Ô0Odg`À÷üdLÿb Œûy1Ç6çAŽ/þW«z»T9°nTzš=}ýnši ÓNÒË¢ºp¢\ ²¿1vö6¤qA1Ô`\€4Ù»Ó%&çF€ãB\ FûÑ ÃËC¤‹]é„ÿ 8(u€¤ÈCÄ1;¢~LoÎC‹)%t´õÇÝt:íým“pÎÕshÍ7z®7¢ÓÜÃxju,™™¼œŽÌÞÊéTøÀôX†ƒZÅ|râöü:ËyrèW$Fl·&i/(óÕÕŸ¨§t±Å;ýéRÞtpW½àB1Au99èq¾nD{ Ußs‡íù.AžòÆ£„V"þ›“Ro pî•iäÜ(kS²‘} þ†õWw…Y—NqOY峌ÐVnÞt²ÞÖÅ¿¿‚´åi7+ѧLòœYã-*t•ôf0ÙŸDC‰“ÏIïÛiä÷.^hHÄîe‘ú¡–~³ƒ ²ïPæxœ*M©ï2ºÐe½o'>­ w'Âð½6«™Ó@ímtžƒ]x^/ƒ¦÷Ð-ç,ZÂ=¨û|”ÍàÌBKN«)\õNŒÏÐãGÐ/SõEBh˜„‘ð"!Ž:䌧{¨wu\ŸC÷ílÇ3žµì™}¹ý(Nñ¥å¤Í Á—‹st¼™OÂ>Àsך®“úÈŠ¹YÇMßÁžm8J„ìNÀ‰Ùaÿiœá±wó/n÷½­£·Q󦳧]Þ ç&'\–©…÷w ‘®+ܲ׃,3sPc}f§Å4¨Åà„¹)•^_)-Oð_Æ|òÃÆ‰‰Òµ†O>!\çBK¡$´ÚLæžNØ¥*ˆ‰¤å÷ ‘³Î¼ jâ¿»Ðæß¨]ŠƒVݨÀ!tz >¸ÏV5áï3FÔ0æ MP×¢Cõx gZí¹Ü9¬ð™9pã¥Ü³'eÒѸ%—‡7®Äh§;8z.ê1~•SXy?Àæb(l‘ÛIÍóuÈœeütßm)ú¬×” Öb£çQtóf™MÃý0Ç)¥Ôæ¿)}\ßJEzïËuæYX›â)Íž:òû—ŸaôÛS›Q1H’ðÀ?3` ÉŒ9‡¶Mã%W?PP8±™‰Mkr£uêÛxÐÅ-Œklz‚déŽáîw¶Táí ¨tWEÈ*¸“PÏ|Û:m{ÔiÕÕû{¿ÒqÁŽ^ÎY>Aàm”#ë9ÙØôòµiŸ€J‡çø5(’–€ï™óñð”fü>ú›&}ø„þ@‰n9fDxn|ä>z¡IÙšpD€âQ·&öD8þë`¥¶\g/ÿâzì^Býƒƒ7Øó¾:pÉšs³`Ž['ê™§ÃLUeZzh9=Ø›ÅþÛ°b$d¨´m³ÞóÄÈ0Õ6exlçgv³Cnæ'‘Ë·‚Ì]fªi ü½x…x‹ïe¾ø7hÓJ©NVnÚd~sÜÏøä‹Å/8±;˜¼£™ôW9èÝ> ¦Y¸°×z¿s¾ØÊY‚KK^¦÷¾&þ L„'9Âý¨@BgMEÝÊ06‹Õ"Z¡¬"Wûõ¡Ðú`ZþÔNÄ=bv Ó¦kÿ¯£–;EñPà²à\Vt=ŸT;ÈÉö‡Ö•ä²?MÛës#ãQVÄ* J­Dö´0á>Ýñ›àÒ®+Ж‡Ù‰çðý¿»xZráÐDjh~ð±5ùÜYϤ½Ó£S–‚Çýݰ|{۠Ї9n$ò…8î‹eÍæèÑñ &nÅE<è“Áå3 ç^”ßåØ$L¡cF×Ùk…}Lø½ùDqd?‰þ¼›Y è¾ë‚ÔÎ2•í+{ÍýO§­õénŒ+…ÔsòpíÉd~÷ Âw…ËØÏJÿÕ‰fyR" ?ý¿î³zÉX“3ŽÚ QöÌ­°ó˜nr2ÖäáªA?’ŒÏéÚMáÈÿMJ½Ã‡3]-öÃS\óaT}8§òÝÙÌŸm¸Œ·¢Up©rœ([^®¾ì®H;ðzRøª‹’ê@Gv£±I*êB9áÐÔˆá–jÐmœÇLÌõûhgæ‚joSX>¿k`vo“vÐý¯/»÷®`ëìL<~€À¼É<-0ŽÃY«¡´b Ã=Ð ½Ÿ×‘Ç•`&6Êj=w¢q§™¤úÜ33=µ56²þsÕÈRé« áƒHÄ1ßx똮ª&8a6“õ;û€ùÔ›š}aÓì=s ú'α–Cˆ`‚Õ¿.«Ò¾£S³,+ð…Ÿ¨6ÿ€;S,AÖÒ‹ù*SûÈxv·M$ˆ‡©Rð˜FUvöÃÏüH°í‚ó½È—ÔTøöldÆ£Z#+arœï´!7¥‹ë¿ áDŠ L^Ã;ï;L ¡5T¾Vƒvn(TßåL}çf͇iÚÛQ‹gÓ¼X§ËEh÷ò/¾¿—ߦD…Œ«¿ :qKØWìÜbjËOÁÔ®mh)ÅzÆOæ@Ñ—¼6„»¡c­ñïÆiÅÕLô=^Ø9"H;6Ç‘÷w-¸¡Ç* {ùöÝôC)p‚®_/H< ÃIK•-yúo6¬:½…ú¿K¿wAÝ !0áÚS®ð{Ü7C„¦µžœop:†P­ªJTÉãgKñ[Åî™_pýB} ÃµÒ&k«­¸zŽƒx]X–ný– ·kÃì×w¬£Iï'ñ®‰846˜ éº‡L¾f2¼Ý¿ù?‡Â‰5²øGø įÐÏ« ø.µŒð÷M!_²Jq˜ÃкWKˆæö>$¾ã(ßñMÈ„ÊJØ¥-Gds™//Š™}Â>ÔÚré€4öÈ¥k ìŒÚ“Ï^”á¨ý¤ÄÚ"CÇ̈ñI|rÕ/ÜÄ6ÕT@é¢$xr=<Ïv`ÎÞÃ8ÃÛÅ~ëÌ%dÓö Ú|—æ±vöú$›>™;:=–c-^½ÈAXvCüÉ®†d½çX“äËìo¥ugα¢ráÃKOÂvº:K08ŸJf„àÓSqh¶ú[þí+#ƒÙâP –~¦$æù2dB/ÿAMksÜzR–>µÉtCä~jwK_>%zÙŠÿó£^·a£ÇlªóS Òá¶Õê½1Å¢û§1Iç/k²Nõ›™QÞCõ°ê«=½=5œÕ˜v޵Ü7ÞF2¼ o`^ž9öï8À.‰jÅ{Ad$\7$ÒǦÇà”Ÿ£¸Æ½±îy=˜ã¡j€îIfe8õØhÍÞøy}ÿI’ûëæâ%¹³ìÔ C¸bÑXx Œ}<¾‰8ì?¹õÞ8¡›,Ÿ/1ÿ}y3߀ÛÁ\y 'ÿ^izÈ÷;Ûí?ŠÇ.|`®Gô2u%ÇÙ!AsÚñ÷ÝÔ.®ÏñÈ(ô6À¦²Uàýý2ºŠÆbt¦* ÛæM§¯šäUWf“G~úíW+µvHeŽüÛÃÆHï%#êÚ´Ø\‚r[Aq,ŠþÎÏF!ÉÛLiêMܬš„g/~gÎUàÉ[¯AÄL™ ^ªÙ†sb±ñæD‹"ÜÊ- xyxILì-urÊù=Ëy2Þ›ñ‘ªéFDüvñ«Ö S7-£k¿ÁP-3ìòB÷ÔfS× &Ä/®Üö-§ëÚ!xðÎNõ¡þÛqÿªz27šC¤ƒNÁ™(²z vo1ek_&3²k"aqÏi–ðƒ5O1/'”ðì-gÞÎG£/Vn>ÌY×ÉvêÒæ7•° Ü ¦ ÿOãŒÕ˜º¥GA"i˜¹vn(‰`ÎçåP÷w0ÇÐ’æ¾Ö'ççšáèÐÌN¨Ã|—ŒåóRvl{Srjät‚èqyTŠ'Í)~X_™Ï1ßü§l8ˆ'd†˜_êÒtý¾2ž& ç~²¿?üåp{™+§y‰õlÆx¹.œù›j9Ì•›Æ®×߉7üÖPž& Ps?~‡d@5ÈJºÙ¹¹oÊhã±qØ>2jY0Úz•ä~¹:çƒxàs<éßÅHË9ƒŠF hÆÄCfO3[9µö„Á5<»d½´c*í*Óc÷I©’QÝ4ÿ®8ÓÜ>É©…™î %zñý0òÝùO{ŒaN<°÷jNK­çŒÎXOš‹cˆÎ4vÑê84ÙwŒñÝÀ…•}‰X“Ëüü{ŽNüÓ¥~™/˜Ï7`ŒÀuö©ó_È,ØB{»§ÐãQ´óp.Í“ŸN§ø'a¤‡'í–´†,¡ýtîï&ItÁ)êÄÀ{*Iû—5‰C“Y Gê"È¸í¶ [ŒTié×Bm›NœE@²ÎšŠÒáæ³æõ W7Õ¨eøo°V=N[û.Q—Fîöð u{ÍW±[Î92KNÛq–”¾€¿y—É‘*ª=¦±*è¾ì)û'n­ÍA¿}vŽ:ãØLM²ïÖ>R³—‡Hˆ÷`b]ád&€Iï(…·ôä^ì1‹Yâ Cy’q9ûšJçNÈ’’ŽZÖÛc Õù½™à¯<‹D|»N&e!SÜ qbÊôͺ¥´Lùù<|–c}£Œ•o` ãРF™Fn¶8‘o &ý ú¤v½vóÚÑÂEz`"¶”½ø™\ïœO7ÝFõ<俚ЧƦ‘y6Tðµ Ã¾àÚô\™ºö뺻Lš>(Ì&Í~d¦X3”¿_D÷™½¤Ï_¤¼!)¤ç£*þ¬Ç±C8¾8¶ÿÚˆc{·±=j_YÛµgaEéFúèÅmüxâß¡†ü@Ý2žf­õ¤:Eql«ËYZ"ÙJ®ÝÑ¢ÜoeŒäÊ4ˆ#ÁΉd. aFþc-9Ý̳IüPºÂ?$ýüH÷úD’S´Wÿ9ꕲœâÆ ú¼ÈåT3æ¨j NU ¢…|RäTÒ_<´ú$JÀ‘— 1’zÏêßIb°¬VÿžE'¢ë¹¦¹Qà¿?š]ëBо½·ŠçY—!¨ïþöhÓy9û8‡b®Bfë|ráÏLº')^ù«à›Ùm ±ë3ª‡’»?ŽÂ(¼Ž›u„h_H¬Ì’ƒæ+üÕøŽ{A¡s,øI³N,òòÒ¼;Åp":”y8,MCߟ…î¹O!|€‡±x†•sOãcgA²¬ ñ<¿ ©›îñ“£:—6„üÁÕ¶3_Ž~6ÂTšÞ‡G)ºlÙSøý¦ßEùÀxJ3öHÑK†CèRÅýî¾?íÈ`ýn>Ä}&´F‚$èþÂõÌ´涬lüЀ3‚ˆðƒ¨_ΡÝF3@½ü3÷ôåíøêÂb½«³‹˜+¹ djùUø0‹çöý@†ÛÂìu3aê}¿ ñ¶y\†£ÚÝÆÎuíç„(…íjW¡°_\ Q¿‰_ÿøâšãîÄß’ *CÔ³Zfüu:숖#©&ŒŒm>ŽçF£ÿzVÐe—ÈÍÄüÐB´[sï~Rì¶IWLú‚‚;/aØ)Ü÷+pXÆAÚ†»ôa›Ÿ¨ÏÐOô•|þ98”¢;9ú€ƒS»éP¦M^‘ÖuðÓ”ï`¶Háë—$Q‰è±úTðì–Âã)sèü0_ØË{€<Ûï‹PÔªx&Ö%Ó+Àcà 15(Eõ þ`>1ø‘kÑý¦|_Ä6JbÎj?Ãç‰ëÁÚp˜Ê%FyQtQ¹æÝ®EÌ» =[šZÊßÂ)‡kpci»€ÀóV…,#ýh¨²Öñ°ŒhVéÙ˜s* 0ôÖ{uw¨fP 1t§Ÿ‚TàÜû ì‚S#øÖlì-›KðcÎÊ—¦ÎÖSIX»œh­xyfë…·“绦 £µ¼ôßïLZë_K?Èä¬î<Áðm„‹M9ìcö71`¥)¤è®¤U÷±Öñ.Ý9BFÏ¢ÂÜÝ4ü½Îï'Z+ȈùYT›jL¶ÌÃÞ§±Ln£ÕïH¥¡Øšõ-&q¼£ýGrñ²(-¥Kø„‹ßåtÆåTòÈCš®;Ñ@·½7†o÷EÑBªï¯íeÛ›Nï®6(OL‡å÷zÐì”Ý*Kv—ÈÑ[/ÑqËjü¢Çþ0º –Ö_¸_ŽRh ›‹túÀr‡Üäñ)˜µòË÷º˜=Pºû¤Ðzh5mÿ#F+—qo ¦Â®pa<©ÿôžwr~†SÜG?àÛË‚Tyk4|­ÄÅ* 0ͬU\ÄÂo}œ­*›°q¥,û¶ tµ“‘ãðs­˜ÅšRÔ9?˜Ýù‡´G~g?ýÕ!WàÌzŠë_bÔµçöナðVZ„ó\E7íF×k?Qnš×$0 .=)e]|*q ¨†é{;ÞˆÓñ1ëI5˜ðÓÑ)¯Ù[sÀmcf¥Có%!¼{öçÞMËä¼LøÆH^0¢‰!k¨DÐRB8 õt1ôÕýÙ¹=•žXrÌàvê>çqâ}¼¾B³+½) V ±g.ãÐU°¹™ï:Ö”gW„N ¡…UIKàûŸ£ãØæFMeHæ›™(žTÆÆž1íº­øqWuŠcZ¥å=‘ˆŸeÍ .8 f¶í¢e|÷±nG:[%ÁîáU´ð% –ù ¸¥î­ú¯û5éŠÜZà;˜#óhK¡Í×ZÌØï– ccè41“†EU¢Ò%Š>'±¾¼‡-¿§C;SöS¹ñQfǬì?ñ1þw¢Ýc§þĘÜêŠéMmøê± òÚˆÙ«TI¡wÎàäà;%nWYmmt”wÀæÚ ìÿtƒŸ³µvì<)MÛ†¥ÚÌÒÌzÞ›dÛ¶:úpê6ª¯( ?Ei¬À0Æ^ú ·Ô¬p‹Ø6ºdË6º¶x#/"ð¯WnÑ é|:$õ»®í‚ESì¡3ÆŽly$M´ùãÉ ~ú<><ö¨Ã³ÏÉbgg(° a$¤Æq©Ð78¶Ôa°¬ô'nÐO©½ ò^’¤8?ÇW[A£UƒK¬$ÒChzÀ lÚ;¦ÜcT¬ñõä#ZÃ}‘ŸEö–†’¥jtÜ©‡Fä²nÓsÐÄö$ _ú®i+ýÁm´t¼— â¯'ü+¸nñ³Éž®0Z_þVŸ˜[ÃÕ\' {Ú±øŠ4<"y§eèõ¹”5Bø$ûÚfDQ³¢äœ .y–Z@â-*OæJjQE·kÚ±6"¥`ioOÿÍÁÓ9Áü¶ïα‡w_w‰G_E¬yU0pÓ Ýö“—H²GfóÐʰ;¬zîØvæ$4= ½˜ªàI&û@÷ž#à}ã M,ÀÖm”>t|¯Ò¥åÛï!ßµUôl@Dr¥‰Ùõ\úwféûÔƒ ÇæÒ“Ël©BÎV²â\MT½„¿6NwXéZÀz•HŶuþtXJ“è¹Wr.{üAnZw î&S»r#ϾÌ«‰@'å h”/…óÝ`ÙÀC"ÖÍ Ôø$„þýÊ)H_ì€Ò…)XêÝöÓ a¿_#,%N#tliÇ\Â\–U'ÝíU¬á…j8/‘N"•¿Ö¯ƒ`®>.²»‹|Z~¨þ$ˆ¼Q Ç•6ÜwŽ¿pTU“f¿!ã㤻~-U/ù{O<‚I Wô<=Ü]þ ϰʜœ›7åéG&{W¹(B¿º/¤#¥ôœ"µNe`ÏD(óƒ«#_„‰ÄFGb-(Döü™ÅNä;–Œk—×"©¨Æ©Wn±Y±Ë‰w(/¼ŒtÄä âÄíÑrRR€XlÑ'dEÕK—•>Î ?^’ReÊdü|†f_`J͹økþ6ê'ÂCŒå臛©x+ü6£•)H?ß“&Ouã›Kà—B*%3Þ€Ãy1:o¨„=ºô ›‰{ |JÆ»]æœÞíâ¤Ç»X…Û®ax§ù«z‰•ÖžEÊiB¦Ås¶ìZŒ+E›_x¹ŸÏ~`W9º.Æ£ôuj+Q‰¥" /´›J§°`k¼ªÛPE¶¥òãw:ž”¢é!näÞ'âÖÖŒU_–Q³_ðxáy*'!éói×Ò<Ôn DMæ¨c#j·vQ×ÌiDPДH© Ì¥`º[±ó¦usSf%·G‘CnÔæŽ(Uqω;}‚³º€ÿ½?l¥®@ÝŒd…ʦÒmeG¿uåp`Ž 1ãå¡AÑûЬ/—]µE6n¼ÈÄ’À ;Ø”ö4W ;K)Äíï:ú¬L‡<)ô&ÕOQ²#‚Nœ…©'Ö‘í#¢ôÈ•÷pØö éŸÌ_ϲÙÜ8-ç2¼~K )#œ YOƒAA‹û/ñé*UFšÏ$#V8eÚI¬±!M|;A:ª §ž¦‚UE¨Üz‰ö.«Äñ⿘¬òE”}È sºJå½ÓoÃú^ô%ÍÝ[H]ê7p·ï%»ž¤“í‘8žPHµA}ѺÒXœþ:’H÷ÎÌ —»[¡ò·)U± ÍE©TÌ<\ú£HÆ#i‡Ñ šÅw ¶ o‚Õ¦÷µÑäé 'Ï>BÎÊ×°7½ {É¿{¸¾ “<ŠÈâÈPøy…—û¡„v$Mæ[À&ðßÄgËm©jŠQqv§¿M2ðáAN°b¤n~¶J;1¸ú4¾œt‰ò:ú8x*PdzÂa‘îbf´Çó8½¼by=~žæ¸vÀõ…¹DíµìMœƒ©"¯`ªH9L§~`´FžìX“k ²Ð34z[ Fß“,—[@6Úïdž=ꀻ~²u¯• ¦Åu5ýHoÎT\XË4Xè‘Ì*`[y˜†]ÀË3F`ö÷Η/‹’H„œ³ë“ ý½ÀâÔ¶QE5=büHŽÚä,ÃÂö¤åÓ?Èr~‹žO¶ƒäóËhZ'NW•àŠŽ0Wõ ›²£›åyøtŠý  ÉK”±²ÒµÎÁR›Üì=Ãî\uSœBaæ:o6ÏG…È.´"‚Á[Ø—Â&ôsp>¬}EOÂIÏä½ Õ€òø•tÜ÷ ›_ç@®˜Ç§æ ©„âY|$ùǬæÑö%¸'Rz=OdŒæç±%êù4n.À ì{›¢Vb~Õ‡v×çÙïYˆÜßò³ãàÐëÏÔò┑DÞgtíu0ZÖÇ. ^Œöîþ´Æ}ò-u3}ª¡Dy¶ñ“ ÊëÉò­ ¨¶í– ÑÊ “}â@™Z¦É-þ½—öëÌdÖ}–!k„ÇØwÝyD£.öòîðצù·À«MÔÔä9ûHÄ·¬ÅÂÏÕÄéÏbÂ8l£÷K\È@×1ÂÝ}†½cèMn%æcý™D¢¢yˆðÄtb“²;ަì oŸ/ Æ36ÐI›à~´:ƒŸÎ‹ò¢óbgÓ–ÓV?Qò•G‹Æóرw$³AÌ# ‡×]gk“3¨æm8Â}ù«¿ïŸ‚Aç”ifHóòOlŽ*űˆñQ¢‡¿Í€4ËvfÊáXu  §öòÓC_?`ÖmCÚ¤(wvÉý™×˜ÁòDc} ßÓ†m ®r¬,e%!|Å? §9/a÷';uÛ,R´#g~֣݇Β¯o@'ì1“[x—sG÷ Çî{ Ó³e?7î¦ú­é´@\ƒ¾;}"êô»l$npv!Ò_\ɯÜ!\é.ßz³ÁŠ}ÂíW$2Jóñxñ]öÉôwì#™K°÷¯,͹užê Æb„Þ<’̽‰¥ ÛH‹éU¬ôqÁíb÷Áfs ýú|r#a…«]=37L›BbZŽ«t²½D¢»oÃÆ\%:<ª *’BYçšá‡w)¤(Ò¡e$‚O†ºnÇ ÉEâ÷ jsÙçL<ö2…½àå’(…*N‚ðRö7^Cï¾tÚýÈ„ÔxFaˆ$—ˆüÍ&MÅE3È“­í€ÿõñ5ï #{p%½íõÿušÓN›x¼Å› úë3ÁæK(•2³¥·z¯²êsxIw¨ ›:1 £%Ì;¥Äk[2›fåŠÇ3>B¼á³Áú?Ï!ù9¾þ ^üÓ„¶ÛÉ漢ábÂ?š¿ÏþÑœë0$ߩևqéknWZ*½qà9¦…ÉKm5šÚ¸ îXœ…Û]0%]^|é…3 °€[Aìš"4§Î‰æz+3wíÂûÍÒ!€;³—¡·Ó\z‰Ñ"ú䉊”µºr—"ûh’«Þv§˜ˆ¨~À|˜uŠ\oÊŘ~ Úvz:QQ ã[+ØNç#T'?ÏâŸÌ„]ô‡÷Ìqöž@ølÅ4¾Ó Ç¯ 3º"Î4²‘‡®Ù ’òñ_ÒhÝ|mô< ¤´â¡ÿÁofáfH_˜çJß2 ž„¯Ngƒæ!Î\‰¸w­,n8Åü™óËY!™èõ¾m. ½Ú~0«÷%›y_ü±µ€Ämãö<nŒàw'ú&ÄJÛ¼€¿Tš*zÔ²¥wÀj—Að>¦HU§j±czædé—L¼d ™rzX#®ÁÂÅ2ô…7.ÚrÇf@Eµ1¼vŽc­áŠ;æ¾cB0÷Ô&Ã5㈩@þ‚c¸ë™CÛÅǰÂ͛ЙYà8û¨$xй¿ÝÀìôj|P–JO•ÀbG8°ÆÔ:ån #Ö÷¹K•AØOžzž“¦‰-ûiÙUfö¾"Öòó-ØR»–]Õsîî:')ì—}ΰÃ7áö«Q¶Â´” ÉÍÂèbäî¬*v\÷nßb?»àÞ[À|ìƒÇvœa×{ð1¹Z ðßlaŠŸšðÍÜç îr »¶×ᬄ4{«E•A÷»ìqœ‹æ·£Ù<'ñчi²8•­bu±q—°ùŒ,i>‚B‰gnuTGýp~áÀ…,ÿÕd¶ƒ\?¦WŽ…ÄÚQ4? ¯× ›»|Ôtão<ÚM(›Æ.mÉ`¸×ËØùw±“çíxuº¸’ÛاMɬ£q2Îõ%”Ö¶¡ße\~î>®ìÛf/²vsïƒ1Wa5å1h÷TŒt¸{gµ`a˜/ÚÕ•3S2g² qg"»à˜ý{Nf‹…á!LÇx±¬F®i2¢™œSÌU“ ¼¿ø8×qÙ«iŒoö(ÔÑ'ÿžrŒ–‚°ue…/¿@u¡\ôû Fþ$ßmí`˼'(0~üKV3û–µqìwGs’³®!`çIúÓ% ÞU$Ëç·à…À °nçLsSa†%¡Ì¨3ëýS—ŒÿÞŽ îåÀG‡4Xè5_Nη Ó–ØQÇ)2ÍÞSӦ¡žì÷®&o›¸5ŒÆmÛqÕéò:O§Y´xÚkŒ´8ìœßàªqúemØäæN4ŸÄ™)dz™’R+²]=…+šÕ>q€Áž8×ÏNè(ÛxÍd¿ˆN`>‰bß^¸ÍõjG˜ÑˆYÓÉù5ðˆüÀEŸö`ûjo†-šE'ò§â‘ïuLC¤&Ñê)ßkv0À«BõÙS)ßÐ’'žÄÇBÅæ™°»AÈg QÐ!ªf³‘Èܱ¼öÔÄl<.iLÚ$záŒÓoîÕUqÌ0° Kîàûþ»ì‹7%¬}˜YŸu’3Ú~ ï¯Å‰»f83x¸luÚ*@WŠVaµZTŒ–—¿²ãSÏÀ"·/N˜]ÕÇÖBV,S$‰g!6Hžc0»Åè6³ek,<µx <—±/ß o¥Ÿbu¦I1YösØ‹G˜¢%ìúeéðëu.ˆ~ìfÂçÑk™óÈ mh[¶ŽNžgø¢ÛêrxéS„ÎfØÀEþ¥a ·æüL×§&þÔf¾>€¯Ÿ¬%ÏN+Âc-EòbíkˆJ+ÃLܯCoY;‘?úW@';Ž©'œ>ºé|ö]‰}«bN­Œ–3­ÍÄðOnçTÁÂ3zÔàf4hšJÑßùÕ ub3lôš`„ÖòR~‰s0åk8žÑÀ¶MÂgùWkèt‰pwa#’oŒ'õ‹ñ1oma¸•Œ—Š¡*2*ÊjíŽ)¤ÈO˜Þ7„Þ'{Ø”kè û•ylÃ=BÔü“žÛ<ÆvæErV©–Cé­4Ü?îHK´ØWšZp=\„{BãÁxF2~ŒXÈÚ¯ˆ×&ËÞ¡·sFÙÚL˜jó{æ)H]{W.´$êë¬`æa‰ =­ßÝ>bÔ¡y1»ôªÑûqÓ½ÂàÜ^5ú«F”ië~„-éPÒnÍì—HS¼qŒIŒ+ÆLE\”õëJï¢@n(Hí¿Ê¨´\aÿˆz¢ië_ö›)UºÍÏšl”¤¾O‡Qc‰Î.—¦égë¹8½¦'P¨fþbĵnÃƒŽ»¸Ïñ$=øÓ¼‡Ððƒó8R ×¥["ÙMW´k|– zp¨®ØE›j5Ï'þ=´°n2n[má‹J8”ÄŸÂÈ)P;"ަ×ç²óÎää;3%xz4ýDÅ8¥¯» }θþ˜N„›`ˆÜ#F©òÛèF·ºÅ3Ï„ÿB®Ç[ÜöÎ9·Š0ÍWù–I‘û›øÐІš‹ Ðó­8lŸ–мD&r3M·À7GͨÄËÇpOí(ðì^¹MK È(òª«¸î›Ã Üì9òÓzfÝ=”ñ–!tü"ÍVMÉ…Yg«X‹ qä®·½Y±‰oGŒ3#‘¹®T¯ø ,Û GY vla~ñÛJ½¾€Æ” pà•A‡}ȸ‚Ìp^~`Êgî¹D89RÀöºípxz¶ü©cs9+ùÏC¸Þ2LímÆê{˜˜óúhÞt¦ÿc—qæÊÿÄcÊ °?Da¡ ñÜ{-×Ý`®Œ=/ãR6IôýDÍWÈМË,XG©^äÎFž X5}¼F1]·­†Æ={˜ ñrpLõ€®-îõ÷`½vlÁتl4ËcÝœ¬Ö.€qþŽá‰CÔ{–VèEû«‘hvx ý¥ SÖl`äÄÓ™›N’£ö ¹úÌÜî‡[Z×ã6þUÄøâ:~Íd̺{PlI'ä¯aÖÅ­gÎäÿÄ·Eñë%.¾çÝ—²¤i:ü×ÏN7#È¿ÊÈWúàã¤0x#Š¥emL÷åèqò­H&ýdžú]Ãú˜G̪JtØð³îÚö¸î… ßÇ@ÝBßÿ®‡¸åoY÷f'r6ˆ¤ÕHs÷U0~{ #hȲKœƒÂliÄŸb,nE.ME‹­bø±<—š|¹Íè)«“Ð…,w…ÏÖý“!Jí&/§à¦+é#/W¸Þ Ï/ïÇ ©@m=hõ~Õ—²G×裴ÞašÃØ zÆ_?cáŽ%í»w€^¯K³3Ô¨Êvø³¢¥Õ¤ÉOÛÛÐ6-–üü)Fœ×æ33V­‚ãü‰ô×–Eô¼ð-’m=ƒ®Û0f¸Y±{äÑ #ÝñƘئKN¥Ió88uŸ-J¿Œåí‚x~y + EÇÌå(¯©ÍѸ‹—TóЪSšØëK1{ŽÇÃõSÃLùò¶ìëoR¤ ¨‰©ûp _–{“P§©Ü¹Q% Ü çí~ض[0è;S¶ékãÖˆZæÖŸpüyª_L¥=«“€‰ð†g¡Æó-õ„­ÞÚäÑP slÉ߯„gí_`ñ³¥`ðì>ô¼Oe/ßa5æ>DÔ»'ehéÓ{`¸ù Ù¢]³Mè³K4ΣÎn†ºÜqyÌkBqÏÓxàÊ!2ýµ:®½% -ì×ɼ»ù%«âÄ,MõVS•vvßG÷6G™E½Êø<÷;fø†ÄCw‘·Í’´'¦Cï=²EAš„±£ÕIJôEþBÚû{<^³­ÉM†seäO>Hö…–TéŒ+­Ý NÏ ^)©é0·b™wæ9~´Áªû½xåc4E+Ñ’²ý˜njFàL5¼#Œ#†®¼µ’¼1? F.![ÝÏBéê´;î\u !E÷cÁÎÁ†¥¹ïÏ0ÏK‹zLb‹#ìtê+«Dçܘ\Ǽߠ”M$ÚžÈ £Ò',;¦‚†‰mpwŠ6œù›k§dcÅO2¿p-\›¾‡Åµ’v Xt̾gÿ.ðå“¥/BÞä¹àúÈG°{«%Ê…ÎÂòˆµ{»ßпÓza[º Mé*ŨCQlf%ûtùÀ¼ÌþaÍN0_yy#t±wF ðžVâ˜Î³¡‹EòØFãùá}$öðR_kaXî¶}ÊΡ WÀâÔE䣋W”Spê¼+X}_“t•&`§Å^hÎ7 gx¦ ¿(Ï3äî®T_#†áÏ83‘ÞsèË}'iŠf5¬ÛìŠÚ$“G㫪ô±þ R•³4>”ÁÝ}¤Ÿ~gÚÇZÙ c™wߘ éc¨Vœ^/.Ñg‡è½®×”¥6S„A¼éÅÈÃp"#– ¶°ë~Ý©n¸³j3á/¥“ØöÖÂNY¥F´v€“A‚ QcY_?*i*Ò×W"ÈA×@Òlq™¾{ÒÂ<üµ™:—ü‚‰.úúeg– dʹqˆ»·Ú„¬»“†kC*Ðíb*É~{¼,„iûàòˆîDsF† Ÿ¼)9ÿËö Í$º_1‡Ê¨÷&Miî¯WØ™po/g ö Gs©§C Ô¯‚·k°‘»ÀÞM•HmÄ«›ÒÑñSÝ}ì·¢ÿ·eÇf2rÙ“n¤€%d‰9)ªJ®xÉÍZ@5z:a[é1"sñ½Ü2ž‚ÑØ®ôíE'b0UÎ^bIýÖ£ÿW=ÚkIÝuµñ¡º6™]÷€ªíYŠÃM‹ˆÄò‰¥å Xõx+É,I›¼® üUt©I^?þÚP w¼¸p³ö:31;ƒé2ˆ¤÷oÆ¡ß|c˜—˜Iɹ÷'39`w·±[ƒêIZç\*ÎØQYžÔ©ó¬¯áÛm0qkMs¤³sM™¶…FĺՔŽ- €`“sx£6Ó½S™}Çc¡®k#LÄ[pÜ+æá¬„hF'A’^<|Ë.°f «a‰Â­X×õ†íï{Êõüw î˜!ßÈ:в$¹b<ô¢·¬cR+øèku!lzT×âÃâË[‚Žšá$©dS*Bq­ØæF˜'œ–¿Á†ÜñdÃÅTY©K9_—Ç LBûJª„ŒàŠ_ªö«“Cã¸2o SïÀY#×aî?'4ñfÇ»ÔÙ%Þ›1;ì“ò8ûjaÊAUª8F§,Ä ~Ãpßs‚ˆñ…i¬ò:`çYëç‹Èåå`³6‰©½$Meü4a£»‘Ý'FË=ð†t2Þ½ïWDÀâ¹þ´hF³Ûò6ÊóçpV¬øw¸g!Nßüž>CŸ“þ¸Æ„L™–r·‡‚öy™+J ¬m˜_/çÑÁ¾]xÛêF¹o­K±}‡Yœ;Š÷*ŒèEñ-J E‡çá^ë0ú†û`ÖãPÔ~<“z¹pL!¼ ‹ØùÊqÌáÏÝ ¹á;EÙŸH¾È«Û=L™]Ûçw6lZv Ÿýá#.ü4på Œ <Êíîpë[ìë»ðÅ8“ýÕ|…ñSñe—é CíÚÃØTÝßþm !›ÞàÞß±lú8è¹óÓòþsì–“Ó0àÑMÀƒ|0?ªn”ÇßM1rÚEŸ¼ éB0å{&®ePáŽ' MàEßw«ÙËéÁÌÀ1^@5c;þ=„æûùÁKŒU,é²ã ¤ *†V3ù¯]Ý®]êUŠg£WΖ.HgôN,¾S=ŒÜB2§ë ØáTX”;‡²×ÀR˜—æ';¢Þwrª,äweѺ§ PSBÛô“Ôë˜ £|æ3¨óþø¢‰MðÙÒ!ô8uv,½‡-«éÞà`ܤKœýnÀ6þ…L\z ¤µV ç7³Î¥íŠàPÈ`{4™ZÖ¸VÛ¥'yüæðï°ÿë(ë|‘ 8ÀÛ…“kîq+²`úZ2›<)[A}Ô«™áåز@Œäg‡Ì-\rÞ 3ž þôÆîš|«-ÂÓs'¹õK¶iÏYü>º ø{Áêt>¶ÅE£¾é=Ü,MJœŸÂ°n Õ·zÆO&´}¬Vÿ¥§†t€§·›–*‚Ùw,ØÅZcêL±þÖÄ¥ŒNƒoñèÒËC?R¤¿§UÖ¥tDC£¨(üzFNÉLâÄ6 òcëm8<›U«qÿmòØÁ‹Ú†h¿Å¨*W‹V,#ògñEh!7±ë'#b,GÏŸ›MÌê½qׯS¨N álHaî€Ï¹kŒŠZ2ˆ™Bvfsñ³¯qÜŠ*›Rüµ úÕÓœqK¯I kcï&ýHLwÙD®ÏqE­9zmb%(Ê“¹FÒ0é ÇÇëg}?=ÖÀ‚ÚšCÐy³ vÅóÑ‚«_aT8‚*ÉÎ'’AoÙ–ê&î¢ë[¹®ê©hÿ&ºnÉC(rQãŒ`V.ÃlÙ6¸/aÕÖ%Deh{IȆ¦!\<›|‹XÊÞz¡En¿¾‹}Ûg±3nnËF+ús¤Kc‰îXÈ]$#Éq~“I ½Ðé'°vý®të½ÅìðüpXï2´²GœI÷Æ|¸q½šZúpõÜ(¼e²ÏžÜÇ®J[A5´#¹k:»™ï·Ë!øåÍþÁû‘_é–täu*Q¸9Çþî_x‡f‘ƒ¦ôËÛ4]U ~Ì êKYÕ„Ù,Lͻḵ}öÇš:>†F» L mÁ%dCÅ2ƽu#›aŸðPÉï—¹¯wiÒÈ9Ô ÿ¾ùÄ–ÌiDõ:ÜÒ‚ãß$qm‰/±´T€ÖÑcÐy"ËÎËÓJÍxÔûË´Å“=z‹¨Og&*¶¬C1KA¢ÿà%YTZ ¦u¸Šõ ouÒe›”°ftUÒèCµo«É³ßºDÚEZ½>D^>/†a¯Évc/AðÑ1âäMÇ3ärÞNÔÚÑ n7ŒéΖÆ×÷0½!•ˆMº'É’>sz40 ~ìd#œ!­ã¦e±¬š#‘{92»£cÕ©$èw:8 õã—Á©äsÖÅÿÆÞ…ð*2¯ýÎåxl5@EË/ЬÒ_{Ó[‡Ö›Ý£alXÝMÜT1y)øGú(+jŒµ{9Do• Y°} >z$KÝÞ­„o?mèÐë7öè:†nGƒ’dÓÐ:6€§ƒ)¿^ðÃ-\“uÑRÄ–q3Ÿ@c'g2é„•ã@5G‚3×OšÈgò±¼Ù¶X6ž„¦—3QI›êéCk߇`åþ•‰Pncqõ]øÚó’{$ÈÛ0E•qPÞ@7?/Ä¡¡\¥P+žâ÷-*û;_Š´£ªdÛèB¯¹8’äTæ’¥ó•šô‹‚ ËÐt“5nµ&ŸµâY7ìòjBáLÚ÷ßÿ”e´*”±çø'tª8m…ÓéÉ“ˆß÷¶`Ý]3¶Ö”,¹iØá}±bZ îV'øø,‡¯Žf$YÕ‰*ŒÙ¯:ä&ŠÓ´wo`eá ;ÿ×\rryq‡Ÿx\˜Jí?ðõ×xÉ­¸¯‹‡žÇ¢Ì¢¾:Ëï÷8C3—ƒøÊ瘳fÀWKš/q hÅeÕ6¤³U¨~g2Ûž”‹‚³s°‹/”Û¬ú•ëàmH¿FQa’ØuÿÞøÂy8fO¥ÏãU+º»¨bnŸå ÌÎc+_K±>þ_Ázf7»[. ë/\€2~ÚqcxŠ q´"œ±ç(ú/ÞǨΩÄuž4«h" s]šàªOZï/ÁÒ±F|ø¹srO`ib’ö âá»ÈäýQã¹·Q£`>ízÂ1—Ð!¦•[}à‘» ýpÕ‚v€™´ –Q¶£¡D²W™£¼Á_˜Û¹‘Ò§räÛï>LiBÍL0>«mØÁøŸÌÝ‹Ÿ°îÉp–V©÷!­jH}ÚJ—gíÕáþ¸qÎöœ‡îýíÐڪȔ¾—`çÇ»Õ}³½‡V¡¡ÞvŒé Ð%™ÕRä`A:4=Ö ›K]á}ÃfÒ‘*‡QIát÷ðj:Äò0J§ãÓFXyå:˜3±˜SBtr& á¬=•ÇG¤…Êh£{+ì ŸAš.P¯YTBÂ8èÞb ¬¹Ì$½5²T´&_Åjάšç¥Û´áôç'è¥þ ¬ÏxrWÇP|4uîè`N7€´„'ëªX޵<ŠÔ¸ªy÷ûÒš%wê4t‰ÿ´üþL‹ÌñJí«3Èásø>óZØ39^ Èi|7WŸ5)f/ •ƒgº ¾ iÉû9œ»|yäÇwòàBš‹Ù`È¢àw²¢‡·œÅýfH„-†!I~êk˜ Õ/2!Øý$ñ¼òÇOÑ2GAZV·„Ú·Ïc¾ý;ÂÒŸ’Ui㛄¾*„èµkÉ©Š'\Ó¼ä÷R rë–0-æš1„Þƒíîwk`‚Ÿ­a¿(¤áÁÝ-¬…¸#ð¿W¦øûêÀYÔý,ަs±bá\<•6 þî?¬mèôÃ[1ÜNŒ™6âóïÚ“¯qÖÌ“´óTûzÚ^êưÿƳ‚éŸwDµƒJ#+z÷E𠣂³+ª±a Šu/߃ i‘L„¹Y±Uÿë뼑?„"¦§fÀ˜_¿}ñP)Š-1ÂÍßÃ!@ÆçUK’-±–0´Óg«\…åÒgpÞÜ .6£xFí}˜÷¾Å e¤Hë¯gÔ¼FqÑiÒ6ýè§"÷ñÈ&v±ÉbêÖw–íñA—4wªÞ ä€îj8»i' ÌUÞ!”1!•ñX-‡ßX:‰³ìÌÎÞû¯Qùc&ñ_z|jSÉ{ÃbQ¼ŠX<ÚL–·0tŠÁê»_‹.‰ Å)_qßâ°¾Æyû&àï†oð÷¦5éó>@—‰ügBä/á}/!Êô×±ãRžPÁØêºÁ¨e܈Uƒñf˜ØeOÆ‹E‰åVQ˜sr:éîÈ€¿/ˆCóèµO´Já5‡þfýó}¿—°©³žqo¿JÊFaCc;¼2PG‰*~¢ÆMÄæ¤©dÖæ\qx3%Bô~ÿE"ž²›5¨šF7䯂ãRçÿÓ3âvP:jm›HcÊ[ýñÕ†ê¼Í³§TÈy¶ïÈCšÍþ»¨„TÎàw…ë0²±û“PY#Ê>éb©N!Ë é›(÷¬R„€%ã̳çH™á NÚ¾Pºûú~œ{¡–<3?H÷«Ò¶E7ÈïÒµôµÁ3PÍ_N^Ͱ$zsDÿöR[•VÚ²†¨Œ(“ºhÈét•ó ‰ï”hx¶ òJ÷Âa)ZyDrÆŽ3žâìªlØáåL.7'ã¥SùP•áFvÔ±‡ÚCKþfPžý/zÏ£nO¯ âr_»«;·“3¤¢AÄÏtQ8 Í͈Ç6ì[:«5p|Œ÷@]rókJ¿MaÍÖ-QY;ö“A#NÚ$w·Ð÷5< >s |Ø®Éjµ.ÇWVÙ´ÓÊ4<Ïj]âp§¼uÀ=rTÂÛÞ?š…ó^‚ ©‚“\Üxæ Òçþ‰ŒáÆŸðË^þõ©á#­·0«„‡œUI¢kD71>ýæÜ[§éOGöƒ—5*<Ù‚)c—Q`¦ÖÖ1Šv1øÉ7ƒ3“`§¡ˆ‰öàñ›0¼§§v\ÎŽÎYA¿®ú ÕÄ2I—ýzX-xg£õç`ê¬xø»ü&û¨þ5ˆ~ú¾ö&ZIæà1›,ô6%ï4ðç§ÃÄæª#µ÷ÍG³2A(ÿÊKÝvŸ 9GBÈ„Ò[ˆÙçJú=–Ãõ>{º0l;òF—°'V“·°?m%ކ?f¯F•1‹—¬&OJÐÎQe\Ý͇™ÔZ{vÝY>zЭ• ²M‚ãu&˜pz«÷›ãê^5b2ôÆþŠÏV$ü9°øƒ,ÝQY™à™8#)¸½E\xÚ¹X_râ5Ui×ó{LçW8ž4‹t´›Âëq&G¢9²¾‹M‘|:÷ÿ`Fûuηœ)T¤¶®è„{ç`bÿ{ÆIN.[<€m’jäø´uxü+}y¹g¶£ñ'zP>ŠâëW1½Û ŠÄ_â†P5æyÆtt§ïû8ôÚ7=¦mì,âÿU½f[‡¸·>!êÛæÂú¼{ç>Ó«dJÄ’à¤ùFóÇXòæ¯ëg„ž,„(ß T§›DÊ!5¹[KÌ)ó±ÉþÉ;>†îZAf}·£ÆÖÀjØ7ºxÃiMA0\l† –ã¿ù½$è‚·óHË"¢ç¬É™Yl}§lZ¥BoÓΠrÕ‰øòà:hŸ_:gOâìð)ŒáNyÜ£š…å ë‰}F'§SX0ÿ½?ÜJ–’¸'\d8îìáæH(úc ÛOeý.€A¼):åD‰@Qž ‰ßËVFQ‰9uÜg ÈêòoÁ\’øb!£ÛZÊ= [>Ï€/Õªx,W‚Öû-¦«Ÿ…ÚÉɽ  e SÈ27M%ã%Mä[ì |²Š×aAl?$~¹¯ÄÅh_ã7´õ6eéÌã¬û½ƒäœ¨xÿ x;­”b9¨B–4½×û °ê©%Ýa+H¿Ƽ%„]wš¨*M%9ǃà—ÎÜXËuz²„õ¡ÁÄ/ÛÎï˦»Çk~{Ûú^lf_Š`‡O½g‹ð‘O»ßÕÕ¿Ó¤Çôb°r±]çR…jüäŽ:1pN•K6¤¸z±0†A^Lc´ • K$Ó˜ ï. ‹e%séIcÚÎKUçH ÚIuâne>‰(ê)üíÎ!n%&øl&o9ç1tÝ%˜ò­'²ÁýÚ úcÚ4rÿ=eÿnѶóiÝcp¿K”zËšÑ@¿¬~þ6\Ÿ¥FÎâO_9·d ¸\;….{8ä‘P!‡Ó`*z§íjÆ„|Yz_Å”¸ï!.&%xbAd€ïßfA¤ÄØZwŽq°ØðŸ›cä‘Îì¶§E»â!÷Òü–1ŸöH}a£«’åÏ‘7ôíº`¦ÖN–X¯½w,Ó`æ=]8\ý·(âo©©4ØŒx•Å(W؇œúlEýšÔáIY:êmdxl±Î‹ÉÑJx9›þý±ÀÀ mb2«‹^l°ñø¥™C4>¶á‹12ÿ7sV‘]O´é'#1­š #?b¨x¯M°ŒÃ“²"tƒ+?ÍýÈGM£¨ŸÈ¨šXËYuð>t깚±™tîU&î®4o™'l-ÿ‡3VþÅMßÁ°UI—§yNthßuH>*‹aƒ;ÁÛÑͲ‚½` æ/<Í¢uöQ¸ûíYuZE~}gö/dhó½åø£äÔP#™Ÿ¬0$ó[Ýg“|îùŒ)±–¡?'¹^Ò^ò+Åš¤½Ì„Ãûÿp¥Í#cê(EôÌFÏ÷ïðEI 8mmÇôQð­y3^w>‰©Ö¿¹’“kQ«an_ÊÄAŸwøöf½)¸øù³-w2Ñæô8Šžè†C#rìÓCŒ[E ¼ÜÐÀÎÞ;Nßf¸Ÿ§ãs@hþHíeÉ“}fjÕ’‘ÿÀKx_¸ˆ¼\†Óˆwæ\{úÉ‹ìÆùš’4ö¶(iñÅ;ÞÔé=•¤å»AtÇ4È壯~=ag=øšuç!v•óôkú ªaAS “vÅ ÎôÎ%ÞK„èÇ…¢ o}‰Ü_+B&¯c\æ§Àÿˆxp,ÿïÜŒŒì-«¬Èˆ"ã¹Ï[hm£Ñ.%#Éž™%dK”D%RñÜç­m´  M-šâïóÿ_¿ïÿ¹®ûzîçïû}îó>ç¼^ÏõºN¸ò^Úu|™ø›HÆð϶/P¶Kõã’¦ƒÃó‹±wØ:ÖâKgðÎ¥éP’y™D"E¦ÜÏoN+÷¤ Ò]¸ã¥ Æã*g²Ä¶ÂmÉÔIk`ÚFèNn.™‡êÀn÷:¼"›ó¾ ¸ ‡ìû+¯C!çÅ1´‹ÁôÖ—œâ˜êöµP×ôÍšnBA½4^$£¸ýýཤHZª¥høËôËʃKÝ•PiwÔX“¥½Ø¹L‡ˆ²¤®;}Ѻù«ôçô¥ñŒìÎïG nTTÁW´?Á𝭍—þöL}Ɖ¨Ÿô·;S”Ë{lòÄ#%»æ ÙÐÅôŽ›àÕñQX™ÂD+úQŽG;ŒýÄH4Ë‘RÊlœ{Û?t4¯4àžÁ9Ôã©ñ$v‚9댨ƙ›Œu|“tBœ ÍD«M¾tãmòÄf9ÕÎ+FS'MƧè1¦¸íd/¿cfçðŒýRš"¯”îp펆¶ºSðgÛ{´ÜÓÞ&^É‘ª¤Š)b¤lãÌ–k¶›V”@>ò“]¹É—Õ÷Ý ñê'!Qå¾:ŠÛôݱtV­X,LícZÁ\°Ø™¦xÆõ»íq\œ5ŠYŸ›Dê!X®ÕúÍ þä9„G]úä˜Ôããµ' 6ë î½ÆÕZ®Až¼ÙNΙÄãºôk JµÛyqLÝRÕÄaü}çä:ÈeWeò’b5.ËÈ Ù À‡zªUÇOßè!Z¹ÃO×|ÜŸ2½h¬ó]åè¹±“°OCŒõ°cÑ|ýbp:Á¾…Hœóè3ìñÜÈ^Ýýn+Or㔽X>­ã?ÁO÷ðqs<Ì´‡ _Ö`Ê}y²ýÚdM8‰Íî¤g²6\ÂsRº‘—áq¨â~ùÉÜÁ4ÝùÇ}'ÊKê¿çë5bð¨e*Ëë—Ãn¬Èg&ºmNü ƒâØuÔeÞ_T¶«V3ÛcU©î¥xj× Ì{që(BÝ[M²ÂÆ‚}²ü!D|¦œ{÷6ö¾Å¤u_$ñ6œÁf `ºÕhÿT“•F^'t䬥üÚäÕî6àpJØDÁž‰Ü¥‹ï‚ÄÁhø3¡Á¦Úÿd»‹Ð˜[Ä~йÄ,Z\ \O Z+°nŸ*‚R“¼¼r·a‹±*ŠT>Ч±\)x•Gpåå^tó $®u£øvKš—­êå ‘š­Ä&u>vhcºgÂÕ)˜|ÉVªÏ t®áÍZêÙ8Pƒ|Û‰Aë(gÂÿ/>²å^;¸Œ¬aÈw?î=Û^º^€ùOqê93bƒ,š¡!óÊ䊖ÉâÜÀZ÷gŽnžô¤)9¨nFo?A~ÛMþÅ©“:<à;m@ž k²^«ãpCžÿœÃþI*欙Y Š<tªT;6‡CîÝÏ8ëÖ5¨^.BþnóA»í­Èo…7¯ÎaÅ?_ƒŠÚ2ìÙ°ŒxX…µyç¨Àµ:h”ÊÆ …;(å1dÿÃJÒ;Þø¹(ú¹ ··³{Sèï²LÏ —ð~—^öDÈc¸t8R/k‘¬¢lo³Tâ ·ß +bJÙÆû0Â3‡JaêˆÁ½€ZÜóâ·‚èвg`s/µÛ/Bw:•AN¤Z8Äj,ŠÇ‡æäDúô˜€kêwæèÅËà4Q ~B3¼×¶4CGåRr#öÜ«&¿íyéÃæ>ÖÓ“‡êžZÁ§= mf€êsÌÛ>avµ\~ÇlkAf²1éJ¤Þa9`L^¬a>ë_cù5-¡âß=Ùr†³JÈš-{ŸÕEìáAˆ|p—kr5Ðõœ2¡Æ[¼@"¸5÷\Áp\_Sè~ÎήòÄçŸúXC­ÖÍw6T[¹QRªEKk4iý…Sì•QÄÜù8”¿·'zΖ“âÇÌú |ô«ÚlfO…9nŒYDsŠôIÓº0t½P¯äK@ódå$öq&Õ\%æ¹æ^žzZqÕ¶R¼TÍåÈÊgàV‘¼““¦®×/ை x¬mœ:ò~ëc^ŠnÓÈxÅ †{ÿ\Ø×† ò…ÌãÖƒlsJ]Û 7ìuHr)/ÆY鑸§¸zËSÐ÷4&ÅÉû8J-ÓˆMØmôºx ΋ô"Ï"kvÝUúŸVùÝ0Ð5Š‚pF¹]×æÃ«˜º™=ö²¶HŽf’Êä¨mjIÏϱ¢â7eÈÿw°#<–„ç)S¿­àñ‰Ms±C%`Â%àã‚õ=åf/:@Ÿü”£Eá6^ýx:’ÁÃ+ì‰îÆ?ðòŠ6VGž«*ôeäiXü™—ì_ú‡{Îf-Élêašw4â¶B=òu6ƒÉÑüà£!Fö=L§“÷AÜS?“†G¼Ê´íë$ÏèžBöt™Ñ«ê˜ËNÜŠ$˜ÿb¯‘[º{@8¿€½5ó5.›r­ :1÷¹&ΰ8 òþ ™Sæ£î©XöfN)÷?½²jŽ(–hí U:˜LÏ· Ù÷ª?d°)IJÍOqõid¶ÏYÔX6½j4ˆ [#÷Óã:AX“þ‹¹±Ý’ÜšH¦ÂŸ¹êÑ çÕ?¼dï¶»Jpîí4+€ù ÉpYv)>IsÁíùE07å>{ «‹w–™6ø&µ°hCÏS¬4?…o™Y¬”G%û½ÞüQë=™ÎÁðïëgÓ$¡£å$|ƒ¿±B&ý´ÙèjGfvhÐ×uP%ø´yÔwé$€¹{ÆØ½O³ª/ƒà€>, îÅÌs?!ØãX†.$ç›â //píM¿ëÚtÇ[Q’ZðÚöägcÆ}ý lY=–&'Â¥³¥Œýë¸éÎ ÁZtHþ9e ªÞ4èo®MqÆûƒÀž€ê3øÿQ¡1¯¯A©?‚•a&çÆ–88×ø û¯Ù²³ÂtÄoB»C<¤á›<­IÍ@ï~òvo™Ú?ˆ³r aôÈ'ö^‹#½vtDK«‹˜>É­xÛy57'q-ïWÁÎÕáô¦‡ŠÜ“‡úUݨý×jÎ~Çž0†$l]Me3ÑQe)Ýë-Fž_V¦{™§¯eppÁ z#@šÏ|í£³D޶ùZUÚÁv‡SY¹ëLSžØ7¶ÀÑî`¹b3NÔ¡æçtè¿(D[«²™K¬„œÃ“bÕ8èjO <»=öv9ãó›§èžœ›h#É\œF–5”Áû…EX*IM>}ÃGc¾ìQÇbH²œAƒNÈ’-ÚšBEa‘%É/æ°.ÛÔéB]fo 2§ãXÁ‡IÌ„‘ Õ¸7•è¼cè™°Òu&|+uéÝ€Zvn“0ãPœˆu] ¬Á’\îüÄ5dý×E4;UƒÜ< HVË“¸ h_ …/ÐÛØbnÝÁÎ,™öˆ­U~ÆZ¾<‡ußóÏVôòÐLòÜD€¾*ò#[¸é°y(vÌc™üPæ±÷mÙH/ÓÇ®”&Ø—¼lX˜cºátÊSÂã·ˆÙ¹"·/"~A¤²W‡ê¯· ~[çáê¾tŒoý§‹˜?\ ÙS‚žeθËê#t–àp÷n´”SB{û…älðBº·Œ?‹…Ý`Ò·l?;­ŸYî2¾|gn÷Î!ã…YôOP+ðꣿì"þÝeK$Ô€˜½ÑÎ'U§™XÿÃÔêækå¡åN3ò:'X¡Y—˜HÛsP¤ÑOÄhî fÊ6tzrŸó¹ß’®0éû"mgô£ñvöìû9hš: ¯=•!ÅÇ—âcç{ÌЃœé'Pl9ÌŒËQ~ì5{Ó[“n=ïA^Í¢òS&`îœtVg%‹òkØÆÈp¶ù8gEL.žs{ʬãð¿zù­üƒt[‰£ÿØ™ðp-Èï¯v$Pu-ž{Ú|Qwìèfˆ±ËÀÖxúeþH¥*V؉̟ÏâÎu ÔæónÚþʘöOÄÒGÃà¡K7–¾cé{{~: ä©çß$ü• ]Ó—Aæl Æç¯CóÞ+0}ñ ’´`„q´‹¡iÃsHQ,\üZÅ]óíè©."ÿv—AMŒ%Ó}= tŽNÀ)so\Óc‹[‰~ãl8!«E?Ž@¾’„¯" #×Ö2?WðÓ»‡6Aô¹poT¦<ßK˜YI¸Ðì(z}˜J- -It] £ðv!7Êç½L£ocVÁË+é¼8\E¡Ä1€4:†ÀÝC¯=Ýåxi¾#}®ÂZªµçâä¼²b鳵רiÅiy™ ½Fhg0*J'‰Î½ç çâmÎK6º­T J´ÚÃþ¢¶Ž2Ôå܇ÙB&¸×xÿä±›$LiŒ¨4Zò„“\ÈÅ÷rãäs¢šnÒ¢óO’d®*ñlKMçо‡ÅŒQ† µ@5i4¸‡–vRtSS'ŠJêÑm ¸¤Â€ì.ù÷@ï‹©¤86CÎkÑ™#ÖpñÌ ú:ÖxmóÀæ8V–Ã}K´LŽÌOƒõ£Ñ4 aè.ÕxÜßâ@g¼</Gn°³¬ÞÁÌåC¨n=⽋ðÅ››ìEub³|5Û”ü5ºöÃØ·X˜æ’f÷0Ö»•1ðÚß\‡çë†1àn÷®œ7Ö«h³ÞåëÉýn;rhÙa|tî7îq¢<«»<|ûësü¢ î²¢¼ßw¢ÕòR²IâÃkƒ‚ŸJ4ÇÆ˜‰Ÿxî9§Ñbxbª—@ô€:g·b*m!{L›PÎ4ŽŽLA‘‡bRF%~<…ö—è©~˜=p8â¶IRŽº }´až]b;wÂGë8æÙ·UlR|¼$ T*s&uä[FÖÿÃ¸â™ØÂÈ~±pêÀ%Р·Añw6êÌŽ¢Ä”¼ÛÉGŸèìĆ%ÊÀÛ¼Z^Æ=£ÁXÉc@Bÿˆ•çG›“Ujî¸ø!ÖÛ”rízmèæêkðV½™Yº…-ã!:zTRABìÚ虚´{æg–/·†óob Ÿ2Ôèeà˜à?>] Wš•ÈëüƒŒÔí6vûIH JbùB¹zÑâø*|W¸ÜðÀ1Å)8õï[4ïYJÓœÉðí¸±n>w—²b­w‘{r¾kP¢føbÔ¥%à›¿ûö@ê>Yzúž5|qob"·ÚÂa;˜Î¬EÙSñ |u ùë Q÷ÛzVÃf}}ÉŽ¸yþÁ4(é<ü§‰–ºDÁQ“Hl’†s)w0)ȃJ,úÖîåçç ÉY º¿!ÌÔ§-'âüáxïêNè>BwxÎÅ]¡Ù®fäâÁ1<^Š&£9Øüž—®’áøiÞÇò9ßm‹Ô‘©wg¢M¬1j²ðØø-óïÆtòÝ.Œ>–$O›m0wÖG<õzˆ/ÙBì”0²dä³kÆ*”¡­¢${T†zÚ91ºùv)¨–*ÑŇ„Hù—Lœ,Eå)»iÛ”%p ¬ŠVÊ ÃÓSHƒËSÛÔ õH"¤Áϧ„)9Ù‹ä©ï÷A¨‘ZÌdÉL`ÏïÐqȈ =­Ãk­¸¸³¤–—À y&žÔ´ iþ}{)›rz;Ðj®œþÃGwk·2;¼„IþÁƒDs›“>0ù 9#¦,L+6g]»Z˜=fºš<Žwµv±ƒo‡`ñæ8ÚÄìg߸\€W{:™z;د)û‘$¼ÕðÕÔ·‰™‘~¡A¼mq8Ñœæ/7ÑtïF9t,ޏ“ãá]h7v_ß=ÏKŸAeÚ êi+‹a;üÐI)Ê3W’ê‘"Xç\ÜYBd6ÝÁ=°fÞ¼Ó¶bˆ€ÈáCfÏR;+)£öí]Ëé(Öó†vw1ßËdÙW>vxh§>X^JÅu‘¨ðú2œ6ù •3«Ð¼V]_GQ̼D¾?Åž¹)ÐoÙ_zæÑs}aÛÁ}l®èZbø¬‚¢ôã‘­dÑÏx²‡þº¸ŸHìõbåÑ]²Pã¨0µMW¥ 5ôI¯!«äé‘ùRD>˜…ýÃÏÙ°JgVýø*¨7s…Yý}Ê;Mìs3£^gáýF{bñ»nE)ÓÝ=‰œ‡¶Bž\Ó Î£…¬ØvcšÕVAÒOœéà‹£©×õ¸÷±/Ýdx råAÈ.BE@àu+jr- ÎÃö„HˆûÁ ma‚†øI²ˆ3u~KRíWRÿ qé=ƒœp,Á§G¦ÚM^‹r-tëô¦§ï’S£ßå²é ˆ72&qß[rí£‘oKÄš¸—*k@áÙÎŽ”ðâ=Ÿ,¹š%d$#$²=áö\;Œ»t Dý¦’­†Ä5s!£cÈv]YS¯éa®xîZgTÝÔã>Ç £>„ÔÜ2dz,6‘׋ -¢œuùŸü€]|Bì£Ër¤&»‰{ÙR—<¿Ç± Ï'_ÝÀ Esñdý6"^„H´Ð¶SÛ9ÃkÙ½rpvâ,›gBÄl@|+e}ÿ^%Ùž9¸Ÿõafù†â5ÃÅŒƒË&f¨rUÚ.@fÞJ£É.Á\qÝmdFÏ'H~Ί·dâë>¸¼‡¸®ÞÌú¹j’%2| á¬ûžJÓqûåP…û˜Ž31pú’ùš“ŽÓµ×y;ª²?pÈ\‹¾wy‰Ôˆ†¯Œ†%mrôb…]:–‹Æ0µÄ—„T@qPKŒ—,Ǹ–™”ïòQæªì3(]1¿ûÿv#šÄs[á,•ëî"7ú”©ö¿ƒôW¹-¸Í\ªö ±ú}¨äòs™‹¤N<’T ߢíÒTùÏ ´mèÁy±šô†{4J¼`zÍ–âîyNx¯Ô’}²‹Ÿ“2eâ4>±çwÃÓýRà%Ð ½Û™¦ªÌšÌIœµï0ú6í„C7oØò_Þ9WCPê—õ+¿Èþ^îDjM‚`‰Z>T­þ‚E›?ÏË8¸ý³§þ3g}w;?IêEÞ²/vpYMa!6cß>ÎÎ5VäG¾V7ǡÛ3@Í2‘q¾6ž8N£¬S­{~›(‹û’èµ}U8îuÝb”útª¯ÄCU>¸‘³RXW'ü—3ïòßBëçrXкM¶K“ ;,ÈÒËøú§ý÷ð-ÞRebU€×Œ-„þ%a‘è¦òh"ÒlAʹZTòêƼr>Ú|‡Í[‰-Š£Æ§‰#±ª )|œ>ÏÏÇæá´HЃD,ʦ{ßWÐmꕜª’ûÌ¡CæV Î!7ñûÑhÆVF‰\èÁçnñXVI e\qndߟAÕñô]H,}à›C§KŽAYTó·²9عÆüÿU¯S¬îEÊ^]DþDÓù™Çˆ¾L“ý@ ê#ד?pÛéåðÅT¼årm §ïé FÑÔeO€ØÍÆg`ý$ˆç©¤QqOpB¯²§3Ì. š¬-@¯7xb˜c?{T{†NߎÎënBtœª ·šÅéÜ+öǯfæþD)œÃOsf“³ž²ôk£ró6Øþþî®GàØN}òÌ:ý¨àD¯‰Ódz*ØO.™53eX£ï¥Q37¸û…y+?aEs4#z×—$¸Áóã¬Ð¼wØöYÇ—?bWîñ jë©GÍf¢šÔð‚&q~m§Ä4I¶ÿõhfNàrD“«Ž‰ °ˆÕæ%+ã#Ù]O¥ÙÅ]ŒÔÏD*Ô{Õ1ÛŒ#ëG é—´«èÝ „™žuÌ¢€*LµÓ#§õ?âí‡6„lúŒ· ¿"ßuoö ‡½¹Z¬÷°*™1ý»ý¨*Î3v$´\‡ÙÕ¯Lßv¬¤“çIßìWø»AŠ*6ZÒüìÌøÁm°Ó~Ún>J—ei0ó4Æñ¯×^ø<Û ‡Í#Eß°~ }ó6¾Ü‘'Ž‹Ù“û'šwã†5çà¾Å.Zóï.~¼ß 4Þ…´—má,L ¥[ÆLé¼;¼-»ÇWC¨ž=@ÒÈö®t0KØJî''‘€‚p¢áÏaZ/0ƒƒX¶¸šúMÔÑ\‹ëLÏ<[”89Æ-•ߥ̂ûŒ­JY*ó²BßvPç©tƒ7?Ž¥HßZí} =ø‡HÏÉL¼>¢ÏŒTó“µÿõÀIºF”V4Уó.Ða½ ]¨‚2íþ$oý.h’+ ÙŠaxa7CÜ~΂¾$âw¡ ùÛþ€‚¯Äû ߈Öþ¾;Ì¥eP}²‹é;Ëë.}fú”`mÔ;F9H•J{XÕíøÖr Mûpl7'ãïSa<Ù Ø Ú¼95Û…øòØbîî‹ÌÙq8ô MF4Èí krbÚAXªûM·G±2‡®ãôî¹ôhíY ŸCÿ•®ÅƒAaäâîí¸ïæ42üJŠÈù¦¿ŠU˜#=‚FG§Ó%šŒ} ÂÂæ¹ìO RsæÌ3Ì$Ò3é=·]½BÏkÑ;Sp³¹ 3&dQæ_fÿbT«‚¦°¹¤½¯Œäɵs÷®ÂàS©tÿ/W4ªžÜ'íóï‚Ç\+ q= WT?⽡½î¿„àæ:8:-6aÇ–Nt‹ºI÷Z·`‘¥8™gcÉåù÷;†rÀOv#â>ŽhðCöæô|Po{Ä\O•äž· Ã%ÀCí _˜Nµ¨¹Æ¤ïC¥çLifNSjö¬`Ý?°£’«¡|ÜŒ:ë¾öÓøúëÐP\#12„V‹aé"58*½†ìɸߺŠ÷ t~à#F;¾r77åÒéb“ñök{.ø!†¦Dk'*ë‘F\C¿qß ƒm vôÖ)9Òum3Ýê;;üPk+Ëþ ݉ì9‡r´Èßõ9’Ó*Yä·Q˜KNá2'ΰÚw‚¾ï#Ôš*Rs`ñ¢³äFüR²‚ñ$’¨ë‹üzý)j(g¦Yºàóc·@ì’$óÆ^W\f‚¶þ D®Zç5èNá .Ù´”\ßÉ…•UÆU í[8iÍÀž»›çº#-jMCóW<ð¸õ¾xæ†o@ùo;áÇ Ê·Tœ½Ub¯ &µž‰‹¥¨•zï{ ýÌSÌÙè?¸õýzÂô¶nÅeL'öÄe<ªüñ_=‹òŠÑDi**ÿûÎâÎØ“G伉t¹0ѽwç^êÀkrvàu! m¶HRñCb$Pè¹ÙŸÏvú/ÂÔ÷Îðå[2æ%¦A§5ÓmIÿû$¦âÙFï“"Ê‹´£ßšiL».QW0¥®ïvSåÍä^þL›—/¦e£`çAòjîtUþ÷1ªËVˆ6ÑoY™sy”jÇÎÍ,#k"WCvy5»2Y•æÜ‹K{;ÑB\^x§Œ«’!vü&©ö²¹ek`Ó7Gò—ö#÷«]òg5ñ8l„ŸXQ²Èg-ºD3õyƒðAnµÞöf’»ãŠÞT ¿úO+·Z[¼÷c7Ñ5N/Sú<ë»Éw%)åáàÞÚ—¬í]Ø~(‹M|@Wß ‚Èúĥ¾_ ì¢rÃðæÆZš¼r{ѽ¾W1wתÓâù³ÉWîR[8\•rþËaö4.œu²¡® WϦ$6*#ƒNÙ3•SŸâ:ŸÃX5c7“tÄ‘ÎùÄO”~U!_1nüp‹3·³Gõ wʬ|]&÷ß¡§YX4®A×°Ðeâ?îéî< ±ü.:oçYNÌÕʨÎå¥EGÉð=9ò$o²îµO€m¯0HÓ`JÆQÑcgQG_ ò¢~âÃ-*ôÕé…0ª+Lïš²?55è­-shù„(™ø¦£WþýyŒÆÿˆý.a*r|LùË@©ð [& ß _à<ï»à½ c,޳8ƒÐ5ÉgØuñ/ ã‚>c+HcŽú ßÉkLÔÙoù¢Z:Ká S÷ä¬{~ ÝÀ= C %É‹y§CAi3jkK£ùHwГÉì… ¢xÒ…‘J)QSÒ1íž\w?^?ße_²«dàùÉÚ_:{r“þ_­³¢O#Ë+±‹Y‹rÄÎQW½ ÂýpÅ !lþ#EuVî§<‚éØ)¬Ž¶›¾€åÜ{>}4&O%ÑïÍØ‚kqÁ•ŸH—µo”Èoë(Fpq-ɬ“ÝQðÙý#J8>„Y‰Û©‘Ï,:ëV3þwͦaòŸ†º&Qžö¯æ@ÐHòÙ œó¿8Å5¬mdvIïr³ÀÏ` ¢Ö§a˜Ø dŽðÐ –w™/g qçù=à1í9ãù •qx#„õïp]­$Gt­!µidçÞõÆì;ó-¾—=}"]Ô„hîªhŒwuçVÀõUlÞÕºB'Ðí‰2­ÿtuï€qª"UÝÁöM¼Ä®­ð^‰ 'oš€B–7ìÓ8Ì´¿ÌOÚölЬ; Z&M"k¿1·§Ì†ŸáE¸î„á‰þ˶>0‡k·€Ã› šäe©ï«™—ë—‘í{ïÃ#¹Ì”LyÆ2FwÕaʵÉ\ÞÑ‚§Û´TÌfÏ]Û uÁ?1%ø, oC Ë5PÆ9ˆ+ g“o£ñx°@•nôý¢Û!~Ó\fɺ,›M›%É~Q-ðêqBžÏ±WBAø±)=µ…–.v%S¦}Ƹp 侞I[¸Ì¹¾¸ïøR WBSK!v}§A²‚ä[ÀoöïJuÆzJ}7¢’ïUÉ’ŠZ˜‰ß¯‡âä96b­ ÚÔ$ïÊQøUµÚ@Šœ5»ÿõ…¶:áHŸ(jÑyëž7§Ú‡2—úg½Q:í‹5½5K‹o¥³l!-t ÏªúñûEA¢µ$$~»`Âæ4œk¥NïÇê0ïv‘‹êûIÛ”<¬9C´v}am”"éwz[õEÈÚvw-ÝŠKô?–¯õàÈF³§\‡õÅ|ÄüøÔI¬UŒüñWP¨y jò…¸Ý}Xþ«й~uD·P»g£í1Ò'÷ßàÛÖ`æs$]n€îΠMóJé³»ž¨Ÿ ÞUKIõ¡EtÔZû¤ˆºl3»÷,úOþTËfr§,™E¬\8ûvÞÂö½óÙð…â-‘‹[ï^ǘÛ-A÷7Ø“.gŒQ¿‹Ò=⌡Ê|¯ùœ_¸cv° ž¥³Øî¶p|ã‰Klʹv½Ûh9ùp_ÝÆâM•0æ¯j‘O™gçµhà«El®p ïõcŸ}}ɪ·Uºô¡ Nûwç„Τ³¾ï‚3îfD|¿c*•…Ëäê±ÿ£8©¼ñf¸ýÑô‚G˜ZY,Ãã&|´y“»vÊ+ÔÆç%Y…ëÌ‹ÛQ¤Vÿ"û·ú/Ü‘—'×£¤ØþYNgéz×à&^dw¢mt6gúØK°ËՂȲ2¶]{;4VÃ&#AæÂ‚(âfüŠùÑSÏþžéÇ.]önËœâ\>R‰bÓÈŽÖÌ2~ÃÉïþÅ4œ·GAÉ8è+ˆad{%Àvæy0Îx6fc±Â$'çe2ÖÁƇðìßt”ø¢BÆl±b1ÖÄ¢\†V]–¦B×’ÓŸ4I¯^1”½ª…Ÿƒ°lQ4zØv1&:ªÔûpZYeïýœ‘OÓ dÿJLY&Lìzå ¶f.­ï¦°2]„ä¿ÓG»äT¢ÓíD{}3aÞìŸ8»+ þ^ÏAH¦Ê7c8‹>™¢ªd"„¹ûÐÉ9qø/çAzCyû­œ|·óC£>âëæA&•c‡Àao,•:žNÆÆUɧ sH͹ÕÍ»®EbŽ£Ýæ(6`wäV`ø.4_¿ŽYüsÙ{Ð6‹ ¿î¹#$á³0Y$¨Ì¸¦~Òûa]g  (iáÆ¢qγ0$ÖŽc%pc`ªõ)ÃVh…àÞX \^ÎÜ2ÙG.­Ä©ŽNÜÔÀ‡ Ì7ƱmøŽ;Îóá»ÀÖþ/˜ñ2/%¹Ð†ÓaÆ}RõÈ…#Øi 3,Î2?φ›Â0ö¹…áÆÝÀÞòûpÕó)F/¬ßŠÆ¼G˜ÑÍ:,éÖ!CƒGØǹÛÏc—O÷r€6GT®æ6Ã÷/ÒÙ*n%øöÁÍçÁð G‡6¾âƒÊ'ß`œ½ˆÓÅnãI<Ì}ÿÛ MÖë£ü‹t\¹šÚç3Oƒ€mË8=…‚8ådHaŸ ÉÜ%Æ=W~÷~MàÌÐÜ ºæÂäôîgl—š3rÝÊžKS]4¶¤GòN˜Ð¶'‚ÔGYŠ|:¤Hÿüv$'^&àò«ð­u÷½˜‰¨™9ý“"J~Û/¢Q:†tßYkP$Žd‘ñ.6píyþý—M¹2Eå§ÑýÏEqÊåAtºbËþówùOÿL˜ʬo¯¨ò•³.&/!Cò#vF*ox4®¼q•­ï½2áAd÷Ÿä‡’pvš[Ùð#?žÁñ{œägá¦ñæó¢@î<‹s°ðï8_gJ“ pBj%÷ë C*<¤KŸ«—ãñqyøWÊ(ÏZ<èCGÔ Li;kS¸ýîlK|ç+Á“9\píý„—ÿˆ°ó l‰«±û~z¦÷K"´ÇæuTYÊ5Õ:ÁFy¤Ã|–åøÏoajrñs˜½ÑÓþ;†iÏáåç(æÞU Zÿ-Sç“{%™‡ÕX1 Mº}m.ÅýÁNt¶'á¦|ã`åÁSеóä:gæñ¾f¼°—½Èÿ '쨚ébêrçœZ ±»–’ÂÂAÜjîDÏ/塎#)|à'×^ÿƒ÷r,¢÷Cè¹éÓpË—5ÕÛOV¿GÂ4vc¡:5“’¡ç=DÉ:áXN•#kÖ—ͦ ÿ¨9å;¯„yšÚ¬ûüaŒ|ÜȹҺ…ä]ªÁ¶ÅB¬û1å2š4¦°O=é¬þ=à?ÇšÝ|!O3NµS‰t!/-W¦©ãV àt° §Iì dÁVGJvùïù¬%œ‰ wQˆ‚ 'uèÌÖ.öyfªyÞÀÖãÜê«E°õÖlZý}.›»dXM„›2ÛEeàëØYšîÀáš×•0ß²£9oÂ,9íÉ,ïÂpÅÇšó¼û(ú·÷3_Æ¿ã¢ÖkpݧWþÊÆ:ÅÇ ±ÝM'þ’1ÅéÄúó8¨~’¢7šQbw™]bHµRÚs =½ZP_&¾Ž¢û¿=D‹­:lwè$.°c;_톃»åɶ´è¶ÿ‹{Æ.cß¶,êZ—Dwœ_G“%6ã?IÀ^1raù1]§F O bo¦éšË´.¦°Ñç *Æ‘/3¦bÈ¡pyÖOVî£0Qˆ†^•rÔ7`æðÃÊ28ýý¨­WÞöfã/\ÃO!^m’~À¢¸ùäÎÑ=ŒÁÃctOÛc6p¸#ób3(]¬Äû©^ôå¥ÇèvwX%sá«Ü ¡'qóE;zá©8¡mãL·I´S&k ñª]5›Ä˜ÎEIÄH2ŽþÚMLhP§ÅfD°ñF¨¾Á/ó'p_#óÔfá4þâ̽ʦ¸ -©TOøH_’ k†Kè¥PîŸK>М™Dgk'Á¿ã´%gü»’ø9ë®8‰?¶Þ†¾3U°|˜{øÖ]iÅë»D˜}·™{ÌÙ—áÝõÍ8»;®Ÿâ'z‹"QÔ}6ÕvPô~ÜØ~ ëÍïÀGl•.ÄW×O0<¿5ðèñ>H}/Bí &9žïbHüÀôìЀS9‰è²=¿©ÝN/ƒ¨\fÂBÉå$Ýú" G™ÿôÖ·TkÙé–4¹/î}€#ö³@—[ÍW1éöcF÷c3´§ŽM‹bª/ô2ákŠ@bá×Ëa;#ûË<ä'­ª)t¯þlòÊr ˆü›E­ÅHZÒJÌkžMŠLãqvõ\螺 ¶m›Jçû£—l 㞣OdÒSÍÛVÄ$î:Žë× æÔD:®oD¿Ý#wrè½ømtʇaxÀCŒy 'Ý ÊÙjpJK—D9O¬„‚óŽ´Q¸m—¦r ‰c°*.ŸÌÃçC¸3¯9 Ä;ƒIúh‹FvZÓŠåwðmÊ&È5ó%GÔàt$¹”çEEM´1Ǧ–sdu P s4;àOD4ùo,ñö³øA8…êýÉ5{BIòq ¸c2 :Ó½ˆ¼ª¹™£LûâÛñÊcr¶N—žË~«O"Ͻƒ¤pêÅêàK†ŸîXŠFQSiÖþ­ÐþG€è\[HVÊ*À…/óhpu×(Ί\¯F=5u ËÆêÐl˜ÓLâ»o y•ã™Å(ᓳNð᱘Zäé9M mgÍÅ´L'Nت0¢©ë+?â±¹hµý f[ì§«f»‚‚úNzh†#^ûÁèY<ÂWcQÕ{*ùÛ?›#ëá?}õÞ©"$äí|â#¶Æšoµ5*˃öW3 é9ßJ¹¸ÝÏ€z†òÚn9yŽ8ì§YOQ%Ȧ1òô™ék`ù–¡Ø~~"t4ìUUÈš¤W°Øx•xÉîhU£U510.™n|Ä»E]r„ |…^*Ž MlŸ=áeª‰øñðAx 9‚·ñv’ÝÝ1ƒ||7…>^ˆŸ-Öqï¥ûR9ÎDçs#5‹±"ÝUëè ¥º>¤=ê‘WRœÜ+Èß@Kå÷ëHÄ”éÍvûݘԡEDðk7t“Á¯aómMâþ´˜ìúû–Í,£Ý÷Xåw¢ðµÚŒþIDÙrSÒqȈ-ÏÙM5ÎÝÀÁ "ôÁŒìG9œ^Žr¢Â3xáeL,ÚŸM—æÒWÙÌ‹Y9œgÚ†dÖf Kÿ®!ž£¸¬f ýsã9.ï'È0$ íÃ#y•8£Ÿ¦\ó 'fMÁó©^ÌúÂsH~e‚öD"›•JWï–cû¢³Àq¶+Ù³Þ‡YÜJ>´ïâõÜ'|Îd˜µ¤ ?À±-ߨ¡¯7ߤµT ö(-ˤ³5#áÌ c:ò@“œ’ý±‡>ûDJN›áÔ_[€4ÿg'sÎGŽ‹çÃÉz.{Æ|; ·â‚˜£D©;ƒí:ÿ¦7»£€f Çò'í¨÷á2 ÄÃñk)Ô»þ.¼TÑ"êO”È)Ò£Y ²9ÌL½»ìÕ³¨ñÝįf)íwúˆZÛ¡D~€u²»É:”©@¤ú5|½F 3ÿbêö-€mÞ ÿx&¿EŒîÓø it]‡õAñôPh#Ô¾1&5Þ3IÌï@6[©‡TdC6÷~¢ï*è΂XÏC²w¦“?­ÉÌœèKÜ#LDÂt±­ô(ˆ¯¢CÊÙÎsYPþ oö0Ÿü¥ ù²N9Ñe5WñÞ®«¸üôOæÈ±J–/Q˜ùjê#4¨‹ƒ$sì¨.=œ‡Î)èôÌ‘¶¹æÂõÎl0áZÞ¨Éã) xo#¹-KOÖz±‚D#Xš¼o‚¿¦ BÁS?ª÷è€mãÖg od‡Ïm &‚}Þ+Ò7ǬpDh }½6=½@d®8ѷߎ¬ìº• ~QÌÐîU°E±­ïî¥IŽáŸ—‹ÅÍGté1É0öϪùØ »TŒ‹$€Ãƒ:Ø4W‘Ýÿ/[¬ÿ‚U׺ÿÂT: t Çóðé×ӠХGîrßAyI4¿1cV»’å9¥ŒU¾{œGì$Úa^ý>¸²ë/ôTGã®LÚ{½ #—Þ$IM ±tM=gÊmzÁMŒÐæúàj­qžK¼#î²-;ôˆîWBþÚJ¾Å¦è}+-7°ƒâöŒÒ+Zžh~š#îíh¶­_”^eÞ3†GΨ-ÃOÖFœBßÞ Ddñ?¸±Þ¥ ¾94_+!rŽ÷‰ÒÖ%Ôx÷l W¡ ÊïÙðOËè¬úx<ìÅŸ|Ö%²C¨ŽùËŸyA<–éâ7uDQ2Ž,¿GsR–3›“iàܵZ™‹‰Ï­»$WОèl/'.Ó¢èª ÑÄy{8ŽÍþÉjò+´ìyð êï7¡8FVRžÖ¯ùPã4Ñ}¥äJÁmü>áL?I 76“$‹Ï'VÑ#”8¼N¶4çƒ"¹K Àä`'ØîšFt—žf®×¥@ÈòJ|çCÎXÌ&CÓ“ó¦’ÙÛ¢Qâû=6þû<ìK*ÿY.$Ö'€,œßËtûà?mµË¬@æB“$+ Í"‘¼Ì®¤ã*Çéðp&eÏ}±•µò ë¤ßp=nÐ=çŸãg‡5œ¡[樦aùݨOHœiU%š½ÇáDÌqâoÿ6ïéÓÉÉ‚¬à™—?q¨Çžýt~ ³Ê—Þß%P؆d`’ÜHYNÞŸ£¤Wëxg™_eÈ„™ãøB5z6¾œºÔæ½çr¹_Ð$âr±˜öLB+ïú’½c?áLUq™=„.ÇÔÁîßuhtßE º9.Bõ¹Œà¶°zo){2›õJ—FõΡ߷ú—GÍ¢£‹h†ä.â·%/1ØL’NQ“¤'"² ¨Ë~iOmqrCåO½ŒíƒlX•>)½1Ÿì¿ÿººq$e­,ýg› @G\BB.Óĺ3ì(Éd—›kSÕGËÈ“ç7‘ëwo¯¹Ç¤o[Iþ¦)ÃçǼܚ;ˆëÒµˆë5ZRzY+)EhcÍ…Í8zb.:Á;@>!zd«‰­$Öo¡®¯9øV7¦laª‹–{eeþ{ÈÖNÔ„,;ô†•CP½RŒ¬ æ'»–x€¹’qþ¸Ëžpðs lÞŸIšžÓ)ò8¶#VÕ ]Û“˜Òá%ľé ]¸Ëh-%|¿ôáÜÖ*ÒY;ý$`qŠ­Ɇø°bf_VÎO£ÅÌŽà/P²þ$ýšÖÈ ÊcD3Ö‚àhÆ{ö-œ®ãÊêÖ»­µa çE–+†ÂÓe霅R¤ô_,í=J^Αa®ûhĶ*ËF#WL^Ë/¡Ëvž÷q°ã§Ül&-·'kð7/ü=½‚œÍ®‚õÁ—áÉ"5ù^žä𒤜5¨²ñ8 Œ‰’ï?”±~úV²èiÙð‰4¢²ÞŒ¯ÍÞ`ÅÙ~âo<<ß ÉôçkɆÈpòæ^ŠV4GÝ®¢×þYSëJŒ# _ßN9æDü–Åpd¬ËibÚEèzçLÞ¹A[êô‰›¬06z'3Ç®ì#•sRaZ“½yp)ýåŒa â´Ø|‚D÷ÀÞÂnÖq¨“É>ú‚ŽnR'v\YZËø'E:¶RˆX{îB7T²Ñi(Ñ"/nØþ7Þñ'\¸ûû³ 3‡º‰;-D0âÂ,Úàˆï»áʇk4K°šQ~¿î1¦OVo¤ÕjAd§§)ý}ï޼I£Ž^Ÿp³ql'PÕRƒ‰—ë‰ù< ºúX„´>„õüÅÔõ0“¶¥Kòº(ÿj~v#›þvˆ˜$X‘ê.ð2w £Áj¤D¨©~ZNv?yCŒ}h”‹/nÓç„8\Ó(|—CS÷‚ÍÊT1ÊÂÛ?dWM' öÓÏ /è×&ÕÍÛ¡jßr:øíMȹõƒèá¨BgÌëex…V˧‘öå{éý¹út&m`‡]Æ SÛ‹J¯6€¼`ĵ©^W ¿¿ö`Ç!ò§SŠtÞg#wš‹3 !bœ}ãD¡¬êÎêJ Žÿºaj1u>áwª°åÓ]Æd¡)Ëù{ÿmóñ?‹ÉlÓ(SñÇxyñõŸhW˜BßÑeäìí_ìc~>zÿ¡©ÙÞYpþD€B \/íC~ï9 *5öðÈ£+*™Ä HZ’Ý«*ùØÎû*t4`} ÇÈ™tÞÏg%Qï­‰¸dÃ&"ý¨•ýX8ÂØDŸ%{ÃŒéw¿pzÓȧï[K7ÚüAæ@û,0~;§G/œ©ƒCAt=…!Ž'Ñ”È&5â¾ÔG?—Œ¯Ä —õÉú5í˜ÇéDzϪTê[ÈŸ±¦<”ç|j"• 9¸Ä´ÂNœÁ•Ù{Q…R rà‡=÷Khù(¡]+ÈåÚ@–•›Ceç=/Õ{ŒÆœ»³Zø‰—w<¾$s Ë§~Í$vµ¬#“ÇñùKˆåŠ[à²ì&ók] ,ixÎVÿ¾ÈÚ^Z J$.dÎè“ëo³Ùž|%pE¸Â¯‚Ò3èÎë×qYÕ>¢?”OçF22·º™uñÒ,Â&òÒ€3Eнÿ5²½põ2L?à'—Wly7Ói’J‚'–1©’8pxϸÁ·åxRM“¼d©v‚ =¯—Àr”¶l•ñ”î€ðÉŒ­©}qQŽ:§Ðê·%L@òs0ˆi ¹+Ôl:ï°/¦¾âz%<ƒ4ß0Zð=Ôü¤!õã°ÂóÉäø‡9n9gاÌE°ðx¬B=®&ëc݈ÿþ‹Ø'’?—‘ÖQ VÂ?nÈß@k¦•ã%5pß”ˆ üt—Ø<Â|‡CÂÞàó\„(ÁËÓâañ¸ þ r-ìAÊe1]céDW¯$¼»Ji4SDKÿâôl’£½—|[Ú@ïoœEйî¸þ˺½Z‡&s~£ÞU Üvè=æ}X\ÀC. ÔR›`ºúõ:úîCdæí&ê»áEkÌÍ ¥žeíàÜ0†[Ó‰Á†Ý°õ<æ;çžü]BžµÌhp@"7ó-ˆ7Ë‘ƒ–à\”#œýmE¬/\Äœì$Róä7˜mʤ©§˜bi J»vÓ¢gŠt Ñž>ù' ³óuH÷Î<à'1{Åp½l3GÒA“î÷¢ò:´^v]^¼”F«òpô]"MG»Qô’ðΆ°¢7øçÅqÒjÎGä~ #ß_Öø›DL4¢¤¯4énþ„1_ ~y.³b]»q_3í£)}¶8†ºŒžÄó;<ˆÌ¶!¼Ìý MÕèK“ zòïOð.2ÉS:xÐð,î¹*MÄÖZ°=¿½ež¨§Ó¶ƒžœŒGñÄÀ;únoùÕzÜèéWàÅës ¬ŒƒO/íè_½FØPÖ‚†Œ`³vyN¡®«žBß 52µcݾB>œá´4¶{iøý>–†lþ’ÃVäZ±h¹GÓ§ÑõÞ¶Ìòa`:þêzä~ŒÃäSŠÄæ5É.^Ž}UÀ—}æYu0 wÐ#" ð\ãrûRò)Ç×oÉ›]‘赺¬yH‰Ì|ØlèÆüu¾À¬è&›ëÄHÚqèn<&¡-à–(WÔêPͺöÊ S, º*Ëp¯ ûûïRÂ(••œ“p\n%Ý|\ÎÔpÖœO„};Þr‡Ù*ÔÚ´›þMÖ§‹?aιUô†¢, ÞXŒ_×/¡êv¶v’—^þQD缤;ý­ÈvÝÈ›Š7ýêéÈ¿w¸­-¿ŒÎd4Oœ#êB íiMì¥ÌèéW9kw“çÝ!!M“ýsä=X‚=ù±ð4¤9dAÍÕu°|P“¼Ì­@þþÙ«‡Ç FµÕhk3+HŒåejp«w¼ßˆåó•Ù³NË@©o5~j×ó»›ñò·šü×Cz¹ÐJªúl;¯]N·¯¬‡Ï vàö•s1T)>-a.íz§XcÇ‚ò.j ì>ꆥWá³Ð[ór˜kcçQt×[õþ¾1Ë9’s ËBbIö²Ã¨2•ZÑ:øþ3†.ìM¡çbdâO=s z)Mþ¬O+=BXÿ›Æ°[Ó6¶ Ð4KjU¹™Ê0#Ö›ÕÉÆ}1à_)Å^š°Æ3̸h˜B üૃuñ\vþ´2¸ºo>³ÍĈV)¢Ìćå7ðãâ¥`)ý~¦‘ˆ÷×p"`¥çrÊw´aIÜ÷ÙÏõPß”…Ï”è“ã#(þï ^±óÇ¢ý½zâÛí4ÆÖ¥@Ãjw¬[(E×õËÒ›š»`Õq–dÉsØGÎDëÖHUNÁÅO-›œÝ·ï½Ôÿ´q±t¦‹~N>+|2×÷1¯ûb9·nÎ&gªµàòõdÈ9ó‚ÈâœÏßáç—Œn¸%ÕžzE8½•Y=SôR øg¥aã‚tÊ¿óÈFRþêûäc•,ø’KÚòé79]*Ä&ÒD~šýhûŽ ¨©.Šª,$ÁoWÀ}­sM]ç–Ã)©rÖ§ËŽ>O¥¹O'¹±‰¸!O«Ûâ¸ûŸ<ÀÔGM`×»¾[[ ÖQ3»:¤¢]ÿõpƬŒ"Þs’©½çFr¤‡ØõÓBcE¢¿Q‰FòCùÕÄÿúIã¹ÊTl~”h+êíÍæ¸¸fÑw—®¡ íç #øöZ£}n êG}c,ÿžéG¢´ê“=GÍ;ÉâˤV|¹‹´I~fz?ÄÓi]É p”`õN  ÍÈÕÇ¥ø(¢‰Y¨šÔÈ4¾ºìõîc?qFYHÜÆû ·1ÃÞö«JfK<Èö5P¸7‚=y†N{åÐñnNžGm=gP˜r†-<&FvnÂ=ùû™[ƒqÉY!rëQœæÿÆh§æ—ŽDxš9=ûÀ…o7¤Ÿ™»0þ__cMÇ¦ßø*Ê_¢Õ‰ÂDZÚ.;{Áó~%ªê´×Å)S‡Ã{aB1 w_;ŒGõØØOÙŸopçÍ (_rÆ–‡²¡©I̘™ùš±œ¢Ób;iËŠ•^´åír2ý / ݳ‘(¾mÆ©>¨üO‘|øÐ’º4ÁSØœGÃ?{¹¶¼£lg9N\á!ÓM!Ñ/اq3áàšö‘·/<lßÇ0/­˜›Ä™¼–Ér(Cû·š¶¦«¨¡^)<õxóŒ@à_ƒ6¼|Ó‚b~â«5w8uß®p4ð7çÕ~F¼.“åOÒà8ìèáô¯I óïp”1§æA¬süZò°(†o$`ø¼(ƒ3\¹¥¹Ð…¼·¡ ðéb]êT&O¾bÙÌ!&áöû˜ÃÀF!rpƒnÑꓦø~¢Ï<~:ß%=#S0;BÙ-~˜vlÞe8ã‰(¼ºž]åô oWU°ë£÷à‹–—ÌžÞ£øéÞqÈóµd“Íï7nŒg¶_K„¸·/Ù{Ýg1¦û-“á¼ï5ìÃ9>û™=ù¸Ìû S½á­Omê‹M#‘6ŒÇKpp¿Ì,sÊÅi«úѼ“—nˆ$ÈwÑ2íõé}í?°;ž£¿|ÓÖ½ŒØü©àë´ï$?Á¨»º »Üš’©Ó9ëj38S’a`ÎÁIN“Àþ§ëÎê™ÁõªÑ†íVSéu¹oIÃçhíi†·#ÔYYá›Ìšég˜Ú¼(²Ù! c©bTiâÊH½Åˆ'v`5v,–"Ï¿Àß÷]pq¼ ÍHé¨Ý/5›M¡lLJêdW–æ2úF1`Ãneu÷Áä8lœùev€c…™S9Ô n2 ·¢™Å¿ðÜŽZ¼8–ɾN¿ÀrßÏãTÜ6ÞŠo%±J¦ù„U°]û°5u°µé~ó€m<©ÍÆŽ)7‰!S'cz¤Ÿ.P  ³ñ X7÷à¿ï!X𾇽=žÿò„YúYŒ,÷¤8z æ¬ýŸýæ?l üþu˜èKc²®â¦ÆPœžØÿi£?ÿž`…·çÃUÍeœ]¿ëQX`ý°á ÒT "ߕ½`5­B›Â‚Ÿà:ãv4X³.u!RœPòøho*göjáŠi,ÇÁï18›G°¼ÖñØ\ìÇlœÒ ÆjGà‰³*„Mñ`ÆÊ7ƒg¨#ìTXå[uØ ÅË̇è/Œ½ãs®SS8L ç}Ç!¨-=-é”'ÍÌäÆª˜ºÀÁ™n4îatôê2foª`v@RÇy¸èfzo-Äþs˜²s õNI®bÌþ*Óÿ³ÿÛ¾ULrK(kÕMëCD0_㮵ÂÝBü¤]jçí·]çƒ/ÖßÀÐÛ¦°ÃÝ ož1¡­ ó èÅ NãfUfå‰qö°üìiƒÓX˜kúÍÒ^ÃÞF¸½C”> žÅQ]ý€1¶ï÷wîìSÜX‹G[íhÏšxÍ5…y¥!Zw fÚüd+ í¹ë2ç±J‹àФ,zN“_ ’´.}* >ñƒ™ ÁV¼(¡íz_/™]÷O“|üÂa'®.ƒígÉïî×xsM>ë¶¿‰m¸»´M$6q@–Ö)“xµÎÿÙÿ"ä3.5 ¦úâPº\œí|; o®ÜD?É‘™š‘àðB‘ž\v^iuµïÙÎEµ¸‘¦‚¥¡&®ý,†÷T²gBêÀõ?þ¶™ Tž‡²añ¬lC;~V¯‚¸l–á) ÆŒñPI%:ë¾cçºãù_b¤6Ïšl{¹rx´ˆev#ó€ËA¥nÆ{h;ªsAùê)ôðÑ$0ÕšJ¼.^Ã]¯ÿ23‡Ãÿ½˜÷Ô?LW& uñÌ7×ð¼ó“⢸¸1OÇkñÙ7–¹!¿š^Ùt}=³éðø?ûW,nâXz°Î⬭…ŠŒ Õ"²ÂàdôËz½bKÓ¤iÌÐX­â™¶héV‹²b¹Ü_wÑÝy2o4ØrÂSèrñAlZ¢Øä…>Xà‘ßjsÁU©ÿNUÇDå@˜é³ E¸¼Ø¤9 Ÿá1wì9ÅÛKÙ>â¡BO†3 ¨ä Á3áÕgvzH2›߀Ÿåÿ‚Ì¿cX¢ýœ¹¹:iœ½Œ S'V[«±8<›.ú<ïñõrÚÖᆑYPÍ“ÆÜ†U$Ac;¤6hØÖ zƒËR”«`&4õÿçÿÙúºÜ n>*\Âé4×®¡nF$¬NÇ’ ÆÛè£SnèžÍ¢bÐaΊèܾ^Œ|MÚJM~¨‘ƒ-EìÄ~}üé;ˆ³D¢qònŽkq Ë™s›©O¨<ÕÒ IZNlò±ATœZ‡óEÃSaºÖYŸ¥“ýæÔû;ýgˆð´:GÇ££Ú_0òŒ!ìþ8(^MKní¤ÛzN÷ÒyÄ`«…7á=™w\ŒŒ½¯ FZKI’Õe(>‰fp2u0æ›9äž0áÖÏP úFb4æV(è¯ñÄÙ>ÿ³¿ä /lXÛåáÍÜ[ëýè`BTóÕ¿Ì—è ^þôC.ÛÓíuÁíe&Fòk/À'Qx_j Ýgш™»Ä8Oø?€Ï»ƒ4e¼vî4…õF&ÌbM^¼”<Ì”µd0NË?âïª:?}Ž).U!-+eA/ü4¬{™c¸¢P—$7jÃÃj¦Í5.„ÜsGèV–!sK³±sî?Ì…Z˜¸Sïžb ï'’+2.xYZT]À¨ý/ðÙ:k œ(Þxm2› °1·xhŽöQx+Yı2è„Å}ÆÌûÕgaòîÿßÿÖR¸Zí*«³YÛMŒaÖ’ùdP}²^º‰xÉÌ>\gn÷srˆ4øË-¥·;* f(³\7²ƒ–„üù­:ŒgµZჵɠ?j„%‡+Ù¯sWs>dÃÔÚù4_6ì'ã`µ¼0mcÒà宥lÈÉ`* E¿ì›A|Mõë>6n½È¹kÕˆ  èÅÓ£¨r¿“Q®ŒƒYoî±Wn|E?½tl›•OǬâîó8½D•CEiKm Óé±’Ÿ¼‰`EÐ9űÄ]Ï‘1èüɹÕé> ¹ ñöïh`G çü§3ÆZcO²P<7õÆ ¶Z-Þ÷z‡³{t!åž!qo/b’ž,e‹Ü×ÃõSºÝQ€è¾ÌµÑ©”Ì §‘$i¬ŠèáÒ´@²‹N#ŠžJD*½ÿv9{³ðìÂCÆLª‘ÉyÀz‰x——íšzȾÏy“Ö÷çqhš«‘X$ˆÿõ–^Úu ƒvÂãÎ3¤Vùn+- ¢ðwûHУBÕÃûs Y|$M̦1&{ƒðìѼTFïñjÓÿæ˜}} ‘x0ÌU¾ÖÆN][ ÓºÄÿWÿÿšÅ³Ó¬³˜µ¹¡TNKoTärÔÙ+ô T7°;VoF°cl¯¥(Nw!`.[Ë5ô`ý·Ì£r\/¶§´³SË-)ö{% #¨€€:Ñî+g±x„÷ŸáÍ×!XÉFYIzð(äg:ŒÏáίÖtå~ òaÉμõòÔó‹)Uܗቚ ;ã–%µq9\¸{Þê//±Øg5è&Ç~¿ÿùßè†2.]á £[iû.—åÞÛ6È‹¦{Ÿ3ü{ð”Æ6àpÑ[öJf ¹üJ’zìp€™Ÿ™^/²‰7’ûpþEÔ\“ÏÜjìâšTöõ»ÀŸo*Ù~y>30ûÚ>Áâ7ÛÐêb"HJ»:…ÝQhgÇ{Áï,¢ß·V8StNø(Ó"² ¾¼*‡ñ1yHzL¢>Í%s8_ðƒó xé¶Òk…¨È_?©Û¨£[:Ô%‚é¢XxþX—ü‰ÞL²ZÎpÌŒ¥èé“¿ƒ¸ÅÈÃZuêÏ¿‰ÑŠ…°Uräÿì·QÅ]Ú´R*šNk:NEÚ8ŒÊœ)DàE¹º:ö? 2“þa‘mú¹ÃtN:ºúìÔ#‰Ó/Ð Qm²ÎSÌï^B§½Y¸äÂ1:‰ìý—³ç{Ê8…©NdÆy„9;øäI,õ—S>umz²D‡êlºGÌÜŒ ýÃíæþëiòW"}×€šÊB©ÿWøãÜ ×_Ò £,8ã‹&ëðuö:¹c¢fdÅëg̺V^jÂg›Þ.…S›oÀÛ†H²ìú)t‹¬Euù2÷G2i1v#-Ü0Â{þ"²Hžïu¤Nƒ_ÿWÿ{ìØ@ñ¿h ©_‡bÀúîlê›Ü޼ëc&×ã+h­ŸI®ÛXbé··°÷é0ÖûP¥ç|$iF×­ U{Ìq¶«ï;öŸ&™óÈ¿«9Ùo nçáòÊ5pgh™aÿ'lû±(zûȺ4+JŠ‹aű8°M ¾UŒõ c½žÄ¼Jñ¥ÏK˜C­+ÈïþýtÝ#ä…öï,“ô#1¤•ó‹¡üÞV¤<Á4kËÐçDö†èaîÚ‹ÄõMüûnE¢ë4àôn†¦–9éš$ç½ åS0 k/—>úŸýM‹@Ëã=#æâ Û„ˆÎâ\"`w^~?ZϹGO(Ñ@¹‡¬€#.r™U·Îaf“.zŒ$€ì. ©e 슸v.§ÙÛë¸n³@0î0F‰hx´cQ²Í,éÂZƒÇÌîó§˜ÌtZê'߯«‘Ä„ÔÙEœšGk>xÐ[)tÏëd¶vðÑ'bpì÷S€ Dÿ a¼[’ƒæï¦2Ù0%ß•3âÊ4d‹Ž_*¾Ó!£ýº¸ò«=q¨0ó_!ÅTŒˆö.& tê Pä<'LD‚»—…CïqСÿ­âßÉ[‡3{=^p§vÏÃl]Iæü=к¯ÂØ>îbO„%3 ÂVpjMÁÖ®kx]‡?ïGø0Y»¤¡\÷Y°ü¸µØNŽªaž¿ÜOw ‰ùeY¦sIsH¨V ’÷ûÇÆêáŽËý4VÌh;¯‡»Ž†ÄCª—­}r õã5á®Q>ž@ÓŒ“WÞì­ýo¼två)Ø6k/&OôÞy’3;4/ø¿çèôqgn…ü•Mè|÷2´^LÀõ²¤}z &ê/ÂÇ–å`ì¬Io~y¼5G°2ª”½?ƒªƒñÿü_åŸÉ½X“ JošØ~±øÆÑ :uèhN:¤‘hÊDÈá*g†Œì*¡%_™Ñ½9O/Ç¢LP$ÝiI[kçÒ{§6Ñe‘nôèAd¤Eš/b¼´¯nµ¶ù€k4ž€~†?»pÃC8qçq“¡B1$ÜYÎ f©Ó¥†©ôšJ3ñä.´a;}K¯Ô£´œ-©·vü"#`Þ/6È“u%¹ rà†ÙY‰ýÉÀµlé\·ždJ]Ζ’NQºþÔ–G*ƒt<—£+gÓ£¢pmY&³¶þ3,kšN3‚™ø€èÿåÿE¹öp@i‚ËåúCþ•'Ìæ˜/ÌÇ%jduÔf0‘yŠÝ"ÌKç/Œ†õ:ðZ#C|TWSN÷úg§?»µ[”|\“GuÃèú"‚擱±g kçà ’ä¶æTæî*>’§ÊиC_Qñ%–*˜¿ç72°…éuúRÜ©ºáUvæ¼hÆíT>#›c KvÑŸ¯ÅÈ[Ÿ¦1H›ÜN£ ' ~ ?%×5=˜ms|Hq 3­S±Fô+[g¾Š ³–Åò]Ÿ>w*àtÍ'SvÍÀÉ bt\IèWy•ˆÿõ·®×iýßú?&o€ ŠOQ_ë;—éï]=\ÛßÞtŸ\ l;ƒ2¹"4ìø4Ü3W›FÊ?`-OŸ£#Ê£tf’”í¹ä³È¿o®03@ãF=é}êˆÝ¯'kññÙ¤UB†H者u»»,›ÉÝ‚F8u[˜ø(¦PtË`¯ì!óVÒ¯ùËpÚÝ[TéR&<ãßG†!'Á•Þ)ëFÏÿtÍLWøgÆ%>¼×³Uæ`¾‹¶™Ì¢³¶-‚e×áêS:8Ë$ÞçƒSªZhá4Æu{´¿IlL€Oa„tÚõ²tù*Ö´˜ÿ³ßÈòJ 2°Öe]Ów†úaOÞctX_‚x²ñv€0ó~…!u|Öž}¥xc·u—ÜÉ–+IÀ±­‚pÐÔ|V½C¯™¹(ûþ!f§<§b\ð„cÑ‚Z2’¸ú¨uò\Gä!³/œgnJ "YóØÉ¹séO_îs?çIŽ>Š Výìúí¼dî†ïŽ,ûö¯ ¾/y•Ñ1è2åí?µrt©6M«Æ&TƉ;êø§öݦÏw–áл‡¸Èךx<˜GÇ„lëjŸážÄ¥ôØm]’8Nc†—BõQjg|tG‘Ånà¶,ž­\$JëQÓXET*%rT¬óއè“Ͳ³É§i¿Q!Þíkކ®Ú“pïº8Œé-Ñð9äÍËúu…Éh×ÅM†08ÿíì.~Zšy …5“°Õâ¨ñ½A쎭ØH äáM»£ð&Tw»áž©w'ïÙl«ýÜ“V==ñ¿ø×ò«‚ii¡oV‚W›§Ñ mº¤ÈÌ‹’’‘ÿ¨;ÜÒê…uçHä½tL žF£RÎÛJ˜mdMÏ`¼Ê=fƒò𗨇ûtσÞÅ Ì‘Y˜Ç#M‚&q™ÜOø;Œo=aJœµ¾kô…Ý%Ùµ¹‘ä Yî˜M¦ÍÄHÆŸÂÝqùÌõ[(!² ¿‰Áiâ‹Aµó0|¾1Ï ,Bí±9¸ÜR kñnL )œw“+ãjA\ŠE@}™"e„]Àã#vè˜2>^{˜ñÔq¤/ hØÉGŒh0°^5¢p£z½¢|ÿ—ÿ­NÃ]ë›xwÌ¥6¥“,d/»V"ÞH€Ä“N®ðbÒ¹¶šædŠB¸¶É…¬ÜTÂøé¼Äk ´]ö:ÌC"&e@æƲ¢ßض–(°R’®ZÑÈjˆ•0žkγ¿\ÁõZZIüÁŒãg ×ÜcíÙ5<ÓhæG>:—êÐÕÌ}æ¿ÏÇÎ]WÿH$_V³¾ÛgàÄ…0øD´Á¥wî¸).–ýtAvlVkî"4?Ö0S‚š§óR\z'Ö©a¿|#Û–j «¯C_ÉCØþò³_D–®Q¥;µJþçÿe›Ì8ÓïÏñƒ i‚ßG0G™õÊ+§×Ü2ȨS%=Þ;îýĽÑSH‘F»ä•5Íyw¿ú Ÿ²yA¥Ýœ¶ JâãWû¡aEª¹=À‘º“ø2Amí‡È9¿§×.Þ&v<6- œh×%ã ïâøÜ©dÂßûÁSÿ×Уäw‘sÔ#sJñÖ–\'·’n}iJö×Ð&3u¿Å3e‹Åé†w´q°|©v¶8ÇO+štåJ“ÉçÂŶ*÷é¯_N¿>ÿqój`‹ÏN’*2St7B—ñköÉ×»ÔÇÙ_œÐäD§{Io#EêÓéÜž¢ÑÊvIw`Yü,ê:ô÷ ¾c{†›^úã²÷\X>;’ïçc”Î6ªåëFLK ˜ÿ³_xð3º7g?OW!£{ ˆðŸhl+E>9¶`…“(';05Ö×kÅôj„Öžîà Íhhð"’¸Ù†’§6xy“Ùûrëÿû¾à“~ÅÅvÙÉ!8ëç E±Qðé yx·¾­_IÞXfâõéèÊIšôé0™¦ BíoÄ¡Vô(ûÁé8ãžL5½M*Cb0ôä-|<(Kï†ÎЦÿù_rÑwN[Íc7t5 –ÐËØ³WÞ.¥ðõü-œeÎO‹;rñ¸P&v'G§Än¬±CFù4Ývù`ÃOÒúO2;Ô·ÒìaTSÒ€]»¡”¤e¤âþÍÀ¬á›FŸªõ±RSxPlJ:î6)ÁŸ¨Eµ MñÒ˧ hʦÒbîÔ¯™`i©BŸ¼-+»ï0³™žä:y]PdÜ =•W˜½øhª7“8c%;*@çÈ[cðOÖêÀ;ªªf‹wc³W¿l!¦R‡èÃs%pÎz޽šJÎDâЗ?ìæ]pH:ãþï> |õˆ×ry‹ãÁÏ2¡™ºd—áY¢×[É-T%.×.`k7ŠeÛ€ð|úÏy€øe’9Z³i×9²ƒ‡ßNqø ä.!ŠHø:mRü³—ìq/À³¾î˜¹<‰œ¶aáHSÞ³´…ûJâT×2ƒÊ®’ÇŒôlv¿9 wv‹…¯*䦡5¹Qx –¿keÅÛdqÊÞétEÚ9f4}5ɘ± ÜÂdèñGÇQå”%&‡äHq‚ ù4cJ_L Å;Ðv\ y}ü0ê?y‚ÕŧȂofpõ¼¶ìÍ¡W ~eÇþçj›Ž9Ô’NT¡¼B#lË #•©‡ˆ]¿ ,­›I6“”Šyhgî@v¯z©+œH´ÅUòzQ4×Ví'ÊI9¾—ɇS…äÇUÚø… ã·»_1†‹ßýÃ×sw‘Ê%dߎgœJè„’"ú0K¥%HÚ”“«CNiE¯tª~™\ºu>.yg=ªÈÔÑùäÈî:GØ…$ófß9¬ ^w}Éä}ädº'îü×ÈUn·#}Æ™¤‘ )'/ ë‰a"ÛrœSñ#×§ð£÷ÓyT瘜YCúéQw Eêè0›Ì.þßÿ· Ø'‡EI)É@•sïp¦–;mÛ½r¸SèOc*º¹ Ê N2më“mB¥µ"aîq®ƒØl{í5;ïŒ)Éž¥€M7a¶ÌS|8nåÝG ¶#WVìôm†|çµ0§©–y±Ñ›™¥ ÁìÜgϾF's²-{–Üæ…ˆÒRfò8îµ BǦO+qв…X–2•x¦¿ý“‹p›¢%¬6w¥’ï2˜9Eäg[ ¶”1:f{ñÑÊ<Ž2»»$¨ÂÁ|æá«lâð~*åŒÞ…wÒ‰ô^R ùÒ§ ¾úUÜמiÿÃÿÛÿJ“ÝÓÿÀÌWuxbøô= Ûôùé s%2k—?}xØÓæÌƒæALgÖ(>ÌTÔ ÃêìT½ÈvrŒ2Nà&K3¯³z=nøsenMíG·ÊøìÏĶý‹a†a~³…ç³ÓÄŠíüÉ)ж!"‡<°õ– ,x9‹ÖìAéväÔ‚WìÆ/²"¤Ýs1„g -™äµW–RFOëõbÓW[r­ï¶ww³½J©•~9ó^ì!wXe:Y.<•Æ_S¾™jäôÒ*[ÝSYì×à6Øt÷¸—•3ëî/‡8ÓÜÿù_ärªm;ÍÜ~ÇÛق׋U˜a8€ííAÿ/Àz+øzÀü{…HÆUèÆ v6 ¿Jò±rÚi&#µ#ΟF˶QP‹r@~æCÛ6<ù˜—„Pä“’E÷Æ1pžósEiñ1zbóöD?c½s.{žb¥U‰wÃT] Ìdœ\EoŒ¶Ûì%¶ÚäÏm]fã¼50ӤЛ.{îû×á _=Y_„Ž à°'ò-Ý„_9‰°-|7®Ïµ¼4ºrˆo2Ä=‡épúŒ!ÍœVgrßËüÏþeÃwÐéÝ*†÷×Jg¯Ýã5 “u:¹¸çkz†íÛ.rGÏáLK-ú5£Óðñ(†ØÇçN¢ó3¼Ôv ~üÃùvp¹!×ÏVeÎKÞV´‘t‚øs?Ø(ô€ ›ƒ‰¿Ä!棢±“ƒtïL}y{¯¤ƒ w áME—¶ÀèSÉð5º”ð¤hSÝàÛ(r^ˆÖÌ“!ñ\CÚ`Óɾ7ŠEßÙ§P§ô»Öû#™°ˆ:*ÏEÝÍñÐHE¨€x_tÿôŰ¯âFði‘y£ÎO³G«8ÑÅO9ÕfTöìÕÿáÿñ.¶yå7Fú_&,±ØAχ4ƒÏY¼|0îWÙæ´UÑ7)Ÿ-K¹÷Iæ§§0GV‡°÷Hi› \ÛÂ6>ާ[§.¡«bˆî/²1©“®›‡uc"d§c>´Båk %JK)ï¶—°L¡‹ãdqƒÞÒÄ&Ì'Ú¨–àNÛ ³×#áìÍ<²ÕÜ•Lý e›¯ÑÞdrbÖNb£¥Ã&ë5¿´«xóÑ7üˆúî¸ßÿÿhhoÒžšJÑêܯ+ iÉÞBB’ì­½UJi—&"•Jç~]%)$£B*BˆP¶_ïïãñëóGÿœsŸë¾Ÿ½Æóù¼ï×¹N¦m&ÝðãåqJ¦]&ˆWc–/™ƒ‘AT.ý8±t"ß­¼ðx¯!ÙqœÎ«ÈÀçúIó¥sâù¿Ø-XkæÉ<ßîÆ¤Š’ÄÆgpô¶8êÒ-<äBìw8C¢ioævHʘOÖà.©Ä—LŠ1ÁùݺMŽtbYµ øW¿gßW ?îŸKãtº½,½\ñ‡9X„(£UŒ3—0ð³ø([›"GþDÍoY z )’…ÂDz9¦ [[èî+ô±]‚ìhï:b’´„(«ä¢Éïˆñ^—‚ñ'Öîð¢Ñœ‡e­ãÚ,Й,8¬H7â!Ñ'àÍiyYÉ ØÊxYeäNª&4ŸhÝ3%gÅ ùøWÇ7ÑÿNœá¡§®+CïÓ7¬Î=f¡…1½Ê=²?3¸îš[ÁïÀº·ò=c¾qŒsäÂÂ#wàÀž\°N— YáLÉ|úáj!(Ù/Ç çÁ—KxKK‹l‘÷§»€,–ƒg¡:‰iʦa"òD3Z»".CdÎ<¦Å8í瘒ºm_XÏûç¸Yn›ˆÕZo¬JFfÌ<ÿ$¥ öµ<Ëóþ¨uÇBÄÉ%Vƒ—ÊðÌ“å$W†C½$µØŸÞzp¡Ù^9§…Þë'sž`e¶V•ÁͧËè«…¬ÃàTê^?ƒxŠô0_f¬˜àÿ³G¦‘oå q3½é³“)ÿ©]¿P~(Gí:ñGt$]¹(r5Jñ׋Tð,ºÃ|)ÛŠ¾nd  ½,H“ËÎR=Ý­Œ”|1˜.‰eÕn°›l·’Ÿ-ÊÌõ!m*²VŠÚ ¸´|H‘Ì\ÜÓ!ž2HeP2[psçWí.G.ÛóäZy® Å‡>Àß. òuR“{â Š­ä®¹x­ÄÚñ·›jëøPµÛ:0Sˆq¿cèÓÙ¡4Âk--þdC—Åt¡ÏªN89ÚÁì ú‡‹þc?©ØBÚSEziÏOä9è9ÿSøo_)ð¹,Köí2&ß³ŒB‹?–Ê“)m"4˜Çÿ¿{ äÛ£KXS8ÈŽšSNW6êÔËàHµ³¾ÏS=x®°ŒfÚ‘Ž078c[Æî9œƒϱd!ríu ¹)J4B$ðM:ìïVñ<8oßMÆ?‹¿(QbqpR? DI€ôwgÞÛƒqeÚåd`ÉK~Ò³‚CýMN³rCæŒQþ'è¿}ÂÎ3Â*pç°(ÉS¦­/+ñýâÊÀFp¥~ÃÏ-káqT,dOù­Ø|¢õ-çNä¬ÈÈ™í‚á¼8Î7,ã]¦0ƒÍÐñÞ³ZM”.ÞÅnåA¹Žô͆L@[¨œã采ȽíªÄíÝvÆRü,lh0%xËAyò~ Ðr$|Ý4ˆþ8õþ³xêüð_x(ˆxŸeäù¦¢“ìMð1ÛEöK'=â-Ü­w•è¢Å’X£‘GŒ8p³ý¶ü3Œvr=ˆ’MxÇÂŽÈòÒÕsÙªWÆ4!)ô䥨L/Þ)-f Ç›”»#§(N Ò]òéOE ¬”%­wDðÚ@ l¹ýŠIÿû75þoþ'ZÍ€sþí.ÿ߬ÅE9¬í«GÿÇ·aŽl LU'Œõö ±nƒþÊLx©*X…>e¯NöË~³_ý©…£>T(Ðc5`«^X¦ªá´ˆV¬÷Çþ]]Ü‘ÕÓ@³ã:”ñ¦0Ònåçç•ÊÝ»7öLÂÓudÒ‘d,¾wP¡¹¤*_“d\‰ÆuQò~œsþ ßc£dÂq¦Ã)X7#º à£WZ3gzfA®¯&'_ä¼v•'fZÔ?u% jÑÙ&V6ä=l䬃Á]ü9:ôœ¦»=²™Æûôk` æzNÄ?Y°ã:] "ûŽFw#û¹·+aòè[xg2—dJýÆË¼£¸îáC<%k©+1¼ô3ŲáÊÏfvÁ¥BhÚqTo…ÔKa`ÊL¡†gYF¦bu·³qØ­‘¼Ì§ON(Vxç+4B×UÚÒ{Vv7¡Ò¿(¸,¯AJS£Ñ}ÅvÖcÞzì0ŸB6> •Mr¤²^ƒt?極˶¸ôáø9qéë\´»{ >Xù±Š;"1¶RœV’›l$C“â!t‹÷õ­>Px¤LVõGba®%\ìôûµzxTàÄÿœ5À<·Rö‡rª,«Â¬­6/-2ûƒö€¹|Ì\]Æ^:¥M9šu¬Ä¢96ó#5qÄ~)¾ }66æíËéþPôé$üÐ|·M7°Ñ2º ÙŸˆ’%óÑ×X‹~nÀ¬ÎÁÎÅg¹ŸœÀæ ‹s i$ zxßãå{嬅ç4ڣȾï÷®O&¤=^†×~Ã…™lï§½pG„ñ!ß_´à‰dyì~v ¸Ž¨¿å'š»ËÉoÁìC8¹hì?,µ¯ÂYÙ¤:LQêÂ÷%ºo_3ËgÙ ëìÂ&ð7Œk,È“`²–.¤þ;>±üŽ”ïô&fêa)Ú}’e7Y¡È©™$hN'ùÔÆdÔ+Ò¦ëlÜ%Iü¨Þ‡›–êbSQ5Èr^3gW“ÞÅ·a:?NíYÍê‹-K!r\¤ ¢%éßóÇQGá7nÖŽdªê’ñ`ëdÚâ¤Bá=¦ŽóÙªÀ|(ÙÉåP,`3š?\¹²ÌjRðá#[(–7¥ž‚©ÔK6×ý!Úÿ®0œ}½(r/ŒÔ‚ˆøqâ÷"^î¢üú±ØwKò»ûÁÒ¹«áµ@2k>ÆjGÓS+h¦ Ε0r°×ÄFî*If)ÆîécŽÚ=ÄñkmïX¦l©Uã°ïÒ>‚Á 3rU} "^I¾^E|îýF ƒƒør1ñ›G5Ùœ?^°£×š*ï¥iybÄ.h®ú­-¬ÇÏÙö¿þ¹ ßi2×Åñ˜g&Î>? ¿@òƒw˜ü¤wùÆýrˆ´÷-«zþ;žüû†uÉøŠ1ý¶X7"…÷ýê`»¿=Z‡c†h¬ã‹ª¶Ð˜Ð#¥UÊȯ²ÃÉ¡ÉàÝ ÏnxÁ*W ô< Ïêj™¥?š9v\ºPò2îW4 û}ÌiøÅx±/–9$—e€kq有@!??ðæþcjíìÉÁ#LÖ#5.“|Ø—ö/ö™uxHì Ú»/¦À•Di9—[“} cÇ½ÓØÇi°|ENzZÉýwðÊ»ËMä©ï4<¸¯•[þ ˜YóSºnâœY“ÑšKõ´çÿãìv;y½@’†xi\Û|U1•¥Æ}mÖ_ÖXE’n5“í/ê5\ÛÌÛz¼ðÜ=×ö`Mþxÿq€ŠEN¨žž7FtÁRÒ%†ÀÂð™TÝüL›áwCQ-f-­äOÁý76â®)’$qåÆ$sG·8~õ7àÒÔ£po<ï–'3ÊÓ×:R¤þg ¬vøÚ²àx}&{BÞŽ¼}ÜÈòµ˜Àî¹´TáÈœþÈ´Ç«"Ͼa–äÇ¢çñìËò ý_xñ›Ãã-°Úd-8_…š'oqE3…€F«kÊÞ8§B— ~͇%¸/–åäÃUћؘÍ|½{œéØìOçm|ƒ xÊqq¸S·ó|>£+5`³D¼?r ×ÓÈKÏÀÓwý§u›Tðp§c.Y‚÷=Ò°B= uÕ”÷Á9;¶ìþÌ`wì. Ë—¬î#&SPy‡îc|nw Nútâ 7nçGzAñ £¼¯ƒU¼!§ÿd;S¾³‹§&€Q´"iWbçL´~ÜΚ©.»µ"õžËe6ñafT=òå¨Â2“Hbò£Ž•wOçNýñÝx20Ê* o½-3SÒø …ùuÈ߃SÈÀŽE$lÖw)s'J!Aèáq‘|ßvîM§]§O“_5J°/y:Ñßè@V>"p½ì&rr!|X¥AmïíÀEÃñ(¾s×w¿ ½±b%u7=„- =¡6…šUËÐÅi­  ¥ËÂàŘ£–íÍY„Rrè/°Çv—àfMM,S«æ¦úA `ƒW¸÷k+ž¼?bwrH|òü©hBt\ÊXÉi|¨ö¹<Ú„&ðoéžËâˆ>sÊx*™qvÖe?àº;µÀqµKlÀ;I"}q6lõ܃‘mº°®5 øgé"œúÀÌšòÕ`ŒU/Ã/Wrá’µî|_€‘Ïmñm|0·}À“­Nì³Õ>(ùGŠŽðÐz·¯œ%mÌjç÷à46ÆmÓk¾ n”ÜnæLg·=ácªC[ág²N·ÀöaI”ZŠÿÄ]A©ŠË™éîÃu\¸Œ3sð5ìî=ˆ>qú¸þ÷#|qî ›îjB–)‡ïZ_¡[mÜÛëÐ:%Œ³y²¾U²‚€wÑ\x°ÿýDÿ³wˆ@—¿±` ¤g çfÖAß&/²'3V…@ñC^2zW‰=·Ç¥V€ÅûmDäôLV5n&Õå_Bùƒ 4GƒsùÓƒ¦¤éA9¾-'p¶j#zÍe7F)áÁiN܇VsÉÊ*Iº!Û œ¾}å#g9®&¸ïA?†oÞȰ/?@…ü|TW-‚×~¤;<Û?Ä dð&îL¯ÝYþª„Æ»xÜ›é¢pK N5Î`cvÌÆ«qx{5Å/ñÙh:š8Õ0žûþbgÄ-6­F—¾òaN ã½{ÏÙe‡]'îë±×Ÿä 1C6C,^銥3Î[Ànû£DäÈXÉ?ÝÝZTPg ncœ²;9ë±—ì·1‘ œó%¨  Ðß>KÉé8vM‡?çOŠyó ^9BÍ¡‡xeÔ—ºó{AÕI*ä.BæÌd.ŸÇÈX‡½dp[v .öxŽ—Oû£ƒîd8¨/ÉÞ‚xqíQÆù…ç-@c¿Cóú›9Š2íP]]‹mö˜Ò:‰\îa-1I€týÎáüY¿Ï­¾ Zø“¤RÅ¿:YcFf;X3);òòШõš<‘ÿ·Às¹/^[rn3½÷Ž×Îü°JÎö¡ùˆ™†VoVR_¡¶ç€$í›t Ò{²àf›†4ÄaáŃ8IC“íÈÞ?^Ã^îChòoašÿ¤1ca^øjÕ)¸òÏÏ€+'†X>~h=$EçJÐkì2²n]‹„Ë·(l+Ý_c±½ÿ:l߇ç—=C/ukÌ;˨ð*R©ÑB›7·’¸§M’ØX‘½àxT¶m…ûr]VÌå>ˆjÿ-ÍF§“­AG¬W„cûÒ1¢5~©ƒ•—ƒ&ø/ål6sät£¿t – „€ö<°{/CŠÄëXi20ëð¬ nÁ‡ Ôî-lÃ-7üÁaÚ'ƲœC½CP.5 ¥†ñÆâFVÖD—þ«ßMÊûdÇ0á¢òèÖÒ…§­Éì9¸Å³Ò•Þ³Ov©ßš§ Ôð[ï–²Óù͈fÝjö§#Trº\ÚêŠ+3¢ˆbö(n#Wþd>áT"z6‚{ì€n ê¹Ã ü{`vÑ-NáªK(X>ƒ=S M>ånͧ+p]€89p~?¶6Ö°?Òƒ¡"qˆÉO%ùùù?rÏ •L˜>px¶y9ÎЉdyø?âŠmÕ É5¢žášô ÀRztÞö¾†­YzÜiQßQãôNcÜaíø¶í+$ðâãïbâÆçø~\#Ù<«ƒ7ßMÈÙûY³:M8.•aÖ;Qý¦ôøÄ³ÂÙ{ÙÔwåX¹é™Ûò bÊÔ`]óv„dQõO‰XìÔ³ ¤¨ZÐUÌß'K’>ÿƒJ•¬ÒJ2P´íèhè‘]É dÍÈIøv ‡œÐµE‰ožÌ¾õ¾Ä¶™+Ñ‘upWø*òн'g‰b[újÃÿ¾ÿåš.H¯ÕÁBQIfgÕqz"À·¤¦£BK4xq›i§g;À+ùĦì Èfœ3g12%9‚Üÿ?þ/Ø%6¡ÏF)t±&Vï¯Ø¨Lþʶœ¤«î¿À“.Fô^X=ž®!j.Bè׌z„!ý®déFeÈ;`J’ h\ÉI4üáŹlw;NeŸmI…¼ãJäUÔFE`õ2zèé{d* )¯Q›¬·Ÿ5’~ E;ɺàÅäà&Ýg'~¼Ÿ…׸ÁwŸÈ}ùÚî¡#2çéÁz ü%Pþ——6ˆ9ã‚®X0—hÊGËÛbô¶J®¬á~æ›*2U÷ͨNæ*\ûýdÆmc¼«Ì(ˆã×¹EP9£l¢þ÷ðÒu"7`žìR²"÷%œý0NÝHŠ‚Ùejäª9?©—MÀ7UÂôvÅ-ì6ºïå<5Žãîý ¹71×Î þ ¹}ñ´÷Çw|ërŽšÝngx?gžk1ñ ½qÎÞYÏÇûáNt—Ä"QsÚqy»¡2>m£¸ÞˆÂ¥F;òÔæ>þw ‹ÏQþ‚v¼&íE¬dfÑ£T "{¼ò…û3»áx©!e[í1o+â×ääé\ ®Ö–Ux­Y–®OŽÁéBÒd£O-k·O‘•ë¤Û“~¡K¥ qØ)N?÷Oàþ¦NCw¢jÒTšDÚ!Cv¸îåb´"ev¦9Rí³;@¬‡–Ò›b¹L}™ jq =ŸX¨û“¢µë©ÇÍÄÔâ=ÛùÏZ×kfèSù¬ñϤ’G|éµ3½ØVäÎnÐó¦1ŠôMcfŸÕ ·7KP¦ë/»õR#ö'Òë)„ë”H̓ù•1䤯e2·Ã™„(~ÁÂê—hߵí£µe#ìÂe÷RU¿r<øŽ±º6—ñóŠH°:’â¬Ñו%'%/3ý~[L*mVø ö°ß˜2Ñ|*dæý‹øG—֎IJC¯ Q2w:µS2ªæ«;¡W2f À¡g¸·¯–±Y>@ÝûgÑ/‰èz[–~?6 7©†SÈÒ™ßQÿËG)ÖcLW0DýÔq:dÚÉs¶ëátL,•$_k÷—9ïö°iØ’Îzþe*ñ_±c;{ÿAüýþÚ~÷1m‡ë^`<+€Z”l!¾Ç~² Ož`e‡)ûRÂ|*Nƒ;?$h„OõÛ£@†¾· WÇ/|·ø:VÛ† M¿9§ìäSú'æÝ¸®=©Õ„­[¨÷Ývê ê+‘ƒ¯_ø b–%.W@~¹8Ù¡Ä´¾Z¨Ó¢q )ýb­¼¬J-®Ý‡Š¢:XjêÂ(ø›à¿Š|æã©~&HÞ®æ§ E@ñð\6}Ì’Ïb[µ$v@ãx/›fó+\™ã…ý8”¶¬öOs.LbyR&ð«6s´›^•¹Íàþ·‚‡Q*²”m=1®ëyŸ`k¿)iäJ;¤­ixû&F÷C±H‡˜ zyÊ3sk–lßË]]Ù-ZzÌ‹bO²{áj²)P’s,¨ëârFã28èIK ‰ÛÔÈØ2  “¿ƒÙ¶ÀIÝ÷Xž+J[žÎ½)@rÃ@Ö:S‘{QÁ4[YÜàåD€Wƒˆ6¤ÃL÷Ílñ9~nÈ7wÂEo¼æ;¡ý_¡ÎŒÿ¡à'Mò6öѸ—1¦ÖíX¸m\³ü¥C®bäßÒŒ ü5×ó éLOƒg³¯AàñD\pŸŸhl``dô¦š÷zÍÑaºýâ›Ö=V¡šÏe™œ­ï0~º¦ìé€ÄÞHfšÄ ¸šƒË6ÜÇá¹|`´J‹»n›ܵgŠ×ðà€Ù\:½Ã€) Ä|WØ0·FBdÚO?¡mG`Wò¨^u…õoâÓù:d÷µU˜ŽE/.³K'ÓÅÔq_¤6Á·<`Ò7x€~õ,z[Òž–]uƒí¡ftJISÆÞÇ”Ç# Qv:h°Þ K^?A!{ô~†¼ HYNLùø¡ú-d7;Ãñ\95®»yˆS†žPz¢{.BäR+rSë.„?=û3ªÙ?cì{~rP¸+~L%ƒc|PvJ”ËY÷fûÌ„Ýð%¶‚óîé-Îèìw\Ùô©(ù=…•‰Q ‹¾[ÑÓÛt(Ç<¶½`"1:Cñà' 74CAw­]ã›Á˜~냓s×âCÓb6º¿šñ{±¦ÎÆQè¨ÿ0:¢!€ež ãþoо¨";fýS Q‹Æ0ÝBÑT|Ëf„ÿ/k"<ˆþµÁøxU³åž6þ)v§ÿcèýºÔZ‹ <媟T ›†ŠXõ— ì¢Å LÎsèŸ-Š&Š  -k { ½(šõ}âˆ]Ò#p±>š­OêÃî¶Ed'«†å]ì#UR÷»V$°®€òõ-°éfTÙoÄÆ¤¶à“£BäzÓF,³SÃßv{‰ªÛ# :1—^ǵܺ“ÏqÖwOH¿9Äé²êã éPÄÌŒâ#ÛnžÀïýNNÌ)„,¨Ãuoñ¡o6®™ò·H %c·É«…Ý §Îƒ‹kn‘{Öüôx”§£˜öj'œ9ÆKMäÎ’nçåä!;Ÿ**ÈÐ…E?˜³ÝËi ÏMNŒþ[&äâIÊcsÏ!×ÏD!Î\²/üÔÎÿBVxÑ)ÖXSrWëúÑ«÷bèYo’nòÎ3¸–?…¸ xãÁËNu:wÅÇW¹âô6ÿ ¦Úô豿8º»”ƒ×í÷¡¤ØÕœõƒ'lœízΘìj/äºÇRvÛ=H:¾ƒÒ߂͕À ü¼Þtã\[":ÙŒ½¸Â˜þH¥ü{.0ËוÀà™ ~{ç©“û×'“Mº×³™>7NGŸæ Tj̾p—Þ`Ý®\e‡”~±Ç#¤éÅ:ö©y q¾ïNS­·±³ôÃV”ùtŒ† M#¯ZÈ«‘ü=ŠDØEúDñ¢N?'úšÍ!ŸOj>âqªo^5 ïÄ×Ñ®7Ú÷l5˜5MÁ¹°þ‚mSPb_ {ÐgÔè—¤›Ä†/›¬ñ½Â™ÛQŽO%&¶KÀ½ãTX„RmÛ`ÖAjÊ«JvôÅMÔ¿ô‘;Ì€‡dÞ àò(«ÌõÁçàuƒ‹:‚ÙÖ]ß8k¯ráPM4<8ÃdJƃK§ͳդõ2ìÕL#ô: G5òcáÝ”&&Éü2N}k×Cª¥W*E&•‰!F]âÔ£àsv3›O„Î{*Àø «Ó¢ ¢úÉžVÔÄÂA¨HÂ3L%¬,—%Aû€VJ<¼„âÚTrÇ ªéxØ{7çN·6½q! n[ÅC²~+\|IÙ³Hù ­ê»5¡ÿæ\Ž`'Å„Ao‰ÐlÁT¿qã'þäoí&aÊÑÄäi=›ŠNéÔÃL‰˜í+;I–ZÛÊð_A®pŒTS@õÃô胃,'å&d+øza±Ì‚YçÝñ¸s  F#STÁ¤ñ4b\Î%|>+ W›çÂøqtލ&­:údý3ñ¸À|ÒϧÈ:*rØw|$O׃ûâ¶*ðÔ/‚Å¿˜ám øMï _‚o_ÚUQ¬ž·58Ƀ;ñ»é(gÁ43ܼY’8«šàÓ\y²£ä0ÙßjH4ƒ÷’”©âøçéÁ‰ûŸzåìªk ÒU’=/L´ß^"CËÈæ¼nt)­¥9 U³œÎØðN”ûe‰åب=Ó1¨yÇ‹G½ ÿUªÕ–ÏÙ·¢ÔöÂæ‚™ÐôÝÈôBÔ›µT†ob‹j&šg*’³O1%sÙK:¡¥¤¯vwã~”£®Gè[%ª¦þM)¡·©; ;/»—*¦•;`t¶7Ñi¼Ž­Í ½y oµB¹SÔ_<˜íŒ†ªS‚t¿W »ˆ›DO»(Оáû'Œ¶†O¢Ö™8³ÿ:¤xŒ†ÿÍÿ2'_‚äª ¸B¢›}±÷4.\ržÖ¨>`6œXJf¯ë%wÇã%§"ì8,E8æaä?‰,b6Íg¬ŸèQAWj~ šæ½DÝ$'Zy_‚HKkÑÛ©ôðM/fÅæ!f·€?læ©Ó®Ãa±£œšŽpV’ÿ>;X‰açÛñ—hY¼ÀÚ™ìRiqâõ˘J–̓'‚#¹:”:8*)TBªuσñ¥­T¡q=½s¬ßa~ÎÈ!¯ú¢Ø±m#pHôº–Ϥ7OšýÉã½sk*Žxjà*ïzfþ€:%¤IÝ„ì‰3¯ÑDý^{À,x³䢯Bœ£#c¸“¹¾¶-ig°“!Z®Dñç ¼¨»Ø•l¸LJ,8:ݪP}Îú©¬¢×oÙÓî]<äbße Þ#ܲ­4-˜Ëôé+Qr·U”™Ñ-C†ôÈßKEì{µ¬= £ø§Ö‘ÓÀ›Gf:<äÌÖ] I5•ôè–Ðù]Ÿn›‹·«,HÓŒ.I†Y,üŽý«ïÞbwAtîC µyÂx?Û\ÎÏÄ•ü-ÌÏÞŒ\v•ÙKqµæ*æ×@l’+Á•÷yi¨™-­v$ûå¨ÖÀî®wM0g±)O¢‰Y5£ÅìhÝ¥(t™F¶©pÙ›æðAEœ|•Ÿ!Ø×ª@:5ÿÇg`á IcõÈ«®Bþ5oËàÍOfaïslNÉeÚd³&â¿¿$€-ìØ„BQ›ðºGk!ÒŽþ£Å¨LÇ5ô)àÙ¥¶mÝðªd+Ns/ûï¦Ü%tw\`tÞÉ¥4—Yºâ¾÷Ý„g€‘~Ú€/~¨‘àÊðpî”üvŸ_æÐ©;`nš)ã4¹—ý8•Î[ø/ážØ€¿.àâÓ-à+ÖU:hVv ñjBtßÙü5I¸uÆ,º•úC‘J Š ÛС«yì›c*¸Œ½œd]`]ò âÞI ^IÉêza7ɇŒ*”*^N-¨ÿâKä˜H7)tòeÔ•šñœ® ÝVj„³]v ”_%z÷0µEß°·-ÁÉh„óþ¾u´)‚œøTz¦VFGËa‰x ]y+™Ç|†õþéÌëâéüŸÛy}B¾€àÊôçùHÔv!n“”¡FÁZ¯3N’ÊŠ7°ç=4M±${j™üHEr}?]ö`}{Jše´Kp]W3¼è-À­—ÏÀŸqO$€Ð §ɧ‡wmÌz{˜Ól6´ç.VL«Ç iäó´glMI DG70“÷]"‘Џ† ž`>¨NDîm§5“ØÕ‹qžJ¢rKgs4éBºòQ ÍÒ§œdEê³ï).U×$+/݆EÒÈSU Z[1ëG+Ó·ãj²ad‹(¹:_œâ‚,(öí‡Då .èŽË==ðmå<ÛøBæYÛÜ¿}VÍ&`¢?ï¢êŠDÜž~EØc8ª¯úº”}—K_÷qZ•¹ÐÕ"I˜Vsºfë:X[}(lr‡¹‡+ ÷Âb’<3™ 9¥AÒ??ÅÉ©–¢€Û޽gšúŒéت¹Ôw&c½‚lé»=¡ÿ‹ÒAË|:h*(ÁtI ýs(ìa„·b‡dòÀñª"yÇ7‡6‹÷ÁQ»j`ÛŒéïûûÈóO%Ý—ÁÌË{ÊL½z„ìÞ@Ú¶Ù²_Þ Ðàjk²!]ŒØØµ0ézø³%˜“ö•°œ¬:÷äé Õ4ñQ7ÞZ«2þ¿Öd…„2jFyÌÿ"±“—–U¬FY…qÎæywN|d Ù[0çuGcÅ[²`OÖ¨q!!A€Ì¿ÝêÜ3Äž3‰Ž;4Tøq†ù»•…_®GÐf· Ê,BTÄeKÂðA9ø+ÇÎ 'ðç]Ùƒß>YÃd‹qýèñÁ÷,ô\)A¾sèp+ãBË$f~]7Vt¢€÷$Tµ”žÈÿ áÖNüѤBÆÎí„S«ßcàô6¨žù÷–3º¹Dÿ‹NiÓ„.id`ã 2Wª ¯jÄC>¯?›²uTèþ†Íú˜^¯ÃÞØq~õ­"#{ÿ@xÛ1:Ã@®߯›îêœYG¸/Œ"á+¢ùù~4hf-&[ÑrZ,-t+â¸]õ žë"Q|ÛBäy dñô%`µ]€~}=›þ.?‹«$ÅÑ)ÉŠíÙƒ’SO‘«¯\˜Í¸Ð$¤vTbé…ÎІµ¬´Ÿ)å—7 Q«K°ê•1·îµ™b¿…¾ÿ±’òp&ÿ¼Œ þqøÊÆjŸàZÅ ’m·7Q¿lfÁÕÿ‰G•èN››œ„%ðdɦõi,ÌJÉd2þ¬¡ë’Ø wÑlH•æ½¢|O®‚tD›~XG/7êÐt¼3öƒá¨|eä¬ùY®·>gÿ¦&3–—p’n€’¯½Z‘þyÁ@òÜÙÄl÷hO#Gïe"§e1]îóìVU¿v Dxþäìj:Gwi‘Ía‡nÙ'-IÜGà;ÉÿP·gº°K—ÜÂ"ߣøçu,n²¶W‚ò¶+ñ Í{Ïaàµÿ‰Î IøA%›ÿOd Âó7lLä8³EŠ6Ÿp¡¾®çàØ,Eòüø}l4ue9i:ÌÈk:4\dÇÒöÂÕÂE0$²šÖ™u㑹xETŸû^Ι9—pgZnñ®†»Ñ%ðbú-v!oš–œäÞXw…˜µ¤ÑØÚWÑC)<³3¿í•F}»/0çr1ìá[Ĺ;åwˆ+ó°@‰Dÿvæ.üåC|Ò£»®î£Žof£®|˜5ɟ͆ ¢´ª™o?üÙ²·fÜ‘ô¿¸–“ök/1â•þìõ•p¨n1SdòÅ2‹Y™²yTT h‚ÿ6šÉÀVÏܰSÍðTðè+O”Íjïb8™}U¦Â¡“†xª²†Ù8ÓŸ£}õ*ú. Ãß&Íü‰g…$©gf½¸ú¤é9m¨P¨B%‘P|í«OcRÒàÍs)¬/›†±aíµ%SRqŠB-çãìX¶$Ž¡â· N_M“%ã볌Ùvüèc£!µ])m(@ØáâØÍY„kqY÷LZX= ;0Åà8Ûsp º-Iöþœ Ù1êx½$áÕ)‚Ùo^°ºWq‹°=Ûxv]íGê®Áúì= {¥å&ôošŒÞ§³˜à 0Êá‡è ï¸vCx¯‰‘à«‚ž¤4x'8 ÝÜ3ÙŸŒÈ±^^*qì7ìáØpÏåèÂõ]*ÐÔÑåò š¿"ƒVånX7:ׯ*1€M‡^تâ†#Àïj7|JWÀNhXÖ€?Žg#õܯU.±WÞ³+.˜P×½óéÞµæ¤QsÜ÷ „㊋m›þ›Á¦;M¡öúYL+ºÕg1þ}"-ýÝTÇÂk-_ ¿Táøµ É>ý(ýÈÍ›&°ìC<ö¬Çý1á˱‰øO Ÿ QO%q Ÿ%Î;:À„Í-b“#Œ¨š6àñ-g˜w;o`良lin4&øáÏä'ø¥¸„wîÆ+‘4½É¿©¶sN0¹R š}ŬjôÚ9ŸììýÉt•†þJ r¦þlð“¢>©šäðꘫŒ{Þpéþ|&ú· ýðsü<¸Œú™|dhrÌ9‚#[pЀ—$ÃÎÍâ×x;0šŒta½òGØoE‡ÍñâP1•>o¯¨BÅχýË«öfCyûxMÕ'ì²0â“"iƒß…¾9ŽzÍšèÿ¯ËKY‘a˜ÑÊa%wØÐœ=.øï /ùk%„³¢æ€ž]*Nû;ˆÂ:¢`x†C59»ˆ·Ö¶br~íÏsrähá_ÕžD=Ž}Ħ¨ËÌLÛsL„å/P«¦ÎžÝ»çóæÇ7tºZ€I1bô“•/¾s·å€÷öAx±gÕX!HAUjÇÔ‰Ú¥,F•œ"GK†p¹«¹¼‰n²•ºùFš—¾+S Zј´'æhfF{Ê›q[  wß1ôÕ a‚/êÁ֎瘭'J̵ZaÉÅ͘õ(‹ãöç"8oP¢»”ûÑìÙ Ôsà›˜ùo^ù‘„6“ö%€û/}ÕZúñÊd•´ HèÇ4ñ¦±Ùê”Ò@9f'Oé \:¤Ï¶Th’]Þä<_%t™Õ@ô’/µ?7™Àˆ‡â¸V#B ¨”\Ó‹xìØ2jüw¢ÿͲ…1ÿ®jÿ6XÞ÷7KyñëŒx“.Ýö„½ðWŽÎJßëyĈ[q<ä‰Û‘¡|òëÆ<è[ñÔ$–³E÷Ç å¾)}¢†¬m·Qu9$œ?B~÷\BûFŒš¯Jºe¶£"÷)L=6®}ãcü”ÅHë[%¶M.FEÚh†[á¦P>úÜû:犎Ë_cõ1„_þ`SÆ VR©—ÛœlÅ8Ì `¨†³ß_iÃ@PËçáÿÍD³O#àþÆ>£×`Àò;3M™‡4V@^u1Ưë1SHr.ç¹üì±ÿ=ÿVsˆf6ýÈ ï{„«Òp¹{&ÇÔÑŸ¸yÉÐý YLà{Aö|ùbðtNÅÓ!gÁ|0ù¾ÃEKÇyÛè[ýK”™t“ÉÎgV=}Ê¿·á³½zD.ËŒ¸ !_˜ˆÄå›þZ™'Ž÷ ?ÊP¥—MÒ¥Þß°å7«ñL’,󤥆¸QÄZGÁ?Î\º²J‹Cq²{;ÿ›—>ö= ¾öùBõòÛ8/zS|¾r…æhä.Cé*Æ}ÞF(i؃֛·Ãí»á²}õËá(?`{ó. e"ÿ‰âHç«…+¶"ôS¶Üÿ·š$d<`ÒÍŠ1aûß±G×èQÅ×§°“.§Žà½)4Ê'ÜôHþ²Ÿänùƒ[ûjµÖ£l0=ÝÒ˜Aì%é÷à—ÍæøËfXçr 2»°ý×Ä™ÅþiÌâÛ`$ƒÖŒëÒ÷ÁPñ@ˆ,«>‰ir–Ôxëžj?Z2î4ÂpUQõ„CÍk¡=@ïÏ‚¬gzô–O ;,QÅ?¿àäJ’¸’–Õž¤9Õ§Pm‰5-ûJ<Ï1T/ÉZB?ªÓ,Èã(xhNðŸ]&÷Qž ëš|tËØß fã¿ ùü±i¾'»«ºÓK!ã™óäÚE\©ó–{/[‘6íßAg‹îBõæëìhÕl²ÀIlüøwlUë ÃsHŒÿñ“Ñ™¸r£y2ÿcËØžîc¶GUÂXn%:iÂ-¥)µ½÷~1»dÓÐåp"WeßF¸YæŸzÿξFŸmý¢ÚÂVö ³¯Õ¨ðPÙ6„U­ãç5 ì’5öB÷G|Q£FÔoFàš]BhX~\wÀ•åèí± Ÿ^C³z\n³ ÏÛ]AóÓ9S¾‰Oè¿RW`ˇLÐ穯)Ö´|æpÙýþ¾>GÖ [»&ÉìIÅɹ«‰á×hœqð:†fçÂÐÓ­ÌÏæ*öþ51Ð…‚ãí(ûU•¤׳ùO¤Øu|Bä»…XîN?9/²îëuTÈÅêïÅøÉh!gVj$pÖìÅ¥Ñl5›‚bÍ‘À»]¨<]“ÊÚ‰Õo¶ºâ VLDˆ}¾© ]jsqM´ZÕ-$»Òs8W—/…ÕJ1l_§=*A}w3‹N/NÇAzyŸ{è^ºó,óøåF¨ó⧇¶œ™¨ÿè+eŒò?l­žû°Nž2›òaýjPúyNÅ0k3S¡²õ~hðÜ©äØà ò©¬§ô ã7X:ãËRjù~«í¼­ǰk*PñÕ*(UšFÛdDi‘†1:'¸Ç@h©^åÌoáÅ»ïVÒòs‹Æß+ÅåWÑÿEä–›/L–*çžX<®]nK"5ŽÇ5kËHO©85;°›®ý¹ êãõ0ÓM[·–1S>sÍŠ‚\ú`¿ZÃûs˜|#•™=x_åaǃÀq_p oÜföžüÇLG‘SNrJŸãSïØ ü™ÎÀ׿ &…]å~ïöfϽ§µrƒâø×îd¦ƒÓIi"Þ“ÁN’×&µ±!xh+úÁuŽ(M=(Aæ¦Xa¥ÑÕZå%¸À[†ÚŸ ÇÌ{¦²ÅTþIÁX¬²ªr’!q}*˜wlÁ׿…ÈԳרKM/š9s`«kD/oİ»GÉñü©´eÚ>ú=9û•Ö‘ó/K¡ùX²V²ì›"mtLÝ ‚ sY‘êd˜Öá†Î;^3ˬƒÙר ÷l$ˆPÂ9F–3‡ª-fF1Z%Ÿ†<àd}º‚ë|™äÍüãïùLà—uâpÎŽ¯9±}ø‚®œ…ô©0¶k 4lZ ëÊ{øôY—UK‘§uýsÉò³Ìý´WhQøü³¡BqÅpíÑQ"”v“©^ïHâªòñ½©H [Çs;rÞ'ø×ŸÅ~žâ›·GÙ,~žG?ÔV`Ïq ¬Œƒ¸’˜rÄùì\À³Û‰w¬Á›“ÍèÌ/ÊX¸Å*ÔìÅßå5(ޝ‹êìñá !t!Ÿpþ/!²ô…5]¼É¾Q. _Ø¥ýdí/?fï—Ét[ÆlLüAqû½&Xæ‰Çä~¡³Qól·þÿ;“‹¿ ½p¥×=8’‘̆ä\¢^ùô¸În¬í{ÈlxåAW6àµ4gÆôæeT "°l£3ùVÄKÝòã«—nTyUÔÍí¼Í³<”æŸP¦q5ɂ庴ø©Qô–£^˜PWÌ q$Û¿!ÛöbŸ¸ÐI‡]°&ê;>ŠÎÕökœëóáѽÅè\ÿûÅ{PqrÖ½e×ZÓí—òiL³&Ä–jÑ)· h¦óZ›bŸàõ3B¿i¨A…£}5hŽ£çSX ÿƒ´©#cÎ(çãÙðC±kU7¬ÉÅ’t>þwçåd²}e>Žò¦gDéå'±Äšï,ù>CXo Z ë÷±ñÐÞ¼ŠyìoN:¥oa÷Öðœ¬NdM¥ï6Ý̶*f¥I«¿JŒæûBß|lÿ B¶.s'«%‰„œ:µÚJ^ËŽ_øqµÙ;›Cú‹Fðò-¸ú3”3ÓÈøZ¸åv"·Ügµq÷BÁ³Pï&(¦+?ÄM%·°ÑÕ…ÍžtùÓñîàcœ§ÇÏ哞ƜZ¨e·ì 4;èΔJÞ`£›oaâÝ·P;kÞ^íL}c¸À0ÂDØè>\Ééþñ~Idšw‹O|hºMŽþ#±ðµÚ »kà s!Øu*“yøŒß )Þ˜^_Ã}ª(?ÿ£gÜË:1}ŸžjÕd~kŸÃŸëLè‰ Íõ¹€2<¨ðÛ”9µF„NIS#«+4ðׇíà’kÍ\Q”¡ÕA_Ý#!p;ýø)²Ã…+úÃØë&ÉÜÙmCÞ^Ñ¥ËoD[>öØšßïŸÅ1#gŸ ¬{+7_m¾©rwÒk‡l!ŸÛÖÁj+uÖ³dÿZÌ%ΫàñÚJàÜùE÷W‚Q–'Lñì†rž~(»Pr÷‰Šˆ9-º> ;Ì™Tçå›Ð?ÍÓ1Ùu2y)ÊíáÙIﴊý‡ ;Ó.ì-ÿÿ×½×Þ ìª«IÔ¿s%¸~îbй“HŸ†3µw‹eƘ"OBîÒ\ †‹ÍéüTqZ²½‹Ù4| [®èd¹o|‡1ÙD»=õkßÆÚƒÜá‹uÜÒûñƒÖ,,·š†ï­x©üµHr¨?ÌÖ Ú i¢;ù#óÙ@ÄüÌÙ€:Š åáèiÅòïy†iSÚ`_™lT„h•S0k–9V8sðTŒ:i]6‰þ+ìÂ[Ž26žþ 8Eo ¿×"6cñZÉq8°góJi% L bs“=ÙezS麥öT÷©SÐZ†w+'â?zÙoÍaЧÁ†È­Ða6ìÀǶ§Y·ÖÛ¨+¹…¢îkëžÁÄhÈá‚Å•p¿Ù¿]˜ƒû…£ô‡زãU§‰$ý”9­ŠR$NöþôÔaê={ Žûa±øB QÉ´õÈ)ÇûðxÉV2zh3ñËö„œÓäÙ(ì@»{ë`ÕM*èD˜’0:£> ¿l&àšÃ:o¡–á‚ôÂw( ZMnÙ…BrÁYz*Öƒ¤/»“?á9#²YFJðY2U”¾ñ°£=Á793o¥áüUx‡{‰èGˆÂ†uœÚ¥ß'žÿÌׇÌ=PÇt ^hBõ#Kjß}qÁ±à2LŒN…3žÙ¸ãZ5,­8M63ÖŸc¦Üv䆇Gø (‰KuIsQv½§,N~øŒédÓ j˜ö9ŒÏÝç\¹“I§Ÿ)çÕ‚ýŠJ—bEoj»7»IùÃj0]óþ›Uø ‚ÚN¨÷ßl÷ó8\›Ø‚i;&“YWÀ0M—øÞœû„{- 5Þ+½áfÍÌœ¤ö ZÆ{øâ 2Ô)§‘Wg€²ª÷ðmš6Yh§Iû^ŒàX¯®†¹DuÓ"²– WÉOð?>9çg—"×Tƒäî]O wÜeŠëÀ¥)"lá¯ç(–—Ͼ1—µy—˜€Å%×`#§]Úcÿ›mƧnà×WεÚ÷`Rƒ*Õ2ªàÞY¦Q’¬×…¶È:)C÷gT3»öÂWÓéäè±2®ý³ã¬Q©ÃlLƒfÍ8ú`C0I­Å@ÁëT¢Nþ†ý‚€ápñq®—y€ÿÍCÏˇõFìN‹WìU}}XþÖ‹»ñ¤åV*z™ê·žœûîÉü]¤Ë8­:Ftoç€Å–ohGKµ‚é0ÿJð~±u_Là×N„SÉ;¸g|…¡*~,³bå7­$çgVCz:¾QU%aZç0P­Ýüö1߯t¨ã¤l(Íò§Qg{Ù·„`KvW]:’“/À#÷ 1úÙ: E‰Ý›RÏ|‘dE·Y“ùÐsÍC/ŵä¾Ð+ø,€Égxh½­$‘Ts¸ïáøtéôj7à ³l°9îO53ïž?‡ONÊ’H#?.3ËîzŠMÕì@E3ŠÇKàÂ#p+Ö\BÍ©¡Ï­ª“,ùÙSõ ~¯­x/IË¿Xqb£Ø!ïw¬øÜË ¾íÉDýk;ç°áñ¡hF¬HŸ u›]vËé­Ä)úö¸o›CoÝXIkWyÓ+b†ôŒO9¹oN¯'gG'QÓ€“¤eÕ6zýfM)Ù€ŸæŸ„G|ÝÀÖ|Å…3äYÐu¨¸¬rÒ[Á¼½+–M"¡•šdMB4=±ŒÎË¿¿àFârÚã†ÏÞl%Ò›pÀt>5 MbW\îó®?lc£=óôÜ/,oàâ^÷³¸Ç¬‚{ÿaÓŠp[ïM +Ó§‰¶(0&Jwö#"+#èÚ¬I´ÅU‹6-«ƒ—…ŠÜ“GìȹÑ%tË®¡ ü%†¥ø~¬Ë9J4Ò\jïr$°èifÞQòM„Ÿ38uãs/7ØáŒv!ÒàñO}SÀ _]?î~ò„µäËÔX¨°‘ÛñçühÚ4ûû îd.Z(²7}^ØüØkÇ1¸“¿3¾rÀA…ú]¶e~}°&°É"ì·Â’ä[þ :ÊFÙû^cPa3£™ÑU¦Ÿ=ç¬u`¤?Xqþ¼ a÷ЉRGíITúæ,Üxó¬éâ¡vcÒd™71›x<´Ü6x×cGÁ,gzŸ=F´«ÂAÇ*«ÓR¯[ÖÓ'TèoÌ2kb%g}o/”NÂmÓpv„ñZÿ‹[ºÈ‰ÏN@û-jT÷ ?ñŒ}ƒ.U2IW&ÃKã îó@=’?ð îÝîۮN÷.«€3FýñÏÌ=ˆÀ£ †gB![c 9òR™”U¥I‹ícòFcàôœ…0Ù5˜ .[Jkÿ.ÃéÞtÇŽä×m^º°k¿’¢þOg‚šJü®óDÿуx_ï e1-L´X’íp¶O(•¥²Ñ3èkЊ³²dhi£8¦ŒóŸ…ÿOÔØm@.¶ežhÑ"I>ú†ufÒÖ¤â¾ßþœW«Õɇó°]é y¯d4\|©ÝNj®I~ÛqÏ©¾d}&CxÂ\ºèYá­ÆÒÆhöøÍAà˜Ox“å,ÈÿJY¾ Ù‰þÿHh }×OªÕù÷0ô÷£ñ𲹆©Z@.X”âýï›èÜê¸ 9 –J‹S×?¾T;±»æDX7À5j5‹ ¢ŽS÷^]ºË‘CƒI9¿ô¥3× “}Û•‰É ïoÁ1i’Õ]J§5‹R_‰<û2‡sæ×¸ ƒC'¨ÿ›4w „Û²V‡~ÚGæí>‚GN¡™[Eêfþ´¥jfÚ”ÿ¥þžBîMd“§m£ §dˆÎ™“0Ötž$H:²cûséû±Vz{Þ&L:.D†Ü.Ã?ÍÃT(G—ž|ª žC÷ NJƒn>þkBÿêj솒ø·7ç׳¾x³ì9Œ¨Æ.¶½Ö\ ô» Àÿ_4ipš1‰Ò¤g—aD¥qöð[ØÛÒÅ·4è%cz`V!Î>·…›ÛÆORdã¦éü”Û¬ÉMž¾GãðË®t®\Ú¤B‚µäiýÙ§µîKhÔž9¨¸[¾XÎü‡‡ÅÎ3ÍÖY z.€“}ÀS;Ùý+ØpÖìíùÎí\·k0þ:s0b4T™ÀÑúYdç¼nð?-Æ s\À®Î’:eVfÁ…W׫›ÈE·[ÌØÞ<¨ñŸˆûéclè ü™¸"! BC|é³{†4å ¥ŽeœÿĈvö,ª ÖÈ:åÈ’!¨ÁÚ?¼à|Ù¬¦!u ›t½×ߤ–ùl•â$š²ß—â©d;{š&4}ËpGÒö«wzcí¿Óð»ÍŽ\ŸLŠCg,ÝÀWƒŸíí膵o@ÏÒ™oî‘`MÒ-Z‘gv,Qär¨UÛ(d>BVŽ ÂP]up=¸Oˆ5ÏUùI˜ðï^ìûËΙžŒÅ¿Æhý7ß#Ïv‹œA-†Šœ¿ŒWæ\vÖúˆðÑÖ3¢D¢6NêéÑØêÍ`4 Œ <(ÐîÈ,w™N|+ñÉRwb"âI·Of,Ìøàƒº)còVPŠœ«­†²;Ö°FZŠHnoƶ^jóüqE÷;,ýBwÿaÿ^ ƒÑ U(T!†o®d£TïCün{ŠØ³¨¿\âW ¥g§aÊþG¸§ý³£ë [¾÷ 8–†q.F0]s¯¡æÐs–àÕ£Ïa‰m=̽ ·Ž¬šÀoÚ÷@oˆûßüðZŽ?>®M„ʆÄó’3aÚ~1B.gðÕ†uŒúv:}šzϹi¼fd–ó%"¿K†ó°ÜšÞðÈ$N‹oìÅo p» ñJË¿3™p×^ƒDözÓ’r{º.М¹dö P}.gê~QZwq~µ= …ØãQŒçQú!£ ¼{6? ^±ÆÍÆRöØBn(Nòt cÁž´:Û ·*ÇÒ±~°—û.B³¨éjÚÂÇS7p[Ÿy¡2X;éCÌéyÔuõäºNùIJáÕtzItÏDýÏoþ`Ãì f¶ž ¡Fþ´vöa"Ù%I}3Ó Í9ð}Í$ºH$” ŽÖ3ËDiœµŒÐšxþ;Éʬۄàöï+lëÑÓp}ê_,k€Ù× ³&?õÄ¡ºt7Ì.é‘{ö`­ã ÃVO ¬EeÉ|’Rî?¸QAxK m‹84~ýØ'vïީ£É°ÜÓƒýÏj ¼`kš×¢öÛ«Ì¥¼°`ÝE,¸ïº >PÃf.4Å­îáLžûìLÕ}xãß)n ÏfŒˆe®¯y¥-blÜ?AÊ7ÈðÔ£¥ÚôFÜ(=5Œûq”EÞ—NhþZ’´·9Áš¨?°ä·0 \ †7¯¢ÛÂ8¸®Ý€òÄ ¥Þw"ÿ½È ç}›:v¬;ÊÚ³‰zé?Ü{»h²[¬W³/O(’¿…^DÓi =¢²‹V„ÿÁÏó¸Ð±ÿtî¥Ñû Ä* š§zÐïŸrñi»Œó z"Öú ’K¡ŽtS9³¿>„Ö€uÌ\D/,&k|Uç]™}K[qCº}ø òä^àWæÿUpÝbIo» ;NԳÅŸX³²3¬“¸Œl»÷/'’žÞ£äßi¾ÞÇâ`1ÆvE3çjhpqÀ¨uÚ$h¢s¥ÅN²uõ>ÃÝ#„Zõ·™¬Uyd‰­êDþÿ¨ôÀ²%3pß‘I8lŒáKÔ%‚ªøg}!†7^Â×ús¹ëwq–ä^„`ËAft¬ ?7]¦SûÂp‘ž*QÜ$?5Ï:éMdD>`¿Õä°‹Fθ ­@îÅWÌEÍ!è|¿g´ºX‹~ šñ,^ Fž%ËØMD€„Í»Ãy†1ÖÑžäåJ‘aóVè̈&ÏKg²êkmFiá%øßÒî5ÇHPD>œJªŽ”¢Sv¶œË÷oW¸½:Ö¾_†*çå°ò‰…Œ½Ä t3ðf‚Î`Âô'Œ«á=x™ÈC¹¶Ó&ò_-²Š;³2Í©‡æ~mܯÄ\sÀѥŰr[Š „É÷¾³OãÞƒóªïÌ®2Ò˜z€Â/cú®mê¦&±›yÁá|ATÑõÇP¥†ñ^lÈøzáÑ>?&®± ^$®Çó—Ovõ*ÄI2º{˜®ÛJ,S$˜«³¥àcù\òsþ0B7ϼMd– ó1kGk—Ð}øI¬-w:óyç_©?p yÖÔa­:.ó‹Ù`bA,³£AÈPä¬zaú½TFïn~Žæ™ØÿÒiw ìœl ¬#€b‚0áÍF§ä7¸Jì>‘å¶ Sðèz …÷û éA6è¿ÚÃf~ùÆd^x’o=ÙâF°qü´‰æ_ÇßÙhš ñèdbzÈ‰ÞØ½ÓûÃþøgœîgÖ yèßaHþÛÃùè”öïg»æãéwÑ`®¢B‡, ßÿ5´fâtww2ß! ož«fê—Yƒ‚Ê3îŒÇ×Ôć{üw`\‡/®ùÓ›+aÔ¢·Zo‡wy›A6öìYþNµrŠÃN“<éx¨ô÷ž ÄJß ýûÔÎÌߤ€iÉÜÿSv-æ¡¥Ò™öAƒ¶…Ùa½{NÖ±¾0©– Dƒ5˜|a1Õv‡€>²Xg 3¯6Ÿñ€¥°áÄsÆ”{Ž_GÃóד' ›3è·lY¥P*­Ê®å(ƒÚ²£,o,üº6á´ƒ¼¨t컩(%»vÁóèÚïr”1›‡æFøÛ€ƒï«uèz»í°UãÜ89_>‡ž÷Áÿf^– ‚çU;èÈŒ˜Ù]Ápex¶~Ývã2糉·s±ûK&‡¶×0[ÆP"o2W¥TöÂrrºþ1ûêSvüèA^‰çÜù‡:ÐÙþc᲌1gƒ!è‡ˇ¡ê÷H\;$A.ûw²Ï^ó`ŒÛl³óÁ—AOóïKq–¢-ü²‡H§x”}.Lö‹ðcß ¹®ò•}¸«&ŸÁŸ1ï?\{ïРb;,ž7ÑÿÏ*+Á²…òû ð´·ÁµÝÌs/.´ê áõN3ì;! 5.úä‚F ÷Øé'Pó®KLùȪýj¤%^…<ÎË„Hq ÐT)âN*}í’ÐGù;ãñ{'ºêb•W‹a6%Á|!ªZ¸va#„óœ‚Ç›ò˜òÅϘ7¶2'âÎr>]•kØË‹%™5øÚ¤°‚ßUaäe˳kËp5ýÜPÏZ‰ÞÆiUr"s½úF ¦6¯â$ý•c×þ˜É}'èù«¦SobÂÄË]‡o3zðä¯0”ie™ÎHºÛÉJ—o!WWèMÄÿ×Ñ»%Ûx°ƒvùÒµX±í;Ï_Q?¯£Ç‡HÚ‹K´I÷åßk3Î7Y<4¬GŽ;wÊ2\ê·–¼½y˜Ùºb!ü÷Ú¡Äo(#—ˆ3úïÀŒÛZ8”|™‡KÈÂU©DË4˜¹oz”©Îû‚Ÿ™=K|hôî»øÂi6†O‘¢7\‰Äµdò0J™j*"iû‚ûkÛ?,·þü›4ɦS#\ÿ=etÅ»9Äp‘"öÍO£Äõ_D—) héw3n¿gEý¹üò‡˜k›•áõŸ±ûQìµâù¸»a=.ÐÆ¤žpÂ{²ãÞc®Yh„Å—NïlIòû­"í[ä@<¼=âÕϘµŽÂäËÜAw—£Y ØrÂõ=¨O/ïœöbµhB~À»’«`süV~þí[Ðü~òμÄ,ñˆB;]zYf.ß—†öW;ñÊ”ùŒ5¾Áà‹—˜èႉüOM‰aŒÍMàª×K4»y’ª0ëO;vþ pðȶ®DSíøx_ÄcwŸÙ÷q%ø¢Ù4o «î2™ú^3™ &¥ÚŠM8áŠÞÞ7àòÍ—lphd܈-ªð·Žß¸Íè.Å'ÞM d½ å¶Hᘧª-Ð`› J*s™°ó{ÇÏ#C¶¬N‹¥ Øœq Ïy ÓŸ?°’w`ÕÀOÛTkùiÎù4½Ë!#gPÝܨ5F,íe…’¾™5LoR!»Îf6ëx}&xK ²îJpõV’sŽNðŠõn`ößB±à=¤±0¹$^…OŽAœª7D…üa*F×™¶#ˆÄcþ™F\ÖôUô4ðw¦Î9ŠAÎ[è“ ð}N}Ùó¥liöQ=XóÚf|_ ƒ‘”y @T©²^]+C×ʼnâGû%´óò*äoxÍ~K ÆCwÑ£ŸdùF`ÞÆ÷¬Ä“PnêqmšõNŸÜ+y§Ýý5Pku#ÆæË9æÞ柜¶šè÷éÞ3ÖØÚnÄŽ9zãÒÕXƒ´C˜Ñ¥HËJ…@õØ!t¯ ÊC­ÿíûL\ G°ûÀß ÿ¯ÿþzø1~‘~Êêj‡¤H¦vªqœ£@ Wè’þŸ‡'šxÍ<‚}ÿõ6n€’^&5ìÊÔÜÄúê=¤WIŠHüÂY¥iÐ0[ž$„„ZòÑ õܽäž@ ÜH‹‡¾¥Œò)2íó!¦ÂUˆì°¼ÏVÏŸ¥’´îÜN¤ ù…þä¸[=”UÇ×Á¡“ 9Û7.8…AMãëȰó^Úág¹ï qÇw.N!5q£Ì+åYDà[ÜÕ Å¹/!)Cúœ7ÜÜ<ö÷cñþ2¢àlB+µÆ½Íéõý/¿ï0|ÒÈõwÛ ~åðß^ËÕ¡°¤Þäy!¹‹«õ“°s±:ðî:ÿ3á–^8Dl¢[üE9aÎWA²»L=ô±Ïê ãÑžgɽ0æå’ÍäOÚ¸,äì¿'ÉXœ†W>’¥MŸ!ªb/˜ýŠU þ°Ì ¯ázÜÑ—…Pô®ßÌ ë»’«ýò0Ì‘"Ý⑘ìýy#Û+ƒv`{u»n Ê}Šá´T&Hs nòYX´2w¥9¢Ì_Ð#».lÇ“>ždoz!4Ì×q·tTr©ŸÀ_w1›3‹do£€»ÊuØÓUáž,N²߇o­ 8j:]a¢CNsbþgê6¾ÆCž3(¿9÷„˜,«3TÃίp£ôYN쪜ºF1êôT›?YÁžû•Á¬¶{QS°ë»–«žBèïYÔÙÃ^߃™Îò÷©­BµÖëëΡÿügãÉ”oè›ÄKÅoK¢þì³È8àbgÎ3Ô©F©°yW .0a7E«oB × tr§«˜{±‘‚Ì’ý45'ª"yÉÁÍQxh/³f÷(ûö{ÍDÿËú•‰§?À¹{„ádn$$DÌ£“gŠC­êwkn´b¢N8ÿô,¼§0 ¢É«6Ü;õLýíˆsÖÞcŒëål\ÜBÙ_K»Ø"Å¥$Þe.7drÚ:­À+â-“{W·Xá`T"\oÐ'çÅpÝÕqÞzÏU< Ìh>>„±ÕýÀ®Q¥Þß"mNå×âçýwàë¼'øç&/sè´:ÍíxÆ åÇÂð/5<âp[­È$UÃAY°ï()»ÛÍl9ñx‚bþÃÁ\ëùê>ÛÐÁ;ñp<(æ‰1#ÜÉìk÷Q¤h¢ÿå5ö°ç9ÝœF·xÈ]k¿úƒíœ6”\ÃlžÏCTŸíÀf+>vaÄ>hb™÷WyHï½_k›XÿjW»Ñ$èÎSD­wa}Ü”ø+@/Ëi¡@è ÇyýrR=4®qßêÐc¯–±¹›ƒ°~ËCv©Ò3p[æÃH0ä£,‹+kÀêÎmüù-˜+2× fÆ@LâˆMd“ÈîÊÄÃëàØ†lvû)10¨-à†où EZ·@¤£„Is­á&¿$ö#àå|~z׫‰ñ¸ÈËî¬a–ä¨á« ´MbÕ†0Õ( ¶¡ùï·Wﺂ`U ;©h;®Ëy}SðÒmÆÿÑrbµÞ]Wxe‡3·‚îa|ÔSV~’1(ô«f°4Pý¿}žuwžÁ׋ šO ÇDþ±ç«ÔHɉB¸ñØ€²‘ÉèÁ¬´ÞöJ‰ò.!š½~ÀóD‰NúcA-ÐÎLæŸIôâtéo_s*jÝNNM–§fÜ¡!0v ‡ÌÎጓ*$z­4X&·2EKšñÕfqЗ¦ì­"°ÜAW+Å0úì ü6¿U·àò²·Gžd‹ ¿„Oб YŽ HèªÃ ±ùïNxˆ…O#ˆ‡L¦ßNþFÞ,oôÏðeîâ3æ†Æbê\t 7š8’ zû€7ë21åV°33øÉÍ jV®E˜Ý;Èž¤hrʘ¸óð“?½d^” Nù¼‰ö[Ï îy‘ì”w¨uýisÁÛØ¾°Žb'Vs¾ª<÷@Îdý™? 8`­@ÍÒbm §'žN"â·¸éwÚ°@®›ÁCe¸ïˆ0ƒŸ—5zc1/]¾,ìæÍ$Tu11Õ$+·“y« i»‡wó“¨Ñê‹ÌšT_¢vðH·"HÈÅ¿ùfxß{Þ„þËl¹Í\}¸ DÊ#pë¾k8xr =1C‰lªdŒ¼{!ö[ö’õàÿD†„*v¡ÈÏÐó­›íüq‹q5[‹öç ˜=#àÒu—Ó«ÃK²^ œTŠÖ0íNѵ|û&äzÜÿF€’°‡(°WTDÁÌ£ûéݯŠê—ÈZ(–³ºGX—ëü´Òȧ6„³Ü›g€{S ¡eg‡wØ—ˆDÇá֮īCJWí'ï¶d‘Н'È£ËY[s5úØÚŽy¢¨OvÊÞe¯½ÉgÝOÍgTì{aeÇ+ãoACq¤Ôm¾´4™Ào¯iOuK®ãXnMÚCwúÜ2‚|:¨I·­ÂW{בžGï@³ã 9±@šŽ®7…é Îì‘ý–43*±VË˃Ö8Š)–p%÷H´âzU!Ú]¾ϋ܆ž6EHè¹ÃZ6ø¿[çÜR†Õ×°Ê6ëQzt²+Ìð>ÍiÓ¡ì%âÈv´˜“ëßkÞ\‘^ˆ¼F°m<ß¼RSÖ\‡I˜ü7× î‡yAå÷ ìßud¸L›A›(*>’F_Ã…«ì¥L(ÿ'‰Þ\^bMƒ¨ãÙ`*¥-ùâžø×¼Nb?¾M:ÔEv]éhA$xDpŸÌy"çù?JœBUÙ.쨉 ææVTÚ^™ºf«Òé œ!›¡Ò›’£™œëóhOQd._NGÇyu«Ò>š¬îL?Pƒy~tÒÈOöÃŒÅäx¶ 9}ç Ö”ýfyg*…jÌù’Ùdü}Ä/= Ë™ÆoVÓ/s—’“wN’­'|¡n³.eÅb™ûk°,V—xÿ¶@geužF§O=G¤fÕúÛDΙ‘$ÿx}/‚”,Ô¡×çé>Õºûf ðÍî)òY: [PÏ zèÛñ‰þ·bJ8cíñ ¯ýœ QâÓH§t0,œWȘJÀÁZzŠÌådë!¯w™¤]BäážÅ´=A¯¹„aÉÜY°:šî“м T1š!7ŒkäK£°CÌ>§ÎÂÍË‚qðfÓ,ƒùiœÎÎnýÂ-•Ò¡·ãèË)÷pDz\f¬ô :ÄANk±þŒv·ñ” …à÷oÅØjØýèWNb |lXÇ<(e~)c˲cŒUòMÈÿjCÃ{óÙU`®ôÞ&ð“wK˜Gg“ÍÉz ¯cKŽäN¡v®»ðïk38¹€‹£&øOéÔB(9½Œ‘ÌN^žÄm‡Qû²ÑøÕ—MðñGQt“Hƒÿf¢o,ü §M'Aí@2>tHÇŽæmT¦ø®P¿Šïžža_É3áj|$r×sV+x*I?‹;çý†®Ý|äþ¼Óh î»—ÛÓ½o3ÙN1(}–“"1ÒþævýcMú[`oxJðèQOeÊW=‚á»Y2³8yð=žç½7žÎ¤mÙd,0–uí7° w“G³¸ÑzÄ¢ëäC%¶'0ß mÒ¼P>dÆ!‹30bœkç.©žˆ¿ŸE ¸˜Šª¦[ÈCÕxlŽ4Ñy«@—½OB·šl~T ÁE+iø—Í0`˜‹ê¥?™õ› h\¸% –T¥}?þì§mF°úì=P“o“à-ä}W8ù]=Œ©sËe<åMA{ž'‡„üƒÛk¿âÕÝ2T`Ê_ftl]Ú¸™~ªÊ¥UïÓ`Kwºv¡õžXH™Í4?f—ߊ„­"›ÉœôdÐÒΠ‚GÇc«c «Øô6iBnÌ¥{ïñàÖ鯰ëÃT¦v†ÄYq‘ZówÙs€…w-s’g:SÕefæNàm«`Oȧ`k%BÝ©IÔÂQÿú¯¦Ç$Ž2­eèºS‰9vû%øÏ«„Ÿrj_×ç€ya/Täµ£§®l \‡£ç2¹3«Y&Ú¿^y”٢Ј;ì‚á¿ý¬}•ÄéöÔXÞ½ûW\‚‘멬DÁž}"Äg¥Ö~^áÛÐÙ +ÕèpÁë`ñí?%Áhk1k¦ªl'¢”[/´ª·F@»â0{àƒ#y§•ÊNìq8ËßÕÚŽ…X5*CƒúÔwž£©½¸Ù)˜(\žÍäÊ›’åÆá°ó—<ÙYN­"pNؼûõ(XÎ’§Ç§¼ÁãÅɧöt¬¸>”Åt¡üvYº»í);×ù7îØ I÷ zâÛ®Ä៳¡}!ÈÿíÀÔSÛ‰ÿµ¹PÄK;GУ5>´fé6ŽæTøÈû³?2ªúÒþ, rþà2µ.VÕe9Ê¿<1ÿTðö*»*-…©8ìB÷ZýÅp£aæ£Ð-F ñô¹ˆ ªÙwHKKÿÖ"º þ:nXœ}s-ƒá:ͺnU:0ÑáÌÐò$è9æ6nßôèH?}²÷êž7•{‰àûª4Þ‹CT‹ ¶¶Ä“RŸX©“o Ó™-¾š‡Š(3–ÇÔ0¦sÝ\D2w¤s¾T°l6žø¿9èûµ$}ïø­‰V‹ÜÙ9’DêîTšm¶Ÿj/ý5oÜIeU¶f±9ëçÃü yhšNßJ»¡F[5;ô· /OáA%‰þwÝÉ™9w, O…óÃÔkŸ~þQ*ç'‡YÉ–ä™H?Ø@7Í$§FRÕ-IX.´Œ^<3—ÊÎ ó_K“3!/‰ÜÇ.ªq2Ž®ç„ò tʦêœAt¼w@Û;yúœË!ÚjÊrý?áÂJ?:o¸ˆûm,– |„«òšd‰"½±c¡ó‹À×f!\PD+©vhÐÿEWCتÖ9”Ö­£eŸÄð¼h5õ˜ºŒL÷¼Ä W¢ñRaº™›I…âýàPÂM4»E\”ÎÎòÃL÷ +~÷…‡Hhýf€º÷š&ð'ô¬G«K\·í±ƒïŒ-Õ¢ Ú‘8ÂãJ$ætÁ´ŒF¼¼á J–^³‰rµÃ™ª$¿ô(â–# “¼7+5°µúʨxH™b+A¾œˆc×_ã נƃN¬”ß0£(‹á'× ½› ÛëgC3e¨l^'ÛáEÙc–¤Ï  › HÇEQ\ó°Æº'f>.îÝŠ¶YÝìeSZúüüЊ…‚ÓÉ ÛƒÏ|ì¨(Ÿ~Z@(;? nûËŠÎ%¼#NäQü x°ö ûÞõ7´X ŽYãìAô¶ò8w˜‹ëþ¾‡Á¸ÿýþiðŽr΀þt\›ŒQǸ¬æûØ}Z‹†ÍøÈLé]Á;̸ßäz˜ãl¸|NÂo&GAû@:Š,˂έv”ç]²¦꾘³u_µˆÖ–AÆGöTð—³ÃÝF´å¶1èÄÆ£Y×EÌ~‘†ÇB9 蟙³‰Œ]‰ð6l=Š…þc̱ð4¼ÐßñûS¡'Íz­˜C—…%Ÿ$Y É®W[OÒçßÅÆwÞàѧLæÍÖ"!ŽâÍ¢}ñ!¶Áz«mô¼Ó\&ݶ ¯æ —&^2¯Anm Š/R ‘áృÏÿvöG>¡Lxi?;=v#l8ՀѾŸá|Ì4ÉÚõçñ£ìS(>,BýkùéÇç- ôv~+`}o “bÝ…hvÁ†ÃvÁ¢×™ 8JµùRXO©PT*||=¾Èq‹¡‘{Uiü6úf…2Ñ !±i–dÕþPxQ9eAêQcØË߆¹ªh´ôíïÐF…ú›Ìga1ZtÝ¿{ÇBÚ3âµ] š«jkõê`·Y$®Ÿ¬LÓoÎ#™ŸÙ§¤;;þp;„B×Ö™ 0Èé„Ûw1WƒÀzv ˜×}Æ£?µ'ò¿v^#ËÕ£¦ZSɤ¦+TcÑ®W$b'^°©+>ÂRûhš:ï:±Ùð„˜Í ƒß2k³üa£OÝë×ÒIæÉÔs †”¥c‡3ˆðäqOÂù‚YFdh™¹I‹øë„ £éŠ×‚ý‹ÄHRûìWé»íæxéò Æ#g •[FoôÝ53rîÔpmÌþ38û‘"MÒÒc|gaeW²’\‚Õ5‰hÒ©Qû;¤Œ|çM¡=y'ÈávyJ<øàmñ!\îaGlÜ^‘’Oèzñ¿ÿ}f8 ެÍCF?Š q}å5´ýn1ü©ÿš.æà¤~Ð&újЬ`»n·«ÌW†B¸Ñ¥AîN)§Ž{¦RׂVön"]R+¤Aw«ÂeúX3dK"ÒÝ€{k3Û1u Ѷ)dæa2/6reÉmÇF|’s [/€’—7|½á>«¬84ËV‡±:š,§ˆ?gUü`Eï0nWLX[»lF­«CºÕè°© s×Âdë8z®»ñ‰ÆÄý/±Â‹ì»Er¨\žŠ^C¿Dª“U•L{|žÜ“ÃN•x`ãcûìsŵ‰D’å¸vß±¤%wu€ž‡Sjû‹ó+¼¶_¼ÊÒ…ùèk)J« þ0foäAˆyÁ-Þ¬D‚’•Io~ üÜ ö~ôÎnSÒ:½4"•™á¸aÎ5— ¾‘ øsyNF/ Iâ9N­ããhžq–6βäîÓN‡Ý|aÎ.M:+þ(U=ÁG>¬µAdX‘½‘Fmç#«~:ÒÀ‚F0Î7’jk).¿ÈN]5‹ª<1ø¿õˆëarýÁÿ~ÿe*¯<‰~åÍr¦\„‹ßå¨dh)Óø[•Xô•†§mØ¡æÆýòu=íZ$OëVgᤂµ,Lï/ËãXY„µ³ž øÆµtE6bú¥Ý…Ùë±Pê~ò²¦zǺpùïfògZ“›¸£ÎcüŒ&kš¹Ó‚¤¬ Æ.$…ß?"-“¢¾–Ó¨ç½9äÂúæVÀ œñ]”NYªˆÙP[=#">…Ÿ.ç½Á÷õ¿ÚR޵ß§ç4‚ø¥Y°`ÓòŸK´ß-!Â[yê†Ý(ýoè5ÓåÉÞkÜ×=TPA‰š‹†’óE}ÌÖëùì¯ÞËTàø"»{ޱþRÈý[óçóØ>˜kC¬·…RÝÝ)Xyé^â€?‹ùèöÈEൟ¼•é7Ee)ަËÃTˆA‹Y™z*.Ï'/ω€éߌYg0lM€v…JÂëäJFôbhfîÁ›-ƒ†dõý>n°4‰X§_ï¤mMäçYi’TW@ÏÞþ‹²ðë<'zL†Ð 呜¦U§ðú{ª­"2ÿyÉè™<®Ï͸¸5>‡õ.2€„édïú*™#Ãt4eÓˆù^4ýÈšùx3ym¥{ öoä#f™Oʰ%%eñb’ Îj†r¹+б¯þ{­òH89ü“Ïö5HÒ_‚´\i*¾®JM•ýˆÆGÚVÀB˜1ü;öÊZõèÔáËbº¬>Œž4°Ä— DÄ•þÕ¶̱€“j;Aûõ^ªë*Ñã½YÏM’õ©âeò3¨òãF2­æ9\x›·¬ñÇîmtoÁ–™7i|Ý<®­£»(¼4 5Ç‹&ø_;P†»«1«4Ð ?¼Ë[Q{æÀ§8}:mw03}N ö® ÷üûµõ³ÁÍ”…·…AvÁLL.pÓ©°£uæ÷âËïvø^íó{Ü;6°˜oœÍíÉ•´Nð[@{¯ ÒEÌA²ÚÈ&éuP9#½¹ S_R\fׯþ·óí?pǨ8Éê aŸæ8ž™èq/d«ƒ¼¾îì–;?صƒ1l2?=°Cœùذ —ýr§«—(P¥è&näªO0e…ÌܧN%¾î§mNï‘åBe8ÿX©jØØìDŸ ݘÀŸ±û0#s¸ÏØN'=yÃø³QÜ[¿cûécÈZÞCçËåà0éãUð‚”4é§'ªd,IUùP.jNêMd©]Ñkf}•3‰ˆNEÏqÎ>°„*çn`BäÐôl"žˆm”Q¡aÖ¹fS $Ý£E’©œñó÷Ûp³öuÚ}-˜¾1òÂÄ?ëaÚQqòÑð>h¬Æëa!DôÝe|ß|ŸSQ/‰F’Q°¹þ´=.`ÊçY³ Û¡ìÑN¤É Šá“÷eöwqšîfàר#~òldSæœ!‚’o©9o;”8Çÿoÿ3CI¸qmJ­óÕÇ8ÇÓ†: (ëœX:ûûvvéý¥ðJ"”äS¤Ãæ’ð=#KxW‘KÉç™Ek棒osh©:Ô[‹Ñg¯ïáÆÐƒŒç³mì1Û¬aJùsÎLÉ^k²˜g³æ€0¸:tÂòkˆúY3¢ñf{ÜãÔ¯„þÞÔÿPÀW‹öû0:/Áç:x$ÝÄìežcìÎh䫳à*²ÏMy DÑ >ÞnýÌ@ë:z3‰U5ÅîÛ§¡³3‹sß2™QÒ› ©¼ô܇(p²›³d—·ÿ=WŽW2 ü'øïôÅhº8qÕEA˜Þ7…ŽH’Ó3ˆþæRs)Zæ‰9’&liÊjH ž.ÞÖ¿é áxç€$mØmLw6LƒVêFnd‰Â¿§‹ðñÿ#ê»Ã¹úßÿm²÷ÌŠ¬ŠŒxû™Hš$Ñ’ í¥2²GVff2"+*áuî§"©Pi—”–J4´ËÏûw]_ŸË®ãå:çù8÷}?îÇãé>GÔlº£¡žytû) "—9p2Œn}€àžKÀõ²kÿ}”…΀/ ·à8ç!“Ë`ÚŒØsKç¶*RKy!0Ë7…eì¸o¾e¹ž^øê,!m¡Ð«ÛŒ§ 9(4]‹Ì6và ¾d¸ùiLë¼nØ–ú©Ÿ7G\ĉÆq!b‘pŸ™ŽRZÀ©D‚îV›4Á;ÌÒ™GËÃ1âÂ1|xÖŧ¿¡D§€åº9ÿ`!Õ>N]0㈱㜫§ƒ¹,^u1fv<í}ØmvKè–w.4H+÷ï9BÊ1¿Ó˜Ÿw–®4×È{?¢Iø&*ä; ËEWùë´ß_–FÇŸ³¢À+ö7ã:ÍWGm&l_Qy¹d»CWý¨§Šùê}™=S“ É–)tf×j€C׿¾AÇíðî 1¹ÕEªnO¥ -K¨å/O¢Æ›B×AÚWÛï}Æíëo@ãljø[¾í†SWð-D½†ytŽK:vn•BÉO"Tç„?éʤ׷è‘Éi“HgH:¤ÿ.‡Fš‡Wzå}¥™Dýz#QÚþn®¨‚½šUЫZMÕ>œÆ˜ÌctÆa;Q—aävÂdR»s—J±­hÀƒ%Ð –çòYF+f†#è‘ÕR t"¾¯õcÖ$hÅõ¯2ÑôSú ˜’­3•H¸úIfõMš¦ùEîÊS93ù%˜œÇaДÑcñøÇlÁÏe[#HÍÏá-û³Ð®¶¬Ûç“®el¸5“üà²ð«VŠì\F[6ÿåÎdKàþŒ5 ×Y›°¾? íw™è„aEc ¿ü,|w'“×cv¯}œm@ü™ÀÿµÛ åµ ØwËéßrÌáfCRÔ…Æ~´¸}_Åä4ÀëæH’¿5•FîhÂ}Šß!FÒ îžBÞäfõEÂ=õ}šÙ»ºà{´>µc5H¤|3ˆ’%Yq%økÃB2³1ÕÜ-ñVîŒ ¶¦™kWÒ-+SPáïOTkŠ…ïMî¸Àõ×±bm×lãp⸱}0Éö^c/qü¿ ã’ý­pvÿvë[NA@°œŽ¯‡‰ÝöÈn®Ao=¨h0 éJ£¤õpvÏ¢’…÷‘ŠÚvL_ÉOÿä)£¡ê«‰þ·zŽ=;öû³cös¬+¨Ákæï9ßZMAþ^iBÎr}uÊ E6¾Ûˆ?±Øœ$:_³ßµ#qæ-QªQ‹O›ßa„ƒLÙûy¸sˆ©-? ú£0TxŒ¾×‰†`žbF·¿cUÝápæècK¹•èÎTIÂ×ÝÁ3LɨÆöæþ8¬Cúw—3îÆ¤µ9Ž|½rdþÔ@öOÝ^zq•:ê5 ÑIwŒ ZòÑ97¢z^:øWåÀž‡¶î9*tΆ,èKµ§…·X¸X¶‘.ÌUÛ;x3s»¼Z…4טÀ¯f+É<¯D#¿ãèXö€mÕ y>ÄrU†s¡ þ1“¥sƒ ÑÏ©“žO¾‹œ1¥9Hn'‹3ç@ÇÖTlS *-ìoQàßõÇÐé©©¹¢H6y¦ÒJ•Íú^þtȇ39g'1ÏüáÙ¿¥)ÐÁnpí‡&2•imˆBþ'ûÙ›ÏÖ°†×S¸/v´B³·6åSh'ž%°ç[.vþÞLfÖUà --|§Cç%®áÚ(sö¹{ÙCŒžŸIá¹äκP¢îAS’qê]bø—³_6_ε"ÿ6DOðÝž¥œ9û’0là7³ûèk» þ§?ßiD6v*^öÃNóSvOŸNÕlMqÄΕ=Ù,HF†#A«Ý Þ§Øs‚ÚºÀ«zñÈÓA›ºØô\‡Ën.Jn‘fž°ñâYýmvDãìPÞü½À @/}¹?$7SÉp>õ%û! –H„3ÙQe8oÕM»yÝšä[†Ë^ÊÑ·?è9ÓC°xÝs˜4¿µã\€¯ÈÖœÎÇÑ»Êh:· ~Ÿˆ¿öKvh–/È]ÌA_GGÖè¡1´h=ãjÞ,N¾4ÜÆ;m›aåö´nÙöÚ›Qü”U…ñ]×g›!Ñæ)eÓ$´¨Lp$JrqO<¨ÑÃH­5¤åŸäˆG¬/9š©É= ¨Ú½2–ÿÂ’Â_øtî/¼=³2?…Rï•bD¥±q¸ÕÈ×K`è™|ĆR÷ëîDóJ2lCЗ˜Ã>’DnºÍÑøŠ‹ü¦ãì—ÙØFO¶HÁ)ãxz¾dˆ#td\§ÿ-À²ÍÅ(pm%êè%Œ÷ßÍt‰î 6ïÛ6êí¢8¡ÿ6ýÊ|ŽVAÃRò9­óf¯Ã#&±Û§R¤7µpÆîã™M¦"çÕYvJ°QßN†©Ø*¿ ¬Æëi• ÝòØô²ž±òçµ`õ×1¦{”—~¸Àý8¦é´ü¸±M¦G£°|-óDó'fþÓ¢ÏÔ8ìõµÁX¾ð&ežcÌ&mš³ÐaÖ4ÚÓ5Œ/Š ?5 ƦKÑ•·üÁNDGØéûwâ;7%V-ß ÷Ç80·;ë9÷‡æ3ÊOÁ|¾3ÿË1Ž€ìzp[¦Ç<}¦3‘ÿdËók™ Ö·¼döý gwùìŽäµxÉD›fìŸEùäÁ¥ i µYœ†a`Ég\{Æ.qs&šâÓØÁ溉S~/ û…v£ñŠ/ÜÁg"ô,j’ÿÖóø'×h2'j¢ i*´¤à5kubbÑ„+æwÀìÉ1àš$½Æ@ëPx¾_ÄUò• Þö|˜ñhôíá§=OÂmA˜?]òºpé: îá5­èýN‚¹2«?A'k6èÑ+Ÿ„Èžm aîÅGã½*›vú‚+1+N Ý=f¾‹îåkŸ5›@i£ný“BBðüèk¨JΡ†³û™†zI,ŸCäW¯¤ö[¬Ðóø{&iö*†[ÊKÍŽ]æðß5…ÌþØ´=[OqV'ßdW– Oà¯a5~σ Û×@—h^Éä¾¢ð& lžf!ï.y*f}„ü÷žhÍàXqLþ›5Þz+Ì~GcTB¾Ö­ÁM»*Ø¢Nkä¿ ’âää︽})­Wý\ê†Æ-Pz<‚·Gû¡N\À×Î6 ?„ Ùwµœ±'PZyÕý`r²sެlÆ0Ëcœÿþrvç~D›Ð…ôfm"2îÓak ~¾ôâ…Š!Hh=HÆöÃÓË×ðžjªLgJ®Þƶ‡kñhÛ(\¯é›*Z/³“‡ˆ¤q:^ZNà?®)‰c;$ñÆ~CÚôÅ`c)ÇTt¶sñùÇLv4$¶B²`¡Sâ³céɨ©ð áíÆ;ʃÛÝxåQ®ýÂU”×é6ØÿÆZ®ŒGß3ˆÞÆG8½ÊÔèç;,ýêL{¯÷0îMŌֲüzý[óIÜ—‚l<ƽq£z4Iu{ìÞÞ^QUP¶m:ÛtÆjN9Î ¬¿ÚÖubtÖµ^få¹Ð•"L’V8¢£ã"Û³JªÌWIEÈ-"œ)Öý¬ö'ì,™‰GøÁôU u65ƒY›øi×ÌçþG¨­y¹Ç¹fïañŽ}´k3±k[Á¼Ù·„Î=íO³ëI”CǸ/âR,Fj6Ü„eyö ÿKˆ”SG™òóŒ?kp¡+,Š•[ñ“R þñÜI¿Og¾Ìóù xžî"R…-ìÏW™à9Ê3E¨¼t%Þg9ÄY¨ßhNÁu÷3¡ð£:õR̼“È"¢˜÷LˆT7Hãúü=ÔXrT?™Åúˆœƒïbä‰âd,)JµzHÏØ5ÜT59rî§"§ÛÀ’08½^t¾gñý òl{"¤NÌÿlŽj笶q`ÏÆn#O„N@˶sÐÞ+ Ë_Wâ#…|%åýþà9S9'LÈž[F0$ö™uˆÖ¢#•à`½Q£–œÂ7W@šµ`•Þ„ã½µQè=àʼ[> Ÿq|Qñ†9!†”»×Cvë2_GãaåËjNŠT!SÖ½¥k™÷&Wà-Oóù„¤éêÔ]"º×™¢æ@d/=ÁçѳØÖ®¬tª1$Âoó~ø½Q‡®¯¾ƒñ¾rã9!¾{Säç0 ³ÑæöIä-Õ šÅîtìLšÍaheB*éûÛŒÍ+öá¨\›n'R¯Ÿpg,bÏòŠà€½ ƒ”5=¸s9P“L;(‡U³?ËŒ¼Õ%%VtTF îhÒŒ_òÖ@€»Ë1ª7/p†ž¤2źâ¤pîJ”ö‰Ž¥X>f÷¶oÂMßTÈü-pJÍ‘\GŸT=`ªÿ NèŸ Ýl[~ú{¸¢¤‰;ûùƒ¾XËK§¿ŠaD”Œè©Ž©D,k~Ëí±ËP¾v3àäL.认‰ªëiu@ ;ßß„][«9鋘ÔaKr0H§Ò½_ÔYÿtkFO΀ ½ÝÒ§R£aðœê‚_ ÷âm¡Lú0D¿>ÅÑwí³Ë˜þ‘)8|¥‚½’åɉǙ¥[¾²‚*\l6À:azkÑ3ö|jèU83ׇ‚ÿ`†]ÕcþaäÏ×ÀXÓC7¾gýÛìf˜Ãýoþ>U•@eCÞÙGØ5Aü·ãK$l=ü›Ë.IfSŸ–ÃNƒÓ ~[…:hסY¸ Yº~'b`hZµyé?m²óúWîÅWÙÊÍ 1¼[òˆCánøkýøGÂàåï ®µØØgØÊ^û =X1¹&œÙœÉevâ¡âì~_>Ƚ”…J/ÃÁ)ª_¿? €œƒUÊЇ¢D>Îû‘BÎÌšF&>ã[ëÑÎr†ÏqÊ—£[ÕvÎtëPÌåç¥;†“Ñ=(Љ(ñÆ»ä Á;ì6k¾F‰.²nU=kÛ®LmÍ?²ßâ&ê¿Úé»N4žVãO–AüìxtùyÍ) \Ÿ/î¥S¹[ì´:ôøIj<šÌM´Ã×™îÜTVì=úZÉw Lß¼hsΆvº}…² ,÷aZõž”ºšf ìÌG{cþá‘@=¢¸Ç’ÜÈd•³¡Ü1(…Ÿ‚>Ë3ìÓŒV÷àvntïkÜÆÇ”¿ðg}…ÏbG¶$}5)~V¨a¯Ø(¬;êH+3ô鱃† ’‚êùÅxªr;cZˆ¸EÈŠ)Ó¼øßu0)Ü^y®CÞhØÐ_;zàh‹$Uz´¯d(Nàß ü–É}´ŒOĪpMúøö"P•J#Î]0ç‰*÷ÍU rÁªN3×8“ž2.GæPÿ­¾xuã: Œ«‚œ'peÁw.ÇççŠ[4îü†¶fÔ¡‰ŸÌi(Â5¿àIàtÄ‘-º` í\øÉ¬Ø]tŸ9™ü /)óá6û¨£;sJNCÒâ6hâ¿Ãè„d’œ8Ùò:Õ³Øï_&sÇ I¬ß#PÐ?Ï>6ÿÉxDJ1¿/›Ð÷IŒYÐd¨NÍFÃ¥^øãÂ(†Zóânp§³õèæü pEò2dêòs‡×*Lðßðë£0ïðS¦u°ÉÝZF§@÷-DÇü…Lu®0•Xu€ò›ÕrïN ÁÁàDXõåðcfºu¿X b$“„õ»¥DJ5\Æ9ùw©õoüU˜Ë>>~š½;- 8"$«T“†%3ùñŠŒ‰Ú#注É”­Â év)õwqCÌ/ÜXÉGÖZP0ýžNRç›R-»\½È÷`P<•,µŽÕ†"&Ø>âWÕæ¿ø"Ú†TðFSÃ3¸Ýæ¤/…×v_ðfö|üPò.ZQ‹p.Š„÷À’z*¸ê óï•ÿÿ]ªÄ_öûq´n­*À9qä³Â*l3Æûæ'Yï-Äê'úžÿˆ<}†¤%F–ÖNá☌õɧÙÛª€ïÄ\Ò‘sŠª{B+^AL>µPv‚´S§àí]g¸qý Ö¸ÛâG6صø&lÝ}ˆ­«9FëúQL,…V2²hWIÆŽ|UùxüÈVâßÎaí Ãa|=àÙFžÚGRU_pâã¶áÂø `ì`ǯ{ïÝ)R[AËo<ƃ;¤(Oò²æV( Û ZütÓ·Pr¼²¾mZ`h9æË%À†{ýSé¼ ‚ ý ¼ËïÁûXð´¯…›_Ž£\Š=°‰9¶ˆ:?½ÈQ4$ÏÇZØê7ê$lŠ0{à¸&ýú£ó¿Ùiæ¼M.¥#UtŠÆJ:ßô+#ï`‚/\7á« 6ZƒúÓ’!lC4dõ¨ó¾Œí“ý &Ý üâJÔNÔ bâ8VßoAL΢9‹×…I“V/lø¦Em/XâÅKIŒÐžG8è•DGÿA¿{ÇÐYTŠzŸ^‚ «aþX5~u×dV®T ®Ú±ø€|½ômq+ö95p¨¥¡¢“-G™«Hù-†ò ýðC#}vÏ']wǯóç.ïyƒÛôt©,O·½vÄ Vbl]|•|ƒ•.ºÔâÓî¿…*Ü韗S{×jxW-Zx„&Î+D%1Ä×l”Ÿþ ͳlQîl9£ï1ˆzýJtçš"àÛeêI­ðoîút@ôþp!ا–Ý¢ü¿÷Äæàò¾FW4'k©‘ˆµááÊ:TÜ8Ê”ßß…ŽŒ0UÞ®DMyöC$pQ,ºäL£ýº¼ÿÿÑn H¯»µ8P€Õ©éÄLí/óà‰ àòÆDÿž alâqHÏ‹äqrÚ& l~àößÕÇÍÃ)XÚfǽ]­ÆÑ  tú2[ !BþtËЩ»Æð€Ÿ"ÅbWzrl9¦K*·!ãuù:¬7$ × Ðœ¢”>3+tÉ™MÇPĹ"–~†==(ôó3t‹[/MîùŒ(”ÇĆõµ³ïMøå7œoQànÛÄŒ©Ê¿bÇákËk®Oˆ½ÝÂvKÂï‹wÑ ôúÏ$šV>ø`®2 6éûGutcE8\Kbø¦#sX”^ 6'ôÌÐ$>ÅÖÄÓˆFï-a¼Ÿ´³ÏûaÚ1×¢zïá}Ú(8>zÄj¼bžd¾bw†ØÒánØd1“($°pVõ´Ž ^ßC—iùÏ%Høs-š=8ÈÌ(â³ïÂtq|- <ñ‡=ÏǧqA¹<ÛvY€¨î'ó~ëÓì .`zLƒ¤ã¥ù«páÉjfËÈ?4ãI›ÈÿQ³TèêgÏVrŒ eIþ@/ä§œ'ç5w‘9:¤ãÙ;¸»Êˆ&ðU£kõmèAö©ZãÜ‚Î7 àáƒítÿVeZ{å ^Q #‡>&rþì›FA2sýÀrÚÙ|ê–áŠd?Ì[ÿÌT i£âÈ«ø«Ò–Ï»g ²ü ³ìò;Å&$?•øµÃòGj4Äc=9X"Øb9” Eé–ιt™ßX°:¼5K(lJ¼‹‰?,0(H…(uä¢NÑf†…4œ^ŒÂ~ëhV`>ݤf€‹¬ê¤*aÔþL”ÁÂÿýÿo^[øzà*ã+XŠr*ÚÄL6×ÊåÑ“l{ÔsÈ1•ü¾ÑóvcÁ­TÜxn_Ž•ã+ýˆ?~<æÅ±*jÛÑÛ-kk\HÜÎ)˜A&ãOgWvÂ0ÊÊ4T‹j'dEÄAkæ"j”šÏ-QÈÁR=>o½…»4¶…»__Œ.ù©†J¡h77¤z2 Ù6æœ;‹[O!M«±Ç ›©Y¿‹X•<ÊJoO(äšHÓšüwpÔ[tlcåYU:¹ï£ö9AÄd‡)ÈŒ!bf@´Îñ’Ñq `àÑ<ÿPÛ~t‹—ÚÓÉ'k%ª'm@ßOnd ›6¹€&t'FÔ¿™§*uÁÊÒ÷˜lžNkÓè"žœ}Ý€a–E’‹ž1DxÎú­O„š›ì¡º†é°}§­É¼ ’§=ÇÏYÆÝ«û $"&ßÀÝÍûñe!ÏV-B5‹zn ¡'ÿÅÒÞÂõðd«5“"s—­¥'ÍráõŽ 6Š"{ß…Svùlz%0‹új¸Ñìk—ðdÄò€/^¸EбmìQ:ïÑBrÉÛ‰Ö}?±¦õ\Y²¤«–í0ÄôwI$ãóRð™¿h‚ÿnuMe†×¯ÇeÞÁX`'O\†úGZvF³E¼8Ï>‡dŸvk\×BÔjWÚ~L–®™j­ É\Gm±©Ûxî]’ó°hÆ;Ô™-¤'¾¦¢‰H ³3q>§ õ%ÖvƇrD¡9 £C`¡t\±JEƒ‡ô…°7ÖvdÁÁdSÖZæ*Û˜,FÃvú Å»­„c×Ö‘/Á²8El”/Šÿa¬j¬[ÍÞò»}<À>ÑÊÜ„÷Ý[±aD„f'Z19‹¶§à÷ªL°¾^ 6Żا… Ù§}ë&üŸù×@Vݲj&›×;BØ7§Š ôǬÚêJùÑ>;Kâ½ÚŽîrö ¯ü‹™`Kxž…NïmM÷Ò]9mÄwÇ$òÀ›Ë¬Ðƒ}• pÀà,<¼± xœØô‚ ™%LU‘µ+áV-´’ó} àg¥K‹&ÿâˆZžáÕ·°ROea®ã$ÂY;÷ìï÷¿o£âòÄf;~>™hF/† Aª—íŽ%¥Xž=RAt‰´`: ##B@Æg1¹ÉLiUà–‘-£v! îÇ`„yèÿÏu×À{©…lfÙFyêM¨Ù¨‡[þÎ&õòÊèa½‰¾|riò¢ºk¿ŽFõbÌ5ë ˜iO4kçRïP!êq,í^ôžùØ2átß^}¢X fÏ“Ð,ý>£`,COV¢ïå„ÈÕ¤MC¯ÜÆüø\¸ÔŽ£Ï8-{¿3u|éxw‘8¹;ZË\ë«»·¬p C ™¸12‹Hl™‰ÛËÕ±à’ 1¿}Üß×rû ‘ó³VeÑóÉÓp`ó "ùwÚó‘à%xýظo <ÒZvÖià‹¥¶øJ+vÂÿy}žG»Ÿä³6udúhð¤]týŹ”¿w>œ~iN¯š&:U"´æÉ=²gi‰Úô”NH6°HøDgFáj䧸#=f7¶5­¤ù4„Ô¸^¥Óû¬yìɱ|cj{6ƒúfK‘poWr4âݰJ—–ÿz ÏZ ±t—)‘}üd™‘Slé›;¬Dð\±G—Ù8B—DUPÝJBþ›Ã¾ö' Ê~Qí—3É,I\å°+ב«¯©Ìe¢Ö1Þþ3™C´n²:ù-­‹-óÀ÷Úeè.¿HÍOáHïЛàÿm-øqLîÎ,Å;#zÞLn+³¤Q=žTã°ÔÜ?¡äCÃ\º”6CÚ†“ìXD = ?óóÒÎÐK ÙÅɇ7!gòÒ`Û§J…nûá*žPܹä:Î>qï>¯|Àf{9¸ž9OÏ=:_zó㑺ª&}‚ÿVØ…M­×uIçúµtFKM—(ÃWæoÑ¿o½ëQ…%üƒôë{`'÷Õâ)úÒžÆHeÐ3­ìiÏݸº9 ßÝeEŒ¶Âš¨·ì$Ÿ”þ÷?Ìtæœ>dÀDŠJ›"VÊö*ÒÍA;»»iHþ»z3•›JFí†ÿÞ3-ºB×Ãsb0;”­£ÔŠm¹f¸Ø{‹8 jVÃhé G¬÷áÓWY´^q&-Ë:‡ Xé‹¶ìtÊi±0UÚ ÷F„‚þ±ç8kýŒzåIÆ|^âÛk'ð?~)KÃ^÷1çųpõX<¬i$µ“‰±\(÷ÂýbN¦Q U ¶+~7±H2ƒtº¤Á“´i ¾P¯ÜÉ‚9]°÷<‡¼R½_ç¡7ÅéŽÏͬݚ‡è½! $þ<Æ?‹ßq/¯>Cªÿ! ^uAu¹ úGüáúù˜Âü³!SΞB{p·ú/¼sÿ¬\½ŸzÇP¿~Ið i&uŒ!®~¿ˆvÊ,¡ªªß1xª(ø^)geR_aŠj‰h¢Š7S1QÛ€<^{Ö'ëí—9¦éßaHÚi¡ñÓlrïønvw±õÿug&C€èvvö¹wäe[»þë¯шbŒIYý‰6Œ~ÀŒÄµÔCóû°æðÖŸ¶ûp6èÓ}ÂdA¡¹þ0‘zs'“e³-qzChKVµ(ñi#1v0ëU¨Â±õôž{8éi…æBT¹æºÚ;ã¶¼•¤ \¸uüôÑ+²FJ—Þ:ÏO=nò¡B5¢1°žídÖΫƒÇöad•ÉE®äólè‹ë§¹šädá'z‡§ƒÄ¯_BÍ–QÑ?|ã÷«-ÝQ·†8¯ÊÀ¯†'ØJÛ“¤Ø[‚kýS /ÿ5¢ š#úoCZ¼½MMÆ$ý¯µž¬¶÷›™?Ëôáo6±üCÿÊžA¸{„¡ÜItÁœc(”E+o…Ó¦ŠtýaL\ƒÑÏǰ¤O‚|=]u^þôÄÞ\ÓÀKR~œ%¢0δâl6?$AÃ2ôŸ=›—Œ+Ž›ÒÛÕé5!¤Qðwsï*Ò­ù¦edw˜äïgL6‘öAúbÃ<¾³£ÙI1Œ&çãÒx𙣥¹ì_6—Y“¥HÿbKœ³W,?ú€ùŽoœÆÉ;iX{&ûÍp*~‰dlÒgLè¿g{âÀSñ5û{VŽW@ SÿÎ m;„¹æJgQÈw?‰oÚBãÞÍ%_w–²÷Fjqk‡9éÿœ e¹ø¤_–h?|ϼFÀVÇïÍy‰i•ŒØ¬EÀc×ÅØ?³ä@“7MÞBol»Oø.3]:¦ùaƒŽ¹s¶… ¥aW˜órŽØ¾G/¹Ò>%HïÅ£àë· í˜ ?{áse>]OaGK̃kP»è„6Yé}[W1‰ ôg” †8ôÁ!¿Í4nœ»ï1ƒsû¢È¶~!\.u„ý}‚jTèàÁÆÚ‰ø—é0 “Ö2ßF~!s©ÓîÃGßÓ8Ëì$¬Ùý^ð…É‹òpGv :ÎÙÉ&÷Cø¦Q6í×Fü^+†,ýW]èF‘™ls[ ^^ZκlÕ¥‚“~³–Ö¢ðRr~R„=ïBáÐ7W6Ž)ÄiÙ]œÙº¤¢`*Žþô܇¼t¡Ø÷¾q`– C–ÜÄ×ïzYžá?ha‘ 5ùq46)‡<¿³¼_ì`ÉÚÓðØ|.}vù3:¼‹cï¼`Öjmg>xR79„‚Õô‘´0ŒÊo‚tk¢ÚÍ~06‡0¾‹xÈbd‚ÿNïB½|<)Š á¦æv±i}úÈôìç¶×'C±úWJ²–Ø“'Û+ñÊ[#XÙ…3£€\;&B‡›7yÉÍ+·pÛö­$»þ<*u¯ÇßOºñ—Ïb8hɼ×|ÁÌâ±§² R´Ee˜©߃2´ õªÖ´¬7¡ ÅŒÝî7øP˜x+Ó–Muéä9Ï#ðm7$Êå˜úêT:í`VÜJa¦þ5…11¢µ´ûs¶âÒc1hïï†sö€Ù  ãfK·‰ã}i-Ödr#w‹â~® }Å{üþor"ÝK—ÓøZ9úEr7:¿ÏÀ£a>ÜïŸDˆçÇj:/ø$K¯¡ŸLtÀÅ$,VXË;Á´¥Ú´æ‚ý¥fC v$ËN.¦·2;hìz_ê#D¾nà¬õÓ¢Y¸èpÍ”*Ø>¦—-sQááWpùzŒ”-E£é·|5òñœõp•À©ï$Éé;ÛÐ}l7«*.Îì ¡râJ„ºFÐó[ØW™•>‘T'§›Èúgâhµúþú œ{{>à°9^ʼ“ k¦Lƒy”žÇ "½á@ôeÂ@ø I¾‘È^»è€Ò !\lí_s ôd n+ÕÑpg?ûê‹Ô¾”y~0ªtóéd'=ÒÓžÇÊvݦ­{îAçõAfE2/ùt‰!k]V¡Õ_qš5;ìæ*ÐK±÷Ðzah¯‡Iö†àÝ<§¥ù[5 ) ¿"ë!ãæFL/æ§ïùíˆOØi²»[ˆþ¶‚ð¾SÀ%7ã{6ÆÜN‡†s6@W· þµ& œFcõ”­^ÏùwÎŒ:PŸèv=·ñ®‚'xÅi¡ëüoÌ¡ñžcy–‡½|γœÖÁ;¡Ul·vmi™;M•Þ[Ç©÷RD™çÓißhr´#²% äSµ S"UŠº >smÆõLÅv}ríÖ<ÑãrSôÜš/×q1Ï÷$×øËTÒ5]“„÷îÅ=£Z°7ð+ð ­!¾WÑó­:MR½‚êEèóŒ—ì| —B¾£ýžV|Q ¦16 Ÿ¾ŠÏÛ wâßàŽ§<Ø5tŸXß¿Ebð\ž—z‰wAQ,iÅí«HÌè;x½7 ±ÐÚî6¯êÉÚÒÑÿÅ¿U%†óåJcÀ7ˆ'ÿD“¡XSª4z•côr&Fð>‚CV¬ÜÃ!œ¯DŽx§“ÎòÙdG˜$ÑKާ:t›®&ón᜷7qp:?£p>—&F>€Ûf£íuüð>g‘Ĉµäíþç0oߦë©û}æ"šz¯šŽGÒ®¸Y>{ó½ î/½M²ßÁ嵋 G­ óß ä®kÌÙÅaváB²Éù\çÓg‘Õ—7ÑÏK0ß'Dš qnÛÇD,Û@b‘Èîº~ÈURc3"aš´;M¿nF{HOèÿOê'ð½¦]g)‰Bηád•MrÁÛŽút>•£yþâ×–T”¶3cW|=ŽÎìέ’hªkóŠ•ÿ} Ã:µÈ¾PðšÂvLîÂZË´I®†Ã2êĦ«]÷ÁýKêH²sÀíÞ%Xa¸Úî˼³·¹†k°‰lR÷§£]ó¡òn)v 6ëûay~Ùr7š?ÿŽ1û}˜y±Üþ¢X¦ún0Èm¼†©[æb—·Ùñr¬“éà·!®'¼Y¥BîmêbzùÐê3!%‰–/ñwÅ_1½iÔåõdÌžˆÿÂÕq?ìý”‡Í}à‡òðTFÓ†QÎïnø•Ëâ¹ÄIôþ»j¼º‹éTÐ%35„a›´óÉü$1]ÂO&OögkFr §à;3Ýç;>Øe¢[:›`5î[ê·©PùÕÉ€7Ñç;ãÀy²ÝtµV¿ÿ‹='ÀüݦtÒ­÷¨¸Ø+n/¦Öë0nstéë÷{é¹¹ÙøÀ§ÊŦï?Ê Co ÇÓe»ø±ðÎA¢1z¿~Ûˆ:JÎh²ZŠdÜuÕÄ4üÇHÀs…púÉ}- KXR³ðP²Aff·_€Ý´g¢þC‹`ÐX'¶@q¢ 0éf¶ùÆA£o†ÔĹý•0ýüz5Zˆ6Î>‹_õÔP» “I'Ñ¡{©üO侈:`:™Ì4|ÊòæZ‘ì Þ°|y%ˆŸ[vkÑf§ŽiÕᎵÆÔ1{&süª]‘1îÏb‘kw¬±ýÇÖ¬· ßÇ)“[ÓæÓŽY"&ðÊ´IŊݘtuù2Yƒ^è7„?URtJü2²ùèk YÖÌÄûîa½L·âÉ· Ñv/º>)^sº0§‡-ñ `œÖr$õó˜Õ^çÐ~Q/ž: ~¿ü&ð_NtÝ”¹ì~ÝïŒÔ7>¦OOœ¹ÙÉ‘þ©Âfm¼Áx›æ0Ë/„´lê3f‹ó uµ7µwlb>|¬€ü/,çVB2ÏÐgÓ쾃\”R¶Bi“PÖåž{]öéù—ìÙSüôŸ‚,!`6I„Nž27weƒª$›°ü.þˆ| 'çΠ•).dûÉ6”¢Ë8ÓH¾Âèç©SMdr—,½Ûð•£jh‚:·Ãð•€ ¬T” ZB¯½1Œ.·â¶ S÷漓‡¸ÞhƒØ>óñÁþoè={&1ùô§Åhš˜.TŸÐ*³:Ø3-¹§J´¹³—ãÖ$Ò.Ä{Ÿ~àvLcM#öÂGà¥qâdNåè<úo^VDËŒT]˜ u­ØîÛÆ®7%;êqË®ã(¾Õ”)>”„ÒÆ„Lm#{|¼Ø™ö"àšäŠ–}ZðÎu…rú !m:w\¦³^,ùÜjÖÊÚo¼Û Ñ›­‰¸ûOßrxé6ÄŒ¨øÃ`V|­yÏÊMW¥‡ÔlZ £ä¹BšŽdß²<¨1•„“ibÌÅžßh^d;’£Zöˆ;£ŒÞ¼Âj´Ó±œfçÇóp|¯'©ßRZ(Í jQCðƯ‡EC0UúD<· ïŠOðöŠClm¬eþ…ÛW—Ò }‘¹ªH:!æ}7òöámsER‘Zϼ9Á”ëÌfwÏÃ|C‡m¨ß¡­TfßJ©êƒÖf—³Ÿ»AÔˆËúCTv¸‘µ l`¾ìS_¾©¨¡º˜S/*ð΀~·ê†»£rdõã½(¯œHn?˜‰6=°ãÚ}ø›’Omz,q䈶=,€÷¾àÍ+A²µ|3'êò]а5 ÷ x³üS8ÇUe¨;¦•&`À¤Iäc~FÚ»²N)¯ØâÓHÇl ’Z˜ˆd™Qþã2ÿ{&¯˜ÖÉ̼ÛèKx]?fWÝälæ?†ÂoJ¹/¾/ &Ú-÷´EH›¹Íï´Á j.Ê>;̇ZB$)Z’vH&׃Ï`xÒpWzk?/#K¶™¥ôá‚+ší ¼¿ÍÉ¥Ô+ z#¢7õršy¨îìµø¡í$]ýd§wø4ÜëLDt¤˜Ê/¨e$é{ž‚æ>\ì´6oJbÚ·±Vz'[üûÜàÁ“{ø!í:×z¼&ûƒøzøzC’ÏâªsݸüÀ…Î!æb´½¢û‚=Ú%”ÂÏèÿÍÿœÞªÕµCl˜Á3lS?ÁQ Œ½-A3’ðÔ^ä«eðüu5²ÿo(œî·[6¥œ\^l2 †']³ ‰Zì#¦Ïžd®ŸÉý`ßI+’…çYÐÉR¥+H2*5Ø“üµ.0uI3Ã8o®wsv°Œ|ŒO^«ÓCåòxó±ŽÝv¡&H*¹ˆóÖ|†ëýkaFÁB\jÙÎ\òàCì×#›†¶âŒ#7ب r®xO2΀ÁFÌì‹„¯—7AÏ}Ø<¶ %õÒ±ÀUO p¹L–Dí/ƒ×ZÇAT`:ý¸zõÄþ¿òeQ´žÙd§´Ä•±¾°šçϤǦ(ÂáYyìÇ:ÐmÆ•J³‰Ð S°oT¦•ÂðA®6WöKç‘dÖ®w…uõ[˜ù©‚ôødª:ª[ã¡çÕYvLL˜DW%Ì]ØØË…ÃkxÈ£­–0ø. í­®ÃÂe}ð“kºR£X5¥‰kŒæã\ü™z¾ŸâÇàf˜ŸáÎU»8=¾õ°çU&'Pl ¾(H¿ÊˆÃ;Xûn|m.ìp3ͳ"!°Ï…µ1Šb.ðÏA¯25Lå:ã[Ÿä¾eó?˜ð¿w·MÅÁ &Ûk*=,³˜t{ew=îö» GБ¾˜:Þc g® ÅÎððÄ|ÿ%BC–ø¡ª‘ s e(ïÕô}ÛÆ'6ö^xŒžé°åù0Š,Ï¥“Î1“²b¸W˜ŒÔíL¦fc'¦—çà“ÓúL—´xvô32'•¨ßûyä‹é ¨lõ!OwË¢¹¹é˜ñRnÆ¿µÏXÝg°#Ö~<¦ óÆû¡xÍŽKC‹…£ ¨=s*cé,\ ýǰ¢ggéïX,U8Ëþh”by®ÁÀyH_+ÎÎ^hŒî{ÏÃî9RÐ1ç:óCzx‚ÿ³8GYÞî"ÆiËyîܶvjW8³üà6ôR\OºC‚Aß)ŒY*ÚMiðÄ×LÝÌøæÀÕ¤Iv–êb½\;ë)¢éþ/Ù/NÅà{Ï8r~<+v—bCwºû&ª²K…¬õèÆA2›Ø9­‡ù1_ÐVÏ÷6cVÎ×Ëð,Çí$cT”^†¤:î-Öy£×íï qÚ#š0ý¡ù°k÷f6.!5⎵xÕ]ÂíAuÍ7³úaý"TJ›ƒËsôIÑ•]8ºb³û]:û9Ã&Ÿ€ûC¼ôÎõV¶®-ªWNÄy¯ ²™:–ë¶¥2¤©Á‚òl Äí-ÿ¸ßø;àUÐR »ÌO7ZÎV‘èhÑÆyXÍG¯“ÀÔñ¯âUÌ‚›QhÊù–…ÖxÉé&,¯ÆÁÛÓeèåìÇÑ*|(W„Þ¿O¢‚¶»Ýø9pG¾ ý*4›>°úßœBù]K˜‰EØþ¯6ÑøÕJäoF3|z<‰ô*µ¢ãý^ܱʎθÉÚœÁc¯ç÷ÒL2;BÄòœÀº­ãË/Gµ S;Å@bæ£îX­JÂ~ŠÐµþSÉ[÷çPR©Æþ„È ý3Æað§‡${¾y&4©;“Úë÷1鹑nDòâèØHР»ÆDÇØŽFå>…&äuëehãaúÐgæ$L¼ÏÇêÏdh¶{>[>™|žå5÷x¡lIÞÌ\¦µn ”—ÄIRbw÷>WãàÉXwògÁ_X›¥ÎT¹£É§«øÑ*/+Â-Oþ1/š •ýÁƒ§…©?¿QBX*}þ¢Ê 6·)—틘6Ÿ`ù—"˜ôÊš(]sÃö“¾äâ Ø¥~ˆÚ˜Y“ï>|Ôð­& TÇo—qHæ¶ñéQ»«ÿÓ?§Ï‚ff)Žñ;AßÈü¦êb§íFòûGpqCâžœå1Z¬JÙ–³ý²¤l‘;[,ÉKÕü ã÷t•EZ–=d3¾Å¼¡p>À÷¨ƒ¿¯±<Íir©3YAåÁ Óóß…¬¨/¡ƒûg“m‰B®;µº¡%3çP“Í¿ǵØD·câxìï-ÃùRÚ$ÒH´Ýe}$Lú#÷’忱w°R.8Â49ºê`c2 Ï´™kÒ´%¦!gÌpT§¯‹ø•o%Z¯?BB7v0žCKÀ0©b¢ÿ Á™ÌDHr•#›Ï_¯yF¸È¬×M†_åVä™V"þÜÀCïŸ/æèñ¦Õr?lë+†Ç ïò¬?fˆÖ+ab*¦/q¥[«.4§¸ÎÀÄB7®¶k5>Ü„¾U¡ôÐüÔ, ‚“¥ÈZ'm"v$ý[¾†ü.#K7å—F¤p»|þc7â’ˆSɯlÁØô4%í¯ý?ý"Îy¶î—¢~u3 áº7üÑ1'²ûÉø1Ǽ@ˆXæR_ó3Ìøš8%oZȤù³iiåfšó[…=mHz–m&Ý©{¹ÿ‡ÿãÛãé'á—JÖq±\ú.³Ã¨^}~„oÚ0÷Mu—Ù…<²P€^LÿÎÎwïáܳVÌðŸMgœ =:Z`Ò–ÌOï€eB¤~]Ó8' ‘eœ—1Ã<ÌËÁí8‘ÿ\YO‹õˆ½B),SÉf)P‡Zj/ïÍ—`Oêû¶r§^M%ú¢üPWW‚Õº¿`šo;nÎó"ŠBâtA›pñ›nsþŠÔ¡låC\aÌ\ó;J¦Ø¤€uánòãÔ^øïó]% ÏoC}~Ä ¤õ]NÜœ|ýbD—¥@ÐÁä þœ¹ ®N)‚k¡¬"Nô¶,'o®$‹’Nãþ%VôP_,º€Öš°…³ª$ä àÌræ†!Ù¡¶;žŸ†ëìLkc˜>C†|²ˆ¯ÓóaƒþYæàÜ4µâb]è0¦îA ±ZçÊ¡³¹[õ²p¡[4t͘F4šÖ²þZL´£=y©h妱˜PäFÏ+pè©3èCù~àý`ˆ&j¥àö¾÷ï? 2‡¿3RK8s÷'ƒÂœƒxA4 ½N=ÀÉáÐdt Ôƒ¾0ÕZcìòÓC°àÍah˜²7ÏÄ»ÿÒ1;uÊDþCûQX¿Ã'Má¥.!áôHHã}æ.”K Ñ5óŠI×X6ÌâÃú’{ÌõåKIíI^˜yò(¤Ì÷ļ­¡ÌCƒÈï’&Z’V´®ï=¨H(Ð2Ù™øÁãn²¤Iŧ±WJhÈ¿AîÓXNŠ~jÄÝ·n³½_{`Ld:ëžüî÷P4Z1u'–ƒ‡ƒ}Ÿðyùä"³ëÀz’ù¾ž  ×ðkÈîå™M_ņÎS Ô-'ÃÏ[Ò’Ö,Ö#óJµ>ÂÚ޷̳MKLÉìÊ â÷Õs¶ ãÈv2¤5™,L•B«!f"ÿ¥öÃW³L˜¡>9W¯Cº¯ÊQ£ê$0ï©'gJk°k¹‘Y_"=_ÐÈØž”œ'Ë`´®èSLÙæ•Ž–Ú “WÅg[¦û¨¨ay¢v׌þU:|"1Y÷*tÛtÃû…Íë2ǽßÎ4àWƒeòͬG4]iµž½]öOCA£»žY6:Í®YãÆæb‡ãw©b47'1à§N=ì ‚;ñƒ„)ÉØñž——àɨ˜‰ø+MÆ%–à+VŸX(ºÒäk_1ï÷%ŽÜ‘º[àÌøy‚^µP%Tr?Túãf5˜->‡gD©Fd6X.&?z§“ ǵÈfî%&c‹=óÒ†L;¬H¶È,eHíðŽŠm™—‚Y¦d8QˆÖœp¦d£È“æ`vàiùôh%ÔKS —Éx¾ê,ž&¥ìSÝeðIï#w$Ä ¼ÐA¿iŰ›ÔúBºË¬5lޱ—]ðî]p$R"üœýºÑ0—×ä½(¢Ï ÕñdötÕjÅÂë“á@g zOýßûï5vÀ‘9õˆUýz¾G&3ùÏ¿Ã?å¯x`ØlûœƒIQÌ=)òÜ5œù±˜k÷ÀÀ”tîâÇNôòŸ@ÖÍ@‹ Ígqö™É¤hµ:8/ÊÑ?Ë™®ƒí eÜŠsŸa—iÖÇð‘Å+ÉHÁ;Œô·‡ e"0êR⢴í7?NKR Ö}VdIÌ'ÌxýÉûù‘5>ýÜtàÜZ~Ú÷Rˆ˜J·1?.^d–Úžåàt‚ü†åt×A'¬¬ÝíÅXÏ3¼­ã ‚ ‚-Úi,X%E$ÜÎ=Wm‚ÿ μ`äÕ‰ŒK6Ùç‹?©ÍoxV?–šÏ ß^œã$P'0ž3ù¸Ê ÷FK“ãPËI ‹oÊe4ÀB‰œŸÂOù CПHØúz÷vÙJšï ¹:R4h“ýrê"[^¦I²¤Í©¿ìØ#Г;3ߊ¢09€ÍÝ¢ÈÖ?¿›4°º¹78O§¡ÇøH8x ?7ü¼µ¸yK@†™>‘N %Y)ó°vãaºyša,Ë o¬œË¹–ÍZ5³)j«Xâp:7nsïÉT´n[ÑÌ£ƒ‡€8T:öÐDÿ›îüŠ;ÉË“}cö±9¢IZë„àMøF‡H’çÿޱ$÷ëcFx{؃Ó¨³¹>_gE¯\܃‰í̆ÎEøýðnL1Сñ5,tUo¤Æ‡ùpË¿f– ‡óW=L>;ã2«V.¹2ÇÉ>®%l–…BOqj}ñœO¯k)–x…JÖ7Ø”¥7ñÃŒzÿ€Äe Á¡½GÓÊõŠ€ çzVÁaÞŸÑÂ1Iÿƒ§Ž°E¥*Ùˆ_Ãpsw €ÑÛwÁmä¡Ý˜7xá³¾â_Lß$´Ka9·2ËåŒ&ö¿Dܨm¯M O‡¢Óòd sóÎú±! –Ϲ|»æÁìÜ8à_,EU ȧ]íÌ’3w€ñú[v_¥fv¡D(€óe“¡%û镽t¡Š/<9F´¨"&j\×@n "ü¶-´±ø §ûO l\H¤wi>§è®²¥Ø¬RC¿(^g£ç’jßv0ÍÛD7UG"ǃÃÈ!º3»–Ó1¼„¢—ÜRÊÅÄYÀšLµüÅn/IšsÒÚ–ÔÁv']TÉ€­={4N†^ åµorÈŒ+H“¾=„­ÞDù_ÿ/þ‚NR`}np›³é&•6ˆ}ëM ŠÏ¢W %˜ðçÖ;ÍY•˜œw–6¬ª…¤wÖLœfñS¬A7!UèÉ×…Œ|Ò39 ¤êaßNS¢í¹ògߤß é¡ý¨û!ž>Ý1ÂfGòᦈP¼½¶ŒVرeƒøGê,UÛÿ슪Eß'=tV8yý]v ^ev|%+FÓû/+Prq/›üéY-¼…¶$ÊÞ \ (IÕ¤Ñ 2x"(•u¼¶EÏù µkÉë—‘P±ÜŸöØXÒKr²LV(%zo-qÛC¿ üQË™1ž,Î6¨šzïµÃœÝ¼ô`ÂqÎÏV#jè=ˆ_ ÁHÜW>ƒ.ç˃‡º]xZ3ÜøÇcyR4Ž€„:ಹeìMß}¨µç9Ô©‰RšyŸ>å#η2Ùî‚? ãçCþ_CÁ3-L¯ÇaÌ ØB¯Þ‚¸¬ÐË)€»÷3©ÉxT}lB1ªÊˆ|L+»Ë¯™5Jè×y]&¹öriì‰!ç+óØÕZô‡ãTPVÖ!?1ÔdÄ”|rZ/§@]ó ¨ê>úTc­8o‚gæÉâ}õ7Pjøx¢ÿO{øÉ‹‰ììãX²±²ò\aP9ÄË àñ>orÉ2©„Tæ­ËSî°¡ tº™Í[p?ï,Æ5_ Ô}éæ7+ÈWqGZfÜÁ$ ª‚©HÙ7°’˜ŸJ¡LtÈçopýÚלš*´ („àe¸X|èªÄyMN0÷@9äɶmñ¡é†³±Adˆ™n'Ét>ÂÓÁ°÷Ì"’’hJâ=˱ÕRŽ~4øŠÏÔn3{Õ¼À(—‡ÔèèîÝLEÿXÒÁqªºýVZ ’i†dùoIbwJ™wÙšÎ(øNn‚ÿ7¯"LØëu›ERÄK7 4¬Èã*c¢æ<™µmYN”´jhRø©…î08Å<‹À«Ù2‡LÌh¼nåAdwn$Y¹ë‚[¡}~t˜·°7Dúñ•öZ/C¤—f±·ø1ëOï žq9ô‰m>cžbåçb˜b§n¹„ÖoÙÇZlb‹¯…’' š“4V7H›=¿`&½”ÕJûN.£1®ôìHd6þûïú°÷ƒ.ýÑY€jÎ'™Ü_ÓéŸÆÙ˜—5‹\>$A øÛˆ¥D hWв tñ–0¢@d„6Q‰pµ‰÷ÿó5v°Ûµ/1"+7`ªk xê@^¤ŠQí$«"Èàü\X %¿ãy5‰þvzƒ/{BÐÓ#šÄ ¸³‘ûEY—S’dA˜,å<$¯²ߢáSf".ò²'Kç+Rã÷Z´µÍg½ßI˦{Ñ‚jOªFSÉéäÖ 9úç1ѼáH%Î"âé¡èÔ¾‚&: e0Ûè8«ù*Dÿð·îb,ÎOÅ«ØJvÀ¿JG/_ÌØUGäRãþ#ô´lq¼MFŒãIø£çìõìs¤76Ì$8³¦\ÙæM]ú§c³ gZ¨¶å`]¯¼PѧGº=ˆPÑe¸9w<ßF Q­Ù™CàïçX£+ÖF±÷}U¿¤klþåǨȿƒýÓIÎÕ}Ç¡R0³¶ÃÚ¨JX¿®ŒÕÑ,>¥O~ó_a[4–ÐAt4«Ä,{=Ü´t»jߺ3Æt¢3Zlb°³3Þ®‘'GŸ}„c²d¶Ñ²–MÓË îÐJ{÷+ÜV€ùŽý¨œ¢€OÜ¿ã-uxÇÎŒÆühD‹ÓjpöÙ<5‹ø)üý¾:ìþ{4ÕßaÝèæ/«N”‹OäÿàæIµ°tHyt V®iǤّOtÏ2$—ï•’µNtÁë|&ëy ®pŸG ®ÈåãÜ/*#Ž?t§ÑôÜ3ZrŸÁÑasš¤M”v%šãL3^Ï¥AÎW@NÖ€Há½gȧc"_,ˆnÞ/8Ÿ‹>0ËÎI“ÝÙOàAW<–°CŒ×¨9qa v9‹Ú¬éç¯=L‹ß1kFˆ Ž@¡í¸ü¹Õ”4€ÝöÞ…`úq}Æú×C¶ÿb$¦fý‚*/§‰s‚¿ò‚ áíéP’?‹â·$6u•Yi9Á­Öp:Ì¿ÃêŽ*’;4ˆen,b†öápà{nQ·/3ø}ˆêüDë»ÀÕ¼ ¿­7e2Ρ]?¡˜Þãë“Q:!]öVâÙº÷øÆ©Zí°³¡Œ“rû,Kç:Œ)k˜n=¬û¥!Pžy?dö«Ç&0¸á‚eÆ—™}‹GÁº.“©¹œ»¶¿…DE"g´êghÓ ~Lµ–! ]é“[wqŸÿ,|ýÎ ÷~Ñet¼õЩZl¬R¢½]¥Ìòâ\]s맪ÀÊc_˜yZ‘ÌÚw͘ž±—]×^¤ÁÒk"ÿ—)ïa3.üä^e4ÉWðô,/ìQ!÷$äéI¾ßxªx„·_„hm 'îd¶n×Ï(Wúù‘(ñUID‰øVîÈz::KŒÓ÷ãV‡y¤j„CünÆ‘ò“ÙÇÊÈØÞ‘d%|uéávD¥ú¼|ÂÑY‡6¢PûyüfIóz¶ûù6´æè&w»aÛiöÙ¦ãÄ­~)6Ž]æð:¤aHÔFÌ]wÝSÉ´6Æ]S…‰ãe's‡Áãñ‰øwCnÛyŒø¼š ¦áomØ=åøæücïû6›‘Äea"ÕV=-ab¬öÅ©dÇŸ) ×;—|²™„•ßxèzó0ûêbNÂðÇÌvð[¢F•&ÏŸôÒTïItjW ™5ÿíI_…¦+öÀ¤öUd{\>§Ã¾“pöþ,6hß#´:¯Hw›]bÚ.ÛÑë×ÿÐO©<`þЉܽ\JO›G\ShóÁåÄ–™Dæ=Mdê-gqÖÆ°¦›“à®±«„,嘉¿ÿã'q“vÁ²Œs˜ÃSÃ>6܉ßyª™z³dR,X„)J×@ïíˆÜÕŽóš¡oS!îZÍÅ|½@ÐïŒDn ¿ß†ž9\t½z z¬&Áo&ÃË«9 ÃSËZ¤¦àÎ`Súœ¤Ãμô­è=äÝîËÌ÷¼Âjè˜Síja|õˆAégÂD§¤€Éox‰Šš†´iãa˜¬·"—÷âg#*Øl æo³ñÿL²£úgmɲv¤™M¡Ó§»©§`…éS0sýŸ ‘FOPTmeB>=„/Þ–¤õe‰¢s7®%Cœè‰ø?±;Ïnb1{‰/.»˜‚ RÃ5uêþð F½³Â«mæ¸xÚW6úÃ:ÈÝ)Sö0ÂÌ÷n9fçËØzu„9oh@M;§R·e™pZ\‡ÊïEc'èôøÅVÔlG‘Šn¼§\‰ #̈A53ïê“⃶ íéd?ñÈfWüÄÅúeÌ…ÏqÞÏïØ×^ c¯à¥V1²©bó!Ùí‡u˜Ê~¬K_Dš„/ã9OIlkêdw<–‚Ñc“È»Ø{PÅÌd—ßÈùoXmpW>§.îeÞ|«Ã¤ax¶Àq¢ÿ[öŹœ‹,o¦™²\”,}šÍNúy ¾KŸÂE2¸"ô/ÜV«BóÕ2àj¹»ŸéS‡Å0,fQŸƒ¿b‘ç¢3]ü¦ .O;bŠ Ì‰çOðñýlÆÏN´ W@™æWð®ê}­(McÍηüõ/'Ç*Ñ—¯C¸‰¹ÿ8t3ÀC'ÉjcCÎbòßshóMèo£^ÍSˆu+Ë9}Æšl=Ú•úë|þ!ü“¥ÊZ#øwu&clìƒiñQd×—]°¾G—–¿±!wÞa¢?©Ö,ƒ&É0h!I¢ôßÂÓ8—°~"ÿ{^oÂãQ+QãÓ1JW óh<ê5„É+˜ÔãÑÜnù\´ÓÝώ̾„†»Ù³õè NÛö'€ÒU1ÔfV`¬µ>ìUËeTýb¡ræZòöê,¼;æL á ­ß¡ÈÐ/¬Å‘QŒ]ËÝÉa8å‰+cjˆŠ”p,§Yÿq¿½4‹C×¼‘$ß y½é¸{÷Vsv ØÆ”´+,çáç-82ÿ#~z­J–—Ö¶Àñr–gNã[Å>áŽpÎ$ÄÄêËþR#¾Syè\»-ÓÒŠ@qð-Ç"'Š2 ½ž„I ÷°ºs@ÑK?wAq]­ydÉ‹,bΉ­§··åì[NáDýŸÑ7…'u¦íŽ0íâèà`hÌ>¹µ<‰üîT 4ñø§Ý@Û…0sûTrªG’ô†Í¦Ïž¨‘ñÇ`Õd.üìùbÿv‡Ë÷ZÞ†ò‘2–¹ê=9} –‡!ÍtÛö$“ý4 N_½D™!=úÅÊ/u¡Àý¦NY€~‹0Þ]iÌL8'ýð°k¿€3©»°»ûÁ3öÓ)XxvÜ™®‰‰£C°6±Ÿµ¸9•úß'ô÷¾i¤+¦SGÄ1%’"¹-d±Máùhõ/,¿3CbÁ`ž ÆK/x'üïÒ14~=ñÝxýÄRbÒ=Ÿ»+qíºì\)ÌíÜ ­6˜uHTkFØ  5Ò›#èשü,ß|^,1A.ûË=VV ¿d Ü×—*ä¢N„+þº7 ªšµÑ!ó>ÎM)Ç닼QĆÅëY2D}d>qÒV§Ëck`zÖZÝûsÅÐx•9³íô¶ò§ËýZPÆÀ˜1¾×˨|ã'ÄîúÄû ï̬ïyäŸ!»íõøò4Œø$á7BqWc!„ó/cÛ–O¡o=í ô¾(-©Y2áR¥7ÂÛ¥‚ým¾Øá…b~,Swb3«¿¿ŠJ4éÔ®Ã,¯Ó!Óg‰ÇjÆt’ƒ GÍ¥ÌG¼\òVÙäH«=ìŠ×:yˆ¡†>¸ 2»YÇÍÉt©j?'û{4©…Cÿé+ÌãRØñ-$ŽhCô¹eÌòaÒ¤'-¢à½„-1m÷}8ÏsÑ|»ºÓØ‘½°­Ò—¨ßÚÊl3žA§&¿œs B˜6÷­§3À Žv¼ÏeÓ…Ê!Ì_ü~}ò·À˜³°tUãbÖÊvúXÓá´“XðÖs‚ÿ–Äñ1?õbô΂±ß>ä¼6À6“ˬo&…r8]R‰":ǹ]©ôº‡çÒd%fMÉfpY|;¶êS½~îo#ŒªWÿ7Ÿl—uþ¾=,‡ £P×kC]xÎpjÓ96i8s\ÇÝš»“ñÅ_«§³†Ÿo€ÙT*5“Ü­ÏF/e\Æ)­‚yžÎÄy’;Yñ“Ÿ6O;¯ê›q[± ™®”z‹4Ñ}H“nŸ÷<+SŸÀl;]¶ø§Y!~GqÁbÐH± %|éøùa”ñÊÑø%gñýïT[§?ȶLàß½wŒ}\»• Û:¥nʼ€fÞydí2Ú!¬ JÙÅìï;uh¹q ¤5±°ôI*‘4ºM@™>Ž¿© v¾-ŒZfœd«²§³~öÃpRY"겂v>O€šØt±‡ƒîByû)xxÓ’,‘jc§X­$"YqxÂS›,Õ¡µ'v#àvÕ_ðân;6ô_„ÙSs07h \7ZÇHäÞbŸE‚[„Ý!G¨ØõÕ¡Êõ2¼òÒãîš@Äíy¤:ÆŸu’»ÄÄþ“¤ßšŒY}þ ([–FµÃ€ö,f9_ÿ÷üË盦(6= „nOÆ/Wc¸nÓÚ1³“uOãœ.Þq³‰iÔ:ÈÕÖaA¦é›W Z<šìœà0[<ΫÝåxØÒ òºe`Ÿëq(+ëãívg@ÿû÷³*­ØÏŠ.€|íÚçÌ®(ÿ&><)½Ó·djEù°¼à DnGæBÜLÖã¨ù˜<Ú ›Nï`{Ö…àž ŒÝ%#úy‘XùÍÄ€îíøcÔçË;p…-cÌN{:ÛIp}’9ú L`kÔ}ÁÂUC-Py9® oªÁ¼è}ðùÀm8üLŽ µKNÔ¿tu¤«Ážà3ÜÕUOÐò5…ý±—ZĤ“°¥Ø¶ ²¿Âûã¼~ÁV‡`aÆJ®|uê£>½®Þ*¢-µ'B¹¥ÇuÙ¬s™ÆR(±m 3y©[£®¼x„ói>I0Hó†-Æüt®€<­½yšÉ–xÎä'-eîìÑ#‚A ¨´Ä,2mþ9î#=+üá:›ŽÎ ÁÝ%mpõ–?ÙrN~R&Þ—¿Žß¿R®’ôiˆ_h…’ýÆâ•ÁÉÊX‹?œƒ«m‰ào¶žÉ˜ŽûL-pž&9wQ ,¿Èw¹  ü|iz8&¬KãŸÀÍ;êìºð¹ ŽgXŠ‹¤Ö`8ï+ÖÙsšZŠ ›D}…{¬æNE²&_ ì’"Q÷xŽ(/†f%5Hñ|þì”ßhÈ,®©Ã¥ßfâÆ!/æ'ÿi”Øñ›­áÎÅõíTêòoìî¸Êê,Ã\mE0hCÑXÔ¿¬D6<Ë¡ª¸ìW*‰é•%Œ¾ÀޅDZWbý6•zï§Úlã¼+-š´G·ëBÖW.Æ»§â=ç|Ô¬ Es•X¨=Ÿ^ùÓÆ6h±ù‡Qxe)Î{ï@"WÊ‚W™ÅDÿyi„›asÀ[x®SÉåW…õÎKÐ'Gš¨²<ô„Ñap|r/Õíà }ƒlýp&8û\a}'Mf_MK'_ìD¸Qnë§À«Teòæ¡î/ɯ­¨olF¯ m@ňLÆòâ|ø´ð-ëË}ÁJ¯ÛJB®HÒgËK[öÍBÏŸø((ÌO(‘ŸOì@"ÖÎ6eÊYè{ïH&O¯b çfq¬¶ñ“åÏ^2“Û*ìrµMÉ›ë„]üVíoŸÁEª‘lŸ2z;ÇBÂËõL€¬8±ÀÏy‰L»±[¿Ê›ÏCÙ©ßuo;íš¾Âÿ¸X5±'øüÀϵ8o¯ ‹ÀN¦ý¥9Èl¾Ž‚¹Fè;ß\UìÃkýkÀ¡m'ÕÊœBc×ã@ÒØä¶‰Ù1“dªIÒÓ‡ïã‹¥p¸ÁœëˆEø„>÷PÕèWÿd )·É†àÞHŽr‘0Ý3ï “:=ž/£åÇI78@„iÉ_WÅÞè+EÆ<ÂJ ÀkÕgò¹ƒ*CZ|I‹á-\û÷i@Û¿l_¤HvýZÊ~š¹~h ’ÕQRt‰I9¨àÁh”¸Ñ9·Ï£ìRYzþ¤ÙÞ¹¾o{ÊI{v¿Ýábƒµð„þ½;ôƒ­jrÄ4ëTp^悯RÐ&ø*fj±u®í°Y½‹Qw’ůéè¦Få8ý¨T!³æ¶lº8N?‡æŽçý S™7Œë¾gB¿ª˜Ý¯n1ºÛÚ–””†÷_c ¿î\x¤Cמ>|c×g<Áð}°’gä”ÅÁƒO[iXÿ/\”äF 4`ó­Ú m×(÷zÁ•O×§çø*°N( ¯Ð »x%¸_S¡ÁçÎthÙ/Fã[paXZ9-ç}­€›¯™Å>¯ð—†ˆú' tf*z¯¡üÚHœ>µ0ãvIcîIÌgs2õ!:/ºÝxÉ3Ù9 š™ûyP'Ÿ8¢5j¢Ø«oðÉ÷<Šôª±ÙIhõ0§ÙòÑx¾XĽ nÊfà?[ƒØ©·O:ppÍBV.V“öqo²Á?BéÖ ¯uœiÃNa2èkI ,9îEÓ°sÊI–s^M&³k‘‡ÎpDP_n¢ZŸŒh7{,·-¢M:äûEàºË‘²YÁpìÍ6¢;CÂQÌõ´(ë@NW%XZòcŠм° ˜Øl¬½‹¬ +Ì_Mì¼óJçnwr¢²n R¿ø§Üî+Õ¤{õi Ô“BÓE©ÈNfà›µ ^z›ëuFŽp>-¢Ãï@tø˾?ËŽí\†îöW@h›Y(ªˆ÷ÝÉh°4s0KŸš8ôà7]m´Ov £²¼ç`ÆÞ9t|ìï d¼§—s‰w@{ß.„äòÆÓöÄEDëSæïJþ‰üÏ©<Ìì»”é¯THÎYbºUzqÚª=+Fûå’ÄB'§z rF–šùÔ_lµ"/ä9@îl^Iô àeY!Z¶äGê;L¹„È}¨K×NE76áîâðäÎbjÜâJrÁ»)äÙ8>¹Ãjœd]Æ~ ‡Ì ƒcë5gèÜ‚Bëî°›§ò¢CžÌBsaòï÷5æâ xòäïxO¸È9êÔZEü4Ïe.þbŽªcVäæãœŸ ©~ÞÜC”NønPÉÞ~¼ „Áü(^Ó3‚ÆWw3óÞ—CDæ¿ þ?»Ô•ÔHýUsºw¥1µÊ”"cšŽ¤ßo)Ù™Jx½V1+ïf–V?Äv}`y2“Ý¿ô˜Í°b²7±R–ºLKõ²Vbáy~u{÷0[†Ja‘¿2üÛÀ¢Û=^¸¼ë;éš ™iB6ÎÛƒ®›ïâÀAö‡oÈx¬dÉŽ<9œ¼a9+*∵щ0÷ªÉó€Ùzp!-õž€²f =ù»‚ôŒ…Q7m{ò£öˆˆÑŒ¹ãžpê ð9z’z3 tÿifïóÄ-ߎox‰Ó÷a£ÐþY—‚jú0Úw25 µŸèÿ) ¾CVëM\-õzƒæ¢ÊãïìÞBÇdÈîòr˜rH‹NûÕ œée´J8”Ú|LÇM+Qáp¬3c™OïXŽ‚ÐSî—·¦ØT=“ÄŽ>@Ê“ï‡lˆÝ6 ,m»ôHx¨ÃÌo– ƒŽ¸ÿì‘–u÷’Yãëߨ¿Ì•;iT*Œ”ÔCí9]Ò庬Orq†öE,·e¸Gyiòðyh •†gPhƒý™¶•^|bK^|¾B*ßÁ-Â~Œd¸í-Ù@vWÀNžçÌw1F¶Î‹Ø§)ã-ýgX+¶¬ôùßüÿ‰<ù1“CKém‹0ú4®=›ÙG_Ñ?÷ê?ÜCWùë“Þ {Xó;¾ÞN„ðè÷}Õƒ®t+€’\YÒ•iE}ú>QëɲL@®½q˜I–ׯïóÈcÏï̵6(hÇ®ñƒÔÇ^´ÿX#4C'%a‹j>p¬YGŸ ·`r4á›NÈ´^R·O‹<B爺Q¾Ð&8>? -×(‰%] {øãs\‚óg{Q–?Eªî«_MÀ «¦ÉǰïU+R,Ääì°U!ôôPšLÎ"ÃZmÌFÍÓXSùdÜ«bD»%áî5ŠŠŸá^ø¼sã sÓ$/ì‚'‹dHc·(|êÔC­S"t7i=ÜÔ²ûÖmô\$Og‰vÃoc9²·.Š.2¢S qJ[+_×*øºä8½>S•ùÜ õ .Â÷+ŸáXèiœfägô¼éÆï„øôÆOBRÞ]BÁ¬0\íŽ0[€8~%ÌŠÇx‚=Œåýû°zÍvJ–†#YóžúøããI.ý^-ã¾âKö¾·†ÅÍ–dZä“o«Dw‚(s);…=(#C´;ÐM§ Úá^°^ 'Z•¢ ½ácž’\H5¿.ÅiS4i@Êv*×´Z½G¬ƒÖkCðâùø%ß ¦~ÜðÍ;IxJ2Εµ`ÍóÈäW5Ð4)‘^Z VÇä`¨Ô–l˜»¾mÑ%k|¤h•V#Ä_[Ö*ùîð™®Núò䙯—y¬¤š é(lÆÁõApï I5¤Þ’ {ý†-z°¯àøß3ì¡ïÓÙça‡üu0¯Â+øf¢þ¥öYÃW ÊñܶÄm‡’^æ˜Ö3|Ë íë… ÇÀª ñ{iî }¬kóKøœóD+J™¥Oáì7o´ÅS˜9¾ÈÕòO“9«:Œ‚½‘¨ÒÉþv/Á¡9QºAhÝe¢™ ´˃æ‚à=ǰ§·}ZähO¼ðòÀ?­“húÏl¦´»ª²Ù\“XXö) òDèˆñí–÷‹Ã‰%ZR•˜XÓÀ–P »ÆõË›»kÉÅ ¼„c¯‰ž´âT:}—c@¥ÐðäföCa ¸¶â€³îß7Ñÿ¦ž(fmžÿá f˜³KðY©×ŽÄÎÛÑíÐl²Îv„»ÀÄ׿gåÊ`û±žV”ÀÍÏ4ȬpbÿÍ]ÓH‰ŒRAñýÓ _–éc³¸!;i3ËÌÝëL¦Þ¢Wް’¯æ3Q·¸ïÝþ÷üOcç/Xj;ÿœ¢c¸(­j脘Π܇ïY÷˜C‰Ø^ÌD+ýá&ªg0A¿0ð¿y¼kPòÈSä.µ£AÛÂŽÍ p¨\ íÖÃOÇrFÃ&ƒÖÞè…ä›)t§½3óCj-ˆò²ó¼xìÕ§’‰+éÓÓb´D娄zVϸ¼xð—}8*&V1»)¢èï(öýTY*þvÍNFnßÂÎ{ LsÆê ÒȃöÇîÇs_äé*2ÀÍð‹gVo'Cý®4fòrx¨JdMcihÝ s»³zj3W_¨ B©ýýþçÎç£È£1™Iºd‹WEÈÏZ8§c ï/q#A·í ·!_?Äê¯2ô³Äi¸[Ÿ £ZÙÆ›vwö>‡E’"DöÂl²üϸe J®Þ˜¢·¹Lӌ׌ÚvÖ³“dµjûÝþ|`аôîGöõÂïŒÍvGt*@ÿÜ…u`ÕÍutEŒ)h¹ŒÛB«î0Ä-UÅÚƒ—ÙÂÞ‹Œ€2CûVá]mú{Z=ëc›kæ%o\V¹ieк'M>ªÐÅ Æè^Í=’¢'ä—`Û*¸J¡ÊŒÖʧ˜y¨„4]'ôÿ›ƒsaáµ°˜ô]7blÖaˆ ^ c]gðbå{ø¸F“Ž<¯ÇÆÜR–“¾þLAÕÿ óY: Ó'W1neñL©þZµÎaªBïpÖ>¬À£ưIU™r‹P#66ïg¯Í¦g‹uˆÂ†SÈãü‹ý»i÷I‡})ð‰É÷“BŸ·H7v3Õ¶¶LJ´;ý ² GÈÎ5 šÁGÂ=³Az×Oæ‘“|Y ¨“ÁXù„ÃŒÌÑ.b‡·¨ÑO)+¡ö–Š.S€ÌFiúÁV ?uÑmaÜ  ÏrH°Âîÿðkw›3ÑFטC÷˜e+ß`Ï6GºsÒ#Ë ž!oð¿=1ÉXH5ºóàòòÄ?#oW€XóO¸¸‰®åƒjëËäÓ³äkóRîÔ< ðoé!ÑÛHpËQ:kE—¬ë[ñ~|ù ²/<#Í3nã“+yè’‚ò5”‘Kë&a¯ò&ZQý(ïßC«]ŠisVí­&~a‰$å½ Ò # "$ Ynsï0äò}òzš(,6¦É/µÈæ‡ëIîŽEd_Äêá$ˆÍ’Ôû¨>°º$^æªÜi%ùÔ¤ãnæ• þóë=sT¿2;êo"·s¤¬xAÜŸN¢ÚʃàZÄ!)µã^P9|®Åq2woáz<¬gS¯xÑëRD MÆž³gq]È—¢J6ãËr<®œÎhv˜°ªËæ‘‚Žã´lu:ñJ ;Ç{éjÖŒ®YœOÌø‰åA^x¸Òˆ¼§û禾Ó󨪵;>ë$F3sˆyã7.l~|“v²W Õè­_¼´ÁÔ}²‡aã*ŒuË£¹FÎ`[cI¼3øHúJI¢Ú„ìj·nÚÉÚhÆ`@€)õš½–áz}C‹a72Ó]„ãw“7ýµû}£²@ròq¥Ý&:ÀÌ&žSÜP±w&åÑ‘!¨¨¹ mœªÃ¾M¥¸7ðxg;cß/!ò¯Ù€hmþ Ó¦Âß)ïÁÔoîøg°eš ;ûóæ.×vÓ½6|¹+–5ÿëLnH¨qv/®BɲBܽߨ_Æ1Í­˜_9Â|ÌKÄ á"ì¸ÃR¤O"V÷q¯Öài¨©Yü¾qØVWFgtY‘¤Òìñ^Ïaµ"ͨæ™oxöâRÔ üšOÐÓ‡j~[ 7z’Ñår!N–˜‚õ§9Êý `$ÊO·¿‡ ‚?XÚ~{¢þ¿*­ËYÏX¯©>pno&SQ®ûåW`éƒxüt\ ú¿¼†}7«‘í¯dô­ÀZ× 8Òi€Ê#0År%la·Ò’ßð~ÕN,­-"†=«HT2õ7ä‰Eâ¼A–á”h­`Úd§¨íˆÉ½·Á«5vsjf@½¨<1·Ù©|g ­+®Áûµ£85ËC”„è®ðc˜oË/粬Kš8ép»Îž>د#È^ïJÃÚ­BDnñ^äY$yaG1R%§|k¿îdY²}“¬D»î…ââITÓs6L‹0ƒe/æGíCÏ?L},Az¯ÊÒ©ÕÂäHS!çÛþÂ…Õöž4pŠ9l>*Oâ&‘ÁÄϳ»yÆT!b’oE^\ÈDd bý„"=ư4zºNÂŽû °0öí× ÃWÚÿ@¾Æ–Ùð¤ÿå7`ÅÁ‚/Ãá÷˜¦2—Zýº ?ÖõbÌûLܽc9ªÿ2‡§9}Ø¡âL[ŸDÑöY qÑÖåôÃB1òe'‡ŒeÐ ªGáŽübd*ÐêÄ'WÊûú:›†ª‘¼x{ªãó{à iã|â™@ÿ› ní8É*ÓÄíÆ ~{¶šZÅ_!yã½$¤½™Þ>çDϾû Kg,c‡¯æÁ¹›If½½ô,m•Œ…ìK_`_Œ9DÛo'n<EmUZ@>ÙY™4ÁnÜH_{j‘L.ýü5;2ªD4-¿Âƒ“æ†eT³Kd£éÃBF/.uŸ`gøN¦6þ5´½Éƒnúœ…¦½à¤t5šùc Z™éDöŽÄ’+r硼BT]ÿAOÝIVd ’çß\0Øø»9ÿ-ªd­"KsÈ⪤¤˜!kž*±4|‚ÿìø$0äNæÈrEÆlià.Aâý‘‡42Àw\ýÿà3'dƒÒ=Q. à‚9Ü#I%wLc§˜*`‰v8ni·ÂyUöÈôê‚Å"Üj¸ ά?ÃZd ÞZFtUßvH«hÇÆËM¸x?½ž1…ôW*ᣯbPõQÆx‹ÐµEXÙµ odâOéN\Ø©Goq^©}Œ%+8m]8ló•±I¯bl¤OÞÒE×FØ % ’yèTXðôl¿.xWÄaD‘ý¹œ«ZôÕ/°xúûx‘$†Xî³øµœlpUÐ;}î?LÓ 5ë »zÊ;´pp§Ž-)ã~À¡ÖXê˜‘ëÆ‰TJf. P$"ÇAnáNœ~ר{2ØÅË‹eñкt37'¶¯h?Â7GÚ üþ#«Ð"¡‘E꫊üÛ%0öÙ$rÉi'×2~ ÿ`@Êîÿ1õåáT}ßÿ2ËzªFã +1vL6Žùä@ñŠ;¡=ãž—ñv&ÆX]M…nâNç:"éö-é •ÙcØŠ˜4|6Öƒ„ݯ£¼xŒ~ƒÁ V£xí R²ì"¬8¢…§ŸÀï#ûl ®'ÍømÖw¹ÜÅKá¿ñM¦â\-ˆÝ𗯸WŠœ,¡k³*ˆfÿ%èß=åb0íü(vk}1Mù©Æf¿NÅä ) »o®½È§—ÔaÏÍýdÈ;¤jÃálê;zRï!]á±ôs;h½fwiùæŸé…)ÇrUÑ?6s³Ñí®kÙeMFŸØ÷+kÉüñV)»/áÐ0ÿÁ_­ðòÑgª;Jƒi¥êîs÷‰¹ÀR&´1¯ù¢îomàflHfëî°ñçkáÎ3Y¼ È–o×a2Ÿ#YhŒ2q¿z’®V®àžÿ*B™ç×™“±2/ÈôF¯’¦µR×ñçÖ¯¼iÓ5Éþ„x·X† ÄGógþùÌié±'›ß’­<¢ÿp,Y–4‰)åv2mk؉îÇüéWrÜò5$¢£˜­¯&ã©[,ÀôoÞ@ ÇzÌ9#Kz¡‹~ÝŸM¥ÉXøo‘²sgŸ´x#öGú뀑D$N0æ€ÕŒ'þs زyZôï× bóãìp~Ÿ»FžºìÝÚ4PÞRI®=ˆ¼:ZÞ\ N%PïE4HtLíµ!оތDÈN'MÇxY;/QC¡©dáŽÍ8í !WžÖ¢oä˜Ú3Š(»†2ëƒì÷èn®NR”l+:Å>ùˆ}ïËja²fî°.g:ìÞY—üÛ]á@³®ús½ÚëÀ­#wj½§2‰cÈ]yÚ“)K%¦œ†Ã …¸J¯Þýðà/"ÂLe׬ִ$gí;¸r•&쿘c7ìá]š;É ]§qïÙ$º¦ê­tÖƒ’ȱl¥Öœu!…›c1IþÕb’ïB©È&|´V•Y¸‹3!7–ÿ.o<GÓËðóé><ÓD[\€mûßrA—ˆPú *…|‚N+öÜþ ]™[6¬Û÷°µeÔøËxôSR`.°ý–)_âLyaÔOEˆ6D]÷{îá]ùATš9ÿc®rWÖh³ÌA7VšoCÒšïÂޥʰàt,(Š£øß±ìhï^NõÇL¦nöO}‘†ˆkNCl<[ç«? ‘ {…YF¥:ñi­¢¿’öáå-Àz‚âÉèÏizÔ(â7ñ lãùÀÁo9¯*gþþ+¸º½V÷ÇC÷¹òøÇt/lDÞeЛ §í¼Ùúõ‡iFÇ*»z©{ó~­ºóÙÂ×fLµy4[þ €Ó-v@ò7|0è+‚3ª‡ÐºËºÕÞZ8®ÛOãþMfE)îð!túÜŸÅñ¾j³k#ö_É¢/ËBÎ8‚£Åaï½Û`ÜÌqáÛéͭ·p¡ænÝiFšåÐkÁ9LKÞÏ}•맯ú~ó_Jž!~OÛðç/YÔt:ê©«0gï 2þYÈ©eãY5U2$±•(ŸÙïZàšæB¢¿-dbÂØÇ´z«ŽQþŸ¹†VIòyó6œð`%=ve,¼l‡Þ×Õ0“®"–ôq0~lû NxÍeîýÉ`xú —¬ö6Mm­îYŒŽð}È.‰¨2×ÔV:J{ßÁµòKÉio[îÙæQðI{‹Ï=†iAMpLNy$þ¥žÕ!ùRÙû¦³atý'ßNñ¹‰•BÌd×hÜØÖ‰_úÆÂ@Ò%ì8Àc¶ÍšœU¸*LQXCòåÏ¡äèQìÃç÷ðåêlÕ˜~A1܆uùð"]™Åõ ÃÚ+›0åK'ìænIï€2ÅæîºŠcïòƒþ]¡>yÓ`ÊC}˜S•Ã/ò†;â` m=.¬xþ–fó9WüuA ·ZVÓÉ3†}· î&Wá6eV9?å­Ÿ_–J±€ÞË<+¿NÔÇc¬®ì3ŠüzÛ…ÈÝ5ìóËè‹gìdà!Ö¤ Å3GößJÌn¯™ÅkQÞŠJÚ™pøöONj” 8Ï7"+•†pàÓhŠ1„©«OÑ%ša×y«Êæö§ù~rЬžò¯“Xæ°„j„”ƒ¬¯Ó8{Ì=ç²["'hó¦VnÆP®ª*"{ï‰ãAÓ_›ŒŠûôÿ«£Æ1§ÖƒO\9˜dŸáÎÎí¡3L¥™AÒ2¾ÀM.yI/VLJsþÆa]†,>ÿ}CqÅuì<‰GšÌ¸à>_t4ŽÅîqX¤Û|±Õ;Lgþ•'_v)aÛI°2õ`³.Áº¦ÐòMˆuvwÜÿø¨ÛFW"±Âô4MxF0§ð%î"½œôŒ%øØCÍnÍ#ªsçQbÞØrîíôªäNKýø ù,’æM@ñM»­„vÒ‡zÜÃÁ~ëç°jèD'­"r¨…6—1µP“­¿¶–\b¡y(ßLnqáZ DmŠ8np=ÿÕ™ÌZø F÷à×kŽxeh.ñ𺀒%z´®û<žkr† ­ªøJ[Šs|x™»”4Ÿ´L4a7>©““ĘçJ¢ir‚ÂS ³Û§M°1jŒAæ¨oÝk†pI±f„ÿí/;J«µéFòvvÖýn”nT€ò*[täRyÅO¹å ÙóšÙhX§Æä6è`ýe öëƒ —ýB›tm¼ÏmþzÚ^gAæÔ¸?³šüïß-†žÅMpgN7ëê19¹:¯„Ž&`ÓØlz">Ív€ÁQk4ŒK§ÛhÂÖsÚ^· wŸ„#Nu(˜dI¶Ì¿…šë²éàÊߨbqŽÝÀ3 ×é„1_x±BY˜5$‰½?EIJúSؾ1¸/ ‡«r“ðêål,\) Ïg¿üo>Î|Îü¸OGfò„gªŽø¿ƒÐÙ“‡ A󗩨9}…æmoƒEŠú¼;U¯ éàlZA‡¿‡Õw.‚áJ'–ëVŸ«Ñ¶I™|?àE5¦Œf²Ÿ&Ç×1Ü_<’?E4…0|®Ø“fÊgµ?=‰f=c§±ˉx·l—ßœANOÏ„qû×óÂ$‡€7f)3#æìâ=K:÷Ž^¿³Ùv ï)¢û9ô¾rKÚ¸¼ŠX4aQ”sÏɤéb,ûž.KªI…;‘o óhäþTC—£œ³“/¹z·Ë|<‘-Êa`W%ÏÖ—§ßÓ±fÿ&ž·L: ig'\»™Ì8âÒD˜Ø= âbEÌb!Yý4?W‘àýÔ4àwÚÀ™™ëØ“vcX¯v ù=e̮ϔÝ]‡ñÜahRîävÜ /œ ¸ËB,~îzÌ( CÚ²ÝÛž`Ïú¸A+‰ùÞ^„¹ƒ?¿Rjw¿š\9¶c.³ý ¾d ghy€mõ|ŽßzoÓì?ϸ ßá¼s^ îÈY°Ù~wpËg¬¶HÅÙ~‹ésžû”&H¤–{ìÿƒòªÆÎtCžáSc¦QjEJæ ÔÝ<,íÍ'B°§z* ÏKć¼Uè/’€¾û÷òCDHÉb÷|+vä`¸wžš[‰1_N’«S|A3ë!gtyš°¹͸­gýðL“‰½+·š.·Œ{â‹W [AÌ2†Å¨o ¾O Va‰VYÄ-ý–Äþn=¼—âÄýÊ#¸ÜçÅß%6?ŒÓÃТýtźbüw÷+ÚHÐè]‚äÓ§mà—­§NEÀ;‰•܃I+™Ø®zº2ø'yý"¤¬7ÄŠ{&ЮÓjO%ˆýar?÷ç_a¡Ù húâKO PIÛ¹$vŽgfôãKµËèü7ßXI‘½cÇÛçSi݆?P¹ù+¦ýk„W,ñu±î›ö,>¥ÇãidœLO ýÖ—û²~ÌÖr3t÷gÁþ”ܯcAnŽÆŒ:Š»þº`dY·¸%‰ ×$SÇð|=&€üãG ™K—wyÁ—ûÏaú³xªAb}ºÇ ¤ž…Y¼À´¯0êͽÃ=îssk«.¦F_À®;‰è2×€M CŒÛiHçØZ@#ìÇ“îï`üŽ£Ø¶¹àãèþûoæVHÒWCÛ!æ31¬_@ÓýøJo&ýU'C'öÄC°e/È=‹&+øg™‚ ðõ¼“°ÇÚç¢"‚qõÕܸÑ/è¯ó–ì°üˆ¬R Ô–â%W€·ÂN¨²s?.ÿÔCæÑ ÿna¦Hn>wšë¿Pa.zÄB³۾ƃ@Ì!|õ;â|·²š d\”1Û´[`¤[\÷o¶«à®Jb*Á¿-“3^¨Ç¾ äРˆ@¼܃gV‚ÿ–XÛwÂz(Á‹O'Bä0nvzL%ïÂŒ9«õ2ì×ùÿ=ÿSo° ßmºÎžH·Éx‹UwøÂq‹°OQˆíùKßðyÔµÛ‹Úš j!smG¾Ÿ/Ä¡xü¹³‡ë:©J´òg]¿ñE ¢}{6ž\?c„ÙIŸ +VOäUSØÜÛÜ7¸¸è/ž±½µú µ8’“ßJ˜ç¯Ü'ð‚40údÅfŒ}ˇ¢'È*ÍðAb,ü×zŒI-ܘZa×м[MÓ)Š9h`0\~‹+xü¯ ÀCþ1xhÀ¢'»¢èŸÉôîvå ¬5z3fÆ¢ÓÂ>îM£ÈþÍ Ãw’:Ôú”'F^IEUa#ÞÞ¡Uèüû|J˜ÁÄþâ,,b,'_Ç4a¯°!oóõ ‘u~ BùL®4žSo`£æêóxR©ÄvM%žøÍpóɬiÊü±r!^®Ù€¿Ê1£Q§ ñÍ\z«zxj³+bdNñc$“ԣѿY¬%e_Ò°K³±*Ç{˜;ü[Èf²ÐÙÉP–àÀæ'ý€cÚÕ0öß}ªÕt”î)¬Çã'¥ˆyí$4SXmê+‰´i;HT!‘ç¾À{^uôÿÃëÅŽMew:%ŽHð“ŽJÃï+ ¸"C¦/ÙCû³¬‚úß8€9©À«“û’º­¨Å ý"j¹5”Œ7/À§ÑL:$ ¼¸iPZ»’½—²ä)ð}Ù2Oæä2µeä”ývÔ|Lv¹ãúgÍÉF­?ñäÂD¼~ü-·ëî<´F7"Þ˜ÇBÂYè¹EØÞKÝ; 5`;eŽïõ™‘˜=¤×*±ùñù„D¿¿‰ZlþavBo>û©ÿ´žÄ×mÈÑfõÿ7½:¡[25ä5áÌØH ܦNN¾ÍÙZ?49ëÆÒsØM› “|šEc3äÈ,‚SÂBDç}%Vn#í.akÎ^ºg¼t™K¡Ô›éøfæ’2ÅŠ¯ ªªýEmK9²=¥ŒëX׋±tqU(9qN›´µ—BnÀR¼¼­—~ó;Ú¿¼ñÚæ©0ê…-3þëÌø)]ÜŒ)£¸Ú{8çnE6¥‹“Qþ[ᵺ=¾†Sñ^ 4Ïbò®23kÜÿãÁ{mcŽW§AŽ}(W·ƒ%ùêãV—oÜ1Æîîñÿ¿ë¦àÓÇùt{ó(0ªú ËóiϺnš¶#ûöo!çxš܆U5xî&Ä.]…ØÜOCg™S` »,°OáÀlÕ°tA5WÑ=™Ý¯m…½)²¤ò€:ýøùo·çbªzÉ„ö—oƒƒ“ƒÀØT—™./ó¡$ÞX%9æñ nD }Ï 4ªõ„iµUP=[­_ykcêáÊÏ/8´Pý˦¢N”$ÊÉqÿNiÑÌÕ:ÌO´ê Wr‡L~P [G8£ï„ƒöÁàhð’ƒkoC™ÍsJ:Ïië%ÀëAÆ™ Füÿ‘ÊH?7\ÄB¾pEOïÕ4Pã—×àF]­ó‹“ŽOÞ„©³f­l¼aÍ6 ¥P_¥_hê”ÁVòŸà8™d|^oÑh·äÈ:ÈþÆ.e¬âæ;ídû=ޱ2h¦QrxMô5n,˜Ã¦ƒSÿ. fÁ} 9­ž®UGž®¿Íê3~H"s–DáãOó`ÙkNnt›’1ñ±­<̘"„ŠÚE¬žDN$,+ØŠ3½dѺè;m™MæL*d¿v,…+Ç4Èš~‹ÏÏ Ãc›é‘tÅè2úi®“»3uDÿ…úoÀ{ð›Þ Êçô>ȼ rS¬ú°ôG4ÝèцɊ¡,¢òoÆÄ›ÜŽ–S¸¸!–¼êA1Xï§D¨ ©¡P ë¯€ÉèúaþèŠFõUø¦êäL×ÂBËPÙ*†)ÿ* ûíôYÕ‹·{·qµs¿¢ìŽ#¨ûù(žzz>lÈᢞiÀe²õZ%ŽiÄYÛ;ÀÛ_šLßCÞ&bÊëyPl“Jk7(0¶» o:‹ÛuŸh<†Ÿ‹©ó·eØ°Æ $Ï|àŠWÀåŽX Ó;‰R ¯9³­:Dc(6‹äØ?ÿûe¼)ÐÀiPæúÈíjɬ÷ „‰i°›[-ú:á{@—¸zZû¤²Òª|08¶—å˜?€(XËŒ;ªÈä\Y|«';Ý5Ì#'Öúþ?ñÕ¸æøí¯bÞÞ3нí<ÎmÉ‚;aï1øi ¹É‹æÒzì*a¯6-dÊþdÛœx7f©&irñ$ŸûQã¤&ÒûÉ÷ß;4Îgí.wá}Øèý»CI ¶˜'ãûy¸ãôÞü'–ÞÇ‚[ç±\ñ3ä]1eÇ[E‰ÓxKfxîOf²ÂæXI*a«[8‰ÊzÇÍrŽem}˜ÔM 2fÃ_ü©™q¦5°­÷7}|9˜y[}á¤ût˜ÂT)²¼‡Á]Yèüs;’(J~^ë†&{Kþúc”î5XM’»ü™¢íS^ÕÝ.PO'~/.Ñ¢ g à“<³JK&ëD¿€ äÖ~O^ûW—~çp3ò£Vƒ<Öyç¾Mb+vDÂØMóÐÌo´›Dí¿Ð±½ÍT£Ü›,jÁÿo¿pTÌšEújâ™ë*DB`=$¼YÁ–PdY š,¼i-[}6 Vÿ9ÂYŠüám»æIkÞ€‚ÒkV-éÎÝØÄ–t¨€rZîÝpòŽ!ÛZs^3f&/ fR -ÌÌÀÞ£8çZ P³qøŽh‡'%¬¨y,MìŸLzvJ‘I]¥vÞ–­zäM]gºÒ=R ì¦ðÿõ.(ZÅÙ„”p!ÇÓÑìÙKÈàÂðÉ%ŽŽ]û”+jÎçu=’Æáµ°Ý; fCI­«€Þ©ìœ¤:Øx¾¡:Ÿ+Ù£'m8(=žì‰Œ¢ç×wàP@½0‰ÀÝ”UTcãf°œö™nø: {K¹Åíx´þç¨1€¿6ïä|•5ÈûÀÕ„ÎÍ&CÐóv»â2©Ãh s|}öš¿'8¯Äs¨ÝŠF‘Ò'!~wj¬Ç/7¤¨€ûušQ-Å>É1·³jSÈÀ… fÒdu1ŠsÉÿzFó›6^€cnt¦…W–R¤#_® Y±›M'¸d ÖÞ×Û²V’uNÕôP£%奅âW5;fÔ³* ¢ÑâÅ}ÜWµ„ü|ì"ÿÔaÙÇ\Ì•V¾–Ë÷½‰ QÀÁã…£hqAýsºê%•٬⋜Ç%(»³ZðhŽ›P\q \HÉÒ˜ûöF½Uá‡ü†üó ¶]|ÒÆ|³"¶¦D–¬úÄ=ÔO–îÑ…㺡x¯6ïR¬6›äKõ}$ÉïPêt7oºOÍŒÉÚ;Ká]—1y ‡Îgc³x%Tƒ0Æ$ÑKñ ¤Ìà-†ÙMg¢þ`ópjµh#â*Ü­Ê£ÓžÂ.<{cU_ÁÇžÛ0óçÝ‹O¼Ñ<ò#î¹Þ‹/;D1Ô)†›°#‘­øDLÏäcÄóÙ`òRg$þOß8FÒLáßž1lþ¨\ØüR ¶®ÉãÿȮǞÒn´Þ:¹Ç\„äÃò¬ôòr§n-½œ WnMÅî>=·&‚ÿG7lvga‚H)g|ΉÓzøˆ.—ùŽßž½¦³vZ¡^µvì1a&ɨÍ;ê0œC&²§R #Èí C©Ó[LÓáõë¬^J˜:ê¹B§”»“kÌä.Üâ÷-%ÿfê±bÅnhsXH,³}XÛGuœNÕÞ*±é>s¹M_nòߌÙA˃¦/°…~G0q×z›|ïqa:h͙帑æ'M#õïì¥0\n‰B…mv¤¸S4Šcȸ­%DÛ ežë!~™ 7¸ 5÷È!íƒ$®IhØS+˜Ì¼LS©zä¢KE˜ðÜŽ° çW2ý%œÆ·Fqo"…?ŒØïîk$8Žþk.3ªr!>Ç:Ð,e:×…’Éý9ìÆhöÎ}!k4Ï%ÊGó1V÷ Ó Áª Þ,{=®Y[ §"ýÈöQqh“ìÂNG®ïœ)ÿÕOÃXÛ%lâÓ}džg¼g_âÄû˜;_$ÿ)/iÝVªûf4!§elä_DrϘ„”y½g3NüDZÇåÂÆ¡ffãHlÏ_äåÛ[²oSi™æ6Ô9}Ë °—=pbïf˜’ÖŒ‡ Hÿ÷z)(Í<„¬037šal ‡.îËÞ}™é†ï‚xvº*È;‘ý­\ØO—Ì‚%O%àÃäOøZwHµýÅÏϦ²´y£Y‹”7¨uŠU{~…Õ¢'aÖØë`PŒþMà"»©¶á®ÚF¸ÿzç]w•}JM!pu/Ù¹.†rÞÿ• ä;±ÿæížtxÿ>.¼Dï&ÄP3Ou²q,{÷F·â_q¦¼Ð–½èÑ‚¬úwV|MÂ3b²ðv˜këm# îÔjB.v‰Á¾½e\þ4šä°†EqI0ü 3{ÓQg)Fm:ƒŸç´ÂD[5öeKqœµ¿ÜÑðüP’eÇÜÆ—Ï!)ªIУàüŠø¸ €iMÏÏó$}t~©:²…©y¢ï¾õlÑfâcÿ e'î‡üï Ãgš^IåçãJ¿4òKÑ“õD¥ãþYìë·åÄ_-𥨾…š¬×lö¢8…Í\¾’Ø.T&]¿Ö“mÂgÉqëSp-ý»0–˜¨Îgƒ)ßáOP:óvR#SŽˆ“U³T‰ß’¯QJüø¹0ÿïXÒò>Ž´^±ÇÁúôíŸÍ¼ÆQÍÏ#þßY*‡wW¬€'N2°7]µñ¬eÃ+zs£*øÏÍ^ßšñj T–d²ëú¹‡R欻è5œz&®œ¬mÜŸ-Ël¦+3Ré…üYâ¼£×n`øg vóúMì<ÒO<ØC¼+“@b¶dªÞ§§ãÓ°él {%’wm ój7RÓ)Ð7>Ÿîü,ªySyC¡åyìñ 7qÇê·ûÉÐT+rmöcúXØY¦¹CbŒPf.¨ühƒ¥Ç¦²ïÛ/@ºl>Ê:eã DŠ·š´‘]˜ëx D &á1¿'¸ÚKîÿïÿL­Wüäeí z‚b^ á›"˜ùa7öÆešpŸ;¼)eÄ}è„»·Ak­®µVšiLvÝ·¤;šñ\›81¾ÄäSIÅ"Xø2tãÕ°fF2Jÿtw™4¢b ·ò¬SÐ`Ž^K*ñ߯ƒü |¾ÿX!¸~i;fñÀÙÿ{ÿùÉù½t¾º5÷È{5#´™»G ²¯Áç3ü–Ql”­Üœ>“|šó£QÇ̆3í“aK¨wÙný¯÷óFC ïl%±³žrñû˜>Bc­~XÛÞ¯"âà[KvNQŠØŠG¿I°uÓÌxÓB\k!û Òæü Çãå©ï8ÕîØ-ڟƭćZ0h©3–j¬ƒÅÏ$Hòç.Œª·á‚¾=§CÞ‘ê8ý×÷yÙ(OÜ•ÈVô òZ:¥×”‚ÅÉeìíS9·Kf¤“*n½žÖŠá¼Ü¬Gì÷̘ÉÛöÕ× þµÆ8T¶…y/øAëÂѰ«löÜ ´¶Þ‹¾³³d—> }㓹¦¢DÖl1ŠÝ÷› ÞÇob@6ÄTÀd]’›éÎrwh³?t>ø4 Ç¼8¸Ñr‡»Y– -§ó0ìÚ ¿4Ó‚¹”Œ9îCXéÝßcÓ¬iPQ(B–8œ`×JoüK¤}ž¥Lé©'; {Ÿ67s/zùèîd’X>ú gÕ÷Âö ÈÛº4Ùß+™NèÞ~ ¬[A.ù*¶‹œ‡}¥Ïù•¢á‡’ ‰Ë|N¤¢Üš füp?¤EÆ’YÇJÀ«h=çyndÿw]—aÇ"SïÅ€ŸÕÖn÷-IL¸.9ו¦3—ƒ›‹ñ¤» ÿw;¯ºD}3ç<cqwB U]-·¦)²ð‡‹™Ëëß¶”,vÒ&½k«0ü¶<ÉŸî…Âů¸¨¨»ì¾V! ܔ÷gÒ1S\­÷Ï{ ÖéG¸“!,e™ðû'ç–$Å Udȕ܂ ²$$[­ ÔïrœU<­p©âô]ìá’J ¾N#Ñ7Héè4HvyŠc¦o"¶z*DûJ/Í?`„íï•ÈãPÖ61œu¾ºyïø¹£pkØ×þsÞNÝŠ-Y_8£(=…ilõÇvúñU'tYžÄ¬¾UÐ.fDL%ä¹ý=îÜ©ê¬ÝÖkÂa“¤>‹ò’À™r‰øAO‚^ŸË]x”@ë–ÊÁfÃ)ðÖcRX¡MLìØ´I`í7KøµN‰ÝZ퉃ÞWÁ¼"— çoÛ?¦ôŽf ñ—Ù1]mR}+––Ìg[.G/i¬–¸Â’}_åMÈÓl¥üV,þ6$I–‚èœtþÎcAýO¦wÔrTÍØÓÅX·ñ'ÌteÕ‚d’áL’C§ ï¥ÕˆþÝž´ ªD9µ›Œ?@ð­ÐðßF÷ÜþΩ|Õ€b{Wä¹DH0{å5ðë‡5ƈ‚Òñ8òs3GÜn©ApþuL·Õ#‡…ã˜eÂolø«6KÀþ[ã¨ö†Ëà¢lʲ³èçg sÍ~ãO:¯¥¡Ñt!Ë)[‚»ó!„$Ò¢ƒð½x<»°ãÌsö#83Ƽ¼„Q»—CùŒ8؇Å7@V°M+Já. Z Ø•1Ž;vy<ù¥÷dî½ÀðÓ&\ñLØÐ‰)¤œ¾×&‚YØ6y¼T'²Xd.Ò‰§ZߟÁ¥õipãr·ƒ÷ÿ[—šÏaüþä?@O{Š~CÕÚ*˜ÖŽcÅÙï)ÿ>›n ‘½êíL+n'D&ËÐp-”û©FÒBeØB¥'86߄ЫGêŸL>>æ½½4“Þ=¸”U×~Æ;l+·}ƒ]‹ª@ ÖÿÆ3*“äHW ìçÎ|Ä Vâ)·0(ÀgØ·¿Y—B„ÓNðˆ\ƒ%‹)×,i…{º7q RÑèû®{dOvdÝ…—JAxp¡ ùyχSß´Þ<»A%·¹n´à±û® j¯ÑgE!P-B ®•㪠lJëlZa ¾¡õ¸âŒ!iólÄf0hG{.„hà“ ºûˆ)‰ü%Epc }‘+ÊÜxö°û `“,ñ/œMšŽ‚:ïD~æudÿ§ÿO]zĵ]Ž€]íwàÓËÓ|Û ×ÿ«;†?¢!ck;^›¯FôåWàÑÞÿê9Þ/´ZöƒôéqÁ-e‡þŽ£ï^˜a¯gLJˆÁ…OÌîØfx°þ6]Øö†'2u}ÆOûeÄ6‰T€í)rt ”ß™oLÎ…´€¹¹×d#Ê\Oü³@ 'mƒi»¬*ìÌŠEÃÁr®ÈJ›tF®Æ یخüóÿƒOl“aŸÄ8TaÙ$ûÆ3œñxOPqq)Ô‡+Y¯Ì}NgÃ)¼à Íi­ûA|ÿ|Š®K€¢«VP0Ç$óÂÇ·vâfërˆðçZnª£]D*ìüªCu-®CLêÛ£ËÔµâGðïU£(ž9rų/ÂUÁepáÑ:Pvþ‰Ê«¬«¦5êÙñE \LÀ26'¶ žKç\ö?¡ÜþY8e–>?j™3°ÿÁ=/ŸKˆÈ0GÙLÐøõ†[ïM‰}‡mNãÜÔtxc…ÓVKá\61KŒ¹j&ƒ›ïd¦®p Â]Ša–î§üУڹõjäªÃöã-ÿÝÏ%\±¬al?°”nº Ãøý®/¬xu‹Ë¨à–ŒY‰©ENLùt<¿-c{RQ>ÿ#œ¨ÅaûàšN'.›*“U‰Ší,œ9zƈþÕ²? ž P'oAö\® F%j‘Ùþ¥¸ðN4α1'pk²&QA©¿|Ãd]¶<0Ÿó,TåÂø9Øú¤„;•ÖŒƒÖâ—0gñg¨mž(>Ú´voˆg¤ÌàÕ;*|TˆÃ¯7àÈb âþ+ó˳͟D˜€Wn¿È¹­’#£‘›¸n¾&‹ZiÞœ zŠìv ðÄs¯†æ?Q‚}{þŠ;:y/ëi~ûgAƒ¬0\ò’ö:ßk,<" Ïí£±ºn4íMÁd©óhëXÏÿ Q-²FìÌþûì~ÈMðŒÁè¢dÐpïÇüÖhp?Z‡»à½Ó.rýK´¸Ïi\sã3 A*ô=ã?oð"¦Ý“™õšBh+b§¿4–ªÃ‹U_xÝÿƱºÅo`…Â\íj ¶?Àµù;j¶…‘ÍÆ£àßÌ3^‹|ä=‡¼T„±™q,ƾ{rã‰Ã¢Ox{7¥Á~{0îübÖ~þ)fêão™ ÈÔ$¨]Q9¬x½‰)/I‡™¼L¬p‰ ¯ûs³eLhEIáô³ó£~Ê ÌnÛLâggÀ–9×€|™sïHgk÷¶Á£-;FâÿðŒ­ðbšŸtðÍpµ“1;ÿú;ÚmHä”óˆ`b6vMÉãÚN"û—mÆM!ã‰Ã¾%œÞ}”$]+ê°Mí¬¸ÔC—;iqosW³³8ü~¬¶VÃ8Áã0wí åýÃWZdâJ²{a8³Š–À¿¿u`ÍðzîO‰àGÝ¿Éíý‘UÜè¯óåHO¼à&…ºãã;õP*šuŸ½„Æó¦p7¦–ajÂ:&~¶/VÀå?ò%ô²›m² pˆ…¼Ä-8Ó¶Üõ¾TpbS[­˜Ç_U’´‰Q‡@!æuÒ–}uªæß»î¯ÏÂbFXÀOSÚ6ËŽ"ÝÚªøfÛAš×cŽÁeHΗ4b’{‘ ýKeË®ô£HT3ÉÅ/²Âøe .¿\CffÜÂô.žýÿ凟܉#ûo»¤gWärõG}ñ€v:˜†Üå ŽÈ¥åÉï\}2~6Œ"-VÒÀò ±ò×Ü_#M¾[Fkç‡9{ÂL6ŠÊœ€mç§!Í9 ÓÓd±#¯ìœ|ðí¸í ¤u½àÒO¬¦ÖcöãõÎK¼Ñ?/6^hŸŸ©ðgG.»î…°Wk`ù-aâ(­>É@ýTYN°¾ø¿úlþõ‹±pZlh˜/ì€Î7ŒçªNþ8éâó«ƒ\j¢"ŒÕýJsMQúë7j7 FË¢’øOx4t”y|oÛO+Ö+‘ž@·û-ep¶¸Ÿñ€ÓÂZÐ 5"ÂðE—(™)c ‘Ó5 ®q"ɘ×Oµ´¹9µ£Ðaé]LõÆwòo ÏkÉ:Žù=< “I„ïw†ýdô}²î\…fWÇŸ¯ùâOÀ@ãzZÓñ‘ë©Ód“ì\XôEâ:o/ž¸%Δ„çÂŽQölë{A–5Q›¥Ä¤"&ì‡3¢0}e Ö Ãáë>«ÿ·óáч] ß^ä~\ŽÂºw9­yŒË'1Û1÷à‘}9ê=­†-I}èaÚó.w`± K|&LôN"¡¿³GðÛL9øûÃ4Þ á˜ÔéL¤'Žë³”`}I©\•½:»Ÿ[D¥¬Æ²°êv6nêfÔ nË÷ãʼn{É'Ý'-¶42ëpîu5ôNêß¿9´0’ÏÙMZIÎXÀ7u˘•P4”z_„g¦z+Ÿ©¿W'¾5 ½BЏ¹Û2Ö+NLפÇþ9Áu8µtbl÷ç߯€ßægi\Ä1÷Ú‘³zqsX)·ÏŃýÁ;ÈIÃÊó»°têLr±`™©˺ф.Iz°úñÌ-gZËÝjÆd›a¹Ÿýÿ«öÒÁ.»&®.IŸ°È8×”‰»SÀ¸ª —º5£»]î»V䞃 ÓP×½›ÓxWŒÛÚÄÁ| ™ž)~ßUd©Õ^]x)“7¾TrK†-n4|cÀ ¶œdýÑ#ìg.È ÅÞµT¾× 2¶}H†€ê<'vA=’Û¤%Œ² Êñú³tzèB)Š>M££d؉­ûˆô9Q½~솄HðK¶;m?.Y€³·–Ý6®ntlã·Ö0´e9;3Ávk.C2 ò[¶RsKòPÃ¥ê0¯¡8ˆ9ÿ´–QBÃKtémEvæ›ONÛÇUãÏcú$#FŠÝu—†oKƒ*GcÜßcƒH.‰ózÄç2 H"Év ÈÂ;¹B»·‘Ë=Ã$H<9ÂSDßä!kÉËõüÁgäfÁÎÖ h>=6ûxâ'qCÆÖ/ ±ksIìÚ{ž{óè \ ÄZë‰ä˜çA8ö¼~Wrú‰Ïàe—ÊÌP Ndƒ^ê=tþxÄ…ãÐ;*•y¥µãÏ7ÑhkÛB«‰W?M™ðÏL…ë."äW‘ q/à±ÐâE8nXƒ eßñÿu&Ç1./–œJ«ÄtÈÛL4H:Ær×6ÒiqGY¢z.×õí1žª»….–“ѾK˜MQ0q³XàÇ,†pt~¢Kú¾sÞÛýŽž˜Õ‚ W™Öyf<^‡‹¹Q‰ÁFdtüEÜþ:šrÁË÷í£9p㸫›²„nw5Ãì}¹äîÑW˜÷¡†ÿ>ØÕ—XÁ‘ÏÁL\T„̽Äzx©cCÞ¡YádrdŸˆŒÍ"¶F§ØÚ‡µÜÔÖ¯¸Ü׆9$ 1÷7ÍØ¶º€FãŸ'©¿ Íomå,疀ЬÛ#ú_ÀLýî’%zø¢Ašl®Iæ´¥A‘£óßëL”–ºq±ýÝô<•e½DimqXêVˆ+*~ÂBÞ+ntº–Ùy±_ÇSYúÞsÌl@;¾Ã†™®?Ê&$•…¡¨ÿ!0ÔRc‹•¬À7ôäªH‹vx2(wŸ‡Y#&aeÎÞW<ÆN™Rú©¯ñÿjž·êÁsò}’1³Þ¯DÿÊdVõ+”ÜqÃnæÃxø,„sæ ø¾û3© ±‘×Ù=ËS¸È³+üICþí# 3_Z„ƒŸ‚زåM#ùÿdºMXÞ€þ2:¸Ìi3Z4¼ç›ýâ‹JA`L"v2ŽmºUf{%Éåþ_4ñ…É7…©ið:LSÅf.þ›ëÖà»9`{o;.ÌÔ#‹+·¾°*ù=Œ‡¾·ðéµ*¨ˆBçm¬öÃ+zsµ-L-¦.èh ò6Ë0ÑgÐÅCšl62OµbÈܼ33ßroߺसôY¯*³ÙY ¾Óqà™±uyñ|"-ÎÊÑìBz´x_cO¢¨†ƒëgãö÷{q›+ø<}Ê­yQO‡àøìZUñjäü;ßµŒ¿"È»ìég»` vRñò‹¸6²<M"Ç-Ð?oh(ê°Ù“;A&v*ÙšhË6—&bõ«çœSJü”O…=ª¤7Ï’~Úu+Ÿ<õ,Çã!‡­" ösGýx·vÏá<޼ÄoÖ_ÊÐji>Z÷l„%V£õÏøë¿†½™ÄæN9Š¢­rà.£Îê‚َ•) ®¾‰÷]®×–XXG¨F±Zïð|V!?ˤ‹fMº ݽ·qkt,˜h‹2Ýí_ñŸÈÈè`x¥u–IÄ‚wƒ0ªPx­9 Ï„j±ª‘þ. ¼?‚ñ™•Ì|Ëf|Ñ6¦‰µ Ou,•¹.ÎC"©KöKzcÌ[®¡%Ž·£Ó™ƒVS89ŽE/ï…ðÍex©=Œ=µ™ÁfM@µgü¾} +KÙïöÆ#Š>{°­oÃéÅׯÐ*<‹É¾x ¡“¾â.GKO±³Î~_Æöþ½GÛ-‘#p=—‡%K[é4Âý°ùpš#Ú€G7-&O§a¨±&Ñ´KÅÕYÜñÿ² 9\FG'6k¥‚èB;,ª*BýÄú$[•ý#Ùo¹é’ñʌſû0ÞÏmfýŽ—¹Ÿ÷$Ávõ,æü’ÿ/_½_óبS– ë‹YÓ¹~ó• XªL6¨Å’Î÷…œøÏÃøwB(Y•ÒݸàoÙ¶t<þ7÷ø_2Z|:Ñð{ƒŸöÕƒÚÌW`bÈÿ¤²ãåpƦüÚù2\Õ”»øD_’­p»_bÏA}ö!øå Ê.|öY4 ìÚ±R#²d£7;´¥†¾Ü;›úó./Ä_þ»„Š|¢µ ¹ðìXíˆÿy=+Á†™ ï¿å—g%Oà@ýTÜ0`ÅlæyCèÃܲ©Õl±´œnÈ#¥+ŸCüŸ·è{A•pŠ«À/þ>w $ÜA/œ;A”}Ÿ%ÃþžM䤯ÚrXú¬JÜþÈÉa*|-O'–»$ÑëÇTr8å/êm/†sáªdõ¸k²œÕ®^ÈÎ-;‰ŸÎ·ÐÉÞW`HEŽÔ„®Íe°Yj É3âF?CeÝ"´8>–m¾4 Ä‚´€|:†âÉñü‡tôFÛƒub¤òûwüæ) àÐã•àèÉ>;­ RÔGøOsÅ7zh‚#³s ””TºçßéJÞ41ë |K´&‘S¶²²o÷ù‰¾Ïée Æôcó1Õ¤Ò÷ìc]¥§xã>_ãš… wamhê¥~¬ŠÎcÊ.è)à}ï>Xu›0×û˜\¹Rw¨U0CßÁ…äƒðGÎ7rX#œ—~F'®jªåûÉzCúÝ䀳+ <:žI?ħq MZ1"@µ¹.ãÍÿàIduÌ=òŒëš1÷Ï|̧'Q¾÷U3dMêÚ¨PdÇÍÈðuü¾ofäêñ]hò@ž9©B“t8¦Ñj2âÿ3¥(ÞäqAu—™ëDòxã œ$BŸÖº“}ìþ„ÅìaùY¤}Õá²®…²í_fSSI¶?K Þz“ŽmÙ|“Ô8r«z*~¸¨À†×@¶y=‰Ã‡™Ñû \*'Áí¸,A$®+χ·p³÷²Td,S9ÑIñÒÌvër¢ž¶gŠ©^æÚ›åFÞ¿¸ŽÊ…#öËŒë广Ãü]QÐý´áïmÏ“#+O´pß÷¬Âöóo ·Âþ{¡dÀ‹Ë|"ÇD*bÙ\‹B^ÂŽ\ÞÇ·‡Ñ]èöÈ2£³h®Aæ¿2sg‘´é*LøD^’)E¾Ñ$¼ Îiã躯Qpš;Hû‰1£œ™ä†˜y¹Hê^}…Ýr‰_.K&_X„›–ž¿K èf&úÃUûÓ÷:ª•ü ’Ý”ñå®?Üôaív!¦ ÎÇ¢NaÊ…'q7O-#›>éÅiBÌ…Ãæ,¨é’åPOŸá §çFâ?áãwšm”Ãe®Éƒ˜¢”¬rfìú4ª?±b}õXΘy\Dt$úŸ¶"³ûX?um…‡¤pA“KŠ ƒUEwf4:òÜþ¡›p*ti;CÊ7=æñ%?ë®mW0½fkz¢’ÎìÆwøøw'~?‰õÝÿ„‹z4HÆôÐ*ø›Ýr1tÅaX4w*‘Ùô€|Ÿ .§ aÎ{Wv`T74:rt™ógúÄÁ’ܘÂvtÉC­t7þ¬LgÓv’uÉŠDd±Ñî&DcÈ<'~ÙIŠèño%4‹›s¯ù#úWo'V²|=y™·'}Q¹²¬ý¯‡2L•dÅ‹cðhZÌ”åN;ÊESÈ?Ïy0ïÚ!”6&.É3Ù©}yÜÏ¢Ðñ«b~mÀ±·± –ðmŠòáš›¦nʃžý'©|´ì:ÜÆ_<„RÝ ‡òh”bµKuÈ‚©Q¬%}>Ô ­$çÍsѾt,›9¸Ü:3\ Å®"Lí©Ä Ÿëa­³ ‘LÅNÏÎÑ%*`}¦NÕê3©‡¼‰çÑœ+Àý•n~«©’fÒkiËÓ±xÓ‹¢å½ ®Þ"aÄÿ­“cOw”…Çé®lYø&vqÚKŽé'y{žcÜ3¶$¼?k³ª?8 I‹>ƒiêq“\»nÿ‚°G8[<…ÝÖâ²3Û¹(s⡟?´ ˆ!™G¹°#¥dÚ`,—ÕËŸÿY’Ù|XB$»jAÐM‘¥jp¡÷È®ç=|û‰½p4Òˆ…²­TùÖ÷¿ç_nj†‰;ßR"·•5tMcıqæüºuDýU,Žßú9lKøU”ñD·‚c¸þV tœšÆvΪ`‘ÔyŠ2K^_ eŸO±=¥ÄðGN‘_F5V=åNP2¤8‰ÉOé€|ëUìåô<áA–ëf‘Ô»’dïæ*n¯»»7OŸœj¸ëÚÚañ–W¼ømºäëÆbiþ ñ¹$Q)–úv5×ÿ–©KÆ~?À6=XKÕeoÁÏ3pvºX|*çZ¤¢¸¥1G±³B—%ôÏÒxÂ%°cƒ^ìiÈëÒçîœá‰ ÿ{ÿébÄ}qÒ`¸ñ2óþŒ Ë\X–ë>ò'¬…(z†Ïó¨úU³ÆSBS~ ,"ÁRÞy¹7y-!Ö!ô©ÓN8k>‘“œ²‚Õʾàß ùÀÏ6ßÉ \J^ÚmÇVÃOùð+Íy‹[Në§ûFñN¾½r‡›éaÅéÄ‘gC^.›ÇY¦Íš³³h^g šm†~ÛDØ÷¹ pâ&Ê}qe~+ý²8šA^ÓØ_²8Õ‘i%œbÄ$ö21–.Ø‘ˆ s.°§¹.¸ð±s'Ý9€'&ÁèõAÌlš#“’y*^po‚éØ4I¸,Û¼ªž§íE®¬è‡Íwý»5–ÉñÄXù} V0þ2¹N"¾_ý€^°É&ÔØö® "´ˆ§ÛãÅ„`¶L3¦ªÓÈxëq¿ÜèÚƒYüÖoa°Ô·¬Û.Ò{ûÙ’ †LX_–Ôé“ѳ-ÈóàlR»Bo³¡qp¯›0çñ\È»–R; ¤¤ò@=BÂfþ€61dìÂê®ü[PÕÊËY™£m¾Áé¥ëIšg1 ÓÙl.E¥‡m¸Áw,;1? j¬·Á($·[ÙäðÑÜùmOÐãÄ Ä_Úä Þ Ü65º.ÍcVÍ7ßMéhÀâ\¬™ž®:qŒë¤2ÒA' 89[–©ø¢ò6yâ³ë,ξw–K¹š™"rhd‹-|‰"&dŸ‹' |"ÕtVßâöLÃû“µÑë²%wÝ6ãÇå°@RòO1ïXöþ=_U{™Ï­Ÿ,ç|ò1“0U?'¶‹çB>ý¤I.p»ZbÆ’ µBðñÒ¬ÿÝÿ5©â¥ýR#s/vƒos3U=¤ ½Ê[À1C‰¸´ ‘S®ÄfÛ;z-¾„sÜ/¥7»ê‹+ñ”žÙÜ ¢dež+1’L‡‹û×±Éiî$n¶J®ˆaQƒtâÏL×ÿƒÁ;ù¾õŠDò!ÈžƒõŽÊÄ]ó¶ù9AæÊÃÐo+FÆ£aë‡9˜#ÆïjÐ!êu¥pàÝ-<ø6Ç–œâ«5f¦‚—·Od’ÍUì>Žõ¾Ì¡ê‘AdzŠ$Û>¿ 3ÝÃñó¼10#ý¤?šK“yzEXΩìXc-IKôÈù×s=%U‘ƒRÁ wd/Ô\ž¡[ðçÇxøE  9s9Ö?iÀÞ 3ãÉsõ¸Ý¢p0Ëtxˆ 1¬DÕ—ÔÇ~ÂÕ§r×¢@Û¢‹kPÜF¯,? ã¿ã©ÛQòÔ3|û5Œ%”8ƒ•aü½QÏàEåØakqÏtsòÒë×<á¦fZ7 ò£B~¢ŸÊîõ%í®óÔŒOÁ*Ëäjd9Á&µºá^i"ÖéÁ’oŸÕüâá5KÀÌ)‡¹Ò”í,£Eü¬¼ßØýÔy㦱.g)b&Í=[_@”Çoá†Ü±ÕÙxŠ—i‘âØÒ]Šs6>ÄÉ>óQ$_ŸÍ›àÆ-wÙGûïÉoz †MLùå=¼Ë7µHDŠˆeMÀxýt, ÄmÙéÐã<~T&‘®úP<Ø=Y„ÓžŸ„‡c#ÉÕ ½Bös¢k<ñõªxpg{¯”öî±ÃÒŽ‡p¨ÑöY5‘åg”%Ùx®©³97–Xº׳ð@ølßZŒÉ¯4€w£¿/=HÜFõ™ïP>: ¼L·CùØÍ(ëîV1·“x={9¨²&é·@êd4/93‚2+ŽÑª¹U lcU*÷jõ§ 牷]Œ`òwã…ð ðYGˆ‹ô$7Ž€ÂvUÜ3îø°’µ!aÞ$éG%5i{Á?n„gœ·B~B µ5ÌÇ•õ’¤°\‡ŒÙEP"5•»jù¤{ðÀ{ŒKlâìgpOäÎá²ò»<©•D~ÖŒT~ÄÉj¶ ¿Ó>n<‡E]8L®«ÇÍrCðòã86Yï*h³ÀybÍšó´ßa{é-Ìf'îÄc´Y†Î\¼9S” ú¬Aáùe(aíWoàœ%¯éœ$–’CÔ‹ŽÃ‘­#þ¯Ê.P¥UÑÕh7óZ3Œ©1S˜îѧøxÃ{×2…]|ˆG+Áh¢$1Û-ËüóþâñØ0b)ðTãǰ„¤ã8îÓAbÿµ•³Ç›d™‰_ûy=ÔÅ_È’XðÎÀœ Ù¯yø¼h4±SU&¢ÒÜ6ҬòÁœË?Æ;zÀš¬Ý)¬¶kצ ¬%Ær²I”ô^n[÷[ì.sa? á·Ël8ÐÕ‹åêõLˆÿ OÍm¼ùï ´{ƒÞò›Àÿ¶Ÿ½¸2DžU¹F,®éù¼ä0ûµö Ü2ã¯øÍöcGìOjÇîLyvñ©=?ù­+¯ ðîûx®^6ajcöñþ¦(`ú'¢ù¸•é8Gÿc™¿S…íLp`a&õÔª?/Ýõ†ö]qä¶¿81±M$mGEØïµÈ¡Õz8¡Ú~…ÚvçâØ1Œ)[6݃›J®eR88Ÿ4‰‡òw&!CÇCãƒ2jþ‡&D`ܤ±0!” hÝÅüÛ“Ùï•DmÛi|bkÌ~„#n9C›?g¡ÂäN°þšGvkßiY£V½§ÞözDòÕ|òÚê0öéäzçÁž8‰ŠÐ$kfflÓ¾Q%7þ2±„£œnÎßs ÍVÔÁ·üéÑŽ¤Â»WïÛˆ¿B i‡amû*Euýïü»f×!ì2ç,WÙWk£±ö¤4[~ŠGx7S¸.÷8N(J„UmÍj«‹0é„Ë.ZÊžÙ8fsɽ–Tšúon[9ÎʉW…¸¿Ù€m ÞŽý…údngNr™juÖ:=Óû ÚaÙ»з1ˆ.šrsŒµ`ó²w8§q>þkêÆ»Þy9¥)dÇ¥» ?#™5ß bS•³ª?åØœeÊB‹|ð}6•:ÌB²mg›FÀB·X´4œ‹K¯£“‚Óøx—.>íƒUEÌt×?ªÕîKº¿÷ êêq¼ô|ˆý‡’Ü Oqõ­0…'ÉÝø©9\µrÐ`«ggÑ?E­ÔA0\Ý`F([÷.‰„¶»ï鳉Ô`÷úd¡7»½w3=< On¨²¿‹²9«äe„_xþf”òOîàîªB©Ä×¼£þa¨íc …ØD«st…® Ùe_J§eBNžþ˜HHhÿÐÕhÜbìEü’L([\ :÷ÍÙô*¨Xñ‹ÎÒ}ר{¼Ïnú[ýpÑÜâÇCüûe)¤N@…±çÁs»&ÆâIÁsÂUxýj5Ü>—€Oô ¡Â^éý{f}s¢ ‹É_[êâU8—ß‹-Ž0tY{Äÿy}qaŽç²å6¹TQ{>‹°›N"fH¿SV\ÖÄñ츽XW™«Î£¨Qã…!?á²åWàiê t:µæ™ã’âVn ¨–ûÓãÿnr†æÿ°hІ•Áûú^TlØ&žãqpÔLvsç*füp*ì°ÿƒÍòï`Öái䟹+÷ ‡w£Ù'9Ò#[ “I³ÿE8¯þ·åPÐx7†ïtüv/ì'¾ù?y‘FÅüÍ=ý0ök 8 +Òp%yb{ …îYT2£—t‹ÂŸJxZaÎ7[(‡›Šã¹è§SFüÿrÜ5ˆÿ$ 7]RÑui'~ŽPdÛ.¿§öNáÜÙªÕ8)DŸå¯çÊôq÷º‹aìøÅØ•Ï%íÆ‰ßÇÄà8·ÝyºµÁKîlÑWÜ)—·Þã6/²"Ÿ+?òËõb9‹^öüŸ%‰é<{š`ÁáXP2I¤;5D {‹'^•? Ÿèò(QÖN`î©_ØôeüK„»Ú<ÇöH|7Æúõ)æ=‡rUtc^°õUÜÔ„dþ»'`[Š_#Ê>[j ¼Šîžƒ5ONb¾§éø\Ñ÷*‡mÙ@÷}HäøïÒFøo¦×^¨ˆ%üDß—P¹&¤Û})hɳcwâ1ØÊÍúeCn(8¿;¿iWÆz:Ýß™Xn˜@þT([—•@yyý鮕á1lò±J¼|Ç‘ˆm s.äáTÇ)¤ùm6UZ©I*E^¡êÇa™øÍúh¢>Íö¤óoü3žUìf%‡ïX}ùöa=ÎÞÆçÞ}Y3Ì5§°Ùú °óÐ F±Uø]«k× fÇB‡9×´±Lw¢“´«öGKòûo>8ß‚µ6Y{uâ«àÏÃnèS9 scYͼ$ë¬Æiž]hsðÜHþ V# †çhÍúS4"-›WwxQk|CeW›“¡„½I‹££f°Êè—\‚‘SX4šuBÄ\™.dr¾+É”Kì>n`¯Rî€y;Øß ëk„ɘ‰×ÑòÞ8ˆîø<éP”¡zÿc.Lîö'R§‹8ã~/"µ1ŽL©‰ö/Cväg½~ù8VO;F¦ŸwFí£CÑÓ!Þ÷Q»e‚äïØ$Éý’õ³ááBóa=øœ_ ×_mÂÇ.sؽ{¤É¥笲EãÚ(éï­ù1= Nå%ÈwÃz<úZ$þµœgƒû÷&ô/­e-(óÄ*s¼˜¸Ù.g.4àL>ì¼ö‹{9p§™ ÂõìÝ`yI†4to¤k*ncö~WçרdË…š&Ù~ýü¡žC\ìªQäVŸØçõr³¼³éŸã³°Á_ÎÌÕ74¡>²o…ªé$wÁ<7^Ÿø½ˆÃ)±Æç¿‡Ïw)h¬úgÄéWë ´:…mii˜ìöKXÂç©î ‘³UwÍ$W–?å!ÃØ.Cþ}_Š+£Î𛥄`Zr+º·ÇÑsì:÷xU ¯wª&\9wWâ&x"ô .øä¶þÉÄ»² §®Gƒ#Nð¨ñ&ò•Jp³­Åþ)ïÖb¿ƒ…˜ÛnæÆ^ÀùG¹|õÊŒûP‡{Ô†èœfAëz!%ö­N"=uÉÏB ºïBŸÿ"–b/IZBš°{Ëd¨,ªÀòç°úªûpJäí~siË7“=:ÉØ$™Ÿ¤Ï±³vï0ðKw}Ñ|kW»>j6W„EÉÕqµñÏip¸#¯ Åu À]‡¼ñ'àâf›sŒi^Ñfg4Á½‰‡1Ow. úeA¶[9МÚ*.p^;® ²†Ù£oá†ø@”±˜…vÔRø)­+ŽÃ0ž ž0šÍ®å°b Ê­šiÕZÅÿ üJ¯áêN{²7u6Ö¿y‹a½®OáôNÊ“ë?¿s†&çÖ‹¾?`³j!9ñ$.R×¶"iõ"$’?éïŸèì¾Ê D¹B•™˜½Å„‰Û Õ4ÉEÒõÄ—%nÓA›šHŸ=‰ÄÙŸÀ´OOù¿*N0Ã:Ž,2“$bwEðœ¡<®/€îKÉèõSÉxÓBªã¥šj}ÜUw‚rI”竚…ðb® µÓ°I­IxâüîO¹ !KNÀàÊTÚ# &ç÷Ó¼}lšâîÙè£øö«&ãÁ¼ü7=úguøgÖM¯¼…;õDSô0½\£NÓ™|Ù.ü=õßéþ4„J_ÕÙŸ×ïøu“à¾<ÐÍ›/:6ŽÄ¿fÄ6öyÐÞ~™»ÂqöS|f±‘•æÞgYØþ†ÑO'Rq}å'x|*›}ÙÇiîäo[ ”É|GÃ_™¸|™ïqI)[?éVi¦ŽÚÁÄ[j-º=i¸Æ=ä¹*sþÏŠA&ç§ôÏ—|ŒÿÞ¸Qô ›€EÖãé®?²Ì7pدÞ-g‘.‘Ä5ó3z•å /w ;6)ˆÝ €êš(ì¼oX9“æc ;Až›·eên¦9m5 þ Ž.¦Ã"Àû&!'ùw’ƒÙsñdŒØkÌþ˜”R÷ÃÚìÎÙ$ KFøl‹®ôGÎ4þ‡5ÕƒÈ,"èa¨©õΜ–Œ$»ÛœÏížRÿ¸§ÜÓöH·Ã;í¸—6å0 ým7Ãe'¬s°[“jØn+Â¥˜êóJ¨ewQòA-ˆãövsð)û¦½.¦ü XÞÀ«[’0eÌ0Ó÷8‹RߢyŸ¦£÷_6>(£þ¶Ñ1w²kéW¹ø·¡(x¾ŸÊZê¹Jï×ùcc¦‰qÕcíå ÞPc…b~š éX¤ÈLuVÃÓÒc¸`Í1¬\cAÏî=ÍÍè,Á‚]ÇQýŸþÈùŸµ‹ý“+‚wÏBÉb+(g]ØbÎÒJàÖÉ:ØúÕ ÏíÍ¥W+C¨­ã=8?å*M_¸“6>¹Í™tE¡GU,”é[ÛVæ ko4¦»w®Ç–‡ç°}“5Yw<ßúÇÚa¾« N)ÞÆ–‰‘Øf÷Jùú,fÙD¦´H^äl‚&ßh\Z?4†yB8GruåY£ƒ9Ûáè´X™½N×gûº.Ñ«²ïñT0žóâõ–. ô#i±sÊžsoäJcby*÷uÍ¡shñƒ¼«¸ù³IûË^bôq1~ËISñÿIÏ%YùÝ[|#|¥2z×P+¢æ@þŸ¯¸án6ž[jNî ,"º)–`ðÓ,ŽÃ@N„¢C‰›“6{cR‚Cw6Ñkƒ'Q9Â^Þ>?by<k²Òs{«½÷N6@8PGOébÍcW²«Ù¸¨hŠ ¦’h€ZÙÔoÆÜÔÅIïŸBÎý•{¹þ 6ÿªµ]{'ß«¢uÖuðzï,¿ë Á>ãÈ\ظ=­®ü;ˆIS ½m›&)ÐkLnΤ5jö)vb bz¡]d Ó¨J&IJ÷8“¿¾#üw×Q8•6ž¬R 67PCuûæÖÊ)-  }~>ÜýŽòóBYò(f›wŠ­®(€ØËÈ?³ó¾d¹}â$(l(´[1!œF×~(9׉oúd‰¹çd¦å4 :ûð ‹Hí¢EdŒÂÔץ쨨8 yïcÖ½Ç7½9‰ÑÆ0k0‘ÙÚ‚ˬÅ[, jM`è¼8xö jgmŽ%[Ò%Çf\1b¯ZAª9‰þ7Îâ“út†Ê¶ïå3x<ߕݺ]ÈMöÂ&Þ|ÈÊM€âƒÃ¼ÃRƒLIž‚YV&lCÖŸûûÒÿpÎs qAl<£A¸È;™L\ù¦4kKáØ¬ÓO×—¸2Ãà˜«ž€b_r c¼6¬˜O2–~¦’%òtG£S³òa~‚ïàrÞ¶‹ .KƒûÚrÒúýl Ý ÿ!OšÆ±óIŸú‘3‘ÜÉÍwÏÈ,d²~ÅlUŒŒ›#Ávûíåäïǯ‰–ðZIc„ÿ®ùdàp…óäçòLVÆÐQHÂ%arï„ùúºTu™ý¨Fnq‚:Ó²\EœS¿R]pbÕ«rÁÉ~)?Ü\ŽÞFöóµ¡èuî—ñ$?ÇÑ ¡ä¾ã]î“èJ²v’3i@n‡×V·–¤5"†±ÊlÑÙamä¶°]רÉ3UCQ¸p£‘3ÞKŒ%¦6ìØü™è³[ÉëEv`’×dÆ_¥Â¦ÏÅÏ ÿ@Å¢öÀw:J’Æ(ªH¶ÝçàªÆV°x“¦KDSjîCŠÖ³!—&HO4‰ÿOR¯ùjoŠàk‰ SºUJžj$âvwu¼Éxó×ëkØÉrdöhu¶è´ k¹¢ù¾S˜Mû.8;g1õ»[sX²ÖÏåh±t]ÃLS"`¦T ¸t­' îÝéçAm8Ç ðóÑ»w,©»z'&©`Õ+i OÞN´!ÝIwˆç×drçâ2¶Vg› §D¤âº ·d<[³LŠpæ§Áj•KÚ¸=î^ææü󢥦kXì‘´7®ŽùÅÝ$7”¸Ók˜£§nͯ $^½[~‰ôD*²@ÉdoÄhvÁ:ôÛÿ÷ü«wþLŸ¸‡—pã ìY\ ·Ž$ÃÑ1Þ(€Z8~“ÔÑ­~ì:ýÀûùå§uQ‡}ùø²ŸµA„›!gºc<‘wïþ«lºÛtvWÕˆ«tk¢é«â!-g)~¬„js \ÛŽ(§GZy<‹ POâÔÈOf^K²¡Id€^y÷¿žƒÓVÀÅg¡Ij=]ÞÔÑSdØÒƒÏ‡‘‚ÒìÜuuòvá¼Vù.Y›âæ•ÇÁ·Í/y‡²¤z=ŠB±HÖ~Þì1\Âdâ)63n :‹ëãZ%bûpì˜õ¿þŸ w^Ðg¶;p ~lKúëCö0Á&#úörn:QjÝïYKßí¤÷bOó$ÿ\…×Å{Á¯§ -EÉP‹$y¼u1¶êŸ ò!ÖäÞÉý`°õ̘+Äî=_ïK’CT™<«Ïƒ)Ùð²§ ü3cÑsjÔ­l¢ì¢$_~_Ûî@@Qu2õAˆ©û£"q“&ØÙóŽ£XÒñð7ˆr[.O"Á·´YAJøvÅq}.dðÙiÈ‘*ÄÏ^[Ðà—'¼È8×xü÷"ÝòžsL&< ÔJVÇ}JÑ…£Á¢#þ/6nl´µ§x‚ü@çàmþîA'æÑ2•ì5aEËâÙ•3åð·íÎc©2ÃZ¥Ï‚¬ò2$åäù岋–=¤2yò½(N<ñ<´!öuÅüÏe»ÉË T™IïÝIîõdâÙéxRä0±ÂvœåïfOg…‹:Dñm85Í|ÔX›¸ŠT¨’}‹ؾ@À§sSÞ í+QýÕоÞÁ©–ÔÃÞ[j²%š £Çi•uü˹d¦é#lNÆ3Nè@E@ú‡×ùä(мÓ%&k"YIù2вV¦ç—±ÂsŽ[üg&6(²'‚Òì67™\Úñ &.e½MǸs¹jDR^ì¬UH€QœÖpã- 6%}—ïs9ØT$`JŒ½«Ãíy ¦?¨vç{œ¯È±ÀIÍôªœ?cmXÒ½»ØVy_”y“y3TØ=±ø;C€Ü•œNöœÑf^ÖÂäCéošfõ>>>F«”öcéR7´ ]‰ƒ¯ò0½¼ÿmÛÀÕ"ÁêïÐÛ¾J9ãî§Ù8åÓê¹Â/ã©…·7…ÉAøÛó6–úmÇåØìÆ]8~´;1]§T®m‰ÿÜä£p|´2ÎüFJ«ç±í27ᛞ=;zf6Ùiß­—MYéüM0NÁ‡ýó;„_‚7á¯>uO]J.C9w&µ{²Kv Z'FxóZ¹ž¹•0;4¿9«u±9í,×=‹éC?Zhߥ%õÍôE×Ԟľ¶K}6_¥îûpa•33nÿ‹òýžàòã!Ä9²{ µqWF¥¨Ø2ÖŒ´»µ`3G6Ü¿‡k²ÞãõK÷0iW±²`Ëb±ÐüÔ„^Â1¢äØñ30ë¯Ýw0ÇLeív,4t5³¿S;ÿá6£Ø“¹»6à›ü[Ÿ˜á™¦·˜Ó;—;qe¨&>§÷‡õ’B@.ÓȹH:ÔúAÿ„1Iø—…^“à·=·ùwƒš˜ð£•ìÉù8üõs8†üäïŒBûÛÁãÉhÒrfYó Ô~Û‘×"ñÚFWt|úì<Ë·³ŠE¿qÁLN®Š=ô‚ì_øÈztY=áLÈy'qšªçI%§ÄÛ–0£´foÑ&2Íc ¿°ÅOÌ[|èÎÝ¥äôÚ_Ö=Ïâ¥ÇaÌWù—½å$¬®ƒ²yˆŠîAËúËœ÷‹ ˆÚ{Gø¿Ñã`6Ëw[£Gþû ãN4àe[®Yg tÊ„|‰ÅÌq Ÿ GCf3Ç;JìgÁ6ÈØs‚„îØÀ±• Ø»AG"RQŽ/â¯sß ½Gaµ’öab/w~F9n^r›ÇŽ“‹ý žÛÕ˜VÑVbõÏç˜G±þ¢lðu>ycÖ‹n—bÿÀ:ÎúK&áIð:r ü©KpšY Êãá'oéò>œWV‹¢‹Á„g±¸)áÔÖàì¤#ñ_Z-K v\ãõ s£ª;VlÞϲ\±E¡K²Dzî[Hù´»÷“ •L?KW<Çŵåá¢'·ð JlÞ.‚ŠK§°Y›ÀÍ¢“D{Îa®÷LR‘”uÜÇløk–O$ú] PþØ€÷\¶…–ÝÿüåâvEÓ=Â1°û›Ѽ¯Äª“søýt,t_6&«ŒoSe ebfù åU™ÍT´«ÛCÖH‘ßß¾Cæ¿\P/]D„7rÕI§ÁÅX~´:pù?ŠqxŽj·ÀS,Vã}}íLôvb×R€n7äoܱþ(}fÝP‡;ì^aîÁ³ þ°¯‹»B£Õ.rcK ˆÆúqõšøïpÿœÜohø9£ |ÈŽÙ·àÿ"Þ ]–g¿“¨–dŸWlÅÇdzGÒbL×è wp{8¹zy,ù"îKŸ¥Œ@r:3‘dÏ' FËX}¢žY&ÈÆ‹¿bùdVV˜õª_ç1>j:-Ý*k”ÈÅ-c‰dôòúÈiJ¨ÆÖ±iäÈ{e¹ƒý÷yA\‘"oüoóömN ñ'ÃAÛ?Cv;Ÿ…lñÌ™LæN.¦Îäm<9Ž„M¾ÃìƒØ?i/÷µ+¶Ç¸2…È^4+È6sòàüÅ‹í ‚¢×S™Õ‚ÇH"ÞÂïŠm„mË^¨²³ôùxï-.ñŒ Ë[˜Üæ#8­¦ ¾;4`‹˜MüdýHOiùsÚzoáàƒï\µ"êß}¾®Ý_F¬øÃ%´vò'Eñäæñ>ª$îDrîçƒèý,mm4™~?‚ ¬.$ß*!;ÐŒþhKäï?u”_÷;¼ÖÄÎ#}€òdŒH0ù¢“ËF'¨€sã#ü-$ËŽŒ“cÂÁ¹¬èÚVr¼5ƒ)nÍÂk§1#sc2äzp$þï…S–qߟôõ×áÄdx18†Ø/™OæJ>ÆÉ£Z°, KÏì`†‡Î“!Ã?Ÿ¿^÷ãîÜçœë¾Öóyß×}5î»ðeš'½ÀÀ"Ùäò½–:¬¯Þ˜Ú´£s‰¼X%NžI–¤7Ù4ó–27ô˜„^cŒù¼Ë‹9Q¥U{ò˜¦ä#6¿Ö6P¡C"ÛñF[N KgK”-èב•¸? ƒüuóÿ4ã¥7#ñwJ¸•W²©eq爿dýK8dõPŽCüq§¡œ,CIX»ƒFÒ‹#†²e£>`Vûpºå39 ½ŒüA¾ËLÕ¨ÞìD,J‰Ã^ •4ˆÈš@ÁÖW(_)†‡óº"®x^’ö¶ŒÁëžÏ!u„)ž9½vÏ[þÿ)-¿o¢ÖcßXñÁ¼qÚZwÞ8÷; Ž|aÎ3Ï=»Õ1h†3?ÕNƒD¹©y5ZvûóYÙ‘|¯`9.;ö –¸¨ñeiØ?Ùá¿ÚbÁW{=~`[0œºóÂåÒë)à÷§¤”aã¹J4{³šænG·}:î÷þ¦½:½3ÑÉçÆÉ~ÄSOOáRëÉôË Yœ;&¿9Ûÿ~EýÂEÈ^aʺßàè0 ˆñmLjgŽØîø&è[­k7ZÐ'MX_³ß{c¥rAªõ\ ZR‚ï8ú}&¼Æ{üEþ'i4dSÁ]‹kfNc /O ´ÎÇR1ßD"UÝ úš.;fÒöò¸1ø4®t–üÐöÃhˆú*ƒš/–ûÔu´h6¡C—Äé™ (:e=9H‡Û7¾Á«ÍvìÈe}œs¢«7|œ ø Çš3átÙI&a£ÈÞ¦üÁØL Ÿ`ŠÊ¶´ö!1ùÖI Ï· /¼‡]f˜‰gØA™nAóÇTI 0ý<»Ñvw ¶œÃŸÁ÷pO5èÊ’õËgÑí;þƒµ.ähR»6è jÍÖ‚¬®"&Ý®¤üOõ{Ìzò=d* Jy8¤ï‡Âõ¾P8%GüÕ ·Yt‚çjÒ¼Ó¢ñ=Ó7CéBŽž»ù+/Œ?¹jïþ$0LwIÓßhðÉ58Ñøl_ycúrÉÐÝ·â•—|CÑðúÏY5‚†{±e·e#Õš;”y³‹›Ü€÷ xí ¾Á?ŒŽN´‰Ä•4úg ïZbI1Øšî-ó¤±Ç×Òˆ¢ylô' ¼„í›ßŒ’ÑñÜó×T~Ie+»6àñÏÝéC·¸_äyÉk˜E<á&6T%Q(œu3ãÓ«Yº“¿¿ …Kó%<Ô}Ý-v‡ä]o€ßƒ›Ð¬å!¾Œ\Âó÷vàÅs;03I„^_,Ï3í¬ùuÑeÂk(.nL‹–óäó¦¼>ÞÛÞA„Ô1ìmÀ†Y ü¸ÿ :Ér>™"qÕ¬eÁ¿çó?‡Ã®j;¡âê±?˜–>ÿˆ¯¥š¡~Ãp:è°.=3â;(¿o¾®‚YÅPݳï¦k£âó¿xdçzeÓC|:E¥äéÒ}Àg#G¢ûq³Ÿ!>Õ^ÊÿÎß;­¸TÂn¬®Ç‹<ÃiÝìÐ’#¢É³ Ïá ÄŸ¿ .öýX7À£2‡¯¡qãgâ5ˆàÛÿ¶¢¹e,ü\ ‡ÆU¢ó–(šC£ÏˆâˆÊ,üRò…˜}(蓟À>FÄåeá ¾)‘p±p ¶§»UQ+Ì¢³ëÖpƒNcü£ñOÿ&«Lqõ ÿ Æ}Ô¢ð€òm&SôšGÑи ´ëÕ ¸py0+!ËëÔß@ŽH(ß´¶€î¸5†uTbL_ ¬)á‹^‚¾„ì|)|µNƒ®R£2úC¸ùc®ÂOÂÞ†«à2Ó•Ÿ?á=…¤M¬tÞº³pÿ™$¤æ$œšF¤hà—Q0£ò8ÉžS…ŸÄî¢{¨*¦È¾B]me:÷[<´'ÙÒ€#wáфøc‹-¶ÿú˜ðÓšÒ´?¯LSW€ü±·°es"Í´ÈÆûÍyMp)b ?*DƒÒy§Gø¿üŸ²çñ>MY¬ás¸²­¶~Ï×—B¨ô $…/¥ø³í–°±Y“8k«ó‹‡òàe†Tñ×V.ï¶~®H„Œã_Pâìx|( “¹;~÷•³/Ñ<Ç áï‡Ö0qý=agÌïw8Q¡q +–·oâ®Ï‹!b× èXvº÷~Þôíb™ÙÏàäi,qʲtÞ#ËóÕ¡Y¤A£4÷9c¡‰ ¿oþ >< ã“=ºÀk÷'T{¬¯‡œ†Á]N¤æÇ@}8ŒÚïÑââcìøEIiî1ä rM?ð” ;ÖÿÄçoG³©ýÿä_žscFndCÅ-ðõÌ1¼ªþ ¹Q4¾jtºcÁ–7ä?ÁúK…0¡ü¿‘ÛŠ#w`õ´hß‘ ™å¨Ë– 0| ™cò0ôE*\¨~‰oP´J+Ú„s\ ë=f‹öÜ÷ê³ù1í7,pÐøµ*p­i9]Rv‰šï=?¯ˆs‡ª´lëYì™®Ìn]aÉŸNÒ¸Óykj xiO¯òû4 šß¶¡Oº}Yxz=gM—„;F,ýiÀqÝÖ±$fÀGËfòúípšña ÿ[ž#MtèËïo@º0ï_ÿ“…-2ÂñEˆÛI$i´8_=«‚ ¸O)†¦|bôø0z#h½M€~&‚+·1³»“\É•ƒ%¢[QÅLÚóq½ßst^ŠcÈÆ/mxÔø¢ð›p?¶ m’¥#*w²ÌîµÈµPwÝ{註EŸƒu[üÔ¢z{¨Æ”&#³Ëà7÷Kði•2µú=œ9|“,]ª º4oÿŽv9h'Jú„;PdªРA'I±?ŹËi9¨;©ÄW„á+¯ç¨*rÌ»P×ÕŽ8ß}.5þûmD˜\â÷¹ÿâŸÏÚiÐ{…%íšHMeBqüŠqX¹ïþ|£0êˆ Ÿõê'¤8ˆ ßó»ÝJi|¯-5UjÂØîxjÂz\²n,÷9ÑEj&˜ÕëÄh¶,®2˜vùEÍËCqæÔreá‚ÿ~Ù÷eå–u'j>ÁíÒ† §^—;=PvNžUÙ&Ðþp—XMÐ\’Ckü$œR½–ÚªàÀÜ]»Ô·<ÂÏn·X»•#Œ¬ÇwVjü¥öT¢øz,ÿÈBQ­ë(×—‚õµÁÜæU0^¤¾¤öáM–ºÑ¶xî‚; á·áM°OJøgÿ*úÛÜ’¡fl+qo“ä[¼pÒ’iø‘ŠáÐí'!ìG9SÎ|€¾»£ÈÀNž1pÞœwšÖãÅéps²ˆ·šÀO÷&r½ý6éê$Áoíu¸YêX$ ^Ǽ{°¢†âãòvÒ Ý?œ8ŽFuŸ‰Áͱ`[•êbþ ¨ÑBŽÿp‡¨#ôŠd(A†ÐõÓÄø³›šü]ûc êžA90wßrÔ»ÒMîj΂H×0yÇ1|ðz;¦–…@ýwžñ;‚ØeîÿJ ¼;³eªo2›õg@Aý’àL—Á?üsäÕQâ{k0×S‚ÛI·àUy!x¿0Ä@§?xaW4> “à¢kËp†¿.N»s¬<²h^«?ܸ+Fëo®‚ÞÖͰE|°zÐbó"› QÌdš–—ØÂÜ|8›rXÊG¡Y³ÙzÿïÂâïqxÖ+[¸1aއ/6ü'y]Üe>€ >’—©ñÕCäú,~HyŽ4ýŒÍ¢pÑ[?´Õö&:K¹õ ÷¬lä ¼6å–X÷6à ðýÜ+2°ÕÓ=&3ñs1P›ö cŸ8 7ZœE?Í|"ÈŒ€×ÍZüœ ºŽØ¯·Ša‰®t‰†à[çPs¨¾0wúYŒÎéÃ[qㆠfL .ÀlX uñ é FeT—ÂÀy(ÖÔÁê6 "añtnDâ“Wÿëÿè0‹·ä0L  o–L€rg-~‹Cóåá|©WüU»’›Áû°2o=–l©Œ]/JÕê>±h³•°oýè󺂥?ÕéÐ÷oX¼¹ŸôÚL2;7;Z+ó¡è1£U ;ø!L1È&Fáöè*/AŸÆµ@0l¶¯‚ #Ó9ËT¨Û];Yõ‹­a­B>ÉȽ Gú.m)+¸ÖÒwÂ5Áröʲoãi˜?i@ ,©mÅÍ70`]íU b7Œ!cå–ñ|(ÈM}‹ßvíxSç)ëhþ'üûðìåXÌ8Kîþ”€/Oa¢Ä(¼U|ËÏ‘TÛ ™2¿9ï±òˆºUºIc÷úb è$ÖÓ… ¦iØ2üy˜=•*ö)Ó…î©ÂñÅŠ.3%‚D댅Kï ˜œî)á…QG m¬—ºkÁ/­^"’UŒ}o㈟¨4½bªpò»&\;ü:ÞÉ-€ð¸ ù]Œ3›ýäp²7~W i‘øc¹$<ÑM`Ó–ÃÙï7ñéû¬`óq¼¶ò/|<-Îå á§Rµ$42ì 7Rè`‘'·ÂœÜr˜û·æ_þûs,_]•¶a‰àíü—½œ=bçŠð¸&Iœñ=ÚÓVÁ#kk^Z¼…ãÁËÒ”vvaÇGîŸx 2vná ‚nà÷xE8`—ë&΢¥·g D1¬,wÁ W•èѦwøüÌ~7¤=<ƒ§…î)ÿ† â5¨µ¾/‹øÍzÖ$ãKƒ4¶l7ižf§Æ-{bHVKâ²Ç" ?†»5¥x ¨œŒ¿††¼[L^=®ÆC±©0o®&Évs§™V‘À¾%Á¶D]~\5® ®`Nã!VšddzHS@ìßûÏ>ÍO,È6š™Ù‚g¢âlõD«Ì ¸ëm%œP»Ïiò«>AñX(ØS†e&õDg /IžâØ1ÚXx±:—Œï®¯]Ú^% ÖÝW¡m–óÀûÛ0x÷h±ôFÇgôÇ–+ÕUƒb ,VЂÀèÉ8µ±š½:Åû ‹Ï‘‚šZ¶Å¸Å7[šøç7J³&,ðû9Ò°ê}'Þ–Óæë Ïâ¶7¿ÙUô᳦ÝeßeòÑYàÀ²—ôÂì’ýø4î¯ËÆ!ðqR:æZbªG-ÜyA虤—ÂobQaõ4Û<áŸýÿXòƒX¼^Ì"¬þ’îÀ¡þÌcñßlñÙo$*sÖµ*ÛË. ŽëáîÆð^-C??Ê[>AÈ7¸FT ^}¢ W¿`gf¥Â'Ÿ­p£*’ÝØßrm УTü!¼Cœð"Fõâ­‡`ò·¨`R‚þþ#ÿɯNQÀC¢ŒÍj*‚þÎÉØr( êvNåý"QTöl+~†ÁE¡¬Ý!ÝŽ½ÅôW ¾)±ËÞ¯¼p\°ÔJU· žÙÔN,Sˉk¥7•× ã¦çÃ5/¸pO‹*õåšÖÏÐ}½žÔôÞ­Ë0ˆïjO/¶ŽãÚ;ÿš²kh×}ˆÝùà ¥÷Ñ:“I¼8û&J%ÆÕ™¼©îVM_‹“­åÀw %Q‹"¸ç©"2qQlòÎBÛÌÌ+W…ÿj ãf7¡´ìGVµNŽfŽºB\ÇYàöKAÿâÿœ«Rðá„.¯öуC¿ðïÏ»pDG‚väÒáÁ~tÛðQ ¿œFß“Ã_Sé%ÃrœqEç¬À%<Ö¦o+L'#µ?€äž1ôAy ]8ú¬nnñK<óQ:ùìE£½òw«¦pée›Šê`¨œ,Õ2Ø[ìÙL÷®…*Ó3¸ëç¦ü·‘÷†8ઘ´RY…&ÞÉgu‰¼ýZßfM ™´ð ø.zäÖBhÅöT5oч–a†¸Wd=vø¾·¦iƒ¹¶£-ß¼á%¬ßTËÎ{ò™ZO™’ú±øÿù5/VôðŒûZ‹c‡`ôh¨~ßÍtÄG ÞÝõá FoØ­£ Ü©i€ó®¤b¯ìظMò˜Þ4„.w[ÈŸ6˜Ð[Ë⎣´oZ Û®ÖCÛ #¾ÆXÊ£RÕhª mM‡Æôàˆµ¾ì—íyò*bý\é¤5—H}l0¦Í6@ç/Ílö§«øó•‹KåYáLq~Ò S}¹Û©Z¦xW8ÍÑŸ+MäÖ7MùµÚN'7еJ ì+DYßüiñý_]ƒÂaØ&Z1_ñûóÕìþ #j\æ ÛFëSÛ§q¸qŠù?ûï8;¶§Ê‚òÛN†ñB¦2yC0w ã+f/¤¯kò^'˜7+΄j’5¸!QƒÚè ¥'.݃ǯo2K^óÇ æ-LbÓ˜ ºP‚ÒGžÃ‡ÇÀ#À¤ë²“?’ë®rÔOÅ.â©€ËÐwN“~;¿4uÏ‚\Ê+x÷~$~{(Uî^ÁŸê¦cï÷èuË^8ßô êšÑŸö$C!µWºð癕°ù¶wæý3Íã‚yjâ´`ßx¾£Sw *Âxµtô9ttvšÃÀ:tÓ´¹ô‘G«¾þ/ÿÛ úIúå âw«_*[ÀQçÔØ½ ƒ·¾Ä1×õ@7ÅŠ/ùþêÇœŽIïqãÓ­$xçN+zwý7X°á8}e4”Žþu»ò—r•àPvcÅc¸s'¯úŒ=òz8bÚX:>íŽ|Ž“%1¸¦Æ ’on¤—³ ÈŽ’I ýÊtø¼ý éõ]Eh¹m-,Ȉã Ôùˆˆ®w2s«™Æ¯ÝräƒcÀú,w0Rƒi±Uí C¯àHh5ÿ)L¾oE—ïÛ@JÞ_†ò’|wñšµá/Ù÷¤Ï+}†AW7C~ëãòK®7íþ —Œ#®t×C 8uj$XÆ—ž>…‡hë%|¿™1êºEf,‹Åã7y©Ë>>¯-fKK³äY†¨>ÿ—9ÔÎbR‚hɺ_LÿU4KÇ€öN¦ß»¨ET! ¹mÏ/Ãã¯@—èHœ*}©)h?& ÙÃé4‘în¼™%ܶäêÆÒ«ªIÜsn4g6ŠŸËUƒÆIILF˜Äkú’Ø€ 4â^ ;³ŒÝ¿‘H&oSç^¡ øX}$,ŸG¯¿?‡‘³Nÿ{þëábGòy>¤Ù‹Ð¶~! Ôý(p:¼mF&Ào ž÷…˜ò™ü9Ï8iÛóo±2V—©ÝF©ð i<“eÆÂþ‡¥ìîœéñf,-À¥R*´çèiáþþh‡em½5ì›òXú¨¨æM¬¬Ê»0€ñ$Ø&ýf‚Ç“å× ð•‚6î·Cñß^TC½]袴ű¦—Ì`‰rók G} ß¤ ¼¸þ;´O˜5öZüBçQ<¾TœhJZr—ÎqܹS×·…‰·_â§“uøöUFºáëàz˜¤þýß÷O–ÓÄàýIÎôXTS=¿Ë#Ó­6ckÊ/(¾ÿÕBu¹A—,ì¼[UrÌÖ‚Qú”·éÅï wAaO ~¢M-G@Ni 7ÿ¬ ‰¼Èªéè]Š‚¦«#ùV0åí ÝùÂî|q­ ½wÞ€ÕéÖóœ`ùI4hË\¨~‘ݹ«ÉçJÔ¢ù ¯ÝH°ëÑ ][h©»¾$u/HÑönÆ6çúSð]øZxÑßxòf^EsûÁiÕè?ÿ‚å“ ý 8wS³º†SÁå84áAÁ8Ppñܰø·Èg«NR -GñÑ¿ÊØœÚ|ºÄA*ïèAÇ^^ÈgL|&8,S„_[mAµ?$ÅaYq:î4¹ŠçzZP,׃Joü }F¢—>òö‡Ò[ãk˜ªb&>ùPƒÛ<0s[>l¿ƒªWLòÉe?Æž×H|û(„ ËöÀHs5ºöÂ>0žtõ' ›õþ%O…ñ>8ûÊ\òÃ^Ž^ŒYÍ›w“8~Ž)Æãœ§Jç¦&XFtãwÍD©\âr/}1O>Á,71Ü°Ö _­V¤{N*P³/Ëù>‰+(okûþ'ÖáCdŽ¿89`‚’¿ÀyÝÛ·øH¢{0 TÇèÑé4z[¦óvcøîp²@_È›ع‡­(ùL æpTsÐ#}Œ¡Ü¥×îXI?,I„!’üŽz’R~¦¬»À!'ÿLš‹Šçƒ;סã3PÙ³”ÕÙ»%†Nº°¿8w:/&ﮎ`Ï;pØÁí¸±h7ÿGrnƒÁéi8.r ,}‡L¸Î4—ö­Û 2›„õÖIèTg³ àk%6Ÿ«ÔÇç³Þâ®ç^\'Zäîïá_vÍý'dTz.R‡V.Ü?HN•hðˆ·‘ø½¾o5¤Ÿ’2ˆìˆáh£ß…C”ÜpsÑ!ˆ10#ÊöüŽŒ#}vs<ØíB·QA“ðá“ 9º[¨Hèf²îº×kA^o‘¦ËqŠS þ7&ïôK´P£Òëó?™,eeŸwƆ¨ž†Üãá[Éb"ÂãOÌ„… Ô«{+D(¯€OÛ=Ù„¶F8°F˶<€`É(Xô%ž©Ä­ã ÅÍ2¶’8ªã£=·âüÁ1dÌRÈ9`NëçnÇE0œ.¼¾Šßù—ÿ•øp†,°ªH@ÛbgúÕã+ .H…KnøÊ<‰/ßL•^\ÞM>ƒ"Ž¢ôÀ OèÒŠí_±™Z„åYgÈœ@h͇}~b|2›ˆuó°äÑÀ}ª8ksbøFÁ»K…¨<à;mN7ÿÜe2²št~µ/*k=‚Ì ¿aJ‘í·´ #¿µàSõ¯äÐD~\> O<Óà ßÄø—Æø9ã#šŸ«íÛ3Øì‘áà7š=äˇqRT¶_‹^oývCf°¦ƒ,©=˜u—â¶“2Éûbwº` ‚“5¸³®=Ø…ÿ¯ÿáßH¸òh)~˜PF†m-à7Wà–¥6óö#Wãa ÀýÒ"Üwd"ùâ*Â=Ru…_Žûy NM÷àG"ýhÔ¯rxh´ƒÔ·§|‘>üš§ –¥g@wëo,Þ=š_ŠØŒ‡¤¨ôJ{~íLîÀ6Üóëpô#ƒcàê¸Læ)=œ}î­"ƒÃ_á:͸è.ÉSºõ¸tñ6àûC¥ß¢¸K=¶9¢±U›qÒã!T×¼…¯±ÝÀç>šK$ âé´g­˜<å \#ÎÇ <î̚ϗ$¨Qÿûj8çù1þð¡uñ-%“rNþ³Oõ?³[*øÔΆš}ÖáóM¥X@H ÖšCóÇøæOû/;ð"÷æÑÅ×T¦TI*4/ËŠÒÆŠT2ãü4Pqá îªN…ìøŸ{0«+î+tEµ§ŸaVðF>®ÉLµÜa}Ò/h­×„š¡ÄPq¤U¨ÍìÏB Z|‚ðeTñ’J8¹%<½+zÑÑÔ•+€Ë›Ý°%õ´ËgçöFW—›m˜B@ü2\8 4‡c.Çb§ôsÔ/†{.y¬ þ­²\éÍôyš‹Ñ&%Pä¾_Öˆòë*S‰àJí?ùí¬ß1Ë“®xUìÒK%šc\êΞù¿vô±ñ{r¸l­`BðZ¶Rº':7Bû“dîçÜ!Î4m¸3„ÅËÑÁ¹"Ø~‹\ì'ô¸ãAHMqÆ5WÍ@dñ˜úÆAPÿc(³`õöhpx£ &?Ÿ‚í˜<þÌׂ_«r¢?¿ÔÂuÉ|¡aª=ר¿þú¡Ï¯;dï‘,:gfŽmW"§ÛKß]Àd“î£kAj‹Æ¤#‘üî‚~Ö:ÕVÙÒ\ç:ÌÖi€|U{px¾²ÝdÑúàa¢tæy c¬¥øïà¥tÞ”‘ÿòħ%¬ê†)$·†0¾ý5å+UrnRÔ‡G±8:L â—Èð±)òP[÷·êÔ‚8Lý}27ÉÒaYÝhï–Âü˜r±òP³_…»ÏÍa:×< ÚΑº½ïÁï’)ض\ªT•‘Æ¿8M ßßv‘ú½™fÔbAyø^ÀËMøë•rÔíÃBôx9—=dÍ;]ºpò”dÖk'¼¦üˆ]ón‡—Y¯®î¬㣗ŒæôÏzzeT Ì|bM-¿Ã<;‹Y†ºE³@¡Ò—e(ÑC+Íéâq:tý—)ô 9†3ËžüóéUGÈüTM¾¿}›[~Ž—-vñ ‰TQ}|µ3¦ý¤hÇÚ¹T÷2M »I7I…ñë½xF¶‚^1L #lzÿ9Î_÷×LæÝŽ—ñ\k&(o4†Ï÷CpË>Y´¹"Ã×UÂcFÝØ¸«›ú”PƒŽ}×ÇžÛÜÐ㯯ã™_}¨úJâö¸ëoåÂúøÑô—TdXÔ¬GÃýÛÑuózêÙ}”ÞŠÂÊw«‰ÙÑ\*ñ3–tס¥ýsá<G¾\}ÙhÏ‹|'qÅåüÅ® ´CÑ#ÖãTé³(öQÿÿݹ~¥àúî9Øxv6ïë‰U‘[¨Ùø`>y\5íÝ=‚÷nŽãÇž¦’­häÌwÄÈó Eô¿úæÓ­F¼YóN-– 7¿lå:*‰xcm¿ípÄÂò¹ß‡¡ÐxÛŠÏ»zq»ŽæÉÓGÓvò‡nÂõÚ1\IÖ‚Ú—zÐÍÃòh5ßcëÖòýƈ»i°j7ÿS[Ä&ó¶ JÏO%aR‰´|Âp~ÇP’­ˆÉãeGù™åAÄp¿*×_#F%œL°Ë7Œöl«ë]>äa¬ =„WÛ :}Lpuzìn[¯ÿÓÿøk–Ü´ùikz+4oh‡äÆ»8ÿ©}§i[; huV*Ÿ÷™ÜP ä?_Fñþ®7U#·½ƒñi[©Ô¸bá;ûdþüi2m¶.¶5Ü‘• W —ýmÿ Ø`¸Q%ªúôÁ3¿˜SãÁo]S†åeß`ç"ü¶ìÈ|Ä/àz%þ9BÞÅ>~<^~ûF%‰òôÕ¼ýò~¸èkSé—›‡M¡ò,˜jͼ蓌i¡9ò”¯À&9—ð°ë ¼>í‰ ?¦Ú ™éÄieNž{4ï~†ÂÆ?,zÄÄòÇŒŽ& w*Ù5óÃPžmƒ ¿åiût|æÙ{nYq1‚V ?½a ëï‰ÐÎyšÙ°$ÍK¢ý¢\p$ÁçUìÄ©~­óÐLô.Ò܈÷ÝÔùL{=~ýó:úW2šë:J&Xÿ@¥Eþ_]´Gço<‘æ^©ÖY¯B{HÞ¹®J¿,·Ãš g‰²‘ M‹á0;¡†‰] ä×,WмQð@qÞzü~ú¦î‡²ípëöaè'£C%Œ“pÍsvÿåj:ä®L¸¡[O »zë9zß™ˆ·&8ÒÇý³ÿµ‹–ò«N£¡\_—J¤EÁµËÏÃá±E3»ácÀm<\yvœ<ÜŽ!eÖð°ØPú_]±êVQ>Mó6¾ÂÏ-÷¥?m¯³¡SÀcY&»×œY¬œm¦ ÿê(¾|?Àá0£«˜šž9Å/&ý z—gñɃߓóGÅù±µúø6_–»d‡ $‰þ¶=?ÜåÅ 0¨‰Õ0ž¾QnÇ'0£,sx,-ËÕÝéœLYº™¯á7å/ ÁBü´0¶©>Â[Ö¶B µóÅÅW‡r…åœÅi<@ÙéÀžg‡ðA“ÿÉoP°ž¶‰'³wQ¼tþZ®–Aè¶¾uÔeûY^4BŒ–DA˜_„x´>3ü=ùF êú§“µJ<£ÃÄé‡ pN4…—gÛR»—ùŽš7Ä<&š¥I(RþXN”ãÉ‹Úñ\T8Å-£[rGhEæÿØK»œhÿHAö…t*Ú¥MVΧß$ÇÃã2tpùTXûQaüó:}Zcq•Ròip©–DYó°¾RÔy¿æúSz ¦vkÁ uü-ûNv5’ì~tÕ¼fv/õ&ó÷GIkèŸÜFpHãjÿ§ÿÔýŸÑà~Ú]]E?7.à ŽOz7„Ì–fUÏï€ö¥lý"„x¹½à|c ŠÛï·±Ïñ›Ð´Ù6È?”Ÿî_‚Ùó.ÁAÏg(0.…ë:Kù¡•q0üžOj“‚ßY=˜|_¬ïÄ Y@\3!8Îä彡ñû]>üŽŸŸé…FÏEÁ¹x ´W¸“k6°úÏps¿4_³dLüYˆ§ºüÁúíeT(}Ofá+NÆOr äP¬,jXÀ<|£„¹úø×9£öôñ„ Ý;¤’Õuå —^R† ëPmF·]³ÿ‡žyšQ½¹ápÌ)2ÍòáLk¼“Ç’¿†bÚMyø¨8 ÙšâaÏtöú€*_ºb Ön;Ìk —£iì&:©aä0züÛ(¾WG“˯ªÁ=)Q¾ùJD„Aà ?Ì™Í{9zzF:ͲãžY ¯½Ä„ŸÉpþú(X¯<‰VoS¢uóƒÁyd,úÚä­¶lÿªßÙö›ùÓÚtêNâ x°<<[:W 1Üšr§xÐÿêµÇ‰gÑ»C—rOïÝ“ˆ+¾åCdȨÈ}¬ÂÅ[NÃWÇRîºÇ‰eù$ýÓÿŠ£¦(ÿò)ØøÆ@{í\¸ãOý‚{—þsÉ?DÄ"èÊrïð|¶{ú X?ü6¦Õˆ¢¢I[+Ak Ú\,%RC ‡£î‰hºÿFA¯i{}¯ßVÀÐá¯`f›2Š*ËÐóM›!¿d\ü*Á-nâ“*=>ìmŸŠ‘MýPE£pav-ÁøŠ°ï]­À!÷Ö²®X?z(Ø•Í^Ëä„8[Q…³k’°Ã•Ò™ÎßH’Íh¾tB-Sj‹‚€÷¸ìèl>Òü5ütñg2W/ae` ó´6fì´Üÿê_Ö÷‘åS0ÕÓwm±!çÏÿ†œ•X£fŠäø:²qSªÓÝT·Â…ºYÏÍÃ-i»ÿzrë"»òðë+I¾Xä •šF Ôû Üè Ãaxé½7Ó†‹© tIg{h¤e}öÕŒ¤QÅ\£O–šE>i ~îÕ%° H‚cRnxzù¥i—áó«…Ís’dn‚ñ–n¶sÿ Ì«¬NXËíf«Ã›ß>œnœA·¥çñ†çòfo¼KD'¼8ÀÏlÌ:ª6ƒgD‹ÜÀUºñhT+/èx:ˆ¶4³ùϱk&NÜž-Þtˆê¾Æ­æ8Á·úÙâaÇÓüÑÔåtÝŽóXi3^÷5¥Ë}Óé‡Lú9þ1ÄH*Э½îôD˜'#ò8So äÚ½¸‡_Á$x?µ¯Ê§ªû¸t„3ûî%?aÈî3ÂóQ0rÁt¶fH¦?D[©çAá7)àë[¡2ú½–gVÛðæp/Ht”§Û"´àKi%*Р¹Gt°¸Z“¯þ6Ÿú=¿ø|eª]®Á„\§¾Ê$¦ \AÏõC<Ï]úŸþe~¶ Éx ^8" 5 ù§~1:§z6Ü™tÓõhÐáeü Ñ,’¾ã*øÍØÁO_ånÜm£[0ý®P¨vy0ÿ}RGè5Ï.>’#"ž#¸¬O ÞúCì-èÏ[—Æ-ó áÅ5ç¡Ó*ˆ¥í½‘›=©±F>7²]62ô€„ ²w3µÖ¬‡{v.ÔøöC2Éш†¼Àܹ·aqV->\u– †+)þD>X¨·O†²|#ÈÝ HÛZž1Á(+>NV’Öì[EC£¶‘^»½yv>Õ˜ûö?ÃFË2”™°;Û×ÿ‹»J™ºÅ2aÌÒOU{g‰Ðk 郰E|å™|÷(’©9ÿã kX˜^6H ¿TßÌ8Tƒ­¥Ò4çÛødâI÷/î §Ù>o°`ò)8tBgM›ÌW¿x½ûX³-TNJ?Cœvp-¬QkŒî‚%ƽì¿9úéÑÏ…Ïp”÷"þa_oØï …*½ a|ƒ)}>‰ÐÌàÒt8<ûÑ阅jC£ÑígÊ¥/„g"^ä’ÓuØ•bŠ5Ï¢QÅ9f/Ê퓺ɘF®à¾ú`Bº!ï¶4äáÖGHm–ö¿÷ßq9xW: _GJvÆQÂ"˜þê#G-ø 1]¿“DÅÅçÎ5B—Úù + ð,ˆaT)T‚»tô©ïËðüà²ÙæùbÝË?D¡É×éᄎ $E&yó ¡>¸›Í¾/ÑN»a¹}ıº˜òcxd•fÈ‚¡6+ØÇ¥ yãý úN=žü­†ôïÃxwX ¤–§âö¥<ðÍ0ºIñGŠètû˜B,ÕPuÉ °u¸5÷š«;ãt`Ð%9úeÐpÊÊ¥+üù©pâ­Ã¿kù€Ú¾-ÿÞ˜D­~sìñÖQ¬w½:>—áÌÐ ïšomìG×%á ù(L”–¤6êï ²4 ¦¹¨ãc?ènWfNËFЙÛ œV\Áî>Y>¡x)´*‰ó/þ=ÿ‹ˆüÉÔ_ìÇçr|ôÅN2fìQT<(B««ùè"+~î“€=†Ñ #xVU%¯·£gkM ïúp*«~—Üû+†CþdÂ4Ì!ÙåKyvž/nÑuãƒþ@YvgUáó4Á”^|Ûä÷dÄùðNÜš,Æ0ÝÝ™,c‰{µùºØÒ[·`}¯9e£÷ñ³½_á»ÉX}j Ê+g k5øîx5®lÙÂ>v í²3ïÌ/íú*¹ïͤ~×Iœ¼4çNYGÂÙÈå*ôí}¬“9!Ì 4öÀŠÒÔ@å í®œÄgÜyKB¤Eèx¢]õÿåŸ8úÑ­¸Í7)G§O¸ðQ ô‹¶Bå0ERüçÖd<ÄÆÐÈ`WÑ4¿ßeòÕQ$êê'œÙT)ì.I"×F9’âöLÈO´ƒ 1Íø¡7·O:Ktôµ°¾¦˜d+Цò8ô : ¥Õò´aÚKŠËÐ*þƒÚfø³-YxÔ]އöÕÃrÑÇÂwÃDù¿Úë?KÖ“’ì.ÈK4Ìö¡å:p‹#ñûýb—a“¶a w5 ïì…‹Ð|âÆâJf½GEËó àöz’ôg(u; eCׂ{õdW%ÿðs('dÂÔ 0¾V ‘wWQ%£¹d°œ$·fñËR+Pà¥Ât¸ìª·à5«ÌÓàªßxÂ~I®nLoÝõ¿(˜;Þßu;=¾²#® Æ…wîã馰%ø:;:a»é‰‰ [¡íhì­Šba‘Çñ›Ú[¢¶óû¯os^\OÛw€”ûäÖ¤oï>ÃíG1ì(¦ÃOናµ\š×Y¾‚¯çFñÈ·ÇàÝÅ©8ê¼ IŸÂ—¸n‚£öÑø±íÌ~?*Ò gHÚ×\êljͯE@›ªø?ÿ?•ÆÔJŸ0:D™$ÛPaÅ^Ù탥 0c¶ŠÚP©Ütö.3þû¬ü³wü›É¾nL'ë¼&9ž·áúiü“˜Ovjè j¼’ÐÿW<ú8ØsvÑž ô äßÃÃÒø=Ê Âí^“¿Þ£˜*Ç[µAôÆ95õtw1¹Aܪˠe€Û„ßՂžI|å=]^µ ¼wÂ?ü÷tYûø«Xé‘§|3ô-‘”OÝÚÙ\þ«3Þ¦O§,m‡ÌãïðÐÛSÄv8]óE]| !/r1»+†¤º¿cãmÆÑ~ýÐ6ÿ‰p±.ç3ºIæ’o0Kñ/Tw”áû×&|p{Ô>€kìÄÙÇ1Èq3]õýD%¦¡±ð üt§ž¹ð‘}:äü½ÜKNͽC^‡¿6y°æiáørÕcH=°¦mÒâó#º‰Hª3 Yò™­ü>¯kmG+S ÀäUdÐâòÕEïd%‚˜’x޼…îÛ¯±ÔÖfób+æ_<‡ù*áúWqëÑ-ÐÞóÝwнmoñ†¥9=)öž,šó…}{ûópÏ"qzä~wú¶XÇQ³í ø†Þ‡àÒpÙs ¿Ž}ÄüØò›9Õd‡Y0Ök,â^æ_ð^|¬ìÆq² KØÃ²Q'ˆwo ÷?ž‹Gl¥x§þ Ü >'OŸ\=>Kü“?/j!¼š™ àîÆŸ'¶=€Õ©•ÐP Î¯„†“KY‡hœUx«ˆ–w¼Q”æ£Ë%èödŠ64Cë+lÛµÃùÞ7PðÚ……÷ŒÄ}Óïà;‡Lt¬çr¿ŽTrT.Nž2‚_ö͇9gBáÞ†‰Xt{€ÿ½¶[ ùÀ1»çã­Ï°ÆG}¦ ¢†HÑÅÕºÜìvÜ1:ŧq›Ô=Lh~ëS8Ó³.¢®ÙÛ¹’íP”í]B®—ÍÂGÑʆ=ôä)Sú4 ¿Î·ä‡Ôq9±r(Þ¢õÏþ“ÖÜGÅ»ÈäéÙÐ!’àxnít)oÒÐzËÌ´ø ›Ñ<7,W%™ð®‹ûfš!{cãnÞf–ÙPjiÆò´â1îíFhù ðÖ?.GbërX0Ô”åÇ¡Jñ¬Ø4/Ó³+Løµ¯Éxþ£ÞÛóƒÑŽF0™Øj»dhì“‚[ãL v\9”L £ëwÈçç;‘„7 Ž%Ëp»/»Á|óeH;<†·Ù@õ®3¸Eò"¡ }ŠúàÊ®U¶mÚg?¼írT WM1Ç=}3A'E€‘+ÿ‡·í|#œ=ö´æ@¥k2³rÈÇùÓC è¥ ›§‚åcýqZ*²‚§a~åÜ®fFN¤“>žÆuÞcù²€×¨ý+ZŸN¢žŸÂÄÇVtçö› Ùs wîá®Î8Äm.Yøx sº‹økÅàƒÃž$¬?Ž‹«ÅÂ’tÑÛ@ÖX’Ĥ•ôèÎt\—Dm_fÂ}Zñ'‘©ÏÅ_Û®Àç-i˜=#Ü_ÿÀsm¯Á;°FªÑÓ+ÏÂâ»ú¸dwÌHÎÁ,/!̉xóû]yYó(6Üh5?×M~›¯§1~ µ÷Æ¿üç'9 ߇Ÿ[…Á½°b«=ŸU½dåLèªß[pl§ /š w¥_ãI/ÉŸ—)°.û2œ.ØÊ]ü°u…Ë`lßOv³"’ZKiÂÚ"Ò æ Æg/¥ueax6·'Ù‡SÛù!ÂQ=­Bù" :M)ŠœO œ6í ³_€ML!zOJ_9D ý©.E£Â ów6MGvÏ“.÷Æö÷㻊óO %^æT͵,&ö)«f»\ú“GÒ3Ñk—W¬˜@”=ÌéƒoñÌß,¨J8ÎTîEáÓ#I0oèèýü¿þþ²Í,Òô 4‰¯¿Êøè×n\Ád±Ûê3Lîg\AÄV–ûXžÄeæÓÊdâi„âôâ=^25 î¸kÑÉûqEÎ >2̓ð~d¯@"p ª¼ÏçîÁqz4¸œ¹ž©>,û¤êMr¢©ÏãÊEá¿þÌû…5¬ó¦™7m®ÌõxS3ÊÐïAè>ùOÎÿÑ3"aøÍ\>àp•ÝÝ:¶p£*qð’ð¨ØŸàIÆmyGhá(p­º‡«"Àb‰þ±ÏÀ‘Ç`áŠIü`Ý$z­7š5á~ßð¿þÇ$!ºQ†Wæ…£ñåj6ì¾ Ò®ZPbMo»0ÇM†bkÊ&º½· ; ㇟Ðãõ|4û³TP)ê@çÌ’‚ú9 !%“¬áçPúí¤1õkz…jÐÓYÆoÙ×gî…j1C¸Ú’!P7FžßšCNf°RqK8í"p½Ïò\™Ší8á3n º‰ßݸñüŹ=ÄÔYf…,ÄÚCѰAž/®Ç«qå] ðÞgF¦Že“?¡cÿú&Â׊[Á•:=òHf3kŸß‰AÚÿäw ºJšê´I´Ïþq‘Ô½îÁÌGZ|Iô_”ÑÚ òóiBÓ/°:3–]¡\%:T4è´ÐwÜ6¨u¬ÙèÂwýì%ª¾&µñÞ|?¦¬ü‹C]ƒ¥[²·˜uÚˆ#LNÅ÷M+ë±g2¯?+ÆíÙ¸ƒ..uõ] ºÍ•.hªî¾ó@›;HeõœsUJÐ[æA•ÚõhS©:úY'’¾ÀÙt‚H9â­¹Ðé2‹DE“žÔï[ó|¢Ï•ŸšÐW÷.àÙ¥{@%&º#  å#®Ð´¦Ž¢±ì“=Íö•þ—ÿFN²¡:›ÿë—˜ {Ü é´ð+“ñÝl|c ;/Oõ&|&Î+½iܯ(0¼ ßjUQ¬mÎXÁØuåÂÀ/ntá jÿm.u¾³}—%âÔkôu¢Ô^u¾hÑØ;’&ÏKdsŽÈò»N ÐékK'ß×ÀÑ¢ƒé…ñ×É‹}žbðV+ è‚f1xf‰ÏÆŒÁ¬¹‡È#ãYtɤ|ëPºäÄ 4Úb‰é÷·cÚn)®ô¨S%OàV£“|N­7z¥)sQ›‹`“Xˆu·«ÑçÁTnXgK¯$$àÑM>ûM7\‹g…"zÛÁ§U |¸ÒT’'ÜJŸœÞ Òöƒ0¯*Ù"?&þd µ‹0¸ö!Þ,Éýn‡á6£—T9<µ$vã”BÞ6%v¿Ã•Á(íÑJMXEß'×òtÃÎ*/wg¢1õ)¨wïÄåA[xÓÑz|l°ãXJÛŽâ6)òq†ºü×$}î5|XU]æ9zçŠ>Žö©JÂôsSÉX¹KÜR׉·&½û¯7=뮞vô]íÆ;|ýþÉ?~žˆGOÇe]“8*\ä'yØ_y¬rP¦Š³Ìràu²Ô|,,ž•IÓÃçr5ë~ÔëgæÑ8ûkOYt„KG<©qùdìü¹tž{²æe¼³ý¶¼®}=uä–’\ø|0o÷•µ$,ÂüZë2"øëuP:ë·±™Ie’åÁòi99eíÉ=ž$»ö×à^&ÅnkžCåýŠüÐvOšr@ JlxñŽBhóÕEÓeδáýqòž˜°“Â_@J®Â¨G“¡eS1|sš÷ýiêÐ`ZÚ!ÆÓ ÓØên+ê eóÿŒìÍC›CÈõIn¨9S“œùhÉõN>Ã-u#é…žTXšœŠ_‹±šSŽxlÓª¿Õ…žÐJE7 œ–d šwD)¾tjwŸÂ¶ý\¯ÒÆ ‚Cñ}Ô¤Nnê ©òQÿ.ƒV¥¡)éÀµ pvt[i»2æÑÜ_Q¼fl<\¾OÃö—+!ôd1„¿? If6Ô´^›Zõ%ã—ÔnÁ2k)n ?o¾¨dÅ8jâK'?øW5[wð9€Æ¤¡éY¤c°-}3¶F<•¤!q!'¼ÄÀ? 6Ù;ýËÿ?VÆ÷A·qÓ¢½Øòd,>×};„ãøb >Íp;÷ÌÃ_ëfÓòÁϘ#çC?“?Ü3f =wdUåÄOì<Üë h?ÖÆyé§ë©øçB¹úUÈœš¶Rk±)h·¨ŸÙÜt¦'Î¥¦EM m#‹ŸEóß T ¥‡C.?ò=Å0H26k;ðº\]P]° æ/b« gÀ·å·aâÊVøp`6x¼ÍjzY@ÎÛ»¸n‰6Ou±Á³ï ¡{ÄhqPžŠˆn_Î^ ¶Õ¥9¾V¢†Ñ<’:oÜÚÆ~¼ÙÓÃÎà.rYÕ-BÃy—ÈXKfõ÷ì‘:JŽ®=}‘RDo Œ­Ì‡çUYò×uÜÁ ŽäýÀÌ> *4+†i‚—BP#W¤& þZJBZ“IŸÉ[vÞ)wûÔaÀisTZHt­‚@uB FäÈâtû?°@ÝÂc_àËÈ<²ë~.t(T`TþhT=ÿ“„½—FáVGXæt©RÓ 0öQ1{´>˜Þ.ÔÐѦ\ãß5=¹æ‡O˜óòÊËáˈ¶óÁÌsÆìW}†jU€Èu:i”7$ʪò•>ÔÌRšÞû‘ ÐGÓÞà öì¼æ(zOÝ x£.àsÊa±Û1ÌŽ½‰oæþe •Ö,,Ÿá´Y|5y6l®Àý_y`Ф6Ê®[ ½ã)Îû]!P÷•ÇæõR÷LŒòñê|±É˜ø¬[=á‰~ꔺ€Ð2>˜ÝáÊü#„ÀöÑ`²‹,ÿ¼ý>ý É§œÝÂ7^´â·s/àdIÌzÓŽ³Ã2ÿ›ƒDʤ÷ž90RÿxX&£°å:È£×pwØUf `H9;ßåOu9TíÂ" 3ζãIÓd¸:?uó«±F± « ŸÀµGédÉ$AÂi$¥óŒù†ÅòÜ5”BìÓú|’f›ׂ–ÁɆã¬h”%Ÿ½IެqØÎß»ÝÊ”h™ë1ÐÛvB-æ.Vè/K¨„Ö%ð*®c}o ÈPOOvWþ5ìØfÂg®ÜßG‚ǰ"–jF]Ì\QõD© Îô>aYðXqó¦E‘¥—ÑíÓi63¡•-°Oö%ªøÄðc›î‘ ‡.’£2Ãqò3VnóVOÜÊjdÀ™¦Áøºþ¶àZíYœ¼N•΃Yœûd’ëø}f¬|¥ë÷Ãqº„O>ÜN„YßP—~Oaùc|ñ'˜Òo[Ô©Aÿrn<úù"mC_?Œ+.Ï&W :nƒýÐ.ãA­@r¦*ÈN,BW9\s±ž•-ãOlûˆ©‚Í./Ù~¦ÊX£d~(vœbêî"<­6Kð1p$(%ˆóaýÐtø5˜lt œPæaË=Èsµ¬­>…äóÙ£·WñM@õÙ·UN‡N¡0ë3OÜÕI=(ZxšƒU¿åÀøl7ÈËóò±ÎôFû¾ù¬.nýh‡di¸;Ý0» «Kúˆ`P~,V&Ï׸¡ýWMÈïâºÌýxdz<°~J{7â×¼Wäâè²³oú©†ÛBÿâ›Jþs¹$ÑZd³nÏÂô#³p›w+kÉÂ)*eÔ?¤îw ðà,°?P /:åø°£6\áöR2C‹gõÅcÊ—VŸúÊ[FòŸŠàÏ!Y*²~Ð}¸/|}ކN‡î-ã©OðºÏÕœßW¢©ÏÀìL ¤™&±ŽcËp'žºÝlLþàçwÇAV¼êtË`ÆU[\ß¾€¾¼xÜ»=°§Ã‰¿º!!ôïÔýÁÃï­ƒ«Z•Ôm§7OØ|ƒ™{ùÍã§ðë›Ð¢K™ØÞÌË †rJõþ`ý`}z[ï–~È‹˜4qÄxr‚×wÜ…Iq7pÃbMvÏ1uu"øìÊ Ð—8€ÁR°ñ˜Ž×3àŽ1F<±æ9ÊNGáxoºU1ÛN¯ƒ/ƒ×âBí4n?& Ùž£XêSA¶mÏ$öAx¸½V¿&¢† ÑR“a¹IpR}%,œ¹¥†’ža×`kĘì5U"6€÷œ7ª²/ì¦ßLkÎ]ÿf\|ä;¬‹«'#­BßnWîQÒŽ)äA«#*ÝL?Ÿ²Ê‹~þx"˜Þ?í˜|ÃŽª!d®Ò>ØaßËDç™Ò«óT(}!G¯8¯Ëäó¢yxä -8õh¢i÷D°ït bqÿÍþ\e?–êÉCoBÎÖÖ :bÆ|ÄÝ ¾"O††ðý$"o–p`mr(~ ¾ç0‡øûÌ%°Z¼W0ÂÄF<*T­wc;ç×Þ¿…»¥hnX+,lÖÕÚàúÃì]t…m~¬LÞ6IÜ»ïùl¥Wœ,ÉGÓop.o#\½™„cæºÑÆ×TëüCÜžþ 78“e®ƒ¨ÃƒàU~ û0h˜9xØúÓ¯¿ÖÓU¡•„nÓæj–7QJõugóña¨W{Ÿ !A|Wô0Z\ãŽEŠ»¸ƒ<‡Ãù¤§=¡-¬m‰†c¹r0!]–_¼Õ ¡Hö!5˜Ý­ïƒ$ù`õX^ûúŒQÓ¬džxpõn¸/™Ýt‘^*Fsê3ÐOù&Ü?9›§‹®§öÙª\yõpšó@—OQ˜³uïƒWŽ"ç¥ÏΕ‰æÙJT–^Ç× õŸâÞ†etÈ]UxÚŸOâ~ŽæZ6:´·-šäN¦jã²ù7'œ1Åž_¼>:ýÄùÖO=pÖi=Ú>¥+sùå¹(þéìȶà3ä¼q§ùª¶Pžÿš&JC–~ìjÀº¶¹È›œùÚ³çðFÓyRÝåQ“ûʳEjoqçê‹¸í– ‘=–Çûx’ᵡSá±øEý (Œº„t<~´µ£æÒ¶Š™üò÷A¤ý•(ý8‰ÃÄÃèš$œª›‹1cEø£Æ9Гs&;åâV¶°oþD:¯„<íý{Õo‚H{ÆŸøÊf>S¦ }ÌX¾ªjÅ81¶–T NF×± ¿Oý©éDe» {m0Ø<A»Ž»Ð¡ó¬9¢‡`3Ÿ[þ®y®†Èuª¬/<õaÚ.ìшBëdwþÍ¯Š¼—î'Ãy Ø­â÷¦j§)‘ðm’1ϸù‚ˆF Êei‰†8ÏxÞÇÂ#I¯£u„㤾²ª_×¥Í1øÞ°Žù¨Bâo zè§Õ[»‚ÚcoÞ˜aå9Yš (Cƒø·Zk̾ü£'faË»E|ÞrM8Ùê„×u÷âVï;¶>žüÒŽÖÌ…,£<Üïzp`øjwX°Úxñû¨Co]¦tÊæ 007‰–ò ¯#‡sgRïqËeØ4U*fµåÞÿƒº?QâíÞxÍŠè«ëE~9¨Q˜‚X¾ /_þã¢vÓ ×…Zï@ EÙù 6óæþÊò%“›×O/¡sÃrÚ"•Üx8WŃÃÏÅéì.ºnÒ~rS‚ `üp8¨ýNª–1}‘½pÒì9FuéààYØ™ù›IÚ¯Z!·ÈÙxÂfnñB•«¦ek‹p¹z¼4wÏ©ˆÇÌêJ_‹&28üèX áÎkKÁJøÞ9qLéÉ.Vp5j»GñŠ»®p}ˆ%5zõâ™Â¡µ¦ü> "fc›¬Iä¥Y=b0.¾/M¯)&»¶Þ&Þ» þl>•˜F§ï¬cjwžâ]Ë" ýŽÓêu!xEÎ攗óåµD¼u"…«&ôâ`PÙdBÛnŒáÖ-„…>ñFáJÞ?q gnýùâ –‡©ñ!ß× ­ <ùÕxc˜yÜ–~ž9“RŠq§/½î‹%³µ`î”8Aqßà1 Ü/Ãì/öà³ö'Ì]6š_û {Ö%3ÏîÅx*Ú…ž”4§‡Õ~AÚd’^¬|ÕE|°L:ìV±m½çHÉÚcþWøß5¿îBìŸDvw ßÉù²“˜UÛÝÇNÀ Õm¸TçÔINe«¬øçþZÙ~ ß¶â5=ÈTŒEõMcî÷†àöŽõ<}¼_@öÊQjOì<= 3óIᡚºa"=©³\P¤ÝÞ"Tomù“kÝúYÌJXÿ5Vß ‡Y÷²àyþÒxò ü>°ý”¢ÁÞ)™ŽV5¢{v?a)G‰ØÈ (ÚV†+&'Ñvó4ÁªQ?ñõfõãÛù2¾-°¡I7V+ñ”ÞÐ2Jšú©v¿ƒ¶#’X¯ØrP3Ûc—@Wóá<Ö´òý³áGö0>_q#ûâ3„-›î *‡Uèó~iž×{‹tÎ\ƒ­ž£FLÿ ›\®°üY[zv|r…פž‡O¢Sx›ü£ V5Nä•+<@Óí¹ÝDýé x¡;™å‹ÒÅg%¨bº5·²[Â?eôÃì£ìû^ì>ì1‘[Ó»ìY ÑÚmJ]R‘tË×ûærrï4Ö ¢ _ä@oË(ZMÎc÷š<|áð–IÖÞÄ•:rt_\&¶¡gN%Áœ¦ùôcø2TQû #îVð•‡>s§Î DÕt.mC*TqDüjhhíÛ…ºü×¼…ÜÑã8hêðüFÈ­õcý7è’S^èõ#”¯ ÷0iwx¬›Èuµ²9ÏÁ_êŒç:wØ©Þ8÷åøÃ´ex_ª¼74 Ã_òfɘnµ‰ûԦिñ©4¿;· 2#Ó\ã]çƒnOOÁ5Et|û}6QØ»6}ÿÁmщ qåØå æU3=¡f€ßJÍWNr\Kõ]€r ŸÅ«cˆy1x~½ Üõ-6/öA×tI ¡…(f4B%ç†ÇV“o`»[–)'AnãL¸37'×ÅBËåÃhüM’¾>äM“ÅDù‡«—É»Hb=qÒŠï$§¨ñ‰wÒì W÷JQûÊP:ùÙ-æœ Þo© z;êODìÖ\“Õ#Ç:l ck))»/ËîZ±5>ë©ìÂ/h»º$‰Óf&ß/(¬Ë¼Î¦B\~6Úw|+îâ¦C–PÜ3ƒ¦ÄI™Ì8n|ï5{T³âßÙr 2„n²sƒi½Ï÷–°Æ†~쌫†wZþ|ËCÎe![\‚`é“nè6ŠöÜpn±Þ«âþ RY²#ä*'ò:#Hƒ¨ ]ôLžî+)ÁfµC¼}ç18"u ,§®\:d"Ð'9D >œW°Sиf!§J-0n¬]WÀ¡ÔÌšûÛlæLnnzº ¿yòÍ5™p(MŒÛYÝÅß‚°>ã‡îOš ÄW’îGŸE/q×£,0Ù0]øÝn8m’öà.A›é¶#ã0ðÏ|±½s<íáü»a¸yÛ36qZ ;»5‡H£7®²Ÿ=Õ(ßH¿ß‚?Œ«@ÿs.¼Ü#u§Í˜:íAX2€=îá ¯AüºÂ™q yŒ©oŠ}J457q«£7YŒÄlÐÞ­È,`_üT¸Ûðf›Å)^=ØÓÓ"øøw)O>kÄw}_Ž_‘wQð¤Ì…žŒDƒíÊ ’¤ÊÅ®Œe ’©$CD âRV¢Ùý=ÂI Ð'֌ɹøeúôŽ—§¯77ƒN¥;d„ìá;ë‰÷ýÉŽ8‡ó=åÝp!:;G]€Èñ² øÊO˜/{ºjÚ¸&˜qf(޽uŒ\ÎÑD·Ûpqª9-Ü:…æv*±ízÓ¸^Þk WiÄÓLþçTϳ\ÈïU—ÃŒÍùôɧ W÷=«/À ñfQ9 6ÎÜCíŠRxÏjCôK8ÓL:Hl >}öè$†TM@rVlü¸ÐÑôÎØ).IÿfŒ¡«¼·Ar“õp®!ë|éïÞíü‡A Õ{~šÊ?-¥1"©ðFe:5¾¾ëYÇÆ¶³ä³çHÚà€gûOðùw²hì‰b^l>“î•–¿êúì0$÷»ñæ©1´ª¬}ÛÑϾîT¼êÜÐ^ÏC¸Çà–ŸìšI#¿üŒ‘‹«8\¬„nú ¼·è>¸œ:Eí¸¥7.ðù•p¦Û—x𛂇†Ê¼¬p%É9öÕ] ‡d>cÆ'†õ .<ç²wÜ:‡ebío޺Ѓm2¢ÝÓ–ÂèdƼÜ8ž=«ÆûÙx´‘xF7eÜ¿ñ¹Ü¤Eƒ{ß(†Š¦>UKžKJ”bÌ‹cTqôy'ÏôGCî«Ë ¼w*×øEŒ-½PåÆ(ª®GOŒÚÆW+·>>‹IɹWÄgÂ2¸·ÓÂÕfï9[Àë‹;h¯“äc5éÎÆÞª£niè¾\Κ{šíxÏ®^¨¼éti­ÆïøŽÊ`PßaÔ—G tšpïê¡üã×=T¹gÿÛ“‰ÏCî­TDÒ™~¬z åC½s#pû¤C¼{u%`P3qëšF•Åü¨Ä¸¨¾|?4œQ§AÑ'±5 ªÛöókRáIýþò½(G‹6ð>õK9B347 ç*åÒ[è©ùq\ébý$[Í,çÓn-^7>÷‹ÒŽ®SÔâý'±X‹A1\ê™$Ö·ÄÌ€tŒèw¤#KçÐ×cöÐ ÏZ`Qƒ-—ì™@Çl»?†q8³*†w’ 3ý9sBgz\ÏçsUÕù|¥WPt|µžv€o=±ÿŒŽâ©Û4èÀÞQõª6)ß*Îi`f7f¶gPéJ }á’Ik Ãèð×M$Ãêž×Ds£Ƈ‰Â ëróÙðEƒ-=Ð+j{á[o3,.„¸±üÙkh/ÆEë×p×·Ò¸£³ Â/ÉÓÓ× Ñús (ÓTÕµûÙyðcøu\øó/¤o×¥¿–ÛòûÙ TÆ%eÃ!BåÿýBšïoHça“ó‡ª gsð°Tȸòë7àªü>öíÏ]1nÔ‡?ѧMûèÀþú H‡ÃjÌÂí¦-|C\Ãþa¹4Âê-¬yo¯¿œ‚ž›ýÁaú^úÆÊtHDBrì´U«,jРʔ–Nsà*2:<Ôo:]Ú§M£Ës³ÒPzîž÷åMòQ8ÄÅ—ÌhG]ö þª_ÂÁùchlÁ`Zs]ž&o<›^'C£¡4FmJGòs"æXP¯fI¬|%B_n;ȯqz)ï+ó¥÷ËÙR œ<¬/mˆà ‚Ûqþ^†Ògò¨îµØ“Âÿ“ñÄŸžªÈÛz³;ÎSéÀß"&?Ø” šÜ÷ïY0ÉVüàçÝ ôä½&véÜ`’󯕱 ͵èw†î Ñ·´*ï¬,„Ë=WYæÉTÚcÀN¾ÛðGÉ¡xÛžÁŠM%‚ËW•øø1dïeönÅb²¹Ô|Cíà\÷Aöíj\«;ýod`êýÃØ¿F²’­˜TÜBžù2‚ü蹋ë°žËÎlÊíÝE>§€(¨4^È]šÉ³ »× vªí”ß8@5¿£gK ½w»òÅ‚»L?-”Ž™‰í*z Ôµ¢b:üaëMvÔ{ ZŠéókï[ˆHÑD´ia?Cyƒç7ÌŸaJuü&“_† GŸyd3Œ3ÉÃdë¯ F ¡ÓK‚fÄ~Æt»pðQÝcÉC¾ƒ\ÌPܺ<›¨8:9U_^ ˜¸X‘¦|6.Eïª]ÁQ?œ }’TÄu“ Ѧw_ŠßpÅéá¡6¾˜M3=FÐ'ß Ÿc=•òV.…]C®BhÚÁëãgFóºÆÅ¬ß»„ØOÉjžÆÆ½ ä l:!^bˆ0óWú7´Ã¨ÎU‚ÿöq#¯S¥ìü2ìž„Êc‹º¿â3Äì=KAgÜveÆ{¢²t×R•…Ì7!ÄYù4W?©Ëï?6ÀçóQµà1·,Œâ):Ÿë-áé½¹‚j)QMÖÃï %ôx^¶•Þ!˨*·u·¡;v’«÷–óõ=ø`yâæÓìë¶@,”öª ªw Ütók-.)œÎÅýÓx”e!½Ü‡Œþ†g®ùðG“„‘é4úÚTe¹è¡K…{&¢\ÄgþB±ãH”çæÙ#a^Q2d-¬Âe4œåLi »†¢úÞôІET´Æ•çJN¦[;|°7§ –?}Ë–?¹ËŠÜŠ!oÚoòþY%®î ƒc 1ÕS@¾[—§fto^:l £ë=†óLˆ¦nõV]Ø9Œ^m’ sk3èÄC¡³u8ÿª¨Â÷{~À¼„åC|å½|pqÊu&UÀÁU…<~çFlË:ËåþÎà_‚5ø‡ã ÜÅh4;±kº%cÉS *¢WÊJ‹=¸o=—üU÷of;oEðºסŒžÂÅÊâ¼òI1Oí4Äí;v£2¿Ït[¸’ã8œíÊ?®(ÅõcUœ ±3YÆ=wÞÇ©SŸã Ë—äØU¾ogµLQ§_ /W½i›H[rù‰3w”!7w7ƒó;"Ô’Q€í¾·Y¡j‰0ý›pbú'|~Ê­×á}'çÑæ§º¸ïCìµÆ­®tè®w§¢OÉ“9 Ôýœ/sà­ý¸“Œ5Ý·ò3{ÝåNM ¢?rjÎj­ÄÜŒÂì‹ °ÄTЧ;Í8?P±b³Ò8Èæqgàª}®* .€÷ï@èzl ‡fÓάXæFÓ¸ûØ_AVoˆç5¶¿áý‹jØS`Eõ¢Öuñ:jýpð^öz´ÈÙŽmn×qùxd˜+yÄß²<ÝJ÷5ÁÃûêôØUS¨ÊE_špôZŠÃ{› ¢cœÏ¢}ÃÜãƒÑ%ǘúGm麄ˆ{ÓÊŸr4ü·=|Å‚æ‰É Ñvr¾‚[µxFý=¬Í¢o¥µ`ÔûدºŠúm0¤Ë³V¡·ÓWòëY5®[ýÓF€ö÷°êhu1:Læ/¼Èä0vÈ}ôœ\ˆ}÷ìù,®öR—Kç㉜¡|eØ\<À‰¥—boàòA¨ë†‘ô²º¯ßŠ+‚ÃÈCíeþ€¯ðÈ÷†ºî ŶKqtSO¾ŸÐG:u¼è?}þÛW…|'³‘|Ðà6"wa.{ÁþŽs"íòãßî¤ §ÝáÁ¸ÿþ—™<¨c8â,Î×å˜O§ 4……àѦ,¹ý7l©ðÄçºGXM¬2vö)Ö…ÔÞß; 2CNãwZ\vËl—ä”ËÐb‹û(=Í–Ç× ¡Ï/fúe.$¿£µäðûÏ xñ†,÷}¹Ÿÿ°Ð¢ÒVzp}d%:ÑÎ’Ó8¹á ¤‰ËÛt{¶7ƒã#Ÿl|®–À&{ˆñƒð•-©²Í¡<ÞàÎáÊí]SçíÎ\0â*)þ9ŒNÙW‡±Y=pý—(ës´æÓµÅùÇi_á„B¤Ú‹ ­¸ðö£á$ËÆœNU Ë3¬~ñRþ,@–õÜÖÀÓÖ¹ÜÛw>?~|ßZó€°Ã7E¬ÅðáPHŸàHvM@ ÚÏÃÛž=žŽ¤ñå©ì—z˜Þ1 “6Øã…3:¼qŸÍï)fá #yØpÒ¸—³[ŸCñ…‹.§›RÁ_Ù +~ý!¿/†àÂáS`Cg­ÞnÐ5j:¸¹Ûà »G 6¯Üm%¨Î›v(ß®Ícîwßûs‘ù×0¼\”B7ëf eŠ Ó¨8„æ÷ìA±¾ý\Ê*R´óÐéŒ*ÏÑ{z'ÑÛ¾àDËc˜èy6XTV%½ìÁ 6àQYŠ‘󸝔^,Ô<„Ó½O㤠«ñÅ*=K ºZô„ãã¶…oGç9)äkÞú5u]õ¾ÎvÁã[.ö k‘h²÷\1Rk΢ùE¶â°\]Œ>D!@Ö Õ´,pðÄ2 fé0uj VÎú€UvaìÈ^KhºeÏdÚç þÏæ‘‡W6Íä;ϲã vyo66íkF#¹A亢ï×ÌÁðEÁ¸æ–>×÷݌ϲ±ã[Àìá_¸¸} Õ>Â/+æû“*ô…¥ÔÖ¼Á½ši d”Œk®Ø¢šþE¨+ò$–ˈà y dl™â²(á3Œ( †ßåðmT.f†õÁ³ßÏpHÆLV™0k ܸÖì5¸~jŒPr¢(„= Åæn-¾á,Ê¿yŒ¿®ØÃªûšôφcØ¡ãÀ%ýwŸIZlˉ¦`(C÷Çe FÕàßÁÜæçWðx}ŠÌ åàÏáTfìpÜø%ƒïzó¯ÚÓð–öSXijÊ—ÉçÁ‹Š6˜ùÌnw#æÓ‡ó¨j2ÿ²6Ä(âë1· ↉qS_*Nö£—n$–u1ó„Püé9ƒwW£ãótX`P ~[á ³ßÁ±ðçèfRÎ$ÓjÙo÷ÏÄFé|žý Ǽ E§ë*<ü(Õû8ïêꃹ¹+½¬|œÀKAåx¹Q–³­QxþËBºÿÇ Þ;ÞŒú¿Ó¢+-Hz§>˜-ùÁÆ/ƒ4fé{B²|“ÍûÊ~V‡ãáÂÁ’3è<ÍFÈ›Ã:ˆ1™ÉS“¿ñÜŠ,æ-8Š_[_AÇì4~oq0·³Cû¬«‰Ää¨ÝÖŒ±[Â`¦O"’–0ü’0œãåiÜäìöÊ[7M"Ž·k@¹ÜLH“í .Åk à¶ò—ä÷«Äy¡!Ÿo׃Ë\½¡£½¼‹Ávõ%&½¸ç…Ã$jË—.QäUWáî<}r;x6C1 éšLÐ8Á^‹p²h†Ú19ÊtöϘßLö.XƒÛ44¾c* ö?Ê ã„*WliLOn¼{ׯ& k­i´ËBô̵"+´¹ðx~w˜ÂO{Yà³ÍÒ°ï÷6oOˆù ë[ó°dÐÔÍú)<6,L;ƒiĺhòQ¶Iø—½{ý—M¸2‰2«ÆƒöÙÄC¿¼¾¯AK0xZYӷÿÃÝ çG/6“…QÓ˜ßýóø«íSÙµ„—o2ýaÃhâœ[è8%oœNÖÆÏ‡èÈøø;'Y}Áøû¼=cPB~n÷d·L"É=¡#/Uî%cÀŸ=7˜ÏvÞ ¾‹j˜õüR!¹pìY~¨;Œò‰>°ç¾Ñ™!¹Ã&]T¡¦]W°Ûx(—­EÑÝZ¼ÂÄ™,$ûä"ñYÊܺã¢P2¾‡ö]FõØÔ/8Û¤Jæyø°U;^Ĩrø=¼Š-¶ÊBî P–œEж–K9´q-Ë~Ò f¡»qÙMÉû*Òí*|eâyº²Ô‰¾˜wLðvc5,½º–N-‡–îXA˜â†ÿzÇz§Äcó«ž*~|ÎóhĦ­?@_ò®ùœG:$ÐÖå»´H’Ë›íá^/LÀ;þd«û¡÷nq>{K4,ÊEð²UçXør1:ôxÒú5ü·§"É~ø\Ø$Æ×t^Qm'þÜ®‹xEc²ü z>¢³œnã¯&4ˆô€ ‘qبjŠûK%x¦ü<\Õ+&p”»Íî«Ë Þü+$CïFøÀãŒo¨ aŸ“L3†w6ÃüÛðV«ÿ•Ø…áT²:¤Ý²HlÉy<Ü&¼Þõ¼WE Ý_üïøtR º®>ç2zšeG$ñÑ_Œ~¾Œþ>ûgÀÌ[ŠGÑÄÍØtæ-´ŒÎ‡6ž„T…£Ø+Ô¥Q¥'é`ìo¯À´?¡^îìঋo÷“³[mZ|ñÌLxj8 òÊcѨȞ:¬7á3b~áâ o!jÿ8»ó:¨oj#©™}ðd5ga±Ï1æm \›»%ݵ†¹“·Á¯ïRtIJ¹Ì䔢@èÓZRTõ§9vþ$[ý ÇÌ£M[­ií›TòEFdܵ!ñæwA4›H +ž >O–ãüǸ$¬¥–ÅÀÌ<ˆX§:âðéÓV|!×ob·ÍDáó½k Ød"ýÕ´¯Ùý„Íc"@'±}ïÇv%0g¬ác) ö†Âá:öwaôtä~‡çBi^4}Ƀ_+‰²H(Ÿá¢Ïö¥íÀØm‚E[y‹Œ$õêÔ…è2 a¸N&;Õ ÝžrÜÓ.K0þM$ñÓ*B;Ï› úh÷wúÂ:O<Ä´ÑU‚Q/!±»<…ÓW0öG\SÊ…á*ŒP2åã×9³ªÁ—ïU {o`ßÕX®jý›‘ÎØ¢¨ ¹^WȃÅçPÇÀÿxFÂòw-l«ùyØG‚°´W…{* ¡&’{΀PÂ>| ÿ(m•xyzª¡n&’UÖ;Àø]¸ ­2§ð (0ð"^‹BÉfÝ´(Qâs÷mG”ü÷·¾dmi€waJNî-Pcw—×À9¸¿j»qŽL:Ý™4š×ŽÎÄ÷H~î¼µ‹áÒ}|Ö<jìð\'ˆâ‰Ÿæ¼V{!Ú<(ÆÛRGñÍwWÚ´ ÌNãÅI#yJ#c 9ú\bÕOtʼnÜÂ=†îŸØ†™ê9xeÕ8ØUM¸æ˜ópMgÝ„ƒúÁÖ)†H‡ ¢6ÚØvпÏ:ýzþÔzâÖ¾_‰þº£ /¿¾Å¤êvÔ6õá? —ð¬Ý"ôÛù!té÷Ð_DÛfÉðþ%SùÇmµp¨3%5÷øï„Ó}¿st·UX\飼²Q]á” öÅÛ£Žâ»‹²øl¯ ®y½UE¯bÞô$2Úî*ü¬ÚO_Šâ…™ÒÔ±÷/ÄoÜvË®‚R¾}úr“­*úï›HXW®Å®eÞÛдà;¾ñ=Œ«ìãñàI,;™Œvk“ª>*”±ž‰è¾w?$ºs¦9q³0“i¶IñMÖKàØ‚ð󺀌óvFáüj\˜Kؽ$ÔëJFCÝt= ¿Ý˜ z_dIè69XÌ2.À£(–|H ô8û ‡ÎâŸ_ífÂ¥ çÀ²OB™tãmqú*Ï ×6œÁéÚÙÐ5LüO` û6|•ØL FÀ½S¿~jsµöû°¿j*´¸=…G"p¯u4;Œ;~ž"'¶£Ú“çì«B?k‘xÎFåtBê„ÜêÎG<{{%¬{º7¤81Ç8LQÄãõÇsN$áwTÐT“¦¿®Ã¬ó Ú…¤7|*~Ín€G'ü:ýf„)¾Êá#¾Ü¤¾ömìË y~Ýsß ²‡.–„#Exmù,ZÕ£O»q‰ârakx,{ïìÝÙ3³3ç|¾Ïsž³‘ÁtÍæ…°qm왌×Ó>ºÓçá•pÔï( ¼ üùôþƒDö|Ø3L»ö =»Ô ^g®Ág%0ÚyrŽLüsVhs5T¢àúP¿³é=“/ÌÉÆ 1˜ÃüÛ¿ÆËÒV»=4?ûª²IAx+Ó}ùæ,H§·wšÒéÕõÇBçãð1ü+®zumK¶ÀÎPµÊŵO±-- Ò/ah£"„k ý/e/.NûÔq¶ÈAv–ãI˜¥?²îí©ÎP,]#OìtX—Ug`“ñ'îv}aÖØæ"dyJ]Ù§J`ÿ­ùð4Ê•\ír÷OCɵlîä5œòUȼ~Õ‹7 %—ámü ´^ }¡¿˜C~™E/¿ãîsÂÈý†›0ÞúŒ qúG)´Ð¿y£V[*Í»‹–ý.æÞM>O1o”#aí'-K#¨¯¦U¦w†ãÏÑNPí`ñãnIæ’t%\ÔšdŒûéÖº,ŸûüÝóÉÂµÔÆZ“Žq†§Ú³@¼žÀCƒ#Äû /þlÒ!:©ø°¨ 2#2§q,ÂHMÀ„3Æ µ@œ\›W¦{4ˆVàSW6—™"µ•l™¿œ¾X2Œ{_µƒÔôetÚùzŒï†{@^Ì#|2TËZž¨‡HlŒ±Kfú#_Â+z2†FñÞÔŸP7*Ž×÷ZA{d„ß}9ŒWý´ÐxØ ¤Þâ‰L|wd±CL5…[EJè´4‡•}«ß;ã¯CÓп¼j@ea ö-7¦Jy hŽíjj\‹Ú¯Ê`ÝûMðŒÇê-%ÉÕ6x~Ñs}f |³<ÃD'ñ”ÙqƒDc¡²tÎ^-E—:ˆÑÛúRÄbÉx[k§EiÏBº÷Ñf0°“¤bUüäÅÔÖ¯¯‘9µH$6ѽôÚ1 3SŠHçT~pJƒŠO…ðdóìI_`Éu•ákÓPò€:†[UથMÐ8ÌéÛ™ŠÛýzØnp!Œ©1IMú ;JÀýõJfÊìDÎIÕ<”Úì€ßÏ41?n gñ¦ÎÞÙ8¨íÏrf†ÀÝDz«!†L!Ä]2’‰ ¯â~¡aƒ$ý[ÌL}cD^Ùkæa× 9ªå žwn³÷K–“Þ½ù¬ÖÄ#F-Om8B¶ƒ  J¡—Lž1'%cðRG=T)AÍ™KÝÂÎàÜBc8pü ÌŠšEfäãŽgsè»SbìU—'s› fñ¤¡²˜åÜW%V뾃¹· ½_Òݦ*TÂNŽï9Šr lÕÚI¿ç÷Ï?…¸eN8xõfomfú/aÌ#–Ò/¾€;òþ1޳7ƒf‡\PV%»ŒØ9ïÚ B»¤þMº"Î{Ë ]¬:í®9Ì|æ æÎ-ô ƒwô¨º¥kÐ¥‰K!næ3y§Ms¼…pîŸ'B´®cSé©}S©—±S¥|Œ.S¤sÜîâÀÂzI¶ƒáçübk„vrŶYQ›žqXÿíwçUú»óîù‹hÂ+CÝ6Í&«æÂ›£§ TØ•æZ¿C›æ{ð\u§f³Ù$úåt²}O4ûT'‡ü©žŒ)œXÞ}ÖÌÅS»d‰[Éd^ Ý :õ²^‰®‘4ç¥:¸H*¢žÏr*úA™ÎünJ…ÁÙù0Úñª°±ë%c›ñäï ZìÑıñ©t…A ñèNÎÑy‡£Q+áûµjX?/oµ'’ûáÛèGðbÍW(‚®ÎYÈ4ù*K­™õ¾¯@Z. ¼UE²}Õ!FÙËMì€5êË‘¤^GîÎr¼Qº…í^[×]xéàâ.ühF7S½û$¨ô/,}—‰µ£ÃÍ_9Š;µÑ!f>µÌ%›V0]—ÿÀ%oNν ö—†#Õìæ§|¹vTõÝMîÂÓ›‘7<ï÷¸È*°¿Ž”Ò¶uÅð²ÃßæÏfÅ>ýfÆšMè­}Jô×¶aEœãñÑTõ¾d 2íh&ÖŽ g2´Mžñ]%b )à'?ŠÑ–*T¿Z _\ÝOcº±±»Þƒö‡kpÕù4Ä5☸9zœ¼äððW7;'e/¹[kH ‹jÉÞgÛ›.vƒŒžn"EªNŸgŒT ¯DŒãèõ6µì—wfÒâ{o1*!ñ©6ù—3 +J“KÑ@ÓK;ÑçÌJÚ1=¯Þaõ¦AÇý"j32ÁÉÜ…€´Ü1êj5ÉïF‹‰ç‘¿Œµ¿ Qjˆ…Ö²´~è ýËw–ûÐRŽŠ(ÒW!òt¶ÆVŒ¸‡ï9“þIŒ|H…þè,üÉ}qÑxò(Ȇæ ãš•±åP*5+kÅž :å!¡éÁ;(Oâü’1Ƥ_Ö!FÃ²Ôøí6Zq<Œ–ëÃÞ=u ö*‘ÌÖxs7è ^ÒÔKÛ-ȈÒo0çá!ò•½Xq¹&‘Hÿ¹Žs2<ÆPèÖ>fòÈ}!oEÎ Óî®-°ÀR˜œöž?§³—Ó…ivA½Y’ÊÍqhë$Éh¨óàÐî\N]a ¢ÿ Ø8hŒI±gÐÆÿœ°`ÚŽÃàþ?xJíŽîÁ)'Œðû'+¬°3 »dòhÁ‘‹È{ô=(&mÆ5Ãå䯼Tµ!« Ñê0*Kg“Ù“Lx6ï&~ß#¯ûþBdF.Ø7|gv?ìdEÞ¹ã–Oú˜˜"L6;BL» ýQaNž7=¾îÕ vh8òN#®õwØ–©i]K£2¡Æ2—rì|éÓ°{̵ÀqöY™³D[·k òrŒ †mUß8á^×ÈžÊ&h¿Ê8¥ŽÂ™ê>æßÐ>fÙREöõ£C;ëÓ¶#Ç‹‡ÞW SÆÆßüà`_FñâhËó̓2IœÁE¡Èç,ä¨þd·F*QÇÛ2´MËGyÃßeä]—<òûŒ³Ë¡Õ" Þí=‡ÿôøa8Ðà¿1áÒƒŒW˜ýò»Væå€ÌüjÖrd!ñ`6ËóÈЗÝt“C üøæ@,Vщch<ÉzºŸâ~çøN¿Ä’+¾1Ì<)=òè”)©= Æê$®€û=LýÒ Iî]ïFé]äêASòGÛ˜QÐ(¤vÝ/P²=žÑ]w _žyŒÞ±¯3' a¦6Õ¾íA¶œ$ nÉÀùö^ì#7¶=!<õ(çÔe¨o´ zW½ñN^)Îî=ÊU•v¢tÂ`z©--Éà#‚â&ÛÇ"­.À²¢dÚœf ‘l¡ßïÊåóôÃ„î‡øÛ§.²˜…|þ$Uí),é%3» :9„Þs?â“3/‘Û-Bkú%l;a<|©Ò•z²QΘÕuÖ"†êFì£z{r&CEy,‰Ó—1àï|FlÎÌ¢r·žÁU[îú+wI‹ò’Ç¢3Ÿ•ãQ$×c‰î0"ë2—-N)mZ¢HWëì@R郮'ÒKh|Š/îÒ—%ÞwHÞX"¬]_D+>w#¶î´¾à1Z—”Áïž l›ˆµž)Aóñ1Ì1%é­+àÉ_ÊÚ­Uu ÈI‹‰Zi`NâÓŽìÀÜ(ã5%¾YÎW¹“šî'²í¢ÚLf³2X‰žÊÿÆX´®…s/RpŽT9*5HQ÷…šdà·0ÚÎØ†smS¨÷òtvãºøåp½ñmÈ^Ä\>ÀC;V•‚Z€snïLl ²¤^߯²ðþ œ „ÀüúáóG±^O¦GaÕ;{‚ÊÑ4ü·-®Ñ£û3×ãÄgrñÉOÜ>¹Ï›š¤,‹Á"¤Ž¬¿¼R”¼a0tÖ‡C‘ÖÌàV*®îµåBœLÅàœ ‡”ʺÁA÷wXæJ³æd’EÉXmÛxöéÝð+ãÎ"ḭ̈™·W0Ù—?p¾KfsoÈŒ@î‡mÔÜÙìž_&×¶ÁÖïÐQnŸ¦Q¯œzˆʃO$QèÄ4üתˆ¸Â„Ü;{3}Jâhê0ÿœƒâ¤¯UûÎOw ¾7eªèù5˜_ ÐG!Ò+Õ‰µ»ùºU„ØöMW0&rqÒŽþ#Ò$Óõ»QoigK2ÂÉ©}/p}òÒIÍáKtÞÀÍ«6S Ñl¢UÉ|—T oû¯Cëù.ÎÊ¥$5¢…Y·þ¼œKùIÛ%g¨°V¡îr!ÙÊ~Œµ>®ðÁ´‹ÝÓ­Jö¯ûÃÌŸ0áÆth“ÖòDØ{#ü{Ò®EÇ`ÅHyýj&»‰ŒC™“¹ >~ºÌZÅ<'ì{"L§;Öâw‡pzhk+®*ž}f1Íò<~»²¢¶êC¥ì+N@•Þl¦}úî(¹ £Õ2T‚äßé‡i½ÇvêÇ0ÜZwšùܶÌaÚ˜÷Vb:Ý=ч‘+!i†íú³‡~-ù‘}çÁNî,µ˜ëˆõ—± =éû¿úÿÏs®á£/r¯ÐÓkʼnݴÔÉy®fÎjÃá‚GìµS£ŸF´Ï 1ùŒ÷uAaÆ|²TŠ×ž²æÖàœbù›È®  ♨ùÂ’.zCÍZ‰‡’ Þ±:eo¢¢J ãR•ÂÄ-$¦%?‘Ì9M²6ÑæoJÄøï|šøç>öÎŒ'*à˜Š–+ƒ!üø ®5Ø‚*-ëèÆ$Ú¥ËC—O$~}Ðh¹‹þŽ9rôýX ܲ_C? œ±¯žLcì&æõŒ½Ô17Ç!³Ef©Ê‚KþpŽø’Ø’rÈÜiHZ†¼¡Ô¶ž ’*f/Õ^ÆeîtYLwþ¹Eß}³G—Õð³YœÚëÝà›>ŠÍYõÜ•Šz¸Ô©æÝ4gnòçbË7MS_ÍÎóȆÈꕤª “iÑõ¤¦¶LÚôUÄ“·4¿ÄƒÂF0v^Ž\¯Õøl¤˜«]v ºØ9ä41ewåŸdâùäˆÂ–õ¤‹Eã=áí'úY/†õY\Ž«3Ë KÔ&ên2Ù‘zܵ¼mØ«) &Bâ”–¼Æ•N¯PÜ1ºçæÃgÍç寧º7|Ö\EÊï¿eÎ8€³D Œ,¡š·¹xiÛ%4¯×£·²ÃIMÁ'øç KžF=g¶î±£¯½ùØ×Û§ëŸg1Îóä^ÛÊð’yÊDcUKFày«+‰X9›¾”1{ÚÏ’ºØÿ½w“}za ×d9Gf¬$º K©…W«[Œ[áSà‰[@ye7—óP§ÍÄØ})9™©CeŽOÇÝ)(¦s—MÙSŠ}š|4¯O‹zÌW$3„§SÿyZ4Ì_”ÑnÛF¯|ü r¼ÝxpeódW%R•Øm U‹ü‰`œ?½ÛÙÉ9ËKDÞ,㽎ï¤ÍÉgù`z'¯ ­×n`0-Šß*¥í>Rty¤1¿*@ž¿#E LÙ³LH £²EÐþù3 éÆßüTá[(¸ËàÎKü Í…ñÅ4¥p.½¸IŽÎièÁ^ßftlª\Ó ÎrX‹©P9ñ´<‘߇C Ë€úoGÃ5:°lÅ:4¹fÿ=ÂP{EfΘù·ˆé=ÃÄa³–FÍ\Ž!¯^ÁìÍŠä÷b7 5C™š| 'W¡ýU-BŸ»¢ü÷dTlY &wvA™|'ûJM“(삈÷º ÚWÇÞ³Qb£ïº]¦8ÙŽÛíeÉÏQ9òìïV¼ÏÔûhg öŸ½ ÆÚ4=‚ ]¤ÈŽ&÷° ðêC¤l>=L¬c (÷‡ÊÊ“Wvœ™?gÒ0Ýϸm0ƒÉþdƒ­G]°ô 9h¹Ÿy³B µ+[‰Êê`p¿>¬{¦‘¼Ò»ø_ò26ûÒoàþ@{øh•LõÅrºmÂIÁÜOÂJÞeÎôª»)J‡ä8ì¾2æµãMÜ>¸JލŒl ã¬^.8ú—S—=SI¸é Ø99~;3²¸fW׸½û ýpó\˜cJ z=é*ΦÃß”9Ûs\6÷°;w²Ïî‰}ûÆ|”¡žÛ¼WŸI'náÁãóÈ¿ÙAàEÛßP¹•ƒñ 3\;‡¦*®Åê‹2õIuZ†xJq9ðB{ üh3ï"«0Ìm¶Ñi^͸ÛR†Qy†<Áj¤r‹ÎÓñðUCš|Ì–x8ŠSÂàñ³©ÈÃjh~²W¾‡¥ú ¥ø×t²i‹¹+VP _QòNÝ…æ.f月ÂG~I„òeEœ”éîË—ñIóøg8 Ñä ÊmËEñjéºu–$ÓW_ãP GdÞbçŠÆ/ÿ  IS§E9„&q¹´îÀ%ˆ|-ÂÝøjœsþœ;‰qh`Ú¼F wÀ}ïï\±¬çh3¿¦ŠWa¯ßÈ$̤:†*¤Ž÷5cæ2€üÏZ8Óqݹ\Ìr¥Ž£{ˆ~û<8+¨+ǃñkM;oŦ4IBÓƒ`~áè¯%œ€*Bþ…ø·y¿\»‹Ëc!·ØëÜQ³ZšpŽ®'¿gûÃ1ÚǸô›•6÷lòu¼:ð¼ùrÈüßàñ´ÍL:ÝÆ¸9#õG¯ba|9ÇeHÒÄ:ðZs”¬[+3ètf\%Ž9:c11tV"½(°ñ *­MÇíÞÂè²£„ ˜^JÃÔ+除-¤‘¹žØŽÂÙ ÿâ0wÁ­Õ4av5$¶²}ðª©Þ÷ ŸdÉ­µøÊä UôD ·Ýh™%ÀDêПï·ÀÊ÷æDìÑ^îÆ¸HÆhä1dTx`ÂÏÉ8xMÌ>sz;¡c§+Ö3*ò„»?•½+ÙŽ=V®„qcfó]ÒeÝvIzîÈÑg­mÐsb:©·[KÎ2šlàš5p[h‰@á,†TßAWA ø~;“šþ>úÿú¥Íº1Ual^pÿ$,ÿ(7´²$¼ï Ч^€rzyÉ©‹–ÜêåÂTzÎT0ûw›iD·~!ª±Æˆå<ÎÇÜMxÕB Ç~‘£5ÔE\…ÞÜø²•xȹ¡½”§JŒÌ·æ#7[ÊIR^!9¨DCuŽàì+Ö¤KD’~;*†z1Ÿ˜¯î)x@qXÖŸÓ8s)—-Aãç‚‘˜iw8ŒÇ~˜Ý0›µŸ‚ˆŸÓè­ÛâsÏÈýEÝë$çb¬¾>,Êg†Õoâê,)ôÚ­ÁœzR3Û1ê•çåïý${g(ÜÜîF¦mX‚k•cX§–HXxb4Í÷°µô—Šû¤Â\ƒîAÛ-´g‰NlÙFWÿ– Iam°´õ&ùÓ®Æn•k¦ÿ¬56 óÿ%ýÃPÓÙ ïÀ0áÉdÌNÁ)¹ñ˜T,B…KȋɌ€zÜ67ËsÙÚè6fÏ ðÚ¤E½òÙ˜åí›ïD¿Ax.ßA`ç>¼­á‘‡*ñCc5fˆÕÁwÑvõ]ÕÄèh¸ ëìô ”Sp_æ¬i„£³XáÛuxç//•Uã…þƒì´}Sé¥Ã)ðÈmüµ[…s:9‘?<‰ÙòÍìQ)–ð‰âð¢(Vä*: Z³X—J™d½N0­ 9SË¥€JÐyv¯Šj³«Õ²‰éòDµù‚té/kª±§Ž–mSÈ‘0K2«’y8óþê=‡Y†lýz-ŽÏ„#ê¹£ÄÅ%4TuÎìý€ËÍ é‡,uü/zH"('›âžQcö8¾½7ÄJú‰‘«IÖ›¹T_Û2Òï2û“ÄñЦCh¬#œ¯6x`圫ø¸hIÒ{¤há­ÓÜ1ñdO,ñšw®˜†ö@yF{û7Ì™a åÞÇPU>žœoxƒ+¯)²î¡0J5è¿ 1zlì-Öh-¤óRõàêÂ`büÚfn{Ï9&ðêš²>ÂìÙÆpºâì<ÓÝËÚOy[“Abì&^pÞS_¬JO³ÏÂDìÈy&C‹—ÎÅpfÚzæ¥ïë>ðUÛ^Ï>¿ô„y¥«Iþ9¹Â© R6ƒGí¢½ùsuÌ=>+rÛf)ó,0g ÚP?ç\\ÚÂ,4Q€ÎU›ðâ_F°r?É™›ÅØ… Í÷ðY„3n\½Œú|ˆ‚5w}`­ˆ(¤Ô=ÃÎ'P¤`'[üh6½òÿW£˜!‡ËÆî¡ÅždfÙ·dRÐaLÚ/ÁûñƒÊäbT6pæ›áó£Óqݳ#Ì=ÿ'håµ¢ä¡8$æiÖ£­ów¥O*²ª¹eÉŠÔåW*NôîïÔ/XɈÀᥟ8ö䣎1õž‹7J¯2¶×Ÿaøf¦¶s‡ÄŸ2Ê}Âtme8o§ å¡ÛïsÌÆÈ$(+§þ-Œ¯tޏ’%RpPè>›‰ C`î3¤=Nâ_Sòå³.ÉU ß´Oà6>âk³¿ 3}Æ›/·Q÷ìŸþvôÇë ð±´jIJXÏùxÌå»f²»Ÿ‚i– '6û)7‚ï ¾» ñ%*œ]†(Âþâ\ö2öÙ¢dÿ•UäßlM<ºz÷ðBìbsj¸˜Ü3­ÁúåЪ÷‚èC¼ky®!ÈÐá´mø¢º—Ùq\݇7ûvðÓX¼Ê¹ö›!Ò#;#OŒ¼û½&öÁ.Õ¬cs’%‰T7ËÑMü/_ºaüñ&¸ É tŽœëÇ!WýÕ“ëÎ#ó¦"4~ÎEÙ€§˜.EF.=þ9P¿E–ÙþeÛNþã\Ü&NvMùàP§OnxÀ Á$nÜ;/<ÿv ˻ϑ¶ÎÒdžï€(Ç|]AÙ·g wmY¿Có·CõŒØp7’Ñy.A/Í &ßȉ¥màü‚Cø¬>²ß/Ï'É™ŒÈ›!öd¬kY"M4Žÿqhû]kç>èéÑ`”°žD¹*‘õoÃP\ê#ÓVÍz>ë‡-YÂä€F4Ð>à8C¯Ï›B–¿æÅ¢¢¸B?²oÆØéJÇÁ±ò:+±â®ü#Oµö®¡^Sí…_ÉÃã¯íŒeØÖIFžnÁ¶=°Hvs[Y€DëÒíæ=°†(°1Û g(Ÿ8‚EqøÏ‘æLùÊ^)ˆÏÏzBÅ™2LKÑ„çyÁ̪ðóŒÈד0pÚ˜ðfÁ˜°1‡ßO뙯®2¨†×á7ž¼À‡i» £fö'ñ°¶¾{‰~ó0ø9άá¡Á·>3 ‚àfÆu:Ù3Yp÷f .?]OSႆ W2×CKöÁl[?x¿®yÒ]¨ºs8Ë’c Zgoý'ïNpØ\¾ÇØyÍžÿ‰/˜|{Nç6;$Ÿ8Œ{Ûï2YÓA½",4·àùɮƀìð9f~ßF¬¿’†[^wƒÒ[ÒÚp G²fÑ—û{¡$J\3² ibÄë‡#¬¿÷·Ì Wd³ðÀnm²Jɘ<ôr²Ÿ”¯‚U 1xªO €ê1u2ë € Š.Àd± _TÈ´^H™d·ÕàòK~ï4…Ó‹v·Íq¦å‡0òÖœFÏÌ¥tí+é,_Hâíé˦tFºÍ$Ói±®BæÝ¢3x{þ2êï-Rµg°íðG ÕÏc»'BÙ l²i¿,ÝæõŽ÷†C\‡!‘qЧ¿žbÝ¿<\¤Û€ožL!‚ ¿ñ˜ó,Œõ8 IæÙ´$[RÈ*“ËØÛY'Yæ_œÄ}üz'1_H’­žy ¸ 4ÑÄ·³Açsjä`@‰ŒzíÁg©·Ñd†%]GëHÚ¹¡˜yæ ÔSæ:ûÚ³]Íb6îý‡î#O è "ê,Ûˆ’õ9¨2 D%õ2ð±]5Š´,b†V-Áë¿Nq48â²SlÏæ²I{úY‡ #Ú¦ÖÅÜß³ke5éö?X„tÌMYO|˜ 6¹GŽ~Z•Ç›“|LŽv6¾eV-Ãc/mÀMõÓ(ƒ–ÖqìÌUŸÐS¿†Ï–&Oߨе\Q:¿1/Ÿ…$‡8¬RÉ=[p’cîOC¥Ç Ìœij½¨›ÃË”8íÂŽXææNEn½„CfUL|LhG¼cJ–Å5ÈK¼£ó•©Úþ@fŽœ•Í´¤z×8(jD6Œ Ð_–Øé“œ©qÅŽŽFÞf͉%+VΆuý’쑟ԡóè]¼©&†©3?``H=«ã3‹ò—=Ç_mpóÅLbs>¾Iw@nt36š¯#woN¥NÓ… ¥ä#öy‚¶™¦h.JJ›ò‰pÂýI»¶2á:ît"¦#¢ä¶ÀT(æ¡Iï$Ñâ— ®õuǧ‰JX$}Bö-Ç_÷Aoƒ!.Eø˜ûëEµ±òörªÛ¢ÏI™ø„¶ùèàÆ³˜ñf,ú=w]bº.ß‚À"{r(a1‘Wª`5›ÐÀiÕX´õ>k:DÙkºv4¼=…Ý+iŠÇ}a˜'}îWŒCAŸ`Jò´c5ɽõ×!’Áê?ªà5”Å5ÒQéL¡€$h«p×íÜBk¢žqßfÎdÇ}WÒx´¥· åÉÁô¯\$ZKa‚TÙø;Œé83ÂÔ±@•Y—¼Ú»ªØŸo ðU©0Íîí`\v„ÂÁe|Dwq jòMöåÞŒÒÕ6„5q…±§fd5OŽÃÙ+ ¤ä¼\Q°†šL%"uâ8»kª}L‚.ËÝGÛ¼À^¿ZvTñãµ{'géß ðú4Ä^öŶPGùî*:ݰŸ=²4Mlb¶wroEþ¦…ìßT–ÍĪÚ$Uc=|¾Ͻö=Æb_¥9ÝuM†xèoÆÙŸyYzÅN”³¿´R˜-/VÓEÊÕÌÚ2ôÉkgå–ºc‹Ù~¼Åëå~P{׌Ó/sgn–eÆ*LÉ‹ÎuPÌ›ÀÌ8ìÖ¾­¬ÓSg|["@»÷ð¡Ç.Ôå—BM³2XERÁkµM÷¬ƒn²´GJß=…©QW`ª\ ´O·b«_s°· ’‰ü4tç%¸óDžòʇÕZŨNíèÎéÁì`W3üm£g'A@ùYX|äü/b³‡žyÏ`ÿA ÜžÚÈ–úÏ&3ÂhCzÛ­WŽÿÙf;IÏ;ìȤXÆ3{N1íÆêÌÂ!&ý¶ÿÙ‡:¯C9“÷ä¶ûø1ÚŸvw®@…üxzñ!šj%’ß’À'Mˆ¾íÒ&=“z%iî òjzu-}ó¼ïãësä™ñSðüKà”ݠ܌ڹ÷ð†9²(w¯q£Ç6d‡ûΰµ½Òö©¥I­½Fó†ãMÏG,ès6éÂL¶×φ.Y.BsqšmùÓïLÕwùÀ“½BŒ1"DV#ù×6cÇÈn0²+å^þt5’6â†\§jl©0‡GMÛÐL' œ7œƒ³Ú9`T9G¥4ˆ®{sQ«ë÷l‚kp.‘›-R«¦Ø@§ÑVLoè†]¶¢XYÁfó‘è=3éj³Aôëlc³Å›OUC£ncN:/ýâÅOŒÚcP?5ƒî4u‡›ýÔ̃óvSø}–9çuÛ~ ÒÐàã0±S…(•LÚï>DÅí¿—‡êB—±ýR.X—Œà‚Ah]DÕ~˜Óµ;xÆ>„¾óçP“=tâm:>xàŽÕw¦ÀUý%4[(ïlY‰ã‹9™o0[8 Ïܰ¦¼ˆÉÔR9î9õúf%‘Ì×_àÚ¬Bæ¤^+Ùö#|l ÿ'ïŒU~¬q?³B0L­×¡_–5Quô#Çb‰ï‚D>ÿ+\ñýНiQ'oé]dH‚[Ƙ‘oÓèJ/{=X“RyoòÈÞ”B—gtwsÆRffER½–]öƒpaKØeNxÞÀo­[iâñ)´Ï­†ûíºlé·¢â?Lé 9Q2›-±ôóƒõoc™ UÍP£{Ú–Óy2¶ ‹¸«¯†Ó¬ƒË)ÓVD·÷´2_ ;q ÿœ¼ŽZ«äưIZy† ø˜K›KÆ1áT LÚˆÚ³´ÁÍV‡îIú³L Ä&ä©ÜÓ.„¸{ì†Ù%¤ÒÖ›îé™B>ïV¤Ë"ÞsWï'äÒ’A»9…ÐÉgå?ÕžžÄÃEqdAï]ÖùÁ:ª³ÆŽêò£Õy)*gf¦*âÎ#u̧»0¤ÁC´EQ/uû¨riiî‚ÞQ~bÙßò×\©Eªþäs.ÇiMú4‹n ŸF¾Áîút˜¦kLޏ B±ð~ºÌ¼ál ƒõF¸P­Í6­Ÿ—¡Äry1¼{È¢eG)†%ËЗâ¤Tý<|ÍCx7gãÀžlœ*ÞØ ;åôk´ƒùjkÊ»N~Ëî¹4•DÄÕG|ÒÁFžº^_t >³§îËKÙ$ã—ør ž£;\Êäµÿ½ë‰¨æ ÖåÃ~lÛN6_0'σ³qç4qäò!{Ðß‚[¶d’¥’¸°¤$„¼…`Î]üÊ{æ×ðB¶¿]ö•—f?N…@wPÒ–kN,„ô˜Zæ|Eù«jŸ—„‚˜w.q]õ ³]v¡ó¿Œ[ô2?|Ü»å ðzg}hI.0+& ÔØ›j r’ [Yá xÒÓ„‹à¹cÆ¢$M†”â–6aúèŽ9ª*•3©Û¼©¡æ,Ì9MþÞ¤œe·™àáøúž)+ùðë°" L…¯àúëï ÅPYCG Þz’µ7{ÂyúçŽmÒ¦›¯écôÍ|w²¥e>™QÊìšo“mÉÂY§ðX¼Vò,§c3÷àØý)$ý¨<½°~5LÝnÎZ, É SÈQ©RfÕryêYe=£ H&F~z”Tç,¤{Cqcp4 ÷$ðcu:ªÞšM~ß³¢·Γû‘—ˆµÌ3¨»Iü”¬˜±¾rÒ;ë8i”Qi3aôí17Â[Ñi¿¿ÂžraÖz—ÙíYÅÞŒ{À®ãøPŒª€ŸžôÙbÈø‡Ëß]†Ï<#õvNo™Þí8ø¾­\¦£2äÈÚ\àÿÙ ÕvÅø/a{}œ™FŸG«€ë‘Æû$üìIUaª‘æLM/{3*-ûiÿ’•øg Ô¯˜“ͳ,áîÔé ¶T‘lZ¸ˆ‹)QÃÅÓÉúóéÒ›Õ³û>lÇÏ##ŒlˆÙÉãѺä¤g¦)*BûoIØtE‘éš‘ çë6ãÇòLpn8n›®àÖM‚4ÓŸ$¸¡ÒfG¶×ù-öuÅÑ”8ºÓ_ 64ᓺªúæ¬v\ØçH{ÏFâ_µ•dGò6æüÌ%ì` Ž]´æL8 ¼­‹Êæ’ë4È9·Gx%‚0•µa0±fr×/¢¡¤Šñ׺GŠTi¤ú¤o=žG’tµÉ ùMè²ÅîVá„ÅYFѦf&(SÏòýlÌjIÌŽ,bvv>e¦îª î›hP].Ѽ‚K-¤‰¶ÈVÎÓ¦r «žúZ¤õ-¢qo7\º¬N¯ª-áÞý†Z‰›ð7ÇÏiá¦zJMƒß‘%ÿ¢iks&È•n …Û÷Ò?e«áB« Ø’‚SJVÄ•‡úç 03°t…1zÿ îMO#âÚ¶$®ñÎ+_G¿¡g´*t‡H™%~MfÓ Þ&Ö„fN!Ò©…x@O“ªKº‘ñùÿ°k«Ñî¬ÃëŸyˆËûéÔENÛ´¬ÁÂ>ˆ?"@VÜ(À·ÚäPÏl&QÜ.ÅÌÀ‘‰$ºÐ ‹°’)¯V¸˜þ94Éá¢>ÔBÄ|½ƒ^«œÙãî}°ât,-ú`€üXßßëP•IÔà¥ÛeÃðƒªŸ,ÿIEú; —ùåë YM¥ {O‚S|¿I(yqg±“w%·ôë~Ž5‘Å-f:ìí0¼—?ÁøßÔ$b­µÌìOÊ(éQù/ªðçì࿎/žýe{Õ|¨ß\^²XTöÍ^Å=³#H~È"’Bd<¢N›j‘©iÔðmz_ßÅ–í€Mêy¤å|)gûܧè´J•Ä|˜dAr/Ë„Øù]ßÍï Üó „Æ²°\N—L7”‡¾PúÄL~ð2ÙÖ“E§ÚÒˆY¶8¢ô›%ªñ–UyÂz>|3,'‡ÆŸbg½!­<þ³A3K”ÊTýÆ/cÓés=JEèúó³™Ïwñð¼4F Î“ü]û#žì£ÚÝÏá û‘swÒëzXìÒg[ÜaýÓr¼UýnÌ¥~Î`zê=aÀXgÙ4,ßu£ûM€L·%ËÞ®tà¼F¯§¹¸õo(.ý(I{ƒaùÔõý$ ®Êa¶©£1G‹Ù+*Çý5^Ã!û1-üRD,Å |â§CÀr+òTf-1©†;Þswøí±ËÖï#ì ®"Ýf–76¬¢w u«6÷o>oZÅQÿ4•<6&«ÎE¢Ð´Ù8ÎN>•]$I[:´êá=.¶ÇÂî+Üý°—‡­&×ýA1i€Sq'g …âŦíf»FÐÿráÝfGÔ£„áßt¼œÌMêþÀn8çG»¦†s—Þˆœ1g²(âö@Þl%²I•pl“>õ¤õã:àRÐÂn8‡ÚÚêÇ'³×÷jÐn×2k/O0%o¡aI;[h0¬ujóÑíw`¹oü½§NÔ\÷àÒ}.T¹$•ð„êÿÈrŽÒ!úKׇ8Lˆ–ÖX<¸ûøë+cÒOK?áósÙ¹«©°s=½ÙƒnúƽXïEŸü§K:¶ƒÇó¨ågʈ»‰ªÒFüóÔ• Ô¾…é=A˜4ØÇr6Òý<9`þ3ëW©Ós­É´/hwNîwüÛÑ‹÷ä…h²éq\¾âƒgaxßBzééz}5?Iîý„rqòä´®Ó7=Ñá±×yØÀkK¾îS'¶ ªTÞßœÍï†K;0ïæœ™sýªâAñ/õ¹ ¾ÏŒðžº*ÍíÁÊÊXã¤Ià›5^55'Wâ¼a>ºvÈÛ"’×âì£À”ü%FNipÈ]IAZùZ•\:ƒ%峘½¿ !Xˆô¼ÏÁës  uC,¨º]‡žÃ²évv| 'Ë)”•Ýé§ý¸˜¾Iˆü6B/çx°GkØ×ýFh™lŒK„‰D^Þ¬˜ížý¨¶b-õL½B÷*Le—ûóQ ;7¨ÜàÈšMYB 6ÉÑh¿lfÍ$§:DÛŽI1›švUé&L÷‰¥å±ÿð·Ø,ê9/“ú K;Ð7/ÓÉ2‘ZÌv8@•V5Å€rt•èòÂ+0+g#mwýä¬b°4l„¥ßØu¡ÑÈ+¶~([ÒsÍa°Û® G­lÙ¥ª¢(Ù.Î|¬2'uÒáUJ¦®þ 7.¦Á5ub%Xo”oÀc Ö¦E˜æîˆ£Mó•n<{¬J,­®RÙ•Øè¥ÏîÏ"RÛÛé«Ee`œ«GÅÜpÅ—®C¨ýRòËzÑi!ƒÛVPS€¾{‰åG€}¥ˆšVšÔWz3ýIsá€ù<1L8'móA°®€Ê>¸•U1QÕ”º­›Gþ|¡ñžò4à÷?˜fŠ?_5²Zj§IyÊ7-^@émcXþ¾îçü;% ó S:ûafv0c¸Å47§S\ù…Î8Cðü0*l>””1õ"´0g'^Xro–O®Á„gÜw&}×nºcoƒáÖ°O’B¢|‰\Ãð Él¤fè µpüÌ΢ L9‰ÔlP¼“ú·ò°Tö!ä¶Å*ÌoU²¸â÷…6Äv'/±?rÏp¥Ê—¬Øê¥ª¸aXöêHЪ,nÿì…4È•åLÍx,ÞzŸŠ…ÙŽðéø?Vj_,ìÔ}ËTêhAðçô¡îqxxc)‘²Ô—î‚d6w/Ë/kCß½ j{`ëÕ;4¹n%ÿU s㵡#lGw«*«å¸Žý®A#YR®`‹µãØÆñ*`b‹QXSšå|Þ…ë“ÛÑ3u†—~ãÎt‡,X>+´{ó'0Îà[¥Då´íXcþèýÞnj¿Ñ_‘w_2./>3y+R¡q{ܻ܀ýyª(s­ž³o4‚$F•±ö×ý‘o³²›½¨/GŒ$¨(’„º|¼ã4Ù^èBÛ;OÀÛXŸA}†—±gÅ·Ò_liÎçH·÷eü§JѕҪä¶?y<ß‘DöYÒG|ñ”ìò¡]‚¸¨p`ôþhµ€.«è€UŸ ° û0=Ù’'Ð>C›©*ÀEG¦z¼Õ/þl806¹¤Ûð‹µ:øúŽ>™Fž¼ò"Uã"Ä»e6Þ¡øC€õ§xàÚÓñ¬Ò'\üAëaµm ºu¶/~†É~«¹ï–>Á¹]›ˆEØaÈ:4Ä,_pžŠƒÛ3¼È1£ÌÚþýðqµm¬ßDÜ^éÓIØžÞÁþÞ¾‡ŽNùÈ}¸¦Žß¶&ó¹ ÿÏ-¾å³–=Ö ÿå(ÿÉJÆÅi¤íÔùlIþäbç·|ŠÂF¤âD5 È¥Ó“¾ü¬P$0Nù«ÁÁï<ä©A#ݱЊrƒÏaÛYWÐ ´®òq~–ÝSC³æ62›Ä¶AYO|ðÅ)£§Èôš¢ ìÁˆêi“ ÍÁÄâ…)ý%´—Ž$Б&;óâÀ­óXë4q í׳tà+;íæuú0Çz˜îÕyKe g¥-0 Ê‹¨¶Œ*ñH€§×3 ¾·Ñ%ßa{zRÜvkñ>‹`%o²MüùT²ý'š9‘ BÚ"’ÈÙUï¡ûO(^ù€B‹ññ¾yÔ`Л¼×±£…<ŸÙõŸÄéžÃçÈÆ =dŠãEš´è'ølÙQ8ƒ¸Å[ßâ(ØL.—\€¢°Dæþkd]o¥SžŠ™d(²“»j¨½Å_•LóÕ¯l°ÛCðoà‡Ê/±þC,=úáÆ/œÊÌã ‹@úçü(»PX†¼ó˜Þü Z’„Kü•é— |ÔE& 0Šª¶P’"ØÁ: ñ €PžXeŠCGçbÑyrt‹m!êûÂzqãÉ$ŽN£ß‡v°WºÑÊ~ ˆví‡ýó}À%™¼ —G_%iZ&#Ž6Ž ¤!#s»† ò§>~ˆ“¢<e\ÇÚÙä—ôüz–ä™0ã¿—ÃðÜøq"‰-Xt9,âŸ|ÿÕÛüªa7lÊ‚ÀƒÚÌuÉtx¼¸ Z¤Hq/$Ï$«™Ž%Fÿ] Õ?ÂêYaÐ“Ø ÓU1Ûº˜E£Låìåø¬?g¯ùÍìÜ»šxÔÌ£F~±£Æ¿à\>;=´iµ`ËóHÖþS¿½ŒçEð¯¯b=¯ÀëÝÖÏI*CöL(ìúXÞjðE%uœçàáI·­óêèÇXMFvy+õ]˜(Ÿ—í¦#4`z%X®p‚?I‡IR‚1â·d[6¤2«íh¿ž-©MÕ§IOOâªfYz缉ΙEþ=IÁ2 Ù°t‘Ëw0#!À™¶”ÝÁÙß÷,ÄY6ÐORuåI±•[qtÚ•qúlE4ØPLk.xÓîyfôêƒI=ô”Ëö !]j/Gý*QéËTõë´zÊa.ˆl¦M®ŒÞëdŽ08þ­…ž³¡°8jµZ`F5jXmB÷/an ÙY‹“ouN§3{Þ¸zÁ€ŠòÚ½Ïnƒke:û÷ÆrTÚ6[‘¡Vz¹¬ÝŸ¬ÇõbR\l©ŽWDýõ“pse.ô•! g°ŽSìè‹ûÛhÞÎ>\ôX–f¾±ä2-úðãø(e.FÍ_›–‚ñ& p>}Ùk‘ÂVl2¡“çàNȸøíj5fÞÀÅðÀL™;åê$L=BszÅ)¿ÀœícÊîÌžCóæÑ?Vx¢é8kpú+Î_áOûÊÙ¯¼™ÃÄÅžb~õÎ`´<þ/˜yJŒèìÄŽwÑ4~‹í™P†ÍÁéÔqëKöà¿:ïL)ûsÈ‚,77$%YnäÑe³W„BPØIúo’9V:fCkÀ\öPÛmw˜e§?36+dÈÆêt9èÓ/ëi²£,äB¢þ{0ùC½Úõdá»e°Îï=™vVŸ–W3Ì¡w©öó H&áÅÃ+è^4˜6Éúkt,htŸ#iôò5‰‡nôy¢osIñ7FúS\²œÂäuÕÂ+B°Áºˆ]&cB·ÌLTÅlÈ0£Ô?,k`«æ«“ò÷ðÖpcƒ`Ç l]1•ù¨Ú€ß__^„Œ;‰³ÕO0OŸrù¿®fÞ¹+`†—&^«ªà,ê8Ã(,<‚’Cƒ+?ášÝ,:Ä{­>x]d 1Fc%|-ÅÝÑœù[ÒiЊMèsáW|ÇeùÁ¤uˆá§³ûñúhÜUÊÉä§ÛTøðÐi–ù;*LJîÞ@·À~ø¯ö²Ø“U dÎñÐÎJäÒuª*þ»?'⢓¡XrsÏü:@Ÿ¶puŠœP_H›,Ír¤k XþÙ¥¨¬™L'6$@²f"Ô¼ö_S&„¯uaïµä#gk5y¼S lÊܨÛ^ x¼Y‘&dEé q :•‡tϤü7šàM{T¸ƒ‚²Âd<Æ”K¹ðæÁ2Œ²|ƒ;EBÎÚMpkå}ܤ‡~t®ÄeX[ÉƇSð˜^(®Üêó’Πµ…5¹Ì'JjlîbÇÊ~<6|îŽÖ3ñÈž°­ –sS_´€ï¶ª€íþ{ew4s;tèvÇÌPÀàúL']£/àࢥðˆGŽˆ še“¤TËrôâ.^ÐÞÎ6dƒóÅhQ<6‡~laŒ–;`…£=lû¹~p û7²¸£Äˆ˜”:3·ûf1æá`{ð=˜ÊÎG‰•ôÎok6áñHÛ܈+ì§î&ùxcc3¶u™’…J$¹§™µç3¤6ÜfuÇ3ðÀæn|9=ÇTƒ!”ݵœ¬?»‘ܬ´€®›J°§›Pãí ìÊ>ÇŠ:™àøÌM$Gñ ŒÝ.a§Ží¥‰µÒä`‹tëÀÕ“ñ'‡cE5õ€søÎb^—û\kû+]áZþ[\4þ‹õø†õEsÈ {[2ÃIŠ^ûݯ|ºñ±qÓd×¾š»5‹Ñ87ûG÷Áûóå |àzÃÔ†JæW†ùúK“¶X6ŸU:så’õbžÂù3ǰ|ËZ¶9~>U<¼J¥iâÂÎýyâûp[Ÿ8¤Y/ƧÚÙ ™9²~eàX=÷ ï°”>0w;^‚ÖØIfÊöJømç€ÁÕoØÔø'x/ògûÃxÔnI u’k¼ù¨lqŠìÛÅ M¿Î”b6¿º*¿ˆsÈΈõÃê¸1>l=ò€Ÿ¾×ÎÂ6kÛ¡“®7üÒDÉ‹s1jÓ æ†åO¸=µ éÊM &g@â-f×;VÚ0#R˜†¢¬w” É›³¾Ã3wŠÙŸÆñìÜÆÆHÙ¡Â÷|¥Vv¸E!Åô·å»LÄg@uýBê9«_«^B{Ir‡ÛÉêa4é´ÔBƒ&+€Ó™Èÿk+»Nû,X/­ƒÅìT’{ð=Æ“»ìòè7%yáÄp1ë}rï~3m sgݤŸ5e\jÃd{Üè L_[üƲÉ}w+J/s2}§ƒÀ‡Ïx=F¾±Â¯¤— TmäXž˜Ôÿé¨mÓE}ņßÉŠN¯¶„_­Ü…¢YèmlMÛ¶ÚÂך£èa¶’\ò‘…FÉyÜG (Çá{FÃâWq î$渾‡ýóXÔçyÏŒn€ª Ð/ù™ùµ€‡œ2°bqn7R`Jf¸SïSvóÕ³ òK®îæFˆ´1¢¯# ÜÝ 7®~ a§¨{àÔ>Ë ¥s‹Póðät{\ýŽu›¦á‚¾<ÂAoÎê²wõCWê1lR×^8'BŸï6#û?àÑKÜ»ùó}ËV2øg˜Éz%B·ÿº…Ë:?ÎæóËã– ˜G›b`Ά‹°kª'ñÑ2ÇmÂsÉ¢ÔwìþwB4`Û"ÚP4Àp7ÙX9Éqg¯côÍ©tÿ“kì¹dzÐÐÚÌŇ WRŽŒš¯;añG_2Ù|"¥VFǶ>ƒ®d¼õý(Q»‰M$Z½Œ¹?6à ¼Ø’¦]dÊMµÉªø$&~—lCou$F,ã§I3BÙ¯S™&K%j½ÝŠÍ÷%¯NöÃë§Wa¾§1q2ƒIy²Eá~Ø›N)ƲQsû±]MMð¼ÅÜ2ÄÑ\‚L¯¿1™i!Ïø_.‡…}쎛Ψ({5 xÉ•C)löÍ håõœur烡_;ÐèîB*×0‡ûìów¸Ôž EVrd±í-¾¶ƒä(ïƒÓ ±¢ÓÌĽ¿užè¼£Œ•îPÿÓêg]…¿ ïáÀJYÖ‘e¯ÞŸ…2>+0Ví>G›'ŠI``Í6#RïáÏŒÄv¢×‹L‡{Þñ¸ò¦„Mgéém¨Í¡÷¹&ø{™=̵@ûßµ` ÞÍ]¾<.a7%­&o^7¬Ópv?'­QöN¡½Ö£ØiúÎÔdáÕû—“§ÀÉWî. 6šÅŒ½¹$¬ø^µÚ´i¶<½Ÿ(†w«( ™ûÂÊôœfèúƒ1°©á7ο”NWŸIci£=»¿ÁðÞ|¤û_…Òsÿе5 s—SÙG1l´´%Ú6ýÁ7›2ñÏO1„ݦD¤)ŒÕÍã¡Æ;†àÝï0r~ã,Ò—T…Ò:åÜc·¹ Gö~eœ/žÇÉ'~’=½5ÉÑêÛé0‹1J´ Þ;÷sdkÕÈzþ%äÀ6eæÂ¬jð"íà™¹’ðþÞEW+Ì‚5[%I”Òö“‰ Iù ÂJ¢d¦âwìŠ"¹éfP³z Òx‰´™4LŸfgî†ÂUôN§Ÿ‘1¼ÉÔÞ»‰›KgÒîS£¸¸– Md#nk…¬Ó™ÜaOøç˘ƒDØ%¸gÞŠ5%ù°º=/LW¤Ö8ß/|š˜Ëý¨ÆÝÁçà§GÌÊà£6¾§¹ ùQ·ÝçQkÚ%,u¹‚6f°«Ë|H|^•I„sž01O—”Ò4ü³Û–*ÇT3fN?AI&Ϭǣ×mHÿ}ÀCKþ0ÿä³{{ðë{ä?uíg›á‹yÏÁü@,j.fVvv‘Ë×Ö7jt§ÚlòÁh6}\žCr6?‡*”hR£›Jò°ß» Òű·÷|åW.¬¡Ð\8p;›kŠÐ‹ÿ îí§ŽÛ^áÅÙŰÖá(þ֌º~ÒaXÇ)Xf×àuu:ìºöng»£Þ070«‹BV<¬‹ÕnvsEk1{$i3lÒ*öBÝCFú÷eÀ3j°½n:ñ7"b«Ö»dçá×\¸ðän‰l¤[”ACá`¢$Ù¼]•%bÛ0o ²ÙY¸çxïÎG¿}×&©îƒò‡ (»ý‚YI[΃pF8®S~ÊMÝ} dϤß:1”¸Yƒ*ïÕ9ósäþ³u5Y]é†&¸éâfÃ5Šgƒ/2Å!àèñ]zF $¿1[z4í a¿$11)ÿ/©Úç ¯|sY¥O13!*܉€¶§›9¹C¬½X ½qœþ;òçÚàâ`.»öíWÜ5âEœƒÎÁ¸Qù,Òy‡:)QÖÐ>ØFüwdÊ2E‰Äö+ì¥_räXï˜hßO•œ[Ø-‰J°Æ²™Qy•‹5ÇèÒ­™µ§Ma…™•θí!š´7Ežžø$‹KNÅaæ¾AöPÎ;tóYK™¯xæûqùs<ýÜ%S«@³à(•ôÁ75*?8kø˜LQUlÊöcÊ#THؼ"q5‡Þ}_^È•O …MÉ óS+‰Ç%ÊŠÒ‡ŸW0ÊÒð<Å’<™3*ƒâpà@ ¹óÁ•|HaŸ½´¥Ù‚;ðÈÜ!¼ª=—ÅAŸCpï„ý·j:±¹/M^mã!.‚ò`–+D»wGÂýëRÌð—$VÐç8¹â/e˜ÎÄŽQn§ás( ØÆ¸™²ï¤â jÃßK OpŽ®Œe?/T¢Ÿ,`ŠÒ6Á¼`Ut¾‚ lsˆÌ4E²Úà§þœÛ`3žÒ­;îÐCÊÈÇ‹<4¹= ‹û‹õ‡¢8ßäN5”–%Æïö’-MëIc„ }žRÍ8Lqâœ,]] K&YÒ çn§[î¶âîTv‰† ÉI¢ „èëlRtð8•ÓZÏMÛ Ï™Q^aÎ[R²]”éÖ6¦w$‰ÿ"öÏ*½Ú™T¸-ÆèeòÄ$‡£´ÙŒ,ú!Kš:6Ó­ØGvÔ²ö&ªìÊf•ô#¥mX´8–kBdË~Ú´˜ñÎb¤aÐÍ4ÿy]E;nªáõ­´|«·s† FýÌCÉÄô‰‘I=ÑE.ü§¦XÀ\)yÒ*£Ÿæ!7Þƒê‚ö¥ôW ç®æûI'· ÛðÊŽ’mÏé´xIbúCç,ÂØÔ4Ò¨tx_6ô»yüWó™cÀµB> ’ÒIþÜ VÖ;Á¶?›uvŽG¹oáÐ\¾‘м(ÁÖ[çH1¥¯,wÒýûBÈ#¡ô†s+b¡¿’Ü©Ñf6Éçapäwâc΋ƛ s¾‘qZˆW-©u¶'9‘ñŒþ•cö>º3y^M8°œß¦Dg‡ÉV‰dZ÷”ƒŠ×y´œrm­ ÉVù0té=ƒÑº™t¦ÀBR®A¿µýÃ驨ðÌÙ„X«2”ÉËÊd<¾]Ûàk°/®ÛÖ½[Y¿#½¬³ù:8ü³÷Ú'àuåxºOÚ‚ªÍ¸‡µu†Ü8¾iäâ„,‘¿Þ#»Ó ©ÔͱW´$QZEŠÆ Eq¢ûa”… e`/v†m Áy› iÆ-=rMÚª•á·ùßÎf[ ÌÈLeâ·w3Ÿš×›Èø–~Šó³*‹ªçD1ðP'õKgRa[Ê&¯·†›×o“1…Þ†]CCè&oKg|þÁz¿‰K娮3/Ù{ºfØûi»Ï’¡G¬mIØž.öG¶;È|ä'K–PÅåaì®U]ÜÌÞÕɇôŸQôлã¤J¿6‘¢S»á¨”w6= ©ÜÝZY‹¦r˜sÔ—ŽÖÚÓÚ•fäô£…ÜÍr|tU׺ÿΧI»ÍP]ç:†t…Ò†–«ŒV”7ræGpb$ßàý`sÜ›'CíýSÈÖñB½.ÊZµm€ü{‡¨dE6¾”ÞJUýzس›£Pc6<ì>‹GNr°Ù.ß9‹¹í¾Kα<*âV _^·°ÊnçÙáw؇ºtñédñéÛìû%gqùúBÔi¼k¾açƒ>fªú)è®ùË69«1ιòLÕ±È}dB®d—Á‡ˆ¸#áAƒÒ¬}ä¹T-¬LãÕ£îøv¸Q ƒŽëQÇ%2 W€ÿ!㲘½ä˜¦Uy»r.³Á~J c`(NzrÎý©ÆT‹àqël½0À¼Yù˜9ø=‹ÕùÀ¹µy&ÚðÎ"Se%àg:|¾ú'ˆ £Ê–&,œJô_Û2©6/ÙñïJP?€WøÌ©aÌ$;É1ÕËGç%ÐñuM»PK"kqEþ:ÜŸ Ær@_NeVJì`^elKÞ¥pFn<‰Î‚ ‰_L¯f~Ù,‹/äx¸¶RÄÎ_Š?~\×H4‹k6º!©!$`Œ­7ᬡdå2~ ÇéÉÁ1FÊà4 ºz‰qs`ƒ:Q`a,왟ˆ)ºäÃù4âÛu¼1qàü‹”$ãwQô÷ˆÙÍC–TB¥§ W…G|º·Ä4%¢ƒ3v‹IµsP½ pöçH²x‡9Õjt'of¼Œ#OàŽ«3œ-¨AI„¼v×ÑÉ`(;Â}Þ=,êo8È͵$»ÂíˆîG¸ÇK¤|dªú—£ëϽ¬æê°?·”J¬ŸN®0Ÿïi9ESVoÅóšÒ4â[(铬é*ê•ÂK­"ÝèƒÉôÜÑLøÚñ™Ùuk;ÇâLU#.Ž/qWÎnöòè zšÍdíl¦¶´ mÕéö³µ?¥&¸7<€zIï$åûøàc_á$À¹­mÜç—O‚ÛA3j:ñˆÝë7ù¬ÒGU0Û”² ›‰tz"9ùkiQ€Õë1¦!’á½2ì¦ ÁeõWñWön ¤îW3iéŠ-àèN I£DNÊÒü¼”ü§ Ý]Yð¾üè äÁŽo]üÊžz&K㎭ƒ»Ã5x?¤cì=XÁÑîÈgÔ ’pHCŸ–îR ñùõ^Θ¬µ†)4šJxg”ÓÌÍZäªë¸"F'šOÀgC²¸§üUÒÅ)ÚòØåºžLÍ9Goøƒ«g-¥/Å“Ƶ~tëGª°+—Ní=IŒß'áÞBºëäM¿k:ä­«Çþ—s¨¸n2}ÝyOôñÒm¶gQ‹ÿ$^‘D/*6‘¦´vêu# oœŽ‡Ÿ4˜Ã'FÚ#È_¼L®‚µ¹(Í«¡ôÇút2å­´W ûÄŽ’—océe]89y9×/¡®Sn@D—åŸ1Œ6ËÎQ&ö$vîkÄ%B:ôŽ@rk=•Ê%{Îò“öÇ&ødK6]zŸŸö­y‡Ö‰KYñ¿(ðk%YÌß0zì )«À€Ã_à×! ¸1߃𿠡6“š%GŸ‰ÿQ çcå˜ ‹¡¸u-U®Íc"œýak³½žs•®ÑΣO`üÊ*^iJ"zt‰à /bza™[!Odº„èàŠl’²³™]e¸œ|›sƒ&;ÚÒKÛÓÙÆŠÔâRƒ_õz’#^Nš=îÐY<•dþçs´ûótœÕÍü´ ¦Ìš®!ÝŸOáâ S˜Ãõ¦¼;:³©”êùÒ©þ•8´n]ÔèAÒÜ|˜MUDÁÃŽøëÝÄà5ÊtÊ*ArÝL™HߨFwì[7+åIT|½¿lÊÍa½¼ È‘A^Ü9ÁÞgIøAþ5ŒÜ™é×`{1ïêþ‚Y¤uŽ'Áþð¤þ¹£Án Ñ¥í– \gÐ?™õ0gí5¨=_Äô®„÷rS™JÝ ö›Ì2Ò—%#$]&6fQÔOHS/‘oƒhd=—áø º3V£'W(ô<'U,ö­ý Ù³˜³Óð"Ã)[UÀý_ ˜Ê¨ÓEYtÔ u¦fážÉyXÿ| ,)ż—n4¹®“u[*‰w>±¬”¨˜OW§½OEIÆK`às„zy_G±•RÀ6…å?D8_ÙÕ„Ó°>-xI3jq¸`á¤×€®ðì>«˜ÿ¹‡½~‹`é©5$uX>пÜWšÞ´îx)Œ¬:'jîâÏO‰ÄïÛ,XŠù  ÊÓšxû{‚úö:HœžÈYzì0äê¾ó é¶&¹ÙbF]qG6d*{èM‡“È[aWRγ”ÈHôAèÒ³…zpðþ>&ÄHÖ9Ä´QE*¹ÀŸFÈÁzZäЮ^&Ùò9*†ýÂ[ïd¨|1-¨Ã%èB2?±*k€Ä,'_ëxÑÁøGÏÁŽþÚ.KW””büœeX½½_ ÆÑe†á¿Íÿå(¿Ud¨Núü!Qìx¾‚};Àî*Í#å?æâ3æŠÍVjgN¤ú«1ì×>zOt Ž|JÇ3iUð%^›Þ&DÕE/ª˜ƒÃ£ *ìÅxñ³0JÑ»â3ô}q9+ÇnÓ÷#ïOGÁ2ÇÍdV¤ ­CÃ)<¸3 xp—qÔgÜ"RWç>–?I2e\˜S?À¡/ß¶£©UOXñáôpÀgfÊÙݸcèÕ†gðŠ#BÇ{¢Ùu¿ÑTg7s‰è°;Ù2¿œYíJk7¸§…âôT^qõ¾F‡]ÿLêtñ!Ýœø:EÁÔ©§¨ÿ¯`8A‹Ì9 LUÿA [@Çu;€ÂÐ7d*+n§¾ûÞpPÿ ³>%›–g×ÒSQè…Êy4,¼­¦Ì#˜8¼ÑN™¶/ïñ‰úT\¹kÒWÌ3wðzÐLP#d0‹¢.㼨OÔç°ýUû¶ì±$ö7à’—:U ¤nŸ3aóϲ™­[ˆpÐAÐ9ŒŸõÉŠ;rdVøäwúñm±‰UÃèù‹ ÷N©ƒs²:™a0ã´Óp®S ÚÐɦ|U!<òwa¢g3ù.ýܯæãßÕdC»9˜ßßþ-"ñ§™Å!Ið³..{vÑ.›!cúÔøÊèúΘ/“$ÓžªÐåœè¿Ò ØÌ»…þ »ÀFU¯Â›RG8ëŽã)¥û0s÷\<>¶6´ÂïF¬†-ŠÆðxÉ àGWøþÉç¿Ão¥ó¸‹OÖ2»ßá»í™ ÕLaa¤œŸ{ –ªÎõ‡¾¥1(à±&8»ÀŸ3"á³iiŽ ̦U§ìñÜÞb»¸\/*ÂóÕ¯ð®[?&÷ï æ.Ù-J2ä®/É:=×òhP³"ûgël6¼UKQìà>”îÃBéI®‹“!#=(ÍŽ\ƒ®Wïá{W0fý>'r¼¢d‹¢·Œ¶O¢«âÒér¾à;y/X¼œlª?ĉ~ûLP„ö oy|¯ÑÁ'#j…¡Oi>M ã«–ŨVªA{—eÛ±çøáöÂÉxßËNŽ ½TiJî|æù‰=5³RމЈ _@—6Uá¡ ‘q«•40&võaý/?¢Êêû˜°º–+<ýY=À¨,§X}$ bl¸PdaA²ž;amí|ˆÑÒ†}ígqÍÑoLPT÷OXuãÔù0Ñ'@þYí±_…ìÎùºî&– ¢ÆËTn£—.¡÷às¡¬WõƒÀÝÇ™GÊàH©ÝÅÛaËÔÁâÎõI_Œ7W©Vü·ç,¾<"Df.RÆ…ÍøðX#_ÛÅY¦˜ÃÕ{¿–þ×÷óO‡ëiLYÍnVK» «gXã%ë5¤òÞ#øº%Ÿ¿6¥æçæÃçÁ²DªÚv¿ñæíLÀ­_P¡µã®ãݬg8,'AZôüÀ¼FgØ ÝcñðpÕ'ôžï éâkhDñOLŸ•€¯³2YOAÔ4էûŽc½\)ÛúÖžê^žŽäÑû(y4`D´ ‹ñÓN'ò1-‚®ÃÑ"dy*Slsãí& ƒ× N}’ÁC~ñÓfÚWe_3ߤA,Ã,N÷×£DQþóÚ² JÞº’W£&ĬkZUȉú÷3Xhó:âi†h¶Gtû•éæˆT¦Ö‚û>€9GëèŸ!RD§‚Ø4W’$ÍKJ•«`t8•½õˆ;P~¾´åšuÚ™dà“6¤—µÂ{Õ… b©ÞGïnØINþ±#ï•ó`³Ø=ÜÊyJ|GŸLjh;cKžµÇÒ_m— S8œ¼³™J"W¬¢ß=—‘¹SïANe5<š"DwuäQžšµÄýUýܵ8Mý.Œ qt¦k §xEªeoDŽÝ;ÌêNÓ¡eÏPà|ö¸ßjˆõj~ÁŽÍ¬êfퟛ3Bõ tz±4ùü@õ¤é²Ðqαs¼8SN‹œ) ¡á†P "ظ“øÚXú’Å~ç¹tŠÖe|îI„l—G¾ø#S7¶ GY®9\Mô߬'·Œ-°eήDÔ053&kbôè½›¡ä®ömªË#Hjêb8<Ž?ØÕGòé•U(-ÿ¢û3é>5_LiEÕŒØÙÄ)E™-•hg•?IÑ´O aw;<|”FË|§IÂY[͉º&N·$¼Bþý²¤Èq }a–ÍÒt ”þ1ƒ&Tm×Ô>T‘þ¶Z,šd5á+¹K˼ÐRe+›«{Ÿi*ðDëó:4L`5’¿IíìL‡ö¹ACÀ<Û“5!elçûÛ¸Póä\c{—Ñz×ái8ÁHœÖµÇy¤¯\•t˜FbÉÓrv²F‹‡`’ÕyT?ð6·ŽÃßoŒÎæ((º9‹þLñÂýÎðEՌ̭äÂÚG:pK͇“ôüžõ”'Üãxë…4Ý_2›nßý‡ÙN†0¸r5ž¯*"ÍÉúŒŠÒlê,÷[Åí1íÒêàÔŽß'ú¡eÃL8Ö™›2ÁË’``~<­hºo•Ìa×?!ºÝ…ÃÞ‘ÚBž¾ˆÂºÈTÆf]­øVIJ~ƒûÉÖnb)ÒÈ©LÈÄÊõ4ÀŒ‡Ä¼]sÖóÒo¶ä‡Ê+æâAºÄõcøÙŠºYZý?—ú¢üFè«<Éþ65`t’Fð§Z(&Î&o]^ÀÞvg:w›aòâ)'ôÏ\wòDÙšˆ3øæ4¦+®›öŽÖÓœ‹žô~Y9¼ü·:¬MÈ»OT€52^äÐØÂÿƒ‡^¿,gÏF3’ñ…môÊ8*RŒ¯ž?whÞu“±RiÃ#Íþlà‘íff¼ƒ·æã4kš”*¥ÖÚÌÑsŠ8ÇçíçêÑž»ñóÞÎÿrªQö– 5Qå§+z?£A‡ óêíqÈhÒ'Aéžt~)/Mú¤6™ÉÓ?JŒ_+Á‰yÐ’œÉ\Ÿ£Í<Ûv‰áí·ãöûiÐôKš ÷ñ LXÁrÔ÷RQköXñÜv&Í­»@B™òößà<þö˜ý½t5mßq ιüo&7îÓ¬oKUãõàLáaòg†8ɲ/"/?™aWvìêä#òé<¤3Ä„ J”4$,¸Á~Ìâ%§scÙc>áÌZë<üÞê•7ÿ{ï)/κ;ÿƆ‚Ó­Óè/jH/f沺×÷dBž RÍÔúÏ"X)šDÿô<Ä.›[ìåœçX?{'ª¥ö‰ÒCPÙ>ù.…bjÛ4ܺ·ù¯Ž*¤1QÎÖlÀ¹PTš«‹ªv-·Ã t›åkëÏã¥ËqÐÞ£ !}ÔöƒLÄï?±ÞúØò\„zñ‰ÐÊñP’œ]?¼8`Ö LÝö{ÓCÝÌí ôûÜd¼”ÀŒ]€mqo›Éúü #/ǯ3UÑ0|¬šáœ½ŽnŸ¶QIÕ6Í«“©ú¼M¯Yczˆ4žØ8ƒZ¹cú—ýdß©‡Q/ú{Ϭ l—gr¼e‚¾¾‚eêÓɉæ(æê2Üsw.,ø.mN€ýŠQp}aI~e0S†mé³A!26/é”;LÒãÿêBÃÓØ6êk+¾Ç›rËPõj0ž¸ª@l{ÇâÓ˰ֹU|ÁâíòÓ¨¶Ä„4ù ÃÆU•LzØ4Œ2âç·sUÕA£L&Wd‡ë|>ŒóßùòÀ(,g‹Öã;¿aöä°³t!³ýû'Vãg${¿ËjÉ‘ïåìëOÛÙ÷óÈíj}¦EaçiOz$-Õ!·÷!8¼YG$>±‹¦Àóß–p>©¿Í|Ë-«(Ç).C`¿Ï~϶ķ"¼pÇ#íou¡Ü*Íï‡Ïiãà­ÎGWF¬ƒçÓŽ³ÿjÍéŸe‘(i¢Nïl¼ ¥) ¨uÍ€zt¨š–ñMš¤Ïº 9uHñjrOˆ<«5sEùi.ÕíÚG.?ÊÄ'üü úsmI] =?8œbE윱#dÊ}Úú)Sº´¨ˆÁ{ˆ ¡/ŠéÛ“ºtÓc 9#þ9ôîê~é̆îKsÀ{«ýµF’Ä%ìÆÌ¶vßCF5_Lg³žÓøHù·dKk0Ãs1³t-©‚åL¸wUT6¨RéóŒ¦]Ô,‡¥ŽÓÑÁô¨»<1¹&—i3#´öÿI»6šÓÓ’RôêãïP·,/, ñE‘øóæ1°ºÜÄÌM—'<¶ÄÁÆ“ÉK*g•|oã½ð©Ät¡7µyÛÊ{ÏúO%²Îw!`ƒwz`ĺ‰ UfO‹€âªî±ñg˜vüëÿs“wµ,‡5&GÓz³»¡É1nÊA3+zrúE˜RýžIe/Ôçu “BmË;v*o¢ªgêШg}v(”•¯êB¡È°Ýù›³²Àçìµ×¾{ïµÞ÷>ï³î–äªÕÔ5Êó½‹èËà¦Bd1÷±ÜMæÊÂHäÌ%ßß:҇픆Ä,8tŠM±ê¯(5bðz˜™n qWƒÝ žâ¤À̇Îd‡Œ#~Me~ö ¼~i±‰×_cà•4{Ï‹óö3Ë ‹APÔš‘‘ÙÉÝÿ°öç Ö‘ç'êÏ¡s„1Ü™‚qñXî*B‚DÔ‰‰²Å63€ì—`šVÃe­ÌÿûwucûUºýÙòM'ÉÒœ›~‚èÃáZýÀ7u‰óëL²Êd0å®—RƒOl¶ÃæМŸüKxÈ«$Sø¾)[¥°5]ÐÝû‘¹n1±“<¨„¾cßhèÓ•ëUéªlarf^¦kL¥¿K20Vü˜¶.$?|‰àãÇ׎œFaßj\âó5”Uþ_ZïónG•EAH_4O:ÒÇ ¬©šk"YOûïYv!qx`IÂÅ®al±«]·ž4çÛÑñ\u‘døñs nˆR‰ "$jA8d¼; ×so`‘ÿ)Äõ$¶ö>ÿ-¦åµãx~«<ÍY±ˆ:ÿ¸ÌÜšrˆþY®ËÜ^w2µèúò¢Ò‡„ÿf(7Ü kË­ÉöN-fãã|Üu'dX"dµŸ©º#óêÞ¢Ðì§ pG°è7ƒE»YC£0¯Ÿuêp!rbêбðÆ™€%Rç Ü÷XÛPýe_Ñ#Áš´U„‘€¦ó¬ö:æ„ÿ|\úÙ .ÏÇJiº¼Oï=ýÜØ6Ï{€)¾' ÝÆÛðÅss4nþ ×MWãßçÿôØ\A–ªÜâ'…WKAC–¿Ïˆb¤] ˜ Ý!hn“ïcßý…T"8‘Î6+c¯ž V;ð^’8ñ–(Bmé¨î¯w,$~‘™¸…OðÉ>Qr*usS¾>Îv$.·Äéå¥%àj/Éjlíã¼êgÝ™c8Ó®$S¥°æÂN *Ie¯ã±%N ¼9]pñÕí¬å7ßò¢[ú˜ôç8º± ow¯$•xá@œ Ñ­Ç5íe7¥†ý¼ŠUjéÙNk2z¦õ·0ø·§> Q-ËB˜0S §'3ýy"äÕ¢¹X6¿t§Ò»‹Ò©p„/Ùç@£2Q„ нW1×¾Û¢A%6®M$/‹íÉ–ÃRxït4]zÙ’Èÿ’G¾w®DhÏr”·Ë†koŸÂÝû"¤QÝ YÞK°?ÈM9=h*è±>×V®"ò?JÉÁ­dvåtªçuï¤D`¢¨& ÛýÚæ³ªsWÓ–üÃðó¹&Q?i²ï$š—Ð(ñ‹Ø±! B~b«G,œ¦/æÌ ÚaÑdù±"¶|è>zç¶r¦ÐM¢4É-S&R}Vެ ˜÷óƒç8£Å锓1ì¸ÈY"¦*GÝ÷\¢/©#±QŽÁå3×1.{ÒZ±K#›M>ÇË¿ó‰í U:šºŸ09¥TúBë gTÙ‰£&Âtí†z¨=7› óßbÎ(®ÇïÞeô³œUõ‘€C«È¤_pþ‡29êÓùÂ3`Ôò%nv=‚³R¿bJS>öƒbö~ºÎñ2ÎýpÚ¾^ƒf›f^„)XûOb×å &_„êÌÏûáÃK^HÒÚÈαs ƒÉhù…~¦G¥¿O§~&¼dÖ¾.PKe¯y]f¾+Nb„Ô£(}g„á ¶cjk¸å–õX¸ï,lÒ=ÁIÉë§#dâkžÌm@ÞæAÜe¤Á,©•£ ãÁl€OÞ£ømX—äD£­ý ºp_"nZù¯ÞÉb-Ln£¾×\ðp!Ê…†ãd-¸Ågÿdé¿O²d³Ôäyhá´(¿ÂÖçûHÿWŒÍŽ@«ð1Œ?OxÆ•`iÕ',+CÔH’½óíèA›V¼ø–Cxó·Ã?pd0ÃW<»ÝÛBöÓMKÜP¢e.ÿk€*×ÅH\åà·¼Ëìzt€ÇH]O!Ô]òœ£ùpzUó^Ó™˜j_‚âɳâ ÛüqŒ+õV’Th½gæJJYƒ£(¼—̯ ÀcÒÛéµ®b: 09vÖ¤þwO€Ô}¾ÈøuTâ’2”-—!3ïÿF¾TVÇ<|؉BoeÈçD„¹6´·&,ÿgÅ\9LŒ°yTŽ¥é‚ÇÖ½[´û>`ÿ¨'ÓK_ô“‹…ž†%ddä&U>º¬½gOõ€²I×1Vktig÷6œ*5gjYCé>²DÝ“ü§wN2–¤‹Ÿ“ËãªD|J 8;„“áYø0sË/§’ñPrX7*›ÁõÅäכ͉äëUân¬OÓÉÑ1ArQ¶–ñ™—}G8á§…Áhím,üÝÊv=(bC¿z“*°¢I×…ÈÐæXìøÆ¤¿¬Á·î°;×âa§Æ;gœI[üHoßKž{o'¦is©ÈÇPÖΗ–º8CÅTˆ©ÏÃ3ãdýƒ]?~Ý@G3HßʽwÓ™š¾îE;›`’5›ôý»‡Ñ|Þ$÷\úÆ|›«GWw«£¢ß]· #_eh¤4â©\ È®ñ Q:kA¿d.UyßÇ–]H'39Ìa²• œX™'ôuíR2»L–Ì(1"*ÊÖXð;–Jv˜“ï§J€qmÇÚ@1§ÿžmRfŒ_oÄ=³ž!ÓCèÅe¤R^F,äÈ×KÕ$aÛȉý}ÕÇÞÌÆç/âaÕ 6aÛ òI’{½Ü‹Í–ÐÍ*¤0÷ôõ- mB ÜÀTò|£%9–»žÿ͇[˜Kš8±>éXæ®M˱ŽømTý¹[E)T¯p'3x³ æoìÇÆo*dã1^øjn‘+Uq™v/xÁ$OÿBb[ÞCXK:3G2R;ÎáHç~((™M/ øC·Àã€:èÿ1Eæ²´&åˆ9CZÐlºú÷ûÛ@Žõ.do%âÍÏŒ-᥿ NÂý»æ´]Þ‹;+åùшíXqjn› Ë’Æ Ô~3Éh)g³»èÆN1|z6™7Q lª×ü€ìx&n26tö/c¸1Ý|”ÉTq’ýE›Ü~¡EÓ¾…àͯ¿ ÈY¬Î—³ÿÕxöâŽ1cÙø.Jˆüñއ«×åèÍ –Ìù“ àâ-ÞèœÇ~™"¨¹ÂêåÇ3ÿÍã°Ï0´Ç­$¯· “™ÃÛQm Ί̡[HPçM>$Éêó_ h¯5S®ï´HÃÙCìeuÒ½è».»†ûüö ö‹ç V8j;IùTÖŒ(’‹9tvÝ|òòÖQh8ùµ_ꎛÚЕ÷8ð(©!Ý8ª<7§M'`•¯*ÈýCîßå,š‡ùàQv„UTÕ"Љ‰À'N'š µø¯LѼm Ÿ¿í?~„Ž×¿Æè;ÙRÙó0òȞܟÐ&Ù³9læ/šš²ü;kŸ rÓ8²óðzòߘ>ÕÅ@[@ö‚VNiû{úÇ>FtxH£þQŽï’6v»_,”/rÆœJL™Â\4¥“ (æ|lÏf7Õ‹‘Ï-GØý}1¨Î=‡ü2Ù«AÓI[’>žU ›¼Ò_ÙžÄád=êh²Æ&vOæ‚ù:ÐTt¥5¿ñV®aöŸ„À9ÌáÚ£¹§w°ÒSÀ6tÓ¼¼ˆ;àEöL-„ðkž´E§ «•£ß€=äU ‘¾ØÞ'ï•·aÖÑ|ü›] ^ÎTÚîäL±ÓCÙlBf*ûþ_ÛY1…p´Ä¨Þ‡$4®E`œt© Â9ˆ8t ùÍn°s¤eÈâCæ‡E×2ÎJÒTj‹ùó]’äÝç'7ûÞÀó‚A¬ Š¡¿Aú£ÿ¼Ž½å§Î¦fä¤ä7öçáú/é" ¥Iv¤÷· ýu2–èk2ôʇ†­à׿M׊>Eof²¤7â:P2ä0‚rôÁE€Þœtž‚e+6‘× ê’7̬•=Ç.l2€àC¯HZ¦ïúíèSS¦‘ ±@Ñ ‡­üÂ$6è2s8¹ ÅC3û¯_`¦}W¡Úr¯nânÎPä,&ô¡Ò+¡S™‘Çî&;]9!RávŽ8{σ¡–+ìTÓsìãÕ;àÑö0ê­øDf¾‡ÂÍ Ož“eé>å³ìõ;-ørÛJ,z%@³Â\ ®Qg¼7a_F70ËÿéRUö&ø¾ÂÃSâ)i¸IŽÎßÏX?9Š•¯`é•,Ô¡GE«Ø%I§Ùy¦2¶"ª´öDq±Ð#+êJÁŸ'‰Ø«“ÇYQ°Þî1ì[z³tÚ±ø®Í7Mìû&ÄiÕ)Æ'± åóC 2ŽK~Nmij!åp}ÚZ¨hCãÅ\Á¤F†¾|Dzwáè¾`Âûü]¬* ™72ѵ`2}DÈ@C ÑÙàŹ’Cð·q!øk?„ÁRºã¬éÂ̺>¸Àæáнo™à€dØ¥t€Yþç&ó<,žX½CÂú6@áÜHXü# 4÷ï”'Ø—ÄÀùvuFj¹xÖÐ÷g‘Õ· ‚ï#ßo‹»ƒÃ9U†7Ù‚m—àP©4»"[ÜβQA¬þœ§¨Ê½ŸÁ÷úÝøÐåN30 Õ«8r‡tXÕ¡J°µ›J’wJ¢~8BKaàÑúºŠó %7Ü­¿°}£¯˜š5ŽäÕ¸ ¸+MeƧYãán?` E¹ÞìÇYkTØï]„®»‹°{ˆÂvë ¹+Êüá#$øª ë«%ľº5ƒð¤YH‘$×/ï¤uFæô­ÄNÈë0!½" ¤§¹öã²²×päÊ&ô ¼lþ‚ëB1c¯%¹õ¶‡5ôŸÏêÿÁÜ+§ÑÐê8ÖŸÞÃáoAóäR6Þu5qÈÆàOË@r®©ÿåIiã4ü4ÜÆþhÍļ­Vô¹V'·Àmɪ#¹-Y]c¼×)G•Öõ`¦ç>xh±„c£È»ßtr¼Àø G’3{9,ú ¸K”Xna§ƒ<]Ê{“U±òæ¸@x"Þ¶þ ¬µ¤!õÌÒ³E¥¬­3“6ñÇu› \þkv–ñÙ$KÎFî>ãtH×5'3‚–€½ô[¦Çb€;*ZÉñã¸|ªÃ±Ãëi½³'á Ž¢òIÙ¿óü1«Û <­1ü¾QhU€ νI›ÆìÄŸSt­²ùHµÁ:t’ÛZýåœßê \¶d\àÙÖ"(Œ8Ç<«)ƒjáèwV%ËV;aZÐræ}Œ<­¹ÇØqœãI¸þôsy?'‰A@Ã&ªDqïþ¨Tœ Þ;oÁAÖ˜ßn<Ã\Ág²ÇQ÷k>;.ö‡)ÏP§¾½èWöÈo•¦;Ž*ѽìi{`De’åÇyÉG}¤cEôu û_ê‘Òà"w¢˜ÿJÜçÙϸ Œõ«ƒtot «`žBŸ©› aìl©ƒ²=?÷ºË6¢¥›ÈŸ>Ê )§ð·É~–"wJ%=ç÷ãÖM×pÓ#j}>“¾Ü¥E³çNuª°ZGy¦ìällo$fß#ˆÍ^7Ú!9ˆ; wC=tÐ;wT…ÉÕ®®–Æ,bjKs^®!Ï ÉG€.¶ÑÀ?¦â$£>Žž>8—¦¾ eD¬¢ÿ¢­±Ç7 ¶qoÛWAY¡×®Ô«šKÙËGÈéS­³GÜNSõjø^µ…š½,g¾’pòí¦ ™¸„”s² sãM¨¿y† ®Z ZñéxbÓ"â -®)Òؽ»wmTã_ý,Z*z™ÞbüL¯à…ùÛþ«¿ÌL=ð{±+~v¹‘€•^äáx>•k,¤9u{1á³!jŠ}Ån® ‰»9AŸ(qˆ®a…b»±öXN\ÞNÔ·“6SB\Á²i"´(ȯ·ð“3ò"¸-p75VœÄp}<äµ`-¾Êä¡?û Î^µŒ¸wnFâ•÷ÙÍ™âTû‘ ©ÏÅgÏ6c™Çbztï𘠂‚Ÿ/yí¢‚7τ±2IâØ¿œ<9ÌÍÞÕéXôa©»DΞTN¦AÞSaãa6!Ò6Y˜5.rÛçXqI¹Aq°ý/áovÃ}§ŸpÛo?„EëJñ2O+Fð ‰?öÜî†<Ø#} 2C‹ UžIáV\àLZrÐeíª3ºžt>Æÿ4ÅO¯¼cgXYr&ì*`ç ì÷= ]§¦Cbéj²7Q„d¨;âðãXÔRü §ë—’zÁ,Oò:L»ƒM·M‘{(¯žØN…·‡ÀûÂdÇrIª³n­]nCí³xȉ%‡H€«%y>jˆOõæÂO½åäep%ÓöEr7,†®Ü^Ï­ ô¤ zÀdl'ezØÀÃPz¶6ßcdL¿Áo+R÷¸†]䛆zFm ÙÆOçé%ö‡ p y`¶Í6&Üt±ú³‚®ÙEý´ð´eûÅÝ …+"Àðk1š  ÌÁÃÆ™ö-z3Ny°ÓUÒã¯4Ø ÈK¤Kê9t(޾þ¥G×ÙœAîw=,ìO<†Sè^qQêxûõ^OsN­¦[ßâ‘x2óÃ"·3„ˆh ÁGs1*õ î]ß ŠaæÄsØ´ÿt¯cÍ3Ë} äùèæo5웨à°Ù€ëÉ“Êͨå,G}-Š1òfxå`±‚6N—1änÛ2§ïð£Ó>Êу ï°Ö…Ù ºÿsÎÏ2,am”žI%ûÊ_w¬F¿ï+¹WPSqŒVÉuM:Céò F‘Â=é²C”j,L'KFêÉþþtÞ?S`­z˜Yû.›º È}ØQI¦v´”Ãùïäc»PÓëÐGð;ø,™;ê‚ 'W³†v´y•*ý¸lë»Ï’šC0œ—@'TL9ºQ~Ø\3—¬ùz„¾¸3›ºÿT$%bFdéóznnk ;ðh&¹sô ƬÈñè 6÷M9é.!ÿ†ßa”ósÈ}¿E‚ŸÁèy!ÌÒô¦uf½,p‘‚µQñÌc¼†ìôͽü¥N΃]ÿŒØ+Î9ô›„.]-|cö=‚û»%(Ì '‹/Œãž„/!jŠ =’ÌÞ…3)ôÇâ9/ÀK:† ŸŽ³æv³y ª‰Ó†)¸jÅ^ÚÅGßJG¡—?×f¤žiPÞ:-"rF¿7°£V¸ÍX‘Xÿ¬‚Ð0{¼®xnÌû [Bà‘®"h.³ÿ’Ùxãƒ/º‹Óôâ·ìÕR;úD"“É2r¡ž?ÊY¬¹ U_Ôˆ^ðs˜ž ‹n óðn‡¶J7ÃÁÌû0ßEÜÇÕiÀsúŸ§$HbF @Ë;tŠ>ƒ!ÿfz%º„;lxŒlÆox¿· %ìǪaªÏÚ0Mg$©r©8[¾nK§ÁGóAø1ÅæD]4ŽÏ†)˜mXާ4gà¤DNòÝX|%cSϰҎŸpæè#,xs‹ÿ NVe5ãÿ9øsïsÓedŠ&±‹³Ýµè ³cÖ|úÔv}“IVç,Äxg"ö[ óÞo}07£’ ß1šæü30ìû¸û6ƒÔÍT+¸óÿm§†éèr² B¯'ЊžÜeœR6ÁLâi“?@,R‰˜ÏB Ï,²g߸:k%ÒE:½ŒÍO_HjêÇQµçèÅG‹·”Ú)*þEþ}*Þš¤÷œ(½n9|þAA¹ç¥@ô™KL¾ØJP?5¬­ÚD­êc™\&Ä'œ€Um_1Rw•Þû Äczª/É|¢ˆ¶¡‚¤ý¨í˜Ä9×rÚ@a‘<•Ê#WTÅAé­‰Ó¦'®¸Á•;à]Bë$OYù£±µDÃÿ=d®ò§PÛš•øuF&=3ÑIü÷p é#Cß}ˆcƒúú±$î ×!#…Ñh…Ôut|‡$ñ­¥Ns ÙBÉ“ =˜!kס®¿h“æÔ!ÝžŠ/G˜‡#í¨ï,Dþù¡ã«é¯¶@]Ÿ(üŒ¥w§{‰srøx唟8HÓœ¢G@2±øó6cžM`c|s:ŸÎCÔ^Fè==ÜÊ“€-ëGpx†"õTëñ÷Âäî%#r[™ŸòžÀc¹¸Dõ=®?dF3n£™ý Év32`L—8Ρnuéüš{kYJyÃ×"Ÿ6µ+±†–¬ÃX‹¦×M?®a½>Igªø‘ˆ6[ªèù Î÷:º”Å@Uh<Ù±§î\#.ŽWY ÔžA/uÙS•Õ®D_ ýèþž¼ZLž+!¿ &qè^;X2Ü@ÅiõŽ*Èù²™¦£‚°özˆÓ(³•“Ü4ª:—˜‰³ø,R„ŠXMùc¿ÀÙþôÕûÓàxc ÝöÔTÍùß\(ÔÅoÑ"ô O(¾mP¡ºsrpžÀ¸½ˆ‡<;oŒ. ‘˳`óY–úÿÔ@ᙾ˜–òl,dزä)tE\+x¶ø¢×½"&7\ŽTU9SΣlìpšMÖZlÆ£Ïó‡ÿ ÌŸÌ7žs‹˜„eSAËbºU_1Ú¶¶Ôýb˜‰8a³ùMÐ4Ž`»–¡ÚÔ!phýÀT5…’™=gzqÖ)ΕÍ_Ù˜h)êçÈô, g-Ò˜áÃ]Çáò»BàM± 16c ¶€¾òÓ§’®Iðyº)ÚZÅ‚WŸ^‰ü‰÷?ÎÑS™þ¿v¢Fä&6ÌGƒ¸}›KÌÍóÑíj»i?©z–ãÛúQÿÕwè ý‹ ðóÑTÈn݄͎ڸaI[(­Ô—éÏ&*ß4ˆ·\;¥õ1Ä¥ó ÿEÔîÐÏgUèæ'+ìÁ¡Ãè-/Ltòü˜Nk`»¦Í¤¡žDúÜ7®ñüm aPˆU2Ñ@)ޏ¤žÆû#ŽÈÕl‡#©d•p þ ×#F8%Y”Ì-øÎÑœ­†È=|íµµ.²&ñWgø®ÙÔð…:}²õ:Àòpäu!^>T jw8†o50çÍOð¢BÝ…ð~©0ìß¾‰†<…¼÷HÖ¯†*KL-ÇЗÒ`89¶Ñ™÷l\Ù3ÆZË™Ö+ÿ†c‘‚ Ùð×~r"‚FXºê$–Vó€ìe%&û³?v™Ι¿qóˆ7IV^‚{/4À^ýHxó8ãÝìKÌÇî0ŸÒïp¾}ÀîÎ&ì™ðç>X˜Ïœ[oƒU›UÒUŠ6äþÏ[lÐ ;‰ÇuˆQb·cÞdü^q–„)#NÈ×iOøãUid©5ζòCËÝ[Y¹y[Ó¸h’ÚMÝÇ4iÿJxž”@õf\€os΂ÁólüdyÞuœÂÓñöN)ðiÔ 'Š–"µ)o|òªëg¡Õº³ÀçÈîÎ,‡µ 9êHxgϤ9 a³L<›NLɱ1Kú#ñ=Ž.i€Ãâ ôUÉÍ“ƒ‹çá±¾Ìò‹/ð¢•.v-à`gßWÎ41E’[γibÛ|Œ¬‡[«âAÄâÇ×ÙümްÁº Vu›ïðgu"iâJžDÜÄlÍ®vKŠÿÝ !~/8ggÏd:š;Ù)íÑ7Q€öþ‹"ÝïMÈ¿ù)lߢ7Ø:Cvµy€£¬'>±¹ÃݵlKi¬)êd/ßs!-ŸOŠ&C¼´-ùò½ŠNùàë `öûYœ°Yõ ½U‹¢ivLxŠK—§#¹û_MNþÅеŸïú*æ“i»‚híÛ£0r]gİŸ€ŸçüáÝNeL¿¦¿Mr~\´ÿ ³uS&nZû Œ¨Ù¹!¼[ôC†šaª¥³xãv(*Ó¥O{pËtTUØ‹½š°×ÿ>£©RÀŽçxƒÇû¸ð©^• ôéV°·í¼òC„ïÊN”K»Ûعx Ñsv$Iyì#¾Sp{¼VHÕN*²÷0„VЇóP+ͯ6=@Ã]òäçq¬_ÒÌMðÈîî™<È]ï{ûâuÈ@ˆ=Gý»›Š“Ñ[- ñé³jÃçá~@(;aNUžÇ ãýLn«ˆDîFqôDMÎÙ€[;fâÍIºx½©j9?óz@`BTl‚ៜÞ8± tG?bÊ#2h^…!ÌEæQé"¢åhDÏW.Äc⩘J3Øö)G±VÝ 6j¬Ç²oÒÄ÷ûœ®¶ Çžæ¡ü]iÀ+Ÿ`Þ0óÛùöý,³êáÃå}Âf‡¯!Ã+Û9–,Àœ©V«Â÷¡×LŸ®œä»_#(+rÕošÀö#µþÂŒí9Yè‡4—Ëñ,8 c°Ž(&ñÿÆOµ +Y5<A±®À>‚™Ã¼µ‚œ®Wðä¤>vÒmœ§o ¯í3$jŸ3úp¼ÿ>~±ž‹N=pûNT~LaF,ßÁ¥3HœÊ+üÚñŒNu£û)m²XKÚ.(ß_Ïà×Ú5Œº¼2¶`9;VQ†×ð¾õ.à¹ð¢cŽp- Ó¿E\Œ¿Ë.:܈™KlÙ›O€µ„1nm7çhòÓ ŸXã¥kIA@?V*FÂÚ¬Æã¶=µ~½œþA+F­Õˆ ¿¸ž =æÇü°ä>‡îé4…Eæ(hâcë)†nÎ…Û3¬Ùd ý¼YÆ­úŒÛz¯‚ŒÏ}G++âmWüs´‰æ+œ²i%ºÔÑÛ½¼ä¹ÆkæÝàO4–¹HŠ&„Akµ)ܽ³ZMxñpó\¼Ñ‹ºû÷ÆL"?ܧöúY><Ô¹žÆúíg£Ë!vßQøtÉJ*ýÉŸ5ìøåõ„71ßö2ê•Ø8t—Öë×)"ôè9€°35̽’œRû# gÒ&§$ì¬í]²(°,7Ȭ£o YÓIøô¤ çï‘ÉïÊ+ˆ –¾„QYK:VÑ—š«9ɧ¬ÈDY›ºå0&òŒ ÿ™o0ãÎ^ÖßôF¼â|ž½µoÇ>zîã0[<œD·˜Ÿa ¿c³¸ çx×%)õÞtÎô¬],Aª¿Mž˜›x<*ƒÙú0fôVÁ£¬/¬c·µ²þÇr»W‘iOÂé‚U§˜³BPX6 ƒœºQ<à#ÇýwSO3vf•·9Ðò#sÁÀÃV–ÄÕö °âÝ\¿þ,m~ʱóÞzÖ½ýq53¯f3«|n®HNF—– Œ÷ž<Üø¢  ‰¶‚2xR‘H70O3ÞþE‰¸Òe'¶:­d_Kjÿɳ©Àô;ÊÜ ñãDv2WuÝЀ±«jðs“ vú™ƒ§À|X%³˜TŽÝ…+¡Áø"R™Óâô‰ÿeÇ„ãýl½þöHç-Yí ŠJ«ÐË6ÍŒ±¡‰Oõ úÝ;¥²æÚGác?Üš˜‚gÊôÈ’5­°vb1eºÒ5.FTyî gxk‘ö€Jˆ<à„ªYâ„mZOA‚{B0øÎ4X"²%G˜Ýöòtz³v»±›¼ðY«u2q,ZjDW|u$¦§cò`»t}6N]VŽïm•p°x8‰Žàjv£w­_üä!Ó{ùÀõØùkˆ_*ùHÚ⋬Aí~ i ²è„~ª…+ëÇAaã ¼jòo¦t¿2¾O¦[Çu‘ÿp< S¾›žÅVÄ÷bâˆ!:®=ŽpÚ rÚ/1YkïAÜ•5Ôxójâ M_´î%fK%ÉžœFôiõ$Ò)ñÀËÝgá¶’¨›ªI{i’CVqpÿÎGä/‰ å«¢QlÖVòY•É}—Ð>a òèÄ'ÅÚœþÑ8 KÍ¥å0¸²9×–Bü¥f:ÅÅ “bÈ-‡@v{C/øòz¡Ëq3"è^â‘ðá$œÍ[ zIí¸õ2ÝëbK‡”! ®…>ã[œÈC`;²º=È—)È/¤H2>ïÇ_÷†™k‰þ$({ å”ëÒbcx¬ëü6¢Ík&Ïj~<äN…Ê•"0a‘Ž_¯t°±uaÌ·ùØ híHsŠÆI?8ë8êz4úÀ5&lï#æÚ´ýŒ×“õ(“<×Z ¿ìl¨Uñ'>å‹A0Lo¥Ÿ„¢âføÓ9¶Ï^ /a¤÷›³¹ícð‰ŒÒí`ŸyŽy¸ý ú,„]߯2jUP+ÚŒ¬Õ\ûTqnÀü",MÅÛÈ’û©XÅÓÄW0ðîËBª»m)+añ›¥7XÓ% ð5r1Ϊ3aÕì»à‚Ç”Óù•%ÈM,”Þ5¬©¥ê2 æàwƒ2ö¿Öµ©î•(ÿ£-??†L¤®4ó/ÝÝcRiBýKá??g~5"¶XÓ[€=¾Q åWÅöø‹“Rñ0Œ³j@±5í̾9Y8SŽˆ=5 v™_Ñn˜q O_ÆE‚ŒÐ îjOk˜ßl$éÃ7qpì*ö¬²#çù¼PÁ*«oAÞôËøiº"}ïmLâ”®áÖçÉ>Õ äÓ¹_LŽ1oüÈ1ÿ žH…±g×µcàcW0;ÞöbKÐAÖíDD=á¥GêßÂjV””åm˜Eí„“ÙZuY噌ÕOáú:YúCÀ˜zEÉŽ“}ÁÙˈi›áFîçlÃc‘¹l·Nt§ÄÓ—lì…¿ÞvĆŸÜ?ÉPϨdòç÷ ì~<æŠfö83·Ô_pžö£îæìÞ,¾Ößá·œ‘¿oEdd² aϸ‚î;HßǸìN<ƇlÃS>ñøóµ¸ÒÚ‚nÚü‘™=–4‹¡& Bdåh™ÕrîÙÔ.gµÿ1ßNº²úoaÏÆOÁõ/ÀÃ[’úkŽ2Ó¿³\a/Â5©&§w¿‡tmÒ xFJ˜úk2¥°ŠÝn%Aü¸BdOp n”&¯45ÿ‚¦R˜!© ­È~u²Šã‹-*Öt‹–%9ve'iÿÊCVŒ®¤Žq`ca ÖÈ'/…¦€sÏcëx^NT$×#*A^ßÇd¦ÁÄ`ã‘d{’¼Ù?2úõ漆ˆqwîÀâ¶ñ&$\ »½‰‹ú~Æl´œÄZÇv³• 2Œk²àηë0fiŽýŸÅh_&弞@6ÉpÝ\^=Pþ¨ Žk˜CÓà8{Xð!ž \Å:Ý ÄêE=8£ý'n¬kcÏ*•à‘{Æô‡)Ë&]”A³µ8¾F…OÇ(õPöð¸3…s VHþëAñA7{ß*–îÿ׈º’ ì6g âÕðk_Ñ17i:/\”åÐp ˜§ÊFlõ%2OáÓ}GpëaEÔZ @ÿu$3IOÏâª×¸ê˜%›{Z_wsDlÑ„à¿_álÜŠ Æg«,É:ΞvEá–ê,Lʦœg¦>dêãåļVˆfñÈ“jŸwP0°6­ª€íFfÍÎ 2>² ·û‘{JÍ0…Ç‹Å0¦þºò0ÃJ,"·yp@ˆl3œC¼B…Q_W†÷rwuâÎr·½å#—CaÉsÊY)ÍMé4¢n»¹Å'ð]Z šIö²®û:²@ñþ –{àø "3ó›ØÍå§0je zq‚ðçýãèWpžq½²níÆ%Ï=ðÌœó°ÆÑˆx_íÇ»7RÁTIoéTx¹y), dylHäO ó¶,°Z¦Žeí¨׃_&Dœnåe€F‡Iy~„.T>¬é“Ö»xG¤‚KsecÖ-%òÉ$HÅO=j ×6%ÓãS3ȼ\mò;±w_× e‹¡ T ”–@ƒ] á†k“檣tþ„ô¬-aw¤\`=[ßr§sU‰ŠÜ1„vsâ7O ߣ³><Âéû®±Ñw›áiÒU¶~ƒó:ï1®²9óŵ‰Î8ðÆ€ 4 PþaCà;¬K¾Ù§‚¡°™Øø©¿9E ·‘2cCøüï"‰ 3¢º2¢ô×ÔQæˆÑ ›z³¦üS¡Ë÷Ø¿wð©y7œyhI蓪ѽ`ç{{_þÁt‘Õì¾;nĵn˜ïê·CÉpxíL(\JTß5c ÷.3=¶„S0‘ƒsB·‘WaªËl2µMŸM&»3R™5ñ"°þúfrhÓ4º+Õ^D áèéÍ„´zqË’€¶IThãÞùþݵ/]ÓƒÆ7K¯°™ËaÒw^b§A¨±E1.1c_'ÂÚ8è˜'ßþLbfù 5Rb䃈Íó‰&Ží'ésÛq”pÇß/%†ž!V&À[hJ=‡_ãé-¿ØU.ëÉÛ‹$úD¼~ Ó‚æÉ¼ÈC{ÞÄÄ v”{ñ¼ œJ¿ÚL%Ur±t§û-4>/D–/*×s²ðll îѾÚúÂì-ö¯vg]®œ„Þ[Húòäõgè¯ÓIMÏ+¦èºÍTšE¬§±³ƒwBþ½6TˆþĶ|E›žBæLÙbâzQ—¸' WÈ}FþLN~@Øæ)˜S&û]Å©lßwØyæ%DμÎìscHLt4>µ¦ŸrÕ1‚÷fú¦P/û›ð=Sdc÷ƒä«_Ì+×—ºð™#ú“7.Cpk·e¢þ¢ØJk|º½•ûåSÒðÀŽŠ-¸îlJÕrqÖü¤JLŽŽdK1Q¿1!+:°Q4†Ún¢cë_3Oï6àok^z"$“úŠ¦Ð¶1R‘õœ1ݽYCLmÇŠÕ¦di%8nà¡ó‡í1zÏLôiX N,á¢PZß2!»%ÐàÀnìÿÚ3\‰Œ€TØÅ“Üló˜IKßfcÃvÑŠ­ætÛ«=4ǽŒSõ²\ÒÖq·L˧ÒgÙªÈ'ð鵤¼zÄ©Eèr_ý<{Ö’ˆ| :uèw&­B"¯/†õX ÿNÃeí9$l4„ÈÆ{ÁÖÏÞ 7_`ˆ¿¥6sc¶;³Œ/˜lÜ%È:^£M|4w;½±ëûŠ+ <…*ô{Ÿ½èjA\ Í'Aàå|.Ÿ‹>žB/Ú<Ãúxb×,¬OÓ…v“XT Ö÷çaµÛ`°‘™væª6—Bì†ÓÌX’=|Frø#¼;Ú„ŽgšÁIÍ•Ú#}$’ÎD/BU^8¿î1«'«‡5Ò\͹*h” ¢L žhsd·­†ŸY…8ZsdVMžÍÝØÙA+è–ë~°ç„„ñÞ'_qj¿ÊŸ*‡^¯g0Óÿ,T¤]~‹ ½H¼NÆp&mÁO¹l8F¾.ò‚Ø7ápÛ¼7~ÉLœ‰I¶¶xl“dˆÊÕ¹rè™âƒw‘ùg’‡«*Ê »£ ù‚&ïiÈl‹f‡> bVÈ,ºäÆw<]£GUx úßy˜på2]x¸m5k_ ®Jdw9dŽ ’“fêP8 ãUÌÏ7ª~:Žå_¬‚%Ó¿3ÙË’áîk>ü½±?•lfÙ…üdi>K˜FnöÄÂðC*“¿ÅŸfá£èÓùZ·`btÖæu"ÿÛ¯hÜ(C%ŠÛÁäÏ}F>c¯Ð}Ì_ßÇ16‘`–jYÎBS*}…'´˜gZÏpMÙNË,‘@Õ7™AᱩDyPÜ5¯&ÝJ;D!CǾµŸÆ¦Ç°ƒ»™þ磟vçu*uÏŠ85ÿáÚ, ³ô؃t¶0aveZÑvG%PZmÉŽäûÑôò;EÐd#ø3™„óTÓ·œý½‡.ž7#Îìˆ×$v¿5’Œ…>¼Øññ!Ê[ “¤úmTï®&M‰XG˜ÌdŽï7ChYÕô+¾”×¢­'íÙOg®á©VDg©<þØE5Ä4Z›”ýJ@ëoúhófËÞµÀR¯ Žž ö9µG3'rISÖÞc×Ì bÿöòc,olþ¶ŸˆÝ^Lç󏲧忓=%ÁlüºÏ\™÷åì´??÷¶[Aƒÿî"»0Zøz;Sà²*·Š‘;1u¬ÁŠCìŒínøA7‚üö¤™­!DÛ;ŠMd³’ŒP@ٸц(m[ÌÊr&Ú¯„Hnv. >HGG…—¸ædT¤.Á*ÿÓ˜7‚ ÊËHô­hfg [fг¦áÌ`;nÌ9À¡)åð9L®v%ÓÜ¡fí|¸)ÛÂÊ/@¯q*yxa3ì“Ê õ¢¹àŽKè‹9è“[^ÄÔÆYG—†l;ïcDȬlü½8?6J“ϼÿ ÉûNÝ¡ÍÎýYD¯h¡N™Ùv=–±bf±öNøÍ%7 ãd2:y*ÿ>dîŸ ‚Á>C:8îN?«Ñ3úä÷l>êþà<áî› ç>º1>î&˜gåJ¤ÜÚ˜²›ò«”(†5JúôròO‡§óŒÑ¾BŸœ0 ùêmد†‰õ¶TµF˜j2ä XµÝÅBMe *˜HSÑ´"H\¤§c™©“E™A-Ü7–Ä<¸”È\äcE4Íi4ŒBµÀÿC#mçJu£A8:û#¬üÀ-./†áš°;§“]UkLµ#™Õ{²!jºÙùr'ê|yÈìO†W£QÑwêÏ)Ð''Óqð«/ÊpÅim• éUU"ÓƒÒðQ6­w.ÅÍsIЮ@Oé’Ýw"éÏt¸8 t/žgÿvîEež]?÷loÁ S:$‡Sl4 BIl½¥2ì÷c™Œ×%̹icØ«÷¾›by‘ÉuK''Î’ER½LÆrê£~–Ý$õæªÑY)ߨL57¬<6µFP97»ÌÁEó"Êõ[­}¾ð|ý[vïs"~?šNÌ•%›¯ƒ“±};‡z,–‰•F$^U€¬é• U+èÀæÕÔ+#g¾½Êè ­Þe§Èžs“¸n§‚ó?5¯ãäp¹'<ð dÄGOû8)¦±Ãmùpɦ ^¯—d‡ÍfÁ9 6C²×Ug2Ž›J;³0}H„cÖpˆ9΀ªškxÁ¶´ÖBÚU ÊköÓ¾™Ñ³Ë¸Ú'_ ÏÑ·Ìq³p,‰ã¡‚'ÛY+£ÓŒŽÑ/l®\OyPAP“*G~œ0¡Þ/xI®Gsy™ ¢òNLÕŠ#¤äÝjZÇ–âÂÔDøîÍ|p8Žó"Ï+ÈÑ¥UNÌŠz2ȉÇ[C*d^ÕÆÒïw¢ë÷‘À/6«Ãƒì-Oųÿ¾XA?ç̦Éï« ý/iªMâ^¾ÊRï íÝmn‘‡ ™/ón?7£¼|gYs54í›ÙÂ<8wè$<6qdeХɍ[ ¶¥SL¯‚M¥ÆDf )5„ZÃ…äAÊ5–vµ9Ø®æÇÇØ\'zQë\r=͘oÒ%Í´Hð@7è½ÛÉÖæµ Ÿ™TvÆÂËõ:X9– sÑ'%ÃQ…-¿šÊ2™èðØ€®a÷ùU³üÛ½È\9Cê7­›;±x= 3Ål«ÓZ]• êÁ{àÝö)X¶î;š¸åÓÀ"]Yd‰ Ï9_½Oò]mRW6ŸnÌ9„ L'¹¯/ÓUb{éaãŸäÈŒù´¥þ9Ûµým wc#濾̇dƯ:fdþ^ºš ,î[ çjpfÍ ÒWéÆ7Rô¨MM/ÈÇB³n:x(—HÙÎ¥^;Ø©YÅïµ <‚οX–™ÊO_ÉpèÙ­÷9¾^tÞ$÷¼5–E”Vì§› §Ñ§éÔ`á ï#Iç¨ü± Çó8u‘{awÌïÿ†_ÔòÈI#:ß‹hÞ>‡Ž‹É;ãJ• FL.¨‘»“Ižú9ZxnÕ‹Í$ѹÇÙŠ›Ùî¬ÓÔ¢£EMè$TBNo¥ûÏrñ‡6«Ó%z/g‘-…wðû½‹ÔíK íx|¾ÚWÇjo9ß®ª€‹ Ü}ßÜ4H•Ã_èK×¢åG¢vËrtà=óå‚2É“RB¼Õ‹|}CLVv7Çr¨Œê̱îy‹òÐk3÷S¹Vy(+ ÔŠ%“öË¿1dÏ]E˜(ƒ×«`¥½„o7&I¥ÿØï9xæòZâ¸Lî,ÜwÕ¿½ $ç¦,u Ôƒõ+૸C¾1Æ_÷’ýKȦüãœßoW‘–BexRqêÎ$¦¯¼‘›"F§¾bV^P…ÒŒlìñˆ%R%v˜µd1çá»—×êÔ .¼.8_ïÃÄßSðÅ:'ûS…ž(²`ú>¦;.û»Ba%óL‡—$g~Åm™ð*Jœ¨ðvÀ­ÏØ~™Å|O ¸/Îèl½ý¦¿ü¯ ûx7]Zø £—äâØ±¿¬Á{õY“pï$S“š ÝÙ†ŸûØs×ÈÍl]˜:™.Ûc*™p-iG×[fLúê™ì¾ÑäL¼/ÂÁ¸í›2©ã!VûMÉ×´ï̃NÜí£Ä|jQƒ&^)ÈH3Âb²–•÷ú‡2ß qýÎìÂ9çQr(dv­%î_}ɩق §?™w1¡bW™¼íƒ ¼@žèè]Â5 /q¯F&9ã¤@xïÛ£íÖðkÉ;68¶è[¬¼÷ì¥4éCñ™àù­‡U§iæ²iLƒšÄ”³<Úù0Ôqôj Ñ9<ôeplÿ°ªÆQ]Ïêëo§}ÀÙB²¬t¾#+üV–®Yó—s”§ Z»Ž\e› D™Ó$ ×)£Ã¶¨ÿ$\ÆCÒº¤t»µÝÿÛ'ùÂ_q>˜B¯ fE»ß@ÿª4ð*š‚z2|d€Þãð3!ôžÚq0ñíK[@.×¢N’Žª³Ôà¯>}ØgJì'‰ü—!©Ÿ—|ÐÀÊS­èñ ýÝybý§RÇELîœwlÅýBâa<rizäýÅzð©ê…ëúã8:­Š~ú:‹öI?EšìéMrÓ3ðß$6º O7šg‘̳83Ï”Öv>ç0!Ñêî:bgÓ CsÇ¢†„=9Ñ/!ÛÄ©¼»iÝÏ.qÊÃ¥;@ï} (GFP«®NQË' âÒô°£0M:󉻥šŸèœ…Õ* ¤vs úkÅÓCž•äÔà~8ðéußJjjù/~FXzØJ—°œðGÒ´‡÷Yü^ÿkÓ9g˜ó—…ÈÀï½àóQ‘¤NË{«!VªûŠù´S÷¥ö„7úpcІ1Ís¨¥Ç·x&w­b+®ß˜@ë~•³!C£ÌÎ:;ÂFÓçO?ãý-å8íævžãe,‚CÁÂÿ=ƒS‹£ˆ«û6È?æ@X«¼ L¿ú.&o~lP$7.¥¯ë3ÈÊ GIƒÎQêóü k>ªCnÜ¿ÃæÛÕ£¬=ªƒ%Ýô¶™Ø>ë ~‚¤…z³ú 'õ˜Õè™ÉŠé½dÕv€ãÝ!6 Þ‹(~<…FèòI‹j\Íg :§Pеð8yß„5î³YÕÕÿi“óÐ1G‘œÈXHyUÍȉ·ü4sã1øvçÖ Ìp?CvÛè½0ýº&ùè$B$Èëä!,s¢vgKai®Í—!þ¡$\&"<ž3OÅ£Od.$F«C<˜€º¦1$–ÉÒ—òËhèí#8ð÷($~Ú±Ô0*¾^ãü§‹FÙ›3Ù®=*)öƒyìt¸ñðÚKÌ»àÇŒR›‰ü€xYK„܈âBÓC}j.°rW35ÅÁ{áAP,;Îf*“ÝÆÝh£tÖü,‡–¹yÜE ±ÄúJ -UO„;çÐ& :Òä¨ó幎ç~æB†äz¦åÑvz0Ö”ç2‡S¶¡I€Y¦M¹Ë7Ð¥&äR]YzW‡øÀÜŸËÈßÅ^ìÜ”+È?¤Lç›Û Ø\5"rCŠ|•£ñ|ÔFíÚÇx8fO]ü~Eî”ÃÕḛ½¹¾.<§º`ܘ<†¶Ç±{èäc› 0„©ÄÉÆ€X½3¢š¯µØ®keõ•à8&Bf§ÆAÝ”THV»Œ1óˆšù ®²”5H‹‹`í±˜õu¶Þlcÿêxz\òË´Y™ÏbLQ*Uœ†Ÿû,IÏm%"{ø4>Xž3&4Yñ|1ʶîgW…®­iøÒ­‡iÑñDQœMîy>vœy2ZòßAÁ‡W°¤Ï‡)˜VŒÆSV ßÝÆ7ÃÞÜ£Ò‹IPî¶|C%VÊþÁñÞK`|Õ‰½Pš§âaaãGœ)̽¸x!ZN{ß/xs´öÊ‘³©·X¡¿}ø ]”á·†äØÓ# ²Yœ<Ÿ/Åõö• ÂðHÕ„ß "7¬Ö1Êí¡¿*@tès&)DðE¨ßΜOJ€¾ã vI3Ö[® f+²!Hñ*lø˜KÍ£ŒAzª8:S—“N±Ég3K˜´½Qm~úÍýÁtè…E×`{°(³x{+š„ :޹_Ãgï¶0'ÍÄõ )µ×ícß~ÎÚÌ:ƒî×*Áwè,(ÿ5¡õ…1&/°ÑÞÝ›Jrbw5¢@åE3q‡Xð Ò—# p\æ&TÙÀRß6˜^³•Û7® Ú+€±Ö«&î~(±RšÛÞæž-øó˜*}—#NÛdõ +\YcÀú‡ÿÁ¡Âd_NÕWƒS¹jt¼=l.‘¦/óiЛšº'ÁƒÇM®VÌÁ2|ò7ªgy=Gòùmª[7ÂÅïÉ4ä–"9XG¹¬˜ýªnIÌ÷`²äfð}q=H»WO#µ…ý¸gµ2ižNû é…‡í¸Þ©–…Æc‘O%£ÌÏô\™Bfß8ÎλŸz6]À¡m‚4[o5´ï97›Îã›:+ºv’ _X|‘ùRý•8„«¨CŽGá®’—kÒ†w–é’y §Ø‚¤¹gÓâÀIÿVœgÍe„(Wþ Ü­K´ë;XõU ©Yª=˜ÍjϠڗȉVX²ö(›òáû²Ã†ià…‹V‰Ó Æô…ËEp ËeöZb™° kkm€=6«a?1Áñ¾ÍÜý*è.¼È5ð…΢ðñ™Õqì‡r…sœm‹þ±&:çÐtÝ&±F»®…¨z9kåIŽG-¦s§²OW†KúdÔ©Ÿœði¡V÷ùÎ]®º~{òâÈ:”9Þ€Eý?ñrótºê¾>Ìk„C×60G·C‘ô|z*î"ø;1õ+¾ [¢MÑ× Û¨J§G7°‹,w²ë¬ƒë;¦» SÑ’ý{Gãá(G¥©¬DF$x]ááÆ8œgæì»ÊZêœv,ü,Kóßœc©yôÆ iÆrN·|µUÖâ¥?.ªóoû«XÂl8b&掫À„ED¢lF³éöMÖÀ½ mÿê°ç´$IpÝ_®]æyNf§>Z­; ¡‹|ÁA|>¾ŸÁô] ¢Vé1à7¿‹éß¼ûLwNbGeô—­ÃG¼@ö¾;ð¼ÝƪˆÉQwÁ¼T0ÄUhÏŠuØ>å:\Ÿ€j¥5¸]î¦îÚ2V™ìÍφpps:%_f O¾„®Ã¼LÉv-š®ÅÖ?fÖ,Œ7ͶLæ ^ZܧAFÏâ’=õHï^ÁãJÞPxN•Y}‹?³œ ¹â æÙi‘÷?=‰èqܽv1å.Á¯|?ÿ¦0tÀ‹˜“zŸ)·W§VOeªpä›ÛTÀ4Ý5#:Î`é3ÚË&9î#Ç…xìÐ7eŒûáN1;¥í/§åãÂDÆî‚-mH9ˆú\)òáC 3þ`=L×£»kñÀö<èÚFùÆûq‡å FÄn7ÖÌ®ÅuËsËV>† MPR»ƒhœoƒ?w‘ÿ‡8Ê_9¯k8+¿„‘LåžË½ºåÕ0úXfddÁóU³kóÞr‹&yE²Ð'¬\;¥–‘â²çL§WûŒ‘¡íô!4l¯€0 9Òº¡Dÿ­ G]É΂$ºý6b ÅßåˆRì;kölÚ´3>®ãÅñ·ëÉ«—bT©ø2X[j_£$#X:™®°ÔXˆJê¼×«Ð³º ßâåRiœ8 ÒUKðîµj8qb¼–È"€v_0.³®bƒ´Éâüy>‹ÙæÁû§‰Ï]xóÑ º”܈zÐ'¼U0•œðˇíY‡Ø§JõÌùaS"sΜ^o‚äê7,JŸ/+q‰³+DÍJÂ5g9]À†0'vãåz4‹’Á§‘?¹§¯” NÓOÀ©÷`ÂÍŠC3a§í&:]±‰½æ‡/Œ³i™úQü9³ ÷§,ÇS)Žôš…YÉv$^(?ž/dÆŠñÑ:ú´Í,þu`·`-öé 8']lLïæÍÓ“p×6–-ûÉ.•¡šáÓÙÄ~<‘ƈ¨dÕrMñ*#fy…ÝæñyÀÕ$.2!Ä—3‡x ®À°?·®ûܰ%ïÏ™Òòš˜ò®ÊŸ6ÀÓ®øÀáã®/lìða ‹z°r^-ìœv ˜äÉæ‡2Ø”ןJ6Z»IÒüKȯ´_ãg†Ïu-´»ƒÈü[hûZƒÈ$ôà±Öç(rq,g´yYÃÇ“…¸äf|Ó¦wÝ‚ª·ÇðÁÐ5ÖíÙ ° x uŸ˜³üSéuËxæøLvÝ 6R$ Øp9úqÌœøeîbÔ~taºêRÆÀN—&…O®ý¯eôÊÚظ±Îñš¸+ð£‚ÙM̨_ètjðFŸÇ'ñ¼_£v’m,{¼^Jî•}Ÿ=±£ת¹`ã )µŸÿ€=½ÄÅÒ*@b’î‰Kž\—£pòö8úÇcEm=FvëkÐ'¾xÃ5–„÷Πµn«ÈôóQàõ5:ÓëAÃ5ŽV»^a¶5­D®ÛvÒ4¢C9«Lé³ÎkÌÒæ@x排b.äôJ^’°–&þø3Cš‹˜)[Ê@VT‘N;óƒnÃôí‡!6ÿcè²sâÃP>y“cy†™ù5öÿÆ»ºóIƒþ&ì]wóÕ1î®,sÝP•ýye6MÇÁ§'`ÎÉdΛl„G=þ~}Ÿv1€¿uHÔózhš¿—•Yq²Ã×A‘ƒa’ìéª0|(àÒ8“÷,vªŠ‘ÍóláP¿ä6®Ã”ÖA¦:^Ý|DéáB´.`Oçä³ã³Ná +W¶®Ô„î¼gÇL=Ń…|ÇÉã¤cø,·•åœIÂE⩬”`7ämV‹9Áܾd|Rv z»ñÜa'ìÖ¦Ü}›0±k6û‰èbÜÍ,p^¶™šù °j÷*ÙŽvô§È}6ùÒôT_y=ëpÓ•¿“×Y°Ëî5{ôa2þ~Ù ubàbèÈ ŸÂqàmtÏ)Í$ÂyqMÊ ¸-  Û€ÕoEú{‡&9_èñŸÖ˜uüÑ ý‘çÀHó ´×`#97PüÄ¶ÇØ‚b¢2wëŒZäL¿YèƒÕë•tîAKt.&Ç<‡ŒuÚÔL¢šñž¿V»e’U‹ÒÀ¿û*ìô‚?Æ< ü’ w.†sIó½Šõ婆ìcID¥â4Ú~pØ 1\ûC83¶/<ì»i‘w©FOøLî1Z•¹š¼S;Mº Á4IàéF"ú«~*ÑÎÌ!·V}„ŒÒÎß2]ºs¡ Y·#à?ý1ÝÐËi6ˆ¤«'î3ÕGmÐÒ=äBùÈ‚+rhz 7éD&©$C_3ð¨`óþï¨TÓ†dUè+Muv/§SáiázÊc&DÔâ@âºîM‡=gÏ¢ŠÌ{¦¼´–:ñ“SSé–Ü,:çå]4Þt‡´?§ÅÞêD0~JÞ¦9¶¡ä¼kîf:ŸœØ³Û˜å»å¨ü²“DÔü û,ÖŒFdgœƒ–eÜp‹Föbúº~aÓ?ï»`{¼_q¤ë-ØÖ—x8² 3UšÑ@q)ŒÉRЇŒ:ïlæéåø¸Z„~6µÙô‡èé7‹ÔÝûE¥ÆðÉ23?µ²›mœÉƒAuâ]í„¶…CøÜD„æF´2ƒ±Õ÷çà’«p÷Âuèp§ƒõÝ’H)`[ånb’k&³’»’ûس$ ®±Û/«á–Ê7ìŽ_ÿØ ìÐÛ©@k$gÅfA…]!=®;~‡¶¸ ’Ÿ¦Db_>²Ëmͨ‡  Þ\¡Éµ û„ ”sJ»™á4±ÕS¸Âæ5èl~š¿éÑ0eÒ˜`LÙ]µp5ÿhV~a4Öl…Ô•‰p½X‹¸ühAIû;˜çKΖðÐ5¥g`Tᆲ> [Fó.ÀÙ±nøæ¿‚| ˜Í\tqcWF²ø”÷8爋; ê)ƒõ¾ªôE…>ðÊ–ãmLAM:Ñ\ùt’kõ°Î…“5»‡°õÛÜuÉŸFiù®`/ƃiAñt_-+‚ëIç¸c[pdåAº9Ë›ÔüT£R3‹!æ³8Ú<ë ªŠpT‚‡LÑóf9¸I„qzé*Ç3^eSË)ulãD wO@ÚYlË,¦QÃyþhñn »ÊèþØ¥r–˜µõ†ôqb6)ÓС 4•´ bE*ÄÔæ2›ä;ÂV¯Þ _ŽEÇ_m› )B͸Va%]Sò¢ñÝc8Ë|' T»ÐÆ™¬µ‘ ‘ œÃVEVaíýÇÜ¿clÑŒ{¬Ýðnøýô<Ì6J¢ñ{œ°ÒO†˜­çþݳã6•ÑÌbCòO8ËÔVÁycwè‹Ò#ç*_BÚöpê*7nD4§Ú¢‰§*kh5_l~‹#xk2ãð{]u~l‚vtâÛ¾ÉÜ·ê ÈŸ‡?¯9¤*T‚©O»7Œ´I¹Ãrœ5yœZeèÅ»ôu­>=™œWØ’ufdêÄÐ=§BÁºþ k+‘NÚÕãØ•|Vd§ ©wNÙqªE’Ë2øcK*ÜF¹[Ž^ŵêô_4l0s£Å-[ÀÈâ *­‰‘@=µÁ‹JÖ¡»6ÿè¢Gˆ2iOžª/H ÇŸ^m~ïå·0]¼ Iò‡ÙŒåE²šw)펒!õv%x¦¯þ> qWðˆð-¦å ˇѱöì›ÂvH÷™Ìþ"¤æP"ŽÄ”ïaR› ˆû¯–ÓÒ%¨|ªRµS¨È4Z•+Ï£^aÄ«&¸<÷.´O–¬ýQÎÆúGMGŸ¢ˆ€0îadÙo<Äõ¢w¸­ž9êä fMgÏ+ö ¸{ LŸç ¿Ï>Í;Ñ»;‡ÛlQ)þ&,½t€Yó"ªë[À¨ì>¦,|€BÆdõË(ôµ®€ooÏ3ß×Í »Ý4èmQúŸÎ¸m1?]÷Dbv2w ‡@@æ .•‹¤ø„!Šû’QŸgãµ*ÀfÑ62Ó•Ÿx\á'®èK×¥Âg9yrêšéÙfÏD&ìÝ]_—æoœŸ:@Ì\-¶?÷ÒXrP% gNæ!íß…$,a.}¤ï×'·qr!9«ñ›l1ÄîêeG £áWãÝÙI·Ä¨YëQ¶ÿFì4€ôeùdß |ivŽó•zðÀ6/òs žßþNáÀš¥ôÙÀªioMíÔä˜í;ÕÙïA¶{…ÅlOXœ• ÓïâÞ'LüöZf¢I™.ºäOϼDñÁT6ìœ5mìô£ªAÏÀ³ Ëâ£åðéó:¥é ü³`¶+4rßT3UdË šXkF/I…Â`ªu8fMú7èÑY›§Óï“éÎ|Iò9ó TYCß.˜äŠulP…(¯Ìܶ äì¾L˜ó¹÷ðgîZ°g ÝjKRåV‚áï­Ä"ô>PZDyxxö'i’æ/¼.sž=±EJÊŽÂí‰Cô~M2–'73ò¢-xíì?¦E¿uÅ¥ë”àýeBö6òqŸä†€ë‡ëP­h"îƒàüw³” °Ë*S`øñÖqq²³Ô,ó%ˆ±ãô:õ8ý¬Ü2 8õ‹½xYš.óG¢IeKZŽ!øfw ž_z Û¨9Œ,œB5»¡p`±mv ¥‘C0–dWws¢Õè¯òg(#+G¶ô†³G.ÝÃPáX®È¿8ßdíRŽÁBëà}W+£»aŸá7Xxè0Üþ$@uP¬©iÀ³b»éj­˜š2Ä69}fgþ‰Sfl¹Mß: ôülˆÛåŽà˧Åðãöå¢ÒZk0ÀèA‡xfqUçÕ|tŽ´$¢÷2p¶l7|ýZ Š!†0}gû;ì<(Ķk!t» ç• ^–"ÙÚœ‡·°0á!NÕÓ°wé’Ïõ¥—³G?Ý`|t`€î*ÂNl<áý0ÞÚJådC5W²ZÅsiƒÚnæÞÉ$¨ü3“î}%ˆ•‹µ¡°N Û4.À8_"™wXdgd€(ó—Ág{™`±ÃìÀ¾0*](A#ùN°seã°'`çM!Ô%[ô³±0£… +AN}:нW 'ÚU‘ñ®áιUÎŒ÷íÀúéQÉ':Þ< þá‘D­ÿ1>è¸Ã®‹"ÉG« »U$¨/ƒˆ° ´ù_&FµÜúCE6èx1¼òH‡×ËW㶸:Й¡Àâ Dæû;°Ý@œçvpózWP¥xXeT…•Îp “ÿ¡õâWXjJq XBJ™­ý`Õä>ý9ë®vµ"]UõXÆý„Ùã£K³Y¾[¹ð)p9 {\‡U.´a¾ÓcŒ™eúðêä†\Ök¼¿aþ¹ÐH_ œ²—E±o#høê6lÑ>éRw˜¼g8ù7' sÙO²Ç_ kò…èáênæÚ>3òr¬â×}†¸¤ÿÖ¦RRƒZj·CÇ3æ­‘5¬z¡@EÎJ)÷|Ô­aèå•ÞŒF&/}4G™JÁ4ëtÞ2Ely(L=æð`èÚF˜*4›™¸_ð­Æ}_à/ñ„‹xž-ÊD­–iè?<ˆÿ<3ásoÞΡ¯×Ä-ËïAFÄUœ8¸#xµ¡çÑõÍ!êÍVÌß@7Üä£7bY%>-쬡l®ù:Ìf‡pT³ÁõÖò$÷…ña…ÀàÞ^Xû§“á{Ôˆ]…óè~\£½üšã˜§µ2t´Â–¾]†+%N¡†{½-ྼC ŒæÓ!ëèè÷’Sw¢˜•ÍôÅ'Zž¨Ñ&±~X~¥Œ{}e.¦»«àë«,W » R!ÏÊ•¡ñÏpå 1z‹ñ¥Ç½1ÊÕ}»gQÞ—' kƒ- ßÁOn2¤#‹é¥£—ñþòR暣4¯²¤WOÊRõ—JØxÍ…3ô¨–­Ϥ£°ìŒûÚ–YÜÊìákƒŠr~R²xl)N:cµðÌPƇéÅXkBkÃuPP̼Ÿ¹’4îù®Kå‰V¼#Â7_¢BñlË ÑšA¤‚_a:ÿ9ØÇ{›ÚHZO>½ÏÄî¡Á£ELÊüâTûø¹y/gÖ—øhé0G8JnªlÆÅ ÉÈÁYpUjëÚ±öz7;ç§/5ì{ ZÒ½påÁFðóÛŠ"o³ˆÌÄ9\ZÈO {’Sæ¿Ùuâëi¬êZÚáàK–™÷á­¼Dš;ZÒ˦ê1›V´µÂ—Úd’­¿‰ú ~©‘%©¶dXï/rš‰ùÅòÄF•T¦T@Öý|\SËTº°Þp ™vzò†B•G ÚfÄÞIûÊtðæÒg~2u%®¶>ŸÙuç=†Y‰±^ÇûÐæuµt(e•ÿÓ.Ó¦f|£ñ œ®]Ås]ìÖ.gü¢AŸ]'Pöë%ø½É$Bbÿ ]¸ ¨“q1û‡ûëÕHgv6kµÍƒW þyRt°þ˜XŒŸ’EX‹X{ªÅ+K¦u €êö<2òL ß$Õ±ßÞ°‹A½¢ãêë  ¸uœBÕs×°.ªü"þ ð3}²¸Q“ˆ-ÿ EùAϦ¾zâî™`;ú9ºæY-?ƒäv§Û‹ @çÖ»¦À÷חȉá3`ÑŸ×Ó¨ò›n`7²ærëñè³!P:˜ÿi‡üï¡EÏ?H”ä£ m›¡d›Ó=»÷XØc«i¼øÎƒzÿýY¬Fû=ÆažîgMŽ•Â“U§ÁùD*–9t¿ù;t\òc+õ4¥™?ï"ÉÝ‘ÝltJµ‹ÜFK76±ñq¿a[…2uýr¸±Ç¹Ó?Ï"kDÝQýw#Þ=»=É?ilMêæì‹ SOÕ3J[^¢[¾Úõ¦è1’§Ýà˜¡'¹uW JĘ™Ië°Øò Fï…uÕ2œÆ_Iln∼p³¡¦£€™¾ó »çe &ÊlDÑWºÖ„^â\/õEçE4á²$þþ¾ K9A©ŸYWÆ¡»3£D]#Ô=¼HÎ>› —RÞ¡Ij4ŽÖ`×Ú-x´ä{¹[ þ¶ICЛbX§@¶‰Ò“›cIÊY.ص¼`­\ª @W„.bvPïÈ&l3¯I>=~’}}û ;5¸ ø¯åÀôütñoKøûâ8÷ní},vþÄ_ÓDÁBw¨Ù´ûÿºjë3·±z—ùT×D5~2Í£M^–-¤æŸGA$®¤Úã3G¡ÌMŠŠ/€uÒÏðol¾t¥ØŸz6 O£cWšÊx^rÆË/³eµ¹ŒbÖZøO÷/Ó‚ÊžÜÕ%ÿýLkÂTPbmÇÁ»rï}yóP{«9î‹©eô/BEzêÒ,Ùôƒof™%^мlwò~`¦© ’—_Dhëy]hº! s?±[Öœƒ¾iТw„aÅáß%|À„@¤!‘âì§~î@©ÕÐZEªuZ@½úÈŒèPæÇNœŸ²“u½´Ï,)å~d|0.P›ýc´–_úÈZî>†Ö…°<\‹»Àgüs¦¡Qé?ìÈ8ÇItžÇÙü®ÅSKØ.ót칿‰:3\èžBn¿ÌÚû R^å’Ò|CrÙÙ…låÛ'—uƒ!§JÒ^}ôÖ]@ïŸ_1ìé"â›JšÄ[aKŽ-*Ÿ®Ã%uW(Œ¾`?¯ªÀã%zäîÏolìªw\ãû²D/kIà #òƒc¸Ù0•pÚ¤©Hö1´ºum¯Îgžwô»}€ð/–†ÌÐ Žú5’¹yÍ„ö¡\rÕû%‚§J<À¹Ç Üœ®á‡á o3ÎQ†´½‰É°SM^4žbÒ³R˜èßðI“šik’qÞ êÀ›ÉÇàôs3â­iNŽ-Ô!k;nÒySíI¶Âwx.“i˜D*³I[,Ãáù ©žB¬Ý4…Š|<ŒA/vBÖ§käR†;õÂ>¸Hx2jð¾Å,8n²ØÍÔ"¿œ)é’'÷ºËHÒµ³øP-U¤ÝqÓ‚%TóºÙ‘yô|EÙÇ6ã“NüÎõÃ9'Oâ̇he\ Iï¢ôÇ Ù£y wH½B/#CúgC6^Yµ¢¶$dËtjÿù+{L°¸«ßC‡ÝS©[Ö8.ÙaCl ([˜ cOK ð?ŸÂäIÓSRÑYž¸ñ„F“ÉãR²«1q‹E«PrYÕŠæýæ+Þ á=îŒÕ›0 Ö+Ni>éj•£Ù| `•¡<}Ü6ÊÞÚsoÎ.†%<‘$|†2fËC ]"˜™òãÐZaHÌžå1ÏŽŸa–ýZϘå?‚g‰Þ“cB»=Uì&èUµzHiŸKÄÁ9qôÙ|BFì"‹ 6€×ïtöbÒ56×z.6¥‘?T°ÿŸáõ yÆíf29þÅŠ¼Ëþ Ó ªEõ£×ÝÏ0ÿÁ&ÊTW⑃ç¡ì+Lúƒ9óÓ%…'°8Ù„ü8ÛŠU§gÃôW£8qê'HÞü¢ÂL9˜ži‡û“ƒq­0ûáú8—Èí¿AÁç;èò"êí¤~ÝgT{(¯o¬¤ËsÈØñ´·ZB«ó¨ÂɆLJK“³=É•Tø)A½§9åYRñoP¹A™ª…Åœ>$HïþÓ%_§Þâôšg¹#Çì 8™¿“½èÞŠ$éÿfC%Û‰Y¶YïHb-X¸u@ûpãxóý'˜~³¡¿Ö8Ðe™ŒMÚfg’·~íÂ`Ö9> ܬ¡lÎüÿ´ÃŒZà fCš_3먧ibçp¬éZp¿à¥p-HýeÇ]—X‰ì²:”Ê€ƆdKr,{ãÐAÈ[´–xÀ'hÓR'‡öó°¹É={Ú”p~^a§Ü±Qc²k}9Ãã–ÁÊöØ“ÖK#°ÕéUJ)Çs <‰ÓL•&'ïöþ€sÏ5Èît³˜JÞw ÊÙ øûÔTÒ?+›(ÎnÁg×g‘9®Xã+Zl~ò=ö.¯l+4@©>xjž6³ùYÿÇWpþ†VöÂðM|ˆùJ°Å™à‰,)²ÂTƒ~½ûÄ}“¡Ãk?îž~Œã»Ÿ—q˜_ŠIù¿pgÞO,^º–ytï'h^rŒŒœEoÍ5£×qU› éíëgíÏñ8œ ÆÆA"·Þ쯂°§Þ´g¯Ý®úŒ¾0¥E\x˦Ãæ=tåoIúÅ÷ÊØ[áë#âݯI2Óð[BæÍK¡ïµè'f1á?+Dêÿ¬£‹¥&ÈŽý¯¶tÕn]ò:’Àë`m6Já"ízO¶¸W‚i©9W²€:›Q¡Œ·Lå –9ôIŠXM_Ã4΀z“o@e®e²{T@6ªñ·jH¾pG#²ºùǙӊ×QWG”Ù¾®bŒpÇÁ^ìȽŒ/…≯طÛ·ðáåPc|}ÈÓ—NǦ?Ø9#áTãb,¼ÔW¾†¹3f0{ó%X‹;^(:¯/ZD6_r`ÿĈÑc%Õœõe2¤ÏAL÷MÀû3(ð†.ž_ïß-¶*Ÿhén%6¿ŽP5óL]:ßÍgb-°ç™Q>m?‚J§4Ù÷v34Ù=a®Q9nIȼ¬´Ç²ÂØ{ÑÛá?]uûo>ïAÿ;–tŠƒ%=5öeôd€>íg5:¯ÒÊ…IðExÏäUˆ#ÎíÄË®"à}ó«vkO¶_ÆÓœ¨qÁ_h=ñ&~«àr‘Øÿ´ÅÄ4¶®]ɇûî}0{[$C‚ðÒ@&„˜‡ÒÁ?t²Ð%e¢Ad÷K]&(í"¬îý‡)>Ça¶Š á²WØ7S,@ƒ[ÐdS§=8™‹õÖšiAÌŽdÑߟžÁë­Yø0@“¸RQòiÿú¢á3ÓÚŒsdÈú1a¬©ÏCÛb$•ã3û)]TÙd…[Xÿð] fûlšäšÃ†×•Wó-xèŸY·,–˜ÊÍg’­nðPœ/7‰v=ÃB I¶šÊ—³ßC®aåS8úÎ 3h)ÜûB+i¬ÒÌs'KŽhÒ;=1ÔõÉ.Úvž:’³=Ê4¯I/û1[s¯‹Îž€/)*`üê ¬+—£6—BéJQI²¹`.ÊxÄø?Ö¥¼f’t^4?d˜œ¡3þF‘³Al¤zmX(DÏœØNÚ=báá¢SÐ}í{\µÚÄwqÄV“çžt§ê³qŠ)mžö‘,œ¹Žò˜h’–èPÚ@mÙ„­ÐtY„~„­òüÄC䜀%„~¤Qkv³ž[–³ ÛöS³Kîœú»™ýÜ™t¯L <Øû×ëÐç_ž0¦ð ÂûÎûs­•\‰¹úbÿî3zÕœ¢O^-¦O®$OmiÂ\%š’‘Ä¿|·qõŠ5øª|+Þ1’¢Ç ‘é4›Jß~º:©5X°¢ ž¤3Îàç!€!1¬A®'9k³ Jäg£[Å«²‰çÒlJU3ùÕỊ̂Ѷ ºX’]‘ú·?|Î4- !_ ¼hWÝC&|&…Ÿ'¹ãS%zASlvÒݳ¹3Ž¥6‹Í»Â÷Y3Ø-^Va æÓ 6»´ÔŽz`ôÇìŸ08ü˜úõ„îwšQ°ö%3V ‘Q7ÜŠ2t—æSV¨ìt$lbþ|œM¤2D(¾p¢…¾àø°¯|æ!¥Žj´Â>“JË+Û+Ç2È¿’ YÇ:™Ž&-bš0ŽSBk˜wñè{L€4Í ‚Eºó˜i#Qïâè’.ŒÏË®êÑ$«TÜ!0Õ–vèÒÀÔÉuhàv¦®¦¹Æöäƒo°n\Ÿj× !¾™œoÅÉw™uWZQÛî0;sÃ8¸7—Y¶~!Ósñ Š Ä‚ûÇó ü8 ’Íß‘_òŠÌåg µ=œ¼öe·Þ¢¦†—ÐÌÃ…Š+J¢ð+E,1,u76üˆíãa4”í]7™Fþ2êC/¸]ƺµzÔ]qCÂÐ~È“Š—í*¤Ës1u¶Ó >3H}-A·^˜Ÿnä|Š ’ô‡…ü!.N—ÛGO$?À›kNÁÃ>j¹cIhT‡vTéEU‚·Ã$h…ÞA:9”XÄUëËéïóßq=d[!áÍ|8ºì¶²¯ 4b÷?ͳBY;®ÌG%œ«˜f \/gOf…’aÛÐgùòA”‡*~ËAÉâZx¢¥M¢i/Ëéêþ2Yø»ñ1'åÃmxUµ˜}'AHˆ¬ýñu%÷º¤)‡ŽyòÐÄ3Hžl-Wþ· o„¿xŒ ‰—ŒÏÒZ!zÝR–ŠA¶2íŽ@Z.È‘Ln¢¥>7óùivƒŠ8QPÿˆ; ‰ŽŒ¸9ÆäÉZ`o} V/ãè\~úòÖC¿^WÊ Y¹p5~ÍH:iC±D6=Ûw”Ø/ÞK]ÙÅDà :åÿ‰?…ÉÁ¯ñKêrHúnÇ] 3kïí¡¶‡“8Kj¶÷!Lk»„oå–A#p0Þ_”Ηlf<ø¹pÂæ-æüÜ@Ë”øÈŽg³Ñ •—Zø±ÊãïÀ»~œ}õ—±íùrèúÛ‹ÚoÎg¶wÈÑI;ÄhYeWÞ…dS>òn¥ =£lBØjobúûʯÛÁ—‘½·Ám*NÏ]“!{N=g<Óe0I~ÚÈS´s²x¶Û }j`ÿð}<;ªL7 *Ñõ‹‚¦|ÌÊYߣÌ#‹ê• ÷8'¡:*•ü3»= ñƒ+‹O´襂{Ur k¤ÞrNA,ç˜P3ûUxLÝü±wo2[%›®…ÂgÆ´Kç-gwïY渗5¬‹Æƒ‹Ð»å5XZM£=Q\§Õyi°»YNŽ~OësÃxb#döˆ~‚©Ý*D»å9coíAþ%¿‡ÞĹT#N6¿‡/cØuýðï]H‹Ø #‡¬±ø« šùù±o¯–CÀeQzÿJ Ú51û®ph!s!'¦ù¨AƒÑ*¼³êD®…ûï2Çî«“~Ǹ=}5ö9/…Îvdt×/§)ü§ÉÆß¥¡3öÓÐzÃùy1¾©ü‚ë3™×w'ãáŽ"6R®oÔ+Sku*0±~+Ã66 …í&ØÞ²d‹ÝÞvóg`É ´"M¼OºAVí+|=õäœqË‹ÈÍš=‘uó"è‹1Ï &8‘•\×fûn’ è§Ÿcéz×L¶ Á›.6þ‰Áe£\·+„tS´¸·Šô‰QçÚ7\áÓLV´y7Ä¿¿1ʇ¨Zÿgäò’ã*;¹)¼Hò†ÿƒý¨¦éG²Zâ·ÂÇ–<Ëzª'óÁôv"NN< Û]¤LÕgróäȧ/g1å÷8¨²LƒµÇŸ€mš/ðæ.äô*¶ÒÜi¹¨µr€íôu²{ãBúHG¬NjPÃî,L;ŸK^­€J×{ Ñ2†]+K=& Óá{·!LJë$½ÛÖ¯Ü>@Е‰Äçb(XDpk^ËÐ÷JÑÿ##6LÄôc7HBEM£ÙÌ1wÖé°ª²V<¿Ðx†qö2¬ò³>Z Î$"‡ÈÑÀë`|aÕ½yˆætɰ5Kp‹Üäàe—3½ÿ‹áŽ…¹¸sÍZŽ\„oÑÄÿj°áº§sbMa܈ßAü,߈k_¯'ó/ƒÕ®t|’íZ2ïq’u&š5\îÓ¶kƒÂd1j´^€õÖ AKÓErL6—è ÁάBµŒS)]ÃW#kAt\:æ3Úâ¹ù°zz/ª¿+ËQ>pßöue1y0y œždɦ€qý?Þu»"Ù]Ñ:+áé†$6uê#ˆ¾à—»ZŸ‹‹ÑqUŸIž‰•’afç¯`Ü)Ü-,‹=ÛyÜ7rBÝ›Y?¾CMñgûoN¥[BZ ÀzÒÿõª×ŽH°úï¸s­}‘6ЧsàûÜ ®²xXÍ;þ95ŒYùÑ â ¼-žikY O§á0¬÷]L·¼ÂÙô ù›oÉè,6ÿ¯ ×}¦4ú^rÕIýq’ëÉ( `uˆ$´µÇp‘IÑÜ×£‰üétT™hMë7—ã˜$° ÀÎî§8”¡ˆëJ?ÁóÇõpÅô(oÅ =x}ê^ üÊ7£ÔTº 5W5`í¢hèÌ轂ÔËóV{l@:ÒWã ½` ýÕ߆ײB¹âß68}])Ïúr¼˜NÖó–|=KVOêBºý-ªk½AÅ‹‡¡Hëé˽ÓX˜-Y¾¼ªV#ýîVÊ$”°y‚“Ö°K_ehøGfÔ«MîÓ`’ë â \ÜÖ ’ ’ŒhÛ±3Ke±ëS;½l6Z‹n¤Û‘žØQ%ý‰óõÝNçXi5 qa ;ٲˮtq›7Ýþ•á÷¯Ô³y-ÚáÀv-`‡Ó°UБ–¸½€€–½ô¸óc\²hÇ…K3³¡)ä¹ñ7IÂí&ŒaKNÌCÍ ÀÓ[Æ¢šf êœУ„’cŒš!¥ÕšM?v:ŒŸÀ¿Æ×xð·µø#DN*f·Ãö3u-W¶l®*=¦·ß~†Ì ÂÌ¥Jº†Ú¹Õïᇻ;?«È.x‰±1†Q_- <…ˆÄEZô“€ÌCfb…¶Ú¢pÄJ•ޚ̗xð·(Û<[ê¿‚÷ïG9ZáKšP'²´=è©b´J?¯ñ<*Âûæ:tÈGŽö>$뢸¯¿Š!á†1u_!o×éƒBä¨û;†ÊßùÍ½ÒøÆé7„DÇ^òfÁ7,Ý™ bVÊ"ê}àðkˆfo‚°$S"溅}®\ 9»g+-ØLøÕª?9¥ò_å"7wÊ\ü4”…6›Á6H—Î;…mU·H÷þUÔNJ†œžcÂ’×Àç=£yâ¶.S‹ke¥¯Lp¼‘(xÈØ1ãç‹qþ0ê§Ý×z ñ‡!óqM =óûqö7U*ªh@&=±‚…åkpï‚±Ôø¢4}¦g ‘§qÁ—LÜ»¡E¯Œ͢P8ƒÍÉX¤9ðô~îÒØÃwÅWiG²þ)c ÕAŠÝî_‚KüÛ`ŸO!´¹_G‰e׈ǘÏà_Çèéd9æÐ–‚µ/fÀíë_ø&&¶î«ªâÔõ f©,¹®cÅÊŸé±Æ‰¬-e}j%I]˜€†µ`¥xÝdÑëd óWZ¿VÖÚQîù>s›{ ª5O ×æm u‘÷xÅL5X@‹ä÷j®Íëç=vW Ë?ŸçžÍøDn—5áƒ;2,|$8«‚ÃU­§grˆKøÇK6 …ã“k=X"#ÎRÌ“è£õ&š½ht^˜¨ wqnSqQ¬1]o%sÚ87•òïÀ·u¼56 p˜ßÓÎÅ-mÅôB äÉÌÄ3žö,ȶ–´>“å¾.!†o|QøÉC>ïµ)fÄ[°´)ÇðœS &´ëAHQyïßM&Ïõåºð>iÜ9¸úÛ£à¼*mß“ü3gà£ïwhWóæWü3ÃëGÚHœTìû¹’í©4€Ù ÎP®dŽÉß3ñ¥ /ã~96—Ò—ù騶ý’?žÚð;ê}ú:‡;Ô™Ïܧ\„Hãbüäý>Ï~Ã?R?ƒf„üAÕåjtrC)¤þÎáœô-øR¡'!§e6+:÷ § 8ð;6V ‚[gý.Êêt_r4h#O×芭W@‰*ÐÖ¯ã†ÕG÷ÿCÞäúÙ°w4NÜ哸f5*³G†wð/EŸâB†{p½ñ^pX·d x KP¥×“àmÁS´ù(Nç‡á¬±-ë€øŒ–y‰ç®FŒâ+SúdÕaÐçûó­ZnQ™:ãÇKA³a2§úv<Û«œ«ªéåUÍPŠOÉõ\Ìë}Ôh°‡¬}:SÙpîyø±1•Ó*œIwÊ.q­‰åL›W~#_ïdñ‡¾ƒµ¼,›¨³ŸÓY*D_ø˜ÓŠË9Üš›’t`Ü[<ýõ *û ›ã6¡âÌË$£Néä®|~.fÌ¢’³išñúøû7HpqÁõpõéfnšýnrJu:Þá/ÌÙsÑ\ºøLF,Nàì6$ן‚´suÉ^çŒÖ‚nïÆ:æ-f¸(}Æ0U‘4X$.À¸ãЗ:ò\Þ‚Õ]ËæÛS§¥ù:l×}5úAãXú˜°}ºª­ä„-Št÷AEÿ° påË-~iº=¨¹4†Nm¢¾ë†`‘IZö»€90¸U~"7–¢Eï&ÐÑnijË9ï– ä÷ÒIL·ä5y³ñ<èL„-»|Ø4`JŒè§Ú ö¹—›¬ö”+¿ïÚ? ñ²C×|þTIMé%xeŽ­iÒÀw¿…¨È²Ét££+(ŸÀ2§+SÚV‡+óêpQe+o­¿.»ríd/±ÀçÊqý„°ëv9Ä($‚ëÙ÷0s‹:Ô¿»ŸÎ]iU)Þe¿Oüç²-–6Qšäß ¶c¹þûÚ¬pj)—#w›*îr…Öï´{ð.½ÿ÷¨ÆQ‰L,g 7k† “+ñGlaóÀOؕ߇ëw§ýíùÖ}ËhΚjèñ»•Õ3hßÂS䆻% ™Ó‰[äè_éCh¶ç(ü¸7‰ÎÙ¦î£`ª<4.ûƒÛbμ{ðB\“&Ÿ 1õ*lÖøEdž¬‡z¿¹E'$é¢ë%Ü™‘mø¬¸ÒoØUs 'ŒµaµLƒŽÜ¾Šmùø~¸â“ÜÁ÷q1Q×Mä ®ìçü^ám_“£‰•–ÌéâÜÍ¿ƒGPçSsml\Fš7 D{2mqø‚¹ÖµÀ“}Å¥Þ·&…åï±uÌÜóË£õTT0W6)&,¿ŒÞ»§ÐpëX3} ýOϹséSlؘAÜÜíPìƒ*ëÞ"@O §ËÖõÂØˆÓäç¦Yä’7b¯®]µÿ¦·³´_"üﯸóë&S]}-~ç b­Yæ ¿r‰µWåø:ˉúÕÝ‘0£L¸@–½üqœ·±¸¶õŽn2ÜùÑÍ¥dº °ÑCâÿ¼µÈ%®1b%tVƒ–¯)h“O RÁÓÙDÞ¡¿ËS°È7awöР뛹“y p±ÀšÜ‚ùš%Ð%~-`Ý œÈÄC¦ìæØ6<9^ÂÖfÃñÉ’x™ìjM®SQ(¤?›.¸¹Nl•ec×¾ áýF?ߨ1¬Çú¹ì¢zðZà‰_Ä#¸ŠÁ&?þ¤L¼‡áæL:%œÛv¤†Ü  Wti|ëV6+×›EÍŠãV÷9Ðí!fh çÝÛ•©Ù ã¼Z!õ«¬Å=n3Q">†ýî«à–—ë á+A<=Ñ k¾&ÃÂÛlÞÖ‰p£‚Ü[zšXgPZrð:i~½Eßµ±;‰áLþåK^Nñ´(v‚„)WÑY“OÜF6ã¤Yà„L,>ŒXÉy‡7àÛW¢ÜbÙqtK‹úL;ÎΘ†°ö¢´m‰-vk$óVeïƒ eàžÌ1öýI<ú¶Øšƒ¼»¦²Û»«¹•L{¿þÂ.Í5t(âî;$ÌôUdh–ß<úx-t†O÷£!gX¬JhcÅ%ì¾›M‹ÍÃUÞ5¨©ÚÀ´nm=ÿ©zÌTšÐ²€j²^~Wõ>9²Õ̯bGÚ;TcÒÀGŸÇG@âH./ÏÆŠ6=2AûŒ´YQãAfýd=g|É’ôìØþ³›`õ¿ù\£düu~et½Þ2Xḙ .§,zƒ$u_ Àöÿ‹s<=Ëb`õ vk‰Ó›kÖà¹ø+8ÆKˆÖýÞKÞ…—`«X¾ 7dܘZ³&ýòh,[`-†\£ÂÚ °\C†Æ<å]XîŒ#OÁ™uÅè¤üê&Á¨-&õB#ÃŽr }ò9±žSÓ˜Oo/?LU—áNþ*·ÂžÒõíx+-Œ¿äm JªŸA‹G+éV aa»‹ê¼n?yÖºÓ¸öôý«λ&7 †"ðB¢85ÔßÄ„ôï¢Ñ°Ýð+™ÞL%–м ÷)lôã˲ßaÑ,oëTâïòž‹ÜI£òßÅâÕ3iP÷`ñ’¢•j¬O$…Ž•˜_ªÜY^—5‹ë1¤í@ïÇ:.Ev!k-3gê÷%¬1æm¼kÃz 0û·ÙÂþÝÿé9COßjæ;¹”½Ø$q-:b#ÀÖ‰*ê#Ê ÌÅw×dÙ9åutÎlÖÜéOë ï§&›÷{.Q)˹ìÃ&y"}ˆmNY R51ÔF;›Íî ¤­ñl©H!~ö€ž´&Vñ!ÎÖºðèÀÌëâ¶ÉÈCñǧè=ºFwŒù¤ÜÊ™šü´¦yûôX‰ÇxpÈCÏy&¡i¥5Œ<[*@ø› ¬QÄ}éå ˜Aáß .{õY€ž®5ä"‘ÓzžÌ]ñ3ÅG»G LM›MßkK^ƒÅq˜qß8-¦gõUYœäQ\«lA¥´ëáÂ5Ì~¤Åû¤ãÎVEŽÆ('SpG™/°Vj~×”d k4Q¥ü:ñ«Jy#6`ø$äÇtrc‚+xWÃaò«rÎøÝõޫܓÌ)*n…«kpûža·²ŠCåë|ôËŸÁd·-å4ü%X”gwê4“ ?EÍùò†§þ‚ëÞ¿™m9ÃÎLef‚Ùµ_yÜ~í®RG˜Yÿ,„[kCðykëŠIF‡Éq˜ôS¥_ÆÚ´b`¹ñ[\¹%<|q|ê\–³ÖŒ…ŒgqÅܤlü a7Ÿ~#¯w"D4à^¹ý µ6-'ãÃŽö»~¿çdj3ÅÙ¯`hb3m*Kn“"†59'Âǰ½Û™¹^9õ–œg"Ïu”æ•k—ಈ8¾úØ auû@Ýo Ûd"Ä[ô~=ê­DœeÝYÎ?cþY‡ È…Oh°Xž”ñW{\ÂõÛ"$Ѭ류ëjC¢¦^ô,?Ø][Ž‹¾,oéÔõäJÃ}º³YBÏVºaí]8.\Ä>vF’Ó7½éÛ÷¸ö<–7U†ý)éÇ‚aYjж¢†¢ðåcyö2J“õß*Ãk‡¡@è‚„(MÞø»îŽðÀ¢•ÜgîêmpéúNh@)MGØ3Þ¢4¶•°!Çšø3dêà `ýñ8ܽ†í~åÛ¶ £æ>Qº×p!´W¼ãëóÖ3… 4x·í>þt¬8ðͺDÕ¿3ïñblw½íMþ„ªébtH×ÐqÁDîGK?ì_T'ü£ñS§-›úòÙGÄéîL?"a ľUÛSóÈL”Yl5P¹…ìQìdì\5™n‹)¥žŸ_’þÓW!±y*¶j4㦵ì9G÷ÎØÁœÝ4.<*Ìb­×¢±þK84÷\Q¢S;<™Áˆ4ý¨ÙÇZ×YSs zÙIüäF‡7 óv¬ÜO£VlÅ;,ƒ˜UÅ®¬ÀÔ@Cji°‚Æ<±f¯dS¸ÉûºF9æ\øó¯<¯œ_Ëk dv:=` ôÃÏiTô²!v>³øw¸çæ{ªj³Ý³œt¾ð¤7Žòþp3z>x7ûjÞ/žˆþ*_àÀ;ë µ;Áh¯afˆ3Õ‰±n >>N÷Oªçì1 yRœí2ÞÍöŸ ¤§eó0X3åj38“¦ZòX¢ö½ßãd$XxõèZV Òɾ‹p³„ò¦„9~o¸‡é÷ùú‡^BXL3·«þï—AÏiÕvÃH€¦Ÿ›Ä®á:¥çhfgÒÒ=ùèÿ+¿~“˜7Ž8þø^Ôpjg»F `εñEè3î /ì’ cå“cÜœ¥+™Ê‘-låBÛîÃ'“±ÌåêŒ\X ù=K`Dã 'ýû-hFЭAÌŠ ðé¦/]/ñ‹¥Ÿpu o°ãf7ÍD„MYå%È'ÅC,²8«ª×ÐÁ¿q0+ö8_ÄÙblNl*„·]†õÙ¹LXè:ÊÌ¢Aq/ÀÃÏøUfT®(ª—±¿ûå˜F…!õì˜KïùËP/Ëé<µ'lÌÌbò[ò9Ñ<ÝOoOŸGkLµXsY*I¯X‚Öñ¶RŠtðN;ûÒ¥Ì]v” ÞÕ¡ú?°}ËnxïiV;IˆÍcÏ×¥²iÛÃXÿÍK<¥e®äbØZ*>6¼JŨøÒ|çé ,D_Ÿ(ušöŽÌ¡‚gÇ —zÀ(‚ÍÑ ø¿î´ñÇ 8qúQú˼µÊObRpn0x§ìÇÒ9ã%èÚßé¬äÎn¶LçêUÅŸ1ž!Ã],_§Èß µ bÀ5O›µ¾eÜ­ù0Æá#÷›!äÑ0ÁC×aMË6v÷f<þÜ8E¿<€ÃQaø©(GœŽaåcLt¿rîScÐcôe[èn6ì ˆ¦!C¨´±?P&÷/ \t]iÓÚ Ôxƒ2Í  Ýo¸ðI²"Z‚væjòõfK€mÁx¦ðj9uß³“Ô:‰‚ЕOöÓ7=åN¾ùNæ ¨:x¶!>;• n's²¹*‘4<#£+Ì< 5ó é/Ü1Xž’Á7|îÊŠúZ9þ³[8ñóY­­IÏ rI·“7O:AtN~šLEÎ7‚Sg×r.qýÁ Eï¡Zrv å¬ÜGÚ?^âÚ;°ð¶=¶¯‰b+χÑO"©ÙyYìÚBÝw Ú½¹²ã}(Û9‘–šJÀ‡ôö•xÒŒÝ+ﱌ5ê,CX¨âþæÆ…Lýë²ó£ýÇc¹»(4Zd@œh²ÕSt駪 z—’½›ç³3g@tFWóÝ’“Þt’=×gzíñû¬Û°#P·Ã‚.–RdÇmeéǺ%(ì¬FóúŠbcöX,ˆ­t{‚ŸÖsÄ%«?×3ønÀÒÎøÓñ*ò°]ï0HíH£ùµóظ–6‡nZÌf0˜©àjfÑU AíïàVÜXRÞh†ñóšÀ0æ2îl}†iyöØ/kÈN?M&)ªR8ع^»X±‡Žˆ?†šáL›<U­¢v|Ð9ÐjúH“ù˜”‚›‚‡û0 ×®>„VïþðÏI±>ër H1dZ“xåwNb¸Ï?Ô)ÂóãˆÍ˜KP&‚“ß=Þ>äKÖ¨fÿ7âpàMމ¢CF}+tAÓPŸò þAÊêëd>úò›MÒQòêSm‘Y‡Ý`à‹§|r I¹tʆڙÀ‘#lÇekêHò†å0ÑÅ”­<+ MZU¨¼b*(×ßæÈû´7g32`[M»þ1ŽøÝ7¡Î[V²Üø1Œ=K@_ÝXøbvoWFƒ×*Ü©µå ’ðƒï°õ&©f‘µÓ’pzu%/c¤†É¥º³”„]˜úy®Ê-Äȧ±m… 9qð7u²ýò[™ÉL‚KóõÙÝ‘[Ðâ<ó…Ùæy~ÜÔØxÈâµ?­ÐÍ{Š–’p›É²3Å_Éàô…l8%‘Ý«ÞN«­”iôòµ°¢Tƒî8‡½²÷ù};Ò‰¹Ì›¨-£æVÂ@L!ákNúÞ'^ýãgàò½¬Õj«=\:º¾*ÛrhsÿÌç_MÞÌÆºTA\o%ìÝ0„}ASùa³Ï3ø`P_³‡];*ÅŽÊ.cßdRõ]3[=ÃÇÓrýÆé2ÛÕÌÄ¿Šöˆt²œd4(˜OWúé¡*3Wøþ;ÙÞ+|v_sûâ0í¹Ä_º"†yF-¶|Á½pàÊfa؈=ㄸƒ i°D¬‚)bò´wd–æ *¯¸:Ï}dü@‚_·uÀªe«ÙÔ•ÌiÂ6æ”~–êUµêKȶ8aìLB“ûÜÖÙ±°;Ï·žíÃy7èîþ{ìÏò«øAªî|>H-ÝxÉ>Uœ…ó'êÑq†éî4 6º“À7 E©ûÍß8>¢º2‰ÛÂJÔ»®°_:ä°üNžÁ3‡)›6›á/pã÷ÉF³7RpQg,^ÙÌ•QvÆr'ýOÛY3Æu_kCÐìvv0–?h‡Ôô5'ªÏ/¢Úe] Z›D}†:9­y2äÅNs6¯™OT•w°09ºÅ\%Ï&0ÝŸÛáÂgš†_qý5ežsà n~Ç ÎÛï·å´7¹"N®L6Fóß“ÙQ¢ÑF0U³†.?0Þ?_Kã v²_±gáÐɯœfú]gíä×€w¥b¸Ïë„éÒgˉbß$ºã¯ŒÑì‡c`­îR\HÇ •K>Õ;ßóÌâñÑœ|¸†f^\Dãtrƒ¯Nc›½½' §Å"èÉ1»³˜Áêr^ÁÍÚxh:=®|õÞ±ÔzÝL6)fN¾tgj/›É¬’M4ÁÄ ^oêÇ-­7Àèî=¢cË7éÅ‚,ÿUÔ¬¾A?^~Ë©tÍ™«'ÑO±é\ëÕp.¹.ƒÝº-ÍzU~sÎvÊT{Jþžy¯Ô|êløáö†§¸MšÝzHÔ=Œèž¶™ZòÚ~~‡“Ù`Ñ™øcáQ¼–ìÞ?#{rìµ&“7gÐP‰¹l‰W&?&¥‚>/ânVÂá_rôg´+=P,N‘õôÔb!žP× ìÝ2‰^H]H'm«!£cóO¯È¢=ëå¸3 ‰î#P{œ»\ù †ž€L~oÿ\°ÿ1šßÖ*c`º#–¿s‡%õ%°üãPUcÚrÈý©3Ù³Z˜<'íµé5ùdüïann¢¶Ï§q‡uwãyÁxð•›ûv?}² ls½ð¤H1κxüÄ©ñÇYØÛl‰Ók-à¥Ïâ—ó홾 {6û.ÛiH…¤,HO§6 ™Bµ÷•âОþykQª4ç9V|Ía铼 ìVXÕïäïoLž)R,Þâ.øTOEåÌÜa”ÄÆ6*1ø@Sc‡¬Œè—Ôr =Òh²;q ˜ÐiÀÎ6·`Šo žWéÃÁë×±wÀ™®ê=#I$qê2¢t \—¼ågl{'Ä&‚z;y*Ü/Y)gdñÙvΊ™Ó‹,àù +–Ùr wù?fc‹²‰ÄK-æ1ncNq*fÙ rCŠî»%I{;î¢C4¹jFöÅ ÑÐ0kö§Q¯I»±; á™(Á#S`BÞƒQ¾¢À®•ðÕ§as6¥á Z8¿xuVᶃ »§r/¬†yK©=›Öû¬uе—Çý ¿Vóžï0¡ò\Ú|µÇOú›`æFkÚÁ3€œ/¼´ý|Ÿ»œ·.l0`R{‚éufì³üUø“抻å“í——Ðk$˜ë€MŽ)†ÊOÓp¿{¼ROà.=Q‡öÙÔÒAšqµëØo»É`¾ïÈòyTŠ/Í‚i@ñ#ä/–‡ŽúËøö©!êöaƒ?kAö¼#¥óï€{ê'H;#“Öî¡"Ê“àôNíÆnègÎ=Ùï#s#@ÿ´½¼O+78ýW—LÜK¿qJâ½þ6Êãà’þ^’á¿s»¡Æ¾ú/!q»ÆÑâÍŒ[øð» ÍäöXåÛ¼˜ú263ïHn4‚sÛÂÍ~UzÓ+Fßá¼çŸ'Þa®ÎQØ«²ÎY¦à¯ŸkFç´ÁA#y_U…biRþ¼«ŒsîWo§DX}@‚%èi o²6 'W¸¼üºÛÀŽñ5©êºÔ7§Û”·Â}ì s¤¼'ðø#Aö·1cÓ“¥‡„ ŒÇ§{¹½#·q[u*>]C× {é"|)ªŸxI¿k…kÄÙ©e°‘kæVºðoõÀaÝ4ˆºCµlùýµyì•÷Hè.±ðÖdµ ÒðÉîÑ3÷:É$ãÃIùäÞ}s<œ%…2`¬Ô5ødî £c ãð3d+»1÷Nû† â›QÐn¼Î;¹ýͼ3’ u¸5ñŸpùK%éú»¿$þà«þ΀ñõBhs ÷q¿&‘•$»U þ}o‚†/"ðné3:®F޽C–›×·.èuÝ:MH»ÎÞœXÀ´B5èfÍ"<çç…=çìa~é1œ¢™Ã_êðhõºB~ûöù,ŒÛ•Åÿvþ/É zG÷ ̬>El7£Â¤eàÜÿ‹[º¾…\8[„i^~\Ŷ¬ôX!¬;IPRN›í»k 6'î³áSÌ?´žÉ8 ³dYJÕÒ‘· ¹ÿ…)o•¨WÔöp® ¼ÕžŠckGÏ÷‹ðøaì2`Íß³(ù•« Ù‚â«öá„ÈV(Šê‚ï qÍV{8%•ëfÒõ«ïò‡§·‘·ÍçÑ4ò"´ŒûËû]˜ÑËóI–@5!NÃÌØ³Ò‹Dðižô¤w´r8!— ¬iÉÄÝú¿PÁÆŽ“Ã6)UÃ×ì©Tau!ÈÞX΂JsÚ§bYݯt^Ø\!ˆ¾_‘có„Ù“YúêO´2…Þt» ßwÖAïJ_Öe½ºýÁiÕáܽ§“hp}#{±§ ­ýÆPÞ`.®ÑÍ'Žæ503ýºÃ¤„ÝØžû5>äëyÂ3¹‚Ïæž„#Xfråëá¨2³*c>ƒw¿º~“ƒ]IÆLÔÀ™ï^É1–šõš{6\È>Ô/`ªA¿qìÔ¹ÿÕ.£ÅÜ3d ö (P¸ÛëMoNÍCÝëßp¼í^iqf3I<[p] fŒA¹Ô`|P”ë ‘= PÛ³›øÚOF©þc¸äèYT=‡|™éŧI]¸P̧~✎çÁ¥v,é&dà9—Õs>sÑJÒˆM.°¦o´ÄàÏ÷aÏb0ðlÆþ–/Ê‹àCù 6âtæà‘ÅØ/x’ðM>a}’ µ3T¥Æµí˜{­œsµ'Ÿ~à#_òÇñµèý…²lÃhŽ OpUN‹*÷ÃY×y«’ŒYæ¾ß˜g"†G¬K6BÊ;²µñ/>EÂ$ÿÞÀ-K°Å;&XŽ÷±cõKðŠÐ¤šÎ’ìm&œþ—Óͳêpç¯J+âÿ½>Ä?!v Û&syÏãàõy(PI†·îr¦ !”KSó¬oÁu4®ƒ°zu¸:å¾uò¤öJ‰÷­gdùÌp·Óœz ÏA­þ·+®cT¼>¿·Ï‡–ü,€þ€0&z´$ÿ>Û2¦æÃü°Kظ׎©Ýö¥‡}Sx—îfq G®s²awáPŸ8ŽÉ¾€ùïðú„Õä§l5¶jäÍó‰Âïa‘Øô6 %ºñåÍdÄ6 þm¸­‡Ñ̽’W-a<ß¹ÚÎõä¡sôcRýƒëŒŠ€âÊ¿XÛ]ƒ9î‘sž~œÙ¿°¬>žÌ*¶af ùäö¹wäÑ!1îˆ×rHÐÙÀŸé¹Ÿ¬ÌœÃ¢çñá³=wŒ¬­«Ó˜ZÔ×ZKðwÜ(ŽÛ‹nChg²¯‚O$Ý܃º÷×3Å[£.ËVw‹p61éˆSS¸Ä¬cøò3€ÙÍ#X2Š-ZMáÐ+v~N19VƒÈrä/ÅF}ϳxwNFr³•geɰú¢C{SæÑï4€HˆVùÉKñc2FQoåiœÃùìT8µ4¸»ñÎó„øŽ¼Ñ~‘¬+³bÇ¥ ¨};†¦_…Bbø p köžäª6w‚ñ«RNàf,™)Kãš÷°Uª›¸ÚheêaØ‹O'ËÑ'Ü –ÌÀ,“iÒ¥‹ß„ê/ç`úé%xQiä~I‡Q&ç2 û÷®•'ÙÝ0.fÉ/WêWïQ}¤í:-é÷"C¼„— gq/]ƒì6eTm7œ忔<‹¹i ÁÕÂß?؉jà¡® »\‹@¾é$ø†JÿÿŠZ‡å¨•_­9Ëi×;( ÏA³QHâ ]ô,j¯#E™ðÖ+ÐêYÀß1æ¼ÃеR|-µ{ð`_Úy.Çk¹3T¬Ò@~ ‹ÁfbºT˜ ×áüñL½ü.4ß»T«»aÒÙ¥T0Ы7\€Ù8Êoäk‰/Œk~—›D|ìeäÙºÍyÜ™< êù},?¼r{¬¤ NG·rmÄp­ éÃxÅGBŒnš3òr‚´&BÜTÃÉÈ”$,Þ‹sŒ¸ˆ—ûÐ9Þ öŠÐöÃyDŽêQ‹Ïep~ßV°¾òkm½Hbʼ¨Ù€[r.£àæÎÜ`Ç÷jnw„BëÌŒÀ*KyR]ç!×2Ÿîÿ9‹¹üd¦¿ãúÏANj=FJ ¢‡Â œªžL³¿ rã{óáD³åGÉ á§Ð¥0HF¶¦p&ëÓ1±`>•QzÇó³×§i/f³P¥°f^.¿ï‰Ú¿‹ØíÉñøð¥Å~0¥n4æwO㬊lh ßϵ£¶ŸB·5‡GNmC7~ vÖPYŒG+ÁIOOzÌI⿚mz×ÞŸí­âÂ×Ïã”ò+¹"ótºKÛ¦Šsm*q¬ði3ÓÆMu$ã÷÷áb%(Î{€Ò¾× iæ(v·ßVzff¾“Hà=HÄ቞Ô_² ÅÄ’Ÿµ6,+§¤ÖÔƒÚ  k¶c9£>wL y§»p᳨0¼ÂkÙÌÇÑŸø{—x¾î׊¹£+^òöøÝàN¬#«_¨s«-æCó¾50K`4–€#[Øß‡Ç/nÅS gü_?zk`<Ì¢úLýÇ;œ³®TÜÞƒŽeh<¨‚3Ï[äžófŒ»KZ×þæÞlÏöÝ?ƒ¯W%oÑXR·Ö‡˜‰\ÛÝ^¬k·#T­eN{±[gwýi ,Ò¨oÍ‹®%c^סéÂn[÷¿,åÛM\IN×…Ÿ#-ö¼—Øz*ãWàvjW@ÐVGþ.áå¸üÝÆ‹9L*]nqIèÔ(|À™£×/B²:ª`mô ²ú³"½w”gi³‘ñ2§ÒÌ׺\Áœ\Š?æs¯†:QQ–lÝ<Ücˆ‹4äpXÞŠ=Hˆ 9{qgÎ^9ON÷Åâ‘èwº¾àüZ!(—Ù Ÿqú9V}óC³ñ€î;.¿&Ž~^Å„BW½[øÁR€i»’'+žï ¬ôlý·eSÞêN¥Oák9B)÷ WZíWMX_ÍMÌlø‚eЬg ³"„¦]Ä¥Óo³½>ž\¶B ÎöŸJª†±q²5[éiI?ä{‘^)&2”ÅúÂ[¸#‚ÒlêÚÅxÒÇ|›ãÑsð8·êZÝóî.OÀ}㣡0‹ðmÜØ´Ï“¸ŽIï¹Ô p!u) îÏ€ç#£˜ù&ê,dKJý±9x!¨¼ý Üû*.Aþ;ÞW;ˆ7vì‡ZÇÜá–ð"£gGŽ®ñþíì“24]=L-RÇãõ3=ôõ¯\bj±Œ¹¾ƒÍBâ,Pí39`ËEêf`—ÄxãwˆËîy mÒìªÏeÞðÔ8Ø*’€ÊòWTàxèrx±âôĘfÏelSÈEKý“T†~ää½yñÏà®qZi ê×Q C ~õB±÷GÈÌm¹`*ÂÜ&²½ZžI¤­=³ xTQŠW®‹³õ§û»Ñ$%bò§h¸[å F1ŸVθŠ)¡ŠÀ­¿ ÷ÿaÓ{Ìtñ„UÚm0ø)BS%8ã˜5\^øLToÛ»ŸŽA]Caªð5˜èëÏM9eÄ2êÆàóTnù[iv^3’K‰º‹ÅYÐb¯…6záVæ ™»µŽkl›E/?.€‡lþÇ>°?üŒ×ù&¸üªFÉO¹%-ƒMhò’”r{‰ ØÃéa~` W2u•¿Lë §!_˜[‚þÂê;»ÈÏ*hÏÕÇ•©<|ü?¦ju©Ç=cbIûpú=0ƒÞXö†«Ê*'›Mf‚gåv;ñ”ùG¢!ÊÎn'ÎúO—cšàÇ´`j› üÔÙ’\<ÿ« ´Ÿ=ÀÕÝpY% Ƭx‰Wüàôƒ‡ vi ýã[…»¥Õ©Ö¡dh«ô9_"èƒoF˜ñd=]â· Ü¿$ú¿òPÙ˾Ù[à×¶Ü"ØD~¾{Ïý×6a®6”IK±÷ÁC䌸ý~> ZâË#Tìì)n½` $iÅ’·>xãO#„‹Â÷j6˜ìtÎv¢f{Úá?=èþ/ŠB™Äûétæ¡0ŸþP¨ƒ]ó%ñ‹Fxø±w)È]HÿÓŠ–›Ì²8pƒUDa™2Ëyo@Ëí×Ó›û]áÕ§2šS°ŽÎÏ9ÀÛcËVÔ¡ÏÔwÜÜ7 ¾n„åâ¢ìˆJäkA¿N(])Õ†b ³¨tŒ3üÓÊQ[Ë¢ŽÙBq E~Ôj±Ä˜n:Ig¤àçGœÕ^uÖ:Ažº5;ÐýéŒ±Š¬™ó%½žG(ÛºŠÔ¸vƒIê §ÖušêIª¡!Œ@Sµ)Öéíe–c 蘓‰t–`Ý:ëýêóogK±ÂSè)¥m\ÀõóG¸˜ÒA'^‚V—N¿¬‘3;PH®de'þ¬ Ï_n Ÿ›°ÃK†vÔѯ¶ŸñLÊV`®È¶éV§§=.\p€,U›C³s3XOó_¼‘¯@: w° ‹iìÜÞ·Ør|1“H?À9mL§à[pÑ`4Ÿ3¤·×­¤oâZh‘Uª A­î•4ãúJÔ9G:ì Nø bŘJÐ"²`—ëS ùϰ¤@‰)Þ‘³84«‰dÎÞ3à’{9–š,'Ãâôò¥ m»Ý«špAÛ|òqµðõ¡M·^£ãþô¤V“Ä\y‹V×ÇÐU+Ù;û±¤aÛV5`‚ËW¦¢ÚóhNF4šû™2е;^ o6T…ï.£AÚGàîQÚfÃdÚT9Áæ\tæf/Ö§ú{ÔÁ2WœþÅÍ‘Z¸^Ò©jí¾‡7*-ȇ¦¡­´þ§'³43£rÞ`0軯_DM—…,Ê{€õ >|OÁÕ'±³ËÒÀÚ¼3¢†›ð*oµà8Ï…ñ“hÇêS0"ü _ Õ¢Ù½/ð}u­ÞxÈ%Ž‘?€©ú Ø v¥5tO%(L«€ƒU"´Ëj-œ¿‡·uqòÓ©¸¶QZ@7'Ñ}¹gaF Íë ÷w•ñóŒÇÑ4I,øÓH>¦«q6ƒ31äHžÇ&yòØ‘NêÊñhÒ/rÂHæ/͈t¹2]_-ƒÉ«6Â÷ÎÖ–·¬UAIÎGEßZb¼ÞmöAZÿÍѠ݇Ñwv÷¹îË0àÆfÚ;ƒo!˜Íå9\Äò?Þ˜¼6Éoañ„ã¯âI`éû ض°Æ°£9 t+œÈy—¨*ÎߣçXzcü®ÐG›vsãØ}¯§äænzÐïäïŒEÁ»„\p³gûªŽq¿måžìÌ€Œáø­H|²C‰™ÿS£EÀ/Ë©ôWý“{ðM‘3ÙáYŠ™ÎI(´wÏ:+BØ¢ÅdήÅÌVEQó<°i~É[´ù9o…ÙM¬I: óS’H±° öפÓèw9°!º8‡>ÂÒ­ŸàG÷ 3nñÃÜÇ“Zƒ,ª`,KÔäàbá%$~$‚e•™mB땟A_Ä8£¹žíŸØ ßM{y}?Äq¢—++¿ ÷tÕ.8lê‚DyNV©¹ùÞp{¢–Î O”Ë`ïæîšîeš+VÁw<êÆ¨³ÝhCßÖ¢xª+œ`sþ‚w–—xTA•ÆÛž!ñ̫±„“»iDŸlü¯.4._†¯.xj<7ÞÜ™ê(gâáÈÜðó4 ÝQà:ã§²c~ÜwQn_ÎÎÒUwùÿÊq¿±6>ë¥ü™³1`ýºêÄX*ñ ®ÊM`×å-È÷ÓËÙÈ®yœ­ß9òybÜ=ÇFüÐ'¿oUϦ¾QJœÔc9&|n磳º%%X”V1){y§þ‡»øš4aÉBúâÍ5¨OuÕLAzï\:½e} ¤nq}âci¸K=F§¿Â]Ŝظ‰à(¬ÄxLËà „íº:ۢɊ?Wà¯\ÄæáàóÜÛ±{¨/„ •gÏŸÍäDÅÖ±á<)ºí÷0x€j³pWÚlîÛ„[ŽóHfq~ëÄ`t.èµ<‚*¯†pF¹õpë>-ƒ²ÉþؘªK…ô=9ßÒxåÑ{4tم٩âìOe2Q*Ƀµž’o… õcÜXµÚ¥%p-Ë}zí'×U- â²ñqì7¡;7èRÃ8Vw9·®žA§z«Cøâ­ôÞ!y´swdcÇIs‰ï»A!u«ù¼’4y 1‹¥—¹7/Aê®R2÷¶Ÿw'Œ†åXóÛdœÔÿÞaÅãOC„w&[£@—öfŒ2Ms(j8NfNf%±Ü~cGSÏþ§a¾ .!ʪf§—HÒ“.Ïx²e%4Àï$¨^šFKb ôÛÒíÖ0C7ÜLDXI¿œr6!»êÏ@C˜šÏ²cÖÓæ@ÁêãäVÉ|¹R¼| Ð@þ:p8vÁ”^ §êÛ¸#OÔñæ¸ý´~ƒ,]14Ž'îùþN_ÏóikƒäWêÄSbûzz2eG"H’Ù3ØÑ.M‹|“p¢D=Ø}ŒàªCˈyk?7Å—…éä#»ùÛ*zÐÈ+çÏ5ÆßfIðåš4Wû¨õ—Ûü!õlò.µ¿nâ¥JÓe6bìÛBt|*Ç·upOfŒ¡Âû˜öÌ•xô})º?[Kv°yåëð›¸To¹6Ó4É¢›0xÿ7 Æ-beèÍÏÅk—޳8•7\ÌÓLÜ\aŠ—’ë@h‹ V I²7Eñ˜ÿ$.Ø5-%6ãxKëßY×0Í®®6üG¶¾~Î¥›îa±«j¹ ¦†T^>§R^‘}»~·{;™>]мÉÐçb¼hXÊäH ˜hN¦•cÓQßõ#¬7Ü.ŸÆm3j8ƒª›h–1dšŽáöà ôå®7$Köôì´¦Û;Å ¶Pm³ÔÙ«D#íÏÒên˜¢ðæ½;ŽÕÅÁ¸þĤö!t͸X:5,–4Eó¥gárýµñÆU¢è±ž—R÷ ”…•©¹^9©Ô‘…ÝœXøØ;øõéZ¼1ý3¹¼½»õö½ËX_º ¢'Ê ¥É8îÞ*-Ý!FMù^è–âJ'ìq—‚ÍðèÇL6" *ÊxŽûD"7SVpd O˜ˆš»”ñÙƒo0-o”lÆŠ#Ü_íQÔñ—Ë*Щa #µ’ΜÑY2øÌi,û"ÖŒ›n˜à#w{*döc¬i{ž={d[;£œHN¾W3¦2·ÍÆdåï¯P8D¸Á§ ÜµÿÓ60´b;=óQÝûú–Ì‚íÍ‚HâʰyS&¶®«æ£]8;öR‰Úv ¡„êN¶áYλ}…ÝÓbS4íéµmãЫщÊ{%°B«Ý´uuŽßy ´¿Þe»c<˜žÖIœVÀLNM¥þÁÄL9¸õiÁ.x/'Bôì`†“¨@’7s‰x‹ ÂÇÓ!’4T®Ÿ¨ƒ9º½ü–pçÊ×½F qOç|´Lûïÿå%¸xn%K7¥§©Ó£‚ã˜âß[Lßj1µö?D«Müé˜Þ´"÷#zn?¥¯Wöpzì Ÿ¤Š#‰‹X³T-ZãÌBép×e˜H=Ží1búÌœý9Lo~SÆã÷2ؾ„cì7\‘qâl¬Ô/¼X­É&î £Â6 T&rm½‡Ïrô¡A¨ƒ«ñ)`^üì=º‡i8T‚oËêY¿”:g%²JÏ@üxNˆ«b&IÀt®+;yY‘õ‡¸²µW¥lóaƒÿ[¶Æ§õ~M§?£ïAòÛ›ìŠiÿß‘|Vyb/:ù óDW˜ —çNøV,tÁ\Phò.МžMîÆs™pdÆ3VúõdÏn % ),?ýmz›@æ·0X{5ÑáÁžÓtfR=÷¨'›þÙLC·Ç1;ëã¹|-ûmúJ‹ŽyƒÍ~E`¸ë û<‚‚èKð¥i5´††Â¾À.{Q¥vç8»e­$ñ³«Ö}ˆ‡çZá=Ý7¸ñ¸*ó®ÜÎþÓnþ6,ÀæOPg ze¸¾BŠ ]"Ͼ¦ƒaɦéîaba¬M£ˆLÐù(UÂø:ÇH•Ýú ¿“´~'µYoøékÊ18Ɖ›>EíSÃK ;r“|¦qâ'ï B—»¶¿”ð”8Ø?׃n>´™r=ì\ìö¤u:×ð†Lg|9o^d-ο- \’+ú7Û°“ßÏ㌫Zt;¯”Æ„%ÀÉoäÓ¢D¶Rà Ìb&‘·ËÓ0HîJ«ÚñÛÖÝŧ±r<ŸwÁÑŽ3;v¹Ñ ¬RgÓCÏ3HÀ†+M¥i£ª9’Žku7OǺÝý‰Ÿ,‡ïkÊZ¿EùÑ#³2aÂR[Lœ ôrÓU|ýsìv¼Á_.Ê¢ÜâñÕ/-VÚTOUfA©ÏUbÐ%B»Ÿ_ G ςʃ‡xí©L’ "W¬x­E[è… ¿[„½É5À|—/|£³F¨ïëÂÊTCÙÃåÑ çÇcÿù6Fe Nzv äè Ó'³ n@œ;{F™ÆGÌ`eô 2}ÛQ–Éô½¦ÜìXÏá´óåhĽqùºwb×X²¢cUdQ!n¤|í,fZ`|ØBJ"þÁ¢=º89û¥ãšÖ°\Ép´m–¦ß›£¸ÊåÓ胀ÉĶg)ºÎ¶¡ý ÀÜ#‚̘ÂòNMÆ4Õ\vÌæ4ÞYÅflL`†+ãøk]øÔÿP:Š&|ÁÛÖ9tÛÂS0îê\¸6r‡î`^Û?bÎ5)¼¼”mIK›œÌhÌ)x=ùÝbÅa®S!M=¹§5¿#'º Ðæåjd D[§OßÕé—¹V´ý£SqÕ`[ã™û±¸âÀc"-ZìËá.¼œ¹5ÑT&*áþqª%±kÕY^×” ¦ ‹Á3Á~x°Å„z¿õb‹µg0-³pZ|ŽîúÔÈ9KyÑ)Í}hÕfO9ËÒ˜ª¥ì¹êa4xñŽäpTà¹ø½‡%Üôñ«iãSögj:•éP…!JtÑ¥ùôüü`m[A—¸/¢~Ÿ‚;]‚š1Û™¹ìlÆüô˜ÉÐ È÷ ÒÇKQ¿g68‰NcuÓ²iÆIIº”®¡¿–GлÕhΪp™…¾9"t¾¤1J)L…nùt–W3mTø2¿sØ_—qtœ|æýÙ‘¹–€ƒh{¼6ëo¥âf²U Áª/›¹ëS·ÞþºÓ7ñØuK8=ë(GÜ .ù½¸æâEbŸ 4lùc¢ü1úk·³²«y¼\K„»_[Ðw7lzÖ V]F˜0Q”‰K/À§»DEpýzóÌàÔida(õÉRÏZ9f×»ŠY'NïJoÀÅzñ#€ßî´â"Ë;`ðÆf¶ßÂÞÝ6lzÕ{ú:ýìí™|},'™èL;?c¹ÐƒÖ¬UVr§¦aàpîw m´âɃªýÔi!)Þ'GÊÖÀÃ.o˜`T‹Dáï7 ë;¤ÆÍe³Vå9ˆ<]ÃŽ€)oBYÀËQ]œMg­R'ùdXÔ)_–ÒÔQ|ä±4…-VÒÃí²—Àzœ Ü ’£ÇJ9s*ørˆ°1x´ø ú,C§‡ÃEó^¼`¶E¹‡û}W–]¼Šfjëé¦_³y+x”ïSLŸì åÔv"L]¾•.©ÇÑg¸¨)vè«'JÝBÝèн1ll¾"»µp¸9®`›R®Æä¤·ëí[¹œŽ,@¦ä”°Eñ+'6W•ÊÜ]‡cý¬ÙåãÙxwŽ3Xc¤%Ðw^.xê!ÚŽ˜ÑBžìé‘¡)Ów‚À³WØÄɵµ!f[m¤Á 2°kÛ²ð©6¿á,\°SÚ#ÐËM;M@ÖK/U ±Ý%S˜SÙGžmÖSîÈo–¤©ˆ"7Ó>Y¬Ö`.ä:Á)ºaÖvtQ9‘Ê0ö¶4íôUæ=-F‹×U“Ü8ôzY„НïãÌYàݬ}—ß`ÅùEà,â ³“ëaÇ—Í0ŽF¢ÒKiø³ç¨®ñãwDËÓ5± âmÂ@éãDà\rá¦Ç!Ü)Ií4ð× EVëƾš<Æw)áUÚ,7O[Æ/âRæ>ã§Pb»g©³¢•ÃÜ’½Yp¦*ˆNã×A¢§0¨8£} Z¼†ø.gŸƒùªkb脸Sœšã"ZÚA|)’¶"î®–es=±ñ§Þ­ZBL#6ÒKÇÒ _u!vÑ6k#‹Ù:*¬¤¨i*B•õC’µº'ïˆò{/s[œûaŠX)ŠìÖ‡M3NbæÖ¬øy„LÜeº¥nðU ù›+¶ÓÐo˜wÛ—^n4‡CüãU{ c_zð—?íç­}–É“Q‚<ü;ÄDÖAÀÙ×Xï¢ÎVo:Èe*L ÛîVr§…{Ø©º“¼GËX%Ébcgžàæ´@Ñ©fönÉ%²Òu=ͬY ³ÌªÁqö'˜þ÷]{µŠ>YžJíÒ/Ak?0uUsŽ×ñŒ«âѺJ¼g²™4þ›4”~žEÈdÒU‹]þo$,J|iwCó/ të}t-±Ã=~ntŒÜÆë;ÊU;'£ë¡nܶLŒQå/°±FÙ a*l‚&M¢¨/"Fsv솎ןAç„3lÑf%NŽ,eávV0kö}øãæwPÚå ?€»V,ÊmZò.ýq¡Í?UèùW¯™•é4`blíýÛð‹„RçbeÞa»ÃxúU½S%2KÚYàãËd(q,»:'·[½€MbYÀºuÑ\*Ã,YG©#¾¤Ê<ƒŽQE·ü½©ý^Ù±¤ußðò/AösL/Z_< kŒgÐ?ßNˆW9JDg2›dnGñ*¸Ð]OßQSêTVˆëH5¿ß9kÁ“ñèòñÌqà4fL-ã½ÚØ@ÜpNÓetïü 5ßMiUf1ž}ˇìm"ì»äv®àˆ9RçG½6BÝ úp‘K˜Žœµg9Wl冑96lf0Vñ¨k–¾pk ‘î§p¸[P;Œ}µ?qGÈT"vN„µ?@Gá*×DÎ\!–ï3:S­lôKçà…UÌüN¹Ôî]Ñ|q*¥¾ÃêW‰\këâv^Õ $ …6‹ýçp›¿#Éțƪ†µY¥¢çÑY`erÞ-ÙM—æ`Ф8ÏzO2Ù½ô0þÑ>wæ0×Îô)g)o`qÇ"¾©ÍVÌ<èC=Šê±=0Y†&sf*ðm›k¯ùÊËþ³o:ж•ÑÈy\¸¡Ê¬¤Ù_¯­7½þ’¡«˜÷AšéÜ…šõ…ÜÚƒ Fá"ç;›q½SQéÕ$j2|‚*™ÔaÓ®£L(ò<—ðÍ÷Jò`Ú+i–k!KO>‚o9øsì&gï®Cíåf ÞÊ( ÂwQóŸ1‹@;5>?c} õxÁõ´K°âó9Ø?#~ì¥7™À¸@aê‰JíQ»¦ëLqeÀb®ÆO™:º°jwz/bO~=nt+Wf³ÉFLr¦=•Ž•"“ûÍN ã©_äËÒ,ØZ¿ŸÕÜ£ f˜Ô=ÚN…VMcÖV%CCd©}rû7AB|8n²GÞ±z¼9_Åo>@_4b³¶Ä“²iýü™F˨Åw[Hè™ …)ôÆá?è¸ô)‰™îÇb?OÆW§£ÎKavlP´·Ñ@ž2†öô£êEæniüt1Ïþ!œ›£MM椢õ×hˆ¨Âu»âÐYìðJøziM"ž‰8p^’mFŠZN¤¢sËñ…l™¿¾ö/‰;Kðož9n>Eî<ò¦¹ï™Ãcöüˆ>“~5 ºÉöåÐU7 ãͳIÄ« týGU¬ÈcJ;Çr³«Õ¹ûËoá&û¼oÈ@óðíÖ‡K§ÓKnÉ1WöRîZ§ÝÊô¡Y2Ž·Ù Y¯¶ì·•ç2þè0ׇ§ ÿìqTY³‹ü[V‚Bç.CKU\eŠI­øl«+µµuÕŠ9 ñ¥ç°ú N+? ÿ™4¸Š]T\ÎÒldî:Ķ¡¿WD“8RŽ ÓØ˜ö½À¿’ ƒôf\‡¢ùÓaPÞ¢÷GòÆçÂU]L¯mâ„¿ë’i!íÜÙé³!ò‘&kŠëã>z§²7uG‰h5©3GøO®zB`š>³·d'å ÿÉb2²œ ´fq£F¡¼û.ˆœîÓmWæ}“‚±}šyã Ü Öå µ›Q™‘nû#{ªVœŒ< `†)©}cÃÞÎüï,‚Iÿ]~š¥,ËM$óð„s¶júìæÄ(ìß\Arzç·Œ8KX³9gØá燰UrìœG`wŽ0 jІ’4òÙz\ØÙ&E'á»ö\:£0ç%Ýä%l±€3^Vð×ù\ëS¡wNZÑiVtºª»g#N’º æŸð‚S2šað½hÜ“±³2FøÆà‹´ó$pÓ2(ö¸5Š£ªy¦¸+´–Íçés³NüQ€šª¸ÁX-Û¸ä)2ðX& –ÿ,‹ ¨ìm€¤¬$Ræ­CnÓ„Ž,ÒÍN¬/$+£bA¿Jñý,`ÓÆfh3ÿ@ÜÖmÆ‹y…Ø6=’(ä‰düÜ8…'n·¨É Îxé~ÎHë Y~h,8žêá.IVƒm×|vç÷mŒœ(@gåöÁòPA617 Ï-*á<§±Öy—aá=t£ê Hß¶dÞ²³«³Zg)ò´ý´YBy;¤N…Šƒƒ¼)¡õ!VÔát&ü‰…‰CÏPãD*&~Ñb]å|pI¸Ã• ÌÃ3¦´Ïz ­óžw£Ÿ@p×xû¨‘¤þÎæ=^Êi,/ÆoB“9é—–4FFœzà¥ë²ÔéüÒöø ZUÜÝ7 ÙV¹Lrx#×¢7»öý ]6g+º3hXkè="ןÎjÍúîf‚_õzæÚôjt+yÆuFÔó§(nàÏfûc3¸wuí0×ä3½¶–žYÙfQÚ4/²—äŠÂ™Ì;øj[&8šSúÈã æ[Â9åT}þJHЬ Õ¯Œ¨{ûdêºLÅá'´úD`¯´ øÏ¼ŠÊŽ÷¸¾‹úà·Ö ½jm©ƒÒ?0©ÒÀÑ»¼§³Xò8ø ƒîêÎÝÿVyû•-è> EW÷·¡uù¾ïª¿DõúAðè { K  ®7eœG7á:®ù2{¾ö8ð>ìƒhG„ygžáäK(åÌ$–üXÀVwýý'²lÅÐR¶ÿOyh„œZßuïÚà{È1´ ƒ+ÀªqFØcXvXÚD7ÃÊ÷•pàÛ}žÍUOrIÚÅWbÿÄ“Xû|57Kåd&Vócå)¹™œ“öoÁ7Y›ð[n!Ãïø² J#¸ üÇðdÃ,²÷Ùyrõ˜¤MûZ¢§Ðݺžœ´=O·ipG¿Bñ‹?ptO7g˜,Ìâ{e`¦,cnZà§Iê`¬z §.”ÄúàÏìGp9c àª1ôŸÕj\¶ü Ùû²•÷¹Ñž¤5sÖmnàtð×i߯3ö ÂRÙ°rýEärpg‡_/ÌGí‘7“PfbÞ+LõÂN¢ôÆsxü‚0è¤RMÆ¿õ²œ|¦²£Ÿ® ›_5²*Ip[Û¸Ùö Ðûm+¬/EgýpPn·e²“Eɰ}Êï¡7§ÐáÈnžGUò&Ý$<þp³{¸þ»†ìƒæ-®Éc#º ‡B_ˆ™|üÎ…6NƒeŸÏiöwˆÔÚ.¬[ËàAŽ®?®€¶¡ô”æ4ÜYN~oL cîrÓnáæ.sÔ/C;åˆÐBHs1ãú;qïÚìµûÑZ§¸éõ60[þZw‚žó!üÇñ!úÈ.FnÂ\oMzöº&”-fo3e9»-:à/µ‰ÜµMÁà,O^´flÎ:ª@üÁOØÐ6™›¤(.‡½a‡(‰å¢0xS×Qêr¸ï>J_;H-ºÙ]ŸÌÔ' ñ^ï§:£±Cؤ úxXµ¼ž6”+ŸDÃ7Äêo§ýRƒqtËÁõ˜\‚¢6q5oÙŸã®Dß¿ýÒð¬»ù¤ãb×QUÇñû_´êŸ fñÓù~"lë:ÛÏD¡Í´4Ncd<^ú²œÓø³üÛ”˜^ rcõðZÅm4üà@oNxÏ­=µž_]T^·¹©Qà2Lì=G|JýQ½Nœy˹“«£ßOv^Z”‹ Ö Ãæ ääÂÑX«€ ÕLåÞûgeö}¤T_"j²Uåh•*Žº*bìçI¶Ì#“ÿÃè×è(ÁVßêÞÿ“SÒ‰úEr­¡KBG±ÛŽ'õHÆÖãJs18cÎvË‹Ó/$éÁp-2ù{ˆí/†¹;+ñÍÏ8Cï '²f ÎT™¶“²`Go{HÈÄs›ñ…a=~qVƒ¥c3Hèí&<Í9’ÒÕè\§È6–Š}¼5>š%€óZì˜ü]¶úŠ)=­3ãøJTÇľw’‹ß|˜ä3Óv5£/r›Pnó*¬xù“TŸÛ‡¢+WÃÅÓaÀž#qÞ^Š/,‰rù2êrpNû{ Êoh0ý5v¸«€+w  ð8¾;äNïýZÆn­>Þ Ñ)0ÞÍ|-1(ŸõJ¦+Ò/gÛ Æ'Ço„{F´Ôû [°w#*Î(Âñ3’Ñ6-ú‚WR^[*[ÖYB”_Þ'«5ÎØéųh¥™Tûg:ý]™Ÿßåڱdz³°¦á-‘ÎÜÅBÒa™£ >¹ñ Å{G ÐTmþú³Õù£„й`Ïc°J< £ ³õ‰”w4/Õ,fïóçÃzÑkdy¤ Kš×ÄŽæjßDR›¦[ÄPRN:|']˼r 3½ç°œ뉗| –̦ókïq«èÔyr#Ž`óÖrè?Ò^ÃÆ ¿W‚Êæ(óÞvónÜTc?+ÿ¢xª §÷k®Ã3`.7„ëœã ´O„‡ÞâËf`ÂuºgÆ ðÙöŽ‹ÞhÄ6„ã‹Ó°äðnøUìóÙÀ¡1fÜ""¬yu“nÂÑkéü¼Õõ5AˆÒñµlhük\¦ò†è·”ƒÝùY´E ‚ OÞL[êâËEq¦Û}ÎoÖQüdžŽcrp:OÞKŸ%GWà*{s&¡:™ÖøgÁŠœ©˜|Ñ&VµÀµO§áð"›¨Å®ìS$?²AòÏe(òW>vÂÏywðó‰(ÚTîIßxƒ’vQwŽã\OìäJ“¡'®r†>îAöò§#û´0ŽùÊ%—}äûÚzRÇ‘·xZÓ¥`ÿ^î,pãÿÅý6Îk'ÊÏ•8oÍ2.ðÒljó Ǭû‹¾Ž"8°]™n³È «GùóH®:®¿Ç­/^À5ä”ÀªÉ½½'qáèuÖìòfí¢»6ds;£AÕ‡O8Ñï$×2,Jg½„÷ö/ðçúùôÔ k5щ¸>}ŒO6€´\¬ÞdM£&¶q~)Ðdר6CÝžD@ßj;xÖº\ èlÝ:ôY± •¶†±%Ôþ1»îÈИå´g’üþÞÅÝ~óžúÁ‰DØÿ«Ç¥Z2¹ \úb ÂEÊl‚ýUô-›‚{§o ¯fPÕÓ1¬ëP†ºæç°fÄ…Ê®¨`ó–Š2å¶hÐnÏgœ‚gvE˜>mûq-^íÀ ¯SpWæúfÏÅ…éx73Ô 6cu—xã¶\$šï}Yël%Z˜/ò‡l˜à“ø¬O€Ù.3b®ËDØCe††Æå¸¯=•›3ô2±øy:ø%¯¦7C´èYWyþƒÃÏ×¥¼)tnÄ ÜBàz“u»ŸG÷|¦uµÍä·µ ï΄ÉÌQ H[¹³>Ÿ×ê„aÑOyœwŸëT%©Ÿ÷CCPk‚¨8Àkî5óá抱“b¬k¸xhÞ>9 Ë7r]öàº&‹gkÅŒn½µÔœpú6Jšü¸Pû»XâCãÖ˜r.L¡žSs[àÁ/{jƒoàA§7¾> ¢ýFÔË÷é_[„³&r%-è¸$eTzV„ÕÊlæüm[mECþ‘ŸX)˜I.‡Ð…¹è#ƒ§gÒñSå辫Ç@éÊ<{JjMÏ¢—DU#H„µ×R¢[Ì7“P5S½°ôñ18ºeÞr¤Ÿ<ÊPWàeëªý¼s«^ÂTÇAòi4og]r¤—.ã²o×àÓ%}fsÜ*&›s+ EÙïP>ßÝ­30bR‰½1;‹°¥u{ˆÇt,QÐÄ '°nsçÏ¶:£ÿª;øÖiH/Eý)GqOÿ2ní>îz‰îï!¡ ¯¶;À{ØÎÎŒrîöã8W§Læ/½N_pIÙójïb×eÊkjÁò€"ÓSzÌ ÑV‹xÔ¼šÀªÝÎöÅ(]ÙKDäàèA`9ZªÄô.ßC°“½Ùòûü%ØKt%—:wPýZg:iþ-üiH(®“d4¶³Ç:`el–ù'³uÓ%/T_ ƒ•éÙt‡·¯AYôÜѧf‹º âÜoДŸŒšc’ŒàDX|ü9j…¦°ÝÇšè;ú ÷–3_*´YŽ5½õfQ)µ˜wÁ‚ çÇ@n‚8^3É€ÕïjX™@6;cª‚꿌ټú4‡öÛ· Àž2Z9tÃnX[0{«éøMí"ðò§Ãª%ømv9šº/Œ+oáÎ]HoÅßÛ‰ÁuI6­i._¨i–­?"”ĸmÕœ”KìÌEÕ¿QëˆÎ‰Ø 暊ôà1üð·…uø`÷•=ÈöÄ„}ƒÜñWvüÊq¸ÒhLœeµ†³PòÊ2¼ò¿÷busȾ×ýüÇÞè;I„ž-s V ¡|ÐìKmë±(¿í"~¼%M×_‡Îú\ëz]ên¡€ëæ›áÙJlÖå½dÉôº^ îN;Ø{µýüÎÒÜ Ï[àæßÕ «â,>ÎŒ‹veÞŠfðYt4­X„‰>Êìˆ^") 9ü?¶Íà} q¦så$ *+Æ?9Ì“ŸG¥wDÑAq)*x¼æ-¸¶ãçPþ®0ðC>nÕ¤–Ú’‰ŠÁtå-=ö"n½y?™uÉžÁ}f.ð¼Œÿ¦ø x,Å¡­Sqð$“¯W`¿4zQÞP‘N¼­ò·GÈ®UEàq(f™° Oš¡¦îLT c‹Gsû•s¨ÿÔžpá¨æVSÚˆñ÷AÒàÓ…¯=†,&ǾxœƒÆïå<|+K½+¡[d"mT[ÂUM°Ä)²ÙDã¦![øó'Y¿DšNŒoæ}Ÿò ó&°à…d¹ÏyHßÎvÄÊÓ×õãÁr[3&—f …Np26•öóÏÛ³ªaáÍI4ýÃ|¬6©'õ?0§S–í™_S¢,芇Ì~Ÿ .ºëE;ÇU¡êÙ¸qäï©!ÚËËáÈÄó ý* Û"NàÅÖ»˜uôÏŒ ¯£þŽT³>‡ÿÔÞB”}v;ƒ^4Wã+¼rçT^³€Ý3F÷Àî)éáÀ<üÙ!å%ú¨!gÊM²ºnÿË¡m–3-kdzŸçÅž¥ÅÒ}ݺ¼¥ò®hx‰ÇöÝú…æ2ÿÀI|;3ù4ËvJ£såu¹Í›Ò“+„ÑŸÀV_7»M6ôf®+,^º‘N{ƒƒþ’,B%ÏŒŸÆþ{~£®:[׋ÍÕGÈôì *Û-B±yTìúZ’8Ô ;½’ˆÇa6yæ ÿé( 7>ËžðlÕûÎ@ØNŽÕ_ʇS¦‰¨6=ˆ™¾¬BY{ 6WÏ›Íñ¢©‚Òì½ár°ç¢ñ~‡ë(!,%ë\lÁùq œ&æ´æÁ\4™Ÿ†Ó=žâ»•lBO÷¾ö(§ÒÕEBfÇÑömZòm'†õma.^r,Dù$M’cáz»ðÇàB˜›1…nÞÜH>îþÇ+´œÂÎüu"k'M`BFAfåúõŠ'½>6‘«<˜KÎnÃÙë—â 0bOÆN'¼w>äü%Qò„#5>ÂYÑ$þ¹›¾ó>¿Ç)lka.šÜ>ê ô^Id—pŸraõÇP&±Ê‘¯°aÇß¼ÇïíSYÓ«±ôǶ2ÈŒÖZû£²þLÚ–J³ah\%F6 7ëp^X9Žû¦Ç¼f\„w3À­D ÎÍ‚˜¥yp)ù+^âGuïò_޳i"¢V žcw áÉMhòôƒ½:wÀrN3F½‚ÙîÉèûœ.Ýgž‹/’Œ¥ïa<ïDeÐ3§ë°ÓR ¼DÏ©L}Qס×*Å®T;@†™OŠCÇÚh67Ø®¦°S/îäŠÇÓ=ž)°úw0mÿÑVå+ ÙȘ +Œbš‡1 ?÷«]WèX~àÞXXqÉÖÁïÓgâ{G‘nMJ¥ún0æë3Nf©>Í,P¥Q¿qõïbõ<’n~Ò‚‘N«a¥¶!=,ŠtÆV/6$hD¹°Ñ\QJn*¢BGÆ¡î]AZµð*L«I«šñl÷[ºsW ]c¼–/i”B½!–>½Å6ÛL$N¢t¾’ ]4œÎ B˜}Õ½ý,M0g•^š4jY6N¨i‚ÿj¨Õ“¹¿Dõçn³{,ûÿ-¹ ƒí,;1”…¼ò§U` ß>¼ƒ¬pPµ}‰ëRÀ²¦rþßõYÐÿÍ™®¸Ú5»ðþý½,üë/8à¦Àjïa£é8ؽBñ€8É —¢½Æ7Á~œìÞéN§6YÓdg¸yVy‹[Þ'²«:ò¬Óßãׯ {Ï.alÖzøŠŒãOÔ¥;ŸTÓ…xºVÕ¶Ã ’Ùú_½2z`³s ¾›S Sr¹INŸpþõ ÒdG?-›É ÜhSv-|ÖLïù8Íö1Þž+ !L‡¬BÏ–^]«ì~ ‹TÊÐ~ü(o¯V¡A Zxå—·þ4N_~„S¤9,ö‰ó¤á„úUICPôe¼•hNEFàƒ¥…PpŒÇâ×Lf ɱÓ×;€ÝÕâ ¿‡9‘ܹÉêtÓïëÀ_g‹á;ÓÙ¢¢£ô¡ök€µg¸M3À0ªzì䨜ô 'â2|1šõŸ ®™ŸÏ)”]À—éÈñÏûÙÈ’-XRˆ$É,“·ck?¸ ùÓRóùìägÚ7gBöŽƒ¿v‹qï îÉÖð@ç3d¿™†­nô?Û‹´æ³Zm%ð=-ŽZÐCùsŽÀágjÈÿ0Ž=–I„ÊþÅtÏ­ap9ó¿-t¤‘AûQYÑV¤÷M‰¿[Ö…‘åvâ4žo ©½–L¿ò:Q•Ñ…ÆJ,¯³˜ÔÁb|‡S¾~Á¯L÷è(”Ÿ®ãÏrÛ ƒ"îP1ÍŠI½ÏÅt·IðF`'|?ŸÚ'îaÖÂVØp`#>š>E^àö;’ÕF´ó¢Ä…ñÒÊ84Õ†½.ð瘉½†k\Q}Û¿dæe8ëð ܺDÁhã¼Ãı‹÷Œ;vm.Ô×Ͷz‡±b4ËGÛ½ŒÈ™)Fô¿çºë/y-"…È,ÅY³nÞƒ°eŽ08Ù¶Am£¹aˆ¿Gñ¯éÆTXÒqs7„@ˆåÔϪijۭ¸]ösÁ¼ä*dv´6=HvÿþÄÙdôÁi*ÐÝìhpÖ>îñÆ??Dè„UÙü¹çù0ÍÔe§ä2]MÚÛt™œµöÄiYcqÓAx­ÞÂÕ$—Àøfej0Yަ­¤»oá¶ú¿áén¾Ð<öâËËÞ,ùbÎý®Àäþ„£ÿé§ o”F›hŸI–H ñßîjÀfkwZ®þN‘… &!€fc§C‰¹(Wo¯C. ¼'Ë–,‚…Û¿£ÏËËèXË/ÙÎ׋Qj^,{Æ™ÓêêPö´ŸvÌe Ý´p`ö4r¥s7ox‚‚|Õs‰û×R0™KÅ®8ñ›zÇ¡ÿKczr®µso£ÇGrxs/¡zó(Ž¢Ž_Cáëæ ´nƾè÷(ZU>€¦þ­xwW+½w‚ ¼ö£v pdo8³¹m;ä¯@ÎÕ·¼»5±«½„-µ8LS§_£¯E¨o^½û‡²ócNb¨‘#Øö§°_¶¿¹Þ!¨úë"O±ƒ §ó?ï£Ù(FeÞÀ×ñ[pÃý8pƒ¥{sHfÀLø0Eš½;-û’зO¦9PHÿ(™¥ 2¶™šÔ\Ühò¯íˆ#—¥ƒ©ßJWÿ‘Þ¿sàRä.Ö¼Ñõ+N¡bë¨Û°% ›sÿäÁ‹Ú]/G˪ÿbeÌxüJËlU¥RsàcÙ.zµ%…„.Ü„÷vZ±­J‡±´O…mܺÃá4ïJõD:ün1‹Úýø5îe÷å<™~õAœX®Íº";0 ÄhÆãrX°Ø›&]ŽÀ×~ ÈdíT²÷Ÿ8SÙÚÊM¿ß&÷ãôÓ@âî˜ÃO1*M˜ï2¹ Ën§z½i\ßò=¬´þËž(ζܳ¦íkC|ónh–MrßÅ©†¯£o“lAþï(gíéø¿²Œÿã2Ö•Ð5J#pþM&I’û ‰÷Í^-ÁM+jÑ„÷ ƒŸFá­ädî}CÜÚËç®IÚs·>s7Þ}Òñ)æL“Ê|8G¥£`²L™wÅ=,³ãåNÕ¦Ok¢àè¾t•kbKòªm<+“_ðj¹íûO¿¥´¥­%ŸV›Ñ5FîõƒÔ!þ3´ûºÑð縱™E0à¥Ì<óÅzcàŘóSŸ,µroÍÕ¬=Þ -?ûì®aÚ :Ë^móW¡Ô­= îÄCgõLG}X¼\é«y²P œjùüާ«¦Á‘±Ô86ž¹X+0ÿ…*0ï­ô>Šç>ôe¡åáb.tÖTÌK’@뉅øÕê>v^æÒÍy°6 òŽ`‹7…œ §Ñâª1ËKŠÇ³#xé°ÍRÕgüÎdjª²‘Ž}bGŸwƲåì8 !)¦T§@ž¸¡àêh<·ó*Šc­‘dÖ¸³´-ÀƒµÖî`Ÿo¡Ã–[ºc9Ó°”Ãm1U,°Zœ šÄÖÊHÒäù‹è‚½F°»d1ªV…÷ËÒ!#ÑÝ; Α•8|ôwùÆ+õƒß EçEâlÒ'Ú»¨ázê“ÖÉâ9Ï,¶çD½gT]Ó5LÎ9ç ä(AP„é:`A% "fÌbÁHArV‚HdºŽ¢Q1 bVŒˆ€¢òáý­çzg­þÓÓ«Ï©©ª]µ{öÔx²Å—†Æ·cæÅ0 >IìMtH¥kq’ÞYSÈKsÊîÅCÍ‹wã,EĆ¶á£•€KÌÙÏ&SˆªØ¼É‹b^VI²ßn²µØßßËÖIUþO‹lÓÂHÞÑg+ã;˜/¼ÏØ¥KÓáô…­˜k%‰ºwî u1!Uì߈P9Ã^±îåæŸt!wó™í´z¯¸a¾·0ÔO"šíQ0ÿu Sü'¶=^ÂȰ”9qXøÂ&Ë`ÝýߨcÉê-nwÕWø½é=‹èQ¥ÅÐæf@D%¶bµñO¦ê¶"¨\”¡f7Ã%‹o˜$ñ rcñµd¿ç9Fú“³à¹9sÞý*†ËÁ†¿Ö :jN7ò]Ûgx#x <Ò‡"‡ûlÄ|9úó`NñÀyrTTOeÜfк¦rȬˆÄóîãìd!/®ä`;3S|ŒEœao¬¾‡ªnìJ'T´Lù¨9d_ø\jâ"Ãz©8°©îÁä÷>(HÈà“'é<©3pºð-N2ñÌÙëÂ`âQ“{Ò ÉŒY#á¨A{ù$T¨ê0ŽZqÌò€@ǃCø÷l8€\ëŸ|»Ï㮘>ÄO:ô§š2y·^ƒæf?ÀöäHšœÓÙ¡FôЛ*èi?}Ë.3Íñ &™ÉùÆS]ØlY úµD”g56¾7¢ÂËfÑ)ï Sbôåô0Ä¡¥®'Á»é ¹§:™X[Ö[P'Îj×!#9 ]IžÜà>³ZŽpc‡6œÈ±¸ïÀg/B¿£ùh­Ä˜HÀ¿†êò%„S¸{2G€ïK3§éq)[¹E/˜ìÏ}—ñf°4*]_úÃ.°n8‰°wÀƒ³Ñ}¾“žø" ñ¢<àEÉÝPäê†ûr¡Ømœ®ýʼf¤ R !} Ä"¥ÞÈØÀl˜ÆžÂõË´0ÉI…˜Ñ)º°iÊhíÑ¡~ ÄZî{ê̬¼?Æ(6áíµÏqÛJ|™Yݽ!°ÛX™$ýY ÏÕNCž§Ù>¾˜i³“OQýñO¸¶¾V]‰ùªFo\O•Ï:²G®àc/}Ú뜆ұa öfd4HõBveí^ºÇd+>ŒÐ#µr_±ò„"ÆÊ[±45†Ì1OfOG¿åNUºì….õzWÁ®ÚêƒïÔÂïï'q^{ l(³‡»±H–ó<üT¤‹Uº°t{ èì³ÅÂõJD#0þ2¤3·†3~ßgãHÌ/Ö0{/»T@'oñ%³qéj>býá ¤gh“ü@_„+ÁAW^lï…„ìjŒè4Å?á–ú3æ$-ÄåG&|íC?»Ï€øO©ðOW²v‹.ùXf·.Ê€©íL\Ã…œŸ쯘<3¶†§;¥˜ÑŽkðNQŠJ <`«Ë¢0hó1ø(ú›Y{ñ|Ñ1§ªEGq­_ |…ŠT{ &¯L¤Ó‚õ¸ï~äà X?£W˜¡ ØçÑß/­ðÏï˜×Œà²=0}q5+ó«Æ?GǬh,Õ=›“2!Wÿ#„Ý“d„é¬à«ŒLâlîÇbmrNð göñÉløâûìM©I̱W Tz»|µ©c–MôU§Zü­e{ Õƒº‚1Q§Ë›O¡uãÜðé1 0O E S܃°ëý¢üˆ¹ÓéïbÊd/Xýe¢v/yɾêå%‘ÉÏñ¨~ËvèÐæúHÜÈäA/iùг‹ñ{ ˜“‡ÞÆ1j?ã42 ÊÂÙT^'jÇÏ+ª(‰zUÄ=x_éfã¥ëœk; ±å³;ë6Y—«ü(ôN‹‘ÕBYf tü»7SÄNÃæ5½Ìœ‘8ì°iDÑ"^ªä¹ŒQ ’¡E¿÷sˆñ–m+W¢'\ÌHÓå©DLo6MJóª7ìŽË²äî ²ã”)Ë’Å¥âÁ¶] .,Võw%#ÒòøI(+taè£w̳;¯áéfÙ×’»ŽWÁ¸8ðw¼A†ô¿²ÍÍ®8óú üÚÌéêwœ­ Ì&=†î»Ö„ ïs ou™ZHå.¯Åkr­8% þé«ßÛ}²],¸¿‚;NðÉ0 ×̤¼¼r„ÌÞ «¥öCȲxr¥þ3Êo˜j‹Ï¢ÞåÙì—Wݰ‡Ï¾ö«ÑŸŒP÷D½(j†i™Æx–=ò¥’‘-|Š1÷Ï‚Sè(uײ¿ü|ˆíç>x¨MÔ.Fëì!|°ã;çÁ…!U§[ Ì>ÊG·ÉÒ^CEšº¬'0Þl ç“[ÝÏØ9Vзã&Óù ³6ZR…°lL¼ÜÆþýæÌ--Õ¢Ík7EžÃÀk4Žg``ù4«©g»K­°þU"Èg>‡Ô$Yª*¹Šj~‘¤Z%â`$Zç}áeöꜘRâ#àYb‚=ï ©+ßgTò¸ËæìÃz“É8tj”ÙñÍ3 ;ñæËH¦uz(yx²"…õ¸–ÿ6º•a®PIS-ʤvoáùY¶$+"‚~-GÍ͓ŕ^$iÅÔÝùÞöž"[Döá¤Ûš(,/ÀYà ¨þb}X˜L–[È ÕSi\¡^‚f‹Û!0>=…éwó2HÜù3&Qó( ¦õ‡•õ™25³¨Øß­T&`%J}Ën ó%Ñ:öÄTâ!Ì|SÌæ[0öµLà§Ï¬ç²iþ§9ÞÊD}‘©‡ƒ~éìŸaª¹›Ë¬y`Ð’2‹#pT“ ÞpÒNr|£Æp2ÿÄÛÝ´t4÷…·•ãø®k€QÔ»rB¹«2ŒòÕ–àM¢ÈÁXXœtŸY²ì†6ŽaR|&g}ƒJš"–Àšñ øÐñÎÊS 7!µ§¯_;!ʬå_u.µòÂ?mö‡¶?Gu®% Å9‹¢rt½ælÎP¹«]«#VO #ÿ«pÈÊwCÚdægL-Ú(4aoþoPNT§]º9ý±Ë9gNtáò#ÊŒpï àuïåZΡ‹Ë[¡;²‹Á8*·~24Ža¹×Q¸ú#ž9N åè®cA9=oNÔqŽÿidDT2ÄÈç:ðb§9¼Â FÞÝKåß0˜Ó ¼Êì™àìÌQM,ñ䧃F·àÆ–Íl€»9ȯÍ>8FÕÏ@UÎöêÐW&ú99r­xÈ·ÔßÞJîUtA¡ØY:åó[ÈR+G¶ëðŽ-&Æ{棇/ýc—ØÎ‡SµŽý·´i}>Ú³Rtå¬ fã$(¹“¶ß—OàÇ%v÷Q~: û­Á¤%šnÛÓV¯”·ŽæŽ(’ CBÓÝpkËa& N©ñ½ï¬ÊŽzxµè%|\žÀ•ûû òYIªó&öMaÜ}=ü!ó½DØÖIÂ,èÏŒ0ötóaØlM,›$é¥Óó  ¾N9Ź}á0 ¯Ëü4—ãÎtmŒ,}ݬJÒv™Ó‡ŸüP¡i&††øAJÃjøÔ`L«Ÿàîã· DY‘T¤ØÁ7ãƒlf7Üü%§3d©×Y rðd\]sÊN`ˆÈ2˜:¹î•#¯ó BpÑ 6½šžL.,¹zÑ™h:àFm È.Ê|ÿV…ÎóIÎÒ|8ÖØlEÛfÓ®Â+ìXÖwœ,Efi “ ¯Àpówˆ—¿J†…3‘òðPØ_Ð(áJòÄ_ÁÆ4XÉwÏéääk®m?w}©0YÚÀÏÜ 6Gƒóõœ¯{¢IåG}’¾WJý3`㪳‘ºy~„ÁY¡Ô4Xhïˆ!#ij½ ñh?oï`µáúŒÝÀîHÈdçæ¢ìƒJô櫇)I·éÌÿµyøh7ãJ§z42Ýc‘ºùà*3rõ÷'°9oD>(Á=Á8âÊë„oså#`CEi4Cmñ•ì²f/´d¦^$9–¥œ1þIÀµ¾Lˤ=:_`¿r¾i$fôó ;˜¶Hþ2R¡þWŠÉ"‹>L«3˜ïN’%’GIë)Bãfžf•ä1v& QyÓÂ?àî÷Q„:êúÖð3D—µÝÏOÂL¡«çR¹½70¹t.ñŽÏŧøø;‡þ8cI£oÍ„'+î Bi (º’–Ì©ÔýÅj–.\Kl^Q§ÐqÔuãh=Ea§÷-vgÿ#îãcA¸Â_—èîU <Ë_²ê‹ÄCûÌN„åDbé NÜ4Úl'ID7 ÀÔÇ^ô± ѹ‹föêÔÉ-‹Þ4Ì¥âËz ýÞR’ôé/# (ÍÞ\߃±½¹œœ<•Õ@‚‚µHÚp3´x„£ç½È1Š"k¯ŠVf6­cß(LœãǨ' ÀKk¸W®•ƒ¹¤zœÚJ’gmÄè‡Ñ`Ÿ)Œ‚ÒáÕÞ…tÍ€™êRŠ ™ù;®nëE÷YDZJå5¤oäɯ5hè÷XìûÙÏ4¿+Áé© ·åøfwrmåÞbùœÍPðÎÎNº Wã”ÉŽûѬër‚ 5üéÆ_)àTžyáX6ý/÷L¥±×÷€Ï¦ ‹óƒeG°l‘$ÝÙ{âé“¡ƒx,Cœ~Km…+N`>ÏVò6ˆ÷ßþ˜ÌÜ$œ­\Í)3Ú‚#~ĤF—º<”„¸“I—”,埲…d®eïž&ØìÿtÝ{æ†Æ×ðbÚ8×Õþ0QÑ|ÄÄþI„øIÕÌÒ——ª\ã™sûÅh@Æ S,œuæxõ¦]¸Êù{”~†ã¢ÜmºÐ“hÀÜœtŽóRæðD<+nj—6W'~µf,wŸ ] $¢”¶\Ìaö/Ç ¾ÄÔ•‡±}¬DˆÇUò ´p÷.“£q×U˜Û·åè½|?jÛ°Ž\–?‚7oê Ì]Q8”Çt<ôcçés¡!h#(´9’¸É@¬*4ðüRØg¯G̨Äj\MF„u¸cõÙPßøt&ü°ÒK„†l‘¤×¾7€—ÅE&o’=U†yY¿° ʇ\Þl†‡w·1q5½é ìÖ¥ƒRqø§­TT »ÇîàïcËÉñ§µx5F—h¨ aEløN╎tm÷ª¨Õͽé<½x³þÝ“Ëf² ö+£m¹!¹¹æ!4‰7áÕò)ôùºÕ˜:»öÞ¶æÎ Wùw¡”£E®«ø2Ó—=DÁ¾Élp§±õ7Ã(³gÇ$1îÏû¶Öî³{?€¾/š_\Å}S"G7¾“£ÅLB­=™”(>Sr5Óï¢Õå‹`^ Mþo9“÷¡Pè<7(IŸ|ó'íU˜œ¨@ ÃFð™T=û¦}— PóS¬ÖY˜tš- «!óΤ9 ¨xÔ–öÇ×Ãñ>øÍÿ :VzSg{–•y7Ä,à΄3k®³)g’ÎÆL8åÙ…ÊY¿€›Ù ÙßPÏB–¼ìã>}õb·0®-796kgáª"Õ%â¿î‚ë¾xjð”¥úÏ™KezØi’sé¹MäœÆ*œuð!h(lNÝ$’Þ˜ ƒ®ä^ÍE8(+ ŸÇè1®5 º4 |y*¸üóNàá›OpÖEšþ×’qeaFñ2âp íı­³ à{àO*ãÌ!äù{!ø@#Á~d„yÐß«Y[ê—ÁàŒwcX:{å¸Jµédæ#?ò½ã Ôu:’zpPÃèŸÙ iOÙ—£cP]Œ‘û¶ƒÃM%t]·Š®YÈnK©f c¬6ù®Ø‹øÒˆô­lúoÏÁE%ŒÀ¬RP4Á­å"0[ÿ³9Á‡œ4P¤)ÇŸ1+îu`ÐÜ'h»x,+o x¹çÛÇšÔ2¼?Éeã™LYú»â;n/­¿fá˦ƒðÖÊé5SþA– ‰A˜Ïl«øÅv™Ó¾—BD±,N©»Á¡½eÌ?mõ‹³>D*[—Èv`^{ì‹@ »áuÉ(ùm+¨¬XK%ß9Ó+šŠ¼Ô¼XÝÀ ]¡w×ãÉMÛÁÖE .ä6©¿Bë‚X LÇóº» Ü”ŸÞ¾ÍŽtÖÂË»ûÁá‚2 ›Ak6îe^‡áKvÊÒ¨›´…2ýUìí<Äþš}–bˆáÂ-ÇqkWÝ;~‘ˆ:4ÐJ•ÙDF7\]É8ǞƈéæäBÌcܬÆ`HôæïòB¸wúÑ«©¿àÇŠ64º"OVÌû/W2»_éÓmä^‚1×mýtÐV9BfO]sÊýñãÞÇÔåË-´K¨£=ÙJÄÝY˜•»ä*ÛRmnó­nïý@£Óg+Og‡® ¯/äÃ\í0ÜwΉl•'nÆ = ©Œb¥D=Êÿé··¹Ž»"ôÚGÀÊJ9hU ¿½åΓ5dûðk¾fÓì!¥Ih¿rJŽ”£°è„oLI¼µ 1òg_Ü0Ó‘ ñw5où݆EâŒÇÈÎ wsØ–WA…H0üü¯˜AqªØÞ*7ÂÉ ·Qd©?Ô}\¾ErØõÄ’”YLggª=AæÈ r}Ö î»/´2x+ø Óßk%ɤk(TDžË!Äç«©4Àg»€Î¤TL›Švo•@ß}2ý"GÞ ÆÃ/1*ÄgŽ.éo˜‡û3¥3ãÅîu(kT€ëâfÛZÈijƳG3püx8 Y§‚÷[ÂàkFûN»«& º»/0SæÜ‚Äö:vç ;zë"F•2ÙÂ$õõ=X¿[^²ñÆØå[XŽæRrâ¨"ñÜ-‹gò(k ÃÛ×ÖÝ>Lu2þyëŠC…ñA¢r‡ÜûðÜ©-Ó_¦FKåcmŠ.-¿Ü†g^FÀÖÁ_°$ð ¶Šò½T|ü>N~g…ßPe~9zÉh”,C4ýtaÿÂëðC7˜†NÕbÇn¡ÿÌ4’ßFÎ= ‡€»§¹öÙ,|.< ^ÎÁý¥ê<÷9ý¬ V¬v¿±RP†˜³.hùÙe»G7CR9ÿ)­L"®ñ\޾ mŽ0¤[•÷¡[k=X)dqå5 07Ÿjš¡ïÕeéUE]º±_…zâÙÈ %ÙŸa8ÉGÏñ²zÏôiüwWr®E‚¢´9 tÃ׬*:@©Ë“wÖ²D&V’<|Ì0q³CàÅÈ2¨óT„Ç ÄÛF“-½†ý=\Æ6’bêvÑLÎÁFÃ7øí‚ÉNq¤a´ú¾Œ€Éª©è¼ó³}k$ðé·ážÇK™óÇò@ß'?ª©&Wð°ØB„•ܘmñžx±MˆlZZ ÌÉ&X`ð”â/9"å;<7 ‡cëçi~ BJ#àu¢5“t=™{%*k z8b¹- LäIq]5¸üžDïÔá—Ã\ì™eßRS0b^ùñ2åËÅɉ‘´°³¦ýУ°Ü6]üÅößjƒ°$¶Á›l^šºÛµà´ß[Éda.í”R Ÿ¶úCu™=º–Ð\Y–gÖn]1ÑÇGÂÂg¡§f]Óˆôõ8øÕÒ ý30D.þÓGGé*‹II}ï3¦ÈJœtiU¢Ú9ObWù ¥l®’Ô†ÃØµØ“ ´Ð!Ó|åËúÎ[Ä^*î §åE ¬¢†Î÷o`Ïl~qÉê×\Ÿ@†²èôü6¢øtæ†/…ñ-°z“môëF³·Õø³wž}°€,¨Î¥Ö;¨Ó‡ä´î•€Z´i<Ž/~@¥E7ØüµWágï+6ø¾¹pl:]3²ý0 g%ýÓLƒÑcš£@Ï l&ž•S ýüz÷ý ú^$‚ÔÚ®’fäfèZ:ªÉCM_^ÁÅ5è$HB'ÛÑæMb¯ÚÁG±ð“î  D¡\(ooen¥­$> /ÑmÂç¦ËSYcuDf8’ɸ~ .¢zrù¦$àÅÇþd‰Ü FCN“Æ+§ÃO7K²Ìù#˜ÞBW<ù÷È1gwQ§” °dð)>oŽ÷™!%4a§,è_Q¢Ü Vk$…m»™Ž\gUZyô1gèý†K·¥gQ¾ŒÈ|ebÞoË.ò»À.HôÁä‚dxJ¦õà•Ÿ5pá?µ>.B÷Öl /ôyŸAûÑ9ÖýžÇȶÅ-›2aMàlÒmÁ>\9ŽWά$YO˜•jjDüàcøÒ?›¨6Ϻ`21Ó#¿}Ó˜Ð}ï±°¾ŸÝ@´ÖÚÓ­­IÌI¼¢£à±™ !Éòlâ·•t“£¾¸ø8·ð’i¸¸ñBr…YsjÛž¶Œ§ƒÍ–›˜Süƒ½ð½Xq\«'“¹o+™õ–ÒT;Á›5â» «^« ©¾ ݽv1\ß/­íà}Q´Y;…êšGóHc’8l}{…Ýô]ow:¢‡ ¹ o m~ÂÞhs¥7Ÿšä]urɵ=(}™#Þ;†ëîN!çBæ’øÊW쎲õÔÈJ„ ¹þÙ“bÔ%u!i<ÖÇfœma$DäN ©0Wbú¦Ö³~Oâ`E¤‰^N‹Ü@%•a9NÆ‹RÚ!‹›<Èvéç˜Í¨ÃVÕØtå=Zž–%“Ãlè¾Ú;lý­ÌÈ÷|X¡ŸB]Oñ0ú’†p8e-UTFÙÂÙ”ÏïkqéÙ{}>}%NÜõ¦ôâm˜5Äe™ðRÎÙ ²íÞ ,‘ï•á-°PMŽu"Ö¥Ù <*F&8¡?VaSë}ÐÕØWÑV[@ÊGßq<ëá½µ4Ùà´Ž‘츉Éï´‰”–#é5Õ§©GôÈÎ4+ºâÝi–ç]èµ*dÏj}à|¯þ“¹?ðõÖXtíÐ]Áa–‰Ý¼“ì»}·z ÃH蘰­ø\‡MS™F%£x;½Dêâ4Êéóq¡—úÔHÌkîÓIëàHM ¼f›˜Ê˜Õô@·5Ã-E†}Ã÷«Å!\ÿ.wÞ ,þ¨mãfp$¸?;öÀ 1¢µüÜà·cþ&ë³.eUø(òF'[‘#{j€r¶¥VOnÁî|M¸ÓÚ’ŸâŸñ(2—kK 2eȽÆTzåØÊK£ƒ0/6fá?]óm6§˜Þg°}×nZûæ9sþ•ulQ >£Â°îš&³ÿÞ"àÂGÁZ‡|ý$Lr®#ÏE~ÚÎcw¶ƒ|/Qúä¬1G¾ÚŠŠ>éd§§Ñß·ØëÜ,û&á<’°ãa<šŸ³ÇBátI—/´KýÅžF;,ž7 õWØç?+ g=/•Þz6c ûÀ™±³I(ÏT¶Àê+¯à×°$5å«GÎ32ûÃ;ØôìëÔTŽñqwçüvFC*ü­À‹eÁ`(®,|û–‹bÑ?*õúN=zµ³†–øÞ)n¢g@õô_vö˜2=4ó2ì1¾Æ¸Ô¢õk¯¢&:`­ØZÂ4¦¡¨S9Ìîc¾¸Ç1 iƒœÍ¡G£ù,Î=Fjç„z+Áêˆ!ÕÚ“…’ëfE^±ÄRìF75C¬³4UȸÊnÉoc'z¼¹Ç-ÈŸ%bûFˆ”Æè@½Çr6Þû®+…¯—}ð#u%·×sÙѲèÕ&Mûÿ¶±*P×k¹Q&ÀÞ1ǘÁ]d ÍkõpcngËÓ¤=tž¶Üj-bM££q`Ú®µtÊÅ{Àý|™e–à‡nÉŧ.Ã#c72Ì»yM̨†Ü0;VߎN+γÆ¥©Ç`,üÓV÷xC£¢Böp«7|ò^ÎábùÁ¨ÈT¥ü!Ú>xŸÍqý–Þ? Wꇠ½–Â|ÁÅøJ¼ Nœi©œBx×DèÑrIb:-ŒV&V@ïŠE$îë$*ɽËl½I«»–­Ušû¿ n·´À[n>)©¼oZ¡x¢§œÌ¤¨Ð38ëW6¿˜ƒ3gÖ@€SLœcÝ'O!_ãdˆô¤ ê4Ù›íôbR—\¢ôoÑúè?E5tÀú-èî*†ï=Xé »úÓõ7U p*™Þ›U…G/TÓÛ›éRßëxKÌì^fLÖïce‚6sÞ9ÅÓÈ®stb]fÅÿÁ’Ìî!Ïó“Ê|¶¥ðÀ>F€êR5ŸÃdÞe[_±—täÂÙ‹nôç.uùt#™“¾ŠÖí²¢U¶¶ÄÆ^›|Ê&ƒÇ½ph<„4æ$ÐÉBShÿa úrãM*0#)IVŽi¨ƒû½-èÙMÑî™?º¶À‘˼¤1[•X÷¨“ÿWl eòùƒéé*&/~±ð)]˜˜³‹áô~=:MK|ÒâC÷-æˆF(Ùpg+™jbNU»„˜çElÈ99¹ÂÅü¼ÈÌ÷²dú3öêÖV 8rJf“â40:Gj¨Q+§j|Ó$H~¥“ MÏgþéªÍ…_bÔ‹tßO¶''¥þ䆹ªÑ­KNÂë•òLMrõ)®€Bû#¤µé¼ÓïC‹¥7›/2Pc%òeÝ0CVNúük«TÈ©zòâ†=w2ß'Cp˜ÕU!3ÃÂáÌýNü°ð Þª9ÀÞÌmÇcf«1æ´9Uyrš "GJÚç¯:‰SÑOÕx¢Ø)NíòH…ß2¦í^³Œ7ÿ’—±?,íe§œš#üˆÕ7H ­Ì£ÒUx_þ¾_¾—öf;Ò2 ïy?Ë-ü IÞIöâþXâø}-Ù @/Ï)gSz¬É¸"®£\çä•àk³d³vÉ3¨„ÑF´ÁžHè‡}¼ûhÏö5 B~–âƒ;gqÃösÇBxÃB$V>çù."1{ò'ØQ{Ž}ù‚Ë6)aÊ·B”v_E"_Ò§s9µ:& yëvß.ÿšé¸MH\\ܽW2ZNadéEm…ï Z%ÂévU2ôQ?ßÔaj`úÓÉp¶1ŸTfÂX} u¼Ž’ÓïáœòyI1`ÄOáW º<`>KTst¬oC½RÔÇDt{cÉß5±4ºÏ–~ùó48‹Ïm ¯Z%hè„ØÝ$)ï ³ùi(s‰ÄO'®½ö"fݶO óz55©ÉÆšJ$TiV»tƒ\õT:MÏœüUZ@sô¯@¶e3CŤãoí¥˜¤ÍOµ>jØN¦ÛÏGcžºº]1!Ê2%„ÿ¢ùš+@ë»|ÈS¡2ä´RÆù7÷Ñó£VAf––÷ãü¦º‚žÜšL7~E½´ôÊœf³—ÑÄú™èÏùˆúsU h, ì¶:ÒžÛuÑ¿ƒoFà ‘JºkM:‡BaQ$‰xUÂêN䪼É,ð®dSîæq:gKpLKÓ˜kæ‘Ó„õâ×¥Sèô ì™Ý{ V–²µº6°™“Žç7Õ徨­FwOÄïî˜4|ð¥u¾c@ò'ø8r¡¥5ð9šyXÒ3V¢ä`€#óù®†«PzQŠd~ÝÇ9°g:¹ºmc\Ê{±v¸LU C™ºão/ý¼“RÍÈεXÚšG£v<åØš’Õ¯iU†>LþÔùhìtú*ë;8ÚZ“uN°øßÿ«_‚…7béëiƤ·âV…Ó-$çÔ(KœèI2IåÅ(\y$¥/f’çýŽôýÔ>|ò [=ÊÀàO-Ä“[ðøj85ð’hI ú»’uHn`87jµ¤ÌtF¯ÙS™azoÌЦ% {ZÂ1n±(±+:Œ‰V Hè Cöù+¢0ÜÍOæÔü]8×Çü^zˆUˆ¸‚æó«qÌh;ì\Hfß¾†{¬³°RñÞoÒ£BŽ.$c;¤ǦéÜýÇ_àMÉWìÙg!ôìûbøüD‰@dŸˆƒbÞlXZ£JŸ|Ø–9‰hÚ\ŽÖpmD÷Ú´âÛûKÁqÚ>ꜥ"Bá(Ï|²\WÞÇ(“îʲ·O9VQJNt»ä28¹< ­§”¼zíäZa2Mz Z޳ý',°è‰.Sê⺊)ÑP×ý(½A“.Í.„„§í,l´Èf}yÁø_\a Õ0'û>– sKÍÈùÅðÙó7Œ„ɼ°y1\S†s#<0¿è7Ã{ú)>´Ëa·T0‡ËæáÇâ8þm {ïf7vÿÅïž=l¼Þnf`Ù"ì>\õov3ž±"]çã¿óëU„!P[†~^8‚ûà´ C©ž£wØìßAÙ2éãA2hõ¿éQåÊ%l…þ~òU| ‘µ¤Ž]wPÛFžjdÅb‰4FfHÿ–Fáñ:Iúh¯ŒfÞX?¯ß7AÞ%}§l}y2¯“wçúß¼å󺞠eKšÔʱCìÆü²eÎå[aKÁŒ?* 1›Ê@wÎNØ­·òž0Lådä·ja‚nFwÇ6|ᡱwñõµTl Ù¶ÃØË_§bo¸%<2ÄŽ´3ì*‡ÏÀu8—S³ôÕf(gíÙ{æ èÒ^=b³‹L ±àkl«dê]õÀ·žÄ)'ú8Áe£VÃE².(­¦ZSÞ×®ÈwG† —ÀÝo‘h-NžlkÄ©Y…ÔÕýFH:Áwë?¨ôÓˆædd4[‘/óB¨L^=9¿b=Š8S‰”çÒ$Å[Ÿœ™YOvM¶ÃcFÍœý{{XáÒ™ŒÕ'Çx陥Eì²éÏÐ#¼Ði’éftL<ÆXÉEù”[Ì&‰txª´dâ=§‡]àKÂt¨ÍÇQ¢|yGO´´ÅclÃ3Qû3:Ñ?+œ=¾+¢ø¨Ö+KªZvã èr]IüpÆ ¢'O†{ „Ô¥Dúݷðö©vÜe>ô÷öMpqª AþiŸ/Æè¡ÑIjçk[rn@èìQ4˜óWý¼€%ÙoÙ×÷ÓZ‰°1;'±Gé5~Ob+T’*á¸R}>)›sý?èѧQõŒØ}Ò§ñk‚ãßÇû9›Ùó˜ciÁÓµc—–Ç Qï6[xäWƒæ5– o‹'HTŽ£™ø´ÙG^²é®ê¬à™UämåO°›¤AÕ&ŸIpkÈ[¬*]Æä}Çúñ€»äz!˜–‚{wâ^gØË:q/ÿQÜ]“ OVÇ@ò“×ø}m$¤^9fïXrH’<0€õ-Ó‰Ö -í>‰_{áoÝщúrÜÅø˜ph~/Î8m«eL×çà5çMÔãØ5T:÷ÖgÏcnE¢Å¹ìÜ?)p¨~$7‚Hvóq ÌŒfBÄX/gÖSI‚láOž¬×!Ñ—›¸¢¼)0­8‹[8tŠNKá¾¹¬ÏT,ÿ*ÄfNy…›¯ŸÁ¡®ö(Ï}4_Jñ½>üt&Æä`žœ?É? ŠFR ¸—EæÅ"²Dt:?­„·rà{'Ú5Ò@úõÎ׆ôu3?5.§Ééë¡0ýçO/]Ò8ƒ”;7@èñEÔu4”ZnÌ3 CœîõwTÛ²÷jÕhà–…ä\þVÚÓXÅH¿ÚÍr"Vµ¥¢ä†×\vµ¬ÊÌmfÝþ¾‡yN¸SjïŸø"…DÇUwJ?aÊœJØšîã8ý´:ŒÇÛáùךì;Ñ XpÏw¬'ou¯á"§`[ŽŽ)fÄÁHŽäî2C‹Ý̲§“hÁ½ÏðL|.ûçXE°x|‰HÝÃQÓí¢=ø'ì#çhM=,eÙ—jô´Y/¾½º…žiô…;Q}ð0ç-n[MÛtÁe"R…mMÈöwëHñ’ÉÐg_ÉQ¿«KO¹3w­VÐÝ)±ºÚ’]Þ¶—˜î&SÖ´¨Ïñ§zÇ¡@|6i>±.½ Ø DcOLìÿÖm¬ÿ¼ÓÁö²ùãÛ1+@‰ Ô5‡·ì§z"©¤‰åY-ȯ%Äùöð2Ωê¾ )Òøc%H.¡»gúÁN"èeû€C”9)èº « Ó‡>ÀÁŽ xàY-†#;²HÓç,úóíèΆΩ¼P³Mƒ¶D3tü}"\~_‚ÜŽ(wû6ØÍÞNí®¹â—-ÐݰØŸfgýJclUî:&ŒÂ$¡Ì¿V¶L£Â`2˜z0Ó?–±¡÷pFf]ÞL~× ã«Æqx2u&éˆ}v[œh¾Ò#V¶ç›ë¿š:?0¢*ÊDÜÿNm”¦Ö?Õi\2ˆ_Ç9 j¶l> véBÁÓ¾$«ÿ!äÎ}Ï•édxVð€öû¨ØoM?l‡´·© {Ɇ‚Óènׯ´_¹ƒþEu(ñMž°Ï•‰¨¾3Ù'ÙURELCäa2…ǹ×WƒÂßµ¸:«—sA•¹ÿ¾5OG“÷ÏØ{aû¾ ïñ7à*çËlŽH$¶›>b%•Ø›#ìÙiŸõNô,1ߥÉl ͦ“Çùè¶ BØñ e¥—sÒ5QÅð'Ö¼R¦ìᜭ©ÇQ}[-ž “FG1㑉¿«Ž0{’³-±´Ó‚º&I†‚_Á’Îa°4wÁÇB_Ac#/ž‹$¥ûH¯‘Q=aI¥Ä÷/äÄF=²üâmØ@}9ëN•Ã?½s Å ²IÌ|Ý©ÈÞšFÝ+ Õ6€¨Êü†Û `ÉÎ[ø0M“ÔÞX òE°råGȽƒ7Ÿm¦'ÓÙ¥^aÐ’(Úžý’—°jîFˆêzwÅiÎÈyüÒŠ“ïp¶Ô[á{£VîzÍÙØwUæǽ%…Ü/ýyL{»©mƒö/‹Ðaí·•ÉÑ?‘Ü=³‰Õ~z¡¤+} ¨­·¬ IÀ-·LHëšJ<²G€ $ư{ÙÜ#¹³Y³Y9xxög6ìž cüú4¢À‹¯{Ýg1‡o¶âÏ^%\¸(N)A}©iÔ¤Ú ²~·ÃR|„mǪ0¬Áˆž4u#º×c衊t¶tùJ®A7iß/MgK—²uÖŽ(V! ó?¦s¿¦Œ³öCý¬Ø“(ü;£œµlmÅ×·qË^ŸWƒ°c[ S|>~)¼Áòcxz— ÞÓ˜D$ÿÜÅÌ5GXÖ::{«áeï<¬r—ÕkÉŽlòK° ¶)n€¢¯. ³L–šŽ¯‡£Ÿ–ÑÛ_ $Í—ÚŸ]ŠZ–Dqž7:ª[ÂäU }ã'èªÄíIÝà:è‹®'¢iƶ+8Ùàì Å 2À›ÖNø`I·ö;+z± OëIµÈÉp9щ“¨üäWLlß^Ö÷U,°ŸìàÖ~räLúk¿Áè]JTáS7S¸Ì/·ŠRñ3ÈÓ÷¶¬EÒ šÚ׈ÛN¤‚±ñ)(@ÒÞ|ðâªT”c‘8­¬ã#ϱ*ƒ]`û£¦<¨cþ­íîxO7±y­Düôî·#gñÝ£Rä= ½7<>ž£³FïÞ±`X8šÌœŒ¼€âû®Àãe±gÓbÒòGšïc`’ÜSbÈÄþÔ&)‘¸fÔÍV£nsSÿ÷þ³Ù8íæg¶fÐÝüó±$Ç›ópˆ“——»Ân ïëvL‹…O_å¨qõA ¡È— µzn0&ú¦T_Ešâæ86žßúoT ­Ö\:­7†¹=u4ÒfAoákve}Ù´; Ë”‰×µ!ÈÉ=œ$CdÁ£ÁsÌ]çD˜¥qÝ+Üh 7нQcŒJº`õXê`û¯k9aKO~bOžý‘YO!ˆ_¼pÁkp4T‹î]ãFS+Éœ²UÈöDÀâ/WAkO4M>žÎº);xël:P¿]HÃÃÒb˜hÔNb\c0£žUŕʼÍÊg×3âÜdHw±zÅOK§ŸÅ¨T^âñ7Ú_.Áƒ«öÒ;‡‚ØKR.X–ZÍyºÐž.Zq†ÜôGœ>Ó‘Î""‚q°kU ^ Hå~ÔéÌ×`¯pœNPXRÌê4ÚÒÅK2.à4™ôT¿)yfÇG†¢YvÔ½˜q\1€Z6sï\•!èàåPúí³cGÙ¾Ò-ùbï•4öÆ*êí`A~¾ˆÉcèÚ®ÃÄM——^43¥ƒ<:ty>>LH“ ]òð7ðHNÓgå‚èsë´Ù=‘¹=K2ÃyÈ+‹…Œ°â5¼g–ÇÑ…ôñ×`ÐEzÍÚ™¤döÏÒÀÐm¼pM Wîxbûêi§• o&ãg*hLB½˜¯ÇJ¨Ô€‹q0öÏ»Ž¾IödùG\R¶Îÿ˜NÓxˆ½N'tœšA…žë£½ÎBrêæ úo&ô 1!’}9Y÷ò¦÷4i÷/ôùx¸F÷ö7øÆ_Nï<ÁÀÙÊı+•õ­(§t¸Ò˜¨ÈÊ]Îz{‰Øhæs;.l$OœkèŒõý0Ú ‰ë<©Å«VRx‚Ú|áãl\$DrX’áËáöÑBêþIõ¼Ã¹óFAÝn6Þ½· "w °‹ÕéÜ~oR›Ç…¹KŒhâö·øx“ùÒðŠQÛáE#LňþÓì­°=Ĥû1”8Ði¯âYaídrEuy#@.ÜHb§O%wž*‡ ß®€˜,*¡DBƤËÒøá ÿšý…šJ8"%ôU”ìÞõѱZƒÁyÞK GCäß gö¨Ö"Ü™ÃO$^ÌcçÉÁ߸•ôq]8¹ £Èšð/C£ˆúrÊ}â2lêüw(ƒ^V«C·c{èÂoj„ÕÀ߉Þ}»"©XÕ[ç“HÉYäŠö1è™ÈÏð㬋`ªIVCòu߉ÏÀÕ¾ƒŒÝ0|:ë'2™Å%ɲdʹ>‘["I­l¯›*]^ëõ¯aÀ}&Ô@š¦&µà—[k1ÞlòÅl&ö™x{•”*/ƒçš‹ØA’‡;¯O£ßÔ ÈþÔmßqqÈf/egø^µÀO¾X4~º‡³â-Ü9PÁ3¦0®OÎã³J Zò 6°¬WÕ(³ „j¹¹¤DE¦Ÿ;Ë(J§Æãn}7 ;ÿö£³£áû«Š2WÑbÑ9$ùâ7®ýúõpɈȧmâo áß‹‹%½0Èâ £R þ?ÜÐI{—×½¾ôV0yž‚ÐþCøp‡9Q‘ÒKò§!=VÐ3e“Ù[!™LÿZ_*mµùTÝrÝõú>”îÆ3K¶bëGU¢¢ ñ¡ÓaÈä¼dNqOó·ǹLïd²ð3"¬WË’Y9ªÄî‰*‰È¬Æ-‘aѳó÷î*Žæ0B!žô˜¡.üx”‚öiãPí9Êݯ±Ö‡”É‚ž>fÕ‡DÖr½iWÞMât*Ì“±ëìHm)÷Ìï|fÅÀT†Gµáƒ øsi*þÓjûH¼ÅËjn4{Æ´Ü4ƒ¸ÜC#î>Öò†{)! =f³5ó¶1‹Ò÷ÓÁ:fœ.DK§óÖ%¢¸Œós!»nò¡ñpi#½j«ÈG‹i¨7?rlÕ¸fd“B÷<ìœ5§Æ8 ^ÇÛbIPîBòO3 Þ¶ôÇAUL ¥×Ţȉoü¤ÐÞí¶dä?i“"”—Y‚W|^@³¡i¬ècx™:8òd:ñ"…n=iôeÁNm5…KnúäÞnzÓõT²[‰Ýï:0<+U]@9Kxí-üqƒ$ègS&·ã¾¯„5‹|bÇÈÉ+nd–Ù iäP)õX!‡Âb Èóÿ¿Â·ZrðSŠœ…iò5|ê­Çœãl‡Óî˜Ý ·3¿rü6DPã î~ìݸýŠbW/Ô]o`òg)Òy]ð¹ÝUõâ¹ø‰ÜRfÎOšOW^:H#¤1±§œÈI­tfÖw];®î‡@tÜ‚øÙšÕàˆHÑù§]ÆÖý¿™¦­ªd惛˜[Swâ·ÁÊ¡8TŠ;ÏšúÅ`Ý÷³¸¥§”q\…¡K ÀE€Î[ò.<˜‚Η:9‚YôµJ•«zÄ.ýñ˪ ÕB‡>;žÍ´vG0Y?®3^sÅ`Ωôöc­ÿì|v–= Ž«·KƒN^´Wb š¤4éÇèe³uÄ÷´67NÅŸÌP¦†7c¨ôâ[àfhY³ëqôœlĽWuÉÆåYÌBeMz‰ª@É:;¬9Eë¹] ôä–þnÇuóaVu*¼k? TRˆž0™JL2°òÇÞC{X ±œ£C,>YÓùv오Lþ6*Ó@¹ØáHžÝi g²èÁðÿÌ'ìŸÓØúÄîÜ¿°=T«ƒ‚yµ,;»‹ÜÍ{ËïW#®Q&t’çWœîù–‰:Ç\ýt]gÀb>^\œiJÿÏþ]<«A]íFE\ÅdQ’>ß•v}ø†ÕÚQŒGä1”g×a²ôK´¶ü‰3»ç••b为›œ„ú-6Ü÷‡¿g[:f~f§šÉ‡ڵܦe‹ Yà<4~û†`ïF\6ÅР„Và+/­äÞœàFêV}¬V¹¾É?L½¬¦ǽiÂû\jU†ÏY°æÐ4óó#—}gÊÕÝÜÉb¤{·*1»{ [>FÁöôblº£Å à­A>6™Ò® ÓL¬Ê~r+L•®šfß›XÖ¿DTnýo>uôÆ›+ó–úÏÿÅ>q‹<’Á2”ƒ/k‘7çɰ9knF.9ÿF½žõd±£ Œ}ô€í¡[á>i¥v›D±²wM{û͵‹AÍ{&íx'íÕb¿,Õ¡-C‡hÛæ¹t§ l{tè£ôÇÄ~…Ù>¼š¾šðÙý *"²¹€|û󆊹 Àöþôiû)èP&.¡¦$úÐ*¯è€Æï AãŒ&+.DüÂvþcÏiôâ}¾£r÷ÐÁ’ø¦þ9ˆ¬?‚Ò?ÇAw ‚Ž} bœy@fE+¼¯X@¥×ÂÄþé£ïûI­H”ÝLG‰ºßÌÿÙ_ñ¡¦¥EÒž ‘Ê ¸êcïØu3as8OßC—ÏüöV–d©éF(Æ\ l£I0ó›ýÝ:‰¼ïS‡·´¨â¼pRE…ò}L@Þµ™ løŽMû¶Ž¨¨]CÝ/8{Zžù°ÛÎIÞÏNŽã¯pij´d \{ŠUçZY_ËD¼É$CË2#ˆý8‡É/Ä7ÓEÈáòAfgYìÙw¶õCk›]©¾‚½ìïÃN9ŽS¦©G}°†*ʤ0¯ÃY‹pl“ËÀ¿Êâ°uÖ4¨#MšNà |>§pÿÏþnŸ |x¥LFRåÉ¢šfó±&d÷éc§² ]uP„JÖû‚·ž ü‘ s}BþÈjH[$H÷H%ñ߀š¯Á ³þbæ…NTª–"‰öP(D^OC×Ä´kÖCà‘pX¬ÍGjRŸ`aµ ÑàþtJ•̧õaW(°þÁŽ^EÒ΃”Ôj§òáðì^-Ë׋þ }è…Ù}`¹“—h²»ã—Ó˜Óˆ†ïù0hh%]tk 4zN&.ïŠ3WYü,f÷,lKZ Ñ/ðàGeôzò f– žy<)ã‡#Ë;¹ óŸBÕ¢©äPW%ûî¤7yÛ~Šå4¿YÑ×ob†6f³ÝWrpïïø?û}â¯Bï:mê µ.z¡†ÅòðæqEV¬»š&Êš‘¤éìqÕ5Ü¥"éuO!fAùÿfÒC#±ûL†„ðOÔÑPÆ]1šÉºk‰×Ôli±Öz|}õ~UçG«Žr í©ûTÃÈܾFŽÇÃtö°±6ÙV·‘>.J ñŸá±Ÿ(ÞM®çNŠ 'Cvflƒfñ›þ¥=éÅGp¶|5õÊÂ)sÂq9L7%Ö\ x{rÃRIÉÔø4åLŸG'ìa²Rç8ÞÆˆiÏÈŒ$3Ò×íOî›ãܰú:íØÉýÝÕðŸý¦7­¸¼¯V0`=„JURd”ùËT¢—n&Ò¬××à;Ïa¬ôØI—¬‡}x nxŠÑ‹nQxS6˜HÞ… “_iùòO—llæAR~¹ v2™›g½{ðà¬$ê¬;¯€AY$FðÂñ§˜­MåØñ+–¤' ö¨)Síg …žµ/¤@ äi_•9œ¦C/%±s®ò’ ¡†8Gà-¦YáÍU˜;igö‘{s?¡@‘<ºèó´w=ì¹pÌd¢Âriç³qÈÛC:§g“›ýɸGu/-2ú/þ§ìJ„Zëµ “ý•Í-¨´Z>ñª,Ç+Ÿ„‰æŸ!¶pÙDì~ П]0²k5Üw1Fý™†Ä÷óèóá'Ó8ÁÒÉ-Œç%ºo=ê˜ÈSãÂÉÄè’yÙS ¾Ó£©ÊK.L"°áž¹r¿].PÕæëØØÛ%¹”ã÷:uœfëÞ„çχéWÑ­£ý‰8•mÒ%&ëÈ?ýµÚ´,ŽÏ´+{-"ïÎb ãÒˆi íëCWkqû™Õ _´O-†Lg¬ÖDÓï3‹‘»LŒ –šÑ‡BôŽÑðþ ›Êu7¸;DÄɽIU° =BõåHø–™Ô9Ç–}Û3Ìî;o¿5ˆ¸dÌ·NĆ´,HkÔ…˜¤Cdd錼a@#ÓcafÒ²ý‰0áÏ:€Ja*d¶×iøaƒPó~&tçp_ªqqíô28Ö‚"»É¸H>r+¹üÓ‰õÖfÉ{4¹lNÍ|Mñä«}Ь:|<H£I0 º }›ƒè -Õ6ÿi˜óì–ÃÊÈaft(¯&‚ª—i/¬€´ˆ³½=ÉVå3*L`ó|#Ý„S§Ø“¿ð…qI1ûÏÿõÇba S…qÞoaÏð=v÷¬U *XÍüb¦Àš±qÝäOïwå2ž—èÈíItªØ/fÑ¢* ò•ObÖXLùaH yzÑq|'•^OÒÄžá¼Ørœy¥î|_Iö;r—ð{ÓÇ Ï‘ý¿úÀò|1¹Ã+…žûdé´›eض3–l×-ŸŽ©ôõ¹ïpVk{‚MSÁ½˜ìž‘ÕÄhN]òM€êƲծ ðÆó!ÓðEÜèHP"Ý̵¢»LIñŽlÌIZ;ù$hEÙa˜”ø üÂíÉ«/³Èï¯:°G0•\Ìÿ³?.ó(ß?ŽÍEáDÓ|?m8úˆzî}ŽO¼ö(’Ž^dÉÓ!xé¬@k-$ f1YòQeÏAUKì:¬ëì³Mˆ´N›Dfo›‚‡ºÈúúý`´³\†´aîÞpšr¦ù8ÿ¯+];l:ÕuxC«¶VåÒž9î ¹À.p¼Jbôï± Pe1‰xêË’-¤¨Km=!Gçå|‡ÉTJ/nQÕ"xµ2›1ó û7£š»ÃïÙ°$}n«Ò«ªÊô߸vŒ6’5÷Äé‘ãLÚ:ÌàûŠÎ&kÈÚœ‚ÿú¿[|óa­â v‰ª?›è·~ÉÒu¤fۂ·ZÐyªJûyéHŠhõ¿etü¢ÉãEEÀ—÷/é¶ Ïú)x3ã0ÎÚw›}³ü1¦% ѽé/ !Jˆ”XíQ¼ÌžŒdoÄùcˈ×Ù¥Ðóa„»­1’•UD …•tÙÛ`È.¾ËÌmÛ„ŽßĶi)féKÓsƒWA§ ƒü2> ¦W{ÙúŒp®­ÀÕù' ³8’yVvŠý`2€>“áyúUö·qû7"ì)€îm¨œvŸ)há]ƒḵCNÔ»x¢–¥éÑÖSïþ³ÿE/Þâµà.ñI†ªÎW ÆãBÖæ7Â\AG°óbØß©æÿÓs›ÄÐë—4ÊÔszO£‰ïvj©[î;rXÉ^0òK#ú»Þ‡¯n‡™…?£É­·2$ùn œ«ÖÇÏÊ4¡ú)(5è—úqvgÇ:ÚÎSÉò—UÂÁ¡[pïg"gµÜEÐP{Ìê(¶¢`œ z–^q² ¦îoÄàlÍi(oð 2‹‰ÝÖðCíØJþo4YûTå1¯µÃ¹ië&?<ñ"r¿¤èi…gX«™†d¿à[Swê¼Þ‹>Í‘¥Õóÿ‹ÿ“‹8¾iÌ#ÑCN“7»²ÿf>5i€uÛ©ÐÚŰså%μØX¥ D­cQeÝ6”£%‹6àȦ6•/‰]Íæàà ›Úäâ‰{1ì?íñ¹ô\ØêÂGJ ª¹‡OÇ tƒ$•ÎfÒ§&AîÅoh¸r,vÝÁ*­âô.ã!b™­ðn¦: _â%Æ—²^gœðɮɜ… o`s ð[ësm¾]£¼P®É}z? ¼RpÅ~z5ê ýªa›ãÕ¿³!¹w„i½«…[lßáÚ |,ú­BSæàËÝh¸ P)jÓø>:§Ï««‡[ibÔ7ØïJhrü1FØûwÆãIäYÂŽí2äåmfá¥udÊE/Êß_‰›Í,8‚v³¤$ןe ã’•ö£Q[>aÐêV\qÞOwç—’ÄÞ›Á¹Ó NµúƒQéaôr]Ît¯’'ƒ&°—ZâÜeƒ°pRþ[;|Ò}LX냗Câúu¬&ùŽ¡TéŽâTºÙ¬ܺ¤è^>I·z‚÷zöÚÕ/yáéânœƒ~Ájð€±eÛ7dʺإÓk9Éþ°Þ«0k(®]Jý/þ£-4É}ÝyDâîTúãV VôlÇí.Ðßv©XÖl@×)8`oŸ;ù¾¬ë&A!'áS·(=š¼ŸFöÝ„/FŠT>vIlÿŒ*+ûð¸×Búý¯ô¬÷‡9C穊R5óƒÂ) Wª¹;¦m¹ŠÞG‘Ý3ŸT¦<Çä//Z¾•ÅÑFKú®×§4F€‚S;Û¾›ž¡9dnR }T‘èÏË¥Ujâ¤Èf5¾&{üIùÞa2“¥Ù'ÂÈz>72³I‘Eé,žÛ:Ô?J'ÿl"R'ÉZ+Z®×¢1Qõÿõ?>˹3¢ñmí&”h‚z##t=Ù†¿×—ƒvc)có%:–6CÚg-*¸.7q!Q¶ÂôlPí©DAf)~&MSžÆáûÈÐ2/×ßm ‚üçÚ ìp”‘·ê‚ßp?a3i~ |Ú‰t¼YˆXÜïÀì®ëpÓþÕn4`fÛ¢G•MWŠƒqØNo铇sp®¦ûùñ¥Dakbºá[,NÇoïf~´qõšVÃ’ˆýäWœµsð%dº=)yž@©ø>úk‹#ñé4$‹^¦ÂöŒýðoŽTÏ"QÖæAìñ¿@»}%‹Æ¿}™üòõİ|)Ñ_uÕÔ¶±N3¶²¼&gCä²ÅÈ×Ö¸b–ÍIm—£MRíU2 g”º¢ÄLµ Þ)G2õêqZä§ãÕY dú Ëëò¹ ¶uÈf«ù*¸üê_œ}ú`ÐŒ«Â9ÔZnŒ9óÄ’áo(áòï€Üë1˜¹w»ÖÅRéé¢dNÐjf#{5½¶c¥m"#»Ó›¼L×gÇÝlà—H2w4"_Õn3?œ€îÊÈô— :A§H\½ NW#k¥Ø;³©Jusïö( ‰‡ò5ùÿÅzË4e§Ã&ýU´äók”øÙâô}ª;ùò[dPb¯ý±¸¸‹\²+qZµÚ¦6DáÈ›T"â÷ C¿ú£Ì°ý1~ jÑß ¨íûÕ¸7±Ó¼–\” . %œòPe5÷JÉöÍ-†u•²;K²Qñúrøðž¡ôI º7žƒ_1 ®¸ ÌÌîÁ®IüdÒºíäâaÊLRfÓ†•éÉ‚,Zó¼‚ÚX+¼ñ¦\‡Ý.fœ³œ &ôH#Ó÷ú:4w¯/ï ±Eí=é‹BúOC=ð\ÿÈÑnþhNîÜüÿâ\~ûpM34ÏV¤J§µ‚k%b½Ñ?ñìçZ4Ú×nüj´êÖ8^·y ï¼½HxÕ¼ajKŸ[$ÈcûÿL¢Ó­¨È*}väbSbvw½—$¦§ÄÈéj¸¿EƒNY°l»cYo¤x/€YZ¼Ü¼9Üï2„.}Ú´±m ,¿ Bæq†2†f›êÑ,õ{ Ës0¿æÓ¾ŽkpA¨8dˆAÀÖ,΃êà\cF3Cf¡xÆzÒþ´’éHY];¼iÍßCØäðƒO³M?s]Þ ¢/F8ŽIR„Súßó¿Ï-È·±6Žr‡ kÀÿš^Q“´Ö ØCMbi‘Í>úÇÖ×òs1:ÒFb®æÂ¦§šlç»\ý:ƒ;êÀG¯ªÉ‚qö/h¹.@©6¢É ©\:NU8€ÀëNÎÓÐA8¯ákukàÅ6YúÒTÿÍ‚>8Á7ðß© °«þ0Ù³_‰I´úˆa¶˜làC3¾ÄâV °wßAJË´±-º†ã[w#’ôF†N¬Ïá K‘°ìM¸=}9YåË}ȇÛÞáÝ\¢µX€ÍÜ¡O•šP×Ì5Ï}dʼ®±ÿgÿË›™LüÊÉø<` mZå5‘×ëÈËy]˜·ÃÂõ72çÓ†˜’ð•Á/° °¦ªÎëq~`ž*Y@6NàÞ®¾zø¸ªŽÝ´nä:Ùâ&»wP×kÄÆ'e¡ÿGSnH•.`>05:)(ráóÙÛ ~}_ÅÎ}]z)À«8G¬q¦èœ¶d9Xžó/£Ì¦A·s—Á¯Q“qüaAxX’ЀA~>ð< Ö÷@`˜8Ië¹Çœ^_mÄ…„u±†Ývøb}:äeåC“\&{»i¨Ô}ƒ³ÏrýÏiörg„üf&êEÜùúÈ 7ñ…í¸Gp9êÌbw¬i‘8¸ tÓÏ]öN~þ,Èþ^·šÿž‡ bSF`J1@ž5î´}]¯§!ß iè-·â:ª7câ›8öd¢ ûqÏTâ,Ñ‚+„iUOÈ9äC_Öwô1K ]Ç^ãÚ*/üžT‰»WŽ`y¶ lI †7?Ë BØÌ#LHØI²ùÈlÔ¯œMŠ3àoàMô]v–•ŠÝŽÛN«MÜk'´¥W3S·ÁÍä °ýöÚÝõjŸ©0LýaœÝ¹·ý-bÅç(£ÿìzœž´Ì|äiÆÔðÿâÿê>¬î(‡=Úåø¾4 äÖŠÒ€ÆFÆtóTfwTT ?®ñyÇ6½éÀ#N÷™ÔõƤq(n\-…σ‚$õÄ<ÎÜÕ6ð#0%S§âiµp5ö:Tˆè£V¸˜®íwR¹3‡Ì½Ô‡ ±Ý¬™|öåôpn8†´´·­`îäf±¯’§³ÑßÝq´å.N¹“‰Û§¿äΫÇ÷û—â–Í»!Êb-–¾Šæ ¡f$ñ€tÚ=fuÒ*NnJl€ÛL3òÞÕL!êß ±Ñ‘œŠgß[8:‹0µD‰iµ§¯2hyï"N»ÛlŽÁý½ÿù?°­-ˆ1ìêР£ÑðM HÊä!7WŠœï8…cNxùÁrâ/×ÍênÜ‹ó(SûSqp¾N•F 8Y5‡¶í·&Cé0ëø8X¼ÆÙ„Ÿäz±úL9èš+2z+=è"c~Mc¾{ƒªåuìÉÝ÷Ø÷›W!oS4lžÀ.î|ÈÖfr¤¾¨‚¹Ë7ŽÏ‰wœÉGŽ1eÒÊ´|2Œ00UÔŠvð?…¸-¦lë¢jük}ã<*JÝç¢ÚIú°™‰l+ÁåñÕXG³0kÿ_fø` <ÛIïnÈÇ΃uìŽ@»©óÏ¿«–ÙcÁq2±}TÓ:ÌòŽiѺê!´;½‰<¥ÄVÖ7/CV¶V°M 3ýM2n¼s^Lr'×÷?ctK$¨ï“X²Àˆðêô |@¸eT2çdÚÁ'ŧp¯bpñ„}Ò†È/ÄcñZ§(Í1°¶'6Û¸UBw¿P©žýT$»Õw]d »»@ee ´>›L:\ @•?€ÎüFè¿ùÐÉdÙ¹Lt½ÏC”¿çá“’ ¦-݆ª Ð YPü>c%{½î(-² ’™îÌŒk$g¿Ñö.Âí‹2!j÷´ÿúŸ/2Óè<×aîé)blô 9Xhô§ÄçÀÃàs(pë.!"âÙŽÏmÀÚí!+vÛ¾pÇ/Ï!£"½œeù.PÐsnÞ\ŒyÇŽ³ÕU€ýÆÄÊ/ ³ qªIùž‚òzÛ!ݵC‡¼¡5Z‘VèO‡Ø& òÈxYñFý‘þ¼ûlAU Ž~M€(ñóìÓ€“X§gB'/Ka×Ì [UÏÑ-oÕÒ%÷ç\gV¹¦Pδ0+áv4¸ï0ë^ȳâÔí— íÒƒÛmÄHÖ2^Üú2” qÝFìømÉæ5ÿÅÿ ËY–—Çô³˜¥ì"ØC£…ŒñrIÒÙ|Ú²d‚·g„ŠǾ1t[R@â4î2û?Êà|멨Üõ—ÕVI€C?4˜_35ˆ‹ 5Ž7È`‚HX9R¯7ö¤Ø ¨¹Á1=õ”{Ô´nê´H}_·Íߣªæ Ø5/îϺí&UÍ¿^Îà0!;Aáúg<3¸žn¸Æð Õ!_Ô¼˜ÏÏ…m¿•i—x2|í· Ú£Q˜åû?~„cöQÌ­uõ0ïÚuXœëfZÁHÆqdÏ‚¿60‡Œõ‰1Z;<™©O9ÿ៙¦0säÓFfS%MQ'ëÉ3 è†àˆhÎO¯Ë “Žê›„cåaÞÖdA+t5ý:ˆtëÁÞjاTóµéƒÛ¶`#s™£¹ë&L;ƒ£?’0Æð¦PJM5¿¯cÄØY÷Õ">ÛÝhª„±OÑ%fe &ªF¤§‚ß[óA°«¹ÓŽáŠŒùÈy®I.ß„ã·gÀ»Ç Ø_u.­ÛvÛÙÒO@úÍTö–1=ôb>½s¬ø¥ßƒ`ûð7b†ÀmƒF872…zŽþÀÝÆ3þ³CµÃÿ´Ç‹7í'åãƒØì&EÌÞqÙÇY5ɹÚÌžô_ »Ü¿héÂðC~¶mDŸœoJ#¼+¥èrÏ™˜®Ú œö.L¼Ÿ‡ê÷ߢür†šõ½kãpji :K˜\cú¹³F+pnn³!½Æ´¾`dÅWdF#QvY2=eLŒÍ¬QO'šÜ1™:?8FöGÑ‘…áôÕZeÂãÞ äÄoì7‘"Þ^*,‘øÄæ/79n&糟áQ31»À„y!Y5(Ï Óº‹§Í–ÂÌ\^¼ç´‹Bž(#%O>Þú¯ÿaš€{ÄÞ ÆÔyiÂ-rU·’|ºXî¸5s¨ø~Ë‹-¾XàJÝs¢ðáb”8;=½(4zˆ«Ù'X+ûØ|=tZVmë(¼{wY7Z„¢"-à½ø[u` ³;ò2J׫8í›7¼Új´£öú”§Â»‹êÿåÿÅúãÜï<<ÀmcN/î‚í—ÖÃ⦫¸*1–5>•¥ÖwPé¹?¸“·lçá§¥G?ì4x<ˆÃ²êø®ÖŒ]RUñp¾€Úç5ÿÓV£|ì^! ›5œyø^ø•âxå`5öº_eš.&ßþ2TjÿÍk†¦_¸ÿ´Ówà«»ak¸ø7σ½u¡Ê-\ðyÄ.¬VÛÁüTö¥Œââ¬áÆ}cê:)Ì'½ÁqM-ð.çÖZÃj¿UXþ,Þ\‚¥µx×f#üôw¢Žj4R7š}»½4¿¬ü¯þV–0†U¥"Fx÷FѪkËÑÅö6|ïíçæ=çÇûÒÕ¨pj)&¹¿†ï&¦Ünó-å0šŽ¿Ça ¡>ˆ¥ÚË1ç‘>~~mwr  åˆ:ùÌ+*Eˆý”"6å‰!‰(&LjÊ#¼´‚}¢pa‚=£)ÛÄ\­Õ¢Þãlâ&|ãÏp-R؃o]H+ï:²¬õyUÃñ&_¼þMžYk’–Êg¨7ɪ?}ÈèË|„£œUtܸ¿j5ëy[†ÄY‘³ÅWX³—é8_w[ `ÆøjÊ$ì}9¢¬8‡žÕý¯ÿÓrNƒ·ÛŸ³<&¿ðœá 3ã¨>-÷̓¼m¯ðùwê]PÂþî ‘)Tþá,RimÃAP¯êŠNwÃÙ·’–4xyç/-.<}›3y8‚ÎÚm‰{wðŽn4*gßV LŒŠbìÜÿò¿ô\/³Cî8ì½ZŽcïœØ½¦+±hýÆIý`>I¾"ŠFê#ÅѦ6¿§±pf:ÉÓ Ã,SzáÎ[üðÑǶû È——?!¦çnœq &÷žÃT¾ù8é±³f†ù)…S¶Ç3>•u8uŠ+}²ìÆš´ƒhæcÔO¾ŽíI|´dGžôó'é|¨ãÅ-Á"ôúÙ|Ø»ç÷H¼ 5›ã@æE£ìkS²bÑ:Êï)Äȉ%ɲ8¾¯õlÆ[“éþ`q0,‡ç–óé̓i£ •³‘3Å^—Þš™ðŸýгñì›2Ö—là\EÚãR­Ž¤lþqðUâÅÐY8Õ:†qÿ²‹¹d¼› xrjº­AûÈKè‹â%×3Ñ©¿Q¯Õâô‚®¹óxsÊbã÷Ú”yz1è O'Rü ÉÕÇ(š­Á,þ¬Cò¶<Ä© _ÙÌ›>ä”ôCœY]'øÖq.¥¬¡×=n¡zH%<÷:@ù•TÈØŽ]»Ù‘˜+maºÌ%·ïjÁÞ…W¨öÌ{`ã¼ã-¸qÎ;”œŠ¡«Ï0»ÇÞ³ª÷K°}L› ϧýîí`§kIœ¹‡™äoÆ8[¦ž=_ŸÂÉ{¿ŽMþVÁ=V¾CôëZ&‡(Si­/ŒBŸø¯ÙA43öBêÈ'ÆQ.ôOkÃÞJföØ”*ìSÝMÌ×Jûó—¥ ™øKÚðGö¯Â|áµðÐÆ›yб5tc>|_@•߈Ó_º‚ ÏÅR&ÿí &ÛŠÖnÿç³ÛÙ°<9 µSŽãÂÀX¶ÿ'„ã9¤y1ÄÀB˜úÅ$£ä] xÎAwñ&‚ÒÛí°ƒçêÛ-¥y¯- bž5»Í†¦=Ì~ÚÏŠìØ·y;»“Ìò+%sG–š3³ÚWcŒš¬Úõ~MôÁ…¼0¾F$î9;Áú|l»qŽ)x{”å»lÂvÇBîšV0-„¯Ué‰O«`œiBÿisá]5žÔ oþdpÌXJ-½tè¿9Ï÷mz9ß|„Íþ£è”~ÊË>ÁwŸDxUø‹7٪ťpƒõr!;V…˜þ²ýø¯oż<ÿ†Õ.í‡âÂ0uщœ–¤ó¾î%á)3¨ÍÑÓôlö ¬í4&ûCŽd%³4Ò.þ’’†ä§önÆ,ürê>È&ä‘C+øˆÅõÃ`{¦–y¼H”nð ¡»“…¥šÄóíDÿ1³’.qIÇÉsB!Íg*Ú…c›yØ’+Å4/B7±ç¾ågs 껩ŒñÏ^ÇíZÄDÃz‹VØ}!œöd¿ ‘gŸvÈá³I•Ì+žXægÕd<8¬À8U/"n7Á¹\Œä•›Ò¯x迹Ó3›¾ƒKñD߈Ÿ¸‡qñIõZfn}bÞøEPCû5Ø]MFsÁópDF6t˜Qç`Ý9=S¨Cá8rÏM!­¯õX[æ3ӵĄˆÅäÒ˜‘$æ¡— #ctr¶€ÛÉhØàªa{1©Ðž[þ@•¹â¨JdÅ·þ÷ýÇ­×1¬îTq=?†¯Í ©n¾gaDºÇä¨Më":¾N ·*ïAEkÑ…ï(»pÖFvª½ Uä+Æ ›2ùvè§A ¾™2uy—ôïþ½F]m4qñ‡óà~½–§ŠcÌSgzTø)诲¥šŽÓ>ÊF_ !wî‰2-b¥4Àä!»À8†Ý XÉüQ•„Q…餺J»×P£€jfµôeÎw‡\¥Mü¿T3²òZtŠÒ.b=]‡Ø>)¥ßÚ–sBét<ÐXľðûÁäýŒ%_pz€·Ä—à+ê@3óHð½`Ò+±ë?ÿ/ðÞÊ^}ÝÆ†—‡wªæ¤Ê} &{=`¥Oçàë‹ù(nÚÂÚùǵGÃŒW6síQj—í èþEßNô³ì¬ mMãøè"Gþcê¿ö¤Ó„Iù@.pR$Z78tn¬Á¿wÙEa÷!žS+HM~ L7Š¡—mÓð˜·?<3ÇÒŸãÃUe0uËJÚ.¿ˆ4Š|…®ßØË pú¶‡²g½r1ÇHŽtœ®c#ÉNå•D¹d)fŠÞáxl¯ zžá€~4µ'³ŠNq‡ïbP±ëbíIùÕ–Î¥G!úÍÊÿò¿é}ŒÓ6ÁµÌ6Þ$Òº›¹ïŽ~eåâÏ“KÖ…ømÛgª¶(‚<ÚUCΩCJ>²Ì¤–Ûd17üsYã²Íiõ>rNž¡k|4¡Y2žˆlÅ/‹EiœR1h Z`cÁUV… v›âØsÿé®—-Áf˜ÁÌ®h·ª§LR _.Òõo—A+aªø%vº4Ãü¼É˜èÙÝ4†ªè–Ò¢£ñÔ¾ ›˜k³›%¨Eîb¸*B™{ kФ›bφP:þ‰,¤B§×¾cžÆ°%“?ÂúâŽÿðÿ¢x$:¬ †™+0+ˆ’šMÌïÀ%mðÁõZÜø+ßî{ÇjÜÁÜ·ðÀ±‡ðF})úÚˆWò+.É%, WC~¡}Õè„£päú1:mA~åK¦*H¾ÊfªÃXÛ°•9‰—f™‡@îLit'÷?fŸ¼Þ‚…)¿¶ôyy´Æâ6ðOÃÌ™ãPoJöá{cGf»4?]^Âàeáå üè#så"ž=} TŒÕ1ôò–¿3“Uý:¥^pDn'ªü¬Ç®N{ò¥å6ºø=‡ìºMÌÍϹhÙ¸”p´~:ýŸý?êV¢ZÉ$Ì–¦|rg˜Û–ƒc°ùp[šÚ´)’› °•o)þ´‰N™® JCz\åˆNÎÀ)â>e*z½Ä©Šd8x*v•FãõîX›¦ŽOaïÞ:1ñ‡ŠT8ä2l˜ËM9áÄùÆ„«öøâÂ'C w÷4ÙùÐ}ùÖTÛ*V!Ý>ÙhCÀ7ƒŸ]m>GqŒ»×Ujz?«/|Cõ½‹i@¥ ­Ü3…êdÝB¯G;‰‚ÂAzî‰Ù HÇ£ãxG3’v»oC“sj¸dÕi4H“¥Ûo†ÜàAæÝjÿðÏuJ¾6‘¢³Å¥@zäÚ"Eä§áTn0†|kä¶½Þ –I#¸¿o˜Iw»o‹Â¨¤xLË« ¯J%À«å"ÞÊ‹GÝGÇÐÞs/Ýܰ‡yyò*|>£Â¶»“[†ìçì¢%(ƒ¿¼ ˆäýÉlÙp [˜!Òxæ[+.¸÷œù¶*Ú¦}‡®ìåëÚ;´á!~ÄÀ…Y{aÉú;ø®WŒVl™†ÅÈÏ@"Þ.INrªQ£Úü¬<ÉUOˆn”&Òû± ô ‘?&²§N²AQ¦ìɾխ†[·Y&ëõþ‹ÿ‡‹nÀ{K ®‰t݉»ö¼ÄÏÏÕÉ©kndĘ߹ÔNŠQé-ænz—@Ç£Þ0*ïëñ–ÉMHm¢óÖzÒš}Í4®:œ,mQ ë9äÀ c –^ˆR2åô`ð2â;u þŠ"¹:õ r¾î–ö`ê÷rÇ(’tÿzÑ÷]±nI G¶Jž•S>s¶U`‰v9û6;ž–«ªÐ­}ׇÏ>àq.Ô÷.£jCGP¡ä ô½·ÁOqÊ«vùß^:ùyÉÈ×xÚØ‚ú…¾"%7S±|·%Ìá8NÏ¡U£Œ‰æ>|çgOì>xþ‡ßÏj1áÓ\¯ŸÅp``#M˜éBÜØ«{ÉkŸ{(ìSÈ üñ„çßf«øú1öèc"òÑŠ†1¤³ ¿%üÒúŸ²4øö]f‰ÜÑ©žÄ,ytÄ(Q×¥iÌò>úƒÉ$ï¢wÓ§§T¡II ~Þ…>q?™48>CÍ€6píZ„÷·JQÛ×~h#»k«EénghIv¡]ŠtåÁz\32‚$v:ÞÐÕÔTnÝ3%ØT,Av9 ž½Ö4’Û‰sSâ:/ÁÙ½"¤†æ3UÅÀiK9ô4zPsÛ8ö…$ñý?ý»uYW0À,ß›Î^Ð{Ã7PçGÓHâŽAŒP—… U:”p†5»Á,в$/¾paƒ:ż¼‡Ì¦ùÎôÇšiÈêÅBÞôòêãA&ëÙÛ©…ò›å˜”Ú?¸Ä!Þ2阙"AçÄÄQíK·ÉæíaD¥eíUS&î¹j4†¯”\*±¥ËÏ rË6P'‘]4Þ*–Üù’~]‡A²y9ù²]8ÛοQ"´Èó(­°bÈç¡vHˆ=Ž~¾Ê‘Á<¬rw<ò3;{bØ}çÍé½þ³0SO <ñsç0+·¥Næø/ÿûx”K 0Ó«†ƒOŠq›l#Nûñéx2=[ P4„* KÏÛé³u‚ÔÅb/‰6ÈáL?, ï×ï‡ü¬é$î¹,-|Å-²#k·Ö"[².Æ}Ь®–åGìÈ"å mìA°à Û9* )¶d¶Þ+v}ÇGF¯ÇŠŠê3N.¥ ¿år³W…LyÊCÏA ܨ1¦ï­2`õœç ;(FöÕÞáÊ5§áaŸ.F¬ò<™·‚œ+kƒüŒ 67?Ömà#³/ À*•i¨™1Àúh솧]àÞdeˆY¼‚êUöãl½U…*ÿùê"cŒJ=†v+dÁs±7Éx—A^¬O&ч³ñzK0ÙýØ*wòÐìÞ(â,bIX…)Ë[ÉÔÙæytÑaD–ï>+íd{®ÚÐX~>òts ™y_„,u”"}Ò=xíÉZ¾€9(GQ1³¶‡¢Ÿ¶ÓªíÌ[ç|ˆÛ~ 2yéØóLjüI•ÌíqÀïO°I¸.m#‡è;u/bÝu þz¿ffáƒÕ‡HÀv{îÅ?¯IƒÿIØi~‚\:gHÜÊÂ\)_ª`»¬´X Yâ•ÜÌ%ã°Â×”½ž 5)²ìYÜÿú¿Œs¿Áva.ž$Âb7䇡3[âH\ìgô9þ¬Å@‰ê,êY1›6ÖŽ¡Z¨%í ÚI®“7$²³”þ5 ¦ú»waŽF8¾ä„{NÔo¦ï›¼ü"GŽ˜XÑ?¢`¡ða:°Ê“,ïÿƒÓ2³hÁË:ÈHD~ú­ñ<9$+‹n·dȶᤣ§HР>ä &ÇA[Þ*ýŠ#2¡KÁLYõö‡ŠóŽÐm·€ì°ñ"9—¦nÊ[è®û…+¹Úôè¨1œ˜c‚}éa!sR1¤‚ýú‘ ãÀ¬VÔÇÉäçÏâ–ƒŸ(ê:o¢­–˜.žŠ?“éYoš)BÜ>\ {Þï€w…Èž]5°÷·5=µ)<6¹QW­L,͉ Û›£hï¯4X2ms¯Û›\Mˆ¹†äœW ¹Ÿê˜…BdÆ W¯’õ,¥½•—D5ÏžcÚ ãÈ:zl#Ä/¼T¯¥@Sndà>ceº{ù˜ií#Ë58?,Cö¾y‡þEÉéOGA™ÇJV»3m.í¸Ý}1®šâÍþˆ¸riíïhvO<4Ijc¸×L+œ]ñ[~¯µ¡V^ àðàÿ=ÿû ‡Jɸ*È›w ²ž.$"æ±ôô†TZVõ—ýЄ;ßÁ2O#rÊ¢„´nÛkV®`8OOLP€Fì¿’ˆ;™pH`-Ó1g»Ó–cüÀ`C‡XFp;È…a­cŽá·N|)‘ËôU;cCP<9øÙ‚n/€~÷e¨?>—‹S/ùu¨¥³'g5³sýpz̯³Y*6J:zp¥Mƒ±{—¨âÜwG›ØhA2ZkL nÍABþL"Š]Ç©fl2~v©¡E¦w™<³DîC'3ãÇ=Íq¥Õ²¢ÿÅ¥æ0»ØGŠ: E`ÜÝ"DÜ*"qtk¬_ȃÉ<šøEåk‹Ý )ºtPŒú¿å¡k¯G“ºóz°…oYõUŠnÌ5§›ª&‘i*ÍxdR³b‹6Ž_ÆÙ6@JõYæþ{k8ë[B#¼«˜Ž£À·jt-¹ÌÆk`“,D wù"ßú«ðº4<ÜFŠŒM0Ì .n~Ìü¨§±—‹¡YQ˜ú/s¤›ôž!7*oñíc6ne½…©Mí~•íf&8ç—Jþ²;ù¶Àº©+Ø/*Û ¿)‡CÁyÓ¦RM ž]ûÏÿç^ÀU~GÙýç3É3ÿt”­£dÊdí®Â¶cgà‹þ1ä?ÎKti²zÉ5 # pÃôN^aκ`ÅüüsÛ£yZફý¡{¿*f°[\®`”Hæê7`XÛ/Ü>Ý—~š*Šwz|áÜüF–÷Åû™•M¡œöð›4ù¹9ùm »rk±(r6nÔÚƒ y8ìÃcc¨‘,‚ÞrÖ¨¬ºŠ¨­þ6Tˆ‹¯¤o¶Í¿'³ÙlOKöå“T¦€CÂrEHÅg)|;z‡úLHdÐyÈ>ð<|ËLÁ÷œ×å®ÿ}ÿÙ"3 Ç[oq–‰l%i_´iP‡ºÛ†7{˜Äïâtm§)¥'Cì@~W™¤Af{‡‘¥ëJѸDSÌ’à‹z3;»)œä”1Ô6õ<}ýÅ“~z!Kw—°«ŒRhËR#Øô€CuŸhÐÅüˉ€²;[_ÝMµXòÛˆ„Z¡lܹ/¹!¥Äö$ ’쀇)¡Ôk6û ´‚­WCy2™Ô¯E¾ØþD½uTUÏ÷>Nw7JJ+ ˆÔ={PÁÂF[Á@QƒnIiPAJP°îìÁ@°0ƒ°,l°¾¾>¿õãýë²î™sæLcíaɲlrs“¯yÇŸƒ@[cuV#ß›2IV_ >'Ƹ ›¹éþ7YX],’gßh6»²ør˜ÜöãÚ³€_¶ÄìAj¬þ£>€Ê—ð€N™,üO‡w Ñm‡•¹C˜éi ꛄ™ã"hëÙDÖðÎräÌ3š¤ÄBƒpóGgÒë¥ÆL—æcÁí턯¡MþžrCîÖþ¹x–çì×±ëòÁ˜×E•'œm/Øy»éŽú ñ³×àW’yQØt?|ë–Ó^À¿O\•®Bv÷|Ça2 #l˜N’ [^p“+þ§EòCû©gGTIžAá;Ö4!ò!]vS¨Taƒ÷ Pçá_ä aæVØþJÀÙAB.½SŽ$ƒá?Ý}=g:»ã%îqälÞ%lRƒÎM¸ïW&ù܇ɥ ¤ýHú{81Û]3ð™uMŠ$âmo¡á~5Þi©CËM4qÚ<ˆ›²”ÅV/Áwû¡ç…™´páÿíyå|ÒÂŽì(Ä7«ÔÉåéìÖ®Htñ•ÆÑÛÆÂ¡:œÜZq–´Hç%h¶ç‘†y|PmÁV•{aû¤wük}ÕÜ›VV{`›{õ"šú쀴óqØ£—Õø8N“Uùm$5qØÑ¬ ² â¤ôÓ{üY/Ï·Ù²Žî`÷ˉéþ »Ïý…X‹•8Ù7“ S‹ÂáÖ¯0!x±‰(å6ç£éMo6×È’¸Þ7'ï/Ÿ#ízcàÄ£!Œ›l9Òï¿ ­6~Ç[Q áÑ–! NlSd1œ¿-N*aùíáñ&Šeª#øßíÔBOÝ4îQ¨-Ï5ĈDÒ‹tàûzØ·—+²ÏÒ/é§÷Bìßu:Ws4±Ùø›÷IÙ€ü]‘Í9Í&a¿SIF¨§¼ì¾y¯I¦Š9‘¸%6önTÈ]Ljþ7°Ò÷"ä¬-"›‹_ó2$mGùt§X:ÜüßþߢäRØ8Û»¼ýÙ„YÑ`QK^<.´£ôBðZ+G®dưœñ¬† ƒ±£1ŠAÝ*³ù²õ 3ºà—° úØéó,Xõz´/ÆPžçDŽLfùYÇÀ»±¸?ëàƒâˆÍw‚¶{ìôÓtØœáJP‹ ¼$4ˆ‰‰KÈ÷£ðTŸ;á´=Œ„˜dz&§xu<1ÌKä÷ë`\ù;Zê"@¦ÄµsQŠÉyM`šÏŽÂóM?àçP.‹ìã2%4X^âC<úˆ@æ“?˜›—Ï©ëŒÉù<`ªæ#ë?‚• °šwZ^ë»oÞÜ襓Ѥºò®Ãü”G°ðÂ\2£¾´Kº9Í£¸lïQÆC].:/ =ÞÊ;ë4pÚ¼—-©‡éòD|”±•Ò!ÊqŽ$ä^(®ë¤Kf4ð+0«9uøã‹çY!cû/cŸH þ&¢\`ú]䂎S½}ð¦Bžœö ×–„âÅ_§i”µxZ@x¬‘SÚD/' ÿÚUŒÿ z¥ÞsÃLXès¨áõˆ†zÝçú‡ÖP“:Kf™õ –=ß ¿Å:ðüËcwÙ”ÄxðQr±ÿ¼/é8Ê™³ÏBŒð,P›lÄ;’Pß»#m@L's$%üš/qšZÞ£˜¦0KΓÁÝyYøá¾ú¿Ÿ,þ¶+tÛxçj7cœ™c:ƒÊ7€ÛvWV#ãÏ`U âÜ÷ÛPú»(Ûw¤xáǨWÊ’{»˜kŸÎ•Ml„Ûa ¶(ûç5œ¹ìÅÞôr³¸2Ïù0k¨†;œû ú#µˆÅä\¬«7¤/oñn>Ž5¥ð%n"»â"ÉÜVmÁ‹G”ØðiuRèô—öæ¢ÚAgvŸÖ€¨“žßFðKÀôªÚÆS] ´ÐfúœRèCö 8›‰AÞ‹$.µ›ãüúf°$ù…X5ô ,†ÅaaI äüNG?³V„u>Íøð‡3ù’¯Æf}©‚-c°Ìø7ïÜÊs ™öÁ)7)•3\íDuм`ÇÚÊB¢`ÝØ5D4ð|½öÖùëµÍ¼4e¼Ç­äÜËŠñï§o Oxƒ4AM€t»„w5ðóÓ!Œµ¤R‡¢h}ü¾?%_$pùR±È5˱­6“iòºkœ¡Jÿº8†&۷¬Ð*zç³ÓÚK'åp.d$ÿx„¹ñ)Ž˜uºð¾!¶(m»Ä ÈÔK°û SÌÈv¹§h{83Ê—ÓñnšxwñÞì¸ú=[N<™;Õ`òé§¹Yš;1-ä_^¨ƒÏïÀÖ€,|!fÇÜg’¢Am¦ÿd) k¿q+7‘·.‘˜sLŸlÐ('‡¬YÙù lLð::R­{*é¥9Ö{Ô” EõrÁÊn@ÒàéÜ9\ÙDçò')£€«ÒÇ)ÇÁ´ð/>5¹€íY‰àÛl@¤Ï¬&ÊÞ€¶ìOn{‚(Yëy~6Ì„û3UÐØùîˆþ‰x0Öù d¼^A@rT¼½«:í£`£A8Ù1V•\é˜ÀÛ”‹»×G0ë¼g ÙªÆÉDS¿z÷l‚"šP¥YDÅ>Fѳêqü¯›sñ'£¸Û_â«ßªäÈ¿Ì-¼Ë‘äp ~ᬖjl¿'ëaÿé"nú-YXü ƒëDé ×G¸H¸ „>æÂ[±Õ$wÞàK~2³h ¥O~Q~¾Ž{8•j§àØðR¢=~·Ä_¾á>}fÚéhw¨=¯¦ã©ÌJ8ìý n ü…“ù‚4õÈ[ºßF¤_µBÕïèx§ßÏLfÑ'GQÕR5Fo^ó®s´°Ði„ÿ’dx˜|ü¼s弊ašÌ"þÕÍü” O0!Ê "ÍÈ] £ck câRrëÎ'¸\Ž{n»3…Æßxáe.ýº‹s]s ÕwëÒ('5%ÜNÅì#È‹l öL!Æ6L Ovå3ÕfMrjï#øþäH/Ž$Û"Ä6Š~Èñ"]¤›cH6?„QÛãò³‚úƒ8G.>:ËßXáÞÁ*@ âj‘±¬Šôpµ!hõu[ ”»£áÉWX‘dÎ]¾#J…¿ÆÁ^óýÊ1 =ÍŽsv@¤L?/3–`j¤ ûTiDäÆ\…,hœ6t•uq÷g7Bϳ~›W޶~”‰aN›MÅYcÌØïZîm§ùÝw}ï¤aoÆ®ï='åÓ[=âäÙ«ï h/†¥‰átªø2n÷6E˜—UQ‡ $~LÝÓí8Ƽ´#£íHäº ¸°­W?u†ËM˜ÌU;c—ƒ¸ÄoË ï—«?¹…\§ùôÛ¶#8\SÅÓŒ„lWh£â‚5ðùìi^šñj8YÛÇX¢>K1¾T Ã?ðß÷G¡¼¡ÂÐlA̲îÂÅy,‰{E¾Uó˧œ€Aù2¥Ý–¼°½‰mÆØç=¶Lm¥8q¸4,þš0ß›/Áe[ú×­bBÅ póI×| {an£3ù¥) AËæÁo‘t"kê—Ö±¥ó>¢Ä¼KbfÏDËž©ÚÃ¥ëÿà*V³_fSpyûÔ~×˿ߡ6 Û!¶ñ/í÷ ç¥à>³w?‡Ñ÷qÖ®h zÒÔç®–O𕟪ñÑS@“ þÍO_-_xþ]g¶’ΠMÀ|Væ1zÿ˜`'gÙ®fXÃNñ”¯Ü§AàÃÇ6P:„î*tÕLsXº\¯íH€}>þ›ÃˆPôG*$ׂ•®øÍÜ#ú¢` ævÓI7/B»“6Yâ¯ÂId†sŸ×ï…ðµqh™¯Ëö¯3ÅÓ~ÓI6Qxchæ~‚mkXÉÓzä­“‡&M8Q9ýWZ0“‡FÄìÉ6ìÈ™ÉZš=Y Že1ý[áMU÷Ìû ÷ÐìÞhNç¶7^à×ì¢úG²Ð L *:—{w`- ¬(‚ùs\É­«çPh¦ ³w†íÊÒаšò²MFü?QƋӫ ÏÆ¦Á±ËqœQœ"lG{¦ù4‘,—3†Óúh÷;-þ–<)öI; C¯ª³²»ãp߀<ëØ¿ÑyÖ3 ž½–DùŽÇÁ_Q|0…g°è.߇0çG­YDpê9š¾5ým Œ÷ÿß<žŒ#{5ù˜I›ùãÂ"g¢½+Ežöâ±™}pÞfЙz{³Îòý¢~ŸÏ_a)=s¥0#Ù×¾ „‡ë6pwra骧pô:h_>€ žMå<ø„ .8 —?*2_Ö$#Àne%Föq¼ù7ÆœŠ Õì1’ÿü2æã€m·k‚#Mw@°×s`—C޲3ÇÓEÌðl ]s™ÎñN‚D1#&xĆ;#ò(>áéuÃûȸ«ýžoÊçÏúxÏÚecpMÕP䱫Iåh¾È€Æï÷î•lׄ‹ëaGä5-˜ûšÃÜy^6L»aN&ü¾€.|îañVÊ­Ý@–‘à ò©Rj7aµ¨;ìdƒ*¡óȦSfLJcÛP)w{sVg27·ÃT|Û\Z_;3 qídIœ´‚dq'YO·~¾oL‘ögN¨jkAlCŽø¿BR3ê ?¹‹r±ðŒ¯ïŒ=vÞõ%üð©Ó\û³gxñ~äù¯Àß?øÜâÖ¿øø@5HË„Sv*¼·TaŸ OÁ÷ö\´J:±­*…×±{ª'©öV&…¿‹Ñ\Ú•,õ<„—žJ“ùUBtc]ý©= Sû2a£¡ ¶´ i„/7{z ¯ës wÃ2L³KñƒL-Ô{j2iÉv Y3øcÀqçå†rñXsB &ßbŸŸìày+²­¦¦¼_ÍÉd߀Ü燱òJ D‰þDñÓXÔ“ beMŒeŒà§ªZäq#~Šà&õœÇõ-¼á”(Κ̊2îU-=Û™NΊ= “VŠ‘»gÉ|%–øÃ‚;{é5ð¾=Ãú#Bìyš “[Î ²r1TCu<‰ýe¶ÃEšìÉýƒwTšáeY n?šGÄ9Aà¸p}=j-Éswà`ùg8Ý ÂnJ& öÄ×÷œ"Išœóh"YÄž8Ç1ÍÂHöñG:\k>È?ùà ø—«Ix…­e<…©äðvÙXL$÷1\L‰ÄÜ`cGyUV:· ~ÛJ“êXU0 )ñÿŽ×«!åÁAð}r¬Ï\ÁÒ}—©`¾)YµÎ5c‹4¼Øã`cö|‹ñ~ O¾Ç2;±˜œëôד^¾ZåR«:D=:hÆï¯\´ 9(ÑÄm×x ã1Šãws™9ÓÜcF E¿ó Ž÷ƒïí,HTÕ`;µð6î«uøF¸ Êì@î¾ $­–&1bФØ~*™÷küò#'‚DÐày$ãÚÚ0~l6ƪ03ÑqðÊIŸ}¼îȶ¤Ë±¼áÇôSå\PÇ+~²Âö±å­ ¥ ÍÖŸáž ÀˆþÙ–!Êø¾ú䓇 ¬–¶d!¾(ž~¯&EÒí¹¬èH«ûìÁ¬¶G[°bý"Ié4A²4|9Yón=~ÐW&^¹SÉé+–,è¢-I ¸ÃÅ* Mg²Y”}v`‚Õ3#â» kžŒN‰LÓª`›#-p[ÏJÔØ>ƒ¼ô‚ЮÜ_r—®BÙu6ìn¼¸@B3O™À/²á,=I!«ÝW7âó?¶R´ŽkºdSBîÑñ_.awœl>‘/VÇ`ÞR-¶Fñöÿµè|ÝzŸ G‡ò²ïƒˆ.è«ÀYupþ›(®^?’˜¸É‘œ%ÖlÝ+°ëp5öoà:swÃìY3Ùwõ|xé“I‘;ÑÊ@ŽýÎäöSdïr•É«ÅSÀ,3Þ¾š ö®ÃÜ`¬qÊ–$i·R8ÁÄØ|Év|{/…wÀ‚˜heÒò÷éªÏÕÐù#ŽßÒÇpÔD£cælëÆqœËö(»p ÚX?cãMÎÀ2‡Ü0¼H¿ìe1CwðÀ!wf”»ÅBøX|çZcãyŽ­r#ìÌ㟮·°wÌKHŽÒd;¾mÁÿyöîGÛr´Q“fîÇfà•ìlPù4…¼ÜÎcëº2©å3ØýÆ‹;c͈¹* 2Lý¬9¹{Œƒ¨’ïðr¶9ô¶€û¢û„wUø²#ØÃà¡B'gdÕG*¬9ÏœKࢠ0V* þVI³w7$ÙðÞÀÏ•e¼U¹ãwøp÷×öcH>bÉÍÙLª,§_B%‡ÉÉëcYm®9‡èöG†ºmùÿöï\´•™™ØFön‡)Þ‰¡ŸÌÈÊÖ¨:þ;g}ø ÷¬y 4—Àº}ôçL!¢®“ØÈF_€Ù¥GêŸr·%R¨o®–ò¥yô“Š<ó(em…Bd¡Á¼Wx®ŸOוòÎÞš'ïŒ!ܨ© ­¨ÆívQe÷šgC­z*Tü Ò˵°#È»dY·ì7`×v²ŠÝ˜ú´‡ÏÄ1Á&äKõjj6èG,7–áKG&›×‰©Ÿ„ki—x:¹ú/&Nî5ß ¯]þrð³•ÞŽ8„—뮡ýúdÀC*ÄïNÜÿönFCíÏûïÃNïf®ãù›î:?Ø¡A†ë#‰k™éÙ‹»~¯cs¹¸Ú Ë–j+BÊŒZ\²¿§“c2]ÍœÈŃxãþ;pÏq!¾™°ä›4+œlË¢Ô¡ñè9<¸û=XzNa6= `w>_Ø 6oÓaþ7a²$y?¶?³"©Wvr§¾ùrìïÃB_o>ƒ‚S>€Œ8ü®AùãÑåì––ebGNã£xy&<ñø¦²ô]GaƒvŽA|äҌá½ð7]|î<ãs,Hz«O㻽é$‘Ôï80bÿÁŠh¼®‹óÌXÆ1kõƒ¾:Æ]L»Çÿò' ·©7þ~eÁ– fàªöSpFk9jßf7·‹r§šê™õÝ*8·o‘ºŸ“ÿ:³µoÉvÃó¸oë L\§Àö.@õ–PbP…Íûó{"ßaPˆ&¹>¯ó¬Åˆ¿”(µnÒ‡€íúdÍJ_Þï{‡ÉÇώܬ$vU^‡UXU³Qs-ºoÐ@eê?¿YI¬`ÜòQ'ó´«ðˆÙrÜw\”KÝ{F'ê¡»Ì ¯ât ¬Ô\ÇZ>\Fã—€³>j±%sþB¼Ž ‘¶uþXüqäüß›; |#-ÿ±®aÛ'3²z ;n“¦&ê¤Æ¨…ëR^ëÄoƒñùÙtÙío_‚Qc6èMô™e§aRú&8Ú'ÊÖXWr“bH÷²ÇÔ¥ÆN` ÂñS¨>qõDZéÈZ'½%£ÇE‘ׂÜú ëŽkó}ÚLrÙ¸`‚ÚÆÇA@\…l6»Æµ;|ƒ³]kÙÜý•LÛé»tT“|ïýÂÿq/z u‰Æ’.6WÊŽþûŽ­ª‹/tsA-wëÔÐþ³p’ð|>¯kÞ™'0Û#€m[šG !üÀýþÿÛ…“ÙÑÅG“ñîá R]‹ó¦Ö£øFYVZÁÍ,êBÝ¡¿8÷æfºµ\™¤;ôÒÜ r³8 üÒ6ÃÇŽà-UÀ"‹¥oyæ¿û8“âBÈz¸„etÈ){ £‹ûO¶£ª&Çjß屸©èÑšŽöì”`<9ô6 ¼€v†Uu˜è>~ŸÍ‚ê7Áõò1ÄK@Ï÷1¶ùÌe¢úghˆÎih=/®¬‰Ú³Êä¨G!nxþ/èNËSØìúÌô©=• üŽs­âᤖ$¬O3%‹îˆpB—q'ŒÄüå­tS¸Ž©Ó"ûfcñ°Úäy2Ù‹O©ácsðØE4F³”SÎüǸd£ }L‹ŠkÁv©Ù°å wÅB·ÅÃÍü7P lMw†Ó5…­Y¢8êü)t¾­Ï}U¢µŽÞ,44‘kjÅJ:s0ä¾&yx‘ùuEÉõ7)Xÿ,ÖÏK@D2<áœ|ñô4³¨@Í){ÑÆâé єߠÃ~|Wö £r+@Dúž-Ãi·}¢j®€·¾Ûðb9Iû#IÊ'ç­DPo4áÄ΀ó®ä‘ü·Í7¢Z[дNìMÖ’=ž‚Paw*Ö¯¦^W3ãîq"Ýirb.”õ¼kœ»ih\ëà Œá¢”Óqþ!³Er<{qÛòÞMO–]³g™ý‡€-Ÿ7¶ã–¿iú¸˜5«³×ÞÏfW•Òá-êTêÉcgïè…$è<6 k‚fuäÉã_qœ› Æt!z+2iù˜äÓ§_\NÏ7Xo¨@ž?.†Žò©ðúmœrq€¡[‹ÈŠâ |ziUl‚.ý¸À ŽK?úË[#ŽŽ®ö£ßÆŽð_£ëO.W!…5(Îôcý¯>Ñ;ÖÄMl!‘p aV/]Pþ×Cü81ˆ-RÿŽ*yjÄí‰ ‘;uÞªÐ,ƒÆZ› tÂ<˜ì¼hÿÅÂ5¯rOÖ\ Ò“wò7ý«›Ï™Æ–$ÂÓõ¯y‚V³˜0H’Ø×õ¤"~—eù°¿À”g Ÿ"§® órdU 6««‘Ü}}xmÆSø¹ø9ÔaóžlgQoNÑKÒ!¤Ö^yDû“ÝáèÀžÀ…¹`öBÛ‚-›øX äGøal,É_‘A¬™ƾ+Fü?ö‡ ×ýt¦èh2©ë¡ä‚‘ñ‹‰‡GÍŠ€&‹¸£©¨cR€ï4-ÙŠˆñðVÈ– žÉAËŠ$þ•8 "³¥˜üzîÊ.†ÃýU¿AÁjј*ÌŠÞ.Ç­A´XA‰aÚ¿œ1Ý gº›Å¾6—}‰R‹+é|룯 V[^p¶Î“)¥B¬ËКU'á›ÓB¦Q*A”?uÀP}4(‰lÁ'ÏìèÉ¡ËPÞoHúnDÅ×£ØO‰èû7•ðÚt¹ ©kÈ_‡\êÊäåNŒ5J¸¤à=¨Ü€Û™^ïHý#Rp&_ÆÓD1ùÔj˜WáϘ ËÃú ÐZ§Ô{ÁP§eùŸ™ÜéðÙì{«;?¯ÿ>¶ÅýpÎIÊiÔ6ë9¶@2…ÈöÆF°Õ¯aó­ÙÂ#vz|!tÈqûÊ-±Tñ0´”%æéñ=•x¿m!ùˆ¯qñ¢ŸÛÿ>ö‡’Q›.ƒùdÖܼˆô«sk²Oë·] öÚ”-g‹^bÇý~ü+kN–I§rÆžP¡±ü|Ær‚•°!&5ÜØé¾i¬³S€5ôÙAÀø!LØ"Ì4Ÿ)‘ˇ’Gü_þL+'1)….˜¾•»÷5 bÅ7±Æéäs'}rìT¾¼Î{¿pžoÍñ™‘HԠΣ]RÄ‚1Ï馶o£ëšt“SŸØG·VQ?kð·ô üùyÞ.%ÊkYUÞ~à”†1,Fš˜‹¨Õ†x|Y)/kU˜jG6ŠÄæM>‚¯.Ò¢YQD9»%ýN¤«ÜGçÅ%1÷ Û°fz÷'&œYÕŠ¦1Jävz-,j!¶Î˜nUÊÂq$¶[‹<Ð$†‚Q>¾º¶Ps½élÆ>1&òkîÛ÷üoý³r¶Z}€Å¾¢ìûS9$ÉÚhοš«Ž¿‡jÿ‹œbN6ñ{ÿí ^èM"â;‹ø%óaNÙm^G\=/´R†[é}˜% ÀV DúÔsô4 ƒÅ›¶€Â¤RZô¢†˜ ù2±¨Ý¤¹e-¬=(ÏÔ;Tɱ¯H¦o*Äœ÷ÍÐjSͶЧíŸWݲÝ,íÁtxä-åõ|C&XÃjÌÿÚ>¬~~3ÕõQoN 1Ûñž›Wtƒ¼²ÃöŸ)D!=DõM2iŒânl[Ì^¾n:™š 1k•p¦>®—¨JÓ‘þ¿Uw&2aCeVqb)3ÜWÀ¤k±$WTîêq2×ÖsrS¯€w{ö²tÿ"<€¶›Ó—gUmBŒ[ß3U… äõªNö°¦Ÿ÷ÂO‚½ÎaƒÇÁÍK xõÉ%Ã\2°À›\j!³LØnë‹äÃø“ÌbœÊú‹aƒFÄ€ì!ÿæÆ×~hÕƒw:µYØúƒ„5ME¥GXX¼ƒå×Ü[¿^Œ½Íž—óÊæ/&o¦Ë RõÜo"ÅÙ§5Œµ$×UcȾøõèph&4A“Oä)_µòI¬Ñîò~Áú£øÔAŠì:£[»8wyþÝrnß³ƒØ%P‚ÇêþåÝá+p,߇ÛßaD²g÷Á†›™rM. º=æjx¡ÛÑlIs5^} Ÿóü¶ÝœÀºg‘‹Yf¸`û²äk&*NœEîo%dT¾T~Hƒoõ²¤aþ7zM„R×ud¨bìòÙ„ïU7Ã3ûX. ó%nÎ.„…gôHª‡1ûw=¾¹Îl"Se¥H°ˆ,Ií«Å“O36k-íå ü›««Cç?]Üë­£HO4ý×FÙ²)èŸLü‡w+ÞŽè¿EϾÀ»_ÛÈÂOìàý$®wQ4kmž‡jöŸðf¡”ˆ6Ò"¥8­qÿü§CªÖ`›[w¿@„Ý*<ÎÝÿÖ¬A"Ï.bËWæöƨ054BÕ² Ыê‰Î¿rІ÷ÃŒñlJ>úZÇæºˆÍ5Àû¼±Ät³KšLâvY±üqcɼ\Yâòx2T夢ÒObÜ!Ì´æqs„[ÈU«PÜsõ˜Ÿ¬âÚLXXɘžÿ†;Þ¯ËÕ¹¢i‰"£~<½ydÏ-2” ë?œÊÔ|AüÓl -™„AÉwFì¯ 6’ƨÐÕÖ #{1Ï·Ç3±¢k[9á}f08éw\+\Ö9²ã¦dmª ª~c  l|`’{&ÊÏT‚¢Üúú{}7¿Œ:7‡.ÕË}¯ÖÁÇcnã¸øvØ7Ž /µg“%my=åQ˜1Ë‘ê[ò¸Y³·a€§Zyl"lɽ· ,ïI“Æ7Ðy>n èÙ†ë-2aÆDN(Óƒ”æg¹“y„ÙÌk2$eF:qZ2pOžÁ¢Ù(´M_9ìĸæ`¨úbƒÿæ@Èh¡’¯' SU,f¢ Õ¼xÄþëM q´DH™•ñÄï»%'P³Žì=ŸÝMù_ºÍp¿”1† üË–³Rö½n-Ü ÊþmÀ9Îùèy¬•˜B\Ë¢tƒYþU\ýBš¼Ô[̪ëÑæx”þiÄ\†æ‘=!’ì˜ÙZfmƒS4ñw¸)ÝEÕðZè ⋉…Xò*Œ(ÍAF[‰ÁÛ!!Rï…Sïm¸÷RdžÃ1~ä;ºæX"ˆ$DbœÊnq=u^äp–ë2T"…¾³Á+mºv¯å):6£¾Þa¼ºÇŒÍôŽâ¥]?+æÿÿæÉ:\}Á˜œ¼éJÊy&°àEïÚŠÑDAC¥?^` dî«C$fÌa&rbY’˜Ê*•ɽÁÛd}$%Î ¼OÈL/¿©ÝÆ$ÔÒ—Ø…¦à¬þ"²¶s‰¯áÌMÜwfÔÂÜcGi¢l4Ê$õ?"Ùâóód ñ‰bR<#vÿVï’Ý2Í!‡HWᨠ_0þ MêÎ1Pdk¢…¡}q,=“ÊfœÊ:jE˜šÊ¶èT„¬YŠ«uUÉÅ1VlÁûÄýÚìü¸dÒh4­ê”óËȹ㫸ÓU;¨ìU ˜º5šo«÷–¬`~Ú“ ¢\‚8 L§Rzº86f_˜ÁÜ`"_†gA7Z¢ûÙѤjçgágR¿Y=”€­êˆþ5¼Ë{¸Ôà ¨Hæ\à÷u¯À}92;P ôö @äô Œ…üaÕeJÝ‹Ÿq®1°­5Ž÷ûÚãÆ±Q‚dÏaÖ{c.·Ï&4l9Y8d¯¿ê±ù[ù ;#Á8î·}•-©¾?š¦‡áï~m¼S|…î1†sÅ×é{÷4:¸Îžíâ]â’mm™RÎ'(o •ãVâ‚Êzšþ*cB(¦^ð :›2!Aݜܹ± VÓÞ5¿ ß;½,{ùë-—A$i³Ø÷ЇA òÂ5‚JG(‘õ×é]M7Ü·Ý}DÿŸÞŽîLƒ-Öl´³(›èò/¯>g2oš¹y20Ù4­È3÷¤S\—ãTòènmã–õ9dR“!ì4V¤çÚ.Aà³›¸íÏ0ÌöN$¨ïÊîñ¤XîÂãP`nÁâ0½·áq úOãÍÇu²™`ìTH~/¥9_9øÝ=‘ÆHYòWå âé´Õ(/â÷_3ûë¨A%6ïýÃ`¡§Êòl1DÒœ´ŒÇ±—¯£è˜nüTQB¯9ÍÝ’×f/–ç³ô™G1EÏŸ›—ãCþ:fÃû7l¢ÏFR±)çm@Bœîpž/FøÏïZêÂÃnðÕ…,÷û •(cx{M.]%'D¶~&ÇJ@¡Ì7Ž„™s6BzBÞšªSq.î·è¹/³é>ó¿ùìËügs=¼ãqz¬ÖchIÙ+Ïšo†Ãgéõè®úkU>ÅÝœó'²,±™Ã4úº¹w¾Øçð»è·@·\Ú‹íkXánÔB?°çºÎ†¢ŸÛDà_÷m¸–²Œ•Y5:_ÛÔÀ;7M“Ä8,$q¾ãÙôá-xP\˜}IH¤OªÌI{Ûz¼;£–?Ÿ0b½‰’pîÔ%žÇž(ª<áå"2¡Õ|,ú)À”wÌáÒZáŽ3µìÜç<úë´?ŒéÈÀ†°°áJ&n ½³¶ýŢȫ·ŠlûbCܬOqó•ßpÙßS°rÔ4¦þÛºT˜@è9úls<ô X°~~,Ùž†ÝKÃÁÏàv¯^McíƒØÍ–X5 ÍõOÛŽ£—›3 ‰XLòhás»» ÏÖeÂÌ%¾\èF1®{çŒþóÑÞ ?.×;«…âÞ&äÁ*3,Ûô¬3µØêú™xô}+<=šèY oµËy~ž&$4Äš™k¼mÛkñém!Vè2šs—”d齬{»°ÜÍ]ï€5ÛaèæTø7ïiŒ†ßÇå°,/ FWŒfSž#}J>coõ]n›š¹vy&n/áô@–<’}€F"‚`ög)n1æù?æÈNx÷cŸ¸N¹‚çÅl@mØmT &OMñS†>yéŒ4ñ >¶žÄ—€äZ òW …ãO8 ¾˜?{àÖ³ýxøã|v>sþx(J2&œ„1§/ñžØi£ôAækx‹Ã˜³JW~mxà?mÿ¯¶¿ðûËNhýúáZ:vøbú‹ãÜ{Rûœlç–ZYòKhÞ±oTËqÒ< ²ÿðzܤ¤N¢%Ùþ ùš¢"˜sÛ¢š¹ðU°¶t7|ë1dÊë’éš/¹¼ä‚µ$~ª>þ/^+’3L˜Þ­{0'аâû{Ñ`¡Ì8½[¾÷€WB>´Ÿl ÙØ?é)»xÞ1‰wb´œA•»¾àFk]œ¿è·wÎ,^¼Õ(P=þGtÛ Êý¼£e¹pÍúX}Sc[TÃñjâec À;Ôøj'«a–|LµŒ¤ðš¨Šs«>2‘\–5#Ã]KIêMöïÈ€ÒY´Òb6SªW‚±ñ8¥ÔBÏÄ¢âyvé— ˆXK‹2f‰D#2õÏïþ›ÍóF7ú™{ñý)\´'ZÿÐ%/10ð >ÃAœz¦÷Ü˃vç[|§C3péÒ0á_m{,â3½ë,CÖR˜i®Ûò×+†’“uIÎûï<·¶Ýh¡)ˆÃ[UˆçáX?u\½5ƒ×Ä`]‡{°e-Sš ‰Õƒdò—‡\ôø¸>å Ü7\7ù†rr|®+ù-Ü• Ÿ£saËìnï•uàQûnº‘Sïǰ1Ú°qëh6–~¤/¤AÞhï¨éèÕ­Ê¥ž‹ƒ!ÊðâÀ4ÌñÄ/[Ɇ§‡@åx5t&Žà7ÞËmó<n×Îs1ÏæAôœf ¶| ‹¿'ƒ~î (ÈotõÄ%JÅT_9‰f‡":¾å´ÐÒ~½ÍJ[û¸?Ò­¨q<ÛÚÁѸc¼›ÇVÕÊ!°p¡8ªýê¥wAŒ{}wïÞçÖí¸ÞÜ] 2Á¤ۂǰ9;"8©hü~~ ìóeË?™Â÷Çå´ˆkeÕ»8×) ù{ñæm±à¤;­Ù›=7!ûŠ%åþ~ä¿«þ õ7ÁTQoOÞÓÅÈÖ£“ ¸7‘½Û…}mÉû}è3®¾[…›}^R÷Ž#øC'äñ¯ÃÀªP®º ¯ˆ~¦W¯ÝÆ+c9æ0÷ôüµÄC.SØÝ°gtÅÏnXnÿó#ÕØÎÅ:¬.Ý ¥ýÆ¢ßÙÅøkm,«Í–çfµTÇïcÙÿӈΚTúÉ®ÎeEàgÚéGƒ®Ïº¯r Í1Åúٯ̩Þ|CÈ:ÚçþÀ¢ÃÚÐ{Å^{r‡Œ 1¯X‘Kî—go/;+·&rè÷͘TB˜¦¨ “Øl€ºáÐÔ ÄU¼Ÿ ÆqS’ жS®'&ÑóÚüp³57^ùÿ°TgÁYùG#üW²@I]Oᣚ.,l¿Ç%θ‰{7âtÛƒä„ÊAÞš9^LÒ!îEéâ󪱠Òs+C—ý×sLžÌuåýì‚ÝýÞ\÷rg`,ëqr~(F¢| ÏÛM8—q¶ŒØFÁ½í6¸7<Œ¬Ø× ™ª& î¾‹”¯ûÕ8ÖÔ]s=‰}ôïÞÞUg.ÎG‰æýT†¯oô` ¿FèM¥NÜR2†v–5QʃÊ ü›o£éÁÛ'éîËRÌaÏEx4QòK+7žT™S„©rûð©ClÓ ‡ã)÷pâÃJä§·Øßð5:¾»Î9jÊhné’_Ü@ÐgèÝæÉ§+“á˜v1I¥àvÓ·ÉOòË^4áZN9$Á.ŽžEÔ£ê骗bxdÝGp ìeôÖÏ«\1™ƒ}¦âl£˜gS ë>fqƒY d^ïöã™îiT6ò‚í§ië½4¨Ù‹ó†XÍ6qp35„¤"öR+ØÄCQŠ2YRæBŠí¦ÿ]ÿü¯–¿¢¼ôç‚Ëñl\g Ý쩨&žù{„¾^0^j‰²+RñÚœ»ÜeP5Єå_u…ý«,èm£ T¼ök„ÿÖUpw2ÓQèÅTè«‘Æ=îq0£H¢¿O ÉCÿ4¦bÎÝuη;09•] ôG–\§®RØø$ŠÔ%hJ½'^¶+Å %`ŠË%üeïJÞNž‰7+ßÐZóBX—_Á¥Ii³s ù—xSqÕ™\ù£ë`v ±ÿË®#ó1Ï:ºd—g×ä,ÝpJo)Æœ¨¡»Û’»Á3Ó;gF ¶‡©ùªLså::új ±!¯Îü…8±~ØRkÃ>|²Ã‚Ád¾ÕÄdº¬P¼ôBqfÏ]¸T(ÆP&š+_Pµ¦œÿo»UB£ÞºÓ› ü•û…p/)¡Ez0Ê|.ºö挚Èmš ­_ê°ä_ýÿÀñ.Nì ‹{c y3[þ€›;¸Š½:!Å^˜NeF“aßR'VòOƒ=I9ÚÙ[Xö½}Up…{u‰ŠÅw¼¶¼ë"ÿëf‡½6ᚯf°òD,öšØÁܽK¹5š)´0'„|ÆYÉêÄîãS8s]Œmþ8oî¶bB•c±½Jˆ‰,L„9#qxhi¸=¦»ÎWæ‹Ã]ŽÖ@§Ä>ýH6·d%ÛÚ®ÙçÄ™P›Âÿì?º/þéàÒ¶Ãe³cè¨"!. R8Ôÿ^v&çrúû^ÿËp¬Ý~î–¯Ühmðèù`cVûEÎI!—»le‘ó÷ÑÚžçôárYÞIo–6Z… ¾fi¯î‚Ló<êý¢4tX`ùk\?ùÊy ËI“ë#ñ{Ù0w}U4*žÎTµHëY^OÿB5ý!ý ׋ÜŸç”í;‰~ô ×8ˈí{÷šžó0†JmàfYC—ø«ÐÔ¡ã¿j:O9ÊÉm_Ž_wÏ¥~ÔÛø$‹ãÙåûÐþÞ…¬ªÔ!ÞLÄì=§©yb¹÷ú&þ‰ fÆðØx'˜j¦Ì~ǽÂá´‹MBo¶C….àÆI1`)ZÁ_|~/F¨2óöäD\T-*ÄÙZ¸#cÖˆÿ‹Ï†½Ç-ppÕ-.¦ó+\9,Voì˜^êîª m_Ná¬X+<ï"ÌJß6@‹¼cßB^˜ÕñEÿï…~éÑØð`¯Êý匟Ê"“ôÉö£y°ð'.VP&oŠõر=Ï ‹ôãE«$¨¼Àm†a_ÍÉïì¶ú¸Z ìæþz©’xk[zbá9¶üÍJöÄ/ v[ò/l¼‰s…ÉQ{{”ù¼˜«ß3‘èA ÏwuýkñŽ… OÅû½"tÜ<ˆò))f§­F2¹±¢ªcTÍe*ïKÛDfþýïZ†‘µ’,[½ï³û¸÷í†ìر§±Ç”ìNéþßþç¤}œçMER—”A«MÃÉÙ“Ø5…£^ª3¥™ÅtýDB"×X¢Q™(KLÑÄ–yçOm³œ2”[À·å7ȨŠÁÚä¼§ZÙümB¯é “ œzµÍï­EY©f®A]=ž8ëz)ÜÿÛ -kðæÎÓðsi:Þ0¥)77á]…XêyÙˤw²“36ñÇLÁ¾zlXæÏ•Á#¯2 nbš÷ÅãM] <¡ôŠ«>:«ôcŠ^qoŽ»Á;Ç~ø=}+wWðlxØ€3{BÝ„lX³²ïãºI6è¿ü-fŸøãz Fì¿z{w[‘Šjâ$ê§pý9èŠ Çkƒ’å0Ýàý›Ð7h>¡Â´c`´±=k5Ù£sáÀkÉåô Tɱ¥§©VÕZ”w˜€¡«žA‚¨-š¾xŽ¥ŠŒwH…>ÍÚŽ¿ç ½©nß~×¶øÀ§×kÀoƒ ‘/R`sÞYÃþçe|å…\ɺw°Š’@å›-Û?[’dZ„À'Ùpñc)ÎyÊpŒ÷8¼´ã7¬’|¬¢Hûmqœ¾sÿ‘}*7§<š=Ô€­ÉXôDŒœ½¢'›ÏCwE,Þþí#ñ¯¼¿+jݹ®”xþ© PõU˜Šåš’ {-ÈÎóH¯úTrc]Yqy*³Kß÷+$ˆ.O„5靯=eSȉÀX˜$ a<Û.h_[—©rµöüÁäŸðøÉ~Â;ŠpDg/eC±(}C‰ êÚò\I%Æ Èàh-þêË9ßGbÄ,) ç*“˜nar>ü4Þ˜>­ÜP;w{‘£ËLˆ ú1Ô¯…ÏpÏÓÎáp^$ØîÓd "ÉÒc¸)F?|Ê&~õ\Û—ƒÜ;1R?àÓK¯“ÿÞéÞÇf²Mµz¤ÿ3$Åò¥pÒv9èãŒxZ¿Dúõ@³iˆ— »÷OürœÞ)avÁù6Ì=eÊ}îý ÿ]«»ÓíÜÇ_ë^J¡,HÄŒ#ßK„É~·¥œÄ:ü¡ Ù¬ØRœí)ÇâsâPÉÔš JIÚª ¼»ö\Ô‚O*ò´ð\NxûÏ¿ße`‚E<.UæÐJc%îÐ\FŠ„ä¿. ¬7›0oçhP?„nköÀšÅGáP®-9aÀ¸1spÆ!®^ö, ìÔ„Õ’ÏÁ9Eý°²ƒM.5 ÝZOíuUˆtõWœô¿õo:çÕÖ-âfÎjà"nŸ…í­ŽÄjÅ_zën>õ¶õŸWK€Wm‚_‚´Éü›™øg]¬ÎGL¨*áœ2šñÎòÑL¼<Šó3»Œ——1/my6}Ô+úþ.Lé±£Üpâ•ôWÂc> `ªú“ÆÒn4ýÛ hap¬ë¹(‡¡S¨ÑÆ+°ùi0§x`nžÕ ã¯ÌØ„ë¾åÐ;¬ØœÁ-rp@Ýê&°À­:rÑL÷$v<;E¶j1‹yâ8°u ó:¦GÛD§±×àè^ <7úê¾oÃ[Ñl¾…òþ"?gô¹~€Ë~åB[ðØXY´X±’^êqñlõX}¢åìµBcɱwÔ÷öbö+φ)E¸ªJâGIU1Lh¼ «yVd¯M¸³båO4H·F3ƒ˜•:äV:|ÒgÂ%K?¦Ðá\qž"nþnÂ=,mÁ?4…;Ç·eG–(ÀREr%7“ê/XÈê%ÙáïY ¹¯ Rg-e_c˰3/œÛh­*{w‘rÅ8~­T TÏ1döfÁ\¸"+ýfHÞäÀ¼“3à¿ùVdĆð<· /ßH}×-E㬯#õß{)òCì3}yÑÏVœ¥>s"!Ê}º/ñ‚“!‹ˆÝ÷4tÏ-xŠ€íeA²jÓnñ®Ï(üö6¥#™€òÐù«L3Ž‘ õ;0}è ìFª?ƒ©+ÇòëÇAá±wyÛÔJ÷Ä“åùFðÙ8§üyÍü·Ø×!O–,MÌdÖ³úsËyÒr“¸Æaü¢ßŠ®50§èÛ¶ê]ö ³yeôbƒùSÛÓø8ç.Rb;¢ÿë©fþÕÅÕ*V¨ß£K¤V’9nU`ijÉ]0ÿÞÒ¹¥ŠW¹Û|Yò-5hDÿ¶‹;qޓ龆sÔ²2ö¼­£¶x/AœyRdwg–³”Îü×KŒÁ¡†äó|KæžÙ1ñUœÌU\rÖßž|…»Úg€µŽ1«:~‘*[ïcçÖø¢ÈŠ~ž_Î¥äÎÀ²ËÙ‘ÒéìõáG=3¬©uë¨ðNžþ o„H²Á/¾ð# Æv²lÞ`Ü!ÎÃàú¶žG›M’Î æWA²É æÝZ·2  ÀwUôâÈ:®@ë.¯ñ%òk¹3 OQúóxæ#: ÕÖ¡Æq"!ÖÉ›~CM¶Ð`†ÿ÷÷× àâåSè9}=n½—Ê9l]On•r˜›s| ä˜y~(䊓õàî2ôA*p[Î>µˆÂy lËKazáøÜ~TϼŸƒÇò=vÇÒ×O2áÀâ•ä÷îßøé•4æ9ŠÁ~'OÈ8Q× Ãáv´ sëÚÍŸ³é&¯´gÃ<¦qc<6é<Æ…çeXÄv!$?GŸ}Š9¡3[˜á‘#ô^r>¹!Ͼs"“—±&€žcûœï©¾ä•™×òÉT²ø´ÿ©cªSÊïS¼Âúm—êüׇ3üÞÀÃCÑ#ùK„M…ƒGŸszRVD¹¿óŸ¸ b r¹¿{7C“pé„)9T’‡íqn ›03ΊÓÑŠ‡ d_ˆêËbÆ¥\A«©ŒG6àÔÈÔ{ßÑÃ~=<•bKW7Á¤Ë‚Ü|¹§øBï0¨ º°ß‘_yþÕâûOoaáÌ…ý÷¼uwœ1)ôjð.ÃʳF¨ó£}ZWPnÏa|n•€OÖæã¾á¬¹b° Ö^âßLgy×TA^?,¯šÃ•,Sv^G©qïêZÖóì ðkÖ÷âÓo¸Ô~ ç)‰`³é ~0±ÿµò hdú~Êe»§üÓê0oe)c¯ZˆpI÷wÔu¨«œIÎ{&ÐØÖµPíy¬½¾ŸÂC‰ŽYªŒKøÇ{ExùD8÷ïÙ•”ȼîÇ‘z…@¶Üø-Oi$þ.,ñYÈìÔcGÃçÝÒU›Ê*úç“Ó"¥äé¦lÒ\z€ô b¾?rˆý†›xê»(Óý‘Ê}ŠG›º<¶Êàõtÿ…×.ºŽYËU¿j¦Åç:€K2!;^iÑK 'Yá†d0Èdk6—Œðߥñ¼QÒzÝü‰Żɑ¨à $ëY~_Ko5C°Ê$²ó®38ÙÍfòo v†×sc”RA¿b =dŠö7éÙ'.øçŒ cÖg÷âd¯LÐ¨ìƒ ÙðØì$žý¼„¸\þçƒA°O~øÃ?ý;a!jÛ[“ûâqnx3\ÞXö}I¤Íî5¸ì"“¢µ˜tâÃ):lèàgîDư7Üãö㉣É0Aþ2\}P=™þäžóo´6æ±°M¦#Ñ—?'‘y³D9ÁÌ+`4#…»Û\Lt@ñ];pfDÿD<`/€~»õÈÏ¿A¤Wú&Jm¤[œû [Ù 6×þ>îMc–Ÿ½È+QпX’­~Û O¯_ƒ¾­²ä\úz¦fÏ,'3“TK2}û/|òF­ÜZ?`ÿ÷Xü›Ÿ %W޳ºUúd§­(»eeMé;Á¦WKÙÍ JLEvx¥ù1±¼ÙtùÛõœUšNš©N¶½3^®3é‰Åxr†ï³R³Q+‡nºXGÒ:ŸBé¶l4òëD±n/º¹O”Dd¶à½Ÿ-àTÔM5Séš>¨›ÒƒC“ˆ_Ô,pŠÑ¿ ¦o¥Ëûye/Òp´x $ÉDÃk9—y9'Ý¥9{ÙŸ¯Ã\jõqjÄêµA%{ŸÆÃçÔ0+z{j!Æ’î|Á‰‹4¨VZTÒoRñQÊ0½´fdÕÓѱwÀ’?‚Ï7È0 Osâé³n®éÅ–õÿboæd²½­gY(¡Ã*Ì|°™;õD•tŸ.ß޻¹E3dHãî9³7&.Êf{ZGã‘?“ȳßÓ0ýS÷¹{'™Ö ÛF'ÑÜJYòþê3¾â«Kœœ_Îd§÷¦aRi $G,eÉï]YGÓèþCó¼ ãCÿjVÃë׸ænÌóŸHo¶`Çö9‘„uÛÙ±‚MøÒQ†¼Â»ÄÈ­†:¼ˆðøÁ}ÿ@„±qP5•-û+ÍNœNåæ*½AKe&Ç?½ç_€ÛSg4i8È[kt ¹ã0ÿ¬™úõoÁg6áÑ-8g)9fŦWÐ-/–ƒã¨.Ü V-ÞM « >\ÒCKrÜa£±ÚßÚÍþäžÅ¶ŽJÖëÞH´;§ò.< C›Y´Çzx1[†ÅUIG\=¿{¥)٭þ´×ri›y‹„îƒkWûHüoú ›N©Ã×^¼±z6{<µ"ËÃX¥Çf×ú’º ÿ%Ï…p‘w'¡sïÈÑ®­¬}ãb<Û•QÜz~jÛ6¶(x7ÙÄÇU…˜Ø*¤~ž‹|z‚*%öc`ású±ß–9(f.úá¤uº8[µÂƒnÕtèÔ~>îØÄ5‘ï©dÙ·ÄçÕ*HÕg!Ã{ðä)ûðîîîŠcíWDš N@¡øC|u¿Ž«ÝÌ©Lqµáx웘Àøƒ–tƒ° ß9ÿÆÂ#Ù$p '—%“äËYdpí4¬÷R>˜ O¯æò'Îbú˜æ^‹Ùq.'õl+¯‚츾SB(¹i!™êgg ùÎÕ¦8þº ±ºVAw¬ÊÇesNý÷¼wŸdîf¯iRƒöÒ¾¡ÅÚæÍÆ§Íù¹£ÿ·þÝ®-Mü—x¢¦B=ÍC6.+&‡×L#×y² ³àoé„t¬¶?‚ÿ½íÇ:TÞó:ÆË³=ß8U³÷üÜMGà­Ìkš½ï‘ú´W :2³'܃‚qı½mD Èþ«Å0öû;ü½`ÑýPÀÖ¾ŽÇ©¢¤:Gr7éáž«R$¢c.Ó{œçwoêÆG_mx;ÎïG}Ïqè?ù1×e«CöÇÅ£J‘,Äâ4)÷ƒó¸÷OÁ¤q¶ý»6ë8$•BÞprH‹l=ßÿÞ”'+¥¡E,Øgö[nÁQ§°3Cm#øxÂ`:Ãò÷ë@h”)DÞ9ƒ‡‰èïT|ª‚ìÞ„h!y› ûp ‚kÏbÆ`<¶<ý‹rýʤ~R0$£þxCx‡"û5_ˆ×­¡Að˜i¦ÿ#Mg<;ï$šº‡›Óë'µsY°4mìŸë “x xcÒÒ|ÑƸÌÊôÇÞõQ¨¾ø“.ˆ–gYéŒWj¨3üÂ?¡ÀËÇ:&–·`Ï.(ßy‘»úÓ™KáÓº‘‹¾‹vá¦1 ô¥*7îûF¢ó¯–¸÷ú^Ód7?ð)Ÿ5²þ]ÜDÃêÌ`ºÑGÜ¡ÞípüÁ{¬9xšfï¶$Sk—‘óïrQ*lXS ¾âåÜ–Hùft^Þè³07Ö±hÑÕ ·¥=qå]P9|^O^ †)ŒüÁö¿UµªP¨ w÷­'BR…å0䆪F/f9Wj±ÆØ–׿p^0É‚õ˜VqSÖuAV‹)¬¦Ò̶,™ÌÝ«Ãè )6¸”ÞY¸D ¢póoG&¾&f¹—¢ö±¿ ³º$™Í9vÛq ÚÀ£,‰¡Óvçs/4ÍÒä.Î| %Ï!ª®ÛóRb„ÿÏœ@[I=\}p•ó쮚Mï D[ƒš·^OØŸ«ÕmF/·1L{ÖIÞƒSÛ`S­2Í=ÇðuMïkŒ'ÿ'íl·dâý½h)’÷GBƒ³Ýx0cF_1ÍðLXk¹‡µHX²«žßùî}À½˜¢Î*®Â|ÇF¼+­A‚Ü Èä?+¸Y‘Ú¬ó†Ùóý 9ðV”ÓùJÈâÒhòpm!n.‚M%LSi9֛ΖÚ@vX¨à|Í4:§ò/üðÊG[÷@ÕÓOü¿*yýÍ´fÊ9þ²mc`¤D¼<ñ§PI|(˺UÅGðÇg{“˜Gi(8ºf˼Ø?Š´|šÌ• øÁððRÔ˜ä…y)Çña¿0©H‚Þ¡2šÆþŠ[‚w«Þ*3ÄxýIlf½!Ä~и-à¿dˆÛ¢YÕ 2pކÝÎå&ùGÂvçÉ8»‡“>s—÷dÒ1ØÓÏÔWÀ½ó3i¥o û:` GvÇ’cÙøiû%ìû1{†£àuçh ù‹÷Æ4€†Ü!¢TfG¯?ÃCÂQôÂÑ%ÜIWe¶«O“}\¡Âïn¨ÁS–?¨N µ/Šûq'^W|ÚÉÖÙÅJÚFôÏ97Й" ²óðÇÕ ¤ÃàÑ Õ߆üL#ì¯w¢®¿:±ÂÂz‘2Œý˜ êûW“ùáNì™ÒTOmºDÙN[1æÏ—glƒ(ªÄwÂÎâ‡\pØfòPñ÷µâ6w³¢‹×Ú¹\yÒ–[­ødž#Ks/åÖ›¥Àùc2ê~7ób£Ÿ‹@ܯk\rñ"\ù Ÿôôr§.Ê«YͰìà3NáN ÷yûD ð“ ¾ý¨UÏoBwvêqÇfÁäoyOs%%ÿÂÐ.|á*„Þ Þ$#ö2ô¶Á¦¦ÿõ¿¾¸kJ-wÇâ—Ï0AÜž¦òÌ“õ^('µ;Øòm˜s{#Êã7H‚Ì›fÞÌE&P˜·×Ë“©bÞõŠ{è>¬9áLþFƒ3¼öÚŠý”ÖFÂË!vî­ðò• ìmèà›žKÆ%F†,§E–ø«¬&ÿžKOßÿ‰òMmè§q…îÅRén<+Ý.R¿øÅ/NÃíO8^¤JÍá»q朘ÝYn¼»˜ï1 }^G!à¥2Hï?•M"Lûª¹ÔlÆ]Ž"Ý¿;ñ²¹¹µU·Õ‚kR8ñæmÌYdÍNêÝÉÿâ½a¯Ÿ ~ Óçk$œF!XDtŸsÿÛüöÌhÙÀéjÈ““ Š8cž:÷X3•=…ý¼®§¼ ·–ìþþ·°#õ×Ôm*¼ÿû‹iñÕÐôZ“,¶éǃY†¤¾´’šÈñ9ûª}`"Ç#Âs¶¹¿'á[®Ž›ÖªÂ.¸’ǧßÓ•ýJlç+L]‡YV›p]SUüž^Ê™%ž¥+Üíl!´gDf80¸x{˜³ôVÃb§V²â:ÀGu”9ù–ûrCù‰a†ïZø’ CïHËó’ÿÕ¨×2GüÿHf8¯¼¯—œ½ Ï–7s—¦ÎÀ¤Ç«Èô]’ØV!,ôrÇ"xü4\þú*–Ž-Ã]{Ç‘ƒf‘\¦4s¸¢ÀÚžfAZ‹*ç|@‚Ä´%Ÿpr¥1> ÊöVæ{¨!vn†àÿe YÏ`óÜRø…MͰñK-]áÔ §çâÝÕ´^&“/í7™ö L€£Ú¹t‰¦ºs\ù;ܡͲ#Æ Œµ}ÿWŽÍx× ?2¼ˆç]´ëøc¼5ï,×ûå¿£‡šä[’•ô5¤¶}ûÚœ«ø~¨nÉÁ=ÆCNÊ=T™%8¯úbsº?4‡ÏE¹ZlHZlÁÑ­‡Áìï}ìX¨ƒÏ ÎCé ¬_U9–w }ýUi냱,lŒ®£Ó•Æ¢‰Aæºòñù¡/øâl`ÍÊ8Â=–ýN— ‘Wvù0 jÇ/G‰“?qÍÚjÌÜÎݳLá<ílÉsÉ&îØæ¯ÜûðC¨54È+ÐWé ¨õÇÄþ8 ê¯“ð¼,­è„ƒ¥ã™Û:>ŠoûN­u-ØíÑ•P™hJd³5™¿öcª÷QŠ•FÇ[M¼KŸÕÙÅSÜ”1øÃ«ϬFЫ‰ÿ<‰ÇØ„.nÓ‰oÖ€Im™ëö,]n£Å ¾Én‘¤Ý-«°v™mï W/ßÁ…ÂkÈ|·‡œÀju¸6áÛž—‹v7%I…]'Ö×…Úç2txŸ)ŒÍ»Î)_8Ññþt÷ý¹´.÷Oë[ÖV½l'·œ,ž‹å~,åý]¼C'`WB*wQL„|^¥Ík<´°q“K-ÿnîZÜèy¼Pƒ³ØÿÀ†Ì”&ÂÓÿqWÝ\¿×$Ñ'‹_qïÓqB‚r«¶ãýg;±I¸Ú¹¹J…­5…ùæ¥D`uÖˆÿGÝÜÏÛRìÀcO#áùÞ°þ×rÐ|D£—üœ-™èó<„—Y_‚>·þbMé\÷…3ù\ÛpÆ£sÎ Nǃó÷OôþÏGT*%¶Gš2ø¼–ó›ÚB76µcÀýü¬ñzÜ©íôûêõ˜z±Mn˜A-ÿ!~–~ü/øÀ’YŽð"%?mÜGå7º±þæY$èG8ÈMŸÍžœÍAÓr×õ-vŠá#ÎÌ6…ü ±ppÚMnÝìuœxr&úÆÝ¤;úu þìK¬€¼,Y\oÓËm/7…ÚM­ 1*ó‰Q%‡ÿíÿY)ÀeŸÜÜWŸÁúÞ?žÛãƒö×9ŸZäÙ¼}\ãÂx™¢ÕT'¸¶u'¤&Of>™ÌæÈ1“yÞ4´ò:Å~Äœ>-H\Keo›â¼È7 š´í‚YÖ×yü±U'pJ»: ÜÃç´# ¼²ŽÄ@âº!pùöž?^7 K¬Ã Û±äü„ôE€9 VvÃ=‹>£p‚7ªú/Î×Eù%›`O„&ù÷~°/p?¹tö&|,[Öyn¹‰ƒ- œT ׾а½Í% ¯5Äí}hŽâ®äFÌiRÖ*ƒæÇð˜~!ïÿÇ/ÍÖ‚†s fÌ÷Æ0ÜÁ$Dˆ[®*™¼ÏÖo¬Þl¢±&ïßÕ÷²Íigàyt™­K˜iãY%=9{é ŽùœQÐ>¶Íø$×q§-ÄSé”R{ôiÝÅ-uÊF£e"h_”ÂfŠ$zümÜ‚ Þÿ…R¶d:¿”25?LˆÄ«]ùp8S™Xî8 …Ý\P‚ Ûy|Ô¢Oð¶sbÕMÀáÍ$öûÃd’Y¹ï÷¬ã)T¢ëñm쯳,1¸ìÂ~~0w¿€¤"«»z?®^ÊÖ¯z@T‹Ì½wƒ¾T9=ÿO7¢ÞòÀeô9.Àÿ,ê– c×vqæxn~£‹Ž?)SS']QPï—Î5Û =Mêøðh,‘ÖpÄ Ó²ÈíùZ¤øÂ&»¯›ÊG¬…œ˜Š¼:1Ùr—á%€¦És¼~HœºÓh©Ò ŒòzB—ÉšÃÞ:1 „Ó³<‰oƒÛp¡w ÷#q û³¶Š~M"·H9›–ÚaE¿"«|çÃî-™ h×K_nt„§k“º{GñÄcaÔ¬ÉÃÃå¸ùødðZjÜ aæïp‘“"éû.ÅZD› gÛ}X-ž»M­Ø¾Sf¤ÌqËÈþ‡viíx6c}qB¹3qœõg_â±Ù¢dÆ}wì¯Ô!vjä¿3“¹->Þ»‹J‡R‰I‡¸ÀÌ}Øê:L/ZH§Us»=Æ‘’Ü!šÊ›ŒÆOLYq¼ é“ þ~®ÌtÇt˜³#6ðRñf­ ›ô®‡Ã¸ÉÀ5ÖÖßóBÏèé¹hºÊ‡öòàkåðþ"HÊÏ&Ági#\ñpd—Åãò¤˜ï°öÍ ?wJ½i'ÑùýtÖ)bˆ›–pò›¤ñ°®­ž°•ä<Ïñÿ¯FaͲñL³s,“ {Åëì¬øiÈYíÆ=A9*·•˜Mbö=‰(1$ëëA«% qçNÚ·Ç>ßò¢þQ2$›¯qâ®oqE³0ÈœÀ¨¨äëi¶`R¨<õΚ8S# .ÇLdxZPïœÃ»c]AÆ5áîttm¶æ5W—áÍO²Óe7©ñŽSrÞìéä$þ¾+QxÄpÕ+‘qäp¦š[V6—ÌQmÄ£‡îÓ¬Íjä.ä×_8ññ(3ÿ¡Ì²/æ%ìØZv“FðÐpºXÃZgbV½¹ôîvWÄàŽ Ç¹W¦é˜é~eÜÏå-—´}ÛotÁG^Ñá2ü7‡óµH/H,þ•µ]Áº:iÖ+¦…ºkç3ëŠ45íÖnp™æ6¼±RŠuÙ— QLìáDR¥Ü AŠÉx6î6· îœ³VëÏ1ç~/ý<-äV¾äŸ² ‡^{¦¦ü| úºÇCÕ¤"²8XsŸÓLAX4œŠínÚlŽßa\ón6nλƒ1ÓpCÌ|îÆ›ìo‰f™¿aç­5ßDâùåëÇÿH²eß@dØÈþoÆ·DT{ É©xûƒÚ\²S&?Ô¾Çv|Yø÷ùÁx²le*†>oÆ´‡»ñsÚ58œ‹]Kg2ƒ×_á‚J[\•„Íûm?ƒïŽcÈNý4ÐOŠý(ʇƒí÷œß,µ,ˆÝžÂ✬ˆqq ‰#ªëtÙÉŠR8.—½á‡‘{:ÛÉ»QL½"¼oÍ'¹® ôÍšœƒÃ:ðÿàʘp Xç%LvD¯ñO«ý 1êlå4=¶mühN~@"›3Ië»%láWa¦šñK–4Â~Ý[¼f…Ku¸ÑÒðqxüHý—³±Œ~•†ÆYç¸gOkø—ܘÁžÝÈˉá‹ÿôÆ}-‰”Û÷ŽÛë5‰½tþÃyˆtp/OHÁ †%K·yÌ9–^ ïV§³ÎnÔ:­Ép¥{-7 ¾˜L"ë‡qIÓ~tËYuÚæèè)Æ]‹Îp>R>êïÏfgõsPÚ!ÿ‹^€dǧxͰH•ˆÂk–p6нž_ÆŽÙáü«£ÙÑ~8éÏ' ê¤ÍFûP=ÿ9yi8^šï ÅÓ™õ¿šmªR$ïùژؗ¢'_aS_:67zž÷42ŽÂ÷¿¢å‚iØtóÝÿgÉÅ÷Ãáp»ÎlZ…Ù2‡yÄËâ!Ši3­ûˆã$Y2ÿélæ]˜€œÁQõ%Äçœ",P¦·’Š˜xúEД!ÙÝ@æ>Ô&f¬|zQøLÑþ { ÞÁ¼ ±Èæê1Íü£ðgýXvÃy Y¬?“}–+%ïî,gîZ3Éœ_îxv¨ÍÙt¶$)šLŒ“ç’ÔœÕÜÑñª¨‘iL’S­‰ÿ£4Û-Ð$Õõ…}îÌbï¸ýÀ›zŽº8ž'æd‰Ù¢£Jö—¢ùç‹Èᇊ¤óÑ3ÔG¶ JÞÐjöA?“EŽà—áç¡ûÏ(h‰š êRD-þ1Êyü…Øù–ì¼þ|v®Ýˆ¹^JBCuV4Λ¬›dÃùî]@ò 6áÆ{-P=tœz5U`Â;ØÚB(”ѸÁ„°NÞÓ¯—-ÿ@¬ž—‰O§R×ìuÌNV??Ž8>•†÷q:dwv”ÜúÊÍ}ñ ßÝ,ÄàéAÆùÿáàNç3ïë`æœVP¿;ƒŸ••NãÚ ø¶œ‚4SG¾E®Ûò:g/ø‹Ê&jìçÖ Xù|6¬s\JdVÜ¡ûÊ#hª×Av?°›z. ×-;ÂFI¤‚L]ÿï%òfq´,Óf'^!½èMþÚÁ‹õa4Qi-î{ïÏnžGz¢£á€£Q";XƒÍü~uÙ¹tÉÜZBuÏàóWà÷¾t(Í5'í¿‚ ÷‡#â#ûŸŠ[¿ÓÁõ¯9S©T"ñšòNÏ£þF“ØûŸUü¶ËÑΞp,â¹ÞíàrýpýFÐ\d ¨¨n._UXºmÁðØõ%·M¸Þð(` \¬¼ñ¯–#‡rÏðFõ¬ßUf¤;>ŠÕèmŸø«üø=]0*f; A´¤¿p¥îâ°~­-]cU5z9ËŽÿ8¹®Ø :óÉTsjÞ <[@qf9ã§ÏîÚw Â$€ËÎð'ÐŒ·‰²òí®¬I÷nyÝÆLÙKd:,ñ÷:QLýÜ ¾™XºÐ{ÄÿÃïk“’„r0®¦A5UgsCÏsÛÞ.¤^}Yì¢÷îð®*tèÚQ2dómæîù ÿjj2ø]ÅÙ<'м¼©°cöx’tFaê£jaæ,'¬ÍŸJlfãŠyöì½ùK¼q òHêã!<~ÉŽíê«ã|Þ©íÈÖõã”YuÀ_¢Cx†ãéãžÛ¡˜‰§ÌK :4r³½c”H*§näÿ&ÌÀi7ë.i¢6uѤçÒgÌÞÿÄYÃP”åûŠ‘¾Çáì6KegɰÙÚ‚0½.—Ú6¯ƒôVç¡ áÞÖ²ü=§õxEÿ ´É[Áåˆ ÝÝÏ’½®âVoÎZMÌs_*,—Õ&‹Îh“Ùyoùn7­IdÏr:Ó|&Í‹`9DUÉ„,z§·çJ³Ø'0øð´Ý/Cò 7±¿/QåÌ)X^ËqÜO>OÙæ(þµR…/”ØN÷êý‹ç1Ó™]6Sâ&½8À;üJ𼵫ÍG:€ûÓJ;*CØ´ëJ伿¦ÄáÖ›&dùUObóe=_ðñs] ,²™ÈfÕ„±ÖM`qz¤óî‰øEÄfÝu" öü×7Í…ïqäT^;1—øU¨R[NâOÅ@Ø‚Fº2m‹¨!›pÇZ¼_€_AÙpÙ”åÏlãÄrVa¨ù§ÓHõŸV4½UíÜYû•ר¬ ûÊQþsψþ=Uíç§ÂŠºÿÌŽêÏÀŸ3±?ú0f½qÂ0—TèØ;Y6®4™K¶¾_ÊMʸš lqÖ9œó§™Ú?2Ï=`½mËK-ƒèÂØ÷z5Éßxœ†<0"V»u¡cÑaŒÛG¹0fÎ2vã×{83΋SHüBÍÞÌ&c2ÌIpõEÈžßÄ%q€¶#àËýÅH8%fùö^°‹þ:ŽY‘ÜÏOïéš?›a3<‡çÝ l«®*‡þ€Ýáz²º–EsO•­‰×{Öí© ßú'†ø î˜ßœ|ð*¾Müßù×÷Ʋ”¯uh"ú¼  ¾NLä³6[ܧÅÔ烤øDR¿õ_9W™œxnõƒœÃ…]`¢Œ¸>1‹¯.ÃT…º«+ÑûÅxødÄflÀ§ ]Y™A8Q3‰!®†÷øaù®¨úùwÊ£”j»C“–Ç£èÂp<¿è3Ú»˜a“ï4=½ÛTIv¿&™|0 ‚$AmIxn<À†»Øª€‰LNB•i×@à¬VÉ$à atëxCú0EŒÜ{½ ~È›…&°Ç:²7 3§R*Z¥LžÖšñz¯/@ÑšdfTÓþíõo¸Ãoíh»¨,½¶m+3þ ­%KDÛ©TÁj¯S…º£òA+H5 …¾ol±–Ó«I"°‚ܼnÍæÁ,üªiÃÅó…YáOy2°Ü×ÎUgzaÊ(œ“M_ðw²´S7ñJø{°¸t…µ|$¢úIt‹c(Ž “ÅŽLiX›aD~-ƒþïNìö­·œäÅ48bö/¿•Š’ˆß®äÝ¢±˜Àœ`ê×/ØR»ˆ´Zår¹žLµ!9p9˜ýÐ`cÒŠñýà&VhÉ k-d!bȤÖoPÎýÓÔ"øi¬ 3ïÈßY=‚ÿMRx-¯‡½O$ІáÝ_y²üOɺ×2K8÷Š·tâ–UxÔf‘sÿžûÅáµEó[`A¤h“Ï—•Ȳs˜›õr“ 9Oƒ»ùxb$%AðÍ2P04àÒÕ4Ù©åºDÙûiñÛ¬|X˺ãè¢úü£’@áÒu¬S…åA1lS¢döG1¦¼U†9֌»û^ò¬‰Ô)öDè)“{´og?UrmÆBäëÔ­ìWš[›Tb,ª½jÜ`%ËÌ\I=…„"Kâò”[³‘Æ“~ùþÓ*V‚IžÑÿú”N<¢.çVr¡©ìiøVœåÆ=xñ»’ÍX?‹¸>np‰3L:q" s—Hceè4r7ì7÷êq.ùk*¯½cÙ#÷‡paè/¢s&‘Òò‡ÕZ§I‹Œ›öÝýƒû«‚¹Wþr[ Ù ·|’ðVxÍìÂ'Òzò¶¶ø’ÙÿjNÿûBäwÂol½©EÎ/m‚eãYКs¤J+†¬êϤÿžc­²ØCI3–ä¯FÜBL‰Í êuc±ÐQ¼Õ6›H©W©ÜR ǹaª·±—•ÉÆ÷G#³ñ¡§×öñnž'În vÍÁ &¦c³é;‚p>;?)¶gB…÷O3(G:ë(SR»Â7UÙÄ­wøÇAÊcÉ´0 Z‹FØÍÕa+6¿-“øþt`ÁʇàîJ ²:&…}Ý’îé1$0]—-:–ÊÏÏ0Bù ×2½F·KHCkÇ$x1ï"LÕú†np`˘º<‚ ãCÎG°žXž þ¯­IÀÌu°4€ÒӉĶx;¡¦JT„^âû0¾ÓŽkœ¸úDÞÝ ¹Æ¹1æìF>™â Íbì^÷ÿÖ?z=ó`\bm£F‹Ûy'ßk…ùÕÛñ†~)/ÃxÕ/Ãú“¿9¡¢ˆû¥LŒöüå·-"Ã^]UcþºžtíÅ,º3M}ØãÏõ„ÂÔl88§ú‚Ó¬¨²´û>ôàiÿò…µ&¬B¢Š˜¹ÌëÕ§!Pþ4oÅ[=¸é¨,βgjzö< ŸWÙ‘iÈO]NÊO,'Û{:á\Ë®jûI´utÁÛÑ9t’ÿJÞ[ÕßÔêØ)®¼núõÀ)oÖâ¢Þ+¸Òk&¦ÏˆEÿq7`uífVdÿ­"‘Ïör#ë_3‚D#›Ñ:—ð¦®`mñ;Ðn»‰{W„]ÛfcFY:®xU€z·&Ò’+ Ôõý?n›Ÿ³&3óu¯y9žäÑjWTzìÂ&¯[Av4 ²…Nìý jqM=ï ]÷(ºëˆuÚÞ»aðc£³µ#Ï̺°Á)‹5¨º£iJ&y¦5îˆ)eÓ9æúÙ];²6ëÉ”ÅAlñ¦wôç¥dÎgÖœS6—4‘2ú´áFWÒú§èSLŒJÚIâ…Ù·[5ð[o"{¥rßׯ“þ„–,߆ˆE²:QùµzÄÿgŸ¥ºéïñÏÆñÀëË®h0ùÉÿøAž»¨`H’”>Àç)»¯ƒŸ^Æ‘¾O'Ñm’{‚<ÈÔD™tð('qî±_¶“°uâd‚x8›¥®BÊæ/Æ“öÑü¹2W™¥ÌóäbVì¥OÔ£R˜àð:rc˜OrËŠˆšÃ.fî¹µ+Dz©ªGHà!Ö™?Ÿ #J·W³ë‡nBï'„è†ëô4¼ê~…ÎOüÂ5xß‚ //€`°ó*#iÝ×ñêÌ2²ÜJ˜¬Ñ`äíì1,¿Í…T­v_)[¦væ·Øµ\:Âÿ^2¶´rïz=_‘ÞÈíÃêï„íš~ÓyzpdJ>˜™•|*ÉÊí¾Ñdÿ›P;õ(:©cà±ì>+Ë^§~ƦU¡¨ï¢Æ¦l²Æï´À7Ï„>¨/€PÃ8n´Çzb Ø7 n¥Ö>R!»œðz«;®=ð KÕ ë.ŸªŽoàʳéþ-9Èy2ÍA”‘,l\É÷ÅcŠwñàB˜µZƒ\pŠÕidºÞ óè'ÈŸ£Áþúr‰Ø;†U¦ õ{Agˆ»ÃFÓ‹ÜЭݤ­êªÚïf_œ< }cÊuGâ¿àK0äLQ#ËâóÙÖ¿¢lÑÌñøß™ÏÇ”xá'§»Ð-zQýÔ>¸È­]uÓÚ8=óØ ¼¯o±yÃd<ý;Σ Ó>OjÎQ*1‡­P4" ™ºÙO!£ÕÙrR Çš¡ÎäÌ}P5{v:çÑQÒŸðÄâjNþûXÖÐ.Ly“Qx×ctè¸iû'CßU}Zë´ž@~4̰ºJØl™wÜøêuet#¼ËþÆ©k}ñÕ\5õ±g¦dOzc’`ÌÀcn׽먦)áü ø2”ù¼†Ê)|úHöÇFX5vðÛJ1~ÜC8û%J¦ƒ«¨6ªÄƒÞ–»ðWN‰X$Ú²Dù$¿N,¶d›Úïãõ-Ô¦…Õú͸7¬ï4&ãìU—`uÔ7çÌu¾#ñoÛ¨Hyÿb·GÏ ×L“cƒ7µðníZ6¸{41Ô;M%ì4Iõÿ#êËéú¾ÿÉ<ËL2!c$ܳ¶J¡4+E“æyÒ\ÆY2 ¥P¸gm•ŠLE4h¢$Cs’èçý{ž¯Ï_÷yî9{Ÿýº{½Öz­ý¬»ŽK1/‰ÝùÖÄñ½é–Æö•£«ôÈ9˜îÿõ^üx <6.„Ÿ ¦¸­Ýu~› ýä›0¸LœÄ+g@)ºØíœ«ÕL´Î76ÿ·Õ‰)b\”"¾ËõiUð4ùø Ö,€×dæf™3¾ûx·:ì‡Úf]BóÜÝ´­ïçxcGLå> îü2ŒŽ b6Í£ _Åc¢ú4æÙ ¶Ç³Iøò7Lxÿ¼´ÿ3(«â—U ÄaÈ–ll™Ð?'~ñ“DÁgõ!•øé0«¦·¡6ŸÎ{QÂJ W³6ËëqÖå46ðc×uþFý¹£ŒGž<>Þo÷cñ4a´/V +ƒ(çÙË›ø˜«Ou­é¡?±`ýcmê³ÿJ¯\I ¶—ÐÄ[‚0gÚ|ê‹c0–rˆÖF²g¯E¢€¾/é ZF4Ÿ/D?={¼¾ËÖÔFá²±©dõŸkLÅ¢4r·ã2û©%Œ¬è2$[¹e Îû¾uÒ(?ð™1ÿO8á”—ÈÕ­y»o3OpÕuÝÕKe1uB4+t:Üí0³ ñïÎÉ&X04Jš·½¿léÀívvn{*£TwV"Í-3A3X“ºeÉÜíäyÇlÒ× WÚ\h_s"¤WÜ‚!wFÞ ùÀë£~0­N£‡Äȧý‚Dã²-ŠY’Ñ#±zQ ý°’·œŠK_ƒÃÿé:•B<Â×y®B^Þ,"Ø3çY¢Þ†+hðølR%Wòs:l9ÊK…ËC¡´5ëî`¯ñ†¢Uˆþχá?$KÙ{°iÝA24iåeSêm¸„›f#M‰6@ú{"¥ò†[©š‹Wâµ'ð?·^Žéëßßr,,­`¾ë›ÒÌt.'Òõ:ÆŽF å¤Ò·zõáí0È'E<_b‹¤aîÆÏìÑwjbfŠ^ 2ºÆó_Îc¶2èx«A‡ŽQ­¯ÇÂ’sø.ÂY4ŽY‰BÂþûÌ®u€_gU¢Ñ¶vxèy†ñ´úމÓP'Eºm½ê>\†{KΔùß1`P—ù×^ëÐûŒâØÆ{¸§â,mÉrX˜wã2a¹°2¹§ì‚^é 8 Úƒùo2\®tèï`Ûñ¿µÔÜ–¦aèÛ[GÙäfWàY4ÁÿIk¯²½ÓØßqøbÑwôx!‰3ùP/u61X7ÎͶô­Ú:òÄ3‡l''Mã1˜Ïžì²Ë£Ö•×ÈÊî$¸¬£E6žR&]ƒ¬ø¡Ýìcw^£°ôYqsD?!i*° ÎÐt+S*)jA$2æÜFLUæ6O!•<Ìx, ¬¾yC–fçèÓ2±Ø–}žÄD¹Ã¿pEzú´ QøRŒïsŸãÜU¯¹5?zá¨ê*콕ˆ÷|$Ѱ̟ŽÎɵ°yót¹¢OÛ ï3ï?Yµð©0o{ÚÐí9_@KW£þÀ_ôH}Î&ß­ÀKáÔrÇÌp $V†d‰Ù<{Ä Æz=Q‹»çÔ߃‡3´©š³+µÙ’JNµ$âþÞpì„}š²Z^†UÛ²à„Zè5'Ûoü–Μ)[HF»!°‰¥-xyi1n W`ÏÉØé0%Ê’gÅ‚öâ;m‘ºÄ|je«¦ÙïYú;|mü†+‹-é”mÑØzΑ±ÙÔ"nÂdŒ$õË‚è±E‡¨·Ê.ævðNúÌȯ”ïc_븞t{ÈÑì€S¯ÏÒM)çØàèÓÎc8qþ뻤‘Ù/,‹_FâQ\¦ÅyhÒk2Év Y87ŽäÑ$9a ä÷•jT#ëg'Ãä´+ÕýaÄÝ?,lŠÚßCPçx&9jû Bæ¢tÊQÊ9ÒGb¿bÀ•Lfùðb“Õ€nÙAÈýúW[‡æØ[fls5W`ïîѦŘxËœÐùޤºá@ÌÌ]`Ú‹‰Ï×mÁ SGNÎ)è~yŠËîÒl…ü°YyfÈÁ•óRb#ÔøÉë6£ÈnNz°zõZÛWø=ÝŠ]?]…\ª· Áu›@Ã_ÕŸM…EdÖè¢yW‚.\]ËüiËrkŒ]㪌Ïn'€‚l»I|>¿_Žš«Å9Bx©ÝÌ»p°f“G ü÷8v²óáâÃÌ–Ðj<é°†„7¢Äf_¨Ù= ̘ ´æ'Gëï-‘„~fÖþxy2n3Ѓrû~LïÂÐ>Â[éi;OpúÒ§‘üà¬ý½Î]†§M |Q]†'6´¡¿¹²? ƒÅ¨«ä%œQ(å°éª#•Š•Á±b_îD§l½„ŽÚSÑ¡PŒZ¾6Á;Zxl¥mÈ%Žj칩Ëià°nZY€·ÇÙU÷Ý©k] GTVú†²ÍøëÆÆqL)(ƒqx°© ©I"´Å?€´Ã¶œDƒö®ý¼hø¿ç3?lnr'Õ5c½;úì‹÷eÁ¸Öb=:HLøõ¥KYØ©lú)ð¹-Þá²TõÛzÖøÛ9®3SÎÌ7bÙŸÇûAíá3üf?Šy2 üC÷ÉNA:Éñ*îï¿ëNÃuÉÅì2ʇ7SXöŠÖgFEk7P~A² $ÿÕrpóx,亟gwüšO]LˆÅö 4=ÊÕþqm·RŽÈ@ËJIbº4ˆ#â,Æýû¦žiZó”$²IJD}écfãâƒøþG(^;ýôj®rJçò†ï°4\„©"¿>öpœ·”CYÜmàïÓ ï±h®}®‚Ðõ^’†Ì—%)ýç >ÅèõŸbYÌ÷ˆïÀ¯‚æj“‘©ÚÈöÇC»“(vO!.wdá¡ù"v¸[”¸˜J…©Ó[«Xð"Ó»+1ü~>7• oÅÉ|…/°³C<™E^ÿ`Ö=–C5î)ø“_Ã^gº®=ÁÁ奨T+L®§T`U–¦(ÇÛõ÷У?ù¯nz̽’ þ‘ɽ$f‹–[S Ïߟ譅C——’ŠU7 \í.¼øù’³Rà‹ƒõñ!ä†g¹a(²|É2¥–®¦N['z¯ Èʇªð¢VF—P¿Dßœ Ó ).ö™àÿ‹Í‹ðÅÝt°ŸÓ‚3U;ð תUÏwV³u‡Dû½í}ªDóê{vÇïïXRjKndà»eœ½“B걟ø¢=ŽýëÔM:k ÑŸC_¾û‡·×:‘ëVÿ*ãz•ŒúaÏFøhc^pîµ£ÃßF쫾Õ®8û|ÿìÑ$'›öã­½Kèl/;²êä ò4ÔÒ´2|~v15^h@2oøpç@_B‰’D7c+cÛ'M#d¢\¾=M¬ 3O[ù3%Ö"DáÀu|ÛÖÿõ‰žzT¾ãUÁYa'âŸüåSðS=Àa™“˜›Ò¶3Ф¹[†äí,d¨— HÑæF¶-Á}#àlu†Åc°Ûu›=4u®­²\V‰MŸÊÈêãdÞ °Êé/þµ2!OŸœÄÇXÆLC˜q/¿ ™&Bì¶=Sàz®y¸œÏ_Žoóþë×L?¾| žWPýF‘NÌBŸ{ð!ð# THÒ€QeòöÏ*6úðyØQ÷ ZyÉ_ùÎjW÷[0KS›5¶Y `A–ÄJ‘éÕĶÄ<ãC±Êe W#Ïô¡R´dîŒAAW»î­Ú¯1ÿž^= ´,™c¿þד™óvqû²ð8ì箓&ÄÏ‡ÜæL}h.\ª‚ßoú3»ËDX£rnúô|¶x£8Ýþ.¬oV£û‰÷¨nAK®1"£{ñ×îÍÚÌØ¾~ÌÉ.gÖVV@ºòÓj9¹tPËèsô¸Ì¿/U¿íŽa§@·i¥èÿïŠ4q¿“Ål )ú÷j3ª¯» ïÔÕèä7á㩨 Ù=Ç3Õ@9’Ð fS¤éSAª_ÞÅöˆIá¦z5ªöþüxcÿ¿÷¿%:`ÞÜF‚ìE‰pM{´"˜u'¦ÃïtÈèÌz´ûÞ…ï„~r=–&«z`Ò@úʯ翚e‘p€f£l¨Ç,¦uh•0¡âs¿âÎÊ!lˆZSméóÒ0ÐZ ùßü+¯¥+ðZTgy\6'iÑC¦ "}\ÃB¹n.í‡S»áÇL;È;yÞ¯obíqÕÍÅ )[þ/ÍIR÷[Ø;}/íØÅ~íÛ‹§Ûá29-FeØ‚à®tÆnµ3„0wª8ådôë©Ð+bàô^Š¥ºó1°NcÂþ÷jÂÎ7uðNFË­ß ê¹Ð×à)é_@úW>^.˃»¬áI›:Ñ?ÝÅÞ𾊋 |ɧ#˜´ù·Ø÷ ³¾W‹È‹`7›^e} ðL½Ê„ý÷ëÀ‹¡™ô3òDÅ̃[b4 iI/üS$æ ¸Ž‡žØÏLoŠ'î¼2ÔýLé’)„5üNd©ý.hr¥p`D¾è\d Þ‹ÚöÔ-–Vƒ§ï,¨_ΪÐþ•¹vßžáÛ$H Ïû3}Õjx1—ÕŸ2j^* Kû/Ž´ÉbLó4ªb¾™ŽnS`$å<»D‹¬æÅÑЇ¤­&4Èg)1ÿ~çéLØÿÕ쿸Se£yuï¤lAxâƒb[ÍÙ4¤—±æ ½°g»ÿ:Œ{ȯžl€³¤ ä»Зý¯¦'á^1ÊŠÔ¥¢ü'#õ¸oÒGæÓ/søqTƒìh‘‡¸{å ÒÏŠËÅAÃá$ØÃÕúÿ}›ÃíŸâÀ»äwU"c˜p޹ð~‘ƒþý ^µ›°©CÓqù‹ ¦îq'œV9†n_+Põp*ì<0—Î\°ŒÖù²XãúŽËIÄý׊1¡:Vö#'Ÿ–ÚóÎÄå¡~ظc +ûMöæ,§{ªì©˜T+ç¿uý~²ÓÕkÝÁ´FvÏŽ7|ëÁðéP]nO/»Ob²¾$âcÝ&8×+Å> a L˜—ˆnK…ÀEû}Ãìü?T«óÁ·¢1cEèr`SÌϺpœõ>™‡â¼Í ŒM†%¢÷˜~¡f_<,yÊÁÄ:k*5ëK¼ÿúù(·r¬›» o‹àfØÉ¢ö½¹@ž´qZNFý¨,o®äå^4r­ú­D49@ÆÝ&æs¯ -Ô c–©Š‚¸A?ûË× T’ÀZY™_¦ÝO¥ÐyQè”Ý@Á““Æ…èÿú_|ßÔ‚¯@€É‘¥ ¹“‘ÿb+-š„†6@©HRK¬ØÖúafÿ׳°èd!qÚbÀþN°cZx¦‘»«@³£:5‚ÚFCò«gq/"øó¶å+‹Fi§ƒ˜š…«ÏÁÛëúpñÞo¨¾øž‰ÿr¶i‚tŒ=.¦@«?¡6œ•ØøttèYØ·Ú”¾›¿‚wüè‘$˜b¶“^2äØsªU‚Š#9»‹»7:ëGDÈî‹Ç`Æt!º "’Þ˱#Ë>ô€˜ù ²Â@ ­+J…Ø\ìjÒÎØ/ösµy#;±ÿ‘ŒA¡èãÎÿ_ŠŸ#·°_j5@Î÷†Œ ñ¹Ã¢ôÃH|©§J,?7ÃÃ%aŒýòï`×gCyݨ—À¤’—9'‰0>4"ö<ÄÈO‰~>›ƒ’ “iYƒ½73Ï×52«§ÿÄßf3S-¦cÀóàÿú4#«ÊKïqþâåG»É¥VÔo½=Óû!×J—r¯n7¥:çEè–ÊPŽpž6á<æ¡‹gÉ‘7¬PÈv´ÿMÁËéè`ŸA‹Î¨BÆ¿v&¡¥Ö_²Ä›.û¹šàoouë™õ0Y5ƒ9í;ƒÌ›‚yŠÆøï¦B\ÝkÎÝaY|ø£„ÉÞÅÝÁ•©kóÙƒÓËaË5ðBN”~-i 4#–ÞT¸‰'žñàú[éð_èÕâT)áiµ Ûñ-Xáâô!æÈÞ8ƒ_t‡™¯+oÃ@qd’¼ÂIÜö}ú0_ñ9ˆ)$P± ëÉŒEÚT ¦+êáé‘ÚCÑx8=YÔ™ûù©j`+Ò÷ØÿLž˜n Å¡K÷Q¯¼wBÿ_-0‘ظÅÌÞKöøÑÎ}ÙÃøÓ†…JàÒ'g0S᥽Îo0¼äNûü$îá´–=4¬“•]„ßža-ç)>áÜ7"$«­—ÍðN€ëŸŽámB·rZ¸]‘úÚN"«.ÀŠãÁo—ŒìAŽB|»48í}‡nœ9„ÿÂÜúöƒÐè¤àÇ£¼d§«l–K œØ·2¬IDܱÖ|¬ý µ>ʤ÷ ‘º×G¥©ß¯UøÔÖ™¼z‘{€zèöÀ¤«BTYVšœé=CkO»ÒbÑsúo‰ë6ëÐν-ËÈ~êJ¾D§ÀžM¯@WY€J.ˆ&²W´ÔbÞx¬”…†…ÏÑL4eŸæÁ§˜†g _CE/w§ÙbÑÞû±÷äA¦¥my¹ÌWΛAÝûop"O>ÂOn…Œ˜}<³g7$Ô„àJíÉä»l8«¼<•}æý>eUÓW¡zôèª;Ø)íBß«ÒÎQÇg§€çLYê$$JC¿ß!ä6SAórHž|•2®n™Õ ñ>DórÒ<¨´"QP¥<£+0 ÷×o"Sä䎛×úäÍÙzFp³ }ýTÓw+„><}­FÈû°ýôç/‚qŒç?>ûX›oo« çÒ[…~Ô*"ŽÖ''ààæøi†žaѽx¸á}æ?ÍÞ¶r²ûl¹s ®‘¥=š 4p£:®Ó¤œ9Épcø9 ÷2OŸÄrý| ¡ÜâórÛ èÛ ÛOLa:’ÝpjK(y¥¨Áì¹Û‰Qq·ÛÉt£ÆM“— 0n$ü€%‰Õk„} y ”ø37õµ¤R@¼÷.ð,0Âr[CÚ*á‹"ùYÐßp%X¯p^Xc0Ä–Ô€ÕÃ5†&äŠÝdÊW¼EÌcåP#ýÈT™¢êëoÒÿ—ÿ,ჹ‡ƒqï¶ÅôacÚÍ·¡=6 pË·© °k.Éó¼É¥ánT»ã!3ÛÈ"æ.ç8nÊÂmó XͲq}tm%ye,F 0@8V} ãT+t°Ûó{¡Ÿ? Ïn0#w9k‰§/Êz‘¯Íâ˜|F˜ªóÔÀR½^¼›È6ˆ  g!µ™Iÿ>¸ü¨Ž´ Ô „–Gbrxó#¤øzß 3ÐÁŒ¤ß`D¦"aÌtmHÀ’.¥xëeó5û ®ZÃàm"“ §æ1J:_…]Ë”ˆywÖ¨,‚ÈšØ —d¦ûc&sv~$™/ Kçø Âǰ8ç:ít¹A#íèφ—q±¯ÑNuô¦'ŸÞ9.É8¥˜Ð/ö{¥'|Ôa‰-Þ9Òâ4,7åé½¼F’Í—F_é$Àv­Lòp»Q3»…†rUäùÇéXuê;6îëAMc-â}2ÕVP²5—Ùë'Io©ê“GnV­wË;—iþ¶£4Ús5¡úD4ï(É•K£2Æ6Ä<+‹pG?ãáúâFþT êˆêöäD,~Ô …™ Å\Ï¿ ÜE˜›JØûQy‚ÿÂ'”`ýÕD8±ò*ÌTšCÛÅÂÐŽ©‡îßc¸ðÜVv×]qâ]¡Šätùrô= ¬Œ~º"A §¯f˜F/ÙÅÖWáø1yÝÃGC]–sV’ýZi¨ÐnƈÖdQÕX"Z£ óu¨¹y,M-"K»)¬nÆ¥{SA¢<šæñ…ÀÖ×HŠøyÖ .7Œç¦¾L™šuའr Žñ¯UÜ ò_à¬yÃX­¹—YA™³õ««mZ$‰œ×a°¿ š7oàÑR ¶™LKšbv¢4œ¨´$w,7blÎc¶üP/ÄͰ€Ï*9ó4ֲߎׂãÑm0˜e‡>'Õ_£µ¸d[ð.ÜH‡ÅdÑîÉvž÷0¬ÌÏ`.~]N׿ÞÄk­pv<ŽªÖiãýÑ|l_#OË¥þ”‘ ¼ÔoNLºÍú4þe}l™ðÇõœøZ¡ˆ¹ñû=ðJÎÂ¥E¬Â&ð ²ýSH¬‘ eÉ“Ì.BLe›ªf'S§Ž…OYM蟀ÖX¼ÛxœD‚!îžå¦Jû~éåÁt¡¢!Ø)ˆPmS1`Z’U¿CªçzSÚtˆ,1^F¶é~fRºažÕbxu$†½V댧ŸŸ‡ƒ/q¬×”ìè‚AuêÛÄèð£ï¤çŒº]HÕJÑ‹Cµµ‹1h¨™íݧHO¦"NÚ@7í“$«Å pÏ¿ |)5ƒˆÏ¦ÞSV`w”½qà ]#ðïøBvMñvºˆ»›2‹>CWB[h•EÄ‚©¹‡O⌙N+²|æpû#òÞ¶öê+ìKFÕ7¦è¤&Fr°Ër•ʯǥê7@/ o:I@`ïøŸìÂkR`ûiôÈŽNfˆß ¶•¬×»]ð°Z˜æÕà’rI×+„‚…™˜;÷$tŽ8`à3Sý½ O¹ë‘øý¸;ïã#&F÷? ßT?ÎúTB3« ¯¶8ÜÉK¦4‚Ä•—°,B²(÷Ö'àY¾y£m@_jyÑöëaøýž8 bœ“¿OvQ1[&ZPt§×A·âØæïNßâ®Dzó‘nZëM͸g04â ­1t*õ‘i¿Ê,9aNmÔš0rσjïs5àôÊdGŒkLܲm 5˜%#ÍàyI™w·…„.À÷öfó]g¢p[œ^Vìcª+¡|Ó&vòµJò9†ò‰-dï}ã¥ßÛyHÆ›gøõ‘ã}¥ŠÉ‰ŠÃpÑKhÐrWë÷à•Ö vеYÍãDêf‚ÑŒœ³~ ϼŽ'³%Q3ü0÷Àë0ÐÜ/‹ sBSþý þ‹Ú|c*”꫃"vâê~EÈXaJ Û·“µ#˜ÝùŒ]øã#> ÎÇwÁQmëžÉØl(ƒðûØÙYp¾[•”¦Lƒ8 ?àf›T(ìÏ„ýÌ?è¾°èü Ö”¿¿|Àè†nöoBÑ—ñx©hyˆf-uà°Sw«?À–]Õèid'ª÷ÂßëÁÐÙ_ “ƒƒØ «i°Ë“ˆÿܺ‡÷¸ ùia²ï¸6Û…šÑŒl˜=(O¾©¾áƒú-L¡tÃyƒu¢ïà²#žT³‡»¿`èûa¦gò6Ø9:ˆüÈR`νµ‡äñß­öÃ?6ÿàØ«Á~Íʇà,c’Óà->ø{„/<–#/×AúM+FjGˆ‰ï‚5>Åh°$ž)g?žŒƒ)<°Pø2Ë‘‹g —E‚Ô˜0úUõáq/ðép¦Þºs‘»úëáîÃêu8`úý8ùä9ܧwƒ¦a€Æ­Õ RpÕij¬´Óã–icy髉ý›¤~Ó”QðùªuÅÕ¤;á±ýÂ|©¸20»¬U[yÏò_ø³ç6¸ìÎÂYo2QƒÏÂO.¤ÏL3àØ½ƒà´AƒöUb¤ó& ó'ô[Z’Ànr¼<—½»2®8дE_PŠã¦«Baó!a2,CæÏ>ýjj`NºñÐ÷šñuwàrSщø÷Ð" ¿ŸîeceÂÕ Kèè‘éë¨@^ïD‹gßán{23¸[–´$¼Âˆk¼ÌÓÎ;øñ°:šÌ b—FÂ[ÁgÐ{ú&.7ã%>ç!Ön ©ÉÆÈç›q$pÍ±ÊÆ˜3Ïpï†?pSÚ%0Ï6Ú£ ²Œðt”OH„•šÊÌÜ?Ú@h‚ÃÆã§Qû¥ ݾÿÎüjÁîŽ> ^¢7[cGüׅì÷…lÑþ60Ï×FùkØF¥©›äOVÙd?V/pë%9œâ£ÄDŽpßä2G'Upd¶ñR/˜‡y©Æhä¢@fUÿ˜°ÿ¦ßÆÌŸYÀíÜ ×6‡²¡ opG1]þ½šMžºëÓ•‹7‘£åŠÔél3ûþUñ >Ìêø…‘ l¹Þ³òÐÎgɸÙi|ï_}á L‡‹<ɶ½ÍÐêÊý^ƘǕ,£út7t¤YQ¡†bî†Í|Dòfüz“ÍìT÷g·o " ‰:œÔðG˜£šÇþÈW¢anžÒéBDÕCáñ,ì[õ¨0‡øŸ…*‚ô¼grDò°è÷v]›Ýè«C…MÉ”slõnM²ªÁ ­ÍĨÚ{Z{I„¨ž>±ÿó%>1‘î²÷§ø>/‚U Ûý0ܶ:Ü=¢Þ9§ŸÏƒðeοç`É^WЬ±¯’Ù•žßnbÑ-]ü(¶ æÎÁ‘Ä$&Z¹ •úܽ°š)Ãõ.92koÆS¿/~²äì:ÓÅ]¦ø E¨Ð“OÜ9Õ­°rg#F܉A½¹Vܹ›‚¼°œ÷Š)ª’h#â7¸{å]‘U•€•¿,ÀËáêzu³K_B”•„âMá,‡WbF_2N~3É<Ã}Èl¹öe³TuÁ&"w!>Ê^žà¿jÆeH PÍÍb$zïÆê¦J䕲P%—–áÔõÓH—½ýf-@çÛ†q¦a4ëý$jé>$[TIT‰(yœ]‡—è Ä{’ú^—ÚÆiR¿Ü䀉 óÖ¹¥ü4H+1mKÖ“úAö{‰6¬ð_CW´ñ’bw.ŠgU‚–€Ùzû#ØïõÄ«|á0yÛ¸?ÍhGߘûðâÆ|B—VÁ2?&„·l a¬I…œ­ä%÷L"Øïâ_ ÇA”ÙÀ¾úͨù2h¿c _)’Ò=SiÙ¯›à×1®.ÓõÄÜvéþy†ñÿ›`Ç Aú¥n»aÅ?´ ÝM"'s©åu"tò:ËL¥{Ï|ÞCØX£ Z»™‘®LÔ:hBŒ_4Ñ$¯làq=òkÕsü®Y뿸ŒÇò^P9¡Ä–LÍd²<&QY?úèÁ)zÙn2äeò.OîÕŒƒHÔ?¼Ù/ÖjÑí"ñäÓ ö<ªy /RaÉÔŠû€©‡ÂŒk^eåŸ/A¯mL…OçÑïœyôâíSÀmy*¨äþ”½³Áê¾WÒªsÈ:ï>h¹êC’ã¾Ã‹.;FºÀ‚X ìžàÿ­ær˜žbZÛtœ‡oAAŸ w‚C?tÈWÇœ,ćwƒÛRA^ü~~1|“nc[G*]™ƒá‹À£œzè9€Æ)sø}kr>ݾ:³gg|g×X_fŽ–žƒwöQ¬íRh›ÑÁÎøÑ‰ŽŠSœ“ÙÜÈRöÌÝtªßÊUV)‡+™JL›ª,1<«I¸"#¨°t9×Á.¨=Ët>ž|ë8¡ÆKñš©}{ó ʯKEón`Ò­pó†Íð˜û‡ý؈I[{Q0f7°÷áaÚZ˜}|/„ç΂ҜVì×¾ ƒúGO³%Î&0Å~ êô”å)éã„|<ý/8ÛeïK³èΡ[ïóL1kýaŒ8žëV]bÔ4ã±KIbLÞ¡í+r>xG*¯"1‘d¯³~A¥ ±l`*M;PŒX/œøx‘­›á…ã|½|ç¼Ðsb•›³áIY¸f8Ú!uö[È] Ž:?XZ8Ëg‹.Ê×!È@‹¹·ü;×n©ÉÈ›Á,9Øúk䈮íNt^ÊðibYÎVgîN¬Ø]Šë㢑swŒ©ùé…¨‚©‹K7lÅ–Kž¬G Hó]™8ÿ)ª˜ #1p\Û .•¢SRfBkg9G¨1‘Þ ÊÆ&ÝnT9…¼êÞ$å§2vƒé×€ZM™Nè啬† ÕøÙ‰W¾Z2é÷ÐSÂŒ>áOÁS% u wÄÕrß[¦@ãùt*a(JÖ0ô¦‘Ö9DS7 6ÿûï_â¢õ6L†ö0L_F”db±÷üºêUÓÓ;(ENç¥Á¶;ė%Pëz r½—’× äÀÓbt¯(À–ŸßAéå¶õs4È1íœgý ?2|¿×R­–ydyƒõ >Cãuk!èû ssæþä­w¹ýYËØž7ÑÔµ15>È0ó>t²c²UЖ Fw6 Ï‘íääÓj¼£‡AûkáÔ©m$yQê“ |Ûx†&½iÄÀWíØ0øŽT¾a¸qO€ –&÷Ëá‰ÕufJ*Ù É2º4|ÛLRözÜwLÂMSHF” ½hòŒµ¼½ŒžZçFS޽‡Yy[©èÖA¸ä[,\ésûG`Ÿ„¡ee˜!¬V‰¢àÚ ÎæêpLÞ”:¥ÛÀèÆRü~Ö([·™/WYQà²þ $ðŽ=òI‹FøgOø¿?ïxéʯ籪°5–Ò´¬8vV¹; øõ:¯Y³¾Å8º{\w 73üÍòô·¯4‘q½Ìµ1×£!_0‹¢”0âî5Œ^"IonêäÍAN%[éæÇC´‰O$y¯|k\—<ǰétÓò4ÔíËÃÖÁ0hÓÝ&Ïó)]@«kšqö‚P6¾ýëQ²¾JŒ G /=<¥ÜÉÀ Yú ´®¸bÏ a|½ÖÞ\[ gëUÓtý\è=‡^£cEÀäŽû]¥ÂžN· «£i‡ æù±¶‰üwQn(ê~´†—w7Ð/x ¤Ö‚fñ!VüS0Ù<ÝžMÿ ïDï#}$ ÌDàˆa”‘¾J…ÌšYZBUÅBqƒ+ËÔ~ï‹åPqÿïô¢ÿE°}w’\ÝA×j$-`Fô"›9b½ÎñÌ%É ¹ ôT‘¾Ôå£o ™^|cKþ ¹b¢ô]×¹d_´Ôµ†Ï­’$‰Kèt‡R˜rf*-Ž–&.|ÃÕrràÏ*ºU¹ ‹ só^ÐØ¿²„s’ ®¦½ßCá·…<¦Ê{µDqðÙk>¡ž¹e¡°ýÝE6&í%/® ³ŸÓR9ó¯~‚å·‡ñÞêzÊîÇ3§Ñ¥o_Ý ðkÛ‚ò»a9IÚªp¢øf2?>‚‡ž¯À-É•Ö}Z‹§â£õcÔEsüÓ¹/_¦&^8ì(Ñá±ô”;{X±Æ•£Ozd’)Ÿ—‘Úu“ÃŒN0—+Pn6Cmƒ{hºñȯ‹$6ƒ— D«=5£íU‚D·ÞšØ5gÓ×¶“¨ÀŽ:SGžzœõ îÍOh¸Íyâþ——Þºý×9ýCÇȇ„çÆWöœ:.@WÉOào¼àà 4ªâ€˜6mÛæ1®Ï™– °=ë7÷ß6 ›úBá˨yxá2½;.áËvÂQŽ$Ù¥¬M¯Áòí[AÀ'úâ² WvÉqöOèRÆn&Ý‘"L"ãŒè©s“€R =J3ˆÖÛ;øb<ÆËì%Wg}Cyæ)®Ï‡ßÁdA&˜Ý)ÒWr˜)Ö¢cS/–µÅ‚íÈ^x¼³”ùà >²Ô$9`‚ÿ³¿UâL\TÇ®uZM^Å;²§uáí×QÈQ¶¢Ñ:40Û•*”ÅÓQ‹Íôøä;Õªšop×ä|òµ†äæQùmSiý¡XfwÒ5t[ýΤÊÏò lK"*ÍZ w^œ$5ï„™Ð'–ï»%½ÚüŽIɤÙdr›ÀäÜÛÐ~w1Z¼^ˆÊvÙ2v’>É<ÀÎÿÔ§Ù‹x÷Âw0uªFC›]dþØ'pêø¥³ ÅN„Š/ˆÃ;*@É;¾]ü‚Qùʹ’ˆ»‹ÂhâØWü·ªÃ9ÿ¢spc¥4>©¿E–Ä|ŸØÿÍ~¨·s ]xíóÔl£ð& Ù7b³ÑM¨S¹Š—ÛæMÏíˆ(7uËÇ÷ÉtÅÁ òå¡J¤)ù½ªìîà^O~…R¯Ò@ÊfIïÒZf„[ÇZ ÃîJ#(o² 5KÔÉï“-ÌìK,ªÜ´_½|9¹'» ¬'…ÃY< ¹ÅGOUTâÇÁ¶;¾ˆ­ZL7î WnÅÁu'z¶‰ëQÚŽë=v"çŸ?ݲå7ÓÓ®OÚl§ÐøÔh¸uhÜG%ÚÐõÒ¼°@N¬ÏßK[ŸcôñTÐÐaRŽ®ÂjïÉôÅ<ÿ ý—oiÅž)uÅ;ŽÓ)§häTOfß«mÌ[Aeªç]‹62¬ÃjCjà9¡ãywÎtu¢òˆ±°x z³ŒØ †aìŠMíœC³»¡õù!üœ‘0~ß ˜êA¬.Î ?ÝLHƦËðE6’tåZЀ2YúÌP&]ÀXµ fz²4ž‹WeÌÃ×CB‰?9zå ,¹/@ ÿ~ÄÔÜ Œ2žLæ(WÀeg~R?ü…g’HGªÓ(G;¬î±W_L£ŽÇÔðâ"/"÷9šë¬ðÒ¥t¦+µÍ7ÞÃ#ÒàË—Gø-f &d…÷ëixâû üê›·q¼'qñ6_~ÕÅü·„ûì¼.îöºÖS¾€Ó’¥4Åð.3¼À’6¹òr¶91_•Új/§¥û ±ä!ŸÓüÌ?Ø…­[ûðøsUú­  [CzQÈ]´¬ƒž¢@–rŠÀ©Y‚4 3Q{Aú]êÚþr»ã~X%Ã¥+¦ÑîQ´cj„v%“+m·&âŸ|d2ãÜWTL¢&aG ÆlqIN‡]#oÑÛYž¬Ä+p¬Ê8ìèÌÕ%KUôɪ‚$,ÞÙÝæðÖá)øeÝs¬cϨ·Í&}·!çñb>w_ÜÁ§uƒ¬à‹vÙ­}[ ¯zÏsÞÞ,þÅ쪃 ãÕð…ÞñßjÆm[NÓb!¨Ë*Ä—Jþìá&ÒV"ð¥„m¼z€ü>Ê.ÕŬr;ö×Å~FC??oS~š÷;™·0'˜ƒ >ôââO¸+'”CÃ×Y¡™xꄲ/„Ÿ“Îámžwàç¶bBÿó-Ù /öÛÀýxAº}²hˆéôïb¦›µ‚C—}û* NÜéãeÜKõ‚Àw¿µzV™9§˜ÄGbqŽìÔ-€oóõ¨ùÛ`8fg³Kk‘?Ÿò¬¾Ñ3Æ 6ˮگ’ÌA¸Þwí%M¤È•}äÇö˰nNhØX3æ/1ÿS3÷ÀÒ߯G­ä|ÉÑ1?˜õ5ƒ,Í'vûçHWOçÄùOЃ n£íK¼£FíÝóè…pò.W zŽÄãñ‘3„'0é"1rW­S:áé”8cD»Æ5yp]I\³‹wGQá™w0"«:˜„ÂÑaÚ.ï Ùyÿç·•»…ûšALcÜ÷eýbW:AÉÏztÆlC:ea%ΈqÁ åxowìߴй¸÷Ý¥9“.Ô2 9•’ìsSt!;iwi;ŒOAÓÕ¤¬ËDÁ¢'úº¬_/Á°·Wèk”’P¦óUÑËeÓèL®nu.Ä0Gº`i=VÞ=_²ÉÈÊj`bÇö%A–>E{Ç÷À•îF1…½X´hG_“îœÉ¤yû=8$ßÈüZ î_epæ$È\›´d¹/wŽªÿÄÆo¹ÿx÷ø|?Øàc‹èÏGâdwÁƒê¶=R4êÇt ‚›ÁA|SîI?½ S7Ÿ!N#è;ús7ñ 4kG¢Ì_¢¬ŸÏN¦:]ædfÈRjþA„¾ÝüÔ9WñÁm êPÎ(ÀõEUle /]1[’jnÇêÉ™P¥ªO¦ÆK1«ˆË2!2ô=’^›ÞÍÐV[ìœ5·Ü'ôŸ†ˆ c¾÷+ÈíÉ|)Òä] É–àýn˜c&{™Qt‰åæ.ç'ï̯Tž—á%^<³Ibgö¼xŽQ3xé·„ÍÄÊñ„û1Nc–n…²Í hÂÂi´j`Îáq×øÍèêÍK·ß#‹³¶`ÅõpXëó`Ò4Üüâ>hd%Æ|ô€àvsM7*^Ìô¹ÂÇ­¨ì²X1˜ÊWñÂégœÔ^ê£3 ›\/¢Ùò£¸,$ö} Í2`ø±ÒÌ€Ÿ¬3ìl…©M\Ù¢¿Ð•Ûo¹ñP}¤ k”©é¥ üßO¼efèCÛä ô†£Œ]¤&/:yµŸIjå è÷Œ Z&y…¤õêã"0p{ëC£‹»9£r^°kk)sZ.-º÷¿9€%ûëpú_¢û$<¦^ãZ3Ênàì Úu‘¸F1ûˆüžvŽ› 蜫 k‹¹®†ŠðºÇz\Ó2%®,Ènz̹öÏEùÂðèêœþ/‡­<Ì4–. í{ÕiÐxíêE´–°ý¤çÎ!:2 OýÑÐH„>õsƒ™šræpà½Õ z÷/D?Xõýà„ÿçá•¡¦ª™ì…›j~•NÞ U›‰úÅÏtkQ-ðã>/›Fvêm! |7aK?xèaLíöÛbb¥©òR!œž½Dyn#^ Dÿ«™Vÿ" ÌY72|c/INí¤ƒd‡©0‰oÛO.Íu¢¥Ï&Á–]ß°àz™3'‡h¾’£5ëøèúÝÅlÈ€"éÇtø"Í*_X€›¾ARÞŠjxÆGã~V_V½‹*=„tE™â¹ágmÙDvŽ'½ØPÕîU¤´CŸi “¾#µ 7m€±YÒÍŒ¯“úÀ&ìŠ*f °¶ñÅDþçÊ—I-xqX€Þ}Ò¿63|¼=Õú^6¤ÙO÷AÉÀ,è:ΨŠÒ\eQöð×6ì—LÄk=Kh„”ú$?p>Ü,{ 3·È³ŸÛ_±ç/%Ò9OäéHóAü‚-ø´ä¬Dà±±Ù|›«^›‹ÃËn%«0Gy;`“¨<}ä7ƒÛc¦G§éí"[l4ÈdUAreI9ö¯_Š_®SF¶¢%‡¬Ø®4kÂ;¦NÚO+Òùùã~Ô݈žy—N“Äl¡LÙÎßsàPt˜ Ÿ‡³RY#…kXð8u×Ü€N×a¯’Å„ÿ;·£U³ÃOäÑY:ùŒ¹"/Iý 2¬Ã‰… TõHíZrÞÕÐm¸¦QÓ+:\{­0ãc.\œ¹‚šò@jÌ»»”Ĥ’Ñ´Ø•¿‘®×“§—ÚŽÓû§ÌP81ù>f»J[gÈÐÛå¬ÕƒAø¾Ö”$<2¢w ¦³ÏáB¬9‘™I£¯àý—ÝL:%ÙåÃl쿯l²ýȘ±É⃫ÝÏØƒé8ùV"™ÚŽÕë>Ï$6½õøtî<Ì»4¯¿è‘¯³E±ÿ ø*kÁŒ­õ°;^_'ÐO%'ø?ï¤3FÿÛ ÃÑîL²ˆ™éY€NÅ‘Øp*9)°<é2÷/P˜ÝûdƼˆmà{lžÏ%_>ŽáµÈop˜7 ”ˆ'J³:¶°!ƒSáߥi+|+>ÏCA[Š‘~pYf=í‘Ý !ç3˜çlI£Ð!²@’(bˆÚ}{zjº]ç{¾Þ‘Äåÿð¦·6¹‘+Ë.ä¡á½åÔh×{œî¨€rEˆÚÊtP°ÂIÁr„×6/ÑKÜž‰µÞÃ7ÉÒz ±¹Úäo“n×'z °tÙΠüUK2pÉ®wLÎÇ< lQÞèªEÇ›A<²ºkVpǯqBrO|ñ[Þì½/`àÜLÌj¯ÞZ4Ù; \“Sᑳ9¢x„eT®á[r'¯9Ç•ý°‡¶8 Ñ/Ì,T›Ù>™˜ôû+ã}Zš;å«"ãýf&ýýù!^ö„¹s0Á!Z“‡~áeuEC‹…¬–ô 0lªD£þÊ/׃·:¥éø<èš·›,Zø›YH-ì/þûi3:¹G7ÁÇMòsö5ÖûT“ª@V-e;Z¿bÉã­LSü[Æ«O‰*Äí›°ÿBÛ?èUÁ‹Çίe×: P‹ÂfØ7»‡‰[G‡TžãñoËXÞE²DàÐWp²ì@Çx Æ[éëú¤èqݸ™êžÊ$¯u©aj +ž£BÚ\¤vssÉ¡Ry2“éêwÖ¬çƒEDõÞ2ªâPßÜ‘ê²@< \°"³ÎVâ_•'˜º~ ¹p>¬N>FÞM°©è-$yIAS£'½$tu·6CnÀk”©Øƒv™¤#Ø4IGþ5tßù~Ö p-õ–·!g7Ñ·QŽ$f â­hqa é{êA:¯e¸‰Ë {0h"ÿ¹ú ß>édd-ûXÅ|YÚæ´ˆ8¦Ç$È¥S§>Ôß`Œ¢1«ÖÂÀrj nμ˜Ç{î(t:+âÛ#°ÆK›©KÃïvѽϮÀÑû7»EØ9)ÒtŠåsVùß4êøü<ηæÂ¯ϰâEûÉž Y*Tw¯4¹úåâ!“DÚ.q‘Bf™—;ü¤ÀáSñ8­'Ë/‹pí åé³:šö¼'q?°Ù¾wàø·¼³ ”&ñcŸÚE¼.çÊý‹‘kr >Õ¬"iO×´8Ož^TÂâ¯Qx&åºmr™ÀïÐüýhAP矑®9Œ|·Îà}²ã˜Ã0~rìQÝŠ<>?@›X9ý%Xs;‹ÍpÛ‹Àëâ’9“*³1W*àÑ­o¸É&‡•Ο›/lù*—‡àŸNœãr˜c²îä(© Ób+›qýú…=ÌÞbí5%躌EäìRz9“‡ØUN¢³¶»ÓÙJ8ó_óV¶ƒÝÕ«Û–¡YÉœõ÷®q÷_zkœú¸ãó0Í N_?9Ç„~T%nfü –ÙgÿrÉa¶w‹2ºñVäÈ~\k8‰°gÕ±ç9ª ËîLð_{Ûˆ·å'u,¨ÒÞ.ì|\IµqÈ™§Éœ´Whå1æŒöûä@êçmÄÞî!›ø “ŽmÈBá3ƒ øƒmÑ$vt/}} «Ú3©¿0-ÍàiÔ ‘÷£ˆÝótl«I¼‚6æÅܹ_Ù_ÑÁÉñ=š[æ³Vs\ }å²Üü ®2ò¢2{: ²¸¯Oá#«¯í ú۾±‘»l°³y|{n™šÑµ_øˆÝìV°·¸Í¾:¥Â~ˆ®…íüljÓñq47?˜É|T޲§í¢5ÌÚï¡p/õ"0 Ò´8ûõ¯¯?Ì…ØÖ –çÁVìÎó¢×}µè›ø>ênUÙÒ/4c^¿ËÀ‘0U%zD´é¯ 4ä÷$z©Òþn=Ã6 :³†É:0É|-¨~È´yȾá¬x»'t ÇÁ×W„Z®?ÅV¤D`U’½Ü9—è)ðS¿-kØg³ô˜•j¤õÊbÒ›rǶÅcÁ:!údz™]ù¦œfÓ¹ºåððÀ ܳg÷K?óZc'1+HAÏRäóúÌVG}€7ÒPíÉ1¼ù`*2\ fÂ^äܧèOM†‘¡itÞ{.;tÿãzñ&d\ÿéðøïOƒ‚“ÀÜ)›•—L…´µµ˜¾ŸÈø /ý…7–IÐË‘=|$œg[­g|½lÝi½…ÆÜ¾yõh™wÅÁÞt~ªJß_—>lÿxžÞ‰ñÇóÏ5è‡JAΣŪ°¦G…¾ÛŒùŸÅ‘•ÞLÍÓ¥Þ ±|[¢Xâ” 3£–Ñ¿%]èT°’¸I-§ó]’9ÌŽ½,Ÿ´£¾-ªu0»G*9â5/ÑÇ[½_„öÝÀ¶M  /Q?©NÓ›÷hŒŧÿ^[—PåG"ô¼t!*^c‡Rÿ0mW>±.½‡&øßå{”ú/ãÂ÷÷§ØHr¥µ¾z†ÉããÊ’­5×™¶•þ°¹<’W^EÞ¿9%^-ð0ô ÎÎÚ@M}}!v¦,q8h…%üçY‡G¡Ðjáʨ¥¬ÇÁŸKIl…'ÓÖŠ ©§j ŽfÃå&8žÒ"ô^ýTØãó¦ékè¶U)mÊ9Éc&Ü9Nå yú©Õy1ý¹(+Z…÷½µ¨ÆõÙ´å9¨æ“’˜ñk‹1¿k£0µE|Qõ£çKeVI™Q²÷3æD]Æ#/Ráªl\Ün‰V­—&øUí dw>Ã)²üÔéåE®b ·¥Ÿ­<Úú9›áâÚ1,<‹ßUž³÷Ší‰ïÐF|Q†¥öOHŽÀ¼o0rܘüÈ•¥zsà]ÔUæÚ$k²~–:Ž*æÂÖ:Q¼#µŒl÷pR*pÜfËGØ–º\`ÛFà[öJâ[v–Ù¿È z§bÞÚFvõ¡oÌóè$¶-`ޝ…$ýqf4cXÂ=–[ÿÓD(cÅK{¿<`‚¬ ¸!¼¤…÷L}ZŸ¥ƒ„s5èëÅÃKÕ78ÕZŸj…0U<ô¡×aÖÆÓœô1áø_¶epC£ãA¤¿žß¹Š³ô2‰€#%[I‘‰)~|„5^ó’Yó> –ìL‚W{¦Óí¿y„nÁÊ'f0÷èÆ¢†]C½7àNÍë8L3àÖCZüÇ C‡`Â[Øë¦@öý|­‚UÈ!0;g'ç/¾IÕÅ•ü¨~OË_p¸ÀÕÆkþ²ª/`ûa5|ùü:9DÀ9z¼®¡@’ž0ásè4^rç¥)»¬¢¿ÊŒÂ´é›Au6e3§²å‹çƒ^u9z¼ÏúðÔ_"£@KÍLPä¬>óê¹ÓÿçÌ¹ÅØT-Ç3rkc"𯞋Bqðˆ¬ûjN<Ì‚‰†â1ü0ºœ‚”©=ÿRR&ØŠÆ Iìós`xò/_KG¯„µ&Øþr#ÜurO=r½w'• cV®Š2ÞMJF3F†tE®ÆÝ[ÈþÏUlý÷KlVÀWü¯~Ú”]¡µœœ®”¡‚ƒ ÑñðV:Ä;‰†^ݶ{çÐÕvÅ\ãFôËkÆlß^nøÉ{ s§†Ý©öfØŒá¨iÖÿ.g}yè@rÓÔïÏ„C.þ(vö.ùiÆí[,JÊöþ/þõ|Ïg··ÿÃZ}²{Ü÷¯n…ô~?ð,®†KÖo9ÿ¶½‡Ð¥9zà¿iÎŽ¾ëÌ6iÒ¼·–,k)ƈ¡™³Y‚Î<Í!¾ ¿X£©[à™?•3pƒñ²Üþ;;÷!îynKlNO…1×¹…ùБ}ždËRaë;8;žÂy'½©Q̺c®Ø,5—6߸ŠßßY3øgQÅšR$òŠb„csS·%ÖV„v=ÅHÕë›Éñ‰ó “úÑ®œå Óó5ðïRÇ}PÐZ‹TÜ” û„¨¬ÚÒäá7qþ™±1þ !¥=»½ÌÃe§9Ë^‹“ÖzMàZˆà‡3mÀ̯dýLgCâ<öÕØL¦èÞó¹„çSY+JÒÄ•ÁUõ&Ú¨ãÚ?Gà\W:~INÃ¥ƒÖL]A¼!ä\”*:÷ÖÁžÛ˜ÙkUi§„79øn&lªÄ´Ñjø²µ÷D/¡‡ÖE`Ì¡|Ìy•ÂJTÂBcqrïq{¤m‡·æøø®b^¸ûäEŽÔçЩ5× ;4]¾j*½UíAõ_.¡ï)RS¿Ë°*²‘õßõîyDà–l ÐݎǫŨqï† ý/°•Á_?S‘ÿq9 Ãý¶KLÂs 4Ú`ãà—ø¤šù„×ñÒÿp×è$€¥¤§ƒãþë?µ?Œß¥Ý/ƒå¥3ñ¦t3jðÉqÙÝCé00ß„(ËgÂIbjr–ª~‘Eg|dïÎð¾0ÃcZêD/}L>|O¯óé·ºè9OœÒ¤N´Bã22TmÏ„æ‘lVȪœÉ¦5W7ÒÞÙÑ`’ÊÖÜx3¾fUR(%®14UÍ„zîxžÔÿÎþK'nþÌÇL1¢G˰µo2ù$ý1y†šaö¯<´–9yµnkÆãës 6·ß:';,ßEÕWaþý €›åxøû ÆòI‘á"M¦¯} T¿Ñ…ý¢*¸±c8i Ðä¡ïP[" _ Î[ÒwáÛ¬ø)³€I“¤ž2sF·ÛL¸Í jýŒYÒ^lÊ¢¨V_ ¢ÛÀÀ5…ïT€*}t‰mŠzÀ¬øôlgLã9Ë©Z½%Ù2ƒ—*½rx*¾ƒá½¡J¦hîÀs)ºam=ZxX`…W? 1{©Ñý_0¢ ÕK#0)Ж]ÏËž¸Ïä ±ìÛ¦ ü¢ž²X­üƒ[y¡Î¨Aà)äËb“îRUlXvÀ•æ<¶Æk¦¯ÙènK,ôÍjCîUÂXûhÔ!òF£» L#7sx%o:°*g™¡­§ñe1Õ½VÁÌIî‡ÙKïÁëÞS$üöD®) +FZ!u_,ðO;ÃÆìv ú|¾Ìªa úè¶ Õöׄ†®Å(6öÉ ?9/ØkïÑg½¸= „úR[F=[˜ÊÔ:‘Õ½üè¢nÀÙ×P–þ‡˜’![2yÒ"Êöý"wàñ ¬þÚLü‘Ÿ„ WŸ2læBiX¿}Bÿ,œò†ÖÌÆi:j,ÑÌòpsà×Ï•äë»Wø­`)ˆîTÂ-¥½¬æ,Iú‚ÿŒ”ûãëéäûð}LþÀÂúü.vóˆ?ó*Œ’;ªP¡È CóËžG‰JlÔºh=ûLi­bø¦gãÐ[EÒæo…£&ÌË’\øúÐW½Kánd!›òòó5ÂÝ|s¶/>ã¦ECŠŠ'Uy~ŒÌY‹:+ȺՃxi‰yØÎ*{Çp[ígÌ{ýoù¾dmžž‡³^×ç¾\tM”cN´Ýe³9¼ä2PûÝ¢áÜÝ»Ó6æJÀ² þGëɱcçÂa]r"rŽQ‡oFæØ8x„ÍȈBõÅKóBùÝ_¬ºw38úRõ4úpx£žVÇ2}7AUã+‡gÖ¼>}I=»‚»P¼›]+-M…Êa:‹ á ÐT¡RENôó>|1P•×ïrB°hQ Ø“2„ü‹ðÚå–©Z@ñÃ,tcÿâÂ;ñtZNÔñßÕÊ@ë~nh 4Yp…-vŸYKqñy:y¡‰^¦G.ߟU¼rœ]3© )†C\ºì6¡Îõ'aÑÐkÆdN5ÈoÔcÛó  èʇ^âz¼Ÿksosæúv8¸m ˜?jùÞ1µoÁ½`Y÷ØŒMÇXÙqß+thÀê9„ð§3^ïAq%²ÿté¾RpÈÄ,C.&òFÀ”Ç}*@ä~¸“½Ÿ´‰ÌÓBlØ^mÑß·LJ۠èÂk8zº‘8E¿H(Ó[¯²77„²—<Ö#oFskLˆ*˜R¬8tˆ™†ùpcÛCX{-$Ú³º},V—† „ót§’¯Ãd‘Ë(L?šÏ”;³Ð¯›Þ¾‡g.Nà E£–l„!ŸgNËðù«<”kæÙ1ZÚÐ …D›²‰óÿ•Ù ¡¡†¾àÆu÷ñúIºO?žüöˆ€ë'Ï@¥Çu4ü·ƒ6¯Í`4xŸƒô‹lRÝ3ŒÇ•“Ùaá™äÏp!žù›E[ØÂ?„k…ŠÎ`øx· ¿|Fv¤v!åX’]AI0O…C ûVbÏ(.6»+Dèü“ÈÆãWñj® ]= ÿ :ý‚y»¼ÏoÈ¢<úªŠò]1»ð%ŽB© Ýá¿€V‰ía2…›˜1ÿeT¹r=V |ºéØ¿tZõ½†wAÒ¨ãw;fìáTØ5ߌ^ž‹N‹± C*'ö¿¼*»F¯Ax£/ºöŸ!ú¹Û©^C,} ‚wüÅìéÝ|š«°žt­­Ã †âÛRš¥Xr~ˆ…ÙÐ3T êº£pzüÞëFwÞM„MuPÿ “†mÉ«—(,·ƒ0¥åÌ…¿:ÌJ·{°iô ³eKHLÊd傪Ãé‚Õn´oO´èÃ`+pøŒŸ.ðã eò¼ZôÌÒpn£=Ü„BØ¡VU0‘Ô&[Ó“STúÔ‰,!lϤ'\’áÇî4RbY 'zÞ£§ÑvÆfñmA¨ä OÄ¿×Ri8£=ÛÔ²ð—Ñ,˜Uvw\ Ç»ª^t K‘¾7ñbúfX³­ßã`×/g¨xíAB•Õéµ0wæ Ù(sæø;P.5!—¿{ÒÎxJ^ðÓK»ÂØÂMó`›^3¢iIzá ÿVë€òx¾1yš­Ý„ ùYé;®°®.zliF§mx¶äÌ ¦‹žÁñEmØÿö>¼ë'üÿôÅ[à„ËyÌ ï®‘HÃCt³«q=§Æý§»ŸŒ1¯a²º(õðNÇÌòal|ÿ¦Cù[/´Ì¿‚šf·è~úùtZD-ÒHål^dÈôïjÀáªø«”8¬7'ÝÒ6Ë1üÖvß­Œ!ŸúfÓÑ9Ztí+âuï'ÔÎ4丮ÜENIÁ’(iºàh ÜkC›Òƒ 6E˜†®k縌½·4‘ ô‚á¼D<‰¿®V€Eè6¶'È€ZJ΀#ײiÿÅ*´Í¢»ÏŽ2â·`×äCLsö'<ˆš´o™0qßΈÿ ‚ðÌ¡”ݾÀY>—ía îœmB_•ªÑxùÕ8cÝ$bÿë&4_¦ ¼t«6/˜DýFí@~Ü¡³ƒtT…'Jân3Ä·Éÿ•Ùxtýü3ψ4^øéðE,ŽœhcU ¶b%Ïg´¾Êáè@Þl¿ ÑÎŽTôâ}Ì]xΈg`ÿé4ámfû+ 8ö-CÌ!v=Û…@ø3š5gÍ:ƒ“æÓ'Ž`hùsw m3HûjØû6 ½Üæ‘*©ñÔ÷³Hk3^+×!¸XXŽ¨Þ—¡fJÖŽÁ¾"„o)?}ïY‚K$¢YÕNc,^½‰Á; 3`ëIvG¢eggÖº~øVV€e¿€L ˜oÇîVI*‹’b8 c¶{‰;žôà†³‘0½Û‡ O7`|Íb…µÃÆÙ&äåé{Uùâ}èy¹f¥¢òí^xýý. g»„É0àãJÔº=FÙÇZðÛ¶mKuÑñg›ê‘usXôsy#íiEZ=||ûÌ?2‹/?é)°™ˆÓèÃÌîwà]àMÜ1Æ&—E鑳Œ°ÉÿúÆwøbê•6xš)M ƒ&µ¹˜÷Y™(}æ!ÁÏ“ÇaŽâFúÜë-kÔ;-TµÉâªKÓ"H´ù~÷ñUY¿œÎÿÉ íÎ$pÛ¢*Ñ "ýFôþ$²pQëXV|~gLt‰Êç`c`&X^R!¢£0ÈÆ‘ÛÒ®dèÅToDçêe¾øMÃCP™æD¦K £ª)û~Ë èŠMÁ1ÉpGÇÐ×rõê$HÛƒYäÁ=}ÂxŠá Á¦m[<¬·Ô†kÂsXãn_Røô"†-ÏïE;pî‹Uà¸}΄ÿ‹[’‰jFìò§Qì.ew25%‘u‹`æåÈàŒWå¬Ä«(ÀÚ¢±¤<×%Kåg‘?,ï¼6ÔOÇSóÁö)ÀF ?ûFsØ¿[Àú­m”?ƒweéq÷OŒ“P,ÚÄM¡©Z:4ÔÙÂüb嫜Aöû»¹·ywŠal!œ‹û ßr~ÓtZÚºš›h/ΰg,ð k4UÏïÅ» ÈfQú­a}j¶$ü `®"íf 6ŸD´Ð ËmÖ°J‚·Pñ×#ü}èÈÔ‚*ïd >ËPóÙçˆÖºÓûÿóàLæëó¿,­OÒ³ÂIõ®ÅXáø»ÛÏžïYÊì°½ÍÆ¹‚{·~bæþ®­¬Ï˜¸?NŽ´3ÛËc ¡ç&ùÓ>jôžÎ]ÖÐøîlóc‚¸‡qÏýfîm³}8p2z(ãÏGµ<Í…ýá’Î\rHÄÆiSh§4Ë\‰€Ë:ötÉH(ûòÐQP×ja8-Ÿ ã´‘4ü֞Š®¸ÈÉ)‰ì"r.ó>r»aL°/Ÿr!‰7bé¯OÖ´ÿ\>~٣͖ŒNÃÅ¿ãµÝ3‹ÞãÌ+ñjÞ"Ÿt†–ä¨àÐÄùç#ýöÉ«#xÉ]6•ôâª/†ô¯ýPš^:{€åBRÍå%*›]Èæ–8¿ëã?m.ššFUÜf’_ªúuðè5ÆÜÖM¢ä6Ÿ!Çßs }Õt¼NsKgM!M®¼0¨·„ëÆszׯðHü4Æ FÕˆt­˜8å—"5-¾Ä\˜4î³…aÙ:Ü>é%.XíɈ˜nƒy|%øÝÅ€meÎîd»ÄöÚ×™$¯p=]\àCÖþɼªU&Û‚ƒ‘xG0âžqÌÛšÃÜ]…žôg„væ×&8¸J öH)ÿª ÿŸf¸´MÑÍþ |ü=Œûcð²i9¿ê=5Ÿ&I·e™2†ƒ˜ 9SÙij¶$Ra9>ú7›$Ìn¦µo° ¯ž¥Žb^°âl02î3X›ö;è!܇›ï=a¼v'0e6dj¹T”lO5dB¦£ñ±ïœ¤#yø_íòGlÏç#ãÖù—¶cþÕAГÉCµÈb|°ˆJ•ùa… ?’vÃ˽å0M{% kzr†Ï`Ë­û°ay(í¯_€|Ó‡9Ò Gqy!‡f¼úåág@Õ&‡§Æ‚Èç[ðak ›~=u‚ÿ÷¾5±õì×Ü<œUvºº*Æ BEŽŽë¼^–)ç¬9ÄCf¬OÃìk9¦›O0+,„©æ Sk×ë,?Â\G#â¹9‰uù+MgUcp\,^[ìÌdüŒF•iµÌÉ~É{\t,€áÚ¯l·N‡÷Õ ”¸YÍê‹Gýq4`’¡5y÷.Ç>€ùe^ØÝîϼ×*GéÓš?Áß~4 ­E-¡AÆ%3o옰ÿ¨úzÛ–Áïmƒ¢‡¸Ò¶8ªÏ93W@µçñöÏId8¾ >Q…]õçq÷ð7Õb§“8Ôû˜†ü^š¬öæcœ ­‰TV¼|æÏ²õ÷Ù Œï îµ^B{=T cç'Ø´{&¨­ Ív‚A;/]½ª'I@À u ÝNì[¦“2­Ù$`Õz\åWÈt¾Ð¡Ÿï¹á—W𧲆=ékCÞpRÀ_¶޸΃KWÙ 7àaÆ>ü¾ƒ*âTг¼õøHA“QX{Ú >£Íáð9Y”ÄŽ²áñ×&ü¿®œ=šÚÈ2ÆO5Ó¨]¯ºhKÄGê»ë-Hò%@Z¤Ù÷Œ½½ò9|¬«†YÍÙ€êh“l419&ˆùÐ{-½\”\6‰HŸáƒ¶Ç¶˜ÙIÄW/# Ôß“™&v¨r¢V¾v¡5¬<>’Ťö¡ŸD©:ö€ÉŠü» –./ðîŒFL¼š šcÁ¸/MƒªŠŒçN™ò䀽*k¿ã\žôû€š¯°i$’vX'C¥Ì./…òYä­;NñJÇ%3üÉñàétÒÉ+Ä%Ô šD0²Ï)~:ÈÃÝÉ?¡ÿÖ,Ò§+bÝ~ÇÓ»òÉŠ« ï*¡÷:?0!»ÍhÐ4gêu( j„Õu;Kæ·ðI™2“ÝB§7H@þÆ`Ò·…ü; A^¨×Qù]sé¹6ôüOwäùn«ÚàJï+hl7GÏoKvnçÃÜ÷Ï0sÆ1âº/X߹ǹ 8™5˜j‚òÎÖÌÁ³ Édn®ÿ•AEV­hÝä/Š—ßØã²—#¸ÌÑ–v÷¿ƒQüt ÛåÏÁϵì¥èEôXþZj1—ö¾L¢WuEÙùÏq—ÊM¨ª×bÎXZ`ùÅ xQn1Û×ú¿úÇ«±[@î!î©ÏdOÏyËKðëÁ@Œ|~0KŠiŸÅÃb$Rz!Š»E:OÙ–cOPzŠ-Ùe±Õz¦®œùÄ»·I Áè‰ 0SÞŽþ·Š±«eži¬c­ŽÇ»‹_³¯#l1;×òÈ)©j±%0ÎÉÓ›AéýzºÃÉjçÍ!¦éQ˜´>¼LÙCö0g2™s* šÏd´.:ЪÛé˜ôr,Ï ‘›.LÅ$1²bv+š³’{Á„rǯ1 m‡EήLûÎhâ¶|)Ù鯂Àº$®ì.;(³,‚Qί‰óß+]­œÚån¬á9wÎ﹩Ü7¸ãË 0[¼_-5‡'½LŸ~|ºð’ix"BÂW©‚¶†ݦŸÄÆÄÐ+ OÇÒÔMã¹ÄöQ\›œ?¾š’Ð?ßXUÿD–ûøîÉÌ›À/ʾC¾§®˜ÈÎ¥åC²Ô='“謸 ç‘Ëá-y¨Î¢¡Ý?‘/áEþb÷‹nèà ¥Hô[Á]ß)\ÎM&TÁ&󓔈hâ÷kùç¡LJ&  ÕÁòÅl™90®Å÷¹Ñ0TZÞK5bÏ åô[hô³øüO’˜©5ä·t:>ÂߊO訶íWâœ2¡f++QZ5g®óFTÆó<\êæ+.ú·î¤ŽTÁ&ëdònx¾áF3óBéq*ùÞ’*ñíÀÜØïì¢ÀlåLø?‡Î6p $Ýás·1‹²7QFý ¶ÄíÄŠ")ræÓ5¦zn-ëÔÇOgÂ;vyÝeOd÷û€mœ%:—Á´^]rTÍ ˆ‘ø­’¬ýi%ˆHgÚ^ ç§—0KßçKk“ЊLü;ÂFþT!ÛóÀ¿y»q…Ó¾¡­m×Áï³ ˆ´›±Æ­é\Ï$M¨=§JU*¹ÿÈrºÓ´ñå¦4v…߸PðÜ\˜ŸŽþô@À>vqÕ$úŠÇ‰s%ødÕäwÊDö“Ií]„b¶{PÐ3…n¿Ådœ9M;UÀ½é#þ¼›ûøOËdSëDðm{=ê|P'•)‰0õ+ÐÐWþXí_ÅQ½Ýþ¾}Ød;‡tüÞI®çÈÒ–K¾ü]Bó_—BðzißS¼’FXŸ ´æ,—Üøý…‰ùF]ú®™vö:Ó_¿BÂ/!vfÀÔjS#ëçyÁî)°Æ  ÿtàIã{Ü£žD-ì=Ž)Z‘ö˜>LOq‘Ûp­b ½ÐkJ3–ÿ?¢¾<œªïûßݤû¹rèkJ²†ƒÈ´ØŸœA¡4*è.A—­¡/‚æ3«ò Y"ZÏþµ> Ï(³ j𤴻1‰Z¦at@•ýiŒ}Y&8I;5-"»ö–3ó¿="ÿNÜ@…ö¨»#MÓÖíÂ+ieä÷-s*0yÉÌ•'Îw_2Öm‡ zÉbÊ·¦“ë6²®^dñ@2Ürž ‚úä£sã~O’äúí!oî«Ð¾ÎÀ’º…h9T²²‡Aðl§7ËŽõ6K€¥µp3Ñ…=¶%…Ô5eãƒú#ôôÁô ù{—ˆÁ7Ÿ€É<`G›INW~þÚ†{_bfÕyšf܇З-ÉÁ£ØvÓàäþ>(Ž+¿Ì§G8)`دN»ß‡áâ‘vW¤Nrü¤x‡1„,XM7ß‘$Âê£è‘ªÁÌŒgwæ…¡b´H]ö7³Û"gg/šœ/L¿˜óA;ÑX* \¨c±’·w0´x#d¯pØM¹@ŽAˆc2/9²N‹qýÇì…“ÎøÞú 1ß³›é ò€úg‰y’ µùwWòl‚o‹(݇e?_€ãÊpúdzˆ‰¼\ÌùÿòÿÕÃãßåØ-ÙXç_:ôÈ÷f¸ÿ  =“ g‹> Õ¬§Å%BöUßtèW(Î:‚ÊQ;@‘luœL^”Kr…ÿb*®ÁJþ›¸°i&°‹'ƒ,>© g—Ü‚É?áhm0Dd׃Ǫ rv%ý,>+©ÔX˜ñ'¿ÇW²“á.úŸÏÄì'åLš™,=îSwvb ‡¬¶ÅíâɃÆJî¨26yજLŒ“h…}bÌœq<Ö_-IvO ¤ÁÅr$fÐ’^ŠX„¯na禙觢G+¢upQ~~üc0¡ÿ·Go3Û‡Àíƒnà;ȫ㫘NªÆ—ï[)ná±ÅmT‰®IüåPyàDé}’=pgé週6­/Jf·¾MÃÎÅá¤æ”"ÈÌ=Ê~ß :Œ{èú$ª²õ%T¾„§»ÄáÙ–H蜹”˜O+FW_U¼Cíù6Ì*…6<:t¬é–ƒ m-7¦R;îŸñVz³a„V”Ph¾FŒ^dâèà6Á¦üÎ"òÍ'Î]¾·Îù3oT–‘/tiq— ½rºÏѫ̾O§ÁÐ" ›˜º\híýè tk‚ÿüW[lèdÀÜ€wÿ1°š‰„]E¸ùòg\w†Ñ½´‹ÆªÉà÷!*°X0|ðœÿùSí`$ñŒ£ØTR/ÓÚ,Êæ’­sÑ;K›Re5RÐ|_îLõ~ˆP™ë*äVs|L3 Êà籑¶üoR:[>¯aã[ÔˆºÁCtöNflÞêÀ ×|Òtâ¤V®»w¹ž8’É›å¨ò,~êzóø:ž†¿þ dt¯á2®Ämmožzw‘ªÏ‚T=]’4ňç¥c_EIŠÕ9Ü~óW,Á̯O15áòÿŸòµ¢½ƒðá¶?¬nS2ùþ)Ší4IJûëÀ¸5.5co¦ÎàD½]@ÊŽKAs¼Éš¢…×2 y<dzy´¶úl@UGs$n îŒ"²û0^Ù6/T÷qgj‘%g:y‘/Jê ’ÛQ›if\]¹œäSEâÛ€°Kr=š¼ýÈÈ/¤·£ÞÁ3¡ÇXá>¿-K…ƒM¾Â4KA•„†‰Ð­¿ìa[D=g£;Ë,|xœŒúϳðÕYØZ–€«?2ÇvXÓ£?¯@lï'v{;duŠá·GÑSÕ‚.¿ñõ‡{! iÚÿÞWjá«chT#Göï&-ò+h—åfYàJf ÷!<òLg«E–õÐl¦¢ä ·¾É}žÒ‹Úl5òvwAÒç·Ì±È×øiá~öqˆ:[Á½ΛïaÏ÷«øb`&gÐå4lèÔ"'Ú`n€±8ÀKšööÃnŒ‰eÇ6Å»Ë>ó,Ä@ ìLE]-öm NÛ¿B»ã1$ù= S²°;t9ÝyÑŸäŽ1§âlpÒ ÏðüãÜ?L’»ÇUYå ñóŽ@Ôý_ø¥9’ä=›J&-%Ts×Ì6Vk*ë,N6Î'Bvnô‹ÑxÞ.=¦–÷Àj8’9{ßœ¨;…'xtˆÏ`ú„þÃì5˜§ qÌuÂÒŸî³=Êál\œ83 jŸö¡Kæ*VúÎTÂïbHöèæ’”ï~8\ó bŽ9A¶Dßb‰¼¡!åã9¼s¼ý£I%çuà+!rÎÌf¯þÁ”Ž® µÉÊTzíaXçoˆ¹Ó>ãÝ®",ìF]æ:(^ŸCí^‰P÷îDâS>²›ÉTo_‰‘¦–Üü´DvÇ?Xûÿ\×§Þþ‚ô~Ätíƣ⿰æB§rÇþ«T…  [ÊI|X‹Ï÷I’c£DÓV „b¢˜Í}{pÍp2SaÈGGbÁî{&)sŠùa^¦v ·a.¤ÜW&kþ­Äë¹ÌA«Ù8åW.#@C/îÄM50Xû•í|šÇ:Üwd$A~qût‘;:&³=Ú™ðOM•Yº®z•r@g’)4Ú$±™žSØÿîÓ©g2}ÂYÎ.^?»˜mûkDªv ÓÏzq°âéHÌCÇ× 6¦üôø<;Ø6ÿ ²u‡! Tœy³€ƒ¦ƒOðLëêh•¡ª^Ž€âì)œˆÿ†›™ yUÜ–Û޵ÅpáË0®ZYí-œ¡5œ·†µ`Ó‘C—3ütöÓ °WןÙyvå_9"µÓ‡Èë¶ÝPå†xÞf.þL)“ém·‡ Pj v‹ió‡§"‰,ÏfNA9u9/Æý6ë&øýêG¡Ôu”¯'ªG£|Îcœ&JÈ$¡S ?òŠqsæ²àkXº£Ùãýz6›§:™=™[Q[W¢4ãsçò]nËÑRð«·cÔnö0Ank`ŽÃ&ø°¯#÷ˆÒ[%M(#Œ÷Œ¾"ÛP‰†aü”oAÌþ1­Ø­[øÈÛšXëH4¬ÐèÅ ”¬Y­AÔ¾ÏÄŽN#Ô±eê^ÍB/–4I‘Gb1ìYÚâˆÝË#HØo1"±Ž‹­é* ­»¯<HêÖ ~:ã}dNŽ0ÂÎìSû0òû› ˆ‚rýpTõ®Ã±¡ ú%”Ó"IØñU4tô4<½wúh¡Hâø(èż÷‘§ýüôºlcÍ ·h-ª²ÍŠŽj…µkÆÐûXÁm¾ø©¼°µ§|.½-œ‰±¸aźyßÜñôsÄÜw÷©±I»ÓáLlÛûž³R:ŽœtxÂ~^ÿà;nð IîÆμ­çáİóûñ\8\5"ùÈ)«z𺼎 *›ÊAì4}É|ºP fš×¡àm‚ò65s-z6ŠËoÂ$ ¦=†¸¼‹‘Ûý¡éa¼°qYùBƒäŸØ eÚñïåfdã³Ý¦:ô}ü.rk• ³¡pÌšYì8ùæv5þ*ÝæhQ&†‹–+3£s zÛ&,¾3‹,t¿Y}üô¹ór8¨NÚÖ_žzQbÑ!D7þf;¥N ~kQ>x EªèØ ,WAqÓíÄYzd¯±f„6ÖàÌõRä¹ó}t¸Íš¾T£‹²cðhÛ9´ø…î+o1ß1tMâ%x±î[8¢C×î>B‡š¨é™2ôL§Ç´,iÏõ8b¾%®Žõq õHfE6=-¥¿x4èí[çhôuIb¼[‡L«¶ŸØÿü¯îùÅ”r­f_,æÕ’¬˜É.(hq¼;Zf#U ¬TãpgªH|ÿÎl.O¡Ú»6P¯øé`%¯ ³º @Ë49`r•}zÚ”ˆM>ž¶Õì¡ak2¶-Š™·Ô‘»ˆuc˜Ãៅp(+†ž¬úŠ^ÎQgÅcì=ÿšypd2 è9ü×S:kí\5 ¯‹ëZÕR±‹u¶Ð½DVå±&/­b ·þfâ÷(“åß3Á hÊ; Âoƒ-JRŒß±÷x*þÇ1ã#³ê#?æ3ÿÕrŸºØ ƒrpa2Nøÿy­ÔwzÇ.q,ƒƒG±ç½ü™7›~ùÄ(ÞÍÄÓêßà²íWìâºÀiš€îߎ ÿGÐÝýŽø)ÑÖ3•T¨kŸù6Ý+I¦H‡”ïMÜ—V}Xb:ŠÁÕzÄ}´+Ï5²’‡q-G²fG?Vg4ÂY©<⧆[å‘7}l½³mÔÎcE£pšðšñÊ#yTòÎ<~1—fí¤íZé4•£CŠÜ΂<Ÿ"Õ“§¢CËaŸP1 ËeáÞ“ÉÐ4¼ƒÎ Üc]l®¤ö8» YÝÔþ½ŸÂ:!€¦LÄÿ˜¯÷ñű‡º²ãtdï þÞÏ£úñÔu†^šq9*,ä-²m­Z¨¹ü 'VÒ52š8)êÕ¬Ú;2~-¤h„+:“pwX5¾ùs;vsÛ–E²:?®1±zlدHˆ¿•L4{YïžI(·_‡èÞ¿Èʼ–&åãÏèçô8¸$ô ŒVÛãËØ#lB¬+¼ÜnLŒ=Í™Úè$‡>2§—@Y¾:±Ì×ÇÝ å°×À‹&¿\ o¦L¦9ûæ3'ý¿Žã­ -ÒŽ!wÚÇs—·-l˼¹ð~±'äúsoód46üÄþ+Ï×–Nð?!Ýü˜ Žc%ç™ß¥õ¨¤q7)9±É)™°®}!±:–²ÓùÉûxYÔ\´¹²X“È9o#Î_–qx<0": ÄÛÁ—„õ kŸ…õF¶¬·Ó9pp§÷ío£ýÄH-g°ÿK=ó@9/ \Um]xñjIç1'üÁYpùl/óëç}vÃ5 X/ÖÒ;1+©Ž¹ÿBÅ Rà¦êBÜ?°Ê &‘„}Ñkæ²sü…À» í,ïg–óýeÑ(’ÃK›} ÿ+ê§ëDÖÍàr·”ãì³_>eœ+hÃ}NB÷DýG/@ªÏ5ôy™Êñ=L}6ëS]r%CŠ• 9ý`rDšæñ"Ôœ9ʼ,Ò>ŸOLYÅnân?8 }¬¯ã,8|!„L³¨ÔùHïƒÑLúÚjÈ7‰t<ìDŽ)]iJ?õ1tÙ×°R7šìý“DOlþ —³¼ VŽÀUã\îÏ:C»i­øÉ¾®Ç|þq‰¨̦§ÚÔ)ßÍ`²dz:Î^×Ï_°AZäTÒe¦a÷O*/Ò%PãÝç'ðïn¯ †‡äAf@†îêÃÈ‚5p“Ï -ßáŽnôÁ÷ÉT× ¡Íy‘ 9})»èp0ÎùkŸËÁV£ªJ“#(·} w²Æ®³a–œ…›ø˜L¯.|ÑÛÀ|µŽ¢Í)Îøø•-æQÁC"†œéÖÑßèå‰'^OgÖwâœE_Púë.Ž .”’f£n8C¤Ô=ŒoØÏ•ðÏbs÷Açñë0ËrêN‘`Fv1q?]ÿ¯ÿ3V {©ä%®ÿ~j™ W¥›™‘Èg¶hlyY_÷4‚Õ·‡òÏi„÷‡ïAØE=¦Kê!´—™aƒÉG&ç÷y˜…‘ï¤iÌÜ9h·¾ÎÛ¡^ÏL:öD| â¾#a )fòýá “„Ñ{0pÃÎqW¦ß)N*\H‹kk¡Ád$Ý'™Ý²ŒÃÞÐ: F‹F£ÉÍCBãœò%ëª|œ7¤1ÇÄ«™¸ß˜õAܬ|yíkž=›æ‡çuY.|¾Tá];yrxÕ=&Ãj[ÿ–!k¯Iœz„éÍLõ]]\Ìç '/Ý‚ÍëiŒj-|;ØÈ¦¥ªá ×:Ø’^;qþùæ¬ ¨`Öî©Voâç¯Ï ø…ü¶’‹Y×K’t«Œ‘8ÎÂâðV”ª¶Àç-Èyh#K·W°ç>L§G›šA`©%Þ8ÚY|šÀqÚHÜû£ˆ¶•=qœ¢F¯Ûã•“ó‰×ÂZôùKt,ômQèj8Nî¤ãó€Ó°yöSŽÚÉßÀÓŽ{ɓѴ$µJ8+9žSdð¡»&Œ.º‰S/”?¾p¬ò ¥Oª°<2’o|È×®õôÚßt\øý%˜¿Cë‚׌Qˆ”w °-kgCü™2ðù6{"þù7É3-VPËÓLÍéa?L«0©\a[¡8úÙ†ÐO¢ÔÚÁž=Ô€ÚÇ×XŽæŠ1pfU t7g[.ä16˜Ðo…p·ºâ„ÛÑÖû1sZå †›ÂûÍý(•ñ ‚&IÍj"¤¾Ý€†?€3¯/Aô™ ¸îŸÅ “ëÄß/5È\…ùàÛ1•»#ŽAé©rt¨î ÷§äK&}¢ª5ðô¾°>é.^‡#y–ÔEr)³Ðj3ýÔ¨Nâbßaêm&»{6õ¾¹ˆ5–;Ë™´®”}²[Žô›¯fžJ©‚î¦]¹ùÃDþ¿!§ÿ«Uº~™=︓¤Ï’‚wÂáˆW+£ðum[ì,1Z"ºûÛ„Ñ£uZPÍ¥hõIŽ<==nk:VlíÞFÈÚ€e_#¹‡ÿ ù~²bÿ'.GFŽHÎӦާ ‰Ò‹>æÚ–IpÀî<Çcÿrìî önÔFnê¼Np0,c…æ0Ï·Ñ‘¹™„c >R™0²t.™Û­ˆ–Í:.¹Çt­ÓçX$÷í5ˆäËÕÔQh'³!ZWìßζRqÊûLŒ©Ù~~(iQ{—B"•|¥xܸ‡ÔÒMN‚¡Â+(jI«jMà¿ÌùP¦°Ó´¢!W{{z‡:œ”"nü=è(²ª½ a£F<¼v–­lÙÆD„‰ö›6±bøPW§ŒüW/}4X »obªýÓÁ÷%CF}mélËÃ]Ò¶ aqJãTˆ¡¬)ý‘¸ ¤•GPÀ½ ½Ÿ›äL¶_N´‹»ÑïG¼nüOÏ ·‡+|d= ‚é§È]z ݲˆK{\·ŽÎ O¤»†ÇH÷ýƒTýI2 €ô~=ÔÒ¡ý›bÇéYŒVñ*º ‚~šÈ’¦6Kî2#3mkü-ÇÀ9X.óÙ*'¸ýš›d÷åÄÑS¹ñÈájffSDËãó}È,çcj¢Ùr>1’¥˜„ò£'ÙáA7$H—8ÃÙÚÌYw.TÏ‚?·,¨iÇm&Ï© ÍM"‹fš‘íºfÜ=¹Eøkh81›0ÒY}³¤h|Øø’¤5 ³kVàõÄ}«yhjÆr€[p…ûf<¡B÷T^‚Qç“päY³xwö6OÆèO,Y¸¬òg¨0ìØxvàôx às_JŠ` ´)¶¾¹;!ÿ… ^Lå/dç+}ÑœpRäÏ Z'®ƒZSßEŠ$Ñ€C³­ÐÛÿ÷Õ+ Ô¿­Dj#ƒ£»k˜’c/C§ž#ε±T÷Y5Ûú*·ÝšI®^ÀŒîžŒžc&¸L÷ Í…CÚ7áŒR4uоŠþÏ瓃‡CÀxA2ÌßË<H£¦¥‡™ú²%Ðât'ÏL·ª†­É.ÄÀÑŠ)/‘Áp_.“VØÆæ\Ž-‘”ù»Gšœß2†<ØéN³9•᪘pF^q0¡+{Ù†“1‰³Þ¡Ú1Å8³°ü‡-§§ÖeNðß4Ç©à}4é?ƒò;¾Dµ{Q™RúYLfã^öï|9xªMå:ìÈÅk]x¶?‹¹ôá8c%¢H‹v³sÝx±ª‹ÐÀŽpù–ÜNÁ7 ˜Å—·ÂPp,Z?vbÄãY_éYLÇÀÖ¶¾Ÿï™#I¤øö x/z œ×ÒA…hJÂKÍc ¼m§ËàèúéXø0lìÞ³œp}bQ>›ÜÔ‰åÚ¼ý©P_æK&Ç3|vbŒçèf˜ûâ7ü‰Ô…„î$œnKx:pQD&ø„>b^©HÑ÷~ax&³ úC'ü?_" õŠcŠo9“GÝ×%g× ¿Õ!Òv;žÄî9yæéä½X4šÁ~¸œ|‰T–â*ÅLâ~8‰°vnuúã¢ù9Ÿ´ˆêî^ܘuŸ5ßz—y8|ç¥$¶}òŠ$ ~…åŽÌ$»‡¾0oÂÝs‡‡ÊÉúœ3ˆ$ãc°ÏÊûqºy ˆµ¥‘¾ËIÕ1mRÙaƒï¿œ¡¶µñ$+Æ¿cÒ8x‰³Aê1u ]KbhÉ•£ àÚÄù¹)G5,èðÊ*hµ÷ÄãÁ­lκåe­nò…û¢ \?B«;ß¹DÕvîIV™°ž£xñ|5ìM:ìÚï„GËç‘Þ©Þt%w/6™‚ÕÚIÐéS‰iºë)OÜ)0ºeJJfÊÒù¾ $Ï«€±O00y2í—3Vû ó57ÈÛ,Å¢<ÜeÏ»0î” ™’6~ËKÓ£*´ëv Ìy@Ç’“ÏѨðw|µî||áìRˆ"g¢7Ò§ºô¬ù;0~lOcÚŽ'¬cõï}¦sèüŸ±œû“m­Jeîmõ„Mz^Q¢M\— ãbµÄx+u«]BÞN¢Evîloi®}6žU³ÿ{ÿ-|¯þ ÃŒguP—i‡kñíð9¬ èa9ÊéÅ“´þªxœ˜ðRì?/J®ß3%×o‹Ò¶Ý´E¿ÂݬèUw7JtÜüæm•WªP]\×Ñ™{’,K3°JǵŽd—Š ÜW¼sg:mNÀmK:Çô¹¡§ßuÅâh,×4§§­.ÀÚ‹ÖP¾o kå€& •Ñ£º¤®eÜ'\À‡6°î/2ÈãÅŠäÖMMÚ39=ýd.Ö¾ÞÒ:Op§ ©xûæó'Ëÿ2¯- 'ô¿Ax2ªªÌ¡ËçS~ƒ6 NÿÐÓº‘3ZW‹o¶·:t6Äï…Óè¹1âÿäV&ÿ„Btôi2Tj Ïâ:aæyˆ‹´$Çuïr›‚o»•qcf"3p¡•v¬©£#+Êȶmòd#PNéiò-eœœ+Ÿ÷âE s7g³f¥Ý¸®Î™œœ<ÔÊ ¼ý÷SY]û)äê±…dZÀ.¨Õa%¶M"¹¦{É·ê\ºø©QjŽ™•ô¢›>ŽZÆ ·­ÇÂ÷Eä½g2;õõ'8™hŒr~‹Ù}ßuèÌúDÐý!è]I=[Q¸œë?ÁÿjRõɉg'p© ó'¿A`KqÞWÊüî¡SêèZÓ%Ôºë>ùÚ$Eñ®ÕËÁìàK<ê4Œë½Ô©]6Õ*T§¼r|ôRÕfF4w¬ý§LK9åŒÅïhjfˆ1ÙêTãðs4y;4¦ӒS›I¸@?{x‰>1¾ó¿mücÒæÔáŒΰv CçAÀ!]ˆŠùŠŸVdTò¬Éùè ÿ_¾#ÙÚªJÄã›é+‡cÔ}¬žÎ9Ó‹IüÔ>Z®>r**îƒw½<Óp÷~žBÊ—ï‡9»Íiõk\öÅn 1¢N:£pÍe9zòê!xviC¿à†¥Jä Å©¯K1ƒ'Ö4±N—D©_"¹°îy³×lëM £!8ÇC”{O”Öcü§­xɘŽ0ŠÓ8Ø_"HM&;Ó´¸ƒlë¿pšºomÒ¡‘¬3ínÊc\ósé| ºLè,F–:ÓŠqNã³2“>_¼¨ùÂ÷YKé¾þ4êÀ;¦âá¯ÿÅ?Q?Øòüa››sÓ÷-”É 5îÇiß ðGi ‘µïåpÀåÛ,™ ¯‡úÙÛ]FDÔ}/DL‰`S=ø‰ëÔ TÔ)ùÛ·™§J0­H“¡ {<Àmì&•+Ñ[õÏÐ-m=u•týø*¾ V¼È³Š.Üûæ3žéeFïg¡Í•8üóç”i-$[1JP‹Į̀…êd^9õ ~<‘€™Ûð(Ö}nCÀ]à!UÂ|üïŒÀoá©ÓJxï`ñëû³­Œu7ÕF›çzÀ#z‚oÀõ×çÀŪ?öÿ¥Û}ÖÜa›2 ð¹¢M«âÒ…ìsæÙÊ×ðäº ¹“šBþ^èfU³o¢DK&® = kFÐós\{§ ®OõÊoN›‘—Ö\fzÙVØ9ëÔb.<4Û„Cž¿à\Ë;öž|Ù¹íx›üÌI´þH:ü7ÇÈÔ#³™ýŸ€$‚z˜=Iõì¢ !Xÿn3{ùô8·ˆÁd?ì9ô œ«>`’ÊÊ ~Iã‚ë é±GAؽ?7ü+‚bn"êÙ÷¡\¨ݹw tçM%qo#ðO›0»g‰4œ™§‚ÏûÓÈáÿ;ÿMïw zà2žòßA#Õ1ÕךÀêj$Xˆ^GÛ)ŠÀwß–èUoŸ\*vËÉ%üéÏK×-m…-SöÏx¾Þ@Ïí‘ ?ßõáÖ—ª¬x*?MdNQ~OÎ…ÕDÅ/žåt zâÓÝ'¡(ë¤Jd“ Úz9‘ &–PÎ. Œ0”'D³ =>5²/Œ>€kµ9–?0'‰û^Ë)i½§xáA{+õõHcA©"‡sßS~ïý…î¢êÀÑSƬ¦äº[ÐÆïq®^"½~Ÿ¤Óì7ÌþºèÎõÁcSW3w{*P+Ù†ª‰X¡¡xœñ}"3û­Cí(ÔQ ]`ñ5wG¼ÃÙŸVá:ÝŒ²« Ö{žÐ‚ÿÎ%Õ`çèYƒý‡F‡âÑ×L ¯*žŸBD/HÓoi‘¯k.ÌÜC ®¼³" põiò:ú ŠkŸ€.«h¨ ¾‹Îå´àÙBöNù%”ÊÉe{¥Éš.aTsž aï²1gs=T¸;M®B¤m<Þ¹¥F¹“³Á²è=ZÆMcÞˆ9’¯O£+¯;ŒÏKîËdzg™ ü;xU/D®Ž.á\^G{’Õˆ£“/ùôB„¬ýƒ2Å ì; bE·³F½®”ׯ\Ìøêx·nÓØÌ„®•#fë·1‚ ðÃ&iRcc@ßn•u0µÖ¢Öªèÿ.‰š­ÿÈ ÍX‰]mV¾¡ =߆ë)¤ˆïò‡0û÷Ol9ê¨J5Ï'»†«íí;Ø¥†7!h¸þMÚMF‚O±=“íßAo²©8s/XѼ›Î:)Kí-ê±MЇd¾{Àš˜GÄDWÀ£UG‘ó&ŸilP o¶Â9/Z]áSWšð¸œ±OÓÆ¶K1óý~úpòsH™ÇKÂæëÒ±MKˆN¡ GÏ"™QÏ7¢åléÒí²ü¯ò“¸Ì©e2g»g³¡C!Óñ’‹³àLY(¹ÿ-¦ÞÄÑÏȬ7"™‚p:SÖY¿CÎ FJå0‘Ê6&‚t¨ûh&$(þ`¶?¼ÁL-$œEÇŠHýVb´âL:¯Î¤¬&Jx†®<¢M>V¢KµâMŸ½øø§>± 7!æŸõñ:S†‡ûXã£öL9ß:2çìKäîWftWrPrT”ÙÔ@?ÔÆà¬Š4²‡ç"q›[ó=k€/‘&<-kê"¤¡kè¸r]ŠTõEu;]²ïáyâ–{gÂþŸój3NùIà4y ­¯fܧs`—F Èìb+°BöLîñÈZ®•¯*ÍX¤ å¶ááe‹hÐXübSae‚¿õÈ3›£$±¾Œyž²‘¶qOX¹_0ðÌæÅÀ…Yû²oin8S¥[•‰=0õ ƒb7bÂ?x®6ž{Z pùJ˜×VîÜûëÓ`“{)85{á˜ÇGÔZð^Jl·™SqÞˆÅIìj%­™FOÉýæ|2R£§‡j· ûèiV'ø#Úz»?Ìæ£îËdi²Â÷ê’.‡šÀÿÓ7ž³{xè3TToaÕ º4{žC,CEÁÿ›Z•2ñò8_®5‹Š#‡ž8ãÐ8W±ÞÁCwyÌ#¿^j/ýDbz]™ù³ðTŚ÷ [lû…(æÒá(¼Ö|ÌáZX>¬r1§UÃZÁ¯˜ì?äSñ':,‹ÜβƒvW™ïÐT‚Ð8:Œv>¶ØüO7-Þ@§uôíÝ0ì+]C» ¶ÀçÏ<äõ¥x4R9§®_†Ö‘%Ðbª#›á´K[,d@²×IО( ÖÓe7©ŸÓçæúAŸS3âtïç_­ Ã"+´¶K‡Àõ¨1‹|_zc~Rï¢ ø”´ƒäˆé`q€>‰XÆø<¹ Í_‰k¥Ü_5Œç8#—ÂS2E1$°žqŸ”ÅÅÌy•ÈüY©ÆO÷¤_EýY¯«'îÊ/qgB:°îo2FŽ 2ïÄÌIi½žSüÆ û‰[g[a8hx|iÂ÷“Ú™ÿjÏÜò@ÿŸßЫL޽óÎ5lPÔl Ÿ÷‡ÒS°ä&/–wËCB¶N½Ù†n·Ôð¸Ó~Ø‘œ†ÃÏíÈEÑr®fÐtréÎ/,äÿ>ùNÄ?}/_|"ß&#îÀY¸Ÿ2Bt;ãL~§óÑ'­¡öõ= ÞGC¾3lŸ´»9þÀ”Ï ¡IÃO1ÝŽ­p¿í;çoh-ÊíiB¥gàІfνsà·"ýk1Bü™ØtHøt²B’ÍxÀÚþ›Ó?ÿS¡– ÁåŽììcËѪ ÎkÄM¶fTçÙ4X62—–æ7R¦ç¾i[‘îͺ9édÒènÚ$óÌxmPêÓC¨(‰§*°Øõ!6…Àh®>51Æ|ùþ˜\¿ð‚»¬Â[¾ræ®N%­—LØÿÏ<34¾½‹ßÄy•–tsáwlŒ;Ĉ–*ã–qxîÅúY“ÛýÐ6õ°ù8,&4Ú‹˦1¬ßB²çü-ÌYÑ…ryËÈòD>Ø(ùw?©¢¼ŒÉ¿Ù{ñCï1tXˆácYïÙ§à¨~4i˜¥¹Z@[•Cœ¯}Dkí¨§Kt¿ý‚Q±Ë,\‹DuŸuì:Iú Ýž\3¿,SªÍLfJÞ‡BRNó@]Œºš˜¶Ù¥~äl(<ÄÑñ3 {ÏrZü¢˜Õw̨ËÌfе&þ:ÎäGÝ*ø]aF OÇÛœiôvWÔ„üÜ©¢0sI³ç‡?5º>ŠÙUñR¬L·ëÓ€1!N—Œ¹Ð—ʹó|)Tô¿ƒ‹·Ôᨢ¤ô—›s+ƒO _†-|—‘#eë; þý‘¶Ö—~(ÒRVSƒÕ^zuÊ;ŒH/FÅE®Ð½°B]E)5(Ãÿj¥Y[Òº’ÛŽálçϰj1b!OŒ¼l‡©âð/å)þ\Ã2¹+Á7¶»Áú8fìxÉ“þ;áÿ’?l˜øƒ>Øn>“xÚîǯ±ò~cqV€>Ž)dþÊÛQµëÓÉW‰ÉÈYÄK_”¨Óÿz@~ïO)Ú¹\ެյ%YáúäDZqWH+^òXÙ„q{×îy,GÊ­‡ð½L<«µm/,:‰q~!ËJ©“YeªÄÆT“õ_ãŠ^©clæà<ýâ³sÛÞþ˜5|õ l—¤C©—*ðÅ1ë|±U vÔ\6Š+¸ûö¥}dŠ-»qÍê8Ž˜†ò.£ìÃ÷¬¬ /ZnÔ‚ÍÓ‹8ódFÙɲ[Q|F>;µ«™’ ‰ø'ó÷¼w{ â¼ó`¦ZíGöXÎZA*9bLê¨Â8N-Å]ªÎ ¯W#D¶Ý‡v•ilâ[R3i:¹8 ¿ùÅ3ënsŠ‘¿×Ò,•¨dóO™“ÍÁÇAD'+Ѻ%LºÎ|Z3ı¼9 ÄoûÁLž³à'hM¬¦x03ߘá ]}ª—þ¤Òˆ û§|©€1ývN¸Q~œŸ‡²³«Ñ°®w_]‚…106f \÷s€çÇÅ@1‹búõ…$¯WŒŸÚÇxNú̼Œ¼‹E)^þ̺5/$cç5ÈÒˆ– û¿·?¿þø¯6ƒf±*æ«2/ñŒr£ê-S1?_ŠÈ´‘œŽô·a(ý4Ú=޲áÃÓ4ÄÓ–ìø8” ^i“™Ö©ÓˆïÞcx&¥š JV"jü4dj3”6N%^ºsÈ诵Øj÷ªóUÈÁ:y¡;èÝô:¼ÿ±å¼¶@A‹)ÄǶ‡ùY¸˜$ÏÆgFgÙwë-X•%Ê·y1}q'SŒ¦ 57 BïðbQÓFø€¯gyQC¡.4ÂCÍr´È¹ÛšÌuÙ\ÁSpa}4ùÆéƒ×A·ahÕØyMÚþNŸà¿Q2`i¸¿þ]NMβLÊn{œç!K@æ28iN½MàÒ½—8‰ 0œOmëðŠSudXÒœµ0³>U÷—c’0[zÚ ®-÷Æö¯Åðp c±h¾Øï"fx€Y&`Ë6{ÆyÒ ìã‚ß»ÝÌ›ÀÚâÇ ±ÿÌ^|:78¾6ƒp_K‹µø—ñìedÿ†C:›Á-:.Rßɽz›3×F…L[ù›hèIÞÎÝÁüógùž‚'Ñ3±ðO H.Q€_|¾c„yxMÊ—rÔÝOœ>}Fb6m™°›Öþ¶s2ŸØ Y%:#ö<ód¹$s/B»ÖM šÎ¦!7ן GHŒó#ʳss“]OT7jPÓa¶qú(xdA*hШÀûûV¸î`.î ‚¹# ˜¢È~:Z‡q3Ø:1gÄéü ¾&ÿ ðKù<äòØ|g?™Î~ÓvàW$§jZ*äãƒ-—˜‹ªR`¬Ež ’õ‡77y&ìÿÙ‹"‡Òo±çÁ!âŸR‹'ù€õ‹1P=+Cî´ñbëÉ/ÀóÖ}¼†9G^BöÂøÜP“-ÂÇ{7ƒžZ˜S&ODœ]èä;u ;xŽmÎëƒ9ï¶›“é·°nvÿLsºõ:?­U„ü+äÆ_naÿëïÜèÝ{3Ž«æ‰à{åTf:ÒTaxŸ=‰–Ÿõ‡¡]¹\Ï|ÔßæŸC¼ÏC6øâ}6üø6\¥t„ŽìE^ëÌ­ùÔË¿v.…»N²CÎÏaJò4Ø ÕÍŠ`Ì7€–¨“t~súk¯.lYÚÄäÏ*shS÷ev<]Šqâ(¾{c<éË1®…³låDýÓvÎ &æøafå{š©"Ü¡ôLRô=PeϘ?ƒ÷¿J”l˜­ªúô^ÚA\)CdÜÁû똶ò8Z„;°rºGáÚžËç’ÎàG²Ê«©ðSµk‰SŠqÚ—¸ÆÝ³F¬¯r¥¡d¹Û‘ ý¿ˆw„À g60g*M?§Îº-+çB¡F&>(ŒÇ™;;`Ú’D0 ¥âþBt°f9EæÑ;=nX$˜Ë,Ìá¥.“nA¯«<±7þjAƒà“ÈJUãèj.†,=ˆŸ¬NÐGzžx½À”lõ¸Ðv·µ£\Z[ÿ«›ž¹§›&cÒ”Ùä—T M?ZH«Ç‰ÈnéJ°óqÑ?cI¦ßÌ ?m²‘ýÍÐõ‡qüDÚ¼ã#¬èÇ7oƒ[”“qÐ 6¿Å—»Ëà÷…^6æÁ0:^T¤}Ú³II_Jv‡SZ±Š#ÏNȯù`¾ÝjÁixÀ¡;=ËQ­ã10gìÀYÉξ{Àt}Õ»E¬†Tè=âç$ßs¤žï.€@ãNx>ž³ß«³Àò`W² F ¾ù}»[êdÕeC¢9õ9\l\1þ¬$èäsƒ¬eƶæf‰Ã¦ DÛ8šV *²zbf]Ÿ7¯Ž¨o—« ­°설ÐåF2g6ÕÅÚšp,TŠ|7™ÅÙ(¨O•X»\•9p­ êx²W¨’µ»BY{\LRq8&û…ScåÀvïO%N=òpÍq;¬ãðw¿aWu¦ãÎc¥¬óøsÿÿò+NJb{>ù3EXñ0“ù}ÉqÕ—­K«íÈП^0ŸÉCö‚—0|c¾RLð¿3¹´¥ø5“ú¼1&2$ygŒÇÀÒ1œò¸’NÍÅþU–ì“ÒB# q…:,Ë€ 4- áË$CÂ~ðHï,˲udgÓï[§Às¦ô î†À]ʘJ—:jʾD$i]ÕáÃ.…Ш] c‚˻ҘI΄zœíx!‹?oê@ôƒÿé?Ð| ´½{‰}-)¯Q‰(w&éÞ«i‡bwŒ$­kô‚Ï|/9ÑO#ˆa>µX{ Z~àh´´bYƒ¹\°¢4’¹sô›’Î=¯G=uVÂ;ú²Æ„º‹zÀÒ¹æ4JÙÒ‚b~ÛÁy=dYdÇf‹Ö‰‘I„$ìÇ^Œ$•uó‰–Ï'T–ßCâÿV£zª?û±w6|ö¶"鎊ÄZo,ÔOÂT9 r-ügé»'xÅ¢’ÞP˜L3vÓ8kcêçŸH’é!rË$–Ù÷ÙÓ_(’FÌp@ p¾8ÁÿëÄ Í%ì¾NÔywªZ‘ïo{È-ý¼°‡‘§`ó{ ¶ˆ'ÅA„ã‡u3%˜WEfAÈ RIfl·d½Óé–¨+ ¸ü0ú6O#[*Rõ»oP×߆,éDº(–V7ÔÀÁâ·0–Æ×Y£kF {Þżªy³D´ 6¤ëØCQº )6¾€oƒ?rïþ>§w)ÐU³ÎÙàÔO–£ÇzbèÀ‡y~2[ú{÷WÈc'"ª¿‡Š%À1ã"¨^ºªlÀ}¼Øð˜ \ÀOŸ ŸÂÈó‚l__Èÿ{=w&hœ piWZmýky£³÷ï¶×—P“Žù±µpóžÑ0€c5zÙ¢”ï´~9ù yÏÞ'Í*TV§64•`lÇMfΆïlTç{æf½3¹±{3mhæ”M=O„§a—B#ÌJhcjÜ÷2ò».àθnnáb\Û]—ÿüu×G™×,÷M·naLç:ÐêØU8R×ûülèùÞ ²ìÝ\ôþ}Êän`YÙA”­ál<»ô} HÓfc µ8Õ1"¸K²‘îo)E5"@|àÍ^=Ö3ðÄ¥üÿ]<5‹í‰jâèO=‡1¾vmqJ ™ ©$ œ®‡¢AD,o «î\ÊÃA÷ì#P«ÖÍ~Ζ¢óŸO¢D]òH忚ev7éù¦óœ&Çp§P…òJ9¡G{ѽ¿e<×/arÜÏØ:ƒïÙhfà”(=Þ$M/;àCÔæ\a¿øž†Gç2«csᇇ1˜Ý̤¯sfU¦2Ýyóðì`#ò©kÒß.àŽë¡ëë0o+gW¥HÁÛéKè¼ÞŸØ½@šì8WË¡®Aކñ9„N’„[cއϚu@ˆ(þ›°ÿWáYì¤)­󼀆Òó!B¸$µ¥ÖF“ò<‚®3›ˆs¾;¬tÓ§…[ ô}¿§ÛƦal¸¹1{39¿g ±¹&A´Ááæ0¼º÷ ¬‰ÛjÈÞ8 n•CŒTu-«¡[Ãx9„üE}È­*ÇrLæ{âEø'n{ÛpúlVqs\º63"us Y5é$yu=–¼ Ô$<ûàêú:“®ÇÁ×ðW*‰ ßa ~þúL½H'ãÞfI¥gï$‡u<𿵶œ€~wŒZ)JÒ¯«Cì•6äŒbC×nÇÛ'ö¿HañFYŒ˜ñœ¹+³ ¤9tÕg\y7„ÈxÈRùJ{ú³&“¹f¼‰kÐÛʆi°°Ë˜|ä[Eç\Iÿ¯n²_ÄÀ‘Sð_è…TZoñà Ç ÚßyTÛ±µæ³H8ÎŽîçοŒ¯Ãv¶ulžG‡—ªpì’ ˆÐ¬Wй"ü¹…¯öU1õíàotM /–wgÅLwЗ{ha[>(vƒdh#¾³¡eN³*Sǹã¥H6Jbˆ ]RõZßamO<$t§Á¼§™Œ¦Ø^2~wúqo<°ãjFá„Ø‰þÇÓn½cÞLbŠpñŠ!ìôe¦wñ‘O3_á‰{i¨ÿU g=hg‚:'·,®ßûgËpà(Ï»ˆWoÎÃÛ/Àž?'põë}Ä$߬U¿ÆÌ1Ê7Ë„¾¯¥Kd a^¦áÖ‰cWõ ÿq"86•6\zÆŒÀžsH¤°…¦Æ0¶.ä~˜ê´‘ÇɇåI°rô̾2ž£”¡Ò?GÀÚuˆuù-JzK øÒ*šø¤‚Ü ì“0c8ynÊ’ÔC–ø¯Õ*vl¦û+upóŽëdÃl b ý¿ó.~€8ŸÜ3|¤_¨ [Ð7îm ‚Æg89©¸åR3˼Ÿ–Ë…Ü9Y\H~âta:•*#ZÓ½C‡q£Š7}X}îø’·-°yAÎãgá°ÁeÐàÌ%ák©†…+.V_Nd›QËAXô(˜ÆÎ"ͳ `Å@òdB€F1¹? J{3ؗٱðò ®Ó5G×-¬Ë³°ôÛîÖè/è²@™XÞz„™[É¢G_˜·Ï€0£¯‘ÕLbß´@²’ü^¨Fï¸|@‡ú|¼´vÜf,¯A¤"Uòôˆÿ_ÖßÃ,‚Ç|iVà5ÙÕͰ1ºšääälC>?] — xÑ@ÿ·82D_²§4 Q—Yû_¤µòìûµ˜:˜JŸW^D*5‰lh_D¢4 Ùõ(¬›TDå§zDÎÒ»õš%/þøýÞ~ùÊ|{¬HŽ~î E‹P<˜Ÿ,ãnÂnŸgxµÀŒ¿^ÎnÌ’&Vµ/ðk\<û·­6öGᨽ%¼’ Êÿ,Xß;ëñ‰·#}óÇŸŒÜ‰¡32G`3˜Ñ?Ç¿à MX/i̲³œE:+hgy”æ[ã@À*ï)3ÿj\nrÜ¿·ü×Jƒ[ x8‘>|g@c2áæò¤ÔHˆh”5B¨Ê$´Dc2?ñ,ˆ'J Ãcs*ž*@· ï`ƒ‡Å¨ŸÙE›k­Ò¸º¥mXôSWË–³uã±€©Ïgk §ÒFÉp¦Nî=ð^ƒÌ§¥à?Â<>¿ŠIý|%.y‘ gkþ›b”ô`qãvìS= Û¾3WÖ«2 v<ÁIˉÉÓgìuÒ©…D±»ÝÚŠ í¡.y‡mƒÍW$؈ö×~¯J¦Uale){&ŠObÜ]`i’8nz;ÿ,îÃ²ð‡œ»§-‰ÊÕrpû{¼•}¬Škçob÷y1ûŒ²'S¯Aß’a4i¾Ï¦ð7aáÝ眓Æ0â=Ìàù9d›A½~5±†kÒè|Sz|M6¼®ýÃS¾ö‘BLðB?øðiIâ¬çX./`4—>br¯êaÁxÌY¾æáÍ<ŒW–­§¯ÌÓ¡'æ6óåô°í~¿#pWj5•Ü·µK<É¡ZÔúý &ë–¯‚Ôd+Òøm:VnÇÂ0;2œ`ÆöŸ˜Š$N‘ÖVrÃ|o£Ÿ‚ßt³{y(šÄ}¾ßá××rsoVÝ€|µ-Ç×çàã*þ ü»m/Hº¸\TNã„wp\‹=èªo*Ä=®+± NÉZÓÓ;ËÈRóUt:cI'Ñ©©ÇÄѫҼd«×,æ•»;í¼X*Ç›ñ³öý¯wôÓÞ p^÷•”ºÉ4yßC±ôb#š‰ÊЮ•%Œ¥ÊfRük {½ÁudïÌ\\f¤Æ6G©ÓY+<è÷N­=,íMwÄÁ«mœ’IdÚŽ¾fîD(£lú¥-Dß²Öe3)iý€ñ_LÐêä ÷-ŒÞ+Õ'>µv¤ óÒ„üR•"$þ‚I:ŸLìS ìy x–ÇeM6à·S€|=jL¼-ìèjÚF2¶³­Œ«¿Ür€66Ëí2¤=È£›_TÒ€9)ðªÚ‡š»gãS©Ëç?øúéX悃ödøámâµÊ…¸¥?ÀÚŸWÉØ5cZkI’é8ÑsM©0¥fòXV³‚§—ã‰ÝµœCQÖGàaâvr%ˆ ÿÕ,ÏËaQr!AFˆfÀfÑÇ2"¾;92¤V”÷§Ò(¯Ïlþi]²U,…ðÏhfJ‹ûÁ×4û³Òœ !Z˼ƒ)éñ0Wþ>îI& dq†jë>æ¶òÊr ¬†ÙÁŠTÀÐŒ ~šC£÷u1ËÍyàщprùi".¢ø7ãˈõ±`/'"û¢z„y\YOS—2ÕüC¸#Ì7m"V…ð'n™Ü1™„Í£Ù.1äwA*¥‹£ÙÅγ!z?/CŽŒçéC:dÆ';ºs+ýeÓ ¿Ô·1+9÷¡Ûå:/¥ÛëN`ÃåCÔfÓ Øw¨æ¼ÐG­£‘¸)œŸZÇ¿fMë÷£ñá£øvÊÈŒ+‚g "U{ OuÎ#YïÛâoN£¢Y )ÏÌ}½Ÿz¡•‡„èféGè]nƒšßá;yÆöë\fÿVgN•{ Ü:z®cž{4ÁŒ ºå– ýëÖ9!ÿÒ¼ëh嶀،VÁ‡çgÁl¾ •( 3`%ŽU‚¼ëOØkL!ÝmMß}ªÁAºä{ êß]sÙˆíjäV…$ù¥M~4gÝW (ït¡"t¸Ý…¨¸7ПuáÌ”kr”Ïv˜c•c~†ÎäÉþï`k/Å>\vs³ŽÑZÖ„{^Ž3ÿ ¯Åä@ &åÍÔ&íÿœQYð3%:…©h½‚ ùm%úÊh&ÌBä:}ú`m!)"ûˉA”V!ªG3Q·ÌžP7Wº?x=þÒ/C£}„g>ü÷+æ­Î¨žåNØ¿¡Š,-úv‘›ÞÇ–ôÇÒ8ñ—q«œÜ²5!‹çX“’·iÔÖBS#õȽ}rXwYk:ƒàï*+²:ÿÙ¢e CÊo™ …%ð|J9Ý}û‹J :H‡ï3l·\É©Þ^N^&zcÖZ}ªŒÓ¾ ‘™jÖt’s1óôt!§]û¬ßu[MöbÒÒ : y”²ì ½wë6(%K‹¶k¨òðC¦Fÿ “¶Kœ²7méOïšq¬»Í¦?Gl¾œ‡áUrL©eý÷²Û÷óPáþãìq¿ÅmÒZÑ~ÿIŒ #î+7ƒn³%}>&COº%‘•WÝéJ#mòàyÇBîrÓñýŒ2¸™©.]- HDoêåÕdéêNPô'Oç.%ò!‹É½“ËÈÒWלEp¹w—|G•‘ Zž9!CøT6ÿ«J™CÍ!¸ÂdžYsì ìˆ>ŽÌÃK)Ïmb^‡?HW¡ö% Î5!BØwú< M-‚76Çè“B9šÀ2æ}Î乪>Ýøî9°ßÅ¡"é1ׂw-k‘¡âÖCÊ=;’.‚…Ö yhóDþp§®Vt°W¦ë{óàòEKú'Ì…”™nǶóG0$}/Ú$lg5#T l¦1]y#+ÍØÔe©àV‘¹)±˜™@³áð º0‰ü³DýJoºb}=¾~ Ý;«™1;lwïÀs×Û`Þg>\”³”ÔoS…\·wxêÅkëSßÙ[y;°5QŠ®Ûè„Ý;͘€ÛUÜ–l7r¦ë iº„Í&Ž`ëmÙ[[Ø|ëmùMfèüí2nšÒ‰s¯³²ockÎæ€ëÜ\#Šm;&㇀Gø.å%”·`I[<9žtÆ„üs SðIò6æC)Q9ý¯-¼æ2³á©èp:SÂð’éWFbÉNäì…8ßý*'þŒ±ý☼|3nQÖNÌÐZ€£o^Â]¿^ÆgÁ:<2•H›Ê^݃ßr#P¤~ ú-l…¹š1Dd­»ôl';`c‹?Œï3NÛ§“w;g’¢SUo… LÊ6%¿”×§® $·­4'Ò‘ú´«2—iñT¥|³úqÊ¥ºíÈZ(ÑÉV)g2Ÿd öz#ÎðÉÓ\‰_/ñï÷«æ r°>(„µ­EÿwÑOb;Jºï†µ×;àEHË„ýÒX^õ¤’¬HìÏ>¦tóRX­M{6¢|H»ñ‹>s­¢n_͇,é`rÎå4s›Ð¯‚ªLÝ¿Çè\¢AšËv|-Ú\(IZu£÷f2ë-ì-¡óVF¢§à1ˆ"½?LÉùåQPtWžÝŸøŒeIûð\3|;‚Ž-ö  õQE&‡ÒGÅ8ª ¹?•p¡ü4ðülI<¿Üǽ7Æ}_Gì’ë{Xt¿¶*©â™âK°n¸ùï7ì}¤Rc)· »»WAÍÌÄc¿°ÚJËMèÿ¤Á®îŒ8dB)t¦äN÷P"œ’§ð†ÝE³…‰¢ÄßV±}wñó?gcIÖ¬¬`_yúå¨l¾ê†’!Řó1‘}}þ0÷ƒ´"c—ŠÝ–ÛÑ¿˜Þ#„¡¾Bt±ºiÀ± °Sâ.8¤¥aï±Pþ ‹Jð¦²<ÝÞ¥†qÍ=›p7¢bÖJ1¥È§%I­ d¦^ô4 JQA 9#—;&v…«á#ßó:Žá±Û30cïiÜëj|¼áÕÌqì8Ñ ·6óÓɦ4tšýjÆ€Só˜£ßZÑØlÎDü¯¸±—ñÜ"ˆ…;ôéÇéU0;'Þì^CÖya~¡¦»éüwÁõÛ3 SU Oï]gFÖÒy_ʱ¹£=ЕŽñîšäÜÂÛT-ÿlY¯FO]׉™*¬5ÿÜ5Ó•MÊdŸëDÂVc Új JOÊm„};dÉ\‘üõ}'>™ó g.¸ÏH­Œ'ÍÇ2çýO0„݃ίIœáFRï9Ÿÿ$G3NgÁ‹c#0’§F‡Ó˜¨)2w&U²sš|ÑÅÀ˜y|°Ý#lŠ8ÇyÜîCJM”É@W;ïʘ°ÿ„µüÂÏ2`ÄÚf.¸D* ²w\‡ú8?öï Ê\zňùoÇø úy~YÚþn…¹¾¿pïÇHW›B¦EÒð9Ù ÏbÄr9ÛBÕ³[0ªYî2xGãjV&ë÷®€sŠsðÞ kP V†Q­Z,÷»ÊHÁÍ–¬î¯¬Jöz>~™"Ù™ô¨0ìÁÛ©’Ðf¢áDO–Üa—ºÿÃÙ™ï8ÿ­µ7RªçðÑÂg»Á«k2S‘dJü…§ÓfúäÓ /¼ª™Cu·ñY6Û*°åÙáÿåÿ'Ëpãâr|Vú ŒÅÛAsßMöŒ¾!©p× ökPÕ+`¿ƒÃ_Æ,ø!C.^ÿÅÌüþ £“,©d‘$îZÀC^½!0·&Ï|…Cs ÏãÑx¼Nå©ð]q&Šæè_Ú—GÒa’g/³ý;¹gµŸJs$±ýç˜Å³³‹Ô½À5N‰Ô;ZÀÓ’)DÙb*–Ü}Ï!¶jc :)• °³+ÛQÇ‚„àvÁÈz0Ì\禠àäÅL–×]&tŽÄߌ&âÿžaÈž%ÔKa” öÝ9†÷™¸P÷k»ÑEb¤Åµj‚ÿí»ã ?Ésy‡~·~=©;å¹J0£\ˆÌjâ,è²'³}3ÙtQ²hC‘â1bÜ«c0¬Ê„x&¾ãT3/p•ûBZt+/<*„IRCLÿ¥K°HxTnke¼–…ûûÆó^ÌýÐ_&ƒWÀv².Γ²À—B¬_s~üz È(þ®×a?ˆ`Ïû¨#ÍOn*þCò|ØqFK&o`/ýíþ.Øqqþ«c¾Æõ£þ·iôŠ—$1ßSiÝB¸2)wð’:ÏD¸÷8÷_¼w4:àOQ=:\¡õ'ùðFêš ýìü K·êbÔh?›%¾€¼žvø~B©È¸·+€èš2ºÇ8p`Ç$ÎB™žù¬iˆ0Ò«šäÀî`Ø}X8êÛ·}:EK&‚Gø|MîMø 8íã´i˜K7+}€M epîx×/» >®Ëá säh’ÍTj±ò{”?’.µû6;¯hT™F6í1§¾ñ?ÑòfÖ¥Á’¿exrG (änžuÊ÷M0ŸŸö—JÞEeÜèKIÐüJëÀMЀê¹å±óô7ÒGÊèéÚ +½™#ÙÌ‹àŸúÿ™ú–ù°I äwŸ@I¿ ö‘ûfwÂ8OëEí“›±¶§ »?è× æÝ潂Ñà±³Ž9ò‹q¼}{_7bwr,^[$H~û×2ÊæùøÌÉv®V.g{·AWàWÀYؽsøÇ悪ÈlhÎ2çÔH£ÐHù¹‹X:‰fv®€C«›pùÜ}0¬=Í£ÐП€†7ÂàÚMs8hè)éwÙ“÷L}ù6¥þ3œo¥Ïe±ƒw-.–íãò®ùÍæìü"ÛKàÞùøàµ*=]¹nÿ= »»f"Ù­=¡ÿ±Ëó óň{sxu‰A–7d¾È ˵ssC˜»~’¤Ch ¹ÀÌ‹V8ÚaG™(­I¥èÑsÝÙÝÐXν!ëÄ ùxb² ­•P‡z›&¼äëbÜùijòÃDý÷µk±Zyþx^©Hk?|¢»ÏY—ÐÉd¥ý0ŽŲ {6ÑÖµG`%¿íûã¿mÓ‘©¹8Ûú ¹·–Õh`_¶|Æ]‡¿ˆüx>»™ˆú‰“ŽŸájÉy¨Ä™øîK$ žòŒÕ­:H?¤=Ãb U:UïD²«LUÈ¥DBÖ®ÞÆpßû"ï E¾j?ÙÙ`K†qæ'*4ó„;?­‹•lLð- û€?¿#Zo‡áŒ]>g®Ù ÆÏáô-P¥Ü&#x~| w@¸—9ßÄ2×Ó¬ä­û °õfõJÐm¼#¸cÓjÿ‰ŸnY¶Á†#(|Ÿ#'ø¯êeiFøLÌ\xg0U ïh!®] JúÌ.3ªš?áѼ&ÎoóCÜ¡$‡YÛ¬¹C»Øé§0Úß«·ía•eS ¹Ä„ˆ]ñ'dž/ç‡iBDp4xQ*ÓÙÍaÌÑyÇ‘rÔa&)ç,MyÞž=b›„½…i–Ôt¼ÿbŒá6|‡äˆ8ä¥oQ›7/’lÅG²ùYè4Jѽg³Ë­]Á.\o ýóµÈ—»½L}Q ìzðÚ¾,•u½Œ·èAöoõ]&Ó¬ }·§àØ@“à¿()˜9ÿ×—O" Õë@ïÙTª·ßÍÂkàšÂdr±9 [醑7Œîœ«pèn qùyf”æ3#_Ô©ØUúS?LÎн<)$M¡‡Z£ú|ƒœ7;aXW‘¦:ÞEÙi»¡oÃ(†?#{Vž¤ÓŸ¦0<ë2€ÝÄöÅIl¶W?¾U'ÏbM(o·/³Ìz?½³½šÒJPo6`±’ÔCݼHƒ¤™™b‡™>ò=:ƒ¯½o³|quWmºül÷ƒÁCÛ±.Ç· ˺°QΉü«ÙcA pA¯>\¡;&äßúSï7žÑ¸¨:’„Ü4p¨ ¨~¬›äÌH»}¤á˜üÊèÇ•1Ù¸æz$'t¾×>h‹©†äÈw!zÎäO›[Þ>x¸Ì›]y©†È)ÖôÂpr¤{}^à ÍÍP}Ç’Çõ%¥¨Üï¯ÍïãËÏ¡Pv-›!zˆa/Î$%嶘1m+Ïmá<ßvd¦Z¡úò­ô`Y!òï{Œíyø™+×LªPÌK7ø¦Á  ñ\ÿ¶w%@Q†µÞYÖÕJô²uXšz óôýº:B.àÆo&ú»<ºÎ =àÂØÆ0õSM6#cœSɱ¯Ksé‹Jeª‘|‡ ŽÐSçÿ ›Ï–s]1ÐÞÂêèÏËù(§v&ëeA…Ír4°¼ ÿ]ã¶1Šin£‹7ËÐO#øoÖTÆ^ʈœ©¢’ÿðà ª>=Å—_`âe6C]ó >Srfæu¢©ù#ì·üuŠ®DPÅŸñ0£±ò#‰¦aûjÐ ŸtýdºïIÙ¦ÙTÖºB¬t¨Âi}ZÔ&NÖTisMR’ðÌ«ø5(‰½Æ§lH?ª<• Iò¾ÿ2¯W¶ÉÛ±yÇ}é¯Þ'ìÜ-3جæ¶úòz0•›Ïy“Ì`WB=\¹>‰™5Š“iÓ‡à)ó{ºúÎ=Š »ðRü jý9ÔáË\iXìêÏÚN'ç弄eñ#œër…}5M†$.·!ReÙ±K)¬h„9M{ÝÈÍÕÁÌ(…Y xs?”@,ˆµ½ÇâM0éw ¼áÙ>þ=™,|ÕU–³ñ*Ÿ¤ž¹#gIÞÄW{o€˜šøåÞ,f÷[æ`ùY\™=ÎSï±»›,ÈøúÁèúIM5„ÄÓ $ñl&–̘¨úh`Œ÷öèà÷ª1<§ºµÛ­é¡aCRn0 Ë4!|-|S£ÚlŠz=a½ÓëÁT 3Þˆ³+t¡Ûj%(é; 1Ѹ&õŽ—'ú‘-àTjFþsÁII¦ü„¾ÇÝ{nABøê|@”ôr¹pküÓ|Ûg|íu‹Ï²O‚ašù!8pÞ5xŽ1Ÿ:àÁ—#ôç£cL­ÚmÐX€e}• 1‰S£ÊaßN郗CÉìl}æàèY¨ÒÝÆQ0™Lú§tC¢í"° äƒQ «¿e(Ÿ:±ííIhe¿fÿ£Ëa±ýѶ×íñ¥¦ ÖªŸÆS}ä“m(ÜÞÙÇÌ–îdåó3¹m3~³‹[Å $£‹ó\í¬t—©KHˆ<Ã<÷²¥÷ÂÙ–Ö °åì%¢7—”Êå0cE-ôlFÁÏ>2¯:ܳ]^îXç+J¦mÉ£‡¯TÀëWÓè[«rÌ ráïˈÍ'C\<]“t^ýˆã×Âôïú8Üò†9·àÊs«Yjúû`ͱñøz=g~{Ì}Û‚U“fÑ= ?QuŠm!âxçI]ï}šÕw΄£Ïް¯bñëBØŸ½ˆøé[R=¾ ûO¾gÌ|Ë©†=çчcDÞÿ°À¶æ}¸wÙhÛÕ†²mxvï7äfJÑ)hòñ3ëÚgæÆú­Ðõç·g%‹wo¡ K é³Å¶xS‘Ú »w¤;ÈHÓ£Vž°dítph¸Æ.Ü È,é5¤»ju`‹JyýÝš)ÙQ¹m¾´÷† gß»IÄ¥÷5kf—ˆÅl Ç!p/ºñKÁ¦å§@Ø9Δ3º—–’±åˆ~쪗ɽf–²†ç`ÃaæfÁs¦¤‚Y>ò½&Ê`“b}éº0õ)žoV¢9ê†Ñr_ž­žà?+_J§ÖtæÀæGYhL¥TSñÒ"iºá‚T=ÔÇ?ʛȦíp{M.åP£Gødµ7ìù‹¾[\ðùâ:(]BçÉáι Œ7øâèñÕpo‘ë\ð¶ï%ó÷Ë5OÞÛüÏyH\\]õøÙ[AZ®@+@¦øÇ3ã÷³kNá.{&q¥öpÁÓKdP!¸ˆ£…‚ØW¢J<|xˆÝ÷v>}C(ü³‘~4éÄ_EfðzˆÜƒ†éÙ„RªÒ¤¯ä¾ÙÉÍû±àn<¤~Ñ"íáJpÂÕjBÿ¦s‹á¼»;>ØeI%™žcšÈŸéÂZéÒo¯ÃqÄKŒ¶á$M¹½§+ˆÚá}TC¼I´Ê ºJôuo Ï1$×ÜÕqxÚ/No…6è÷F#O_x9hNm‚’‰?ñ<ÚÁ¾ÚÄxˆX×&{röö|õG’>¸ŽÜ· p°Q‰´0¦Y]ÔâzÒG¡ãµF]ÚÈ*§7AzÎN(K ¬ËvÇÍfðÚiGšõ±bm‰ÐåT²ì×bsÑŒÞ}߉–A+è½ýÌ5ÙmôÍËØ[QÀ±=>ï¦è‘Zôyèÿö?ZÄÑsV wW¥&Ý^øESª/fãâ)óávMIîw'ÝÒà¡„+¼Ì„Þþ »ôg³êåç`ôÏ4?©B;íÎAh‹xÿû‡-B>dè¦ µÊÙW»k馨‡˜g°ú‡ ’ƒÇô‰8oHì–&4:ˆo2]í…ðßõâi¥LïÈaU¡›žÝkÚUõ lç P¥rŽï7,Þ´ž&×êÑm ­ÄdG ÉË"&IyϯAùUðxœÆ8Ú[,J¬ÿ¥Iï£,[ñ‘Í«´¦îmEhÞæJ¬Œ&þÿò˜ Ãê÷ט;NmÌuI=¸}œ= Û]\ig¨ «\­ î¹·!½å,ûnpº½ÏÄŸ,F}´ ¶Á“ð’˜! PØJ†›&Ó݆û°>ZŸÑ¼8‡.èÑ 5ËObý)’pê7lsÓ ïîL"*a³!ì­3Ø”YÂí Úø/Ùîx…€—ë\¨y* ½qõæKØè±Òkš™;?Þp…íâÜ\ b[ ¹É™ïðµD)?%ŒZ¯ZQC[…|½2 #  å­ýà\‚íoÂÈû¹¤Bu2½¢Z_îèÓ'“°|ûdÐXÚÑS»&ü¿Ï»–9rŠ«ó5¢M¨à&wç-Sš:g1Ǧ4‘«ùvÙïrPBŒNY\æí”Þ\;n‡¡ð×RŽ =éé»'áÀE¢ª…Æ&pv¿i¨-a5ßnÂÓs°²æ%ú:¦°ž®;™‘È“Ps‹áSkÁ!óJh]ï \Óq†’°%±dd§rª³o13.ðÐDCuò¾-jµ¢Árh7xIv`æv!ÚÑŸ½Þ…çÃxéò]¹Ì&ˆ€Sòz8¿t%{2´yï±uƒìÇåBôïzu:žÍ±U_£ w+¹áÍK+E/°®Ú7&ä*![áã_yZ;—!ò¶j}áL;”Š«Í [z¯m¤wöä1‚K$Áýüy<Šs>lïÀÍn¹ìÞô¢ÂhôR¦’6Õ$Ð…].t …ã\r¹QÎ??€ÂãšÄùÜ—+€E§ ÷â»}"8»¸¯ÒURqÛIÿp%£d*9[] [Ö•¢àËô¹ÐŠfö%FŒÄƒÖË\˜stý}ª‰¦Wþd'4 Öà9¼õšE%d²þuYn¬ˆ™*p¯¨‹9zN—ŠhS­wLÑ!kFèM=ܰ\;!Œb +†æ… ÕÆÛ6ÜÚ­Cv¦d³KÙF0oœ‡›a¯¤YÐBÏ)ÅJo¸ýã;»®T’d½.eŽðžîæ€ZÃÅp'…}„ÕÓÂØèÐ?Ðx­ÙéäN–]ö õ*5X—¸H…)ÏR#f³YÓUS&øfËÈìAT Y RpöFÔDü¹=Q›y›QÒ‹§=òJ—$ãy>%:Û?ϱ§·ËCž9pe¶_#:€'5wõT\(F9Ô ¼–sᥖ7œ©ú‰íBàùþ&Þjáé¸ãU',tÜó/ˆ²zW¢)ñi­ÂŠzF©Ã‘bX‹ëa±b,Yé<_"Ê4ô3zfÊ·’óLÊÕøYPÊÙš´Ê_„¸¤Û/"Y• ÏÞ{£TR?¶¥ÁŽ- Xö¥Ù5Cž9”ÀGŽ8Ôâ­æ'pñÁ*Íét“ù,æÆro²íïbðá Z¿~ã,þc°áä øQCwïJ‡|•\÷ì)ûIà9(~v•WñŒÛ"úïzÖ5<ÀÊ‘ÄÇ_¾;€Î_Öå2Qr2}jýû_ÿW‹xy°iÓÀäÈÛðàS!vKcoÓ¯ ÕÆõ¡Cí7G’ÑZ(æ±g7ÃÙ™(+a@nó†ðïqÙ^-qòEãý…xOñ4íXUF/þl€2Q1:úV æÆÑM §É4·`öŽI.îRÑÀûóÒvw}röÄ.æáŽéDï‚yí% oƒ{PxØ–9'ø÷Çw B]Öò²yY/K3¨š¶gDŸGYèó×/±¤­ÖåÌëok'n¹ý°…n\¨Eç݃›ŸÅ‰i/?í.ÿ¿D"Éî­® #° 9 Uûvq…(8瘜Z .‹ùȸ2U³ðG¿¤P·ã§‰²ŠÔzë6,|‘ QÊÅxŽ3“¯ã!z}xÈä-8EjÞy„ɹh{ýÖF¡ö¹<Σòx£ä0›½¾ Ö½M'ÞÛø:¿ƒ«ÞÀ½üSдʈ]þIz7fÃ@IÛ˜MSŽÁ•¡BÜ  šk?‚Äñ¥ ®\Cu}œlfÏéYgökCǾ $-å°[KÍl[“hƒŽ£3áCà Úü„¤öÄã`Ê}\Âlb[0(ñ,Ù4Î#?@ÕâçŒÝáÿ½ÿ=áû˜Ío͵Æ^‡¿Jdp§á-$ý·òáÈ™ìÖû•pñË|tX‚Üpn„—åMÜõWo³·MÛØFÕ÷8åÜ$ÜâÛ ô™' tÇbp¶¹“{ƒ} ù†ò[q…kO3"ôÒð'tø¬ÂzÝîCÁe\.oÂBºEÕ̯qšä³ Äž\‡3Ö‡aéx¾o²÷#bÐ ©×~¢ÑòµTãÅ=æ¥Zæ•Zá•ûy(“Vÿôq}î>/Bž¹Õ@M­Ǻá7V\ €îÍ3hŸnZKïÁåÅø­ÝŒôXv±¹Sÿ×ÿý'µBŸ’gh|bŸ°p™ŠTÔ Ò2Y/.ˆjüe…•9 ûÅbØÃÜÊL…cª´;B& HØ YJž¤;d‘ë]¡cl1y»ê ¾ù²X®)®›$î.FŸÂ™È[ÜǨz7¼Ô ¬Þ"‹%$iXê\j廢¶&1ë­º@|_šò=3¯ïì€Â"˜Üï@ŠklhÕ»°Øg7á ½Fñ§ „ë‹•©]{Kw£ªÜžK¼Î~àÚÓ-ì7˜÷a%¬ÎÑ%S>4bÉ3uG–­´™Û[4'ä_"ž½n‡ ©ùuN…ã-8Ë~.ýÕWG’Œ©õñ+ Á.¤QÇ3¨r‡=á2„šÇÁæÞæÐ™.h §«x¹øsxß/šši=‘¡düô÷%-:±™˜kHSÑ›tvh ݵí3„UGœ; ×Ö¦âù"ØòX€F¿ó&Ì^úLã=ˆ¹pñ^—#½üSˆŒ¯ MÛ¡ìÏyj¾:”|ýð‰4o~uw@hëBõʇu¾¨ÃéúZ4‹~ú•Áìþ&Ëi˜Âß| <7ÀQ9$GÒ6òRã£CÈÿ²Ç<7Ò†Œ‰ø·ÛàdM]šo3§íK˜Æðƒ({ˇì0û„™ñQ¸e—:ÑÙS†9ë£èÓ×B4%ø4{MÁáɃÛs³#exßû7JZÈRæÑmÈž¦G‹?O¦6Bk©ÞP¬¼°È·Þê Ò»NVè´öãRœ ËÎ_¢7odÛ¯"©F:RèÛmƒd÷¶lä¤ù£yÿ*ê˜T³ú¥¨ënÀ5S1þ{M¦Ï˜(:yã}ÈÙ´šŒ}»Ã9öÊvQöí§/`Ö‰Œo©IÝ­Ä©Šù0§Vu¥EqÇÇUDwë`šô1ÁÕ„†÷Â|jüb!ˆÈ…¯ªÁlì6!ß<H]ÉízkÜΪÀ:×ÖNoÃìuÓiÀ—wÐÏ+A|°¥á+$ˆ©£8ñŽ€kÔÀ LŽq Ùà¼ü[„~P2&†Ý÷ÙÖäd(xõæ5b¿¥ åYãDUê E3\ä˜ÒKÓ9Ë•8ôüÓ,ŒþôEùŸCÞŸ½ããÆ²‚"鬻® Ê¿Äd³ƒÌZÏC(—©C]EHIÙ/ÎsGðž7Y!å‹I÷wfÑ Ü=ƤÒäÀ™mz¸îDì…å[h¾IžžX§LÉX3=¡/öJNðßÊ9+±îÏCüõ¾ÙûP×A'ê–šAžá¦ÝÁ £–@Òê¦SÑÑbdoJÂßë±ì4­6´ø©KwÚ/ ¥Zj8ðàMÿ0ˆ¶Åfģϓ¶f–àÁŸShÊã-ôù58uü)Ø…ràëñ³Ê© Ù‹©ÇkG0¬o%=- I¾¬‹¦êQdæK².|!¾§ðˇTü«(Fx ³+5O_wç—Ä"z¨i1þÈ\ϳ¬œ=®P%»§ž"'ú{ }a j5%-“'3çf=${“<Ù¼Ú‹øFŽzø‘k ×û¿ÿ¤e%°ë}Îâ{‡Í°ÚäÙ.ËÊ=·¡j^Ƹ@"‚ìiצÇ_Ó£ßf“U?ºp{{>}ØÿSóœé“)A\½ûGXé4{0Iþ—Ž®GåIÉt/ÿêPÎþlÚ.i´¢lðêjbÿëüÚŠs¢\‰›âêó™“»+ãÑ¡!\ëó"+¯j‘‘Ô ìQXAÝ¥˜UÜF• :ÑâD<[W#‚—"Ú1Ds'5ИM¬xœIãŠPðÐ¯Û ÏqWE-Ý:Ù’^ñf_g8’¢ù45X&õz²ï+XºÝÕ‚èÝ—!ÛæÝšÐ“@ÛÏY…ßGÙ?r5¨‘LËÇãD_€µ‹²&¡éº,œ±L€’{ ›Æ¹ “5 våø¼º_Ù¾ô¼ÿGÔ—ÇSõ}ï›ç!2–¡ˆ)s%îY©¨HƒJ³4ˆiyŽÌ3 †& ©»×Ne ¥A”f%‰”Òìçóûãýýã¾Î½ûœ=½öZÏzžsÖÙ OÙ± ¾…øvC?vGNg›ëªqqè!žÉ®Ï4@ï<OuÒîƒ`—"ùYŒã! Æ‘D-¤4Ø£ç ¹£ ,ufª7sØó=i(¹µ žÊaÓ³<ØÆ;%hh$Oª÷ßçb|/pŠðýx!–$Œá>䞣úÅKXíË—p Êµî–òÎÂgîˆM:O‡¼Ï¹3σqL(š‹““þóÿæ[¹ü5ë4¡Ö[½|nRÓa1ÖÂ?)ž‰àhÐâÄ»9ÑWÁ}ƒ¤{·à5Ó±0h0‰yGeMÝŠdËÝJl8QÄRfƲ×kãaÜ4m¶øŸ;P¯Ê¤Ìææ–oCOm¶I%ÑE·ÐFj:y¸“‡³0j©!± j€1¶±5GnÆY¿¢ÿ–óX>(AF¼Óy³ïØ“YÏr]â`|ö1¶¢Ù•ó·Îg{~å“g“Ù°r xŸ½‡ùŸÝI®˜gTéó”ñ]{7œÙçllÈ·Sɲ29*/H‡}eÿã+m¯ÂYá"ND¨€ûøþ2šÏ´¥³¾Úq®¯:áw º?'óó¤ÿ—M»ÙNN6S 1w¸skwÔm &Dp¯hº‹‘}4 ºÍU˜\©#mJ+„¯G¨ÛÅB¬¹3¬ŽYÄ`ÅZ÷ô®uÏgU a6fà^ªE¤S”§œmLG­YÖÄç··àtw…˜q;)£rå ¼ÒðÚç ³òk¬ ϧIç°f’*KìÅÙ×îÐ]®¦0Q,n?4ƒ„½‚ŒŸ°9Ræl¾ÈKàUW¢kÎÝÿì?7ÕË[îÁ£´±P¿ÏÛ©„鳿±—Ë|2nâ<Éeì~;#>K±ÙA dÛÖÙ ÿR‘‰Ü £——´ãc­P2‡#\òd/Øù× ò®$¢Á™Z%¶R„^á™ÊÌ®ù$_)NaÒÃé¸KÝÓ† G®x;"@ZªðFÁ›Ä^éOÍ(ïËí#ê+fsïD¦·3¨Õ‰¤Á¥Ãпôî¸çÀ•ÏÁ»Õ_¹Êx%ö k:}¯r rç{@ð¾@ønD¼Ò-˜¯äKT:T ¿ýáÑËøæýÏð÷ÁàäX¾ßø`k‘GíBsnfd´®<^£ïa¤S-éOçÉŒAgŸ»hìÞÇ]Í(þoþ¼Ì Øm°Jöé³éá8©»(^ö•Ε›F ƒØ^F­ãŸaL“(J9ßǶGú c˜‹va"Ì 5µ°ðµ+ |€"jD]°ŒÚC­÷…«åâØ)0Œ_Á a7\¡¼œ§5÷:¤ùù?ºL*Þ ü4?s)õ^¸ì]l®•%×5×Lwüí36rQOÄØ¸QülÌDrLÄœ½šyûKé‹B¨º¥Cv˜ ræé¶Ð8¸–.ÐÃq®–øbŒ¿õ…mÀOú«>dó_µ"„HL‚¼INÄe%BEMž4OüÏÿ—úîç¼ul¨¤ÐgØž·Œ‰ juø­yË\€W¥«x¦2pñ³ßøsÿVæ¾ä..@yãpÄ ¥uZ˜Dø7QÏ’ÓÖ²Âð¸JlJO]ÎçâŒ[=prHd|öáÉ?·ˆßTa-:Êǯá÷Í9X\þM-/À«æ*¸·4Lj\ÅÙ‚' cDS­ÆC´ö¨/ϱ‚ ‹qz2‹_§ÈÍ6¨©q³Ð›< „ÿåJÇ¥Ÿ &E'I](Ç.?ÿH+ž¥B§_&tÊã;Cò¿¼ì¦DgÖxÞ…HKþßþßçó`ÈüÿfIÕæ‡€ÌÕ™y¼ú£=™æáÔ]TŠ; ŠàÐÈ8Ò×jŽíÚ tÝÍF¬š,‡[å,hÆÕµøÖfêo€œÒÚ~x ™¢ûg­_N®{ǃÁ¸3 á9?h™ãc"Mn«©°Í)øÁ sØÚ‚5«Sx+étp"J¼ß˜aã<âáXâ%*üµN±Û4éiê©>‚…ãB¾ü…;¢W迨¼÷su/¼å.Ž\áÊ4ôitÌqþM­jœnÂÚÐM0•2ÿ lÑõ,t½Žêé¯ñø“kü´ÑÿâŸYýà ¹_j<Òb*ŠCôþw'9j†ø¶ûú]-…‡WÐqåÔü¼šH¾xF¿\ÌæLZ“ùš—QQ3ƒÍ_GÈ‹‹ÉDí?<Ý9{Ñ¡Œ;·ÆœÅK„øíOaV9ºxI#Œvßo~=4:'C7üGq²‡w»ÕââI»ÍKÜ+œ ºõæl0µjºk!_Q“|ý-Cœuº1¶ù2§p÷ºZ‘%(ú]\/ Ë!ôй4»°­™¾SÌ 3×öAÅo;È{ˆí·¥ˆxÝEP- ÁK)3ÑþF+753ô?þS¡%ùT–¿ƒ:`”ÿfn­ "±¹vpyËè=iI¦q&<©yƒð´H‘YðsºŒX1&®Ê£îýY¤åF1Îák2‘«†dö7mÂ÷Rä,"NrZ“R9/´¨ƒËüPw[ŒÅdMÃë&@¤O"&~´c}ëňZœ8,¼üöt~u KxÙ Cª"ð`Ìs°¾ø ¦½ã±'Scßðé¹ãìSà²o=ˆjˆáþ7sÙàvS¤ƒåprBñÿò¶qÛ¶|ø2]öSþVXúvË®#«:ŠP«JÔf4ѧþïýÿy Si÷Š@ξVˆØìÑ%’kð’ã"v!X…”ﱇ*Å ÈNÏÙzAº{ç{øxÃÆïxÄß|x ͉òëyÙUqÙï |rM¾Ð<½ä Ëo¹L¯šÿ‚¯¥Þ¼&`”̶8Ͷ¾'f*‹aékÒGiÜ86µ4T#±¥Çh+°ûÎÁ§sV¨ù½ Þ¼Å@ù‹ÙñÜ MðsD9÷1Y F뢱Ò[ª¾6ÿ—ß7+ îÍI‚CMGéÒX1´=öÖù3¬½ÔÍ¥KàÌånå+àX…fôuù¿ü—'S úpeMw¥#þB¹#´º¬›û¦êüécÙ…2´èå‰ûÎÁë³U d;‚}„8ÜÖþß‘nŸð¿}OÅ ­«p»&€6nÒfƒZäHÇQþNË h‘ú`=uJ/øÕ¦aE^7UßqN^\å"9 §q W„Ó7 ã£5Zt\_èïÃØYøæÏèàsvNְלå”~ƒ(kSú.u: ¾Ø€Ü*n¡Î9´×ŸnïPí“>ø·˺ɼçŠìäôiøÄÌ |îùâ:õ*¹8ÚÛÏ£·©†öœøÿ~¬çaÚM\uúxß#’o¤ð÷o ëï^ÓHt—çèZ¾åÞ8æö.˜FgÌ—avæw­oÿËV$usS†´‰iÙ{pçI³FÜù¶ö¬k§)Ñ÷ÕÀ'ãtÉðõMÌÂl¬»„äØ΢¨ Çþú~ŽÆØ0bÂÝÙtË¿ºš?Lƒ÷Nm0±ø·wÃëúÒ6˜©îÏñGßmÈú4 ‹ù-¼Ññ±ˆ5¹¦¬bª¢»›NV—';Ž7À©ã|ÄZ1º-Ê—õ&6Aù}È'åÓaÊâJŒm» rãy}˧’;Õ(ï°ôöVë469„Ž" pþjFÍ¥´AÙ‹z~àãÔ;L*×DY^< Œ…Ÿ—æ1}Rdý«ÏÜÊð8>\ŒϳÉ@Ü®\?´Øa[2aw6{æìO¹µ‰lß.Rýæ$nwüL/Ë–¡@â;^ŠÔ Ì⢹›Ë6²µOêàŽ*¹Ûa¶ÿQѽ¿+jðKÏ0ì÷བ¿†rììªóôКXêW› B×ĉ϶¸×>³\Ù§š÷ð¢è61/jC­³¶8ç˜1®/ìÁ‰¢¦eÓc`¦V ‚§¯Iè¥u]\.Wýf!Y9Ždh!޵èå)ȶk*š„/ñû¼Q‰>–_«‹f?=p´žÕÂÆaÈ3Q$‚c0ÿærÌŒC¨\»s¸[u2ÏÒû¿Z2‰10.î,ªœñãV™_$×>~øÿìM¢³~p[.:A¡íð4-¤¿SõHG½Ë»6Žk1Se±+/ƒÜwÊÿíÂl߃àX¦‡›ÏãÊs|8`$ÍÈ¥§à9I`¿xÊœ€qÞ¤¶;—{yÖíwžî¸JÝíÃàrû:z8P¶¿Š$ÈM„¸9!Yè#+ÃZã¤Ùªºiðt×f~QäU¼³³€ÿ%Ñ€¨ ›Á“zü¾7°Íè Ê I°ÓžædL ø áf-æpÉR –> ùçKèÊÇΘâŒgÃ"!õ¼>Z»Ž|ØJýgßåm˜¿Ï6pw"ÿïþ'/+¤Bø['aZ¹‚ž ¬´ßÇòÇâšøÖ#…£~MTèG.o®ÏeX*£…ë–ÞÇõâ‘Xyð/n8#ƲÚó…a¢ÓbÈF=q¢x6Û…ÞÓ€uXÒ=ž’äô˜&h­á‘]˜Am–Ê3ÿ×oÀ”vAåŽ.níÉÞÿò–3B“0ÒPŒE›U¡õ-¦š;Åûk¡qÚVþU™@ØæûÛ,—ð “ňz57Ï"n½É™9ùƒÕ¼‰X¿ÿ-r‰“Ù›qµœ‹i¬Ö‚Om±PrTžÉ¬äoû!€–ïÿ·þ{äðÅ(‡Nú‰úiž¼ØW[8¿«`ΟnH>GÈÞº†É$³ú®(•![téŽY:EÚu>ˆ vëNçl§™q;àÁ­(q‚?»ç±''ýQyübbíÞXC©Ä4sØï8žL|5¼sïÐ îcIüœHX7ÒÊK6~Ç•Äkò÷Äcå½›`ra •Û|ý­6cŽwqfSÙ3SLÝv­FISн>=•)‰ÙB¢¶.ITÊÀq*Y(¥(KÎ^›FÎLèÅKæÛX—ŒthÆÐ'tø{§q ÖDMº|÷Há†/ÿ—ÿ4¯Ð +– @´ÀZîèºV”›~Ï:‚Œëd¨)¼£AȇpHî׿l´~Ãë Ä(Huªøâç“x7d^ ãñnnA–œÒÝbqñ)캷‰òÆHn³#Ç!ɶ l'ýƒŠó¼¶@^MDÁ]Qp±;•2Ü™–g"&¸]G¯‹EÐg.ÆqÓ÷Ár¾jÎmòQñÉnÜ6! åd:ßÛÐ,E…;‡¡ôåyD?ã&&ͷ¡7"dG”6¾¶#h5YŠ©l€ û~Ö|ðŽƒ-ÂW`CÄBlë±ýÿæÜÝ -4Y¬Iž˜Œ`DUˆ5ÏáîŸO䯕 ÁTuö¼T“|y«Ë>ˆ:²-«³ñÃ^ÜÓQ³(D~KÑáñãÀxª̸~ >ŠØ’1§XÇ¡"à}êäæÜH ?ZY¼´:ý”¸„l› gUÿ@…yqØ7ÁSIVÖ˜“ÂGÚDSá4è‹?‚ü" ð슆•{±UKœÙLû‡íS¼HXðb(àÁÁŽqàógœIÇ"Îïd2î=Q…Bk”Ù¾QîR;k=y¢0´kqäà$ÖxÐ ÜÌÅ-цx¡Ýï?ÿ·:P3FùºÉ7Iü[¤ÏØõr½§žiÇ]FÚ“ ι1óÅ2pú \•>Ë·Š“jxë{c"ßp…¥`Ëz¶K¤È„ÎbOÿC®‰-[ðI“î^à„Wv­…ÞÖlѳ3ðëò?¬ºŪÌNCǘÆÆé«1úhIþƒŸ²/pNÛ44‹ç„GíÄn}6³Xñ C~Ìä6àå“…(Üõ-’B‰k¨c¾påó/íJÄ<¯`ä+m6» Î6 CCŒj¾s!K6‘Y_åXîÇØé{/±èÿö¿ø›—™™M2l…ÉJ¸ôÕuáæ¯y÷nÀ¨¯¢Ì.Äž²Mƣؒfü‚KnS©òxÆN†…뉧p(;:é*Ì„_ÐõØ Sûö¡üoÜ™ôZ´¼~£x¾ü ‘1Òö9¿& Öï@~ŽXù 79Fr5ÓpÀ£Ÿž¿è /ÕˆPz3äæ…ÂÊ—ïqkì,Xg ƤqOÎc·mÆÂ àžù)Þ©]"xï¬3™§°:ˆ¬‚—BFøÀMO» ¼ÙÈ)}8LÄïäð ó˹K’xïùÚ$^¦â¿õ·ðâdyt‡ˆ"±+ºÆyµ¶c˜S8ÛìçM¦ EÁÙ5¥œŠh*jÔ7è¥p1l™ç²×¼O“¯Ðø` °‡Üe°*ž#ëÒZáóæb(}qþ—ïübÎf /›Hþ—ïœPÈ2·ÕоK蟜¹d²Øwô½Y„ºßøm‚ÏÐSLž½ëAÂgãûÍTSfŽ=?5IöÃŽzCÍtæ–½ÝÉÛ´¸—Ôè³`Ø )úštÔfÈÀ+òè¶,¿>²ñÁÐ}iú3m{æpÕáÌtWî '¸Qˆ,{Û÷_þQx'¼'5ÉMç38n²#yôû>‰"¾ó1"«ˆÄ8†Àf©PòkM ¬ÛM&Õ–¢›¸ùBwÁëÇ~ìMÚ0'0#žÅÞ\IÆßEô·ûuÞeÙ,¶YˆLš‹GÚ÷³ôwçñqðTbb Cº/#.– »~܆¸[^lu¬ƒØTœr÷:þûNÿ¬ŸAÒ&«±\_Kn«W>Z‹¥Ã›qOù‡2®àÊ£þä¡ÿæ öGÛ`SI¶ù¥#ö_´ö!øFr'¿;’·“Náž#í0ErÙ¼¶ïtžÄÉÎܹÿÅ¿""n‹]-ìœ3¥ñM­˜ZJÞ­²dáò^º ÉTû ðû4²æ,èdU³)NÆçêάgÍ$Y P]U‘´/FÚ¼aÙ wvØð*×÷ЯÉßÅYcA]§ƒ‹ú´”­1ÁßóWMi#ô™|“f}>{ùBsü$Ë[8iiiÎ9ÑY4ÆÃÊKPUQbž6Úäý”£ü—Ê“Ù*ñ`¾s&1óL¾Œ«kA—o£ºe¾*y6è0ÄM¸íFbym RÜ€`>”3ÇÃóŸB†Ëææ¿ œmÔ¹ªkÿéŸúKV¸ ;†K™ch’Lδž"i‚ìÈ8!"úËŠ”Æ8QÜH±ŸÂ¢žì&~'Qn—0s@Ûž¯'­#q×Às`‡e0e÷Ní•%Q®H| ƒW³™¤Ð`™ý8_ÌíéÔgK <0°}KÌ(åvKe§k–€yÉa¶~p¿Ä­'jᓉ“¹ž*ç®yˆý²ÓYîOҵ͗¯¤k†#âgéµâIl~´)´ŸCû›êéÄ}a8“»°7zÚ«{™6^€^>„%ó®‘ É÷Ðÿ(.Û÷3Z?Ãq ÞÏ?JƒóùÆ¥üµn1(rÀVœtf)Óm‰­xVü åÕ§Ø`âÏ1œÁP/]Àx+S¸ÄûHŒx7=ñ­F¼N@iÂxfi-YS±¯ ŒNŠä4tú± íý7Êçhþ†º¬ÔÑóç@~µ—‘ö‡í×:ᩴܺ‚Í»ÍÑaÞ ÖEÇ#ét9ÞUŒ›#‰x¹g?ýzâç Èуµ83jç ¶GѯöØKo-+÷©¡Üì2k³Ö*˜ÛÚŒç&ôBÿÄCr¸· ó8¯éLaÝuX[aÊÖoüÿäÞâ½x§…M„d<ûxû¨Þfï)ãD%E8ºá}wÛ“ûR3™^|šVe4´ë6÷#p©Ë\ íšad[Î#´¿eÇT®%¢¦IHÚ¹bêWf‡ë±÷g¹cãêa‡úeøÆ&[ê´ØJó]| Óû0;Þƒ›mo W¬kxÓ”…È›º(~ê4i%©Æºl}g3*ë¯!ÑãoâNW¢²F‚-®#ž*¹×ò%WK’eM¼ M5v>î6¶EX‚Ý0Ÿ*ma;+â[ŸÃµÖqó<ˆUó(‡3°Ô!6ÿÓÜÿâß#éh¸V×Å…< b&‘-˜c)ŽçG ضÜGØxJ‡¥¼$¹)Lɦ•“ÌÛ‹»þþReè ƒX! ¦la©¢dgï7èÑd&ÇŠpœ»¹Ü5=WÔ0f ’×À­g1Û"¶–5}¿Íß·2Þî[ƒcK§‘ãK @ú{4YjÌO*6>/ŸSàÖ¨rW†bضô¹ÌðR*Ç·¬‡¦ÏpZ¡ rœ^Ó±$ùÚD®ëÏ ”¹ë«‚Èú¬@îσ+Ø%ÝÆ™„1x`dÅDäѽ.•à¤Ëó¹ËbŒgR±=7þ{ÿËÃa#Hú߀MOOÓÆÐ`vbf¼œËNù§#ïò¶Ô1 }“Öý2Šw£†Q-§Ž*Ö”bN{…¬\ò$ʨm€¯i’xJ´?S>¶,Í#¿{^C¾èSZ:ç ú !ùyÑèz±ê÷uá¦z¤A¶Ìa¡Ù<ÑŽh-¬ ãLcÈàxA&©V޵šÚ0³;ˆŠMÐa»~zK‡LäØÛ;}p;î5ôfhýúÿk¸”Ž.õ¹¨:õ–üÝKJdɽ7&úÉ’‡·’šIW`Íý[8ÉoÞ6ƒƒiÁx2 EnÔ– r׮l\ÿñ.hú|DÏM|ºCú,’÷Å—-?!ÂB…xè!z˜MQ9Á¦÷ãÏÂ︬á0It¡\A¸«-ŽÀ¨¿«¸[†s= í3tÉs~+ê]Òa6£znÖ7˜—{E¦ÎG‰H:ÿSxëÁŒ¦ùÌõô5f† ·G@û«ÏÜ=#9âš®MN6ý2hÂ*v€Ê‹ëLU,õ×,ÁõË„iá“ÿéߦπËì$ØSŸr(¯b¢~÷áK’¹lªÁš›ðÆiöï‘ Ù Ë†ë¢÷¤N›aò8aójp9TT£<[‰é]†¯Ë¼[Nb¬£ý9¦N3"4& ÍúÍÉÉi°ËÜX8ãÅšî²èQÍüÈóN>ð ¯e‚ÓÔ=äïY%pzœˆ~ä^›‹XÞ¦;³´BÒP´]’,ðFÕOÜ'í³ø{áXrÀß/>³N÷RÉ*dãF[|±iB_^ž#¹V–pjþ!ö¸;–“:#@¦GkÂîù9pbmêø÷Úc51'êÿZè‡%•˜äiL¯šJ¢Š"YÞöÇ|uGaíÀ #W-Äe¨X?Ï Š…8ˆ®«¢ ¦žÂ™ÇS¸Aµèñ‹ÅíT–­ùu‚Gbº3±è¥™ï¦:aÉÄÂ8™ìüüž›â‹ß58R*ÁÌåv…N!gÇË’IM ÄyA(Ëká\Õî•LÂ<×±gRÿ¾3·ÞŒ»ÐŠÎ",*k79åLŒó ['E‘þIÉ4)öNM˜S$š-ßËM×(B™M=hTw„¦`Pòb»• oxõŸýð(uRžŒ.o9ý£­0(ãNºKîÑx¹n˜{w\ÈŠ£Ïåäà”é1p{ÎÝ~×IßÈeÀ’o§in¸ \µ¨DÃÇRرz>Tè_ãn>µŠ JÌÃëŠæ·Ã«¸±0ïÂe;Ч{w~–%ª5=ÕgK§°·“xðòžüë_ê× ¾~ ÊnJÀøncrtÄ”¢>ÀÛChk êoÚø;xIôgúThQæ‘+ï°èT!Κ^‹UGÞr™ÅŽÄ áþ¤D^ãø0îŒ+,ÞíÊÚV ᤠ§îXÓ#ÞtÀòçøÿsc$Š×E ÙÕ8û"dHH…OŠ|ÁÖ°OÙ?تðw¸1MW+>3*ï½âfwu#.W³6zíÃv<}ý)Ž÷V…-ËoAS–!›5cE°o÷~àƒ<;©:º·TàÚ‡°G( ?}¡É^²Ü.³3àøÊ”µž^ÍŒòÚèÏOɼèžZ¸$ʺoˆáÝ2$𤠜°‹LYø€¨¿…kô‰GîD®lãjxèv”üSY +ýöÀ¤Yçðß¾|È<œ…§OyCTO‹š±”<°% ®i¥ê(j%õO¦“ ²ÿ·ÿÙ™)¿ÀÏ5“s=õ¡f”ò‡}€7£s{xo€>{ ·4ÒðbFy¦ÁÜ …mlÞÁ8xÿw:?àù­åøoã6âû6qQè[ö }ÕÿÒëE£ùŠ+DiØQiPÊ‹ÛKXH§;èý'§Wä$œxâ¯5Ѹºöš.aûÃ:¹š“ެ/¿—v+N!SÃv²C;1i«>;1áèÞ°"èÎöC¿¸SVJUÿèóødœq0%DI¡ÿx8öì§ûr1Ù­‰Ù×߃tz6éK¡Õ¬Å°ƒÈ¢æx”!ˆwˆ™÷Á„™Ì"7¦ñSgáÀêE ñ*·8 ’‡›S¸– L²¨î=ïÊìÍÄüÛNÅSýúæÂ{xΘd‡H°G÷)4WSúF³vT7s.Çb0Hw'jäBæ|\°‰ŸyNÆ*§òúžá§2?ÖÐ •¿{p N<.“ÌGûñçªtŒ]HκËþ7Ô¸CÕÖs?'Ãïvú¸'å(î¼7¿'ót\Ý`ÅN.ÑÁToQ¦Ùÿ¨(m°ˆU|q&æ&×q¯@û¼"+"'bÔ„\RÙ†®ÂÐÆÆï-*dÂô<"ÿë õ*\eRÎÇV öcé?—\M'/ÃY%²-ÒÄ–$ùzpÊ_m®Uñs=“.èãJ—I°•e\ï(ü v!‹xgQË¿~9 v»ˆq|¥\ƒðk÷C.qò γî36;„2}MÖ멈dzôé‡Y¸O)&›…þ‡ÿCKcèÈ´Åè¤<–Ýx+ˆÅZdÓ©SdUB,5³3dr:©\¦J2}-Kà×Ð×3Ç)Ó™¾ÃM0p¶#ê¹òœÞ.7´ÞÓÏR]…»V®gKÖÖ –Ðjr¤N•ýáÍÞtÀÛ2LãÁbù,„}ì 6ŲìJçn K‹Wê±\Þ˜éÜ›B¤šÈƒÏø[ ÖO=ËRRë˜Ó’½p!k ÉÁâÆlï‰ ²xW<™4&žíɘ@žŸºIöd£gÐi´,™Fd½o\MȃR±w:÷Àa±pYB^7àîì÷pÀôÿžuäÇàÇïÉÔc×j<ñ±]»ž~ør7B™ß;ÅæùäÖ6…´d@¾ÈYÜeyVÞYL&–Éê·'Ø;d@T ƒý–L¬¹„¡%ñö} -£¬cØr¯ÕdÖÜ(ö{¼:)3õìĽléÉB"ò|!éßø‰3ùe!S‰Ï(®êü[MÜu»9á³¹ðÇe®Õl²þ(ò6² v"‚¼~2âQD¶×)±]r2¸êù ÔùXGnrKþ—+ »1ôv ª c_Ö 2¯Žò(¼V•¥’ÖÒ×dÀq*ñWë ëUŽÿ7"wžãÆSý¨ºuY\,Oã}_¢€6‚æ$ó@d Œú±3Yz€–˜mÅ%ÞóêI5NºhC?õŒP¯EæÄ]P™(¿ÅŽíõôâò),@&Ü?cÀ¶‹¨—-A6Gê3{¡“èü@ˆÛ: ʤ Hæ}œÄuG8^ý¾å§Sÿ ¸§ö‹aI]vêžæ‚NÞÀRÁ jnM*ÖÙÃÓi(í¤ˆ¦§g²’Š™Üu-1âvE›Õ\V™gXlÈ öYöCçlœe–ËŽ•ôr4¦çlE_Ít¸Û|®ús·²`ò¥ÕÿÝÿ©sÛ+-I¥èæó U³ûѯà)çµÛ/U;°wfW¡ý‚1{~‡¢­Í´n¶›ºI•õ\‘•ÛqêVü6fNŒÆ0[IÖøo"‚ù$4y3ó+ß.|ž©(¹6~޲å:ÎÜ…‡ªÙG/5ëÞÇÀu}ÿüÆ}Dz 'Hn8°Í‹¦ šh ®-üŒ¯nh­Y<ÚŒT9† øGcW…ÓùÏEqÚöÀÿÞÿwÙ QWÛ¸Â]¹¥{Ǔõgáo”0yÈ7#“•æ`ôñQ}<êKOycÀit9N‹Ã°f>ˆuð!êÛÇ4È?KòÒ®û%`Ê Ó“÷anË›ð­³2ÊÉÄÒã ?0D]p S•¦Ïdò"äû<ëó°ÞÞ•ÝÌc§¾ ±ÅÚä+Ç{>ž ½•cc…^€ÖˆÌúö÷8 0[Ì4T¿ €¹3›ï*GÚþh‰ÆƒG¸¦ySGç²”ÙOPàú7pó=H$W¡‹†öÓ±°èyZ~ßËíx)L,ÿŠ »¥û*C¬üSYoçT\ñR™ÝµY…·{ájûqJŽ—ƒ¦<€ë±OÜ:)C8`4›v¿ÚÒ²ävÙ4rÐ+ wFÄCô¥d|^úýH„?k÷‘ÙbDú¤%þCæks¬ùfFöpÚ˯`~› X ƒÞ(ù±£ ÔmT!õ†ë5Ôb¯®*BÕNu!ù\ÐG•c \SFÁ=¼+9¼¾ßx‰ðËQîÎ`d:`ÛØ]°4m÷.=‘%/GºœÃ9u+Øýßx–¯ iÏç’dR‹S-åÊ•$°Ò§ˆîΤk+‡Fy³¾^³–½èƒÞ®mÐ"ÖÊ…Muam§8³ /²øiT´Ÿ´Þ.:¥]LD¡ü<0~fÁ&NOckBmqÕ­°Pb;¼ïß)+sxi3ˆhx>½@¤;ÔØÆŽ0ôT›¼º§ÏÂTh‹^‹z/Güäš°êA ÞªûÆ5_Ü€}³Áú*ü(Hž:Œa6uÇHo…"ÌõIÁ† AúBë¹ãì·@zp(Ën£õsfݦòܘf>šO"]Ïe³Ë]ilFÚM.øÌWN#@Ÿ™¸en_"±¥v;›Sf«>@p¾*~x!@Æ^©ânŠÿDý«OÐ:‡„|6án¨»sQK¬Ù>U!¶Jl-¾»+ÇîjOf—=‡0£[“-zAþêzCà:è(!oǃ“‚61‰Œâ…|.ã.j­_Ÿå$5ó-—“x—wåÄs” :ýáiÌïýsø’Άo‹ÂáS˜,w›¦Øü€IâÀÊ]›¥V£TÙÖ¯°ËúsrnPûœOxei2In,¢KôºÐ/¬ˆ“ùHç¦åá–\!•nˆO^n" º!çìwØŸÅÍÝÄ­82“ê òØ®ÆÅL5é P‹/¤]™ÿƒ•dåº °ãc”YmbëÅH½«\›¸”löÈ9ü Áçz6ÌjÄKUS½ùv䎟)«nÄøIfoô“&ü "Í}Qá´ MžCb€ª>ÊD¯#ÙpÞ)‘e6/!K7™0‡­IlÌc)FNe‚ØXa¶su†~zL®-'»§ñÏ}{kê‰=Ñ&ñ3óðQ†1Ôƒ_Šª°âÞkšqZ÷ª³ ã=èÒ]uô÷Ì•ì[ÓvHòlä×õr»ž^ÓåW¡-¢ŠŽ]‚{-bÄÔî]Ýû–œá±ß“•™Ê†0Â>ÉVaK[Þk\Ntöp¬ÏÍ’KÏÄwÞ€{¤6ŽDáÊ?²ìm1^ý6#/w)‘›{Ž€þä "k÷ûŽTÁw]sñÕžM¯•¯ÆìÀnܬx‹Î­¥Y“ÇàÊô©$cÕ¨Nô]ÈެåÔrGãBb œ‹„ÃãE!bR/£QŒµ‡¾›6õ#FŒêÙý¢lé㱜ñšjº×O”kžJâ¿SŸY–Ú]½{ù=ðÄå3DÄ`g{C‡Í¼ÃMéÆy}Rl¨ R!÷{=îîÌ‚šËÇÉ-Ïf.æû¶²UTÌ †ýýŸ1òÓI˜ìé̼’­5Âanò)Ôþ¤ÃNŒjçDü_ûÐrF–4ÉZ‚Ù|e®caï¥Õy÷¾Sp–÷éjlY‚2K“GÞÇ‘sSˆZ­i©½¿ŠÎóíÿvaOn)„*VÒË7ÍÑ!ñ4 y踓>{+mÓY+‚_´ï`¬˜X~úï!:Xçú¿1Òwk9¥s™)´¯GáöR&˜ÕH{wŠÛ\jej]§9ëlúÐìœu†› YÌ;ÖåËÔ•oâ?ÁZî\á]¦]—ÄÂŽËýâð †@@âz¶ß·œ©¬ ¢§Z³•BJÌÙÝß*Ü „=Ë?Ï,TàDÅ~‰ck¹!ÆÎ®g_S]HfÌg4<¦KŒß¶€Ô£·ä+ÍÁ$¿4<íš- í˜:å/èaš—‰Çùf¨Áô6«ñau4Ù~_[³ÊÙnÓìøþ§œ€Êüôcº¿¦aÆâ+P—VñÝY¬qÛd”]òÖ(:p±Oððý·p×~%<Ô2Á,5{2ÛÀKñ$¹ç|¤›_ãËI3¡½;Š]ZÁða)÷ÔÇîWC¯´§¼¨ï·YÝíü¦<ƒæI$áÆiä^ÈEæsO\ª³Ù»K2}”s¼ÌúB„3¾âlØ~ä%7°§ÌqžCtWF߯Ýl†Â{x+ÁнÂHxÕ÷2žM|1“É˦ Acé±Oáú™û(¿t={›ëM'¼ÿÀžlbÇä¢èÌ]Öh#îáð¢*×.H‚ý*lòþ(,Gã° ’v »¤±¤ k¬Å½‰‰L-],Ü‹ô“F7ý+©‡v#œ×‘\âZ‘'Ô¬ÈÙÎmpþ÷_<ð©WÌk†%fÏ ã½+Éo‚ØÔzt2ÊÄ(÷ ¦ùû/~Ò¼n?j¾ê“¥Ù#ÜSÏBªøI‡$É«“D2 Õ›Yßp,luÌ&ó®ÌÿÆÕ,õZ(ÉU¾É÷aƶ\[†›ÌÇÆq “ìÆüæÏǃy|γ,€«û Û¨²#G/ÃÆË’l“ÝXwó½VGší±læk#X“¹Šr¯[ aØãùr¿9aq m s½Ö6"wæ.wÕà.!±§¡:Ж)~6Á{?âÁ××iGÅÀKrL ˜²Ê æþÛˆ9vTáO;r2´ߌÕ!ÇïE1b¾N+'éRf¤ô©XûogGÞVÑ+ž£Z©Ìˆô/q!ïN9£ sØ>óN\{{-6Ê[±g›ýa¢ó (“gëèIM]ÎêÒ= *æº}ÙŸÃ*Ä|K.¿¯Aæ:ûÒ¢ÜÙ5´  ‡§ €Kë~ظm2;;eÓ©N"s²—‘ýC—I¥M&ôš3¿m¤vá-ýùÿt4ɧ¶ì¤ñXòÊ{-ú‰žÀ:ë&´\ë²¼-äfô6V†òÚͤ.àûÁ­'¦O°¬X”ð7ªZÀ½¶"R)2 ¢"¬5i/³ÚÝ‚c´sAmR a•ÍTàáy–zñ5¿õÌú¼:š¥å`å¹Dh.ïÚƒÝäÀCqò΄G2[œ`mS*Ù¾C–H4š“9Š!D!^’L¨*ev:É,¹ú(P‰.ü¸ÿ*¾èßDÔÌAÓòüë$öâ<ÕIv¤Î°{jdoÖ.ÜuîhœN´˜PÊC–³;Ö Uið(îã¾4÷²O ’©p¦û)ÝÍ=®4`kçïgú‰Öäâ\y–’t"Ï„@øÉ7Ü…àb2#/…H“OWù4¶"–—=IŒ™ïàÔÏ` ~ã5Gÿ%Þ-8dž.½ e2¯9¯WŸ¨Ó{ Vaý®lŽ %¬m/Í'¤ó6~·ž­5[Çqtl‘)Û´Ò'N`}ÕždÙ˜}RXBÔ(¿1O!«18L&Î[p±ÎAxº`½­ •ú—8Kn6õ:$˦§’Ù“áýafÒðs/æàµµß¹ƒÉ @çg@Eþz,¿Žîz1ä®q,ÛmbÇVïEü9Ë•3¿äçÜh@›9‹ðfâ/¿czdÊ$tÁT™:^–’wÃ%‰#74ÉþÔᚌÝhî2 K¿Ì#_¶ž£)ôÉõ†jØ“¯ƒI¼] ›¹†$ÝßÉÑ7`ÙÕ"Z ޝ¤Hè&}U'ªáe»<÷vy0³¡0ü–÷ž¯©I®¸Åá†7âøn‹y8k/þÃÄÒŒŽëåÅŠ#ßçµB{âêåð=Y˜L2ËÅÀûÁxÀ‰ryk ú†!ÊLO—^P#<Ž}d1Ðsu ûéÕ F‰qøQKŠømI256‚]´ ‹&WfMÁƒÅàÑRÃÝyŸÀ®Uj1uò'Ñ^UÜ̼›3½1—iõðJ4>œD~Ö-&Oä“è§AUöES‰l ¦1¹De¼p(zn¥Á&+mL/WeWw¯c÷·ŒòÑsÄ;t>è×O"O-CIðÄEÄÑáé(#KŒÌؘ6#<4_ˆýÔÐ"›<¼ˆZ¹2™4¤HsM'‹‰õ·\=‘YæZ0äN¶ìueIâ¯@ ®®<€¾ Büqf+¸»;áÒÒSè\ÖÆKœŒ{ݲ¬•~Y²$•{|…e‹8~ggîlLdsîÁžýX,Nf~ÍÁßÂY8§U•ùß*Âáœqh>©2žÚÁ„5œºËF2²|"s˜·Úo®²ãÂn=£¬µøb²5h–"4ÁæÜˆâ–t%bÔ´µèn1—å>n߯ $­,"±¡GcPؤL«'Ãó˜³pÒE)p|Hp“"oŽ( yl. gбÅúW1Çq+ytr;,õyÕw‘ÿá4v”ëa¾˜·ÈæÇ¯ç}we•ïÊq¿ÄvŒêx†‹sw³ã«:©¼Æ]P|-ƒM·Na{Ý<|öQDv­@‘[6®$DÚCÄí™L½<ž›˜ã†ƒmx¾¬#­–ZÈ‘©Éúœºx%\Üþ:Éë‘='F¸#sw°'¿§àœ‰ÉplÕfV¸(”L+Æn÷:h LÅNgS6uyÚŒÚkãýñà¡Í#³šq«ò'Üúñç%:wß g6¥áõ“{pcéxl9oHbÔȽñ?kâÌEYû†k˜¥I"_}-g=¨JúÎOJYDnè=äñF¤¹ IX©,ûf„õI°`ºþ ©`îÞƒPÌDõ÷;ØŽY$ga»[aÆ~qak+2™ì3eòÁ§˜ ª¶`¹Œ>û˜¹‡T­®ÅUGzè†;Ùlq]1§#ͼ ž“AÕ%l­k=n1´$Š )ø·†#¿80ÙNúv]âõ©Y¡Š‹9^q:Kƒ§5“œWwÉœ¡vî|6åbwìeƒÙçP|›>IŽ ¥E«ÉC}LÿL51u""ûz Ç{{ñ8WÆ‹ž1&þf¯ñýs0-ÚÄÆÈBááÌ4$ƒø(‹²U&+ "iù¨jnæ38Ÿ°ë®êÀÜV z¢škÕÈ‹½NÌí›8šùø°;ËŒá¨*™ÑÛ Ÿ´„XjP/XYÉ1±l ˆk{FÇ{P™oŒV4y’Žy´ì«6ûµºŽ’¡J·”:¢Í¾_úÎy­êÅkµ²Ììx —mÈlE6’#é$óbKízo°!%FGµà­žåœàö§('¬ “G2Èžìål´Œí™–ŧ^$ã>ßÚ€÷æþG$+7âó¬ï»*{lYG½í&!§ÔmOeÑ4VÀÞ,aÊJ »DÈëÎQ0e9t:–ɼ#÷]4ÉüWà9˜ÈH¼Æµîʲ*;®”Îyø9w¼Úz&/`£åP«Þ€ËÜfá»@q!Jâ,'r™Óå˜÷øPË8Ƴ=s™ˆ¹]…r‘¯0ïi)^5+Ç¥ý…h÷w+_ö•ö™ŒcNÒat`,û´Cˆ)Ǡ躈‹œU¯®à’ ZžtªÆV†àJÁ(,zü‰vœ)ã†k(Œ¿ú’n›".\‰vsHaã_¬NØÊ|OöÖÙ,y_ÜZ/Ab­—â’Tð¹õ.ÕyÀ³Í,ðU7;( fýtd]~ÖŒ=ƒýa°wç|òqA›«cÈÞÕþäB¯Äâd«'Ü÷;óñ~âh¬Ÿ<ã ‰G”$ɉíäŠtÕè SEH7 â_»x¯¿(">jÊ$qL,/mb hÝ o¹>^ª4à'ðw¹ [ù{™qþuÍŒ›ý-šKófqïMè¾Êw4:X“ÝȽM6[³YÚ¹ £ ÜúÂÕà“óÒ ×n)L!‘ÖŠìÀ%1v¬'˜õ¥Ð=&’døz1ÎTNƒ­3ذñD PMú;®<é±%÷AhV¼>×€Z³Èd½­¬]K“¨NØÍÖtói§³žVƒ:ï0Õ¥Ðg!Aê,E™-QüÑòä°g?ä`72·]f]B}›Bºõ{ †Ìæ–/{†?ßÌÀoKb)ü¤zÕ€I&h!JòãþÀfS[öÒS ~Oåpc_ I(êæƒj•cؾYh,> iñ/>šIzÌQ7ç{¿CñÂ`Þ=žô¬Ô%—æá);Xñ;…Ëš×Â5l#ú>¢îqìêÉçlïbµežág/  ªK8ãâx$7†ùû¿B~G+ö[Nö~Ú‡?…ÀsÓ—¹qÇßaKE ÐÍ+99[î§Òa\™_ˆ/õÖ½³fprz,òj­fªpè”Û›’‡Ã®²œ3_ý¨Ó C”§Î&_½³µ·ýw‡È‹$²³]–íØÒD5Né¢dÔ˜JáTŸl˜»šÃV¢á«5hÃeòü„µYÙ}ö|OžöÈ“¾§Tt‚WÍ7¡b“0õV™p¯–>£Fzz[/.蛂ڳ'âÏÊv|âñš ¾{™“ÿ#H.¿{ Übvæ%ü[§±~j hPómw0F£ ÝןÄKq Óy/vú®ÇœO÷ (K†Üy4ˆ1¼Ñxî<Û[â¡ë‡ô³¨ [þì7¬1¹ƒ<öÿì¢Ð5Ž—M†x—yÐÒº…ºñìo¼çévi³<+Q6Îbï^–~4˜Á½¶ßwí~s·÷»Â2¯>XßKħशµX˜9fÁ¹;º ¨eÍ ùNäì4ƒk©¸Ñ!ŽU,3%î=ÉÛ=œë‡K¼ Ýÿë›ûÓ…Šå)°/ΗTÊãÞHe2g¥›tâ–„§1ÕÒ‹ôBî&öå¶(ïéŸé@„DYA\cÍŒ±™xëŽ5I-2I±ù³SHƒV ÑâØZæ Å¡HÆÕᶬ$ÚïdÙÎæ½hýX’ýý;Ž<~2½NräCOï¡ÀJb¢ø3usÀH:×Í4!§Ÿ¸’×›QÕð¶„ÂR«< ª·§Ÿæ¶bÇ,uöãNªs#›þi±¯+Ï‚[ÊL8Áf^•fO%ù§³w!æìÎN$¿ÏØ‘ÅÓdX`¬8ô?i⯋¨IÜ2\‰;„z0/?‡z¿Õ%-7ÿÒ£Zl(ùD~)¡êEñ8¸‹ÒË '©âD)öã„,1 u‚ÎAbõì'-¯XÉMRšŒ‚F¹Ãƒ-˜XWEó×É… ¶`Û¨~wìº#Î.Ë~ƒ©ñô”3£qW `n`%Ü Ù¿vÂøØ8:˜½îÆðÂJ’pMÛ%”±”õßD0_zw¬“áÔÓÏÂh_<ý›ÛšHé!]švìV·zâ°Ÿ+[pk¹âŒ3¥ê9/¦EI/l©o…Rñ¸°n§ô2œ/"w…†äŽa†³²Pù~ N™#‚× &þÿç¹QÅçAm| Ö/UÚ 2Mð(gTœè…;Jo@ïÏRðÌ:xmð{†2‹ÎŽ¥"C øëâ?YîœßZ(%¶J®Ö.‰ÃñH0\ÏDf_Ä…ÇyhtZƼèåì:óp°z<1JÿFø«|>uØHN V£Õj/Ú¡ëï•ñ™ïãmZvŽ&žÏ±þÇ{ª«J®îyŽ‘éÜpIWÞ†FÄAkÑ>8päö5•òŸÙÅ͘ݎc4DÉÝÍŽ|û‡%8ÎÑÃß¼ ­*qÔ9lÒÈÉ»ÅÀå}Æñ8^¼?˜EÀË—çQúR t=ÓcÊkþÂêG–Pä MqØ¡Ñ]åI¼ÌÎoÔÖzwáÌjî¾U­õÀ¬“\ª¢:îñ7ÿµ˜‘;îî Åžhþ äÀýÞ‹ƒáN¼;¾WÙŠ¹¦óÐ1ü+¶¿5$õŽ¢¬ÅÈ”o¯™ÁµÝ|O…ÀÖ™o`|a*l8ZÄùÐãÖ×Õ94îjÄîÅžP¤ô†Û×”S ¸r»^nîd5˜§—g ¯¸ãK…“•'Cç”ÈõŒ5¸Á:n†,‚’'1xö×y0œÏË<ðUV7ŽÖµ)<™1•É«”ãQ+Žœé®'ó9ðô»Ì¨þÖaOÂÇÎãvä¯A™i›èÝhqrøƒ gýYÔóã?ã nÑžDxŸª ;[áÝÑoàóõôØ- ½×%™š=ó)VâdÛÕÈö¢3ÐþÚ|~ã±×Û@wƒ%Ó½fGõ|¹.Á*ŽÍEÓíµðé˜òMr`~g4gŸU‹ó\‡r9_.ïåB6gÉYýNm%ûᵘ莱ƒo¾´léGðHîçýnm)jÍI ³ cÊñ`®Q,]FvM&:ÿ>a£ènËä|znäìùèÂTÚ?s™"K¹›ñÆžPŒÕtaÙêD¬"½õ™ÍvM Áìý™X+ÆË«a»€»°%i†Pl¡)AsɼçàYz/,,«ÅÆG!¤O"ëå™Ú¸ßðRªW\n†_’¸öÛIxé°4ó kc—‘äv0ú÷•[ð2 E'„°¿R!àI/·ä»:.Ø9•[î@&¹3„)•pÐlŽLi½À?é>›}=”5áOàË`/÷"JŠìf’¬Éô(qò åרâñI¸iésNYF…¸¾xH+ͦ!vˆ›/MÃÅȼ_»ðõ`0W>B‡µ\áœÒ4z«DNÏÖe[¥áa`]™–Ã9)’bå·ôöëMdýq¶zÒjÚ51Ïœ”Ur’¿iÓNà‡¸c~TÈ\Â- ¸é`ùn7\ü.Þí€ «°ZÏ™ˆÑÌÎËh´m½ZÛ‡GùU‚Ï­> f\¢i«Uè¡Íôt±ÚK³?Ûrù[Š#ï-ñáÝ # 'äø/>‰@^øêêq ¯OiAñ“7qíMm(2SàMÚ®ÇÜŽÊCº¡$‰>"ËÇ,à^s3Xަ&®x4†åäÜ¢G¶é2Ô‰l¢ÓÚÛOÃ÷{—s¿nȂ֩ɜÛtY¢T ßÅØ,ë=ÅûøR€Ì82 º~»C°T*õ2ŸFnÖAí'Å0Áé$¦%.$?Z¦à¾¿üæ¯XÝÿFÛäLŽÀ¥þy˜V¥iU©Ô|}gµA•} ÃתI\§aïyY5b§.˜¨Í2#âëöBj2ñž¹ ÇÌ_ÑMÕcaÃ[>ž<ã@nË“G¯Îâ;Â,-Ó{Ô¶/Áûrä—CˆT7fÔ#}úP£  u÷dÞä¶\Þæ]XùÑ­ô¹ÉwØx=‚• ½ò(?$×€íˆ)f/wföÅ(}ñ&8g‹²ûÛØê ñ5`XÁ“ì+¼ñÆ…\Ÿú‹<-€Js“˜ëÚ1`š¥J!{Qqç/6ïYZ¬®k á˜Õ)­T<>š° #x>J‰lZøÚ}Ô™^˜$ØÅ+¬nÁÇsBÐHç>Ú&À©OÝÅxGö „§6W’ ä©ÓT|þs=X<žÆ\0&2Öcˆ\ÉŽMd<“my~| EØä샤 _â˜ÉÜ\b>Nú}›y¡ùаPL½”ÖuÌ…¤õ³Ì—œHá|ý†èŸ¦&¨ÞsŽZ~‡}‹sàñõnú”–B•¬Î<`…¥‘>`øcÏoWgºnж%>? 9Ö- %O3Î ËG/ÑÅW=°öÂD¢|ö \/=BóÿjÓ·@4¶»¼'Ó`f—7ÜXöw¯¡”éJÚ56Í>@èŠrH´–"®1g¹U­Ü+CcöÜÜu·ÄÑŸ¶ƒÑ¸»"!ÄnV­yx+SúžñÂ*;i¤iÇ;ÖÆg˜ÇMë´Q”O” öÌgOõ·@¢âxvBà§sD “¥v²+·ÓaÃ<ƒÊ&ìŸf3å°CÖ\ÎU¶}¨Itׯže%x;d·ê¬éj„Éô9â8t?…,éKæW|AgûâIðÓ5Òc—ÇÍTÅÕý2¤äO3g¾÷ ÷öøNZa<û·*„¹Øpçm®€š8³; O¸ìï\Aýl²AéîþçŠiîä›NÕO|„òxZ1Žl¸}"6ó­Ù$êè‚çï`õ2#@˜™¼Ï€1&Ì`¤Ο4Ĺû+yÚ#°Àÿ?éÎ:ŒLª7&º·"ÈžUßyŸ³–‹é¡Ô1SÐNÀ?’¿i䌰ûc ö÷µØ­Ý l±˜%ˆdŸ$SÖâ×%ס'Є¹Xý/¯Ï挙òf;ru¬9q S÷ÛÞw¿Pe™óÏcÕöjy«Ï}âþ>ŠÂë_Æ’òO?ÑÆó!zï\âNèÁ½ ªLè‹ Å¿¾@PÆø*qóáq¶.°¨;Åpá‚æ]è&¡çµ˜Ëv ö4gj/>G_NXÇÄ#÷mŽã¢2)š±°w ˱Gç !-Í?*ÂØåéx¥A…T. Ã;Ÿ~ +Âüt9š´d6z†‡á¼ï|œÀ&Z’¯ÝAXx;^ØYºÇ`öŸbH˜>ñ×P‹°!Ÿv÷×<"¶NaÓaÖ¹ï0uÛ+\>f'^m/Jª Pªn7|§ïÏ^ §ö Àúy›9“çÎtgŽ ;”Ä’MHNùkØ$[G¿/íÅ5wæ1üŒŸ9ª±þ>¶ÄóÜ]½¸Ãn ¯Mõ,ø)Ï„fí@ÖlôÞºøá›§‹X©ét2ë÷ÞêFîwÁñuŸ9¥k7!@kçHPJü·|Ø³š¼Ž¸{#_-ŒÄzöÀ‰û’øôÜHMýP v.ä£[J#û¨šHgÔoel{%|™°„nxŸÇñÃæ o4OA~nˆÇy§Ù<§/@ª_Á¥”3¸Ó29’Øž-ON´·àJ·No’5z­¸”’C éO"Veø×“Ùî'!ЧØL¾?ÑóÓeçÄúo¹­¸…çÔà§ùLiY;W“}z•AÜ^š 0°Ñ”¶À€úü“÷dÐéïjÎ<¬þ¨ƒ`ß\Oïy˜üÕŠmàeHñH­­5Áú\è[%IžnG¸9õž5€¸¬ žÏ #š× áêÍ6îçGyt–”„æ7?y‹FœAyÆXDçŸCtîI䉣$±»°û“J`ËšºÆé->TÄ©õV¬áæ Üs‘ð€$ýtÎ9ø$/tÂûçvÂò˜nèeøÙ¼÷´M!_WÆ.!Cz¥H—“ÐÒdg·i2—¸Y`yaQ…»Ÿ÷î•bb¿Q1+“z{fc”Î=øvø&½xãíH>Í5hÌ%‰ø¦[žlçž›ƒÁÛåð^Ä"¶ùfvZã§ã½Xüz#Üûv5I°Ë'…™ÂÁ:ç.l"‘/ þ±_é}($®Äæš<´]ëijeà8ò¾Ý×`±»«¸©AæDrÊUxµðÈ£8Øáà *•àiìpßvÃ+÷¶Î„á)ØvÛÎ.yÃW5J„MçB Zs‘E‰D-ftwhƒ‚jôÌŠå^ôLaÉŪìDØ ~Áç¿ ·mSÑz·7¨ÅTV‹7Z`¡V5Èuà‚ÎCèݾ•l&\…ðKá!çd¼?lÅîtK®=Y“‡õm 32–VÖ|à.ÜoÆ»ýqñô5ôý%T®Kƒ”gWñ³‹ SŒX kýànû*ÌR­ïüúïÚÃæ¼g¼-_ }Ê{ gÍçÙÖ5ð®ïŸI|JœPjê[ÞÐÎdvüÞ$Ò©ƒxÙ¸Öû+.þ[fÿËn˜Œj/ËðªP=Ÿ§JïÆ×[ïly„Qßïç]º¬÷ûLn¶¡&Û{ÙýTË“QØs͎ܰcOÚò“Ãîà™Ý 7~*—ÚqŸ /ƒÛª™uo&·¯QˆóžºŠ‰”DÜ7)`âR C.ExˆöÑG÷ÇÒy}RDº0D¥t´þ9îÏwÚ:ò„NhßFVv®ƒÍ±IÙÍ$”SÊŃCxýZ1µ“ ‘w÷’5KÏ£ßZÂÉ!‹gªàµ,A<ÑS\Hþq62’GŽÈHòšnô4»€Âˉpæi®ò•6þrâÚI>÷uþ+nÁ +r¦È„ˆýéã"’# æõ4ë¼ýqúËJè¬PÃŒ­D=æ\2&ïzÂù¡núÄ„4Á±Ûã@R3BÛ’aúÒ0zë’8©œY&e©xëR~9ŽÓú*GµgzÝžÎô­ÁÁŸÖ¤é­}ò.‘¾ˆ€kƒÁãæ?ˆÚmËu¼Q$ûK ÑX"†ÿΉ0w)lW•¥9Ûâ³dÑM÷'|•{‹-9A`•<Â鸀[‰‘c$Ù]¯h¶ ZŠ9¬-‡'z0Äʉí,øO6ÁDßK´N؆%M™ û/ãL%iìÝþœšÝ§×º£É%…Rj-Ùçóóx­S-ˆ½>ÃN;9éǘüo&GÏe¯§%ã€IW»Rœ-:çÍW3÷çFtuû]:Î1‰j¿JÁá(!ê2Ë‹}XS@£w.ÆEE »ò:Ÿg²Ñk¹Û0™›¥r¿T{CôÎfÎ_¶–•É@º˜áðçü{ŠBm}PÒ²&\þu ˆñ¼8F¦ £šæ³!Ñà»ñ {trÙy>LEIXj˽¾eÁ>vî ‘×}¡Oø\úz¢Vý„°ŽùÌðE )×½C£Œó!Œ›Äþ—]}ÉŽÚ–‘ŠMx*FŒ­È7 ã iFæyz$?8îö§,Œ9ܸ›ý]×_?°+ÑœŒ9eϪްkZ2 ·_š„z-„ñëYÍ£0|b:…ÔùFÛ%*@óQ?÷2tÛ©´ˆ[R¼ð"iáù4‘&û7ýá-•‰`f=NÍðÁð4šï~…{<€]¾ŒÇ Z@ƒìÍqàöªn§–i°óËIúîG(M~hL&CÂ×p4¢n¼2'¿¾Aö¤pmŸ¹, BþêFœV¿€\‘ÎäöØ îy³¸I§CGq¾…îˆ^„"X’w±{hÀþ xµmñìÈ$>_î‚ÿs[26iûvÉ–¹+ýæ©™K“œ„;T@R‚%·¹R»”hvdM[Ð+Ï4Ÿ&–cÅ6?ý ûÛüqƒ¦2âmvžÿ–‹·¸bu¼1ÊBOPnc ¯ÏävŒ”âÅox¯[‹)×lA‡¶n°µõÆ®—¸¥ŽN\ι²vG"Ôû¯äq3CxÞ ‰8VaÜ”TbÆ?¤8ï3Íwé,-²èÖª=Ó„m¿Ê ëuÖ¿gªŸr»zÚ1uÚ l(ð$›"”ÙvAR/ñKMǃ`+ÌÎmGÙþßøÏâ¬á ³h©~ü×ý5óO¡¾ú™Ï“ »NÁõçuîTÍîË»¸'zI°®U’¾ž@B‹À»{:¿ çÛ}…ìØkܸB¦zú(A•¾? 0v>yßšEF"D&ŒcÁ:·¹Íø8~¶ QÔëJQöyï*òÃËŒ˜X¨€æô7˜þl˜\ÄÃþ@Óòü¤ý`ÍÖ |Ø?Wƪ±-¯­ÉЧ!¨VP‰YA,f¥‘îä6f»‚—ý\²àÔ&2öø Rœ‰#Aw ¦P” Š_Ç©3éÜ$Òý ß·âM¶¢l&xœ9O}Cã•ø¨ÿwZ7iWSÙñÃ;Я·‘Θ3‡˜ö0Ù(BÛT4è»°ZÐŽ áÇ`c…?!qè<à™Ÿ0󫪃sR Dôý¦1^—ZÏK­Xq¾9è”.‹ÇÏžDÙ©ªl¹²8Zlj1ÁÕ]X±*m‹UIø‡>|óz6ϽK§GÀ9ó'xÞ;ž3?MǸœü=<ï;ˆËU°Ÿ“.`ôÂi´Já>^V%Ó®ŸÅÿå#kë ±§RܽsW±@¶Ã·Ã«|LoP'›Î«2ËÄT~«Wn>„ów›y?iû©Vˆü4ͺ9‘ü†ù±öäâéU,!:Ü0Ï[ê`ÄåN!WuÎÖ„ÕEÀí‚ ß] ‹E”ðÑ£ƒÐ:³£â²;«ÎÀ¹Â»Ï»NNKØÀJÝÅИäF- Ê÷ÑáüER‘ô Þàî=³Éúoåìb‘ Mn˜ÄÌ~áì“ò,Çüùüe #çØ^!h{'…?õ¢ÀV%3 É8©ÐôŒg}éÑý0®ã,XU&LX¶™ÚÅT³°tæ¶|y9ý+,ÚÀ ‡sùÙ±FÌò¦Õ™¡ËäFz°»z2ñ¿/jc%Z}£CàëMÛ½…7Ø‹…SaéSUlëµqÎꡚ›ëÐ?<ªË‘¥FÅìF~§7ÌGjmÉIrAlîvvÅdl¹7žˆN&ÃÝÚ¤5 …êH÷"OW› g3¤~\“%Jg¥¾Eÿ‚ódoA ºÂöûä}"s¿ ¿\&~FÀd}nEÒƒ pÝןP¦ÛŒ‚Kf3s\rk"³m/ÿlÛ|»&ú{8½¬< DU€5i]ÄÁPXU_lI©p$ëó\ކÏÂê•ã‰‡Ì xŸœ ÷ gÇN}&#½ö³¿¹¹-aèàŸ <9qf÷%N~œÄŸ›· m\ä•_ ¦-ÎvÐ1dÿ%Ís#GÏé2ÏŒæš=ó¼HJù ·æ.ƒyQùdµØ'\›ä‹YÛÖ±,Ó àò^“„¡;¨ ¹qó¸¼Oò¬v×9#âP’‡â¶¼ðêçh.Ê‚SU‰yf $/{Úºz¬hÇ2b)Öϲ¸æYò$åH!{¼°7³ÁbçfÔü¡É¢”ŒIü¯Hj‘öDcrp˜ƒì®ÛèrEPl 6oOeÒgá¯6ÚwŸçh°ž±lâõتODï(‘JQb‘w˜]0ÞD¼®¡;¶Ê0‹y<¶Í6‚ÉËDó _Àãü>ö@?ŸøŒ-d¡v:l¿<ù´!„8nÞǦÅ1¼¸öFXÞª(öùa·±¬†‡òXçò^l;} 7/å¸Çjè¹±Ìúï^¸ºQ+L=ñ3ÿÚH”qò2ġՖ3šdOdÇþæT“µÉµÂ¸Y,÷ßKP'“,™ÙBAsø×åXCü²ì×/ì8v–é<8Fžk˜°Záspy_ n•Ø…É«ÀÈýq°ŠKñ6©|åª ‘)sòÈ3%Žê±Üº¿ÜŒþ5 xÔ’½a¦ϲ;qnüZþƒþ;xÌ,—{­îÄҔײ4ûu`1ù=ø½lb³&5‘oË.òNûîdÆ3ËÈ«çÈúº¯°ò¬Û­w>Þë£'±—7ØjNàæö…™,aÓ ”Û-Ïr']„y/‚ÉσÕd‰Zu¾Æ¥Ï»Š?yÏB‰á©–+ÂǬëØ=²2fô{/›è5δfÂÞÓÁLyL%Ó¾ ìsôG|¹ñ{[~—’paµ?×0ñ’±Ì°4–+i°²Þ ðÑò$á ›ÃW­R\¿-~k®Åµ‡{Áá°¾ùÂt„HéÌçôœóml^î C~rì¡£5™Y* u…Éñd!2ý÷)l¼R†oo%Ó=;±±'–•,¯9fôý=ÇLmñÀ5ð›¡_sdÅ=zÆMç{Nc™0«ß … ã/ꕳ™i”^ÁÙŸ‘¤º·Xù=לÔ&U²ÚØÿo1z_ÛÁ¼\|è¬_DÞÜ‚Ÿ:rbÙµ4Ñå œTéoÝÄx6bìÓˆ3ö­-àI}‹ä*ÌÉÄï™LQøúÏf¡lŒÚæÝ²é4yû\(ÝÃŽd £­‹2nH€hë¼ BÍ4]WŽå·¤³‡£Ü~ë«­8¤»_^”c¯MűéÝïJ¤ýR/dd)’û]¹ë?”É?}ñQ$ǧG]·Ù·S–5Nv„ä|1‹øk@¬.*2s ?’5÷ÈJ"b0ç£)¿@¨^.:91æÌ´{,Œ™F¦BgSW(úeD¼Kb0¬[¨`Îm‡ß‡·fð‰» ™«×†{ë H¬]ŽÅî©ùuÎTk3k¼#Á~œ9Dí'Ò&‹ç¿ ¶b1Û" Ëâc•Ù~ïpä».óu*âBØ}Á`s#Ód@Hsh}z©«È’Ø­P«ìÏ¿Õñ€ÿþøS|¬)H*º“!\L„P£B°wÆÖ‹%v¼¡‡žbÁkð¨:‰nʺŽÍSlAó%á6pÁŸ|t»Û )æ°±éè*œ w÷A–Ó¦nwå4œqS&6øÂ1sm\-3ÊÕ{qóµG£zº€Þß¼Î.#ÝîРRKð¯O…!µ7Ôd±.¶Žqe¿}pÇôLp¿w’×Ç›ÇÜfÔÀÖi×é‰ãhiYÀõªýå¦nC†¶íäv•áØ„¶c÷§"1Nk`DÈ×ðüeÉJËNjà$ÈÎ,A±Q¾0وà kEYÑ-1â&ežIL`þF¾„x:JÚæÂÔÍõx››ÌïÂÊ%k1he jxˆscòø0*ö@ŽlNJïA6{­~ÿkËÆ•p®;ç²ïºÆ^ÝÁž']œ†G¬ëÝ _r±9¦™šh Òƒyßáüs%3‹4¹…h*ªÕ›@[ ýFù2 fí2윑{o߉òD y©ÏGo9ñß ×ì%w7P±¹q°M5Ÿ ä–ª+2É'¢{ƒ63µö`Ì7c{ŒªñÐÚ{ìwE™À´H©e5É[ñ†}˜B²¸ó˜ª¶”yXJ+Ä…{ÿ2£{-tíâbRèA<…. Ÿ0Ù5îä¿§Kå€Î*KRRCŠJ"¸ñ‘ÍL¯§cÔVÊ™Î,`^+Ú¸ Rïpvw ËÒÜ@*–€óó ìîê –ypÛ¡›ô@t v’fÃV“šþ~Ðì–£{#Ù´2y<£Šù›eÉòª©DIÿ7ßø¼~¬Ó¦7È&\Œ>q~¾­ìõ`AÈöñ«YPÁYtR¬˜s¬äé|îÀBiv(³'-iàÔŸkÁ·D˜ž,Ï·6€·Ž[˜P{TýàßÚTüõC‚Í(š k¢è¯bA2lL"¸öÒ±ŽB-¼ÉŽŽLÛÃf9ýõzô›íp׺ÏŽH"ž×!ÃçV2óAqb{‡Ò¼+—QàÆ"öõ‰Q˜x\8)L1Åš/ß%ó0ãWáL¬p:^ˆÑ-™™DÃÛ›÷õósûmä ½+R» þ›˜¦j<>|.Ͷm€²Ìå›8±|žÂjÌçvæEÙ/Žù.–†vbÍßîîÇgÁÚ>zîUbEÍ–1núן@^ÕîEÿ ŒˆXÀÝzÍm4Ò…ý›¦A[«-«õßÏ;è¥ÐŬyÝ[ÐÕµ&>V-¸®&>ïÙÄŠ&¼¡[:^@ÓäOôøQPÜw =Fü`EIy3·ç?ô!š¸Å@AÁ1Ö\'DÆý-¢]Ž˜”ËJòÄç:Oøâ]|q× ?Ÿ\†7“x•S®ùólüwñ/ªˆoÀÒ}Û0Üý ^L÷'¥è.¼…÷÷“-WÛ}Ÿ¼±‹Êõ.'>ƒyÑGàT‹ùZÝ vƒ½\>”vÀ%¶ªÄ¨k);vG›i YàƒEÛ0zÿ=ÉôÆŽ“ø´'€KÞ½ŒˆåË“%§¥È±ÛÙa#]bpB³ˆä³éÕ7`óHIÍ_µhÍ0#ÄÅä¿%€È‰[0“`P=ÅŸ±4ãh8ÿ5uÕpK/ÍfÉמCì ö!ü1J&ÂÙèH.»Î§«ðmŽ­@‹©TÐf·î‰-'QÖ«÷)³‰‡¬ˆiQ0x$ËÁï†ì4¸É{˜ï‚ä]#Z†—]Ë1Zà;¾dë"ZÌöÁ¬R)¼Ù­@FLj&‰SÙ“£kQD4f{àô­1$}|:Tngÿ¸p¼¦,B¢ëþÿ±iòJÜr©’£õ¨áû(\ê¨K¤Œ¾aã%k²t©1NØ) ~‚˜HÆx<ó.ê*:Ñ“½¾lܹ$ë‰äÅsO²ngÈ¿ ]&ÊÈ’µ!‘ `ì?˜¬>™7X\`ìªy~7ËXïÝ'Èr%¯Õx™ÆpO¦÷áÔ[ù$sãW\¸j9N=- 82ü9àÌʶRºÒtœY®È‚bƒÉèÑæ~Š%qìwúMt;‡ön©w#¹ÈMÚ´eŽ&j5aC+¨mjuØØº'lÁbqwT§†+y,ßB z¿Á÷SåÙ'±š%!Úʉ]¬Ë&üýê;·„ —÷c¯_Ø’ì¿­P2QÈ’ð WÆM®œËô7¨½zaeï±dT»‡,݈L)â"9ò5‰¯”'ÌD–y°ë7dض´„,"±;úÐíàWìݺ–×ooc_ãïÍãXHÃX¶é&¥1ž¡à|ú+Fí}ŽB"¬Bv6 Ò‹dƒÍãÙo©Vk%ËiÛŠý`+Î)­¦{|§_»¡=Í€Dç¥U¨ÞbFÖ6‹³cÜ$vLþ;]UÖ­aŨ?‰? }*·DB’] €^^É’[ѧ8ž½Û¤n-7O”U fà¿§¸a^(¥¸“Û·Ä•ðŠ—ú³I-ᇌ'¦J×âÉŽ,-ÞƒéiI3™û ð>{KÀü?ÊðDT…ý;oÞçÑOÄÅ1£kZ,uåšíʸb*woÕeX;Ýš9û¬"IcM êE§Í†}?°÷‚¸ÌöÇTÀD(o5âÏZ=§É@õCÉhî]‰Úû„˜÷t9È›—ˆo™ë[+Î…‹*q²oÔÈÜíEð^Z«/…òáçXè ÛŠ6`Å +îÊ×gø=q™t`÷ûk²ù©I4æaZrU0f¨Äߺ“¡ó†(þ–Kü£"ê¡8vYðÝÈ›ïÐ| ¾—Ô¡ßÒx Nªz5)xåoeè–†.¸‰ÅÂs ¿£ æåà ÷çü³ü±lÇ äYRmµêånC2Ï»Á&Í^Ë’õCõ?êLÔ>œ‹/Ï ‹¡îÊ\éÃÃ¥¹‡ÿ¤a`é|ÿ/ÂwÆAW•Q¼Ç—|$Z?oí+þà$Mný&¬ùádˆ®CÃÔÄKó Ù‚˜)´EØî±¡ F§éã1þDa¡kZQ†s³îð²PÝo S-÷3A—çÐ7d‚ë ¸®ŠHL‰ñ k”ú¹¸¹/ ¶w ³6gðï¡4ÉpÉG»ÏÐ-x,™¦+G¯È?­møTã6ZÎÎ¥}𘣠ü²ï¥\!ªâKŒï †ÅÛŠPëæ$¢wº™ ãc¤ÉGŸUô@k1íùƒ'~†³©}w¹ñ[´YýHÓä·à¤’/¼œ:rìPýf¼€žHÙÏ~<“§Ê;AÐçü›&ÆÂfgqy׫ ød=¾ hä“ ùgöyc×bº#U!²Fø*øM³g6 N¬)q2)´®Är‹ н8Ô ¾š’ýNlÇÎ3`30—ÒÍç¨ÜÃ#¨Ð”ƒrѤíå ´´#öú~8-Á…ýF^ïæ`X”]Žî¯ò°_œAûà_Püó>´=¦º²°>¾Åm!v2±…p—8aÒ{[˜ÜQd^%æß;Š…šYÐ顎ó_ý†7S¹C Liö˜¶l)3o&s}) Z]ñ°Á®_ÿSÀûKíÑ`^8†+ª3!1 þ÷nlx8*;ª9ÆL<ÃÇ\øJ›<ßÂÒåÞøl…}-H²"_PÉ_à¡»|S+Âò¤xì)\omCìsçe.YÄÎ3!®ªtkÓºcÕ§±³Ÿ]IôUÊS¯²cnïFÁŇÀ¶t ~ Ú€ÿ†e9¥¬p;ÿ„Zx_Æ—é`Þfíø$T“ê×±ù>q¼¨‹©(u}+ÚXSº_ÐòjèîØÑ¹ç9Ÿ§dGžDTqºIµð¯Þ­¿>Å7¯G9›÷7º}+i.¬ç‡ùfpûNŸÁÌaw’R,J…l¾àK"L\œ‹¿^ï@q±K¼›ïø´KTœSnªÅäCWø]ödÀA‹ q7gzµÓàõn*)û*á~¼Ô‚* þÜ ¼Úž‰’ ‰ÿmC"ïû^Çüƒä1Ÿ—2s'’è¤~^ì±ñxv¡yè)¬=Š»phí9gÁ¾ì5KíЩOæ(ßöu˜Ø ãRI¨¦Í á'oÙÀ[]¯ŒfiiðÎ:îÕÞÃKÙ‚üíá@Ú¢i€©$< ©ÁÞ¹™Ðóx Ë®Kn‹î¥U)QàQŒÙ'sëXjmÐ…i.;qÜÑ lä« fêN`»¬Â@RÚ€|¹#¥©(úé<Îi~ƒÝpÓ1-¶¬>ÈÀ—·Ù§­9c£¤‰aÂOx*Æ‹ ÙM”…­ØëuzVKnÍTdwó üÇæûK˜¸PO¨”‚¬|.A¯Š:Ì †‰ŸñýÙ‚â<ùíšpÐð#ºq+y}M )ø oëáY,ðC7 &žᩬ>²v–oÅ5¶Zük6 ;9‹ù͸ð rY¿Ç’{»Øí)b›"Ïn®­æV «“ü%ßÑŒj³m5AT ÚpÅÂ+Dàp"—³þ?+^‘xzÏUÞsXµëŠ_Àsñ„7~Û*þúgŽpÕ] ú¶[7-„VåaŒFûOqWæ÷@Gd*ŒtœYÏq#ÙN~ôbà¦G–:ùr½ónÁôÚBøxS’õ½¼6ê;ý8³Ç{G5w¤þ}êægEúã¢r)8ëóø½˜ø‚R~ßòn ?]·äö‡ðégT%Oƒtñ'²9“È.C-Ž+ú…ž“h—ìÔ›þÆv_À©Î•”(¡K–‘nû‹ÉÛºpÜ e2‹wÇj·ñƾ‘'+s®p3›t‰¹ü/pû.Fö¯?꯿àD}9Þ­“ Õ» À\yãD‰ä' 2Ñc¹[Œ6y ô½ý,BM'ž¿¦àoìÿê Ó:¦ÁÖµ"`T;žº$éâ«oë±Ré¸vYR¨|’Ó?ÔûMþâë<ÈisAyáé$qÇ Þ¿$ªpò­ ¾Ámü§ƒÇ äÿyМh´ò¸Ç¹ÓW\ß-çT ÛÔìÉ´ÐNªöQ›¦'†BË›ó¼‡Ã‹èجQý ?ŽõÞR"ŽS¾€h^ .­éXÝ^Íÿûå’ù¶^~Ø´°óÉȤj%ïp9÷ÛJfl/ÎHžíz€zÒA.ã7n0Aõ+ñ,߆t¾™Hç^üƒ]jQp \—Ä'êRž¥/¾Í¶ÄfŽ`cØîͲ,ìúJ­ ”qëâi´ªÊ}õàÞ hüF–¶¦•Ò=®vP5g§´Ç—üTûIw+ôcß ®KG…ßâuîäNxã'ñÏôð2Sôμwêé®ÙÕ¸èþ<æòÙŸÛ0ÙÎÚ6Àúê°¢®ˆö¼¦1gýÑ#º™ºã‘Ùx‹wò @ª†û³>ZÂð»X›á”Dï=Ë<ÜDr.H³­".d÷£DxõK“ìæN°\®å~„úû0ÈÔŸË™ÍAÀàþPqå»>MÕON O;•hÝf#R= tøN’ºªè­Lúiþo m/Â8gàÆ…×X½O’XE’ã?ˆ¸,µ=|€95Þáÿ{DÊÊ`l¥IåüL!Äj·¿nÜ8ô•¥ü+ƒEa¶P¼Ú‡9ËÅ+…Àõìë°v ŒJíMÁíÍïP.l9” Ì=¾¾NÆÕÒÓk$É>Ý¸Ó ^z®dçèY¥Lc f¸å«;Ôl‡Wqéxæë~JÑ#ö}ôÆænÚ¥ÚŽãÏOeŸ³TA8íNÑo‚Ç‹•¸šs2l¢èQ4Ø…NÓáL vïö\yú",«ˆƒw_vC\ÒØcÄš?aŽêËš¯{BIϽÔ>Ô‚EÌøÄJÛ×áØÛ:Ôx­6ö&úkÐõŒkµ^BL/NE£˜ptw6iéxQX’|Señ†QܪáSøÃO BKÐ1ëÑwvûQî\Š©Û› iåpø®"˜0c§!¯®ã·òþe®ß¥•{U4d…ð{{ááÁ<ž}SÞáJáÃ4¿á'îS!/wÄÁVÓïY3 vßãÎM4ÇO!§àùŒnQ²<ö:ªÓ½'eQNø(7IPš¾¿O-teÙÁúäukÛôGmþ]¥°ËòÚàÜNÖÈ-ÀÄ7Åus+.&ßÃNúõ{(kÔŸI¢¯ôÀGr&ú#‰eZgØ­ÿƒë§òýÃV6)#›ŒŒJR8ÏçÑP!‘i‰¤¥”†•¬¬-»¢hPÈy?O )i-Jio •ü|¼×¹Î{žõ¾Ï¸ïûºîsóÿΡÿήĥAG¸&Á`úN#™uìØÂfÓ$j:á·îU'i,þ +sO`µš2¾4´EÁ¹OàÈò4ˆ=Cý£ŠËÑÁÎKL‡¾;̱sµ¦ìI„¿±cËìúñã[d+›Õƒù#ËÉþ‰¬Ödï®ÉÔrêPÒ÷>¶½ŒeyÖqh¸å6H†¤ÛÑæ®oca$ r¶ Û3ãh»B<«ø—–èÁ2×`jkˆfþ•лMƒ®µÖ£k.Þ ú~=ðš4sfþSàÃÆýq#›_P­?ŠÀ¬bmW•¢ã%ëÑÙ-ƒ­mé¾wÑ÷IžíÕwàq®Fu'Bš² •=–ƒ/"¶Á´€3œ¥rzン¸Ë_Ÿ“V5D…¿IQ“%{%g_oÇpMè¡¢,àäú ÒµÐæ—'»¦ž/rUßÓ™ìŒð+‘6„ãÞ'¬-OØÂèÅi¾1BMS:]ü+(ø‚ð#a©­Ë®_„÷Þ‡`ÇD1–öyZµÊÔùw<¿G>½øZû¥¡9¹˜' #Cý>¿O{äñô©‰° –Òk´•ðÏ €oç0×±MŠqùà¶Loóάà£D$×ÖiI¦1%(ÕË‚ÄgÂç² LM“+Ù‘31ÍÁ#=äâÁ3$±7gœù2^% ü"|«Õ©qÄÞÿÆgcÞžù)ï @å;ì=´Nÿ;G -³`á%öÉV¿ÝŸ€s¯ø¡¡ ¬]6Fž¥@Ùãj~Y ß‚¥è˜;>,xâ'm÷ N™w‘2Á2˜&à†–³p7²Éв?øýG?ë¾ÂNaY]´sV»q}±s©íÇMPbê΄û0u1*Vo† ÛR˜Kš“‹rã4èmÔ7g"\Ì](Ž&›¾ðç 3»™M\䇥¼q+®ÎŽÃÜ£æTÒÖg«öú³¤£ÚA–9lžANÞ|Mî-³Ä¤Û8É|¿»ÇŽuç'ñšÖ‹À˜ßŽTÐÍ–.‘Ãcºc¨{ôQ¼ ÊÉ®¼Ç¼w­Þ§âPo/ë4 8Ur–žÂ|¥‘£p\s/¨i·îi@‡Ö¯å·Î¬#áïöÓS®£öûel[ë9:1GˑȣIPê.Áù‰¡ãõqTùO-¸–Žâðžà+z+w­eÍo6Òì—r'9sék„¨û{UìÁïpc$4×hSø©Ë,¾ÇõýœÌuU‘‘žÊUœ·™‘ãDf³â::K°qêð¬m5Û~ó‘[a„2'"ÙôøqtöôjTóT6• KÔ&LZÒHÆç.gåóbPØxf©‹®PXso¾=€Ø]9Êoû¸e6Ô¢a?­lÚŸ_’ï+ŸC`Ê0éwÛ69N‡^ÐÕg•'µéŽoÝ„áwBF‰wõ£‚ðÙþSp¯26®íwC<ÕXv›‰‚"T£ætõ™°Ç‰IXpÞ…™ˆ3Õ];@óøNöÑÞ3£%Ðã§‹í—ƒ0C,”$´kÇÎøþÜyXuù7åEÞøAT^YÐ×v#ø3, uÑ\ átM bÜèÒ{b±år,ÚÊ×£‡Æ2öGç)1µG;ê¬XžÙ6@#×›ŽÂÌ½Ö  1®E™á²;òôf¶»ev.kÉÐá'ÏÐõí"ÖÕq„K <ÿFÏ‹4Ž_™¬BâÙ¯Š\TÌV }©ËR ·°ÀGsØ»Áû™ŠlN—)•œÉ^ŸÛ¾ÛWáܸƒt­o5êÚ;ªÖ¢¯ü :d·í¨ûSöœ„òuh{kô¿ÝkûRí6ÿÇ~q*ô{õºfÇnbøÓMŽÌÒW'_ù*Æí.óҽ¸ýD1>n£kÅ´™NwäåL”¹rl´ì8‡-£º¦ä_‰“ ÛÝ™z‹“î¶çŽ/U¤?¼N!ÿÜ5„–/8wø,GRë÷jØ.y }=Àzwÿä&f“ÌË3íoüáXCbì&Ó†Œålâö:@gÚïD%+o|^ÖÒ­ j^a’S ÝIÅŽm¼C¡ÿ•%g!Áé‚SR¾Åy„hÊûËM¸&6ÐÖ¤Ë< ÏqQöqÞ=eî¦ëvÔv+™m Í–NüDÎö<‚¾±JìÙ÷Bº6÷o›t·¾½‚üt×g'VäPT輡›[„Ñ-.¢Ò 8ž ØQó“\¼Å.ëçÇî\?Œ›» qêkM&ḋg=½ÇJ>ldÉPØKª5R!OÛ2›ÝŸÕC(À™æèóBŠ>ŸüŠdIâŠ1Üí%ð÷V|Yð½ÛþA«ô&b賯¹• e¡ÔÑà(U¯| [¾Hò©R }+˜âOàO0{õq+V°”Ï/ñ ØÓG¯Ös©RQD{b36ҿ¡ü^.[sšO^ÔˆÓ]7Rè‰åÆXí"OêZ&Ñw"渫ᱭÜVßnµÏ’ÝÕëèÙjˆ‰‹as*ÙÜS¹4qZfNª¤»^$²ä¢7Xa@—f2%×<õ·g-Á‹Å Ä£ï߀†M·0Í_…ÅdøQ—²*Œ²™FgéŠP¿Ièüô=·£2œ;gd¶rkޤМ/léq¶kÝlOï€ÌÉÝè}¬”¼2£«ÑOO™—üg†‹¢¥u¹à¯„Í›–áú·*tñ¿^kÏTö®p2§”,Og‘8'v9‰ÒI7m¸ïÆÔ7+ vþkÇŽ:°lÓDúsÌ.¶éÂAÐ8 CWÔÀ¦Á"èzzÊúÖÁ·Ü¡ñ}Xø{%¤KúƒêCˆâYð\QDB?¤Žj1-vö€0ëHº µÛnpýë2H¶¯;ž ÷„ cœ.ˆ–ºÔ{άûy‘sÿ›I$tzáέkpnq">³€3Ó<iÍ–hæpdVë2½QMóp¼Y“\I¼>«¡ßøœo±ÆK“ßâ×úœ*9 kóñýŒ 2o?^2à¶=îßý ðqâk´Rqbš—áo†3gê‹mVíäÒsc6I¢–œ­ªãúwݾKE0 ôG*ÌèáocéÅ÷'1¤ýy:ÁÂ3UØð’yT,4ä•Ü©_¸Ý韆¥ÖF¬Fb%Ã*,ë×d{EAbc:NzëM¾í?Å÷^ø”ú ©Ö‹½T}w%hÑAá½ÄWÄœL<Ù çÒÁoñ7n—ÕxÒÕ[ + ãßC¹²®O‚ñ›> ðwÔ{Ñ´¤ŠÔOt 1—jPå‰( ±‚ê2ip}kƒ‹›U¡Af<+H â*MEèÍWQ*L ÕEŸœÃœj»*ýy+‚Ý‹ bž~rT"Ô%•Ñc–¢ÔSý8À8vAÀ¯|lƒº¶ .ÊgÍ[„'ÝŒyß)Nø½$^AíI4pÌur}U!r·¾£íºJÜk¯HÚ éÅ€ ø²z*»[Øbw¾ñ®Èµâׯûðô÷Kè°M—FÈ ÂÁIŠ4]¬³ÚaÒ)aö{|øÙm€f„~{/Æuù&?mf>_îY!-6¥æ¢•tFñö®þ€Â<Ø÷%ßð™€3hüÄi³À¸âd º§¦’;DhãEÞƒ5„ˆð`¨aDk6‡0«žü›*Ç^'Qÿ¾±TræJjðM….ýRÕ˜°xˆ“ñScÛµ§° µ—©Û,]—<ƒ»qŒý ·å»ñô€13z4¬ÞgåaÄàÆü«± uí±ËˇY½ÜÊF6aTêM1ŸN2¡‰ñ :ÿ:°;aJXÿÓ™jîšA;N¾BçCôôN¶ðù¸9cUâÈö]±†Ö—¨žÅwg|m;mTEÎÚ=$õµ ôª$K™‰Ÿ¬…‚™VÔòx$™Ô,Å{žw°$è5LtŒf&yš4ìª0­³`‹sýèš²TK­¥á[éÇœ8ÿ‰{vΉ-è8F7(t£WyüˆŒæäÝÇ2‡¦ÛLÔ7*Fçw{¬ħޣ½ž{ÁÒa2ݾïíå¦O,‡õÂ2?:Î>þ»%AEÚ¯‚ÏüoL ¥œ -‡‚»·±‡—fsÐY¼!>‡¶Ê=`ù† ÙFÝ_Pe•Ï6(¬De×JZ&nÔ{žÐ3Rða– ù¨Æ4VбVç·ÄžšKÏT¯ƒØÁÇ`YíKÇ?”fBRà{àè™'µ™µ¹ohù„M*Tö‹õŠ–cAæJܵXž[ËRæúa)ÙõP‡³—–†+½.t÷j:qz. N"¾f×HÞ‡öHø _æúŽÆ}†úìÁ`ì7•Eå÷!¬ùs<߰ǾEJœ„ا€N ÎU˜ûþ8|Õ—Æl“ãÝKÉ~ß¼ÈPgòÁØŽÞZ_?®øÒÂYpEÔ„møvÐVq`F‰ÁEíûAgÍE^©ì .ñ÷ñ!Ð2‹Õ5I•«íaÛ­L®½µuêàç9ýÝáHyQú¼ÊÔ}‡4#"ë0æ îŲûôŠo1æ_XJ#÷qç'ž„’[i`¸%œz‹'¥×,Aÿ¡<}è7öñÚQbgmœÎùËË\À:̃©+¥…A’ôD„;îœ8™'ߢ›äæ3]¬ä ã¡ùm4µ2YÂ…¯ áRúP¿ M¦Ú:–:Ÿ™ƒ5ýã™Û‰ ¬ÜOÙâNfu/òî·AèÔilשìT.5‡æ£”ñ2ukÑ[&B¯|êæ®ÎÓb7¾¬ÕúMlåêWDYu'“ôΧ‘ϪéÙç6ìØ»çp b5µN{ÆÓÔèWðRi }äÕŒ!GAEZ0ü¤Zí¥÷wús>ûõè˜Õ÷ñFÇÎäΖf_?¢Õ‚bxb1„ߪCðLç'LÞHÇ™CTt&¯8ÌœÆÍzÈ90ƒÜ5À5б»y,"$™zF˲MMPzÞŽÍŒ¦i;MhýœIP=„wÄ"iþå|fŸÌÂ÷ãê{÷ñIø7¬øA¶Ì  y’»è<¡#œ™øx6¤]Š‘EŽÑBí¶õãÚÉ|ø¦–ŽçìßàP_+txϪP† T¨Îý¹ÔáÅxuÿU”Â/Ñ ˜©Ælõ õ3ßÀ¦ FÁÀO»²ö)(Å- ëy¯àtÁaHÖ߇Kªþ©qtíFUôŒ5b+LÇÒ£ª ˜ç°îê þÔ±siú—n˜W O÷|: [Ý‘?EYùïÛ˜¿A·ÌN óz#¸ï™ë±=‚yû~^Œð~M‹Õ aR¤hæhr™OŸWð-ÒkÀ2´Ÿ{ü¢{Ÿ€£ûé~^-vÐbv¿æÃnӲƇ;^q&0™̯ƒè1û`¾Ž›™bÈúdàZ B;B¥æ]xí Žï‹Èe©Xò,‰{ÝULïk,§m+žÁð´`Ôõ~Cv Ñœ¤8˜”>SZxO/ÄóNiG˜šTœõþ:ù›‡*†Õ¸e‰¹×}ã_;°mš_Qkb&ì™ÃcËyà ã{“¢ßqí»DX×ý¹lëѱ¤6Zƒ-¶oý©?`†ÏŒ²UdŸï]íÏÛé¥= ³&ޏ)£´Š!5Z®ÇŽè‰Si¡_x?p œ&A©ç9´ö”‚îšØðÙŒe+!*íù>™;+aÊvOpbO?@¥„Rû…ewõYŽÛ'ø׌WʰÙü Ï…á–yÿY.9Óv‰Q¸Œ6åSY—C4<ÙÛ,a‡ó*TÙñ’÷í†ý÷5HSEw}÷%ð•Ï…ä1±p©y9u;R'%æò»œÿÚñ9ž¼;–š–Š‹]U±§R™ZVAñ.Æi‰øà§!Ê—Ÿ¦sƵÑiziðò‰;ÿ?o±Çï0ªa(L=Öi3µuzÖÓ—ÛÕx6¢š¯Sð¡_.bºKø l·ƒ -9NÏ.EÃΔðúJVÐ÷gæÓÔѤæbüiÞkWWQÇ•“Yf"e—ôp2Eüî ø†²]9þ ˜P‡%ÑbÜÈU4{>ûÃÆPãpêÿªlßr$¸D„$šÖŸ—DÀvf5Y¶?­Òm ýñú€}Ò‚›nÂxJhÔZÔ#ÏúmÆÑMáǨèß0”'GEÚñõ/ ¾ Ådü¢8ü(”À)øf~ß'Â4'zÿøL*o£ŒþzSh6=ñ·™<¿.Ìn[Ÿ=)øø(¥®Aù7R¶Ñ•ceáÓ[ ºæ…8–zántˆåä~à˼Tf/ü¼<Ï{Û—aD³»@Éw-¯&aQ,×Îf²IE{q½’|¶h#›ÕWÑXš1A–^î(áü+µd¶ñgøUvŠ[T_B+©£bèîÇÎqÌ|oý:™¿®Î›5]Dw¤l‚ý-¨8ö8›×®D-Öí%ã dÙÆ)Z4gC þè[Fž]SRh`UŽ)?ŒWÎ;2Þ’®Ë1Íiálej;ï9.3`ÚûWѲè߸=µ <¾h±§{’ØçÖµÐ}´jó71nº*õj²:§¥ìï"1œiV•Rìò N¢>Íë}ÉØÑu·ùV/–Z®Ãî'g¡iO:ÝŽ’È«7AÌn^>lÍÕ¤¯ÇXö‚¼zŸ vO3ž™ø;™Ãe/7˜#Ëéûzñ’Yñ‚7Þy ÓÕmIJèd(÷÷eÉ*ËIÕƒn²=ÐeM7{fãËm3ÀÏw)6F,cƒAË!®0·(JÓCKpõ%`jˆ“¶ÀgÃdì×›KÕ»vÒVµ.0Ôð c ¯á~:<¤:•Þ{5Åß'W΢çéx;c!ݵò¿ÍÀ”ñ’#øRKl dD…Ûí+ 'ßã64"ì‹Ðq°C_íÅYB: ŸË=º;{ÄÆQÅ%Si¾ž S˜"@4ãobs‹¹V,:Æ^¬ñ¤UsG8hûÇÝp\Ksmª±eV‹’Îb'NQÍ…Ó¡è##BŠ… t¼†V[Á<—Í( ×ðάópæœ/Æœ:~ZìöšDøZQF¶¦¿çÚÎgƒÜN;j6ki]J]šp÷™xþÚƒºwsøîËìè“ñ°ðÖdv:i:o+Íé,¯SEjJÐ0ÞDÝ«AéØ8¨h‚ÇYÛ`C{ W²}Tf¹;±ë™è÷\‚ªémýuQà| Àfõú38µì5xÖÒ •¹-Do-©…ðIâœÂjcÖò©"“—° 넸EÆ„í]?\Œ BgÜp\^4ýUY™?“¡ þwaù—Æà m8’’LD”Ï‚¢fd?¶áÚoÇpáq,3á,©²pc²*âÔEÿ!Ö§ž…09i&×Õ„^OpóƒXvßý/JßIáí»m_wv‚`ùf{!‹Ý~¿¡ZïÌE†JýøXý$þ{¡GæÈw‘|CŽ›ê«dlqíjX0þ~ý­“Êe¡}Fd ˆÃé(yVÑp'¿Þ/$ì03Vˆn¯Ïƒ“V2Ÿm·!óO&ÔjfÂQóaRf„û’xÁÑg¡^匬jÁÙž¢¤¤H×Í¿¢Ô>­•3Ÿà•ù•\T{ Jß4ÆÁm3áŽçxÜjb É/rI¼¬&¤q÷mÅ‘'´ºˆ½nZ´î€2éï#JÅVõ÷—ËÃãß^BIXý.%ÂÔEÆà¸óÒŒ»Žû„F±#š.ÕUÄF—'0ò&²Ñ?r%»ÿq÷K8·aƒeŒZ™¾šì…ÿù›yÿ\p•Æ”P2g†á&h¿O„©–†­é¢ÔecyóO>2‘bbKYG|:oeÏ/xpë ûzËk’@éÍçÿ¼ÙägˆË^…ï2íÿ®b™¼"[^­ ¾îÛéÚAiꢫDª'²ñƒÕH•”XF›žê †%§ÒàÜ©Û$Íþ4:=›„ýKŽâ¹®Å¼1´Ø˜¼ãD£~-ü*Çǵ¼èy;áÛì](9I$îÇa}W9W[s6D go–¸íØýé!Ïggsà«È> ÚiŠñ/WÁˆE='2¯œg¢F¯½ë)Ͷ:~¹p¹“DðPÁO±Eå»Á«o+æ4Ia¿ÇXæhmÈ=KRö)#¸åἸ«–Ìûé@mj4¨ø‘$$Nr´¯1Çj“ž{W¡÷ª.}£Â3vÌÀø¾'pØäc£î§)ZÍzTÝð{Ÿ•㓃Uß— œ‚_ã²°)f´[3²Êœ²È¤!u k蹃f·WAΑË(Nƒ¾„ÛqG¹ƒ99áµtåž%X²ú5NÈÿ 6Y7 §áøÀ’—ôú3=Ô€ÓóhÚݯ¨ŸÐaûÄÿ->Ü2ÖÕ± i. Ï]‡Kÿ Ñ×!©ì½¾Ë.âÝ]¯S^ÊÓEuÅÌË\b‹æC¢W JÔsÞÓüa©i ,|ø§d(áó„­¸ñú |£}»tîóžK(Ðû»ð§Ö{&$¬'ÓÔœ©ñ"V>õ:—«d„lj 86ž–›f‘ý÷ìadÂDXÜñïÜpâ²õÔ¡ú}Ö+k2•“7È>iËW'1§Å ëвKÿÈô˜Y4tJ'qô¾ƒu„ißТ ¥ƒ2ªPY´Šë4U@í{.P6Å”’Ä><_3ƒíݘÂäfI£ÙøaR&²jì»AìÇbrÑI…¥rï™Ý±pûÓ-+ÈfðVÓ yú¬Úv µíÃ{ëQ~uÜ»#Ë·óW,‡AJ“ié„™}â<Ñ”WfÁ}H(ÊBŸ†¢Ë”ž‚“!±èYÒÛÊ?iI¿¨åáÎê%ðð¥ {1_Œ¦î§ëÖ®c.)cÙÈ‘ËD±±ž-ã½nÄØç04_þö5‚ü¼U ä2HqW»8“'ÐáX9^Ýw·¿€—¦'HŸ‘ Û·uô]j§ñœìäG×öc8Õ/ÉÆñÜl[M'á_A)M2¦Ÿ®ÝISéùíä%‹ag ´Ù¶~sº°æ8âú„ñ°­ç0(i,°ý÷>¼–À\?ܱz\ gm[ð6;¹·m/¬zÁIì Ås UÙ‡=†´Èùçþ!^´mÁs‡É4‡$é¦FÔ¯Às6 Ž&‰±ý—n“•;=QÏV~ lZÃ\gH @q Ïö…'^pýƒqPr܉#- Œ¶cT䩯ÙÏ{ø—ºö8¹Õ£¿Ô›á!(a¥‡KVÖfþ_`—Ey/iç­ùª&ðÅ.œ¦ž#[Ë¢‹ÈU¼7ï n<ÊÅUX!ÿ‘slµB—* û*ACkÄðÌ [‘7îø’H³ø2ÇFù׊ĢVÉJLfKÅòÑÉÇ•=¹† opê9ï±#j^rø ë®Ä£G“.7–Qøb•‹U©ìÆÙèÛ'À2Vïd Ä:1µ÷+ö4b–÷Í å”Néž žxžx=¡“ÖÅC´¹0«KZ„•Gýð¨æøöi#¯.µ ZÚèÛkpÓ¾ ºÖ‚Q—oòcC\=;5bé«‘Hd²§Ùý18ym, ‰&Yú‡`ç®"˜¿Ðÿy<8ÚÇB‘½õeÎ\Y×?¼Ó…00ö ×zY…ͼ+€|3x€ã÷‡ š2èþŠBØå} ŸÝîÁ©IÜï `¥bŽ%§e¨Xj&Ì¿ÜCÞ—ÿÀ‹)ψ\p¼S;Î=}ÜA.Z–ÀüçU$8Ø–ZœXǬ¦¨±kaĪg]¯!â1 IRZ3¤Ðxe~êðG~<õªš€’¡LrÒ ®?¥­º §ÿNÄþ•Ïaöžnþö;$l„Ðoâ*²*vŸ×G[ûOØ=ÿ8ûe?†>ÒgÅ«ñsÃ*:3„²Ô^ÝÓ¿…è™´O_”žÕmB°:E¿œœDÓ]ša™×~80` ­Õ?ùSp¢ñ/ìŠðD·BÔñú]”×rå7Úîv¢õ*ðâœÏ‚èÖ6kƒé-¥ vî;/º6F^Ž@ƒî= o†q‰ÖÌ·‚nE`pÕ|ecH¶N qw-Ø” ³Ñ*n65…ÂwÓÑý¾ŽƒœÅÜ}^-PT` otpƒ‹nÞü‹œá'Ò/Eèòñ¥äaÃxÚ±dÝïŒ{ÛäH ¿  ƒì÷`ƒÞ]°»h‚_f[ÁS‹nrêKÎfóS~ñ„<"’l@¾$Å1Áj².ͶìGµŸ§àÔ´îæ÷BèsþŽ3+ÏCØË{\Ãú[üoHÐgŸ›*X‘að(û üå;¢Ì{ľ©· eM#*[åò¾$qó¿êr¿¼å©æ#f™Ö w÷AXÝ*ºèé]ù¢¥£‰ílPrÚÞ:aD³,Û·S“  jî1‡˜Ï?I”f9ÃÏʰ7K‚ºl$³÷6ÀfUVZ8 ßÞäˆ~ÅL\v^†•gÈÂ!O3.ü7Š[ÂÕM~ e}Þ[µá1}qºMn ?ÛŒ«B‚¸Û/Pb·=¶åMeòãëøË Áaž•›@Ú®a ͦpîœ8k¿´šÝ;Y9ŠõºL6$€½½¬IN=@2{.áªîê±m"Û»;’v¯È‚öÓ±Xµ8–®)÷eBÖàˆo9±X}ó÷ß!nŽgpsæÈð åì4©¾ RNf¯Vj±r?n&3`*;Õènµb*¤?€ã¦®v7§©ÆŽtž–.Ö©­a=áoÈT91þë¹»™p\»ºL”E>NªÞB˜z~<ÎEãûµøQúì¼®Dµ„¥Y?ÿMFËŸ‚ôøçD<ÿx%}ײ>_Qd©#óÙí|îsÖ<Xz5BsYÖ÷øûçãÓDO®ATkjÁý7,1Òå/äæ<–¿ÿ#®ÐiÕ(üîü­Ï…ó¥°;˙ࣙÿy¦IÞñKÄÑTŽU”„°Š„Hò>g˜ñy†Ü:n៛p½Lí[7¢_h·ð›ÖœùäxHCûïlÛ‚E]XãBïØª‘Á©Øa†Ž-ö(»nÀ&žh³ƒQpBÿ¬™5Ä-ìÆ·R ™îä:ñEwBÁf¤l Åýåsà =É"–a‘ JvKÝuØ·ë>}Ä[ 'B¦Ì"º·íä~•Ÿ?!ÔöY¬ÿ©G³e:‰¹Î¨VÏÂgûoÁâõqø>E™¬eˆü­c›î^q3.]äìê’Ežã)µã,³q¾Ö(7‚9\pF/™)t‚|Þ{Œ—“4ÄÙ!Æ M=éê‹÷ÊA4qõ8±yy‘hxNv]ß ZõžøõÀ:*Ù󎼕(—ÞÇ¿¢ÕäÜÀ ®Na°K/4†Î#ÛsøçvN¡ÇdxT/O>ÛAð†êR$ÊMC…Ôø'GGþ*@Û¶ð u²f s“àÑ«‰(žs:Bw³šT»y}1ì.)á–ñ×PÏÂŒÆÔ¹Âl}m,ÙèÉþ>ÀªÏ¡ÌÁ¦ë"8”æÌfOž ÆÏêÈóò6hùެ)•uô>€eª–zÝ’M{Ÿº^^ܪ¯Cø×r€ Ö bÚÉŠ´wT3>œÅ†.|×CÑôjât¶d¸C5añ/&UÕ`{ZZ’õÆaÀ‹<œ#ð+´õ½‚ð.)Z±¬unÆ­ÓÂanftÝ6AW…™¨ðøØ5ÚRë)Õœo®µø»žÏMÀ-ŸÅÈW{QZ}d×GAv®ÔêÎѤ•…êÔox÷_‚uò ÿ7 >¯$ÛÒÿÚžzœ3¬|é/ÌÂc©H2ụ4ìõ€ˆÀ‰ØæÐ…öÏ&R9³`TþÞÇ__Èñ'_ ¡{v±PÙ·$åj6ˆ÷ôqÃ÷-P²H›Ñê"~ÉëÜŠª’6‘n8Å¢,zŠ 3Ú;žå4£vÊY¼n¾»6õ!7xÒ‹z÷Þç=Sº4ªy4™ø6'¦Ï²áÄÝǘ¶¶…[÷'ï¤ã_ ù’EoÊF²¥îq·?†Èª]˜*kÅ=8PJfYTƒØ:?º«´€möŠÀ½ž±0u”§ÐVtî“ÉôåÜ÷èàOJ¨Ó>‡ÙßÏ>txÐØÞµÔ©ÅÖœ‚‚C%ðæN)”¬Š$•R >D Âå­1+³,óÿ@¬×^XÊæ0õ•NÔù‡ËøÖ gÄÿ`Bd ûõ8—’…ŠÉëÙ‘õ›È½ç 0aÁh©ØÃ¶Uß@ *ƒŽ~Gp}`Š÷o.aÕÍáLÊ/ÂÝÿÙ«-qÖ¹)HB¹–ßGÇÚóV'4¡Á]I6±)’K#Ä6ýÄÍË»aº3V³‚Ø|ª1e5žžö—ǘ1Wù$´£öØÛÑÏÅŠ«o¦$³œ¸œIèÉÒ¯g“éÒütœd¦@Ó†¿sù[…™ºd»k*ÄÖlÜÉËf@¦\wº#P*oì6êÇÔèíÌÀ/Š‹Èú;Õ{xZaÃ0cýMLxSÂvTLbú'm“ö|çLzž¢óÜR:;j9ýZªlÛÚÙ#j2¸ÌÇ‘ l ãùmÂmã7(Õ‘½ûWª'Yâ³F„¯F}?í Fñ6ìÌúê7Óñ-.Û7×Ñcz‹É¦¥Á(°x9LŠß ìœ(ü'ºá=Ûl¹Þ‰³ÓzKÙô{¿ñäØ28×\ŽBQÀÉÖ?Õ<‰?ÀEÎïÀ+ë§ÒÃÇ@xÚW ͺ™‘ãY`±4xnyF6¬ Çý‡Úðö^Q*¶W–l>á\5 ™|× Å0¿[+©Ñ×>xÞ«AKßåÀre}ZU©Ïý®˜Ì4ËOÒËUhX˜¦md.×7!òbÐàdn4%2Aâ0¡_ý„kÈÌY6eµKÐÍÁ( –—Vˆ÷Ä_£×f!¶­øJ<–äžœ½ §/¥ãÕÈuØ":wdÆ î4’ à£ îq¥$8–-Ë”hÐ{Ë ì :u‚†öšÙ: E³±Š;9³=Ÿ@äPS%ŽóºH[fs³Œ–ƒb-©´ÑÙIû¹OÞ±zì–^…C £óÀ+X÷WEÃO×VñfM²ò@­Y—€S¤×¥VÙ¿!Ësäå±ÌÓnlÏ¢ð0%‡Û#?— &GÒ­‰ÛÐàh”/Ál›…ð÷± {O—¼÷5Á~>žÓŸF£v¦RŸ/ƒpó§^ºÚÝAgâBäîEíxæ»4«ùuŒÿøä506¼FÊ®õã×c¦^—Þ›Ùî²lõ¤ ŒYý®ñXèeÒ¿@”’Aü¯>ŒÞÃ:ï»|è‰kÔ’¯’Óu°»f€çxò¼l Å1a8Ûi?TŒ]÷|Ø6N„æ?ÚÍ ýðüŸûĽ.ÈúRtÐrÅá×sXð…6¨¦=Ñ»¹UÃסÚ>‚YFÖYõ}4Dl6ë¶Ô¦QmÎ`y~d;SÍY±òNÖ~ÐŒíþ.(vJ„=´Ž'§ÚvÃÖ#MÐf¯Ig*¾ç‰I¤Ó¨  Ô2ÔqÄ®²zôüR†ê¿ sÉOòÜHŒ>K¾/…èS—(ÐMš‹éÅ}x³:œÉ9ÝwöPg’Çõ GÉöxÚ6†°“ÒÐâ@·vûRÔ®mB˜HgÿÂË:Ë*“»Zb¼8"0ø}<¢¨Ó§&×jðÚÙ‹ì…_ (¹…Ðí‹’vÙÜ®‰z$÷Bážá– > ×0½q,Ô‡éƒîÆT:ã‚%µ “ñ· ¼fµÉl×ï]Ô›¬;Îõql v,>2ð ‹Õ ä‰Š.©G¾t">.‡.b=m:SÞÓ„o.G‘íó¿`¢øC[£S®øní-<Ð_ZTi½Êu+XI~ª¾‚¥y°%K€TÄ¥‚ío¶k½w:á<–ØÔ“ Sb»Q’užZÍM|ÎÛêØ…ÉsáÐÕMÌ5ù.™ÛvaY´Ï!o® b)RÇ0ÈáÌò@_}³jíýŒ:¨2Ť©PÄéAzj(ÖŽc½²õh½[ƒ­{+KϪ:±$Ù+,…Œ"ïÁ½5ÿ&©†ÜÝæLÇùõpó­+qŒJ 8ËV$c^ó蘘Vþw™h6ëÆ'0K(†1Û½ñÌë_è£q<{rðÔla¶C¯šŒ®…zØD;Nœ¿ÝÀñ1w—*¦4†qæU=ñ#VyŽ¥s#5Ù»ÝðYîÞ8­JKwÐO÷v°ƒOìHö¶A°Úÿ »Ò“輂ã¼cÓí£‹Ø«ùc 0S¾l÷¤Rë×q?V¾@§ j´QŠÆî› áÃúÔ¹ý=<5Maç$"PË~2ë°±Îy¢³V ±µaoÉÛ6c~*Ž@O½þƒOÓ6Ó5y ¼L†u6Wá{àLôu;B+„㣠úµBÚßè¢rÌì0•Y±ä„<±¥Ñú²Ç{N¸¹8û_:EŒcwÍBù StH:ÂíÎï‡ eîëNQš~.œ]ŽUÀ™† èÕx¥; žp^.ˆì´;|k‘í°âà–oøB‚™¸¢®²“©¦ "œQË¥ÜÙÁçkAhC°íÃÐsDÊK§qè;m ³\§ËdM¡I:Ôyöæ~^‹79ÉTæìÅXi öÇ?•4ZåÁ²’dîýé¿\ÒÆ£§<7nàÕQÎjªN$Ϲ=âÒÌ)ÑkÀÈÓØü6 }—ŠpP¶>xê&gim…2óVwGß¾•ìÆ9z!æþsTft§3S_T<:7nô£ *¾XNz­ÂÔ¬r¦£ Àkgå… DœÄ3=g`×v|GwÆä£Kà«B:3ªc†M3á'F¨îfÙÂå8¿ ñNì<–ždF«9qø&ÒL3Ïã¾Ç}PòGš™]t£Öw–¡D×iü¶_ÿó>kº?†ßǘÛyöt•"ó¯-À•èÝ,‹ânbL¬PЦ´ª± rð|V:®Ù;‘fUñuß>ÀßÅíðÖu ,ÐÞƒöîsÑ1þÐMÓ¡º5–}W± ÑîX³Å•~/¾ >^—I–ÛCâ³MÎ7aî ƒÏè²ùz:ôÁGd¹yðþóAÏØÞNsõŸ¿“ïÿ”Ùž ÷ázT¾#‚|‰ìK!FÛÑl…1È» R©Ø÷,磯l\ª’£‡×E°ðQܱDgÍ0àRøÇè53mn©ælî„ýV¾ ù ðþ-†ÖûœØóe…àÚ-ÁË]éŠ}bÔÅö0_¬I.Zr™9UlÆ7YÞôÕÁ‰<5£:úÁQ¸Ñtš*38ð”žJ?H]üS°]Ñ€Mô8aÜT|sd ›ºSô$ÀÏ#ÖìÈíN¨ 6ÄÔ ?é2þú®‡-~ïA¹]:ìbµ0;»ëû=c-§yQ—Õ~Ÿ@wï²­ÙBé9Rm®¨³™þíD‡¶YÐjw„u_]I­•.àÝ+ôn– ­lb‰Foqü—mô›ó 2­ òÙh}î~â=x0u¨=t£5YkØò(_únC,êFŸ<ßËíѾȺE®ÁÞ–@*0ÉËñ’¡ÙIsÎA8ñ'ìÇâÏݦý½rTXç·¾n ÞŸM‡ÏDA/ugׯ®£V‡`ËŽè­ûÇe¹ÿàWׯÑË:W©°Êª{ßV5 ¡ËËa ÿŽâ±;G!¬·äGÒ8—qr4ñ®Y:- 3TH»À&Ì9 îwÎÀÌ\%–:—2_ç«ìG -›ö¼&RuÉ`›Ô ó0žüăçV³t² M´ªðèˆ ¼>—Ã"Ý.ÃÛªÓøc±éªZÈ–Ü-Åüp?2ÛÀ”¹H±Ò¿¯àp†òó'Þw•eß‚íñ Œò˜1wö3¦ºM¦ê Ÿ’•«[ˆñIê­ H¿}öfýßñ½ÂaÊO ;dé7.å¦É³‘ÊCpt sÅ—ÁS¡¿\ζaü¹ÎFþ¾ÌÔp†O¾pá­MÐ,àÎÈëŠFoc'¶õîsøòa {¬æÊe9ìa‚ëÆQož3Ï"ÇŠ™.Lm£,¢ž*Îïø5Žé©É³}çýià‡'Üõ†$ÜXD‘¿1 x¦òì°§$&­<Ò¸F*ÌÅÖ.nM‚‹^SÅû-ÆÎÆéìsp)Ù”F%…+À6Q‚í<ŸÇšÉÛsa µà3ö=Aân)±…JÜ£•‡a‡à:jt›œŒ'Iÿ@j¾ Öl’¥oß}ǸEó¨hâZz÷ðn×S$ø›6½îÄÁšÍ0xFŽ6oˆ§ËÐ˶‘kÄHØ ý¿s÷ñ·ì„†*7&˜;ˆã&]†¦XåßÂ?Ç$Ü-a³ T¡É‹yG'M†8‹"ÿå6·Žò8NÌÁK‰uÁ.ÇáÜ߯Tk])ê<‚÷Jà¹Þ 2`9‰Vj&C£¹6l¯† !*å™C^‹wÓ…¶bógS²>Õ¿@-Ê´ãqpÑUì ÜΗŒÑ¡ü.Èmͦ÷§ÐOK¬Ø‡¡sà¿{;>:mRvlzW WG~ø6¢“Å2ÌKhØÔf;ðÓl±äföm±+eHWZïaC ñÍul‰£Ò¹RLjëT6'^…¥Dq™álµ¹0M’PCý»:JÝìγ¯=§iQú{r‹9XÓëJìÖ½†w˜Œó…H¯­g:_]Îý×7:5ôrfatß=wwa&µÛsŒ~ößOÄd“ãY˜`ËìåÈ•0V²I™[¨q“¾Úϼ­ÌȽ Þº¹Éœ÷2A2³¡ZqœÿuoàœÞr3Ä Ð:Ì‚Ú;Gs»ï¸‘TÙ­ðòŠ,¹™-×ó'Àûùbh¾´Ž¿«¡Îv¯»;l\¹Ÿ£¿—NدÇVíWƒª÷OQ""†“æ“Í߯â÷ð¦Ëuõ çбàÓñ\H“`^™¡©š;ü>Ÿ-5åyÏžÎÞHÃBÍÊaqÐyÐ…±Kãn=ÉŸ"À>îÄðÓIÜc#A8UñÊìåéÃ8s¦um!ìº+DSÝt±QBž;rñõŒÁý¼*7_›8©Æ± ǯr O–²â?‰ðqgÚ ¡Ñ(`¶‰$¹¥¯1€m*bñWÀ=\€­ZŸŠß…&°é{²ØÅº‡¨|ü"ú…S‘ç  ;¹9þ…’S¹ôbÌQ–uõ*`&™=¦ Ûó•9¥°"p“ß ÷ô°‹2¸Ê–M߇žã2±Ûô &zÑÙ‹ØäÝt¤g<Õ¸8ÞOÏcUYšP©o*Ót·5<‚N<•¾Ùô–Øúm r×€º ìÝ%Ç>zþÅ­òôˆ›:û ‚dO&¶¾ãèúg„‹˜!J¯ÉíÆ¼þ©Ôc“ Y{û»`G’vh,qÿΦmyJ,|&ÂB‹³ØâûW‚¸ *øxUø~í,A™÷úïÇï}90íÇjüχlP¾…¸ õ ðšñw½§™»5ý·yÁLNpµº­¿¬~ÀÄN-Gw ôljlvßp2+ÖÞʼŽyÀ³ò ôÀîXxè$κv,³«Á.@mëó¡5_‹ú\ÆÛ.»¹öƒ1œ°ÑT:²Zº¶þƒ%‚V`êó ݸÀýÓƒq_Š9Ÿ•4¯­óÃA[Û ŸgEÀûÙa~­>ؾO`‡¬4:çl†£Ï…ax£+çà¸+¶G3åõóéÄö%\»ÁN!Ò! qã®­Ò‡Ú¸Û \± ý M™ÇÛ¥p=~6úKƒò|Q]ŒB†#ŠÏàÇêé¸>]˜Þ¸7uôéÅçuv*œ0•䇞LCÙe&¨tû4Zžš ‘jðT_-»ô’SžŸÏ›æ,Iw w`ýøµxÐ%„ÿå’œžÍÈEŸàiè±èé¹LsgÇþ°Æ÷- à Âä®?ÞƒŸÂrKë aÒŒ,Ú¯z L%ô®ˆì)É ý ~åZ¼žv¤J‘¶t‘ým¿Œ½<7¾ØËä&Ñ›/‚pBD kÛ’‡ª—eaƒÙ1p6¥5 ZH|F(d§+áËiÙô]‚Ö[`wÓk²jÆ)šÐHÍvÓ“xnx?ÝâP…öÅp³s83&5ú¹póøò¿Æ„ÂÝSåxèvµú!Ë}oOÝ+èl×è¿tÿjaN˜qÆÎ”§?wL¦KÏ7V;N¢!_ñ‘2j9†‚êp9¹Ó )µ/°YÇ•ñ$ÿ¡”Ué;¿ž’ƒ1 Ä›|=’Ísb¤¯®Rcº[Ïb÷üC `Ó¯ÞxâÜV¼km‹w¾E£ÍÃaÄá¹0åŠîá‹Ê,pxHÓ<ÇQ¥Ç§ÑVûôÕºbrL<ïBà}X{2¦ª¢­­¯‰U%bšíxœÞM²þ,dZ;óáñ<é¼’E²L£™Lšý=ØEv–<Äž¬y0î¯7iËÛ kœò¹Ò–E¼#‹{ðˆ•,Ö†]ÅEÞÑe§6óûû†›­¿zp×fup=÷Ÿß9î­¼<ª•îK¡qì$vz|-Ë).áÂ÷-¦[wtÂkmiÎa›Úṳ̂ãßûÒ¬’it¬ù%¬éqEÇÖ¸æ£3.ó&j‹)8[ã&[ú¹gÉ¿ëÆT{ºÖQŽ–MXDÿ™LcË nAÃ}Þ¾DqvÌ~†~ „ ­ÿ÷/ËJ‹¡•íúÛpÅÊÃX’«ôfá3ð=ª gijù_ÞÎО©÷c€~.þZ>ª]¹±†×ˆF"K+ WÍ~ÃëÌÏÿÝ–M_€gõ7ƒlíO\UšëEÙ9Ѫ'òÆ‹ËQq;Z’ƒ©ÙÀ[PŒM6 ¤¹ÉžFhÝÅ/f‡!¥h5X¯[Eú{/€ñ›L½é›Ï ኶tÈ:³:tÉo-ªÖZм³hÚ¦ ¼‘&)†'â'Y–5/7«v¯y{ hE+šYGAær *ô{;TÿËîyE|©OŸ1äÙUë À|þR«?hדŒÖ–L{½&•}†¦­ùlDü~©™Ÿ0íÞ¦¶º‡g܄êr£çx<§üc:Yf¢¨»9ø•ÏM¹¥K•‡®â‡ªC1ηy¦£S$CGkâ1xKV€Ô„dzÎDνg‡’é<ñ# ´‚îraŸ–‘Blí<ˆ~– ä¤_ 'NzÃ%×´….^¸Œ)Y8ÁÜ£šðÁö ;ÅÊ%lYô¹µpñEä¢d˜^ÀÚuÇ\ÆíIü’îÅ+l*—áw½ÙCó¼ÿBVÐFÌ=éd[PY1>&DýD®:³ŽÑ ëèÌåëè‹m“˜}— ³ÿ²’+yªŒƒ—Ô¨[!ùøT‡ftÜGÅœ8ØVºæ^›ŒwæÂÙϼñ[´hó>†§žཱི?Ðâ`??çæ| ‚Žyð7P·vg2¹á‡xdã·Î C‰k¯yÅ¡åDÀ|’¹aì´Ü|ÐsºH~HÅÿ2–MÕá»n,ÊÜÀ•iÖìtð3˜1V„]êÖŸ—ð2À”*úVsõ*ÿ౺! ž¨„ƒÎÑä©×k’ã•‚ÓÕ’ øFq{pÍo‡µ`ò.ìüðv=F¸øÀ#§ LР}[Rðì‚ñ´uµîw7¥ã·ŒŽ“òÊL´hxBj8$‚ì£Ñö2xÜ•ùS KÇ9Q3[ [ûké°>\v8ú`Ü›z˜žs_Õ'p á3ù²òž Õ)Iå53a‹¨³òc1÷[ˆÊ§ñÄp¹ƒÛ¦Àƒô–Ñár:^bðÏ^‚?‹ÇäsùvÖì@Ǽù…»ïwÀÑÐn²£Ã]T±Ù²§IÂÐxp² bÊ¿±#^‘È\3‡3ëmáçÓ…èÛ›ÎKÛe‹#ßêPx›/üS>Œ¿ìÐ"H”IÚÿ±¡Û$±ÔŽv^÷ÁúÈýñD¾, ·Öœä¢^Á› ¯ñâE6¾¢€x˰ãÇ P % ÚLäa]èV8%û6\Å)ÏRpã‚رüô=,‚_ O¸y«nð¶®ÃcèßÑŠMd97Læv¿ˆ%kañ„ãXÊ\„YûÒ¢y¯˱âÇVx¡“ˆ»+cHHš¦ß˜ Y¤*A„¡~ ÷ ó&Ž–ç²nÃMw:P¸ cßñ—VD‘Œgú´ú{H…¡Rã$¦y n®5b¾›ûpqy~· â’÷ —çA¢~¢“™>\‘ /ŽÉïûø‡gmƒ¹ùdé&1ìu.æ=9hFrËĨÍ-ÖK˜ú×uäù %°o!xÿô*Ô½ñ®UðáTê¸c»•ê/ã„>•Á+/ÄÂ&åPtWÚA‹ÆÃÏ5ÎD o?ªüûËyÌÂ-9îÜ”{V,7Ä ™¦á§»‘ô–1°-o—ÃõƒZ´)ç51Ï_Nõ”A2jsük K¼iÜcKÖºyÑçI÷ï, ^Da}o.hTCœpYu—{ ³ìÖ(\”׳«ªñV‘+{x{ˆ„­™ ©Yá’켪ÁÔiÓW5¨øXü3}5{ôMˆÛÔ¢Ëô-‹•Â)ïN-­}Á_´B“µ÷’Ó-U¸Ö"›Êû$qñ{Üñ£¥ñ¯àÀkü\—–DÞæ¬ßòø÷…èô¬¹L@L {:6’¯ÆÁ4q²;Pp‚ ßôgÓ"~Ù>ÔRbúo—°|Frvs }½†’éÁsàʘad/«ø[fÉÓÍã3©üö䯳j´Ùdº¸Ý#+yb·½PèLŒ Ôá|}´6V6Þù@Zæ^ÁZAsÖ’z\’´¹v²0G$uk¡Ê7Ö£ÒãU4Hoõ÷kçÔŸ;ƒÜ-¸+´œMÛöjRPùº.o£ƒ3ð³×#o¹“nÒtf;ôYÇØßDòÌjÊûWÇ?åz™÷{ÖŠ£ÌòÃdÌ);–¶Èš„Ïî@ÑIsé­?4}ÿ¾·fÉ_©·÷<æ—<‹î}—C+矑iCü )z*‡Ï+ \³ã 6+©b¥é-|±‡jñð_®S7ÑR¬ò!D:û }q†Lä ÜbÏœt&‡³ÿ`ç81–’.džL’ÉáhCÜœ¦É x¸ˆwÛæPAUqîˆN…3™ôÆ5¶Gó<\r˜ÀnNŒÆ%½ à|V"©Ï›ÁÚäKH©ÉZdµÂÔö´†ƒDM&> á¬[—AÚÍ:ûôx6gí74ph#m)öP]š™6 HF¶Ú®š¼-jsƒßsAèˆÛ{n,´:Þ"wüœ±üã9˜÷ù$–vÅÀÖ¯c°&@žÜ›Íþó7´,…U³ÚùÃÒ#Ü@º!WèúV´ð^Ä* ·D¶îˆã2§ˆ±_^3pj¯ûÃ&¯¸.[›ø¢øÏf€;ñnÔ=y„s÷.…”^˜?¬›Ÿ’Ñïøñ¬ÖØ¥ÀŠ/ œÏ1M ͳ8˜A÷Ÿ·‚GHóG j©gFEüÁ9aå°±F¹|^L¯”£§7¥sn=)ds¹$'›[ÒØHgO]†…»ê9Ÿ]–˜^4‹úÆ1ž'hº¬ûeÖ òJœãœ¬»¶¦l¢¹yK`žÓd˜¼Â“1V“mQdйD¿WJ¶àÊù²´áž2Ë(S£n;hŒ9»¿«²î‘%ð-¸¯GÀ“¹T¤z<“ðŸóoƒ}› ~®“cåißm ׈¡™…+]œ×b¯ãó¡‡PóÞÎxÚ^céÓ½^üæÓšlÏ5îÛ,jÒu“{þŒD–ýâåú„Ô¦éü5WEÐa^lµŽä:çÔàô³žìPÙ®hÅ7|¾Ò‡Õ¬H†{ë—³Ñ~¹eâ’”8…¶ÞßÂ(º !Ü ÕV"¬c}1ž=>•=Xõ Ëd²0|«ÛåM•gËÙŽ³R§N!:ðKw9 ÒíšLiQ€9-øÝ¶;¡nJ'þç›>ìÝGŽNNÇf?]nª]Ùi ŠW†Éõ «IRI,ð¶ôCKG 'i¶œNˆªçE¶iÊx-´«(àä§¾ÂEo¾¢lQ;& |†í¿­è³7.Pí/€i;Þald\Påûä V Å’âÖ{dõ&!VU¿·¼Paî “è=ð÷Ú ˆ Zˆr&O`çÃL"4ÇûÉ˵ñ¡‘œÚzgйžŽÃBº #.oÅ´ÉN•pX(×sµ‘³éJ¿ÿÿ<Oî ¨:Æ?æÎí«‚GÁ,Ù¿»Äí¸G’ô|õ=Tª§áǧrxûA†ž ׿²o¡ÿy]•’š5púÜLO`í-$ÈD`ÞYNÅ#–忣½ ýØéEçé—ÞÇp\Hˆ É]ƒž‰ÀÕÉ.£™ÖÇøû»”Y[š¬àcí9`œ¨EZòøP›<‡-°š›ïÒÛ»r!îÀzÑ$‡5Æ@Èëdû‹Ë&+OÕÃ’™¥]$ÊW¸e¿8÷éÿãN_ÒR`ÿ¿F‹~ [÷Xã³oðZ1NË7£g 'd¨*á™ø1ÌsD£ ÍYY;Ì Q¡z9÷Ÿ'9ÜU€Êh@øùë|±ùŽ’â«âìö–yDu¢½ú9:%¾S™¼é4<ºï QR MýÜúòÝxÁQ ;n\ƒ¨Ù«ˆU;(©ÇàçŒdºùú%`9»qcº7ú¤dã"ÑêÛçcÈõAn¬gNèÊ'}"‹ cŸ ¬†•“ÔYÓñ °i¯ Çf›â+n.¦Ù«# Mü.îokãžæË_ªGÛàÙº ’Š—IàY_Eܰ'•m¸ Ó¨ÿæT1Š£îý[1¼& Ìd;ñúщôå~)ú㊠JÚG´úqÓ¸å ê¾ìíá¦j<å"d6°íRÇAÛ‚ÿÕ;=é<„žUc kíOá½1;Ðà¤+»ÿ1†¥‹Ã½ÙÔ¤Ïr%uiU«+Þ,œiáøÑ8™tÏ‹gegу‚ô‘Cn9×ߨðt'ÍÝ|·úÙS-Ã= (c'ïp¦¯Ì™Ã¾&Ø/wÛp52té¦ÃtgF bì5Q0‡˜C 8~ýd˜e>ª7GQ^_˜Ò]¥k$á]V<–çâDî"M䓼;«Û{ˆÖuÚàºÙRÌ·çŒ-±÷å’4úÖl¾FQ wko˜ý„¦A9vÔÊÆM7…[3ñÉ—~ÔœJ8M¶‘¼~ÕD®Žò¾_wá¤[¸ôPŸ|ˆ(Å–ª&¼•ýM»†ó}ßX’[ìõ_¦4Ìùø‡FÏ ±+ßàM¡Üy>€s|œ‚JŒ`ûÇ Ì?ó6l¼¡Ìªë 1® Õ#'óþûßì¦=æÿφÎ0—g=Íî`¾y-½õ5œ4TÑǸ-¸‚gqò#iþ~M¹ÄßV‰#e4?%þ®%㪢+¼!šw7ÓÛ]è@ð6{€n禛`ÌÒû&ÒODXîæNX}Ý¥]s`Ã7v²Ë…^ҤׄÒIO:!*¹1Þ¦`?ÑžýÅ0š]ƒ3™¬Î8b´Z/ú5u°óöåÞµ*€þòêÖ±Ž&´¿€/g¾Q‘Œ“ìÀ¦wüßoü¨ÂöH6Á=—íW»Žç?‚Ü £J•~±¦ò%g™ˆB3»6(M×tîåL̦çß#æWÕq{_•s鼚n@í¾ÏÚ¿_#)»§±Ý¦Ò¾3c®X´ cÅßöær>Tmñá¦|˺†Œ™òŽýTÇM…é„QÏ/²4x}Þ(mæ¾Ç.õ3ÎÐùÝtå%i&SQ©fRÔFùþç¹vTr¦2w/Ü“4ìØÌ)öýlhGgΣtŠ? 1¼·ëèu:=b˜ TŸ°Uüy5ÕãàôâxYëQ"½D]Ít«ÆÐEç˜Oß(RÈFßçE$.W…nè‘)ëQñùo, 1â½ßƒXÇ h»»šºØf@§+aoóïãjq â.L}GÖüéçÞ¸Xâ±3!lW¶,8OI”ä8Tí9Ms?ÁK†ÄoªË. aOï­‡åwDÀiI,&ß¾ 2ç6ÁÓN{ÖsÌjqZxGÚE"ºáy³6æ¿zŽã\ŽÁëcY^öUb!iÇb¿Îdc¦É×Còxé6ÇþUàÂKaPåpK÷i°+%Óhú9¸:x[Òã¡m¿;6 <«îygüñ²œ'ÓÀ'…Ü£¹‘àzúò®gsëD镽è<3æìL§2z|¡"qv˜î9ô¥ú ¦6ˆÜj»yðdótöÔÊ„¶¿«…ö[蹑u8°ˆŸ@u|,äÿê$)uäßJ+pã¹·ü©âR´X]†/t`·uè¶ó]øxÂ)Øì¿o?zaÛºK„®¼öB’£¨ÊÓǸYÑž{à!\˜@ ,= 4nà&ôf  á›Á¼ï4Á‚+Ú™n»5D×ÖxÚjÈýÞ»ô÷–¦6sjžÚì±ô%.ßÍ}–•ã<¼²9‘Õ‰P™sš]zêGjgúã'V¢%ØœG—ñœþ#(ÿ´®yÏ¥÷éÜÚš‹+—÷=Ø‚šyÂ•ÙÆ”Žm s·S?ó½KåiöËdnØT6­Šfú’ÞÜy œ¬Á=“9føü2§•¤WvgÑYs´˜ÿOz µÜ¥§¯mÚOagâoØÊÍßkcçs’¥Ø/L=~ÞAgNr_!߯qFK¥¨Ï‚V°/ÝÄÞïv¢B…ú´å[%Æþäúøý¼£Úï0`Ê}úlˆ4ÅKžbã-êSú%Æcá Üof­j!;M òŒ1kúdŠûìñÌ?Ï}8Z7è»ByLTR%¥1¨.7M›÷£®bÓSY,AIGrýMçË÷®¤Æ)‘ªI’üC°h— º^ge\C $ÊkcqçØf8--AÛç³Y8·±{691›.»#Ï¿Ï篢 Ž,à£À>ê?]‰™ì>L=~Á3VlyZ?ŒÓ_Â$ž+âR½9li@<8ìËÊùK`Œw=y·é!²Š#dÍ©>´‰)IfÛþrÑÆkã(1Þ3 ¥©Á kf“ÈnÞD×S÷±x“,1z¥ …eB¸YÌ”rcÎC|¸ »§´ “ sµƒ¹°Wc*ôänfA:²T\ÎÊO¬Â±O¦‘×v˘ÚH7Íø”ª²ã©º0~znZý’QÄæ"ýq[ö˃ùudsnBÆŒvM¡W^~‚”Ö8¶õ¾«:Dä0{ò$æòü&¯5 —Ž=ãj¬G^i7¶ò·BŸÔQxP,Æä¾hQe£ó‚Ÿ,Ì:‡NàÐ{o”;·Žxld¼‹?zaB¬3|ô‹B«Àjvdw#—y}|úMžØss6ÆÐ{ÝëpÝK[êÚ«YP~̉éï^ÍÍZ ‡GgRmsUz0ËK}‡à—‰&ÊQ,èÏnLŽuTõQ¹8lkРïv÷ÁÚ}„=~€Ã‹$ض)<ÚÀDi‘k*­üƒ·žŠ²f©Ð6Hcz£û?)—èÜXÍ&ž=E–©ªÐ $KtÓí9L'‹ï¥Éo½øÜrþçÙ‚´Ç÷ J:T¢Þ­«(õk ›ÂŠ”ßÂGWÑ ÇgR‹%,ô›7褀ñ'“X{¨ õ 7@ÝKÛá¹g1fmM¦^ª³*ö Ô8T÷V‰kc0ê,‡÷Sãà܇x\ù© õÜ/¢AX›ÿA)È„çÐè£tËö_$'\‰Áä lܱjTĸ›wÂh a ¨Nc®S–3þAê”~’_(³æO£:õ›<[dÎbåÒ‚gI½Ýmu¸ ž:½8%ÀM]µ—†8L®d˜Óë>˜xk>÷x· dž·¤ÍãÑÍö1I/ªÆøŒ+Xggäš2¸¤ùÁØ`Fáí÷ê´-¾ 6aÛrïAz’ ÈLb’]Ï`¡T†ÝË&{¼àHÔMòâ‰]ý¬žëu‰ƒ…ÊYÇ.“I|<«ËG57A¶)k5uVS ·÷~A¤sì¥Øeߘi2 j­ 'Îâz‘_hµ7~ŸŽ6Zð.»ÞÍŽo>@¾U5÷(ä ÏŽXPöv3F*¿ÃÎ+f0|¦•¼#ì‹Å–U;0rõOq !yó'zÏy¼æ®‡)±D¥Ë¸øm¿||†¯ÉâWF\Ak_6cq¯í³·ÒÔï›McOÛz^A‡•’£³~ªÀ-³rÔÜ>&Ïâqê~†X5ÅÚØÕ0»†©ôÛûQÞßÖÚû%X²o )K¤'ưKáÑòiÆÌ)aÉÁ‹ðÜQʾæ3 –ÖáG3gœ„‡¾khr¯1Ó;®ÍiŸû¢Æ°1©[H…‰;«‹Có«>Ös£çDbGÙøH>bX÷]6)Œh±°+á‘ö}N Þ¯&ÀH÷%|¿?ßÝ\Lõ—²g½¦$Dƒ¦t“òÀKØê¸ ż€ÅN-\Mb¿ìÆ÷ºaxÙ¦ ¿}·ãgz³=:a÷³ðE«’ Ÿ-îŽr[=¬ú&ÇþfÏG7ÀîŸ`Ä÷jþÊ'9îiœýãäzÐCî»õYŽþ>ˇڙ°Aó ßõßwt½ Ê" ýñõ.#–nØuáEÔùïôyùƒ;ñLÞ²¥Wz,`O+z6†nîw¸¬¹ñ8¶DÐUVΔ+‹Ä¤”_>SŸé!WÔJéËaœc«ÌšfM¦;õiâöQ %¯…’%WA»ýLk½ƒëz£ðÈ¥iø±r5†/£ Ù°g!»›ý^Ú,fb‹ëˆÙ’ëø*ÛÕË_âô3ÎLÓ:k£ko½Àvni ©wIÝÙÔ–üÎMÎ:%Ÿ}`™Örª©†ÂÍRLqÕ\zF-,ª8k#e6Ùô2-ÈZ3|Òa¯½»§–Å ²ô×`$¶Ñå1Ò´tÖy^â,i60é÷Ô¸Ë\ŸO.Ü<’=Ôå¶ãýfßrÛPñÄEÒá'<›õ”{v+‚HO bJ…ƒðß'ÛÍ£O7}…š¥wqª|;¨SG¼ìÎûÏ']2µ¶&ÑùµÕôôdUzÒ:€](¿2ʈiµ°5’ñ¤XF¬÷µã‡wÆlš™îÚ¸†¦ª?ÇêC‘ï6‰Í§ræ;è‚fi¶&o1šù²'˜åQ¶L6ï®îƒ;WÆÛ©NŒA7ùílxœ$3ÕÍ«WH2±W×à ¡”ÍâÒó½Fõ]Ùºk6å­<г¤ìØ—~Cfñ¡’¶ÔL§¶áð¨fµ·û–^G ¿Ó‚®Û/Ì‚Öêþ—! [#_BQÚ3^Wüabúû¹®<—ÆÊÀå[ð’§Š[·úÀ±kõ°$Y™y,£8ƒW)°Ëÿ"xu;ñEI& +XÛÕV²)­o±t½%ý%¢Ïj–NÀ•ŽÍ¬¹É—J(8@|ƒ'^ò,AûíàtÐ4 ižC…§ÏgÖÑ DSbj“ƒþ¢¢Tî¶6“+‹݇¿µîÃxïÎO?Ob·<Áýa7°¬ lgùs#õZÐæ&ÍÒâ J/r`Šþ&tŒ;¸¥/ÖÕaVè kV}çË‹™ÀÈ_¨O½Žs·W¬&[0ˆiå;à'_fh%bß¿©ØQµ‘é<)Á®N šö7+_”a©Õh;ð]§ÛÓK;±‘úRTlÀÔ•…)>y ëF5š½»#}ç´í¾³óøïTá7‘6‰ÕìëÝÅìÙú#TváD”ôÖ„©¾<²Y³…5”ÆlRé~á Ýûáü„zãz4KšÏ­õ4eþZ:£sSVìd^Ú´eÌy¦°ít#g~g^,–g‡– €ÎGWz:/"·/ ‰X­9<¦¿82S!ÿŽ´}¡’ßÂiªÀfö!FŸmñøˆ§ÅÀþ£­ø¾Tœ–žÅ!s{vÄØò«2á¿ éôÛ8ÁaGó3§ß`±F@ñûZjiôm採Ë|÷Àȇ­lë«Df²é{/q¨KcOÓèzÓg¤åž#Ù´H•®}µÞvó–|ðéH&!›éÔŸ±´ziLå>ãþ˜|v[ã¬ç>¿Þ|6ðp3¼ëÝßq›®„¸³j´ÏGš^ÛêL2½Òà¸9ÓÉ̶àßë1Ìu@”êè)³yÏó±µdØ }§nËšÙÊÏôìË(vióFvgvè¼h@¡ŽLöüz(ûž*ŽPE“hÕ Ç‡/Ñì]¡:ì(ý>~ ü±ŸÇ,n¯Çƒ+ 8Wß[2g1Üù]Î[ï²—®0‹&ãP˺›î_9ëJØG æ6û ñ¿¯}гð_H ³wOeW»îÀ)ÑÚÐìG衇d¾Ÿjº®Ù}q¤sú˜Õ(‰†A¹]šö©Že¾Ó9¡W‡È$UaÔõÞŸnIA~üR÷[‹“Ö{ãòɽØZ{‰|üçÄ’ÕDhœßDZ!fÁò=ÓPåF4”EXqªÄ„vdÀÛµ°²*6LÜÀP´ç6¨Ñ®HÚÞjω=„—>¶¤¯òOZíV"ôÚ„ 2ýj‰meÕ;Q§g >úbɬø2äÄcü1Á‚i¸Æ`PÇL*|¶”þM^ˆoUË€EŒ­›/¾ÞæÏ´¦“ƒ£áÉ”Þ8‚Wƒ€€@Øš‰†Ø¹¦‹«8NŸ~_ Q,‰Ö¹obŸûm E¬žL#AhçZxJˆY7ºÐv–hë¨À’Ó1óF rVNìÛŽû k8›Ø^V¢ÔsY{ç ä5\€kÚ°~Ü6öLZ5ä°‘½ž\µ»ËÜJ%Oª`P’)ˆ¿êc`£`->—+÷6cÚÕF«guëØòS—@[{;KÞ™ »«á]ñxê¢û’öû¢Sü\ºÿ©[XȤSƒá˜ÂÝÆlã< ŸÃ\ÛTØÙ7øn‡<›©hA‰ç$~Ýgm.ð“ crOÀ6áÌ·YoÇñEeÅ ú´31½´œúh%s­ûËÐe§*ÍÐTÇ…½ôî-úw8ÖÇm€?Jñ•ÒXF,ëá¯ÛGrôé$XTWͳ¸ËÄèŸOi€3·â~?)&§ˆ)ïg3½q¥œÉ¼à½àêþØÓšÁ‰–¬³€Ë°È@Š-Ê{ «ëpì§-x Ðü·…ð+[婚ú«ËûŽß…ؾ3P_eM=ÖšàÌ“µXÚtü» U¨Î‘vÌ+Á3êðˆ[ºx«±ëf LÍnĶëv,ÈI”&Ž—c¦n¿‘w]]«qÌû\üë|nrñ0ûI¯§õù5dM]7Ó«÷´ÐU¹‚-k«#ç·ð¹»/±þé:¼ñr½˜\~—“ê5S!⯜ghûDÒˆfôèà†ªqxrvª|zß’&Q‘²Ÿ¸·:‘vç¥y çkWÖ)>¡[w;¢Ò+OV±E²h 绢€KJ‰"³ Xè¿ñìê" ´ƒÀ™¼~$ÈúsãÑj¹"µ¯ÃÚß–qÎo!åÙ îó¿Éô½'Wü{)}eb «Õ¢áŸOn^õ!nǶüç‘f¹Ï#¹'Û&(†²kJ‡©õ(§rï–£ eâPiÅ67qGêÇS^¿%ù÷ý'´®í&F+äùwl÷3mó@ég¼ÊN®ZIsÊG¹zV7è&ÝeÉ˸5ܶ®] ¶>Ö@vMàEÅÁJÛq‹—Oñ âACBnÐå±}´DIŽÞ½Ã&û0»µûðHË<:çQ‰md¡=^ˉeOK4Èò‹‘ôƒ—ÜPuBÏïWÁc] š™O`;Wa _+8¤qoE¤àÃù“ÜüÑ Êl<ßþG§Áýнó)8šyAÜ~Yx¬ý€8•ôãÓi¯àÙ35Vuî ¿ð0OÜæî|Š‹ü›É’7\˜þ-$3ZðfnØc?ªí]µ° ( |MQ=ºÄXŠq+ÕP¯±Žè–‚Á]¾Æý{piÓFÎè†Õáɼ>qðQ@“ípˆdÍÝ*(é¶’‹:/Må&ãŽÌ)LÓ€£"W~c&VC†Q ü2³æ?1c°Ö6“I© 6~º2 #Gÿâ5þVp| hL‚Òº Hš&³º'#ü7[ã±§JŽ…ˆÊ°å’¾\­”?º?Åíy‡Á//® ¾ÁÅÒ’s_`8MŸF,§ 2=ñE&öf“žúÓuGrȆ3Œß7‡N»o‡iÛÐâs"V^ñc Ëìˆ÷—… ¸O‘ž©ØÁ~᡹ÜQÚ­C^4汌Q]6O)Í}q†}„1(!G¸jÜa-qj:Øl«øËœÍI3ƒØ[kP)´®trêO„©E£SZ3µßœ/Ž>+:_ò6Ö±ŒŸp$J¯±Éý œK}ÎÙt*1ݹÔú¸)”kØÓñûœà|}ïóÖxúÊç"¼™s ßZ…Ï”â“Oü?ŧáÓË=8Q0âÞ R«v\r »(²Ü>6žÀ¸ éÐðr ž­~ª]ŽIѨ¢‡ B¯a}.ÅÁ°BnÉ4-~†•Sþ1×Ô>ºM1D¬×ƒ)%¦‚CS1æ8l&^Ã1¨hr€¨ùe-í‚ì¤(ÀšÍjôß›Ç\ìs{Ú±›Â9º]|ŽÍ°`v+ëø{4™ô’CØ¿.û{-0ÁL˜}•]*»×jsÛcWÒs<Ѽ"ÿžä›ñÐcò.ÐÆêæ”’0º Ý„;¥«¨EŠu:åÚËŸ`ËiGÈëŒÍ50³¿†<˜ñ£³x¡®ç‹(Ðt¥0–zØýb<ØÍ«á,ë®1sÝ}˜Ýd0CÓeþª@ûõ<ôö”‡…µ·AnìFj¸ð6oÓËmÔ­Ð…]xXHzWˆÃ!Çóøf«0+wÝfSÖ0 QvûÜt™-Äö}“‡ía›è‰ÔéÆ0lúc½ `Öµ‹á³i%|?éβÒ+¹Wq_H+Ñ Û•Ùþ’QͱXôŠ[¦ ;HJPUéÜ3:…oV*y ½-ñ‘Ï6R{üïóʨf1Å•‡Áàò[È~V‚ly¿•Sp·æÛûI×pßú&R·k ˜Ùœ&1N’ôž¨M{uU‰ÕéR€FûnÒP«B ­¾‘3ëî$‘ (XéŠwk¹Ã'¦CŽ×8xù*çü¤Ó’þŸã|Bw·SØ®º½;pÁºnîÒ­nÔ<¡@ë>cüÁ9ôc~LËç ñ Tû ÅJ ù¬Þì_…®ý„@uš> Ϋ7À}:ÿšMT¹ keÖì×VT[=dŸBQ·†kxÒFg6íM?ÑË(f÷)áúPØ–£ÊŠf›°“Éèô0š[$ü }÷± ¥x%¹âzÖÓE‰“˜j´3­¬9‚¿gžO !|4MŸÌSR O¢W|+¦n+Fªñ›$¥@¦¶.=uu ôÅË€³ý#T‰‹OÅQ(ÚtOÀ‘úZŒßÅpìµ*ØþæJëÖBü?8ú¾>¢ŸŒÌÙ™õ¶\èúNnäéü4Ya¼â©Â¤7N±}8“áܼº/ºÍ®‡Ü¥X°-\ ÌÑnE=Ú¯„Gˬ êêj žÚò#Æã¼Mþ»Žè>%Ïl›À^j.·Ô2 ;.o<©? Z"w±~w6Zª@°JÚìømÛ§ÑJSfØà­{áï>G<²z)ø Œ¶ÝÄ ³ÉŒ?ï(ºŽ“‚I‚Ð~¹û«9-WWrY)Ÿë+×âÌ*×2Û£ßðEaýQ“¯¡©œîò—‚ÿ¼Ï[,.ñ¸bÎù^vlþŽ!Ï…hæYU|椎AßÏ¢wë[ü+½ˆ©¼•g÷†½ÐÍKˆJ|CPõ¡U»žð÷d °®ff¸¬ÿ‹¥òø VƒÁÕz˜Ô:—›¨>DÎåø™ ýxdW9|ìã²Ë¸®mw@ÓK‚ijžÄ£®ÜË?Y$©k29¾ø-fçû1LÖm}ŸL µßŠÉ P…x-¾N²; )TÂy‘P´5/ÞRJáIdAB'·cÞq8—üÖ^xÑU˩އ=¶ Ü+ÇîÞZÀÕW£§\LSÕaâÑõ¸¶UJf$3Þþ5¯ Û~MÆ«ÑÖyÆ®à“ù>™ÜœâTHê*'‡ÂÛ–¡Ïr q8 W~~5óEðŠ>SØ0“•i€±ËÇÁ&£.ØõŒVŒš”k8£ÿ.}[‹È.àã!¦-mÁLxâl©&.ø9úüF;Y€ü!*ZGsÏ£_—5ìÄ5ƒú´ 1‡;¿ôû$¶Päù/9¾á ,Î<2âø÷R54‡Î¢k?ÀÂ%uÜû$Ll¸~UÁg6ý®> ú×Ê®bäèÏFºA3¥J²¿_R1JÖŒ —¥™µRÔA+IOeL3õª¾ò Of£Ï;pôv¥ð½X Þ®q¥|Íh›fîR™ÂÂNïgQe±œ¼£ëžrƒ›¯z„ÊÉÇÿ2¯ÏlΆ¯å:è´˜’ø]^ÚþÕ4\V“©ÚrÓ[àÑ#U:a…ßm²?®Íz7G1¿FXšªÿY Ó`Eâ5ÎßÄÓ$Ú¡Ë5gh¯ÃÊ0€âû/qβ68¨3—Kº…;}êx!+³áî•ã<w&GÆ¢hzy[61[š†_ßš!ÍX]Üýµ?Ë4p¥È¡‚•nWzÃ_Ùº¼D`ü˜dþçXnÏíòÃØñTçº"> ms\Hç? ÅúÐc xí4:—Îãuç±Ö~^Ö䬸Ñ'¦’7=Œö¥U dÍÐ!ð\p“34éÛü¯ÄÓ¡–|¸†Ÿäöð×má†$NÓ(°[ú2ð~­–œ³Ä‰ò2\›– –_…ûCÆÒ)÷”høÞßÌAæb=¸pý6yf8ŠÅ= 1à1ö— ÒŸê4×$šÙýÎÅ÷Zñœtx2|IÂI gðO,‹ƒ˜½I´S‘-¼ãÓ+ÈüËÅœã¿>r»×/<ªã¸yÚ°í“( ɱ¦~79‹j´¢Û‹»q‡©¥ªÃ÷øU -’H¶¹ñqk¶ M=Ï-tJ„-WòIšÑ.–)^ê‘HëúBL&Íf±–[‰Ð½ï ßðÞ^°ßº«Ï6¾Û'K³uµ¹gÿNCœ¢Ø¦…Ôä±8N,(ÿ/Ëç6ÿàŽk ©P93ºãùŠoÙñü©pá¾9ýöé•íî™Ñܽ¨ ðiûjô² ÀqçˆÍ½ 8Ú6ïtO .}-GãŠñlÆþ¶zQ¢Ñ¯&¿5©r±ýl‡Þù£Èk/ܵysF±¥ïçO^ÿÌ·z!™\kÌ:ÄŒ¸ø)ã0öÄ<\1ª÷ÁþÙ<õ¾‰äÖñfàð—|~Û|0¼÷¶¯œ@‡¾VàÂy ¤6–ë/„ˆ)¶804—[yJˆÎxÿA~.×Qýc %xåï><¸ÜÂDÍp]Ð3lúÝ Å’ÛéÀ*‰Ë'W¦²mKT9ïíâ´ðÇ­úŒõ"ÝùVõ‘WáÍä °näP?ª˜rňÒTl ›'}ŒÿXg‡@eX€lŒB<¯EÓöN‡q³çP¯ø×$ÝÕò\wXNïo—¶Ò¢©AšJ¢ñžûIhP(D‘†MID{O)mÚÒ¢!£Þs?ˆP¤²J%”‘QöJ¿¾¿?Þë:×{=ã>ïyîûþ|Îõy?:õ3ú“;fñ ï2 qlËßg Ö7{›ÒÅÑJ—rôž\ÂÒz>Ö¥&N×&aÈX.bUn“Mw}Âþ‰ŽÊ]xÿ³×õ ›®ô»öç}vݨÉʉa·9ˆ±Õô2óÆí.š(ƒ5¡2ô?ßæãædÜëØwâ»ñC¤Tx*-~?ÎÝò›SØ 'ç¢Fá.T¬ß¿¤`¨Fæoµ§k¾†“¶ëÕ/u`ø‡•;šÔL÷hhÒ~‚¼î?™•Wé+«&^ä îtáÇÆ8cl)ÞN5Æ.Â2Å\ 3 ƒ˜m¨X¸ŠTˆètP9XËæ;:C•Y&ÏJÄŒå7À4ÂÖ'—³–¿Þ¨y?þK+äìu£ñ7ʨýîCdó4¦Â~+œQ+€g塜moÙÕ–П5 XÕ.NðÂ\>¯G6±×SÌpÖõt\Z4¶z(‘˜ïil‡ÝyÆ?ò6÷Èmo*7Â쫪gtÒ-iò±X¶!n-{²˜—Œê‡b¿[Ó=ÏDœÓ°jã*ê©s]–±¹ƒ¨w–¹¤“Ï =øÍv°q]¢dw”j³Ï2qMV†2¹Ð2-í±âx^ðdfÿÊfy `AËfšëZ¢×íÏ©A¨ÎÛFðÝÌŸ•ŽþÁªtëcö•1Y‘˜Î ÿ û¤;ØÜM ðòA^x.˜n0å~£*b‰ÏÍdè ÏÓèøù=ûrb5ˆÇ-b:ö¾âpOìƒC¯—àºÖ8»•PµYÊJ¥;X%-c¼ä}$fNò…ü8àÒĽTšÀäŸz ç“瓪sÏXÿØ·X\f¼§øèt·nä ÑFYùlëß_Ç¥õ±øeýB:¾ñ5£d'OìÏ5ãê'ˆ—Ÿ25¾¯%TIôÆGhív ÷¤A1ù’"CÄÃyƒ'ØÞp–)?Ê,›@j¦°ùíMl±ÏE¼“;‡Üy/GÅ[Ï©õ¥Ïò,'k—™°!`¸˜!AßgÓe—w@¾}¼*Ï'Sªaì×nør B"³¨ÚÌêXÇ®RðÁÚ¶é̳w \–ÍšŒ…sV9™Qèí±žåûCl²™ÏBPV<3Í40_½ÓC–@¸±6óøÇ ïUHã¡ùLØÈNx0ý œGÜ}c£#8“üé.t=†MG©„ÁÛþ{:óïå!êúè{²Eùý§BFáŠÆÓr›évEy2ÝÏ„ÄbÏ ‰S‰ÌOÙmA :™sWO’д8¹§\4g-L#ÓRNCÀ9Z¯g io¤hâî›ì[òW\؇æ&… Gâp©ÔU²TÊφ3Ö’‡†Uæ¤aïshËR„ªqAª¹þ)èî^Á íá„ù\åŠ[çãüê§ ³w+¸/i…#§ÈÂUsè6oö~M+dHý?]"Ry&ôæ"5²gm3=ù-ˆx~hÃ-³)ceAÛùȾÍzÀb"j5Ì¢eºkQûÛ'fû«$"˜ž‡ýnv3fñÏ·T®WšJH’ž *Äeq)è]F¥K’‰¬À-VøÙŒ 6¡…™½l÷ì0fñ×§˜ O·ü{ …m‘Ð0;™ºŸÝMÆÏAáfe¼ÚOpSq_J¯Mâ°˜ y:Ò¬ ñ¸BIñw~6ã¥'#z`j/¢Ã[æÂñ³x-g6~¯ižÁ|( keTr8|b¬ˆÔ(þxî‹·$ä‰ûCdNßS¯X'©9Wõ Íh:†âÀƒØý> ~mÉ@ÛM°©"5sI‚GŠ6¤ÿâ;lxNÐ,6Í»ðm×N#+Ú]àÂø]ðẏ ZðÊl²yõ~ØVU"}JøÕûv¬,a+Ô!Sj·žTžûÚç—”ü×xù‚ k¶`˜ý}q1šÚ\AQÏݰóë2xT¯MC1"5ƒYœmNòæÉÑäÚŸœ™S¦’Ñ.˜ôжÊÊ‘+×ÖÒûúŸáqf$sáæ†Óø}5M—ćòfmÏ÷þÿðƒK lû¾›¼’#Ñž<Ô¸* ßÎÓÃkÏãAQ¶wøªX&Ò€çõh³ÝŽúóK£ÃiwæÀ Kt®1Bç7×x´äþó§fÇ^½ƒ©Û‰£Ò,ÉN¤ÓZ‹ñ}É}TZáI>°…+Œ5¾Kj€k;¡bÞJò p*†¿¼È¬ZN~[­dþÓsKëÁÕ¹ÐÙøú%ïÁµÒ‡¬Y›ŠŸ¥íê!$`¢B_FoÓ>sÛLt:Ygá=,›SA¯F…Ï`ÅÞZ ôÉsLJ¡­IŤqöºÏG›Îÿ·Ÿäd‘ï·ãŠñPüT G~$ 3ÚËÿpŸ™³Ê×Ò«®±L›ÍKÌ ¿Z{™«Ìß‘J0ðÖƒøl'$¹» cP’ŸÇt¸/Êúp\µüÀÃ\¿‰çeNÀ}Sɽ9àôc.Ý%¤Í±9‚ bóaçÔLØê´ï*Øq ßi‰ï7aÕÊ™ÄGí.n0 #¾ù(íe†éúˆàZaÂo7 —Ɇ’æïdÖÓ)´»í4N]YªªCLÃôä`x;è™­¡.D€‹Œô¼^GŒ,¤SDö`¤Ò<Ø“Iª:ÕIË·Rv¨r ™oJ6±¾7`²VœDÏ[ŸwUêØþAƒH1zzæeìX–ÂLÎÁ½»%©EW<5ëZF߉ûÓœStÝ*¢òÃV½ÝBרɓ…øëâ(O £­ºvÄë÷w0ü¥E–G… ‰É, Y>Ÿ´èåҠ׳÷|5훾…DÚüâžpë`çM#Û+®`S›'Íÿ3 ¶æIõ–]4áK/½ãÌê)B‹yAÄëá8¸ c”Âf×¹òÔ£*¬“šáQ)á7³`žkiÓ4Õ2ù=m<úË¥ë`ŠŠô¾`ã^ýD5Ÿ³°:,³ÜLé: ïß”B­¢.¼¢–‡Á³SqâtœÚTǬméDïÈ©¤Duôõ«Ãö¨ÃxDç&¼žCÿTÏ¥¿´¨ªÄkxL†öa!¬9"<#68Ó# -Ò±½ó†¡íî;lè`fÖTÒY9jÔÁÍ5ÿîCs\"_ ¤Ã‰ëáäNÛeñÉ’LÌq:²É‹E]0kä翸¬¨`¥þ|ÀÞA Æòì~GâŠ'?sƒ dÈÞ¥xxTŸÔŸ9¦Ðøy±‡Z¬L?wjÓôÝ<´ÏJê³b6–Ör\æKàI½½ ç{5 V5 ¥bË4¨¶·ƒ‚GÓÐöî)0TK47`ö€'šþ­o~·]’´ 37W6á±5ûpvèd¯Érãt9(Pi÷AfêZ,¬_Oý±(¼æ[wb2‹.D`AÚZVcD•^¯h…þ_ѰÂÍ GGнkf~VeF1 ÌRáiÁT}åHûöjS)àö’‡>TR£–y«€Óq‹YP5sfj4?+®°Þè`÷W*Ç1†¾±öcN*.¢Å× iä·DØ)RNöØÿ©Ìx²¡Â™kñKÃ0àfIgd±ö*TlI$y‘ÆFµ.âl:PSÝÙcµ`_åh5šE$ŽW‚Y[=“=”ÂM ¦žÎÏpG*‡ì0?FîHa CÅðí”BæÇÝSÌÔ–SPy}¡N•XçlG{æƒe®p„hÏât(ÙÉC¬63&UÏ™®Î,|¤ñš»î‡ÞÚËO>Ÿº„›¶Aµ/ɱS€1×B5ã¡Àå4žJÜEdùâ*|‘Û_C+yæO^¬¼,A"­¬]-® ÐÅ2¬öoæ¬Þµ™èX¹ÃæõÌwí:²Ú”‡º^}ó[¦‘ÃÝrLãÐKèŠPeÝeY(é—#1<Ò¶ÂsfðQ͉ͤ5ô/37fOMöšÖNyºàQY¥˜orÿr Ó`ÿ C*ºé1ʾP£³Ézø¬KVãŽaqÖfrº_¸žåïêÆ$ùjí%Ž®0?æ¨×_;˜»¿rš7¾gèóþ.˜?½ ,õ†ðãC WSÃ% ðµá$ÝQTCü “\e™}2ìÎã§Ç~Ê¡ø«¶ªóŸƒ '½§1=ýCôºÔ©Ñ€7Œg¾„›b3®†'„àñB¬¸r‘Ðp5®}[j“Ï-ˆàUÈShD°GXÎ×"\;oh¥w3;Ÿ£Ì†j˜Á”·jP³JcRpå0F/î½2¤þ@5Žhð½G¦?Õ®(};¶<œKÆä¾1»Î3»~ÙÁèÉmtK•,}¬Cçx©Ñ¡·kA¿.s¦ °Æ"“Ï»4^9½ž'pôðWlÒL! \Ñ“{@û(Ö‰':÷æb˜º1y¯ó‚-y¯O£‚èÕÒT®b 96%—JÝEäG¿4½Å}ÏÆK+•œZöÇ=wøkÍC—Tì"< “¨ÉH ÜÏä.öÒnƒ¦ÌÎD®$¿ÔŸ¡>gr©æT?MœÎ°ÊìË.4¦ë.Ü’±vëð§IsóMùPv¾a5Ë4‡ìèulÆv *¡(¿œzE/&Ö‹¥©¶«ÍÞ÷çêc?­3 ¼cMOŒmö¢W3Ž’o9Òx”8`°w+4šBˆô~Z+·á”&ƒÈÃ?ìØ[a꧆-TA7_‹›¸/,ñË…°I#„þ¸ÊCêÎ21­Õ˜~AŸ9Vð”ᶦƒDÀ6Ó&;çßí WÂɵIÎóÝí¸‹²Ø#mzÖ‹˜Œ˜³0%¸o®Ö"GŒÑê^J·Ây‰¹T€«‡Ì Íï kø;ŒÑ>§ ²æ2KLóÙÐ}HÑzÁ¨—ƒ‡3>0OôCÑíO-.þWíIá¾RäyþÇXòjdSîP ¡ÛE‰À:^ú¥mbS¶â½Kvðå»eKΖºT®ÍaWü!°Š&ÛKpüUéî'™íw°cá\úr®,íÜBhé¹pYHn¾Þ 1ÍÙóZ ëÎ#æ+¬¥»[àŒÛ2²i~6¾ìð„’sÈ:*F>…"¼ƒx÷ÏWæµÓSXol «ëÒpš± æq$#¡ vJ„ÐA†CgB³ÃŬï¶Ópq° .)¾†'Ù¡wVDêš½ðìËUº‹~cb„o¯MYk‹_._DÝZäTs >Ø*†sûAEÂJˆÓ=*«Á!Âní>J%Ïš4þdŒσSmilóE!já‰èÎö©ÅÏÞ㧘‘¸ 4Î⋳¨šñ÷«oá .ŠƒÅÚqjÍ^¤ý ä ¿*ÃXESÛŠH¼‰Âäy† _Zã £¬MäÖ@ð-YºtÝ 8t_œÉZާ,_ã¾ûRt@ì(x§BÈ— ¸xî~<-D¾Ší…è¼lnû’@æy ÅœS°¬ªŒé¦'˜Ï&ç Já"´SïK·@º;Œ­_Áʶ¾dñe뿵´EõOÓðRÁ2ôˆzÅñîÚÊTZöâ…y ð8G 7vâê5a$î·0¨kßeí'˜¢7&øÂSv‹rú»BÆ_Vpý]4þÉþó`…4¬èƒÔ>¶RHŽéè_A—^ö ß§ó^cGª’󯞃ã diÍë[´¹0ý¦“ŠO¡TtÛV{O…ÎrS ¼6.ǧRÅ‹!ùÒ]Ø<–7ŸgÑÃW»Hgx 1ˆñç†o•e’ Eê¨Å²“¤kÝ;ÒÖ}óLtèá„ùTbq8qØ«Íæ¨èP±öL6¢iM¼+‹­n¤É5” _J–­tƒÁìÔY꬯K>Ç‘f /ˆòžSîjލa;ö„BÊ$Â=÷Mö%®d×=Ý…kT3A·ï,ÔezÒ‘ji¥ââÀD«CGÌO¶ÿd"»ç¥5õÚ6‹F%¬‡){¸ UIP…$)Õógžk4àÈÀeV`4B¯èÒ^/ ¼°õ,^PX™ƒ ôû¢ÃÜÞ×ud¯q=Y±Þœ Øó»ÆôÓáO¬Ï·5¤÷ˆåˆ€úõF´|^$=±ü$•ð2o­>€ý_E’óq1¾vQ¥Þ7Ï€’…kxtmVàì¦ÕÔ{t7&¯¯Å/í…¨O¶qtm#ô›3¾ðØBͬ_3/j2¡áÛ º¤ÖŒ ¬½‡öçƒÅÆÜ£“¨ïhA—9é’Ý‚j2Fpvýy_ù„sìØfªO^±*.Õ¸Élþó{f%u¨dh%([bªB.T-d·¬a-„éYI)¢Êd ÙÓˆªÛ5hYí"¸“øg~Ë£1[±2\5Zv·¯mP¥ ÀKë¤èÊž^’ 0†Mf ¹¢6“œI+£ó¶uáíèÐ>êÏÎc¦qÌ´<ºòÉ‚e¢Ô·æÕÿóË”ãèÀ.ó0{%ýÊFvÞ&sw7“oͺ°Â$ˆ;A?™“áÇÉà Rëâtàê€,ÝÇó§3ÎðÀu‘y”L‚5ÝðW…*´h»ydÍ|Èiëÿ‡_ÏÒSüÛˆ/WšhÞN¢3òèEŸ8õ»<Ñ{¹ƒ. ‘v‘Füí-„Ã~ºä€D,»‘ŽûÞ€ßý™4Ë~95-Ä«;˜ó7pØg;ýöê>s7ÿšªË ªn­Hûsr'kú§"KBù©™‚ù}&šã\)KO羂هò‰ØVMëÝŽI«ÇlâCºÉýµ ÄùŸ‰÷tÂK©µÌñËCÖǨ씧ÓN¦2ÅePqlŸ¥IsfIÓl«õè¡ÁG3Úyibæ.½,¿ž]EœRõd‚¦&ûȘV õ|·ˆN{Ò~wÞÏØ¯±&’Ï•èE±bzÉö¹³ô5®í§}&Ä‹Žxq.Fn½ÅĤbá :þ4•^"dA|-;ãè&Ôßûíö(aÊÒ8¶ºo;ÇO"âˆTç)†9‘Jm=¬i€ÿ\ìqŽD<ÜJm®§kƤ§Er¬\‚™b qzj,É ‡&‘p-m$E†%Œð×6à J@³#â½!_Œ(Pù¢“ðdÎ_ÖŠCŸº*&¾‚Þzÿ„½¡9 ÓWS‡á4ñçW\üSœñ_1€‹/ÿÂRÝ8^±ˆY²ÊFC&ñýޥ̞è—è9Ýsr\÷ÚÛ;P6±‹9™PÁtY½dNOý3LÈ"‡@wí#YÑ2 ¢½—ÿJ€Ýsg°UÉSéŠs¿ñq¸1ÁGFiìd¬Sé¿ÑBA—¬÷—ÁæÐ&Π7?!CÑ𵼋9t%@kœÂˆÝX —[“¬z’NµÙÌ͘U‰g5Ñ1ɘø (Ñg÷ocHÃL¨<LçÝu@éÒ\7É=Êâipªåù솋¾‹ò#‹ºfþEwí^ç¦ãÈ;=eu›µ 5Üc´"^š¤u:‘Vyæð“DêºP*7@œû¸ÖÌÁdÏ1|½v=´ú5Z1÷°'œ‡¸›™“Î&æÙ}{*°{>ÍΘBc7êÀïî'øOèÞ»›}øI‚!vO1Ái6d)ýźúp¢ÿ Ž&GÓûoùÈþ'úzΧÜq«}¿¸,ò ½âóä½ã±'^ïR  á[ØÍVFÏm®ßp­JvB·ÄÓýd1{÷‘)ý=øLK’t¼à§cZ‰°±ÖŠd-¦°÷ÀY¦¨#.íx Þ)Š §‚¥‰¬ÖÐWv½k'ÔúãŸî¸Dý8,˜2ۯ탾jôåõ ¶ìæ&6Š,n?—ê^!¿‡9-u’-r{Ä( 㬕1Lý²(”z%¿z³MDÈývl_©85dÐæoØëx…qœÌi©'¤åv!¬è:…‡;‡±­‰Zö7Vµ{È:Éò<ÙÆœ2½•é!§\ÂÖãϱv“B ÔìŸJoäÕAøýfcóZ’{{¢xÀaxí½üÕ‰tdw?u¦¤lëÞ­'㟈 Ð/´ïg¯Îua]a’—TLÜËaýÜ5-›bhIC6.%*’çáxêrîÒ;ˆgÌeÇ'k„Ì+ÿ²¾¿Åk?³Ïì&p½8§ü]»óó(S`a s®]„“kÄHý“óP}\<¤fSsk{HeBqZh~6,§[þJ¡çÄ0(Ì[K~©iR².˜Öh=Ã=ÇWŽaÑÞ±‚ûÖ³7X$µ—hË$?Ó3Ì¥)^ƒïUΣà éã¯;É[c†ä”Û’·Û„È´ zB„—TՊЉà©(23ƒÔ+‚ÀË]$¸£$6GÒÔ ‡á·ˆYzÞ°”"m]ú“ûMrç[tË–ä[ðiîÎöpà4]#ÇÝ|ȉÝyäÞc+Zyø'É×Ȇéßâè~ëV²ã-æ9Í–ŽOxñC ôzn¥‚gRÁ‘gY´à,Þ<»Œv+Ôc®J­rYϽàâ‚KLàÆNm""MÈ]Cg»GÆŸ¥ãŽ¡ýä©Ãü¡á@¼ÞraK¶5Ù/‚RåÅÐ×ÏÀûC'XÍOŒqê-ÜÌì Ï_ŸƒmV*ôfÕQZ°à ßõ §Ð¾üxÒx¹–rU´ˆç&A²@9,FñûY(pÏæ£çÀfâðªt/Ò/§wÒG–8þ>ÃZG9ŒþQp'îShÝ1CR› àö3÷™d¯Ã`¥÷á–hÑ È;èéXKíé<*ÓÄ$“¡¾~Ê({îò­þrCŽøË÷2ȯÿŒÇd|éüüjz§±‡YÛ~^ëÇýïÂs.†è¦rÂ×Ë‚¼¡0µ:àǺÌÆJžÈ{Ÿ†“íŒü­°`ö8ç™ÙµA‘Î|&O†l×Cì‡JLÐÓ¦-Â}ø¯ûúÈ©p…v†CÝJ.¿Óq¥T‹Â¥ž˜Û¡J|v¹·Ð¥ê›ð[ê3(ŸÏäp©ªÌ}{<ÜÌ­Ø–Küá9|½zêRÐ=ã(5hÓbG®^ÁN6žŸ7-%6‘4³c.îÔ7%…YñÉ|:õµf>‚•lEV#’&q2˜¨’-¸g*>PmçÜ8·šœ²ŽlN1`ŽXò8‡-êúÔTYøˆÚ`íÛ,’ÜzJÔÈð¡|l/fHŃNì ”€¦Ìd¦-2–´—Ýß.$>é¹'"C‹G"éøÉïø5±‡Õ·µÂ£O¥±¡D‰º5‘õß…HJg wð’@™·ÐX;J¯jõÅÜγ÷áïzY2‘à‡…Ÿ²·sÊQÃX„=Ä· r®©p¿}K4ž.'.•kè—Iÿöh¿¯Ü äàÇ„t):oÍÔØû ÎĉC­Ííé:øf1އ¹‘8ûz¡Ùñ\=ö+,%›Tî³¶çQwãwp­Ä„µÄhfËòf M7”=ƒiòyØnÔ‰mÃÑìß©®Ôa!]^,£å–—Á°ÔŽf®Ö¢|º4¾Zž||*MS»#ðL`(È6HÝ ÖxL~Kbàws$XùRlY¦JªV­Äd‘3$¯T†Ñ¹~’Ýæ«†%¬Î zƒÅ†rd±þtºÉä?â‚?õ©HÑ:RðØ…|b8†sIBäi…qÌT˜°™×p~,Z®È„™«Au_¤©ðïóOàîCLà ÷¼ýÀçì¤)Öt‘ݤlCÏûøp'¬å&ïo³¯€èÆ,ÿ‚eàé0u"—FZÎ"e'fûÙäÓbf¯£3Vˆ}aÇøè³S§Q÷N –¤iR›Q½˜—øíÜËØUMÀŸ3æx²ö6w^•›ý~ Æüp¢S¬ê³§–7sÎ+NC0 ×МûßÿNchàNIr^W”©Ë¿A>!TÂѤšX÷×ÙìÔÏ”ûO¢yõqwšú½€±¸Ü#™h°†ðm.=‡@6²Ì’ª™ðÑ•kO³F[µèÑåwX ¿zXq+ÚÕÁ¯ð7ŒO3fëêÉjcY´²S…wrº$íK?†}qÇ}÷l1Öí5lPäâö•^$ë?Mª’€¸ ¥L|° ¦V˜‘½Wæ€dN-ë³|=^0Ÿl Œ…"çbÖP¼•Y¸HáúͲ'eœ—Ž›Pø·ù%£Ÿh}³Îó^ö¤?ÑzèHË<÷§hÙ›ËØ´^ÇÎYøç\84´ ±–„Ÿ¬«¯AçGÑpWÈ/ÿmh6¤ÓŒÜqÀŒ3Ykîczýuü—ãáy"´4ëøêè³ö³‹¹“kÑö%tKÿܰ"Z[u‘ì,*åkCùš(Ö"· Œ´ÃHP­9I®*çÂì§{{‰7o# U7p‡^'±•ðÜY|ÙÜ ¡JŒG¢ 0—‡ŽŸÃ:Å?xíÕ~Ô|†bª„¢…Iõaoî¬K¿Ø æ’U¥¼lÆú£¸Uåº8ŽïÿŽpŸúuBývqzßBŒKíÐeºÖÀžŽ1Ædí¶®-ç”q<)sjÞ³¥Å*¯³=xañêe9ÎÍ_?,ÿ 5‘ì@vk@/Óâ:‹{õ”$½j4ƒ–‡ÅP|ÅÔ1r°×{+^œõÚ^ÉÀß Ò¿E‘Tí7¡'sà„u¶ŽüĶð‡˜xŽûšazÇ_ä—¹‡¥oZ»Þ+gRþ>„¸·òPW›‚q§áX{ê‡ëÁ·?§°sq&ÄϦާüºØÑÀz¼Ó¤Ý¶2>\C°z‡:,s¶ m–ÌÃŒ"#·uêͰ^Ö‚\ÜÇGzgÓ?_VPÞ†)\µ: Ô^?SÙ3«ÄXΆfqÚSü»ÄšJ¸ˆŽ%Z¸hÏ6ÒÙö‡+qô<ªœFóÝÏ ûýE SßÀY}O=tkÑqÝroêå$5÷p⢿ÂÏGìH‹/V¸ÙqÞ<Ç=NòäÃçAF'«îx73§u¾áÕ¦+ŒˆÖ$gË·'‡Ô—’•›î³ÇfiâÖÛeÐy[‘óÎx:^sùK„<¹ÏKt鯂 ö“Q.–5áÏ|]6jôî¸~ ÚK³8ÞÉC`ø5l¬ópv†%p•qØÀ‡ë8¨¥ÍÂÚ%•Üeç~!'iÏ*®§É£!¬ËÑ?‰Ù ýá*ê:Þd’§ûÐ]ùP'Ntâáhê, ØÎèý@üQ´]ÆSqÞŒ¾¿eÓ¿0Nß Ø‘òÝá(Ü^ö[{ñùD=ŠÉL§®ñk ÅjúD Ð|Ö‚ýÁjµ*ÑÙÁΞeŽÖKY¦]Ò÷>Á𔔦¸€%ý©€zîyÜð™Ï9B ÙFõ+ͳNC7ÿ[¶:)’›Ú}i¾Ž“ó {i˜ÄƱ€)Lð™0vÓÄ’Xv+ØSY Pcø.ÃÔ`iê]>¯ÂñÐÏç˜h)ËîÊþŒm«è­—û¡öÌæÌ)4–Ö ïvåÃó/¡p}ímœ{ŦÈâÓ‹ºØÀ÷‰±S go]>—m©KM-$Ø&¤ž¥æÁ¾yÁÌñ©aD†â¡{ó¡rõ\ás†KàÛüHö‚Ô‡Yå§Ð_Jî=¸;;°Ð]î\Îîz…Vq´êYk¶5ˆSQ|’mÉóCZL·O©`Ôr ¨Ë¤»$A­Y ‚« Ø#-ÂlÖB¢³ÁÓP¤ù`p½èÚqÆ´/á’Uèžr‰‰øô“cWÛ Ëfc6yã#)G2Í\‹™¯xݺóã,–3`wìQÅû…C _ë`94W ž ­³ž½ùlRL„k/Õ‰òÅOð~Q «$ã)Ãütxý'Ìz›‰^ƒäËjA¦·Ì‘òYÒ‚ðÀ—Ç|·÷Ø6üWí F‰_8¯% Û‘Fs??aJÒ2pïÍÎú§Í¸ùx~ï'›«­`cêV4™{í–hÑMÀ9ý4~º?B_±·:åD1,±Lf/D/¢Ù£ÌÇ9.xáÖLºâ@·iÇÙª‡+—7Â^é=ˆÍÑu¡8md¸ŒU½|¾ù›1‹ž½AU³+øUn†EÇ“óB$+.ûê1?ûÞU๯à…v(VkcÇ@çÑt2,*Is¯ï£²ÿ:Ž«¦@ÅœF|k¢€=÷§ÑóÊ&Ô+¶œ™²´÷üºe)èáÓ§À‚ÿ¸žXpîlÁ¹çäÆûöÂ\Ç; }˜—ôlÄ+ô¡ÈE¸™éªÞѤ'½ëLDAåÁað~FåGYƒ yïiØÜâƒ'Úåi›Á|×èÌNe#÷É¥ÆtúÛȦqÙ™ÏX=qEë£qxŠ1–¼ +ì"¡:þ2^4Od<DhàÚëp& (Åzи†ÎãÑÀe§Qç¶$8¾â4®ã Q¾ô¼I‰Å"ÍÙ$ê`NI/€«Õo çW tj*:Í‚éôÃÜ-i°Ê' ›íd£³cácÀFìÑ Gò·‘ÄÏ*d\ùäQž§«á„ù<&a{0‰LÀË q ÿ0À\­½Á:ï@žùËèÓR'ò@u×/ï³`b1žÉ%g¢©}YŒ®Ê…Š#ñÒu1ÂËvÇ·—lÆ„ Î ‡¤$Mæ“ü‘#Ó 4©µÁmFl¯4üN y»BˆÁlpw·¢KççrÖ÷‰pÚtW°² c™Ÿ¼‡YÁV{j³C Žòî¥ùæ¾´,`*»òèo¬ÒécÏÎ'á;øè¼† V2²¢fËQsnî‰7†&ådlP)ÀÉ5Ù¼]\,»çË\hÃ×ìOŸw NÊÁŒ~ÕÖ*p¹M7Hà·}·aÕÖôu';ü0„x…õâÞ38wº3ÙÍ#ÃOvŒ¹g™Ó'ÎÁ`×E¬JeÍ7Î%­æÖŒ“mÛà ÷_Nìß›âàÊ&=d Ýî£HgÝ]LŒæÿÁM!iLEK4²CA«ÔˆŒëë’Ë7…èô‡·Ù¢Ÿ†dºV Ô=Š'[nÏ #áZÆ(¶öųÜ™ôÕBK\•ž iߘ ŠÉúÀÌ]ÄVZ“‹s%èk™ÍTáZ%œP£ÿŘÞjÁ¼>:©í(óÆö—^gœå22eq¬uó_¬jmÇÀ€íäÛ ªìÏ–Ç…5¿¸îûlb5TÄ,ŒÀM¢6Lå™Lì°§³+¿25«UASâ’»ìS=} îl[F„±àW–D²Ç á¶Û”´¯`ޤM¥?üHÈÆ<\=4joÅâWë˜ ÿäðÉŒR첸ò¨­„¯Ã¦ã­XÌÿÎ`âîÙøîñv_ÿjü虀-zS¨¥¯}%*Ü»¥IY#ÆÙz’›- AÝx}ózܱ©j2‰Í‡Âûâ`e‚F¥îÌóDUt;’ÀÙñÏ<5IõGbÿÈŠ&ñ˜“|'Grçõ\Üâ/…©r/˜UF{€e“A)Kޱ¬8;ŸÑ«qyÐþs皉¸oŽÓ~¥s^ªÁ Òþ8í÷š0ó'žß­JEç„¢\vþ]ÞÃ÷ÑlN7pºš/¶±W~ ÓcsºEYvͦњ¹e|€hý`®Æz™“ý’÷™Â¯$o«(Ѽ¸š;{É4z$÷ëë]ÌÚ~îÄiK {Êb>­=’Fí˜70øÉ[àQ˜ØaY²áê+;Ò±é36Ý´c_ÌuG%%80} ý~\¶jO²ÄCž¬Ü(Èüçr22‚Ndd±ʪô¦g÷k±öl§á&)t$µ ?½ô¢×1Fvõ™ŸÐ"Ì€¸NbúkO„Éæ¹ôr—Ù¶¼†gâaù&"{DƒÌ“2!ñr}ÄÍ"€x”`y¬×Ñìàq&}‡$i<ߊ©íIÌmTì¶”fÝ%äàAX¥‘…a+¢ðsÙØÿ4Ÿ._L›üD©)SH,Ê¥¨H°+>ÚLf[®%Û]¢™ s¿X/lç§Üïždà‰<éþ©B¸ï^sö)QǧY¾%Eœ:È"𼑴t¿Ý­q‡ïçE+‘òdùA¶êR-£\[.û©àš=tbà 6KןùîöÜ7œGÁ¥/§c4ì[Œ«o9£Å°•»§á"m´[âlœ ?U¼‡9Ö’Ž.7×àÞmt^Û_6݇ÜгXíXÀœ0èF#)e&Zù¬^Þ†Vgòà¹í0ø7\B™£¿Ù´´ä¼Ã«óA…ô¶îEƒŠOl”Ÿ?9.;—[V‘C½rÔãáT*Ѱˆ„(f“GҷОóCú”|VÕ÷²ð›h98?lç!-Ï£P!$\–¹ OU'ºÙ´A’Ž)žï—B½sØ=®ô˜ÚâöÁž2Vo¸œ¹V^W Qú["ÞP™`ž…«uÉ2Q}Ô7(Ç=‡Æ $^ްvÅPú>G$þ<¹Ä[qéïU%žÎ²L¯…*y:ºŽ¾Nq"yŸ‹ñEù('ÙË•t0©ømö7¨¥ŠDšÇ†|þÇÊìZLë¾¥ã«Ç׸uÈT•5œO=*hüû ˜±J4æ/ûM¬œ‰TïãP¡AÆ–/#ïî`Ф‡™Ós¦¯/3É£=µüï’õ‘s/ 2â+÷^÷JN½þaüw&ƒÑ x•¹z4 V›Fw…Qåi¨uë5ûEZ|^K×a|·PŒ0KÙ¬b¤ÇßalÜyΪ‹’põ4š(IK»°Ï'—•.4Å ê´BÑ™ÍlzëK5’6À¥R ò²Ën|Ê‚ udØ#”¬U¼ êKdˆÉ[ôèß1ä$ÏÜ¥h7ãšZphû­RØÕŸs;Ù«’çêÚ$=M+I<æE ÐÚY·1hgÌ«d"UI˜u#ø}„ø A"ò0çKVc~æ;ØSµÓ­¿1ñ\J.ø;ý$ L¨f¦|õqÿ¨I]þ¦À¿QŽ[³ yÆÇG5åIWŒ*‘ ¤¹±ÊdÅùZÖo×_´¶ £jò-D;ïبò±kº$Û߬¼Ù» ù$ß×zµZ#cø‚ª•ǽÒWЂ¾â´››ˆÆÓÖÐÃŽŸa{ÜtÒõ˜jý}FG¶eâ¡2çUEû·…¢»ä8”íª#Q-"4j¾|·‰%=óêÈ%¾B8Üow,šàòêQœb-DT’O’õc*d¾=†ég¢ëâ)Lø¼eXÖó ×u¬ƒ›ß®1éfJäíhÔüÐ&Q†˜"š÷¦WÁà«ø:oáÙµž:´†\ýËCµlÁý€={ØÂÀKÐQŸh}cê÷m9Ýš)MšŸ²êE+ÀZk/=¯Goe+WGÂïÁ´HZÛ·“­êó£¡h×&”þÙÅ|Ÿ{ëÇh~q"ìSÚ‹ÞÈ»Ï ½'XKsj>s²ÿÚÏd¯û¥Áõëk™®³§ØEk>Àþ UtÚÁÇœî+À¿#ü¨4q}/_bn7³ææÒ䉒hs韬SDÉt3n1–EùèSTîl«ÃO+§ž‚RY™oj4ÛZŸV½N@=OA"½…‡8¯¼ÇØWæŽ?×Ås›—áªýº4{vB…1ì᱄ûþ~8C<ŒKrxÙCšû`ÆÆcìÜ„ `mdF–œÃkq;m7©•ƒFŒíŸ~컇eM+˜ó='pëqeFßk1±}ĈØç°©É3&/àý²/;¤`ØP›q­ÖÎׂÍöÙ¢¸)ÍMšbø›× [á‰J9~Ž^þBÓhÍúmÜÃS%@w¾7ÍïÀ¢¸Xμ\Ø+1®¹ÊràöåäËy ºcô“q2î¯k—è{Aq“מX›’û#aÆß…´z)uÍ¡é$B›Èíj$Ñ} ±9ž‡m Õw5øðñÒÈþVlï.êÒ.JÕ/{Ñ’b^`/¤§œïp_%Y +1¥çþÍæ~»m²^¯Þ„ÁÏ¡úì°ï‘f¯˜Á´¾ûìЪ P'tåk}ÐóV0ñ¨d¶×îe¿¶%áÜ{ç@©E‹8H¯§Ëšwá+KºÚóóofæ8 Â6#±œ:þ ÀmÚ4áÕ´Ølʾ·³‡ÓFX·×€ÇÉNü"'G ä¿£÷¬vf¨²29«‰ÌTIü»YhYÇÒ•“ù!ØxÝzü͘­Y•‹„é˜d!½zQ“–*Ý`Ç«q29™.-*çœ)=¸Í®ÖÝâbaã~.ç´3ùùe>;­~³WÚ‰“Fw;Üã´ø="‡s¥Hdž7ìS/ ODZg4œŽå«Ñ¹‹¬™jOʱ—¡Å}q¤q¡4™Å¯H§Ó{³°xL—úÿ‹%ІðþÁ*¦ì‘$ ¹JvUC·oÙÓYL/C“£¹wdIÉžCpãÐmPM1¡mo èîŽSøæ!ghÿè˜.ØnüÝJÙ}´µÈî®é“Øû;ü}£ûžÝñâ|8ºžÔ—ž­“ÏL‘T¶B|;M×N¥t}9V€4ÎÜŸ~N œ¹°Öó.JÙ$¾7ƒ¶ÊÀRÄ·—“ßöPÓò™5=2›³£|Y$ÿòm~A÷Ðoh°Æ€oñœy¾â¨P³¶| %wN‚§ïÄðYµ¦‘¢#ÿ~ÀÁ[ò”³ÇÎ)«ŠÓ]& Ë–,8´‹»Fu6 Y’̱_w”)ÿQ¼i~€Ö«J‘€â37÷NMF8ÃiåÍbq lQŠý|â㪆"Í› èœÆ™ÿ9Ÿ)«ìfòMÑÙPd{o»FÓÓ ³Øà(òq€…;‰ %ÿaÓ‡Tkíƒ*à~s nÕºÅúôš’ôß' æq,áÓ£[dzkî ‘5;—‰qÔj[Ã]2Ÿ=¾. Vþæž®H\¯Ô·Ü‹§@(«éS†­å2èb|’ØìîÂ+×N‘ßewÀGÝŠú®IE×Ì%P.\Á¬êÇD;R»67<5¤÷&1«ƒD0¢²0 EͺN“'§P;8ŸèÝŒíÛõ‰¦–½œ—‹Ão‚ñgæÕÃÛ 8ƒ‡œ¤‰Øœ(عð"h¤«Ó´†z¦V¹éì˜UrøË3á»ÃÉpiÂvõbV0P˜<ÓÏ«û'¹Ž­¨$uÓL-pklöŸ(fÝÛÖ1:ó3ØUžb€ðM®‘Î&¬\ÌvÞ8Au^d?qx&±’6ò§¢œÿËŸL•¡[œWÓ”-UpFò0[UâFë–ÞÇcó¦‘/bïÑÆø":ŒM@üÆ6¦ðTH«ë÷}uÜ?îàûÓZÆdÉ+`ßæN¡K €ïGiœ³ÜŒùÔy„n¯½ƒ _€Þ!]N0Q7 ïN@»ª=• }ü$Þö2Ì̢ͣ9%LÂÕVÆ Zd •PpZYZò•Ï~C÷](qs:•|?—ÑÉÖ¡Ü™Ä*è×ÑlfSý¹"º¡EH>í‚{§pqèÔé÷ }~dΜxªµ¯ ÔW²»üéùÈéx_9ñ’ …ä‰Ê_àŸžk4“øq}{®‚ÎÃ!àV3ìbÉÏøT8=Da_J)U$¼¥\×#‘D/à9°öF9$/~˪ë)“]f®„›O$TÍ1(Ä•p>³dù½×ÄCg/½V÷…ž¡RW3Œ k²ï<¸é¼FX`÷ 3wOg^‰„í³ˆ/o*™ymáÛôuÄÐêgñÈ¡2´:ºÊ¾£÷‡+IÆ1½®Ê]ût/ÞËÆ‹"´çz ûfH%ŒÉ§•”ùûv:ÕU”&ö+U©ÅŒ‹Ðý¯…Tïý‡ÕKÞ“ÑOh–ß®—ñ‚è9Ê‘_ÅpœÞ±k>¶Sŵߘx—Epu³:}Âõ÷Eé‹T>²Ò>œì×¶¦Oá3+{ºài?GäV,¤¾ÙвyÉØ;rìbæÜ¡š.:ÔáŽ9˜ÌqEî·È÷\‰®&>›³‰jÝ\€Údå>oV]š¥ÏÔÁKd^;ù‰dä¦3ok¼hŠW åÿâOª"ðȼT¸+• IÓ¶ÐÍ2zTó‰KV]D¯¡édªÕTrö~ 6_ÞB‹¶î'× ×[ßùåH¬•µµõ÷àˆän¨O9[û•0ú—1 >B¡@þ÷W™%}Ò£CÊœ8ÓôâÐÆ9¸éuή¡7WCËæ]t­]:#¼í4SRoÌyÄMì)ä¶YŒ³W³ä@»Pæ*šÍ°¤3 1ó¶×€†É;,í^ZËm˜Çéý¶8ü(J&ç1âŽÑÂa)L\òƒ‰>×À šÐ-«ý‰Âé³¤ÏØõ¥<¹Ó©k*xöm„˜áB<¼Dšê¬²·©F¸!<¯®^I´'ÏÌDH)ÓZr™ói£95vˆÃÕº¾(ÉSq)±ôxn°üU¸±b iˆþÖÃq¨¯²£ÄÛÁ´é<ò|»h­Òó­m\áÜðzØ<}ªÍ #zUàÆîÌÒ›NÚ ¨îÊ(¢3òWåÞ¹F•Ô6Å•~q¥±#9Éw¦R<è*Cü®Åæ2æ½íe¹§ÍŠóŸ%1£žÀ/qã…—‘`i "zÿ.·uš,¤Uã6Ä}\ˆÞ¾±‰ÎZSˆžaœuµ} ö`6O¬ÂR÷•Xò`l»’‡´ãé©Ç‰ŒÔá üéöê%O£¯6¯umÔJf£Ïš¶BŸ¾¬Ì@à -tù®H´êÁÊ¢œ{tþ?àøÞ&'¶ÑÇãÙž¦tm©23=[‡„H“¢ùÐyˆ`Šê´ÌÆôôÙÄÀUŠã ¤ö¯èuŠÎÆTHQd…¤ÑÜç]ì¦{{1¯\‰žéÉ ®—¹\™['PL®ºÉÓ;ây¤µ!Fô¤¤øø!Ä_c Âì3›Pk}Yó¹–Í>oJºWTÃQÑxnþh =ЈާO3"”¡Ïupj×ˆš¹œ¹÷g;WMA2Ï(¹,› g¡93¾ª^œqGØ%p5 ™ñúÊ[8|ã<ø_bŶáÝÞõ„ÏØŒU³«ûiš_s…­Í`—ôˆ’&405;…1 ̶q]’ÜþùŠíö;A^jáãÉó¢ÒIp¦µÐN ³U÷К]!€ÚAôÏÐ#¼_A'@†¿…ìô-‚±Àå{„‹sÏÈpk-7Ò7BƒìétK#Øf:ƒˆ¯LeÊY&µ©Æo3¦ñ’Çé~_;â“ÌüEda¬.N£ùgŸÑ å/Ņ̃ÙH%ƒbWϑýä«Ý8{ð-ÈE=d{¼¾¡÷/!ôÝ)„Å{e°ºoµ‘˜Í® žÊw!¸dF͇ž«Z˜ög¼ÀAߟ˜_&Ïü+R&w9fD¬á\ù rI‹á³ß ™ áxß rŠXe¼†wzKh`M.¦$¡æe5šI§Ë> и‚;ž„ãç¿FØ~­•ôKd§†q–ûòг~§QöØ"6æÃ\Ÿ!Mê>ðÍX§!‡­þ!?Kˆ[ÿsÏ×!Ó†Ú¹[’¡;é'ì>dIx·7[ÜëaÜ_ô2A)¤-]ȇþàìXÁì?= Ÿ3ÐÝ3 ^ýÑÇ"^aòpÙ»¼ô0X&èÒ3ÞÝ`yeŒýzÓˆ\+Ž_Ë]&{7k›áj)yú¶ÞTî·’ÚïÁlÔ}6ɹy©Ü)k’e8…ž0†ï ‚¡ŠÍ9ô£«C/ìjĨ¯! ôE†.4¤4ž Õø[&‰|>$JvXY‘ÂUñŸÇQ2Øý‰­J0#.w×’_C«qJÔ4šáÖ>gžÞCEœ/á·Q1šÞußg3eý’„(ÅÀB6êK.Á!áìF^°—%_Þ‹Òñ;lGÄæ?ö”Æ`5†fŸ ”Ör?®1@±½r ]êæ<£°fíT2K,j1_™žˆ³v ½ºÙ­;—À†´íÄÛ½unÿd¿ƒ-žaÊß¼ã¸çå­Éªt[“( ¸ø¶¤[¥¯ñ×½<8}–=‡çïÖ0+7«ª·åñ_gLWpjIå¸|·€¹M Üg÷ylr¹î4Áñ,¸7…ÒrqZR E{O颳ÕúÅ+„¶Ê¦Âûd™g*-ä„H(‰UÞÅ}õÞž>]a€^#Å@ב¦=Ì­ÈT:þa t cn/Á€÷>8l\ 'ÓñŠÖL/ó ZIjäÞÂKøùBÎì›Kö¼<ºßÇ1àX mo‚´ãÛÙ¤£•œ9Îá{›6NqÏ ïÿ†%LjÛl^ö]Í1Z­û^Oჯ‘Ê8è0Éo}”¨É®ÃT”»š¹¢(È:õY±ó4¾/Îd¹jqhœV9»v¡êú-ŒtV,L^•ÃÍœò9’øvð5j¢Í9pÛÞv"šý¡q~í ! KæÐŸûÛU£Zl7cUhJ<*p}û9°ý ~‹ù¨™h Œâ¿ûzŒˆk$ Ê>Á?ïù1ôIçB‹<û6ÙŽm xÌDðå¢ÏÜGЧá¼X ~ð"뿯£»s`’— ÓY¶î„kîMDÞc“×NAÐç8Ÿ8³Öä=¿—õ½ÃÙpðŒ5©ô.:EžzøŸAÉ?ûè¬Ñ5¬ÆvMfë$—lm; “û6—¯LÀp˜…‡Ç/ þÖYøôÄc¼N;tc¡Çb:ÍŒ·ZJ_¶{!¿ö 2u¡/ɽHÏ”ëÒî ‹i,1[jÀ\Ä—>a䦤$1_² —=°r9ÌKãDƒHÖSŠaoçÁ–?ÓI‘O(98 ŽËäiii(ûmèó¢MtÿLØ>+”J^"oë` ÜGüZ8Îú*ôÀ'?EŒŸð¡‡ZÙö*'øOƒ!~“Ìò“eº%D‹œ>x˜æÔ °f§én±vŒú¿Ò)ÏÉÃôOò[4\b ‹–Á¥Çcö~!Ì5ÕÆ~Öª¶€¶™ÌiDæ”Mù²y®±žt^ÙÅtœÔ%‡å÷ƒ¡Û (ïÝ„[–ÞÕ“¦!¯5¶ª!ª† ¨êšu/‹t&ESù”¥ÊVD±Üm®Á&eè±y¾¹P*ß‹B{‡ñó¶/Ì矾$õ›éû‘ }ó"€3ÇÄ…þÁ¡²¬¯é/ó¼€;òáÙ‰%È™s–4;ãæe_àÞõÛppípóèa?4³šJG¯ ËGŽcfËrëÌ®4â® 1JR·=މ tqúsxaÑÂÞšŒ9*±F] Mó¶±WNâ\˜ÿeœ9ì/C †²MÀ{7Å誴Pbô¸ZpÀ‘ŸóÁë ÄO 38ûѤç.çîÂ%ôqJ)z©iÒño ´&oXÑp§ \pc#^Þ9n[ï@îuà5TgŸ=Ü:õ›©Áª9DÂMŠfÇ‘KU5ìË?±qU*ó}w>4ôè ûëlÎ"uyzó\WâÃnÂî ¼ÉZ]'–A|T6.s8F˜,&/Šz˜S û¨rÖ,²¸³_kÒ•ÝåxáD/ih¡mŒ«²í¬ P uÍëÉÞ…Ðò«“³<˘Hh"ÍþUÜ3E÷Ø\ÿYt?Ø2cͼ r)’nRŒ¤Ë`À¢:žJä}.ƒù´°;½Œ}²&ž®ºµæþjÄ+ë¾ï½¬‹úMX»BÖýÀ+–)èêaÆÆêAC›1¼º01ÿ õÒ3œÀKœ5!”ûó&;ÿW6$ŸÑ¦'ß$âý7ìíVdÀf*­’=ÊíòòAsÕûÌ%Ïâ{œ‡“÷ÛVÊip¯T›Àþ~îQcyâ³þ“  Þ¼3™åC{ȺÞíä§/E'qÍ{Üûq?58ØÇo"o.rlV‹Ãƒy ¨¯N%[ùqð‹‚ïÕÂv}-î«ÊwwÂÜx¡ ž _áà9hq:Îþ§ofþT@êænØ8¸ "ÿyÓà‚wx,^Ê`T—¾†ê¼jˆùu—ŸäWkGÙ=Óõh§ÛT2VÛ*¶P›B#Îtˬ}Šê}‘"_ü÷À‹¤*Vµ¦ß¡ÜkMd¢i@èL<Õvw®j@Iãf¦æ·œ‡EŽ¡tK!Xñ¥ÆV'°ÓI˜¶÷û²Êr 03¼Þý5Ψcâ ¢éîè.z/U¢9¯2Qn¼‹ÆÝù0ñ³ gÏ9ÄÞ±¸ IS‰Å%j¾ØžN¬¹Å¢@ÇàLß´¯™ìá¯YÖâ×d•«¼ºcNv Ov) ‡ö cÝVíù)àuÙù?c!¿`e™ÒFš¬~À ˆ0Æö¨cô}µÍçM€¼ÔJ¦ô/Íéhç=šÌ,›ä¸‰–?p~È/Öqém˜³¦=ËÎ2;Ôüˆç§ìú’´Õü8Z˜Æ@ŒÝa|Q<ÃûÐÁúuo/Ôé³­ákpXi I‰¾ m&ª´£[™¾H²CÄbNsáF½ÿÃnôî¶}•ÌUýéØ:8rH¦w!ދ޳1¥ÈéÔ,Ø9ífªŸ€+{_²;‡å1òb4öûöâüQ>zœGícÌé„ZÍàÄœ†ßVkpU‘ &Îs¤Ñëš—ö¹€ÉgAj¿Óîy^`¶‰J“ß äÅGòCvà‚¼!nÀ«é˜»þ9¾ò¸ ízðþ‰/Ñõ!Ñ·nÁ­»'ñÞ®ÕXU)QaaÉžN”3lâßþ€nü¤vå#p /g³µ‹0Û6 ÂZ[Ø#þ3¨g¨“û¾Ÿ½¼o9Î Áޤ;pÅ3—˜\{ÎÌñQ#§¥à= ¯<Û5ÀÔ=Ùx&& ‹ý ØàsO˜×ݘÛv`ÓÈ_8u>´|ñêŠWÀáBŽXÊ`©ÃIº÷ö9fÙ2Fäb+Ûõo‰]U ‡7~†+þM0g‡>xz÷Á"ÞA\™uqÕhqÌ•¸ËâáKÿéÃséó+Ùœx‘¯x¥G™¼uz÷ŽŒÀ27Eº¾ÄŽ´©›²þ#¥v.êå³–­KPjS,=mDoæ½Áo5Z¸ã¦"ùíºÌ¥|hu¢"MP£»&RiZü8Ý€£ËvÃ÷î<Ð6ärùZàÃ-"|ë'«i(‹6üו€¿ƒ©ùSﶤ Ÿé¦ïÙ+ÆPüWh†yU^«s”[ >w¨b"ª%‰×Ììó¸¦<âtV°-\SZ!v-,õˆçôRHlÎ@—›rd\ô"[sÖú0øÐEžÕ>fFJøIfj +¥a¤æ3°‰ Á]4™Ç[iØÉ ½2¶ÎĦóh¾\™.ÞV ¢—žâÎ )êU”ˆ§,1bÉpz²š:owÆáü6¼r¨Žùj~ ËâÒ¹‘<·qËë«Ü ­r||—Îä=Ï~¸ZÑk–€]änãБ˜Û0±Gò²û`üw2 Û'B“cñnS¤°’ÄdŠ4¨›‚ëãáð¿" Ôë‚s7º™Òk˜Ê…¹°KÃ6_¦DBS WÝ¿1R]7`››4½œû ®$¥2©NvܾÔ0öí¢d´Øñ‚»úh ·nÖæÈ Úxÿ., ˜ÜUGq–¯h‰Ìežžm…÷uîxã÷2z†5ƒé—°¯êóÓ@Ì{ÃpƒbÐùÙ4E~&|=+OÄF SR'þ}x ­Y«Ó«ˆÖû0<ú)ƒµýÉ,4ŠbûL!M7y92¼8Ô[ Wqgt?`'ŒÈF‰ýð¨w¦úàÙ* ¿%A ­R˜³·Ï@ÜÓÛ°*²>…¨`êå8Øñ 1î=ŒGCÌO°ŽÒ1Á˲ְý^I?؈u§é…j¸ñ². †kñþûT,?˜Ë™ª¢GEJœh¤{6n’Ó§«ß2Ÿ·^D_='¢3t“s©6ŒFn³Æ&¸lì£b¼ŸlÒ‡Œ0)œz ¯ì7†9 ÒToÏî6©Pæ§H2Üðdo>ÝK> âÖ§˜®F÷Gd°Á³Îâ¢ì(4'Zî¿?Q±ÍÝ(©/KÓ„wâ–Ï:$ÛF>ïÁÉ=Áõ®#ùa|…,_a¢™Ù›†¦Pdá §¹ ð¸xy÷È ®^œÎîY&É7Шi²FÌVduü!Ûô+^úK˜ÏqxÁÈ•g(Qãzq¢±ƒ‡äJl¥S,1ëS:;~h#3/ù“þ'Š£sMÊÍàçM¼`Ï>mÂuK›°rËVúQJždhÁÆ3°Rø&Ûx{쯷OžÌó‰ø%¸ž†û£À¹ˆxÏþ¼y‡-Lý.^'PâR^õ7¢mb2ä‹[=hrž)gÀÑéô¬wN¤ áÉó£p#ç\d]í’à³C»I‘úÞSf&f%1CæÓ ÙÞ5s7àšè(—§LÖ'9øç+ЦQð8L–mYAk·0fRN$8¹››³ËÖÚà û'ŒÄR†ìÉ"ÊQ;pšØ|";”‹RE¢„¬ýαÉuD‹r;ü;ZÅ>kµ‡¿ý·péµµP=_ÅnT¡ú¯ä.÷ó¬#¹É½áºÝü4VÂ7Pźºµ2ÇFS¸÷÷D‘W®ó8ckÃØ»bÜ‘­DÚÏãGo„N¹,ÎÖwñ~–í®ÂիĸF—c@œ×w75à¯owðÎö­twéNüu´Íz]²á–¼ag·¿ÀÓõÀxß(÷lóFv6t9-&y÷’I™ût2¶{/5¯µc åûØK²JÄ94Œí1¤‹ž†7w^Àu·6ƒ[ ÔÕ=b8§›ÁN8z’‡D ó¤V ‘…Æì÷yeXg›‹W™²ïggÓ ƒö;ñ¬¯ö0‡fJAµ3?üz.sdIÇÐ&x³cìþ©¶4aÕqœóá/ü\¾i~Q‰Ç8/ r~ ¤£§y7Ê>úcÝqlJ‰«(‡ä¿|T`ÑchØeH¨_Àww3ïßÕ°Å »0–svm¶ÿ¾‚+Õ«Y<­Iý\Ó°m&úí8KM„éJS}¤ë‡Q÷C OpE2ãØóV²ð=|–·#óëâ¥Jìšås–Û鵬Ã#uAŒk‹¾Åóð«ù N7$VKˆˆŽ$y•PÈÒÎo ¢ó2"ÁKê*ì2û3Šˆ@T*Mt'¾ÿ†ÚuðAõülpKåiöÉÙ/LÉ#,J´âL͇ÌåêL|3e'{çÑ-ŒqÚ‡gx§À–½ ˜Á‹ Cúu2ö3³ù±Áh¨©ÃGï÷Qñß<ÍÏ«[˜#o qgÔ}È癌±w/i©TÆëó[Ù×ß’èÛåFtê‚þ"jO‰ÐO7>1É#ñXÍLb´¾\– 6¨‚‹ókØÜ{mþáøè)¸~ì 쾺—묂)yÕ»sh{Å\XvÊŠ)ðk@=†J‰WâíÚ[à5%™)ýú¡+í,”AÿíœKm*Ðþ3‡-üËJíË`úlÇpÚB~ÈUÃä}Ж&~ã66‚¦ÇZ²}3C×ÛÁÂX8¨„®\˜£@¡mï?ˆF}ÉThq#êÇñ| Q[v¼ÌB¨ÒÇ\ò–ª@væKˆ-IÂ[ +áUÙ(üšo?mf‘— 4D½tÚ½q8OB¶‚†Ãæ— â¯ÃúäFÚªèõ›¹Z¢›%¶é|ô|gÌP»É95û(š]mÃâd_ô»¡€š=üäýVC9ðªç“íËàs®VF7fiŒ)~ɳ„²ÛûÀe“òÌäpvÛt²·ÂÍ`ÿ¨.y‰ëp|¿-ÉÖ¦Gr–BHt5&;û1Yú×p`t5î÷ÂoÓ0²¥Žôú³X˜Ó5‹üL^NÊõ=ÑÛâ07{Ã< U†ïwMÎáU°ã?ýîô »¿=†Ë&lÿð*èãôÍ7@_5 BJ¶+éùô’-ÿÛ-¿ ±†’etÆDwTsµÍ˃#ù PØVY+P1æ ~|‡™b·Pí¾MSˆkÊk6z¨7_‚¾±7Ñ9)ÞŽþæ¼?TÉØ­T‡â9Óðçèu뱨ìÔ[Fçœ[2¶EçˆÜB?,·!ú)ß0¼$ƒÚjãH®Q Aú®äìÍC˜x}%»|º'\ãOœ|™Ì@ƬXãöýÆŠGªXqˆÜþ2Ÿ¶(P9½äJ×:|Ô,ò1û¿%òéTË~‰býe^à׌8˜5ux†ªAuå|ô\œJ±¿°åÔæÓŒ¬Öæ*æI»ª¶á½êQönð*fñíK'[8çE’÷&C§ƒ2íø›Ý&˜ Åû̳¡Ã¸$YŒˆûÈ€¼ƒ,yi2êì¼Äì¾µ’ÆË›‘ qÿi¸­ Û@5a®t¶¥gh>¨Ç‡àŸçðY2Õ¤k÷í§#·5H^Ä:xÕ´Îh6éùæEž}O'%¼Y6á$~óƒåk’ +§‰Œ+ Gè\Vi‹ ¼ÿ‚‡gàüÍ%LÛ[ПõïÊ>ƽ»çâ›W êQ³ß¤¶|»Žø¸£<‚iQÚ0zù7~E…Ö´P+…b²h³5åSïÀ4“>rdÍV²b×2vÞºûTôÃYø¬8‰3cœ¨Þ¿o¬ß{°.›‹ÊÒŠX8AÁsÊTè=ˆQJ!XÆA¶kËàQÍÎø…~Ø¿á¶Òa¦¿ï,;h¿†Œ\€X£©„ÓÑËŠ6é15'nƒò¹Øòzͼ¼ðãSze[¶6!;®±’,‘·¡k9“eØHó^Ê»ê"^™žö­(læ±àn[ò¬(†d–‹“ÍÀ^˜V—Ç^an:?sTZŒR4I.›Ë @qÿ'x.¤@L'èå—˜Ìèttô—&Þü…X¶å ërö;:IJqŸ&ÉÒç¢hþæó÷¶8yì:Jü_Xˆ0üÓù‘¹óè.?wyž ¿Œ•°ºs2¿o|Äœ`{ºæÁl_›Ÿ Ó¿¯j8‚—|ièYü¸Ç†<ðÊ ›Å0iéWœc§Û¬8›þ§}6fè­ÀM$wñ5œÆŠiëb¯U/x¾x ¥- Äük?ƒ¯_±›â ©Aæ"êhÀ6›Þc÷_赉óH÷¶`On&~™çŽ~.òìHõTBTˆW‰ø.(b´æþdE>/ ¯äÃÃÙ™¦ÓH¤×fB4‹°åª©už ÒÆ—þÓE3¥ŠÁ°RBŠíô«c\dQhdá÷©…רí®àíiL¬LÐ˳Hqk/’I<´0^‰É\ p7viM!ÛówaÏÄTÔ\ÌCD+hÙ4©þtÊJaPwÇUý~ÌÍ„&tÚ—ˆ  M×ÜVg6¯9@JœÇ¡ì“­=.I›fZÐQÑJ:éo¯_E¦ËÔøØr\• Ÿ“>XwÎ嵫‰\âuΫ/é0gjcª´ˆ>p‡Êê\öHÄ ”0ñ{ Öü ‰q¨ší~ðÛdEG/w±Ê5‰ðO2¾é×ysâ\Á‡9N| ¶g:i‹¬ŸäȦ,°Œ.å…Üò}(©ªO%ó„­*°ø2.²¥äá{{6&áþ”¸C;‚Ã'’ˆ ŒUðø‹Uàž ÉÜ—í‚¥‚`Öy7΢u‡b!îH+\ïëÅýUóèpíèú}[ë@„“' š a ¤- ÆsÓ”±Ù3^ør'ñÀM˜yh34­à'Ÿ½gѶÅ*ôrÈ ´ÑÊ«±(äÐ}ü%órd+ÙodÂ?œÂ¬ññÆ‚kx?”ˆäxlÞ×ÎnŽ‹¡êÏãO@…V8Àhõ'xXvò‡bhQj? «ûôhÌ<`ö]Xбú´åËÐ †g‘-Jò´3!±ÙÍÁ¿0±K¬(Ï·çìpÑ,âó çɽ©Žƒ…ÛéÑÝálÜ¥<š»Ÿ¸~£?dWá×ÇcMÜÛ½ðξñT1s ÉáæÏ´ià´¯…ý´¢tƒúACúzèñÐ^n c_1ƒVüõ†æ#­Ø½·ˆ¼üúºƒÓ òµø"M•$o€ÂK²îö*ÐôZI¥ÑÃSbšž.¡®ž0;Šáêð:j.|Ì !ô÷ðä3¨×€Mž’D¿µÅµc`dÖÿ,™Á÷  KHŽÍjúŠ&W¡´s ×sÜágI6WxA sgV]sÃ> ˆ^<‹Jny†E:ÒÔß« mÕ>`lú\ò9téêæýn-’­° r,YQQcü¨Q³í—ÃfÀM?GÜî2ÅF„Ñä%+^ö³­·Ê±pÿ:œ•“Š<ÂT]ð#l©83ºã·4SFgÃÏ4j¥Ôm&ìØûÝ•49ÕsSÿïAz3ߘ­›ÆÆƒæÇwìûK£·˜5£Ùh8ã:Xúò^ëA¨-â%4‰l‹åÕ¹Ÿ¯kÀÐR¼(„å>paîØÑœÁêi©Câóøx±‘]Ä× Eý[$MÂT’ˆ¹ Yu} y Wó´O³/&yÌü@=ò:£Ûþ"ì ŽoÞµÔ’$î2)wÿ@êûrVÄË—^L¡n– 1HÈjk²2üõw;Å%°UÖ¢.¥0ïï.ÃÈaWÂH/£ß»$¨“æ]†Ÿª ‰R|(ÔÕVâIñï˜Ý”MV|É/¦Wj‰“x7§3Ñ–›«NÔ àé§_˜ìaÒ{í.º8¾ÃóžRÄ*q:ÛÛv•uYñÿŒ[Ñ÷!¬Û5Þ&N~$+“µW¦SuESÆÛFœ ˆæÁröž—¢Ö!^ŒN]5¸”*ÐÛZ¹Ú;ae`!üìKGúöÌæ’X·Pfm] è\éa“Ü?3ç§.€í‡â˜ýïe |öu¼ZÐ>¡*„Ç5ùøŒ‹%ÂŒžÏ|ögW É:ã†Ígpø‚?9Ï×g´/ã²€ÕxfÆmx_܃ÒO¯1V§Dñ ÷5Sùi“÷£€[l3ƒ`=_ZZÄýæÙË~ÿRŽï´3ŽÛI×íq´Ê¯Â«óçÓ†ê}ð»ùo5<¨áŠÌ9r;qʼ‹ÈŸ®3s'5Aèç´Îø*'ÂúÃx†âiú d ÈÒs[ÁؘÁƒi Ó×eŽ`è‡dæû ÝâÑS¶«Ð{CX`nDöŒlò#Œ:tc1 ¡jr«©ÆÇh4xüÞ•1óÊVÀ'/šïªF*JnaÅóy ³ÆšVÌs§'V r¥'ó3¡D•¸¡=âÉ$vÑ+ƒÿôÑLÙtÈÉò"¿c“à[A Iø;>ŠŒ¢÷U1޾3½™ü`:Ý •ËÌaÙðÅ…°E|˜í»eŠ~;¡›ÿ$Óùº .éz@‚´ÙÓ¡ŽaóÃhº*0Î+‡»ëîáêž›ðüÒU(2<ËoC¿™^ð”k·œGfcœökÐ ’¦’R_ØTÞaÛ¦‚[ŠO ±%ŽÈ4ƒó ×ïI!—¶Ø‚í·É~73˜ÄÓuàj1ÙÇcaå¬ôv¥ëÃCÙoÇXñ$3P:«BµÜ atÛ)öÃÂhbð •ý+í†éuѰàË6ðú)EįôÁ¹bó‹ŸœI™ Çä”pùèòv]6ûãs.ˆ¼e·Ù(A€ó6\{u+n bÎê‡râ\”ñxè5z¿o;qHÅÇkOBàçeÜõÊQËïHب‘°ö.˜ø» =8ÜÉz>ˆaïɉ p——£ç·©!óx%UyA\.,¥‹CùÈ©]¸Þü;>:;•.Ž«a}8 1E®žî·RÍ„¯S<ýÿâèÔEX¶Â™ŒÌe]0‡FÉjÃÚ!-öÓ¾zºÀi>¨B4´ÿ€›Ö 0üöq´Å(‰¤~ÄL2ÂùåöòÛ‹‚bo ‘Pmdg§5¡[]j ÿ!cªàÁ‰'+EÔ€#Ü £JåŒP· 9ZK¤²ÂbŸM׿ӱøj÷Þê7ð„g;sùò ìÿîIxŸgØ( ~ÐÈ©k†Øà\fÑ1Câ¬7ž(á †®ÍÖÅ— ïÚå$S³‰ÔÖ§y^«Éù'd½r%rà_ =–P¼°–k4Ð9)ìÏ«MLK‰+Öz—ÅÙoCA\xNƆ#ÒS+1ñûGÈY™Æq ~”çt&žßL¢þ(Ѱ+èK{s¼ÃG¦ž\À’ubc÷Ð]šùäù¹"»–¿óâ†9#¬ŸO8AÎÎÄá&Ö%|ÏX0õ‹­wîQ€ãã^pû Ç7ÇA‡pSBú¸§/éÞyµeÓ—øD(Š¹îø„ÉæW€ç\8ÿ¥–ñëõcß<Æ9ªäôÔ½Ôþ㈠ÙH,X *m¸û<ÈÖSÚtó×Y䎮ûØOŠx/[@ºó@§iØG6„ciÒIÐ ªÇûQäX ~Ôoƒr!òvA¨uçoÉhÜœëƒ3úrat°F›4èiÂ÷£nØ‘ÕaÄDó8š~pÅ™ë"ñË»vy£¨žÆgkÚÀ¬¡×cx™¦cçñ™`"&KÀ¥ãºµM†®ç¬…8ËD 8« iCûðøOgTó>ʦàó_EøŸ¦Xß/4%C˜—½SX<$°Ü«þµcü;¼¤JbuÖ÷vCLk“$©ßáBfé¢í;€ìío°ëô2¯ÏsWe×ãÊLURZ!ßT{U…5ˆ«máv¸Yéò½¥èçy<8îq$Ì›ð6q@ÛãôÞnÆ®&î– 3æ¾bg8,¥›U9ù¬_J57÷Àíx/¨|…_öðSý›Ë'ñY”k¼EÁÚ(Ö`þuølYÆ,tçÅæaù¨ N]oÍYìu ¼àzÂg¼ìø1ƒ=lñ wßbnÍyGþ4lDnð'pŽ0&i§r`:ãŒß/ñQ6½—;ØÙáeÌu§µ[½}/í¥dz;=È`¥jØ3—`dj?\\¶”>PDÏÇñø¡|„ÜvÀ¥ÓôÜá Ð ØE‚¶åR™À$]¤Œ”,„hA êy¯,“eaÕ;iR¦,J§KX‘Gž èÚ@õŒþ± ÓÉr+òÛØî?Ž­ëuÈ©pñØz:¨o ìßTzý†6uί£A—£À¹ji$])VtÐ]¦é»7_ÜQ¶ß‹¦}þ5^3áä@;½¥`ÊÝdr’ët)žVü §OïM'+|Ç®çðHŽ+w=[‡ROB©_@Ìuˆ%·IËÀ.êÖϋۢtØûîrôàÊØkàCN¸\Ç ·qÁðß´šÞ“²z¾ã(ãE)çej±. `J‡6SÓ˜™ ~~éÁ~ÈÉ…Ë!ÈÆDB—JW»DC?‹Ó‰Æ+˜õC”œ Ž'»Î=aš„}Ø ò&eŸÑšÒ·ãé 0KøõrèB3+"‘ǵ–xN¼O«`¨µñk;JßEöÐÍrqxýŸ%®ù8—œý¤ù’w=<Ž_?]E… 92öú9ÛNþâ‹âQ\éÆŒ®¥•»«˜u žôØ1ø¦µ›ü:oOhYÏŸ¶ oªf³çóïìž='qP}í/D1ÛÇ—PÝ· Øa1#Žná1«u¯gc«c¾úØÏDÿ®c¾f>Åô/ PÄ+ »øˆ…èðä8#«·G ™¿}†¤îzÇüÝU\rSƒY£ƒ­²ìã«{±k±ÍÒ%±> (>©Cïjf}W¢÷ÿp‚]ˆºÃ,â¬ú eÞM‡uy¨›@ LÙ«ÓÜŸeÉv§Mj ¶Sȹ.7ºäâ´eÕ“_úß5ü¹¿†ø¬ÝÂvúPN” ˆ±^ã Ê·‘[öS’ª² óèIc=zª´¬fîgƒ6زƒÍwAÛ[œ¾_óŸ÷3¸¯ë…¶\Uò¬Áž¼éÚO'fIãuÞdîáÛè¯âE÷¤yâÉ) ÙE¶ìH§Õ¼¹÷hÒÜÄ8"ÞÙó‡÷àÁûC(°ô"dž¡žæd@à{l¢»m ÉÒ3Õ8S^ Ÿ@‡21º<á®Ý7•¼us¦öÅ50 °‡^>AËmݨp®,YçvžžÜ‰&í¡8»¢éúŸÐäw©Ø¬%’êØÇãÆ6?”§uõᘨ»Ò”ÄèûÏV˜Í'Jr ÄàÉ{'Îm–ùÏ—Ù.GvÁ"ÖÈç~X¦_Á =þàþ¾µD¯²q§ûØ+%S±£Ù«giÑximj¬¶—îÙðœ=yyŒÙªtÜ&æ¢ÝnGܹR^šÀCÛæCpË z}u·ÄÈÕ^ArªÞ–HÑ-Ÿ0lÞ[,'<¡É0-EÞûòcãp0Ó’öœ9y+?fÇpöm?ÇhºæÂáÚµ¤HS=/ÝâP«3Ío4/6;’£¥§5‰rÍ+†/܈-õ‡¨yôé‰\XtM‚$Γ%`ëD#Œézå:Ib±z²^”êàÞk! °?šf¹ÄãÓœqV;å:Èóc{ =¤Ë™Ãu1*Ì•#;Fï¢aFéá§…ßÓáW¨ èDlµÜ[Ìð¢78|N…”:‹v¯ØÆsØs¢gh³á˜8,kB÷zôL¹/j„ëàƒé™Ð¯MìÖŸC½ééðG|+Z>¸­fo)Åq >²,}©üfAí#ϳJUìð C³è}4/¸Ž’•\¦Hí+rAšNñßBÅr‰fÁ,²ù~œç=×¹ô¼“´µr•¹4Ʊz8Êþ¼íJª\ÖÃò/²I5j¸ ÝýI”c˜3_Ž…Ð@Nøy»$å©„Ã$\GoþZI_ë×s»(õ(¥ÀoÝÅ©¿;ÎFí!‚£é»o±xûƒ,™}8öËͤš•°Tý6ÉTvC×=pVèy*6p~åHÝ÷éDûž6 ¶ §zÇÞÐPqÒŧÂtœøˆCßHb#­Ü‡ÿyGOUçÁ„ãzäã) tùãG´"Èݸ{˜_b@ŽЩ}…ú¥ë“ø'kñÊ·ûp¼`)¦Ý]‡7¥ ÁîÓ5ÆÕ² ï̬Åg>YÌÀÔ¿°ìC"])VÐGÚëÉÍY/0ëB30€<²˜w" 4˜æça²\¡Ü’‰¤Té?(Æ^AÕ]o0KS§QE«$ªa*. léÇ_Ì^kÚuD€Õq^L«z·“÷ÇL©.5%™cïÙ¾ÿ|ža±ú9´RÏ*ÆGÆeö“»ó˜oÇ[ ç ?yò•‡Îµ¹Æÿå€a4kăޭ¼Û…çS©NM2TŠFˆel3¹$M’*mq¢¼nrÄí²? {[ìÖb’òÄj¾•B"»›Ã¸ àXØ"Ü×'€QÝ™¸HûÆ1üâe4Ñ ¡6¤†üF—:=LTëÀ|^[-óÞôÏ„{aåÙï¤9 ·ÞŠÖ¡x—%üçé|);Ýâ"ÌÆ¾ò³:rú© ªjgô·ì¦wÏÅôÚGØ6,Ì.¼u€ÉóÑý?™çÙ«´¨¥T {{÷Ú6Ù‡×W€ÏB]ruZ1Ãï%Iý*΢@üLòìÁrº·Ê•ºó!…³ÅhÿzCâÄŃòÒ4¹ž¿ŠÙèúU…žÊ°#c_âi’øúŸ_sÆñ×ðBjMç¨bÈM£‰ó’AEê.¼ãl¯îâçT!òÞ¡Ü_gCt¦8:INÜ»ÃÖ”žÄ±À~XgüW¿,ƒKåñ´÷l:§¬ï.¼| ŒënxR‰rUº\æ#ñ \¹i0úÍlŽÓU!k‰­É.Δ ¼Ð}3 Ô†äɨc÷’±6nvlƒ²9ædȤ”qM¨"iÝfÈså;$ž)‘šýz µãßi׫˜¥—XæØÀ2ùÁ§b®a“dqÒë³8Î/P<¥dƒçÁ¹šj(°íbò¦iÑ5,Û]ÁlolD ©©ÿü)ûÉéw* ävŸ9>p-ô³ÁD“$ ]„K—ÁÒé;¹6,cI;’ÀîšV HGßAÉz{æµH«v€‡JæÜ‚ÆÍøø¨:|ý  éüUJð¹v-¼‹áž¯!8iÁq› ª-iÓ;È/öϺûŸ'5;ó<¹ÓÍOOú»¢æ” Ø»ô;Rl¤ÍvóÃvhü<[ð· /³oŽüàž7¸ƒraßÀôöSއf#«7jIóæ53î…×`àf#^ ˜7kBÎ7}ðÑgn«O@ï•“¬ñA4œ;Žó¾®feËg‘#3Öá?½+LuJ3Ñ¥áÛÁÌÎ!8ql?ö½°ÂŒâè{$“ݺóZê<¾eûà•w>ôŸ²Òû8ý·ÿ*ÈæàÏ [âUWƒÛ™¦þp¢—ØŽí.M,«N6*pGo™áŒTWòÈyœ[I5z…ýcjëzY¯¬aÜÏía›>¨Rnô ”°o†Ý¯™/sm`sú<¸8o×ÃwS÷(¢ÌN"#÷¥ÜòØ[5°èëL7ÜJbd>àÑßzè±>=²RÙ`A‘ÙÌæl3Ã?‚«íWðòw°é™Áxò6ãèŠsøŸæùÂ÷÷w}þÓ¤%ÿÞC›}û‚ÿÒ¦Pœ¹I’&uiãÌŠ§X¢ÁKtRªØû‘w¼¤ÜOÙÉq°e0 -,Â`oUâ|ø c¡È‡ï'šaµ…Øìgdå Á{ÿc|ýa^òR±GÌÿ8¸òpª¾.lžÉ<„”!ó”1qÏÚ•M”„’$)$ ¨Tæy.¡Dd¡¹¸gíF•$E“’Q*•Òç÷ýqŸsï¹÷¬½žsöZïû>÷}–û¶z–½p 8ìo»h f›qCz:ŽcÓå F¯ñ¾™ÝÇy>q. ˆ’Cûåð^lß÷cüá`Ý  M?U >G–ªÓ¹—¡Í³ËÞéK8›ÏÀòµª4£WÒ·Áªƒ§º+A*D¾ÝB¬ÿ.A¦ø q¤†{ ±WŠL?Žï6哘uqQ°3kÆsÞAlæ!ÐmÿŒCRUðRÎ:Ú)À`åY\X!E^ÆÔaÔd¯Þ¦¼ySVeÕ]æãŸM¬ü]2ØØSk¿Ãþ˜Eôȸ¹T§J2ê¶A·W!ôn`q§°ñÚ#FÀ^™~=’›ƒqLHžê>ʦ¡HÈ<-DÆöϦãcðkqójØo§žƒäâ ŒÔìƒ8>Ï –ž^Nºs@„„ñTõ'ÝCM”Ë.£–žqÔþD?¢èð)nÀ™¶z´Ìi#bç ÜÄK¤†ÏRõˆž[Aúkı³M‚<‹)e­÷çbÆ@2˜ä¤ѯ0áþ0w…Q ®Ù­ÙÎôWñIðÿ#ë3J@»3›¡?«àPÚ.´ÉÚJFñ4çÝh þÓ[K=\ƒaô–0Iá†cÐS>Ðþl@>}šJ—ïØI‚­æàq§ù„e7‚÷#"ÂsW=x†žbúÔ{·(M»€{’º¨óüùtËòi4ôé2L­ŸÃuÙÉ"­ìÆæûÜ„iK‰7/¼È .s74d¬^ƒŸYÁ$ïk,ÃmЬÏŠ J˜i#k`ŽröÿÇú<‡uë¿¡ø÷lÎåÛЩcNïûsh‚Ë^~’°+…öxû°=Ñ íaÌÑQT Ïf;a“u+Ütí]Ÿa®B:†WÕBc/ÉS“¥'ð ûÖÂM.ý†1§€³<†–4 ƒd¢6¡üÙðaoÆù7¢¸S0jïÐgOoÇ·þhe0 ¯7"òÚq¾%ænÐ%¬uj‹4zúÚ.iNoö·àóv=xo‚Óùq§4*“î‰tÝyMd[Æ9Y.ÏìýÛ¢{z°ënh+·õqæ¥É Ù°h]ŽŸ¼Èªy§ã`Ã('xY9ï'@î íd¹¶õàeîk_ïC×ò0õq6¤Xû@^m;û±t%47j㌫6D{Žã×Êe²CÌ©É,¶b÷/Oé/ƒL¿«)Õô.v]v: «ûD뚘㶇Øjý4gîáIe@¿¶²rO˜Ä±4웃 ó9¼+¾¢Ýî$¬Ý+Bب|¨±™FîÍ,£Ã——ÐsƒI(gš‰Wþ–©)Ð' ÁÃâeL$äN`eT#òn[¥´©‡Ë[ØZù‰¹±p™¶°uÆSŸ·¢QG³ô´$Í?ÝÁÙöª\ˆG¯÷÷QßžP© Nš­M”/Žpz´³y¹7™%ë’uå›8pØ ´ŠaÊîW dˆ1;1ͬ9ó°Ý_CÒKpÞ> rõBÿYŽF6¸«;|üg‘²ÆDô Ë4ξ[L³gãÛwFt2s«|-Õv|Æ=äÿ‘Q5²ÄÅ;GÑÄtˆ{¶uwå쟨¸¶ :Þ>GóuKñú|òϦþî•Ìä÷ÅÃìÆï@ÚvþÇ<þÐàƒnm_aîž ÈW_IBªn²+‹’ØYípdM1§êí3¶PK›N¾ÐIBœ$aÚÌç§«åb¦ÈàÓ/Fƒ5.ÔÿÚˆýPã ŒËÓ£‹ýK Ç¿ û‹UiîKÏÿÏ^6½µœÖžŠa¿ìËî1 ƒùÛô©ÈS5òC´{üð4 b½‹ej3H廨ø~ù³XŠ5¾ ´â»#ŽQ'jjÚ„ûÎn"gCw÷JúÓ³þÜø ¹E&Ld£5øýó"kV&A¿Á?Pú£ ×W(éâLõ{LOÛOFkÃðþ—ÅDj‡¿¤ÿßgÆ9w½M-0}~7~…¢ÚEðqƒ :xb8¹ºdñ#yÈ,ã!Q¼‚Œd¿í€kµ€]²Í‡¸üL`6No†Ä÷7¡ö¡<ˆŸ»†{¦EÝ@kß.„Wâ6ü7'Zœò°¢²‰³õm»U ŸF/#–õòÜ“Oj‹™^ø¸E7·\„ß1c˜ü±˜-±¯dkÂÓñ‚ý1¸ÿá%LÕz†ú6ŸF®ÍéΠ3¶•M½¨ O^&ÃÞœcçØGj.“Á¬IÉ‚½‹8Ug¡ð†é=pgü<ÄÍ«»Ëvˆ&2Us^‚ù“ðãåUt yÇÚ:q`ïÒ{¬¦.²K9‰Dz‰"ØÊâ¼ß2ôr×B"ÑäA¾‰Íǃœ;Ìß7ùäºïØñ=Šë­hÒl –ow>g5Éþÿ<èš•¢äÙn3zã=qÖaPF€8ou©Eú8!¯öõ1'“¿£2/1s€¹eµ½Ø4+•#w]…ÐG|¬W«ÀøÆÁRÅeyÁÊJ:uÍ!8YŠlš¸|¯xiîéT´Èvi\drΞƒ¸%Y+KöQM:XÀXlXIy6|†–`u0+={ç.…²)¸Úá lõP„W‡˜þP!a9þE:ÂYìVo="¥$M#v§Ÿ—ÆD&ã ®€Ô¤x2}±9¶Êƒòø_ÆÀg&xsn>¼òò£.dAÊvc$ïÅèÉÏ×ñZÀ2öÝoU°_¯‹&w*á§³&ùÊÙÖó c‚¤Û¾ž] ;š/ãÉß„êD¼eâKÈhK1ø¤˜Ì2ób+’кËïPvïUºnOòí$˜Ý‹D[ñWäÐ\TÔ6¦ÌS”[º¿÷‡]u¬Ýq^º`f/#œèÇ ‹G%ÕæyÉŽ©ûCµB0v¸•¡ $? ^´ žur[·ëÐ:¼ôÅé\¸VÃKG>¨1‚5pSq ^žŸ{ Þ@î”K ¾×€¤éSÅE±|+žT~ˆAÑÌ£@VÕãEýñd®;Yê”AÇU2`÷˜<]Ù¹o `æ=Ùû†«¹z>öDË7­ŽÍ¢™½¸7}g1Ê/Èi÷UTÐ,©£;ö?¹ËxïA§?želîwìm‰`ý5ñfËQôÎu+Y Õ`(4cÁz‰µ1¦ÛTk`Ù… v˜•¦¥ŽäláZez›iÝ3÷›¾Â·)O0ùÙTVôÜF&Ó?–yU‚‡SK¨Ïü]X>g”ýYqˆºŒüå,Ú® Ë jñ¥}ûûu 5š}€ÁlÉ’…ðiE01š&I>$N^œ:I/&3L=˜ì=‘˜idD®~ £/ 31±¾•†Ÿoà¼ø<8å²Tûœ‘Y¸œ ‹ Ëî ¬Ü¹‹ôz«àv>1’YÖ½]Í|ç©o•Ñ $ÏÖöa±ÆvÁWÚgM”³†˜›åfÌ#Žgˆ´ÎΦ•™ºt»à6fÎ!VòÀT²{ñoVÉß—Õ…ãDQ"—ÌP I/ ¸hÑwQÐ3ÒƒÝäôXW»qž™IÀßÐwà²Ø„dïD,êÛAMö%g –‘µ ¼7ë  ÑnaOŸ¼•v£L‰ÅNˉ’ÿú¶.X¿Üw‘Öíøõê<1¦zêUÐ\Z¯§‘€- äNbZýOÀ×7z°,x?~]!O£^.d­¨Yj˜E|Ý ˆ]€±ß?›ñ˾ÌMøx t+aÌwRÿZrŽŽá»ð$Öx¡ M™J?Fý«FfîèRxÔÍCÒäe8é]1`òã;GýQ<^ã¦Áé¹™ðÁ¥¤jîüïœÍ6mÐÎ$·Þ6 Ïšx2\2— fhÓÏU]ìLÛ`bV—øœ\ÓìùPõ®$YTv[,Ï3×ö¨£\ñ5V¡šo‹rp·¼ZŒ‡Âä‰Jˆ½7$ž¨ »Í†8½ýZ ýû'œÜhÈ)šªB|‹Åé«©CLPM.v°ƒËѶdý¦)dõÂO ôØž¶l˜‡iBÏš Xðô78nyÌú¦AßòxQ{ Ö ‰žü¿_C?_Ú.«g2}»2yq)ÑCKWjÛ¶;ì‰ÔöÂÛGp0o¶7ç°’xéÄÌ~­@‚¯†PÛ¥[X>ÁƒÐ±¡O´—r^MÛ¾Ãìòê(ÜüŠŽò«Q­¯ëðøH%lZ§G^¥oÂï[(l´8Í^éŠÆ-Ÿ½ Ù3&¾mÇëŸV²oÔ  ]1¢y= ù ØÔbäuÈÁ½Y»øÖ]{>œÔj먇ð}øün:ë' @f”9R'½ƒü4’‘ŸÔ*2‡,A|¹^o;ß–-×k ¸"຺ҙÅÞTÝÚ”Îò(£Íy¨èà?öHw¹Ô=ƒj©eéœ üù”oТb;Pmb •·£Q2‚„ªÙÓÿæ7?ý*Ú«0’Qã¡”H ÒŽšÜÉ›@ó-žTiO:ý³¨ ßM¡£[äH´“49«¬ÁȪ£!4ò»wQñ]ñt­ÕyÜ•¥€¯œ„Ž–ûИæ¹>‡¼ó‚DÅ&ÂÎmþAװkùh©–ÃJãF<~h&ó·v69þÊxõ"Oïr•üú0 ]<ºù™æ—fи-šÔ°Ášèèo"Þ¦A—’ĘՃù8aÆöóÓ©z^¥î²»©u+Á£35¨p²7]{ ­Fvêr=”okÕ¶µÂœêzhßeÅ\¹?ƒS!Ù Ÿül8.#ΰ@.þ 3ëÅVBÁ£ÙÔqKsìŠ]s«?ăÝ+A<3ï8)þ*޼;b˜¡ODªZƒš¹-ÃíçøašÜ ؽ`¢µˆhÑnº·)£³¯=¬m€§yµÎâ¿€3ýÃŒâ&²ŠÍ3‚/—|éçô¥èèWûœuHiE+|zPÉ2ƒyý]ŒlÝ ³¹â„<ÙÍ&dÇaâ|¶z Qi[ *€Vg2CP jmæÓ¸Z*’…b*Õ§1€ÑÂŽh1f¶'ì`oqÝ·}sÕZ€h‘³2H5gx-"–7–ÔõcËRu"¶Ä„5|‡·‡LIFm-0òZp÷̺WÀ¨ºKP¿¹Ê4žûsEÄé;ëLÚ<;éF—¯•¸#°ŽÛ „| ¿ŠDí*®²ô'éœnÐÎ &.ŸØ3ÞâdÂô3 þv;,Åxz²ó.vx, ížwÐzÉy\ã°€ÆÎÓ¡Á|àkщ"&ôŒn2Û1±‡Dj¥Ž×þ Žã*Xó[[¸ÒЧì¿} ÈN#wùÄèÞ-'0Uø ½šÞç6‚×)41l;9ø~*Ùa̶¿‡~NdSa±¶¶Ä§«b©Í  ]èOö®všgŸ W_‰Tm@fjœ¶~«‰ñÑ9À±^Ͼއ¨¶fLx@Œó<¢›ÿZ›6ÐÕ“\Ö\±€T¨Ò{g‘jùUÔåm!´ÕùýrÌ”£¾– °Xk1ªmìAg™¸lj(Þñ‹‡η¡l•$®Cæ¶-C%ß\U|]ºŽ€6Þ îÂÆ¹Ë•Düadö‹ÐS,g±¢œ.Xܧ°•]ýX‚¬x#@6¦lÁŠpŒç,'ôz9Dî]O§ÍŸG2¥—2%Y¿þ0Œù¼€ÌT0 ›\ŽÃŽç8ç6­¤ÿÍn~$™F2÷~Å·Oê1²ú(X¼7=O2©v‘·LóÊùŸ°ùÂ,r“ÞAå_zlÇiñWŽrß·AÍÂåäÑ.#öø¿R*¯ï‰¾—’¤Ø%¨Ýµ€6(¨ãÙcù¸½ ,6ZBxd̈ñ¶-dž ž8H:áÛgÌL‰Üw ô‹è5|zuýùÇsÏL™8ÝbÂÏÐûÏ4è“.˜}o¹Žø’SНkÒATÈÙ!$é$Îf_/z7’/r*î ~¼³SØoêÓ 4pÝûé «Ä%W¦Â»zIR1õ!ŠýLa^î$Üí/ÙÓ÷‚X¡¹±ìÞá"tt=ˆ¿%²©î$N¬•ÃYx÷\Lû¬JóóØ˜¬lÜyeq“W@Ïo4c%/3S-­‘ÿê}ˆŒ=Œ§ÕÙƒõŸpåI|  ®÷Ç™=²4&KÛý¦ÒiŸ¾ÀM-QjÆ»ÇÞ2çs?üp秤?Šir§(þFwè½Ä¸¢2uùæ’gžÃ»l”åZ3§¶$² ؽi/}ª»e–DõDTH@¢:;òA /S#+Ñp‹,Zø<}öR±ÞÆàî×9˜éÖÃ^)ÙEŒƒ‚ å‡*¬MÜÏð¤åz:;²œ†=·zf–¾k½\€Š­ù [E*¨Cÿt0‹)&÷˜‘055êùއ”´¸’DM2ŰšÕöŸB¦k?GëÕ$ìßfÂ:•Øê “$Åup3Ö¥ÏÚBAÁüùOóDÍé—õëØºñCd·îTwC…ê..Ã>£8d˜…Üë©ðsé9Ô²½-ímo¿ud•;¹ß7â.Kã}Ò¡¬ü)'gZ–ûâÁÌ—lO¹v|$¤bX™\öIa¤—~ƒÛªu$îàQá5!֞Ž”n°.^¼3©‹ØøÉ7¹{؈¾³ÅŠÍ—0, C-MX9ý@6µS<Å–“ÌM\ì’õ'þÅC¦Êu8igJÚ¯~¬9Šäªq¾2›>8>¤F‰ÈfÈ™ÛÌœ°`GÝ5@§ü7~R8ë«àÚ„%ùÂWOêOXQu»6?–ƒR×NÐ8e'jcþ “ÇS ÿ@¸èfzþËnò ïr§E$ƒ õ…û…|Wœ3'{Þ>^ºØPÌŠpƒk'‘z=Ü~)‘m%U÷aþÃfrÖê [¿Œ ûÕÁ¹ù¹¸ûÁ¦5ÿ¸¹+ÎuY`‡C÷&¨Ð¾»Ì§a9æÉl!Žî P\H8ÃÄL@›>ÿ. ³e§²ÉlÉz‰«„¦“·˜üªE¤æá0iÒ;G§ÛX“½OLûÜUÄÖ¥üËèàÇj¼;_ÿ*¦öÞ2ïÕ'fVîIt•ª#·;j©ÊáÅð§6òœSÍY$dÝa¨3î¢~eâ—CüW:åYîñ¼ÖH~tÞ¤ úàqö<öMü'ØÈ¤³×?û÷$yY)>ŸÂºËRËäG´ñE9>à݇[b¢hzW:6xï¢ò•bÄ"ȾH< ¿y‰Ï4ZªÎƒ :u˜®jÈæøÜ&áw,I‚ú9,aJN¯¯âTµ/AŸYôóªçø™ïk­—M¿“¡½? È=XG¼ú²IÑ ì»ýÊäÐË%ôÏ]);|-oM!|–\°i°'yRñ}O$=¿ÔÇ~†‘ýG’ð¤Z)ìKÞŠE…JTÏ%°Y¡€g‘Š“Þdlæ$þ8ÈP/žé(ÝÅáV¯qfèGRp_¾–¢Ueù¯~/Û‡…Ê›A;@޾M‡wÊø*Ük”{ .K¢N­0*ìÅù©äe½ œ›•NrßÛÀ»Æ~lKϵ¹Þ¬Òòz¸w$Þ„C ¼F™þËaóæB†!kæC£Tì 0¢ÖiR¤Bü4Û)+D}~}Á¥KÉògÄ@l –ÕjR5uúêZ<¹Ñ$§!‘±Zô§ÛÐgÒ‰¾zºy®âÀðrúE-î*_Ÿ“&ôö× (§s¬eSâú`Ç r.á³\tv¿¢Ž ½Ò„&½{±;h™ä«\ ¨µ“L•:ÈÜîu%Ñl7ó,Ó”ø±ÕpÏØ‘ RÄefðÖ) [íàÝÒ0¶‚sÝgÈÒÂêèïŸ@Mþù²®]W`KÂNï_¬³8Âw”ÀƒE½Xa_‡NO¡›ðP`2q¶ìe=4/°âGþq§^Ùï~¬ÏjX³íÌ9˜¦V+¸‡]c”êGŒqÌâ¾LÖ"¯ªÒ™ÓÍhvÄînЧŸæ_Ä%ßšðéJŒJÂ6lfÔ£$©ÚÅ&XÔª•žsèÊòÔ®£º˜rÀHOX÷²ž‰oâ%šQhýiœçB»¥Œñ~u)ÍÛ €.·Ÿ°ÞÉdM íyÌ}âÿ§1‡‘u ;&v’)bö)–qÞU åð®ýiua–—ì¤espHò‚¹{ÂrÙRcÉ`[þ™€½1«ðŽ£ ‰Õ»Å­èÏ£«7Ï¥=gT©öÈS´PƒíI¹P1Üx$CÀõínüŒÕõE¸QÎ’nütcm|‡Øð¶½ØÏÁïqqzYþw—» M*w‚YCó@e7¶óš‡£mWqæŽLúÇÄ—ªNjþŽ˜¥vœ8FTúæMféÙ òN >ÿ9BKM›_\X|ïŒ Q)¾ä¡é$9ÅM^ûéÜõ ¸í¥'½õ]Šá}?ƒêÝÞÀšŸd-l‡°A3ƒîÓN!œo‹¨ç!X){„4®›É³iSµ*,/™MM¯œƒÌ—~`l)SÓ‹ÉÅ„KÔåE;seÊ=HžõŽ-‹À-¯Ç5‚þy³“òÙÈÁÝÿþßJô^•‚Ïxój… `ä UnÆ_­ñ0¹sà‘«1) Û/2ñ]~±¬7½4Ày™Ìð¥AßáÅè¯ÍÁyYrûö}:Œÿ˓ºSŒêUeüÄ>ƒ#BÁX½´†ý} |c.ûƒÉºQU=¬ŽÃ#ŸÕ˜ ûÀer?—ç‚5ðvóñËo_í#4õ³ê~x¼°# w³ÑÄ¥–-ó•¦ñ­rpéÞl–÷ýrR³Ê€þ=y• µÒ#o[FñØF}ÈI@¯³­Ø¸ÙŽ:},À#BŸ¸c­µ­Å²ÿ[>Hf0Ñ„noYC–ÿòÆ~YVúŸ&} uOAïx)¬Ù}}[ý²xk8këåÀDw!ûUl ÕÅ Tÿf­ÞYû¾àj±sÐÌ=ˆÕ Yò’âüqÄêYg6|^þM¯¶Áhz»}¡¹Í†,1–»ã_°É׆¬=Cí3%¯…Òx1È>³„ë‹“ËJtè„ù¹,WÄeaÆ(?î5€¶AwÀPô&žÚlMVœ±'w1’o Õ ¦0ƒô¿¹Ñ.e?™ó2—Øöº­[­É~§-¸Ñq=³SžÌ8TLFž‰‚‚g!ýÞŸ†eÁî÷Òlœ%»Õàv#n×p£{ù7÷ž×Nh~ø‘Pá h¶{*n\-@×Ú…U_µ ÚL·NëßóÜ9=¥ág *é ìbGžˆƒí9}ìáÏoš®IºN×BNC³Ã¶7óɽr`Õ.NÆ:ÂíxîQuìì7™xúö.´ùõ]Gz±ázäÌÿÏWãˆÐ øÛ¦ƒÛR¯™†øÐ¸ß:Œ«xãdž¨Ô…¢)ÌXè0~ ‹Å¯3‡lϳrn*ìÛü|xä lЃ;DYüØ}G?=úÎѳ¢_®fR©‹Ø~7zú½=º:'á¬Jªé4†›. »/Tšô‰n•ÕÿMà«6œÞÈœ´Î‚R~Uê^s Ìà™|=Ëü]õž¶–q£\x‰á²·¬ßy<1CÖ¯¨ÂU&ñ°Ñú:ž‘¶g­N°5£F‘“ò¥¯Øÿ<ÉÖíeÙÍ2äÀ¹<ž-®ÅXQ¿õÇkAèz:£»3•nZU@E_lfæÔO¥þ-Ðù¯"Tqêu/€U¬'i¿ÓÈþüÏïÂÃܺ¡IpCÞI¸-)5Œˆª ¨iTÐMgÈþìÕ‹ÈýÃÎ4·Ä¿®Ë€‰Á ¸X‘Js£á ïZò¥ê\/R é¿}È­öÄ!>?…Ù† èËïJà°Éõ;;Ájý¯Sõײ£ã˜Ô ä‘ÒhnZHv­'ε;Iiƒç‘{-Ï7 wÔÒ˜liJšJäZ/aÀÛ¤{[€®%G,i‘Ú,½=£ ‰7Ôa•þi6JþØõ¶Ñò·åÚ“Mæ—&P£˜ìßéMN©Y1x¿–<Ü ´sG)å¸pÉuåXzbº¹ÒiGvv¬ ð±·¼‚f6Ò[AÉôøV*vûwãEÂnuÅöÂE¼Sò¯ê‹2kLˆééS¬â-ºL> m4¤×ÞhÓæ“O°¯£œÑ2ï‡ÍU+Q/O›ò×ýC•ÍS襪qøXº–q©1ÏZÚ8›XíK!ÌU:íÁ¸vÅ‘®Ž³ŒNrܽ³û]àbTyÞ`Oor1,o éüƒm¼ÎôþþÎÜÙcJCT•Ⱥ»…ÕoÃ<¿ô…sO=XI-ìóT¦>áS˜}:× ìð2ú`9‡x¦<ŵ‡Nâ¢ÔPòùÒ2|nÓˆu×’;#má¤Í_¶§F“lúâF}µLŸ(Ù?†dç•´uIŒUwcމ$5eßà™+@÷»fM¿îƒC+"o_•_®š‰?%}™7[Ñæö.èv®Ùo1°Ì>ÌaTNOHRfýCŽÅïåœ5<{c¼?)_=mSæ±Ö-&÷I-þ,<…‘³Èû¬6¦ùE,]Þu¿Ýßr£‘ÜÝ#s8{9<0ïõ[˜ó'fê&°â_° ¾CÆ1º:2Gžì£ª!j˜ôöx]D— ;Ü'rûqm[äß4!Î;fÑ3½F~Ë€F Å5'¶³úÙ»aî²ÙXЧ;™Ÿ»ó§%ŒˆÏ¦c®D€›7™ÿ—Vóæ PR°e›$tȉ§+ˆ·B+Ž z‘~:ððL?˜Ü]J}ÂHžÛh¯žÏl˜±Î>Àt-m­é²cÖäÀ}¼›®Dná{LŽ\Mæè”Aß×DÒðz&|¹Á¸Nrí¦Kg9Kg´ dx/È/Õ!¡ò¸KÄ'û\§>©>M§7%Ó­CG˜åÂrjLlÔÒÙ“}Xq‡C³"üÈPfÄW%‘ëHÒ)|"Œ¦ßW¡b=Œ“!AMŸn apÞÃ,ª6[šôW 0ÿ"2Òö<8±¥šJ’©Øª2öV†<>:ÆÂn±çx•û+N Sƒæ©t®ª:{áQ0»ñä|ükC™og0ß̈ÚÊßÈ&BŠEtií¼q¦¸ùûeõD)œ_"Hçñˆ‘ékŠq˜7#lŽâg‰N8Þj€î56äÍÉ œßµë&uz½;ýœzâ“p·¾µÐøÎ n?ã#NäoŒ Ù6³W˜eGœoá®áýÔfÐБPӷdžàßpá‘9»ô”É »Ç•…ÓÃúòtf¢1-?/Bf­¾ +Î/Æu7(|ÚÌí܈OÞYÓ7‘-˜>ÊbËd-Öí{[õϱ¢‘ždÌñB«¼ k'ôœÌ ëñµ™>®(fßèÜ…g}H_§3]|ñøçb#sA½~ô ¼èϤ‰â"¤Ù¯… iéƒÉtÆè¬(&»’³ ßövyØY/º?«žò¤ÃæsšŸMͯ£Þ<#Ò±Mš¨_R‚Þ| øx1æl5!绡æ‰!ë¬tõÅÈþ†Nìq’À–Û9ÔÐ9’«{†V[æÐ¯ú‡ÙY±“ý_ÈŠ¾ZØÃGœiýIKÌÚô˜™ÿç5p;n³úGfâE÷‹Pcü‚5žy gu߇¼_Äx¬ÀÃs 8ZÉ<´Ìî5(*Оe8U+ú'"Á«Š—Ä(´ÁÄQÖd&?)û±Ëw60oòw…q{ü$à²Ú·AøÐLú­Õ‘Î]A3_Ű-C¥˜³ ­Ýæ™’ §0m'k1ɽœ®­Äß>IÝúÈöùÍ" ×—Bà Qê%9ì¦l„¬z&ñ¨,äß ¿Œì`ÊÓÛ(¼ÇˆÚW=d5)à„¶–4ž·ìV€åúz®ëÖvìõÆd•ÙðïÝVò9ðl¾Ûͽ㨠¥§pÓ•^”½ýŠ­”¸ÁŒí‘#"ñÀž÷xs„ÒòÄÆÖ’:KÀí/R‰—®óØlÕÕ¸Í~p;Ìu¸Ó ¦Šª¤ïöJ”°ÒÇô9ÊÔ`Z9„ê¦0i7ÐE‹ø!{›tÓ߬Kñ6|ZXlÎ=èñÙ‰¾‚1díx&sxA Lw–éÒß"¦d¸p#Š„Xvø¼CÍç÷p¶W ]w‘u{1%A¤Ì1#ÇJâáªþIfu½0YyÛùŸ€`‹^™»†|Ö Äüé=ð1Ü Ö øÀ—{þó\;ŒmMÁ‚ß‹Øh Üá|§±äv(…œ->¸ÕM4ßÍǹN´’ÿ"^Š3²áô<²(:-Nª¦Î€È-·ÙÄç1è+NÖí‚ÝpcWFµéϘÊn~RJñï¬Å¸è!eX»&¶¢þ)>™¡†¦çmˆ\Åp’ÙÀÄU0£´Û¡¢älgç1'Öxãù¡&ÔŒUd´ïßã>Oí¼q„þFXi/EëÒ7Ͻ`vë(‚ÿLÄìx:²•¹ö¡Vh˜0'ʃ˜K§‘u‚œwÓ°Ðq$£. ËTf…ëñ{˜ÏÆâk³ÏŒ7s‚)šJÆ6©Ã7'>8ÿrO?|K@ÿ»ʺќe¤íöäÚ‘£É­lâ¾r&2ê,׌Ã#I8¬¯ Qf¸ëâÌÈÖ”.æß:wÔJŽc T¥þó#3™­µÔ¶,»òG@À3T?-ÿ0³fÛc9Û; ‚~-£`û’µ³—Áÿ|ÊÁ7 Yƒù¬õwyºÌC ·›²–ÅÌWn®Ã¬šDÎæGÝpå¼ Wô 4y&_Ã*«þýáïx[6|Sn¢1w1ùÚfNÜ@XçY¬ÀCÝT„G’¬ï¹ðs°Ò·–=XdÏŠ®´Â¸Ÿ#Ø; Œ«~]Ow:2µ ¢¶ü„ÍkšáëC/†uõ‡Wadè¨|°ªãÜ\ò>U2þ«§Qõóe\5¿qì<óÿ¾…³náL“• mb{Öþe؆,ZÝÊÈÍþÈ$s}!7mþ~´…†¯½ ©>°Éž»(O¦¹Èò1º&[˜OFŒÖ€÷§ç’„þ|Hœ°'Sóéý- ðø'<}w³Óªqg{Z8.Ÿá¿ìz¤ò±"b4Dºä•xHÛ8õÙÿ÷LßÛbåÖ.°C®š½¿eÚcFŠ Ô7#g÷AÔKAÆèŠ?CŠÖÑÀìfÔõ4ñ‡ÃýOh2G›®yt’ûýÙnRûKü™ç‚·Þ;ÌÞE¢ ÊÉ¿ndÎÏŨs©Ed<ž1>Æ”>LD‰­Û`KŽõž½ˆ¹ýl3~iXÎÞò›Ü£ªJtñ‡8$m…¢kâàêLMf§¼.ü9s©=ÀÝ<ëËœ;·”¾áê'§Ñ%Zí4-A›ú¡éMg¼¸z½rèÔÛ&³qsºqüY&6G$¼Ç\Ù°7ð­4„Mºÿ˜5JiÃÓ—6âåŠç°Ó{x*ØMÖõ3u«™øÈ>†Û—ÄäwŸÃÿüÏ1f,“e£ÅøW1Þ2ÊCƒLVè‡Ö‚v,¾é@°ykë|ȇͳ–²›”ŸÁÓ“|ðŸ÷YÝÌ€¸ij•á#GuÀéãÅ '¤JÞ7$±iï  ¯¤ö˜Yc¤.ì³ZI× ÒçÝŒÃsËD\•£M«- ñÀ·8¬ðlò¹[á=“:\(+HåæéàÂã5pùõF”ÿȘ)¹ÿçàÖ¡DÛŠuþà×/Ü¢–…Re°õÜ2&÷K#k»Þ¾gîE‚è.úÊ£`?û°í_¹»ÔJðì“LØ;lN<”è{+æóönLz-Á^¡@—â]Ù38ñEŒÃwä&çïÒ2Fù£![Ý~®h$cìi¨?Ðè²3è+CIÚ¥f‡ßZ×ãïçiðŸïx÷ç©d߆Î}sNgõà®ÝáF¥=ý¤ X· «GOcO’$˜ HPÉ@Ê“üвcHGc+^Ør $âl\P1þ‚ ùlç)cºoº=rÞ˜µlîÇ]_ÓpÕ‘­°kF3†—0òŽÿ0GP™YµœIÆ”GB”®ãšÇ£Ì ’?/˜í%%pÂH’—; jy ‰²±£&}þ°½Wtÿ»#¶†¨çèÓ…˜®Ê$LWŸ —}äˆuN~òÉážÍeÚ¶Ž‚^ÈQF8ÑŽþfhM­ ÌäY1µþ¬yÑw6*¢] ‰…œÒj1xäg·cÅÅ*íD„’$F–LÿH¦}\DÊCÒÈCçQ,êצ½âoAuÂ,ÇÇY11jtH†NûÄÝ–ñýRôéÆ/ÐpS“ºlKg:¯ía¯ð˜1ë–ërOìñaTm>#÷J#.xŽšf­P•'O½©Â;o¨ä»ÇØ^gÚ7„Òñ‚Z´m(¤Û¯iBIƒ4œ¶¥!Ó7P9}’1,FŸ:Ÿƒû»ãé”î? |ßœ¸;^‡¼ZMFÞ|1>÷6%Ž·Øãݧfsá;çvÎŽ=³M—æìþ­*;Ùi…!°{‰>-^By<Ò!mH…—åY—Š|H=¾Ÿ•¾wÕtó¸…Ÿp峘^¼ÆqŒ“{,óå‘7¸¦¹2ëüι±32žã/49ÿµîFqw4~eë¦Ð|»‡°àÛLL?F¶ÝÜf>ǻʎ"ï@šù4Y3òèÀ!]l½¯4óß¼é_ÂMvðhß„]Ò3ÈùC àØ M„lDYáìb†ÓçÁH °£9—¶‘#ž¼DlÃC¦ëà´ÝÓ‚É'“Ø0»ó8X]‡.1.ìàö=°xÙf…à6¨hìaæ6ËC5$·6\„Cþ¹v¥¬­ØT2Ãò2cÍf;ôòPTðQ¤9ÑBdÉ1LƒÇ³}Àu­Ùÿ-Žn²§¹6g8Á=Bô^°)êRKÖÊÕœ¶úo…oå/9¢fÖ̇0PK«mÝ¥\ ÆÑìn}gð k…‘SÈ£çX¦2"G–ÑsŸÓ Ÿ Ó ‘"¶C©˜¼žë_: ¤ãß³ñä…ØPüm?¦‰bµðÃË[ÐemKözžÄóGà\S$–]1¢KSêÐpKµ¯^EôåÙq{¢¾ y+’×÷ž#'„˜èÆ4-zFö..9Vˆ߯³ü†oص‰Œ–z ɺ „à”¯–£¶ŸØ©o`·üÙð‰–£?Î]ƒ{?¯0Á÷àö2ì78@ ^EA£3x6zìuld Ž‚0®%# gR×G¦Úf5«º7˜:öÅÉÞ8ñ£ULu‰Ï6šüml*:‚}?Häå,"6®C¦Ëb}…&ͱ q¤ U¡ÏèÙ¸X çíW ›~ǃÎßlé 3’¥¢Ç.ãá´\½ö{pR<-Eªûf±9é$þCðÝ0¥<3'uÑ °ˆãkÆ0Ò:…øYñ2ë·H’8˜þä[(aä|Mú‡û‘íuYˆÊ?•à#7Œ}ÿ2§lu{µXbõÃ1pÍ‚«bÅ­;ÖüEÏEÒ¸2]ìs7ƒlÇ»èæfIÒÿÔÁâ¢@2º™A…TlÈ0C3qV¥Á•á8QÇâ·x9¶§ì ‚±Ú‡èn‹UÞ6ìíÄ 0Þ%ØŠ7¢æ eÎìeÍ0oO(pŽèCÎÃclïBø$,ü‡ª[²£ ‰£éwyÃ=¡¯1Z’ Xæ|~ç7¦Åd³ã¿`—÷"FÑ@žšVz³«yöÃu úy u“á¥o‹ë€ÿ¢“²Á†v™1¤kk”¿…:>zˆÎ ƒP[r÷×o€‡ŽMøªJ€ni¹^§tˆe@2 ð¸Ä(šŒÞ­IÕvëVxÖã  Ýøþ˜ í¾iÆìÔ„¨Ô_@…Ž¡«a r·1›¾HÑ5y¸*À•þ%=Î pÑîªÉD“rðÚl*1Ë“ƒ£¹bô§kZ.|~k³ k…*‘»vE\cè]5zÊâ dÖˆƒR«(.g“»ühTü/6lçoì46§žÅa¯ü |G¯+“CB0rÉ„òf Ï܆"™Ú ÛϾa·Ý…&óxÐr'mŸ‘Ãì¯*¤«'°V÷45ú£H »\Mi¿õbrbH›änrƒ_VxFS‹p?¼cÂa=Íøý=šÅè‹óÍÌí^E*ÓPJW^^C–-dá]å8žü{ Ÿ’*Pm6£&W•ijd HŠ[^±½tÈ[|é^H·ÂÑ*kjZ¢ |ã[iÌÆRÒ3Ç…y4.Μ4¦?NЭ²/9¹nqžä¥‘§&Ä`5ê‡Å´>Yó€‰çk"“ëqùˆ3’E»ã1D\dœ¬äA›³Ë@ðçXÛ²ö8[ŸÚWì«IÍ“oæOò‘}ÃÕ¬¨Ùfðô¶ašú†˜Ü”V6#ý7ÌJ-`§w3N,é‘ÞTtu$Bµ 8y ÉѶÆ2<~è ¬7¾wßâqá(æÎwY媔Ø.¦ô×)äx¸Ì83çë¡õ£óˆbä¿ã¹¹rpÚ¿¶]Ø…òZ¨aÜÔšQw VÎ9 Nv„Séçâ@N!Ì+SùÛp­&Ë:SA·¸’u\XÀ)nÇ¡ÜÍP·Ìƒ¦¯!yKcYvð+&ø3á©DXy:¹ÞÓƒsž@|ÖKæyåq´´ "´2V«2¢×>Âð˜9¹=? ¾ª°áXÔš7/¢^BÙdÚq.5¢:ƒ~ ½5‡¬pW§×]0; ¹çsOqÛÿN¡KÌÌè—ŵÀ#šƒ­7=‰îR¬ØnE]ûÝ|M{†k-É>µa<="n3õ™ìDc"6ºŽªþÿ üãsœ½±ËžD|S¥[—Õ`ø9 ²þÅ-mZû­¡J+‹œ¨|vøƒ5M#›äV]Ëøií~Žñu"V ÷åã!ã£9(á \ÚAº¸%^$¼C /aNYL%õo“Aö2O í%îƒ÷×G°Y§žÕ¹ˆùÓDé»Mýðsä*ƒcçA-+Ö Ÿæ®?Ýqþ“ü&[‘ˆ­ß …Ÿר’I“_LfGcˆÚ4î8HMž¶““ã"tl0u9?õ­7%œýÕ¤|›4}þê®Ð‚?›—ÐúòNØÞ'K—2wñtM,|xŸ‘ú~˜Ywc>eå5èø|òîãεˆ^3EI½^¦­<­›øÐ]-ÚùŸÃÖôªx6šŒ¿Bó`Q ä­† õŒ;õÑ91D@M…UÝÎb}–?Ìýpñ÷ìXùRU4Ù+¦—SJXn¶•òul§ËnMGM 1’û.&fŽÀêÜs°+O“nu¼…Y­¸øòaä,˜GÕþ%ÒMDÂ#Ž)ÅÑøDü2æ¥H“W†~´óå)V'Þšx”ÔÓÀájrvõqlw:ͽ¿ñ&û7«%Dk1Øúr¬f‘ï·‘ô%j‘ƒ¡Ett.?ýÛäÜÊXø`Kt׺Òéë×1¯· üý˜ ¿€8­9¤/›²/Åé[}o²³Í^ÙÄzZJ½ímqÓoYšü{)]w7½¡ÿÔ`ÅìmœÏñ–Ôá{ -ÒË.P:‡1žåT&Ùˆlãf‚Ò“<ÈÜ?“ôNN:RãÐT<“½S1†æ]0”mKZ#~ÀçAz;¾šˆÓT&Kä(Î ‹ÆéæÑ¡e2dó€(qýú‚õ Éäõ†KØ.9ƒºP™Ö´Má_$R¯³m¸“%| Ôðø+LÈf£}øô¨ y. %ËéÒ8ðÞ7ŸéaIJEieøÚ9ßÂ×6ÊÄ?ă}ÊO_ËÇŽ9ÄÕw5Ù»Õ—Îs-‹ÕiäÄc1&?ÄÜÆ8hÔ™‡;–\`-*µ9kbèïWŽ iÌ¡@têG¬‘=Ít´ÖQ›¥Æô°àØæÌíü Ê’o`¶ãν˜sœ® ŒL®C«þ^<ºú-“i®G÷Û•qÖömf„~nȧÙD3V„„]¾Ê5`Û3ŒI…ÓCÎìæÒ»ÿº½Ñ&º7Óü¦‹ƒW 4qQ¾æ…;¿ËPC‡Ýøß¬g®AS¼ç=þ0ßKCףﳅpxàzž,Ã]ñgœÜŽë†¯×ø‰êÚ ¶ÔA™†»Ý‡cšðÞñ3óæ§Èù*ÐæÒ;@"sHx°Kü @ISûÅ­Þu„Úo ä!]¯+¨^‚M› 9oEÆàÑa¼ñ˜­¹6:αþÏ›šÝÁ`»x+9Xq˜¹xÅQLíǦŽ:2¯ {;ÀñB2 qòdv ÂGŸ·sá[Ñ`öæW‘%óž-òAÃEÞôI×(šçñâb[MòkÝ:È\Í\:–Aj×Dâ}ï#hb5u’'s‹5eIà9›{ ×ÚH’’7³˜oå"Ì•è Ü©ÿ™YÍ'AC:ÍHKè³ïðr‡Ùi*œŸ2ïPøÆE´p ¦ú‰[™7F,øø¤°&—‡qVPU¡?Ñîà =¬VâÉä4bqâ`¤JöϘÆLñÖ$µËxÉ:o}%Í¡J×òhõóƒ0ÞðœuÿÄ…§rW˜A#¸QêFEfˆÓ‡6FÑѦš-§&ëI¶–q:T‰ßRB!ØD‘ØËÉÑyféðD”Ÿ¶ï²¢vëDˆ@þGh[² »Løf‰"¨¿û½³}hïQ(p¾§¾¼açªU@RãnÛ beEÏþgÒ$<ᥥ5ug[½ÂÔ3ÌǵªC¸J9™]3c*!?pvp®Óy²1³K ´ª´¡õå%,ù¥Gc$>ÀÑã3áÏ›éx%l#œçLÇýEHS‹tÜry:ý<*O `3}á%³Fz¯7ÍÈv&[’Ô™õGßCšß{²ö 0Ï\ú˜™v†ÄÝ¿»õåØtò‰$=nXµr;qL£*sšŽ]c=WÍ ewÃ!¢z¨)‘"uuúM§ CÿõCO‡8ç‹… sêQs÷@/ðT]"‹‚Íಎ#t+v¡¿âYl:6ÎQªÝwO P[ÿûðQc‚=Ùy— ø‰m Δ߫—=µ——®iŒ€-»È‚‹8M½§ ±\ oºÖš§*fé€Èº/°˜# ß#‡Á.ã%$<9ÀhºÀHÀ/ˆ.uE­±ÇhœÈG– gÑ–R«í(-Mý =ÀòÅaò³b*#LÌñå2I¹o'¼ÎkIg2''£ tÑ8zì?3³.éëµ­ GgT©Jæ‰:J¬Ð…Ó󒸞Á•ådK¼=]y\š ‹Í€l©ùh°ý ªo›F8Æß™bIaÒ ¸«_¢ô«þÿòe¿Æ,Æí÷kYïž™$×±-ÖeÁé°“ìÄ&þ÷èŸI¤?ó⛟­Æ)ýÌ– WPÚM¶ßàÒo?ßq·œ“%j懣Â`©£ÞM%3M<ÉBeIºAl& µ:vYŸqÑ᧬¸ÿ~jD²°"øã«w –ÎÞ†W. Ë™IÞb¹’ìSØÊ¼“'›¹ØÈ#j^g]4é§ ôÙ^I²'±`ç–I8²_§U1¹ZàÉ÷¾•XÓ¤äNpÿf€Žî/!ømt^É¡ÁûÒl­{a÷Ü$’ÛLÖ!VjŠtî']úv[:ý)MÅZv~k´R9ˆÆ) TpQ)¶Æ×ÂÄd{RH$¬/ÁÁ¾#,oˆ#Ýx¥ÙÚ(&Û׃æ·#ß.Cö½ý˜xYŽÂ¼2&ºtUR¨AÛ>m •ioÓàýþR<~û(FMæ´Ô'x†æto"ò¸Á›”Ñð?6°aÁ®•`(UTëã|ClÉ6ƒøÊõÈÛ«K_ªÅâ^'!GÓðÜQ%”?2:ÉÌØ«}ðÔB’ÊyÕà—RSªæw’½`<£ÿf€ÎTœ½#¶g5Áƒ{5¸?tkóZöÛÞf/;Âq×…ìÙ…ð÷¡ ~}÷ :PçtÎÃ:X}*mV¢öÞEÌÑ@~RÓÚ)³-C9¸/v6i¶åE¾¤XÌ\õÌN›Ä÷ßì—ÅbÌÝ…ãØ,)CÏÝ› S´Š…×];Ö«Óù ÑP.”‚•åÜæ»âPòèJŽøs2r}è¾æWøu6èŒJÂߪ4ªŸ<‚!\Rm«KJÈ (>1•/O#BÏÎÁi³9ìI™"ú“Õ¤ Z5IšÁ“N𤬠žÄ’7Û‰ÿĵ‡dHõ{~2úoœµÔ}O @åÑÝxŒ ›iãPüu9ñmÑ'_CÀrõ\\ñn?¬ÂQÎŒÛbTst1=âZGŸÁòÿÖ ï^èÐÞ„yØ?¦Knï‹§×¥rþY tê‰xÚì2(«øâWu;ôSÖ‚«é71¯]‹lgBgV4 ˆÙ@úô{%œúx žœ«ÀUÖÇéê¡fïË­àýz&NgI ‰O=Ãn,:O«êñª4?q2lDÑÁå¤ÖhýyˤœV;Ø,Ô¤gm»áÔ‚kHætu%ëEŽãÆA (v¿˜Šà‹©3ÉΚv&¦ÐæGùÓ3¿Ehïµ>ƪÊš?’Ãêû©ŠY V +S ‚WÔö1á~ŸXáÈ”è›àÕ*fîqœ€*Ñó&û¸ÏÞ z4\ñ¸½”ò/ü‹Ï4cIC0? sõ¸ƒª b´œ¤™ÆgbȺ©qUÕmõ'ÍGÉÓžœ94Àix!s¥ã©,œ]³˜9À¿(¬\AÆûí1òÓvŒÑ@(Ý…³ÂÙÅzFôžtn·¦n;醵—Ø çüè&ˆ9ßèȽôžÝ:É?ñhPä”’s¶>d4#SõŸ’ôËIà²d>†±1L!½¥@H¯a'æ‰%“;9 1Y˜ÌJ”¡³“Ƀ !ö(~µgø8t!yK«Œ`žT|³"ò(«–@œZlçÆî„Ó¤¨]g8­Á…äbaDïEreŽÀE:…Ó”áÑ# OUID˜$òîéÅ’0ü(-LšŸ&!]( ˸xî¦:D–'Cõò!ÈÛvŒìʵfVYSNå æ­ƒ½±«Ž^¹¯‹"üôË“—¸œB÷=¢ËíR‰ŸÉGä’3îy›é[qû÷,¦ZfJÔ%ü œo•»R¬Wà0ü^ƒ,×|ÅÄ%£ú?·²ñZ<<öù¦PÃ`¸þ'—.R¢y •°a?L}­ …ïCi‘‘šó¾Ö˜Óc{'kµü7Îâ`¦þämþFå}É÷pßçûÕ^p‹Dˆ£²Ue*A¹S âÅléñŠ£ÌôM¤»qÝU 1_qÉe²’‚pÌeÚ¬Bò\ðeG=Y¾E ;Ûy'l©zèrôzqŸ=G©Þ›(fËÂ( Ý]ÁmKŸB_žr'ú‹Ñs{ʘ·Ý—A»¿‰D…DÐÀ¢ì%£íÈÛgŠ¥º„ÈgÈáÃ$uº‹'¾ô2Žrç’=ýÎdì›)çï*œ“âIOìh5OÓàÕà\úõj¼€þ»³é±öx~Öv"+x’n¿ØÇ‚Z:þ·Ö#ƒ½OæáßÊôèF–O¡ÏƒÖãùy¥¬lNˆæÄ0Ö_fQ{­SVrŒþÔø£ÙL¡ÂjZ½²„9v¾ÿó!'DéâÒÍ0‡Q¦òñ‰G>'?i!ÞÝ ‚_€G/¦­"‹ .Iœ.°dæÝêÆük“‚9€|êBÆ–b ÚÚ8‘“ð‹ø>/—  ¼Œ’‹mWñ¦W^Ê`um%Æu;óx=:kÑ|%+Œ%£~Ô_Zž„.غ§œéñpx9–³–Ü€‚³ òFóÍþ.[W&@.øahþ~&/ý ¼à †€Eº¤'C”½¼€¼ýÂKvzœf€Ï—$.£AÐEMOgM»C¯§HÂxb×´¡Ø:5æØ6rÓ¤‚æLäÁµfÖ M|ý"Ÿ¼ž™ŽÜÍ8ýŒK˜9 „¾Ä½9{`ÙÛ» 55ƒ b¨#W”piÐ…»¿Cþ3¸ªaGxøæÀŠL#úá[ déàdL¸lT‡ÿð‘Ú¾|èˆÙÄ|ÛâBÁÁ¯•åÄá¼jiXå}]"DéÕ‚x\´FŽFjËà¹ò´r²öWŽ•³³–Q‹pþ¹•õüd™É Ú|}9u]´ˆ8‹\Dïësi}h;r Ýïåb]?]’xõÅד÷%ˆ{`.æ[­!‰Kâȱóßñ¶3m¿CÑY’6»ŽÙg(I9W Pºé&Ldí$ãÎ8²¼Ðì¾p~ ×fïk‚ĪLüÊËó%Ds'KCìî0ûæÈÁ!Úï'Aûwœƒ ½µ¤”-d~«˜¡Ý®$êæõ¤ç Ћ‘3‰¶ÖZ¨ÝaÓÐÊ÷nKÝB˜·Mä³I-Ñ8ÝŒ;_ìDzVäìâë,óH˜Æ]žâ‘!¸D™¾ÏY8¹ÝuV—t?†¶=9ÔöZ4-½{Ž$™Û}c8çŠÐGþÀã£ó¨MW-UÒ´!—JØkGè‡Mjú¢ÄE‰Ò™"l>XÍœ^Û‚Ñùa(á6Üà‚]›#U¯JdÛ·p ¿2yW;ZãZ±Ü¶”¦¿Òe}·ëãÕ3åèØžÅÚeŠÐ_øHjì[ø)¸W{ì… ÉäãÙäBPÒvk’:x§³ö´>c#˜³>¤cÑa¸?q–œî‚:á„×!ûB"Ù³.¡¸÷ñ"ú`f ü¦à-hì1 Í:¶PKþ€´IkBô~º ^>Çf42ûþ*ø¢mJf(«¢l>JkMbÃj>ú÷‚<=úÔå—…“qW+ògÿ_l¿]Ïžo3%9Ÿ;ÌH&ßz^Âû¬eTÞÚ+!ÕûllE.ñè•‹’p#ä)«ìŵv¹ê‚³RÒ±PÆ”æûh3–chëU¶àiLK^F‚Å9´o0„U»þž™hö…´õ"D+Ô•jÆÒÄ43Rþw:½¶Éˆ“4G›Èl[éš3ÉšÔiTãN6œÑ¢’û²àËUjÓ Øy×zÞ‡ÐmtÇíc˜;ðÇ~)2q“˜áЩNfFÜ„€i™ð‹¯5­É ÂF×Ò‰i°zœËä<4À†¯ èHþcÜ\ðúÞÓ-aWXý…ˆû~üfÝóÉübŒü“ ¶0ñyhñq ámŸJÞíŠc#Ú¾±l¢8œí8‚³Ö¼ÃuÞdS‡™Up"l+<¹Yg± å÷êH¬>6ššÐ+òøf‰²« ˜õ¿°Xâ ›BwÙ2I2öÓvÎã6 ‚©ˆ0\´+i}®y€nSw Á6w±ºTeR Aõ*ÊŒüûÖoY-¯0bÜ?ÌÖ/ô®ßøæÁÝ.”Þµ„óïK<Žûu@ß7cŸù‘ñ á#5¶s™Í{L`øÆ*Z)– W[:Ÿ«Hî>S9"fFææÌÞô.¿$ô¬¸ÇóÞÏ’‡oÑ7Ày"cñÕ†–ØuZôÁ oì:´°…§ïÃ)A/0y°»#3*:Š.oƒ·v0¥Òù·èÖuŽ]› jhE¶ñ›sÅH‘ÝÜ3–€…!xçk1Ø“ öŸ‰ lßMuöKÀÆË'ˆªLUëG·Rfî×jè½ö•5u‡üyAt™Ø3¼|ï*ðØ¼‡Ú!¢2dΕîæš kÁ&ô’»°éÝ|js<‘^îèE¡ŠW¬æ œRhB:Ø›¸>1/žÿû}1""j–ïÃûoî3“¹²ó: ˆ#‰ë^Óë¥Ë©Tgaå ˆò¹[Ä*J‰ˆ½Ž Wcï¼u=qò7'ÉebÀQÒ$¿.”PþìŸÀûûͼMò+Û9ÿfP'qSÒ´Ežz˜‘¦edÚ¦|¢–’D?ñó“ËGB‰­Ù̊ȃ[Þ„Üá'{<2éö’ûdôIûŸ[™íÍš=‹¦í ¦tÚ¡8¨f¾§¬§")GP]:ˆäªÆ£ØqLŸD”?ÕÐvm zÐÆÜ™™5t«ýt|Ý¥B7ÞFï\°Áwó¡Ø¢Îô†`2¯=áÌ£«Ö$¢¹…"þM¯#ï 1+F?¶îÚЊò%/k´—3 ž¿Î™+GØ‘ÂyØ¿Ž:µe1Ö:½x°WŽTéòRŸC"dÁÙIþ‘ÑÈLϨ„½á±u† õˆ³d=Jnâ\£Dr­1-*}¯–€äúOÌ5-c:­Jîýœ‹-çKÁN~ªìÆ?æÿp#Ÿ<Ü@rÞ”reýèù™Ž@f<éöµh12 ÊŇ1úªêð~…)*Ý„›3`BD .-öCÇC¹ôÁáãhŸFcÏ+øÃoOÿœ‘‚ÚÜwÀw=›•ø´õþòЧqBôóÜ3ìEÿf¦<á4|OJa§­+"%Ü-0­kIìwƒp8r¶]|2n°joq_,œJ’éÿ»p4ĈJÔ¢w'/#¶üÄHF- Kºèî®í­6U“¢Ïe§Ð›Q$êçTfm_–·î|cHLŽ€Æ›xü{>­½ÇG룢éQ¡ `A&¢Yœy%#†( Û¸Š¤çdåç£kÖ,| ‰55^ Ìq¸Õ'ÏÖ¬½Ër=pCê_˜:-?äÒ}ÕóáCΠܬAøwÝo·%̧¿ÑϺ‹a¾À«Sátc—(µ•ÕöOáüäþÓO‡Ñe›Œiü»í08¶VŸM ݩ¨²†¼­ úGÆ ò@|Þ,@‡ᱦ&½ÿ žk_CvžcõïøA”c|:•Z|rüfnŒÝb zèR„ M A¹«zK_êƒC—A%r)¦œe4=‘ãæÅ- é¬H—8ÐÞåðzÇNní÷µ¬jÛ`ÐÁ*NÁeÿli¶äeæÝ0ÅÎx)¦EWÃj·D*žž _§²ùßðIä).ó{ÎÙ}V[—/ä²]Ïáåâ)tM§U×>„ŸÆÊ ÍlšÙúÓÃ7¯Bã{U˜øPÝ—˜¡ûÒ46V–lûÚˆº‡5H†@0é™çHܯîÀÇ; !?y*ð.Ÿ‰i¡ˆS.³+]/ÀàE2ÝÂX‡²o‰2Ò߉_÷[¨øz€¼Z I_ˆ9³ÿ†Z¥?8¯Xˆ<èeu>^€_•Òôaw=;UR {ý¸(¸‰ÑŽe”ÄM/u;d¤ ‚Ù„(,ÊOFÊ¥pä“Ýã%†¬t¾¨Çs¯™FÛ(xJ†D&1Þ²òo¡`@Än(SÃ8Kâ¯=˜èÛ.ô½´MËšâʵG=s>ªÿVK«»ØCmÎt2Lãdz‘óÎJ¼ÄU©m˜Ûá†w^ƒ÷q¤øsóðº%½bægñ`Ÿ¨ ,öÙ +½Ø[ü¼dFOL/ú A½Ú¸àSJëÍÙv,s”¦Gåãö!ĔՎòd–£#)¹Ûþi¡¸t‹M⽌¥V㬼çØþ¨[4´È÷üå·ÈšþN_I×zÿfÜÅk1A0h¥ íùšÃy¶5݃ژa-CP2Ï~[NæÃØg}Ž[ÂÝH'Þ\boùÂ#Á¤ÿàâ’Ö °÷w+¾Ô@°•/zñg¶)²|Íù)Ýæ6÷Xƒ$3àkL?|œ•éU†cÛó™ïçbÙC“üý×ÝPüôP5 x§›ØdØ}·~:ÞÄ7‚$ïì:8å%Ißç2öÇï¡ËåJô»–‡š¯2àâᬂs½§¦â@§5ÈÐB¿€bV;]t¯Od-ôTè—˜Imøð(ñ‡@¯«½,挱Ñ×qnר:/CªÒh4g69$¸¯½ý>gq‰U#wî{3VöÒIhÜ¿lŽ!kÌ;à̸<½7&„< .ÒÕ tÉñ*':ô+ŽŽ”AL`3gö`_W£®ô4*tÚÝ¡È,þ®?„¥ Àsïbëh^î¤Nޤþ–,÷Oµùù£ª®GAUT Tú£qû&zæ°>~Üé‹^¿Â§–BÈÑ1gæ=mÄC_sÁIžrv¬›AÄ"Üñ˜ƒà9“8ê.€-GeZm<éÒ§¶qV.Ù§‡ßE$ÉüÏ»ào41¿Ïpý4îÃhàâÖ[[5édAøòQ•$zOeר9@žã?X¦L¼GŠpè°½ýîìøo^ø6°©ËA—Á|Ξý‰ì³2ì÷Ë-¨Â¨5m7΂<Ça•™*ø¦e2Ÿ¥>beP9Óã*Oó„²Ð¥«Š*{Ç,2ÖEMfs> ±W9êØ³|>œµó„µ õŒ½ƒ.E’Úƒ-O.ZMû‚.Šð\B;ÜÉ߆¡V§í￟b?p´ÈQ¼tj £Ûÿ‡ÝYPΘðjíPøÚª46HL;ûÜø:–†½¯±TÖn¢wÅ÷C…˜(þ~M‰ñœ¼<ÌüûÓ}ŒpQN;Ÿ3©7ïuÂÏßkZÝÖâZ|{ý1~þ ˆËæƒäIâ ¾ô>δÐ%®êˆzöàÆ'LC…·a n†åìä˜wÂê[«!vŠë½w”ý½á&ý܃ZCw˜ ¾jÐ@ýXýÚ Õ]è{/Ú¸õ£xÁ ²cžÁn©dpÜ燢ɲ¸Xwë«ÏCÔ]ÆÂEê¤Za3Ó6óº[®a«B”!¦ÓØF/¡«¿lÅÔqìŒ1€´/Á^¡„ÌŠ@ùjs›mDfà1#ôœ.ÅüRpap=^Mb†ôÃaºÜzìý„Iyu:®}EU²µ´¶UfŒV烙Ë~®Êp“RÅ%q†dVðG<'øýþŹ1ÊäMf)Žl!Ý+z1Rj³BZ™qòŽfGæ]į‡}Z‡¸n±ÀÜMi¥NžßÛÍØmMÚç;ƒE´ýç x¿EˆÃó.È\Æ™Ÿ“Á¹ï'üôøwâÞüÀ¸4i¸æ»œM=tZ#£±ïN>ÒLCKÈ\ºŸ=,1‡}5˜1—èaŸZ ’?>àS' HS“%.rd~™ mŒYL’lxle ­ë)³žqSŒÚ} ?¯U!}¨Ù´ù˜ì¾šé•¬Äÿ±ÿү㨡ÑR*€-Ïú×4q©©PHÁ¹» y㔋‹ö B†·uoŽ:ÛYž²e°/óìŸ(Æ®gQðÒåô—jÐßË.#ï ¬§z"ªiÈ«× é’In8š€F3üX±kr¬Óãìµò XóšJ+Vã£È_líÁëpýb>³üp-»mM*¬™ÝÎÆ›*м.dâúxÆûy;a‘­'Ø] ç>ÌÅs^Bìc9øbð c:‰•º%7àýÝfð–ù·O%ýë9bWÔ“®Ä;Ô†ˆþèbzoõᵯòdžY»Uê)ûâz*œÿs ¹º¹P£QwŒA×ê/òúø°-5lãÓiäí‰\<¾=ÿ D1ŠwÇØæ‰8óo¶U³ãËàÈ* ÌùÆH¿OKQ.Nœ˜B£’¼iÆ•0šhïpqpð§Âj1p>@ BÊÍÁ(™yƒ=ŒD|¦¢»õèžžQ¦(ú [®,Îzzƒ+äP¯_Ê?ä*nîÄäÁ;\m¥T0z Ü#Ðï« NRànŸŽkkŒ˜î“ˆ¶¯£Î®·@¦ÜÜøz¬v!©H_>c÷Ô–xåñÎÃqøÂ߉6…Ž,eŽ{ÕWñ÷ËÉfŜ֪o-CAÍ0}·9‰”øRZi·¥g™ã×3¹ºÐƒ°¹Hy¾é ³][–àßMs‰’m&s¸g ±µ•ô®Ð"Ê¥hë,H÷}oB~ÛWÈ÷Ê‚rç N[2ÙW!´* —þ” ºà ïODc¬Õ16ï‡"\(Ÿ€×ÇN`³Ø4ü®£„§3Á÷ ®VŒ9Ãæ°;@÷jÅA î8û<чÔ-FÇ[è²[0|)MU³âIÒ¬KÐWž3ÛšØohu©5~vxßi$^>iJ}N(»–$>ø%¬7/»Ñõôñ í?*@¢Ï&žšD@ì=üð™É컊 TÌÈ©[PlõfIR[ïcìãÒ6œ³G÷¿Ù‡z&äÕ.r£=¼o¤%KDHÇP'ÈËO¡ å/Ð édZ9§’ ‰ò-4ââ8z~ˉþiKõ†Ù²aŒk$í Ç0/AÄ[Gk'?šõN…&vÃ÷Gm¥<‹†ÿ\Kzý[X¿Â™2|‹üµéZ3ò«C ~>ÂùΞl×”,ßÜ K»X§ Qê™\{o%‚—jš»na¾ª”à8ϽIÌç¡‹ï>§ç* S4Ó ±-ÊáŠ+ìÄßVè{|Ÿñ9ÏLܻ˪ËÛãt™:vaãO†}K@¯ïû4\ŽþÛÇ$®€áLî©Eoæ—ïÖ«ø–L;Âô™W@²©}clˆBÿ’È&ýÒsЬ›Mú‚ùÉM©}lX› cp^c÷Ì@ª |Ó%I¥ò!(™aDd\ª0¿béºxŠ9:w_eC/êcÅ]ë¿;ô–Šƒw¶£¼‰3½t÷ùzµqq\Ѧ;§ãẅ '¿ Œ-¦Às]Ú4µ—SSò.yŒ2‘vRx½« Çmã/ñÁÈCp;½„È(–bço´ õ¡ââ"D¸ÅŒÞ}oK¶ñ5Á+B²ò{ I™Ÿ § µ@yúcäÓ"ï-äÉ¿Wnø™;•¶ÎÙ ª[µIFV¹ýl÷k_fÌ}<‰ŒÕ)gf‹ëÝ·€ŒüvÜë6§6ûl#ºÎÙ³µ®è$ñ¶2Ô£Mw&¹Ð;›+±_ù3×е¥ÒXf@ç>êÜl„V•Àu4…ˆkúïÍBRôex¶Á»š9¨m‘ ‹–`Ëܪ¨å“aXU•Š‘^A$wq,º©Jý¹«Ù£Ï•AÊíhª‰g9à8ª†âoYyR\¤éæ!«ÀÃõ!xan.Mr…§þ)ôÒ?7rDÔ‚¾Ø.J22W¢È1x~1ùÂÌï½i²úYJ©½e®d&óä rßQÌr›Çzܧ7ÒªQ1v%ݽù&:¨Žb‹È?пuÖz¾cO»—ãÚ¥XòU”ÊÍQ%†«û¿BUd ˆUnq›Ä9%h;£KšqŸÞZªWÖbÔöªO.~ÊJý¦jxq+WhÚC?`öá|ÛÁOâ³±L8ô˜´FIQÿtYªRÒDi—VË €LÐ&º$3C¯÷ã/Ó`(Ïñã^^ïM„;ÈjΦü°_gu²—YäUÊVN\Îpwùµ2Y_rÕnÅ»6[ñQå4ò(2öüe.wó‚Ä—<œ¿õÔlDåé$@$\§Ljš^zLj…f7óeƒ±æNs/£!̓//z­Rš¬µ&¿gT¢ùY¦k:/ys0–|ó›IîŸö&æ[rÙÞÊ f÷ÑP²çÚ ;üÀ†+U}NiÓÃÒò´.:O|ãâÎ[a­Dkwò²¿ªiÓwì¸ru}Tæ$ÚŠfò›‚ov$:Í&n’1èê`N®ÏÑ#g‹ÁšZ}’ôá4k±D>ÖÄ–%Òp5߃™“lFÍ=g“¦–=t$–ŒûPFµ ~OfED 3Ý­œ;÷ÑWÅw¡=î/Xù×1©½wàïŠ&ða¬½•^œ#EíeÓç=)øõùS4/£aUÊä±B:Y·b„àGy+J Kã¯KhhŠ mß~„½¾ÓS…oßvÆ\¾†ñ}»8ŒâÚï_©®W4íûx¯¼'ǧ’Œ–I ‘ðrØm5Ê´\!bZ3»©\ÿèÑ?O>à£)/ ÉÛ¡ÐQ¹?[À† $:o|K‡m=9lÂÓ&T¶žMÔfÜ„Së—Ð Q/À»:„4­¹D*WfÖeÊ’m{øi”ƒ¿s’½l<ŽíŒ'r{Í}Iû ÄÉ êqêtü|‰×Náî6šÐ˜Íþ8©Jö£Êã©Á‡ü˜Beô[¶MèàF:W„˜ÿ8 ¬Ùýf*Ŷb±œ×n%úÛ ³H‰œoÓAµ/ñt¹f3.'ÏWŸEƶ™=|U~â~›}[ç Òv·j¬?ƒqÃõL¼ /íû³ž6MêÛ<~[8#(­¦ç—ÇÃ!ñõ(ç^ŒÐ†ÙýÓÈ}îK¦óé+u3…=¨”Sêàó(^ê\œJùÇN²¢2÷ØãG¥¨f@#ì[ò)B´»/Ú¡þ¦>1|{oobªK×1:Ew¸âªiЫÝÒöãIˆÒlOm4÷ãÖh ¿ŠAV Ä” P¼Æë¸1¸Ð¸‡ÃÙ1‘ǸŠw1/îC1Öîk®¥ï.Àõ¿û™ž²Ý¨º ‚_äÎ œ‰gZø´ÀèÍÙôÉçYxã¨.im¸Ã|¸A7´\B峑P¶ˆxg>³z‰Àë@ìz s©}°â£’ÿ §‰ÖÀàªé”½&=ü¬ÓåxFÀî µ"í¬ÁF_ö»‰Ý"¨H:™û ãÏnu §5Þ6äeÎnˆ2Õ!©fAL'yeYðù}Ôß&/¡Òq'QYn‹ÇŸï„ –‡5„õ•­4¨pU…•ÊdFF°CŠgßúWgP 1f‰`mpw˜ÆÂk)5z7ð<«8Ó—(Ž>fô7 qƒ"Š×åvרQ£Òjx fD|ÿcîHů„ãçñ$¸ÑȰâzLŽ=y1Í yRIûÞr²¤¸—oïd4ŽóMöv!©° CÂ/±3ø1Ã`;>|t8–ÎÕÇ[rwº;ø˜'»5[ÁPå$¬w§…B÷Y>ƒsXÄlÂÀðøg¦PX¶˜GõÝèÞ%4ùŠ)ÍXë‰C½P[S øÌÅz˜#Ÿõ&;º—ùûR7þ= ín…`q7œi¥ÃL—*¦K¦Gf â7v~ wù<þf·CÓ;X?_”9µIò¹ëÉÊ¿3¨êÕ‹ ¸=ÏnÆ¿Sp…‚ ÞÚí'åÜ0[Wè©+K¹lË®HL®PÅU17¹¡äïl_ÛÁV/X ¡4—^4mÙOõ%E¹‚96´Oæ5kY« î›÷ѵQÔ¢f[Š‹›wÀí˜%qÉ?ƒBa˜2–Á¾×¼H;i‘}çÊ©ôU@ÉlÞ‰wŽYã–÷2ÄyëAعÑ3dép C_ÿp çëwSÇeE’ð GËXPËcÝ,ž0†Bð˜fØ7ˆÂà ì&+Ÿ3…Ý×-H÷‹Ø³ƒ÷êY«õ±tßûiDlb Iån‚¨S„S,'K>.Ñ"×.rO©ŸÀM_îàºkId·™MŒÉK‰)ôï„ì7ú «  ù3Üøf!•⺆–ƒºc9¦OrƒÝh¬Âºê̵éP/]‚·â¶â‡.%Œ}<Œ}>ÉŽß:t,h)IÛmO£’¸‹ïÁ ¾tGì?Ì‘ý ³žŽPkÍ4¶~ñZj7ɳü— è4„7‡!é¥Ýï>¥…jp77‹Ì{dîÝ‚DnÕ.bhÌ‚§_* )S‰G¶I^& ?Ž“ØàD;›—æ‰V€Œ´}Ç9Æ^]¿®WsÇù3”ˆ£$y¹1 õ~aÂÛl‡ŠÌCÄÐá4«î'AÒ³l‘†TVéÀûÆ)tIz,s©J›é”šËtÍ_OçpuˆèÒ"F‚—´=¥ê«è³ Rp!ƒýñ{‚ “Ö"cÃÇQûœ©Ì!£Ú‚TÞÕ ƒWæÐ'³òɦ+ +WÃߨuDIz=yíû”qŠXL&6‹’òBdÓé4ð‰w'·ÜÃyûzQ0'™pÏh‡Cüô~¬"Ù&5„-–UŒxR¿óI¹y,qÑÊ!¾S2è°P4œœÄ²7ûŠào‘cx†xOêçê"RqÏ‚(nMAf÷tr0ýFkAL$Ú þ`”ÆÄÈ~<¦ Ц^ýË0,ÿ3h'Í¢ë¢9oŠFq_»:‰´)ã(»^€=M…¤[â+²\“þ×d¯‡í¤é-LßÅ 23+þ€‰…ga˯é4qÑkîÓØÓÒý-Ü’ ¿aÉšïØ~~r/^`öîv"V~ëqÁù\Ûã±àÌëïÌ-éÚ\kâ4éÐפ¿sF~bB ^²ºqÛá‹”lÞâšu2ð§~:„žNcž Ñ©óV¢¹¢·ãÝq²¡N“,Ÿ–OÎ$°Ñ³ÝIAßÔs͆úµ‘(õ³þ*ñRNÚ Îê³—Y¡þJ&sâ5ŽLêÛuû|!ˆªKÐËvÑ )´›8޲S—X0óFJ8œ½q~_¨gS~¬"žÎÆðï‚-ºMâÉúc¬Ì¯Wì®ã™¨°àóÇIƒŠ_/@×g] 1¿š^P&»¥›™ï¡\Ä™ú簲ͦtþƒÓàò«ž«sð>~ö Š”è—ͦdÚ”S4ìC–Ì‹¦²³îÂŽwþ¸f§8\š˜ÍhнÀsư/8œýx l¿k³·¶ê‘tù9”ÚßÅêWýøzÖ~ò^d+9ø£Š‘kq!WyÖò·¾ì¯àu´P΂~‘L¥7WÓë3ü‰JÉ ènÖ@û(K²žã„ñ5æ™’ ™Çã³¾ æò&ÜÿíHï‰ÆŸ¸s¢Ö(²LÜb}¶ÔD“j<Â8®I€UÕçñWº*ž”æðøl0íhûÆlŠšJ–_W@-/s9W WÐ?£Èõ)„óŽ¡ç‰;™Ð çBe”áÉ`´¨{ÿ>Ã_Å»hÈv†³íòKvôaMã’á•\:4×@Åu°Z±ù¯þ€ž§0Z8’•+ wðÊ™ºþLTÖBúÜR þUÏ ›¿%‘•ÃhPTÀø Î$êþt‹ŒVw {ý/<°F‡&7+qEÂk>cP³åÊõÄÁ¸˜ 5~œ|íÓ&/j_@^œŠZÀ?Ñd}©”Þ´%be¨{b·™Á”Ÿ}̆›Átp  •–Áçµè…h¶–f·%’<‘;àÔžO3nÀ)ûiŒ¿Â<²£ã;j«z¢üÒ »6\í ÄDÞ ZdDnÊÅYdOŒ}7–sK]i£Û22(Ón¤ qž#™z- ªß¡+Iêã»?‘ާ¥ÑJMº]ì=j ÄPñ¦dSoÞ|4݈c¯uøÎÌ[žÈV×Ç× ðBýjnª§ËÌ4 ‡§ÃûÍQP#ÚëÀÄi9 ÝN?°ó‰ñ›¹äÞûXÙ’Gn¡<¶xzßÂ÷ädˆ ÿ ›7ÐÄ,¼´=AÄÂaÞî”/£¬A_.ÄÓü{¿°Sj0ÿ}(Ø»GƒTÌ®ü<…OÚǼ™¦HwöRüœ{† þòÕe|iÁŒí6Fa˜B¦ïò!<Œ(Y¹‚:O2¼„ûi; Ýy™Îy»v<È&ë¯û³J|" éÄ77æ‚ì+U‡y_ö‘ÌårÄmïa2cëYúzKÇõrñê•%1‡k!4:Ú-^Ó€¼Ór"ÎŒµýP¯*¡üý)Ÿ ¿´aNSåÎMÍSŒÙ¡Y Æ§£BydßêYä^y,XoÇ{^«ÔI2¯ nt¾Š©ŽÞö àñ”Ì,>„=Áóf¼; †…¸Vß·1GëɆ•s‰NN3TÌ¥å¯1¡I†œØŒ7^€ì”'¬3?/‰³ Âíþ'¸!Ê ©ð‰TØu`ÕØº’î¬ø¬ào˜jxÍß0tÊ̳ÀÍ-f¼åèOK g9eŒÀš0ÔÛYNy=iNüjhÊ·§+ª“™‰”“¬) _µŠÙï GòNÆÔèûñ fü`1ö`,;¸d }°÷-㻌‡6ñ€DÕ>ø¾Éž;Ÿ‰£%’dTø$¾°‹fì:Ê÷ÈyfE“âOß™ÀY×!Àw%”qe4ÎxЇt áO¦5«IÔÒ»ìÞ/ñŒØº"n×rbJÜê6“¿wô‰…,ýQÞƒ?/Ž¡û;7ú~ð:X¨PőӰ}›½•p wÝÊ‚ºSÉìTköà©KF,3w¬^ìQœ¸+ÃÈ…NAò¶*“|õå#aþÏ¡µ| ©|û çþ%Öw•é:Þ$*#¥‰¡'¦â…ýì¯S™ÿ)´7$–E<¸ã©hnbIc…ïↃ/9k|xÈgkcI߯RÙ$1ŽC§Kœ÷Ö¥d^¸©HòÕLáG 9Ô«ÓË’©ö° õê¿k‰Ó:º:TÅrçÄFã±í"ä¾mm=¥„QrtåÎÌV÷׬Q7 ±[LǎЖ鉄ÿÃÜÈ% -Ø^mGu?­-ø‰?“Ì7L€À®&Øýí(ÝU&Ö×Ëéñr~vðí"²c{,šœ=Æì:’C\° ¼FÝÈÝ‹1D1‹.#Š{‹è°ë<ξ+°R6}ÚE´·e£o:ýˆ ýϽ\Hp®FuœX›AV®· Õ¶ÌÃmØ}¿‡Sþ4?Dޱ;bV£( †ï ¦.Cí6²öÝ=bAÕdŸ°fyÉôù….ô¹°Œn&¿°«92»ÇpnéuØ]°u6f3ô´2ìu»Œ’Ë‹!`L»£fÐUTžr›x`£l!ÈzÎ$z}g¡ëÔg&Èw*ÙcæN|·ë³›Ä“Ù´96E‰> (gâ¦ø7Étå€/T8Níþe`x‰1ÛqŠ;T˜Óï-¦¯¢g“ /W0óšÉFéZòibO¯; Ÿe2n½Ty8 /)<†Ì$.S÷Ý=ëƒá.q&;e§ÃøM!·.$=XÖ ÝÞþÉ­Ø8Ó/Àºúd( ZÊF¿s''Ž'@QïRøHE@cT:7tGëˆ*(yŒMnËéÞ¦ólKP89UkÈžJ›ÔÀUŒôìMdWP‘’äüpL£!sÐÀø!èð|okhÿ{ T~1ò%„ñ@eÉw¿ÓX¾0ýëÙÛËZ çy0ªøÎ>È߆‚-è”k«Ammg剹g#”=®ÆÛ⋨擨÷ik_N\à¢nûOHÊø†ù¿ô@}€E ˆ“¿ •^·”¸Ä˜0&êõ$¥Á€\ !/ùuH¸Ë^jœr+K!}RŸåâÃ* ƒË¿õ 1Rô>(Wf©‘|Ô­b >C†”žyΪDsgZES­m<ijO†¬åÏ0Ã[!ùXu¼l§€Ízt Û— )fƒÀÏVÜPø “4Ó;'bA>í8Dø—Ð3nãLñyAæñÅaÖ¿Ì9}ޓƼ䷷!Ù’oÊ}&¾Åã†ñ)_¸×µÁ{ ògå4RXÅ^>SðB|¬eŽÌwÇzÄë«5ø'cÑôzB *rpÿeˆÚx”Uø¨ð=tç/a_—Ÿ€7éìœó+Èù–R¦¨tàœßh3(AŸÜfb˜œj7ŸÈzÓ3;Þ2zQ•¬âž4î‘'ÁPg¬AwÔù3B©åñþ¼ùjCW,›I^\ÁÿõÝñT¿áûv$›½÷*Já¼ï§B2*’©¤4”hØ{„d”ȈÌ8ïûAZ¤T*Z’4i*ý|¾¿×·¯p¼Ïy?×¹Ÿû¾¯ë9×ëV>ý›•ÊCxo܆¶½.Óƒ×zžÀÑ›_Ùß+ ©»êqîÀ¹“°Åâ,Ö|aÜy•qæ¯%ÌÆØp2w«2Š4¡Ý’ËzþŒƒŽa²¨ð ñS‹"9ﲩÊE¦{úbñjüz*òΊôl©GÿÆrm‹ Ù0é9õò+^ÝI~lþÉæ5èàÕ3Ѻx9ô3™~¿³¹På€<¬Ùç3ÜàÓY²G §-Fiu5òkÅ"úÜÌVžÏâ,~Íõ\?Qü1Sq'—*+ÙéŽda&óŒ5cL¨ßÃtn“ 2þTc™i!ŠƒÁUhý¡?¾‹Á‘ᕸrÓßôƒY»û*+ua+èl‚ÁhiÐW Ðl)]z¤=²},5¨_e#§…ºA¤çÁ·Æhصå%“¢:NÞeJuçÓ5wnã“øó u0‚ŠNädŠ˜i~z{F8”œ2Áe¿óàòU<¼Sr>÷2'‰{Ã×ÕÌÃû­p,A‡n=FøÕ•Aïp0÷Ša0ؾ÷¶àòöf¸±"WOAÌ}`jû ,¿bjy& í«àj Ës·ï¾Ðh5›š~/ÇOÅ+HÕsz´/¤,§!AÏÊ2×(Òªwö0xÃ#EÁ}1ætU?Ë=õ›õ”w"Ak`t»;Ïÿ,õöÇ™åa´²_™†}xÉ Îé°,·4µæ žÊ”'œdÊßxßÏ#¸zA>DU'&ï½ñV »T_ÁhÚ"Ô”Mv“Ë€é†w`öÆyt¸Vœ–Mš~P>õ<mð‡¬C[±{öC(á BÁî¯Ð˜êÀ.ýXo;SÁÿm5D$ÆÚéžå̽pþè¿GñÈ îÃ'æ"ÏŽéNa²}âQ§ !©N «·¿dp“7Þ`w®B Ïä— cõ³éžr.\õ†±«³pÖ¶L¨] x­r/ г7s™É嘪#æ`3‚‚ö ùÚ£OOd Mj>êÎ&Ìzç^¦=å ››Îm¯Ji”s£k¥ ØÕÖ½Ø7÷%dFÌd·ø¾ÄþÑìž9JTøS0Šª¨å 'ªÏcÇ[CVn6a[^³7…¨¿F=ü0º··>Ãf_^;E…É0Ukˆ]éÄO¼Ï¥’ßf^X7;ïîÅl•)pØcC”r¹±$ö®b»ª‡á²]%¼ÞSÇ ðÒ;Îdy_>_›§¯†2a¿’Ù·w·cÐÙ´ö}*bÔXáô>¼¸ÎLâ£+ýêáeà1´dv°»LBÑöŠÊÆ~mò9ý´¿°q«‰ Š#ïgOèñ£à-Êmö ç8UÇB:Ÿêˇ¤bµÿ&]ù‡Ó9C"~lcØvöš¶;±yŽŒãÝ6ŽŒy#x\Ø ç¤`¶G;w¦x?»ñ«/°Ë/3ÊæÉÜéºòò*æêýáP¥¨Y4òÞnÞ‰âv!¤ýÏuø~¿tžü :=†‡D=µ¤ÙÛ&#­Óç<éÙ„›45ÉßËÚìù¤÷õ¡Ûarýwžxúâq yT³‹êÑiGÆàKXåÂüÏ+“-Šì’˜ OGá szB“&ÃêuDö„1öOc^©äB‰QOha“ïj‘pãÃléÍ^¦í[ªèaí«PZ)r‹Ižû÷-Ð!veʤZ÷\}\K–ÀiÞi Ðv *„¦Â\«”Y× 8Ô<ÿ/¬³Ãs>tez#«Rs7¾: }´Õþ èß L§ˆþ!ù¬^ÿ^ê€_Ì]°ªr„u«†ÓøI°º.>ÿ%Hn¬ªÁxWxøü:’œÉ–ôå®AöùT!R|.²ß™Qñç–•"‚ºk½áͧԔ®`î¿6ÔàÚÕ¬šþMHTüÃTåvãÎá{lÃÛÉhx6 ôáxG>†v ‘:~D³-5é ôÊ”¢N F=‚ˆÐÌÂå¼ wÃGŠ6@núJ_Ž&nÒ¢Û}Õ˜ŒAy"¢°#·uôsöX˜4û¨DùI’¨²jæÕ¸¼\ž_„¹íטWU¯ÙºDy2§ùz_Eïp œâ—¶î…Ì®3¯`ÑŽ8êøƒHÕÆvYûÐq‘;®=%ÉÉV¶=CBdê=ô¸<!›*™‚é+¨Å=EŒYèA¯Ü°dGàtólµèiΕO7A®Àþ<:ƒ¢óˆ†Ã+¶eÀ T·ÌÅyç4è5û^&é@|CZÍC¸é DŸìÌiä½Å jc N)Ñ–67²É㧨v:Ù¡ò‹©Oä%žó°¿Ñœ²ÐÿĬØÔŽ/ÜlÈýõ[‰þ¡b¸èÿ;–Ühj8g5aÛÝ?p8R™ >–‡ßßb ÔUl+މcšŠ­½ÅVH ãÇdÁQÔ:ˆßsí~êdrÞWÀýç1Í*šÎ¼ÿ™+›}‰mS 7'1·7JÓêÅ‘d­õOèËÍ1dfh"ÚÕãð…<°\V;؉š/Óžç:€£‰çû A¢f³iÉFTòªÀDó·¨°¢×äZ“’å¬Ih6úœ‘A.{ÏnÏW m|¶ÈÌ㎊ÇC%;•ü9g9‘ϪX?k„¾®ÀU¶’ô¡ô#Ì6á²B+´é'ÞNÐ1R ˱q´Í?U’׫±8eq¸$L?ù1¥—jñÔÇ8Vãc$Ž‘ ¡Z⬩†(½úª”ž3å·ÊC­¶äšE™]›cJÅhH‰…Ýwẓ þÅOêåaÜ9 ÝÎQszuÁ<ˆy'EO0åðLã4x¼sçhkD–ëÄ6)®¥¯k娋Á!<ä’Ù'ÓAêÃv˜¯ƒýiBðåt3FÖ•3µÏÎÂê~üb,OÃÒÒ±ßÙÍl„ ©2Üt4 ÄE2Àe·¶y"Ês¦|‹‚/!µMZ÷nb]È{TÍӥ罯»CìÕÇï`x±±1zG)3ÅjOäz%\·n„‚¤k¶ÝÍ7™ì¶Ó$·ÚOq/téS¡Ù‚´zì4º’»ø¹Ý–kfÐÁwçÁûfÞ—÷Lë:;F‹qüSˆ{ \1ñݼ\ˆ}£Dÿ¤ñžÏ3ðæá'ø9À˜{Þð*ìS<…'œWÓC"VTzæ9Xp/¼;Ãq™Ír²|M;f Ôây™ ðsº‘¯@·v$Æ•c Ñnf}w ^\áWƒýi5Ê×¶—x•âœIû4…T¨¼®6ÙÚ±¥ÖÀ’5Ê´nF³öd)<»‚É”åb=°ÊßRh_òZ²¨WôÜ]Š|_U¨­’1ñt)dd]õ):YÀ¤ÏÉ,ï†óÌë­|D£;ß݈£c)Ïðêñ|,mN¼#Žà´-¾ð±Øš.F /™ß™2ï½™™›' —{aî~4IÍ Õª °2{!ì]J¸ž,û"âã§Ã´Vdw¨0Œ, Å9OA$Óû;f¢‰?ÝR*ÅwLp´£‹VéÒym ¢lµ9h³™°µ´AX’اvA«¹1ÊYµ1=«a‚9I]±• i7#e`BÂfÜd|²³kŽcMš&Ÿ8ɼXBÛý´¨|€åîE“wÛ“ÉÞôñ½p01ü†?Ì€š±fö¯­¬Ñ¢3°A*Ÿ=øA œš®`Ïu]òñ‡–¡vf«k ýbï¾$]…°e CžLím\+á„Ó¤Àå3lô ‡|ÛÌKC$íh&¿ {êÜUÜÆ!Ú=SI)׉«ï.ãcvਈڪï¡Õ‘ùÛÚáþìÉópÏ[]´ÉE®k!£údg13OØsË`®©E0t?ÔÕžg<Ïã¢;p>ξܵóÁ«Þwáll$Çï CDÚÁñƒÚ,Gy:9/n‚ôá“ø¦x®ŠK³w,Áhcv¤I‘==ºdʱև޵ ŸõqqÒ#0‰“¡ŒdȪ;ðrîöÆžsÜê½2\ÅcÊÜsb;zî ^—Æú^î¾t”¾Þˆæ3CÈšOᯣ*Õ[9îX-qÅÈ­#ŠÄ§<Õ/8ãË7¼èúê6”¬*ÄuòvvAŸáÞø F㊡Òû@ÊYŒB¬ðZüCü++F†*ü.ö§‰ß;˜ëO²_õºØ‡Rt4¯Çl¬¨›¢6y¼c_O Ϭ…‡VjP0®Df×cäÞjSëd–äÏãµ?9åBÙôÖSz[AˆôC«¶°JœŸÊKhYG.–ïE>cGrPÝVïO£QµÅø©E“ÖUW€6mEÉGäp%/Ý%¬BN…ÇÁõGP±:­⪛*wöNöÈO6f대ð&RÂçDÍûa÷A2µ8$l¤Ÿ9î1‹À×k:«ý&†LŽcÜmŽBó½x¡* …ùñ½•.ɵà'29gÀcÆÎW—,˜ŸüLs›vMN`tÙ0XML +*¤CÆÑ¢÷;ÓÔ|åTð1Ï7¶å¾ÛÕ›@E=醗O¹æ+FàÑøÚ ž]Çt„è›=¼D̯Ýöø1™ûK9Êü ¶ß‘2R¢ÒMƒìɘ[0¿¾ýo¨ 9^È®‰ÃÂÂwÀ[ŸŽu`Öõ½´H]•Y×´èʃȯ~ µæO:È6v“Ë÷®ÿ¾wlã`–mò¤I£²¨ßyçüd‰’/2÷Õ^¢HŸãO­Dx2 %cçX).œ8¨†î¤ ±›éâ-ÓHÐÎt» ×¥aõsj/LÇÃÝÈ™h &WIŸ¶ÜåÁÏé¼Ô?ƒ‹ •ê¸F"ñ]7Íß~º¿ŽàkØ<ÐÈ]ôBƒ,ñ¶¥åùÓ©×$œ0̪íÏ&.¾>Äe¶>¼[ˆ'-gÑŸº0Ó+xzøˆlK"~wÑÂ:¦x?îÎs{STÈßû'ÉLn*Dš†£.——½¯§Íe{àËÙ-Œ×p&µÿŠY{ˆ~ð6·y”¹ViG·}f F—¡TÍoLZ] c½A¹ÚVüÂ_gncmo=¸eͤ¿ôo€Ak!N+ó¥\¢O¶ÖËâÒ+™Ì “vˆÃ̯s¢ÄÕFòŽòP³¡br1x yû<ÔŽ+ Á83OpaãäDêÙX^>†óÚ~”µÍk3!úy!DËzáÊTU˜B»Ûµ™p-+2®ÇG°K†T…{Q)ÿsX5é[d¥J^”Wb#;¾{{˜št<¥½ƒ¸¬œBtåÖáÛÃ30šcMJ½ÃðêŒ_ÀyÒ¥=Hõàit–ú Ò&÷à“ ‘å] ^!ô¥F4‘Ýw"r™»ŸcÉžno8šv¶í«CõHc2ZÏ2öä×óÒ'kBd¤ˆ„‡5"²DÜ!B¥7@:â+z}[F?†`¦¿%ÉZöƒ t-#ÁЦ$ç€鯫0wý¾Á–’#ö”ž ®ß FW΋e¶ôJ³z~ ¥†¤&|Æ×k-èÅàY´°A›JÜ Â§ ©zÁbl§@G6_¢Ž[ÒF#Eúpm2¹“mGL|tqNLí,d ÆpÏ }8Y±šê]Ë!%áÔ/áœþ¹Þi/ÀëÚÃðŸ×úzËLò…óÙn•Š,±ÈJ¦íE?iàŽ‹00{¶8~@™}#ÐnxbýÅL°ÄÄ>ª‰aÂ21OâȃuÐ×݆kKñâÍN¼HÔé*4£¥©ä€m3nŠdöȦpJ{Õè‚­E$|¯QøRÁΙ) ¾Š`À椶'WDZN{êà¸è7t)I‡À{° †C«dál¨+έ$Eùðn=?üå/‡‘=¿1Þ±3˜Ã`q¡ ·¦^Â(s2Ýs̲Õh¸„™6ÛOžIdÝWkÒL³*͸òR{@41‡»€´ïþ†Ù »Ð5g øò“üJbùÖ z³69Òo@[ø (hA••ß±êÚ¨Ú æ}?ØoÕÛñÚTkpöùÅØžŠ„ðæËL`Ë[H• ̧ ãߎv|a»ÎÀSÅLöbe"®'K¦Àþîsfµ¶,#æ'OÃ}æY©>9#s }ùÈ\ˆZEO^”¥ …棒ͬ¸˜B&…¯¥÷jp¿Öœ»q)élmc˜¥°"¯ˆÅ}b%WŽ*ÍÑpQf“žöc¤ÄsR&êŠ5ضâu” ÂcÏuð^Íë/Áþ˜&ƒe:vôi 3tT™œ¹$NŠìßál÷EtÙéûìS~:½o>ÆNRÅØ_›™ë7•È1ípÇái­lk”­PÝD‡§Äý8VÆÌ®Õ¯`ÊÞN%Én @ÖÏ¥r7ÛašT2ð³tà¯?í÷åÁ=OfR¡ÝNèî §ÂÄhÐÛb0ÜLDV R'S¢zÿ;WtŸ%•sÒ‚£xmbZ˜ú½¼Ôf¾>íVІKã"O¡H/Ýn±kd2é»kLÐ;ªc÷¥“±azê ¿h@n}<‰5.BlýCZ{ªŠBÙC;·0G›l8¾k݉î‚\;÷Š2þý #9ŒUÍÞC·øÐðB¹ÿÁæ “M¿*‚->‚ Ÿu\Ôî`#ùO1ÜFЖ¥î9ÍÀ}5ƒÈ¥ƒÕúüÈaÞE¤Á‚qðtÊq¶Ca=|m/ÌÙQÃÄx74nTô¤q¿¢ï™ó¸V$­ô>î[û˜5F ™>a¶ßॠû65}O ‰¦ÀcüúSšŒœ¯g$·YrΟ¢Êmp÷$¶[‚·xDÉÐjUºúé]•€)î"d^œ"úÆ.b«ÊCª±}ñ…óõ|îm,ewÙM'†_Ô‰è~’œ<‹Z:¥Â¤S;áÜ£M ¿”‹&“=‹þgçïƒèåoaC¡9ž»T‡¢…Év¥¿lRµ<‹¼k÷LGˆ™±r!ÍØJëçpéžçðMþ!Ž ¬Áª¾ ƒÌêAζ•;Álä(”÷ ¥×ch”£Ý–2˜d÷Y™Õ9Ïæ×Sû—Šä‰ß„æ^A&gÖ”nÅéÂÂØy¾Ú5¢bBG-0Ò^‘:X>þsqì{½2h5éáìw\J^ñÏ…-Ò3Çk Ö@OæR¢èÝÅ,X_Í*æ8q‘C½ ÑåDžX?Á-ã݉m¢%š?ûëChŽe+L¯ÁÕMذ#ÿ\Ç׫Ùô|c2eÁ曽9Õ¯6‡`·O¸ö@2£·×§|ŸŒ"~1%sîám5âß2ýwÿå¬zŽ·‚ïKkxõHƒÄd¶ÃŽßÁ„G‹êC¿cé·TÔ㞆C•h²Œ{ÂL}¨É$ÎîcºL A¶b9üH— |Ã'mY¸ÖÈÁÉ£ØuÎ Pm`Êß­¦Ž6·8þØOÃ~\1‘{í+ŽQÁ}KÐvûi*sè!¤MÝAÝy˜Ž\* F:W)_P">M˜OÒRäHŸÜuЫ¯ÄŸßD™¥h·þúr=()5‰áe0ãægŒ{•ª÷ ðåVEr/ó öœOÅHq Ö9Ì^hr ˾©_…©t…´6ÍǵŽôu’4ò©-'ÓÛ½èñþH*6–D+4IOõS,»ŽUß²p‹õ=&x—$a#´`k° }qny”aº:q¨*fV½¨€›ß¡§Ä‘ŠìX@<]˜“.ºÌVy!Ú`º5þ|4‹›ÁïkÞòÐGë„ v ÉѳÂÃÒhòÞxºyc>Ñ{;…|IÁ´ Óh˜´,yt¸²÷ŒE!=/³éÌ´Xb6—>Tâ!¶ÝAÖ@€¸„ŽNÇòz-œ´#—î˜IçK‡²joâí¦ÜH†–å&p¾½zWç@Ôê•xK5?\;­¥²È{£¿öPLLK¢Øí¡Gÿܨäê¶¥4úÖ×Ù¾ã‹d}£æ3ϼž³ÇFÞ±þû ñહì«è€5»©'~Pã­¢„c{ƒ>ýOÛ^kƒðÊ6ÒY“IüŽÄϵ»Ç[6lbžÌ}ÀöX‚õ‘ŽnÇÁÅbÙvÀ•úTI‘-µ DøÌ<ð=·–¬²^ «´‰õo>t?vZMHÀ¥flÊÜIÿóõoïØEfK&ãÆ“á‘ÿ1úA¼†8zz‘ö7¨=ÖÛ³Sà°øv¦»0 ó|ž¢fY*γ 6Kp¶L2,\šÉ­ìJÅ vùƒGxªþ*.•ÁͧÝèÓÛ»hTY8´§Óîí‰dòÜÔt÷PmzL^£ÀMN/-·™o“çÒ#¾Ô÷åußREw­×bT/|Âç£Ï9ŸÞzÐæ7[¹e)Ó°óf2†¿¯‚Ö:3 .\†ùñï1=]”ì²ÏA×f8w1õç}Œ¾bc°ô°!Ý2§Vö—â0½j½ÂI‘Ë¥—à·@%Ë)¶åf‰%’üiíœ=ï¿Br,•£Û!d]65ßüÑšàæÁä^lxßP&÷GÓ‘mÌÌË6˜¡Ý§c£ˆO¦.‘ê¹Æ=¿jæ…²ô)2FaåHב*Î …xê} ² Ù”ó/ä·`ZÒevwͲpßw˜ëqŽ,÷CYs§òñV˜Q+ò1–Ìä“PoãHjYŽ‹´¿È³l$­b™®»1x­Ö‰¦RéwIð¸ßžàá|Ú©­FmT Xru{â4ˆÉ’g;ÇàDˆí ˜ÉøŒ3— ýñÅî øË@‹šÏ8‡Sº§µGà~K$4jžÇ#—Ï#ßÌÏ̉7QúôÚ%K;fÍbÆ´ÓŠ6Ñ#ûçªX—òW°JøÞï~Vr-fþœ]ns£U5èÔO3ð·#IÛ‡YÖ»aâ~DåS)im¿‚•· ¸+Ŷ3îêÙ¿†®°Eý%ÝÑ»‹dÌrB© õœ-'H­ YžðÎËVcF R_±ýÜ|f¬“"›oñ¼ëü]Š-óç9‹Ÿ ¾5õw=¤~Y}`šˈóPºÄ²š|_‚Ä*½a S—Ѥ}«o± .Ñ.ƒ¾ntË;‘<ÜÖßJæ@Cf f³ROü#î©q§!ði]²&€|Ù(@â>¡e3¦’y9x-g9$–ДœÅäŽëG¼¸Xž$y_%üm„ˆHhÒߎ+HúyUfÃó$e·!Ý-[JU…°<¼Æ#hñ´ë,†ÖÒðŒZ’wê)L&EF»Èb·ÃäºZ QÚÃ¥œ6d—O „ŸYJ#³„HóšC`½8šn-[Nƒ'4üÀqÁ¦ °{ ”ºƒë³_€þÆ2;'?%¢Q^pv7iý ÏzbO¸Ê–L›Í6{ÃüJÌj}ÂV¨nfþÄÏ&ƒxͶ>bF‡£ùøn|>5 Â2¯YÙÒë0"û‹Ùj¯wt¢P†:‰.ÃÖEOÁc^"¶Oè€ÇM·¡Cë< ß<wD)P[á¸ÏÝ›:°Ïñ“åfþ.’úôÈÄwìüz€åUÛΪ޿ ªs ™Æøz”¨$Xýh.}¹» ©lãHž˜„?|½ðûjgf÷-üs/{Îj:§”ƒ±Ú\´ò=3å˜*ŒÇé±ÚüjÄZ¸™Øl¦×Ig²s¬﹆“Ê»„æŠ,$‡\sE„©_["¶º²ÏJ’ƒ¶wáv…ý•ôÜ—º‘°íÄóˆ!•8ºDo`ïUÏ¢Ñ!1ÔñËZ3_‰|^PMfÔ‰ÑãÕ0óÖ1¼¶Ô™)» æêv1oK%¨ËµhŒÕý KæeüÛg3#ÕŒî”õÐßF§GÁÃé xδûKfRŸš8œV†þ An<‰ÙòóÄ U£³‹#ýöYx¬<@Öí… {œ´º’L—<†•y½ðݨ®é–´v]•¡ëkМ] ñØèeÐJ·¢‹f¬e­½ù©zßæ_%™ qŸEÊ¢ìÈG¶'T…Z ¹`¨Akyh~ÁE6õóœóU÷ ôÀ‚‡…lcŒ(#d`ó2ŠBWM7³ð<Â)+j6*C¢4ȱ—QpÄÚ˜„–·0&ŸùÉa^K"®éìB]ˆyüïø©‘Y|Ö´ôL/Ö_iE÷cl¿Lä½§(~à,»y žüç™vZÜç®b¤Vô„õ`†Xf·ƒ+lwûAÔ‹ü©}s‰ìYÊ>Z¹Yë°g4˜® ­¸NÊ—ômhæ™”“[…k/ZÁôeÃȯ6 Ϲ̦'§£[¨†vJ“¨–|ȉJÆÀÅ&ŒØ+2V“Kÿ¿ú¦xÖ€ ãYÇ´/ÛL>l#´G n¡êû(:¾…$MãE³·ñº.Ì%Š!Q}?Y}ç=NK¹…±bš€ñÝÛ úÚ ìØ0‡žþ¶„Ú—®€Ãª_¡ôbŠœ !/™Å¾÷pÃm"ÆÒÛúÏPx¯üiþ’¯ Ùm"Ÿ1Öÿ<„{PÙ‚8>:’oW#ÏÍyô|ÆwÔúcM}¿ù€üdTŸÐZ‘NdÃgI¢ãɇ% †Q _åeˆŽ½-ØdHr6w‰ñPŒDKRìÐÉLàcÅùȹéIpªÀîþcg¬˜Ñ¿£Œ¤ÕLÄ—©³qÑW¬üýÏ^|µ3é†#ŽŒÆÃðÑe„˜™0Gú-Žü`Õ]?°éò™'_£ûU êS´5;L æärÚÑu ôl¡SŽÂj7„Û/èÆuÕ+OÁJ@-žÜ€¬ÍÇà¶0ÜRyŒ­ÕñØóS`wלè,&þK™<ÿ-,Dæç˜úª挽†Ôm"䊘7Q;¥L‹WÑUþ{X™ßëHJH(xD>eLÖiãÉmòлµœF­`fŠ$M>² ~P–,”%Óg:ÿÅ¿ææ*Ìq}Åh ÓU*瘄ÀK¬ÉoSFÀH‡ŠëI’•; dhOº&¿ç:ÉÑ>Þ¤'jA“¢Œ!ìíVAw#'2ð=jSŒÑÁDˆˆ…¾ƒ§¯OÚ]¨îG{1ZçÈCv8Í"u:Bt×3˜_ïÇT-Â1e&;ö%{1Ý‘4?9›Åv“óÛÓQ¡2•ÜØk®‡Œ jÝå¦B0cý#¼¹a j–úÁGSK"ÿàsúz1~\ÞÇX<í$±â/™}òÃð38ž ÑÝê4¼yIìd÷Ší½Ð,™÷ —¶+æõÑÿòßD -› _è’.Gs ÓÛŒ@ÔbÀóÛ#´ò•¤á›žÂ™m¯¸ê?ÌÈ­†\2ð$Üä—vk©d%b”í9Ø8 žÿ#gÞùp:ÉNÚô(⊽¨ÁÂ'¸sÃuX$»š­=í€'Biè ’ëÛ ðÀÔo÷C?ŸÃ7?ºK#”p~O…Q?æ…®ȃÁ£G1²ßŸiŸæFM&SéØtpX ’Þ:ô„î*¶¹€Bæ 1ö÷ô£Þ£HÚÿ‹õ¸ãvÉ|¤ú›UØ[‚A=]vN_ŽÐ¢ n~¸5OÈ·Àÿâ7˜z+Ý[ït*²)…Væ’%/À³­t‰y+*T¹áû» ì¼C’Ôõ'ã²SK ¼Àáè~æÇX,Ö”z°m¹NDhÎ9X?¹Œg{“Ci±(2‹ôT»’Íùéi+]xÇ©ÅÝ8p‹¥{&B£ëh[+J†£ÚXÙÐRVõ:/el[˜ƒá}x¼[ªè!Q/¶¤¤¾¶eàˆÎW³ññÿ`IÙO9Û µå"ÊÎÜÇίR€l“MK¡5êãÝÜ‹¨ oIóæW#¯…<šö…‚»Z>æ¾XC_“0+w7[ÔÍ)UØ~$=—’Í÷¢SÎ ™WàçþaøoÎôv«@nNHíâ9Á3zÙý“û aQ¿ÕÑW§Ã*Ù’Ù ™©WãÑá± qîŒÂø“èÆ¿ùðà™"Í{kišŽäzéiø¤fÎŒKZ»ÌeÆñ:2ØÚÑ]p‰øBóP?©69iQžÉo˜Ñ{Ylvƒ½ñà>ûÐz+ôËð1æ'þáÌgD?Ï {-&Ó€þ-¤ñl'¼^ðtðl_; )‘ “Σݦp¸ÒÁ:'Y“qg¦iž=V´ŸþŠÅ/wy¸-¦¨‘‘êHƒTb½©Ú¾x5c!“&'g—GÓÁ%܉çäMÌÚWÈæ9mÁǺ}åÒux3ž¹ÿ H÷òxºæõe4*èa4óÐäæl¢¶-vì}Äæ–1ëËÌØW>Òž>\È„ì$Ŷ´RÜ…k2Ê0cýH4ŒÆ¶§§Ð¤#Î.å%ßÞÆt!.GàäRæK•8´j…ü«ÿ[&³Q“ü™­®‹èëYrœ'‰2€±)Œ £ 9¿«ðAÈ)¸¦hNâ}ìÈÇÒaæý”LË]\š€š—$Q±ù'áü/ɸ^Äåy!OLòÙö€plH㇗9 ï|‰»3L˜ÄD~fî”cSµû ð‘§=¶å¦ÀhÇ0£6î AÄi[èvÚ7s˜2€v\Mæ×¶›Œ¶åv®$«B2_¡m¥3|Œ;ÎkU0õT0ýâð/ÿ}:Â8j=½ ¿o6=Ûþ=Ä÷±Wý¯ã¢å5 Å0ª<‡ fuOœ)·}¢¯f*1˜BÕ&tçS¡D išA+Q…æ¯drê<8i–Aì÷YáPÜ,‡FâWpÒ‚,2®cÚ‚&ÓNUÊïmÅU~ ¶!¦D*ÝFÌA÷KÈõ¨æžTîåHéÉÒu–qMeÛ `Q—$¹ÿ 0ÿ;q »Xâ0Ép5&{Ÿ¾C‰-ò´S/—¹sèóýÐ6’*šµ÷¼Ð°l {äÖSæÑˆ9|âÁþVÝ,{ÙØêȹ3x\ýÿˆäù8Ú}²I‡³”xÍÃe@ïïq¦QVêÌ~%ø²qJ×¾…‡§ÈÜqyüÏ‹ìwü 9ƒ‡åÚaÉÓµt¹É|')O,Caö³AÆþoq ýÂÄå. 1•UØûÇ„l z‚fÏAOó.&w÷n:!`JV,Ôð-<:ç‰eáý|V±NýT¦ÚùѰhyF!lwÃÍž’4èc ®]ØÃ¼,'q3 ´å'Р±£øiè#³¤‡©\"ÑÓs™%ÆTTîFÌýÅö„ýÆ5;þÿúTcI‚´îK Ö?…Š)ã¸31Œê-¼l øë'%GäàÍ^*¯²†Î;ÂÝý •^“ç‚DF€Ì Ò!*"xÑècÓÑå÷0–'†ÚET°û—. ç|6Ò‹FÛ!m±'ûÅ¡â_ýŠ4a…×P&d<¶$Dì°³/þ…ÛµqÜE!ôj2U2U¦½^[èæ5+pÅÚ錱_!<¹µ ·:;ÓϹf¸³•Ýr/†>Ú¿œ–’¨‹LW}³\Mœ”-[ÃÌ9Û‰B›©Õ÷3è¸;ûü èéx2›BÃ;ë!poØä?v¡íæ§ëúZÙ‡óösî-컨f T"ÃÝ_ã­é¸øº$À‚hé»’†64øæDÞTç¦*ÒÉç(Äm‰ÅEo‘{üøÃ°ï6ÕbJÙ>Ð_t޹ÑÂO_ͳùwþ%\džMû̾ÝŽ÷ç/¤fÞaözb¬½ŠnN>ŽÙßΑoyIÐô;†¾¥²ÍeɨÚq(`>Áåe…ð¼²­é»xãçøXѯ²\H_(³Èd Å݉*Ÿõì‹d³ªÓÁ°û>±ä¡c³Ã¨~EZþ=±«$iÜz„¬ cU¤ÉQv_.³Çˉ˰îÏ.ÚὄnÂùDIs.n_¾ŒæÊ áææEŽB5óñK³#Ęäœ×‡IïË&rLò¸èÓýƒˆg×÷{Èa(=ëT·¢<ÝAcVÒÔjÑùOðÓ½²Ï؇ÙûèJçÍ´ËQƒ>ºÑG"$è-¯qpÜ*L΋ǃ‘]™±/—œ.!¤ïP£ÓEd¿ë‘ç ZhL_ÝM¡ïT-±.W‰˜‹êcø&{rê}S“à½ë »õ>áކ3Ð,)MRðÎÃ’¨Ê>õ/¨zïèH^"^žËˆ™Úfx¢Ew~z ÝbQ°›Ïf¤ž§ö¯öƒ•(_aC*g´q¢Ü®ãu»i¤ÍHìü´‚030T‚ã-V$fê;n ¿}ãS¡µA lœ§Aíª¿‚ÇÜp´zfû/ÿMs8snŠáЭTÛã<ÔÜÞÀ~d7¯'kÈ‘Ý)$ü*äòÕ’ûû¶ù€M+aw´Œ¢Õåédaš Þ~§¥Âªw‹ñγohña=(L+vî.*‘´‰9é¥L>ö‡wÐá´5y½=Ä„!¥‹x8îƒÎÓõíïÙ«6+1dG(¬¼ÁÞ\[ Kn^ƒgî} ò¦¼v(3·ólf3 ^ŠÂü‡üÞy…³´º×Ð4…ç¸P~ > ÎÆ9ëÇ™Øåélÿœ;¸êú0àñHvç™Î’äpô˜w·zÃ8k3R9¹µiîhj`¹P/Œ-yÈH ÝÆÂ‰|䨀‘ãx()cDm0%Ü_Z”ኳ·àpk1Þ9г~àí9YÌÿâO1·Æ„S™è|ºzG3¥}‚°¶Ç‹Ýóé.·ÆÖ›þì]VKð¸ž!9RÇ™tªk´a:~1UÄq³ vCú+W±"VL"vhq`fÛÆb4wëmÃp3V™”¾˜SVÊÀÐ~:gW&œÂgìpÞx”^Í}oQ‚òRizcj;ó¶(9«6…#£aÇ`,f¼IvÄ#\ /AjíšlϪÎjƒ‰žå3gÔˆÁ=ÏHæzõ7v¥‰©¼x ´—L'rÙŠ(Y?Ñ÷ünÃô¾ù°M;í­J1þÌ8ffóý‹ÿ›ýp8"Ÿ™É} –/‹ê+dýÈZ,]yÜmÑì¾.[o‰Á˵i©+ÍRŸDV¸nÀˆ!\¦ZÇ(\Ò¤æš{éî.Ox|#­†NÓüQÉ¿|[zŒÉzýÃî[×ÛDvÁf.ßÌ-œi÷TpÒ~?Ô!M×T¹‘4/?RVdIßD’+?Üà·udJŠbÈ]Aeí½1O@&erìuÁd#k’|/{(kjÿ»—¹PN9òÏÒ†¿¯è±+„&±TmÃRr%çe9Äö± ]7ÒIÿ–â¿ü²Î‚ fZc®Ó¢H²Ÿ¨‘ô×o8Á‚ÂdêYij¼?šíJ½ Gý¯Amw 1‹_FT[6ëò6xj¦N®åxâqé:f¨'ºDç‘û›ïCìÿ(‘Ûêq’àÊ}ü¾óÇ 9{ê2_‚æÉ8fžö*ðR¾æŸžà G ÷G„¥ÆÂÛÉ–x´aI«ÐÂßd9•³ýÌ“÷r7:· ;?UbÈN¢ó£™²dwéN1¸)èÕ%é8© å¦ÃÁùLKZ yžäà¼%¸íÛÆ6ÝoQýúýOnë{VÑÛÅ*¿°”ÂÈž0uºbƒY÷½oL×"C ÄÉÈ•qVõ„'γ‡^ÿä{”]¬>±þ¹Ù(¾Îž,ú9êû3Çã:ñg²ck(Dlg{`òV’žÝÆ0·‚¡5'–x}vU%²U<”Ú•€Î|B†¦PÂ’÷Ý5ÔÅìsk°ž™c4?/þ>¦KK¨I:ÉfmÕ «’ýàÆ–‹Ì¶w×QûAQ(AžÏÑc?9~2™:Þ(ä,>r®Ÿ1€`]f²?ýálQ ³3)¡Nâ ÿâÿ'1†Ë_˜ŒY3:àÅ 9rgÛW,YÙ€¦¾}ø«¯ ­Ÿ¯!~ÝRÌŸUå0 ¥D)Á‡" z]-œE…Ó"t¯Àkr9‹Û—IÑæxwò åóÉc“ºS!îÇMäàV"ÎWóZ/SÐ`ÓüÇ.T0Öú€#®…àµm%ôª`9¯)Y—¦AEã.±ÚЗұñ’ }¹¡fB¿¦0<–Ò˜«e¬é›“Tëï ðžZN½ƒž}Íamn5îþR7[8”W½”é9ÅŒQ&I{½ØŠŒ(ˆŸ_‡®|Ic fÿ øwþw{ô(xnxŽú^SH´º¥Ý~ñE4èwª7gáý›ôž£ñ÷9C-”³qry'n:SJƺ‹¨ìºXêµIŽÖNÒ£«þòÍÝ4²Ù3_È]¤Û> 2è ;“<½½šl¶p#;?ˆÑ»Oæ¡„þqÊõ´$ÓÂ;Ù¨Ó¶ô¥ÂY¸ºóV„6À|Žæ_Âñ'ºÀ{‹0ú®ÐŸ|ÿ¢Oö{ÿbWfjV¾‰J5Ø|,aœ _áŒæTõãa(šü„êwp2¨^Í'×Ô é:ªˆ¥¦/9eëÓ)oCóÕ¬’®·x k~…sìãŸýã?ÇwNf¶-|ŒŠ6ä³@ïÞ`¶óðI ¯g¿>R‚Ãó_‚RU-ÄŒ„[cÑ"yM‰6'êUÙúS©fä ´ñ4Býlä;öJi¦T÷£×!âÕö_+I©£Û0RE»¯ÿ*6¤ÐÒ½™Ð²(5‚L‡&§f½35;Š6ɃQ½än4ÂãÙÏá»°& P¤É¹Êäµ¢Zvì`â.` ŒÚW˜£aK>|áGý3ᦂOØý{Ü<‰öŽƒüôkÜU™¬£Q=èŸJEÇÊÍ,¦‹ÒkW޳·çÿÿ ¾µo;N‚‚ ãaP@Å–xÅSŸ]hœåNCù–’“%çØ¢Ûßà–„UéëÆÍ•€©·FÁy» •|hIÜ’“IÕì.´D;52ÐÔ?½Wàö»d6:¸—]ÿüÆ_1'!?Ö¹7Ñ@R˜ê 80×ñ£Ý½>Ð\"€{WòbŽáJš3Ó CÕbî§ÁµÛÑL_ŠZºF0ec°dýrü¬aJ¿Uѧ‡j™óJñ”URF6B‹N[›*ôiªêè¿þÿÀ߬ã·àæ‹¿è‘OÙÐÜ4‹ ýÜ@w‰4Sf¨ÛµOàAo:ë¸38ÈË­¹‰ÌöVÐ ¨>íÃó ÞdÒV,ޱ¥k¹œŠÄÄ%ìc:]'’}V‡HøDIô"Uמ€Ê5sä Å=·õˆkéCðÔhÅ/11xFÚœ^> OZG‰ïãÝP}²Û²J¡ðíö[Ï/FxtîËf‘}M×hà ’­êEkü½à¦äe¶û‰3•_«‡fÐîRÍäl¡?Ý"äñ…¸t¨sçÇ\óth¶ €«ê ÄËQø_üE-fPîlG$4b¡*ì{s…𶛂ÅN}üú(„«!²"Ò¤~çBåšRùÒ:“ÞBÿ~F…¹™9—曄‘ÓÍ÷á˳CDj9C‡µEQç£I8ù§^VÆO Één5úáéO<9Žpu^ ý)Œùã1¿ó`£K©ù¶ ++B¦UŸá.ýL…w¥°‹ºó@Ü׉,Š¿ÁîØ.M<߯í²ö䆊êÖª¢qè=<¼x ®;Ï:.æÄÌùž}{Z÷¨²jûÁ% ×X³èò·.§¥¾£R?ãÓ¿ø{ª¶`cM%úYkAØKÎpº<æž¾Æí¨Kø­1h"Ï{Q¿¤drZ.œY£²&`à"m8ç$I—,¨¡ÕûMèï­'ÐÞN…–½ÆF³ ti«%•%DËÑ‘Zé3óûÃ9¥WÞÂÔC»è”“¸$C dwõˆ°&·ÒTô® ­·mñÛi=ª¬‘E‡¯À´Ô³ØÀÁºM`ÀãJš=µÈÇ·AÝì-~¾ŸO-çÔW´#{ó ÈOºÁȵ(áëí7p­}89ãMs¦©é²g¡¨JOŠ ·&¯B%ËÓàsÎð_ÿ¿µô%lË1f'Ö!“âó71ìÇ½Š¸ìÀ)¨X~ù/„BͽpeT婳†+ñçd.*¾T¦í¾ÇXçñ`ðÍ[… Ò…è±¼x\y£–Q=b¢‰ÊÁ´áøRî-c²ý€5:IÓê²g\ ÿ»ø8Ô›…Éê§™Ð{Ú€–ªYÓ²»ËH©k#FÔÏ‚M9ÛhRË#8k­BC¾nf–©°Ù@Åø þ›û MQÓ©Rþ(Z6Ï$ÂÖ3Mïb±­ =ÈG-á/xèacCçHí{vê†6M˪a½ŒHÇýÛÿíÆßéÃøeŒ,˜LÊ7^&:ÐsÛ£q’&ñ½ó»/‹`†L-ÖÕ¯€ÿüÑyϳOƒ,ã ¼6ôѬÜ“ï|*Dâ±$Q ;âÏÄÈäk³éQ“E¤ÿ&TJ݆†?¯¡KN§Žˆ’™+OÓÿæMw ¼[]¶æøQnN¼k§Áæ­?›àŽ#M‹ú„u‹²@9A‰•q|€&šVDýy>ZUé!'S"c @MYq«'áßÓ‘$*a y»šä{;Ýã¿5Ñ,žã¤õî,P¿—Íݨœš*ÉK5ùwþk}Øý³´‹µ^]H<_räJ¾óž|¶Ss ÒŠ'©CQĹ\Šú诂Å&‹¨ñ1+¢yühá¡}AÙï(pžš‚õ¤7†0pu9QÜ:Ï'fÀ‚”X=c[W [,玧ØtVmÓx?i-±²4¤­UTHü)³ïMáÏÇ™ "…°Ó“Ÿ^i†¢À*¾ÿ<±“Ž ÔÒmVÝÄöÆ2|¯ù€)Í‚Á¦+œ”@ò0Ó…Ü2éÆ&}*uœÙ÷u~øK:Þ5ÌÇ"d…©Ü\œ±Ÿf­€‘§ þíÿÕá^x±ï kdÆO"žÁ¡œ³ ØÓÅY(ŸÆ}ÚçŠÖÁˆ¼"Ë®£aIY˜‰žüy=yn6Œ¶ÂƒìûóâøÇ´;e¨â!sp,ÆÒOj6¶:ŠØ•³xÑp~&š>¾ÎNÕv ‚G0ìì÷Sz>>û LŽ bNN%Ž4%å.—ðòb]NPt$ëR^ ¢ŸWѧ:sp=ïiz}¶*Ôl}Æì^@¡¥åNQ`«4²\øŒ+yâñ*O ˜ÝGî5”ÛL{ —“©­Ý¸KÚ›Uå7¤Óÿþe¼GA™^2ðìÿùoÿ+^….eN›¯#hÛ¼þ7,_ŽžxRï:$a4—í¯œcìûýÁ£ÖŠ|¸Ýlgø{!M[Q‹¸;aÏóÕ¸ðY;]-r@Yè(–l=åÜN:´âVHÓ©6v9¤éîŸÛ©•® ›iIçnr¦/_eƒJ´5†¿å¢øÞýtOË%àÍ?K»t^ã¾PMÐÿ"HE;à¼`]Ò.÷ 7¤QÚ„‚rätö{PåmÃ×–3ae†;þq¹…:B3Q˜›Aæ p›CÄp§ò~öΈ+qN )ÑÚ4pê-T³ŸôÿnO; ˜Ë×ÖAõлN7Mªïãô_9ÖÍÂºä­ éåö‰x"LÚvUåêAeâ›ÛNØÑ{+6üâ4(Ç¢øë ºÚ´`ß™cÐn†¥³Ã¨U´qS¶Ç=¢Ë0«NšñX茣'3*'¦`ååP3¢wÒ'KWÚM’~Æ„|…k/&îÓÏvo'ýšážž0ûûâ׸{€º¯xˆ}ßW’ß¹G©Àîìt€Á’4øÙ9ü#Hïÿ;ÿ‘˜½‰e¬'Aï~oÚY‰0wý ¦ª(ïì9Ö'ÇØ¹`qS‰½Û²ŸõM,IÊWØÌ.wM²Ñ`\8FA“K¥Xç>…ø—o';—?Á~ +ÑŽ^ýO®£¢Xà™æIÖ¹WÀé"yz)Ó”ùr} S5Ïã%Iè4˜ÈÙý%xÞWÈ;¢n?<È3“Ì2WzýG?l¬©c¾ûè‘m¿— zÖ{ܸ.†1þ íéËé@¥"=p*ŒÑtðºï…®Gcõ%Cº)ôLº»Š¤])Ù5̈ŽU,®e;weÿ«+}ÕÈLÇITçòTRæžIüOÌ"Ž{GXeÃ9PU£Dg]-„L?O—º„åõ_@ö½“$7ÛÑí¥ûIŸŒÄ]ýˆîn«é‚K-œO$¿‰ÛÓ©|­äÈ]q_z†|qó¦B|Õ@÷Q¹¼›ÔO¤/ðµZ€Hh<¤ÁÏEèñvW>C_Ÿ$TVBTžÀ⾘©ô"‘K­X …¿u ò/Y?\ÈV‰×¡Ÿo΋v€Œsò×¾ Í®|ŸWôÿã?)bÎÆRFùýz<ž È$¦gÓyZéH“öÒCß•1-LœdilÅ×üGaN/l%§Â6‘äÎ=m²ªîÉï°$Ü£ƒ(ª> é978 ¢•tÉNzß0:µÅ±Gp5…º è®Çøª©à‘ʪ Øþb?*ÌXÉHMi‚ç—¢P{ÁG p‹;ûíɾ®EÌŒÂ2<4º äjÒÁLÊ…NӥϷ&{gäÇr—è ´Ë›!g”8q ¯Ë\é¹exìý_LïÛGŒ— ’óÛ‰zˆ"Nû”CÔ<3h÷N{–çwù¿ó—ÌÙ€éPkXÉæUÖ³ó- ™er÷8ÉÛÒÓ6S‚ç£N‰F†|ž •oбÈEƒìš×ƒZõðbŒ ÊïG£ödƒ£8pžØ“B`øó ~\ÞÉlyñ†ãéòˆû±€=ñð"«à—Á!Üwð´w>7ÃB̨R†€ #pøV'Ü}þ%ôIΈ9Í«œ ·k♇BlSP ¢eL*˜ºˆà1Ú‹:ñ¢8Ù¥jAD¤n±‡/ñÑPþ²/W”ä¿+bvië…œ‰È‘OpI²ªBgÐ¥Û›Y]•EÿâÝ’ 6Û¥iÉÑpüÀ‰¦/ï ¦wyñ{ø tX¿–úL¶¢†²2Lu†6¹Ã÷?×|¯p¢çû`@:ïþ9BBvJÛe®4üP Šz)ÀãíŠѦ Âô$ÙŠ°7^6½ÅßF|ðWÍ·7]ÀÔP)ŠSJpõ®ßp%Þ,Üþ{ü¿yÎxÕ¢¤g‚bs,¶-‡0}]šóø8É ä¢ã•C0ì›Ï%ÏCð»p}\‘\^<•^׿Îi–´òÇ"¢•êKJÌè]bA¿þGïOá¦%=8&G—øüëÿÍÝvÜ -¡8»ŠvÃù;Y «—„ÉK£LðIê‚7ŽŸÀá†0~^ÚJjôjÞbŒ· £7î¿bÜ £±%³ä¡ R^Üå”È«3G9¨vÜNŽœbß=JùêöÓ‘Kp¼o!X\?À˜ê,AUé‡}äÓ«$ŽïTè®YFïØÄ³9 aä±Ó&xÔ3 T­¦u|Ô6(’ãboÂLŽÁv=9,Y†ï…ÕÈåõðdåy UÃÅ6rw쎎Àâ˳À/Lž¯ã<üc‡e%¬¨› C/Ì~yþ_üà2¤FDp‡¸SHÕ¢%$oú0Ž]mQGAjyV•nùÁÜY»›|­ÿÈõoÖ!‘«"S¹‹u¶Ò¬×w°£¾_)Š+¦Í#¼<³@8QrÁ­¹aì±ç‚$æ°3˽‚&]³HÇœP17!ÛNÝ‚ßy¯Q_2Z,7Á”%˨±c$îá}z³ h”ï*ZÊ4½“„o‹ÁeÃu°¢‘˜µÞ”Ц(Ú}±‚ùþà²Âto¹8n^B|2ÝèúéÅì’‹Ðú4/™:5œ®¢¥eܾiˆ;ib›þ®º ‰Î)ÿðó6Þ†¡DPk?u<¦h¦x<‡¬é“¯œ vªº0îœjBֶ์8x½“¶{› ®í½—¹žË”¡÷«:Þ;<™žJµ«^`Nž¾ÈEÿÕµðp—:qÿöÍ^A7 ƒÿE=²²ò!îxÏ Ç«’RÝVèK[ :R"$kÛU0ܱ}EHo}D¨Ñm‡2ÁÌè0нöÆ“#²ŒÒOf޼8+eàH˜= ¦Î'ÁƒíȱˀÁá0¼wLƒj ˃£¿Ù»n&íþªC/Æv⇢Dæð F¶u-±‰–ü·ÿOv3k©(Lñä,ã!Mû˘ãw#ŠSId\6g‹êe0¸W ­È¦K‰ôû< ÚÛ¹sÄ?3^çõ͸ùÇ)8©m‹n‚ÜjÛãø¼2åò±[»ÒìÐuÿ ˜aó›9‘éNÝWþÁŒ?¡·à¦lBEŸHj´eL·t'¾þWÑ{‰r±Š\m‰@s+úZˆ,“¹Ê} Fµ|³p©ƒ(ú§m­®Ë©è FÙÒ”®|xŸÁw536÷ê6%s;ÆWËÎ\“Ëþ‡}r¡.çbŸ1Ã5ü‰¿xœ¥­žÜú¿BTú3‘ïI;X±avÌÀÛo½PþŽ›¼žŸh{ä7%.ßŠîŸ ó¡7y‡ìÕC›¹S™ÝFCÌþü¹¸Þx™iòGw¤ ‚I »j<Ÿë±²81|_D¶¥‚ü°\ÞWç¯^{Žý%Z¼حGî´]B·Qs"Ù$‡Î}ÏÀââ^ÔøeêF ½r2½› ýF9ÊÌò‡Õ95LøÐ(Ô‡lŸ•èÝx›±¿ †¯Áåo<³­!¶õLeNX¾ä²å&Àsj2\9š3¾}雂$÷ñ`ã·cdyï¿óï•N  ä€ã×»õíâb.ÊžÒ$/åM°û|5f©ƒm!Ý)³ VJÒï|õàôb ª]²áîsæ…ïʤVð"ŠuLp‹Ö8¬¦ëÊcðJ"!Â7ZpÿÊFÎÍÃßðZ¦Hb~WþÎüªŠ&Å3iÑ÷\L®ºËx´´3Mµ_ÑâQ·'7]Ï"¨F7zò;Ç<»Î­Õø¹Ä‡=V§EO6m‡?Ï àü[6%@‹ 6%’—‘§×šö;ß‚»s ß¢v&CÄÙ}ôî¤3Ø)}\ÚgSb1¤9˜5[ù/ÿ‹…ž°©|‰:Ï/éÇ'“„8FÝsqöásLÇðv2'W–v5áôÒ¬› úC‹ŽØ´1®´ÈíBpiûWܙ҆• ¿£§¿±Àbµi°ûWÆúêS¿Ë‹ð¤ËAüö¥Ž]È@¼YŽQ†èÒDfz¿;sÌF‹þ]ò ¤‰ïÖ5TBd/ó4`*óœ×ŒnzåKm §¨"Þ+c«×®Bs¹éx(óÀ×»vÞÏÀj™±MW Ï^ér7Ì'„þ:û O&ú¶ì¼ç©ìÉ=6ìnïA\ut*57šùÿD[]aÍøþÞ/`R³&ë°O—Þc1Ÿ_“rÐÓ$øy&¾ÞPÿÍN~u}ñ)·C¯j%â©óÚ Ž = (ÏÍ¡ÚI¢Ä¹]žnU^ÇyœvÎ/¹Šó¬IXN0ÔÞ\Nþ›ï Ç,)ùs^8ƒh‰•lñƒy°¦æ6¢çúÁ<Ô’;Ík!2rIûë+VÚ‚_j'3¼¢ÿw²û"¢Ñíº»ÛK–ü·Æ¦¢ôëæ°Àø!«râFÓj¡CX=ò øÐÌ—e8]Ø]³¿¡Ú²wx«Y>“l¾;ÿÕ¿›Ó#p}ÜFÐ-°{¾æÞ{v‹’òÀ7õ—½”Ú9¸Gï3G‹6³!'ß0å— ù€íu:]QËQbñz «ÌDá´F¨-ØŒ7l*qò’1\^s¿d—Ú­Zã ÷ß}ƒ•¶Ûñ@€ôl˜'ꆿ6σ¢fLíé˜åÊâ…¸ýŒ†~6ä ãÂåá`r!›+^&ƒ†² ôï ðý7¾¨ýÍ™*K ºThĘiÌüÈfŒŠ’áê\ÉÏO-³‡=óÁ?BjSìéÅnõ&öÜÉÅøÆ‹‡ãLðOEÖF;ÿÓ¿Lu0ˬ˜™ép7 |Àþ¤bXö]Ÿßa«ã.£“2qlHÇ'˧Қ½ï¸Ï—i³¶kW“—ÞÇ™«n&nŽ.µHafÿ¾æ—_£âuÒwÿ+üQˆ(MhñŸá0oÝÔ?¤“Þ‹ÃæÀ]|çœÅýúÐ T+§qÊ«ï²JÓ&úµ«ÎZGç{ÆÃí¬ãlþªðþ  uTÔ|&‚W>¬BËW-¸wÇÖk ‰Lb.£ÿlEz­(´Ó·ÃC¾À·ë3kabO©’öÛ®g“þßìiøKJ½™ŒÍqÌo±ù_aZ«1Ì 7r6E†Ê<‚'õ“@¢l™’‹§g¬Á£ËñàÝyô0Ç fFÑZwØ‰ÜØÒô>ÿzÊÐq¦~¦0?ºC¨Ì«Ï W¤ƒ‡We“}îK±  Jo/À’,ïÑ¡=»¯±~ °1è±$ü’½°ìéxݺÁ±ÜÖß÷ =X—Ħ8b×¼Í5§k¢Šq`±/lˆObI¶j›gSÖõ;Z‡  ÙEYµÈ»Ž›|Èü+Êvç#22 ìèãtáü©-oH{—¨QI‘>Tœ×ñ/þ÷·«AaÙ0/ô/ËœXW8>fCP”¾ó¥ž²|¨¨NXƒ‰¨ê‹·#%`Ë>âðQå…-¸_c™"‘p6ef!ÐAA²¡UÜØÛÍÖ/íƒS«•èà%u2tÐ"#IøùL.n‚ŸC_àQ‹¹Y2= ¨äI,UêøŒÓâ3“Ìû¹ƒsÙ‰Ïú.Rw^Á'wøè™¯’4ýž´w•ÀúÛù µKŸ¸EK‚ÇB rãqÌ>)IhŸžìĽ¶°Ì)è?4l]"B×¹´añ^#˜_¯êåÿå¿Zôû°îc!7)•¶³“IÎc=tþmK3jÙ3%Yk•F}*ƒWÛN½ÄñòæÅôpwÞ+ÏyU¨4ô€ñfo”3Š“›&p&£p™ :̓ü~&ßÔG{Wã’ÙaÐå› †}Ÿ ?¢>ÙÌ&êû- "é&ÖÞ|ÉŽMDíwV$%VóæLÛízÌÖšC¼…ÈÈßitT¸ ÄVXQ÷’çLPzkSPÎjÜyœ+ÑnÒ4ÒVÆÝ«|„ƒ¢Hà"GLŒ3Äo§ÔY­¶µìÅ‚©´€ç›®+ù¯þg C¾Ñe™/OWU€ðÅ0Æèæ$²èbó´h'‘,åÐ;‡»Â&LôNä£ë©tX™;“®|å€ÕÇ· q³?Io8öïÅk ºýy]ýQ—ÉDßpødvº\€3¶eaˆJ9©X ÐgÎq?·ˆ¼úú›c»”œ›ÉŒ}…Ãrí¸ëÅ!¢¨¨J7‡d£ÕhE‡ryò7Q{ûf¸°! <¥¨X]4ÚÏláÚ(bq e“Oq/a›­YúNwéýriø¶é-ªÍ Žr¾°þ‚Ç?üã†sÿ-@\7CÀ‚RÜ4Í‹¾«™]Åøqõ8‚í(,³Áê­_0øGL“K\”ÄÛ'Ö`ÞIÐýûÄ){‹µ‰8í¶=Q5…ê-rÆçö̰´H)éа“žè^z]´s{zë<C˜ªí UgÖ1Þ·½PoOçÊÃÉdöøcæmI>WJ7Óö7 oõ‡3þ;ñR·4wÈ3Ÿó}PÕw$1òN.¨0˜ÄHµsŸÇû2×,ï↫¥œæ{³r?rdIЂnäZÅ(¬·eÅ{Ú¡ë§>]3­ “ç›P%[Ûø‹üº˜B®5zÕX…—%9h0n”°‡+© i³·„èÐU\ñýøÙ7N5Êm-©ðÎÀ¹±–u˜÷qÖý\C+'ú`ìY9Ö°á"ô¿eÊÕסÁIühÔάu‘"Ê׿¡Ñßžÿ™'½íš!R$ˆ§¯cîI ÄWçnr_ÛBÌì{p.³ý¶‚âaü bÈ%°7¸š!Â(m‰ST²éoH DN <3ŽŸÒäá/„3ëGpÅÝxüä%VüÆ3Ójõ´‰’°Ã¿óOÁ½ “{s>C‰#…h¢2Á{†Ei1§=âGIÜú0šé[Dù>4qÈ”3𦦔³°ßŠð‰Ñ€9ÄÊÿ+~w;ƒ±L ^(­Gw›£lõ©0œý$“¼:¾·]XAõ;Ma8B’ơß&´mé58ö8ÏdòSÙ¼2Ø)¨G͸ñG(s )]“ ½#‰iKk0ÙÁ<ë7áÔcc`¿Ä—šzî#å9|ÔêÕ2Ú“Og¢ò›tw‹'åÌ·$q?—…ê7`¡îáÓ¥Ÿæƒî|Wzòý®Ã1gfæ.3ÈXFþÕ×çZè¼k)½Îlº\ ª'–Ÿƒ¸ ú'ó$&Îz”ᥞ-°è| Ó.@—ˆS¿"P‘¹2ã+’õ0­ƒpéi8FGTãhàtš•Àí34uò0Ã-ÞF ú¤Aë»Q2=¿\&ÿç†gÖ2¸áñ(Ã,Ô$ÆûMéQ×TÐØëA‡T²8s·Æ“œØ¾–éó¨ÅESyé¾]’Äuf7¤‚á­OXyˆ~q®€«¾t#‹Ãã¼[ìºTi[böjkÒÇR5ÌŠf#Xüi o­Ù ‹¦\Ëû?ÿÓ?ÅÞ¦XÖ¿”yuKæ;ÝcRV$Áѧ%\fCîÍ¥¦ñQù¨q¸ÌoJ檯„‹º1pg¿JVâÊÓTmèòÄýeÔ”õQwO³£¿¦´DáÁ5x(k ¼¸WËpËÕ˜\¹ú:HœÈ¦. «zŽS >égE–ÍŒüÏGÍÜ«MwæíÆ’i:ôó¯³LÄ:j¶•dõ·Aî¼Ãðì/kÆÝOE +|Ù™¬‰ú7®àdó°êšD_Xã V&™g€ìcq»u-mSv¡ªž€ÇGüUšªDIÈË2 Ë þÅîšL\îæÈÈ[f²!a‹iµùŒ¾£¢1ôàÞ~»ƒ~ðŒ™áöG`Ö]Ê=þÕšŽ¶°oøÈå¡E°b‡Ij:LbêÞa™Â<ˆÒу#5kÉÛ•óhc5ã°c6T½‹b.MëÑ^±IÀ”äNl{y–þ:´–F¬­§ª 6ôëé?Øh𠆎cÖ²MKÃø@ÐsÉ šOäœ-iH‡8m‘ÃG“u1M¤ Eä?‚ lô6è£Ý{ ¡O;‚|‰_µÕÕ ¤2,Š5xVȰçî3Ç%qö S(ØdLJ½yÈÇ™ÿøßª²8[u1¶Æêй«&?^zðÉIØ6n͆Yõ²™ïQÛã¢ðz!Z•^Õ" +Ixƒ3ÕÎy‹#bo‘«zå«AnmßgBrõyé_»r\[o^¦ö~ r}Nì¿ø¯M;3[õØE¶J°"gmà¼[ •^&Aªƒ2˜'n?ÙgBù¶Í‚\0å]…J `ñ$š½ÏŒ ™\‡Äg^õºD:Ø@Ã;p­UÄ÷<æ}s¿C¼S&ì—GœÂ±|HöÀ-5J°í×$0ÛùVWMƲõ9Ðü÷-ã|-Að÷Qzåfí~ÉàæçûJ>ú&Ɔ”xÅA÷à!\}‡¢~îZ²®z7þ…@t~|§ÇŽÀ]šìtnE‘ï¦É4r÷>Tù!F.Ýc+8ëþóx³’òuÞP÷´öfp½ý‚©6!ŽCÉv‹là“  s¹„Ô.q'{\ è¤õI ®‹+f²ÜÏΛ‰TöDý:²™þî²À3¶ó@ñ{jEsxŸü7™{@Ž1úë ç É_!ºÐÈfÎXBŽ%­‚¸àT»'LÚ>¸s‹s'jïvÿ½#Ð)&@û'£ÄƒãpL× ü­E‘ãý4ð»Ý»£Éè§(M/}_…ó ¯³%úùP"MÍŠ‹!˼ì £ÿI¼xñ+‰Ã‹–Iø±÷ žyþ·Î­„òw !.ó[éЃ7¯”€àòUÿð/ÿ ¤?›-Ö»CÐ!–‰¾¥µlÕ_ðë_A->.ÀйXÝΧpgî¼Ô›Ï9xÄ&´}ö˜]8X — Ïëw¬ðè¹X(ŒÑc7*°ãïøI†Aœ ˜DÛÊu˜Ô}V°íô$ºñ?õÞ×—ß½`µò?àŸÚ²¤3–)Ong²ûí–)^ Îã…¸øª.(ì? ú[ü¨ìL%à3_Ìí-P$ÓP£Ù™>šU‹RÄëôy,†é§HÛ™÷pëãIF´y/UÛñ‹š’ÑSIBQ˜¥Ì'y‹^rMjÏþëÿ¡9ƒÜ+‰Ü”’ÌÚùǹÚVGXÁÚ‰d¹:&“Éú³òØ÷ëщ¸#ÂÖNÄÞöÅ—Æ!±K¸ÿZ.jxêƒI8i™àüD¤,òS¨üuwnfX”ÝMuÛ³¸¿\ŽÂt…G̦le²HŒ‡\ù J}êø Ö‡fêõ0 Ïà¯H ¬µ˜MNŠÀïKˆÃëæÎ£HöÂÁDäM™Á ûtÎΟEr§MäBÏ"X;_‰HæÊ¹òƒøpD…ny›ƒÃ7#ñeÜ]ŒË“#<Ï#Øye¾tŽ·+SþôÞÀâ/ÁøÀV üïü«³4°ùžà¡×ìê×Oá ê Ó“]tTf”“àPŽa7œØï“M¨‘~$J9Ëc‰é]¸ß–ˆÿÍb^ O0é@'f¤5ñkpB½‘u¬Ê¦¼¥å0OëZRÇï›A[¿“ß]ÁYºJ™èm›Lº¢rèÊu£¨­c.:gI¯1••’%Å—g“b§4šSÅVć ×6–oR$å„ÝšÆDB§†ŽO ¢sÏ#¼ß­ ßGnBýüp8XÉF,R!/qvÆÊ› W:ŸZ÷«0›oÁ¨¤õÑß‹‡-|àÎ _|lœ|joþáßqœ]$Ù–/zÌvê·.½ƒXiÌVzÎxݯ`lº3ÑvAòr‹1ùþô“ʳ!–÷Å2*¾‹R!Ô®x!•ÆyñSû^" ù\·Mà Ëfˆª”'hEWùš™n9jô4L.æ³jJL¶i&¹Sk‡§Ù Ì<µ ªôƒÙÛ|¿‘Ͼm7 âŸÈ)tøQÂ=ÈúÿV¤×êÂɇ.;ö÷³~<ÄÊQý{shv¶ŽÝ3-P™nNÏš©QÑK²X#-E ÷Ñú¸w¥Á6`•¼Œ^?}–µåŸ:±^3¸œ0õßçßî zàõ¶Ð4ž¶u óâŽÃÝ-eÐD9íñ´ñb)±*ç¿ùÊy[j±e¶[ÙáÝ-üÄ•‡3«Gaê“/îsó‡APeñƃX3¸Ö\­Ä.›TjO enä¨i”¢¿¾äPó”Gà¿ö–â6:kÙ6ú±g™Ùr’qdGÀÂf¯Èûû¯q(ßôÍy§ïAÄMÑÔÔ\îç£ â¦`Õd’WbÔâ'éŠïÁ®À(Ø£¨Âþ]Gö;ÁÕ×$‰gNÜÈP–w{¿ÐŽ m²Ú[õ/þs¶9ÃV%Xçß…Çeáólgh¼U‡iœ­Ìº‹ftRÀl()Û‡ŠÉæ Cãp®F;3À?7Ëš£d#|Y€Žå|t“‰±0ýѦR÷ÀÞ:ù(ͬ8ÒI~·áñ¯ù òx÷7h-E˜]¸ØM¥!Øo@bº’Ùÿ<×='ñÓŽ·,Îe$™wÓØ¨ïøRà >ñm€±—’8¥J¾Œ‰„5³è¸â¶Fržìƒ±íEv[pn83•XYTaŒÓKîÕwÇ0À©Û·”Â>®){ã†Gµÿé?ƒ5Û`—ϰ¿S¯¦‰Ó 6© –ón­Íáì$®g?ÀH¨3Æu àµfZt0˜¾_~ dUØVÍ‹ PƒÖOýáê—1æ7ß*ˆÒ ³¼é9ªì•"WÖ“¶ ep~Æ7Èþ˨ŒÊáÔ—§©v§ þQÎT‰2Ðßt”JNì‘òëÿÍ–†!%¨¶OžXÏñ%¼‘Ñ„¯YkâoêÌ͵Ë9›ÕO³Î?W’ÌMÙééSH›ú2سê&'Ùuø©ûˆá¢à×$º0/ç'žÆw2>4á0Ó{ø®?wëÿ/n=؃&ÍQtéUÌÜ)@×lÓ¦©ÒññŠ$ë¼ à¶º éŸ1ƒd‹½b³ÖIѯ‹±{ [r=Ù5ƒfª`´Ö–¼³Ó ûÞL¦¯”d¨Ï5p.(÷郜C¼¦ ¤*Io­{ŠîÓ7rÌ3×á'™°Øq‚“ÌUÆÄ/¢ä´ã)Ðh\‡œ=úôÅ;)|ÇÇ_óµÈõ.1Âý’‰gûVãUA òò‘ ”GÇkvA[èvö|„$y½ß “Ãñó‹Xì˜b ï·™ý‡BF’9¦×¤è|«‹pþ@<×Ï€5ïÿôãÿúÿœ3À3ÉL¶_Àê¡k¬Xdõh'[SLé³[jdþ©“— ÷e³èïkºÌXhævN¼Ó×pïíßÊÞÖßFê³ëã#ÁÆvM«/Á+Wzl/-Í&kÖLÅ£}/X5›¬aŸ"|r.!}—³ážÿ:ª•¶‘’ÞùMoÕ‹HŒk)sÑ¢×½“©ó­vöì6vŽSlþ@qWN5S²Ñ?¿6Ò”^q,|û¯ÿ®ž3'VtK¿p>v掠þ¢ô÷º59–:ÕK4 #¯ƒµ&xº2Œ¬Ø½ýï‚åЀæû¿Ì°‘ÛÚ$BŠÂmH餇wÙ•V,w§!•Gð)ÇŸì7¢f²»èêlæpæu*ç™Þ~ºt(•clKÅpž÷Pxz?8­ ¢™Åqú¸kSª@,q ÷Œ¯Æõ&ípä[ ‘ÙgCætÓH©LÒ§SA:&d`ø;«—7Öï\ˆy«Ï­ t–a"©¡¼äÒÆ0R¾ƒ‹ß¸Á5u ãÓ¼¡áëÜl”NMГÎ7–§?fBÞði²tHWS½³upZ %?ÖÃÏ=Âs¶K‚r…0ò·z£/ßmvÏ®ðø‡}ŒèǃÙLþµxau*²‚¿`ç*èrv™Í"kÔ¯²¹ÏÒaÊœ÷T‹êïa%¯ßbß­ëefð"» Õèè.[*ߟFÂ??áßïÈ•MdÝÆ%¨Ùs‚‘8"FøU¿£½ÆNä8ÁÍ©¢ëÆ‚q«Ú6³ºmh:»å^!/— 륃áÒ–X"z§÷HÜd•:*Ù¢Sôó½ÆÖ2ÏúRNè$âÚ´‰Ñ‹¯7ÃÁ5iQŽ0ÜÚ¹”40E¨ÏX“e·kè{uUÒ“¿ȳ‘dT:®o\ŸØþxº—¶fäÿÛÿéž=¸|Ë{øUÿ^¹ÀòáDÜc$N”ûö/_*öí ¥A£y¶[i2ð¾aSÏÇaÖï=(uJTòEÑgSEÉ<óÌ}çIœºáÜ_°Úe^¨·'ïΆ3¯¿<ÃõÍó1í~c¿z1Y|í1¼~UJ.WÅ•§÷2·g9MßSùè½lÔ…j¬KœLú%¿K'ËZ" GºØùáSéÊ@²½ž‡ã'Q{Ç0Ô$ ÿØ áŠÏ8Ë*-NÀ¢gBE§›cVÄIû:ûÆß‰1[u>B¤euÑ¿þ%_Kû± ¤BfSˆ±Ñ4Xà¿“ÌÎ,{Š7g•“7¢ñf<õ`ù›-ËÑfñrª.èIEÃ\è•ÙØ¾(È'÷q.‡3ãˆHÛtÊ#š}‡°ØÕŠn_²¼6®¡´'ެK½†C_’ßò&dîÝEø(ìn–¢“õ`î=êºf!Ã}j_æ™1q›„ÐAë+²è¦ý{°l¥'­»:ŠZÒ9øX~‘”Æõ­!þ[¦£æ‰|<àž€•ÒtÍò c忇׋Z]r<£•ûŽ Æç8Ïöÿ~‘_ÅÀ|ÿ„ão1ŸæÔÀSþf»½Žxÿ¡BßNqãBˆ·ýU<¾%—éì9@eR›àd`ÉZ“B­V&ÍÌZ’xÙoø¤`ž4º[œ&§¾›€JEš™ë&´„?ÁæržOn¯]@Ö÷vÇ—&­åMÓ!ÕJ¶ûËj´û~ó7ùRþ*dí}sâÓnLÄbóðM©é'ÕÊBs­¬èŸ[cœEF'‘/P^¹žM»{œU+ú‹´Õˆé€±Þs0´‚Ò”¦3WïFŸEé¤ÉÊ-Bë –P+aôËÌø§åM†^GQ'K¿âÐÁ2mç}Åäφ‡¥MNBOÀüÁØj“›tÎòÐá¥Q‡´`HžŸÞëæíü»hEËl¸ôQ³[cÉ¡_Í8õL„½(ƪ\x7‡.)Lå¨8tâòÁäD` ×ãQ+ùüãñMÛ"MˆqCüþ •n#'ï†RÛ?8_¾°Óe~Ï£Iç¾ÐÃÆy(2–Ï„w9Ó¨à_ws‚W¬ Î éûÏiÐLSñc¯½‹ô 1fõßlòK…y¼;¨îvœ¾Ä—ˆ;KAûíðmU.¬¬%Ð¥$L—˲ø²‹ö¤«ÆW)4÷Lömèº?Çã_ý;Rÿ‹ù¸92Nð—Sñhíüu5ÕNM‚(îqµ-äÌ:uî”~Öª@¾*Þ1ÑwÜáö«ùp¤ù2³ÓB—å‚È'Ml4H‡—ßÌØX.ÿí ›Ð?•â4Î \½Ež´L? ¶Kkðß(Û¸ë%c5íª-HÂÌGÀ èÂ-ºœ]åñž€ìÜ'LüDi¼kmŒ»Í×áú$ »Æ pe4æ ª“Ù!Ò¬Z}3gZ¥ªçídzw^Ç;)~à?³^Þ0$ Yy’n*Mè"—hÜ×Éκi@x[>3jûŸþßÑ4ÕEñÓ€Ỗ¦ n³g®9zEˆ*Œ³t­0Y ²øæ”Af§(99I*Ê `nD8H%f°.OŒ°tè!Þ9ÙKÄlègÃ_À鳦Շ §,²| jqЂ™f÷ #Ÿ¼ÄìÇ-vKîÈ’`õÃ$âO-;½m7|0üŽÝöòDÁ!Œ¾7m†Y¿;±áµ2¹û&/ÿy7‡~qŸ(šÒÉ&̧D†D}ÄÎ/.DSˇvÕqïØžÁy"ÏqÒÈ™tR'¥±­W™ý‰ iµ«pîZ½ËbzsÁÓ}LšòïóÏ7ÉÁ÷{ZnÓ¬Ž]Tií†WR•îÈÍã«O2-Æؠٳðøx23z¹ÖmßFg“Åÿ¨/§êûþ—yž§ ÉL!%îY›D)JŠÑ •JR)†$dž¢ ™ÇŒ¡¸gí2”& š©$M¤ñçý{ž¯Ï_÷>÷ì³ï^ÏZëµ^¯}ÖÝîw¶¢IÓKXõm#Ù§Z ŸÓB‚-ñk˜Byáæ}[Z°ÿ̨Û«FC—Z!ü9H?Eú5[ñ¥Ã˜õnËc?XÙ‘×?ƒ›ìˇ0’-ºóa£‡þx|l¡õKvÖ¬Ó̺eQ¤w­3ðÍ:JææG²Š§È¿¼uT?Ï}FS§+#¸Mf/™ö# 4 °ÄЕv~šCN&àŽw‘Ç0ŽVê¦ÁÔï ©Ì5a Ó ˆâR_rp6ÿ„ÿíJÚpl­!Î9ó íß'qF$¿‚Šr¾}aM]òéÕYŸXO±ŸX°2ˆ[…‘Æñ\o‹ÀMó\éá^1Üüå ä)å1ŸóÍp^b#ÅÇôzÒ‡šXT ޥ臶oðÑÝ,fE¾37P0£²­൥ü|ˆÍ³‚~³kéÍ7qøFè7ºyÒ‘Á4UÁiž.4ø¢6Yê6‰Ü?|Š}ô”ûyМ^ù4…:N×óû¡Ü\ä/†Á¼Â·àö9 ÝkbztàÛ*iÚqë >3gAþ‹h`C@#,–ðÿM÷ù ð°Šs…ou2O`õx–1zK«`Jž0<ËþÄθfDò}í˜}Ž¡ØémJŒâÎÂÕG§qþ¼{ óo'JŠ3D0(ÓjÅI-˜k%Hƒ^€uæÏ¹??Ÿ&û]vÐõ«ï£»s,žÊÖ¢öáß Ò@ˆF“µŒ[‡¾vKé`©=½0 Ó£*[èŠÏ ¿å0Uïw!¾sýÈýç&dÒ±3ÔÓ|'gR:„Y…¿p=øy5¼Àn¿<_X2ÀoP‰g¾ëÑÄ%˜{÷;G´|jnÓ#ë'/#î•õLg‹ñˆgï­™À?K}qòSûztèïÔPÆb‘=ƃ;„qkÖBøvþ8ÓÔ¢JƒÜ…Ê#i¬Ø– yd]c'æÌ'ž½"¿¢÷ç0F:+o¥<ö#=;QÑþM 7c'UlQÄ7¹2¸Ï© Ò£³ñ‹ƒý‹sg[@h•áïI9Štè­ íº,§ç-ÇIlB!çüñiðQ\›¨ob|xÈ:§bl"AB'ýc—;O§“;"9ÞK®±ÏG—Pÿá½DRó*½Z…õÍÅÌdz£Pñ, Öæ.çï˜KOß S·»ÿÝÿÎî›ZÖÜj ]/#1’J¹Ü®¦ah¯¥Ë§¤±ßæ37ùˆÅ¥J8jœé÷ÎCæ…ëð«IÌí}ŠcôèᄅT¥÷,9ÅCn~8 ¶DèËâx6DUžeú˜,þp|n€ê•“à‡Ö$òøY5ßZÂ9kÿ öbÐæ†o{Ú*¶rgLã¥6Q ªËOßýÒ"Vo•ƒ¦ç Ž×1ópY¬ 2#ÉeGPˆ_‰ Çð@G‹8ŠVfÚ…ä9DmQªPŒúi¯Âí‹…éÍŸ»1ù²?˜©o!í/RóÃ*lG¥î,›6±ÿ]Þ·vU€ A½x¼]Žž-ƒâ¼ËLék¼²ˆÍrÍÇðùA›T‚…q92tíþ¥˜¬oe)W œ´©¤×qì~ÜÂò^oáì¾ðîÚdb“¯x¨¹°^ = !†ënÍ G.£ŸËdìD³ŸØg²…PôDÂ|%ˆãmuzÄ,Í&Áö’ |ùGjž Yæ†f+OƒÜ"ý3½™¨!v®™'zÄŒkƒfüiÊGv˜ËC¡æLRQti²ÌB·år蘲š^ZÊ,¹•ˆ ‰Oؽa1ð})œ›–cýö·˜óÇ ìò”ÕìÐÙV@KeØ÷@s÷)a°‚L´?²L0©néá:YØ0ïÁqGL2гŽ°á§Ò„´ÝœÎY>ŒãIâ^b˽¿·ãŒbˆÄóvÊÈ<ºŸÏ¡Ypÿ+Öùê7æWÞc8lÞ†vð—'QÓêZÔÝä‹ò_W³òwØÅá`]<…>ŸFÖハ†üšÄÉè34}µÄvQ°Ý$‹79ÒVw²8þ¼•á%b Ál§Áqöâ·púé¦:ª;}‚¹š>xÔÃouVÛÿ%îMÖcG•ÒØ¢Kdÿô—cfó,Æqv&XyßÀLž| 1÷ðä}ej"×Ìú¡²Uùž4ñÙâævæ0°k„é:†‰¾™XçÓŸç ÿ]&âVð4ŠÀêÅ&T”µ†¦üxuÓ‡Bñ&°ù}lþ%ÏcÙ':³`Á©S\…T+¸ï.IîW1pÛçxVÃ=e©˜UÆËÜUœOWû41“:M@ÿïU”y|æzW2w£u!w¶$¤”V£¿ý"Ò:ï(úo‘£ÅƒK AÈ“T|œ„©–'ÐOò>zz†ýó….½õƒÊÂ||òóÂV†v%kÿïüÇd7ä¶ ×¯¸"é.T+aÅ×&1É‹ùa©Ã$òç·.* k qô[fùXNMÌçÖ‘0#;D® €—¨)ýz™?T Ì®ã@§MjÒÀ¿7ç5sŽãRç@JÃ>#Ù3ŸÞˆ_ÇÆ°6ÄáZ+6­›{¿s9WͰ:/ƒÁ×ßxÅ^‚\O=ÚÇ[o¼½¯«ÓÔ |uLŽv?û2Êß!V¼ßHÌ#;® wêr¨5–%IË™gn§éØm-,Z¡.Kä zø -–§ ÁŸØNó¥ø¯!Z‹úŽ‹1æ‰Õs_[.N'fw;°É^‘ê5ïYˉpÝ7(:&J׳ÍbO±èJ -œ•Væ!È«gDDžî ·‡ÞƒÊ³•Œóí>Ö.d:I-¥ÂÛè‘wVô€Ég´hÉÖŸ¸í/áñT"Z…ÛX&º+ÕvnÉ Ö{n²µ«H€Qv<š~­2d×QkägÓB[ȹOÇèó±¸v<.ogzæÖrtÒ3´~^OnL%¾«,éœÇ[¬{"t‡®…IªSHáöÍ@m¯`ڿלæ¿Ý0³o §x†Ò|O‡‰çz%^ q@^ŠËѹGׂoA9d–Á¨oFÐ`„ÌŸvÜàPÃ]%ï@>¬:ÁJ7µî[ß‹Öjó‰·Ãf²`–Y~hÆ¿‡@õ@¦Ýw>ž;݇/„b§:*¶¢­%óˆ½+ÜŒŸ“–2«RÒ9ñ=²X볎ܔ "ûø‹ày„4Ýp¸ låc˜¡}Å0Åkˆ‘â”àÅì2¦Zü.„­ˆÀÑi²tpvããÞŒ¥\è ï)8åïYÖH@Žúðgas™Ýt-œùsjº  _*ýWº9.É…óHìé{ÌËWì•ÑíõoKó ¦E ¹l0®3¬aÎ4›ÑÞ=¹]Qz$lþaωùpÈFO­$•< Êaé¸í¥-t[/…Ëþ›ÀãØf&½…œ“kÈ\b¶,Œq³8{]ñ¹êØáæ º–B$ûm-÷õç'ŒþiQ®ãÕ‡(|ã„ÈØÉóIL´ýUç ó–Òȅаn³Âà^¿$Ù9Ð˾XZ~ªåð3l=(EúÒéSâ9sozs©öÎ>‹¸ØŠYàŽ?²]1¨¼–>uG„nPóI”é˜DÅâf°œe‰È`1ahÓlzUÆ$Ÿ¿ gºñû½hâ8 W„.G.Š;”Ãö¿}í.xpýõap~TsÜmô” ³;?ÿ…¸C›˜M¿}þ³GŽS6ÄÅeÂÁ)Géçƒv.cuòeI»›8%±ƒ*ĺ~ð»ÕàÚ =ð>ú5.­ûŠ[®ÍßÓðÄwç…K Üm2.ç»Õ¼êrlr’ wŸd²¬C”ÝŠÁÙkuèùÎ@FlÈŸì–¦±süaÔ>”âë .øK̈¶Oa23ÉnÛéœ)ƒwY©ä˜Už‰ÝAÝÏᲘ>O ‹³™ŠkX¿2Ÿu1“ÖŸ‚ݶÅÖYJ“‰ÖƇ̚Ý#pw»YPV†?nÏÇG{ácp2SdG|LÐ&†gÆ€xƘt]”´òC×ÙÄi ñááÜ®y$Hñ»öcšO=6…LÚ>]røõ¢‰ü¿á® çïAóÎ?ܼl%tœ#BíìÍ{{š7o]Ž'N È9<´^_ô©ƒ–ç!üÐ>vjâ•?0pŠ"{j¹&Xø4`æqëõÍväe) ·UV‘ÎQGx‘ ¦¡…X±¯ Wîºý²¢äù!}bºpëtï,¹ÝQ¦ÔÈ@’6h„•&¹¶;’Îø}›ñ‰Ëƒ‘ݯð«Ä|æãi$Á·–Iôhã0½Yнø.äïA·Üj¼xaî||•Õ¶ø„H¿¿ŒUõÃÚ¼qmìt®Éu_³ú~þðM€§'°KD¯C¢×pžf‚«¾gZÜià¥~Èö#Ö+AÎo>ÖyaqjM4h¹ém<è·T‰ò¹FbÉX=Ö{ýÃî†V<òá0ž’ÿ_ÿ›æ6kvçÌ£¸tþ¸Ž›ö–ii•§òreP)ÈÔ®¥ZÕØ4| %‘A[*÷!4þ`Ô·a¦® ‰Ûœ Ÿ§÷¢´×|ºMÎŒ†íeTäáÛkÏ™Q ò2ßÏ‹‘ºÞÇìÝÄ.q ^WD‹Èâ¤, >øŒ»òËÈqÕaÆÇp+¢ÝÙ%ùPhd„{M¢ÆFTcò)Öøäkèò ÙG2ì¶2ªë¨†ÄrRW´ÍŽn¦ä¹9R>Æ™ùà+X'œ >ådfD, ã¥Å çáîÒ8è¾ïËįiÇR•À‰ú÷î÷ø€(|z¯ÍôÜEOãZ„²3ìÒZ&ßÂC/ed0ë*±=$ çVØ훯@Ýï(5ýW‡û ëavÒ\óà2¢§`OT0;‡û2‘Ú­ž6__v-&J\âgDôߔҧ:‡áê R>}“Ä;ðÜ!¸Y«ç&Ú‘ß~i°cÐË®ñàz…2ì2¹ ßÞÉФ‚L¡Ä,hOØÍür•Æ3;àNÜ:uá>ØwìT$¹×úh`rFP¾o=ý°Ù€ÌÑ¿»kp²ý1ÐÍ]ƒAê´óÅ’ þ?סVH€w¼Q¿§oX¨26×<éÉ"«{C«ËßÐ6¿*¿Ìf®+“³ÙTÃ53—wb»·2>hù€K|\¹9_Æõø)Qê´`YÖ7Äl‹7%ƒ3f6;¾Sbßù>ÄoúÇŽ[¬Ý›³ðoÑb4Œ0¢«8Rä®×/¼ärŠ“¶Y4{½`Š\ fÞaü†½êµLµZ Ʀm%¥‚£ì¶þE`‘Áj;䩞‚7ô½¯E/î×:ßß_Ç®_E öY È¿‰0Ò)øLVŒQ:5ƒf5ˆU ?в#““y&ðÿIœwCìcÆ¢H¶çûøŠRv_ôw<ôF™žßoN¨L1;Ðtˆæ>s"¾IûØ2•Eño:øk+ÙÁúïàù.Ê›9PþJ}º-ÀŒ^¯ö"!ó"¨|?y±a1Q¤’(˜iId§SÉëaМü5ìiwn+ÜÖ;LׯÜA ‹ËpFæ+ꔦL³æVÂ÷[BôEw ~øŽŸùâ ¬cXkôÀ#¹.è´ìCãíóñ…|?ðÅöSÅ'Ø$éMÌ– ‘øZ“ä'²œÉ®W:>t«Î3¾¶B¤JqíçþzyÈõ÷Ñôy¾Y:w% ޶5QºèM4¦ë³Á’CøíÑ[,Š’!ÏêÙc¡µUcÂÿñÛ ÈÞ† àÊ“G—Ÿaܯ&|æ=ÈtfÆ‘ç½[áÅÃäd9Áma¨»¡J üÅwÿ$Q_eúÔbQNE§Á¬˜ƪØ]g&•„Òcø•Ç‘~=5ž5`é§Y«QbÙ(JþÚBnúNÎÏú%Äaº7s»BŒv_Fçnç¢Îªxîç—)ÄŠcŒnM8²]œ„á@U'ÚÛȨÓVq£¨Øf³"m1é–í¹`æí¢’žðãæIø°q¤Ôµà>}:ë“YýÞŽüý÷»¾‚¿Ç¿Làÿ“Ø:öy\ ¶6r¸‚S©Ýûaf_(wƒÃ8ñ©‚Û;"L^ްiÕbôüü‘hC.ϦSG Èba ’˜ÊÜð~„É2öÛÖB¸½ÓùöG¡RB4M¢7ÖïÅ?xïaFJ÷Ö`å×hÖïB¥)’›Vka×ÌÿþÓù=HÆN%.l´^˜M‡ Í™œ™Ò8õÖ/xîøŒd…HÛéÉ´ð¤ݤ'Dß—YÇ½Æ ßNÃOPBÌÕZwÃZ|¶·ëfu@À@MïDw¯ˆÕ F¦ÍR·‘poSZ×r‰‘¿¬>.:ë Ï(˜y5Õ $2¡Ï§€„q ££ïÎNRPB¾SÌËÅù8Ôqšäl-ƒï“ëàñtO:fû‚éó Ky…À•/ tÝÍzí`3÷?‚2XŠa‰¤¬[ÖJhp§ù¾æž²‡áŸ`ê.A²½, * Òqøüntœ©Ž¿n3hÿDˆt;e€ãò‡`äü>gô2ŠSÏAð±TúS im‰¨¼N–ó€]à%ØsE—‰/½ŒF‹{³õƒ51N“ŸÎ¾_Ì>?²q\—Z@ÐT1¿'â$‰{j“Ô}*®´Î5v"ÿ¹f†Ð¥5ù<\©›Ãl˜’Ad¬.s¦/S þ†7ðÚþϤQ…Ù´(n ~ÙŠ²½ `&!MQ^š=XäJñ«(¼‘Ï¡µç@þº'£¾yMKŽÇáÉfàx˜¼¸÷ Ì#º9Ÿ;oÖ§§ÞêUG:´õñÛÇiÑU7¦dÚg´°¯eÞöˆQñ"|\øì>KV§CóîÁßûZ˜0s )£Ÿb±úiø—` LØŸ_&ˆÃõݯñô[ƒf}Å9':•fÿQ%Μcz•]q>`EÌbé@IåouÆ¥kãõÐlVk/|Æ­ÅmŒò!8¬By{QôkWg‹+îîGŒŽ–ÀçSWWݖ‰Î6`kžã£;\84Ç0q+h)Dó=ðÚý^¤sJÜáé¤Eôã`íÔØŠªii°Æõ.ë½#–>Z÷Wæo‚'¯2˜%×-©Âq2ÏÒ–þ«§c­sèÞeXr£2ö,€ò—Û˜ñùЬ! ~Ÿ;ÊÜ—lãFìÚÐò#n"ÿ}àÈЧ-1ØŸkK’5í`±²ü z+nˆ²Yé.´7e*Q/øÍl^¾ 73¶ž˜õ(ˆš5Yû{Áð0¥·7¤È­W‡€ÿÐ-ü[y7þȧUÉÚtþ5)jsÛç¹,ËÁ¬ß†U?䌌ׄÏtàÛ^)’š‚¿6%á³ÓÈì.ìH¥³æÈ¤Ñõû{pú:zé’<‘¬u'âzYP¡ O„µ°ÑÒCPÿ/ÝÛFÏùÚ€Ê*yª(;ÌNù•0>'/m±žåôò÷(ø¤vúj&öÿ7˜ö Ôâ'œ‘¾ Ww¹`§áWNþ8ß²s¸Ž…I¤õ¥ß¸f®b›ËbðEŸ9¦Båy’÷3pÕâÏL‹ÖšæRÇ™@ÏšXhÚ°›^ü† Éï`¡¾?]5c#ý\}ˆ½žfO¾ZàñOÞó6+˜¥PDgåK²A=]͆»CñÙnG’¤7…'yã‚­ŠÔn9­…í´kÁKfcÍÈ245AÞ›×Ëö®YˆNÛn@ô–Xû< -–3pѱ+ÍçÀ »­dÁ5%²‚­G‡+ÒÓ;ƒÊXÜb£ ¤0+@‘y¿®~Âÿß®žâ\¼^»ªA눊ÊDS6ƒ}ž#CïìR!y"þ¤*Ö•Þ9cAóÖIóiP3¹µ¹ ÂûÄèÞT|`ù;«rØ·×¶`ŒÞl\1ú/žÔƒ+õ1}Ýiöò-"ð¢ ndãÉÅÈ»F‹4 ÓЧ§àZÐFò$:Y¼|h1t,m$õÎSa¬–CõÞ-¢™3‰hO:ø$O¦¡ÎFMzu ±ÆÁì›õ½Ø¯h«’êH-h¨M u»°Uã3^,ó©=@?p¤LbàI„5\­GpðöÄù'µ»¸üDŸZZûBïŽx¬ú°Žë¦»yL×" êë´žÚÀêÎÕ'äR&žÿi„›Ö\dýGwã¦ga£;c¤À)Ù7•æ6Nc&¿pSèÕ¸[hý>”Q%±Xºì/í†?Z›`º†+9­Rͬö¿ óµÕxã—9³óH³7O„,ÉXM§y|„Ão?1ž+4éuî¶þ~"<ÛŠû¹bäEz1GBã 8 ÌÁލ¿3ÿ÷]‰"ÜE ø¸N#›Ì°²æ¼IÄ•»µ‰¯ÓS¼ø*’¤¥%Âú@SzÁÆŽþ¹×0‹Þs9;ÎoÄÙÖ7‘_`&<ÓɆ!½•dcó3€R#\x¢ žFØÒ’€—ŽsÙ¥¹¿!ÿL;Æ‹fpíe^â¤9~è0“ŸöŒôÇÜM Š!Ðÿ=^a;Ù–E¹9g2Ó66·^þÿãZ´7°¥A;@xÉ<øïU<±ý~Ю˙EûfEÿn:kÑBtݨBæDÁ"ï-ÄÜw•µRU(®+c~·ÿÖÐVø?ü˨_ÇÌ}÷ƒýnv™Ó¼î9|ÝuF’qí7=òÕ+”õzÐÝ´wL½/ £µ Vy´ÊWSáéÍpáù6jSJyÊLîÍdÍ­é·ïÞd~˜=¸þž@–/8Îrü°Æç&¶äÑÚRI:c›=uM6gφÔQ>²mÖcè1EÆfäÈí…˜òäò–^p¿ŠªoacßTëÔa[%ÐCq|D21‘»$Dæ§à¦}àÏ Ò½¾õç¢Íñ‡`¨í ;¿/-/±MN—18)nEo8Ñ Þ2áÕôê8䦲_žuëGPQ"—jd©¥ìNúJQ î|ÜŽ >¢¯Z%,k¯!ÿÂâi»Æ ÕÃ×î YR£LNµùìa7Ô²P¤O™ ÝØ®¾éÅÔBõ=zxÉ’ÍK¢˜Áõ$…“Z¢ÄC²ˆuN&ïíém>eº£%q÷s4s+Ãæ;/`ªV5íä±äû¥°rfñºcŠ÷Nª«rgha[µ]±c -ª”G¤Hœãú=oÄŸÕÁ~à.+ûu€¼óº†çw뺵Pñ÷˜Ç?†±§'üå»:õ‡¥A]è>f¯Ã·Ùú%øÄl=9ã1Ú[†Ò»Dpç­"¹ÇîâÈ[äý©KÄ–@ž×ŠÔ9ôÊØ‡  ®<ÜLm†U¥ÈŽý爺Ùfzg9´*M£_‰!=9ËžZï¢'Î%ÑšEjÐ#6]jÖ’Õetˆ÷fÒ{ΌaÊ‚.ÌîO Ý_ɸt>¯»ˆÐ) VÂ^Kštž˜w=&ósσk§9Y-b…OmÖĦNÌ?íƒV†”_Vr›ÜaH<¿˜ö``@<®›6Àtuþïù×Ú!˜vè5gcz Ü]tŠ}·X’>LHGÙcŸ«;*4~t±ß5ƒìˬƒßo2Ûï¥ã«oŸ ¼D”|~‡]㺧Š3™pïõ /¬˜Š‚[Wã—M!DdÆ"X–RÈÞŸÙÍ^ñw#uÛ‡Øß&A¸J”<ÛÅO{7½‹Ú|ÉÿŒ Ö¸«~?€êu3¡H(”ñZþ‡±',(ìd–[ÉÅ{ÃÞ`c\•U©€7CÁ÷“"U3P¥¾×À,mD†rÌë{ORgP‹Ñúç?ÆÜÿ£@2¯£´ù=ööÍþ×&âÿÖh0+¥„RµGÈÇߨQËÖáSá„®.l;•nó÷àÔI÷9{[¯€À}¢¼]•p?ô@ÑãkPÌZ°OáÜQÊ?[“?úÈ) jknŠ×¿)Yó|†)±ÎTà˜þ»"@ËÝ"ð­ƒê“9ôÕØyTÚýžML‘eêgÐ…‹GÑ]¤¹;ë`ìztôbÔ¦,îš/=à-òä ‡ÀæÄ Ø6S“|õ<…¯KÐÃ…•¸´ßŒ,pXGcÍk8¶„CåŽ}Å/Çc ±ph>–Ýb˜ «´©øµh²Ne¤9ÃòŠÔM🆬iÄ4ú–ä§[¾ìÁW}¾T:s/ÕKuEƒõÕP_%MB»þ11'nàÉ/Ñp¯áú/ÝCEœ”Èß/©´Ùdžˆ¶:“á²ÍTãþ/ð-<Å$ Œ@¹Öêza-þÖ²zƒÙ$ãS%„)¿`Ÿi™“Sw¾ã;¡ùäå`*nÚDí·¢WÔÓ`yvÞÖvdë-ÍØÕÞrÒa/ýáõ ãb9Ž¡±tڭϨý×mœGÎ>±#'ËW¶ŠmÈʇ/š‡¨SîLêõã0–KÏää´§@Ég *üþ5Di¦8¨IªêÓé,ë(6ª?¡ÿfØx³ü‚`çA1Z—ù†\uÌ!n¨ŒÆ÷=Qõœ;ÌÚˆ%ÒjuLvYH¹—l Á<˜,Vê`ëôf¢¢ôëF¨}Ü-¢×žC¢9VäÛ‚Zä󱣯ãwÑù;vËæ³éÍØl­B–‰ã÷tõ£ç y‡MHçÓaàퟬ=8d Ñœ]Ø='ù‰ÏûH²m(‘x²×^ $·“ª„½äÚSÚÖií÷ªáïzÒ°>Teµg?a=:æÑÚ´@:÷ðlÒC·†%œtܧ±7”‰fyøåéÄïîßzÊð¬>>ŽÏʸçù’8›s…I ÉÒiP×› êOBhCÚ NéI2GL•• ±qDe…ø7b¦I0u¼â@úTœÈ€³-½ñ„âVßìÚü¸ ú8|D׸O¢ ÿJ±Ú=jDÁ~*ñdßâ»9ú¤º!7 ±á{>ÿ>ªäç@ß®Ä?'ãh»÷€µ×Û54øåo(V2£y›m¨Ãžr²nn>¤Ò­ä¥H™UØ “Ÿ°q†+¨Ë^R™ü‹™ùé6§¨ð$^Ýe³çø±–úæTôÛ,躒 6ãTæÁþy}Ï…ÙG“XþŒ-$d• vv¥9®â¤Yª«w®^Ç lÖ*ƒ†+‚dë 'ð³YDJyµñê&I|s1o¯¬žf˜q%næÌ¤¼¿ ж*nîCà}9fÞbÿyYSm,Eï¿V´î“½xê+ªq«QpF|ÿ¾šKÿÂ[ÖÒ”§yy-¥á‘z0Øî°Å3޽ìÃý¸$Qˆ4.Z oÿÝe—5`C'yÚž‹Vz‚ôÇ)o’õ¨–ékHú†–aßî¿èÿ›åÄ?°Áé©|乕 ÖÍçs¹7G?²š4‡›2ñü/¡x€Y¯Â7qI{#”t#Wÿ|M•¤ŸòÏᥦP<¸0#åÉǹR\¢i ;å¾Ð° õ3_¢±ôrÚŸkOÃ+B9œŒÍ\us¯[ÿÝ%B‚+W@@ÞFZíò¢%è™jZ¼1fL…[1ú#9 nzFƒ”í¦¦¼Í­h¹V“„œ†~m!’;™”ð”ÀÀh|±Q†îŒ¢­C9Ý6ö¸SR¬©Û€^³êXíÚklDyHZ܆#I‚åZäþCü›Àg°œ/–…™Íýìš[‘¯-Loðà´›ÿûÿç ùpôñôzô…qðýÁ^Ì×7™ 1¢_‹" ¿ c_ *Ñéâô_­˜Ÿ·Ù†T§ïŒœS$Þ‰!p¹N–.”ÞLÄæ2©ỹuÇiŸÓ RYt¦ú©ÛZzz†"LÿУý‚+1¥œc¦¥…b*àèñƒºÜ­k£øèB>úø vmûÀpÞQ¹µ°Ïí˻˛ÌY2‹>ü!DÕÿnC½Û|ìGíï°fÖܾЂékŽëmcra!—¹öH—ˆßœDóM.`ÅR.Uñ“á…ÿð´Ím‰’ƒO<ÃL˜äÉ ûo7òCšx®—ñÜ#Ä"™?7ÑiG YÅצ잚@jf¥‰«‹åéûK¦tL¨’½ ic“k %c)5]4\“yD=XSá5x _ßÄ“øü0©9Ÿœ™c‹f;ŠaïugzuêO|zó>I<¦IÝ™ üìb ÒšÒ$INŠÞŽ&û‹3±Vå»A?ÿŠ?cÞ²q—à,:k»å^¶ÄÑ9ÇiHŸñ Ô!‹ê›áÔÃ(òÀÅ%^­#Ó*Œ-ËRX±ŽzÝÁÕãx“x0›yÜšNö.t¦ i˜¥48‘ÿýËTÉŽ ;:VôÅBÖêYZE+]É/°ÞCÛ|ŒHÕ©*,“’fÓNÎ$}R”#”AÞNÏ`?õÆÃ­Ø=õ|2̸ïE‡ìSè¹7TÎ9r€ ƒôÜQöãSšÜr oêÿb¿FÇC,=@m½oáKÃ*¾O‰´7;ÐÃËÒ=yݹZYÓ,Eî0ìëŸÁ ¸~û*`¼ü9nŸ‹ïœÁÙÛl%ÿcöj ¢ïq=ê¿÷4ÝcÌÄ΢ÜÒÆuáU˜À2ƒ'žBÕØb:õ¤5ò/­œ/V:A{hÍCý û£Ï[B:ÓÍìMp£þñ'ÁláF€Û†ðC—â!Q?ìú0rOw•®'›v^}y0µüÅ6j™‚™ú:ð?dÇž8)MÌbOpª¾s]g, s#š€žfg6pÑ~Mȶ[ÑvQÜËÃq²ÆNëØEªtUU3 ,r$·úê bÖ«Ñî¸P¦" ,-Gbo-”gnŒ×Q™]Ál”‰Äõƒ·!@yËøSœ»]‰ZovEnÌØ+© û~²> žîOÄèÔg˜ÕOº0å¹ÑÍÜLŒÁ¨÷óßšË\Ì4þoÿ{Ôy+ØuãdT¼§En×öq¯ÏȤ_6rYKˆò¼¹Ž“žSèp¯`TkÊa‰Ó ð™öÈœ6E @¬A‡›|‘“R! *yŒ{ÏlšÖ·¾ÍO‚ š¦Øu}cóhù'\ —ø¦“€q’ùsÛ¥˜†IamPàLï“5tåI#ðl•å®Û}›ZŒ×ôSûP |,aŒpjE5:JCë}ÒÈ•†Z·%(4Û÷®©ƒg«œéò%z´©ö8í)ùÂQ;t\gÓ9ágècËiè´^‚[, …Ò¹·pƒf |6fîxüï÷Û»ß2òWÒÇuê?Hö½‰Žñ([À↱ëÌ•ïb´6T$æòx(ð4©Mѳ̜#­˜¸ÚŒHíœ !¿ÕéÂÉ)…'¯kÀ!Æ™VÕX“\U4p9ÇÕ“À ý*¼ð瓼2L4­€¯‹ÙM®ÑôÎÖ½`"õW<“eçò~f?$«Ð¿ëý×ßÌtìãűöZØð¢SÒ¸ 4iÁÎFNb:>õÙ„ƒÏ‚S«é{ƒø’%Hùú-¨ð³p|a3z87dY§´ùÄÇâdì» ±?&êŸ×”FØ±î §Å¯—k˃—E>äxÇwòRÀï¿€Üö\0^WW@¥ýxmüÆÈ‹7Û[ÌOiš!݈Ÿ¾bb¯*Wg?sjGTsá~;ˆå§}fg9;ëÌ*˜AuçÀƒ:CÆê~!—÷ÕGpSOª¸©`óçë-ðˆ;èêKeˆcÇeX;_ø¬ë…+ßæ9Ÿx<æô+í¤±\5°52Á› ðrÐbúmÔ“X'翵º$ZA+øø‰›Ðƪ´ÝêÃ>R˜d&Û½Aiî*2oi(|i‚3· à÷ó³¸5×nâù×éÒæYÓ÷âæý³¡«ä\u\H7\•£kÒÙ@KpÕà²æt1§–ŽãÀt®—öÖä@§:£ÝÈnøÈ>>¼†¬™« OÎÂþ@A²zf0Ž¡q x"üûã²½Ì÷†««š‰&ëÂQÔ˜Ý áøME—D±Ž¥Å)çŽ+%ÆOñ'÷}/‘!ýú§;%º¹ .ö¬OeD»~2Ÿù ñU¸ó(Á‹zî›DSǪ‘>ÑÚ,)\ºÐȪlÜ#ü˜þ–ÏqV 'ûÅ&Ó_“› úþKÖÂäd7Š¹Ñ¤B÷¾Š‰À-ïpä¹\:.I¿ Üe*P>‡½OAK–ÞqŸ™mW€™ÛF!Ô2Ÿs}¿ÔÔ-k–±pÆ´¹I¨ápYž9aïl¨‹¨céÉmŒFÙ^t`g‘ò·ÿØŽäräwzÌ~ôØ 0e”sé|+ï;'}c1NëdðAÐFOtöI‡Œc,znÃÉÿ=ÿéxÏé{j žÏûØ“ ¦¦M‚UYá^Âä¥LQF g}À\ŒµK›çêXÉ»jÐ"L£†9œ/×Xk•(üú¹–ÕØÔËí¤ »/™ãŠ.gÔš$Aï~v|œõœk©0ç1ÐÀ úîÃïOndO 웕†­Â‹ÉQqOl¬ibý¯÷X6â<´l… ` 6pLŠˆ·ïAlÕaU%¶6«+ï‡Eœ%¶ï^5’€âû"ª^+‘ÙË~AÂÛkÈ3@TЏ¸23 ´¥–‚-‘g7ÞÛ´k[z\K,ù™>×î|Ѥæ»*½üã«úÊŒu^z jœ"ˆ§öÏøa]ñ! óä—Á¤¢›ÖŠá1M¸>õ\`]ÌTñzn2ž2§» &Oد»gF˜ý q²÷Ùdò>±|ŒfÑnÅ÷XÓ} “TעРŸAÜSŠ*|ØNŽÔKÂæÝ‹‰Y–û9xnN· ³B É–s²L±í=67ËfwóS~·bû6ˆì>|µ3M©ß#~æ(˾C³†œ_°Ý~ËÓó'ïà äÁ»¦]pø°#ɵ]Eò c1› ƒ#‚›ÝL‰ëú®²ÓëÛPt† ¼jçDÆ5«$=&1ÌÌü“;£lQe¾? Š#B¢-ýµÞä‰ÒÆa/*=y51K†—3’齨ÿíÿŽlEŽ.ÃÖzkEÈÛ ô›ç}x‘ !»V‘ج®wM*Wµî+”?'g¯•°k?°¬}1Þ¶T"›¢„P1ª Ëêa…F XIÑ¥€€n+¶´!˜q_P†N]-ÂNdDdú£Š6»¡eœÓrc1>±MÔ¬€Ä£ ØX~Õûý85SWÓ?ý-(â(VíaÜOÙËðŸÅMÖâm.µÝù•Ûº*ƒmZŽOŸÃÆÊÕϧEÃ2-qÙ–ØöÙ îU§`û¨ N3=HÜ;yáloˆõ»õ¯p2AÑc…ö+]ÂD˾{}ìíVŽÙ£Œò·`]–yœ ˜-‚/ÓaÁÀf›ÿ ÷§`Ê€zÎÞ…·ÔÈý´Z¸õw ¾xâH³ô’ tÛ Tœêbõ­”—qO®8›‘ÉÔU¶__±…¾ï¹ýæ?Xzh6&™gýš"qUË 49´…]¼—|)\ŒÓjBá×ÈÛËGµ}8?eð㳋ܥƒ¸M‰Ã¸²FŒÿœ5^½šÚ‘ '}ñçf¸¿µ ñ'/yñ„{±uæsåÔO@ÈÀlÛãõߘ•"1ÿ=âóà»Ö­Çú6ºr^:›q]›™þOðs ¡@Šä¶¯†S•p 5ʤçâÑpo¸û/4óÃk}PÓYÁŠ‹JàÊ~3Óe =R3‹‘ºËÐã’6Œ ê?¬Ô ÷¥±B[afö &S§‘=ûWUÎî·Þdp“õ1T—¨bæ¿9¿µ§³ÑŸ±kã`W±ý}âš«ñœ©.¼ºy†þü¢„ U¬èÎK0ÐX ¹{Âï‘Z¦cO'“Ÿ˜OgÓu6SÙ˜pSX^ÌÇV(’½Çìi°·¾](rÝo=¾7ÁîÏ])“ÈmAüHG¬Íæ¥3óT ¿Ì<(ÈN„AK.nfŒ<{?²>Ž7˜}ÙuÌ~‹|žIBŒ@G™+ÕoB µá†JmÁ%9#øñÃWüo¹É˜W¿ðF¸dz#rÊúðnÇ9öµy3.™2•D -`}#Hæl^|um1ð¬Ñ#¼ªILÂÓ78oGñýËø¾© Nš…úTS:úÖJ¤©rl++¾Ÿ‡X»o`Dz¬©CÈqX!{ÃÞAccaÜ>“¸ðì¥38l¡E( Á¡lAº|’y7­›oSžð¿ÞMX¯©Üä8 Ÿ?Cɰu8Im'$\LÞgVÀÕ×N8³ó´Mf¶ä݆¥V›`‘½XnR`ÙÇ1ÄLwا QûvF$‚‡¬¼ŽóשY»aÞóµÈ¿ñÇÌPq:î,}Ëœ’x†[áô“VèëW¯Ï 8gªnpdwĂιë¨×Ó†…"t»+ùÐ>ƒé?¸’f忈“àWÂ…ƒÉÜË}¯ÁåbÂQwë¡ p8…{}ºyêüöîÜ$Ø»¯v›É‘ ®Eä©ÐL ? 7ÊÓØûïäÈÖ§AÌúgùï:׬véÂ;Î2RºšŠNê3û’¢áú5%¬zaçl-‰¨Ä.öØ1 ø¯§¹õ³(T\v†M+á,_s8˜w W,7aR}<™çëéŸÄ¿§ñÏÐÝR9¸lÁMî(ÏnfQ‹ƒàV [D1¦KìÈêowá¥v1îØ{”­©À5¹ºôŽÊkB  |h6ÆÀª„è» 1xL†¾ÒýÂÑ;øTöý- ÌuØ“ø«±ž:E°ïµNr~mÄ“Y¿Ø²õæ‡ý‹a2áEž5ì‘õ~°«ZpÂþ/yNøŠ[`ÊÆ¼ç¢³}:±½5ɪËÇõØrì7³'+îL¬-gSG +{•©ô»0‰ýRŸµƒÐÙ1‰ }ºA³òÀã‡v×i’ÖòhDdɳ5 `»ã»ñ_X>+eÝ„AÐÛæn»ˆb—8ħUœÌ|jNþôÞgŽ¥Þal;°æ•%ݱöÌÞ½»Vœ%ëäÕé1XBÙGn$ô¬ ž ÞCƒ/\À†ûŲÌiæßŠ—pcýOì6½k-ô8U¹ÎCo%®¥£ñ‹Ù‚'¹øŒ¢½µdø­,¾¹~‚ÿ%&gw…?Âå[òØ%Eüxë£;ºf[bØàùô·mòÆ`ÎLd§‚Ø‚Ýq8©›‹»œ-QÜŽ—Lµ øÖ:à¾'UYz*äjóúØX„ûWÐb—#¿`¤‚ž6ÿ,¼Á|úÅÆW]Ä×ÎÑð$1ÎÌa¯ýH¡åhÐ8þ}êpûƒ%ƒÌÁmöèþMÐaË—õ`Z@¤8ÎñPÜ8n/ ›”VÐEgäë|øçýŸ|¿òî¸uïnnK¹ ªö†£Ýš®ì7|_"H¨Õ²Eru{&:‘ÿ³4V°±¥ºØñOŒóüœ&<}]‰Çì#pÎ eVKR“6ÿlƒ§·Sƒ‹'è¼íRðeû;\ra.ÞZ%MëutèŸRŒÐ•L”›‹dw¡Qú%Hwá=—7Ððà1è©1ÑU¸`è†.+g™ý℆†¤b¾…µ¶ø é»7àqÐÞÔŒ¾}×Á][¥LçüÕͼʈœ´…ö öúÈ}X#’Ž î$á^£>½ š¶íÆ[qά±³5u'Þó{½:#¤§v…6¥0ê !0?ç6½¿£‹Oàÿˆ)íù©Ï–UŽ îùJœ]QΨæÎ V—[ðÕöБ¤æÉo/3ì²Zt #waôÂ%ÉæÂ½¡r¼¶rœ—(’2ž•ÄäëV²ÊQœ•›çÄ©~FÆA±¹6±¿´ú5u‰Rn:œð‹e·áÓ™ tþóu.¤¢¾¾´–Q? Ë#©apƒ¼,INŽ!&ÇÓs. ¹î#G¥{ópùpû¶3<ãFÑå“;íô1…’ÜzÄ îÔ×ËT!øïzA‡$¹¤‚ZþQÂ[€²ÒuP\¹Œ¯çƒoÌÚ ÿò•³Ú£çàhS+§ +ÛKØ9’EØ Jg\Q±Ð‚Îìnfõ¨ñØ”û–#¥È}¦AÖÖ³‰çpnÉ![*8Ê}»EŒnp¿ÙuÄN­€û&XòÏ€ìŒpÅ¿ÚF@·M8hU@uÖ&bd…Ÿ4Õzs7dÏšë Ñ y%Yt!gØŠÐÉîã5ðœ#›5…ÄV·UøjO§ÎïDÈUýv\"V ¯šOÑjŸ-ôª±ºùËuù[Jõ¢@u~/:o¹Ì:ÌW ½V°®}u ó¡R $pÝŠôý«÷èãpNgN¢Ò–S¨Óîœ3©÷,݉ EY ¼vs!ÍŒFÎû…[WP½ ïØÎŸùvn$.P‹hÈ„ã”Éó!,LHÏ}=Qÿc³7`Fª1Xþƽ·”ˆ²ÈIt+ãÐò]a¸öÌCævK$Ù¸, ¯µª€è½avþ£ÄX¬ÞЍ‘mœR¶kvä< ‡EG96ï"óÄð5n •¥  Èu¾øÇà.>7úÊl¿Á®X†•µØî*Ä'nÖö¯€éN“iBÎ0Ó{>»ìxÒÿïwökæ§ßü¢X®B5Ó]ÿØ0ù^›ÔÃŒíVW¢Š0víTö·®•óñd—óÇàãØ¢»•AƒÞ€å”¹§ž³ô°ÛRÌæøÒ9—Ïp³ülØë½ÙñßÌhÁ ¾_¬ã«°ÑŠÅ [°_ÜŒLÉMÑ„LäÓ ƇœG-øôQ$G¡PM œÊáû½SD©c ºLã£/—ÂòTƒ‰ø7ˆ•Óe`ãÒ|/Åé×$]$•‰ßGuLê *ð{¬37x)‘¢·«I@ÍBbut 1nÔ'õZ’èí×é ¥ÿ"Ôÿ#OV¯¦;Õvá€Q»Í{®ºM‚Ûö¥dEÇoöÑ‚dV°Îž\:Žyys{ÞHXV=‰‰Ð8AÜ—=Ã]sLàÓð ÎaÍZ˜ç Š ŽHÒÊ †QP½‚HÄ畸Z(®ö%³'fîÂØ ôáfS’6ušcå±{ö!üÕówgª¾×ÀÔ¼n7mf?.”%á'/ƒÙ`$ÊýJð?ÔÇ úÔ3QãÎ:Ï„®‚¼ÝøC첆cÌÞzsºÅIiÛÔ©ŸN»_,ÂóÛí¡iK9SØ1„§x5˜Õ®â‹êIô[Ë ï‘£K‚¾¡à¼dXYÃvë†ãuQxÔ" ë]–1?v™Ñoò—à[ì=´ç¼b§9Oš §É¦v{4ëGÁÃECðáWZ8ÀãóW`°ÞÆ,Ïcßï1¶±-dÞ€{Gø¡,ê-XÛ¢ÐQqlùäɳÀF"›µ² ¦½æì ÿo^6×ë+wr†ªù¨Û÷„ ûk¾Ÿƒ‚¸,òí¯øÑco´Af¿ÝôH‡ZÞþÌÎ’'v÷¡ù7bÈâÃè³ÐѬI¹‰Ì&Xˈzµâ ñ7Κý/áòw1ò\\t ïdöJ…/&S±k‡àù=o:EÊz•ÀøüøÃ¸•c/aîé(†¯õ1{–}Š•¥©(ùˆŸŽ 4ï,+a§­ü‡%KŽÀ¡/ïaÕ%edi†<Ô·ï*ÞŠBéß9G¦,æ4ž8Þüü0|Gšü1Èâ=°eZì­Hª­z«cDÏ ²bÎ4+Dé‡3à›v¨Yüï÷ïþ +X÷•šÌ׿nîÙa"šsª¾ÿùvøî`HÿëI±9›bvÕâK,|y †»'““Òl\Õjò¬²~f¦ã1£2,ß.J/¿ˆA·Žý¸òg(µýó“ýù4NvŸÁcüpö÷Àí1P{õ+¶ï${µíé§ÙŰjqÃÌ6$•ûNÂÃvNùûXìv.bó®óRãÊ1fÉ”#øÝ!+Íg‚ïùh6÷ï0n ž:¾¦)ô Þ5>h2_¿¶Çééá¸2’‡øÜÜCßšL%õ®@ÈO7°lR#^ŠJäFîiðŸ«5a¿ß»—¸u© ‹Óxë8z²í’•fä¿þ㛕©Ù8ú:D^ø­¦ƒãüµ>Èš6šÍ¥OgLìX#ºÛ&›,»Ó‹“H_B+»”>ófKUìðî›ÓtÆ·›`n½ îÀ• ©[l „–ZP‘NG0Ú¶E,"!wÊi4éå#åjcP¾oÓV¡+š«yN+îk­d׫őMAwX½MDê&=Õ鎉!høÚ~”~‚Ò·{H-^i!› ±ñ K¾K³œ¶‡ÈlõÁ±¡”ž]ÄÍÊýyOïƒZ ĉwLÔ¿¡¤fa» \Tm`+²ÛáæAM<ýà+Œm9‰éïmàUÊq®“Ás0•ºQ×xÉ×> p-‘CëêƒxbŸ<ý•šNb,l)ƒË =Ѷ¥‹66°E¬yW ‹|±§¶Žé·:ñç‹0Ù^‰þ¨`šåàùnc¶.è#$¨Ð÷Fg±³<¼ ìé»äøZ}óGÂÙ·‹"57†©: à.•[hb¿6½•«ÂHÉ_Gß1îjÿ»h•ÏÌÜ>ÎgÆàà••øvù0±'Á¸ï6v^ùŠ™c“0»G7e 1U˜xþ7tW´…1BgªèßW²>³/â:¾ýžH¾t:‘}ì±OW˜Þ9d,ÉcÜx¨çH8/+‹-ëøQæ÷XŸ,NOHU`ÒÉ ˜QnÍ.Ÿ~—M¹7Ÿ=l¥Ïd¼Û‚ñEFôãaîÉgœ;‘k0`›LÊQa¢t{pJ{.$І HpÖœ@@qúHI‘ç¨|¦RÄ÷žÄSS™ð>M[E£æâŒèHÎ]q1*ØÊGð}Æ%NwÀ`X…\`„¸­Vo ,qD±e»Ç×{œÙµçnžä LJ“s4¦Møß^”<ºÅØð…Z¤â–þî¯S‹7 øÛK²áöeKš7ÕŸÔµgÖÓ&׿uAÅOpª>ßœ£¤ôþªåªüÜ–™PÅkJÎw@Ç£~¸ûHì6Èã;—mðþäf|pü9|M¤fÝ’-þ"œ°sÙVõ(漦Éi׫Ï3›´]¡=$rw•³ƒY¯0«^déV†D*±Z~š¾c–iJ0îž<䯼id£‚ælþ ÿëÐEÅñã1$‰?ÙÍa×1£þ1wá:%òw?ÃÎÒ"±ÚP»cúDý?¸)É{Åè Òs{kéH¡a{ùÉé:ð.ž4ëà"eB?;çP/]ªæxCdœðù 5ª×íHnëvãXZÚfm&#¥`br”9qú8z.ŽŸ(R#ãKÌSɸ q Ÿ?†¤¾g`u]™Üó%³þ6ƒ¥´Ë^‚µ­Ñ8eÉú¾õU^¿¶ymD-˜«@ËjaË@ŸŠjˆIÄ™³©DHÉTà‰:ÄW‘°ä=TXŠ#ºáOù3Öà—:¼9§ËH¥æ™ÊãÜÉ|]°V}}+Mö.yƒ_áOÕ‡(t×¥Ķ ü?ûñÁùSàþ‹C¤”æ’ŲטP©×ðÄe=ž;›6<ïÀ/ÙƒLas·ÖÚ–žî›B¾™›Ð= o ‘¤y ‹9wju@Ê¿û¿Øâ²5¶ø.…ÂE«¥Œ|é¼öFšT®r@y –žûÃöì±Âmö¸!#fÆôàã&["m&OÖV¬„=s¨_¨¦K_ç–•­.,Y?^ú³[‹.±™ë4é*ù_CÞÂc`ÜpÚ×gE“#ç Ø?‡]0'à7.RŒq‹Õx‘‹éª˜l¨‘îdl Œ¶Ul&.°Æ#ü +w…Ý—‡¾ÙìêÄ4ææ ˆsNd´ºÏT"/=ܾ‹L¢¾W§Õ»×qt·Î7 ÂÒlñ¬bÈÎi8hiLWƒÙåàÞmÀJJ»cÞu^"·~,%%+ß3mÎÚT/0œf[Ðb…h4ÿ^Ϩq#›–§aÊ!o0üôjbÿ+úR1”K—Z?);M­¼ˆñiA’×ÂRs³{pÞÿ-˜^Cû*‚ý+‰“e¸ñ‰·™×a9L¿ÿ Ô\ÍÅy'.Âû+§HçÐÌyt“žµMG~Û}dƒr,jUl!¿×Ä›g¯3–×”0b›^œ¼‰NV_LÎ{=cv'L¦¢"òäôŸHæÄšÐæ‹q,ýššF}Ü6ÃßN!¢¢Z„Á¹‹‰¬Κ©B÷ l§Ï=pí{Ü;Ìzp„ˆh­=´ÕÂ,ý¡/k”—ÂE·—¨öÁY4dÉñfãÓá¸yÍ;àO#Xñ7žˆ/¹‹•ñ߱ƒªiu5×èIÑÃò¶ :²„8¦¸±~åøo•}_HÈàÜ0"ö׌ÖœÅ!ýŸ`/Ó³Ÿ•7ë˜=Å#9ã5ÈÏ™~»³ÇhùäXú:ñû4¶· ÌÂ5ó×a”MyÄ3•º^?ÿµ8v·ŒYÓ´Žló±D—Ê2l¿@‚¤)”ÊdcÖ« XBw »¯{¾·82¡Ÿ µ« ~ݽF+•ÚhÀ!:mÎ3HtZ`}Í­´:Zs ¯0aÅ ÖN›Aÿëwî‹:Ego–§µe|tõÅ}Ô{­ ­)$ߘÁ§ò¡ ûÝÕ§{‹§Æ¥´iÍô|ÓŽ6ÊêôÖ¬0j/w .‰Ó/Ìïùªtþשtib(Tï{Ï´xœe”­ðêu-z¼t*3I.F…º˜–[‰øùJýžF¶7ŽAÿ´=ä`§ UyÐOk¹Ã÷uˆcÕ$üŽbß6г‚ÑxÐr3üs=H‡]2È,¾ òcÊEü½ä%××ÿ;¾µ¨™2ŽC“Ÿ@˧z0©3&÷ ÿB”ÃLúÐn.)]¶ £VaæOžC.蕃ÝÊ?´±Þø¾‚eÍ82ó<>’ÝóÉ·ilÄr]øùû×Dý¿*ÖÀ.Ù)†²7%ð‰w(ºMµ'z†3Ø£Œ*ã$Ë,”džV\D;ï\Hz}cà^.—€™‹ ½VÁ3×g1?g΃¾¾0w>£8x òÌ% q·Aø™É2…ŸÛÏc·Â_FC÷ Þ¶~æÞK¯ÞJ#ƆúÔ¤.]P“f›“4÷wÌÑŸ6ôþXøØõ ߆&Â\iyzòޏÕíálêùŒ1ñÞDäØ-vû·n4\ê kr®â÷‘Zfó[Œ®«.qûÖgå\O¤G¡çƼœö góÿ§øp™8Ùz7›½[²`Âÿ¿'éBÉ·÷\šƒ“Ý®BÂ_M:Öx Î#&’`à¾=ð'…Ùýøô× ‰– øº{?4ÃÁ…VTÀÈמ}WL錗£ààÌIâŽ>¼ˆ=ä;M.À‡Jô™Ê{Lf'­ŒÃoê-ÌÍ ]âç7È ‡1~ë’*Ôâ / ct‡ûXâ¥ø¼å ĉ‚$Ãð>Ãî¼Np¯î3§â ‘4ãeÇù”¬<®@ †“ûë7ÊIÂB±põÉðì§ýJPîú_š(¿]7?ˆ‘ûÍ'úŸžÏ;Í ¶ö¹³{ Í·dw¦c&oç83$“=ÚX™]{ѵº%•Êá^K$¿ÚÕ§ÒÙštô\Â&íx®Ö&&ñŸ¬S-EÈÛ‘j¬Ïéfw¥°ð "÷,3_ŒCc’ɰa;œs{ɺ¤E’kcíp+µ­UhÄíEÑllÎæÒÚ[çý¶.•5rDaOîðšL|rN²Eiª½A½ÙöØtrpÍ>KHà‘Qlçq@ýWÂð+i6Æ Å3ÃoøÙŸ×üPõgsœš"ØžüÙ©QpøëB*«â=Qÿ´°]‡4IoÜ šäÄG‚=w±Åú*ÄÑ™ æîåüúÇ ·ŸL¦êöípÝ´šxèâ˜RÜ1ò¦{á¿?‘:“. IP59B™fö•Ÿùoüf«½4tßBâ2ó9µ¿±°æ4mÑq&6×àfÓX‚þ!๧FR Hûñ“pº——Þ$?C'£e#5}t™I{úŒMš *± »nà…tº‡ƒÈ0GPÐ[I î|y¸ùU•×÷…›8i)÷Ù'/O]Ùdðhú uáò—dºÅ>üP˜^éòö}ɳ˜Ôõ8‡—±Ì¨N<ç ¦.¹V«K¯úbä ^ö””yÇ/JúïO¢/rÇq÷Ç-x¿.7°Š¾X˜ˆ']¢Øùr1ì8—ÑØ³š9~óLEœoîÿ½Ã¾+ ÙV#yAªF-Î91‚ÁUŒ†Ìú:È9L–ÅŒÀÊYWA»Á…¦V„ïNX/óûøœ›Éy_ÿ Í¢1@m#{Èi™ÁcHõŽÁÛð·(ppé:ŠésVÂa1ìw^ z™?™æå•XBr˜šûÉ/Õ%h¾;”تÖp|‚ÿ…„¡«¿ó¹Q”²WÃ1sÿ2Ð@ÄK÷fCÊ|άÁð³ŠðÑNŒyêÁÐ*@º˜[LXÐGvZ'o™à¼Î$µl?Ø××À#· îŽi9ðÏ·wŸÈf]¾uŽÏUÍ$$%ÁøTæë EX÷ü-Úld?¶BáÈ,¶¼RLdf’ÌkÇmlóæºtÁÆd}*&ÆÑtèÀrÑA<4ˆÅ= §1ñîk¦Ãa%>¨¢UkÑk.?Öüþ —%ÿàŽH,‰þŠVxbCgúN¶Ä–q®4Æþ"|O|>ÿ9ûVÂÎm£9ëÙεxfµ Éœ+ˆñ!»Jq+³Rœ[ Öiz¼×ÿÎwÕÖ©Ô×ÿ=Ð6‰îç«gÎ^Q#½³âH~b ™ó(ê²ò‘)©Ag¦ MCI¶Áo<¹½ ¯4^À²mÊTI œÌ˜%е0>?Šg¸çȆ;M¹}FŠäÀRq%Æmu©©çG-Γ¡Ü·ÓXìœA¯Î›C"¿ âw?ÑU¨\Fé“\éUƒç÷›m¡?·q1é˜ ý®dHÍ_–ÁÆJ_¯ ñ¹‘ä{Ïß ü·r‰•àM¸{‡=MRoç”ÿ¼'jĨÚÖTëKOßpëž›“áìOìÆ¦õ$äõ%vþÜR¬ÿìÂ|þ´jOúAÓÌl³üTÖç›<µÒlƒƒÍ0¦µ‘þeϧE`7Ô@Gè4róòBúæÌròÁq–må£ñ¿EÑqå::>–œö| nï¶Aã4<ý¤ŽMä}ÏnUO†ÍFalDW+ÚgìÅ붯܆aÁ?™/£Ggñ¥²»O°ékžbžy*Û÷3ÊŸžF/FHx“_'þšóhcC?Ÿÿœ¯¿°õÒYÜt(îòfNØÿ+1*#høçI”«rGó=p÷ <Öt¦†Èü¸dl*duçŽ2ÓNÑû^_˜³KN¡¡Œ:ÈjǵÁH]ß~dFÿ²+ªR×äkÜ1ªö¯ÅíóåIk¥ ‘<—HSl›ÌæÒô:š¢¡Ÿ¾þb^å<ƒÀ1lúsìüÑðnÞ—Ä¼×æÃ·á èïûiï,ðÜ Ú:YæÊ_S²Ãmß÷‡±Q޽%Í:ˆ?‚Úßÿ`ÙÙ(zÜ–Fx(‘×]pãy%˜u´cŸu¨ÓÕ‰øOÑšBÞ®†/?qÊÌ<¦ÿB&–Ç$ûúrœqW‚öÇ À*/Ò¼ˆµ¢Û¸0ûÆ28,[Ϻ[ÈÙØ/D6Ä–_~÷£è)Ú¾PžÞ–סºW/âÏ)5œ¼½?±Èà²õŸìy¸¯À ò{|[3ºç)й‹ŒHÕä5dGÁ¦òÊaÿ  »‚ìM2]™‡4¹ˆÿÒÓ0_ÚU„Æ5JÑÓûíh›ú_V½kÞk› ñFá¬B~´xqDÍTHëŠCP¥¥¿áè9ú j©-Τ…›ØUƒ2 ÅÏ¡RV!þ?ÑæÆwjÙú>r'Û X­2¬©HCÞÐkÀJzÓÛÁq0¸c'zýþŽÖÅ™8pFŠ”«ÿ…oãzš)œK\ý{:˜cÇ€áw%Ús÷ËÞe›Îjá¦_ŒzéFR´Üëÿê’IÏ/³©7Nà-+ZØgo«°lË([«+IN¶jÜÌ”žòÆÚ¹¬†bRÑtµÛV«‡÷ÅkÝO›°ëÕFà)l…úv»Æy7šÝñSŽIuø'õ üÊ-…°t]cûac{=>fQî¾ÚŒãs/\ Žéµ`ÉÍz\WQ‹ñÓþÝì‘%¸]œ¨75‚ä3ClåUx'×ò«UqÒÇ&øQµ ÃÕé“Âsñ_ÜÙ€‘MìÎMBD½LŠxÏòÅÖÓ§à×:!ŽÿH-^ç=ΨJ 4^4…ܨJ§O ãQCû󶻃µ &|§àï"wà¶¥Ãk". þø4ÝŸ%^‚{äX:ã6Ù‰ÒÝ"ýsاÿÜè¡ ^òª¸ ¿lÉ„íb2Ôûœ3­q!œ]ЙTÓ3Z™‡¹’t}Áëm6xæÄo˜1dŠöWÂ!òÄ$z=õ JwDÓ‡º†D1͘ã!ÜY‡hDï°¹ÛüJü8,\‚Iz‚ð!ëná?NŽbÁy3hA鮉ø®|“É«Mýi\§sZx¡ŽMGßâ¤c?aµ²Mˆ¼ˆýB{¬%M"E¿ÿ@ä!'˜éàË&¾×„hC îÂàZŒ·ÝˆMýuPœµ“j.Kd·§›R­ðxz·íë£lŸ“øó>ÆåµwàÐô~¼ó`=Ì0¾Ž1¾ï°`²fc7äƒ;}r~×\´nÏ€c«EÈÅ\s ÿ g»Ÿ@ {?Å>†zKyúR{r"ZØÿt˜‡ÅàâÖ/ðö¦ùiåÇ\±YŠZÍa?^Âì÷ÛÍÌ3죟mÌž“~µÁf‚ÿ­]þƒY<û¾têACAüïlebQ JåCÿ§LzI?ÚvÇàwá)DNJ÷µ±)iÉø·v\»îAÔŒöÀ,ñü"y“yÔÂa”<ñ,Š%ÆfytåÑl´¢/Jôu`ÏH ,xë‹ÿ9Ó¬9ìÖ/màþV“ªÿ°gOm)†`-qz,¤œ-LÇœè½ì×ç;4 cO» Go=}e ù~x|lÑ'jáa›®HÂÃyˆòE²Fw9ª]„¾ç(JÆ?dàx²:Ù1]™Æ°æ´ðO9î¸dSóàIŽ!þèü„ÿùLÝ™®_’ÌÍßÃì¿S±Pë›y⨞c;DŽÂää@^o½uò4!Äù®B½öN‡5¦rôûÍH¶ƒéRúpøÓT=Æ ¬!þ¸ÛÁÊ^*a~¾ÞªÞäý{ÔO¶ ÁØš‡‘xÑr{Ð÷;Srí8ö,óg­êøˆÃ\tÌ`ôä…©pI º)áŒ>¸¬¥Œù(ÛdÖC…Ç.ÏEúkË)&¶9D=q ·ôø tôâaú\õê{‘ŒÊBþa€îz†$&úãÿd]óf¿µ`ý]æâ¥$î[7Þ þûÕø.3`>È^^¤Î,ÌHÂkÝï@+6‡ËìVe‘X4ßÒŠû“}iOÉ0wg¼Y,ECWà€`#ñZŽ|+aÛýÔ‹\†¹ò¡íÍTzàÉRã2¼6æEø÷“2èM1Z—¾¢Aå±=v½Àh¿hP¾h ‹’6 `;´ñWAœ±|ù;‡ü•:jE²¸0Zܯ̢ç>YÀg48¯˜Æès¸½¸.«ü̺I†aV¯,ýQ8þw>´€º1•™™Ë¿=ãFý™Jµa*„¡PÜ]´ _M3º0)}åþ)?ç!¯Ï1&™lœôCäLvAFŠþ9¥KÚ,†Xñ­— ã»$Ým[ŒY«?Àû†¸C3 #Nlçø§jÓáé¢pî© 1•á—°=‚Ceµ§ÑïY’äž…4¹º¯†mç¯d†¯[ÒS™Cçö ¯º5õñ^]ë`WÈž…}嘥7qw~¸ áõ½¬ó¸I`1EYwWv@øa¿XñPÝîh÷¹‚õ-}0:)£7# & DËî0IOAÑ© ŒDlŸcN‚/ÑÄy Aö'=G?%úí%‰XÌæ-žÈ¿•Ö§[²'­Æk›ý!«ä%ßÌúÙy0n4ÃëïÉzh´Ã«c‹°éíbÚ*º?Ç.œiÚÄÈj'uðÛÝ©gà÷áv´“Ô BÞ£à¼y.-ìD‘G°eùi¼µìй´²o€aTNâUŸF×Ä”=*± X&CXX-Ú~ÜBÖ4e¢Ï²0|{þ.\â•¡.믰g{2AÍw¸ñóã·9¸ój h¯ƒgëeñfÁa¼õkÎ^3“Z ®‹ùW`›9‡nFŸú;ÖæJñ`òô4*t‡ŒZˆ(–€Ó×U99£}ùÿeh%¾WÀák»™—Ìv«ƒ†‹•OEö;]Ŭ¬§¸7à1<Í_O_ÞžF¸Q`xíŽõ¯F{ðLIæÆ aΜbœá€7½XFǤS·„À}›V\j×KÑÑSaãã Xønq-Na„Wo&çÒnˆ£Ñ7tÈñ-YØ<Ó y=]ñ¾HÚßö CçÃ÷íµpûû9ú-s*´V|ov¤'´TG×ÃÈÎ##qÓVãe¡.°¿ÅC6—ofœÖð‘å™ç tU>|ì·€³‹{žþã‹ìá@Ö ¸¨Âþ§|渹UŽ ½c:¬üˆ®Ë7ô¬ ³yqlÃc¨K¹Š+åðÄRR¥îJªF ¡,oMSBó¡Ä1;ŽšÇã.‡4 S¥(¿o35¸ŽÛï ·>ÀèÀ-à÷¯cØ¡/œQã³Xpàè›wÂâdI‚6&d•ɲdÙ.rÆ0ãI)|Q^`woèŸÁ¯m«IFç8ßôÛŒÙLl<ÚÌí=³fì}Œ/¥$iæ\ °-¸…ÅÛ?3bûã¡\ÞDþÈÞ{b´í‚&ˆ.¼„í?Aõ@ÎÿAT=¥‰qóèЖšÀ¿¤G)QGÇØi¼1w‘)îúøm³Î⛀•h)- [÷³“ @¿áYk»¯­xÍÆMVÂÔ¹ý(»^–}-Ç'Ü:6DËLºÑ†ÆÙš}d5 ¤×áUÿÉà(PŽæ’¤íaó-I“p|¦£·˜wçSÜ{ ßqÕi_r5sf¾1RzS'…•TNGÜ8QØú”±æ sÇ fìð4X9SÒ¸L¡È~HŸ' ŽÜQ5~²3),›ÐçN2ÔDÃŽ‘UHFgÀ&E Ó(Áé§2ámØûëÈqJFK&üψƒ©üùT·òÐC3×ÂYiš¡RŽ!/ÐRÁ J:bV£4,<×rĨlËofy“lp™Cñ¤A‘Û"¨ë}ÌČ״ÓÁÖ Ïpžà|Ÿ_ªhÿ™ý´ˆó€MqOůŸ?1IÃ_¸;ü…Ù n™ì—£#Œ®G3úÛø­1–½½~TA™è%x·uÝ„ÿ×¶ù€âÛ™Ä-s€•]hμ;ÃG«t#È¥‡wa^ÓSàÍ õh˜õ/^Æ\cv°µ„±…§GR— ”ß)~_qí½YÄ!³ˆé¾(ÄpMÉåÐ`AßÀë:ò§v-1Q}Äì)>4Î+òØÏ+EÉê1ªéYŒ Åî®Ó˜:GY˜ö¥±ìÃÙ­ËULqµ çÇO:­‚nË“9ëŸá2§÷욎í!bÈyx÷ü¸À¦ö\`ÅOEãÙoöøÖ_œœâ«™ë/°ò³|X¾Û…øL˘ü8ý×Êàz2¬ü:±ÿëUç’󎲺ÛвÝÖGeaü u8ßúžõìcå8ú"{\‹;ºsJfÜC‰³`§V€kMrñ–ËSLË-bùÔ–âϱHV~³;¬5ѵ‚tæ”ÏèY~%ei×jZr bç7ãñÐjr[®k°ÏâÈ¿/˜[b¥@ùêðk¾(Í~á ‹ï¬G›S„YüªŒžQÄ©c3þ;Cì-¶±ñW#@^p7dW7Ü Ú÷Rf-}ƒ*jRPýí !ré¸üÀw¶B5‹\T‹—£S÷L]â÷§W(‘¾€ ÿãØ*nâë!`ßͦŸ"5霰>T!!ði4žz¸aùkEè\]4ý÷!ˆ'—é A6g’)Yâ_̾Lü ’¤h”¨Ø5®ô4{º Õ•VêbQŸ½~1’^Ïñ[`A‰H%f(ÑêÙ ¬mŽñ‘»Q10’Ñ18ÓÎÉÂ_ªJö¬½Ìj®Ž¥ÇÌéókèìè4q²×]„3XRÁŸ ï,-cx—Ûw¸àÔR:¾~ëE+ìè«K¬¦ùÌÎFªe‰ÎÒÆìc#Õë¿A\žLÕö.&ÖùŸ™ða'*튺ʥ 1]"þëVœÁÏ”1òR Ÿ$UpI —õÈÎÝ4k©>­W7Áy+Á:¼n9ðÓÒº](÷æ6,-û„’ÜÉtãÎûìêõntö¨,{„[ 7ÃA)L•š®`x|‚åo¨Ãÿ £Û>0‰Æ³˜ïʤìeW]þ€oR‹ˆî½Õx`Él:÷d4xn8ÏNj_EUª1÷šTH6À»jEÂùz–óßwŠ%â¦x+$ÿ–rîŒÁ¯/Eló©¬Z¥"¦Ñ¥$EýÛ¤Yˆ©EiöŸ[Áù´WOÈÍ!Mqômˆ2hÌ­…-¢·qmm꜓!{-úðeàð»ÔŠü+`[åm¶ä~7z<¤Ü-™Ÿ±ÿo(Üë–$ü¿ r_œ»Šª¯oÂñÜ»ˆˆyz“£HÆ÷C g÷˨áÿNÎZɽՅàʰ±>‰¤sÏ7°_ O+˜@ïö­{i$²ª“Y;µ> ÐÞ‚æ nbýqí‡ýpG•9.¿4ƒ;Íf&Ä ®¥# Œ¡Q¥ ŽŒ±Ïuþ0a×ÖÃé)òä\Lqu•"Sõóáì 2¶麌±‰ü÷Œna…âR™É¨.Axž!ÿõ$Ûr¯23çhÓãiÞ4l… ~Ïÿ…‡–¸ÓcÌ(3ÅÆGÙèftË0ûOø¿_åß¾²Î}fÒ³XãaGó¿™Ó²áUdA91ÜàLŠ ˜³3”¹Öc³è “æ™UóéNw³¦mÎRÜúz–Q=Kž¾m„Ø~¸qÊ'æVòANï7kì6œDmÚK>.ƒYÛ¤€Q‘Dë’~Œvv!‡JUhЫF¦Çk3§P]º·98G’&OÅÈà•¤>¿–‘_~‡ýþã~T«Ç®ÕÛéç3'ðæÑ<¦Áÿ §*ú#Š®jàLÎuv‘­Ò€ø)ç`¦e wßL²àü(ãÊifÒÔÔ˜æ¬NÕÌæ±JGïrÖÆâá õ¬È™(nö„»àÜN™OyÞoe>WL]óïÜÆxþ£$Î÷ ;h`‹+Ý[À59ÜÖøÞO,8m¦,'Î7Ñïä¸xî~qŽß¾«À:€Â½PòçU,LëP¤oÌAYÔŒÚð¢Êzüt±²&N–%½úÞtq ɥ爒O8i?¢OMr3aõÆkÜÕ𬖠:G®Ó£Kþ‡K§§²ïÔø $K”Ýq~.„ $B©g/*.mÇ8‡ÞÑ PïxÄs6Á23À)Æ4¹}6(¤my¸_2‰ ÄÛÀˆÃ!ئäöÿ{ŸÓ\ê˜?‹fÑe8”Žvp|ÏG3ù×s1Bú(óVF£Ãiùb5ºe]*µ|<¿XŸÀïM bûM<»U’•Û(NÒ= YµP#ª'@޼~o7€Œ›1™³p ÂZ§Ò[¹»Aøaôôž‚‡RÉX¾=¤-ŽcvÀ&ë«ýõ´ö°ÌŒôf”»WI_ –Œ)ÍiÂË;±ÿm¿Nƒj[Í4´Âs¯ê˜ùÝ]¸V ü›îD ­ÄÈše£\<¿J3¨}ÄCÖêg:õZü ÷”bÐ 1jSŽæ®v䔫)«åâÈ´ø°Q—ÊZÉ0vz“)O‚.Y?o)”ÔÇ‚&Z”£Nbó>3_Ÿ‰ÑŠb-X›Qƒöåz¤íÍ\hr<ßÅr[“Œ©2ït:ãº]rùäÜ›C…ÊÉ»{Îh©<È_Ñ€’mgÈž}V”ï¸Fª&Sç ²Y'OÉ.db6lþ«ò„gçJ¸yà/Úþxʈ ¸üwîôÄþÏU!I˜ºì3\Ù; 2î3 vP ¯­L‚kïãð¸ qÙYå1tá*cZ“³|fÜE>žqö}MoþWùÌÈk­'ù#á˜`×ÓœÚò…UÚðò²llÁú^ê|{m„9|#„5e,öng—7ó£=1ÌÊm§  ?2¶ZGþúÛv-ÁE.ð™é± =á`Ôà÷ßÚý,^*2¥…ùXºD,E°ðI52擱¥„Ë*+┾+¤7á\Õl* æé ‰8_º%­°:Énî¢Α(­¢8‘ÿ[dâ5Ã… ÷ý/†‰ˆ‘î 'áQÝ öô·ýô·?“qpyjHáøî•Tç¤ ØÀCJߤ£³øgè9ëœuÎ.ÿZem `aJ´õ.²ž‚‘rTÚÍ k—ÅQÖá»JÉ £ÔéYa"œqnH€J£NyèÖÔ#¸ÉälX¶ˆþœßÆÊN{‰ËqKÈ(&²¦ÛÉÐ b<ôù‹>8¸¸—÷.$¢)Àë^¢±Ú´8þÊÄa¡éz—ƒLLyå¾”m8K}ïÙaoš>øî^_f(“Rç|b.â;ÿ/´›A¬Ò½TÊ 2‘…ók£p™ðoôq"+.ç ÊÞ0kø s$÷ûÈ0ž(ì›F÷{ÓU÷ق哈yZ"®2õ€.kîò¢,Htj`$ߨÐÛý¾/¨JÖ¬$%6!´wz4Û`§Mÿý• 2Wþ1öõ#à#¨Oç†18íNœ;Éy¢KÕ·÷0ÁcöxN²Š¹œòƒI-v#jÑ÷pmw)*Ç‚-µSѰ{/ÂCøZ‚OT¦‘)>ÁPÜaºßkQrº€£›çÁÅ’Aæ•ï¨â­æ·pMsA¼úÝ“ý’'âÿrrÌqf¼¼ÑF¦™yÿ–q®´ÁsŸ×x9 ž.LäŒÌQnóIìö耕 -Èö¹üôŽÝaŒöéÇQier´Ô|XíŽ{î.¢FG#iuE2*–4_i1::¹½]¼ qŽºÇ꽓C ±Éw;®Âµâáˇj&"B;O‰‘éÞ_Ñ®+/îYÍX’4ŒäšÒ7çMˆÀ1ošÛ'Nç—ÇVr›Ä÷¿Ã›#ÙšU¢”]ÙÅ5ßS-7uhúªEÀû¸‡í-YN×eåsONÎÀœRuüØ‚ìØH'Ù5÷.°xL\6Ÿ°¿q\‹¬hEx¢Nï1æôy6(µ:“€Ž,ú•YFE×dÃü¢œõ=·ä­!»z™·- 4Ý@»Ÿ¦;…Óò?‰`¢÷˜Sä“h;¶[œfŸb¶&àþ¥(qh.ÌKŽ&ΧÖoŽöÔªô/­'Jƒ+éñ‡ã9ûd3}\|b¢tŸ›.){ÈümQ'™x‡Ô¹kR~‰éTâxÅ6c·_u _Büƽî ðêK;\õɽ҃„¾\‡JûO{“µ0{®&ézCjœ"©3*¾G’4 òØüŸýÍ“ ¨$š³_i/ ty .yhZ$yéNôÐ2T¸Í\oXG•aw»¢šA:wNJ×BrU<|¥ÆÞÀáö4úØ%Ãj&ÑÇvRÛZôÔǼYX¹öžÔMl”#e 'ô-ü¬‡ú­Zä‰! æ‰ÂÌš8ä T·Bæ•S`3ƒë$ ö³x ”àwÅ3ÔäPA¥I qk¹ÒäŠÇvüÆþ‹žødƒ›½È¬Î_»oâ®?ƒ²!ÁWÝŒ¢—R™Þ'°}±6Šp(/)a†Ç˜ZñþO/b¯ªnâù:µÖ´‚pÞkz0a²%™_¹vZÙ3¢e ¼ò3ZìGïWg±/ý¹>ò,ÓÍûÂó:P¸L†öÞå'ŽoÞ°¿>77[.O¦ÇN4k,†µÑ•¥]Ûïcc»?Yw}7쨪Æ7.ɘ½ð 8 †ƒ˜£¹/­€Ó¦œc÷Þ›Müþ9Ó¸¶Étúp%8ðÈ‘w:ÅàZ˜‡f©W˜›nñ$<à›U×\òÍê7phÊ|!ºÅ^…¬ë{…'½êئòd’ÿ†üßJГ4 ^êÖ“OÖVäUûq˜µÿÊ„ý““t÷†ÃÂ[QTìü:Ú@Í9‘¦EÌÂá^r:{Zv~4€ïI‰ä™Œ\‘¸ƒ+yY¥^qª-{R£ÈÜ.vŠRùsZ—¼×¦2w$qþ»ò¢d }̉µ6ÔqÊêW'Žaú%80Ã|æ¹-yítQG8Û¾Ë}‚ÿ •~dþy@‚@rÛíÆÞJ <› ™wU<ô—kìæ AÚäI÷%³ù|“q‰ž8Ís$g?¤¹4¹«šÆ4£uÙwiˆO…cmdÒsgæHâzšÏwçųF«,˜zÓãT}ê0û‰=‰g¼Lˆ÷ëd·Dî=²>-drçâµåíÄ>îµ9êC¯: ±vJad˜¡-Kµ˜¯Ñ~r"ŒÓtjÔmAÚœ\¿Èݹ¦ h‚[¼Õ©³QÄ€#iíÔ¦g·ëž—fd|f¸¦ Îô¸A‘À”•®œð?ïH°[ð½&~üó‡9+²ö=”^[7Ëâ=0}{›­­KT§B¦Äa½^cvtöªâã Ïoœspkâ{¶åQì2tcSØ.|Ñ´‘ÞùÙ'BàÏ$8½Dd·­`ZÏà¬[3Hõ švõ\XŸÏg‚©e)H_º !>4'$йµYÃ…uÝ™Ô# ™ñZ`÷ “@Âä.LàBkè¼R½nNd…4g Üè*ß6ÀU·,•¦ä´ÀIž¼ ~]oežË ‘+y²r x(ú²#©úwÝ?aægÒAæh-~ªIe ♘„  lÑÎÖï#§ƒÄ©nb, wÁâš$JFó8Fz~lkG¬Ôؾ¢Œ—ÙiìØ¶;x´ Ï;–žÑø„8 Õ3,a/zªÁíÒï0tÖ-¢•?š¡æÎaòs»­ÝK~/%OW=GÛÒ2àßÝÊ\•ö uÚOé´-k¨×ã9ôõ¬]ÄæO"vµ­Eç¦rÖò1©r;§e¥9­D­KVàÎäÒC„ Þú7kÍF&òDYX±Šl;¡/«¼˜™§JºCìSfq¶"ü×£ýöG¼Oaä³=Ñí¢0³âÐW8Û@WZÇa|ü;8.û‹x¹êx·À½¦ŽÀÒ7¼äUfúÞóÄPõ—È'¶€yx¬ Ý9ÿý¢4ŠÉà‚ÅédÛý*L¶|Žã¯ ùWžÝ·CÝÞö Nè-ÌÓ‹Äâ]‘ã¼/“Ó»ú:šoв©^œ|ùÂ\\¦Ž‘Ù‹IØQÌæŸ£G’±_»šSýh ü¼[ˆÓ…L!ŸyeÆGÙÿGÔuÇõü}ÿöÔÞ[;M”õ~-%•ÈŽBF¶¤½4´i %‰”­÷ë\%d4HFFHVF"~>¿?úþõ~¼_ûù:çžó|Þ{^÷>½jÁþnÝ„'‹÷3ÎÇžsfGÁê AøÙ¦Õúô™Po_èïû— ×±Ÿ?Møÿ•ªÃ?“=‘ËaVët@åºÝ0fIÂúV°½?ï3|¢šlÁN^úáªûùnX“Óö¼øàê1¬? EG¶,B¿™ãÿŽ]^népcY”KgÐÛU‰¬¾Ž•7½Ä}îTHï>ñ€´ç\x˜p…­ÁËB´÷L,9Q#Ížÿ½š$­\ÀܬÛÓÔ PbH‡üü‚VCs‰“ó9úO Â%þpÿR&t½9†ƒ{afÃ?½qºö_²Š§†¯Ñ9;N}ó#Û’’ º¹—½z ©9Ü’¶¦?ó.€'ß[F&ÎÒ^‰Ó`³ÿéÿ#K†áÇìœÙã€i'|ÈŽŽJZýRšŽiˆÐè‹¥Ðݽ-yÊΜâ¡Sm‰ Õ=\B ̤"{ó1jW¼ÑJ&-ò{É$Ý\²á•q´.ÅK÷›p`Nù¯>9±ö.Öm¾:+÷1Þ{´Èý¹ü|'…¼ýè+o-ZÖðX›æGŽ+ì,ƒ¶µætF|$ºú/…I1?àÝêZ]K•6Ÿ`çpÈ\»Ó('ZIöï߯zN§ ŸWàæÉ}LÕ2}jP¯FG…1¯Jrhµ~>{æO:•Ð?PÌ Ò)Ë~Mèß™[óàtÈ6i~­m2"zã‹iN©(Y=ZŨץ‘ž•.¸Í‡ò„nA׳—¨Ê9 ÚP@pƈÑ’±@Ǫdjw/{Â|©8UNÚWÁ:³X¸¦]AÕB°=-šlJÍæ´ªÚRÇC·á,69´æ-´ž• ¯[ÓÑÄv#÷ÞcÆ$Ù!ãäíÿÆx3fxò#œ‡»ïôÃ)Óxx픈Ÿ.÷@‘œ ~LM€´U4·jœ)LÆŠw3ÇL¸r±¦öîzÓöMéX__…Ô9”è–ˆ]éäQ€˜gðŸBúió„ÿ[}]ˆÑnÑx4×–Ô¼=‡|gèÖoqD´»“Ù -Ú¼ÇI‹ü¨0'ÃnºØR½·§ÅÑØEl C®ùòí¾bŽOŸC^Ã[&¾:#Û]9=¡xDá«ðç"Þ8` Q©åèSÜŽM_V°ãé†ôAíдââé9ŽÜéÀ†™Od<Œ‰¥ ©[¶AJ‹V]‚áÉÜ„ÃFÔK$r»˜ïÐÀÏŽáÊyÓ¯+CØK­ ù1¸>žÕ >F—aë¾zHé-ã¨Y‘ÊSó`‘¤ XN÷¿Ÿ ìô@l÷F˜y2q"þݼÁ[G>£Xëb¢¿&’–s' *ôùÏ4šn³…ög1« KöGª· Wž²"õå} ×}ý½¦yÅ(ßt[RcDËAš}) oS5È®³&Ôºå1ˆÝw§kFΣµ» Ñ?‘ ƒsc`w˜N/ùÌ;ÝÆ¹`ÕQ=ð;}öu<ž½…(¥šaæé³Ø0íÿçŒËàå—{¨~ï5Ø™®—œèâù©ðòï ³ðÒb@Ý—1dóH¸r½Ÿµ2)¦%«Õðòæ\b'dF5ÝhìÎ%ìWYNßš“È~™?ûX-7^ÃÏj¬· bÎ^QÛu}§ÿ†µ–ƒßGúÏÁÒYïðkV¤$§¯ÏSÛIx™[¢9+ØR› 4V_Ãc&,<û“{RûãŠÿ¶¿äzƒÙ“)œpŽGÏwì•6´ØåœÜîÂÀzÍ0¸_:y"þìšj¦Å€éš°kz”ªºPYÇ&¶îznè‡[™«©!Ô¨5Ž—vaøyaâÌ5¦3æÿËÃM­8gCÿhÖ`DõQÄ‹|d¸a3rtöB ûIüÝ)Hÿ›ÇYÀó9Ì-/‚Á,Sâ ÃEÖ_—¥þ„ˆ«ÌŸ…qxwG>ö®¡%ʼ„™öNm‚ù!¡T#_æãŸ&Ÿ¹û·òà ‘'ì£îýxæ{3gïø|GK¯™ˆ Úz쉒Eà:OÅv’¼-¨_ÎGîBýå£@¶¤ÃÑy.ôTœ5³ðË,ÜP¿â9硸ò]rbr²Z€ñ’`>)£wk,HÞ³@…£ÌŒ ˜ož©ø`Žn7æ–gIÀ°ÔI6ëúVÔN&~ƒ¦dåk_,幉>úfÔ¢.â·,ÇèTbÐ3ƒþÝ"õ;¦B‰ ÿýe[‰‰•÷™µ«q¾ƒÝ«ø ?;iPÁ·ª&2„º+~@Õ‡|,{3ÿr,ªÙå¹uÆFú·Rݲçxy±0í~^nûKñ7_.P»‡[}uÀ3é{a« œM…›awáüº+lÙU}FͼEò1ÅõË3Õö»ù¯3éG·ý;Ư¾¢ëL¨ÊËÕD&#†´×zcÅë!0Ÿ~ÞV¦Q9Ê æ¬E<—1k¹(*Â¥›³ès¦ÞxmL¤*±QêØMσ  ¼Ù‘Ëó Ç”_Ò(°bsÑPƒl‚_/ó@oÁ¼¬ŸA3Óa¹ö58”ù˜½×]Åñ_=…aäg&qý¤D&ú¿‚¶2ië&Cx­2½]žÍtêóó«+™û“à½V<|·‹æ.#bîiÜÈ3i°ùŨà•&Z³DðòÈ x&ÐŒB¬]\v‡ä‚ѳ0‚‘ÏdެdF<ëážîr´´/‡þèvt,ƒP¡~öżN_ûaòŽÄ|‘!æÂ¼“ :ð¿} ÿÍz¶ZÃöòðó|l¸»Nž¬Y…R‚\‹¾DŒ9cŠËÍ(;côÖµÙÈ3¢…Më¢oÇf´›†ìQjÔè™_R ×óÆ?=ò?­e×ÇÜ Ú|f‰™Á~tåÇ7ºÑœƒÇÀ|¡˜öl!½mÍG6—^À‹.™X·¤'7߃Cåa éÿ$ï$K‹žâá–þWcœõþ6¼nûá¿0:xN­J€‰˜˜•ÓÉãÎ|Ößž,ûæ»yœp©~3c~EHÆçv9hò8zšÂˆ*ZcòªÖÇYðˆ3Iâêddg7ѸŒ÷ìôùQè"pö/ÅogsÑBÍ‘ª¤$íëËñ¬´ ý½¨bN>¦‡î\›LQL:ÇNÝ”ËkANL|¢ý'™Ÿ`6˜_Bƒ6kÚk«H$oŠ“UöÖô`§>Þæ² ‹NA©¿+£q]p<ÎÃbýdö»¾#~÷Û†·E¾ƒÓFYz`J ã8 ÒÃJ!å”)½;ý9Ž ­ üiTxÏE8’4™æ')Ó§OÇ!ï¦>>_ [N°³ƒ÷B„“ŽFjŽ{ ·à»ÃªkQÈ·ã+Èi…eººŒNÍ&ü…«ã+éÀ%Ò1ô r,’ðß3@íBš~Z–Æ.–Æýc™Ôð¾%‹±V%câÂjÎ.é$³Æœpd>€ßâ*X¯˜…Ë,jQÜ;{¿lËHLJÀ¶ƒrèw¦.´  YaãÙÜÀv*Â5¬*þrdßy»’ YMŒ©f5\åWÀö+Jôué "ªÄËüé(å hDƒ“Öìz Œª›&-Ò¥¿WÊ¡×ä\0~Ö…ãøvDŸxí´…-÷ehCü)¸Tº^…#…@Þû&æe*Õ÷#Aóø)e/ùù4î¿¥Hê?æcvp,q|ŠOþâÇÀ$9l–O檆ÃM>©*î ³›æÒÀŸ|¸éË3v3w´±n>÷™·­KØyiœãþ¨i˜ .ÆÎñ¯W#ŸišS /¤ª Qœäñü㛹’$QB‘DÉÓ&LÆ>ÓÅe!»ˆ‚T"³»ìGvn ËèåGáç/^ÚþöÄ’Ãu9¤©ÉŽZû°¿÷x§#}k| ®—I2bœ,¸;øyç{‚Þ`l¹>åÄ»áÏ»HèÞe‡Í¾¡aZýjÃÿ晞½Ç £gýf«´ó`¥¶ìzU ~9Y8›««Ðy³2>`¦°UÛ}é‰ï‡Q®Õ’¼Xyê;äÑIÌ”‹^ ÂÕíÀ@y*Çô9ìûÐÛ–œ$ : èƒøk¸6ó éôT"†¥È$‘Mìð±Yôº›"ýØý C É‚2Uª±ð„Xô£É<(]fF†ò–‘eJð`Ÿ% TK¡–óÿàvãjVÝdpz8UZdEìücñ€ØsïØ/6ËDšäË´±S—Æ_Ö0NÞÙ.×€l?—”≡´&õ7†/ÒéÌbÛx<)q7ÍTB…ª*äW`;ºÀZÞö/'’h—*-½Ì5Q#høº„ûôž5¯kSÝàÛ7ò¢Ï2¹V10UÓ/eaÑäÅì6_]Ɋѵœ³œw3déµDi© Ò 7Ã@Ç6pÛü›ù¾2¸}IøHò›}{&؇ „îÃ|A âzB’eÙo͵æ ÈÜŸBG˜Ë—ûðšpoó‹‡B4¼î8c"2ƒðóÿÀ³"¢ôsz+™È~¯S‡0ß—É|ó·dß ŽáÓøKZžÏ]©x]økj‚ÅŽc®¢ô¸B>X:G0ªùz˜|#»íîÌ ûÏšvZ:—béœMXyü;ç²§"ýõå0ØN‡ü}?™¯M=ð;ê“%ò-Îw°úå^0`^ľ;à‰9^x–ûæU*“ôËäEÀsC™$M6¢!jßѨšŒñ€É\M2vt*=úä'ãw`!U¼¾¤o%à» "f LŠSèÜ^~B+6ÑEYîxdãœæœ "or ÍKöEÃáìÏÌÜ ´­]Lx÷Þ³Ù¾ 30V¿ŽA•£?µOâei2ZJaûz!Œ+…ØÕúdÇIÔççMÖX2ä4¤:¼2n=ïMøa~?£sø(c]“ýÿö]ž‹«ßÂÓ!º—‘L9>ý?òR†}0†NÿðùZ@ÇcYÒ‘Ò÷>50¼NŠP! ƒž¼Ô,(¼–Dâ2ÿ*0RH$ÆAÓH5Ï£ˆk¿édTI2¢ïLÁm¯ѶʧýáÜs!1h¤³Ÿ³c4ŽMÞÝÀ™¥yeã†/Žä¼ ¦j߰ΧKlÕÈæ½±0Õ ƒ²¥0ùà"ï²’ý¡\е×ðrósÖúü{l¬9Înîö$[â'ð‹¬YÆÜ·ëŨÈùô¹î6û„5þ>¬@®û«pZ˜!÷Dœºç/ŽÇÀ›sïY§ÂhrÕNO’€d«Dv£ãÀàBô;oŒçdžàæ~^W÷Ö~ÍÏ× ãM¶û\€.ÕD@¦‡=<¨IxÍúoŒ†Q°Q:$#Ù÷;â`ªÑ°ZõŠYyÏü¼„_†¾°Ÿõ,˜ššöAã–ä¿û¸Î£çeŸ¢Ì]7%ÝH•§5õdæÀíž7xFc2ezàÕñ\|}­³ýY™gõxóW:˜¥M™…ì”î©$ìT .™Èÿæ6UÌ€÷¨Ê Bc @à¼k¸`ˆc¾Qö‡‹¡²ç,t›ÎGÏ8ØÿW¯ O/8“Wù»égoaò dWÞU"§‡âÐèG'ûðäFî\ëÓhô™j¬a©Ž0c¸` »GãÔHøÃ'­¶®C˜žiˆau–ðЦ1¹§XúZê3ú¬NØ¿·ñì“K° õpõë$ÒkƒliÀ4T/èår° žË9Ì1£¾uç _Z ¤Â”!çØ)”›mNOWCì pZ]‹òpüès¼®r ånz ^#¬ïÑN¿:3‡N”LàÖºŠ<(ÖÎÀG:ù4sN,{;Ÿ—äT#X¬gË«Y£U#h6C“N ¸†òüIPò9R¹‡‡¼‰¡MŽd2בþÑr¤ç S}qÈFÈ·Ñpà ÈAŸæ´¨yñîg8"²™ƒvQ é\ŠŸÆ2F½ÙPèÅMË锇ï`¥N™¾y¾xv‹füãz—š6>ÓLñŒ8*ò–âvÉ{‡ÝgÿkרV?ƒÔݧ¨ã­Lü†Ôé²92þÅM¢§ÏÛÓò/¢DGí(ÞN•e·<$ÓÔ£\â"fKÊš‰þ¿sÿÞ_±¤2Lá¯Ä€<5z~ýÞ*I½­Úð¿yžÍÝs™/7ñå¹hàyÖO§sL™ƒöv|0jsôï^Á¥Jï˜Γ¹<øƒ&÷rêËAÖ—ŸÐ™BMyÖ’É›=áh±Ù²Š,¼ÄƒÊ K¹/cõÑuÄŽújÑÿŽåqªgß?b³ÃîCéÍçòYÉ=²à¤nÀÆ4¦£å ^rë^¡Ã”‚*ÎTgHyjºp ý•WƒèÁúÑè8|k@®ü)Æ?Ÿ‡qóÒWlä£Oè±' ‡)¾³%u£ç°Øä <˜º}býÏóáUPWnÁ.Ÿ’ Ÿc”™Ý~³ˆynTÌÓ‚šñŠrÃ3>_Â…«YŒçV<—=_ÙŠYüäšž,y=4âÊAÒ`õ:X ‚!„û8;îé2é…ÞÌâ ¡Œ°d;ò E!©<Æ_Ý–›9Û‹q­Œ }bÁd~ú2z/xÈ ¦Sÿ§úçÙã¬k‰ ×[ʶ>A-z³ˆ°Â^X~çkyš—þ:Ó‹g/êaÓ7Iñ£[ÁñÎ L ·"³èXÙ”BJ²ÕàEk?\ì 'íæ“ùAš4ô‘*Ï# ùÂdßx6ù¬ëBÙ‡·`s¾•K7¦eS/ö‰îQp!¢‘ µwÌ€ßÙûÐ×h/{Âi=Õð¯ KR*~¢ä$‰ßÁ0Föøº‘¦å=ª¸:³Ê6[SNI mÍ:Èļۉ½­ÛiÞ‡ƒ4m›nÒ¹…sÄéÐÕJØ”{‡È¥ÍÃA‰NP×_@äŽÉMØÿ­æ|8^[¥ß–À‡êlßøæ¬ú§ûô¨gó ¸ÚlÈ,zù„ûêáÛÖ\#®‡“áúÇíD¡³ÝFŽ‚aº&½wìŒF¿g[¤Š˜EÇóqñ‰ æ$Óê¹Ì‰i,8è_ðkÿeýy›Ý Æ‹mÔi›èmØ8ó_ý5WmQ#ÔqÃÏB*¶)ïidáˆ:“4õ¾ûæÍZ|`¶Ç²8ët5_ÜÄ®µO†KWoàSm=Ú`ž‚£Ó^àUÏZœ_5Ó=aæ-·Ás»èü½ÈÕØ_óœæ<å¨ Mà²ka^·Ô³ýT,]üXüŠ‹ö\‚ÂÙyøs};[ý ,\ɇ6ßE\{*gæÞÅ¡ynÏAw ÔzÇ™­^ÃÛ–Á…'ÿ/ÎŒÅnážSÍdêþ^†u§µÉÆÄ_Üç0Be« ¤±çÄZõâ´i㾺ãtë×Föú=kœ)QŠŽ.'±Éù2[t˘ˆÿ”ÃÃÕè* k_  Ä›íì6åR–+_Ìôó`J·*’›ž#Ìß»Ká¿k+_}€/þ.£Ü%qìÌ"býgœã9r Þ­~†Q<ïаl),÷2™èÿ¹Z¾‚ó~Ç-Ææ Ù]± o¸æƒëºcŒÊAa®zÑa¸’K×ÙÇB¢w8Ív[À™~úþ\·—Š?ˆ@Ž(SŸìŒu.Bÿ Sù¾1 Ìkú$!pA"‘›J³Œ<ñvm#›7å¶mýÍú2§©â Sâ¥¼ŠŒuÝcÖªE°Fã™$×]=rzñ·|Gèã&"}6‘¸ìB#Çx×=åÔ °»N”“ÜbF^9n÷Ú–Á‘–m¬ùmc*cßÈå«J#¥¦Ÿ™ûw17ï=IÛz§éžœFOcWIÒäîø û+¹BºV!sÐMŠõŒéÛ/bTð…,qhÚŽu½—ÐUÌ›¦gSÝÓ‰]!a^>‚ ÌéÔÓ ñsd*~ÎÌm޵ñCS#k˜U~§ÿj:9Ã<ÏReèùgµð{J8N¾oKZ¤Þál7öë¾rÆ,Â…è®T&9N¢¬VF'ò¯zŽ¿¢H ±4Ä–â©Èhzn†'*ˆêÒß©ËHúÀÙŽ¾­Owu ±2'LIÂG¢˜»õnXQëãnDÙz%{¶$ ×ø»Òm9û¡A\‚ÖtOÅ4¾§áiL·®£’@*Nš°ÿ·×Ýìù¿5¬ÏF®ýa.ݲîûÎg>uÕ±<¾Ä*,i°e0u^ôÛ¼‡ù2xæѬŽÂ]z #UÂØèÊÀ5†‡’òåô?õO›OîHÒ”{ÙðÐ[¾ÿÂi‚oÆáÌ<Ǫã¼>Ø‘H/¹U3Ý6S/ieÚ¥‘A5·ÜÕKq‡ÅøÑ}¶ «Bb2eÚ²ç±ÒOivî+”4Õ&B]‡‡ša˜»=ô]ØBîòÉÓû~™„壌•M-øGË ËZgZ¿‰õ¿–@ÎÓ4ÌÝÇcçt õ?ý·|د†§ååhųgÐe‰Mý¿ñ¢Z­W\OûþÉSèž”™d£Ò Œ•–f`x‰všFU” ¨[ÆV°éªgÄû­èíâ°˜ª¶]K™{s*PY$в›WR~,ÑûÁÔ.)›*_Ž¢ÁçäQ­E‘H_:EºìŽ2Bâf¸Ò„…w»XF£¯/=û®ÂÁ4m*ªøÔtᢻµ”ebbæã‘ê%8Øñ^h¬º„nÛ› Û/íJÎ%J’Ñ H¥ñR¼¿•‡jÿ4¦9W"èÊMM¬¤igó`uôDþ;ô)ž®?Ɇ.eÖN¥æcô©àmÐïÅÃ+ÕÉ»™§¡jãFÚ꾋úcŠÖA†fˇ¨¶f tWEõ)Œ’Ô°ë)D‰k¼$žO†þzÜ‚Ã×g‘CMÖ4%eèö–G‘—æGÉ’K7¯Ò,mâ•üã’lðbÀ]èn×§G¦6eMõ&º»ƒ1èÇ?~?poJ€Ñ«Í¬¯ÿŒ:ÄC$V?Áÿꢄ…ÐÂð›úQ]Jwå%‘Dè°4¾n›ÝÛßÀŸ B°¼HŠŠ€ãþúïYAX:º¬Ù§:ÿëÿ¹6+‰vï`¯[á |Í=¨ûf%†-‘& ü¶[~áoCä ¸6G aŽ;lÂò2ç¹ú;®ÀI_ÖŒ)ý'qyaIC"]£9È`0ÀOŒuóÇ3:qkY ‰§f>BØÖüü{ƒÁ%²lÌ2’?I\&±ÎˆÞè‡ØâBîЯ­(—"I|¿“cÏ´HÆ[¢k‹›"ì©ï³xxjngè ×jP 3,̘†Žë(F™`Ó‹»ÜŽ><É4ºÚ‘´È!n+á%b9NTâê|z±€Ýæ¾CªÄÈùUÍ2Z ±Oý‡‹[. B·Ùþb,ý¥'ðkíýÞµÔevO¦1ï¼0Â!…¿R|?G€ŒI(P‰Ûjä[–(«–Œ‰õÏÁ~V'TɈ1ŠéiÿâñiÖuô&&Ï $OOÉ“_Çy©}¾&±¾^ÿÍ)½;WùúvPN@,ÞЮcÿ”M!Uî|¨½ì3žš‹w"¦s–ZjÀûåÅSSÍ,<;ìÏ#lM›¡wgcϧ0Pz $vƒWèhZp”¡y ÄK/œ~¯Oϼü§¿Æ¯óȃe&vI+>ÜÀ‹Rëæ\V¦LÞT 4‡ pø/`NžELäBÝæÿ}ÿ¸NJ·Ï.B‡‹?˜‹óTÌ£>LÞL|ë"éê¶6cþUZp¢Ž Ÿ5Ž2ÓÅéO¹ÔuAZu‡bÓJšâ²—”˜K§~R!U¢Qp£bŽ, û÷gÁWÊO÷ÝzÃ{> ò›BL¼é˜p̾“Jþ›/úéŸUF“CîQt22$´­ÇÈ\%rv0NŒ†‘-î ^ߎ¶}÷Éôl†ðË~f’æþãeÏqX:…lײjØ??ƒ½^È®©Í S½¸+>ñ¡¯}râkU&ŠÁmHùçMTÑØ‡x:-ebµçOà·Ù¡;ݼaØr ÙüéFò&92Ó©Òˬ“¶µ;rK3w»C\ßÛeD'L”Ô§+ïâõˆ/ÔˆsY!Y›8ôïÌÄ‹þgñ“ÈFzs8m*>@,=Ò™îJgrêè'|™Ä‡uëHÀn5Òg¸’ñ_ÂÃü©ùýû¡û\/²ý'¦¼³¡ýÁk1Éù¤¦+-¢%¹ƒä:d¢êUD¥-‹Ð¬ák¦«I´®pqÿ™IŒÔÍhbæý äý3àªå5(yRZY–äslDü &#ÍFÄ<É Ï;Ãå%oÁ×U„Nyt~å…ÁZþ•Tpnâ [๗ˆA6´æÀáó xË\N.^KÓoDQÑ¡hš’Ï”=¾ ‚s Ñr:|÷ÎÁOë4éþ`#zÄgü2ŸGqÖŒˆºÎ"÷Ÿnµ#)ŒŽñ,Þ9—Š‹åQ>žp|«Q<Ñþ…þÚƒkt6¸ŸŒšwVÓm?y¨éˆ*æõR(û¶„Ü™&ƒ/f;}ÊzK„ÝkבÉÇATü|ZÈçJW>`«œà‰ä&ï† \iÞ‚ ÞÛáý]d3ûÞb§;D3‰œ òá¾üWwýÝ+|‚ÿmÿÕÆ†naéÑùœ%"MLß?{Þ6{ ½>¦ôŠ£ù%Løþždõ Cü£_x‰¦`ÔEêTN>Õ¯`ßYTÑéówà}4 3DŸ3½ÔïUÌJÆ},ï•­¦ÖÔfÉT°¯Ï¡BÃH+ÿ'2©˜â;…r¼‡Š­JÄkǧ2g¯¾„—µJôüpô +sË„ì×^s»äHè.YzAÑšèý,€z›@”¯ ^çiÝÜ\FSüŠY¡V¾Åü tO^©Buç_cø pF55<×tÛf!¹r÷0é4Œd¾×ÿïû7ËcúSF…VîºÇ'/‚[JqlÛÆ ”—¡zu‚˜¼kYº`X§]™Œ´˜sšžÉ7$Õ íhª#ÁD£pêS«M—¾É„Ö†y$Sz7M[1Ÿ‹)Å´ÌÇ®8\^ÑÓWSWÞvùEöyé6b¤ìFz¶]ÅóñM`v†xn°£IíÓˆ8­eV´¡œ«8tò‘y¤Â”ØÍ"¾C©ØúîˆÈleKC_3ñÍATvç%J·¦€[Áð-dDâÛð"]IsˆÓÑbhÈ7 º_òq8É‘¬l.Å®°åÌr3yÆþ-R¹¬zBózßdÖ·£¹ç){µ‡^Q~¥ýsáÄãN˜¥å‰ãìï9úøÄä³VIÆ¿/Á$Î:RòÅydÐýLWkI8FtDCÃÖZì“q¡om¶Bžµ¨{Ô;Èn‘„×Í_ °èlÉQ…ýÅßñ®q',Œ}ïã­qíK],0Ô 7Àoáe¨8ïòo-Àq0_ä,å®ArJ–ü:zŠûã÷ixèÓŽE2w°5јútZÿ OÙ…Ê–4lÙ;ð““^ñDCç—ßeævÀY×dø0kã–¸ 潚hÿA'Ø#wf0Ç~Ï#ÒVgïWË»#„8áßIÚTb«-mpÇÕuGàö¾BNÕã'ðU{5éŸG(ß=ðËl~É”%•ÃìòBò7'ÿI°Ç©e8; ‡#Nsˆ´Ü[PI>Ëmè¶Ù¥5ªâW‘§n°žÂÆ8Ïì:}ý4®ÜŒte.ÑÈì,‚ÞPO´:lËÝÈÙ…}Ë“8?6]…ŒÙ‚dN£! jR$×”<8£ëIBn ãäÀ ÅºKDô™~ÌÙ‡ÏxÈ•çªôÜ/Üy1úÕqîÎÏì¡‹Fý¿O^ÉàŸžÕ˜·=¿óø¡Ý˜7®+¦·fƒâÏP*ûšÑÜ)˪»”ÎnE¦|©hÙ…1£vÔQíMóÞÖݨÿÆ M³¢¿V²®ê2øt.Ô¬†„ü9ìZ“é¨V¹”ˆýà†Ê}Çà±—¼)”fÇÖõ¢ú×ýä¯}w~Wªè,=ÌcEBN“ÃóükßÇpÅc%ÎS M\°¿íhâ…yDRÏ‚„×DÓÇI%˜|K‰|·Ô›ðÿý;\¼× Oò%AlÏ]Üë1“Ü.BtjS¨¶þS&Pþ6V¶4Bh¾S´¹ç×½Â+páÑŸØ·0žJ©@àœ{ZŽˆÔas}~oGÕá@t#0g³ ~_ù»¢lásÜs˜þà7.jòGW+qBž©’ˆ²\{i<¦39ô½ÁB\Z4Ø´àûgóɶ€LL븈oâÈ W6~5¨ak<çÒ÷² tê9à s ®JMƒÊ»Áu½ ‰º'Hú¶¨€–+¸>ãn´!«1þÁWܽ o¼ð"Òiÿ[ÿ,a‹,Ý~r%$?ëœäldPy¾…Ð/a¸…CÃËø©ÇaUž¼ç¸hË_fL ¨F‘uˆPê$²}Ž õ˜iº9kÀü7eš˜Sp í1M——öÑß““qZM'=Ô„I6먠•±mõeþDœ`º”4@ZêS¼H ùÁ1ó…O £Ø® R°Ýq6ÎH%WÞ‡£í%ú<Žœ^.F¤ùôA ) Wµß†‡©wà’©|C„j¾XåÙx¼jÝ»Ø5ù· z”g1?nÀÞØõOmÚògQR¶œðÿ¯meì³ E̪+O@n"O­&O­&âæ38åp)žœ ljœQÓ=âzá_û ñçüð\@OÏfz_D?1ºEôLeDMáÔdc²ò­ ¦×^æ>» CF~™AÓ·å•Ù…šv½Ðâ0ï”ü„ïá½·cœÏïD©×‡HºóS(÷'«Ëq¨µÎÕ±®õ'ᾞ>3TwvÕÄK#b²ö Ú­îÆš4ŠêçìU%iªsé—ÇñYÅB«@aóà?½é¾(Þð<ŠqwÃ(Þ^—JYxUÄx‚ÿž_…C(ó(¢ÎÌG¥‚2*¦9…^}©Fûc[ËñÇwœ¤ÕFÚ‹’Pk/Cø>ã5«hzxú/Îâ²P÷Þ¶¬|ö©÷˜ª_üÄeHˆŽ¶9AfšÕþ+HÿÊõ7 "w¸qK‡ uÝ(—} AÞ’àj2Ÿ¦6þÂóã*x+~6cïMoüˆƒÒœTü½^ƒYþd&k*Óê]aÜ‹ÜÉt,0•ßcø‚‡ž-qôz 'éä;`ñaꥡM®Ô¾f÷kzÝÇ‚,ïÑDC» 0Ëã!'׊áMn*ó*FwÂÿš"ð(g+ةʀŒÚ[<_{BŸÃðÆÈRŠêØË“$ñ V2=–Ë_f2M¢ZL Â]@†k?ÚDˆ°°,±$–Ô"ïjýÓΧ>0õGávh'žûÄl‹€Égô¨îÖ™(?ßïœñuª,OÌÌ| ÷¶N!úeÝlꡃøg—/NÊí‚Çcz4¡œ‡96é;GD˜ê7=f ÇQþÞfÔ3 Ó6Ç‚ô5S$¹Fú¬)ÜC'÷h’x5 M=qV¹Kqý€<ŸKxe¹s#ÕŽ^έæ&eXÓdYó ûßd¦3Cùf±ß/a]‡¬²>ÛŒžÁ‘®`¢zƒ¡6.UÌ´ë=¬°y7ôÞÞ¯BEhþ—ì 1°ZÔ…²§þÅ‚¨f$.  ~Å~4Äòù]:–èд'ÏœBE*{˜È¡SXŠJøÂ> £|~€üÜ ﲆgÚsÙ¶ê•pëØ£DûU,L[4—®Õ£¾ ¨Ù"{˜bzÍSˆI2ñwra§­mÄS¯,IÛ$Qâ¸Æž”yÔLXŠ <Ð&­Ë_ã‚?:Ôt‡&¬÷Ñ€S+wÐuËð¿§CÄì°9éýDü'-íÌûï\ŽXåw´=aIË4ÉdÛlÕ;„=äÛeA¤º=°òj'\Õ¸‡W³ðæhþãFs­UI‡?}ø§ßæÃ—ŠDÕ“Ëj•;áÔ…PGò)ŒHÙÁ¤ ÇP¼8–€ áöÅŒ¯Ynsë„ÿ¯Üâ¿ÖÖ鿠¾Ð´>…Ã.k bäÁŸ½þÎŒog3ž¤ãìÞ©<¤ãŸ¾ø¶ÓÊëýE×*-Œ9lLNÚËb¯ séÃbÊÁËN†dmU2þq?…{»ù¨ŸÊ_ÎçÉ3?ä% ’y¸ÁÕÞììæ~<“oÃóÆøþ²/yï$D¿µ7ƒ¿ë9¸‘‰WtªØ¼ÅG!2›‡Ì¸ó ]W¡k—%o(eûzŠà‡´©à¡" ¢ n6 _¦—À°£5n¾èŒ)“äˆz¸É³  »jÁUHž™ñ|-äî -;ÿ·þë_w/&a<ö[Ó­¼ÁÌïátæÜ(ë>!¥ãØ9§F¯¢u˜/þ-^—Øjvþ¿vøƒmfd]9"<¼äI†0Í|µ»ï²v¼©L°÷]Ü"èGŽoe¿<.`;s»ñ¶/“x%‚í„èš®S˜+8ƒµN¡Ã‡/3%yè½ ¤åØ.°Ÿ Ñ]¦T:µˆ¹³$€ªà^‘ Ný*Sæö~¨üR»å´pªÏw˜÷­OM{MžF0wÇö%ùȨ5û³zÛý”8&}T–(Â÷Ý^Œp@Ã.Øì°ørx•|˜ÀêQ8ðÀ†°4FèÃM8¼«ÞÈx³ª†Ç±¶æ WÝÚÎ^¦›B€n v¤ÎŠ—.ˆiC…À﬷ëû\­Fî.žŒKvõv}i­ W¿ãàÞw¸Æù&Ü òäs¡7│6Íz¤ Ð÷ú9êoJ?%ýĽËÅaü'½x`)ãþÂÞG‰ØÞiÚRGÀ¤Eš„ŒA C>ä¿’ %)&$n.ËL)DV.%B‡iZÎ)è,^§‡˜¸è)ÈתLÜÀÙ4Ú£ áo-é×~+È ÐeÌ^ÙNðßÇ{7±ç-‚e[ Çà*Xô c´ç]ûψû k&éùAö¥–(û®@–\]]Š1gêñ¥ÌQP”nÅøq-ê`Kõ½ðù›hfïêHø|ûH9Ã4ˆ|à%­c›$&tÇfi¿,I>"†vÙ_¡ðƒšòb6~qFc<ôÃÔ÷Ü|V3¿yù=éQ©µ(ýG›}]­Ã (N¥¥;Ç9!~'±Þ{œ|¿¹ws¹£»¯€ãêZº €/µðJ†!½ž™wô×@Ù9YrJĈëVógg\cÓ¬f€©ù$Ø0ó&{ví Öóøá û¿­¶B¡þnö}à>è¨;ËuO©dn.’c7ëãÜ÷ÞMŒŒ´PãÅZ1Å fâåüÍàxx6QKàEFït¢ñf”Õg@Cv67n‚ô÷ŠXbu;ÖÀ.¿é$±ãŽÌfoþ6&6²D¢æ èIa—Ë3Õ?Á$­FŠ S7,-å§?Ú éøýÛh÷Þž¸¯…Œ§÷9²ß+™k§× º‹·6чYß-húÙ—¦³r¬†$¨î¬dŽ-˜M’e^1÷ŠÝ©æÍ›`yð8Žø“°áp¸à#EÓŽ^‡“§´'â¿Û#K:”ö—-&ÇWÁd)OºyëBäÁµä„âëƒËÙn‡¨Ý`>0éÀ%Ùû Áýå½ðY¡q=µ€ÿÑ(ÚÃǹAL½˜)•P %WÂøØÏ ®BÉó!&@Ç#­É±®Ü0ЂƒEœÈõŒˆb$c´[ìÐ,f W áÁÈY‡»áùnG˜¾ãeóþ,{H‹ægü¸¯N¢›µ,hËË^G—ÞŒòeR•ÈÒ©ˆ»ê 1ýk$å}üîÇýoýS§ö©ìúe8¯*Ïáãƒ1Üe+ËÖ9ƒú×M£}PÇüÓ½%xÙ<“õ§“Çà‘º»÷Í_æË‡c8ÔoLd5g±1ýŨ>‹eö¾£7ù»ÑÞ^ÎÐ8Yª¼º•9¥7Ο^„_ÚwA,Æ „×etV©Gž'.¸\Ú GC$ˆÓÇ<¦åøcµ>ÅòÕLôÿŠ+Ÿ|>ƒŠ±Ø=²¹¥ LoÔdf1“SnàÞir¶=˜æˆ=Âû©¡t™{i\¸‹çÍ#¢‡ëÐÏy!ÅŸª˜4ß*œdfžÎ†úë9;ÓˆŒÊ{vôú,²åÓ=î†‘Éø'Ï—üü{\VS _îNþL‡ù‘ä[é8¾Kôc튅AðÀv{ø œYf #ºô)ªü‹¯—!M tVi­‹aôM‘?ò O¦ sè ‡~Ž”§ï$5É\[Â;gÌ÷¥ÓìâÈ Ó¨²®žâ2âc° ¢ŒNÀmÝ»²Ë'â_½².sãAýŠfVú)ŒÐ ï_í¤û]wR½7±&n ¸ §ÄiÂùH°Ü\ž…3µ‰Ì·H:"¤¹ä!^—O$•OâwÑ&¦SÅ /|Ì`ÒéTâ)ÜŠÜTÊ\7ŽÃì¯0ì½69Øõ¥•¡uÉzNZ–¼æyU'd`Ø=î¼Ø ßÅ!…GNSN%¦¶p¶ÎH"¨zšpj%iê½?ÐoH¿ÖS«?¸¼n;‰ÜãHÍO`›ÈFVØIŸ†{ QéßQðv§6pÚ¸G ‚D6§“ÍË‚°ª¢ ýûi]tŸŸ‰Š÷7ñ–<ÖxM ¾¦Kb¯Ñìõ^ØY$NyšáµM.\¥ëhø™#äÆŠ¯LÏK¶f¯±÷¤$ýõM€hÕ PÇ/ éÎÕI,w¾í8Æfù¦°/6$Ãô«½°v›1 % ÓHErÂz-í >1£à6Î8²DÜïäPó= oêÊžon@é³S¢[…ÌhŒ™6%z¾x6c!•oE—E½Û[ˆ^Üd²a¦ÞîHhøÒÍN]ýÛ`"þ½N¹ˆ%æ×Aê„‘”3ǧ'žqn\:Mê†w·•qŸÏ>v$ý3c*¸óçFPý_Ç@Ð3{øÍNäÒÙCNP»÷3šš{Ї™·¹oÉv½K™€Va²›Ï+Ç4èù9Ð÷x²•1ª ü“ñgÒ&jÓ˜ºM',~¢¿êñ¼Í\“Ð%· Ë©Ø`¸Ó¯GŽTîAÛÛÇÈÇÙ•ùøÅç\ðÖ¡—*ÂÕ“ïPãè4bÅxAß¶È7Y ƒ‚Æ&ü†ª2ãŸ3I³t'E.ôÌŒ}¸]=Tº°nÒkzzÊtÚQj€µ£†Xÿ½øý¹ßl²d#ÄmÕ@Sõý˜úüíõB¦ßDmê~t ¢¿µÛjT®A,È„¤Üø‚É…¾1ð¾A÷»,Á‡$®l3Z?´•ÈÆ+?àõFÀ„ÉãÒÄú‰lZ:‡:JèÆé+AdgÌy"Ooï€É»«éÌÞ_ˈ…5û”ÈVgäÖµ˜ÑRf7| ÔERCsU²Ò{ ª_z|™Íÿ¦C¯ÆÝeåì—µþö÷œ}˜¹YŸøyÕè"±Vˆ¼#DîÊÎ@CÞ ŒÜ/i›UmŸ]“ ·:!Iô6.ø,оTñkôN=…ãŽÁ@Y» Ø”¢¦4 «Ç—‹ð¡C úËOCŽ.l ˜+áu˜v¶‘-/“&湚FîàãŽ'Xâ:ƒYÇßé{ôØÈi É¡"Ð긞¼q0„mùµœÝ”ÍéQE2<ïì01£iæJ Ò=ÆL- Ù5dX\$*ï‰Rg¨|DñÔræÞ…xT‘â³óÃ? ª­P̦óp¿ìËDþó{ÄUñµÇov)Ðó ‹Û78J¶ÖÄÿõJLgËÀœç1”»XÁÌþMtiÑ-˜<³ŒÙûja£{q¬Ù–¤nLÂ΄ÜEgÙ©]ß±æÂc`<²Ú½Àò_zXrXž4”;И‡®tÜi#ùÐr—ufÐ?zFìÖÏ(û„óðg)ˆ>~Ì ÝtŸœ­AC$ª÷&SX“À ÏøÆÜ kbÚI“„U­ÿ4ÜytÖú„ôN3]–âkI7¢ m ·¼Ì ¢{3ètN~\€Í–‰L¹ý|g&qÒò,R²Ä†Y‡"µ‰ü— |j#¬Hù‰XHÞµån@ß9e¢ÕkNcoc›´8mÿs‘q±üÍŒiôsO»‘³ÿÇ¢EiõŠ;óöAòÃCldB/\¸16O ƒJß RYðÑü–DÁCŒ{Fh ùõЬÜ~Šê{À7át§œ;N>”N6…u`ý—88IDèq¥P"¥dIEüÉãã§[wwÉ•ïøëá O6!ÊŸ M¨¾½à§Ò6 î]“Ž]%±hn1JvZ5ÃÁ´uss(„z­½ëÛ%è<É8ø‡ÎÕYC_TΠ=òšø‰_\JØ”ÂÄýK[!ý ž±üäÁ  «~ jªîÀ“^qàÙ`à²Í¬X’- üÓŒ/ßÛÑÆóp4mò‹{Æ+.’b?Æ[ù!X,â…]ß·éBOpðYLy¯°{,q×ÙÊ£$NÞs§ÎÖ‡a×{Ù5΄ãì´®_&ĨÁïßì¹]’dÒj5ZÜ´šì$éÓIÀ¼^K±ôÏ/ˆ 3„ÖT–»{X/{±IêÙàŸÒ ð×Î.’¥ýë³É¡%ZÀWúÄ5\i^Þ?My’ÐÖEáÀý]9áÿË”5à‡å[FìI›Ç4²lý|0û̬:]üžD@¬&ß{ÆÏ¿1¢ Üâ¹óIÕxØC¶ÙÒ?öAÿ o°½ C{]Á ë4;µ$Ž}¶Þ'èÜúƒ¯¿‡âŸzäÛNI–+Éœ±SaevC×"ºm™‰¦çþ‹I{0¹C˜ÀáœÒ(D­J³aÞ”B¸™,B¤hïfòeã]0š=•vkTÀ¦¿ï¨¨bšs/bTÄAh·§Âú+ }R’ľÄÉIÕpjÎqf]c)T¥°+ZÅq—í(#¸(›nÝ4‚WN³—åU QË‹oÓ;Ù%¤¸O˜|ŠCÿ:$ânõþìMD›f‘ïç\È\ÿ$üv@—Ú´+Ò±Ï`Á«Pꇋ Cü1Ùz¸N'C÷ÐŒ‰ôE ʸŠ#¾)ößU¼‚ݱ(”ç1‚ vý¦ O‡ØQ»­Ç$çcqÎTµ–ãe3ÓwÉ¡];e~pÙñ¬˜ÛÜJ®hèRÍ,‹]‡®¦ðíä,( ‡QЫ˜[¬ýô?N†çµïaò!M*}´ `é#ös´ô8~àÞZÒ‚[žeÞr]àõ¹ÃÄséN¼[n7ï=Æó/gâº9 èømMzï2 ?ÝÌ>=‡RV"¬juÓ* ÀÅŸ€…0¯?A/]ŒWÂW[âáÚ`'óåö È7xƒ³óJ›D`WÏÒZÙ õŠýLÇúˆ û¯ÏbP"Ff^<‘¥R¬“ùwœ~Z˜lë^tˆBgIä-êûâ/lÑÉ… ý^ÔDR‡>¥{ m‡%]­þ’{:Ì‚UwV£úˆÉ²áä3ˆ³.ŽÂôOS8ÜfÏ2ê^ª49{ˆ¿ˆ®ßTi¿QÞîË×ÓIÿ¨4“ûÍœ„Oƒòå7ñj²Ý9GœL{ćG½ÑE"þ$ ²üüßðÒÉsð##Çœ2¾£lˆ§IPÝ?;`³V QŸ-…ÃÒ„.z Ü곸_Òë IèW-X­~¹Å¡üè ûgÎð–RQF»ûø± Ò„8'_´±*{÷2…׷šÀ²žF$ ñÉXs“GØ%V}S 6ºÏ§ç/T²¿›“°‘b\mäÈÞ^ÒÁ&-w Ù<‹ÀRÊ”4¦ä7ŸšíƒìYUª`qèa~LÉÂÕ~`R[Ì(lÇ‚U5äe}#†zdïöüÅ7kràU¥’:úìþ6gæÕV Öì!Øül„:GK0Ý´€HäÙ㳯 èþ·lNÂØ¢Zx(ÔÊh_¿òŒÎ݈źqÛË8v`’5ùóÔÈ>ê‹0,Ϧ6øíUûáÄ…õèæ9Æ8Ô_‡NBe3È=Zš&G3+ÇÁ81‘c,¾“õÜaË™Vvˆè޹2]ÔÔ ;¾?aÔªš˜ØÊê;å:j?LA¦?”¬1 žªü˜u[—ùà÷¢ôSÅOøüê,;Óôž ³xT”¯¢ŒÉ[nãÙ[ø#ŠëªDä>NFÁ4ßSm èà0Eöd¦‹Ú‹S+ySéKkò ™eá¨ô›•¹wŠù³ÃãßI’¨Óš4ìD”͆ÒÏ÷ 8à–ÿ| åòM>²G|fÒåm”iÛûï9*8–ÛÂÙÝê-äÚU)PjUšðÿ o÷ ÅxˆÕ¢ëéÈF-ý‹KoÁÞ½áx­1˜y´°ÑáIP;swóCPñË„¸(=¼Äå'ºèï)K‡Ú‘Ýqt+Þx£Wñ$Ú/)FyÁåíOcŽÑUöý‘wÜ•ïé ‘Rغð:;¢rŠ=¹JžFkÄá¤Í\‡­³és9$Nn¬³þÃN“"f/ Ùk}.d÷†ÅèåтӒãN'a¼ZŽÎv$œÌ#C-–wŠ-,mé©ä“ÔtÑGf¯•qr§s׬†&úžéU`ÏñÃëäð[Êc;HI’ÁþG[Á{y?œ–›JãÚ_qÞDÛQ¾o”]?S¶q ©MH%­3?“roìi‡QM=fC{ ü¸ îi+JÆŒ#Èý:S²°]‹ºÕèóΕœæºŸ(«Â>Û÷gð ‚þ“O(·ôã?_žtÛ@‹yôûAQ2䂎tWág¼ð2 æ1Ô³; Rç©»¿å¨ùxìÆ›n¢ÐõYÚB›õú=ðK¿¼n¿ÉÉéס.:OÙ¡ÙB÷Іï±÷€ý`‘Î.à{̆î’ÅØ±XtüÇïùn&æ1CxèïŠ þß3ÜÙÕdÉ\t=ƒ Qô’ž¹jìFŒûgá×µúD~[¹µa)_𛼈x »È¼fjž¿›ªäÌ Q~LYô\`† Hû¹5I¬¦:`ÃÔ‹ì§Z¿Éƒ¾‹9ÁœM\‡o‘²m%te@9™êà¥æ Apc%M¼FéuAÂcŇlɱÕiÄgÙYœÅ0«ŸBÞÆR[®LŸ¸ÔsŒïžÖÛâè‰åÙ¸wÊ Z¤@öqåÉ›Ë:À³£† û:•\rZL µôIhQ³Ñ8‚ÂʈѪ›4Ñ8gxLäÿÝn¿!’_d·Ã_ù"|ëÎOº’"èå8^ê_qÚF iM›|r±$}O©çÀVªø@”„@ôK^œÂô3;i:øž¨åj¾±FF]ž Btº•Â*=çà^åý„h¦Ãq‰nèÑWæ¶n{Ætu3’ÓÙ;û{ðŠr³ë‘ ¤™Áêýôš !¸$?B¦P×ŇAMkJù…c/ûµcÑ\Ï¿3™¹—¸lß÷8Ù%Ý’Äð‘ïuP[(@ =ŒÕ?4±í3h¬;Œ.cűLð£±/Ÿf¥ÀVãy°æ…ÞDü;¾á&SoµšéÛß ïnŽÃµÜޏidîaÝêBœv¶Ñ×çW°Â]×wŠ ð™Ÿ»útù}qb{RÔaç $;$)¢&Bt4&œx¸qh†õMöKïoÌásÁ[¦œ×®~P”7>îoa¢‚®À·ÖYð&kðÿÒ%ö!÷`Óþ0Oÿ1DÕä0­fZÈ 2ÔÔΤZFP.O6¹¶' _½Ó!zŒë¤ÇlË`©\¨DWDIbzv$·j‹ýQpäº$Èe)#zv°ƒÁ$vîËi5¸8Ÿ—]xÏŠ¹$Eçîfë9W&ìïÓ7êp¢g »ñÈ}Žïx>ÒZ_ܰK'|„–Çûˆû÷»pèHÿ¿Ž\RùŽÎíQ.Ô”NìÊ/દÑÏ3¦M£‹`«Ó*­1À®÷2ã¾Y$†w›QÙ2Ì=?CžG»&ˆQ!àñì5û¦Mšî¯€šê4,¼/N“NÎ!ÁN +ÇO6Kd·t-š—ÅÏø[Âs pÞ§?L~y!¶¬ b t­híw¼-3ŸDt¾„ð’Å0™á½älNÿæm(™å€OŽ+’gM³ñJžìx¡ Ö%Âdä×V(\t"þ½Å‘E¾ìlëB²GÖ•üJ·kUÙNnÞ‡@ÐSNο¾ÁŽ,¾€–kƒ™€øõDìà-Øçÿ_¾p‚·]RlŒ¡<¸ åÒfRµz}ø,@m>Ï!ŽÅJtóï0æòËmðit*=—7‰Ê~­„êøRÓW·YÓGêë¸Ë‚/‘=!å?ܼ‚H.\¬#sû,HâÌÈ¨Ž§2SKñWÇ1v±uøp\.‰“ÅÕK¸zu$¦§]‹SÐe÷VÜýޑȘ=`¦­ÝN¾§£è¨nËÝ¥z¥Œ]‰,Ô¾ž mû÷+¦0áÿM;W@™Œ$]þ2‚¦ðpàÒÀ5,¬¢¦ëcé×o8ßíd OŠ#=tœÜ\'N´Û` ƒÞõ”Ó²}Æ´jÚT²âZ˜´O¢®‡ì1æE:‘µ9FX¨W‹Y²§eq«¾Kï]œGWÔñÐ|÷kxaVË¿j¼/Ü E?býüÇtL?8¯Ò"czctëgø½u¿O%"e½„+bÊ- “UJ4«U‰/÷Î.€Z)c¦aD–Ñï +}â.¸ÓvÇ%äX›1ô"p>õ'„Ì[IÊŒæ“Ì©¤v{?³µð'*îÕ„ýGcRANú;9Sb=`t¾u²“àöÙ[°Ž§ŒNUùuŠy1+•Œ'À‡Šmø}M#šˆTý]ðvY:ý¶2Ÿž½¢DKštÈÈŠHXÃÕ$WÉ’¿™±äÇÎ(Â{—ƒÜ¸üg9×µÖ xö(ЋÎbtST: V¿Œ›÷\øÎçp÷3޹ìFrÏÉœ\¸•ÉGáT¼]–V‡Þ~‰"ÿ4¼Ïð\_ú_}s‚SÎ\9“^|ª@ù†1 ?MÅßOoƒ“š¹°ÜÜG×ÒOã9dA¾ ûDéS³#¸é½¹­_ñ¿õòA´s?;f³¹hûS‹9rwù~ôs%yÔ?·Ç5ªnbgpL.ÇðÛ¡¦È² ºp‰yáþ K|˜wŠY\” {ªça‹]LÌ©öóv®­ lëAá-Ùiq¾[ÀθFïÔq\6ÝgÖ)ÿ=¯Ó0 ïÁІÇC.Ǹm¡rð*<õc97(w_Ð[v™½?Ë£N_6ùÐ.¯£P"U‹¾ûçÒijÖ°zÑqv×ÑQ†MÃÍvô¿}®÷:`ëÓX9Îb°”¬rpáÄ®aÈŒÞDܶE—\º¶n"ÿ[Ëv¢Ä™¸RûœMÞ{Œiñ6GsØ™&G̲³ñ¿ÿE¼:¤ÙZoøD@Ur]È$VÜ€á:xåùù“ßaFœÚ{É„Mtîk.Ú*¸ÌokèÜøbˆ?ä‹– ŠØ€ŽÐ\HßHà ۩ũ:d_ ’í9d¾g'ìøÌbîÛT´\u“S×wŽò}…ßðLOÜXa@jïiÀ ph¯ÊgN+6ãý‡"–À›%רsûÉÞ<2׎¼ ß›ø>³Ý·¬Éóá6,¿8‰¾õ5)º.®_<ÿ¥½í0¤ÜóK<6V¨¿Á Ð~öîšMqŸ…¿×A®U‰Îw8/šöÑ7#QÀñ_üÑ"´m{²CžÖAøÈ¯ÇxÊQÖî ;·æÀNîvì]s oî™E~MÞM*”ã0Üâ£Òt}h¨O:œ„iÐ…íè#{`¹9¾-†¡6Ìcõó°¼}oƒ&Òàwó7ƒ-§ÁmA*«{‡30'mìV!×.Ù@…™/-ÌZF^ô¿ÊÝì5n4H ÒþJC2ÙPŒö…]å4WØà6›(¦±ïžíÀAŒ{_ Òöªø/X;°óý¸œ™£ðª<Œ=z£“óÇí4¨>‡æB”¢ø=.šÛÜYˆ:"ˆÖy#zÞþ1RrõÔ°·6È“6•$˜õG‹~Y‡eoá›»’x°ë „äK’ ?Úôk›6áí'#ÆäýРزQ”v5^gN0äNÁa< ,a%’óàÊú)¸Rý0êU½FÝÝͬÎìx?i?¸%á<…u/Š‘ÛN"D¢yôÉŸ$£kW’Ü>FðG#êÞá§/äÿù@!DXÍgS¹GÉ@æ(~=½¼yý¿¼‘ç^ûOÓgcöX?œízÌ&O®ew’² ü’V°>—Œ zÌöÍĹbAUz&Qi8‚¾˜³y:Ò=‰Ðñn#dÂï]« dÌ 4OŒ8´¯§Ý Ô¼á>Â^šèX Å÷­èîև̛!ôÐdL ç,øSÄ$=UµugáÍÒO·å;v-{Ȉ.y‡nõû1ê÷BÖ­­–y÷ šrp1ú.È‚•‡éL“…øo[´ÂŒhžá6ÿ¨ƒS?'“cñ: ÚÎ-7ïY!ª»`YR ÓwóÛ·$|zÁ mÚï^g–`Èê­`лã†jñlÇc¦ªÇΓp˜ˆb•{°á%Vm Ç Õæte6ô|ŽgºŠ?2j“ÆÙœH b]Š ÏÙyå’Ì—]píòÆül2³T)%O÷ ,5Êj~`ÉO×2Pv¶fjõBIY|B“ež>g'ìÄ.$Dÿ6.#wa÷S%ÑÑ ’>ÂR%566Ñc¸^Ç„Lžž‚ÈèßèÂõa½’dèð`(Jˆ¯äªô^DY·h0žÈ¼]RÀmó¹„*¯3ó?­$®ÅöªÏ°¥¨ƒóuJ<[,¯Kw2»æºÂ”!~&B³ rcw¡kÓ&úüºÊDþ?«ZÉt¥óÀÌKP(c:9³\ޤ:áš®Ã0?ÙŸþœ¢ß¼Î²iûpíG 4ÚÉO¯w½Cÿ¿äׇýx}LYÇ™+mñ¸9¸‚Ó—.FbîƒG‹»g§§ÃÂÕÒ8o? &¸ûÊG¨Îöÿ‹wÙ  &¯õ&!¡“ˆ‘Ï%vôM,^8.ý¬-¶F9AëÃÓ\Õôípp|/æ¦vpž÷ Pg/5š¼à=>”!Å‘Ý\C§*Œ/w«Êf”:üY¡y83^sŸÎá¤: ¹Ò둉8yÊß ÂX‰^ ¸ƒwÂÄq[ÍDþç;œÆ6]cën9“or‡ H_€[Du îN¸~ÛŸµÛžÆn¿tÉÁ±g;¶æ¸ÝlMš(AªDõ“Ñ·%ÕáoÍ3˜$ÀFœ©bÖ–AÇÏ¢0Rð~[ÊP­·çqðZ 'ÔçóDýG˜M×iÍS&Ãw0﬘‡7Ÿ±÷.‡C¯?º4qÑsÛ)fŽï<ÖØÊ¿áúëKôO-ÃcSŸÁ€ÑÞä¥êqœöý0áVO‚KoúqEDó³}1ð9é’s IlÏ÷¨êÇèO·ÑÓʃ˜ñ PcŸ ÌþcŸíh¿Þyô± Q³zÀä¤9ý·á•åÀìJ[I,°Q')·D¡ÅØš#.E4CÓ9¤PÞ\E£w?pÃå§X¾ì2ÎÜ.NÏÏ4Å«QЏcšò# Ž_džßçAGsܼ–ŸÜoQ§_3ô'ü_íáR®®õ l‹@Ÿ+ñ°µ,…³àì3Œ>ɈnnEÅkÁðü¨µƒOÔFN~Ïë© Ã^NïÇ©È<Ö‚ãí£Ì§ÁT/T¦Ç­3qÚ\#˜uE V]V¡›­ã£fß·|6Ë“!ÁNŒÍ¨áÜüý‘{nëLÚ~¦–Õn”ÀéÁ±LIß Üœès3a÷Ôåܱö™¨pÓˆd`9"® 5ò 0%¢ ßÃzÍÀIº†¯ï„³Ýž’ä¿zìÏ“gÀÙ§õì½û©«è(¦f˜áɾ°.bIó¡¥ÂÐÍÇ&2p_3ˆœÏ8:á´‡Ü=÷‡ N2S°ô)fr~ÁÊç˜$÷ƒŒE›«6dŽŠ(õØ´ýdƒ ¹Ìxý.ÎzÓ`œ¹zµSîeé)f{Æuö°‡¾n5Âì„a&*eü\Y \—©è^’ÿî©ÞKðÌæüym£{°—®¨cïíR…±ŸbÌL!#â÷ãÞøÒlÎ9¨)l!rSJ9KÏ—bŒºäÿÔ¥Ã2ð'ï"÷&ïȸ™†;âíé[@üïÞ‹6Šã‰Ì¿€tX¡zž8},HáçÖÅc±Ï÷a?%ŠÐ²æfcÙÿæ?О=< 3ÀaÖ-d„xèëXè¸þΟûÁ¾âod¾_\E/”¸±ç,È÷Y ÿè/d~Z…ïËKQ(¹¯ñ."ÏmAàº;pCÛáøz1*'mCÊÊêоj:Þá¸xÞ`¢|dÿ݃ùÉCÉø¶þ&Få Ô€ á½ñ„yߦú_Í3»ò“÷Óû]çRÙwç6~*ÿÈ伞†;É‚2ÿqÜ®ßËqÐiÿo?nn“A7¹yÔêÁ,0h‡‘#1¬wR<”‰ŠÓøqGöD¹bðqázÒ?$Ìäe ’_Nð¿ ìi\Ûö£ *ÈKg%¿m3q°¥†õìв^6 X³ÊÒ¦B’m+¬5½5ëxYk`± Ö _éS+›Êf'Ï—Xk. m—±­@­"ø™àÊhPø3Õ¬à¡õ²p¸é1÷÷.FÁÇ„H œ}"äA–Î L3^ c§À†QZ' I›:ßNíÂŽ“û‰xP N±žn—ö£«¯>½'_ öÚ'pë<X”´€Ìƒ`n‘µ\þ-NÏNÀ¥ÅÇ¡ûÊq®êm^Ò`íכб6A—Síê ÏN‡Oào«~?«6ÿ¹&nímu´8œÁFíÒfŽŽ `Õ¤Ð[Ãà±ÇG I×õë$È6Ž4MõÂ;×K°Xæ»Ø(“Ñ+lh>³Ï‰ܶœŸ%âœU’¶Ù Þ'⮯ó¨çr1â›ç w=Ìvcvè&:Õ^Hr¦zö@NÈ'ØXÛ a¦¶Œl½%\5°§k”ôñºáEè6Û ó:èÅ„Ö}xögŸû§bþ¼"-ƒž©Þx¹åwáÒË`ìx¦9G ï{žíµ§1éc×û‘¹sú€Ï$7¢G˜£ëa´_mÿ÷³¥œAQkLºü]'bÔÐe ôègc›„‰ˆiÁ_ÓiÂ:kÔìå##–­p÷ß{H.s¡f¾>T'kìm˜LjN:»_ˆd[g‚Oào\ºÚˆ{Î#Â@ØÉƒ$bÒ}&Ëj'Í/^~*ÛñÌv>èê0aw°LÈë`îÛ ‚x~Õ[ÆÉb{/OrXxӶͧ|oÖà“ jË áoíˆb•蜃ÙÇ„èÉ i›Q‘JÅõx²{_¯‘/°×éýW¾)€ë´ }ˆ©Õc9BKÔ诇/š~¬Â°Úsø(­µBÌíœà¿ã#uLzÐqì¹p€õhˆÃ ©ç‘wíNP‘&ç ϰ¼’ °åŽ ø'ýf>wcÔú±î¾a$YƒSpÀ±%ˆÆÅðfñM¦¹0/ÝÐ'•RÈÀYùö¿ñϨŸ}ÌæºP}c2÷YmZê…ãDZjXüî1’á%äÃõ6üÀ #]Çæ’Ømgáþ¾¥Øðy.;`’L–½ÙEru‹h`£%ù3à¨ΖÃN  ¡/J\Bw°Û¦Á¤I% >çö'cCk,ž‰Ûƺk§Ñå^Gý«N6”Cm£5®*ó¹O´h“„0ù˳u’¨¾ŒŸpj¦/6ô¨ù¿_vý¯î¹ª5 ÓÖ@Ñ%ê2sµ “£a¹é¤”M†¶„GÛ ¾ð:xªLAiý­´âŒ9Uz±ïÿH£ƒƒ³éŒŒÿ­Úpö­OeŒîëÐû‹ƒðÜ+%zë‹ä9ÉŸ-ãp a «t ª†‚tƒÖ'ÿgRß’˜wÔŸ^V„YbdãbÕ,c#)`Qú•mvâ÷ "Òa@+Ž ³™–ð8¡fs=P@R•D7wC¥aìε®õ=(N2GÝíwqa¸"5¹¥FÕmÄœ¢@P?-ϨY?Ãÿ®ë²MÚfoÀ-7Aü—0é…Ñ$ºB…lÖå§Ûíµ“1œJéÁmŸCWÎ¥kuIz¿yzåÈ_äŠçGÃŽ–Sl‹èŸÿõn˜Â¾Œù‚¯8ôoÈcö­•<) ¡Û §~»6¸.§O ”"Éí=%´NßöÕáÉ,wr”÷<³Š…5·XÑŒb¢ ù «‘³UdéÛGÌpø7\øP•.ñsm£ÌHÚãQ8Ëû*¹ö°e½2<*3Faæ9¦’ çTÈ€Øy¶¥R—¨ljÞn…Ù—…¨ÐQ˜-9 rrبïY¦-Ê<˜ÁK~–ÂqÄcÃQ c&M⟠¢G­HIÒtzëóïžì×uêÔkø~´HDoÇB<æËy9Çî…ßÄç_Làß{ü=&´ °|Ê_QkÖi”Â=pDE”Ê˱ñu“©†ßZÐ×pí5®¬¨c)Ó“jFRº¾cûêJœ|\—¬±¹ëŸWaá§ã¤ãËF"Rw‰³´JŒ>±¡á »É¼õ—àÅ*/1…džr§bÂsYãÚkÌ7%:ÊW„ÃK깚†$Ôÿ<Gš€ÕIì% n_!?~ð4dñ÷ƒ…$m$•²?õÿ«ƒ¦S-?0òš?î$‚_±/‘ol…ÞoÚtÇÓ}à!G/Dz}ñ‹@úZ#nç„’]Gâ½BEÆÒðäeá%Ëÿù¿·?ŒŽ½ÇYoß3_ðîË:쮟Dn”ÄÀiïDP™}•ó~ãeHé÷€ Þ1„fú¡érŸD“ÿZÀ³å4ÕùÈZ¬a—¿»»GZ]¼‡™—9ÿÕ÷|?É~– ¯V’»x·%xÃøÀRUT=¸kD×ÑÏׯQOÈ™FG_fõÖ¼c,,lèú¤’}À ´w Qƒ3sQuËVòå—ÚŸ …5Q ¾|© *zõðmWö…8QÅÁôôv†„x/Å1“Dâ–dI¯·Ðogé©éádj¢ Ã*ÎÃâ=ñ?¸Ë=9¸ŸkÞx™™ G“%HŠœ+yjºÙwkÏ'àìå¦Dûˆ-h:T5oqN„‘û“ ê§z,‡­s1uúYêðFšÔø)¾… ¬ûg%âa(LÔËžrœ·éÙË×Ð?Õ’ø§ÖÀ‰!h<‡¡Íõx…ÃG:ó­è†±4eOZnjm~=xă¿ÍEèdH6×$Ïv}dx³…¨ÏÀ>&¯Öæµ\mNâû kF¤P*t%¶)¹“ð3±w«-Þ!R$D^’ ð’}ÿò±Ô/%\ÿˆ±µ Z…rôv0ÿDüwtРMæM¸iï NÎx^íƒú‹-ÀüýÒVaW¼HÙŠF—˜•yßà½ÚSœEãÝ&¡Îᯜ*k_r3$¶9Àâ™Fä“™pÉGÕg¦4Wê+Í0£3Áü1¡çtöCIÙ2’.LàÍ'ì¹±ƒ»á‘í¶ÄÐÃãÍ2rµ¬•o7ਆUL'ÎÏ3 ìâ ®¸žÔо„œÒlb×I*áú°Õ‘]¯Æ)O¤'ì_êüˆ£o,ˆ_W>Á+ýañ¶GlÛº] ½uZ…cÝò8 ñ¯då²ÁÕ˜·NâØø|!SÃÝW,XñŸ‹¹Oœä¡ýÂ$pn*]¯íDvwú£WåcHÙ7€ «MÑÄì!¾¸ ôÞÎQVZÊîV¤2¯æãÑÒÕúmí-…-5˜¦[¯¨sóÀd)‰!UYE·@u[>ŽŸà¢FUv»&+î åéÕtYË%<îeoæv5fÔÈâ²§˜¬Ç,2ïÅèÏ ¸uM ‰­àÌ5_…çòOr}ß)ƒÒìœËm·&ìk‹¹uP¥~ýe1 -žäŒ$‚ÃY9EºâL µöI&yû>âåôæÍw:äiJ++3ARÞ¢¯I’— Çð³m"¼ý‘Åïø…¡À oÝòA—Ô͉e…Â.2ÖzQË1Æg#ï—XôÉ£¬}éu\8A6V«±ûT°V'gsúfù·7nLåÛ(¶bë Úhƒ]IªÞò5|瑎Ÿñ ë…¶Ï5ɇéEÌ'™§8é8¶¶0ö‹éÔGäaÚlìúÕÕlçs {~Ñ…NNà·¹0ç^?ÅlâpÙè;:@!„}o´Šè[ôÁÂ…2¸!t;–OO œªhB‡¡¬nŒåÊâö©n¸eF”÷<Å‚}ê$g7ë”øU[†™J9CF8iJ|âçÞù@ð/ÎãØgpè’`Q¢Î|Ä3{¯qnö*ƒÎÑ›¯[ 냓áW ÐÏrHØLÝu®CÏ)Aj¸•à«ø{Šlß›7²~ŸrðE` \Ö4Æ-ŽÉl~QËZ@ÜMù‰Šà·fe‘÷p$Ä”Ü6ŸC‡™ÿq Ù¥ûC! ÊŠôÈïªE|þŸwÿ2ök²lV™Mx@$Z½ÉÇájm\·É$Ïo£‡t副±Hs1 w¯GÁ‡¶—\)Û)Œ†‡ínëÃXe”‚÷åà|asUæ=°;ß”|cc#>_¬UßÂówa…1~^‡~âÙBùs8Ç7’¾kAvMš Þ½ Ùm ¶¼ù‰²‰Þž_p0°–zsŠ ,º‰†»®ÃpOTùqhô… ìŒ0'oSz™/î¤bc8Ø0ËèËŠÓ$RX”nU‹ãu²Ä³Ê˜=kÁŒ•-¥ïÎA€¹=mîÿß÷âu Q½6 MkZ`Ä?…Ú,Z &VMÌá»í8ë rÚž¡]ªŸÃ–ò*¬x܇ïn/Åuûžp$ÄtÈ«¾ç0IÍ€5ìµ'J5êTß¼ƒú£E¹ a½‘ ÙiP…ýºÌ9ÁÙŒ¤Ÿ©ËŒÅS ]ºhmQ#³ÿåÀ”›û¨þ9ö‡.æ]jSg†,<`-©møü“Ž7õê‘û|-Ý:¸…9ÀÔAõÜà׃ã«rñ¥ƒ#DŠÙÁñGxû QoOÆ•L$î‹·¢í‰OadÅ‹MÞÔn~9¨Zg4‡[Á Iÿ›ÿ<ó»Ö……¹ 7ا2ÛXOºôåz(OqÆòý®1؈ÅÞkñÑvô¹åE/ÛbâÑ4°ø2Æ|á1ÆûKß æ_g8"Lù.­Áh^[LÊuDÉÇJÔýªŠ~^ mgœq–~,uY ¦«  ¼‡­Æ]Âu(mJÏ«i“ôÞHãÉÞŸ1í“(YiŸÍH™Q<ªMûÚ!ZDªß‚?\‚r 'zû®1©ÞKîúhÑ’êfWÏLÚa²¿_äagùÑÿj¶áRdÖO~Ïå…ú7~¬KsG­‘¤&úÿÌ{³¹û"M¸*ó hJâi|}}){%Î:ÿÈÇÉC~$ëo»³õ¤ŸË•wíU8Ô^ÊͽºŠ´d8W·/cu—n„½¿¡eÏS*ÍÆß?Ù××&Á¡»¢ô¤Á –÷â+žuïÖ'qj]­ñdÍmÀ1f•ÜdN€„ \Õ kÇÏcóëÃ5¼¹ë][#ƒ\æ{´Ÿäˆ8™3|˜7O~cêòqô™²4ì+pco7ÜìRÆôsjlÝ¢8Vhí º§ø.¨HÝâIq£Û1p¼Éüó£“hvÈ[\”šøþNðߎã“pUfÞ:=›ñîÃËósÉ›‡¹4g‹ ž¤NÇÒÈ+R 5«™ª,MÇí±m˜?c:¿¢üvÒÅÏ[ñ]u7±WZDO 0U‹€I!ËÀÁw}©&N×:x(RK ¿ýZÝ8Í2útÚÀ9ñP ¨®ÆéÇ·Þ™pBÑel pQf¿¸³ÿ8Gæ¬HÒ5Í ÛT? ”~ j9§£üÉRz¡*ÝäÇéÓÙY2ŒLþehâ,‚´²t<<Å€®Ô¹ŽËÏ^D>Ñ î·OLú€5¹?à@î|N ëKxÈà¾L4;Nùs§ö½w'ü¿Ýo)–WÀO)›ðÞ­=ôƒF 래jm8c@ˆ6{ê3;mo㬀xÈt9;—œB{—$ܱ÷ë^ù†á; "ÃÌâZ}æî8k‘­MƒO›€”÷|«-…_›¾ÃhávÑ])P¦1p¾cIx¯H6ÿ’¤ý3-°œ)ý?– µÖ?–þd‚8Ãhâè~â®|¤]k¯û[ã}G]±n0¾ï,ô ôÂØWO¢Éw—}ïd2?³}óÈ8qb/DrnÅ `,ò°ÄãKmÈÚ„~¦nVh|Jãüaö=Ï%&Æÿù×õ³Qò¾x9x ÊUÑ- \7‰¬*ÇkVr¦ÜÇ>´+o14g6Q±ÚÆÞ™òR\ó©ÌÉ×X.µ¸ þ¬â™Îâwóèx¢ã!Lo\™Åœk¨`³–.¤"ç¾3ë­¦]ø‚͸eJþÛ>ÊrØžÝ#Œqm2Së# m›Ž ë9vgv ëo& î,\=¼ŸF–¨QÍîÔ<8ý5 fé^ÄŠßÏaTÿ/$¬3 ¼™4ô¸-i¸›©"}z_“¦ËÿÅédÛ“õd‡T;”¼ ³js‰ûí:TÓ䡟êŸLø¿¯˜ ­°÷%©y¶ ó “zªžæÆ-÷gMê5É|P‡û]^ô’¿9ݲUZÒ{ÚtL>Mù¯ZÎÒÉÙshHí Â<¸ˆçfì%ùD—ÿU¦GËRؿǼ0öˆ8YúFž®Õ$9ý¡xñJê:õ™œ±o#áÚ¾(zãb7ýZì Ú?ï‘%q¢ÿÕA“•™þ„ó2^6ÀÝ ÕÜ÷<¼Ä;Êÿ ‹U2èø7²åk5 ìÙÅ*GÏ"™mÁLw/ýù0€)²("Tƒiìç!˜þ({Nîv¸ðLŒ¨jEÒ—‘±l‘ÎÿÆ¿CK¿Á›%Ÿ°{¡<¯ŸDcß3ƒ×g y#øhò’ŸÊÀÁþŸ|òŒy3W€>œ"Or&ëQS©•¬ú"~zùÆf¢¤¶¥Ô]ðDLG³b3,ΰ€Ë¾¼Ä×®n,×'–°æ| ;Ùü"¼yoG®ê—suÞb÷ž<<•|ƒQú9|‡ŽcÕ.†ÍÉ^»“œ× BÔ|Îr²Ÿï .×2…ëßJñCîEPúqàw&jò±ûŒÍÔ_ŽD“4^­á6—Vs|¿E©g¡8åás´_¡DZåÎà‹@"¼¸NyY>ößÒà7«}b4çÁC•ïlÿ¼q„&åi³®Ÿ§Òe—ΑÑBr 6ë<ˆ–¢.­ý6aÿ—Ätî‰ÇÒðÊ´Š9š¾ˆN"þÏ3á‘ÂBøûgö©Ñ¨²ìÎ<°iåRÜ 7þÅè(PóýRk_‚¡¼*Œ dkޱòD}CÚ¿ð²±(²•ïžœ¸–S(Æ/‚ñ·šábLLIࣸú<žkÞ ûìæ‚v|Î ìßöF윛ø'à!ý`íÎ]ƒ—G¦’Íu# ()J¶öà.¿Ltj˜½®bÄñC:î Û6eCÊ·eô^~©àýc§P|Ž ¬éÜÌ|ªcèäù\ø{fl?eL]o¢×›õÿWÿA†8Ï÷ì„õOdh‹¤ ñZ¢E…L§Â× Z¨åÞ›è²q½ê×*@-Ý—ããû2ôúÔyäжNæ¸Ùä(‡™76Ò|K52Wþ~²] ú^…À/4Ü]€èôC¾ýÒdìä08+‰ÃOqÜ•û3;åZjÇ%«»a¸}Ú~‹þ”à”úWФö¿<ß§BÝÜ*PÝw˜Y}¼ÿë#=ëñ(cr8¢§4³Ò‡«`é‰cì¯Ô?¬Ìxüè¶zâÉÎ[0‹óÔ6ðQMÛ*ðóA!ªh—ƒ[äpŠLm~=áÿ+þGÁϲƒ}¬z×ìAfÊLÄfiúÅA:Îe£k¯£ž%1® •Ô4¥}÷aéü7lþâÍb·hê]ð•ûÕ%Ý;eˆÅèjâuó6óqÓz`q(oô¤QjÑù] cÒr—98CÅ»WbWW£yŠ¿ïý Ûø°Ám2 Ó l{8åaVŒWPqÛ…Œ~¢œ„m…xéãeºúz(†M›Æ§å1C¢¤=ðéS”¸,ª¸ "CÈøXZ«.»ÔHzöo+Y;ë+z?HÃêL ’¦°þmËœ°_B¢”}·¸Ÿy^yˆ¸-ªdK_»ža_މ³êl4ÔʉpeêZÿþé×°‘&UX•K]D¥Èèó8vã>qúü~4ùžÅ­U–ÔõÎ#²OrãßÉÑò‹æh–MH§ Uí…àu‰9µZ͵¹q“m-•¡gü.ÃÛÉ¿ð¿éùÂSáç~4\.O. o$*s~Ã-CœZìL”°ÝW*ÙµŠxb÷Z˜‘ ½ŽQdô&ŸA`¹ìŒ’¥dyñTjX4—Œ.ú†›Ôm?~rá4½–#WV¹á/ÈÅŸÇH…¨ÿ­ô9²Z¡Ÿ8z )`Õ²~\ŸE‹<ž³VâïÐù_.žRžeÚ¨t³?y"稳M”K')nõXò>x”¢¿·Rè| CUt”ɱ`÷^ÖO^„eeZÿÕ<#óB‘M;5‚1bÙÝËâØ¨ý›Aeýcô>"Ho{Éâýå4A|ÙYÊ…oé>Döè?.QNR^ñœ*¶†ï×g…ΟnáÕWni5Eð8ªÙhIÞ’µdøo1qfOð?ÿžpVûe3;ÌÛŒEŸù!¢X—rêÓa?"àùŠ =ìÏžWã¡wuØÿÎüíIl­¿r.{ÙÃÏn;"»k |-ã¬KåÖûÇøú䳑Ç3ðÌÍøˆÓ W›· u»U‘—FUŸº,¸Ç- ˜FÏÊO¢¥UIØs_›”£)ž™Am¹Ó>êå1“­ÚVßr2€€Û²Ožx‘19QÈþzØç§—£½ÙrGó[pj Õ+@s‰‹tvð\›ø…jB‰JX>Hp&6€Oc锲hLO¶¡Ó’­H_z0{"ÿý‰?Ëè»hÚ°ìx/K6uí‡êéœ{•¨l_,®îÿŽ…oÂa½V1Ó³ï=;/»|¬@ù€~Ї|ë˘ÙxÌc9˜Ý<‹w«©L½&ŒÍ9Míêèxj?ùg;›Ú¼–d(·±ár‰LÀØ9‚´“mf¾µ‚ü):罊òÛÐܽÂôDfY®¡ú&p·-Žã<8™¼ç=ÕRP¸ŒÕÆàÞ»\°±‘¥Wç®Æå/7ÂÁOTÕÿ û°cÝ·ö²N/ŸAzÅz"$užÛõ—þô! Íÿ[ì-(rû_ÿ—I÷EÑNâ ×Jã&óP· ¸·’õËáhëâæÍ×BÉB²m}ŒªÉÃ$qē։ƒeUì ¤«®œH:#÷s sm –•4“\®Bvêé°§uæP“ù2Ä,ªžy9Ë;OüC§•2h~ÿ'6߉Ã7«™±Þ î •˜ÕVŽ“9߃àyÊTôÁ¦Ëæ´†Œpk:2‰¬Ë¾‹•íµÌò)ºhl8®×n9‘ f¹pÞ¬wÖ•‚ÔÖ•tn»2mZC[G^"Õ 9ç¡âgtù ¬²,Cö>;ñü'?"æÏèÁPÅ v?>zµ¢>Š‘3ï‚ÈQó °íÄVRy¬w¥2Sµré‘“øåj0³¬¾-½œ ÿ·éظ?î^|ÌØÏ®À kõqíØcŽcŽç8O„ù ¿0-f4u}ŸÍݸ<-\‚àvð\öÔö[/Á'¸›~t_Fë«ß@cÍ%ÐZ¨LÞÍ …¹3#ÁqH|“¡ö‰Òkä-Ob.™Êó¥ ÄÉâçfJûß°“j±—Þ/ŠøíË5ÖÑ/]ࡼßG˜¤‹GÐfs<į×%!î¤!³šcÜù¿ýOÏ×*±_}1¤á+ÞXt Ó7¨®8])ÊCæí“bºãD°ÑÔ|ÖÈgÔ¶Ÿ‚-+ɇuÇC†ÔÇa{ùè<¯Xýs=®Ó½wñ×™Jˆ¿2ÎH=²®}Œ‹êrFýW[ÍÞ©@)ëÈo;‰ò‡¥Âà†x¢·ŸÜÆ­VÀ“Lܽ«xÝE,^€^Ó.|öÍ¥JI ÁÔÅgð\Ä–Õõåpü³=3rp¶Ä mz5à·å!븄<¯á²!ï“ðd„<~Z'¯yÓ ßå†ådòy&ô_W³ T?oAƒ£Éœÿ«zË¡¦b«‚Ñ—ÃpaÙ>tcDHQý¯Nï½™ËÜ>òA|%&<½k>.¥çgÞ€­^ÍØPŰ’¯51¸8’šÏ„+=Â\Õ*ÌÌiÀk"<Àf-C“óáÑvYö±v) À[Ý$«-ù•ú®:[³ï+/² ²Ù…GG™â~A¸2¬€Â»ZFµÄÈï˵0éƒ>çœÝB¶,¡­ý^c„å®x?‘ÿ§usQÈ%ºÅÀ#×®{‰Ý¿ŸfWwÁPáV¸uï¶ü¼Æ&ñm…Ò‘¥Ø "Ž/~bd2iâ.™|dT?*à®ÆôQ• ½ÈÿvÍe¨Öl{zº#z⹃̂›ËiÒò.®¯åYŽ+ #K×y’ W¸w›ÐT¡#fÜ`§ìB™MÇÈ*Yrã]!NkÔ%¢Ò<00W‘p—Ú‚o§5š¯ÁýoܶÛo0¦‹©üüïØeœwª’ RM”Ì¥O™Éjú,ÏÚÏ}Œ™û9·ÚûF˜jÞD±ë?áªQÃþ?éGÖQ”E÷[‡q½ùÛë®#¯±Óæ \Kác±fÖ|ª<1¶Ô#J ß!âX)²ŸQJpó¶c?êÑóØh>A\﹫§®á:]êpé Þ¿/Á¬”QƒÍ9npfé1–I:ƶ €‰FÞÜx“ù$˜ÑÉ äý™[,²"¥å _!þ*é¥:XÒ2 †"ÇQëô'ÜᾊÈ^=u:+©o";ÌT&­¬%fôáÑlŒÒµ§5Rt»³]Ý Þ†ÓXoAÒ²ü Æ 4AÐñý´ët;Ž•Žó°¡ë|²8bcŒ‚’]ly¨zKâÌÉ|àòª95E…˜kG3¯Ù;Žõ`}ö4ºk°Æ_•hDí;(G4NPi†–bhÈÓØèS¸Iý(çàÆôû 3m°‚1ÍXãð ¦}µF¿¤}ätÂU¬ºb€{*tHón®müAvÌNX¬ÎK„*3(¯hF ©P›*3Rc5N,hļÍQlû¢˜ü¡ƒšÌ™¥›±É¼wÍfˆêZ}}\W=eâXÊѳè¨%Œû÷‘âáº]D;@‹âÙÉã8¾ÕC{"ÿoù>ÂiÚè –M"÷þÁv¯°ãØ:¬tœ³sf=Ìo=Ã.‰½Ì޵Y1sÄçA²×´~RŒK6ÈÒË‹*ð=ïgf«K¯Éíáˆì=%û±bÏ(äô³sWdqÏ)ÍiÑì¢kñÆ×¥d‰ý²O;1‹‹À|GNÿ‰ËÏï€íÃfáê y£Š±] [É‘‘]ãIÁ•ë¯D›Ø:QQš8+Š-·ÃŒ§‰ÌIÑ#œÁÒ`pêñ#áj§0!*ý•~ªïßAuo+àkO^]K†üö¾ïˆ®>ÆŠ,•Ÿˆÿ?™‡˜sÑS±‚×ÛåƒI‰‚4y^ÒyŠ!HlÆ¿ãc‡À¥ŠËyf_×ÅD‰M:?‘úû|²+QÔy6»TàòÚV~ˆÏ­(ó‚å¿MNávMI4\ÛѷЯf7p篖lœó9YJ¸învß³fÖ/ŒÇ kÈ(ˆÉ±‰bx'}*Ys5IôAˆ(ç¸þjŇ˜ ûõv§³ËM˜ÔØ>àK°Ãÿ`d‰i{øˆi°ùɼà»ÅF1~¸Óí.ì®:­*°VÞ›J¶§²2ðCD$­Hµ× *?C™L¶™ÅÌh…«íÝì¡°F”\÷‚}B6BÛ,iºIP½ò– ¾Lß*´Á@Äov=ÈX^ìÇ—ßö¶~6,ùò›Ù6{Ë”ñ²ynƒÕõ˜¥TÆî1·eÃÃé‘P„þ`X´R_¬ã¸ét¾‘©-ä0Û–ÑÎ.iöŒLš7 ÐUé¥Ô¡ïÖŸ{ÎMmW€ÔkC(9cD]y7a¿Û3IüV$Ç䴸µÁCØkÙˆ­‹äèGO â^ØŽ3gUÁáy;ÐÕ^€æ½<ÌÎwE%¥¥4; ˜"Û‰S9>Ú'0¯Áé,(w}„ºž£øÓ_ ¹÷DH|ê2ìÿxþŲ‹¥Nâd…`Tû/¼jd _Üg¼;ŠØ²&^ê,ÓÏ]ÔqâRtZk,®üKÜ«­›ç`Ù¦éäÑ*eø;o¡!’àñÁŸª‘#amÁXÃbôÕÄâlôw¢i‰,[¢ôúÿzGß½ÓCWݽï!í@2£²g7›ŸaÔôÖ„ýq溸®Î+S2µÙ—ñL$‡î¼Ïâ÷“EÌ;wš¼Ý LÿhCù³ƒl‡fú½L¥Ñbb¤³,£p«ÏQßë§áÓXšȤ5ð°nŸ÷ጅ6ì*?~|ü`£É[ ·¿èÑ·ÃD%ó#}ÊÒÞ¨‘‚†ñxy1€'~_ùmlmnN%'¸ …^¸ëàMöœÖMƦ;ž±õ|Áòî·Dûf^øXQÁ,,*ÅáÚ ŒÏ‡Å ÓîKg>€ø¿xtèœÔŸ #Ã.(½®Â×Eˆ«énÍ=€Ëÿaî™^Âím'øÏó/Lœé2Ûü-LÓ´¢¿/œ„¼¤¥Ìa}7ê¨,ºª5X̳—xE…]OðÎðZªÞõŽèëQ};#®î ØéÆGÌòØòoÞD=Yƒu”§½çÔp¿CÞ=xÛ†ªñà_EŽÞêvÜ,G¨ Í&w‚ÐÂä蜳 ×èÉy9ÌÖV“Ï9—€¦fc"ëaÏCÂýg“EÂ;˜’YèðØ\óÚ)˜¾ŠŠ|)n ÊöÅ„=‹~ùÛ·›ÇïÕÅú" ¯…îã¥=›`ÕF¸º… lé„°†øÿî;áÿRƒ§ì™Ü§Ì‹…M¿Û¾0Ë•ù££þ}™³ßÒË‘/P¼5ŸkæÚÂ4ü–£ÓuÙ|êÞåOáP¿l)6£Í×”ÈqŸó°cÃæÃŒ1´[!Høó˜ï²údEŠ6Uê–hIù܈I¥ÏÀ\¾˜™a-ÜââÞ n[§>ãÇŒaNŒ<æcš0?[~R&EòåœÐÝv['Ýn‚KÍl¸/àˆÇgháš§™W}Ýß-GE÷øƒPø%XÜd‹‹Ã>p35]É•EîÈaîp˜mA¨*.‰<ÃÊXvB„ 3$v+u‰ZžÓÿ‰M cù$£q¯M1¦9oDû6_X°˜‡ë¥eõU°¥a:4‡.Egï“PQr9>ÞÌc³ tÿ\?x+ ú.S|áÚ™ÞgÃíTÃáú\_ËñUå¼6ã…çH0~°›\¥ÐòÐÞJ CʩԤú($ ÊÐP­pŒã^~\ËéeCMÀz ίl ÁwåÙ`Þë„7EU‹,¼tUžÈíP$OÖ÷±Ü‚÷L¿Û øÐ¬ kKáªÇçÆu¢¸\?Œr»ÌXÏà ÁÕƒ{ÎFD®)žrE©Q€1aª“[¿­›ðÿXäofîŒF°âZ@‘¿æ|Ч÷R-YC­Ö1KƒÞº9×ònbŸ„M”æÏ'g2"^ !G•¡ýÎ x½ò&{qY<%O7–ôcD9ö)Ä‹DèWn!d§À`yîŸ=ôÅù“Òy|™Èþ»=‹hv7ÑÈ «ß,í=Ï(ÿŽÍtdu1¼üx ×T }Ëæ‘DïS úê Í›a¦ñàºêT,T—¾ö´XöŒ‰¸žM']?ùÈqûlœqoݺ‡Œl·%©;å©ù½ZœBß™ËIÒön\·ßL—]œ°_<.é1`dŠÅˆÊWÚP8¶qDð§ï6l9<•ÎZiEÊ.\`KEq'Ø’¦6ÔðM$¼Ì˜M"dγ_ø8dfŸ:-µ*¤N=þ ³p^o«ãL¿±îþÑ‹^ά;›„OL1lé4jœ{›5¶xÍ0Å ¨ÁÝ9¬M÷—;L׌|X.÷eb,àSÅ?®™ÿ{ŒúÃÏå¿u€k+¡Åžk÷ÇàM¤BÁ æë[º6g¼\ß6ýW wVƒÇ3u Ž«Ck¦ŸÆ—¯þá÷¥"ŒžC*ö‰F±#‚§mÞÆÇâT󚶆3aÿ¯ûÖì¶ç°ÝF„ª¼b×ÿÚO]íõhJ³ 8:•Î Š€·s5ˆëyŠö×w`ƒ²^ÎOï§|b{)>ßM›åòqÚÚGÌËs½ ö: Þ+’›ûø)z ï Ò5;¬éÍmbdûdzîÛ}ògz*ž½þã ÓeæìX~5ûµU–êÕÆã“0ñe 9ï¬Èz†Ÿ¦«Þ²e×ÚZ"÷î$™^SÀçÍ |D@z „û ¡‰æ9åÕ'ç/§_oëjóÝä}f $Ý cçûäâo£ù§v’‚€«Zún‚ÿn«J€°NO½kGwðŠâk1~úrë%ø¿~ËvàâùÈjeÔÃy©¨9«¿KÔl5 %G`Úl¹¡ÅOzŸˆÑËó”iC=>ÚD§˜'Iú8¶u¢ß]¶ _ª®¿R 9c^Dpm ³ø»èåñÏF÷q[2 \IY7HyÐÖ¬í¬ŽrñxwœµnÈ£ßkFð[÷ ¯u(?/µúG!@¬†v|‡)™§GÐìÒhªVîÈ*ï\OÊÊÆPd×Qj©HÊj¤È†Üg ‰;iÙÏgpTë;'[ èâl§ ü‹˜uø1 !Pý7–o fÜO4—/[É…S:4éÊ'È^¹•ö¾;N¸³M8'ØÐçFtɦ«pÇóÊz¥’7? h•"î ðn[4\M¨ÀÔ%Oà\Úa½(rýQ7² f¿$ÿï: í$´Ô9‡vöˈÄýëè1šÉZ)K‘¾›p\&š.ßxˆôpXÑÖ½týq\R$M¦lÜÏç”C€ïêZ›C[ íJü¹–MeOK™ÀGðsé\ÂÎñá¸oþ@³^¼Ã Áý4xK8&´e¿¯À nR4ÊËÌ„ý.?÷àöŸ ŒVÞỷd¨´º-HþÎ5»éJ”ÃÑu—9sæ}‚åÙm†gpÙ½¯xä¸óÀ`8J[b®Ìy8Èy¾i—`UûI¯×~ú&霭âEfÓ=Ðà{Á„šîÃDÿ<0iÿŽy]EãÇbYWÞXa™ÄÑ‹ùo‹61Û[5i˜<üRÇ9·i6yÕûßi“ ËA?r)9ZÒÅÌ~§ÁDÙû°’ÇÄ 7ºu¹¯¹ ŒkÁóº9]³»†9Üs5?¾#Å(Fn%¾ éíì:­ï¼ù)C§Åfa†Ñ¶ýtþDü“ä-ìè±wlÔú3ðæÄBòZ\–î­‚ïypÿ²+Áä5oÁÜ1ìGVƒYð¿þËóÆX Ùäã<ÁæK3Úm5†Éë`÷žzî·G`[4…=cªCÖ*¾Fþç<$¤S–4«ãÜtô=¸£yÛ× Üy·r\÷có¥Q¨—IÆw†G`áËÅÔ×~9L¾ +fÞæü¹&OñØKMLò·£3j_ÿµå¥C4›Ó¸ûíÆcB} }â-Gt.ÃЋz <ÃÊ›½†©«ƒiÝŸ x¶î'tÇ‚ä–ÇXcYÂ4íŸÀÿt™Ãh°ã'=éïMª«±Ï†&‘Ü­Rÿõ„ÆS3ÐKäF0èÏ*šNw%ôc˲8Ü4¥mµb˜ÿÞ+t›Câa·Þc8y¤›=‹ÆÄ¯¼‰±q''J*!ïŸ<Ý+ <ݘìöCÖ¯„*eL—-ŠýÒ¡Çûʘ"Þ¦tâÃ’t:RÍPãK?a¥¨1M0—¡É-Û¨¾áX´Ö_Ç-ýBT 8Õø3˜ª8yÚsÐŒÊYôBÒºzo@ƒméùÁLNФ@rû›9êÜþ¬ÛŠä¶5D_úßóŸÞSSÀSBRósßà«`\”‡[·šÐˆõùpSNžv7:Që«ÑpüÏ v~1ä:³Uµüøûï<œ›,ƒS+¾0*õãXvÚîú ¯eA•F*>´Ä®fO6RczÇ~€%› ºV€Y6¿w~¢å%B~o°gž/¹¯Rš øs|º2—Éq C±6D±’ñË,ƒù;ý™gjØØÛùlËR¹à3Óö[¤…3`Fá~ÚíÚª4ýHÄ8gšJ6\?‚ÂbÄä%? ZÖoU(Ë;<f•·²»=Ù¼¡5ôO„ì„ý/ëðIù7¨½5…Ô,mfÔÔkYÍaêôº•Y$Œãs)[iÐÏæztñÑEÔZvS°¶’³ßµȲJ´^ªL¾¨KÓAãlü›“;νs÷ùÂú,#’îñ=êïàñ½ËiÇÕ©´góææÅy+Y;f´Bšn³ "»E7’ eÉŽ†9ÌÉŸ§@¾Ô‰äTíÁ;WlhÍâ¶#d1Z/Ü…3.M£Ž¤žƒ3û¸Ú‰^X1•îIÌaKþdä/)”ÚP€TÒ}Žƒ¼('âÆCy»”È®‘mTo äÇʲ}ãyªlúÄú÷[ÁçœÒüÖíxšÛ‚šò¯ Gî..NÛ›VCUD”ž$;è;ÔöèÄÒÉãüd$¿XÞaµúƒ] éK5ák^†\¶å\×<Å>øcB:¯˜s¶HL&Jn rÿ²Í‰fGýóë¼d’§Ù­ì í‘›hìñé$ðÈxüµ LWrÖžƒ!×/pð[»CS‚>+[GNF-ëÙ̾ÍX¹o À79…ö&³ŽFE¨i¾^šÁ)PÈJªf %3<½/˜ÄäX–´œdNYŽcs²môtÅ)§xå0|~÷„ÿÕx'ÓCóÁûÕNTi«a5W%RÛ‚¡%é!¶ ¢u–]üã-²û[¸.—±f“˜<›þ9rŠ–¦´\?lL&ý¥E|ÈÌÂcXÝ7вùq´õC1Š Äì÷óp‡‰œW Ó×fâ£áJ­EÑó8ýïNrbÎr˜»W™ Ž)’Úg,ô¼ caÙà •ᢿ]õÉßVG¢ ›A…eÅÉVï&\ÜqÝB0 ž—|—ÒG}¿é̇ rE8cnmÁ»}žä‘W7ÚlCTa§Bhb=õc¬![Ö–kÏâ‰ü¿÷O¦%€¼eÅT¨ð3¸?|¢oXƒµË؇¿„È“ŸG`Ö6`wãex«;‚6ófÃÙâXXÇ©j²Ç¾} µr Õ9?—Ù8ú3ŒëŽœ–ÖBè)çìüÓ˜û‘¼`«öƒï졳®z±ü,44ŸA÷Î&ö×ßÂ}‘4|Çó­ßÅ\a}áÕø¸9nÏûǨFžÅUy¼ìjë©0º}2>v§½‡½ØºO¾ô¸g(„VK·¸å¬õö<—pF–\†ÿjºWn)„/RÀ€ËGrÖ8¢Ifž>>ÿÅŸ<¡}çdfŽ@?{ÇpÝyþs´` >Sùÿsáa 7¡üp ˜‚—´óYÚ6ZÀ@u?Ìt"Œ0ôpÇþ‚\Á1Ü’õiœc>aŒL{Ù Psr»ÉŠæ¢‘ŒO ç¶Bjè–öŒBEx9º[˜Ì)½kwdà¾-áÌ”î1vŸT'f¶ ;‘ W_¥¡“Hî|~áa‹ITz9«4óÝ,àʆ¥«’à+ÐøÄ…~wOÆìçï˜%SrÍ`®y‚õ94äÐVPcNãÛO€Ms¤^i‡éUtYPçÿÛÿéûdú]ÃÕGŘeyHá)#J÷ô£Fy3˜ø-ÂËsp%5C‰÷sÉ{ ‚ªú=l¾m7œ;¿Úi{ª3b'¶˜ªÓO±šViP#½.®ÕEkÝ\l?ÿ33¯2¡žîðáÚ´þ-K¶»át"‰?,ÉÑAP]˜1â»qÄóŽw€‘ûoÆîÀªì1 É+Ûq3¯ š<Àv?=D“µƒqÊŽçØõG£RwÄÇyß“{ `[¹iZC®E½$— ”¿ˆþVYJ…HÞr:g>CÞõ÷qœy,$ǵ iTS.S- ÿtbèO1u"1# ÂctÐ]>i"ÿ¥ë‡ü¥°}ýz ëÍâèRÿ™kÁ…§Šè˜¥Ã‘˜L c¨ÝâGÖDñ’_$.ùõbÜPb›Ë°Éð€™,ˆñ­'®W#Àza*¹²Bœ†û•³û'û)­"Œx:ã/Eàß‹:¦©q¹Wô„Œ¿&kŒ$i_il‘,‚þƒàû5†»W‰Êð5áp~:]oƒ™ö©¨–ÀtŒ:¢ÍûVL ^FÓç£_á\£Nª¦;ÑUý)Ôþývl1ëS$Å—ìI‰]t¿•¶þv#_Z=÷åFúûá*²ÿ[)yk½~Â~íÓ­PµDˆš<)D¾îYÔ¯v.QÏßDRö+žê8NR5ì8¸v£Åy²‚wæŠÒ­Nš…e0òíÈ£0QÚ†ÿõy¾p|\/ù S\ ÎJœ•!•§{ dËÔÿûË<;ÎÄ>W"]eJÄqæv·dMžEÒáÚÔÆä8:}¦K?)PÑÊ.¬[´Ýúõ;Δ%ü’‚´ó÷yæÊ±"+[Kßž\ÉÜ;QÂÔ­Kaæ„×cGŽ 1ðMb†ôȇ¿ë6RŠtnbÕÌ‹•fxržÂÛ¢høÌ/M^¿™Ð¿ãš“kÈuÃÌÂT~[~¥‹æaV‡,Ý-NS¯.æüðü Í]6s³”éqiº#<ƒ¹W¥Š‘§’CÎÄÈ-½+ãûh•®sRH¯ =¤& ÖO*Ù?75éça&(Jž^\9•¯ÝAwì6Íѧëg*‘õ1°ÛØ _âàãåW`Uv´¼ÿà£Úϰ::…[´@’I,JÃqÉD¸u?Ór†ºòQZí6´M1äMñðËIkFeéÌí>Põ$ $ØZ® ùf4÷«Õ¢Êã0VGÕ“R±ýàÿê¿'ÿ›Šgí¾³K6ãâ}o˜Î× aýÖÓ„ÿ›> ¶å%»ß ûIàÍXÒ#‰ã\Bp)ͳ½Éš¤Ø4–øã¦+°Ñ;ƒ~8Ã=¥©×á¯ÜÇhì›Äæö ¢IYu{·†·IÐ)åÅp¼~±hÌ!åÙôÕK|!kÎ&ÑŸoùiyv4)Vp"•{ïÀÑ=ÅhíâKNÙÇ.ñàWÆ8©ŠÞOá£oœžãÂË”ìcÀJFÃ݆Dƒ…Œ¯óvò_¿èëWèy?(7ó#BòNˆŠzÇå1_A™³Ô~e#Ó¤o‚ÿÌW²`Ö<ýŠsdèJ3O²Ì«í„È¿k;É™Ãtßï«Ôâ"%Ü,²¶ø«Ï¤Ÿ¬ô˜LÚ2ç;»=‡AïS£!D¸NžXž “M€Èê¿{YÁ¹„5ÿdÈ·…ôë¡1Ö`®áÚK‘‹Y‡™ìÅ€"âÉÝòÝ4ï¹Y!!LÊ#màÎ툹ÕJ¾3;ãÈg8¸T K…JöPiû²K‘ìÛCL'QÕOkhIÉ šW[F~Ø  Ê^žQÝÈ®«ßë?ü%‹Ú2hàsz¥¬¦EÚCÁÐZsæMàÏ0óµ2€=zÙ›l’!r1"d¹÷jR%z½œIŽÎ:imw) K9aÆ…r87Cj„AólE¾Ž%Ée¿Ùôµ}܈Œ¥ Ø['Å–ê97¡pÍÿ‘w…‰¹cSËXŸ)0TO[๻mëõA»pI²»çÜñŠ#ÆùA5)ßXÇ`Ø››ÀYçLbîò’…£ÛQšäöýú‚îJPº$‹k·$wH+H’J›«¬ÂdÔ"bõ¹“è¾oÍðçã4rÖ% 5äxpòÛ.»‚r¹åÿëqzÑäé­àþÒÍ@AÍ 4£]޶hN¥£m»Ñ¨q7áøÜç·!F¸BQ—@g’ÆDü‹ì~S.Ö2–)+pþLÚ'jÌZD©“Wæ¢Tˆb\*öo>œ°€zóN#¯s¤hÉŠÇPó _jè2â§\É8(€Á|IºÀo?QQæ_îã'þ=ìŽÕ%(´‰*¾À…Éë¸ÏbÁŠA6&ðîž%ütñn hظ™Ì|vÞ|/vùC\“2•<ÌÑa†EfÃëIÿS#àÃñljýô<Âåh2ã“-}“#ÕÒ°cLßp†ïI‚_6ÔÎöļYRïèH„í•éû¬rØ·X‡7VÀâ`^âÎùßþO.Ä`óµFø9>·gìËáþ6K¦i§è…k©àÃëðMކ焒ƋrT÷M>Çu ÐÙ¹$ùr,xFdÐÛS=ˆÞ…˜¨¸•¿,Âö+Õ3³d¥™ä²™tñÚõ4Þ˜‡ÞI‰ƒø¯k9¸ùý Ô &z¢—g;î"ö^¨ì%C3<‹[äéÀÃõ´n§é0öŽ3úi%é;ŸŽ#ÉçÁ¿0’®_Mñß•ŒÑm²ëüZ3®ó:TÅðÙOz\YÙvjÑø¯Ú½[„d~ÿ€õ˹Ö®¤ñèc()á%cé†dÆË«P½lÏDþ3úÈœŸt“»,¤œšlœM>p”°´±ŽÅnæx–KÁWé¿Mnt4Çyuíð¿~ÌŒôtæ¡íÒQv¢<”áŽc.Æð®&7ùH½~.=ذ~ëU‚F‘ªµ`ÔMÙ9K÷Æv’ÃL¥õÅfÔ¤[Ÿi¬¬£n.ÄmùnT3·Œu­ßþ‚c„àÇŽ†oÝJ9¿ÿ‚Ë~¹rGØIt‡Ø)fwófš§&ýç/Fïê^äEÑ„=³€liäƒS׈XÅ\úpÛc(k™G¾¶@ç1` V²z×ÇÇÑ æÛ'æÿ©"fª×)ÎúãO ³¤“+±7Õ쓸ÃhÿúKp?:{fòèÉiâ8my ³ª•Ë*oò&k~ª  & æT’¸ô³^OŸyTkÇ:EÃÙ6íœù’-ÜAÎIŽ|éüYzí—%±Ó{Böù|fC/•ö”"ïUÅÁYÏþéÎâ:ûO¦ªªkÉš{"ôˆ2ºÙÊ­ ù…äÁýsg˜ y”}¸Á”9RÛÆ”/,‚ÙBx]?M¾»qååéôµO¡Ó~L”:ΰKgŸòPòSŽÎzîÏx„2[6Nƒ?»J&æÿ¼Ë³ ´5š»ï¬6Yh^˾SyË ä!¶Ó¯‚r?ü £]Ú8ÿõˆ^ö3³Om¥×Ç$éÆÞRÒT¹%×>‚GÃL»xÜ * » –>KV …ß éïá 8o ùQñë«`kf&\5"ãÚ?oÔ×Ä‚¯w.]`󿯗&¯²ÓHÚ3;ªqƒv*J_¹%ì­˜}TÛ0k‡S[Íi´KϦFæAÎ’M =Û˜¾.LGþY¤>m>y5‹5{lÈ3%ìCf#\´_Œñ~ñÔsý¨½™<¼#É]ôÉž¼½ºqÿŽœOc„¢Á¿ôv´SK“ø{4•¤êLau­W™·x>ƒÂ …ƒÌÊèjüÍ@Õ ùµ×…Ëßù¡ÖËšµ&iôÅ/uø*£ßüíh}¾mJ‰À·rŸðÖ~! æM„•3Þ°¯†Ï ³T&&TcÝ,Cú¼wq„Ñ"DQŸX hNŵ `dŽcËwö•Œ›þñ ¼õ´#äkðÏ"oœ¶E€Èø’´%"vô,<ù™ K¸hþƒë[1•}FÐÑ~†­4¢úâÿÐçÓh,i@Þú),ßÐOð0⃩þ<ìÂ^ö)ÆÚñ~” Þ? zypýf=zï=?ѳ ¥Æ2kñhù (rÿÍ.pùî[ÄéÑ[¿q¶VK½ãa÷È8¦,u??võ±®–Ñ0³!–ɸ{ÅRé²[ 8Ý—'mü¯þ¹‘·Ç£«ÌŠ~éYÒ˜{•N|­•@{® ¼ŽQµTïŠâø¸±ya£5(Guh<‹P‰_Ðþâ ³îu3óÕg ².ç6=`aÒš‹xê]2J¦ú0GÕ=H¯šUô㥋_ˆÒð‡¯0E²B4Nøæcðp×ᙓÙWg4i‰Û(6bœÌPõj:^½?„ë ͱÚ¼?ág0Ñœ!GdŸâ‚ÕÕ°èÅöéwh¬·A¥¤#LÎ%~ÚªãLbl1½Y>¾;…ìÿ#@@m}3úòÕ¢Q$é cµ­‡=9ÒÈZÛN¡)ñrJ+¬çh/6 ë/1|š{ÙSb àR«ÛÞ]ÆÉÓ$éÖA¢5تηð/ø>–¥ùK8!ªå¨°u|\G•hèºÔ`™r…ð(ö7°~ýµÿN'ÎÙ™`çþú'ýï÷­½;ñ³† £Ÿ>Äò{—°[?ô‚ž—9»þ†m2!N/»ÍšF¦™AƒÜe Ù3‡‰V`¤{²àxt-•¡õìQÈzA‡ÛçÃÈñÈ.œDœ<Ëá³ôt2æó‘Mz×=Á§ë“:4ðì*môä”ÉËf°¨M£ ‘©UE¬¹†3`Ћ}=§wlköèæÇæmà[B>S2kHL ‡Â±&°ºÎ‹Ó¶ÅÀwr¦@þ«îý“{£C„$þ~É6‹3už“ÉÙs­8ÚlÎôrý t¥5¸< 㙉úOO NCBǾL„líüŠ‹3#Ù[ªwi´†˜c¯a(õ84|ÄÒ–f»¹ ™.SŽ®¦=ÌÜ£ð—CáÍ=%öHÕRX\¨Lº2Îä+Ó0U¤%4ÐÉå4³giø‡ o¿\„œ0/¼õœº9; ޳!‹K@gä*†e2ª—¡è}(¼YDáM ܽ©Mw4Ô[ó0þÑÃF”‘\Yjs‡‹‡Nv³{7²SŒqW?õ1§èkËé^«Š½æ¾6Q|ÙLߢö›:'YaR’61Ù}Œ³7Jšµ\|}ÂþîÕ³0vÿsv…±µëqQël¸2óÛ¢r"ϞƊhü_Ã˲pªný`<2Ë=ÈêKbD²Z™œ¼·€ÝRªKnŽi{ÿ*ØUÜÌ´hЉ×ãx#†Ö©E¸uÓYpº¶˜þØ¥ Êúô2”$gƒÉ!ÊîíF' ü„{ñÆj2")Ž;…ÑùÕbòú5«%¨Jk^m…¨O â#ˆ!žj¨|É‹<=žÁz/…éoÞâè&:È3­›ƒ —³P(>“6„0¥¼¤åž?ÉNª…£?‹a|ì˜fÄOÅn¸Nà¿æT!šò¸m|<È}·ð.÷³bÝ£BÍwØÁ±-×ÐQ& 6¶‹Ð¬Q1²Ý+ÿ RÊÌ#ñ–ÏÑÇc•L°÷†éz¸ ¾š‰ysí™Ú(/PM†«iÄææ Nœït3} 2áVäþ} ¸}ó¼]³_Dâ–{ËAY¬š3þv$\2ó"º´ÅU¢‹è—]‹ðÔ {,4ƒ|å¡M. ©–ÔëfŸÛÚ$¹5†æ=5"Ÿ7vƒä¾lt½¾EòyiåïûÈÎߌ‡G—µ8žtÄÌ•áÌ`ú$dK3w×cìêb-ÆýÿökÜbE-¨ÛIQ°ßöù÷b™y?€<Õ bC?^aÖ'ÁªiíÐáóµë&ÓO¢a ~Œc1…®€7]šD齘Ý9„Û¬ Ü&gÞàRº/(Ƕ)'ÛµAX9“ÖßÅÁ[näÉ`,nδÙä/-{ËÆùÂ×;ÃlÓüaæmF–| $ŸþFCRt&««OÌ…•¡u¹)qß¿ ÌÞÕ€òà‡Ã˜–bÅòžCKæí@ç+è2#‰ÿ\,•€ù\1š¯æCx"´©‘qÆL¦šXÃx+h—=éø7¬¤Á8Ãí­8Í:DÞÿþoÃáG)`—÷’;±^¦üÄ)^Ù‰“Á´KP¨;éž—¹ò-æ¹µ¹m¸O!Æ"–ôø|ƒ•ey,ß¹r0ø¸™W6¿6 âö`{•i NêÑ'›UéüE{ñQx#2R™„–8ã¹™ixÓéüׇšM¼È:4aXäè{ü Ãc{ns¢F’qÿ/%é*C44 û–`;Ž?c.Ãöáó(Æ#Iâ–ÇÁbñéh(y+Te(·rúÿwZ–‡ +Ø-ߨÁÒxúuŠ \7~~üÿ e±Ù´ˆ…±ÆkœÂüiP~ .¾Ä nÓÿ܈>–ºkAzE# ìb¯AÿSm* %"¢L¸n''âY%Éwä#£ÚY‘SÉ좕«èÖáv|´}s×ÛôºîXkQS“it—P&wc¡6òØœ…2íûÜì™î0û©ì·²¤6uÁ$Dþ3çÐF{Ó~È~ÔÃ\ëñj ,܉ݗó›Oî&«Ü®3'ú«˜Õ:SÉ?çÅÛ&LjÞ˜r;sÉ} sR¡êô<îíü*[Ä÷¿ý¯Ê¥ÉU·Wlhp ZKp–¹w÷ên¾Ÿ >-è¤d#:¼'‘©4Ð×qÒXðj\s[Ìöf`‰f ˜oH¿ZÈRÁG–´ÿþ>ø!ôp–+]¾|ÕÖP»¿•Ð!¿“¦ÇëR—Ô*¬ò–§Ã¬$e?g½[Ĩ_q)µž÷ŒM[u›]•ü ^^ÒÇãcŠ&‹ðéC®E¼ýcð×x;Ó|;¬!G¦Îc@Ø6îÛáãuȺˆÏøN0©ïæƒ7Y·˜ËÁºd†,Âuì ÈuîZŸjú¸Ó‘¾g‚]ó)öwÄ’ ü×2ÃÞËÀÉе#]W0ü³£â¨ÓÍÅ`¶‡<Ÿv¢  wG\ØHP'‘MøµjO®ÆÕkáêõQtì©ý8£õÇ9_{M%ãî"Hž­2$Šé.-Ë©‘YÌ|ºñ*ÏLc‡M aþεðü4œVaû^ÇqARƒ¾Ñ¥Ö+Î1N1ŠP5|·)¨²Ús+˜s/‡Ø«ŒØ #ˆæ—Ðrcìê[™œÛ]ê´¤Šàd>keŸhÐ;Õë1Gq >ç•Çå¥Ð߈D†Õ MLÙÜ|tÙÓSÔéý¬ÿ¿ý÷¹ j çÁ6M+*šS@†§®5ËT,IÑ%£ËšžÍ¥m¥ l Aa‹ýН¬é‰Çð¾Ã‚®»´ý|Ù‘¨忆+c‹#ë~â‘ER EtPr™qI~Ú'.1 *ÿáô;Æä‘æl¸4Å–¸ß·´wÛ!?p ¹“l€zE÷PMáÚšL‚C÷Âæd–{yª(¹ÂGæ¤þdk×îï+‹@ShÚµ5@îû(&|Ç~Ô~p˜øÃpë> XFPi&¢pl–¼lEYÛ¯ðUã;ø¬nŸx„û9Ñ¿¯Mä¿mMUØ6¯§á!;^§’]9Sɨ]Îã¥_ó,I¯›?£ŠÇÕ΀ôÒ öçmòüëa¶.UÎÿ¢ê%r+S¨¨ì% ªÊÂÓØð{ 8MJ„¡rœÚ]X¹TŸ}z+Ö&|aˆÝbû‚—RÁ€k°lö„ÍœS¸nr öôŠ‘·3¦ãíð“Ô±êëô‘Nà2‰êyŒ\å Z§”A?ÜŽ¡;ôøÙIç¾3š2àZ«-´©†cÞ§ØMn]œf^Xò´ß¶žgû j¯•!5yQŒÇèRæãÖ½Øìw‚N;û`ÂÿÓ©T¬„¯gëÞu¦lª=щ«;-9ÎJÿY4ÿê lËôgþ뽜‘š‚“?"8Xc¡Gzžmê!FM1~²é0<"¾æ÷`çß]dÖ#KÜ÷[ƒ8:.b“pÁEb+xI­ËA• ZÆß$£,Oâ¡ ñøWU€ŽìíÁäòDÈ!Gð[ä8·ªr$—ÔÕèdõOlù[ö·HK‹ð,iྰÑÑ—à4/Ê0ç"~ ô$êS¶ùg!@f­>‚W„O³3 Üñ¡» Ý·=ä» eº8\~ ½M*¹~3'øŸÛ_ÖaÅAV°k1ìÿ€ÏvÕÂ9ƒ¿Ì½¾´ãº´Äˆ`uÒ[ƒAruÐ9®!šN"ûóÂÅ"&ý›ÈÅaœUö4‰ÐØÄ#°Ãl \û‚sÇfcáIùOe³Zˆ®Y‡ïßDȦgd<ž5 îŸ™µ-”á»÷šáÏT'Oþ=€œ’9XÐZ¿ ”$Ú8êÀÚž!#WAè-‡îš"™7]©×¹MtH"y>ž7«éÜ­œ /Fpfñt,:¡Š[˜¾®nÐ|wÊïÙçóà#;‚jb!$Ó%¿«Þ²™´LŽ ±"Þ¸¸á #½êf\¹g6Ò¹ï/+ƒ]œ¸ž{ºJ–¸¸¼`õ+ÐY¥M ®-YäTÁØ÷VŽLÚnIÜ œHNøvbªÿµçûk.ÿ¯ÿ…Ñ36×›UïgGÁÛüÓýüiTDÝ€Õpº…oGÓoÅÐÜ{ŠIðL:,: '­r‰¯šÕÝ>ÊnÿðŽÎ†1Q_TÏ}Åü,Cö,ÄGLÇ)½ ¼ç±×nLÂe~3ñUêR¢é¬@xdv‘“ë`祵?$F\óÌ™õí©p{]:[ºvÞo¨eë™:у·^ ôd\WÜq`ê‹òÙ‚‡‚hãúÓgT#×åè>Y¿w•Î0{¥“Ø–) °ºÕα~ÝSɌԼ ¥•½— Að û¾dÆDüoû›kâ6Á݈®¡aÂôKè¦Òã4 ¬¤ ~ýe_žƒ§à–_‰äcŽ$£MŸ aoÌS&´þZÌd׺<‡ä[TÜHœÌÈï=Œªa#ÌølCúN¨SCÝyÔAT$×¶@ÝgZ0½óï½@Ç~=j?Ì'qir^ý þ×{îü¬O°å—ÑÏAÅ-›¡¦—–¥¶0Mm;‰¸Ž9©$H„¦+SÕd Ú¬aCMþãeT¿N–D<ëã|ºW ÅwÖÒ¤#fÿ7¾Jz™é~IÙ¨òŒÄ³_°0ùÙ„ýù½áxd˜aþ„ŸÀúÑ0¼p"ñ1 a¶x<ƒb†ù=W—,芡{oÀ©è*ö¶Ïd"ÓèL–î a¥ ÖÒÚ-ÌÍ3EÀ“`_ÛÎGÅcsaePüW¯¬%„æŒçg±™ì§åq¡¾«wL 6ìš ‚ÂíǵM]Tè#€²¢5¥ÿŽÀ¼öéX=ó(‘—ÉDZo ™ð_G¹p†cÓ!{êbšq} ꤎz5e`Ò3­ô/°G{¼è ‹XnT'e¦KýcŠÝo‚_(Y²!…ŽT—Ó,™òì*>:0‰<(KDï¾Ï¿äë Ë¼}(˜I7;y'€²ßàl:C¥Ï²Å·lÉÞ“.´éìÊìœA–<_Bßù±„΋¢–¯ðùw)(8“ K^«SáÀ²S}¬’ dí‹thwX$wÛÚ›dUê½,ZN¿W$…Ž®4^؛߹AIÍýLONÝ„Gl,˜ô~GzCž?’8_®„L5Ab4:ÎûæÖs-&÷°;÷ $¬Ã{–ÓŽäÕ;Ò½2,l˜Àÿ³Ÿ|¸äá.ŒÖnd¿D‚œ‚4îÊfýu ¦¬1Ý az ®Á7w!ª/:<¿-Â+^²AÒ“àЭ^&ò¬*†§~Á<ëR¦q ?Û™?ž†­Ö8’}O*Æ¿_qò+O‚ó߹ˉCŸU&«ü –}öfŽ.œËù}žŸŠF `gú#ØZZ¢¾Î ’‡”÷”ÂQÐÊ3£a¶öÌûwçàY¥2ô*Ž0†e¶˜¨×Á¦í/…I­ ~Î*¾Á„ìÚæ”Ä›)Þùòâ w%ïJük6Žæ¥‘®Îbhw–¶ùÿöÿö“i5>,Üßç‹d™ç…q²âmV/i=g~é7h‰Ða—ÞªCÁ ezcÿì¡àúJ‘Ú'1· ¹Öj…ãç”p>†>A—ˆ“l‘{+L®{[׈Së§qlôî3ܵ.¹ÌîÇú`X—ާÔãÓþ1›ã'ßÀµú>8Rzy9 |\øõ”àÆ[ ú8âÔédÂØÖ<ܦí¼‘©õgmßžE‡Ë¿Áä¢t?‡ù‚³ékŒ·Fãqúµ=tøvµ“m1ÓÉøN' ~YF³öeÑÍîÔó"š=ûÃj>9°²bçiãÚÆøÜ4•¾×Ó§"µc˜ÜAy^aé™lÈ6 &š›Ï¹©CáÎæ%´ÚF‹õØ(@.ó¯bUz»&쯮‘¢¡6upð–$s×míNYF¾}€SR+hÇf 2³¾ƒõè™AƒÉ­™X»q3äµ’YxwÇ"{¢í9´#©c›7Tže‚^ͧ Ó¶¢­† -¿yÈF&zô)Ýí\ƒzŸ'aг0ü澄 ›Åÿ_}µÜKI2k‘&ó_ÝôÖ[ˈ%µäÎjèçÔ²ûÒeö}üUF-+­ƒ“õlQ2¤³™ytµü}úÈ­ÁÛ>m>8ä»êï£í•Õ2Ï’¼þÄ\œ?ÐÈd£'ïô%®ûÔ©´òÿYkz Çd³pSÐBRhÒ 2‚-Í!€w!}›`f@{磧_ÂáÔGøùófÍôG`C­|è« PrÊ ub—×O¼öä>göŽZÐÛI„$Ñ´bÌLdF™|ÁVú¤m=«yѲgáÎÕýË O87´,çÂ'ai¸üw ¼|¸†rtep‹¨ù³Yª¼5h{"~z-IÍÎ좷—Mäÿ#åÖ°ùŒ"~ ÌÀK›Æñéz M@Ó± ⱦ\¾2íC‚ôDó¼ >„Bíq6æ¢It£"q`ȳ‚WÓps6$BÙMÈðo„~W°¾Z -âÄÙ°—}6u¯|Þu7 yÙl\þ^‰Ø¬(FµÒXj@.ô¦ ¤E9³@xÍ4«'¶)™ó^ìXHkož€­]g8%†)¬ßÈTVxìsÖ¥†(?õ!«&±ŒÎS7zÌi!}0š|¯THå bÏÒ‡=ï™fƱ¯x]éëÃT¹¶T#ÄÈš…¶à rbýÇëš)ksNO2šdëÈ RxÒ‘žy³â-R±•ç<2¢¹ŒýDV}•q«f¯EAªõ{Þ¬¥Õóé3Mqìùd‰±J°?&€¾‡Q®¸ Åد:Fóv7Âú·–Œx4¡ÍWx(÷²é>_IG[a~—GÓ[ž?-¸ôt#‰R}lZ¬ÎqÞ{ƒUfhC†yÑ É\ZxÏŒ~G£ØýôTZ(ˆÌ­ÆëCD8bëÿžÿN£¢ qòªzvg*{cÉ5¬£Ï¤ ñÆË-ħ,œyZä‹1¿îƒÛKAZ´¬- ¶’™;qÃÎV¼Šw‘Ýó×¹…¢¶Ç'¼PIQËI]¼?2×Á•ãòda|Ç÷t¹xu=Ÿì‘°†G#¦$ñ.‹NÍ*ì+õñüøȲ)Räu½=3 R9ZÅ2ÐìïƒOàÕdITïºÃö'Ä€º«0gx3Öéf æüÄ¡\_r`Ú9öœÈA𠨇ÍãšS?",ìþÂlµT¸™* kâ·Á}º<ÐèÂer™Žûï'øï‡G£øAE§…W¢<«½ãüÝ‘Âu#ðôJ2Ü»RÁû˜Q}ïœ}-ƒ«¶€­Ù:æ €(MZc95š°IÌ‚Z‰×ã‹!g¼lZf\`zÏF˜™5‹úeØ“6î™ÝN¤¹=¯p¯† ZVÁ_™r:¤JÏ-ìv±,ùÁ“MõFQHYòkv2¹˜íÊÖͱÀ^›Pb`ù˜åaÃË8ޝúÐÃ`ý8þ=ŶÇà“[Û_Žá䕵°”ýÉ<Ñ&]R'°Û˜—|<}jvd|ÌÕçÃLŸÔ~r0ð¿^EƒeS™âž 0 Ù7×QÛ1Q8¡%F^ïÝHΡ¼ü Œ@†&ö¿…ióáïY)’¶»Ô\ßH–ÌYCOt“âsÈ”m‘ðNë 1_èL<‚3Qæ,y¹m!­Lµ 1URXrô‰2üÆ}/y€ôñœ„•-wAß–ëz¿›b#F@ÑhéŒÙ âž–„ûß^dOJ1fï,2`ìD|Çmëx2¯Î˜>>±„^û!E4šçÒ\wY¢Û/KŽÏZRïEg¾Î$’Ò̦pcòÉ‹,‰4Åœ-zpV-+¶dãDþÓz:R¾b¸iÅaèÏ!O3gRÎ,L³=CFžwÀcÇ<ß,Ä\ÕùYž×!ìhUÇ¥dƒk%©»üsœë7IÁÝîGIýú¥hííÅIyæ@Ö f›f%…¬jhv‘ ª‹o’¡ŠRÊ«¾œZzSíl52CT®‰ûXŒP'Κ{iï^ þD¼V‘ÓÜ!fºGÍQT ¯Å$ñi•/‹A^C,ÅÄ`ÕäT“U"<{üIZŸ‰‘ZHW9 Ñ—!‹ðO:ÙúWÿÄáüø©GׯJѧOç³­?‡&ò_ºb ª|åIÞ$U>ŽþŠoùt´†ê_Ç»Eá´éým8{%›mdȆU¦ôîÉDxj³ˆ®]X†8+è°ùTR0yœ­¨õá!æTY3š¥%¢Û§bðjh¤{ý¨ÃÌ+L¶Ì7yRí\Ïh/|øÌʈÑE–3¦¯0oéƒ.ðÈÑ$kRYÃTsRFnp[ÒOG‰Òn+Xg/ˆä­/éz_{"Áþï8ï–<ÃõüªHúʹaMiÄrð ÷~dÄ|}‡ª‹ä¹>?Sqƒß° z ˦կ†Øþ•_1í_Wóÿ·?(XÏH5â‰Y´ ô3£rýî¶\Çè켆‘£±øs^Œךž<ðõf![cì„›»½ˆZ{Ei.m×ë¶Ê™w*3 Ù­Êq$$'ŒÉû7•~(]‹+‘hD+°}öj|³ãÙžåáÁ*J‚ŽøÖºþ ,VÒC-Ç£$onÜ» têE’ælC³öì‚Ü?–pû³Áq*r.¥•óȾ±Z]2®MÚöfä¹U'T= ‘aPÕPMÿSDEÉý,ïµit§ð:½ô ØûðÒ%“x¨¥¡ jm5˜ÈOާ±wÎ*ÐÏòp}ä pz‘e£^—ý6Þ¥DlŽ´»¸Âµ÷e (bEtU‰É=7 0ßȤ®&[Œ‚ðëäC`²dšù ‘l~mÈÚë‡m7VÐ/7²?…ª1@Ê•¹l±˜õ—'—ëÓµF$WrÝÛyÒ´€«äVrj‡$pEH•X1Ìsf_±KŸd«¢ñÚ•ép@ÂD\éeN8Ρ§~«áÇ[R(¨ñçN‰a=·~e³}g%çVpª;úMa®u:ÜÌëbT8Dz\=ÂÔÔp.açªÐÃ1ñõßFÓ™Z[ÜžOüðôÜ (y!D×:iÂõ5âD¶£¿:³øàeˇÚ:¤ú˜S@0b¿*š4nn;‰cÕ°[ž¥]¢Áö§7é?{#:$ÑÎwá †i1ëè<þçLåôÉ`'µ›¾}7ÎÍÆ8ÿ¯LÑû}¸)ÄgøÝÄ9Ç[0‘«€óvÿèR4Ù¼›ÚÒ1߬ŸìK‚¾Y¡¨öe ÕŒögƒw`Á¢\/µKÄÈÓ»]lõ™ùØÑ(Oo•ð{Sáª_,åÿêî¼~oÛ‡ìã*ü×Ä;‘ÿnÏ AšoÀ6×½ ßýLɼIdÛÓˆ{¤=¤dÇ2×b@#UŽ„-[Ìà_½îºÓ£9txø7VÝ?ÏøçЮ¢<æ±ó.NÅÕ…ñLçU4L5œ™t9|÷Ë¢JŽŒþ‚j¢öÀ2—à®jX-ÍoÛ¶ù/s^O“H®’Çç¥=¬I­Æœp#WuG‘18ƒÏ×ïÁ3ϲÈ# ÂýÇÜÈù µ™e°uè =>¶Œ.”=Ýl$Íü¶2ÆUÄàÞ%üïóÖ*Oá狉‹Â.Ò¬³„¸ñãù·¤éA¨§þoýÃú•êêS¥È¤ˆ¿àÊ×IÂgN%:î©DUu1ÇO¢G¸pâ»lå»ß3C`mÌaró‹&žÏt$öjB´x?U/Ý ß´®€Í™˜~.ê,uƒéCDêLîÿ½÷=sDñ¢‡3\LƒÒ$MòØBŽô}%tò&ë3²om&1¶ó_ãlѹԿF„¾I²%m§WƒÓU+ücà‚{>¢1v¬ž"krKn=» ʇy‰Å A|ÕÊroÍÝ‚ÅÍVãÚG•´W¹‚á%ÖWËçe`êÔQøø5Âì'ðßÕ»œSs²–;wªžÞ€îšqQ)FVg»b˜ÀˆÍ*åè/)…»£øþ‚{)ulTµ¦ÝúÐ}ÒÞ;§‰v=Œ„@ W2›!® è“}^­XMµ}ê1àœ8{þ·åDU,3ÕRïU±I7OcV ^x ›†ÑEMud¡dQ1ÜôúÅx¦T‚ß*Ú0ûÙ±¸§‹µaŽm1³ù¢ ñ+: fËoÁê¡ytÓ¿Ög9”E1¦ò6»mf,„¿ëf†Ž|IUiÜ®´‡úàau ¸úâ<Óº¡bÿFϱL©Óa(Ÿ×„Ò·†;û1(§w™•#p.÷ÂNê;ö>¹ÅÔë‹Mm6Ó?†ÃsíG´2Àxzºñ´Ý{ÈqyÅ*NïÇhYg’e!ïÛréÁø —HÕǵtÓ>3rä¬ Y>} x³Œªf3á;1á’ÞÚ÷‘YåÔ…·lrX÷6fö7Q:kU¼èÖ#_ë¡'{·„”îg-t¤F_HÞŸOaf´"sËF.¦ˆÐ²Ûz4VJŸo¾Mo–Õ]î´õd+Óøm&¬A¿¡—øÏD†F»ŽâØr½ÿýþñšHêi±g%¥é“Ò(øüéÃYÿ Ëø[/y»yâž-öÀóÚœl°Ö£³^— Õž:ôœ¯E—ÿ< Ÿ4Pùè;lA[*ìK¸ }Øÿ:¢šq¤¿ cy§qÂPžý'SϺ»²/ÅKÑGǘrŒÀtЦ>‡$!²MîÈîÆOš€ã9Þä¦aäû0ßèN‹Í©·ú'¸úq;±ø%×¼=‰ÉQòä‘õ6lÅëxˆKõòô0òžÛHy¨PK•ÍŒAYÊÆM¢a¥ÒDís õ8{Ã1¦çÀC¦Íûû?$OZ—>áνímÙì§÷æÍ;ëàùxlÚÍoò„_+Õ‰ý¶tLyý›‰í‡ “ù0´{³àæ?N(Ñ¥E/¾€ë“*xÊ¢çÑ<¦¥h;ä:ÌÄ™ß{A£Ûˆuìð›•j¤õ™* k]Bç\ÊbÃÚ¶‡C¿ZŠZ •ðH@–­ö!ÿŒ¡µzBKÄšv0}EÍrsèH™«Fî€ÔäeôÜúh]„Âþ¤dÁJ¢m9•ù®4»åõçr¼ùŠe¢f’1tÜöÒœZ[Ì'U#| ´t »$=û§‘ W !rJðÄüŸDP=8sLÁLË [’ (NÀ¤¨,x]mCÔßE0?vi¶œº „wWÍÆ3ÇÚH—´Fl¡ú«¶RÍ-ìo¥ñüÝTÍôÕ¦°Æ‡ãáù>] °˜Œ„é’­ÝÐ|ž¹*ÒÕŠK‰åÁVÜa—îO3ÐXUš,9½‘­*€¼©AÌôqÖÍG“ÆÔô3‚DëËT5ïà†dz™û×áæ Ò4„reésKvì·4lßp+ßr6èmÏL×#z3ÛaæÅ‚–j.´ê›"öÕŠÁ'¯Ž·Ô;îõ­þ× ±Â¦ n¯^Tj–`_,jiÀÞÆµðù‚*Õó=Êyz nr,Èš9×9!IZû7]Š$@¿J7^úÆtDVBµÆwfŠ–L‹ß‹uH„b&^µã¡vs¶ÃªyPÄoÇ$Å…àåsø8e:¼­EÔ£›£ýáÑÁÚN/f²e°Ÿ/ž·ÑÚw•ý×! N>±Tòy"6™\q×!nEáÌ"»ÜYšÏ —cµÆN&µ< ONÞΟ‘Âåv¬…—ÎÃSýÌñ†‘§™¸ÊsùøñÏ-Ó]sá^ƒ Ͳ:2aÿè¥|ȹ™ÆùØ-‚Eë3á{ ñØÍGß§eAó’;Ø»E‚äçš°3‹žsãìB˜SS™°£.øáœ$ðôCÛºx\ÀwíÌV ŸÝ vÓüv´û!&Vä¸ëRöðNY2§ÿ#ãí6ŒÃ^iðtêzÐ2J†=]Ì“œÉÜwÙ÷ðSR1ŒÄ."÷7‡p¾O:ŽÉ[ÎÁú?ÂdÎõz´}øší œ‰NÍnÛ¢qrïCLz˺fbB°.myu†;~ 'Óû8ãþŒ¿tbùƒc¬@ò^¼"¿¹ÓJà¤HÞ (kó `¶ç$MØ¿Ç?ƒ{l»ŸëQI;yX{k2ëS›ì¢ËxWÉv†ï‚ªø{)±ôp`Š®·_˜GƳŠk÷àÍlG Ï`a³™ }#…Ö×aW©qý¬BJo-g÷ å‚Y8<6ÕÆ—^¥àsJ€È°-l­M ª¹ß‡3Q£ÐßÉ–„;P7Þ‡øe¾*P¦Ö+h5ï1˜óÊŸlà€ÉЯmEîÓ‡0°VƒcÞcEž SWK)L¾fϘ4IáÙùwÁB¿Úf·ƒÍ3 Þ§â·ŽRØX÷Soƒj”(Ñxw¥‡›'ø×´yo‚;ç IùÅݰõË#\«yÙÈ/ùb6nw›L\µ@ã’8 ¥ ͯMɦ)ïpŠr!ɘ%DîíPW çQMmà¾N$*Ÿü‰›ódzgæÜ–o2ÓHG-eå.Âû㪠B.»_b³¶Ê‘½â-t`ãZ%’ÊKEh_;tˆu0܈¯JÄ©r¡"œŸöæ_q%^Q|¶i¼dcç\X˜‘‰R·‡ÉÙ/ð”O14Y*Bë«u°3hv=µDÞ»Znhîd7×£‰¿µéèÚM”ì†/šñ´¸ò*Zv­ƒñó&üŸèzŽÌ-®‡ õ¬—„§š“È*'*£w ­‡ªR]pô³eªüplúgî?¡ ¬Ÿ»Œøh’<­«(9®Çöï‰Àvõ·&àêÚ—r ï,kåÞá­À¿²ËàèS %{Š˜B ð‚|4:Šž„g Q]`2mà¹æ†[à0ï”ù[B'•Xc/ÏÌоe[ýQtå4ûþGp&µfW“¨¢/àb-v„”Hæi ¶uF<ÄeÌ¥”m.$ ÖÀí;’éÈ»—`pQŒœ} #Ädé¯Î— e%†-q‰ö3wÓ¹Ö)Ü3®ÇA-i3{²\6šÍ/Ô··é6Ý,¶>þ%87 нç“V#Qu< |õh-”¯¬ïƒ…ç(óüØë%ÌãëáiËY¬ ;º:’à}¡î8¤…+`w¯<­ {…½xóh"l‰«ãŽZGü <ú« ®fna´o›bn:Q4mÁOõìkZÉ,œëI%él«­7Ùá}^¥«‘Ï…öø:J‘¶of†u_3¦SþÁ{Ëçhvòk0;®‹5žp,’{åwã‰mñhå*CCö~ÄUŽÒÿã?N¬ÒêL®¡Â%¸¸½í4{ ½»›‚7±"ñÐu ŸMúçMG“ Ž•Àoþƒ;ó*O~!¹18›]¹…VBZ¦@¿3±Ø© ÜÉ…eB\é&üÍvrCï”2Ûäh‹îAÜÝ.DçnÎb³Ï?@&ýë"M$ÿ‰Òý²~P7ç"ÄÏ3Æ`#ÒÓ(³ãæC7÷%gz¢ŒnY_„²CÃìèp"óa Ø(X’î¥Z˜4z [xŽãñF)H(ùˆ‘jæ”I·§‘8G^´q%Iô=aó:>áÿàwq >¤sWqU¸ •Qr¦«:pCj&ˆY„qÌBÄñÅæ=dÏÈ[œ™«4á}‡{8ëþ|ršzíÖr*»hñ¨Ü—ßaØâV#†„þ(['¢‘™rÿÎ+Áç¡ÊRÁòd Æø“Ü;L˜[ äÍ¢ÒÂ)ðmoÖˆpès²Êä4įûÇ^y M‹ ž_þ’ût ˜?°nmü|È8IM'ë’Ãè;5r9Z”=ºs xâË{-Ø¢ëA¯í9ÃXuU ßN¸ìD÷…Q±Âø‰L%Ú6þ_¿ö/›n®AA˜Îún›Y5nø¤èLñ2gÉzãAo*¡tÒ¯z`mR¡ÍmÕ6.•?ÌÜ+bþ}ļ'þ¬`x ” ÒõÎrÌYšŸ}„½zŸ¨¶i@ úQ6=_ºx%ˆÄ¯å øB‚ó¹ò«?ÎTÌ8›VÜxxkz”q–º N*Ø|¯d§Ù05S¡§+™.´ÇÖ¼C Ûv 7†ŽéÒèŠ öߎbäщ‚ç²È'A«û¦”ýŒúKë 8ýÌ7W"iÍ kŽúå_Äìí“éñMKpŸöÿö¿N=ÇŠ9³×|ÅÉ™Áw@e8ŒÕ2;,$MkvÝ«c*и2y7ó Ëów2Ñì¼Á±ÜƒŽBSHÝ"cL¿{“[¡NŸ)ØAë¢×øãét _nü±¸ôi!TtŸ‡Ù?­‰Ì¢Ùä±G 벜É›•ðëÝ!8ýä•|uÝÛðŠÔo–÷`̘‡Ýÿ"ñò¥äªÅ(8ý sR®Cß)qØøÛ‘rÚfÒ"²œ{ ÌiÞVMüåŠ!ÂÖ[e¯N®é.%Ïs5¢ç©6!*¢bHö9äã’G«9Â?LqÀ%mbþ4þâj¤ÊxÞ—ž·‘]žx,2î!C*nž…¥¹mÌ 7’˜{ öONív3¸T[ÿ߀sQäR#ž¹oo»ÍªG”Ҟ˹60“’ÈÔA¦dN Ù®¦ö“èÅ'Z´sÝ129ò¤~ØÏÄùBòzabwm{W˼v<‹‰œ Ô©Oª9á²6w%`Ô*3½ÎY¸£o­8ͬ–%f“ñZƒšUëêL+ÿ uÁW+Ñg¤µfÓ@¿Å(õÐ]5G€ ‡Ú@Æ­ì2êÆEï•Ø+‰Y2ÿÛÿ¨ïš «l¼bÿ€å4äAmuj™BÊëEà"!GrõÈ÷†Pæ ó/P¶ÎÅЩþì,õ˜dö0óÔ%èAçhRˆÃo÷`ï—OXÔ&N¾¦Ã ƒó°Ä; >OR&~XÃ…NoR:…<´¢:ŸyÉ­µÜÑ-=°ßÝ‚\švw«òÒ3¿n¢Ðu?ü·H‹Ô¤„Nî¸ËA6j ÝÑÍu=ÍÃŒD÷ƒRZ6Þþi{¦€ä©X”{Që…¶ 󑫜ϸÓòsÜÿ4ܹ?“„Ç`zY.wÀÒ{bý'ìOÿZÌ,=5ÆžȤ[_$ÑL¹(Þ»ˆnçíiMx¥CS´IEG*‹Ó¥5ÚDäý[Îá]†ôÜîUDC÷:~ܰœÎ½;/Gö§úÍjúÓÿð8þǃw‹<çìÁêOA³.Ž\<äDV)‹£¡2Κ‹ß˜œàa\VϳóèíKÆ·®ÏPÔ6/­Ü¯ND“'Ër°Â*;ûÖS9õ.ÆšŸ}ÓÎ\|ÊÝŸÂÏxÍ–åÕâ"¼…Éý¬QZ/2ÿÖ³Ù‡äÁÀÈì®Ä³¹‰˜‡u¤‚לhŒÏ›×s'Ö?Wl‹`}Ú›¹Â¯÷3Qóö!msfôŽw²»IŠÍ;Møn$ÑÒõ…˜{b&Ü lCSKÈþ7|7=‹“^jƒA!Ûˆ¬8Y¾™”÷Ñw\e¤L¡vð.– ›w¸‘óÓ=è€o8ÃwC˜Xü•&{­ÞpìÄúÑüâ`–]CG9²Â›:¹þ™)ô¥€ X}Ðf_A^Ê—$qÚ  üÇžÄTgRå\)?¾¯Ö$.Þþ”|+ÆÐî~bv4 d·i²Sw…cRÇ1Fü޽7 E F9u쉙 }Ød]AµsÿÇÿëø:pßÁׯ{Rzé’ï?žCþ¼‚ýgPò”éKá¡ÍE0tOqȽ‡‚c_˜ºû^ ;ã&¶ ÙÒ¿“µ`Z(Űm·qv~ Y•U ³€‚¹ÝâiJ5®îÃg&×Ðú®u2K%3®a÷:R4ãêÞø‚CÅZ¤[s:¼¶ßΕ>t$õ îc‡ºÔIÝ\7P9EµM9´IÙ Ãv’Ûœ(\Ø»§6â}kUj\ ‹3nd£^²ßÂÓ¹ =f ¥œÆ~ëwgo(‹pæ_ˆ ÏÿÆ¿><$èàW&ïÛ”‰ú×O¤Ù• 1Ætå[¸¶p §f‘è/±‡‰cË€ïî ú„™JFÐé³g¡âvÚý·`¤NÏÝÙ¦5WðŸ¶Mø8uI;Amæ%4‹-ša¦Eõ7£[º„Ø„~Ä‘l-Xá‡k˜q,dÉ|;FQï+|Rrfâƒ]ð€½N¾ïÎÈQ$G¢ðv/~[.ÀÞݳ›ö3‡ðfá}\½¦”¸K¾–Uà± >v&cL%›ÝöÄâ»*î\•MbîuáÕûYÓÇ|$I©~M$±¡×°b)³êÍÒ6ùÍDüó?U¡ssJØC„Q*,e7KƒÏCl’Á3˜<_„¶~îB»‹G@åE|Ù‹"ìˆÐÙù˜çW«öe­Z2ËœÊÏ»ì"ž=TPù]dðýµÁŽ>J§ßÞñS­;94[n#i3 2´ðßqòzë#vµ‚$Ù²\ÛGhã‡s˜}$†,Êu§ßÇN“»ãxÞÓHֆް+:•ÈñóÈÀ‘™d[Ž!yyÿY÷9Ž|+T¢Í‹0h’8F¬ÐÀSS!J7ûÕÊÁÚ5Òz!ïtÉoQ#ù·â_¡©dç{o¡?1ÿ7Ô–C¯J&{öúj¢"èJ¿ž¬ggNÇ€>5z5±‹ ±ÏÅêœêYá > Sñj7ãŽê`|h?æg¤±‰O“¨Ù’P>šBó ½(ÿ9 ÉI ·k(.òn„ͯµè1ž-tÛUâ´w;íÓð$>~.d³V5ÙÕbJN©ë“wJ›ÅOê´ª] îîS'§™ òh4¾V¦á¹G(ïn=2¸u ±y`NjÐð‡9pü&Ó†_Ê.Âý5›!N£‘JVx£²wY´é&ò5DoÇ_ôld‘o› ÙžlM>œ¿Œ¯u¾Oð‡Á‹Ì²éÚ8èY‹ê:ÐPw Ó¦1ÖâjÌÓK¹|‚‡a·² U&»/e; 1fÀ´§~œ?ÆFìAÓX§ïKÔ–ðÑ«ƒœ¸´ÍÆp©D¶kÚ5îÚûqhn± [¶Š¢¢ÐAÜW‡=a›hQì0ÚX¾€×G§Àó–c ñê+w. ž­cÙW‹5éí`q4Y„ïí ©ÁßDš\v–·ÙÀæî™Ø¶UŽ]œÏ®äó¡‹3¥»øÕp{Ÿ x;îîdc”â?K$ß¶€îܳãì×±þzËû‡öØY¡y#‡&üÿJ²TLå ìi1˹y¹65¸È½†Éî„ÛWÙ$ý;Ëüîƒ'^‹˜vIOöB‹Ä_©ÃS©î]ÏHiÆ÷…)`UpŸ}¡àFý¡*r ˜rGX‰Í3hFJ"æ—âyYe8¯r]kÿ¶»•pæJm"C·häòÌÆµQŒï§Q¸‘~ ßñ]dÝ 7Âf¯» µež«ÆÈôTß¿@,ÝÃG*¶È‘úN1TècQúyÌ'Miu@€`¦¾”#sÿÀ#ËŸ¸t_6»ò¯è7¦E5˜°Þlλ\fèö´¹üi"þ+¬ö°¿|¹Þgïáù¢f‰§ùë÷‹YæëªÝÛèî¹R´hE::ÌÅõù8?,‡±½WÂv ^>Ã=Ë|ûXÇ—°Ï±ÜÒw±7·`s‘“zÄ€*Î} '¬ ±ˆ ¢ßj5i”ô,<ñûJåQ¯òç¨òðK¾ñRþ?|$;4€ø}µ"_™°nF™³°€Ü¹oÄõj$%í4V¬꬛!:öGyº U>ŒÈ¤l!‘NÆøI^3›ÒàÙÂËÌ8Žtw¡éôçlðÔØëÎCâ[g³{Ð{¨Vî<Íúí^?áÿòüÓœ1o˜Ú%²M–b¼É{\"ÈÏöÿmdÓl›1g ˬ?õœÙ4žy%gž·o`óœê‚ûúÀoz<»=™–®ÜÇê-ÐaíŒzï*}ì¥Êñ ƒ;2gØÅµ^ä[¥]_ùR#åi(-€…ë&sž /Åwo¢#rù¦nzß„²¡·¸sã¤É¤C_ÙäŒ8hÝaKæª|Çš7±”÷¾´»ÜG7…Kx+´”Õð¨DµúöÀy½mäìëëL°×àHŵ<ý³œ,´§ò§‹º£S(ºRŸ2ÍB?哌[ÕR&]7.œåDdê’Å;ŠÐà †/•Ìj9‡óÞ>˜ðÿ÷åWÐ!?ˆØ¡ƒës(YšÆð}×£—5sØ4ÜÆêtéµb(SÃ|¹€Z·3‰Xø{P³ÛD2WŸÀé³ m`ÿüÙNÞž³!uñµØ`´ T/ÝËë·³ÝÄÈžayÐ9áFÎspîJ%oYªîGMM:¸gìƒ ÿÅøF×ÒGÝ®äWÊ3Ôõå!’–¿·ã{±s¹Ã;ï¸Ø¶L˜r¾µŸ‘)×F<PgœV eÇÿ·Q`”ûæÛ—7!и—Ù.¤õý1ĺ;Ž©]Ó›,¾@üßTvÖ:I2~úu2P®dŒ®Î…w÷/ÁÇ„ÅL|}³±<ÑfÕw ¹çç‘ø‡[!Æd° ›áõî*ŒéR½šËþ7Z±ÙÉ,]™†òkßb¦‘/~£C·‘p O;¯r^‹ýc}æà¬í?`›ÜQˆ³P¤O|¥‘•T§æ þ´3 ¤§ ^žyX²àÌçàË-\h<Œ­Ë Puœƒ®W±b5«`Ý$U:À§Ê4ÜglÜÁ™r·ñÓ÷¤UN…˜ÔN¢“6¬‚_¼áeøªÿî%ü³XE}kÄ©O}­Ûú_ÕƒåÂ>¼uø¤ ™AÕ? zÑw ’ß»xè›Óçp±œ‰Uˆ&µqÙ°<ÞÿqùjDØ¢DrI§ ùí&ìï×âjOÊÀ âi`öf>ÆÞ½Ô‚çáZøh>›‰¾CY[³ ÷<“¸C»üìÑs¸Õ"K/³ù¿­ÜÙ©ôS°>5®÷`Â}±Ç5™K¤f×Ú¡ý°|Û[æ!tÚ#·àYý Œ.w«'qèyø lIõ¥NOkÁ%Cþ®~oˆÐ[Á­ça s¥m²«ßðÊ»Ìw sòÝ*‚yÄé]Ãnöëå6Ì_t€{`÷B¢ó»+¹=‰ ±üÆ.YA4ßyЋÙ9Œgƒ y«ÙÂ,àÅvÕ•˜ñFði›°ÿÁ¹ïÌ—˜×8Ö¢Džu·)ì€ÆO<;k1ý × Ûú{èV½hšø˜œŒ²#ëO‘Êv%\¨¯IÂv­'üCø9+8›ÓI_¿ÓCIÑdé©h¦ÌGÔclÙw«VË¿mí ôþ–DÚO~ÄÎ&&œ“ôæ'Ò˜pj3†Åz¿c¾DBÔ˜ÜÜ­¢·äñóÆMXêQ¾Äw†ª’”!>üþà.–œ îAñÄà@–Ú.ǨLrÀâ®È+¥ûŽ¬ÆÆý:„ $Á¤§¨àü(01k"æ_%pí ªE$ €eèþ÷[Îa®ì¬Àé‡ÑíI©äãL=º3.q¨` —ðûY VŸ~`cš4ÙÐ}cÃv±¼Â÷°ñüBb%×F´›T`rä Z-CËg6ÅFMúÓvdu”À’ïoaQ{(]ï”É”h§¥D–ÕûÒCÂøÐ9Ž˜ç\ƒõ¿’Þ×å4mf lРÞz¹Îi‡Ãdúâ9·éŽùÓ†E”®hå,I&Ñãyæda\6º KƮ⬱¦àD6yqí"»:ï.5 "¿—Í¢žÅâȦÑhzÈú,Ê‹x#)ljõEZ»p¾ÌUP´Ë¡šï:Yn›2i)heÖmjÄwÃ"(.þ—CË“\áx2â•„ŸUE(Ÿ£"mblÔZ26© ž à¶™+èÛï[Æñv5ªP…ï3ç“¥þGa¯ërò`´ g9öãyÅ{v9|w_C=¦O#æþ†hlöžµË¤5WÕiEµ8y>2 }?ãС)äý>\ó¹dïλ Ã(Ø^†bqU˜ð”ú«ý‰Æ\SrA¯9|büŠÝFÓ*§³ÂCc°ò‹?÷ÏÅ º°Ù‹îJÅÂ7¨v¬‡©öä~V2|Ieúçx—,.=ÌW\,!¢)½wÌ¥`®Äš²ÖÆzÚÆqö.;*Ir¥{ñÒèJöoÜi,·{މáÓÆñF…êç•Þ‘/ì¶çMðéþojþÍ:ÏÑ!óÛçÂ97=œMåÉ¥e͸%lÒø}Ò`¥³l\¯BöÎH‚d¾Xq÷)žYÇkOõ‰J9½©%‚SŽÁyÞ\0rÀ¹Á.xAx³ö‰¹{<ý¶O‘ú% ’/7?sÞ'Û‘£šp+'©ˆõ£Ý.wá–é4ãQÇTÝ|ا2~|3çb/·¯¿a+G²‹†'âÿtã_v–z4ž×¡Y4½PŽVKS=8‚{EX8É-¥Ÿy“ä%Æ$Yæ-Çd^/Ôô,iÙ¶ ]ubAn»8¼Ì’¥;_øÒ]+ºWÐ ¶‰ 6Ì>jÔp–¼t¡Îë’£‰“~0tL`xþ#Á<þ“Ê=põsëÐBúz Ž›wõ c\5™üzP²‹xr”úäÂr¿L4vܧ-“'ì?’x ÎkPþ`>œiH½éÒºó 47)šþþs’i ›G+G·“yG‚¸Âx``çŽdK¼(BñlA|\OòúÜXË«TËù3ön¦CwjaƒÁTh·ž´m/&SÅ^P]sÀýÚ#L ÚDŒ&Í"v»ßã´¤P²vÚ âT#AÄéÀUOb˜Hž¾‰ÂU.Åp2*Œ|)ƒUAùç¢T,G]˜¬ÌL<ÜBj×}¥+ÔðÇÙJö[ç&Ú§Ÿˆ‘B÷áÇYCtüôK/‹íµ¹¤üe×7ò Þº0„ÂON}wþ×ÿÇfÍÿ#êËÃjü¾·›gÍsѨII•:ÏÚÊL‹Pe É̓Ҭ”5 ¥½vJ¦”PCÉüöùýñ}ÿ:ç\{_kíýìµÖ}ßÏYç9&î¦÷7Aãçhx®ßÌ]ÅKù ©‰¹à´sì>®YÛ©€_&qì²b%Ìh‰ƒ—8Þ "™jªD,-à†KøôQxÖn䢔bØÉ”[ÐþÑTªŸÈ<O€~ž3Ð]Žc¬ØÏþñèÙÜ ¯oPy'ðÆAÒ['ÈVM€?c¦þM…¬¬ ÜžüetxR6;8±[áCøâuþÕÀ³ß jvì’jŒ‘]Z~µ¤SiC4Û:B¿RnÊÅepW?®>÷oÐð:Ñ¥÷ðÏ;gVy”c¶Žÿÿÿ¿³J!(1ĶVcsòu¸S¡Ê¦w'ÆgáM+nI°pm#.aºÙvá${3V /ma[–Md—ƒ–a˜iF]_ çC®qwÌ›™tN/ç8{&“•6úD5­Š Y³NÜ u9A´8íÂqP¥â†wñ {pªÉüûªu´×‘§W–6Õ‰æ§3þ9‡\Û1¼dgÍ]Ûg„¹Õ“‚É7&¡çºDrJݗɃŠ_Iܯ¶‰äã«nX²‰è7¶ åy2ÿ½8i®³$Ë}ãiò­C|eµ,pÑŒûÿø'¨Œ·GCd5`XÉ'*‰æGMW冘ƒá¥5¨>ÏvGÁÔ¸zêÿÀ‘Íé §ÑÕ]Ðîu…®Oìà¸y“0SI¸ Þƒo'ÁºlþóÚC=¤’¹(NˆÍ2Ú×émp¸Àk+û‡GõãA«ÈuÖ&bIôX/»•”HeÔ‡¯d*Ûf·Ù£¶§ñÁo;yS§È‹ô|„ =>¼›».Á°€= >’$˜ú3ÚæÉÀ¥ã'çR1—³¢0Tx–«Î âwr“‘ìvgc^Š\FÖ_uâíÜJ‡95ÜÞlËÿÕ™®ë|ëf!œÞ{Öw¼@÷—¯é±¯CÀ[ôw~­ÁjùxWè4]Œ@ÿÁ"ÎË%[¡ñ^xÞ'\÷ãT[èK’ac¾¬'[Êøt8–l%×¢¦Që ]h¸âÜ+ù×iá‘h¾GäæV­K¾5 âïË[ cà37ö‹,«YØSÄÜÈ€Þ'êu&8ÃϽÊì1Ö‚Æö«@²À©3cY²¿+šž7¶ÂÁñ™TbMn5‹³¤c4'è.o¥3mØûgGqÆë:7fq6ŸJ:ùÚ¦üoÿ }¼<µsO2Q çµ*XLè&XqOsýSˆÏ°!©6”åf\ºÊjv!œtZÈL%è.Ź,ê¥0nÕº†W&`jùúÿûl/hR+pþÙólÁD#µÜ‹»–È»¶âs½8i*k³EŸ¿sÿ®Ö1Åç¡yû_lõ Á¸²mdì­ôïá±D¤mSŠrÁ{fâ6Ù™(_³›™øk±‘ìíx{Š<’í¤6K“ÉYÓ0Èr±aWd’‰ùo “”óÞÃÙƒì`T¾y[r:qÿoà®=™T»ñsôl°VÙþû¿*½3–h²•ÅæôÝðž¼ ,\YOö]Šc™Ï2Ë×ûHÿOGvyÅTœg7Œg–pÑÇRاcÂ$Õ<†ùì‡÷½!éL2¶Ü<´bȶýæl¿œXÊ„óügžbË÷TÓà Yð‰ã,nŸ2»0€uÿ€P7+öxqÞ¾^xâ«4;!p9´æìc?Âf‡du®õÖÌP]N–¯?Hþ7'ý6a´Îmî¥GªßcèaMò¶4Ž\ښʉIÃôøô;,ÇxÛÌHykÛ]ûö:°Ï:4óó)S ÁZáÿÿýöÖá‚sš÷â¦3òÜœ=ë±þ5É+0ÓG/\*_Q²QûJ}‡Hƒ{¨:÷7¬yᘺc'DÞWÅËïpz×k½ð‘~ËVÿðÂmF†,ììtš­wïš3×E¬7; =ŠLpùõu0ð¦e×'þàîçYè½¥?I¼Á^ï!zxÇU˜ .ϺN(Ó±'Ó±åË{ø–ÿ€ªU5À÷w‚,Óò6Þ®(ø_ýýv¥ zé+P(ü÷p ³¸(öèÿ¦û¢ÛÑÅ^€ÝÙ~îK c0߆™¿`o®ņïTéÇ)îÄ\ ÜsÍ·Ù\ÁÁRÃR #ÎåóQü{f3‘®_ÊF²àœÒR@Ø–=Ìrÿ÷äR`g¤ +i˜OÆ™*²t))¼ÿ9ˆY~*ÅÎ^ízÊÌÎes®b’` ·qò˜ç©MåÙ@Ï îí j>äG^x}v\2VšØÌ±àþóÍ%g³n[rôöup¶amÇ3¸­¥zÑ> ¤€Óêƒ8qšþÿðϼ2(ŒÆo`>êÿ¸§ËNàl먺FglŽÇç ZÐáÍl,©¦…žðâOª2™±xÂi6Ĉ°$Ÿ!Tÿ¨Ì†¾ˆÃ°ðRJãRP³lÊæ‘ÎÓ1œÒš}¨ô×Mí|0¨G“ èØ™/D9µïX|ì~V×c–}Pð¼7­Æš'û@tòx"v_ˆ•פƒbß?ªøe×9&·@Œ=}¿„¶ggÛ&Á½!îÅ‹Õl›Ón4¹ÆJ "9¾Jäëªïøúóø,jIz>˜Àím÷ðÑ|Mo0“ù;  jÈ¢ÿÅ¿¡ÀÔ(¹ÁS[8³1îKfXº:ã¾Î<øuûlÿ¶·àÔ-œ[ù‘SØŸt•ñm(Gö:¢º«Óïr­õE[ü‰[7£r”w&°:ñÍ\BÛ{Îoå!xzÀý¥tÈ@óC”øìBº‡b«¥ Ú*ï¼’ŽGJÍó¢¬k]ÊB©àkÙµÞTùjD/ŒÙ®ÊÔ—T£«®,yÐ1‹x‡¶&ù2Uw3dK³ñ˧šú¬¹’ìoÇi°ÔxeÂL¶è„œ_Hߚ럫œH¢÷'ܨ!‡µ¼Ï£gzõñ¿_­ dÄpÅŸ7B¾c0.htƧÿ ­PŽ,?wŠ›Qt»-ImF.®oOšŽÏĹ“ÃaG~9.˜KX‡ ‹úµ‰¨ßϺ¢Õ‰ìæ_ÔµÖ„¸sA×Ü‚øÊÀ­Ÿ- é•QþÕD2¢ŸÂS%âú’œ·ì{jOµA—Uš0¸!ÜÑB® Ü—ÜŽóqŒÿé6èiX0]®÷¦Ì#^¡}Ø‘¶‘8l¸Heh1KaR½? Ü&Ž% ^‡Ñ“¯jq?šÃ=Ï)°$éysŠà¦]!ø(O’Ä ˆ2Ù#ÔvÎýÿÿÍU¨¨ ,De <ÏÞ~Î4HßãÈ«[8É| {rääek†'ðÁRQx!­?§ÅpgŠ®ÑócÿÀ÷!1,r˜Í]+¿Ç}Ûø3Ò>q?Ìa1A'ÁXâ$zŦs·»ÞÓ{-A¸§o&*ëÉaÓ¦h>#Ætâgðgï²­B_rz%èG<¥¿²7p{C½éʱ+ésÓŒJÞïÞh1;¹  ÛÝÈ9VEÍB«ú¢]/Yì ižÐ.šà×Ïÿý,—FÄ(C¥šäÊ~m–ZЋcÂ…ñùÁ'TTD†£úÁÿÛõðBü°8‡o»d=Õñþ‚U : wÛ Z“øŠš±xàñ<º<oÞÞˆn¯ÍIǃ:ëº=±²oÉËàƒ“>‰nÀ¥©×ùs/òî”5q[ǰuóc°}2Cgµxej¼ÓŬ¸‹ÜåGŒ©¤c~×Ã…eYìù»¸®lÊ?UÀ V}T¸__xC‰ÈU¨Áš/iôf¯4­©¡_“ÏÁùC¼q ‚:¿·À»€·ZÑß‚ÑÏ/cÀ¤PŒØ÷_;Gð<Ç$cï3â¯M=JOòó§ò'»Üt4¸µ™´7®ùßïÿ$„’aØÎŽoêxxs5Ù¾ûHMCñ)çàCåkúÓùÆÆŽ!Ð Q'’á]šsšñ•Ü`HžŠšàîïp`ÿLô`|?eF5Ù’x•e\±õ[lˆø ^±—·ÌÂ×ÕŠàèûÍu-qïŸtüÍRxV6—¼’K~?@A/xÑì©? 1­^‚ĤÉPÛRžP]¼NS&pn5gc€§5ì ÔEOÙþ¿¬4e¶iãA4åü[€ÎÝT&HtÿÕg7*°¦$iX>ï/ú„ÎÇÛAëÉþ¬fxþïüµÇæ’ÿIÓœV8ЭÊ>Vj±WïA¡! T6åRáíì¥tïWÁUø½t9P÷+h™2‡(«À¬(aè¥'ŠVÁ²,ýNœÜ+¹ÅZ}.gTA Á"\0ÝL_p³ñ¶-{C¿l•Ñ5ñzcJ¡¡ò)¼xàÁ[ëßÉÅ:¢˜E(¸„ ÏŸÀ™Ó¡±l5Þ‘{ô¿úñg"Þ(Žlý¬½‹Ãc+à•üO8=4Û=Òð;Ó(–öÅ1H¶ÄTá‹M ð„ÝqGãŒ,ôAû Ú'ÉbÓ~ºWì‰ó™ãè²h-x|(“b¾p;ÑŸlÇ—à§¼ üÄ”At503RÁïQÕ%K p¢ìSZ#0›õÏ”ÅìÛaœíhî¾™I¿6!ΟåA´Î[2/QísV°dª &™žr8·Í…}9î@NPÂ¥óÁÐ9 ÎfüÃr™pê|F‰«¡—S?˜/é«q”‡dsÍׯ`tíÿþ­ÕÁX^µÝr\ÓõÞ¯yÊkëà:úîÂq»p\”è:Š ýÜò5Þ°¡ÚâïL†0C3üMÝ8S‡*Î*s/µú=Öµ•ž+e;&;þ;|ÎL…’Þ8ýóÈØ{…n2ÄŸJ8£Eš™%.…Xžá'ÎÇ·«ßqœœÚ|’_Ñðœ©è¼(E,Õ˜ÆÌhZ!Ž?ÍcQZÊž/yŒsú„˜V—[¬<†_”WæEÂjûV|/mvqmÖQ¼áy— 4LãLŸØc—ãˆ0ß–ù‰ÖK_°ûP.sŒÑádÙÿê_¶ÔWüûö·oÅ4Øé¶ ÃIeüdœ6Æ–-Z]‡’‰¹œì^¶iÆnX|°ÞŸÄ–-e‹ãÓÌ‚[¶ “–\‡…ägi/«6ƒüWnhm öüú…Ñ…m¬¥ GNï"bÞC¡Ô*ÞÆ‹”I“È|°; g Á{©Il†ô+zÝtZ_XùcËðÉê_ÀWî¢ »ÇÀ÷ú0fE-ÌíÞB.‹éðv…‰“KÏ?b{˜§ê:G$‰×ƒ(ˆ½8—OÈä&«j“‹–½&y{miÈ–+¸¼i]áPõ¿üÅÌ·ßiòÜO4^k¿ào÷`Õ~ìˆSCíf [Áººl¹üâ& ½µ`{¨ ÊŠ pÌ>œ#}çÏ<@ggèbî¾f˜Ÿ3Fº]¨X™‚™OhÛŽx®b@)TßA›ÅTf`<7LâpÊÁeè²?ãF¹÷e‰lÜø’.&¶”mF1ï;¼§Î.´:$‚gÐzŠÖvaõI2½Š×·&!‹A¡Ï Vá†Úž4±"ýW?T'dº°[ùÏîŽ ½ÑH‹,”Ù·œ™<ßÊ8nÎ)Ú½ucËØÿúßD¦¤RÝ÷¢PáTÖúûp°q<;¸HÙ/hâ̾ɲUô`Õé«`û8ƒ>9z=º'±1Ò·g|:Iår¾@Óõzj]òWú1nÒ[\ãø’{9‘å…}§"åÐ6,ræÜ$FFm Sà’Qý-E<òÒ0ÜOj?NŒ¹]õti <ÚÅ}ÇÑÆVê¯1:? £æÖµçð?Es¥ët¹®KUt¾~$ì«}ÌýR¸ÃÓЪYœ…†Éὓ‹a×¾®R,jžqjFµŸUP°O‚ˆk‘kyVŸ§MðŒúNxw OV— QvâÙT®¸b"IÆSÈ-XE¦ËµC§üž#Ç’ K@ÂUiM'™EŸÁþ0²IUëqR“,ùo콎 ù¾]›”xìÀv?ЗšÏš?$ãÁ /A®ßŸ›¨îµ›‹Qãï~¢°ë¶SNh’-$jÃôR;"`cBFm Ðõ«Ì­*‡èD‘YR1¸•Ö£Ýå8lqƒ¬üXæC‘¬Çð[ýÒ߂иG´ƒÇãð;&j´Æ l…¡ºDdx+2&#RIÖ·~üÕmË^I‰±S Uèy£ v[ª“J3-æWÞOwŸ` ãç2íÖ̦ée÷¢“­]…÷–“UÝ d›cmÝhÄrãÙ¿±ïðí’4Õ'Ÿ7(6‘ý·ž”MTµ?Õ¢~´ë‚O@ÿ•“P»·ÎQ€©^6ÃÓѯ8ÝΨÕý£êòï˜ÿD‡«Ù@ SŽgîE.ôdÿU0ŸÚO¿aòn)–6Á”8öcÁü}¸J*šû(—ŽçD‹1F½Ûì? “m€ýIÄy/5‰ê~i¾Ü¬5Ìò°1Oa¨—¾ïû†"1jät¬&°®Îdbƒ 8?Ș£ËL^ßâ¶õ®fWÔKñ]ö2´ÏÀJšc˜Î¹@H9³€˜( cæÝ R!­‚¢l¹Þ4õ¸Ê;¹E ;ÇŒÐýbdyóäiE¡„ØoNni?Ôm¸È¿ö£˜»µ4“;·8_¸CÙè¯\U’¿±íìwð&i}öLDr)Ö¥ ר­!‰{áÞíû8åüºzáYÞkþ|6r¾û5o‚ÊT÷owø¦"ú©½Ã²WF$nÆ[Xœ¡Hæ}ƒç[YC—6»{È—¾ýaÁNöŒ'¤ ÉÖ}HïV#>ÂÈ’éšØÛz™?A¤Ë–Iã‘k^",¥‰}y3¼(‡L똫üsܶùø¼é Yx>EmN&ûñ:É6ÓÈ…(- †† ‘0âÿóîOcreí`ð¯Žß[B¾ýîç|V4 –±*ÎLÇð[¸(á› §w =ª PûÆjšS¡@úœœÉ‹«3§€l² 3ør„—´¢M§_Ã/¥ ±ZˆÄ˜AÑlMÜiñÛ&°ž£v3«8±Ú ¿nûU¾âÚ‚ìô<"iæX\°w5-eú[˜Rý2õX L[©ÈÿL!^ÆŽlLv<¹°‹".Z@Š7­€LÍ*h ²‹Þâ®'rL"¼d½óÐÖX€Ä b“·Ê=q=’.%ÀIg²òu¸¹c<³T—fÛ='âõMl|Üö$Ài-!Xót«Í‰uâ-¸åúŒŽÉA˜?"Ob2 á=W{À|\ Ù6U“]¹Žpë°3sZ;ŸÜ6îC·ÿP§!¹û,™ª¦®`÷ÿð—L–¿Ÿ¸Rò™ã¹V$ìÚVè•G–==€ÝÚaäÇÑX\µÌ…Œ‹9O£ɰâhmâmÜ‚‰_F8‘ü<Ô»‰Óîé‘¢ÁìæîOh¼_|mîÄéeE¬÷–Ó6T!CMJlõmYê<ñ<=tÃn|ô[Dû&ÃMx÷áwÔ>íǾ¥é²®Ã=°ô…i#‰+0íZîTçRXáÀRŽûÓ•­lqA"Ïþë;8µV‘4¾‹¦n  ‘w%T¿h…H¹ÅØ<0Žó½ñ äš¾@Öôç<+a° åO^«Ä 2ø‘ó·qÇ£áãâtØý4 n>È­ø‘‚aÅd¸þ%ƒóîF@œ­(É/üÏ~ÔÒa¹ªŒNꆱÝ?¨ôúÙ¤²C…t¯~ž7L˜9‹Zg³h[Zt¸gÛr½ÞF,P½Ÿ5\Æ’˜(øñµv9óZ_цÃô"ˆ@ñ-{&ë8‹=t‘@Ë ‡!¹t™ÏÉ‘)qÕ`¼t¬|zÿvÏ„Àe÷1iÀׇWn¯µ73µO6?ä¯ÁaäYc†ý¿r•G?aɃ0ÊùUÃF‰m$#7.S*.êÂ:¶ˆ@VzGX‘ê3&ÛkÀÖ ²t*Æv‰RM‡jèòâáÒÌl´`NIÉ05Fž™HÝäŒÝ6Á ûÕÐæRg 9ƒŸñ¨\*±õqd¨b;ȇEQ'Ã+ÜÈâiD9õ\hƒ“嶬}üj%(JTRNÑû »#©Fçïm˧mÒ'ÏòÏp39,;:ƒÁ}.¶©TeüððÚãÐ'IœEWp< ¢t=&¾7æ’ׯÀ¨Æ*\¤>`$ž»z¯å»uXÜJ˜ r G£‰ØtD’]i+£Eá¨å†“¦¿ÇgÅ«@@· Æ6ˆ°‰T†š”u£Ãz .>è—7+ßø¡A“éš·Û˜Â]cO\?Ùj”›ÅÀã¼ÅäMà nÚ–c ë Ì-È9‡»ßµpý2=tó)s\¯ð…³8 ‡ï_GyÙ©$(ïwKD‚O­¦5öùtš¹+Ù¶L›ŒÓÛ «»¢á±RŽO)­¦S°ìöGG¥›.¬Úá"D“Epnk2Ü:v_vAñ‹­üGLqüÎ3ðº`̆ôê|8ÒmËT•Cÿ×Y ##H&OšÂŽ—¤¢¬Í#ø¹Ôk&Jâ+}z?­‡z.Hç¿P³ÀÄÞ)¹‡A'¡×¡ç¸]D½“xîp™ã.çJÏQ•2ÔxìÆRZtÈk7xïÆrùaghÔ˜ÅØek‡[Ø]¼¿î$¹¦Ý¹?Cè¹Ã×`V×~>lƒ6GpõNS¨õ2f/òç“}o†0 …Éo4Ú¹•©…±7 aX•² ·e/Š늹ƒ›3¸l½hBæÉ3¬ Su ˆ/ʆ»¥Ø‘îZú¡ç8ˆ>×b/þŒ…Eêjt'·–i-f ^AצžÄ¥®§‰—Éjò{E>ÚþŸÿ¾‹s;‘Îö¼…òeyÐ;9Sy“ÆúnàøÔXĶbÖz†c[ô±ì+wß$Œ!gåÎ Ï.‰ÞH:J‚—Ù°5J÷êsvª’Äw·áÜgÓQ®^ç8òaù#Ȉ $rFÕëÿ —Ï8Þ0.gÿùœsßlê{Îí÷¿?\ÑÝÄÎÝ‚åi^Á/“ŒØoÐÔcË­û9‘ÜV'ª:ÄÑÒ¶ëNfJü!íÀVœh=¦Ê±—‹Æ‘ »HýÆ4½¾•‹­[ƒøœ¢e5 z°¡uõŸ»²%=$=½ža$|ÏrçïÄeFWpåoxö·€«Þù˜JÎR‚Y“:@ëV ÓÞlÇŽKŸ¢fkÓµqÛŸ‹`Öñ‘SÚÑ„êvt›,áŒEH˜t&ýMt ÿï.rÿX2|°Kx'¹M=º¨ízZ×ËSó¼ChKswº’A«l<ëÈÜg}¸Ðhî9ðî]`Ø×¨ÆdãÒR|£)ñØÊv°Çó–2Wwºhõ*æ}ð9¨îá‚.GÂýçVĬf,3ןˆ3öØ0¥ýFhwãfdå{TIè‰ãtêS3¼æ0 …¶}ä ×ÒÂ9“/_NÀ}c'BC^5ÚÙM3”¾ÐKÏ·ƒpÊ2ÒTaž² õ£?+çÂ)·¡úm=«ø9îŸÐPŠyDáü©ž¤½& t¥ÊÀ#ÕÇôS 5fóo©ê(Wò“dvwöaÃv d:­ …‰£ÉNü5/NsjÁ301,„¶=³Aå'IŽùâ4ÌT—_±&> >ÒèE–lªÛ.4ž´™é4/e$VÙ£ 4Ýÿ…ï/ò&ŒË!«×çÀWCÎëC }U æ¡ï bÕÍhö‚›uupÓh ]f¡ÂÞΣg' A;ÌÂ' qxÇ7óÏÊá›k§a]!¢´Œ Øvg¡õ\'öaQ2µ¶¢ÜÙ} „Û"BÃz~Ó»—#0øþ rhj&JíÀ®â̲  sNïgæç }l(Ã'Qùši81ÊÓ/åQñmÙ?=¬ŸdÂæµÎ`rã–ž‡3E®8E3›ÓûMEsÁÈú)¨vŠ‘ñÇwr¹³_r㯆0lkg-v÷š2s1€Gagàæ!e?! 릖r:°<è=\HH%¾Ã9½)“3ÃjЩ01sevTb½ð›] W&`óDqòõ:ƒÔÉ;Àys$žÛ»œNŠfVG#¸ôQÕqþ!7{×jX ØwsCá+Vé–D+æj²²òî÷`¼»w›Üp³n1 7%Ë`H[bS/âî_6™ÐSS%Fu^#hjÏ&VW3±'uÌ)ljñ+éäºaê{ù4º­Kç<cùgpªÞ:h®Æ†Tà‰Ò(XeÌtåÛñ|æz.±Ý“Ä)næ–ŒYÀ›×£¶V+¦ýÕ'÷çÇRYóýÜ¡=!Ðþpü¼¿ ®’4v‹y{›£+‚g~¶£lË x?c-˜¼Å,ÿHÜ^ÇáçÆÙPrCˆffÀ€úzTœZ¦‹/ £ˆ›£PoB6S~K&ÿÒ¶–âG^|½åÉ 4é¾ùí¹"RÂYPä%æz&å3ÓØ¢Ù׿Ú&6O$þùÚĸ°ª,IéCK=[©öQ÷ýl ·¿8‰± ·)ßò ªæl>÷$â«­är†Æ€ÆÚ58Óx&´T9BØêaºxâoN&CœDomæçú÷¾l×ågæ‘Εyè:RˆßÞ¬Ó=›i“f?¾âvœžÃàO/~F"^=pÛ1yÞ#ÜlÎXÁK°4 oK¬}”ƒ6™i¸àt wóØCÚ^ AÞ‘b‡õ‰³Êsúz”ohxÀþÕ™yËáTª3îÒO®|ÙÆ•vSŸøî(KF±é$¿ Ö}¢¶ÇJp£Ó#Λ?¸K/&iW§P±KåÜÝ~¤ÖÖïaAâ&´éÿV“µX’ñx6°\å¿ß5 S…öÛY£¹"‘ÞXÎïY6†ãú!ì™ ò†ÎµêõÚl|S)4ŠëcÁoÔ¿v…·ýÌrnÔ÷h^7ð_ €¾q6JU~€‚³•è—¦Ã4Ë·De€ØAGN²<ãZD±g<6µëQCëxX"w*µÑlþtt9aŠO¯K#«m8­¸ll]sš¾¡ÇÿŠý×dY¿!Åñ:2ÜÞrÐ>òuË0§¼$M̈ü-%æöÌ…Y†¥9IT}kJNfGñÚ/e8†N3e3¼Ó°IŒ‡e®RÌ+© ÇF1JäÊN¤!ïH®}¢Âuü^…qÇñqì8ȇ·ƒ‚ÌÊ7ò#òx‚~|hÙhÌò£’µÌïjSÆ5:À KUvÔÉ?«Aûþ{8òh,¬jÎä?[‡*\Ù³ÁqfÛ1Ýåí¬0šýö±ÔhV>˜f΢+ö²â”1`¿è^ùW¸Ò© “mȽíÓÈÃ}7àüúlâq--Ÿ—€ÝyîÃùïË6¾®¡^«í‰ñövt[9dTÌu”𚺎r»d²±ñÈFöʨ’ÕÛ¡¬|:E}ž¼øï}´»uÜŸ¬=t—ÚmØÀ ²mù €^iOúafàXv³!®ˆ’²¶|¢+M]!ÙPžíq¨ÀŠiü‰­Îœ†äòi÷-nÌÍydã!²îýòFn%̹é¸Öx,W´Zž,PÜ‚O¾ ‚Ñ”$¨ö\ Ù3Y¾g)Ïk©5kT Ÿß7wô|õ%XçNî_R v=žÂdÞžä_aÇÌÚaè~-ôÐXEönn%ó¿½ .pbáYGÙÜ-f’ÀÒžd1£8Âʓ߰>ó ã§…QªïáÒóRÐ>ý„'¿?–d9/Ç›=½(jXÀç>À¦!ÜëÛºlvº0S¶¢ ¾¸ü=»¸Û¦ï`¥;Y$E¼¿^Ãï’*ôXÚW\qņ“ã³£Qe´Ýª=\CÁ÷Äu8CGàdÙ.&w¨Ô;T0+ÎÛ?°¬µÇq¼Üxö³ÈŠ ªß}ŠYuY"âËbngÊIpûÑbøtº†7ײÁqâLÛÛŒˆ‡í&¼hÃpc݉¿V«™R‚K®ƒiu÷nS{ýã}ãY^r8ü½ñ‰É~eG¬cÃkV Kb•=Lk=dÈÞz[(ަ i®Ã}Žˆ(CÅË©`©ÓVOƒãx~:f0áë¶ÖчTL’E£Ò›tÕC5„Ã6Ä{ªiʰ mM“ñgÁJlð¹=ÏÙq‘iüõzB,lÙa|oÿ’÷"œG¾L—ýʽ?`BVt¡rH&†IÖÃÝ'¡²UVÄN ãÍy$S*­ß:Šîñ Ë“u¼9‘épEÙãÚ­˜XÁaöæ™·QW•-žº®Iã]…x|«jA [J¹û;*qøt$û“xòfæ¡'߃Ì?#‹ƒÈ‘tz7àØb!Æ%iÍa¥·”!è™.ñ”¹eòXnìϼ.÷KBëº,´éL¥‡g¦ãïxd–<ÙôÓŠ}‰».¾Ã9k^¡Ã¨f—Í©1rE§Š´¬b»œæb‰Õ'ô{z‹«ýÈ=_Zºëþ4&èÈAÕþ6Hü3‘èíÏÇe1å°Äm˜“ÿ g7q_›>€·A.†½ÍÂÝ¡89â#¼šÏ2‚~À™š<Ï#—yØRÐÈY˜<¤qñÖøçþ;LòúÇß¾çï|º%']á>TŠÁO[Ž-šàÄuèG`|³Y¢»öhtÂüCa¸øº%I[<f«Ö¢üE¢/Ñ6ü2“9l‚êèyÄL€ëcÁ1+>þå±ë3!ü )ÿVb_^Áæ/ãYûO–á̶/ ìú²Ê%SëvÃçqÒVü¤\ØKýë¤RnLL\˜àû¤û·<$gƒé¾d×D’’së¸+A,7a[ÞžA Ç9Ë…1$ã'Žý ¶§_pt¾ÜúGÈχôí9 8#[€³T3ðÄäôÞŒSøþ¦$¥'ÏZ^„Îò‡xn¿8³×‹ñ}Èml„sœÙê¬p¸;µgouzq}Öal¸ùùsè£Q"ç€eºEƒ¼·=19LjFscž q7n³Û¥ñø?C"Wr mäéžm!,hŒ,›¶j‘Ò’Ä-½f¼¨KÆLúÅ \›U^m‘pQ7’HÙ†_gÛr iböæÝÑ•Ìd?O ~Á-\š“E‡¹­·ñh!³õÐ~Ž?ìy¸_韱çÏPtºqõÑä;ß’rT[ê ê9Θ“œŠ¹&Z8•èÒOù&$L ­ŠÅ”¯Âøå_¨™ïcÑ ûùShiNdôš ΨÿãµÆ³VLvÒ¨áîY0&ê;:°%- ³ð¼î8àÝÂ}™°x ¤ìš ìjn…øw÷A¦‡Dæ¤yó!”¶º3µÐtfvÆ’- Í@ãüq0ûD Þ8­ÌÎê 1JgaåsžÝŸydØß £Žm!²û!ÕɈé*MeŽEXVÈeˆ:À§.G—C`ÜS˜¹£~iZáÜßzÄ¡æWýÓ„aè> @òSùtÉ´$vóiž—V!öxLÀÇ+—«tUÖn*ÅæÕŸgcÜ/ÔGºæ ä_4Ý3ÖQAf#l÷¨@wYx½<ãVÍaWwo™ö3xq‹ yö+•Õ¹"jöÓëîc¹íÔÉ6!Q€>Ÿ¯ÓŸ÷oñæ¾áÚwŸþ`L¼íÉnÞIÁbj¬ó½9ñ™¢Äl~ˆ’!ù"2sŒqqy…N¾îðC! ‡¬éÖÌŸ˜,5B5þan@5¸2<äæÃæ|‹æ¿–À‘€¹ó³õç ¢Bìꈃ)iEŒóOZÖ¹sXc+ÃÖk·ó%=åYw¡%È*fâ½kV / ߺwà®æ9äŽ{g±WU(m}Òîo"¢ÄÉÁ¶P’ÔÌ¢ó¿ÃÓµFpKa’SW‘“ESÈ·qR±Ù±rÛóy±³OÀÊÔId¢A?öXÇ„†…ì9Ž .*¡ëékôÀ"#îwÜ[hn—Å :lÖôJ4΃#6“Xþ¢CLÅd7¾ú’BRã›ø²Æ@ÕKô¡¾ëÍ´»ƒF©SXÙ΋ì¡6Q-:Ët·îe‡Ó øêitTÿZ*ò,Ö0†íö:Ã^h’†Ì²9+‡½=x‘´—û7ù¸Ã°ÝŒJá»Knz³•)æµ³O*GØ>Ì»²™Žrdõ0lŠ,åþ¾àš,c¹™Œš4Z0èÕpO›&TùTq.ëŒpê`vóoൠ?xtÞ’+¿…¿3  l9lZ=‘%H4àD)¬ ½a¹?8÷ª£à7G—ŸÝ¶cƒ ñv†*{{7æNÁ§šÌºf.Lˆ®ƒªþœ°‰, õ™É|þ¸¡›2,ëæ÷­Þ{ž [殂#Ö.´v]6À2\{†«¼ïþ› Ñí…]Ûò!=¥‚w/ñ^mU"âÛêøv¼AõLÜaÿcnàáàÈü3—uõ•Ð_Šñذî"ôœŸ…K;KQx‡ÛwâÝá¹›=é>„¦Â»@hU9¾=R×6ÅÒ¯sú.ìûü:>å d'­‡¾ 5ÖöJˆ¯¿SĘ[®)]Îç]mÃlŒ^pÝ ¡Â& b\]ØÊ‡›Ñ<07øó¹Ç„qÈ‚GVïªÅ;ê]x`iF}÷åO–8¢]§¯"Êžÿhí¬eäö@ä7%ί…Ù¢wÖìdæv–ª¡À.z—cÅ|Â.ɉ“)¡þR̪«Ã®_ÀãzÎ$“M—jÙf•iärÿôÖ¶.ÐêCV×î„K.3ÙóeÜn÷ Ÿ!@üüfâÉ61âÄÓw´RJ§3rqoC?·ä¨6ûÖ_ÄiXK“_J<ØWd„ëÄc[5­&G4ÂPf–+»ö};ùø;öoñ§Ž÷"ÎâS#]²EV‹ôo9O‡‰Áøu¿hR¿9½Pƒ¨N´!.É2Ù©Ï¡XÌ‘| ˆäOšŽEDƒØì‰a 뉅rÚçÄæË¯"õ èéAîÙmÖûq*TŠÉ2}omâÓ4HXEB× Uò¤t‘ÃÊ17 ùy8DÍYKßo–ÂÇ.¡ÚkvÿÂRÔÚBqÓÁÌR’Ú%¹]•ÿ7oɪK¸óòLâ%`GÚ>ï‡Ïï.›—Q¬þòVScÅÝ0|‹‘^sðÚÜ.*pÛ]¨BâL¤Ø#Mä{l,1êáÃÆ 3 ~·œI¦)‘EÚ™tÑ×(–ôY—ìÃüi»> –RâNžž‚ }«¦3ókÁ¸°Ýtγ­nÊäÅ >9*:€^&ìü§*b>û&ÌÕ~G–4¢H²d¿6ѽ-úœpÛYv{þ9¾QRÌÑtdêÿNƒÉ²ú´eø¤¾¢ßÙ$dHô1&Vž…Á¨(x>ë/¯o¿‘i”d ÙûH˜I(ðog»iã ûwÜК0ý‚9>ÛˆhyåÀñ3Êäð7;òlá ¼ùf*ùzÓÈßx(^6ãXKÈ2ðWó­ÏóYOê-˜P^Iö<>SÒʘíý.֤ǎö¬¢7úsYæ’G¸ýõ¬r½C+RMIÕ}䟌±i0 RFBl#ÕLJOLHÁ!MZêßΩ¦,Äz53x·r<ß8Þßz”ûJ±ìŒÓ ˜ÊÂS%ÉÎôïàzö(¬TwdÙ);`ÅÝ%¤dÁi2møã³‰isEÙeÿ©\'͸-ŒIϽHÎÔï‡êæ¹déžOÈÕ…sÚ.üâoJðÚv +‹:ÄÎý3„"éQm_7FÇù^²çÈÂç³@yð–ã¥3,4È]Ÿ×¸]¡wdè·‡ð–‚k xˆ÷¶’ÔT#1Êýƒd=˜Ö_\¦H¦*­cIs®@ÁÌó°Á¹&ÛÓéî ç— /¤ƒõÁ°ëE¹æÛHÕÄçðeN¶òv½†Úöpz>î´¢5÷bˆÇ/ÜŸaL‚SÙ¡Hviú "šq‚^ÚºÞ¾#8RQ„Š2ûÀð]7-›·žœò2'–ŸøÜzú·¸xáÜýžL!¥“KŽ·«ÿ:f,×b#is™å㎜}‹rÂÄðh#¼®ËÆ >¸rÕtÂ?愎Ò$€¯‚÷æÝ€/Ò†ÈWV «&žBE‹@⣗κë¥9•çC¨I—³…#ìjÊXHóñаqܤ@|´®ÁL¥èË|އÞl š$᪯útÞ¸PpíÎâ±%–/·"Kz'°Òïiít¶°L“Ø÷qšätÝf˜ý`>´,A%§•ðeá~RlÆa_k(ïˆ'±?ƒÕAÖ0›¬>Îz%Úb®ÔÝ”­+E§Ïö ù~'û¾É?pì²ÇpBA(«û3kRÌ÷G2Mgr8¿¤®$à}»zôù›€‚Ï pçƒ6žÅy'Æ“4'*³Ù‘mÅ$KšcTwhLƒÜÉaZ²3m™™ ë!… ~LĪ×'1ÿS4_øù'Žw#oå”·=n$ÿ™ y?fYvè"Ì}¾—©éO#Û6²ƒgŽ1û„)x|Mÿ߉©“h˜òY«mA¢&ƒÛ yÐï.!¶‰ì©ÇÓ­Õä¡Ït6çÔx8•/L.Ȳ5BäqÏ}<ê¡GÎÕOFO}–ÌêäÂî`‹Õ,™sy0;Ô]ÃxÛ[IXu+Hʨ°Þ@Ë-غ¥¹W¦£õì½(1ÆŽð:k¡pÊeŒý+ u5?`¶O&'Š’’5 дxKTÅIè ®)ô.™ºÕ›-Z¡¾ž}šZNè-Iròv {»ß}΀¹¿dÙ .ýÍvÎ)؆ææ[B³¦EðÉZRóS¬w›KC4@éJr{m±Õ0A!¯rLí¾Œ”¤È æ?X`/Ì쟞„ÈVà xw^LŹÝØÚÛU0X7‹dáš3à•6Ì­{+é0C×€rÖ²dÞ’…ì‚¡9ÜߨΫŸ5‡{S„S¥Ñ—?Ri\·PŠfù¯ÁëM{ØÁÚlí-Á úmj)NÜlïBtÍ,\ë9O-/AË?¾Xñw×v×8ço†øð8LöãòÊT-˜ÁË^}ž*Ɇ0‰ßàžú¼ÿ>Âg¶‰DMü®ñõ‚ݳIæ†õlu.;©ü_È>€”:-˜+r Ò'ç¾ÍPÙ7“¬• ÅóÝlëmrÑ0Ž Þ;®ŠËàyõ"ª¬–5ÖJ5Ž™àÎöî(¤«ÂLàj»ÉrœD’¯e@ÿ TU\ˆšõ1d¡­=%m@æ[×’cñu‡*iÿ&GÆ&U¡O5ãüçêb˘Lâ[zŠñ§ôÊ¢ ».©Ê¸ Ï ÆCüÞ¥(ØuÎÅFÉ2U2ÍÒ ›¯ÃßsÛ°%U”‰œ?Rß[¸ž,î™M£eÑ{Æ—ABqß‘¦2oAÏV>Ÿú ì2o`Ìô°ÜÜ|ÎàÖìƒ ïϰ²¥ŸÛÒ0,)U„m ßQåÅ®xAé;XÌå(øáð“ðnÎ#ˆ]UË…DÞ„i]rè1¯ 6¾†tgNcësƒóÅAîþñËœÜ%7&š‡CÇ ù«[úÏžÍ)ÝB„û?xjÓw£I¡5L©$ó¢Îs½>7±-Aƒ¥§4Òg•tÓï>Îs¼Ù¿u€¦¸“ý`K&¬[†GŒÅɯœ±Pè]®Óá’QL½ÿÿê­;‹vv†o{õao›Ï³f±Õ°“Ú³®O ìË¢ä{¶ýädÇ^4©úÎý¶ñ¦IÛ{aOÚ)’d8¼táhgF´^r_O¯Æqzá„ãbÌ «ubp]Å8òåÄ$ž½ì9ðSƒ£ÿ U1Š½Ý¸œ‰Ü>GÝ ¿ÒºÀPzkÅÖD¦’ÒI_ø†É\䔿xàn ÓŠ)ƒ[lx–4)ü¾š‰ì*ƒÌ—Fd÷:”¨÷$ÃÎX¹`S[Þ.µÜL½zšðñ%Îò–â?|“DÄ$ãÁÇ·ˆÜÜõ~\keö¼0¦/ÿ~µP2±y;ªîÓ#—õý‰K×%bÍ:ð¾f.z¼\BÛ63N.Þ•=ØVA7ìÄëØ¹ >gÎ fúd¢Ã &dïGÂŒ}‰Ÿµ9¹°—­S_Ì…~Pcácˆ–{1ù§ÅPÆB„ä.ËAÒÅ N5ý@&ª³…,¸¥Èm´ùsçh±ùµåÐ0…˜¾B'£…ìÉ„Ô áì1`¥—#ÉùŽ(”—ûÁ•Ìã6ö”’sׯ_»O`:†Â¾¤<6Îù=´Œ[N–š>Â'‚²phø×±ÆXŽp«zZqE˜.xðð.­‹"ª :ðžNÉ\ù?ûæÏCÙNñZ\·œñù.°¾ªê[+2«_¹Àà¦>üìQ|³=êO»O=Ì"X×1{Òó;«0†¶¼ÚÉLÅNÀHQ°WÜŒSÐÓ2Ä”‚!äÔENrPrßpÉ}§y"‚sXÅ4 œîðûwèàËrGW쪡Ã2∅¹-QÐ9¿Þy¢âr#v3¨z¡É' ùÇ4@6¸‰?o —Lõ˜ÍÞ‘úaÁyjЇXÚá[4*SV|³"¨3þ¨ãÈ~Ÿñl·}6âõíØg}”À3væKë¡\X¤ÊE+Ù±Ž*®¿a/Ý>wªÝ.c÷ÏM´·±žyÍÇÈ._–yý– Åwû<¸Ùá^öé3$9AŸ¥Õ Û¾ÅìY:÷·w:óæDÙÙ ëùz!Ah¶M‚L±½0Z_‡ø·ó'лÑeà]wôLx¬{Ö½æ-32&ªŠ_§ ˵ã°#Ê{ž`\_4iI €î”xümE ›>Y:D£öq¸à¯6J®“Q[Ë Yj•)\íªÀ­üî μG›Ÿ„˜º®e™BrÄ~x›ÃÛI>éÍד„,ÝJ~NlßRq°Î¶f#bYP`*Ë-ÚCŠôXb˜˜ÿ[Cî,QEã]1á}9ìx"§^‡¢þåå¤o’»V»„!=Šx"î ¿÷"Ù¼gì$ø¼Iðëabÿu3.g‚ÄÂíZ§õp¯+®_{ðæA†º¹ºì†_ôß¹„{,‰ÎÑeÄoj8ÔÆ‚ðVnS™ »5ñ&®á¹á¶E—y«¯¥£w‘72$Ä6è¥Óg²sPÌG…Þ~-n‹ñ&×<5öãE4|ç±%‹QqåVÜÁã0%@‚lôϤ²“ÖÁ«ºõøäõîòNðbõÈͺÃäËàsü—X Õ±OàMZ.ÍXL³4Ó1 R›D&‘A­,TªAßJ2þ;=w!ÕQLgÃyHù‹ìÖ‹:Ž÷E†ZC¨~A7nÖø:ë"º¹_¤sÇž„E!<ÌOs˜z]øjÛŠ¾o¸Ÿ…7€hå@•n&ÌË*§º5Òœ”†û¸k ɺ ærË!ÛCŒX}þïÎé³M™àñ#Ô×?„ºÏH†Uë‡à¿Þf'Ǽ±C=_*3Éþ&º¢±榊0ï„YÄk†˜æ\¢':ưivDç¥(ùýKŠ5>­…g–#Ð~)Ÿ× ÛùäçAv­ú¾_»J­OÇßøøM.¬,‡¯®ÿÇÚdù´—˜HM&ÚSñÙ=xt­ym ©"mbW¥,x³Œò9¡<Ü9þv¹³y/ÀÃ4 c¢ÜÉ>ÙDª3Ëÿr¡ëÈñ† õ”aÕsö3~T£Ea™¢/Fºã©*øOð|Y›¹ ‘0½ñ:(Ú Q½ý±Üý—åtÍÏpPViEONþÊ‚‹tÕËZNüþbð\uüƒÇðkGQZâ;œÚÂ#ûµ#Qÿ‘7­…ÆÏ!ìꢋXbyœ™7þàBÏ}çùCŽ?î#½u3ŸæÑ™ŸÇÃ9<<ˆ“~Ü Ýg{`Û± xGêWs¥ ÷ö¦ÂÕó÷ ééYØ^iŽOþ ²}:špD3ÓË”aÛ+Ëx­[®áŽ%!`oØ&¾@O#àîfn°{pæ%ýY‰ù9Üêú\aÿµª´ï˜¹Í¢W> E‹¡mY!~­}¢?R¸îëÅ´0ù:îÍ|Æ+XÃq_=rñ ›‰ãyçèý¶b{ï6Y 1µfvó•3Q,[ŽùaúØ0KŠA~;5f@vZ´À³SLºs!“\Ügá9£R´±õÊsáC“$yÌbᤪ pÍ çdá<—&PMÇVn‹ l‚ÙóǘÑÄ›øÇéÙD ù eVkñ؉™Ìq½9™ã&DLþ`£Ù­o@ìÖ8²Eê\çÓX4ÿÝyùÎÖÅs2 b{ Ú“qܽPjxÍ –» ±f'¹37bAGwÑáXê‡g »è¹Kz¬b¼®ûyvh“¯3œÈB£ ä€õú+ï†ó8ìùŒ³*Oc®cÿ{_8´éÏqœ Š‘Áƒ•Ùx9¯ž\:ÎÚý„i×ÜÉfÁ·ø³ß7ÇNf­“Èxõ,èóuÆû浜œÕ¶_¦ÎõÏåü–M¤ãzˆ–¬1™`'Iú¿xÒ˜¯cÎ_bÙÂ[±YÇ’‡qٱΠ¬Ї_âAùílOF#"Ÿ¶‘<=‘2Rlxëú3‰,iœC2Gõz“8Õ¥ïUÈâºwœtY8dîåÌ:¶rÚ®g©úë"Ø(p W¹; r„0Cn_7÷±3Ëœ4‰ýžˆ/T‹Ó~ã?‡{xEw:ö?­£wn†àÚ1²,âÝÞÐ:ö¾ìÖ¨n[‚¿úd™éÒÀkå~ðr·8“€Wìiª§,ÙûëíGIÖkü'ü^À¹yÙ㟰ó0%ç†IgâNÕ<ÐEæÄúRëillöœ«1-Y4 »ó/§‚vÊ;^ ÛuÖl{¬9§ÇLÞPlKZŒü’ó|9²!Á›ÜÓfí§ðES"m8¥ÍJÆUâüS‚äÑôDÜvç6üü ßåõÿÜ&¾f]àV}‚Ú»¹‰V\Ê­R˜àuµ là̦KÔ¿7…垆(ò«‡0É®ã-QÃ!Í«ƒÆMQgµ>MtVì4¿SÍÿþâ.q[ÏMV¹CwË´9NÊ`˜Í½§Â‹Os;÷åÃÙ~CÔÄ&pÜ#…æ"CÔçë"2ÿK)—?守sÅ1øãf;àûÓÙŸÉ Ñ Í‡ütÏýQ#ôÊŸo(zâ>•[2*ž`Îûl”Í]´x‡‡ÎÄ¡m·&ˆ,HÍ|#²ñ² (ÒÃÓ!ç¡bI4ܶN%Ÿ‹˜ê¢†7Ƴ‰³ÐoÊ7j˜©Á/ïÇPÁÍð«®ׯˆ÷J ò÷ŸÛš[N}ƒ„`é|¶]Øwºý„t™+¨Ô©ç;Ω¤FDTË„Pv#œhŽ/€gwöQñ^ ›Å£ÍÏrqú3/<°’ƒÙéæ°vÝ'êïydt}…Ü•ùŸ8jÅ §üFË f? …=¹x]a%IÐrB*ÏDÿª€}×+þù§¥\ãGsòCãåQ—bÙJp©},ùH êì|T^1Mæ(å/Ïzâ?Ð9–2¤zÆ(Ìø[W°‡«Ô¹³rD‰»ˆMp­Ù'ø-ñ‹Îw×ÕÜ–Çs@Ò}?¤_߉Áéb¤iÓ,fœ} (—sf«k©[Š ÑZŒËf(’¾YØË_“ŠÞÓ® ¥)‡3˜¶…Šõ¸OrO±ÇÒš†=Cz]@ó€&Ú©)Kž•<‚0ÿ7`49›¦¯¿\zý(fæCÂ6[ˆÀ]^Ÿ…¡Õd\–!^ñøÆ]™,EÚyŸ¸éx¿IiÙÞ¯üs5®j£Æ²väkmFÛ LŒS‰‡ò«G '%R•èQ‹l¼‘Ô ¿º‡a¦Èí8x–«’oÄu's¸m2x¬º€ŽJÑz‡(Ë-*ç>YLOíQ¶üðê5%†«8ñ?½®„[Û¿ac£ä™ÛpSöìã/T®fþ€‡J§ðíJynS1ÿëMe}”ª-@©K纳`¯Äá.lá*³yäÛ z±!¸*ÐX‹Ö2­«Úä–u·$©ö¨~ÁCž·`ê–ƒ8«~üI‹[[-Ø5³V¢ÃãI¬ªvÄA¹ë Þ¯Š¤ó.¯g§eÙšÈNP±~:J/àXS$ø%§ðùäY± {.ÃS6ŠÌõxy½8ûàVÎÈÇGëîq®Ÿ‘_÷{aÀ^ƒã¯ÎÔ•s®>¦g# p›ó"Z3WÈ‚O ˆŽIe« FùU®ko}¦·²9ƒìSôw\6›=«ˆŽïðÁ¼\<Êñá|rj càGKbzáûø@‡IÔЂÃÊìMÞ|&žWD8zýE3ޝßÅK‰Ïß;Hߪ’ȇŸ€÷0>¥:2Û*sHèå=¦/³¶@ë]!ò{¥;±Z¹<.@éRK'H—ÉŸ†yÔý-ì~6 –Ô>¦kÄÙ®•ù0ýÝ8ÆíZD­‹Š¹÷ncY•òpz,G$÷žÄ¥%¤ÿÊüâ]j0Dp“æ#|ozÖ%k­M@fI9­þ«.vä·Ÿ^îêÅè<˜œŽ³ÓêøN†YP6äȽ¥Ÿ¹â J Ý5|pÒd#pYV-ÎOÈäüóÕÈ7ÕD^ZÊ5Ü–!Î=þ‚R ¿¡gñ½;ó4ÞÉèA³¤Ÿ“@í¿^,¹˜ ïJé~¡:°Ch×ÑéïðGóG »¼Yî1 §J»|ÿqq×7@ï˜Ã }ÂdÎA󕥸¡ª††=´áGÿ耼 uQ®;±óWç§Ì*ºèÇO½3ñ[ð—ËE}Qƒ• bxû€"É=ûB®ýåìwwáŸÕvÐV†¿ò_¡Šä9Xsâ »ßBö SŸ5–a§OžF#»c0yÚulnÁ© “I‰þ6Vï¬yšÉ°8C„ ´‰ ðC ¦ [þFÚln1ÏâPÕ5?Â{´!mÁ,q^€Œ³…ìwtøŒ˜z‘:øéò\kØßá7ŸŽ;¤‡g~Ù¶EL^y=78ñ7¢”-í«˜Ë°ms÷¡é"ª§‹@Cÿ-tŽ^Ã7Aò0XÿupºÐëÑ;|žQÎ5g3} I·v‚ûøG¤Pê Úî\Êî¼ò$û²ŽƒðÃ|"—È8ŸÛG¿Æ¥0iTc>\0“|ŸÁCI1)¾ßÓøãÊ%Tï 2}žÄ+³Š"OãÌ55ð¬Õ ôÅxìêž ßîHŽX}ÄK Äë‰ lz ÀLw70“?lÐç-F0`ÞßàæJg²è´  1óŒßX{CžŒ;ÉfwÙá­×áýV²ä¿' )GÉÕÇLÎ|¯òG9í~°ž=î´†â™vp:—¾J’¥kÝ¢û¢†×¬@ךŤ1øL58Ê,w);Ô%Ú“|‰¸‚8Ûrn.·ínûwø(|ÿº§ƒ¼m!\ÛîÍ©07sºþÁ7òK;‹þ/¥ÀÛy! ‡M“×1JdÃUu6û€‰Ô}Ç©¿nÆóƒ’Ä*sym–Ŭ64cÈ«-¨þê‘~Ôgö1ÜÚR òD?‹­wñ‡¢3S¶@pžÈÝ7èB]·îíxS¢"{ŠªmÐ!óæp˜û÷šŠV€ä'Ux›ÓŠ£X¢ýVºŸEK6^¼P!Þý=»2#ŠÉ+ôâ×G#(ßÅ5Mºá9ЫÈ4#ûáÉ‚}¬·©“ëù6 ‡cIëñÚ·œ)Ù¥³ei|üÞ‹ÿ¾‚ˆ©VätÉžq_ìR.Ჿlà/jzÆÍß2Ì›=…k¯H¡“z¶²EÏi}@56Fn`±Í 0Sƈl=bDJ[žòˬap·º^)ïË[¹º$-N+|»sW %â_K%î—+•{ÖAy/“xÅ«#`ëUâ"d‚Ÿæ—¡~boäé Zyæ5WÚ„r¥­ÔF:‡ç¦»“=žZ̲kçm{}TzP]ñÎ8?Ïå]Á€J+¬Ÿr îM˜@Ão®B¥[‡Àüë·2û>'¾·ž]MÄ!â¤'ŒUy‹x °G÷âÀd»7–l¢GÈë‘.kæOîÑ…fŒý“EM$ïs‹7uâ¦â)pkõAÔy.ïJ!´@Ÿ»àqÉëñøìîbš+ÁWÝø€Ë™}~9ï /–tr[wœ‚OoiÔ+Y¨¹±>x'BˆÆ3”Á<üYÉCeΉV_JÄ 7ðÐ$Iöøþaš=ÉýÁ/\„Ѻ“ø¢õ4»o¢}þSHõå¨ë­ã¤Gã»UJ‚wµ€Ÿ³0Šºã‡/@¾Þž]<ò—›¥aˆÛxl@6‰PzCán]| ½_®_eQb&–‡GôªÀZAŒÓ6><; ­ÚtÁ‰EÄØó<Êÿ[† Ê¡ÇR—)/ÅÙÍŽ¥rÆœ¾œðTä/’ƒÎîaû5Áá%ñ`ß\O¢zéÑç HXyMÛð2ß¶Âá ø_o²Û?ÌI)‡ÂêR]°mØÇÛܧÏz‹ô8õ3‰›2ÖMµùŸÛâ 'uâïN.íßOZmoñ~EE×C¤{å2´ï$ê“ÙÐkŸ‹çv°saò$6w.ItÁm×.“÷“êW)u£ô.c8dÁ’ÍAŸÍ²=ùYï0g¾†Ì½õ°u¡ÌPcblQº¢u ê$UÀj“ßÔGþÿß±&˜·(Õõ? ²’ qw^³ÈÜ4‡xÕ s+­ŽPb¶†~©á߈=CõÜ0ü„ö޹ƒSðn°}¾ŽöEôpv3ë°Ô­ <5Qu³{š|^Yˆ±ñSf³ÚUj¬ÉüìQ'è’·¥TèžÄ”Æ{ií|o«wxôéyô¶óÆð8Ð7‹‚ú^äo”a*3¥ÙßötØýo“Ó±füÓŸ9Û½ŠLâK=­g+ÉŽüq,…¦á†’‹d¬$»ëÛÉš[°å•9ùE™p*Aä¾_8tE'åñÄ/ÝžU˜4ÂÁeEPzAïÅd€ÁCMö};éTl•`%ö>\–«ïúÇûnL[ -ä>á…å<UXœ‘óÏžŽ{«Nó«Ö)°_€WœƒLÊUêË©0ÕŸ?™ÏÎ*|±î ¤ë5£‡Ò0™¿€…ä·W-Ú†b\Ÿqèü™ÍîgÝsNrK©‘ÃØÀ[M'÷Ý‚Ô'yǶì@ß—§Qõw#¤¯į6vDç­$guÞ•‹ÙüiÜl3‘Ôo´ eù­ü‘¥({ÜŽ:*£¢Ü2}nçäÔ,E›ð†Ðpæ?¦þO{#­AüÙèï»›ÜÉÁÔç¶l áæ\Äã“×Õ‹Ï(ÅÄ[Ì_”OëSXÕѱÐõ̘íQ!ßR8÷Ÿñ°j×{ìðÝÀ‚3ÀéS!{O¯±àð¶td‘¨ŸOÆiwp£¼“÷x Óˆ:k™+F&ý¨cÇœ"¡í‰ßω 'ŽYÝ@ÈmvŒí;"àt¶=„4ÜbЭÂöÍV$‹´5˜ÓO5òëó9–T3™´¾ùŠYîÃÂßydW€~,ÄŽÞħ{9Y¬º]Âuªë¡À¦»,1©'Xã›æšQ=»+6»ŠÃ’»‰ø(V})w&ª³Sñì).÷ƒ¨Ó™>Ìz“Î:íyLÒyûu,;7]"¸%™8z ±ö쟷3wf`¼™2‚ûV𠽂YäÅH.òM»xï23 ;ªcìX”ýOjëá»zJX¨ºÑÛŠÙ§Ùs¾6ϸ?G«©±L>k¨Hbe§ɽ€}$êœ6Ûûï:»eаNá j}OCWm7–79šfFÀQ¹1dß¶)`l·ŸT(²©Ðâð½Ó È¥+’ óù¸ìð!0ÕÐÄB2¤{ÆÍËäçŠN†OŸÈÖ)oðFXˆã6ûþYë$—!rà=]ç|W ¾“ÒpñU1æyú#ÇÕ ãê”2>>.áºT·áäé’¤só 6Ëð„yLƒÙ³-HýR"k­Dbý£àV2í©lòôXN󉿋é)ßèEÚLG?Ǜƛy÷NZõÁG\P‚ÞÕGÎûIaÏ(Þ)Fk÷ýlñÕ}L×\ œ8Nç¾·f»»0Wú%Þ>B^— ÔguHƒÆô«øgðÖ½»Nf<ôfO/7pmNó™ÝûTËj…ã“§Ž|1R™;ƒ´<û„%møÎQ ÚÔ* Yû8MÞ«‹…ëÛùÖÞ×±ÄuÓ}Ép¯Ý[þÖoŸÆL3L~ÊÝ ‡Á½W!3xôúÊ0ÇŸƒ¨V?+NÂýÕ+ Þ¡ë6ž ÃrÂ+†°o~<^ µ«‚Hh‡2¨°%KšØž©æô`–)N[c]ζž›bkÆ~––áÙC’Äç³ ãå; cÄ?ú}ò8;5 >¾3`“\—1·˜H¸Õ  ‘º7`]e'«Z…b»û‹»¡Æ¾Šÿô\(k\o˼ÛÞÚàN¿NÝC[¢Jr‚¾à£í€{%ÀîŽê÷®„õóPhù ²®j5wå®-ÓŒ˜„¡qþ¸¥ÿ,H¿Öe}Çβ)eÉœH´Y¼à k77&ÉùË7,A6 ìÁ¸=±è_|“;ã(O$f xÍI¶³1Êå'ð7$`âÍ26-z%{o"Ã*-và߃û£î_ÉqŠ -.=à6-—?ØÁ¤a~º1dU‹"›ð¶6Ø>qËËÌÄ2ŒYÎ-ÀïU‚lü§zöÞB²æ†âGº¦¹Ž/ó·÷ÄZâîÈrè|sõÅœ–XGàÏGd°æÛúÕ N>LCôϱ²{fÑ^ŒË§çsC³3q¿j³v÷&Â=ýlªç.´n?8k:†(³y-ˆükY#@>~`yû¬v†–Â]ÿcèÞ zfß0ôX\Å$²‚ÈG.%ük¡ìäPw(y9îyví{ׯÏ•íd¶Bi}·l&”¥)qÍ4à÷T\I4ØÔu>lŠû®#4‚þœÿ~ A´}gHóÉ\U€° ˜ôØ–M¥GçgÃ`ûV !Sð¯T9Íé»Ïú´ubä¶ý!ˆõy1<¶?ó‘þŵ’ÏIVÓû%×@ 5ƒû%ZÏ‘Úé\l¶2´*Œg.¶âìGÒ8äfPÃçâ¬áùmk³$wDÒÉÖçÏØÄ9òø¸%o¼ÝH$¤ÎÓ £ßèJW=v;¹Ó|UZöuYqiÐÁbA$K΄ÄÁ#aÆ;¿å©èå«¡]lñ§hºË=NÅ_…S¹ö¤r¡{5ë)øíͤ‹Ã+apê"fé ˆ¹_N£Ûر$ó{Ê™]Àc/²Ø‰¶aÌœÕÆ·®ayY tÕ"£v8ë Þd[›´i“w ¿Â«yÍœaó7îSø^V.óN´K±Û=ÊìCA&n•Iƒ…—]É–÷ÖõŽ9qÂOÝ«m×øqÛLàÖ7ó¹²;QXúf%\»·s²¸¸íwùsž‚¸PavËå<…¤û ß„”œz O·ƒ›¢¢Ì¤L˜ÄGÿ ¢g[yéž·èæôù£5<>…>‡ãºÄd †vi%c†ÝK´‰ ¢Ë°|?9RgvçÄb„`™6æ<ŒéxH‹¶_„M‡F8ÉÎ…œH^‹ú~ºŽU ëKOv}›:ôv o.ý†«åÈ<˜ùn>+ˆ9÷ ã1óâS,”­Iµà½;šÖj²5'ê¹2Gj¥Ä¸:Nì÷åî¤E‚Á¹2£ l“úijg•KäYqòZr]ê)Y®ÏTDTÙ–M›àÔruvÙbŠS-¹©C¬‚+à]Ô8Š2a³*¨Ö¶¿œ²ø2vC ª~~NãU𰇧}JŸ78߆ܼ}„“o‹‡ëÇcîK®=@7*dSuk‡ÿláîÙBôv”ypAŽÅßx—gƒÓ?r±vÊ0T®Ã§<šïÂÑî(:oߨòüÅ36'Óƒ¯@½úÂkϦI}yCg,Ø@e!K_e—ƒ@]Ÿ5ªü‹¶Íøáºà¶:M-£¢ãäKÚubÒäAúºu‚ [4s%Ž%³±Ùô/ˆW`Oú\¿G^nÎ#«>K±º“ÑØÖÑDËMwcð¨î_ý¾”X¬É›°Pðé±eÉ5Pl6›]<VÑämÅ.ÜåüŠv¾¤õv÷ æ”5¹q!‘)e"Mg1¾]ô=Ô…ç"ìz¡«x >û/%uQÂäB¤9º9´²ÌcŽ,ö÷ZŒIùMò§˜løm¶Hâ¼Ê ÙWðK©˜;?k+”ÝJvÔHòû©§ò2¢c.H®mñÀiÁµÌÂ{'=XÒ­˜U—˜½€详^ãIóJ§f~æº Р`1y74éÙÀé³ÍɵŒ%ôFìš÷2¯Ÿ ¼²®@6õ±X4Ƒȹw¹î¿kYµt"æ6\Áaíe4iº#©t‚ñ+!ÈÚÕÆÏ_;dÇù°¡„¦Ðs‹Ê\Û‚#Ó-ïm¨5Ùäã‡vj-iÁ=KámÖìÏC<÷ø—åÔÊMê4„þÏPHTíÒö·z^[ˆŸÛ·Rç 8¿\˜7§ ·› áàvø‚ǘ—g -×=CÜw ü8Þ]þ¼ÅFyz*’9Çé‘åBdÎøÇ°ÿï(òq$ƒÓŵìÕqû21p¨[‰Ÿ6áM6¤=M]La%pÝ^+’ñó<Î:ØÌ©¬–þƒ¦ ¬Ï ç…îb[toJ±)Ë °ðôúÚóÖç ƒØ¸8nC†6™4V¼ï‚¶ACv̾Üëö‰„A®êW-· ïÖÄéš$],cˆ*yo¸ü.R”X;š«hþN¼y¾ ‡Îj™ù— ðÜa+"ƒ`GüÇèÒu¢ª,㣠ž®¼/ÛùަP7oò°Nß8£HâGèt²ÄM¹MÜŠWQxÂØ&ßÑbIm‰Ffžr‰ÁmVÀ{t ö>t'Á ƒØ¾s¬ËÖ%¢¥0Éh8 1¿¶xwî5VÜĈºìŒæeþðÑ ¸zí„JÎ OS­`Š¿ Z2 œâJ’z-¸b‡Xòêë!¼²x á{ñŒcTÙì_¸ð‘>.$_pšÏ:Ñ<û ÖÃú.Q–7%—Œ<ãB"UH]Ðoˆ7¹ÆyØ…CjèTœüõ4·ø{Žï¹uµ¹v>Ié+©åÒÿU“ËW¥­‹,ðΦLLËöaFîµäŠï´Yħ%òîøÍ¥…=dÙôŒGüºéɨÒú5ZL`†(gíðÏõ>üiûij›!ÍÎ=XHn$…ó€§zgúš0ÛwPÉí®ô©<RTžI>½qdÇâpqö?îzâ.¾hž6úñèøã˜Û{6tÁÝ˹p+XÌÿó wfz³Ÿ.wpz° Ð]Iô³µIèc H "Ü^ºî,rõÞY[çþF“]úÄf©=±™ë–S™qL Gýø°ÛÚþ¤Þ¦ ó®,¾á»iå¥{X+HðC9/#¢ ^}äè±-—ËRØ#|³~åˆ ŒÏ\I¶¸¼Ì\@U½R©ÿ·#t©—F|Ë‹Ûk¸žPâôÂT7Ïc§*¦Ó##m¼ZG ¾Pç{lý(ö”Î"™‘¯ -K‹8?™@|+?pÁ—ùpc¤ º2ˆã½T\*ò“÷Ó'EÌÃà†DnÌóþžodª“1Z¾¹îR¶ðwF#õÞ›Ìö[ìbYõ \°À!0KÇ0ésJœ}@2—Ÿ¢À&çï¦ó\ÄØÔLì 6&iš8ÙÏ“·å¬Õ9˜yð/ l²BÉ8)\æ{š»tà3lï úÞßpÄÝ&¨÷óSâ‘S3&$I9¨ÈÙ>°#§>bÏ‘rè²YN;Š ¨©¹Î,HÛƒ±œÅ-1"}׉9ì'ÏŒK±ûx'¿^E“«ß£·ídY’Fw ð,oÊó||ë• Ú6G¸>Iœ™sVÁï”PØ}; Ý×?ÆÆôç:kØ4‘”Î'>ÅIÓÑ[x;[±û ôOˆ#B/˜èP"¶ˆKÓpöo¯[{w{³î-Toœ‡j¶rPºR‡Älÿޱ»yð÷v5×ö™Òi€bI4E;¥/Mã/åôHûù#øâË9Î&V˜),„%ä+—@›#¿@oöNP’’ƃ^Ghˆ` χò éÆâ4x±8Fü¾Ð¾ÔÏ@’올‡~|]˜¬ÛÌ4úJpîîõÄÍ$}Ϭ éQ;ù{mÁ%s<¾‹Áì#‰ÌõVtÛÌÆû§v“ÙɰØ&ãÇŸvlö=ËÎE?‘Ìk”çT¸@n쯀ΙEX±…dL[À\Å/aøÅë3+¥¡Â³ˆaü[x_×ÊøÙ?$I‡y$6m;…—¶N#Ck•Iÿøf–ÂL™}‚¬¸Aµß@|q:Öé(‘{ÃR$òC*†}ùÁiîìÃÄŸQpc¼&x›ÏÙ½õ‹ÃUÉ9¾=)Ÿ#ÁD·dÂÁƒ-P¨›ÆU·{séÖp‹Iü)Æ_¶Þ‡œÓb¶§á0ùù,œN€¬ä<"9SV]w¼­ŠÞê0R#˜œö*‚Ï„·q6z8ðÌ’MÙÍ×åxø)¡þ™<êô©_È(q«w&àS ŸÈêŠ+pÕš_xÐ[Þj¹£¡è aM.ë_À9ãfàÊoKùë–äÒ¤¿}t‚o%>=± ï]*­¥üG‘TYG‹ì$`É>x§TŽŽžìsè{ùýñY»gË”!MqMð1ëö#³ó\LÞ·iÂË:Ò]¢‰¸±Îsñy<…Y•ü—>b¤%©Šs·= nGvv‡³‘Þ΄ËãÖ,\û?èÁN32’›„÷բ裠þI‘hG_â½} :ÏX€Ùz²%J7«q¢cÏsÓsƒÛŽ(vSMÇ%óZ[;8¹ëhíø7½U`_äDÙd‡o >£¶†Œe9åÙË€l*£ž¸¶âÊ·W1㈵ÑÛŠ«ÇnÑä~T®´eÐk!D‚æ1Óóœëq´Y\è ZõJ‹]ÁÔs.ß­I^yŸ†¿s•À]º$˜A§Îx¢–¥ à1cQf7–õ Bׯ¼UÛÁ‰©yáÍÂSpçàä5Ét\½0œ¬ò‚Ö“Lp¯ ðÞò Œ”Î`NQ“QèÌ}üˆï=á”IüZ6‚Ï AõQ„âPûI—S&ì5ô¨‘¡ÝÍð!K7LbsªW¢Îá·œÜÆ3 Ú²’IP|.ÈÆgx°_¡^¬•§Ån~ȇ9 IœíÔ]aÇV[WÒÃcÏÓ3¾äêlꀄ¿ä× øÛ5ŸÐ¹ªŸáݯ·í—WŸÐaÌÒ4ß`È¿‡ôÐtIv|[ ?”‚ì‰lð9X"©E2Âì…âdÅ6-ìÙŧµxÇ©Eb2ê nð¯äùRÓX|½è,깟¢ÖÏÞòîç´âá…©œÏ1»Ø¼%ãÏBC_¢ÞåplûÃ÷EÉŽ0áüBn°Ð“¯%lpƒ lxÝsêtÙ¡‚jnç;sVj0´µøÓ(µp¿´š<<| »eÉà”x¼‘ásÁ4™Yä¡…9©ç9ç–WÜzñ./® #ï= /ω`””88ÇaEŽýýN—¶âÑ—¤±¶ž܉bÂk„7Ýà:! ¿Õ-ŹM3®në˜B7­õŸ·I¨ü£‹”%¾ÎÁ}Ý›ôåûj”ªCþº‹Ñ˜Vû¿=k¦2“ûF&Œ [äVê0e<VŸÁ7ñ ´kí\ëHbÕÐ~_ÌÊ« æ oÐxbŽŽq‹ŽAŠÒ]š¤ëÉlÆÒÃRïhɫà ùr#ð'ÄŸ¨ñzÛߊ }ÒDJ¶_È)’¥±r4P‡C•W“ÉåàÒU…ý.©T¶ë&õ®O—FÚ$sï7*ñÅy·é19sª&A¼'”â¯ÁÁZ,´û[˜ ý/¹YÍ]tÓ³ý¸sÿx.ÞAŠHf´aåµdŒÊ8ìÏ²ß ²¶»2¤ñƒÓ›=ˆ ’Â0þO7úJ’L53z|^:ªöŠц‹è·%µŸ=³¡sx¶qsµÌ‡N[URì·+lpÅEL:5‹w±O•íJpáLÛÆ@”´9¥#+XiKçñb'8Ídü.p©nÃÄŽ±¸Öäˆ'4aÍ÷BîïbE€šoXT‚ƒ÷tÁÆî3ºG_ƒ,þZ_¦FI÷xN,Zפ¤¹ ÑZî»^>Ò—v´—vÞ˜o®jÈ…1ܬRwk´ƒ®Á¹ðQé §eQÃ{}ìÒ3qnH¼¿¨À¶MPÃÚÑ:ï2Y‹»yßäÿÖÃOý:uØŒœÛ›·¦GÁÅø«œïaÚ¨~Õß–6»CpÚ ¼¨ï k¾q 4Bpx÷-Œ¿Y‰_îjÁ¯˜/PXºƒ­Z¦Íœµ¤_/ s–cK9ñak–ðžG‚åâ!Æp€JB‡õtìަy"£±£4 ‡7¦‘Æ:o<—xœyà+šÏçŒç ‘isÙk窑iJ¢†&²÷ýØý÷xúšÐ:#‰] Å A[˜j«O¬C¢ÉÙîxkyjJ@üì—°ãÞSþð-möbs.µ8«ÇÎ?帢añm;÷S³«ò‡ê-ªL1F†{ ëÊFëµú]˜™_W?\†Ïºº¦8ƒüLJ\Ð@Bs2H4j¡þ+–}í!»¤p¥ üY5v H&ÐÁExëì(KÅŸE-¼ŠùJäО—¸÷ž5Z÷~ƒ Ç6¾Ø,Y²Vòp¦¹+ÍþBlÜ}1Æåˆ²Ä–[ØëööýZÅ[3rç‰VÄý÷0µØGÈó»71|I9ÿ¶Œh¬™Î:ÒùªÉ½¼ÿÇÑuÇõø}ñö^Ò´K{—Æç9W‰ŠÒBEÈ–ÚK¥E[ÒPIik}žs YÉ*›ì‘™Â¯ïï¿çu?ç¹÷<÷9÷œ÷ûõz?çsÁm3äªãµ»‰´•+*d ‚~ºp"yqþåEžÇÊAýëGo{œÓÁãJž>í…gSU‰w ¹¯¥J:ת¢GT¾Õz7§ @¿D4[ÿÙ˜¼”ŽÅtÓ@ é»Ã߸ñÞû‘£_åZX»ÖlŽº‡/ °û×3 ×Ô0¢}¸y73{BÁom+Zjý‚¶¥z´üŽ‚Í÷QϾ|ž±s&ÎM1XÖª=0õˆ9}›" Óòùg9Ü| +uŸ3Gí§b£E måê1»SÎcÄÒ>jîfCTÿqERvà©U*³4ªRûwÕ`üÊ>µ1ù‡g¡¦s*5½Š Á°­¹ Onð#Ø@㆒ӆšp¾¡ˆSÅ`¯Ë#Ü ´Äý4HÏNcìM»é„f:Ü ñc*Ãë‚4|¶òÊäÃÇ^k’5¸†|xØ3‡ÖÀaQiÒ0.Aðf.“ùbs.0Ü„í`ÈׇúƒIPÝùY–ˆ]ˆåìÿ­MNyÃS³|êèg%¸àËý ò62WJ0jZ4n>ÈËä}Nñ¢ÖcD˜Ý$}ŸZ Ÿ™}e½VxC˜¿<½~IœÚ¾ü ³NÅá2­£0íí†üeʬø©‘Ì&ÑSšæO _îúàH£)›ƒ›Î71¿Œš .ÝœzÇ4‚S* TƒÇž3PzÞ„Þž›Ptš¹”SB©Œ9»Œº/q~úEžžëÇö€,ÌëT¤{µòÅ`NŽ3O¥èŒEÊt ã1ñã#™—ž³ ×kÙÿþ;TóÎÜÍoÂnôõ›\ƒOq4®S’d„&bçß`<ÿ5Žñ ³ÂŸÂ 3ú%Ûsr*UÐP‡KÚ%Ìsý—¸ÑGW•¼EÁq)´W™AÇG ˆ÷Û™ôó*qÚ1#³…ÿ°#UO ~_!Üg¸#éÑ`ÞY„r_²–N›qoª+8‘‡ŸÚ£àÔì$Ò>íR¸Íj䯾(;`hI^ÌyŠJ'蘶5ùZ‚šä,HTóS¹§Ïpô…1ª¤ uÉÜ“u±xkýUWe\ùòá|à(t`IwwU(¬o2„áéОÂ^]´}2~|é»é×pSû ´Ó3#K§i’±|öVd;õ¸”½û‹:}|—Ÿ æï'>’Ÿ˜†Ÿ™ÌÇœ³Øuw Yê6‹üœ@{#QRÁI¡²®eÄÛõœV˜¡‹s‰¹†­:ÁËe´aûÇ“hVŒËV}‚zEs,I¨E×´U¨©t¾ÞQ§\ï&ÐìÙŠI9à$‹K®D2OTaÍÂ#ðÎ=“Z¨ÙÓ6—lL¼<õ. Ãl«Z²yI¸DÔžÐöø~&{CVá ¬Q¢Å‘P÷(B¿|aħ°pÿÏvªh2€?N2ðý@&4f.cŽž`N¹:ï"ÞlÂo*¤¬žCöüÓÁüÞÇ`ù%Œ„ú¬ÆmáTIÊ^e’ƒ¸if8wz ¨}w!.Z£K7RÂ÷ÌZðä5%å¥7™ð"žØÀx·…CâH ÞØ+Amü¼&kÉRyUÚøÍŽh™-À8gòºÐî-„) mðÅ#ò¶>ö‰ ð~Ñ%ù3ê‘ûfÙ¿i&þ<;‚¯çIm#ª¶[ÿ¸¸²^í¼Ð5õ2˜nu÷ë÷Pºy¸Ûr]0¶eªÐè]+€ògÀÊÔ&úßÊËoAyòäÐZ! ?Ü.åìÝã†÷ž'ÓßÎåÔã³ùh׿½1Ý·Í)wÖy|¹£ ÌÛ³è¶;ÞLÂb-ªtÿ2äqh T,ÿj âyâÔ»ºž®”Êîˆw$ë<;鍸J0wħãj¤T±Þî¨EL}l‘…öó£i[C(¡Õùto!ìY·š[úº‰æNseãXÝà)tÆœDŒHéF‹ŸXÿ]ž=YkÓIàN¾I¬<…HI{²¼~ãì’ÖäýòGè5‚ñÁ ðcO&¹åJo‰ü`ït¶°jÒ¤ûwUðëc:ù®°õwu©å „;7nà5ífö’d?3g¯­q¢çù¦ÀöØt;¶®NâÍ=g°‹®¦Mx[¯úp‹Iêk^¢”—‚)Wëèó}Îäêšxˆ_Mäµö“#¡N4R'||a¼ƒ±ÿÀ>ó/n=Û+7lg;‡òY·is)è€Øß¿˜ÇÁkaœBs³ŠàÙç\<Ÿ$«¦ÁÍ™’çHÝþŠ?“pþ’QĈ|ð^ÎØÝÔ&~;=iß"9²@*†Ê´eàQ׽앟ò¯ ;¾ºÂrůX`ç‹Qßá«øc¯>dìæL„BØÇ0"oöU‰4bÚ€‰3ˆnGÅ$·g”s°?…p÷¾€ô`è¾ R¢ñ›Û^ÈhŸIuŠ0ì÷4_ß'ó ë7ù›ük¡Pk j4éÓááEÝŸÒòÑÙuLì`7(40A_‚]‡æù:‘wéž§éÃVúšàÝŠ]L`¹ùR;Ê F³×Oâ×w·™ž‹ü´äÎJTMn‚+'Ï3…ï^s/Zö€æw|±—I¢Ä²i@¿\ ‹.ƒûã0|ñÔrà¤v\c''ò¥1ÄÑZž, :Ô¸ `ÉžIÞøI˜¨¯”Çæ)—a&ØÐLÉ#7PZ· Dh¦Ÿ.f9•AE·qßJ~F‰Ò3忘9­SÈ´™KðÇY)rµïmÁ_×spøÑ<\/ûNÈÊbê†sŒªŽ;ýpbÙv·ÎYã¼.»¦kœ=Û€#“¸Âo‡ºnwÇmvŒÉ¾>vÁ³bHWŒÿL#Ê×ÓhÞüÓŒ…‡ ÑéƒÐ惠"9œñç2G+Ù½ŸWÂ?ÕP(|‡·çî&ü“175”oý]€%¿7Òg_¡wk9*-IeZ+y1s‰ í Ï€þϺäw´<óÕsòš‘å Ñè»’‡ê_‘«1^ôŽNf®rdéôUOáÈVÔ‰¡š«™÷…Jð8›ÄçÍìîÓXËV~¯íqpwd ôZ¯#….Ãì0O#vÿ¬„Ÿ¨BzÆÐc˜—,ìʆ†éõ(¼æ.³Ýü$º8ƒ&VP8û+ÌMôuûÞµW˜%·X1àlšäySKñÞô[ðT>³Ðz3$Ì=C7+Pu¾,ôh‰ Íõ:lßIf8‰Ù=á(ß½œ²Þwá#9Mþ æG²íàJ:?|„¥‹Àñ·ÚXó~6aP•[\Ò v¿ áå}ÜÒ/H=T8[Â}mØÊˆû_›‰^Ï7Ôµâ=çùúæF>A_—!L¿¢‚fQ¹°å@!Õ¼(„B§Q!P‘YnœŒöo›hiä6²ïQØ2ÊÞYë@ÆõÅþ¾%’ó_প+dDznFa·éeÓhîʹtNM"Ôäù“)O¦ÂÔµ‰(wâ&SÅÒòƒ·¨à~_êâöu¨6±¾×ÓiÉžRZu‡á©túütVäÁ¨ùUfÔô#òíÏ!Ë>1Xûi1¼˜ä9Ο•AœZuèѪ´ºgý7`N¤?£òßuøáÖgœÞ…UÝ.LdÍL\½~Ýôu x-'»c±?mqp)~ü+…G‡kQ¦²ŠÝW«Hß¶{Á×YÅ`·7b¶\$}äwÆl:j &ãÅÀ»¾Œ|úíEf m¯WÄtj*¸ê®¥wÅb¤y ‰þ>f²¦ÇW“­†¹`»Žƒ–‚D;I‹~÷YLœJ^£^ýÎÀý*×û™ýáH…gx÷M#ÌšØY¤NR„N½˜£“qòXè|úô‘ — /W³qI¸8ÀwDûá¡iäܱç˜ér’ŲߖœEM~jÓº‹Î¯Îƒþ¡zT(ü‰1nIhpš—vòÑro>·h‡o³k`æˆ=/p îÌ&¼£]lóíøD§›©:¬Bîõýez¬¡aÑ:ü›2Æù~ ÙCvwmÉÁËïá®­a\i?‚ŠÑ üTœ–>9ŒòáÃLKîlÙQ AtŸÇ.09Õ[}ör?>µ¤ä½ñý/¹-!M½Fô±kÙ˜ß»Š¨õRï?a Ò…ïÔµ™…­ñÌß½)lR@#Ž¥Hã«m=øm{.ý<¦Gç¹·£çs”#1‹EBû¿W¬7ïÚ¼Â6|¿„£åï™§÷“Ûב¼s/9Õz(6·¦Å._Á{ëU°Ö€…Wnbå˜ ¿Ç—ØÁJ³Û°dßMÜ-ÌBE:-›I¯hÚ‘ÌÎÜ>hƒö­•Ä#¸ Ä;1ÍO™LéÔ©˜ä—¸§“ ±ýÒV"}mÊŸµeÆ€kËrö°6Pm5t•°&¶‹Âºé¸Ÿ;äÈ5GE”u³¥RáC9{|-úW•`žÀ°ý$M»ö˜ÐÇ\=ZPè Æ¢)ÌN…QvæÉ¹È›Âe\]â!ä†6(m; oU^áÑk€_k#+Mî Áïyl©Ä.RØ¥DW5¦’­F«iÁÞ]´ï:C·»õ³I_fPŒO‡ñÏ8Stw‘çKŒçÄt’ÜgKS¬C »3—n;,GgïbnšKÓû¯ã²Ú5¸0[ˆ:«þf +–ÁºänxkX‡êê鱫þ˜È/ϧ›ãÖ¬ïiÑÎflÓ$8¬IsÓfƒéS²ñ V»‘òƒéôе:ÛדližA¶Ä(…HºúAþóõ§¯”ðÍ47Z,–ôÅb”Ž&©/îžAý‰ »OI•œÕœÀ+ý`ùô4Äšæá”ÌB¢aä€ÊEò¼Êp ˆDbi>Ñ •‚Ù´oX6"—¯D28ßpÉ™­ôç¿ïàî¯jÔ¬w‡ýJsÏš“LÈ“óØ^×…z—…ÈìT[äy]¾siȲ÷LÚ¼dÔ_Mfog•T<‰ØßJj|Ÿ‡VE-¦f÷ éÙ½D¡4‘Øh KîÍá{KÁ&ÝâBeðkNühE?B3»Xö3Ù¿±ž ,Ð |“8ðîÅpUˆ¢ua–¨—ûîŸ-dÿNæuÚ‰½ Àz“8¼Äœp6w’Ùì,ÉZÐåä±Y‡ŸÃ£Õ[@T±Šù6,Š»>é?Õ ¸<ÉÏÔ–°A¼/™SŽ–1F¸ ³\Ó#h÷"b%¬à—eå¡AÐ d#ñáÃM$è¾1û" ™/9+®R‡íÁa0ì~ˆ1±*fÑ6t?ð¦{}ÂpZ~5<÷[H¶3qšö¤;1x\œEÓ^Ç¡p‹Õ2ÝÊø7Gk¡FŸŸÊæìó!S>Tâ–Ähz$9ý§þ¯ÕIÙëðÖ^–Ô¼îw^ `MwÊÈÓP ÁžÞpbø¥êǧÁý)ê`x!œÎI6 þùNd×Sê>z’WMÇ(ï!FlÀDt†A„› î Ÿ€ »« *çƒÎp‰1$δA5ûÉÚfjO–¥;Ñçó“5A (5‰¥»÷¤A¹W˜‰¤Á’ùAÄ$±ϘKa ߢëá¶ÐQ¼0ðŸûlÇԨSçp:±‡ÖÉ7'ňî9nC&p§û«-Í b©£¡=µH‡.–'½'Í&ëh/#¹Þ€Â3¸¾Åž­€}.rϘã•=Á(ÖàDÕIÁáS"¸k ¼© 'wœ¡v… [rf+…ÝYßø½P å­‡ÞÀ¶ýkróîËW˜Lóf®ÁõGœ]E3©u`"ê¯Î@Ùôå಻‹˜öþÚÈÐ!­ý ¸ 5>⻕ÜÎøW8C*¸û¸iºhÓcô+Ó¡÷{…È*ƒÜ—)çþÈM ¥x¸¾Šs¯„Ÿ|}¾ÊbvÃk÷cèЉ#á}ÿ4T '¶rÑÆÖ ž¤­E'·"2'¡ ;£ÞǰÞkA¬bÎ8Š.AÉÌɯ-däÏW®Ì7y"°q*…™ÁTn;ËÜÙ³‚½xõ<çºú]L”O†¡9A`zñ߃²ú;Ì¡/©äë$Ö8ne€­Ý´ö„(aWÍewz®¢Ï Š òhÌD&ó¼\O¦´à€p –÷"û}0ým®€œ3ã>7ÀÄ}aت؆|ÄqþÍN8¿ê(v<ˆanT>Ç·ÞeÐ=‰K>¯/d/ßþÇ*V‘ÇÏ×iW€Vm8Ì•Ï^OL7ëáüÕzè¹'ŽÞl g(*ÑÓ g|¾±y™»™—/`[¨? ïëÄi)ªíÁkTCiëÙ™¬¶çn|5ÿ5¨+…3ušÀUH¡—é5Ä‘eP#nÆE_`­Cÿ°+¦2#y‘¬¶èbÊÕû!‹×cú4²9µνÕÂXGkº7ÚmŸG¿'ÒÕ ©h8Љ.DŽ= b@Þ)Á+ÂæÕS .]‹saL²UÈA‘õÔÖéÎèáÁzQ(+«/AËÈbάŸõ“™!Tˬúu¹¾°¢²ž-Ú ÇînÇÙ«öCDT L| þw$ÉÁ"IúÇ+‰¸¦]À/­Ø}6øÊ0¾”VP‹ƒà)!|Ô¤ dãà~ø8Z z~s¸3þIã{#rêg.sc)8cÑ•eÄuN$÷¬ÁòÂ(غC–*ß—õÈÍ…ÃØœ§ Ê÷ÁŸ÷'NÙ£H&L¿áóˆs05£†î+Âñ5ø¢è=š>/gnß…Œ/ÂT1'Fk¹ÜØÂ‡I‘ç»?¯g`WÁ)ÔÙ~““çX W86mئ ŠmxüÒnp˜ù÷÷ë‚ÿy´^9]þaçŽc²:œwŸe W `Eà ˜=OÆE߀A½:>¿¬n¿À4\Ia£Ó.ÃáÛδÝí$JMGã8}Ã1Zá †Nðúþ<ø´±rÖҥ׌©ÏmeÐ:n„Ÿ÷ûñϨôIí*FÒJ ߹Ρ±ãµ\'„Ûç¥Lù÷*˜#Ч(Ó-ýáAmÿT„¢_ûÙ©Zÿ¡;ƘrY¼­éõ¸æÂôq¸ážˆ[ÕêÀ^-÷x)QO­+ ¯o‡@ùïwîéõ†Tûq 3õ– n‹±ÃôIÇ~Ò1Ëz="ððþ‹8W" Oe:Ñ€âÚI~œ@?f|‚_ýpæÙD¬is$Ã\E²®Áí+B@ìÏ%œ2C{D˜$~IøOßìà·Ž¨¶»±I!qTKû~áL§k´‘wÕT?+Gà ˆªŸ#ñýYuç°çà&üñd!f/;=}#ë›ò<ùž¹ø—cê#A#PœÒÚ*üÓ»6oâ\)à…¥š¹0eÞpËîƒ ¸Á𶉱Ó9VTuf lÌm‚g‡Zϩ㻰J80kž–PE›{Ûh²5?m\úÙy´­­Z’™‰™0׸Ýyïp)4ÛààQsùá6Žv£‰j žóÒÆŽ}–WÀ2R7˜µ[;pHù¤´^eÓ«jصõ`B_çÝi„[éÏðFösÌR¸3àÚyR‘­›Ç×s.µlcÔ;ÕXë¶T™Zè߇Üð üƒ”~u§Íš»z™Kê0¯=¬»Æëþ%'釿j8#vÅùÔ ÐY² }›¢`ž„—‹_CìúÁîkykÙõ„µøU1yîmHSâîM¼‚‡N>a¦HHk].¹Ÿ~ ªe˜£ò¯Pè¶([”ʳ³Éþ5Z~8F¥௷ÆtPçþŽDó²äcáC¶6ô/kÆÿ“õ(~…*.ÒÌâc"ÄRé Š·ÅÁêÅ-Åù;H½“ñPÜÇÔUä Ì²X½ó'>ôGZ| aí×O ²²Ša÷ôÀ’¼„îw–{qßÊÞü'ëõéO»k ¾ŸøøS”¶w$CÑÌj·ß06ï&ã÷¹º X+/Y&³ù-DN^³Å ÉQæ?ÍôìvŽÉ‘V¨klƒ"jBÅPœŽ#Ïá:'?÷F`jézlb¡Ó„IÝ&4öÌLb&tž”E…¥.ðøX$J>¿Áøð5᪋èÃóœ¥Îã1Z*¹Œv]§v¡g™Ñ—\<•àôöE>|Æ4س%Ñ?u¨Îzeó—ÂBÇNÚ‘&EÞ–Ã9§ÎîD¯(‘ª‰^ÎE;ék80χV1»*±>Éòç>=‹LÈÁWRdiùR#’Ë}_ô˜Ó Œ‰ÿª0÷¹×÷Æ?n7QoAœSÃï[É nNcø“bðqS<54T˜<û¡Lã†ì?‚®–ý°sî{Ú¸.´V÷Å ü¯K×Þ~ˆŸðÜ”ûÌÄ17PßÏ…rÙœN¡>¨³8…~Uî°(;ZÔ“à™ö(Ç?P‰<1Þ†û/ÈP‹Õ•éPÃJõÂÛzÐm4¡ãì±}.}«HC·Ÿe†W/Ç™ž*$ŽË4²sxÆ8i¹G~¸ùi˜°˜N žÂø¼ZvÙe-(:Å¿J°w« ½ß¾ÖŸÅ۵s'pýo.„]Ó¡^óÔ˜ñüݰŠ>ezÒϱ—·«SIƒ$âU Vu¯™ä¶J8Ûú´?ÖÀd¹CÎûxH÷<¥,h«õÄ";¯Zcãvìã.Âhyƒ@aQvs¤F¯²i ¢>ä_ÝÏxc*mÌÃÐA ] }Ï?a.ße\yž—þ5&#ç¬IˆY ÷âYqº'¿•-úv‰Ý”ÖŽ[Zìá¢?­ØÍ‡w-\;r„Ã=–Æò z¡[…K–§™WtÈž¼TÜ—'Gê,H’}2fæÁyåõ$•/ê68RÃidŸ@1¬­ †9©kçÌüNq 3pTsL¸ÎoW¶ÿS'ÎüâDÖø6±ÊG­Øoê‰ÚõMðßü¹’tç–4`•ÌHW‡|zûgò“‘÷eh4ã&1–ø"†±‡Ü‹s鉭™8ï…4šqqè“´G„³å¢Â¤Zŀ܌Cá xÚ|‚•i(Á1…‡x?Ep_GO»2sÂéÇowé>\Ÿ ÒeÕÐÔ‚œYãM‹§¨Áù:hÂ÷rÓ•! Bb bü!ÞR;M=M°ä§/'åë"²bÉz0hý ʦ3!Öy„¨ Aþc"¤÷†I•å…?4é¡u7QøÄe4¸Ól™ÎlqßÇ;æLøs¬öe¬Ò…Åžjøì½$m:¹†åª¹!ÿa0y.ò­ÚTËÄİ1‘x#ó‘O„ÊX}Ø=ú¨û¹³19ÌOïl¾ŠÿJÚ˜¶]~ô¢þS0ÏHÆÝZ¼8é I©Bö»*-а#–—þÒ-¦]ôrEóäýmúd6ðã•ÎP–¤r¨û¬ÓpëR)gúÙKlàµdÜö¬ü/Wùž,;Ò$^éÝÓ³X þfxæw £y±(è7œ6^@^rº­¿äâ[8_Êð¿&í«úÇ‘öO=JKëÀw1còþ(ÌP™Ä¿Ï‘ý aî•(òÒê&/þÏGî¸æ~Øü¹[Îýb†¯¿€®];ÈC™øm4ŒÚ*$ÀÇœ”Ë wÍ?£€Q§¾ã¶k÷؆¿Yày;­óàÐ@.î9¶ οÚLŒ®šâ‘–]õ=.ÐKb1pÂŽ†]N‚GÁÕ8âù˜Q*˜JÅ|2`·—µxH©ÊÕ@vmÇò]hä=ˆ—7gÐ*e1º¹¾ "’³Hï4ìNéSmÿÍÍ=›¾Œë ÈÛoÔÉì‚“Xâüé$xøëžø$J¼æ]€“)¸z›2Ë Âß>N'ËLýIBüæíü[0{8Ij$œy¤…K‚óàqÈØîÉGo²“÷á¾ÿpʪ'°u2_mÛ)Iþ…™äê«`_`5ì ~ÄE¡øã<¢1¯ÎÿÔ¦³¯e@V€²R-poš=õ¤Ÿ®|ƒNÃø©) î}û˼OIçY‚Ävn?{kœáøh“¨3q(¡(M4ŽJ±ƒÛka÷ÓE 羆/ °’æ³ÿ+Y܃©…Ýñ³ÁÖRk2sÙ%x(þ;Ô _yÄ÷ݰ_P”ÜÞ¨LæÌ¤3ö·âõìV,zfD¦à fö›•ü2œ¼&WfÏ *·Ø•É|d‡˜á¦Fó¼2¸PÈ]—x¼?¦bûRQ²û yÿÉŽ4jöRäPûVNg.ñ¥M±7˜UbDÉæt*“q‰b6Óãšìcï„#ãhŸù†ÕZ Ž /ØÉy wË ößëlКÉZgYã É“¯º° $Š=üžñ¯­…*A0·ÉaN8#ÿ¶`¿>潩Áà¥\¬~ñ}¶ìÇò9 !øh/.ªÅÒ¡»ìñ?ªhg)LæËü…¦ù§‘Dp¿ùêer×—`|Ž‹ÖÓ5+{±ë£6™ùî#cÑ”æ<§[>>`ÌÎÃ#ܰ®X{\Wݽa"X·ë$;þVËž³Õ-¦ìƒþ“Ø› O Â’h&“óOSýÒH*1À «1Ãþm 1·O Àª²Ðó!«ËŸoÙÎ=&‡£›ÿ⟗YL°•9˻ľ¹t w†Òn˜ÕKÌ~áRžvxê°„”¨ÀÕvóðDÈEªº·?wÛÓ­ + 7C‰pDfSëßa Fœ4  Vm!´u©=[¹!@tð‘åO@|ÅaT6}†Ö©óét»ƒÔ²õW²ì ü«ø oÜôÁ)Z•¸ðä0~‹éu­vÞdy±‚ /èÆ—þÁÝ‹ilÎR2­FŒ(†¼DáK?¨DÔ3©Myªvw#ïèeFðoêÐ"v_…zð©“ò#<ô±æb´«|óÿub—Öâ%Þðo;?|_6þâ|ã(ò:OÖåè)ƒÒ7®Œïrßãvºaƒ!/y—hG Ëï3òáüôz™ ªjÅí·+ÈžSÓ¨å|œi.ÙG±ÀP…þáa{/YÀÕ K¸ýã'zü*ÃZl8£,²m'h¾HDóˆWŒÆo%Î♦l › éH>ízŠA5YxëgÍ$§IU»:6‘Ør°IŒÚ˜™pÿÓ6'>]ÀQ½; d.í!9)be”L~m¨†¸“%(¨çIÿ³OÔ³¢ZÙ³è¿æGxüÏ!Ï–A÷ÖÇxj±.]7!H¬²Æ˜uÓOÁþ¯²æšdª)pÁÈÂxçÙUt˜{_Ї…bdþ` w–/ðäù£ª)#¦5†roqŠ€ù7£š]k÷«œ¦Aús+0Zò†Ó2̤Y9ÓQ7s^CÒîoGžÍ²2³#'£Äìn®Âo€kS×ÀÑ=vøÇ2øyyü§ ®”Uƒ%΃̿G ¶ƒ'*GàµòTš¾º–I¬\ÁÈÎч£W=°E% —FƒåÒ­$j0_*¡úÕ“Œâ.|½p‰e»ÓÙ_×#s>jÃ>ÔÄç—Á_Öž$WrÑÚ銧¡õ¯ÍPq!FïHÑ»ë(ÊÕ è¹†Ã¦ùJÄn#¢.>ä>…ûà±þ J^W%q)rìöjRü$ ^½‹c®©)Óõ!wð°¶ŒÏÒÀv½°Ìôó§#€¾®Ð"«Ú²™B…p;ÅšDÃ/Ý<ÿì,¦›b3˜o¸Ò WÑ9|ʬã~o:5q.}âj‹^¦|tMÄ_æÿ7pèZG¬¯ÌËOMÆíx4EÎð_'n³Èѹó¨û^cô*=‹y޳Ãj^ðjú1b¡B¾ÖÓõÇSé+ùEdíôM¸¥Äìq&%;…é“Nìö¢ÖËý$n]»”FÜÎð,²ÄXãû8T?*EW“=âÙPîÑ §lE ¿î³`Æ!æð™%àXÅyÝ™Þi§m&Óñß3z'<…|õoA®Ãfa 1êÕ§}ˤѱ¹™mס#òÏqS¢=ñ¥†¶_8JT”KYÉïpçøÂ÷Å™ü½d„‡ÖÊU…AÄ!cÌ¹Ü Bæ%D*édÕ‡‘ êF›Î’œž+ðA4|Ö©Ñ<ç9Y»Š^Ûp:“Båý4´É ´Žÿ ìg•Y9k6;ÌÞÒø6Àæõw­æÖç /ipÏ‚"'.ĸ%1vŸë`YiÌÚ*A¥¸dièÖã–8]Ö"ªæ´Æå ü»£AÊçV3µ³<‰Z‹'nÿ݆µræD§OžÉ»HÈr°wP.-NÃï§gÒû×divx.iÕ…CÇÍáÃÍ8ÇʉþÝ+FœÄ§ãRÕb´tO$¦ƒ5 ?¨Jä /Ñc#9¸¶-…žêP†Èö`Z9ÇŠÜSÒƒM»ìIg0HÆ|…_N`þqr¿[™JÛ\¿ã·Ð³‰Ÿrìrà£Ã?x’" 6KÇàú¯ë2hA†g&ìò6‡áoì]ÄÁßÖ‰ÿé›ÉȰ-êñ$Ó(ÿ øæX†îÿ.Àm™#leŒ=<« ÔÇ^¡Ý¦4vX*¶úoe&¦=ÉmÔŸ—T‹Á²Sþ4µO7ïòÁu×(µ¼Ï:ï1‡ØÀIînÕ?§×€ùê¨SÅ×:tÁû·ìPƒÏü£ÈVÆ5ê£ CÇÁ£ç<^H 3Â8;LhÐý+04%S.*’J‹x|áût£`Y÷u ÛƒWFr`gÇ&"n$ˆ·âyˆú> ï<{A`vž¥6œöù?`P¥ <^Í ~,™%èF­lÁðwÂÔ[Ο¸ )‘÷ÅáÙ®clÛ9%j›äH-ÃF&ñ3®†§Ÿœx÷~G- ’[œF-uepå–.8wt;¡BG§¿IžÔ+$DŠ,³ðÐc5š™%Í,T_O5ý8à:»=Qà ;÷£Þd•³z•(—'E» ûÊC ;Ë·–íÇÍ¿7mcºa§9Qÿß̃àÙ“-ÔéâÍÿôØœq:E.œ|°·  Õjàtþqj˜³˜ŽV7·#Uð&3›œc èxI&~²ýÀ5¾[@„÷(MJTxÔ‰&7Ý¢ÿºÃê¾W¤w ™–m{LTJæàüŒÕÄ>d=9¥\¾O1xùž $µBæ‚Yä`‰1½à:?.ÙдK „{^`–v·]BŸDcçkòsø©N"5Ȉ­#ýëãÇÄ•Þòä‰D3T|Õ]›uœF~OÌ}€+·ÞÃ9÷!UÎ=£™ a›åhâÉ]dǾg8½MœŒÝ4ÆiÆ\¶¾(ƒŠñ¥¡Õ 3è."KgÊ“©b¯ÀÂkËw¤u ˆŠÒ^BþÜcgóþ„ŒEÙd–¸ m³Š¢ÓNŸá<[L½Ï…âî+ÃŒÔÆÔ‘Õ"k*üéÈ&\~ѳ6g\˜JþxÃ#=`q€Ck!ÿ þI”ÁÚ×@W¿‚5xÃá–­“9lýò]T¯†p?F¯âøw  [Û ý6/7sƒiøöŒ‘;v‡â…h|ïfšÝ^ÿ鬣ocûiºúo'LÛ¦µÓ݈…öœÿ}>ÙuÕ¹âEím4iejg†÷M“¶#=j8¹&wÙéJf–Ò-ܪä¬ö¡'Ñöý:>ÊO¹ìè“Ýd]… =¹x!V¥Ó‘Ã>À³Ô”LÚw/“œeÉ?1à¶:™%Nï„Õ0ž—ñUJ[+t„:fk! z¹Ï•%¶RL÷'ä7ãÕëñS´5óÒ|~î?‚J[Þ³Ïù©XL.¼ešEÜ`Žýœÿq÷?Ùð"aœuÈšB†²ŸÃë×øvågœu²ÊõW11½úäTиiœ Oƒ†æÌµQüu1˜ ×»ÄáÙ[Š'+w×Âd`äº)’Ū 1{ICîÉÁ¦$gŒ¿Í²Q—÷²’Ë­™±-±0,§‡:ðþš½\&Eó „pvå6”Q $Ÿ/'0’Wràæ‹KlKãTÚ,ÔeÎ\’˜È\½-AÄvÝcU%5ñ‹ÐGv—k:vœÔì³ì}÷• |O oiI’¦çpp ZòÒ‡3£@ò¬¹2z \Þ1úk½QøOm;¦CWŠ‘1ž½°Bô8Ýj÷Ï^ÂÌÚ¹Ö–ÎnWc^Ž˜ IQƧ͕ª&ös¢ª7µ 2ä× KøW®A¢ž2NæÆhØe‹oæÑ‚è7p¬oºà¬ÒV®6 sGfµãZÜÚÑäcò{öjxò™¦Cî3¯¡ìà"9í4wj²í Ž°Ï •‰‘ÊX¶L ÂåüHÖ˜ uõ´:%›¬ ô“I×››>V“¸»«ñî­XdXCgŽq~†Ñ*%vszî$ÞÞ=_.YD~š 2òéôó@?øÛñÒ]ÆÓifv?e-$ïy 19ÀŸ>PõËòôøœyôì9Iš©C k'í¯ùËów>›Ä{»Bèñh/2-±³ç+1~T W‰ËR«{Žh/<ŠÝžw0GY†{GÝ™¾“÷ÚòXúšFWeˆnÊ Ôþ°˜8µH௳hþþvÂýp‚¶=¤s…#X •HœîCòçÍ¥z昳Q„lü|›#ux.f“‰ŽÊ,ð]fI+åÂAÂhÙRÑÿûœ»`§y3¿Õ'íãéI™|˜*[H˜«O n;‡Ääl%ZÒQäüæSäì¶6²áe+‚rý4Fê‘I*,GÅYÐ9ʇÚ[ñÏ“2Tõ^n P€øÙ0*z ÷MÈÑ oçØÂ„f>ë}]ß$„2­ïnEFu¸Êyoõ—qðëíК+On™™â‰-ðXüצ£éQ}}®Œ|°°¦OÂÇ 4˜73T §lÚžû •%>p›B¤I©dë}º¹x¾ü&•Øn@–&B]Ëw䈊уò©ä÷YKîý1óì„_Qä– Ë”;„£Pá2d®0­r #ñÕgºÂ¥·wQ¦Èϧi8ŒÍi¡ùÔì¨6f z?³íùè3Iöÿ6ÅBX5û\÷îwv4ÛEŠzÌöd4Òž¢èø'h™F¨å³ë^\Ç£fäeèù.\Âs–ÝpÉ„ª^t§mxÉj÷¥ì³•n æ;›Þ¯1!G×Î!›f_c9ß pÈ{ aOφÁ*MêÔ†:?­Ì¡±Qxeúb¸ä¡CÍÔ£Vµ:À3“Jï ;V5²OyïZ£;«úªŽ9®¶ó¿žÕÜ#iѤdãwÈ 1øàÍFOð4ã¿¢¶l@›Àioýå\RÙ†OUƒpƒf(ìkÁÏëSàý=ón» ZkiÏeà<ƒüHŠ¡BÂüÄ¥ýpB7Ão‹)0Å>‰ðÍýÇ‘ ØA}í…éýE+mÚ-𜊯SÓ¾F˜u‚ÖÏã!a¥Ï0ÿ óm­4#ﺔõ°\AnZªR¡“qÔA*–Fyá³ßŽô·¿.깂õ=ÉäëqŽŸD…ÉÃ7i_òb…%³Ká0nOÉÕæE¸õ@:c~}Êβ¤…HúºÃôFœ¹0ÆÒ»éó¨2ú®®¥Ú<¶8áØ‰/Xüud”ŸBC£HïB>šõ'  kIâ°!{¾íº6 ⩬agO"DÞÕ#¿s'ùî]+šqæÜḣoÎS ÏÌ, îetŸ]f^Üå²cG?cæx*jA£í4'«ë:§Þ‹†…Ãâ`“cˆ‡vxÐ@m5Î;ãHñ‘¢bþÛè¦POº.†¹–PBY-"6òO˜¤U¤Ú9‘½Ô}[5óáç” àÓGûz£0sþ|SN“6ûs•O®'סÉþÝ$wçA›3ŸÑç`!r×T2aƒoØÿ4Ï'÷µ¤þ¦ ¡ØÂlØÖ F9g)'°X†Yof“Ôè\AY‡¬Ç0ÃÈš.Á+¥xfs>é5 ¢koÿc”¶¸“¼Õ¯­Èç€xjãDƒ}9Iîhhë„û–ËÇ¢4}ý*xËzÁŸñ>ÎEzãÃLùbÒ¡Bg“ÏîÖ¸\a®ÏiÁ¾õNtõÁ&LwÛ sÙëÀzí¤I²K ÿê}¸’fO–,g-‚¡/BãÏàô¼áÚ‡Ã\5rÀA>oPc»ÿ]ÅØ@ ¢·)žHwM#³„=qÉ–cà!ÅKÍf+­Äd*Ø&AÌt%³¦Á†y;Yïg¸0Ñ ô3sóîkðXЖ}^‰«åÿSš-¤MC¥¦Ò¾ýQ°gß3X£±²{ý" ´N¨€ÈÂVÖÉ* ã>>CÈ”!×÷Í$1…1ÄaÄ ]Öôb×ì·P\§”ÖP›*iêM èhÂqv>ß\ZåÌVŸLÅ‹•û™ŠùqøgÆLöñwMœöût~yÅ®}?륲kމ1krd„ƒ¼WÑ¿*â°üó4|÷v2¿téï™lGÚr¢É‡³2h,öu{ AÌœ©Àzþ²—öÀCÝý¸¾) —NÌ'ú"òäåvFqÎvfõõå1«¶]â¡Å~ s§>½}—F\ÌDéCO¡ÑFŽ®bøéš]ð&ºŒž™ SnkÃâÑðhÞÀþ+"WVÖ3%Ûü¡ÕÅ”Èeãî¶$l;Ó…VÎwiÛnxq åïàL?zG ¶XîÅQ.wPì6–§ÅbÚi=Tüþ–c÷þµ‚˜ù¦ðp±ýž=ñëˆÞ?^jILâuIZãõ¥¦yÒ€®dÐíçaص¤šè’Ý®jp úû%0m¥žÛjaÑ'Æ NtÃÚ½[i¥…J̶fÜ˸Ði–‰Íoá÷O?Yž윓Ž×SÅQ=Õ䊧“¹«Î2‚6½p)å<˜ÇsœV¨)²ë¬¥9²Wý_c\uíð>ܾrhå×ü%·ýŒÛÆfàf¬G7ËièAÅéÔºËL¢asãp JªâÀUyêÊo Oµ…i©Å7fÍêjü JÍÆŽ`”—|¶Ïm3ŽucožQ"ž÷¬¨n,=H§áŠÙëaáæ&|áÚÌUx>UuI)ó°üÃùÔ:§aOª-mmÒ„íŠw™é…‡ Òe „;€7UÍ„—NÉÎGù–ÂírâµeõÍõ…Ñ"wXÔTżýt +2·Âkÿ˜ª«IgsmH]j)§iúbbòuÞó°MS—Ðëþmø®ß‡*«øãO×í”÷/q^mB’fö2csÉô6y୺Ʊ¼wœ1Óçt+O¥©äuûTÒ0á Ài;ÎyhCl ýI¡N9¼zÇO5¬íè:å}D¯ì3\(ØÏ [ÏaîöîeŸªù’:MGfaÕ Fx×gøvD‰”gšSYEaŒD˜•»XL*7EÓö\*öE›¬j‚Y…m0³.…4­ ¹÷Wñ¯áî¥:˜é=9\˜( &Š;úPZ²þZ,¦×d£AKׂŠNôâo"àdžÆt iöèÇϳ™‚˜­)O>­Ð&oîO%©¿¸—ÿ.ÆcgÜ ²I‹>T;Îl • ßâȃ®f0«ç¥M±Ûi‚íK0M¥QÔÄ … ÀÉFu<ÉרðŸÆÚ¶æ,Ð’@’.,9™ÓƒÈ6'CÈUþÂþU7wá—ÅMØ·ðýx- ƒ“ˆ¦G6­<g^;3“œ?'ñ®ï¼÷˜gÒ5¬Ïí|89ÉÝCÆbPp ä¯Q…áûÁVxíþ¡É晉À~Ì„!S> Ï y`Ó³#ø·B*iUrFWAå» èMª£ÓP{{1æÊæ€ð²›ã;Ñç¨ Qòx õã+10!G`ÔÊ>—Èf—›Z7á§äâNÖ¥»Ÿ+‘yéÌÑ¿0üw jmØ@¶.‹¤áTwI¬Ó‚EŽÌšÊlýQê=UNDxrú6ŸøOMdìJðü?#øÕ  É9Ùèá¿Öy:Ó1#1ÚöH“uN?ŠwµiI‚-é^ëÃìžaa³ÏàŽf=z9¯ŒcáXÙ1ôüèƒUqë¡TtCî¤âïÙa<$™µª³Ý74¹»:sÆXQw³î¢[A°ê1”NoÃÈãZ°¶áœ[Ð5U@â¿4±J9]l¨ã|û^šù¾¼ n5&áÜöÒ‘Zô¬æ‡ï·ÙQÍ\Øm ÷w'³Öö%*-XI|¿gAï“HëG©Ù2$fňÙ'F[fRŽù²­8uá?FBdÛ1»‡fhHÒ¦ø&×qôã!b÷ØÇ™÷ÙÛӔ范¼P?–Ny‡žÂ! 'Ô¹˜ÖsÁ‚¤!“½åeÉœ¢$Ò}l Þô›Pv{°8¦Üä8fWc1àß«Ã7kÒf÷=xÐ]ˆfÇ®¾y ¬_#H7ߤsSº0>ÚÓêð kÖÍdwÅÁ;Õ¨(Þ‚ûgúaÛž`Öè ž(ad—º€»,çoŒÄLçð‘w9‚-°äjÜþjÀ™ºŸ#])°TD7>˸P…mǘýoE°'u5»ï^J‘¡+·²ð}¥ýÝû"¿ÂQsðÖu&öÍlù׳ø²F³—qoßEX>I„9 !¬s ù7&ÁL5pgTsïs;ŸGi5:kº1®‹]ÎgA½LøÈ¸jÊKD_Ù–èP¾÷<{eW Mw1©êM8÷Z z–L¥•†éÝœ +°bÿ@ Ól2â5Àª-ZD¸Y2!Ëg’¾’rÐ×r§¥¿ØÓwn ´*±O“.Q‚ê©Ì“2qzøû<âsý04ÅRÓeï÷ªxcoËú}¿DfoŠÃe·f“ û3‰z¸u,SGÂà§}3‰ê0Æç H®É“éLï³ F<ª ÄÀη1±ÔJtðÇÙìÊóÐk6éÉ’ÃŒ&¹¾Ø “.EBuh6ËìbìˆÖqx°”âv¸Sû$s:ý(ˆ·°Ç´ˆÕÞŸìâOY°nÇgö†ô1H¹’ ž6|dæp ìØTÀ:s*!tó]Ø”oB‡WZ2nrà_Ðêb½ 2p“ý^r–1úü‡³S[¼»ÏpEç’Á´fOx?øÍ ƒÿtÏoåDÑ {Hi,c=Èáa “yÏlÜyÿ†,$éëP)bx?¢¦Ý…¢†c¨0ë6:mÆ#…˜ãâÜú4•¹+ª@ö6¼fm·FஈަŽ~o‚åöäÝDfÏT¡áýApmÁ=ôrƒb÷!x¸Öøá×—–äaÉM€#‹h²Ãc®ÙQ5z&£ ¾ÿ>‰OrÞ1«N{‘Ì.+®;ZóZ°z/Œ|´¤3cã ç¦ Éjo‚9/ß3›C޳!<,lÑý‹?Ϧ/›0çl!sÈ«ƒ¿)¨Æ[ *%!þ–¨¨™çc»»-ˆpµ$=° luÏ@ÂÃXLq‘§5cñ§½qò]Fbˆ›{º‰›;,˜Æ÷˜_ €ëèCv}ob¦Š}C S ”YFÖ¾Ãï¿ØÒïmø­¹ dóÞ Õâ¸úL–Ê«×2Ë>ªá4ž8RV_NZùÈ`k"ãÛ‰?_qrSÆá~õqx©yŒQþñƒ±y‘¾ÛùÉäŠý°e—û~ç𜢧cšt÷Œâ3Ìœ?¡oƒ?™5' {{áH±vñq¡ê–Ù¦?€ ¤þ@ªõnÚ·1— ù¼M=”Äú{³^*“×zgà×_U4.; kBBg‡;}7°ýo³ýs ;× r/ÅBqó|p ~Í&¼g§«'ÀÇj"ÜvõtâÙ:kš™ùÊ‘™²H†WEÐÉZËò¼ñÀº=ÂôóêS(37Gžc7œa]/ËÓ~4\Í˹r\<À¹ÒÊÃlÿåtÜoßû-a¢)ª×0­Q˜ŽPDFÐaFdwá:—8®®çÐT ƒÜLIÚi-’¾ô4ü©ó‚¬–CðÇúê粋,ÃphÉIpÖa4•ưò8»¶Ñ¥w¿9o]ú©Í¤Òf÷q5 Ží…Î9 /,ŽGÄâãI®ct‡îùÖ»œaMÖ x Jü¸ÓØvUÂ߇ð(O(9aD&zÜáüJ.(.ºÈ45¼‡•©ž4ãäUVAC×l×!v³™‰Á£È¿O“yÚñB¿[0Jk$!™õ }RÕÄg¶4üÆ#¤08›rgMÆÇš$)w&©Õ‚õ~(Æu$-Þoàë zmдÍoe-_Ü„-W™+zÎBö”!ب] ®¼þô‡³"z_˜¼gL˜&¯>Ž+,¶Ð ׫jå¿S !ôt;~­ÜJì0ûñ›$¬ÝÍC?Ô…ÃØA<¤<ȪoßCþð 1?kï"çN%mkö#’üb¤X÷æzjæO–'=áCù·ž$O3#°ËW…¬˜jAʆ» (KH¶VÎ(Â'eHâ벩Ü!ê6k[5¨¤Ù¼³šê7F‘ëgßá׉7ÇäÐX‰ 4Þ©SÌ͘ûzáÏÍ:Úg+J]»ê耯éQ3ƒ>3 Œ; J÷­ÓÁý¾ÕdTz=¹Ç©£_ƒX~h1½üö,áMD“ܘò‹lÇ=ÿX0âsEÓ{CLãviÊ(‹Så{YÌ]¿µ„ûôî[W«SÕ©Ô 8¡oD_ÖÛ›óP)PŒžŒß&9ÜvS‰ÌuµÄg/¥ì1¥#äw£:ÅT^@“<{^Ç}òCŸ©°×ƒzÑ/6~´ƒ1¹ EÃéœËA —¸‚½(£È~U*¡×ã‰Ã]šÙªd»9Ûõh*;c‘2^møÃþ–f‡ÂO±]1zäÈoz§®”«-ô”k(£@B~Ä‘MÔ^~>•¤Ý*[鄘éy]?'à‚ºÖyÞVVµƒý¯³µœ.ñi:×>Û0ry‹ñêöãì^# ÑlDsÓi¸Nn:Ö«¥ÛÚ=ìüã‡ñÜ® ökÇ8‘'M¦pûÉ#üô\šNŽÁ»7f´†2îÑ’%ßóÉ´½yÐ9UˆØ3Sa´©›q5«ˆ_æö£ãNÚ_†Æ<ÎC|›½•¹¾o*hÞÍÁ)‰ZpÿyÏ…Œñ«ð25 Ïl«YÍfMIf¤–ÄÃ¥†„Ö¢ÎKmª¯ð×XÔOâr¾ªÑ‘•/Aop:]×| î—ÄL¥Pæû‰ñõ ð|³-=5Ç«„ðÇÁvÿ“qø´Ù¼KÿõgÆÎÀ&âðâ:6Ö>f¶oêÇñƒƒpx!?}ðIšjõî†ó•Ãl *1‰êƒÃRD¢"˜9“¯€qgȺ'3p0ü5ãÿ>:ildTtíÁòV4iŒ€wi:Nœ{½ñ}Ìôù»0Î=/l¦Å•ÅÜyk{Q51M}"ØÞøEXÜ_Dú»¯²_—ëÒ-*BôÆOc¬Ú˜Eý`ùõ%ô±êGæÌ$Tµ AM¡HœPÏÆïó´‰ø‚ °»#OËC< µÐ¢[ÆPxßuÔßÃC*K•qÕÈ3ø}f1Y07È‘‘[Ì’+‘Ý/0J¥…ô"¿² å7‡ƒ‰‘ÕE¼_ï»~´áØåS(t|iM}ʱ¿5Y%Ai2k»?‰õ#ñóuÈ¿|‚¿§>e”^&cVa5j– 3)¦_ ™¯d8ÅDÝd7ËùÀPyQm°Ý![Xƒ)å°I+ ·õ8+s²óg˜Á?ŸNðáçA­·Ä‚68Ü…41ú)Q.¼3Ccðï6yôo’<ÎHNO죅lµÃQÈ •!O­!+C¦ÓÿzH(ŽÂ‹îéÌ âî„‘4ñxÆr©; ]fDé"ïŒοô _/„5}j E$ à à3l«hfΰ]0êÛ‹† f¥m#Á.ŸPáã<ÈvÛzô÷MÙ{”Üÿ_v‚êÀw,zèIíÇwã¥Po”/{ÌþÈÏâþ'OæŸÑ£R|Ùä+]Ì®¾ËúVŒ9¸ˆ®{q›fÑxÍwèz+Î]òªè 7ó÷ê#?…±IO§gÛ9 úoÙBÑO(jÏŽ- ï½s:;?0wï/§¹O2ñÞVuf_ç4š šÈì?ï‰Må–¯¹žiûž˯-&ëê_²ï#Éaêq䜹rФM«d÷š9Qó.šy8™ŒŒ¤JJøÚá8‘£/5ré‹Uù´oq êåпcùiNïI(ÑsŸÔéÓ…IL^Ë©5¥sŒf™¤Ï ¯J¢ƒ§ÃWî4<÷sD,rÆ´Ê‹ð¡èÓë+B ‹Ãöó±¬LD6µóœ %ëÁiÙ!ÊsÄ‚n±OE·iËáïúåäT‰-‡NâhNè>øu{2ænëB˜⇤8Š,¬B‰ /øÅrœÙÙ„Ý¥ÿ0R§Œ+t  0y1>†_àîìçÌ©’VöT ÁÔDì3UEú³¼X©S«l©Ý”sX²]†ár;³÷1œ®&Ñ{õˆËþ§0³+“|¯šC˜Æ,4®PF9/J·ž]Âü}÷cãùÈ«3Ü0zˆúÝ=@ãm©nÜKŽõ\q>p#äN“ ü¶6 ®ÎK¤ŸçÁ¡–-4*å/§âÞEÐvZšÜšº‡27* æëT^b1^{ɇizàâp òlÆrµh"ì-l^,LÛÞÑ¢ÛN™ºið“oçEé‘m\:?$‰èÌÄ{‡µIGNÀ¤Ï#lçÅ4Tßò.~__jG™ÓOAbã ÊW_ 'd¤¨ýè]ŽÄªHæÙ_Y|þØÇ’ªñdúuéd?‘ãRAåæ œi2}ME¶àE^—:Æœõ͇/‹£˜òkià÷¯ Þ­—àV¼‚4•FŽaß-6'¦>ÓõSöÿ¿Çó¡ywàÕ‰/°ù†ù¹Fï6% –¹ L½ÂƒIü”§xFG…úØ6A°Ì °—R«í]\ó;ÖðÆdù©ÍÐKŽY̹{ϘM‡XTX!AåRMÁÔg>îÜ}úZcàñ-9U*ˆAé “\ǃÉWE¨ÐÕJfG…qðk‡ïç̈§“%Nwî|Ëzµœ ?m€ßm'ø¬P#ûÙ™´@G”ü 2%I:ÖØ¸ ~ÙiÑMË•(ÏòŒû,D‹í2 ±¿çÒ⻓Øhº(ýdªøÇq÷ìr¸ñ€¼Ò{ÍÂUoSç€à²î-Ü Ñ ÇeâOŸÃ®4-zp£¹œÁHTVq¼­&K J î·:³È"âíÉÌ”ä…Ä¿`ÚQMuT‘Ü |J©ÈÄ^à(2†ÔáåFÖÙ\‘tÊY’5³g0×sóàS ‹uÄ ¹2öæ§0tßÞþûü‰»Ï<ÜܯB5bìQë']Ô˜Ï4,øÌ<満ÜÍ£0§$‚ùQVÇ.ù=ù|§W3Ç dHØ)êpí˜ùgt'ΦyZº(+^Ï}W t¹9Ùqú)™ôÁćCèö¥í>}ÇF’SN‡RÃ}ìúi¹œ…õÜ’0>Rû: ^Ä®§WËÜ(ÿw~rš d¯ì±nÓZ}ÌþøÅ”²ëûß³“>±QvôA¯3YÅGýÕÉßÍDbãcpû·‚¾zsÞ,šF=/âbê "JýÝ QG‚:9zô˜õNTô…K—ûàô¡óè0§¤¯Ï'aöT5 ‡î¢ÔHT<€ ;(NïÞB7ýnwÃø²v9ì‹püº.y‡vßdˆÛ±f3Wwîz2 ;YÅkù“¸C›äX}ëÏHÞcö÷ÀjÒ0Ò z*IdÿÑ—H®w‘ÅgÎA ý ƇJ¡^Þ“ìiŠ&é,Õ¸ ÿÍõA<—ŠÍ›N…ýÝôy5eæþ&ã¥úŒ ¼Ì4#v»Iõù§è,OÞßš;&‹œÉwi5Ëži’÷#ÑìŒ_Çpþ÷è9ô-4-éç¬ù$xy:¥«§Q‡§3˜Ï3~=ÆGßý;ÞhN6Ï<@„ûeÉÏYuT:*‘Ýj'K¤ó`áÅàýD‹þm•¢6Ó_ãÅhÜÐÞ9 ÈÈ“a£Øà×Å]E¤˜'?¿€çgg&(Ü.]\IQ³½;ÈúnÆ‘'{k=5Ë‚~ܙƑ+Ì%÷ŠSÍù«ˆÓjÂÎýÿõ®ÿþv\S ²3!¢s .ÙˆÅÿ\ÈëN,³¡½›Jè@VãÆ?Ât‰/ 3‘ÂÜpœ‰`µ\ƒ*ßÅØÞ$@V>ŽfÕžÌD»4'à÷Äì²(»z//d¦U=ƒÁ%m›©8ñSšŠØˆ“ùR‹IN¿&<7šÃ.]FTå$®jaBfÃŽ¦lªÆ~ý¼‘ü§5vy1Co¡Gyð;¤š)p߃ÊjôEõ;x7´’tY鑯RqÐà(¿¦Š’µó›itH<&‹ÆC–óñì=%’ "Hy©®/u›äw¯T4鑃EÜÈÄh¬ô´¦ž|g@&é4ôô›ÑÂ+7¡¯Ùž”(sqlÏ[®ézv,å0ΚKA¿UÒÞO°s®y±¾!7HDÝ8Ö&Ï€syÉçڗا%Áæ±êäFH-s:Q˜´mLÃ3ÓL¹ÁEâ4}Çyœ¢»‹>®M£Û‰Õé~øµêstÁÄÜ»è$@KVWÐÿ´ÔmÖȉž 1î ¹"$¿x=ÝR¾ŠJ,/€å•~À'ûûT{á=ô‡Óz‡ŸÜ¹IÔéÈî4Vgެ½5@â…vÑ£ÈCb~ZÑG~64 È>Ë_¬‡òmºµ1‘>œ¿‘Ì`Nrü&ÐF5$`BõwÄ3²}Fô'Ü¿ãí<Û&†9ËC´úि¬Ü”õ´ @ÝW-G|š€¾s$ { ç|”8¼ £[ ÐÛ¡o õÄ-¼þZƒÕvª ÏÀ~³Ñ¹÷ÕÍðÁ¼ŒXÜŸ æHÔ Ç±Çý<˜ð&“ó°Þ­õè1£ ¬W;Ã|É? ðvñÐÜ@&~f ÃTè^ŠÒ<ψ˿ï°zqå‘ g²^£¿- ÆÉI0'KÞýv…~È‘ŒËVäæ§r6þ¢‡?Q/ùƒ™7sÈ!ËO̰-?y­aNÏ‘¥pdÁ^øPñ‚žÎ2e‰p%äû¾¢™7 ÎiÙWÑåÎ{HTE)]çD¶ÙQ/Ýè0ÎOë2½™©æ)hfn ñŸT°í«,­_1œN¯…åöXµp*u8šÏ¶«ÓÝW6_ôcüu^‚-ÚÅkÚÈI[I±/å%Ûîõ¤Üa¸}C°ê÷?ü©²Çy6ÑÙ¿€ìŽYGM“IнFkl5te¶â¬ZÜT8gú޲sæw¿c7£ä€)S¤‰;#ëA­Yƒð.ˆfÏž.Ũ볡ÈC¬·›3:•ª°ÇI’TÇvàïÛ1nS&¬ðTÆ_¢ë ê¤0ïVNÁ“<*¼ k=‚ÔËëqÿƒ3´^v% mb¼Þ`+™©`W‹5މͣ]µØTÒ ­WЉ OPrÏšy¾í'ÄD›RbýE&’HWA*±û4ü¾½šíÌÇêyµ¿í8±ùœH^}ý +O£ûŠ·(ŸÑÇòæ'À—Hò·D•¨ÚNlÿeÀâ®Ó0Wù4•Ø¢†rœ TÃØ¦8¤Q˪Ø}j¿O¥NÔ‡HÚæiÏ(à‘¿ÀŠKÞÇ;+Ê[DŒÿÎc¯©‡am±3«[>†%þÏYÕT_²pé4nK/ìƒk(Þ ÇmÌ©©õž@´k-ñ_ÍTïP¤E‚7q¦ï<é´ˆ¬¯ÿ‡òSë ëS0'?ak—±YV ®vaÌ”®x¼Ÿ2Â(–Ænw=¨Ê¾5ÞRp'j)ZÇ“ÚÖ@LíŸò+ÙƒFª˜;3Y¡¹ÊÀ‚:%Îý"k B®Ë ÐŽ‹Ç'\Á–ÔÉ=±£öáTKÌ<Üß‚ñó7 úõÎ1ÆÇ»›vE¡Ñå F>XŸtÜ•7 ˜¶ç¡p½ù fnäƒÉÖ q{ü’$†šY‘Ãw7®Îä>ÝÂѬ6Ç7Èx;;¢ã&#šd•‰‚´R 0÷~é°^–Ú™Éaï-D¯ã¦°cYØyƒýñ;×?À2s)æË’}¥…ÎMâd¿„¤Óá» ”£U Ö›-hvE4ô^#Sá«4Ý~©ys ž…Ì¢±†4a½*Kù0Vw£ˆí*òË[›Dþ[uæÁ绪äô‡j ÀèZzŸz§àˆò,{z’ ä!·]L!gú"ÌÅ/PWp¹×&ý»÷7VÂ…DÆo3ë]·ŠË¿°a RDP}a6Láı5õ×Ù¶Žj¦ÌÞŒ–7Ž3ƒs_ãiû™äÃÁcxA3•2ïK‰µ¨L›w´Ç!àÖSºzÝ Üâ¦L†¯¤`Ç 3ú™».@‚{Sn?åû »ŠÕP>µ‡Å£Ðaˆ¡Æ‰§à¬L!Æ=5ÆjÓBH*Ÿà(Æ•1â¿«èþô"üJÑ^647|7dîtaK'âïKOêÐ_—ú™íKÞ@¬~;8½’#ò—‰•ÔRVENl*ÃÊ{kØÓ~‘æqŽK€.ñoÀ[QJÖŒ`ﻉ¯ìã!0䙓 5})”wÍQ2Ö~·ðСyOQ§,RðO›Jˆ¥{Þ·³Ï²Ð^7²^“¡¥CèÑÌ ¯hò¼©…åñy£ˆ¥ÞnM;÷Ó²½IìÙ›¯Œ Á¦¯wIßwÜÁŠ/xq'r^âüÉýìSh½™£°òR.ôe{`{îp.ç%õÅkÉÅÃw©×?%zh3Рy;ž@tã 6¬ZÜÖQ'î-‰GÉRœô_Ãù¥‚ô§PÌÞ ‚Áéæ,·õ,ĘJkbþâ—㳉âPýdN’Cãå9xÿH‚¥Ñ‘=-2KH´Ù;ì³–§ŽV+1zI^=„ÛU¾âs»´ƒÜªZOgVмïÞ¤3–æ øOg]ð* ž¾^D>o¦_·¡ê3®|ü|ºÁ—õxûŽâ_ kO¹bdáy¶¨ÑƒXÿlâkØ#kªØ®@¬‡¦0­‘R¨´ÕŒº( ãš/úü:ŽîÁñkÁL%8ó ÕA]ð„5lõªm ádDgJM>Ÿ¥%ŒjsÝ2¥€)ÍMAa£l†3úÔÿDrs¶C_’“°ô²*1z|:¼rkp}9C‹.@S˜ñ6—'ä^±;®Ä²ÍOfÒm/n i©6ð®’¢é+ß:Ç­ôBÝï/q¦átúàâvv¯“/ùï½}y“yB„ÄgÖÀøÉ☀­E<´8¥„Uî×ò©‚¸ôšb§&½HÝ VÏžìøH†7 ‚ÓåzVfÀ„fÙn!Ÿ^«ÁÓr0ðîLÿ[G¡ÆŒÜÒ>HôVL!ÑÈV.l<9ŒÔBÿ -:dû*¾ã›}ꓹ¬…ª¼ÌáF%D‹vPà 6#+_MCSa]Ê“¢K›~·£À¢gpøhYøîáÙþ•»šá«˜ qó|Dó6-&¼tÏ´Ïpº­"îç’ôõÏ@Á"ú·¼Â–MLÊÏÃX徂r<IÝz âŸÁGÖÊŰrê˜øËÒë™;º°kC&‰Ü‚†ÒÅXx°è?í4ÛáKþ´ˆBò­¥æÈOνž lX‹‘>&tzf@Ô„4uñ©ÃÜšlh3]ÎV-ŒÇDOë] _^„5¹+YµérÔ2©Sê|ÐØq;ðoYBý¸ó5‹©”ùU—j§£ÿÄ8\bÞâÜ#Åxj à97´(Á#WG;DÉÛ­@¦ˆÚØ"¨=ž uó8cÌ*X|ˆº¤är LšýÜ~ æ9j1‡ŠQkêc áE‘cïá¼Û¦lÇ<¹ù=¼Ñ 2cBLÀ$†rê[‡Ï‹vCÒ4;œäV²¡'¹™‚B0%~f‡ýæ›`åì :É}o>zëW6Γ[ÌD|ã0Å~Ëqv·\¸—.NâfiW*µý+>ž¬w›ÐdÕ8ã{ù 6LžïqÇ9ÔK€¸¨ÅÑW…¨4;Ö~®ÆÔÑÓ gòyßlÇG/„‰³,¶ê¤0+ÂüRºqÖr8Ù½¯k©¾ÓðfV7ZHî‡ÒºÔŒ…‡+É)õx‘¢9:|tÃÕÙøUN:Ö9ó =d䊧Cb«$»~å3Ð| sÒ7Aß2?8—ÿ^›Ÿg›Slؘ٧àP«ì/CÞå‚Lz«Kwü<êaŸYÇ ¦äû¬¯à–̲‰>)ð«0œíi…ÆjLéÏ@¦!Ôøì`±?Æ5[Ñk}ïðS9;Ñð?%²?:•Üþpe"ra­T"l¿|Ù¤R̹ÃOxK ¢6Ýé%H¿üøÃ¼qÝ…Úas¸$]âåE¼¬ qçó4OÂÙÇÌ+] ’ïHø¿À­b„SyœØq°…sƒÇ›(ÉÂó?ÈqA¥2["4ÖÇÎR£¢ëà•4Ñðãè Rt)š¶÷&@Ì@ ­û#H§ Ó¬KRê­¡a~|ž0Ú…k‰¦€+Ó³Pž\€Óµ¿1-†w!<{8/°b‡{ÿÞÃÁ Gâ›ë‹c&’DãT>v½º„öŠ3è÷à*8wr3ÝÊ×€‡ûÉ7?:´\£$Í™ßÔH§Ûø¡Í¡Ùä‰òÁYôÃú(œÁ2sOz’M-é”UQhùa¿e¸“¦ÁëÌTi^úDò ÓQûj>$À™S(O¯=ÛñOcùrŽs›+œ—‰è‘@_c²Pú7œ¾¸ü𠆶@UôKдà! ÂÅã}°B£/X=f_­ó"s]ªzk1|¼l xèn¡*nÞ×%ðY¼‹Ä!—ªoL†‚ ChôEŽ=e³Œ®©Á­P'ÙÏ.s«Á:æ/v½nA º“ô4!‘?Îaâ7:Í@†zÖ' ŠļS–D­ò&["—Àëz=¼¼¤˜v bua&ŽØ‹Þ~ºà)Rƒ~;0o‹-­Éƒ9`MŒ* å>ÄûmÑ÷û¸gÁß2¥¢­žàèXé=‘˜Üði.dRb‹¡*9Ž9̉<[âpàg,8|¥]¶Â ¥«ìyDæK»<É3Áðu—Xnh3ã}бÑÿ‡"-3i îú±bG8#0 w ÑÝCñ°c6÷lÓ&'Ê5»½['s|ã6°R®¥ Ÿ‘ƒ¯¢y`¾õ-û_èÓôUEeñDh;Šó/d_ì¯pv,þÇ„LßD_tè@í?üÏ÷g[÷u Ôª1‰ÏõèÑÊ^èWbš…£9’Úi €ò&€XŸ Ω)„ó¦sñ™Û ÜUxÕ3nÎ>˯Åùr4tÚìÃË8÷?ÙÛ¥ŒK#hUBäîyD“Nâ…ýè·îÞôNÂò¿¸¶£„Yp‹e_霹ºC8/½ž\n‡|ç]lÝŠ3ؾò46ç%rs_ÆÆ¥Ÿ92®{—'–]sošû݇åᲤ}Ú*¬ÌüÁyóÚ˜(mT ±¿èÑK?±T%㻢ì!¸“¯…R;¬qg|*ü¹Çªì`ÕR,èÃ%踡ÊœäX›0îÝÃzô˜ÊKGvmn¸®ÊôÈ1³»Üe|Ø7e)ÝrL–‹ãÁlÐÞê Ö7Æ•U¤ÂJ’tg`}üRb¹ønS‘ÁO+­HÁIc´ý¤É¸u|÷¶rfì]w8–ß·wfeï…÷¹UŠ)-”J%´Ó½GvFf$Ù QÞç>Š´'*ÑÒNÑøùþþx¯ë}ÎužsÎs?÷ø|ÞësÝoGVI­‡Ó¿õáÃ^ f¨ÝB~¶ÞhŒQ+ÔGx ½Ñ%<ÆÈQå}ÿàhÚGŽ«L<¿ù÷šµ äú’+Ë9|3KqÍ/3;™Óxöe@ˆÒ%ÛaÇGÌ’ýñdyåtÐVg¡Æø1Z›÷ƒ]n?«:}3]ïåÏÝçb¢[’˜W‹$ÀöT{ a JÉÚ‰A(ªŒò+ŽF¨¹¢¥.›Á·¾0:n@?IjЇ7. À-öës r3¤ŸIª¸ËÈ56;¨ºŽùÃP„ Õ}ÕÈ×X‡v:•ОwÄÂ&ßmð?æï­Tˆ¼#Oçœ !ö5×Ùhô|u›!D¾þ#,ÝðX†$¬*YÊHí…“FÐ@n5~>(ΗðŒœ.æÆ?†¢&}\÷ï"oÜ“dJžmáô¡dÜÈ©aޤ—°QÝp<€‡¤ŸÂáÓo…b«xþU‰nX¦ÎìÐä⦺ ¸¥y ºðÃó^ìÌxŠ­Ï1ÿü¢v;7ÆÌžÑ!JÂtŸrì¶A˜¾ ÛÙQ /yZØGC¡LS2#Ýx¢#à!S9T°`P)Cã^®}NîMüÛ¡þ‡Í˜b³’,Fñ…Ö ØSd‚/û^¡¤ ánx÷h¿¾ìz­L›7eÿt¨±\Ç4¦uç •I"¡;oA@ˆG_sf¿þÈü*ZƒÕ-÷1vW£'çdçÃL#eÆB´ÛpÏï—(Ìá£á;!+=¶¯EÕqgù© 7]8tWªð“h¥$<}Ãü×çyíÁ÷ì‡úã8<÷6þXcJõ5Ç;olãè.°#Û'}s_Zô Ucª² 0Ò÷ïïgŸä™ÐÕߺ6* ¥„yè‘/À©žà¶î4 ËcLÉiöÚÐ;”Ὴ‹,bÉêw98oN#D¾:Îý´€pÛÄæB‘¦(™½á4,ô &æGpÊG5δwš(ÿT€ªÄsÙóvïÙ­íÜY;Ú`oÕâÍX¹UqÌ!!öíÅslÖ½L¹ãja÷4ŒÂ±ísPµ&y·!ƒ‘ÖÞA©h‘5¶‚ì³<|öýÔ¤–š"p6ßVéìcGÔyI»NÈv§o·ØògJpí|ñâSeWŸÐ¤Ó;ǘTÃ3ðAD,àH‚èªØ#ÿûKC ]ÎuÂã¿wp†ï}P?R¯I³¹ý(›•µ GéÓXWʼíÝN–º–³¹P×V€<ˆØ¿»x܇‡Þ œ×4J°UðºL‹ÁZÿ³Ì» +1<¤—cÑ)Ôáj̇¼vAß27Ú¢ØÈføìÁ»;eñ¯(žû 9‚Å[u8•\ÃNq»Â´oâ§¢»S€×§œU.=ëÐéèj›ZÒ¤=9öÆâœò9Pjºº,éŽô…¾³£?ˆªòÊPSå4°[@ðîO–±kÓÀC*ŸPÄϽ,¢{:]gu$ô„HÍžþ”ßc6Í7*¡¾½M¸ü§"÷ç•·Ð}>”ÊN÷º 6Ùž%¢ñšìÑç¶d8ý·Îò0ªç%’7L¨ÄûH²ª;‰ÌΩ$K+e˜uاSüI°ä(V*ùr%6ŠÒÿôÖšŒÉȯÏìLn"ãøZÞ­¥;‘s<‡-:•øx&Ñ·Ã7Q°¥ ñÆ8Üèó¢«mi×~m,‘ýIg”Ÿ£ñ‡H˜Ù#¹†ð_÷Ç_&Äññfr*П²Þ÷˜D<{J¹­ÿÓB³ ?E°Wî†ì±![N?Å„7 4u”ýrÜ&ÖCÈüzX8îƒÎW—á#Ÿ>v–Æz¼6‡Ý²¦ŠÞu~ù²5peAg曳UÆDˆ8ã§EQd¦Òvü¥˜AÚ CCï¸KÎu²¥û’¨O؈òÓ©VÔóÎïÊçìÈ͇6]¢PÖÉYïwÞtü»__ ^A#~ MÁŒërôö]bG Éÿ;М‰G²1¢a9»Ìω`†pÇÁ R¬RËF8»ã±äZî\ØwÀ^=˜N3Ž_æŽI¹°lÏ ¼x[•ŒÅ•apïòèÄÝvùè~¼UÇÅœH\~ª¿¸n‡–©pê“4}õ­ŸµÅ°Ÿ‹Vtœ .f§&°‹åÖ㣠l§K C´ZÑÁ—¸G. U™æÌÚíÌç­°öÉsðÒ”à=]A¿<ßÀnx= Ï‚ß±ßÆ31*N¤7î=ecœ~Ò6Šž`ÿ[{«G ¦•Yà”Eð¡¿ ’7OúïWè~®HæM©‚n¾(vtþæØ°)¸[ÝÂ%÷¾r÷¼Dž†röƒÀ"fO¹ÍÿÈîºÙv߃ácÒy\òT„úÎ é0‘ÛÂ.Pž´ñmK8¼ Ï^}ê·A=OHž´åÉÑù2»°t`Æ–ê™Ø;ÔèéZ¿Ü O.ˆ«Î ˜ñtòœlEfì$Öw¶AÍ;:qn/õ¸#^×€“Àv°ŠÈxï k†[ÑîÃr&v%´.èn/å¥ÛÚGeÈà^ Ì»¹çÍCžÕ2ôò< ZµP—˜6Œ5S÷ã•?¼p|£5ûiæ{Ù¶žýÊËBrs/‚ú 2d±‘Þs'©Õ*T,¸ <Ûk9ï/üc}/'‚ª§4}×,üŸ›…ÅjôÖÀÒmä›Ã5¶|ð¯V§¯äX­-y°ub„ýó8¤ÐY¢„;@}~·_âÞ_=ø:O½tNŸä9I÷ï£×7Å‘‚ B;^ëJrÊe^U¿&~ûÀª^èö†|®4‘¯\Íš†­½ž#¸b"ˆv „ë,-Ò|È •Ê*ÙØª#ÀÍn`#Òž‚—Ýb&æä8>0†}: aÂJI”a¾óUX3Íüû“0ãß~<Ç–¢Â„6¬iþN#º^ ×l*¿§~æµÇ°/;Št£¯Cõ©ïÙ@Ò¥N9·‚ðiËVZ鲆q°ûÏÝçÂ峋IŸ»7“â¼,n,a¿/¸4Y 7‘û„WÞìNo凛Aé§qJ¬Bó°×(šç„9ó1W¸ jKÕ™‰ç¯˜ý¯¤©fø*ï®@2öž5Tö¥©WDè‡ã}l°¦7Zn ¶ÃYìÅèHxÔG¹uP¶ã&+”²·M³¤¦M)˜# þ»§À‘%ƒlòycòñK2<^q«æ1ëH%êf”2צi0ƒûe˜¡ sÈõ#Kà™ÓuzΧ¢÷ÍGµQ_ÈÕ³¤ß‚„ˆÈ•$³ԒŒÈ¬!Ž"¹ìŸk*¤Æ{&,g‚ˉª¤½§½†¢¿î 3Ÿ[ös¿t$³QwËÀNÛª&P©úSì„ü\"O]ݼ‰5' >5€–â©:5‡š©‹’“£ShÃ.Zòian¾½~ΊΛE,f¤àú…“õÎîÊ̲g¶òÏ$íYb ó†™Õº?|« :æ›èØ- fö7 Úv3œ]òè®Iq´ê;-µn¿ÿ /<šIñÜOøzaÝ(ÍÈ=/‡Ù§S™^ú›%ôñ/3’$ëÌœæÍ# Üè_7Ar%¾‘ñ(ZCÛ—„xsr;A•Ò×|$3ýݵAÙT>«&Øðê ê%9l°Áö½ï^¬NšÅ0;`‘u$óoýNþ3*¤KºòTéþ Ðuš¶· Êa7º"`“º™‡zE°Oõ3IÁ %lø“‡wºÂΓ•P¹·ˆ=̱Ɠ.¯P‰£ .ÅBÁ/!R|{¿ÛBdÊIVÛF•|}RŠ…KÁJt7_{ˆ#QÄå´ ÝÈóâ—Û<î¡îÙlè<•ûr«ô”é]UÎEŸnŽrÃ^ôâkM'SHÀÕxny÷©W¾Ù$Áy1ª‚«w`~a8î*"£Ÿ¶S£´T“A5œ„Êq*r±]ÃL¨ÝÒv½´;üÚÖc•¦´³î#|©?Áþpè„w±`z«&†—uÉæî ¼ £æhלG _Î!¿î´âÆ‚ô`T&4ì‘ çš4¨v‚&eúz:|÷;Â’(˜¦©úï3ŠÆ×à¥Ó{Ž«d.=œq×\“#ÿé®7ð ßþ‡Ì+›E4¡{åáá‰L{ïagÀ÷‰9™©}x’åé…óñ±ÒF|3™kþ‹™ƒ§÷]0¼:‹ñ› {󉽿ø¤.DI>EúðL-Ö›kSv«?YÓcÛyèî9Zu¹Ž{îø c<¼ƒ¦’G·@âÅmæÈ) Ê÷®c$›²?%è ûƒÜ’½]ø7P±;Š­_Ë–UŽp¶_p¦NSé_nä€õAX‘œ‰çÇœÑ{;º}Çïw‚™g?m xýe êãœlš€Z»£\ í2nþTÏ=Ȧó>çFÖÕ3Ü4²ð½'#ÓøŒ“ý[×ﺓ{0mùÊDÍ­ƒý´ÞˆÚµE“¼çqôJ»Î¢>+fÁ¦ŽÀýÐÝqâl2Kjå舠;Y«E~><ÓfüÃ/È™çB Hk*TF¹ãë0Eê•ÂO—e4Àù:óV<­ÁEw-`z¨óËUŒX¼w€PãfX±IŠì‰Sçr&TSè9q5^E[ÊHç;l¿Ýóó<<ÿ•fIg ‰¸áh‘ÒZŒWj È1%àúU‘‡¾Âe›ã¤g»2¾Í{Ž›ÓE¨µ8 zðÌ?ÐSŸþÜ;¢½1tk Á<êäs˜þ6&Uë¦É!~ü½VÂ~ü±x'/]L?~Ê%Æ è¿yOX‘FªX@sΕÑä¤AØÅ8jèAÞ ×kÒ`ögš®¨EDÑ{®ÀçFA²jÛ ú´.‡ŽEð£ëv¸vk [º<Ç×@)!¾ÑÌcGÌ::ŠF!X¼hÑ{M\¿²øÌò=ñ~âÌ`€åúÓ¥¿§R§ë®¸G;Šþ¼S¬Âù‚yxp]¸øÝ…÷"M¸^¿¿tò’ÁŸ±)äÿB#ŒBê°3^@˜ŸÛpôÄèQÞDŸuc_@¤]ñQˆŠ7ˆÅutŽW6É®‰!9o'ãÃg2‡^k€ ¯Uð=¿Mv[JßÔ•Cäî0Æù$,7—å×Å¢ENä‹üòúèT¶õF)½Ñ°›nzéfۉȩLRvôqP mEض-ÝàêfD†’ ñõŸÝÔwë,Q4—6ù«Ñ½rÑhµ ÙZ9Eäêá`¸ãý—ñ‚ѬX 0®FóçíD£Ð|rpž)Ù¼þ²xŒ6WÓü#ó?+c­èÆÀ]h¬ N–Èþ£3jâ±¼?…NË(GC37Nf`N C¯Æ¶Æ-¨›OßÝiâl_`J‡^fRÏ ‡±D2G/說ŽßFûƒ¹à tF´ÞÁbe}rŒå#<#äµÈj2µØœ„/áW×têNïpT¼Ìžâµ…–‘x8`òÊfö⾇Ä¿b5¬0m ¼sÕIÈ¿#7‘xx¿÷ 7Oð1ãq6–üæÁ±‡†p©¨9Æòl¿x8ì+NÞEÛ-Wi¢Î¡¬C“û ’š¤ÜM‰¶T “„vK´8Wìæ!ø}R›Ê.Jǹ%…`ñõ\:Ýn®ñøî/»?äÇv¡Ls:m3˜ƒMNE 1¿yª€–­…‹N'qOhhÍÆs£‘p†¿WªšGS1dÕ-ÈÝ¿’nÍWþÿýÚµæÔâ}9[c‘ŨçÌF3ÇÉ\§K\é7Γ^ò¾Ö¿ì[Fö—-cÍ'qîÖØ©˜Nê"]¿#2oüPù€¬ª8Ø«l%=~j°]ÄoËÕrS­„È3Ó«¶ÉW†ðTA4ùã'A¿ÔfñÃà4+ŸØÞI—ÞÑij{õZ$O¥2=;ïøV÷¦Ã¬=?@°*sý“iÝÚ#xE9æGマXr3ò|Ó ½Ñ*$vWŒ0{¶.%}ñqÔªö'wõÚ &³ÖÝŠâì_1•<¾p?ž´aëþ+Ë@ê±.},•ÈÊŒÇÞãô€› _TÈn1f}„™.þ«‡£ÁøÀ|"Pú¶&bì`%4YÞE÷^*û8‡ Ì>8 ©¦’ã%áT@ˆ…õ³¶f2T5K„žÒÇEKqÉÂ=Ä|²¾˜mŒÃ¶h"è°‹Í;§ÅIý± ’¸1löLf£5¹ŽÆB6 #Xné¼$dÊ vÖl?ª¢¶µ"Æ‹çâE›õðøÁ2Ôú—[”H\ùLöò=x®iMæj?Íé›qLy>1㥸á”N®øQŽ%zÑ(Ä‚U°X qe7ma\´Î“¾žù’yûø$êi¸Câí_ÌÒËÍ û…Œ¿ÌËɼëX>^ZJÑþ¨SìÊôdf 8©™›‚êŠïØØËŠô´tcmUJõ}Ž¢¦Y »á‰Øe Æg‘3Uè&ëÉã}|$ÿEÕál†½³ :¡wì>…+þ½„¡Û¹â)ËV,¤7'6Eá\÷ßêLP9w¶ýPÉêµø 3Ü~KUèä^•Á  ±Lö?yjÑ?ÙÕ:äM‡ Òç?j!çw½øc5• ¡<{xéÓ¢Ä8ë ÌŠ8ÆÔž½@– í$3JÐlÅš^œUÂçQðЧñ-`vf'xùô3ÒCòÄh¿>—p~eÌ4¬%IŒ ÑçÇæÅ÷9ÓR°Ü1 žÄÚ@UG6 }&ebH |&kÛ€«ïU´¸¦ŒËz ©j  ½ÖJ¾»BùOg“'±çaX±˜¼>P@ì¶ _pdRð§àjúÝ5•þ|0‹h1¤açBátù µïgDB¢w4ó—÷ܹ·Õ¾Í!ò3_X‘¬, 'ž‰ô\‰)¬è³"==í°Äø3Ô}¹ekÉ:ËûìãÝ*ôu‹?c±5‹,©—'G ›Á2u±¸‰äÇA¢gS[ëþ`¼t5åtãÍ’$¼ñë¶™(^]'hyÇ¡U;ž0îIÉj–¶×Á›‹ñ74ÊŸ"?£]ÙM×ì€}»Åmþ²ß¢^±í›_Âcѽd…¾,†Ÿ‚¥+®Ã5›É›mLß€š‡†ŸŽgA&És†ÞÛ‘ uÜúr'¹ÝI)2{;<¿ŸEe¯9qnùä°!ÆçqzâS¶”AæÈ~®æucrÙÏ Õ›ïáµ- ¡˜ïž¯U% ªD… òÝMX[‚ãAf6½c–ù”²ÃHqx.Äwêüžƒ²å–ôîiÖ~ó(ãRnFLŸïeß×@þtyÐn+ˆÅ[‹QwË#Îéô8âW#CÖd´ã…%ß.¨Ëd¢»~6[ªAª;Ú9…ÝLüMwØYrõõ9ƼÊųÑÊ´Ü·™m~ëgTz-f·¹¿šÃGµÒ;AuÆas ­¡a¶k”g¸™á¯5øÈô7ô_†CW QKþ;3`®ƒy;Óalj%ÌgsKè1Ïc]Tʈ½l^ªÎfãħÓË"†ÜkIÆ´Âq u•EaN0#+Ù°ªæë>Át¾Gè^mI*þ¾gìÆ.ßL¸n-AÙ,Øsa•)SüYž¾:ñ B ˜{L$óN; ·nQ£UþÕœGg’^óûìXB,gì©y‘¾“ ÎýÂ4ÙýdÆvî†Ùj‘8Ó¿œîAnà3&ïÝg8±ÿ.ªó—‘-KŒÐäÇ%VCp„*fH:ëñ>œ jN!§NúÙ%u|O‘‹ÄIŽÌ¥>!‘0{ü½ð¿sži>¶œ+ɧK¯<‡{L*Ü$‚‹ÞÁ·‰82+½˜S·¸ø;BÁsݰŸÞ‰ó^­ ,%»ù—Áós®(Z‰Ag[Ññs+h•\[[6Ö¯q:dÌä'ªÀZï?Ðö³Ì¿M¤ëð†)S©}ÖÖÌOeA,Ç$¸¼gyÉý†3Ó塹ç \qM` š‘Ås˜cñèó>Ü~$&^å°¹Ap§$3캇õµþ ›>‚ŠÚ¬’A7 ž–c}•ÏßbädV¡š‹§ÿiAºìN)甜î.Bý*±sçd¯IÉ“YK¹ŒmÛr7¤rJþ¦™6ùu¦N«ƒM¥r´ø Uô›K7>µ‹m2ä×(;ߎ(d Ѭšo>î Тµ—%ȾÔ#vE-z˜ÖÖ€W•@–Ûó*fÀõÅ›°'1Å3náŒÅ" C%È­;Ptû/¸ñ±¹­;Ûï¹áHS3¬\Éà‡/¼Ü¹¾#9̹WµÀ‰} gWŒ°Iúa`aVÀö~YñƒkQWp&.6ä*«mM™*,Ïh☼9ƒ{ßèÒÆDz¬¯»’+ðsxþp|É$å0?×A~§)Ô›Ö15¿³6¯øáó+ü4™?,<ß3‹‚×A›,qž[ Í¡²)¬5É1®7³GÖ›“òK€kw¶3Œ`$¶Oã§Ù^a¸¶§€øVá—ƒ™4­æØk÷qf>…ëÂÆà×8²Ü–qFƒÀ©»˜òΖÚpîCŸ€ỹÎ|šF_7,£‡ma’ÉÂæeZp0ʦÌàÞHIƒP*@™+*hB“ÏzЫ¿z€IhŶ·D½ý‰·ÖÇm€ÌD-Ûl\† žgÅòË™“âÐzt|zUo_ oW׳6ÁwñùIJCösÝ\)¬ÑA³|d}tyòIül¹ äú/ør,–4/³aß­^GU¾ˆa|‚ΪlgòÍɱ `~#ÿŽ$€Pýê2®»x T#dÈ<ÞØÝ˜‰§dáð6”ˆ,ƒÊ-Âh|o+ýöZô ’æº7lÖѬÎ|€çMšä¤åê¢aFÆÚ²÷òëðc”¼RÉì™…ÕzÐvßÝëÀO—bðß©~¦¶ájð]Å´3f¸)> •xçÀ½t}(ëþž2šêl@K÷1áœH˜–§ˆ›ŸpoübÙ^â;޼gÜtH°˜ »!á(gc8Å•ýõlï?a¶GûûçË8S6¸Õα†ýÒ£Þú %¸6¦Ç2*×fÛ|!¶ðÕv_—-fš‰–°ØoBÀï0&þÖe•ƒ¸&Ï>ò€õ^ÎðÀà6–›YÐÛÊ·‘ÊuwD¥I’ÏÛ¬ÙëÉ<6v%Í9øº±“M„qæH#[´ïtqvT÷1Ýõ~0Øïqv`cà_Ö©`: ô÷R{9Ž¿rhFê<®ÿt9Î"Ï—l½ßtzøK,Îne.åM§“ùœ•ãÑÝ]³è*‰A¼pæìØ, ã‡m VGÒ6Ú7R ð1‹íšûŒ^âœð‡¦”xåbyÐqfö­ï9YæOibÛŽ°þD†*ÏJbªžã™´¼sOŸ ¿u•±Âè±±€ûoñÔ\þ«wäNgxBôèU íÖ Ó7 ±oëeðØ× Ëûiö½> ]ƒ_‡£±í¤ ý«^Îë£Ø{NÒžÌjœÞ-Kë»RpßÈ6x iß±=¹lsÅ÷…ã ?éø•‹Ù?²«_Å@Ñ'K²ry'7•U¤‹#âÑ¢€‚SOà“:AÙΜ™æEÍŽŸƒ>{´L7©»Ç0ÓîºÇó’ë¸Qoã£G°ÏWŸ]¾g- ºÄÜP>À|,ŸÄ KÄ©‰DÒ²3²g²füE³¦=ôJÂgfùélvÉÇ—œø¶äÉù¿¹›ÂCqYt{37ÑÛf/ìV úËhôzß¶¥ûçgq´ †ÉbîAw…6Qul€^f)ä%óD­pBö¦É&‘ÙXöüÀ5Í–!«Ä;ZoÝæ|´Ñ'Ñ÷•ÁnÌxè<Õïàî‰÷­Ãg¿¹°IÖ!­.?¸!ÊÖTäá×X€Æ£Œê²ÌåC×°:圯ØÀÖüÍ…ìUµ¬¬¿8==;›6å2!M“Ò%™’š- 1Œd޶-‚벎(â&Is|Ʊ÷„ݤG{õ†Ð½+–sçë U0cg… ‘U—·£ÊæD²ešŠkËÒH·ïœás°±·Ò7lÁ"ïÀøp3³7PŸÜùU‰KCWR †ÔAHPŽFRdsE&¹*)C~±‰¦BL;¦Kd¾ iþäo‚¬[ÖäàOYâÖ¿’qï`æé0oT%¨åsCŒ}pƒícïÒ4*¿cƒæ/x°¦€Írã£b–ݨþEêUŠXá-º`är”íõ>Î ÷„Lálv‘Ü «qò:F'0çÎú€Á¬ð]‰­O“ÀkvÏŸ‹Z»žá>¿m Þ_‰/ïâ ²cJ”Gý#7 ÄŸ8$1m#o oè##¸Sø/øÂŽØNX󼓱5; ÷ß@s¤,±Upð©!y;R–O§1÷KÒ`Ñk.4 m =B-hgÕËÖþ8Žw§žíð?X¯¢Òñµ})1¹VÅXj‹îÖƒCÔ(¿Ê†f §¹,ÄwëÉǦ¯ˆ× 1k¶Ž:ÜÄÙný¨(“ˬüœ !Yà0dÄŽÿåÅçÆš°}õ°ÛÉOËŽ±Ì²öÄb± Ù»b ¾ì™'Ùà•ƒð-ИØë÷³q.DZ²Ÿ³¼Û…Ì^ûv†3eÏ¢Yñño˜#XÛzݰºÑ>lïÅ–ÌÖñß0v$ÏyJñ—òÛŽ#HJ>„$¯ÜÉ@hü.0Í…Œ U'“Tkåðý¨›¥dÙ½Ëø.C—H­éƒ×K˜„-¶ôãˆ,ßÏyÁ÷µ×ÙÉ5ٮʓ ýäS»`·*iÚnÃÜ‹QÃÇùIÈñÍ`ÇæjâµÅ‘0ìø„mLÚÂ2É A[ö cÂÄõX)s·¼N 㙿¡_BSK#®g–ƒú¯vx›=k6ÿËSIxO·×a ˜²£8¯]nеN޹³^G]it¨+îÓ~„µ°d<˜v_‡L­÷°?Õ‚ÌeÞ¡Œ›`2¦‰þW~°Þ+ÌÈÑ,+¢„Cc.”I!ôd…89½bœÉ§ZOÀt©Ýì{ãZÖÚL•̺t‚›j5ozXû™&`=HI5B“®$t{Áïѳp>ì6­‹Ç í¼ àþ“[[¶…kùA‚ø‡ÔÂÑMw‘}ü Kó¥éh·‡Ÿî5»ƒ‘¼"ŒÈ<1ª¤²$}ypÅÇçÑ›@FÍ©Çp wÉÅRã öx˜9÷l÷ Fwb7û[Q•®˜-Gâ%ÉPÓ;ˆ8*BqSH¥[2\n¾È¸mFV~=Ž[Ö߀µëjàaת‘ÁBƒá èNü`ßìi†—‹cá}ƒ,۸݋Ãó/›åžNd¥âtÞ—"»}ÁÌÞ Ê¹Žè^·ô7CýÏahìQcR¾(!+ïI7¥á‡hu˜¾k&Rƒú­[`É}-Ü›ZŠNüT,ûË$úÃ}éiÄÊö±8*J f¶aóÝv†OWlãqfÛ¥UìÄ^yØ(ÀT™á2£í£Á£FÝçAéˆQv${O/„ŽüÁK"¢4dc5,äÿ ¥LÉ'±H¢RšÀ©^1ƒ¶:¨ƒîœ¦û­!›û WÂøm<Íä´W`ØÂìïsQp{0T_^BíŠbÀDô³fC÷uº+©þÕ˘6¶q¹&‘)ªg¿z(³ÊO‡ày—$Qû‘Àn\c‡å+¨»· ÏD»J†œ]pøYEb­«M»tžÃò9é…ãÖT+Ž—dΜN‡õb±µª8" ݰÕN‰Î2΢/ËKȺϑXþÍ–Ü Æ°´HÂΊ…ú'UœÌKÜÄÏœ]üáPðöê/b Êðt®YåЉ÷#<1wÎGØ, yæ¡ûßÛ°0Á…­/Å‘In麴'¼Q¦n„)~(LÇÿ9N0˜CcuâìÖŠë¥ Éz;zk ¤¤ ¤E_vL¹ðŒôZã׊ÐóÒ?ðz¢‰®åQè´[Žt¥®#WÃÙ>ÑLø°±ôÓæÑZþTÃK ™5‡I¬vÌW]@’Ëî€Zßf¢T½Wº³%È›§IfO×Gy}m\5|Ÿ­šÂp½¯àˆÈIè•Á-ï³RíÑd™˜ñü¼™\ýô·ß®e‚ƒ”é΀TF=EŸöþËJž‡€®ø$´ˆµZ¿ ¢Jððá*×?†CSoãÊâ8¨öÒ ‚úRÔøÐaÖ<Îÿ5§“gf­ht¤.ûŠüƒëÈ€áìQp¡Ÿ_˜ÐÛ 94þª(Ý¡ÊÙ?èGî|Š¢ÎoÃgìNár2rö,éØD/F–úç’g¢QöI$¹¶#Ÿ£9ú7ySqWrêiÈ(“\?ý£áB<Ôjï%›´¦¸Ñ㎇&yÝzœ»2œD”’ÜÙ-™z ¯×Ýâv6À­Ó‰ÕñwÜ+ÇM‰Ë0ÐÄOt~´*-ŠÊ½ø3œè¡|œ4[uºqGá ¼*º>m2-m¼óò,ô\H¨—à'ŸPZËC¿›l ZRd_èLb)NÇkñ'T¢~Ùmtp$Ÿf‚ÍÞgpí|9ÝOBœ!3½zK‘›¼šDóÏpª-Ä/£é¹d4´Nk ð«X™Îúr~v)«dêñ%Ÿ¼[Xš‘Ç0>‡Ug7QÏÅR¤z–m Úwš]uR;«ŠÊg1WÞ+€ì§röÍX$kµ¼Ž ýÄöšøþ|;§-N†ÄÄH1æÉqÌ÷GiœèU¯pC-Y¨{Žl{€ ç àbGUj[÷›yrôn‚qcsúm@˜«.~vÅIB¥“9ðüLY‡ã3,é ³ëìÄ=>6¸ä,þáÈK%àï?O‘óƒ‘l#³»HóîxE« ì_Š0&Œh†*¸þxÂ=gbK±\›*FgŸKá Ùhø¨’Fæ’ˆ ba7…>[†7ÙY$ÍU£¿Ø#;ÜÉfY>Á‰‚_°¬3œ2ñqÌÒ1²•J`^‘a&ÏJmt¡êœe$¢sIö9‚ÛM{ØžÐ+œ!6 }GpUÇ6Ø´n'TÀÛ·ŠÄ£ož_? y>1h–t”^ǺȩøåQC¿º\xi¥ÀfŸh*k,?›ö/Hb¦þ»ÓtM¨ÔB¿Ž1ùàý$ £ÞŸÓÌÏ–xØñ*°Ò~¥Ti씪ÆÑzžËÌm£.¶°‚ÐU { ‚“‚3´µa‘eÆçõ£†f§æƒ,©¾Ëe½fú#{Ü’qx|o¤ìaºOU€_Te†yÉy^jûrû¶½`Ü—fBú´2œ~§•ÝôU<¼œ )U†èýI–Øî”`Õo7pûï²ïáHÞð¤s`#V7íBæ¸øx ¼ÑP†9éOG:²bÆ 8! rtûòX:*@ßÍ ¡7õžaïl=J9Ò{³±ZGŒêÒ½åN Ë%ñSý3Qdi|¬yÕŠ‚ëiÜöm¢¦ó”–w1Þ\$C›föíKhXÓ<QdL¤#쉀“8]u+æ=ÛïcãðzSè~Ÿ 1FØíOÝzUéÀ #L£81u«{É»fß.Ö8„¶e¯&¨Ã/Õ£ØyÀöŠßœä3S@ñά<ÉOãmH\î:ú÷ê½Ãg Ø~;^×1!ë¹ñäæÊ|¼vôºÌ‘!ÅW8qêáÌqÈ!c>Ã3­öÕåq¬J?Ç,òø‡ lë¼§<ÄkrWع¼¼‰½'g‡!›¼á÷@ ñ«ÙCuOôa?ú¢^Gx¤¢D+ÃŽ1Ï´üáÒ„5TíazÅáÞQrhåcÖþ®µ±H‚í‹¶‘Ö{v5ÛÈÝ ÞÌ¢­°á :l® ‡G?3D= ÿ‚ Ï%?î £7ºÑÛ²…Ó2“—¬xX _çÅ@ØÉÌÅ9Åìw—Ÿ`¢s‘Ý2…ºZÃØ~À†]øÙ,4/e± ›ôi¥â{¿· ﬛ [¬‚z‰Wœó9;ÓW±–¬¤F}pJåt=¸Å~Ut¦W|Û馺«ø[Õö÷0ì-‹Ûø"{+›P¾‡th/c%âå :ÜÃ7öbZc5ã›,Ît”а{7ªÓ™g‡Hg¤-Êï¼àØ­‹­¶Ë)¹)Š«.å³:eàŒh>n?w¥Çæ’¼ºƒ8Û•ƒmm¼d c+g¶ƒ¸#PŠU⮲aë&œ;Ó…²å‘ p}Ç:²m~2™AzL"ØP±jè`†àî¼fîŽ{èõ™CÞÅëÁUºny&ìPû vÏž29¤nw$–NU¦Õ’ñµ”%5¹7“Ô~àd{ÅâÁ{}ðkg9 =)©ñÏà{þ—ÝRÃ¥°ëV >?ZE¿ •GåéXI,†f»NrË9ÔLi ~ÖÖ%½ ’`{¹Žs´ð(|¿¿ØÜe_oß‚VCŒÝüðc²ñÂk"”Œ ¢h-—`g¿BvªÅ‘äÒ:Œ;:ÎÚ8ŸÇ¶9.äΪ!d£¿³‘R®¤èS(ý>ÖÍþüO5Äsš|øèHp97õ#®¬±U"Ú*³ á ½iÌ#àÝ¢O^v%ÃŒ½4>ÞŒuÎâÕ±à5{€œ£RÄuŠõ~ ¯¼p2Þ¼G¹#‡©Ëæ"Ü÷h!iáìè ˜Ôzšúgùû0§‘H:òû Þ;Md‰ {ðÃxæ’ΗԘîvAbˆÙdµ^,lÉçܺVJîÛÈÓìåGÙc³ôH¦U}s‚‘ã»Ý{— … $ÙÐÃBáÝÕ‰Ð|Uƒv$Ó,¥–b¹ßŒ¶R¾è¨IÌÐO6b‰&w£ËC1øÔ©$5øgGÔ?Êà“ýè6Ó‘=µ4ü¶Wà[gâ³7‡ÕýÌ^w˜GŸ9ù¿U4{ƒ:îScœ¹ÖTQs'lc&æß[Šý*õxÝ‹‡˜eÚFQ ²ÆO ßH‡Ó½d¡q$ò¾N#ßgà sŠ5Ž9Èz/‹့t¬éñج=ÃGÎ,L‚™ÌvÇéØêõ•Ý{ÿju‚Gà³?ñ8ô^Š;ZÖ€«r²¯}È’ùÿ=z%ösŸ¥É¢m¢&IJ%¥JèÝ£[1q‹›ãVÅ–¸­Ó¨¢¤*»âf¨”Ç'¿Aåœá·¢¯~ARi/CÊÌ20ìD½µº6%‹{ºÊ¦÷x2‰Õ2PÜm6É›n²ÛaæV:îëÍ)]øËåν"CŽ “¶}kiìËy$®uÑ JÅFî :cò‡9òO´‰qëb£ÒÅpÕ4éÕ³?àl·45Ù¼nÜ9‡&8íGà§ò›µ˜Ty‡Éw·Š\‘ìÓƒàÍF5xJæÙÙ•¾Ãÿ¸Àìç¯8ßVÛ醂jv×q]Ò²t»ýfàþštöì]bx-ÆnÜ|{ª‰}Q\S ¡lµdÛuƒÉÐu|ðþ º|>…÷BYµ*Ù–¼{¿dj9‹Ùý9‡ÁÆì2Þ žÍ˘û§ƒµ¥=èe±—¹»>~lr¡ð<äÏDõ—&cù.ðH…sÎ…øgËT/Ì'âs_ÂÇ M¸Yg9›¤¥L7_QD“°D¼œEMà °;t;?êá¾ï,Ly.Ê„NòÁJËYàµû.ZJ ®F€rï—@W7ñˆÒFÊ,| º$Ñጅ+É ‡2M  Àw<ÓSßÔ ¨zº"Úi¼b¬«fw—ó8÷&J‘™¤îçfЧsofµ@Ìô4±\—¬þ®A_%wÂ:É-ìƒká†UŽÔx‹ªŒVáßç5\ …?¬Ít3Â?r‰©}(ÎZÇoÆYU2àÖB °]¥&ªšd(\I–|çÒ5OÞày›D‚Ç|aƒòN¸çCÿÙ “ÂøG0’XNn_¢%ªòäqÔcXRñçéšcoáˆìeÜb˜ÞG»hÞ~ûÕ“<DzQç´·ž8GxNKCîù— i,EuìZȪßDJc­U1£çî ãäYÈq¾I&ÌãFì.j‘¦[Ú\JÝÚ2¹ÉëºàÛ³XâºH×l¢)›žÁËí p¾)šÐ£üc6èšF¯Ž næg53þ¦Ññ£CôÂ…&È~fŸKû‡ÖµicÌߪA“å¿[ä““÷1&r Y¤M7t¢Ø Sr´¯Ü[Ô°¹óz7è¢ó†ôXï§—š¤àEF*»«L¯ã`óÚîÓÈ| >†×óðˆt®âx“‡eF”dðŸ9ŸÏºÓG.bä‹L;ñVÙBs¦tÀÍÈAH1H£õªpúÞ4Àß™wù>¾Ú+ñtt(…ìÑ‘£ö7è¿FaÒàöLjö`C– ö²#Ô.‚tl÷§³nýƒÞƒ´Ñyw³[#TÝt¡/®Uà›‡õXá” Ê’]eÇá{g²vð’A‰×̳’1T/eÝf'€Ÿð%”h%—Ðï©àÝéÌÆªÙäØ)-Ú6`L¬Nm¤s4¦ÓVn°á—2άÙN¢ÜèågóH-+;Ÿ‰çÛÉTeïbºn½ ·=Êœq¶¹÷,ƒú™°3i˜©µû³™3›ÞÀçéÂäÐãÔrnX"A9n¢ÔỔnôô /»B½U,VêlF ÁOx:rq¸aE*T±aÒ¿¡k@Žz‰= ÊÙ}à|Æ’NýÙãÜxæé¹hØ‘¸Êøâ ò§º`;ÂýháyæÒO.×ô¡#ié ýC°æuó|¡›ç&Á&2£=2qn6CšÆ èç Vö¾1m=WA.<üÅþ*ý ‡,iÇúSxP~?ú»“…Â'°âd>I´. ÅoÃ`´êv³®d©Î¼˜ƒsNÌ„ u™`µ??ê€ã¤}&òÔˆia¬r­ÄQcø.³O./ã|2·%iË?Í–ÕnkDó»SÙdÿnÿ«DG¾E@iû=,±YŠ3³féç"TÍm.,A±«ï7×6«SO ›ë"RŽ-x€ŠP+Í{\f4á£õl]§(uÏü€×V. OVN£Yg$é…òDvø® >5ÂDÖß„óÒßF°Ú£‘_ÄÐËHõœ6(7+¤¶›]p¯A)H?x ¾z#I»à.}.GgíYÁy} >¤,§KÕR¸•¢_°6U—¬3™BtKó™êwóQùN:~¥ŸÜãoQä‰v#ÙÜ{pËî »? „n ü ;6/¡­ Û‰­€(©t9 ¹ã3‡ó*¤îòKÜZDâ­û!,W8A«ýüˆß‹e¸Û#ŽÞnK`6šJÁCÛéÜáåô„ýW˜|¼m¯>YH·Œ02eÀüQ!±½)N~ø©P©Ã=˜ãñU†£qVö6z£Û‡p¾¤P"½­6„³êSˆ…|#˜).ÙžYPYƒ]…Žœã‡“È›ƒâTuI)ߣ ??6°\O³¾q¹zæ¼É–e¯;îÄgÂû¥éLÕÃôîÝ Tö´¦2•ØéZéîSPGy;í4È$ëî‹‘,˜ñÂöAâP—K[>¯¤!ÝSé É|uá$ûb>oçø„+m˜¾ˆ†ÿ™M§5­¥mÅ'í5fÆçbƒëÜÄKñ¬Å¶îL­¸|†ùQö t ^à£3Hüã,z "—ýjuDdBÉó56p棽$âG¿lá…úÛ$0÷½~Ä—½!ýºiàméèÒÅNòã'@É’?–Ú¤êÝT²øG(t ¼†—7E‰Z*)*†œ9«¨ççVr¼‡ç,x97«Ê8¡†7Ù›|÷qéõ 2¸^œQà¡&kŽc)ñ$öK2!jvx/\æ?;κ`ï/'ÿž ò+åÊb8ñ¼…§ßÆ¢Ò®xØ[)‰{+Óq¦ª±îc¬üú8'Ô³î®QL#×K>»K xêõgxaGÞª¤µ…è¥þðrB†\Z‰<ÿ&sUí¢îx>¾«`±§ v7|‡üåÖXÏÁåyøqG6NØiÒby5ˆ;>‰ïƒúiöÎvµ½ Q©[ k­3yÓR èFµ‰ :®=Ïy-³™}ý³νIÆiÇ%PÄxS»˜þ"/@(‚Pù—<ä»ãXøÔŒ<\O¾¿Jakвà)+H7ÙšQënQÚ‚&`ð䪫ŠO2f².]x„}'óÙ§!F°ÌЈL¶ÅìU_P»=Ó­nãÖQ2.ÂA¥ÎÍC@nF>$°ûè³Z%ºk=?•8¤G ·ðão_yöK¤ᜭo¹´á"ÉmÊc{WÍ­{̤\lÂ'ßWÑÅÉP~‰Uo¥À+hþ­ŠÁŸ™åë ‰öŽ“4Ü¿x2ïáØ÷30Üú¿¸3ó÷±NÞŒóŽíð7¸M¶ùÍyöå¼<ïDoJeÍ}5‰F’µ?;‹~N"¦ÅöYð‘•uõ“¼R…>w˜Fò/4£”q £-¹6L£õýÉíñ8Xñu67¤‡üßqºi•X¿ç0ýæ¬QqS©ô¸!÷6ºüî4ç’@Rô Ø•6Ôãh:™/ׂÑ ˆ/‘%æ½Ìº—V$ðT~GãB)úu<‘Üù½” ¾‘'¡ÏRáÍm¶®í ×V(™Ð/yôªÈ2m¡ Ýëšã_^@‹‡å¿¥L¯X×cwÄE”³&´0«xý•èþ4æÚ!u.-?§´Šáú~šÏÏÔúû$—31wn€Ò±éøb3{Øá ˜¾ðäH¾†»~@äê³”L©‚˜=‘$°åºKÒƒú8Q!Óϱñ;[áOw¾Š_Lšà›…,X/Hä‡a ˜¯Ÿ„ îÏ ™œãy ì6*Ó*v´Oi:Iz䡼U–!wTÏO®#wôü˜1þ)Dý,*Ÿ8ÏñYôŒ^vœg”á÷s7¦\ÅŠx—/yÛ`ÇDÁZ‰ã$Îí†LÝN²B'p¸+¯ÙG“‘ÅõPÿ/›ÍèaeOJÀ®SªèÛxlÍq}L'›5‰1ÔôÙ¨èÌ|=òÇ)^ ”ÂuÙTºÇg:·×å4ü}²‘¬×—!-¶8³ÿáÆ’¯øÉþ&XÚ-bÆjb0Ũx„âðF¬>ºuïf?Æ ™8¶ŠcÀl>UÊö-ùÂLØw£¨;ªW;£ïóŒÙÞs¸@8Œ4kßWlÃÖÃ)ü•ÇL®Áuê`ê›pó3UIÔ9K:o?É:ÀOžDM¥ù/ƒÎÓRr% Šl…ˆÆÌslô¾14J§Ÿü…>âíqÚ²æƒyG™YÞÇè5^)fà‹3 ÏçÅë§µ‰Ák;¾§SÉuÉsì§@s*¾¢¯ãåHh¾º‰‰ÛO2{æ—Áõe¤°i MUS$Ú÷³qyÛr°{°…Ùç?Øaà1 &UôÍù·PÒtªÎÔ¼UÂàfoMF\U‘—¸±ø6Ðo"豊 ÙÔhÕ!T{¶–žD¥§áØüÔË!y[oéYï8’!ã•bÎüðbƒ ?áÅOçðÞ®¯PçaC û ˆ×lú()=£ð½xóçñýX\¤*ʸår¡ø¤sG. Ür9Pýñ&^ÛpwÙ~cËç1m¿–aé²ÅIã7mÜtT›lî:‹Ÿ±¢¢X…Œ­Øõ'{¯ `Í«xº×íýx`vØgœÅ“Dcwu—ó_ðòƒ¦7Ëô…|èàÅ(zÜ!-#Rp)ÏR’•¬O‡”ç’2òÒ\#˜=ÎÙÐà*K%’çÑž—®dù¾Ð7?¯«›ÙTM„°˜ Ð<µ¾¤{YQú£~ ýÅ :jSÁí îyŸÿé‰ œb¬2$è™Êv@[>2²õªt‰Ïf9Z¬´„üûùÈÂRó& ÀÝkÛáÐŒJ»û-*$T°¶ªñ¨Y6MššV´kv©îáèòÂÛ˜¼a&™.Z‡æºä£Z"î;e‰YgžAŒ…©Û¡NªD%iïw ¥ß ¹¹ssá~QÉáÍÀ ³•Øc”¶ƒ•ø:0ôÅÀ`+'¬mÈdNn`Þ "÷Ö­d+ïÍb¼„—­KpNáXªv{¢uðÏùj2äþ€ð¾ žYM%¯J¡Âó&Ya/\Î^‹ÌÐX$Ê®7LIõJ&ÿu ^“·${]‹ P®ØÖ×Y’þ=0‰d脲 ï ÜCd®_?ütñó,TÚ) o_x°È”„CˆÃ&z$(Šl~êJRû…É’õ«©È¬3©{3§?5¬*`f{/&EŽÍìà?ÖÈeDÞì$/¦³¼Ínà8ï”sœÕÂ|ÿé Yo7>ð¹ýù»ª!Êc†y>3úòHëZPaÝ—Ðý¥9°q^3ÈœÝTÌ ZþÀC>’D@f]—v˜ó‘ß%üÌPÙ1P_v \lçéH^D1Û—Ü/¯…ÿtÊ•:/Ø–\s»-JšT¥þ¼¬ñ ›fÇ¢Cº7TzìÀs‚:&Pj}4èIÕCïcU¼â* ùkÕÕ¥ç›g34LÙà3ékH÷_7¦v?ÀÛCÚÖC7W¶oV’c éøsø(9V¢ÏÄG´Y·7>µakB5Ù%_ ‹/ˆþ§E8ŸÎû Ъ\>RuÈ€];£…]Pi k…±º©ÉÈwd*üy˜ 3üáÏÆdTÏ•‡ò svmä?Ôñ.e¼bKÈßÓ2dÏÒepËF‚Æœ¦(K¥>u1Nã¬N#Û—Rz$´ú.þZ¿ˆ5TqG§w ˜Ïá¾âWÌ–îÓ®åe,jX˜z’CDTEÑd¨o~Q„ýj'ÙÒœœ?ÍÈ}ã,ïØ©ÍñÊšO0kÿK¶ú6÷Ý*!x–TUÉEr»2¤­ÿaüÇ\Fv‡2ñªéd­ÝøHÈ_.ðßjÁ;îv¬¼  ËyÇ í…}þÏqõ¾[°°Qv¬§;›*¹"¬àæ½Ô[0‡©Øiû{aº£'èœ)†Ð&Uò1¶Dí`oœƒ…Isˆ˜˜,Qó³ÄÛòi0Û¢t,ƒ²…wõ| àër„»þ°=I#Œ•‹ŸæhrVVsöÇ“Žo¼¬¯®4eDCQ±ë¡ÝÖ^d_îÓCëñŽ~=òXÇãÁJ]VAÏ’êm„ºÏKh&©bǦ«Ò_U̱`}úôËwlæÎ!Ù™žXm¬ù:ÿâðߺD+O¦¥¨QMé vÎcYæ ñůӎ¸‚} 6 -wDé}úm×K’«_öAÃI&ðÖI¸W Žæ¿eõX“Þ¨l\dUŠ1SþáŒÔ´ôW%lŒþ§‚#³y`òµîlEÌSÄíißjy‰=÷ø5×3K˜Š$¹AZdep›kÐÄFZ*ãò›ˆÙ±ËØxë)ˆ/j½r(I¤sv ÒÑ“Ð/÷¾çM!E€¡±,y;W„þP‰%z"Ñìî} lÎ5QÆòƒ=À]L{<#Ù±ö˜m²ã¶ubëîåär³(ÌÓ&ºîq‚_·Àë­®À“î ÇÝ(ß(÷ýæµðb¯QìL§gâ„Èa^ÐޤÎÓØ¡ßQæòZV{©7p>¿gŽ›r¯QðÌÓ€a{®û€?ùˆ„>sðàSlÏ­Àß¹6¤Õ\ ¹=¦,)º êÉÂÔÑû&ÇÐ%²÷NCSõjæaÑXÉmÁüL„,,ZXÁ ê%r;ïdцŸ³©))‡ÿöµ»s]¢C»x=ñÆðÛ+¸eƒ[9‘†3àjË'Æ0%Š >S§;ò)ã¸é0ŠùV¢ÁÝFvúü0b/ÝÄü ÷D}Ò‚® O‚¤Ã1Ø·@W.ïd³½_1w÷3Ï~¯…[¾¿AzÊ]žÄ¢^xëÄ3¡¦ô;§ä̵>­£—1Τ}¥x™?™‰PÊ™Aá¥>U|ç Ž—¿Âeº“ ÂPÍyÌy¢K¯ipwß›@,ò0ûEÑ–ì19‹±*\˜j?^ͦ^ñÀľ¯Ìæ°PÊyëÎóÁ†­%PŽ©sQh®ý}ß7f"¬ÍÅ7¶œv0'ŠÁ†Ìþ#ñl¥D$Qxý ÎGž³§ÂŒV&ƒ_GàöæWè²ÐÕþz3ò1Ex$F‚Þ™ŒÓÞŒvx9|‰¹ÅÃGR ’™¿cu°4eL¹—Ž t-¸Ó 2’ Ùîã÷~U «‚=y½Ñáý ÄzÀÌÀ˜:±ŒIü£†7/?…´;fAÔ|ˆ¶Ù?óå10 3®?C·&,Ã^ÃÕû·¢øLÊwö®ù·›MU=•2J(ÒžJùçGÓBN±=^¯Žç²1o9}ËãàŠÇjÌ U·+Nðe8ÝIXuöжQù óíw~o÷`v}s#>®dÙ–ùø.o’MI§hžòœUÇ!ÌóØ5ciÆ{ζÐEtÎP \Z“NŸÈeg”Òøi£°'ϯ3¥¥ˆ´ëcP¾gNgú‹ÒŠ´ æ÷g‚KÿÀW¢CÚþÀ–œc {§&²KsBñÈRºèÏ+´cbáÙƒm4úãl:ñï;$o¥[ð8(ÀØì(gg¢.DRüD˜@Üge@:5ˆÿù4’%C{ë¤È”úböF&mÙMØQ¼ŽÂ>tLÛKëSrð‚›½[ÞÛ²®a˜€ ûà‹ì5ÉR.LN´ ªžŠf®-…¡­‡¡j±$r–dA\y&§¶ì7<ø>¯ìfå+à1"usÄi·á1¸ö½ÔŶž[žà´á þ 7&VæÑLQ¹磄Ýf¤ªú1ÔËŠ“\ã[Xqå ›»®_÷Æãã´x'‡pä:>[eŽbò%Prã5ÚÊQ:Sü>x‡¢q‘±a3nþ·÷éá”ìemJÆ/®øæº·u!„MN»ý(¬Ì{¦œtf¦7Ä¢ÊÆfæ£ü tü¡‰›”hÐ ¼ÓY‹\§‘ ‘hóê=t,a }ö"zwç2ˆ÷[€~ÚØaG±Ù×…Üì:ˆ_GÙ¬’Tý %»JàDÄHQN{î:âŽ{‘QLG ›Í%.›_q¹ûá“!§Wƒû_:qÖ6GcºòUVo*öáb­k¸èx$›Õ$NTdè¶é†dä—:ñ>L¹toD[ »¢E®²ÏÍN0¶sbISÒT†x_Cë…sIä9âzº#}•Hœ³8v°Âؾk.¦·øSUc\¿Û 7܈ƒå¹; ÿìRPl !é0à};ý[¡-ô w9ª´-d Æ—±êwLØM§Q{©<°-× —™NîQkH®›än™x[–‡nøŽóìÅ’Y°»èül:Å,‹Ô§ëÙQÔñcHû²¾ãû‰nD8pO¦‰fÄG%ÖNdÊ–¦ú¼nÔŽ25Ú䑸èÿ›ÈìK[L§t/rEžîßFV^hÆ‹O¤©Ógqºõ®uÍ[Š}ÁÙ´ÄU 7Ü^J[•^³·ª ¦½4ƒ‹FñLñÄ:ÿÓì#¨ÍŒb¥}dÌ–£Òóé•еTßn:µ¨ÆÖ“XfñJz ë÷ˆãìèTîî©W™Ú Hîk>Úüì£ÜW¼$äøô°¥»ïçpCãÑV'ªFÁ¿_“f AzÝ<”¾0 7¤Õ°© ”*tÇÀþ+l¡ñùŽ©‚+™;¦Ð ¦ßæ'“–=èL£cö³ž®ÙÅÛeÁ8Û›ð¼ØOç$3Rwà •[Ž7Ä%ië‹\œ×ÑÅYSŽ$Ê“m~¼Ôå€ )ÑŸ·˜ÜI{Ì L”9tr—£låk¼ø}ˆ½Ïþ`V†_àÆŽ²ç¢é.Î\wÎÄE²?s2/j ÂÛ”ñöJ$gZÇí± ø<7“Å6‚LwÓlê¾í0hx)Ñwä"íçM¤.½²c4æÓï¶äú$¦1ïÇœ‰¹è?’H“´Eˆì…)pßv ¶Í4‡†ˆï¬™M4œTXAb t ÝV”Y¶uçÔÈÐÿ4ϧÌÞ‡‡ÓŠpXT‚Î4(Áß­Lµ‘{öy¾©ÒËvñ™q)ž!áÓ>áù iðZÎ}³_«bcž0ܵ™ô®ü –ãdFܴᯂ½aÀñgòšŸ ß¡*Y,<\™‡ÏfÓU-ÍpþH]þs&kîŒÝåø¯ë.Þ°ž&ÎÛ «íÜàÄa|Ñ(I<¿GRq«HVóô ®è.CPqMãÈ»M•ïAY šÖÄFBÉf¶îKøYFÉɰg//=>_žj–š£iôk,2p&¥¤®}XÏU'³´eè}‰Ó :BÍußAÚêƒPëO–_ŸEH)[Gþ|$tÍ×:h.ZM“î‰Ò¼¶æ»b5BçÓµÎÞt)Ú“°<š²q7)l°§?ŒSÞ-G‡^ÐP}úÂÏ»ª,Hkq³õþ².ì/¾Ÿ` ¼¾óWÚ½zθ‡Õõq«_…ÇŽu0Ï›}!çâ ¾„‹xé–q–]MÖ埤Z®¼¤Ùî0ö÷í%ÆO¤hO¨3ë,w„ÞªÉÁ2Ÿ|1æÂ¼­òt­é;8´:žéù°ü>öƒÈú›0=˜äÔnBÓçGœ=áp)Çm¶ÌÁ•féìJ3IH¿;›.6§mV­Àli„Û_Òhäà(lp€k¶±ºŸ™>˜ø†-ɦÝËM¡?M“²×®c°Í830-¬­åCÉ_šogjz„üš/DŸP¢[v.À²áqXøz:{cQ&zÕ#®¬oI} Ê›lŒýž-ؽÑ(>“¡ÕEvxXô žî=‡‹s«š#™NåZäß)Ž38&´%,šjuépù,¸iÒ‹°Ý²WíÙ#@.u§§¶oÀì‘ HëšÍŽØBƒ¬ÐW!È;âHÍÍ…È ª’] ÓÓ¦ÃYû[ wR“¯&p|M+p7s}$ åêf„íP^rªl*Êñé‘[ÆEpo”uò«ÆM‡O§WÇñÉu{xܬˆ¾óŽaY™ýT˜~+ФI#0ëÂʯ™Í=Ó°Ö'ˆ²ñQ0zZ!H®× úÏ6’kÉDwÜÓ°¦øÝi—G%äH>@Þòj––µbc;µß´ Ó#ÆØySñâ¼?ŒXrh\Õ+\;Ï”„é”ÁiðGËôO°µA cŒ$Ȭ•*ôƒ9QWÆÖÈD‚ðcúNj Ï;n" Õ>°÷nçT@ '¼exÔþ²kåZ@þÁ,>» f9d‘®[˜moµH¼t1ª¹ó0ÒOç œÔlZ^äMúú3å5£XWH³VGap  ŠZ˜Ð*çÇL™÷"è™#‡?×߯ܝ…à5g+T <°K†^§ hP•Šlá…Q×·h´g;#(]£&.-ì%Ënt)’'®9K Û!ƒýOÃ,«|ž§ŠÑ×qSèÑEÓqÿ« îî^?˜½_WnþÇäLbrXaI.„e±i©àæ Ù WÆ‚±vÔŸF¯Áܹ$®u}‘•‡›^ÒÃϦãð Vi¾cÎù‰K*XïS9ø1ë'ó-RU ɻשÌ2ýLX•X)øåG#™]‚f>¼¬vR }à<‡y³À”Õ2ìP:ò«}‡Ö°Tï‹ x¯M”bQ¬4i;mŠo~ÉÐ ¾kv•zØ5åÐÝyÃï™Ö&m×™‡â‡®Ã…½«PÑ £Ë¢¼Åi¨¡05^³ÌVdå/6…~†Yoñvf&œysŒä˜¿Û†Ðåêˆßë²-Ùø¶p3übvÿòs´ûñJŒ’¦õ A¸bæ,09_Êñ›åHG\‹&ãN 6XÝ„§‡˜w¿é~£ÅT¦Ã™4Ž¿CO³$v^J|!ÎW´}E^¾{Ál~Ê$?áõ¥ì¡'ÖÜ©K´Ðû\$½ªÿOÛƒqš|1S`¯4É#ò©Î² ¸i@õäËàbÎy[ùº·ö‚Èk5Zwf­Ÿä­‰5éôÝ”EøiÕÜq$€˜ÌU"lýLXWóõÞ¾„¿&©Ü×òÞx;೨ *wá±±9äq‘áÿ'öÛQþœo(k5z÷娱³n}2 ÞÙ±šGÐð@šTiµ1‡±~ŵÌs³rP¿£BG$¾àó…gaÏÑÝärG®ÔUÁoA¾8ª"=ȱs†D£ˆâzÓhìzÞ›Ñè6Ù¹Ù*·èÁw^6»‹K¸²½æ;ZÞdôÄåè\%aö¯À-¸°ÿËdñÐÿ4Ìj;ÔÈ!µÇxt¸‘•ÝJÈúå—Ù=ú@ëÔ,h_o V=w¡MRtMÖš^À_oÇñ´ù1"&77«“©¥ëq±œ8*d˜áâÛ Ti/Õ,%MtM9|Š”(ø£8ÏQö` Nîùc¡Xpl¸kE¼.@—ت\7C¿rÄÔÌŒZÝÞBC+DI¦&CÅ}ÁÎYÔÞ"ñçªÒq¥XÖA._p—Д"{º5SCJs™Ë›tºìvt$˜lãJÁ»[as…$ý)2…tZ\…œåX÷à ¯èb~IƒàÓ"=¢„ЧŠáª«ißÀ‡NTüº~¶ÍànôoçZx’T ½·åiv¢ ˆŠcB¦*­¿œƒƒ“¶‰t¥r;é²¶mðÊ¢»Ÿ‚CÍ|ïhJ•.àÎð1äÎjAµÙ4†{$|¤éÄÇL}H_¡23ìTcÇ•©¥W ’ˆ8›SÇÿ1’=¸5§ÞÞ| ÇY=ö¸„î ïÓzhnò>’©JG}*hÆu%2Ý/¯½ §g]¨VOVÉÇáñÄþ¼m|©ö+tpnX67l!¿[›!r0“óÐÒhkjZì ÿ}ŠËªàôÄgÖzš-ºÎŸG!üMi6‘Ç­¾¤%Çê‹ è õõ´'ù>Zæ†×3³X³a^š¼9œ]JàÙ<€"P:¸Œ¼¸¦IÞŸã'EZæûó<ì˜CyÝVÒÞÌ2äl`Èõ-GHŸ ´Œþ…ÒNaº[0Šì©žMä¢IèÙ}pÈä,Џķ|fT’ו©™ŸO'ƒ¯‘õ}å@j~|bý;‡Ø’» *ùnµ¤Cž¯LR‚j5h“CKð.¦ÿÜÎ0ÎãlŸB*~iÜH>åNà »èâ5G±Õu9ho )^éäûWo”öJúï<ÌêÓ±P¥Â<ƒªV‘¯JjR3Æp¨oÚŠïŒÍÉËe6dÅœ(îÞS'q!{™Q±ë‚€BdßÅ(pË®¯¤9+î³;K™®—aäòø1Èo]Ènã`{KY8¿TŒÑz~çùSg¿ãì›sF4Y\˜ò, #Ž„Li¨õÒvÎ]/K²÷°Ä$ŸnÆõ‹ ñÅ }*ô£–{w+?;RŠ{—«r¹dÞÇHª£TųÃÿ»iÌ Eë6¦ëS ìž9ã)D™°§™ç S±5Á™?Ïâùk êI löÉ`•^;B­{#ÊFÆ÷®*øý¶9¹¡!ƒéSó°à¾2ié+fvYŠÃ®‹çÁßÏŠxõªáóòÅØï¦G6¯†J­HØ÷5k2׉ “Ü âBa`røPé¼:™ùWÞ†%0g75c—kl¨€/ÍddÿÎ͆"œx´ÃSʘ;S3°ªÊ”úÞ–†½ŸµÈÝ : ÅjÎÑ"÷¡Žk$ŠáÞŠ†Ž„¥‰Œô¢·ºáêÿ5¢ý‹üQ5I’Èë(Ó/Qfx»Vßâò'ôãçâéäð‘J¸x 6íÅ…V¶äâ€ÍÌ9Ú_/dÛœ}i׈( àMeÈ’:²lS hߎÑ:‚TÇØƒ½ªœÊ©uŠ„ÿt×ûŸÆ2Çœ¤™pƒª;¬ÃD6÷ÂT­eøl+ËpѬ=„­ú¨þ[ù¸‡sL¨U›U?ÏKUÖî"µV6¬Ñz¢V¼÷-? •)#Ï&c½T>}U±øþ`„æ0N{‰¶7Å!¥œÖGQÝ2„çI.;òç&ÎøÌì›;‡¼tˆ &âs±$†þ.ÁŽü›ð5½ñRˆåÖâ·ó©iï&l lÆ> kÒÂKå÷À¦Â`è»ÈKR\‰¿ÝÌÀbÔXMk×ñ ÖÆ# óÎ.|ÈŽ,ß w"ê±çj1¾Û6ÿ?Í6yØœI¦ ‘a»ºm€£éu ŽðÄÓýçíÉó^d¼ 0‡Ra=ê0+(z âçÍËÉþ$õQ9ãÓö’9òª–Þ jæXòHÍ?Ï©ñÜ×Da8”ò§Âõ!* Åó Hi­émŽD¥3¹ðÖ*ôuK’Ó›)w$p¯70¢nð¨ç8lRQ¤ ù? ç¶7ýø*Š|Q÷¤ámD©¢h2G“M{§Q‰Þdµ‚0fÓ(íy›E>ÞˤGì@ƒØydX4=Ž%³ÇØMwÃh¹5?•æk…òïáP¨,qÙœŒÁØ‹“>¿éÖ(™²—Y ³FÇû™Ñå§`ï…‹kþaùz÷†3•ò+•p<%øØÔª@Z÷e ¨¯Eoë÷¡¸L.ªÄ¢Â'/zÂ]§wŒi™ö•óŸ^:²â<ãnËn®& ߢwç,¢î1‡S>±aý ìš'.$­‚¹”ÑzuqUëÀÁâÄ“kE®x®>BnlxÏúHKVÌOb¥¦&Ð_ó_à–ëàXÔ fkÓ;\þu'ìsáå¬ÝÍG¸‘7–ž´Ó¥ó>Las¶—‚ÖË,ø~éf".Ú…H|£ªMS›ü§¿F›~!º«ðãÚÿšýþ°ÕÛöˆs/ÍŸ¤ã§S ž× D†˜GŠ 9‡rÝ¡|7$ÊQH 37h V­x…ÁKe‰N4£ôÄr%÷Ó+egØK/&¤aØÅÐûÑ Ü Àjûæ åeáñÁ?þo.Õn+@ÏOêÄÆ>f·V‚²™!sˆäŒÿõOæW9óùZäøÎ4rc„‡^ Š#–fBŸ½ÃIØŸa”“%ý'ØQóKì̃y(½Ì„|õ¹ ¡ è›xlda—ïc›îào¯ äUKÃ-sûþ ÷¸vÇ_®ì§1vÍý}`ñŒ!ÊæJМ™DM]Ëðü8übåèd\Ž|Ž§Ñ“ÓÀŒØ¿Á¿•ë°ñ4Mïņxu*©Ô¢ÑÓ¶—ëu?ÿĈǢôS´8–.gD×ÉÓÂÎPjçùˆ){¼ |ûöÑâ¿Ð¶p9-W„HÅ÷8”™ŒãÏŽÀ©¨à×N¹³Ú!`u5ùð¾µqäÿŠßòùHßž÷p²¬ÙùÒ¼Ó÷8«¦.¤åŠdã<_Îwp˜óñ½ Yök”õéÒ >ìSšŽ-U%('½¢/‹M·aо%yâ=Ÿž|Z' “éχSéˆà¬#;h Ÿ MPªÁö$†&ÿüÀzO¥qº·MglßK7z±8Š[0ûû.,IO §†‘+ú"Œ˜ã)-? G­xéÐÑCäëºÙ4ª= §_‚éãÉŒl™0±u¸!sf÷‚ÓŒ8x3XÆÛ<ÄdŸÿ ªyÇp}Œ=¦Çe0&#+h«C¾¾oÀ!W°lb7o§×Ú°màÓ½žÆ´2þÿòñhê~bVÍC_¼Ň{‚MS!¡X¤Ê$0GXF¡%…çFÑøK¦äŒ‰:;¥áL ‡ÅIeÇrhœ‹:¤éZ¼(»H—Õž$doà…yÅÄt‘9ÝÜO¿ž‰ÅÉ}Øë>IÄâ„ UQk!®î$ôi>×y®òÅÆAd]Úÿ{DO)—ņÏÌ—³òLJx-ØðQ#»©ø¶¾‰ù÷é>K3¦.A‘lƒÄ•ûoMâÒ»®ñÒäÖ±äÝ»"ºvï9l¾…Êá:˜‚0~†13ÊÁ¿—f’ ÑKì©dvaÀ ¨Û[‘y~½6Á6„TMY"/³z4ËàSËLÌVpÇ‘U#ÈÉ¢ÃÚ§ñéž½˜>aFMä‘Ô·sIÛ ±Å„5¸® ßÇZX]µOpi×+(sŸI³2¡ýìø9w-\Ï 1i0eq ~Ýž€‚ÇÊQµ!…4,…P«=òeð—Ótúèe,<ÞˆïÅç’æÙe¸q®ɵ1¢m%mÐpŽÎ E ­æK×ço ƒ)Þå “3)ëXÿpAúyÿ˜Ñ„©÷íÈ’Ðf_À³Êͤ>ÞDŠ-4ñzF|ú0Ìv¼K€úàßA>²©&‡x«¾Àþ*1n[Ç"\“@.[—€Î‰àù:‹ò¶V¡‰1ðX@”¾­#…ò˜Ç“G>®¹†oÎÕ°Ö¯76àw›;ÌómMXøü}ì7†S<ŨßuØ4'Þ4· \Vbj,c\¹o.kb^©Ê‘Îb¼6{-+wÛ+b¡hN4q誩ְ6¹8E5Ìù÷—0|C„©e}ŸŽG”zp5ç:­ÍÊ\‰a Ƥðª¸\J›¬e–58:W¿hãBrƒ¹ÝãI[ÌI™ó— 9( 3&ý#¢OÔ§HcÃÄf£ùZzjÍ:G¼Å‹çÞ3a6:´Rö-N¿$ ¢n@A½;ý*jE\?ø¢Ä•oð°\r²>ïƒ+¿ÃIVáü|*оTÒ >1I¸$x{»Xê¯K¶XÊÑÍû¿@Ó´ë¸fópRÓ`Fv$ #¿×ç:D¬û*î} uu·qîsa:8ø¼;Þ£ºq )áܽÌú­©„®ÒKÌÞ¿ÏáoZ¦qZ£ëBÆŠW`yÈ”§ã·ç Mî:ÇØYL1Ûf$M6¾ÄY‹ßÔcÁ˜Þ4ƺ™f ǃ|7Ÿb.Óð–À Öju§ö‡#q5k8Ø/L/iÝÅÏ=ZÔoe.ÐÅC« ˜ƒu­ØãÎÐsQÙ˜±4=ípØôr ~oϦñ2s/‘À¿[0,X€Ã+pÑ·~ÛÍœ'Œ¦ì-öÚK ö2/ýÓ{˜]zá9ófÏdžªõ,L¿µñXTƒsgIÒú Ir\k˜y˜>•(ÄE`±›8·jc•xƒàÎ)#ºçà%fû1rèãe–U9‡–Myß²V»¡s./|ú¢KÌŠŸ9”½7Ä ñ·`ÏN†¦¥å±KR.@ý¯44 nôpÅ;ÆéQ7c(²šôŒ¾G k’<};Þ2%o¼ÕIìèiæ=K.*Q°9ŽÇ6½bã]ƒÈ>£QŒ›xWŸ\q±<ýɵ^†þ~Î'Aš6&Læ#û®î2ê>`:±…¬R4Çwi;hÛõ”rñ Ò/bÐ7$ø´·‘5ÂŒ@süìLù$2vø!rÿùaáƒÕtäM9tªà–Çm¸óÝM±<¾ûÇ!iÏsv© &)]f*®×cÁ—õŒÃÄu"Rø‚½¢©üùDÛaµÊ 1zâ¸b¸—Ùóš…ƒ¹ 'Ì~ €þ¼z Õubéâ;ó0N%É«tЛîN¹Ö? {· kt ¿¥´b[Ñs–÷ž Ýz/—œôÑ£[`§ž%õ5&=¡dÞ#KÂèÃÉ¿ñǹéøÐî8ô bW…Ê[ÄÑOb¸hc9£675m«¹…ón’¹FòøJ²{»9Ô$O‚§R1ƒ.Ú­¾$ºÅÈù¼lxTÁ¬ÊÛÁZ¥´qvg†©éŒõ!’ß/Âaa[©é*ù0ú}˜Ã4‰&¤Ÿ~`mÍY”Ë,»¶ WZNWŸIÓ£ùhwµ5'Õç,¾¹ñ’#tWŠ‹Õás༺)µÞl†ã€ožÌ}ÞÇ 3ðaÉ$6Ñï5~VÇÍ$—G× åÒù¾§Ó°¸Ý­äIÂç3 —vˆf?˜€ƒµ:¹9t®ÅÁËOnçŠóì4Š Ò‚òg tÎïÎ=ݲÈd6›6è[z ¶˜Ù‘!#réÉ6J?í…6øÇd³)àmdA½ö¶À¨_.×m×DA1M̃®yü¤IÓ…0¹g°û` Ä×j;Ùÿ´Ôçô~³[g‹P—LÈàÅû;4Øbú·uæWKòëÚcfn—1êØ„…'-q¿*Û-NĵN¢k[ 0¦Yì^cKðY Çü/¡Õ¿Hˆ l‚|~^|ªeBÏ/á!?ïÜgËK–ÁHãrüOoülžÌÍ`2ŠãéÜãJ+`p? Üy…½×ÝËypï4ÄdÖcÂ=O&»ÕœªnÄ×âZëö[r«¹Í3ï~C?÷mXO×GZ"ŸÄc(0"à½{²OY×2)[&yw­Ùx(…^ÚÃèäóÜt³n½j…+JV»X›B[pÖUÁ·´C~$™)|)Ê‹¸B-h¡û5ìçOç\;ò bdrØìfY:Õ`:\ŸÄ:û„3¹QœŸì<-Sòä¥*pë¾0³æÄ‚­æ?¨‘Úö^.Ä$[ìëÂÞisˆçZ]¢ðc4ÙɱPd’?{{iã¨n"ð’eGéå)¦T¹é:\ÜiD8¬ÝÃÕo®À­gÀqÞzŽšƒ±¸,‡]ÏÂYW+¢õa è‰JÂüµZ”p¿nà{Á|Ÿ¦I“š¨`û)ÈÄŽŽ\ÈßÃÖk*Њ³ü$ÿârò¦Û™f½ýÁš?œý†ã¬ Ÿ8ýUsž-WûÉÒüh¢$Îéä]IíÌ)›#¯G·ûh’¹=CŒÅ“d¢^H>$²ªÁ³Ùíûå‰o<®0¾ÿ¶»·\-ª˜É¸6óÀXoJ½Ä8[&ù·:ýçCŽÌšŽ| JYùóx¤û'\L¾ÆѨ= Ô4áÊÁLöÙ‡XÖGèlð‰¡m=› ýÀ ¼j%JC‰´PÀ’¬ÛêKÌuD‰ÚÑ~U5†;ŸœÝÄ%.⎴[öÊÿý¤•Oÿê^ß ]XQ×S¸s°‹BÍp³›äW„sg~2©kã©ÿ•ô6`ê0¿:‚Ïgw9l\ŒóKº`p®Zò›¹/àbA4“2æ?¾í§þ ›¹1Ó“ßÖDz]üÚ5¥¥Òôo¼¸ÎíÛtWI]ž£ :O€tõI?O qÁ¦XÌ. GœùðW‹0-OŸ ŸFñêÈ<„B|šdÓN4Ž6Ãü`!üW| n,\AsgNc#ÞÍÄ ‹œ+Ê€òp´ÞÒ8Xu‡ôüáÜËÁc4©ò±VÐvχ“3›¸ÒïÓ`£ÞfdnåáhÒZø]g…®/ð÷Í\¨¶arrÞsk,gBðhc2G‚¼—cÕ·¸BÅ5rMÏ‚ê÷,§[¯úce]÷ÀEˆ |ÆŒ{nÀ! vé mÂ]_ÎõB —Tg1bY¶ðfkðý@Wsöa%/}<·—%&¤¢s%ñI~{ò^AhÜvÜ7.J?¹‰RPä^XWÈý±å1[ר…ßÒfï`~*a™ GãCaÖêÐH^BÕú©äÏi4´©û}uò]!gțкv=2¿ë&ë¶iÆ Dà½Fu:ÃÃ…™q&î:È@ðóy,OÍ\ú$á9÷Ñ”žû<•¬ ?É&œT 9­Φb3 w‘^"'ôÎqÔ[ᇻv×€ÂÍù„ñ0¡­ZtÚ#Ü%,M v¨€òôŽ,ñ?ËOÕ§ŒaOBSëØp÷0z™ŸòëN#qSƒñXc9|¯„E·Ï‘÷ÍÐæA7¯h‡iƒвS‹^ðþÅ6l_@GD$iù':5Hïj6¢ mà^`5ê±íÚ3ˆjbµ¬MÉÅ é–gª° A‚ñQŸ ëÊB˜]™¸³¶‹Û¡•> Ù¹žêñ܃â0rÈû*ZœGûÃúñµ±%Ñý³‚¬ŽˆÁ™ï2ûç~½ÑM¬Ÿr4è®*ƒ¸Ð0m÷€Ã–"œŒôaXñ,81¥ìÂŽ…äâøH^ˆÒŠPPŸ@óþ!†šƒö ðÏh:ÑsìþoúP~«VäjÂ΂´wý²1v;³"RmÖƒXöú³Y€)÷šŒù\¿²¿?“b޳Š{ÆqÚno&‚U±/Nä?oi÷HG%Y.†¹ì`ïnèåj†f˾ÖPQÁ"Fåh x½§¢é*И³ƒ¦4d3‡j×£³ì ‰…ÁQ:=ª-žú3u³€?b —ÖÀðˆQ(Zȶ­[“±q­‹wã‹ ©X;â¡'ˆM² ô ‡t‰”Þ0Î.°Æˆü£­¢]X«îÍÔ͹K.y“õ·aâëc®™¯ µ÷<Ïš¬ù}õ܃‡“É¡¶0JHÇG¥ðñ '3;‡:îqཡÔê$©L¿!¹²† YHbåܳÀ7G L^æÂ³ÝÚdíy>ä)+«Lõ»N£|ˆ"ËÚC÷ŽÔ2B27Ù¿ïœéËͯÝ[†‹:Pàò×p¦X‘F3Ê`2N""û gÛýÆî©Ð°æ=ìþŒñÞM€FS&9‹6¾7èÀe Ž{á$: —ÀÇ%•¸:ùÖÌ=Åv¹"å¿&`ìû+èí5¤ç¹RôjP8SÃá}’±y&ª/ñÆŸ÷0æ6Áo_4nȡ޲ôO¨Ò¦«ì×&Iú ýHõ9æß,;Zêu‡þÌü\=‹w4áö>Ö7¸ÿ? 2íO“ÃÆ¥Îô}äZ½¥G¦so2X(ÇaÔÂ@´k:ý·ß„‘¼lǼò^JUçÆ5ÚOWí$÷+g­Bh(ÔaÙN}–(¢Ñ¢Í ÖZ‚ñ¶à.%f_éOÎ7&£~‚6¹?å$Ô‹1ikS‰ò"]rag;«na€)—`Üy,"’عڧah…q7ÝC¥.çàwÁ8jÀ5¢§†¤èà·‡0w‡/•è9N•™;д “ û°)n¥nxë6?øóÌG„*Z¾ˆm÷+o8ƒ³½0u=¾îõ%5F­P–saæoWTK%#Ò;ÈØ¹ *Ò;€9É7a¼¸Ý[·Ë$h™a=n°Jgó§ ™o|4[þ<+²Ø!<Ì=µ!¢ê•(Ÿ»“‚½›ì>ß7f$æ?È;ïÝÜWÖFÔUçÎ ùìP7Ó…Ñ"N‹Šôè̼Ì_–äõ MÈ‘SÅØÛNøÎáSrI–L8,—•É”¬BŽàºjÏHéßš™c½‰Ì1 rÓs Ô}_J'ϳ˶Ɣ±ŠÁŸP{•þÿµÄîöB ~ß×~)6–À\É$ºÁ±%ãã céb¡@øúSü9$iF&“.ºÅ$Œ,xi޲†øšØ3óÄ…Q¡ó‹xâ»#눔Ž8}™#@ K¾àÒÓÇ¥ KrhBoÍ=ÁÆ Rzçÿ»H½È. VʺèüÖžÓþL o¿½¡½sè-ÿÐ+’ŽÞçÝPøÌ/Œ+}# 0=F–æ_Æ£Ê4Ðìž¼,D: ²“J6>‹ Q.…ÄÐð­Sñš´*iJTdÿºŸƒµWbCÖ“•7É‚ü(àITRöЖÅ[ACÖ’P-˜ýÀ}G¹ùE{hŽöΒñóÒ\­‹£zk$©Cd$Ýý7èKÒ—‹šqy@f‹áßßÝØùåun)'ÆiÌ•}Ý*y€\H?7O.¡×ëeIýõ2\_WÈþ8·•¾I7¡‡åR1™š‘=áŸññL ²ÌΆ®ëä¡ýOPƒ¡D¸x\Ѝ­Z«–o†¬UOa@Vªºmý‰Ñ9¥h5Ý’æôS™£™ôÅ u|+ùVþá‡åw£é¼‹¨à Aò›/–ظ},y„¸.Ê'’+³Áåâ3µa&%‹Ê÷÷ Œ·¯{€vöz2îBû§-¥_â7`ᮟð÷厌÷²+ ‘ãìèG}²¹Oš”k8Ї(ù£ ;gѬ9.´Èa&'ÄÿÚ/(ó*’æ¶?F:“~ͼÏîíAõÑøÉ(l:óŠ»ó[HÑ ßõ ä àÞÒ5¢‰oÊ0ã8o5éºü‘ºÙžÁ— Ç`éìf^WG(ñ#3 @üËþ§/þ{U‡ªíê„iaB–„GyÎðÌI’1â5"R¢pYo:îè×ÅbÕà-ÚŠO¹¿•‹ Þ°lS KÄÑ%]äUù òÏ>y{¨‰±vŸ‡;m Hrú6Èü£E¯ÜåкÔEänkkPb.š · Ay»; /›Šý¥è½µùÜ{¼Þ„­˜Mîaì±üqêd›÷$—9s¦¾xÄjh4BRÔ-0\ó»«`ÛN[rl÷k¬;<jo¨ÍÀ«p:Ý‘m²ºÄO™B–ê·3#²‰h¸Ø†J±åØñt½ªdewBñ›_ÜWRqÑU@W Xˇû«Sõ5[IJÕCðÓÌ„}K|pÑê,˜WíÄnéFü1ç}ë‡CSàÞ= Xzö8ûC„ƒ,{Vž|ô¯„Ò¦ð±ˆÐÆKØQÙKŒõú$ä½» g]ŸŠ ²XUx €P™qÈrÀgO¾°:³ÕÉÛÛgXµõ-øæ­ ªo¥òÒUØ›) ëßõ£Ø@ Ãþë ÜŒýâôZÈW”»Ž6K§@@¢0zz+ýF—ðsP²rÝ+ £߸ÕIÊ {¤©õ¾—{G÷YÄè¤øUBep<è7ê8TÃwz™ý|ö ñ ˆƒ[G¤È¶|E:åÁ&æÛ9ÞYˆ¿÷eLæCcßKÃ’ïT?4rsbÉ·nËlF’üùtâ¤0.ë:$šñÒÍy°1ó v¦ˆÑ‚ð!Ì—½x‡êgӈ̳ô^ÙIN“çGüÒóoÎþ eó&ûÃú™¸­Wž©÷¡9‚a\&;O‘_‹Î‘ÈÓèíBrVØîÜ'¹,TOeD7’/]6¤:_ˆzê™a P3Ì4>ç0gßeîÀÌœzkæ‘«SæÓ…k¢ÑÖidèón0x·®ZF{x@oýbÊÀa»DÐJÏ…ykÂóò׸5â žk6Gù¸œóáê”%•°óaí§Ó|Ž;Ò¯9ì½Óæ¸?}Ÿ¬ãµóK@ˆ—Ÿ¨É}n¥Ã.(„Úî2ʧà`ôÒnI]{Ø:e-ÖÌyYÄ;Äì½ü ­ZÅ1â‘:ü„a¾¨“Æ_Æd©úqrOþ'«Åë ¡·ý™1±ö³Vò2C•]iKtã?bF Z߼íñ–ùh³‚ Ù¸–î žOÚu„èeÿF˜Ãc†š«!,°ÒáàKmFe —±¿w#”AYC{¢†ðßÇ˜É %ªÛ×QAÉ4ùê:%oÕ×YLÖ-ãÐöC¨@A!;RbA]SééM÷PíÔAºE­ý³Ü^0àqë4ç©Rq·´%"sœ¸b©AØ42ίH/èñ p@sì~8º œ’ÖSå#\* îEÐzrlqØŽžëžÉÈÉš>€ÿǼ¯~Îé4§¼—³WÓpŽe5ÃîtÉx¯äIj# ã\…É=4À ­>»"b3 ón+:ÞoWã³ì•¥Ñ€ÇÐ/Iç_”£svMÇaßbøüHìN4cÆ3šµ(–r¯RÉJU(–“#­k¥È óAV·78S÷³×QˆøÙgszÜŠp®Ùôé"$;¯ûÏK+^€ój½`Òô†Ý®˜…H‡ž`öyâyW˜ûÈð]†KW/&¤¥ü¸"t¢‚¬ž5Δ~é‡ß‚DÓÛ€~Ïq¤Qû¹øQ’°•áï˜×=¼äU ©ÁpüÕlê¯v3r‘•¾ ªÝ/ñºÛËS}€[&çÏ\,È¥@’±¸—ðŒ€ÊAæøƒ±Z»\¦¬ gÞkõ€™t£ÞWUB©5nÄJ¿V¯fÖŽ_Ã…b:´6Yƒ$ù8Ð4½g$LØ™¼Ü¡†·¯Âº0Sð®θ½8€»ÝtÉôYÀíÃKðUŠ$ÿùVîŠåxˆÞç&ïA£Ò\ÌnÂl&“vš}C*ê©;¦¹ÕÓiÄ…D3'?\ƒ‡¥’üβ U2ƒAäaõnR+¾Ÿ}î˜ÛZéló~V[.K9¢ÎGoô¢Qš&2ãDÿÎ}†_o+EËAtwØïÝÉ5ýHüt—®-ÏÅ’ñU¤ã¥’çndô—½n{•&´‘, Ì¡›§%ÓRÛ:Øp²Œæûèûf‘æûi#o ý}ai´Ð$öÇÑO|«/U†ëíIÜw>ZvM–(GîdŽ £¬Ä[¸th5P'[Yyr=5’Ú”ÇPa…õh»_Ÿ˜uÉá]™MÈ#_‰Ý†Ëéþ ©Ô´aˆðÿr¦®Ñãì‰3b¨^9ÌÔè.¡ïÓ“Üä”É}”jO%ª£`$…&Jv2Å­/Ù½êÕLõ\µO"*%C0ô£äìÌhçw‚eߢiõf>R\Þ:óŒ2d®U¢‹/÷àžÐLéôyt“Cþ˜°N‡þ툂ëC@‰K#·\†EcjÐó;·MŽ7!×f6ˆ÷´õq¸Ü ÎðË8fÁt6¢–ÛWQ—Èi%ABý ™NÈ¿½–øÝåæ]Ç»+g“ó3ˆ”|ŽÙ†%3éŪ5ôÚHò%p5ýðOŠHÜ û>Fc>êÏ«A]Ôh{ªœJ}ÇÝ.dD/ÎJgÎ…T°Ës#Éîèãð¥*Ì5G˜)kíÈÄ.¨z!¿+™í·ìIÐ%²Ð¼jÙâø£7Ü–³uìç+¸°l½¶D’k+Ë âw/»YEL}8Œ‹*ç° GíS ¾¶äm#îúÑB†Š•ˆ„à:ú…¯.\äü§y>~qU\n‡Ÿ* èße äÁ*r†o î¶/eöò-]lxô‡¿rÿóW¾Ð’ÅtËη°¶Þ3‰l Ü‹O¦Ñò¸Ë ,C¼þÍ„çÐà K~ ‹»µÂ0G9-vI%Çy¨öùV(ŒñÀÞÍðu½1ú÷ýaçLt;ûä¶8¹§;¦Ökéf穌?MÉ6grÓ.±ÑÝdá¡!˜bý3¸ë¨ÄP-ýZ 9Üeà|Ï„Œy$C¨G73îRÆ ‰d˜#@x Ž0ÑKð£ëIúüí7œšü•;t˜<ÿ–BFEL°ÉV€$Ö43§² ÐîÇ'Lýº„D,> a Â$¼/YÑr \~ýãëèÌ—ŸÉ¶Ø_ ´/=Då±ô¤)Éq…¼ÚúºuÂn/QjФ;t3goaCãRd÷8Íryb3‡v†ˆ’žþDFî)= Fß`©œ)]:° \ÖÅÈíªäˆÏj¡Ö™ú»©ÚÅRÊ“v ›ÖÁít |^¥FÊF\è»Cÿ𺚊ún¥Ví7 iÓF¢q"2dé­kªË`%¿ ˆX—bãɪgý“ xP7÷€¯ü’oú‰ðþÛ@¬§£Ç+ÈáÕ;ˆ€¸1hd4é§sAûÑ rFÎ Wdå@®ÐNì =OÌËå¨ÍƇìA2þ£ ò+œÉ>ƒÇDè¡GÐtáin ý;Ó,‰c“5ô+Ð97™»1édL)Ú‰!"‰Ôë½óµÈ‚ÄÜUä®”-=m²‚.l¥w¥šáåEJlÖ.¦Þ¡ï/i½O.ÛS&?—$»+>eÑ-ý¿Ø›‰âèyð>L&¡*Í´}Ñ#È]ûLŽM¼:&ór9ÕXQH»\†QÝÕ˜¤7ì®L!_M…ó¸Ì»œK-kΔÁÖc¸Ú؈.è÷%[©ÙžRÐLÜ·ö¨ÓŸ‰O¸ „ÈöÞ[ìˆÁ{<ÿZC¶§cÄOlfV9kœNÜưÒâàïd@êþÀÚùÓ髽»±ägžM EG¯N8P@¡sçýnO^[ R™Æ+xu³ý°²·–«`Ó+ ÎÎòtëˆ ¾u †³ YóDl…<ž] †•NéðcU<%d÷¯.&5d m´¹„ï÷5à~±dtÛ&ŒœNã>‘TŒšþÛÞ2ŒvR±¾nvÃLrÚ0ÚÊ}­5›ðß§_à÷ò9,»Í6¾ÈL¤Ì Ó+ܱPã;+þãcUµ#TŸ Žk6Só"rË¿:o~GÚãÉÊWyÐ/ÁßaeY0“ZÐÆž®ç`¯5•´=Mö.H£{Èj²Xû2„Ї£ƒùSØÉoŠ‚œ0îÆ„‘Ø%ÃíO“†áŒËàÓ¨…Išºt´û7Ç©7L!ÍóuèŸ }ôošÿMqÖêT²é¬ í^ÂC¢´fK·`£Û\ðJå!Y¶¿.ïÞß bNCx{#LYKæw%§v¢o3ñú¼¿(ìÄWO¢\Y)®=Aßγ‚µýŸ9FAfL¼wØŸH‡;ÏUɰ_6>–Óáh:5²'N8c´© žçÙC'€p± £{'™©,ïÄ›¾w9õ›39ŸD”¨b¤¹Ú¯U.°ðªØÏ’¡k®Ï$yÈá-¼äá*{6êÙI¦Ù„Ež°ÒwÈ™=RhØ‘Nó'³ms¼àuëC0—ßHïÙ¾cNµ­ÆucL~H‡ÿçîͰ²á6œŽÆ“Ú.°9o™.qOet3Sf²/´¢˜f¢Dô/º- AAÅ#8|csEû:A!l/–$uQ TÖÀ¾aî¨=e s˜f¥ì¼æÌ¾Üï\ѦV8ñ@–ì±/fÎÍ¿ /ÐÁp1¼ˆ(€™ŸÖ³ ¾´§Å:ò]ÛðÌ]9¥ ÷*ðS†ãŒ{xÕÁ¢‰—9¶Ð”ÞfOÌHÅÑ-w౩ä?§Ä ‘±%ÌÆþ…¨°,ýã%Ϩ'»ÛçcôîE`s6•„Iû¡o§)s¦N…Ãh“.„KsP÷f©t‡…×̨¯ì6Ö?BßÇS Åe ÒQG›ß)´Ê:–¦hðÑsOû¿z9ù\hF…ר“y)êpižjÐt.œ{b9íªR¥YÎ}Dá@|u.‡‹†¡Žw3)Ÿ*@Ãܱ¡£ vŒ®¤]«­É¾)”GÄ‹ª–»óã`SâLThÄkc8ÞlKÝìµÑæš>† Ç‚ôñ0' N¿D/CéÙId¦Ée¼øô<™§\F¯þ®“ˆ†ú+ie¸ itÛNxÚ‹ˆòæ×¨3°ŽMÎ6çÑPR¿} k¾W$=öªhûÁ¾ªè7m˜®—¿ÉÙU$f¾5é•· §%`­Fåkʸ8‡ˆ‚·[Ù­E¼ZÎbÔerZ4Éãd:åñ!ܶd9/ý ]÷ a«¨9Ù—õ[®@½è64H1ƒãsV@Þwª›GP¼ëGãïCo[ÐM÷(KKþÀÉKS ¶Y‚Ngê˜kÂѵé-9mÙJ¶U >è%¡Þ ÈñÓÊxMÞð÷²}­"ÒõF޾ó§KæôùK~Z—D›TkÐÇE†XóÉz…côŸ]Æ»ñÝ\»ÿ>Æ¥ðâæsoñ¢[%`ѹ˚ 'лÿ$HÙš©è·Ö†ÈŠÙ±N×<ZÖ€—&/•ú úiÛhòùë¸Úð=k ³Mg°×cIzÀyßÀôö)Dr{ «¦ŠY×Ä©þ½£ô¸øÐ^¾ ^®”E‹çJ¨Bœ½“`5݇Ù”Èþ{vé’ø#æœêÖ„§ïü¡fÃï@3ãJøwy¼Š¢³åzqçW¶üõ28.Moô&ÒK_Àð†€É'αÀÜ!FàY®OñÀ-'Âq¶ÿÓ4áhSæC9®“jÆŠÊ?°º|r­rí8ו¢éÉ”ö![H½3´™G/¥Ì æZADÖÆ Ì_1¸¯?›|;K5nOg>Ë·a¥p$;Ä3•õÂÄñáضŸ—JM@öɧÐ2ß…~Î…÷mÃÀûq%m>¹•Z2™G“¼ýÔÝ™AæÌ#ò¾xØCèÞŸ ¼æºœìÏÝð<3眒ƒuñ¿™â“Çhä´vl?3—fæx±v¾"ŽMįù sbÕuL; ëŸÿ…· 9ÊÛT13<™È gb’ÍfˆîœK –b^ ø¡ê³Ì >|qÊyxä4e4ãñçiL±„'ñÙ²Zþ‰&O±9Ý€;yd— ’ÝÖéÐúï%^èÊÉüÙ4uk .å/E­àW¬dÒ§ÆÄŸñëþÙäÈô'ÐÉ} ëøÏÃú¢VÖ€"û³Aó­0å÷» >l©ZäÜöA…_ºmfw²â!*0ä8Êì’Z¢ÃPðBƒ¾rS fÍâÄob6® ®çމèRoí©pñèmö×\^ÜT1Ÿ–zÍ$]~2z'FáPbnË8‹ßÓµàÖ?Q¨Óçcë7EÂ5ûX,™ÊGßú¼žæd\"¼ŸóÑüfžÙÊJoŸŽc_¸wZÞq¦kd±ÇO‡°!ñRTN±„U1ŒÑ~sT>–.u¸¼FN/!¯ÂãÉ¥Éý~?S} - sUN9áƒe$ øîVpæº/ÿýlç Kà 'N ÞHIJëß°2Cþ4‹à>EâÜÚd+… ;žbó')ª.¶N,šŽ§ò á?Ž$ÑiNu¯!íᡪ™¼´ñ¤ÍOê½õÒd|é ªsv¾½7 @ ? ‡ù”HÛ§ß@ßÕÁ/z…µÛeΆUã´Ÿ©FÇÈŽrC)ûk#|ßqÔ¿©UÁ0d™ùífpç¾ ~J¤î}Gh²´7z.醀þZâªKM}yèÎ- Poè4^˜ð%R«£Iõ½¿S`gr"0g0VJ^R•èÀ2ÖÂÒC?˜¸8>RÓÒË̈äcJî´bæR/oÚ1O?rpÿåƒðÅÝ‚}ö³”yÒ–O—ŽzÓ¸œ\\<µoß|Áž’œíç‡A@° ÔMŸ²Sj“@ĽÓœ¦’vá§d:ÆgN–yè‚Nh+ ÆÏMf»1& œ®¨Üo7„“P;sr¥ï'Œcä7àÐÅ2\w™Å‚Íwð‘ͱª!ÚŸ#èÓJhqG?ûûâüÔ;œd%SæêÓí4t×!\j‘à /9ß è¸ÎÚÝ/Áï=¥“ûMžò|«gÕ_ogÞûóÓó /9»ÄœÉÁ€>œsÿîo°¦Yc:t^¾Í¥¿5.qçíÅÓô&Þ’ë‡áÍ“¹me±Ç»ÿ sô Œ.¨aª¿âÏâu`7²’–… Ý›@üƒ=™(«X2×e€ûôºÖ«YW=<ªŸu¹a@87bÌ* rT„ƒŽ1Þ*úm%„{4#Û§E4ÇL˜½-ðÚ^†þ§ÙþåÌ +„†CK©s8g YóÞ;<ÆË:œXCRÞ„ÞkTèãÀVH¿†»ö‹Ñ«þ§_v8rw:v\ŒÊ–SÈý[ð†:H>¹ž©Ò^(ÁO¾¬â¸ÿ¬ýËUÉÈðI=Îj­ÜÕ9µ gÏä:¿ëðfÎÁuY¤LwÅð2¸à[&;úæþóAØ`‹Oôú8y7ÐSÈžÄûaÇd/Ÿû™‰Û;Äæy23§Sƒ“½áI HùZ°~SÂÁŒÙ€çÎ.‚lqwún±4ÑZäNN­äÖlíƒÕÍã­y÷Ö1ÉËSÈí_0¶±gŰe~¤Á|.Æy41«n'ƒñ]ªæ·6;àsm~´ô„ì&Ç•ó8O¢ã`û_˜¨~9½aßÁvå¶íœ’ö­ølKÔºCŸÐEœ_¾§½ &cYýò«àðC‡öÎ_ŒKÝv2ù­7Pô¬<è7ñÐç¡8‹#ø`‚ç3gí÷”ö5$xH•¹ù«£¿óVžÕO€¯Â0+b²_yã¶À øÏSÚö¹' @Ù•ÓØ¼ÕÐòa*YöÚƒº†°o`Èl&éA7ñCïsöÌ¢«h—ûÄž`réS¾¬EC]E¥ŸL^çµé`¹Éò+!³¢„·OŸšj×à÷®kxî~ §ut7l0ýŦü6#öÍ-ÌDžk ’—i Ѭ՗ŽÌºB8^b¤s-!çEaç¥O°®v”Ó?A¼4ä/´`õxÌhÑJmê¿™^S¾Ú™ðÄ­^?s]Ó%Pû^Z½ßÁä| ¼nf‰·`‰ØL²ãû78u²¶õá¦dYÙÊkâGM¾»‘ªEgò5ñÒŒ<,šNº¬°»oßÂÑRi,ß~ J_ÝÆ#O™ÛO@coû9þ4 ]#Þ¯7`éÅgˆÇdà?­srÈm¦Ä,*†°ËM̓æbÂÀ8qé-,ÛóäïeG¸&{È’P™h\½mín`£œÚ|(„†-z¨­9Ìݯáù"̺Խ |ÁJ$½bnàæviv]Àl*f2 oWÜ™©z$jj)<ø‹=m¥ÀŠíf?–ºÐ¾Îtöã:C¨Mœƒ{ÅƒÈ dΛƒÁ$šÞü%DWïÁÄe?b[å ¸í-øwCWfº‹ÂpÉ”S@F%ØÎ›#Ç'M“9$9j ³9\?aÈ ‹bÉÒƒÈJÈ‘»}é­>WG±îT3Þ–ÅR¹xF,Ç‹¾C‹Ý (y"C/•žn­žáJ“ôåDê´ç¸‚|SQzÆ] ?«Íeö)GAáãëÐùp¤5Ç[:ÛʘµËs`ᇠpøÊ6nKêÜßAuùQâuL›îð=HÖfaé‚IÞÖºƒ:ßã,±tˆ+t&œK¬Âº3¡&ÆnÔokãÔ¢ËѵÍG§Ü£`Óô žÝ–¥ûw/ù¿O5Ø6³Ti=9’èJ–Ü”bnIã[†xø[3Li:‰SÞõ`é^tüyµî‚KV â|…=ß°‡†?Ť:4°³¾¼|i4ûa"òpCB›Ò¼pXX’æ?$ s¦Ú÷eÃ=|Îw¦=\‚AÌ¿92T¼å Ô(wÃüuÈ·¸%šjÀò®":Ù÷ÂÜèûXå~BÞ¢5׊:m< »¿2Ù×YÌjùËMR0#ý|T -Ž[öþ*\ßû†Ù´“Ëþ‹Z_“ç°ìÒ^f•€.•ãšùá.ðf¬oúÂ$-‰ô# hMÒ°à‘6õX¢Gí»úõ3f[áÙ¨K}‚ݰúŒ|ZzŠ­ðŽ`G¹âÔƒnîÆxK‡ÒšáLVþIКă|#¶Ÿqç‚“ªà £AKöü„éqúäµb¿ƒÛ ZW/›ø¯sþÒ»À÷àv¨ûîžWg‘7Nƒ¬…g ºÎ/Æ µa°råW˜yÁJeÙ·³ázW2´«v‚QÞlÊ»}ä›…6Ò¼¤àŠ.}'ônDCøäÛtó£æ4›°‹mÕO2¡KÎ=ÅÉûÙÿ|¡]æPñCSé…Ýé„ÿ²H¾v£N‰JT/u'p·'}çª È#Ž”{×£dz8X©’r¦ùÌßqƒ›· QM;ÚHš% A{ý{æMü3TTjÄâ¦Gðþ¬,j•­B¡›êà¾B(?bLl8çá»^Óˆ9ÀÎÚ¨O]ùÎc°â}ÈÔœ…¥‚—pæ=lçPÐPØM6•œ¹«—ðèÔF¬^lLŸñ¹’ÃËùY¦ÉÍÃW€òÂÛn‹jp8o+<бMo¸bZšŽB¾ÐFš¨hLª¼–ž+ lÝ·4ÃqÇP|^´‘|P³ƒ©§È§ñ\úö³›lÿrî¼Äį۰µß‰t61Á™ïÀ~½=TÅÐáŽföʘØYÒïŒqæË$ÏOœöÄ{±Ž‡ÜˆÖÈI˜Òìwçn`¿U¤›ä-`íô œõ£žª†>Ù fÁ»z¨’Š–ªkéM%Ò=ô7lÖ‚“t'­Ý°f'ÁŽÕù˜$/K#..¦·2S©îSß5ÏbôòÉ™Iúç5?ä]GµÍ‚TBs;¬ÝdE/媚/é|rõÛMÈ“6Ô÷®¡&·]i¶*cb÷>[k1ÛÍ›0!:Œ¡oc!¨U’¦ÅÌ¥ÎÆÁdùØEŽ‚k01øµŠ™³b%Sè–¢KtGܲrн„©xÿRZv´C7½a¦:w‚[éC(«Á«ÇšpÑç@âdþ SÞr‘{Z„æöH‘[2tG²Ž)O£×ÄÈÊ/·©TJI^ÞO¼¤ˆntØ(1h½\€Ê+ž`% ªSZ DêFžØP¡R7ÚÜ2Žyuð`ÕF_õ¶Ê"ÚIëðÈ“ý,Ÿö¶û±+®–Å _TÎå_Â(jT°EÕîäÚΜÛ=›(g¥“ÌFˆ&„åãæÌŸ—­ˆ*†M°uKT†gÀ;7%"'Þ ^²ÝŒÖK5ºN'Ã$ÙW×Õÿï-½æ¬.é;?†5…>”º‘AÝz4nŸ`=dWà‹tsR®AÇ#ÏA"÷÷i€=Ñ®éeÍW’¶ìx¼´¡,‡ GG‹ÌÞ¡ÎpªUÑîÏ5ô®øE™ápqP™ìùæT íÏ2ŒãÂ{Ìö…êÀ䆹§fõÜõÐãWÊn{¡âN%˜æ4±7¥@qÅ|æ01C}îÊ’Z£{œ-ݱDÞ¬æÛ‡þò§L·L ;vtŒûë¿ÿÓ ˜CwÈáIž§ø=m   ÌÝû$xC•zåçéÝ;¼Ö.Éœ@#âº|ŒôΠ·‡ Hƒ¯'¬ç˜Pï{ÙPhvÏ_ 'ÞQOAÕ'‹Nã<…*ŒßáE&”ØçÉhr8‰Ý1É­>LHóßp?¯!}Q¨ˆwmÅÈ|"ƒ)40ì5ë¢ãE ²½˜¯A„WE3Ë4É93蟵'`e\ ­N° 'â`ŠcË~!ºß‡¾]Æï ÷îp\Ó;Ð[Ùtjù²ô¡yÛD?ÞƒW'!ài;³|ŸÉ¿œÇ±Õ¿›#õàQÉ2órö¬ï燉zÅwH?u é¿ã¨!{‘Í8bD7?³󭈲WÜ:ün©DÝë/PŸÝ†é+ÅhÁ*wòo}#“&Ï¡ü/1šnÛÙdgx/WˆÆi!ô×Bøû»›]c¡EJŸ…Qßé»Ð:ºN~“ÆÅP Í„V½ ¢bó§‘ÆÕÏÙ’$п“@•Þ²jÒR$³+Œä¶‘v&îi•ÿicPùF´È?gLå¬èµ7•ìñsá?_é`Y5ÙvDW¹ÐmA¸¡» ’Qàî_ÒÒIÞÂe¸±3–JZ‰µI*÷‘·Ÿá÷‚,X¶c;˜nÓ&o7Û“Í ü8ïøCØG/a™¨!Öö„3ÙgÔ¨N½2}YvŽa9Óéa;Q"6¨ÙöRôÚ-Æò‚Í|vÜS‚Ï!É„¶5™±Í ÖÏ+'?0áÕ…Sý{É¢¨Ç[ÇtMô°—^¡‰©-=rSÓJ鿤ê0ʼâqÕ,yjþG…>ÙµÛÅ~Âõw‹I»¸Q\0È¥‡€Ø4rli¼ùüŒ/øãÉu‰gW/åíy ×g_^—u4¢6ÒN?‡›DŠL;ÊòIUaM7ا9ó*oëÐÅ2F`Ó­AwíÌaG\d²ûôðà ^ÜÖ\Nñ°`ú9öè`[{.ú›¢ý,gô ÃÐ÷Si®ƒý{*O:Ð?üaç+‚·÷øùlžè¿Í=Q)k?ÐK‡Ö¡ë9.h̺Ï9zñs-É OÕÔ¡æW1œŠ»PJâ~Î!¡>áX½š¹[ô…}œú•Êã—ƒç¸ZAMð¨î"ÄÝkkU~+CkÒ‘;Û†VþÇëo%Ðñm=h^3û?gœO]ÐU-š»éºùyÙŽ,;ªL>årQ²P˜67ý6žeæ]<ƾ}ÃOÚG:Ø¤ï ¨¢q†Í ‹""¢dÊ·ÏàXìzi¯«#[°‡Ç÷2%OF0@y7„}ƒ="_™µ·4Ð¥áÞïñ#Ûòsà÷r?x0u-êèÂI… ²Âß‘®x0•ž~æD¼+Ô™Ë Œò¬c:¤Å8gò®³rn ’Ë-ﱆì8ù…NÏ"rÓß±#J‹°9âìÜ`M} ptŸ0•’ð!F ¾ðI¥- 5ÈÖŠ:<<œîGØ#W¥è`˜+- ùŒvr¥4ã› ÿ½„} ]„»äbѶS‡®ãbö ¬— .’éÌ<ŵL—M0pMé{н‰FôØ™½ sVcºGöî|ÆŠòÁú6ÿc6Ö$DjN{û øtç‘C(°Ôª ä²Ró„i˜ ©‰†ó^‰ÄG´è‚bÑ:¦·Ù˜¿{^õº2»>x³q/LÉ.«=ðJõ*<ÁÊÍ(ägFxõ—Ð9ÌU³C ôÅÒx“ðó[ rZê>æíÕÂ)íblíD,9Ñt uþl¦{œÚ!xQ%Ĭ<ƒÊ|Y°fy2ù6¸ƒlÜ"FšV~eÃrÈ«„æ®C,>—Ô`G›ÿ2üùèà°5äçÞBÓÛ{i»ìt2¥Yˆ}¦ýl×À“Ô¿¸¶¼}‚òp}ÂJZ>ÉÖyDO0f?NBXëbˆºÊaØÝdîÓaFw}Ë¿„$ª¿û/:‹ŠRÓ…Ó‰Ï"Fr±Ûí¡2ŠÓ×Y‚ü„=&DÒ™ZTU³ óy°{æfzóÑ]kØH3:¾3Å…¤-ÌöÞ ÔùŠOÞ¹ÓywÙÕ„J/uÙ‰;q`á!ƒñà ^ñê“IÄ ·–â×£ÅL}d7«ÿ±ä$&¿;’dNÐ6Ø±Ï nŸºË€î:òèÊQRÔÄ‹¦¥¢ô³Ïv2vЃ¹â5‚¼'îc¡¸Deˆsß•íÃmj“ýê:£ßñžó™¯;eãÐù82oÛ2Z¼ý >YÇvÌüEAäò}­|.)Æ\áé3©ï?c¨ ´`:Šòé½§‹É:{95Gñ\V1áøgCˆv?;kÏ_þG}ö¬ MÓ’Áÿ^]•ÆÊøsˆòc/FWOYeÆC‚î?Åæýåd¢æÐy£¸†¼Æ%} Ñxcs \O½ Î ã¡qQºýÚc6[îgéM 3:hœŒŸo1örE¯˜(u*ÃIçXki㿊|’Њú®1¢‚¯»ÐH(’Ôž cuzú¡*Ö£}‘YWê±è¶ˆšE5UÍÌhwÉ^O|«æ†o…̧žë•§îäÅÂZ±â4º­§4•¼Ö"__"ÿM#Â(gV·$‚ä…—Ì–ƒðÛ§ší^P ˿ӓ½pûµ0]åÔ„òŒ J=Ä<·¤¾¿™OœnÙ“‚ÞT¼›ÕŒ²ä™gWò˜Âóèt¿cÐv=+ñç×X°|ßÅH{¦À»·Æäð¾s, _B‚âs‰çÏDÐßúåÈÇH!bnWAje_ªh œ©Ããy[±Ia ãíóÊ ZPa³xÌÑG~Ž&åêp¼çþÝÄ(Ÿ7bÖÕöƒ^´ý3&Oc«W Ððöæ)jÒ¬Mëx×@äC5h¾_ǞψCƒ VÂ+Ì ñà69Œ¹²–.{ô?ÄO€`\ ûûÛ#ÏÝslLÀ6jÒçI¶Å¤ÁÅFYæ^c¼°›ECÆ.ÁçkÙŒpÖE,¹hAm”²ÑqÆYÌzý”¦43’oµÉym2»\ñ w$º“ô#Âôج׬_=nx(søk!ëÔ"²÷Î]™Ot–§ïùë Pj‰Ñ6×±²¦ Š÷ Ž<&t‹ãã’.ÆIߟ¦KQ¬‚Ú¢«(âG®Ù¤ÐW1ØŽ_\ÖMf-l2˜ŒÏ D2WÓ»vòôçôrT²Åñi”ð|Å ISuÓ6xgtÎõΧÇ=ça݈½_õ>äÏdÖoåªT(mÁÀå2ÔØ;KN>g÷Ýð9{/;zÖŒjñÖàRƒg—çO3 Í xÙh•Hß›ôB{Ù*ôT£;ÞyÒ@ÃdYÖ ó§U2|§ ÀÌ ’š5‚&ñLŸÞLzÇÝ•NÞF;^îÀy¾OñíqúYUdLpvŠ3ÔvÞ†E¥ÖôôÈs´(é-u팥ûhU¸Ëñ“3¡êYÊLÂ-ê­°€6ž»Âl互ó6Ó t/=:1ȼ8ýÞˆg憗0–¢{ñô¿‹ðòâÞ•w¢é3É»ôÚ$Ï.Ýy›kùñGEå?Où(O(uØï`H½ö=bÞ5§!üq¤Ïl ip5£–×5oÑÄc{å°~t‰­8”†ß&+=ÙCšŒMñEtÔ™ÁèÎî¡1§­ ?iõö¸I"3ìà„鬑…þ9¢Z®Hk5Ïßo²_:Dö´S“càjíºtS uYCo¨ÉÒ›¥tÚx9™Y[KÇçEqôÑÎdzT̈´ÅÎÀÅÜíT™"F®n_MÚrd ¼kÃC Ÿ¡æ¢M˜Š¢^9Ìy;gáZÓÛ×-é‡ó*Ĺ9Ì ú4χ›ñî$d_½¡»®ÒMºÂtáw{:¡ iE7Ù"Lr3ÒhІ`ÛØ.8j¹Ž¿*Fã¯et[Éyxµ³âÒÚ¹%»mp׎¬øfF Ïó×3 ÄÕšÞïv\}%—ð­$×€îò;Jï¬%†WŽƒpÂ5²:€[¿/™úE³O_ “+?áDv_:Jó¥Ãí™{F÷Ø?ö³‹Óˆ÷Û$òå®7}¯þ–<*†f“1rqp<èGÓ;HC\uwi¤=à@âã!~Ę„ˆ•3ù`Gž[éBƒŒ91v1aƒTk–ÕÞr½i$˜È”1o'ëï΢ ¸P G,W›±-o¦r^e˜1ÜlWúŸoô§ß)ðÔ,‚âEqò¼<ŒÙür!DuΗ°g YL~~oÖvTÌ&w?zÙ~¥LòFyÚ>ð çek£ÑôpÈÒH„–7™Ü—±ÐÒF{v¢Ïò0fQçRúPØúv/'¹Òc̦eûÈà+͇üŠìÐq]úøùY掉3ë”’ƒ¸IˆšÙzPÕ}ÇP>íþ*BらöÚ7 78HÔ^ÆuƒùðæÒ0øî!íÿ°ag2hìâJÏ<Á4ü6²É^\Fu’4&×>€î­‡/6u …]÷CÞ_êxÚW©Û’8–þ[A½Eß1U÷Máåq G¯Gã‚ùnäèØ.²V'“ãr¬y¤Ðˆ{­ÊÀjG;j|l))^Ó­B>°ìŸ'Qµ;Š4j–-I£ª“ózÓróoÜ’JeòÕäÌØ·TüãpDñL:‰wƒ¸Wz>q;NÏ£~5t¡ ½%wö棉_OcsÎ\øÃñ1Žg®ÉU£LðY§Õ˜ƒÅMˆDWü4~Ç,žIêH\J¶'ƒ™oàŠŠñÿo|Ž?/ùÄW‚Â#?ظt=vß Æ$#‹Ýý©¹÷JÌ<ÖÁH.Û lþ “1ªO߮֟ 0»²ýˆÙ;WÖ~á.¸,ñ -<ˆµ/ jÍ]¬½¤õGÀHxñg’ jq-îÝB¦V©Ç)™ÐùéåçØ e03d>¨^ÊÄšíyhÍëÂ9*OðÝ;9<‡®B´f£0]cçÆ0»ãÚÔQÔ}ÞÅÌÖëÆ*ÑãaÑrtéüYoH9îSh’ª¸¦L#Ë¿®fÚåSY½M«À·Qª{¼ƒ¶—WØm^êtlá|'/ ÁQϰä»"¨G˜Ã²Égõû™!]¡c…N+fm“¿pÕI› yˆÀƒïÐD-„V ‹“ >Ní<ˆG§?Ã5m©¥ÓZäaP%^즸O#KO)Ò{6g˜AM!¼ygDfEâ›þo$„_Œéö¿ë™ˆÞ0 {„ÃÎ<Æ‘8{åt°waǪb™5ÄYÿבðqä4Ã1©@!ñDÔ\RÌ}U ÇbW2ùóÆ¹ …¥i›ïGBtØD/aÔò 粦ӗ5p}M&gé%u|½².ÇÐï/+ÙµË+Ñ^ù7¨ËÚëêX*Ü®FvK=bæÛ‘u2W˜ÃÜHüííKFùÈÇÛáTBq:­ðjFÍ@d¿_‰PEbå~…)KÂŽÕ£ÿÍÇøv¯4¡Ö°(}±âŠlí.¬#ß…½iøp;Ó¬š€CÛÚëÔѽF´5ÓŽkXd€Šo‡ñÒ­4X¼¿ ·Jôà”L'"y¤¿Ò·\¿e%ìKcYbnäNS~—’K.çLAzkEÃÿT¾é›âèëbM¼d¯‚­É$tK¡öùÃ7y+Z06­á»èݬ"rlµ*Sx¸‚\IÝ@ÚÜë™!ƒ$úyû ¤EûèhJ+¼-O„iÃË¡~þ~Úvq&¤P³iæä–rB½ vD”qnŽOÞ³P FÍÄ ov$9nBFæ§ëÆ­ ¸£ :†<áÒ´°\w—<´)ö!4ðAšôÅ‚˜ìcž“û;ð*ö}W¥¾?â¨å¦…j€'~Ç7áï!ðàrôú¢‘.F†ƒ]X¹#6ΞCœ\–‘ý ‰D÷ôMÌ8;·wR¿–\trÛLVɆ„ÅÝL\D2|L'µ9LR8 aª úßË‚?Òwp omKHÆ9b[¢>â¹—•3ÎrÏf#[xhÖ£9Ô¨"û½EO-/yÄû†ûtÃ2Ôë3Åyô²¢)=ß݃k-È‚Ñypîh4¬²¤¹s6Ñsï¨ÄÅ [û׮ɦª$_=œL+ ÄÌÎýàpþ3ä-\Kœ85ò˜d™ÐÖ9ÍU(Û· ®<½ÏX™‹P¹Ý¼ôYa&9ïÄOTVq™B“­øÃâ!Ì’è€ÓÚö Œ;>0µ•\…X :%Zœ´[W0‹¤–Y’iñטLÕÕLò» «×c—Å;èë¾ %+¥ðøÛÝ½ß ÄÃKÐQà3»<ÉVLí–EÂô*ç>°‚ÌCÖ´Lý#G"…‰R+Fû+ɾ§ ÄØDŒòý­¶0Ûà‡ù=ŠP|x yÔAaê˜s4óº{u[}oŠmCÏù8/{2Ôõd8C}]hVæ‰òK¦â¶+'ØEn©Ø%Ax.êÓä€*Hv©ÅÆ»ˆêc=òQH†¤›‚ó’æ˜é(wMJ,3µpi—Ô¢ÆU{È¿ý"t`ºíëNÄÐ#Te•ýt‚–Cѵ|ºðÆlbxV•ÜÒ#YÖ’Ð+D¢k¥ˆŸ0í°Û‰Ï–d`UïqV‚/Ž ;D÷6<Ä{yRäϸyÛú¾¼ûÈìë?‚\ÝH:wû\ötßZúÆAÝ0üß¼À(íר¥~þu"¡ EcߟErÓŠ>ˆ`söÜ!ےÈ­G2 …âôãÛiÍëí8¿mÁº&øèîÆh-PÂí×ïB ÄR&øÈÊI\6”Ç×yTBËe<ðq:‰œ¢kB7FŒì\Ê)xÌC£…Î2ÛµU »ªƒÝÐñ>N!¢~Z쥚L$/l™²¤[ø'=›ygoBF_Îb/ÝVûÇ¥1ù†Î>…ÿLÈ<iò¹âž ½ƒ/¶‘ž!¼üÉ¥ôJYoÁx.G)š‘Ú)Cö(ºÃŸˆW°\5fñlá45ØÐ…¯RÁÿ€ sÀMˆˆxt±qüšD=ò!£½D†;C¸ýÍÉŸ)K‰i“ –½ÈÆfÿlv\÷X_Q¤)þe¬Þà¦/,í3,Éy÷8 ×êØ§eìKŸd™7ÌÙjOîcuÚTwyö\–N-c¶åi“Ë',IuF]øU™ ¼‹þ‚kñ#Eª=U©Ðôµ$s[ÕµÑÄSÁpn(*{味߹e‘V(ØIº–Ä¢“ ã=Ö3ì7÷L¥6'·ßPAR]õ€1IlAg­9;6Ÿmu¸Vrüe°Îd îá·ä¦ä…‹+>3N¦/ ¼Þ^ç0­ñSx©är[Ô婿È~¸‚=;>áèèT(ø-JþÜ~Z±ål†e2ÄÆŒãºÖpRzþS±¥‹Ùü¦Ž^ªI=cE˜óǼ¨Žk »ïW;.ÉW‚æì#­E$´yœ³mt±—Ôá.ë?„ÂU+èÚ™ÛÐöIÎÙçˆ)þâdKÚò;¢–Õ(K­úÇÁ[<–fËS§Nc:Ãx, A‰çóر5ÆÔê¨8[ã'Cš¯xÒÄœ$fÑÝô_B:󥩜Ýh}¾ ÝíeÙZÓ›øÀ}o­Ù¶-Ÿêï@×žÅØ…ÑÀ†L¥Ã·lÀʨšU¨Z‚ci N@EÎ23*æÂ®i IË|W2}ï X£#1R¤UWZŠxHIu {*#ÞÎb¼³|áŽpDüyÍkÆ|º*…•Ï^`Þ>CÒ•¹”5ï,ÅóžzÛo S´v“_Î7˜áçZ?ê—17$VgRÓW(À/ÎøxL#E~5lHÔ¨Uع¦žØ4Ù“œ­!‚÷|/Dw×bTZAX¼¶¤bÛaÊÎÛlå ";ý6>J Æ_V°cn#u;•½wê,êîºÊLY :FcpxóH<ò#F­Ù¦»üôD±;ÇŒDG`蓵Ðb„»KrtÙ,š‘“ ƒsÚX©{eÌå=qˆþZ0R@<Õú÷oúÔ4í=îï}Á͹#ë°jFÉ™Á imâRpvÜ4„ Žaf÷B%˜fðûò¦3åöÿà‘}äòA|‹2ÃE7,A'σûý,Hg暦ÑJ9êB5rUË8‹GWC@ÅUS.r/£¨´cÜ«8 ÂÑ‘L×r0ë}ùÓäÍ¡9ªü¬Ãá*8c¢D,Þî+‹WÌ›ò]ôw–(„W#SRGõ‚$qÙ‡jÜÑi}G ÈZCeú¬H—;)Q½Ây˜¼~ n»‹·ÍÏPë¹ëð¨}üÊëdÞ(܇/ZV(Š àP9Dé`TÎöpO,JÀnÙGì~o,lÝ/AŸŠ™mÇÛˆº¢e8ï¸: ²€ôûÁd•~Æt½¶Ãü\Vý¸3}ñ/£bm©k‘,ÞÎ3%2[È¡ÂÏPï / «`åŠfÎVnQHÆ8c²å{/ãÊ·™Ý²½„e½¨™‚•2ÉÁy©X"ô W_)bŠ;º¡Ùó.¨kóÏœ„\W¢÷&Dýž3Åï§“ŸWK°1U†½|k.©7]H{÷ÝæÚ+ë‚ÜçP¸ë<‡,«1Á5¼3Ð7«Œt0kèÓ‡A`'»¡!¢ºRÍŒ,$ç3º^Ñf/ÿ/”áƒòëFÔ:!žÑ×7a¾Ô ³Ë4Èç?™X&†C߯²\/}Ú±é"ŠîèaJþTÁÞ]_Q·$&禼۸/t qÇ¢·ðúr1“ðf^qCšŒŒlü}xŒ#4UüôÊŸ÷pí’+93æ 3HÑc;é±ãÅŸÏë½ šgçÛÒYèsä8ãŸu”.í¸ÎÞëOC=çRð¸9 YÑ8míStå?îlrGür”Ü{8 Ç-²ðÛ)i2ì&@>+‹“ز ~÷‡vÍ&ï¾àÕù¼¤Jè4Û>}÷¯„Uß—`ÿN3X¥åJÄüfPÃÁ Ðë¸ÝøfˆÉ¶¥åW3`h,‡}¨IjmçM˜¢¼žœ´VgºÛ¸>"´ûÞZxT¹‚õ‰0æ.ÑzƒT†>OÃÆï¿QYü.³@#^hÇ€›«áæõ ÈxâÆ4¿Xͼ~\‹µéo9cÛŽ2ºwôé"‡mÌ¢ :X,ªƒ‰ykÀ¤«6¹L¡Å¾W˜¦#¬iù³Þôx½¼Æ÷I9xou¾äS¡7_­CñÉ­B ålt“Û1SÃ7¸Ó ¾EˆÒ]°t›/\®~ g²~û4N™hÔå!ûëbàà :èrM‘)À©kذ€Hh´R†iÑs¸ îÐî’̨N'Ê‹‰Y× ¬MzˆŽÅ6œkî¯`ƒY<š„±£Ïoçð(›îu÷dünu_-ÁšMÇþ6t¡˦c@ç;ˆO?Š|‹aÐæ¹óþûôQ'nšy ª¢üØ¿Ð=±Ä2’·›:ÁJg'Ünþ€B_€áÚpfK»éüµ‚4He¾äSgwqzS$†\´ÜIÞe«ÐìUøÉ#K^å\…Ù14õS9®zdM⺼ˆÊ.útŒâ§¹pÎÒg×lGµÞÅàqì&¼þ8Ίj2JÕ-Ä/B¨÷1¬ò› Ì¿«ì2Å<تt<~¶ÁògXÞ£5ã ºQ³.¶Þ3o.ÍílÉÁ:lWþ…†:nP´ã»`Æ{Vç‚òç»hkãGkÝ*ñä²}0ÿ^?Ä„ZP¹-XUÊÛžü…×{ÈП¯°c§ÝUœ‰óøÞáÏ5¿aw;Ê|üÅv_G·v-g3F—ž¼c˜#÷—©›'CVHÚÓ®ÿ ¯´…ö'õ#Ï "Ña ë%A„×KÐþnÕ§¯ãsÓ½Xy @‡ÙÍ(ZîÏšeH§u#3Ó?Øóàùà‡™ýé’„÷‰:1xËæ•’~»¡÷y úoõÓaCÌRgSÊ}¤NTh!gáÎñnKzéµ7˪C%¬¬HËé2Žér1ºÎ^2ä@ÙI˜&©?‚OZ,ê”üd¶~ÆìêÛÏÿÌÅ—æqŽ5œêË &ÖÊ ÔÏä8_`r]!eµ6©U&æ›$HH³1xÝ#ƒl¬¿* „]Ï¢RöïÔ¿­-GºpÃwœºN„ª¶ª3=åL=÷]ŵEõhbà‹"ðÀ¾x(·jÅîÙ{™3ÛìÙ¢»‹q•‘9Õ(„uGýÙݦ›ð‹½±ûxb‡yÐTÉ8)‚dÝ=YZ¦‹Ó~`h‡,^¶ÿbUTP¤³ñαÌÚ¨¥Û1T_º•vnÄ‚w ºÿ>VOæWæž_ÌÉyééÊDüvå>}ù¯ÔÅBÿ–bÌô«@æßVvÍ¡i°¸Ó™ú„ŸÃ’§@¶ì+ÜuiÇÙY„+IøŠ—2ßtêàí]K.­˜GšŒÀ‚굌àî«Ð×X N¥"ôn u§·’`½Ô%(Ÿ«ÒBùX)›;uB° 9RR>‚A÷Q•/6hÒŠŠ38Ïõ gNÎ*sr5U_cYo8œÍ7aê†Þ;Óê@5ª¥ä&&“|™ZÒËØô)Ó½àD´Í§Ñ)EzÓž*¶+iA€'9KŸÓ~áÌCxœó üB£ê kæj+¢¸ÝgÎrkvÉÒkWÄAüC ¹qY‚ê0%s–8¬úžHËåv0R Ÿahš8)æ[ nßî‘]B~ÄDø Ò}Ã~¬‰äJœ›<O|&±®Ð|zÇ÷óbûQ¢Œ{¥~;Hó¥‘Åñöl/9w,DhØ÷õùìÏ?˜ÜäV¶ëÛf&dð$´÷\9-É‘4'÷pÝcs¸:ßÓzÙnãýlÃ&yÂd?‚‰ƒ076$¤ïq÷s¶L~V$û n±ß8á¬Í¦‰›ByjóǾå^ÒVø¼i¾?ó þƳšm™lK¤0ë4Ôˆ¡B‹iÐKY¢<ñ‡}¢áÚR%‰Ê¡Ô982M=5UèI+CúïAìò’$gŽ,Çàº~¦0ñÜ÷jjmiˆ;Þ<6¸Z äßC„•8X"OCgšBáý[Lùß:°>0 áÄè3.‡¾­avGE‚@D ì aÜ8µx&T<:NHÏg;hO—‚*¥ï¬÷,r³Ê‚ÉÆÁ'[Ë™á(!`Ýêï±<`ÍâQæOº(ÙR8ŒÙ6c ,6¢ê‹œ˜g°ù¥:Ú ‰¦Úh½#‚ù¸¸€1™+ AËŒÈ9=òòÓ-ö¸àåÉÞ2™¼Ù8ôÇ“ô‹F€Ï÷ºÎä1îN?æë‹ýœ©¼Îø‹·Žuó,@»Æ˜³u)&¯ áB[²Ÿãn»âðË1)"з·w@á§_-,g^»SÚˆñ°¡! Ø(»ø6þpëö+ðáÌ1Üü•Ðñ‰¿è¾V€O ‡yÜ;h§ÇCÆx¹ÀíÙ@DwÐ-{’á‹ØO¿È¾æˆÂÑjy¼¸å ë—ý ëDÌ`k©64¥$§À}Ë>¨šÉ¬É¡Sö„FFƒ:n‚&AšøTî3¥Ìb¡Š;÷AŠèiL÷U¤có}HÅC1¼01›^2FÇU6_0Cì9³ÑÐÂ’+ƒ„<ß.Oàu?†L£nš±Ns'ú­\ÁÊ¿û ³.-!ëïžDß/y¸p =]/LÞ”ï£i‡Ò@чC]Uñ¹Ãœ“¡[Ê)«¦öŠõéÉKbä›áLrK1·ñ@òÊ^\Ÿ«H7ø'ÃÏã)ä’¾‘ùcIůCwuyºC-£TèÝçV8CÕÞñð¦o3l1íèSx´fÕ2B÷í¡DWN„ÜOî„Ǿϑw(‚.nä%³ùíˆí"2¸- |Ù ¤YúdZr_Î¥)SoCtÌ%ìIS¡G[æò\Bƒ£Yߟ%f¸xиoÿêo7P»ÙøàP51{,I?Ãé™kÊšþä RŽ3°òüÄÿD:(ŒÎ#·?ISëšØó—ï ¥§ý0ZÒ–vxôÛ‹U®ÇiÛx1­É¾¹Aé© (Ñ lziý fù—$z͸ûÒëàFØK¼ÇÞ„^—- ’½ƒóC¦ &„H¹C?d_÷ Ó”©ôÈG,¶»Ý*4Éû2Z±W˜è¿ .³¥¹‘…Ȉë2Îç˜(æçmŽ„Jó&6'ôè›-ÌÛŒj,î[¾9»±Ñ|€«AG`ôé+Üžø ®ÉÂäo-Y™ëMd–}cùˆ“epý ˜œ´ÄB›BÈM›§Y-ÊßbiSv‚ÔX þu3ÃñYd}Õ4`BA÷ÁnøžÃK{È©??àæŽŒÅÕRx`½ž¥ PðxfïåÖR#>¬ÈÊböëQK;BŒbÕh1ª7þƒƒxÊâ1 õ\b%Æ\é /Õ̦c²pk¶!‹pォØ5úÆòÒ}¼Îœˆp7ö®†*™µ­Vý½OßÝB>å㘼Q¼ºµÕ@_ÎèeîÜZÉ®tw8ú vX×1¯¥PæƒJ ó0ª=ŽLÆëçMs;Ô ŸWzróÏ€Ðvg©û£Hz÷?‡4’Ž[¾1íA4d}#´÷ÂrÎ{øSk@®ýäGK-yºQO€ÌêJb‚t†`Ñýd©œM$“€Ì{ÁNÓ»Á¦>e·\º~—‰ÀdøüE‡ß\f¹û!˜}ÉNÏ BÙ·ä=‹]Ạ›ÝæÎXž†1ÒA°'ë8UÜ~–Y{díuІY3 À§ïgrl®Ö´qüyŸ¤ªy“‡ïòaߟÕXÚÉîø«»Rʹ߽w[ûpLq-=ýµÿŒÆùbl‰ïaÕÏ9à‚½‹X}Hd×ò\eꇎ³ ™MΰOæ>ÄÃ̱vnrS>>¢M,Ëæ›0)ÜñÞ-fòÂcéO,<‰¦ÊKÁÃM–lmŽÍK9Ac}8Ïó¼›k€ kê M`¼Þ<• Ïðƒx4Œß‡3 ÂÝð ‹çóã¹O8ŠÁÙƒ9’´ÿ)Y„1I £»+ÃRç)lJ‰97àJB#ŸóµX¦·¥žýöþSðæ¦ªÜÀ‚߸3*n³áQ·ìkJ<ÙV» È[cK¨çEÆÔ]›|[ðØøiÕ²'¡‹ÉQ«Ex«éxæò’êÒô¨@Ìæ\dd¯)’ªîÀº+€q>²òÌ{üñ¹‘;Öö Ű˿èØY8k+„~QŸáŒý^rjh%ÝQÃOF»ëo´Éö=ûmr÷œÆ='`ó\û2aZ#™ïEè׋|ðôº-5‰›B‚¼ÄK=—PÑÁ„nùí×”°¥BȘ¢ï¿ ­+]S`óÞU$è–)þSÃË1wãðò_D΢ÃétÅù³è–ó:Ç´ {~Gž€tÁzVjƒ:Ù1²íëÐ*{+èžðoÙäÞ eüà©<¡ZäÕŸLà¥`ª}Ó‚üˆÖ'êê$X=’îìdE126?µÑÙǺpÁr1P:ÌCL\úXÃl~¨þЊµmÍ0(˜„}I»Ñ\™Ìñ“Ì÷l{LŽæ }þÌuî0*†è "äîÆMÄw§(9´bß‹'€iœ=}4œnÀµôbŒ$ÎýƒÞ ›2"Ÿû‘·3ÿÁ½¶ãÔÑÑ<{¿“ÕéȾcð¶Å ç¦ pªyÑmF¦ør™Š3ÍUpp43ïgå>oR´ ¼¿ Z=âtÍßsÌ›kûè;?–-+>†®ÄaT mûÚqòÜuZŽ´à*“-A‚#déýyù´åÕC|zoJV¥p]íSɇ™ÆÔ4ñìý£ûr¸eÏè'Çãâc¯¼gÜ ŠÆ/àœÖar¦Zi²^´Â;éŽÆ:8¡ Z¦ÈYå m»Í +Ó‹®Gð¢Í-œægAÖ× éÚú·_Yâ+Ó Õr9«sµ zñIu?£¹ë7Lùó ^XÏ£ý:ÖÄ>Ó‘„Py*N<^Ü ¶æëˆ™÷uøvúÎÊaWV$ú¼‰1ÙUÈ$º0‹^*±õ“ÇÆU†ø·ßj(²¹¹;˜®æTkêa˜ªÙå‡ÓÐ]W€Î¾ò‹-™Uª‹?1öCÛpê>¸Žg˜BÇXÔÙ9‡´>ç!ùu­G/ á“ 6èÆŽ¢mZ6ðѧ™½*ŸÁ4§ ^z±Žg„Ùå·7’)AõÀ7µz7ý}õ ·tÞ~\¡êÓdXOËëì¹½eìÊ)aÏN?¬Êz‚¶Ëmž©dÁËwlY¢!U[ŒöÛÜçà§%’,t”–ýc·×ÁßöëI¢u>ûfúV­÷1.¦Y—S¨i*Âêl5GQ¥“p±Þ 7uØsçÁíT‡ßì±HM ¯Ò0܆ö™ÌmÅB|ñz”y£è‘_ÁD¥——¬éo"½[uà¥B+£ý´Û³ÅÈPê)oS&õÂÀ뺜ÒÈFûMÆ?®¡Ôzø6[ãüƒ­“=ô(iñ¸†Ñ@–ým#ošâiÑ%Â8ýuØñz´fàÚíXhDS-$ÉÑã/Ùe±C2…þ¼ÄRþ'²dÆVê—0›TN¤g;[Šòe[Á²Û Š[2p¿´·z0}ÌÒ¨ËM/¢a!@3Öúƒh{Î|-„Óõ7ÕE- órüß;PÁ›Ÿª:¡O3mkñä'GzÏ“Ÿò$ªb›yIcåèöo@3·âͨ²‚1}ª”D]Å®`›ÑOÌulf?Ñ"Ç„ñšW ¸ÌAõìpÔ—œÅÈ¿3„G­Áèû/³?ú””1ô½/½rщL'Ÿ1+l7¿w%!øÑ>˜Ø5•]Q–@¾IM#‚JÍh”juŽXò1äv¢>¯,HMí*Vw ý\ŠEïÎ3OLàâø°HÜË*ÏH#GE„Hñ;a2¯: L‡ß)¦”Ѻ‰W•nLb}9²ÀL“U ÐäÞ{`ADp±ø /Û0¯¶ýÒ$íÒ\|så1²×Î2]Öz(>hLjßrIè»ÖÜî|ùÅžz]ÇÔ_`n<_ƒÁeà8‰ý×™+ÑŽ6]¾,Í"qÄÌ ojIãÁå4hþVüÏÉ8îÛ”&°ÎFoÎ#aók±÷º±œ¢J°Q…= jÈ,þ¢L?ÈÌ EË+T­ãœ{°ÔᎊÞ=ô•;S6“fœ€^9®‘á9l ‰"ÉCɶÊä°’'u,YNSsœ([o›¿2ÎKŠIdÈIä]ŽÆ7lÈ‚M_Ø‹Cwˆìç0hòþ¿gÇÓ3¡Òà-5å>Ì|º†þI=Š×å•Xõ—À¾F“x-¹A;mÇû‘é¸Üª–±ßLRºå’Ä+(œ¤*óÛÙ]ÕOPq\¤gÅ´ÈìåÏþ27ʹpÚîœ6ûŸÏ–±ß/9ÑÎoi¦¹8iVeÞT%íZêX—«MÎÚÂ<Ý50ë˜m3¹6+µií÷HÒ·« s§èQ‰¡)4üÇcÔ7ÊÀÊy«™#ºšì5Õx2q«“ú뛂ètkòÞ+8§ÌÉiâO÷&œ#V_†e|ÔD¹?tµ1?ô )¯ùz¦d»&®Z»9DPn ².aúT膴.ø HW5¡´†+mÝØMZ-ƒ¹ü]úX2ˆùB5°gj5Ûs‹ Ež"Cs’™õtó»䕳(=³ë…cÀOMš½c#ž¹5›~Æhé,„Ìõɦ¶$æž#1åýLØ:ÓÖ­¥3É¢Ñ|üù3_Ô¿€¿_øÉ¾ÈH¦Á¼€ýÙ”6?Ï„´èQ\ôÝ‘£©ó‚83À»l)9]ð[F€Üg$ág>£©¾šP5'Ê󕬿©Âk¥V0x² ¬oö:ðTQ›Fm#ZáF¯yÓáA\QÖ`Ôf‘ÃQoàøéX<žåCÙ×"˜ûº®8ÐÇuRðÂ?ÇŠв#ÖPv¹’-;» -ì¢ñhû2´ªJU÷lncÇW®ÈOÎeî9 œáÍl²`ߘ¢¸›RuðÚTL<ÿwIÀõ{f´Ï1÷E¢òì§ìžxÚ2v ?-+€%ÿrØÌ‡s8=>ÆÌÒn.K›îÂMM|5OŒL ýÞÒ³µpœÜ×X‹‘7öaŠÙ}R€¹´qîßx×Ò¦€c•I¨Ñm…ð¾¦_gûSùËCøuNsÈÅœ¾‘Ž&eGÄI¿ýmPÊÿÁªhE‘‰V2Õ)ŠæÈÇ¡Ýx¬!¥Û>ìØëhùr†DèÛDkr]oEóÝ@uÊñ½‰£žép½Ø–T?e$rÈí ºØjt 6)ÖÃ)'ÿÓã飚´ÿ¸ œ*¾?£«A,|¶%­†;Æ«©.ð0Šª½LʼÍôö…HÐÍJ„9ÑÖ8aõÇîEÄEâðMŒDÛèÁö3V¸Í°£_ÔpÇþèp.ŒM§½ñ»qzÐ-¸ýˆ³Èþ1Óᜎ7`?dõ2–‚P(õÃaö’~˜öá;X5ZÑ5kß±3¦IÒ6ó]LŽD>Ó'Û£*Ùªø"öŸø!Ü€_±ªêËeÝ„ úÆk;gµÁ˜Ïû“Q©Ì‡ÕËw‚¯ñ!2çð!ì8Ë$fÿ0‘iûaêjpW ÃÞYÝ +WÆF‹ö€Ü³‡Œ3³ÜÜÏù}a=ûõ‰i І& ‚Ò%ÜîfJ| G¿B9êÜQgdîAA`3{¸{*ß}'¯ƒ¾h¸Ü™gÏÌeäZ+!Óê!êÝyO>H`kr=s𺠑ñS$ ;´éŲNæÝ.¢¹²«é*JÎn€.em´´8Å]Î+‡ŸÈ3E'U[Ë4š%LŸé3“}w,”Ÿð[³xe¯Yvø*öÉ™ñÎx<Ú:ÉéfÛÑ”²QVÓ߯«àÜ×mPUcG®mˇ}Æ'`õ–&Ü&J¿vïÁÁzCºòäAÚ¤¶ŸáÛǾÙö™õ¿#€­Êt·p!=ýU’zì$ ‰+èsé”ßÞ•šßR&gl]Ȧy«ˆ#ßî½_ô1j˜ÄF{éÕÚ ¬ß_ŽjŒ6>Q'ú}’$ìZãÔœIy‚]q±u&Ë}üŠíRФt‰a«hÔÂø¼PŒtÔ²ÜÂF>b¶¹™=1–ϸ¦1´ŽòâÝÃuŒO'{ñ²2c`{–¡í3è|·&¸%±}ØÁEþ-Ï™s¯¶‘®uLKÀCxûý˜/nF‹.PçæEëÞÀY§ÝL·¦ˆ.ø «gø[ß÷\…mñpºf7³/i.œÔ‹FÏâ ¿@Þ¤kpàv­‰Åê/:ô‹u1çLåo“+¡ÓßNEwˆžqÏûÊ·\å+÷£œŽ*¨<&A3ªZ1Ëü ,ìIƒ_ ŸÎS"«k#PU˜w±I´µÇVý¬†5b Q"$‚tLp?ïNÅIZ¤æO’2R1êÔÌ:ñ“AÚ™šµE¬äÑõp÷û¨CKýBÎð÷¿xYÕ…+Éï¹j4ñ¤½a ÖÞÌÖŽ­$¯Á”Ü f5Ûàž–_l!U£kâï£F•lü)£ŽûK£zÚ½þiÊüÙó^#掠ÿ#ê=À¹~¿ÿq{eïYV!DÙÅëyn%RDT$Ñ.š*›ÌŒUJˆÈ(^ÏsÓ -‰4ˆDS[Kãïý¹þ_?×åâòzŽûñ:÷9Çyz8˜i}ß°Ô8‘u峀ǫQJ]­{þ01âg™»¯þ y½ 4ÝÀÇk¦ÂQÇËŽ¸ó£Á·šðª›£yßöRéjlXËKv¤ÊæŽè>Ö…Í&½«¸c|ä9fýJ[:}° GÖÕ3—<fŸË뻕JWÐŲ¼4Íaœ¾kG¼lŽ¡rÌfÜbõ‡·õ Ç Cúã“Q[:ÀˆÙsÏtÂÌWØÓž³ éW#~~ìCº›¢Qr“:Ñf.¡½à¦â¤!¬MAn”<¶¯¡KgýÁ=9jºí«ãƒ c»qmðdfåôÏà|³JxÔAЧv:å‰2èYÑ‚öªÚ”gú^Üj1³ÄÉÒœ‚Ð¥{-mwÇú. ÙÕδ89ÁÞ™@g:÷M²¦q©›6Þ1*¶âä©+ÀïŒù¬íÏ8üFVÔâ’Ó!ÖgÖ(­£ÂtêVzwÝrHK›¼xýnGâ5g ë«0_˜ð.ÄmÆ"sÅ’VºÎ‡=WÏ25­¿QçÃ,LQ&Gü¾ÂÓoâä­¸IßeA=k÷0—tg¡‰üi2çÚü먀î£FÙõry8…ψŒéÐ_w‚™)Ƶv…úà*®GĪӷ.7à@>ɨ›Œ m¡´m5ŒÂÃó˜úVxâ\ ;“w´ñoÀÕIöä×ËöÞK°]úƒ9`ð‡¶¦á£ýíìíÇ;Ñiª¤XâMi[ÖóÕ‰&Û­ ÞùŠ]Ñzÿ,ó§·Ë&áe±³LŒÖVJX¼¾ÚýJ9 ½c å‹áýµø¦(á vzNŠíá”ÉÔ=}“œT‰?^ÓûfÒtεvî5­‡`9ù,6ÿ‚æ•É«ßbÜ%3‡˜WÏW²ÇÜUðáåt¸&vWf%áO¾“4’Ídžï8 ÿa\µýôpäÈû`s¥áÔL׿«ÔØ-—Éç”CÇMÅ…dMgbwEcq=^¶ƒì ýƒ¹P>Â9½:³‰ïy¤¿0 ‡¬À¯_f(ÕÓ+(l“½$¬§©ã“Yû¦–گ¶À«¸dJ&c¼èÙhuDB>‚××N¦¾r% ½p‡M{~n¿Ô!šÄè\9g´µ= …s È‘•v§ï„q9Õ5xaîsŒb Òéø?ûÅîÖâa&Ít%9r|t¾-WꟖLšJ ˜ãT¾Y¹–Ýx.j®æ‚œ"_3Â>;/éVîÅ$êóP€$¹^fW+ßÁ3^cÍ·)ä’/~Ú_²AuÛdj»á"£x+NNSÅßÕÝøßñ“7d4]Xª “ yiì6eòf©žZœi’IXvZ“@eîü‘Ä8û-A›!¸¥›îÁ'Ø–Ñ ÈÉŒ™ôL¶×t=ÖOÙ×À¹!Oƒcáðs:ûhœ¼'FRÖG“« 1ìç¬mz»˜rßßB5›_L©D0ùά¬å¥Bw¹àÓ½ÍWg«ìöwØ H*œEša¶ì?6q²!î4úk*JÐç§ÒœV œ©ž>8³~«ZÞg Ah©8Ýq“-îW¥Ï¯²7¾mB³)’„²ãÚZóf·•‚_X+i÷Í)eçÎêÆ¿C!|Vœ”Ìÿj—à+ ¾ÏÊ™;AMLÒ¥*üÏCmœY{÷¤Båj5VÈáǹAð}ÿbZSë+×aßÖνëëðÀõ+pÖz R©yh*@[ÍYÕE–øÂ«‚+¯âJ#?ø3Zã†!Ë}Á.ô ÄÕË ™ndä·3lz&Ã:h²¤gÁƒ¡æ6Â\ØEÛ-<˜Ÿß¯ã•ý'›¾ù¾Â«¿M©“o>nãmcöÝE½¢yðîär*ñÖ8ïúóhe:wY¾sËcîÝ‹€- ì@ï‘%•)Ä)GFØ=Â9I‘À¹­LjEDhM¥Ø^IG¹h%nÌÚlíßw˜=BäÈŒtN¯æ[NæJØbÁGìÃíG1–Í)©@×Ch­2„ %×!Ö f^Œ ÅN ÿøµé‡®*<óòµÕó—BÚè‚âíWqíàL!Ö´×Ó›ë”ÁäÑHƼp>= 6­‚ôI!‡™5GœUç=|4BÝ“a°M?Ÿö¥ŸÏú{mlZûZÅ-¼-ˆ+BŸ3"³rÑì4yóx*î]p6Ïl…6;põÐ^ìã%qçŠaÁoGî¨ê ›Z!EÌ|jðÝlYRÚ–€;¯¼b=» ‹éÐåíPòc|—ëƒÌƒ#øÌuÝy¡˜]E’™7\!zúè *66 _û.Ç–i¾(ûä)šêbŸÁV´]èÍ«ÃOâw GºO0ñd‡ÜzŒ8t’Õ»ý ‚Ÿƒ<­®>ƒùá‚ùC¶ÃcÌôU†+Š™ôVᦽ={ Ài:ûo¤7.…“….æë`,Çš6B¬Y#ÌM÷Ç5›Àå× ¸<Æý¢˜!'ŠÃ‡¤è±þ$ø;„»"fÓœü6¸ûÜtÝ«5ᤔv¾ZG,?ÿDŸcÑwר©¾scÒ9¨L1ÄM‡®ÈðíÇ´‡‰@åvNþ˹)î4#°líB§Ý÷ñŽ¥$Ùt8Ò®W#–£¢²–ÎÙ«Ö­F´ênãiÅGÂâôµêOœÇcIt˜ú#çó[ÿ_¡}!ßñ¢äSœ•ÏöO_ %nZôõev¹Œ¯ªPÞüü+ž†·~È‘™ü€]BÚDœ‰¸ˆ2œH[Hþ¦Þc£>û0>/$†SÊYX½…þ áC»gW¸õ¾2³Üaïõgÿy ÙyöÖuöck)ô9øÕBó÷…îû¦¤èáI¦®l&§~q3Ä~¹êò<ÔûV7Žo#ǸÓñ˺rVlƒcÓZÍ./:Û<0ë ê65Z형/Zð“œÝ/ìBÎ1»ó°¥\’ˆnmÛPdÈÀñ‘cp'Á”f&¤ÚÉlXB¤åÈ쟙Î4I0­Ý¦Wм_˜ÓóÕ![û*Ü©=ʺ&߀ôÓÉœ§gkñ$o6Œíß@sv(’¯[Xn,ï÷ÌV²U]â4äS-Dú_÷rH¸0ƒ Zbã¥çÀÛ%[Aîæ3l´3%ŽEÉÌìc&$ïÐTêZy_Ö_‡Èè&öƒÓEP~wŽY§âÏ”É~îG&öD |S‡EÔñ‘N‡ N‘æ°iê1»zcSFiR1žèœc¿ß0ÓÇu°[v£ÃÉc„Òw2e%>„uSñÌŽ+¬u·ÜÈ€3·aŸ… ٹ‚7£·Â8§6|B¡Ž4î‹o\¸|7‰ù²åXÈkÂð…\ð†7ôcÞj%ÜiÁ-žÆ{ö&Äøž*1ìÞ)–ìä‡ =ö<ŸÞr[\{ÁÈì{±i+8þÝŽ;®ßÁ‡YÜ[ÿù‹õæØ3¡'?@Ô7š}_—>pxȨì˜E_~у]ŠW¡T‘¢ôÃ!æV¢?7»xZ’÷^Fõئó}÷pFÉ ª2ÖLÎ-‚ÅÂ#èùk/`#/­wáBSþThΦý½O`ôS3îå¤3§â°ô¶<Õì #6œwS[†:6}¬?)¾$EçÈÚ•r²¦ÏGiiÌìß"äQÐ&jÉ=8òg!XŸÐ j¢ØÁ³J¤A¦ Ö¿áC¹fKx+Œ7:9ônþæC½%1®½È²ÅÏXTÖ%Yœul„Q,·Äµ*Ä¿Åæ,w¾\¦g¶.¼¹˜LñÒ‡ñµ1+?Ç3fŸ„IS¹zHUCÛqž^‹ïe5°_¬½É½,>¬&[æ¼ýÁ Ì—‡D ] ^ä$aì¼iôNBV›~ˆÖ#Í®%v]!”í£ÜÝ;ºñIí%ðð›{EóÓ3æF÷)Ø–üŒùêgÂþ—µ+~•‰IçZÙ¾E¾ì­…Ãà1M/Pû‡¼è"3_á¦òYRãÈ*¨ýûœµp%¦ !¿Íû3ÝÙèY·à=ª•íë·°[»ƒ;¥JöD:2¦#Ôú¥ð”þQ†§r=u˜‘ËÓF'ÅÓ¨Ñ…Š ·™U¼úl gÅp´œçC“Èñ=ʦÍlFÁÞj˜Þ“Ɔ¯ÚM¹nÓ`ÕŸ»ì`øOæéíçðıgª®Å bX³nÍ~7ù·“ú#h{t6Xè.c+Ë]™/*+ñé %*ôU‰ž6_¢¯Íi¿—û% ½Ã€v’¸×¡DQl/¬Ú·ïtÃúK¸£$]=VŠËt¡€>¬¿h@‹o6áõˆ(p } ^þE0öJ’.ó…Uë’Aü³%–ÿcR¯“ëWXHžB9“V\¬T‡MíÁÊJ‚¼°OAY 5úMò;Þ”H„µû—ƒë æ Ûžu|˜z˜ÝPÛÂðš©Q úõ.¬j3Ç¥=Ñá‘}æþ„UéXŽùܻܬÉÉ4ÄL€“Ä{ãÿ'`ü#”6$±7Þ„2ïÌܨq˜6ÝGž3[Ý5‹Ã/ûd± ?½<ƒ\t< ú¥ÙõG—’vpýÃFÊ€ ÜAõNI*$6‹~ ‚>IøëÎuP¼âŽb5'méoìç%¬j‚ÈùøTŒ âºe TÃ6ÃnîÕÖGx;¤ î.iwðã‘4¼ e«Px…=U:ý~[Y§Eþ¤¾Õ žÝ_S &.½ÃuãíãôÏ=Ã(Jý‚Ø•Ãpl´F+r˜¥u¸î;‡sÂŒ¾…aâdX~ñ†_/Äk9¸tc5¾Ón€ ¹q Óüš½#ÈOQK‚þþׯnrÏÝ Âqð}µ?öÖÂÁÏ—áÅ¡ÝÐÓÀOeFæ“åia°¸>‹ž”%Âè}™\0\$y«òYÞ;=ì¤6ºÆ]é¸ Ë:N¢a¡½Ô1˜$¿aSŽ€SB4Ü…U{ï [{8·ÉÐá,-2}Ù¢õ/Ó÷Ía¯îÔ¡'¾GÐÛªjpã€1•ÞÓŽs£vÒ·oÃ’}Ð’7¡§!’cB]鿨s(ûî(å Q¥¨dYÂUúÝW ¶8wZ¶À“qà¿O€ n> #µTUuu ©8K¼îlYAþ‹h ã/Šæn¯àŸv}¿ª“™®Ý†7õèÉ'eØw@£ŸüeC¹K1ònk¹U‚Ѿ)g~ƒvÞÆØ|”±µ1 oî«[꼊ï³×«Ì1)EãVéÓ÷žq˜¡”2MÏà…±žz•4™õóŽY¤’¹N|„ÿ°)›QÓ†Õr 5hÛÛàÏœ©T4g#omD•Mg’Ѽ5Må¨áî6TÛÏ‹7TãìxºùšÎmí`Ø„B”lûyù¥“Ã{[aÆÎNõ PRý¦–”³&<TZ5™»t÷8t¢j"ÿ½Ì;›ÎµàѽúàXcBçÛÀPc&[tÜn©ö Ÿ§âBYO°{4 ªéȯüº:ÿ7;tq™– <«pÆÖ hÞ3 Öí f >‡’ØãE‡n2ȃ$“s¨’1la1}£8…ŽÉ ƒƒpÄÈÊ@³K8£–îÌ*Jp¦CØx¤µ‡fÓ±ìÓxz‘}Óº’©ßpeÇA‘Æ+μoË賃«!0*®]7&sçå°’ù%ÐéS‹³+÷À÷öøeÓRj¶"›7Þû×ã=Ø\\œrʯ;¥—&ò¿›ÑÆ™—úñO3‰ÿ‘½$Â(·'œÂì têÑK¸Õ-RDÝÇ ÈÞý\ ¾~”›G¸[€~j ¹ŒÉù™×¸¹;-‰Ýljrþ(†^z üfUp/V¶Ï"<¶GIÍ$'êž§Ž5Ûá_£5¸|[kcðÔ¥V’ù°/_n]gKÏ¿‰j³£AÚE—¸ùË“ÐMG˜ö:G’TU2ßö`DtOa‡¡P¿ž”¾fqËd!bòov4oƒÆwR(+û„-ç´âìN¨•*D¥vQ¸#FUèQ›jÅ|~óÊÒÁÙðgö{´zj#"t±e;æ|» 3†aj±-=4NïY‚°%gÛÀìÄ_`P½•ã¡®A\¶á³ß ·Jžøï¿œ›†ÀsV–îR‘ÂO“B¡Îe¼íç#ÕL=;~›~Æ2m±ßñ ûAÖ”c9{íE¥ñýúãe5r!Â^œÌËÌ‚‡õùðDÿ,˜—Šü'ôvÔÖ•·’ûò\,kµ=}ÝaØY bE'äØ õÛð®ÆiUÙÖÎQ8Îþèr¡‰Rf å¥GñÓ“!í˜Z• WgºÑÌ3ñð0 —²Ìж Œºõ“­­kÅ:C 87TIg…ȧ^tÊäçv×ú¹£·šì”³ÐÅëþz[ÉU 2°l þI¤qDƒÿ{ÍÒ›ÛBd»]ûÆ‘tŸ× òe\[‡¥d™ÜVÇö:+Ÿ¿šÕç“ ±»e©ˆ”©l„v©°f±*{^{ÜK0@•~mrj딉úßUW"ë©=vò{~ࢥ³HÉG/,”t Ÿ¸¿à¶¯ý¸T Ó úÆñâÜ›ÍÐøáøj'ƒbY“þçƒþ6&CÝåö'Á”k–Ø*|Î>…IEê6v’œ+£óå ™O=ÚW²le=üfmÀÕyý82ßQxa=èDaÀF}œã¾_wZÍ/a}6_À¾ÌØÙ‰. üDt4Ë/¤pUÙÐÂ{Çt_BD×#pïÜG¾·Ïb¤/™Ñõ)#Pþk=ñšEîYš¢톯î@hÚ4Ô6Cƒd݉ø§×ä3õ†âذTŽ~z{ [ú>q÷ƒöSRð²^¬R<ÄÝyL¦³›iêk UéWP!®[TøÙ3g d–=Î,8AÊ}"°|ºùn¿…º@)DkœkóY0ïC4²qƒà&} ²Ar!/eXSÒ¡xufÒÐêh)û}z4«ƒ?âú•ݬ–³4y÷ï7”¾Ý’ø#ÆòpžÙ(³ÿ¼ 1p¶ã8Ü6…Š´3Œ§ÚkîûyÄê;{/µ N&ÁÚSüè½8lõ޳‹SNá§·0£Nˆ:‡Qžäo{ÿñDþJw…i&Cj¶Nj°§©j”JNë@KÛ6>áIvh¼€õU\µ¾EäMà;ÖJÙƒmH3¦¿Ÿa›Èmto\I ¯Ñ]ß9E¥ó ºÕ>ëÓkiÇáÆ y¸£š½VhB·_‡¥Y0òð³a£ ®z ÷ó‚`æó­ôM§ëäÃz[L#w/'Àª*m"1]ŽYtB…ã°ÌŒùïÜ}‰G¨É]jn?Æ»=ƒ¨Í÷ñUØz*ò>Œ–‡Ý„©vŠD¿LÌÎ_e¢-Ö³=ûðéÎg¨0Uyv¾GÎçí¬wßC´‘=Ý4ïTèÄþ¯š{.=KC‡]à¥ÂK-+Ŭ27zn#ÌnBtt†­öTï´<ÈÜìbÒ7;±›ºȦWbäñô9ø;åìu¼‹G^ËPñ~s"å(\ªø 2‡íXî@ä|†”=^è*øè³_xúÂ]ÈüºeºÐ+½ž]׳–Z5~Çû…èXÚ>R<;j}4ém®(ñµÕÞiAçüù¡çÞÁ:™Ï8åŸ;]ôÍ–‚¬£ù5ƒ‡<ø6ü„´è=ë!JÝ ƒ}hþZ[j²R…(YK¿L’$Ϭñ±›#~´N*.OèßÃ:Ùó˜ª?1çùKØéŽàòu6y:WîE¥¿:2Mz ýЀQ-3ÈütYÚZbªvt€VÇcÜK:‚ò :CvÒ»ùä?/óÁéYÐZvY³Ð×ñüé}äÑÐȰìÅÏÄ`~cÿ´…%`ÁCy<b‚œ>¶Žc? ù—¬ª%=fþtNáNl:MJþ%AkîìŸêDŽH¥7vÝEyÓ³ ß—p`³ í³uƺóI˜tòrmˆâ~bUë‡m+æÓ«+hÕR *³Š¼º¯BLŒ¸ù(„þoc1Ö±y]¼XŒ Õ§ÐED N LâØµ€ã ;zàým0p"YéÚ쉿fc;‹7ùfè15‰­[5|Ø×ÉeRL¨Þ{¶'CÏKÃyÑ(Z‰Ds.Ø¥j¿å0¢q‘‘~ñ „éÝ/Óh£†;[{Åß9Ø»¬O4¿>‡¯Üyœç‚D\Û -±‰õi öN‰p¹á3Õó4ûóÞ\Zj3 . Z¶°¦7š§9‘¸]bÄ"?Ç×@~…‘Á{ß@^úûD›V,¿š×½ >€o"þ—žÃÀäF+u?îõ*ÀdCVÊí=ç[ïUn’Ž.^5™ÂÊ Ñ§(5So‚•V:Ýkagö`Û.[PÎj†=qHO–KgÍ¢Â)¯ÜYöଛø#~Z¢ºÑOF‹œaÁ0ñ1×.ˆÎ4öfƒ?ÃûæâÉ tt¯cÒÝAðæ Vç+/‘9âI]æÀpl>´èÓÄäQL¹ð–WD¢ˆòê"hwzÁ1S‡{^ʇ4¨^ÂY ›¾‡ŠÝ1‡Ò4*Z’æìe_Òåôqu‘û¼3Äsж%¾*€§‡8.O´bŸÍ¡Žòƒ °Àx¯/~zÔ ][(£•ákŒ9?Ï/bÔWqÇõH1¾6²C&ÈuïªC—ÂRÆ«:€ýM†A sNÇ2PÈÞ ù‹‹‚Äa¤NÅrjÃAqÓdØtÈð>é}çS€7þ ˆCøá4v{P ø¼g_}IÆ5¯rØÝî†ôV"ÁKAÞýO´s.¤ºøÁÑ|öÛsIR¤³þåc÷B+R{O’ M#PO¡#ä½¢ÍÈJ :£µð7ë ¼eÜIøwQºðªµ¸rÝWçááüN&¿wÈ9 ÕSèl³\eÙÊòTëAÛ©4t üÖØ BÞ6#êÊÏeF¼“ 3ñØè#³ ޳3½¦¡Öwòúd#þ\Ô»k¨Ï­ƒÔn—xX•“Ú!¨1£÷ 7Òs çÙ\»~ø˜‚#¿‹ØÝq±DÍÆž-l?KßËh’eZ¡RáSÒÐr3¶sÃ{ø½Âr"þË$„¨p‡99ÏÚO#ŽwyI°—{º]Š®÷ÚE‚>4ÀÉÑ~jTI9Ü)3߈º|U£ZÞÇ`Ítr9ú4­sv¤ŽŠèz;¼ì×a©¡9¹ÜŠ{L°¿2‚>Q²¼v~Ú®B—žáÍê$â/=Ðq²ÏÛ1Å£;ÙÉÇ:`Ãñ\¸NdˆÒ³YÀo2«ý:î}†¯gvÓ°÷…x³:}V±‰žú ™iæðqëÏ9/zíÑÏJ*NÂ>í äDÔ èΩ€Uiä¶ÂLº©HîÔ7ÄÉ?ðùÌ㸶òç³õO¸]Î9 ïÐsÛa䔬 3Õå¶¥DÑ·½Óc™Ç||D­.kû¶P޽|ü;®Øá@¿2)~åÃ~lÊÁ?oOÒ–öüïÜ—Ê¡Åx>}pÿëð…Ý~ô;þÔ6 Ê' ¨cÀnÖsÖ ÛQƒÓL¯¡ºùbÜ£¤Ov_øÎbD퉤¨N¢CŽç)×ò0 HÃñàTâö¡…o½Æy#59¼ñiºfÏšešá§qX°M–z,£ßEµqµ½"3ÚiDÜ”㷻ט·>éØù=Ž›¸ó®^YqF|øýìÜÙyÛýÙÑN4ýÝTÌ œFÒ¤:ìζ¥@¤rF|‚äßÀëa2ÄyÜ„,­vg%ÈŸô+v»g>`®ñ›’-FWÙˇTÉñA4h£ú…qþµœþáGØß´ﳛ^L"›æ[‘ü¤é‚øø5æ0+ü‚q‹ý£¸æ8³4Uäñ“‡I(ò\däÂpÖüʹ+NrµqÒžb0’¥Öa´á)‚H¨ ÊÈuâ·ÔJf2“×*KhÂ÷³MÄJaÄ:BršqÕæ#09ùÓŸ¡ Kdxˆ!‰çŽÈMä¿ý÷hØÙtˆ}rx1 óûŽªáTdô*IK’¿IóI»x>ʇpGà­‹b×ÝiêÜ©dåè†w@è¨II ÃOdzh‰ª[ø4ŠFL=Hm%mÆxÖ]`³¢ð{õxß³$K¿D Ó-[Ú½¤\6 `}Ò2îóÇ“ ÒÄjw7rbl=:ûïdåÖ­f„æl>î;d”¹ ã¯Ä½}Ú'UP%ãúä¢Yhü¿=JbÆï +æzß– ^†ìù–hºýÏ~úaœÛ6î\L>š‹÷¥&ãe‡v•MžÐµ³ 9«êaÏ•ã<[NCÎVÁXpiOì£bçSp,[‹\7$ 2ý…0ŸaXÛ]«©—öVæ^]ãU3?* Ñ7Ûœîëc†›7ÐG%øâ#‚¤A Û°ÌüîQ¦W æFuäâÈEFý¢)]_ÕmŽÑ !mä„Wýs‰ÀøZ «ñÈ”,Ãà†©DÞv&=÷°7ÝÁSbQLŽ d÷/fb’xHŸ5Mxû\’O W¸mÞ<†¯ìIb"DÎåÐ̃'hÁÔ©ðùÉYi€4Ê*¾ÞšÀï¿ì9nÿÙ%á¬Í‚9xÚ®ƒ•?oKŒ˜¨zú.)ƒC‘þ¤9{™ýo/¤šóBM=‡æîYDŸiØÓ-k`ð‚:ôâÐŒ‚`<¤{Þ êÐ;y¼ÄI o*Îè;ÌŠ>Ñ"ë±+ßĦ¹B´öýLÊ ¦·%óÁf•191°Ld*!Ó«zÙ°Yk©Âº‹pæY"TòÄ€rýyødcϸθüËlºkÑjØÒìÄ´´}uoö­úÅZ]¨Ä1g ª¦‰ÃFxÎ-7öf€ÉþSø·F„¦ƒ-ä¯ÌF½á‰úÏ¿g³êík¼úÐ^N`'/#|÷g‡2°üO”–70Kîþ†½Šš¬?Ã{Iz¦’ls•ÿüÆ\3“>lUeýœOÃ-›ÙƒR3ˆ°d4§¶š¡ï7D7w2/x+Ú—<„LÝÜž×Bôûè \ù+BÚÛ²gês³”–Á‡÷)gu –hòÓ‡Õ®¬¥y'8ÇsÁâ÷"(—JÁB}uÚ!Cvó÷¯“(ŒÄŽA§Ê'ð¿˜CÚ÷„Ã70?ï•Ï¢Hf üÛVÍQï×%½jÍ vœ(µËáÍkŠt±²$õ²¯œ¨¥>¡œÚ}EðÛb ~Ñòd÷äFà×¥`{¢–ódô(c×:…Š‹M‚h;Aj¢Jï>ÊÄ¢"HJÒ°îçtêyc;6¯Ý?+¯‚S´ x=JŸ3ñ‡ç*·^g&›*Óî£ß°òj$Û,Ìîîc³û‚Pîòk°ŸžmY»ÉôÍ#LçPzº)ÐÒe¹¬¨6¿˜ÇÎJç+)PjÅOmÓ^³%nëѶy$ùÙ'à›—Y/ÛĘ•n¿¯$Î>Kœóñ~­$]v»Œ‰Så“õÌÀéÂz;áŠ2.:<>…–/#ÿßó¿{ qG)÷ìC&rá äj¤BýÏVîŽÆ•,Gÿp Ï ãÕß8ƒûÙö,Þ(K³‡ȧ9… –%½WêÑŸºÑSôI¾—>UBásè¿&ØØÝÛ\ëZPË—`mb‹Ø?í ´òk ø%öàÜ,|n=?þçèw_¼VúË|ï@0§œ}í^Íj¼â#Ÿ€÷¶ÝuOku%‰°±VPÂ_:`Zu|18F1ÛÈ¡xSJé²iú;Ì ]‰Ž>øiŽÖÝOÇò£ipÎN™¦iÛa¸Üoη—åõŸ/¥/=&¾ïö4ݬ;AèÝ"žEäE¼þÖDc=ãTP &?É9L‚zk="û{iÝ4 Ø jÔݶ•ü™­Dg+ëÑKº.4`G/.šáG9;cpŸ¸.å_І–¾xWÂ{Ö2ò†c‘e=ä-Ofü+5Øt‡~ëbò¶fü!è‹Þá©päÛ&²k§‚ вCñäc÷2ÌY¢MR~$³Þá²Èm”`5ÎgsªáöÒÃÿyŸÑOU¢Õ‚ŠØV6­§þØkúéþ#-øšÀ2Ÿaxì«©ŸÂÎIÁ•Ö„Çñ"£^†Î8xíh=½ÝÎr¯ônÁø1Qª–q=y§²I&ñ„PD¾ýgñ¢_4ŒMa¶(ƒÎTÖæbÓx³uõ{À‰¹ÇUà©ÃŸ3ÃÝ¿°‚.¡{wH“:Ãà´|:U;o@ŠÖ>„{œù¸qåYî¹Ð³°H×vü<7F±f“ú`¹ºQüu°9‹ê@¥„='’¨kË,­*BçÞKL—/ÆFëóµk!×ï".’2®˜¦Tˆ£‹#?ó“PÃU(Ö]T*}s³:™µmì|;#,ÓûÆô*n…âöR‹zÁòtÛ¹+P%<Ü5šDP6l®I5õì-E6ó(FÝ­g·-=ƒoä`‰µaüa&ÿV¤OçÎ¥‡‡”IME†Ýl…­œš Eæ@Ã{æyEòüúÏ;M"šZ°†oFM5Æ X`dFvý˜Be–âÁgt±nĈ“½(ºý))Usø{¨œ]¿Ò–U7¿~¿'â?3É ¤–Ÿ†­Âi´>bIYÚÎ)½ŸÙ¸ïÐtr}5?i´lBëJDß+†Ö÷V£çª\K†å²1ÔýxŒ™ËP•¥”µJD—ϓț BläáÌ¢g¯ ûö^Âw3g6eÄj’¹{&aÖdÒæà*¯ãÄÅe69âiDöïóÒ÷sc8J˲°péf‡ãÚ²·˜æ¶¬}-èK‹N›—‘'rko;'_ŒJo q²æ§!æ+=fÃ2Å8š‰Ã Ðdà Ek-GÒû1ˆÏŠþkŽ4Úö+¾íÇccehuçù7s#ˆ‡swœÈaÖünEÝ­Lñ‹”mÕ£ÿy’²–"hšê:‘ÿf»š‰ÐÉŒg4½0æT\jbÛlã!ák/d×¼aóg=âÌÈ.Ài åÐÍ%¥\Ë?[`ŸçG˜{ú披’Ç~¢ð|`¹¸z»ç ÌÏ‚ÐÉ1„’èÊ#Ešä‡ÐÃ5®‘+ìÊXðp•aþ±) ýÁ›Zâ/ôøˆ÷`^!R÷˜ï~\ð‘€ï;»šÅ_P õڬІÏÚ¡ð¹,ô?}Gywì™’Rñ:ÔEþ&˜ìH«`ëdqK¦ò¸É Öè ¨þ Â2æ’¦Ïçqi˸šÎ' oü'ôßïÛ \½‡ºø:Ùn•«ÀÇ Y8ò·æ˜Â¬ôަë½ã=ýÜN8æõ–ðé¨9xu!ÚÒÉ}Xõ‹KŽÀ¼Šµ4æóT:]Qžî½ž¾Ü£0o™=ýÌôÍb¾ÕêC—'nÒå|Á…ù@K2{o™Ã룳ð€ õ/nùï{*<·7î ¾gãYÕÈNÐ=´‚ß~Ì-CUÞÆQâ(œæE˜Î»š·Î!Q!sp¥KûöõdrãÎoT ‡,HÌù0Û¥ˆ=Y]NÂy*1jY?ëT5Š9þö˜`Ø9±ÿÞÿcö>¬Ü&~F5p$ æD‘ðz6׺ƒ•^MÃJÔ@mÑ,öþ¹é0Ô…§4È^ÇtfÛV.L:±…+Òz¯—¸J# k”â™\m~ºKÄG.½aÃCW€¨Z${á¹ ^Èæ'ò ‚ QãFËêX9^²Kî!jÄ•û7Ç8_ÝÌè®Â/0~ofŠ $©÷¹Ï¸Ï0Dý7b$fª%-d³Ùˆ§vTÿöj¬ú°æ$n†ëUPêl™çOñÅÚ;ÌpZ/þ7û¿DcZö4b$A¿ŠL¥Œ> ÚÓ;èŸ7Ó™è—ZÓδ1,zä æ™Rà½W™J½cƒ"*Ø;^s%GEÙƒgiCUýÜø¿˜g¶sˆÌ‹0ró¦{vjÁAöûÊ‘{cñð&Ûe)Ñ Ekòãy2É<ªM‡Øð%+6Á!‡eø9E5­˜ƒªTâ¾ •ž-úéxŸƒ† õ³» z¼4oý0n‘ºŽ'ôÏ`{М¾V‚¤sy¦€”ƒ*Y'•oYx‘­Dkä/°Qõ'aå .mµ°‡ÁýcÓ’²‚SIÆ1-êGÖ€Ïôy4û¦ÂÄþ¯–ìe}ž ¿Û´@¤Ì€1ÚHaÙ*zeÏK|Q£ §·dbܬ ÜÿŽ¡ö–ãÕ{©œ¸Ðb”÷]Á OI€>Ô£¿ž¬Gß·íF»3ñìÜÁ¦Þl2¬2tCù‘ÌÝ·îXÛ…k¾\cù˜àÆö+ðà ?јWÆîiBÉgmxgh2­º¹ûô·áצ÷ìg+ú`ÁM¶gø%›T×Ù`ÖÊ ÿó0ãý:ê7 «cs@Äæ.Þú_ß‘ä ™ § pŽœæxúAGg".`N“Â:ÁS'–JÿÔ´ãW+UÆ3QÿV~$>ÉãûüÍäw2Û|±µ´„µË€“õÒÐ}¶+JÃqÉX0›:Šev'ÑcóMætåìt]AÕÂÞƒ˜Ê)\»é {Ú`ì­;ìÍcøx› 9¥*FoçÍ‚é³Ðð¶}à©’ £íÝMª“$é¶ðüéÄK-¾Åp¦çðÉ´Éðl¯åqiÅ]ƒÖtW‘ö í¦b«GÑý™#u,á§Ža™¨äsšÝYiL5¸[ÑR3N©ÆAƒd4k4ã6{©)f…Ã̲Wÿ›Uí"t'§âØ@æ `Fw&(fý»Èù?ü*Ò/Ñxä8Øâ+rËwø¨tÀó° ²»GLf 6Š*sºÕ½ie²4tjf2âzàR¯–]Ãug ™1…gÞÅ1-?æyõRuyl¸ÃŸ®ì„~úèT'¸åJ½šo8ü4–,a˜ù+Œhc®8Jø—ã"²ŸZ™:Ó¬( úXV”F·ft<œèŽ+ò˜‡¼´íJ®bïàŠÄB¸iÞA<¤xÒ?öÁúPý³€Jy>†bášË"Á6Q¨~ã#¯ÔÆ”‚Ö~o¢XnIþ5çÁëÝioo\Üâ„jcÿOÿÈxw0ß÷kâ£ÍŒÂ\f¶íªrYœ5`ØÈ^t[we«e¡cþy8Öy޹¸é¬µ·ÅN¹™Àô)GFg_.ôø üç¡núpãð¦”3ÿø–£EM ¶GçÂó–ã¬C@#P„´{ÿ`d„¨kš6j÷Tƒý…dœóVZŒY”þ @Tj@Ê=wNå1+X@yYÓ‡àr.tw†Ã HdæùÄQöå%n£–Á(Öº]‡©ÜèЃAòLÒwØs.‘{pG-$²Ö°Ã*xß‹°›´j¸&?àWaØ~ÿ乬áT}îóßùgƒí<.ÐÏØ²_~ØQñ¶c°8Rž’šFïd¦Ã´7ÊÜãÎ’˜åªO”<Îã®Ú×Lyõ,rf—tÐá8v1\=¸¾n û{éz|ô£_˜¥S©2I’dJÏ-yéKåŽïW’*cKŠVmƒ!òôÍf^œº¶ mU#ãçcéÏðû`ø.Eç ôaLq?§F †ÎTêãi—ÿU°ú?ƒ™ ¨·8©X‰nÑen<²‘­xÙø-*p—ËïÕ„^Ø $¾ªµœë9¯r×Ò0‰þ÷Û´øa|”ÙtoÙ÷e;wuÈ=¶hš?íóŽ¢Vþ_ [z+dÎJÄÙÙøþ å¤wakðqÚµå Ù¶dú$_N>…=§t‰÷ÛB|¤à-+<‘`˶ FK3\æÌ&uFZÜ“y ¤"äù}³–‘ýy}~–¡òQ:åUû°˜N_ï@äUåéÌ^ j7© KtVÑX]3R¬¥«¼'û8l“Ô¡£—ÿ7§z8qöô4ã”ò‹þ«çâÔn™ ½¾Åß©d² ÕSáÃ÷=ÌÎö—ôô­ÝP(çL~z¿Å_£ãaúÿñ(7µ\…îL\`¨BcRÿ â’ÅØó­5Vúaü:!Œ¸8û+ÐÏ«z˜æÖ£ ä"EuWÛÀŸ×bTýØHè¼*œØîÓw@þS|¿^Âpâ]ñRÞ16á^9L}Y1›þÁqÏ\±†Ú¡k ó0ÇòäPÎ,‚¹ó%›¤ÄaªädCd!á{'wR¸½ y­€BÄÊ¥3‰tJ;Û4©P„C›s`¦™%ɾ–…ɯí@h›œzI§U ÿÌ|8ÚdH·‰–rþ›i=~O¦|ôfÈöNìÿÈ`wðÔ°§f±F$O6›O¼Åï7,±þ øç´à#‡/(jž¿ß¤AJê "9œ}Õœ¦¥rÈëö 6ŸöDç b´x¾ížú í½1¡ÞðY:×zÝ?˜÷‚—nxiNaÇtø“ËŽ~Lc{OF µ¹D$3œ&%ý&‚¯%îàŸ'6èÈáÉc–þ2¦œ}f8£èã¢5tHÙò+Ð|-ôSÊØÅK*1rA¸Çi»¦1Æ6¤5 v°nÖÆäÞšíI&—Öb·ÿCØ®YÞè 2Ÿ.º[Â8½IegDVOðß“M³‰ÿ4C¦ùv<ØbPJ"^ݯÁrM[X‰u‰Lln36®å*þ”&ÿÍdnœÕm¼yìÜɽÒz’<óQ¯Ä…ÞöÜŒCÇôHæ½5¨p8‹$¦Í¥¦áµlÃm°œŠ;ßBúØl¤‚;]µæW|¾ËçŸç¢ÑèµÞ‘õ[ßI{¥d‘[öƒYš¨N«”.ÃV‹#¤Ç¿3‡ôˆc¾j¸ÄQ¹Kà¢u æ3ÄÕÈ>J¦£’:DÃn Ì'°·ÄJ°gp-ÙhŠŸðOž³èÏbÌÓcêÄÂóŽLþ4_*ô‹Ý›tyjvý8>é”!·úâ©Êý8c¡0¹&¥ ÷ØhôŠö^o€W_ØŠ ªW¬M·_dªu¨Ì>Kªª4Ê|âkÇÉ Ù¡-GˆëöhùÄ–|l€E‡,˜#Ñ×a¥f/ÞÚ°ÄÜžÁ·xØœÛwóਸ ø=ÃW*NtëÒ"¸¡g;þ:2ÈN«¾H¢§£é Q6:\†ã˜“OŸCôOUR%ïÊéÌ(Ôð`Žå¿f§kE3Ý¥óÉúâUÌ“ÉâäèAcÒéÚ†Sî“×úÐy_sn«;[}yÑDýKhhä>Y”w|ffíz€‘ñ‹©ýk’ܪ ßìàØ¼†Mâ7˜0ëaP n`£‚›É›û)¤ë† ”Í9ÅJŠ*Q¯ý<°éÅv(¿ÂrŸº€CkY¥‘|æg².ùÓ•‰ír,;WB€|¥ÓË”@ëX–¤<Ñ+Q²c-ùë1ÆÜ(¡øˆÈ5ur ü$ܱ&¯ŸœeŒ±[WDàð5rzL‰ö I3¢ÿ>cÙ ªÈN&¼‚}èä?‰D—­€Ö äù™30G%›ðú.çòýžÎwד;Ù<삞ÌëÜ)1•DTÜgþÿ`»($ÞB…´n¡ò}ô¶–¥Z"Ƭ¿2ù&Jn䣤æ:cÛ§Da£.u{Ÿ„]3³ñ‰r(-ˆ·§»èÑpe²¬ù ¸a‰hÑ'akL™·–Ç@ÄñîÑ|‹Å›°”©üÔ.únÌÕ®ðÐ%Ô’<-·‡O¡áz&š= RŽÓ§:ö?_uìª }¥®>¡ÿíÕмÐݘ~â~k‚Ü;&¤8( WÎf´%ü9 _ìÉ‘óÇþÙtÁNþÆéëg ìË­äÃŒ\¶eýw}é,œÿÊgÑçA'Pî¥ÀDüwùäsÍ,{Jî>⃲ì("ý@…8‹‡Á6î:Ùž!™j™øUô9]Å'9 %)º­8GZ§R%óÓ$WJ’Ž\«„ìÓ+ˆ}x=ä¹{3Ù+|ÉÙ~æÀâ¢w†©b¥™Ý&äDé=4_†\ŵøŸ?¹~w+¾%3..# OHjÙ'°w?ƒ!eædÞª ~€,áÌ£mÅâÄä©Î^îÄÍ’þ>§Ñª¿ÉøÚ(ۿͧ;­½é£½ÝX¤µÿódg—%qÿŽ€o)yíGòž0sg¶£áÆ©Äk|ÿþGt`ïbèy ²²«¨èÉÌtÁÛh´ ?J#Ò¨x$áêêàÓÝËhâoCÒº¾bÆ®ÀˆÜOœRkH—ʯeXäq§¹pqÕÜ·Œä=7<ìú7÷Õ°ò_sAîÀz¸}! =Võ£´½+µs¨„—r™ö«<—èÓÑ”•¨~–¾fÀ‚æ@ÎÇðõÛtœåù¤5ypWךŸëЦGR@kæzÚ-C¼5à`;ÃñXú“M÷Ófî^ §]¸œ’» ×+‰tH2HP­ç;æì‡xMwj÷ÞÑÞÚ‰žÙ‰úWô“Ÿ¸~qaF> ÁHY=Ö…¨“ö·;IøÎdÌêwSïSu/—É݇ ÝÎä°ïKÈèX ÆÇÚA°8 ¬]HcÔYX`Ö¯¿Ö2øß£—é4ú2qN=Þ íŠ¨!4•´º €Û4m:ÿÓKèx«Šwå2ñ0rý?Yx‡ô¼,çF»™2µ“›//…¾"F0ÅhWµv-ž’¬À'd“°)(& oóæcEËUÔš² ¶†7ÌDŠ|-)ÇGuÉŽ?QðrO&Üh9ŽWty}.Èšþd•¾oóVsqxýU|·ÿÆþTÑdæÎëÕàyZ‚œÞ•Jò7G5Á%ÔÓd5}  vÌlÆòSaP|ß·ªÂºô\\–½êŸ€šçÿç?^ú+V9ræ¶½@Yˆ|» ]ªYÃuä¿9Ò×S7ݸZ}o{Rõ^8ù©–¾LMÂlhå4+ÿa«Øˆ®Gx÷þ NUbY#ˆ½›SÀiSlîÈÁ¸S»Á¾ù8Ì\fBL”`ôëI8µZš]Ûsže~ÔÀõ¶ÿ´ã>1ãEäŠ×:š{Ö=ÛBBí,™­qIøKlY—®>‘ÿU§¶á]´€´Ú)ålC÷>c"o¤àÉë.´þýOƽ&CfÙƒ÷° ˜n¼&¤D‚ïAhÀ,r}{*Nïú.óˆR‹é)?Θ$ât¥ÚY ŸÚ$Éš2KÜ5ϰ¶º¤óóžâE–·ÿ¾*^Ê–üîÄiÒáñM²yX ®µ²ÎÌ‚O jعœ31®kÔ­ðäÑǨ¾?f?äôVÍÃÃ!ùèþ†›æ«ÅŸOs9Ï@ÉªÃØ8½–oW$ó¯ï%á{rp÷–(¸ÆG,eŸ¥N%ÓäÈ”CÕ‡{'òÿÒ@;ò®^ÉÊ—×µý»™ÃÞS©•M%Sé·Š±«êÃ…‚윷2ìQ7qæj]< ÏÚÝàÆ7ŒuOÝI–ÖÄ߯€õ{-1åXBo§ ±ÃB§LlR%aæ—åñ÷=ᮎœKD¾i°ð‡\þúÖ4ÐSulâms"4©£Ya¶*Ë mUŽÝÂ.o]ò¿9УßáûÇz,°ÙC?{lÆÕ–ôÞ.Mô³ÝGºœ¦‡»EÐÅWŸJ­0 Ù7~‚ÕÚrzÎrÊ-R'W>­`c4]È™¸T ë±ùÊê ü76òæÙ½lYa8t..c“Òi BÌNðCUþ$˜lÀDdkÂ)Û\*èþƒ³Q| zô×Ñaì/ݾaV°³Ï_혒b¶ÀìîÃ^SàÓRbÚ³8hÞgRlhôÊË0˜S€ ªúÌ1¥ËˆÙ`ð46[O¦!œÜ¦Ã‡™kñ¥LÖF'1¡w ˆæSàÙDzÁ*â´ú’8Ù—æÏ’{°mòUÆÜ^H¼ƒîÇ›pawÖ€&9<ïêñçKðMV,³ ÿüÏc­œ:„Ô€>Þ•‰eª¸cKÃÄó¯3¶|p{M˵ñÂ÷ëqùÅpòÚ9œzÄØ¢õ“ 8ø¦ž?ó³È‹]D'>žó{a/ªé07šx7{)ùϼTRž „k’<Ëp4,J•§úŽtX¨§?`vóƒÄ_“éM¡d Ø«ï¾ßbñ!Ôh¢¢8‡œV8J^]ØNgý2$ÞÒg§/!vß?€ßÒF&ÊZ1­Â‹ÉäE"4„U¢tÀ“1þk Ç*[0äýÔQÝGAÓ„¾Þ Byd‡}Œ¯k¢þ}R»ÅÎ-dUVXÓX×ìÊ®P±€±²ƒìóôQº~Î.)¥ÛÈŒN‹c%ó&Ó£_öAt¹ Y³l2u}À=8})é¡ÕÜ…wÖ’^11Ê[U‰æ”?èß@¥i6àõóñ3ÝÁÞj¹Á^0?…)óƒ@l³,¼+à§š›W/£Yµ^xbÈTF¾@×7¼$s ?yi9 U–·™¿¿“¡®Í‡Í²NBÝŠXhây^ÁmÜ OŰâ·#zŒl¢:L¨å;Î<­?°±î=û]··µgТ¤(¸‡‡>Z˜9Ñÿ©ðj‚™v<:ºˆ¤$Ç÷…7‰©D·V•È8œc{²Bè¦I•0òm ˆœ²&çïÝgxåôÁÔ÷=âpæI‡ºI‚_˸ .‡# I¿+MCzÉœÑ]4óð;(Ÿ¦J³Üø`÷CS¸˜ Joý݅ƇÃðsÏnúB;æ–ÖkŽm”% o-)‹;8I¥ˆZ…3¢™ 3ÝžÞ›6ƒõ6y€gПPÜÞ"N•. ®Ûâñ§º9't‰‘LZJŠG@|Ázâ-o&.8¯@Œ|ûñ*Ý¿à•ª\ #õo¡ÝàØÄþ¿ø-ýßÇ2g3b\N¿7¢æÑ· d$G§êv3…™›6ÀCW1œ^² åÜÂã?§ÐÏ?›ìý ©0c:ªî·¡¼0­ú"ýââ45QšŒâóïãøÏÙ9~«p Ÿ(*ÆGm°ÒÏ‚˜@œùû7.{IËBAõH=jÿ’#’…Øç`O¶/§*&GÞ2<ãÀK¿>aÖ9²ÌÕŸ`^E4d{Øã— òN톷…¡£ü . ›G´»²_BNã®=fÕvkìýÀå\›å9ú?àÌI4×U$Ô.’úÁÕ¸f±þ|kÓ±ÒI;ƒ-È‚þtªÆ?fW`1ì\ìBæñ½žêÔý»‹ÝûŠy{sH¶o15=µ’„l¨†ÀÄŸÀ/,Mþó9ûŠ$’À³÷ñÓ‚G ôN’&qî’Ö Õd`PœZµÆÒóV$ðB ¬PjDígðúÎLÒóC•Æ7Ÿ ª¦ÑÌúÃ#dQÔMzûykHÕ©ÏÓw¸aVû£ëk;”“ö0’Ì_2…¯âBäª;Ú½°£ýWyðx‡¾mªÃögaØÀÀÜ•ŽtêŸD’Ð%•3òÈ‘ÈÖϯ@µÔº<  ŸÊ’;=$5ˆW¶ÑH׺ þ“ò²ctø“¡§µ~=Ãð'…øÏN–ª¯!•Wp›á/,²T†Mv;aý`$wOH³ù¨ùÀË]|8¯ðG½$û¦¤›»±(ã#ç`ûææÅ:ÖÀa ò Bú,-M‡{&‚Dú?*{©ÒsC[Øç–B"Ñ—` 7ž ›™NºüÜBúDȶ‡FÌ Ñ1œ}}ÝÎ3 ¿ÚÎC{ t0÷LÒ ¿&7³3†;áá3˜öÖ•Æîö ÞžD?é+Ì‘7ºÈ—NÁ/'‹V‡ÌB¡õSØ©6´fùø[IjOŠÀkF>Xø4s…:"é™äˆÃð×0ä”aÕ'jñœÝ/æAÿ–{ƒKž8ÕLÁøI°Ý¢“«\¸“ñF-"P>ÈY¶z#œŠ5¡Çú¡ß9jN‚ûŽ}ÜŽ*^?|Õ2dˆûîCÜ÷sDèåZiz¹á8“øR£‡âàÖ­«¬Çµ˜,Ðoì-HIõuÐ(™Ë*K¿|ºƒv%œÂ•›´aº{ùÝáHÇùÑéÔz׿³ÿC8v|ìA£+î©…*üwëw>oã%iKRqK|,¢[X}¤]ÏØv÷!Ìî_ ’+×@öÆsä©]5‰"SæP£fMjÕrŽå\¡OîØ“Xz–, ajwuá[Ìc³ô¬•uýÄKî§G³IïŒiÔ¦ºrý_îô!_&¨Ã—`bi-¸C¯6KP×9ÊTSG•¬X{–ž¹ÉíàíÇÛíÍŽÒm¨atšªu ‹v°]+ŠW ᇜôè$Q6%À’ìŠ~Mç4œ§ÌF<©‡·êèUÛ8²-”f. F_ãhüô)õÞЯCœ‰Ö«‰ú?iaNÙµ†}¹ü>ÓT¸¬}®“5jÉØ+QioìpvÙ¸þn<¶/'­!{r7x/ˆŸ;ƒ7n¡fS6¢ùÅ¥¨½Ý†l?Ó¹õ";Çû,\9Þî±”•=[²9´ì¼ÅƒÃO¾`Äëƒ$Dót6¨¹Ž¡hÈyÞ!g’õ¸è]ŠdRiî› 'OÁÛz%Ê,>ÁÐë¿ä m­ÅÌ൨¼5ëɾè³ÌA¢°*xO`Ç]¦þ]U}· ÎF¼b &Ϥ÷ YÚTj8 vßÙ@œÔa`HþUN c‘=c-L_e)s!t‡ÞŸM~‚̆°ÝEfªÐ‹]ô¦˜!I™”_ü…(w»-:©¯ "+Ãèh`:»o4Þ_a@¯­_÷ŠÛŒ¥÷†ÿçÿ_àÌÝŸ_ÆYêÉ â3K9‡˜´ì1S÷e3Uù‡‹ß™Pÿ/ ÈÀâ%¸rIíLŽÇˆÚXØÀ  9÷à››?¿Ë@¡âLÔZ{e7ÚcNº¬BÞç Ðá%Cß¼8®×¦“«>Âl›F1NšÁBÍÞo\Ï¿Rä¼Ótìô¡ßU?c*7[N=”Qîd˜íéìãy <ÝŒ]‘/†U†dÁ?vÎÝ ¬½úÑíãc·.§,¿j&¸Hf±²õSèÑn+ξ`q:¥«ä_0 Î~iz ¦J¶°[¾“c-癤s‚ôaž¹ºS Ö¬ ¾ÿ΃©‘äDü/–·¡ñlÜ߯Ôlõl¶;˜Ž âPRY{Y™¶&¤QÎNaâ¹.Õ>Î '¶AZ°:1åÊ(þwU‹ÉÉ[+!ÃUÔ†"…¦ï»»´˜sžÚ¾´!72þ¢ùÇ]ÔëäT¢p‰¬jzªö4[!š¬Ó¬õE¡qäºã"ÿ©Äi Oæ9z`—/½ÏÏ‹+w†“9œqm¨ó 9Ô_çgÛïEijêàÿ},9zSùéÒ`~ U^´Öf6SjŒíE¨,ò܇F+sQ6â*θàÀI¿Àeb™ \î´TÛA‘? wL™îÉÀ¾’iä­^ËÿÕÍPÄÑb´AŠFß^ÅQ~Ù OÓ3>#(”æH;Ü^Â9>øyX’°µMx0û2\k›|›¾“#oKž•Ñ'1ö@µÉe œç•„n¹ÂìEÆäÞÙz\úU›˜Gí”$èÝSe̲³¢DÅE” üîÝÈJö‹³8Í+ÚAöM¿ÆTÖñãŸAlMœ3ÿgoªØ—ßÂàÏÆ›PÖ$Aº›j`iÇ&º2¸¹E`h>ƒ–GþÁ e ¬öÓ/xpÄï”–S‡'ËðÜóCØ|®‘|{€ÇZùÆqÈÑàuÈ,•5¢R;rÀOæÙÙk†É^H½âÞ ‰Ïx{Dâ'¥p;ÕgÒWMŸØ3–øöH –ç}·³>ÅÅá "b”‹uCÁ¢L…Nõž‰ÙîPTHç” WØŽþ]~ø=#ÚzÏA¾c9dVÞbVÊüàÊÛ}AÙU)ðò/‰OžN¿}ƒÃý} Y M÷èËàÓMø_Œä9j¶?Ø7;aOÝ(úJæ”qJßqZ Ïâ!6®†drœY;™ò߯ƒÖÉ ÈûÛðwµR‚K}çÿúA6 W–Åj–Çd?«F Ù:x… —ÀÇj?¸§÷sìž´Ác#JkøhºT¬¼þ¸ï%¨òUê‡êVc­æ.óàLI8‚^'æRÓkwaÚ‚[Pá™Jù@ë~Í%dž7ÙØéÄŽ±!(›À¿Áž°á²ýO25§v€K™ I6Ó§ïxÿ1'>éш-á¨ß6¼ÂÐ;ÿoþ“AÈf˜×WÉÜ<ÇO‚+ëÉh€‰î›ßϦGë²Ð°ö²Á¢¯Ó©Ü¼Nü¾äøø^†ÅIøõ±:ɬZFÜö3›«7Ñ–5<´K©½é·ž$Y¿¶ñ[‰ûæ_Äœ3YˆsH]’Zäèפ`žiò«nE½ýlTÔtû*΃ÅšôîýÌ}òcyrñÕÆIdûé9Ô$±4Õ5¨DÑ_Øy‚©ß;„B·üð¢"/* Ë·2áséŒí2TOøê„þ‰jøÈž?3ÄÞ«™K÷MR„¢E§q¯-7{®6g_÷'tÏšK7 ï¸GW¬ sKœØ4{%qÅ^ï%)Ëga”i"*ò|álÁÉç2“~aƒûâ]jw]Šê¯á£Í) ‹› 3ó›Ù—'§ÃúÚX¼´`uD\ºz^:ÏÏëxbS1UŸ¹›Ïà¦k&Œè|9ºãl?gºwÛj7ÿ&#ãýo~U!¯çÞ`®¥ dô)(ø"L½k“ûKùè>ƒûøéª5Ù¸KŠn©µ¤wYœîLšÝøˆ’_/*óžÁÒ¨>¦‡™ WyÚáåü,ô ›{CKLóáL.C×[g0²;2€÷аã >(Ö§ËT”I’Ÿ'6ó°AÏÍ0°ñ †-º %V&ê¿Ë6SpÍ*eŤ+pwÈ=<ö­Œå0È\§)öë îp%þ«oRqQçŽõ„À‚”UŽŒ”Ò«ÿÍ_&*CuP.<"}»@/"Ï&œ!óÌV£sÛ€EðoÎLôƒ3P~蜚üÈ¥­˜ ïFëTœá‡õRü¤ÅåÞâa«S!Od-.Úúž½?&Ìšr ÌsЬÊ| (Z†»Ló ËÁ–Å< š¸“A=üËVï*„,ÿ`”êtÇK‚tŸÏ{ôáHÚô d€3ç¤2ü7wú§Ïb”Ø¥‰A‡d'ð7ÔˆÀéH7 Ú#À˜·@¿ÎBNÄÖf¨˜ýƒQje…çÅãÃ5XtÖÂÇÜéæ%€ÛŸ£¦²)|³áL齌{Â|à,šcàn{ÜQ4‰Zê“q§±w±" çîgf<³Iš´˜f¡^?T>íãÙ9¸·ùèV§à•«óÈgn çò³Sì[i;²T7 }úÛÐßÀ“ð "¢sõ¨V€ýš·ü¾ª¯áV ¬zLÞm‡Œ2G2Z&AÒ•øÞä3æWUb×+Pª•¡‹V›“#+Óu匾ä?ÈhÝ8ñü+À8g÷¹#_þT»÷óÚÙŠŠp¨ð˜If5ΣM_7“ùÓ«ðó›AXñÏ.fP9ðÜHYI«.§áâ&sÂ3öÕ§~ä^Ò-|4üzƨµC&¿”ÖE€`«&yJuhïóíð¡7ƒŽŸI "SÿçO>qï"«´ìT^ÿõR4ƒ)ƒÀ½¡¤{ ð•#‡ K0euÝ4c%™)q}sŒß«é†¸yŠs^†Ò_!žd·-Ã>øó™Aueòw—žî- “'Œçq "wLƒf“ïÀóT ³|ƒýàuzâù¯ÊŸû¬Úˆ"ëj&†Þ·º¡l‘sêôe8j[L-èš*³#×\ßƒÂ‡Ž°´âaìÞn»y÷BàPv 6Wiàï=AP˜ò=ÅCaãìg¸½W¼)Ä­5zÐÕ# U] hJQkG.^ 1âÜsÀOöòTWèZ¤\À•R¶d^ˆ7ð<]Gg6â’kFäî…8ÆÌ)§zð’kTaÅëHܱk=‘ÎÜ„æY㹯àUFx$þ;“}ó8Ó7ÿóOøgå$c¸Ó—q¼’Š×~†À6sìÒúäÅUo0õγÇÜ— ‰úÜí¢§lǶCòêxúèäÏd9ú~(\8JªÙºøÞwp/–cE‚ƒ1P~7ùô㘜þoæsÓßÑb ÞÞFÓ™jÉ^¶Û´‘±yÄ©éšÊ|Ëß…ÍiÚ©iXž›‰C"Gáu.ed4¥ié-f&ï9¦X6žfKã%£° é„0OgH·<‡ÜunÐgGÆÏ'Ó¦J±‘ †ð«8 ^ocÛ¾w3 q/¦—£ÚFôÖˆŒÅYÝnàÝ™ |ü¦¨0ez¢`Ë,(*ƒÚ›÷1W×ÒWáÑù/ë¶ <;úѤW¡nò‰¸5Ç?®Ý™8õ¯ýeûÞdXÍûîÜf¢_bý¯pQ_*”2öŽN/Fk`ù¹9ts©̴ǽ´ìÒÜÞGdN¾cMg^®q'Sž‹I£§îßAöî7Ø&‘ÅŒêí'õGÛ¡ä•*½–Áâ^ìűb {ñ-ü²ü ‰©È··ƒ ” UårF æÞˆœ7þñRŤA(qN‡£OUئeop£PÈ+,Góa[Öý³¦Øô`_óyÎ^e}Ú¢ÙÇèð듽M{àáØó‰ø¯<ÆGù<#дž,‰°JÞ±oÑž?yƒihiʃMÌÚtsZ7#sõ™õ?jIÔè¡'ß!âÀö‰ÕKè/TÄ–"ýØž=ºÎÚ›IÓò$öÚ‘Ÿh<“Ð{iz¾)‘ò0Dz>Üvðìf;Öù퀬­øgE-´ªð¬Íj"Û¥êÇèÙÀLX4æÉ¼vi²TÞç»ç»,æoA’õìÎÝç>Ë¿Ž»‹CßcaÚ·È—ÑT,nªùªN{ç‰ÓÍ @l—<áþm†gBªÈ.gÁùc¢£yW›ò2Œ÷?FôOL jýTń۬øŒ¥(ž-„ÍŸÚ1Ë(^^ÆŽÖ—hÊ¡Ê\_úu™Ö²j̓ÜqÁpëó+nË3‚¥ýp`® 9eÿ™åÕ%§/†á»žì ü÷k£Ù:ç­lH£^}É!î>r„êR÷ÍÀÎæ0;o— w?½ãWƒ]0®& ­n0}]ñÐ Iì*š†]æf4õŠ?üNϤ¯¿JÓ´_dïÁÔ_1ѧEù*ñÖÓ$FÁÌj~…`Qk?n-–oŠ>üŠIØŸ ~‡EiîŽFxîö w÷ 0ZTŸQ|ñŸDå u"¡%ÿ–Òëóîb†¯(q‚E䮯sà_K\÷¢:·_Iv@§‡,ZWô²‹Ÿ¿†ÆÝÞlcÀ3<µð»³ì-¶õÂØ ©ßå‡ !±°óþ´ þÓ=3›,UQ¶ž‡¾y½•YtÆ 4 †±çóA†ÏgÏÿŽoí³@.œÅ^¢dd Öq«±”OˆÓíÎ%Ÿ¦©“°R²üÁíÉ$9+sšò¯c…åâ–èlü,KÎKÄwÂþuÃ/§_p[ ¼û7#¯¯M®·¥~EÚørRû±¦äÎYã,f&›ãŸŒU¶ëŸKÑÙi àz- VÖ¨@5çYV”@ø£.á¤åC0©2™0ó}™ !²¤Â¯qpWŸ ñùiD¸Ù¸ò‰,®_ Ç–ä¥ßÅ4™¾ñ^õÿðŸ®c¹ÊY¾ýbàsXìºW‘×r«ÐÏ~Ía™2 ›ìÍ(ÿìD»µGoâòI VUD2 ý¬È±æòPU¼lñkx¸°$Þ’EYºcžô†‡{)¸Àø=Œ™³‘˜«yœ=s§Á– ^Âü]M…GøÈ{òyUÏká:÷Á`K*·³9âႳîÍÀ¢è2yy„é›zkžJÀ†W¢Ào5VÕùWtY~?>òwÀÒ„ÇpU> ”ÍÑøxwÚ io´àÚýnèSÆK¥'ê¾ib]å7qÔ+†™#+F`2%œˆÕ¬ˆ±,tÚ†Â/Îãrs8óÀlz-KªÒà‘ªzHHÈÃa7Ö7È’®©ÌÃ+> /Ò£ßâcH³Jš}INµc¡ô8=NÀ°þbü>…\§Èâ˯³È†ÑXi] îlÌë#8C*ψ‘Œ)?˜QeQš.# åãQÓ˜…åAE!¥€‹sNÓû»7<ïQLøžA"9üÐy¬-cíªÁW­‰hªoMž©™—-f¸§’…óq8•®bê­kø›;v/¬é쫺ûߤc1Šð27/û0iO5è™[,¬Ý­KÖXÊÛ™óÙÒ Ì\ö’{èsº•I«ëÇ-Æš0{á¾ÿÀ@$bó눃{4“¿{'ðæ†“îZ¶ó÷}\i‡—ʶÒ/ʳð?ïòò¬9EÅ©_§§jò5Ÿ¶wñ÷^gv÷ú*8i´„ž=kL|âЇGàºE!šœ1ƒCý3ÉW¾2âé7›æßœ‚â¡ç\2^©~d¸ìuöt Uz(GWÇ4£«TÆÍÑŸö8¿¶úd'É%?½“IÕŠ“Lþ¥î ý7˜r’Õë%+kzaŠ æÖ…_1r(!½D&k[Öû†'Æ…!H?ã§] oq¥C/ÎëŶ¸³ìšƒºÄ讵I “NÕr÷ÇÎ i{½!À[—Ζ@õªcXÀÓ‘›û“%÷p§‚¦[EfotÂ¥Ýé@7àÐÂp”+ÇÔiœ571|³¦-9H¯+”s ›9¿>{‚UB>Û¯3rpgÇ>¼Ã %A["÷µ%Sχ´çLúÀ&úôñcVÔù¦J܇¿³ÑiËE¦÷ç[äw&7\12ïé„þñ2÷à|·Š@&÷..{]‚cËäÉJí©h¥ª ÞAïAX<ƒú¾c⤳à™3y³{ž|Uâv( =WÜ…)j Œ¨„(5: Méëñ…Ëê{ß“°œb”‹ãÁ·Ç ˜ˆŠûœ±l-¼úmiØLÕÙI¤pƒ$r·,6¤s 3 úñ$‰YÞþì _¸©Þ ®?Îᚆt“Îg¶ xìocàʧl“¸#Šm£¬ë¸è]p%FP€OU÷ Ë+¡ÿ»&I\R¥ç©½Ù|R¸S€rø×12¿J`Ià¿m]"xÐú8T[Ý`ËÙQ´ì9J\Î%žvۚݿ€\ŽFMéÄH™…L׫™(.Ãà£11rÇŸ–½‚7Ó8äj"y¿×ŸýQÀVöxþŒÝÀë6÷ªåP¦ß;3é\éø0òÜ¿´Úù摬O>ÌÏ¿ÀÈ*Š:˜y•mޝ×5P©$wUlß"‡ 3Ç¡ wÌæ¥w-e>¼–e"CeÐúŒ1ÝbS>^×ÄH¾›!¹êÇZ(àƒ*W"ùí-h[6C´W(Í[áC/É5¡\’3«Áœ¦Wìÿÿ]L¹…Ãü¼Ü ;3h÷9 ÜùYŒ \gsyuhÉCm¶4?–É¸Ø ™tÞý]pïÈdê-?ðØcòeÓeèFÒ„¡nÚ[,}±’*H Ó²lA"«æMe –ì¸ïœ¼Dq(ç²uŸãixḮLLàvõ™CaÝ$²¶B™V/Î¥‹ÎÞ#g" ¥°‹Ù'Df0‡ç¼G÷)“ÑǂĘAK ©ñèê[ç‚[ýIEãAÛ—Oàèt#ò*õ2DúËàƒ%íèòM ¦ÚÃÕØ¬`ÄÛq•"K£Ó 8F õܼ.>{gñ° ü³]­0eÖ&xߎ•t ì?‘†mÃUX\ÿ¶ô1GŠæ¢áp ¾:×>RR̾pATÙ!Cý·%ÁîAszääSÌŠ«ƒ‘åh9_‰ÜA§ý“èü»†´çdú¹/n“?Xæw-,k¬Å‚“ Dß7vYF‚ß…³`2ÉuY²ç‚7C.G’È?÷gW¾œuÅœŠ‹ê &׌m¥ðtë¶üöxžý{¿ó7à£Û]ð‚›Äþz<•´Tq!ú$?½ÖŽA_ŸÀ2‰ ìZ ¯¿Þd÷XÛÑ'uHW?ÜÎ[Aòží˜àÿu9p`O6ëÊ,šLÿ¬Hg|y¯pždž1çå$èqúó?ö«"ÔÓ·ƒ O6æ¨âÉ«ÉìËh“åúòΓÚ@²¿ð&ÿýW=™´xzþó-+/[ç9æD¸ä,Þ´Q"­!`ÓŒ]v4qEöñŒ`gÞ ø ‡Zm¸¿»V¾< ª‹ÝˆL}(H¼˜M̲R™Hï[Ø1y¼—æQÀäMs‰‹» æÄJ|ɤQoä,ÿ6_ì‰îÃÝÐØ%EO^°eÏ÷t`y^‚Kû¢QýU4$¯_2ÿi'û›–}_þqÈ cfí§ó0¨Bm6Û°÷í  5å{Où÷öþt2£'œ„¦CZÁ(QV¥O7LA¹Ø:49ÝŽßÞ…¦£.yg€Ýo¡áq¨Û’ t%T¶ìÅ®jE*,¸œ:Ôµ¢}ÇT²ñ(15–¤ŸMèJñK,‡h²_w¯!e먮ùû+$†YcJ÷½ > °4Ù•\›6Àù.¼=‰gõl˜=¥'àé3WüÚt‡UYôŒõ¬)c“/-€âUYôJ‹ZM†¿|[Ðãq ©Vµ!["Ô©†ÍWNŽZàDüe/Ø£{²FkaËäëðüûdà¶$RÍ“±Œ­Œ Q~ÅKJuW’Ãn8ÿ{xz‘µH½†bsxHbC¯ƒ§à_{,ÑÚÀ¶ß%²ô:;|ax|U¦/\*˜Øâûø2¸—ì=¿ 5àázkÎf{º}J/xÍÅ/a¹ßŒ;únõx­×„mêô¿¹Î—£Í™áÂ&æˆÞ4²èë-ì[?„|vÔþ‡ ®JÇÆ}¡øvÔ”5iš‚3ûJþ([Í`·6WÃ×ÏìÏ·»î/%"ò­:rû 犊ENû¿ÿœœ…‘§[å©14Œ/dklÉ,ëHX¸I›¼F²àÅdz6d5YépŒ´Q¼úú2{Ãâó¼^˜´dn¤ú±ÉP,nAŸVÒ @.ZU’‚®Lvæ·tõŠ\¸±r™×‚ñ›ç“ ø¸{V%†Xðš¥B=ê2ÛK ÑJë^hüX•…+á‘‚<-°ŒÍQ»6Êÿ;ˆÁä$?Î -gÏAø»Ö¦2°!lốÉ&¤×5}gzQ¿Ý²”û³©¥Qª[@Y£ðš'O¢ÿ}À0?#êòx:Yhö ª´&ø/°÷ wíÆ+¬Ôžh¢«š…cÓ襦xÙ?Ì^Ý=ƒJ6?Ã*óg¶Ý“" òB –JTáÂD ²M˜ü«ƒÁäýîgw òt_%»åž+λ–ˆÆW©’º (P_šºäºw¯¦ Èï0x²ûÞ†u÷égšÎoú°+a)qQÍ…Dæf«,•Å-ß‚½±›ÏƒJî':ð¢SWÓ'¯âèÍü´`Äw¼<Â'u¡*ï V§,ïÇ4‰lî+ z׫ݨzêrš"ÿ‰¼ÞÅj…½á|iÒÿ¾lðY»ŒäuÏg%¯åƒGÁ-Ï($ñ7/—ß­„uK1kÎ ú®ä¶u–r"«·Óä;C˜¸{hÒxüç½SgÕÁ!]Ÿ† å5°'u2õù¶ÊÉlrgŒŸ®Úš€gýSA1ÖŒ)÷«éTlì/–cÛˆäÂô,˜Ó׿ê¶0JK´É–QΚ>¨=* ÛÿÔAiÜ0;Ó­ðkJ~¸8ÑÿlüçŒ"?áëT &Iký¡fFO*Ý‚DyozèQ8õ~2…µþ ©¶räO¹•7t§íÛh¥äO6ð©+z=ëcìuõA”Zsëæã¬Ýº©’Éšï¯Ké£[Óh!ÃaoS§á‡åØe·áiî,”“ 7„KÑæõ؉šÖ‡ÉscH=õ¶ÌؼòåŒgVüzc‹®&vÅûpÿÕxvòôVÌ×¹‰CÓ)äñg2·c„ ~æ |ñIŽT}hAûœØ”éä=zõ“éh† ¬·øË>–§Ù³H¡Q øÅžŸÈóøeX.&ª^œB]_†À#¡~6ÊAŽJ¾ù+f“=eŸ0Ö„KWY:!%ï'‘0­zvýzZ²õ$«,$Kテµ5ÏÀ¹h&‰õ›'ÑT@³ #¥­a[á©¶˜ý¢N/²Ns5 #Y§I‚i]G¦Ñšb¯rf}ñ \²Û®7%uRÆäÂ@ëìPï,ôHeG1.ÑÍâ²—b”Vˆ­­8ziîúÌËš/*‚ÏZ\ÔDzP[¿µâ¹Ñv\.Ú:žôé» øæp Yd]ÊìÜžBŒï뀌m-.Ûþ—uÛzÞ†YP›á`²lþÜw)¼wÖMà—¶V§{O—âóÄSèþo5h!q¤À¡†^_÷ÿíû…4\–&ÚÑÑ¢.ü×qž–Ò?;´Hxͼq Ün•bµÓØ~#ƒq¡Fðs™”´ÝäÕ¾NØ¡ø‹<Ì8Šm—)d1CèÜÊÜ"/©ó[=:ïØ¢–+D¯·žÄE÷83×›Þ6Ĥ=!û±ÿÅ×,Ø7€‘)Kèw£1(Xüwî× 3/)0¡'MáöäÂã¨JN4¨° –]h`ù޹“§FÌOò5ßÔµßóyµ|¼“nM1¤ QôEt¬\p»¯;¡MþeºÇ5Nü¹ræÛÆ{ÂÏà}åBºý\ ¤K~‚¨¿ÙQµK`à¤OÖé~ çôì­CÁu&¨X;Š•›Âèü^b¼Ô¶\‰$ÿ Þc1àˆv/zÿˆ>³\r/dI4‘¢gNÒÏgQ©‡r$ÅV‡©kùzJdáxͯ°e ×Ç…¸¡‡U#¶¤(Ä’žú"Nóç®#cwPŸõ•ðç ×ê0Ó½ žÕ™Ó‚*”ŠÈ¢ó8W÷ªÞ (]'7 ’ ÛÎb™ÝMQûÊЩùu=ò›ÄÔ´0cÅKiUÍÈDüUœeX«²d6úž'ÙÅîÆé¿L™ÃÓ˜#²äÐßåPŸÏ727­±Êq9ÍÓ9ËM›éHŒ>Ö€—ÕUèøý.ì± Ëgà&±\j>m äÿ¸ý]ÝPòg†ž?„7;oSf縱ê;ëÚŸFg=;H¿uëSº©ƒª\8ÔËòŒ¦aFXÍèo"œaü{PˆÞròæ“ÞŽp,X5™ìob¤Í}qÆ×Œ›Ÿ#ý¹ ÊøŠ±Ü5Û¹ŠŸÌÔ7ÄmSCîpáÛ”DöÍõ"ç_£›ŸŒ;ÿØýX°›<{Þ‹?[†0ä·'T}Ž„Ó"1¥Ë£Ã/AÖ*O!«» ›}xhá’bØÒ¤Iƒÿæ ¤ÿ~¼´¥?ý#y/â!8‰:ÜÖ¡‚,Îs—ÄÊU¬`¢éŽuâæ'îkoC’J9þ™kMžÉnçNû¼ Gû‚°Ýú<+êeÀÔôeuÿéÁ‹G ˆÃ¢Tô ÏóösPñà;T‡‡Ø>ñ.‚­NÄ_Ï–õSÖg‚æ*ãůgpÓv^Œó“ÄZa=âšejÞ-,›z€v~zÀ.T9ÍŠN¡¬à)[²r·é.žŽU3_t&þb6=à¡ëìÅÈìü_¬ýÚ“ø ©òÙÛÈ5~‚ MI$r:/#tw%é},ɧÔðâ5!¸z[ÇÕà—Ìv83ÃÈâ°þWfæ‹¥3¦Ì6'Nÿ Éã½ iV…ÑêWýän(u‰û]Jƒ>X&@c¨Cß\Ò]žˆ ŸÙeã Ï‰j<ÃŽ¤^®¯…æ8B%¨pëKæ@9?å]Ó81ÿçtRúÍ¡¬¤8d»-ƒ=ÑôÁß}Ô-d‘[NuTs/f¤cXa-̺§Mþ?¢¾œªï{ß\2–™ÌS•HÄ=k›Bh*¥’¤T"¥Ìcf¡(BÈPqÏÚJ¥RM*¥Q¥ñïó{ž¿ïsŸóÜ᜻÷yïZ{­÷ÝgÝ}"ßòÓÄ ‰äNø8ã}Æu‹Ê¡YÇdˆ›2'+#©ûX{äu+V>{ŒœTn`|4LZ°•jÞEãYofŽÇì”(¡ ËU›™›…"p²Õê® £OäD {Š1äV—àó'ÄuòêÿH’~̼ ãý’–ÚìØ|®ea@!¼K†]ÃÕX"™‰w¿ìBÙoád»3!¼éFàâ À±”¥¡óh£UYª´Œð’5"18~Þäöy…‰ø’U…Yº©Œ¦},•`ΡDX4ÌÇ|,ÞFªÈÅ­bdþ«.Ì«B¼ÛDâ#âß*WrÊQÓÁŽ›ŒŠŸ<3NƽÖAT;l¹ÀI·§ž‡ƒº¸_ª¯±¥ûÍ8ôÿøûà5ºÏ‘¦öÑæStëÏ«Œ ïyîÂ%ÅÄ=4æ´ì£í—°Ùä.{HسÞù˜{º…Õ ’Ûõú–ªŒ—%¦‘‡ú^Ä;pöéú¬^?ä¿{õ³¸æÙ=œ?Íc#ÐðM‘ôɨYÞˆVˆÂ%~¨< CNÐæ üW|™ý½ºTOä%ÿ¹_¯“í¸êý,œ#|…õ¾ÞÍæ¡‘²$)q|ˆ݆ ;qµG,ûŽnFÇÀ\ðøJ/$÷îê“cë.±T-‘éžÓzãÈÍl=8~h' (Þ@N.råôm6ÅÛsëP¶`#Jÿ°ñ;–D ¢Îp&­.ÒÁÂ[sðÙù_Ÿ±:<O¿Çþ·05¿2?õ*ZÖ3BçS KKü¦Z„®¦Qv†­Ïûßÿo˜Œm#Ê4añf¬5ùdQøgñ$Þ4] ŽQm? ñRÇhRd?n­PÆÃãZw߃jhÑТŒÃ3ØÿY“l̶÷µ*d±‡ i/ÜIø®*±VöIè²$îMŸM\öÏ'gô× Ÿâ/˜2‡ìYÀOÓ7¥ûbÔ}Cúšã‚{‡Q*è<“³ê$å׎À³<5¨PÍÐMŸøé½¨dx–§§ÑÊf/Ú,<ŸÊ~†§ŽBÝàLºã’6m“'ß|Ýð~©41¼¾ŽÌµþ+xWû­Ra¶ »Ûþ`×ab;Ú‹¦† Ëõ üüs%—àí9Ùýú-¬óÄ7òQ3w2Î=ó›²Ì‰ù R 0òÒœ:úxbÙåQØ/D;*Ì™ —JXÆÏ,¦hRƒû ³’È(:aÁ^¤¾æíìÍäjæòöJ΀º9ºÝ‡ºßNµnO'~ç¯0söi¡T„;kÊ«I×M·d³üZ™Ÿ®s?ï%ô%o ³öâ"bœzŠªd§ÂQ¡éLV«(yR)–=6nîcl~C\ÎéÒËŸ²›ºIy&ðǬE­€0e:‡È£Ò8àßpDg½ÂX\@–ÚÄ£šß vÔÀDý7ž;ž ï2œqDse1n¾=^Ž6ÅÏ¥©•®%]»&ÙÛ@û7\Ã÷ixã×|2þî›™AÒ,MrtÔ…ŒÌ¯„b u¢ÌEgE-eÄ>³‹±èJ,;ÜÍ\‘­Ana8šŒrиñW`4w÷+“çv1P¿+ m6Þäd¬yÄiõCQ¿Üo³7«Ö@ÿ¤.öþ÷çPª•ë/>‚:ù>L‰S¦ÑcÓW˜RóSõx#ñ4Ô >AWVÍt¹L<¦W¨‘[šZWl|ňkÜæÊxRµ’µÀº ûç—€£ +³I§ ^ì  qÑÿÏ!ñÔ8ŽcïN¾Jk²µiNn˜D¬îüå8\¼Å©/½ŒJÒêóGŽYs¿ßÁê8—³nS,àÍ5'l>3 „nýð)>ÇùÐ˱“éæ4}„ Ò³e2yпèchëoæ}Ï0ž^^2n&È'vŠÕØíÛ»OÀ¡(!¦d\ß\VœÆ®Ô¾‡’«ØŸÒõhkúõЬ!•—6¹áåÚk¸¨z5¹¨ñÕ®ä ÿ=¬™¯<žœoþãÚH’Tä(P‘#ØéÞ'à«-Xsåü~Ãbfÿ41ø!‘„™Âzø¬è„.0‚¨š@öâÌ® ÿ6öߢJæÊx~Mìõ'Qš·!lP‹>n8‰ŽùÓ âO,†I0ÔùA𜆚IHPI¡Üà{y„³p$kvf£œU.Cvupt¼On³FØoô„·’pÓáM4PÑ }$©ì¿&l;Î9ûËaO€§¬Ig¸Õ€R d…ª­Kÿ êý+hÑÔ wŒO3r]µà%ZÆ>7-„ŸR½ìÅ÷çA¼ÓŽ“Ã7Œ›4‘ÿ?Š9ÃiîZœcÉÃVëB·ÌsÜ5Àe®FA¯L<{™îÒÃúZ‘%¼SÐÊÐS4ó ¤Ó z ( |Љ1—ðo×)Xu¸ïºö\ÇÿÛo¹îãÿÒ—Ùºð!H.‹²’Ú±•×`Ö³‹ “”çf’¹-Ed°«ûà…âE<>0Æ.ªøÁiº4Œ&ÕÁ޽±¨øº 4òEáËü4øÝz~´r8gõ)›Z¼•;hu–ï+a.¿)œæ¨Œê#¨€ëË O:,’†úâ!DÚ¦ OE<ã{‘Iè2&A*ŠDöW nÖƒIã!êcXî­Å U¦³¶"^„ü 'ÉO,ÉÕ¢ÛÃ)Ê`!z–îW‰Bó± ””z’|4êDŽãAþÖIôǪZY7ÀŽI—‚Íü~6'8åcèÿÏGe8îÖ…*¡A°¨j\Ùˆ8;š, wžhD]ãHVÚHšøh̾9[Q$÷ò.µ Ö‹i©™"a¯,Cë’4ý…wä™ñY÷ ‡äó ï n^¬FÏX]Fݯlwµ-DŒÝ+³è }èÞdÎèl¡=›Áfçðá³b窛»©ôÛ™8¢íÂÙwM Uz»@7ÖK7dãU‡zÐõ[Ý»Eh·Ýˆw…ãlãì?p5X…ãü£¥;yÈfFŒ>¼˜ΓmHÆz.gN¹ q~íÅš =@篰±É~‚ÿ˜˜'µ&Øx±…øÖa òÜÑÆµ³Ì |d|ÊÁGާQ9!ëO¤âÃÂøûë;–FƒI£¾»7™¾Ü1™¨žÄ9|šx=ê7eù)¬¹-Dgçï`­p)\ÐॲÏäàÓJAâ“¡Àq—zCw3(Ø.a~¯¿ÎUs·ƒåÊ™µÙÉìMæDb½ Z?g*´Ùó›æÒüq æðávÊo]¢&ýÂåïÐ&ÏŸLëvÚ“Ëj™Zûënà´ƒñeºˆ4ùofo› N>äiÛá|yмL…žO°#E’êÞOøíÝìéw˜„ÌD\UdA§ùijsÞ;ŸME(?³ÅòÂàô§i`·ÉŠ^­CAx{Þ€¼¿|kgC²¸¶‡µ±~>×™ë=’8ã› ½0î®n†-ûp«ïF®“§o[aÚù6Ö{/†K—“iFœåaì“F?OúÒ”Æ5Ÿe—å‡eÏëÑ YÛL"ÆÇãæâqý)¦NÍN»ÑóûùÈæéT>ã5»÷X+ûŒ{†L5ꄼ½¼´™/W°i0Gv‡Ew¾wЗy“¹áÒ6#Ti¨ FŸ]‡3…ñßàëd|=O‡MØ‹§“ÁÙhzMKFŸšsì®Íîð¦6 ^l¦%[ç#É3&•ßÝÿ½Àïº2pó°5uê.@2Æ!‹ë=˜ßï…Im»,ñh…ßZË ÉïC+Wq.mÛN[lì µ\Œ>­d´¯ÃE!6d™ÉÖÚŽm—²öá;évi´Û5€Ó–°#T’Yf`Ty4î9D¶×$±Óèàñ$h~G*𙡍xn:oýþwGñ*4þ(WüŒ)sì&4믽âdùp)ók<.¦Ý¼É8^Áà›¼¤ûñ¾-Câµ¥&æ~ ¸£\·?æzÒc:uð[ŠÐòŠSø<Á‡sudûbR1Œ¬Ÿ3}‘¢Áz¶"»fï"í¹é4ı¼rÖÕ að俘Û‡é ±¸´(®>Ðd&'°ßw Ñ5ÃiU¡ÿ‚À# •¤•h1g/D}[A‡^¾ÆÇ~+¸¢_f‘¨AÌ‹}‰m‚&äBIÝ·vvΠp žP¥®ûP¹]œŽZáæÜ{¸ei½yY™¤k$Áï¢=¤Ìv X@¤y2þ+ÒˆçÖçÈ|Îí‡GNêÔ:ò*Z^?ÊB”OÄ?‰\ h{ã åÚ0°:bë@ÿ ?<·W—öé8cj[9šmgã}&r›šHî$›‰6gN{îðYŽ“ûσö>cüRŠöv+áÀ& Î¥}Ñ:P4U‰ˆ>ò"*|ˆáãyR“ÉdrÞ'³æ¿æÀΰ™DtÎ¥Ïî§à![)æÐé6Vâ˜&}—(GÊIÒ«<[Àgï ®°ÕÄ»¶=xýp >ÕЧ;ÿÖb\w$&žg¡²ZŒˆj˜À@¥Yr_2ÍÊá×oˆÍs…ÚqßÅ€:áðP%Ô.q"öÎo8zûWNØÿÎh h%jAp“·"5ù"âµòÌÕ¦˜6CSç—ƒøSU€ç& ¥-ÎX5% e¸3T±U ’«M¼Ì2!Oá)~÷Á`)+VØn º&%Ó‘!#êyä «ô\›—MÆWk•áïOMò gN× ƒ'~ƒ}ãy8R#Dzd›áØßÔ“O….þ87?žNfWÉÀë‹Â´sÿSPZiŒÑ3ê1ÝØ› )ñÀkÝÉ\;}xûñ5ã=÷*lìuCÿé’„“×SùäPØÒtCÖ` b6 )Ð%!b¤¡ê9<-ÍaÎ,&íçNØÕAÄ®K‰ìCÔx|‘™)L/¿=÷]ã-§ÆKÇŠàAaêÚó‘ÊýÉÐóR†,Ãõ_cÅñ ¢´¸Œn½£ #K¥¨sj9¼~9î?bèàËedhy¼¦½øj°ŒÔGÒC1,«¼ÖD½çfÜ(ec9þ4ôûr‡¡Â³ÆH÷^bФíŠ"׿ª¶3A/¢„…7·‘ôèxâ¢ñ‹«×4‹üéƒãäë³DFwû ˜÷o>§iÚ;|64ƒ4xÈ™¦× Yæç@Üù¾4™îÔŽf䨇fÊ«¯Jú§(MÌÿìÙ2‹®<Áɦ¿d=MÂ;7}ÐÖc cUq–Ýﻌ{!%‹™£(O^ô^€f79õîàñ¼&+a¬¶ñÑú†ù× ÎU L/’™ê`Æ6ïuƒì¤8ø|ÑêïÖÿg,®ë9È<¿µæ¶8è1ƒzbÌ<‡mÊæ¥£Ïás‘#Ó{å¹8üЃމQÃCǹl'øzeÐ’˜ED_ñ9ºš’˜—!ô×&aªôú Y¿§Ï-ÚJ÷vAnà.žÈÖž‘¡û3¢‰¯Î+!ý…’æÐ*q”Q_K3£“&æ?6lÍäøeiÎ{Èi>·ö™d³7©];Ä]÷†1’*¦ñ¥b4'&•%ë’èÇ¿éæÀŒ™ÔÛï/ÌxU‚BOZÙS ÍÌ®Ži´$u?ì®…ÏÅ^ÂÉ‘+¨©?‹TIõÀ«NOhÔ; 5V/Ѭ‹älB?„HÆ@˜²%ª)KžßšŠÎ )šîòÝx3í8_™ªÁ5(£p³H“SËò ÷L*¾5Ëg‹Üj™B•0:wõ[Üýåì¾í:aÿÇry܈jOÜæ9…~˜3•||žÎŠ$ 0ûnÀõEÿ°:í!üPˆÇèЬň µŸOîȾÃ$÷DÈŸ²“Û“ò­è´TÊþ«៞¤Ïá¬î¤×7Ó¯¬â°0õï½ó‹[àA MÅAöÌjwªÞº«ƒ"iEèüñÜr*ïY±w‚Æ¥AF ï—}Ò^‹Êûm¨½D*Ö Å@”æQœ=½Â2G,¿M’#Sz|éÙ¢—éÊÁ˜ø'ÕÝ9“ï¦Ã³ˆ$áZ Éš¿ŒÚ™¢÷Ç(X¸Ñ”Öñ\Dá=-JwÙNäk[vË2ºŸË&/ ³Ú¢zS„íàÍø#Ú‚Ô˜ÓÎKÌ› Bß6dîtïÇf÷ØÊªùÒˆnš]D¬Åc)k]ÇÖË»»Žn:ùs:múV;®iÞÐÈÆ2ÆßB]ܳ޲'ÊXXv‚Šìˆ¦S?ÝÃý¹*Ô°·Š+ÅüŸyp‡Ÿ>ò½Î¡539$yõ&æçZú–_tk_®I ÉUkÀúC4"dÛ£c$%Çžì±ú‚ ÕðW™)q|yŽ”Ž( A!5¥Å¤DΞ˜¬7arDÞàáýÅÔ¯]r¿pƒ>Ì1ÅC‹òYù¬=ØUûo59rgüüŒ[-æ7 –YãBðçõmç¢KDí3 õà µDUJ»Üþ¾O€e“ç[Û=E¹d.J…ÊÃã1èü1މë5‡®%TÒ]€z||¸n÷B<µë]ãhFºæê‘J— XåÏ•=ÈX„½ÇuaÒ83e¶ýX‡‡®ž…Oy LÀ a’ô![®À–€½ÙÞó¯ZtOê)6U+ôó­õL§l1¤ü3få!GBä4/DÉu ôÛ|A¼R N¢„>e#dMä¿—=S ôå^4[%BnmЗJõ`§ÃO“Ô 0/b!x÷UŒó1!vV©º½XJßÞÿ‰‹ù$˜Sú.\£D†ûä‰Ct=~¼bÊx å4þüÊþÈ’ _+^CÁÁ8ΖF/Ø·1 VœÔÆŽ3!¬Q×.¸xéËi’dçq9|Éx|øÍÄü—é’Ó˜­4ĤO1ÀUgÒB›òbÏâ |Èæ·9-zYÌTÙÂÃ=çA³Ó„–@uøuˆÅÆÍ`}Þš—-§Ü–|«|;uHÑé,Ü!~õG=ð’a ‰¬lĺÑ(d/+Á[Ïsl÷ ±_A¶¾ZA…D^"ˆNÁ…›œm-™7~ØfK rÂ…8AºïÕ²zîbìØÏèêÉ“RÑ#àÔ½‚‰w©æÊ2ç.'?Þ‚´z© 'îÝ4‚B]°ûõuöÒ²%Ä×{©A&ŽùN ÙPà‘HÒEØWþªùOª3[1¢×ý×<‡ˆS ŠvéŽÕxJt'ùss6öL¤ARÂ(xÙ˜î×9…-S„H´ø¼'܈5f¨iUÇ;K¶~ù ZéYp~nçÉ·…T±xêô½QJ†?Œgk%8»ÄÔÝqrï.6xöPËÞÅ(Ë7cà.RT¦EBßÀ;m2Ýú¶5}þ¿c¯ÉÒâi:$që â¥ÑÊ-m„ ý´ìr+jC¾ZÈKµ"Î@w™.GY~ûàd*ó©õ4ü ” xòž6LCp &IiF„I:ŽYú»'øÏ)§ÉÄÄ{cR¯J æ`ÑŸ[`f¼†\´®!KËSÙ¯p†i¹6Õó¼Dš‘D–w¿]V­ý ZW ¥£{@C%çÝ4ž“8ĤåÀ¸‰Å“^P5½AóœÈ7¡W¸%c+ý¹@šxhÝ·%Ž>Z.CËÉг±'Éj¯ðÙ¼µ%?Âj]8™«@žør0nå)6&1,LÓ;­æøù‘2±Ù3€1v’·oKß}ÆÔ=c#Nõk£÷eEÉu5Xì JwvÒ/ü X¢íNÜЊlž=ÃF\S½—\ ÃñßF¥ž ¶ÉdÏ~íd–ÿR„¾Fm2§Ê^8Âìš?HÅ$H¾>“ŽT;ãå÷ü4°çòO¯CÓ`Öüa¼ÿä µÁ¼$µâ/“®YŒZ«ØØÕbÔ¼P–Œƒ¯LpKÞ_6|7/ºŠÞD«} 6Ç$a´ÜY ~®Ny]Éq®™³WåE4PMês÷Ù>ºCO–üÓ_J,—XàÀ+*Ø8“jüä!ö¡ëñ}¼)s»ÏÎ/FOEû$ ‡#é¿’]*w­S?AÊ‚~j¼ÜËï^ƒÆîÕ˜Ò¶/úã ãVn°Õn˜ïð?þ£Ë|ç¬ïlbŒ·Dã§Yh·öœ+C¶>Ì¢AyÎ$74‚ mžÊÑ—E;©X²=BÆÆüˆŒáohLà*ö”°š4Áá [“‡óQ {ºÐ×€Xño@ý7×ñÁKYøí<ÞJfNËŒ1æE°àÄf‡~ó‡Ý¶÷ËñÏ‚*ÈYbH³¿K³ûá×åš6ÕÛkB33\I×iºà›-î´R Ýã\AbÏ2ºkžÝwá 7*„âí¦¤I½—³ºB›Se°CE¶g-s$îþiÙ¤Åpö¥Sæ³ÌT*ñÍxbü™ˆ¡Ì w L 7ç~ÀZ¯ÌèÖu¤çwW`é8‡?£LV.fsû5]A,JùËhdÄ`Lbˆ–váïÕ¶8àß "·^1ŽÕÕ5¾ØçëÄçP›ìj¶ä¸0©Ý¾ ¼×³ìÕ Ò ÅÝÖpϺxu“þƒâ$çê*ÌڗˉZs˜M2kã°5tnƒòÛ²Ä)U†«ÕÁX«í±‘ Þ?™¶z%é'ó\º‘ˆ» ĺª‘ ÉŠ&·ü?ÀÞ…eVØɪ–ضVŠ9§3OþÁ¢žcPñâÞ\›0ÿDÎ °6÷}J§Õ³çÁ(¯ñ!ÊUN`7ìKìídK%ºum-ÖüL ¥W³ ïÖ `’ƒyÿ`EvÎþ–Œ¼O§ŸB%ªµQž.P8 C¯3Aeû ü<å|úbnzü¤e‹<{ísæn\E[“2Ëâ§`ßàªn'ƒs0+ì&>Z/HXŒÂoA”6Ó#]WÈýO—±Û¾Äª. Õ§ËÁ›Œ`¶¶$˜>²›I/iõsVë>O_Ilúøˆë·6Êÿ]Gs'½d–ïûfVM–ʧY¿L🔾3¬ã‚wìß«FÄòŠ2µÓRb7þÈ•‘CÄa_UãÿŽ[kgÑEIþÔ˜m# ZÞ¶x– ³Ãü£OÑ(`%.î‘¡/«\Èš1_Prt¢.ófÓÉqT¿–@ϳ\úÞ-‰n,™L/Êä“yÍÚP*̆4å05êäéˆWöû_ˆYE3âóéÔwÞÔ{Ž}±H¶oû³ùÈPÝ0•|8Œ®aC:÷•…äù˜)=¹8'éž@Úe¨ a) çëOªy’hï‘~”-ýƒŠæ<Ô~†*Ù5)N„ÀG¯2(;ý¿õï¦ïßγÏ3Eòy ¡} óòNŽs¡ÇÒ:ð2¸"ùÉü秉ΖQæ­@4ìëS@›ÅÒðñÞn¨õ?‡{†'Kž®ÍŸDOÄœarÞÈÑ“?À#ÉFnÁßýäíÖ;¨;Eˆ¦4œaú] ¶þ%¶»áþv'’¨û ZíQ)û|ëe«Fæa®!q´TŸW,-Jjgo´¦Àƒ!1‚Ö‹qç§X²jª3W¢®<…|0Å: +BŒÈ•‹¶äÀõܧ‹MimÀöõUÚêܽ´À›Uu›F©?æn¦ø’O“JæÃjMÁ‰øÿeѬ·^3&ËÊ»èÜw†¾ÙÒÀ¸ ÃúÃmŒæc˧âŽQ [~˜&HŸ]ZGüc®áêûÇáßJzihú© Yz‘UòZšÀ©Mñ–Ï_âÌî#䈾]üc„Uú¼‘FB!þü‡å'ÆqþÖ951s':Ú÷9Çy$¯/Uâo.Åé·Bñ¤ãˆŒ©`ôùm¨~j*[¥!H‚¤évÿ~˜ËÆSÊ3ÈõÖÃXÁÑbç%Ƴ׳MW'MàSØwÂÎàçbä…©79~MžLþåǤL®g®ôqˆ­eٹș|OW!£C©«g>=Ù@‰²í\{” í߆Ÿ‚pÃ]l}/¸…Ô†|çìiôcyá—/.ðLj´L7“Íëj@^Ltº½éaWŒ:@[§žAakÜÕ2†E»õñ¤ÁIF¯¨ö;Ò¸°·ì‰³s`‹I(]äˆ\Ë›ãÜÁEOð¾c~?Í&u èl¹mèãVN÷ïåÀµù¡î¸0ý´»wi™5Vâ4×í ñÎüÄ%·´{YÑÀÛøï¯=v¿_9áÿc;¤˜g‰µŒ‰ósD< #o$Y³öàÉ‹-¤±ÿ#]ß7z­øså¹1ÞõÐû¯æ˜¬5V†ô¢`ÔŸB¶>»@Omä'cÐzcù¨vn*ò7™ÁƒvqòtÚ¦î\ ÙPt tf0¢S—°?Tãé¿á0üy2"þ šÌz‹rÐ09íöÇ9{ð‡ÑoØ"bÏj™¦A{ÆtÚÞqy¿*¡ñƒ¡‘"\$Ú‹ƒ«¢bõ=Øu¬BÕaN ØY‘ƒ}ÖäÒZ]:£Óž~µX?ÆšØh`°ìrxž×Š–*ÿð½\ ºwwá¾ÛØ%›Û°åÄ&¿}“úr»žaìWÈE‡ù )|ŒuáÌÇo—'êT¦)Àòg‰ìÌpVª[•v(sAo• m´\ÀVËÙPU!Ú^­ÆÙß‘"ÿ}ŽÎ'/2*òPNZ’lŸ÷mÁéŸxˆx»$æ^JØ&h½_%¡HÙ–÷&ßm éÉ·N Yǧ1>§VQ ×ëPëÓ×$°đ‡¾(?ÊåÙp”ý2ó:oW$ºâHاdÔø&Jb}øÈïÆaÖN¬Ž‘ºÞ‚GbS{o¤£‘ûÄa®bxÞ¹?º0{ìñoï\$”Žs~píý*áý÷ ZUwœÍóa¡¯Ð„ÿ¯ù "Š0õ?I(¶"ÛM.Ð®Ž¤¹L ‘ØAõM>£á^‡‰ùŸaǘ¬Ìïxöº<õMÆÅ·…™MS¹ÃÃ2›@tºÍK›f°ë÷Ê_®KÞ/…W«¹ð*î.~Ø›@aú Æ%-q)ZH#gÂ?Û#`èój²,´[u&ÁáSÚ$ÑÍ0OGÏâ,?Ð'ö â辸ÛãþÜk&݇“áFxÄñ·xA•Y=›¯Ã>}EÚÝÈG>ÅÃðÒxÀ›Mp0¢×Råízzì¯YhÙ(Ý· ôöòQ )7AìŸùÏœ³Éë;n°Ò5 ›Â×óŒâ2•wÌR 3féMØæ]d$Üpê.]Z`TÄ–¾úƒ·JP‹)e<Ýa«Ÿê^¿ÊͲXHuš?ÁÑu ²³ æäï&§„ˆ³¦œÚxˆ•Ñ%¼‚Óè–ÍÇÃïñѯ»èê:‡(eöÁüòFÌ Ý ÒÓæPoß½hzÿ7û>øãôç¼±/À‹žkð_ÞT$‹f‡ÆI_ØÔc ð|e0œ©–¿Ž#Þ“±…»YÉíMp?\ ·®l„²–ðÞô ´Gùˆ€›Û›ŒiËõ¨õÇx\ðxÖ.¬Á¦-÷Ðü»}š¿¦UÃF6ÜÒC¾¾ ý“÷¹…Éó6 …»7`R û¥NÜ6ÖãŸø§t§ “ŸCͧ9¬å‘GÌdýy”﯑Ÿ­Bo¼]N]œIC9RU‹7RÍ•§YBßÃÞëk.iÒ鉬¦ÆYV-Ú ŸùÁg³‘1Â4ƒvÇR!ºo™]4ý#¬ôàÀ{ðZþ*ž&“(ßšJîâêOXpÕ‡þ¸ËG}q§6ËŠ‰íÍfÖ¿ªlƒ¶àîiôÂj;p ›DØ7»Åa«S äλgû+$°¬Ñ Rb¸[æà´Õ§Ð8%Š®(¹‚µ!p:&céîsM˜*I;|׆§ã¯WýìI‡÷œò™P Œû׉/ÕaÊÔ‹ql ì˜LÖæøç”V¢ÅO{uRQ߃²åÿdÈ™lŽÖ.x»îs(ÒÚr¨'Ï™—²i|akA|]÷ÜOà¢ë÷áP¥aÕ {UË¥àï¡éL¿ã!ÖöÐûñ¶“9./ÿwýãÒs¼=­éuº-xÉŒúÉöD¦€Ÿg9ìO*Å/ oÁ¿µ‰É]ŠJŸBwÄ/;Õ÷™É}`Ê®ºgOFWïµyÑ OCfÃ×lKìô(a`û ¸³"]žù²¿îÍFÿ½Ïð»D:@Âok“È¿|ç c|¾O}¢Í@Ã6%䟪Cµm³˜mdAÕö+κ‹ ûCñË‚xxsá<Ô,€ƒNÔ•âîÆ/ܯçf… pJŒ~|„µ¿Ð ²WaßÂ1®ë’Ë\룇ðÒ…,Ÿ¶‘>ZŸ@ׯšÈéž=LÉ__ŒÊÕ‚ö‡}x'>u~ÚÐ÷ËØSën±‘—͉©œ ½›½ ºfqî),ÆÛçf³Ç¥¦Ò„*?æC¤61.E«ÃÑó|?{§ž¨¿?eÃMåã…5¬מ0NñMj6–ÏKŸ½Àîž½ÁtëÛA›ÛnLm: §öðÒùÜ|ŠNg×î9‚ïg·¢SJ#SŸ½˜Û5 'Ÿ^YNÁlj;׺ʓG‹$ÿH"£ç;…ì‚kZ³‘U‘Ç{YfÌ‘«sáÄcDŸI[HkÏ% '=Éì…Û“&øOJV(:' ccNÓx¾Hg’-‰tà4ªÑ~_Ô8£Cù²n6ƒ‹×±Ï4¤qfáj8˜PvxyxÄŒ\ ©¯ #D,OÌÁžÈι—EÜ{¶[È<çK¬šó‹•ë™ÞˆXT›,HJ¦†Ð­Ï¬áÛh1<ØÃr>>¯ÀÅâï2yÞ5vùÜ*V¾ŒÃ ý´d›}xHvÇkf‡6ײX}1ˆÁËÕ8r6‚ËaJ²3ÀuÇ™j~¸ë¢ »«>…±Rþ  ñ|†ÇmN´ó¾5¼-ßÕZhªÅÜ1%òöÕåLðÿÔ¤"æý}!¦æíZ̼9'I·_»ÿ_M3sjJlk~¯yáVa-ЊÎ†ô7˜œÓ Õ/)Ã#ÆE)|¼Â?ÎÁÐï:8ß5Š‹Ë¯†3°åzÖµËÌSÅ›°D¨”E [—SZn€àòØ~þ†ÅºãªŸl¨êtæÜÀ\|y!Äç¹àbs"«œÆ–¨KQkÛE¤p*6}W€KòjdRÌ#Vn\c™_ÚE‡ˆLà,®âkìÛn&¼ßÐmÕOöüKQ|ýC /×% TSëvó›~g;D¼;ͽëV4Qÿgú­Á¢ú Ëœt9ÂÕÝÐÛ¯Ažm36¶FÁ¢¥bÄAOˆ6'®¦¥C<”·òs`ú.tô$´-µƒÉ¯€ò›³©uÏpÆÝîå(c²oS¾’)À3‰SÙï+{`àÜM>ÅdJ=…=¿ŸJ¨ŒÛ9o/ÉÄßfæÜD31üû±Â§‘9ì­gÈ¿'¨'÷ u꜈ÀÕ@¼Ú"LÔ—¢}Ä\·.–q9UÃn.x?®òÁÉäHÜ8`ñJóèÒUËaöì—ØVpž¹>"…¹Æúåäî™ g˜ X®ü– ò>‰¡»¼&â¿Ïºó8øƒ=SzœçÈÞÓ陂IÀÇÁ &òâ>a#È?+æB+©Hj1=˜Žº7®Bi« Z*J=æºÒær„µñ"ù÷ÖÓü HŸgJ=r?°;O$â`ódvç,oØšgô…XsïN˜U 63+Qúä2j..Ïœ¹ €5ã¡[¶‡ÔI¾}5ü0σ3¥1ÿãE¦Á †Æ; ÎgdèÈPÎð $2{Ñ\(?lX‹¼šqÍ” øçC+Y¦ô¼±5&Ç#þ™ßqts6š_R§w ȵàìæ%ÕøÝ®‡o½g9‡2û5Ÿ±ƒ¢Ò¬Œ‹3 ˜•Ë<6zÊTLƒ¹7«ÙNgP°æ)ýrdÌžž'Òxôû]h@ ÓÙõ/d:Á2ßöá}åB˜{Øîw]í?»aÕªw°HPåäKðH[ð ®hÍ~¡³œË*—Y¹3ÇñºöSty®K/XÁ´wÒÀwÕ J˜ƒæÞðæöüwy>›d}*’#á­ø®âØŒ@Í¢8{m!4Ý Bë¦Ò[^¶X{ë)+Þ£OnnLÅìRˆ˜šË¬²Á}Á!¸*0…X¥<šˆÿÛºBXµåZL™6Nfç…ÞƒÏã9ú©G%®œÁ‡ÁR’¨o<Ÿk«Û ƒ[ÌY¡•^œw­gQØo#}U ¶‘µpx‰)aó Uéñ|mAÝÛChÞñ}à2v–ͨ؊wçª?Ç8Ú½ìµÇ  ø'–¬³¦U©¦ÜÓ³áÁb jÁ§€dÏdÏ¿ »lË鯥QÌ5—÷¨7RެƒL RU1l.Šã Üög}Kx)ÙBØfÞÌ–¾™ “Onƒ z*à±1\Ï=€®¨Ègò Ö‚4êEoÂËmš튜©ý¼´üLß„ý_{¶qÔýe7u0D9"› œ…ׄ¡V6 g&]Â¥/ü˜„ŠLÚ }š{ùýÛ˜®ïE‹Ey̼÷éP}¡ëþg|U(v+ †y‰z_0º_N|ƒ^£ ÙkÆÿè xÚ«3O/DquX÷éÚT¼{h–÷§À‹;çaè]? tƒ¦Ž&Ù#ÆKÅÜÇuÁ‹yPîõ„ÇóÎjůP]Ù¦:ß°0õçAË<\ù ý/=œ´æ~£° !9ã¾¼…Ά!ÿ4.óý¥ª¦­¡åy¹¸`¥DycQ„¬/a”ÄJ™Kº8o#Éâ…{Ñ]¤~g„‰NxÁüÌ!XükœoºÄbHÙe¦Ýu ]›„B0‚!s\™ü›i†ölPh‚¦Ðxø)ñt…1xöz®V¥'t”Ø£öS,GWÌÙùÈ#¾zÂÿË|QKK€l𜇚ó~²NŸ«8:RÙ?M“àZB<®84—½1U‘ï#~¾¬ 쎃» /y0­ ^”­¢÷õæÓÜçéè’}¤õ‡Øä5RtîSì2+À8ns‰aV˜`À|-r¶væ&›Å›|”7+”éítƒ˜'GðWƒ àQçž»þ%50c¹“ÏžÆ,ù߸'A‘»æðLziLŽm`>;lÇîÌ (]p ¦)ãut‡>ܽp ŠNŽ` u[_ûRç£TþW‰e¼p½Ï=8Y­@ŠëZñÊVMÖå°à„ÿï.YWü—0Σúäc>1J¸Š¹O0ÇŽà…Ýšôx°umÉ:zåP³Á£jᎠéãvNf Üg+ßao*N6£m‡2[úБ£Ô,@Â{–‘«Ö3ç¾FáÔ¥³¨w?#¯šD’ž¶1~–ƒ´ålj>Ê„â¡ãÙ>¤ó¿OLJ5 Ayê[Ü´Ô‡ymÀÅŠqdQŠ:eó6S«u;ÈÊȧ鴼Ύº(O"Mö¹€kè&ÎÔcŸðK‹1½êQFûT‰GÍ8ã.hN‰‡“õEþíª`ê?2þ¿(‚ƒoç:2¹]÷ëÝMà8*Nnxûã~ÞCdqY0ðº€Âûzư¶ò<Ûäæ×û¸ÙI‡øÄéã’ûñ(Iuç“w?e‹öK‘½‰ÉäíÇÝ+ÇÚ”¤’Wà ¿-ùò ³•©Qó[?w6¸¤¶FÝ%|²Ó†;'üØá(c\þ{ùï_Npàl3•÷¿{ƒñÊx¬Ô‚ìý•̼«Æ«M+˜7?æâ çAÜñjÝCÙþøIÕêT4Ø© 7 ·1u±ï™9>1m$ Ö¸ZaûG¨«{Á ÛVA+®T3 7(S¾å$9P•¨¶+’Ê»°bC*š¾ƒ@{´Šý Ç"÷0óú„©D^ß«‡ÒãÆé°»¡9˜‹‹¾à±ê^×f¾ˆóoìÆýsÞ‚´1 –õ<Æù·.ˆl ¦Ç“Èî bOªãÚ ’8„RɆ4|Tô÷?7Þˆó}æð¹H’¶½ªdût/úgG2·è”ÃN°y½I·xÒÛ¥(ßî`úŽ­â¼<R9<ÄGõ#öϹ—íÂŒÏ×ðS«IúÒÉǬ¥µ×<Ðݪ€¤Ÿ6 .ÛÉ|‡‡àûó óõÅ%êÖ»\Ÿ[Bçó °S]–â²Í7XnÇ22m†ñ¹w–ŒE¢ÒS7òM_æõ¥“)Oňõ_/üü¼%º<‹èf["ÉT­YÎÌ?Cž ưªÄIwÑA’/]Fþ¶è¦L_k„–ADÏgghå¢QÓ=\xn\{ûž™ÿnÝÂ+pd¶Ý¦÷ Ï\7„/iÍhl'k‡¤¸'¼£¡'Sfô¾‚Ë›Y’? Þ;vbü¼(niHXKgàc•$ÔÎy{…°G% ÷?Ì"‡f¹[ïµù«Cü[Žg¡"áë8ÌÎt÷á–´¿Ä*qNÍjaèØÔ§Ÿ»ã¡Poöªµ mÞ(GÛÃö˜ 800ÂòÛ52 òNr>­òlûQ?Þã• ¿`Kýyîê'¸ÞO”Ønf<¤üX“èT U$¿i<„– öq·½to,ãtå!÷ÕËhêõs7óë¥Î„ÿ¯¾!§$`ÑY1R·|Ì}šÙ­¯ñÀ>ÊY£cLÉ´2l–›MöI± 7 ëF1ª®fKÂ/ò[3~ƒ‚ši ’eò¥9ÞªÉÔçÜTºBy-©æ§÷m­hñêï]Ì!;yŽÂø†©ÈÌeÎçÅfð•å6¡K9Iõ¼Ëtþ+€$µ~8u{s«s÷Nzß/à‰Uä^_5ÞHJbìFã?¹¬«UÃÅùÅ܆¸.vÒ"íçEüI ûÞÒ“ì3ÉF½á͸~ÝgxtªÂ"ÓAé¦ Qš~IqQ˜Ðÿfžç±²+ ÄŠ9þk§ókþ«WfMOgB÷ ^ª&ÏG´ößaJW3—¯Xa}h^A½ÒÙÀzð¡Éˬfj¡}Ϩ<†Üö£áŧX¯ñÔç›Y2àHO+Vàmb@äf•ãPa|\˜ŽiçqùXa«”©“V(µØäƒŸ60dVíâ}U“U] Šyy\5ßAðÙ?›”5ð³kÙbEØZèˆ~ ³„ èOÑD2⺔ƽeù~}Ç3 ’d†ì Ròî&–›ƒvîEäÏúIdFŽ3YQ§Hë=þaÏãNfòÿîÿ°ûÇ vŸ9G­œ×;ls^K¥Na»Mi¾x+“o±Ú'Ž’ãUÙÇ&‘­k éÔè\ælè&²yì<~Ú¡G=åÒ8;¿ááÊt(ú"õrZ6®ÙŸAÛnOR]ê>§A¼kY4C«£•qÛ­wX#Jèš÷«É›VqP÷™mÛ­Ù(î\¸¥û˜¦ð¥Oä-iØÚ`¨>ÂGÖ>؉kWS…wBD÷i8S”¤Cõ OcÉ]2Ëã"^ Ƀuâ-˜(Êooa:Ó9’”¤M¥½k…i€IOc§Ò}ܨ¸lbü¯Ûuìod3sjU9%IŸáÞJuö„VîðÌÇ` ì¾>±ëƒÇ/Ák…=2ØÊ‘KSâ8ö×€ãƒJ,IÕ‚…¡2´ÒÉ”^Þ&‰Îãq¿.ÜŸ[·ß½ýÝû`ªÖbøšÁeˆ5yÓ›‰Ú’týW5œ²ø$<_ýCϳ7§®&Z‹rðmx|¨´G¡ÇWÐÅ›…¢¦ù4¾å0mT}òÜ[Ã3ñϵaT™Ÿ3ÇØcªÑðrP™ñ¸Ž(-ÇbçU>b¿;uz¶àû’@v•…0žäsÂø]8ò®°Õàê!h¶¾ˆ1qY{ía¶‰ãLù.¹@B¾óuI^Ù„ý5*.1×½NÞMÂíEXú@• ëÏîzûí6Ì'½K®kéÏAUbS ’ïŸÜXšÎ«K1—˜9Ìyõ ¨Àúàònk›ÂÃ~>å ¢Ãú¤S@fܲÂíŸ'1ºI3è­+!ÇÚŒ\Ÿ ÍQƒ2'ß<Ãè’`𬅉O† ÕÏ¡ðeUP­·‡@7QL½:®G9ý/L©Æ%ü[\‰[¿e¡HÃ>Øsò"^Ó’űTxÝ*}š+¨Èç\º®T¿J}…¥‡êà}ˆ-Ó>‰íÚåE“ÏH³Y½Yø ³rØÔª08ç sYŠ¥õ9#Iá£Ö)˜Ÿp’m¿ûô¿:e8Û¡Mï¯; §uŸpåN&¶=7yľèæœnf6î –O¯€ ¯:¹×oÏΰ©…à:5’9hM<ƒUáûµ­¤äº˜LÏÙ’ï-gÙáõvLæÖ0rYBŽÆ?vœŽ=7ñõNkšè³™3çèîGoXo“—àstÜ™‹fÅð¬î2s%#çjIÅ·=Àv¥$x¹yúœ.¶žÜFo¹}åªYµò—£ÒH;œcH¶*¾…p_.ü!˜©¼rüÇèMÞ34¤¯ï ÓN‰¼±¼l´ 陎F4iõÇtaG8ØÌÐò*'ÈÈ)b5;M`¥Ö}vÝú>Œ´s£ójÝáŸçª´ú„~,h9#6&Üf=çøSvïO”ãŸÌ?D³h š[_Ê3Ôñ¯›+©êÒ€ðý.X|ò|q/oÇnÜ$ÁÚÊñèågDÛ æ7|ÿ¶“›°Â”™÷½–þì8Êyn?‹Lòp¤Ží— .á¡:Gì¹å‡¿FùKÝpÄ+ ó°¼A kµ\ÉÁ^[ºÕ5Œ1ÚÏV¥Ï#î¹ ¼ë8qýSý£0÷r]³=TŽú'Ûsõº·0õ±õ¸eù+Þ˜ˆ·…޲E¯ï¡Õêyô ÏoöØšÃtÎ ó¤«ñ×—Yœ^°Öï"9öGãÉ÷±É´mÝzÙKšl1ÂÊ5oÙ£çÀJ¡ýt“¯17}]ëWš ¿¹¦Dôn»_Ù˜-Ö¼U2DÖz©«çNÏÍ@dÅLR¼©øåЉl›ž¥SFs¨o+ò· WEi€3ÑÍ‹¢ðÒG²'!ÅGÆþl ­Y9ùÿj¬ÕÞ,¢úëσAv:l»G¿yÉn96žÇ#fAúöôÉ´ÿÝÿvz|3ÞŸZDz]GiA­½˜ q%Pñ-†* áðR.¬ÕêF_ý#œÑQÿ¼ «ø€s¤Ï kÎXm’H^sÏ"oM£j’FöHiÒ—åôÞCRwÔŽ¼¬u$W#­hå.qäçQ¢Ñ«~áæ¦Äwß90íbÑ'§”‘=7¶|ñ ÷óÒà‡Ô´çÈô?¿uÖ'Ö{‘æo×9’hžBMd§`ïØåÿúfžžò!~ÆoYïé8Öt¶íXFë#/£¯±(¨[GJ+þ°k5$ˆ|“?é=¥O$7(9¿-!³Uûü_¥˜#÷"…UŸvuƒí‡YôÅûF¦9r¬¿“›ô™@æp- ³§à>ÈGo73GªÙóµZeÃ0lˆe šxà‘C þj•&5·R±'ÍÝ6¦A·p&>Á÷¶Wa±ÿ Ú°MõéÕa.¿‹ ê.°§,ìÓ:à¯Èí)ŵ×n ÀÇX¶»6ð¿gfUŠó2]žX1ô—ø?<Ô¡ w¿Â‘YV¸Z¥C¼;@Æýìп“ŽÝn»6VÄK”ü×\ëfr1ɘ]ÍSøRÙü–b¼œ§=qýWl»8Þ;˜Îæƒ3”¢!S…‹…ÊØ{‡‚Pî%žÒ²4Lü1n? _߯¶N_’ÎÆ¡–8â––ƒï~$rÞ—doîÀ+‘µìÚ]÷qPH€lÛ¬DæOŸEFj÷cgœÞÿ º€;•¦3'K Àžbit*M”Ò÷“ºX¸à23½…ƒeþ|øz[ ÄòØá­A8«EîYÏdå¢.c§r. $’u{¿°™µÂTFM„váýƒlSñ;¨tÒA'mH]¶.ËV sL ²û;¡¡A[Zb`M%¾š-S#7 Ô›xF5Oÿ™M÷Äh+Iøp£ªgžb¿×jš©ªÌ‘å×Y«.“]$N›¥GpV  sȆÕËÃâÐn&›/t‚ÿ ž[»["q霩4j× û\‚õɲ$öˆ']7÷,Ú®$+ÉÏÅŒùÅÓäæÊ^N}ò!ðù!IGWL'M×Íœ;lMP6r‚ðl©­Ø6‘¶Q4Òü7l‘0&’·á~­y4úæ)b¾Y“|O¶¤ò-Ô$CäNòÐÿŒéÃ5ÙÿmİHЍO¢¿ö°ÔïHô©FÂ×–$ĻÚpdÓK¡Ñó*DÜi"F‹?0ñ&C’:ÒIˆ€,½z-Žùá‚ïáø£M âÀ‚-¤E[k¨í£…dÏ ë ü•"= ­@æyKÒçBû¨±Ò&ºâ¹2=á(Ž=à@SûOCºyçLµ&y[çBg®ë'ñ{”þÛO…Ÿ¸Óö>b¼þ¨k`æïþ‡-Šá­øvP¡F6‹n¤ÆKaé£<Š{.á·`_zw¨ Äbý•wx©C‚úÙóÉ·ëhan:äXÝG±¬­§YLß”%ãðJmZCCé×FYêj6“ªVn¥¬b>Þ‚`<8[Ÿá¹9ãƒäÁq|—K dç6}©z«»·‘ÚÝpAµ§õæ¬ç¹Â¾ÝÑŠ-6̳®Ùo1[ãn­Câ®”² ìvB›~„mJÀ«óÉR½y‚ËJ4È03ßÞ…Wm~Tšß9 k?ƒì:=Z¹GÎ?)‚¾ub4Qï rV”âý3T.Ç #`îf#pŽ˜DDªñuÇdÁúÓ9.¡Ü¡v0ÈEÿQ†ŠŸ\FÄÊzØ’‚ÔñÃbzÇx {rëTîÁ+ÿÒ¬ÓÕ­âU&xÀAµ‘ôÃQ/úúóµ‰ÿÿLî½Ì˜œ<Ž#û®ƒ‡­)é•ßoêXoÇȘ™ñr6…÷V®4æg+®î³c$Æ5?ÿAÜÕ÷r²¤a»ÿjð}-K¤¯GÞ†·à-ùWé&ß”*qÿËXvÃòWl†þcfEp ãßÞ ­¿Ÿ‚·ÍÇ“0ÉË<¦GÃû£æxt¤x—[²|Ý/™È¤íð³p.}žþ–‡ñÌCæVÞ*ä9 k©.Î6§ÓgÜi\v“Ó.:Éœê®uaü_¸2Ù?¦ÒBGkZ«`E®Ý—¦Õ³•ÈÓ:=8™±øþR¥‚'ŸA„Ä… ÿOVW*Ã2#!ÿà×OÊeÚ©Ä2šêŠÓNÏ!œš÷ÌÕ%mä~OîšCõ›’Ì_†¤À°Ü”Ve‹²\ÉM•%¤|8•ò„JQáþÈŸ‡ç›ì {ãLZvk ‘Ó‰do.ϵŒa©Æ¿¢^ÆÇ(×€î¸ìOšk"ðwO%nüð†ù”72Ì·“§Ñ²Ìm Rö\¶âí|Ö]‘—^È©&Ÿ‚)±>ôÔe~XÖºƒ:¸9œU õNe¬¢«šMñæ†UÔº|%[Ô—Kþ¬ÊN’4}¢î×—ª’Öo@ܨb‚ÿu G÷®zžÜ‚£Æ‚ôcñ12íG&e¯”±šßOÑ«eû©ÖNK´æŸDÞj³#ÈS¥câÞ ±¿¶³*Ðrµ¾zIë^B]ÁVtÛ?Á3&Qwñ4vrˆܹíK>g«SíÎïìíC h3k7óXÀ«—rq÷pV¾„éÏð·Ï1®¸Ðlú¾÷ úÌS§šÔµä$S>öƒ™”ú8>Ðn øÙÃþ3è½q”T‰ÖK&â_ ] «ì´ÔÍ7²K|raáE¦séøê9ƒxŸ^ÉkD1–ËÀ rEˆÓ¹»²9eÌv2”•Ê\Ü…¶KNbÕ-5²jxº;Ë“óyøß:҇߾ÇåÙ­`÷ÔŒ,W_HœÔàk1?ÜkM^ðÙþ™µ qÛó8î¦Ri²•䮦åd°·(? 3¿¿°t‘)fé…rqŒo÷QZjfHk.¤Ðjb™dHú¾14ÿÏ„M—Ew‹ª‰ëßSÏ{á¼Î#èù:N —Û^P÷v6FíÅ'N?—}†°Á1ì;u O,¹ ;„È ED¾û‚äÒba”ËDï³ 6k5¸"F'}Ð÷Œ†$“æE ÿ9Ö}›ëuÑ,ç¬ è=Ùˆ qñ]˜,ùð‹ö߀^–=¢9®cxhD*‡œ9ÀŸ:Oã4§·`ùzœ#j“5ܲi<ælÕƒxà]#EÚþþå¿2´¦ÆŠpê(þÝþg×!ÓUŒÂ2êðç9w¯@†½êf/ȉqýÈFÆrá š¸[q‚ÿ|MbgüšA'§ðƒ‘TûSo9g|ɻމ“v<_5zQQ˜kDbûh4wMçUøx&ŸlÚ†×7fâ6S=Ê{ù"r×O‡œç)°]oÕ[Õ€_Úˆq@|ï{ÈèiÞcØïc ¿cê5ˆø¨ mËÛHÞ¿€gÜDú+OôV¹Q—®Ýh²æ-ZdœC›„dbo˜ ú)2£ž-gÆÏÅA$¡¼Ͷ­ÿL@¿i¶ˆÇO©tF[Ê&È@_&ag†+Síuf×éïìÇE ˜`ÓÿnûÒóÚ`©çÔ ü? ¶0"‡¡‘«CC?4Byƒ3غm#‰3u˜r·iý@e9aº¬èƹ€ Îz2šáEEÚ¦R³ ¹ä»8C'_JB±mVäsÝZr§c=¿í1üuR„wé|ø)7‹›M3ÇfÞ’½–RFôOH)ût²0Éß$Cqþcƈ§–1ŸÂ3:—‡Ëm&ã„]eί~Kjtw3kxI–þªšEÛîÜ€-5ÉPêH•–÷ãÂðÈ`9ÙµÔ|+@Å ;`Óµ%$¼fY­îHs¬£¨XÖMbVªCÿªº‚¼¤kÖ76¡º:WS¿?b°lJ%D,UÏei¸òêaâä/K¿Yç³/”Ã08ïz(4aŠÇ}V7_™îçå´ªóêÑ ¢g&eê<䈯 M š‰ëÑÔ2Ü.…½c {µÓп>fª.ƒ¼¹2¸Ù é|T¢14¨07ð)Ò𥨕"HOëe`f6üRG‘¸ËKSޢēéà}*„ì]Iž€W¼,½¯N™oX7² ¾w¡… e¨_›4Ô͵çÏÝ:¡èC2ü»»‚8÷ç»-8ðS‰æ^Q&õýÆöÏúª5£GðÝ›"2T4‡¬±ËcãC¤A4FŸÚ7ÞÃÜg*8^ir·Ü 'Ýmë6$¸Ò_i · îó¤úà•ëT¢wÁòêYYÿ£8bËÏ>¾|ìá]ÇSˆ6% ßµ²n¢¢4$4ˆêoçÈyˆê/MÙduÊõjýiwÐmП kõã_PV+–â/gƒqUËPôï,jZɼ=)HôӬòT´—\J Èx¦[Þ/çhö±³+ù¢¸€ ý ªg$ÙYÆ4<Ò%¿‡Ñ™oƒõ“ñÿlg#33Y‚Ê7±|KiúaŠì©»XQ® $>µ]pÍ{-­iÍæP^Êß±§ò߆©fWÑbq1Ìò@qw^rÂã&j ýe9Žy .˜ Mí^ß oë¤I¨çò§ÀŽZ¤gV6¼EÛ<‡ðFXoOO Ñk5w®:Í„x§£ÜEútü\0ãõhÖ2Bo» òÎmÚwQ1©&0õ¤g—i’åyZtJy &LvÁgâæ¸ˆaœ8ŠU_³€ÏXŸ×âyÙ>n»2/ÙFX™¯Å Q3¡ÃÊ¡ü”•Ÿ^Ž‹Ÿ ‹:¦Àœv3Ê;ËüÍñÜŠ]èU8ŒŸ/cR}ïkÂe›`Š­#œåÉf7žg2ËÏ0WbEHú¼¶`"<[€ñ#oa¶Ä(öônäȤK¢ý‚xüË|á$™¤±‚zWX]âÐׇÞvs•Ó~³©ò»0æâ4*×4Ä1̼Íñ2¥½GVÀ¡‡Í¸6Ç—k1¯çÝk›ÈM„éˆB6 üêR~qy²hòe¼Çù™ó+*™Öú— ¹Ê™ÊÔ1øßšÒÖ£ùìèQ{(8ÆWƒNCÚéî þs‚žd•WEƒbÿv!3ÞD£ì³ã"H³óÕ ¬+Ÿ ÿ!OúýùAV´•õXz˜Æ>8Á^~PD]uoƒè3œ¡ù•ÛÔbI“KZaJ¸£®+‡£#_áö¹y¤K»‘“ÑL~+.!®¡{d­Ò–dÄZàî»2Ж¿ îÎÆsÁ®ØÓ*K%Ž{@––­rŽpü={n×ßH*‹ÉlûvÌNF÷âÝÿíÇÜ(ª²ãùôÑJ/\dÛ̦dŒ@ÏOu$_vóý—©¶¯ˆŸ<„:¼IØ:)]¹•ð¿1š°ÿeÅiŒÌã¯lQ  ”ÙÒÝ’þ´KòD®þÌá3͈n'š·Ï1êÏjXÞƒLîTò÷ž¤*ˆÒ‡[ß±=Eè u&­€ë£—9ãßãfÍ´À|Ä¿0¦üt\Ÿ)Àzî± ë4$Däƒ5H¶Ò°öHB÷ÙtOŸ$½{kN/xŒ=/ &@¿îbKþ>cÒ ¥i‚Éul¶fÁüZ »8"›Óy~=üËeìɡ™*øúk7üNv€¹Êß!ðëNPÒT ǬõIó¹äøoA2wÞ *4ÿ9Ê{‰^.xM«fÂþ!NEø÷î"Pª¡ý‹`’ |W†q7µNäð;YÎ$ÃWpgV.¦†¦Bl­2ÜŠ ‡í¡'Ë>8þIîN‰Ydñ¦ìo<ÇGï£z;:™óŒùزit½øZhÙßÌ=Ბ<Æò÷·ã›ãÜa Æ»D°~_`F†<Ùy>šÔ|öYì\}“Ý>W–ŠtÁ¿RÂôNç£ÓŽ 0o쓇11ã~¼€„)’‘¿3 þaóø …oÁ+éÓ0øXƒ®Ÿ•†£×`ÊçéÀ5b’^ÉG%ðᦪþÜ;Áÿ'dzƒç­™ ߃ةíÄÖñ)ZW¡š€óÇ) å~Ö|ƒßlŸ2ñ)TÍʇ¼9µ’˜ÿ¶ÂTã,p¿> cæ,ôKÜáØ–ä`Zù/<»h–³3™öuÌ—ÎɘÀýp@)ÒÂÈü¹Ñì¿¥×çî8çï¸ísâHÝ ¼ÆÏYøv3F•(àC2äñ~òáí|Þ‰V·&ÓŒc‚œUñ™9߯PeŸ)U¨†Ïñ¼ôUžÍt¸ ßífŽ÷¿?q«á›ÔiÔzÄ¡A¯Ž` ÔF\•&ƒ«¯¯Æ-G¢Èt‰úg‰äù`‘v™Ý1§˜'JQn‚H¶Á‘ýüœÇYLÛ©Jî×}9๊{ô3*íÙˆž_mÙ§ÑÓ°±;?Í ‚?ɆôÛÐZ<ÎO_zÀ†•ål§H0f­ÝÓI(]QWœ1,{aëÓß"¾î€ë kµ€¾;‹‘¡ÓpÃJ=j-jASeŠX§¶´ýJ/_jO_êð°“³DŠv+É3I¸žîZû\W•°aOMÀÊBœfè9“×’> \/RÅK'rðĉ\Úm™7+PÇÒ"ªÕÏ‹µ¦“q|léy·‰üÇ^ùÇERhçe·‚JÂ<æƒYéæb7çÉ›_w8ùgéf‰xVä3 šÇÒø‘)ÔçÒYèÞØÇË}È÷¬$öGé6H—%W•EéwCšñГv»tA©åiVw× HW:>V̇²UÌUR îJtç¾1нç‡Ö{yÁÙa«ó#AâÜüìÐkWYÚð]y-Ĩù“a¸JxÀeÁðžƒü9~0{-x˜Ô³ñÑyôÚ§^î¾;eXšz>Ø>g#®¹°ÇÇœ©GîJö¿>¿·O¢ùs&ƒóƒ·h³×މ¸Ö5¡ÿÏ&à§Å²ˆbúæ*Àý¡Ç” ºÓ4³“ ÏxXV¢í£qÏ¥2tªÐLœ}×”HÒ•äIŒÿ|štkÙ7KS›¡'A",io¾æÌåÐÏ×üè—ØsÌeñIÔŸƒQyhƒÿüNãTËsPºm2i¼”‹wgýâ^U7!ªû sŽ©Ô{›~…×~;˜M wá´É@÷üÒ¦ =’X‘½„–ÎùÌ¿X·· ’£ý§1OYœ2[WàÉÅUø7ý'óŠn…KœèAf6Ýdÿ‚ Ÿ,C;Ÿ(`S‘.ž£L—I‚oÿDü{r+—›yç«õç>TÁæÎ†TÈrö‘ÕðOø-ÈšÏ%ÿ­Ýü$ k,ÜiXg.TÝÅÓ§ïƒÓP4Š`ˆg™Žn~|i^Ð]&H•[aóÌ#B· ÁÀ‡wl`£ ýùŠ3°b/®ïA¿“4ìT:¤XAÿ$]’R@t/¡¸¢˜d™i±%7ï¿`¬*’¸OòaY (tÞÀì]ïÙ¿gÀ:½³Ãª°žÖÿiƒlÂ<-býJO3sj0ÆËŠ(˜…ãüž.æ‹G>Td˜âž¦&îÞ­YÜ?jÓaIÀ:8o>»û}*ýpUŸþ¼«G3ÏJÐÝ(ç[n=`Sßêâ“ Êäe0Hü†{"kÙJÇEèœ D\Hu– nj]€û©À=#,ÏŽ÷EÎÑD¢J+ŽªÃŽÑùìSÅ­då â’ý‹ÅÔ@%Z˜ø·–<YëÁd ~ÃäÇ ½ð*¸ “¬ ì ¸et„ å³R²“ÈØ—ëy¶–-å‡Uá–¸“:¢Ð¾ »t=n]ÂPÓ™¨}ö,ãh':-áußwæþ®§ìµ•ûðõ^)¸›½ºu2Ž-Éx#×ô#ÑãïW6г¸µÛ% ¾yÆ2 Ͱ[9À›ŒÿnÀ¿ô)ã¿7a;íJ$ …Ï‚HS¼wÞ—®Wθ~*ès „TPÁ6 //¥ÒõÑzDÇk½ÃÆÀûÓ—4Ca!Šár¬ÙL•Òw3cCqœÝ17`‡€ª¢<ûwáøF’JÅpäâaø,ÔÁ”è8ÀÖŸqT*Ý,’f… „‰]cpÝ.ë$Ui½Ÿn¨¬ÅX&¿xJÔ ÎO€û‰ï¹ÈU|¡£‹¼ÑÇHf§=÷OôB`f†û1޹öëê¾ÙH­%µXûQšÚUŽ?ÄSþ[c<>bÄ 4ãZ~-ø E-< Ï´ Ùý Lz7Là¿e/aÕ—ð[ôGÆ%ò+&\sj=~#—¾Ò¾°åÙOfSLÄ]öÀ©¯¡O\˧'‡ ~ÃÏ„(Fî_–{=aU*PÝñ"÷a¿!,œ%»¤Ï±2‡Àa翘­e}aìM+UfµÜ8®"‡Ýwb°Üj þ«¥¶V%.bˆö?ÉØècp¤ìØÝ…Ô²ÔÑ) –ÀŸåj´ôì¤NáÿÆCÝ'ѸØï×wr^7òéµãñôÆOÜ4÷-s3ýþW'}Nk.íËnc„¾äD¼“b>åC˜l0ÁÒ$ˆmΆÓzð¶|0*n®ë{<Ð!v¢Ñ_>²hO&·z’ ñpȤ»Í¢`Åç2 ]DÞ­¿Ím=Vîn{Y¡Œ-øÈ¿m«¾´ZäìGµÑ½p3V‡V­§(§)Jé?¬Ζÿê–Yèã%{êï`ç?1ò+*úw–pe_ ËqrtµÊÕµV}æ6¹“‹c]ÌýcØa$ˆUná–ˇu)Oík\úxµÛ)EŸ“St÷¶YÆ2·SÀp×\_jD%_O§/îLâúÞî‡m)°éð:òMOgÿXHdÓ'ø_ÛEv´U©%Y&ž½]ÄÞ6ßX9Á­LiÌ6˜õP¾,§k”O‚ 3?›9Ìzm,­à]/ æÿ%Ò¹%–)hØÿ­ë|ÓMžõÛôÚÜ3aç7BjÚ‡€6XЊ3nÒæˆ4È‘¥;àÛL=r»íø+ÆÀÁÏçáu[öVXÏ%>ßÓ˜9Ö(¼£ˆy^X {ä§’ «Z ïõSÛuvGÆ”š1—Þn[Opß ê¿¡ÄŸÄrflÍ@{›ÃDæB |„üO+¨<Ÿ ­“ÚÅúot#âüñ?,œÃ7µ‚”,*fmD’oÍ ×Õâiοg-;&³Ì4ÊyÚ‹B™„7¥ uË·•PôA’VJJ1C}lЦsL„ãì:=„e®û ¬†Ël©øS~‚“Ëê˜í¥a;‹àÈ#¦Õu/‘ÝàB/Ër!«Q–Ö=Yb0ü„~ÎͰú–ß:°÷cÇ¥Á/‘E`©´—æù;‘«Ýôü"MÚ0:n¿Çǽ•‘~—BÎÊ?J±L¹Ó|ÂÍ̧«±•QaÞíÙÌù¯žºõX% ùe`#æñˆÑ«•6´×Æz¿ÄÎ~67 f_ZHö—†0y__sÞÍ)¡O^-¡ò[@q~#þÝÖŠŽ4bï*îŠN’ª DVF3ÉSÉÇZòè Üâ¤â™?tOíâ´÷2 pÖEÐ2"UÂi^£4·¤¦ÌÒÒa~x{ûø\˜$IšJ˜#­<4s^8t’×P`ìK¼.%Bù{µÈä£-·g’›#cX»5ŒüZýg—O£“I¸ˆ3YÏ×ÉÜŒByû“h-ˆæ_àSßà¶ïúâó9¬·$ñÝêÌÆûßáÒ¶vÜYÍœ>”Æ.(뀣ôÒ„ÿïÔ¤Ó„wAÀ‹uWÝC›g9tó¼£xè€ M/d˜‡Ø3=ï`}SÌVÂ'*‰`÷ï~4売K^“É¡g@4'æïXFßwì‚ýâ7ÀQf˜£½ŽŸöná*,4&6EU¤Þkžy´†(¾7¢žó9Å¿8ü¼Šïë, ÖŸ‡8s⳿†^î8 :½èsãXŽv¿1ØY@¯„ȓ߽•ðtñtrêå Êý¥B:m ˆ?Ïeê¯tœøºn‘Tê½c2± sÔAˆkÜ¥SIdñ9*tXœyXׯ®¡·>0’:+&ìÿgs ë’íŠÏ£ˆzE7#+M’W¿bþí ÀëÜÛø=¾‡m)xéßm´_â-SQù»Qx`F/³)ÌóºN]˜ê·EÒ?ˆÞ­Zøà»›ÎèÎ1ùv _¶ɪÍàZVéqÏ4íEÊ«ŠŸò8dÑÈK8¾£ \Hÿ\KºsÄ™J¾Ð¦Þ3‘¯Ñ‘¶Ÿ¥kÒápT½¤qÌæÞææÚ:‹öèqäkíÝŽ¿š0Ù¤¶ QʘݵËMP}£®7/ *þ«ñÎÓZ¶$ý8“ux&MOÔ:ßÎtÂþšý P¸õÎsDßí±è¼Å‚vÉBôµàÇ» VS ª~#w ˆÑã…V´ê±5up“„Ût ÿHbô…0)0œdhžbVLO¤‹ûû!Ú¦‡M4¨¥ÍêÇÀJ/ <ÓPfÿIX04—ãýXÅò˦rLTŒ(ÏýξÝäÊdé›Ð#ké’[ȶEÓñ6¢kŽuÍM‡÷?žBâ–R²Òè)»~ïvbpä+õà:oS€ù} p·+ŒDhm‰5ÍðfP”Œ·Ïnerœ6‘ƒ‚‡ÐuZ'†O+ÄÙUy¸Qì0R©eS¾ ü¥CqL†¡ÈVaÌÄ'Cvvt&ý=p‹D:ñÀ×!Flý_¸›oM¢È…idIê?lͤ®g¬9‰WLf#ä§âs×ÓÌÃ=ð6œÔ©MR«‚ç꼓ހ'^Ø‘•û”HZçTŽ¢E&7ËÜš&·”US©O5Ų8q{·®õmîk(ûé2xHäãÓi¦Œ®°0­z_ÂõdÿvLÇ@™QÌ¿‡<ÍŸ¡%×÷ô ‘CÜlüùÕmÅp“¦< jÓ…E#÷!¶Kz)n ‹N ‘ŸIYòcçÚ¦Là5?šµå09’eÌiNÝáàI‚er1 ~9ŸM-:Êå/V%ãryþnzí Ô?@Ü’_Q4yaM ödï,™.fapÀwŒ6ÓU^ÃÀW©KZêÌ©ò ¦&Y†¬wV$‡}…)«i@6÷KàËÑì Õ@¸ôã'ãôÑ›¦5g0ÍÍÓHÚuYòº|~6¡Z/îYºÔS¦4» ü ¹ {Æ„,Î!Û5ЍÁ]ìl] ßO‹“ñódK,$sgÆRû…ª`yO‚xl‘ ZžËèê :Ô´f\g?™Nôïï Òÿ«ÿoí«ƒ)t›0^wÎñɤI°ˆþ{e ¹òSé\¿RFkQÎ^º¯b؆ÛÑhèPƒ·G´¨E ·æ¸.Q6o`D¡k ò6uãaµR6`ÎSØx&†u>RÌÞ +c.ˆ»Ó!oå-Ün(Sxm‰…g {±ò1¶ÙOc7owd¶Ùø0óç‘#¦_·¢á{~Òpã8Ž·Ã¼;0~ºtŸiÈ{„ü „ú©µâס΋íOP—'$VIr^„%ç²ùmŒíva·Iƒ~ð©g£÷Ó»ê{pÅw%¶øñ7HI˜À¯R—ÉMÞÔ¯ÏñÓ F¾Üé%æìrq’.Yiõy·¥nxµå2H½å!¦­P÷ÞO鱜Üß…C– LPõ³èˆ>ñ=!N+êvà˰· ä:i<ÅÑ)Y<ä´QcŒSAÇ÷,ý¢ú“°Mû ®ž{,æÌ"2ï}/¡Vh¿­H߸΄Ÿ¢’·æát y²ó,— %>Ã*ûQÆ¿ý/˜­îŽv×ñ¢A>w…ƒ'½¾Åš8?ðâNæùSÃùrÔñT ½É•¢ãÊ„M\Þ0Q<~³DÚCi÷tèõ1šÀ;Œ_„¼ô½ð¿µ›ÛÖ+PÍ ô€=™yÛ\L§äØ2Ü^®B¢lòˆ¯O3r¨ Çg‚Ë‹éDàC#\ÞàW»c©ö5H\3“9åFB,ùÑÙU7®¡{53Ð)Ô‘Iž ª>ø{s ²]“.íŸ]ŸZgбôbl2`þ[£ù[¸:¬¥¶dÍØ rj=?z¨J¦‰'ý׿á°{ ü·ÞôETýÚ¬Ì]ß¡„î{Ack(>l¬£·®k«k†ÊA"äðxRÒ<ÄŒ~¶»Åƒ¨%ó÷Þ´€‹½t¿Ñg˜,ØÃlh¡*ßÑšï ¼™÷®š5ƒzºÌîŒã!ü- ™›DÉÙ3™tç§NRcJ¥ê;¨OükºdÆ0H['RSWâã Äe G.-#7^t0³Å|ç6æ‰ZSó°…ìºG ä¼ø¸eç ޱ<ç…iñº"¨çÏ Ÿ6í$ºÝ}¯8•“>©“$’ Ï>ôúÀCÍž[YÎ>ø4ÞÁæçÃP»É›Þ8)ˆžÅ iEv>k:W'W áÜÕ´°Î¥‹ÙïFXþ ú9®‚»MhÍ“òÁÝÚW×ðÜDþ¯} O²¿Ì‚˜UOWãYÇr,4ø„®?U}ðcÚwŸ¶¼(+EM.ÉSî¡%èm¥F´=®²«ã6 ïžH(k`ÐfW&ü6éeä®Ð÷W“ÑáÉN|è{‡}”XŽOUD©þ6>’± nO¡æ[ÁYµPÄ~žýn¸à›=¼9ŸM©Á:›ïÌlNá§žU{™eQ–(yH²\€W³y¾L¥wîùR¾Î¶mC;«U°Š9:–Ë~.tçwUþ¹élvúÒ,Ûm‹gú¥áä¤¶çŠ Õi-ÀCO’ØB£ú»2`xeç'ÏwžcBbÃ÷¸TM“ZDÑÓhåð ØVó6ܺÇ<3•TצÒÀ“(­‘ ¡ƒ!bºƒyÿ§Œ]õ2ÃRïÂwïäZò;(}Ö Ü·Q´#ék«MÝ^öQN&*­äB…Ž1´L¢*íaL¤…Qž¿Ÿ}&ÏC5~ƒmFáàg<ƒ¼/¯ƒ«±u ׈‹ téÄzË£‚3Ù½YQÌs2]$`Êä%­´›,ÏIôåŸÌʲÉäÚïÖA';0/Ê`šlÒðê¬|Oë„P%9²q—(»+njwðNØ?»ç1&T€‡úQ8gjîšÎCÖ¶¡ØÞK°¹íBkpé&x,­u«޳û]F•ˆÿšmxÊ9ŸªÁ–^«ÕÚh5Ia>„N%IkbÅÆj抅^Ì·ÀÏ@œŽ±{Ö@¯9 0]W„~»¼„ÌT1¦cÍRd횫춗Ux²5‘ÖÕßFû_—Ày£0É8bGÿõ ±Vç yǰ¶dš0ÆfÒÔ : RBôip¿Ùn¡këˆwXW}ŠD_d~oºÆ$^¶¢¥{S¦òÌåÀ:U¥Ók±DÓ{bü·‹ bÊ´qYA{eiœÞ_**•]Nkííg¬GfÓå:´°t…+_4ÈØX0Ÿ&=ÀFÙ¹dR÷d¿ëõM&ënq¨¹Ó5Ò[Sµkúa¡u8õ 7äŠAööbvM^ÒÚ^ÅXÈÛEöÿà½ôOÂK*›á~Ý ºméBzñ‡ ‰:{ÄEÅÛ9 ¼¯ÕQh„~ÇÓf,‘Ó2¥«w’uKw²_â\©‹w'DŒdÒ9™G >ëðr⹫@¢E€Å‡{3Bq‡úVz¡ðœá”ÓÛð h€Ð„ÿÏ\y,e&¡XÔTNy†§Kù¹—ÔËèUÄ•%ÃlEƒJ‰#öáidç…94ç5ÐSÿŒÁ¬’ôGÇ ©×˜‘_(Ip»,gH` 3I§˜Ž¿§]Cà~V5Ëè>m5>'FÒL&?ÃÇpÆê«øÍG<û± Ò˜7þâÂ` ¢f-Àúÿà%OÏ„“Ù~W!çS pne‡Æ4Éœ©®dÇ”bÔ:ãFµÿ^Àeñø¾<–\Ò Ab.tLên•èGëP-Ú•ñæo(Ä‚ ˆ?_ ®„ï{ÈúòUÀ½¢Ä¦s6NÌ‹W¶‚Ô9zCo |õ«€KW. ð%)æ’¹;;«Kè²lÌ(¥¿§O#Í©žôDÜ öÔÛ·øfÃ'ð“B¡uljZÄSfÏsqîä|Ú–5ïgÑŽ3³ÅÆóQ 1Z> ÖwÁïã0hoþ¥0w5 ‚}ÍçÚ‘]µhoÖ]Ø1t³,Aý–2ìÌ.Hõ" ‹¦PÁ¶c¢nD•£áWÌfj¸ë(ÖH¸0'ºXurVfáµYkÀðPÝö±¥´ŒirÙ0;oU>–ÒGK'+òf´•òôÒ5 cõ?þ·~ysÝîËŠÇ¿£aÔ)î–¢QÖ«£Ñà>>Læ-_NÛVÎEÏkìÆð|–ëÁW¿uº€ÿr2H9ªÐQ1\QÊmLKgRvŠJÏcŒÿæípb½H¿`©2`Žc÷ÊélO¢{ÔŒnæ¹ÍXŸ= ûÒ%‰ÉñH’h¥]9óécépdøœiªÁ=MÒû¥~&ÆõOvã"ÄÀÐøÄˆ8?VPíÉý`;“ŽšE¨#yNr†šâ1Ï!Ý­¤A"i9ò³˜{DN úžfš àËÐ#ðRÑxÕ¦Y蛨úgÕðqÑÞ_¶‘˜uŒ)~è/‘"»Ž-ec;.4¤k©¢dóO9¥>ï­{˜É]A ŸâñÓ›piåèkØI"5sÁ­]|^‹3Ý ²ûó NrxÂÌðŹ{èQ•(z«]›=yt*™Ô8—~î¼B<æN£ÎüԌ7ª­±rR5 Ð,&Œ}÷µpRïX1‡ip=]:òdëƒ6èá7hãCÝÇè\k}ú\{Ëîi´^›ÿ˜N©zgY+6‹<”E9tŸIBµM­Ñv Û×jFuâzךäåÏ!÷¼ `¯®&=µi Ý×ÉîšõJ v³ê¾cðÉN½,YáŸã<'¸ š@Q3«~TŽdµí‚õ7uɰ¹‰z»•-ÎÀŒèÔLôìßv#4/@Àσ`û¡×=hf7~h`V7úÁ¢]¿ØŽ+SpG ¸¿ËO@G…¦Ó[§Ÿ™ób”a&¢s•µ÷$ ¡’´OÞ€™'BZ‚°´à3ò#޹×@áuÕKh*»2ÿÕ fÝS¦ËeŽQé=-pïh%Dx“•ÝGÐ}åi¬l‚ÚºB¼ºG›D'{PC£C¨øl!]±CŒ˜˜˜‘£ìXK®a/7âé2V8Õ ¯(äÓ¸z@’Þê`Zí~ƒä†™(‘SâjÓˆòµ@vIÙ\rór6H”É7•B’|éâ?2ɤŒ}ÓíÉ=\@èÁÅŠ¸7í.¿>JìùÉŠwý,¯`!òî\EƒÖÊ£ˆÜ!âzGœŒ÷‹ª+“†›â$ì¤ lŽ/¡ÛúaCæ]Vù’:xùê}Ý•òÙ…fG˜Ñµ×þïþ_ŸWp…îö3_•<@ÔÆŒjO¥Þ|qìï¹øiãwæÂ¾JØ2*DUì`nüULJä»qׄ?6##~¤1qéB`Äÿ’-ð«É)êfÃ^îJÐíV&Çnö²Ÿ³.± Ï=˜J¥n8(1‰ÞØð‚}ãÏe1þn¹Ê¬NN`^¾´‡UŸì±gÍVèîj®ýùyDÅ{ÛuЈ^.OÄͯÐÛ4 æŠ+ãJß\H™õêÿõåñT}ßûæŒežÇŒEQæéžµ%*4(¤š£y2‹R2d J‘Ä=kGˆ"šˆ¨4J%4jøy_¯ŸÏ÷å^œ³÷ºkíg=Ï>kï ¼ š0êòîÿöNâ®ÅœRüxL‰ûë„9t‘xÕLŠÚ&è‡3åø*׎ìôx‚†VYÈç=Œ›øéÙиaáxî]ûfN¡æëN¢¬ï/î`á€ç ’b­ Ýab$à—®kºÂÜö ãkö÷ˆQ£»*á£TÌ¿Y ¡¹"Po½\²ÁµçOgÉÑ…ßu©Ô>ôˆS@ÑÖG4åF{Öf!»Ê/êýÅwBjôQ•6úb…¦gã‘[hÀÞ£ÿÕIŸ5¤{3Té”UøêA$ø^ûÙŸîÁåk©Ø {ž{hƹؑ˜µôZ²ÞO:Ec¼,ÀgÉÿöÿàö ƒÎGÖOih¼gwkNÇk ±˜âC—f‘ßl.û#Ùûx[W¥q2o3µ ¶¡òîíµ¤ù[#aÝ8\f¢J“ôIkÛ0|ù‹ùf;ÄšÏÍ»ütÏB"Ì õ«bòf^ÆõM¬à×i´|u-¥Ç qã RwOŸn VÃ/Ǧ€äæQœÇ\| ¿wÄÁ¼ãix%щQ~¾’yvÀû^.xW7M#Þ³ÇØÒ,1’rDƒzn¸ƒÇ[ÿ7»þ”PÅn/ ޤ²¨«aÇïÞ1"|Ɖ8<Ÿˆ‡T)4;ïÁdÌL@«ÓKxÈØð§à#3ª;_˜.ÐÒÂïªôEôgÔ?v ruBHƲml¦ª1Š=1Ýq»RuÙ»æÔ(–„Cçðó;qrfFÑã¾Åÿêž³§Ì&RMF.ÐÁ&;§à»K‘x8ñhö¹ÂÔ&’ƒQ¯_ØÒW฀ýoOj«ßEÌßqÔ¿î8yq±œÑØ»®{ïâ´:dÂï$?Ü;Ì:yí¡ëµÈäÅGQÔæ<¸do£|yYXe…žKX“?^0åìY “àÓ 8T=ú¿õŸÒå1œñað¢%ƒSÑ8 ¾k$ÉÁ” i¥K–<œbZ/˜¡Þ«8| ¯ ’ym «oH¬>ËÒ%0Ðö4ØuÄ`TQ%cêðÿ%·þ—ÌÚ–:<[z†Ï®d£ŸCä1ê§ÃO¾¯ G±•Lrm vmñ¥EꈎFB¬}ÁQ¨hÜOmö²>ïù‰µamÏ/:Çœ™/ÙËN’Œô?8¯ƒ¥¼2|$³!+?#Fú§A×-è²5„R_7òW|.9¬ºT¥±n©„Úô°ÿíc=—o ¦çò8<.™à¬O(ó,»”=°ü$mîÞ†óó0ji’t*¯[Õ³¬O6ƒåÓPJ–$ow©bòšçø$÷ ÌÏkæ¬;6…”Tháo“qò®Ë°YÚ’nnv&zKÝ£!År…¨WôtžœÖàZÕZÖD÷0“P.‚¬Í;¸³?‚©t $fÛиŠÃÄ"}18OãFË_‚ŒsbŸ5ÂfrˆPŽ*†§ý`ÒôCá¿ëV»µèî[/h‘Àðþ‹2ÝqV¬>y¾“©™²iMŒe,ÌË™8e"©Ë¡–œSœ˜åöür äÝšÿóôº`Ž©$ÌÎä‡Ç36Bé®ëø8Á›UhÓk”lMf•ÝǾÁP2/k'º-Ö§bJÙêq2¥¶©ÉvVf]M&#É¿Ð÷Á+ô1Œä,[«N[¾}Ài5AÓ÷ U~†K‚Êqª|ë,þ] s œìD²êSè©¥ålÎI? ŠËÀ—A‘äH¬/øÄ«yêV8@üOä\ŠÒϯ`‘ú Žuü)zÒc'‘ި¹¸t¿:à¹Ývx69Y° §ç¾€6æ,Жz¶¬‡ýÚ/BUÐnú×}5|¼SŸÿÿ&f÷Í퇬(5´ÊЃìSHƒþsõ]F~É^ÅsÔás† R6")©[訒?{„ï.p…‰]¶)=N÷2ñnÁ;ÎlCŠb‚*¸=‹—îÙà‰kž[CŸ£%]ýpØ_ö$õƒË0ªvÍjæx\^OXÍ 5ÒK°Î+ –˜*:±nªû²E»³Å‹ÑŽßb‹®Â´€Pöí)y²ö~"èe©°7cÌ_§C‡&EWˆåó8íè\(¥ŒK×cj‡óø=|ÙõÒtà ~ûyd³ã™¯vp¹¼'âÿتÍLã#=Ì2)Åö]ÚdmN6Û=%‹¥Y4Ad³TØ×mÁÃé'í78‹ÔŒ©|aÑNoÿNìpƒ¾ãß:3ï3/x¯1ñ£YX¦7ƒ|:”ÉT |À’czhÝcÄ)z§N?°B9EAx_ž†²Ïª1ªCíý¹º^ˆl ÌC‡õZè¹h=´@öêu³ãíÒ3©2œ–YOñ¦¡9àQ¼¿l»²}p'“÷Â‹é ¥Õ4¯ñSþâ&œ·ÔÁ^ÅûL¬ïò3k§’†.ƒÉ¬gþ Ú±?‚¤NV§oã38ƒ‹Æ&ì÷ßÙÊÜT’„D!qÌ÷Ö•¿[pô¢!ùW´¿½¶#ó^Ÿf¯æ¡î¹x`Õ xà‘α{ðšF»Ñòà#ìvX¯_XA¬_;Є|}õLS˜9ª„⩟à=¿1nÔX{/ÖàÊ¿Ïj¿ì°¥F&+ Óý¯îðS¸m, KZþ²‘ Ikbù¨¥ÙAF­S–=Y<«¼Æ¹~9¾ž—ŽóÏ~€/œÙí{§1yÇÂóB=,h€äù\˺‚‘cWÑãf-dÜÇ¡¦!\Þ‘Ë;t1ðázˆ-;„ó§.Çòäw¨;kÛ;ž“ÿ¿ý3Yé“nPù&Ê­à#®€Úd<^Š“÷¤Y|½˜¯¼”eRŒé“¦kx6B†rLŠqùyJí©ÙZuyE^ßÓ"Sû¢`ÙCS ;Hxüw‚œëKöÊ )öõšÚY„òoJ Òl3éŸCÞJÞf^'ÏF{óxˆó 0þ2ÌÑÓDɹäÍ]ܳ~ª*N"mÍÙCnž¬è’ãTOT˜u¼”ͽÆ+¥Xýé t>'ÅcüÙ÷]ü¸±xþ2:ˆ Öõ²Nâ±à¯š7§½âºJ¼‚u¡?@þÅrÈ}=¡ÿÜÞ•qX»ÜSÚ% ¯ú :v„ %¨®o‡6Á&(û´­Ö+a Ý}þÆÏ¡ì¬gWèÀ~nvÞM°™>‰ö¬|‚¼Y…øIW„³¥íf¬ÆYÇ_Üô­Àû\ÝšÒð³îoëÈËü>B#ï žº…Àkâ ú£¦ìYðÂ#Ûƒhd©>]3™Î Änat³rA?õYD-S–¼š7‰Dzþb8œ‡ìäshÛú“ñ’cŠÿŠ“#ÇíÙâŒ94[Дž•óUÏàlj8ˆù¬‚SÝãã°D‰~)nÂõlŘ5QWð#ÅO'ðï°s ;ºÊg°/q’—%Ízµ’N9Èý³@“««‘©_ϱ÷Äç²~ºIx=¯ˆÉm5¤E"àûê7¬¿QŽ6ëmqVSòR¶MKšœ_„Ú'»q«z8¬^¾dg‚ð>ÚòíÓjÆŠY/aÏxn‘ìz «}ÏÀËu×P;ËZÿlî >RÅ— ‹"î Ñ 9²¨ì^]«Óz˜p#EÆýÛ ¼RãýralEz.Ž—Æ¥-•ÿÛÿEr#êþ`šg²Y&éŒäüÿÕ.×,téëÕ[ábúÐûW ߺ¥0 q\?É QNúKˆ=H ÆùÂñàלï³T ÙŸËÔš¯‚xž(¼0:Ê ¹ø¯¯ÿÂ\q¯bz/Ë“çóŒ@±<ð êá…ÌT“'Õº!Üeñú´lf;›°ûMñYÆºßŸËÆ¾š§—Ì_)$uª¸dÞÁø´¥Ì¿…ã1§dFmÇcR­ì;wÖb&½ùÌ k®ýút©r'e-¶\TÇ}`¾ wàãìÓPü!×ÞÃ(Ž¥k0Ò‹žqf >˜ˆÿ×׸v'õ`Ey;L}Ê‘9¤Ñ Õ£¯€BñgŠÐfcQšÖ Evœö +oßÇ›mI¬®± s:{y·´kÂéû¾|Ü5 =‘ldP–>~ƒÚßñÙ®F¦GÌ ž~×fZç©“þ‡ùˆßßÀ^ýü͹HêÍùè]5°¥­gþeþÛó™¿Oˆðš¨“³Î¿ ¿cg$òÇãæ,QaÔ—:@ï,Z<=™ð;ñ’âfDÇm],ïB69‰“³—gâýISQo%ý‹ËГ7ä[ÖÓ·ÙadØØîéÜ:™‰ù‹_ޱ%²ýÌJ[/ömy&´…å€õ滸٠ŸOK“4ø…—ûæáÎç·YÑðoþ]–Yû’]w¹à{-쯣ŽÛ3˜ìq V¹)‰—:"즄å³? ¾0¹Ë–‘ë¡Äm T]ÿ®H¾…ïfƒ‡óE†ï» yÊŒ|ëtË7 Oôe²abjJqéÇã8n4b7Í e`üî‚< a{—Y]]-®f‡œ#¿1m}<ôàÜ!0«¿ã97ßøîˆZÅ u; A0+('ŸÞ›ã«&ü°áã{¯ž½¿Á‘Ñ¿ÕÁqy…o5Æù¾"•ÛLÆZ#Ù›yý¶ ɇ©<øX'—'}fÄ»M‘\ ; Q1RÔ&ûì®¶¦E,8'?Þ‡©ÇXí}øe#:»!]î+î¼yÆ}®ÑÇ0çSðL²N—b<Ã6“X©5_~†. ó°rÛovãL –W©¿&gâø{*²÷#Ç{‰OzÉžœÄ¬<ÒˈÆWãŽÇ]¬Ó(/D©‹QíÁ³Àõ9 7Ÿg0Ëïáâ㣬_– Êw’33YË@vbüßýÀä»d2åÈú¾ù‹§=Ó©Ïå½ôÒû %oM:ÔkP¿"Ü}À¦mxváIÜvOñ“þÙäšÜ¼v|as¹[fWc¬ñ:ˆ]*ðñ5=Z0"5dM GÉÕ*vòˆYø¨ÞÙM#}fŒž„åÒoqN׻Үь[é âü'Ÿ9½+Ÿ]±´ÎWú0äÿ1–ªO!Žç‹ýÌcW˜#GÏ£Ïó¡Ð‚P¶IGLÒ¨MHDmñÓ 4ÑS„(î\ˆ½;Iý{Œø¨øˆž]à¬:4ˆ”ÉDþ×7‘‡2ï›è-ôÇ®Fç­;ä'„ã›ó¬GÁ#|yé,þéÎû*ÿÅ™Póø5ÌÕµÀ‚°Ü•™mø¢½€ ­YÓÌ8¶{6àJ÷LÜ› IFã'á§Ýì™ë±Ïþº§M‚µYYÿ@Ì[2ÊêºÈQ=.FÞ-æ3zÏðf©ýöî~Z~–±e3ñõ3i:}í)vËŒ¹mÄÛs/3d«/ÆJ5±A6”~‹FÉp¢Ñ1²½eÀì¼ÌdÅ;–Y© oaF‹N¤â£ƒÓè±sT€»’möþ͆Û&’oæÒs‰šK  Y{èøWöB³ U„bû! ÒZ Í~»˜°?Rš¸n¶"Söì„ÑÂpª…—¤È£j)/a»Ú'FX°„=c»õ…H’%{v¬S&ŸDÜéy¹V¼*Ý ƒÂ%´Ål†¦àÍ×ÈíÁ1ÐK4%ËÆÀIUx;`Ó+lÎÔƒYlìXÎÝŒ)¤zÅ#l½ÍihÜ ƒoö­¦¿à}e¹Ö8TOŽÂÍÐC/⬄dår@|Ãòfþ'øa½†ì¼&BG¿Ý‡àʽ¤âæo&p½Ñþ+HnÜ ‡ô²7žóÉ(ò wãïE"â0aW1ç#AE­`sÍ F *w=}½{yØUß,ˆ°nu0‰~3å£_ú­˜ŒGíh#bH"liü¡?œ¹®ó`¾4±ñþËÄ\6a·/\‚}û£à·‡.ÉŸÌOæÉœãöný>‘ÿ}àÈ«oÄ„‚Q¸´Á†nšCb¤ÉðKò€¯„|ÌÃ=·I‰Úb¸ò!^šL£Òxˆ·þÒ½´^*4Å’À ׉Àpž=åÍ9‹øëëA£C¥¹´ ¾MnÆ6áç” Éþ¸ØŽdÓ~òår\H{6˜9å Ý!±‘óMË™è÷ýâ~¶{Á~¯oÑyß f…?«Q!Lf›‰RÇë:¤Ùi/*”éÑä%‡hÄóLº(m ðs×Ѓ{jéœÕN{Ç„‡W }Ö(k¹»Ÿy_Jý3V‘ƒ&†T….¥?‚t&ü<ý.ë1•á J¡Ò¹^0J®eÑ£,VQë-ùì ¥•dô’1I[o@¾ÎÌe­'ŒpÀ1*ùìzŠ3Ç®¡×47š“~“ç$€ïë0ÂÈ%_DçÃÖn´9€Ý‚ojÿ½ËpDtPA¢ /-”¤«Ÿ©Së[ú£Îþ I¾¬•À9‘×Zé}d4÷ô´«îúšmŸCkçÖ2[Ë'1r¼g¸%>ü4”Þ`¯ìê„_N!Èô÷Šj<E®L‹å3˜R4¿äóbÖi[žÁì~£Ä“*…[ýôø.ARP1::I‡Ð¨ë'»5ëô ‰Á[‚Ô;ï6ì¯{Ë’ÌÙÜFÍ<o a]Ý€Ô”ép¿˜ÝЪK~?;W.ƒ óe Öù.L~ˆI.>°˜×N¾»Î©üµ‡© ¹ ƒzÿ°ÀÜÞ zÂŽØqápÓ É;×Unà^)¨M"ös;Ð\K.¾ÉXœ`'I‹ÂjÛi !pžñ/˜N «¡ý‚>éìÈãÞú µw»ñS˜kü»¢÷nb¼:‚DZ”mó«d5ª>A¦p?+øówõ..cøG½‹Rù½3Iv°ÝR(Boüßúo¯ŒFêÉ\b Ÿ­ 0Ôæ…ýópwpÅRµexëtªþ‹å>í¿Ž!qêxOõ3sœyÀþÔA›‚ز”6ja4K öò†}¹—èÔ5À%^îœiºds³0íñ}ͼ’ç%ý뽨Mç9¦_ªk‡¢n§ž¬ƒ‹56a±Í:êd~ »ëa¦¾µÖ D@<]öcNÙñØ~ÖTerwÚwœ›ì‚«E8¤iòÊ›ªZgjHÀT5úÊ’¢q’"µé†‹åÖ̘Ë/nÕLì0gÇ1-Ó¥ÙÏdèÖš $Ó}ÅÄüWÄùl;Äðg„r¬ð?]yZ¾D¼ëý‚-" ÌèÙ£QE!º1ÙÌ=YLbŒÒØÿê”§ý¼KÞÞå¼µ[G_ñ€)ËC¿§ 2Ç‹à—ëåè☉îþßpÒŸÓÌ.×@ð<ÆxnÓÂO·Â˜­ÊgàáÓ&ø¼|/>—‰i÷jÍw¸ROŸ ôº¹–”·½gðQKö{Ê6ûsÚ_+!JQáO§È&þ0,T£»fn…c'x@]J T/ÂçUñ«né\d§{‘cgtØ×-5(-mJ-<9àmÇH¼Ša\²LØÿû^8_uÄø­ÌO‹9lâtMZª<‰ŒÉÒO{Õk_NÓÃ¥[Eiá¬kØèIËËòqÝÚ}@f%Â]ð`À>Ú+©‡•@!¸–yïz{ÆVཞJøèJϯSÆ‚¼?ìŒ)Ôü‹IÏ#éÞÕÝøàæ´^'‚!¢8Ãé%¸­\ Ý+åéë¿ äå÷|â%üß~Òv^«3!&·~ÌerâùÀeîqÚ´oº=/Æ0UÚÖ¾L~^GÕòTRJ‡.ñw¥’Êg¨ñ>A: .E䯣×N‡6`fç\Iâ?‚In u΂Æ€‹ð3ð×UÈF5gÚÓa…K\wÓÄýž(ö÷ ~¯PÁÍž’„Ïù&¹Á.=¹‹^xê@Ý¥¡î_ ;£õ8Ãòdï e2RYûaç³ÇÜ;Wã©ûÝ3¸aýTúUB“tí {ßS«I¤ps4=òGšœàù ñ‹wƒièvÚ7KÐ’^5áÿÎ>úÖy½Ž¦ì Á'pIOÆiha»k xD1¼eØúG‰eXè 4bÉÓéd_H2é[}‘îýù|µH»‚µ%ÙÏ*ÑÔØH™ }=ÍÜJq'Îafsyjm…½j¾Ôïï4BrFàÍÆVVR†‡ÆmQ#.?›ˆ¿üBòIv+½°¹ãÿ³k…2Óu×RÍ¿²t–õ1¸<Ô¬\ÊPÉq›kq— '‘Z¥˜8+ˆ>2©…õf[PB]˜ä®.Á´îÓ¨Ó­N¿ÎÚL¿-é‡ê“Ôåç<ÈKí¥1gôÝ÷áç_FŸcs¦›`ÄžïhÚ„g<ïº+!9}_¬Õç,Sû‡o÷”±©›Ï2¹ë~pŸôîÄŽ h´Áó4ý¡,HnfüaÞX»ÂüÛLŸO$}¢!ÇFL˜p\{só¬»笩g ŒtÉäžpذ’{çèoöE§]©Ìåj>Á;KÛà‚Ð6X7žë|GuS4š‡Ö¢ DI0€Y•¤¾Ï/³J2˜Sû~+#g­ Ï“)Õ)„yÚÂU{Ù€|\œ¥Ÿ {Ö®fN'ŒóBKªÁWÆn£eö¹7CÛÛµ¸æQáþ¿ÉLÇÁâŒ‹ßø-½œ~¡@£¦ «ýmøfß â4 C5ÃÙx7€˜í§q«/9—ÀC4CyЦ9fŸ5ƒc'Eª7f(ª“¯¡±0W5”NÑ] ×oŸa/)¼eßË®§ÌKâäí„LnÄþƒé–‘ÈöËÓªµ^ã¼Ómæ§ãÌô£pb>‹—âЫ~;ë¶(ŠÚüÙ yÂñŒ“’=ñÚB1îð0~y(Iì²Kpj„(§gh1çîó»Lª‡½®$†W>šÓ[¿DÉÇ¥F°aÞiè=;“ÌõÐ$§yþ@jÉJxœPÌ Oà?É!UlGõÈö®ÄˆÍòäíz˾’Y À‡ë^ȳï?´oX¨–U³=y¨ùÑû Û`Ó$SR½n=5óž†"~=øv§7?.m.šSz‡»Íë$j5kÒùaøDη(~ƒ–›“ˆÁoO’ð~9^ëO%]/Èü ñ÷½b+Ø_½ø¸QŽ*¬)í5ªT=ºå]¶‚[Tq8;[#á~†3Ø>“ ó[ ñR’!}|4·*­ ©÷~ƒâ™H¶a®½3Š8¸ÂR5OA¥ðC¦ßk2VÏiÃ'„éí[ØDvNø?O\‘6D[2½½µ°éu é×w3 cÙîO3E™šyzP»O-Ä£mÍi §+×ý‹;DÈלÜy˸ù×Ó•Ž•˜’'è ¿ÍBwSÛ½¿éœãÑ)¤îÐRº-"‚iµ†^ûYtîO9ö^{>Y®ºÏ.¦5„xnÐ$5;äIlK§Ÿ¯5ßn ‚&‘Ô­sˆþ’|¸ýQïtX‘ú;¨ûºkÐWñÇÿFÕ¼àÏœ£ÔÐ3R2¶&#º»Ë‹>RP¤î¼sè»”¸Øü¶ÏŽÄ%úgàûòxr¿Æ ¼¿TÝ<áÿïlµ1ÙãÄþ`'ù¸q9´«º´±i&Ð[DBÂSçq-}M(³ÁM+_¢©h2µm{ÏHœî©ïð¶¾9)N¥ §‡Aç—'L wáo·¡_eëqÎ"òþ¢üõÏ\ÓÕñÀªê’ŠM ¸W(%žek·§ÒE0%Ztd)f:Dâà[ÇTÉÛ?úøïP;|æ‡à¶eŒb$ v=f–$ûÑaWzIÌg_M€IG´ÆÇã#t2ãLý} W%†£ýô1Tþµ ÊaÊñDk\¯øg‡ÓÇΛ'ð¯ÙNRböÀÕ@wóÎüÛBä{ó 4ü€¶Sé n4“°|Õ /‚ï Ý nVÉäíû¬ìyçšÓeÒдcyñu'¬ÜWHTÒ”°øÛxñ³b8ÉP{F™œ?²+¶Ga`ïbÔ=ƒÎÌx¶V§s'‡’7»ÝÉ¡º/påÉì‚ã¸ÍË}ÄîÙ’Drƒž)¦0¼‘ Wp?0Fz†—z=×&Ó¶ö@½s6û’ßʾ r>’[y'¢‘z„A‹ÚQh¶4Å&ªxeÓ5ÆqþyB±ò¼á,—†‡b¿'ò¡s5Fë΀º’$õQ¨JÃÍ¿DH«Ú7¬lˆ¢“¼”ñuÖý„•ä…û0”? •á7ìæ·ñø² ռ훮0™£ÁØŸ?2bc™’•# üq'0â˜Þ9M+¤DÈ ¥FfÉœ?\­+Ò¬´é;fY TÉÂè´u4¾¹Ån+€æ~e:­Òût)¯{-£çÓJN]ŠA§MÉ¿k 0%{ŒIY¥J7_¢·2NÁïçª4+ú&zVèÓº\®¤u7½âe““?#ކ;3Tt¦ÕÚâEÝ¿œf¾­ÿ7aÿ”.Yô[.†¡/\I Ö,j)0•žÚ¶›|ñËGñ¨,‘šL“®,¡«\€UùÜÞ§uˆn½þ-÷ÛØ9Òah:EvD¢“—?^>·t¼èάpøJã­^ÂÇ•X‘îiaÔ}îU:/¡áj&8×®†iÚ#LÁël03:ǘ˜ 1O«ëqÕ¿ÝäÐþµt84\¶…£¼&3Y£¸ê¥ ·S‚F_ÿ¿û•½¬ûK²eùL2É­åBç¼{(&„"êž4LÆ”žèO"†S®šîaZ¼ê#F|Ü@ž[í˜À¿ƒfC̤Πn÷= ¤[´ qóx2·þ| A« QtêÁ³´zw6¸eñ3ßå¯!žâ%ÛÅ“¹Ù¡äì© ÈŸfI276£Õ¨)½xš†Š*‘Û>·‰#ìËçLÚ3WrÀ/•²»šÀ°üßµ{æï£ß>·ïÇ8|¹ç0½×mA‡ìÆÇÃN’1ôuw¾Å×3oƒ´Î rý¹>]#íLLÔøá”«]|` ¸ÿp£óîT ÉJÃŽàƒoð„± ÉÍÚJ…̘ˆt#/µ“´ÿ†”­dý‘PZ¾ð°Ã¶´`ÔÄügvo/l®égüI+Ë^¸¿w ¹=¸«*X¸âæ l}Áz†ût7 ¯¥)QÊÔºjÊ~ àzïN°o?÷òí0ó®*[ýã7”¬¢îmØg¦QãFëæ*qGºÁSW`Ü»,Aý¥Ò;Ù’âø`»? ÂDæôEÖ+}­vúÈ~-ߊ[—=A=œ];‚ÞÞðQD‘õ'øK›—8ä/¢Ln47bMÅøÿìæø PÅ9Ú0"Ú#?±ö¤)Íä/Å©"ÕŒ‡´E†~p¢dȘ´Í„ÿ:Òì·=&%jÅ8Ó'…ÙÑr•[¥1öÖbÒ½a˜d ‰‹ëWÂñ±4ˆ8ŸŠKbu óÏ-Öq\»}Q@Ù¹pø½Ù <ë•é.E²_ùÙtüjHñÒøÈj/‡%Dµ^3–Ž‚­ôJø”/@¥÷ùÆTø¯éŸ}s²® ÓqOz#cnÚ0N>øÊ…ÎÆÕÑ>ÌÓÕçÉÇ3øTÀVÞdjNÚ"X]ÄÉqbì¼·qDΘ\]ìö ÆäœËS8®C+¢Ð l®c¬`=íç4¢—V4³¼0mÂþŸ|²è³ñ,|u\KóãE°¹T™Ýû!‡;/@ëìzAn²:ƒmóéåÛý°:Íþ¢,Ñ:¨D„Ü-È¡·¼¬öé¥P<qEŠ9¼ò®Ã×N¢ëÖýÐ×±‘rGmí?ÌçÔzhÚØÈ„ŒcT­¯Âüž^Ư –k %rÙ0€ž|lCgÙ£€Z«æþšÀ߀àÆ¬¡~_vZF&¯ŒÄ„ÂÄÝCç^B~üD×àëLbQÛà-K*7¢ð¹ãLºs¹Ó£ Ÿ_¿f +/bqs"f‡Ð7ã=˜Í‘‚[]ìþ‰¯Íùs3¦žÆrÂO†ŒiøþKý%þ¹¿›ÃjaöÚëì†@cú½IŒ&¢$Éá'KNð»ÑÃþ”ëgŠÞºãy5>Zövžàí©Þ0T*¿Ç¦ ¶°ï5 Àëõi|x°—)ù•ÈVÞyÊþ½gN5c `þ×.ùô ǰA3ý0âþ8y’ –…Õϧá7wEš.èÂ(­»†’Ú1à嵉k¥¿L;‹px6?¬çÀý,u J"©%û`í‰s¨ïýj–ýwáYÊ]†ÙªÄy´D®.]Á˜Äìc_šê£…íþ ÿ/óßOf*²î6¿ùi‰†‚÷˜þkò¥_›™¸tWòþé1(é¡Ëþ‰³k+’©ÅvC²¯Ä ¶ÃçÄËØ«÷s À)‹Ýdë-FÚ>«´Gð¨C7då,DÉÊÖQ‰ ÷*HVÊCïÈq"µ›ÃÿL&k>ÑÆºXºI÷6 ~üÍnû ,ä&œk“Æ’ù5ääYÎ*•ŽÊØ?0ÛçÆÌªÎ!?Syh­ÀgŽ‚ó!¼’Õ ï]$OÇð]P“ —'¯¤*F„¦¹Í€í)ÃÈÎwr8“ü²±©á½Ï,b|)rÿŽXl»ù ü®•0ûŠSQú«á.AΟCîo;·–?âf5¹Ð¸…©ÄîÛ&Øtó–‹wâŠ@kòhS"µÏ|¢"3¨fïbâ)P_¥êPæõ4,(@?qGî;µÔ6½Ûî=ÅK¡&4¨·ƒœü«‚•×ëqòµ­Ôœ/ƒ\Hj㮺DG/£ÿ>`ïï1îPÜ"Fjæ4XùëŽ>ŒÆk~_ åéW(êÆMy|äëÕCÐÄ 0Û?-„1Xøl7ìŽÿÑ›ñÏ¡itÛ%;º9Ôn:œ†œï¡Ì¿ž ±Q¦m‡CÙÏ$<·Ö;ª¬€4Z+|H¤Ý*äòŸõÅÚ¾¦ôÝIð_‘ ’±¤»+ -~lM"j&Ǫ¶¥Âã—épâÑGø»ý>X×|ïØäƒ0Ô)•^_ÖÂ~úŵ‹>?—å*+"É–¯= 칬ÁÌC­r¸5Ãú˜üïüWÛ”{ÌË÷LW—4Ùÿ«˜í“§ËÖ4ãÈÞ#x- o:ý%BäÊñ˜4[’lí³&_õß0³šü]ǘScµ¸åô ض0Žîûiw:9ì´,Òú3”MÙꇑe‚\¹QMꜧC­ë\iyO/®b¿Ã˜åixž=:=“Š“C¤/nù©`Mseè©öxˆþ&Àœ°¿Á°cN,˜ì— ʤg(–œ›Ï¡Óüiçƒp´®¾7sˆÐ×}ìǶlû´\î‡Ðø¹ ãéÏÂyðßëËõ°\æn? ït¾MÄÿI‡Íx¸ò3ªÆ4€”ë,b°ÙäiT[ñ|ÜÝ 3Ôü«wDÀËÕ_ª+Ò Ï±ÆÕôN¹:^:t ¦^aƒ_˜Œíþ°A¼Ví9ÃëU"ó¢¦Ö–¥eWîô[u|aÈK“íÆãÉ(~2 Î_6øï9Ô y'Îè>q Xs‚ýþˆŸÒÜdTyrÓþ§Î |·ø"¦¬¡VLæ3(庖>Ùÿžg†d¶q·ý‚-º-uc–F¢^°+@‹ k·R—í™Pµú$î,aÊù¯BBw.Z,HcŽINàÿŽÅž¬ë3ĨF6v»5ü”œKÒ4Èœø=PÓ8mï.%{•ý‰Na,¾bfÁÉîB®ªùèz{eÀê&Yz=ÿ$e¸0½ü\˜æ#¬±„¯ÊÑQ¬Îñ=Å…ÚŠ´Jë& û?‡£GBQÿޏÛ]T¯âlú•‡ i™xeñMØ‘4ƒªYhr•²YáõpFâ&(7š0‹$çÓÑÏ lìP`¿Ù¹Ó)<2ÌÖ.ðèùq¦§Å‰±PšLºß„1vNìA6sã;,6¢a…Š 7â#²k8Íé_g¼êwfÂþö[³ ^b[¯Å|ÕOD‹rb0ØÇÈÚg5…òahÚ®‡Ì*©DóŽ ‘ÜO§T—~™ÇAác èônüáÙÊyþî<3Í3ͳþ0BñKد7P…Ô÷pGZ ¤¾ÞàÜ0‹Äƒ_N¢`çðSÀò*yú–Ž‚g,n9U[Ê@ƒ$1 ?a§¶!{@Ð’–ЂvPÍOd¬Áœd¢û‡`c²ÍXe*ðœ5µ»÷pI–ç£hšŽ±zCŸXÏôóÐÃkÈvËj“m_Çã Ìö‰¼Æ&ÞY˜û¿õßk—²Ì»—'™m›ˆ¶?$ ¼ÆA¿ ïy‚ÁÆÐ\Eчï >÷ œs·±º+“wÿâú'sæEâäùë¼½\ƒy}&Ð)æB«¡Rò\¨]U£ó«•è‘45rç£"ÓU¿F€p÷È­B‹ñžb£haCBfjÙï¾.†’¹‹ s¸ý…»@¨T\þ]ab @î¾è”&Jóú™iLÌÚULõ²H å0·™9«r†1A ztÍi朽,vNA¼¾Føî‰â 7ËÒ ?'¦T)5ÿ7ÿÍ Žct¿3 &Û÷¶áß ùŒöX!ˆ^¢³«Ñe@jE G-ºò³ÉI‚éW;ºŸ¦—œ†@õÈy¥&½BÞ áñ‚af’©á®ŒÝqMŒºËfŠ;s‡ Déö¼óØuPí,bÁÇΕÛyľžçWP§~GžÅ›ÉÚ¬eŽ4|×åæxˆ‘3·àoS:~·b–Mg§ÒgɘPQB·í±ÄÕì༪¬‹\ALê3°íP2~Œ×a.̽€»“p®wÂÿ‡[1z_8®å±0¯µ˜Ñ2Ì‚-ï¶àŽÐhænf,뱤œšÕXðÉ”0‹BÊ®ÈÔ› ßWáã1yðßw=Å`ÛÊ÷œ±ö‡à.h–çÑ’JPqŒ†¾ƒ^p7ÙŒþýÃn2–!}!ª;7ŠSæÈÿµ-ä Jå§a­œ ùÙšf³Ã“HÊΤëö[Ð{C ,Ö7ƒY+†põ«rèIUGΙÇÜ%òaèÃHcwx#äk½Æ¯1u¯y 5Þwaf™û]¦¶eÛ1l o“pƒ@7sð  {„ uäŸà²'å!ÙX ×û ƒ¡Ï{»$þ䲃!®Üþ ?X>ßWùÒESá§å(ÎúPq¶ÑxN7œ\dOæ X‘5IM¼À ˜—Š¡_.A¸1Èš\Á+{J™åj³ˆFÆbjˆŽPš  Þ)?ŸãÛe Ô}µuœ+OÝ0d ýYÃõø*qhØi0›å帻Up~¥1­X"K¶´7÷*~ØùȈär#Ù×^q„uæ@ÃUy!L÷Ø?£5ȶd‚«ÅeدfAç,ƒ&qrp†±r8 ¢gà…Ì Êó i"ÿ½k°‚¢ê,v,á6æÉ®‡¤g5p¢{cP²‹sLW¾m>T›j½mZSµCe`S§Á·q»*å%áSe+Ë“ÜÌœàÚÁÌbÔÚ†'·*¢ô-z4}ùwö(Ô:æ3Ü/üä]Õ_0á÷„ &e2¥iÃs^Ÿ–9¾Ç?“5`×mœÔð–Ó  ûƒ#0ª³o  }¯+³[³„õ ]3.GO»‹ã9Ï:ï§bHÝjÊ‘í5°`ö1ü¯/¶O˸ySÕám™#s+ŠÍ²} †¯¦`ÈÙÝè5÷'»´ä”Ÿ>=aÿS_{l‰¨€". Žâdö° Ñ{‚+?ÜÂóÏʼnÛ7j·„mt¬ÀS×ç\}ûg¯w`iõdúOá ¾h6¡æ:]¬Ö0e:ø¿í.(ðLÎE²Ã_Øê²ÇŒxX ðù:øÝ~‰K¶o§;Ã-˜­&®ŒÕ¯'кoÓ¦~ý¸OÚÓaOùr*3›)~wu5XÀxêH Ósøææ ìk˜B΋ ãüÍ+¨éi¶è’°Ò|,t>½žG8…Nëñá‹gÌ ¬`£ÜÔ Í/ö‚šÏ"0±eßúÀ`%© ûãæ £3Í /éí «ïØÑ7²@àùWáFú>,DÉmi`¿8—^š$hµŒ²tG†œ-½C >âµws釸|üNǵpƒvÒ Δ×Z㺞¢“ãiL_wŠè_O=]Ì2$‚uOÙ#ÅÍ}÷T@ÿ a²VVšná+c:"/²öÉ›à}ô&îÇl˜ãCA´H 6?>ŽKkü©à;~š¾@œÚ)úà¯}S±©¿Œ¹~Zž·¸ 3[´'üïâ¶…]x* ì #a+ÃØé´zœ Î%ù(,QUÂ¥x´=Œ½þå Ú´†C~¬Y5p^cdvïi1]óp·hµ)}U|"Ðéï#¨ºã FBÜâlćQ ‘HäáªMKAuJ y?Öˆõ7ÁÌU3œyáIð%ôT5 í|b~„LÍxÎìMúçt¼A–ð3çݱ{™yýµòÐ Bœuè³+×Qøâ ž0æ%wEvRßY4Yë^ß}ž3“τۺÒï=ÁÅéøÂö6æ/7… qÒÔ¢ðë?LÂOáØâL j†Ø·;X‘bu.©«cÊ”,Ù´Îl8{¹Æ½IA:&ŠÌq;‡ÚùŠT*Q›Æ6¤×®Íq§&Ÿ›™×¢ûIÐO~Âã0•ûëæSfðH ½æ ½òÖdò6 Ø#I÷l{Æ©Ÿ)“V>²ï0­c’b~@ù±~\xj;FÐPÚ¦JUnóሕ3©^óWO§Ý93¡Ðo ¨JÇQÎt•áMBg«€MË ÑNbN¿fÁOwa5×>W@ÆþØ¥Õl„ÞÇQhãÚàø>׆bÞµ¨ØîB•Ô‰ø‘Kö¿÷×ÂÕ{äØ}î@üÊž³‡’݈mæTºÿ€=­Yrrü{1ÃwÉ ƒŽqbôû0å‹3Mþ9ÂÌtl`[½.`ßï¯ì eS|¹‰Î8|‘Ïä'¡+yIþ­YÔÂÜ‹Qôµ&úZã\úwÊÞY„Vü©˜úð1¾4Ç]®ÇÀ¶bòµ€:±]¾,¹ÌóS£å~tÉQª>ÿNíÈ,Ö6n7x¦ºãº{W¸ASiYS>çÃç¿ Û`6uê™ódè½=ÒôÄî.ùZƼ’N£¡=BÔiõ ±œBÖeF³¾»D©µŒÐ„ý «ùIzv#³BÊ“øH ‘_ ®äñÅp”X»ŽÎ!—¸‹Iœ}³@•戻àâ3Üw#wžfÝî3(#N-\ÅØ©³6ÒÒÝk9‹Öž€Ð'á ÌmÃQ¾»€ŽÏ`aÑj¸}b ù±ýÈË%“«ÈóåBDvx‘jY»u_þÝbIÚF Kº˜•¹»cÛ‹¹«oï†û~•`¨„Ÿ§WàÊw²þ;öûõ{xàߺdM4Ý|^žö»PQ•¹äÈ656ç0èyLÁ'ùš4ûP/ìq ÀË»’BvÈ7fKo•K-¯ëã_äI{ªä%â™±ƒ¬Rž ¾ì‚•÷<‰µŒìº ’É1´AF…Èí>ˆýo>°õ Î ºêÑ蟙@Ô礃q­àl.ÄzU @û¬7ø M Ok}pÂe`\V,V{ÞÅË鄬޻ޡ­•0çº0Ù¹l \^‰â©áì¾ß/0èPˆ…¨’ü¼\Rj¶fу– iTÈ;‡ï†A¿´ ƒŠ±~{—‚Â"MÒ­¸\†òIR”þE£kˆ¹Œ:qùŒXEý⦓ïî䈌6ÉÀK^„üoý“ªæ^˜AЬöÒ¹Õ:´ñY'wfÜq¨/B|tn!YÜ•ˆ•“›Éϰ>Ö6­L/rðÓÇvžÏG-c²°5¤‰4¬>‡T´œ Zƒ —äÏ#†'¯1÷ótHáŽ$âšØ^I¾Ô##f›dÁ¼˜tþÌhˆK‡£ï¯2%û‰‚VÉ«ô¦GþE‚¡j)I6u K¯Á7f“©ïçk ©K’ChÜH.«=Û<­‡ÛÚþdÎì}tçÏÃ-~0BÍ•¸Q¿œTûd á]Þ7ÿ>sÙöç×ñΊS°ðÛìÚ¯òfvi ¹Ôrnn˽±^îØÙEñV þ2M p’™3y ôekÙÍ[qÒìÙD­ù s,ô£—fúÄðßA4KpÀÐùF0РÏüw†æ×‰idÆFcÜ<ÂLö(]Ù\à §¯Žk=rïI<ÈèN§SöIJ‰w_AèóýŒ£•*ÑôÚÝÑwáýÚP´l6¦ÍU'ÓE=©±w $‡m·{Lçfm:ýø fÑ•÷ý£\¢”ɪÁëÌ¢“ÁÌQ} I²7qɵo¨%#Oën˜8ÿúI½ìÃKµV égfãÈX¡Ë'õ€ÅûgX{M‚)ýû ­óĨI^L»%MQ3 úö±±ª[ß®×o@%Yžy“x™=ðC˜¼¶MD˰pŽÌë8º–)Þ}ö;Ýyè¹ Þ¬YßNº€ÏxmO‚yÁd˜®Ð†ŽaÀ¼ˆü3ÚÇóÂît#êâ4Ài•Uc½öuBß~ ¼ùc!f”*Å®¢0CíŒx…ak¡PÝŒð’ñ{w1RËb1^5ƒÝÎÛfã€í\.‚ûgj`ÆÏ× ü÷>›Ø¥OïgðM<ÿxð‹ °8Äñ’êJ¼)§?ó/áÁ²Y°hc"yj°¹L hpOÛ0£ý±)4÷·:Ùä+O~ðGBÏNQ¼‘öª¶æ¾,Üv¦ª[õlV+VXúR¡–Éæ3ë¾e>¹¨˜ÇN j‡µÿ>ï÷(:Å­‚]Ïå$Fìœô9ó†`kÍ´ËœM“ }HøHÉwŒ!{ò¤h‹B;†¹ª‘ÙL.¸)…ƒ5ŠP'ý9œ8ÖôŒî¡ãæNtÀ&ä­\IržE©›ñåvN÷I¿$ bcÔ!ÊLš4ÿ·Ö.eön¼£ÃápdKlla„‚Áä»(}׸š|øtƒv¼;üÎ>1=ƒý[_ào?ráÜn¨î³b¨šìŸ-_-Ýè÷ùhœuCl©‘t&÷Ûi «y‹Ð-ë%¤âûZ5ºô¨4M;‘Ó¯,¶WÕÔ¤æä»}•mܼ3‰Ùüj%|éõú¯è:ˆ¤(‘ºŠ^pf‹‚ÚØ)öÕà»X…úÛ\‚Íz(èP'¹0ô!“ѨT¦) ¿ YïÜýä GÞ^צí$0AŒn2 gþkãß{Mòù0 7õávÄ,¼;΄£Ì‰âÌd¤&ÙamûpeŸmÇýu ìýÑ-0üº !ÀU¿å3n:æD®Ý˜^ÓÁ·7ÃéaÓ{ÀQcÒ~F‘ºT]Ü|⼑¾žÓ…èæöÓ¸W¿…u’Î×hk V®¦þkxHZõ-–i•e3Vá›7™ dÈþàw¸Ìî=&lQ`ÍæÎ'ËV%á5‡æb‡ دâ|×¼ÿïâÙ‹Üë+ÚáÓæ xøŽ-Ø…™“š+Ñþ— |&L÷Æ7OsÁäÖ |B+.ýd\Ò´hÞ—mdþ:õ‰øß›ŠÐoþMmÁ«¡©²–Ý—a ªqñ)c¾)è>‚?Ûž1 ý0ØÖË v5Cßgö½ÀeÏ»€Ê6)ŒåAYÂwC—‘8\ÅÜQùÇÞ¬²ƒÕÇxi]žY]*ÉÜuò¦—ÅIéêc¬X¯")|…ùc0-½Ó~é寲|%4Xm¢Õ_ÔèÁ«î »;ˆú|Pí`íËâ5pí:E¼ëa ?ãj8"~zxSi 1óÝ ¼FQFt ]×QÀN-¹Ägè‘H-¬­˜1ΡgÎCÜK½á͂ٔ_ΑŸ;ˆÍM‰Â‡Ô ÿ/ ^ÇJt@9‘öÙx/½~‹5¸54ŠÔ'-†#Ùµx´¿™þ\lM%’‡1ðX. 8µ·JðžúUØòN“ì¯ÚJ |„ȹ°*Ž÷y÷› ozÍÞÃø'} y­½•=){Ö:ÌÔ^´{¢Kt^ìfw‘+ {ö*’Ï%h@Ù|’µžw¿‚×´ƒðáöiNvÕÒÎqM6&Þ@OZç“6g!2æ 1a]N4<¼V/Ào!Ú3#Ÿ=g¡L;òýçáy÷ ºò¤ zv ÂØiH mâ|­³½cè>k-Ñ?7.- ïž¶BtŽõÑ™k6Ÿ‡¸‚ôÁòTYÕžMÃimÙl]Íl²o@bÿú¢‡±œ.#û–þŽq)\˶ÿ+ƒe7çÐñþ°‹vñÑDÚÚËì1ƒ“÷îcôä\0[prÂÿ…ÿåÂ}<2EŽNË6#uUd‘Í$êé&KrŽTØ¿?¢E̓ŽÒë;…È”åpu Í­?‰*Ÿ`ŠP<ý0¢NßZ÷cEW( þGHG}iP OLÉaµÕ,ïÐ#”™îHÆÎ8SCû°ï÷$ØÑØEBs‰æ¹ed½ìäk:FLTýÈ2[zÃu*±¼‘¨=;FóFfÓ&ÉDºÔɉ>ª•!ks‰zV+=mªK$¶*0G6Jž´xïgÜü!§‘¦‚Éû€¦;ú’Í…Ð\ÇClCWÛfH ãwXòÚo)Ïœ°_ñQáK û2é*}OòlJ'ÖuF“'‰Îdò==bÚJnm 'N["¸· Å©~›,sqJm¹@Dš«ÉÅ×xxk5Ùf × êáÐP QluŹªfÌ'Ùz¼Ï{œ}Å;‡ì?9‡ö_ $‘wàîÝxýÙŸ3§ï©•>DÂ{bÑù‰ÈÿI/I§î8ÜaLVçb~úmV%:“«Îà NîŒíÀåNñlÛCæf@ˆ€2«•0鯴Ô,@NçG0ø q—UƇ˪p[Y;P˳î2­Ë ç²=æ–õ¢Ï«•¤ à&V”ãÃÀFVPHéS_*žP ;™4.ýyvÕM&þ[Õ¨¼Å;øWŠ’Eæè"H¯ª3Þ-~Ô-Ɇª—›qA°ÛMØ)o_bÏåZXðª¶zÝ}Èß>"ÃCn ýD^ÃzœS~+ÿá8]j’ mý•´y®=ªP?ñü'fYxÈg¤y7cô»¹ÐjsyòÓy‚xöÀCñu›ª~t=ù÷í]Dº0§ý#0Þ² 8=Ói«öfÎp ªÌ„aNWãþJ¨V?†ñ»öÑ“²B4ã…$•tß>Ž!ª”ÿõdRÐûU ¬1ågeáqüÌð³ Ûʭ裸Z¼°'#7Ô‚º–xµ§Ã ¤ðós6ÆMƒð½ÂÀ?\tJV†w1QŒÓŸ­xw„Ÿ´íצLë¸=´…§<$ÐZ‡ÖîÙO^Ü&7óÍaíœlãîø´ç 9~P}®›ð¿Ó»×˜$"ˆÖà?=-²‰„²mÜÖoº#ü,LÝâM–_8ÏÉ5°&^€FŠÒ[,©$#NÞ>ÞL=_pgFÐöuòó«$‰DpìC]è…X-Æt³9^( US„HÆF9d±ŸÞ®)d•»3íê{˜-W¿A±Ö&ïùvh`.Ìø †1’ΰ×0–tžÅN·pjÝ{,É¡ f%[ÏNø°"†ÄëpeƒåZ^`4Ò™g–áŒò_t¯ïeä!«¾½‡{´Q#m%‘߀Ã2S‰ü)\þ(3×~½›°¿6²1ò…,…G`«²‰Ä=çâ¼C™ú±Õ#|~.9³x‚È&Iyø%w îîÃsÏ”!ÿ*`ÈöôÂçR8¸Áƒ ùì#·ç—±•ÒGð̲6\ÌWŽ{ÞÜ„Ã?*Ùµ÷ü©Ý{i*£A«ã‘ûñ¬}Ÿ~=hÇ÷’M¥-PXƒö“v`Ø>iR®—H–÷>¢{ztI[JŒ±"âny¬“¿5 Z¡ ëüÎà‘}LBÂx,v£ÛǦ€¬X ;f †Úp¤b/mS§ºíŠDrÀŸ8àíùòdZácT|g)JS'â?¨*ÑÞBE Eúpäh#&ü‘§>™¶dÓ³Û(:™þ ²'›Eqð¦7w߆´MÊdµ?«‘M™;°ît7ó³7=c=:ÿšàÌ<t›»ú´CRœ4­;mÙbáÒ+ ü`AæøN"ò|»áͽJÖ»Ú y\ÉUmæ|?ãù®B6OÚu&d€Ä2¢ ðl0‘ýX¹~ðªrw¼— Ñ1žèmœËL,`rõUÜ&þßî­CÏößld /]0ç"{3fÄ{ˆÒÚtW–6Áta2û£W“3× ÈÊøð þ7Sè5vçVÛÇë& üñaØÿ[’|ø8…d±Í€¶!_(R£w£ÕØMÉ@OR¦.ë›! úïŽäÛ^ª9xŸÂ)ÑÇ\ÛÍâµ+_¼æŠÅ¯…—Eæ°Ò¼ö{sÍnÇg^fsíº‰ü'VÝÂŽ:óbúçdürï ®ûX÷¦×'9ï^…]¡.,6­¬ŸQA"Û ûïÝ€•6™Ì,¯‡ÀÓêŒÁ‘¶ø§M„ª¬çE3×"ˆzÂÈmd-?ñ±Æ5fä{íLjöl)KQõ“N¢hÑtölÎ~vx‡$|å<]cEÒM3­;DåoˆŽNf o¼â.)È‚öT9RU©‹Þ=c·.'ŠºÎð\³ºq0#Þ†>H”'ŸFÅè«Ov~O/÷¹f ‘r~€Y0î®1`Ùí€Z>`?–Óˤ¸&à‰}1øõŒï„ýómüXüµ£v‡ œõ~ŠZå£Ì›qŒ´Á9Voð®õœ¹Mtkn©6àñ:5ºx0ú~G÷§@ÆTi\•WhÏ+Á– IÀ´E=Xñ"lgý…Z=†eÎã ¿Z¿lpv}8³áo7bPòz!&Íèg·Fq ªÄèA~Z|kÑÕ磋9ÀÈÒi߯s²qÚÎþ%~¨,‹:®CæÃ-øù¾¼³_Ææ‡ û÷EÄXFЬš~»jOà€i+¾ÅWòÛ1ø²9ÊW¦óê·aom>:ÅÚÒè†ÿ›ÿ0¨c÷ÕMÝ{áÔ):ä(6â³µ“ÈœUJTþq& «N¡ƒ-ŽdémOrÿÒ1T8€Ë¶‹“æ|~š—ù×o9KÿŠ”ÂÛ¦È:D_n¨šþª ©¯ÅtLþŒ õÂãßYêÕ~G·ðâЇ¾‘Œÿ³[¾“ÃèÄ#@Ëß=ƒÂÍR~Çž´«®$ü¥É®?ñò$sx4Ÿ¾–t"ì«XØvÎžÜØºö¸¥Ò'9öÔøÐmxQpÛî¡­ì,®ü‰ Õ¥èáMôõÓè[ð˜q.œ‹«Æ¹`ûäS°õÖñ ü Ž’—Ö|f$¯ÆÌ£Ú9ëé{ô"?×¥²VZølàzùðRY£gÜMºeLÌÈOô Ó1ü› îo7¥5dIŒWVN‡ɹtî!c !øKL{Ó ú¯3:מÄǽìÂ_vÔ ù0üH/f_¯xĬt;«óÍɱ§QŒ_Ý$ü\»¦\)ÇŠ™‘àu`sõþ~¼óy=j ¡\“> )€âOàæÓfH”ã½%™°â˜ËùÌÈîý…W¼kq8÷ÈÌea~T×n…!lzºê쥩ÃH$›¥R ve ¡7!'l1/׬H‘TÛ‹¸ûW"4¨Ï¢ýw9£E·0¦c1£%„Juðaó8]³”\¼ê‚®ÐÔ"…òqzó b¼}F+ÇŸ=äã…l¬ë¹Ày»€|[[ ‡¶ù‘ms6@š÷ œ±ÎKeBá•ãòîcw´‘U?ËK†Nl!sIfUËæCe,ø·0§ã‹“6>á\ÏÆßöçáÅV øv£ C¢È­™W˜¹;®2[bQˆ4¥R!ç¸ Ö:ÁxžåA£MÜ4¾ÍÐ6»6âÙ[¤‡ó!s×â ûŸkd®N^ËZÐBQmS2Ûk veÍÄØíÖ˜%?›lÍŒƒð¸n¬:Ú†?æ2¤qêB8ô™³¦Ìƒ¹SÕ Ë)ç·]OôÄ—dHå3I:Ò dýŽÃ¥…/¯‡Áµ)k`ðH6§waÑWºRn!ðw®¥Iá“)_T ó¦D Õ²¥áT$ H1Ä_C tú®têðà.8šöÁÝfÃûf”|íÁFýY@µÐ…Ä÷ê‘¶Çc’Õñ·è°8p%k^5†þ'͘|?~4š›€ÔOaªö$RyV‚ôW…Âmýû#<PûYÏí±SB_·°bËúåó´°²aZåKñNKtóH•ùå ï\C¸ø¤†ÓQROoÂ$³0;¯>•1ww9áò+8ÉÌ]˜ý“éî S©ÿ ¦DÇrjæ ÐÇ»€}u­çªÔ v^À„–3Ì–¾&VÄ—7žÅ•fàLµ.zï‚þ9Gp]ÉÂ¥l˜Æ5 £w]ˆý 7ÝÎëÂÊˈ‘ÇŽEx*Œ_ šBzxѲ×Ý8‚Ë<Ü XE”n½¦JCË7‚k»îá>ÇLëW~jìýfÿ—yÅ`Aƒ;gùƒLà“_J~»„C@óÃq\» ¹v"hÞ×Ì*‡ªSE§É°±û*Tâ¼³ÕÜó9b°Òò;72-–>¤Ü1ßï›E_ž…G’Ï¡Ç* %2zÒX¨NHÔ3×úžâíž)H·ÑM¥L’¡ý€zì'8ª–ŸñøÝ¬ƒ£%ùãS‡.º•¦L;ù€çÎ?Ðcp•=¼¢ &‰Ø.Ì3%ÔÑ~ȯf?™ž‡ÄÒGP{c]õ.zŒ `ÎÆkÜ0±pîW&¢N…¡‰LÃ}¼í)阈ÿö¨z΢نìd£ÍTw&rN¹Fâ¿/ÝÌ2Æ µá2h¶Ç‚þ9ÜÖùg†2xpàS‘ Y‰ÍºØßìF®:ýa­OZÒéúf^©ºö£ëUÉþâØôȉñNpÁuZÞÌíÌï _ØJýHt(ÊÄ&_[ÒÙŠç6ŽŒr þ{ð-yÇ{—@E [÷8žc,)I\ßZ’°Å—9 É«8S•×Cú)±qý<í–ÊѽEéÐQçŽsˆ²¯TŠ‹s—ÑíêoFäå9öu×?Ln$x0.ÔÖÂY³cÜøIÇð£•ôDþ«Ÿ¾“üƒ0×l&jò›0×J±ëŽ1b{?°¹W>²JWsáØàIph^k?}gï&åàð¦RRýÇ}Õ5Ç5 •îÓdyƒ²±wèx-€çÕ"¸/ ŒRÈÌÜ`ŽF”Ö8¦Ài³éðîù¯ÖÙZ!ý*!sÓNЊl\ó˜ûdùm8rR¸dh>ùr®s%daž¹;–nÒ‰47h1ºÍdŠ bðÛipdk6®iôõÍÌ¿¾eþ¿üó,4²¢oÑæ¥ˆøÛþW‹ŒÍ]©ì騭˜öÊ–Äþ¨m·t¯gÿ«oÞ›T Ó s±x\/óÐ5«w@ÏÓ¿ é°ã`Þ6ú`ówÌXÊ2!ž¥°æßA4v¹ü&Útþ+’Î2Ê:ñ¸æaÕ…‚«¶R­,èQý$ ³ó™°ê.TÞ=¿ïÀ¨‰+‰Æ7o›]ŸÉa+ Iý: óº2.Sk™¿é˜q]‰¤3Ýì!/dæäAæ–læ÷3˜?T¡Óгç¼f°?$£ÙYûTÈÅb4Úìžóá“ì í|ŒØŸ†LÓ]ìøO|»ò ¨?[D—¾”#É7yˆ÷ ?©‰tG·”lÝ&ãíÏÒA&~ ¢÷H6݇¶‹}ìËãÍðlÄ.SºÈµê€4YSX(¬N”ß°‹D·Ú˜«“h\p5l[‹o•sЧ-ÖËëS)V¿~\2ÿKº„XE‘zvÚ›rc`ã*+ͤY«/—t¨¹¾=Úgpe•Äè™ ôñjToMc/®nÇ )øìfky.P8ýœËžU«á ìçÃÐ7ÝðË. m¾ÚÒ¡T}bm9›L­MÅu¼¹§E^ö/õðXl»êúxþóì… ùXUš¦g`tõ5œùL‡´þïBÖà°˜x¹Qf8µ‚åü>Ý¡‹!kµ>»÷â‡iø Ø…½"=ƒpÔ"0`–2£ód'»ÌLƒrêƒ-zôxbäˆiÒÌ§ÅØíýœùíó¿ý_ÒÆŒ˜cÒM\©òÖáJ-¾d¥HâZ%bf{€ bž9~šAö·‹q›©äý£ ä©äƒÍ?o`ä%ËãÉçß™žÖ'¨éÖ“ŸcV|ž ×Ìæžy‘„ÕJ¾Td:U>$Éžpõª•š]^¦Ú S1 ïç)à*¡Põ<FÅ‘ø4 €­wzÉИ—l™ãU6Ô×>tî`×ã+ýRL]\‚ÕSŠ˜ÒjgJ¡ƒmšNÙÁm3`è’*¹°0׎sæ1S”r £µ&¢ß>&²ßôØi¾™ïô„¯uãžV3úãÕÿå?gP[8­È~šôÖy ŒÉåF2ëž`†ER@yð´Êç1’‹ï#ïàìãW#¾ç#™oM"O úºgÌ¿µ(fyŠ, 7¦+m<ÉW„.Þ†—v\gvü{ ¡æņ̃KQ*L¬þ^cœL¬Qló}ìÜ:‡˜šTâîE×!ea›î/O~úŒkè¥ÐœÀªzÏ¡o{0±{ `.ç¦{ÑïR½ÌÑöXv%4Ë®À>­ÒŽæ\xy%Hé²ÉT¡5øÂ~ òpþx5ÀàR檽/÷À¾}xîÕ9ߊzRµx¶åì„ÿ7|ÛÃ6.¸ÌV_Rq 2o¸ÀÎÚL8šs»zàê ´ŽjgŸèœbô¤ÿÎA¶hãtHÿf©¯÷1k-@¦wÄÚvBQµ&û)à4Z?–Æ_a¬ºcE¯ÊjqÄÜ`²“8]9κÜ_„•âÅøW÷Ø;3WÎ8³I“¬peÙ:¼Õú‰q>øn˜ŸÂÃGײí#l׎MðLV—œL…C®rÌoAøeãW6Ø*r!q;¦”žàn,ìÆžGѦïfÎ&¥£Bä½ú]x©>‰¼T‚3þѧŒ×»Ë\ï×Cã¿SY«À„]1g5ðKƃšw*Äþ«oå,wédZ¾´ˆ{$°˜]Ÿö‚}«žŒ#ä0ë{’‘¬ŸOù‚PyÁGç(ã¡ÎxVï /5ô1†’w—¡Ñ÷=¸$.bØ¥1ƒK¤Œ‰mU˜„Š…¼¤ì‘Èøl µüôÿÚ-› ¼sñVˆ‘ùdGƒrF˜µŸŸï&c¶$^‰¹6ë;{ù• 5QU"ÛD@ÕFF_ä6ÜŒ€ºAmºöÃlꡈw/ ‚¨ñJÒëkJ^üc"îu²ªÝØ}Û'˦ƒñn¸qQ|"ÿg9Æ2×ÞÝcONogn¾L‚³Ž»ÉçuA¸€#‚ÃêŠì‚€)ä}ßyØ®‡nR´Äè⸶ÿ‹Ú/áŦ§ØBêý^ Ο·@ÿ¶$í’ê{¹„ÙÇïNn(Ÿ >u#X%ëB<30Þd€×ñÎ>UÔ§±b¤å‰çM ‡ù£1^–üÅ<¡Kà¯ÒËØo¾Ê–Oª„Ü—XφþýX@~8^Ãõu2dáGήÓñxîùB<ز‚¨)sGÖ9Ò±fã6WrèXæ=†¹ã˜òå-:¤Ô_žkÂs›ÃÉ@Éä ûOk²ÍKŠX¾«*Liñ$B¯Þ]Q=¼[š§ÂT¹µãîeYQx.In¥?e'+Ž÷§W…Þ*®ÄöÏ”AÖVÚ_§8ⱜã{øÐ Jýxù»95R0¥d„úîS *ßxTNÄô×PÎ} i|ï»jãRܽUÛ‡*˜ Çë°ªÙ×WxÕ?—º± áêôì¹,”{ĉϫX¡†üê„C‚¥˜të>¼˜éÆç·áÑ›_qù™Fø˜ÞɾpŒ`Øšèxø¬—½‰)¹›ð”ü~%21þKÚ·@W¯VÌëÂ˾!Lˆß67Ñ·í\Do˜Û@`·®7daÝ«Ýt½³3ìm¼Ë¬)v%]ûâ‘ouÇËåÄŒk¶¦øVög]úó*~^@‚…GQzX.¶3šòàSÅPô› bݼDg& …ÔYüñé1èÆöúIö‡”`^¢ð”aÌi©aßÇJ’Ió²1<ÃÙZ‹0ý¤ÉNk„àÇÌ}ë1NJæ,r܃{?í¤~âYPäìC½BA·®V$ÜQèûþ’ù·©*æy£”ª4ãÝ;T5¼¸¶&UúgÇmGÆDõ[òEˆZv·²ýwÑu©¯¸7«f‘ãvÎÌ‚[$âײàV8•}p F_äà‹ìô¸c4QÛ§„Š)Ð~ÓcNJ‚áÉtì~ì ÁúÚx|ï2öàì œz-J5ÈP‹!yu§ ®ù1Ž1÷w»5òè½o­Ï˃+š)QÛ ×äs´‹£à#Cå¶ €ó>{žÛJdM«­¶¹ÁÌÜ0Šæ›¯A‘ /­ù>ƒš}™N<Zàa¸8Sx_“®©/"mödüó ÒÞ¨€ mÇ(q-™3èóñm°g›Ïþí¹‹óEà„öDroŒ®cf Ð/—w0bƒDbñÍ’gãc3¥Ü©W“˜úY;ÙÿäðÁÜFnøûhò;@‰²pЬé–Oßà~žTð€“èά¯ÉÌPº!Ïý§6œ`æl±€Kât¨' „ÛfCOߺv²Ñ]ZË­¤K-cØ­ÿòÐUÑî¬xÛç'³»<ðÂ\mzâš³³Û^î^@o§Ô39Ï„kBÔ¥©‰ÍÌ2$&KÞ1BŽ~L~j³ëÅqÎ¥º”5¹ W¿-¢|µç˜—‹Ò‰cM;þtëšXÿÄnåC¿ÙýÌ\r”óy÷%¦7[F’~ ŒÂŒ—Ø|< Ö¼Û®bLÐö|`ö@ð–GlëÊ÷x!ë§LW£ÕñGqÅmjU%ϰs—ÐæµÙE…‡pŸ‹&-Ÿ1*–ô±O|œ”:´wrlíˆGB©Þù? ¾v7ÙS}‘í’.í¬“4oY-è̪Då¦Çh œ¿’ô»Îü"`/:‚üj:°ï:”pSIG˜ pä£WléÚÃ7XåcTÎç3{ô.î[H/ºå~ß™Æ%‘$ô÷ÿÎ?4Ë·Å”-ËÇ_¬>y »—u¢ñºìתqžÿ3~ ´Açp1Çè£6}½h÷ê}ÂýsDpñQWÂ\ÏF飑V’„gÀ ìÀì¤l¢\iÎXbE?3¥^Òd^€ ]ý½‰¹òE¼O`È©YìÆ^{:¯«…©?ù wcëÜ«1\}=lü.‹ª•äˆD?𾾇šYg¨o-dmlÀ)k·âmÿ«œ2q²f‘"¼Ÿ¼–¾§S>Ä*_ *W\‡…3.³[-xHñÁ)´V¡Ì ÔHñíÔþµ8Ä×/ ÞoªÁ¬èÄDýëŸèzæÁþ}àP¦Oª,, ‰_ü7TžžŠ cS¨Ð¬d|r»—¸Û¦Zj¨:“zã9ŸI¤*ÏŒtæÌÁ•¦ŒBu=.~¦¼¶â³šÐë ¦I¬Å|*_y *òÁÐu3–ê%õ~+phÕ¹húì‹ Îò¼ ö?†°Œ?ˆF³–ôeÈ[^u\ÔÝhÀ®»´=²„ZžxXÿüþíaæ…¶Òkï^³EYxZx:¼À¥L0½´þ½x‚¬½Ò@"ãËØÇoç㛦4â¦$Êù³ö ¹¾êü–«d Õ+'ðÿ¼P>Û‡þI®ìØ{]â¨Íz.À[§™õe’ppÊ ë·ù–ÞQfÌ.ìÁ™1çÐDÜëžòÉKæ`­¿”žÿËÎLå'q¿ß€±ˆ íäŸ:¯¥QdÛ{\á:…ôÃìz—î¾µ!Èm:ì‰`_nã'oÔ1ù¬IØ× S.â´Â!æ¹›z=c¾Ü_æƒOð‹ÁÐh*gß\ûˆv‚|)uúOÂåy*ôÄ2]"ȆäBðÝévÿ6¦EåÚÇØÊãŠôÊX\^7ï2˘[û:ñb¥ûÿñÆAiæìa9jy蜕bo=æÇß#j Rvn]š‡!a ÄN<ŽðåVBØÆ}(,ÃVRúìŽ`’¤Ívõ7×½1ƒ½Ð Á}^>r¯ÁnÞFx«’µ]®[ʹ —ôàýúù`-¦óǺ¯?î¬ý%×ÈþmZkíÓíz·kËšÁøüË-Ü.-Áœæè€îz 1¿˜ÅNMLß~ ’Ú°þ¾< ëÃ/ŒÒµÕ-yš¥éé`^ÿ ÛdcXèØ†‰q(ÞŠ‹6bR2~úÜßOà_™ëZÜä´+¶Uyäx?» áÁ .ÿ yû~Õ\«?{¡Ìç'»N’|Q}‹7ÖA»¼¤Zu˜©?3—ž üŒáˆö{ÎÛ{¯ðàK°}¬ R6_ñÚÎbŒ>3Z°¶ºÑXô–}/´.5ªÃ–4%*?ïþW·|@ü¶%ê1üïäéã›<$¢téT::ÂÊÕigÙ²êyö*Í9°\*n\ ZÇ^"ÿ9Ç4 ÛkÒF>*ÖYÂ]ÝIçD ·@VÖï ßÉÒQDÎ÷>U|ê‚àä%@¼öøA¸© Ü“-e‰ù®6—,کɪd B¤Œl?z•«bð§Ú ºú™9g(J–<ˆÒÛÁ‚.JB÷ †÷jgÅ!²¯ÎØ…ÜC“ÀfOÄrþ­Á §B¼[³×?Û?>8aqº2*;jæàõ…Ô#I2“Vào7Ü¥ßuõ|¤âÉcð©Æ‘ù Ì›7ÿ;ÿ°ùW(Såá_]Ὠ§ ñÊåÓ*àK¼ YÕÃékG\¾û Iða„–JÒ¾½Ùœv™;0ÖìL6š“ešƒf­Mh …§95Ì£öÇ g~ ‹:¤h«\27ß^ ‚Äkà@’.ßUÎ~1Ù„‹*¾1jáÃ%[ôM7—ÈBœÅjf,Av»Ï‡(9#:0›!ÃÊ¿<¼1+jÑÉa m߇çåâÔÆ%þuÝx ûãe±­¬º‡3SOÚNynzBZ§{²kWS‡€1(¨T"ïÛ§C`¾3$ªU2Õó'âÿLä|o1»{q®é˜’-JúR&t²»šÜÉô}™âåg™’>Mâ±|+ÖæÕþ!¸\8FÿŒôBÀõRÜ<8ò<‚tÿYlZ‘‰åìg°~iGw_H'7æÀæ#ˈëÅ¥¸L[Ÿy ,Hçv¨Ð¹ñSa~³åÛ"Œ×ïïƒü_Ø¢xœ9` Æ+’é²€sìéwIt¥{ëµB„(6.€C¿œqãB/Zh?Ìú+^¤óÏqvÏ…*µ-¸ÖR„òXØB™Â!r••Ù¶â+-·÷CB‡&=ÕÃ6ù˜«±—°YœNø'oÛ±žÝQ¤KË–ÐÛÆÃ ¬ñŠÖé‚ïewçe"¶nÁ¢^ð%Јnµ¨„‡»5Húv!t©˜Læ6áçÈO¨)SÀFLÅÿêžg¼ÞŽ^E`ì~9Ã{|.}?r€f]þ K“÷¡ñ‚rvÖóHìn‰ ëÐʰ«4 ÑIG‚òlÿŽG¦íCNË>4{ó wlfäïµ`Ї™8ùçDÞŸÉ&TÌDŸŒE¸ÏÕ ÜŽXÒyeå(}é 9óoýt«„ó4Â?V³~›¶‘͇Žá½n6(ÛŠ6öAòªl¬ü`‰V;9Á ™\ö"„Ùô=ŽûÂi³Óôïšp€¿„¤[ãd •2ÄžQDÅ;Ö`+ÂOæ¤Ý>:€! ²-ÜO ’†l×WáxúÂE6Y·¤¥ o8ûú|S0ÆÇÚŽ–†ç.l–G{¡Où 2fú~ÜʇÅv '4´} Ù•»3^¿€ãÓ.f–½]}Ì O®ŸÁ§ `•"¨ßbÞúÃêÈU2ào­vƒôkÜ07‘»»1ž¬øŒ›U±?:Lr$É^ ¨—²AG?p»7üa/mÔ›ˆö€ D³?¸Ý¢+¹OÜÀ=œptv8=_1 %­úQ«vvœ`¶þ¬EŸ$E\ýȨf¸ò.ˆIAîâ™ôÙµ©d˜÷=™l5“Tí^€±ýÜÅ wˆPÓMy¬y7…ÊÛ¦t›ú-t}ãK½T‰äÒ´yø £}é9ûù.?Þs Ï)î`íKWcßdxd^ÉFI#ÌÅã¦`†A+ŠW/3“Ô ºÆ¦¥MCžœZŽÿ5`Í“Pûö;féŸK8ßõ:vjüƒeÂö|{¤pž½]"K¦n¹Ë~òteJ—¶àT±¬‰ø7j(ÂG“DÉÆ½Q ›{\޹Ðô =°=ºƒæèÝÀõª³é¤z5òõÇjhØ~œø]É!åµÝ¸èÇögßzÝ–LÿJLrHæÉÄGä)d$Ø@Šèq&-fiË$ ŬlÓ9?ŽøõG/ƽæ!Ë’¯ÐÅì8ŸMÁ×DÉÛÃó8çæÈSÿÌ Ð» ¯J[ñ»F y‘JU¦Ë¯ò sY3#YÿtõoÙôè¢g>ãÇ|q"¼âál3"˽1Þ–r± %»ŽÒ±.#˜úm.á·Ó%fbßqñÍ+Xø+ˆ~Ñûßü_ÀšQVén,l’ÎdL¡—#Z0,Ά>X`@nþºA…žQË©T]S†(ƒØ¢Ï|/†m»zÌ𠦘ºÐIš×vZRÙa?ذZ‚é’˹™ÜW<¤VÇ^Ú>†ÛM,“dÃ%×¶ýÁy¯£ÅEy8Ï<5¥Ÿè>9²´ÜŠ>´81>FgÐÜ©þäÆŽYäo*Þ+Èþ²8P+²¢K¿›ï)òô¡ÊJF¢í-óªÆ™nƒ^ìò6#%¾ÔyÁïT€¨8Ý€S~š8I—N¥ÞR^toìvÿu2mÓ׉üÏÓq‚ÝÄ£ËZjÀv3]˜…qì°)ì+¹cò1T¾L~¿µ&ìÆ¸Óš»l'áÞ»ˆ§kì}¶‰Ñ¬¨.¼¹pr¾Œíç@ Ї|½í©VdÔGŠö<ëæe³ ÛñÕòCö;ýÃŽßðòK-îjß ±§È}ï%wàä„ÝF(lçUA¤+ÅžuR¨™šºõ±ÄÅî9øybÕR'”Kä'É  ¡Â€,ù.J‡l‰ä¹|¬nËSu]èK›îÏ Ûå¥èȉàaϨôKÀÀ¶¶\‹›%t'ü/~(œq¶„låæ“U9«¨¥LÿûÝérñ•ònûü´è(ܽXÌå(ƳRÑ(¼þlßÉ=óñó_^j®˜E—›§¿œU~ šÍ äó7aº;ã6.| roÏÿ_Íó™1ÖjæAXû÷+{.äfØæÕJ)è‘Û Wp£,<¹ÈÂôpuzÔñ+[Ù¿úªçÀõ×J`ÿåV%%3NWïpß¼Ä螯ÁÊͬ˜n:|¼Ëtÿ$í‡6a÷šŸì²‹nWÉKÚnò’Mèªz”y×ðšÙÿé2{KñÌWŽRU÷ÿÛ_!êþßÞÏPwÞ‹Y÷â=ºhð0ʇ4¨_Øiúæ©5q2 ¡â±2Û˜˜ª^ÐÈ¢/Ÿ_ãüÛb‰»·‰Þº]ИðùçÒ.žh]Î}ûB‘–u3Gä5諚he¶ŽÌñ‚ÞPðþ{ÞvÆÑÝL/ØHÚ¯Þ먵a<ŸºüÁPþhå;‡ÊÝéÌÝÇð%×’|ÿÅÈãƒ_3tÉ·ëŠ4Þx9=³ð=§º4‚DÜ“€MsÄhi‡ák Muºk–2>xÍiǵ=Æ$~ŽÝ›S{øæ=‰„ÅMø_2J–³úÉ1äkøB­N@@b2©ôã£ÌKX­Nz£é‹\ÜàZ9Žù‘d¶c8Ì{ö ê©ûÕG¨PbKž2çhäÑ0vèÁR2M,íF ˆO º{œ$<ßÅè‰Q+´ù&u¸N«µ[ÜITŠSÀæ¹ yÜÿ?;ž¤»¼!yÓ"`ƒÔMĈpvßÁpêÒ¡GomK%æÒ‰ô°ómÈ›O+¬ÏÁú(ErÐlˆkÜ¿Ÿ}á§Fß µÃéøXöE^)s÷à6¢vG™v.DÁ$*zv=pƒå·Õ—57éôk`Φïø_ú¹ä“î3>É[ðÁž˜`„ù§¹ÑXÖêC"„ïþˆz9Ÿó„w§;Cº¨ÎSäêί†6ý%h’̉?ŠFòcp º: 3sú;Q} m¼­§x§EŒ<ûö˜­ÛZ˾(MÅVCq*Ú¾Á[™¯Ï ™•{·ÐN× PgfD7?¸…r&WØ*2‰Ùðj'T߇‡^BÔêF6s¿í« Ã|_Ù¯sqÎÝp B‹œtHÅÒÛÉŒ‚圙mêôýÇpl¬Rbªcíeüb'J¿'K“ÉRÄ.–gbþkA…nÕ©-zµz* ÅäIu~6|y˜Dçð°žÁ7`yºiµÀ͵ýöfÁnäÿõÝá\ýïÿvV6‰ì-ʦð:÷3J(R)-•´TšZFö^ QF%D*¥ðzÞO”–”Š’v´5h©ôóþ^×Ïç—Ëuæí^ÇóÜçq.\/À³Kó!3<ªtYü¶-àê}×,u'Ûç ©%¦Ìn‘8…°ÀU¶ìÓ§àí4ÒL.ATøVª¯Ê—,eVwþÊžÌË×påå:î÷" ë«uª±öbY¤8ДáKIØpï+n …#3+¨©±4³r˜HÌí–à$Ç÷ðáïyööôinÓwer#`¬õIºuvý‰mØrí8‰ºãg‘©o§‘‡±Y¸øÏÑ‘õ½ô9¸j×Qªr ^Z ÷—pÒ‘Fy_TölïâTç´sKÎ3—œ:zZW'nTd§>¬‚ÑGÂàwxF^tF·aî’ÑfŠc‡9³ž6Ç*¹ò¼Aì®:O_þÀqR¥øöÜ6nÏ.qru×(Þr¡ƒ8kC5è6Í Z¯J`s×çöebÃüMÙ äáÃÌÌßâÉZœ³¥!œj¾€^íòÄ{ëhð*³„€•çÀ† b¥º,¹ÏcqùIpùÏFrDs&Uú½„¬®<Ëí(mÁw5Ôé_ÔGv×»EÄ“gSÅÙ¹»ý4×mùHþßÜXó.K2)5oF“ơӉ/¨1·šÛëÅ¢˜Xã n}z†;y>ï7°àÆÔ6ª°î´z˜^x/ÌHc³¦£|ý$ö?42¶õOÅóbäe`&$eæÀÜ*eÖi:? š°âÕg8‹ü€D5öþÈ,•9Ê[TfFìò]ÑçùB:pk+ë›àG‚Ch߬"¬9ô‘^s¹„‹öµA»']µ¨WW}†eÛ:褯ø!ÿŒØ}›hÞ¶Æ…f³Ý}ݘ¼çL‚H²SÈ‚ô5(vÒ‡sæ&²ÇµQþŒ!ÿ_ýíü/wþ`½D˜ n²aÑŽ£‡à övë ¼i®È÷[¦Åâ¿‹¦x·­ënéÔ!:3<ÀÈC‡ôÆI€žH¼íÀòê ¸wQ‹]8æÇ§+L.¸æb¾9:š‚²_ýòvˆwÇ‚0ÿ±Þäé¥ü˜,Æþ)èÑ8?Â¿Õ :0ÿ§3I¾5&ÝìÃÙ>xÜ4}Èx Ñ÷ðÓ\=òT‰‡ë2ÆáûâÒËßC‚Z,‰l³Ž6­ýØYæ%lèãË—m¸çåøûKž#Ž—:qY(Sð;ƒ"Wʘ݄ˆµ—‰Ã|=²ÉágM]–Zº€èÔ;êróôijKæ5Äòºs¿sî¯g§Öÿм؄eÏe‰w–³t¡£ð~¾4ú ùfsO–™»‹mw›¥{‚ßÕ!žÊ±löÖí vu2( Ìf:š$ïf-¬ ‰ üšQÐ~ågVÂð¹©ÍOAf¬&LŠl`n)ÇûÄM¾3“ÕIÆKOd™š0™ßÿåu¡40 oé°ž¢}°k¾(3q“ŸÆ«¸Söu½ªM’»}Á³>‡õ(‚•âQ°yë[˜þ³ŠkôóÅd›õ¨[9RÿcÛÎâ]]NwÀs'ŒåU}Š.ƒí¸,*·”ôÁóyVh½ižq˜Åk;7‰ù]öúIïðop]cˆÌ~RR'þþÀ*ççÒ¬uoyqcë¡Ë^ ÆíÜO“»bWP,Ô¦¤òò/Oã6jñWˆ…Є¦$|ða"»9»rcaZsÌÍ[KÊ<àúÏ}xÚö,,›±Üs¸ À¾Ä:¡ì­¥x}üGŒ·èà[µ Q™£ãÁåÃW>w&KIJ~:Fñÿ9èÉDr»Êéð±ôÆÆ Ðß8wzÙ1 \ª õÔjØðT‡|ýº÷–ÀµE0§s2†.4£g·q"Æ·ùj»»¸Ù¿¿€ÍmQ2nœù~ñ)­6Š%ý7¡ÿÂxòf “ŽR´#9!ÜémXÓ„0¸<6;¯¤¢þðÀ´‰>s>'DM餆YðÈeh¾'°g̺‘ø_lϵ_¥r‚é0AØ2%ù˜??véÀ ë‰r`!È›šq{.5ãÖðÈA›ù0뢺,‘-ÎIäÏûã\|ýgÞ²ë ܬ힤ûÆ.Ló#ÌÈ ‚WÃñ]­4{?ÍêTòÿú‚­òƒœw Ûµz@ýÀºzâQ4Ÿ&ç¼#_ŸLÜtÒ9¡:•ªE>ÄZ:|òVU­ 7Úý§ß¬øf6®ñX Ÿ“ÿÀͺyv&•»¿‚ŽB8wáø{ÜiUÍ=&Jvng³ TÐÿj&§0c>¶Ã7Žøÿ`Êfº-G·L¶ öcljü;ðÄ'*Suè ëÀ_¥õŸF3M×Ó¹1¯9[è„×Ê›ðuékNXý”ó¡jòA!Zî¤3z°øi0ìí ;ŠcóôÈO”Aõ¦q,46ƒfÙ¯þÖpl;hN6¦žÅÐ}¬HÌ•œ£¸zª 39yÏ%tÑßz¼1Sa‡o=*ñ¦7ìÃíïñÕ>¼ÄÂÝÇ‘LP$¦ÃËÐÊUÖ«©Èj)vä†"·Im>ä]ÜW%Á6£Z°}‹…Âq´Ó=øöF…ÙÁñ꿉à¿À£¡m}¤óÝ"lAi¶gí ~¡S¦ÅÁTùiT´ß7wïéX>XäæÑ u¨{ƱðËi²ª9ùï·XA O%ˆÞR†g§ã¿¬éøcu'²·É¸dÇnJ]¿èC¤õoÄjñ$¾}ÙáÏ…)ů0Ã?žq3/bÇŒM°¿«Þ ͈Ìf⨊ÛSpHä:MÚœ U¿|+²½¨|%£quÏW|7:”hŽs‚„’pôêk Òq2D-Gš•éJá¨温v<ù¸ þ{™^×Tc‰|%{çÇHý3“áäty°®i†ÞpÁO{úißÈáÚ–ÉãnÂ6ç"ês&®}LÀ®•³`õ¦T8¸ëÍ›ÉщìgA;Þ´µÄo¹aÐêSí=ñrÁ\Üæ¬‹Ež‘„stªñä6, ’fÏÆòAèÙ:Ü~UŸ}ˆœu×¶úÔñv\orÔOš½ä.DÜ‚n™.tÝ|šzT•Á忣´~±8Î þN›¶?™¾Ð´É–ô_ Îùé`#÷œ>*ô‡³V¯¨Ø¾ì†wWAýî) "¶¸Ì1:2ïp|®ãM÷ëÃÛÇ€ç"¡‘þ¿Fä'Ý÷lo÷UMV™„ï&Žäÿ™;±°Øü"Œ•ŒÇ}qý°’›Á‰ì@Aól :]QÅÂ,ÉyûÑçxrN. ‡üÀyTSÀÈfh±’šm¬qÁØ[Õ¢CìnV\ÙòV”žµ\‚«¶áQñ7Æâè spÒ}–r¬¬mö‚ã÷ZÜR™Œ7àîÈv'Á¯Ê _&Žç/jÌ ÄŽ‘é«C¸ƒÒªÌéª+_ÖGœ¤ô¡¾¡;QîœÌÜ•ÜÀQñ ß*!³AþÛhÐ]Œ£RnÂý?àz¶ g,ØÌ»ƒ[ß Ì%Œ»}_SÿÀÃ\¨þkzòÉŒû-']¡ß‹®ÀèåÊïœÌVøÏBëÀ$ªé› †ÊXµÞŸ+¿BÅMV¸4âƒó¼æ0[©K¹4(\v úwhr†~¸Äç® #†9³øJoÝH•ùDªÒý8—cüî-‡9KÛÑTˆZ?6»Êît,…²ÔDØ\~’Ûú  Ò™åfFà§'6`ÿËÙ9ao¦sù/¿}ÆhJz‚‡VäÚzL™ôv¹þ ~‘œCÊ£e‰Çé)äWò ʉ;ÑÙë› z?Ç$ùÈ÷ãûñÕäÕp¦k+/IÈ…ûþ<o¸?åŒÅÆÔ?Ù”}üÕžãÑïÐF°ÿvŠ«¶yEÝBÖuIÖrˉ¹œ”ÃÛ9r«Ï,Î5rë™LË(2N<Þ5ž¦!%òôâØT~–æ ¼ðSŒ”[ æ=R,tŒQùyœ½µð%/‘ûzȤ+²¶súð}»"ïÁýùz³gi|Ãó·=™Ä[]¶ÞÓ H÷àgç|PžÛAµj”©¤ë9þ¸S äs»®ÉOàOˆÈœɂàZ-JØžIPk¥Â|ƒmÉä]vp-ÿ \_v26˜¸F…CÏ{œßiDÍÉS9ã'Îèš6ý“,¢ÇV9Œø_êøÎÿ® Êý2i°,)àÉ'yðÔINåvÐζ A­"¤C Söê'äEr6¢šg 1ïÙ‰ŸŒýAT¢œ{›ë÷‡áô[f þqºØ¼îÏçK7ü-(†ûsnÃË>/Øq]–|³iåBCjÈO»å~øÎÕ‹çG8ç'æô’ÍŒÿ÷ D'X£W9í1¡xUô"Êw.ä³{rp%C•\þ÷Ö]yGc;—±¦›4V‡r’鋘p½þ¾sû( G<Çá¥Ä ã6°ÌÄŠ!R²Ë *xÅœ×{u¶…¤ŽØÏ,¿ÑEá5X@äfæ=¨ä¤ÕLI~÷lBÞE@Oïø¥ú—>Õïó¤\®£Y„µ8¦p½s}_ŽË]¦¡÷¸×*'QÓuëÿ{Uõ4Àýã^Ô4øÄÝÏbïßiÀ¼´íúá»§Ò¡øa^\†;Ç þ—䳨ûÞŸÔtÖ\çßk·ã' wö(=’­<÷ÂÙ³ón$a[)¤ÙÕáºå‡ Îã#ßÃG˜Œ“„Ã’–(ðÏ/s§ŸàïOÙ ´¢òæ&pj­¨bãKD^ÿ¡¡M ñd4©JŒ&:¢·Gúß’zÑáþ›„&G±™3MÐjÔV®åûEnò¶hçß-.wÑ"£UŸ·ÂÁˇpþ›Ó¼© y50. ß/Ê2ƒÝ ð{k7¾2 çpû#þ¼1ÏëØðåÎü—¦ßßô¿yÔuðnkÞ.±€wRiÛ-°±èùåŸÀür2”­*Ã^I»&eíaãŸ!Þ‘KZlÏ{&©$Ͻ.Ü‚§ß7ð§×çél¤½u|X8[Wææ5¡¶—3x/ îËOs+ÖeòÃgtAºùAþ•ù*,ç£ÑÝÅ%DÖ¬ÿ»ßE¹—µ¼êC‹`ÞìñŒl“D½q Õm—õã—6ç;®ÍmH÷,åŸ}SB?g}£×§·ófe¯Ÿ”\8ªà ò~Á×ö{Táê(‡YpqK?>¼àGWjl!_ë.ƒÏN)(‚Wøäs<8Œ‹º¶žèG+à&ø/ìÁòVñ{ˆ¾²­ÀÛó8ÿ×ÊL«ï(ǽtàõ‡áƒç÷Ag|Ír°Ã-gncø¶ë büÞ`imHÆFŽó‰RìÉêا) KrÁ%ʼnÉî×$^ÆW ÏÉ?÷4…£pás/ßÞ1pÄþ³Ù? Ûw.znŠ ÁqL#ç=xù¯gùªbLtšë¸1 3Ò9‡,dû·r·¶bòî1ä[o*ì— æ½‘úŠÓYö‰ºÃ6áA»…NÏŪTE¶tÚtñÚHèí_˜^x—ûQzbÝßr‹'I±Lg#6mI8~¸ 'ÇÆQ|\Œ©.ÆLSB’³$CEÅÄâÊ ¶DMmVe»¢“9íqÚ,¡ÇŒD·óØ{ÍŸxÍí[ÜfeW/A¬·±qOÅWò[Ù×sV4Ȧ\7½¦~®‚Ñ-îP^'b´•nÁ?ßàZ­iÀZ­&.y¬99Ö»ŸÉ~Ę½Ê„»£J~é¥Rñ9‘ô\ฌ٢ënÆ£ñ7AÜÝw‡ÓïÃîè¥u¿¬CfÁvÉ´ÃÊ÷Û‡¹ÍIZ¢¦ &OŽaçÙ4ž{eõ«qÍ@¼sÔú·€gn¡¿·ÊÈ¢±áaI÷J_3ë¶áãPQ¦Ô%Ã,’ÂqÌ™ãtÊÑzdU<óÚ$È_åÀ6«„Öñ ¤á~zÍ¥ÎO¹¼ÈLLÔ_ ål<ÚòîÓª´r`“À м‹š¾g°¬ìoÚ. ÔrêÉ‘oy–è(…ExèÏ>x’>gO’éå¼Ù =ä2Ú¾\z£Ø"Çnôm®º7™\ÔaÔç3¿E!;J±é¸ÿ„ü†”¨0µ¨”l¸ICó’Á1땘¨FPB‘ÑuUÀ×UçêË»óî¤7çI³ïÓ@åv#/©i e¦s“f§Ã‘îtô<{TWBNs0üVœÎ»¯·•öJØ¢„Ó>¬é›Ï>ø>£EjÅpÊþ|;=^¾ ºNà’±Qc?¿Îïq{£~?—õJk‚†š;óÅ„Ìv†Òˆ|yCÄÿ¡Z÷0bV<Û­F$îúC†çT£ðàk6¯/ç óßÌ%ü3íeÎàpûÆtÄÓW92½ªç4É‘ÙÚzäƒÑ-4ùñÖÁvòñCx­?Dìʨ…îZR0•GUvN‡°rIÜ0Ç”›`æh,Á{ióü®%‘q—2‡ûÅ&;rg7¯ãl,ŒÐ^räûrh²NÀXáÉè0Ç,|“‡‹ã?mwfGRÒ¼¶µÍŸLæ„•kåXt™[4ù ŒsNÄÁndL[öÝ6%/ïÃÖ«YÛ¥kú¡ÝÍ‘s±–[w:÷5ð>ã.‹Ÿ(©nŽßÇ]ç ¸ðgã3žL/ûA­º'³ÈÊU0Mr>½{,yÆÑË÷ÇÁì=‘xwªotè;tL|NýÓ¹ : Þv†Ç…ŠBfm Fhle^/"a{}¶^ww%?ðL½3ïS"+4#‰ÿ²7ÌǃºB&V“öÇãXïy”öQeîúé }‰įi¤Ý!§é¥êX6-*ÇÇZïõ$ÅІ>É„/Ÿ¦Aý±³½]Œ$ˆý?½éS¯ìÙ•Ôbx¸¾~Õ¶áÜ‹¹XÕ3#ýßj‹~öªäó–³ þ8îA®¶?Äßz“˜¥©ñ^>íyx“Öëi“Áõm T½™ý§Õl2LjD¹ÈÁ"»r¦rŸÎo‡¨êHö=x5Kø~’ž3!-VÎ gè˜k¡œ[B‹W7BýÝ©¸l¯ [¿= £+P·¯`púyÞ‡†<îÆ;¥k dã[˜s çޜș‰Ÿ„´›?ézsYr?ü%ü^_ÅÁÚ5¨ùî5wyJ ¯%n{p<ŒMÑòbãs§’†¡tæ\Sëf'‚ ScBSU˜Ÿý|\¾Gú=·°î÷óXþ»_#ö¯––á_p""ÍØ³½«‰½×e82>ÍTµ‡tÍ8vJú(‹°¿ Ñ/‹ t]›tÓ›½Q*®«XþŒ½{ó<Èâ'C°òÓ8&Ä6öG¦°1tnùQVÀ¯"¿-<Ƚ«“qÚ¡@° àCy<.¾ØÃÎl5&†–GØ\s öd¸Î?¾ÿ˜÷/º r;BHU é¶<„…k0~G69¿5Î܈¦æl±Ü):`µ ~^;×`4ÕˆÙÐt¬ »ÁÕŒycoÔ®V kÏdî–Æì’ó7ÎAêhžÏ#=á)Ÿ4bÿœj4°Ëû7^ÇG~õ§Ÿ–¿"U_r·X þ}/åHnV|ÊWG÷5ïqaš>ä'aïSUHÙfm²çR9Ä-~ËéOþGï>¹U×TÙ‡CvÔ(t )MÉ‹±?qµG<‰"Ëy þÉêRìñËÁXz5]=Ëíê~Ìšs>oï‚ÎSOrÅ!ȉQìö^ ¶jýhÎÊé6Ž1q!ŽRµäzáW>?žËÔÂüt#°ðR„}E³ˆ]´ 3O Ë~Üÿnâ̓x$b=0h£i¦3‰áò×h>U“iynÉÿ¯Ë$À®v2­ÞaBCü¥* °Æí®–C4N€ë+mÉ¥ÏUÐEÍØÅ‹¢8aªïË$yúøG.=¢êëÎÛÙkÊ”nO »þ]DnR'Uzö -€µ+œàë¦:º×i¿Ô€Ío_ÁäSÈâñï«383/ª¦òî»nãúSÔàNìD¾›œÆÇ²« }µ½J ýn®ðW@BBµÐâœ^ûÒ{tŽÃÍv42¬ öJãòbC½ Kø:,¸êl_ÛÚ¯ ©¶§5ƒâGÏFxšÖCLÚP}dþcÑäX3ý<ü¢V´áŸþ|Ú¿Ø’EãÉŒƒ\‹S·üŽJG‹‘ÞƒiúO™ù{Ý€kƒY¥»î_MC·ðé·¯ “žÞ£Yœ›4†Ïû?h·Ãü§Ò^¬µUŠ­{€÷­uÈ•áþRX2 "ü“àîìµõ:ü¸Qµ‚äÎ]žT¡<ˆ>NÀw¦s7c·‹°˜+TïÛ[žÐÁ¨·´%SOÁõó¼ðÄüðx¥æÁÊÉ|ÌÝ|6¦³°¿aõ¼Mà¾n€/zf©ý¡Äx;fâ⊺z÷à9-hdýw¦!ÆsºXhø ­›Ã°P`/—£³‡K¾w –þX‹‹–Ü@=ýpßÕìÎÂþçÊäéW+tirf¿·€Tÿp‹x‹c—‡ÀÐú<îËà-Lýù ž>žÏì ÒpdzKÜ#rüïRf¹Þvë³OgÜÊp¸öã({ÂÞTR¢±—g'àx»¸K¤¯µùo¹·4“5ò"kùRœ¾x. .ËÎl'»ž9 -¨F uEü¬mÝUï1ñ)¾¹ƒ9U`ÄÞãPÆ\3dØìý%,¸y1RÏ${Ç‚««#Føÿ^¶™†¶î5!¾cV±×;7ps­`ïÃÍl§4Ÿ®Xr‚Û7]Óž©²xRÁ}¯ˆBË ChXñ“æë÷à‰©Ð{%‚CãñÈ.K2E_w8˜Á¿–­árZΆ î9öŽiT& Côß×ë0¸‡‡ƒT™t“ojÀ½óž` Ï’Óÿ2…ÎÇ\¥êYÁ©„7XX¹„­;wÎü°Ó)F·À‚õ'¤Ðë#õ/ýߘ÷T& 'kõAEÙ½ü|èv˜2GÅÀyïA1p¨”7aöä7ÙÂÃX=- l¶rfÏréÓ]r¦z4ixö r„’±ãr\–õ&«·ú¡YñI¾+_‚3먠4÷;ÃÚb¸N¼ÿöR* Ûƒûœ¤YŠ>äÿ kJd`HX,Îy³¾ÊP®îÆ¿³ ÁÑ€Þ9ï +ž¬ Ï~J2‰^t©‘À‡F¹ð@A ‘Þb{~® ^j‡¸½/ê9£u“à} ôŠ{¢ä¯¸}¡ µ8A¢üPšÌN¯I³:ŽNZ"²kÈIojm7ãŒþ÷þwG©2><·•f•á¯cò¬nî 6¹Ü™pûó˜vï3 “jé5ŠäKY\º´’H`>Ô)ÌaÏ%¦&ƒüü?ŠìCï(f_ù &ÊÙ3g—µÌà²iX¸ÉïÊ…ß=Þ¯åÇQ’žo`®9Cèô4žÚŠ…Bĸ­d`órÜ»;–͹q‘¨íǯ®Bd•‘.Ù>7 ·°q{½ñÒŸ'ÐxÙ2x€÷ö!(ñpE­ÂvüusË‹{AMÜ“{OÛ_xq˜?Z %À ‘4x;½µ«0è±!ÞO×a¼µ'0üfhÎ]:ÿ 8T™-ˆÚö‚xÍT +ª[À:2&Î;¾ÕÍødÅ \÷,{Žg·.uóõÆŠÝ·åØ7éSàš\‹GºÀW\— )Üx“Z<}Å FŒæÄ|§ç³"ñÌ «|(Ži‡EÈ“yåܯ¼KÜ·æAœ}§ÄB·B“ý^ü-Ø„ÿéß}“ô`ûW56áUð³Óœö˜v´­i†9›’pô¸›”»ütMÿb÷ÖDÖ9*ö¨ïæ)äÍ‚½*ñyÎ’?Kþ ã¾Á1x÷ì¾o@ª×¯ÈSÉ0ñN wüð)X¶[ %Ó-˜á$âQö G- çîŸÆ_ ÁÉ‚Çú³xÒo=ÆÆÏdòóÌ ±XIúE­Fâ|‰ÏŒåÔ̶±ŒNU|6d ¡Ï @©m?©eÅœV››Ü–†á‡Êq(Ç0ݧ¸¡€ÉwLð3c¦ìÆGW§°5Ò]xh`󲎤bÒgð¹äx–xö*Öfã7{bÙò•¹tLº'18wì‚G³-q<( úźDªgTºR<$…Ÿ_ “S]1JÅ•kØXï<Þì>LUtAbg,¿›«Æ‘c Ù­9£qq¨ÞLi w¶…ã†Øwpdì”’‘$M{•`ð´SV‡/ÜQÔ~¤…ÉÍΆ¡Èí·‰ÿWnÄyõ¼õj[¯ Ò'˜Ql)\|–Œí#HµS5¨-àÎï78Ý þ2¹¤»Çžeí™ËÔ†û_ÙÖ\v±G˜Þ={’H´@¹¡8‰mœÉy¿Œ§ñKÄI˜¡(è,`ÉWç²wR³qV>lY:€¹ úãq ¾¿xKÑŒTkI0ï)ªdKá2Ê–½þ±—Çb«—2Òq"žÂ‡ýIÅôKÛpÞ¼°"Ëfß+'Ð÷‹.±øaHÆ^3ã,Ö%±Eeìáþn8™$«r ,»¸… ÷] 5HZLhݼđõ/Ë#ø›ÇyÀÁøBL•‘g¹l%;³¥–íì-ç¹fÁÒšMDßi \;‡²Tfó>cCÍÖtâ:s «8Q€[,ìiê\Ôyþ'ÇÄ»k‡Àä¨*ÛxÊšÈ ²ÈÔÝX¡H6GóB^MÇ«U_x È]ýghãS¡2bDÅÞœœ¾ëI\fnƘS)<ží3<<¥“Ût|#{"ŦÍôÖ¹²¼¼`,_0“ñTûœŽ*š‘ù:ÚägÈTöŸŽô·ßÙèó.žìñÍÀÝÄîoXÉZÿ'o—m Ã÷í|'8 T]ç±%E#ø§%K’›rÌVú‡`IU4RÃimdÕ–Äægum»‚K'›‘ø¥Ç±s‰>É›³€ì“tE3Ë¿˜yÍ‚3Ú^‹D÷³4þdø ›ÝÔ ƒ&A«÷38¾ÉˆËLŠ¥^óžÑUò“@]¾ëypÔ<Ÿ“¿ÂÁzÐao¶²ŸÆ»y§•n;ßòz„¿ìÍÙ6ß*u`2‘p> Í:C ¶A‹]·M¤ ݉ÛÛiLÏ[e*(f›? “*”=|6&´ÃD.Ž´e.ÑÜÅ–Óg5ó¡Fº•Õžp¹nbdoQl¨û7Òÿ·©nDWßvzlÆ6:Õä)]ÿw3¹Q)ƒòÇ’"†¤yhX (°M‘Ó@\hy¼Ÿ³ºyŒæýî¦&é‹`í ¯aþS¬r: Y¼ù,F÷;î·«E±Ù Áa±tE§aVD ˆK‰°§¾ÂÞë‘à»í'6|ôcÒÙãÈÂǹ×ÚáÐpfIÁÐÇ;(§"Ë|½_ƒÜ‚6pdqâ¦àÕ:óg5CÓô;\EO4¯õƒ[°¬ÍeÍIÓnZNŠ,]ÙVse_ãÉøÉ!xnÓ-øÏ&©0¾Ñ’ÑŸù4Ð*‘ wüïûw'‡”!wŸ>ýOÚóQ÷%Óž<7\ȧnAñ¦$\ü¯ýž§fÉ8ž ÑQùNsVVCö¹e «AÔO±ö-óQeN02•öè’z¬ÞdÈh]âu»«IêÌ"Ü\I‚öi n’Då§îZEtçH‘šQfc–K§-û‚þ*NŒ6Òøí<4–6 ¿sàiòxã{¯Áôq+N=bIÎfKÝ€qžW¡ê…»VÂfA=,iôcô8©QìÆ®-xª\…4Õ²ã7s-}²ðhÌì‡ý?…áÁå]#õ¯Þ3‹>9þ&ï ÃÅÓñZ¶%ºˆJ‘•y±,aZ]Ëc«/×úŸÞòKõùü€Î½ÀgÎÌ[( ~&º°ˆ Èß5‘ä}Š-õv›=Ýw ¿¨îÃgïF3Ûuö$n]I¨’!é©%¨SÞÇÛøe<›²g3­ª^HìŽ{â$õ`©Q‡,#ˆÂßzþŽé>˜TE$'_À_T±â².9øõ=œx5ŸÜ—Ma.æ ôÈSì}Ř¥ã–¤]ÁVV†amL$h²‚ \0ˆrMÿÀk‘>¹“Iœ×u9Xµý)^O²é§9ͨ›˜d€œB9ЗA«jeö¼=а& ²·RŽ<›¶ ¬þ‰²ù©o`òå[xèŸÛžõS ÷§ÛNׂ匟8µB™ÜlÑ$‘!Ž“§…Þ—¢ÉjÍ.\KzàóÄ °#â üœ‚çÇœ‡©É…Ë‚Z¼ÌÈúW{âIîÒáóðP\3’î3TåèŠ6=ò÷Fóm¥u؃ Çx!»O#¥»Ùé`îȉʖCbá¶ ç)ª®"*YÖÎ`†«;»JŠ ¡ûÀ$âyá—ã¬À>œT@k{mÛ8¼}ï雷㤉ªðA¦¾Dˆô»ÙÿüHèÈΞLvdS‡ÎÁµüdvet<øõfR/{ÿoîúêŸ} -$Ï´ÝÉ ÷•L°6É â×cjd7=ˆ³î)°ßI)d«Æ}gÏz®e²^.„þ¹ÎV¹é{÷ ÀÚÖCüÜY]¸è´ u|„x®ÅõHÿÂ]%Yveúû|ÃkÚë™ë@9(Ý»±eÍ8}¿^?__D•ñdšM>¶¤ - ²\|Zü o‚)"ËÈ…±^LÇ^…äØTsó>ÒÖ¤1dIò nå /š7 _ Æv- Øsó2·"a³”lßLÎOd»‚›|=^—‹nI6à?!¯äÕ÷b‰ü®„t ’޵½¸ûÞyƦ~Fé—áGÓT{Àm*Ðac¾ÇW¡}î-€˜(b^|kû\X¦Hшý3ºæ9gëï£&—ä`^ù¦,0Žõÿ4%­;&!ð ‘Ñ…ñ/ªÁ—y“±ãò‚Ük!pŸ ,Ôg!f 8›©LÐÎÄU︞™&à)ŒrÕgðÕÙ=¤~¹0ˆ¯ä@#{¤Gý¹k~sBÞ°Æm€nÍ8ñð 8¹qÍ?œw^Ó/§uô`¯– )œYÍéæÂ}c¼¡/È¥½¢_ÂE=¾“w8œm²ÀˆºDör ú¾C×WjLé|j¨ç69~C5ÉèÚ{»ÅàÄŠ±øÇþ 8t,Àjçx8£Fþ×ÿ˜)Ñ; ÆaP¨”o ’ËßÿÀ¤Qr°èÔL8ØÆpc©ó¹ ‡ËÝÐîA¦£ßÂ3`]ÇB³é´=ïÑk–)“ú§ôŸ†3N,+„O3õ+ÙpàÖ}8Ÿü‡}·„ÜW³Gj0Bänq^픾œÌ[•;×ë7%ñº[Ÿáºžίè3ï…û;8¼ÑJ~üö$Ã'°-o©xŸ ‘z.L‹lØѿ<ÔwÚ‹šüÙð5¨÷^vjJ3º¸=§§Ý2ÀC¤v3H›î /߯Y Ih+c ÙwFüÿ¸µ;wŒÂü5­œb‡:ëþ2ÖÜÁ}m¾ ë^Ä@T™+»3N‚ì¿×›¡]ÝŽÐy ìÉu°z|çZ¦ÃÇÒMÕXdsæ­bß_¿AIe8oØÁ_Çó™ëYðVxž?ŒoŠìpó:YƳ¯ƒ¢Â~§tss¢/9Âù©äì:1¦ŽqD¹€Hö)â5>2Sf%jß MâUrÏ¥Ûá@Y#$>ÿÈÝÜõŒ]p:96s5ð_´ ¤ Ñ»»…],>N42ÝÉïø)x66¤íâýâÑdâü¡,Gv¼ÁÿÛÅUé7cÑÿæ‘aéý¯x§{4¶u¥ðŸm®Äs´ Íå²ñ—p/–_dwŸHÑwÏŒHñÐfœýªŽ—ïx›®úiD¿›yTƒÿmà>yÂüI ð!¹›Ø§'ÿ7G–N•øI¬,ÍPc§ Áó[ QN…¨iŠäåªVέè$Δ&Y-+¡R¿„àÐè?ž¸¿ÕžïH&òü‹*­5{ÚñˆYlÔO5²rC9¡1žVÿTa³m¶`ð«à7ù7'Úˆ½_ödǰMz¬2[”­©=¸úNæ±°‘÷_:f&@í’™\ÏÏ{Üd×WZ)„ª'Qñ!2w`#N‰-ÆW_á‘Ï!îäŒç`õ4”FÞȣ댡Ó[MÙx'KšcqP zXoÄ»‰±Ø·;=¨ú®"5îb =ÛHú¬í¸çC ½Êþr™§éþDm¨o ã4éePeß§œŸõ{à¾Ñ¼»Õ¼I§Ç1u× ¸àËA\îMb¿ec¼‚5Ì~þÚšóú\ä± Nµ±Ó4³[¥}xõõ:¬ ±ºëÍîHK°×·:plÎ.íçG² (ñ8/“g²GüïxVœÇ÷ûèè,A·yNMê,H kQ÷ò—\Úï‡ |d5¬ò¨ÇÍ@L¡•Š@µ&ûnаtIÿa~£Ï­íR$±ºèùà ë~˜ãk_vE|ÞÇzF+ã *l9©‰c_¨3×M[¡V¼…-íXÌîKx'ö=Ô á”:]:aÊl9gÎÖä&ƒàÆó¨¹û-Ÿ Õ›wÁæ9oéÄøîÖd+Øä£ek±IâÇKîEë”/ø•3taÙ-Wqp¡ÜéÈfíΣˆçö|Îváxöºä1~1MÇp™_#ö: ÿ°>ïXj¯ÞƒÅÕ:XPR¶æ¶¼2÷ œ ùŽÒ;¸úÝœaªÿŒ…#÷à…}\6r¶É¹ŒÉdþ¶|Õ¦Xdó_`ðÃG˜­I]BKññ™Ÿz—4ñ…ÊœO_..~ø>Mà][Hf^JæŸìF%ÿ¼w,¯^Ý|³4½©Œ[WÎåT_~C‘I^¸Q¸Ý·†½ê@Zú"¶©ÿÁ©S>ÀÜ2Iüôµ—­ß}ü|xk|‚‚˜ŒÎUضx1¸Y{Ȥ/@NG¤Ž†­5»±ôN.Ì ‰±ÿ°tN×1AÏ,úŸŸÀÕR–4êV¼þüld×sæóxMÅZ®á‡×pÖô5ÃàÕè±ü ͛ʛè…_%BAVEƒnÛìÄ’$ùÁæ¼C‰†sè ®ì¹EÛù3zýÂ×y{éªâvÔ: öŸö3ßè¢ {pä–~ÿIçipeÏÇ€¢o"vy¦~†d¥bOqQ)nÚv ;ý¦r£¢Zñá@:&mÍFéÕO SÅ^×ÿÆÞGAæ{ìÚ-¯ßÝ‚ÇâÎŒ”fwÖ>…–+u¨·$O\ùÿ›Ý:^µäZD1ôò˜iï„ÛÛI/°@“]áh £êYB}8,t‹GýÇipn ÖnÆJ6„¦cÙ¨¬‰0eH‹MhQäOÚ±„G±MÇ´h¨…9Ö‰w@Çœ¸êüt»ÑÉè"h?‡Óãmɸ%ù7ÏÑ ÞØYdB¦ec°Ù18Þ6ˆË®ŒƒŽGÈÌoúxÿúdrºZ‹4uÀ£+ðý±' ù¬‰ÜÝÝ©NRêíH¨¨+þ€›·^uB*qŽÏÞŒñÛiêÄ[ À®¢¹Z#—'ÔÁ¾Ù‚A‡Fì¯]½ ïWç5l?M òêqƒgöó}`(?ªOcQÖuPõ(Ãà¹F/'x¨pW,«Gµ²œÄ{Ü6×–>q o¸J§”¡ [&~n÷)+e`þ ª5‚1OZ—|4ˆ»ì+ðaR@ÿ¤8¾ßwíòöƒâfc&l?wn>aª`¾H™m¼ @'¨½áVJÃå¶ß𮳻õ5›ÿùAfs ˆžlE•xžXa=Ä/‘Åêöpnò¯ý Öm€zàpü*0_`‚Aºá¸¹ü$ß@=7H«!¦EK„áNÆÊ‘þ_û|'æŠØšx |gBòyw¹tQ‚ÿi $ŽùÅ}¶{¼Yp`­Z@XÐt¼ÞG=sžðÌ}lÉÊgç1|¢;/Ÿ„ Uí4pü|Ô?væº ±/úcÈ›Ø êØÈÕ´‰Ãò%+àé² ü¡ ŠpSÕ•½=sºã¾æÑ; BoPÿç;\e†J{"é©ì4JÈåÔ¬бòað·?ԨĒlYâvý<8ËÉ8=â;JP¹ªfànœ÷ïµa½é öyônÈ:™‚ùeðÏäjû¸²àkôùÇeä"o$ÿÿÄÿ¥XQÍ¥ìà±KAïqÝ{9 ²òà~^ÊÀ5åù4 ⦹•Ãgœvã>·* æi©³*­r®ÇQ”0«ÂJßè©ËLMvát¯XÂoêtçþÓ‰^fùX˜9U}ºMìA*¼ EÊ ¯ã>:'á|¿rl‘¶eß÷UÀr]b}s)Îü³†¯E#áœñ|“ ?#ÿäBöxÔ e¶ä¶)I¨=ß`ã¨r»Òàýß©ðßìµ’ûXØéve§pëärÀå¨4Nyɵ —ë³rº\)ty¾YÿÖ‰Ûê|¤BöKùbàôƒ°r¯"‘LÙí?eðúÏû¸j³Ùã• ™¤Á{·3ûC—µ¾áÿEµgéï:äHjVMeë\aºSW,çÆi{5Ðû'lÉ«GŠd¡ësLë±$ÒùY¢`À}¹óL?DPÜÙVÇK¶~É •J†ÌuáÊÕBç­Ëmp¿Œ"y Ù]¸]K D°Ô=kqÕÉGð7+Â~sm'{xr"Fp/;eI¿çM¨Ë¿C×TiCÙ¥£tÿ«lˆœ×6¿Çù—Óaæ CªGEXM…[G3¸‰±ú¬¨ÖšæÉtq§ nÈO<§ÑK²)*­>‡Yo¡R’bmD)÷›!Ñúßû¿…™]ÜS[þÞW¿Q&f=}ùg3,U¶!û.íƒ8§]äï a»kÈrþmÄàî;h¿MBÌRpmÞ}¨¾ i© èE9²;–ûbzøÕY.F,¹ïºì©ì+.`è0õ€³U½(uòL׎¥Ñ±'¸—+¼è‡¸dôÒ‡ýy:Ð^²'ÍÞQ«ö°0—h®çp,Ø=ŠÀ€Ù²dô]%êÇ5`Ik*š}5![³ašÉ_ð]¸ ¦Ô(|×&'ØL¯qäû¬Zt7ûå'̘yŸ,Ù£Â|Žâ¹ò˜ÛpŽƒ„|LÕ9;²þ±lŸ,ì½| ºm[ø3ÉÁµõ0f‚WÜÉí½Q„Y…Èà&wru‚-»eœI Tâ‡ÂäÅ©}87þl½ó?®íÁá}X’©›í÷ʯ–ñÏõ¥3n…Ɔ¹Öܳ÷ øö 3Uܵ!…NßëûÙ™¶'TnN"î°}À_~-˜QÜ:ÍpQMÝ ¬HÕf’ ÿ‚ÆžúÃ÷Šé!2„Ë+„Üg·ðÞ¸"¼p@Ç]·%ápj‡ {7ï)>èÀA1¸÷NΉNM.>Áɪ¹)ÿF£‰^3Ž%…#õß5WOû‹MGíðaè_Î@Æ5^T`Ræs…µÜâMu\ÈÔJxwkQiRƒæÐ!zd‘‘X‘‡k¯Ìf/¥6ÓEë×ñ›y‘eæOéyý× é}{i…_6Ùð…Ÿ„ÆR;vÁêÊ*œ€:é >¦ÂàòéÿæšqÙ\)Ú²þ<¬X‹Sà]ã ¦±U7çÖØîv'¤¸s+dæpéŸÌ˜¼°:–]½€É"kQcn0ÎÆÚ»?>nx…á—•Éí=οäl¸íæOèŸO´îth¤}rEIgPrn ­8ᎠℊÑ#ño}þwæ~!z¦<Éö‰ÐWs^œó•e¹4Yö3æ«|@ÆýÜs­ˆš©]_#àØÂ?ÜX‡¥¬*; VôdÀïôb|:° 2¦E¡÷ƒ1 ÿG¿tÕÑwËɆö"8óÅ¿:ËâòÙžàtÙ³QÿË]l_…ÓN§à=Ü›‚óÏq»¼åÙ©µ=4á°]hƒ?ìatúØô«f"Nõ晢í`è·^MR$8à•nhû\EߨsåLŒÈŸ÷…Ÿ«è ×1ì‘"^£9náÀoH?õ®~nc ¨ºßİÕ#õ/¬ý:z­1ÇS)ÏxEÂÅ`¯æ ?Ò'ƒå…SÀ÷˜Bn;L!éóÞáWÏìñVOR[è›Ä#×w}y¹ ü#­ÏÛV¬E6¸Í`•‚»yvZ×q}¾«˜7‡ëX6ŠüXͽÈ`“KtàÕá0ô¨ÏÂä…»˜‰ÅvX¶[=,Ä)JI$níw´™CF-ò¥Ooz¡Õdâ|9†(+HsA½2lc­Ù,Ÿf—f zï]îêïáºý½ºïn’ÓÂçó àê|3Ì*:Çß“?ß|õ&ª&KñöÉõÎ!ràŒÝ/^Ve:=Ö“Hfþ\2âÿ1+RW=+zö¨& ͘¯Ý¦²õ©cáÖ•S\˜£Ó?õp/¼äN|hÆôÎHþ 57. ð &ûõc€œ6É3z­;®ÅqwÕm`Ïvhµžˆ?3ÿ§ÝÛk…M›øŸEUaph½Æ¹ãŸ0ÐÌ‹£ øÈx2qVË=&L®«-&‰\)z« öBiÎÓïŽ'›EC¦Ò:öô²-óä.Ћôp:wÅ émx¸ÿÆÿÍMŸœ0ì/lÁ7=•¤ëª%XnÞÉîúßbÞN‹Èè' 5:™;óLDß d‚lËÓfÜ›²… =â9¦Ýá±?’(­žEt—¡ÚµKøž–šï„·³ `ýK›á–×0´."ÖÊ3ë-ãXXò-tÿaÖÊN€ýº‘мú '˜ôÿ»‰»E#ö§<âÀl£4÷m²®9§=÷M˜³WÌÚÍM‹Ó„·™êdu^66Æ‘¡­lÒÙì? åеeœôÝZ°ÒÆ¿J#¸_~ 2“*·^›ýÎóMn2x¶å,ÜþnÉÒE­Ø)C­Ù‰—Må˜Í¤‡8‘©“¶¨!SS"¶{’i‹“è‚Ri0ªÑ”hL}7i€Ÿ\ Ì¿¯õä…P1±ÎÊpŒxa5ëážæÂxlþ L\ªOÚnGâíj,úp0Þœ•ÍJfúRµX?N§å<ÔœÑ&ž[dÙjéÿ=ÿzü· óÖqÐøJ“tøM7ZJfßxFÌ¥ûÁTU“¼>Yg=mðX:D§N„˜£Áþ{:³Þ›‡ÕƒÆÐå›Ev´ÄaÙÞÛ(¤s€}ù<O¨§y¾°Ž“õƒ=\èù`4pùÞaY(rÍísÓ¸ë녹ƀ»œtñLY±“u4û8Lë‡kÙù'ƒô_O4·é&6Y ¿ŽãÎ#:ÜÞ#p ‘%…“’0FɆÄõÍ=…êšKÉ×lyÖºËïÀ‘Ïð¾ï7ÊNü _ßCx·³¸ùK”Ö Im|ܯðN–`ÿéK?²ÅüxŠ3F1›¤(ø|ùUN‹äxvã£@–k;ƒ‡¨ðíúÖÇmþj¾cOÀTù½Ün>-™@t¾ÕøßüÑV4*H¤¿†4É,gM¸·2–yʯ$kt'²ßî’¤Åz'ë ™CeìA˜oCŦOQk²àÌT/Xñψ€ÅtvÿêÒÞ7‰ˆ\ôƃ{x¬v˜K®Ö­ãâ'“¯¦øÈà3Î Q&'}èÞ {Ï·à€üx’üŸ¾äƒ)ô’ìUÖ=Uû®¼„å&y>‰O X\b¹&,<~Y›#Œ£“L‰fÔ⤘Àµg˱%ÛNÓ.‹ØöoWQoB4h¿åÈ Ÿ||4:]=fBµ¤ ž"÷‹Ë¸Q—×r¡Ö²žK»Fò?o·$:XÃÛžîþüþB:– B¿KZ)*•u¾£º~å;9ö£ù;ä>!žZ̪.š—4Ö¶__Ê{uJÛÏàû«¼Csţñv„ÿíÌ;h[”ˆ¾d Ù<ÛgL÷ßW'›ÛžÁã’l–|[=eÉßõ)[dNfñ÷€‰Î àù¾¥³«Á‹…\qu%-\wŠÂ)/!¿e[6m2{à"Cö‡ör<*@jͼøráq\îý¸ùx }ð|t}\±öalbö Ò b…gÀ9n.¾´> ÅE3X'¬±ÿ²ál|Ø ¦7¦â£g>8!Ü `÷oeö1en­3$›z§“/“÷“#_·bîƒT˜üÑŽ):ß Z A”Wà45ïUd»j&Ñd-K2JÜ„p>(K<§o¬™Höë†M6äË„dÒº5”nÚSH2…’MB$:uß0Æù†“¤±^Ϲìïb¤áz:u“ !§OþÆ_Ë„ ÍqÆ”@Ž˜z…§HœÔniälT\)Xl JͳYPe"lŽÍÄ^YeöbÌìm¸Ã[õ3†ùü+æ\ænrþ'ûb&OduýBIJ8†þøßú‡‰¥ F,¼AÛ“ùÿVáVÞe‰4¸6ˆÞ:D389foM?œög²k&°¬µ pV©ÛòÂ?T'C¢U&\wA‹Ø]0þãOPÝ}æXMDYGrÞÑ“ {„˜¾){y~ù{ÿ µÜjO×ìè†îUÝ+ 3g€¥h î[›óúAfŸ6m±îâ ^”gév¤õJ  Óºóg D¡ÍΗüÉ»ðÛR–\–¼Oß›è‘%Ïùªœ”XÝæ*®áÞè …£þ4â`<*¼‘ãÝÙu„“Ìq¡½0÷þýÇÿïÿûjã«`Y8£—$`Æ…¿!?å=$IâÛÌû¬* vP¹˜OÐ;ßE\:E6ž=€ÛÃáÙM9’ØY‰£½š9Û“:Ì'Ö˜l–&g2{àð„z(ÿ´ 5½z8ygK¶#; {Yø\ÒfzYÄÇå4vMNÄm½OЪe4ù^çÎ=ÛÖä¨àQCÿ»ÞËJlôNçÇ[/`5J1¿¾E¨^í'î ±àåg±HU†ùl$Ë5cÀéÏ?p™¯NˆýÖ´¶v¥¢­Y<;W¸:ú}X›I%×ã#ï&ý@¾I¿´­zäùßéZÐOš!£È7M(ÚÛN³M@°ò"\ÍYÉì6Hàµ`êþ­^¹ŠòS±?¿ÒB];6ÅEˆ”rijf1û9ù~>H{©‘#§Ò¸ªÅ”ûüõr‡Gd¢û*—¨ù``Ù¼yò2¨ôDÃð߬bþ#.-IZžË+Iè¶E0é©3Sg)p&ÏŸ ÓŽÿsnìFÜPµ—ÆÑ÷f#µŠ6yÑ|Ùíx’Ÿ§÷7ØM±ÝuwüõÂDîL€{=Y éPþª× ¦; ˜“ÑWµÆ¢ÉàHòõ-qm_$æ4cêqXTüõ×WCùS*÷‚F ÅÀñkþìÔûË4Èd"µÛð 6îÅ0ŸÔ¶-‚Ãs yñ s05§>åÌvË<€‡/0ÏБ;ýÀˆKSÝ'ö1]3*™y­aÇÆQØr•«1­‡¦€Ý9’SÍf™É;PÍ{7vÄIûœ‰ú«¸AÜŽµy½&J“ ˆÜ¶³#ùÿø½XŠWr«QŸŒZðV?3!J¢7aÙü}0êZ >bñFFäMa-h4Á¡Q‚~'@|îRü—/B^%œƒ;»Ѥ¯ïhÔÔHH}ù‡'Ú+ÇÚ]ÎBË;Âö½¥¡òsà2‹Þál½ ‡áiP™çÍ¥gôS™µÎp9î.çäU Õ§8ÇIµâ`ëd\ó¬š šÛØ!@¤öâ5ô@×QÔ»¿¸¶¾§ÔÀšØOiÅã› 0ZËLl­Èš€5TÅàD¯”c×VǃÒjGüV¹+¢á»Û~Âßö{a: ÃãGìlÆ)G€p[a?c„„'wAêxä-3`¾«|à¯V¬Ç2æóH?;ÅÊ%Âà÷˹\w¡åÉÃü+•Ë,xÛ‹ÐÖ˜‡Õþ¸ùQ8ë·þVeZpèñèe{È­š\· ‡;ðn¼uZŽ:©bÌ´¤UÏ+!¹5–-Lµr÷¨Vìa4ú‰©¦Çp’Š©ílÆ«|0 ÝI¾8ߢs‡¹âöÌ0Ù_ ײâð~ö$²’Må¾ý÷Œ`J·øA ¸æ÷-8½·¥[³éC* xQXó>Š®EŽô¿õŠF(åô c×è5}A¯‰SG_iúÚ'NW˺V`pƒ¤ß[hä¿{ðã^7XEýt>ôÄ‹:y/šN§{¾Ä€è±aœyr.ôÍÍâ²Ðz²ñÑËâÓ Î˜°4&&üÃ9Ü‚ò,\²~<ÓÈ_ÆY®â/3:…-éöLÊöÆ¡`ÐraLš&ûWÏþ5ø•&ð&[I|¼—Á½ÍéP¸WžØ¬­ ÚC©têÓðìM%Ƶ‚cº(IZeWµ?@hòC7?7ö)ÏË_«ƒlãM ’¿lÈýµ-D ËOÞla\âûDzË´-µwÔ—‹°p¯ ÜuBÏEGÑ¢×X: °øÎל⬯tz¸ªdlÁ)ù7Ð[A€<½èD¶]úõ“¾rÑ{ŽÃ¹¨ê›t\µX(ˆ³Û?ÃLûyøçc)åõŠáµÊãøåôt¼! §Æ áE«¸mEãÉäºz½0[š¿ÍP„…$|ÇóvcÎO<ŸgÉ—¸kK¼± LJ­Þñ,neã»ÆÜQ.AÚ=þÁ¦_ùÞ3ŠŽŠ‘}ˆëÏ\º¯¹|Áœ(tŸ‚_3_þoþicç*ï c´õˆ¶Æxœxz<©é¨d2!ëÙö–&œ½s£É­{ ÷ßi³â"98õ(_5“Òå›ðñÖD..e9™© ÇŒbéù,p…:³®Šg7Û/ðOH_‚{qSIÇ¥ƒLw†ù«¤D¦¾4枊óqê‰3¬f¼&Y‘Ö@ã÷hÃÃA?RàYÊ¢sæ’„‹WÉìs[èŠ)bä¦ånôV’Çi1|·ælÀð/º}4#>Ù§Ðqn3¬šLDœ]¹ª õøŒ3Ô‰Õc’÷ÏØúÇ jGMõàH«L%öÓf]‰ÿûþÇØ%c8k.?†×H„öË~ðÜ;•|M”éÕpt`­ÿdGÿ)æÑ”yjD×õ[ç™ ‡ßЦ 3ò-5ž^¤¯‰Úiî ö@jÔ3Z–äÇÞV²'Nœjòšÿo)9ùî3®~=ƒ\Ç•Ž:Ïν£Ç$Å©«éLvýO ’6"WÔ¹SâYS­ˆ?YÇ==+ËšrÓ¹ÒG+°ê„*sÝ9“4]Áѯðô]e˜j–;R{Ä9ðÑp ï֯ɞo‹Äe«N³£ÚÚäqáâáíZ}؃üs$>Úµ¨i=‚ÿMAÉc›ôΖrÆ2 D¾F*_;‹”¦Aû¢ zá^(®UE6ÚšºÞeßž<æ¢/^€Á ŸÛ™æÌ^l4ûn]ÁÝ\ž ;ÀMïlY(Nþ-óÂTÝ×èÝŽŠŒeo/Ë“ˆÖ$8d¬AÄ^Ð"Ϲ¸1ë:N‹Z‚ÕÛÃ9×¾pLíÙÅ}özA_Èá{oÄ¥·É¿OCÈó¶ ƒÁé†7ùs¤/r-«¤!Áwˆöη"cOÁ3ß ¨1•žÞd‡9þOaUõ3´ p„a[à¨l w•à¦ôx#¸Žá½éÿ:ÝÓàö7irõŠ"Ñ6:ƒR½é¼åoaJÿ§ˆ83R^ô6¦ðnö³›' ÉéÕۀƒ·Æ9¨ ¼uUăJ]:M­÷Cœxntÿ GÅ 7ð<¸~œëk3"OêÅHóË0nY¸9 ZƒÃÛ‰Ó._h¹_Èí>˜Š£ü¿ãN³eÆv>ÌÃbl¸Ì}%`²-_.âŽãŒ=Q ÿ ÜýïŒÄ†ò~|š½BžiÃÔÛÁ¨P,°ja(<£'O&ËkÏsã^O‡ÁDä«u~t÷ lÛ|ß„«³ê\o²Ù]mpÒc+n%€ïM{ògáñ–^È1*†Î¤¾$Z[|Xž¢1Ù³|[`¢‹ö:cW~»¼ ¼ù| &m£>¡Û˜ûÝ𬷅k8âbYu.-6šíåqU"”§í| ”¼È|—´h‰0ë³:Ì[~Oþ¾¶)ïá~äOKf¶u ñg;“÷“Y½”9Ôç;îC÷Ÿ,ýÚç`õ¼N”K^/Ö’1øÒm'¹ÿ«½&EáÜu›yÿ,„†PCòg±|Z熿²_µÔ€æªV°;ÙÀY¾s„øBÊ'_8ÕŠ}\à˜µx 7bf}Û.bäY{•|X‹…éÇèîÀɤ}ÆGîÙÀÎÄ=Ëù°Æél®Xż"Çä'CëÔ)8_Z’[V‡woõbŒv8;a'ØH.&Ñ9 èÖÝh—ÑZú‘·î©쿸‡ÈÙ©aÉoùåꞈf€Éj>ôC„6šOþ¼Án¬3W`×Á«ú&`Òº™øvC<̹P…ƒçoòçMéƒm £éÞ»n$&ÿ5”r5Ûè¹—‡Ðé‘k~s˹°ØkèSóæ˜s},_™ÏÃÎ<·îówððh‚I¾zÜçÛòX¢W‡9~-*xÄÝÇäYÑxmÔu$¶ ¸FRÚÁëRä ÿ³ϼ·ÍÏE’i"Kè±íôÓÅz^›m XXÓi‹aŒéM°ü!@¶¿UÂüÙÃõá— f.š %…àŸaÈ‚§Žô?)-)Þ»Â{ð®»&ÛTÒoeQeøkfùtî ²«o±Îû =˜Ï J¸å«çZ%Iô_…Ëv’pI#’ÒÆå¾N¤Å1 j?Æ‚HøZ½S›4,±/R@âÍd”™M!âÓuÎhŠY–}NžÎ_,÷`n–+ïv¤)½{¿„–Aù.a¢?}÷¤;’g·ø0xÇÑÉ_ùÅÉqëpÞQ¶ÅVßb¨œáÇ\ÕuA¡ê >ïwÇ©&£Ø‰U¥¸j•=Ù0Ç=ôÆ{Âñ¿ë$vðaž¬*‰.žÀælß:ÿS{Ðhf·S¶Çp‘Ov“Ž×ꬆ³eÊš¥¨x7ž[9ìSSòît½:ˆ+geÁ·…6Äßõ.¬2+bçœuˆOg)¶»‚m3•HbÕ~î2æ%ž÷¨‚´µípÎ¦Ž®}a)cVðJ&2~ßqÖAcû§áÎÖVbÔÊ»ØðèvƒÜ ]ÂP'áS\ò‹‹ðÙÉ“T êY먧š:ëÂ!î¸Ó ¨|'ƒóÛWÁ×=}ÜD5G²}”2YÔçîÖ™ˆ ga¨Ôœp<–;c§øD„|¨?€'·e°õFâß¿õ_òø"¸¢ê–?¾ÅNK†ÚD=¢ù<'.£»»½_àê„J(X©ÄvÕe’þ9nƒcZš¹rý>ß.M‚®ÅÓ$>ŒM‚‚'Ç!ñ“¹UÉ›4‰\R› ×wëÕÐx>x4ï8è»ÏðiIÙ8ˆ^%PjŸíÖ¦>$ÙØw.ÄUàçlô 69°øÀ¹´3Gž]n¸Á“‹¹ËùÌØ‡ÍÛòyÙãFðßh¯[4øH'¦eÑVžX¾×be¢aR+n|3š*(ÒùíhaÒQß)>G ׿;ÏÊ’íN» ç‘,¥! ÿÍó÷Ÿ/û§ÜÕö6 .yŽ&uùp(* Ç8” ãçXç3ãly´[8ÕC8º¬†[Æ3‡–øèÛΟ+R†M–›gÇYÇŽ­² æ¡djv `¼š]}í}°è8|í,õ ²fQ·Â£ vg8oÃá¶Y¥ CÒ÷4ã•`8ÙZˆåmˆO_|ån=•árÇö¢p ]SòiÄþ¸/UÔwq'„d*±IñZì³ê>ê{£N-ÈÆ¿!jèdUÅ2³É_¯á¤Jl¹ÆIüxé%xl{¬Ÿ£ÖÅ_}Ü<’!ê†2zdX@¤¿ùž¹ß=6$énÜ“¢É¼Ë5ZÙeÎ\Þ«Ó+´™ñÃuÚŸxg÷~ÇS*Oéé€Lœð‘óÉSƒuRñN  3»‹îÖ 'I†ˆÇ„£aÖØ?J½î ¦Q»ÃPeh>®ðˆÆ¨¼ÛP”›a ˜™ÄÎvp:ÑÒ,ìç0Ö°6ƒ¿Ççã]<,8lB¾*ë|æ;²þ×ç»’.2KÅ•™˜éðþúÓ9>ÍpD¾$l àö[a&px*¨À{¸%îJrÇ íoìCr B ½ ¹‚ÌyöìóíáZýrïŒþà3¯ù¬©¿wŽ~ŽákÁÉ_X\xÊmËN*k¡k`†žžD"v†pÍcJ ÕEެ×ÌCa¾X>/˜tŸ… _ö;ד3¢Åë‰ïýoð)í+ïãâfxÖ¤H”K°äz >B/fl•еgç‚Fñaþ@ð>¦°± ÷é¤ sºmlðÏQç ãp«è+ܳå±Ùá0RÿotµsºÅJ8j½[¯ïÄûõ<˜ m_GíƒâX˜Ôú¢nxøþ!'…ZÁ(ýCô‹Ë6öÀiuãðF¯ ‰ãKcÞô©ÌGóRvË’€óçðÕ™‰ ¶Ž{VÀ;»KˆËùqŠ¿n­3Ý©_ÄqQT!}ëÞùçóàøÇ[h<Ìvº¢ßB¥o,&ž€ÉŠ’Ìk»ìݱ~¾š†w–§ÃÕ±h4-ÌÌé)÷xzcæU𵤔ÊWËàÁÕD’P>,Â…âB j"9_0è¢,Ìù5*–ÑÕá^#ö›Ývùà|\1}÷rèôN+Eu»˜Ëqû 4u*AY§P¼Y)K2 ÎbW—8[y4–mÆ(âüšAÓï÷ODþt*±{?§€²¢SŠ Ã]itߘh]BÛªnAÝËùdTH4ÿrª9ñ •˜;¹âH,ÂÞAÁuRS³íÝwŽwqå4®QZˆ™~.†æQTŒEÃx±ó0˜HçVåÀôšj4Z~˜¿þÐ-¼™¡Êö߈bõÄ0ü¯ ÆõØ“ö¨Éœó„vZÝ,Á:²Ã±Ûþ#ŒÊ#g÷â”ÚôÛø|ÄþÅq¼Jr¯³g%ÓÍÈæ"Üíhòti&f˜ï BðYbê8B•Èͽø¦ô#í?`ËŽ¬_ÄøûMaIE9¬=wÄ/“)'Âàº~<›ÿ´^MÊâ›MH¦/á86-Î$e÷cûW[×â6Ÿ·mAQÓ$¶Of1sU›Cè$±;Â¥¼[›Pdß ü{ð'Mõa«3½qM¢>{,n;’¯"¯Û”\ªn€eKjq/fâˤ»´àév¢YPD\\·P÷M©àPÃûÇC°½&EãàúJöðúd2AL‘Ô[@y³[ÞFÖ?n•¿€ôM¼(a-"Z¡D¶ÞX‹ýôÉ´Ïh²EÑpìð¸¿w§ã¼®,Ü%ÇŽÌá²uZàó*G’yH€»ûׂLŠ©Ãá",´ÙçÎ:¶1Ë‹Ø(ê²â»% URé<ÑOÃhxÛ+FΕÿBÓ£žØgÒ…·Ú­ñÉ]†³p^­•4úx©¨D~È®fY:oÅ–e·h?oÌâoœ·ä0ž+h¿0(d› ãq<ÈPaVÓ&Àá#¾ ó2‘¿˜­$RͦÌxÒ,²ôÛ=8¢OÄ›£Ýá7 Ñzl‚…¸°Ìm#öÏ+ID|¦I/â«g£YK`<æ7—CÐXaç“/DÈðñ0%Í neGw%nÙÞ>Öߥ«L¹üÓÐ:–ý£º ÁÉÕÜ̱“Á§"l“žq®éYDë ÔíýÇ­_,òóÖñg:ñ˜Ú\²w|x›Þº }m˜C’ˆYvi [½‹»¸o&ßÝ+ÒôX©º _ðáL|Ö*ÈJšöàµ5^ä÷vÒ35šUȦ’·ð 7]ƒØý©G@F{èdÞg·Ã˜ªZ=9ÈzÀAZ 2C3׸h½=‡l‹ä‘5'”媢zœ#Éã?Âãµn°D|Q^ì /‹OŒðßÛã¦Cw‡s~ìBÃÖÑøfØ×n¹¹~ñé0{S-÷«0DQŒ•k?ƒrÞZn™¾ý”„¿Ç§‘šÌ6¨?u‚[äüšo¤¨ÙÑ )GË eì=ôº£FêZìX¢‘$”öÇróÂ|èêé8½å)n]< µ>s³“hmº–Lì„Åfj;š ØÂ ÃÜSŒT ø²–HCš,gûÑÈ„ûÖÆÙ´Ú0™œÈ†ÊµãÉù7‰xÑf»êPŠ©FëñþNSÖ=·Üc5ÉŒ`„Ÿ¦œAó~&ß!åö¯BŠ Bþù¿º, -âL+A|S‘‰Wʼnî¼P8ªÿ=huÉ|9SPvÿF§kí Ùºû nÕ-|Öï6ÙR<·Berë¾-÷‚£s­9U6•gÚ?Ÿ†qÄ5¼gÑ Ï ”*´,ƒü—ǰdž‘ûšXvi0[…ô'ÃÔQc@zúDæqÉfÎp`&)ë@d†l8u k±€_äý¸¯¼jÜGWÆ·ë’TwQ¦Ž'!l©Lg¯¸³ ’·Wæì&P~®M—`g”®ãÙÆÕ\RQ1?+Î œ¦_ášýïû—§àê!u”ðüFƒ_D@L¾ ¿§‰Å樒_¦‚P° 9V<} Ñ>ršN0I#;ßL€gª8™ ‡È…Õ'A%Ggº ²HÁk¸nÁxØgüІ:ÔÃ6]˜X’OÅÈù1¿a®è&t*•«hÄÄMìßÂ0WÄvTâ[ó[0ç© ë9®@¶´ÃÅí…téã4Ó d'¿­Oé“nëÜ›‡˜|ÈWý›rûù>ò§Ç…èï ‚»üø}·ýš 7¢SAr³¦kì‚í‹–“Ѐ DB§™÷J ¾©"/òƒFün—² ¢xß·Ë\4rˆ¾â».ˤïäâ+[8¯¾Š[27<®WÅGGžpjrþÌߥkOqŸ7h“Áw°x®©9—ŠõþeP»×Hí²3ô¬Xw®Á¿5ò‘KÉhXœõ.ME,{c ù³,#+ÐýË?ð­ºF=ÉIt\0;±–ûÚVŒ±ØüOüšgbÀóÏPÄà m%²/U…¸Öh°o“`hóyø³MJÎTÒÃÇå™äÎfzxU.ÌâòbòáÞ4vˆýá¯,ñ„ÒÝ‘øOO ‡6GaT´–%:b IëÝ÷xèË .sÁS¸VΓFÓ8¯¯Ø§Ö„úbJ¤ÄVˆZæÃŸm2ê“%ÙÓe‰¹Û+azP<ô•ç‚ÌáU)îøììEàx~°G/=s² ÿ;&9m% N.‚ ÜáPm\Tâ¥æó3TÄÏoÞŒußO`çè34@W“U-"^¬àí9 ÞÞK™÷T!TǧÏU­áæò6|$ó"£Ùk1gsš­‹ßÛðZÅhX°‘(9û‘×bw¨í¿÷u ÉZpg}Vå&æŸ`;Rÿg?ì…-gÚpT(î&,…ÇÂìì†E,ôg°à¶6vh±X-C¢e ؉.ìWÅ;¥ö¸f~:”¹ˆ“J^%ÞØÌtùÁŠÃ.piZ==%aĦկã…=¹ m%`­æËþj l½L,3òæòézn_Óò·aß6œ„‹SR™]ð,ø“ªÏ®Åöq'[BéíFQ¢Åç47'ÒŽC·fÙHüÿ]De›º8^Ÿ9™{Þ˜‹øcÌn¬ka8k×Å=–~Šf‚eÐá­M”§ Ëì±pá…¿K†åÜË ŽÅgþÀ±ÕG™ðv›ãÇISÿo|§$âïÍAÉît:pM—ÜšS ³Ì¡84Îÿ‡ÅÂÑ0ß@’Ä¿Óå5'öb¼j:2ôØu±þyð|H׫À«Õíh¶[=ÿæûv?iWHÒPùа¢RÃQÚäH±¹ûio:¾ànåõ¢o¿ ûâ¬ÃÖÿR"›5ÞÂm{ ¶ëq?lÖRGÁ\ZÒNïfü‰ÿæs.6/æüÄ•ö—ðmã;||o€—í4k+öÂÉÛm øâ…9ãý/’¤m»*<÷ó¤݇çeéÎý›ÁÅò+Û¦O{­¦süAìv#“ŽÃÑcûèa±­¸4í•='„~¤ °~§ñÄ”:Hmb§¡þ ¸Ýj"÷&ææ¾rG1VÜ"‡/\–íBð·>)GÎÀØJW:hÆ–ûÄkš@,…mk¹>1 Ÿdhô½ÐìPè[ÃUž]Œþ?¹M½ÔEJƒ¿Dîl¶Ï’á>lZ^³£&°‡.€’þ4®ïð!Wðgµ(ùþÙ€: Ž!_¢:¸ˆ‡ p˜W…šqÍÑìÈ’Œ_o鹃Âôqþ=”ͺ‚ÖwÂq‚õëßÓüïÜœ iÜËûçaºßÞ,uwÚ>`ȬØW߃œ°Š8xÄ¡ƒYÚ‡e£Ý3f45~לåÞ¯{‚µ£èú-øÏ#„¼Îü¡Û óL”e*ypfÕ!Œúý“ÚìzŠ™‡WóE#çãÄûËx£¾—ãgcš2Z†íõÜÍeFçã ñ†)K¦ã5*ÛäìêCë#|’p¬ðZš»Z ’S`JæØæ°Œ8«‹³’¢t~Á| ‡v…±® ± {Qý¯6‰~¿~¼Ž‰‰ùhì*Å ~þ†úÄålÖ÷sÔâÍ|vQo1ŒÝÃ{=`…¥¼óè("Ê5}EŸJŒæ­ØvžŽëJ„ Ÿ°Oüè^Äv¡HøÛý’ñàR?¶¡jÙ:xˆ·DÓKtÛ!wñ(Ù¾CÖ®bú sIV[2·Ôo6îw/$[†¸:…Ï0Ã0ÿi‘ ÇñDQîˆ$àëÁ9$½J«¹fÁ²—/aÉå–lûŽPu* zÚ1[¡™žy ÄÜ?%0¯ÓšØýɇÊp¡lÙ¿™ôt×I˜hʶ>^“îåòÿÉßÅãùÉ0wF Ù?³Å¿ÙÒÁJ9ç¬×õtâ–F8¾¢˜ì?|›´2M½Åç·±È &)D’\|˜¾† r) ?× ‚CõId×|rìÙi¶òáqTb“ÙÔn÷‹+(ºèïÓ )Ù[ÆÒœ‡¯2jÁj Œb=ÒñQR7ïýn¼íòúº.€›+üU^\ëgÜaÕ ¯ö¿Ã†n\ý1Î;ð‹Vd€bö6öÁaúÀL öÄ)ïåè>õ(Ðl8[K®ãŠÐ¯tÆ^[.B|1{}Çú'éÓCzgàžPv¬K!­>¯Ñ½O”ÍÛoÇJb/8OÑï€ ƒ\²ÿ³n7³¤ëdh^ÎK¼f´ ¤7À}ãêPz[+UHºV¾³ÐlQvy×1úÐÊ«"«¹ÈA)(Hðe99:$®§–kŸ«ÆŠ#JÀJaßeº|ì Ž:Ë ßÿ ÷}üA¸»?t­¬ð…±=<„*à‚€÷Äp;8Y‘… ɲ'׎‚ôÐXR¼"˜S½®Îÿ|9kn•Ðoï/ ýµjÁ((•­ ø[­’ë¥õã½w³0±AM{eÌ‚v£Ï‡_pÌÕƒ8 ã7­Túa2S‰TÈS9ž{g}K}¸uö 8þ¼R†»^§óƒEy QŒÃüä)$_ï.j;~c»rj>ü¿oÏ6Ü3bÖ: ’Ä0Q4æV5‹B«ï]N_eûÇ´ã×ï-ÜÎw2LíÄ!ç‘°'l%òäTJž<÷_nº‰ã¢@¹l!»½@‡¹®^Î2^hàjÁ$x~|ü0Wïç>×·ƒéu=îŸJ)öÚ:±÷ÛËèõwßsÏ^ëûyžïݻ޶JÙÆ•×Ç۪¸uB„JM±#êÎ’äY=¹J+6^Æ`ˆg¤Ã#?iíõ\§ b†' Þùƒ}az\ÛÏv+Š“BÙÝP`œGÆ6ÓM?ç‚f×KöðÖ¦ÿ^íhӃ¾Jðè|/ß…!½Ú…ù]¦ .içà¶¹ å‘[@övÃÙÛç¡~_:hªe¹×¡v‘(äŠ>Á~³É-]Q°|ß {ÓyÉ­fw¸2/[N0Ž è“3@® ̦õ'ñðË%4é[.¨*ÅÓÜPAú8ucp‘QÊ9ŽõÂΤ*7£Â‚±8á"•óFÓ"[®†QäübÿÜf²ƒ´Éãß’$ïãyä&(xñþë÷~ý„vF ^N,jó'’Ýõ0ò6 -BÐ9KŒ<ú9“ÞvL+çkq`„»n8'Ü@½0 ­W%®ùÁTÄðüÎVÁiG^¡¯C$ŽI+®%.¶Âñ^Ró`1Ôõ¡{à M7n%Ê wáƒåŽpV_ƒØ·Bå˜wþäp“^ÆŽ™Fáœà8¸yÛ$Û;pg¼,Þ¤0;Þ’WbåúyÉzùãhæ B‚÷ñÁžÔ°Ì{íSýŠ=¡kÉ^û[°g©ü>óŒSédÓ"ЛîºÔ´1>¼S¢þm<´káAÍ,ü2;Ê\cüì€ÿH!{9¹e\ƒÈf±Ãÿ´ïgföqöè‘ÔìËüzÂÁŒ¦©dÛRkÌ=œ‡+—ƒí¥¤r} ¼?t %|"p ä2åÒá‘0àO$Îø ‹G¸OqgJ fzï`wålÀ5²Î0³p+2MŒ+Ås”ºK À¤n3– :S™°éx»¬ùb>c|ùÜ~'W ¯‚ð5\FÀΉ¼áŒÀû{µ\û²¹$öQåcÅM¯qK~ŸÅÓåÑw›-<Ó¡øMp.»QŽ~sâûg w˜˜cä¬rîÛÜox[0QΣöƤ֯¿~Éc1 ºÞr‘ú™¶”5šõÜcwþ h}’~lå'×v¢›*“¸ioû $? <ÿZpJm5¹í/ôéÊiÛ!tO"®\ŽlÂédT-·¢.ÏRØ’0 V<õ,ORh` ?º”œ'NÃK%p‘£·c\HDAWVqxÞ!± V棥Ráô×ãûpò´ {éÄÐêú—N·`¢–3ú2ƒ~‰ ßIç˜&©Ÿ£¬GÜÕI–lè8~+l`Qö+ØÇÕãÛˆMÌò’^<0{Žû¤ÈG›¶Üñ ŒQ‡1·qÃK4³„ÈŠSšDxÝynÌq-òåz¬+ÉbIÛ“?é6~¢ÖæÏ¦bwÛròåÄV¶(Ê=fGÐbí«ä›©=UÉ`Ž-ù†<ç‘Ënix8á&Ú~Éf/-%‹Ñ1mÐU!»N‘£Î|t‰<‰>ªÊÈåñQ«/ÊdÂ3•J¡ëC<¤¾X ñr4~ó ppF]Û¯ŒãF^ºø×,¦¯a>xKl¢·UÒýò.#fF×n’Þ(~š2ø”è”ϽN•ªäì™xRyð+טǗJ(É‘ß7s$̇ +K•YþБ°?3Ɇ2 ꎳ¶³ÅþrÃÀŠ.h¦‹z£a‹“ýî{öÅ+а?KˆÁ' ¶Óá&q˜Ú nËÃÈÙÆÏ4çhS k ùȶ¹ØF¾Ulî 7ÂR˜¦V67_Ÿówùöã÷LO4ÙOW’ÃÇR ÷tã~îÆßœÀYàH$;›@u8UÎ )+ø`¯É1·&ûWš„ LÆ·7V¶mdžƒÚãvæXÝE&J=¨99»¤®sšF¶1•>ÿêÈÒ: Á« ±[¾…J‚à,mâh‘Ó&SPØT ¥'7BÉGG¬±{Çúè¶²K2Y@;°ïŸƒU+%°ïI>k%cÍVøøÀëWáÄëFö]íEPYÛ¿l¶@ózÛJ…öÆAt¬0üКMÏ­_Eù9=3È5ÀŠØ`± 8»1–1<øç»ê2«Vž†—ëæÒ›R›à÷‡F°­ÆÔE‹ðØPÊÿõÀ£ŠÕð|ålYžÁ½ÐZ„='ÐoX "Æ¢ÁÑöë‹SÀ¿U™|_E}ßvâTþû s&Û5²°å*LI  ®MfTK/bSšÀêQ$s";–0€}ËoCɳfXh›–IÃ;æ§ËÊ:ŒrW×XÓBïzfݹCÄlöÎýñŠ©0'3­+ôßrïE—ƒÔß½ìd”(5µ¶g»8’ØRú>·뺯4K·è’ÀÒj®ú9Qªa)H“'@¡²çc&ÙÒ )tzÃ59Ó»ƒt×ÀC\F +/3¢g\AnÛ Üüú†ÉûAƒ\=+8ÞL†Q ê#%ˆ‡“2ãp-…Fnï¡qGép^ Yöð$ŠÍ#)âBLžärÒ|¨ÇsnâãæÕT¨~qZ¯O¸ýñn…C]ã¿zú¸Û¦aZx„¨b{Ðf¼9#•>㜀ƒ,sÚ÷š_zò#_¸½ïVÀHÚVè^=GègZšFÃsQ¥‚—nûÜýóh°ŒRidžþ)fФïÿfK(¨”¢ªÅ.4ÇB}¥ÓÇŸa¦_ ꊄSo³PÓ) o£6×4è‚ç?a—ÇNrâüsä?"@RN;‘÷+’é쳩yI-ÅÅÐ?‚<Ôþ¥ ý¨úš}ÒàEìõ;É©3@ßÏ'ûö…ѹeÎt¤-Ë¿`¾^ ½c<iÈ‘±2stµw£#fsHý®`×ZAnA†kUŠHnü Ü7‘ŒhðMü¡*Hvˆ-§ç}#àIÖ6ì‘#ßEµ‰§$?ý»5†vŸ–¡v¶‘ëÇî‚_ïMœWÎ82•C¦Ê Ƕ½ÖØöÝüoìó€8@sé ­68šãþ ”½WŽîN Û-$A®›ÇâÅKÁp7±´—á£Ñ 4ð!¯{DH€õ=œW-IÅ£ÑÞ2µ¾ÉÒA×…0šò †×ÀÓVÿl WÄÒ¢WäâÉK äx‘.íMJF—µO9r 3ÈŸû˜½¼JÄŲÿÍ ê„©ªÑLb(E¦ût°ñ»Âþº‡(w³—x¬f¯Ýlà¼nÄ{‹Á,}ŠðçèÅ¿gÛÚfáÆù×Ñæh $¼Rdf/Jc—Eï å;Yb†i+¢á ;È=òT™þºY‚ïžüeÝWp†£§ââ#ߘªþZÜYÑ›±ü”$µ˜”¡ê±ñk&‡Íž1›l5æ‚ÝèV(¿:›æ» çØ€YØÉÔ¾jc2ÓÔ點hºÝÜðü÷¥CàýÂWÀ>^Ü*Æß~'çpíS˜{ƒ9¨{¹ôÒœéßWƒÉnÏaƒóøõy^_¾Êœ5ÅîpDƒ ¸ƒ_ÛàûùZ<à} =d™Ú-¼°Û{!Gð¢%ìs‘¾¶P¥áš ˜|™Ë³j*³hì½öB‹~+«mþ²]“Šõ_åÔ¨w£>úÑ,ªFå“G¢¹lâîkë02ZAëo-c6¢–Ýò8òI+Fï ß &ÇbŸñF†_0*YC]v3gFÊ™ë=‘œeP¥{…À÷Õ!âÁÿÃæ&Ðf-)Â#àIW@‘LþŽý‹b—ÜqÆ.S²¥ù·`C ýeBò¶ƒðžP:>jÞÝ'`gÑ-pLY^ðy ’–1:DÁ LSò¢û*ß±BñÙp;MŠ´½QýÊ_5n=¦ãNǨUè{&ÕI‡RWû.quŒQ#= xA­]ȆÖeáÓibµH˜J­ð' ¾%®¥iì×'k©«ª(Ù¶¦ ‹ÓtqòO‘^_@½SÈO+ArÆ×.åÚ,Ì~ŒwÂ5àïX0u囃Óý‹©ÄwŸÆ.ø:Žþ·0òõ;¶@ +”ÐÂÓ(™\Jò—WýÓÄcW%.pF5ëh’·î¶) kM<¯“ΰHª>½¥Å‡R‰‡Ä+(ûF;—cÕ§*fà±}ºe¹fVuó.£Dç(~œ 'û‡Ø^lã=˜`¶‡©Ùý‹QˆsÁÓ‚¤¥Ë‹>zïæÉT½>×O¢9O0ó ßýu¦6S“ñö³…DF]€„5‹¢é¹dv*GzÂ.]j6\y¡Äˆ¼XuœÚp¶°qz5lPæçÖM"ó0•­|âKù^¡Ju Ü ¨¿¸2fßIvxÏY ò3¥œ/ `¶b÷Ë?ÆÂ¿Š˜ì5딚¸Æ= à¾Ì$æóÊÓåïö2[ÎSxxGÐÅ7õ¹¸¤ò5J |ƒîSËÈãqQÆâÁgdÏÊÒ_fá×Ö@¸àÌ= $DJn‡è™ÌR+whÔ§nû¡ÅÆ„»kåF¦ §ËÑN¡[÷öB÷'/ˆ”£-Æ&Œ¢“Ù«‡~µ£ºác4ؤï®îIÈaÆÏL'7×|aóŸGÐgõPjÉœÿ¢ VÛna‰d(ñMBç»ÿ]cV$¿àØO9Œï¾â.”’`¾ ^Âe6ùç ,ð>„Ÿæho°¢7ïÖ@×S'fëìPz¤Älƒèý¤7Le¨Ö*DIÁXöKWn‰u­nFjñÖ!°–]m;f’ðb¸7X‚õcšôa~> >§#Ÿ•H…Ï2ŒÉ ¡Ÿ–·˜[/£oÄ‹Hòˆ›¡CÚšO"o§ }Sx“!o\ð®òpîcv~£wVöáÖ‘zìÖgŠâí °ö…”Ú¸´?¡in$ÜÏ©‚ëÆáDýŒ&9íö fYå23œú@¡¤¶†ª‘+'>àãÆhzÙq9)]&EªæáѹÄÙ×Åñ¾¸1Õ“µ`Z—©űà?H½<²¨ã°õ){ !SÉïÛ–0ël…¡‡£6‘ºÕYYtKë¨òi¶}Uv–¼UžF”ªQd›cíÈ»½í,–ùñÐtÖ¨O‹<;”Cüž¼¹„×Êš,©V`îYÞF[Þ lji{A4|ÎEßO0Ž+I7 ã²·`öa²Yõ"-¸¹¯üDom鑦«ö;ì}m[up÷ÊÃ_!Úzy {ÜÇ`èñ&Gßæªè37¶d ])¶Òó°âï}àÎú§eLñi`ë&$†Ñ»'`çZT~Ês+Ùâ¤=[`…̯y$C° Ý×¼ÁmÍzh¡>G÷h;j>1!Ü ~œYs„¦ Ãû~ˆU‰Ý_[9CfïØÍÌ:vª‘=llÃwÑu°úýe˜Û¼œ$nЦ?F*è |zòî¡nf¡ìŸ{Z8~÷ht³œÝ÷-Qî§&ºÌá%3v¼€ã·è·/]lk´0ðÏ&ÓS|É‘üd‹þžçj;1¤üP,‰ô>H²”ö‘¨º„ÿñ5äšò5ïf:gô 4w¦ÂÝv%’ Ñ öÓTi¶ÿt¼jIè¾ëþ°"ÂzÂW‘¾)ÄÕa3{Éó-†|?ƒçÝÞåNâ´8[ƒ˜UÀsA]FIjù~àoÉX#Š˜ü«½ûòIÎROP3í`WŸM éõ¦TýùTÊŠÌ…÷>2Ä÷Ö{ØÅߎ>ä`Ôi+vÝç% *G÷?˜Ç6øÇÁð5\e¾–,7:HNÙ/†®ªT?–qÓ¼‰šE‘Ðï(A·n>ȱ%¿lŠ˜q}ª˜·Œ?nG¹YrøÊ\ qo)K§P!ÓN”ž­EZ.n`VòÀnVu×Îå9ÔÐÏ‘©Vǯ«ëþñF)lÛ)A¥–£¿=~};êœJÂç,{gr1R]¨«æÖßQgå¤ÉýeݶbÕRlô¬¸ì¼é»§ 1Éîä@q*tÐ;“í–¼=îŽq7ߟd)O?æTò·àm·‹øc|µJÆôáTкÙÈîˆer:ÐÕ—ƒ!n2•§)z«ŽKž‚§–˜æÙS(¢Á‹£±Ün™HÌÇåÏ"I)äsú^¹ÑÓ O‡>W•!C"6ôÚâ@ìT¿ÌÖ­¹JŒ[’Iëöõ§gl<å¶ÌýÃÌìÝ–âkaÊ_Ö׿§vM,ûÊmɉM&ftí²p™©US…TowbþÁ—Ékã1ªƒ?KåˆÜfqÆhy4s®h9 bz µ:‹ur¼ÊÊ$:Ðʼn©ü³u´™ßa 1ò´Úv ÑŠ‚Ô|'²ã¸(ñQÒ"g>º§­ah¶’Ü­·$s¶›ÁùùÖï6N±Ò‡>ºò ü×fÁõ-D}KóùÅ%Ü•îÄ–—\‡ÈyÓàNÍxáw ¿‡ÁZ×!ì]y–4vãèa~â3d»ØÃ*V‰‚J°%ФÊS_/èK!Š í ¤¢¦àçáhh™· M7!»5ÿ‹â.ö¸Áö®T ï¸oþi¼‹hËt°MúËÈ]aI2,lˆ{ý#HùåYàéÄ)ëæåNw­ßÕk˜)cÿôèŸóܾi­ˆÔé”—×8Tl!ì¾e“ÂÖ¸c×*Ü—«ˆ>|q7YÀJõ0º(ûjÓ\Hó!ŠÞh{Òçdʽ2ò;ž¦À­Sq¬¡ºYøÏE\¥çV|b¤z§ÃÛôræÒ¬ÅË烙°½Û“u¸: l]@És; 6o2ê°åq”ÕñPñ/·» 'Ó‡§—@î6VˤI<#ÃU,%ߨÏÜD¼öçórêª}Ì”ÉÍ ¹YÕ¸«ªÊïq§Ë/óC9pzSâ¿œê ‘‚è¹ø0p¨SÆ%Þ‹áÃÐb˜}ö ÎњϪµ¶çHUƒío˜K–äqHÝ‘ÂÝklñšy3ÎÙ ó>ðP_n“âõñ0iÆè°rÜúږ깬ÀdfRï ~ûò”³ö~ ^#WYã¬gÜñË ïÊ=ìJÇ«‘?q& s}Ãhî¯Ä†­,»qôl1ÂÞl]:‚gðÊÝi´õÓ"nÔÃ$ünøÀ¶·N‹yÙw—²nc_/径ұBÜ<’³IYk"}ZFfÎPf\›cPÏïé™F­ËÝðäÂ&ªt§™yðu»À¼ žˆ-$ãÔ–‡¸5j=õ wàØRd»Ú…¦€•23ñq$¯ †O_\èíkÉpa0çÑ'ޝ¥ÏË—àh–6ýù ö½ÈÔ»úÔeó7ôsJƒ´“qprè ò,M¥ê&$­ú1œ 0B6Ç‘ÆÔÙü«±¶LQüÿºbæÍßO¾°!p{5Ý/ÆP<ô) ßËÀ…ëUéúzUzã I]ÄJu9y²Ø–[3ô‰ç8<”ääí¶€Ý«ß°×G4idæ9X3Óš(5ŒàÀdÃVHSSD/©4tw˜MO‡Gp×»RÛôlg¤»ÒiÉè°Ñ.Ö¦0]u¢äÓì׌ûž©$á±.YÃ`ø45oD1îm}’Ú@wnÒ§þU–´ž÷3{!o)T­ëûÏa›Y.– ‰` 4ø˜ÃØãbÄýmDø¸ÔY¹‘f¿X™Wï Ì»ß×p5]÷’áýLÓ€k#‰×2v¯æƒ÷%vaŽ>á'·ÿ‹ãh¡§<µIfáÎÔ =úëp¶Tª¡~ÚsæþÜX&þÏbL9kNCžîŸžÈjœ ¡ ñ´Ùé¶©} ɶî‡O棰±å/f\õ]N«€ç!õêò@ÑgAD^ý::©Žéý›p]g“Ûo͵v£'Ÿ,`Àr}ÿ¹¬C‹f;äw1 7´Ë1„=ÀK<ö·@¼Úþ¿¹Â`c^,ŽÅc‰Ä?r-½õpêõOÁ°ûµPÿÒe·_À/{=È×#ÙõÁÆÌ O Áþ+nä,¦WÊFÑtÎ;¼øq&ìŸ.×$ûÁcŠ >H}‡éz ÉíŠ"X ¯_­GÕ¸öæ-²®”GNþãIÍ—lêèb¶ë¶,ñ]–s=áÐ?.ªšó…uÚT‚}]&l7:5ûšË(*vØ0«¬¹ëFÁ¤™2l_£ŠÃ“ aÎù@8² Öh­<ªF>{U§š­l'0{æ"2a…·ÞÆP•aZ,M¿•o ª¸ãõ?^³¬I$é,ÈÈŠ­8½$¯ö)ÐÇ7A«˜-Ù~ª{I¢òw=zZg Ù¼"×\‘g^(r0Å×ïÕÍ€Ð>eÒÝ_ɽôNüyt8S|Âvb€1_aÏh+óf2……˜(¼†IoaW2ü¥ ;7ý]%ìôïŒÓÞ3Ô~¨gJÅÀ®ðç­Ôwƒ¿= $íÑ0Ú….BŸ;Î0Ÿž¡áç­™ÄYä^óê>C#] îÖ"ßù¦@›Ùxloå…÷mâT²x0‹#fm+PïÆX%!NlÊÙÚ«µäfØØð>~O~ƒÂÌ,¶¤>ÞÜ”!B™6&ÆD˜Skw?ßDrIé3x9K“ Ñ3°¶Õ•¼Èõ"QNVäð)b’¨OÎívávíD«A°ô¬0>»Äa ûtÉÏÄ™ØØ 5¾_Ø9.J0õöcÚìL"¿šÁ­g*à'`ˆ¼?yh!çw‰k"pù²Qçö|lËŒ£t]x 3i¯O~pÞ³Š/“¨xI ðDíÃ¥T4²½ŽRݬșY¬Á~æÆ`Ú^vͼ0(ÿìL~…kÑÈ)^ÛâÙ æùi ´´’ Î3»Ñj=öÌТ“YX”¶êò‡`üÈIpÈíeº€DÐ CÞÅB$Ã9nž‡íçäÈ;á9xýÉ<òoÎ6q²kÙsÑ ö);¡G‘)HĘâðg_¦èyüY„9¥ôæÂÅtÛ ]Îó:#v¡Ì¸ L?©´ã‰†»ì=õ~ü“v-£ 9c9ïØE ¯QS(kÍdÉÁ—±V_7å– “át|0ªÕŸaFÎZஂDÖê°H³n¤('dÉauiº·P‚ ä*Ððîfˆ¸« «Flà– šó>ˆêµd1Rçüqqb+¤ÎƒÓšÓñÀsÚxÆ–>Ø.„I ÷£™0wãwY…?ÞwÃÄT!¸` §¢Ðùt-óÉã/|zqšm‰.cµVY‘qX½|”í¶¦•¦Ðëyö' Ð¥5ÿ²¥d5;P¨Ek¦8€û•KÀW>†0—Q"B’üÐt$Ç4§Äk´ùI¾ ŠN»­p§”§Uõlm~:W¾3N‰êÑA9˜¼>m Ka‰Ô’àÄK œ‹˜eÂËIÏx àîÂ[F¿+~w4;@\–œG QäšKRÓ²¤>gèh0©,áñOÇÆxyz$Ï>½™¦äÿÞlËЬB{e婚T)[ñðÇñy÷ô…78Q3…9×qo<áü®$ÿ Í|J?Æ@Ç|#n/þ…ª}ª |ì ® ô‚£Kغy8êü8œºíÔFp¥p Üœão,ñkì…¯LÚŽ.|~’>ˆ“[*ï±dN><^×ÉhßX/ÕtÀKž_ågé[íP7݆a ÌŽ;ȧ'g{áôG0{Øwëî€ðÂH|6%Ó6r°TÀš‰±²£_­ÅÚ\Wœx›…êßëqu¸2³}ü0´ÏM©Ónàá¸ìAÁ ô]›ÆIL£¾ëm€åV°Y%øS}”t3¢ ™–êCZe¸[H®ï;Ítö’‘ÛjH6½Gîë¶4 dK{ñ¾õ;<)_Šëü›ñÈû¾æ ǶR½Q7ü2«r§ó3â-íp=iÕ^oLUfÅ2ò!pª@ tí‡Jv»øßÃbEkl/‡Í3Œi˧pÈk=‹c§Œir»1Ù§3þ²Cõ˜Á'Çœø8‡¸œøÄTÞT†ªõG±G>gÍ‘…•ƒ ÅZšDg²îër`—ÁYî7}*Ö¶‘\5†þµðiÆlPºŒ{4KmWßÑ„Æ[ñ¬Ü£sLúnâ+— ©úT;v)5 5o…/¨ôð2^kø ³Ä?ïñ³×ñ›½éâ"­åÈS½ž±_ýÆv\R‡.ëIƒ=Š-\Å\z!V—L7íaNÙÃÖ6O}Ëˬµ!oçl«²{aɰ9y·ø!„UkÒMOV0®¿¦Àü›{!7ýUónk†y»^‹Øû‡@¿yÞy-O6„ ÕâELx«<Ù÷Ffžg4dÄhýS¼à÷?Ol€ÉGJÈ °£.QxF×x_i‘Ê÷™¼"$$îþ*8ÆÞNZsô9;&_ÄØo¼«à»™)±;¨Œá«IKs²mB9/ÝðÕ„&]cpÎam2GSŠð¿€-Û†àÙÃ¥àùõŒœRÏÂb¼l,B¾d¸Ò× ¨Åœ÷°&ÀBnH±÷¶ÆCÉ× þ}y3_v®a›I’ „°ÈDš8¯HÂëÛ>£Lè8xä¦úýã t[ÌA¸ÑDöøóàVWÓÿüÇP«vž:.€;£ 4sz!ä –Ì>*]Ê1¶Ç1¬ñFò¨«†[ò8„}=;F¾¯ãZEî[‰2|+a·oâù5ÚdÔb1ã·Ùfî?Æ•Pf^ø-©©@nAâÖ P ¬7}€Œ¾nXnKTÇpÛ'^x˜ìÁF‘8¡“ÆxDÍfþ›Só×õhmPÀêÄdÑM¥`Žj£9K“¨7˜“™_ùéIimɤ£ö§áñ¡z ™3öÈzR.I ©cY«Ad‚_Ò§ Ûö¤¸êXGšêY±ùQ¬Á"/d…¥ˆØ=Áa9tbãLòóúU8º|:Ž¿q'Ûî7Âq/zÙ¾eÅžÀá¢ÛÎYÓúˆ“è=¤M”‹ÑSûiýßÍÔ®g ikqášvîÙ»å°,·¯Å*r]Ã|éþD¼¾ÃŸêîXOd|’iªŒ5q¿BÄï¹þ»Áy™Í?›}¶ê(yPsŠÔ¬ä¡-6ÆtÉ«òá¼6¾Š¶Æ<©4î"=y2Ïí¶]µàî))e»¾Щ:ñô@šáß—I缎' /’à'ID_I”äG”©¬‹ù+"²÷§½Ìyý]1ëã:Ý×À¶fà*œKãX¸Ì‡Ñ9˜~l.¶^ ü±Þ V›ê9©½Ž*¨®¢'›Ý´mUÙ`|”ÌOÀU×?cÇã r' <n£•í*&ÈKÒõo¡+ß’-r¿`eT½U>;-Iã‰pÌî÷£“‘÷Q0±Ûv%À9mkõ”M•2gŸõŸc܇_2¿ôêÙMS„éß% 8_ ¯©†»ýÑÐq¾ yûÔþiLA¬z9Š-¶ï9Õú½>9ïšÖÙB„ˆ!U×~CÁÍxP]’ŸâÃËÂì§x9:òÜ‚®µ˜ ç^Ÿ§¾`²B‚ÁU%ÿ=ËÆfCk¸O1€¬<Ù„øvÓI´þÑ'-IÀôzÕéÿ‹ÇéüùsIÿ,¢»5:®GÞØRNÑEÔì5 QæÝ°3M–þçËžoÙƒd£ ¯“!.‹f ¦î£Gìjp+O6( O2¼!Ñ*¬ŸÃqœrµU¾åÀ6Çà¿F‘š·É’™-Fø`Vþa!y –©Úpëš%XÌ«d‚dÕ‰µås|àù¦ ÎÃÒű‹ü/ÍTðu*‡â½ŸJíwA¹)y\•¦~úx-sè3ý^‘ EI±ÀkBF¿7<ÛØk?Ø‘ìwìû—0ZÇÕ&ÓpƒÏ~¦ã½=~gÏQÃ[òJLÔenö„T G¢Ð=Ž˜ÿU¦.PÊ^§±Ú"CðéŒ ½£û“±Ä–Ýûm'š|ó€‰0j<$½+3ˆæÉ`ºÙs¸™ßóìpZNä×qo˜þmn{à†±NSˆèïÃõ8ÎÀµs ¤+ ü~D*À“É™—‰ò¦è¸îLºÇuÛCI„é6z®Y’ýíG˜õá4ÔˆQ»ý9xVÝ ÉFÁÐÓMdðc<Ñ>„¹ÇLÑài È¿ØGIâ2eˆEž:‚âù[é=Ëûà}LðÌžæŒ+ʲiÞz°þ§WEnÆP¡+Šôá±{k K '’‰ó~1v÷)”›É²Uõhû‘ ´àÑ€mR¼ $¯Œ Ñ"š,+1=eˆmÊÌìu©ôKqXoÃèx5}ŸÔE ºOX7ÞR¸ë(ôP?¶-9ŸH¿@¿"Ü:u!þj“ÅpÉìÚ¦M?¨UCøBE2+á°o&æÎž BñpH‰VJÞC£ò\ö¦Ò æàÂýlO‘!³imFßQ†s¾Øš'ȸv,¼ðªyŽJÜŠCžR¼úô!ØHêå¸Å$\oüºFßÉ2Ðûý²Üù™‡®›­ÉÜCÅ|ò«à$/ùËÑ\( Û”1ýsñ‡_ÌOß}´e9¹fwwÖ`àÞudi½£ž‚R*–ypaâk½ù ›7BzA•Ê}áà÷#¿1qývp®H&»n™Üš­@è/¡×SqðM$}÷¢üIZ7>ãÓÈÇ)Rdã2¬ŸEüîÒ¤cQÐDŠ>Ëã§rF`Vi -¶ jQ“`&²ŠíL"= ØÕsêÁ ÅìÒ|æô‡Y$Íïéóž 2Ò hîÛú þ/#Y,ùŸ—´ŽOcyùé~+S˜”³¢sꆘ žE„g$ ‡²ó˜6°ÝB׿$Ò‰æ Œ>ªJ—}m¤vç¢É¸Ð5ô‰õCï`Ê'œD?Ú p÷¤’#›ŽPcQYr³Ôžöýã´ñUðo,LɆVSA¼üá,;Ý[íÅå`_[,Þ¸¥@Îç…Á©ìÏÜ×þ#§ ’TÂ/“½tî ¶9'°I{Ž0ÃÝðïà3G1;÷DdzºÑZ:0ó_.Œ{ @f\¬BSã"Ü3&AÕŒÁÊ[&$nƒ<ôíÆÃdöú­Oh°â,àÎ%¤¦á ×£{ÿÈPLƒæ¬× {¸°}!né)ƒáÑû˜œ=ËØ–Û±K_¥ÙoéwX›U ¹ÿÖ@“Ûe8–U‡²µO˜ïµi¦Û<”÷¤ù¨”À[ÖÇï&7à·TcѤ£Ý'ͯ¾±ÿƱ?v^€ÈÃ"ô…×+øýô89 LŸv®aNÖ?+ÿÀ‘{ZÙÌ$Y»À_Upÿ·YèNáÜOç’îí<ôªH'{ÿ Ï[Žö͆ôŒËFðŸ˜N}ÇÙ}Q,Ø­RÁWþqs“..>ÔH.¨Ä½`¿E‡má.!‚•ÉDo*…Ú5Û˜¨_ Èìÿ‰RÇbhàO}üû¬Ëwn!uó©qS2 g[Qâ$®B4µÔ•üI[ðÃNmºõF’Þá…$¾xè(:N/;ý…ç¿lè˘rØügíI)Sg`ÔÌn_Å]t…×v/€Ÿ£¦aO°ä£#³®f& þï| Šk®mådÊá¼ÐUÔZ¶y4gRÉSÂ$Žs¸ø÷ë'ANg&,|aAc4²ñ´œ<p§ê¯P±‰=›×;ê×àÚõLºccº¾[N…?~`¬>²à= :‹`†i»v}.ýaQŠw­Èô¶tü:&@c2qÒ1œ/Sé’Äá_®›4&l¾wIëKeIùcI4WG»5éì½}y¤ÍW•Ü#©Ð³=™@eÅ›’EÑt[“Úìƒ-Õáì=Fí2hôL è(æcIG?ԨⲱyTG°ÎðŠÐªõmŒ¾oî|bB¾ìQ%ÒóJ t'CECéæý®^w`‹Ð&Òô>‚õw€Õ2 Ú?µÓæ–’[®dy´3)º†í¶Ï©RáÇá]T%óäh1Ö±í(Ãy†g†Ü‰é#Nöµ.]G»‹ËI`¸;¹}†˜oøEY{`nÐ…;‘IÑ8öÚÏݤE²CRUÈUOò#N‡Z„ƒç¡CX“Yºq [gB64óÂ&93ZØ‘J¢úc‰±NѱW§ÂHÉ$ÇT³LR@´{®RA,ŸÂŽõÌgÓ刹`xÕš¥ËèºryЕüûŸuWC×Ù$¸õ ½+ó3„ÑeÆl˜Rª‡ñkZ˜ÝJ8ÍéŽdž§%Ì}“ ÿîÇœïˆÊß,™Ãõ`®x+Ê60˯&“±ëp÷ ´§†‘Æ m* B&Ì%ˆÎ%D÷ýN¬ÅoÜ®nËŸî'9AwþÍ=7žÛEò·ESÅ=,àÂpÌ9¸6t`=̪±÷U%$‹»Å}ô%//Ù4¼C°'ÿMan¨?*9óᦗÇðæÇ÷/•„WøFY¥¢Dêîm8û`&™#z6ºlf þc„®'¶Ï¾{ç?Áøi±üû´ä*èlKïm#œn ²ç5Cœ95Ø«“†oè,"Ä9NÎã^Ù"˜¼UæPüòk ¾6&«”`‹n9®^Іå/¤éÐáÓT²è=[—<…H%’Ÿ‘…djêv6XÏjäßì·p,Ežè|jq^KŒI¸‰Ó¼4›Ì¥£›£3:;ñ…öt•¡63/¢/xƒU+•Të„ÝkëðÈûJ"Е‚í{[ظÂh’Í¢ÁªqÔh²ý0wâ×HÔïoÀ÷ ÐPšÉoØ7…G‘(‰¼jäSgWe¹xK†óÚ0ŸÎ´Œ¦)1áùÞѲÁ]»×BUº>}ÜšŽ)‡0MQ¹B~KÇpt 5q÷Ò5ôÝ–xïú¾öìÃ1þ^à;+bïÃæ¯NPz(®n;÷¯£¦›f‚ֺϠhÇ©cŸ8–."ËȧÞmD9ë ¬ïd#ÂxZ6Ftá`ú*j.‘8û—_Ò’¸Pº!üÔÞ%êŽÒ¾)4髹׾…êÀn²Óc =ìÿ‚ïÕQÍM'@ð‚ uŽ!!Éͨô~3=¸4bê… oYVÉè’gÓz¹' ñ؆4LùîOÍbüi¼f#9]¢fgòþ'³×k‚)fVºóÐ Ï6zù‹?±¯S"W.%‘Œ ¤r+C\õ[ÈrñH°ÿ‹q‰¸•m¾v1š±0ÍþÝv”E§…!ô³èú`˜×Ž—ÿï¸r±³z9b(E›Î‹Ùý=ôÒ«p–gê²…Ž1·fÃ[Jô‡-@ía/&þ‘¦'V&‘oüçàúfazݼ3¾ñ‘_WÃÑÂKT7û>µ6¦~:êð ²ïm—¥š1˜I-sØsÒ‚È'•8èY.HæŽÙØæÏ+Á÷ä9s«l^0Ž ’pLÚ†Z Î"qgòP¤ò¼6ëdNö^C‡ÍVdÂ'ò_Îmc´J Éð=*IäsZÓHpiüwèÖŽA”¹ôê­ fÝ{wE‡4û1ƒsø©jÛVˆžy_._O»Œ–â©[‚¤u·6ѳh…Ñ]¦tÉå4fÕÍTºÛ™RÒY *޽X‘–E…%…àÝÓ Ís6ÞbÖGauŸüÀP¸ÔØÎN9²˜ŽKÄpþN‹Áð˶k\€¸{J$•ècè~"x92º€Þ^yã Sñjþ>’§y®)0W1šVûm›Ý Øž^‡>ÎÆ^Iñ¨*„U¿Ðí]¾G3¯@Ô1#Vj¡Íf'D¡LiõÆ5œ—æ`¹­6Ý£ôΟGE7J02¼pé[WôìîÁΘ[ìrWA”«×BÑéС¤NØ0Ky½ç9•ˆÔD_D Kû?/Ù¯µî˜ÅD£ì…·lêh$ãY”um{ˆI`$ývð;½ºÊyrç>ëÞäÛC¼"ébÙƒŒ½ÿEd·¢íß½8EPf¹>•ú&<ؤ,Bd³>‘‰ÄûJUšäD³C_ã]žé„[îĹ0ÿ+¾‘æÂÝ º°kÝ·¢5]H©Ë„dKÓMβ”ß±ƒþ@5R8@¾‡ýgH¿ölò^7…z¦rè@åy’6eõ|ÚFÎ^­¢uepíîïhµùkqŠl½MÜÖ=eMÊûýGPàq v _œÏR·m(ýø¹õÓÞÎ|ºdç9ˆ º„›oe0ç=¸ë¦UщBô ÇmìÔ$v㘛ÎôÄHF*Ä1:¼#›œä, Ik#QÈYÆÖþñgœCrqþEf,‹‡FœC“Ó1ôý° Q½,J§¿·!ß%š`ôaûåenQïnY%¨É¼DÏ)µ°îÍ.úm®;%F ¾Èè•´fØ`q†×Ÿ„¶”0pKÁ–eþÜ÷6NtÅENh’ iÏ˦ ½Ü‘½´øÈ:øw™ÂK­wßlzZø nl$‘™ävåLŒ™…'úª9A“7ðñ œ=¦D”jTÉ6¾Z¤š@?]BR£áYG"ªpcöâ>ÎP4óÙMÝk.s;ÑœTICåÏsÁ%Àƒ~~-ÆkÐÏçÜ?Fò\fIÜyP=Ü’=âTçû¶íª¿‰x}(Ùë«?;ó õ`×ÎCü3c˜—Ždüm.ÛÂQqáÅÄq𿢠ÚÒE½SA<ÄØÖKÅfyE祱èdªFæ6é6C€ú›Ï¦mÁ }\F.v1u8tÓ…¯2ÙÛu¯AvÓLöÚüü~,•”DJÐè’\0-›FÜV ÁÌåƒÿy˜›¼2 §wUc³ÿUä• bî½¢ ן3úï2ßv!‹UëàÞÁÓŒ dÏúÌTçåàÒ8-Úxa»¬bŸ?»Ã¸±ú´P°£ñßgâ±u팩[/t?²!4;Š 4 È4‹óÓíLó¿ûãxô:\Ð[Ên<:Þ=Fb·;ÑZ¡#ôQR$‰YEòÝ…k"K]mèÚd?Fò¾IÚ#‚%Þè÷6;Í0oqZ:’¿ÍÕìë{9ðŒ&âàÁ•$‘g5Ysʈ”ÚŒ^³OxÑô8 ûÉGi9’éëÔ©áÖhr?b1 {8€oO¹`P÷jèN6aøö–p}\C™¥æ¶¶0<5“ÎWí˜b O}Ì;ñ”ÚÜ´0¯]˜‰Ó H|Ø+˜r=¯ùØ£/xåàað9OáÞ|°ßyÝB"¡ïL<ÿXI?ñb½ Yro}-VÌ Ö=³×zÁüUÃ8ïE TïŠEÐ÷¡ÁŸ†!N¶û7Ê`Ñ3eúã·/s(Edx3%=Ñ׿;(OWŽ£™–¯{‡jÛ¿°ÞÎÒhqë4U´AKðÜŒcçgÔï AhÖaöªm#óšö௠WºëȦ¤ÂÔíâ¸ÙÔŽóÒÉìO0¿‘²}Ó¡|[hgÞbKœŠ˜o±“8ðwsîÒ*úS*½k‰d.'¢/Ž íëà¾V9Ímš‰‡ñwP*x Dbys Ú¼2–òá5¢ž"K½ûÖs¦¯\ 󵡿luui‡c?ÁðŒ/œÊ» Ýâ~(ý„£÷÷sdyvœeìMþ€Aµ/#Óþ=Ôø“Óu>™4\m ­µVFãôy0ã ³=¦Ë/¢ëöCAÖ_8+<‹tíÕ!Ã’ z¦ïv1†t¦¯ÇeéªhôÖo|FKo@à¢hî烳™ ;ƒÉ%ýs»Þ‘.R_„UWv¢µ€Ìû›M¯ÌáÐs¤ˆ™%…7M¦Qñåïÿ±ñ6\'{:BϰÇ4w‘ØB)ÎóV#î‹ ôÍŠE°y fC[ggüýi ›øfÜ|6Þ^øû±à…g†p;\:=‚ß'-èn¡çœH3œÓ›%ÙyÎÁ˜¾ã2Ûs…‹s‹…ɵ¼kx9x±W‹£GŒöÀ´w¸º›ý¹ú*ñè£êá$æ‘E˜_úƾ—˜·êÉàà6V÷kزB–½e0&’Cè¶ð è÷M S÷ÔÁ¨Ë8­V§N4ÝŒ€yÛ4Q[x±ÿ­·Ʊ‡†Aÿ{ö»¾¹¥èM"öä3Çí¨Îý\ NÛ‚š0ëu/;‹>y÷¼„ÛàNPíM8šv¦o]†ry£ì‰·1k†%E•ðàL †íW§A[˜ˆ=z°DµÉvá–Õ°:QÉ]_ð~²6¼Â׋C9›ªÇYËØ@2QÂCŽ*Ý@¯ã ·Çºš±ÈeÎz|ùM„tÿýaÓ8D@ß«MÝ^ÀqßbNŒk‘à Ñô÷ŠÆ¢TËÆ€Z´†¢°†?­½Q ¡f¾ÜáåFì–Gmìãõ6¸±©Žû%Ž/¾ÍÅó Ýp×øŒxQÐ'GË+áZ‹³+0÷°!Ö‚É?¦®þÎÌ?bK—ýQ%DB¯ð]ƒñä¶s§71IoÄ/nY¨?ÀGgMFãAÓæ}µ0}X¬B&/þX>IxÀ¹ ú[£hÝ:òÕ1Ú&êöÎgý†ªÈí§®$Áõ"¼Ú¿¾¸ÉÖQ”îÜ Qý<$0V‡nãHQàÌ!G.áþzÛ‡µ²dTf:ݽ3¸™¯ºk!Þ «é{¡£Ä$¾ŽÛãÄ¿­ w?ëáévÜ¿™—rB9ðçŠÝT£›¯@õ”,æìó9´Æi m•S"¿»òP-“CvÎEֽ㇫¬#lÔ€i:Z$©ûwš—8_?ù“òä”ÀÊîÓd LÀú—ö$¦àŒ„êLj`vòn”oè1'îA…INóoáuCŸúNh‘»^Qp'Ș¶EaYY0{ñî4\øBŠÎl{Œa‚Óihu ZžûWG½MÈ”ÎVüvì=È3õ˜~ãö'2ü»~pÌ®š‘)†—ÊÐiïÀRÇ0­)fvq˜•~gYE JWîÀðŒKði@„üº3†cBeùMµ›.þ}ÝÌ÷…wÀ*º‚ííwæê4”bmC¼- A7—x-©/O¨¡ó™(üÎ|æ¸9'ຆ¥T.¨“{cDþzãFEUjS]Çž|§Œ–ƒ³ðLO$~DmJéÙ!DûhÃx~9BËl²íùFöíêÌÛ`Cê~á~u—‚_Vø+`äH±ƒûž ÷Øf©S¦7)qó</íkaV ûc(Ö¬ŽgKUdÈ@qZåY@Ö'w|·ªNüfñ+@,¬¤Á™·à•îYfšµ;«)ÔÄc/2êçuðÖ µ5©‚ Eê Ü‹?“ËØ!朡󬉤³98ßeZÇ‹àï©e`UœÃîÈÀàÌ¥p©b]ÝöˆÅÂþfîË0h\[Ëx¾8†e»o`q¿jÌ,`´VY÷Ÿ*˜ÆŽ,HËÃ-;Ø6OS*m+7ÖáÕ<+ˆò9L9§²¹±=vHØ1F.rç¨C0 · åÂv(’†‚žà*è «J˘ßC¡óÏ>h®Ò%‡Î£?~yݺõqù­g8L¯ ðÔG`ò/Ǧx_€†™#ÍÖ$¢œ’ Õ5%ïlœùØ6¿×|¼¸æx{®jÝ"DK¤Î•Íçðæéq´œì†5Ç…É…{°RÔ#æ,!óJØU*é˜â¸ŒøóäÚjK2úÎW@gî6uùÇPi`,XžºFÜ¡šfv÷ œêþ¬tš¸'åv± º¬šo?bÙM—ØUŽ¿`þ©b3Fü¾4™ºEÞžßýPèe‚Ð<,‹œj{‡E‡o ¥¢Y¥¹…c.ÌN&5áPœ 9ä·Ò¾4à©%aLW £¤xNNíäÞ Q&~^€§=cÑ€¡Ï¼XµÜo¸`¢ƒ9ØÛ)Œ’üj «÷Ò?6z䃊#5ø v¦œa‡NöÀh˜9ºƒÝÒQ¾Ê -yÈ®t!š|¡øç¼ãÜ»¯¤©¿­ÀêðM¬á€F²—ø¥éñ?KÉùŒ„Ú[æm§2(½a¾žœÉlêh™î7L¦L0»8ƒÐ±¸›P|«yšŠq¦œ(©Wd«µµW5ñÒý;¥iÓûÛܶÍelÖ> øÝr.ؼÃÝ÷ÄiÜmì7ø`û}m$æþ|ŠÚ²h¿»¶ú1lS^‰¾·æ@ìQyb®¯ qSrl³g±oú1V¡Å‡÷"g8Ž›¯qâÎÆáhýM¦K™+y6Ñ5gõ˜%ÓwÒX Êìî’ šú -²ØÍ”–Uƒê{WHŽÿyCÛ“?"Àe¥"[ñQ€”ÈÒi“Ql¥ÓxòE˜ Wàçtq0t&ÙÌÑ<·“ZÎM…rs@3qÙ2&Ïtíïb.ÕM%é*¹²ûØó¸q:—+£p¾›ž'îÆ DÖÚMCÇ á‹+MEH Žz)™YĪ8'·5Ià7yÔ×JÁ¹áoÑ€ê÷àLÊFðÌœËùÏÛl²¿Ÿ~v .Yý`úvf¶²Ðýê<Ë…ËÍ¿ñY…¨D12/¼“ùïKX½›ìŽŽUl¯ðwm1wó¦KߌÂÓ$ê *K|FOÃíÛÌx2º?œÊ³Ð±e!§á«óÉ&-:7~ðÑ„0Èô¼û5YõãØéð“§ ’Øù6uøØ´‹Y'IÅ ùICl3”UÌgþé=ôøŽeøØ¨ŸHÞC¦mú=Œ>gL\]™%W±Bd\|ÑÊÆš„àA êRƒ~òw@¤gI’ÕùoOinÛbo-¿Ïº=¶&?2o¢Ó²Nü(£‹Bk¬A¶ù ¬uÄ?]¶lc"×¹•rZðP*|Œ6¦ÍêÍ8¾;„l=” ¡›VBÓÎŒ{ ¿{”]Õ HÕV³Y_cÀp,ú2ûpã7¬ýÝ9¸r“§'X÷ø½ köçžSgåK?q–f5°—}ýÑ··ŸÑ×Q§§#uó§}$yÕ òSCŸ˜ì'ÂJÕpzópŸØɲmðãË¿œ%pŠ4}þÂ|,ßC®¥ âø‹¤l¾kZÝMá?•îcZú%Ç¡³WúÑÏ+ÙéšÈ/5YòdIø¦IïïNd¯¿r¤Æ†r„["G.øm#Ö»é>k…šôÙý.j”áL^ï6$§§9.AWxyÔ™´‰¥cå¹\5ú þœ('}ÚšäžY(6Tž§û.ÃцBÒÆÌ¦N§ñŶZàù¼š)yº’ŽûXarn ,©£'–›³V\Bå÷g³îí€+›ç’+§*ˆ¢@­(®ÝröØ5²ô€™yºŒ…ÿ½G4¨—sVoÆ_=Ëéã½Ë±4¹fØ|F§‹Ç‰`Ȉü¡CŸ¨;Óæ–猛|=ì>¸“Y8kxè̓©Ÿÿ­/1";¾›M±Y§¡[*—âú/ÌŠöàôI†^»œ s´åhFò\ÚÉä ¢eƒ-» s*¡‹ó›±ºÁ›`äs<›Ð ¯…]À*‚rdýpÁ—G¸¶î.¼m¥è‹_dÂŰXZ±ÛŸŒÂžsèù<=EÈ”üs8~ÉŽ Ž Äû«¸Uì Pý G xmý‹ØŽQ´lÐ3FŽBñ_D_o Î5Ãnµ(ÔühõvĦëãk³Ý.£}Ë<,±Äø]‰Éß@¦V¶ ®Hâׯ]0õÅ0¸éIÜLë\à÷¡V—ï‚9ç8|µ;íÞ [òŠÍJeQµF’šM±%Å*ßûÏ?LjÈÌ„áúªˆŸòÜŠ@Ãˇ¡& Å6ÚP€¬˜Çè *:9äD«l'ZÊcìò«½;ÒJ_3OZøàá:Ÿÿ<ÏäÚÙC(='5–—V)¦¸/ÎþZ–n±h|ìù¤Mè9¾ËPÒ›Òôká4öí2ºþîrz#Ä$ÆmcÞeã’·<ÔèH(³×a?ÜV…Û'.3-"8FB6¢¼¨Ž»4t!ÍàÀú¢¬´ÓYb”gò“¸‡²´ÿÁ;HÚJÏGTOõGî/§zvÈ4”ÕçK?™ÿüÈG2ˆÐއly³9Ú(Gç× ¡“n†íâQxði¾ë Ç׳yv+S£‘ øúÚ>ë9áçp”)½æ\ÁູÐm·N‘¦ûn4c—®_fLÝ*v£Z¡aöxÀÌnOŽ–Ùm¼y9ð?Ÿ3÷¦ˆ%‰;.TòÌG ó{_.7”C }Ö"Á®`²þeRfüÓ}a0­| ñ+$¨TLƒŽWÐV7„ãÌ(—|‚Û‰K÷2Ù¥O°ßÿ,xrE^„¬Ïùôþçø¯ßß²„Ì»šH|””hàöSÄi¦>yï5v}ãÌb¡iP¨vŽo`3Ñ8óý[HÿtÓÙaßç­ÔÈw¸bÊDò×Bº¤NƒrB°o}5W]WOüTåªL´õ4&ÁéEÌâÃAÔwF+±>ùŽÕΤšÝy¸»âè×ÚÓ«±P”¿”‰ ÃB{G|éy®›ü†î¤è(wøwˆ€ÿÁvHÛ…›óÍð¾L~&Iøÿé4FKœ±ÏyÈyÛè…ï·k°ß¦Æ’~÷+ð¾Ú”Ñ^r­–î%ÿµw¤ü†6=9vâC',:Ym¿—³3ÓØÑi…Œà¬ÅäÇ—ãŒå¬(2–¾ÙP”%H· ˜ÁÖBôYµ;©zëÒß›pù^Kúo »iÿw®‚.¡'ÀÿÒML?ý6F¢Þ¨ >ÓËgW5Ÿ‚YÚ·@jµ–=¿Âð½– ²³VPçQMtÐÀ+#ì—ÐRˆzÿ„Q?؃/¬`e_+#ÍK[ó+Áí¬0ø×¿Ào¬ ´àe}aZ¾9 Ó%$!l¤Œ]¶n>/#.GžBºa ÞNÅiey˜\ÓŽŽ1KáÅî`¢ýßY¹fô~ñ]åAn´1œ’ûLѾgèÄ“…æç~aC¨lÐ;À¾ûj…>ÒÆp|úTÄgËhjþTð²Ø “ËÐÚ<>Äná¼]+Ê {FÃèÀšº+ ™ƒ?W„@Gy«xQ–jÌ›ÎõJJºDˆ‹Ó0e‚Ù…;¦öZs°÷¿Ñ^Å8eá9“Bü>ïŸæé›Êìuæ#Þ;fA“˜1Oá…í}kHVâÝ«ÜͨIœQju°v&–xôåI˜mÁÃÌîÅ{:DÓ+ /J5£vÕR¬±¸‰OvÄC‡vÏŽ še"øâc8Ôõ«Q+Ó|ì•P¢N?´qÏ^bvdUlz¡ÕKÿÛoºA…ÀǬӴj~$9]õšn3¤K.wCOdºîÁÿ´‹K—(þçY–‹!%ë²@߃ò¿} še‘̳O³i¸'Îê€$û\|3…rC×açÈUöy_(+µ›æ0†ýÈe÷ñÙ –,|Å]:ZÁݹ¤ ~w‹Þä Tù#H÷µ¤uíhฤƒß«•Ts8™þ°°¦×"~ eÂB$³Ÿb†ÙzHuª–‚ý»{ðŸ8m}:Êà_G¬ž„'#êjC'ÝáÝBC8·Ù?†X5^F0M ŽY[$ÌZ÷k’_'ñùO ¸0ç6$¹Ÿ—ìÑ^eîòæ"˜þk1ÓÚó/ÇrÀ¾¾0x*º·—ãJp”iõ·IP,cö*·BÄîäfK™kÿ´Ý-ÈçYƒÍ;M¨ê6oP;RMs:Óa{ù/&tØ>]ΖÍ=‡¦_„G­GÉÜÓvÌ÷5gQÓj1ÌA6eNÅ„’¥(0Â/•zl’­ç¬wƒù’à§—þçŒá'®WslzqÕÒ?˜sÒ®íþ†]yÌÏ"$v”]ïf§ˆÖÁJ±](ûzzyn¢Ëcó±n؉øäz¡F\³ž¯‹1³!¿”¿25ƒƒà Oa…£*µ—`ÏÒ%ïtIXK{&-ŒˆeùœÇ%’dïd9\­…w’‰ß§lÜÌóx³KÑs!Ю­àðµ»ðþž%}?1CÇ š‹,‰ÍÌ 4ˆŒ%æ·~à§);ñ×\—æño~\›–´Êôça›ÍƒFí:óe:T Fƒ¬â0wsÝV-ë+>ÝÑÊú_dåýKq×aGX?Ϙ%O‡+Rq÷ÃoècÒVpJù´ ã”Ê+/åKô¸y‡»Sy&ô_ ?I;ší»€c^€Ó¿ßÐû8Q×ÚLm}E٩έ0KÞªÚ’ñì•õ”7F€ž›}µå<ËÎ5ñ ;ïtÆ'80Eö°7ÿ1dù¶‡ð¯ŸyÞpØ£­˜Vs!4 ü…ÃrZ[„)WUñ@S ›ºò^­“@Ãç®´ñn:óLkÛ}`#y¸O ޵Q1YB÷“x"iA òOPÅ aÜš•Ik£ÿb®±hîÿRQ@õ*‘~VNG.¡¾™š$¬µŒó=<’„e†ÐŒK<´óqÜÿÏ™Šþ¾‚˜^þ«]ÒItÍ SËÁÓ"‹É¦‹qП¸ M+%©E&éj“Q«ô„øqRöýyÈq  Ì ]ù\ˆTpñÐ÷@žƒÆCcúÜò0T¨×Áª}ȳê'33Á— Çõ³ âÔ^y.«œMVgKßPî¹ÍZð·í4ú”9"ÞXª¬EÆû9¨laòî}̵~Oœ0[FZúøáÛÛùŒÆKœfÌOÛ_ªâÂÇS&Äw†-ÐϤìl;ñBOJŒÏ¿ó^V ýã`9ˆúƒ™Ê7°c°üµï‚ô‹FðÖ#_”K`Æ­&fئYtdæ÷†²¥Ðòg8Ãçñ?_3w ­Æm‹ÕSÝ#p{É"”+Wó§áo¤<ú>ÂZ…¿bJY3¶|»,éå g• áj¾zÆ­€ì~ð´ð œ}-?Î0´ÏƒþnüÁ6šÏ4< ÅUú¸gÑ(ܸ9ÁœöY̶õÆáB•8æ¦ÍLrô«þ͘ri–\›‚7öŸÁƒÙ]ØW NìrüQ ¾×­à6}?Ë|¹ºoG˜ÚÄIœ¡˜K{?‰Mw òÜË™DŽO’ ±X¸5öèÑTËxØ›gŠ%ë/ÃrÓ<ŽèÒiô¦–8'†ñÞø¹ö2”6oe?]œ©Æïa®kŽè_§Ê)¶ÐàáA Þ¡™[v@œ†©41Ïž dø_x&»K×÷#ŸÕmÈLzWÜ–ä/^ú7cá„On1V£õ%&ôsƒ"95GŠðL_@ìÅI‹@$4uîÃ+¢ÉÅ›F¾ûLÂò%¨'r—:’ZGQT/›-Ù3“nèxÊ :\dÒN’·.µì¾%ïÙÒXmTÀ¹~š`ð «½¢éê›—¡Ëû,.{^õùÐxâ¬ý3•„WÿÅ--pJÈZVÁûho² 1”Aöw³†îV{)/ÛÜÎØä-Ï)òPe°#†vÁÁ èY*M6® Ó-BÐÀc£¶Y߯K@î£=Ñ(Jz^DÓæúkì”x„"ÂÄùÞ,lø 'ÚO°÷ǵ@çå=†ÛÌÌ\bÁ >Lå:uCÀ  QÅÇ/Ðx‹7Ìá!Ä´ìcÔv9·üiOqÝçCÅ›õHÙŸì«úÎnÿ>Ÿ~µÿöY¦m¿§ǟÐzx-­ûW»»œ&?ÿc—² òÝúƒFSØZóiÄiþòöT&>xB`õ«‰£«ËËÙ„î“äñ"]Nóá(V¡ä&Üþtª§È‚ÌF%ºÁ÷Û­µ 4ŠBлgíy¹Šþ·7óΗõXr¿Žà|‡l`fˆ“‹ƒ[h@/™ å!WV§ ‚ÜÀ–RÒãÓ†KÖÜ&e‡\ß—óÄ÷l7`o¾’Æ’› P—ÿ78ø|ÂüE°@>¯LÅš´Ù(*¼ŽX(ÇCॵDâÇ]œqx6ôhø‘ǧHsÓRÚ¼ü ûÆ&ƒŠñ/!OV…QòvûâM0]Ô«£5I×-u²‹“3m¢»mÈùÕ䎿 ´^ä‡Ìm÷YŸÇ`1.EoüËÕóË&CUZ´áËÚ+ÑÁïÏ‘éß=©óÍ*‚‡áê 3Ho]DšLXÚe]…7¯Z’Y­žäÏâé->1ëIÖ\æU/q°,¥Q3ÛÁr¸¾lÉ.’Äk}»H“¨qêž@ýpúQ3‰œ †¢ö8zWO•­5F§…ŠtwönXÒØÅ¼K©íÕg8/¨‚|¤éƒ‚ÔªÍ\‹Y@¼Up½Ë¦œ,„McèÀO²ÞLeG#~CÜhņ)TµÊ\/ì ‹îaŽo#ã™øO&œÁ’ª›ÌºÇ™—o›Ø’à ÔºÒïªt‰Þ-y¸¶úêX˜¿ð>‚•újO•]e˜ÃJŸAÀß–®[ýÜ4Upfg*ºL%ûëÜéŠ!_H¿eB;ã:pÙáßìMìuZåb{ÈFƒ2PØ·†šzÅØßõ%ë ‹@¯Þ쵞E2&ìÁbîXèXo;ü¨ î9 ×w³7à´*ˆ*à<Û%AßCò®Ëø¦ ™óƒÀxïFBÉvÙC°,“¢4"¼J7³·…¨òQ朔 \:·bNUaì§Læóx>“DíýN˜ÙŒ;Vûë„è@u³ X6ùe:su\šäŸI‘¶¤?Ï3ÓB2Ðú„)sçaøùQ‡Ëô¶£)°gS¡¸t=>xŠ*ª×ch¼+¾¦5tMp(Éû¢Mÿ‹W;ŸÌÝæð‰ó,„~5hRoú‚sª†1Õ=ˆRÎ+Ê÷ç >ú}‚º-§ÅUÈÓÍ6$ññNUnñŸ¶“[ƒÑgO’ *Èõ ²ìN}kÍÆ·QÈ= oû¬Å×iàÂèÚ6…mV¡b¥˜ÝÑ6¶•o¼qn_5Û0ƒÔ4?fÌ5e鵿ì23í×1éUèPJ18D?ÆWž#dù¿Ú‘ÚkO?óÓñ©µ¶K-Iã FÝ먅“ÍT£ ·‰†½9YåuTÿâs©:¨ÜÂÝ`÷«Ð ¿gÒïËÓòÄóäe@)®z2‡œ\|™Ý¾gíåGO (ÛOTÓ'ÔÏÔ–þ–{ƒá߬iÃm·^›,$ŠùGÉÂí­Ü¤üÛà=gˆ}Ž%‰Üùû—sôò&]TBÇ…Z±}önÜênC7´ÁlúNJd½*§ÖÂn3ÃÆí¼˜µkÄé< ˜¡º œ/^e®8€øÆ¬‰ôy˜s:Íç‹‘YUZ|1SŽC ÃaÝ‹d¸½}„¦ùÑ÷‚xmµ 8]ÎmÛ%DŠÿ Èw~²m[þÚ+NãÊ$ÉéQˆï¶,;”ܼÆÖ?xÀw?™Ê†P¦c38üú‚-ÍSñÎy9p^ SÖŒÃhÁ¦Z» ¾ƒÎá-¤—>u||«‚ZPÅ¥ uNÐòøFLÙó˜o/lž¶Q<.…àÞÁ´þ] g­ÙÂJC²ëå~ÄÃ"´z(—»ü÷RÚÞÿ§½ë‚¹™5àpBŸ$ŒrÈHÇ“_r‘´ ö³}¨ïKòmS/î?ý÷”øTššE!mµH÷ ]†åõ²æ¡O:‚éœÓ„7N‰ÐÎ°óš=^­Ãõ›D©ûŒ,L=­IzEèô]º¤ó[;ûìNá­…w˜8š±\w–Ñ™"sþÕv%A6ï¡ö9x“‚}O8¯ãò±ØØ*Í¥˜°¥C¥g2’§Ö2ãÝ1«À*«v¡ æ6̺²V>YC²oFAT¿2•qÓ'ãG¼I•à\z\}6m2Â'«¼ñÇå|òmYá\²Ð{ÖO¸ã[¡v, IfÝ»ŠqÆÌZ˜ºG÷ðÇ@{ñ;té÷4ËFpþBþ&Ö ‡ã+ ÷…ò™CŒÕ®¬9õ e·¦12£àbºO…›·Ï“·-‰Ž`”Ë ±ìßÌšü“¥.ǯÄtçâ·Íï9UŽ7¯#‹ÀùÕzfá¢m00t™YyGíîžlþxlî5>çÜè² qÚ¯³é<¦²y!~ûMlýt »üJ8!•2Ô¦/76Kž—”{+TŠ,R!ÓïAÿŠ;`p‹‡›ÊK‚×ÀÅÞtS2cŽÒÖs<`¾[ˆþµ4Ñìfvh›Á¤Ìc¶UÜŸIWu Ú-ñXyÞ™î>ú÷¶±QyÇÉÎã¼PQ#‡Å‚]°Å:û‚¸Ì“õJôßu<ÕË ²åpeS9®f£™Cû—BI¶vþæÜüN`ÖyBaëV2â¨ò¯ýµJÑì3¼­0¼u0¾þ ø˜ÀŽñ2¬üx E3S1öÍe¨¶`Q{Z¸Îð:ò‡í8+I\Ž~Äõ³ŒHø½ ìŒuñ g‡c(¾žŒÄ½e˜X½B0;»wŠÕ㆛ƒL9g+ËÏdO?ÙÆèMÙ‹«‚©Ï\~^ ãωE\&s¹ç4<)9…1Á.¨qJŽ[âsCÛóv¢ã±Íô‹«#ÞzÂ9vQ½çM¡®sÿå™û©ÌBÏËŒzæŒ8ÅÒŸxÃí(b§{ùCú]Vâ½ ^žõ«Î¿E‡n6b‹ÆÛÿà…²×øñ‡$™cñ“3´Ž¹%¤ƒg)…¨}ïX™}ÑucÀã ^Â]Eþ©$äëQöxÔ­ÄØíˆ%v©0=¨ùâÙ#ÅqJA®n’¥f*Î8óG-Øè…²nÁW›“5w )|g<Ùb@É-Fì~§~w)[°aÑ¿tj÷Œ‹¼ œõå ì'V¸óÃgvQÓ]æË¨ª¬%çÀƒåê`7*͈ÏbO 7‰š0»‹Œ@õ¸#­‰:ŽCa/±Zþæ½~ Ï‚*!2ÄÌWàÆûÇé®ÛzTÇášèò¢ªÝx»¸?¾“`¿fœ½º18xRûMpßŨºÍð÷ÙÒWóý‰ŒÉR+0 êµç3›ný¤ÁHíuÅ«ò½˜–¡Lnì„í•Q á¸åúQܾ½ yªÁøíHÝ\Î ›º1Î…å¬On«%/H!YgdI„Bç²^hd–ÇNƒT—¥Ôf‘*ypÂ(œo€¥ …ÐrÁo®½ˆ‹¥ Ï¯ KM‡[¼´ÊÔ"™?5N6‡ßš‘@9²IÞÝÆ3þïøÍ(ÞUÇûIÅͳôÑÇ}»ÌÝÎ?{ªÙìˆ:Œá’ƒpƒŠƒ¿´;Ç%Ȇ³%Ú¼Ôë}(¹Q™‚ö^Ô4H|·Å‚ýá+Ô>m½Þw }ژץ†l›Û³[ˆ”¨‹ÑëZ°gwz½lc¼rn²f*ÀÚŒ xX*~ïáÇ¡;J´6¹Óùè@Úvø¡]‡¦çA]ßÍÌŒaÈ:±Y=â+ß’€ÇÑ77„¯É@ÿ]°Ÿ¿™+ ßYª¬ø®×œŽÑa†÷ùiÖ×3„žèða~hkÀ¼ {¢pa'°,°ƒQlìFj:©D§FûÇͬWO.x(ÞaÔèkæ“¥ ÿ\ÀöI—âËœ8Úôדȴ-&•^ça¦~gÊä,zªá03÷Áf6‰¨xˆójÀÏè<›é8†[øúš—¨{Ò«ò!§+‰½[úÅ™(£Ånü§ SÏq@Fè2;Ó"µ‚é¶èë(øB~9î‚YK¤ÈÔ Ö}œ=vEŒVv?³ÍyFgZèÑýÌÑ[|¸'Ô¼îHÔ"¡ w09ñ~›ÎI~ðÐñ9±â—K“hÅì°½*‹q‡H»žÙþ:- ÂOÉ'gFÃfépve(—çn>÷Ÿ=|ü¼ ßùŠ‘p‹ PšZÆ(Æžg¿|± <ÝräÛ¾3TäÚÔ–W§{]’àá†tàl™Æé³…î5÷ÑRø3rŠ‹P8\…ÎëXÂÔJc¶ïuc¸ß³`úJw(Ì[‰Ö±§‰àáç온19šË2Ó“pÓ€ :XŸ¨œ<­5óÆe3cñÍG6—‰·K„µ¢!åó|vÚ;Srá Y2­t>Éï)·¶AÎ9©§`hQÇçÕÀ°r49ù`Î|-^’V¤e{óÞÙ(5-¤×ðÅÊ(¸n˜ˆ # ÷&ˆml~Ìt/À™c©h³§j'’QôÆyÜýL”ð}› É—±É2”Î1 ¿‚ÈPD"zô˳&™eó%luáÜUARxjî·Âœ%\èódŽ}nÅ¢­Ó‰åxÖþi?°žõ'&E>ûo ú‡Eé¶-WáîúvÆš3žûÿ‚ÇOԭ͆ ©g`jü:vvw9*HkÓ¸W‹°1ì#6]öÙµà4¢ÍW­C—œTN»’&YLi°;- ¢ìáÄN…Ég¶£Í~¹…ÃÎy°Ä_›òÙt´u±_F•A—}¦H‘ß/T‘É^ {Y&ñPÛô|->´ÚJê§e0?Þ·2o¥Ž€.-÷úÅú>{ŠPÓ…SÒ5I}H #§!GWN ÑMÇ.£óâ .Çgß |§< ¯¯Ä<¥w –UŽ<‹2ë7³;’±¾#”÷ 3Ü8a²ýœ99-„WN¡rcú´4s?´T~Çôm‚ôUø!˜éº;„v0ïùgÁ3Õ—ŒÍ~E»ïI‹Šñj9¸©“K.n$Fù4þ1/…—Cí¸²–«¦‹ð2ïÛÀÎd±÷Ü‘Yg<ÀéÎÖ’OЧ ¤Æ* .ÆÖ‡ƒ›Bg`k$ëQÑŽÂ˳ðÛd±UãX w_ëBÂ;¾÷ÅÀÝ$gÔ¨Ó¢%%UÀ9+O–6ÎA³·ª40ow×o'ˆ.ˆeÓÞ³‹ìmÎ9%°O²¥±Y$yÂß Á !àX£? äX]©^ø¶1­¤àòâvœ ÅŠé#¸ãbÜñýÅ.ð¹ñ½=¨*lKîŸÒ§‹’÷Ñ­&™ëu¦p—Ò2»f‘¤_ZÕX›˜t „´8׃FM‹Q4$‡^φŠR;?t‚‹Â†½7ñÝ Ô’¡r*‘èß™ÌEä2'ÖNc׸á˜V"^ÝÂʛ߇{†YxY@tXAÂ+=ÀuöÆ/|ÒŒm™'©Ý"Ikîȃ§þ.Ì=SÁD©pÆÌ~ú2ò“QiW>ÚŸIçÌQ¿ “-H¼ {ŸäþÖ¼…Ϧ:'…hôõØD-eàd²gf¥æîŠÃseŒÑ-e&w!û±~:,þÁúœm†Êò)jkH ¨“¶ƒ`Wðà눂†„Ǩ¿0œTü=J/ aÀèn°|/K”øåÉûÍ‹YQãPò'J¢³0àª-½àýƃUˆ ù|úzü#zq'Œ2e\ýtÐn;Ëüó¶x'-ä±FÓ+ipøê³Çþèÿ8ºò¨¿.ܤIƒæyž'RÒ î»OƒDIIHÊÉ,3Ñ<«¤’Ò,I¨$‘ºï>J)D)‰„ Md&ó×ïûã®÷®³Î>{¿çž}öó¬õ¬}§m& µÖ$z¥Ù=~>äó7–·^¼H#Ù"1$fºýØà_Êà&Œ[ņèxbÕ-Ø8zÚ~– ÏHÔg ØáÖÚ°¶wg Âë@Ê×´¬Ó0wš<‰I&›.§CñYo|¬Q¥´æ¥<Årß)4æ M}E$g÷휉§šŽ÷—è’WÞZtiÊN°Ð)&uÒ›°p%beÄ»Fç£SèþNOøºç1dt¼EÁçÊ´º};^” käkPÂõjÞt×Ä?ß]„c^º ýô:7ñŽ(½pXšMøò.ÊþaŸŸ2B…zëI_ÂãXãg wŽFâÞošlz–;‡gÎ9|Îû‹WI$AÉ6TQìãòZ“hÝã£~ £è‚e…äݸ*¤Jn\Œ_"²Éº¡l¶,=“ûMw:_P&–OV‘íiéÐk| æìÓ§7&±M« õ·ÝˆÜ•AþÅcF¢Í•üÝ K Žë’¹= ßmG Þ¬ É²Óé ˘FÅ2¨ò8[&ùjï‰%Ø&íEëgÇîž(vמxçH7\¦|‡ÓHå‡XFld*¾|ÇѶ¹”;¿Ñn'œÛO¡{f,Ì€¨>c|»ÝoÞñGbŸÁ\nH$ÿ Ìp¥>4÷sÉË_9ôrÀ‡Éz¥KB¤DH„§(ÕÜ`J†´¡á‰jØ}ÿ˜š¯$¾ÓîC«¹#-öÆy”ßÅxfoz8vÔ‰Q™ ¥ðãV!Þ®ÁŠÃ}Ðù¥Eúï3ãûC‘Ζ#M0ä­ +,§À>1fï—ùÄ$XÄ!¾¼/ïGó•6¤aûÖ{j,—Ž¢c——RÝÌ1¼b$BŒ¾´0íÇt‰›<ñRy )Vu ïöù“ùwï1þƒ„çl.\uÙ@÷•yAA¦lQ½2§“åy#‡?>£×¶„ƒâC;rñB;Ù´ß”J¼8ƒÓ;Èûgɬ Ò²t.Ä%òàòˆzuþŽþ)A"ÙÝؽ˜¨ãm÷ìÇAzÊèŽ WàëT0ùsš½öµ‘MÁÇ3O¼ÙË>ìI†±ncfnÏ–j~Âs&?MŸ¿ÿ!s0ú2.å†1{Þ•ss{?N+ÅM"mØx%ÿ¶ªÂ’S<Ø™.Ë©‹þŠ»HQ[Ãd&·?¶: úvc^€Z¥—~|é«F–/;E’¯ƒï²„IâØ# ½>>¾kNEŸ€}¸Øƒ%Œ·(‘æÆclñéÆÑ‘$÷ÅpŽ–&#ƒgl§2®aÂDêºî[GöN™Eäl"îw;í¹Ríd^"qã8Vt^g¨ûU<&t yQ‘Bib}¦”Æ›Þe’Y/ÉXèWЇÜ%éqG!jø~oƒñ{áL¯ÙSTàɦ¼œõ¬Í²0ÅØ‡~¾ÛÇŽ¾9uÃeðUp3“±Öw·w³«úË0ä]&~½€Ú‰³ÈÓk…¨7¦A=½Õ‘(òÇxK˜•áYåXÄûUéªä×÷ÚÐ3Iìîu.snU‘cÿ¹Ñ +9S6—´/›Ev_p¥‚šØmÉ p’ÛÉÄp…HÁ!Dþr|‘'K}ž?†/Yª;yÇŸ®aÝWQúPƒ9µù¾ÃîÙ¿Qëi*\þz—ëÃÝÍùuÒ‹`Õ|0&EŠ®fMŽÇóMC0Ýg1<ñqGÞŽÀ“RínãÐËX7EJ‹îv0§¥—b RKüUœÎduÏÅGú{™Æ–El@`tvYÃí¢ì«D¢pü,䜌Å1APùâK¹«Âéu³“X_bHÍutp"Æ&[€Jdhâzoî•$²êV-]­Ñ sÎ\ÇÑ™Ñôžþbõ»f¦ˆ“.ZÚÙ­˜™K¾ÈßÇ’»h–‘Œ35BïS3Hö!äHœ(yÄç@ä—- K¦Qq³ÓTÏëÑ5•˜-dÎ;_bü¾FãòJ#ºÌ™¡Þ³Ÿ¢i3â•Ä"´éˆˆ¡?¼´ñ×ÍåôiS/´EJk¡úpšyf`7 ƒ…뙪G[i ?¡b°Ta>T¯½ç)Ó¹¿…Nœb“>–ÂG+˜Wý÷ÆÚÐ’†_lÓvð¼h@œ’ëqÝIr³ø"yý̘näÐÖc_1$^½‚À³tî Ùƒí&dç§L_E³{›(9uå.óy1£Îê¡Hð xu¡ˆ=¸õì?( ö@Ð=íý‰£§ŠÐ”Ï‘híЦ/[1ßkU˜y¿»àJóWœø)©ŠdÒ›à(I¶¸òؾ;»X»½²4­Ò–/°A6LŒÊ%Î ¹}a‹ƒð¼"ûI+rF¨¾‹|…]µ†p\ø4F9ÆØõ¨±N÷Ëðc‹ œ|É]ú¦¶¶R©8E¦éÒ9æÇò:üÕã>dxKÈ„®:l6úAË‹ZÏâ`,½t9âÙ©¨Çõ…•›éÍ9»¨ªc#ñ³˜9"A½öW£ìZcƒœt!>Æâ¡_Y•C_Ð>ý.3÷_!”õqØkü¡ Š«¸ÞÖŒZ.…t^KÜ oƒÏ«¦’±WMè )@Öó¡å€pylÉ¿ )ÕŽ„ðØ­ÀíN«ó;ÑðŠñ$ÏïÁ ÓiämÌq!9›¾>´ÌÅÒ³ž1-Ì>º9²ÃZ­É4ÀKÁH p¯XõÁ:^2´kØè#ïþpË„îwÇ{Íxð(nßÎâSŽñ–ƒä"¼×l8/;Ýà÷d5í¶¿Á™þ:œòïøÂ𮛊`@ô“Ô!Ô¢Ó<§3 Ì‚K3qJf18ŒÅ(Á=dh­Í:¿Ž¹eËZ• vnyÐxèl »¢NÂßÑOP.¡‹§ó5Èž;Aäšv«dêÌÖ(¨qʯ$2ß±â;µè’ºhÌ‘¯¢k=|èí“2$Ëç6ûs.?=ü†Cÿ¬­‚%’ùÌ›fT{þ^…e““u_ØDœÜvÑœ<ƒo°puF±¤í…x¦a˜ÉÐçv‡M_XÃøó›­½!N¾÷ŃaÆ –Wy/|\z}œû†x=ñØ ´ù6“¶]΂œrm"£4ƒÝY;Ë÷éân×è6}Ë6Ñ“g>€¬÷fhÛ ®åÄÂgëï z¥ Íü”HÞr]äxMŠ®.òdåVÕ³—_í€îéîdK^3{g*(ŽRV ¾ -þ\„¯†ñy{Kn‚F‘;f›É¥lÜ(=Ç‹ë8¦ÑP!A9Ǩæ®ÆSÀT,7†U‘!°Õ)=û>f'²ŽŽqÿ)ÍÄ}?,P½JÆç w1ú“«IÕƒ•ð˜àq¸½hý,”…"¬ýú"¸ò“ ÿö…~À”nUÎ!î9±äÙqc:ÔÁ±½LÿRD:÷×a²ôÏ%îÝn]^Äß7>¸[@žÖ,ÄÖ'—ˆß: Ú[iLT5ËHˆU© ÊÛèݺù„–k‘uOµÉ­‰Iùç’ÝEwq"è*‰ U'ŠÆÄ3gܧëQѨåó"Có_‘QÁ&Ü~¾ ŠDM‰~Ã1àyTH”퓨dÐZÿËq’V¢ñ”ÃäÔ=M¢ÇkDúؽ¤rÁŠoŽ)Ù!}ý$ôŸàö¼ªÄú©0½Rç@¿Ú!tîÒ Rö󍨀#Läî¯ÆÅÖ0ö›ŸÎë#”¯7€Ý"éGLünw%¡ÀýsŒæ²}tçŠ8Èw‹?w7²¹EKIiIíJ­!ƒÓ`E½0åÙzÂá499?.\Çiz?bpùiˆˆþÅ4BòêS;÷ùWÛ^Á*ýø†¹NÆçJÖ ÒgçÆQÍ)›¤¾;´9ކœf&ǘa¾Øê§AzÞ?c•íÂ÷¦ fä¾gU†©úÂû° ê ù½Q„TÊï'–ò‹içãºa"‰Þå@ç¥"»›Q†òââ¼Cì¹­ ÙMG’\™ý§áK÷Jæè‰çÑ$ü"pËø«Â·ƒziÑ;“¸Fï‡}¹#”ý¶‡À,òZó"=Ç›Š¯¿ub¡:DÞÉÅ'|ãpª@úÄÉr¹F橌1yý9Ö½ßò!"¤ô ´ȪilÁ¾8ò'Âb°åv ùP^KíÉ8%•BÕ÷h¼O•%ðÍ_ øð=ŠýÒÄ*“c”¿rÍûŒ>'» ùÚ)ΟÅ:4úÊc¦.ð Îé´£×tÅèž%÷ †kÇš®zÏžÙ²‰v‹ïb®¼Ê‡´SX)›¿is,xHØV"æÛÊ‹7BVb9ÍÃŽ]'ðÉê³ø<µž{Øt6<þ&QâaÝÚÀ§îóìN$Í.ŒCv±Þvƒ->_`Ïkl^BÞÍ"ûü/ÒõÛÖ’¾Ïa`§ ÂüÁ³ð*8ÿå„}jdÞ<¾…÷¾ s9ƒË>ƒäµjˆ›…*çk™¶=¢t)Ÿ»ðÎL ®b¦N‰b$´½1P¨iQœËÊ.áf-ªñã°,%S°®ÚŽ“=¡“ܧ-‰îÔXNœƒË†ðäÜo¸aàIâo¹àâm2óë ÎìØ^¶\À”½Ux[[ÂɳHyœö­ÓÔ[!‘'„vI$2M/·¢Ï –vkc:/YÞÅ]74j±ZÇFpzž1½(Tmゥة;œ‹Iùl|ÿ^¸Q¡ŠMé—±ñúIR!øœ½Ôñ×9$ËÞÝ;¿l,|\ÇÖï‹cÅ&4Î>„p?ëÁ5-0–éKx¿mƒø?†´_]l3Ñ_Š Òî’¿}Éü‹wçT6}¹ŸÎ¤ÜÆœýé@JBS¨ŸÎepYk¢Æîk”³WË`bV3ξ]Îz«QÏM:eó7<¬¦Cþ›ómŽ%l·ob^|wÄ­¦¾Ìº¡cdäO>ü¿ËÝn„ÒÞš;óÉý‰Ýdé²Â dŽA…÷N¾v‰;ZãC&í@ec5O< Ž|/ÐK…¡Fó éÇô©ô‘Š{íò8dúŠ3%ç9tâ0]pcdSt‡À,zâ£ñœI¸ûÖ±rNŽë´,àí½·LUÎöUä†ö²=ÔF°ÍS㎆(r,hÙòÒ°¢ùÔ8W‘ølÇZ™"ˆç'ŽDq‰å¹¹ÎAÜË‚ú÷l¤ßâç‚ú£Oà7݆Üm¶„à"+\ðË®·¾¡+Kd¼ÖRÕo?;îòC1$½„i ïJÐkò¤% ðé´'¸dñØ1ŠS÷ÿ×kÞ+ÞÿÝÔx¸?û/ðÐÅÓ(=–‚¥•éxßlTîYŠyw é–‰»hÅÜ]Éþ@AR¼Á™f|Ž‚oË/á„ùx¬9ŠwÖwÊÄÁ’_ëPÿä{֯ƇqoŠ?ÃÎú~¢0£/,"â»ñï_Iò«A •w¨•|{̵ $#NJ à#N?ì=ƒ&SJÉ6 ~óË9â¾>ÝäÀ—™Rt×»H˜g}·qé®O𘚑ë'ÉÛ——Ðw}1 \~Ê^‰7D’à´ì5XxŸ—ÑÃT„GøõÁšj«‘ná(’Œ-vm$}J?è´‰€¥úgäè£û‘ÊÆ;þEP¸© ø®#aºãØuü+Œ;-aEå“ bPœ$Ú(EÊ3V!ln~šJµÛ@úÈsÈ0šG—œW"e‚¢dÃù"<è¶Ë’ÿÀ¦û_ íšÜ O°?>µlÜ™˜?ìÞ…Õ¬ü›Œjaú~F£Qz!¹xVkëwQG¡ünÓ†ç˜gsE '¼ŸÑˆ¨†îW®”áN¡ñä‘ý(6…8ý}›øñ½¯/æË˜NÖÛtváÓ2´üyO®ÿïO§âþ}öô¡útœþÖ ®Eçë°×§…Yž´ 2Õ±DÃÞ~Ÿš<]­½¯ÅÏ%j·MØ|â”'s=ÃCÜÚ’@(Gˆ.®éfîi­ÁŠEࢸg„ÕÀÜsRÜ+y‰úÄK8¬s‡K5!ðÉ´Mtkó;ælÆ*Hj‡»W+0Ò•AðÓ—lXõœ) ¾†Ë?ÞbV -Â;µ8¹+Ä@Cg? ª“¦ÁKóQë\3¸ñèËÀ„Ó ÞSŒÏC[QõÞ¶ÿÌ™Š“çåpy ®;j“͹¨—ú„Y)¢‹ïâ2Õ¹ÄÍÀ€~“ç}›<ºäNñè9É|ºkJæžýM¥ÉØH‘UHÁ™ëàù*1\~€û> SõÉ*G;ØmŸI?nÛŽÃcžìú°V®Þé#ŒÖŸ¨l% Á¶š„9×Fñ–øg@ˆšmnųwñéc*ëGæ¬öeî…'—®A÷ ‡õçƒà–»*ü§»k&Íü ¸Í|. ?•Ö׊g*aYU ÞI±§ÊÓ]¨©„ áT›’ÇXÀ˜ Þ‰w¬°þ)ªý~ÄáðûPªtÿ´|µ -~ÅèÙÊOnþ~Í®<êC8èv<‹ýô@WM‰4,ÉF±uâšÐ@­»B‰¡È žÿÄürKÇdÛKh1ÿ&j*#ý.}Ô±{.û«IÍUXïçe(ûÔ„º”!^ÚswÔRé_3ˆ¶’;꼇îk¹ÈLÉã‚­e<{…{†óå%‘¿²3\¡r‡Õ4}Œùƾ¸k÷bbc(@""BÉÒÆ­ä™™Lã{¸Ä3Ž£·h+=Ù`F„»½±öÑyV•7ƒ3ÉQmGqþ1n¤ÇëºÄm òuE0òçßà;í/ؽ»›3û@:Ýl† ye¡œŽº´ì‰ý+žˆ!fèQöCÞ(â^¿Ôe¹(-±yÄ^qˆ&GÛúyQÚ¦$MŠäI³• |š9̤M½Œ‹¯ÅÓºoSèc))$Ü ÐËeæ öƒùäã:}R’GRäú!Y‘—ÎhæC¸Oh¬¢Xʉà’[†´õs6¶'åMÖˆã0ó>™ò¯ ê{ÂÙª¦m¨:‹ð-Ž_«—“²n$ó½ÍxàE6®q§ÁÆÄK´Ö“!–ÑÁ©oáÀ’:ÎC¡oõ$9ïsðI»4½äpŠ$ÿ>ëî­…xµRlÛB*-Åè“Ö?œ)J…õæP5 ¼¯2.}:èZhܙ֛øé¨ù Žq¿9ÄÄ^lÕê®äæ_3ù"KÒ+£`#¯71Íeƒ½ýé̛ةÉ*ì­¡vôÜÂ(—\Ù#|œæ£›X×ÊôSx¼Ï¢?~x“¥¢±°õ‹s ËYe³ î¹O l¶“()øÉ¥çœaŸ$ù'–ÂúÄnfŸ.nÃø´} i™„,,zÂpLÁ;m*/ÀeF ¼ˆÜ÷~Ô;q§} £ñëÊ8†$óv?L™ÈÀ¢S9FS>°ý»ïFÈ“ÀîI»û“+ÙãǬà}ËBê·é[ zW¾<Ç}²¤TUÖ3æ “”/q¤­á»¯b9iøõ‚#ˆhg1ËßúšM_<‚!¦ü¤)ßœ:e»ÒîÓŸ ³Ì¿¹J⎦$øk¥Lf~:æEðM½~òˆ"ÛÌ'yqƒ ¸ÝÀ«î2Û´’ËCx©š1s'Ä¡³›=f!AiÒ"æÚÌàÓ¯a,efÑÏõè&¢%/K,§¬ ï„§ÁÆV-Y&ëFq®Ú6féÖTJgŸÏ9B‹ŸîÁ$–oîgúùÑÇîùqîË?„B›)´7·›ÕcíÉ“çÌÖªlðÿ•œòELCœ.~êË+Ê’’iÃ@¶aœÁTÊ”FÃ^!òŸmôH3“”/H·übÏ bÎVº¿™C»íÈÑšÄöh;ÞH™…ÛEɶZ.û° n í§7•û!Nù;ÎuESÿ Ç£‡õ£ÞpLRtóEC½r£2OUÊœF­²iøËfNÅëìùëŽføÓN¯Sdm».¹´ç2z˜éã¹a+Ê}! q©™Pü»Z‹Ð‰ÕÞpŰ.Šç‹p]ý>hËÿtÄ/š¡l–®á›ADL…þdåæÇpoÀ˜Ðj¼q\zœxHä>mºþ~~Ï‘åÈÿ=Ï#"AÈ¡“zøiØÚ·Ì“o‡ñÇ[Aší»­C–“3ûN@’gš‚½é¹jtQP LŸYÌá̬eÛ;ëÐ<0ÄAŸ' ·<¹„ÔÆ˜åõAäÍÎöÏ’<عCòíµð‚GjD¡œxø„ùlôô^€ý#‘Ô›>"Šr`>,ÍÆXúC»¬9¸— ]ZÃ¤Ýæ£2ãò$vv!Ö“È–*ýD—•J4_ãNõDöc%F?Ÿ]uÙûÿVÁLÓ÷˜œ4ÎÝvf”)›ÀJ”9ƒ×÷ÙD¯¨ ×Ä\Ç;KÙ ‰þLû³õä髇x.YÎŒ‰bÓÒ°¾ò66ië‘E¥… ÖiQ“9Ê,|é‡ÊïÃÎzgrè¡ J ³•Tøv#Ú_Æõñ+ ¡ AÚ4dÁG|sçúbñÎÙáÆÝ¢ìµö2ë”?0&oáHs*î]”ûIÀá•è ä¾|÷$§Ã‡%ø¥Ã ¢«pøÂ5 OäÐL|µèZëóàŸ“R8Sôõ:b¦(j&8>WÇÞíУº £~ñ4Ÿ†ÌÝq|œ[V71G>šÂ™‹P½ê%,Ž®‚—“˜£,ìûç·k#n ïϩլZæÄn1¢¼'zØL7ÞV'?òÜéß·-¯ïà ós$Ëe}Ûo·è±01jñ©‚ÁÏW1'þÌŠƒÒµ[aJÇøäsC†¯±FÖ¢“y­€rKÞâì¼ ¼3í'=¢§‡“óÝFæ.ÀÑQcù© Oš­‚ÿôÒ¢ B¤TQ˜¶šîRð×Àî;–‚Y–Jd|w*c¤•À*ªM¡ùÒ(a¹<ƒö1Îg¬€ Œ1éïÁÉv=<ç8» N–ǬÞ7©00Ó™Ñ^¨–iÅo’a¯=?D ƒ½¥Øb€AáO˜ï™nðéõBÒY;šrž’"fG³A›’@xó ´.² %ÃÓ`÷ Q0°ÈbªÏ¯ÁÁ€pòàZ8 ‘ ³Žt³OW ÁG+G´ú6ÄÕq#yÓÝW~5Ò&‡>´Áúw¦(Øúæ\µ ¼mg™£Î>ŘÂìö<ü£Lý‰`y6s3O3TbñVä—.yÿ¶„}ß›" ¤ø§$¸Äu@Å/kò‰7¹IlòÑl6Gk?cpF˜98xóó°\›Ëbcbº@yoö0¯>·ý§=æHwÑeSó^3ÚêË Õ3 …¬Yxô6+«Ý søÉîÞ6è(ù‹Sƒò¡ü·<)CÙóƒ°óRuc¬Û>\t´n–`ÛÈ6Úõö4z†l¢œÍR$}ÿ qê œUÖ&Í7g“¼Wá·­g*ßKŒæV°åûâ‘Wpj:ÀÅ{OPÌU† Õ!>éA·'}Üz^ò,5&Ħ ÈŸ€ i9R¢¶W­ç£OƦákžW°Q…öËË£f%ŠæÏŽ!~tU"ÓŸY —«œ_Ôc¾àÈ­)Ôidi$žX°Ðöo!Nù`õRkx§9ƒVzÎÄ‹üR43J¨ü²åeƒR„èXŽ1dnž‡FãFXmsÿÓO§Ì4'ÍëÈ!o1ø6s)ìhG‹òKÌBÃgð½f;ŠßíƒeOáS ¬+p®’Ù³æ+Ëû@žäßÏ9*K5UÀ×ó˜gK+Pu!b˜Û<ò¥lr;A§*ލ¿N!—÷¨€Õ#ÊzZæBÊüóÞ]Ê=q#®žBï–£7%ã7“ù°Áy/ÆðѰE ÊJª¦…axŠžî˜›£¶Âý(É"Ôüë¶QÿL\f¥caTªcç7V…yÓ‹­A:v-¸DµsÅVØ’%,¦êÐÕ~Y³ƒ­Ø4`Œ#ï®2É©fl½^7UmnM† í PÉ|ˆmç>3Nÿ:ñ›êsœÞÚŽrõ%Е›ÆákîÃîú%̧Óh ØÒÈ’K¬ƒãW¦®(…·œÅ5µ­` £NVÙ ¥–68sÛ%gûMäÕ{ð:S®µ‘ôêl`²õ•©Õ%c*C·G™Œ://›·X” >ÿÃýiq ÍË«ápÕú{žü3ò¥·Ž4ãû:pôZƒ×Ö%ã¶µÓñë«éôθ8á<„¿U¶Ä±?–uù¸œ¦~£³Í"˜'¤ÈÝ çàhý:& }í½8nÎme ÆÕAv— nF÷³Ðuyè¤Ýe¶¯—¦Öœ¹ìŒ6D]"Å3ƒ¦ÂK}N×U|ን#A´T-š6ˆ3ÎOÃè{Ï!بÃЈ ð¶ G 2áÍÈ~° ­eòþÄQå¯ÛñîÃþØ“;“§@Íf¶?Óýl0 ` /Ï„ çvø·/Ø·³•{qÝízæPÖ?þÍ,Ì7g%’ÂÁ}d„IR›Ä cÀ»r/¼ß|±v r§ygÛXðñä¥åÇ Éž‚ïL÷cÛ±8šÂÔ%ÜKr`Å#1òà¸.ꮇ‹Qð¬gú¸Ô‚iðV}‡uŠÁIè+´È€Ñ»tNÑaYÈy±{ñô#X”=ocG_b1£í|Š-#Û7-aK­»ÑÓ­-VåôÖ¡Ñ;ilìf–†ûÂù$#è-KŠ>OòlÛ©à·åç¤Ó¨š^þ«@&·ŠÅA·GæåO#/ƒqVµ Ö xÓ÷rÌ¥‘91€Ÿÿqh£ɱï'N:³OÛ"Qç­Ž›<Ä›ÃcE*êWGãï9|÷Ô#E§Íp¿Èl¬³¦Ï]aŇ D˳„.2µ8¤#IPß x(OÚMŸ‚ɦï(ã?“ä¯×É%MpîÛ7ìpèÇÇùøZX˜®°!Ú«S±%nnzr"LÉáŸ×QêBæ%¶ƒ¸Æq|N\@ðÆ{<£Ée4Ö›SÝ;xG.-.Å€hn P º)šTîø Àk¼ôjÔv ÉÙPQãJ» Γk 2è'ÖI|â-éétg¶xÙ~Ô}`«MB1³ó"†n¦†Å„6[¨Sîµrü µ 7Æn„Ìt®pÌ5ÎÅ»5¨ÐE^ÁõF¡&\~A“ʆdCw{2þ§—Þ-ð–µ;8ÈÜ*_NC=>qÆŸCú{zÆ#œy´5díoijïX¹nYáŸáì=ŽÝº™“ ÅciS¹2T·ä¡“Ø2Ãr##}F–‰ßŸҟ°ÐמÆ!I›¬ÉŽ/Éð>öAcjO05½)5Ì­Fê¿áðo (¸>›¸ç¸2²>ÂÞÛÐ6»–Ñ­Y mÊàëÙÜÔ^?è’ÝŠ£gãûeªðñv¬ƒ‡b û®e7(eÇ¢Ö0[Þ’L?Tq˜éö¨6ÆGrïÉâ–Xxja¶Ö7ð—Ç&›Ü`ööÍ"åƒÍiÖŠL|´ù4ÜöhcS|ž£Ìg \áôÍ¡]Ëû_|Æçüáñ·½¸õ~6¨6BhÞWNy‰‰éâ!e–t× MÒ Ï¡vMLíoöÃÇL¶Oß?_™t¢M~…0ùD÷#kpZ— «h3?MÁ¨C¾´Õù¬rpfîKÛSÍ—Ça¤kHîÖæ²ÃA*„ä¤@~•9Y΂Ï!a¼ü¶ó¿šhÉ-úƆž~mB"?ÆÃøù]Œ™T$ ŒƒÄòÐuW†x)«mXóîlõÜ‚ªMÞXi|—Lèû¤1{’s]Ùúž9sÙ#ŸpîL-? ×{ ‚óátœcm@¥\…qµ˜"Œ:aÉÆ4ÐpœÍO?FË0zD˜êî0bVn¢îg£ÙçÏàd€y²æ4D>> ñ÷§â)‘gœÍi0ãá>lÿwË{*°ø,?)µõdxrü¨†á $¿U¨ÑöMxþ°™R ¡ = f¢·“=ý‚UЉ GgiO ;å¹<þX(B¹EÆtU•œ[L¿x¿Æ%j¢ÄœçT¼_f.%żèRÍÔv Ë~ÿbœ~ë‹¥~¤K¼ºÜÃç¯uih;–£A}ºEQsàVžÇ? ÝWÒ€#ÓÀTGŒÔœöê·¯Œ…ìöx09v÷_ü…§VVøY<þц]þ ¿ÎdòeX癤ñpsE2­Ñå—4ØöU‰úä÷§S윸û|ðiÁ.Ø!›Â,¼¾ŸJÀÖ©ŸP²³:[XˆÁç½¼‹èš ^°ë´b·ÿu6l¿.i“à§ÿi¦½L½ÈŒ%ä[2|˜Í_“?£—ž9{³ËˆÐ‹:äJ mÏÇw³@Æõ §”=qþ:£íw4GÎñïè²£„Ik+jž” t“¼5°¡þDCæäѰ¼Ç÷-&{Næýô#pmîXr$Ôr!ÊI˜œ>JE ¼æ6X|á#MkÔQõUx(ÓË YäsÍ2lH¸‹£üâäO@ òl ÎÏÄ1Êò tžE–¯}ú;B˜ÿ ‡,"B]ÕÊà²Öúvb-=©mHJ›ÿ²+fîÅÜñµd@f™œ‡âSí}äζ½Œ‚i ‰Æ}r*ý2L¹#@ߎêf;K¶ÔMHf…qÛA‰HaXºš žîƒN;ÊË—íaêpg¤bâ„iê~‡m®Å˜m/ ×òN³dçãNXg´Ú§M!±‹ôàÔWAîbg]Z@æO¥‡=9Äë»ÂD g¯U³]E†d0iôóg‚ZP3¨»VÃÌÍ™×iÑ]3¨I€™;'ûãäC‰®IÎ$^Œ% Ÿ¾a4¾a_ÏçAßcfDvó<õs§ƒVfðòÅZü稌ßÓ&ócÿa»ŠÎèÚ} ½]>âØÚ½²A—|¾%CúE°c†.:LƧÓ8zu#ºN¹ gÕRðOÏ´1!›Vbƒã ºþ@%ülsÃÞ·­¬øÇRÜqeN6§‰«cñ¬ì ê R—ïK؃äNÉšÆ÷üà°õbR¬¸†žð-ÆÞO’žN½N©cÓ<Ú–±EfR›¿çAC´§³ªèÆ5À¿Ïnbšý Õ,‚Å¿±ÇGE!ä¨+£þç «¼‡zÃ‰Ž“14ê߃–Å7±Òzý¥?ÀƒMÝpý‡6Ùü1Žô‡Ö‚PżVgÌùà<ÁîbHýÇ©°3u°1²…ùw=霗Ét î7†_Q¦[®Ÿ†oÂÉÿå~zd2÷CöÐîäEô¡ï;8cSŽÄß•ØÍ=ª¶3¨ˆO:ÉÜ›[³$¡²ä$‰[)H^¯Ÿ‡Ž2³OÑŸvRd¥™‰›­ñr¼P½Ž7y£q±W)Ú·HÐbÐ{«“÷÷(GøÑ¤sø(yÇÏãÔaärâIVvD[ޱ¨oRG§ÒOÍ£°géc¼£+ OåÌÎe¿^`Bôú091 ŒHßÃ\žªA,Ú€Z}˜9§—q_}0¾.åªsK!À/м"÷íØlù øÉÛ/½8{)騬ǢÔÅè1ËÊÐrî,´~ZÄ.«Ñ$†aG9ΖÚ4ñ⟗mh¦º7ÛV îÚhn[Ê~j÷Þ…j¹ý†Óš«`.z±×ÎŒâ3÷\^ûH’GÛ½Èɥ׸Sf13ª(9🆚]’ûÑAwícÐy| Ä"¿è&úkLøøHkÔUf»ÇbTg¤é€Šìère (ÃÒ³ç¡Jë>F/ŸOò<‹ÑíÖ’Nè샧>ñ¬è­µ$‹»û?Ý1{Ðg%R½ –m¼Á ˜ÕUb/â á&ôÙ ì±}‚²–™ÌüL1rÄx1õÚ’3~7³¼ ïÒûU"?÷³îÂjä¿jĦ`0= ­WpߨËo5ĶößÁŠ`PÒÌ>Iëó÷3k_‹3χ9ï/¡üÇŒwfb® ±¶¤Á÷MáÂþÌœîäH°ó%·²6Ö3Í;˜×ò7aw?þoÈ®3¡!±]0UPo¤ d¨Pcüñ4ÜY¨@·lv$&1DäÖi°òÔoîÇÓ¸bÏv&ørîïó¤‘gð{|8Qu%ïÍeîN‹dO±(¾í‡9O”èïÒZæ“ø7|òf6~ÚH¾ÞM^˜ISq6_%®e öá®™ÙXnÁ !`ƒ÷~¦’ÆENt>Qƒ#¿‘•M%Pé´ Nr½à.Ä™{ÈÉÈ›Ü77cî9˜zJ‚lùÈâwø!,Fû¦îÜÉN•è7Q­îÍh™h˾šåMÖyÌ…‡geèìÙ|fßÜÛðó–&mðºÍÈij_½=aú¿½º¢ÜpdÍOü`-BÏÜ€Ð8sb]²ž„šL¥yþ ?ÑÃÌ7:‚ŽŽ—<öÛ’„ÄdØÇćv| CçºЀät6ÎRœ´5ŽâøÍ.¾BƒTÄÉ9P³[—h/9ˆ²‡dá°Ñ+äP.4ýîO °*Šîkge;lzíÉÞ;ÉÁ÷MÈ9Ek&ëÅžœweÖŸd³¹þì»Óï‘s7“>z9,jN±5S %~* »>àX¬9ABòOrެ«7 _#‘d—é‘1uê@K‡KwáÝȼûjÆ6ÃØjÛiÂò$@Ê™ébûñAóà‰E«×šdU¿mOtƒ.¥Úwî>n[¥@{:U˜FÛ NèüŸP"¿¦Ö®ðD2ìYÌ–%h’gÇu0s$„xÔs86ôÔr5\üÊ€Jª’ré}tö¢Z°ýp§é„1yóîÀƒ­XѾk°õ˜?´‰6ãÇ*SW%‚]wâ6Þ èþì]kCCÎ(’¾4è¸÷ >:áÓÑûlÌOyª5û–=d·¼+Ǥ™-0½ÜŠØÂúÖ1o|D“BTâs 2 •X$O ƒ»P2v›É£AxÙ"ˆçŸx“E'ŸA¡Û ¼ô€ußFo¸à¿Í%ì+«tÚâIüûZ 5Þ3Õ;YxµÉyãºN¨q.œjŒº9}MÆ/µ‰±GˆS‡ c’ÁFëÞÆ J©Œò -ˆ>)Dn¤ ³{Úµž±rÆ—A½j?æ‡AáK°¥Q‘l\°‘-ïLÃü²Ó©AçßÅ=mÜÜ/gqr=Æß÷dO%ä~ª0ºÿ]ÑpéÆ&´R²Â;¡uìÍuñÐê¹™|häÇa;_êòD ^/¦Žöq€Â'©Zr0;¯ã¬ü=‚n_—2ë³›ðÌN²ôd1ßd@îÚý@Þ÷Ÿa°ý&¾éþ‡¡MÚt|Ô‚f”µ:ÜO,BÃÙ©xÚþ É]jAZàÂ{®íÌ…[Ʊlíæ8lLV¢"šo˜7_K˜!šŽ3É®çáþµv\©d ®D&b‚é~»‹-©ÑƒïUÙÈêÕp`Ù&M]ö¶þ¨Ô¿‡}k+BÄ¢xÉc¦ ÊêôÁNóT\“%û×ñ C’·¬Œq¼0'½C{ß)´w/§œr½sÉ;¯ Ý+@èöAÜfŒó§i ?DZâ6ΜOM{pÓ¿{xÝè/æôß•o+`µ\s½H,Ñ)-e^´øyˆÓâ2•8mƒüÕ'à[Ó=–ßÕ™L©P"gÛÐêÈWáG¢Aßngî>{Â:犾óü¸p¿2ñ_?´hÿ ôse /ùî¾–xg„sžÉ‚k9gÐxBŠîYµbfi“ƒóÊ!yY’ö Ûs$È•T Èz KjL&±™±oyކå*DÁk.èå “…}?.áÆ×ž¤¼*ôD]ˆÎzT_𠣂-êÍâ,(‘XÅW‰‘óÖ0#· o}“ ]KÏ@ÒëØgŸŠ¢Y³à»H‚ƒþõCÄò/ýÒ Âí?}õi°¸%—¾NP‡¯ ­¤MÖ›Šíz‹(3nÍ ˆø±ÌüÎv»x‘Ø;r a¾ ¶^×'á 7qªª(UŸ%Dþ‰#Y8nÍ]È Êé;>åÍÀ3çôi‹XáßeÀ˜§«ŽºæK•ù3•Š.¡Vi |C„Þï£Aœ¸ÿüWÈê?‡—>E‘¥,®_»©…ãþD`k*g}…K]&heùý*ÍOÝújvüÔ% †µyœH¨ø!øúÄßP—™Ì„¿À;¶lùlBs„iorwãAÕŒPl/ ®ö!IΛVÌoeSÒië@kûp{Ã}néwEpn;™µ2œ,L´'‰ïR¦¸¸%㊠äL‹2QD[Mµ/Ѿ‡ºd<9œ­Ø·‚\LÙ$K¹9Tûü%YÿÜ`<™§Q 0®:*ƒÙJ)ÎÑ}¤5Dœ”L½ñuõ(=45ƒ>›3É{—[lÑ4ó£nîDÙZƒ¶ýÈ )kxÐÄq>³·î4yö.ƒ¥‰9>Û…”=nb†n€Ÿ½žä‰f„nn¢ÆÈ·ê1©´ádÿB®k~²m„JÎlâ×”G3@Š´å{’í®j¬ÔÕ×4qå[ì¾#éXï:ûÉÖF*ð[JêrÉK ±/qnO7äiÂ,å0Ò¢†þ”‰6õfß½Çz°9Ùÿ‘]=™ËµÛh½õ#®uxœ1rÀùŠIr¨0¹òk:LÚp³ÇªÑÝhŽw%¢³‹¦%ư×Yvizt¿iƒqÝi´’gÕ³BÈ_£p=j½ªY ¢Ï‘QœN¯~¼„I²Ÿ;kû@…jU4‚ëUS˜Ø$ò¤àÒítA^2=¼í,î*ïDÆ4 /Ìã¥Þ×v²Çã[``Q (‡mY°©-vv QîÁs¸÷e,‘ Ã¸©–žô¡bé`Î7‹êj®i‹/áà«,®Ç ?Åäbh÷1§zZìô š[£F|ö/#×õmIö舾>Âêµ>Æà'yX¶Ö:>¥²y½" _Žî£¨#·ƒé•¤Æ–$àÝ.ö™ÜcûœvkÏã­å¯ñ‘NÈE¡[äB²ù”9È3Çö‹“L>ˆÈÜp½./^Ü^€[j°N2™œ¯NÅ‘®ÜC¿}LÉLW3ºsØ•º©DñEqxøÌx½ú¶“{óÍp£ÔkØÎ[LÕÂb Ф:&q g ܨ@›fÔq/œ%nS_\œ¸ì|ËrçíÁ;Ï$@®î(ž ÀÀ p2x¯^éTÀʇ¶ÌÆ1\?;z'¹™dsDÿÐDÍËk€÷ /ùÖ»ö; ~Z~°{¦ 1Þb†©sƒXÎA)’r{ ýoËN–x'û.㤂ç¦a`žW²ûå(ì= W, GÁ…dq̸çŠí„ÚÚhÑ6×Ür¢–qEoÂ<Ÿ§×â töþ¦æ·ñò´6üxç63_º•˜9Ùøåkü8•J¾ª­ÅCÜèà'ÎÂ쯸yÈ‚ü»¿iocÉí"Y†Ÿ‚’ºÎZ&vÉ[¯Jg¾šGÅ‚}§gh#Ï{2â&Æžëx;âža•ˆ:Íã=ʼ™a@¦ÏС/ê^cŠ /qrH`%WfýJRèÅá/ŽRŽš$® Ñ£AŠÁP´?Ÿ¾FŒ}£D;ïOr¥ê:¢­§B»=«aoòûGÖ~ÏÁY·ÐMj³¸NËTA寠Cç1Îmóûè2ð€M»åKoìQ'rF"´çå82’"pÆs3Ä;XRÆ%dþd. ïÆæàaö¬åV8hÿcŸva…G-(zz㮵‰xÿÃ[Ößp¬ü×ÊINiÇàz²ôfدÈ!û­<¨Y§"½òð=¨àFæ,büçHÑêy…eé’G&Ô!jYo«GTƒ×@ G‰º^œK–ŸÒ¡3\±æ›9Í\wUeæ3ÉYBä~\2éÚ$DÝÉ~¶3¨ž A‹+0ó 9êF~Z&âažÛp3î*nüNV/ f¯ßšcÚþpÙd[)­ÞúÉ4Î6U½f_ݰ¥+NÿE±øH˜™\…çžM ž‡ /JežCÑür ÿ¦™ ÿ]YÜ7†²‚að®ê¶u©Ðó«ÉPÕ0r¥é&IX­SBå"i‘®!•Y¢Ëj| 3üO„9a4ôß,ròižÈO£«Æú9Ý|tÛ~^’ù:3O&Óȓ۱óòKo6•üÔ? Ù"ݳ\oƪ´ãÁÔ \é|žeî·±—gbÔ’$"éŠã3b袠\O†0êûz¯æ«Y«O7Ê;‹KÕŒŠésdÙgàt¬Ã×ɹû¡ÖY€\mrc&îˆYŸèEø7ê3¦ÿK¤ËmyÆ™}Æ? žìçþV'»+‘{)†œKÇui) sú–öÀ÷½éº¨„mcsR¯ bcá÷²rÑ]•žœydªÝhœ =y^Ø ‚Ûß³©‡v1{Ê…aßû\vÛÈSptÃZª\G¥•¤~Ã-&qÖwÆMÈ~Mî¹Cû çl½/öƒ±ùm jÞluÁ´ÿlás›ùÁ× [lCáãË$z3â§qáK¿­y­C%­cXÕ—7Ð%¯–b¸D7Ë¿â+¬Z…2&ùÌç—v„žŠîmª´Ãu-ݲ9•­GE¬§‘ˆZ,GôÿŽ|ì8‰…èsø,IšH+N—¢ ß/æ`—&™—²•žÖ&7#ͧé3ßÂÙ#póWŸ¡/xO0Ï¿4cÇÆ]ès.ºQbÞpÉ _Ìràëš$°ÒÍUpò¥öæÅìÄâ øOS] ¢ïg;P'Ó^Lk‚õbµÑ± ól—‘÷£¦I$9S¨G~ym%:ÚULçozgÔ›óò’†f1L³Ý…–ÓÍÉBý¿ìaCUÑSFé)ìÖæ—ÌFpœPŽG eM¸p`C ÞÎ Ã[A XéÜ@´½/CÌKDIï½—h.ô _­;É4"º’•0|\6Ÿ†SòqåB~ªu^†tçϤy·zàí#kf~ÏQ¨=†­QݰÝCù¾þÏ|P9ÍHeÈÐeFšŒúC"hLÎ] Çÿ¢1GXƒæÅåqæÖ.ÂFC%¿ç@לÅS‡ÁîEÊF5@—8WÃ;R³Ì¤oÜd¶M» |ÇbI—ãZÏw„ Î0ÇŸ,±l@Œ$V‹Q~¼ÆI÷ñ¿÷¼ë·‰Í*+gÞýz7ßÜžùñ¬Ã˜5øËÐ×)áЕc°Bæ9ÃÊú[¹„nW'×ÛÀw_^ê·+r?Î)¿yÀñèAˆãÞôÁ´Wo!Õ:“~ÃY¤Éâ^ã€æaåHðp<¶‡5û#IÉ7öS9äç>äÃͳLÙ,¨»xë ÕÈ..XkÉ>ø—‡¹VóÉ…÷Ép9ä6™™D„e"_ªxaÔ½4ª`p†z.¿Î|¹^ž]«Ly¡?ŒÒ£MX›Eyåµ!òi!ä^·$u‹¬a·¥NÆÃTަáFµQ/ßFNÉøÿØ“2eT|0…!Z¥V ƇÿâVN>Ô _f‚WáÁÝÆ$h](¨¼y é"Yôˆû=V%èçHX@,!B×ä¦qÿð%ÂúÙOqÂû=ãt)Ä2»¢æÂŽÌmœu»€r¤’Q=Ô”ÄýÂ#Í’8ÿ/UŸa Þª¯Ù@Óþ‚ÿ—dHNÚŠw^ïÁM¹Dsã6to•ÇöIlvù‡äMº€Ô Û‘,rÖi&gñøBòy€œÓe©1ÚÊmb&ëÏœ¯êôÛ¨6)œeAi?dôÞþD#Á ¦Ø­F{ßCÅ %ó^3­#‡XÖ>žáΠ½Í¬ÖªUÔvY îÿ>…¼qPa¼;ø1ŒM ƒrf¥8̺ú—ÝÑ^ÆØÛ»õŸ°A§LaßÈoܪ«•Å:t—µ¬»k^g)î?˜ %¨Mw«ßâzϼ€·Ðw™‰ŒÇB]àœÕ _Jœ±f'ŽKÓ´ÒM}ÍŒ½F±\æ«à˜ÉŒ®xñ$2có”Þ] É!ÙÀ;>æÒÒ9’èv\}ÈϘCædC•O$¼[)@’p˜•{WÃÓÒpFÙé{øâ=æèå/ noA6Ûè–‘‡ p9£}ƒ}3(G|ïCÅœ¥Dbü#.ì ƒù}ú¤øS»þ8ŒÀ˜[ £žð¬ß‚zþø„5¤œ ÕC×~lnà<ŒZñ ²Xˆ˜ÿ ½7¤5nì×@§´“\Ÿé)è»× 2VÙPÚ”<â×°/ †–g³²¶Fä\}"Fÿ‚õÿ¦À!wMæNï ¼™©ÆhÃáù8ÜnE7=!4ÛY–ŽÐÓ*LæêhÔ¾=É™*D©í‰xn“úQÏJ›oЇãœ7“¹tð<–ô‚aW–ÿÌÁé²,[ò|?&µqÙ<ÄT6nuÏ"Ö$0‡©IaQËo­;L¯ý„ÕRÓ@pú¬‰-†»Ž‹˜m‡Âéñ¶"vj:ÉÑÚÏ^©¨gªÖu9ü½|§aiÕ pÁSw,‹År/ š™0çLD)oÅÇF÷¶pú09­JíÏ–1ß^?cöèŒ8˜¥&¢°eÇ{?̺ô ’-~Úç,!œJ«ge(¾žsýE,îì‹®“®±‚Š7†Hô;a¿ÉzWæ)ö^yîg%¨¼¸Ñ´™Ï¾_8“|êy»äÓ!þx¡.O†º“½º ´GlÏ”«ßCÝT3p}â+ü=q¯]obn-™KÃܜȄºõh¦7–@h´y½Á´»è„¾3ZÚ Å×$hêA úêj<ž\vo}LçÚíÉ€÷A{Ø×ç“i×ÖË8Sk˜;¾² C¶}`¦–® «Š_0¬s ;±Ó b³*ñ= ¤vãQ2ãs^±„´ibm]5Nt)³vˆìGl‰Ã;¶ø‚óœ—VóÕZå!t™1ØYDoÙJÓ'EE¤8/ž=y6B£ïÃ*m†ª½¾Ó‡ÈË2ˆ ¤Ny){™ÎX'Ó²`c \Öço@}©)ª÷AØÈö|â ”SêÅ",º|«$/â½ôkxþË'Ø—¸>ÞѤ?“ØšiIèã»3M¬È_Êx+§ ¢Õvɽ×ð¶Ì‚þY´˜xÆÿßGòó§͉…¾“¿Ý³1XûÓ—lâƒò‹0Ks­:<ÝNÉ0Ï÷(´û=Ào㓇b´¿ü >Ooo€@3l週é-˜m@hg/Lwá`ZPþ0¾æ.çè8Ê“ùoÊ0"Ò{¦™á Ô kž¡WÅÙϱaˆŸž/~€/¯ŸGOÎZ`k A ø”YÈø¯Ä×å™f—iøÇéún½À¨½³„E¼Öhzœ‘»˜Í¤¿­˜t,ñ…z£|l¹x†#Û2ŒÎ ¬¯ç9f`ÏEˆéµ¤·-·ü_wìhO;¸2°ZúkêyÙZœ¡Ç2Y‰è~ªÞ.ùÈJs×4L): 3Ç®ÃÄŒVââCöö„<½>v*®†­›åÉ7¯c;K¬=²Ýòû>¼6»ÌVzV#ïüwú1ë3(K.IÿeÚʺ™Á—˜˜¢.ØÓQ‡°—LÝü,ï_G‹Õr4PK >$YbM l=;•d$Ýë꓌Rn>žkœä.Ëî`›E×bžñr”Œ‘ ±K”ˆ×îeÜùËBØ«!‘8|4˜ü˘ƒMÑç;³Œu‰š‹{bƒI;s븼–‡YÝÍÝy-²ç, ûf]„O–ô‚¤§"Xø*Ê0½è ŸÊ…}ù1øØE’†(e­-~ ôž—†·â'¹ÜrÓˆíXÿ‚í4° òUO™ãÅ1«óÊq»ƒ·G`u½1ù=ˆ : w»9-{÷ >ñ.ÂQÿËàï–9ÿÀïŠû¼z.yQ§ˆÅ";qóïPóМØEz£ñKdãd“ñíšVÐíŒc» ¹éß·×p1iõ…¾9ÑX·ú,¹S¤)® ýVž‡g{-Ȇus¸ ¦¿Æëÿ^ãÊ‚®Äc/U]SÃìÆ÷ m㌡÷žc˜ \YXÎX6>æjb4„¾¨eÄ8ãÙÝëÈè@ áû>šExo“ ±Q„]¥Ò´Õ=•U5kfoµÄã¡üHX8%–,ù'NÚ‡‚‰áø&zqÛf¬]à~ak(¨ñ“«WãGMƒ8w°0^2ˆuU©Ý^'Ê_n‡]f…œ©âã˜c{Ïð‚3›õ:L޹*u¨S8 …–[Ù³ý4øÎ3ŒP›…7õÖÃGÕ*œ-Ó I^¢Ø’4ÉÅ9×`ä]*Œù+B±ˆ.¤÷T¸€®7ß‹ÿ’Ô¨BÑQ¼Âõ¦Ÿž7â‚X :RÖÙÐÖ{•»}…Y»m% ^É„ì ,œÇ®Ú}“y' &ÛoaçÊûøW-/>}ˆ'üÛ°ÕSèâ¥V^ä„d>,0¾ ï²Éý'qî‡Øœ±—Ú2á¬Î¦/¸Ô¹†I˜uÜ¡ªJž³b¬ú á‹0ˆ{K¿ýb^?þ–7Zñû†¥0jñ‚I‹MG•ÏrÔaöivÁƒùŒrŸ ÉÌG!Ìyóü½ö0ë|Û™©kÏÀ›)u°Â®–³¶ …k¹y*ýùK™ŒÕÈ÷àßÙoØ·i7ðŸJÊ݇᥋ ¸rCrµÉôV5Z~O€¤ííÇÎp)âxX’LÓM ÆZ˜÷áOqTê3<¼ Ðqr5–¶ýiz û;¨LîéÑûê$¢ú“öã;Cƒò°^¬—$¬Í…ËÉ£`UŒÉÈ~Â:œ`W— 3Z¶ª \ߌ«^ð3'n‰“ä¹ÌÂÞQ¦{|€Ó£Ñ‚Îeêøþúmæ¾Z1tüÂE3>³öŸ¡Þˆ)\ÑУ³‹³ Õ|ǫ̀NÃK¶E`âÊKâ]×aZàÕÿúGèW:K²Ç¨YE`¾j x'ÀÕlô”Ά•Ë2@çe“îN{]øáTy »]¸y?»³½;7çO7îŸÃϪ é ¯ïv«X.Ûw´ ߊš’Kæ2dé­AVþ•'šŽtÞoÃæ­U܉¤ç_4|Ëfôö–áŸôhæÃdœï-!;3™ û&yfœQøëFN¹®Å_@²® BÿI9e©T&´æÀŒ~#BdÚ´Ø[>3—Ë^¡Ê¶ÊIü”câüWC–‘™Š—Së7¨É„ƒÚcX˜Š‰Þ¥Àë.EÞmT ¿ÿcªS­ÈŽ~ê)õ”ΞÆ+A×ñôü;pdg)ÚœûÍݯ ¿¬É`–¬lÆÐE£pã´>Ñ´šBo_z€f×e蜅ÅÀÛgD\.„2¹?MaÅ¥mLcÉsا(NÖBØmýO÷£œÞî4~r³¥úiŒ™B*dºÙq¡lÝ-NvULC»Ù±—Ê$kÎS˜£% .—2iVÇ'¢¦_À´o »yÑ`¾=~Ü3¿!ô«.5Z°š<93uaNÚ,(²V#NG#Àq£ï¼žåÕ±‹i™9•¼Ú >¿’Ĩ$Rnã‡2†ÕŒì3[ú‘½‰^ šûá˜)QÛ/fþ"´jE;uO¹ÙvwðX¤í´ð!G¾4Ò°o[þëOÍüDfËN~„ùO9›;ÅöˆE,ÙÂ/­¨Ôâ ²EñsçRæÓ>cÂåS¦‘ª·ÐrÆLŒ[ÏÅî%9ŒG×>2[ØͦûÉ[:¥&š<’¦:V²#ëKwO¥âöR$i©Õ«:CŠd(Oÿ/Øè8À&¶Zc÷X=ÈP+öÆt)Òê}Âbw`õê6í†È«¡µb.O®¢ ×rpãá~¶Úî&syHµó¾ÁüÞ}Uû«ˆÍ‰3ÜÒDuRý!š+c’‰‡ígÒÄhE±‘D­Ô+è°]•Äà*öV—¹ŒÐ<ìQrtXy\qJåãý«U~þÛÛq¼x nƒwá|£ÕĽ{9ŸydÁ›Ž¿û "§<ÝÙæðjÄžø*ð3F¦JäÄ:Sòª5˜ä«ê0]npyê‘OB a••è°Ãg×g¡®åÉÍ"ü«NÿÕJ,tãqXòN¤ž†9ß÷»Y7 1À?×,©ÃÍT6f¥<]bq™éË:Õã¥8/†Â>îZ`>œf¾®ÜL6?bz70륧³“k1S ÁYýärÊ\âñÅ•lJÝŸ?$€aü-üPø _]ù §›f+…UÀûÑ’ðF+ÃèŸA(_ Kÿnƒbç»lTôUZ¾ý&{aŽ.}ÿ¨ù—g±üËåñÑZU„Žæ1&Ë®cìH‰™ÜÛ[ƒËðGm¥"Ý6ä?TóîÉ34—Ÿì©¶ ÆâЗ_в-‰†k¤ƒ}JN—¾‰O`¦—C¦¯d?ì‹¡ç…çQ™MÌ[ÅÇpÿS$ÙÕö™Ýu  ½K$q0Q·”4¡ÙZ[¢pv5­øÄja¬íŽ!sŽâ´ÇâDZ²K™úJ˜n¼ä©Â ®gF4ÍxD¢ä4{‘»˜´Zr±8'nïÖ#IB»i¿˜â£‚7Íp¹.ÉÌ’"!BÉ™•ˆ1àPi“+t„=y)HË*ͬyº°°ÑLï#è]‚Ó#™yZøŸÞ¹ÛˆÂ†j`wjÑþ“/ÆÀ*Ž5ŒÍ sùˆÕZ+6ædçfYÔ°žÆó¤Bû>¤äGÙÌ€T»÷ð$.`ÉßMsÈ&ꬽ€l«KBߨaFÛ?~c.ÝÈ$eÌ#}UôðØ>jß’B¼_…«•ÇQÍJžjü³ šÊ'Àh§•Dn«S‘µ éí陸ãI1;SL“!r^¹©ôŠ9ºÌƒ¨ö[Cˆ;žkkAFjURÑ$ÓK¦£PÉLX|ü³÷÷v<šœç{ ¢3/‹—`¹‹‘ôKh49j†¾ºÀ’‚X’»Í&9Åêw\æÈtœí³ˆ„×jÑŽY•ØÙ4[9«ˆÉýô¼Ï'æÇWhx†?ÜÉÑ”F`|ãéñš™´ùƒ7DpòQ° {Ǫá|§/tûÞbx&vÀpŸ;½yð,˽õˆ{ÞÛ ë ãQîðP½ø„û¹DbòÀvýÖæÙ"zþA%BÆ=Ø/vŽÔäáº}ÚeÃ)’"ÏuÒ=1la‚*x‹q`@Y’0éš ¢s%ñ&Ôoã'¤õ2”°"rQJȦI²W*Àj4‚n·V$¦ÅëÀ¸Gf ©Ð‰& ­u”½^° ÷Í£cO†1zC-¾¾¯ )Â4?IïúúFDëqod_ã°)w^ IÊKü°i6ÔŠÀâÚ‹¸SÁ’z=t¡«\bŸû= ©~¿¦{Ûš¸4n˜Y)B¶–Ì¡Ó_L!Ös¦ÝNY|/] ÷ÓØž¡ìÄÎPÖa6‹z£üTLÏOÍ¿ìàzö"¾ÿ¸:EסvÄ4\’`@øŸ8ýÄ”š™M²7phV1wð~‡WM¿;Eù<ܸj.ù ‰ŽÇi:O³ëŒéëïºdÍ|úJw܍ޱzjd¹h;OžýNOiÜU×箦fùZ¤)¦d6+Ò·rqíCOªûÍF¿e“¡s³¬áŽ$«QÉã¾dÃ}^ú3dÞž‘Èôg-"ãÏ`…ï?ööUIëåZ+[àêEËqí²Qð_¬Í©7Ðûh=¾r½Œ¢´9kEQOV­"i_“q×o0T‡I/¦Òu^¥¤v÷wøé<Æ4´q%2qÝá‹àá¼gt¢†ê[XF‹“üô&|þÂK€´ù5æÞßD:ã½*ŸñAGÇYœÀ'Ðèë hz¸­®ïÂõÇÖÒÍùàzi•¨Ñ†×_Þ4fÞ‚}bûÈ2]A >¶œ…ÏŠYl³ú'üEv”DŒÜ9‰«—yã鬩dÿÞAüÅ}¿çÀ+³$úf!>ô3‚¬=õW ËŠ/“áQ …F™]ÒßAG?ˆ<~?Æ{W7Û`z…jæS£ƒ1Ì<«7x.í9\ Ë–•‹àˆG?ã±Ò¹ÿ= Jø7Bö£teO!^OÚðÎòK°Ó¶•ÝŠ™Ä“{‰o& •]A0ÍO׬kpX,hÄÙA›ðsÿEÉÒ+mÓH{’)r?½…C—©¯íþ(@I-$1L(µž»Ãd;QÀò4fj2ï9[ N¥ÞõrW?úŒç /AÍïGì‡ýs¨t×_ ø»TVƒ]Ä™¯Ÿ7mD¸T‡ ¯çÓÕ«:XËor³,9ûÇÁË£ ÊÂ4ˆøï3ŒÑ‰ѯE—ðÁtþ#†Soþ׫žM§¢Æ~TGÈ×ëÛÑêòP:§` {ú÷PIÞ÷Løsaö[+/:ËÊ_è?–<¹Í$¢-Œ~øÉÜ2ß̪©Î¤>‡@¥q& | }Ùôž½ýnÅQ/fŲòßøó7ü=Ë,Gè=Fd³Ž@se%6ðûÒØ}Ztsô£ƒÈ/t3.?î'´ýJßs”0ä_²[èUý;ÌC©KŒ¡¼-qß~…ш¼‹¦ƒÙ9U:TJ`'»¡å;¦›¥¢Ï2Ú×OCÜ3Fç_³ä’0yÚ/ÿ’‰zÎ,Ýã'ÖÒ£åˆ~‡Ì¨~º ¾ù€&>B¸võwèºÍ„u%¯3æÌôÄoðpOÈsN¿º m'pîy*ai€vühípfÖ2Ÿ>‚§ÊFd{¨9¿c#¬[‹‡>7.íb‰køOÃ\yÿ6¾ýžÆÕÞ¢Mêõ¯cíkè;ÆlòšI9`±é _[èNÙ² –< {3³û¿í O^ösœ ¹ì9Ûg@îÂг‡xÿÎÖwÙ¨ÚóËú¸mylމ)R9€'¶T1e~ÉÐƃ§Î@S¹¬v]CßýJǹÕÄôÍ)¶ºå”_e9Kÿà®8–±°‘ùiH»ßî¢n+faú„9õï£SÛš¸…“µp»Q:šÓ_Äþp6,ù…%‰V¤;9DW˜ãc¬î ²N£6Ä&&œþþåM4å9¶-á“)”Jäl„¨×T5¨QqëºeDûÉ4ºrþ{H™W®Ü$ê/S¢Fà„ô ByW‘QªuS†ÿDæ†÷ið{šHÄ;VÑö¨,X•¸˜¸ECÉPEÈS[EßmÝD¶-ï…R«3Ø©ÔBÕùdˆ±Ó¤hX’då,LXÂG]Óéøl¹ G7®ïd-¼ÞÒÃþVØHù÷J‡‘2ôÔvâ–]žK~Ñ_ì>ÓåðèŒjƒuÏÀßÍÇØ¦°¹tUN䨋ÒÖ ¤JP ëÕÜÈÎk³HÞïaX÷Ç^ÞÔJ§¿bçh iP6#±ÚºJ…>ÿHæ?,ÅsÁ|Y8TJ´Á2¦ 6{GÁZêô~1šý*‘˜±¬BÎ]‡Ã¡–ä˜õ%èÎlÇ×…¢dj¤ݧû3­âÉ; R”ƒò¯²ˆ£ñbw;œÌ¾ËÆÀØ'‡4jš»€z'þO7Í90À¬ÙG£8s7UÎÅ/k¨ÎBC†?Ü‹†…‘©ÖýàÍYÌÐ;@T~3ZË¿ ÍŽETÔT—zÆÈ’ñRôÓc o¥3‚ð)ÏH«ÝÇŠÃ)¬É²Rj¹Ò ËZ’RGcškάTP¥ÇûÐUâ&zç8,Ö¾ÛE\ñ1'ÊzŒýBv¶P6ù–ÝJ¤JíAÔÖ„U¯!f[­‰fT+»ïþC‚ýR— /r ±,AÔ"Yê¡IÚ›)ß5øé[…'ø¨la5]=Æ®ZÎK4磪…äÍ5 Dqš7 œäkêξTËa9ùôl>ÄÞ’&§ctñ´„8ÕÌ"—×V"c»œœÍiûœBh·9ÏöÈ/BÓ5XÛècäÂÑRÒsÍí×ß$Žª_؉±}äÓ«HèØ¥À¬Ý–ÖP(Híe¿ ù–1O·c«É÷\2ºã.²ËÀÃo¢!ºq7²6 d㮡]ï2h:ÝêK‚ªé3î3ab[gK›Ç-iC_­úíBº b±Nâ§ýyY?COÂÙËBT6±ŸUÒ>I^¿_ù¿UgÆYâÔ\Y“l¿:ú]Pp^ŽVÆÆRåyÄëçpSŸËàåµ"{¿¾ÕõqÔ7ÒU–Zd~@äï6¢ÿõ}ö˜„Ê=VD&·ÛKÁüèEôÑ.c¢Ÿ¤AvÝU§³2Ó‡»¡øiŽ8ý—GyªQe8…Å~£ˆÞUð[Öˆ/ä ˜¬‚Ôkôˆãbb’­Éì~¸ÓW×£š”5‰opoýYP¸ÑŒIíZnÀ“¿Å0´ç7¾˜É.çpJ|‘Ù“‰‹ž±…¤.ƒÒƒ÷XÅú÷ÜØH+”°Ô† øè³¹“XïÊY,¼oN\w|e7™A_ïb˜¿œ1g[øOsü¸«~ÛGRµ ^faÉBò.\‰ûö­/-ÑhhˆÉ`Éæ<6äõtœo*DáƒÙ¼ãºng1®l·u²Kƒ/CòQŸún‡ï¢Àð´Æb£Ëp÷Û¼¹þ ºÇpÏþîÅD>aZÛ} T=å‰Eg=g‹µ ¾÷9Dê‡ÒôT2È—‚åï/öd|ty•ãì÷]‡¨úËdVÀÁdv2µÎ¹_ü‰­_$Êì.ZGÃ_˜áöŸÎ̺ú1øgU„O.õ±›ñHÔL¼úc;ç3ª¾ÓÈÔ=~xô•6Sy:»Gý÷f…¤¡“övKßfÌ„³¸Se%Æ0 ¹>npËŸƒö-§‰”Ëkî j #¡ûFö…0·ÜÑÂ׋h׺zvö’8HÈM ¿éD{ÅÐjÝʺÏK¥ÉÊÍðŸ†;#,Îî¢-&Ð;UW=Í%3ào•d°qÝAþýcÐ)î'ŠågÂ;z”zKg#Eào^(ÓúljßirrƒÂs^âRÐ=»-n½ÆÂÊC„kAšJ0ö‚3}?TÌ[íĤ‰Ps<=úh€1 "°^£~Æ’çwbsæ4ðþJ 5VÏß…Ðè%”÷EM9›“ÜõLÍšxìhö±S"8¹æäž® 'Oè›]©”Ô^’Éð“‘Q…"|ƒÄò1½?PB%¥§ÓŒ>ÔÉ^´¶•3Sëbaž%yÿ,ˆô«ˆÐkÞ1{:wfÙµU¢Q7ú'>Ñ"«žºÐœE¾8REǤè©ù}´«vò÷ý;!áð5r+^׊Âëæ5ty’IH=O7½I¿[ ôÓŒfù›_‡ÉÃÍϰÖAŒØ{Œ*'Wžm¶ø k®€”ÄlêÔF–îMÄOg’IyVõú¼„þ§^3QÐ:,B¤S SèÓÉûâKôŸ«€Î¼ä¹^5hOëÍuÓàÖye²È«t͈Õê8ªâ'Ìf)#íw´žPÝ“fÄ1ï³(h4±_ùÈÚëôö‡MäÄ‚iÓI’Ü[0.mÿ3§Mæ]Á(¯L|ÇËIçiBƒû6ÑG;Ö‚šk=¶{Ÿ€Äü®aÍv §;pÍÒœRFÛ Mò_Ÿä`U=HYg¯O#ƒËuhgÀ>öû¯Gãª4£ù#Û÷/Ÿ¿ÂÚlúYÂ÷ås4ZþÁÜ»ÖÐúx#y\ÙŽÅ«ì‰nã'ÖíÂZª³…¼Æài¶¤iôj>»Ãv5¼ÄÒs Ôj,`¼·€êq¥zŽ]ÿPŠ™Ïð$q¥K¨òÏëÌÞ¼SL®ÐVzŠo æV½ƒûô{.þ5+íïI¥ô‰ŽÑ"yxfÀq7U2¾ieÌ¢°ÇP>]ìF[ó®Ñ }ú­#ǹÙÆ9àí†ßá„S5¾¹¬MndCëA‘» ¾¡1B塼&8ŠV›‹Ûq4ªÛxæq¯;ì ë/^cyÜOsœa`ñZz(ó8IWûˆ %éËÍ“¾µofBÞÝ dðŠ9u•—"¹þøæÖc´<ž„|4¨£9M,@JkC‚P+ ÙˆQštŸ ñ zSÞâÔv|©*Yå®Mœê…­·$ß|,¨«iã¹ÅÆ>˜ gàé^]r¾\vÏ /¶Äa¹©µ?Š_-©Àû †”¤|0\ëK¯ê’ßê`o¸ ž_K‹ÕÉÿ,<ô´þ][Ÿ£OÁ);zvãc¸ûX“ž«ØÌŠY’ó í¾4#ºÈƒê­‡WªœHõZ÷nñœŽ|ƒ¹`cøvã;#[Úž«Åsêñl93Ø A—ñMž»& ±RSˆÖÄBæã™ß_„Ƚ§Ïù\ÄÅo¯9ö…î„Çë‚i’M±šIæŒ6¬UV 1ÝÙLMí\²ær>2:Fà±5F×È;,\ùô³±ÎXËdÉ/ ŠCat¬å4¼ß‡ÚœlÔ½©DVä1¥U½œÓéE³|zA/ üXóÿk‘}_&ÑÿôÉÇêØH·2ö_¬.›šP‹>ïSY•\ºfî ô½»ƒÝ(® |˜Ž9Ýô÷;X«* ~º•À{Nx# aäƒ=Ê~UD3=ORs..j¤jEL.K¡ sIÎë—ŒDÙÅÃ,ÝÂÏ|üôŽ·îgw+/ͺ¶ŒTîå£mصÇGðé›™(æ¡J›,<¹’™ã³ŠéôwøÖ­R]Š¡rÛ6L¬£S5¢ÜÁH€© é§3nÿņ7§<‡ÙÕ&xïf$gku$d~R&ž+Ô0mŸi'í˜ôæ-Ûqå6ßU…8mºû¤}PÝËYc«ì×ãæ×ÚÌçêØÆ3÷:×bœsÇ(Ö†DÝóã®î$4æ˜:, e Vˆ¢št>¸È ïQN ÆŽYpÌ'n|;:Ó qý;S˜vRÎbÂë)¬˜Ûo˜Û}˜2ë^t± n ‡-¡ó mVó¨IÊL€„”ûxªÿÊ=š¼‡Jñ„!¦ ìÂyˆý&Íž»ÐŘÃ^¸Äµ ¶ôÃ¥à.<>ï$^…ã*þ¬¹C8(ÏOçΨqE‡ê ؾð VàÌ»UÇÜÌ\’ýƒrªMÄiíqÒ¿À’åÿ#COžÜ…þ&º}c.¹»Sv²ÂK2ᕲ&šïs€S÷3_oÕÀ®ŸFÄî­G.mÊkË0¡šÞôŸ}'LÚ€çƒûŸÒ9 YuÆ“*"sÌwþCEaò†½fçgîKáv¬Œ9ò™Ÿè^ ‡gSå;hÕY¢ñx-Î*݉!<áÛ?'ö¼Ь´@¾jºu¤ªÌèPÆbZ«ÿ¾“ÿ÷«Þw³ªÍ¤è,á‹$!,•½c!O˜g±´Év*ù¦t–‰ 9OU†²iäðOF‹È5«¶(µŽ9ߢ@}OŠR¹‡Û(ox&·KÞ„^Y¼ƒÚƒµ'.ÃxÌ †ç?­Â+®®Ð%ÑæjrDô‚k—RÏ}5 ìÛR\5¡ª>è1ü”ñÐLØ7ª°T<ç´íxÉ5hv¦MôÖw¶¶—<&†ð¼£•› ©%WYÖ? R ŽÓ©T˜©OAØÈ|bR ì:?¼uí2Ɔ$]êæŒÝ€tµ ’PÿC¨Þ[ˆ©‹gA×ì-dbç0ž¬’¡†b;ÈT‡=8ՌÞ ¦æ/ŽÇýT³ÄØ+~D}Ž>Q?D<ƒÐ`ÖMV½£JMé¶]ubïEUŠÏ4þ‹XL‘‘ ÁY;Û±FO‘²ŸÐ a½ ;è Ï#%ÈÂR9òs¸ ß•»âÑû_!ëogKYÞ°íÃ×Áë©å‰Hæ¨Æ-æþ^ðê„3AI¸i¡©È|…?®HÑ]â”çü;dÞ V™.‡‰o˜z‹F°\R€Ê[C8GŽhCŠíæ  û_ŸgËÄãìÉ€vÖ®¥„½ì×cw`þVôì½Ë½½«š=鵂Ižý<t±óYÌü¸ 5Ý–ÂêW`Ø'Jtwu0³ÌrðÖt]¤[=˜ÿ̧fÀ?+jmäÀáÒ]ÜÕxœNk”„o%7Ù jê $æ ãÿrPmn.ð_⣵p{­‰*ßËýGgÕ×Åqó˜YÆŠ4(‘!Ãs÷A%J’ Íi¤A™‰ÌS ’!¢BxÎ>•ˆ4ªÐ\TŠ%Mzû½hüçǦڷ{°àb'7¥F”•ª®ÇŽÌß(àÁf¯b!]‡Ù„5dp² óš«Èd1×¾U„W5Æ\YÌúÏXÐÏEܾoÏ©’Äm|ùE–EœU :úQÔ7=~ºcò©´jbÚ{úlÎÎJÚà›…«£ ¢_86”ð­®NÊl¾t' åÐìYÍNË[^(úÏÛÍ­‚ÔyzXaUÈ%¬¯Â7·.ÒÀ™‹ál74IzÙD^zªýàš×[x$}×›!UQ‘íˆbÍëiÛ¯Xò<%¸¹²‹Hî‘dX̽ÀÅÑþà½zV©@iÝe¬|Ç-Ž˜ c–È3óÕ8]l*”ù"7·;öš4Q«ó‘”[ •9¯°–‰CwÕÄÿr÷í0Àà jž¦á¸_MŒŸ]Å­‚FÿD JìÃÊK¹ÚYîllÀ%”nžLJ&Á=—ÓöÂAÞA}i¼7ÑÇ-Ñ^ZŒ/[s %¦tÞ£5“ñ "§4bÉ™ËÒ@S„ÙÉÍ'8óŠ ôÁnA›“ñ§¥ ¦O4E”ñ ƒ}[þ¢[ûÆëœH>¨”@b¾8›÷•Ks;i%Þ) ãºÿàpÉlhˆ.ãoµs„Gë0­ã2gð«–zœÁ€moÐòS-Nåk¢eÕ(z9MÇ…½|tÉÖf ‹é»ãlñ4ÌS>[¯G“†o Äè—I¹ö__ešlüôsj`¾ÒAœ yxƒOÖŒe‡ÆÝÁ§¦áòÏ'Àv`;¶ „cä¼3°pµ8yR^G¿ï#QšÌÚ(7?˜^€¹ ¯$»gÆÕþ¸rb÷´µýÆ Îj—Çø‡Í¼ ƒù‚,ôQ.åþàûb|H|Ùű‡ñûßl|¶Á…—í§Zu+YÔ³ 6®*¹-¢©œåŽI ‘#;k }¾‰ù®” ¼ e“ìɼ_¹ çX^’´#TÖ«’Ë&8õk®LúóÞ¦ÚSØtä SŸ3ždnÉ…þ߸ÇÚ\J Ã1»z µR2nofÅŽ¦”äyxšÞx·dššNø憾wøgM.½7X΋sÈçrÿÔÓ+³Êhσ[ø*þ"U0=Àu»š¢¹îlJ„ž¯†"3ÒjÜÍ5/n×ý£TÍ}˜zV`›v{r¹ÞŸ©†Aή̃÷UÆð+ûf~›ôo=¥mªçÁãÞs°p8ÖÍ&–ù–ÄsÒjì¨u¡™ßòàõÐ[ KÓ#[w‡ïšý\ïvm¶Òý-Ä­âR¶áÂkÊðû&]¨y×rà#ªL~lÏâê’½¸_‡™‹÷!21ä,iûs•n•ü‹ÈÙ`ì= ¬oÈ–³ ÇÍÐ-R„üç‹nJ€gÞ°dC<}^ßHoݹ…›RÖ³yIWi¥ |uR'“šÒðO¢oŇWZmœÆ‘-Ê£”›xî¿Ø`xù&(ÞÐ!§4hý2QÖd, L=¨ÍØ2ÜЙƒ’xºBûhq÷3ä~¿…Œµjpñà'ÈöÇÀ§óÈÑSãØÁña0=}9››Ër[·õ¶Ýo3\8~rÎïj(ÝËÇÆ5&L,~Ìj=î¥ôædq¶o¹1›âã W­'Ë\FéÖ)± V:úhvQ©Šös6 lóý3LüÍœ  ¤ÓÅVL(ÇcLÙÃ^xuÍÑsb ·í$æ:'iïÕlýñ÷ðäÐsX±Y8?n„s *Ìnûfüг„MÙ!Âú÷gÆOæ’â>iÜÉM'%ùèÖèÛhò÷2ʽ>Àªä'’f·‰\ËÊß𺧔k½¦NV\°%;‡Ë`TÏ»ýÙ©Õª¸âa·lK³ÚÃ=sq¾}*èëD/ÑÁoâ³™hy‡—i\˜+QJÛN'¶±ü:-µй†ã^ú“ëÙÇ¡ø˜?wQÁG—~å—îÏcñý#mràÉjÜ;]ú¶¥¿œ7¡žIúö·AöÛbüy|:ÙëÜw1»ÅÁÛEÙhñ"€Øn…x)l©D nQ}Ï[ô¾½7ùÑšœçرg)2[ ÿrdKFrõsîÂÜ9!pú·*Ô7¨²:3G2×J{šŽã¡ç¸ {W³¿»ÖuÊuî¦1zQ|™ñÀê@vIq¦‰Y˜^Ë纚ƒI¤rªêóø7º'‚~—<®L­„ÔIЏsf7Ä/Õdz¼Å¸Zç oG\"ʯ7%+÷œÆ¼¨ñdï¼=rv>{`ŠöëîÀ=ןç¶Ž›Ñ²‰[¿®^n÷ÆÁAp²'}—Cqš çWsû^>½¨}¸UHbÆß€mz2Àhà˜Æßt¨öt·ž§vú@û;ðþ±µ/aÏP…ÍaÏZH¦c˜ïÜbºíÜ ø4å hJÿàìÏ,ÇÈ=Ç ÷é¸|UY&AO÷þöƒ-ôÐ'6Su É=!ÆUÊ1«YV b¾š¶žÍ[ÿ>3/ùFƒ¢–‘ò²3(cz‹ .À§H'ü•BàÙq,ÒßBnÏ£ææJÄÁJ ®×ÆÃz³ÉÑÈ<Òl Vƒþ¯[8SÀ±Áoßøo¯’as¢pBŽ»dz ¾¾6 AŸšéů ñq"ü=2Os×>î†ß{p< ”ú¡ê¡ÒÚ b|È$ {-bdaÛ=(¹WÇɸ.ßâð{™/ãX#/99|B èÁÛ•Ø:n'“ÃßùºžÛ蹎I½Ï-\7ÂI°p“«íýÅ´A}¹Q[µ‡Îp¾TŠÉ­Œ§~ñuÔvÔï/[kÿIW½4 >ƒøBA¤‰BdgPþú+ÀLk¬OÛB]q\†’qÙ*ÁÄT[Û+¾Frž‘Up× …›“” q­ÿž·jº©4ª¯pgB+`Ï‹@ø6å¤e©“ÙùpéúFœµ%¤³â`ÊFG<´–Ç´ÑÛ(bðÛÔ7|s)õ4µT"f9`¡Z$;÷¹‚móxÅ”ß=¦nÊà{­55 ඃ[’–yÐ}׳Ùp®ìý³"dÃ&¬Q\H²–Bãú(ÚtM—fF§;ÀÛ½˜ÓÌNÿ©T±ë5”¨ÂÇ+Ø6Iöú£:¹»­ óŒeXìsfÿ òc;ñˆn q=a†£¯$YAWœ\p‰À YìÞ }mRäAé&löfH¸ô‡°·7”qNn š—¬`þï[á¶gDKùÞêÎ>çHÕUriê$b¿õ˜ì†Áö 9î"~o¦n'ØúÏr\ö`/hãÜÚ³ž!ï´µ¹²3„Ø™j{à&tŽ;Fy:L¥qºÄŒfôX¯ôß±õ¦=0V߇[ùâñq“g•Ì¢³vÀ™ê*ÎJ| ÑÓºE¥ÓÓ1]ÿ)§ªG’ªb‰ª:»ìy2\g=M²0cö²èRÑ­/Ërœ> mõQø2lJ! [!Ç.—¤ÒOÙ5 ÿy¡Õ`¦~TÃp?üŒ*,zÌb[¾€èæËA®a'ì0}YuÌ/­3BÙ¢í¡R˜ÆûµÄ†·—ü‰ºŠÿPï……£zÁÞg»»gA« kþn¨ÌÜÎjÇh²²Sï!l¾óÊäž~Ö# E·Éþ«Ï¨«È™šÈ€$ó•á«ó|R}²d'²ÙA ˜¼ŒV÷i“Å–¡“k*Ô)ëâï9ò„×­M”cµ˜ú…ZÆŽhÇ‹ÛZï–!Û‚È’Šb,XôÔúæoI6¶ë9ù$¸ÍC®á·iQðý%Цå‘âÉöù™¢Ñìzr–̸T éÉÁxf$òÄÈ8«zLv‘Áà¨pSáOÙð<7Àt޼†EKK (v “{’FÆíÇÕ†´`x ÙU®„1©{X€n!zm™…ñ§ô‰ó­ß”Ÿ4ŒÓoœíÒ%xÖ[}§Z/»òiÎÐz<üî>>ùœKÊ[~P®Þ©ÿ¼Ÿ÷"§[À¼o¸wZÅâhˆ86ûÛ“5mÖßSŒÙ]yXä¯ÀxÙ[ Â@h&j ³E0ÖÛMõG§Á’•РÅ<¹=!`5ç0ä÷lôÍÚbÖ™ ?]| ²Ö„}¶Ù eññ–}dÕ$Ò´&&¹Ô@ëÄ\ï9­‹¨›Á%ô‰~õ…ú­•?ài!ó÷KÆŸËKPèR.‚‚:{¿r6zõn!N3^ãæ¿h"”ÁØ’˜¶4«¡`V.Š,À%õÑ5¹ ®ŽVàòF¤¬þ4}ýYoÙÍ5…ÝqŸ,QbüùÁ™øù^8|Ú‚¿¬žÂþãO©Ì—4ç*DzE1íÇ1zg·ɯûÂM´ØÎ}•xŒ.Wÿâλ‚¸ß¢—k‹ëçÕX[÷nèsÿÔ`½¿÷„7SÞ¹g°ØÛ:ã ×´v)±™×ŸBÄŒ™lÇC;X¡\'NÅ⊭þ¬³fÌ"½yxr˾ÂQu$îf)Fn? Lï.ۇ殴¯ àŽgŒa?oj1ǪE¨ñ@ŸsBù¥…K~¿*>(’ˆ~n·D 'Ô7ƒÍYÍIœ*_åùÓü¾Áüû;qXøÔ·À'’aäñØilöÍÓxÏo&¾iyŽ;þbõ‘G`;9Š“æ3þFè¸-†îKOX.Å>iä•,éÕÃ/¹îx&`°ð”¥@«Ù½¥Ÿu^Æ+ÎälÓcúpœb~ÌîüÃ…‹Kb¹ÈI¼Xƒ‹å*NdçõuAêÜ\ì­:Æö,ßFƒŸ¢ø|>¼HL¡ã-ǑԧÎlÊn/ü|þ‹%_gFµH¿÷V8mÿ fuÇãïO¸pW FÕ®³íË EÖ #oRRÁw³:3X¤ÂšÆß‚e/‚©èÛ\¬r‰wûü"—e'­÷añí夸öÜú@…iN$â"¥¢J>I”“/»%ˆ÷ó‹<_…ÉìôeWjÏqÕò¦d¡¾îÕ5ƶ0fÈœ- ’eWÔNa¶Õ´½ø RyYl»ÃrÖþv ®ÜåGò/Ÿæ5ˆMg—è°UB -&ããå$óI:©—<ËÜÞ¸1 E;ÌßÁ2áÅ7Í«ƒ gcv¤ í«Í]±yêÕ¥ ݸUA¨Ñb!Xqš#ù·“^W µð=øc/º<ÄЧèœýÂu99tѳYø·é ›SB´–wã&¯]({â¤/X‡î Xµ1ѲO&¶wHrwx<å7î búË¥lÿøžƒ…'ºá¼Ç¶íO +:Âmü=ƒEM§¶G]0d4ÙVãMÃê¡kŽ Ñ®„ÕëˆÑêtTϰaQ/_rq¦W0·â(Ë! ¡¢hP1‹Xm?Í^293 6º]o&%xU&î!ã ,o Ó+›Îf¬m!Çv‘¹·ÇãüW_¶5Q¿ËÄ5ÕÁæËÚ*vu·’mf‚4ë<µŒ÷èu{–– Žozá¡÷yöõ¼ÓLšÃÅuÂi¦óÙ®ÕÉéGjìüLCÖd§Î~y‡Ãü>¦7Šç6}â²²ÅY–äLÈuÕxORÝÚ‹ñ%àxn QTéƒês"äuÍf1DòsÁç×Y|Ó€A—öÃjÂ*]7ãfýdÝÅ]¤b³Ý0°œI”D¥‹A r°>£u «·÷†W_ŽâƒÄ>h"¹x­“lŒ€£÷´‰vgª-¶Žÿ‹…Ócˆâ…ãZ{›Ê–—[”ïÍ­Éug1®ö•[j&ÂNE¶à«‘7¸r•>ÛÝmGÜ2`ƒ\4¬T/kðÐáZç¹Ãý°2ôº'C®±c7öÍ€,6<ä6<|‰cϺsMcd@¯1ŒI¬•!opyýö<7i‹s-™3C[ÀÌF˜5¬œR  Ÿ&ÀŽp9¬¥Ín•“­Ï^bËôÕ\ö!³'È 4)>^ζ}M…U;„Ù³“Úø_¬ï )y ­r0kv73X—›m¸žË™"ˤ¥à噢ljV*Œ~bRïÅ89Îg.Õa½Op†øu,O$®éܾ¡åøyø<Ž$ò ÄË•‰‡ô<òV=#ʹÂÚúýŽðêø=ˆý2ãî2›|öz²!n9½œýH±‚gCQtI³\Rz û´}٬ߞ8Öù"¾ išáDÅjÔ≦ ‘™uïg ³—vã‰xæ,®éZ$ˆ L‡ÀÚ °¨çH^f¯_Φn.`Ÿdw¢¾e¾~y›LA+ ³¯ ‘-ç„ȱmøe¾ {ßö‚“sHÃ^T*ÜÆÍmdãŠßW7n%ò?ì›ÊSÜv˜³‡–gqýƒ)L–˜³]äq'd¿%‚ÕlL†ÞÖ‹Øi¿ý;§Á1MšeŠ©“aÅWà7õSöÑç+J¹HûåÜ™Ù+YhçaùU²äIÒFœ—>um?L>=`§r“臌 ¾ýžxù´E± ¾©l‚l#1 gJKQÙâ8\ŠÂ«èp´’IM b7Á‡E°¾0 6÷òyÌQl!Ãz3ÉÍ·pìÍ:Na(–[ïFîŒL„§…‘èaÕZ¹2äwöx|¢¼w=€¯äȶߋ؜­ú$L׌¨M8Ë^oíÁçãqÏ·%þl¯ïs°ºªG óÁúV'Wûh5ÙÚ‡¥ÇsÓÙIO²åaD~+᪇ÿýq¸§¡€—–cF c¹¹öV˜·òWþ[„ïÏiÅÊç´¡¬׉ƒgÁ;¹Õ äFŒ4¹®O¿iâ·UEÐìæJÆÞÇže'¡ÇÜ{üÛUéðqóü |Ž^¢»šK`²I ¼¬ÑÄ +;x§_AQÊv_¹¹/â &¶ÆÛIKvÀþ—Zt©3o/¯`G§yÁ¯³í<§éÒäâÑ«œµ’ y$t=’Ÿ]3‚ãe†¤¶W ¼‚7p+_]àRšÏƒ áIœ©!3o%¡u¿â£† /Æ ·bº>£Ï­ß§ìgÝGÛ¯¹œ½x2ÿvJ({ö$“˹„‚ƒ¥ nW‰sÃŽSIcn©—Ð\'Œ/`çµeÇ™¾Õÿ Nÿ”mdžß¥POEUW×rVŠO¸ÎS÷Á=,²§Q~i6§GŸ¡wóoš4±6ÍE³<#¨ëׇ¸¢áý8-âóA“ÖÎdÉJ°dÌ^ÛÈEΰm¯ÿ…^gß› õŸ0•ÛSYFöBnrú?®^ uË顼H¼5«E.ä@]ê¡ÿbòª–x“Ò9ÐéJÄ{·Â –ñ×]“üy`¡ ¢~@ã²bb$³*ÓÛ¡lú?Ý?ÐùÎ$x\Laâ­T|R³ïx+S«)2[Ú ò7œàÝó5Ea<ìs[D@0™‹G¥OÀ÷pÒT‡Å+fcãom’ùp{}-{³òNÿc”Òé$w÷0hïÄ ,Þó'ð<îÜ8Ž”çÅÓ݉;§:7÷Ãѵ7¹Ég¾Òé~Èõ‘%ÒÓÏðAj'ç¥H.ß[ù"Ä`ÿ踑Î4&¤~Θ+—‰ùQÁ¨êµòÿï©yé<@ãc«à~¸1éœßðª$™ö\mÀ›=|þuQK0Þü ÏoóäJç"ô6]äÒj0îÁ-nÏUwX´­ ,·›_µ™ùõ t‡·®{&DòOŒÒ–/ñÒ«°¿ù§¢1+(m„ îpöY%.Ír¡7êAñ—î@׿ŠÓŠVZºlDñ »,û r\Y~@76f<ÜÚ g·Ñ„ösœHoºŸÃ\˜g‚®»”BöŒbûDöb ·+3éÂilrÔ*Ö `V=GØ…[¶®v'wEÿ[UÉ2Cæ’åÛ¸ÛyQlÚ‡ì¾q<ü¨£œòëVQu±£Àäó!kx'“ÊïĘÅgØî(: ª»{hd÷82Eà.ºœ)$Í76ÁÖ‘\¬ORÆ‹Ó-É™ÌRäåïÃs*9§Û…܆‰ú43Û€ˆ|â ê>$3Jw°…û`âö$œÔ!êŽèµÁƒ->¶–Ö–±ò´4Œ“x£rát;ß6þín P#ÛËÚq†Q2Y=¥;w  \Cg«ÉP>ãöü´%½r¼˜»qtê–B2^©tœQg–³°«ªÄê RmC²ò[¾8u%eLåZŒ—`ªª8¨~?r#лW(U°¹…]0í¬ ™çÓ‡QŒ‰ÿO¼ÛiR€r9°9nÄŠ¿Ä­?'³?b›pu‘{+¤þ:WÎ&5á¯w|Üz E…Vrv ;À}¤M'¹ãÛéâLT÷¿NÍ…¨ )‘E“½¸{o3{S5öw$˜_'h ‰¦ÌíÔ Ž™› …âð)¦â²úPeWOžŽƒ¡ŠgxO…Çì o¨>\\Á }Žœub…WXqX€¨Õ/¢™0àö!¶b`#ýåb‡e˜ìX'T/“‚'Ñ÷yúâfœ gÌ\ü{ †®Ò9´r Öá+ø¢4òLraÕÏ ºRn”xÈÙCcHl»21¼öŽÿ@Më—gà\b4~ƒÍÃÿõCÜÀ4|õXDS¾Ê†s»Ç¡ýßÀH8•5Ô¢~±[w°+±ƒ0¤ÌÞí×åôv ÷ô˜=£˜—ôß/*ljGF¸3"©DåéxòcÝ(¾'ȦC,$•=±Î8ÖÉMrÞ ýB.¤Ìa/ýo5g“(Næþ]Án=­€uRD°è"ÿZBý¼° 7,ÜŽŸ<Ó\Ú9w€JTFÀ¢  úbG; âíšüô…ñ`ùX¨ð'j÷\Ь›4Š*Â×ÁkHu+íà}¥[{¬¿‹W2‰¯ÕP¹í:iæàwmuö9`"q’ ÅeŠžLk~çõbæÅÁÕÇpZÄŠ<ÜroÝu§G3ðÄ¡ëp3Ê›”*‡ÎY|¾4‡GlBR;…=I‰Ç˜ó-ðp}0'¬ÁÄ¢2ùïÆc–gìZÝ1aÅuË&¿j¸³á>ØÌ^6´q+ƒ‘i)ƒ®†$95ÆŠ|߉Ÿ0üÔÞÈ­Þuž~øƒÑA f-‹_t€Ý_ Öõ8°o«}Ìó &Û Ñ¯]ЉÅmÄßoóð£â|P Ox à«» Ýö:Âfí /rs Ù&?„§¯{Á¸§Qá’Ñ|lÅ­sL¦7nãöKª2Ÿ·+Ècÿ$?·Ð _&[gÍbv…ÿJûX¥vÁaAm¯…g´´XTزücœe« „ÇwR#¢H-@¤s-ëK{þ‚ÓÙù)“˜G_Ÿ7ž“Ø C§;èù)yܼ»+‰œ@'ãÜÊi_ø„¢jðNI›~>/m%Ã-ŸqÙârx¯ëÇ->õŽ{äÉÎ) 3çËÁ\ð«yDæG*°µ^ÙðYl1YÖme³ã¯6¦ÉXÁ•à6:'X–„Ü3—œI½AÿÌTKûq–ãqpÞã ûÎ…BßœVüý± ¯Ù O×a¬™ " >¡£X&VébK|ÔŒƒÔKÞ0î};Ælµ†ï*:Üœ¹'Q¨ñ¥F5¤FÆ€R«¾ÿÝ#ÞœE5PŸÕŒ­™üèJ¿€Üìé8ýÔ9è?ºy³}>…¬zo‹^Ž'’ª§`Ê…Aʦ]µ«ð‡‰ Œ ºÇ$†*•ü·†Pö|.Øíô‚%ËØÆsvü§ë"VndsSDØùøÜuÁõTuª0w†cM±¨ÚÝ„:OuëvÙ?Ĉ àˆÆr«hÌÊŽüüW“¦±.QK”)NâÚBÌð¾ÕFþÂshÉ‚ "éx>‘aQÍÖp½ž?~ø&hH›À«öƒ\–P-úòø· tü ®íD7¯A—I Ž%a†¸%:ÝüûgŽÐax½r.Лƒi­ `è;Tvh÷û‹ÙØÇÀ)r䥂ákÏÚcÖpñ=G%Ì©ä×­tKŽ;žàeðÖ'~äÜ3ÁÂ?5¸¼ð ÂÛs°úíað{eK¾ïb£t«C0Gº&(ßÇË—ƒŠO *܉Åe›þ‹» ´™žÐÝá=9àr¦‹/F¡ã'¼<äȾÏÇ º}஌„âGk%xR¥¦ïgâÅ5º„Úâò ¨MÕƒÈ56ü”‘󜟹Ùsƒzõ©ÓÝaX®À§*ô9á=UØ¿` ü1íË;±V+gjU”|N{£›²c×óŠ[ Lw$~’mÄ÷R0Çù o¶ü+ú®B—ìâBgeÀƒç‰¨8q<ÑêgIóOr©w<0Ê2 ´— 7fp?eÉF]',p‚ø#iô~H 矀µê6ÿöG ÑÔ Ÿ\BAÀ5$.&õUìÇÀNŽ_­§À’鈢4Û^Ã’¯‘¤)²è¡TRã{Ø–`úB8¾rx˜>ú§ñeñâÐ6Öuä¬[MZ*öaî´ÇÜ••RØ\æC¤#òé¡ERÝnA,éCLÊŸM&ÌevÇѻی¤ü˜…ëO^¤ÿøZ*˜ÁP¹2ZÀ ðÎc×òëÈ·KdM"ÁœÏéo <* /zïâîWÁÄ@›95#UR›pqÅe¯éìYl]ô™Aï@9(ê®aß{…ÉŠ J:æÈtؼ4~õñUlîàD•+8'Pm\7†y-ãn¬(§1á ñì˜lášsp¾?wpZü¹ui¬¾áÓÒ&ŒßBÔa8±3ÍmâüëS|Ÿî΂dé:X”î 5o>✣_éfË‹tËe}ö¶hzL–†¡ƒÑà¾_—gk²x‰½0ÿÆ4æe×Boá){Rm ‡F¬?Ìu¢³èð=}{`Äõü˃úú÷`Å”¿xÉG.xS*o•! ?kÀeüKth˜HrëãÙ­96¨Ö‹ ן­F0Q`36ø@[$¨&Ý„”£¹œ X8øÍ˜‡E½m ë8†özD¦Ÿyøúí5V~Œc]q£›Öb¿èxÝ­À•x=†ÖCÚ¼Ó¸k<®<êñAÂìêô| @Þú}GšÿÎÿ@ŸËÄØÖÞÁÈ©"dÚ!!²î.n_÷VCd!\‰ ÛS¿q æ\ÏÔ›ÔyÞc𿱄^Eã¬gÆhn±t%p ixÍÖƒ/ Ûì턜ܗtËy=–)r™s‰:«VãVõl®3q +y•¦U:d‹ñ””*@fÜ@}è:œ€çÃï`¯ÈÒY}R+%È••–˜:¨L~8ÏfV ó`³¿:;k*ûþœµ2†œþ~üœ– o’baÍÝbð~´ûÄÛx?‹ sd2«(ïâHh» ýfDB”âÊ +Îà….ÁŽÁ}ú#´…ðÃCLù¼ÿLØk¾½¡­ÒjàËÏaE ”È ’sùÞ‡sª æÔú8à%†ùÇÁÔÿêÑ 7,Óëçœ=.ÂÑÙiü±Ï"á“b¬u«Ä°áB.Žƒ;óú:OB®N>rEÍìt4 ô…±Ï$¸ªB#Pu|‚ncöã@—,›\lÃJ.öAý{`Ó€Ÿ"·ÑÓÆ9þÛÁÌT„ÙDÞÁ%Ëðúƒw¼7f´}Ég,—׃ãz·¹˜C§¡h–[XÌÍoLÅïƒoQféø)oÆ | /‰«ÂSxaïLbï À?0çà*Œx>‘Èu‡ÒR…`Y´“Vi)²«RæÜ«/–ÐÝ×ruƒ. #fÇÙö‹ëÐÑ07±TµF+P²î nYíHókâÙ·ë’tçêV³™ Ðö”žß†¯v¤ÙH© ²ò'Àã4Q5Œåxv;1ËÔ¾pËÕ~p_ÿ­å¬¶f°þÔهϟ~æKÏ÷¤f³÷py^ÿîûº0Néj-Ví…gkñï±!Ø¢ÕA-^-aÓï[À&n«üܶïÏãÍSqœì¢<,?“ì~{ |„ •È’ê$Þâ¾…,ßT†KõPaÎqFôNÅUL}µ’>k\¢wC¡%ä*TN'g¸ýÔbž#tQnÍÒHi©_ð~§ü­tâ¡öÒ>8ø5˜¾–ô¥'§…’Á/ìm€4sÍ*‡AKÇ͘`ÜÊõFÝÆoŸÇ‘øÕóxǨµÌaÜþ¾®ûÊ’¡Úü2•{œ¼•3oÀv–‹u}¦¤Œ_oÖߦ{çÏçŒEEàà÷~ºÇÏ,†¹Ÿ.®œÁš2“­%Âý\YŠžbC`g>‰«Ê ¤×¯NÃÛÁbDþÓuøí> û‚„ÉŒ'*,1;ŒœŸÓ\kBÒdç@]Ô(¶–ˆ³-7¿Ó³w`ÝûxÜ<3d_ŽÅq1_À%t¸!÷ÊLþîÏ&8EM–XÜõäYn@tÄÒ¼ñlBÂ8"H¼ýÈ'éD!žÄ˜ çÃ×ùÕ ;¹ »M™ÀÂîAÆ"äìwóÚ£ CÍŒM¡ÇGîál6ðö%^ÚØ ¨ù;àÜÄ}s‚>®RàäFá¼]¼Û:§é Ë°·¦cTØ51âvu+ì}‚GŸŒ%Ÿ,Äž!_ˆY(I*¶þå]‘KßÃçAè¢×¼ä-æ m۩츭$kZ5È¥òs°¸­ÇXȱSKŸ¿³ >îáMë°!jé<ð¹{…>õÜȦ×÷ã}ÔÑæÁˆölX<>Äv¼±½«uí0ÇV±ô-}бˆÔмC•+Sñª!ú¾}ÔÌ'S>„swì¸úÏWyËÞס¿¥Œ‘Ÿn)˜ó{;²Ë§A-á5¾Òü½·œÕ¬qí¸m`ñ(›ŽDYòýýÇ3exÃEß{'Û’ôÂj5rËAîLjĦ)Å™³_ NðÕõžÐ‹ÃA:ä&VïÉâM¡3…*ll0ªµ p®¯TÙ%¼t>…GÛ^Cÿþ¥¬0ú ìýâNþýŽ÷7ð9ë§Ùà!½ †¶j‰9pú³ùλ×d^ßÐ[a™ƒ¼óïS-ÒA¤æ*õÒóƒZÑFÜrû#½–/ƒ£ólØæí50gÛ,üþ…C•‚dÖUt ¦_ÿpÏç¡ÿwÆYýéÄT¡¥hüYÆ/ ®‚~ô5ç˜H¹9ýP˜6Ê_{Rì¸[ Uó°¤ÛÜÛŠAP* LzB”Kornwø}!t6™§r]\ç,eì=ßÓ%/ƒf€ûwL_em#+gŒ@Ç`·èñR,wŠ|¿gþ\ñYðWÊ.ì$wn¥¢áUM.öpLø«wtÃQ†¥±c™d麱·ŒNžOň3»küÌç,%~‹5¹¾ôÔß·–&eÂñý°ãû Œ¦Ïe÷u7‚ù“ÛÜU‡Q4ß "¼$úËM–\ꦿŸa?pa÷\®sAùç›1wîÀ¾mB¤lç ~øú…8ñ×:–¡Ê#¡äùØ{4Ë÷UCPܨ'¸á¹¨Ï’'?6L'3…ßàבû¸¡S’¤¹ëèú£[˜IÜ1æ@Ê‚ºÏe…†UP!Þ‹j&'±Vü4~2çNeEg[a5-‡ýÞyØ}¡Íyø›O„ÁGr ¹W«ÇtöZ³É†vÌñŒíœnƉNxÃc6ETÇá4ÜkAk㉴wÔK$Q¨AŠÊ Ã÷ õ› S_Ÿ?‘5õã>èLcÚn¹¼%–°)m#>±xƒqì9¯sûdÈê ÇâÈ;°aþC4u6æ¾T|¢_…nÁóƒÜ„N¤êù.Ås ¤Xìeƒuãá¢à_úBf„ö‹G2‘‡-¸N6š´cý7ßföóÇxr¥wëN?j¯XŸ^ñˆÝ VšaÇd×¾€¨ûFDiP—ýL”‡á©0ß¿Ôcà¼@XuSe¸8f¿Ù›Û=LJ•Àd‘!úWÿ‚9‰Q´ý-Ȭý Ë<Y¢×Mîíi80ù#7´ëjö›°»ç‚OV!7éôF²;b“^`‚wŠˆË-CøûÛŽ¥µ±ªi³‰íÏýñT•§31AJÃNðùs~XÚ°oiŒHÙ$v 4ü4•Hû«7¹8Áh˜ w£óVäuƒöå8X¢| <¦+0Ñ0eV¶?›©òì2_‚ œÄUCñðý™dåâ…8ÇÄ]Ø;ÞL¦¹î×XÊÛ[:Š÷<ÅÉ÷¸eë õpéæ6Ò&Ê|+²9+q¢¸¨‡îȲÕNkaËs#’²ÒV¼m‚zxîå*)¾å„>b°k f\=‹©;ÏÁÏcC|Çëp¡ò >ä>OÉ£'vŸÇ¾¶T(„r/ÂTpòì1ô–×›’1LIöx-žBný§ºð´þ±ÌÈ>]2ö¶#O–±Ð9cȺ(}öün2kHžÇJ<¡ª2‘蕜ÅÿÞ{{¹G¼’_K ·c§þŒ…éßÛÐs"‘è‹&yËá ÜÑaãoÇU¹¸Î¾™ôâû:a³;“—>Gk2B¹)}KX¸0RücÑäp¼±Ú£Áö./Ä­¡U7ƒ9Mî៵`Ë»å™ÕŽc4_)‚JÙíPî’4Üþ…æq8uÕòZå’w±Õ â@ÑzÙzn³û©‡6á×IóÙÕÅ2d‡’$¯hò(;ò÷)åÍf¾—²Ál+§¹Z¾f§LÍ8ŸõÖ0¿9~„sßÇÝFg Åábl:1+M:Äé“„½Šì=Gg—¯aöóÂ016—ºmì›…Ú½¬ÿ×}üõc½ã}‘‘a?Í&²ú\vàâ#P›ó&üãÂ?ûáÅÓ1,AL‚[t´•SÔÁ’Ÿ!o–/ÌÀèrlóºd¹¦t±}à¶ûSPâG$¦‹¦áØ^¬5ëw|ëYk“bqÞŸ-”|rjlOÃöB,¨aýþ> \³šù~{«K×¥Éй{A*¸°è*WOµ Ûe,¾ŽÿÂ_ø ÅoÊ ðùÉdó>ŽX.åû.w¾`‡äØ~dêo†± á½_z¯îáä=¥ˆïøyl“ÈX6M;–ŒåÜÏýææN•".ÆsHÞžƒ rÔ€Ì[ÏmÍ3†4Ó—°ôo¬Ø2ƒÞ¿>‡z.£Ïê\þÛûôïêËð~Á PqÖ…«µCàié ÷vEÚ´Kt=柞 ÷/ŸÄ4•83Äc:äÀhÕ3.êûc®ø „Ù·½î&Z!RÕxdWÆ©ì@íêãàXÓÄû¦ÖÂMúæÃ?š°¬Ù].w§#eÅÝ/ƒmB¸Í5¹8,²‹½®¬á‰6«“À¿¦°OÁ_œ?†ËEØ»{&xuz¤Ëe7nrí:œÍã¬WTA§V8ì¨XÅ®ëÍaOß>‡ÖoèYv\¾É±ª¿ÅhšBÅâoÑG[vâõð9 ±@…éê§úu7¡o· óHQ#}3qÛ5Mbšs—¸”ApW·Öo2,>”ŒÑ¯B€° -tÀ>U˜8)ià_Ûxbe4¶”ÂCèÃÏ2rè¨Úm‚ƒ+Ø3·—ðîá1\›ßŒÏf;ÐͶâ§uNìB¥&;«VÀu¦z@ú7øþ`#]ør ”êlGÙ\þÕ¶u(;àÊIW?Ám7*`¦y9†}—ÆýÆKÙÓ£ö`¦?›Œˆ#2‡=™ùÎ]Lv³&Óy³wôÀ²Ø\n›ÖzÚê‹Õ·1ðŸŸyÅŽ,©¦“@©©’š,€À,RòEœ‰ŠÍƒé[,Ù!Ó÷‘]ŽlËǪxƒJ–þl‰ËYœð¯N= Ѓ•fÂÐS¸…®o¹Fç˜P¡&lKÊ Ñô$!‡¶±€Óর u•;Â_ñGΧ$à£EEÐ;¯ƒz5àJe7®kòR¦ld Ôæ<¯á¡ÄE{"µ;‰¿mÒ.ì°{²Õ™´Ç;3 }â¯KÙµ™ÉÄò‰8¹÷Þ€XëïÂqrE˜å˜Èêt±£}?Î=ü% åš)Ùí-Ç**—£û—6¨!KÀmç\óÝ\TͰÇþ2W9=‹'Y¹­ß•¡²¤4ÀÖ ²Ãrÿ˜Ì"–¸ä ÚɲÜu ÿio7¼)¸–DŽ%{K‘ÁÈ4ØÎÁ÷Iž  F¡¥ŒKÙ‚þßš!î°*Ó.‰¿¦;˜ !T`›ßìc‰)AH/ ® ­aÁÄz\ÐÙ/³´Ñe_ ¾ ü†é“ûhâØhZŽ«Ô.cÀ1Y¶¼| ¦<;Á•gÆàöß ¤§÷œ}™Á@¢âŠ Ü”#>‡äˆÕ6:,nÏnk “££ð~f9ÿš¸!Qý˜&mØ3ž ùQÖ‹«V ƒ¼˜¾d<°Ëã³ÑÇw7.ù–¬ãÍ:ú½%†¨N‹æ?êÃÔWÒDqÊ'Îö…ùEìèDfü󤩟īÙä°ô#Ôœû뾕ááM1ìTOÇä&άÁ}ºuOÇýEï—†„~I„n¥S\ËŒHë=öïŠ8“å¤À/’n?VÎÖNÂ Ü è.SÄ/Cþ[~D©çãÖ ^jñx>ºôݬŸ°~¯1{³ºÿ–L€£8ôº7†üX¯ÆÍ18ÉLßcÖÑ•ø0º·å® cÜoBÒ1Û?ׯñ"G&ÛïÅBv WŽ€ma^‰ZnÁ„ìI§ÂYX£°…dW"3þÝù$šø¬÷J6¤>L‡±‚¿ñcF2æNxJ½'aÉå6øü æJ÷4+beCqÚïL,²\Cªž9 Ze[ûâ$—%Áà÷8nZö3.ÎÙ‰˜d‹±iåg9ñäKXÛ¤“ò[xå¶,×LšìøaKϼå*jçò \×bÏW/À ×l®ó_=;GÎÐÊ Çqö¹!œ~¨KƒÌᵎ5víA~k(µ_z JÅ.€Ïû$ÌD\ê ¬6Ó µøúâ42ïZÇ–ñ& +‚¾G3¾W‰ƒ?G/€‹©#[sJšxì Çþ5>àÜž ÷¿‘oSÓ¤Ãÿ]£¥ü—45w2oGþâÝ€˜¿rìãìLÖ ¦L?Wêu˜1#‡®Ô-B¡ü|(€Ûë*Ⱦ‡|Ç˨è8 R”Jéú"dÒe¬KVcŽU™øtF!Ö,­{Ïí$ÄÙsĔؒ™z4üîy¾¼Y;šˆI‰ÿx"uú\±ÌÁ¤‘dzñ:f£ãʺ&‘qGÉ|möac3nÚEÓL‚ÁSg;9í±ž ÞG™ž:ÆŽÝ]»K8¡A?Úh>Dk¢ ‰ô|kv}( ö‰‚5*§qÖÈŸdÌRÄ&ƒ@O?,V•„i·ñ”i—7„ÐãgbÝQ5òî°[p!6*DÉÉø­hý£„ÑàÂ$?xÞru÷{’Iú&tÕÊ£œZƒ s½d„?×pNƒ0]È%úH0‘¥óÙÃfܵ¿Óؼ~L‹Vc÷ãŠáyéq?]^½T¤##³ñoŒ!©Ø÷ê,÷4K‡‰Ù/ƒäðÌhyȉ«›¡çÃR+<žS 9w{NOxÏ(—hƒ)µThQÚ Ü€Y=4Ü寧ëLº1]js¥—Ù}¿M õ^i¬MC¥ëŠx»à&/i£¿’{@Ìþ>m»ÖFeƒtHH„*ЭÓȳ“ØÚ‚¼Ýö¬ýBìÚÒ9ò¬aW=oÿðUzºc°ËÛUynòjì­ÚWº3´ ÿåÅïÜ©Ç[ºn]ÆÐXUÇ_ð@ˆ>RÀ>¯mFéŽu°¥ü•ò½JÿúQ‘xQ²Þ •ÝhßÈ´~“?ÿøö᜛lÏ|ÂËÔ™’Ž s]RpD—x ; Ã`ieÄV}€›ï]fš»±lw ™ù,“í¹D|-Ö±õ?ï3ãêL=¼¿:¾¥eŸŽcîý2w]~È¡§O#wëÿà¸à :sgqãuÑ:_}×h‘¦­%ìUgi9¿‘<Ñ)‚¨ò6ö|Þþä?I´³´¿% >”dá!0çª £–ä²´! ˆ£çy†+NÃoé>²–Ke2÷MáôûË tm ÉyÆþšq;þº0í-)¤ÕPg áœÆÈÀæ¶IlÑO!æ8;ÌfÛ‘üPöt‰Q³Kžå(²:ì?INÄj‘—Ê™ðcU¢£øWo'7ýb¸[Ý_é’Ó‡ˆ°Ç¼§~`þq °{‡îì:βšÁÒe\ jr É©w%ã;E™ëwúóu&I;Çä›÷2ÍáBf¬«iÍîd¿€3pûˆ9‰1¼W¿HÏAöu¢ñÖÙGwÇãåa{Ò^šuKå!ã~5ߊïpõð|2IÓ˜ElQf›üó‚Ì çófº;Ê™m¬žÌ=NjB/Á |¼0†Õ^Úo»'â[~0ûÜ” u$…GJ¡ý‰%ûcô¾k€|-…m9?áÊᇨUÈr䢈K»).˜ÍIW>ã>—oBÛ¼!¯È2~—@ËÓiøÖlDÎ-ÇAc¶uß[¸""À™|Ô!• è{ˆ7"¥PT,–w¨½–[¶Ø†}1C¯ÖÝÀv‰`jpX„'Óð€³^_‰¶¢dN¸ÿøöBœ§ºÕæÈücÈã¢z^ÂMùÂìùŠFÔô»ˆ{ÆF"?iŠVEBù{#æzU LãáµñŒÇ‚vpílâx›O»f=¤ÆŠÅpöÏoø»¥lã‘á¢÷ØÇ¿Üƒ"7a¿®#–X ýl„NŸT|/ðuŽèØ\¿ôÜþïÓxèÈ9 ½µx¾<ů½züº}1ü– Ù _£A¹è(ö~@•ä»T@ÚÞ×VpR÷†ùÅ«Îàƒ½3HÌaîåßF”(Hç×§]þïú4÷…,®yã=4®-̱¿B71˜=÷6=/&Áx{ 3š4ÈcÁà¶g.‰ƒ¯)Ã0÷­{µ«˜(J‘íE¸~~ œ.‡/à’x–Fö€©I lU‡(¥tZn”ˆ3beÙ‡ùÁü ò„û=ï8^lµD6·Á¬{ûÅš8쥱ÆôIßLÐOœÌf»¸ðŒ¢ažj/¨‚>P1à0—eQ‰;Øu±Xö¤ì·~×^ºeKȯÅ=Q)߉Ó.™’±âžÄDù?Ô¡Ï©#›x×<ÄQ7¢’?‹6Å"¯q 1ú¶~¿yjc³ÖœØ¥C¦³:ù³'É^d@-bççIœ5òY@yC¡e© ¼ÜÂ{4û•åÜs«©× pÉ©‚|îÛwN&K®Ã4"OlÃÑ€Ë+@ÆH|tÈ¡wÁü§[ÐcÛ?NûdŽ¿îõÂXÙÓü}ópy»/ô¦'pgEXZ’œ<€…/ßq›§s^;&—L 6ãÒ¯ÿ<Ó\Æ+QrͽšJêWÁ¡|Þ#ÆêZBpup7D[ºå`Ó ¢Ìï²—É¿Ø ³œ­@·#fM_ÀYeâºÇèÀó̸]_Ìí­¸ˆ¢ì¸õP³$þË –<*Á cžÐÁ7íôû‹JNvA7éàVºÑEÛÃøûÑòdôNÍÿ|ÆWLŽd] —±ZÝwžB_>¶úˆâ„¹cÉëOÚ@CñtÖ¯‚Å£Êø;üÞAu¦)pŒ›Öù¼ì|0)@ Þ6,‚RSq&ÿ&þ£»át85á+üÜ’‰õbÄò6Z.ÅTuGr@0Fº¿q¦;§²¥vEPzö)ìjr&“†Ç¢t·'>ØXÃZ$L4ѯ¡^¸=Â]ÇسVxó8uÚ‡ÓfM†©R·¼7BMp^ÖWÜŽ–L?è&ŠÄŸ„ww­åJæ³@‹=xñfoFÆ_|ó"*»@«ñ ÷tn)TnWd&Ý ‰cNVªgsšBœÑ.2沉Ó!þ‰mX¦¿ƒœî2žóºs¿vd›¾LcÉëëñ‡·:¹ßjÉGšQuÒA®€Ü'~.ñ©6Œež… .:aêyâÌ /|É÷Ò±g–¥1\࿚wýàXã@ˆóÙ,ïz ·ŽLD½ˆJ®÷Àü— Žx±×„0ÛOEëÍÉýEã8£³uôÇÎÌ.&†¿l³üò`E¯KË8‹)ÚÝt¥ëL´¿>Ê•þH¡—Œ Ivk&túg²»íXðã3Ø7k£m¦žP »yx­¨²UN±Ã2,ï~†½UÿÖµÄl&Ã[%ˆQr3Ÿmc|—¥Øyý/U{|¯¿Ûʬ¶±ÛaÈÙUá•O XÌDË£$ëÅ)ôŒ“›냪µÆ cFÚ‰¸Fw9—ЛÀÖ|m“­=È=Í,¦åräÍ´“`°2"´HOÜ+z¨´ŽónS$e/å@·kÊ@ xv=ƒÜp7|QÕ—yXU7•-œ¨Aï[ˆ‡ÍK1~æŒ =Hîð—“YŽÇ1Eý$LQÅùMÔ%%Þ¬cѲ…x£-ÙtŽÃ„±Ù¥Û åÄÉá¡vúëÎPŽÇ… L±3ñ.Žù3þþ$Ó}å²,ï¡ËtE¸\åÈ> q‡{ü̓æds[2ëµ7Wïh(Ýó„›ñµ ®†Ã…$%°0½ nËKðVn®¼T‡kJVr¾zµ´ÉÑ×äÃmóüËuÂ2]e úƒÙÁ öÕš]›×Ï5Š)± Ê{‰ùè]HÌZ„¾ãV±mSxDv„=¸Ào‘ŽÃ‡çòêZŸÄý«P¶ð%d¼ç2×vë ÔZÇ?ñÞ„uŒgq1=Øœ\!žIø5^oçFbýïOxÅä—š›0ohKáÑõ›ÁýšÜÆ¥½@æ¬ )ô-çµä(sVÿôŲ€¹ZܪåÅ\âŒNùôÎâàüOE“¥ŠãWn§ë×A9ïÕ$Cý;ÜP.•þMØ\}ú|’â„1Ü©Úsº¼ÿhùŒ&˜r¢ ¼d˜ñŠG4|óHù§Õ]Ê`y~ÊÅ$TÉLó¾¥)¸fÿox|„qùóÕÈâý÷©,gˆ^./¸梽xp¶ÆL«Ayg Ñô^*~^‡²ØÉ½5LÄÄ|.åDz¤_‡Ió;è ÑSœç ˜7!|gõq¯=# rÙ8äõ‰áнûÁwÞc°¸:E®¬ã Â)Ú»2ŸþçE¾[ ó•å™ÚQSü½_…µ•LË SžÝÅX.å¤é©;@cOšÓ›KÜÌcÆí”eò^¡qùdv¾El—™àƒ¨~nÆþÓ°ÊVƒMuJæô„CXXÈ tû´”,/ÀZmsˆ©ý…s"bQ}ÉÞ)éé,]äôoˆä?RŒ†{öÖ²í\ò¼8<{ ÛôëZ~„ºå‹`Ú©n¼hŸÆ3vƒÐ‰XGGiÝ ¿b 1ìÁ<5ywïVË?]£@V”£wc&FüZ‡\h.^WfOÕ;ºÞŠbR î‚êâSØ,%ËL>K¡Dh ˆÝ"ûúíà?ÿñßSîà°ÿ6Gn˱”GJÿsT¯AO ÎIñ/g힇»ëÎq_ ŸânyUºŸoAî÷es½*‹èжz¼ñë5z~0qÇhø5^ ­&_äÌnUãÂyŸÐßvZ^d{(³µ™gý,µµÁû®fl™ŒñÛTO'­æÃOCú+%ø–fɦÌVÄòXît‘-ûz£ß<Œ÷Ò*y{§ª)þ²<—ïiÂÏ lÙÌ Ž»3ŸuÙ­•éåu¢8|à-®·ñOÃÙó !ø%E·îýœÅ»·°Æd\Ã}-a´¹^o÷µííw~Ï¿uKµvþînÏÄ©ìaat=sÅ3ƒ¢0±ºæ&>£)ž’La[or¨3jÉÆ—–Á7ß(0p0cã}ª`H÷0^ÜØ`úÍSe\™Cn9§A¸”ù' ÖÜ÷™ÞD{¼êŠïðmÆzÎmû |o:š ŽCçÜPªb¿‹]÷àÔ Œa©ÑR&¦Ã[%˜•R*ßqfLù2ûÿþækk¨tés\¾î98œMÄÔ¨B°J³‡¹8Es åQF1U9&£×có3(~ÕŒ'³Ÿ«áœ£{±÷ø)ž›ÃYT*U`>Gÿ«øù¤%JÄÚ¢Dû>˜ÝcÂÔ¥¹/¡ÿ»[ó±áB†‰k“Ö`QæÝàʱ;‚äŒêf\|l:;ûVŠ9¤ù×[ K„;šùPP§jÃp©¡ ‘WŽI2ºX¾³Ïé8¡[H<ýÜ!Çä›Ç±œî2®ßwƒKú0¨Or3Î@Æ)Ib§¿\\âYaÍZây¡ˆÉ/ؾênwµüoZgª“ó{ðóë~ª.µ‡Ùé?àņzÁûº‚, uÿöQs˜ýo‹=4Ë•;AC}&ø¯ÓWû(|´îômGŽâLm¯÷抓¯ùD¿Öòádü2¨\‡?לīÇó@eb_hÜbò%âÙ¦ñšžI ¦Šáëläõá±ö<»¦Æö~/„gßjaû©³X³o1oµI2¾¾f…; _pÌvY1•z/úAkV…s{“ÏR5õ8Šèjó˜nß&Cj›Ê1d‰ ‘sýŒ¢öé8XkÞnAõZð]ºŒ&μÀçz~ü] EÙÓn%f¦õ6Øá#i¦§šN£âƒ‹:‹g9ý+xS‹DˆÌÐ/X²˜Ë²Ã “)N,#È'åw³Ãá¼@‰® 7þáŸßÇÐwGî_ÿ¢ô‡i|î¸;Â’N£Kºçç™ËnÏÚ^½a£†{£zº©¦ i8¹“ü”¿L“sL1 ¹Ãjbè½%–ª  öSad6ªVÅý¯õ˜ž…ê§8cÌÍà^Ä/kµ’pë¶•ÄüÞD¦édÁ\‹¼"&žu㌸´yÌL°g%¢ÏÒFz Ö_…uÿ ãæ}o§»‹ApJ}´º_cÚÁ¨Åß‹VþÑð¡CŽî_4Œ æÿBwz¦¦Š>^Kþéí¡S‰ðRt9¼øèÿÄz˜; ’p oñŠç6UÔ€§úm®Y3Ÿ»@í5CðÍÛ^!6ìsˆ÷¸d•X!Â,%½iÑ&[ãöãŠÏ±|¶ŒÛtæÍæ?Fc…ïèõN‰Teýæ§Çߤ~çKx‰SÚ¹Ø_oÁÁ¹.®À¼¾~lºª 5ÏCç³R¼¢R…ç:÷“ÍÓ¢y¾œ(õv³dIãÅÈr¡M¸ÈyëõÒd{™i è¬v§k‚¬Xï«Ü‹¯†ð.ÿ6çÞñÒfó4)ìïý 62fX›µ“8nœÅLçIõíZ¼¼ï±pPªMñ}¬D¿¼ Nï)}ØÂK’GÚ²‡nÿÓß²m‘ï¸Þ–,òŸ^H“ƒ3NìäôÎxCû×ùÌí§1’qCí•:ì¬Å&³ò6š…‹ìé“W‹8¢ïÄRlTiƒW<­~€ßÔ²É/{ÿÖ mV]ÜÃï¾DSx-7}çR8¨âû\€ÐQ$ëV:óµ2Za~ÿ}îñ¡Fˆ•þW«™ÕäÜü3sÁ}ÛV“fÎöÝ[…í;Øov‚.ì—b›ìñ{Ó4æ­\ÌŸb˜ Q –2öy:³ö¹†Í3mpÆ_Œ¬º w…pq/ª@ð¡(›./K’mÎbdUÐ,€d-2ù»3¼îYƇv ¦‡<ùàöm{1y×2W1­þåë·ÅØ*1"£+ÎÊö_ä*įOä6ݧ ¨çÔséµã›Ð0°ŒÛaDáá}Qv¿w:îŠ: -ó±Òs1êH¢jÇ?qã/ âK>[±ÓjAãh Møt·aõÖ PÕŠïÇe#1°ß:m³›ééE«A{™,™½l gxJ 3‚AÒy7ú€Â –$ᇔ¿BëŸnÂU1iø¥ ŠZ‡ðü-L?ñ¿qÈ¥emÁ€7ûp£d9´ßaàRÿ‘^˹L_O»„ûæÌ#»NâóE+AþãφҬwôÏWaæÓ艅ý«0k·»"—„«v.§÷>j!óŠf»i“¢!#_ž‰²?yºló‡=$IIÞ}¸†O/÷b ÝXð{yº8ƒLÌñ;Bä^O°ì¶d’SXGÀUø²f;í†Ð°‹Öq»¯á»CcXݺÜZ×ٜǠ1ñÙpŠKtp‰¢1¼ê=ãêªêA¼Œ‡Ë3yÓÕj@ý› Œµù‰£Ÿó¡KfFí*åB?â^™µä)`O#h°¶ ûùf@÷{z«|‡Í䑞Bðy¾g³ dUOää·vàªRÍðo9.åŽX-ã^\­£kÅœYEÅÃS²`ÏÖVœyJ‰©¥,€Ûrî(~@×?–ciz¤p(·àtâYÔ ´O†H5 ä»éÖ9/eŸ~Éâ«ÕÊôü[5¸®3Üã’c"²ŒŠÍgM/tØâdAÂÁW)lÜçP²=¬Þ»PÃ9¦ÄÄj,qšÆ@¥`?Z #om!/þnÇ™K‚qU|4nΠžA¹,Ú¼sn@¦›V³;Ì’`—ÛFö÷÷eîzO ©xRŒƒç°îe©ìWS Ý_£Ë2‘Ÿµ‰‹6ÆkÏlÑ¡I˜q ’gÈÞ¹ãqÁ!R|+gÙúŠ“o¼‡Î Y„Õc~Ë Àyr%¸zhCm|0,®9Èæ«²cÇÿr‡® Ã{E~ä )ør2‡>ÿ_±fÅMg¡Á¢|q;ÙÞM¹JÕZÊy§¶˜‚T¨{2f.üÎxÀÍw_MöÜÈÃÄjøò›ißf›G&£Ø5¶ù5ô•‹b¬jˉƒ[æÛ JB<ÂÏ­ê 0!òü—×BâSêf_Šj‘Bìs¬ ÷£$_1äÈ2¥1|Úm9‰œzx™gÿs »ñ+ƒ/}ób#ðîÖ Ø›p‘v%NÄG¹†,r‡:ñ ;s¿E‚ãú=\Ÿ¸-ˆ+—ÂÞ0€›Ðîç]¼}»7ºª‘ã¥å8áȶGè'¾sÃIÙ7ù—u˜¨­<úlKÎm%ñ*CÜ–2ähç0è>ÁI”ŸƒÔ`^Ò­¼þД ÆŽp¢‹þó§­nÀw†’D¡8OüËY]Ú U{áH¼ ±ž¬Èü„Gé¹Ìw¸Ú, å×rŸÀ…iÝp/L‰ü‹ éÁ ¸5z\É•,¼n;—=¸Åè¯ËêäDI ×X¡FˆÙ¶ïÿ×%SÒÍö‘ÕVPjp÷ènf{^äÓéÐ:á:hŽ‚‹e^®MfåŠØ¸é6<.ÌáÜyùèà{…+Ú v í÷pgÖx^v!wø[Ô7¢CÉNj¿V–<ùİè8â…ìIdµ|'ñÇÃ%€ŠÖZ2T›Æ”&®a÷ãBYó4e ™ yƒÔy• ÛêÄÄ>M!Æ=à2ï¡ë6$À%¡UÐo2,6g'n·v]aVDZ}‚)K”akä°Æ­å~tÂä= Ø¥aC:žM%¿K!ce*Òóq8&=”3V‰afO"cîüc©:)âlÄž¦ºãïÆtäæEh¿Þ‹!:¶Lîð’¼u#›Yz†Y9vìŸj0ÿY‹ÊÇá)ƒúˆDVòÉØðcÁyÅôöÈÞŠÏ`0rtÚ’_æsà+`E1!(çBZk2ÑWz'饫X‡FŽÂD`ƒVew¬yd¾Òfwò)ò¥°÷1°F"ïßI'§¥ÀV„R;]ÙfI :Î-ìõÁ&~qÞ ùٌ틈™–1¬›Ð@úôu-æ×‡†Sý'(ÞåÏfÍHfO{BQŒ0Ùûu3Þ[÷¼Ré{!¢ õ–Ƚ£ß›zÐv2ÅL ¸y3ª æzä¤ÁÝ¿…ˆ}»8ù[^Š9C»aÊÊùpcÿ[Íîåò·¬Ã1áÖÜM9ò"3†M“€ßv–=Ù *“J`µ=Bìšcøn‹(ª}Æj%8§ü3‘:ƒ¾uÔ,ôVµ™âj_{Ròf^¿Ù[mŸsbžÀÃ9\µÃœ¬ ØŠöóD±Òø î]°iìB¬oªÁ $ƒ=ÉM@ó;Aü›3¥¡üõt¶±mN·?ÍDÍùx§–Ão-yûÎ×ãµ%&g¹±0øµ yB3È;mC²?þ#:<°À?¶ÒŒMzŠ#qœ¿%„àÞš€8é›=¨ñQ~<±]u øÂUí‚¥ÑxS¿¹Cд Ü¢·’È^K|”ÌM¶µá¾é£)ëexÉ'àúƒ¼³ê<×[¾ƒ~þæäœ~ÀÇgУ;•ÅÜÙNŸÎ(îm ºí¸iH¢¡é³JÑf¬-¹j­È1sXpÆÿކ Ñ}xqCLÇ n†ãk¸:¢‰§>ZB‹û.**†ÜòúÆ09ýá¿ÑK§3EbØ¥ñx%À‚­[ óDmà°ð\5!ƒKÎß½B18îêÚoŠƒÄ;µ(íÿ65öaÒ^=&¾ÖpFú,¥äq™7•Êc!·´rl²_н¿õ˜ÃtO¼¨’ ½Ç39Å=xÎ'„½3ÿÍ¡æ½t;ÖæüÀ¡à°ýÎ(ä»Ü…gn/9óibÌûE4¾Ø<׿ÐuBœ>ðî'OYÒ”§N> ’èë'Hº,HfÚ´˜4>Àäe§Aåµ&c©"„ ¾‹ú·\éæš\h:ù þ¬{_9ƒÓƒ˜ß=.~U®Aš¤s‹\…xf·Â@}‰ÆÏ‘ÙV¤Ú5ƒ§ð3 ?®!š~p¶+¤ycɦXn`vRëÑcž+vûNÂòØ·VŠÜ5[H/lÖe릸Ѫwà§O/¨g ø]ø0ê«Gª ’ß6¿p­¶ª™ƒ;~sÍÒ¦TFe<þ6wa]:¹Ê{ŸèÀ5'hZ fsì02$7;§@i•5­ºi‰à6e$‰úÐY- ‡‡E o¨Ïf¼tecª\õœƒ³wM%þ.-pI/Š6|¡¿M®à—†É´uÜêû{¾5”Fá/¢`bÀ–i‹‚¦Q=·¼À+/jr6G¢fÕbl<(AÊJ"ùÏÖfÀš½ýxÍ- ?U¤C¡ÈlÍŒbEļ™(¹íäÍfÖ\ÂüvÍMÚÊñ¸/6Øæ7€‡´¿ð‹EJÈô‰âì×h" œŒï÷Û°2Û bûýº$ÆçÓsk‡°ì¾ ľ:©?JÀûûÜSt¯Ó-­apvïf8¼Ñ  P£FŠÙöðÁ÷Z5gÃG{1H¶$“ïj‘TØ¢cÈô½òPHZ’D³@îübôÎhÅ€›V¶œ_J#ëÇp˜Þ€c[Sñën_ÃÒd©C,êE‰òÀMˆŠêGñѱÜÚqËØï[úxC'}=Ô€§Žia³›„ -† ò‘ìá2 ²d„‚à¾Bú%CB6Š“!·±D23òµ%ØÍˆì…ÈiL7Þ_Àœç¸Â•µ‚dmÓvºÃy<•´ÖGP8…¯+¿R¯¼^À2?€Ë×ãäô]Y’¾ø#Üø›…*ºW˜u¢*~jV`’¶¤÷j((¨ÿ#žŒ~¼¹›þ¹uŽIO ã¬ZAJ´Û’tf*NaÝöþP+ÆHIj ë±õÂZø ªûDÝḬ NþÒÀé«Í¤@%✦@b.Na{­ÎÒåºo¸oO_âåïyt]¨%“¿øm›À¾ùxþÔÉ좇ºNp«b°8§ÄŠ9ròX3ý\<Tt.ñÛëÇ’–[×yF"d‡¡7¡®åI`à‹::iò|>~˜tí¾m÷¯bEc4>ß™ÀmKKäé×o”_sq§Ô2lüs ÍÓ3°äÊ1rF²•t§»š×r ÎŽ“‡³Ýøçè,Tî·æ¦W×óû~D³¼¯dŒújwΜ1ÈDt5TnOÄwJ`ú; ®¿5«4ˆhWLÃ_/`Ó“Þ`èX˜4Ëãß3ø êÇ€jq*o¥DÎ,½Óð£ÞÜÅ;Àì+°ˆ p¦k,7æÏ4N_G57€ßÈY¾m TIá³=æx{Úºü[ÖÅUj¸Ù <Þ¨Ó¥F«ˆtx,.™´‚Jm‚Ý)ÓØàº¹LÄ¢?ÂXï^:û„=9‡v>=ÿù¡y~U]Øràu Ç”pª¸ö6Y‘Xo„d=¶«Û8•”ã¦w7¹¾âôJõqôûøƒþÜ­J<¼Ã¹¹ö_xw/äæä_ ±fèhY‡‰ˆÇ˱Ü2DÁó¡ô­±:Xðß®yùº™tÔrœËø@?¿•†É~5hÝyˆŸÙÊ×þ‚Ðfx¹æ1§n@”$ÍQîÙ <¿ãîùòœFT†°r”^['Á~%­eFÿ4Öˆ k,K††Û[ˆÆ‡UäïÎ8£]™÷ïÓ Q†g–‹ã«ú“ÿA7ÝWÖ PÖjK2ö‚ɤóx[ÝŒ¨DÊ’Mb@'"OGM' tóØvS´û ÚíÓldy Hµü>¶}²"máDöÏœO‚Gõñ×Ù^ÖØ7ŽÌ‰O! ò5˜Ðׂƒ×oÐ…똠h9ùs.”¨§õb~#w#ÎàèµxÂDÓèïü6¢pt3{™yÚ~Ç‚¸l2´ÍêÅ”óªœÝao¬þǚ˾g±³ß¤‰ËL%²*^š¼zÕÎM›gÇòwKé?¸Sa ÛúÒ˜²¿Ösìï#f¾Üñ±ïøŠ—µˆ°ÿ7:9k[{‰°ñÚWá°O½R|îì´fq¼"×éÌÂÇe+fÃŒÁXÖØLa{ÚE¼u¡Nœƒ/Í®(èt‚*µ¬ !-/ ¤áÔW72§ï3ȊµY9`–Œ[KþÂó²:úi[&n;`ÄÜ_~‡žÆS¬vŒ=ð<ÃŒŠ¼™ñb òåM8 Ï #ö5YóSV¨‡Í“®ÀÖ¹ñÌzZ5½¯CæÎ)„Y÷Q¿«çHSH ÈüÁxýÌi‚ 73`“… ž×#r›X-‡¿:¦LÙ 'TÉ2ùï„~JÒºL@ðk NØŠAï5ACñ²M´±êĶóš“à Am?Ýýù/÷×gY³‘œôÛq£Ã`xý ×Ì?ûNƒ°“@˜¥HßýÂ.yv[¢a¦6ÙÓŽÎÅÂÎ~ª2ï;Îís…Ç™Kaâ 1âwplMÜ—çÍÃ<·³¸Dõž:2–Ìuµgùëu¡~›5¾Ýý 6°õvjäר5 Gžæ€À€6fúZòyž6®\¯E~vÍaÑɬuÄSœ§3Çtg’pL}^ŠÁ„1éP´„Í® À@NÃŽ£©¼Ò]2ôhÑÙ¸î|,§š¿ýæq®¦SÆ)x@¼ $çêÅ9ôJ™*ïè¥g<ûÜép$Eò?o0ê]ï‚.ÿ,š|žœ^õJaækA2çû||¨1§½8Dd.i÷5€Æå܈K˜(E¯ê<(»Énwž {[ã,eÜSàÊ¿xùø‹ãu¹4 wv¯p V¯ßEäïF£²w37j'ÀÒF<ÈÞ‚Db~Ä3/@ØçhîÉ{L”6b×{puûv.í…¶× ÓEª>åZÿf3ÑO`÷¾d&>ïXÆ´VŠq5­1Ø”ƒ5WƒyõÓÉÔ*qöcG¤zÿÁI¸ç¤M4ª¶ââ7ðøï\!Éq Ñ+Åæ’ëÖs$~ÂÊ臤_Ôýâò؆—õÌÞ¥//ò¢†RMà"mMf»°É³—£ëþ,:ýîòãÜX2tiîÌxŽ.ÖäÄÖŸxóéU6½»N“'ŸZ²Ïj·è½bzrA2K‡IDÃ,‚¿úÂgn’Ã&¼°/—[··¦¬‡ š!ðØ ÷*á.Ò‹eó=áÍiV+>ÝL›ÅÁ[HÚó­¼µÞoÑ5¼ýŠÅX7aἄä|üÉ<ÿº3ÍRCnÙ.¸Ý3 ´¬äq¹<²Ó,'³1™Ø}Õ‰ÝXàÀìŸï Îwßq’ê÷ Ë}ÄNÅ/£Æ€šùµ9ˆîè‚Ŧ2cwÚ—üŠW裈Ӯ‹1,„±N Ýæ *D÷UöM¥ððá t1º ».܂ټîª#Xþ;.^ŽKÅÇBÖƒZ; zw‡ã·?àgmÌÞ'þÓËߟSÛcþœÌáãhj›@•ïÜæï(ÈásŠú uo˜{÷f#d¹Æ æñaü`Ùˆ­£†0!KÔ;¤Éé¢WT?å-'"u‹V¿nG²Rvq?>Ÿ‡cî7ðÓiö-  ]ä〠/¾²3!ñ§È®{(.DZ÷Þâ]—ð™nÅ•*_a­W—õ­Þýp3uDl÷ ëósXä!ñ;÷h¼2wå°2éöèÃÞï üOúí.ùïÿÌðyf:uïTB~àY*0”FJgá½ ‚PÒîL¤vâ2usRöМ´øÌÁÛ휩æxìqšÉNÌ’ãÒ½f =D¹ýq·‘ëàµ1šãì?ÿò%Ÿ¹d# ;’KœÂÎÙØÏþëcwl†;«ù‰„YÉå0ªz„©u/„îûîè€ta9ø d‚µÜI|)˜«»yÒšW“G×ΰû¯£qÚQ>¥æ›©ˆm 7Ÿw3_âNßÛü‰¹Í°kާw!C©ƒÌœk¥ù×øˆ“qgf°#éý\”¹ ‹N»€k(„÷ ±”Í«Á%o7hiÂÀYÖTQB;Eñ™¤5D¼™Š“ ’¨ã 'Ø—^ˆgM& ×y‚nÀÔyZÍ ›*ŒÙåºl®×Vâó}C4^3‡zOý‰ªçlHÖ¨ÿèà µ  _¯–AÍO-¢ã¶‰M£ƒ~„eFÀжx}^֬נtQ÷µg¸‰g'Ó«6àÝÙ[ZV‘IÖÐvû)'mˆÃ·vsc¯×r‚.ðDöMbQ3 ¹»¼¥èüª›Ö—E§½ùã ”˜®ÿ9Øÿ§aünê®¸Ž _kG~ÚÉ1ñ|g¦h\ÝLÍ7€Ý>‡QúdJ´-ötk ›¸€•Ç!Î| Cä^¼‡×&ºxõ©-¶õ¤¦ú?í ¦ìrÒ\£ …6FêìÞzÒö(«òÈÆ“ïPìƾu¹Â¹g”sYÔŒ¿L·±Š£ÅødÕB˜Ùw¹ÁOK…hM¢ºu†øÂ$u&¸bΩÈn®Ï7 ¿éÙ,·‹=»z;¾6ÿôFTº%>³¤ 0Ãÿ7·-ˆ–FÍg?µãÈŠ%¡ ^Æ],†c ;ØÎQng2–•5³ÅkNó®ùh‘SŽ,õƒIˆg×bîaa¨#ßèj–;Ê0úÜ„wº3Þ=ÎçP„+ môdõË\~Á»\©ºt»’eÆî¢ô·´[Ø–ÁI,[üÑÍr±MXý|pÔF#øV‰± ¾#à³a+Ù8ÊfVýBãÈÝGñ!Ï›T™àQÑñÄNßí8±5q+ˆu®£—2ˆ¡hš-Ѓ ´SpFσÃÄpAà¾Â>¬uPF¹µ.ì¨{<Öṳ̈žŸ¨«Å ¶ã.XØÖü熶ïo¸`Ñùl©‚&Wp%ƒÒ2B Õpüð$2ɨ4»‹o6äçËD@Q4>’¤—,õ+ýçà_Цvëx&Èþ‘$š-…í¢áwçJ"§’G‚_—Á×¹ˆ³ žK¿Á½å£fâÏ Ðäø ƒ/ŸC_@ç×!ü:±…1í"v±SŠ|™Ó‚Ïf#'—>Áô‡ÈŸæÉ,¯'DJ׳ysØýž×Ñ!tx¶’\õ©¡õUalr“Zеb¯×bœ¶[ñ¤îàØkëißüb<²J Öï™GbÃîƒÎŽ;4ê„1²Ÿù7«qв*“/ÌÄcðvj1ºJž‚=ÛúÑÊ(¤¦ê³Y|3ÆI›ÅGR`àèqHš`O?ykâÊ}U þ=ö“'&k“pfŠ Š+Šãêu+ekü&ÝãJúqÅ¢DîAe—T‡¿#f1ïÚ;ܧñlÚ§ÇÜàû0¸^(ÈŽÍPßšÿï!½™gƒâHwk«I Ï ?`=Ž&/héÂEâ•0£ÏŽ5î» ‰¯ `V5aï»ÝÐÞÌéxh3ÏÆ}°Ò3¿lÏsI9á|™äƒ]Låwš©JFwÓçIäE‘i½Þ°ÈM•&ài§:œ4y.ôU~¥‚E³©qO-ªZ¨ù™óhѪ{¸ù¶3äh^âœÚBJË :pç̽“Ç:A2S„V áOzÎ)ÖŸ@sW]¢ð«†ì¬ÜÅÌáɃ9 ÞOÇ’ofàßëL_ž„Sö{“…sá gº­É /©„`”£?dð+¼r»D~ÊvÒϦáDXß –i´Â¸v|ï"Y7Ž %_ó|ÈŠ½Ì™ºm‚¾=k]Š/g¢ì2#¾ ÍÒiãöÒ‰äýœÁÊJÚvä'ü˜>Ï›KËO×ûâéžç®wwéMo£ýÑtÏ â4¨M²÷o…„Ê%(rô Æxpýˆ`Ï=kÇòíLPv¢/|×Mb‡„7¢ý}eòÚÔ”]â‘ü.söAË‚ìÜ0‰e‰ç‚ñZ|å,•±'§ÞhŸ&¿8_‹SPøV–‰OG(ÊmfO¿œS[1á}ÊñéÖ N€“g©Yh1‹µµ.ú9¯dÂxûUXæEl‰® X]Àé˜aIž^'ÊÍ»óÂ&Vù%Z͆ê’˜ø€ÿ¨q!ÜÑ›GÏ íÄYÚƒØx7ƒÌ.Ïö)pçóž+dÁ>=²Fòþ˜¢HÙ<„‡}[ÐdïºÝ¹ƒ¦NŒb³vë’±1øÉÏal!Ïlq{0Ò :ÏĈð¿½IûÛÜYé=ô°„Ù㺠}§s›FtñùÚû¸ÅÝ’ø2«ü40²'z™läÖ®ùÅ7nMô ÖÕ#‚{ßD‡c2w>HŒƒ$IÊM›²<¿O`³ëaþ²t f‰H[²9ÁvJ•ªf©áI&@¬n-§ò2_8éã˜hÛ1X3Ï(z³&g 4›e#ݼªœN‘žOüþíS>·Ò’×qÝÚ¯ÜçôLÓ=Ïs4ùÌ?yRɯùÃËÛ8*Ë"0ßI‘\[pŒËQ©ƒí NÂBÉ/¸*ÀþÑ41à÷Í&=êG!Ðt s¥xzå<¸j¡ø?ž¾(ªêžÞ;H•"Ø`ßHÁB³ vì½ ½ˆTAA”jÁ¨ (esƒ(ŠbA°  Ò› ú÷÷ýçûvfg÷%yy¹›ä朙3g 1yÁµf…× É܃TÏ—&c7 ˆéij4xïçJÂá 8aõ-LeÓþK%m¬ÿà^íש¾‘½;Q\§b²}l›aÌ7rÙWb¿KçûU+øl ‹ï´ÓKoÄÉðÓ6.¸NïLÁÑZ$¿Þ›Öô”ð-Õ’‘ˆn@÷¤Ù}ǨޑӜö¶‹´6r”ç0͈áÿüš¹Ý2«Ø§©œ’Ë-œØ$F¾½r…gÍ*¬fyôÍŽ"E'MX‚V_ÅМÆð¤è…U³év|êí~Neê1ÃhºÀ÷$·`Û…í×™ówŸæ•Ì%—ò­°dÁ ÂïÓÇ"“‹¸@HœUƘ’&µ‹(¿Q¤ÝF°²ßŽŸ-Ïv]Ë"+‚ª¡ð`>\Þ ÊÖVx‘ÁrMø~hWu†~(üÝ%›E`Ý®*Û¶öÎG.‰V¸œâj¤ÉÎÏ2äÄ TŸÔ)gŽÓ§yh¨…*»°^€d­¾c#9÷®Âüc9t±îlk ‡sûº±kÒ;žÉo\ÔÇ}ÿ’ÆQ¹Ÿ¾æp•c?óÝ×±/s lêÀ-©žxíÜ”ÿÚWªùâ 0”Lç•|—ï¥ÃÐØ °»4c7Vm¸ª5xNTŸéŸf¢ûÆ ò‚g˜9í2Ô‰  ÙßpÞ ²vQ¯Æ¡‡eœ¬Ëo¬ÉÜ…;|#¸LÊ<¶bÀz_ãë±® Yî“‹"È n§ùYaºÇ&&8'ç܄ܔZT(Ƀ »þB*ÓMÃhë(ÁBE5©ÁÓ…lí¶Üd³¸délÛö¸Þr­Täv䀭–„T'²Ê;<ÖYp››¿“»ùǘD·äÛÝy€—ô¨ûŸ»˜=Ljd\Ê^}y„N0'P‚,œåÄ™«Ð¹†Æl£J;×Å‹e·ý,3S„)œ%ÁŸ?qÿ€ vÏã~¾ÞŽq3˜Ïóóqd®tbôÃcºí™Ë'±QP¡½Š×‘@_WzeØš|J(ÆÉA[¡¤ÃNn_{ÝZ€F¬ ©«aWÞ!Àã¢LÍ1Ž5Žý‡‘“×·`qH6ôNÊæV~®çµ$A­ýõèÌÒyíJÆlÉŽ›a‘ÜFXò;œFœ?ËEÀÿ×vBlÛPÕS›Aµ<´ÜG,¶ßÂè)òpP¡Œû–ç"=Žpôh",µá±è]s¸—'È€»"9‹ÛáW2~~@´ýFyV_.&çL5Á»-UÜîè>ЋòF§¿†´ÿM)ðžê²µïâðÔ½‰àmÈÙ½ ÒEG±9á<÷{;£W²/ þôè- ‚ •&0³/æâO ”Õ‰\s)˜\ ¦ÿi±×nšI®> ¥mn3YK:‚(×k(Îßúå.ÔÈkÐG]Ë0íÎs¼§zŽ[½¿ãÐèYÍ\h`=Äž©áMªúDÅ› ˜³†#‹b~z-µ[r…n*ÞÉì‡cñV@M”ó¡C¦G¹¯‰ÔKèLZþ.gO!Klò°ù–0Wà+Å ß®‡íußhÙãùTå¸ oÂî 8Ñãë»°á{ЀS=Ûqê‘DnxòB|ºFöŸ™Œ•/u™WŽuc÷Þ¾çÄMd‰y–ð!¥¤®ø ä;u+(†[m"þCÈ=ùB‡ãðî5V߀՟ð%ð1È‚@@V+Îáõ‘3¾ê¾ž"†‚ÑVt©P.ÈÌÃr•£!¨_>‘b¢š°î\Ü»ÄÕ ¼äšS8Ü2û*7ªSþSç@BÆHé\ÉR["°´êýø¥mRààc’_Ìs §ãn^Ávk¸ê­DOžÂôs’›î‹­¶ RmL¶Ž ¡ë·VPU//¶DNž½ãZæJ±)·ŽsÛLXúWWÌJ9C{’­0Ún€nHwÆYh‰pýp¸ Ÿsjàz°Tn„@xÄg>qÚÃâ!ïI-fMOe˼K¸³Kâ€3Ùi[ÎB¹{ t—\GþC¿°wŒS n“zékq1pcÅÖ§Í×kñæ š>Õ?ü¤¨éŒ+ן´•ó/ åÚˆ+œ9ߊ—ÜÝZì«ßÈœ}®T¬ƒÉÏr5ç½É¼éM|åg÷¹ß™#Ø·ñ „LrÁ;?F©»â\ð9{F È‹œ²cóæŸƒª-*d¦"°SNÙþDžì]jÌÄ´ ¨b·>ß)Ô´’¡Q¿;…Ⱦ‡òDóœ:^ëJE{t‚âäÛïPø_yÎÛØ-l…%1Ïè"çöÿtÝüo3HŽ7xO#1÷¹Ìa`"¹^>Ž2kbáþ“|7£'Ö}¦ýÑYPT³œwî\pÇÜØ„2ÙÛµù€UWèÙZ·³8f =<检œ8GÄSLsâà”äYM__­ aE÷u´Sg9Šo¹ï³êùÕe¢ä×;’¿*†N¹:Ÿˆ*¥ï\ÆD#Cò±’-ò¥¤<í÷Iæ+·i{7_°ìS\XbÜ7ú°^ŽwW¬69Ur¦ç ‰cþB¸ª ‚éÃW0Û`*ä„BËYQîÀH:¬u‚];âðÜÌ‹ü?ƒB¯„<) ³E.Ѿebô„Y&çùº ,äƒuÌF¾þóÏœ¼å˜ô8˜ ~< cÝðΦX°”]@7·>Á÷9×QsêuX}0í ‰ä´v¾çæ”ãä5‹ðE½ 1Í[NÎ7ÿ¢‹òbà\Òn¥Y ë±Ý@v|å?“öãÊìLªìö’ßî˜æødâ²&†ûùP‚#TÈÒ¦wÜ­lô3[¶²ÞßÍX5·‚Z_ÀŠåI%ÿD|÷„g:öän}î´Ë'/ã×Ù*ˆz ºSv>V>'ÀæÏNnÈE“yެ®Ñ ‚T›Uð;¹”¦Jikçµ@ïêLX÷<^N ¤?Àg+öq<Ÿ[t°”¿Óë(.=Œ‰¢¹ivuø¤ý'·ÌןíÑš‡¶¸“&­tíÞsôéµV.dp%–'ÿó±„xú0{qˆm¤³¹1õªÌeñ8Ö·e Tz#B”½_ÏWÑcIC÷ îÛWN}`9&¯ÈF·¥ëðéµä¾£9W)Köú5bÓ~+¸¿:“›¥~ëëp—ßn2ɺ µ2v0›˜8îÌsS¦IaÃÄ$Øõ¤Þ Š“ÇA7(‹ÃÍW"/ó*j ·ñÌ [Á?«.¶l… MÜ7;«$¶EB ÑÎ…{«-ÙØKb$··øåûù Ž?¹Ü©µ¼_B§èå‹@±ß™»œrË­~Š”ØºçúIÁôâ=¸ï•}¹¶ÉÍœçó8îÙ»ëœÒëµ0]%î-œCËôw``iˆ$pìXÛe~¥‡iþáåÔ (±d>¦—J`h­$8­r©±æ‘<—<N¶5_ÑËy>ŽÃ,§Ïhvè*Fm±ç)8 †=AüC/^ãuh´}gƒ]­xw“óU•&÷ì¶crßcþÅêÎ;à;—¯~õ¦4r5y4ßL‚ù Tr³¿TáO­|u˜Ñ‡z“0›´bK¸"X®5„ãŵüs} !F¦"Þë¹Ï¯aÏMf°FV¦½å&TÿµõÙp„_öF pÖ¾€ TÞw |Ìì %_‹¤å‹qaF–°dÁGìíN†¶gyr2’Û‘¼™•oÿMU³D˜µÝ2+½c.ž…¶è9¤¢¡îŸÒ%iøfu >ù|.¿´œFä$d/pEžÃ¬ P’<:¸x`Óþk,k6ÆY·ð¥´4ƒ …pîe”tÊñ5P†Ì ,„ E+ñŽY;$ÝØŒã¾ ’ŠôxzÂ_ºöTÀBñ^š#íMi‚¤`c9ÌÍ àêu‹x‹YŸà]ôßøˆÛ ½…ÌÓ{ϽZ o§ÞÅÐ.1rðôœzeȧçÁ½#Î`]*GFnd“P饘<õ"´Q&ÜúŬHðmâ¹ ÞÓ^­ÎÞƒ£“ØmïJº$¡ˆÝHLþ7Ïr¬±ãÚ,ü÷ž·¯:6¢¢]=LûžÁÝèU&¦üưŸ?±®#™>©¼‰ùïFP*÷ Ô·…ñÆw)ó{Ëô!ãþs0R9FÓ‰ò6WØŒ¹åŸ¤I³Ôa÷š~˜#—ã~Ï'Ýc ™Æù%8ön1[l¢ˆV Æd¥™xUáÂÅm0ðÌ—”ìKÆßÖ!A;p\ÙW±ÑC­ºdƒé–Î5`Öxc·NEøÃ…DØ×”³«pó%0©¹D:ËÀíÀ«àç*CjO¾ÂðkaqD1\òJâÄ¡]ŽK¢›b<˜]L¥M}!¦Øެ7…G…Où©¾â‹ÇôJ¨0=|ø574f.w.º5j„ÉL÷×d“_û§?*8ÿuøOïM¶ç™ÓÇÏÑ¢ò&^“r´É¥2ï`_tü!¥i%dƒÇ4x4ì%oîÑÛWŒk\ÆJSûQPw^Ý΃¦ú ˜w$• xƒ(ùÃj®>€ÀÅF,ÿ“±NSd‘G6“ˆž(ïO¦,ÊĘ#:µ´‹Î# _å0#Äþ¹Çut³õ²Q`›¬HWû°;#è¬ÆÿχZÜw × ¥[ª•‰N—.*ä©ÞŸvþµÜî7ø§9iœÕ£BvøÛ/À RÛˆ¨s£!8³ÿ8kY>…9‘q¬úO nMW%;˜P²×ùQ;‹¦rÜŸîUó]½}‚ý>ÑÃyç ëd p‚W+·dá2ñИý†#§žóæî< ŸÜK©Ê3[¯÷ßpN|,·ðŽ›¹w.Vûù1ËÝYäý¨9ï¬~o‡ÞAñe´“´ ñ5»˜«{¶ô.åT2?W˜Ð[_ýTc‹] «$¶a¤ä|¸÷<þ¨³' ŠÓñ‹‚°5ܸµáé¿Lz¾Œü•8†+Êéݽ Ø©ÃÓ±ly9f,;N Ž"Ì.ééš$¸A7Aàeü!#M}ß™íE¾ëé u_¨tÒZ8{$€y>yˆ ¹[“—¡Æ o6®"\ß°¾[SØŒ—™0ÛzÑ¿õMV…‚ZL(ì[àO$4çà‡e.,R;—ïØ /ÝfÆáðQðçzÍ„¥‰Ê3Ïç/ðBPÞKþÇ©w V_¶s•–)z1õ¿ãØš|9¦7wüž¸ åTCßTg³gø±½/ùh}{tHðëpØ–(Ãz;ÌÉ“‘éÄ×í²H­ÝZœ~$rmV9‰§ºF1¶n œ0‹Fo|¿¼—ÎxîbOÂsÉ­ŸßÀ3è;®·•ÜýC6f¦Âå.²À°jïf¾p;ù*äÎCVµç¨:ç®e¡ì“!9#`jn oíþÍ›¢0—ýù—ïc½§0oz¢í3–†1lÓÊ+(*]–£¹À-à9ú }ý r©[ªçM¸U~jà6Ͱ(ÆGÄÉÃ#EÔd÷ Ö¾¿Œ–© Ö«u(if }ñDGpzùá‘aŽ=6dƒfÁœà]C–( sæ'ƒÏŽÅ T¯î…zD3ú¦tÃÕùèØ²îíúÄm»^ÇÅÕLŵ"G16ý"&€—m:å¬^{¾:ÓOýççü¼î8à³x0¾ònñ!Ás »µ„]¸€©gzÑm¢ì4W¤=)<µÒ~LŒl¢ò¦s¸¥®yÄ´ÿª÷PÅ·>䚈‹o¡’ðò£ Y·lÖ4 #ªµÓEó.ÐAYœ]†ö¢ R+Õ˜§ ïŽc)>ïßŦE‰Øÿa)ht:!Ï] ü`û8%ûl»K;áôé'Ü.%þ~>ÚƱk§6à2££(t‚i*êï`r¹âÝéœ|êr²œ%»Ú#*)pËõÞ[ö‘—aýä—2¦rš¾>lCÆøP Š$'M¬`Ѥ=°á–ê¿<ÃLWn!û[„^@Õþ&¼-îH®0"™Í y`5‰z¦B&¨mgCHÊá&¸tpë.yÂù¯R¥jŸE‰¤O*»<=žçŽaok05£OXp¤½ó‡PYCpWG›>ôq"·Õð•Î7ÜöÙŒ©>çóU³½ Ky/Þ§ãÒG²ÄW˜ñ.&pöìâöÛ¾ ‰àËýôŒõ<ü™ÏYß=Š˜w îïÅ©|çÈ`¨ÒˆŠƒb÷ËܲžvÖ)± _I!Ø@·E«b²}ÞÈ3À…ƒó!`Þ\–l|€­ú‰"ƒ 1vXŸ[¤dV®è„‰ãÎàÔ¤6P]öwnßE¬È:®È‚E.À|#j!Z]ˆí•ááC“ë:…ÜT|H-JÖíC9ü ®÷&iÔ]üHïæYð·¦ò¼¶>àD3­ù#—±àdήô ¾ù±ê—0éÑÕ¬2k¿mx‹3†jœÂŠ'cIAj ôOÏÆÅf quüXêÞ½”ón*¥kµ@Æjã×çáÛŸ‘$ý•(ÏxòLöƒ.ƒcV“‹Eମ ¢sÈ6#Ω(:Y&Ïu'Cp4TICË50 ¯æ‘SÓ/² šlþP=öMZÁÞ.¶â}6¦VùZ¸qöR`]ïgœ·îõìÚÎëÎåêÓ^<¼2•ñ¾6'‘ø!¾ˆÔjÛÍ)°ñS7/e±*Ѻzb¯Ä|Ïmäïæ±¨ÿµNæº2]ipè½ µ.çÿÜlŒ“*Êpí½ñþ3£Zâ‰õj8ÊÄʆAvE‹ 'ïUŸÚjiªÅGÿqÝ›xJGŒ©´x³yAT·)‰Û0×ÚÈM:tWƲ¨‘®3«’§`É\A\»¹g§žC!ËnR®?s˜³†«±¨ÁÓ†Õ£Dr=ÐÐþ}»ÂƒÅL&º: í¡4³[Üy·7(Ø|6~ SD˜ž_)_™?Š%qÌ#]æ[ ]}1tàÏ]Ì,J„Ñ~Ä´<”¼™ ûíÉ­Ì»h§™Š“Àn˜. •mžð\ØŠ´ŒŸÏ,ªÕ®z‘즕sÜO½ðfM ¬ŸñÁÆ$(™f\{ÊxÅ_± ;fkòt3rëâÙ‡4AŸêAòäy9Ôv±b-<_Å"¸\äOÃVöà»y Q#î6À‡™oÍX+F\²7Cü«ñ¬7›ÏÕ“dEÛÆ“XSa¦¸ï$X>±"É*'ÙêO!4zŽ ÙTÂ)¾\À‚hÛþR:CÂ6[“3p×üg/sfÛŸá.Þ"a•`4ÎÚLgçÉzìö”)`9xoCTîŽc~£/pmâ(¸å²î¼=hé†çè aUÐ?&E,Bú@÷l*VÈ\‚³Ë“ñ˜,‘ÜÍËœƒžêP¾\p=¼G„­_'Êlà ·Ê·”þ§±6 ÃU±ñüèÚJúP{2y³V-1öô)2ÁBrÛ!•ž1—u )i5’dU¬,Û#Š6yƒ˜¬¶Î>?n7¤‰Ã'Iâöº—­7Aí}ݼî;‹ ßY“Q‡G°ãT8T˜E kÕçr6pÀR·q¼E¦dâî'´·]‰5¼¹ŽAÂtLMÄÌÿÍ 7ŒâÂ’tã~Ìaȹ—³ê¯ÂÁ1Ïù‰!A ÍÆ°—/!š›ÂmX9‹ :§Baím<à¨Ç®¬=s j`¯x-ª~:ÀÄbÉ8\ƒ¶¤à½½°4«JQÇÏÒ¡è~uv/§±¹¡D¹7î,ysš]Af´€·õö>òm†5·F4–—k3}õ7t8L÷Uíg9Ö†¤Îb)”Æôò?-þ®;6 á„ttÌ¿ƒ3’ÍÉS;khS”$]_‚ÉM.¤ëböN¹Ï窑Âú@æy7’Êi‡±nÍ#üϺ†Ø¤œqbü9Å×`fÒnöóÚ!Îlëmœ;Ò4tšhÉZÕÞr³¯mfW6ÚÁéN§¿Ý·éðÇ1—Šäd°ÂÒ¿¸ÿ¶33Ñc¢÷µ1ëŠ6ÙÅíáùk~³‹ññå\°r%—ÂaßÞna¸ˆdhˆ2™a:Áx®•ÓƒÅÂq÷½ú¦“ÑB‘#±hà;ƒ M$&wAŒf0ý^Ü}HK}Á&]%l}ÆcåK·ršy—lû›£q±Ãjvùäwxªç@ff†Ór®ìý°¶¬ƒbù7E±®âløàYŽÎ³õ«þŃ/'Ä‘ïj«ÉH¡!«—vÖ³‡{oÁ ÿ ˜¼V§Þ„Cúà—x e½ÐÕ5šYÌ^CŒ%ŸñÍv‡cÑ¢ì‡C4s-=K*^ø0ïW¡(¾§¿e÷be“0 ;¦Ê1ÓŒ¸kó3ªýú 3sø—çr2HgÞr’r$Î)± |®\ýõýÝÉ­Ü5Ô—Pžl6³T–!Á¦lmI\ù7–Ѫ8Fvì&C&`UTùºÚ³ì¦T¨ºVÉÂ/H9Å_8A¨ŽDŽ«Y=ìÀ¤Ê±Ž°Æ­‚lbaœÄ±)“Á(W>,7g‘Ú´lú±ù2ã+q"jo饴!ÜÁ¬Eùè~,ÝUýHîÅý8wžM™ÛÆs÷^ ŽÿöZMÐm¼öËž(…>;E¶"p1SI°`î!¹dñ®›ì›|$™î.OÂžè³ Ÿ…YÌ»bÎñ¥$¸äjY3BÐyÚmø´øîTfá{Âz—6öb8í"SaÝï(ä5=ð®9Òrˆ™i_#‰jÇ©²z ~¹A¾E?‡QX{o ¹xH˜E®9Ì<ƒXKV‰ÈX̆§?kf¡ëŠï$|É ÇØÜ¤üŸ×5÷¢ò0ôӯЫ­ ×ÚóðÑ)HVG~ÅaÄÆ&jDë¡úVWž¤Iœy›ðé.o<”'ÄVè™3ÿ¨Ÿ ±"–¹È%“»©>Ì+ß›IFÏ#²²ø­mú¤ŸáS£Xï2®ŽŒ"Ekg—wc±F&ü¹£J…_ ‘aæäÏã|ÓC¼å«ýÿñ)v·/ÄžnâŸ@ú­IjOæãï¥0ñíg^ä¬+ ü3÷~y‰ïŽÆÁ¯1¯qÖ¤±,b²'¼þ5®R@½ªƒ˜2Á€¹Ý䆃r8§1ä‚÷r_Ü–½m®ãZR-™à’׸|ÚB•7º²KÉ¡zaL*ÞNÏGØ‘29 ÛݼóçÑã|Õù½â^<‡YÒÚðäy8÷x[d͵tX•ó‡ëój£bÂôRÑC¬èÜÅ_yÝn.½‚_MžÒö|öeB›g”?p?wÀwRu_Ý=ø‡^P|}(N9àÁšâxdcµsE·‘'8ñ„<9 C6Œ¡Ù'üðêWÜuEfzr(˜-D ¿AVÚM˜â›§–ƒûó%Ìop Oc‘©’!s5ÙáóºdBýkÚþédÕéyïQÞO™ÙhÛb.K²å¯Ñì Q(‘ª)SØï?èçŸÒ„7bÂÎ.C¤=wÃÝ.°¸ëŠ«®³•ã˜kú.H©»ŽÏÎØ‚Y®*Ùs8 s†í`ÞßB\õ~«3Áw‹Û0=d!¬ë !f´‰ì·“äܪe¬ñµ 1~E2ž_ÃûíµPŸÑMãtÍÀíïcv} ¼²x zä¹9Áƒ=gÅÖ "jW¼I’|[öZ`Πtd,Àîï²ð€XàÕwqø e^hMgÒu°Àâ<^å'`ÃbMjs#’›ç‘=>¢Äx£³™'À^ä)‘¼°¬xñ 2g›æœ +Òy¡î$ç¡Ï.&øÝ€ÍÔaKšÕÙIõbä)¤²ç4Ù­m6D¾ø_Þ)Ðaš¿!ø»"ž§Ç<âŠ9Ñæ½lσô‹è2n•aòH¹r ¶Hõ £+ùi yÂ_дà***–CK­9Ãç—è»”!ÿ¦CöwGáï^dm—.ù^w;V)’§ióQèN,Ð`} Ž0Wܯ’Ýd{‚' :r†LܬÀüv<æíøù%¯’%æîG™ôÇ2òѳûE¢!áì-î‚Ãl²ôv ò˜Æ^îVeÚßFɦ4æØ~h¾ÂMÒžó@1Yi)@Ç¡g(Ð$É–D§ÁH(tO¡ØzqÊÇnIg<3µ€{õì>h›ß±­’?Š!æ­ 9³û)44L«¤P¾P]|½y~{‡÷{ÄÉäQ9<±_† yÊMè›ÍÊž©’€Ñc˜âaDb7á^[óoÜS&Ñ™ÙðL=ž{l7é€úù(ÑÔdz††Ð¬yļÓp§t$JvŠ’¿íjäõòÝ\Ùci¶zX•ofó7ãâ/|ᙋ>„áÃu°õëL°úþæ/ó¿X ¶ýÙxÕèÎÔ®áÌÝù`w8ÛaEEv¶PtÛô¢Ïë Ö1Ù’>!Øüö)lµ|ˆÊ?Â%ÁtÒˆ)=û1ƒ^‚ý5òÄde(Ï×a*¿^s‡ñLF–pê'^˜Á%¼¤­ˆŽ>Ÿ8G?šáZ?äºàï=òlr3)‘‚mËPXVˆ;}Hˆ‹è[D—¼µaBß#ðŽ«,¶\„õ߸'Ë‘:ŸÚöžÆ§yqÿÁrN;#•žjNÇæŠwàBÃàjä(œ‡²QíëÛ-Ò hÝû…ªN\ŠÙå°º! éé3³Ùh™‡'¾þ÷´å1à¯(Æi}ãØ‡‘’êZ½Fîä Ž±¡ Û¡ù‚džŽ@"¨žp"WåQÁ¦„÷> x)ÎÚ¨¡3…ÝÜÿžùÞ sç6ýëHôÛý¨-øá†CÉ\ä«.ÜWÒ _:»1ð]¾¨ ¯'Ò&ÓP-ô8»ws1þydp”ª¨„´Éð›úWBá§Uø\Þ•-ø"qÏå 9%762•R!Ò¿¹OcieGþó¶}ëհ˧¯]Ìü\®bÝ´ôåñCDS~ÑúfÄ^öKÁ-ƒ½¨ð5f|f 7@ã—64›Þ 'GºÐÇñßÑN‡Ÿ&Á“韨ìÂ.®9b;êò»N°·±ëˆžÀ(wìQ i—ÛCn+µ‘øL\Le3IíG½ó!”};È {<áê-|ú±Ÿ‹í×&ÓC‹2HDôòd~Óôb›+†yúꡬœ÷‡O³ú]5Pyy1©é§ÓGD™ƒÿFb#ÓÀº¢Ï+‹|â¤Ùàıã±Ö„Ëd¯õ ¶mjVIT AØuØCŒq·É°è¯F±í3!wõnΧ²,_†í¹#ã,‰©›0ûäCÞµøãÇ¿xñK/ä9±Í-kànS‘Ì2'þÊ÷YwÇj§eBøØÃ¤æÙ5,Ï&=Tv^äù×ÂnQ3xÑSÊÞ~ˆ×ÌC ?³ æßŽ#8|”£¦{á?­ô$MK–|_3¿ž€9Áño Ìämc| u¸¦ü€V„Ù¯!0žçÂ3™=Êe{sá§÷0{³•Ðï@j.ýËåÏ·ƒÜ*wÔ”—gŽÈ1×yë 3t1ÉJÁ÷§ÝœçÖ§à¿`#1l<ÊÜ®bcÊ>òtÏjœø¥ …SàÓ²ã cõ†ÿç—<!è¾~ë|1SÕص…l‘†*‰+•E­'¸æ\Ž“}@=OMf[TPWù™ì‡îC€ÆÑhr'¯…©¦ü=,èšÖ4¨~‹½¶qS€{ZW5éñÝænÆx1q¢êKÞÖLæ¶Ê™r“/¸s!f1t›Ë6þ|—ߨô(y­ÙÜž:[S/y:àSÍ/3Ô!už—/³Ç3äðíÆ^lü‘ú2Ç1Üå{&=q&)SÀÍëp§= n¾m¾o`ã4m°p>H  •;*g@?T¨²ö;˜²úžX?è)_ãTŠnS¡Ð·y{,±ã^·Lìh 6ÂñWB0V¥t£Ìß x=oGBý`Ùhì\« ãm3@°/V÷Öý;këšûû [ærMš³]ù}#j0X[tšÊEÕwŒû*oãfÞáöËôªc%Æ/\…JÃ<¥ßïév- T øÅ3´ïäò’72ëuKðò;¹›#·'A­C¥Æz[ìán¥m(ï)¥ø> Oˆ=„úÜŸßRX©JÿÒjwÜFnñãÀ5Ú3¿ŸsqC¶ÂC犱!áú}ó-.`Œ¬}‰uvâ2Ï h”÷·…(€‘°;f(Ì^ˆÔ¢“ú¢»4“Œèm&j[œˆ«„Û!ýŒÛ´®“÷ÐC™nhx=„aà†Û*>•=Z÷ƒwRg=Ä*œK¥8×ø$ïÇ:AÖa"Æ|wŽÇKC˱û×q’ì]€ãpý¨•ü”êOVº8¦F™x)h¡?= 7vC”„7©4 †©•ßéݧúêÖW|âÒTµØ/ãé¬yü1ª¿ !È®žÊç˜õ÷4®¨d.H‹-ÁE¢‚Äïô<¼YMÚÁuã&ôÛ·„¸7gÀà )z ‰M¢ûê£è˜6ä·Ÿ3­Ð$K2úÁ9o:üþ®D¦-_Œ‡ž†<1â7ºÓ*÷Qibyb6,´eõ~¡Ð»š–´r°5b\ °Ý‚ˆÎ§fN€Ç&_¸5}³Pn’(Ü=ÊáøâÖnè,¹ÇÑ|«ÖU4di`Õ¸éæþ3Ž×rV”XyžânJ«0\~Þ‡]¦y7,áÄø6¼ªÁíø¼ôŸ¦5ëáÀˆ:/ èuþt¾®å>}³aB½>t»³uÏÀá¶4Ì[øoNÕÅøÖÑr$vZ!=«º¯Fn¦q&‹*½»^Ãõ;Ûø»Ö@í—cÐpN4¼rq¯.ܼ‰--ú„XÏáäiìtþyà>ÌÀŸ•æxò—lËÀev)-}’Œ»Í¦áYÃ(ûZÊ.àܽš°Ä¸÷j¢œçYî‹´,‰[ëÈÆŸœ€Sv–áèÌ ˜¨UŒŸw_Ææúød;„ÿùFÿCù ’$w:ÊêOΞ~’6aòs*¹x›-´uÑNîö,œ‘ ©ÒcQÌWži ¯úUƒÔFž‚˜ @JàÊ‘9äòqTŒŒbÅù÷ðO¸#iXnÁÛ‹¡€$T½dM¤/ “ÚY‰ÜP©×P{‚ïR¼–éb‚Zç%{³Ò’¡ú–QÚM¶Ê? Oý²>á,Õ”à¸W'Ø‚CÎ œÒïxï÷–¾è4çÁ¹ËEÜñsÙ­Ã@8ØúÆÙßІçù ´Þs7[A/]xÊyûë¾íâîŸçó¹ÚÇ ìû ˆ‰ŽÅ²+øÍð7j×Ã\±zgÚþµˆ'|Z¹·ðtMö£Íßtø%PJ¶þÃPK¶’¡ëñ±HÀå˜Ý-¡Æp9ß›=£Äª[³Á,èVÆAÿv¬jÙŽ;mº¸áµZÿ£{6)zAÃ/Kb_"Ò­G@F¦ ~,¨>b³š.jCqø-‰z xõ¶5Æ¥ÙŽ Ô5©*™)gËœÒ!¡wØp`;^s÷Áw;^àónLXO§ªÑ‡²ò BÂñ*·•4ìñùOçL6Æ!Ù3q:¾â À3»ÐXH:âk`‹úz&׌ý‰ú ¿ò“ÈͲ7èÿC£™›eÉæòdü)5´yƒ»k?à>…½¬EâfuõÑNžåÖH¶ð =YÂód^ãÃaþþvo\"ì ‘b&«AT ¶L=Ë6¤H2ÑŠÔfÔÁt‰?0o2‚ºD^oŠ£“æØþåhp^½Ó{.3þ© `q8 Sš×°IN×À/§„.ßɧ÷ðzËôÑÈÕœÅÝѦüN ëñ–>¸dÆ.¿ÉVÄ%ÂÙ;Qpa{TÆ á¡ë1ÜÈa(ꉦvg'3ÛBÄ>ÌÇš,výü 6´x%Ù^g}åÇ ä›ù•gGD’¦“â{¯1Bü·\ð&·ë`Ó×°ÔÙÒ6Èý"_1C‰‰øºÃɳ"dps¼ÎȃÉÙÀù·×Œ>¶¤—•ǃ®Ñ¼4E‚ýŒ¢Ž[L9±¾"è8Êãü|'0íw>¨ÍxÃ÷ÈŒ¼x s¾ä%DV=‘å<ýmÉ—Ò»°­z7î§Å6L7Šÿp–9ž+kFsåD­=ߨB­ÑxôÀ‚¹¨lÀ ¹,Ì×oÓ#V~€¼MRhfÆ&Ö%§T#Xz3•<ù¯V 1ÿ±ôìÊCÐW1•Ì¿°,Vׯ ¾ô4¸ã–{¡Ô*šÉ9 { ¶§æù<‰øEWê$ïh‹+±aÓä]a÷ =&l®Jîž3!©‡àéÐ3òœÂ·ÖýÊÕB*[ýë(Jlàb'M€ä[}ø~Ÿ#o÷•6Úõükœ+0'ò:ŸÕÚ½ÇmÉ*l¥¬Ûæö„Fý: ß'•b½Ìšê %X£É’¼¨l«.^TZËßô1†©UÌÅïK87‘£\ó¡+06ø ÿ}•Û´/?], ¿ÃLIϦp¸´P›Å&G`Ç‘lòŽÅðt‹ª$º0!Ýæ«-EŠî%3»»9ÿîûèò{ão·ï-z0Ï÷Ò¿0Ée¤NL¡~ç¶âë”`°cÞÕP!u±ÌKQ‘¼3ªÂuj©­‡=âVDÆ7Œ=MëÄvù"›*KÊ_aƒpn»îˆnþ Øë½¾è¸rËþøC_wOoº ¾ó¬†÷éѸMt:\}4 ×H¦ãÍÑç˜aÃà‘àqú±i)nLS`®ó‹ð×¼Õ`c×SŠ"¹Ý¶ý˜c9\oÞ †GÉ[ß-œNx63™–Œ³˱Ã7­Èçì©ô®¥þrÕ3Á-5…mwà¬Ô[ùÊ?Îr¯.í`ú#âDc§hXHìsOB{–ÛðÕŽ^`׿§±ƒ§3è¯%óØšÈ8XŸµykÒ‘-õxoJ±´&EL#=UÙ˜­ñ ÖåÍiæßÄØ•¶ä_;Û(9þ3 ty] ‹îÒgß¹ÑYÅÜû¤AÞf¾&é¼^NG”öÀpÞbæf0Ž7üm̶–gÊÏP½ù3})ñ‚÷°ÅštÆû`ç±qð¸_‰óÖ &Þ‡97Íß´UÔí?OfnÌý‡8h¡Éºwÿ,v qÀ(R€|qîâF!T>µ´ žsv£Îx%é÷Êx‹-ög.û‹¡õ¶5ÑKóÇ´ù†ó8KÈ0õÍ(¼_jÇÎÈ*ÑÆt˜ýu,™>Ñ ‰v7è¥s§)²%j!ÄL–NK‡§¶¼¾#­\ÜdÖº1 oÿæó7OðCKÍÓd}º¹—\mߺéÌ—…Üñ[Eœø‘{Ü…¹hü(˜œÝ4Œ3$ž''°I¾QÌõ¼qä&£ÕÁ¿ÜCÿ\¼Þ¹–Šoà§ž$ð"ŸãÇËöp¦cr^+¨…Mçþ¢½®)FØÁ%n¶aÍê!D¤à碚ÌÛä¸|zZ‹_ ð«ìnœÿÒ‘T…T€§þ¶U$SQ‹b¥SÚÑ­ë9+}„íÉçÈ|÷Pø6QŒMææ2˜ëÎNú/„ ä%w/à˜eŸ˜ýZEöááqq…h{u2Á£ûH_¦7¸¬ŽfkIÿúF K$[•w2{±(<°zl‰*!Þói•óÜ6{”.ÝÈ?sÙ‡?\6Ÿ[—?vûý‚ò†p–zgËI‰ÃÂJivdö$œÛXÌbÎH±»êúdÕR16 ñöÑ“h¿ë>×tm>YëAÆååÔ‘›-VÄldˆûO½|[8Û%X‰Ÿ­˜¯x9fxŸåjY°K6-\ÕV¶uÍndé3|=Ô7”cÈ&¡F¨´^’ªäoò~ö¡àꮈe+Æü‚%Ö·ÙòíGÉ¿qñ þ<…ééR¤B®‘8eû Ö±Yo“{Pj²9‹§>±'¹°ÆåL#PƒmàVü‡ßi‰wm¯COŽÞ[NòfžÙÁÍ‘ûƒcz*0¡?þ§NØ«Ç6èÚ4v‚*ÿFþ‡áºVØ¢'Ndüª–¿·€­¸p”Ü\{‚™Üá\°»6F©‘« ‡aw%uC›aì¢Ï°G ëΤr†.Êäms&©“mÁ‹EÈUa¼üöœâÿ¬žÿÆŠÒlê à—ÀLô}y L0Sò2¼ËCÒ¸=¨mZ†NÊlšêtLH3CãâJ”~<ˆæ÷¹µÖÐ×a'A©ö:-¸³\7Ió¥×Ñ©30¹:YU0ÊkÄà³ÝøŸ§³õ– [ZÏ‹ÍÒ‡é‡ùïZA÷0–|dӎɰdG½ÿ|™¹òŒ~x´$ëj: ]² d³KúÝÄqO`ÃÌdnPM„LÚàÒ‡¯ò¾Ùx“—± ¤tÚFÔ9²]Rû“µqÏu’=y à³£ ¶é,|’,Ä„ã[¹Åó¼xÑ‘¹´rª$qÍ‚¤©ü–h#²Ä`ýŠ t9S‰*æà¶üIpYÏ— D!oªþ0ÿÀµN¼‡u¹š8ºîOaÙBcj@´¢Üø,<ü€Ž®†¿bÀæfÑ?;Ø©U2({>þ½Âõœäíîü‡ŸŸeŠË ¡½µ†¾O¥Kö$cË#GÖ–˜ ±Ÿnpl^âÃ÷Nà®»“”9µ°µ£Ÿ ê­¼ŸäˆjšÀtZë2ÙN:æh²Á{Þhõ¸ï+{W¿ÍÿµõƒðÛñð¯ŒØ÷Ižc ´ö½éõ c?Í K/ÿ!k"Îa½_ µã?Ç«€ÈÀ6ÒNhVТÁqdçõ ˜=C†{Šq—ó6âx($«Ñ§ÍZ¤ê¡2zq?šj’Ï™´0n {Ö!†×*%±^eYQeCÄNì`EÍ園úöuñBß³Èb²Õ#…y×’«c9bu¿†û '@2çd‘ŸÃí°áÑjN̸„ã´Ó©Mq)ÙLÏ!aî–Gøk+±Ê7¤ˆ/ ßßÑçâ$]Ú›)ËéÀš+vw0MÞâ–öeDßóï×üa˜Ú¨C·^»Éª÷§bÌ¥’´Üž8}Édï³Ù uD þinÀ³‘³û½6¦È:À®]ÏÁø¨:œ¿ùne­W“1ó¢ÈZ÷'¸®e®þ¹›‰0KqTa~þÌ(£„÷H%S7÷PÑYõ°SP ŽOñµR̳W’-›jˆúÌ€Ynãó›S`¬ê,¢cð“»Rrƒ¢d(”Ûpæëâx. ÙÚ‹¶ð×ý­Ö¯€S<ìHWaK`ß©¿PV|Þ®Iã-¿³Ÿ×-gÚ-°µr»ÿbãLJsñíEð¾²W³ý÷ ÃdE Zùˆ+›ÕÃ…ü°„™>ÿò”µñ|°‘h|çÔé±ÂdL¾+ÞÕ w¢UÁ&hÝ",Ìý¬½ÁûnoŽbW¯­çX±pƒ¶Ÿ›àí7¨}†C,ÁŸÇ$f½ävé榿Ø#:D{©Ë?肌.sŠ›í‰òˆëݼ…Þt›¦-èBÅsÉ®ÜÞdy(óCïçŽlÙ &&$Ų?ÁÝÞÍh®û CDÑ€äÁ>ó¸çR,sTÙÃ$·˜¡O ’p˃+aö쟘TQˆŸSóðpØnÕþN˜ò™°jÍY„S©¤/d]©·‰*þ|ЃŽfìîip?ÿ§¨&CßÏÔV^ŒMÕ³EÅYa½ì_näXWtClíÿBÝ0ø¯ß dóù3´G)~I0 g7`û  þ±8i×Ç) ;â]C0›p…Õ¬H&Ã?’ˆh“lÒñãlùoR´yƒw– YNr//©µ'q´ñ:+\ÛC ÿ›®æB&˜(æN4Åëñ¾^‡šæµRX}ö*Ÿ•Kv×ûcผéLƲ†9ÞoÃlm1"¸ÚÌ`m›3 …œäŽí íÁœ±Ž¬†dó‘÷Uúú©[–1P1â+íy’Y"ø·„(ÞeÏ&7ã“gGØŠß³17+ ½¿DCäµ+hyUŒ4Ydz†A/RêÏœB>ãËŒWlþðXgÞ+_N%ùŽÑ¤¨á/îÐ×&JŽ(ðÝûÚÁ¶{|ÇÙÓ®Àº]gÈ×ÙæÄ·û/:pªœ*>(Dèp9Œ[‰xü¸5>ÖñeâBP2ì}î$:\"ŒÚiàÌêñà?ã(_zVIëÄ5¼vÀNóÙùýÖìÎßóð¤Ú¼>ȯ !km „Í".‚üÈ_úþÕo5)SßíçÆ,Kü¸H&€ÛgRgqŸL€€å§a¨R£=¸«áonWõãÖ•`²a+Ô^l†eOwcÐç ølÓ†‘Ük. ³sܧ… @Ìð –‚þ3 Ô?6•eZ+C`Æ&œЂÿÆÊ†åðºÒ]*›~‡“™ø&Ì&Ù«.‚P Œ³f©›¤¸AO"°o*LhwßOu('7)Œ?p"Q$á´NM™àtÎõè <+)@Î?œK>M/¥åÃ8ï‹Ûy¦åO @é¼dée1^KÖŽóŽÄ‰ÔUÈÉá•Tè•Ò/ÝàÕV]\_¬ƒOŽš²ò§ƒ(‘Ã~ìý‰)¢E\ß¿\d}v[èŠ/-UP=$˜]™©‰ÇT‰v »ˆChÝÞ®J„é¹¶“"º ÷Íu¬o\Ä,g‰’ì¢e¤p—[xê/×Zås†ƒªm9³y°.§œ»òØ‘´ç¯( 憯MfßÎ6òN„|ä&þ=Ihº2Œ-;Ò_'“Å~ùÄ×Áß'¥Ã‰ËÅ\´Vœ/ òž= î*…YoŠP@ìĈ’Õý± ýêv•ü‚/Üó‘ãÐ,Ô€Šã1u]Ñù‹Ö’uI–…FÌçh./•&7„âÊûs¡Rú*Æí݇qͧø®/¹Ëë$pcT†zÆoò-špløŽß5HßM'|pê°»/ðÝNÆñVG÷AoF§+o_yv 9–å@^ž-ìÛÐŽ=rKЩUçqNÈ­§blç¦,Pù®N6è+°˜áñ(.2G§ùÑ_©ÚìªF*üÓTiZ½Ñ,Ò3ª×Ÿ˜6%¥³òà}Ÿ:ȵ‚ú«›pu®>Y;ﯭÜÒ#¨«¸”•< $sÃsáç³£Üóû øNß’ù¿ðd&_á®WìDÅ)7À—×px:(åëì!G?=ùSßÒ͹ӹíß)®Ý¸™mÄÒë½³ãQ¦Á˜5™òþÝ÷€L6ÀK{ ™Å4ÎNcÒy†Ìmƒ K]ù¯ü£z®,CÕG–X&zßI›£ÎÜŸB„Ë3&oG\ØÉú4ÍêçwwÁ×!Qwçž;†›XÙVùkÙ9~í¿˜cÛ­ìÏÆ€jÃkÏðÕw/L §9*Ç@Ym'ûðõ/B¡ —oóÇž¸fž¼È „VÁ£þëm†ŠØMïqìÅ×Tkêfî¿O±Ü[0}¨2îÿýÕ$αñnü‡ ^xÇÃóëBÄ}(¬0 øýšdîéðIÏÈåp_œ~£\ÚDÔËXi·/ðÍí(=w–YŸÇ*V«n7¦ÀÅEù\ÿSÞŽ¶clúàêòRôY5PÓ¶I0óÜþŒ´£¸¹‹Gj³æãªÖDè $ðÙ”ývÃÖI3G§àýÅã 0Pœü‡ã7ÕYƒ!:#M«n“Ì·á¢qYxÅâ ÄYf¨}åì¿Ë2“°ï4ÕÆ„¬t–#ïÎ![—qÊ£°¥)‰ÊІ[‚‰Tôù<Ö´ÔEA0ê|ßæG¹K ÿ0å„=¼òÙŸ0Yâ7pC^je¸g“žÒ_/Âaªm(š½œF›ÿÐʰE˜ú%%$LÉÂ9&Ø?gÓÞŸ6Uì‰ ¼A/¨ªÓ…2¤X4†” ðV/¤i~=tæå¯ Wk G×Ðzñ¿ÕOí€gôðððíîAÁÉøì`+Ô‹æ~u ’•A7o·ð¹/f÷˜*Ðĸ‡`´@%¦Êd{Ñ*ê|¬”s)Ò"*Aøw÷œƒ±I‰ $ŇœU¹Tk=Ggú¯Âp¥$8HuØ©ˆk¸ìLïízÚí8·ü|)t¾; VɱX#£æe'¹Þ‡åØÖlµÝÐCƒPl¿¼ ¶Æ<±“øc¯Yq5ƒ–YxÁêìZ|å·†«C˜ß—ÆÙ_øEg>ÑfËä£Ðï'â$sYPô›J|,†©®Ÿ»V>'ö=€ÜüBœ Û §[~¢Wß}ô¬þÅÓº±†ÿGó8þÃ=ÛÜÿp¡Œ*ªý…Jõo¼íAûðæ.ÒÚIu¶¸õ¤4Qi †—û,àD,GÔ%ø¹ŸãA(݉Â-ïÒ`Ç" ¶Ö¥„›ïŬŽÿ†;Vµ‰¯!Du‚¥•"þ\.!7~âÝ«ÐPêN­ àÎ1Ö$ñHÔe¶æ¾8„fzÛ‡/q§Ö"(f ?-¿¡rÀO¾À®k¨sÎöÞŸß-d"þõôïÃÞ„yq ¶aSØrØn\§ÅÕÈÚ×K@ i\ìù€Ê§ŸÓv›.ú5yÅöÏ;f$]Ñ›¬\‰SßùÀyãx\cµMÆ€Ä{–T´ˆ;Úȃ Ó'¡ˆŸ['FäOa÷.yÒÎK%¾[œ™ÿ¹Ë 0ëì$"+ŠhDU ͈§¥Ó*±Ø\„+Spb·nƒ`QËꬌ™PãÖÉcµˆãÚÌHP™¬EËŸ© ,r¼\iÓ¨<{_ËÜlþ‘5g9¾{ÿüâ’~HSlƸ:¨cüœÏÔDµËJdÜU!Xwe%lš¦†ö‚½.k%!´íF$–™F0Ÿ¼B¾ó¥#öI—Ì[– òåðçx„ŸûDõ½„%Ó6‚ĉؽΎÎȼÇ=~Ä ¦Ü­þ8,V˂мÀÖneÍÆãñrÕfД¦²×ÓyS§{àÀ÷~ºçª2;Þ6wóVõ,©Ä+Á!„—0—´ÒtG÷L†˜5:°8"„žµ3$)*è³f66…ð´0¨áBæ2O8¸ˆ¤‹†æ'ÏèÅÙ fåÀ.ëY£Ñ²[hµ5çîdç¶e®o`ƒ¥¢8ãR"zÅ«2ÑóblG¼1°—YPðâ¾›£ËÜ݆ð¬ì(¾Œ‡è[pšùzf.y‚.~ϧ×ÓñÒþ©$¾Fˆ;m¤ô¸9Ÿ-ËÝ\oOÏ]Ž‚Áà4yÙt½µOò'?Ï?²TîýÃÁßt¿píofp#†êøæè=š÷»¹*WþU9ÈÂux½4„3}°r• FRiÎmq°í£v­z,,`/›Ý§woFÁWãHœ¦“Î_“ Ê´nàÏ»éßíUÔûSj×Và™²¤f9!¯×´pš:ìÄ+ª­•ƒî0²xÉ?&> ®«Eqê#Xê"Ã6mWÂcÎ-P¦$Ïj ”°oÉl4ÉCc^,–8ÞÄ~\5t‡rfÛWq_'æ¡Ã>Œ±\ýgðgdž‚®çhaœ/HiϋĺñÅÐ!ÚÆ…fe€•£>}6¹ÚëÔ©j*ÌÑn+8­#ZçÀ`ÁkŽÛû˜_;Y…L7Þ‹ƶ¡öUÒùJ–è/1‡`Üè™LW½R%ÁƉ<½ölÞhÆY°ùZ‚wÇ]çY¨¨â‘¼Þí Üñy.Üá'üé:yÕÿöâjn‹ÍwnYŠ8K4ø½µŸêAäúxz|çï£Hî{Ì`‹ù7hû\}.øÍ¨ÿm¼(סNùk:\«.¬¹+ÅʼnÏúy¼šÞ4¨f6ŽÌ”ÁD;eØn]Œ£RsXËøJtŸ±Ä3Çm/)žÍÅß0Ñ´˜j8íE7uwÛ’§ :;ÄÈ 5Ò–¸‰%Œ•#Žëà”þJ6îÏzrÝÓ‰œQÝÐæÁãÔç s'ëðoƒŠmàf0Õqà–·¬e/u UÿKØñ/w¤ØŽ†ºR„›,ˆ“j¹ Û!$98”½MŤ›éèo£!v~X®çÈB˜BÇ©Õÿî‚Î9h˜&Ä,ÍÅÙæAÒè÷›>Tj§“lÖÒìÒ+`­<ô÷=…â1–ü2éÜ÷ØØ.cüVòe³xàË-NtDƒMKÒ`sŸ¿ÃSµõØ»j+•»ˆÝÅ`.– ËŸ âÅ+!´^ú!|¶&FóÖðêf(Áæ„H4JÈÀ³ff+µÐmih/MÅ®K|\n”¯Þ”‚PÓ1Nu£±©ÿeÃ?Ù‘.Zäè‚_´ÍÒ—£êøGܧiÔaÃ-N‰?ŸU§ôssïHù/0<8žíä-…q»V£iz)¶…– ÒsV|7C®«Ñ‰Z>xí»)ô$\ãÖ6ãâ5ÝðKRùðÈʼnã+‰Ò *²Ì÷~#¯¸ÂŒ=ß} ,'¿Â⃨btË×Újü”ÅSNrKü²(+pè¾7éÎÉbÜ£•ãpÊÜyô°c3ÏÉ]‰¿âr(„<†3 kÐ-b<ÀcP¾% Ì~Yé‡ý€˜lÃÁ÷áË"e0½/AN–VRµÈåüºÎd}K:ŠÞK·ü~¤^Q͈:Ü{:Ú¿€£úiø$1‹•®„¾™ŠœuD?÷tËÒgjÌ<묄 m¶$iF®(àïQ•ÇîlAvõÊft. Á¦ò \œ…cí#)¿ å½ïó#~?…஥ÐÐɇVC'<µ6õƒšAn榞ýsºJÂ/î€*“ÙÌôê ®aÁ)~Ìõ 4˜;H«Ëí¹‹‹NÂ>ÏÅDdìyn88žpgmÕ±$é1ÍQ<¨pî_^Xƒã±ê^"~½:Œí…j—áŽC!q;’wm@üù øizˆÜ9•È]°DNé¿J{>9à>‚æ†RXtâÔôq$z‹> MîÁ•ÞCê“ù¸eý5šÑñ–Û²Þ‚÷dÒ=¬l«F»LÖøòvÏoAÓ)­ÔOUºgÉ0¯ÞpüŽáé†âšûÓ µó¬–|Z~òD4ûd™(ñí`ª2©•–ÅûTÙµúv:ÁHŒÚ÷gbÇˉ,d˜K€kÊ4n9»bµªÀ±×9åAW–»N T+ÀRa5\òOâØ8 vØP‚7aÍ®A<°ø-„$öa‰°ž¬¬€T™G°gW#¤…³z+ábç)@{6ˆøÈkJ²Þ´Xðð·â:~…ú,Å[Í‹C¡n È/Ê£6i).'ˆ¥ò¨ij Ç«:I§ÉW²`´d®Q¡øÕ¥‰·þp gTl;OˆœÞTÏ¿J†lÀðÏ2¤iv=ô4N'ÏBF_™äVãVа}vÐ(yîá!÷ú:×hŸ RÞ'胛é´ó×GÒ¨Ëoñ¡Ïöçõ˜ùíÈ=àní …ruqÔŸ6÷T»±{Ï¢qp‘| ŠÄ¿» uÁ M$eu"¶žÅ"y¦nÔ‡ó_FAÜéB¼4¯Ê>~Âë§ãïÍ^$Ó@š|Ûwµ¼µqæì¤3â7ÈõÄq–)ÞìYÅ{îbS>iÍþ€|áºàÕ¾cD&r ÉýbÂZämÑ¢r5Ùž["IEésüf oç_Å/Þf,3E‰ŒLa»®Žc!›¥ ðb$þœ+@L>ÉÑUÁ)(ðs'ÕäUÓ¦õ+¹UÑKл¤Š~ÐQ×V3ý.¼™Lãwñ®~ dÊï9ÁülÜû öŸ/Â7RïþåŽLÐ"zË/ÑŸ÷=ÐkR&žéZ‚Ÿ]Ç“ÎëàŒâèW¾ÈŽ}=áZ¤£î |P“eoçl¢>®2ÄCW’´eF`\j)R½A/újsKLXü¢:6ƒÛ$¹1ÞCx9©m¬ÍØôÂXî¼½:ÙzX&å܇7 ²tùQwrQo+—neNo>‰†×R‰ ×+Yk=0_^˜D›½‡ÛIùìG]Ä>«;‘誓;opCÒ•À BÊÖË|Î4Fìv3 å›°õ™ªh÷Q¥)SY²}"§]˜ƒn#¥ôÍÏHóxß<Êñg´0}úå ~ìÜBnøÂ½*;ÌOüN¿Ÿ3¤›?ß…?qÜ÷)lA±(;20Ž,È í-؃'ÆÝè†ÃyXÄÞ̾#šº]w"s/”ò ŽZt–,mXŒØx äÍçÞ> c×. ±$ÑÏœ¦Ü?üexÚ&3îТîÛ…÷¦`Øý{ð@~>—Š`§½É7…"n¼y!æþúž²mpCðÖk³FuG#õ„»4Õ?l-‚‡ÎGAeÊ)ÐÎ<Å«:püQQعfŽXíæ‘–€c!‰dEÚ¨K›)é˜ßú>ܵ!²ýS5¼ÚñÝ?«³)Æß°È½Ûwz+†5<à­x5…ØÍ8Œº­²¬+gˆ^»ÓÁí-nµ=_'ü‰¸†Å“õ;~óÏÿ€ƒ'Å™zÞ ò{ÅæYò$Žp·›à€Ê ÞÇ`Õqàs'ý\“htÓ^lÝWû=‡áòÓJœaõ’KyÏÔ;Pgg6çñàý¿ëi¶C°{·)y×GÖÎØ@¯2°¹¶Èœ ÚκܺqÁì`K Yïz-’;è{‰tЛ´œ D>âò%êá“z8ûÃ’é¶hÞýQ°·Ù›Uë“-K…Ù acBø tŠcYm¢I,3©œ ìóºo\ûr{ Žo¡ÕOŸÇNcoûo’âL¯-»JZÆEð"Bá+ 8(Cá‰dA¸(Ð iücÛÂà_;,™Ù†/5íÉâOzä²Ý4·zþ[†àã©pb÷¨’[?¨AÆz 1K÷“˹üw&nÀ ‹ñþôŸô÷xGüH15ŸÓä³Þ:«y#· Qí8ªÂ’ÿá q«ÄØö®vØÛéGŒÊkp¥Ýi²{ô{;ÐË··„ˆÚø`ëbHÛc ;Üm™„©@ï‘h¶]߆(„gëUaâ‚MdÅ4]ö12–Žõb Ý™KÆæîh@¤VòðOÞ4k7JüP‡¤‰(õ¾»z×ÄGøøŸ±èßZqÃ;;úéñß¶(QøžœÉÇØ}!x®¨¿OlàŸxt—·±â%zÈŸá=•hØò–“=ï °ƒÓ~÷—ÛÏÆ6®êƒôõÝ`vò^Ô gÆ9ó˜@ÿ²tíúûͦ©Lö}¹L“Ÿ8³‰ú«ÉºGtÀð3eØé¿dhH}žuú,w£ÁÚ­0©a š_3ƳVwA ²•3J¾Œ7Ý’è‹m¡hõd ʸlÁ #œrc (etp)á§1ý÷fž§Ý~s­'ñá·pƒ#«°,#Jni0©;rDÑ~Ñ[ôd‹!÷ÓG.ÁÄ57Ãßqä@Èl¢t ën_‡ˆÒ{”ÍNÃmævÜæ Ôj‡ÇŸ•%¿AOv†N&ò®ù¼#ʺDú¤"·Þž‹#Ö.Æ.5Y8qÀLëAÝžr¨:2•ÊдȄ¾˜ Jžý2Á‹ŽÛéÛ§+pMâx–éãÀAX êVýÃjØùC•ðÇ ‘¶ŸÆø3B†´•Ìaú{r¥£¸kWx7 © “Ŧ!uÎÚl Έž »mÃx¥ßàPÒ$è» ­»s9Ó¡mœ?/‚?öLœTyˆ/·½Gi¡1,!P†^/ŵçÛxéu±„ÿ½V$û€ÂI;ð "×_µð5î'²¨Ü\J«_­QŸÄ’? *ÇÍ {ˆïfì$S&ñQ4=Ú;ó )¨?%ji½ð¥/‘{uó X7Èãõ) au„ÖÍ„¿—pÆÛŒóžÔƒ‰J{àF‡¼>éUîÞ°sæ/|R,D¶äº€î»Û·û)äv8€hóxÜ뮊ÝYµ:^_j§\ÕIîCal:ÁÇK˜Þín¶»ˆÙ—µ™@ÀY¸|° þÕÑy']ÙŽuxªú ôœE@~ú)h;Ÿš+锽±¤g uVVsØþK?ùßÛqá´¹à)ù§ Áš±çqÁ•%¬ùx®<ìÌ‚5Y¨érFžf ¼&ƒ¨½<³@;Hb2è¾CŠ<á¿Ä-FûðÅ'2áæ h¾¥O=Cœó%É•0èj~Ãl܇¶ò¹˜e²{åu{¡Î<§'ÿãPoñÿj’Ú Ôµ1M?Ý€T¸“¯@â á¨X ”›Õq¶ZR ë ·½¤—‘k?¬aÏ{=Œ’&°Ì?ƒŽøp˜×\¯ââŒ1èö¨M?K£†=H{whžòà¶žãRË ÈJ¡x?,Ëî:ƒðêY¤7ü#Tí³"þòa”wàjÌ?eü˜²o̵ƒð¨|ôO…2A]öÎw«U¶e%ÿðçÆ¡§<ÑóÝÕ‹µtiÀÃÔº5€i?/cgE8‰Y2‘D>m­Ž.ëEó ‘ Ñ ªr_±£S›.û<“$-Õä-6>[Ž{$áWØÍ›ëV(ã¸|ƒàÈiL4&ÜN<ЇNâ<ÆÞ-¦ ç6À±Ä­Üž; ®[ceÝnìUC­siœ×ý3¦­•ÕØ‡=Æ4ã~8V>>Á7ókä}¸u/éë“öß·P³ !ý–ð\Ðö‡!{7ï'=Ðl ¿J'ñþ7þgó\A¦Ûê¶©qöø¥ó!ùWÂ5k'x֚ł Ô‰ÙÌËœ§Ì,bÎ@­óülMOÞ¹Qi¬]Ê’=PfÐìp|é%N˜6A †ø‘øéÔ>¨W f{¼®âW§)x%1™ô8¨€ø¡ >Ù™¼±M§£bSH؉ø=š#*92dÓ9̪TÙ²_i<3Š/H1§ßñFÈ^²âÐ.~Ú Ÿê­º†DÑs1Û43ëãX•D[þñ,ÉÑ6`ýŸp—Æ’­šZ,aÛ”œ¦’m=ÂÊÝ4ˆXS6û<æ)üoüÃG­ª»WLeûÒÅAØL†íˆIf*y¾¨dO˜ð-önÆ$˾àŒçÊlMv.ÌŒ×#­·£!!ˇݖ'e)Y˜5.‰ü]tÄ^ŸÇ„ý ¿ç0ŽÀ<-}.RÑ'Æ2H_cÈÛ6 G&¿Å2c ^7Ý-­fÇR ðãÉU_þÜNcÝŽ7`bߟf 5´`£Ko®.’,W¶`yº1(X!M´öûAA÷8x9ç4Z®'é Vþ{ 8]!Î! ¿g:ÛÔÉç|VÿÅ=Ïœ¹í†PO2”Ý“AóKŽÚÖ,"ÿ¿+'Á­™0•«Ñ¨iµ:ÚÑ‚'•NáÙ·8÷Ãͪ›ÿÖÔð^aæ|_ ©@Å·&äRŠÙµu=~O_xJ`µ°QU}Ç)ðg'BÀ"?œ$Å‹‘ŸoÂÚ !¦|žK^OV̨‚Ÿ¼ˆaµ(Ž5Ûõßäص&H±÷£q®«"™SÜ‹z þx [„¼(r`‰ÏäHÅQÛ×±k@òäMÈ7[Ï„Ÿ`GÂ[8Ò‘‰_߬C©Z-6ª Û·fc«C)F–áÁ)àXϬnÁý›ªð£QE9Îc¸cÙÿÅÿ]þtè8çféY ÿ¬Ái §1ƒý)˜®D¼m^À¼¡ Þ³´ùè(r ×dMgÉW:pæWmÎÒh3Ø-ðÿÇ$Èyy_øºMžL°LÁBf ÂúëaZ£,¸í>Goö“µK{è‡?]o^ý|»Óƈé*é%ˈ¸Ñe<[ó “âÐáµ'ýlº®ÚGÄ–ØŸrd.;— þf}XxPÈÌ [VH1ë¡(·ø§°ÑËäî`ßui¹`w ƒ™ÚcáÏæ\Ú·7š8OûËgqçLC𽦫I®Ö=¥€þ®'þ/ÿ8ôB­$^#:õAìʼ5¸gG\ªiÉ›LpÔb,xŠvÒûþ§i_˜<6ÁЛùø+.œ™™ÄìÔùÕ§²˜Ëˆ;k 9Nv1)³œÜ«T"E!mþ]Ó£øs•³Èu++2|ºï=gÉsf°‰%GH]V ¤×^fI­û膽£\øk>9Ê&³3DòE¥!mæQPøwæ8í"BéE¼y©ÐÚ3Õ](ÁÞB<ìó Ü7>Ã7–Ãà5¯¶ Ífå®±ÜX­},è‚nZŃÛ÷ ´²ãnqV"]Ö³ès"Ì'Í Þv^„y&Äãh2z_«‚Qñz³å üýŽ|¹ÍO˜¦Ž²½ë¥‰¦½ ÉŒã*ãîÐÜÞ=lÎI¢nªC þÚ³7ÉZØØ"Fžß6'§×”c_¤ø¿úX¦¿ˆÛû&‡{ltФˆ¾‡ .”Fœp»BsŸ*Ìì˜YuEŠ•|]KƯÚÉ”kÅæl{àFŽyÆ~7âæ±qh°Jľ(ÃéD{ËcXvЊ<ø>HºV[°ÿ[ÿ¸€¶Û˜VßÔc5{y'}æ`f£"ŒU,‡¥U1¥¦îÍù·ÿ}ãÁR(ˆO%b*ÄWì L¹GOzŒaé2ùg¿/@ácòD[ºŠ>¬þ)Ÿf3±ÃQ`Þ.¼9 ¦­ÏƒidÖ“~H3U%ã oãÎiÆLÌl©–Ò%Ÿ×ûÂc™£Lh\¹¾Iÿ÷þ .VÚµZEJ†µšÏÁ¢”ñ¤ß! ž«Ãô´uàL_yosdéêE`«)Åh@†¼ód§W0œ_-Áâ;mÙùI’Ptø+ZÕKá2»BЮÏGþ(ýßøe'»³--H^&E}cƒtˆÉ¥ (xЈœÏ,ÃèaÖö¾ÙÖì¤.S]B“«‡¹?¯ŠØ¸à(|¸­í—¯nUbæ‡NË`¥½ÑlI‡'®b°`~3^ø!€#KXP°6w*5„Zÿ‰ß—‹AçÚbÐnT¢÷þ¸’^Rì{´=zú+ õ[ζŸ3c;3Ò¸¼ú,!>‡ŠÜÒcG»x¢›ðê<74ž­Ì®•@É\ø ‘H–È…ãŸ&¢§Í΢¼5”•;o"=™´¯\ƒE¼A‡Bà´ð…ª‡”Jý¿ù¬°Æ^úÁ·Aq%HÚ€4à¨,Í0Nñ‹Æmy=¶Y6aø³B>½+£Fw³Cb‘J PeY!rûž+ñÕ"‡F‹q¸æ¯Œ÷ú#Y{£ &oëDǧU¼çá `„Àú©Í°+õýÎWóy6­íœ›¥Ó¶‚ÚÏtc¸¥æúLÚÚ›J\2¡W×àDåA¨U)çö|dW¶[³aý½Ì¶«‰×º S˜Ó›Üâº:9Â¥mlG³‡!ß«‹ KÁ²kÏ`sTçøÔS:ê¸ÏÇÁÿ—ÿžzñ~÷ñ»û‡qvç/j4"Èô†J`åi0Õ…õ¾ø0ú598‡ìŽ5‚ÝiDZçÃWzù‚‘3|\Ç\äK¾@6›¸,¼†e#ËaärÞˆíu§°'Ò2÷áêú±6^w¼N„½º»Èec8ë?–%›ãÂ,]r"Y”ÙzÇ£§Y"Ú~šŒ/æÃ}î]¯7ÿx'Í•,FËœPöSÀŠÙmöäª~q¿’©Æ^=ÈLXNÔN§@¦ÉLöºTœ,ñ95û÷_0ûµyzêˆTg:“ywÚó›ïü¿ø¼Ó¨µŠǼ|Ú¶`™òÆ•¸Oƒè7§˜É©|¼Ò$ ¯fv¢Fô5òº]ž­_MF¾d“öU˜²`ŽyU€Ž3Î> Š^f„oCѵ’lò¤ ©ëàOÞІS–K‘áð£¤Òé tUÕð~j”ƒ¬ÖUð©ü âÁ\޹Àä›þxbU1Ï×! 6´²Zwgg I¹ô¡ù%òÝD…¥Ü¶'aÖi°®xª n'?5¦C3û Ÿ%ÃêIE¸ÆxÄVíÏZ ‘¹ëØ·4’½ †« ZFžfô žAPÅ4P´tÿD½w<Öÿ÷?no²÷¦ŒHÊ×ã¿ÛÏûëv]O·çõx>Ïíœs?÷ûùÎs¤þïîDâÿÕ‘ÔùsˆLV§ؼü;,ÞnD|RáéRNAà÷ô„=œúν\9‡Zœô6•9 ~z!Íà_¥}@7,K÷„wg}…÷þû&6žì»&¡þe›©þg–æ2öËz0Ïü]LyíqL<[¾N¦s¢oq©ßŒÙÍíÖlï%YZ%'É~ŽÏ@«I¢´ñ…Õ0äÁñÖtó‘D8ñx…~>îþ•Oo¦㔾S¹£þ² M_–!tYƒêÜTªøCœÚí;F´nåÑ[]Üã6mðõÕ£1wâÙ¼É]#þ·}¹L—…ÖvÔë¶}Ⲍ_-ÇæÇãéõ]¡0«ñ2öHgPëß °ñzÛ08ÿr¦y»üÑ}OöùáÆ]ˆw §á¥t|@nÜЂØãO 3w=úFÝâ„D€•×9`X— Íû[ÇŸMâ–üpà/=0Þô{‚†&p{ùN\b$ÏR#bðÔ'ê°ú$l»ÓÉW$e1Z̹YÍ¿‹»ÕÙ\†¼ã âùñÿ“­'ÍV0}ΈÍyò|‚Š0m\~mÿÆBP>-Åw„ˆœE`¾ã$1NT Á­é#ö¿›»™Ü-ƒúD9”úæËñŸÖ’%ãqÉÞQÔ«Úƒ†<kúÖ±¾«vdƒŽ Hò‰Ú0&=Ì"kd7aAË-lsg^ ˆ¡?%9<,È4¯bÉÐyðYÒ7ÑØ¯ ø·uÙz9/´¾9€búÐá£8û–(ÌRf±>t¼³}"NiœKƒ„£þ$v:ç –T¢“îQxrÕŒù.V‚#œ6ËzQÓ¶:roŽò&,‚s¯S-vü”I/´¿çõI%Ùn+q𯮀N_øV¾/¦Ý@ùÁ*Ȭ>€/¹E#øŸþ^„ü”¬ÇmÞ*l§›!¨}þD|¦²mF[PŒ¿[óRðѸ•,UP Ï–•ÛQxqÞeðuVÍç‡koÐ'X4ñ. Ö^"çû¢À?_‹.Ÿ ÂÀÒ¬‰[ÿdªúGÐ8VãÏÙUÑV«ÞÝhÓ8†×ån¤Â2R¬ö×[L2ÜËñ’%q´çj¸·á)¯ÏéV¿Æíšx©b¯”±…&ÉìÂ]ê6].•¬!ÝÖÍäwa<æãÐÛȸ`€÷dF1á–ƒ˜~ÞÒ[[x¢Wb@ùû~HžqoDÿÍÚhùê0þ¼-í~ÛAÔ2¿€±É3fÆ+Ñç“<›lÈRj _A÷-\ÁÌçÌÂò feãa HŸQšÒ zíw Á¶‘ÝŠ=Ê)‡å0Óªsìî fµO™¶œ`:[<`Ö >;b¹‚ {œÎëݵ‰s¢2܇v^[Ö²eÓDVtZþûªKOäŸÇöµÅ\Þ©#¸pb>Ö–I2}Ÿ.œsx9÷ÕÄNonÃ8û¹‹ÙùÕfm€Ñ³#ØR©@¶o¡"ëšþ MZ-°øà~˜Zî6âÿ¿gžq–ñ3 { ë\N»Ê4òrýåX¹X=˜úŠbLàU¼të$ÿÍÖá5/r©žwH©o,çWƒÓ±©æUŒs ò÷ä«qG&•€Úô¦•–ÏEe†ÓS³ .]§M›|ãAï­Ì<{›œÒ{Á‰ß©êWËcà¹T@šF¦Âæew¡7Å ßv¢Å»%a0Í TÏœ‡5Q*ܲºU8óì°P†Ô#E¸ú÷"84`„àw«Ä  ‘Î't;ôŒ›Ëñru/§÷J”»TtŽ˜ÓÒ ¥pòzs߀fƒ‡Gö?NòŠù!^“Á{Q6(œz²²…8óÞ˜ùtÑäb¡P[‚ŸôÍæ˲[Ðhq•ë+aBHtßM&ªî.Í¢bd˜yÐ2¼X Dçn¦ícBÐ'ñl»{€S>Òí-:–.é:(¯’Î ˜PÔø®Á.ª(Òôßx‹ìf Îêq,jU/^WV‚ôi;áöÊxh:vx8¨e‹ á¡ ðæî|Cš ?“[Þ˜›×&ÁL—døôTM«NÅIñÂdÊžÝÈNNàÞ‰+‘¥5ÂìÂ1ü´#µÒÍ[#­ab×uhHÛ ½§gcRÅ}.N¢²é\¬P§½W+ Ó?¥>Ö3ýø;÷ w™?Æ‹«áßæ«`çò¬GgQ,ÍüË}Wvd¢Ñ0ÿþ œ˜ÿ__5×›NGì»B^Jû‚.7 ,ÌŸÁcZ(µG/Å këHÈQƒ%S™è¡zˆO=¿`øØ•Ð8ñ*”O‘acL²`å%F¦u+cK¯8¾í–Æwá¹`^s zšÌ!Ã@ wÌLåú¡‚WmMÐWt ^ÿñ‡H„¬Á¤4†M’tÁ‚ä¿ëœìãðûÌ4 :„Û95X“ÏÛºC6îÓ ¨f)ÅVýò¥+ŸŽcoû—à»}+PŒYas^4|hS†5 a­Ú<ÐÙ¿‘ÙªQÚ² “5­ÜÉ)Î7GÈ@rå‰=gj!1’ÿ7“–ÀÊ… g\aiwAo×r´ÍyIæ·ÝæORŠHeöPñ=qwaÛ.á’§Mæ6Mï#ñŽoÐËä$oñcÛO øg<+MÚŠŠy£¸i­R]~¸VfÜßÁ’‡b4°]ŸvÛòpM߈ܒÁßt4Ï6œÅæÛtJ¶å2Û8õ/âl æ"D^A¿Ø Ãõ-Þ®l…õOdñ™ó’îaéðÚ7š˜žGCuÁkî˜c4š›^†ŽwÏãõ6q_1âç±Ò*âBQêp4Ÿ Á3mX—\8Rÿy:☔ù€òH—XÄÀ¢üÄû× R&x[L¶'Y={±˜j½…_¶&Cô›p,+žJþ;GOï2*™w@θÛð“x@¿I<ÖxáAÃ/D13“|ßПN%rÇ—>‡0_ÜÜU}{±ø ß•Ëø¹+Xä“,QyÂ×8 ŸÉ0Ïâ(‡»Áݺ ¼8-ñŽc?úʰÈÄJGIÁ‹/%ä¯Ó>‡«v6űw}`¹Ì7ü¬t¸õÈ×·x}Ý)XrSWw<ưßðÂGw»…£ÏÅåX“j;âÿø»ÁX¢-ÏõIÍÁ½ x0Ïš‹¾$AKƒqß± ~çâ-øFl<ÓûmFE¶*07¥ -œD;ÄOÜH võÃkº…¼ü”λ45Çž C±ø~(3ÖX3³©ùØÝsŽ‘e.‰Flýþ>È~¾ìßÁ· Ð ~=¼×œ9µ²Ðúù#"Ï>±ÿÛ‹9¨óo5^ÉÍų Â0[H“Y:ws­î¹ht¯·ËOªˆ:pÚ†W^Ü$rÍÛ1lÍ|lf­zô¬)wÒÑÅc"0t¹'Δ~Ä5NG_l÷€I SÈç—‹†ýØÇò›P^?•ôÌŠƒÛGâHòßn†Ù&°ÉK¢™g7rU_ñ¿säÝà€R¤¾ì™õÓ¨ÖƒÇ\ŒŒ›ºG §ø°ŸvßaS¹25¹nè&{m³Ðjê<ž‰Â ^Xò\\“OÞn­%»r_À™â\¸OçàªQ\ÁÀ¬ ÆÎÿÌI àÿü_Jp-cDhK‚„’5ŒO.ÅzÒË{¿ý'ÞO©† Ï@²•<4.šÉµÄé€RE1¿þ¨6‘U†Å³vÀÑÉ`0U]²˜Ç¹-ã¢æŽÁ4ÍXÞó–P²^‘nýr…ë¥y_½òŸ œ‡•¦ç îy>69M&EǦCÚ•·Ø_ÕC<" á´øhÌSóÏvSf+ãMßû}åï¸Ú3lLpŽ$ ·‰óG á¿ç1÷-s@ß{—W½ <7¨WádvœJf1˜tb¬My óºÀ¼§ƒLXªN¬§-…þ’ظèÚÈþ׿¦0¸©Œi×1ü•Ìn%8í‡,xCTë‹ÖwSÌèêµBè\z Ÿ>N.œÖ9*Fy‡`ÿ„³ðgçw˜í¨B [ß“ôµÕ°A!µwR9+š•ñbýNIZ¹¬Íµ>r/IÈû_Â&Ò–œƒü$&ÊŠrÓy~Ï¢zßÍнi²»,;6¬}q‡ø iÙ£—ÓYæ=œ”Ê7ç€nC-º'ñÉž¨Lœ´x«ÜU ›Ÿ÷ƒ¹¹·A5“·³QR®K‘G'gcU5üåÛ`þâ«Xþ+ô¿â ^.ÇK©‘ø_S±¬¹«Œ¿ç£DZ2>4éĘ“ÑÓݘFÛÛП˭é)ó›`®îŒVt`è¶$ÄÙg:²,Ôý:4öfÑWÜX7™¿¿/€~œ3ÚOK†M/¾¢Õå;0«ë.Éu½Ž7_<$É”IÌ{ ­!þ¹>üø”÷þÂÿŽï :à/ (ЬÇÄ£ ÷űÝ}¶À´åO0þùÞ?¦hŽnÿŒÃŸ©@Ñ$îì.[–!¹‡SìŽÃBPá×Î õ¿Àí)©Æ;fð—¸HáÔžý˜ø, þÕ´¶Ç›àá -4xÿfÄþœ”å¼cî=äb›8wЯ]‘½[$D¯*ÅËÒÉëR‹ùUªNÐ|7†ëv:Ç•Ž«‡Fýÿõ“·^¾$dæ_”{°nÄîÇðý.Töí}Ћ¼M~=µ»jffÕ]Ç} ¦­Ô‚É‚ƒ7Ña±·óâqüVwš„8ð Q'EY°åçgÒ¡•CäWÆh¿MœÍ[R™o,`ŸÍ#S…?CÏ‚ù›X|n¼[À"ÝÝ`ÏhqÒ¶îgÕ{ ÃòŽÁŸ?‚à8ÒH•¥ìÔQ Ü0ð_Æ”AuØ&\3…UrŸGöÿGje0sÒÆaŽnŒrçuÙ²@0Ÿm‡éƒGQÙ(Æt\†ð\qúîáÜ“[Á? ßï7JÅ]Ã?rÒtêŽs8ºUŠ>ß%Ã2Ù_œ¯;‰S|RdžãßÅVäíIÚ-Ù„3•©ÛµYTÂì+ü>ÜFdE½pÈЃµËÒôíÀB&€NÙä%ÑXXñ³ýdäQw{^|Ù¥A¶ £’@.™ïÀU¿Nâ/¡¬ütS](š/`YÊ®øHÐPÜ"~š6ð‰œàì¯à]ÛGXÁ©3‡_‚08`6’ÿÛ*^¢‚ƒ?xÊœ$+Z‘î²çŸÜ"‰ãv“©ûâÀz× ÒódñKs¹­ .ì¯z!$N@± «x©Õ+ˆfªý¨!Çèâ8êb5‹¥-ú‚ûíz!Ó# TEó/ÜW§o§H1§Ù‡ATXDÝm×~˜ˆ“u`r )Ï”ÇÆî°eBr{`ûsG|è0DZ>*°•Ö2\¶–~¹ al&',CŸìÒfaRÓYÌôsœ¤H7ø. áJnkrv±ôR¥(=®fN5ƒbA‹çŠ)UØöçM :ê Ðþë†w†Ú˜fï3²ÿ÷›ë… “^îÙô"îëœSdü¤|¸å|m”DÇttìs8°Ž”–Ài±üWm“øJ´¡¥€›RFNõòmV ±äû`§ì)îÕþ0RjÄSúŒi«^aâÍÓ°´üvj˜£ËVaÞ¨„ûm­QËü-O¹Â}‹ ‚UY‰¨åÓÊý’3¦ÿø5Tc³/='û^L$Ei†£2ͧ“ ˆi·Ôé¿Y>˜ÕmÌn—¢èUnñÄû-O6lEO¥+\$Ù‡I‹Gã¡*qÆÂ¯âÖo:ìõí8±‘òê²ÏPÓgPP©òQ!(!#hüãc©ÖV;zrõq,Ó†½=°I|§]…ù·–B›­¨VåÿÙ­FMÄP(T þ,ì€ÔÚìm·7$‹÷r‹ä­Q°i4ì8JØ—íÉìÔò•Ü´ú¸áÚ¦ ~>D…ê5ð&C®¾n‹éœ2­ŒzÌÍ23¦—› Pc0›MøEͽb¡sú<8U‘óù6$õ£Åðê¡¶~£)óqEYétVoù^/xµEñß}–vsžªÌ¥G ¹>ºØŒÿW“{í5D¨^E LCÆ…Üá2Í&ŽàŸÆÏ1°äÀ¬ÿzznF¡Â z"\ˆ¾–þ‚Âsþáòîhn·¿æŸ?Œ—–Qº!É›]+×¥u¯"ù¨C/oüŒË'O œ×Lë¾9-Áùc7<ù—Åœ?@xí%àwf28SÏnÅ%ÒѶpã± |!Ѭk!íÊ…±:÷ M(‘½ÊešÓæ³ðU\ƒîú¡¨à8êör ¸Ãw›”˜PÛbQÜ Y¥Öà¿Y5(ePÍ3„­´(Á²’Ì*ï Ðû•ˆk–ïb´ŽÁÐÒ1,&ô8í eUjn”¥tr™ gFìo2ËàòæÕÁQµN(:¹‡l n…¶9]¸&í)¾;€ÆÊìuY<芣ŠI›hªø:H-²aq-¦˜šò { àÓÎü'ú«7ßçév„^>54`&ïe¨å).·Ô¿´®ŽsçÍÅöûž4áŸ2M-™Àu3¥3W®A'¹÷IJtŒñ½Ž‘³ ááñtÜ“–‹»ÏÀÊOɼǦµz»ªS_ðß»%?¯u½´N”V’<éHE–Éy_6ät&ÞBOaZ&~\®ôÃĺt˜6 ר‹£ÕeÀŒ‹'54Nß—ë¥Øá yœ÷àrlÁûfõEWPF~ä––rü9­<âMÙ½äè½:§>ŸˆŒèï~ ¶ò \ë¾î6qÑKh”ýˆöÆ·°ãÄŒÿáÿŒYxØ o6U’ ;ÿâï©wÀý‚"¼r&Á6E å]Faò÷å+©8÷Š»2 Ä üà•©>}cíÁ‚ö "/r‡˜…øyžó3-¶×f"޳ÚË͘ó­ÙAؗׄW»ïéIÜóïÇàõËxÞÝ]ÎÐUú–¾8Ê­|®Iß%¿€²¸Ü"u¥µ ö(­¸†9@°ü¹ôÝû 'Îóñ™é(ê:ŠùU @¹55ül¸Ñ¤™ÈÏjê›ßO¢äÈÌQ¦TQH†]¬žWmÞ‚Vp<ß…îqö\ˆÔ2½Ö`ÿ*åšx¶7Ÿ‘J9G’tƒ¶à$äü+ÏÁÖe‹KN#sê%èÛR×øº4Ú÷ŠÕ@ìãxP¯z›CÞGÿv´ø´ë¾ÈÞ¸Fú¼`Ûì"Ô˜)ÁEýƇ;’˜øÞÐ{óÜýPgwÙHþG¼‹S?ã·ogÉ\W/Å&Ò¬3{yW.5€ª·šÍ  ZeH{þtêúl:ÀÖX6µßí4Ç+ÛªÑûQÉyaÌ–ÍÙ²fSàš$ü±™ý<¹óíÑ"þkÚrq:Ø¢ŽNE²¯öEtí¿oøªSŒÏ ³éGQPúŽ_-ͬ´ri8˜Z —’Ä)÷ý¹§¯HÛJWá<êóâ:þ싽“Ñ8½ §Ï{†}s@¥·¬´z¹¯ÆûÐ{¥Mƒ×Ñ.ϞϊûAK^jƒ3µk­Ç?Õ"42Hˆ¡ÿúÿ lÝ5ž‡Ë+^ðz'¯¡ýïvcä’=¼…&Ñìúî¹ÌæÓ2¸æ‡+Ø@Ë…áàZ¥m\ØšA~rÃ,6kЖ½’”&'C-\£Æ¦?û—šôÊë“ZÿW‡Çâ_@Dþ~Þ$DTûá^ÙW+ÇÃ×à‚XOH>+¶÷‚¡©{7þ.é]øœô·–ã”i¿@ãoº5ïü¿¾ì1Çí¹+;¨Ò˜îöÚå˜Ýõ‘¯ö]‡-^Huÿ>†ÃFx[­# -aòAܺBŽÞwÀÉe.ÍÂYlmjs¨,Dóž‹h.š3bÿÙ¢F2ç´‘K6¡¡­¦Î÷¢VƒY°:¾8‹6‰m¨5AæÝ!i«>cÑYOÒ¹e3„Õ¥À¼Ýs°è~>œp3½úäƒúzÎñÙJNxý)þ²3FäwA@ØeÎåèVnøO5¢ºñâŒïdÄ}Ý FpƬñØ4óúJQ¡ã=¼Ðƒz¸Ö`z(¯CÉÝöб¯^,;Ë{¼|¸v½ ßÄ%Õ?Ϻ±Ey¦¤$,Ž‹s(Ä¡q p?‹ ¿ãûŸÍ,:ŠÛ~¤«='£ùÍ$hlgP;w°ù`ÚÞü N¹Aî=nC'^5¸6‹áÖÙÜ‘¹¢l B-iVÙ‰U³}Z RÒ‹èú Ç1æÎn85Á Ož •c\x­0xŽ¡=hû·ŠÈÄZ0Ŭ‡ð¬.»r€°ÕøºLÂ~ pïÃA´º& KB¼àXPÜ-÷á½ ØÊzí÷âW+¼n+H¯ùi‹…‚gîø@àú¬9‰=’OÀx¦0)\È]~ŠV[.¢…ª¹6ÛÚfù ²ƒ³î¹þè>ë_,"&ÍqX|÷[MÈ2mºpìx¸ðî$ _{ÄþŸ¯—‚Ï®RXY…v¿¹iÑSAâ×'nv§6­•ðƒ?÷œQó…+^W¯gJWŸAúÑÃ>˜@;/å×'{£[˘a6—+ºr/ß݄ݛ{]?Õq1ëQ+-î>(JߜޑÑ6ôi£ž:mGUtƒ`ú¢×Äþ€¥­¦Z¾Â‡,^â—2?5 FÉ¢cÌ2üµü·?>×)¨’•Ýüè¨=ð[Cº,TbÓ:ƒ¹Çàœh è.½çó<¸+ªæTß~Ézú’,þ1‰’šè2"Mc ¦Éíïœ#ñ¿;I—ê†o ¼ê [ÙlYú!²ô=º )Yƒ­«¥n®‹Q°B—î©~M—J\ǨÐ:f]ZLÍüï bGý’£³{1Ïtü|Z‹Î˜4 ÿMÏ 2wãë>%C›†¼éìÓ¥ÜêÇ9l¯û^¾”m«?®ÏDÕCéá¼TvË}#žúq{Ľ˜ôÆëøj·9]]|–•91ÛV#z¾u)ê˜]㮡‰1:¶„ÑKï«PW_‚®ßì~gWЀ›¶°ÜÈ.ÔwÁæôÀßÖAí¯þã¢?‹Ó7N¯¡ãÇYnj•9ý6ïùß¼ ?.0áN²]ìÕý8føn3“´+ŒphýœÎÄbö⮩Âl\’y­3¦e_olƒŸ³€~”¤·ùžÆÁ¡Žvœ¹m'|É8Ý,|&˜þ×—|/ŒÁ‘ÖTöîòöï…V)˜AÌÁ¹ìÅEt@x=,ý¶–·}¶,ÕÞ~— ¦ÒçÖSQi¡HÌ•eW§81•÷ß¹7Ó‹ñؽ@¶³E‹Ëª¦_êøÐh~®õ¿^çE–I¸õÉ*j{΃öÿ½ý{{pìyj$ôdCK¨–7ÿƒˆ—Â\yÒWœÝäJX3ú¨TŽØ?§hˆ3˜eŽOw ºÖ‹Œ¡DѾµ} ݇¦±e3Oâ¹37ql·ËÛáÚnÛð’S;Z›®Ò³WÓˆï¦oœam+Ùë¸ þžÁ–n['×ï€ÎägEŸ½9™S3™Î¡¢n^‡Cª‰¬q´&ÍÊÊb»vLaþ_•©´ DGå@ï¼zà1Þ¸áÍJj;8[©Xí×Fõ´ƒ­¿'÷´²Í®WšTé„)2: ;4uü-{Çý¼ê)Ç&Á»Ë åæ™;¾Ã¹€wpµÈ.;³G»Äé%®š(5é¸JÌYÊ4bFQ• —üÿßþÞùò䉎Rž‚?×ÿÁßoÖa¸L*î«ÙI”^ׂårf5ïNùêC×üœ@·]aFò›á¡º ʸ‚ýÞ£Á¢t?÷BQ ÿ]iF§ËG¸¶°óÐÐZ€¥ýã¹U%^Ü$G>eËó‰VçÃ~ãÙëM²ôF¡+÷Êg€L¾qå®aÜwPs¦~vÍqˆX °§_¤ðìÍÕàsH“jQ£SF™ÑS ®Y äþpÖ?á•€-ÍJÄM×§â„`â£!Cã}>³[!>0ë"CýrôÈô]ò“T•åeß Ç䆫#þ6Ö.Ë.âd7¹£…4›7­’^º*NeT…@oì!vfçzPLŽƒ‹éŽ2ì¨.†ÕQ"ÌQuLΙLwœ}¨W ¢@éyv€ úº‹ãþ-‡GØ(»û|CizdT"í|1‡i7ÄrÏ>Äâ v`¼™³y ;qáþZìuÞrÒZ»è»¯k\¼Fñm]ÎPNw^V§†ªÒ´ØÉžV›Ø¡©o ñÌæãÜ5Ú¬Ô{#$C—°¸‰¹ËšQógþt±=ýÏžd¥&´I¥SŒ^ã†)xÀù–ÜyÊuÕÑ¿ž•‡9©ð%ôYŸuØkS¢¿Á‡ñå4ur ë¼y˜µvÒy%.àµÞ¥h°ôÌ™u;ÛeÙ†¡ÙБŠw<.Òs[dé÷dÖ…'ÉË+žìF²•Ž®TjN _­`!uzÀìwQK¹{ uE‚yi›üƒÿÁs:>eÎÞûžK=-B‡ù"˜ÛH2óÅòT^ågelI§>= «#V±íó­èÖ;ù8Cç¿^í.wª°Û¶cŠ´9K‹¾«Ü ¬q2Õ(ÁI‘ ¸öE%pN—é’’ù¼+ú]/”¼†§üM0Cañˆýgž¿Ö5à#ƦÖ²÷æÍX^pŸìµ—fŽÉO°!S¦×d"3¢@I½‡§Oåö%*2ùGY«| G+sN³šï[3ÔÑÙ0Öæ ѽp˜÷Œ;6׃î…1°.ÃŒ‚OƒñFü¯¯¹áEÙ¦q[?m+t¹GVË?‚¿ ×8ÏÓ¹üe®&ÌÝÆ#Ç5aVö¨¶{I¶´»ãzÉ`~ÉNÝy}¹GH¢}.ZÍqåoÜØÂ™=7£–¢ °¯Ý’0þ(°^®ƒ¯³Fåðf`m[QÔ¤–Œ]¡G ªÿܶû‹¬ÇƒJßWú$É^¸§0šÒÁ:ÜVû d¯°9]|ÈN]\M*C>ò¾j„ÐÊÍdÒëø³kíÿ½k3ŒÑO'˜¹çâ1ãkDï¦ Îz_ÂÊVüæv>“´]ëéyÃÎÝéç5Q“9n˜ÁŸ/¯Eç?´b¡ ’aÚ÷0ù“Xj áÜÔ8Ö# _/}I„Î^úîüLÅ-êßùË«¶Á”IÏÑ£®¤³žôÁðí±s™ç V‰Z¼×EÌÃÙŽ ²ò®=ÌàŒ2Aýίœä?]eƒ»~à×Eêlmé:Kíýÿùø°‹ïíZîòò)Åß®µX´0„‡ŠzôŸãx¬ºóÒûöƒ¶üDÌ]ñÃôMØZéãpÄ·\õ82•\P£%!ñoâLX¥··G§‹Ñ9ð ¤GœÄXSæûÃ6OoÚ¿ŒÂî/‹aûêQœÿÅ']@†åéÌ)wͤØUõœMüf”W–@[E·g;tþÑ`«JºaBHˆškp»ñmß ò“T7~=LøJn¨EE)6ë» }>¿˜ tº†Ò°‘Ӕњq{ )ù'tæôöúQtεp:þ¿þ»w^|ë±m .ñò̆`ª“ n¯]ÁtïJ³—÷ÀXsÊ_ú¿/]Š./»¸û2‘C Ä~q¿fYy ÐãñêÌäéжž…tÞS¢)Ò†£{ö°ñK“¯7ÃÙ–¹†Pà®BuÕgÀªºøiS—qð0Û‹6ïÐi/_À´IGᮨ ˆ—fný¼ÙÚ¯¹3KÝé…·; N&Ÿd¿C­Å¸ÀëxLì™óö*Îx0›Ö4«pSÚ ¸Êm(×rµ+ŽÁÌ™¹uÖxtr þúïÙÁ¶ôUÊ)X*m k”؉™cGöÿcÉln͹§$~ùôÕn$ªI‘¨§J©Ý ë7O‡îú³ÆÝ 詵‘ æt'®ðœkoEÉ=-Ü‘™Ïpž ýi`Àùf–ÒóggÒ½}‡É£ˆÙ4kŸ$œzö$]O£þmjvT€è?›Is† Ð—ùô›ÍM0{Ü¢Zk›±¿æÜk?½ÙÔ³`4Mæ t'9'ÑÅì3J\;åÆaÓ™,š·ç2ªï#0áÁyl»Rƒ‘šfÐÿF’ZôıµñlT‹6û×a †{q Α 㣠=0Ónìz‚~ïs¸«W¼FâŸT¦¡ìÝ>Î3ŠÒÏ{0æŽFÝ<7ÛÓ˜•˜"†ÚÏÕñ:ÙÅjlKÏdövâvÈë"~ø½ iº² S¨´øb80 —ÌãSà\vVªÐÝüz|%tö¥‘s¡s¨šívt}úD«áVöz„âöð0tè~ÜOƒ‹Zaæ}3z´šsv°ËŠ—PôúÖ!þ‡Å#<Â^é;aÌÔH\%(Ä…uñà‡åsîav{ºn!~v+ƒà½Óh§s’¦5—þrÙñ¦4ïoÖKT²è„ì³À'\2óû¿éYo¸¢_:ÑËŸ´yÿĆ}þË « :Àüõi\¥~:=ÁS>“èMëÓ0ú‘Jû >›ÿpã¶}â’Õèê,)r£â<™V-B“‹ GÓfã›ÎH¹…)ß<È…¦ã éÇ£çžl×ùh!ò_¥³øÈ}㩈ÿnê0éü1ð9Д nÆp+´Õ&ÿœ€É'l@«áW¤s ɳ±|«yÜóYwáøÂƒä¶è_âØão=ó oW7IÖôM¾U`±®^úœYÚóõØñg“È©•ëÑvôL,ïJÉQá´Þ' c<7BÈÞ¬k¥>Fâ º¥¬s~áf>Ѻ"Hã/H’ÓóŸp%¯ñ6Ýûw[Ò”ômh¥½‘»qާ+!HõfS9K@i•"«rø…oãé ¶*ÛSXófÛpšcâ™›q\þl3Y'êÄ×ýÄ5ψW%UJNL Oá–»á˜ÌRžtìÏ9@v¢ÇÌ­èÿ= í?ÀÍjeTø¾Íq›D¶¹þ$Z±ª,òú NMÜ“šaÐýìL[,6|ˆ‡lŸ6Ãp"\JÛÉOÕc¾«”ÙÝÚ%#þ¯êÂû1y/>KÅ&…,RdÑ ÿiç:¶”bìè;hýZŒ6í ³&mÇíMÊlËâûœÓ¶ËäY¶ ŒSáAú•Z¾ ÑdjÒO¦ì(Ѿ‡œÇ©<0öúÇœé”ÖÅxË% tc¼T’~ÿ5M¦ó¦Ë°G]Óá˃3Ü͸Ëv¢úÝdˆÔó‡’“:Ô!Ò‚‰¦Ÿ¥•àDˆ:Û÷sL[Õb› ÷î%‚íS]ýækÌ…ø‰ýóð@e ¬Œ†ûYû™é©1tñ¦íœ—U<ßÔÀ•ö¥ÀÁQïáyì1Xš`>Âÿ{¾û“¸€yhùjÜ{›H›ÇðÁGk› 5œ£±-ø%(güÔaËñÞ[!Ë$í+ðÛç}d^òi"; Ï“¦›Çý†m­¤4.ú›¤@i¨ Âé»-ê@ £ kúZÝgB'\ ‘cEhÇvåáÏ–¨úxý¯?yÀã’ÚtbŽÚ×TpbgPàt˜Jì„·ãÅáñ=‹?‘Ú»yùÜTIˆ"º“³Ð¢·írm±ù96óŒþl ¹G¼ÉK˜ûBfݾõfÀ;-áµóáΘ긛ۧ›;‚®¶’Øñm gÿ˜(î…¾«Ÿñ†žß=Âüà¹Ê$ÚKçÿù‡1ãÛ!ùïgþ<&׺£q‚~.ÉlR§…â—q`ú‰çÿUq½ø;®Wn+Ñ=¦!0zl9¿/â^ÀÖøËú¨¡½ÚØÖ¬A—Z<‡f¯„mNå¾>¡²óhâL |ód€ˆþ3 Þý%ìãv+n‡[Ðàb8–ÂmPWcƒ›„é•äjTºaƶoŸÎ %#šˆÐ—fgqÒÜ1ÀuœÃ¬šF¬ñ•¡—C•Ù¹K7I΢_üÞwGà~ U;=’ÿ¦‚MD»¨®7§72ƲéA‹!î±( m¾š¯Ö»íáæö'XÖ½"AìgÄÍ=bêæ±Pá«ËöâîÀëb ÊNä¬ÒB~oY)fTÔ!ç§ ºŸ+Aºà4–œ=j¿¸ªç&™mÂz‚é…ç £§‹¨ÒfþR·ñá΋¼åb=shªU%±Âµ87É|R@5ÙäL‰0§ÙÅSAAE¯õ¸aáQFçµn›( ûÆ.¦¾‚–`çu …+}¨OB}ƒó°eêg2Z+ïñúŽ*ç3(\O^›ýŸŽ8\¦u’WüE„C_ n°l 'å7ŽUÍŸE9ïÛÜ<î‡O:Æ´ÖòdUöòõ+ÿq¦GöᨖÑÐÜm‡ß¯ÎÁÑ-ØÍ5ŽÄ¿ä MÞªzEx:e I³<ÅUø²0¨îG¨ŸÛ‚åj{±å5Ðì ¤9ÒP ä|«0”z‹‘V`û Ì îÆÊ%øtÛ~L©{†>˜5Ïžú›ÔíKëÀÃj v(¾ÿþ.¯Ï+ïã°Ët5Djá^ý=DJo:Ý;…óÞ÷&>r Ÿƒeéµ’C4»(”kß‹‹%|(Øm¥;ðPº•§¦@ül^펑Ö;ÁCÝzDbpùÔ $ǦÓ5},•Ÿ‘ÍÑ$¶?‚=ãá™Ü-Ètš@üþý©ÿoÊIÜ´—Ü‘‹ðÝÓ\Q”æ.Ç…SUŸ=ðòb4ÖúDºÄ¥ÿ·4–ñ§å‘_—௄+Þ¼Œ×\À+±{øºmgü1×Ùšíÿ‚Ý]JV¿®µeL3ž ǃv«;‹HÒ =ÖŽdÒ³ ñØëàÄ]¨t¡×n,fÇŒ).L"i{>s*wTé˄ΨÐÅ5Ç¡TsÕÿÍoõ¾K®›(ѯši´Þ³ÌžB>7Šní6ᙺ¾‡)ß©C¬9óì¹*ÇáQê7j·¦~6³®ð*xúøV>2ç2'ùèß½+ß¡“I°rB oîr bÈöJÁnëèTy‹¦ÎÙ“Ü´”,r|á%_ª†O8.íÎEئmõë]é¬*!ö}ŽýáUQ¯^`T¯"3iÀ:ï*âÄ•û@¶Ú–¯MÓ GQ½u}¼ çñ\u­†‹K:…§Å+0gÏYâóʦ¾]¯‘{¡Ýô¾‰¼"¦·fÀÂgÆPt{šA:ü~.†iš­[|£ ©3ƒÛ§,AèO!>Ö¸Ežì°azõYÈ„/xíðxêØœB2›Ñc‡m?~˜Ü.üßï?¦žÙÍZ‰Î¡0ÌMØ‹ji!`[±ÿ6äaè´$xfTJŽw§rOý  ûQŠkÒ;'Lü>¿”× /çÌÀ«Ln³oò Þ,d;®m.ÚìÔ®®áe<?Áˆí¶.€úÑа)—l®À>,æCàÓv¸;6ÖÆ‰ã9K\i8 rÜ•©õa£Ûg5à†“ÖÜ¡õaÁ·Ûø©!Ó³„hÌõ'`”lÆ Ú¤Á_Ë\š,êÍ•ôÜá=ËÊ„ ŽÓèüË'AVT†nq‡cc+ñëÖ,  ½ U mU=bÿÜ„%Øzñ%_2¾Š‹êüˆ×†¹Gc˜.똠Fo0`bºöTl È-¶a:ÍÓXx³0³²˜…Žm‡pðÜ.¶%Q~Y¨QIËv,]“Rt¨Ø?À»\‚2—¢Ù >+îÇw£?@ÛÏ2PîÛÎ.¯ Bc5ÈÛDªíBáWÍxOÇê»UhÙanð]þ/‹íÚÅ ¯E‚Ùpß<*gùÁ™§9Üyÿ7\Î(ë+OL6ˆRrQmš¾—{¾ŒKW1¢ß;%¡q¼µ|®Æ™¸WsYú¹¨ÎqðùÌar–ÔŒøÿC¬ŠKÌ繃ι_ñùý ò!¶»ßS®´Ÿdátæö³™©ù:nuõ öü¾ Œú(Í^ªšÐ›ß˜ÕæLx“Y„GdÍhÉ£[è=Y‘-{¿ ß: °y ›­õ beEé܇rܹ;¸,.’½1\ÅM_þï¾ ‚ý—gMÁÏ êÊbìT…ÑÁìøø.ˆ<7‘ßí´ º¥ ÚßþÖÉjPZu¿<ÆgýçqEtÚŒr¦ûûòæ%~!{–Ìv¡Î<¬ÁЇ˜Œ¾,\²›vWŽ¡UO¢ñ±©7ý¨‘Â-]®9Rÿ=£Ø4¨ÿÆ…{yã7«}xÓûL3€Ì˜Ç¸Í鹋ËÖ ¤aùµ³°§Boaö›Lñ G{3G‹8\òh7JkŸÐÜ«Þlö›Q”¤ SÍ?‘øu[9[5þÔ{'£È‚O­»Š¦áÁ«’Ð uOÌçy¶Š±ò#˘ªhyñå9îûÚŒO¯'CzA.Ò¥VT2/‚Q—¶'xzÚÊc@Ê$Œ&Lr¡¾6ît«Úr`ãÏ@ŒÏœöd'Ýí‹çJш—y,ãY- ÝŠËôΠJÌeŽœ:ÔGö?î«x¢pYñRJ‡ÕU`Ü;ê± ¯8«Gy6ŸóÿyŠýTAfà^’µbzÊ,hæqt¾„xï`nÈ}né½Fh+M†cFë1 J†öoeör[)·KaÌçÖš¿qôýë ÔÒu+f)ÆÃœÅs°Ð²¬ÞQ‚ÍbxÿìR²ùñí®ð§] _¼œÁöïÙ Wr×a¶YjYïÇò×w‰ÍJ!¤öÙÜü—Ýœð,9:‰7Q׉n;<ã߽ω¿N¡ž V§@ô|oü¯'{ìß<µa9#ü90÷Yxïu±ÿœÈXXWl…O;D)Ï.·¨¨ACE"ÏHàYÒ†¿ اµ„Þ”Éâ ÉÄ ¦S¸™Ñ 4ˆãÍÏÛEÆ‹¬âIž´dª¤èÇ¡ƒÿÍ]&eÑ`¥·ßœ¡7]wÑ熥mƒ{Ü6Ы½ËÐP|8Ö â¨dòb¨õ¤«L§²µj²´•S¢¡·ÁôÁ8±r9-¹1ìÿ;¤¥>“6ò/Á·Û%(5ú2§núÎÕÎaw—\†¼x¶>ú0ÄüHÆÏºËà׳#Ø£p3Ö2Þ:‘ ÜsY‡Š0¡åÙUt[ðØûw,×Á¿3é63?.ö¥&~²`é&;XðŽ|R™³ þ…³² ¹ü¤”vNr[³Ùn/»wcNÌQ °MGÛeÙ8#o5Np¢ùçÇ1*‰4Bd·á‹+óÊã¿Ê®]«ÙxñÕ#ô AÕŸ‹@ï“Ëç…#'ØÅíà²ïhvÌÇ‚­Ù4w¢ï¦m>&KÉÓ‰³Ö°õ'ÑwB‘Ÿ¶Õáw3sz1^›I¹HƒqØ6\õª«‡í—Ô„÷8[&%(XgBWÌk¡÷^›üw;|½œN15©4zÊ£˜$ŒàŸÎÚ¼`¦}ű¼ îçø¥‹(Ì]Jt®ï$k~ïáü®z°ÿŽ5´/‘L±¬oÀ œV£ìKåP[â hî³A˜1|nB÷Q+’¾Ì¡i«½¨b!ŒÙ $Ù§•p¦I1*L gçÞæ.ÛF±xz äþðQðž,æðnÑö0azW=E½[É,Ò]#z&ê~¬ð$ÉcÞâr÷Dßmû8þÝ×̃¢â»ððä*˜YU5§RY{u¾œ¥Nï4Ù²8gCÎqâˆTqaVÊÍØsã9Èõ ³Ç3CÙÒ)ž#þŸe´÷ýÎÀ[ÛðYAÖoãíöHüîÃýàƒøô6ru_*<ÆÌ‡p‰£;©V ¦’>ÜŒh'¦º­ŒØžÑc_öÁéEKñ´>­å´è"ÿø4mnÌoÇŒ5ïÁ¡˜á3Å!NÃ{:Öö‘7 5Ä :L?qêÔ f–¶2ƒSޝÐTv:ÓRÙ ÓiÌÇ©‰1¥w†tzõ-N|Ìœ˜hƒ×Ì—áÜ£€ä rq…`IbW×á‚/èi<ŸŸFš«™Ï \(MæÅiãÒa=×n 8$»j„ÿjwq’ +`‘Ì,8ò})<ïf—egÃÑ*=Æ=úBÜrS]R3‘^½FR¦©³ƒƒù¡«üXá®æðÎPi2~ÊpaHu8QR×*Ä­ûEÍóXÁ’Ó˜C8¨œz‰»y{!n|¤ÑÄÙKµ¿Xx£š;(? Ý帢6#vno#ª¼ ýÞ¥ìWÆTÞ9Æ~Ýù—xVEݯ‡áúÄ)$È^úD†’:-Üý@˜Jò‚OMÙsça{›çBй°µñ%ÞìÚ k&ç)¢@.¬R¤knýÁ[51àE:,m,‰ÿn›sœã›^x­˜ƒsJôàu„[ÿq÷Æáñ‚ü•O…pÍÎb\7c´ŸE'*‚®Y¿!#aU†Ky§…ãBàl 'š$*²77àÕ.t[ë¿¶ÏFï7ª4¿;ŽæIÎ@ïÚ@ê(t޻߂ÒõR0í­¬ÿ¨É>˜+³&ÃmTVA–­Nú ~cg`í-úqë8;¹ÿæEóÞ]¤"'Éå3±4æÇÿ·ßq;p¼]~zxìœÉüÔBnbõ7‚~] iÈñF…±ÉèÔµT'Åa‚?Yë fÙ#õòíkdÙƒ=p6Àâ¿¿Å.Rߊ3‡ñx¥Dâž478?f66ËÎ4Êñi'êgWq¯÷åÀ®vg̳ ‚—󽜚qŸQ=þ)3¤ó4ÆÐ í:t“È8î’ §öÄÏÄy@j”·}Å8Ú]¾ÜÕÖü Rzh·ÍÌ›Æí—lákÕ`9Ú—ÍÙå OîqÁën˜ €ê<òŽþuC´Ï‚øèkXZ—γ‘R¢Þ—ìÀfÕ2â œK³‹¡Rx ¶ßDÓ:T¨µ*%èGYmvÄÌŽ.+È*o<Æ»zæ#þ¿óx1Ý>æ^ŽÂ1£¡;è6F{ïBÿYÃq”­‡«lÆ3åACîFßqÄsÇèCœºs?ÉU}ƒ•ò§¸÷Ç·àoQªóY˜môÔc{ ³Ø¥4©\œÞN´`WÞ‡±Ë%ÀónˆRîáþB0_ÉÍãŸãÏ»Âý] ŽyogÛ_`l˜5»ôÍŠ­…h”Ì€(C|4Þ’µIõÁé¶íôp]-+bOÏõ‚•£ÕˆÎ` OßA9{oœ;n&«ß4™ån¥ZÇ!Ï,’69ä“5¨FG[8ânaŸû®9Örï®þOÿœú¦‡cJö“k{zˆ´ *qF%]~²n©^t!sú6Ç4=àD*@“?üïÛh>ǃâú…ç!/Ë„zx,äj^ Ð kïs‰&ºxZñ·´d"¬x¢ˆ#È-—ºï0ß½v4 öCèoa¶êÈCÎõf ye€ÎØ1¬©ÚŸ¤…Òÿf8!)(”4Ö,ØÇ Ž;ÍÞ¨MŠ_ÊѸg;±Ð\„½ùs†¼<›BÐø[’H’®)ïNÀïŠ+±iawNg?òN†öü;hØi‰jëÍÙÍOá~è"xìi>‚)é8ͤ¿ %ÂéѰ#ô5¬´T µS*à•íI˜o¦Àœ–í¦E¥=¤cl"ÓxnBçÅŠ°öí6ô¨¤ ü²Ì¡¾Pv¦%å³7—eè½ÔN½"4Ùžtþí ;q¼Ô7R̽%ÛÖTp{ç)Q_pb»…@å¶ qR¥U“Xñ¦UÔmÜ?<~ ñÎåÑeóâYkl_ëÝBBóÎf°ã—«0EÕ˜aw+ܸ§Nÿ ÑæYìo¸½JñƒôÙ>GšP=fo.dŸ+¥hUÖZêºá+¾7£YjIàYS¶xÁΑüŸ`×ÉíZ„·œ…ñÒ²Lîrß ¿#HãŽy¡×DsHš•€ý&ãØ¥²ë$êz:̾áÌŽ­¨ã\Âôpºàüþcè†ÙÒQçPBLh°÷?]ý:öÂÊ·5ÄF'+?€Û7}ØÆ»˜ F‹w$æ×}áñø“°ì"Á¡=(°Žh¥b͹=­o;Ì}Ÿcœúqt°•³]Øn‹ã˜ß®ŠšE± È·c¼-ÆésëíDðî¨×8Ë‹YdÃD\åÀÛäç0 x;áüc®v”iúCéisôØ7Òÿoë{Æ=E2XùDñßüxôÈ8@ºñÎðíâÕ.£3½Š ýüGW®óáÞV\ÓPSt–b¹žÔ é.$Å<%ÿAPdzÙo lam[ á<Tqs¦³™5 ;lD2A ï÷.®&»Æþ9‹®‘ EÿÀ ô‰Ë„`—ào*ÅÉm‚U_Jñ£µ/]À„õ§ÝÃíµµà­TRb™àOa*_¿ ÆÛHÑð™Y\ñÅr4:* ¾/ISüDzAÿVÙOe5çãaÙ¤qÃBüpi24”ç7ÂúÍIMÅæµ?Pô”0ÞhÚŒåã7dhHry™sí.n£½^t|ƒû›ÊÁ†6<õá›n”€¯Ûëáiœ.d¨%câW7q=§æ ß5ŽÃÕÏj(wm/~0ºD\jÚðû_y2õsÁ¾õ¬a…‹8ãŒ+;üùEóæ±Ù›ÏßU'"ÓYϘ+dÑ‚zøX0‘M>ò‚üÕ_‡÷â6ß@i¯AsÝzÁ®ì$X… w”À38t›XbóK–'Ô‰c.E§uÅðfÊmNö` ö®‚«^'\½Ž”‚‘Õ`­B#ùo–$E—Ç¡Á43(°õ ‹GåÐõ‹ÝùA­{áÓy;l4: ÁÎp¯*ŠŠ_Þ ¯¾ƒqá—8Õ.¾d$ÓL—ÕìÞ=ö¯Í—]³ÞKmœšZe2Ø{“Ô-l¨o*nõ²ÀÚ–vé’»¹a9TïjD—ÛÁ¼¨†XÜù Ô¿ï§ú%qòoÖñ7ï~/‡ÎE(¦<†­u®Á‚½û™êVAö ]·†Wp+/´l…«mÙ–¸,­eO'üB¯ÝÑ`”—VL ü×ëaîÔ8Ê›«Nëî @þ8ríÁÞ‘üo4wG›—Yp•ÿÞ]"îK{APçm»q‰ê `£Ÿœ{Ñ,Jõ—{±—¹ötcÊZ ócèþÈ”µä¹SÁäCÛ<ÿ{˜×í™È…ô39x“åԘзt:ÿŠm8ñ^µ Õ)¿=P(G)ݬÉTÞoàü^Ng4 j@•í[g Y_b`ùBz/í2‰žJ–±ôÊXºvý+nqÞ]þöÁ2¾ö‡ZÌ8fCÙ̹D`üu´[˜Mŧ±D…zfjíú8½!d9ýqXŸMJd…)¯àåè¹´Q&s/ÅŽÔ?¯ªsœÍm3ˆÚYË;9 yÞÌk‚ uK:ÌmžîDÍçáT¹<8ñ®‹rV¸¨×‡ƒâþ²ÚŽk>ŠÓ–WGpÈâ¸iÄщ¬G²OeUk }T„áG²™³óAàúÏ'A€æ·õØ;¬ó&_Ö¥z·´©˜€,Þ±_=ž†éö´Õ( 2J¯’˜ñhW½ÛbŒÑ9}/ûcäß3Dè¼ÞxÆÔà¢ôEx^3‹…ž Æ/Š5Fø÷láŸP¦Ó§ÇÓ— RúØëÒŠæXê›&y"½˜®>ƒ~W_;bÿ È_nÇ a|QÙI.ªÝ"YÃú°3rÍt~‡§Ïá²_©´ÿÒ\üŽÌº?¯ÁµèÏsØ/Œ–5±ý¬=¨ÉØ1áÇ©´‰Ø“wq‡ÈµõÎ5JoÏÞ®Ið_òáýõêå —…'½à \·{ér`p2¼ý- Ûß‹1“u~Ìæ¯dOÜ‹'†»?s…»P»îƺ8²œå°øN¥ǯ}iäŠp2”Ÿ  mk^¢e­;´7k0Ãk ì8×»ý“‡ñ܆ÕûÛŽà߯èŸÜ`ˆ1p§µHÎçöøÑî‹V‡K˜Ó ÷]–²5zOÈ‹u¸à6ØMÎñ­ÇAýÀB4½2‚̧›×¦cÂú\64«˜õŽ ƒë Ûþ+™íO\ ´ôYë;_¼t7öàW´Œ‘'ågù\pO w¬×ž9®ª€gõ%¸1ç<ßlIòÎ=‡)Ny(ýê%Ìq‡™ÓçcàÄ¿­ÒõGØÆ‚{Ü›=ë0ñß<¸û± .äX’Oï9N¤ÛöÁÛIÒltñhîÎëóðHLŒwxS OõªÛ0’ Ò¥7ײéár­«ßÂ熵€ hø_zmõê÷ø ~¹¹Š<¨Ô"ê6×< óoáŽÈZè}­çhRNo ÚNËæ[åhÓ»í›ÉôðÝ8i›9ëňX* [Š'`¸wüa¿b61ÉÓ³×:°Ò%uÀk§ Òqs’:v«Ò¿ÖÞØ½ÌìN!ß ÔGü¿v°¼Ž½ÊŸ•À:ätèFÕ=tMíMÞÛZ-pŽN Û×B1¬Äÿf7?~ŸÅô~L¢öº|û„í´Nó,NJfSŠ9ŒN'îÂÈÇ,Az¯ü Vß”bkl øùVêŒô×c»Ïÿ%¼š˜É¤fþÃñêw‘sú–Cßß=…­ä ®X IŽBb·ûd€ÙG™±ƒÕ³qéchÌQjk<Ÿ… ]vc!½×¦â¦¨«(£¹ 1,Åû&>z¶›³`ôsÃx:zÅÒóü w¬¶ãSžƒ¡bÓ¬°ÁÿæÜspdè\LeE-·0ü3¡ãŽˆ°R±d˜så®PM M"èÐ 4fJÑ…‹aM”6=  %Gé­×W0 CœvøÜêÞT¨1P#¾çßB±D;ͦƒp®Ñ‚÷"W”Æ…KÐûc¡½D‰¹Ë{QmB—ÌÍîæk1—§÷f °¼‹ØÕÛ-`¬lJ7îÂ×›Žb€Î8H¿¡ÎVÒe 3ú'\T1 —¹ÑÛÓö»êrðüÊ&üjš—C%˜üW1úi@…>P’Ã{‚À|érÒ;ŸÆ™ªÐ»¯5ð¤!Â庭Ô+W{„ÿÖ'ÛcÎ#P 3f‰±ëx~LŸ§ {´Ê_`DY"Û{æ)*„Z¦<OÅ´ TCu†ÂÔý|öŸþÈw›-Íø»£Ð¾ò6>mÑB·ì°z#@‚šbñ‰JÜ¥F/$vríšS©wª0íÏË„ùñ_¸¯X;g9~‘ÊMÇñje™ÙdŽæ sÀøJ*Y© l£Ü ¼}¨Iù²0Iäý¨¸ô´¼5Âày|×–_ïQå‰[sr>ïapb-Ná+,Ú}ÞhÉ3¨cK àô#[6$,Ê~&ŒüÿwfFçó¢KY! uÇÀ{ó t¥Ì4jëÌ@W=šfíZJó‹•©IŒ'½n-Òòcš3®·¸Î}27û¿¾ç¡'ô¾7^9âE_x%°?£[ìš@„¾GA…‹8ü~>ЬKT•šZŽyÆ pªïþhEŸ©WCÉësd„=õ»ß Ç;޳œï3áÍ!mæd‚>>QÌkeSO~—˜àãKÞ›Dx—6Ô’þyçÀ"ç/H^y†Wäaê–“dåâih¾ê#ç?qëŠÞBvt¤C7½ ¿zì0;1£D `üþ“pü« ÛY/Éœ†fàs¾<òî#nó7?©—¨.‘§O¥ß¢ñÐ-nå`"oJÈL¶ØÜŠ¥…¯‚ö^, D gýd¸9zÏÅĵWÈ=£rnWc×/¿nÜçö¥t³¤ÆvaÆ=u¶Éà<ª|úâ¦éD9Æ:OÙÀñŒSgñRbŸ¡ôFMX™ómƒ•é›q·öðÛÑ1ÂXò!"7^ ¶Åõ¢ŒøLf7œ{Ž‚‚ܦüDúµ3‰p5©n[;É~hM½ 3¸ûÏ“¹›ÜA³.‡›áA'Ý­à»ÊÉ`¬ç°>ËFÝaþ>÷9µ0Qø!RÖZ¿) ¶û‘¹·P(ªWq>Vy ~_óÕ‚yJ!—ñoO-Zë Á—";në“r<^ªF Á›Å¸ãï¸ÿ> Ï.ûL\j |JäâA8¹2œ¶aÕ_3\>0 Š·š`AtJvnMï?E§mÄ®ï81ÃͰçÀ9î…æ)29 ÔýPâ©,©«´ö£hïLÞ£Í/ñø¿ï®_Ê®Qvµëµ  t£û}ÔùÒŠ*¥Ó°Ãî7DvØ/¿ÁR(³[VâÑ e<˜N·ôòkûàå@r&Uø¶g= ¨†÷Rk¸ÒOi¸‰0+šµß¦ró3°bZ//´r:TÉ%£Nš0e*ŽF8ž@cƒ.l~7(‰È`朅œgÅqV£úŽ‘‹u;™÷ ywU±lŠ"þãÂÈŸVô¼‘øþÊ#îJy(Rû ºÞOÁÅs¾‘ÜE¹˜è{ð:6ŒÎßOWd¯Â|ºŒå/‰ƒÚ±eDòÏSÉtgB+wZ_€Žy¹m º±Êaâŵ†vb`–ôzvîóÓqh‚û¾k#ùÿ[ê/s“'¬5 AÍêØõÉà¿ùÎàžð>·pÖïÛø¿òñÇ…Y\v¦«9]„úUtuÇcЛg£ß^Ä·åÏ9éùãÙ;Smvl8žƒ%ÊqÓ·E8yj¹*/Ä_cÖ‰ó¸_ȈU[¿Àä÷[°2¡‰G߆YR2¼å—Ò0øµ¶ŸTa©¾£QÕê<¬\šŒçÖ"/´/o =çOZ¢ÑžÇdHµoôÀm/]¦üÞŠç6¨EÇoÃe/T™óÚ½Xô芒U¸wê6þ‘ªDæ­(Q’îâ¾úLÀ‡M –,ÅûãñÒFi þÚ .´–—_joø?³¹M²MdVígœh‹!ÇFø€ï.à’!ÿÝ[Yömú} èäÇ=šÙDZ·b.³éQ¦ç<YÂl“ë=D’TëPjùNx•ïÍE&¸8‰úG¸N9ö¢ú)îtâ½M? “bò`ÿë/Ðj™Œ“{âðwu(k©¶ce»ÅáŸÄ0w>PÉ¥ý‚Wwôéíg–d²˜> ­ýŠòåŽìÞºí0¤ñ~HÄû·ÅñUÆd®ê§<ó½¶Ë4FÑ; rì­ê fÑŸOª®iƒŠ"ÌÔx mâXlû.ry²3Ûi1ÃÕÀp=mü}·ëu“ Ÿ@pI4šÙÃÇ%Î4Ód¼Ëwá=».Hk×UáÚg>§ÄâU(8Çè¿‹…«ñ’–ZYjR‹ú`"ž6 Ë´PÆ· 6Ì IiŸ9I³$ürî3j†^ÂÓ]çþëgæ¯:ޱ:øã÷ Bè¢ ®[†¤Øè±JÈ&Å|¬ï_·VªaLÇsOK—÷OCn¯ Šf”“ é r)…#3M85ó+°ãŸ “¿šÀ6Î_›†9P^µ,l×¼ƒ»ã·Sß¼ø%…šÄf¯+üVb{ÿM`ï³qO›?®½þ4‚ ųHôQªT÷ [~XCìžÌV‹É¦¤,bZÆ‹0‡kMø÷Û&X³À×µFŒàßD¾9òï¿výçV ê³T˜Þ¿¿ðq[ Yüp,†yÍ¡÷í ¡û^,^ ‡â‡òt‘ât豞 k½©ÂÜÎüÂ,Å‹¬Ïn4›é&Á&{ýMÆ0vÆž•±™è; Fàá«z”ï ö3Í9aþ}ž[÷mT¨M~Ip"b¹’zSº2ÙwèeéÓ; —¶„AaøZpü}ÝûÇ‹‘ͪPô±¶åw.Äš»E`óÀ“,¹)VáÛ—·aצYìjB0œÔôåJšÿ@ÅêD(˜6ÌW«…©çÉ>¨t•……§“ ª£Fð­Ê†˜‹SÉ#’‚kAñÉëØûH‹­âf0‘÷_ˆáûDÔ»“·°C’wÂ+ ûÄê‰C`7vLÈ#‹?é£{¼:jb«Sö²Ðü“0Vj†]ϧFn#ß—ãT3m6öó5¢)n„Û}ÞÈXeŸpßw'ÒáuùÛâ¸H?ìSe¿Æ¡C¾ÊÏv§-äºE7òýÆuK¼YI`\y’u‚gyÃ׆ýåšÜÅ  µ¬ Ek Ø9Wy–uu[Ï…ööløqê)+ð€7>â!TÎd/ÇÐÚ¯ËIÁœ4L>ø¿çVj(ÞêüR1q(žlÖRYÐ÷%›ÔŒ³CóÀ1˜{ê(,L(—œ$Îu¿%–~„»n܃ŸêlÚ¬S`tö&ÿýü[°o³]Ðñ‡‹Ûs”øÌ܈†Juÿ7ó¹âvÞŸNÄ`2F?á“ÛYϸúLÖWžK.kD+Ÿó6…Ã[}‰Pýõ±òÕÆŸÖ`画ܿ™ùØó»ù2 q®~N.œ‹¼µU¨s1 ¶÷…âôðÓQøÜÿ;™R¤ ÷b ¡ðž ¹ÿO~T¯@*H©r³D³!œY{ öuKÓD%q¶‡hÄ? MGË(ZÄÈʦ8û|Ùi#ËN'ÜDÃB]öÓý1j§\wušBáÏ¡ÃXFÅéÙ¿j­Íyú4iü Ôõº€äÚà‚ïhX¨H[2žMzÂéÜÑgÒ¯:ñé4QúQƒ°ÏÓóÁS°nÇ1Ó) ÚÔýg"þìvw]"kŒ^mÓX^míz¤Ì:î±–¯h’|7s¢-ÅÓDEÓ¤sœ¥ƒ'j>û ñI'nKù3ªàf»5µü ûûz‰ß¼í¸ýÀ,lHãpêzŒž8“'Ä¿Œš„ÎRuiJäî—ª3§ýÒôÔ¦s¼Æ Û¸©«‡1¹½Ì}m^yDØu3+(h%:ÉQeCp«È‚5pXjîöÀÈ;sÙ@ÆGŒëŸOŬî¡]Z8ûºµŽ?h}çiÂêÊ/ð#ŸàQõ]˜¥!Ï–­ fþûKɱÃéhöµ“Û]õ­¡=£ÊÏ«)Û®“ëóC©ÁŽß¸æ©:ûe9NUØ Ñ[×âÅû׉~³{\ŸF»¾‰2¾‚»úrŒÝªKWïß ÇèTÚwR–ºiÙ¡Îs:öÅnpÝ·Oê°S¾ìFñô‘ú?¤îHr|ç¸X)Vód4ý›*‰cœñ>ËâDæÇ“½á²âÜjÓ¬4ˆ{›G ¢ç”mlçñ4øÙ¯"Ãã°b˜··ü1Á¢¬A¶Kú~ÈhC$?¦:0 y’ÿõ<Ó¨‡6\føv’ÔºôÄ Kv?¼oõÂëßl¡ñ½:D)¹ÑÚ?»€¿ù/üæØa›”¨ƒÐ&²rªéSQ!/7ÚÑÓ‡)Y›X‰iްtX÷§8£WÏD׌ È?ˆ“?IÓÊòÌ3ÂŒ K4ÃC Öe,ÅÚD_ðecCiÜeð|„|;k¦ÔÜ æ¾||n¹¿w¢½Fa{/ƒyÆHüóæÞâ.ÎÞˆýoúÿë%•ÃÁx§ó|“ž]$ouW“Wf,ù~$rd[…¬˜uçQêa° ¶ÔÌe£<Ï :ÅAYïeœZ2“%¯Œ¤iJܱA\â¤Ä2Îðz÷ˆ²èçÆôòÌ/(Rs‰¼>\ŸʰëWíḽûñ² ÒòxÿÍn}s+•¤,Тó´ƒpìTWŒRGÓþ~ƫ׺Q­µš¬x猭sÀ|( ç}¢þNgiQ„~j¯‡Ïšuð<§k˶ъoÙ¼®Ù»ioc Ùå.ǸD.2g ˜Ž>‚Ö#ñ/[¿Ë÷aá®ÃôJB›ÿíÕ‹,¦Ž»ôéÕbæÍ®ÝêƒÓëgæåïAöÀ%,Ÿ6||‹»,&>KÃôF±™›ØÏApÒ¬³÷Å/Kf0É)?°M¶çûýÃC³Kp²§I)¨Ä×£yè¼ÒŒ)n8„d÷ w$çìŞô|‹?Ûk³ÅŸGÐ=›'Ò¼‰ûhöÎDéù/·2·7²¿Xæï±Ží =.É#,=TˆýèžGí¥vQåû'ðï"Ð7ua¾{ãÔ¤L¦³¿‘Y­.ƒCÖë¨è©¦wBz$þ?^q„÷îGq¯°(Ë9)‡·6ø¶$¦–¬õuNÈxO¿òñΩ~ÔñìùÖ­$ñ–;ñ|·Kcú;Pdc±¼É}¢ðÏ’¹ür*3ô c;ðG˜9œ¼[¦’7‰hI<Ì›”F„Å;Xƒi'㌮p>YR,ðêoîÒ£hì:'ºqÂ}_àDÁ§[¸©ª®kýár<]°cAž®»]Ëd¡ryyAm]¹Ø~Þ§Ù[0di2±uŽÃ’6_îˆÐ{šJü¦H°¾µpç3IvàV:gq]Ÿ_ÿßü£ÍëÃ@0+–„^Ó¤g›]hЊFrÒ6_ ÁfΉ'Õ3És”w-œÂ3µžR90ðI•oft œl§ƒu¨u´ªÇù^»0èD6ÌùÔ†G®›rŽ-×0VBÖLz òŽ âU9'?rѹúÕQŽŸæHÑ•««!àWuó›7¸Þ]“ NÈ玟ãÀÎõ"Ôïuwûú8Zì…Mò5ÜÿFWÛ9ã9Ç*+¾»ÜÞŠla ÿTiN¼&í> HW‰ì%ÞZó É7‰ÌÃ?f‘U" èrøY´c"Ù…Q²[’ÍÙÞ;²ÿs˜_ÃMzE8ñ_)X¿X‡œ[" ¨ÇENvå~ª=sýD"´’VŽþSbiä{wúW7 ¤£©G·F4ɳq‘·a¢º «™Í–«¡ý1ú0æq] õ°ÕÇXó}­Úढ4³˜7¤o’«yÂÌìy†R gáà–N®Ô J6ãÑ9ÐÚâÉní×eMoÁúèI`·a5sëu…5ù}(•rÊtpƒÇ6¦R<çtÜp­êS`kòóð±[§Š;ŒGq× mÙ?Mu˜ÖfÌ øG©ç{V.”6ÿwÖus?‡®qßÞx =)A'7’Óoor»·åñ§Î˜Àd]¦ã*ç@z|H ç—>C.LÖã/÷ä‹Gá,ý]a7P¤lØÎ” *¿.p± ¦áë'q¯¨LP l¥Ö5Œónƒ+¶€ÆÃ$î…ÏWNz±1–7 ¥ËÇ–Cî”ýà^È=îÅ ¥Vâ$!@3DaïV/az ùº²˜Û&ßÕeå'U‹WAŸ3 %ðX~Wí¤M§W â µ$¢µZ„Šæ…ñk*`tá|ÜÇ©˜5¡Ñ²A¢îÒ=œu¹z W„èPï]àý«˜sTf;³.òêLZ¹k‹¾ò½¥œ¹–Ìëܹ¢BÓæõ„Q[Ô Oa ÷öCrc'¤Çï·ƓÀ¯À?üºÃÏ/߈½Ý"„}U—’E#øÿàÑv{O<Àsz‹©óú¶èEÀˆþ°ÛŠåëàY¶»¿P ¼DœqÌF=t›\ UHÑÆ;œ¤Ç ³¼ÿŒ‰Ð¤-ѬêY<ºÝ41 }¢EXôÛ\Ž^ÚiD€o§(X¦|à¢Ê6Z£Œï½Á4Ñb|Ëû‹Ñ,Ê:Ý|o#îx“E–,9|µíØ'XÍË}¾ƒ+Þrð%ŒÈé$úÙo@æýh¶|C5Îu¯€fÃÎu·Ë±ç—†ëêK\Cªñå™. q‚¡*ó<´GÜWÇsOÖÚÒ]ƒ<öÇpìÑp}ãB[Böãë¿@¸4iÄÿÓ~×@ÛØƒÜG~0&µ]ÂÉ 1tu=þœ%ÏŦ°×Ÿ&cµÔ5Øe$ÄžŠWÑ$oE6ü$ eÒhþ"cVç¯ÊÌ9‚›£iêO¼PööØ¿ FÚx¾³üÂÏ௽xkÓN6Uÿ Ú©Ž£Z¡9dÌÎzÖ[áÊfxeÑOõ±ä~*´š+âØ5,°Ò)yîdÑŸ“±Ä_ ¬4p[3ÿ © ™ÎÞ^gçdJ¨ÍÅÓ¸vô8éªíöÚã7,z¤ÏlôbØÛ@öNl#Ûœ~‚íw¢ÄŽ1‹ï£ÜŽ_„ më¸0»·XŽ:#õ¿;TlX/Gsÿõ+«‹ïeŸß Ó*nìzÂ7øëN Žžv·RÎlu)®ö ä‚íÒᑤ7õ¿nÁ¢áç¥Ð\]‘Ýú–Eýö+@Ç­­¸°ªèophÑxÚyM ¤°Ä )(á(3¶©ãW·0óÅAê<–™‰gÀ¸‡qìoa&îl)äDÏ”p‰™Àt®¹ãcE?ö߬gò%sJ™ÖtU–ºR™­vÄÔ)é,¬9•¦_ס¾wÙËÿÐ¥g%—à‹üÔoËhàr²ŸÄáÍI89¨•±ücȳËá‹m›?‚ÿK]—q³¿h‚°ÌinðaoîÛ;·‰•ØiÒm¯2À ÎŒR›Ðíê#ÞèVQqòßÅ6ƒ~<ñ›Øî:™8¬¯GÃ]¿38õçZüi挻&½ÄÌ25êdp¾êЙO þòœ¹ä³³ä—ÇX²oëLÙ­'ûÉ#wsˆøÞǽŸVƒ³é÷ýÈ`áÔ§ÝÑÛzlüõ Ú¨RŒÏºÂaþ{+æüf­Çé§Åiê§hªõ7“˜þ¹ Fwðâ’Cp¢ó lú–ÈÍ«|H¤·á½ëéð¤5ˆèÿ¸ú;ÇÒ=[ßá¶ý;Fž\7ÿ+òïæi;,g%¡y'þêà ÞÎ`Ü4¼Xˆ&þ¹ŠùúŸÞsbuÖãºä :þ;{ýâañL†“Æ_€ÈŒ'P1E·LÚ@Ž¿@'ŸxجX“†äXß`<ªÝ¹E&Þ3¦V1c™òª"ø,îÚ“rÃ_¼Æßà¼C!ìqŒì“§RûÉ•—Çñ÷)˜×†ã¥T÷¸!ŽÐrí;‰Ô z%?É÷ܽ˜Y&+%À-á,×gÉîNÓawB‚Ìi4©Æ%Q7ɎǺLíìz{öÚ6ÅûxâÚÿKl7ÀÔ²¤þÀ{Àte÷nsa(„ö¹Ó»÷°·jà÷ÃãÄq÷;*ýÇŸžM ¡×V| ¢gãÔ{}LóŽ'ªvƲ9{µaê¨8¢ÃΈ¼W)TÖÞ…ŽcÍ |ÜhÝCFÇObëTèÏœ~„ûYJ|LÃ×G„IÏÔ(©­ÎÔUÒÐëIÌYÌ€¥œNÇç^ÓQø-Ýš“ζÅI2é™ñ”•<÷±[™Òµª,¨y&=ʤ½¿€G"™è)Kº}ˆ^•S< +\.ãÃf 7y£'ð`cón÷úßüË8}(¬u†9w“!ØJ†~8¾“ΔפÇ6ì†-Ù¿ñE{køùÅ9°œÄ–UÂõ*§š@1‡ocÙâ±¶=eiTüµ ûª/@÷-¥ªÅ;™ÔÅñØ`»&?rÂ¥ág`ÆýFºZC޾Ÿ8rT>s/•,é¡ÅFP´y2£JŠ,økJ™USEËX–|µ'°Œ½lt²Ñ›<–=¼¨§ÞåAá€, 4êÇ%ZlßOú,nˆ¿ŽÃÓ;oð†^,j`è,Â4¯ysŸºÂYèê èí #}‚\[¾3~3ýßïŸw¼“¦ŸÏæ2ò4ˆÉ\wNÁ =S™F¬.©2E2¬ƒçê£Ýfs¶o…V£7¸zgCθNuJ׃¿ˆ_`÷Û£^‰íb†7ñÁ§8¸(çÎÞ,ÑÅv™¨c¹W Œ¥Óßã´ƒZ Lx—6 Kå6ðBå©”˜(ÞÇLëÛ@h`ÆÕuµVòJÌØáX{vÔbóp–¥ÚeZ°2î3Ľ0¦™¬»@ŠUùaÔ‰V47îG}kUªôC .FÙñ‚3Û±@A§¹ Ò n¨žŸñç:zŸC½Óð"áìÈüÇ_ëÚ‰›½,v½û²a§ñÐÓœ¼2ž®:A¥Æ$N &,fÓ†sjÖú¨\u ñè}ËO8¿k4[FƒM¯Q£*+DÈÅoMÜ]€Ú‘ÞôIÀA2©ë >‘ÝÅ4“GãlzŸ·J÷ ZÈHÑÃØœ) *ËÑàs¾ôoÆÏg±¥‰ÀÏ£dúçϤ½t%qòÕ¥…糇“þ©{ÄŸÿÒÏ„Paftê!Ooü:²õhlólÅÙ¯éÙP"¿~:VÛäQ‰5£±^§û¿ Aç‚V¹ÏîÞ1žµ¿GÓ³ø#õ_xûN¼\mKÆ·ºþÌ¢çWïån5„ÑXÝN\^ KJ6°£ïñãø$¸u‰Q7fë RU›ñ| uY¹Çø-_y‚ÒL«RŠFL½„e…õXÓÚ‚·Çƒ¸n·ŸÚ Å2#ºé¥JÓnÎçG*Þ%å£cq¡¯.)“iõ«êÀ°É"tOÈ" wTÆKÙ¿c÷3büöéÃü)oÀñF,áÜן@=±¬ßEÿE=D“Mx<‡-_~ †ï‡—°¥Z»>BÄœG˜yå07o£"Sz‘–1×PwÙÃûýtAÇJ3¥ ÙW™4¬aNýd_¡‚ÛAn‚û{®ÂÚ€­j˜‡»c®Cê¢xØi¬L¤rÉ¿™•¬~Z*9~ïì:© :YªtÔà,æ©AJÖîæ½:pŒ}# žÆL`½Ã1`±²²„' ×„Z®aÎMNýŠuqÁ-z:íÛK¿Çݧ ¹‰v[©“qÊXjp‚°þSoWÓ÷ý7Ïs¢Iógç^Ïõ\ç¬»ŽŽ† Å?.`×<ÿk”öpžöË÷ŒF¡Áøý5ÿH¶mí¥— óW"~o‚‰Æk!ów2è„ýä¥FÈaºÌ/,ÒAr£Ð„j®·BÿëÿU½e€?Kň§ô£:¤n»HK'³í•?¹ ÂA`>·kRˆÙùÞ´ÒÑýO[Òå“ë¼¶Z`,f§È2Å¿YXóû)x®8Às›ðÇœ68ñËú²~Ã¥Sáléämt`Ó2x•‘DÓç5âß-0õ`ñ´6…¼µªôM¼«™¢À&äøÑ÷¼éOO7&0ÚκcNþLgüÐKdœâ\~‚Ón¿O'­Ê¥ïƲ¨½䦼í⹈@,µ• æ=Ä™çŽÓyUX›˜ÏfY7‘±¦¤â§[{"nÖè¿oú.Äð’ f²b65 à\d ¢Y¦¬Þê5º7yƒ´Œ0’)sèŒá\RUT†-Ò]Æ<ŸKÐã¦z0û…&&¥.`BtEzƵ{3¡qS˜ÐÞõt ,nm“b²·BéNK–<»ˆ»/·Œ«ú3öÆc=„£)ÓG¿iKÙŽðt\}G 'O`¦‡Xүì"ÖKÙ+Ç5&ÒŠf¬f÷lÚ6ÀíV.Åo7@÷»%÷[ÔVÞ”qs d/Ž€Ó§$ÔœdÉ¿/Ã[ݦ¶ä÷w¾m]¡J?^·y‘tð,Ã×»æŽà¿æÑU²y¦~ðPÀÕSžÃ⬓õ £ÉŠÆæô´ßS\³o4û-ú âîò«òbñÅç·j« OÆ-Â=Éݨºù4Ƽ³„E«L™`Â!´5Pƒ4åxN±å¦X«Ð¬f!f'î}Ÿ?ãt¡8"Ñï΂•<\[·{ÂɥѮÜè[Uú2Äœÿ!â1¹¹B§d÷âó¤A‚Ç^çŒñ¬º0»(Ês˜SáÑ)°^_… V<‚´©&`> ÷wyˆjý"{Ý7C®ö4VßULvÏZOî7ÀAîíìç°Ó›á«'©´tô‘‘üÿ;o7nºˆ2Žû˜êiôˆ>ŽÅá©4õ Foyœ÷‚ìÁ±4‰'Y¸£½yk“~–€YW*áÔi/„‹]üé6]húC˜Hùž!ËMbUCsº‚2›<0žÙ€j*›aú˜èËW¥¾›§ÓÝ7çàY~Ð31““[¤ [d 9m7¼©iÀ@-q7ÏÏýfãÿjP£”®cþEÒ#š!Ø ÎsÞ°†¶­KcV§òÎ7'¢àUÛa_•·Húº2­MŽ=Z±ü Æ1š7ÒÔß‘¸á9¿ c/v,Ñ¿'ZÍ@k@¸àRL÷^Ê Ÿ¸;˜›¸s3†sIý±¨pw"ü5ŽD…Oé¸*eXò)-’¶ÀÐäÔº¢Ä8¸y|1K¿w “—pF;"ñZý'péÓfõa‹Ñé³ûü`>nXr¾o3‡BÓ68TP@œpȬڃ‡cTXcòsÞ›:ñw!þ}‰ã<îÀ’úE0°±sÜðt¢«H1wde(t~e³?ɲì"€+Ò¬e£)HHÀÖ'ƒœWÕzžÓ˜jlXჅ„¶¾W‚ç¿HÔ¿´–%Cé‰pÚ¡ zÊ^dmiëÈúç6hÂ^ŸK\úÅpg¶{jCu?µr%× ±e¯,5|qܯ¦gŸ_} 'pqÏlìL‹…A‡ƒ0ËwNÉ4c¡­W ºû4¬Í»E¼¸P™F˜TšóÓ t³TcâSx›ŽÎbI”Í?‡ ?éÒÑïä™eù8Ú× ý$Ot†±r[Á?5ø_ðÌéÂÒ#pmÛiˆ1s…ª_Çê_LõÓG/ðæìjÁß©ï9wç™"Ã(²D}à¿Ò¥š_ !ÌW™Þ ÚvYîÔvA,ÿ½{/)ßxƒÇˆÃ?ã«È—ßÿ³ßU£=Of›ŠÃ·Jn’A;è¦TAhm>”wÙЋ3ýñ¿mæÍ™økútüÙéÃ} ­ã,Š%ÈF¹}œUÌŒî1Æu¦=$­"_÷Ø‚ÄGW4ÖW¢q˜q0†÷›—G$ãmHØ!Ìl] ­Ã.¹êv‹WðëGÉ—YhP÷µÒŠAU½¿h¥‚"Î#Æ©ÕÈ­¬t5âEಭ ´@UˆÏÅ]È´f+w‰±Ç~øÌøÈ͇æc!5D…vë½³ë`Sã}¼a~‚SÛ5Ï58í i‹ôEËH%<×ÇÇÂ1!#ñÿ_S>·³Ù²FŸ%VåydàY%ßa ×õR€G„0Ç3‰ûºÀ†«boì…iÿõ+°êóB”y‘Ü´•£³Óê½WpèÒ ö,ÏkzÇ?øµ俎ÅM—,XqJ¾ÔP`3¶?'±bÊlÛúý0íØ78VƒAGÂ8£…Fì±åK]uÆåâ]a Þð83¥TlTf}on¡Õ¥ޝæÊÒý–€CÕ[øì ¾CêêfQGòŠôsEn…dmÁÇcÛáÖÝòåxkÍ:}5>Èx_ÀÓ Àâú:Qá¿øúɨ• óßš¡ý$eªÎ<­Û× Äò±ø‘‰¯ÛnqãèÓ+‹ìYÈÇ..Ü/›ˆ™ß&Ëv>â.?æ:–0-µ…Æ‹R ïÒIu8¥©…‘ÄÌYñäÓÜg8*½„û¬/Ny…`²üŽ—(âèÊåÚŸô‚IŸÛ5Î œôF’ÿÁôÝYh&þË /%ÖØ39Ï2~øÚ¥Ô‚Å«á´òQÜ1^ŽGºPáA ö|~ ôJ11çU¸¬éêK‰ î«…`òÆmÏŽõ6@¬‚1¯ðb ‹ú¸CÌ“Q}{X¶EãʃÉç%Ðu¼ º¯?Èëp¥»ß’³é­äës31u„Û0ø ŸH.à¶ít¦&‡Ü˜ó€+X¨щchŒL ™¹éô7OÇïSh£Ý(ü¨ùœ f³ mU6ÙȘ9y¥PѧE(‘bÎ~ó½Ø”Æ•ti^!ê??€ã ¶¢hýºsš½Û‚Ö+°_}¯1qS 9RŠw^æÂžPE¶j3Á·»ˆ†ÊÜ\iÌüªr1MSI¶+^>íAïgÆÑôí{ C ¦®½Àÿ”FL"VÐý›Œø?^âvÂrô‘„õa©à×k… ƒtyb›MêMOœ‚]g&°VAa²ôýaX$ÔŒ•ÝÒ(ù%ñc+×Þ†®Âž´dÔÖ:ݯAB¹³~Á#ð{˜Î]µ‰]\bI<½æËò¿\çN%ÿ%r}ÀW “‹.·f7ìŸ@º§.Û®¿wÍaúN¹›â"¸6T¿bm<´^¸‹k”hÊ“ùè|Væ®5Ãuѧù?ýóñõ-w˜õ˜E¥“ähÀ»1pú‚L ®Éjî{2Z•™Ñ™Þ’XºR¥×ƒ©_;@è™òˆþŽ÷¸¾=šP˜:ƒ:NXW½žc~oçg'ßVOE¾§U| °K÷·U| /õºijgнÊÜóÓ0IÏß {‹›2#ŽKùø…Y'hŠæƒwý>ZòΈ^_õž[Ö»‹OâÎxn€ûÚ/Ðqåö'çV¬øü¥-Xë~™·h}$?yÉNµ÷$+h1‡×±úÉ!ؑֆÁg‰ýÉ|wb-D¥(˜ ÖÑ0T´ºw«ón(‰ã­ Ðio8¤AŠëYØ7ùEU·pgª#N|’À™žv‚ûG­`ý—“#øox}†³ÿfÍ9WàÃë$¡ç-¿nàÔ—®†›Ž2ôg^®mE說¿e1c0Ýß.I½”­HB²'Zû“ëyö”kJ¸ËÞÑ=}š_váÙõ¹Ã±pÜ¡þ³SîµçYÎn÷U  !ÚÒïy²qäõÓ!è^¹’¹€Ø•¹²y6cŸÛFàeÇà︢e Â!ã\P1œŒÞƒYY`¤ràÏC`IÙx´ù$\öF»Í…˜+®Nǧ:Rá ÛÏ‹ši0A;›|}±™صc¿ WQ8½xÿàÿÛvßÁ!“xÀ´{<7’Þ ÍjŽyž¤îÝÙQ ­-ÄNo´dáƒ|²ªâ+¾œ ’éqp¼éµùs$ó óý:›]Žý)G‰åbM<)ö 3%Wãõx7XÕ?#]Ù( PšVˆÖ `AÄpßÿ ¿Ý»†y“oáØ`ð» þüú ‚ï±¼fBm‚ÞcHè1„'B¤Éñwiž ˜÷¸Ó‘øªýwDê (üçzü‡6\k fu}ÎÔÛ¡MÞ\)Ñ0x»„ï÷yÈËÎÇ©…h(Iyƒ~8üGn*á¿IOÇ’–¾xìÂå^™‡£O°Èi›pž¥m±¼šßÃð¦Ï°~¨Š\\Zýòû6VëÆ@Êmä æ¤BÜ›¯¤®bTly!ÛOÀ€ßDøñŽÇœ Ø1åôúâ€q:õfÄâÐá…øò«7gey”;ñZ8&ÎÍW3a%ØbÌ:³–Q—¯7!?`6Ç;2¿ý­âƒ3l†Pe #2öáªöI´2É»Þ܆mÜén_÷.åÂÕâ˜{Ê›“kÓý4ø¼=‡4ªeÀßeiÌü¬Ð6–5†-ƒ³‚FâŸg¸$ =ç:¢3Hă ˜“¾ŸË7/a~«ÊyËG soNãþiž'wuòü›Ó{VÂOÒßOÓüçùxìA4,Þ½«½:1kµ6|jÈ‚EëÓ!ti;ßt6a$¤SjÂé‰Å(ê¢IÚœ‚f0.O¦ó`÷Ñ(3Å…í9œLJ&@Ó€ÅyÜ¿[ÇÁ4î(™ß2ötûÒ8‘~ˆHÅ­(òIa1ê-¹ÎmΨÆÁoc0\²ž<½cÄžß„ŒEïy†‡÷ÑÏs ] §MfkIIWV`sš_Boe?ð¶Âìvýÿ_¾ԪϒDY+–rîÄîr¤+¿ù¿÷"~KŽXØaïŠq¨h;æ³ëÆÑ8>¦eãò_µdtžpíòakö*nçò%°&º}¼ñÒ™V¸ÖîU×Ñk• n*£ŠûØ£‰„Ò»!ؾÜýÐDŠÆìFÕ¨oØ3}.“üªÆ~?ïã$' Á3‹bx›L&OÄ;¿¥aé“J|ôSŒ?.…öœÜ¶ Ö&E£74%¿eµaK}"OUm:¾Ø= ¶ˆòO¯Cô»…Ë–e³ý­êlx?™õØ*î¿ï—q«-`൯Åô Ä>Z¼GqPðj,>gÌtw)2}±hŒ:“ GjBä¹ëôg1šåjÎUQüðÆú7ž€žµu¼Þ…¢Ôa¾/|mg0á•vßO»iË`Œù80mýB²Çç¶7ru¼&wæˆ)¶í.—ÂcÓàtŒÿ¸æ­øŒYnìzh$:èêâŸC£àDN˜¹æí¼†·”¿Âó‰·AéŸ$³*ü„ÏÈF9u3./EÔ9Óh³4KÚ•‡B©}ÄÈó¯ çBÒ=ö²DïËшé;1õè/óBTù»•¶q£é&‹¯8:í<ò¿óðÊãiü{˃Ø|}| ïVù±ûÎÑSÖÆ¬âü(®~¼·%9몹m›¾\‚ö(ÓŠƒÉLNǃ4ÛÒá±xì^5­i7 qïôè(!¦ëtŽjø%;¹-ÖÇ1ß0Ag}? ÍíZ?–\zxTdàÉ1³èYêÜþ‚S2Œ©ÿ¹xÐvw!·_Äâ…eøÏ5 œ–ƒ>ìs4"sÓîƒãn„­q•v6; ã¸×Gðð­ùtªæ8´¨~A¢K–Avv$ìªÚ™õàUôFf^y ç5½sµXw+ ꜆癔ŠÛïbîä`¶¸!:é™@ÈÄ¿<ÞÄ ¬:š‹2õœÜÙ%ؿŽÍï,C^ÁGÐôM—îç ™dzÔÀ!¿hL ¤§ÏZ£ìö×°zÅoz‘¬Oš³X4tÆpã–ôáwÃ\(¯ nl›¥­Ã~ûÓŒÌ1’Á#‡Æ²n²#þÿ)OŸ•ב…†(iØ ž³ÔéýûɧÀRØšÙBîçpQ.åK>†ÏÓÀÏ;žáׯYQ´[ò=`Î 3+ÄÁ„s¦|ï8ŠYX[Þ5ó†Háîí¤«Iˆ¹Y *ÚL'~4O@J Åþìå:õU™í®6T¥ft½‹:[ô=L¿dq{År]U:…¹aíøÊú NnÙx‚¨'¡ÇRÚ0Û~#ü;uŠèǃ1ËYÌI(ñ¥I!g¾®±U§ùÛ‚ãø ¾½dŸƒ ­ àñrÖþ²¥¯ÍûñýÅй@iÄþ1Fe$úìiî¶É ¾œ:.g “¶îÙ\ªd*øý<„·'賈²~ÿe$¬>B=‰FC0z±bc°×,Æö a¤*ê/£o…f£ã1<ì[Åo³W¦LÝa”ekOžOÝCÔkÓO®ïÅ 6}à tÒa¦nò,Èbxy¬!Z[ŸÁìklC¾+øÃ˜´üöæš6o­gFX)u»ç/ç|Ö °‹»ë=7bdÏy®UØšþ|ø zÍDàò‰@\?ç7ÞÖ8šCæ¬7xs>žAãÙ§YÂy;PÐüʉŸ…¶[Fìïþõ†û;¸Ôëë°Zc;MYô5üÐoí5è5ýƒëv/ÁÏáj˜°ûïNÅ?^“¢%»Ÿ÷u:Uqº8õHR Û7´ñ¦»tc–Iñ°½ ûcÙ&”…ïÕJlh[lZÓ ‘nðêäÔa›b6¶[Ðø¾ÙT•«UðL·4ýøƒ;k–ÂeW B|)IÂ9íé(" Ôþà˜‹ý\çÆ¿ø.F÷<ºŠÓ–Øc]² Ø­ÄïûÉ¿•?q juèÒ˜Á|Ъe’Îy„ÿ¼‚»ô0µ*ÿòŽ:W൹R#ù_yÓr’¥S¶ .ƒjúžßKw²þ2íJô÷ۯᬟ- äÝ![ÎŒ…§Ç¢áäÎn¢×˜L{‚+8AÄžAÿúå›°•™:ýáŒú0¶§ „?áÌ7›pʽ‡¸íÍ‘Ð@{çxÜ:¬{§15…~¸Ðô ¤ié¸Tšr*”}¾pž¢z‹š¹ð÷áÛÖw0o± ­?šyÏV£·§P¯ºÃ$Êì0kyÈùY1ÇàO¢"sÔ|Ç•‹¤ûßdòÖÜ*ÂÝ‹ÕÙƒJUb ¼ ¯„ÞÀY:æÄíì<ª ãG–ê/Éÿ,0 ¿þÀÖCW¸K’ó`]“˜—*ݶ¤3qÞ'8ìs€$ ¼@XÚÅ#¸)½×g9kœŽûvHˆ`·ÙÞ¤lx6¹Ä5áÓØ£¤7Ê›Íq¢Êç§Ÿ]~ìDL†næí¬Ó_ƒ®]kÏíLЇ|¿X:öY<ïLñ}²_ój½L X†Bôaï+¨Š0aľç q+žáB‹×DÙQ„Ö÷@%õ<¦t½Ý-qü”NLZ R÷b¨ìm9–»Sƒ–7ä­@>ž¶ðâï‚‚ki#öw© ñk[˜Z•Ï”<©À-æÁ6¿EdÙáÙ)\ÖôËœ _×ÔÆòEO¸ÉÉ©¬nL–Ód¼t%,r¥jè.Ž ÙÊÒÞ]FÑ_Çø ý™¿ÎnIÄ/¨}ïÃü>x ×´[¸0\‰þþLéš žlkå e72/‘‡]‡Ù¦o6îÊ|Îà—:[:ƒo{÷0›yÛ¸Uðy¿:Mý@¤‚ `aœ9!ÝA¶—ÐÊ=ì¸fڹςÓÓ+Aé(C^ê1[z~O'VÌœå(vuúæý¼Ý Ž×:b¯‰648ª×JÀ2ïîÜB©ñ†Zøô½81“ÅGYG*Åî/*l¡a6F¿ZÀ7L›†É¶¹¼7VÛˆ»ÝDèðOw}¥=–õºCØú³ðéɳ/šÅ‹×Á˯º°ñÁvÞÀÆÐÚP WÊ‘oížì­Þ-Ø«q ?i’Õ¦o1mcºÿ[Á ˜÷p±ÓûbrªìÅ ¡L^M&ýÅ>ß b‚ >v]®!_‰EÅnìnIî¿cß»±Ñµ¥B?ª²?s&@Q° 'ýû.&(^‡\“qtÏÊÙ#üw!%ÜÝ2 /ê"*z…ã¦{ ¸Ó"ý\áÚÙ–¨‚ËŽ¯ŸÓã°'ó”‹Aƒðzü*¿Ô.Ÿ‡ë„y'&4ƒÐž@:÷"îGlÖTÀ‹¦qçØlׯ!â5#ÿ'¥Ã;|`Ìb\,èÉžl¿ÛHæo‰‚çÎcpB, ÌyÌRY̲æ±(A1ÎónÌSÙG2ßÉÁâqpg»hêÈ4 çp+å ˜ÄàÜiû²~ëj1Î^¤sã8p³ˆñµ`ÉäØ=£<ÜOéw— ¬ˆëT¨lÚxzú3šõõþï[]?oÖd®®ÏŸNHg+òřѺ$èÁŸú^uÈÙ7:>þ$A1öôããz®xñ¸:3‰»ø¨^6ùïéâ^®zìD*{ËŒY\KÆ ›.TvO…'*ùœìܱpÅô9)l…'n*ÌóѸ)_ Oü]¨ÇWevUUêþNÁ¼—»¡::çÙj’ÿÆ$RÎ.ã:¾HÂ`g¸9W“]œEÔNco M*åàE.½vèdXÅcÞ<]jì°=V¦Ã‘c㠨ćí9Èa/'Âfe݆yëQ½×Šü˜‰ö–ØŒØ?+ÿ#lHCõ.AççR¶hÞ}|(©‡»¬“é›3ï)çàÍÌte˜’Ó=²]¹‰œÈâüD–y=—[åÍö׿ÆŬ,IÇþÞMýŽs…V7n˜z|‚ƒ`ï(=Ún~ y¯¤ÝŽ] ¾ËC©éßF¸¶Ÿ—yò18?5Ã=ÏB¨Ãmo¦eÀÙ ©ÑéåÌåâ| 3U¦ƒ…¹Ìö³:µObšûÐ+á5CÙŽQXrê¨.bN‹6°¬uYT¢ð ´ÿË£9cWÂ’„KØÛ„^¦%¸·)‹]òÂOVí<™]×!}^$H~ÔbkÈsƒFÞÌ!ǤO¶ñ[n›ó;'PÚëK‡úÙ€åqvVζ^©Ç¥Êç°¾û=ë<×ñWQ«WJìÞö묹#šæª¯Cw‰TçÛ>²ñ÷\ÌH»Œ»Ý¼høÝñ¸tšµÛ¿”Ýý‘‚"ëî‚oêÈÍœ†íÞ2 `¹w_E»ŽÝÆuªqX0V‰LüÆV×]ä&þ=6¢ÿuåD@3ó,x∟f=çB®f^'áx°,+›dʺÓã@ø¯ífÿ0·òdõ”“]‹Ö2éÌ)«‹snÎ!‰jnÜíMim÷tœePŽ Íf]xiq8¦=<ySš9ïé^Lïð0Q%Ïÿ-@©9Ü!;3,ÊrfÝTŠæÆÏ¤M1qnÀGøKd_ð2Âð×½Øô¶yBX…äå…CðõK(½‰åUiCc½Üá«~QÕ} Û/Í…bþœ?cLßIYî?øxÑD²y–6ŠÆ…=³ÕÙÜÇFÖÿ×Õµ|££òè9š ÃþX‚ëGNኧµÜŽ:<ýïîävîFØ>>î¿He÷\f€GInÑ\Äöt|Ç»³â©wÇxºÂã)Z'Ï÷fbËOÖ2ijn—E¾÷¬ã—3ì"Æ4îšÐŸzÐ×@è´‹éÒéNìÉV=úm  ®ýZ•¦± »zŸ¾xÄ:@¯É‡?’âüì¯ßñ$D£`C1Èf²µïïâNçè b­¸ j²ØG™f÷AÖû+¾…A¾ÞÜ»ë$h7…ÁâÉ dŸÆÜ+¬Ìë)—Yÿ×·Ç¡ƒ÷$°Z£BçÜó`¹S7ÕùØ÷ðp`uy ±Ç×ë¿§žK¥‘2P©ç¬wâ*5Ñá1׉ˆ¡,|2:÷–u@ÆÐ!–ð1„V …¯™Û¨Á•燱MÚ%̾>˦ ‘,yÞ‚’dÙZYHÊþÇjÓöCÖñ@f±CœÉL¤Í?pI\)Ì<åúým2O1aROèºËgQñMX숧ƒ×Ìè¸>?ÜÏÓG™Õàæx]Ï\D3fÞ!Ž »þ÷ûÉLÜó|îM.Ǻ«ö(pb÷êô:¼=C–¥'§±À«qÌYä,ŠªðF?à‘Ê»•¼×7¨e‡5ë]X!Eõ¶$ÃññGɼ֓È÷>ŽŠü¹xiû+þ>%h¿¼•Î*öÀ½¢:ìì™j0+… ’ùôscþ|ßÊó¯-&¬[-Æ“‹.zoUHö=vyô!ªR2‰&Û°1‡äq`^I&s{¥OcѾ\‰í-®upN–ìHðçhŽ6BñÜ]™®^,¼¬÷òŽ«+°}•ß¹ÞÙPv0…UûJs¾f@îXÊEÅó![ÚD”ŽÃ¹n-Ø–"ÁWŠ:É„zl<©ÆwI¨sÊ“ßüÞŠJXÊLâd{p«zÜSšIWN¸‹¨jÈNkÆBÜö0 ¬`UÞ6t†Å°¾Ù© ¼äH6å½ê¯K‡.ñ9(6à…í±p6èL®ÄÕ ß‰ŠÀxà Òٓز‹£07åáUœælŒaM»£¼éhïEš·¸öÐ!Î@d/8çø>MŠåcQŽûþ{·5ºJ6\†ý'PÜßS%é_«lHàÈ‘×߸#éØÑ¾‹^³F[~·q|M'èÛmÉ3úÍŸ<Ä™gì†×Ïñ¶ZE¶iË¡œÀ,ïv—ˆÝ«äÞ‹Bù)¨8ã¶5;æà)ñ»Ðûò*×zrSÖ@›SÖ˜ð°Ä\^``C¤lQeqÚ·IЉ ÎÖA‘ßè­†ÃcÑì¿÷ïA§ŽDЕ7 ï;Í7A Š×^ÂÚE)X¨ÿÙ¯q [ç@¤æþ÷ûÀ$HÓŠÄè·2ôSy>TŒÁn#úwnõ9¬œ:Šeà !"›â`—»:»°âÔ¥Ú™™°N·ŸxåD‘%ê øå„óHuGÛæx.-1&ñ€§E_QYB«¿¥Ã–&UÎåCºKʹ¶8˜r³§Žçz¤®ãõm˜»³%usrF¦ôDÝj*û¹ zßÛ°v»›®—LwÑ(Á¯üãú>ÔåÝC.öÖJüž²’çËn=Yÿ‘ï#áòîyc6yì<x+°»­>‡[îaå¿) ®þš’•©Ê.oƒðÁP€v” k^kþÂä˲X¾AšøýKöKx±XæÌC­GcèƒÁ¥šPDZòxrÞ'9¿ ó'>ÅÂKHÄznSò¬éøDn?׃cïžà-QjÞ>»ñ‚S0Ëô2?îÁÿ_´»(§ÓÏ£ã¿lrïÁ×£‡ø«ÿ,à¤BzɵËÅd³E ™X ÄOÿ² {e΄ë-Yv¿)ûb c÷¼„ën"ôb§ 7<ž·±ñ ‚Îçz1¸i>‚9‘ Áû]%.nYÌ‚2ñ̉g¼[R ÔM…ݼ7™|÷ªB±òÏ>’ؘ[&ËÕªáÐåh,ý÷þ«¥~)«Œ‰‘ñ³y]óט¥Ø*²Bõ«(sS ÍOmäëJ{¡÷ðF9Ý góöÀ´:g"sæ<ä‰vqÓ ìèê=u$P  }ΈýV>×ÉÀ_UîÎîãœÁ) ôšý“ðs½à\Ð+,(°e¬Ÿ‘©E´°3šÅdØ2Ao5n>ŽXDb®ˆ•a‹7óð½¼v·$‘=ã{±ç#¤=q£PBG ’Iá×îã:îŒáþ~|/ ظ[âд¤Yî',^ãLVg^ &Õœó‰YX£Ë`rO5oîÐ\Rø9›Ý¶šНá=ú[D" V£«Æ}½‘…ëy_$q£ºñö2Z¢6uÆÒGu‹°nòWÐ2Žk[Ãÿê¤]–Ê}í‡ÁµbÉ­YÿÕK¯àãޛܛøLv¤·X³&?-¦ñéô¨Æý_­±åM_ú…ÔMú µoӹ㮈 ÀÇWŠ9\˜H ”¤é¶Eûi§Ù òon·òÊb³n17t°³ ¢yš—ÄXø s5Õ›¯wÌ;š À?t s[šÎuÛ5ÃçIi0æ–ìS¸ õ“»ðð"vnp¡ ¾ zq8ßÒóBÇ$®üÒjîÅìÌ·Y$0‹kbìÕW Ýqæúupk‹Û¹ýö"<}‡¨fwÔS~@ÂÌT,ݲÖw¦oóÉ•|${äGðÿy-y¬Oºæ/Em9Íÿ«3Ø2/ë¯BL²‚ô)ÖLFœ€³q4tÏ z)c،՗n]ânµðnú3u'b¾‘§uµ¤#OŸ^‹Ú%‹2àsÏ ®"ù쓆9¢¸ Ì–**ÄúãdÔ彸0¶ –YÐÍl÷lœ[¶4Úâàªë'\ºq S”¬&féÒLG^7…%AÍ+!6~í¬ß"j¿¥ÙþIvŒ—ÄùÝO}Aj›)¶ˆ«Ñ{G¹±åy“/ âäÐí&ªk@µ_èvÝ;´sFðß=gØœ¶†4]+¨©â£ä>wÒ#…"ÀÎԋܨÄ{ ¡}N߸ºŒ¬¿\…;䲡8¾ ‹r™Ñ9¬ ³azOô!7é¤Nú 3U‘Ò$AúªL}‡ÇÀr ;/¼ª—¿³ž}Ø9€çý'°¦‡ˆ®½6ß¶ÀLC_N&Ý…õè}Æšê)<ÓäÏ\Æ‘N6í<~z²g xá‰;c'~ýÊÂ:‹Ë Q&I÷h*¡tõw¸œ³«v°ä9QP3˜ ¿> £äeN{t¼ÜsšD÷ºrÊwðè€ýýsñþ«‚Æ»øèoš‰ÜgŽë3‰~«m_åèœqRܧlIÍ‚µ‘0Þî>q««‡m_àjrÔ½Q¦‡6ërU²+ùQ'ãñD¹ÆÀâ™Gý¢Úõi#©² À×AÆ(8ak“ۆΙàÚ^ ÉbtƒàBp|ü÷ïÔ`!gqÝšç0_!"ÍìaÔÝ àHu©Óž ñ¤Ç+—Ë©°C÷æ@¢ƒ;6ˆ\­o—*‘FŒK¤7êð±Q( Yç£Rô PÀ«1¿ñoÜ.øæ} ã–¾ÆÌ¨Gði– 7qåôüÏ—È@ÀíeüÛ5:hgXB/DÙ£Á±Hk$膿Éü“Ú—à”Î7Nçfè襲c¬-Z@JÒÞÀÇäæŒã´ð¼~>áOÔ½¸® ûàñmÞšF cR‚§Ìzr)^Ò;Š—2æ±”ë߸Yµ]‰GëçŸÂâ“¶ñz[_#‰‘¡GûÒ±Æ?—¿mÁ«×;] Ÿäè.=khZmÎ~F­†ž¥ ãnÅΖéà̺w 7§£2+8»¡ƒœµåxöïÇ9¸Þ§I£ßý€æ7W¸.ôkÙû:Þ¾hÜ5¯Æ­;c:Fì¿£³Šî†²¢ê,;ùQ_}LcË5_ˆœö §ð¶ÞÄåÕg¡°mç)ö Nš¦ÿ3‰ 7.™ˆõ”À+ßs°|v{ú€;å+UåYmë ô¸²¥Bor1çýGš5lÑÂŒÌ#ë_ëk!q|³Ô¯à뇧Ðt6á?Â+¥Ë\Ó¤Ú:"äðp^r; -ÜDQ~òœ¿ôÙ1ë6Jˆ<ÆóÓKÁóH0\ßòëê)ÃÌËK‰sÒIØXóœÄò³Á Ûñ]§/÷L•ä65â¥z€å“4¶>öAð=“À7ßäOÒ¹ AS#ø›3Cp·c,)ÊC)G8uƒöÿãZî‚¢£ÕX·°,u™îEãÃÝÑzÀ E{IçÜ\8•;„¥àô 0b«Oïø*·&ƒC"ÙÍ‚ü§yÓ$¾ƒíÇ (D£GîPßxqùƒú§âÌ»Rˆ=(—…w–ñðK=>öíÆWíãaÈH‹m<`Ö¬ øú°Úž“xÇ·ò’ à±RÛ°Hæo‡dÅFs&xE‡Þ}Ô‹ö·}X÷}ºAû:¹–¤Ë+¼Ë==ŽZ†éÒ}•ü?g½ax;üî×f*çEq ™ˆIwÙ·g†Ô#Èœ~ª>„K|¯£à¼zŒ›-Ä‚f¾Ãwû“é?ùI4ˆ”ã±ÁpžŸAÄš9Ìs`¾fcèÓ«eÐ5v-ÉËG»Êìýœ+±rÌý¨¿®ÄÏOBƒóÑ86Aiäþ·’f“«íÚ«¤î¸;n=ZÉ ìÒc>Á*té¶Jû÷¶ë½xÖ‰Çî(ÅBÝ 3úÞð÷ÆãÐ{òkŽ5*ž«$/\ßãœu˜ÓO²¶çÂú[I¼×-PÞ¿›Ü›¿‚ÓŸ¹ŠKnc¨xüwŒLÍ:°Ûê‚:•™ðw¾âb½¾sMŠÀ¯”èêW¿8‘|[&XbÍ&¯5ÅCYðbÌH7 !!guq49†=k]¹26.Ô¤f·¡tn>÷J2<'dcÇçòÜ'±°Ù`G7âÅœØéé°yùg\æHUìYÿ¢¿ÆÀ‡½®P[›‡ŠŽ¢tRº ^­ÎDE´Ã¦µkáäl]¨o…¾Ë8â~úlîàª&ÜÇÄßQ(QfÇ[°âƒöçŽÆçVßàÀ(¹AÑ%%æàžÓÒtBÄúxÿmR¥®áS±{\úpú^C±KþË÷ÂǃМ£ôéT®®c v§þäx1uØùä6hnù ]5'±øõLô¿3¯•ââé3qÝÇΣpý+—É·[M¼¶‚VE5çç2š&>#%Ç>À²Ñòôgʨ];™lµR…ŒAkºK-s„ÿ¯÷ºDaò´Lœ?)®ù LjػxñS6äæk`}¶óy_‚ÒÛ“é{kköBÛ=úæ›dÝ™ÀûÅì€ÕM8ñdu=û› r/¸C'º¹M‡SÑÝÊ´Mv-“p¿S».¶™ú,ÚÄÿ¼n,½P0‰®/ÁOí’ŒïÐŽµ{°sõ1nnÅ<ÊÎø!é$(Þ¹ÆFÊr—«ðäÔXNÞ*¶T”eN{ŒŸÎ­¡[Í®£ç´mÔð >^“½ ™1Ûè¢@c&:¾Ó·Ÿ‡·õ3¸oÇÓ9v³á{þa¶tS!´mþC¶É°¥á÷ÑY×e¤þcëä)œcÆ4¤Š¯ÞâýšSø¤É€ñÏpöò]÷mÓdf tUÔ%>‡Ú> ¶60¬ÖÅ b9#Ó¥YM”^™On^Ÿn¬FZ­¿˜‹I ˜b9Åi±áPS3»[yô¹}¹KÆ2Ým|,â›àÑMšóô ÉL9HT£{ëÇK™°’!W*ùG5I5¼P{V»×$›§\àÝÉdKφĦ@±ù"¦¬>*&zL$[€5$Ž:Ñì³» ÷sê}Rê)Lw,þë—¦qá–ÿÈCÉÐ×ù Ïü×µÿ{ÿíÌ·È­UÅÃå’èØ1ˆçÕ<¨½åG®1ù9̸»ÇÝ”g³²Ñ¡o ;irÚuü0í.žå{UrÕàïâÙÐnõ†;þEÍj¡ÛXZÚoÁ~ÿƒÔîElY6€õGǰÐ3‰VÆAü7ê4¾mͤ™‡î“ƒiy¼G’ °íK$^>»”YÛß ÷ôã!_?ºÔ ª§ 3‡=2Í‚¸°¥âx*ÉÕœÆÓeA“!uc œ_#H·»Ð-C—á­¤M¿3¯u¦k3¶’m­ èû š;[–.l¨:ò‚3Û;•EÙ©³‡Lš• ª²m[Ç¢âÕž•‘û<1ëV£ï½tîÍ`-4uÂì y|V© ó»eéõsmÜá{˜å E.ÌŠÆÑ£]éŽqîôήøSÒ€V?”†½óvÑ÷ãÅØv¾L‰ÆßV/8•s²#÷¿ôîÌ…Sò'IÁCKÚcxÿÞŠ„®ËJ°Ñ\›™çͤNjw‚ƒLÞI¤ïÒ¦¾íZì×þ@ºÑf;þx E¦±uÒ‰½z&ÀÈ,V­«•æyø&ã-N:dÈäáa•JÈød”sGáß»e˜O×-SbŠ}Å(p=&Ì`?H-÷Û´û·7>ŒŸOóâ„é’ š¨šMb˲ñEº8»Q~ƒ·ñœ#Ó qÆøW98´6Í¢;ƒõ FV”.S° wóóé¯)‚¬¥ù‰+i U…Rhâ°c~Ìc¦Füàl}èß4sÏyO9Ùk& !8o&Ç_ë£KœFçrê‹M¸ýœ{/ÎÕ˜tÁ·«ÁhZY~"I.rÀaÚB¹2L~ö ^p Ãw¤ëÀqgÐÑù2æäj“­Ïã{Ñxvà´ª~ÀS*í® ^ÈW®™…¬eÛ#÷õÎM$§s `˜+æGHÁ¦{òèÐÚB<« Ñl#Ñãµà†xœfê> ìP¾o…F–ÿN!¯7’‰>ÆTÃ={Û<È@&ƒG±Âì8¨ô¯…GYþTNà|º G5bÁ§G\Ï„®ûž#ñÏ(d.½VÅ?žuSÙú VÇÚ£"Tâõ?Ò¢¥M­[^§¯Žh&ò +”Õû/ÎÆ&}1®é‘ {÷Ï•%N”fÍ/b攟0¥¹Ðu8üðÏêáÚ¼Âxq&rÓ;!|^6šŠµ÷ÖcÎ^_úë¹)ˆœðÇ!Åz¾ŠÚt6õiÌ»ñS¨&;:ËŠatO¤k•â&Z¨Nëöp ’ZˆØOkX4gÞû¸õΡÞ=W‹Që@ªÛ)Cç·œÆYì¦Ü5l§Í.$Ú€^'PÅËH¹E"N>3i„ÿ"þrøé<Ž(Ck¥ƒè;GÐßy¼¬‡“-xB·‰g| o8ÞƒfΖNßkC.íâ¼Ü EJÈÚ¥?&ÊrMœåßqì«€Yvh4Ñ5Š ‘§Û´È®vy`÷ünÞÀ‹õLä'ã?Z ²N˜þ'½^Âd ³9ü0E[ÞWu¿!ËÜt‹õM€±úõ\×Á´6ÈB†`rcÞø»3 î¼F—À[¼ïgRŸKÞx£Ø‰š”.aå!vtѹ÷0íË}üÄ ÃŸ|IÒôž‚dµix] ]9nt­Þó‘þ'·àZ¾ð¸­_nƒøÏ"\˜ª jó‚!\÷NýÊM¸²ƒE†Ów}Çpr«$Ž‹àßsO‡¯ß­‰ä²Xâ@½ùXÍµÝøé¾$ÏÑzîZðØ,òû HûÞ…)[ÔàáÊ\ÒqQ•MªÂÊëðdº Ê,ßÌv}|_T…áÛÏtpÞRÏ­œr‘¬j˜A¾Æâæ k°åsm•ž´Épûx{ðÄ=¶ÑÀ€ÜêW@)áXî˜ïrô©Ñ¤*™mœóÄ\¬LŒ¡“㸆ƒÇ¹W&*u7gâÏpqÖ+½ú½¸À,)jÑ7‚[X&¼U˜‡fk±åù¸º·Œ-ŵ's;WÍ¡â‘"¸ü["hvÜ‚?ué‹YcYg¬•¸¢ÈÊwÅà‹AhñGŽy{µãòÚ¬°a«è> wíq3ËÕìÏ@žð´ÆìUÓ°~ð諉Rb4Žš—m×w8èúÉÛ;_=ITgº'Ø- ºñæ~r"òÍö3ž«¸°—+žçݧۿ¤óõŸÄ¡†Æ\²Üjt]ÂhEwæÏ¦{ɰ÷ÞΆü‹ÐöJÖ^ RsÅ`bÁ~PйÇuþIá^žSÑ?:¹¯!oÞ9¸öp4ÙP!Ì>= ßS¿aºÛX²*Ž4¨Ò ¯~ÂÖ›S¨à=#¸öü*|/™ÆB·W£¥Ù²z©]ÂlÑT VàbLE#3`®k\¿# ¹^Z¨5Q‹+šåΦ½°e›~¬¦5Ëq÷$SZí°u+ÆS}›¹héšÈÝ_×HüžëáÒfYΨçä§µ›c!'”ï.iJìKá«Z'œ;bI×÷îFu)!ö#†uïW¦~æ¢ûU7vü ÇÿîÓ‰nŽ¿È®wk8_¥›dÁ'2ok!óΚ•w›ì¤SÈu˜«þ;e„ÿ}¿%oî©€åÁû ©NÀbN*ÍNoÂÁX{ª¯úUrgMçᇂ·hµ3™§Ÿ,FEª“ Û„Ù^½f½’£ö‹Ìð·L KqâÌd|øDŸ«¹³}÷îÙâÙ\i| ¾z†W¾|@Ýëp“ïz¾Äö HYRBÆ8.‚¢-P03žrw°rlù¸U“ZÌÁ»9sA@øtnχÐß1Ì;è×â_ áSí±8I|Ö 2E¿A7ªœ®ì§Tvf*½³¿Ò “iKo.lM1ÅÈH+f¬v’ÖˆÓ'k*C¨rûáû¥ÂÉ—YÉß±8ðà*ºÊfƒ_T÷mepOèXõ¬²w&Ö^~Ù:N¸{LI|ú†7gñIÄÛ¹ø¹d8WM@Í4W ðJpÅ>)üò_ïCÝIМ²_w(QVªN”^æ¦þ”7wz#¸ŠÅúÍ3è …(|>««þÔ˜f¾û<Ä«ibôER(¼)¹vK˜•}Ò‹_y¦¶Q t€Ò— ”¨òÀÞ(V[fÂWz€ß é‹…à½G=W=´ž{xí>$îÜlC1§Ö¸˾¸ãŸMú¬?]}ÞŠ{ëO‘ƒÃFì8|ù„’­«Å@`²$]îÂIJtÜ»QT°TâøU˜DS¹ÁgÑ}Ã{Ð|ªÊêŒ*±UJm ÷ÏX°·;wÂ1¿êE}ì©@ÍXÔõ‚sZ¸7Ùíhõ@^9žO74bjÄuo®‰Ã¯àþìa|ùº,vþ}ÒÞ”‰F?ÊÑÎè*f|ÇiCnÐ2Q‚y–ï‚Kyó`gç,¢ìëÀ&“4`=U¸V¢ Û¸W&´ñ’½¹§Š<ê1`ÞaÒ´°­‹ð//¦ §ËÓTøZÔ N ì ³åûVã³”ñ#öï©»Ém¿ví’Ÿâ‘K˹¤Æ!Èqç©Gà‹S’lÖUÖqV vÛ†ÕÒÅDç˜/ z ų }ˆ¼Kdå» œ·µ“áfÙ¼Vª OR΃úÇçør.âßɘ4cª_²±ßÄPþSe·ìDØ«ÛN¬nÙš}'ž,ü´‹Ü†.A):t2‹ÓSö$âí!Øò"Z¸Ð£©ï «Ì“fß¾3åÙÛ^¸äö‹·Ý`uþ&Tzg:ž8pV¬9ÎR¯Á«(*ï[Ç^U,çÜ*<ØŠzTGn~aYøå^\—^ĵˆ|ᩌ»ƒÙ7ØzO vð[ ¸?q±r,§³Ñž–Ôá×Ab%dJ=?ÇÙ1LŒ3]ªMÔæ‰3§ÇÖŒw×ýW«Ý6z"ÝkÍÏ­Òb¨öÄ"rÑxT mœ•2ÂÿForaçÌ娕¿ fn1¥¼1¢<ŠÀ3Å'&´˜N5*G#U!š÷Ê‚Îc˽¡+Ê„%ͣϭ‚¹5Ø÷ñdÜ­4Ô9v+hÿõx"ë¹-úCLJaõ«u˜’ùOtÿT&»j9Cÿ­´ÓçÇÞ­ÂΰId]ÈlÜD{é¬ £˜‘[(·¬c RµJ YäNdb™e"§Y­A•­âm…ö·\€N9­JKö=âzRÒhÎv9°™6g¬ƒÅÑÉX°&‡ª'ÎÎʲÙeÏ1sÛh–\7•ny•A^ýÿôoÓùI"ºº/ À… ¬& ¿kØBíµOáï/N/?Ov­ ZwËñºd~,+FS.‰_€ÉüЇ‰hz¦èÂÊÐ?õÿÕ*;î„}}1üF°Ïc?6O˜}‘]ÜãLëaŽ 'SÁñ1ìçÅ~ò!ØžeÛÐŒ€Hò)ÙÎ÷`‚w°y0sQ5¦ëx>Œï-Ë…L¾/ÍNyÆÇ5-YÉM½rí¿ Ó;^ñA@Žo^JO؎ƼñÃþÔ­íçiýâh.Q9×Å›@wF=wõsœû°¢ÄM±¹Ÿ‘Ëä8â»$ü;^"¿7‘NQªÿÝ\RÒÈli-•~<µ¶±KRˆ©¶9U*ÙO“ñÙÖfdÈŽöþ…nÝ£(âs™®‚§QòÚ”ƒÛî&‘é3áÕ°«pv*›4z>jësm5“fýaE¸)k"kxæET~ÂÎn!Vtc 7¹b>^;|dÄþiÅ"ø2A §4’ÌI°Mx{k¹š&>bÎ>adöÑ.P[´UÂïžéYVA ˜âÅÚ%%aNEÛüvýM£¨ú‡ùTàt ÏCŸtfCì©8UÛ ·}fòOeï^YÓ†”/hÊ hOì‚,-8©NNgÞ6ed¼D6Yßþ€š.€¾¼´™¼¡Ò*HúYù_=4U•œCåå'Љe¿¡ÜB—æŒc+ú×Óèº@Èï0fí3ý:±çá‘,QxÍžZoO¢.'·Ð/çƒáGU$uØbÀ’Ü­Gôï}‘m*Ë]縰û ‰Ü×G!¼eÏŒ¹Òµ÷“ȕܹΒu‚)Ùu‘»½ìúY-ƒÍÏÞ‘`õlôpn+=†rAgàëŒÙpÒ]wOƒ“Þvƒgçe˜ ù˜½¾f&áMìÕÄákeºì¿^ÍtyJ:3¸?†š L-ºsè·åIìá9B©Õ&ºŒ5Ÿ—e5òQ°ÎTN; ¸+±—HŸOþÆì…EèÛÁ)lƒÈvêô.Y¾ÙÇ 4ÔdúÒ„[wÙï©çqúAX¶a0t »¹( t?Ó^y”†,aS°¯ãVŒðÿù»®S¾£¦3i^@Æÿ|à’+u“r"àv÷6¦üm§ò:‹›¶Xœ]>¯†¿¢ÙâS± °g»ðŸ.ü•»Ää^Nàg=µçudit~¢.0½Kûg]Àw¨pk2h¹ÇS»+k¹~ÿdòãÉþ;&JñëtNsH’YœÀFo\þ~}úCCÓ/àsï>h©ò£Së³9ÍE.ìÒöWäaüi˜$¸ˆÔDõ©)°Aï÷´èÍÃUxÃ!6-²ãl’à a‘Ôæžá”>þåR±Êïýp¶Pê¤sœ[<‚ÿ͵èÒ—y”Žs‚„O·0EÙ‰9K©S‹ºMlÛ_k*aü ç\†ÏÑ[PA\WÈ3t^Dw̸ $cçYc˜x2…s<¿•¥>T£ZY‰Êú˜zn¾»L·Ðü Ë\­W†ãïôëØ9ÜVbF_\ùˆª6E ’çĬm&–{´ÙîŠJ~«l1Ÿ3ögÚÀ…6}Zµ9G§¾'YB:®C'"ȹŒ2pp9 ë¶ìbr«W±[Eq2?š™&¼æî6=ƒæSùUϨ¿£F4]ɬjòv‰'0ûxÿÂ~n DÍ“GüCÆ==†å6Ç\ª†ü¹mªwðtÔž¢Û"®ë‰>x>ÃZÚìúØT˜O F?«ü8×5ÝÔ‚&7År³ô6¡ƒO9®÷Ò†pµû8_í9¼Ÿ‹"ñ¿ÑW¢V$»¡¯…c³pï¶F~šcR‰7õÆ<½Ã˜k{ÕOÝDç¼X©-`E}0±÷üXF„"sC `C¦5êMy‚’—ŒÈìë?ÀC7 –>zŠe ç¶•ââð$DŠÝþWiž¾DËÜ–ÍIöYZÛ7Ýâ®Ú[àÌuçaiS%p ®c¼tÑþöJ9ØVö‰¬küvצÃàñp4 ~ ¢´¿T‰ U‹S ­Q'}•šÇKÙÂoS*p¹ƒÙ‡–|”‹%bSNÔk¬3¡&Žãªù `h— ºu’Ôʸ~7·{.Ûuß ÒÃÉ®\;Vý¹ï„öÑKH¸‹œ<Ž—ÕN`ý˜V¨øz‹wÆâÝ=ãé¦5ûppG Fu¹VO GÖóB„§ È¼cÜlP¡sÕ@*B„fÌ ‡= ?ÉÏ–Е{öeqtV0ýqs7oùúלkA%ñ +Ò4dðÔX€[¶@šXÞÔ:1-¸³ø7­m4f-Ü ¼¾e¤Ìi>áÛ,gåÍYœ9m…¢_aLÉ$ò?8ÎH€nI” Ï‡óÓt´`Ìý¯p/ª„;ý·€8†;Рù‘4}§ÁHü¿#?dj*¸ôµIØ#* ™ïx °NŽFyì„£ßÞÝ…ƒ98)¯—Û[“ÃüP¥Ò”^œËŠ[tð²²ËŸÒ «CaÐZvd8íkêÀ_þj°âT+n)tcâ÷®àù™°0Ï}²¤J3#!cZš«Ð¢œ?°Á!ƒè^:‰^ F¸´?w8'Ã3ë“pìI%ð^r'Ö Ð‹YW¡Ìg2sŸ„v¥dïñØöOöì×v2<†¨?çÙèu‰ ›– Ny_ÑFá2¹þ8 –„*=u…c»¤i#Šðßø8Uœ¢·~Ü÷åæúCû_3ÜñªtbÁ¬D‚¥Ó.^i” K{Dôv¹+°Zi·7#gVÃãÏ.L:©soýÁi'sðun#æGˆ"ßL‰±Ó9 Íý`uKVjë³»ÆaïP½Ö—ºÜ·r¨'úìü®­`’Çâk&ahÖÙú\Iæ¼{"SïO»òçÍ9ë©ïš nÎá“ýdì¤B<›ÿÆò9±)û¡P<ùÁ–‹…cæŠÖË1ÍQW±â²ĵ~B³Ä LÈý&œŒ Fí_G1DÊxÄþÌ{ =0 f†´rh ç4?WîŠãÔNJ±ùÂô渕ðÜh4;ºAë"øÆžÎСô„ø[NãDnY Ä<œ©±ÍÛïpÊ“ù²uÊdé9sØ—õ›è\*á¿É¡e_&ŸZ ·Þ…ë~äÕ"šÿBùG~Y#%{³V.&î£>ü7ÉÚ[„1›«À<ú>F7ƒÔU0Ó¿Ì[+ 1)Û›qþCÙ°À*‡r—²à‹_9ÃÙØÜâËÜ…¼0±ì6”*‡‚çf̨E ÿ«·¶lMÆãûcĨd¼ª+;’ÿ»Œ“`‹‹3HˆW!X]ÊqÅ Sp)úÇù •`y™,K\SÍ]¹™°ÜåaÇS-zëÉi²ä_üØšŸ$¾£‡añkчôòl“¤Æó^qïõcÖ£aïÌ»‰G›…Ù…¼l<šØ_Ï?µž&­ ÿâDèiD+Ëñ鳘¶Åôk8¼æ{b"ÆÐ'œ<[[…|YOøs¾‚ü­²†[Îa4dã’6;=çaÊwC¸z~ö´ ¢|™+^y€úû#àý¦É¸BH€í¢œv² ½c÷”¯pã×:çhø©ïðÇ|wY˜v÷"Î[> ??È _*ø³4 jÎŽÚž«{I‰´|‘ Ózƒ‡3åƒË$aǾSœ³)Tû|©R° ¾•ûŒ%ÞË òz4NK‡iaDïI˜eExwþ’oü2<ôs'ÖÞúÈ+'A!ïùÏ30*]ƒIO>Ï>6—ÿè ùZÅEœ_ïúuC"ô¦øâ«Piº9SCŸ-„3ú¸ÉeåˆþÕÝHÆ%;sÓ§š²ÇNáõ„ø{­$.G»Ý?㹃è8á–ks/HƒÑ¨¯¨õt¾g‹sÅØùœó\ÿä"|͆³\0Ú¦5O]¿¬.T:íq=·ûûvHÌW€…×xëþ Ó‹wθäQEê){œŒª§gËåñË>!úC×wÆ¥à¿Ã–¬›?ÖõÂÕ@Úø¢›¼Ó‡ß¥]áúž ðÐÎ+_KŸS!Åo6ÔO‹ ½aù0uîbºèQ?œ>þ úÑ™Þ!Ôki< КˆUc·By‰) ˜ˆg/ç²EÕAcŽ3Þ2޾œ“ŽMöBàZ›ƒ{&á£M\EgQ=X Sߥà݃¼¸ÌMôÑéP”²…›¹Ø—¦ev½ß“`kµ’N-.1Jˆé,Jëv6¾4;šòÑ8:rfÓÓVnha*ËÆ™Ÿ¥·öÔCÇj¦Ð<s–¿9 ÎpýJ:Åë wpëkü¸x%Ö_7GÝûp>§«¢Ž’À±? Ñ.}Äþ…=¼Ú3A0p[›Ûö Zdyƒ­)duölPø Í…åÃ1ƒ²Ø%JË>•ãhiK8øà&Ùí¹‚ªE¦–ùǹݞ=øg§'.êÅÙ¾þÊßd à‰.”ι_kpqÊC”ÿtýÖBùuU=êü݇2ìn;;¥sÑQp#Yòò f™Whƒ®§ Õîq¢†ß&R—Ò |9¹Ûà.ÝkÄpͨûÕòHQWÙ^/Î~ÚÇãyû³®{|å𸄳]=“MÞqvu„›ç•à»x¥Q“Œ@åï2´8ØòÞãQù|,ˆ[̽lJçñyØö ^üLGÞ›.è‰EÿÍþŒ·ƒ`¼Õ"2¿~ÇÛaMŸµæó[—£:,ØèÆ÷p­;çY8cC´U&@åÒãP‘ô?ç«Ð5k}¨ê!¸²L–MRÕ§ƒ^`Ï©0×3 °C1 Í#ð£•_mÂÛZd²¹;²í ¼Q6 ¾1xúU¶^ã¤5§ÞwŠä¶ ä-4Gâߺõç89õz|sä—s™Kíykå§ñuör>2NxÊ\‰É߈g~¡…äÁ˜Ïܲb4g»8;)<åîN.«yÿéÌÂõÁJtJµ½ôüèì)†S–þ,lŠ,S7`¿ÄÎpõr›™ÝŠöøv5yÖÉ u®6Uå•Á=&:0I¯4aÎóçâèÐzìž/Èö×`tW›ûç ‘—}Ä}ùs™¬2„¦Ûà^sW-µ NµðaÎYÜcG§]Ëá6—9°×룉ŒI ‹óô…óŸk¹+¾8q”1 “Xú IüXùƒ°¡,vÄÿyuIr'mê®rrã‚à}Z1®öÂù‘Xr:g¼Hð±`è7©Ä%Ýã0àÅ üÎÙZ±€w1xtÊK²óâ+xhS "2ÉðÚd –âsr§‡äaú¾çÜÌ9ê˜ß— §nI ’©(ûo»]¡ümrfM+ú¹êg¡hÅE8ŸÛ ?}4Y\óJX¡±IúÐ ÍX8¿þ¿~ΨÊupI#Þi¿€iË#ÈÒqkñý©^\Û'ËŽN­Gê]CV¯9NËu™N‘>´véÀG×ùæáÂøÞx´ØXP®< þ«þÿìŸWu­›UàÓLª!fÀyD&à Ù}°c}$—{5zWÇm!õ81þ9¢¹*g©›E¦|ìz%ó2d§Äò'Í¥ÃÛ¸¨óædAJ=·oÍdîըܿ5%0¥F ŒŽ+Þ PnKÏmT¥Šö°íèµEÚõÜ$ßsOps¿ Á€Vd9ÆÁ£<^jy4ÜýÑÈlY¶L—b4eiCm7 ʉõmäîjvk–þâÏмˆÍ§‹ ¦I³þ†kÜ“ëmà=FjÖ×bi–[m­ÃE?§[U“ÀRü-*‡/¡Ï+ÿÇÿíFù0±÷ýÎn¨‡É×´é˜Ú‡`t*/í£{@†6ªfvžÇé>ñÜܧ£è„Ò}ì[ÿ nD3Ò+ZA=\ˆÔ1À°¨U¨Ì[Ì šàÕyúxþJ"ôU§­viDTt¬®+ÅÛ’nô±q$ï4Äà |*•ð‡[j§ËþŽÝ·µ]Ù¼‰3ÙY»1ÜÝu—9·MrÌåbûé뀉í–“ÀãÈIÊíQb%ÕÙ èá ñê90õÃU]‹QðÊ br|+Ý-ã‹ –d1­A¬Ý]‰³JÀÿàÒTU$§x$ÿm½Š¡ø¡eø:È‘;bX+¶}‚ѵ`ª¥‹‘ë|ø\ÉnaJ>í½ ïÄ’/KÏ“ o¢ÐñHgñ“‡ýÚ´`fÌÉ9ÝCæ¼ìâ´|¾3ízŠóö“m6G¹ÁÝIütEYzÖV”Å?™ãú|g¦\¨€ß €ƒ»¥xr]âLïæNøºçΰ¦N&ÏA¬O¦ÍšŽ`Ò€‰ïb`G½4tn¿D†ÇpóêêHǦVîq?WOnÀ=O—áP¶'3ÆúÁxQŠs ƒý¸p2ËÛÜT²ã¾-·ÅìÿÝÿüyé<Úö ó¾¦;¹ý>Ä=`Ø›r憫³¾wy`pI€)'k³ñŸ›ê£&¾ƒ*å¥ljzîÞlKý”M`Ë—V¨öds¦þâŒ?<ã~¿^ £Eè#ÍXØ|C‰vØ^ç?šü^žýíê+ŒãŽ?äâß.]E”¶]ã2ºy›\úPÙú!*øI†qÃñFM•¦(ssíß·[|¤ºo­>ÄÍj™À.Ÿ»†ã€–¬(ÿGH?Ò.<Á€NS¸:BÏ9¿0û¬ŽÍyè“>gû¢]Å/N<>–.É£©g¢I;™7bÿwŸ»å,æ/®†É*ôËÉ÷8Çíþ{·‹î¸´t^É3úĉÛ~¿˜3¹:>‹Ù0mó 2ÍÇŒ¶dQšlÚè7&ÙTðÌ.Üþ@ˆe‰ËÓQžÆlòSWê‘!Bœ@W˜8BÁÏp0¿Ú‡oÝàÌ]Š\ÕoÈYW£¸<¶k¢(•\É7ÝZg—°G®á“ Èü™]ãöÁ ç8¾I¿!û¢¢AïÖ<‡½Oá‡Ä<¥ï‡ñ´nÖm.4â2zð×ÒYö¬ou?¹m~•H^ñ§ÕUØVbAu&·ÂóïX¶1~Äÿƒ¿~á¬Ò†ÈÀŠ-l0ù x|+ã_ï}ðÜŠ (eBÎâùjÇwËäˆ=§>}êc5eµiàþý8¸ö0ôždûvPîà|Wvæ¾6»é¶ß[åòV ýœƒ÷"èqy6÷_Œ­.€ð§ñwng®b‰ù/¶á¬'îœÿEÔY~å…kÈý•]Ó‹ñÐÇÓlŸŽfX¬à½H¤ŠN¾ìdí<”úTƒ›ž¿$£&hâ-éb×lIÖ18Ñ’*™am6ÿÏ®}f+/ø£1Ûyrn˜ :¶·É›+2lãŽõ#úGÕóYî!DÕ¬õÉš´L|5ñ8 øvìzü×ß9ÑD€:Î’Âgû‹@ëK¦<½ Åf ÀqW0ÝtN¤jòº¿bCð¬QŸ…'¦UcÅ£ÂúÉ…éU•§5yó­÷„œC \ÿšÑPÊ•—Ð~Ë·u¤bGƒÚj feƒ‡ucÒøàºŸÄýºû®äâêæk °r9ÖÆ0Øû¡™}j]âzçæ(–ð`-5ûcCKÒ¯Añß%˜Ñ`Î&ÙuãøQ‡QÄv‘'àWŸ [[_á÷"6¹-‚{¯áN-8ÎÁÑüe\^ ¶Í;<79†5;…§Ó‚ÝU¼{Ù”vIݦç€ÿ4e8}ÀÚï¬äÇÚ4ra–`B¦,õ\¢KÍC;ÁË&‘ㇶ$¤¼Ô®} û«Ãs›°gí{°|yˆÆ]ªÏ.¿ö4´/C¯|hâFÑð ¡~‚2[u–Ï»í?œ÷ ob­OšèŠPcxµ3š_ÑOݤ¨ÑioŒ’ÔÆ×¾7‰ÿãNjð)ÔöÖ@EX'~ú²…lyDl梋èF‘ùd{á ÞÁ5˸ Ó»!ú#‡Ý–t^ÊüÇø¨©|aÄÿOÉë⪆°?AÎLÝGS+ÒÃ"ɽ´½êZ6ºÌž‹ s³¤8—cÓ/:Úï6ý eñz§1@4 gyY¢o»Ìöô£ŠfÒ˜òÛµ„ØŠo¸Û æüHÅ,w ×ÑwR1pÂA°ù kMóãçâuÑý7«±ì°?mÞß 1G¶°PhðÜ7™ªÙÑê¼y8§*UK¡õF‡žvî‚m$—c¦ E}ŒŠéÐË™ÉÝ•´GçÇ»ÔòU‚ÌÉn:{pU‰ÊÑ7M‡ÃKù¾3@P¼S¶;Žà?:Ñ TŸËÁ‡2/ȉ™Ë-^ò f Ù€Ôò“襚Šóý‹°7a<úؼ'!ÿª/MüË}™/‰©;•ð‡Øy0˜¼ž‹L—ôK¸;ØŒô*â+¾·ÄyO´ Äh#}ý¥‹o; ›î¥ð&¨ÉÒô¾Sd”l>(ê|&ZUG kÇo^‰ÏM?ÔÇýWŸ¬ðTŒ]Î `wºGqÂùñt×NJT¹ó%OAë•k»ñ¾|8 ë'–ÓmYªX5oŠ rÉG·ÁµÙ¥³âx›È ŒØÓ ˶K²§¢1 ü®–´ÅÇã+Û{xáäÿú¿nñÂMßÈš AÐZ|ªþ¿~Ðgß¿€çSš1Kâ*Þ>;ó) ®Ã+‘>Å_‚û:ùÓ›p×%e0Üû%“£°A<»^úñÓ"0ùŒ®“×ã§ Œew?𳝻¾ãÁdž\«¢þ¸Ø ÖçÊñe¯^œ%òŒ˜ò›¼¬oCŸ^–¢K ”Y™f^·àuýqwöK˜6à3ì±jðÁÛŽ…ˆÐÂs§ yÌIèT¨Â{iêô€¿0ݪf‰SŽ'ƒûRÇás‘ãwœÍÞåXA¤EŽ;¿Vj²âCÁüµ3ïpÒÕFêMd ÍŽôÙ ©­þXtVŒ)t>"ù³±VF€I†ôŽn?y«“°ôQë/ÄÐ J»]!é]¬?»Ê_ᙑdÂb7Ê—½µ›ï@=¿6J$àùVSfÐ.ÌÖì/„rˆ¯dØ·Ž¥¬þ˜Õ¼<ŠFUêcè°=~¾`½Êšo¦ANxwð4'N¢É²„%_Ñõ|0\nK·]Ž«ó“Ðy’2[¬|óÎî…¨\ ø¨´ò§‰pÅ!0VÎMŒ†Ýñ±õq³ßà·½PäÌ~ÿGÿûnINåHÿ÷£í†œ«þÒ2*•[»ôæí™B+êÁqv:u r¡-®p`æhú^æéX±®þeÀ_ž`»1þ¾5–7ÞÝßÍÃmúQ‚Ͼ$i4`›K·âíp”èx@¾JîæÑ§Žæ6°üx:´h9’¤¤“h8M}ߌ†/z0'Ë’…¤ÿÁ¡‹Á¬¦dtìÎ { ]Yà›(ºm1‹8ŒfÚ]ïx¸“ZpÖgV~q dΑ£VŸ¢ÕÛkøÇa›òEJ•\cêœÖ­dxÀ¥öˆý¼—ŒX]Z:ôæòF‹sdýK3L¿´i1û ¹çAýãü$b¾;LoÈÓ•}QxìN¶Ÿ—ëû¹Ä3ã Q|¥F–/ _±½ÞšˆMå§Î\Ÿz죄 ¶®ì¬¹UDÏ=— /µ!°IzŒž@¥û0¡}´Sè¼ëæ2)¥AÔÕc Û ÍÆ9ƒDV –u|îÂ4’2 É*\rpªŠy+M[_¼ÁÑwH­î4òWÇýÇ}`úFnlçGn“O&ç}CŒi͈£gmòÑøèWÐ+^ ïg¬ã>~žÀf¶îÅ™ÑÓd8Žím×Ù„J£6lÉÿßô4ñô_[öÓ˜÷¾dŒ<ü÷Ùm )Q_Ÿr5tÀŽãûhÉöblêÁž ðµ–ðµÉÈŸÓ«œõöç Î$àŸ }ê¼*©>ôô.ÑêQS8€ò׸×jÔýD,4®Me½ýÐ6ÑY;M#í+OÁËCZTÕË…z—ÎGçx4µÊÅOøƒpM@‹FõƳ=ë?ã³åš8ÍÖØ»‹ôÏö…äí©HöÈð9|ØKsµç Ý/5öëí$^ [žJÒõ¶_ðJÔ‘øïUáÑø¸ÝG·.+^üD%˜Þ¯B­Vxáýòí0ÇÝKøë¿~äŒO*’¥Ó&ãùHø[&ÎEË!ËÒ8§!ÀÙƳî¢ÔƒÇØUóš@<ö1èu²¶É&`²ÿ L+PÀæ…ö˜sè7h§bCµ u|Q÷ì4Сs+?ás9Çañ1Ú’gÌ÷3ˆàÿ~fKkVKPÓòwøºt.oò°&\qft=-^xã÷©)äþ¶©œH­®Œ¾…r&7q…u#|‰óÃÅ) ÿ ,ïx“œQixêaÊ,þ_ÿ߯èµ8û!¼øItöC@™¬Ñ¡®ÓὙݡßó© ä2ݹu¶B±•Sèò‰eèùØ'Õ]ÄÌÇphÕ\3ƒÇŠßŸåUЇ_ÀÑX¸ÿ4¾vµg_úEYŠu•l¥c{Ḛð’IT,†Óƒ‰ø_oè#%ªë½ "$"h¨™¬Š+a®÷´è_ýìkäQövRÖóЩ2âÌÿ±(­‘¡ss'a’Þ bµ«•Êe? cb¼YY„Û±}ý¯~ZðI 4º1‚¿[þAøz¹ LÛƒáHÿBn‰÷~ÈZ±vlx„•WÝ1Ft]«&ÌuÞ»ÀFî÷46yn/·Å÷ rWæ™›gÂåÄï¨ôC—ZTC«Ñðç;8p)âWÚNÂôeéÿ‡UÆL$v î¶æš è–Oð–¡{w82ÅÍ头UžýànåL‡Aû¼ìL~/¥ó¬»P'þ#<ÉŒ­‚FÞ⪠˜¹¾,ë pö›˜PTF­$fAøÂñplIƧžp%Ö$£EéôÁ"ܸlžy·rB‡·²Iéé#üߦz?›“Á}î?8EŒOÄÆ`ä> g¦5¡Ð=YØ\ ÁvÒ[Tí®'êãF'Ûúáýeò¬ ‰ã[g1»%û¸¥÷¢©¾.³š,3ʸoKàÜôsH­äha@ùüð*¤ò·S3ÃR<*qŽ´&«>E¹6¢§HÕ­vã~ûfÐZQ@.áPÝê÷Àí7µ¥…ŸÖ‚uׂôP¶éŒ‡W6J8~Ø'úÃL!ÁNš/Tƒíq+>Ÿ­Å”¬dYúý'ïÜÀµˆ&ʉÁ¸gGl¿«NÛ後ð¿u8ê‡óê ‰cæ/@ûWXqG¿üÆ;ï@ét8<:b‰«ÛIÍt-¬ë^F…j…1wí}z`\äœÃ7róY–¸µSªXØ6FvLe‡Ò}Y©ýÊ|*Y»85^'ªtqì‰ Ò²9 s”ÌiØÌ5¬ËM•ýûíÊ–{áVhL'Råh'϶Luƒþ}rì̘2hÒR¦™ ”Ù²=áÂUÜè)Í‚[„˜Gh,ø7‘…|ÞAÓ.z±¾ -8e¾ kJZÇß¡Nv"Îùm{ü{¸²ð"pýÍ6:GáöÔüŠSmSYö£*“nw#ËÂò͆({L••ý¾Md#z°Ù@“ö§¼Ç®¡HºXMš{‘EýùT78š}1‡[y?…&²‹æÜd#úås6\nä+J¬­0ÁýIlì¡á8©NßµÜã—”ÓXñPþMOÚÇ¥á‹Â˜À¯ý´@碒§ºÒ‹eg†õÁ£á\å(™ù'©H‹â:Eq¦é£F5÷ Å»xæeh'v‘Ïb‹aUÌ®«6³û¡Îô ÒXã‹ó¤yáXº-e ½üóÑõÒ¬ŸÇîl‘ /õÿ×ÿNÀè'ü'lüåÙÅiÃq k!fUÓ hÿrUzM0ÖŠãVõ›[Ë;Çí£Ç³”h‚ÿ°nò¬ÆÆ>Z× NyB‹(Hì…moeXÍ }jýW‡iŸfM›Ø|!VÒ¬Ì\ sp›À2 ÎáÒ—çøïRôÑ5¯—Dÿ<„ïSqðñ°X°”ýu”…‘ß(ÍÕÂ’§ûÑ¢qnm¶¤Í“îÀƶ1˜og«“_à³ÐÍp)Ü‹#¾AYóyüaµ $NŠÐ~…süW{Æ@rì~gKüw^â¿J}Å-IÌÃnGuZ:ÅpDÿ"³Âxë_ø†‹„[^Qp 7wVoq.8½” œ¯Âô ûX¶t /ô®-}8ÍŽö ìŽ=2ÔãPsÕ`ú+Œý ß¥¢É£,ê—ž÷"wÐ×é“áÂÒRçºÛ‹]¡fq d^ ìŸë<:X!ÇV².4w$þ Jl‚• nxq[ò* Ñ<]_^ób9 §³ÕwìA÷– ë.v£’]m\Tû¾K>âa™ÕtÈjïUòpüý“odŸ†{„ýqÅN x÷_¦‰ [d¼ &†J/\3ùèoh©ÿŒ_Kù6£¾¢CúLÞF*Œq'»ìèý”÷¢íʰ÷m.†/BËÀ¨-OÃê¥,ü¾.;Ìÿ]÷JôRI·Ç_üD}w<—ï÷¿½²GfÈ,4¬ì×u®"%2„Ðh§¤%‰dÏ´(4„4(¼îs•ŒJ Ú¥R*4„Ö¯÷÷ñøùüáÛë¾_×uœsžçù¼ûÜ™¸ xe ð²ÿB6¦ï9ìöC¿™<“ÏØ-Ù5À‹KB÷äJrëì|<—‹úÒMx1oz™‰ÓcRZtªÛ/¾ÇÐAÞ¾yO¸-ƒ^ÜÑn,¾Ã½?3‚¯‰EK3ÔŒy Ôâ-f>&̉Ӣ ÛßÀ4m;VxœâÏ¡ê2’ ˜WÛʱI0]Õå_Hž%8¥tvyÿ¥ÝKyÞ¯´TÔÞhNÿ ì&£Ûg`Q†(}­êÈZ§ÎåGÊ:}¸/IW(Q½øŸäìVØg«Ã`[-±},‹áÚ_QÕw%_ùA Û?›ìÍPb—ßXÑu\™x¾WÔK©úk Ú*êLÜÛŠë]®`á¶¼iÇ?àÃOqšÄkb²0—i™¡·&x%fpûÎiL‘¥{´~’™(Çý/ñôqsºEß ¢£Ø¦‡‡¹›72pÌ+ Ú¹ìã2Å5xæb<>±5Œ4áAû/"Á×¢o”™ýR+¶ï"Ã?_op³6O`Î£ŽœñGðÏÃï.L ®%/î.Wgž*/¹cé6ø«3šK_“‚^sïÀ°p‚R* ÿÈçJ8ýð O†ý*X•p€¥î7¥Í¡V´ìùEÜüU—Kë²Ù¡»èÝ?øšV±ë&Låž&ËH¹ƒÒðlµ¦Ä‚8@ôíF.æÞö9ÁBì¤ý¶“Ù|î IåŒW{0ƒÓ§áÔxYv.d )“G;GÆ8 ¢S›ˆuQ4èn<Ë––k0Å^sj÷ä >ÏMŸ¢ÓÍ8úøæfÂë"–ûØÜ€GðBAˆž”X@?ë‘‘üÿoö²È—÷»¿šmžâˆõþ*°H~9Z$̆ˆOciŸÓ$¦ë‘OVDl£gôÄ™WÇF6OÔ†}–O¦Ö’òLnò(v¤Æ‡aÆ'×b\c%ÂÞ_@ûwW¥?Ïp~›¿!zO¤y äPþ<¶åÊK°ò²§"¶Wɽ=º” ¯ƒžwV²Î‚öM·g) ÐÔŠ<Ï™@£¤Â© M nÊÀf•þÙÅ$ô£(õ”±¥¯»bhEWÓÞ²‰û¹ šÿj¬;”-FñÕ$âqød³y*Æê•€/5Ž <üˆÏM‚8-ÁþgsëÁw÷¹Ia¼5Ù"Ô{í(>>›7=™üÐ_ ¹ã'›ޫÜ΋:”wÑ‚ù ŸçÅL˜Iל„…·ÔñÁk{²¨tóIêGëúpzjrš>Þ‘cíõʬpFü ãHQÁ€Ç{9½½žu“#¦@gsÚÎ Ç™os%‚ª¨ö& ÏØçp¿ã`î¾é8p¡C¾ñÐë׸º Ê_kSÉ“ËðWþ(úr`<=ú¹²­vbÆîrï HWžˆKgYbºç o°ÑnŸPA•<ؼÒmÿ8~õÏýµf°ÊLŒîüïý?§kQT<TX›Õr¾ð’Ü®þy­RÔôë '6Ø—ì RQK¹Oc ØØFe•ðWö›qű"lðf¹-ÎÂûWBeÍÈýXå ›q¯é%X ý—çdת1óã fÝ» U¬õ­œÑµE`­?ƒÙ÷Êü_¿óâj š°þ–㥠7¡a¾Úâ‰'¯áŸ{PSJ”áü^üÞ©D Á~Æ)‚Ǧâc¢ÏÒg&àÂi?ñb¸<ýÞ™¯šðlÃþÙS'aì\QvéI"û›³–¿×k# ¨"ÏcbquLÚHþ=¥AŒ7äÄר±ã‹Ã¸T™ûUæo·;°ïf£Ø áanyÔ¨šîÉ«œÿ;žEçñߎ–Ÿ“A¬ó»}“àF»ë ØÆÏÏœ|×^匹™ ©êúaL¸|Ä×ÀE7B託éT&hÿëEþzn:Ûq‘B=÷`‡Yô-šá¿‰#2§¸¹³§ü7oš¿%e üWc싰¸.ý1Áö®(öÇK |»ÅiôTEöG{%Ìž™QJt}ø8üoÎóí%oÑ‹tØät!6íiÙã.AßüŽ©_xIuá!¸´ÿzÍ¡¼f0nÛ‰Ñx¶,Œ-Δ#ή»±ç›Ît)Å…œÖÓ·`~, ,íÁÀ¬v4Õ¹JzDÊ@Ì1¯½ |Žw¿>!•°m*;‘¨H·Ê&áÄGpO¤v•eskbæáÌýeølì&àìqÉ© ”TõÛ¨‰tÌåéL0½ø© "ÁŸÈÍûhtîJ´\î]¾™ Š 1>ŸágÐCØpö8Á¤Ë8œ$Gk6.á ÑäK/ g/ª'³°§a°ê’ôd*=dw*«ë0»A}$ÿ¿ŽoàŽEÞ€‡9l“~×[Èà‡… ¸LVd·óú1zÝ^£Õ\ë­ƒ—-„ë&~V†Ù‰z¸óœ6óÖÅâ.Gúúȯ-+OîA•ïöœ¸êyØ—̽L»ñOãïÂT‹^øëªÌ‰ÌÀÁ§wˆs¥46=gÇ]TÀûÅl\'•‡Z.–PÎæÝ¹!‰Ë£²À¢¡s-̸+?†áõ:{0þüÿoŸÿõ2…9¼Ÿ¼£dÜ><¤Vðoí‹þ ¢.ʵ’»)½£¬e0HN—Ù½ºW¯¥òëׂ •¤AßÎaعS8Õ%y$ÿ§ t;‡ôq/“  ìól¶ºÄnö&ADÏüofòqõ$î†Ú!2Ã;Žû2MšMu€ÈÕ?¸”ø".{ÝÝÏí¾êe–kF΀C&G¸¼Úƒ?6RœÚ=½‰?™²®µÌ‹=„v¿„CoFìÕc0l €›Ÿ4 iÇQ°{0[­À·Y¡(#Ç”¨3Ü9)Ï-3½Oö JÓ˜ô«h ]hÿ¶8$“ Û÷¡Õ0&õ/×½ã;\U¨åþ~Û ýs×ãékS@ÿ­³¾fÎ]ç ¡êÇFPüÈõxû Yðe¢7¾ŽÜºÊó›s*.ÿäëÍæï°ÊÂë—·A¾k Þ8:ìx¾Œ?uÝ.þêºÔ½÷†|¡k™¯Ì!çÆ_ˆH=ÅmTºY¥jðß¼êòñ(HDéåå-¼ÿfM­kç ,ÔÇ3êQº® «gák¦?b¿kÓW’²ZŒ?ì„C¸Áa ”pjL"Û'HÃ>í 9I6n9Á{§nŒê³pÍò@þ…µ)pØæ1Ôº®‡âKamè>åÌT›šßæö|˜FŦЀÍuTКβà‰k8±¸ý±Øº]ýÓ^ãÝ}o‰™Ãsu&t‰z³E"4].JtC!ª`ÿmQ ܾf‚¨(‹wÏÁYï^ÂÅ´?p4GŒY¬¾3n¥ -hX£€:}ž}'í‚eÂt¢ð2OMú;f²½ wÑKO‰3”ÙƒYÊð ²øþO×Ì _>I–-·'êÝ0j×>ðJkG…°éèýZm{„|X¥Çüµ‚âô5ôÌÍ?áUÇô“¢›G¡2z'ûoÇ›~_u^šÆŒµ×»‡·É‚J·ÉÛR…p+vÎß“^Z…×VõgêShðÚˆ…ᓨVî neKœÕÚ‡wÒ'°¨ZE쇠ê#Lc´á 5ÍØÂŒ¹ܰá¾üS÷¢ ô"|„ú€Óë+y«NõâïÛ+@µÐ”-3ZãtƒÀ]¢WžUÙ%5h݇p¸û0±4ÞçÏð¡ZÅXÂü÷È&uó¿8ŠÓ×ÿþîáZn´eÒ›Ivõ^楸øüijË‚Âv=ºà‘Lrm#ß¼õáËMmêþì´0¢îõ¢¬ ðÿ„3=´¸á¾ܰ}'>˜P‰)ÑÌ87›;Qާ¿Ûr/·¢¹¹ w;5mNñFð¿vê’0o¹0¿Ç žÆ?×4^ìÏ€]‡„؇ZXl£Çí«ÈA¿¬Ç\@úeôIJžs)÷…§ýà"òÛiLX6~Ûý ³í„Àü½]n zÞÍ;P‰³ó4›qvTŸ‰}ho+÷ó('Í…î“!tºáAœTá@çjqhÌß_×0¾Þ½ug=?º0ÀAŠm® åÛ¬õÆYVZL:é)·òº2´¡m±!CãĘ¥³»çz—‰tÀ™Þ¹¤;b\ÎÍ„©o{¹e" ˆaƒ>S­Ó?ÇסâŠFxÞÙ3ÿ6º)Ä×Û¬¥m™™tÔG‹²3]õh}ÍšÅkÔûtÄîÑ1°hç P“¸.—>q'ârð€u±éwÿÈ@~‹o4è·Öÿæ;ŸtøÉI¤ßÆ)¿¹a•ˆRU²G Þ. À¼ïœ¦œxe5¿øÓ"t¸8šIrƒ„éF@xÚ#îŒ,ý MXcïʯ î7ÃG÷µ©§Ëqì<o¥ZÐÍÿ.Ó!M… uðßÚYû—‹.içÿî?„‹”Ó¬]Üð®ÅàAC_4iägi¦ìò·G3¶H?]O~äOâVNãÑiÒ¬áÝ5ÐQÔÆi öì·’‘Þ:4‚ÿ¾Ž¾8Å÷y¥´ýØ2þÖs+pÊ7hпÀ=Ói!£Ü%÷¿.Å”? æ¼ÑGŸ`;4à×é°eÖÅR¤s[eXä23Ø=Mš?ŸÎçû@Ü«/è]MòçÕ`ߨ,×ÒÇÎ!yÚ¾ðmœ®Z¾“Ô¿Ë…¡úl¸+‹{ãɯÈHLÐmF sçœ ¯[Õ18V‚¾{x"öÆeÃ;ÎÑ꿦±„ä[É•ODROˆÞû\þáZzZÙ¡w¦Ü‡³Ö¤âMè?Åù–J¨WùŽ_Ó]çðÙwÓxÿToú‚“ynTiÂI®VhУùOgfàüã\/ £ÇØÁ­Û5gá†.»9EO ráÀ+®¯s¶¿±"è-D}{ ã>Ë@õ¾ãÜ“SúôÄ•2 µ!CŠxMÆ¿>xT@ŠóÖñ–o«dG²pg„M¹kÏ6zCîø³& ¾åOgn9±i'V³‰³×²+ƒ©¨Û] ¬3›Ø.»†ò¡ è¬ýF°ÕBˆî[7†íø±ÚÐyÓëÎ~ —¶a˯Îu¥ÚWÓáò­UìË^øœ7Œ»jÉ…Ypÿp |¼|r$þEL»ùßW(CÚ%}`gB¯.=‹Ÿÿå´CDZe-Ëðá´O¸b…·Ü¹TÜVaš}ªhÿÀÂÛuo eß%`Û†ÉL¸h;'#:'‚Óq¢ÓV˜ºëú¾ÊÇyñãÀêjÞ·&WŒ uÜL楜_Wñ œå±>Û‹à»ñìŠ×ãNL—cKÛ[¸¡¿Þ´î` y^ BkJD™Sè1lÜ©BWŒ¢ïß¶#éô»ÌZî«v'©’ÇRXÃü¶3Œ©-EM œCà©ÌQx¢{g©1Ž déá×—ÿ>»<äèÓÄ‘=ú#þïžÔÇÝè CaëÅìïâNìñ¦qKòÐéÛ)ÞP»Œ¶:IÖk¨²H·ëÐØƒÓãï€Úr†ý=Ƹ¸²ŠO/ß»ãaEâ\ÈB$*’LiúAH:¨ÇÆiÓ‹a!'AuÏ«AÒ‡x4sg¶•ß Y½ $®X×E,ìÍnqÃQîÈvyœÐ¿„ÚÃ@ã5ÐÜ ËøNPPtËn*2»-8(½6Š_'ë„=©‡H÷Òi% ä~çÐì,y•‡SÓdÐËF–I—óyó…Àïú+ü¨ü3\k›cøýTëû˜û$&pj1·ÿë[þoNòz9y:ê,vŸM…ýüñGˆ€ÒCœüë2ãD ¹)Yn ö4Ï‹`êCÌ¿ŸJrèh.´¹³ShƒÄ3JTà¢,9yh¹½&š¨ïDõÒPR\KÞ[ÓÝf¾tëÜ×(½FŒÑÊn˜†ù;l¨”q&FGçðNhÂâúJ²½5*ñ£sCãÁ/F‡Å5aT‚Ž«zDn ä‚ôêé°Äc7U7—§[7‚N¹c búX¨öc·Eà‡Çªtˆª²¢â¬ ~Ž’ÛKA`ÿI´& ØÜbÌXnÎ}ä”i¿Þ ¼ ó½µhðÏUlÏ™QX^ÕÈýÚ§ÈN>¤™ïe9Á;›Ð%<^ˆ±§ 5 õ¢©îÝd9æ'ÑŽbYØ¿B€ï‹ã\.Á²µºt߬5KŸ´²> ‰Þ”XMèùOó˜³ÿzfŽÊjtZôdþ~RŽÇŸ,{B)pÍå6gì:È?xT¶™Ã&ФãfVñ¶È>%±v("KUd,¡+CwO¦×uuDÿŸ9rŒLo†žÝ%2¬÷°D˜{.%ÄL¦§Àí¢ôùéØR‰ñr=¸ÉíjiH°Í[o×rËbù-ûÿëe&>²qâ óâÞå¶q—i¡Çp#œ• åd_µCúá`\¹(‡<ãïƒ ÞTÑ/“ÛTŽùQàÞ±„ºÃ6ØtzÜ6Õѽ'¡z§<—^âfV£Ÿ_]¶d; jŠÑ¸ájˆÞƒ/7ã­£ÓˆEÌ.hЂø8:¼à6:,äo¿6‘–e¬¡‡¬´h˜xœ6Êàë›ç£íñ,ògÕÿú+1³Pìe 0Ñ ™* LÚ²?ÁXêù8¶Ç„¶ éЛb½ø25®îdü8:°W”^?™ UÚ?±í’*±œŸŠ¿^kÒûa̘f¢"I£B‰xãw|x“éEÆ29·µtJÂøuZž’ûñX~4z}ÙDlxÿe¢Ý~ ›}Íh˜©0»xöºd‡A‡¢:Sè’¡6·¯Â;Ënìµ7Í…ÑL4+ƒ6m`–7Óð‘Q-×!ÏAÈÒ.ë‹1t轄ëëÊ1||¯_]“ý³C‡xzïœ?=—1à°¯¹àßZÁ¼{­!tŒY)«'Pðù˜—0÷åоã#nS8H…wŽÔÇÝ…xi¼Ysi:?áè®o&ä˜åAë‘f®Lß·qÅc}‰“K.: e9¿_ú‹Ò1U{¸ÙGd™zæ.¾K—( “dü#Õ$â“¥£2ðÝßZ%&FC×hÃÆÁù½è ì/oðÝîã¨Kþi©v´á*š;xÛóˆã¸û8ñO)Ö‡h²QדIy„)qÍáŠwžC‡?©øÐ)ÕòS1A+#äâ´Dut2éÂT­…d{Ἔ׊ÕѤàÈ?žœ®€­…Št[¹ñæžÀPã-ð ÿ…?÷ªÔ¿”¬pHåO =מÀ’=¢œØïÜ·±[á“ÄVyK_ŸBS¦ÖA£¹{–¢¿r×>Ç¢IñxÌVn¦Áëq•@i{¥È¿ðò ´ÇöðÔ¯ô‰A ÷ä…hÐYO>còàsB'|ÿŸþ{®©¶}`óMúìsÅU†«ÐqìźáËÈü­ê´óÃan{²s¸‰e»æÓ²™¹ÿÍrfó¿}å„ÊtðÞKavw/‚˜Ö\ü°õ8ó›GÀzn5øîáû»_…•Fi,êN üð\ÁÖñŽÂ©}SèÏûèc¿1¬8?ŠY¹ ±óB.ÔÈQˆe\K¤O]gÑÁ•3Ð],˜ ‹o“uqó f}WœL^6™ÞøköÍ%פQJg1wZ/²šcÿÛ*6³:”½è:kÒeh´õøõlN±›Œ½V»èüë æ Æïëz‚›2Òÿ<»è4)»ÜÍÛ”#Lm$ÇPÿÖ\Ž:û<{%º)å‘ ÷ nÔ .‹Œ…»Vn@é òôÓ:>©|s„\ëèN.j’sMÚlÁ”Mœó¼"Ø© …r7 ß’^þ‹Ð4¸øª”äžž‰î_aÌ$Eºdµ-{wa¯â›DbÄ ½Š¤ÉúÆÄJZŠ áüµ2‘¾L‰"ŽÛÕX¹«:þ”ö„³pÙÕÛ<…Gãq[ÑFØ{w!ë±[1úSéºV¸òö¾8Ã5ÏO&G«.rÛçâ–ü§seg¯ré‹-éÎ#NìÕºlž€îþÌ?ÅÉEÅà!Û¯<©gjtgÎI,-FµãR‹9ìÖ‰ ŒÜ¸“)½Z 'Œ”‰Íû)T¶¦To<$H>Œyúâ2±œVršÅà÷FUËdÐþ׿óy¬³¾ë ¶’UhÎNä8É…|G½$ ‡z1Ù¯ 6ŠZ°ëáÛÍ÷௫&\W=ˆ¢2rì:¿ßeÎdýgsƒ·Ø3ÃAhž–NOÎe›Ý4@áÃW®:ø*H˜ÒÒø¯pySŸ9Žo)íÄzÔSC‹Ð<ŸD÷(ªÂC[-È p$þ¹³IeÔÿÞÿ.°)³Š‹¡ó‰'½¼ûe¹¼G¡úÂéž’H½îÚrßÊ Ánå^à’é¦bÍ»ã`¾úø3œ!ZÒòÚøÜèñ/ˆ-ÕgÂæNìP­ëØ<‘Í1vF¯ôb8¨G“ß6ᵫ°ê1b­¢ ´ŒßÇ¹ØÆB•¾"ýÜ»ʸ›(œûŒ(üýNΗðXbÍ0~<¾ËéÀ«“P¾©Œ“i±)SYóó&œÈ;‹UÏ?‘Þ_²Pü’ƒÇ}àyá"L{ßÂy~>Í"GÓ(Gè(À°7q­ˆ¬ú®N]kxÇÖê±ÿ˜mfî½€þgó£7æ0±yádEe;Ïn„®F·6zí'°ügŸæ»o¸)¬+Ô|Qù² KükÖ5N¸fñ*"˜ë‰_æ\Å{÷1Ì}6D½³fO꣨zF7| ô…)úБìÀg´,XÚ‚Ot¼!ÙÃ]QXyö ¬ÆkŽlé&Þ"ùØ*} ã^' I˜/£+7@âŽ÷£·•<™W 9vº½5´b¼tu>ºðPfóB˜N°þ…“•ÃÙÀ’&:œï$·í.Ùõm7 ý)J¯dÕâZµÇ#üOê­)ü o'W\¥`ÓæTÏ„¥Öâl`Àž™‹HÑÕ7OHå‚Çâ§ÜÍÅ:xß7ש»}7öžâ2zvÑ&ÍK \rúã᎙9M·8ã”_àÌ‚t\¤¨A¤ÿ&“$ÁOÐ2ôK|ĸ“×8)Ï•xªÅ ˆxÔ€R{DÑ:> –Žî }"Öøëüt}äJǦ<€I5s¹ÏDùr6…Ð$¦v/ŽË2=rYÃ’zÎpc~O!UIç`—C§SМõè,¬‰YwöâãK8¨ïŸÙrÞ_J‰¶üx¶óŒ‹?ý†´»½æŽw`þó£DXm¶‰Š2Õ·æTâv>W»Ë”ɦ°Õ!r®2+Û Ý3ÈþiŠ´|Õ2¸ÑfHoÞÝÅ—¯¢WOyûÈ/¸z9d$Žs'sSDeqaïRH²P‚ó~‹qîP‹Éù8bÿ竱D÷ÐZ—k$ƒËpBQ7‰QÑX*S1õN쾬9è âB¥7ûjGæ÷r[Ç¿ŽÊol@ãU"êr:Ð=Q–†ÚXRA}+ºÎ«¿m¬»=£©‘ÊRê7Ø…2Ç4Øc» ín'wzcá[ívõÒNÈ•aF%Ôg…4B3ùì/‚ï#Þùñ’„ž%Ãj¦üÒ½øMÁB­Æ€{{”ÓÙ[ŒÓhÚŠÊõ¤õX*Õ½ó£ov²Â PÞ G»#'ÞðeVwbsþu°,§w<3PÃ@ÎZëÐj¹±#õëcâV|;åeXÉê%lâ+?˜Ár˜`à È©‰¼«È"'Oēۚ±t^ ·]_¥n•5צe÷}#š¼ãlÑ6)¾¦ ÃéåuX·JŒU$ëPë–‡èVƒá ÷±]‚å´M¡ Z;cXeÐMûx%$·ÌÇy«ç2¥ñK™Æüº§¿ nd“D ©Cu ’—£˜•d<录ƂÄå0Ú³˜6ÌÝÉNmudi_•Ù»þÔ´ÿ&âÄ|êX+øú>·ÁZWÚsäYIZ5 †ù³½Wmx{o§y åpâˆ.[EαÝþ—ÿY£ƒ¹çÑ39þFÖ?z.]Ô¤O¼Ž¦Àì¡)öÇv_“f7Öðh‰!¥Í‘TòØ äîÅPwÍL¶>röñ0Æð-¼õšëÞ^ˆ¿®¹Ð!–Ç»ñæ½µý ¦ ¬ÏÎGl-cg½ý—ó‘ß÷{í¤3Œ×QfOßìM §p‡é'l4x_—€eÊTâȌŕÌn… ¬^¨IÝ´Sà±^‹=ŸÊþâ3ÃÌ­°¿í8XmSeA™ Ù© t^ö µ ŽÓ‡79!t Qš+ò™ŒÇ ÀâO{˜â]=üQ¯0¢ÿÆe{à„ÇoPÈÍôÄĘÁÑZbøû `_6ðÔh•r.ÑJáyj»2ÉÛà/tzwl/å*dðdéë8Šw¦m„Š¡2”Šë­ëAòv%çW Ç*Kˆoi8+›Ñ¹‰Y¨)dAEü&²7‰4ýænYñe.8\›ý˜¬óR·± „Ù q¬h¯ šÿ5§.ÀæŸÆ,sQÎõòãݾiË7œ‚µ>:€Ëƒv€*]|õ%¨æHÒ8­ü–اDÎ +_òw.Ø…åÆÝxj½íÐm€Àjs<î;Çÿœ8Òÿžy1Šë{Â# )›¨ú—gðn€»s2XFÜW°^ßk° ¯^Kæ6Öc¾²9ìž”À¥ø j¾ð+›ÁÃý5ºÞ,ÀMuÞPßÍAÆiÖµg-^ÿàQ> ôÄýÑ:,ÀÈhf¸¶gÿcE“ËQÿé_¼æ…ß'p‚–~8Þx(hÆÆ?јÁî±ì›<º8jÒ¡£ øeÅ9°6äRªËá[â(šóÄ‘Ë nì´€ÑÀÕÙ˜®I\NZÈ4Ý{°¥¹+uë„ÄÌNÜñõ2Ê%QVbIçeÿoþ_™j%9Ô[ŠºV‘´ãc,qMa¿Lcç—ÕÐ3_öƒ×ìû¤Á. ßþõ¡+åä骊,èq4œkEÏα¦c'øÒUº@.;œñåêpÑìgÜ™1$ýëßÔA†‹ éB˜ÆÎñGõ¦ ºÆBü[ÚÕpZضÆýœË6Mï6Þ*q¦[Jö‘@_q˜jÔŸް—öç¹ä’BN@Ó…—:¦,Vד„‡ø} !»¾Àµ¦Dö7ø$홺ž L_Ä2ƒY®ë ®«BƒÀÆÐ3ïÎó¶n8½¾~xéÒzhž9¢ÿŽM¾Gv/¼®¦q¨Ó?Mu·§NXЊu£è•ÚÔC«ŒFV²‹ëhsˆf¯±#·DRYp-î¤qYÞƒ ¿Û›Å^í`›VæRëfà7‡Ö‡ýV?ñgÆNLÎ5Ìa"%ÖìHæZ-} §Ù0µú#œ[~=Š”¹0É}ÉtÛfÞÊÈzÜõQ77¼<G†[¡Iò™ôø8 +Åîk«¨óÖ(®±°z¦µ!dz?o^À¸¤gàj^Lol`£ÿéçÎaqV+W2bÿò [eqÃ&SÞóD X|;)ÓkÁîîÙ Ž)OzΤìœ]l¹²b †kYcû0§–TÉÓùµŠ[fƒÛ»ZðÙå8oPަœ7¦ž›ä8gË…`&yž”O.ãd—_DZ¯à·UzHS‡iÑkÑU¹Ō͙_¸ y™>n)€-×YHÙ,ÎðÅ?-e*ÏD/;“ÒˆÑìÏxg§X‚=ògH{åš©1ŽvšhÐ.ëøCà,¼åýÆFYQºâ"2í64És±±çÈÝã€sWΆ##õ)ÙÒ=âpÊÁ3ÐtÐtÍ`ë—‹-ÿ빘Bž =ãJ?ôâ v7ÔÎ6rªV CDÀí…jŒí@+™s°eçx²ï˜»6ã.fÚÊ‚q’þÈ\ˬƷÀ¥`rlÛM¢¯YÊ·g.[¾‚Åç”hÍïŒ8¶¾©â<“ÿÕ*I¼EI2fïs%ýÓ‚võ^ìý{šWµØkÿü^Š[3ãqÕ­sPöp ®xÓ_'³ Ú,÷ẒôD…)ö[ZÂcE>ÜKh„°ƒÝÄY?¤÷©ÀŽËs`Úe¨4£gwTàÒcS©ÝG,: $w,œ´eªÚÉ®ÄEÄD4 s]öa±Š(ÜÕ²a°BÞU£®Ó¹%®-u“Ùâ¸ð{á’zû"އ ppÞ^ Kþá*ÞÝ‹¥¢é#ñï.N—Ÿp?öO‡Ú󓡨<~9{„Mmq¥^b’x#¬½mX™ö~(3 ËZqÿÉ"ð¿5·V÷C‡fÔ=OÇšI±-±ÚôæË7Øzs/Éß°‰è~MCoM68oz|9…5‚ÑTqª+•õÎD¿9<]æ@Gývfc—:P÷³9`•Ã-ó9F|ߺӇ›Ò±£'8Õ±yþwÙìÍÿ»ÿ›¶¿t{kÜ-8ûø9þû”sn 2þ .VåÎÉøÑåE µwx±ù3* ÝR<¦•Ñ€»ÕôýÕ"¬Jã~m?@æi‘QÚ?pØÍ‡ùöâÔÄíð0M¯>ëE™¾RÕF _äâ|±ZÔ5ÏâÂJ¿ÃÏœKœGƒ}Ø”‹*ùîtŒ÷zn½ 7ôR™Ûé]¬Ov5 ‡ lŠ­§.±jÐþ` 7Íê>ΞN—Yþ央¥Ñ©“>B¸‚«”5ÆÃí˜reÔo:Ã:8ÅÐa»ü©‘X8a4Ó1æðádæ¾jáˆý/kîq•$±:W„ÆïûÆmÈö£‘‚°¼o´üÕ‚7÷…X’?ØÔ?Ç !vc±‹&U‚kàëñ•¬×¼h£•Oäéô6xzQ“ÝH´Mxjy0¬ï1a[&îÆ—™`;„ŠÏð‡­ ÜŸÙ—M7BжJY¹ðû‰[!†[ oaÓmGnÕZ ¿þÜ'ÙâÌénª€¡-N,Iu‡çÆ@‡C–„L§ÞGŸB|‘ì߉MÆ›É)­4ÈVÕ„…kç±¢c{a å<÷w@aè>\ޣÊRþwÿ³cÔ4nº¹dÕ™“Ñz×4ƒác'¸B/Ö».UªÏ%×ÇNb^Ò“™‘ãuܸå#N¼8¿ÿhv\âãJfè…X\kßH…_DÛ ™ÇÞ5ç [½½X›‹]Sp…¼ÈÕjµµÏ¥)6θ1…AR—“ eÙš`‘h‡éçïqòÊ9é(´Q\ɼ5¥‡ 3‰_ûmø0Oîžr†'Jš´&£¶Ä$’Ããizw‚L ÙkŽ0Õ ×Èûã±p^¬¤[CeòXšüA”†DÏ+<ýo³FôOv‰txP\u;#EMýkZ0M«8°©‚8V±¯¿´Ù1\2A¾¼ŸÃŠöwòîöä¢Á††Ž¨da‚Ï|¢x°<ÝFÑMÀJÞŒáÇÄTóå_‹‘<ÝЗû ëTºñ8Ï€ºýwzW7”(H¤gý‚ø m¼¯ËÆ“sÍŸ0«ùì쥷ÖUCoP|§EB•žù×»y²—~jxŒØ‡ÖÂï4¦=»îÔvá¬G¯Qó’Ö´kÆŒz”€ÇíÈp@–´L î[—cáõï2¼>=ž—êGü?ñÒ'°\îÎ?-Ãc¶Ÿ¤¨äDƽØ7ÊܸeMÄÒ¢…ÅFuóo b¤Í=Hþ ǵüä¸ÙèeGy|¦©IÚîÌ&_ö7ò¸M»áÏœsWoK$—`WR"“Ë*»Òôú»ãÿðÜ…=ØúO5!oÙmrF@&…8‘½)TÓF”º+ç–ï’¢!æfxï»ûôR†7GNŽmûEy¢¡‰¼q7³`5æ‰ÐyZƒ^T–qœ¥¦L$÷ç åGê6æ.ù9 Âöž]CÜ®„¬n üèñ›·–2?ÆÉØæB€¦DUèŸû×ó±iâBظ|œ<|[L‹AKö8§Ü¯BooÔ¤3&².3Vsÿ ?5ÀæŸv,…­Óágq!² îØ!ÞbɃ\ñ^+(‹€·î°Yè&ÜX£D‹=èÂw­¼ŽSЋ'Ã…ìÜ M®q¨d¼“p‹ ‚áᙟ°âh.×fu ^¹ÃhÙ{\nÆg.wQ)nŸ«×ágë‚Óðâ7Kã7žU}6…\»4Ðýî¿_ÂÉ@ìL~MòÄ4Ùã;ªò¾K–4µ€së*~òÉÎÐq¬þõ?ˆÞ&}õ!#þÿ$¾סÁn Lœ«ÇªŽÇ/ˆÔ‰·¸ð ._lÅ1v“¨ÑY»Íÿq§;Ðì¡ •ö‚’ë "«¹edãã›Ù³±nï¨,Îçš,­˜ƒ®!W=;£cLt¾9ÒÆñã^r‚‘8VI^\RýûObÑè©Ä'D”ý÷ÓòâZœìÔüµÇ?zNÖc+· sëSÄ3M±ÜJ¯³dÉÖ2<@¥(ÿÈUx)ÙB³&òÂÃq9|cÎô´ óø‡o÷ Àçsù¸ð²Ô.=K-–â:4XÜŸ“P5;oÄ~ç#ÕÜþ8Οg#F‘%wÆÀàJ¼ŸûSÆsÓµg°Ù'üqÁã .ÂÊ‰Š¾Ðg"ä‹w.äéW“°S·0:OãüÁ\û´º‚Š7&rò±:vN†mVÑ!÷»dæ¡mdÕ[¶Ëa>¹l¹“?,R„³$Ø[+q–êP ;ÿÆ“SUÎ`?ë–Ó~ ˜p%W½ÿN¶ÊáÛ¯d~f=¸GHï†,mî0„üË“èöñ%è´I›ÿÚøT÷_'%A÷aéªâç$Š;¶óÈ‹ºYœí¥ÝuS† t©šSSùþÒ¥Æï2Fâ?¥j —q/ Ÿ•=Ç€1ÇÁ~HŒéÁ3ø{o4¤vO‚uÆê,ö_œMì’f~æ¢ËB}\š.LG}qy¾+§ÂÔD®âµi"l}Œ3n*á\÷rk~ìǶ …~lGY©ýÌ#¥› ß ¥PÙv g¿ñ§ºýñ\÷MRky„ ’ O$Éâù»à۟똛"@‡ {¹ '˜¢›'¨ýÂ5ߌÇû)‘ ÷µã§Ò¯^‚°Æ#…M &‹$)ç¬V†·­C1&P‰9JоÞýê– B¡¹α´ O§ËÀîçL(Prÿ»,®cÏjí'ÀrÀ·Ï®¼~ÞÙâƒÅ"qî†wÜ‘yíðÈüœýv ]WörYïßñÿÌ£MZœ¶·©2§}}`p)›«/ÀBÚÑ@»Hºae8%BܱI•*»•¿€.,x‡ÿÛ|7fÏtàßwdš[ôDM¦ñƒõ³ý aÃÂ^Ê1ÏœÓÜñ›¿8½ç5’ ïaSí.P¬¬‡…`ûôi\×ì3Ðqê#ÆDnÖÞ‘û_fa–(üs ¼žKy6Jp0Ç—¥¥¨ÒÛ¹0Þç&÷íEwß:‹E¨¯t€§ºõ+ÉÚ¸R—}†1jãáŒØDÆ®D/™.gZsEéÒ` 8±í s¾;´;€¿#¹Ü‹’`à %vaU itÖÅé:»ˆM<=ÅÑ÷ öqôªÀ=ô½õ»è–²ë8~8FÝ?¶+ø¯„ !kc+¾j¯‡Ðšp©Ë {ÒŽãÃݧQðìxv©%‚‹©<ª/ºGâ¿®º­œWœ„ñêˆ']Ç%e?…Éä ïÆèçv8vo:œ2)ø&d ’1¥¸²'–ìºL¡lÓOØùEžÎù1‰<üž€§wš2"Ìä½ ×¹‡Û÷`º!œÓÛß³ßs©Í×¹C%—ȃ²r Ñ¬í¥+ÞeºrÜx®)Æ ]e`vi8ø5­ÇpGMˆh™IÊÓ€³ã[¨Ü*ÀÄ2rÉä)’øuyÛë†aϱA£RÚøq@ˆFÅ%BF`÷Í6”.NÀÕ2Ïêú®m뜱Të€ >V¾6‚·Ù¢¯µ5QõjÁ7?/A°” nõ؃{µ¯r¦3ySªÛˆkÿSÜ>w&•ì Ÿ@">-~¥×ï&Òæe Z÷Gq«O {­°øG-8>ÞiÓ:ìÕlð«sÙ"Û?dP¨ž,øÇßVDçý Ør±6ÉVá2å˜Î‹Ç€Ÿçñû ø¼£‹Œ¯“ak:˜ö`|ƒ…³ëðæVOTWUbG¢Žu7Gá¢w!ÂË ¢OúÃ’µÙ¨íYÏŸôr+y_€Åvg1Ëï*·pëMø™P‚?¿O‚k§îñWó¹ý"#úožûNGVõ¿ºŠOOóÈñ¥¼GÉFë ZrÕǪ‡àckÍèöSi ø:4Æ”h]úvÏkŒpUƒþ*/ÆTei/Ѧ%Çáí/CzJ`W5?Ó.YO®›—sëwp·ÇÁ`áæ?sf%óé¶üüu82k všÇô³$Ù­/áQé2HÉ3fiЧÆ]XP-EÞF+Иؿ ÕðMêb£˜-ýö£ë(Ü`¾êòtl·¶Ïëæ¶Ù±Ž”çü„3óYžvýõr<§Æ,3ò¸z×$TËnÁ¿ñ-«PÝþT]ýš¸º7þ‚kÂQ®ðò^L­$\¥^16ˆ 0îțPAÚCé.ÏP æ¨c†P8μ,ƒÎuvpÞô\ÙhAO%͆oGVà`öE¾×ZÆS7ѧüǹ(œêI?¿ÜÉ™hœÇØövN2ü[]¢“ NÚ†K#ÍÉœžlLõ¬¡{ øi=«H¶9Ÿó<¨CC¾n†Ëõ{¸½OëØ…×DûµNSùŽéÁ¨²•‹Y˜ ž6JŒÄž%}==ðÃ0¾œ­€MQEÜꢙ̭ÿ*¶¸œÃøÆ""~ín›$N_ÿßû/Â~qãMWbã6W¶A¸&—CYfLhíC¯¼Ãg3±Í$ 5.½†Ü®`¹ÞŸš²§,u†¿/$¸?iÒ ÷8©æs\þl>ÕÖEm_³|†³UCðV@'~PEµð±ÿaX§wB•Š83ÿª„‰& Úo"=³Èlÿl´þ¦Ê"rwHÓ_ŸcѨ|øÚ¶·/ëR`?¤ÃÔ°» Äí„þ{ÓiÐY–ú`4ó›QŠúÎçá[až=Ø[ÝDrvò.0W gñ²½ø}êl”Ö¤dª5Ó2ÂÙÙãFì—¨ë&sSšÀòÀfð±e_càòtú+f,™ØJ^ 2]—½ÔC<M 7@È#´~kD<¹‹º4ØQ©Ïø~ò'øÃö´z_}<ÞŒ»Ìuݵð\qÌë¡j›"1“¦ 703ÿ |øW77¼^Wr1)R,àÊS¾†Á'{ù/º8™ÒÂõKè¤ClìçN~ê—MäârwÚøÁ™_âbËR·)@Ò> Xhÿþí—Šûö8]Çï²Æ¿gÕùgº´Xyé;8éX ÕgAãE+þžS'šPÜ:ˆ9v Ô¿[OóÆ­‚òŒp‰{ÅÍ”eNS4ئ«8×[q¸#z1ép¨æ¨}å…¹AzŸ)9ô@¡#•H׿À%aúõŽ7>nÉÁs<øõ‰sh ù‘3¥[ÈóQùøï®/­,VBé{È]mOÏë`鼃³)·ÂíSkðlÚܪÑÇ—Òö€ç©Ðü+œf9ÐEïózc"tݬ#aî±o¨9K„ó°AŸÕ3y—ӡåUÆžÕÆI§î_©rÕG !Vrè3,0&³s¹½×l -^‚ÄÏ'×â¾/“Gð¿Eì ¸ù†öJ5*.> ûFÝ€»Éx~»:ÛÙv•;ãw¶‹åýêBL<¢Á ¯Æ"ü‚TüTY‰S?¢;æ¶‘¿û³ñ˜s8gðê,’8…Bôqå iR—4¿,‰‹ûÛÛœ¨óÑ&àŸ1@­Z,0B••Ï’%Ežwqª¿*šZÆ€Ýf[ê3Õ„íÊNÇ÷I+á빸3ðúU)V¥ëWkŠÈ×wªxêÙ%®»@ ¼½Í럀s'+ÐE1÷aû 4æýœ<‹Š¼&i’¢`ûkz}j€´™Èeu³“FîÿüN}Ë‹˜_È5/t§§ÜÆÇ½,cWЧº± 3¡vŽ<8êDÍ…dèƒuã¨ÎöÜÖÙŠø~U U; bf¾GÕ&Ðú1Š•¦EÐÒ¦u¥}iÜïÝ tÉ’¯ gv¢N5ã§Ž"ò°ù䊥{l>aùcvïdÎûÔÈÙ|wo+“ÂáÕé3Èõ=çØŸ BìÀ´2øv´’šQÁñ¸R`ßÌc¤¿‹o¤3û,vÆ!‚:,3­{3çÆŸqaý¦©,»Kˆ=üqÈêKÒ˾¡lµ{>]8q3g;),Wð¬ƒÿ÷üCXàe,8ÕÃ{RÈÁ­A'úêålŒôV åUxõèõ««iÓ]Yrw€aºX¬ÖƒÞ×'·à:é)8O¤D˜ƒ« =YVÍ¡û`P„yïöðÓ/Åãg)û¢Ët‹æ¸Ãñ£QÅ'nåÛ`Ü_AéÞü3DË¡ÂBº7вÝ”@Ññª§°u~Wq–x+ôÿ™K]—3Τ°îëªÛðâk5¾Ô9Îõ]™IêZ§Ñ‹nÿö›…SfÝ¿‚y•FÌ2`˜Xžh#™>fp_Ù—ç×ÞLr3Ìp–QQk[Qî§FêŸhG+F~?÷bÆà6ÙÑ`8æ>—·²kØÔ'Ól©¼Aøw¿"JW¥iŽ@ÿÈ’f¾žš!-þ©A#3&×¹Ó•ih!±^ofKõØîy6l]},+Ȥ_½Â蛉x°;Šðêõ¸Vñ Ø$%ÌYê1w΄*®ÊÉï޷óXå¯^4»9—=œ°Š¾Ñbܳ!’­[%àÅÀX–`,NïÄÇã÷–÷¸±f3÷U]~‚ê͘Qt(œVþBx:–œÖá5uÛóÄèµïN¨–sÞµ.‚å¯?ñ fX>»·N0bÞ‰Ùøm?lãDfÐÑÒòL¬D^weò/¼éßç>Œ˜Ä$…EÔO- ß}ýˆã ð€³µð¸†¿rÿ@Ã:‡:ñ3¢ôõ¸Fþ̦+dö1{ª½{?çZ¾_½Ø ê§•0;}=M0Lƒ]¼F,k”¥æ>¡ÊñwpZ$¹·€ìMÔ.ÿ2f.Z}tÂgáÜ)QHyÍßo¹ƒ¦„¤e£àР!K8sžKäòQ&>Ÿéôñ¾DAvk"›“˾™'[uþ.–b_ŽdÃÂúÌíO-N´o…– S¡8ãØJ§àPÄy,“¢À)8˜C‘çnGãî[ƒÇ3Q*êuš…jé2¹u7aÖO)–•G ý±yßJ¯Ða‡üMÀÊi!kù™†éYÅN˜èŽ;ÖzÑc1£ÁyùVV!ÒÅ~s‚=ú@·ÚHRÅË¿Pû`6É{>‘:æw‰Ì¯v ýÓoÃí¹aÄŸ*š ¶ªŠì‚©=ÿg ]þí·^í •IJöïlÂR]ìúI_ŠŠ³ÜË;`éýëèëC}ãž¡ÌǬ¿Ýžn}QÏŒ‚™µ†tÿðÔŒÙÇ.ü.ŨÐf˜óÉŒN¹©@·ÆûØ?YsFZk¡\r´\¾‰Espk_Ñe?É«éÞðvœ•ÜŸÎE™×…¸bß)sÂ8ͽ<ñUÎ4½J–Îþ(A—­îÛnLàÕÒÙ^ÊBôæO~Zy'¬ŒÛŠ%&¶à¸'ÏÊÁ)òôœL+JÌ}A<«ÓˆLÕDòâçæB!óÃÖüÒ‡²ß{‰¢“.Xës êîøÞû-HûIêÏíãN ¡ý­4ÞߢAòd åMþ9†2?#ê哺­3;]cÁäv89 kÂ%¼QØŠ‡†ÈÎÊîÇ­Îì,µfÇÈ‹K ÈÍÔI¨F·q¤\åOÎÅø/Èõ½ïq³´=;jmß® qÖ·Iô+u\pÞ ý>ÜÄZß;äib.7¥ÇÞÐ|ˆìiGõËøE÷3÷€ÌîÏAmƒQlãl?¼rÈ…³v¾ qç P8‚¶XÂÇ3{8Ác^ày <…Fì—`…ûNâSA:úa®ï,çi'©†<Œ3î%«vtàú 02öoνù4ÄÆðnn`h–ÅÙ_€DëRæSZŠí8³½“¨W™ Ø­¼^óÍiª‰ =µ’NÙnI}Ý`(O›ž~s´ŽY£CP 6É3é‰ìUÓZ±zžaÚf *,2•ÿ8NcÖl…sKµ(çmÂ.ÝáðÌzKjæ–¾/ ¿á Ùìë…Æ·äé<nßêïØwu2IéU„€ü(ŒÜ.Fͽå¨õÔBh[7šŠ/œDûì&¡ïFd[Ø_sHnÕÇ"ƒèqoÊå©áŵþt‚x#|[/̵›f¢÷übP·ÒÅM‡ÙÀ$5êzí0½XW‚]Þµ¤ûN2mÒ=Ï£ÍYæ’ÃØ1¬Äî:ÍDŸ–m²ôI »3-iú€Ñ?’Ñð—íTÌCùÄ^—2 3¬Ùsu/¸²ÿ4õ Ñ«+aLk?Î[÷/wç—’2É: /çzâɽÎÙ4:½Ç­dªf§¿Æ¿{‚Ÿ?qaûu˜±I´ýÃÁßÓØ%ÇdÏ^âíÕgF~?8ÍSà av²ù0oÔ©ÿ§LzðëfR/È×E!–<û'ò¾Øš*Ø~2Ï>úBÖÓ—ð:¹ç)½úûö±”å8…ðTÞ× ÍcÛƒƒ€b,LËÂx××Üg·-tÜ÷vþy×ÏäðæW0êŒ&ýv:†±>QCZáBGõAÜë5 ]Ïî¥|R„òª¬sÎ43tæÄÿÖaéŒZ4xþ^~y½2Ò„ÜDòœ ÷è:¾4çÖÿØ «]ÚpÒï VÔšö}·˳òs¼TŽÁýñäx‰"›ýÄW+¬‡a©ßØ2i ”HÎÑ?&±êtËæ0͸HšÜâ¶½:?L\¯6Bº]íÖƒ‡.µáŸâcœð%ÚùN ®îÿŽRRëA{t -C Û.1ýÇfÔ½Ö=„‹¤céÃÒPœrÞžêx[0}ê¿MŽz!Õ7&{Ud¡å·}"5az©÷°U ^ ¤Ów_Zɤ®rÎÆ=ÊÈÃÖ@è*<ŽI·¾Á™A783v*ø¥pjy“ŽþÆ¡ƒZTë“&±‚!Éß] ãÆÖá•’{¤wò<ž•ù`I»¹êìµ´ŽÊ²C&ñpSYýöïÁÿ”êÕD¸Üi×bz×õ;Þ3 b¿TœðЫtÚür=\V1¦C[¨Ý‘MDuÍyŒeÓŠÏCtÆ–6FšUÛA×oa&¼°ÄNúð¾=Jç3™Ü8ŒY&IÆe î-EuÕñlloq‘×ñ´Ð)®SïÈñçQ£ˆvÈ]íɪ«îñýS|XÉçÏt{ƒ™œhu•7‘Ÿ.ÅZ§.ãÛ‰èQ‘àadAgÅmÄ#Nãéd­»¬x`–5¼ÅR£ýôÉÅçpôD#ù<)‚^0•ƒ>[5ªY|þÒgøíc6›úE‡¹Ìu¤5}}#ñÿ1=dxÃdåžíx c"ýÆ MM´Ÿ§çÀ‰oæ‚cn)g_HÖå-À¤I30þ´&{¢tSˆPr³C#´í™¸Ž kМȦ†¼…σв¿Î¦†Û“ñÏÑ£Pµ~ ¤Nò#ï‚&׉Kµà˜¶Kcq^–;"yöÏÇcþ?øP ýVÅ÷ÆÅôÏ·•P·º6|ÄŠïpR“.ÎûŽštTO"ÜØŸ .@£*ó¯‚ß*!œPwóF±um?ñà³ù$üŽ =ÑOTß ²ÞÀ"[·jÄÿÙý¹wZðÕQ™…dËQí´mhA7¥Áâq†ìÓÔhÒcq~+_†¶ãzpÀê™ø£œõ„Âꈺ« ;RþñVóKpâ˜6ó›{⺜àŽéð“ÉãׄÏfÍ¥‰…~Wð7M6Û²ßáÀ$Ù Ô÷3¾Â'°×h'ú\±Á–,Uš3õ¿2A%Ùùa¥b'Ì ™ÈOBQñY|/2Ε¤ñš~ØA ¯0ûêyb>%Bå–d»ˆ:ÔÛ³—]R,ÒrÝËïãÊ røck3ø.àãñ%rô¹™“þM©¼ÿÿæ/”Ê$ Þd¿ÿeØ^„Š¢²¬õÔèîeï'[Rý#šð{s÷µY–™ûF‘¥â­\Hò8§9¸[ õúiüz+#Å,QID‘%fG£è¢“°ýõ$æÝžAc„9~ppŸÉš ñ¯©?ÌKÏÆ”þ»¦ RLôhPh ŠF ±Õf/pJ²;»¸d77Ï;†š“,î÷']QÕ {7"ÍqvfìÆ¹l®çW"¶,CûîýÆÏ³iÔù™ø¾Ýa„ÿÌó…MKÛa´C5_ÄVc[á’„}s*phÇtfQªÃd½¥é±üèüQI2âd—¤ LZ.C÷å)sn—Ué‹Eu˜5í6>0jãM™£Ì Þ\¢a¥ Pð…WyÑ»{2ýô¿ðÌ%†Ö~'Zƒx,6™ú4¯¦ŠÞwP)(„¶ñ½Píe볟ÈòC/A¾ÇAÜŸó æÌº‹Áϳݾôbù}(-6¢—ŠÅ©ÆZ_ZæàÊÔn?¡¥bËðVb)ð:@’¿8!’êmŽ…Å<]t5=Íñ xäNm/¡‚¦Ïà¿5þ¿ýÎòé$ü´!òLAåo!û%!ÈxÖ)Dñ2…µ¼±z÷ObÙî ¢ª,P& ¢f¿Ðoo`³ÂiN÷ÇoÐZ†F¶L¾ÉXî`k¶¯¤'½4é£Þµx²g9uL‹ÆÂ‰V4¡ëtÍËúÇ{îC³âs«:¾°ù“‘ÉŸ³p@¿ž&µg³ÖÖѬ¯ï+þÐx) o`±½1Ư¢w¦„* ‚yììój懟\ÃΞF‡Ïøsãë‹QÅoç"&B›—\·ŽÙ Ÿú“uŽB©s Kiˆg•%ßð—ÄN¬[00âÿ]MîïžÙ8ªMw—\‚õd7ÔvZ3pÓÂMêJØåM¢|2ñ‰¥ê¸4Ó¸ù_`Ýë œª9-žl¤¬Àgv=ÆöÛ’ðf=›R˜‚ å5q4+¸ÿ®¸¿ê’Áï†:­ÎäÑ-%j0¤c…û¶Ó”fij~}žÏãK/aIƒ±\ìâ8œ:é2 Qk…ƒo‘ˆŸ/ÆŒ¾Ø|¬Öì3Â)V‹™Æ:IV±—É2å­ @t”éS¿$f?õ(Ž{6™ªêAÛÜŸˆ"Õ`À%ÔHÁâë[PÓiÞˆÿ£Éõ¡F'uî8˽ » Ç:\á¯ËNš#ÕEšxbtH³L4,iÂùñuZfaÙ¼JXzW€½›“ ·çY£aö 6%4£•ŽÂÜÌ#¤0á‘Í?„žj£0xÿ-îݯ:L‹_Lo?²†é?êÉ ~ Ö¬²¬O0|ùu 9{—ìŠÑ çtMŸvß'ÛŽ„’¸ L¦§ºžx}úL|üóP¨éœ›­…[i, oðˆÿ| ñtÒJ,ÈO§sôãÀñûjÉÆÇ"Ì: ôÒuA¦¯,Âñd•˜‡&s±éÁÛôÏVd»Õ[{ÿ ìvIð«S{E&^J¼L29¶Vw"³³Ç&xwåkpQÀ‚Ñ®ìdž2 üdz°C”Öô\>4¸RrP¶”MšLWÜoÄn[še2„Êû?’~ýðþM;µKZJ ÏÏÀÏ3}éÂwâ Òs—[Ã)К0;h5m‚¥}ÆÔ.h-noæô£¿ ä³n“‹Ü¢RI&]šƒSj`eG»RâÀ2Ú7ø};ÓÍ,Àqá;YóÎ<è‘ÈaµKQoÃQ¶ýázŒ¼°—YöÀ¡Ãˆ,w/;”‹Q›vbÙJ!–ndÎDΆÒÌUˆÙ³ŒhÚ0öSã:ó¡:ÌÃ~ á$¨Ö’›ôj‡)u,õƒ²©¿ðÔœýðáé%öVø'.8r‚zçcÖ›T”ÙhFóÌ qþúv|™I¸Mš=lŠè1Z•ú[ùP%FMöƺߜõ÷ ìņ$öaP‹õM!)ÇùŠû8–ð…[®÷ù§ùÏbzUêŒ#Ër{Ñ\RŽwæÄ;öcœë»yoÄÿ™;sÑ1%vŸ@ût¾· ÷ŸÅÃïv²75`Ê3+V-aÊýž( {Ã_Ã’ÓÇð\¾ |=8H3'ÍtÜ¡q›­=Š6üaQõv¬àŽ »¥Î•ø) ®€™ŠLˆ+Dš¿wÜ=AVô^Äl“%LÂ2–ÉÏÖf‘×Bà¾¥š‰£ Yä'çãw”sŸ|­Ïïb…U*°ú†; ÕöÀÝjQØìwÒµ³¶L¨½ פGwƒ¸ÿßìŸÈÕ.y >ÅnøâÖ/"!.…¿FKâõ¤ý°$å9jפ@‰SIÖðŸm¦™P.–@Öþ‹Q•XçQÁWo‡Í¯ùi]ˆ-úôF}r¥Áy7@.¿˜Ä]µ¥3§星> 2J„åÚÙx€K‚Í ŒøOÂ’­†TuXÆ'cØ[1¦’·™éjmc–1å Ï°aù_T±R¥mÚïñìlME +ÞœPdJ¿qÖ3Ç…eã§êä§Ý9L¾N¿>¶c{½Ô9…ÅDZiû46Á=Ìw>Â}*^ä\!Ë˸ÂuóØžpqêÑ3—N~«MM̧²xýïpò£8¹m 8Ák®nùßó×<ÐëãSG¾v¬oYδ&HBsèaÞÆG0îØ®=x÷ý™F§|¨@Ok©} ##×sß½PË΀{¹ÜWáh:N*ÕÁ"î+VÞD. â÷^n'´qP)= V¼ß³þAñTQêûHî.Ëä¤dç±ÿÎ7H·‚Dç›Doß]¨t±ÁgmÄÝr;îŸ@G¿æ)´dI³Äi@ ¹âïž³ |œE“kKñE×Q;BÁ»l969S ç¸ï†¯µöì$ ”w‚M¼MîÏ„[Ã×ñ~¿ÔûéÿîîMÇŽð»1ûCSC+Q¦KJ´ÙÙÔû°£j1;W·^?æÂŸ3¦ W­ª‡ ®èyE<ÈPÀxï\%aH/ËèÓ™qf´Çá/>›)ÅÌüÿí³h'á%¬…˯‚hkQ ü;Fòó;þ ëÂí»Ijõ??W;ÑhÞÝ“ògƒ±eIºì4®$!}œÀùaàÊ@í' ˜³V^=„ÿú©Wè 3nŠ,Oþ™¥c{‹»¤ƒgß{”8PƒŠÃNt‹î òèŠ(Æ+œÇ)Õ>´á»2Û²Ö™9nQÉÿ,9ªûalç$à±ÝŽT0_—®)’¥šö{XãÐ,¶T~}¡œI/ô:Ñœyá³ë$Hvwd{jG³4S;¶ð¹u<c♹Ùdê×ììÅ[NѸ¹Ìü™>ä½²„Guªd†Ž>`Šsޏ1þiß nìÞÙé¸ç[#ÞÞ¡ÌêzSéÉ t5áœetå©)ÔèÉ0n –`«ó5h‹ÎâadM•/e+ï<äMŸD(¿pÈC#óV³½ÿtá ɬH΋f/.ƒý'1ç5—áPÄ-X11JlÀõ*×|ôý¯²û÷CXÎ<¨/Ï\æHÒ1AÔ.ºCŸ«‘K‘kès[6å1TΚCõ‹±¾å"D_O`Ã{§ÃX™Nûç*œßq ¥7ù²à)”¾|¼‹ÊyÅâ¸N¬*HT׫3›í{™®þq2UäôÏq`iS« ¼©<Ì XzŪûHjýEØØÑv<€¦°2!vľ SGù€äš<æ¸U…%oaæ+¦½ç®IßFö~½4=„n©öeË7XàYó¥0ô|ÿ:_–ž˜Í¶ÊƒãKA÷­–ÓúæÿËÑ öþgƈýÖ“ÍðîàT<4JŒKDzäE7ÚŽöƒžÎߤâ $-]/ ýÛÄh‹Åxp>+áŠÒg`õ#ä€JB{Ipc(ª¨ö`Ô‡rnõAsà5‰³­ƒ:´Æ¥&¾Ž'2éû!GÕ‹NÒDe:íÆҺąÛ»¦?Ïá>õŽæ»Î ųääœ@zÝF-6óÆõc·R²ùyàÓe L}ßn|ý$ 7ùfËIlÇJ{”¯’a΋öÁó'øž2‘†Žsãkw[á̤|P¢ÃÞªDµbÑÐ:Le`ì•·èê6zÄþØøŸMò§þ…=ÅÒ8A8%È}·ï&lÏ’hþqÛ…Ìzu¯ÓW± ²ýV?»/É¢üœ`©‰µþÚÿÏB(? ØÉËx²l!½´JÇË+`Ðs> 0æî/("ßß~ƒú™'`qôÜr—ê\QÅ—“WáÆ}óÙ%çCPT¹GƒÙñ¢ßðªü¬²E-û_BºU\p—gFÿ!;®ÝÅâé!¡2ˆ •ãÆŸvaFëðv^!‘"gàSëbk¢ó¸’þ}þÛÇÇY2¤åh—¿–oV/‚½û[ábs·nàó/OYfñ­£/iÛýp_rµÓÞEÆö©ÑN£²NW‚9¬ž¶Iu¤¾£'½NÅ€ðœ8ô)Ö\×ú“S?´»<å„ö;âì-¶Ì;øѲ=Dê7<çnt(²…âû`G¢™¬Jã-!`SÄ«‚äÔ,ŒþÆÙýz‰¡c…é˜*æZX7ëo)Ñ«„§£ ÈkGV´|@håJxo/I½uãˆ6ˆÂ¶‡µ(RhÄ÷îË\Öã’3²Ì-« ÌÇÍ{ÔYùú\Z:¡¹ö§`‹ˆYÓ LÛ²eÁ?t*™ßóz¤ÿ«Çú6ùþ=Žüš14›ü`ØE›,F‰wª„“¯ÇÅÏüÔÈÉ òämí¿ßƒ`ðeþAéyìŒD¾iQÃGsÓÐÍG‚&ØÉ3…Ý`/s Ãn«C@Fwò²[nÝâ8VUÍ™¹ –t„ý×ã ]ûC˜×ÚïpìOY_nG\ Wk5sñ£k¾ŽgUz ÈP…=o…¼-kÉ¢5(_áI£Âø˜½’›½h•O}fü‹`ðq,·R2 û1ä×WòÉvåyÏ¢Dó)½±OŠ´B]Ÿ =žlÊíj;Ê=‹ER²d$þÃÔB9¥+:¤ÿõi\{3w™hP¾^ ™¹ì>¨Ùõs+›RÐm‰Éx‹‡NËÓBñØw÷¹÷î8Ô½V¢µÏ~A¯± ¼Í‡ucÃw‰LlµÿÄKù8fi5@ýãrnëºÜ¡îÊì¥.$$ñܘ³ñ©Þÿ  û„w²ªæñ¼÷žã¨d!&ý”‹^JPÓ¥ûÑËy «,RÃCFþD0ø÷U]ƒ>¹©N3,°²Aƒ~S—£Óï4A­Û7ˆkìã·Æ¯ƒ>F÷ÑŠÐ}¡œ*_/Ó¦Øÿrß^ŒN>@ìMñâ}æ»U˜Šž>€=j*è7ñ±;ôœ¬¯zEæÄ,‡1ÝãÙÝHþz;éÌsrìw ?[¨"Kå°AE˜3¯ #‹¥rÛ¾z`«ÝBÎóñ:˜1´šŠÒõ‹Ÿršò!rÖA°I> ×vµp¯Â¦ré)Ø-b»…F£ë•x¸VÿOü¸‰…Æ€Û‚‘'~Ž[þ‡ÑðcVADý~°Ø6Õ/÷ @æ-Èo¼‡z³®XüùY] cn6Ax”2›»A¶äs£Rõ¨V1·ïÔÖõ š³Ú,G÷ÚÏ©ÿ¡.;¹$™$¸ê'>Ä€Áw¤6QÙŽ¾…Ûgðñ‹‘Æ?ΣŽ'ÃðâÝ{u¹ï-ØW·óà³ÍǽÚ³ù…Då¦88†*Ð㣋,ÜŠ³¥¢Ð…A4iY‰ÍsÎcáÌ~î° .X/ +Ïoämˆ E¹)”‹yŒ‚£VÂõSh±è·èú>8Ù<ŠÎبÌ:7“Ö]"ÜñÆTûägz¼—/¾‚8NÌgmæ2ßJ³EkŽJÕ¡»Äš¼WÊáÎ|%Ç©&„-´¥ Ûßs:ÝØ5ƒZ˜4Ú1yëyαELNÏãX­3b¿ó3Î~T÷ë±÷ù…½ÅŸŠžç$xêÀå€~˜;=k,í¹1Iø7À®•Sg™,ûɇßæÐéº/1è–< ]'…ÞbB0Û¢€Œú†]©fJ“ðÒ·ÙðZê…“¶Š/¸ø·!7õìSÔ¯¯çàc”, ª—§öCb´ÞÁ·˜ÐCÁmü%rj4 !ŽsÛÛ‰ó$Ñ)`ª6m¹}+RâpBí4ºâç°¹¹ŒÚøhâôQËÉ›øVX{rS•¼‚æ¬Då¾Õï8 KÄÕx—þâq j{ɉ™?w‰ÿ⼫¨¶FŽä”Z³¹â£¡;ä5ÙMþWÖ'2Ø"H=:ìùãå«ñêúNî§A  ôq²¬¨Ó.IЀˆÏÐÜͼÙ[²º¶|nOe;ÂŰÍH–d$I³ú¹¦Â ,ÜD~›ªBMƒÕAuy5>˜ÁÌÏs|Ÿsü[C*uBÞê|‡Þž}-tu’Éã§Çàpt'|Øhí*ài)טFé:ÎJP‡—¼ójÈøWc$˜Æ}ç5d*sºS†¼™ÓñPú9ЙEòöÐY‘ 0fHŒ%¿.Ãν1.C’½©#öÿµ–Ã'‚Nà8þ(_;~™ÁöÌe*Æ«g€WŠ3=S´‘÷qœ”?)ä*« IÉÀ}x{ß–5Õü%7¾)âKx>ú8ÜyÃóŸ×€‹†Î¨IPÿp'&v•Á2}>ìò­¹ pæ®Ñ+À§ùÉøb̺·1|s èf9¶n];dÆ)€ÿl:±rV&"ß½( Ý4>¡õ‡ XûzÝ»È_“’—¸XÞäÞX¸X7Ûž¹¥¥ü¹á0ZB/-˜&|”³VOÇKßU î«8Í4ðæL¾y`èÍDÔY²„‹z3áýæšd‘G¹|ö¶cdÛX ¹~_mxÉ%‹_ÁQ‡Ì×\Œá:¤ü®8$¯Dë4-ñ@â(,;NºýÏpš<,YÉßš8R6r˜—;Wü¸…Ti2½žò€”û%ãëEµhóY€Ý³s€Ý¯€Ë²p·µ¡âW¯Áº>/hYa¬z:'½WŒîšŒ¾Ãšx¨ë%oÉNa–÷ó+Hó#•Ÿ7AFv)®³;¶9±¹„›î_©"š$JJ Ë*x×¥&@ÈÐAÜo/gÞîÇQ÷~’9?&9Í_úõ–cð:5¼ò]ˆåñgýïý»7óö_þõ÷ýð)} ž—}ELµW Coضn&¾QG¹ßû¸×F:làƒ4UÝôôûéÕ5v\®qïY·wuã>RÓþ”TÀvÛ¿øFç'¹w£κDr—–)â­¢dÉZE8½N , Iº­„ 0ÉÅ«¸™jÚT? ‚dUÁÖ‰žlQ]r:…ð»ÕjÍaó8Øè1‹¦,‹À#‘“Ù›Cd÷pOB“a8@‚DÔ`’\4˜‡õÕU)#ÿß^¸[Ñ!àË‹#š»ç‘„5[˜v@,ñ”“e†]™ä"ž†reäþ‡â‰\Î#W†Ø”Z²9»á}ÒGÒ¾¢'hIPgoäË\ƒÝºm`28ŸF>×–5@¬¼¦’°Vw§–uZšÁI§sG÷yÂN–M-7jEo°¹êбF8y®U}2Š+»ö<×nÁÞ70¿·ÙÏ*Þú³€ž÷ð¬æiÔú™[U¾D6Á½§hª’óRˆÃ¦òÔ©ö,·ò¡s~ÚŠÎ+àKmÞ¶î™ìñÁP»FŒú ÞÓ÷¥úO} &Y†òÔ¢)ˆM+t¡“âäOM-‡¹<íŽßã[¯Gð¿)1”´\ÍF¹_R\g×m’¦Q ËÖr5ÛÿrN¥\‘„%D\l‚di;¾|¶¬1%W| ïÅ?þP/âfä?‰§³¡¼g:jw/n;‰û&¡¸—,š¾¢îj@ZvG¥C9¨âÃl<ØÌ˜åû®—CDÓ=ìÛ—Úí\”mXÆUñ»´,!xÂKR)” >žÆ47&ž¸-õƒÈðÉLWp=õî¾+`À_¢NoãÆÏÕ„ _ýsªqé˰±á6~èZ^[dàvÎlø¯?zùkòéÀœt™üûcŸé¤ÓWE’B ÙžxמäJ–?™iÐ+Ôÿõ;<`}FÓ®ÕHÀBm(üû†,™ý%ga\ÊlþÚ‹A9ÿ9ÔÅBuì(OÚ ëãÓHcV+7ÎÅ–æçÊcÛº6Þdñ"þ Œw·’ý‰¤Ÿô óŠã.êà_¥JßÍyW"RˆW¸þ»žS™w€;þ  ¼,_@v[$FFPÒ™fŒß×w;]NæzUÆsîMɰÕñÔ¯ùÚÆg!ðèyPZ8}¹Ø=-RœDp¸^›Nuv!|3º¬vÖ©ä¼½©¤ÃþÇA^" ´òÓáÿ+lQW‘)Ä®}O‡ø›_Q`©žÉDá™r¬-s?Ž;1 ÖŽrâíÊ+Oô'(2µW)pgö(zEo)ñÜž‡¾Ç¢ÑÇNìÎ ìUÄ0~?Ÿí×£5w…¨“Ñ=´Û ¢§YÑâ ’”n š[Ê!1F›÷¯#›låû%…ÂÁ¹âhú+¸ÛÝ%ú;ôHšòcÈ÷¹‡µgrMÂóéc#Sœ¶%u£äº÷<Ó{¬©à*oL¦qW‚èÂyÒl¶ñ#XÕ§Âb»S°4ögжìÿÿ¬^ÍßmåÊÃûË1äm¶£oÓL÷¿Šãu"pÊŽEX7O}9ú‡›Ê3íËI¦Éúû@õV0ÞHâüüüð@¾?ÇZô?r«u¥èlA/¶ãÁ%¼“_ËŽà_ßITly ›Úºyny©»ðu{Yp šjÅôÛ2`í¯qì¯o˜ÚœÔ×(7kd¡¼L®Ë Ã{ø'ÒàÔWT۾̮/Á.éüÕï~“V+»xØv¯‹ÀCkTaJ™2[ iùƒôæ Óu;<ÉyMZ¾%–ë?(ÉÜ…õÙóݸù¯7}èùžd[oénîs§=â`ëYcŠóÑ9°s$ÿ%¶ÉàpÖFxu_‰n”bMÅ÷¸‡´_¾YÆì|ÁýTžë{²auÑ6ëîPšTHÒ‚ùäFÛ;šÂ.meæÙ¢Tk†> lÇ:Û}¹Æøæ÷UIx³ž¦£´ý\Pz××"lYwŒûé…Žsa§–>ßv -Éå~ìõGFÌ¡¨þl­Çeú [r ·ç£-ûZ¹Š==ÖNážL~@…þ¿Ímï÷gg‹µ €›ðj2+ˆÞD—éW Æ¿Frð þJÐ`×N'Aß" ¨ˆf¯>ùÃï'ÍÐV–¯ŸŠŽÄÿÛ+Ùät±'m}šlé}):Ó}ok|'_ãç'¸¶§Bµì>Ôù,Î>æ×c”ˆMò®Aý=Ï1Ëw?Ädo'™vògz S°\ Of<ÑN¦Ò¿’ö³c»Â_b‹D)±2Õ‚¼­žô ™5Œ?‹!.öìÁÓëô0ß)Þ^“u<» Q¿®¡IVßnív ’ÃAÉÅX–æÄÎo%lß“çØêó‚?uëy\£ê„¯¿kÁ—,k²:‰Ýe¸ás1^ŸÁ¯©R%“0”ùs\B/2íÇ;ÜÀ òµ‰Ó[!§ÿoZ|"¬/R„£Lä°;ÍxÃý×+¼òG&¬{bJ/=à¥!¬~'/ý ï·Bô9AtŽÌ@•žÉ°¥2’5l‡¬»@âlîZÜFt…ú8ogYð¾<Ý9ÈKޤCó¡¨Å|þé+w8ÅRs2B5ðßµxüL6 |O†ûãšñaA(ŒÜ¼UO`µü/’šœ‰ÇZ+áòUOH{x€×"\ rEJ$|Íx8)//ãïmºcz+'•rˆ›*ý Û¦íÂQ'žàŒMûhÕ¢ch2w4f%—Ý‰Ž·çÿ«ú¾¶&;[~Ãî·öNjüi˜npÿ|ÿž™èÊ»&¹ØeA½ÍÏòœ=ưG[9,ñä‹^{ ï÷àÃßCð[<Lì£Yu×NºaĶ‘CQ¨á2kL°ôW)>]MØö›â$-¥’S­‘bžÒëpÐë^wø_¯Ž¦+7L¥wÚ×`ç ™‹Ï~-³~]'ù^Sꛤ‹ò»£`ÃkîB’~]Yòl;MÇŸ­KZ•…!ãÞ³vMìšB5FͽijtC‘;úþ|éÃÚÎï(?æÞÃüõIÌzãÿî_ÒÏ„ÆG`úŠ`œt›³œÉ7œ€¼3lÌÝ’%4LBNàÌ ËUop­zÉpøŸNoŸ¥Aµ¨`ƒo+îJýÀM»}µ§ÞFƒ#Ê4m¢"UÞ±—íîÃQ¶ÞÐéy ϱEÇÁ×äî§Ÿ²Út&ókxoÁÀã28÷}Ãi“&¬†3 óeý’jÌï?;éÞEjÒ¾p\ßAœVÕOû½`CôU<3;™E ŠóŠ$—@õ¸MÄÛJo?‡/ס»-«`oˆÝ&猆•Ö(óü^Þ­Bckаa"hz{{äFðo¯?\”öƒþf§ZþÍ C|c«;xdR ¶Ýšjuµ¨Zzn.åáßòÑPz)n™¯¢wÀ½åÌ?u”7ßËUm™û”‹øÃ× ‚vs^ÕpÞŸa¼!úçfV8mŸÜÍYfcZýa|ë2ƒ+ ©öÚìõ¨… ¿xÓõAÊqÝãÙÖóGp×½ZðÖy 9ò8bþ oåâvç(¤‡§¡ygri5æÍçR&ÑwaÃÒsè,—‹ Þ–tM¨[úã8ÔÞZͺ,Ùã¨@ ¾`=÷æÐìŠi#ø—9o&t(¿Å¿ÁQÜ„ÈxØg7œiù€v*¿È…ÏŽ¸jr½Çسh%´¸d!¹nuþãµ‰ÉøH±jÇ;¦Ì£àgõ, åg¯¡‹—páAx9ß峌‡“Ñü´6Þu™Ž¯f5‘í'yjá–S®¨!ßC⥤Øá¹à˼Z0së'6²x~ ]Ø e˜w½èš‡Ð8£µ='Ö]!Ý>WÈØì`_y§½½Œ€j 2ò1R—gótZ60–ñ>ÚapÒ?œIòÃú„i(¯ÇI¿Àó|qí»gÄÿ½F…ܽXQèó<A"2LmÛ)þ'ñf8br8Vêa–ŽqQ7gÌbN¹Ù„¶jŽpôP%ª›ÌÁò³­œf³½hVÁßÂB6-§*VÍhò"}¹q•Ƀ‚ù2:Ÿw åù:´38‚)ø/¤¢¶ÇAj@ßnÛήŒG勎ç™pTašd a×m†³Çàórµ`Ä* ‚Â%9È»f·OL¤‹6mÇõ§bù·ÚuY@[ –J»co´,×,.Kç5EaO\<,X¼ÆŠãOFÚ^ÅEöâ¼ÿÎYsãûÿ];íqÙ"ãèßM ¸·Í…̧Üs '¿{Á‹B,ë‚ =øùÞˆaÖS©æÙ(ëÄm»r›”)ž¡n6>LîV63ÊþD\ºg°Ð9e OîÀ2Ó¬ÿŽ!"÷ Íù¹€¾Í‚«Pª¼Þ ,ê±òîvýô: é,wµ'­Žª„Å ›`ø‚½ñD‹ý9€^â²°-á:å+³ýnK¸­t([’MÇ3Ú±ƒj¤Ðä˜/¿¯À t«°ÏÎãéÜì¾`ëÔ·uÇŒ)£,ãÇWòĶ–ŠŸ:|ZaÄÿ«ÂÄaÛYe\ê´ Š¥Çc¸] 4ìšGëöà½cøÜµ†—%Ÿƒ¹‚!ïç0 õšt $ ù­ Q%@ÏöÚÒÆ[2ô§ÁI®kóœZ"DÄþðç&ᲂ½¨­9‰]qÜ+µÆâÖêX,]Нr¸qï£ðOÉ”S‰÷<¿¢FÂìM7fÇ‚KH|ÈöÀ§MW@O`2DÞah ´€uÄÀ.A¾‡‘ û‰wðõâW|Ì÷ð ê5Ïü×  |HtkƒuáñæÞprÞnÚ᪠=@Q¢ Þér“n0˜tkñHýë\%Ê^eÍÁj±và‰,Ä(·[À ¼!‰íé¬Ùä>˜Gô`Ͳlþõäo`õ¾¥.ö÷ ¾ò] Άƒ?ô`…áXô]Û_…PáHX<ó(”ú_‡å¯ðTAŽöeýÄ€úñÌbX‚VFQú0¼ Ÿ§2þšÛO*öÞƒù‹`)ßž+ø«„®/dx‚ÙSiÂO9ö{Û à—ÊQƒhðÆþ[G^0™o°˜(;ŽÆ@N‰5˜»@ýíœÀÅù˜øáÏ\PEÂcé®ùœÌeèíâFTúˆ¿ÏÀ˜OÜJtB„GêŸï˜9ØÒÚOŠÉØž|NÏã[=r ÐQlå–L³Ë‡"AcØü2o"œû+ˆš¼ÂÐÖsÄëWŸâ¦õàVRDžû\Ûáñ´#æÆW=v(f4¦¬¾K r 7ë-QQ– ̆Ç'• óa+Do†®$ÈZ÷ ¶Œ[ã~x(ÓËAÒFcúdâ3ð<5<¸{‘·ç~:oJëSxþ‰W÷å$ bË_ka¬d<4¸K²/?)ù* ¼Ë†÷Ÿ%Ù7Ÿ2¸þ {UÃŒ IÖ4­v.N„άK#ñ/ÖuÀÏ s‹ã˜‹A }5ýxñǰ -¨ÂŠÓ¸{Í4*TA ,²M–”p9™Üí%Z”×ëÏÚ[ÆÒå©—ùiH¼aÅÎO„ûžÂ´W¬‚¾|ÅcA£¾Ri™9(yÑ_ÀTß–E[åwú'ÚMo «‡ÊñåðBî°¦[95œ¾WK\k¬è‚MFÔÚEŸ5ÄM#b+òéø¥âÌãu2ˆl+‚×*/ÈÄY³ØÕ߀Ê`)d¾¸?…yX1×ÙïCX0 vè@«Ò&»)Ÿý6ÑdwÜYØß9¬yU"ý·ÖHüßôËÆKÙØ®ä®æž`…wCðm¿ Œ©Ç›O§ŸçEѧ¥YÀè<&ª½ ¬ëkpÙæ]0é›*ë­Ô¥EöÓͯñNÞ^ܶ[‘]øz·vø0‘Ë›éžÝ%dÚ‚TBr4[ñÕ*bå)룲[_a±A¹9φþû.|4àÉV¥Î‰s÷²ÚE1xºÑ™ E­îÏjôR•Ž_gAma‡3,èõg2ìòu_¼Úw÷iÉ%÷ú¸ÀSAèòk&½j6œø|/¨]—ÇšB"¨~æNüŽÿÎס S^Á]kUìV3bÿ›1=<îíA4š»’ëx‘‚‘ÓMÙÏ mºÄ4x];É­zÿ¥cžAú–(ô™¿ ¥Çã3͸TñŸfß²ýL¨Uý¸P# ãÄÙt99˜éòÙÙqâ»|ØâS:øjê|xêêFïtêáÜœSðñÈC2£x!üyŒÊ+6gE6v¼Ë Ï…«^NL®)–u~P» Þ·‡jtl¬1¬‹Þ…3·©°¢¹‡8·+g »¼“::“™ûÏÃ5Ç”˜À¹/híl‰b…™àÛÑÛæ2ðß9 +*73Þ‰í0Ýâ+?u÷n¢¨òzBœFò?Éê>ZøŠ+Ù"à  Œs[8Ÿî³ç ¡ÊÚP»Üþׯ³+¶ÍFSÇÙìÑÂŘR’¹Üâ%°• `oÃFz­^ sµÂ¸À6@×ì™lç†fX(ÌövK@c‰*§R£K_Ÿpfò‹Œ!µÃ…UýÃÍ3=B»Y¿¾(¬ø§ç$khÇš7Øóæ¶|YÊÈÌü8õDíž¾ÞT&ï ªÅ«Q×ICÐh?™¸§AOÌg9ëÜèoÏ‹DüF\{w'öOJD=Õõè±vdñ޲kdÏÀy¨Š9†ë‚EFøßeUTd£ñ§ÇŒÆEqaë\ ¬q;k¾žLs“Ù<7V½P‚Å“àÜ/o¨Ýðˆ˜ºKÀBc˜<ú;Î4S§rMáðòüJþÆéá¬w—Mß FÙùN^ƒ+…ˆpW(oÁª%×᤟AÐ^‘IŽÏžÅ ’–€«´}ù¥ ^Ú[áÎö¢úîLž}Û`Nj‹aï#Ú¾4”8¨ÄËX@y+Þ‘›“Ch^úV˜[$Ä"Ww“Õï29qÿPúå†ÚL»>XÃàAà’oo`±ËkPfâYnº3}ñΓ˜«‹j¸šæØ‘ø´c–Æqžc-éxASªâÖ‰î¤Ø´þfÜ Ä=ÅHëtNV§/í¢ {z •ËzÅÐÆ < ®é†2Úxt2»«zÚ»³j±SäÀ¹dPn^Hï¤1á=\t@"µ6Wf÷âiví)Êÿ$µ?ÚàRh:žÖ `± ™KÝ/îQ.µßýlÆúsKð,†èù±‡¡ÏáÉZ)zƒZ*œä}ìȦ×òd!ÕÅJïûÇ«•oq“¬ÐÑEú,ÕxÕÛ_ 9 q$mb:så¾ã)–NŸ|ð 9§'±Q¶ìûïS#þ—ðÏ qÓÎ÷q]©á‚U+6?UcCÍ×@´`7Qk çÔP„)XBþNþ ¯u¦Ò"£8î|Ÿ?at/\8µ‹©Ml¹}R\¶˜ g'KK®wÁè hvÜÒq~hÝ«$* ,輟rð_ŸrrÊb(Ô‘ëIಸþÅÅíWfT¹?Ç1K|¶æ W1Z½­ŸAÃ_ê´À¸ý?¦¾;«÷ÿßÈÈÌ&+»(JVÆ}^’J$Ñ iJ»TJÃÊ^-##DFÑ"îëu…¤¡´TTHƒ†&•Ư÷÷>¿ÿ<Î}îë\ÏûµžÏs^^g„Ž®ãç”ÙSZ .ŸE¦¬äŽ^Ïö[•`ÿ¶_ÏJ†¼™ÿ •¿áuî¿~ëXßåÔüº/Obìt|ýo‹V@L¹Â?üGùÈ…¨À UÒ&êbü÷°ä©7ÉÏUa‰ ÉúžÜ%¥[ðóO óvÉz<‘¸öœÄ’ Æm˜W‹×Å¡mhÍԛ®¿›È Ø24’vYš®l#cÆn#Û/f²µ Tñœ!ݳü'þì½Îk\•ÚZŠ¾Ç 5…æ±20‰Y3'ÇÍ^CRlÖ`€ÞL\)’ÂóªBà OÁÏY &­1"ÛÎL •Û&Y™^dîœ*ê žnÖ\}Œ?h½¨ç¢ß¤6Ü{)Ìôrÿ›eÍ»]ÈÄ9ûáëõ—˜z¥~&ýß{ÎÖÂê»w¸v17\0á"ÇeÏ[%ؘ4AX×îLÿóöP<’r lòeÉ’‚çŽJ‘‹¼$|ÑÇaý¹oxòÐ&ˆpÆ“—.qúsÌÉ8­×˜`®€žÞºä…O>Ÿ €q;åéé5œÓgIêºöv\“ÙÛsph·8MW¹ˆ§é±Ä¼otXÁ×H¾7œÄ7>8ëíä·gr]/4HjÁ;¨³ØéÂ’dÜÎC'צÅsZ'¥Q›N¡mÂK\Lí"ÅÉ+ Cvla6W’gtsýΨúÃ’®x¯ø/þër{`ca7½bö2·%üÑ©4ÿ¸?•ÝJëûs¸ûÉÇ´ƒ`”1ÊÖŸ±ÅwtC‡Y8ó_r÷T­Ù†qÙÛ$wv\¦:¾F“Ô†q$`®$Ô†ñì‹zÑfâCÜnnëpb áðp8÷8³7«æ:3áÞÂq¤C–â'f1¯ÕØûÒ3 ‘¨A$Gf ^Z*þØ,ºúZLeƒx¾RÆä‰ÒÌ1w3|«Ë&¨19g²q—2D]ºvÏÿ’ë%&øcñrHHx »÷Q-û;¼õ—çÁýÓÂŒ¯ôŸQYLv!MÂ{þá§cñÂãI°V(Ÿ÷%GÞ²#+ØÛ«gIiÜcê®›„.&Ê,¾S…žÇ…Wµ¹Z; ¦JBxÏA†è;]„HïH.3]šy›õl[qÓtUc‹ŠÛáí‰ìX¨>™h ›5ImU2ÎW”amaŠhª9VGFÁòeÛ™¤G(Nþ<Jï801©ܬ¾_&nÖØDä d¶%9¸ö'¼8iC¦ìP€æŸoÎ#–)À¾ˆL aÊ-dk› 6 Î…7kosþÄë¬^œdJÖßê Ïàà)G¾0üOÿJïAL”ûŽã^OÀĦš´bî?hÄΘ4p’‰kɳ«¦,c±$‘ |ŠAÓ•¡ææ(”=¶á£± KúpVO £M²'!tÁm˜þMó“‘®q,àšÑ)‰åT&ÊÈÍ«‰æø½øjÚZg†þNþD¹ÆŽIßiÂ¥sªHJu$[û'æÊ‚Ÿ2GÑÙà¤øK²š #¨_V@‚Ró¶B¬=¼Œ}·ü@5¾ƒïW÷àç÷R¬1±çŸÙN¦ßKÅŽÈÑ\3_†­Â×Ésسä©,Ðs%ŽøœÁ‘s9­ÕóÙM'ÔÜþûÿ/Ë€yKÔAÝD„¨Ÿ‡Ï…D±R„ü4¬ç.º>ž­ž<þÅ>Nco54º[“ûgÎÃoi8pkfMe3.G§յ°ÒÇá-b½3ó=®áö_'0‚m¢Ž÷¹èÁ=°`Í_ŸòußMgK‚Ÿ`Á„y§KuàykzA¼˜ :¤Ê³u—DzhQˆÝ°‹ûy\tâ´ðÂ>kº`· ¼iˆÄÒ¶lˆO“b×&dR¹G’S^H‹Ó{º:ë±ZEŒ­xn÷Ò¡Õ©q¬äòd¿¸eŽ”BDA'ë[Y­gÿùKG¨ (C]ÝCý­Ëv ‘iVÅø_/ñD… l‘µ.^בgQa`Û–ŒcGàäm÷¸Ð}b°tf ìsßÛ»b Xₘ8¶F&—øUÂøœ×øžÄ¡¡Z ›¿ë¬½ûƒ.ÍÂDkgø,š •ÚC°tâ^ж”&æ±µ(*-ò·JÂ{}-ݳ•+}dÏP2mV-"ÉQ_á‘ßkظN ¯˜±Gé~XƒrÄVM˜ôpG–jIÝïeâÐÃï°Mz è(þ­g¨ˆÉa4o)‚‡Ö‡Qp»Ñ!EÖÌ'Š&ëð›ÇV­×ýo¾3ýÕ§^½ÀÞo0`3®þF­Ÿ¶,Ll6Ñ>2…};ÚÊ Í„#€ª 0žsgÒJþÙ¿uÊzsûÈM~T }ÇôÙÁA-¢ ÎÞYÚ¶Óš´ýÈ‚…¿Fs®—ÌØ°w!úöœåtŽ˜“óEq›z·e©4ë¬þ‚1ÏÒÓ7VbCŠ3¸Ür…>OQÖ±];nA‹¸Ñ÷äð½Ð1l¼ŸŒ)Íø|c/§>i4&ÃÝÜtèûsKÝó®oŠã‡y¤¦µŒ3çÀ>ï…÷Æ€‚HüT™Ëmˆ¥86ÔˆíÝX@ º&Àq|ö7nb0AŽÿ×G$HÈš" (Þ aüF¨¸õ˯¸4üŠ_ÅŸ,ØÚþ3èÿªýïW–ôÓÛ× é7‘Yô¿™É9«;aógCÞ„¨xÎ{~ M¨mƒNËUl‹¯Ë~"Ï2J!)/*è)áéKcø×òúFCËUwØžõV]Û‡«wÃõIÄøÅ-nͼé&œ¯› ¦//¡°®ç82‘4ì;€û'1‹E¿æP¹ *DÜ÷9ôÑsØÝ Ãæs·µÁPÅø@»hƒèh¶> #Â"vÎaÉb:„kÌF¯èfã±ì÷8eNØÅìžá‡üè¢qø$ê4åVÐ1Ú¥øyàW}¬h |7ˆ>‡äõï狜EÈu¾éŽHÌ]p¤Ÿ¾F&ãEÅ$ÖÐÝÙ{AëÇf¼Þq•®ttCÕ†T¿p˜o´í®^„'J`Al:ÿêƒå¿¼±éòJ P—«®r¡¿îAßžoY¹«zaô·Dn¹x+ª†©±Á~(ºïvî‚Xz?TÊ’üù“Ø3)ŒY-Höq¸Ú–BN"J'©˜‘žg(Þ{œ_®±=tÝ€Y‡j±Þ!¹ˆ"X(ü™íÔÇìï ¬>+oÿ¦>}Ã+jS89Y>~žÖÆ  ´•ÉšeŠìtk•øã÷ÏÿïËÇngn§ )=?äp'ÎßøÈmšFõ@oœåšîóº¼ñ¼ÌìžÌ¼G³ØQ˜¾Dë÷Ã/ß•ÈJUVä—ο˜Õ…+jñsí Xf2g—Ÿ€s¢d¨ÚØvåF[2fÙP‰+æ¤u>ÒïçÖðþ·—êÍñÐ6]°óÐÙU'ÙC…LësgòÙ”½{RE_,}Ä Ï8€º ·){ÊèÇyÆ`xØKcsá­³}Ž‚ {.@>p1ó  ö g]³c0+I‰~Rƒç ŽãÊ?î¨%^Âù ø/ÿÅ6;âî&=²U€Í^ ÎŽØ\‹³Ó@ŽX`Œ×#|!K!0í-µHXÌ6ž>/4Ïâþ‘L”²ÊuŸ"À¿=‰ oã=ó”ú«Ý[ x²4nQeäÐ<&1oæ3™©ø¢Uœ@ÙWšÅƒ‰üíø|Z*g‚Ñ%”?ÿìºã‹£V¨ÃN"ÂÔ’ë ç†Ë=ªÿ­uvñD¢RjÀV¾šHFžg£¤ƒ ©¼¶˜}Xâ¯ÿÀõÅeDXý .Ù9Ž—’ÐÆMö³'C£ôI´åiˆ÷î qübH¨½k'.†É; IwÁr;7íþ›ÇòúT¯áîtOˆN:C‹¯ÄÂv³m¨zÑ ›Ì¾r_Þ¡ëoyjùq9ùa'Áa‰E°K0eoßåßž8~|Hc"}“Ùµfs<°Ä„MÞ¤Mr'‘­%éÛ§ÞÌÑüò!þQÜ8ê<ºIÄwoa´]È½Ø S‚bÑJOâÖø _^ýN{ë5ÿja’øV.¼ZŽmî:´^µ }3Nrùºé±õ†ÐôÔŠU%7Èã`NÕ« ®€÷Ëè·åM<§9åø¤XÐèx¬iº‰žó*¨{¸óÖĪÍu>Ôû§ÿ¬æâx8ÏŸ‚Oùsfª°ïù‚¬+g:¯ÌFÚ¥rÍr¢Áµ…Ðqï4M9¶–~bôŽ”*˜ÊíÂÙMòé)êtgð­Ò¦ôœ9³Yóš.n¾·þ˜“·ƒépb«=i&›_=õ{Ï’sŽS!Ežá‹\w×"žîš±ppŽ/\ñ¯¡šùQPèë;­º±ðñYÿ¬:új UfF ±^~ÛAWÂjbv­»ÃÓnð{ å,Ì5@.ØEÇðaÑXC_— WI½xó-duZþ³ÿñïôpß:Îr*³Ü<@õ»âIë¶læ‰r-Û›À}¨øöùøÎÖ}3¯£sˆ8%.ä¶¿¾HG­ªÄ íŸôòQZ3D˜à³= ßOeÞ„«¦'pÛqôþ,ÀVúÂꦼ9¶ÏQäøuªýÕÞ, 9ÛøFÊd¾”.=).¿ÔР§›³å Âß¹ßÂQâú¤pòs85[ßaâÚj(þSIWÅ‘3J;˜‚ÜN¸±³›óW÷„­ë‚¢°þ7;š»~ w.ffü_öÞ{ç šXòZš¥Þ¯ÿWÿÊbZÑEv„¯c À|ôVC×;y:+YœKes[bFG/\Û»™<×? Q™›Àú¡?~Ïî¾ Q ²J¿;\´ÙøÃêðô÷b;ë÷=Ò/k Á)YŽ—eÑ»‰ßð1î¤ã!8pÊ“<ܙʌŸæ‘vÁxûP BžÄJŽ[ëLWÄ7ÔÇéø‰9<<5a#ÞxÐÃ_˜9ò­É‘’@Ò=®‰ÛÀ\`À!‰(;±ï+Sa|vÓÿ]OYD‡[ãƒÑqü7§™Ž®-»"²‰;/1]ñí˳Ì¡îÿåÿ#+ÍøÛÆP:ÿø.:a—yš{–S¥L^=IS~äá陯hò£Ïœ4$‚qº$·Þ€ºÓE¾lŽöA¸#­JÞÛ‹…ȰAmôUg3t3ðÝé1 µº„F–åÁ¾ûÒ ÝŠÿxÞoÌr©ƒ%?¹¯\¤gΣj|$zìîÃM¿3!æq8ßaùi˜ý&úN&Þ‘¸G©Ÿê’ëÒA}Úg.©K—.K„–N\P©Ó¶ÍàOºƒF*þܬ+ðßÿŒ=#]t²ÐONcêyÌI¡§Ç¾„Ô凰k&ˆhþûÿﵑ"Øä¶ª–üÂ+¼üËÍD/B’«ÒË…=KŸãøÝû±§f_­Ö„–"Lo? «·srûc8ýP°h”"c×_Æî‹ØBUÈtDË‚lP=Ù >1“ÑàÀ öð%Sµýa²Å8QÜ‹ßÅÙûòX|h׉ÿvðýªÌ>ì„þÙØÕ¯h߯‡h¥@”z¿Â¦·'1*N“÷BÇšõLþ…×ÏõÐŒµ†h "Ùª@T Y‡ãKn±Tnéîy¸c°—k"ÈLöÚ@RÃZ”¹œÖ‘L~l­<ÆÛ,ú—ÿ¸Þ$nwÑ 7¯sê"a¤6ˆôŽ×Ñr$Uè9Dø_Å9gÔÑ}/bóótÞIÕ`]­ƒºEHYÓè-@.ÉŸ€@WIèqËfe¶â"l™­0žò"3£Ir8.Ïu '[Âô¹ç7ö`ÉÔ0¦Ôû N×Þ¡CL˜lµ%‹o}‰ÛUÆB\çYÜy‘J–sñÑ7¸ÏÙn¤xA6®ëg¡‰Ÿ¹‹¡"{=Gl§ý!Y(’‘MÓ‘†;´<ކje%ˆzÂà͵lú"ø~oÅßz¢†6 ŒHiö ½×‘ÆÕÏÕ#åz'¸E5.X4Z]¬¾ëFÒ%Üú#’Ü'Fž¨ºÛÅχí6ªÄ_U—mQ²'"‡¶@LÃ{oϼßL0Èô†ÇÉšÌÙÜJ*5H ÷²áLË!â“Òo_üß4ê2.«1愜»ËÂÉ…\Úòz ®ül í‚:¤â£-#¿¶aÍü‰ð6@³³1õ»5·À# «–žÇó·Xì¸^@œÞlùï‚^ó/|•àAãŠïóÿp{RóÍÿë;¶‰LÂE¤F*ž‰ÍgöÆÐbÌ7}–‡³® Гw’<ó|ë:köÑÊÌSĽœþ·®Ÿ\_ßv¢Ø½ €ë~s¯Æ:Â/çFz×x>­Ma7öág _½Ý‡éœÉ€V™/üչʻ¸†ô=vÀ ñàß_9AB ½¯Íÿù‘#žyÙÍC#Q·™‡G÷Û³¹'tɺÚÙdsHöZ¿þË Í¤ç²äwnØ;<&»æ`•þn¶ ñœ0Ä…=ÝÆ«8“Ù|Þô±àWv½ëÃúþXæ« pÛäÄØ6_5’ _[<Øç~U"¹R”mªŒ'ùKz!¾·šöž†íóãk%fO ˆð½ûØi2•ìû˜ÎI5¢K+÷³„äxpô*·ïVx¸eâ4õ|Hœ£LÌEɵx2¹ –4gµ†I¾ »QjËJ^§ºR%x†5ÿã—ž‡Ë=¹YË!ªë¼VÍt!éËÁlo5xO<ŒVÖòÄÄÞ–ŸÕ›¡>lëy²ÙS˜Ä¾›–k%XaP+ÃØ}ä«‘7 ßhE^í(âߪ|ɌĕˆÏ«¹$Ù6´ðœØ(="ï6Ÿøì[&™î,æƒY¡‹HjîTÜ!•R9ßýý ÷UŸ­¸´'ÙÅŽWf!i ÀMÚJöíL¢íúîôÂTán4.\¥IÖÖ¬®ë(-ÇEÙZ|¼¯À"1”þú»ï%²Øƒ²8aÇÒ¾Fû¿ã°íP$ŸÞ…å»Ééÿ/ÿ;˜ž#…éP'Ý0ìÎ^ó¸Ý‚Úð01l ”¾ó©9>;åI`è2ÖÙ;Ží<¿‰í_4Œ›–s'õm`]K ÌÒ!çëôYL¹:Y&žÅ2¿:³—³TIý ò Q–}÷) ޶d(Á…8ŸÎ¶-ûB·]ûÞ¥ä¿Þå§$IÔ©4×ò#‰ßuXUY³ò ¦‡gœF¡‹“ØÒi›Ñ®$F'ÁÞ¡ÅÄoƒ0Úô”€{[6&nº ñ¸¼ý7íåBå÷²yŠ¿aüE‘-âÄ©î(|Ø8ŠlöWafR‰àqòÓ¿û²þ.´^YkÛBA¾î7oñ¬«Üiï×ðkÑE8)Ϧ|¤ïfª²¨«·®ß¦žÕËÐáÑÐ×ÚB™€ï_ý¼>wó±ëYÔÌRæîça×u65F–h|M‡Ñ¸õ}6ž©OÃ5§4‰“×rØ×ú€;ð½†uº°·7ŘҙHðÞø›óè ˆ˜?p¨XÎe'?åbU_ᬔNÙ­õá—Á|âYú^!'íbAWžòLÞ—òÉVöõ•iÖèrx¤Î>¹Y2Øf€Í3ù…(!“Omjµ¸Š¢, –!×lûÿæs3¿DPñÔt‡}• ê` 輆™°W©lá#ÝfBÞ?V„_;Ö³í®‰ñû"¼<ž|-£K¥rAdð9$¢‘µ>07ï Øœ;S©5p›`<ö±ª}¶¸µâØN–'®—;°<Ø“9ÝÍÊ)5tó°Óäæîy†Í:¢0Æ®…êÐh¸¨¯…·bëé Lº±†ú£ Ê‚º­p"À‰­Ò>…C1 æÀ= ­““ˆ\€ ä‰dÈ}öhOC±‹s±Ê‡ÞMÊìå&A&©NÏþáŸQô“žÙ©D†UVCýÍôÝ·Z~FK<\j $;’5ØÊT±s‚K ËdÉYñ V…Žfë#XJm ©‰ØòWs]8tž«““Æ|± ¤| ›ßÃôÝLuÍ(TÎfNEVì„ )óò\nldͺPI.TÜ'Ð'"³Èsª+þoþóÐ n™ÿ4z'¸›S©9‡)oëÀ}²^×÷S˜íúµ(EgèsïnÚ‚T*Üû2 «‚šA°·‚~åb[¨ùô+è?¶œq¼þ9Ú»d9ý$bÄFwœæ¯{mŽ£!k¤j¡YÕ,’ÂY«©Xˆ“‹]𥙠³½¦ÜÀ™+Ô™ÃÝ89 æÆŒ¯;k1.T‹ýüþ›±•s_Èx7’ g½$ó±À䱜—ÂXUå H´Ø=°Ý}ŒWðªàZ¡ùð.WŽÙ:»³çxÌ<¶¹åÿøÿ»Î4ºO݉¿ùÉ5h9 SE,Øuµ(r µ œ§”:„…ýÄj”\‘Âu]xŸwn&*?'“ÉÞ5¼ÞÅy½ì2:†÷¢f®"Mý˜ú_n ¯F¢ü;»n¡ÅTµ_œÊô}™Ëž9ðåî4yi o­4Iºµ,¹ioÍÖmÜŠ7$Ê!!Òš¼»_ ^ͨÁ?¶3ñé±8v7k9:¶5cÁEH·NEni :JßAÏ£ 4.hÏî…êWÂÈß7(ÈÀy…XûáÏöŒ6‹=åë{žá•Ë>ÿð'eÀðÒCçDÈâi»A)·Šî=ŠO¿Ï%9^cH·Á_?X=Ìp¨Àª½¡P¹wÞ>²‰³¾½¯v~ↃZ0tß0o­Åv¹é/G¡iÿ|ö{îyÜ·i Lj~ÇùŠöa¢¢ènÖböäçðbÚ¾(¢üˆñdWùopÑÛÍwu ÄhDù}΄­:‹×"ÅÉí©%à>¿!ø~-jâsËlc¹ { õ"s0Úí¦DÑÔŽ|Ü>WŒ—‰g«¤8¦x oÑ7ÜÍÆñ4•ÇÁߨª&EBæNáFHࢇæÿðo“˜Àú~½Ÿ‡²^¬ˆØÅ,>f1ßT¶ð•;i˜ø ;#ÿþþ¶d05š u ²§“\Ù2‚PºØ‘|ÓîDWSeòJý ÞiÁË‹AÙ!IîkJ!uN-€¯‚j,pÇYþ­W×qêUX›}Âv/fW§øu¶£/’J· ÉW°Ôñb Å€Á Ä,¶R„yDè¬T…˜‘“pWç\ºË±•«@kp]™ Z×ÁÓò¬G œsOpj'²%‡‰NǨÜÁ'Aae0Å%Œw˜ˆ‡£‰ç£T¢,+ùÿLÃtdµþø'8BÞòjû`wî/ªpA¼:  0Õ—‰ÕÂn§¿¹õ3J¶b˜kµ¿€^%1ε6½"–»T±©ö=}C¤`…à *Z¥cÆéݰݲ‘nŒ¥Æ×Êø{#?Ñ9)®`º·Ž«¾7®ˆîÃ+š· ûǬÎÏA¿GC<+STÑ=!†üÝÔÐï¼EÍØþj![x_ƒLÉÚZk½¨¾y–HŒ#kÖåoOpƒ†ñ Tºîd^ä· ô%gìX® {òÀö€3yÑ„ûÝÖ° 6Y8*?â_þug%d¦G¢q¼è}М~–.Iköfr ŸÑ7£QY(‘Ý=¤Jºç¿C³ Ñxw´»0m”¦ÖáóX™hñ÷7³ëqøVJ‡ˆvE¯`Öÿ| ðoÎD x¸ó‡ÇÖª¾Aqw<ó²Shî¼ 95²/C‘Y^ùŠN ÓðÐÕÈ×ù‚¿r¯BܬÃpÕH†Ú%àm•g(é(Bºûó¹|!L=-C¶‡126o8uÒ’T‡²”}8°0›îL?̵vǰ‘­pÿ¾=ltþ,Žh…³‘—4®å'f^ÅŽ)R¶þûÿáß5î}¤ÝG¯Þj£\iS“7êG>‚N¶<‡¢ÛðlÚ°˜#ËdA“LM×àâv©éI5P*ƒñ¾äÊ=aP,üÇÿØ—•pÇ?FG›òä†g²âQD`´‰rPÁDcS¨\kLÖê “—º&ì Õkl}¦Lf¯TÁq6˘À¥˜­·ƒ•¾XG·H³Sg4 ‰‡ðNRyˆVrO•ÈNOqöºùþÜ×ÜOÇU8åÒÒ5Gžy&˜s¢É ½ð‹„x¾^;E§ûkДYFÂõXÛÆïxáÙ89;—ÁÃReâ¼,ضå7Uðü+6-Å/çÒ?9¼M&¿ Z×Õ#Ñ=D‡yV“o ÏPéUÂÌAø)¼Ô=…fñ­ÿìŸèìȵ(è‡E˜Uþ‡®Tîåî\rÉ Ÿ!(ë›Cá]—ÿæ&ó=î¯Åw1Þ¨Öû ²C¥ˆIåy<£ƽË\ˆº4`¶Ã½î6%oà”…›ð†ÚQ˜E¶C«è3~+ìoµåõý„MŸåXÕ>gV9ŽôV{Û½Ù8ÿÆ5|XZÁ=Ì©@½eSÑf~˜~“bÍ79Ì$E~‘X½”×ã_ÁÜ›V`0¹sψëò„Í‚³ñ²ÖÆ3—Ù“½žuœµk­RýþÏþ:S!ñÅKèv7Ààov„æûâµ^èü•/Æ|¤Â#e?ê\]È9µ†ïXANß`1œ51Ö ƒªºìæÛê÷~æoZ¶ì¾§£k·@pðVÔ^cË<Ÿ£"y.¼žÚßTÿÙc~À¢iäùìTH‘ü¹R‚Õ—rx­‹¢ ð’.6*$Ã÷8'8ùVÔyÛÐ…5Bdó’QÄñùtv6‡»f)`ûóÊ?ûO{çDÍãOÃò-O@²j¬úlç*=É _#¯-ÉÆM%ÙÏÞŸSVT‹°z›ìyÙ;\+jE,'sz.¤E4_>ëÆWø®ÉŸ¿L@ûÎnh‘¨!u·¢±­ø)‚—*4iídC*ϹÞë—¨sáe4Ÿ¢GN¢Õ^òÕ%„íRKx¸…ÖDâX¿†ž/f?ƒ ñ1-K™<‰M™¶ƒh “¥}Ô×Ü•Ülå;Ö_`O.¬Ã˜HZ´¹í œY­ŽKtþÄeùÁ±Rìtïz˜HÌë’Â9øÏþN7®AÇìR¸¸ âs“¼Š›¬ÿ@ñyÇ‘‚aa²v•8ÞS؉k–Îå¾[ïåÖ^JâáncÓåR¬^¤Nš­äÙ½±ï¹èý$¿<• Ý—²P ö£ßîO$‹¥)q›°”Ì\PE¤2ZÑGóžIòd#KÏÒ–ÔO'Â0Òf?’0=Àœ%ê¢UÚOq\ Yíô^ºeÂ’÷ëó륣X¢ø\s² êšø)3r y±~›D ²âÉš»µü—š¿Aûü¶EÁ†•ºÖ²…18RcB–â,2aD•é\çÎ8Oûgÿ£-€#ÇÁÙ´‹øÛPž.™Â½¨¯€WA‚0Sì :ÎÓÇÖ©fäºýO¸ßbCBÖŒ‡«Æ×œwé4Á6q¿wsžáöxìêZä$d˜öÜ•\›ûe\[o®ùàóýŸÑ®ËWߨƒc©?ùÕF¦lçí¸æábR—¿ˆÍË9Š…è‡§væ@EÉ“ÿþ-ýn¼tù ì8;ksTí¦Zo¯ƒâ8lÙW–œDW}]¼6¢LÉq¾¥„ Ç"ÒàwæBh;/†• öÓ•ŠàHêGè讑Ϯ Çût²þ»ÿm·U‚#|=È~’а藙½–Í €(Çh;wÊÁøOZL[0]ìÆòlä†O³-ù¡Î5ôòÏõðn…5èËÁ· =Æ7×!Wz•X‹é¼™[è´¤›/wèLžÌ.ÏYAΟ·!£VÛjþžùå\:¡s ÷E– Üs`iE¯ÐÙË7uÈâÉÍûQX'µ‚ŽBO£ï‰“ógvñ¦uäWwt‰§ –E4ÖLR&AÛ—»š©$A;‹›wä}^KDOcŒo6ù^‡Ø³I²K#AP( ïÖõÿóÿتCܪ¢ Ol†ç'#HËþî–á HEAyUÕ‰Iáz+°Ñïr^ß$ÙQ˜1xŸÞø}°Þõ¦:+%Ì´fy é$‡¿ø˜Í<÷K/àvI2sádܶšËìO墼f²É¯™RY#Çi$WR'û>€5ó 3{žDÌ’^·OvxnZ$œxRƒã_D£´ÜæÊ,·Ð/kµH[²ú¿úwK2Ôd…pg»6h-#O$g`±a®¯ülÿ§ã)ÏåE,„Þ¸\Œ0ÑÏ´7ÝÄK1ðvÎ^§sYøIc?ŒOœ†£Ú©ËHòy¦Ž[^‚65éü© ¦0©e<®ü>nöŸ‡ó4ÎáÕÒ—œ9гÃZ诩™ ãñ·žqSàt•Cܰ¹2*´?Óœ`b´û!æß]Nö—EQ»p+ºý%ß"|7-ý²*iæÃ­D”ÙýÑ(\Ûòßž@*FE4˜Àá¦ý@Ã…dIâ ?Öº¨—òþúíC[blü/þ§¯âÝ>H£»%H[óÝ¿ºÚ–`M@A%÷Æí"|™”‹#Óœèžuò0þÌ Þ¢ÎtÞåápÚYý…~ß[FWxƒ [G¤OWcqfTB™Ø13Çò§ËS¿æP‰Béî9´0˜ñìÞ<ÃwS÷6l]®A&¥-ƒÁ-jø2!‘V­Q`……2ìCº:®Î…UJõâ,QênÁfÉ߃’dåÎ4Èý©'NðÍöÃ|áùput'g>ÿ¬/Œ‡º …è¢3š´n­Á²\ÎÔ"òúâ0eŸ ÍùIWµ½Á0‡ì•Rÿø¯²¯!¦·®ᢓØe"O ¬™xW<±P E·wãÇïÑìêYvešfw…ß † ÿ"ªŠ \1‡^+Æž9½¨3KŠ[]c‡îÞ†wþð[¯Å² x~§„êûª“ÉÃ?ysEIõð'nøKœÚðû¡mÜ ´:ƒÚ*œÃ﹂äÕ=ì>Â{°›ÿFÒfò~á׾ݨ´Q•(y^ã¶$ôûAtÿæ4%šË»ª×ˆ£ ïrÅw3Á¹)Ì€@EÙ÷>¥ÁgÜJ E’­eDô¬1¢åVûǂ⠇ù¯™ì å·Òü›Š¬øÐ^|i!F^ï±Cþ®…¸U”Uõ‡snà«së¹÷±jP°0žÞ±$ÅoŠÑ$d'g7YŽ \.À-júäé.ŒZ˜‡Ã»ãù§O*cÐås¨ø"e¶ÑpÄ`]a,.PÂù‡ ¨´©>\ C‹ ¹ù(vìârÕ„ÁÙ™¢2Îf;ý†G×6ÄÞæL?Z³ Ç Þ4n‡3 ó"(ç ݾpn|!÷Ð%ßn™ÌÎ¥±‚„{¸É¬ ^_¹Áms]~×xí_ÃÏ´‰ÇÐiçþå?ós ¹ŸË9“ò$PÊs%7Õ£iPñG8º' <›‡÷b¤`Þ}ú®FŒy Wƒ€ýhˆ›ýŽêÇ•À“OÁjË<Øß„o³c¹o³w çSª“×ÊÑÓ ¯IøÓùÐ k-Ÿpú‚Ÿ!ýÉG¤IļœØê5üþ„ÈíJÎ^¼žÂ§Ñ/Ñl¶1Y:  Þ„ÎÈR? [²G1`Uþs®³¤ßLQ"7É“ûOqÑ1!âªu‘VíC„_ë³®–°~nFlÆ—Á€·à÷2ã4±:)ÓzƒìŒƒÿô¿g’<„Û$ÒK²‘<[oþÆbu’µ÷D}¡°dç8îÐ4R¼BÛ‰hÞGÐ[>®L8ÈÛ¡öaÚ„º;åÜ0óÙF.sÏ.’õ̦¶Î«±›ðñ‚õäCš |9x‰ü=—©Ë.&zi_᪰ “kަôcÄcûú,DÅñµ`,7gÛ(²6'× í†C'CxçüÞÁñoôxÜ0Ò‹!}úsásFòÞm‚–Yç±kt3 ,n­¯ï¸:ó8úeÔ+l_8›|Ÿ0îZD!]$ÁŠe@Œ.ƒZX3¿Ô'·®Zý‹ÿÛ- @ýg"ÊÏš‚÷}4á¡´8‘›Æ1ùndQf>{¿‹ž줦ՓÙ.%!ˆP}O§´áB¹ßxã˸j’]ZœèbU’yk*ŽöMÂÈCŸ ²àkwfza‰®Ö‹@%ë÷˜4YÝb4¹L‹ g?Š…v/fþwiœ´3šV¿£Í`̬³–p§&±Épø^ ¬/¯çë´p+CpX,·v‡g%U™-Z3kÓš»Ò;‘UŸ‡ Æâ›˜<-¾¼º üÎnû¿yÕFbrÿòÿ¬?ñÜoß£Ô sDU8‰ýîøí×M®†²w'¸1~kñWsÖÝü¿¹Ð_¦ÛãÛSJDøU,*O“"è„m’¨GK>¯“&É®Ñe-9Nù6ÏÐþ¾•|Ë;†À×¶Š<ËÁ·.ä=ô÷K ÁæïÐ8yØ!bzì7ËÊÖHŸ¯ —øáÜú—¡dÄ•=`o¸>z2d„Æ{€ZÞ€Ãï_GñòóVã7?"«@RéÌ;‡3{îðfHòûÆfSÛ+V4ÒØOW21믞‰ÀìßÏ ÇÈ‚Ÿúiä~m—anû³7”ou'd¯‚ßîéè›m †UþdÝä8 ;ÀßÃ{‚Þ£—À7ŸnØVß|:‘ã,Í‹‰h5f3(9æüz QWj˜s×|ÎþÆÞ ÂæQˆð ç–ãÂç¡ë²—xÆZ þLî–¥5æU® g{–óÒ{Ï©í:-ïÕÈùçIØ'˜‰jÕ°éÄAêqżcR&]t@S”æâ5yTµÏ¹rÖ äòajNDï]ÊM—›Cõ•Ð×—|ÙX§ÎÿfQSÿyÞ9÷ ¸ú°&ÊôÜ„!­üçj¶.~ÆMÛw€´K=¢;Ä¢ÑÉGœØ„bížIØuJœͧôÏŽ`TyF¿‰©On–e>ªï#‡îÏEãù3pÿq(žÇÄÎÅA\Ú1Þ0¿Z£'°iƒR¤'¦:á­3ÜÏyá0q¡2{Ö|ë߬ç|5oñ&lW„©’‘È‹»Ž1G”H}ÙrRSóCmÏãë¼kèŸz µæ¢Ž%Þw¼Íë¶:ŽÌV#ï°-þpŸ‚²&sÈ¢@y<iÀ–×ùaq\ rK½±ò òÌax´Ê ÷kjÜ¢G̯ý«FEWéNŸTœ±{¤å¢ûøð8à…¯VH2æPÌVçÚÓ‹ðp| >½¾-û°@7Ž= ¾Šàè‚îë£V¦ÁÐ?Huu€d‘Fjÿ€9–ß`S_#×£¶nŸ¹ ¯2}Áe†,S^'@Ò%¬¸ûÞ3`˜³¤I-¦Ä΢›6Eòm;î‚Cs¸œÚŠpw*ß¼«œãû×üÝG68eƒbਥÇñÜÇ9Ü‚¦pI ÀËiíÜœ­í0ïØ ¼Õ‚ó”]yÇ…¾Ï¸nêlÙÛ˜]8Ç=†ßF_Éúø{>ÞÝ-wÁµ oø.ƯËAW£ÈT¬¾œ…F9h¯‰ºî¿‡Ég ˆª·0+ÿT*MJ¤éä(ÆÉçS‰ÒØöD…ºû™jõýÂïËxáÊV¸7=‘ŸÉx«Ý >Ñ•z·“ÇK¬ÈÍ?âì©$w|mÀ?…9òi°iâjtš< 'ÃUmr]׋o]…4“lzôgl,>e9`ª”ïn_¦wøÛð×Jÿ¾…0pu*{ï7ˆ‰]\P—Þ¯òÇ>ÉC°]í;j¨á×içœXÑ ÓÒQR1†Ú8møWÿuŒÏé\ô½ÉÌîíQ’7°¬S?‹ÅßÈAaîN 6œÄ„_ôà‡Lx4Âu]cû&ÎÅv{øo&ô}_\%À\Bˆ|oͪ2Ôq£3šÙ‡Ÿ®±Ù7…Iûgƒÿ¥ÑäÎSòŽŽð>Œd°¯‡ŠñN“8óiX Ó_u`Ëgœ;Y †§ƒÌy ÝâHø4 Ð^ÝÜ¢øK¸Œø’ó’×qè¦3i;ÞG]ÝÎ& ;ÇsÏ`üçê=Ž\Ý}ö½¶¤‡\&°èZäªÆ+¼¹ù2~»t{ôG³×Ýþ§ø¡ˆŠ8ÏÚù¡FÄéêÜT÷7» užàåм§E¶tD€IÁì\[ÉÍ¿ÝâG1e~&Èâ¶@wøeÝÃ}ÛcĹY†aŠPºk46(j²çvL@̈‰É9ùfp`s Ó–ÛCÇú8ÒŽ;"t•úCÎO7·{Íá4Xi}ÔÌ(„“·ŽaÉíQ¸9ó0·{øYÅò/O>Gà¯P_T{4§¯ +Ïñ+™;a 5긟·„5·§ƒÔÑ(šàòê 6`sÙb¶Nå •lñ¤Ž[5ØïVþ?þ³Gÿˆ_œ†6“Ô‰u-äÆVâÐKw6#RˆhëßM3 hu-wð‰ €LŒüÐĹì~oycÙØÅ£I¯X8&¾„ïí3‘H­æZù÷ùc·S·ØÃ»ò&¼_î®Å¼ëž$ ª‹³Øfäà}§ÓÞ=…üz}˜Ú8™)7²C§¯s3ŒZè*ï#´óOÛâ 'fK±õÒèT<†9Eipn‚ ¼¸ò˜û³¤“ÃLI‰ÙJæcÚuxÚ CžZÎ…ü Ù8ýX-˜ …ã½9¼š"ƒïuXÞ\5L„lÜäÌ?þ#5Öë4òƒÇ,€Ó™Öd¦Ÿ“eÁ&}þжcöÕ—˜–N®Œ¥š9y˜´Á…\ʼ {·m'oúNA½Éü¶ñ8·^  §Ñ‘ƒ²ÿõ'cÜè!°PÌ»Õ&dN`„ Òþ¹X×90ÕÞŽ÷óE?ú=‰oÐ\âÊ65Úïw3@ïÞ~´îÌÄöEÓ‰ìñAœfàJ¬+tÈrí±¬ÑL s‚þþεU(W€;å{Œó)M…£'¾C훯üùÅÂìmèUØn :U¤Ôc+ÜÞvÇ?ÌnÛ{Öjÿ_p;9y^/Üüúå_ük¿Nà&¬Î‚*ÙÕìõ)iÜÓèľ—Ë1×ûqø(Ю2%ǤëqÑüh8}Wˆ$|šÂêEžcÉj&<¼’é,ÞÁû½à=¯÷áb|‡§Áh¹$Û§áÇ•“Gr–lÆŸÞŽa¶F´å¢»o>…ߟƒæ'î‹S!îÚ÷•[Øžx[eD‰¾Ò ºF\évYˆÉ³e·µtØ¢ä0x¸ø:}ñjYé:™7h°§9kòW'Ìæ¡Í‰_8õG> +ãk6 üÏÏfÖ’S¹á\¡DNî—–Š#pçÇ\ºzqÿúßü÷Ò¥âä¸Ï)ðö+=e¢O,L Pp›+Z-›ÇD&„ë/ŰÏR¿áu^¼¡&ÈFgÒÙ®6lœš#êû†3“¹ÑŒ[9‚:éøå¨6iSÜkóqßE!-Ú‰›w³šy¤ùüePºYÒV£YïÈižvÉQ(|¶ì&¶ã‡«1(rÙf¨|£•9½<Û£{ÈŒ1)ìè—b[@•}úÀ*@ˆõI€/SM؇«¢<òH€Dlû€· HDùY\x²;¾+„´÷„ÁÖðÔj/‰ÊîÇÝ›’QÝ1•þøí‡7 ¢8ÑŠC¬J0öÿ»RCoéLG“™ úä&«£¸»;#ƒ¼¸Á-¹å€ñ¡ñË“Qxe®4iÝÉc_ú¾ Í”ó¬rè2ž9Â+ï `ÞB`ÛÏŒ_ì'_¶¦°­ít 7 ZJìYeV)–-Î&"y¹øz_&jxGÒ572IÖÊd2gáW®ÃwÌÆ±7'`íÎPh™QJÔºòqºM$¿Rÿ6þ°ìä.×­ü«!* íø|æ8®:ÖxCæ~3òg¹{èÀÙ" öåö Ô}ÝÄ›Ÿ³¢ëðÞž`ÓŽuÓU°Èë.èN>¡ ëa´à¬ñÞg:ªov`}¾^å&b”Ÿ {×2úßâèb'S¶éô…†Xé‰äòw7¶}{ ¼R<ÍÚ¿7‘ê­ÎÄèáYÔp—'¼K´Æ²ˆù¼¯æ»uºçF CíÎ^dšwsWaÙok’¢.D\†ÌXäP*5W% ߸çwªÙ#µ.n¦¹ j÷Çä‚&½ÿõT“;§gñ´®Æb\\´€W+¾Bž†O›;ñíµZ^IÅA~Ws6àuüÖ;†Ý:šÅWè°;íg‰Åì ¬s¶!Ë –e5o§Á(™Pü>F”ºˆ>ïÍÿÙ¿t D^Ü€ñ{)†u¯„°âó8Eè:ªJ_€qñàß?‡çîäÎ6_7$=;rÝût±"qü›A+O <—x>[íJšÓô™M|9VƒrÑHz!¿¹áÇ¿¾º_”ü¹¼×|§t3’õ0Kõͨ÷Æ}è0º™÷øR=.ûö™—˜6ÓÕ1góü7‡šË7ìäë" öêCNWÄ}_¾7|Œý[o…ÉYçd¬›I„Z¾R‘™+a@õ%ÍîÃ{Wûða·9j®ÇÛ­¡$Ky=73¹œÊ1‡ 2è¯Rÿ—ÿVýtâ j5ø† òS7Ò°IyÐ2áÖ¾/ ^€'wÍÐ5¡›—Ü.b»²ðL˜!4äj·8MnÒ-l vfýc;éÎýð©Ô›(«¦fÅÐýLjÙjí Ö‹[àAº \ØjH–Û(ý7WšÚ|îçN8tÑ: @þ^äK"÷ ppâ/z¦)¤vL¤’nRpl?°QÜlô7"Jo0à@%ÌØÑó=Ñx\,dG–)œÇÇBÇ•÷ðÒï;Í vÁ±àm7 ]"/¡Ã¬rè <Ú›:UŽp¤" ¸ÌeÿðËk_ =´-kaͳ]‡µ´‰S_2Ìè}‚QæJD" *V2½ÂÄay!7]G…YŒŽÂ7qbñb°Vó£–(wß¿<Ï«þ}•NO}Ãk¸‹µsaIèjÖ-›‰ö9ªäà³ë°ê£«´˜ÍaΠV C7Ùª3+±*’.næÍ ¥Mk!;Åœ\‘2gµåÁaç!ÈúÍ/𡛇™³ñ€T$•> BšòäCÒ~ò>[Óçáºð)lØ<“·ñ”5^]ßò¾:\¼~ª­_â²5ÚØ;åŸÿ‡.Ž®ì3ìñ¡w/æÓ“û§A’Êûºò¯:…ÎÖUàñÙ 7mBS?ª*¯B+øÎlAîî§x!_8וNöEáž? yÚõƃ‘±+ˆŽ9Œ£+åaù¢B2ï¦óëäÒ'¦ÒÛû”˜ý#¹k+½Í“»QÉ=ðLÆ!™ØÿX†()Z‘Ó’òÐ76„â‹0í™Þp¾õ&ßVk"Û³bYò)Ce¥™Ôã/˜ùLƒ=,J%2÷ïÏè³È‹hó2 •†òiüžL8·Ž½”‹›ûq!+¥ØbQ›*} Dþ««žfr çeѹH¶ÿWú,å³ÎY.Í_ž“ÇlߌªƒRàæ¯+6²-&’Pzß÷ײ%ÜiôˆÐgßÔBPEñ LÞtÞ»G¡ïØüˆ§¾-eoǾƽþÇ0fÞ:ìØç̾~³# 'zÀ©í8¶œ3'KgFçðÞëo°TÙœ ¹¦J>Õ~…#MM¼†‰dHÓ…×=†‹NEóüJØ)ÿAp¸¸O,؃m±çYÁÚf8ù—«D>Ôamq0øRÍw:“kñ=ó6îºÎeK4qÓìÈ÷ûbøÕ³„û´)ûŸýóÞ£ò<Š8. ËüYî ½ÆvÌgš=ì> k&Ïå:F¼èŒ£·ÁøÖNˆž!Ê2†¶Ó¹c嘢¨*Û&ÞF»¦•s®&‹È;¡",ÿ–È)aNó¹=™|:”áKi:Èø’žÄ(£¿‚ Ì8Eƒïsù!:Ìð[6†WHП¿ÍQ-;Šš-΂ÇüÜ;ÀàfC ×l½¼n~„‰9Ë8å=BXÐte¶”ã¼¼û#Ȳ“½(ãTc–ÁÑ4òºu3^íÜÚpdk_ Ã2•æp«åbó‹_x%`'¶ú¼kxò$÷_ü‡ïÅ—‡£¸î7Dö]ð“„ØšCõàžÿ;eEIÄ1Mð ¼ýg°Äðvœã~çß×dò]ñÌkª4Ëç÷ãÑ—álnpÑÐ3ûË‹òìƒèá‹|•9Dáz̯^SËwäYsbl£¤$i<ñ“÷¥àü?ü1ó°D"µÂĘVÄDXêÓŽNÓï¾^qžÕóN”•û§´ã3)ä÷&erýö+š±ì'ˆÞK‡ìd%º§O\:Kð¤Ž[§C.[Ma¢zXöTãôo¾Ôuú&Òª– :§Ñû½ ëˆY5¸¿[Œ³¿[.®dJc˜]E3g8Y’kUUÇCý[Ì•ÿBûNPáͧ±Üè ÿ¸pyù÷*•È·/nVUÄMZÝÃÝ̃‡=JøºSˆ)ÄvÀ®ëy¨–ßÏ×êwăb´ûŠ1€éÖ©ÍO©¥‚6s<0é_üÈFµ‘,®f“5vNXíX µã–³oãë6ÑäjöŸƒ=E{øù'2ûÔfÐÈÓ'WÆÊ“êÐ|u®Óªg³²ñ°<â<¾ö„†ÛrÇôÜáÒùtýE'¾Q G.,ÑÌE¼òK«PÒ1ûÒãðb úf… £ÏVc­f1¿ðÓ8²MT-»—È{°ZM»P[}à–Û|Ó-É6ëë3b? EžÂãaPyVÒÖ$†X’ÂEMp½ç5pr}Qµ…Æ4 ÷ý¬@d¹LØçNnBcèfúhõT|·ÃíÿáoÿÅïËÝNÏ grû®¼ƒÕþK 8u kŠfoLZPðÜhb&ð†¿ÎÒžämvgg,òyÇ÷Ì'‹äÙ–J#Nuð½à^D-ßÁMuq²1ÁƒDVæq¹ ×zBgÅ2Ï_~>íUHÀʲDÑý ®¼Ó »Ÿš9µ¿µMt.;hwJD èë±3®d.ŸM?mÅjÊŠáM» pK„œŠy‰7þ¨“zEgb#©Õ‹ë@¬ôŠy³PǺ¬MÙ—d=¢÷jª L%"kêÈ’â…Lw®>Ê+Aç%W_­ÁÒ×l$ Aá ¿aÊ_¢dÝôMP¹j;½m>íf”JºŽfyüÎŒ(pæâÄYÈÐŒöÏ–yÁ¿¡ÚÏõz){yVƒ¬4< Mbƒ86ø=x5¦°¾é¼:)x|>6eÃÕå”*±³Ç鬄¥,¡7¶<±eï7¯€Ð\YR-¢Éî¾Z‰#ÑiõA –Ï #oU™Z_7zÍ0@G³:8¾‡ò~o³‡W¯ ®m.»4ƃÔÿWbÏëp´$£ sùs$¿q/6Ù⦠[V9že=„{EOqBŠ›/3ž ;G+¯¢U;V1“'ã9é/XpÖ3º}ÙrܱZ—»tBŸ4—b¯&ž¥VÅпf-yý‹btãÎø÷}‰Ã¾R3^¾¸-Z]X 7˜kQQôQ'ÃÚÐõ£0Ù!|¶»rÊüñÞFXäÓ‚Ö"¸'ïgt㩽šeKp»,jÊúÖp`úBf7ºn@­ =ðÏ‚rs2<í¦{¥‰ŸV2Åý@¾Žü´áîÛTØŒþ 8¸b 4zÅó§üfþïAUÉÅZ—còuU¦eÚ–afdå²Jw%r¥x~ûsÜ\ÍEŸ-sÈ1ry{̼܎™Ë]™R+ª¬> šO0ÆÁ„N^Ézë¢hË¿úǸÛ 8{"œµÆOf‡m·AKÒJ’¯{ú·b²ŒcšJ‰ëÖh¢[CÖ‹€T™¿ý÷{·]ÿPoñv|` ¨]&ìþ—øS|žÖ}òÁÑï‹o*±ª­‘$¨À’)]'>Okaµ‘»²ã—¤bù™èngFî”ßGë}«éwQ–vL h¿>Êñ©sEÅíóàÚ¤}l¢[K ¹IÜw×Ê+R˜=OŠLW%“ uq@Úš¤v!?,&»ôÐqn)lšÅçÍ;™yjÀ»¹0ÞhwÌM†û½—méîÿÿ]O#h§Ö;®à@ šÿÐq,YÀê<¥‰‡Ô|¬ÑÇD¤eXƒ=÷BÆ„¬a=R9ÄQ÷óaõ8Ky9kÎÉ%Šé•ÜC?”ïd@Ú§–Ð/Ü—Â Ö²À– ¼Åž‹)‘Ê”•f‘GS~³Ó?Þ£C_Š0ÅËõâsÙÈ‘ùdߦüª¬,”Çך‰UY5ñVÚÅ–XµòU·íÀ‹Ý±üT-ì˜~šÈ^݈b7F¡”ñ~6Ϻš¡9L#CŽýœ\@·¹é‚Ý–‰lÙþvlÓ_BÇéäpSÎx‘´9„;½¸ÇíM"{oüïùלˆRnŠ'‡~Õ’ìWÀk0±ÔÄÛ×ÌÙ›kaübQ–záš¾ËF^‰’ ¡Ét¯·<ók ÉÚ´ZW„›®˜Å%ß蹋EðT„"Kœþ7ï„k²_›[qZÒ÷c ŒU»Œ^Ô¦ÛÌÅ "ÓÉË;xiŽ7œ¨×eýÞ:lÙ§˜-Û ‡6d‚럃˜œžÓÚLQ.áBÃóˆu˜9Ýí·Ÿ†Kw%ÑÔb-9÷Fö…4BàC öëá0RAEFO/`'±zí®[r"Ùu5ŠLŒK!rr‰¸ùæT,¢Mœë‚¥ä¢é, þ‡?¬M¦µ7R%µ»Øÿ³…µ¿2fcRŠ ó¨9»ÞLLSÅûk$ˆMd…›@`C0N½èͲ—E0Y§ÕÌmX“g ³7w`l®"y+0• dȹÀ ú±¡ æ[ '…9µÆ |N7-߈‚c ›5o"s}&u lçã8\ù;…ž}§†º7$¡õŒ(+ÝjCø‡rë^òþph½»³žÌ(¨†ô§kHâUIâat âZ* ooWâ+ÊÝWÞ@K'LÅçG%ÉÌ)òþX]½ßï8|pŸhÞÁ›TùC¬!‡ÿéÓÚX¼_V€¢ÛrèJ›Z¢þìG…1ËË9­X˜P^SÉÄÞ5xS§Ä³96aC» ÕÛOO)?ƯGQ¸×ÇN: ½˜…›%ŸpÕr±WëÚ[tCñ /_üYÌ51Âiú§ q1fEˆ0~æSî÷µE°tÔ.è²¶õÜå®´Ö@ÉèÛt\Ú¨ú«™: רVzâ» Ì'°‹{Ái»{šîác×dtY.Á>-ïIšKáKñØ4‘ÌPäÁ]Åd¸ªùžkëD“_/ÀO1¬ÁvÉ_Û½¦A’´hΜÏÿn©´¡g¿ | ?M‚¬qPëxƒ¦¿0‡ñv™8Æèoî}ò}ڒ݉ND%~ ¸±ŸN©ò!“R«qÏSŽÞ‘ûàxò+®áÄ Ü¸£ _Ɔƒø²©¸-î(ʦUQ¥þYüѯ1òørè RG±j=²ž¦Á”Ûñ¨8΃âg±N¥uäÑëcج5$>åï]HçÞì‘ +ÅÌ©PÆ^¶·û;(6µS5à0ô3Ä‚y"-8zh“ZÓeab°oîÞ÷ËÂlíu²,M“SûlË®¯7Cžé“Wéëÿ=ÿ'©¶~Eàê3Ì¥äÈ3Ý-2t÷'>÷·Ç¼*ö-ņäßʇº½—Ñ#e&±Óœڇݙ³b*^®ûVzA˜®¬BçÜžë.õCÂÒ1ÜRG¯áÛ~|`T ÍCó`it¬ú[·nÆnÆâuž¬£c†)¼Ö#u#édïk!R³-ä똪¥*KéùH—mˆ$¶¾Ñ sì\ hšÓúÛæ÷³§B”U(`ñÇÏÃÀbOT9oTNáÛï‹Ø”Õ‹@HâÉj†“³ý± r ó‹2¡å§ö°+5å0¹¤‡D-û‡Û¨OÊÍ+_à˜ NX=ŽýXr‹ ÝnÃÐì–Ll4üþòÌ#(ùñ--´#óf?¦!ŠÅl‡é«6óÚ>_GÏÑÙÌ­±4Œcг’ý¶}Ô‡Pÿi Üx ï¸9`aW<®Öº…?TƒÉpIt>y‰ÐÆc½$àœå|ø`{Á°ðØó vÇdÃÚõü_ï¬É,ÕƒÐéG%›YZpQ¬¦r'zq“‡&S*öd©®«P\Êœ¹¦Ä°ÃˬæëKÈØÎ$¿äðï:Ÿ'T#šoÂl¹-Yp_äóÏõ`ÏZì3ñ€:Ñ+P³*2“ðgë*|³jT´-¤µ“pªV»¸]ŽªÂ[žæìÑ»Sôö9ôïÍ«_¨­Íj8÷›1«Ü¸…FO¶‚5êG¸5R€ñë.rN—RàQó °ÝšV|¬üOŠLØJ –ëDz‚é ÁMß‹ VæÃf5öJN™)úká¬Qf*cDÔ~UâR"€eváè¿ä&ÏÛ<Šœ|:ÕAÔ¤ÂuI%ÿ?§3 š4V ‡âNÌž±£—¬£+²žÓI¾J Ôa‚¥ënÓÍŸ˜‹¥%>3ûø?ü2O8¥ `àƒ=“è™ßp×E—õÅêÁ²m‹‰ð²­xDÜ{Nr œ´ú>q?¿ÄÓ§v[¸üŸÀ?.'í)B]ui2QÈiMÎæ<ú—j‹ \˜{Ÿwé§ Ùvúë“`Ú›™°®9+_ò.Ôk’Ý©÷ôMôÝÒDN.wi(S /{C`Ë=b¾½ˆžoF¾Ü¯‚‡äàO|_µ[°ýB:>cÁ\Ã]QÁíxVþ¤Ú°3‚óy¥£Ü˜£Š)»ÿž:¦Fp)oz¡Mì'*,Æx]Gv¢SöÒbzÍ^†­ÖÍü—ÿÓî¤s¹ÊÅ÷‹$àþhèÜG6‡F:È>¦™Q»ñgC߸ Þs. 'fÉŸX@ñ4.Èyk[δ´pþV[NL\Ÿ=J>HN«Ï@Ë}e ¶  äg  me u/U%b]nìS¥"ñKÒ"úùœƒë.ê¶ÒƒkûóHg þXÃyðÔñyöIx^ŒŒÛïã¹e ¿õ~p.Öždq¯^ÕMÇÅ·àë±ÜÌ 56Æïø´Î†¤]‡é£Ž§üÇÏ;00[/N¹{÷¯åþð ásØSίæ:~›Œó©$ÿÃ_ªŸMGË`Üýn(wÚuh<õ ËMEk܆…{çË\®o>U‰_ÂÜ L€æQ ÿ'kΛ’ÿ¾ß×`J„*µÙá°|\ºPéïwC[ìèËÀºº=p²{+ÞlÇ¥6è#ù6ç… bmÌ5xxjœ“V#SÃ.ZÍÝôÃê/"êeÉêÅàU–8(FãPÎSÎÔS‰˜WÊ’ºzIHá;Â„Ž­—bÁB…¾Ñ_ãáõïå4dÒg~¶ñ<´“ÎÉ.ç垈㣌4Ó~“Î9ÇVbÃï%XšŒQ™ÇѰ2Ç™ÍD§²}¼‚w±†&Òy¶­ð#µÓ|7–:ã 5ª&ÔæÎÄxÃk|™#C.d Ò‡÷c¡ö\Ipž mk¬YȺ¸áñZèÁUfqàØ¶,ÎLa –ìäZºÏ€Wzø3…Ý«SÁ·¬y!ÂÊ$…Ømñ ¤fѧ®Âö¼1Ìá×msz MÎ|Ûñxèu øF6Àiq>]n½r½Ù0r 9üüùœ6¼Ä';/Ã1¯^Ž/2À«Ï¼G•֜³ï£ZÕM‡…7 Qý˜.nêüës0B·[òñtût½¬ÇžªlG¯ú¶?ê"–4¿Ye„'SÞsßBÂÑ¥úh5æ®eûɉâ"ÌX΂íN±R°ï/ATî¾Ëý^ºþ\8b”Åb<\pXv*¹#|yÛ5vkš‘~¥DTš‹h€¯BñÃ=ûkä¿XìÆŽ•^(˜x‚x–}£ãWm&ÓÿtÂí ì­Ï’³S †ÆÒÿöwÒXƒ0ƒëÍ"«Ì+a㤢¼'‚娤ЫõgÉ”e)dTåG^û¡J–ðÎ ›–z±ªŽ×à}ì,isžµ­IáNîÙ¡D1Í”}u= ûÊ%IL›†;žÇ·ßvãoOR3t„©7ó`æ gœƒ²ð"ÿ!±ò?Ìy–ž"â\ˆëpúº—]Zro˜|GÑúXLc¼GP+ñ<Ž6§<õ…NÌU%«ª’@½Ô¯í[U¶ ”|ƒ™t~5ù©@ÜFîÀù qT&9‰IÙ9ƒü†§ä›×kº‚;@íRº8™+v$îŒz –´øzÖåú ®O$É]bÌ•«e·isÊ’Ó˜Ì_NùýýyTÖ˜M”£{в Ÿ™ª)³Ø(w|s¶†^È*ކ™IÄó°‘r’ÁmçÜÙì ļÁ8ôZMªÁŒo18¾OšT¿çjöú³ODzcNÒLæÔdfyôÌIô‚®ÂL|>;ŠwZÇë£À©ì^²ÜŽÑCPÜfw¤@öØzÒµi$î«–xoµ ‚3Àçä-,¥+ZwAÖÉ "ÙžŠÅëxWuõ°ÌÕ–×ûˆ…ìi÷äY^¢+ê r²Y&z7îQ…%ê#X|ć¾4W!ŸÞá™hÑ­Aܦì"µT…-v^ á ÚDkßjø ò•è¿Ï𡤕Ϡ¿¬>m«ý‚·jpô3||ÿÿ•½:GÎc"5S¹à¡ýp»´Ô'ŽÀ§ otrOãL[1×Õð{w3Yü‹.æÜ1ß*ß‹^à†jæ‘ùÙ¥P´pM5mÅPmÀ {¹2#'4 Œ…XChU|—DÄÙW‘dz±7ö½„­fU0ð ”›|95|þNwÎbËg»â‡€…à>í ,Ùûz„Çó&]:…Íiçàúk3æ¢0 e¥LÐÝû–ùõÒ† •X“}û:S‘™MÄ0±wø$J—MΨÛõƛ،“ç¼þ…âê$õÖZ¶u¥«¾[†ËFÚ±Þ¥æ¿95 ð]„,§èt–Fôá×-¾L ü1lQ¨bZoàÎav/±šS½­Ãb…œIçÇ3˜I¤-¹ª³¤ê+¸ý<NP\˜˜¼Åô{ÀMx>‘2 %f;ð›C3É{?öéÚt˜yJïns… ù™‡Hï‚3œÈãA,u=ë@Æ¿ùí*¼½6Š ]Òg×ÓbqŽ,Ÿ;¶\™(Í Á©üû(ɼ¦~å`úëº?H"áö#ôz÷6²íŠ)˜"Iüæÿæå?¸ÁËöµ¿:{êQä¨ITJ&îêãÙþH=¸UåÅ”¥3àʪgy{;Y9gé,*Ä‚D)М”AÎYsì`Å]œõjyÓ7ƒý|/Õ¦ñ|.5RÓä »ìwov.8Ræ3…Ȗ裠³¶Pþ¡2 ëScm™Sø‚5 ÿ1˜ÅGŽg&å3ñò*r¶|þŸSî}® @d·Äaçt ×/H“X·ܵä<{úЛ¸ìÖ&λÒWÁÓˆÓí¥ì…¦9êjͶÏNä–îX„·]Û ùI6ýúÈót˜Î«*prSK˜ï‹>ÅQèAdVMEU˜S9¬¼›€ó?ï¡ûü+qýÂpä6J6\+<µ'.¢Kù_=Ö⋚WpÃùb¤ìí#\ßr èȶRi<ùX"ŒFñ©¦ežN„Ào‡ Y)É'æ"t.àqÑ ‘†½ ÀR¯Ù,ºxUCÉU5Þ˜´dû»¶ý&iÖ5'·uÔД¿Ü`šÁ'T[àF‚†O³þ[ÒäŽî3¼)=ƒ±ˆe¬UB wŸC¡Kî¾F76€£rÉ¿ £M›B L{w\Nî®GÇ ñ¸''ñÅô<Zòžó-RgÏCû?fغnE›Áõã(µŸÀ¾Î‰‹ce»R •E6ÄÎt ¶»žÂç ÏòN³³f³êçìqKÆê8´›C®ËÌA«9ïàƒjf7ÀÐç¹öëZ.âûÕJÔ«é#ÔÁS¬Å`²æåL°ønÃÎ\Éè´üö4ü3c ë]ÞĬú…ÙßÏpÖro6N\Œäž½³–ªãç9s` õ´4cÛøH. W—xž†©ý§aÇWå‰yаÔOç¹€òK\‚_7çbÅÍïœOûo(>—Šz# ìÁ–<¬,%öKNAK9ç?Õ;(vïèÁ óãýØÝˆF®¦v/ið9a‘ÛÞmŸ8p ?e'Ž£ÂÜO¸î¤ôäsg£¨èâ•èäsWW%¶úÝ{A#e^îÒbï½ÅðLX)³p[†„9ÙO6i±¡)Ф>ñ¾ôkã4–£Ó`×¢Íø£ã_MÙˆt—,€F Îù[ 6–œá¯Û̓E|(™å¡f³H,“e+N”bG†6Þ}iEr¼¼Qe\غrø˜)ÆŽÇÕÁÑT rtÃ%NrŒ&‘Ç?ò‘¢ÃÖðnÛE¶„¬çVg´ƒÿYCü6Š8nú ‡ÇýåòjlÑëPü¦%N5†¢Ñcå ªžŒ» aøÕ^üx¥­^¼[8Ø] 6Á»Ûuø`ÎÒvù¼w‘´Í¤§-fÙ;2ÄÝ]À6T>ým då—prH$ ÜK%`O®Ù•¤HÞ†¤7+ý¿5@ª»DÏ.fæÅ6Ì®Y’dÞ?DNÄì‚mIQ¼¢rEb¨oÈÀd¹/ðß: Ú€CàâE—=CŽ: ãsK¸¼x»iz¤­ÏÆpá.®IÚ ¼{ùá6kð„w9-,Cì×öýÕz?Ð"å#ÍS;×Ö~¢¿¢ÓIÄ5í#*^,#=’ éc<¶Ü¶˜ÍÊ'\‡äi_ÀLôZ”¼çªhÄmûQíòvüÀ=hoÐ'>Ù8ÛTƒ =è‡ÁR\ü“3¬#¿Ë´>â¶OÑÆE‰Þ—5dYOšøqûÆ›²WÓ]ئ+ã9‘·!4 ‹ÜðÕ‚C°©Ê‘Å$ì#sÌBP†!'F±¾=Zìèþv.×\„œ—ÌÄÕš1ñ”[Q¢œÛh—¿¶S%œ®ûñDg6q÷%¥ "!«HØŠšE'àñs#r§7­ïŸf×W¥‘‹nÑÝ‚-$‘%Reþf|ñc30 b¡-Úìä²}p­p³?îEjÍñˆU)xŸÅ3㼸Ý*pÜ£#Ôó0Ÿ˜ð biÝÍ,ñW€ü…a-ò¥¶mx$<š,!B·+’‹óÇ‘WªçpÍ•|ЩÃjfH'¼óôHýM4t…¹~à˜)Lþ©‚¦Æ,¼lp.Í “v„sÎXæhAìdz(òñíkN¤Ú4ÝŸŒ‚ïÑ&²ûg_Àv[ ¹ 0óÚWÔÕyÇõ ÁÒÎ[h§üœ³x0 g¸Û‘êþ*ØÇ>ºF£Ÿ:±Žˆ(’dŒ£®l…«K{¸·]òij—¡ø¸‰0º4”®Z‰W¯ÄЩi-¸g×>‡=+@ï ó÷çg¨±_½÷9íÑ}Tòe-ßsH“ý™$ g Ñ|G<çåØLC[À¼}6ñÝ£jŠè?ð—cw˜Ë÷j þã #”ŽnÙOàÞ¦xrU]–ÿšæ4½–(¿NX­fJöh™±a-Ò?ã#º¯%žC¹(P¢IÞâbæte.ÞòþçÒÛÍ»ój>˼”J¢™`Ûô…®oF¹]b¤vâÞ.ûOè}ÍyOP©|:Huh1¯äÉäB‘**ì‹WÞVØ}ɸA6–¾ÕF ]>\*qcb…± ¡Œë„‡&þYC%£“É•_¶X{á(ÎîOÀC7”ɬˣX­v9¤Í°à8PÊWWÑgÛJp‰ù®=îËzÈ}7SlîÐaßôjñ¨¼¾î¯@õ[ønN*Þ·¾:2ñjéTrÒTžH­ä2‡BíÊ—°ì Ÿ}ÇM¡ÊDã=Â÷ü© J ›ÉçÞJê!Ð&«êÏãÒ°Àò_»èýpç¨êÕá¡<Œ¶’"<Õ!®hî0ïÏ,{\úUW﨣‘ $0{l>ÿúÛ|¤rŽË½å‰ekŠQ²ïþ–#AkõÁYã$|ý¤ˆeÒG˜ùµpœé,EÆ‹Wâùæ||¶p\å»AÍ 6)rK<æ:“žjL7'‹]‹/Üû‹`s¹6+Ñ= 2 WR{)e4áb÷•o£ÌBo¼ÊS¿5²¸õÂfX'™Æß]+Dê·Æa‡b®ó²(›S7¾š¶ô»¸;h»pL·+ ö¹²qSf0-—~Æ,íëÿÖ§•Ïù~â1ÄÚ$‡GOß5õwg[㥗հfû ¾è2%z:‘xáÁ Ïn†OÑèÞ¼º"/@ý)D# ¼ pÇ_Sù÷!ÈÿɧW‚S›­€»F\ɨÏ/qÑFKl˜CqÁ` Ùpîš@<®¤`’Õeù›«ØMaÆÏô=Á·>û1v¥ØXƒ ›ú{(³jQct"T9Λ…J\ƒ‘}ÀÛ¡F.&à×±¨#Zm[OcÙôc\Èë-ÜîÒA.ݾ?Z²Žõ;9ÁÑjž»`œ¢ sí›M¥Ô6àÂI°Oø3.üá†ÖÓ᜙$ñjÉÁÈë—QgÅ|œSéΗ#@–µ%SMÝÁâþßý¶n§’¡¿©á}pÌør•“xõå[°Ê…¨;Où.Ö¨²rÍPÔ¥%fRرHé;Žf³ã¬Õ ­ž>Õ‰£ëÍ;Ðë¬7ÖgEcÌæùìr4¬‡¨§×à…¢4TXYs=Ú¦h’«GÒÛM؇}îØe"»nÕÒsU38žàTòKŒá›v)Þ_“í2ÌX¤œ;•R‰UyvDPÞ“™™n…F©éîægýáÆúD|ágWªQH|3®à™ƒýUM¸~^M$ãbKñóµµÄ{â{Pù1ŠcZÑûrêÏ~^Ÿ‚pB-]vèÔz±¦Ó­prw)VnºÁÍh|Ë×MT'Þp+çêÂW÷nl9ô£ÆfÓ±‹pÆÖ8bÇqfë< ½jÛunþ(ƒëŽ´ò~$ÙaëÏL¨þ¦€ž‹J9#eâqïÕSð‡€ÙÇ¡uJ'*‡tó¾«sòúcHŒÙHLÜÅ]ê9v ßûwÒ¥¿`Jh.ø¸–¾e¾Zø¸–/íà7N»N+v̓ݻåñ¦â}Äðåtõö6,óÞÊD” ­© ÍqßëàÜ`ŒöKãøu'dU>‘@Ÿ­Ïæù¹£çÊGà0x öÊt¡NS>®ù&ÆêgºÐ-Ýü3yà” Ãä¤éý;`oò ?góæ„ïøw¨Üµ‹•1·ìH‡E‡#PwÔ4T›ÇkøÐ³.¥ÀõôÍœâÙRÐ(„‡ïà–ÇP]~Ä$0ÜS`ÁŽzfq?—Fó7iN!K”Ñçž»~õ5ÊU¦é[ßpc^ðÈßkð-éoÐ;lLÿ^Ôç|ýs.\œ ‘ ›©ÑnA\xç<–‹ ä`á @¥ø%tq‡ »xGˆÑ¼éLeûKN–ûŽ×{.ÔKŽa†qlBûnhØQí¿>£ãõ>*¿5úï·Ð$ãÓ`ÿÞ^ª F“â¬l.î¥F¿ŸÁ§±ëÁ'>’¿â·5«Oa»KgâžÒ¤{ÔR)´•ý]Ѽ!‹ßŸ„¹{ò»Ê¥Qò-n2_W^‘œŸÃØÃÙ„%”aß“l4V…»Íú«:§ƒÄ'i6ïq(›‹“ab;l”ÑÝ!'”qI•xaqz$«öx½(ÄæGaæÙëàõ•žW; ZÎ1šv»aEØN<ã"ê„­#µ#;øÂF"ä ü5LÉ{ËÛ:.ˆí7¨‚¨%Q0Eb<½só1¿­í:f4Ïb7's‡—Yw¼&À#/ãJØGÙ­plÏgŒŸ> o-Þ ºŽw`#“Fæ²"OÔ © ‚»:éÏfQ2Gs;†mÉä\õسmª¬$èECÙÑÕ8)xý_3ô²òÁô¥ ^]WO+yp~i+˜œ`Õ5.Ì8kUÝ k„Ù8ùvа"U®áö4 nWülÿ%L~ÍØ‰uoÌñŒì7zúí&Ü|õ|ž°$w§ÀëŸÐíYŒŸ÷ˆ±[´„6ØÇòS˜¿úú–ñÙ˜4.Y‰3u£œôI’np–mOI„`'+Þåc|Zfœ<ÍX¾\ ÜÛwû5µ½#?c³uÜ,Ü㎞À¬Œ0Z¥ŽžQÈe^uÀË Y8Ö &s›yÓ9§µ¸Ö¹ž¿cw9Œ^¨Ln¸\i›Ï´E× Ÿ?Â̬@x~æ]z¸”\-vâù0>é⫉Ç8§w\‚ú£¸%c4ÚTÇâ±;£Øü°»Üßõ”-@GÀ™XV^Áô%\ÜúJx`¿Bº±ìç1¨ÐiÀŽâz³ÙÖ ´Éƒ3>,F®ä=Æa q²ú³1¬šÜ ’,ƒ~z]mÁjœo"ô›NÇ ¥©x 0m œ™Š… æ–wãlC2?Ì ó7æ*˜óÞŽßÎM™“ˆgV-Bíñ20x9Œ¦o¯âT×Àï_“ÿòm}¨;ÆÁÅ I‘àý'dúÇÍÔq©"UízC»¦È’ôçÇà–œ)É;¦U»s!:ÿ"~z‚”Í檦%›¿㊵-|·TY׫à?Àµ;¦à¥dC¢½¤ ¿±“Üqù\Qþ…¯ø’Ñ¿§‘á¢,îAàPjSbË­KqÍ3&5ÁlÚv”®ß?•®vûÃj ¬ fà«I£¡qÊfê­Æ¾ÿ÷îHWIîþò'pêQöÿõ÷”=ÞBŸ¤Ds¹OñYX0Ø ¾s€©·”Ãìm~Ü;•cp<£‹³¦hTp \c0ü©&ùø­R_=Eµý^dC’(Û»¸jã˜g–cÜÁÿ'6ùS.Þè=w1»FÖŽ¹õW+HáýP® Oý°ï¾?&ïC½á#°biˆýï ‡yó.l§Òªùï¹ç+KððhSˆ½0„ÄXjâ0&:”Ñ’î;àfìê»´84[¶âtb5Ç$Î ÀŽÌ¸åv•¯8‹zð¥JöÚ_ç­ZìØéq`û©ôÀÓbüâ3Bä±E|ÀÃtõçI°ëp}k’ÄÕvLfÕÏbðÊ* Öh3‰æË5¢Qâ1(èah6“uÓE*¨(ÿ3öY‡I&\cžâr.#._tŒ.ÐIàNï°á‡eðöµšï­ÀÑÎ3Ñ@¦Í_ɳ5O°ðó!¸ÙÏG¹ò8.ä¡?V(?ã–,­E‡µg¹ÐØu~1HóLÑ"HšÕ§åó«4d¯ã%\麞âÀûxžŽŠSä<¢Dà´Ülx IxC<”õSŸDBªE Ü(Ô&ÝŽ@ºúºéœbqÖqÛx&“ãó²!Þ}Æø?¦¿¦h°ªÚ¤#HûÌ·“¶Š äg#9ßÉFçÍÇ/ ÿ<ƒ­õYàÙ"œ¯MÐ*_ƒÈälCÞÄL\E¶“Ö–÷ø%Þ¶‰…5‘ó‰[–>yi.Ë×VÀ¯ eäcˆ5&º-‡‡'`¶m4Êó¯ æ§<ü:I bq©E0ò•c°û•{áÒ±vØ.aE\¾ÜÅ ÇÀÎr̒ųZu¸øçQuÀÝ÷Tÿ®14©á-q¹†ª[°õŒSΈ%ú*¹Ü_·±.” 8Pé›Ù¹úÀÁÀ¼fX|÷篱פŽÁ#wù¡wÞ€ùÐ,Þ'ï|˜¼î íp$ _ Pq4]˜®I‚ç•wê侞(H—¿j’ QÓq{o…¹ýT…”j˱n»h”ùXD”¿N%šή9ŒÜXWœªpÝåos%æñ"4öu;ü÷žòq{#°Éç '7ù(ç—kL4E„ÇÆ°xøõUŠ<:µ„æñ$1#G˜Ér¯>ïÃÇ+F°êV 5¯ˆÅ¼s ˜ã¯­œ@ —%ÑËÒœ~°?8Ï@ËHn_ùD–yaïE‚™l˜ËöÞÊÖtº£q~*ç–„Án†øç¶ ¾lz¾§ èœçñ§® +Íœ€vq|¼¿± Á¥à—ø†ÏQâ¯~€›€‘IÇðe‚¸ èƒK·?11äSF4úãc´|—5¥¯Ú†=^%Ëš†yôïù°çù#h»ÊçµVFã–]_aWÆúH<§·ðe¥F±žmS!äÚ88bµ õvsoŽ‚h`ZÇ\„Á †äŒ $é¬gã²íYäEè8Ê£zó˜ê×G°i¶,©ž¯Á"AKïAXîÉ,4Uˆ…ò”l: &îäQS'¿ü«,ØëèÁ¹°ô¬±|Dî:Š>NæÿƒëÇò{ã¶Êž‰Œ¬ˆŒ)Þs"ZR*•”JijIiØ#+3!##•2Þç>)£´‡d4¤"ÚCÓÏ÷÷×»ÎyÎý¼Ï9Ÿq]Ÿëæ-Ê „=OfQ•F;6××€ïuÄÈGÊìÂg{²jÂæϱ°ó]é~‡¯dà™¯upq­Ü¶‹S~4†I;¦£|—(“RY€ÕŠî£Ú€+»ßÒdÙú¢ «ïNA+÷âÄtܱ}ë¯Å™¤£Ð™ÂÛßò/ÅÙ²U3Ø•¬Hœ|“ÛkÀÞmÝKFÎjÒÛºlûÖ‡àõf*Ó7ÆÔ†N{UßÿäÊR,Ø‘Kx6a ý©¡‡k¡atO‹ée+ÃA7è|²­ä%•æ†Câ@äÒ+èfVøÕt:;¹[ât‡À캫)êmÛ÷bym:ÈÏÕc‘¯:̈Vü„1#qh¤3Ž ÖE@ÃZt6Æk­)¸÷M*6œÇ¡³ñ’Ê1æ>O‰^hŒ‚uç]P zTë rÓRSQx`ËÈá\¿‹屺ø• ÛžBþd?Æ—]h÷kãÇk°ÉžÛÙ“×)M*²°ßU¾š*±_?Vq¢&áT÷¡5U]Èá¦×ЬÚC”%Û`¾—<©UìdÖú"‘ Ða¤’WÔ‘™ l§Nf¾°t{îrb*[Ù¢ ³±ø¼)•V»ÞÓ€‘Ó¸Éé/©])ÀL;{‰Áš§pðX!7uÇXÚ¼9ž÷Òü'ñ;’N²Ž,ÃçêKumØzÙ!4*•£îbÅPYE“t?Ãê –ó €5ˆbQm3i‚˜:-Ëú…Û%_ãÎBûÝǰïͰs­,=T Án¡ÞEi*t¬‚殓f bªÉïUr¸vÖl°}Ô ýÁïŽ/qt,oF¢%_R-˜-û®Áy—q–u>ti“:ô7·À]hÌzêÈT–)0Ù¶÷ºoÌ¿:È[1‹YMÇW%“•QÖàmsœû~4ÇÑÈð]Ô4+•(á˜Î œÎº¬¿Ï!Ð0&~s r#xT´ ˜Û°Mv n‚cs—R×Ô@œv •'9Ù†5^±eÕN÷ðœÅ5,õ*G~¶æ…ø²¸¤( £ŠjaÜúrQØ^‹ºÍÙªë¹è6M—Ýy6Ê4¡Áçš<Š6ÎĺzþV©ù—íÂ)7CÖü2ôHxcLDðû/}0„‘lQzî—9{Ý MoÞqa¹/:@R¶›ÛªÔðmï®Õë6ú5Í!“f¦1!£Ï¤ÐÞŒÊÍ‹…ƒ¦_Ñ»‹¿ à{O (õˆ¿sÓ0ti LEaa9ºÏ¤™ì{h„^«Á¥y"« [ ]¹h9&\¯ ÃÁ^-*ºl® ÿ€¢¼æQþ^íв(ÍLNò>¶ªu {lÉ|PÛ¬KˆÃÏ¿ ‡HÖ%ïŠÈ?Ç<©ÃsiÑö -<‡÷|"L”ò¿µ ‚Ôèg9½ç»™ˆapø&º4ߎ,6¦Jº õD…]íMǧ£0Ø×…ê^;Ì.ÞüE´ic¿Odœ9Û9$7N`goTqðáŸhÒ=ö/Aê ЩánX2«=æ1õÈ•š"ru îí§ Uy»Ã÷’èØj9~çv3æ>v!M«1^É3XÖrS«Z°_øguÅ Ã8Ç9ì¹ßEØê™Šë¯ÎÁu©Ô*P…I5xÓ)ÎÌËÌ Ró³½é Üê|VbË^ôäÀgÍ@ºÙn2TVèÑå%ã©ïW<ëâDUέÁÉ¢Wy}â¹þ•Z âøŒS\… lS‚ÏZSáõ¿°ü½± :ZûDØ·€ ¨râì˜ñ§/ÀVìѹddn+ñŸUÏ?ýÒ ÞL£éËE8Ûq¬np¹YFqÙØ×­}ž½ŽÁ—eOHeC™xDõsEü:•¸Fi”–7âíü ²÷M ®_ø79c¼Ü|æm¶¥íkHXÀ!8üåL÷ËÆ·5K©Í$w›Ñy¸¬ò Íèów¢ÁÓóœ’:ƒ'[ð”¶LK{ò.ãUã±tÒ?7˜ßÔ¶'ª‘ŸuûvóžÒàü±&ÞÒ¦$Ù  #qÅúôÕëøX’Š}Úá¦ÿD"›ÛF؆pUà|_ƒN½lMˆºnwÅ©YŠhµüVæEqµj,á<×¥@EÁ¦ŠXÑú‡/Ԣ뮢ÿ6IØ2> CE Ðm2í„llÀyñNxK& vÕµàŸ_è:Žïž¸Ú(gže ANx±ø*wmX‹^ÙåBm¯/DO·›˜oËéûäÒóËV²Y[õX’¸¨M̨ù~+.MÀŠx­ûç’Ã[Æ1·¸è3IáéÌÚˆìÇ::wwŠÿN¢ÇVñnHÑ­{œ±óͽ½ˆ²Ã™Wñ@¾!=’¤9ZÇCòÙØòü!pä÷áD…™` ¼ž¼Y/eÍÙ´ès$kd{8‡ÞØmAbj¥f·¨#7/ÎÞ†^Fù»ÚÔøtä¬:ŠS¯ÕÂÔ ú#Ç…õ>ž‡¾&–©2ŒØÍ¢ç¬©×Àa4ïØ‹wkÞBã vã(Õ3Û¸ Wh¬AŸŒc½ûÎbzÄj ®Y}Lýਫ਼óÄõaÕ÷ zѺtÖ÷4Ó²£û/Q\?Ó _œUfo’3è½üY,uÉ{RõM‰„)Ó°û]¼öHXœVŶaÊÍÃܲ‚M°Í' ŒTÓ!~š ›r;ýã±Y¦ÓØå»[ 7SXŒвëZ,cÆJ4e´ŒÛE©"eg²•=*tÆ,FÒD&Ò;Û»pVC4®¬‚Ûy(»ª;.WB¯ °š§ („NÀÏÓÃðœö4¢ ü3|éÎtÊŒT»-œé;oÄz¨—Eþ=bK5$ؾj˜ž?D*-§©u}(ÚVÊ‘ ™BWuúBuÁ\¶òýFš:þ;ì8:ŠåÚÓØšüÑûÝ G‡ÅsáŸó\x–¨È&Ëz±"÷¸ta“ÎÎ C1o`²ü<–•bÀB”ÜàƒT"7ÏÞƒzj„œW¨ êd8ÅJ—¡[“ Õo.ÆEoÓ!Ç…4Ñ/‚Kø»†Æô22˜MØð.¤ÎÞR¥ëã®CV@ Oê»4ÿì1Ž5xËmü1ÂÛFˆ†,/øâtQŠš¨À–Œ«ã³ËHéeU6i¾6TÝçŽ ­¹ê=äЊ½¥ßâ»aq²8«ÓÔýàž*­­–`[ÍÏpu™KÙZ)¶W86uþ$kþI1K_®ÒÞ×óÉ¡gzÔ£/ïŽ-åº_ÔÁ†Žë$ÑSŽ®UóaeãÃñEÝ)Ø©bÀR¦1݈ü1Ê¡xWüÚ~Gk×þy»hP•ŸÎN2§6^µ\U/VF²ç§Öã½Ò­(u2?,ao_^ÆfË+p­ý.Ø^ K×s/êTðíÙ{2\­¯s'\7âÅeæðÁ+ÑdêUŠÃ>çÛèÔ¦^aðn®×nÂýùÿÿÍ\¡·îf$ý,ß,»‡—Ñ[éåÜ;I{^«ò(†½YÀ^ÞžŠÁåÜÒp`^’“¹W²i¸òO¤-3f;ÇM¤‰’æxŸÓ>»Ð]Ëá¹ ]¼VÏ -_ª¡æ:Ç­œr”›p;œèÀÍ-.p|F6,‰Çë^áïcrtóå/Øâ:޶c÷C%qAc=Zí‹éù•a¸ñFï§`¬ñŽFKGaZêÓ‰§W™Ó¿GOcº÷f1ôÊ·çwìVå¶C˜O5Ä´«uÜù{‡á©Ï"·U J—Jñ‹z‡±l>8…`θfnïkL“Œ†»gùoý2`[.X/™ÂÖ|ØŠc¥gƒï¡·!Ú½¨¯>wº,&ÆHSõ×k°uÏx-_Ï„EÊÎügúêôª¾+@'8væ)?a…&ªmg#ò‡\è“z(y) ¢Þî@³Upßã&ì]u Mjáyf,jÕ¤‚i ôC~$œ ÎDÕaÇü/P½‰m-yǬ¤Xêr}îãÓþãäU\üo1œ Ùœ§DœÈ¾B¬6,ç)¿ÒgËz±´û…hkó,Ž—£ÀiXS¾eBvÁ¯­cñÕ” ê¿LL;Uè^òe41qÇkh´ib¡IšTèŸÍÓeVºlâc–»G–4˜P¶ ©},¥èäͳçþmS†Ñ5¸Ç¿=±fªU—y„õg–±#‹Œ™˜Œ:UüMõ]¢1…ç„å»eh.-# gžßä*ô¯¥wªg³½õGmîï›Jãnûrá÷ËP¡´÷I°GJk@uÏ&˜p&  ÚºYQ]Mº´J›D8ЉËJ1Cù*#RÛØ•½† m!Êÿ /IwNÄGæ·¡¢Æ”ªéI±YTT7xˆÀ·AÀ*¯¥±ã?3½ƒ{©ç$E*l/Œ– øIóîáäÏíxnëT¶É³Vù¨ÑÇAöàí¢Ê;5ø ýŒœéæ½Â4ÚÉ.–co¬/Ëø[J~† Òóçä¹[·˜Ý·¿èûÌšÖ2cîã`Ž¿··vq×÷M[–àp?šLW­ÎGu˧8õ‹$kLߊ׺à@tæë5^WF»¿óÐoÖüý[ ^^8DE/>IJ¥Œs OdÓ#}É䃦TQM–U^Ô¦×Ü1Gl)Õ–,Æ„»—pÂ3êjÏžN!Ï„½ äÀ$¬Q™ Ö*™W4¼HwÃXI]ö}i>Š7áÈ•V¾ø•P^YY¶¾ý/¿VÀcÁOØÒâH¯þËë<« ‹ƒ«@êv%ò\èŽR,Ì|,=u0 ëf~㤼…Ù|‰X¸ˆ7Z¨cêB8w»›Û'CPÓÀ…þ4½ƒFÏwѳ©à+2––Æ‹SuBW¼™Äf˜I³­»8%.Áʇ<æšø)v˜¿ÈÄ|ô~é]M(*ó%ãäØ]™8Z»a:;"¯‡ßzz¹q~UpÜø—ÚLªwêâé®”÷,ŽM”Õ߇ÂL8õ‹úpëóS¼¢ØÞvâ£GºôɨfÊw;O®ßpÅÏ¡°äõcNQQ"½ºM¢Êà÷µã\n¢(‹-„K¢ª ‡‹Ïþår?º‚ÎÛ°hÒ[róÈT˜¿6Ó+}kƒ—Òêëë¡ï¥ ûÐò‰Ë6›D’§j³ó^JteÇ[Èݱ“uf©×ÈÓ§™–Öö24ƒ 9•Çÿä.Bã-ƒÐö¹Xô`“éÀÚIljIæÊ·¼A·<¨CëÂó`¶g}Z²–5ßÌÄ}ÃùÖû²î‚¢U*H^ÛÇ&U¶£øê+Ôàb Sœ]À$]¦c}ý-wwnßñDšóÉòË*4]¿ Ûî„’e\ÜælÓ¡BüG(-í±dN×±ÂK’tðÛDZbÁ6~“c×ͦг*Çé?ÉÄz¥>f:ˆÒô/PsØ‹+¶a'ï.cËf³—yÍ„_›]zTFÅ}!Ó¤¯µ]mx1‚[§sÕ“b±¶1€( l™À;¶-ý>õ6ÿåI®l°OøªFX»œÈÀv>Ëô´Äuvðhî<6²D™>út™¬1^JííUÈ ˜¸ñ0É ŠÁ×¶ðçq7vv£üW.L·ç™Ü‚¡&y\¯çˆtgÂÆ—ÓQ4t,×ôD5L\Sýûæ&¤‹WÏ–B›ˆ* N‘D‘u1çrá=ÜcDÛÁø½ ÌOeÁWnñè(_+MYÆj„MA|Õ*ÞN9ÚTK&ºÜƒdÇWàhx–ýì ÿÕdxX ¥*ÛpèÚRTˆ”áGœ %rOT€_žÇ›x pBo0¯ÇXšL^BŽòkë\TS*CÍÈP¸µ,—3:QˆÕ6·‘[»ß„f¯9÷„|éðÚIt¢ªm¬_Måtš!²[Š:dÊÓþìr^ö0¶<×0±°ßÑ¢ÿäe¨ßíF8¼âÜØ1D,úCðæðÜéÎŽtXÓ­¢¢¤26¨™>Á•W¼pÛìnðZwFå-ÑVC((1f6Gð蕜“ÏnºOü9´%|â/Š… ‹4ˆSÄKJÖĈº“åê/Àyü.:½#66ÝÄwÆ1°™š1¯Û3鋘C´F¢ŒWž;m¿‚iŸÃN\ý/ŸKñ¹«,IkÊqu¥ßìáââ'¼c?½ òMl—;M¶(HÁu­uÄ­Î}i3Õ ×°wÀ.Ä÷£Zq¡½!þZþ?>ç.üq¸cŠŠ8·z{Þ­Î(4Û'JNÁm¯xº¿’¹L~£jøñ~ X©«Ñ5v;0\Kˆfw¬‡‹Wnâİx~ô#´ùêËÍðƒË% @Z= ¸sݘÅdñåà\ö ú9'­o‰ž†—¡Z·EÑ µå¬Ó§”s;'â³Ú0lCΪ {•oâ]åï|ËC|£Á1¸M^nN‡Vð ‚qæÓ@î¦$õnωœÅ7¢“´ïÉæf=Å÷¡/ÉxÃé,Æ\KçÑŽ-oÈÏHN';¤ ÔXŸ‰* °‹ør{n4ËE¹ë Üôª>]õÖ¢=góÖù÷cÔ’ñÔ²5 Û½ YÊî0;Ë‚¾|—­GAušœu¼I,˜œÇô_¤-I½ £kR‹Ï@+z+vWŠÓ&/Svvƒ6 ÿTÌ=}ªI Ú^àÖ#t¥äu\—çMùE!°BjöøíÀEG_CNq(Šo E‡½äñ6?Üý<ʉÐç%ˆ«ó.FÆP¯þ;°Áõ·7iãíR” . ÉÃ_\‚;…{'ïÓD·$,Ú˜ ž¢¡{B?Šû™¯`C"Ÿ;qÒ/9ŠõSâîàëG8¢ YáíáÜÞõ3·(ÞÙÃv¿G3[;ºnÝ6u™]Ô¿“Nê¡ùÏ.3uêDK9?zóe)¯ueòZ‰8Ç>–lþª l¬M§¾0ƒäÊpÚ„<Ú0ÉÍl€Ú´ñlAÌ6ÞmTn¾gö~ÂWëô¡õØVZöD”3[Ïc/;¼p]v ›f%Æü 4é¿ÐõTËï4¶Ý\©¥öøcò\ö80>ÄÒ'óÁþôAº,| ÌhüKGÀ`䮸<.«ZǘŽ(,¿Æ÷I<‡ÑwUÀQPš¾ž¦ÉÜ "ñ\ÑQ»6s„_ >¡O¯¤sW®e8Ò“æQ‘CvXõcÞ¬bö‰·@èñVØù½d«w±?û÷“«ZMŒjîÃȵØÔ(Fçho¢~ßÃyùìÁâi´ê¡}3û*:Á­O+ؽcå4̯ujTY©Ì4"4èL3Ù…leZqñ¯"! 6§TÀåQ¿ÝS€S!ZKÙÅSÑôéÏXznÈ”} +ƒêâN<#Ív9«qóžrNÆç¨A„èÜ —Î=…±Aö¸UD“¹N-ÙƒzxÍí$¾ŠßD²V=#Ÿëlžž‘Áßûu1P4„Oÿ扥¨aæÖôùÂÎhdâø^EÐË™ ÎZ,x§.[,W †‰“ñÅš`æöþÚœÓàÆ^‚‡_„y»#`¶z<?.öÛ)Þø•E½°¸íkÑ Ü«5('¢Œ¦Õ8|¶vìâ&n CƒšéèÔV®f`v<‚HN¡[¿£ÂÑxÞB‘}¬pp&•ëàÃX¿(¦'  º Övc}“kâgúƒŸŒ,\޾ÊE¬äãÿq%™Ëxlq»ìpô¦ÒÔÅÒ¢ #ð-„(²gmÛXœ¶ H´…Ž:Ö(ôB…æs·k7bþ¦.tÖYÎ:v_Íl)oF¡KðIæ|ºL·žëç6Y®¤©Xøâ•tÚ‡Kàç'ÆòºlØ‚è$¦}Ñ”.áí˜ìˇ_Ø aN/«Ÿ§+‹µX× Öõô"ÖÎaFjGÙÙ9ü"µ³Ç'á¶;+Yý§f8|2ž÷Ý(Nìr† {EéÑQ>ù±>šjÝÚA‡SôQ9ólݯ ®™;©SßöçÓnh,4£7=>€“ó('Àc”:º§oªÁÚ²I´Q£Ò´ŒXhþy¤8[¶ô™˜ þþ4–Z/î"{ï®À¯’?ॼ"#­ÁLYí¼{œ –ëÃ!»twI-ŸÀ)tLcçZÀ‘…8G¦ç.wÁ".êL%Ji³‚,TZÁöL”gW¾·àÄšxx•'”MNÅý Ìά=äé,¹qLðª]>ç*Ìîy…ï«Î@ò¬•ø(ä mX.Ã. Ü›•7±ÌD†¼.ÀlAqFÿùgYXÜÎC/ îó’EL³>šþº½ã¶Î`¶sµÈJ—TNÚmx­s¦ ý•áa´--.’{^½ÃÂ\mÚØ}ókÈš÷z‰Sáh¹=wÆ}ÿ#±vOe²Ç2 Ïþ°c;—0å;WIX]ùÙrŒ¶¹~€µÞýd™Ø šýN>4y¡YÀlžY€ì\1ž!6Ì­–rÝûøæÎRháÂa›œÂOE¦0A„N>ºƒÅ·ÄBÔé P5Ä[³¶ѯ“C¢BߌƒœI4LÝ:€—|záÎO:îÇ&h=®ŸÊøÞ¾²ÜÙè'ÅüµoïcÌ–b¶×Òšû•w‚î:SÃYÇü ¤1ÈMy²ý¼,ÓoÐü¹ŽDîÂüÎbx÷ üãÃ/—.8Iüa‚Ÿ->€Wœ‡õvÞ‘Ë ü¬4iâU-›ÙQ1+ 7íé%ÛN‚¦ö\\¦{,*¡¼ö) é:'ûïàLèägm­“ƨa?l³Á;"ŸDãØä4ìÐà±Óªg0vv ŽËàâª.âí,ðú¼J²ßqûj/À™E÷ÀæŽ~8ÌšÒ»!{‚}å¿/ÄœŽsf²{yçÅ))½(ƒ¾jŒ\\Iv–Í—òDŒ_ÆÛ൜Æò§ßË­.–Ç7{ñarÌÕqWcßé›6°®y×tãZœoà††_€h\n'~—b0þÜ6T±^¦±°}7mf íãÎô¿ÏK‰pã~×´¾‚÷æÆn¶‚jŒ(ëË€¤ý ªÑe}\°û=îùýÇdÞ*¸B›]S`_]Iþì©LúçnþÒ-BáP™ýÙ?Ÿ)ĦÁÏ€p’ùâß7à‚ÍR5oì«7FÝ5!töÓв`ãî┹N2¸›TG‘í0}ŸÞ€"P¸i&묦ÁÑ]Ú8}²èujR%ËýYðÄS‰Îh,àøñÃ0’FìkˆZÕÚÿD~œjLçj¥â´Üw˜²À{Õ PÐGC™¸ñW”j~øËý<ú ߨΠsåÁµÓ5ÈÿíÞYIð¦cÜ}Ô‚ShÁ§¿Žp8ót·ñ7éËa×:vÂ0R<†9yÇ;ènºˆÛ>Ú p»žØÆ<Åöë‹0yo!fY›Ð‘¢j¨Ð. ±Cim“oæÚãzž7;ïm`¡r,W¹2 Pˆ€êÌð*'‹$ïôåvjÊÑÕ­#P»1–sÌ,‚1‘ 3-¼ú»¦ä‘ Ÿ¹=+–± §¾r‹S¬Ðùt ä—&’Þ×ô fν$D³.gr³V[³ÃAà!ýV÷ÒÛq1¸¦^ëÔ­¹]I°ðæuÜXWP—þm×f·¯¹°¤““AÃò-6%MBõík¸On)ð¢=‡é3þš øxÌⓜQÝó²…5Ø›X³„åm9Š·Þ6Á©¯Á¼ªdztÎT4n‚wÙaíH”ñ¹KŒiµ®ŒœY‚‰Ž¸ä.(4.e¥ pú cÚ9U‹YõáxéÔ˜Œ¸%ÿËI”dIê(¼”Ï,è’£¹’ãY‡_:I¶wïs‚®ÅýheæIO΢¯îâ¶ÏtR0ïk(?‰…žì”s,‘[¢ ;öê³¹î‡ÙI–‹sÅ0©PŒ^ê<ÄŠ†è/ë\"h©ÂŠò¸w™Õõa¨ý}ód9¼Ì²Ï˜3½ê$¨‰ÕÁ­ŽØ,vûËÓ`i„I}¬Âì^¼C¹Ù¦Ðýr!T{‚jT2f 5€@ã’™ƒ 2éÄÈÔî¹Ê£Os¯º–s»CÂᢥ´¨¿â^›ÝÑóßpÇ»%8ܺæõcOi0GieòéáT4×Å «"ˆæU€Á¦ v)›+P(‡jSµÅ™èö èvÖÜØúá°õƒ ]<ûäXuÑ8)Š*OwaÃaoX—  ùÄ#J¦N ˜€ÚcîÕÚf£ùqðf¥î•»E‚ºŠG±kÜÑÿ§ Á=Ûó0Oe3‹_?Mpõ±wø¥ ðlYâ¥`”¿t“´ ëD# ¤Áõ͆±Z+©NÊ,T ÁÏãàÔïX^&ˬŸQØñª³Ïˆ‚(gN²Ò­!¢Ÿ‚Üj'pÚÙÂ5;àØà²•dóûï$ëQm¬è ïß÷r™»wã·æPÞ J<('À Rwd/ž"g[ áókaÎTo%ž—W`¿Æ…‚­‚_Ýî9ï :¦ÔmÍ ÊI“Îcoë›°S²=ÍDÁ±é4¦·{rB|ÈÞw?ðENYÿw%ó…0ÔH™ Úyâžþ>òq‡<–ìúÍÝŠKW©[;3û)OœÖséûÕØž~/|ú1\òÄ0ö’;÷Å =±0#€Pé2r°µ3_¬Æï—Îè’vNQ߀]¯€Ð̓ Rv‹¼*#n‹¨J ÷¿ÌA Ã|Z~t‹?A‹¶ÍŒb)ºÙ·³ØeûRÔ¾Z ^ZL6E†­ìá9_ú ë´ `ÉiQjúRªAûLœ×>ÑŸÞÏg§Qá苈.%ŒWÁ¢R¨žE®ÓÖc§?zSk‹]¬c‰'¼w ‚Ka¯r÷ø¯&ð¯r~ž¥ f òoÃ'¿Ò=²@xe>W|Ý ½.‹²ØÛñdÝÆ*ÞªÀ Øçw’Ë”¿ˆ?/F°’thÚMç=y<éJ3êQr<Ð'oáÐïwD4m½ó}pZÎGxæËœ^øR»¹_aÜF©šK2ºKñoÜL®ÉMÂ4N_•ÚõË‘÷·¤è°n&„rP’4–ÑÊÛ×àg‹ðA‰Ûq°é{zÎBÛ=Cð² SlijXúÞöA ¬Úk€2!‹É϶KÄWÇ$E4¨WÝ1:¶U ëÎêñßyºp¼Ç|—¶•ððÒALט„]?åyufvXô¯•{:Æ^éh³Ì.YÆ‹pÁíŠH>mßN‡r1XIŒ-¨zK88š€ñ×±ë/àÃïe¹­„;âÝÓ0}W œ6>‹i:çyw²2NŽ)êzÓæ[•d’O2]=/ʶ}Y 7-"¸q›ßã]÷I×\ k§‹ðÞ¼>ÛsiÁÐØžÍmb½S+8¥® þb2þX2åiïëœCüvý5lÂãñ86Õ‚¹5‡û=<™?åUL?¼/té’ñ-òpRIj#Ô 4w+Í GƒäãpÅ/Øœ%´Øì ‰™ÄC›HÃÅ+`iôœ¨»¦aû[ü¶BŠŸWät®AwE7®T¯$å׋ñ¶Z®ˆ¾FC'É¿ÕÓYT}84œWØb@¯™ÞǶ‹!°UQ&øNg»ÆQÎë8L8’5¥§Á¹hþ+®æÖy|¾_–;bÈÖÕ…QSŸ9Tn›5“ÿ…fÙæx¯&·Hê ÔZ•`éçµôbf0kR¥ŸøV£~jå³à_ŒöÃîË;q¥áM<²W²«'‘²ãÙ(e'ˆ[ãfU€¿; {¿šB ɯ¸$°_Šn1Éâräã…-÷ÈÝÈQ³ÉŒ}Δãr¿>ÅY¨‘dFüJGNJU’눇v&^ø9ê½,øÉ®À}´ü ƒ ¿¸É†)||(IÁiOÝRltWÃH#ajp4”›kV„¯RlyÖK€÷$®l3÷„ÃÐ?g p—Žó[ÏðV,žü£;ßiqâ7†9‘¥äE`oß¹<›™Ÿ:Hí»9ìþ„3x¾a nIÁk&„V«•s h&¬Jª5ÆâI¥oÜn­q´+Âz¼µÑø¾ µ‘ËbYÅ•^· ¶\D*µrÉÐ×/ܼà ~È—6œx~€¸¬3GiZ³Múù¶d2dGˆ³¢ˆýQjmÊV§}Í'BÑ¿¤‹¨þhàÒœ¥¨@~0èt—@`%vñ¹³˜‚Íñìet6èc¸ÎTu¡6¸ú_l˜V¢Eð©Ì…ñº¦yñL”ǤT þ_#fêÒËÛÏ[³–²ë"÷¹›÷֠ĸk8o´fZy¨‘,?|•\Z"NÓÞÞ›póQgÇ&³!¼Mâ3p(Vo&Óã‹ƉEÂ쟷±û‰]˜*ÄJ3*@fk:µs›C-{íŠ*á­‹­Æ§µv¬ÿu8)ÞÖÓðÆáÆN‡ÐñÓ:±8þ q¾©edê.¡g'N‚ñÿÓ6jCZy-~ø~ +?èÃï¬jrMä(ÜçŒéµ<ŒyÀ¹Ä+CàÖi£ÍM\Õ¹2¸¯f Y§ÑñA ¾+†a `ôZƒ5µÆíÍ+Q¼F’~t)ů —q ;™fé±òKÞì+§¡#ð½ë ?–5z€ j á¿yà O@®ùžl„”M,éŠ:Ö”§é¯±5)•©J¨Qñ1À`GXÞ¿È^7Ý©tSÏî¥÷ªmbLß5JSá<¶èÅB¶ô¥2NtÛÎ"|‡Ñ/Ô÷û°Cæjtþ@hÏzG¤¥ÙUãéÌöælö.&ƒâf³y‚‹yû‹+›íÁ™ÍaOê+`ãØJú²%Œ]^ëÄ„¬è–rlæ4qºùz!ÈÚÌN­”„Kak¯9›ã”Ì.§¥›ãÒQ£Ý^Ùëáé#м) _)ªÒðiü28‰µ— ·Çáå"Œ­´¦#OX’á~|uÀŒ8mÄeòy4D§ÃçÙó®ð˜âIÔò,3ѧï×ì Ï§Û1ŸFa¦¾@U\ßKM…çÒæ×H£Ù|ÚïCvN8_‚Ø KF!Á-,(ïÎPÌeþ|öÇ–ìãî;øÒ§ rGuˆÿÊ”Êt¥Ñn:ìgÞòüK%–‹¿!/¢¥è†üU4þ¡ N\gÍVIÓÎäq¬UHŠfzwÿ>¢áv‡hÊ©_`R‘_Õpc¼Ë·Pbï¾~$ú‡¨ÞÒÙtãÖqló§Cr ÎûÄØèÜëÅ­Q’¸=s,Þ‰=ó®œ@o?<°ûF4I³ë?3é¾ rŒ¨+"Å]ÞTIgþzžP–Á${{à÷äöI0¯ý&ÔÏ?–»4e,>a[i§I~nÁús/Pá.ôUO4yœ§ˆ ÌnîçyàÑãAÍrÍÙÍ]sdº®€ñé>®´4 Ÿ*Nbҳѽ7Ü#¦±Û&­ðåÆ8¥èEý{Îú v4ø RçNÕV͆N‰›°õÉ|ZÖîBÅV°‡›u¸=oÛ!å{.·Czv>€;Ë¿ÂôÌoxäR=ð^‡öç[‚ß—Olrh M³ªƒk‰Ké˜ ª­)Aû4÷²½ÿ2aóA§Ó„%L’fSB¢Á;<&Ýl‚?Î $-ŒÉÑ °&~ÎÓV¢ë½êádnnCC`Þ~¿‚„^Z@Û%VÓ†•bT¦é<œ~n çl†p¶F!þâpÛíàdƒç2…‹ßÑë[%|¹·žkY Jš¨¥H‡OwBÇ+9úÎ|.9“σ‹Hèp>oy4ƒ‡j¿qáe½à÷ÕÛƒ‹5œÌv|JáÑž)íRòv{×ÁžüB.÷:’3oÓ°ia-XìŸ+$Ž£¡Î(n¯íF¥v;ú¨g¾..įÅV¸äüYœÿð.ücÌñµâÅ”¥‹hwˆ³™Ø€çZKð§H2ÞÉà|,ÄXÈIOfý]»(îMo/]É4â›j$¿CÚìè« èñ""‹Ãá°o'x‚}+œI{Ú÷a¼^4lvÂ¥žhú*ŒŽËȃ™¿«ñ‡ôj*t[‹ž8ÚƒÑÃêÌöù,¶Ö_ä“—²×/,ð]^ ¶ð5èøåg©åžH»xÙÿ”ž‘1ã©¶R:é_©Å‚;ÑG3Jr?¡ïü]4zcNïgèá‰Qû~ŽX¥®ÁGyðNoô'»§Å“ûí³èL½ã/«N7m¦!×ñÐ%3Úe_ Õ‚Ó¹º¯H2¥øòš"Ñ^ÝÊvT¢ð ¨G¢EfÐU‡qqávU=oxÕ©Ç0ïõ›ÝjŸPé³ kvM„}qþ˜;,æ1×í®œ¸}Ü;ÿ þÞ†jžÛÀíe'†?à æ2tÐK—É–Ã-;;`Ù5qŽÛWŠ:?ÏÃŽeƒ ÍS\ÎŒ÷߇¢®j²íµ5-¼û ‰ íùö÷ìtÃæCØš\LJ¾¥@Í„ fÁy߈Â}×çR•Žh¾Œ {uHü_6ë0¢c-Ÿ[¨œ„ƒ§ÅéÕE2ð[OVWLànì2¥Æ·»°Ñ)ûÿfsJŸ™GH:?á_|‰ôþ^c*AwÕ2ÎÓm?øš1¹G e+‰HÑ!6¸üÜØ]Ý O¬hGÅPêüÅ›¹€²*†6¥KÀ¤wÑu›¾qO9¥Ž3ÙÜw!èY6qZ²YÝÜK%6YòôÉ*¿Ã–é„”¢Ðí¼¼‰Ö~p¥csdz€]1I\侬fÁvû!º~>ûezŽ{¾u*ùº¿—Œ$Û°îkex°—GØû ’T8nûx“,8°iml$#_e@#2d={ èÅ#P(Q!7úù‰)µÕcVËuëÌQ`é6Z°Ô9ŒÉg°¡¿¢ô6^‡®Óæ¬qö>Vúî%wã'ŠÇ'‘Ä2<ë¢ytEO5T½ŒÅ'‰—Áv߸œ6Ù ,­ß@T—&v«—ƒDOlUîD9×4sæ`‰Æ>œg¸U=NDe±G˜¦R‡§wøñ N¬ÂæW‹ðÏ«üùó:ÎæMa÷¬àpuž5§æÒôeŽ;c ¼Ù–U[y_¾Î¤á›J©Jý*j2ë §1a¦|ãN¥ R“6kv¦;ì½Ãá×­°w;G4ZÛÚ ãžŠ¢ñcóøúÇ 8¶8•$s~P`¸÷ÎÄ­®<O\îÍ£õ«¤™¸Í(¬¸¯º4™Âá(T˜6/§ÁÌ1nF:­½¼J\:`úÆÅœPãç“ÙüÇïðÈv 9+A=–}C®òThRσWFy$‚KBéìõ°ÎJ‚‹ÝªÄrMÜHò¡>ëÍòÛq̾BÔ+]Cüš~^ŸKŸá×ÕÑóy Üj‡¦‘nÜks,W­ÃÉpñ»!󸚃‚2óáðËjœ?¾%&s7Í{pöK'8µêÒ~úHi Ž,ÉlØž„y7.­ XºÙZ´Ã•±ðǽ„Åîg‰s¡2n==ѱŒùoß@U´©÷µoà$»ÄÆ-Ø…T1{dgTÔHöÓ?ì^ìYÈ»3‹M¶”‡61v¾Ü*…VàÙc£ìú¥‡wÅ‘~#Ë6DC±Ó3îâø0vv‹+S?íBŸßN'ÝR@ׄñtŽz<¾—צg¤ÇRguqò>E’Ö¨wñúD§°•N‚lòä"x¾Ãÿ¾XýFqpz¼´5ÑÑg.tc§öh!›³m7ÔÕµ¡ÃÍ~lÒB x"LóW0u|Á"Kêa§Ê-¢q߀zìVÀ54 ×û’Ϊ/п~%l4ýÃý) bgVÓ Ç.ƒïÙX:{ÀžE»Ä£¿=¯_€¹1[ÀCÏre¿¢©](ô b]m6›¢hÅZÜk™Rc4Ó_÷õSElçÏ‘³%ïȘCå@½txÐä}+RA9!ž’YvÙr7•(Öe¹rÌna(tØ=™ê½h&“~àÍî=Ï@ï·!$ÂëW×îA«ÕWÑßF‘tƒÿ2¶ÔKƒ–ºˆaËõ;Ô>ûÓ‰1¡ëì~o<Òv=w*½Pþv'0xgšÐŠ[Îà›¯J·X.GQ§¨Ð€pE*‘Õ‡³ñ{Ý:vë@3$‰ †£Ëž2ºðæ<î’¢»ÜãË$-èÅ$ *×è¢÷¿8±KzLU0‰ó$j\§Kß_%mËé½nð=W‰øµ˜IÖ¸Qù?¸ÎJœ.UP¢Qk3©ðùXˆ8 ¾ÌI#ªíÔ?%›>óŸ¦àï&ü—·z½¹Á OqMŸs)N»__G›…4õÚ(vj³¶ß1ô±½“¸'ƒ»|½YnW0ó,%åâ‚ì¬å^Cþ›u"6—«yÇ@‘7öGµy÷ñDw&VO¹[^I1“?VÔ¦F®Š*°ýöƒd"?mëMØ’¹ ÿw#JžU¥‘iÑØ¹$ÔŽ.cMkÕ¹Ò N·ŸÂ­™È&cs7±]Ÿ÷°ux©jçs4˜]Q´ã¬–®ÐÐúUzá$îZ\´¦æß„Ÿ)}2p§~¼ò=6xÜÁ‡ñ=æñäþÄr§·¬bi–ó!UÞ¯vðjzïáÝÆËxéçzöZ¬[‡!qæat3 fisßãó áØÛz¾Ža–ç´àÍŠ\Üw{,Þ“€Y»#qÁ/r3'–Y÷3ï vü±;ñÿz!^u-»’Íü+±Q% 4B8×Al…e]òk2k¹ Ž6§³w¡¬ìóqLHˆ!Á™&ÔâO³ë´/ÿ3¯\{{mœÁÍ=çŠùrô·¹)X°†öÿ>Á*Ô=!ÃϘ]ýÍcVEóiò\I¶ùW}²—ñ2“ˆØœ0k×™eÎQg ކ_ÂkÐG§7¼á¾H§ƒŽËztË2Õ—ܱWµÐ¦É£®EìÖ©ö` Lûfjë~ê>†'Q·Fð»‡o$Ôè|pbi5œþåz¼K‚J¿S§a= ¬êc·÷5 N¥ââÆÔXv*S©žo%FòBÄ„¬ƒñ)¬£·œ6{Â~펦û7Û°å»XªX'9`ÁʪÖПW×RëѺ~ ÅÑ%qvl°¢>ÒMá&Üõbƒ­)lÃÇÈ»ÞNgëg£ýMæXsÒžÁ¡®ü«sÑoè/ZYÞÞâÛ þ(xËþÀD%-[Q£H(xöÏò ¨’`Ki7ÀülIÖ5`†Ô¤ð Olb;E0M#[ŽßtkT3z±ÕžÂL*ø ÎòšJ»#N£ûVj$LDÆ0!‹t4­2F§¢\ë]š‡Màß©|Ý{¸÷u ŽMÄßôpÚZnØ`nå•d¢Ù_Hœ–”IžÉ6xÓÚbwWÖ[j‡“ñ…jÌ}÷sȹTÇý×ûù¿ÌrµÁG ³N¿vr45ßűè€\4¸sïvßÀƒ°˜–;,Ä„·Á¸=m9]xø9ôÏž‚3%ô1KØdfÿñÖS´óa,Åï%Ü®âU ù£*mÚ¨²x{å¾&î4ÂÕ¢´-Í“Ùt ¢ÿlø”. }€n7Ö²çÕ`ÚÕSäÎm–Ì.ñ®Å-‚[c›¹ íèº#‘­¹“Lme[D͘ã9 Vâ(ÃUÀ·³õ0ä9à›¿÷lrµ5àãCnwénìjK`/2ÆÑDû}4@ø4Çß"Ìš”nPÑìçø]dÆ64p?R1ì®#´{8ÒõOcñëŽ1Øà¶—¾ÚfBKÀüÕ1ôµasÌ·3#‹X.pÖ7’Á¯Â¬ôVToëÂÐg¡°m¶w¢^”ê~Š‚tý0^ŸJÊ4[`œg?™¶J•írŽåO¿±¿žC¥WvCÓŸ<,ûƒÅfÜÌÃÞxqÛxz-É„®¯ƒ5–sÙ'†…õ V¨Ä·}ÿ|b`Kº3$M[`ÁŠärñþÓ^|ýã·°YDõm¼‚;“aÌgJÿ<<€/ÖHÃj½Hô(„”ÑçíiÛå&²ß;u‹­âÈ䃙X;¥…<üvÉ(Þíº!O×ëûûêTeÊ~xû®á˜¿1©ÑŸF6š¯¤ù×*!ÀH„º,ÿÊ ¬,æDfT“]§oÂŒ‰B4ôƒìOtÄ?¥°¶þ§jð¸Õ5¡÷$ä$´Ø¬ gÑdØK·cú£nö†ªžƒ ±à5„œIÅ‹¯­è|÷f” ™M¥ž¿‚MÖ¾ð[“ ×ÊƒŽ¢?¹3Çž¦žoÂɲ8e{64²Æñ½Ú´¤¶Ø²ÐI7`éÏ Ô‘°M_ï ³ 9é ¶fÇÿÇm ê!«›ÑORq<—CñtOº¼œÇÆmÈÀr‹;йxîÝÖfŽtÉêT*vg¿}>CWÏýÇÚ,`/úfp<ýsP›Û혀ŸÌN‚gC(ž1®Á£S=™Û’HˆŠÇíOÑa™.ãåЧ0ÒØ‹2RL@\˜Êê¢\i?mx1˜@×äŽ>ÿa$®e1M‰:ÉþË*äï‡ÀYÞìÝð,ÞC•FÔJ«½Yëú]t÷ó":þßʆ3ï’ØÈÍxØ›‚°èëd¶véD8cÆ¿£9‘Æ^¯„ï8ö¾ÍŸí™áÅfì>€/]– •38¬³Ÿk™ÀúºÐ1½vxè f5hGlÇœ±˜`W–Hƒ +¸ª¾n.Óq¼Ö‘Ï ÿ³û>§ø’ ¶îõ• Þvf¼f¿Äè‹Ý!øÞ$†I|-§N<˜~R޽‹Ó¢êŸØó¬z¬8’}'ì¯ì5ðš;7_zðϼS@¦Ì.îSaZ‰€‡ÿ„ãØ‡)4;ã9— ÑŸi°à—3ró€dƒ´0>ï åÁ:=3ر¡“Ô€ì³É¤ñÞbÿ×!"Z¾–šdÉÑy“¿5€äBjÜÊ+qƒ‘YÒ´áZûy—Cƒmvp;h,ÛÊ×ÇòYúè4íé_¢E£:à–­2¾k.aúéä°}8¶¢ ¼Øy~p,§fˆ˜K]»0»y14âÉ?ô˜÷—è…Õ¼d>u‡ñ±Êxõæ ¸‰Z†ÁýSؓϋXý ŒyÓó—l劵÷oñxÞ ÄäK·ð+~7qJŽ·qÖÕý<¥ëÞ0WiÕOÊÇ;²9¾§Ñ.m::yPÚ³ad_çt¸/$v[6œ]Ef}QFÍ|¸Xè[2gÓòÊ,¶/\æ¿^ÎpÃϖ˨‡ÏÔ’œ}o^Æà^+Àú¶?áI®Ãÿ2Ê"Ÿœ¸Õgá@ˆ&ÊNšG÷™y2µ×ùã~gÃZc3<7ä…£ã9ÛˆLl©’!ÆÝQ Û¿ ¾Z¶û¾H<Û¸ Žêš£",]cÕóí俆7 ¬\cóähû:Kü×íÍ‚ÌN0‰Ÿ2,5{¨}žÍ¶Nß Jߣ`yæ2¸ßº®^¯÷eÖ¸ñTUò\n÷sè=”7ZÒ¸]ý¨øC‡> áÚ>Àn£ßÜùÛ™<ï3ÓØŒ”ÙÔüÃm%˜ãúÏßVç½6¡»´¡ÓÎûÚqŠ·°}Jµ‡ââ˜s6ûÿÒ龜²AEîòG¿:M'ØÁ¦qž°=i=\2ª'ÃÆðêÃéìªÿ |zó:Ê_@ùcZdyÝr\²D—bd ä&üįžC^Îsî-·Û•4YÈ©êre_QWϯ€ùÚoȼô0ò`*{6¾žù„)%8ˆoHÕ*O4ˆÞºûÑ}÷LZ$xöMëÁgTˆ.²¸€+&77´¥-ÃímEàþe6T9T¡Ðàg4ôºº¥P¼-’¼+ˆÓƒ*’×&ÅŒduhíÚ]°Ú~ KÕhôód²ÎRšmß;–zñBðf{~~z„×€[ó6è-„¶ÅÓØ«$kv³Ç­ç0ê‰)^±=ÌZ ú9ϼۤ5ëÙìdH/~xL¢…üØý›ëTá·TOðÜq¥¢ïskÒNÁ±g%Phzêv,€Ùè”mÌnß`Uû {f}'ÿ­Á¯ÇRë˜:βÈnŽÿbSöÆù ôúî&Óì›Rl…õtRmÿu ýÙªûQ°cE$·ëÅUÜþNšÎHǰº)`R)Ì üXöö;hÔ#Dw½Ð¥«`ÚaÞ™_pÌŽ\Q»cq‘RÝü‹œ<¾”éÞ‚¡ßšlE½^L‹Ä1Æ&Ì»j3õ<‚ÓnL&_‚?[î!Ü’¡¾±U`i´âóéøQíì_*Á^z‹W—^Æêm6/Mƒ»ôá'i\+G½YÒ¹ÂmŽü|C8óÉø# hà3·5(„<\¨Ii^á4æ§Ñçé˜Ö“¨A/¼á°ý¾ÆÖk²ᱸöú2ýÆqR—'­çNß~pšô'_HÀüm?8_ë ,*”c-ÍÅpíác2iÖbö>t+S ª…>‰ráÙNNìa\udÞí2dªöÒ[¯D~løË»ÏÂÃ*!öK{'üf²åŒv¥â$˜¥¢þ£T(ÿƒCQüâoˆÿù:* a¨)Ì ‡ý¯Ü™¦ö\^Åö|(OW€çBÿ0t½³2¿„`K‰S¿÷Øì†Y}zÈu ÝB·q½:“Pñ¦'–í¦Æwn£ÄAUš²e÷øŠ&ýfsÏ•‹âLŸ!ø®­IáR<ÀXX¹?Ž œåܽ«9Ѿd;ÇmÒaÓuîa­bʹýAß}RlÿzÐëØ­Ìùç5àó¯ŸH•x <â–b‘³1½æNðrÎAMÙ ÎÝi¦’‚׊¥è›S’«˜³JÃÊàw¤¬LŒÝŠÝŽ×Ü9¢» Å×N%›¢óÏ›¸ðn$ò»'þ†1WùˆíR¼ƒpþ§ ûîðÍW•’œ¿ºì­éó(èŒyÃ{{ý È-ƹ2ŠT OŽV&yã•ÞéÔåZ6Ì:" þ';Ðo—xÃÑ‹c v<N:am׸ÅzÓ0¢X…û4y5Ƹ܇'U5¼Sâ#ýçdB” ‹yý”Ôd(Ò·ó¶ãý±þ4jcÜöŒêØöÒaRÔl¦Sú¹¢ ê}u)•µXÇ­p'Næ‘ãfôÆÑÏ\~ˆ [óD“•AÒ ÿg)mýˆÅsãáOV®¾w¿«sT“ÊrK3g@“„-ë¾þ–ìÈKÂ}¿±­ü-Ùóèþ8ß~Ùp93eyyÑ’,ñœäŒè¬7¢ÐêßZЬ?Ò„¾MP§SEÒÉK$= 9³·ð¼ÒI˜þ ´|I}ñT_ˆÄ2Çš(ðŸTVß|3šàs§ç‡C½@&6$Lfõ‘`MIzëš ­ó!—‡"áŠØ[t[8gŽ®öXâ·þ æ|ÀMêwÑ+ÊE •ñ͈{å{EÇ@XÁ<ä=íã©;9@†Å"q&ÌÍ2ðö7–m’‹ëíyx ä Üðá­t/G¹Ãb´§j»òû,<{ÿ€“ø*F?+vó•Žw“ðŸÕh'¶Íù[Ù.f~ð·&ï2Ùyë ¤¹¨Wú4|¡xK7Øi…iÍÂp»DˆMÝ4“\—ƒ5¦¹¨ßF©qåvzúÎ2ÕðàÇ™a’ ż)î¯Âà¾nÙ¤l\ „±q°Å¤'$M ?ò¹ å\™Êk.ó#ÅÕ®“ÙÇឤg7–jJ^ÂIàÒw#êœ7Ž}â6Ö=áÞ U A·Ýyp·mu†ŠÒw A®Ag;×ÙL©ä>¨ØüxÆéÞéø7ÎŒ±ŽäîžùŒóf•ð£Þçã9WSš?v'o¥ôK”궦¹Ës§4bÀþ—:{d-K÷N›‰E¶¿ˆYðyÎáª%¾Rµîe¬¥É–ÉŸ8Ñð¨n.Ì7Ã’²Ä˜MÍZ\=EV2dëæ}Å¿ ÷a[@*ìóGgë…¢@øÃÝÚ8L1ƒs~nåN4Ø+<Ï8³Ú-q}Ðès¸½;{„i‡ôm4˜ÚE&KXÀâÕ¸Wªã¸èP}ï‘ITc¤áR…=uê$(<Èw‹øÀ](dàäÓÛ_ø EIgzB›72æÛmÇ[ëÅáIO5rܱUppf Ú …A¼kèRaç{Wáà­Rq|kʇZ1$]ÖüpÂÓ+Úy;ÒOñ£~âƒë‘èe+H¿û~¢û ½Ò7smŽÜâ{³h\o/}=‚_É—MíØÒf„ïÀègø¯´›@wôEšäe’{צ°°UæDÈÇ‹×?‚™°ë­·Í&Š—PäzØœŠÅu¹Èí,åê•×ÕqPwÁžnô‚€Èé(½ñ¿gá´Û°; ÏÀ©ò(+§ ‰UK¾“¤ìnõ@<†FˆÃ‹;Èo`ðRËŠmlîáb¶4`û½'ؽnî ñ‘-ÚÊ3°ëhú<Õî]˜|è^´ ‡WR ~cÉ5¿„þ’ÊLÆ^vÚ¦ãÄÐp¨Øu…ˆ‡×óyñ«Ñß};³T[»=Э4€®9ú Z4«øAÖjtî iê³·øë„ð@ǸÑé€Vö…ðÞ*ƒ›¾Á”}Yõ y°RO ÇM™‹¹ßâùµÛ”`–Í}¼S(rfy‡tÙ²÷¸4£ 6ôÎdC#÷ùŽ]9êwÿdÁüÔd0¿/HW|ù ƒ ˜yolâÎBϧtÈ.Ç>¤añN}=Í‹¡d‚*ÏSÆù îa„¦ûœ±Ä¿ÜÊZû€7úÉ–C¾`67ãìyª]WDæ.1cj…ãèÄÃʬUí(>qz´ Õ.ngop[ ÙÏ™m~‹†ºX½× ö«q›ï໇Šlfö›-Þ¢4Õi,ëž×B¢ Î@ˆ¸)8ùÇpò&Ñè±öžMÜ8Ðo™'mÇÀÁKÚ°¯¾ŽÿÊ&›;2ÜeÀ¶åÞ©×_fHï´œ'TwPR¾jxSÉÎÞݘ+²ÇjÈ“Êø¾z!^ø<•Æ,²ÂCOðÅ¢Ȭ²†f£Þ:¢ØÄÞ «BèæD/6»78羌ÃÖ½aÈ«õÆfsiºö¦#5QŸß ÎrG%©{¤:¶YÅf…¬.ÎÍ:fW„a"s&´# ÿGSÿ¤‘”ËȘûÈ£w-دڎc”ÌØð1Q¶â KœDͶ‚˜x˜Q}¾l’‡Ôç[ÀÛ°û¿>Ì\±ÞMXØH²Š‰ÚÅa’9ÕzfÉ‹ùŽ™SÂ[ì0°ȽB[l&í§¿ŒÓ ç»™‡<û/ã,Q¬ÈüíÁKcEè–V?®ùÈuŒ‹‚é#!uåþïZ!â>¥Rï(Áé¤,4¿ó&ë_F…b>3¦i6B_÷S’›êªçAüâ-¾Œ¹-ꆃ×_œ^« V³£¸Õ.•ÜciØmvÜô©V§!«;¼„]ðk&ŸmÄX߬uì¸z×U÷T\Õ1f¤’tœ[N`a(iö^‰µß#é®ï2¸K½Yü„ƒo5XËÔq4Î"þë¸oýü@ CpéÏ?BÏ?È/{Š“2œFqä·Y*.¾Žá,Œoni2ƒkáõòll%£gJ¾.Q@ƒŽƒÜ&“ðäÂ4®ý—ø®xÅ)yËýÔƒ, ê´#k ƒÀ#¨‡,ôÀìó§P»,J#l,ØÍE­ˆ%m¡žÜ@T-ŠºþWòŠ ©™ _z„Ø3½‰Ëñ¹-’­I»aüŒQ]“‘…í µ fW†Ç"‘?/\–­›ñ…ˆiˆ°€bM°ºÒŽV¬›(aò‚/dÃir0ýì2Ð… š]= ý+ÉÂO2ÎM_ŠèS'uQ´ÚŽrñÁº\î¶V.~ º½+ƒ¶Ðë\Vëx¸ëmÆ2[2cwäãïsÜÈÿ_N—}?O¬®žîh ®Ï¤réo ñÔ&ÚèL{úÂ*“L¢+ŒÎ±vÐ\”Æ»S½†[{B­MI$c¿4Ør·Iyrƒˆ} θ{©pr²=Þη”4™™Q¼oPÄvÄÍå¾>OÀ©ë$)÷û=ºb&ŸFk_éÂŨeú›0ÿï1Rs®Žûºä"n¹,H#+â@U[e•Eȉéôè\q<ò2uå§pÖÓÐêî<.í…Å^—ñÆ-Ü19åò¸%3z9×éÇáñl;Þ®ƒEÜßçŠðä§ /É.qWs¾ú 2K¿VŸÜ“O>…ö-–¹ë vv›qYC ȧê7ܸŸ¡ÐСÉö…e¢ÄåßPTþ‰›¼£&wÍËKƒ'qñÏã`wôŠ +ÙÞÃ]nÛÏ9‹(€ÕÆ xýAƒL”áVÕo¥ëgœeêÑäKÄ(<ôµÿšÑ#b²´üN"†«.‡«b]ÐuTàÆñÿzaÂYôK]Šæ‡oÄÆP™™‡‡bÓy^*%\Õ,8>ï橉³úË"ñƒÂ¯U£>Òû§Y%EÏIÀ¸7ó õpŞíª¡çD3çhoB;1JsaûâõÁ±ó-ìn²‹„À‚*±Ê]TÞuÁ§ß, Ëö;¬ýd&ongbáHðSÜæs·OzÇɾM ÷:ÆP/]EÞ™o×qŒ‰/ígHKTbá÷Îz’2ÊåÛ/±8³ø¥Y˜Ý·W ®lsÍãhÍÝ€š_Áж5†%Ù÷b.éÂÀºøy•/eÑ!äÒŠ*&Ÿ¡F_+óاï¸eÙ?ŒÎ,á:„™ãQgXv¾žŸH#Pó/-‰`¥w/ÞÜP¶¥n%ûµ=ïŸg•¦²k +¹ÿ²Ò/Ë.ÐRoúGo5®S®%û4rçN•±ayö£à(ïéÚEpëïeª$fÇš&UÉ„hñæL„GôXUì¥ {^yìÞLac'㔜Ø”ˆß|ÆÞÅ}ÜA½ †¾f]XÞ·QZö#JÏá:Ô•Ù.YWîmÎtúóšÖE+²ü<29§_f\sPõM"Ô{œu!E`'¾„SÊÖdK àØ•«œÝ¹:¼|6Ö®º‰–/Ná@·"ð×ú1Ãî… Û야Úp‡óÍ]H‡vß…‚¥ °t•™”{ÆÁÁöØÿè y«jß^ÁÍ’Ž8-FI÷ÆØ Lv…Ia’h(÷l™ÜÃ=hüö,v9ï«¡ðY6÷b × BÍWŒ žq·àyèvv&|5)Åœ N_ɇC3Ÿ@›Œ/‰ÿÑ-ã!e:Öü—7¦KçNÅ#„#—aþ%AN:·Œz^É‚¯#é*±WðãÝY¼Ò,ìÃ#T[øl²·0ù°R<§a¼4¹"»ž½±zÉóë çÎíÐÁWöœH,j'FÀ/™é|•uÞ@^Ô:Z²˜ü…sÜZâÃÏØ¶Û™ÞêžÊUWØ£ááq4GEœ;°®2+ÇÀ QNûä\öàÚvœº_ŠqeθÿªlÚ-‹å2Ë©ùžE\ ’xH…ϻΟ׋·ô'QÓ”.7fuwÐÆ¾?¿ðŠ5g/ÂoƒE‚1¶&’Ë«ÇÂðUO¨.› ‡>,Ä(C?xó¢ ø°ö°K-¤ÈV‰.Öáã _O”²^ÀÂLŽàÎ^IÖèóƤ0q÷4v+d>'ÿÃíoëP§ä1Š…?kî£CÎ:zBw½öz5 áñÿ´òv\õ³,K8»5 ö-’´©ÞŸF>XÄ®¶rgBßÃò‹NÌ.¸§ˆ¢ÎÚ±Ì0ÈØ@ÕíF0~s–J³çN`-G1·"šŸhõ¶ž»TÐpfós¬˜ùd’Ñïf8e€¸²ä&ËÒ¢óáxM΂>•§ïZù|á“ܤ¤×`ÑaÊ¢Û7Ã)¦ôY‘|=ãî'–séf NÇ‚óCÈ"osúÐû/ø(‰1U%&øxµÏòçrÎ:3÷¤^ÞÌdQª•úœMÀô RúW›†™šÐŒŽ›¸«æ ž>ccøÓ>>ÃÚÝ£<{t•-6¤_xFTb½!èŸIÄGMqü­V¨ÍâO/¦:š‹Ùé©\Wz.ÜôNç}Ô¾¬ß¶±žôÛæixzŽõ›¦ÂŠWh²##jì]´4[w(’·-ÇGë_àäGØä4[R?yY¼ù¡ N?Ò¥?ýE1s§!üjèâB‹×swJ²¹±Á´çm ºó>AçéëØ²>L¾½à‚;íØ¢; bî=(×­C'qî–cFàl¹oNe7ȲOŠ™3ÔX¯Ÿ!\}•‰^ϳ8ÕÕê0tî ŽTZѬy2lîìvçõTÆ?,ÆõœÝŒ?ÔN€Gæ\ªÄ΢mžÎðåä\¨¹”ƒJüIp˰Vã÷žá%c²¡§þY ¤nLg=‘è2 ”?f¡BïO"Ô8ÁŽ|N/±€³™ȵNdüÊ8Œñ±fÆ\§k1笟ÒGx'åùh¶ƒ‹Œ£Ó 1l\$.¿ó[㇉#6ð$r ý9Ë IŠ sý~y,÷G¹ÎItÏÖTP:ø*R6ñÂoG[­(õ=Žè~ÆÍrf³Nœ…Ç¥ã©bt j(îc3]x¸Fø3•mõ‰ÀýóŠPà~3\ý¡ÀŒ¾fÑýôèî‰6ƒy°è¨[äù´a@ú´ms€îZu|ðOÍ‚yŒ×îH­ßòÙS­“ôB•»úLƒî]AP¦é,Ø Î†}ç–S…”gœ„]^{pbf:÷­¯¢õþòšÃ”èžÆz²Nn)×ÜS3¥<0Ûî/FÆN^9ˆÚ EÒ9pWeö©ÚcÚ×0˜ÈÊæÃ~W<Ú’ÍóºõD¸à-ûœŽãÆÜµyÊe`´oåóaŒþ¦•؇;¦/ç: ¨Ù›vXf"E£rm.Œûáîú±p3²—¬ÚF[ã:ÍGœìÎdÄ—àðÉùà{â$Ü\¬’ƒ­xíû¥¿p…ÍJæò*š±=«#Äô{Ä٬ɸ„x¼™Æšä¼éëcBÔ8Ú•~8¬À[<™MQNdº&ôþóN˜ˆ]¼Y¾±té.>• yÏ;v3ÓÇ'°9Ì’ŽMëª~ÀÒ*m”nŒOsmºß|·QxT—EOdîûÓm:ËmÖÆÑW™¡`ýÖ…;=³Ù¸¾îõŠodêˆûØs‘­^}a[9t9?L&§›à#¥5tÉúåôb~:’ÇüÃù¶Uƒì¤§öÕìQ-}žÄÐOlØ‚—üòÞD$†Ñ%¿ »–Ø¢ß+w*¸ºÃW$0Î˰cå7 µÄUÙ¿¤ëÔP<U¿¸²ÊÙݰîe)zˆ`y—$yV*îìWã5NÓÿz?/¸Àš]rh˜{ìhy„›Ã´§sØî‰±l¼Šn­yÄMþ i7¼ØüµcHŽt´N: ;Rո͸vôLn>S‡t=“ý÷ÏåN%Ç|$¡Iq¼Tù0—ÓñCíøB&÷¤@}ˆ ‘б™æ ‚òÞTf¶qϵ·‘ Ce¾ú Ž^—·{uL÷˜ÈÆé|€¤¿/¡:%›P‰ƒe ¦±õÓ·aØâaìè<ƒ›i•gD*(2Ûð©as–+?p‚šHÀqŸ…´N!& Ðl¾0EæØÂýolê„Ãè0\Ìi6‹°_ºêø-ÆŠ& ¸ã—xEP3ØHWú¡7<{Ùú-¬"w:uÓƒ+õpfš8 ßC£/gñM“6n]ϧc rXGêT:­p9qzÂ7rŠ`{ï’¦Éí(eðZ]eØO9Oç)G²àÅ»(K¬Ü0ÜvÈJÕJèò—Æ×CÁ˜«Ä£Þù¯ dg'<‹MBYÓ”0ȪƒYéSC²vSÿ2ÙÏ: ÁáÏC’~í4ú8ß›MgØåWêêÀ¿ËÅ©™’mõØ2!œ‰Àš)_±.I’µ+%úx>$øÌeo"‹Ù–k¡Ôñ@ «þƒ[CícS6²q';¾V¶çÑå{é­Ïù÷¤{p}¦4lß Ò£þ°O6‘F,,ckÓ‰çæXx³Þ‡ž•2;QêRlÊÞ ­gî_øÜñ§>X‡J¾}Ð¥Ìdçô³EÒtt·aß_#öuïb¨ P¢f¨°Ñë³GLXe¥=«LßÀV²ð+Ìvéj&~°»á¯À4ìH¤ù×-qÁ¯SlV¾#þ–`­¤ƒá› ýy¾¼y€µZü& “KÙ&} ò’¸v×F|Qꆦ;®pݳ™VðeÒ¯2#&§ã»½[è|mEz•,Á·6|§l!ê42ƒ1å-oe:K+ƒ,²#ksÝ@‚«'EÔ:CèƒÙPÄ{ŠÝk»°üýèg­¨ê² •Ü:¹È•¶hwM”Šö‡b}}0˜åì R7téìC…¸z‚.ÛôË“}VVf÷üfþkçiúièŽAZ°`ƒvËÂ~<k®G±9v§8¡}Y0µ! ~:±S…ÈSWætk%X!YË\eºp±„·§oƒÊ…<ìÝ-ÏüàÝêeìó Viv›+•è„wjÔûu<>ÎYMk“Àæ‚*ú¥lƒzá×Ü÷íì«þsì«>Eg}‡¿ð­¤%Õ>®Êxô!gº!œeoÄ}ƒªlãuœéºµlovÓ4ûÆ…u_Aq»+¸X¼ÊF÷¸Ò'¼l ÏO$ý ÔÅ™ÙÕ 'ýßÑç©ãÿç§=ËDz|ž¦:õj½@W˜ÏbÚ¹+ѰVb".r4bÂ+D.;K×,ì@š9²m'Ñ÷~Ôäf<\ùaH½9pã^Âß¼r ßÁî×?àÏý)@ßd.E©R+v5þ _5BÊ¥_ f³>“z,‹OßRiHžìÛ?{³§ Ÿs÷©c · žF€Q“.ËëÓÁ” û Ky,S6ö±‰ù펯U—²ÕÛ$ }ßq<«ìÁ“d²˜a Ç¿êRÉg 馿 øÓ»üEkkI:FáºLÕ Á™ êz|Ëô¯§å빓s’@uìIð^¶–t¹N§s]Ù“§Ð(xž÷ˉžWcóõ™Ãï÷8ûçfL}ý…›0³ƒ¿æZ*YfAe’"[;΄¬Ÿó– ør Õ¶AÑ|O®xG|àIqvÖ¾°¡l®k '￞&ù®16/¾ ’+ëƒq°U˜×2³OO!ðNe"Ê] ÞjSñ€+’³ŠÖ85!| ã݉…¸1Íš^}ŒOü¯B‹Z3):ûŠWuw/„ÛîÈ]íM‚—A[é±™³™ÃCY*s¨¯ç*ÒââI\re2M(DZÇW2ÜÜ’ŠÃç9óÀc°wy)¸°âö;£äÐBì[äËVêS¨Ä½aб~Wë¬NkVÜçÎ&OàöïÒei¼×¥}×µ@¦ņ{R,øU3$[/£²ÖIY>ú‡±%gñÁ¨“œêALÄ#˜cz©å"@m‚!ýÛLMgŸ%?¶’Ü}ìóÚãÛcÉž7.¦»Ý†Àâùk8,Ä´¼-qÞ¤W `&Ï&÷{Âçѽ}D‘pŸØ×¦ rsE£!<§„l©L¤^³„ ëÕ4–Ìóå4žE‘‰Xóê¸õ¸¬¿›\ЧçYÓ܃¬£j½ðl;òè.o¥ÎþÛ)¯°Ø. NºÀ¦6߀ƒ·ç±™&ëÙ‹Ó™4ºðßc?öG:ºGã@—.ë‘£:Ê ÀuØÃw‰,¾Võ†‰Fè)½Êu”mØÚsnØXw‘¿(f·ºÉ€&ÞI%ÎãfÐ;§µðxp*1ë>Åÿýú,;‚ÿ <%jÌV6§Âü_¢Lø‚þ~AÃ|Õ~,¹r¢7YÐj-*±,ƒî(¬! üžswi$Íÿþ®™Ê–ø|Âe_¦¡z°-o†àJZøO›ŽŽGó±•ÜóÈ¢þdö½,c‹Æ“ÅóÃhábZb¤KwgE6\\yŽ*À½í8®‚‡Vƒ¹eØñ¥R˜Èw¢²š3ÑbÿCr9ì2Ú(}Ï«Œ{CFö_ââÜgкsôÉ@:4ÅŒZ‰e—à½<Š_gÐ=“áØ³~ÞÆC‘$øg&[rw®y©È¼g´7•Ãc"¦î¸ëž1o.ÄÓWì¹S\ÞÒ(;÷ˆ{ê{ˆ«_ž > ®`XñÜxà$ÞdCÓÏ5Û®øâðQH Û WC·àÏÅʨêDŽÍ)L‡†L%:»IÚkyð+5…;¦r••+è`–õÝji·¶VN.®²J?!´0[ž\„¯× qx…7hu ³·f²àSìʵhÝÇêS\‰1ŸÓ1Õfzéê/ñhÙv ”4"ðèvE*5јŸ÷0/?4d¯£áÄ.ÎÞ² šB2L§q¿2z°9m ô.Žä·;lU2Ù­|Ô6ª±ÎŠ)ìœ$3:ŸLÏîQfj©aPX¦‹.)ÁvOů­£{òŽC{Ågüžú÷—&£LÔ]rÄwa² •=VÞ§ÉZ‘ÛðõVfV ùʽü•cÒØ³O'à‡¿~í¬2Pßeï–ãm¿%pÌà!÷û1P¡IåüÒˆÓ$ÁÞŸ7tà—SàÍ«}sƒ«÷ꃫQVôùŸ0µ²Ž‹zlÏ£‚¹®¼…EX•Sû JÏm`Ó²‚™ÖZ9øŽgy»½ÔFf´#ìôØÎÁñ°pÅoŒ˜nfÓ¹Q¤òt@iÉl¨ù°˜vd6 ÑÏbw¿t‘ž¢vpŠãÜx[ÆN}tѼXLQýŠï,98wl ž8ß뫽Ð^ÀŒ~1|«‹83î\?AµÊ¤é¥+-XßeB÷H­ÅƒŸ\AúõT¬(eƉEp}É?Ÿ–m C@uo .{G`òÔ&|0渧hqÊs2ºpÇ´{v+&¬¹…';qó›1¸ŸZÃì&=n‚oÚ¨Gäß(¯Á?3h÷‘lnx,Å/÷66Ì?ôÜÒoÃY“¹X=.zU,èö‡øzwÐIskñ³ûd[œ1Sñ‚0;mjxð ÷Üe˜üÇ® µ[›°È­[œ¾"wü ”|Àš›žT%@´f¸¡“•0ýükѹ£Ëæ¿\(×ê'ÒFÛäLB4~ 2@¥uWqصXßšñàèñþz÷ÎI ä”LaßïØh&ƒM<ÂJQßî˜÷7sÉ4\î7KçÙ\•L…—"=¸&Ê\9CÚßìn²šùuä瞨þ«ÜfàÁ3r¦ï"‰=}‚çõêÔù§Âò›Z\ø²±°OB~L/@ùz쀊ÎÔ‹Mz0¦IßÀ㳘ûáïðà¨$í?<‡žu^Äí7på¿°Ža4÷[þXû™ç\}ï7‹ÇÁßÝ•°.( ÅŽ_„ß}{YµŽ6»ûÀˆóô™@7Ÿ²dím¿`¥™(=sßžodâ™9Ü“Ð48´džÁy<‡Çw\j~Y‚zà‡Üf8¢Ò6Ußyæ]ÁäB[6'ÕâTçÁÍ3ɹš5\S£%êú† °¦.òxúDzßIè¼=#Mtááê.o„?-ÉÌÆrþ8‘³ðLíÖ¾´ÀÙí:,¶5ÇK Ø|u§)5¼§‚a¬ö\¸#î‡ÃŠ=øtûuØ‘ƒe¶Å 8ë9ªm½kò§ƒó¥¼0„‚eÁ®# jˆúŒjt|–[¨èðüqB‘¹Ç¢Ó`F×f%÷ò½—ØBȼj8rï;·ñÕ8ú$WRêm0yé*øb:'i_‡s9ø0×S“†¹uÛû¹ž8¸÷9 2ºïsâê2¿ÇŒ›úv"ûæò$œé¡4E(Ð×Dá­üsCÕp.ló‘“î4W}6hÜO=fQ°9GîÏçoŽŒ‡ô¼O`QË FEá÷*œ“UÜH¨1yÒÌQ§Ó9½€ª­4ÆËçGõÚ`8æXŠÝŠOñý¥SœÏ†ßຸ€S=·„Eé·£ðû)4hÉ}ècK°ÿ çâ Dˆ‘[ûA’H’¿;4ib“!ÎÏš‚5~¾$¹dçž°A/ñeÜi‘pòJÖ–®X9®áÛä@R¡9 bfšÐm– Á¯’‰Ð’I¸®J†){p’¯ÜxÓ»r)·ŒèÉ‘‰,×߉FIo„#ë™Í¬¥˜t$ ïüÓ¡òE)`ó}7‹v¦gÞqùS6ƒÍ¬ûDxÂex·À•Ödÿæç/¡tQ[SÍ6ræM7bø|­Ê`Å·*v¤Ü€9dpgK6Ñ×äiÔâäÁ«X¸"²?Œ€¢¡o·Ã—H%ðµì€öãR¸òá{îÕÌJØ,Ì·œŠÝصõ9nŠÈƒuå†t3!ä|¹¬Ãç·È@Õ‹ó°hì3|jdÀ’N="Wn&™-£zñàg®ÉùªÞÀ®!mÚþö4—;ÖÏš•q%åiÕA# ¬äÎxÀ%}ðá”>C®ø~¬Ф™JAUÎj$šH×tc6Û¥¿m±gkÖˆããÒ,g†/²´aT‹ÔêmÇúÒ/ WèMß=äÐGr2+=í‡&ž#h -N…NûÃÚqh³ <>hQUü Ë PÞÎxÕæ/WÜ! ÛñQ•- Óæf;ÕƒNN™ÍŠ»()Ó•_ôHaûxØÛ8Œÿ¤}Hñò1£ëzwæ-2½¢û4ÓyËȦcS˜GÞ7^PëZ*º!‰È¿ÉÕœ~‰ÁÏFxû`¹à]ÂñŠƒ08;•$ZÓ\i8Q4r&ô`Í«Að¹ ‰~ªaè%º‰©·gy#8oÏ ø¬–î¾°Íø &Ì–ÁÔÍìW`t,+F£0èÕ@ÞmnáT§ÏƒËó6ó½VO ¶oðŠ×>;?Ô"¨ÏàoÔú·KïÖsdn¼‘ƾ1j—7†Mw}J–ïïà6l¼±•éõ)÷y…ó†1vý^rÅj3ÓË‘€¹FhÔØnKÆqo…˜žË®KaìçXKy“náz•(ò¯û-ÌîR@æ.ÿ¥‹Cv^8½.¾BŽ^Ú£…þ7†¸‚Ùpªc”+^GÃßsbîä$L×®—Ó#ËyÛú£ûž#œÚÀ4™wœ6Íÿ…‘LNPh §ÎÁûð^œšöÕc6ûD¦‹ÀU[Šð¦ì °úiÎLXÍiNÒ¤[XÕµ—\ùËWpJÛçL¼ÉMz8µØ!hº™Ä,ûˆ•;ÇѦà®>,çoJ…ªþN2ûE.¿ÿfÞ6Ǿ2tï8ðmŽÀ?N?ÉM‚qùð­OŠzMXñß°2jƒÙêu6cìÛ@©ëJ}å`̓&xiñœ—‘” Õæ»ø—dì¹Þ¼í¤,ô"ÇÛ-€ÃI°÷€>†8z‘Ûa&è(}ì¾[àßáZðv4¹…#y8âX e#›ðëË·¤QH™S–·JR ój\øº‘]µÒÇ-:“™ì¡>üTŒó¥ñ­›íܤ…7wqÌõg¯qú§=°¯ä .÷nÄC©ÙÓQþ(ÛjÌæîÅ›ë:ÝJ#eÂÌq®6ÔK†O¢\­>X}žlPøÊý: ÎVÜçÔîJÂþE3ùedÙ¯ˆùìò‹¯Ð)67§uí»ÔðTÜ/k«¢¿Í%üôê>ÿ;™.xúg탗˒1ÐKЉZa`áTÜ–¹ôÕ‡Ó¯8î²(ÅÖ\×UMvYmq:à@lýŽâC>P+2‘ýûu™'eÓÃ…ÉUÝÄBY |ZÅè…µø?c=ØìÒ2žïOTªaäu úÌV¾Ý)âê ®ÂÛû&³åæÝð÷@(¹-iÀ•«²YÝÕ$Þ" º™áæ^Y¼Ú„Û³š8÷Ç#õîùÿ Mh,÷&f?ïݺ}£Xsž;(hFã•äðLÌ,ù†+ªwKïÜý_®÷”7ΞrÓÆ¿e†¦{ðJ÷þ†åÓfÁ嬜œû"Qžëñœ'aä Y½º>+'`Û€.}ß|Š;A‚¹¾¼I»ŠÈ€Çnê+ɆT4HƒÏTT0[Yj­§ïÃe¿(çx46»„tÓkôÆíøÉb¸áП…P"ÿœ[`v‰4zŸá ü£óŽçêûã¸MV6Ùd†Êˆ2>ç}ÐNI”TŠ´µÓ´"É,++#E‰†J|Îû$E;4HC¥¤Ò¢¥ñóýýñy|î½³î=ïsÞïçãñºï[Ïý’h|ów%·Ø:Íûpų_V²üS?M=ƒgÇf#vÍ¥6³Ö’Tþ<út‚Oû¦Ã†êñ¾//س¡£é³·'„%‰ùhk6 ¶u‚w ƒ?ÓÌh’O"›®{žm47 R ŸÛÖf ôÚAÁˆÍ4Ë–ç¶rá˜Äw`o?×?ÞÙ»ðÙõª0S:¤b¥0©ñ<9¾©Žf¹}„>>MSÏ¢Îý1hï¿&H@—wé—ΊkÞÀÌ=ø›vO4¨Ï!×TGÒìŒtе)„ˆ­©tÒRÔŠ®DåE"¬k Œ—¨¥à•9 D>SKl”¨»öQ¾©ÀœŠ» æ•—j^aÛb*ÛT*=,x¹_Š/ãá´9'+íxÓŸÛÈbu¨ö­>TÝkÃ΄ØÒ¯ƒ¾û±Þ}u>Œi¼™º¤ÌÁ”?hƒü÷$¢H“[Œ,Â3ÈpÖÜaÜå”>üüõ<˜€NÄ2ó^}úV‹`—\ÿ£‡ÍQêóÒ߈·Ïcþ~4Zí«FËã°¯Y O2€w 7¡Ým Ý­Bó¥O“Oâ„[æN«¾ËS«ê\Ðò{Še‰û¨¸žÜƯ±©¿ƒ%ÈŸV4Bû@œ`žÏæ¸EŽOÛ! éÂxZs. ‘ð m>{Œ"[o¥Ig—Šâ4öMNá÷ÎÑ÷Ã#¨Î«ôÁè…0A]IŸ? ×ÑD·q|ÞþƒìQ“ÿvð)Ѳ|î±Iè6Ú–7À~j¤ŸDígí¥*‹›abzWÒ¦Ô*b†Ùâ^%ÙL«å3™^Ôƒ•G/rÄ亃+ÄhJ¡*ß,©{¾Åž.†c¦•0ìÕ~’×Ç:ß’ã Œ>YCÌ#îÀ8Õܺ_—êÄ ‘C4åj$ßÝ…ãÆ³8w5rÁ„‹*°ê¥›ÙK‹áà›nÄ4Þöâ…ÌPR‹Ë.²­ó&ᨳ‹„e•³„‰¦×ñF‡>Kz¶‹aÍøP>¥ÕÊM hJ³déÂQXGƒ¯àz¶C“6ðåú󰬲…x ¡ÑÑÕ5"0î]~ýtES:—ó†|ÉT\Æœÿ{=мµ7›Ð)}^5‘À-¯È“»Õ˜n½«{pÆÂ,,,äß„Ulávc>°n#¯¦‚¹V?ð ë|TxúŽ˜JVÀ®½Cøž,-zëÐÖÉ­ÑñZ,7±çß‚EÜŽÄÝg ’^¯,Kâî~·N¦êâå$v.žç|) +âö²éJ’ü´ü\^é:’ÿ˜ŸO%<€“öžÔ-í,qX»Œ?÷ˆ9«Q·Æp¿§±°æ²!¿neG>IcÚ½L”×áÂËóüpiÈ{lz1‚ÏjÉ¡ë}eèïå]¸v‹_»Ü‡ù8“æÍiÄ›‹4¸ø²Z4¶ÇwQ»¹åº¹°ek ¨¸R¯JèLˆ¶~õ‡ £Èûà©ìÅý}x:ã!œ» I.³ÑºË ï΄Žw¾¿‘Wá}ÃV¢¯Æ¿8GA|ò!tÍ›@nÇ“g.«„zbqÇîDvôJƒ@|á]¶@Ñj¦8cë™bz»=€ú›Àíl9‰V}vbGÿ6ãJç¤9ÀîïÚôÖÀ^£áí/Šñw»¯0ïG¶ôR†Duöèµ[žûžË$%O=éŠÔÜæNì#VTúi‚еé/hïK€fÉåhßeDÝOéñåû¾€›Æ:P^-Ài'‹±ìÑL~¶Ù…µö·gšÒO•FÜ~Ýob•$-\”r rÔŽ±ôÕ¨¦œˆïRwÒQE>èõÉš–]aÇ<bƒ!íµ’L\ßw’r`ðvþ˜A;¾ìÀ·­1~á8¬y?c‹”a¤D7-qËÂbqKiYÉÅ;´éñàÞóÏ.|w®ÆyÚ¿ õºÂåio“ñE’Mþù ‹›^ÀëôÉŸ,æ¶ Í¹ðGye¨@”F)ÁÓkBéW±ì`Þ=H ‚Ä]êxÃöÄ?ìÄÝ,CDÑ€[8ÚpÐÚg-fKiÖB:kÏ(>%üÙ¥™÷¦À-Ñá4É­´wzò]áK(7{‰ë£cèËÕ›isÜprÆÞ£ÏMã›'<€u“6ðN#\Ÿó lÎM¤• ìž|-ºMæmHÿù€)ëÚP0®“DLê#uçY¹ë‚+ðò±l™zŽçìg}Ú¦¨,žK›‚`Jì ÐqœMÚNªñSrY‚ÀŽ/÷J…ž'ï!¼¾HÐuõZoXsV[Ã…S`’'^qÔë…~]ÞFmg•_áÄNMÜwZ„/,=‰»VÙ°÷Ÿ¦@Ȩ¯x Ðßÿ,s»oOõj®‰™åäkO&îp Î¿µé⣰#n7*;ÌÀæÕ8*Ð~^sŒ%OâŸ%Z(Sï%§p[ûv¼<ÛYpª¶‹9B¬jaÌÄ,PÞñ‡]xê É[w1˜ÛPqî Ì‹Çïúõ`zY©óºØ`}Ø0<ˆÿ ™åùèšá‘'‹Ám¯4ï²J€{¹98o„oÚ¾Æy‹ž•Axs¿w6¯Dt5¥-/T@÷Ät’ÿq¾`å¥D¨Ü>FøÀ¸/­þ&xüp8þz§Èu}g„÷+î<†.º4NàTi†F…áÓ†í̼÷­;[t—ÝðÿAVhFáçÞ8ôÞ5„šÙޤríSaϹ©T‰dõü‚["¶ôì Ñ&ÿîÂx¬˾I壦˸¦Úçù+ÁžíûˆµK:•¢3AÍÎö¼©„È„/¤ºc†–ñ_5=WŠCl¼1 ¶å¦£æÀõ“0wÎ=tËï&YG%Xl|!¦Í9ƒJOÚêìn¶á$79¶ÇdÕI\´ä‡WñšžitÛ£ X±ù>8Yçò½T°i%ÇrÇ•\¥ö>k»¯óØÒÜŠOeç‹Þ~d#|µˆè|úº)….˜oB›æåâ‹)§Ùä-'ÈãÄUäÅ¥f8КÂUÊqäð›˜;å*Éžc³’ñÍQ7 -1÷„ ÍÔc+6ÅóãožƒBÿ uozÝÄ?«a¸ÜO<ÆH&?Œ}yƒlá9ÉÏ(ÆcoBÊýŸ°`3¥ÏÈŠ ¡å¡%©Ãàš¾’GëàÎ/,WWD ×yüÒo7x¾Lo¹~ºDF£2Û ’3ª±ý •HÁŠu¯anŽŸÜ}gà®Ü¢×ö ß’XÈýp“99÷,Ù9Øü[šNÿ@.af¼½ÏUi+_B—Í]‡É­ ê.ÕÇ›þ²°I꬙7’»qUúÉÑcZ#ˆõœFökY„ã–o½¬<»4Þ —éCØå^†zK1üs9JÈÂcÃß°mO/£Fõp:Z9€äßOß$O¦Cn†coéGЮib³#n£Ûï[Ìб ·Á—Å6‘ƒ¢QüÞÒé4qwWì£_ëì±i¹ì]×(|xä¶%>ݲK¨y}<=oT#Ap´¨¢8ßúVmäè¥+Z?½oîåé6xúyŽù\—JúÁµü5нú/·Õ]šê¨÷O…†»ò¶mØëY‰·ú—¢›Z°!t½-&Š¡ïk ãÇ7Hy"-÷Œy¾y~ B|ã—q×}/ÁYù>>šÈñF'!~gïûú&­-…ããô?m@OF¾%Û ÆŒŠtØ=º¦#dütq­þ_ìz{ô¥WajQ̯‰Ú‡žƒ×ˆípí–6¯¸½“llü VQìmÝ^<œƒÁÓÔC=ö’›‰|ñ‘ÜçÐ*g~*·,¢\øÓïÛÀ'ú3Þ=k %MfìOs/(éW£½ÿj>ï¡)·x°‡†6½$'ß*ñ¥E§iôlMþiv\¼®FŸWä6Å {ð³ü:üm£Db¦¥áÆ¡øv {²qãöÜà˜4ŽEß~»‹ç]ÏC‰ý`µÏk9ä`lÀôsãSRX{ʶÜü/LªÉÝ=Ëáá±&FwºBïùШDc\nà©u Y£ÈiaˆÜZDì6Œƒâi×qÒŠ+ éÈÞ¯dƒÚ˜üò8I9”9äËàMC^}M Œ v±¡ÛÃoG1LþRˆ¶G ˜íÈXãÜ)Є›)§á˜[0žJõ£±+L±³[•½ýmºA0ªV“ž1§h¢l#ÜøVðúˆ+œ°Jqž´ Cñ_X˜ÄWá‰[Sðõúãp/ï+)¸2C"¯ ë´ý‚½{ÚÀ­é [dqõÀž]väÉê9¼ìGÔŠIÓ°õ›ñZÖ0,ù |¶³šž-ð÷žyz~ÜFße^½p6L1‰^ŸCÍdùãbxS†™¶…¸H$–ÙTGCõ¨,1Œ`æGÕùƒYzÜ’Þ‡ë/oÐÞc:)ØÁ•¬|pdÃàZKKEñÉ8ÖNFàïM"šEuÌò n¸:zç42ËÆ.½ûî½Ù‰õ¦Ôc¦5ÎS¤«?ªCEûIØ»Üÿ½u¤¯®omI.2N ‹•ÅéùÑ·pç•Kl`À‰|”·¤w^¡¾)f• ^èe³ê˜ùpúý{ü÷iÎÚhOC5æ‚ÂNC”’¬:©Éã Y+ü°ˆ»k%®>_ÙŒÝú‚ȇÓ``à<ÉülÂ5b®²¹éî8¸†8§âC!»¨aÆ6°ÉúEDýÓ˜ÍñDûìlZ%€¥ëö±x5WzªMš6Í_EÉéP¤±–¥¨ó^áªý{’à¡ý±»0Ÿ¡ËÁ¿—C|x» ½ÇDi´ª##µ¡{’½Ÿ=kEPûE,Vó"´?6†ÚPíƒØÅˆ3œ¼€ãTý— 'jš}°k1Í]jH‹ K!þSŠNrBŸ9¡éQöö ã¿Cñ~ÞU¶pÛ2¬œÿM3$Hö:›Ø·(š,wÌCYµ•™ B·Œ¦Ó¦§€vqñ7AüS«%W-ݑϓç5!ZT_&„þcÎûbèNµ2ΦZs­©yÄPñ>üÚáÏÅ‚ïó‚"ni.Kí;®cµ6„úÛwnóʼnv× ÚÛìß‚GmÄ;±–£öGȼž2!Î4ÌÉ’¾¾.¤¯N}Šn˜À'çyÒœRäåÛÉ×á¼ùçLñä5–FyR§ò?€ɬ«Ù‘OÜc'D }_Ì!Øi.¨¢G7ÏCKK 2ò#¥_¢?á0/-¿Å{ø82e1ÊmSã§Çòç’d^|A’ hð;çP#åiDÀ8ü;¤Gýü7ƒ®µ žßü›Ø5_†Žþï˜ýhYS9•Öt¡“–s+ï:”X¿ý>Lážwçð¬°N 13¢rN¯ÈÄŽetdîwò~Ñg—ÑŠ‰áœ¦E~‡5¢¶˜ß´YKßšºðÍâüï…¬•UæE‹Çñe¶”îh9KÓ>’nJE—›à„h9~À^‘Ú5!Ÿ»Úˆ/”¥ËwmGËo'ÚC鼓 X=åûxe)Ô½<ÀÆ6J&Me)®Ñ½ ‚{ßMѵ¡MÐ&÷l ¢s,Íè‡0)zHõ4|ëë ÂÐ|¦(º¥qø¸: ‡F°Š4+¯~Æî›m¬C¾“Âë‡ë6Ã{쨮ò•¤é;Â`­éŒ~–O0`”–(,’äE K ñŒ¿¯÷ÀÊïø|Gmêš)àg×€%—£À¬p+ž~ó c¹+$»îWÃcv|éPn–¢o6Ö±ëTyõþÇàW&Jx~†,kFññx+)jRÔ©¹å{b¸µ MŠ\/c¬aù‘oLÿÊlÈVž†G»ÃÛÆdКp~þî%ûTß mÄ;Ü ŸÊ`‘îP<¤:È‘×pŠ¿)öŒ² ëþ0Ìâ7–ø*£M^7ĤË^{daöÁS`ød×¼&…;ß\ÀkäXuÜZø1‹þT³Gÿä¹ü·ü¸?ƒ(+zób)ö`¹ª–ëò’òµ°xV$ çðßçÅé¥Ó˜_²%W–êä$h[ä@»…Ãxø’\ KëÃúq Ù Ç ~@ó½XpU…û|xYk»ÈG+R73‹.iô"e“Öò;‡ýA'Pf'x`Û[Súàüzà +07æ)[úîÎÐkFåúL—n,|xÇ è—qšü¹9…?wÄhЈÃôÕ7î±Ë˜÷ ¹InjÿÀãϒ胭ùø9§˜Žúó7˜“Ÿ¦P{¡­,ÞŽcÂùî8I:á«Ì{pƒÖÏ×#_ÀúX9V Ë!7%­ø¸ Nø! Ÿ&Êô³›ÍWwÇãÒŽ\á¾O  íK=ùŠÂn¡{‡9uœ„úä¼ê8¢©UÄg¥èb‡ÉHž·\އz—ÀËÉj0wQaÁ„["ŃméZ«HLäxB·;eq›ø‰k&,6OGy'ñï‡RQ÷¤ev+ÉÐWrø´ö§1ÃÚpöËp^o¹œ/yAטžÇèÕÔ E—Ú]Óp_ö1{5ŸÙ !תÙCÃJߢS~þ†{æqA÷8˜°üñàžúU&6 ¢þt€Â¥X¬ú XìœÁBŽÝ%½ õhf÷gø¢ñ^ØÔàÆËb¨Øì øH|!½°m }ÕVI rt¸þVCþãÀcgñÍÄEn? Q¾À .1:RY›ý5Àýw(Èb Ñ¿IèÓÖWĤy·:à̧œ Ç“ž3ÿGUP:ÊŸŠ¯åÛ4ðÚ4I>lîgTÚž ¢3w¡ãø(|mÊ’ÕBÝ”©ÚŠÃWÓg7ؼa [BùI>œï&¦1ñ!î³q#ϼ«HìÞ$òñèœr„Sõ‰Ÿù:زÿ\}ªÍ#7‰b•í;ÁEçT2\á,hm÷ÇÅ/FñÝú_ñj[+ÔÙÇÃâÃwAÍâÌà^ÖÂ=lñ˜aT!Ì‘Ší}Ï’Ö|f¯9óbûsBŸSrìþ˜'à>0 ò¾ìag.^F÷³á³C 9WÌ”šÄ¹ª\$Ì&ÜbUé\'¸Pü³Vö¢vl‰Û˜[1¤ç«,Õ3ïa’ó_ N®EG/7ø*O:{¨(þ‹Õ¢ÒÉå8æ–¤P~Üëz¬MÇþ5‚à˜+¸uú,tz DCaô‰ûh:rþ'v¬¶‰ø)SAh¾ÿ'F«”eéƒßJ¼M؈.y¯ñòò‰üög_^]'Æß½ƒ^ŸO¾b»ýT*”#7f*Ñ„L-ÐHYEwÕ2a÷vþ%ÉŒ~|YÃ\¨ W£J²J¼eö ÊÕ°Ý›,pɤtôÃi¬ãËø¹ŸRñYþ ‹py :Ë’ù H¢q%2S~×õ¼¾ñ×%OA­e"¸'V =3α†CûØ:ýøÿ4Ó¤u»ø 9óVQloé]LôÖŽ‚¢ÆwPØïAç—ïGC}7j8ŒÖŒ?—Ž™À¬kJXḩ½Ž5…oðô•ã0º.ZxxÅ~¢ðujÆ‹ÒeÍZzä…Ex³NÄËq'­Wð¥~·À`¬œŒ”ä’*ÉpæÍ}(²ÈƒC3&Br]ymoÏG,ÙˆŸô*±;ë³°ø]3„WKÒÕ•ÊuIßm0¾ä1Š8‚õš3øfs–0h< îËÚ°;ç ›§åÌË‚Ýl=ú·r'ŠŠËÁþcäÖÉàãiGÊßÚâ¹_ºê¤ýx6œyù¤â… • åÕÌMÅpÊCŠ—òœý›`Ì‘­4dn0´ü^÷æúÒŒÖ*Ýûïç¯CAÓyùØ,º¡0Ÿ5ï²á­gpÌÈçä¦ÏAÒlN‰ _{®…Pol+8ë´b¿i1ÖJœæ{mÔ!wÎT.Õ~ĤÕéÐnIÜV2…·{DÇIƒëÍñ¤¬”e1‡Š`·g!ʘؑɇRá¼ ÔÊÿ YhvÜVÒ»/\fò=ð¥Àu{Õ±Xô.zuíÄô þs0òàƒs2”>6ñEÚc‹Oóúñ]¢ ¯Ÿ¤‰"óhÑÆnV}´#‚Êáde3]ÕqG¤¯¢ûj•¡Ù`>o½:D`¯bÃiÒù+Þ)¯½¾˜óþôNׄw-ÏáI¼ÖÞ´å¶ùdõ÷š<`AG 7ç¢ÉîtÖ¾‘4éÚ¶tDÏt® Z†6¼p¨·bÌÝņÑaû?ÃÕqj3¡„ü[¢‡ÃfDùΩtÞ´,4Y˜=ÒÃa;ÁJvƒ<1CÓOmC>µî€[qžäSþ/Ø8Ä’l­1BýðïøÔnßfb†·æ{A¶_<™f§¬TÀÒʧÍ2æãêÓHp³ µé0ﴣĥ™Mh z{n'~izè§>¸®¶®ƒ´¶¥p¿(C—Î'AëÀ#Z?æjŸÈa‰û²IOº±T¾F.[xcq>6kÚÉ‚{Eìp30¯|²P8Å¢Ò]àý”áT1[“oóça£ÒA,o>–|‰Ë§\I_ãµ·q¤ïTÅxêóDâ  ©[p¡L Û_@ï]z|–÷TºE¸é ]ã7œ¾ñ;Ãb}¾‚×ïñ¼G¢GÇJã¼Ç‡Øî©Ä¸ñŽ]çŒYFOÈõvîävc«Aãù%öND›Žÿ-JÏÎP%?®•³/úÉt3NïQ8óîÛ¢"àw‡¾g?ö°Ò%ÏÉZby½£¿Seðìw;9ëÊ#Ú½…›ŸnÇÑmNà2n3·e'®oYÇ›=*Øû%°Iz8QÙų¾+Ò“ÏAôà]´}µ‚Ç×.…ôÓéoYºÝ}”­~‡÷îrï/b|åÒ›Xo+G7¼â)¬y¨'Ý$]Dæß`‡Í×À†s9Û­O]Ÿ_CKo®ÖõˆuI¤!§Ÿau*‘Pyug, ¾7Å¥+ÑMM…ïß™”¤©§ç\ê6ÁÝ(­·‹n Eoú8`¶c1Ž0¼ÖJîvÉk± 85¥ßÛª3R&Šìö?¢&u5w*qM‹v¶Ð¶M·¬s›/µ›µ.aäð‘"§›K»|"£LcÇo-|öÇ@?㨛Œ»,\np‡?¸øÛ8°I|¿ï!“9»w~^ÈŸ=‹D^òH0gK y/M;ÕɦޱàþÝN×ýÅKŸø9¹±½C ÆQ?ØÛ°lÒ+]~’µá‹OÙâ%úëfn±ŽD¯ø£xré'0;lO6Ìæ¥7Ãñ â:†[û˜‘¼1%Z ãîÅÁîÇÕ‚ýJoQñ´g)%lc–>_ç“D/“=¤Atµ:]ЋÍ.‘Þ8U,úËA¹!šžCŽ>èüïœlRj,&üŒFg WiÐñtï`™¤þâ¨f²\!,Ï»Ïà²ëaÉã‚®er(?C’–ÎÆ¶|:!!Vn™Çáçl¢Òn‚#Æ<ƒú—ð¯Ö1æà¢‚!U'íÞ2½u¨èâBœ^ýd–Ó*OafÁVØvsžmR>3HNŒª¸ :/êQÎá&óΓ„Ü‹ÞÄ÷··QýÆ&MŽÃÓÇXµg=þÒq;øa(>]6–n)ÄÇ·åQ»½I8ÑMs§H£´ÈW¶4L_0Ëx¹¡¼Û=â~Bp’z"l÷TƒðælòþpAüøe–C$pÒ†û ù Ïh!Ýã6@q™µ: NžŠAɈs¤1²GßTÄœãóÁtà\œ‰ÏN¥Û³J±ïòdœ0hdžf)3Ãñòø7±ž}lƒÆ,ž?µ·E\„ZhP FvqøKz¿ÕwõÃ6‘µKcðܵ=nëÿÙÀ§èSD°A -9‡»5tAohÛ  ºþ‡\y”8íøÎòF§`\²6vo&mýñÂ+ŽÇá Þ²ÿS14ž9{DáÖhWº‘6²ÓåÚœ·PçÓ ÔgÃJûú1Ñ9°¹«…-xx$Ìt¡`ÅX ' %¬¢aæGM¸ªð õOØÃ‚âx<÷¤ ÖK ÌóKð“òÌXþלïðÕÁ~™*aHh$¹DûÀÔ¯Œ¡¦¦û–9ôûQ[{ú.´ë¢_,¦zñ_)Æ <žá‘ˆ옇¨}[šMë1äÙ2ü¶Îö‰”±eùLnZ%9&ì¥v·¿Âƒäé1]¢^¯CCæA¡‡'Ž^é@˜í½ûòØ¿A¶=ÝóŸ.Cg#W>E’äº+ìÓ½1|¸ÞªiãEX¯$¨I£#ºFÙ^'ã—-³p‘É=ORæ³›,p™t ¹åäðä­Š4^ä<²}„ž…—q„cD?  ¯o’K ƒŠÜÜvFµÁ[ërß¶ ž§ŠËµé±#…ðdv)~ü"Rί‰Ãò¿n÷=«¡¹ÉN(×%ÆñŸÏ ¡O†Àú¶Lܤû–8qÝ oÏvÅNK?„ƒiðŸf˜M;öhnÃ(ws:Ä)ކQû&àú â`´£Ö¥GâÚu„#LjØõÍÍsK…K»Ñc_Ê»JâTyeØØ «Oïe??ŦP5¾òÔÜÿ‹ ¨£ƒwLü‡Ë&V z;0Äb"5œ§Å—Õ¯_¿aÜßEÜæ+rr>‚k÷«ANT<<êÂÆ¯%õ0¢aløæù=òp™jðugK¡'×V¼+zõ@AäžhéÃãé{@ú™"Þ>õòÀGq½M†(6‡R~Û™°‡ÑR[ÑÉhиžÞb¯ïzpKá=vÃþbÙÜâè‰5fT-ûna!d©ö#Ò/õö·²šxWxÝó÷:Tâá¦<64Ó—Lß÷Î-WÆ=÷ûØíb'høðÜ®IüÓ˜=ðæj¦O(Áq/–ACñMòL‚NýäÍw¨ªð½%™xC½ÓRª’æÉ'5jÀ»ySاdÚø8¢(~Nh|¥—¨Í•ÄKà·@×o‡6v|÷½Ï0N`Eóï›S©¸;˜PsŸ\BwWeê¹á5 ¬&øãéHtéû9ÒxrÑ0hÕÑ&¢ÒÙps—KYËç€ÝŸ)ðuWóž!CcÏmÄ]éËШø¼ÀúÈtsYOJ?>Á1ëC`äñ¡Ê€?Îòãp­m¿Èakox1Éõ hïIëbbÀa…ʺbIÖ\Ò:Á”_ÄY.ªÔuElÑQ…´*¨šYßG+Ó÷žCaDÔˆ ïbkó@äjHÝýc/É“è7¬W6®Ý5Âa«šXÏЋXºð&¸>úsF.¡û O ºw3)2ʃ+|¶¡àÑ€2õtä¯À*}g6ãøXVØúwß¼ §ÓñâƒUd¬PŠG\f®‡¸úÃ!èœU_k¿³Låh’ã*“w©ÑPIô½c@Ͷ]_¹’1±»¶ÌA—ž9ÕŽ›Ÿ­“7é˜ï0/c™°èýg8p9+Rß±¦ïQ°óq9)˜q_qIî²ì.ˆ{o¢ Å Åt­¹uá+·W3­&–ш‘žß‰“̨è:‹»&^#³ÿµÁ¹.eþB§ÎeøxßM­?„GYÜÇóÉÝÐõWµ&“FŸÅÞýõß,&½TæSÖC±eÔ©˜É©ã–cž,ï¶ïŽÞ†aÖÉ Mfã†hB—¥d’?ý]軬—ä\ ³^Ôá:k˜ýı½³àÕ·Å(:{:UMJ•žúÒæ i[èFÿP¥Sje@nu;¯ üC#nìºú_ ŸfBÖA}^˜‘‡Þ¶—éF;;î:º §X¨Ã÷™iôªŸ;w*Æ. •›•Pñ³ö }'Á˜û¯@%,rÃna8ù’[Z7A_Ã`ì³cx©fþêf ¶Ó FZ}ÃÑàh Vj±•ëuéÐ#7Áx?ší¸÷¯}Âî¶)ôUæ-÷É‚IŠô?=u\ $ÏÞÊÞµmg´£OjŸ'©ô.îv>¸\‚ߘ§Œwš¿±ËÞŽøÉo ZMãlïv ÷߀Óá0 •§‡Ë7@ë¥Î\éÐp* Ç“gÀÕ<Ð]Çv¼ºI~›ÈTVüX d|¼ÐºÌšª}°…óÚ§ ³b€Œ\ÓÆß[3 ¶’ã¾úü÷†¯lƒI*|x“FÜïÏž ¸`X?,óW sBõ°×‚1.´öìör Û'~tu°ÕÇì¨Ô)öÁ¢…]|q†ù¼ü@|bé›_’Ûr^ðÇe+Ü‘lAϬc°øÃt\\~7½ ¦z9_…ÃbÛ™—–uº4D¤Î°ÿôÔËfMÆËÍ3¹ëi.‘lW;üéhʲ÷Ðã!™÷îÀk÷YÒxïŽÓcÁbžÜéü$|S0‰X¥Å“E^À…ïÀÀPVþÙˆÑQ!têä@xSЈÚL¥¸™Þ|OŽeÞbâ­É˜¹Ýž'ùÞ`–+NBý7Mºw¹&ÕÔ| §½¥h¿ô`üîýˆ ¿®I›ýŒi‡çh°tjÆ[š¿ñöÖ3ø`—&ŸÎŽ—†£ïÈÙ@/{Ð.µïP/R¾;õ©ãGcPy£†Û²ÁC›Tø¾l&,–£uŸ¦»–œ´vÄéã8kk,À”¸P„·wÌ¢Uí7 IN„ºk…bèzn1žÃ £ºs3énuªnè€ñnka/~Eýq§ñA©(}ñþ i?nÆ… ¥˜¿¯Z¯†çݽìñãbòTä,«ø2ÞCswŽ¥ÿ'âÇRÐ~Bº©÷úRƒc‹ÀöÖhèeÌš]†C}ïá!'¡£ä[-Fž'Y¥ŸRÐÆ8ƒy\»ƒsÌ}ày£;ŸS‡[_šÒ¹ÀÏœˆ†>Ù+pr±TöíÄYÏØ¦]#ù¡ –0ÎþK]ø-z^z8 È…M}NüB©(Ï9Ö WíEéÜï´ãÝÐ úFþ¸ÆÀíß×qel+X­Œà÷[,è …‹°dž"êv¿eâ¡É8Wü 6˃+%3øºt€½,Í_ŸC¬üƒ°rÂIáÂRiî°²žõ‡¡EòGX³ç¾ŠË¹œ]Ì“á~¦sXÚ@1;)›%\ð6]ÌÑ¡Çh»ðRž!UòÌÔÂÔq&øË¯Žý͘À–}{HjËöõ¢?$ÿjă&‘¡êÇÖÀ…åbt¾l~»¬É=Ú¢¹ŽÿÜÀà«Úrú'ΘÒXQ˜uÔ•^úÒOBšd×Á‚ gÜÒù“ ó»ôøknVž49Ó€}ZJxez‚À5Þòÿºê¯J¡ÿi4„†â›¢üú~GþÅæ Ëú)ü¯M½2¸{HYy-Žz*âñï±dq Þ·».”J+#„Ç’±bQz³9I•¯Cï)oAbà ì¤4þzäÎËQ…¿9hœëÊ+î§ýÇÜJ·çûñƒÀJÝk„öK—Ü©dI³'¿VÛsÝ‚^1}‡Švoh7«w’¦±‘û¹îÉCxêZ¾ýQ yûÖýòÀëY`l3nZÈWL\ŠC{ã|•ÉD:x"ÿpW¸óÙ-²³Ã³h Ôxa¬ô>Õg;ýÙ›2ⴥ؄¿5“EòQ°goxÐuÇÚpïÑ‘¤òj ¼|r˜UN+ƒÑVÇa\Îp8Y&¸bRÂjüñ[¦L$sÜܱçœ=¤¾‰ÆT‰pÚb(Ýï1Š¿ÏÍ÷¬›ŠSr©ëLGòæÊªW£Æ—_S iâç`-—rP…%îtˆo'q.»„tþq"ŒþÀŽ“dé‚›‚à‘Vô?õ¤û‰¤ãv²û̶Ú¢uaýé(d–ŠôlÆI¥·qXµ7(ßÄ̧pÕϽ Ÿ~ò?ίKÔ™ ÅPãx|—'žÅ±ï°/]x䬱ÊvS· #ï³5å0ÿE<6ŠÀKî$lá9%O-J]Qwàâ ì_øgÛíE“/5u³ŒÍé‘6ŠÏݨˆ¬gÌÂÎíÅg)}`yKŒ5nÂûS iƒÒpj–%O‚ìùWÿl­ê]|ñ½ drØÅß=ã³ïŽàï^ãæ‹gñÔó¸eç,|Ÿö¼îùÆy¨fzZøzkr|#¦Èç½qá&EŠøÇJŸî+?'\4o¦o@_eÍ¢Ûà´ßH_6„)é~žT•¡9W2‰¿’%ž›`Ï%ªËàŘ™°G °¾b&õ²JO•›äÛ•XøØÜøpcàÇmœlûj9{û‚EÿD­ÏÑ4§Ã€oÓ>…É.»pþj?ÌÍêFãð•òó5†—àûkX‚לøùsx8/¾`Å {léâCêüæ²"d¸ßì Üÿ§É3+:Iõ°baÔ” þ,-îj|BÁÂ÷E˜’{Šª8CVÖÿLÀM1\×ÒÃJoàï iê³ú<ë7è£÷äÕ^ jÞ ogwá´5ðh…T•ëñ™_áW‡«pÖÚdÊI B&NcD׌¼Nö‡r—Î <ýw|®ŠC•mG™Ä…g¸xe«P*ø5ùZ ¿æH¾b 1€‹£EHÂæ2^{g<÷²È€Ÿw–£[èn:Ûèù«[Göt2´ÚÒ»BÐóÇMpÑò"Y6–xºÏÙmp£•/z»+ªq–C;Ô„¡M‹FCIÆïWExæ‹è¼#§ —€wÂbèT¯GÍö ƒ}ïÚJ‹Ð¶‰‹ |ä(®>g錸Kb²@×à:§/³Ï”ùýãŽ8©öiªv©Z»ú^‚êÎ×â’ãñï»nâ!»Ÿ+¦`šÊWÜòz2u4ÐäcœcaÎIt,Ë/.lâ¾wù©ò\-„M‘Tp,,­eUuP_ßËDòþŠu— ¥™›È.»’Z *§.ÂÍô8Œ,ã{¥©–™,Œ“j¡èlALjÁˆ—>ÐK n»ÓÍûɼÀ5°0ɘš6:@“3ƒ^}ôJm2ea¯±)}˜"Kõžô w­‹ÂõoÁ0R”ï3ÝCÜ*FÀ½#¢8÷z1ê*g7 º÷f¼þ5½÷¾ÊôJcÅ^5ö½=›}­ÆëÐiN SéF?×q¬ôÞ*ì~z“ÃÌã…(ß}šýW~DßbÖêàÉ$Æïù§Sy§Î)a`²sÿøŸ_ #3Îÿyê¼}îu(ˆ¶Ä,BB’ŠÑÊñÞþ8OÅ…[ã½ÂVX‘§çÚ– úz)Xjõ‡ü(XÇ ÖÒÕY#¨G©Îø¤Kë'Žâ톣ÿúqð&Â"®%oÏÎ;ã̸ \&‚]vë™új úæú{¹±—ßVêéÑcè^¹¶íE’0ûØ-8ž—ÃÖãpÅ"êQ¹¶_Ì€V@•º—â”Ý- pî‰7ê…¯Þ‘§ä@Ós¨Â&m°I§ 3–¹ m¯¥Ó›Ã¨e©5­nkœ±½‚1VÑ)i!Ï?¶L„Yç#aÉïnœòlO(7©^RxÇWÆh‰êi±ØXT:›Ã·èmƒŽû’ðæÀoüí“…ûëgñ™n¯‰åg„6áž°L/ÇåcwÓqªÒüüb„¼+2¨ Ž7o_†ßºI¤¸Ê‚†[}Åïkéð·Ãø²ªˆ¦cy›r¨çT’ï÷×Ý}aF´œFáùN[šØ»ŽÌ®féTܲí9é ÏGùê=}Ÿ ĨÒC“^)<1ê)+–z‚듵˜YÐIÜæ‘@óª7‚BÄðåé$2ò³`ÑþóØò\ëÝöCÑhSXÔ·Þ%]Ʀ±à ™ Îéê ¶°XE›òOÊüÆò͸ ȃNÙº4U!rú¤¬H×ýz…Ê !!E.Þ$ÎR.G †ü~†!—Œøk °ðø z['À1/ü8tf$·‚ˆ\!D.°àÏ´ì*Íúà}ððà˜™mŠÌ é¼Ú"E'IW°Âeoû‡Ó›ç&Õ¹nJ{›"V*• TôyƒJ$∉(îZOÄówÁ¤m!<:å’`‚v|£Ä«Î‘Ü©Y¸iê6¦ÿb;ÛÑ'Ÿ‡ñö廸(¿ƒ#Åhÿȱö8àÙ\;÷Õ%8X뎡26tõ7+>ÖNF8õAý•þ?å<…>‰xjÿH•n–ÿ å—7ᬔ'¸jѶµ2’žy¬OE–úÃÚÖ²Û÷3Qï|wËøê‡˜Äó\ˆ(/Æ”õú˜Ó{lˇ¡ò¤ßl×…}ô¥ØBf&ùD‰+YªõòBd6Öú|%Ë:ÖBíÞ©eH{¶né&”ÎXƒÚ º´;æØgþcav¥x¨ '>¼¶N…o)9 03.c†û Ot>†mî#wBn × v wÉ'¹‰:穳h¶ä>>wˆnxÈüeqåM)î/%O¾hùfÂïØOG’­€þ…{…3ExrR2öΡµ*z0z'(„ ‚ûu¬o˜ ñÏà²=þø¸QšVÈði IßÛ<è½;È¢Á.pê¾õ™3í_ïÃÞ OÞ¨¥ŽµÕ’4š¨ð&ï$£Á’¶¯ç {M8¹ªGÐý<¹X„ ¯­¨LL2¡yÆxïÝJÚ"åÂeÖ'·ä¦ÑíF°Ó"‘ßþBWOγn¥“Œ5:¥p,+‰6ggoX£ØGÜÝÁÛä‡Á.ó}pUä°$Õ‰>ý|œ…†Fä®|ya)¯ó¶á:ã3éëêœ{þ,û€ø¸Ê5ðmûJz·g e°]®ŽJ.¢ßR&ã¾Ö™TÂû#¨†È✅‹aì…`~Ýxé²^Œþu ÄâJ¡"Òœû¯¢-J6\"-‘¯[Í+wÅô«V¹‚c;š oÿ<4« ~.Ø} þ¡î¿Ãá‘ütMküãÏž+ió+â3Èùª:˜ýe#]q?®©©Ðƒ¶ôÉ M’tb}ÙÆÑóÅ<^b3†˜ƒøA_®´ ¶t˜Â²§Jx—óý*¢4tM=qU+P“\?NLZql¶¥0±ÐÏ/Á­y¡8ýg5ƉѨ éîŠç¸~ûIa†´8—l{MÄ×ÊÐéŸS°´s(¿ýyŸ:e,ûár³B]iÜÖo¨Xô/µFSaÉ=rö”"}²Ýë´âJ±2ž}1ŒkÚêÔÙ­Väþ+CèÁY3hªê+¼ç÷œ%µ«có®,Ò¢¦wÙaÇýTº* S¨˜DESHôÌWÊ·mXG½Ómyô³(¼¢ÓHŠü ©’Çá‹lU)Ü[Õ#œŠ±)%ß÷|B;w;t5ä·ÂZa†4‚z¿pwèÁlãÃÌç¹+Ïö¢üÖž ¼{) lBÛ£z¢J\ÂJ.Þï¯w çú@æ‚:üUíÙ‚7D:M”þü*Çß×™Aæö;xdK+l¹e£œøö!C¹éŸ/lÖsj·è+8ß?ŠU&‹@¡Îä6\ g¨7äžÅŽËf æÔÁ¡þ|ò¦F•.ñjÂ.=O.b´ Ï©Oä™;íh£Šæ ?y²U:<¦8Ž$·V‚÷%®ÅKØ´àT¹¡Û.Á0@œâ_ÙYò8~¤G»4eNAG¯nÜ[œIw©Ó—9#¹åÑrMQöJ¿ÅŸE1D/àø’¯Ui¸ŒÑöl ûï¿§l%Œ[[›oÀäùrœä§Ñ°ÃXUf,fm™‰f¨ó6™xÆ W÷í†ûl²Ú$Oì8*†•n«Œ¹ñ1ÞiI½w¥ã‘ܤR3’m±î,²ýˆÂª\NL‡â }Ôn¸D¢õÌÀù¡!=;äL¹û†=TWÁÌío!Þ£½nà$•|ŒŠã>m>4uÚaò'×Í>Ù%ÔÚr o•/†ÒŽ"º®õüý+†‚X-.Û½MC¨lª%mÛ}ïdí¡*%;ØŽû“¨i[ Þª0£¶OÄÑ&b*)ì<*àQ;× óG;rÛu*kÉGÑ%¨4…ÒzÿÐÀø4sѷ盽üèÜä«$(€€˜™-]¸F"¬¾£’Ý)Mè8˜¾q6|K•¥#ÄÒZœøž‚–°"Èmíís™’âAT‹Må]­˜©–mº- «ÛjqÔŒø—ÝKoŽhÆÑe$øÒŒU?…Ý“žà¿>nmâI­æ4ôæT²Êƒ ÊæÑµ­>TçþI0²õ…÷‹Naƒ´âåHµÛ²—M¨Ý½éðãÀ<3BŠ·™`Ã&zÏjzªuÐHÛá4Ü'ˆ?y—@Û=·²ì’@´{T¯ýñïêø‡I î‹0¶à‡ƒ{EqiÝ'H5ޤ®=yim)ì <ÛaóInv>ŽO\;Ûd]1>£“’ª (“óˆ=}ÿ~tÆäÿ`ÌÍH€¼ÓÄbˆ>¶÷à’W÷Â%þÈíá hP4å#$÷Cêª>3¢™éò¶ý0竾̦#˳À&r o…xpk¶Ç'SanÈD²“-¦ò}–lÃØ™#õ M÷‰RÔ ¬Ô šÄŒ¿]ƒ™RgñÈæèT| g´ZsK/Kj=M†^HÚ†û׌¡•lÏ•&ê_аB±uºÎ­95¸öÇÏÿÆÂÏyã¯gG`b ,]µýé·¨‡kºÉŠÔ½0Oµ­}€¯9,øOÛœºU‡;ý@Ñßéà%7@¬F‹»ºüýÃgÄç”ÕŸ2‡ªtÃGú”«ÃHÐż»rT/­,O^åm;¸$BܼFü6F”Û=ŸC¶ò"Wd¢é yš^g_“(0g>A2Ttm%ôûÞ©ÖÐ3_ÁV/´@áPm:ÒI†^6@ÊžÇ7oTÈ=?\±i8}}ƒr/³±ÔÞÇÉcÇÿr@EÃ;°Ôn ïGfJqï1¹ÜÞF’'}n¨¯Š&ñI <$®ß­ä{1šl§ó&« ˺#üh/OëM:ñb¤9¨¯’à§o±Üq4È:w®•åŨl~2”5ø‚Ä7î7Rš)‰È¯ç`ÉêVR¤4¾<…–›Óð»L>D­”B]A½Ëã0xòЃ[p0xI>ôåǃ2à”Cì—’'qޱÚà|¯7)ô §ñ¦*ϽùwÊpßDÐ:²•¨$L#NΫñ I!+ˆ²¢OeÄh}Ü XË>¯U½Pý&Rœ˜A' ¥Ë†‡6‘B<}¥W°æL*™zt äVæü“¹tˆè)4ìÕ羿lÿR%Gùù}Å«¶tÔ/5Œ¾„¶8~™¤BÝç±djð8ˆŸ½Åî¼g‰>dK<ÄðY>ô¯j~©ÿÇÌ5ž1òm¾°´L‘?èË‘ºZ˜ÐšŒµVâ£d'˜{2•²‰²à¹2l류ëÎôL©'mw‡¯½¨FæuëpË|BNX_éëëðkXŸu-™‚®-HàO³á†ù^œ”+†ÿå”Ö þLf}7ç£ eø±‰× x¨ö¶.ÿ~eó™#@3ñÎ09Jõ}öPõ­ZtQqŠàô„éxõ›.Bû3]¹`’\Äs3®rUµ ¦`ŠjïfA”°ž(YÁóæHÒQ%j¸ü¹[.Ïl: Pk­šiƒP?lì6âM£œ1lŒ'MÌæ(g^Œ‡sî &˜ŒœmÔèøât'­6çKsHÝe.8E¼G«ÐåìyZõßÿòJ h~€wߦñCŽ“°ª§°H˾Þðb;&â4}Cžn{»¤¤1ï¼Û¯¶^@ïÎÊÃÑFÜ2·†ÎÈ>Æ2ü:qtG }Ô¹üû¦ƒwê4Ùw:_â%MW•Ç­c²A˜ç(Ì:“½w‡ã¬'/a“KÌ\'Ç×ú/äÅ›ùÚN\uŽbÝxä§€¾ò4à÷^-†žº°ô; \Å×£é¥ß·ñß¶:´ÒW _wÙÎZèÞ¿‚§_%Ø5¬êû€êæ’­º_@wƒ9=·¬†H´æ«µè´#xi.4ÝÁ|ï§ñÚDÛ¥ ÏMçªJ§qãÇÍüN›¯ùx”9x04…rMQ:§Ã•èl¹ Z§á®szü¾L àê¨éÔòk/¾sr!rVðPã ËkÔ†:Õ"œ1û7îÁµØ§(Å/?’Àb„üø7”š*7•™f´s´$)Âyú ¡åÀ.ö»n5Ø»Ë5æ1ö¼ÞVœûÇxxÌwÁìÜ>³[>Öô”äžPX·öJÑa&é$('‹kÇ­…—~Y¬KÚŒj¹ÐzòäkÂs˜ºû%OOÇHEUÌ;[F ýi„—7;-|XZJ æàîˆf8 îë¦Äò§KóY¢zòbg¾Ño/NŒ¡Ÿ ŽÌ•Á|վ˽;L ‰Ûêc$¹ùáÂR.nýžlŸyе,Ðô{=Ù%ûG‡CûOyL úÉÊ—¾„_–}PÕ¬y “±vI ´«dàÜ~9ŒUx"Ü©ÿ]ÕN“"ÍÉh:{ H9E Ìç˜6b!í ‡Žò‡°¶ÅŸù™“¼÷Î0¶í0QñŽÀƒ~t))ÄçR6ñ!*îÒ¢Ýñ—ÔøSãÍëÁbÆ|¨Øû‘ÌlëžÚ /äãÉáãAúQz]ûAåÙ|×Å8~4çßùªqbk÷éÝÁ§w)ð÷ò1`jÀu­H×Ò*4Ýò‰ÅÕãóçžuy7t3¾û'"Üòý9ñT°²©s±.à)ndƒd1¸}m2%½bC'óm¦"LÏW—û;ésaéy¼iö%ŒØòíÄkØnš$b+˜¬¢Á}–Y1«1*-îý¢;8tÌb˜%”ö>EÅ×'Y—‹J¬3P^}ÈÁ¶ _p[l o_îÎ9Of[¦ÎæÃ.ÜGæK»¬ãî¦"y3ŸOl5äp5”*BgŠ'¢\ÑGñ9 -ò†+¦lTy[¹@ –°GÌæ»$?<éñ˜°Îíàà]o*fnÄ_Ü=:GðŠ¡Çe?zŸ‰‚ŒAŸb»Ðˆï¼¬A™±ðŒY¾è ;¾¨œø©Ûr]i=fÜ“ “¯€Ë/2`щJ šš ÚEñÄŇèý5’Ï0Â06 «‹uéÅìÑ›pÙü |E|ز˂zíK\|Ðönre¿᜹nøï·&?íù¶Ý×≣­è²W 0ânæŒaƒt@5ØŽÏ6M…– òtrWÛ2' ÏŠ@MÌ8˜½N-]løö Û°¯RÏå"tØû½øP)E¦&±»çÙ ú nà ¿*CqÌG˜áN5 eùz“M8Üm2š§÷³áMè5®ü¿öÿ–¢uY\]]”[AÛPaëU­d_*1ɺ|H—… ²¥Ðy§}*Åwo¼‰VM§ëe™l,›ž6DøeïɺÁõm”Û áçeé¥]j¸ä†3îX®K‡]~ŒÃ†6£}P ¹±Z޶XpU¹I¬öÏLj5Õ;ýêÂøÇ;È.²Ój£Î ùÓ‡ÇlkÑ%Ýx9>7ØŠÑLY'4áÉÞûƒ\#Ò‰Í/‰ß…•hö¤&®|Œn­Æ1ä˼ð`x®y Å~†4ÖK¨ Ÿ‹]S؃µ¹PðÓNЩ—I†À¸ÿçqž{v,5g×è»yõê$ù·A Ð €S"Ñù›"ùr\¾µ; ,ÄÎG©´õømî}’‰M5#ÐlÍ7ýƒ¨?û˜^}% 'Õ“¬GÌZcùx~ɹ…ä­2a»­‡SuÿL,Ù¸ ¤C.cÞa`ƒžÏs`L÷5 ™{Y´ ¦ÌÚJ=VïdW óSl¯ÀKo?‚ŽÐ6&,NÄv³-pg™$ŸÔ/éÐ)½t{ˆÞ©Hìm+D9n%ô“â\\éèa„­p†'^o…k-WÆÉ‹ìËꈳˆWXo–;iP>TŒ~ [†¦ÓOÓThèWª5ïÀ잟,œE»,¸]$af³|KçÌ=ð­u ÆÞ{‚O×åoÆÄáÛB\> îžF+Ëî "v£Ûæ}ÂöÆ<òÒãlÕq Ò•v|቙w|ýü~º@;M‹¦<ûã?ã;9"°]È5œÇãWf`ÿ¢$Ú˜N¾ÈiЗžãÊ[q0·z ÊÙ_Ç]lI®ò?1 ¸©¾nÉò"¥^t¿·'‚±|ûîóÐü"k߉Qí°fØü^ß<¿ö¾;Ϫ*y݈Óxôž!¼Qf“‚–ÃÌépûž ¡CR=›<É‚¤eúa~Ã\x÷×z…/„s6vCWó~³¼!e‡ÉɬL¼´¬úYѼ¦H7Iê”ôôäš’ÞÚ>˜6é±0‹V§ØÖõ}$?´` \”C”; éëjOòï[¼hˆ§Ã‚ôèü¼#,üðÜÓåŒßµšã^róÿy„<}ÿ”ü2”¥KcΡ‹z;*Æ\×c“¸Ã‰Ï‚/…O1ñI ×Ü£Ž×(ÝçYC3[QYÇþ^·­Ü‰uú>¼w ÎjÂ<ºTû‘ jà"Ñ»ü5Î…·ÔEß%/f5ckëru«(^N¬BryxÝûµo ùø^ Uâtäi{TÕ¹ …ßÛðÚÑëå²÷(Ö"†¶§­ùûOoÉìí“qÛùY‚;§§ Ïh.Å$ß;í §²×ãb“0˜ú܋旓ÕÇ à†:5±…ü—óyîgØv\ˆ¯ÆW£õƒÏ‚¼Ó±~½þ’é>Þ~õPDe9,ž‹¿«Éž‘’@e|˜Qu‹R‹¼’( ö§÷’5x[z¤À@î Ò¿õ c%K¼ç®ÐN½øD”e §jro­g|9*7|'Oyᣕ{ dB1$½Šƒ¥#¸ŒïUxyˈœ{ž¥-†ª6˜. _—Ao‹:Y[rÕ|¥ùþþRpØk²w@ãq+)UÓ¢ÖáÊà9í9³àÇÑù UužÎŒC“Éñ ¶´²Ç¡ðe"”]„¹ïaA´6ÄòA?ñw5æ«JÑ&G‚µatI t\©ƒR×wdÝæ[d«ú]h0ûÌŸHG<C¤ÇÆøªgð:©’­ªObËÕÈ›֨c?§ÅޱžÁúÏÖ¢ò;þ}Û´ù+ÁsO¼…g7Î‟õš(‰ã/~ bÒÜõ争ã4aÝé ¬~eDu/zñ}MNðÎO?îIÀ‹M¼¨àLnºÄ.—Àü1ôFE>[i¾fÿù,¬’cÐîVÞcæñ‡büFÃ>:!.}0YMº¶ŸÀõ|YI‹`Y‘"=éѶ{ײªzoM4÷ÀѸÒç æ^O­÷b!XðoQa@y Н‰„†¢<6ûÃ4 ¦³ì'r­Biº­º©³Õ‰˜I/«˜ÿÕÅør£­}r´f°ëul‡Í:ý¶êxÉ2’.Áee›{ëXßyQöŸ^ZãÓzæôU’î:H®­R¤Ú× è#™‰phš!¨¾\·&»Ðoþ²˜h/WÚGv® “ß0õe¼;vg*Àá©™`x¼ˆÜLˆ ÍŸ²aØx@“ºNö°¬¬ q‡•:]¹º¬-vŸì‹€åLØÁàœ’‰[›†mÉlCÓ,ö½Ý”FÈË‘/…Màlu‚ªGÂÌw¹˜ÿª‹üÒ4¢CŒ#¯ëÆä9VdùP-òíƒ ulÉ ª/U`œQ,[µl¶‰¨ó˜— ò¼äfÜ#¥“‹ëz ÙzÑ0gÁRh|¯†«C€âvHñß ‹C³ÁÝw'«Y ¢S¢H»¢<Û™Mp|-‹M\"š¥X´¹„øq€}}…¬½qHêE zB÷S“K‘à}"?ч×-]àת~W¥o)p'`Ì9ßÁ»^âî~ÀÎz*bXÔ³X’Y ³DÂq; ìÊä/ä™Å<®#ULRSZÉêÒOðmE®}»·³¯P©ä§.º‰/„îø(YŠ=R|‰‹–®ähãÇìdò <ú7‚jzŠcw÷b*«|z©ÇW.ë†;ñ…ÐÑ0qØ€»Ë¿cÌ€<=×/F¿~_L âp´û9hћą=d^²«JAÌq97¼Y‹ =B¸Œ>/ÓÂ3[•¹ç¸8¦ÌNL©šlM¸¤|UØkœšÏÄä±+¬W¨bU³Cgj 1ê½({v5Wùù†ý6é®Ý)„Ž«ë¸²ŒÌ)t §Áò¾|Mâ²dÿüûˆSG&.1,Þ€H”;›ý pç=½Ãð3§/OÙÀD#yøº0šø‰‰Ã“”˜ÿôÉàö èZó¡(ð?c‹Î`´ÁDú+v7 ×úãW–ÃgM9~·R‘§µÏøÿo±äšp«¶\> wÆDS¿Ê~BÞ^ urÔ >Ú¾'Á³ïx.±œM:9hjP—ê»0Õô%:8W’öçIñ¹`ר]áŸ?8]<>¨_/L½˜rpK’"ôÏÙ&¢h+M¯¦ße/ND’ê/¯À5Ë —.s¢‡^«ÒOùpôÞ9˜\5ú2ЉO8/n…¼õƒûÒ¨ ôãäJPªœÏ¶€æZ ºÆìÆxÈHÆ\ú_1¾»‘>JPIü ñck„ÄQF_ÈLZ\õRÇÃÃ_¨%= <|+…Æ…äÈ¡kD5H—ý5¢Êsðäi.vÏŽö?Çg×]¸Òùµ7r³±#ZŒÜbK×Lpb¿y*}!a Éoaê“r<ûÁº¹ÉîEõ‡f•û9•: E•è8›w·;Ð%žÎàCèìÞX¼è°“Üš…î³ðÚ® :³ØŸ†ç ã»§H Ùß*¸ȉÀ£ÖëŒMy´bl€ÏY$ů/áÖýiÄ£èýéKÉÖlÐ{eÄíۇР櫨ïœJÅ…câèxŒ99ž†z¾Æí×:ñCÀ]#û½E³öópÅ'‚ôy#9Ýfµ£ö1ïCpL[4H÷°Mã62kqEj’K¶ž“æÓßqû‘£Å7~Ì€áC"éØSötSçeÜýÝÍÊྺ fž{sÍáoà TÙ>Œªø+Aˆ÷žA?ðþ´`š^:”$ˆ½`ÉMµžR«Õ¸çM)¤ªÆ£™²°3O„=xÞ+bfÒ°l„ݪ‡ú¾v¼¼/'VìgÏ*ôøÉu:æ·¶ ; °¥†5…£ÅòbAîŒc‚¢ÃKÙäyÙË~ôÙG¶Ÿ ï'nu~8õ±½ ˯ZÂ^¥ÇìÞ¬NДÚi-î°¥.ƒ/¼ƒgðÆÚ7ð2 ço¡ݽÅš»R˜ÔŒL6_­@p}ëAÌyìÈŸŸžÈœ e×~aðÏ_x8øusN¿5èȃ›ñ“ãÈÕ“†‰×uv‘{Ánê.|HoÎÊ¡ˆ› aò]þÃ:Œ·;ŠSìÕŠÏÞ ŽGrÿ›ààä͘×6†[m<“æÜ£gž vÜ8zâèJª´ú47·:‰¹ê[ÙÅ\ºr~Iè›ÁªÊà¥ÌAáÙ³K:ÃøîuG°ÿê|Ú¥1jÆi’Gûcö %þzh14Œ·dèYqJ²åø“*«AÞ•†óß ôÓ¶ïïjðë^È5¾UÀè¾têêp%:æò€;ö\2ñ 8<–wÞy‰*7Œðú×Z*æã@ío?ƒ[iãðïÐ.Áâìô›ë°^fPyŠ>ž×ATW’íxvfjM‡ñëTù›<$G6½Íx.¬¶Ž„Źkyõ£ý°m¯Mœ €ŸŽž…¼)ûùÃEÀMFèÙÑ-5–TVÉ€³ ügH,N©¢tùÃhªl¦l¬¹›^9Ÿ:0—eêÑÔßëÜ 'IèM˜s·¯LOáaÃíø…3„¹ùgê8 ¯¿ vN\ü‡N[óš'×£MÊ!ܘ„øjø8xøqm‘|ŠÉÉ ôàaS~äÛTšQ±–ûÜ #“s\²%ˆGÈ}Ààüt’~9=8¿ǪvÃúyž|t• ^»UAæ«v•!sfŽÝ}šÐl–ч%¸dä7øû´‘IŒº—ƒ†Ôv<g¸ý°à™\ž>²|&~bÿž?Ä4ç ÜÞptUñØá§â‚¿?S_š; .i(q¢zø-*J0l$|•[†;¿T±Ã†4ÃÌ™¨æWÁ²Whú£T§gà˜•{a÷"Zr}ÉÜö 6³Y®Ið~ór.!–$ŒèGå õ°sÁ²qt.´+Î$Ì“¥Gþjóý›àñøØZ\Y;d­÷1š»F/‡ÊÞ­hüØé(ØÑ%G“«a èp•Q{ð@”Ýa"ü;û n¨™òÃø­R›¶Ú2¸èÿ›y/Þu›‹ ï³ܤÈâÑ'à÷åd”XðWNˆììħë·ò°šûоÄ+·açµs a­(Í<ÿÞMaô‘ÿò6ƒ¢S>œyö˜,|wÞ½[æÊÐü3±±ÜKeϲ+O^GTêۦ|8–’ÅáCy«õ®žBQ×*¢ØGpr5²ÀÀ\|ù<‰X{GÇ[;jdñÇÑLrÛ, „Îîlß*=á‚ñºô֛и1šeÔù£3/1öæ8(nÅמ¹†S.¹à U2c©®w…Ò[ø‚>Çì•. ÕXySØÿý XŒ ¢sC¢¹ºk¿ñv&ä» ëqŽw(½ìæKÜ[ÎãªìéӰǨ˜p/½NJÇÚó¥ Ñ|ˆé"ŸUÃ'ÓœSÉtàs æ\Ñ¢jñÿòJ_ºöü¤sÁîÄNê4õ"loýK72-¹A›¼1”÷¬âGRÝé!*N½¬”aÏõYtu6h?ÁÏŸc1Þu(ìÙóä÷œ$JOæ /}'ã—¶ƒNw *ôÛ³û;5 eá.µò'ú •«âÀðý=öÂØ E:âù½ª´Þ±‚9íöæÆœÁìÛ¦¤üò3¼ù2‰n¿_…¶ ·]|&›bpÔE8Ÿ<Ž®D ¾99Ÿý§AV*ªÁ‡fñàihG2ߊÐ\½ã(+.IdŠnâ…aLô¶9tn)µÙh÷[‡Î3Бç…èk“‰Ÿg1o¹˜oZõÁîÎpb1Ëä÷^f’V°Ãå³`ÆÚ"Ø`kÊCûáÈf ­©è±x³ðâ›XºP å÷cõÙßì©Èe˜~û¨ðÒMœ9{?¹¶(×±ÀœvôÉŸ¡(ýGOƧâd·÷˜›‡µä(¥šÁ»æÛù¬ç4B )m˜I¥ŸŠÒÅŽS‰ñÂx8MÌãoA]åìy;&}„ÎÚ–¼ùôŠ<…ÿÚn1ÍÑòQýÏWœ¦ÒKzõU û@$ýR³–ˆî„ý^˹½ï)nëd+ǵ’Ç~â‰]ð¹Á™þ< kLáWc=éø¹‡GyÑÁcžôŽlÕâß0*K²07§õå&በÚô¢ç[”œgGƒ¿rßß᩸Ö_,ëÚ-°Ûš@C¼yè¤2¼® þO³éþçnb?†KK­€–JMv5Vš®@Œ².t.OŸ5\‡<=kîßåvGÜ‚N}.G‹5‘½W ®9²|Ô9lJßCâ]öb˜J7ÛX!à¤9Т¶5Vƒìi;òB½kÈ#Üyt8Ý¿´‡•~ùŽ–zR¼:ì îSXˆSÃúÁÝé—`唸)¾o”ÞFgªKð§ÉŸ‰°_šú•,C‘Zh|j„–_k°Í$ Þ…+ÓþÐj¨•‹Áÿ᱋Ñu¢ |4öH§c¸•? ¹W[Ìüh}Íe²åV64Ƀ¤ç(8®÷Gíâ‹ ðgõ/è?–ˆïçç1µév‚¬cáÍZ'~Éø¹Sd…â£ùíçèÞ±ž˜8½Œ¥W‚gåVvrC•ë{^,Ë«†Ì#ÍÏ \„«®Žü…˜öÔü9âó”q[ùlP­;¢¤ùÕ«p|O|1L:‚Û£°e–Þ¯ÕâÃÿÀ£mµüÁâï˜}ë,_“ÁÇK÷¯ˆ¤J„&ߨË?Uñ±¶ªÜ¯±ºñ*¦¤VÀŸŸ{è•Ý*üæô£šœEÞœJàͶÛáØÃ6ü4ÐË ¢n‚¨Ú\ôçAÖâÝì^è>0|6‚û—ÂÌÑJs?zxh¡ÈèR$ÊíäŽÐä§à%Õ³¬QJÎ}#E¢©D\­Ê7m'žFؼ*GêriãÏ`©ß‹GÕ ±Êç_u¡'ß»ò,ëg¨Xé ‚<øHFùYEX_9÷›´Cλƒ¾Ô›ÒÀ¡ì‘àgyR{»<-ÞBqÅŸ=ÐqÀ«~Åÿ ¡}R\Ëé>HC#cÏ¢äüt<£2Œ'šF%ÝËëž·¹ãÊ–±¸ÈFŠDU y͹¿Sòà‰Às0¾ì…MVš¨è^4®ߌ¶âÙö‹Aoñà×öã¸_…$>%šÿzâ‚¥¾ÊôIópp­J ÊvÓ«í9X¾ú56Ig@ÝôåÔå¬2ì84ê6Öp_œwyé¹ȶÿ°æ‰*Œ]ÕOòŽƒÜê»x ­&)^ëÛ™÷ô;0¿¾Vàqý%Vµ…s‡^s*1•CT“!Æ/»‡óÒè»QÐwRž ‹e;7_F«t¡ÂbüZ’ Ò¸~Õ|‘4 ÷”53Q½~òQþ8„ÞÙì» 7üÙ ñÚáÔb‘]} ’·ÏhdbÓ¹ã2snÕ5ÔìUú  D+Ï@oê*jßWÇÇæ÷ƒgâ´ÖÍnµ=›ÑÞ¤TêáΨ9Ð]"Éÿ–d³·¤{·íõüˆa‰B¨ß·ˆ”¨Û8V†j]N8Þ£}Ê_°Ì¥‚ŒÌ±|Üá8Ú„oHåíohöK‰/#FtšøR}Øh(>ƒœ;„D·]‡ÿÆtÿô0,}È/ßpésù„®[ðkýÞ¥E[ÀÊQV´Öx«N”¢ Â, ÌÒ#>onÃx¿ p_pL óá½+`Ù¤—lB™;ï €ñý–лb>¾è#Fæ&„yûÒ8§þãÛjzxÀ@>¢;•íè•>#>lôr>gH jä÷°~eÞ-†¿Ä©orË’*&qÙ3]ïœÉÁà-J´1$µFûó+sW£»±+óþ\Áàð~õU(4ýZÁ"‘lß1?7J ƒ&ѺúxfÈxj{¥šp™-õ|Ùí^òFïNÝ„ƒ×˜C³U™ 3D~õȘ÷·Ï!œîÁ¨²7¨çþÒ{ -_QRA†èüz1oˆRƒ#ê¶Ó›Gï\šÄ̇Ñ.ã™™úAppû¿’&ò†O‰`û24·Òï/RsÖl@1ù¿ yO Õöóy•[ùÙn#¯¹Î-Þ—Õ9éàG úïe9½Õ M·eq¼ËDòlWÛ=ʼn¯ÔÕâ‰î Tõ[±Þð1é–]G—Ä¡[Átþ+~,ÿùt _I`X¶€üŒ¦=·ñï ¨Q‹÷]Q /îž¼º‡Öˆòd=šøû¸×Ìà…ßÐó+…›àЭTþÑL…Æ-<ÓÏ>®à†­ŸY£i¸ßÎÇNfSNL£N»iÿùÉ4mŽM;}«ã!“$éÒ¹:4l®¬œªE+Wª2¯(ᦉüÌ©‹îÏÔr‘ÂÔ¥u3œLEu`á‰õLr¯yìÂË-\轺ü}u>o3ɇQ^Sùªß£h“³Ÿú+“{ëŠó¹Þ7éý)åÜ%W†_ò3§o×XrÞc€è"ÁûþÂÛ³æò¬Qê(n®»ÝGþ¥‹àæžôup*oí[Ê7M§åï>‘‘+ÿ²WóÆòK~@zð$~`ùt¨8 0òg-Ö‘ƒø3Ü’†=Áùú2|F¦·Ldç}0}Nð˜˜,OœÒ²‹&´©Æ$®ÇçßÄá¤ç%«ÀU‹I÷Å&ÑLˆøi/„/°êvçLÃoKæ:ÁçŸ.TEWŽo_ÕN“© íÆŠÉõZ»4S‡2A¿ =!3 ü4h÷·íøAëÞTO¥RY÷ôrøÝV}Dè…-]ìr^=s´–¦ÙÂ30»'Rh7(I„3‘_6XÓróÊ„çGÍ综%xÚÚô?ê Òí£èéëáhäjIg…šQíØ&yB„ ‡f#sÈáۺϑ eN ŽNn;¢OÏô;Ðü*Iª³ÌŽVü€@V¶Ë N«‡ì¶ Ð²ë¬¶ÐPqâu6ç™4"…ÎÝÑÔyf?þ¬4âë¯ìcÓ-Cù°ûqÅ«óv; Úž1·$A;ÖT¨qqíÀÍÎÐi7”žøö ´¿>§Ç:ã̆Í7²ÿ®Uù++%jøq;d3?Ûáöïk _5~{­7¾S*Ç¥¡SX¬H8yù®Î«é+V•iÑ?³ßÁЉ2´J· ;ïnÄÝÖ­äÁÌ|ÇÀ0<_iÆŸ¼^Œ»éïIÆh•þ‡”,å Œ:'èr0ÀòãáUØi|ÿ¹…”– qÅ´T(óïFZø?ƒ Ï/MÙYãd_L=ý Uˆ»)Ѐ. ÙÑÉ$@Ô£U“ðä¹`äu¾X«Òñ[Ÿ²5$èá(!ÜÀ¡tµ”w(׆{³.ãô‰átÄúÌ­xHòWÏdZï–EGª¦b†‹cüáã/7¼ýÂŠÊ ×åžj%à5lùF0>+›J?ÚƒÕ-˜—Iì·[Ø*ʳ‹µ1i‹7¾º~Ž?ùIN ¬{•8/¸Š9'sᦠ=¦ M_H42‡Q‘˜‚¯ðï™]àõE›§Nc#Õ„ð_Néú ÷ÐkT¹°G¬dtÒÈ¿m#èU?et/|‹Î¯”aÊ·!4.' ¿¥Ž„lÏá´b¼3ýáòmQ}DÀÍO0guȯâfgŒø…mqí¶(5™[mt9 ‡ÚÁQ=+L‡R÷%  dÔ† ûø ´ø}¥­¤èuÏJ<õ£2dDùž§{pŸT8ÌçÞ?ƒ×._ _rê`¯Í!üu§Œ6pçl~Jã[jóÌ Þ<àê1\^RÆ·ûÑüû¥˜ø¢‡ìT¡;bâó'¹P>ôj À$£<¶îhŽM•àŠ›Î õƒ$ârë L ÙŧY”"²J®ã:]¾üDíÁô¢úz¾Í\Ê%TÀ6{-®Ñ«a­“¨¥… ÿã%INí²G%íËñµécA;û¶‰Õ‰½æ›¤dùoÅ Pì¾9{LqÖ¦Õôɵ;Ø,w“~EÑÓ6•žwà½Æ/ˆÈèsP¼q<·¼#…:ƒ{úÞEükÕ{Œþ)ÛV ñ÷•p|—æ¼ß*[1 ÂÂñú²¤œWÁ‹ W~~±<2â ÚÀ)`Æ>Jef1¼òS‡ÿ—z®¡tÆ[R¹¡‡m¾±m}^~|û› taà~4ö’¥Ë• ˜ÚV‚‘ þm7õ¥ÎI÷0z0>¶šE»ûõ¹ÖI=.#ŸGÅÄöÀå½útôë¹|º~"_·>ï™—¢…Û5êd«vÙQö.H^OÙ€}4¨4=‹½†ó¾è"zE7dÞÖ€›éi>#b“ðòð,lÈÌ¢‚½YgÎQ˜:0oËÒø²™4nc6;²BŒ[æïÀcSª•«åØ ‹c¼%%Em“öñTê`ºTIÃ/¥Ðæ ËSóù¡7ãÀ£÷{8¹Ø@)ªÍd¬ìm¨νÄöºé‹§°Û1:Äbùcá\÷“°²¯Ÿ„©Rá·{l”ÎZõø)h×â/ëøL•ÑtmX<Ñoê”~ðäc`ÙïtxƒU'ËQ]ò™ðÂl!ü,”©ë°:ƒS' çw.ÁÖ +ùùŸOßr¡˜T# ^/\õü+ú(O ïF¨R‡¯ýèûO–·„Ëq—¦ôWD",”Q¦'¯a‘%‰´Pã$ U†ÍÃw.  û†) ßaîíSøéŽúO<Šá+O`O¾2JµáqÿjWM«$(¾6{èSšªKK®_}ë%øwé‘ÀWr—## -oƒ²víD\š³§ÝJ'­"›ù5×D¨J5ç‹ûò¼yɨT‘ö3Pz‚,ê1¡*í'a¤B ÞüiJ½öý´€¶õL~óY\ðS–VH¶:øó;–«Aüâ{ØÛu ¾=‚êÃÃ\ý`eþ/ƒß¼Á”“ ‘ÑÉ·ü˜Ï¤U.ѤZ‰ÿßÏ‚³á¦gø®i!(‚õ‹‹…3­Ë Å9„¨s JêþPá:ö.× a–s+ûpJP㥮åöõµT{öó‡} (Mÿ®ÔçY[0Í_‚¢² ꧦák+°‹î!ÑïîâG‰^ ,nfg>Áþõäçq7˜H]ñ¥£î÷†>Ž£rö1Ö9Œ[Oš P/Çsû„ð]W®]øߥéé«™PQ4€%58‹/ÇÚº"&ˆÅ+j žkÒ=¿àYn¿{÷3æEÈcmÌ 'úNÄÃáìRÚ[Üh@c‡Ò53 j;§Ǧ¡]‹µ"tôA]â>RŃ.âŸì…†¾Ùi†â+_á½ñpl†w4 §¿ªÇ»ÒTúôJÇ`âßfX>¥ÿÓ4ǯF«vcû—ûì°–šy0RÙ›E7á¨y®1‚×îaÉÒôÝHŽÈe£}¦óxŽMÿ\áí­ÄL°?ïH÷7 Öw<[í}ˆÎœ Þ‹èÊ%"ô§C'|¶×¢_…7ÙœÙ÷‰´Xk8á'«<µêFpñVC5•OßðÌß&­á7Ø8[Ü8´Ppio¢ÞÎÀ£vù=*÷‰úk{*çzU ?ñj6äÀ¦É/ññ¡Xl›ŒiWCqãZà¢{;¡(Õ“»FÂå?õù/ý˜Ú{>¡äÝ_1ä#Vó…º|[×ÿ;É&lNÇØqù‡Âü/Gõ«¼)üžÁøAê.,ûª‹çFzâó“ñ1k…z¿dá¡ùsA/t@(Ù6—G|†uSö2^Yoq@é{ù‚ã“àZç Üû@å^ybGûSü¯‡¦÷á–Õ6ôÂ.üSe0XOj5#°òϬë,ü±GoN‡a7aÌÐ#°éé<´´÷Ã{™rhyà îÈ…ãÚùpÁt%ìÓ,%Ù~?!íg©¿jÙùuTæÂBX‘`M8jb‘©ÜMIÃØùyäõ˜/äº{4z­›Š¥ ÓáBÛËù­þ .hž2WXèQ/æFb‘äsòjÇ §€IÇ9 +_ fL•'ÍMß±Ýsï—ZÃtZÎá½ÅFÀZórv~Ë&öó®ß¹Mά&íAŰ{ï4tÛìÌ.Êcÿé‡ ߀ŽåžÞ‡Ï¾f,ª¡LÉ ”ók8±Ë/è,Båº4¼›ñš„,´ÄÔr%>åÐ*žxC…¿ÉѤ'ýæÃHqG´8œ Q‡†ã£ %0z|¶§õcdïc0é3âeOưQG„7uáÎþïÂÞ—¡ÌðrÄH죇ûƒI–Õz0m˜‹¶ÛçC ¦ÛžÖÎ Ó‚yMo*²ÔbÈÚIkàq©\ö=( 'f’âÝñí<n%@{—xÚҌӂÑ7û")7Ž…çÒw NCÏÖ/@æ]ÇM[ë…º>oñÕæ[ èyå‹F ÷‡€º/ÑãþÇÑuÇSý½q{ˆìQ”ÌŒ4¹ç9D‰BFK¥’ •†É•Q*BI…$q?Ï14•Je¤½ ©~¾¿¿îëÞÏç<Ïsî9çý¼ß¯×û~.¬Ü "èw´ö¦-ÜÑŽáFdB0oÎ-;o†X.L‘¦­‚µÜçÂTr|÷/òjVÈ?q•uØvñ*ïâzÈXßÇM«< ;í¸2å`ûô6ÈÅN£+~!™©ÄJÜ¥©œdÊo–c§[’ñLúnL½UC|õÿáñYðïk¨W‘´;¶´FØ’NRT :§lØÛǰS³d÷œå®0…íYì`˰°0B?ÅnnÚÛ!@a}¸˜…{§5“þnôæ1L›ÅU§:ùó¯$³ÅfXj°7Õ™³#ÎàÚø]³‚›ï¢‰~_y p!Ø”ûsšåuØÛëgñÊš8â³i-PØI2±#_æ.>‡ºg\pøJ"'‰¢tê¹ ðP|>ûY‰W'š?Þ'RxáNþûÆX@YO¢ð?²Ÿ#ŸN@åîÕxÍW‡÷+£F®5+[y—¼®á¸É·9õ< ®åà€,*>Ïý¬µçdãnp’?ÐÂùÆmÓƒ µhRâ?lv+•Oãé–YD\ |Ë:¹ þz4èºN†>ˤ¾Ã² rxáÔ1ÔoOÂÐé©Äc›7g;û+±¿¯K5mRñà»6(ß±ˆ¾?° k…¸>{YV3zö£ýᇴgó7ªžÓ™1­Á2R¼¬ ˜Ñ%^Œ"êíÿí{1eD“ª,hÍÇPMÓ¦ü95keâÅ•q3zfÒ1…¸êÉTèÁ/mêÇ;`jÊRº`ÿ|öÊ/?PUõo<­„Ë+Í`žÂ¯c.ai$×!ËbôAà) úõåçÙÖ‡ ôµönŠƒßiœÅö—`¨ë†Ç„™Õ}è“’ “Žò373¢›Ò:EÓAÝ® v-!'ØN^Å›”]éåòÇ¢KýIÔXjFÿ-ûÔÂ0¹B”a~±Š“‡¥Ûm8›ó+ ʰ ^ø’€Ôbþl:‡¼ñ1n—C™öЙ‹þ J*€T'O‚¢¦à›:Ï Ï‹ÌQ-<‘•_=GZ¾-ÄuÆÞ4°®wDYÀ•Ýê4Ïi'v¸J±bî5ÿïspµã¸3ñgðª„;Naã€ë>‚iÙDzcñ}5#eOçïKÒ/½#Ü¿Dðmts™9¨sms¿ðSæÊ°qŸCIáª[°·d L4fІñ¬µ$Þ5 Ð³ªÂyAl±9Œq½ŠªÛ¬`|˜_qÖmn€·e‡a¬*°S:Je¾êSÑj%úÉþ{]†ë'ëǧ¹50Ëȉl6[„]kÈx-ôgÅ“›ïœÂy²Ô¶œÔ.„Ý›ò<ø¹x~×} Zû™»5ýñp‹'N9qE3.â[¯’ög.>“ôFI…dÌéÆQrC7RÑü²¤Ô¥î0ÿ\xæÁ^–…£”Æy|0ÿ4ÊÝÑæ–Ì?ƒnË_pvÇá¦0Æ9³ÇÖûÉNÁ‹°õk*ÆÉqP5†=ÈyŒ‡JåØrétì}·ƒjfL© Ð_„õ?â•§JtîFgÿ5®ØŽ!üv2ƒÎ§‘‹\FU2ž7ȃM·BAé½' ª'¡›"!¶KC1…ë;ëˆ_D&€2$x>Å;{Ë¡è;Ùøúx=l%•³?ÙSÄè:s]ÎN¡h*ÀÔÙ TYö"¿t+ø¼ˆåÿ¹3‡•ªŸ—Sàž× ¤k \®fš˜€ÇõW\ŒüI¾fùsøê„Üí\CL=/¡Nͼ©r›òÜè˰ÙÒµSÒl¨B¾,Œ{oŸ8¨«ûŒñŠc©ðéPÞsù£øú¡­±, ¡…«¹ÕrûÁ¤à;<ˆz‰±žÅx½ÍÛƒÕq[T 1I>„u§âáP€¹»"ÍYžü3™8kÐ|lâ>¨wXÏÉð;ûQ›* E3uQ±2”ôgÞã› ´pUßG±¨.“D8®kÚq›x~ó¤?Zÿ±QP\'DOÜ´†•sèÔe+Q=@œœý!Šã3 tmNÏÙŒ3¾ù`ë€:?p¦[Dƒàÿvpº_/œÃ܉·áᱩlš ’ƒk³ÙZ5{ôü‘CÑSñý©:^sË»sgêÓ»úëÀà±"»”ó⿸Ü)µÙœ ÃuÐiÈ匌ˆÃQ6ðê ›³£Pm:ÌWì†wóè—­Ü’k÷á¿çF¿TtFMóItÓex0e1+šÞƒŠ›N3Îv¯ú}¶-ó"ì¢ãyÈÚ§EÂZöòÇNò¦"jÏáÅ"h-9в!WpCª ¬þXS_™°þÇ·ù_? ±åÛps„'/wç,:áÙÂ{WûfŠ¡êttÒØç*ñÛ‡ xÝ3Ok…ıP9pV).…7÷Rÿ>:¾î6Êä7£ÙZwüi¸²ŽyŠ’=C7m1䆜cŸÿ)ÁOž)SMrâ#ËæDRhéùÛä¨ú‰~ÒE$÷$]úáðü%¸}¡Ä—E‘œûõP9lźu[ pM>®?÷ bJL˜g“5öÎ0cùßÔHÒQíú¤‹o¸}>·éËlîP¸4[r¹¯5drÁÊÔåFûuë)”·FvF?H¼G~ ê4¨³ÍEˆ]ÖDÚèªo>݇[®Æ0ë°<œŒ¦x¥í%$·NÄ’ÖÝÔgìU|ÕñЉ>A´¼×Ó­Ú•h«gC‹å"1Ñp ·zÇ=ÐÙek·ç€»C-qe³hÁ¥MÔÌò‡önFÑi² n¬ÈîËO!¹ÎŸy‹†|hFíT5ô¢?ããhã܃œnõkvÍ_‚åšQ‹QöRD›Lsáoíi€3=頻q'µ“Žý´œž=ÿqäBØlTw–ç"f§`§·#x¬Ë%öÀ «¿<¹±¨·ô×åSF¹*\͹Aò¼B”ª¤¨ÒÈÊ|rד]Y˜Äé …¨È“PghÁ „⋹E U)ˆk¤üàEPßW¸ždkªŠ.Œ?.KžŸBñ­)h¥yãªK8ý¯¼}©ç@Ej1ÌÙç!GsP÷Ù Òà˜‡Ÿ´å =é"j­T‡,ß<îaíh»• O™±ï |î‚íz<ã…÷´æ³¼—Ü1ÅÿbÃzõ42þö:µÇÔ¯„ cˆÜ«GÈÆˆÂP¢6¼z• åZß9§×3`U£ïAŒ.°Ê´1[«Ëf]A=„{ujìD•3–¸j1¯â½´&e1÷¦c+†óqséi"¾ªEèÓ)J«éNÛ>ÞŽÁj”pQaÁK@)å÷Üð r#aP·ëú•ò*”o.ÁÒ“ñë]E6ïäpÏWdO:§RïÌZ:  $1n}¬[™ Ê.ûHÎ’±tŸ(VgÀÒÕgÙ5^$í{ÊÇÉo.CaêêȱŒÍ3™ŸÅ¼Ø¾­T¢éþd*uŠŒO~F”7&ÀÂ1¢ì‡²¯¹è-\¹ŽV” ¡uuN|2 þ{žuVÃ,ºôX 7KTŸm7©é–¬­#Xj°#A*uBˆÞ­˜ËV6é±9i‰ìH»/} &ÉæÝªáØpòî– §–š†ç®È1ÑCNÄÑz+iÙƒÇíQë(ïÆ†õ®ly§:»Xp»ô[Ÿ¾Ú]Í~ ñõåÜl =ûáñ¡3lÏ{ œQ¼œ ^6eå³ù;KqÜ:s¯MÞûÿ>×¢ºµæljR2îY7‘98…z˜Ú›‡w@ûŽÄ8Ÿ÷ÓJ,/§^|™Ë$CO¶) gK1-ôX@ŒA£ ŧÑÓP×`/Ï¥KŒ­oõÁÊÒ4~”*Æ^NO-Â'®ó¡ÚVçOpçÆ\öG09=\ÇQo= ´WUï²Xfòô N\ÉnD ÐǗƱýwiFášâ; ÙµC•~ñ*]ˆ¯¦¹Ø»>_ÆŠKJ°‡ÊÒeî{hµú5P;íÊæFƒ†g·k36„óõîÿÁ˜iWq‹ÕLS¢O9Ð¥Ñ*̼1.·Sg?-‹ØCœÈ>(_¥¿È5îuf·#ÂK›ÑùM*g’¥Î&OD¯!váæ#üÞlAü>Žc…‚}\°t,šÃ\wk°?ÇŸ€XÍÎ釱âäØýœÛXï%@rsד"írT-…ÇøÞf7Á¼½ŸÜ‘ÏÁ¾u| Öf×76‘vÕûxG^ƒÙ7î`=JÊà{¶&ÄEAk¾"î¼ýƒü:ïOïoÍ@…Ãâ´RE’]iý‡õáH.—As@þŠ0ágOcgõW@éô:tð¼êùF°±ÝWù\‚ªAÍŒ™ÍWaØùm.1s®`ŸF«þêë°½Ö•¸ÜaC˜þàÆðJ&܇ fÁxý®¥ðóà_žê‚tݨ¬w÷Î?¼á"ó?âÝïnà}gÔ¤BþÏgÜ%‰I4'¦šèø¸ƒ¤n2üÜG ù+Å»aÅWüŒÚ¡¹dJæ=[þ,;áºÏœºg)ůϴÿÉQ‰×k`ò²¶fbÌx×,-ÚÈ-´¤5+†aÕnI²¾ KÒbð²ó䕆)[Ú†{–bîW\?ÑÛœòÑîÏ Ä=_ÇûØ´•%Ž‹³· õô9X PdÖÂå+ëaÜ}?£# Âlõgm€¥cJP/4øÛؓդ³e,ÝWme—dAÎ{7–Ý— ~Ž£zN7”œ¶„¯LòU03 Ž·hþDŠÚ y¦—¯óâ-îÀ7<3é,sjn‚­ºÚ˜¸ò$¨¹ f>> SWHÑ®·çq;µ`ÔÉ‹E[.Åü÷«·—à"Ï¥,xê/n“^2=–á‹;/`½º2""ƒ”%ÝCûÀú0V7‘‡]ô8á:e­äàèI3Ö×zé¬`…ùú(Òs•k™t³îõñO>‰¹æˆ¶‹´Ùåøf¸]pGÎí1]yœ¿ »·]ÇóG Y ƉDà·ÇôÕò}ühcs0.íÁÞ‚¨±$ƒ+é¬!ú"ÓiV.ùñNÊ´a­öÔÈ[ˆäŸBÃOz°À8†¹ywÀq?>·~ý =^ &«ršíÿÀQÕ’NK¹JÚ¹ê×6¬.ç>”+Ä™…ÐyãèÀ ²¿¨&Ï?ŒÓŽmÂU§¾Boœ*v^'6Åå_pçØNÛ*gz< —:˜‰²]8ðö5|“s_Ö½ èá6ì’¢–cøö«~¢{@gÙœ ŽÆ²ü’§tÁðœ=á¹Ùxw_1—lÙÂÿ=FÕ,/ñ8§–ø™3MøÍWY5‡^ÓT½æ~å/kW!‹‰qÿ8öwÉØyNžj¬9ïÖP×›[¨Þ¬*üº2¾ƒîù,øDuéBÏ Ñ86…L‹S.Þ!Wü‡–ìÄ.g:öÆ\xò!p‹CX‰÷ < (ÀwÁ•w«áhˆV8×£îï«ÄâÚv4_ŸÙ_Ê…j“3£à#ñ£Å5x¡ênUDÝm®«ê–ÖDžÍ[ÊûxFŸý*1eùƒ2ls _Gæº Nsÿðçü÷à»ü#^~˜"«gò„>MBsA6±v,ZH²ë¡¥;„/ç2'²†‘§\ÝË™th© ÞQŒß/=“±‹XFV"Þ à֨ȡôÎí­e ·¢ÛáßP&jùO%ecnƒJM1Ú¶ìâ)Í8Ç^}WeR?R˜è—ïœËt¶mõzÈY‰ý[¤iI4àŒÊ*ò[õ;ùua2ÕÙ[Iæ›Uãú#x£µ _?Œ†VL`' _BÙϯPèíÉ7î´eRÏ>ÁtÛ¨3lF+wPÒ¯-Ì_·bîX·Ä}êÙŸæ01'2LðúJØô(™uòÀ=35À°}ôpÚlP¡Œ|¤ê—Þâ}è«/©‰£4§i6—Nîa æßÕ@j lbÁÆîç65cËÈÞ]qQ{ÖH3ck)~®Â)œ;™ž<¿‘$>Pµ¦«ÐâÆ•4¨b¯d Í´K–¢Oç®}F÷ÌÜGî@bb s0ïX¶ÿ®ÝÚÆ${þ¡»ˆÄFiÑÏc´¿ü6>¬öeãayþˆlšlxßn5¥SÚ¡À¹™Zðî¼;HC#ðO˜/gá¶ÞÜ=)¬51÷›°F¡`*ú#›z´‹Ðn‡ùL¹ä/_Pù"§¾HþÈÔ¿3*¿R¸y¯Â¾ñªôpb&y±l7Ìþ~ §\Y‚i#æ\ÈèÞ¿yT–­È’¥ËçÅó¿IeR›¹Œ9§±^-cf¬ÃŸº70+D®_õÅôò3¸ùItJ}„šæËÐ8W µŠÚq|¦.+:‹9?ɲZÞ²nêh‰Wúg é“dnÂU5fß7Ú¸úoð^þ™ƒ¥Û`@®eCÛqÀY•VK•ùUŸñ•æWòM¥ –7 Þ1 Vd7®1oÄ%{WÃöŒåæãØ"ì¢a;çÛŒ†`§þœ>2ýºp»iËÉñg?âf³j tù£¯Ãä@©å /gS‡ÑeÕ)˜nQEÖiDÀ:‚È+9Ãmx'Dõt©t@ 欫~^™¥‚*ô€àDÚ¤kñwEyf3vl9c‚ß‹ ˜«„-y8t׌U7£Öš*ùRŽ?.¯€Iìý‚V¼M3©èíAâ²ÌŒíàÛâGü±û°X¯žÛßÀY<þ„BOVÃÅ!Œy®H/LaãšÆ²’¼ÔOwC«§c1ªo'yÙjGϸ:±Q{jõ4~䇀—¿%ûåk kò¡þä4yx ózvÂE‘+dX* ¿?xD:ŒKqé‚v.fY<ˆ?ôÁ„„V[µ{ ._°üz³«žƒd°‘Ý6]µ»y†8g8àŸ]n`89ŽÛf­ƒÌr.ݡЂ¶×îpóºÉ÷*m»6×¶EÂD•ræ^œL?Ü,`ÆÏß’V}òêafß—å<"¯áúVí–‡M3¯Ñ\–ên‰Üy¿•O¢÷4JÉü:&¦&Eøê–s]¿ƒzÆN‚Åìxô{6rè ‡w0UOYßáÌú/w’€ñá,B-rZ㸔òuÄPÊ‚6Ëx󣺺áüá4æ|ú<;‘oK'/L­+X"9Ä^…S+öƒÕÇB ïÙGc_ÊÚu†7⦮­£w2³¼_œx&¡Y9]ýI¢^@pª³k$sê öwÀ1GŸó”øÛ»ò1Ù+ ƺWeužê´&Üa›ôŸç˜'ƒ1h¥T/jÏŸN%¾.Ä`Ñi£¯waéëG¨‘žsÂðÈøRt,û [ÒPÊ(Ç_¾C·®¡ÞŠZüÜ”T­ c ª-gÛäæ³ŒÞÙ(PK?æû±+•bõ$x¸múÎWïV£ðÃ'¶Ô·›.T©) 7o®g<ÿ1œnq¿_ _ÜäØÛ£’T¶ÃÆ<‹{ã në9+jpïzœíÂSéðÆ‰¸æã3\ò•(çôq»…^¡ÔôPˆ‹ÀÖ 1¬q¦œÚ'Hõ—òá?ï±qÔ̸5 Dfi2Û1%äe½’ AT N€­{!€'Cµy9D\<Ïþ6l‡t7Y–6`ÌÔ›Yôü*x´Y‹>ï0§yÕ¸ar"ìáÑf]Vd¨Ã,›Ui`š,~½ƒ?%% SZ·Óù ‘LlûT°Ï„EYôÒ‚tZ¦­FW¾¼‚÷›¥Ðã~å]î„Éž%¼?òI¿áGÞ­5QpKê\”Ï­Ž©dË1'²Âø#šlÇSvrô{t&Fî›~ûî¸ ¬ÏÄ]Ö¡NÇÖÆkçÔx‚?×ý†Š‚HúìÞ\pÏ žG(Q‘›-hmÂTK^¢¡Ž1ž2ü°:‰L¿3>ÿMµb'ëÒÑ{âyܦŠòrça¬bíÍý|Hl<ŠÆc7’žÓøàF†T¨Ãh."Ÿ1&ËÞä¼¹¬¨%ßË>5ÁA¶?* tÉÑ‘´W ^PW¿¡…Eö^V9ññøÉÉtiü'›’Žæ³ˆêÉd0G’^ÎZB맆3Az~í{h)[IEßÝà|çùRwÕã8³Wž>6+ÃqḄÀp•ÓdVʼnàrü!èž<ÎÉØf`ÔÉG â;— _T 5»„èûžtl`Ø›±&›eÕ•ƒI^pOš*5ñ¤èïξ]ü‰2õÐàùOxõ¼’jÖWcº&…·²yDÏÑ•]ŽbuŠ 9¢ÆÎñ`ÅŠyGBÁ„ba:sƒ Ûþ[‰âÝ“8ßá2ž\?ŸmznB³Y0{ß|œ¼œ@ÒùgààŠ'?¦Ò xˆž¾Û85Å+¸,<WÿL ~û|¡Â`:ný¥Á>?ø‚S7§áß˲Ì.6•íœHYÜ7!Ú[q L~_§;üŽã‘kh‡{á–ÄlÎÌcüÙ¹æÊ]tàe7û8¶Ÿj©~Ëéó×¶pW2˜á6çR-à³zpPºÎ™…<¦íknÒãíä|’ ¯:J/Ú±¹ŠìÇÁ,š°® ç„ïÀŽâ¹´ÇÓ=®™RghÊøJ欻¹K®­ ÞUÑ«­fÔ²;Avõ’ûáµþ><±åS|5—ÖeÙcÑîÏ\öì+¸h3v0bo5/2‹_¾tÜ›[œ¨k^›}„)²9Týö |¥äÌÕ4f¯M4˜íÚ˰f°^f·‘uW!Ûÿ\»ˆAWð–¥(¤†+ÁÕN ÖØ3?}}ðnyMMØJéj$ÉØ™MoýÞM{:ÔiAôøµà oÎküØ»0ܵô ·Fä6dFã‹åm ó=žXO?-¡»%gqÝe]úcãì¹â\[–dæŠf|„ë}ÉÁË ‘ó”ÿØ‘†M=Äì¿´‡|gCØy\‘ÍíÅ¥x¬%Vœ¥åM²ñ”Smö¦SƒPmµ4m;»„èZ±q56«= –ÙèÃWÏxn^ð=Õð ¬ IõÁù4)E—6F˜±±“z°aÞ}¾âÞ›˜ÃâO)REëH¸×ä‰/î8Ò«[?£IÄvdC-Ê o¦Ù¥ùÔðE*È ‹ÐÀ»1<•ß·±nŸí¾ÍÎß–„È&]ú²XÅË93¡Ã!Îq/U+;Ä-5§Æs·aíŒ ´§ê—¸ÍÖæ&RÓy„¦ljÅÜZlM©˜§ÜF-³L8—¬f›Þ\ƒÃöœõpZ꟢âÂCu?¬ãAÚu˜“jœGþž«¥{W7ÂÓƒ)Ì%'š5Ò4îáÛ$PU˜Låü``2Þq»L3òÒ%³ÿaæÉ%Ì¢ñ¨xç±q?L¨¹f¿ý68dqö¦+°m»/ñ<]©ÅÚfy :åî¢å›Êl-É%ŒqÆ2üØø"qö;Ò …Ú¶³ÆÕãÙú3 ùœ ]¨/N U§P;‘·Ü–1~41Úƒ<+Ät%419Líî`Ûïfx\Z‹Ú“>CíZ%j%®ÈÎŽˆÑƒL"j)HE? Ññ4¿ÞÝ tA§½NoäÂÒô©süØ~5’f‡³Jï081¿†d»Ý$½ë^qÏ|!Ë~ÅâoðÌë"î´¤’rPdy\H:™ÃßH¸(‡×§ö€¹âurÄ:’´Æ£A„ :>/Jç’Ég>BÑAY²zi(›VƒaÓqÔô¥žçvŒ$'².¢íNÞ³n pŠªÁÉ[¢îËgpfüX.¡X§>¯ÇFô ç ö¦ÿÖ‹îžíÌKx}üÊàFÐÙ Â?÷9$îýE]­\¾›ÅøaØh³  ½ü_ÂK±_ ¸LŽÊ ¬€Ÿ¾D>׋SpÌeeË„Ø4L¸ÙÁïŸÄªoåÊ)!š•ä@ÙÓóxÙy )>‰fÕ¦8ß_˜•ÛE{ï­C7¹@#+¢Wö å:¶péR[ ]ê3|8ÚÂ{bYŽƒ~¦´b¿;nEÒãÉ⃶̸÷%IÐQ'kô…YŽÀBª=7ê3°çn.ûÖ»Vü@‘ÍwF{Þ{øÔðŒ;žŸ„çuâîÜ}l¨%GVÞÁY!BtÇ •«„ÃC™´.ƒ·®Yžâ·8¼p2…êóäÙ•0TÐ $ÛÊv²A«2xt˜KþX#™«pÏ΄i³X_+6Òý³NÀ7?Ï“¡4'\Á¾cº·½Ôx& Ó¬È>Ôo»Dfr*±õxñsxò¯Çö@çR5vhòÌg‡Öäàì)â\Ýï<ÌS=8wù•Á0Oú¤™ý6rÉÐ ¥FÓXYE#ˆF ÃÑJ)ØÿÅÎ_pűҽ(ì/Ž_fŒ¡{òÒ1pe>÷íÆ1ÜRöb7q»d4HµÁÈ“G°o»&}~G„F¦Ìb†I°bš-Ë8R¡ë(,ø)O/€{µkèÆcÙPõòi)zÇØ Íü„'@¡¤dÇ0v.ž×f,å2¿— ’­Ó݈À¶f ý BEõ ¹oÑÓQûäXr:UÃiìÄÐU_°bŸ}äÛb§²±®Å‘ø)BëÀnZ ¨þžA3C•馨Å-:2òÔnš!¾ÍÞ–TAð³ì**GK0\•ÈyûàHQŽ6g–éï1uÉ1äyLÿŒñnPµÍ„¼MªŸ›gDþý~vÏ£˜Üü8“¹‰ VN­££ÅÔ¿‚–2à¸9:s½8}{Wf}.á¢×üâú!­a¾s¿‰.¤Vô”Œ¡_L­àç›*pþËŸtD—~q¿ÌM3¬€Ï¢h’G4Š<ëG%çdXêb"©Ê8!ú­ß’Ò% TÛJ“”ú„cE Èf¦ä,Ç.‘¤b=ÜmaꦩÎÚîvAbál\+sÊÆ½á]Šõ†úàXU '^  Ïáñèi5Žš§L  _{¡ñi.v­aËÃ}1"w†wÖág£òÁj/·<¼ƒ›¤7ˆ‘.t¶¸>ø<À)œ‚ë.ŒÛ»y<Ýѽ†×ì‚/…{0±¸ —Õßçm˜Awöì¡+$/‚zÏ(WÒóçp¸¥†Æ‚ùrê{ ÑI &e™Ò6î+<|!‚#¦o±ïÏ<–¸ö&ú($ðZ=ùxr‘Š{¥ÃÜoCœA€ëÞÔˆ:ÖAÝ©ÔÖÔ¤ OÓH~Ÿ64DßÁ²ò/\ÓÝûÐ?]šÆä °÷šÜÝø’Cì±dK¼7‚UeéB>ÛNk×DÜsOO /ÂÓb:´ÔgÎÍ…ØF!Þ½Ä X}Ê„•]KÅ FÐ+<šK^q`ÝË ‰"pÿ "µm'©b¨¹rõhîr¨Ê…M™cÁY¾G—G°~|‘z=Ì܉éÁ^Y–ä̸Š"Æ~\Šéà6ýÀ.løðŽÁgÞ&O3»c@B× B¢œÙÝ_ÓqÖCX-¸>½Î «Æ ãpŸ$',¦A½g|ã´óÎaÊtblCÛ iZH$öžÁI»¥ÙuµK08ÇüçÒukÞ%«2~Ú5EŒÚ—…ÿu#O²—¼¨p€Èæ0ãi³C#Ã_r»&C ½AîmÑa/^Ø¿ú,Žs//5ÖhV?VæÁ= m6aþ4ºÜÞ ^ÏÂÐøÜ×d šûôYáx„véªÌóv¨$á!Q!æ¶T›-Ú—Éu‹qNË«ù¶¼éÈ+6¤ÕãÓÀ.’Çò´%¨Ãej5u(jÃg“«`\ùQ~ðï£ì‚Áq(÷:Ž-ïb fk Ñ})B§…|Gr@¶(Ý¿qœ¿EnåHìÏodVo[óo!µ[Ó„‘£˜r=¾’ÛnZ„î–lëª!²ÈVŠ'[Ò¯|øÉ=ÃYµsèŒÃsLuàNçà§ >†X„m¡ì9ø|Й\Þïé/cÿawLnÙþšiÜÐ[#ªî¹‘ªM•fOv5cîÉ=¬Û&œ6ÀÞN¨è?ˆ¯"ãý¥—!.~ ×¢¾œŽ½5 ‰Ì\Ô"—R£pÉ.f?ÃÆä&c³â È Fr­T†½¾UÎM‹æóý殇÷S&Âdû¯\e/'ö¤‰¿ÿæ+Üáµ”Sšq–F¨ÓšÞÜ‚./ê&ˆßÓf³šŠÉ4d½ØÊÑK‡¡÷€0W{_ì¸ÂñTîáDÒþ(…‘æßÀ¸+.Tç¼.Å= çϙӱvVìKh¯éÁ7®þC/1ƒö‰ë³Ýu ðŠÄ{ò}i!Ø\R£ÙE^ð°ÕŸ„,e\—tV$fÆúˆï);Üß{›‹˜qˆkÙù"•™”ði°7÷㜃pù²rÎ׿Rg!t¬\z$G¯%´£ªÖt¾?‰f_Þ‰k%Zy{6—BŽú%âu5÷9ÅP3áO ÔfË®ÍÌ!ëÆÕsnñõ\úˆ1uˆ …¢«‡x»6Uh-å,Ö²íkiÐx¶KYˆ´RknÅA_y|ß°(‰h3¥Æüðâ(„vÆ@—s „ŸÕÔ8Z›%|q¿ÀŽ$r+‹ƒày¦"~ŒË€Y;ÌX &»—¶Ÿ'‹hÅ©²ÂvüU ‚QãÈ8?›â“À@è’ÛRìŠÐ=´øÿlár˜ýd+E›SžqS´µ¡X¼¾K(1ëÙóèô7\ù)^Œà|vñ¹t!秉R•æâóAˆô|>‚‹Mg”h±ú|²k ¸ÕœÄLÝCö×GñHTû WúTXQº#|ltd2FôÈ'Ýð¿<< ?Y¶ð™{*)rNýëÑ ã"ùÂSè©põý„í+¦Ó/ʦp¦ð¶Ljæ¹>kÂI}žØû4Â?Â:ÇÃ8’~ïÞ}ÌÅ ·Áçý;nÇ¿KÖßÍãsټŒ t¹ÅÕ¬ÝÆój°±ý>Vþ›ÂÎÆšâb»î†Å.$U•zîø‰-‚¹¿;/ãž·ÇqXñ"®Ón4Ù4žìôÿÂÕË@@ˆ(ýù# Û˜ÓÈ€1 žž†§> mó* lóynÍß?üöÕn¬öâxw¥Š7hBÙ•G/!vï Þƒñݰi­ÜüÉ5ÄÆùOn“šÂ0Ïl7³X ÚƒYØiJÓŸãÞ),ÆÎ îUav m@í1ÃJ¯YœFDèΘÀ‚C !kjRÑ^¢¸›”)í°Ía&[Þ„)Ë ]D±ºt#«qÂÏu”³¨ìÀ*´ûr?éãhK`¥Ü+®G£fÚÒîa4Þ“ òØóW§q¿+ú-+…»rY¨š 7®& ëMp³L­N¬Gkþ :ŽøóDŸ 1Gùߤ,¾^ß߀ɻ aÕ^æ1³‘œè>ˆ{ºuYÔâ Ôê\d1%ºer',—F¸®q‡›ì'@ %ã$éP%UL.E^C_óudx¡…íÎ¢Õøož:þz¿—|ܽޑœü2QS™ ?wh3‰#‚Pû.…" w[=܈àô³hEÔüõH îE£a¼ ç(u,þI›.ºÛa"™˜'Hƒ×]½ðsdùžZb>í žÚHp0½šü=‡…K|YÚˆF·³Àoêí3'…Ÿ†ØÇ!8föO8xÕNô-À}j]P>hÏ|˜@þmÚ pwð92ôo çá/Æ|ŽaºÓXÜð§•Ý"÷õûÁw3“Ú+ƒC¡“€|s`¹ÖƒDð]º54r.i‰Üæ°wxóšDqÍòoࡾ%Ü«ˆ„¼Y’Ü&—:¸>…ÝybÃÕ‘-˜AÃC¤XÙ]Ö¸Â']•.ªæÈã‘°DZq‚ûÝT GD6À•­?É—…ÞìÜ%ÓX;&æ¨;jl[•%çúÖWL¢vom¹œøgÜpV¬’¡3¥ ¨å̳œƒæjØ•}^Mqk‡ð¸ª*{|¡Œgðo<”ÞËE¹þ›`e©ÃöEòqk]ŠoùÀ]*ÙŒ_VesÙ§&¡ù°6¾±’›ýq wÓ@®ÝŸSòÓç*¶LfúEÃuÕûMpl½4­yý J{?ãB;YZr­‹¿}›{´Ø/ó¿ñ/L`9º¥ˆºOÀvÏUŒ{$—WYÑô3šñâ+xò,Yü,V;ç>˼ «Æ­cNNV´ï—&ïÍÁ\s†õlù œ¶Â¯Æy¬}Ü8ÌÔ;ÄLdôáß-Ê˶2èt<ñÛã·äî©]täâ¤eâœÆRmðkTg@ô¨•B~~›oþæò*ÑF'XÚÓHzÄpšþG¶Ï‚÷,ßËüᎻë³)uoê~‹yR[*ÆÌvà¡ÌU°þ͵ÿ×|ÎÙ€o±ðo¥ÒK¼Z|[¼Zš½냋g¦‘óE¹°cG'̺^ÊäÑ[݉Ídcvµx6&-N…ËW³à¬ùWî§é&uá¿wÑcøš{nd [´–ùe,‡òƒ¡øV,‘Éþ“…Ü1÷À`q×*wÊd‹ðÚ¡(V!R‚1-¤9ÿ9.ׯï×îy³8¨3žÃRÔ?®â=o(IrÔ!ª4„u6cYòîÓ¯‡Ø·µ¦™ “ ×PÉþFä|µu¨4‡YP të¹Õ @z›¹ÂÞXŒ £X4Kƒy\å&ÍáÂÕ?ó%ïmÀ8-]®¿ç9iXL• iM‘Q묅IcÆ2ãgž8xÖTÎí ßÄ ìÔa’2O•I|;À¥üÍÃ4|âˆÍ(3­›Htp±9=¸êÎrNB¿ ¢¶Ë°m¿‹`§ÄTøvõ\¹‰x½s8Ü ÃÓÎkXâ©xŒ‰ÞBB7IÓV9š«kB1Iø¥X„–es¡a%ÜéuötHFn¶Fñ¿ÊG‚fú.+ë ÝÂóñâîs$õ^›žý—¸U]äì Hã(šx$(…cýžGà­Þ"«*`~­íLÃŽcËáU],ìînõýb0¯1v›…)1ÁÌé3k©îàŠN)ÑDUY6¡n!¦žÖo½(™Ä“Š9GÒ;ïðfüÎáúnØÐ©ŽÛîJ²Ë3ÇS›¡ç qЊþòzU‡Í™ÞGs6ûy4íp¢Ý—†Ñ€õcNÐ \\•Cۖעܙ™ì¬Q-/ôÈ®éH) …}„Ø£1hgæÅkœÀLƒÂ™k‚'„ÍT§¶K«¸‘1?Ñ~k Tú„¥›bh9t²ƒ$°š§ ólÈz—. ·;©r¸3=á¨Ë‚UlaéWq&|+©¤½â&“³Tkd+œKøåå»ét8~«³¾íÏ^â?ˆã¶i…çïßãÙˆS°¢DmÉÁûwµñÀ!›o~àž¾ Àé&§à\ì<êñ%ŽÇ&·õÁ)bÁä,©‰c&L{jÂbâ‚èÖkâLCKÜÎÖOÕ<éF=wØ "´}µ(ÍÞÖÍMý|ˆ>œaD#Íéë—Št<1ƒï÷³ñч øöè'´U¢ÛÏé±+ZŒ“ÙZ?<#vzŸ±ýúj¦jçD“ë°-YYüùÞÊì5½5Ð^…’õ‚ôXš\½¹~ |ƒëq4cFÎÝwˆ¤mÉ‚ëÇžp­§1©~€”ì“¢3 óm‡¥¬a›³üðíEúN]*~!zÿ„I;°r—kÐÇ52ª,ýµ–Itaôv3é’É’oÐþúI|ùã0×4s+·ÿ"K=ŸqòþLèÛTÚ¦gLo(ýÁåùa(è}Š›á= ÛëmëU6Ð’i:lƘ^p÷Õ$»öÏä’g_Bku5®s¬/î·+\©œ‚uµ„/z†“_rÿ¸4¤ÿ —5î s’k *õÕA2¿¡Õ¢1…³nË2äõ‘,ß*Òª%ÄU?¢?NÆ‘ç{ÆÑ.™±ÔHu/¦-/€¦Ž+`õò†md+”yÿF‹å¸qÁê×ztú_B¶›<½þü4™ðÌW,áäNVã½»poÙ&br®•»{ö¾ó„¿!ÒÐyqæ,œÆºÚâ°J# ÎNÏw2£õËOU‰9»†ÿw.náá¬Ùc°àÜWð¸8'MÓIaé ’úÑš…´ïE“ÙOáxÑòÈï·%ëÝiÚYøÆ!>|ûAÞîêÁ•¿ÑÉ€°½zÐ—ÝÆ#_#é¶Âô¬T§wýM˜ûJöVášžÔ|ù]4ÃËøNVš®v‡¥‰zhc(¢Éßñ”ì=ÜÂÙut‚qÕ:pY“íHj·™±þ Ämãr”ú!ô"eK‹ÿ‹¶TfgM£ëó[`›Šszj «˱+ã¶a[£-~ÅK½«KO¼ºÉ¿Ztºæ'á.‡;<ÂË#w]Ã0Y f3mtæFÂPú=È:¥Á:èeP R+œ(…ÖsÒÌ;JŒiZ²¾­‰'b=HÍ¢I¬NÞüµPž_ ùUd÷3Ê|à#Ñ(‘d>³ââÝ™ô-9je]Ê¥¶¡£ÝQsŽ~ËC§ßûèÜÇSàÄ`Ž9¸ ‰Óµ_ÙßK†ll†ËÍŸûVæQiÙë¤C&šsm»‚ùE]<+%©X„ÍûVÀ†Å³HÒ.'0›èï÷¢\z¨n[Mó½Š8‘¾ox´-GsÚŒ%¾Z‰ÂúŽÌvô;¿ê œêÜkÏps^‰ÞÞôaB¦›– ×yàS2úÞxÇOÚÕÂ=5¢²ÿý?Í_eT¿>†ýùÝ…Ë,MÙ›û¹kUz ûï«Ê÷FÍ9%PÒ­¦ôw_)qÜã‚b¥´@¶ †J±Ôh#îûîÏ.×aÆbSæý6X]f*# à2¡7vL"¥qB´­0ê,hÊ8[Z•)ÍB£ì©c³9ü7ÁK´iÓÃèQMá§~)à«cÆ4f‹ÈìêÅK>Jððn1K€×5 8·9 ã´_“Ô‘Øa{›v­—®&‡˜Êq¶«t™ÑT>Ž (ÛP,²úþh3³›ýðq¢ =77Å—¡Àá©Ü’·ðö¿¼=¥s<öÐÇê‘äfÇWž’tÿЖR"^ ÆM ÜÍý÷È;;»lüö¡8÷FJ¬ê€é‰˜*pž[U£ÀnÄtAªßýºI3 À›Oh• O¢™üëH¼o¦*~`7<‡.Ëׅ㳸;øíê€nI^̼4þSZ¾gkH±å{zò½N6kÞC"27HÁ¬n%¶CZœ.r©Ãþƒ”-Ÿ“^Úe‚Ɇõk•ÿ>LÑ£ï;ç0õùr´÷çMðÏX‹g2‹Àã¾Þ¸¨E;Vìgg¾u»7ÉðáÀ \«Äüæ¿ã—kEëºÈ¯ôgûªü˜þný¼DÅ·PßÉDýò<Ö}¸µÈ>pžE‡½62û+5¨bìC§^kdEãx kØG'c–}ý¨îØËòÝ/p²C5¤dC.ÓZ'‰!+9}Nœ–t\Ã)}~äö‹Çøt\œè+ã ËõšHî¡õ¸UèÛ‰iôN#£‡ÆÌvœ õaÅødËR¬¹‹[?êÂý`*wJŒÙ¸D¤l1~A\ü4|Ë£}¤ðé9|ö×&"Ì…Þ]׸â:]\’8—ڈп߃ElÙ.á`œÕeˆ‰Š¹°@v2=ÐÒG†V¡¢˜=[6ÊÝWôÔ%v[Ñ== ðÅ’yní$’qz´1@vþ+ÀgáÌôu%~J°Ð6Ž?w’\]®.¾¦Ä ®ÂÖ¿—pH~€[=Ó”}•ŸW{;Àœi´®¢W¥áGr”ðXFŠØïêF£fCnpéDì<\†F7ñâ5(bþ(%aCwk¾ÁQ°äª%ž@vé[àÕ‹ŽÖ÷˜»t‚È5ê±Léßz§Ïæ2üàæ×M5YÊîlég`ªŠwaCb>[×&C}o¬…Çzª4Iæ­)‡¾Lúî=ЪL§Ëo«cYä3x×Í]x© ãC*hIÒîÁH´ÿÜm×Qý9¦×uâ±…ÂôÇcwò¦ÿЦÙìœã]Îï¤ÝôX•QŽd¤Ö­9p”ˆ± Ã ˜çxVœcá{Å wÞk´ª*Çwz`9†ÇVÉBaÜ-þ‘ÚqP)—™–̼äêIvÿ£¼î;DsjÉ_ y¶zS'×?…A/º@ËÀߌU€3·£ì¶yT"¨œ+híÏu~$>ú/çð]6Ka«ä"ð~ð5îåc÷yk",•‡ÛO¬««a~ÙÍrì1‹­“Þ5•Wyyu¿ÚÄíÒúz¡åB6 n³€™zÖ‹+¦üÂ6TÕz?œ‰†¹E›p÷cS0Þ8 Z]7à¸Üf¸§¬‹÷;m%õOÂÌY0›)bšÒ0LIJ€y3kÑt*ß·Û‰“çMd›9±ê°p\³GƒÆ¥ïÄ„êÛ8fÃw<b€–ÈS•wðÙYijÿpý$®«Ž/e+’_qaÈÙ×>ÇÏUœä¨–®?º‚Å'8°{–Óž¿ºØ=vÙµ”ЍDq¯ÿbçòg<·ÌMtÕ>yvb]-ꨱæ“ñð=Ž¿ñv$=b®Ì¹µ”áÛ…7àhxó6¼Î¾¼*’—™ðfsÚþÆ‘or`ën£o]Ö‘ã{ \&t´ÀÎO­[éãÀ®GŸFË×wqÁàuÌ8œÆ+SÑÇ–ô¬ÿtºì];·>3ªJx Ž¡t—Ù²òâ 0ãí66u‘nvj`'.k‹_»©ÁÌGtaó!Œžþëøù„<)z„Kz·Œž¹—ð   ¬.߇ôw„Nn„?g†8ÓR)xs¯‡—Ù“æó Ïõ³¦«ì%hë鬫YÙö¿Ú´Èó$úI˜³Ú†X6ßv2NÝ:ŸÝ ¸É„b è¼2^À3\7éãQéwMP~pãÚZy³Ç+/AíQ Ú=²"¬±7ƒS·ÊæÕÜS`S'§ñøßAdÒ!t­|€4•-ÿù¤ÀLŸx®nõh¯t8‡Sõ*¹ÛžÂ6±¥´×Cœö_€×‚Ð÷ú*¬ÁÕ` ÌùGKŸÃÖôõÌíEDºjàïsË™Àj|YÄ?Q‘BÃ~)ãëÛŸp0ëþ¾?D¼¤9?˜ž›.üR.kßW¼-âŠdá+¸`Ñ1Øõõúø€#{àõÌ0ªæ6OI£'—á·Ùø,ÝZ“äñÈ{{4^.Aÿ˜‘®±*lqÌqnh]g‚Ì;\–èGïØÒ¿ÛF Ê —į>Ábù‘,ãJ!Ùz =—»©é#¿3†»æ&Ì´Š¿pMÖ”‰t¯†}¯Õˆ@“ ë”)gîiÓ—§‚Øèg|*vOý,§´è=ììKgÕŽêí{Òô‡ÉyÞµŸœ|_ýyS†™*tbxÃ1Ι7‡!·‚·Å+=È'sÍ)—žËmNˆ Ÿû§‚I0Q¥Ã/–Ó[ûÄè¯C)|ußröfv X»Šƒ´…)=liþ¿é¦¨¥¶ží?‹êÙY78YpŽwÿçºÏ™j7'0ñP7.=ø*Oq\·ÎOÜÖZMh²¶“kÓ›ÊÚÊ„ñaýCN±[?Ÿ˜ÉÈѰ]6Ìj´gemг.žƒè1¦oÝÅÛÉÉ¡ÜWW|9ƒÄÑ©Rp³¿‚;ö+ÿ²ú3xÐ€íŽæTbó7Þy‚C1ðõÕÛ¡>H¾¹3‰ÅÚ«§Ô飧.\Gëª6'&tåÃØw‚hù†ÛoSW{ÃçùžxÓ`:Ý‹¦¶áÖá4fäÊc{ó7Ð0“‡pKµ‘·kî)Trf¿©+_îÂÞLRcן=å:?Nµ!Ì´–Ðí7TIhàEÈl¸×_§‘ ÇA_šÙ4ï"K½†¡@fÃÇSß^ôåøWBóÈdßK8X×Õ×W´šÌaúAø.éNãâìi\j7.*“ AcŽƒÕÀ!z’\€±µÉŽ(CüV‰#›nCQÞ[4#Ç„÷;âÅEµümÏ'#Y?7Õܠ….ûñ‰8DÎ £3ŸÉÒ™gDÑZi›e*G1‚"ï¼ ý2§ù:ædÚ–uð8àïlq •¥lùº@Ö–ìÅ|Àº^,¤õŸàq§·¸Ç6‹=M+߃(3Ë•&Æúc[½ê>;ª›g²ü+'˜M¥0u¸M´w¯Ãgw¡ñj!Ôx°/Ô~íI5ÓPA"¼ël¨ºµ ŽûŠpvãÜönà,õepö§)躰˜ä.[¿f'`Æåµ`_¤OCºQ&z­ãy„‡¥ûø¨tJ“]/Ù=#+àôP3<«·†3!\” Ñ‹bø²«‚«u1eS7¨C9Ds߆£à¡ùkü’xžü¬“…ˆø#(;ÿ;”—ß«Gñ‰$‡%Ǹu³Æ`“Ùb,h‡öÔã$ü¶µUÐb!S-\ßoboòG¹®¿6 Of7\6ÁWp€œ¯}u+{ƒ¡7ÿ>·[9zâ®CûiUl©´b,2J÷™@—Ÿ&¸“ÂËsïsvŸÄòuŸðö¡œ²žƒzµ'dã¥&²¬ug6#é †Õ¯IUhW¶U]^Â&7POÇ÷œÝ¿Û¸ÔÛ QºúøcM`Žó=ÙïGüñuEè}$~Ê8Àõ[AÊì^ÜtŸëÍÁ:½k,ã~=ÿkáñ—xøf&F7êLbOù"hä< =™÷¹Ñ¸\³=ÅÅ3æÂÐ#öóåtxc½…lã©Yo-T¾‡”žãœ†Èu9·Ž샼3åð34#Ü4ˆ÷›hnN-’©äLéiˆ:¹ò›~ ½ØMèÉ_Dç¯ZÀ –ÿƒ&zë÷mô?¹ž-—N$äC;L>@G¼Yî­hlRäðÚñ»¨²|¶7Í¢ƒñÈVwàØÜ© ý¯³0Œ®íOS\5ñ,H¨‹’zãºûα\Ëq7Ø¥-OÏKç`ļHè+®ávœ@ÅåʤRªF¦gbžôQܶí>.܆ϭ²aýÁëø2ƒÒypk^xaVi>)S{Æ]ÛpÜ ØT8}·à¹‡'ñƒ.|ƒ]oÉzë; 3õ3öUŽe÷k;`Ü ­(J„Õ* õT ¦ÜŒÄ_A 8E^™6e~#SeNA@¡Ûù¥¼Ö-¢¬T̈–ÎË$[ãС¸—z¼®Î¦'&À¢Å ðãf%<|þd·pî¯GÈÞˆOЪýÆW6“­Š›Hìc;Ø>˜K~JÒwÖ 9ÿ©-üv¿<±ÆùZøAg=½1iÙܳ-Ô|íùQA4‚$SÑ+Ò ïæÖÃ'{Qâ£úºåú¹*µ[\±£9 §93OsÑÊ[¹/=er„N?ÏLáé$+¶âjºWX€ô#1vå׸|~ 'šP†o.ç£íØ~°)‚eoßãÕ_vàºR€Ý5»„‘=5Ø1]Ž1ÿl¬ý„¤Þ©âêš³îÂÙœI-ªJ=…oa:qfÛmE¨ü¿là~ÁX«7`36 daqÃÌÔ²ÇÈþ%pqI,zˆ­&gwÔ-l›{RÃÉôB0>˜ ¥?Ç2»ïï@GNžž;ŠË¶[2¡ ×a;ªÒ*Mï)¾Æ3GÏÆÏuòä1¨I(²Ÿ§(žóB²ªö76Dƒ“N=g;:[5y¤îB´éJô'¾^•1'SçÀ Ú\‡Ù}¨aË'ôØ€©; ñ篃ðmÞV²L§çºLj†,RßÛ€{ÓŸàäùsqâ‡(ÒN¯`û™¿àÿ>¨êGÂ=1ü±4˜Ò¼ýø,÷N5“lŸ8 mÏ,A¦® z7›cØït×϶æ­!žŠ!|<©Äz´ÂÈøìû d]‡÷²l°r¥0ïƒ]SS@lêK°|‚ÎÀ‹ß2,Ïy9Úõ±np½*÷-ýð÷ŒA­©/8ŸÍâ40WˆÌðÎ¥–§Eè‘Y󱉿r â³ð£¬6û“o¥NÂ’mëØq %ÁPxŸÇZ$ðç?~Mn ¦@àúï0±øÞ¿‘󴆯¯Sºÿ«0MMým³}Êw|36 Ðh!]½ô÷Ol §l†‹ó^ÃÝõ%è=WJü\Ì ŸÿKèK¼Ÿf‡ t7¿ç/ÑJË…pa?ù¶`è²Óx14mv¿„[CÜIOèõÇM¦À›ÎUøÇ@„>LcUgÛÀýtÖïƒÓ•aú}k ÎÔc³,Ú| !˺±þÀnNš*XùB*vîÿwåUrª{"Us°õ×´yæ¡rîj>6„ùWV²„º­ 7ß ¾Í¸‚+¦Âì†,´ 8ŠY«ÐôÎ/b`~ š¿©³;Ñ#äèAvB)„¿‡ã®ŽÄI>…œŸš~Òô“¥©•.ã–yÅŸñ­ú9ö简¸¯*¶>¨Gåàøeôî'ÂÁ©/ @Zu_½€¿, Y<Ƚè‡MõÿHšûÙ«Éúgr=ú‡q­5 Ôesÿé±cO8õwÑ} ]HpÒ„8ÎQšª>#vîŸN·íºýˆXXNckí:ø;ª=ñþ:¶¶,]Vg¢´¦çèÜT¹@Õ^k:Hɸá¡%P“TßB»fIÒ¡{Ö¼wJõøg‡ »²w*%Ȳ¾›wàÙ\ª¸\”MI¸ ©òo¹ÿÞïÒ¦TfùMXûóä%QX?î4Ñ’iæ*\P«á Yüs=fªÎ6Hç#›¹#E… y-N AË‹71l#Ÿ$‰ ÐEgQÜø·yñ{ng¤ H-#ŸÎa‹w ñó¥6`Ù4cöìñ<²¼ñ4jFô DÉØ+¾p#8€<‘}Æ_¨Ûʽ»RÅýÕVãÜoã…—gðê2âùÉ ·w½ã¤ÕâK¿@xuU”žƒ‹‰èƒü vPÇÓÙ ©AÛÑ~÷!ÎóH1$“yÔu½OÅ%„^Þ«Õå7Šk¤hžA¬úGüÖçàíQ6éG;¾Ü˜ ÿ×%¬±`s;I™—Õ2©ÏüCÀÅ RþÖû\× Ušé+N;å±gxöŸÐaY·…é¢éå@¼J!Ôãê)‡#ãa’p Ç«7¡ºv›Áùg ·oçc޾®Æ ˜‚‡}>ÙcÜÙápüô<<ù¼šKy’°³¼ü~iÛüçÁ¶æïd#oJ¹R?¸>'çÑ^û’Ûï?È£_ 0dcx̱¢3·t~•×xRO‹Ýzzî9™BÉho¹qÉž *ËâÛ ²Î½ÿ?O3÷ÞäÔO7¦7빕•Éø²) ŸõÞå~¾Å(qGòÌk $óBx:Šï@2O›»·ø,ïäÇe¤ð†¶ŸÓÅ’B+v«i".È2Éò$|ë§Ìšœ"ªsApŽ.³û™ƒ…äFûÜâ—ŒíÁ÷Ó2q¶ú]N&ä"®Ú/ËŽ‹.ó Â"ç%,¥Aʪðnã8&Ò;ƒ|4ߧŇIûØ ¼\·„uœ} .Ïìѯ(6×ð&wþ$¢n ÄwÉò- ŽHŒ;E›çRrwÌE<´vÄöï3ÙH(ž× î­wðà ÈŒcOŸÀm«§q?,Ï“º«ÝøÕGžFüT†Ú?v`­l:œºº8õ««¡ußUÜ#úŒî¢Ôæ$¾­n׼ǂ^sÈSu™ÔM%"Öy™Ûá¡ “fN ¤ï`®Ùw çÕ9ž«ƒ8sÝr…?®ã-\Q æÞtòØ­slϧ*ø»Lƒê=‡z¹·X§ [BuÐFÎrÒ~×/g™w†« É"Õ&!‹ßÊŸò ,º[q÷Ã*ˆÿjAÚ#8’3íÿÕD:¢è†Ölòà8úÄÃô×q(þáV fq';΂¹µ5¥eâè¡YÃÕF„ÚºÌKoÓõsáÛ½tè~’ ™Ùfà5ø ²*Œp}÷\Çá—Ë$ò~¯pfÐ]¢’“j"ኒoíÖLË0¢¼2_4ØKhÚn6ddA£6wBI„ ¹„QoÿöwÆB¸Ð"ÍÎla¤eÇ2uv·N |ÔM¹€I³Š¯ƒÄ"ÇžÿÙù°ܫĮ$|äé¼.µy+WgVâƒ~#Zvµ•˜QǬ%[hèIM¶+Iˆ¼ªH#Áóv%Uyô>æÐª7uÜÌúi£C³:ÈÎBl÷™H~jê&Lû ÏåöFìÆÒXÿ=ˆ¥mއUAqdÕ…HôŒØ‡çÚâ̰[°ÁažÌ®cx¶XˆMû{UΪ@Òâ14´m vN!ùɸ¬ý/×\;‹~‰ØŒ»w—ÁíTÒ®zL~Áý>¢‘Ãͨ•¦OÎo\ÂùZ‚5SÇÐÖýÈ »ó8e_ñ2BÍŸ8‡»°ÖŽ]· äÚ6ï÷Í· â“8ó™#FæXCœ–.Qxéñœ*o *Ï(àÚ¬°ßd%&õÚ¡ÍûW˜³Õ ;ŸO€uÚ’Lbà ôê•…*ÏrÞá+i˜ïnCßÅæ»M£7\†C¥“Ø“Ypl9ÐלUk'~ìƒnÞ8<`{~üø b…¬Pù.UÌo†1‚dN>™ðˆƒ*#*p¯‹×ô³ŠëaµF*‰ÕJ±g1;I+ëc`Ö‚<ÎrËgüúV˜ÍžPÉɧºÓÖäoذɀÆWr.ÕÀ[Ç›g ÏÃÀÝć·Òë293/ö¦b·ð-‡õOä)­óÃçR¬tà ÐS{é˾?ظà‘;˜J³ i¨„#nÁTÑ¡U»;L‘šʬCML¯Œ`võZvpý=¸¯q¶ËÑ+-ßÀd[ 'Wq¬ošÐÜÑxyFÆTsœÍÍ{y@7™ÁêBkÌy~ …-³±¬¼Z’@Äo. ‹¬›¼ ·@{?JÐÔÍ[ÙÝ% !òà&2ßy ;|ï)ù2ÍžVÑ›: Þ$šD#©¼ÏmÎûà,zh™óÌ O‡Ë`®ã,–äKÅ·K²¥#`Ó.Yø »>EK‚×%_v¤â#Ùÿx&—óG‘>·Õ‹ ôá«.ûqdlù‡ò÷ûá$9 ±‚’4jº›~Ã…$è1­C‘·’¢Kšñré†ÓOó}]uÁÊw˜Óžß‹kßgcdd!ßmTƒµ­×ÇØÇÐiR—ܱ*^þæ´RÍÐ@ßšh,œOï•¢b_Úɢ 0NÏ^|Æà2ì”±XyÈ÷~yâ–~ïqS¨LŽ8=§ðêÿ÷-Þ‚'Âpï·£l{wÙû˜e°sý_0³í×£:œ2†¸R[¼ ¸ÒEŸ®±Ã1ÉKàTw9'Ÿ…~k eûW¬ÛcÄf̘û¯¶œó'®¨U~,I¢…»g²-¼è˜Wã©}’ü%çpÉmcοC‚u¥÷ãÞí('[z[¸}¥é?ûÔˆý€!ùÉ42ë=X'q‚&0åÞÙxòn ‰ ˆ‚¶10xûœ¾!L‹þ’;.ƒšÆ8,꼄kYÔ¼»x\Wb°÷x!7»±²X\Ù€öâ|6 Þ½.±úöܾ3Lj`ÀXH=›NL“w¢ô¯­¬ø«"éŸÄ¬§Ǚà 0gy¬˜C%öÈò*Gû{±à/XÑzºžáÓÕo¡»U‚¥´ †uù€…»<(Û¦·Ö€oùO®dúNŠò¤š]%ä{ÕkÈ[iF&Oj£¯8|})žÑÆ£%^ÔgF.“ðwE£úUL;s–|9CÒØ[~¸uó‹ÂG‚§FpÙu‹/µ‡Lô½ ?BCQëì[x÷î Ô–F¢vv!ñ’ÆÛ‰S0áÂîä¹öCK˜n? êûôátS7y¡ðÕîs ´8™f“ÞÊìVgs5`€ï_n†<øªIµf›Ðƒ—S ná+ì‚­l³®<{vW ×H0nåJ²Â`íø2쮚;½±Í_¥À¯×ŸáÃJ%6Óÿ=ù¼è>·®Ú…Ƚ>ÁŸÕávñ/Î~†,T{'KIÃÜž~øwr³]ê@µ|Œh髇øt¡#;¨h…—ü8²ÁÝó?ú¤Vš³½e›8lŽ´i2Veº;[ jq ^j” ÙNŒ¸²8G\Åûm¨çÖ¥‡ñSh ì;0BÊC`zåU H6e'¾—AÎ_tґþªì·ÓJp2Ôe;ÂÇSy¶õ^óƒžØt\P‰—êÕß^B.õ±6©>XÜÂçGZ7‚ànk°Ð÷¦"÷ôX•š{P¢Lï¦K@TD4ž•* ?ö¥b禱Pý²^¾.š³åÏ?õ)´Ïõ2–e‚HU?ïðàB”x›Ë’®ß$¯×8¿L’_óŸîÌÂÐ]êlƒýd\ß0„Ž~Fô…ê;"+4÷oü.l,f`/O5y¯°­Ï‘mù{ ubE—î¯JD! iÖ3K›9Ì—$',MÙì%œqu'ø+L aáž´†_Áþð±Tîâ&j9f%[´ÎÜ—¸¾)W`Žo"ž™äAÁ%T2»/w•°?†ÒlpðÚβ >r-XöÈ™óyÝÔÞ&X”¼Ü> M¸ÌÛ¼:”ýfgÇ6ÂøfBóÂ|qèÖvŒæ&³×BÅ}9<HÂÞȳŒKWHiP:‡.d+ó¤Ù¶iBôŒ%…‡ñȧih˜^s§Ð¯5‹Ñh[™!B Ù¶¬%Ôð\9_ܹÎEü™±5á4D©ærÇe…˜—ÕxXùï ÷Ú¤ÎðL©¹Z9Y-D'V#gY¹žÆo‡S+R ö†$íò†ŽXQúþÂD6ôô8¾”= w»2an±Žû, ÕÓŒ`ça8Z¾óçý%§…Ô¹óõþXwhýsI”¦_]AõÃËÐÿƒ5ås–”úßìÕz´¶› ëžÍ Ƨñ®²ΉÈåŸ1Ä[úÛ°{òtºdîRî¡f|ÛíH[˱CE”Œü“¦;lo“òcºðpÀ…´Ú`ŒïÒ9§ºý‘üiG#yHWÜÌuÍ‘‡^!Fñí`ñ¥±Ì¬î:x5€cóEôŽr¥ßLz ›O‚«Ü¬°Q\‚N‹c‡~ ùÅ0ʳ-k²Y{yÐ+ñ»«œF~í¸„ª’òtZäEðYè„꥗¸‰îøŸG{ÐH}Ô¢E¾——_ñüÅ’âuÖÞ^È4C§1s“ ¶÷¡‹u›7NŸD³A*`‚ûm|@Ú%d–0z#šP‘µ¸ï`2îíËÅ’%¢4µŒš±{J÷Ñzh¿ÝEvøÇÒZ'3ºìjº ­­°¥©§}ç`õ Y4ÞÜ·>†èŽû0:ª®­ƒ%A¼º"m]ªn2Ó}‘ Þkiÿ&=<:ó¬_þll^áÈû©tÅ@4tªï'·Dþq;ÅdØŸÒvNô¹ -ûA,LŠñµ›([³é+6L¯ç_ÞÐ oÖ×Á©ÒØÔ··vèæ˜¹ÅïÔq0ÿ›9iȆ»‚/¹Cjìe¤Z]°Á3®fe4ޝRd׌¶Ñ¦OÑ3T‡ÜÜHø£nYÂÆf¬Ã[{j`³È}X5¯‚³%èø®wÉ?èn¸ºƒË#ƒhO æìx¶ßñë,wªSy;·G7.ø“-O¹a»µL½ó%ß‹§çF³;¶¹tW–+“,ù×ò±ul WS¥%"p,`#Hë¨ÓÕ''°s É·e~tüýÍ´aáM,Û¾žÙ=`²ðýÔrTý™ÎÖþÊ)E,ày_`‹¬mqU„ß/DEèY£‡3žÂÄŠÔIÖgϽƒ|têàPà±ëRyw÷Ê¢õrA–Ù½ôÔÎbÌ«bÎþœ3¿{$nÜä ¥s°POŽíþÇyJ7«¾ ˜Ã7‡ÇVô€Ô¡7Øÿ²Ãú0Âb&Éd3: ¬ádh6vû·‚bþ-.i\?ñðÔ¢Oà ÝÕ þ´ëÞ(3óžk$­a,=ƲAÇEe Þп›]ŠŠn'É–MÃØÛ9—bÎûOG£±úÌÿ™C¥øZÁ÷˜Á;i‚ãxð?PŠÐ&IY[ äE±ŒŒ"þöä àZÐrr_ñ?¿q´¨ ÷¬àè9JBɃ*â7o§ûXœ=»ÿ]€þÓòì÷ÍPÀ'‡ñµ‹&ÖùnÆyQ0œù„äÏ–e‰Ï&2ý¼œ9_ži*BÁø± z¶Žëÿ·Í\Nâ±ý܆¥ªì¿1ÿÅ64d·4¶ÁôeIÌòÛ.\±}+h9Å)5Ü›¦Oxãñlìð–c!½;À.)×IÓÈg¡4dqwyãdöðnõ¨¼ )HƒdèŒñÚ¼±³áøÏª¯ Úq}ì?àª7½œê«xµuqöѧö© ôÜ™…xZsOJ…à+qÔÀiùäªõÚ\µs)n÷¾‹láV T#·r|qh!¹Î_QöòBN O#Ò/»5U)t€'·çac È$ãy¥å“ÀuÁìÒT]ÒÖzT®ËáÑ¥õ\Ý .ðWeWÃå /ðܤ·ŠUð'@#Èd³Ò˜ˆõ{òöt ÞMüLâW1X»² À}lÌ<Í×÷HÇ R~xù×Qò·PžÆíF~Ú~&ê¸ênòz'ãÉÀ’Óq_D…KlèרèµÛÃD ˜N¦CëÅ™’ö˜¤C Ç æ³c`×ÇV_ÎNt˜°¾ž—\E˜Õ4PÇê†ï°õê~öÊl‚ä^)žÉž£v“8ÎKK5¾©BÖõV|£@¬µ+ˆ¹ê& k~ˆÆû%ÙBÓlêÛKnÁþ»¾gŽÝæ¤H}ÅÔØžc;!rÖ w"”jM|†ëž®†[›â s8ï»°g¿®Àöß_àpú¨®Ü)HIˆ f Á™1?@Ào$É£fm8±{»pÛ†¹í½KÖÛM‘q§Pë©7ÈÏH‚¬•a r"¬XCkç]Z‹™Ñi¸¡8˜]ÐI§3Ýå¾§L„qèv¿Óàîn}ö®„g^Fî,JÀÜMô–CêÏ“8îh\ÎSÇÉ›Päy’{ýQ†Ý'Ïædà¬)ðÛs óH5e1Ç êg] i¹QÔÂjå¼Ï0Í\ÖBƒÞ¹Â¹)¡¸ÝË‚"qD¸®š%ƒ}ýì'J­5LÐX|1•Ì"-…·QúðbzJ§ ÅÆq*Îx5Úê·‰ Íþ›Ú#åÖ…©3ý¨§k>mÿÕÂ)=®Àsã¨Á>ÅÅ|8¡†wH²9ÇÊaÏ&Zto=ÕŸÞŽ¾üƒÌ”IìÓÕh«¯HMîAFÅ2Öå劕o>aÓøÚþ'||G|­ÜÎݾSLÙ†šÒÂ:¾ÝF…Õ áuö~ÂÖVÚö®W0h€çöë'ÐÚ–kø¢6+êMè#þc|ÝM¶YE!_Z€‰oÏ”w `·Œ¨F©áÏ*o:÷“«3')Lã³S:¦­ô«¤±ó4w®a'-¬\Æg‡3¡éN»¶™u¹wÃàý‰ìjÑaf¸ª€=Žƒ¨mS!Æã,ZÌœåYÛL+v¹q" ÿªf›IÍ[r¡:÷¾·fj% :ÿ24:Ò%ógà¾f¡¸ùì|ôF:q”ª«•2í]/áRÜ27bI«ÝÁCÑ—î›(ÃÂŽÁÎÏùÌU¿¶þ¹f{¾è/ñ_f>:{‰a%ßÂpä«$æš…7!Øö*Z:ËÒ†ë"|ÅRPU ë&Ò×ûÄáßì9¬:°•$Žseº:py–;êd¡½É)h™×c⧦¸x× ëÅÏ4Øbåéô¡)¤4GÀÉ5†¸gf ¨,ùGD3áÃHWéÅìÙÊ›—Ÿ®jÐÓ5¹æ9¢l¶¥5už+Otƒ}ðêyxê íÞ?I6ê8Ø%\&OÉ@VÍÞqˆŸºA6‡³/­‰OÖtúè‚”ŠD£6y%6ÅãIíË#(=Ç+>f‹ñ³SQÅéé7A{Ö2¼ö~è>ƒo³àÍš4Ðï^Qwu“´îȱQmusU®ÚêKè­c–À.q× èA%X¿VŸµi§ðS¦BÞø¥£ëä ’±cð«ß|ôÒR7Á‚c'XÍ‹Æ-ÝK›Îdç'GQ½;)t0å^Lâ>UÀ|µi$() W‹³‰Ö£{£ó/gÁg¬8kÇΛ²¯ï1j·–^¸M„T9rùo <êë…¥rtRCêœ{_70ד߮þü.ëãô?æìð‹@båÿeLEᆱ®ÃËpã@*pKg(L˾@Ý—¢§Ž$DN‰®Pæü»Ê ì•D“íê¾½9 ^ïy]{uSçÁº„Fn…ÀžL† üõ–£C®qÿdcœa9ƒ?Iþ<àOÇ–<¦] š_ËÄYù…H´©™qK‚Aâñ)ò`†¾ì_Œ©‹¯cö…äV×\°ê9Ú,Ù©r×°ìªÓ4ŒÀØÁéôÓ¿6üVš–ñÄQ¶ÔZœ5lW¦Éÿt!zÁhz¿çÖ†ŸÅ.8ÕC WßrCÉÙ?Qµ?¼usñfËoø¡†sí´ñKêwx÷ã.–_Ë%_ôäɃ×Ñpäã;¼æ6Ìpæå$ˆP£«×xæC$«È¸@ÒXb R'w㆖fÈáZÐíõ8¸Í]ºò–hÇ£ð8sð¶XÈÒ§zRÃê~à.:SúAŽÎ÷—¢É-på÷kÛÑq܃ªZýƒÓ úðœhŽyIã6Ûšdß½¯¤xƽ>ôÅñoD9ñÌþjt≰ÞWÒô¼G/|Ì¡>*™Ü“š‹(œÕIª†pÂádWÿl·òf-Z8†¢@Óý«Èœ7O§ÿ5uÒgú?~`@Ï>)?…ö)äEq_…óhTº:‚iì¼i?…ÆÈ”q¼óGPQô~\eÅdëná–Æ Ø$ÏÒwƲíGf¢Àï îÓñ§:Cƒ]8¹1 ð’sœì˜‹¥‘4ú ß=î>î/ÅxÏM5Éå\®Ì:Öðo?˜.=êciûGÜ4dA;–Á»Ù¤Ëæ>| BVe³6 Ô”W¡ žãáÅè9½Ï6leÚ=…«³ ~H<ÎnI×ÓÃÏ0û¦÷^RR¿á%Pè/Áâ¹2ôK÷òû[ Jc+𩽛øa—Ö1žD`)¤Ù[⼸ Ô~þ-„é.½ôÉayz f²9 óÉÅÍA 'ÅäR”™Õ…:bvy„±ãàÃtÿHÃï©– ;þ(™s]‡§ÜB·&ûÁpš=TÿöØfáEoLt[†v?÷²«Ýþl‰ûfz6ë mg@oz+³ThÓRÖ$$K/µ·Ï^&³½Ç.Œ`ggÊ3Ï«5°›Džð¤!æÆL6²- ’œªðÄøéô¿ØÃevP^só…©ï•š\e3«Ûžy„¯¦ERþtö5šµÑކKG)GéEÁ!Nå¼§q&猥ù¿óPw»<1ë‰Ñx2áF.*hÄCÎSöv‡C»CÉúcÏ¡âZ)™3á?ࡨ¢:gmêúÛÙRYR¹"‹é@vþ ö‘0^y ·l–(-:A.h"¡ãIßTuÎñÌD2qvÿhÌ`’%xôþ ñœiÎØay¬ï©KŸ" Ò×Í?AÓ, tµry]6Å@·¡®³ËÈŸÝN»Áz½]4Ã÷í&¸î‚!}ä·Ç|Š&߼pÚË-˜#ÚãuÛÑÑ+b´nZ-o†ñ;žþ EnÿøúÒ”:¨ ‘˜³y)˜¨v ¿¬{ÂÕ¯‰À×4áý“^~àÙíŒß¦L낆yM))xãºv'n¥[?!ùöoÌ{7¿ändM"t4uËaP_yø®Ò˜sºžðôM@ܧê™4ëŸÓTJÑB1†Î•âíTˆ»ráÂòôOë&k"åèWMê±U5Ê»³ÄP]Ê7}É»>àOß +q ‰X{§žº 3ñZ29\¶´”¼9•KCÕo¡L’ývc7þ{¥Ó*—R…#é|þ˜0ðû¾ˆrW­`$ä‹%Âì¬[8¿E ÎlbW+¢a‚Æl.¬Ë•ÛÚË=à ÙÕ¬^\Ð3‡žñøƒã…r9²;ñÒ»dÈš{m”»™Ñþ¢.¯û9äÆNf|CìÊeäº?Û€NY œº’Ç®…ö@ñã¹ìL¿ LQU„4ŸjxíZÉMÒ|õ7T—X’-WÎÃþÚ6îrÕgR'­MçjPL–—‚Þù˜¹–==ÉÂÆbµô&L·©“ûïYÑ^¥‡IJ”î8æJc¬c@W?8+¼»¼ØQôv›ÛóùJÕ85ÿ&Å“óh&ÀÛTUª!ÛMüï<ñ—RThÈ2¸AÚú0P7Pá¦!çôÏÜ_ï!ƒ=i(º6îDü 8Tÿÿ>Û÷5kT¶¬ iQbÎë­H *KÒž¨”;Ù²¯ÊÖ&K(Ìy½íRJ%E**íI‹6åñù=ÏÿûÌuÍ5sÞsfι¯óZîûÌ}½ð„Ý+Øô4‚­½wçØç`<*AÄP;›‚ƒ7ñË„(V«Ç[ )Ì Ä%åÚD"Û6œ“ÁÖ9[õBðÿ]`S|áÁrGÜ»\ LÎ¥Qa^/Üš @uß Àè®(/y„äö#;_.;DG°O¬³fÉŸ˜g!]ŒeÉ""’³m§Ñc©Rôvà>g3gQ*¶¨ŽfæÃJMUZÿè–^»Üj-WH­#/Õ§ËÆß0¥DžÚVí';Uƒ¡}ž$=¹j2ç~¹O\üö嚇Ø?07˜·ê@ÜVz<æ%eã¹ o nór|1ð†¬Åpi²+cY±x>q».Ix9Àóå®tßgöÜiØåªG8©ú]WvPµ7\_ƒ=-Ó8¡«ã@zk'»¾pmè>Z5jÞ`1ÿ1]‹ãJölôæ'œZïxÆÌŽ\јGú~½årÖÆ±|Ëw€Å­?L¿Ùé€ücWý?-P°'ߥµýßh_â3æËõÓ¸ |_5ØBI¬0¤?7÷ãÔ¹–x¬W”ë¯zk£,ÈÔÐïÜHùO ú[ƒˆUÌ€óÙ÷`‘_>®˜ËêÞ<‹™.ÓÐê“nOvf¦]£ŠA˜©"B>m‹‘ ÿx¼ÉÄÁœ“¥ pÀ]Ø ãØ¯Ä‡kú}¹Ð¶›kÚWƒTü'£°¶¦D¡èÊQPÑ¢ wRÕÕùØè‘ÚªNtCÆJÊ-›Küõj@%BÖø…ªÑÃR‰){ìGÏ%ù`Ç×®Ú¸Ù¨BÌÞËS*ŸÞ0 ÐÍ^;¥‡¯«+àzt k^+E—hÓÛ÷Ðt÷¤þìZ¾T—y}FºÛŠÐà—+é¯i.´ùÐ,r³¬sƒ ˆ^œ+&— ’-UA8nÙ7$p@d Yp>y Áv™èªR…ÚÒ0ò{ܧŒg¸~sQA/»2ŽuÇEVÃãêú߃…‚Ãx.X¯y¯Å´’XøÚš6‡»Ñçf`Câ `>ƒJÝ; ²™CÌ‹›BèðJ–Üsp€†×SÀi\^æ÷Aê¸ìv—ußwÎ2¡Zš† á8×§1Ÿz„莨Mù+ìUÔD“÷'p~Ü<†7t6[´.ø¿Õl¿Ÿ2|¦„âæŠÔæc ÖúŲ¿ ‡™ác„'–CxeI=xôóP‘O‚hW´ ûöÕ㦦×Ìî&I,÷ue8'ö13Þ¸ÑìÒePl9‹Æ¶ ‹ûÎX…›:TRdb¾ëç.0MÌžùýÎÄ£ qšûün2òß,eHKŠ$鮩–^3Û±#¾ä¼H²âAà#Ÿq¥Ð>rúýG¼üÈsònh“ gåè±NÿÙÏŠý•°ð1ÖeÊέM‹èŠa=j'ü‘-ÿ·û²`Ø! ïzh£ç‚`*°Ðt-_°yÁ6̦`w2wš-y÷3ã¿…€o^X¦ÊùæxÖÛŸ‰šŒ“ö<¿°šKƒ}Iîx†™ç¼‰žµ<õß\kF”=Çúˆ³ðÞ2.4ö°_l±lû(…-²B`”ˆ"ù»á¹‡(»öµ¯Æüz‘U¾jA}¯í„ 6 À9I ¶1݆DÜé"&Ô‚öÙø•ÌÇôzˆD~34 jû$IéÁ¨u‚Ó‹C±äd>Zéc\F&ëÁ'ü©þ®m†>yÐk7W7LÇ-_(;lèÇfN2‡ï̃„¥ÆmÇ@ÒŸEbK‘×ü l}´„ÜÏYŽ‹îaüLmüvÊôlçÓO–ìi-b{áG…7óÂcL}ÿ wÞ^ÅÎi3Ã=â î<æ±E>ÆtÜgÎ,}‹ »p‡«ê´%€¦´7ŽØ^Ç~]¬ènáħÎaãêàÌÔ>`ôÑâ×Qæë– ÆKÌxn~=_Í~£ ¿6vÓWL)G¬Ù'‚xƒIÇ¿bHØý›‹~pÕR/³'þf3.ßás'çgÐI†_­çT5ÌäÌþ¢Ï¨ë¾<å„ÑÏx#-q‚¿÷ºA×l*Ù<Ð~ô€qËË¢r¢¼,{ Å—íÁÛ’A x¯Ó—à=¯>Ìò E¶^ùª–øÑKD™ƒï‹-à e@YZª³æqÜx™v/ܹÈwŒÛ æ0s›-[ºÿ=p¢lè–f+²º_ ý"orŸHL“ ôÁïUÄôÚrN´I(ÏL™¥ƒìúÒP{V„ž‰Ã0Ô=숹ƒ‡ï:×[/™n‰Ç\Áú¬þqšÄ˜F¨Ôj{hL,|_Bêš ”}3ö^€m3A\Å(9õªv£sê._‘ÓF¯aõø0V¦)Zëé«ã$í³]ê5ÎÆ²1¡ÒäoSU(Î Å ÎÁ6]úiM=¨ÙÊDx‘ÚÂsŒla9^c]ÇP+ß]° ;öÇã]¥•0­Ãɺ‚>•2¹!ôk&\âØáÐéô»N&»’  Îî?°ž™AÝ3ó6ö“%ȸ}Î }Ã…Kag®>%;'õ{jܰkÁ%>L¸ß6Taþø~äpn¤3ƒ#ÿXǦ|—fqúø›•>Ú·u±Uóƒñã¶h!¶ ÷¹"úæ°Ó0ü}Ïü¢Öú´Ÿ’ /ÃÇáÕ¶X—¤Êø^n gCÁf|m?¼]ÒãÌ™ÞnàëvÆÑ#¨Û¥ƒâ CPøèxf,H¹RMÁZö¿ß_¬b‡Aê†Ä-;Œ:ÜcF‹¯b|´4­¾ý—«f²ƒl\éé÷1(tîíú ošÑ„MÀ[Ù ]—‹¨æž«$…‘€÷/çRžÉ‡õ¯‹ì‰šhôk°dø$ÎáýÓ<ú«]Ð µg\É«í·aÕ&.Órðf¸Q0¯$‹ ÎZöí<ò¥-¨“éÅܹ’žYµˆ\*ûŽÌîâIÝWœI>5ÒÏv½² í–:øõþQx²ªó%½É†Á×°f¿¨.' ý¤Ÿ…*ÞÛY·'/ÙÚÃÂD&&‘ÕsÀç-²ä÷³0´íÜKÅùhá[yøåXÏ sDf£Ÿe™:ö=­‚„è=ìǰ•lÛ£1ô=d Ûüôáò¶sìsŸZ&Âu1Ö¬©ãÞ‘í@÷Ýæ$©ÅîB{]#|hÝ5GËp±ß83æð’ÉÏidºxçÆzåú:ÖSçÇNÊ”®W€ÖvwHš9 %o—BqÕΈ‚ —Ï$÷Ù*‘õ ±\1'%šgÖÂn«iŸ*¤yÆúyÔ½H$~ÝÁC,wƒ¾ó´’dEžÎN m_gÒ—o–»;pÿSª5€|_ïÂ`€-Ú ~‡¿»9dâj&^[°¬Û%Jϲ¿,lL5±óQH ÷OQ;HýGß°$4÷\ Ò“üÕcßl0ö(E·ê½Œ÷Š‹xÖêŠE½@ÿoßð¿x¸+º[{a÷²þh¼Ù›4v¡¨©î>ƒän5ò…n¿7ÌøvÄ6qg¬ßºþ›ÝüÂr1{k·/^«›F¬~<~M¬³ÁÖÄ¡ù$î€:µH쀖]é°âR³ùÓäÎà\¦õ®ýj©hìí2 àQÄeŒ/9X¸«ßdN_ÏÂ9?.a„l GW1öÄéJ™°LAšð¦Ýg97¢0"H¹«í ðùBFá)*Î>$$ˆIþîLÜ[·þ~yd 4 ‡“KRýÐÒÛ îÏ$?÷=²o¹Ë–%áWßãP´ŽCŸÿº¿÷†!äm‚·+iôÈq2ÌQ&kÌHƒÀlQÚžT„»Oƒëš…PµöîÐlÆ*Ë8õDŒr֩ઋËÈø¸:µ÷™€"×'äëRl™h"ðZˆi š>û0Jå>DÙ'¢}¦¹eÂ|)ÑóÄÒÙ–Íyè"O]öð³¥–‹ÈwæF )}Åqö’V!c[»Îò&0S•ÅèE—pb¢œ!ÁrXŠ1™ûlGíèµwí(|އˆûuâ_“hT©¦M£è^‰ËWrªÂãá·¶:ýy3ÜpfÑ ZÊÈ!žÙJ”eã°ãÄexZáL¿wÂÞ/SÈŸÄÓ°£c š…ÁïÏ_±û~)¬Ò}Åý »”,yÄe³fLÀ RtÚ`/;¾L“Î4Ô'­³;ÐHȃà¨l¢ë¶å¡6ç4ù±«/Ô%óùoÀðs9úÊ&ÑœûímNRÝh-ôJG0œOrÞøÐï‡ý­ ¿EO+£^l}_š$©ê±é¤–@Îì¤êËôH݉i ­»ópÕ¾û8d¨Ç,òšOdGγjã൭pÞx+ÞH„ýV6ÌZ=98O&ƒªÌºÃ´Ö>ò ¯ Ò½6dn¼ 5<| ÎUñ Á¿ TZûV¬¤!ü—q1Ã2Yeäàfyj™ž ×?°•K4J¬`âí¦3 Η‘"!bRý_ÁŒ€-dÌ;‡-[ †wÛ#é|¿*8õG]GR§91ÿ7kzI\7ÃÝЉJÂõ# ‹ëgŸY²&µ\ÝÈ\6•?×n®ñÓ^0ÿ#IÃÜØí&·Ð÷\6lœäX'øÉžuŸ¡ve.&ó¾¨ã³Q =³ÍɦX>4b×Síé=x~‘!Ó¹ öÊIÁÃÜ)dšüj¡H™ aÚ/ÅKv©$Ñ€ ´¼F—ð·ç³ç]âݬªÀêëÿîy|‚0o>0Ñþþ+"ÊÇ$aEÓÝô ,úàÉÏ]ä~[>*Õg¥Ýøˆ[új”O9:“:ôf2ÇvãT²¤<‡æûEQKIú&×ÂB&õËE´ø~‰Zº†» O“£q®pkX™^ÿ¨„¥§Ò¤åâfæÊ0á<ÖØM…L¡¡í«,Ê–Ÿ¡}Oá—J,NËQc GnqVéˆrÌö$ãœSêTâÜmàZ“ÛÞÂøî ˺I_}éBŸ8©®ßÙËÞþ+IôdNáÛâlαQü¨6‹, §g”=9¢Rú¤3U\s:Aö^°¬ù‚ÙG|¨ß"7òP1ß¹Ìy±H$ÕO€Ã?AànS• !¶ãB ÖüþÂ^YÅî)È÷=+`£Y6ým†µ‰¨ÿÁŸnŸÑomòØÅr‰´vå5š 4fV‡£ñdÅðVy:Ä^{X‡îšP}® gŸK߬!#æqÇã“t÷¦¦â‰1ë¸ _œ–VîVY·ó9éb=ðQÑœþ„¯_ÃhÓh/´~qcxøK`qðtÚÒ ©1‹èò=§©ÑWmºC¡J.4B_Þø¬6ŸÊëö±ïTÛ»ŠÆøûþ=•™.NÏ…ãœ}è“iôн åy‚ë]6Òò§Êôä$ôÞŸ §;¢Œfj‘l ÛHNÛM§Ö‹‰xþ)æxÖ~"­9yŸÄ¨O«;®«€¥3é…:Sš¸4ƒÄt‚Á×ý0óf2û¥â4žÒjÄ¡iŸ0²…§‘gN\ð¢÷fîb~œ»ˆÆ9ytÇYX_1 ÿEU Æ‹&\}¦ö^ã'«ô;&›I g·´^Æ—&üT)m%{rù:X É®3vƒÁá¸åÅR¦ŠF±‹ÎdØ)cLÂm/øçÿ .ž>Íl[ÀK\â˜ç'4añi1*¼#›Ô¦ÓÃâp¡¢ ß& Y¯™äÊ„$Ùïe‡É•3¹ç~ï'=5HQe J].´Ðz×ÉÀ•û8š-O¿Ñ›ƒJ¨¹ä5tÃGø#èÍZ”½… wZ륖„£ú7Ê\ÈCKf®d,>ùûJá©þ6’œ[Á¨í $»äèØÂfÆÓ@…ººÜ†ËF¹Ù ¶¼hf-ÏCòê ëÝ×Î) P"û»Ôй7ª×0¤òêDCTéq¨þ•ˆ»]ö°úGâP{ëm|`¢M/=‚©¯Lêdq6w¥¥KèŠP]´o?ŠÛ´âòvQØ+k€6ɾ¸Ç1NÄ€1·DËž~ºFbr­%¾aJõ\²/Ïœ¦¹Tsf<ÃùXë?pã2ú¬CÃ1ÎIÌŸ=F¸Þ¼ù'ÖT•±]Sסæ9'ºµR‘X.ÕÙ©·Ø„UòÌ>k‘Ú›ð3*vŸØ#k¼°é‘»Pq-þÎÍ„ n%xååEv×, Z9ÎxÇ_›#AÀm³Ÿd œÞy‡±æDÀ‘¢'`VÛŠz‡} ,‚Üp½ {ßfàȃ¹œå+çÿÇkéËÆ6<L‚7h“„–Ùº·uã˰ôôuènˆÇ¨ˆ‹0án?YŸ¢‘9‘2Ò!—æp!ñHZ»î笞XüŸûÁ¼ð=uþ…W² tZ1w»þmöP j«Aöœ±:Ú!‘tÃpü> )7>²Žk²Pqá z( ,ç#:OåèqÏ̽éI2Ÿä1¥w(ãÞ‰rióqý%ºÌè!¶ß«g¾;ÎÀ©}\ÜŸ“CŸÃóýÅèï÷7•±+¦ôâ°­¦@í‚X}cº^·rÿm$áw¦“e-ÌÄÇõ(õGßå, 2žm¸Jhʦ¾ÃÕG÷‘¢·÷@9â05^ìE7[-aŒ½¾E|Ä¿jÚV^ÁeÙc˜’žÆºí’Äàƒ¬åE%ü2 K‡0Uú=ðLæ§’ò¤>+¹Ë‘CnZîû— 'püØÀ¨Ì"Ÿ¦û±nå\hœ3•XÑÂ_«'°äÌs·jJÜãÇp"êÚFìÇKAy ÕªC –sÁ¥rçìØ WÝQÜ. G|ƒu!¦@ ¤èç·W—¶]Lð«e4`Ñ_Öfk,9±£ˆåƒá?™Ð7‡}8âQuhYBzJDx¢tn¤³ÿÔß¡óý8<8©eŽÅ‹‚ÿ¦zVŸâT‡¦¤o:·Fò=¾7·Á±)ó˜Ï_£àÛ4Õ¸Ÿbà]ìy «ø„SW¥â«$·0ÇŸ}ÆgÁ§áƒÉ,L¾·†¶¸EÅWñ|3—þ9’Ü‘FfÚGUÎ|Í ,4O‡j:T:Ÿ¼»Pþ‰Å±·õÈ÷™›ZŽ˜P§Dù§váážNÜþ¸õ¿^Ž£<§ñÝ Üg ë¸!xø›$™ç/Ž;×XÀá•dTç&kÊßÊ ²ØÇxMå§8ÏŸ,L€ERÂÔÇä.û”3ŒWZDI²ÑW`[ïqÝû1å¢.{Ö¯”‡>ãœ\5ú,à<³Äo*ñýj Cç"¨Æ’—p77Âì?0—ŽšÐÌædèÈŶ£<䨉C¤/H„ˆ™úC‹ýb&ðÉ ªkµâÖ—âë%ÊT÷Ú¸¶i2¾Ë¯@Ô{³ÑåI¹>‹Î ÇÜ2˜y^ìÀ>cª[ìG‚Ò³A¡Á´dÈ¥ p]µ‹Yß¶•-ñï„Udy[9„Ìö ü{“yí¤ûY7)ÿŠúÑòù³Aóž -jòâb!Þùt,BHê˜5ôü)¡VöŽDÇs)›1~Ý)aMx_±G‡opCU~rïŸlÅB«×Øž…}F…ÌÁuX4†5#OÀ6:„ü±Tó¤ Ð?láþDP<“Žý—•ÙmŸ3õwU©O |n~ {ß@Ýu+2¹ŽŽ‚8Ûõp&ßcÖà_5 ºÎÜæ©Cß7ñy{'¼ üÆ™Õ.AD¦Ñ{gŽÂ°¨ý.“Jùˆ€Ô$ÌÒùo›Ý$, ŠãX©\XÔÍ¡ˆ7î]ÅÒ:aö“ôUhð Ç <¬ë2[A™ÚùÊCÒ•8T6ˆ5§^0‘ÃGaÛ{L±’Æ×˸ ñЉ>HSc­X œ[³Èߘ‹¨¬½‘ z„À]nh( Ð+ŠñÐâoDûÞœÅÉpX>°¹×Y —胼X¨…uïÓ`ÙáëøbÈ÷l™F/É>F«81&¨e*¹u“ÑMƒSǯà¾ÝçPš#ê¦~°õ=²uCØ—ÿ2HÉéÌš/ »á ”¾¿Au«®âèu6ÔIˆ–/Ü ™\LN.†C÷7á™}‡Ð¥Yn<”#­'Ñ&2›õz¶[<&ó¼¼þóEÏs Å®³1pbÕW ø~¾ÄoîQÒÊ,¢žñ. ã°…ñ«{Eñ ˜…VgØM[û ãNÎÇ ‹Ix› mŸˆÁN(.Hç5ƒZÏ&}óPkº cÇdY™þ°µVf4¯¦Š¡?©®6ÐSŽŒšÙèþû'ºö{Iñ®Gæ–H.»xžl(ºoÐUó ×!Ȉ>ú)@¥¢®ÀôÝ-ÐÜìÀú>ùÄ\ùþ‹ób3Ðyfå8kÃ-ü*sžÑ—¢J}îZ<›¤ð¿ó¸5Ò –Ê3`í»qðÙ·.Т/ügÐ|‰d|‘&]À'ÆÞ,Obþ›+m©¤IVÃCÎÓ¡'˜•ñø}A^¦ÿÏ…ÓÖRà¶WÆDü8BéZÄÎÈNm&žŸ&°òfÉô¬FþA 5ü^$ûø#49Œ(ŸOefÊR˜Ú»€˜åÉOòæ¹4ÑPš-\¯Å~aEßzs2¼éOܦÁb :O~fjÓmßøˆ÷è»Öûô¤f¤á½|ô¯Õ ø}þ*öÌ%ŒÛšÆÓIw]ЛܮÄGÂ1tÐÁ”§Lñƒó—Ý@²;ká™…$É‚ñ?¹3Lß“ú-?Á.°“õè”& ñ2¹ª{…éøYMÚèÇ)ªD(­ BÍ&0¦? D_G°¦Óe¨…è v¿G<²vÇýHμ»ÙÄà€8³çÍy(˜yZï“‘Š<úr} õ<•Ìäéö€äË ,¿fÊòmÄCN¹t` µ4Çw#òôøu\R—Mo(íƒ!'OrR5š®?s3fh¥:‚/=ˆŸ ¶ÒÜaR,}3Iò-Q—Z‰NÀ¥¨ ¼hv$‡º“Òoùt×yUzfexûëMÍEôgó X¾6…šm™ObÊ'’ h´^žþ]éÛelAòÙb"ät„T}z„og™>ýøøã^¢zÔŽf^8H%¹’/™edñ:ºmïg¬ù ÄlZž÷ñai.ÛkêEGt‘#˜’ðlL<¡ËJæPo ®/ieî¹,¦uäËHÚÿù£Sž® ¡¢/X¦E‰È-YËj}–Åéo2àüµª0k=eÞüÁ¬aÀjû3¸è´)'æ¬;)Gsº)¸#ü ®C˜j¹X ¯ÁwˆfC±£]ÂtÿaÊÍùœ. ÎÊ8xzŽÔ ·ý MVjW ĵ2Ò>ÊЀ‡±°Íä nmKöR±V¥b¦½¸rúcôÞ6›Þîúo¦5ÛàŽWÄ÷¬•ëÙ)ýbdã•tåCYöpJ<ˆ¬´¥ã–óaÐ{&Œ¥f0'¾E1‰å¸"@…d•ì Ãoàš™¹x3œ¹zgÝöE•*ŠÀ®æ“¨ö;7DÏ`L¯äƒ|[-3–ÎOv°räž„ØNÍä™üñtêÑZ aÑìŧ•ísŠJ*™Wû†Arj6{ÄÉÙróÑœ-çíà³,ßä¹-$ãfUÌÙÏ×Ð/5éUeÊ×eËØÿÚÏ~3÷‡Ì^UxÅ¥ËâýèÉT)¬ßkk™è´ßžGå’Ítƒí7Ñì"³ 8>g²F:ÊVøˆ“ÛÑÁæ8­1½C`¼d Ì7Â:M5òòr&Qf×шLG8b2©eçÕÕ?|&KZ¡û}ŒÈsª86æNm!à  ?`Ùþø8¹UQ‚%H˜Åu°S¨ÅoÁø¿FºÏk˜œ¬ªÆ»¯—sŠA¼‡üêŒ"Ù×K©´S9=Ø+.›\ÈYË—™3ƒü>võ‹”Qµcú¬M¨C»y˜æM_2#q'ÆØËçÒÚù¼x×KŽ_Å´ &tÛˆóšÁ½d3á¿ÞJï\‚ ö,òø†1³-³g$“)š•P}ëÆìp†|›WtëKJœ"ÃXÿƒQT.@˜Ú/[Ë-NÓ‹&üsš/“BQñ|p¶¡{gø“¦Äe¶(±ÿD”Ëžpë¥u ïÖ"¨Ù×C*¤~³‚˵Éã¨~¦¬Î€jH‰ae~)îw]O½—WãýýäÞÔ6v-g#u5½bÄ“ˆêÃx:Åõ©Ix„Õ¯JhêáuìºS-XúЊ Ï'ß÷Î…W ºmw©=”M—Ï8C½7Ÿ"›Ì·Ò—ŠËè/§Fôó¢–BV$?7O.L"³„æ‘+ÿÚûßÜoïtðœ=¯Õêã7à Š2þuHÁ7“{kЉôëÌÎ~Ão̹Œ0‚&»õXSÉ!¥<ÃîXT>LÈÓû³|@èí)ܸñáùÕ‹þë“0Šð–d&Òç³hþ{ˆÕ•ÇÝ/ýðîöL”µ| úwç’Þåìî¾If*uÓQð(éÜ"HȾøI>ò•‘°nÁªI¦(¶*Îmƒ±åÓÈ›#V ²O®ñ#G±¦ÏícʧAÝŒ0fËTj¤cBKD·±¿žG¡Ÿ-³Ã뎎¨âY>>4¾”÷g½á|z¿.!·#9¦Ë%)}~ÄßL‡~Í&ãº2fW×$•=âtãÉZv`ž]ñ@\Iäп%¸vÊk¶µ6–Í®·ÁÍ;qM¹ñNL~ûÖo‚ï\S¶5eœµéÜÚè,i¶Ôþ.§ï²ïâ4g3zÔ°7zAûVxý°‹>Î&ùA! ðQ€ì˜>]º?ÂW»xâokL…YI:°VÅ­³ðß¿8”Ö„×!jaëB͵-°ÔM 'Ö꤄ ¼tëûH…ñ räðØX}~µs*H¬t`/M ¤ññºp*9 #oó’Ô÷'éê5wкx6<¼°oÛí 7«z0*é£$ü>¦‡Â²hœìK†ÎF̾ Áo×V樱)[#Iï ‰*b”çúù€§’µ_³ÖiM(³Ÿƒ=FŒòþ³Œß”øí4•¥ì£ù´Cíët°( bÚfœe6¶‚BÏ‹›ìmÎK§TKÜß×Ã?·"ÏvivK^gmû‹¶/ëÑ÷U#Ç£KÏÞ²Åègãlzœ‘ÿ„­þ#úžÁ5ÀV~ ]ëØ“=½hșݒ†à²Æ|‰ÍËlÚȯY7oCáéÇp[Í:vïˇåñGäIçºÚTîGëÏÜE²#ú¾ Î/*hÛAºIºÛ$q<Ü —¿p‚Gfp¦kJS`gWGé‹1ù|½5¬ÍhÓ‘F`îÖŠÂ3ôDF?U\{«»eÈó8x,“AœªÑÈž;ÒÕ€‹ŸÒ+zºdõÙKpãZ0)X}j=³'W®x‘NÙ`Ýu[çÏ ÉÛgÆéÚ4`Û:Z€Þ–d[ºY"D®~þË$•Á›SóéüßËP]¬Wý%;æç0ÍËê±RëczM—lßùšV9Ÿ-L äE/Q$áüa4Æ"—¤3qS¹(¥”ËâØø#)8]ñ0‰ó2åÆéPuY5¶÷¼5²M ;²U˜«Ä wlW o’Éú¨0Z¡oÂöBSÃ9K Õé’çn ®œJúpÈÓ³‰úE× ·k¬O>¢›z¤7£žÓRúè?”3ö Ó@®.yÛ;ýÙÂ×é4þv žRH'޹åÄ @HìnÃòsˆvÑô°_6é zÏš¾5¤ê¯`cþvRru yºvMk»È®>x¶j P•¬¦É×0<ñ‚u¤KÀD/„ùܨðwK²ë÷ ŽoÕü˜['¹‡ù‹#ôÍô©œ` /L÷¡‘´(IIZÊ´lÒ$özóÈm-rõg>ë•¥ƒ7="`Ý+#¢rU†Ê<ô¤7sËá\eü:·ì·„‘¢4wΚàÏìÌñP¨îýмb èÔ <`Xòƒ‘^ö’ÊȼBƒ£‹à¢zÞµ+Ǩ‘›ð¯Å„¾|Ý…lâ¥kpéŠV|Z«†<«ð\ûXzí·ƒŒ8À|9ĵ;:ˆÐWb¼Dò¸©,§†÷#ѹO‘hª.†K'J™®\5ËáSÈ€Ã-|hÏù½®Ík¢÷š)¤öR%»Bt>Ù–…¬Îñ½$âŸ4þç‹–Í} ÏTèø¶8öÚ {¦°I—3¡dŒÿùˆ¯)¹ã®\–¹¯¿"ƒ±Š' ^‰^Äÿ¼Æµ'càmç¦5üëUº#†Û€+_Ë$|_‡b†Ø¼ºY„{$®·.e§9˜Íd1e.½@§AçŽ[¸ßø¶ˆj[v„†b]‹<>X§Gr8äe|(YÅ0oª¶;CÃà Ö4:‚ˆ¶­'?7vƒÀbBÈM§Šg®bnÃ0ÜÓZFßžùƒŽö¯9i›±Aû5´kg¡Ca-ÐìÊ’$¼ø@€½ôÈæ}SÂ%5éÞ#¢¤[[•ö^;‹cª p­ï2Þ>ÎM«e2¬Ùl1·õÎTÝrlè€7ʼn¯¤0‘Ô»ÈÀ@ŇFÄVµùºS©G¶£)£LŽÄï„EÙÏXÙ­bÔ<Æ‚ŽHÈâ³ ‘9Œ!»¥é¹ŒtU#øVƒÏ)!t¯¸Åsâ‘¿:ÿàÀíîßΦ¤Ä“>бzÌ´Ãy˜¸uÜnð‘;W—sê^EAįCà2ZŒîÁ†>%ÒÌnƒ^Íe<d@³_Wbª*I}j·s°R°ön7÷½Úk\X’Ò[DˆhýEx–}•Æ¾àØ¶<î§"Ôéá#j‹°Qœ n8êN÷ûYv(ŠnG¼TÙÌÚ­JE1±©tûaØž= MËiâòHîAÃS¨¿:Ímn1¹n.äöJ}úzÅb<-!@Vï›sUÀ˜%3[º–ᩊDvÞNB =Î\øŒ…šØõ,˜…E°óßÁá³áã@ÎÏL‡¬™Rè4Íž¬ÛͬM»Y?3¶s ñéSEÔ×2ÅüOql… èbÿ^Z'vF£JV8TºÌ¤mgàÍJ3àÈA{󘶗‹[‡¸þ„ã2-)ÚÌ}óÖNV·֎àfí€Så­h¤£=Ä çJüa—…œAÍ©‚dÇ_úΨ[÷¿‡¤ãú¸02&òÑnÑ›ÜåâoØóf9ø‚q.êQ=ã¨k,G:æÝqO´š~޹Õù°´w'ý¨kX<û"ÌŽýÝŠôék Rd5“îÔ›k…ø8º¾‚ôÍÎçXv‘ÎX—‰2Ç¢A~aT­b~ëÎ$-9—ñ`‡8ºyòl6e¦¯þÅv³Ndš).î¹£L­Nq@3ú.Tq!é6U{îŠÍA8“©Äu<–¸oßrâý¹•ÍUe±fvUÞwz²‘ß"äĉtx©ØÏ(ÛÚÐ]nShqçuð x÷ságXóA°•yÕqÃøçQ¹â·8§VæU$Avª$-U‹qãÄuf(6;m¿Ÿ‡ÓU—aQË,Ù K·üfÐæõndö 楢 ÇÖ_€Š¬2¦ÜýO@ìX .2ýt4ä~ǹ—ŸCϽX¼V°ú½Î¢„Š)ÌÒmyo&öÃR¦-æ5œlâ.öúGÉ9)!zvÑ;¼y-”=¶£ƒÝéeÄ®‘$F»ô) — }Ûxéu÷ã@}{àg&òóR6iç‘»8½<ÚŽ*j³»¸SûŠ¡]™O»‹¡ZZ ¸{üq“ùtX’{Ö‰œG£A[®±¤!9h\Ãæ-å¥c)8MöÚ«SõÌ:Ü8rößY‹‡ºÇ8 ÊYŸOÎx¸´›ñŒ ኟì3…_Á¼L/,U&ñÒ—¸s"OÑŸ7|½O`Ac·ÚY‰úç—`Íê¸Ç޶}óàáOt®l‚Q®3±q08~ ZÊ2G¿Bà29útë.zÔÿ*\BÿzyœûÄÒ"êB̸Åî9ø÷ù#ŽÿÚŠWt0¯;‚À^\¾Vo… ߤéVý¥ô?ò}…¥Ðpw#ó(.§ÝÖ¡†êt±†"ò è ¨ÇbP»î>X-es£UÙGÅâœK›Ü‘¹ô…Ä[ÆvàzÎ:¦ˆÑ»†`4…=_ÿÁ Ã’d¨Ö{~€W­ û´IÖ¦Æ+“!³Ù‰}gü $3báõ PQ$v5Ùì ‘$$,-À¯²äe’2iØè‚C¯ãGÍåxaâ/Ã?K”<ÚÊn¹6Æê?à[¹Tð®4Ùò…°Óîüb´®µ5ìÄG0PÆF›kÒÝš–N󯱱Ó5鞺øƒ +×"ÓÒÙk:1’ÿ÷‚”±põÂm¶UlÅ@0çÙ“õljV*%Î AÇ+ .ËýꚈV“ù½¦i9Þçõ…¤|•ÅSÜØoû¤ðËd¯·Ó?Ëæ¹JA|j9«3°›˜ø²¢¹ŸŒ³CWN³ãoý µMž>ñ˜Ä(û–)a{Ýe_<›klȆ7bäÏ÷2PY~¦ií¦ßuè¯[­PÝ|~†~bÏØVC‘ý>ÍtfëOåÀƒ¸%L»W®y BýÏŒÖ/ýw·0A½]ø«™¿$æúæ"¬îŸOƇ‡ÁîDjöV {À§ÀÉžÿ”È.Â6û:ˆ›¶ÊšôrU:çD…êOpè2ܲ¾•£cn@·ÎVd _Þg>‰]…mÖ;È›t ¬Ø¸Œ^[^Íyøµ”ÍjYO•S¥@?h½ë[XÆvÂ4ŠÉc ±ÆŽÎh™Dפ'­<ûP€n5ý…¼ß^³üIúX'ñ¾Aøú„$-Ý%Õ>±èúX D÷Æ Òà fgS(è÷烀÷Y±#ľ3›ý倓l:YuØ·B¸Á‚pÔ¼Û³#'s|:l(ŸEd+Têýâd–Í#ê¯Ð(á,›âUƬú1­ƒ›Ù5A7ñ¾¦#6®V!½ư¡Þ/OǫסKÆ4è9÷­3BÐ}ùqÔÔ¶†ÀÆ+œ9[lðçÌS°46¿O³ÅàªFÆæ½ ãwï =ûûVðÒØ5sQuýJ(þþÓ8ÿ͆Uù?õáâ´C˜À?—ýø%<óJ…Dø‹\/ºñ‹3Ý¡8p$Š‚íÑÀq?ª VN¯ É|?ð†û§à2÷ÚN3Ú]rJÖašìæëÝ4¸V,ÅÈÉÌÅ•ShÏ,]º'.€³¡8’I›™À& K°v3[ñhÐ Ö°jÙ–áÈòIháßZp]u¿-ªe^Ì×£ÙÙªlÙ·~ðÚÆ‹lÏV ߣ‚/‘áøU°R†ÏØ»›ÕɹiõÌ#“h¸¢G8áÄïPèÿ³€f¸eÀ2ŸhÂ%¸®,Ö½#¬k&¹¢,I¢ôÀ½„FP{º ¸•ŠÔ Ý eÀ8a¼Žæuç ðÖ{h™»WÁsÙ‚öXV±Æ‘Z‰Ï¥‰|×QÝû.ÙHx¿eCƒI¹Î?æâj]¦Æò#>f M^9Øs"ùØîã'òQ=8öñ̦ïZôÁ^qú|Þ~oP!êK—À‘²ÓÐ\ÝOÙó)Âôð¸9ÙöîáóØ‚Vòáä+x»?"ç˜4½>s:Ȍ݀ÉW4Õ Â'ç‡q÷ñl¨ÕÿKÚ˱@"Šã³& *Ö B¦?û¼Ä ×ýåÁ:òh»ÂfÀpcö_.EEesø× ×Ù!®ù3ú:° ®ž> [å9‹‡¢!qÛ2ænlÈZ¦YFÆû ±â8,Âü)”¢EÂiØT]ʉÒ?ÅÕŽDË“†&ä±ÍE(X".aR Q«§²¸ÆŸÚÒ¯(—åÆò¸žaü¢ðRÁ6XvqX*/€ÛX¾Å„^UCÓ›±K¬–9Æ¿ ®s qÏÂÖ©ÂM!ðNìEÓ=†uû!üç™æZ<] Oüµ6„Õ(‘­ƒâT+|”Í_cO [³ÀèÎ{t÷R²Øð0ÿ‚ñéÑŤ ®ø=…€ö^Xµk*ïÖd£Fàrø&æ–<]rÙ5Ç.á´(äÛÛ@¢înDÇÐ猻d˜mÑ„÷½ÓqûƒP¦Ö3?Ym…˜eTÆ*™®¼ºzBôýeYz¡´-â¬Ùs޳¤çLÍyÎTDAv•.Lää³ï£ä™í›ß2M¹¯à\A¸Êïâ\=z‘›úægÁÌ?°_B}…N›ýÎ(*Ф…šŒÓ«ÛÌN©(Àj¶.ù(«0þ òïì`ÿÍæÃ_šóÉ:*Å~xŒ‰‚Ù×B©Zú\ó´CŸŸÚÔbå|šßEÇËe©³¢8jë=ÃBE-j·_G&Ð?‰Òàüª—Uÿƒ•w¡w÷^|õãj]gxºçQ ]x40”èä¿„¹[…éÔsè¾]ìÔá ÖSõ.þh6'éq¬]Y0h\K©“龯҆=xu÷˜—?Ù£B¼fÔ‘\¬ükF•¬~£ðûsÄ?8&©% Eº$Ù!û¼*Ѻ¬ËtáŸ,X»=œ¤ºêÓùqj÷¾/S!âþÇ–.à£üÒ…ðæb£öÖ„\?½F8³A”Êå¨Ðá¿éøËÊ÷E@#LþnÂaÿ7ø÷‹ fÝX v»çàÞ”àZˆ`…›£¹–1€±!tòwØñiå8[z‘(̇¹x˜µS„êƒÖñÏÐ’'‹£í·rv.s§2Äúg,X% î/¡ØàÜ<[Ç|vGµ»Èq^†ø’™¸­‚änÁ7¦ë,>9;SiÒÞhgä«KšA?²v.È…8McìÍW ã¦zô—ìˆÁ§¶ÒTÐNÄo1ßU°®Ö’5ŒÓ"]£e¨¾ÀŸ–t¦áxÉ*ÿž—ÌRWfyÜLˆ×"eêù®øì¤¼¯ÔéýÛ†Ä{©1Išg:Qräwº2]›6€§“}Œ²]q¸ö’6‰q‰$[Š/ã½é:LyÞiô2÷AÿûŸš} 'Â&Fœ€û±Ž„­q!—Þ· µûÍ™ºP ø&¬LOœ<Ò9¾tþAÂÚy`|õGŸ¹Œ tB!Á*xp«ìC5ØAÛ º¯•xËÏËáîÇ“ YP`EF:Y¬íL¡kgúi§ È}“kT¤?G­†™iç6RÛ…hýø0üp ‚ä©×aÐÖ”ä*_¤­5¶Ô4ʉ^ÝEÞ¿aì%ƒé7ÎrÒª¾Dí^aÊø\œ£™O¯èFkåÖ S€¬’ýÀÎîCà¹~‚*SuyjÞ\NÇÒÃÞQ¤â@3ø\oeb: 1„Ûês’C¸(kžÀÌ{<ÉjO¦ÐåÑäÀ]%Z&r/¿&ç: qûO,¨E'ôáÞ¬tX©H–žã%âáIl«× ZpA>ÞzˆâsŒgðÉ‘2\ÕoDà ÕÙÜŸFÿ·yÛêóìí™0ïœ#¼²YÊJV^Þã°nµ?ÐUåÌfI`%öày‹û ïá™Ä)?ö§ãÝ=JôºlÙdíNa›íÈq÷XôË…ãÊ ð¤ç¸âÓÄ”,÷"¿Z3ït¥{îW:•‹õfl1»W]šÈ¦s_}fzïG’ã áÖ´lÜÊ ¦Ÿ3³5' û×35/’ÐJ­ñ›Æ’w¶'ˆø†?v,Y$AŠ›oÂáYÛ‰ýß8¼% I͇ÿAñ·0Æ×² !"o7T9ý‚#ëD ¯Ãù¶n«×ˆ[àáZ5øÏ+}qwû¥â5x¶i““ ÌÖ®¯à¾I›VŒa²w|ü£…¹¼*`:’ 'ÕÚa" z7>‚eó«àÒ“Íqb­ÖD3û·½C¾5Òä~½/Ç”±™÷;¸«<øÇš§¦1"†Û1ç.´áwóÀ\[§Ljìg3¹¸~‘F²¶·L cÁ_§¾þÁž¨œAØë˜\ñ,ú&ÐÿѼxþ4èæí€íµ¶œÑNùáßdé\Ý4´_‘£ZÌO3'ºp<«²–±µ}Ìs…œ›À5ofOw†Ã?2àÅ‹“4:|=^)~ öúÀÀPSó•LÉâ·ì[iòpAtÔ}g¨C´Tï"õ¼¿¤¦@ãùøcò’ð¾…Ö†wß Áe÷)tHëâèùÌ¥r*~à_­…ÿdº°Äñ îÙ»µ¤Èµj Xc°ž6mà‚_Ó׿ê)`Ûñ:Sº½ þ\º€úŸ¼Ë<0ë8ÄJüE ñÖæY0»õ7p¿Ë‘ŸÙ–Ûû±Hø S¯ÐŽ>ß6ZzüZŠÚµlq<ë {žÄ²Ẅrf û§”àuÉç캮CÔ³ä).¯Í‚Îû(à&ˆ6ÙÀ¡ÂÞZÅ–Xžä¼Nµƒ™¶3iZØzSÐ ­e“ôî|<¤ÑÊ®øg;ù6°©^-ÿ7£9¼E‘ä,žƒq*ï˜X}9 Ñטíí+i@¼&»_ý,ÃÙ4ªŠ›AòËsÂ]ž¥µÂÆôí(‡¢hþë7”jm†ÏL á–™óÄ ž¬¼Œ¦ÇÄi<Ï*˜àõÍW4{¦N r§rà]¡35ó˜ Þ\ÜS/M¯ˆ «©=RU +€ÄÈÔéŒ?ê\D°\¸“t)Ȳ>ÿ†y‡:ѦUßÑc`Û„jÛÓ‰}<La’ m½ðNàš˜alÆNÔŒÖcåæóÏv¬ç…>ê›KÃ4«ÙSµ²têï´\ö·(V0úú èÜ wð÷ÜJ쑼 –GnÁ·§O`–Ýtòë‹ÉUˆãåøæÃ,‚£Yxê JÏ¡¿} •ÝÌpGŸÃìs®lïÕ5dñÞ×Ì«¶í„ð`Ú¹q?—w« Øÿ±kè=kÂܲšM'ßsgíP'W68³J…ñÌí…’¨˜1Ÿ¬/¥£O¤©ÅÎp¨§š“U& nǺ`*þ7Ûo7Þ‚ß=²p¼¾æ¼ÜËfÝ ¦Û؃0=»ÕÌÏÃÖ4ào4'B¿,'¾Ã`a ˜ÇAºo?©ÿ<ÔÛ¹Rj\ÈÚýt¬£©³‰~±.Çò@)šìµ„àÎ¥X(›oöØþßw®}úÇÝî\ÇÁ‘¥0l‡áaÇ8;ÖCý‰Tyt¯Þ\Bûv_Aè„Ç·é–/Žqˆé?u²ëS–ã9è cx…Iò´Qx4a1 NÍï;¼lèŸ,ŽFcrí†âDyZtà \Q¨ü° 5µc`VZ¶=¬cågχ¬¶%ø­B™zö°åϸxZ˜ËN­ïƒãß³²Šž¬ÁñAôZø¿Ÿ2Ûá+[÷`³qd=«?t þ}Ò&OýOQ½#úèP3¨¦|hphÒƒÀ½…Ø¡ú WñŸcN-ÚH˜¥0 øûÆähzW%s ZNg‡Qߺ"l+˜KZ÷ ¡@erÑK†é6ÇãËÍØŠ ŽôÅñ' ´T¿ŠHЫóMQ¨w*$oØŽ|¦\«}íAyw4=¨ESÒ^€i!éìÚLv;¬…¬Zñ G Ó@²vš®º(K>%£K–)zXarLSQÕe=^(iÅ™!ÙŒ—þøÑœÂóËóñmB¦&¯%K¦Ú“Ûr‰¹Ùx¸cüUyƒ-uhöª ܾ^‘l_"êc…±;ï£üæDvŽì|}þ†xm‰Ìeç8Áþç›ñü½¸åÜRfè¨üäAªn?`³¤Á^j½7AÌkÌ*gÀúà_lìÂM%aÑÕ<Œ÷ðCÇ¿Œô/aÂã"F#Ò|˜;ÕUXþIÿû<%ü§ë”çàL‡)˜˜?+c.².g.àú()\•"Œ]ŠÓˆÑ\CøaáÏý `€få‰ê2ýÀ&(q"Z-yè2ý¬RO§ èZýLœ'ÒUïѨ.ŒÚY »¿ûÀ†9¾äßßJÊ_/Cvê¾f?vtS ºHí!^V2§{Z.@Å÷2:É7ùm¡ŸÏÞÆþ±^nþÝ\¶~è:“©à!õ”_õ'*§dДÎO°ay.¤·(#ï3X0#’m|Iüe¦P›»{È£‚jFûE&-ò§Oz~'/møSµØúÓKh~ {¼H»çìbFÆtH¶Ë7œ{“e¯ÌÏÄðúm™.M•˜GÚÌÌhO ?2)+Ⱦ‚”ÿS1&ÔÁB_%HP¶%e¢¢TôGgîÿù›í:|P$;—èá§Ù®x¦ú!,0÷‚'¾2 }Clö΄…muøãz.8®à™GÚäÄCÒ·Ùúݜ“u[Ó¡~¯-ÜiŽ3 aÒ±.…† ¨d±—ì!ÑÕ0å ­®à#3jÜÉ”â•4gÎ,6õ›j-þÉh|Ý"›½Y‚x*qT&Ãí">ÒWŽñIk°j’#¬Ý8‹ò{vãE¿[Ôƒòu ˆ]ƒ7÷……½ëišu‘ˆ†Ô]¢X±[‹äã[McRø×–u/Ð"³7‹Ó–é»ðùÏJˆœnKHn/«]ûžÙ>Õ¬ˆäÇÚP=²oTv”i’µR;hÍÙ²xí¢àx ÷*Orl£ìŒ†s/Ò‰Yá ˆ s¢¹} ”·:˜h y£ì(zÊGr›"¿aÜÙs å¤k„¨WwÓðA‹èN;ŒÍ»^q•sãqÁBÑÆ÷Ö ø1éû™÷335!Ÿ´©ð‘¶Ø—ôæb^ºð”|_¨"åiâ¬hê¿é ìÌ¢ûS‘‡/¶õCµ“ýë8¹øM¤ñ¾0¹²·›®Ô[n{Â*Í-¦³V¾Äƒ±õpcR_›œŒFUV’Ü©Š%«~Èw/OTß±˜hì"ëTsAÉA‡–—Å8;8„Þ¦çÙŸ‹ÿ‘.¯ëÜVA«]y*ä¢Þr‘¼Y$ &ŽÁ4hø5ùÑx„,z¸ë+¾`{q´8`÷üjs‹šN_L½ûâ0ZS 2¿PQ5yzÀ!‹ ðF:K4uhÉå0¢g9©££Ó‰£è¼úã=šãM¿Ç”‘Å…¤õ1Ö´¢‹îiÑoޟ턟ƒG™›I§é÷Ž\rÍl=1ÍÌÄyoìØf5x^ŽŸöo^…·P²p)yõKµ1ñÎzÜv ~MU¯m 'ê72§_â‘]|Ô¾ù#7CC·ƒ'M}ÊüðŠB£[‹¡ît2ôˆLFVn!©Ô…ü[$C\¢I´ÎQX04•ÕB4.]£¯¼û—e¨ýç çßi-zôá‹ïBtŸÖÒ«dMúº èÀÅ@|Ñ(A¶Æ3K^²ýütbtV¬ü²r- Y²‹5o1u¢=ñ 2²ÈÏN;ú"iÑÓ•‚54rÑJŽŠÿ)’PxŒn¨Óbï¥ì$&ŠZ-xÂJÿŠ.$ùÍ»ðSËvÿE3ÂôÃ2zå!†¿l;è)Ƴ߃ÅhwØ3 ßùö¶x°<þÔ•ú}I‚¢Á(Xùœ‘pµce÷-¤äò<ýÔ9 ©ÖrQníßX13FC¿£êí̇‡Oøiá¯TùnNvžG>8&À”ó åøa¢^Ô€û|çr¾t”ƒÈž_ܧùàj>‹Zbd|!*AðÏKòæÙ|3 æž5‰eN9×â)£fhÝ~´¥à-‡‡Ä¿Ëö•'a~W.ó²ËŽø9}ÆO_Þ²Šû“SÌFhïR~Ë—¹Ÿ¤N¤ÎÿdvT`¤l<<ÚjO-|¥QÔà'Þüñƒõžñ=ütéÆÅøø¯'L¿•g³…Mo8åO°ÁéylÂTp½,¿£'kb,& –3ë0Øð2>º›ŽWZ†×["°¡C—9$°î+vCÁ<'39ò¼´þ4ë¤U¼4š§»ä¨~x#úùÞacl:ÐÑç.7:ÂŒG¿«O:¹œ¶ÿhÁå3ìÑÊYìI;e†«Ø¢t'Ør2s‡VBXq<—å~TYD×Ù+YoÅéš B40ÜžøücÎ/ðdå´˜¹³6àÃ>pÃN QA%?ºå¼ ÜÞ»ŽMÝx+yÕ©¡…³÷]ñ÷-#¨bÈ©‹tžVž]#‹6›Âä:µ<•HxÞšÛÓ Œ\Ÿ>Ù?Oœ…è]e²çP!¦=¾IgUG5I¨h¦Š½Ãg™(«¹ä\ü\òEÈI=%P/7G9í8Gw¯O¬£[N‚Äçðì¼? ºDù*ºzªÂ¾ã:mês=®D4¢¦ït>/E$-Eaí-–qà>ÕÚHÿÌÒ"ßÖVÉo8–OGïMƒJÞ<˜½ó7›?@Ã^D…KØßýËnÃŒ #:¡¿x’Ûª°»$²âKÈ–òvèv¤N_á¯÷2²þ¶8ýZŸŽÁ'ùé÷í³‘÷i ié/ƒmÖº´1Îj>’"_>·Ã¥µ;°½kÕïn‡ 5Ä-G»™°å±Ôyð z×n'ÁxÞP<²=±Ø¶ó$ÛôKDЬ,¦\‚Ÿ‹ ɳk²ûR2.?ôÁÙ}ž*HÂ¥Á™¨2üΧ°¯Ây`Êp ×,w;s¹k#;ã a|E/¢ts0¦òpBP­D…IsT(äSäê¥A¢Ô|rp="È|æ¦âát=Æôüà  ˜¼ {¦Ã]ÞÑ ×Šåé릻œacú¢øÌ¿;묹F]Ÿ±]¸Î[Žá‚ò;ØÒÕÊoÄ !ðÚrÖú[C¶0/ ‹pñ­<à_-IïÌ¡ÚÍ^xpÍ:|sùæì†ö •D±p5¬Ú Š©#™Ë.AÁÚ.öà»Æ:þúˆÐåŠJàa?ÁJzþÃö›¼øw¦{GИÌÿQïq~Õ¿eª´3¼Þ= æòlãLÚ_7ni˜GOS'‡öÛМ¶5dÓÓ/ ÐÀГUJ(0T ©‰õna*ÛG_s²ówp|]÷P…äʨ`åÇ8FHa²¾Ð\X,îMǬ²pÏ+Èü5 ’Mðõq3.ŒŽ€çM× nŒ… ?ºg}¶<Çî”4ÎXŠ ±|Éüv„ñΔg§:†—겯ŠHÛªøvÅef•Œ »Ty=ðüAîWð‹›0þ’5EÞ{óÀüÙoŽQ9- ÔÃu^P$‚¹ü3>u.qX¦ÎØ])Feé6|¿ñ+tV18Qãcõ˜#öK<ŒÊæt,Ò fôZH”ÿcÉF°N¯18K–}?CŸäžB¡bü—§§»â=f+ö6s—=üÆÌ4îÅ™ÑTXÏf<á#ç&ã©1³æ+ª OClw0O}Z±³w€;¯À”J ?·Ptô¡îRvY%~ß&‚³òʘœÒA8Clɬœë vF‚#0>kÙ6ÔË4oLeJ:^qÃÍ=ñÿáϸ˜Ã,Œëge\^'öÜ¿+/–ÀþipÜT#Ï:™l¨åÑF¹‚D&“ÿ#Ç»ôÖøÏÅWSþaë—ØZ“ÈÝΚò9s§÷lªweèã#L׿X‹Á?8â»G¹I·™¬”EÔm?¹Õ6©õÂUו¤Aþh´.»9ö¬^P¨k?a8u•°ÎÌ€Ó×ò”y¤xÓ’1-Ã^›C$«´ yÂÙ û–`2ç ÷*ÜwÇÍ ŠÿÃO]뙃æWØçß.[.] Ìó‰%»Ø¨Ð ¨Ü!G}VòÃõA:u Àó÷~„ßµ›õ}3Ó ~€ùiتÏö"äL’ VvCÉž›lÚÁ‹ðo½;îÚ bŽ4xvnÓ|Êtw©cÏ>òöÔU<óø"Û2_‡–ð/+ph¬_ê‚ìÆ*ÔÆIÞ ½\Õÿ‹SkFˆtïÜÀºwJ““õJ0¡œL\“'Átw,Å{®Jà¤ó§a|¨ŽýÞdŽ!«£±ÅVÖYIUûrP÷ª @}.÷GãCp/yÉôÊmø_ü§û0á‡ÕqM£}é™·×.DËH6ª¼‡>¾ÅÌ9S°?%§NÂí÷ÎUЦìýX3Xpˈèç0Bš·™±ŠB,ÈþÇf¼>UårøõÖ~MÏä(<ý‚!žF£¸+ã.£!§GÂÖqÿÈFÐÞU÷™´¬}¸±$ ž¾üärTi þÞž # 'V„8«_cË5´id<,Û@Ÿyƒm˜5çÛ@*û®eu–‘±±§X#*a“\Ä´¢V˜ÂÊUìôWøq,g™,¥ F³é¡´yxcI#pâ°|÷øÇ'uÓ1wAÈ,U"26ßÙÓ$žû Mã“9Þƒwã§‘#g^1W-hpw)c!)óO-Ä€ %М7æÖ¸0}ôéxkìJ ‚©eŽuB¿Ù½ô)DeµbÑ"¨¼ŸÆš‚´äñL—}>ï%,ª’¤ò#FlY{2|õpe}cœ¨å*‰ÊÓ ¶-í„+ñœWw °*7Šž:–9ùý,1’‡ßèM"S¢Q} ½ón*ç3[#µØ+«eéaV Ó<çOa;tNŸzYCë2kF#%?v¦"¾Êf7¾4ú_üësŸ8Ô0Ûùóþ„lÆ–ó^U<„VÛ`æ`UJ,žiKÃêˆ3¸TG‰º¹:âþXCø$: «™b Aê8%í+ž_„‹¥.qƒêãX½šä³±.q:¾¸0†O2†ÚóAy—.Pgp§õ1Ü­¡BRxîs*nëâêhUœW ?öÌ$µ|/9‡×¬„'3A½,¦{´0š!È\݇ï}@ÈÇP‘DËREõÂqXúÅöE&ã2n%Ê›.üßõ—µž‹ ôýq[·*îpÖ†{ebˆÖ,3oæ°ÊçNÁÕ¤Fv™þÕÿ[‹œ3À¶õVá“ }SÃÉŒeg¼çrߦâ¨SpfUd -!ñgaáTæ4·¯ÕQöÿ ¬«n`ëàÏÖdìôiÇÇVöÜÊpAU,…æ ˜Î^Žp‚:©ç`æÄ&w·]ÁŒà>&ñÛœ!šÃ4IÁFžPÎXc¾IB1¡44õz±âó˜Q¹ (ß¿ÞxŒá¶Ä!V(C–†jƒè7^¬˜[ ¯ã1ÏáwŇ9-Ì })Óÿ‡WŠZWþaY¡«øCÚ'F—‘Û9äèþå¨úí»TBÔ™¸s]³fB²ÆÎíåÓItÍ*ø2Wž (o:å‡Ú§Ñ­ ²¬Œ Þçå¡ÌK!ºB‰ŒñÜ€ûWœhÎ러ܬ\85ž’®)¬Yáîú;›lS 7 ¶3–òv4°MˆÔn¿ÏÎQ_ŠF‡pßoZAÒL ù©Ci,?5: I¸J¹†WZ¡C=e–H`ÊÞm¬»ÅÜÐÀ2ïfû@™8øs™P¿¢½´5M e4að–EzÁ¥WÓ¨®ä+?;ŽÖ×¶1bx[U/ôGÂÅL~LÙvÉ2ñ“Å&™ÉcÎ ÁÆnçkÁï|[±LÁŒ¼ë)âF]@¯ˆ2Ìùn B¶˜!,ν$¾‡óÿðWÕf'ÃY~ù!O_^Ê€Y{GÙýÑÉõ3笲Üû¢.(KB¥ˆýÚÆö-ùˆ©Y®4DËD7á³È… ªr¥T"°¨ç8y¹à½ÃxPÅü,™ Rþbùt_|yI™>°¥²y›ŠÄsVцïÉìãt9X~—؆^\åö A*ùŠ Æa3YÃ:`[# §>ÀÞè@Xû‹¨â•:#`%]Å\5rFº'÷¥?èÇ£—°Ñó(òzŸ2$éߌ2Hc™'OÌèôù<èýßýœÿ~É‘ìŠ"ŠŽ¡Bðum*§tW¼6Ï…Ÿú3a‘b7Yfœr*nå2÷]f [Í=+ãçN3§ðX1Tü¿²+ã¿q³wãýo:(´¼ƒÑ¾êƒg¿ÜbMü„IH@1.oêcЦ¾ë¶<˜÷‘°óDÖÌfBnŠ@\4»²ZœFl{B× !îÏ D´¥bŽî\Ü÷B[¯„&¯TYèÊCÊ`ì|w®5a롇`R0råhCþ,"'׌—>\`ˆ XÄ:˜Ñ[ÂyÀ×#GßøÍÑ+iCûU«Ù£Ù"0—3åø+ý²Ç®‹3þGö±FBé½TÜ{ˆy=t…5 ™¾øAföOÜ…/Ï¡`õT2P“Ê.Þ{Ž$ôrÿVe0Qž7ëÛŒ²€( ¿ ÁJilû“× ÓŒ¾óL™³fOe%g²yÂ.»yT4à «!SĦMê•ù2ÌÑH_Ø4®-Õ+1)ŠÃáùíEÖgÁ­^„¿ˆ‡Ì’sƒ‘D9úÜ2-ò!pPÿ5XU™£‹ù03ܨD*ø2ñ ¯'ù£E…VJ‘Ã*Wñ(¿Ùi¬Èùyê ,<¶ƒ<¸»í९,À(tTîøƒ÷œc/«¬ÀԨˬo9Ý1Ü r¼sØÙ¢»Hb¡$I¼ ÷ ô)ŇË%9-dèªû¨|¥œx‹ðÚ@ ±6 %ëã4è\ùKh³ø<ÅÍg!ËÿG¦Q‡¬»ÆCŒ&¦Áb³¶'zœÅ$ì°½ g­ç3"Öà ŠÏ * ½Êx*&>Gù0bes‰øÈ;¸¼T€d°á¸vÃl,ÿ¢OË7ÎdjvA•È0û>Ú6ì'ú32A×~9¹gÕSîPræÐjr¾±’­jB;UyÈæ†Ì}®Î|:hFWîûˆ[èH7Ðý~a‹d|¹.Uñéb£²W¡Þk¬#¯E¼ýB_t“T„)^ÑàvKŒ¦Æh‚r(¼-ÅÈbØ-B÷,ý„ÜwâTÅ¥œ½D—¬mp`únÁÀKLËqãv·ß`W÷ÀÇíδ¡Mšø¾ZLbgbkf=Æ …AäçÍÈg¹„ÜØ!sîƒ}ˆTL›K3 rxþ°ö*˜•þ‚›ÿdyòÓ´Ëî1Æ$¿Y™I)`í̦`ñŒã¯»]ÌÔ®rh«æ‘ù8ྊIÚr ùY;xs:ÖŸ\ )wá²?¢R­þ‡_ïð*<iÀ´žÇ úÎô»é0Ð e„ü Á|D…]fDt&¼É% ?ÖfÏgŒï£¡G82ƒIÔ’8ÖÑ^,í·Àüþ¥¨3dŽ+zò‰Üv=X&QÏ<3Œ Ý$hÒQ¬ŽûÀiË"¾G-)Œ‹Ò² Äv$#d²yë ˜µëO`ÎÑx<ij^p2Ý‹÷ß,µ>ÜtXžÎÐ’&U€úãöJ‰Þ/A¶^)bn¬—&¥[Ó°n¿9I\‡"-¸{ªˆ[á쑘v#h™ þÑ£˜U^ *ù¿qW4ýøÍýW æ8ûÌ ‚¿ˆÂÕG×Qãn%³¤ª€1ùRÅl6DòþvŸ0ƒqíòÙå šl}‚V7•ñï‰PèéÏ-ˆ~â;ìÛb„ϧ©bÌÏ:0>=©ýfÂw—ïèsªÕgfƒÔªX¿{=Í$áÔ'¨áËÈ:òbØŒ5ì”+¦ÔÏÆžö´#¿¯#õû –zÂtáÅ«²î³d×zÙ3 ùøjЊY8ê ‘™Aì’ÅA8\xˆ¾ÛuJâ¶Ú¸“Úùƒ))zWGˆã±Š4^ÍÒ¯ka¦Bfîòâ>oñûÿ?®‹³T³ÙiÓïaâþ9”÷]ul2§‰Ծߒí©Õ¦E"É¥¦Æã;¬%©ÑµCÔÒ(ís£ÈPý/4÷èÀÈßQ¤øØ8꼂ìùkˆmKÚ ÿOßu­ŒV“•·N‚wòzbs’ôl}ó=ÄÉ÷äÙ?v^"®NâÔ»d3;¯†á';ñÎÂÙt=V7ö¡ì–Ùt÷HJg¢Åηè•"z n)íòì%EÆÕ(>'Ü×ooFÕˆxÆW„ò£Ó~yq*±6Ž#Lj£—y–þrŠ#ÜýÕØìùø-R\ñáyF·„‡D}Æ UNÅm2ûpÇŽ8Ë3»S*Ðý¶ ˜‡À--QÉÜoò múæÒ.oT5U cÅðèÖKðνvuzAÛ÷Ë ÖÅü 5€ø ^ŒîÙ"à.Fí§1ÿǘ’ßA~ÞC¦äw=œKŸJž˜B«w=>Jm…–ùºôènH?)F¯ÎfÞ°nrºÆ¬n͆ûe¡ŸXÂòèÐkq[ζ[jÇ4l8ÏìÑ–‚Oú6¸xž(]6ðŠyógI8ùy½ ¶†ËPµ½, Ø…z3þ‡ÿ¾,&,”¡}CCŒ[ß ÆéÁ{öMÜJÀ$Ù8úyeŒ%ŸCÎ/s"46ÕÒ8L{6Ò†QCšmr˜ù¡øˆÝ÷ŠÏÒù»Ú!uã{lX k÷]aÀ((?Ä'omHŸµ uµ;“DZø,¡Â-/T§ò"¤L6¢a[($”áÖ¨tðÞ;Ì‘ ä§®ªAÌf/=œª–Œ¦aXÞ¿î†I¡Ìë•"Dþ™Y_û¹ìpš ƒxªªõ˜oé  çñ‘åç‰óaöÚð^Rqê΄M£½§Ñ°G«dþ—ÿFÃÑ8⟂’Jc˜©Dž¿cOnÅûÃ2w2oª{Ç8öJᕵ ~,1¥©þa› ñ¡¤¦X‚>ã9ŠÇíÜIkú>R°kÝÞðˆ}¬·*ââÆƒÌĬ|´ò†ÄìݬGÐmŒ¾¶º¯ùÈ>žJ‚[2@-÷2ìeÛÉ¿„*âÛ‚67CèûXDÄü`’”,½6.˜B,÷ópƒÍÁ;Ò“³Vž¼YIaaNÈÈ/Īã3àêú«˜–•ŒÃYš »L–¬ŒãØëG²e ÚxfÌ7íFóD^bÄ þŸþ3î¨`¯‰Ão†9žó®£æ£ŸÌºù_YÍsj$`]aéܷØ%²…B—3Ó3¦AãO ŽzÝ ÏµJØ9-:´î‡ìÚó_o|ƒ|©0'+ÎDð•«öPQ+ ‡V(SùˆétH! ×~‚”ôÅh?k]¥òløkÀ{”ö3OqVK4Ág|8wK/(p½W¬áÙ»ç5HáóÙ–éÇ^x b‘\õˆÀë\^h_OÕãù©·†4ýd—j5 üÍ cýZ U«îÁ™–÷ ·…ñCpÔý!Þsfµsÿ‡ÿnA4³TUùh¬J~LÌ£:£SÁü•>¶Ë!J?)3Ë€|´³åGû±v(1  ¹ÒjgÒØï à6/.pbý$Ð̓ùŒ®øZÜÙ¿V*=Å· ÐWT‚Îäy†™§ àè4ÜÄ_úvtË××ÀÇi‰5òÄí³7~a¾àò‰9ÿÒîw²Ó ÇìêëfÔ¡HžÌ’§R¢”óq?yÁ§As§*“Ôõ÷afÁjTšfBC=<éíEB$½ u›Á¡Iﺘ_| Ø-LÃy/ÁjeêÓW1ÿ˜ÚÙppAvÍœÇýøkãÄÑû‰^<2©ÁN—áºéÓ‰N™^¾„?oW`ê¡3À›B>DaJJ-LdðÍ®8jã{gÉÒiéÜâ!à{r›_ °ÂS‹`i+gý!„ü”¨é¹pص„Þîû…¶¤Ut1v)e“Ñä§l*-ÌèQ© ¸âk!÷YÇdMð½Wv-$ÒJ…¸Fiå”_†„ÞM¬¤ÂI²D&Ši÷ò† ]Cb¹Ï’0×f§T1¾=DJ_ÊãÝc± Ööˆ¹”ÔÆlνÌ.h¬·Pÿ•†ý'¸¿«áû3åÿåÿìÛ¸Š9^RÚTæš2©}¹†”æGBŒx¨Šuà‡ƒSÉAÛ;øRt+ <¯ÚM8³»ŒÈª]íÜ©r–XifJ_¬»'MEqùç=ðÖô7²³J„˜¿ñ†«'»~I³¶7˜\’²%Ïž.9c!4Øh¼%sˆÉ’åÔsõF2o¡ÞÝÊþHØÙ©xÃÍž\‰Î#tÏžj½…hÔ¶ Ü.ˆP_/IþjCþ}®€9¬IÞq‰ånÐ%k3È%9-:ßȃµ´÷ [•ºX‹L#,*xJŸ>_«Ldi¿©µ 7û_ý¿ò4 Lµá¹Ó}(ãz¼·ÙÑ“ýHWGò÷ÅB²R;œwéeŽ,jF_4óLmH1ä§mþ[1g†,éš} #–À–©gÑ+f.l÷º‰å]C‹ÿ.UCí 'qš ?>ÓØƒÏ¿ÀÉß¡hþV’ø[Æ]¹É6ÙˆQ×Ï7¹NÜš~xàž¥‡™¬FoöÀi}Î5͹p¥W˜¤zËâÛ?Qì™ùqjª‡±Í¢àÁCH‡W ì5à¼mAÐÛ¦†r>öì]'9™v ÞñeÀžEH›ˆÇÖáCm÷ÉçïðÇÄøü—oÕ1u>´¾°£7|oaò‹ø¯Þn_\ýÁì®ù²twæF²E@ŒÔo=‡y2DûŠ09ë‘)ÿ ùènv}Êvh™µ"fŒùv`:µ— µ\ˆ ñÿÃßÜ €½tHoT6¾~¹žì.ò…±€TäOø©¾@ož¹gùX5•>‹]Dãªãðzî$f+ˆ‹ùx³Ðž,òp`ЭúöÉÒÛ¦áÖrÜè$M÷ÆÆQ·×êt¶™Gæ¸;Œó›ŠáÆ–Hâyæ3^p%–÷É¥ç’VÄ"’s¤ø:íOýaqgÉÝ£ÒàÃÝHÜ ¢%á1twѨeK¥"9õÕdªýÈHÓ\êxm;µîÚÌáu߆vP·ÙHWÜÊ×Þ;Ìuu1b>3cõ_“Öã˜3e9dn¢>:spJýCüQûáñÿ÷j¤3˜[ §…ÌËì»>†V Ï"‡ Z(o³<‘gyèH´ )uúÆY×?…LÔ¥N34[Ù?]›Hׯ­ôX•5ÑŸzÌ‘¼Ž¾QË¨ÉØ%âžWŒ2Ç qÆùpbm¢E²üàÔ”ÃÙ#øoÖúméºð˜y§5˜y­$ÇÆøÿ„˜GéäñʽL[±+ËüØ ~ßϳúYÌWóz¼d¼î m¯Md¡u 4o‰žeä¨]p‡æ“•û‡Yµ€›8øå÷º÷˜/˜ M÷<™0W^Úp®„QéÙ­ `îjM4Ìo¢}ÿÍÿõ?‘úë̹'‹é¾r9â»–ÙQGÁ¼Á#MV½—Â¥Þ{aØü5,ïyͨ*?ÇWÆÍÜ«ÍD}ìX¿‘¦«¹ÇIôávÊ“J86É4ë4ú»½ ¼ã7r'j¯Ñøî&𻓠zv—1f¿ÉN²ƒr7R`Ž?cæÓ´?¿È&Ï#dž€$tþƧC½Œx³œfJkCKPêŸ)rÜHFÖì$†r2ôì~ Ûoà§`JGÿ²éó$èä91naM`z-’Ø%‚JrB¤+^M%!:7qÿyˆøeG“Ýë!;)yÛßýÿÅó Ã7›ûX²>˜~_N³…’´`ªÇ Ò™[fcïÝ—(Õ!As–‘“#íðy²\MÙR±dªÛê’ ÑïœW`õ©™¹å2·øà.Ô°zL¬B‘G¸*'ž@úÃZ‡æ>îÃ}i±x¨n]”–:Ÿ8p—ߊ,è½À}u¯—Ê*Ñ>iy®íåbHXr‚¤<ÍÁŒ¢tz膥ò´­ nƒ÷ÏxZ–L„w›ÓæiÛè¦È1 ¢õ†¨j¶‚nY{õ¿ýë­ÖåAhêv®žå7ŒØy†*§xáå;ÁäïɇXZ?ð¿úþN!ëÜq›[–>I|À¸ÃÜ8)U¹»ËšFæ÷‹„G•xbþ†Æé4¡03¹÷ŒN\‡ ä_UD¡h,•\Ê ØëÌj rW(†U-ÅK½O òÕ^Ì~ÉùU«HîM?Ùïà¬ÿf§uCnÍãÉ$¹-¹p¤Ñ¦|Ò¡£«BÁãi<.Hø¿ÝUIê]5*®ÏCÒ7“ŒÃjÀŸ³âuÀ¬ÇáPÍOöN†:;*¤£zçÁ¾y_ÍGrݽ¨fÉ-V´‡ÿ’ÙKÃê´ø_*::n#/~ýþßÿ—·ËbÄ)v".›‡ ÙüÞ¾fZÙ!`¹u›&UÚ™ ÿÖXr…ÒÒª§ð}{3J¶äÀhóà-äåöZÜ›¦E.¾ßĉ˜q¢~±*à;ç¿ìË.;ösÂ16ªÅ÷o¦­šo±a‰†—å°'þåáAýÓ´U–Þ™¬I£b¿qX-ª·Ö²-çÕ‰„º!™¨w¢VEØ?$F> fö¼´#qae¬øN ظBœÖFDÀ³XmÚWžÄQîÿ.;[pD¹‚×­'Ë={áî I™`U}ž±kCVcòFORâMwý/ÿÏf‹2sÅÔ o§+ä Br»¹u0 xLÀ=Ù˜®Ý7nž½†©KÑuIɇ{go‚bü]pQ5 6˜9÷Q&&¸1/q©Ý~"ºs€c[:„Óâ߀N‡D?Ò?«ŸPá ™µ‹ >Œý±YPÑr‡äo3åñÏàfê tÒ€Uð®ï FÐnXð[T¥†âŒ¨$·m…~@náF¬Rï€g§Ý˜2 ãÖ'|ñ: å²o°¹ñÒ´µc.}fcÙ÷$0æ‹">šÓ |¶3à’åJ:|û<«]Ÿ3%¥þÿ÷ÿ`ÎÔ«ì7Îã¡ÊŸpow7s"û}ªµÚ¾­¡w/È’}¿ŽBÝ…Sôî§AvbõTû¶†Üßâí2‚Ÿå«Àíà3â'ï@âfh±Ÿ¾Ãw¹16¼¹„ ô“ÙoºXI Gòÿ!ê»ÃrþþÿÛ{hhOQ”R´Ç}ž'¢A%Df!{…¬öÞiHÒTŠ©ûõ<(#%‘­B™YY!üz¯ë×ç®®ºï×yçõ\Çy=ïÇ]÷Tž —ª±y‘¬$mÕ±Z‹A6¢ìä!:¹÷‘“`W„SéŽ)ï Eo'ÔÃwP±NĮ‰ðöA)àñ;æ8 ÅI0xäÒz™åÑH^øjzÈ°Ž©YåðŽÍñ?ôV@Fá|PúíÀfL‰1£µðäç¥Ð|F›sÝÓ¹£³bx³oO%¨ŽbݘJ—¸º!óLâ„ù²Àk¤T~ÿO¼ÒžŠßÅÇR͇®ÀÕjÓ÷¯hÊ•ó¼ hòåj-Ü4C¦dȽ/‘Ý;ò齉…tŸÁM¼µ7ß“§3–ÓY»%©Â«A4Q¨Å»šcéçL3rñë~Ú2±¶ÚH`xÁ20IŠƒ¢‡é¦I˜~N€V‚Žä;`uú /sI6”'݇:ºgè9L­”bŠïç3ë‹+9ók1pc·½§>âÿ3+¡¢ë¬“Îb%êËæc\Ÿ þý5,ã9«ûs陕2hº}FDóØVµÕÉq†~¼\ ïã­è£«Pe“¬®ÇKº1;ü9–Él†ŸCÙà“Ý€ŠÁðlï ß ŒKmCtÔgîݽCh³A–>>ö¦¯‚û ñ°mƒ=®_ö•\îYˆö‘føñÜ-²9MÅ©ŠÓµ³?}'Lh”e{ç“ò©î˜dÁB—ÄÓ·A»aßp ¯MÁåSBc¿ -(%œQÔ8tq6ˆ<äðÎ%”9ÚGŽG³Ô µ|”\=â± Ì^scŒ²ñF”0œƒ¯õúˆZBÌ¿ZRÎŒÇ7b[Ž qö$€Q¯)›à`Ag¿R¦õ7Oïo3Ü¿î8vHÔpåh–Õ€ŽŠù~ ¾ËPψ(TÕñÄcµTU§ì.’ Ï–»Óë?@UÔÏF¹%“8óà|#ž nÓ].¥ùž^*GÓW[2»¯¢Í€Ý¿nC…íhîÔ@?oÖ…ñø/`ëqcµÄhå6 ֞܅VíƒxçËGœÓµ¹nkpÚä¿€¸ˆ|ÀƒÁ³@¦G’®àÏaBömäÿÛŸ›8™mJCý0)0]pk†kþžœB\žMÌ´×ÀÕç é>©ÕðZèlÎa¼Û)n˜´bÇQÄšÇ8ÿºw7¡È>q:m~/Ïü¼27ÅW—Û‰K…(K.„‹h48öB…_︬ñoò%x.iEghyÐ÷2zXô°»‚›¹Àæd(i.‚†ß´CÓHépÉZŒ†ÀŽhJû¦}ÃðÜ \PGåAÁé-îŽØ½ßþàŒŠ)•¢„ï§Ì‚ ·êQp‡8ýïï­Ç¯ã©à:ü,jJKàM?¦¿ùßó¯?©³ùWÃêˆïug6pCŒ.>2ù\žMwæyÁã™îÛÓØ¼j¨°i|l]UÀÆèñOR\#(ù.oa†F;f8þD@È(Zþ|1KY†þM+ðŒ@6Äj˜°ïæ“`\¿Q»ø-¿Æü/þ«Þ ·²æâÂG)¬Ýc&¹½i tvºãqk!æQ‰O6¬¥!=§IøË$¨Ô5C…Y‡ ÑÝÕ¸bö?'£ºW }<‚†vÿ¾†‰—dq‰ÁÛ|‘_lâˆî’‡09 -|>Œ›û–êÒ꺛ð\“Q‘SFðŸp€0ßJĬcFS“ƒ÷19(‹,l«$ñçCá–S ¾:¯ÖÏ‚`ÿ'ô4XI~äÔçlRc\Âå¶öàï|eì8Çž:¿jÒÞÂ$ychÂæã£)üa3 Ä™tò2ŒÿÛ@·à2)3š1FÝW™Æþ$ É>Lz:+^€åãÙ@R:ôþ Î/Paßj±5J+aÞc?N]g4—ÒPAÔýû oÈdRGQûRÈÒa¼I÷ yÔQþ¿ÙiÜ·ý+—0JÖ¨{Ñ[^)\é:eÚ÷¤/ˆÇm/„¨Õ§øû랆ñ+ÿ~,\ØE¶™‚³“vâ[‡ß8í4ø}ŒâFé†ð§ ü™'AéW9œ`Çjlƒòª1Ì>ø.É×€cUùtå®\üà‚%AO9¿ñ*lB~'oÊFexr­…‰‰õú¡cc!ÿȬHXšwñÖŠ(ÓIE Ó7AóÆÝøk#D^^ÇöqX·´‘ä–xãn!˜^â×­ÊAáí`}RL0å<¤LzJðŽE<üþuŠg"•†;kÌð¡60ËF‘'ªñM×l^š{ ütÅ5óGêPŸÜRÀ…•©Üê^ÐÃ'ÚSÉãŠ|rõt÷’ç„ÇžŸÅþôHÒñx7>ß”À_°µƒdÕpÚßãa«g%1I.D…Ÿ—Ñbôv辸 Õ‹BûíP¨É´£þãâ‰ô¥YXñ>oÉ\ùÃ6dó'üÓô–T-xýC·1¥( T”[áZðrbu=…ÿ'I˜¿ÆUCΕñÒÛŒð¾€CãÑy˜’z›Û”CJ[u`bgW·[Ó³€Í^×L²BÄÙèâ2Ðæx££ïAdÍcýœ„m·…««Npn3]ÐæÓCÜÚÆÌ©ÿso)õ˜­\Æ) ? ¯—¥“Nm®RG›‹ùs BÆ$ᎪJH)ΓKlIY"3eé™éÇHÖüZxú/ܧ€ã–yœjvccnŸOMþ¼4—Àç3lÈ=¸{@ŠÓê~ƒ¥ï®£û¹ó˜ùò*ÇVÂLEèú,”•Qr£-“_æ´g³‘MŠòµ0/X‰íySÛæLÁ79¾Ü½Ê›Ä ¼’»߃—[6Û Ì¦êìbÊ2J¶Œøÿ¶Iÿ·¡ ÏðÒ&´pë „ľä$Ö¾ç>oEÙ_?¸ø§‰¸É4šûÔXIS¤û YÅæ),@ƒµ?àò†jì^â‚óæ•€ûQ¾¬x›áy st§²>_>.þp%Ýã¾½ÌI6Ç·äï±98ö‹Gcs 9\ ¨Å—8‘DSOx$݆ëÏ‹ÐýÇpÞïRt?mbøm’ãxqg«qÿÏQ¬KQ—í)Ï€W竱z–4åV:“„s&¸Ú^Žz§òëŠI`ó:²,“Ü4ú¸g³ƒ˜Œ“nK³®d}˜ƒ:›ÔGüÿ=wYpTn ‹ ‰yÓCnûôôj8Ì{žê‰>QºL@j³J¤É'‡>¼.á+­ps¶}³R›™îÿÇͱ¶g¿Úá²»0ñZ1~9h™ «¨ù6¼x¤ñ)y$Ú‹Rçç"|ãTVÝÇÊŽ0ôݦ@Ÿ”²@‘"Å]yÛÊ_8]Í){ýBô7ßÝoƒéÕ.fM0ÞÐêçäg½¼‰ÁÃÄl:åÓ"@ÕpæÓoMÆU9Ïáóµ¨#}˜w@iVN6 ‡5(Ϥ OážÊ9,:‹¢ô„Aü6KwÄþ¾¼>®¾ã —­ÔŒCWÙÑ¥å¸ùÌYþÕï¸üƯ…†$sæ3²=1‹|ߣŒ·Úðɯ:‘¢Î5¸˜I_ÀmŽ>mÙï ‘Í–08³7Í+ÃÚI©ûmM|¤YÍQKEHP½ÈÛ½ã ѽeñþ§Qp°W‡#›Ï‰³£’8µ¬˜rÉ Ü­Gã-žP¿Éý š!+ù ^„™ä/\¼oŠ÷#tÑó`+wÔj7‘È]‚÷õ–AºþLô;¤Qb°ÝnãùHÓ­Yþ8~œ8¼.á­hëD­†Ë *4mgg€Èâÿ½ù#†o;Äp¾K1ðÆ”-<†ÿ¶uqÊj¸Ò½WÜœ†“V”à¯x5”(:¦“[qœØ|Ê 9náÙâ¿}fÔ®A‰ÖPH³< ªå>l— ƒÇÏsĶ¡W‹{â0È+–^ÉÔUa \íáÿ7“ŒíiW‘Èâ•ǯȣ´AÞÀsu:±ß þ$C@[0TUV5V,³Ã{}SZ@œ33èýÎ^’Ø”‰ +WòMYiN@½Ó?à€|-±ÙÃK/ƒìî@\k 3ÈÛôV§ã¥|\ˆ¯e9>˜:âÉ’S¤§t)ïX Ç(¹RÅ™Ð3ƒ}çD¨©Ûc\cßVFȇÕx3пvëxÚÿÌ’ 5eñ^vð.ì¨Â»–&<¥Ú´A×–$¿ß jú °hCo†²•´\'‹…͸4Í„~‘8ŸÌU@Ww<§ÄÇKÎ"˜mó…”éµC—ý Îrœ…§RÕ¡Q.WM™Ú‘¨ñn2÷§à46ÇŒ!îß•™¹~Xñ_]M¿úKü°}`ãEÐ(ûG^o»…-®·¹ƒÐ c!¬—–dóßD'ÊFçÍ¡•ÎAò¦ªÿéá.Ýñ'fÓ Q¼ò>§'³ŠÓywœçјP”x• ÛNÀýU7HþaJï [¼LÍquôFˆMÀ}O“Á­ž¿aßH2…ExrþFr!Obw&q1«„`¾ÍTêÌK"±f64Å®«çj^†Bë‘p|R±iŒ˜òÔ pQ&~3`B-páÖx˜1ß‚ûâ½/$ ÿâI0ð>^7¤Ñ͆tÆ3Ajût-‘¨^†OVÂ…=œNÿ=Rœ7¿= !ÿd`ªÂt¾ú¡j(¹l|†1é‚tîÖ¿#óÏÂ~ÇÁŒM«U»Üû+Øöo38,¸F![ÀoÍBÐsXB<¶-â&êoMzßàaÏJ|>Ö“i÷Ý$Z³îáºwƬs`½k)Î…ÿ}ˆ_:¬Á¼÷6†:4‚ì’|X¾÷8î^«ŠÍ›6à¾üqôþÔ ”±P»p=l>›ŒÝˑۻ‹Çý˜7Ìù<þpå)߸ý£ éW¦W,ƒ»N;â4I!¸é¨9ž§1hr;ÿ½Iï@ù+®;ƃiÍáØZJ¸ÞÂWŸÿÁ”0ü{ö8qnœÛ^?@Î\²j혋\1¬{W1‚ÿ̦º`Ó¯px†,͆»¡ÓïwdÍ—33ËVÝ~ Ò.“Ù­xäÕaO>ÕsöËû°eÓ*n‘n¬‘ÆååêÔwý üyçW?zbºš+²<Ë験/VèÐ_}·œ\ê;È€ø_P*»Ï)æ;òö;×cìççXë|›Ûyõ¢S`@;<êŠÄ“½ðzã8*^Y‰-@¿:Ê™·Œ†ê,e0Y÷ŽÆ÷Â×ýÞtòƒôñš·Dc‰óHµ­B!ìÕxjÝéÅ"+…h±m<}Õú•?ÏÁ¾@+߉åQÞLGØs$þ½¤â!KÖ|]ò4#!$â"Xw†$±YXkÈbn^†ÃƱ Å? cfúÐù ¸Ñ=ào¨Gï*AvsúuÇàñ'10DžÞ¯Æ ƒ‡ñªê4&’ Awιƽ;k nsãËgÐ…™3ð뀪¦zö¢¬öaVò¦ú²„œ\V'æq÷öÙÒ:¥x‘Y% êØW8${—Ãòg΄óÕc˦” þÙá}N&_žŸ'[£ßÂùŠð¹à)„*GøÇÃ\ÁZ9ª5æ+FN¥èú8ŽÐ¼Bü]fë5d÷Žð_å#9\d’NÊ`¯æüà©Wjp |Mˆz?•šò ·¡ãóÑ-&3Ñÿ×øfºL–Dç…Y˜è—}¿Þs}Ú]•vdMàmnù2 –pÇg‘8ày›ÌÄ öÜôa¼2JJª/¢7ƈ0{­,|ó³§qÖJo®@‰¥Ï¸%½‰h¥>-ˆÑ¦Zg4é{ÃX4MTÕÔlø]bއóàfR :¿ZD3ÿàÔÊ»œ”Ø6’½ä@CE%® '/Ž(A–¿H„$‚=á*)dfÝgÂ5\‰ÿ_»ú¸/Óa†ôMj%ÔPs¸Ÿ–ÂÎænG›¹q°ö±$ ­ù‚3{—±·ƒ Ü t 5ŒœÃ"´pÏx†ß¨³·ÝP= BöÆg3Ü{Q•-@VYìe™ÊaøM¦ Ç÷*vcŽ•T°äĵs®W2Þö)Ôð´1û4ïþ”ï!—ÚŽco\a$ƒo ÅéÓ²([†«O/¡îÉÜý%Á kjŠ/³÷Âf!6É2{ÖEÂÂX\òÆ N¾™NÙ¦­(§M5jÌØ|û "¥L]n¬©5š#ýÏ$u1NÛ:–Ü’]ŒOo—`æ9#üBUiO+ž æ!Rô+6n¯…¸=ÕðHô*´Ñd÷Óx¬RLjj˼€†0îõò˜ö'÷KPS‡AÎuÎONéÃJöh•úÜ7vvò›;=Ø•}’ø|u%ú,Z9ì«$\¡gÃÔÿBVf.†¶Ê¢eB!¬â+9Mó{ÄmüÐÊÁÉVX1U„)ü¶b£l•qg )ÌÐc…Ë€Öïš ·Aªe㊚ûà>¯ #nLá²¥G¤âèP¯®[)w´Ù˜³¯!²ÔzÌñÿŸsáØšä ?ÎI)×øß\±ãW6ÌáDI!ˆ;eàë $jó^YK á’žã·CèÑÆî‹š1!±P‘ĦøÃËfŸ„ªyšLrÊbxãx–û¼Ð—Úí¬ƒòsO°câA˜ ì^¨7ݶìË|‚Êiס¢¤˜ÿ›b·š®ès …ºœÎ¿Mpdó"ª­¢IÓJœ˜Ú"Sö7ø‘o*…¢e:àR†qó.›´ uçÂ𽹿Á¸ª^ŒYù7Ue9ïÚióìÑ'-ŠÌ é"Eòš,`§56¼BÄfyŒø?T!ÕËÂzÛ¸õפØ3Äý6Šà;w)Š‹ÃÅ9Žœi5.u»‰Ñuøß¼³|Çnz1*Co$C®¼3]a.@ol‰ccÆ÷¡»Í´6‰K‡_øqw+LNcçeÕ¨…ê'x_ô–8;£uØ(&eÄ6ݶã-šº ÆM>…7Öª£™Ž>§ÕÕó),Z¹VjLj‹:ýiw÷>«²cšÌ0ç0¾Ö¼ Ǘlê]%^R´yh (eá|)96¥Å”¶?zˆ’s2™Ð.\Úºåî(£× ¥³¸Ô®¸ûÇ<¶…в|þ¢Võt!O´ dSC™ž’)Ì?4¢>'ÃLGöTL”™«EÒEbçàг*Í¥sX ¼i4Ñ< º{›Ðsµ?Y™²Ü<Ä™þŽù´Aí6Š™ïÇøå_à`x4œið¡UëºÉìÐ"º­_9L¿…-Ð?*KØ•„(úö[+÷a4‡C«3™°óhgó”ó~`H7¤Òº,kFÒ‹éVí|TßN.„¹³Ã‹£é¬f%úï®(K^I“/ìdÙâ÷QÅq%äÆuWj6tëä ¬¾Q…oí O ÷e÷ˆý—´ph’>\«_ÚG‘gûòyë¡áB5, m#–o ùs>|椫ŸÂøû¡ˆÌ\<” !Qtž/¤ aà òÑ{-f6fÓœ‚w_>“s£FÓ{ªí˜ZT‰S$űóR ) LâúO6áŸ^Y–ê%6‚V„¶:$Æës¥e¨–¯ ·-û ¬M†Ê8ml[ùŠ‹{àÆg]Øc£E·Khx¥$¤Æ‘¨hol‰Õ­ýÿžDóë»þ’~#ÀãJؽ¦Fœ½â9² î«‹°Ç??ƒru>š`ô8)ê.& ƒt~|•U1m(Æþ! X)€òò£Ñgâcèþ} O£ü•RNf(µÜVž›é}EŽoÉLž{°ò5b8û—ûocÿê åÆ(L›ëK×)Ebâ£Óðge,D ³ñ†P#u<}ˆËK<…/®<ÿþæ{&tÁnW"»;·»êQ7Õnnçâ ü'ãl™áQIæøÆ‰ Ì8†Û§"nŠdRoD-Œ`¶OÎáç(éÆê/ Óßs5†9«MÛ)KS’.ò›‰Â¨“AD¯ª}ÕÏ:‰­R +÷@fq ,Ó/æöÏU`²‰|È(»1Ñ×aÏ¢ T|“›zG€þ^QË%Vþà=SMFöc*¼6CýÍÐÛ˜´lqýT¥u6 ³AÕ.®f«5…hè<)µ³;&oDñuyP>Ì«ÛÔ:à²`ô s£ty:áásxí³täüÿ»LJ€,Ï•h³7šïÎ7¢Ê· ðë°E<m…ï¹CBBÌõr6Ýß<†®ó€§¢TæüEX•LŠ.¿#Ÿ÷ñÞnÅk*ðôÎå9SåYãÙÒRöv׸uSìÄmÝà±÷$ê³3j4–’1e dÜ—?8çÖaœhÅ\\i±/Ù,|€_‡ÿf@Æ„z2÷Ìü°f7»v—-1œD¥5ù|çPOÈ“.ÿðºC³¡Âèø€f}ø$cÃõÁÿ%Fýþ¼X4Ûú}~â!Îù³ÌÙãßøÿí/%Š›#§àÊ»ÐÜ?ì¯ðïÌ¥+›¦Ñœ–)N#a„S œÿXÀ}~À|Z@ÚÍ.Ô6=‰2›63•)€¦s¶Ó+a×ðÁ¥Ý°yG)–m°£ï‰6Naá$à]!—¾´•LjDûg“pmÿc¼ûM+È‹Ôïh7^ƾ\F/ ÿ†ŒDelª=ËmÂXîŠË(ˆÝ<-0v,sëðñ_o¾wO¼ÃðÉŠ˘Tzã %sÎxAn=bÅ~J àÌçvLfï#Vóµ;AYÁ-bðf;+;¦•cÙ¸õúôÞà#<äc…‚«m81MQ¶éU1~O: §A[Z|ShâFg|Õ§VY1^W’0ÈÍɦÁ׺p¯­5N¾/æ©£ó¾ n=GÆ|σ™Ù#öKšy5ÞŸ6,‹ù§mA·Ü€ÍÒ»†²Z…œ†¹Žk‚ÖÃÂìCí,8WS .¦Ö4z(6MvN)’ˆÝX€Mª?q@±ç—üAoj?`¿ª Î XƒWvöƒ­¢4=ñð™n9R¤Acª°¼{Ú\ýÌ×\aKÛ ¸ž/á8 ‡hW‡Á–רŠóF 0i:>ºy ÞYf€à¦ÝÔ|˜U<+â´ïyƒïæ_7÷”â´ “k!K0ì/×÷6€œ{Šïýv>Û{6” ÿÉ)» ãì“ÜΡll´®„©'ó˜Õ^JïòáÄ,!|~¤ +4@ú·Ÿ\pÀCò˱·¯_  ³`Åãl¢] + Íù H=d»_æ'S\<VŠæÂ£k¸þ´ø£¼…ªágé±ïL¹ŠÛRg@ߛ۴ãçŸ)f¤ó…ùñ×Ò=¡¤<‚[÷5¤§öp—×½$ÚÐR’HÔ…óÛl‘¡åÉÜ¡%ö¨,X ƒ¾z4öó¢ªä =AFà4_U?þ Ý{  ¥g™ÄúÞ³‰»naN)‡xø­q%ìíÛ¬©±g=ÍräF_‹ƒ?xpêíYÜþöi™Èåïï ?=OpbÏ¥ÑvÕF2}åG/[Ë;"Pº9“kôD½ö¿$ï¶Õ’øÁ/hn¾<Žú/óáCÔC<|Ò˜Mp'Ñê4Dñ;ÏµÝ g¥‚Eý•þn ÈièBò®áJõoÙîÈ {A-ëñgA{Ñàï3ª3£1äˆî(}†Iа®¹‚ýRŒ¯y©ççÁü+£™PÛ NÙ{•÷=E×”^õàÅ©àæv N–ù56ª¿Ä_gE´ýX»­ Èû™³7Šã!ÿi"oõн sp!;ÝšÓ`9j?Án?>M]ˆio"ð¾Šl÷Ç':GñžÉdj~@„%l> 76sÍÊpý…í$]À¹—”pWV»bñÃx­Écård K.øÌ$–Z…¨¡;™>z>–žµ©ƒÐø~,¸0œoŽ8Nù0÷ÄÆ¯úüðùyœY×0×M*€^–¨.0—û%\…o^àÊóQŽT?ú^ÜýŽküZBUG±ýê ÈM6¤f“w’é*cY”K#òR¥pa…>Ýs4‹µŽ=‰öBáߢ¥l†¢“[Í‘#ºö˜ùüž¡„gdCð‚¦4[ÕwŒÈ» Êz#ö«·¿ïFQbÃËîçÒºÁ»xÛu4l»k•¡cž0hXÚcõk—}ŽB…M&˜±ÃÓ¼Á€—ËnácË{1²CöËãOÇÎăqãcø$xsâiöËfŠ¢OUe©ËçÄÿjOh£èZ&ö/sgè°ò;q`²|;ýQ–Â?ünÜYëFÅÝ)Ó]w€+v/bÏ*DP\Ê’uÛƒÓ¡3PµR¯»`OÅRÒÖî‚øq‰/Uµ?ŽÅIÌG_-2°¡£6XpÚ>"týñ±¬êE˜µG°Ú Ùœ«Ýÿø?Ï­'Õ!çJŠ'ާ¹§4ØÖÆQ8M_´u6±•Âë¡05uÂ3èç!æ3›fÌueGƨ]úü-»Í·Qõìoõž}‚^óÐfK­´ðB”3l›MÖ_ŽAvÓ”G' Ò‡M¡Cê^qÝ o¯i°ç“¦Óç+1Í©œ–+زå%䶘Al±Û¯ú‹»V“MÕ¯°ëzϨQn8ûmÛ zqبñv´gì=ŒîÂ9•O¸_˜ÍjÒ#~æ‚5²èn1^g¡«|=ޮВã¯h3ï´PãÞ Iý:ìÍŒ_ü12âlö™°kPÂÌá~—ÆW­9üuW!ft:¬Þþ‹Ø}”e&·Wp7מæ$v|'¿+¹ô jdš™XÆ“.:1Òÿs_sé—dñ¸~]`_N®šE¤?N¡î·oùÙ×ÉåˆÓ_Ξߚ@5 ¬éE›P5ôª–M«  4~Êᦫx7ož2[Ê}JR%ÏŠ¶‚íCÏÈÛS}N]ïëHÕ³d"Â)±o1öâI"_»w4s=Še%Ò´üŽ;()â<#k9mÂ.ÿ¬ÀiÃü#¦±€‡:4ã½ òl—Щßü¹gyƒ°{åtú2Úˆ¥(PýoÓAÿB F($ðj»ýÐéÁ8&ê/H?>ÉÄžîÝœø…tÌ2ú€Õ«•id‚ºL×9ÿG½ŸÜ…AŒ4e_CDIꈃݗo_Š¿h™ýç¾õFá ï°xÊBÐç,ØÛ ¯úŠéKŒ™nQ,“«ô"ëïÚá¹ÚlûÊXؽԭ!^È‘¬©Ê>Z%Á©í/`éZ9æ÷ê:g¤q§ºßWF(bÏQ2øªróÇâ¯`Uú`ì 0±TôÂI^uï†OÁ ãV$ÎJûÿŸær"{+œ´/b»×î‡5Q9GmTs;E5i‡ìg\ëÜÎ}ȃ¹>× c¶%Ý%½÷/2ZÚAÖ|;0O~$ÿg]âqC…–P7ç:>ìsÃk/DZ[K;Qˆ{¿%B²ú"˜­ó0\ 2 ùvü§«,>æNÙ-Ê]é–ÂØï{!5ÄÕWN¿çÕÿý¦wÏn‚5AÚµÛ’M1¹ C¿üö#6r«…a'~Æ^Tp—"+K8Òwfð$o—á•d‰Óz6³¾Œñdñ—9ð(ÂŽîµÙq˵;0÷kçoǘ•ƒï;Åè8óïXܼÀÞ¢L1–œÀÊ8 ù}Ú,hlhȯ¦‡VòzNÓʧÇ0"«~¤ÿÏÈ('NSðΟöß|s¬~.&uM`_g¡öÖÜF{í 'ã„%ôLô.ª®:ƒ½ÐЇ™ž¹x2º ÛØÔ¯"ìàâ·¸PX˜í¨&Ô7»–=±ˆcßïì¥õ’ø„ʱž8«Œ‚óàÇ®§»ÑŸ{¢i•˜·±Î•þ<9…þN0Ħ]û1ä¤k¶z¬Y¦íX:Ï­ìÿºÑmÛ®ó³wHSn(^æAð‡¸ëjHMŽ…±CÊéòPKzœŠ–Œ¥âM ðóht´Â÷YÇ©Eq)̮¦çq‚ìF–ß®<ÒÿLæ+Bü4r·¦Oþ›†Š ãaðº¼=Œ}â#Ky-I-†™=ò(¶ýñ…vWBë|Mx µ#ýùš:ãèfQ,NL†ýî5¸Ù7˜Es –-®Ô{ëTLX7ƒ^‡ „óÜ.ëÌlt‘ÛÏ­ë,€ÎQ¾Ü•}·ðÓîU¸éžçaþáð.y9:•EÐÍBöÜ—+›©çDC*E¼ §Yýϱz¯8óüfFÛ^s%E™Aƒ4\Q¦F‹²)v= n6î½ó&†·$pç?´áhÏ[ÛU2âÿàƒ…ž,‹®À AþY•Ü‹<¶,Ü–M´åå„y>Q£qá$ib5ÉhüÅ ~ŠôòÙ±ptßÔ9= ¾Î[ŽšwÇÐ7þ1€¾Ý0çåE¼q¶Ïànß Ø—âçŸTÂ.WXëê/RBéÓñU\ðç(0OÜ µÝÍduïpœ "ZUôü¥èÉ ÷°™blê‡4²25 ò’’LngM¥ÝHTëqugßÓö$ BnÈ‘Ô`¼zJˆÍœbDÞ'z¡¨–ìî˜Ï½¢~L‘üSŒ=-Ñ#þOt_ƒF­«H‡D ¿jýJºgçlwBÜÜRµÃuܯ†±ý|lj¹ˆ5áðKõ÷AUÝ>sÏßûT–6ã©ãm\Þ³·pùƒ/“‰"wç™á¹m[ =~Íš>‹o=Ç•&ZTuŸýoVúÒ]üZ¥˜ðèªaì–ÁeÌ¥Ug±‚§ eðþZ¦Iß„c÷\{¦õx¶ý( ¶õ¯É”û“XbÈ ZûC„¶‹Uƒ¢c6ž.fu‹Iì’ãœäê¿`²ò>±º¼ ŽÙÓõýõ¤ïÙº_R‡yëxÀ¥…:,¢ÿÞHÿ“s'GzÂÈ·ÈZÔÞ®Æã½ƒ ©hhÄÇû.~ÄDÏ|_x¿Q§mük]ov[`a˜>1Ï&Bö´ÅÜTÕ&^¿Í ¨_r›{ØPÁq~háóozcfG÷X|*7ë×}{;¶úÓöW¡”à ¤ëâê‘øå@ó¾@>ôä¸yZl­¬+lÔŒÏ>;ƒÑºxÈ·ïä§ÝÖ¦3Wyñ=­VÑfõ¥°îV!×3ï.ÆÆø@Y¤6óØ^'Teøç»Óq‰ Åï*YÒïÓ°Áêíj EüXˆTbŸ¬†×”ñÿ§Ç\í¤bëô}‘¢‡öú³¿)gˆÛ”x¨]˜)¸oÄ7k¿ï”|–h†3ß²Ù[nqõ[ N}ŠWmAà÷Tî?íå[gÕhÀ¬Òÿô¤QRžaæK\ñјÖíª ú:•ø¨mÈâÈûÜ›ÐÐ)Æf‹êñVdcJ÷:Ø_cE׾Тaóƒ|O+ rc÷â¯À=§qÉ}ö=$¨vŽ+T§6§s }ƒ&\‡0i®ÈMŠÐ]’¬øùgœOסvÜê¿p 'øk•3~jaPgv©áÅzköm›øÿ>ÿ¹Oš\8|€˜Uå¡›®9îÙ)J³çc¦{ôZ ¶‰lÁ`‡5*vg8µ§‹`ÏISLÕȆs×ku„ö†V¯Ê1f¸O^{¦‡²ô>øêTò ªkQ‡láWp'îÏezypS-‡Ãþ‹¼Îù§ðøÀGâwØ–xSºíí94|ÎW7Ì wEÀ·©Ø¿šÒX )3Nå8 òc¹ìÖèÆârI²B²•̵™BvU‹ ¿¯Ó¥Ý:teä:ð¾×Rùµª0aæhª2ªc\¨cþõÿÓ«Ö]³;6·cß§Ô?êbƒÛ„Mq2½Hv¦«õ^b°Ýô;lêxpÎ:¸uÕËMpÕÀhù3´Û€æ•´\k)v¶Gp£º¢1cJ1ºü BF¹ø=/šxœ³•.àÙ÷`Ŷ4œªþgÔYAÎ8¸8[Ÿå•Qð<¥Ì­IÄž_pWÂ%ÐhAƒ.CLÿ^Ås%ÎVzVÁ´[õ¸;r7ö¯ü‰åÖ§!bÕ ·ݸbh*ÿ^˜^‡¡äRµ -ëm‚°õ®ûÊ"ª°ê­ýˆý¾®ãñˆJ‹là?®ùGÅbèÑf¬‘=‚qÆP׋0r¶û±ÇÓ˪›cû¡"ÿ&®i ­PB{:NZðƒÿÇL‰t¼V`¦4~†ì$ÿÀ¾6ƒBC(~æ(gV^€ÚkÚdq—þej,îKÜžÚb|7±´=.sdˆ±Ï xì÷ õÇ@Ѳý`–äNíDL4di.²¨PWBN‹žÄú‡HìéxNhr_vEyÇ«<-‡x,½Ƚ7?‡/§ªãðûà? êÌš6.jóW°¸¸6L)BöâÑHý{•y‚{cÝEZ†ÞáÐ`R:Êè4î7œ¬—~ó˺ò`ªƒ¿OÂ/þ‚Ñ0Ó•r⹫aóÆJ>Ö·á* a*ò] ½â•häåôú—xF%‚ùeë—n$[Ó8½yÏÛ5gµ•‹Âëß3ÁošÓš&È?ºw-$ª^Á¹kE÷ú%·ý•, Ø|•/r0 u~¢Œs^âÍWÜwoÙ1ÐûÞ‚yŸ£ã_-QÚY'ÈÊä—‘îWøï .ŠÏ¯B./ v¼ÃmúˆúÎ\P[±…]¾˜õ¶ƒÜêD LÓy>âÿtU%20…#Æ™ÍÏsR!tÿnõl„þˆ$Ì•J€«ë¸íGŒiÐK#%¿‹ˆ%*âŽ÷¦wrÝ[C6ËèAf0}ž3Šº¸ŠÑÁ 6(âË©*¡Á¯yKNmb£Ùå. º{¿-O»X kö¦ÒÈü ,Bk6“4^„6åÝdÔzÆéO<÷:ÙRÿ÷œ†u^¿qz•y,'Ž%7q/þ6qÏ&¡òÇ*®¢ô¨>ÝUÐÒz/ðmÁ~©ã¸B0œÆ¥{ÿÉ18ÞÕ©3çxû(®P€nÓ°÷“&-Þý¿Ï?cÉrn㘥àýÒ ¯ucÇßýÔ[ÿ/L”× ©Çù9û=øîÄþÓ\VoÎã{ÎþÈyåÔqswH2Õ]’L!ær±ÛY}Ã-™yCÇysw‡s»¥µ E>¶¡—«+­,©%Q}](5¥—¥OÁâKÅxÌÁ›F*¡éþäƒüwrâZ.'ÿõ~ƒÕ¸©8-p[iäÄd5ÔpÑÐ6Tö¦ÚfÂ"•* úu3È·[gaÀD•>\Fàº#U÷HEî¯ 5>Ÿ„ÆçeÙÜzB–½y Ü¡ÃñTþ’ëJôæo23øÔˆý~b*8ø;—ÏiÓ”Û¤@¶¨ÐG±¦ŽùoÞòÿ-c'¤ÂðÅúįԘ=«Š%ß–º£\Ýúϼ듛Ȧ¯)o[²Qƒn ·÷—A _´îöÁ•TAôìñÃl-EÚ¥x F¯kÄMªàðn-[ïþ˜Ò ¶db:ìèÏÂ$´`‚ñ*HWMáu{¢rî(*s3´Æãü¾k\à¨4|{ÉUeó­³Q8@…Êh\‚«/)¬wß‚&‘7áÁ Œ/çøyæsüx_œþ÷Úü>Wà–c*U$ÝI®Í;Ë¥ÚÆŽÄÿ »¼Ý-j ¦y®‡H2Iû¾Rˆ®¿•‹: \8‰a »TÝh¸¯Â[C¡D¡6 öÞŠ3ï¢ÚݼγÉWŸ·Ø÷q‘¿yZ½£°£~£·ß[<¹XŒo4}|©S‡99&TcÿFR}#gªœƒµS>CS«‹*|Žõ.€ÃÔHnnn<ú'è²ÝÕçH¥åõ`›–38çnåZ8ì¹–¦U=&ÓÏÁ« èÅÍûàÙácüžMIÈžsÚkEM—NdÍm°h^$Nÿ{ \üvhÈ©±Åw•öd9±ÙOÍGðOpÎenE+ܬéG£¸é0nñêÆ µ¥$f®,¤OÏFãš”wA,x‘JZ*cálïDúö”¼Þv‹H^?@{¾|‡³ÇÆkCÄi‘ÙÀ¦¤àKùÜ?M#XûëôÏ„®†)s_ Ÿ­‡·²î@ÔÀ.7ˆÙÉÀþ5§sé0^ép BápÑGº©`+3u%Þ© … /VrÁkÍɬu=ü3k´pûo¨ó¡ÆfìöȰù Uf{¹–[Y¼t"„õQfb‘‡YIyOj½”¡{Á <ùi]8ê$ì }ƒ Fì?ÝÊý}’ÅuÜ2Æß±a$¿TØúÈÿ›vî”Á6u9—%Åät—°½UÉèƒH¥Dù%z,dÖ8¶³ëùx‚«Ä.nV°»£?ž‹Y>ñ?7ËP›E8æó&Ôµ|6¢Ë¸ü¸û#ý¯Ø'yß,›þ(Ò„S¢´îÀ4¹ù$1bù$3hþ™þ?˜ÂÇÓÀEÎ}©¦û¯·àf?ØÿHšÙ>‰$£Þ†Ó_³p]I7ú­ùŠ"¸…в0¸»–x…7Áv9¬ë/¾OõEÓBüéïõ˜ôä²ë^w£Jè|:Ê™é^o…Ë«˜Š’ ±ê¨ç&ñX^ì<>&Ÿu·¥¢³‡„dq‘vô4pœhtfV±Så×p÷½(<5º>Ûwò M¬œxen6ÎþºL¡+<šß3Ì5îÿ0`·±°ëú;oL#-…*øzsõ}Þh»–*ä“z?YW›Êx8°IYP½ó8î¹2šV….‡RËDUl@ë_ÿæHÊ2Ðÿ+.U¡þo¿ Åª³Âñç£YǯŒŒ‚ÞBs¶Îø%Ô±hpÓÎËßhÓ¾^M?Jž.,ÕX_½pÃ'˜«ZHÞ+âÞø¯&9›NCÑ9ú¤­Nΰ]ýð˜}>zȶà0¥AA’t3õ¡P÷_ÃòÛš ‰çåâQ,M…¶Ï €g¯yìæÖ ¤·%"«¤GêŸG‹0œwªå9z‚£«HIk:T›Tsßµ0Æ0ž7Ê!›” ÷Ö£…P^ÇÍWøòŸ4÷Íï3ôùå`ßô¨ ˆÃÞDmÞêðïç)gé:«ÅÀ#¿-ûõY{f;.²¶b,–ÈõKaŒÎ8V?yˆì® "×Z¯róçõóÞ?ÈqšM_ˆçM\r·•´?`s3Ùëc—ñIv5ŒÚú+%¯à6 #ØPhÌúzvpúë³ÈöÝšt|« Û|Ìž3ž“Ë-’ ï¢;ágl¬~·‚7Ïn: Eë¯Â¬IÇFì_¼XŠfFBÌ0_Rú¦Œ_ÖèÝ$ÁÈI;xk¾+gEáò‡æ6mçñ0éŠlÝú;¹û,|ñ„LO¤ï?Í¥5§F±÷>»ØN»6îÔ¸Ú|Џ|xŒI_÷3ó[zÔ§‘]Œýß¼óÇÇ-ÑŸ`eH8ûþú=v_y…‹ÆÓ°wËÙ¥7ðsÚ%§GÉË1Èj5~ÛX‰%éá8wïõa;¾O§ii%œÍeª›¦Æ¤oÙ`¬ÒMÎÒ] +æÝnÇ1 "S¹¿wùB×ßáæ÷)dqZµ V£w hÀcö®9çö?b Þ­ØÐoÝ œ‡Yú°ñægpœ¬C×ôÝçLo]‡Ï'äÑZ«€¾¿_‡¯ÿyÓ¼Óïa£¿ tn¢Ä¹”õËܦ|Ó§¸¨P#<áN :/Å h˜'œºP=³¾à¢—ÏàÆ»ø~“>Õ·ˆeýèï'S©í‘á:Ò’À–ž§Í~s‹gßÃÜœÑ8yÔdZ¬›„ò™]ø’k 2ŸB¬‘ ;;tûuj}é¥D}­ ð`úoX×§Nµ'ò9_ñÒ\ªMÇhÂÝúޏ4\„'Qv>¤ µMòÈ”)oóe:ñÑ#õO7f€gáºÒ/°+o ¾p{„ǯ4<3Z ùïàá¼ð†µÁ9زg4½×ã‹eŽ¢1¯zf6Øô‚U¦:§5Bïï½di{L á¢\.“ž/yée|þäg”Fùþå.Yþ姆ÑðÂ*°NiÉ꿠±t?<^¢…§ß~F¥ÒLÞçoyp`ÖGÜ#Ïc«ÊܸRÙ1J‰D´|Äó¦n ;¶.þ K¢ÎÂõľõ·?3†ù¶jDL›€+nêc¥Ià0Î×Ç}EwÈþƒMÜ8/{r§x]i³Êcnœÿ[¤UBŽb ¹ù:½gÛ¡uen™}‹¾UÁÚ‹Šh5µ\îz>ó~ñlvÚ± %+Il¼©J€¯«àÛ {1X –³rÑz¨wvÚ¤~˜T†'ïäQ• `ó`hÌþ?†1ž&Ýoé0û4ªH6Nš4ÿ¸Ýã¯Ô¯ Q.·¸ êAxÐ/§ŒN'/J %( ¤Ç/sÉ µG ¶Ÿu„Æ· DrTñ©gpÐýŸÎ΋/þ…ãìœ`¨H-Ф1ªR¬p\:Ã|þemO(ò*qÜ̵cØh­ÿüzcœõSœFÙ\©ÿæ(JÌ¿ƒçç‚L\‘Ò—¥®ÆÒÌdr'Û²´qËÈž1b—…b×®ñ+ o@Iˆ8þíÑ£¶l-û+¾ŒB¸ÎúÃyÛ„]¨AßU‚;êáeª/$Þ–`C ÖÐL„iÿ€ØÝ$°›'Ïr§™o6îèaÜùÒ€ ©IÀÃfRpݱçyµKÞŸZë(Îm;Qjw#$ÒsØ‘áŒ^QØz׋*L:ÏMÏhE}¦ñÅ ÒN†á¶{f¸ÞvÛi€~V/ø²C'¸û¯ÁÐÛ#\oN5>Ù]0RÿOLƒw¾þß,óÍWºLý-ç£xzÅ€èé¡alYO =’qéÖððm,ž6úwš&Óãk¿aQÛú¾"‘nŠod•­òLÅÆ’°7g‡÷×àš¾g(d¦ÉN{ u´ óv:EIã%%Ð]Çå˜?PNääÙÿf«™¶r/"QÌÛ‡ŒçáZ]uœªjÊòΤ¯ãÂRáê®åÐ ›jÙÅ{Ú¸6®¹ÀÝ®B´‹“j«÷à`q þ®Ú‰¢}©Ä(Η;dtŒ>œeÊŽÅÇØØ ì‹¹)Lj‡¯öÛFð¯ZÆ1œRMH¨$1î`w)’Œ?™ ‡«ÜÐúµ'íQ’Á%ö>äÕ¬{pù‹ ¿ë8±ãBQüà2˜ÿ+{l/™ ˜º^€Zµ•Ã…+’p±%j¯fs% 6{œE›úP¼¤ô‰¬§‰Ñ›Ê࡯]¶„tØñÒwü7×þï(/v½:Çn Qõ¸t~똗$¿`›ýGÆ–¨Ñü›[°[Q¬Û Ø3{wtÿkÁŒ¹É,q“l¶’‡Ƴ`¹¢(ä¤ê³±Wf³áëù²ûÇCb¨)>µ›Ï°S.=£Ê‰ãFì×Ý@ù’ÜÑ!pÄ'œóÑ|ŠjaaÜ/E5f¿õY©r’ß^ò-$FAÂßdx_9†½’0 ŠÙçªfëÎúóðÐMp_– E—×ñµN ¥sJ µï×4аóÝÔæà>¾³YzïĨЃ[\éw®„¿ôæK³†*uÚ;Öfü2gíË‘%þÌ窼£Øð=ɺÍËQC°ÿôž¦ëVóhZ¢# >¶,¢Ü¸×FÑñ÷}ƒ|Ó§¯»–=vií¹%ô’h+Jf(²­åï¡ñ}$—1{.³Ü6ˆ‡fŒäÿ‘˜‹ÐöUötGÐ㙺Ìqï%x¶¥ ±ea ¨ÙÓ$u gç=üñ® Æ`ù¶»·a³ Iÿ<;Î3-ÚŠž7s¶?ŒØ²ì©Ð?LÎ\NÐT%sX?\;%9s?>´½b÷^ÄÑ¡Oè~ “L`qO7À…<<½£;þü€ˆ¦^bS¢‡/Rƒq\Ú~CI]}À¥W˜³OôX…a½ºa!ݵp:î2‡ŽÃÏÉNCȶð¦2+Þae›ŽÞ4š?h‰!tiÞfIf©–ÅG¯T«.”Ý =+¾ñv\É9rnw Ó|´†ë•Éô½ÉY,>ζôÁ–ÇÄf¢Ûóc3›wc,TªèÒ´|0kÚDE»3¡ùb<÷%ek7—`jâÍÈGÝO¼WnÈͺ+¥ý± ¶ž®ºŽ!.ðø“•ÝoIåλÑYK‹Á—ˆ3¾ûo Ò™øST•> I}mïC¿II²[Á´\ÈÔ7°Ï¼—ˆþ=È^tʲV=e¸˜wÅJA+9›9µ”n^ü‰­ß>ZPÿ²Dxáuš÷#*Å |„“ Ylš'çŸA;b–Ó‡ŸÙ ø_Ir9IHO&ÛÏ8°ã¢,sA”¿½@y¾Ê€õ:‡PõÔàšÊøäØ0/=âp³_–ñfÈÞÃç›;!ï÷f²çb"Ô}®Ñ;£Øœ»ûÀëtî û„UAýœƒ¥:ö¦Åà ûlîqš2:üËç휺èmîÔ=a4ãÁ./¿òJñ0Æ' .¬Ç#‚ñÐÍb`]ñRP;ªNGÝh„»§øKGqÕm+þÄü9׉Dô$61V˜¼•4ÙÆj€¸n'f3˜Õu ú|ßÁoäúå 0Á͈žüÓE¾ íƒ%!Y#þß߸ˆ3XŽ7´D¸ÿ4—NƒõPVÞ ™ÿÒ±½Dúh£Ã‚…Œ/¢Ooôb£Ðd”°Ð£ÝÕ'ÑŠØñמÝ'Ôãè›v fsb¯³à%·NJ‚£Ë21áQÃãQœyp§ûý;LîÃë‘%¡0©._Š»até¸ðÊ’´ Ç"ÿ·¾{3NÄNl…§Î•8Ýû#Ü2T‚º²Fj›À\Í¥U˜gÏ7lH£tÏVÒ[¥ÏÊ.˜ÁÕ/ÖÌQC—پр†ÜÕÔ ¾–]¹œ³¾¾ ç<«ÀlùÓ¦ˆ¹÷ç±c×ÜqÆÚ:ºwèlŸhDw?.ê(ŽÏ4X\Å ¶æì n¦ ê:XX{†Â–£ Å2®D¯ƒ^.ÿ‚³Nñ”X1§³Í€m´¶ÆyJÈcI3zÈ£çøN!åQŽ­†óUÕ€uT.`fº³±^ÍÂÚ=ð±ý(|¾~’ÓÓRÔ¸%‚+ÿž%Îêž:þnÞ{­qþé 82íÙMk>œ¤¦k&ÒUåëé¤þ„Æsvà²ÔEε©’/AHkŽ[búÃ3ÖÇ3©ß̓¨Ù7†ù+–‘Xù{œoq9o}Älz-F‘‰0wfážC}ªÃ¨ð$2^ó:lÙôJý÷b¥Ô":ó8µö‹'£õréAeeˆix!ywþÄŠ4aÆ å£Ý•ÿõm?±´c97ð5übj`ÁŸ~äd–²CÑÊ u&Û‚ØñÖÓpÓ?Ó&®#6oÚùi«îÑ ÛqùGuZ®«E9Mg«f²#Š.ì×YQ}ý=,©•`"K›x·ªOÂ]v4Æ_—´«Ð úA°û×eNéö-Ü«'Ê´ˆ«èЗö}xéÍt¸±¨úM;pLH 0à78^uëÁÕ°˜¤¬ ‡e_âáwÌwŒÏ}‡¹3WÂQ1g2Í5ŸvhÌå¹öDRùIr,s–0•ι76ÿ+u¸ü+# üéÅMÃüŠo9Âfº,ý#“ êsžQ­à%D°ÕJ>Ì̱ ŒU¬`Z]Ü ßoDaE›<=a™ŠnZqhiq’<½R ·Šf€Ëg>(¾‡>i>®ûu\¾ï@©~>|T/ÍüŸ\ÒñìÕ¦ÓhÑZ>†Eäë~}ö9E€~Üy Ÿ6cûœx\Â+ؤw¶tf#Ú)3?É8a¼Œu½·dkMŽá\›00Ú%‹ÏU›°$Z€­¬òÁW±ÔÈx¸“”ÜΠ8Ýæó9Ù‚lE˜%J—‚ãS„é…Ǘɤµ×@ì®ìÔT™׸ ü6N†”WwñÖ™|øzp2n cnÓþÓj¦©F¦ô?=æý…ñx÷°%­½•íZ ò§%HšJ0øtˆýnÚÍl?çóléóÅñPÛ¼–¶¼£·†óVU Ò¥Õ‘‚8·lìô· {‚+žkÒüÄv\½õ:¬ßÓBm¦„11Ãdx¢AQMg™SãÁtÌÄiÒ ò÷]ûŒuƒ é*‰ìMx:ÜÖd£Þg1ÿ#:lüj'¢%ÀØËqœž¼-³[=¯Aà+ixåÒ‰½-¶§Ñ97úɤ='©ÏĵD°Qq¤þ}3(‚Üy˜&oŽº·]Y¼H>RN¢÷Í'1±íûX•»3;°º/üé&Γ'ÓÅt ôaÓ¨kpbe.OÓ¡ߦ〷%{¿Ä÷Z°¬Ê”ÅÖÓêa~øFÆ”&.Óцð©nëNÝLoMÂcÏ$Ðì©3-ßjËÆŽÄj%~_§Âº^îÐj¶ÒŠG=­Å¦+šò¯uË{9­þgáWàgñc®(ù(ñW‹á «Þàè'yPª'‰2'áO¯ ÛÝ–±ïÈífsv%] æÎ"3ÞŽe5/¥ððTtãuC9¬fŽ ²éâõÕ˜Äçs/y߇×Ôe¦£°$¨Ÿ8-8ÌãBˆ…SãT×d0ÿT¯¶vqÿ*äɘ/™Ä[0?ìN禶Ÿ>pWüÏãáóZ¬Ór5³|ù޼úšïMxt~û'ðÎ2fzOnâ7g]|;Øüš?DtÉ®ü§ñHýÏKÿÊY•ˆ.ëÙê…ˆ·þiV©Œƒ¡Môv¦›´¯jÜö‚Èà|ÑêI¹ ¾NëMôèö,?ô íÇáÞ ºŸxÀ:ͽlet.:B0HÉ› Àª'4i^Æìx‡i¿—ÂÝ·÷`Ñr=¼ñîHOüˆÛaâ‰_°kÆIlRË$£ãå™b­7S<{€® Áñúòô_„=¼¼¨ÈtRžp¼±òô\ ¡_~Á9Ûb°vÙtÛ0>í8„½ëxP]œV»X“+ÝR¼:yµÃ¢Ùx:àN£>÷¿ï¿Œ Â9OV¹@DIQ·í³û¨‡\4dÕ*Ñd>¨Õij?ñgðÕboè´²ü‚±sB3TÑa9 ¿ Øå£è_G—ÃE<< FÇžêàžu)±çG§Ñ†ç öò/ð˜;Ÿ:«?"åÇÁü¥þ’)g½MAêÒt¶ç„1ç>g"«œ1J[4°°- MÅOÑohÕSÖ8¼/nƒõDý¥œ~¯öf6þõAÉœ=1 bMgpù˜ &=kõœÄ6mÖNJdâ‘Vl«‚,Óß<ÚN°£wž`•!aKŽí£ÇÌÿ§ÿ:š|ÔOÅ57+ íË/®nq#K ~é”ö®œx:~7Ñ ]œÞ.É¢_¢ôè•Ø¤G¶™ûï=OŽm‚ÞçñàÙÌ£çr èŒ:5vúQ‰=“aºy4oÝ7œõ5Üôs7Ž–Áܜ˸å›n_ö Ÿ…‚¹c Îß⎓ T‚•Ó'³y¹÷ÑMhâ×3ôj+œÏxŽšxªìXÒMü/oÇ`dÏñÆg^¦¢}6TÎáä"" Dø{/ÂŽ„h2í€ÕP·JÚ6¿'ßJvÒ' qè÷(x©ù•äT¼Û†Â3ÁU·¨¬“Ïÿíø»Â|ÔáÃ_A¾ÉJ_tËíÁñÒäq¨?M-,‚UØÉï jñ;–bŒnnä–ã¤ýådt»(}îV‹§!çÆÿ[?¿«¼ç„ÎèAä÷ ¬–[)Oâh»³\©.&Ϊ“ð€À ŒLh…°ãÐhq(Ì9µ7^vh‘µ€š}U¼#¢À¦†4 uyž¸„®Ïæà©È*ø]¡H+OEÀŽ|¶6D×ͺåMJðR.šÊ ÒEË›+ú"ëaÏOiúít*ö9Ó÷Np‚zµø]L‘®ê5Æàý{Ù=Ó¹öKFúבm$Èâ,JåΠnmù°Úã¯}¥·ïV2ì÷öºx—Nvx`?Ä–M¡Û‘,|™‰ãE¼èdyCˆm‰aêÛŽâ:õMì±øeÞ„§·0lh,ˆgÉ0 ¬‚ãçA8MŒí>ÀÛ»~Y´ón¶^+T½ –ަâOfRwËw|÷¿B ©--²ŒQ§ ±Rwn¸¾¾‡kNîôÌ·tþ÷_¸YB|´ô¦ODÁzãmŒ7 ÞHqío ØÞW9³l<î±ëàlÖ¥@CR 7wŠ&g׺ÞmÙóŽ4c›ŒÊˆÿÆ…p©ÂÎxį€ójðÁïy]¤Úï''µæèšA]|&NsºŠ_BóxúBŠ®1peÍá>œÀ½[Ø5G™õ»yÓ¤Q ÙJÑŸ¼?5Óà?-é:3gŒ{û–Ê™r;µUÙó¹Ú|¼2–®|Q«gÆðò{G³•‡`Ÿ@V—>ãݽ·†ËÔéÅqÏÀ´/þN¾L™VÒx–Œ¥§O„-WD™Ö&[¦7<³Î?‰%ÛÅX›ÞKöl×$øÐžD}OMÀf’ǧÊkÃAË\ôkY GÜjàõz}ô©îãÒ2®’Êic8ç0§ÿWÌw…Ã?—}‹1ûOÏyë™'èôÏÌZÚ0{^1ñèö×ÎÁŠïÎt {±ú óK³%Ò)¸Î¨¼[° :ÌÄÀåP.L¼n×0ñú§Ü+‰ÍMi2š¯Äiš»öŽÁ‡œ"*¶·õviü•8›ˆ|ÞC†TÑW§’·×³_ñ“ y‹+Ûq, Þ¨‹Ð;§R±­Û–‡ßã{Œ~^º:lî<]Ö<Ìû¾+±¤¹ðHY›9Ÿ«Ão y¸ç¤)¤jd“é–Ïx}ãæqÛ]õÙÒ^uöaÙWüáóܘÆÌÝ^@ì"eâ0Œíþ¿ýgúµá¿ù庥N¤³¥Ô|pP8¸“•6\Ťºiì@Qí³Ïd‰Þ=rðça¢²t]xҦ͟£çã.\ÜÌœiŶ±(ôÇì+Ñ ŠµÈŒ !è‹ Œû.IëÎfrkD`”ß+¤.u°h«-íÚJÖM$ïuÎÀËc ÝmX¶ð1†’¯.”;˜ÚÊÝoË„ü7£ $è)/n¦)6´Õ༒M¬E´oi‡?Ó]i«o|c“‚s5Ž»}!yù#œE{S/Á ‡&ÿEv~Õì${£7±îáœìY7aÄÿk—*ã7í[œÎ’Aâ–“fwmyçsOÁ3…ù'Ñ8}sxøH‚ÜŠe*^7~ÒÍn\.c;:W2ÉJA÷¨L™oÌoAùËJT©?>ðá–ŒzÓI¶¯$MŒqVK.Ù³=’í隃™5žøÕ¾€—#râ‡ÄÁO«–X2 ÃÒâ°k÷lN,H‰ù½'/òÓp×T!XR-ÂôÆ'Ðã/< óôÖñtq^’,´bˆ§÷ËÑ3Û¶òy±Žp05ª‹ŸpcÎ`«ÜÿðŸØ7ü½™{TÇÕ$±¯èøƒJDpZ3(¬ŽÅû©ÆÔû‰m8&Œfš»pº³Єg Ð{Élð3êûéWËK¼9[óðÆÆÓÜ…ÈÙÜÜADAC¿· èò!ò¹úÈsšìèÚ7Øu—C|¯í¸¾tºTù³|ã¸[Dô§óz˜…÷ï ¢—åb&#wtç²e¥Ö,®Ã™8ËÍändÏàdm–Á7gvؽ‹K?1ŠæzF¼Þ|ÀwÂMÐéÐKù8Ý… ¬ŽC¡£éB¾ùo.š·ð <ßì9RÿýÔÀÌÞÜzM-4Q^ œK&?À¬»xùd·—&Ñ»cŒãVYÁ Ïë d0ZçŽÇ¡®[tŠsmפ·n‚ðêÝôÜœbx$í êgäqKðgBwÞâ¹o­Yúˆ7jþqlž”}iòð¸ò.÷½6M¬ZÉšåæt]‹wQDWYû‘ÃjÎ ÐJf?C’K‹Aof ZUäóC÷¿çʲ$Ø×Ä:Èû¥üŸf4t þ§ÞÝB¿ÅÙ¢ÉÛHB¾?;¹>ÛjQÿ*|;„ééðáöÎ+H“ž4õÂOWŽÌ?´ØÁíÝàt- áÔ¼FTBI&®ØððÙ°L“¦5~©ø:úʆØQ¸ E%"ÞÂZ[I2Ö«’gaŸÄÖæTààór.Å%ϼZ7É ÜÎT᩸íéyåk…iûÖäuK@ã‹[ÚVF¬¥Q‹jØãÍ´4´×R¤ÿÜNq5Bã™ÍçQÔ$ô1n¿CÆUuÁµ ‚l¿öuøÖ+rñç³z.¯~6\*Þ#kˆÖ½3ä© Üí«ƒÞ¶Iü&¹É\ç‡Ó`}e ³ûx³~Ÿ9§d¾ÀN\-\ˆZ¦ñÖ¶Å#þ׺QÎ=÷‚#J§ˆhà^¶cëhVc/Ç}é~kÖŒcNr…ø<ÛULã@;ö&1éà™žUÆÔÎkä|tàxÂÉÞ¹‰»5o£×cȹ”žg‘&¥h‘ü]œä>åc¯q;ÜØ®Ân<Ò C‰ÚTRÜ ø/QÄÀãd%É¥?°øã*ÈÐ*G⎷ܛñ}c \G‡×%0Û”F£ìu˜2c-êÕÅ,N…)“š$b¼Y©”)Kûn‚!l.ѵ&äG/l{½1y®$sÓˆç÷5áío=ðü®<Ú¾PA¹´O#ø7©ø(7k¼+õÿý:·—cë\Ažl‹>]ä$åb@ï(U?þë¿ ÁÌfDC‡Úy³rÏÄ (’p‹=v•,®òèÀ y uüý>,J%®-|pž—MÌ¥àÎ÷ÉœäÎsœÏ,e0I»O"´U¡³º;«×ßÒÀ.¡"øã4†k÷˜€Ãq‚Q¾ZÊ“Ö?'À¯ÍT~JBŠ.ž¹t^õHÒγ1 i=3ÝõŽÓyâDÝ4V“I®GÑv¡.é—€á=cÐçQÜ¿Þ!ðס·á¡hN:.Aå×Oñÿì{W‰Å%m˜;¦mçFA|ùÝḶ¥Áî'0RåïÜœk`\9ŒwVGBãz+öë¢3:МkÅ Ô¤ÂyÇcM~waïÐ7!øªšŒmýrô éàžÉi,´(»n-ß÷›cЏî˜opâª4w«ZfèPë|[5í ,תÆ`éx^¶Ä2Îe¡íl×>Öe×ß ˜*μÇÝ”[Nɲjr!ª‘³+ —œEÈS½(ÒSÚÍkîse¶ÿÃc¥+œÐT?t·²Á,íP<ÅMäwÇ쀄f>è$ÿàIO²Bÿ}"#õ¿ôÔX©ëÉ-Z§+ÿΆWoÑ8º&­Ä-'FKcàÍ÷R ƒÞ*Tuö ¾:œ ;9eµ$Þ±™Ž8çr:«Æàç5˜ð³n–Ä’8Ó*D„×ò§Ëb ÔJ¬Êÿ̽žbš·yz§ûxjsÌЮë(%dz¢,]®-æôe&ƒ^Ö32'w;OWÌ<ß.ãNÍ®D… 7q…w3Šø:“cÉÑÊxHØTB—ÉÓÃý‰ùÞ!ïükáo¥‰÷¸ﻨ¼lØìYÅ3¿ƒMSÔfGÂ4•lâü]r$þ×½«àÆy½!’öc·Æh½{&xá•jCzôl%^?êAµ'Z£­g;¶É‚ÝkUé†ãÇà}Ž Úûеº„øÅéB@õ6ü}|)×ô'[“À†oëq¾Y èŽê„…¶§yíçå‰Å›Z²ãêväîNÇ÷jïê‚  xG:.f÷µ¥!fá{P_ëÃM ¾.JÏ¡çd2„ơ¿ó\fuíp=yNöúŒŠÆû ÁÙŠóúx›[’,B‹}¦£xê6ðØ6}¯f2ÁËí [lLçêîáÖÙ|&¢îÔ5þ<Ê­øMŽNÿŸþ}Ãæ›¸r\8±[àŽ—FBÙôKä÷ë0Úö2‘ÈY…óÖvÁŠv]:Eº$ªÎAý¾0xP Z¹‚ñ¢”z–û7'ë³À©‹1ù° ÚZŸU¹‰§fú°L´ó›{ŠôaÀ¹¶{G«óNß~;ÂúnEö{u2wdñLª|ö79íUŒN‚ûA±[D.ÀÔr-拞̓¿Q¯QÉ ’ºNdÕá ì»_ :*7aËÚòŽÛÀV}š²{ûð܇øKCgz7€VžÛð)‘‹õÐDŸ oÉ?©ÓÜgk#6Ú»tÄÿíU dO5ÜNä*=Iû|9ìi~Ï/äó”NE4¬ý=‘›þB†Í°éÃ-ï¹;·lhÂŒ¼»Š\)öÍH' þ&EŸ©YñÖ¤HŸˆ%£Ý†Bt±$j½hÿp:È-8 C»Ò¡-"…»Þy‚×Õýߪ±ç·¿¢ŽîÇ yÆìIÃ)¨=— ‹®. ³¦5`ü›¸˜')î`cÚ%ø¼?Ëéñìxý|mžhÂV¯$òÚ9Lb8§”ðÙî%wjÉ&ªtÛ,ßÈß~ñ n?{ Qq3®ß¤ÎÍÃÕïžèß…¢oâl–΃õ€¤/àÍuOÈŒböl¨Ö=„½­²,m_4ìL€ IY|ÕE+è@Ë9¨šð;¶%A6ÿ :œäTÂ1‹Ôq êˆeY‰¸vî-ð|ŒvÝ`Õ!‡ÓÈ‚mšL²à Š.èáö^Hà×ö[0ý7ÑÙHí;ÍÊ·q5#‹ß.á\·žã}=:šÛ½·:ýð÷y°R“0ûm¡ùB ój 'HwA·pÎìþ†oB‡Pùˆ÷é• KØñmNز¼„xzÇ#®¢LŽÎ¨+€…yö#çß^¼€qü°glÏš]¼s>œ¹rŽilÆiZ¢üs×@b¨ÊQ¹óY¨~<Üiÿ!{r6€Ã÷!þ(Ôt“¬Ñt£`?ÏÅà8«»«ÅšU:QêÜ#ŒÕ/ÄžW»ØãÀ‡ÐìÞb×%¸gê¯ñá÷6©=g­£á3c2Í§Ž‹®›O Rq<Ìè@¯ÄÍÈÿp…¾š£ßÉS$1C’JM7£OOÂõîPè·£Ò‰ {i­»­ÀëD؃³2ìÅùùè·-™ÅqP½`,wß “·ÀÑšÕÈ¿ÃHšÈ-¤÷<Éÿ‰6¬… ‹ 0xÚ$zÀôþìÑ!1÷µÈÖßÖèUß ·Ì¨[š+HâÁ¡ùü#¾ªÃØëKèÁ8£Ïpb•ª]„–MŠl¹p 7{ìØY¼ ^}7F×ñ_yóïÖB¶5t:oÁèµ_ˆÂÐîOÖFnï×\ÝûŸäÔXIú©8”Uׂû¶À¯¡¬p•´nfÏ|_ïÎ3xïu9S|¿z!{£'ÁÒ-~NÁ”hüÅPþï{ ïŸö\‡Š éä|I%”éìã/±Ð¥¿$Ç’Mm‘ä¿™ (•/Øðèië½7‚gÉá®R:ìß'uLÕ\†]nN¡E-2Låb˘žÖ>°GËî?­fbØ$Ë{UÑGš÷eâ’Žô€îwüOßY®¸€5Ù©1-ó®©­œ+vÛ†ÊÝ Xžû‰L.\Η|ƒ§ÏaüöX¬ÞÜOX.ämöƒ׳™†جkB¿b¦µ$,ìsa›÷€¬eÆôT‚¦"S‘ùÿ’téõ¤8Á°‰YuZÒ1«Õèq¾¹3ÑÃÁ4¨Rж]ó†Ãâ1x½hÓV›Èž4i€BÄ\OM™íŠilÇýt²ëš!§(”‹k~ɰíÏ@ò¯ í­‡Ö­âÌtcMwù~¶`ûw/[þÜ §Ì•¦ÿö PA8 ‹gò Ѽg<•Bë Ê:vrLJ}3³Ý•·Ì§wRYc‘$•L—bÕoùÜ Y,٩®…ÄÞìßé†Z¿Ü˜a.cƬœÀÐfÑ•e{i4WŸv–§›Ó¥:ž›‰7½˜†Î 'PÛ¿Ò}™“1õÔ²ny$;—r¤ñ¾k>8…¢ã†Qÿ7 UæBü—Ê€>üQÅîqS¨ÑeJë×3óÊÉTÜÁ}ÞWS[ËX¬r„}÷Ìk·@e«lÕéªÑ ô¬Î?X^²š¥&š@Zư׺€Ú3ÖÐÓ-,3P†iÓæê4Ù*?}¦!oMÊözÃï "i‰Ë‡L™_ˆÇû(Í®F Ø¿{®!ÆhÕrî8u“ ÜÖo…)n¢ÜO¨¿;ºÝHW¯axkr,X)[Òñ'ãá#PyfÜÖýÎûºm2µ­ 7c!øùZO}ËìÖÞÆHYÌN €ëß ñM^&Ìùt·ç˜;u_?€Kú®ÁðšüÉÛ׳{S´è&ón'`|H[DJwÓñÞ{a¶‰ ]Þb†Ï3÷¢Ÿˆ󬔀ÕûÜ©¶ÀÈï†=c˜Ùa9Ö·äÙK×áæ 6~€²6ÓÝ:+UaªÄ|vÙCŽN++AûRUr.4 š¯_Aᢩ,ž›û¿óß¿?ðó›p;p‡ ¶Ip2q]œ~ë ±3Ïø ¾Þœ„@ÃlrÚø(Žz=¤xJX}G†í]ï=œË•Å•Ë ±Ò o¬}Œš ¡÷THõ.áTgB^¢¯{ñ6XÔoO}X‚×b+¹?Åàk¦{FíÅMÅpÚ_“±;5hª5øŸæT·tcCLnzéæµøJ‹Ã†’Xðr|7š­"Ù“¢˜×0·š®SîÔq{¬õi÷Va´=’E®E×Z§ ë|Ej³# Ÿài¸}ë=èœoÀ[™çXæ} êShJ¯Æ*Ðïs¦áæê…ا3K.ê‚ ä§|÷N¾¿ Õ? SÍzi|׉:.W8¥®Œï$È9­¦§›ËáÓ”udu¾!NÜ×J=^Ï W_™S¬uo *§*R§iSÉ‘g½x±µ”O¾…k)rt—ƒ÷aÐcç“Ìgؾa€¯é“D/ÌŽ…v?10Nh€ WöQ¦t|Äþ2¾w®I“SSäÏþ£?çÁ\þßL4^¼F›¹àÎYŒ}vÙ\oâcú˜{3!¾®¾‹"yƒXÒÀHT€-̈á>,ïõ ¨qÎ^-„•ù8çK4×{(†å–3rêÇ?R;ø†¸j½áloñ3‚£èÁ˜£ÜŸ0çt¸œ$múÝ^„‰äí"®OØÀÌÓàú.‹›)X ïmòq¯ŸýÄÓ¥ªGìù&Mõ0”2º¥Ø.RÁo7ý‹ÄeXÌ2Iøâw¾¯¼v±àÎNò{OVÔŽÃc' èÚ¹÷¡Sf,»4HGð¯ÿ™ØáÚÏÙÔô¡'šÜïy«éW÷U¼ ŽkHŒÔ°öz†¾ã´éØ¥ù,ukýI¨²„&˜Ö2ˆ¥­PãÃ.;Î ­1Ö~ì ùõBw®1'g‹Fc´ê9à/y ½_e¹l3)»s4=–ÉL$Ìi¶ÀY²nWäé­‡?ž?À¶Á€^T—eeB¬ïô¢ÊyºÚÙXì]±½(ûU¯á¦z>¹ë}‹û9pW{Øòõí\á×û•¨þJ„9ôäbîè|ï_‚»Æú´Å‚Ôîé!¼ë,ÉnîôƲ¿ü½ —q´¥Fú†â›Sb¤ÈÏŠ*€~výÍŸý²œÄ÷«Q¥æŠƒI-(›ÈNßLžß¿ S¢N’¨`ÊY ÉödHvHgg5ÓHD¢Ûßu6ùöóÝuuèé®0И{–/©ïN¦®`üReð÷:ö_oAÐLQz¹Nˆx>žÍ„Þ­À¥ÕÖ‰Çs¢äyÍ!¬±_ !Ï6qLu+Ð,ƒœÑŒÌò¯Å¯Nܬ4ŠzŽYN²ÎË0ßÄÑ:ªì³Åu˜+'OƒÖÃ[±Œ´ù×éͽ±´b|2y,Éšë›àñ ûü³À1„ž7“©ÜܧÌTÔß‚ve eÐÇoSíÄH­óÜâñn¬s×7äeXSY'{2ÑÓ ïŠ•ÒWü[ÜãOù¸åþ?n¼ÅØ´6¼•]OVmfzD‡™¨Ç³»RÉ\ïôÏx(}:]š»  †ÓÛ 7¸æ”¬aÞ&ÚwùÄ&ørÙ~'¹µvž4auþA lo ™Î«þ3‡>ysWÉÚ²WÉ"ËHŒyÀe©Ó[OâD™ôÕŸM47Åfß}Ž¢O‘•£éÙ]'@táyè/ã”ÞX±Ïšsñ¸ž žEÞÖÕŒøÿîÀC>9ë?Y¸`T&¸ôò`Ò5-HP¹ ®5’tò¸HNîõˆQP`C/Ï¢žö>.Hñ—Øô¯Ÿ ç6¼ÃqÞ uHžÞû…ógš€ê_Py±‡7¿}\ÕJFå9šxìÖ6œöû ÷MB…zJ*¡Ô¯Ð)ºÍEû%‚¨è;,¹þU ´ið¸Ã¼ì¤Ó…$[TØòЧ¶¤~´ÁÙçê8úgýpßËÇœ¸'du¶Û0ÞàcÊ%AîYlH8†Ó'N¦&WýáÃøÏ!Ÿ›Ó¹WɆàwœù =™&²70Ý£e$ÿûâ©j³ø_¨1=à¡GÉáL¹®†Ÿ£ ™îBG’.[ÆëÍò¦êÎìbOw¥í cÆØçVMtÖU!OKöžÄã“E©öÝM„.ͪd¹µûXÒñ=U‹P&Ù‘|éGß‚ö=˜Ü›_/A¯èxíÜ “sWjàaAe)-„³ïŒc5«\@¸úñ· _ÖÃ+=OîMÅ h¾Ú>AäoªìÉ6jÉÐæ½ö\Ïы舰øŽ”<0ÄöÄmœÔãj|yÞßç…êøhø³6•;ܪ [þ÷ýgÈ"h£BTÖvCxb6dÝl#mS~qéa:YE]WALlö^Oä&7pÇûnB¹]té T¦¿Ê¹¦tù9o”¹>•<äÿƒÔ£›Ëšƒe¶§19ç.¨&½%õ–|°ß•5zyC;¤è™‰`Pôzf6¸Þ|W&»àb‹^Ø-í:/BáV}qº6‘go7K 7ás£sáy³Û¿ñ$ÿE½I"túÔÛ¼].ÈÌ-Ø1“HVá>n–Ùó3vmAGÁBròEg,c†OÞᮇR‘Ý9xm¦3.5ÿ4ô ž·‚öƒ Ün3ÆÎ÷€”Kƒ¬'/ž=KÏcÉ÷ްüå-Ô|Âxv쇻1á ,ùvÆAßõÏäGÇsœ?%ý £»Ç*`«ý:û„&TmE]Ý•àg¾gÜ*DdV1ÍyyÓÝʨäBú@p>ü«bË&Ï…µ—ըƣ³ðdñ¦÷¼”>—¾ß8 –Gã?‚Kß:+l è-c[îã£×¬ÏC’ñ´èôì8ò¨ä>s*ø& 6÷Õâµ@—­›‚áaü+.ቬMÒ݉+ ¬é÷ý/¡Ö,ö‚‘þÿìM#&º˜Â\§%P²î^ÕÒ¤ƒi¶Â ê¿UÍI~,òaKvt“ -|ôTÐ'•"èÍ¥/€®0¦šÌ„­:÷æx‰“7è³úË[¨Ð­bÕ-€ Ý“èžé h®®í w÷‰ix€½Cmlë¿ ÜŸRV§ýÉJÌzû.ºìâ¦vPVáÈÔ?£©òñ׸±s);h¿U…özOQÝ8*rF ãÙï#»Øß럇_[e[miÔT!Ùµ‘éTÇËÒtü»» ­ºœ]Lëb“ÁsûCN6ÈŸ7o@çú—‹äÑ÷òa<Ín£ÈÐÆ*EØÓýv,¸ÌœÎ½›º-¨ Y<ñV݃žæÜ‰ RtÖEv0^éÈo„+UÙ]ma¬y$Μ^Gr1þGqëFænÙG]è“õ‡@MA˜*eȲë‡Ï¡OÅCÎiÀ“5‡ñÑf¡ˆ(´ ßqNÅ­‡¾;dÉZg& :ì{#W$?„SKÙœWÛXrÂø¨iH^ü&Û¦ü$™ ˜Ô‡cXÞ§û±xŸ6I~2Õú¨Ã.~+E@Ç“'%áÿiFsVën‘sZt—¼>äÄÿëÚÝäÛé\¼¼}>îzfI[övòÒê2è§y÷I‡ûQܯþ6­o…ñËÝØ³© QmM THÝÁ˜ÆAn˜1ü3=ÉÅ‹f",Æc ÈšJá÷ö2Ø·@>s8‚S¦‚7výØ› ·VBÿCq8rc ;÷…ûü> „²ý(?.”›´Ç’¾½3w ea÷T1ú®ÈU°ÏO€L%\éS+ Œ°´!Õšµ0E¨ì …>µnãÀlvÃéZ$ŸjC s?ÃU7/ÖÙCg• JwðmVÅmï¾¢Uãÿô/Æ÷\ƒ!zä·çT:~ËD˜.K¹bu¦÷ôjëO@ýjUö— 1ÝËÉës–ò*ާN qÆÜdÊç-§wŽ…æŒrèî{ îù³±Ïð}T(»cbG_¨‹Ñ]ûìQ·8•MŽñ¡ó>ʱܞS QnÁôšƒ{׆ Ÿg=úò=¨»Òfvuáy&6µÕ³æÓæÖ*(hœA½ªe©äŒ<˜Dò`ÿÖ 0߯•¥^¢Eï)uí:ù×ïÙ8ûàstüxœõþPd¿xÁáô•ïYŒ[×MV/ÝF§ÆªÓO3ÙâeàRCFâ¿r§ 2£,ÜPÃ6]uÁ¢G»hòR‡ÿ~@¯;ØaK…ëe™ØÞ ðnóæéÕ€÷nïÁÅ¿P­ÉíœKMbèý¸‘ø_X° 5lqTX&ø['ôOaWo ²-ÎKyɪ^Ôßb »< R 2J×£¾é4ærZ›-6fþ®8âá2ãWS~º›˜/B{»Ñ+ã.ú?ìÇYwDéË©)Ì…ž!×JsÈóQ¹x@ý¹‚3§åØêGÉp^tü#Îì“n%Ñš–Å’Æo¥/ï;ö'![^²MǾâãŸ9æ.‚‹Ž*ã3‹TìÌŸŠ±"ÚNµ/„©þ“—‰3éó®$¬b~6· Y/ÓpyÐ 8WI¾IoAQQ¸m×Á¹Ý!è•=‘^ÔJéÿ• øÖ›OqrV c©B0t» Í>Ó˜Ûr]´2z‹Å˜DöØçíBÀÝYÄfÏ®‚5};@"ý ÈÌTâßþ&I§¸°»/¾óü6øÒ_59ðÚð 7åÉyª«ô=™RîŠÌ?ˆÛ†¯•© €{Ý€ÅΤ^™ò¨‘?„[613‹þãÍoÉ)>ësçï1SÍB²4ŽAëd´¼ÿþEHâ™^Q–QäL‹{7€ìŠ2žßOià"×°Egà üÖ´d%;ŠÊë÷78•õ zLóçgnz‘ Y(DgŸùß÷ÙH*àñ9‹Ñk«•$¼1·:ø‘õ§›äH?—Oǰ'z¬¶v?iœ-HÕEðͬå@¦ë0òz4ÿþ(»–AäÆ7°‰’ùÙD%MNÛwVŦâªúÑØumoú¸­0ÕYŒé>Y‰™,®¯ËG‘¯Óq!¾…¥ãŸaÑÞ‰Ôz¾0Ü“Õbdz>·Kí,Vn”d>¯gá…=q¨lhöM%˵~s_žöc“ºé{¡ŒsºRáêN2Ӣݷ%iØñ7x•ÐtiiL´T§ Ï3…¡©²L¥Bÿxlâ—Î8:ÒÿG÷ÊÁòiÝ€úÖh öŸw³í§¾#^Ü~šbT%þÔuý›×9Å€dÜLÿQß×ãûýßÞ[Ó(mÚ¥Dãu+©¬l!eFÊ(JF*¥¡¡R$Q”ŠH¤ñºÎÊŠ²…BVB¶~½¿Ç¯ÏŸ=î×=Îý¼Îóù<÷}:·!]^‚õXwG‘ßVíÂ9Zù°Ì¦§kŸÆ©–ŠðúçQ÷Ÿî²§J>ð¡ ¾G§Ì=ŠçþŠBdœ&úɽ"Sö~r KGÉÕÃx¸¡1•ÚÜxa ©Xß s½ä‰éì\ôh¸!i2¥Ûä:ðç®l¶ç\O+…ñ_Kpß­¥ð|ƒ6oŒx‡röëÉŠî|§H<Ù–¼ $_ºçò æx<Ò1ϪTÀŽ‹ºõ—?ÀÿšÝkr?ùÖÎÂÅÆÓÍæÁ¨Ò 욥 ¥Ü]r"SŠÆBò%sü˜x›X­ÒæûW\Å˯‡±ÀÖÔá÷(Œ¬!‹·+ÿ†Ð‚ nðÞmÞýG?IÑcú`¹Å W>zŽæýÈ+µò‘ÙÞxr Ï&C[µgÑGêÕè¯WJý÷rø8ÂO#=0w8/zRD÷mÁo«‘¿áÚz]œþ1ä× ™ˆÙ:üè,Ž ùÅ­±Øª&É%¿¨Â¼ß¤'ÈfVÇ݇ÎÂù³Ý(t0¦:mf8ÝV›{][KgZ˜ðß3#Oü׃ÅÛ}!àÅNn9ïû°£'¿»QÂû¨Y\ÇbÍ ú–,ÿý,ž.ߨË]êPzyðL>üTÒ'ãØÅo“„•£øL <¨6RÂü «ëFèFòüHÄ#sÁsy:<×ç3­^b׸YìÏ©q|ï;||ò q¦Õ2¼³ö!NÞ7‚»_= 59ø¢ÑŠíJévVÆ {4x=OÍêÚ!Ú#;lT¨ûÓ[ ÐÊôïN1'ºöË>š´c-(ð¬lKd äÄ…;4éÓ%»à¼A¬K/Ž6ÁF_C:vK L×S¢¼À¹ïŒø=ÏdT nÕ}ÌòGTæyPƒnOìÄÅôQj ʸ&ã¾—edGÅp¶p¥/æüÀ™ù¸x•ÍÀú±¥ÞвÒù`2x6{Ĩù* 3s´U%ÃÒ«#èˆïï`çØŸè|¹•ÉÍÄim€/íF¡dä.×ÞñŽÜÀ¯×ïÉ…÷O“•¯Ex'½.´µü®7†kž›ùŒš£p÷Bj¯Ä€SßpsÀ¼S´_c2…¾N»°xé5¶â€%ï6 ¦&÷IÈÓ9\è ÀÍ7ü„‰wÁt³d£LR1ä |òSÀÿß >cþ‘Š·ÃÁ3ç.}N€Ÿgvcߑü®V—×ôåb{Bò†ó¤õÖ®^&NÇ©ÝG©¬µ-œÕkǥ LÏO™§ËbÏÆzAñѵäPªMuÅ&sØ9îôò¥ôäØP¸´@¸³5’Æam„&œ²(¢é©ÿHÁX1Þp£•ÌÛøL"Kðf®1vþ­8ü¾JÆÏðÄÁͧqR Jì¬Çª“»Ù¸&#š“Þ ­ON°3âÿðÖkIjùs+¾¬6À˜Q½˜?2¬ß%CýÖS‘9aó/~ïs'ën [Ãmâ_8Ìæ«bï&¼~aoW9Ê¢¯6¸Z(ŒæÛFlƉ¯ƒŸÃ Ôòhܪ}••ê7¥ç§àE¤?÷ÑõF½­ë1<ÿ#kI_Æ¢ñ§ÑÂÏ*¡á›åoB–á`¼ûΕl–ûAíÉÉYaáâH˜Ò󂚤談5$üû.ä»–cYüL5Þ{ã ƒZb¡²\f²SkO$ÎÚSçi& w;{jßÁ+‹¿ ?Òõ“\øÞ—Y°@„‡$ÞÅëì-v¥(]Ùëù8[s´¿}Œu&OØÛ¬k¬lûâÿ¿åüpÁ'͉ ;?—…ÉßUxÇ9¢äúýæ¹€WóyHkžÎ¼›Œ yU§%¬i¶÷fm9êÑ–°ùh ¤Õäƒe.|vÏ]œEü`fD#¹)÷Zwë@´œ/_ªvCpz°Ï…äOðtg¹óô ^^]‚íË£¾÷-T<§Níe½1Á;xtíaàsµš,Ø)§‡°ÌOŽWÔÃÓ Åx8˯}Ÿ¼Ö°Hœ6Ï…mSEè„{GA:v/ËA§–/ìØO0³ºçÙÌãSñ¸KD‡Ëû9€ÿŠÕA0zl™êQ‰®²ƒyYÈEìÕ:íòfî+áµ—IÀqiLÖª§™®Uc{˜ÍÓxûFâg±¶[>3ýRWPµþÄÔ‚†bð’4œ;ó‹ bám,ú4ש¡Ã5!*¯Ieº“ÚñѸAÞ³}°£¯”•êÈÁ‡‘ëá‚ÓCÔJ³§ßáGÎøøpÎ[¯M»ò‡³Û‰«Ð¾Óî?¨bïÇŠòùÍõË.§]轓š÷80¢P±WOŠ‚mûœ MªQP±Cnèìƒ7›¤àçÓ£xøÎU|{jàù·ÅPITé|"Ôº—¬ nª‹²­ ôßCíê{áüÔݨôh‰àµÁ8úÀ§.÷㱬l],ò‚ÍòÍdr1çà»ÙK8Õ¬Aeö¡¢ç`º¾Ï€Nþ~‰]©ï´ÙüÕÒ¿ÄQ>–O5Šc·­AïébØ}¾ ^l¤¯×lvËi 'ÇÎG+×ìµ¹/w™D—ßåçf² ¡Ìª-°ì&n\òÓTlxLý'˜º²ZbãÁþóyxòu(ÿ2Ó“w—Äþ0<,Ùy¾jM¦ç•¶ßaÞ‘:ÖÓ4¾zÊ@ÃÑðø3£R ™=‚ƘÙt·Ï´Ÿu*^À®Ð©pééÈqÝÄ÷)èp6\šZN%5£èÅ8Sø«“J Åý)stä‡güÁÌi‰pMú º›œ¥/f³º¾Ë¨º_ŒŽÜy Oÿk‚P½Çp#ÏŒ‹v^ǦK¾ g4›­9Y©WTùïúS¬ë&÷š&øto;t|ŠgmÁwq™\9A«;`IÌf&SgG§þ3¥1³;pÔFOz·­^ªÈÂWC^ÿh¿¬Ö=ÝO ·ãù{‰¼Ám’½cÏðï©N<ù¸_Û@‹§ç0ÞF‰7«7ÏÀ¨p7kê²Z•ïèhQUX2Y—òr)þUþNöF?Ù.,ÃJà©Ù=löVâvÿÂaíñ!<ÙT†~ÌXÿ’ÓI{Ù2qisb×aņlŸžÃÖ;þN8¥Ï]’`¥ZîtÀu¯dȨ́vüîÏ8S^[§°ÓO›í¤ rr*ŒË· ÊŠ“éîø0lÎ6âSžÈ÷plæOvöb³c t¬ÖHð[1Y¨Ñ×CÞ¬€¿ãÞc×*3HùmEo;îC%ƒàóçâןÖ—Žþ&gà½Åxoð‡L°Ä¯—gcÙô)°öG"œÚ? Ökvâû’S¨Ô¤ôHûñcê)|—‹ãê%„Šl,Y1ƒ‹0ß±…Í á'oÀÿÆŸaPh%¿ßÙ³©;¥¨ü¼m˜²RœÎ¸4‹'*ŠÐ)öT÷åV ‡‡IÜÈ;î·FðÌù]0Ì×–‹m^ŒOv½#%„Žª×à«RÕøª¤çøqÖ3(²½.”.¿ß‡Zó-FÃ^s莑æZãléu{9þðÒ,ÝS Ǽ烶©3þl‡½« VT ¾5á{k]{çàý©ÐÞc U<ÕCŽÈáè§pÂÉ›˜§.d—•ßchöDî÷¤Ÿ»î†ïU¨|?’\k®øî…n‹Û1ùDRåïðTÚuGñš}ùáçAÜBLص'Žä[3PÙ{?#äù‘³…ãjÓR^â¬òÅy$¾T5דMÝóXd‰—¶QåUOO¢XD*Æ´}xnç»¶ÁžE¹àûΗK¬¬æ®k!á¯R1Öô4®Ï;ñãFá9—PrgÏ8êj ›;l©å¡hmÛNÞ®¼¡á‹áeãh>md:Ò•Á=7v2‰Ápq¸Ì™KEIò‹ËØ·Òìê˜BahH$ý½C%vï‚'ôx€ä<ä\)\l¿꜆aüj(ÿp ³T6Áþê¿X™ÙŸÚü;B'Á¸/rø@„¹Ú‡ô×Ççî’A‘ºØ››,\VzŸ$ÈÒ°”ÏhZÚÇö»©€LB‹Ño¡ys3 /Lâï/FãðBpܼÅž_g÷ýµiûÞ<4›8 ½¦Ua˜ᓊ»`ÓäÁüªýv4gÓ¶zñÝ*÷ -í8Ù¾½SÇ9ŸâˆC´Õ{V[NÂq7)¬Ît¥×ÿ\ƒ²“‚g2Öl¹6?Îd¡|„ TÆO«gá˜Ì ¼”~Î%±I‹vK±¦<çØ)öæ…—ùVI½}ý¤«7÷C­®9–.µ¥îÅkðO¼‹"ÑÚaý'ÙïÅÖÁ%þðø']¥Ç_Ð0DI5u!çB.Ù7¥«J^€Q¬!üÎ,Ø¿Ñà²Ò™:,+ÁÞû†Üäähr Ö-ò‡i݇‰wâ Þ½ñDNU£’nfðìV;:O¿(l±ö§5çÒ0±kœDÙ{Sl…šmoAŽî„!ís©–ÑîDâ¨ÐÁ™ËKv ¼É+¹-]2`ùæ÷0î°_¶`µ’ì$õZ.`÷C…ûé'ѼÀ‘PÛ-M#U:@jþDþ~´<ÕòÎBë‘QÒè:ÌvÀñ¬FvÜa>Lø4I±]Vg@tú’ÐÞ‹ºŸäI™~¬ѦoªoÀ CN>lçvÜä>š~Õ°)‡Igøž|Ôb·‘}ù2zgÆS.°K\…³ójÀ(S™1¼Ë ªñÆ’á0-ÿ ³þeëáUW¹0©ú:±Ol&Ï»oõN­äï±µ*åêÛ’MnÏ´@•a~Uä)Üs*&2×âÜ<¢à¦†“Â|É‹úmpW7HxÉ­§_OKºd¸kÊHj¹g.»^†÷l[1åô8*/U¥šï¸Á1ã»ñ{<«a“¦\!+Nôâ‹è?l.(€¶ÃzÉ{;hëéÝw÷Boù-<²N‹ª|ú‰ÓåÏÍÅRƒZt/Îc¸1É™"Ã?¿¿ [:…¿uòÈë=ëÑ8h,Zý놂ºaçli/]Ç7?YK2¤ø:Óë°l¬M|á'µyÛ0Ò©‹3÷Ã_ÕSlð…'BÝö 0~‹;5Ü—‰+·<`â¿z˜Ú I¨JWBÙ”_Ø–„y#ÓÌ?Ÿ±í[-~{ô@@L ñM©(^šÅÒÒ_ è'“íí>ð;·ç¾wâw«*Ù÷"ñÛß»-ô”¿—%$P䣻‘HÃ[߀ÏîPÜ~í=^Þ¾œZžY¿Viíñôç~ó’ád›9;þ9¶Oúƒ2ÇÊ Íô!ž?× '›dà@ÞZÒ¿®…Ó鑱§©à½Šº-¡s–‡]³§’øÚc$b]7\F$Æzï†g/äxè=~ÝF‰ŽûƒOÖ á%Ânì[¬G‡†ìâžsçÓ¶ÙöTúàeT›Õ}OÁ!|!ºí½h·GØÁŸcx@5­”ÅÌêÍø¿h,Ч³bN`’«+¶ñÎ !á×þäå^|¸h/¶Ïß…éu@«%ËÕ ¢ÂO áöëí°DÔ/6æâêmÐõ[è·{ÍŸèÂf]1æqÓiˆÿ`š¶ú¹ß•Ï–#î5ÝX´!_MЃ¶eŠÔ;È$£;ñL~' »=gÎ<’ë¦Ó·¾ã>å38ýK*ð3áX‘¯}Q ¹[/À Õ=¨wá Û8Äœ…འ‹ý·“ø§ÒÝ›G’ˆ½Mlâ‚*ðYXñ]½•lõ–ïp'úîþèŠefÒj?À¿s'7û›Èç<†¼ŽëÐV€ÅcOÃÇéûaëq‚F~®!‡0-¦”ÝžcÉϼO‡öu뉅h—ðŸåØÉFðK–ñ¨W´|¶*Õ¨òy­¨ÚÛÆçݗ¾–jf3[’ÛØhÓ½OWò¾–1ðmù0"6 xvϦ<á;Ü×Ìf•Iúø¯ò(bOFa­Ár°ÚRþ¯½ÙÂdKeZôÁ•w”Ô°GõÖõdËiOºüž%_½AŠ×|­áúãò D" nÝ—gZƒ°}O*N<_ :Ù }ã(ú “a‡gíÆy^uº5‰(}44„K}ú ŸÚÛðjîK˜™ý ƒN€Jl2Ä»ñ㾺R Ä7MoËü0h”¤°æî2¨õÔ¤SjæÓ F¼Å]‹ÎS&Tóu1„~ gO!©òþ­E8lZ2Ï(PdU˜Lf’{ýØð£ÚxwÓM˜BŽ:ùà ɦý;ñ­Ž?Ü@®ÛLçyï‡P§Ë"0F&\Žß„;×iϦitaÓFXéù_d¸ {§9Ñ“M“©YÕòXw8Ïy?Œ-ªÇ«¸Ù3ˆNZÕ$¸6[ƒž:ÚjR“÷¾üƒ´9‹–Ç¥a|.ÝN¢ÊÁî—Õuã»ò/Ó¦*-lötáaƒeyü¿›®?MhBËw\°´ŒKOçý{M¯þ7ƒ¸ñãš=4ý\;mWiÂÑ2qðR˜‡«ª®°ƒoÁÞk;f¼ªâ†AU\vߨØ}˜N6…ã÷RÕPs:¢j ܽ”Îkþ×ÿ­"r‰mlG“}Éu£CJ@Ez5n.—¢J+áþs9~¿è^QÈÃm§Æ¡ì®|ÚĘnq¦Ýèm¹¡4s]¹0F;U¸ùÂ?hºãJêD*Éšž~oØæ‹~‰—AaD^_”ÌÜ[Üé¶ úœ™o†îÊÐYX‡¿úk楋ºàúÈyìÝ¿1´y|0–b&î¼v†­qì¯%ëÑ\â>^¿<7D“ö7Y¢£/*Åu‚¶å=22q#Ne@:¾$z£Äù¾Dþâë>¶åª6ޱ˜N¯Ç¹cɱ5Ì=PW=܃©OÏ“3û»a’ªï@þ »_ £âo°ïŠ£ØÜ¦µ¬ü£%ì/)B-Ë`R IîæÍ.ï&ÖRÅäEN;öùNæ½YÉhhq–,xãÈ­µEÑÔï={§½WMªe§B5høI ®½% zÒ¡÷³;[Oû¾’êcÉÂ=Wv£¯æ)Ôþª@Ë_¯ïràQßx‰zäû`)Ð"žÄFǯvrv`_d°ÅÙUŽçÕƒeÈú'ÂM­ŒN(©“»s4t§ÑëApU›?ãkŒ³<'P;£G‚?þu ´»‹U˜ÈQ/´†ì#ùx¦3£yZqÆ@ügr‘À›Trçñ¡Öâ‘t¤Å…‰Oáøl%Έ¤Z6«`„ª;•vÖâÑ¿nÁ™À)T{¬4ÿðè2d¦Ìƒ;P¢ÇœÈéÏû(nÏÃ¥r¥4é§úÿýþ˜†n†Îiz(÷à=<Û4%•ôøhÃX§š ëW ø®”o¸æðp8Ôw…h§®æg¯SXnT”hUÙh|ºrãv wE6¯B×´4â.÷£öIÓ+oæÒõ}ëá§Õ70ù³ŸØg¯ÿú\V},nˆ‚­_ÆR•>Z½R{x|ŽÙKÝvïJMŸKg‰ ™ûˆ—º€Ûÿà<™á²ñºœ/Åv‰ÒïÞaä¼þ÷fåîæb«ÞÃâÉe0VDÅ$œŸÉom'´ëÁ$Áê¬j8ðpËè½:ÀŽÛè,!‘V¦X©^ 7JS!fF1™Ó,*TÔÄ.ÿ[‡ê"q‘±‹Þó§vK«â>Ä.;KÐ9&›>OhÂ!“[O·ò™ÖF|md&í^WŽÛã>àîøv(×’Å5dz~&ø­ºøó@óµú†ôá¢:i>­v<\4oÙnEžÌ=§†r9ÑNvèo'“í]Ä<¬½@»è0–5)ÃZ!Ç<7¥¦Üzþ7ØãÙE7eBšU®kf>‹Š0“Ô’Áôìo½ÅF;$Ð3¶üù/jgỄžÜç‹Yèû`XáBøŒ‡qð|&Áyæý6å<Ùí {T‹øëO_É4ñóðã«Þð“$îBÓ-YŽÂ]P(ùÈÖT*rµ‡×=,…ÝczqðÓþš§ç!,¸w_¬½ÖË´YÐÆh˜ç}‘͉ª'–dù8õËx~Q::¬ÞJŽ¯ËæF>áƒ}ïÁøÞ.>áô[4òÜW/…%pHN‰÷U¤3Ý ‡¬N§\êr‘’i†hƒ“ìç ø¤<îu“ðÉ‹yòüšF0n 8Šã ähõ­.ˆm>Ž‹-¿"þXSQ@Î/üŽ'—¹ ðÿ»œßðõ¯ Œ J€îo;a¥Ü|lÎÿbÆ»Ypº8žJ;ñk ŸàÌÑý¬vVÖÔàIi~¤¯n èèåò›ñêÜlú2ž­;»}aÐÍmøãx5æžV#O#"f/"*›ZAÿg>Í›§Æmgì‚GR×±ÃÄÐ5Ý6 M¿nÃŽÏx¥œ\|8•Š®²`u®­¨õK^°'Ú‡nµ¶ß¿¥ðúM4Ò‘ƒðÙ3)ºåõ)î´Ã‹Š\1ã‘ÖCèûCúñ˜ õžðokˆÐ\“*=;’³T¢Ò¡Å‹Â%øŒÑ]0ü‹ÏdŽ¥ëí¯85Ê4‚ÛxñÍ%è¼0ŒG¯££ù¸%æ;ælšÄ ž¹Áe—TúÎý.úÔš–_MÍ<Ú±• ìÕ÷лÕø’ÏeLiüv:|ù)ŒZRÈ}ǹÓäý[¹·]ž`qà:Ç)«øÛºœ,•¥R“¢WËU6Uj'’PKÍqj„,Í÷ØM§ÿµáå{zarçvv¬^…ž[s€¿üð²VÒ é6·—¬®ç>Ïl‹­ÄÄx]aêDM¼G ¯ªÇ ²¤+/Ùv %—Îà³Å×…¥qÌ´.Eµ™Ã„/-ÂkÜ~ÐÑG(ܑр!‹qÊ…`pOpÅY¾OàÍÄuðëg)3]’JÅ ‚ÓËØu’“Ä ŸN‹Iß&ìšZA_ÕQ0¶q¦ß(ÒÇÍT5=˜Tg‚ï‡o %Óš¸þâÔŸ:Ÿ¼ƒWtò`vþ¯ÿ炯ªôû¼gwFׇxR× ®!«sñL_.,]º]5ÿ¯ž÷¸*@—jüDéÖá]ªCyTš7­½ã[&7‚žb-jÁp1¦ße-ÉÒ¼tîìJü_ÿÃ÷]ÂZ6üUw4Ï•§*ÕhSû žgûfïÛÑ©FüWr¿ïOeàòϼ¿VY ƒUÅ`Íü:ßІJAtõõðjv…eà]¸Ì].}Ëå}@Ãv¦á·£ñ(þÕ‰IõÙbâ™R\»Ü†.þ\‚WŽ£{—â’­˜f9†©45Þò ŒÖ<†F{ÏÁK™¿¨5v¿¹òy{L:üŠ?_Æ ý#!ܵ¾ŸSrþ»6þA`Ku¶4zÛOؼ·Ÿ8<#Ÿí烆«½Å âs`†‚]A Ä¿ÅSBƒèú=š5ê±)‘Þ²9ŠW÷ªr¬ÿ>U”‚à·xò9f ølÞ!Ÿâï_±ƒ§Ò<±-<ð§}Ä øDÙTúÉÍ„ý&OE¾F)¥¥tîöP,rŒÞŸÞG®üC“†Ã1uê¯ÉáQ)Á<¾•‹MÚôò,;¬1ˆÆ¹~É;:‰HÜþa²uÙ‡E`]q4ÍŸÄÜå‹f<ÇŸÎÉÜíäzgÑM|wš_ü­Þ"Džv8Æ#LâÁLåÓ@ü‹ÑÈA& °›JÝWa·y~ŸÂÑ6qÙ}Ïà[²‚žç{aÁþìvl¢‡Þ(ºé‡ŠÐö”1\l£׿ö”¤ ›Fük¼ñ¥î$Eöíˆ×¦Ó"íbú>nu©¥X¶g­ò³âF©½l”¿£â!*ŠèQ­»­ôRR%IÉý çë>À–›±ž­È}G hÚNнq8·õ_LTUéÖ6yzÃ`d zþĿ‡0 ‚+‚‹P$_{ïGVd‚ëúÜ8¿•»ŸÇè¿·D­HŸÚ»zÃ_Ò5_ž«ù>ì«3{[ÇŠD;…‹½ò!Zç9a=^ 5ä#uZp>¯gãû:ðÆ° üX¤8ÍÙIðžÎlxqb,X¯Ä¼ŒDñ‚áü‰M¼L&Ž’ñ´Uá,*.4º…;AmC<ü<ðÔÌÓæùŸ–SçØ§xÚÿ=›Ó¢ÅsQ—×K»À¤ÍpŠá?(y7ÂoÌã3(öWEø¹šH"fãéivƒt0ãy3ûá=œîšöÊuƒ½¯Œ¥+B@„¦‡n<„uúV°j ÷ºņ¼"Jg\GŸÃçÖšô£×vD ü(ªÅôóÚÐá<´±¬&ÿ×u+ø]Kzu)[á ¶o´éܤXþáämôœ=”?ô´äŽ„bm– äªÓþã¢n‰;]!b+ýÙŒÍˈ®G$wˆà0÷‘0õü'ìÛ=l|?/ÍÁ—•—QoO(ßÇÓê+ÙDu=^ø ˆ/îCÍ«á§ôŸKbdùèMžúWù|žY·ŠMq¹Ä¶BBýh¼™8‡ûµBiL/þêVÂz‹³`ªó“ J±d圻ݯG+Ù(Œ´Â Ñw0ÐãÐj˜)HÙ`³5¨ñF þÎ÷eÕ¦³ÛÚX¥¨e=ÏàIê3Ÿ\Jÿ’ McýzR`Sx „)ƒéÙ džÊRr®w!Z…¶ ÏÆRÜôÓšzØ· vähðgz¢=ï\ïþ; Ö]ÄkcQø‹åÓ/Îâê÷+àRŽ$ükÀ¨q7PỶMèD˜‘© I Û‰OïNˆ’ú‚Äâû@ÿ³Ï!Q4y; ÇyIȸtß ½÷/0§%ëYèÙYdpÅ3}ÓNj xÝÕݰö°*’¬sdÞÁ=ø¡>ç{´“z?ëÿú™©ír=^3Û«N„°íOð`+Oìú”D*î$ÛbÌÒXê|ì¡„&Í‚ó;®3½üÐ?æÛøêù&3Œ9+Ø›.WÿÜk3L˜M _ضfVtªs“—úüÞ¸\¨—Éû¤ì`Ø®©p4f$xÕäf?Ägg qºE!Œ¿'à}V»àûGÖUp™8j*ÂGQCºæ¯4_òüóÏók™ûïŸ{Ê­~XËe¼ðŠòÒÍÇ™ì!ªrs(uîÆ/“wp±‘Á¤t³ ãùäÖbêßP™ûÞ²Qlñ“‚DÏãï¡s¸á½ðy5™ «Œ'ŸµÀŒÒ_ðEæh¹ ¡Ï‚ ]Ôr›ùËØQ¿mÃ©ã… lºЛ‰²ÄÙiŽxb ]ð:IœÚKØ àï÷Þ¦E§0~3 &ÈaèÛåo;Š>zú[x’DÒ”o§àמCè¾v#h®Åñ¡W¡?F’¯—úC?³ø¦\AÁ8‘þSÜÍ<ÏÅ‘ûã‡àEÔà΋FÃO³/lF•®þlÌÃú=ÇÍšÇ0ßæ¦.<…Eæ«h°Ì qš©ÆÈâ{<«BWÝšWPŸïU›Ê¿íÏ€Êê¨Vs§ÏL„G²¸“­à[äøyë;»S)w GèþaöÝŸÐÄñ/ñß¡ŒþAc¡Áy ¹¡<„ÊÝÂÿhy2ź|æ§gF‡'øÃ„Jchê>_¾þ‚Ò±;ÀÕ³ß× ûäZ¼ð-“p»eÛë]¯êñ¨Kê0#±aû,}5&.?FÌvŽî÷«ÛáÌËå|Ešä€þ‡+ÓŪLÊý Ç_ל(?Gp.Z¨,G!.ê:cwlP÷î<Á‘q?!jC}@ÐÌ\š6ù»òG1óÑ|ŠWj’„%GôyÿþÂKýüx÷ýQ¬-û*øÒasŸ ’çlÛSÃÀCoͽ¼ŽFmĹšÏÐ@r8nË™~ ”ª7¥Ô-4æ ÚÍŠán¼;<¥48̯šÓí³K ñ£Wì>†^ò™¨}j¼Üórúky/–Ìà­½0g’Qèi{Œ;!o†ÓR­¤kì^x>xþ÷AêLê!ÖÝMð´[o) åÿõCU5'!m«Ajh¬ gÉ8ÕV¯ù z ‹}áâ­S0þ¸ÎZNéùÃáÊoˆ±öÇ™ÍØò|<9”CdgÙqíÊv¨°¥ÃøëOY}LlOÓä q3pÿõõŸ¿´ÊUüH‹½9ŠhÏT¤Ê›¥yqØa0²\Sí‡SÅœèJ²ò•·@Wþ8ˆY7 œ¯ÝÄõ>ípLu'3õre»~mC¥ƒÔÑ(_pÉãºMÞˆAKI±±#z¯’¥ÏnëÁµÀ·$ôÇ|³dZÆmä†lØ3£³>o„Ò(iê¨)‡·†ÞÇžn$ƒÆòó_AÓ“§°LÑŒH¯µ¦;n)áÖ¼"76ÁCK¶lˆÆ+“ÄÓûÈà;•0n˜¶'Ös-Øÿé¤`ÜRZàŸÉÆ]‘‚)[1näU¡ÓÁîžþÿÎû-ÏÇêIPç†%ìŒÕK´Ö ±!˜½Ú†cÿ ˆõ£* ÃqÅQ°åÊq¶jË0È6VàûêÐÀò8èI¼Êè¿_㉺]66Ä‹ðì*¶«ü«Iõ™ŠË öÞk º/ǯDùàˆUêkÖí´CŽNaÙåW€ÜRÄ“¥P;¦lÞ—“Óåz¸£çF¸_c´TžŒ[Û“¼¨å¼kXðÆ÷:Ǯʽc/n&³9·C›{"–üx#Ø×ü›¨Ožî¢\éZ9È~ÁÁîß’|iЈPûbõ@þoßiêý4`vß`¾×æLÞ: f³çû±__ú½ÒÚÏ®Ö6`^€ ÷¸1v‹¹^Š2uKg† åÆUCº¡¾2•¥kÜí?Çñ ‰4;Áõ¹øáÃpºT¾ä˜âLÑHÌÛ*CL0ƒàµ† ôG”¬Ýƒ{ºŸ ÎK:Ï8DÐaTéÒxL.ßJªjßãúQè¶ U±\*Z±ÉÞ}pÁ¸Õ'‘±¤i·sðíŒ=¡’‚èeÿ³(³ê7™¢“˺EQ(ô Uû¯§š)OÚírûî buLi ÿOÖ•1ÅP ᘋ¸åâV5EŸ>sL…°û³qùòý åË^ÏpE« Ëxêé`pðœrÁOÉ¿ä9o$‡78ñ•÷‚q†šݱô7ì¬Sá~ K‰ìí3¬úª°WÛ`aÆhúRp‹É¹¡\ãs¼žyÕ”k¡6Ü…¨ÃskÃ’[3ùß (7þ‰× Áâ•òÔêÂKt ¯é#+Ñ<Êc~üEÍr'¼×‘o¹ÕUy»¡;á(ô, Йƒ®{Õ^FÄ4¥ðÚËåõÓ\=q—̨Ùž_oáþ²q®ŒºŠwŒøç€žwŠáÞë¬Òy9ÄW¦Þ!™Ð7ý4h‘\™WAÄ,íè÷­cðëolNT'ݸåÛ½‚€ŽfÐÄÆ¨ér ÙﬤµÌÇ—a³¯Xg;Ž Ã³=òן§ßf¨¯E[±oFN½uÁc†©ø~‰=öÀræ¼O…We™ ΕL!;Í„×[ÁnO†àØ8?PWŽ!³nú“©/ö3ÉÌ_‚wæ},Äô8sw¹ûÔÿ°d¿¼ìæè$õ¸do“ ™<ÀÃ/ð5{×TÉçõû‡T/w8q.ïhàÿ|ÝаeºÝÞÆ†z­d?ׯ`ƒï)¯R@óe¥3«¯f^È#Ø„WnÏ#§V‚üx86d6Ýå§@ß wFåLM®¤ýæ÷™øc41zN܃A¯…™±¥ ¢&§Æ Ýöd4Ä·ßÿìxå}aÑâOYdF'Y³kîð¡ƒþTàÛ“kðÃÓB½ÐŠ•Ö9XWµ’8ƒ]5ãáŽs°såf~k´ÚTB«þ{Æã›ØÕÄ.V8]‘ZMY_êÿ±Çcë!-cZxÅÃàJ´5b(®íPáùr×1ÏVƒ¦­ÎDÇ‘ªÔ3'OèßÄJ,ºÙ5wÒé.Ï|fÀ'îêØ^¶ÖÕØà¿|ÙU¡R]©ž»¬ÚŠî¹û‰ú}oþªz*ö :0½q3†ZöBOƒ2öænÁÂ75Ø” Gÿ-µBõê7žÿ×W\ðü)dæ,ÃæÙJl«§¾QÝ…ÏŸØÑòÆÛ8ßÇŽßÈÑÄ\§(ÃSÀêÞƒKò~òÏÅúüˆ«`¹¬ ä§™³Ó @ãÓ\¤]¿ÌÄ­ïïCDÏ`á^Å¥Ðlõ ¾´¾&êâŠÜlš¤öLÅ‚³ª írÌÅ‘_÷š`Š•3ýú›£Y&½}MEý”èó­-Ø(®Ùu‡!yT½JÄøZ&mÛEB>¯?JFM#ø>¦ •~}&yâ¶üሙ,ivÉÐmÌ!¢°*‘ ß#+€Íù(íû’DŒÇ]?Nƒþ0m\,Ÿ¾ÍO"–/J7u?Xj»¢oƒ"^¼s Ï«öÀ>eš|ø$Z6õ@“¼OŽ-ý}é}ƒg†‡ܤö± ‘‘̉ëÐ3¡5,ƒ±Ûþ,®Y.‡Ë°¥ýlW÷jH—Q^î÷à›sÁBã ‹ótYp0#d —¡Vø£ÊtÄÆx˜öä([´dœ4œçÿ{þ¹ÝÈl‚ ±ë@³åͰ掫…W;ÌÈÙ=Õ8º® gÌ?‹Êî¡ìÃ(ЀÙñ˘ÓD2Œ¿$‘gÏ`r•{s›J´©ÁgÒ¥­ŽŸC.ÀVZ‚+Ò" ×öÆÌLÅ#9cãæYéÄÕ·k•” :h¹3<0O„õfW™ÓÏ$´ý’˜œØÈZ]Å»Åð9O:Êú=ù/‚ÅnxÄà4D¥½TlV¤™aP¬U‰^3ÙÃ4.?ƒà›]õ“@‰¯‚É¢,Ñj K;7gÁÑuh­³$/ùöaÀÿ¬ŽŸ‹S÷? n‡t o¾:²…W1\ë nÏ(&>—ÂÈ© ì~‘*µ6YI.‹ A-)6åÎwýŒþºX½(Úš{`Èkgºö¯9-µ\'ï‚}v£°ÏÆ Îþ²æë/âêÈo$\Ë^?ë^úa×½xÞgyýdèýHk¾eèAb³72„twbÆzë‘Dª›—†^ë]ØÚ7Ïñ¹Ì]r&èïT£ËÄøâò ‚çbÍØÙL•>‰Ð÷-?@¯BL¥*˜;‚ÍÑ{{µ µÍt„fóuû“áq¸üÀÿ¿§¶fu2QõѾ¨¿ùÞ¥zÂa2ò«+¬ TÁ´‹`_Y¶Ïm‡ÐMwpþ›Ï¸ÆwÙjrÛãÉx0I«¾ˆRŸàçþ Ú;k1ð"T|iów0~\/øŒ=@ŽWžÆ)eÙ{¸µ;Sÿú*YQ_M :w`½Ñy—î£pïÛww;ÑÙêj~#‹èb-ìOšÀ}‹ ˜û8E>ÉÊOÚMÄëënƒ¡ËžëIU…:üë9z]V%ûë´Aªó©GÕ6¡ÇDÎáš…±è廒þ%íÀöÇ€<0¨BîìÀÿÑxm0^ÕGT—‘}{†ñ‹™žôrN.ŠÄÙúkáa¤7‘yù.]—…­%cáN€„X âëk‚¹ØŒ±¨YêM§-þû¿ë2­1#xÖ01Vst ÿó¶Ã6Hâ¢DY*o¦BÕ%,ñzâx\èhÉWÜ]†—ÒEéþiÌU*Ÿà$0}z ­†Œ‡ØNÝŸá镤Ä8åJ^$¢·3ÈÄ–Îÿz³ñáT/˜4Ù„?YµYàü\ ?IâIó4l°ñÀu¡kÁî#9©ì‡ùuâü'ÐíÒ ææµ #ÔÓñýœ |`v|@ÿ¾Ýuƒ !“?S§òˆ‚(|¡wgøÇBC„!ý79/ŸúŠ_“ysÐÒqUƒ^Æ€†Ù1ÜÀMù®•´.£ÅÿÎâR“d ¤o3ýþˆæÿ( õ ·Ï­Ä‚ tF2è¬Sà!ž]¬²íØ?°†¶§@G~mN3‰J—4m½~ª^¯ákï_f¯2_°œ i¬{’;¶ÈEóýR‘xëR(«á¨ò|&*úZ‚ã&G˜~K–^K6cñC²èÙ…Þ|ÃF7*õki]w€ÅØIKTE¸×˼zš8í’­b§t«Ðoë2ØV&6à=Œÿ —Xô‚T@,ö‚мë˜`G¦íú€Ç»_“ijç¡1õòAWÔÛÀÖ]œœïúÝuÇa‰×˜­/B‡Þ£Ëk— >ûͧ=ò hXügÉ[hYý™™•$ ^Š˜âN¹Ñ|nX2½b»Ÿ5•¿Aß+.ðÓ0†=<‚6çMá"ß5ðmº:„5”³ÃEI‚Ľƒé¯5Ãy½¦;~¼$Kf›²!!!Þ&›¸¾ÖÿDµ6Múëâ |Ð÷–(Xä¿K0kÎeÁ§µ„½ovK/¸\Ü´ÕÖˆóòëÈ>Owq$FL1ˆÿîß(áÆ¡çØÑ†08ž0¤×îÆ_ñãùŠsV8Äfi‰§ôéö2v²Ü„ C¼/õÆé)‹ùÛ“îxÑן§lJ r×s!Ô7ƒÉ˜0úîi(46¦¦{F§ÏZôɆD¨0ßÿ Œ5§Ù[ðg\6<‰ÛHÃon¢S›¢1þv:¨¤Ó“Ó5¡­A‚ÿúô4üq4ßñÊ’¾výÇ£« :ùÞ`y¶.ÜæŒ¡óVÿw^ÁÑæfRÛ.Pœ…Úg•„%3=À%ô{.áϳ\²°ÙKŽgT‰rã—òú'û¶- ††s˨~Ž%¯Œµ§Ý‡¾sÍMõ°cûJôî £‚ Öx §?7Êð¡ ÑÃcÍSƒŠæi´Ç¸—ȯÁgZƒ`Ë[gê}ÀwÖ«âáÏ ™òN<¹.žîšõO7å ^0üÿz£Wú`ÅÄ4ÌTóá?fNƬég…M§ÁsŸœ¸IG\î“¡«·v3WRƒa]ÃèÄòf¥¼VÜJíƒÐW®Sñõ•“(õ]žþò=Gj¿ˆ3''!Ôæàó¿žâ¿Hòÿô³òùA&\Y*Uøý{Eœ÷§–/.à¢o7ñGð7>ø®q9ÃK"wóÂ÷90íß{ôPUâÙ« „f^;èþUy¤²n'­ûw û¯…kÌwEÃY<lïå"—‡ÓgrTçˆõÿCoUe²Èwà…`(=ŸØ„¯¢†`Ì¿[¬Så¤êð¬(/š2.ƒÎÍ”ÀFm˜î4”Ú©æâŽSàJþÿúŸeY¿FoÎÃÂÛ‘/èemƒF± ¯êOLc‡¾?vTÐ-½º]‚àAŠÜ`ít<6SŠW©•‚ÄÆð*:–>–ºÇ\Ù%üås’}Ÿj€®ȼCðq¨(ß©å£SÇвÈ:¡ãɬ[܈û4êÙœWUxo«ÔClµ=Rl•&_ÈOû:˜ðSjÑß^ÈŽª÷{΋ÇaÝŒø9‘}‰ÃYï@ëÛ)²t´Œ o»£‹.`S³Ëÿ`‡ÞÕ³àÈèt\eñU&—â§sºôµª›v:æš_g%“a׸ýØ Aå¨5ÇB“¡qx.Þ¿4–;f…å‘BÓ€³¤eQ*9cÕjáçáæØVúýÇvc@žuý x­ý;û1¼’” ¶ƒ¯Ô|†-ë¸HFìuþŠãó³qE[;™»›Ä5¯ÿ1“‘:^%É›Á¼ ¥¨‹ó‡ï΃ÌàH¶[%.×õæÔ#í1æ/Hƒ­{ðHä ìú¶ý¸Ü|yíxÊ< £ËÊÈ™uÉ$aTöí¿„je0Êîîx&Ïç\*egãåØ˜ QT¹8=ónCU·ý=Ée ÿW÷ëùå8\âZ %r¼a—5C,ÜÿžÓfÎá©W`eëFzãå[r^ö$Dÿ+<6ß]q"F™æÃ"È;ßMFwd°à%7IÉÎq(~¥˜ Ñ_]·ãrÕ ¦÷­’H÷Ýš\’™–ÆÞ):zJ.Zp›ðŽD°³GÅ£bøw¾M'êd?íÅÓ'ï/o’7ÿV¯Ï7ñt¸\K8@êžô†¯TëÔ¶þ:ê^ZþÅÖ]…:CáÞê.t×]MÏÀsX¿j:j´ììÝ [‚¯àþ­®X9¸m?çŽ  èÿÝùøéÓf¶a ‹¾‡á“pºË|°*Âﳘ“†ôRùßâ‚=ÍÃñèÆ4ÁùÉØ™¡‚½Rt¯ ÚM›Î®PÆCùfT}ö]ì¸j@ÇÁleyî}i7Ǽ%9h´ô3tm» 6mUDi¸ ž-á»b"Ñæ‹Mmãâ“îàËöãáv¦aAwÄÇ€f‡.®ýƒ—¶ó-½ÈÓT îöOš—Ëô¢®ò:w¶85}èŽ#{б÷ RhŠLDå–nBAZ „»_Ĺ[»Ñh·!•ÛL†·IÉzsXòù!®Z}˜ Ù\„-º¦üÈ®çÂÿ¿þBSa™¬5^ßÉB,¥iÏ­4¸ö& ÞÌ+F]¯l˜'Ê•ÿÁ\ùá˜ØýˆhXˆÐ¹‰Ö°'g:n>Ÿôì:O·Ù±7:Õøxåp.ÿå&èÊ(ñ€ˆXž¶å0nŽ‹uÑ‹ý£|~Öi‰üó7 zÄN_($OÄ/À­SàI‰!¿ý!/ºL&ܶ¨‚!ÉŠ`;¬ãÜPÂÊÔ6>V©l¸l¬;|JÐÚÒGDkv€éáŸ8<ö´WÅUûp”VùO›~±ÎFÇÀ{(»i D¬SÂÑ·×ÏQ|„±±¿Ð¬výÿü¿O>Û¿þ#qõÿVà+·ÌMFƒ^‚ˆ½¹¼he*oéù†í-éâèjî hùg>-³œÊ{ÖDÀûš‘´qævzçó>Úý* ߎÜÂOf{“€ly4×ïE§³µ(µ&™ÊN8É•~œŽ_C¿Ïác5Å)¯²Â¬‹­ìhô)Ðö–XÀÖ h‘q:\åƒKø'æu&R4ªÀ*é]2Ç•Õ2@ùÆ.º(u7W;`k…—X{Ü+¾Zk%®ÿx,ý̄χWâ£N¸ÞTºBÔƒz{±©ÖêÃÛØuÛI|—îÖÿ+)²DNØ1غǘO|AÌ*WAú˜§Âè  O¶æ["Ëø=¸…Az8+e>,hÃcÓ4iE–8HËÿ(s¡•ñ¯‡®Ð¨"eš¸#ĸ. Žã·v4‘ê d^ý~¬¶OeFÃ(ª-¨®½Ü¢¶1”ŽÌŸ¿InühÀ$çZNŽàéÎNô»~7¾½[Ù¼#ç§œg#ÓC¨ÔLÞ}—ï‘"½ ñ¤Æ Æ„¯HK…ÙüwB'™³|$ÝX>wH¡ç»‚‰o¿»®Ü¼&!vl¥µß¾wàÎ&øçžiaãÖˆAÍÂ0¢—ÀQ<¶$ŠˆS¿’#èJ}ÒœŸšAŸ•·CT„L8{ý‚å¸Ü83Ö>_Ÿ4‰€çÿlxͫݘ¸ó†„܇£y©Î“¯ÿúÑ|ÈÙ”n,ÀM»¯²…¡®ø^}3NàU+â0Í”7:¿ж„ó ù»Šb‹ Y—9}ôD“k„<'îŽ7¡Fñ2”Û„³ÀWù8fW–kÔ•¨²ƒG>XŠ‘á @öZ1@µ!ı!OÀ÷ê¿1˜ë\T ¿&v×ݶÞåÆ4c©,µÚª§ð߯*;Üá˜)¯Ø¶”%ô´C,È×§Àª«ê4æg"¹;ú<νûší˜5Š=›YŽÝ¹_ÀµÈŽÚ÷ [òyô¨ì¨ŽvoÀŠfwzpÒu2ÁÚŠu—#h<0Z–Z4œjWtàöò9¨bö‚Ø}‡É_…ÂòɳHŠb\ŸšŽ.V%Ðöë#$ åV–ùlÜË–3<̭̇Oãx˜ÛRš­q˜M8ñøé‚ˆp-{ôÍ |Öæàî*kž“ùUùÉx|rn)Œ³ß PíÍßüÞàªõ7‡xÎåÚƒâÿøäØß‰·¹z¼òÐ" ‡ôé—Kaå~w+†pÇ„gDëàhÜ>xŸ=5†¼fîî#wL®@xc È¢÷‘Íø·r&|aá|Ú¿d:f¸8¿g-§'ãï:âéiÇgðZ²‘MŸvŽŸú‹/t½…>lc¢{ÄÁ|ó :Ü3¾ßsÆ}¡„Nu;€Ç\ãhóA%º¹#ìD¢xÆÕÓxñþNêó÷« ¡†ókÇOäϾÀ‰çtù?cšë˜ÆÄ*í`~qŽ úC|º]Ū+“QãOªK¹@|G ]C0™§ÃµÜUô?ÄwЗ¥¯Äò–ÎfÅEÒü“§+ýúQZDGÒ†€K°Mc,‹ ? ÿB^Â-û4”ÚxŸåHÜ9"ãK–Q…¤Ùf?\ÎûÿæKð,á©PöZ’¾!–3Hãò·økõ¦m!¶=J`sªÆòKWóO¯Ça¨×T¶¥Õ‰NýÔ@Ì}ö¢’K7Zd­„hû³ÿ÷Ûé 'è¾HùhÊÛsÖa=ûäzö× ¾TtË <Ó°€\JVE›Ô@e/·^º¬ÉÀšJþÞäD}â½G‘Ž3 ›?tAÂï¼üŸ*ZM*'€k°êõeÂÑÓöÖ…džØ.szÉ¥Ütp.ŒgMfÒTõ¦wÖ]{úùæ¨Ñ;²ûñG¼wo³X7o«YÐ%¬ñ`¼eŒE_Íc!Û'Aƒl Ö¦ý‚•öfäÝœvö-=µ7½”oÕÁ7ýuÝÛàrŒï8ž·FÒóFÜh!‰ò7¬¸äîq\µe>ü³ßÍšChæEÂnŒgú—µéÐ@i>>¯‹äÛŽ¦ã*jXõJÌÙI`ÀgžYý9<‚>î8„‚åü¿ÙÓßçjò½£?àýå9Ø1ôÉÀûo‘_äÛãa`Ù0Œ(ʘÓã¯Á–e 4e$d½‚Ô‚L×úÕ¾¸*ǘî{³zž>…Š/W1£ùN{ ^}q¸áƒ®¼!Ž[Îã}Í-hGÉd¾~½<®ßyç—ZØTeºì€éP¥Ï»ìù÷]2üÓ‚œPcv‡mPÕQÀ§ª}Á¦i*ø*ä!ÊyY#½¢^ñ}¸ê'ÐPë¾zo>Žœ7ŠïlÛN‡>Âþ¬T%/^&’iòÍä€à0›&?…Êö¶B»íB˜ãñ ;~£‘ž&»LÿïÁ㹊ü¦0Œzû%äÿî/BÖrt“HTä úß!âÈûcá›E ; ÕÚoQ*+Ïd\d Ò0·k„ëžÅéÐË×¢† ÇÂS ÁYõœ/ãæÏ¿Xà®Ópߺo¦D‘¤ÝdåšÔ$ü&n:ñ†™iåA¯ßIø¯o:Sô7HfŸc‡ìJáa¦2´×ß%ËwëñßñqQñ0Ô[ˆ7Ü‚íF|úì5xØò»Ð0§¤Ö£SE5¼<­ÇƘïÃ2ÝL»6õV:â÷v¸à~çm9ŸêÁÖã5††r³!×™æ¶(0i‰…ýº˜â^1°þëC @kÐFvúïm¶±Ûf0gžà´ Þ¿,&/µjñ‹H:Jýú§¯» 'Þ „gÿá‘a/ñÑ(Uªÿu$5‘ºÈø>†È0~^JxwË[! źS…+µdxPb=9ô3í¯Â¡ú$0!…6FÓ#rg™)¬@Ùw¾ü™ñ{[¹\ŠN6ÝH«öÃ_éð¸<O«%௷~ 3Ó"5mÈ3ËmüVØHÔˆ!T ¼m*/Ÿ±Þ¼/èXw6]I„žä!üfßM sg‹.U‘UÏ’áå„,øûå8ÚÕ’k¯ÄU¸’Hí_Œ!‡¾ tV“fôÈüelËÃÝÞÀËa78àŒÒÍ0êS ïÕ} ›œR„ɗ̉‹n ¦éf¸7Ž mÑŽ9ÐÓ‡IPÏBή=ƒ²è¯‚ÀYÓh—œMËtäES¹} cÅcðÈ‘ktÁ­s¦C@­-ÏQxHÖxÆbaÖþר ŽälBYkÜÏܹn#yö² ÔÏTgY%¥œ{Ú‘(Ãe|úëã$D`Š:Ñéê…‰pD°Œ¶-U§ù0„jÎÙMUêdøkÁ^H?^€b A\°´Õ¾ÖCÓúáÏbóá’¨€µIg´8On¬¯Œ?Z’ÙemX¿¾’¼¿ö—D««ð»Xþ¯[Dмn'½d_Œ ñVí8Ë>¿2qáLË0®dSAo^\Ššý »Dž=œ¨DOÏ^H/†K ÉÑT˜UmËW5õ×1Eرä¸j™§ÖF`4BM Ux©ÜÞ&GxgÛdîmeK•ä¡ÒÊPþ2§Ý7G†| ¼MÔû¿ß{¾¶äºížT2ôò°¿(£uDþ²S>bða;z±øþ~& k4òáê’,<´}ß·Ø€þ=ù ™3ö²Ú"t«*/\ßK–Ý­J(½"Ä%Žb˜g$ny;“^:nƃÿ}ÀW‡u: + ®¥_÷¢lióq&·¿uŠPg«q_q.8íÙSt¨Ûl]>Ëæ–k†ÁߎÁ¼/ðk‘"µOûn]íp•õ¥WÏ œ¥2Á é™`è•K0ýœêÈ æYNÊtGD¿ÑÁ0üÎÚ¸&Sr’ɾ'ƒPçm 4ÊÐþ¢šOÙ‰12;ÐÄ7‘-|dÂ-”‚n„ž¤![»ò@u;8³ÜÜ«äi‰;C­øÇûˆl`:O@Ý­â¤í´$DU‡ à?îc#.«sDõúLAüeЛ×DÄÏÇéfoÙ„;£èïâ4‚ÝñáÛÈïKdÔÄTù­L WtÃ7KZÛp™í°ÒÁ‡…0ìà7Ô¾ÿQH· ç|ºÓªqʼnÑõ_ÔëÁ}~Fžü UfÂÃM¸å{6<ý¨Ž2ô̼xw~3\š5‡|YºÙ`*¼ÌÞ…þßêðª{j)IðáOn»NüîÊí/»Ó¥ëRø¬g:ÏÊ ?oPàÕ*|Ýeh£0‚W'n þ7ö¡qé"ˆ56 £uïbJ˜%lÖ«‡Ü'Èí ñÙˆ²Vl“Ì>æeH‡·Á¬ƒñ«®·Ò¶B;Ó“ue<]+tšP/è¤H GIe?ž&k„æ Ö‚¡€òȳhßW'òòAAürÈ®J³-¨¸C—n†¤ƒñMz0ï^XƒzOÑ[‡Óeù³5ÏÈNEÔ 0Çë—ŠˆËúdÁeæ#H¼”Åú‚ͺydAR+Ú™¥’wyKñÓÜé°-j5ÍÛ$àÙÀ¿­kdmXƒ5›aÙ‘y쯛˜Ëž€Ÿ÷_³äb;Ô^ [ ðìÒxÂ’½QBñØ@ü{fx ÞÖIhæðo5¯„ó’“ðÑèÑPû(—dãÝë™LKù3K¡ƒ7iÔÙñXÜÊ>5=Fëûdèñ‹¬[»¯¦ &>ó-ÑÕ÷/håÊQ'½Åðóè Ü‘é€ ýd`I®Ù×bÅÇw±IM©Õ‰zü¨ÂWÖOºø$­,„¡ù6Ø'B{€õì TððUø=å"œ9wƒ½Lÿ„Žšr`üñ'èa§N„—«\±æÔye5Ñ9µnœú‚l»>ŸkÌ\Ç î“¿QÆPÒZLò…©m0OY¯ÜÎèùpÆNÅüpM9˜†ÛFþ%Ö{AF¿R€Ot(ñ¼ÈFtÃïo˜ÊÜMðǯ—Ü46âéB<}(}VJaEEkxf/Þj¢Ä—XøýÝ£ÌÙ 5`މ˜uµ™U”©Ñû Þ¬äÝüüâ ì{Ó0ß0 ”ïÕAàÛ*"8/¨ƒóÂÛ·!?ᛎ¸~$QA» î—(]röàæòÁ§dº,ÂÚNŒÂ"ônÂv*§V·%+„7oôÔ6” †ã0Ú‘ý¤^:ê.ž=âˆ7g0âL*üÙÎnUl £¯º àß™7§???2ƒ §ÿÍd6©ªW§s0-M€¿¾$‚IWöo«WÐmgÚjÉÉT1jl“Ž#%ßaMפ;¬™£Fˆ•ùC÷3¼ž%éì UlƒwkZÁäwÛ0¡ÖŸ:NLë}$õéo3W´O+Áé• tÙº©t ˆƒÆxyj·^‰ßL_L]®oㆥ‹q¤•)¼5t:^ &äÀ}Åd¼¨s~Š&A Õ  ÿ |Ë6¨Ù#Ïÿ›U½¦)„ïѧåMóÀ:o µà@Å^÷ì8è,ú—¯É±Üßm'ñÿõ?Oœ [iABµ ü‰=/†bUåxÕ˘ï¸W tÐæ­{)ݾ —åÿõD;—½ÀæOaŒû8žîW€ÎeÒhxó,<ça ²À‡ó>´&VU|²Pj»;üÚouEjlºž:}=c7Íû΄=þôhb,²ý‡ÍpnMÊ×€›ûBõX¼ýú2¶êˆ”ëÐÛÃñÞYMNE\v›;cÓƒ–±Òb2è.lCµtî=-Œ¿í÷Ìakª`0ïÓªÜ1ð+LLƒí¶Á‰:rd2NðQ*-L|û,ŽÒàÏë^ƒHt3F'º@Öðl”S¤?ÞµáÍú-àtИíÜvæåîF{.æ Ç/CæE¾nxÌ6~W?lãY&±8 k^“ˆu…¸¼\œ†©­¯ßZhÉG ~‹?씩Qï?Y” ªDYÓkØ5G—¦Åâ¶Ò 2+e3 ¿0GÏõBí¡~Pu3}ºY•–áRù#˜UóÀÚ$8³ë1„çÏfËfc`} XÌÆá®SYÿþ0ÌGÛãG›$lïk²x_.ÏîÀoŸ*`Éã|r4} þ}ð¨‹Ý½&Æ*D~ÀæP®c)rJ‚=™;PåH1yh Pu@±à%Q=— ý9ùmüOÜp8ƾÇ5èøTyþÍxšïýuë‰_8ÁQ’¿Òðeáøý¬]ÿ>› ®_£?F>…àò Œ ã/züXzšˆ& j JµÙSó ô¸UÊ­dÜžáùrGî9ù”ñ‹LÞ_•ÊŸ2à—î!ñP»à"<{G¦$AÒ˜\í„DÞ CöÍ@½…—PëýMÁ…¥y¨)Ó Òbö¸÷òhúǵ}uûT=/H¸úGÙŽ‹¦‚áKwÐè[‡Ã&©Ðº­°ð6Ó«¯…ù¥7À·kMªrüªNÄÍýá5ˆ|Iã‚§j }ì#$ùùãñ†\]š.Ÿ¼WpaüxŸ¨Á¯Äíb…âÎ,oèˆÚ/Çe²bQOc/– ¤èþ›å‚$‰UxèÈP¶Ï¹:Ö,„ ½YÌk£ Uë¾@Ö1À‚·öäèøgphþ6<é KÜ:ÑÅ&n®ÇcþÓpio/Ë SAÛÛØ¥îõΑOÙÐ?*jB¨Êý™ôäc=¡Å«éø?ý­s¾oÅï³½ÖªSÓÙÀv¿‰O·iƒ¡V ¬ºä wwë|W~ÛFÌKsâ#¾üÄE1Ù5Ñùà±Ø¼ö-Á Ñ'Áùú4·ß‰[^Oà•JX¸Y"¦öÜdÛfõ@çÙ5s1˜èM¥e>6(îAIº 6Ù±5.dpx9´jêøYvÕåòÆãiéžv,®‚OY¥ø}…Šc`kõ(t ZÂ=2çÕ?¹ÎòX~Ïx!9|WÁKnÌÞ·W W:Šò‘Ý4ÚHSøZj­ ¬S•ÛÝmc§Âøoÿcg¡Ý¥|Rµ3óqøóâ:]ú. ®8:Mé8óë% fÖ$þv5 Œ=Ÿ–ÎØ´ãAøÏòäNj"ÑB$¿Îe³7‰ÓMéJðx—y×ÁvïJf÷>«Þûþ[öö¹"3“J€Œ¯ý]OzW'e ÞW;U5¿›}âˆì6kÚàu•Ä›È`ѼóðÃh'šIÐU•:<âñ0:,!pey¦·NI¸ØÜÈ`ã§`6zàq[=êZmÆ©úƒÏQ!dæÈgàÒRõÏ6áëƒ)8ìò#t:Õ)´˜ïĺ:gÁŽ‘®4Òü6±†¥ä<˜ÿÜÅŒm¾±ÝyžØeƒ‹‡¿`åŸ fVClº 7‰Vâ©Y÷™vÃs±³(íñž¥ž-ÃÙ“xâWkníw­Ì¾cuÁ1öâ Ö»n£š«qË~ð¸V‡’ï\Q,C™äi^eò}‚ÊsÊXô+¢'ê`é FœêS e?_ÁèbcªÈò©¸Ôþÿz·™ðÍGæé¹—?q‚£×ñÿøhfãºIÁäaöF.öF-³ƒE{Ž’euzfŸ_¨ß†Ó~JS£œ=°?b9»c ‹=½šf«A+žMƒ?\ÇÕ©ÓWoUÀµÐKû}'ßò$‹[à¸é+¶<ˆorâê›4cc“&¼ü nûèÏkòæ×%dư1`Rr„¼óÆkRv¬ó¶î‡Œ ÅùðÈ@üãoÃA'gbTâqôÍ}‹o]„Éæ  Eª¤¾¯ù<Æå Nû£ UR" …/o¬g}k-ÁÝrV:Ÿ‚î3O§Úéô_Ns·Ë™„±ý3,´¾;ã6^i9~o.0X"ƒëö±ÆRlÚ…DVg™†‡¿&`EËqT½Ü&ü› %>&ÏCeÑÊgD&ס›aÂÊu´ûÓ3ò’‚Õ/°uT!–ùèÒö÷ðt^XƵ1Í›~P[+JÁáf¶ðÑH—êïÅý‡ÿ›õLLåwù³ÝŒíra*hÍlð„ #þðgŒ’þZ›ŠISã+ ºš‰k®lÏÉàÌY«Pkùô÷Ÿ¾!ƒ`Šß`¼•xÇxŸåãDU_~Ã&‘ëÌÕ‡?jؤÅ"ôgêM캜Žfð^b\mú Œn0§ÁäSõùv /xråã—o^¤ÅHi*•äw¬¤„Ó[±é–ûqPÅBî²PˆMÕ.BÇä5üœ9½ôÐG•#è·´™¼Qíá€þËÞ _ýuÒƒº]¢ R<—5Ÿ¥›ô©ÎòmÀmãÑné!Ñׄû¶ùyÛ 4ëû7f]s ¬Iëž< ¦ÇaGÒ3 ê¢4æõz˜9DX_°‹<»ãËö– ºëS¯ íÄ¢ë¤`6Ñ#'b¸:ÚÔah”®?u˜”,¿Ê^æÃÛïf°j¯ÿu¬ïÀ¥àu¾ÃPŠ6˜hc®âkü¤c‡ÿ͘.0œ >Û6ãcãR8hlŒ1/Š J8ƒí| ÒÙr0­|ØL[%Üa3‚Æäo‡¯7Ц ’Â1š0ÀÿƒT¨ÍÏõh[¾œbôØ÷ >”Ÿê¿=áx1ª+”hîþÕ°Ät'8ŸñÄhõÜ ÇÖ)©sï,<öb9=绢+KÇ®× £.Vbì–Ÿ¸&|ÿ~ã™ô>ÞÕ‹à™QkØæC9äbÑM\­|?J7à8ƒvÑ J•.äà&Cÿz–ÉÍé:´{vœt¥Õgf²ç¥T*(¯ñ¶ä+Ƈs'¾±º^GŒnA2J­eD?»]¯·…ì‡ÝþË+œ­Ôëc¡úq‹ÿœ| Z‹1—ÐI`üYƒÿYœLf*eÄÕ0Œì‹ü-ÿú˜«s(M×Qæ…}GÖ¬‹ÀýıMFå—“t>Š»Ø¨tæNqÚÍ4î, FÐUƒwàwÓÓõjWS¨Èý…pbš; «9 7æ]…ãaëñ·æ!f¼ò>ó8·ždKñÑOóiͺ{xNú‰°K&K•%cƒ³(ÝvaÚj@[Îg@—–<Ÿ¹£ZÅBíÚ×ÂÑÏûk…Î$>Mwnœ1‚æ¼ÚßZÎp¡†.ý–ñLëkæq]}QÚ×Ë2^¡Çºí˜Rý‘øŽé­_Buóp.xXG£ ç=ûtò“á>¼Ñr×D-@Úv¸ß§Í!Ç3:XÞõ<Î'àäÙ¾|ÈUþt¶½éY‹ÿ¶@¡ç,<64‹k¨…™‰}à>ßwT…× ôû¦)ûw€‰·ykU¨Æ'lKåÓ2ePï²25·ÆgjøpåéÔk¾>Dý»Íj's;/IHZUe«Ñ"CqzBú~‰ÊÂGá |OÕi²Ò°ˆZÿûÇ|ärùŸ×\Ñ:ž[dåcþ´Ò þú÷Ó¾'¦¥á¼Î«ì¼u>L™ö oØ.‡ÐÍàé‰$TÌ¿ÎÆõÈc–Ï6˜Â"ñHÎJ²ÿ Üp3áËß„®õr¤!ºiúò•š Ñg¯`˜Â ‹®%÷Ow`+}Sé̾°wEºVc¹Ò[pí„…äÝ÷ XúY eö×Bõˆz?ÝÊn×ÛAÑîL¥‘Î<ªÚñ¥¬$íÚ–+5o{í|pv{ó¾'ól+5c-C›M|ù}ÓWÙ^›.r%^“Žð:ŽÛüÏãmëzÕµ]5õX º2½«ÑlÓ8×*3°þ å[]‹LÉþ‰aU¡ì£¬(½_¶íŠ…åÏçcØ29ÊB%øñ‰á§„&ý»á (ÇY™6¸x³Ù÷­¬áĹptO€×:Yë\7rèG ÏßÂ×½ð\ò>\W@²AF`¥ÿ àØžøË£’hJíåÙrBgC{zyVœ±¡nöÂÑßípwÇF’V™(è’]ˆßõ”ùå¼tW‘W.L8ÝŠw™‚0£í‹Þø6 [„“¾–°¾ƒfà²j/¦WÝdº~ð9þ3ª[Ëð_ÍV|óÅrðÙáÈ×YÞÀ¿2ñ4˱ÁW°à÷÷ÁHA‘¯MæfW¥Àåãn˜áº…ÇžŠ‚¢)Ê ó.‰*ºDqM»8P*³Ù8»v:~ýüð©ƒóKÜyG¢<ÿðÈ‹å®> ïiã¼åðóËP. Pàëæ_€ƒ7p§A ½Ã.Á¯3åÊp‡ž=ï\Ï/þÎ…×/tië³¥¸$",œÃ~ ù&̯ "sª×óÙԩ_(#£OT8€ÈþXÖqØ‹†n£Sl“yCb1Êñ\X;“@Bîkl:·wäRuÚ˜ã I·®ô ©“}xíÓÿ½ÿ¸î½™¸Iy’ÜLÔ¸ü/ýØFõìk`öÌøwñ \±2íÌÈ€LÓ X«îƧ$ÞAÇY”¬O‹Ñ¥ÇRŒ1s®§0õc ytÊœ¸X‡â޼Ó0Ç}%Þ HœZî noÁÛãÆ™ò£o…ywOB iÁ·’ÅXõV™®xWÁn „Ï­ð©ÖþjM<9óh-9}"Õœ=XàÖë̸¤kÏâÁü…ÕTMþ1hOZÆÉËжSËÁøš.Tœ¨7Í—à¿JõQhS=õ‰2ÕþlsÐü)sMÐ=*ÅÞhÑö€1ÜãÜ2z¥f(ÝÖÆ/uŠç£J«ãè™´ñôÝžp߀‹jMž­•`V16X Áû-W¡½CŠš´¯€ìÊ\ÖV—JÜ=ùˆ·ïÉŒ­IìÁþãè™6…wíqa!§âã¨çé ì0$ppÚh.‘rómæðšøZ¬øGõã޲哓a”ï.¶²q_¼vWÒ†–ïªA8s ¦Ô¸òò7ùðÉï6{\ïŒÃϦÓkºr¼e|%ôŸóÿzž¡x!1µˆƒ.MÑøGgåcÆžHÖž„߃t!dáBn.ô ž#àb¤wQp±s@yæთQ.ä,xŠ+¥7;w€ß×Ý êõYÛovÐ&˜øÌ~ {¢/ã?quZùrš,-à;NDáã׳3™™~#þMè±Ï…ÓþÚí_$ò‚5í=cÃ-ªè×?ÀÈsŸK´çš£ÄÉ—¼`¢öW”þt*†CÖ9ð&j5ÙÚ†ïG*ÒUçÆñWβƒË «_‹T²;Yp*Îø3¼ÎBR‡ÕÎ¥ë-7ÃQë5/¹~õ>ey3a̓=øûÍ’õ Æ~ÿÊ'§|‚hÅÓdQOqšjÂg—ÇÆ¢‹ìUk"ÿÝ;–ç†(Ò»ia°ÅüWwö>{ãó„kvheèBçZ{Ók©[h`“5—Få¿GÑèM"}O•»I÷o ôÁævö¢ÄškÄgæDS?ÿ{ðþêvÒ—,ǯ­~àâ‹Þ§Õù›HIbbVÂEÒe°Ð[•½|Ë–m¶æzëËyh~ ÿ™Y¯Ÿ½}?ÿ¸Ã˶ÐÖ Ëyôüÿ}ÿÜñÉ"X/ÕJ\ B ºC C¯;ãéAéì£o¡pn@5¹Ü¶‡­HÍ…ÿf'w¼€cI†<âÖÌÞ9â¿~l2÷œ7$sÇÊ^þ£Þ}E€û֔ѣ‰z¡«ç*ۜ邷­d©È´Zá™BÁ(ååT{úvm¦gM“Ñà‹ŽõÉ>9_×<õXÞ‡r©E6Ì ~w`žf´Ý°&=ƒ9*]À€ ëßCø¹c°]/—õÞHEúõ;vßþ SãÜèÉ—“ñ_lĘþfkG-–€K×Ð?×/’xgûœ[:‰®Ú¤Cì†å€uBdN‡wŠÇ¡J¯^¸?ú3•Š2ÇOñZæ.xc)ÍO„½dá}‡þ› å…£ÿf*Sµ³»p]qUx•¨è–>[–Ï$†”‡žâ¯†ÄRÃ?¹üa—!÷JjÃϳP}í|ÙžD³4íùµ)ôÓæÉÐõ^Ÿ^øí̽Þ¥NóEˆÉv>¤[œo\ÝAŽ¼æø x3½5y;F7‡Ý"R0Ó¸€>«’CgYªtÏYD¦hºru‡]Ðx<”ÿ÷ÉuæRKÛ`T‹ö$~mÀšôl¸u„ð‰GÜñ_’U—½=¿nânæõ'B5ô¹ÁÚ¾Th6‹ëž3¤ Ô•›$ÕÀÌÅbpý­6½šŸ·šÃ·<Õçi®¼ÉC™j'¹ð“÷søø›cW¤î؈[6M¥®“CÑ`üps*p5P[ÆãO©Q¯…â´6÷&)[÷žµÅ¹ ’{x{óCökK=wX-GëNÉÁD–í”ÅÃ5Óˆ_„4u/ŽÇWâ®­%è؉ÛÿŒfKR°Â Y8ëf!U·? þº…K¿ãzÝpQç6.9ê:óU ¯…Ñ#ó©X4 ¯Öâ Kâÿxó‚ÝRþ÷Pøá«À86Â*”ðÖg~½L ü8ô.É5ÿ -)ö€Y›Qæ³åüEXàu¦orⲑç„$MÝèÀgÇ·S·Re×uŠÒüQÏcöDþ+“(€›Úp{èXæ»>žoÆ[Ío!aÚ ¶hÇF´§ÄÏYžF§?% ¶î¥<Ä+ù®³|ýp¼Ê6lßû—¼M0‡°€åÜ~q,=² ~éÅ ò Kx$´Š}íÚ>FÚdl¶Û¯çšßã£Gñl…~Š:òa×!öbd:[4!u_hÐóßpÅÛäNÍ™ü³ìšpö‘R*Qƒèƒf+úKI–òºõw¼Ï¢ìµ&¸§¥Ë»‡_‡GkBønÕ3à=DŽv-_BjŽ”±•WVRYõo‚µd°nиÓv†]ÌÕàû•é•Xëð³ŸÅ î}@s‹õlIåº=/þ›Ý¬9þ;Ƽ›ƒ«Öd ÇÈ'Äd‚_$ËMŽ_$¿Ÿ»Ðôk×pýeI’½ô;žrÃ&òç܃mëðLëœã°ÔEœðêË"áõg£ø°ÓãøâÞëDåW ú>ªf6Cþ‚„·'·‹ÊÕ2FÀñ+Á¸uSo6¸0€lZy_Ä“Wå_ˆ¥íîúЬ ÷+—$ÜÃýǡ&s\º¥ њ긣~25pu¤Šëfð“üAî™:}c#:¯õ#¨ìLÂA­Å gVÌ÷¡%uÿ°½™Ñ\GEîí©¦F¾´†g çÿíò+iüf-ß÷Ìjg4Cî–I°~ÂxÁ,±ºù—$d/¬ÇÃaŸ]?í Ý©ϪàÓ Ãè}wI¬Çr®,mK7­¥÷—峞žôȉ{ðÆ œ´T>“‘“h[r*šnYF÷Þ:"˜éáΛöv‰)–´øNš@pÙf ~Œ]å?•àgkß49M½ÿÀ¸L'”«Òà†Öq€òC˘Ǘ~,BÉÅz˜ wÝŸsoæáÖÓ{ÏCùõ }ÌÎ4˜vô–sG­R¾ìà)0•D¼{ CÖB½;­gŸp×’z¨ŽÙ/|~d×X& õ[·ñúK²|˜Ö:ªø£ˆ·EOÕf ÷ÑáOŸä²öý„»– ©o©M¥’͹†”!Ý<1›>ÿõÿ| FÝ£Ïðã‘bzÒ>”+p"ö0™m–ÁÕtg¿Î çÚWrÈ☠Xé!M×¥eYÓçÒç· è¿„6&ÙžݦX<¥òJ盧¿Ï™¼¿ }ÂáQ÷L¨ˆîÖ¸[c=ØLñ¥ ~D¸$¼F`5xèÎÍIË¡ZEœëW¸äî4 ëæŽ'¯_ÄÑCé³èB¸¸8æ¿9ÑçóΰŸ–À˜q¦t>¯'ö–[(¤ÍS–Mâ‹b&ò ‹v àÿÊÿ(¦¶à¶?ÙúOáüèï"˜qb74O€ƒsÊp;ËÓªŒÙýüPnMjß7gr &ñ>³(ºlAÛ&îå+ÎÊCªq'‰ItþÅ@(4}–ùœi›£xy†—Ú¹'_•å“ZFãòº1üÙN.©¤Æm%æã/ECþ~W®Ðo°–ß²àÅ'Aú~1¶\ûD}yTÍß÷~ó R4h¦"ej’tÏ>™g¥P’È,’ˆ4ШAÍR4*ÒH÷ìS!4˜¢È\¦HdÞßµ~}þhÝuW¯ûz}Ÿ½÷óìs÷9L¿Ëò­~Ç!z‘}.ê€âÍëi›ø½Ì˜Jîâ™Ñã0)ÃC8çJÖ]ç‹á` çÙÓ0]λvìÅAnü)^íøD§Èó¼ïgòß=Ì'åa’ÈxîÍ>àä:SF¦g°«;·ñyî:l^»NÌþ ÞEwp{E#Lµ™CÍÅÀÝâ¤Ò|•—ŸÌzPù‰IJ¯ב%ø ÍIõlì;˜zYg×¹½b‡›%h“¸;èÌìÀ«¥KÉ¡§M(mo@wžÞÎW+f/›q±Óh€/˜†þTž¥¤ gF¶àÙ‰˜9tû8ëbç³9¼¶ FY ó¬Äp·TØê#OïËó¾°˜ÔRíë/Ïe74cVü~r"wsr ë¸Ûó’ÆÑO3þwþáßþpîZŒCbøå”t¼ÏCÜeIùåéhê|NtÝ©nj¸,l¿@Ûz¨Åe¡$oÂP˜wlsíù±áØï-Ž5pÜ9IJÃòØQÕaüäDcª’·†ê—ÇùâhäR‡Ý¥øƒ¶ /`0»_#£v Fm¦k»nÅKqZò•š¬éÜ/^´¦Xƒ&¯¹QÖ¢ô¥Ø >)S’JŸŒ@Ù‡£@­Ã†k-³Ùq˜íL<Ì/Ç>ÀŽˆ«ì°y)^“2¤²·ÞCʉyH«ß/ G઻6ç~\°@SlåLð·›¡ ‡¬Ué´Í;èïb®ç} ›np=§µ°?Ý„–û‹‘ŽÉܨMš’iñø¶4’H産Fë>­çÃÚª©å°1s§ ·â\YUúy#Ò—©êx-ð¹#QMÛÿ “¾ö+,ÂírvMM‹×5ÇkÑêøÄcv.åÿí ­PmD“ócØfó@ذi<«NlD·Î)\åÌET%Í¥ƒã„*ni´3ª×>¼KWnmFû“ª•°”ψŸÆ R8¥J‚]©6€1âTØcKšÍø™Z ns'—Üwê?1Oî#èÀÿô”ݰ$/7퉡/'Èþ_Ïpmãî.Ó*µljB¯Þk#]ËßÁè;“˜ßïµ àÐEŠ<+púѯç±yb!ì<”ÏÞõ›ñ޹f¶d&¹‚“ñðÁÍׂ `+¾n¶CvN†ÔŸGQ2}ùþûƒ@„Úò’SÎØ»€áí„©¼uY w‰ƒ=ýè”û…½(C£nnÔkÝÍìÍ3:•èA—ùtÚäø^LµÇÃÇþ7&¼ât ÿ†|ÁD1ˆö{Å´1%§Ç¸wŽ=òÀ€«9œAëèµXîá10ÿñ{¨Ùù)ê6  Qã±åë /›Q›1éën,zaK·*žƒ‘¦çÁ¸á~¯šI4'ÙÛìñ‚—ÒRø^&45œQuû<ìrt%]J²Tqƒ:Ýš­eãW°¹J¸÷Ö.È_©Â3£õaÕÚZ,7•ˆìÕ„{+–Ý‘‡ï]jtŸ§Ÿ>2Ÿ­®ƒèoÌã<Ò„MÓÏ€ÙÇgèóþ'þ0“á²yTF>´TBÌŠ] Y%ÅoºÍÊ/~ÀÏ{— ×˨B²g9Ï>¢±E%fö¾À¡‰¼§¿o¼|†ÇY’÷¿0sÝñÿ÷·ˆ(º‡ ç&_˜;É“¡Vä) µÖPçÈ(Ö¶S‰¶_‚R[^ZeÄUÒ©yþ9á|Ë|²Ý$´*ÖBýù÷,äQ:¢kž¹º•sã:7ž¿»ݳŽã#Ù^bX€3ËcõÅah˜¯EÍ O@îÛb!ûb¶ôbù«™<#ìUx± ß'>"ÛÖ½ÆO‡çÂõÓÑdÆ~¬_¬GGªÈ£îíÅÌD¿õkÜ`òž5v =Sƒ72}é ¥ž[â‰ìïM \ÓMün¹ñÊgAÔwàŠ `l.Œ`9„*Ìw°?zX\¾ÊÖ´êÀã‚¡X”™‹/ÏO£ ²#¨÷l)®1ÆŽÛF6ÂçÅFtMÊHþfîžÜ=ƒÇ+Åã4Íï3…à8o—<˜kìú)²Çf?F´”¢~e%vÿL]jÀ·Dqœ•+8mù•ÐIVx,åÏ·Æ“;gVÁK(ƒQÕ47×Õ5]b̓ÃA¾º gÅÆá•E‚(«)t®Ç ,ÞªL/ÖhÀ%וµÿßþŠêäí ¾?ÌŒÊñeOaƃ(,´tÆL·ø¼õ ÞI§lvƒ’éÈIc0Õ¨€Iö#fû Fæiø*ÏÁCùÏ]¥EM‹oCÉN²½ ðôˆ¸®2Zè°ñ&”¦9¢žéY¸®z_xåL 3š!œŒÏäá\çß÷X.Ÿ}^ªüÕËX¥¥@Û&ÿ@YE¡ZÁf:¨õ6¼ýz‘ØÎ,¼÷ÓOJóŠÊ—i„EÕ¢ü¿Þí?}gPrÕwtá—Ñ$[n¹°5(åÇ¢R¾ÅìÍÔ#¸âXþÀïß}+±<¯Ûlê¹×ìŠg¤ÿE/­½Ïò¾Ä¹›œ¹IôPöÌö °zÅ ì»h@ëÒ¿’`Ÿøèô‚ê‡ã¦¾$Ú»n)F‹¾g?u3ºï)ZÄ(ð.Û@<±O›?ˆA³Nk¼ñU@ߟJ*ļ`—ÕfÁ°³¯þÕW·É8UCzŽ wåŸBŸÃkà¢|:¼[+‰ÇÉF2õ_†øá˜7ÆÎË€Ë"£ù½$¼?Òõ­PÁû*ûpb‚M,ïd}ƒÒpýxGpœšÂ6Zéà2+šºO ŒW¦ìà ²=¸m½Æî@æõ¿óOëU§`õ,ðü¼Ÿ=­ß@g:D“Õ.® =Û&®†ÍU× ËÖâ®Ò4Ðým‹Þ-»QB$Tò넱>+0æ }쮑շÒá—Ø\°h®@§Žd¶yaªýšEðýñ(œ’B’Eå0åŽÒ?^?Êb†ØÐÑˤ—ì…í7°ß/ëa/Âp‘m(d!ht®ž|;ZÜ«pëù¥xïï ø™§Ã?ØêrïñUn—…Cƾf(ÕCáu1:nÒm4‹÷Ä…S/à¾{¾&‚Í&iäÊÈ5äÕáxV€[|ÇòDZ3°ÙÒeªÃö?¸çSC 6œÁØüð;ž2eÚ‹ÈRÝތ†C`÷Ê8ª÷ ni(žã;Ù·Ë!TxAm/ªcçD+®´ÍœÎ=TÅôuyÛ—BÂUÞ Ko…¢ïµøJÇJ˜73 üðâ» „hÀgû`׸(_£E ®à ïÓp¼UŸÕ‘£–…+Èäw¯Ù©5É(˜¾T0¦¦‹+3Y°ÞU2:Ù “ûÙ¬™3xÀhkúÍZœ„á<œüg¼€}pâΠFÚ¨¦«—ôÀ•°Epž·â·ªpäI.W½ÄÅø4uÏþÿ6²›¢éðºs6Y Å©§ÁAxµóBÈ[½—†AüºÝøz›25Œñ Vaᵡ.é˜užÐ×¾’Àí?ÁjÆ ˜=uWppóvÆè:4šCßéÚ2íg6ÔÛ)†ÿþ 96¡FV#[ÿ‚ý™[ÆkŽ»ÒÁ™¸ÅìÆ[K/l¿Í®\2ÇMTòà8ûp(ÿsÓ‚?Á£ö;ñOÍxþÅ}$ 0>«Nì39E:ÁB…KdŒ¥‹zQB:–®K5 µ{é´-ø»TŒëß’á[Ü¿‘÷¿ Ì¬‰Ty`ëîèŸS¦el™2Vyõƒzõoòûy¬=×ÉR¦½ÃÙ;Èg©‡l¿Îe\Q(ÇOkcnóeñ›ð ú< A‹Cc±Z)­ÕZH²ëqxôîÿ4$–K¿±€µ¯IËDs*᥂ç“:Ñ9+Ù³U8¥½;Ÿ/dŸÌͨÁø ßÊ Òí3ÜèL…“»/“, ÅyȚ鮹¸lošxùáòµÙïä£lã̯௼ MõDùÌè©<"+GÚ«ÓÒ3ô¹¢ÅOŸ¹+?Çû?³‰ê£ßÈrÊ5“·a–Ú`>µÉEÎÜ4ÀÿËjW=¥QP¼ù¦`î°‡d“ã2Èœ5™Ç¾øÅöxB×å)˜w«ÕI.œÿÇCß.å:×ÉP%§\e"ž ´”¥þ„5Wç£bc,ŽŽ‡þ£ð½Øx¦½¨@¸#çœnÙ Ç·{±#½V¸)g|íé¹î ¤‡"Í~|zÉ&å¼d“g\€9³Gòñ²6Ðyæ9Ž´ ~ë«€ùûÈœußaëÈÁ¸kêgÁûÈe|ô ÆJÚfáQ•lØÿ>^³j®ÅÙçŽ0õn9~¼v;–Í>O¶<[Íf­€»3† qqî;×mzü?64— %"ÝðxÑoÒ¯¶ãû¥éªt;þ¡¸]Ö§ìdd‹_ˆf &ZíêTdò4\º¤ ò|žBŸ£$m^䂉¡8>=u½Ë˜‡ö6L~mO¬úôðí«¡ÜXFž­}àH‡xż«b¯Œ_³÷;Ô1ãS½,hÞz>w¸´x˜ÁÊÍo@fR"»[$ oÏFÁÈÍê0ãø¢sÕ·Lvšµ]ž#Ÿ`ÓBºÂY’–‰çC÷Øm¨¾ú¼Y¯ÊÃ?Ç-N‰dÕÈÉ Ø|ø®Y¸n8ô¯öíz5`ÿÈ›˜ÝøRUXÂÝã©—­)ThðÂúQ\J»f›¬àWM°‹ç '?U£n‡¡ q>H΢•y–<åQ¾:±ÎØ|‰IŠ|‹nî­åúe¾ª‘ä^w²™oä0Z%U)o¼YÄ?ª“\ýcñFÃx‚K‡rù¯“`U¼?pœ;ãÙÓ±Z´I3‚··¿ò)’u.•ⶆkO\Ћ•CÏY Ø·élñè@íìÏB§ƒ¥pëeŽ•rẠù®¨te1™íY^«&N5t}Èڋ݉u°»(EûŸþ·äªø¥ÜßþŽJçØeYÄ>4ØA±}T—ž‚ÙW*q^Šæu ‚1»}Q´v<Å?kÁ{ú,ÎWÃFå`+ŠÇOcéSfàN[ÒÒ¶MzWò)Ïö ŽKÛAeÜTÒYŽêKõ@÷Ó[|(F/ÎAûoŒÔ°$G>‡õöžðr‡! ¼ªD%K'Ò¥Rc@ñ|µÆ½âoál^Fœ"ûµAŒ(ò.Pvu™ H/]…ÿ>^oÿ_dŽ»ûÎT— r–ûýIÐûO_}÷©>íwICÅùÚúgE[¹›XŠÆf 0S8—«ÇK`€n;ó¾ €‰·/ °Mgß`›ÌæÀ=ikê_[*0ô×ÃtéÐzðŒ Ø!‡Én=G¤5ƒÇЭ˜PX«SÖðëKwÀÂèÚ"S±÷[à\Þpºõ|- ¹î Ç'ƒù/ûC qIá6=!žú'jð“ÛþT”ÞÚщkz OkQ¶q TNUéD¶fõ>r·ïö§Gâ Êßp^7‚v*óÝC=ð­V"ªÕf¿Ác$Uzym¿™n¿:Ä!Ýøµ2Žö»p©qΈt†Ë7`š§!ÕQ¦bëx×¥j0»_‰CÿÊðH&Î݉9Ÿ¯Â×ôÂáÒ+¹øÊƒ ¿é_·¦M0›5ƒ°6ÚÒ£ö3•-c ñÀÞ(!ÁÒÊL¨ß TkfÅ?iñL#0q?E$x°ÈrõØ_4 .©S¨ï¬ÄÃ{'ï¼BéÖ;í"¡G¶ðï ¶"~u¯½§L!mþz¸Z¶²‰2¾±®J?}Úðã'YÚ}ň6‡Ad4Àÿ½íN8µz6þ; 󦈠ã½#Ø|S-¥ f'@ëà}ø%x"|+ŽÀè/aFå;H·²§cwÜ€Ë “q‰ZlèN͈ùs‘\ñô"ê2tªW4áèå8ù­)jŠOâQÝ‘à8ÄŒ§’¥«þ僒D|°ð‘Õ{$|´:.¾`E®*Cù’(pý5…Šï«ÀÔy D’‡Ñ“kçáýì~x·`xhéñ²j0Á>~cE=ºaý¶0ö¹)„Œ^³ÎÕïÁkŸ/ AÛžið ì7üqëÁìk3¨‰(г]Xäâ6€¿\÷ Ž0¯£wðÚÝÇ4ß5ŸLTå «Z°t~—ÙögvãG.N§eØ ì{s§ÆÂˆ¨]´èMèèi6\ä€÷ýRÓ©ŸÙIÒ¥7‚úæJ¹Q qCŸÜ˜é‡N¶øóÀpþËY„Ê lÐ ±ÝÎ’&«ºÂüMo +< ì¼zq¿ò\kRÇIÏ-¡K^;òá×ò/K§‚©¤Û¿ä=4ÞwçfÃþà·'#0>r?äרó‰c×`AŒ#„•‡¿àR“@zç.PùÕÚ¡áÕiÜ´]kÿömÇPê°+‘ˆ óá©át z}>ʯkC0C©†9¥šÐ9Ý+u†ðŸtñIÔËÌ}6»Ò£$éÓ'_Éà6zð×U2lª+>þ¾ þhæ=ÊÁäÚR&ËÇ×lå~kÕùfÕÑtQÁ/¶;^ƒ¤¼°äbÇyά pz½ S+C¤f¸ ¥[$}iI='Æñ£>g¨¬ÚC0Y­ˆ;\úðç[±1ŸWía_׊°’¸–Þà.›[èMkÓH×™zœ«{nz•›tQªXo †Å¢ùàøx4d‡é¶óÍ$5v ÷;·Ÿm°}Œ]‡vÂÛé dRC.it8÷ªVðô“ÂÛÛï°œ)æø&ã&Zæþ»î;{´(—÷±ÂPi¾á¾e2½8Òÿ&œß:ã^ÈÒQ’1üf špŸ¼gÕ=Øí9ŸdsÛ¬wìYJ&´dCÝ´ExôcÝ´ÛÌ:¬v_ˆé¿­ tAß8Cžò¶tWŽ Ûmè"ﮋï䥸² v´IR›‡îha:ª­ÔJe¦¤-FË­X9ù†nºs¿&d;qµ–:«8Z Lô$þ\TªúONŽwqrh.<Þð {ÖÜÁÆ·ÛIÔy%ú9c'-xzRÿÈÐÞé"ôk©#uÚéÀCcÃ&zçìm19:í‹*ººÞ«õ)ñ¤ò`“Ç,ô‘!T#˜CŒˆ&B÷¤> z¬ÛŒ»/l/­ìNçrzm혿”š”˜PWIz^ê,kŠqý3Ï 8A0`ÿʤ8½t‡9¿KÌZÌ_òÁ$Å¥YOE¥’:\§;‚«­q¡:㎂eÃð8HÒŒ´-|c˜–»R»¸F˜«,ɇØÅT?7ƒä+,ÖÕ¬¿¡ý_ÏòÅSõÒÜ£×¢ rYpìaVf;û˜÷|ü/¶º†ð¢K0ìƒ?\E~QÄ+#ŒàÚÏ>²~¬<Ûæ.N× y*|تDŽז½ƒÙ{c08c  u†IíÒ°ä¥/’Á¿ܤu ̯ÈF_ÐäìrØtÄ7ŽðåóßAë™X\õ—å:( ä¿ïåAäü“ÃØqVš>¬G?6'ÇuùQ›ñ()ÁÏ<· â!99ôŠ`éV?¾4£…n–¾€?ÛÀÞ?§ðÎ##Z¹@†ÊÛµ‘Yë^à•Än²ìI0íœÖŽ6[¯Ãœ‡GHëì—~9œÝ1Dw«òžòet(Êl<Â^»…QÛh~îÊ^ùõ­|NÊ^¨i+%b÷F]ÞØk+(MþïRŽÙï>¬Ë·)âÁ`üüÒþ¦W‘ ý¤Gá>›³´­K î–£Õ‡§Ñ…ãú¾PÔªam¯ p›W @‰?Zrxÿ¼Æز[‹ÅNà…ó^ãrmQ~¹K‹òU¨txµ0¬ÙŒ&;ÑnjKdÕÆ=˜“ð†È­:Y óYóIE˜õK‡^ÈV¡¾×(Ý¢nË“Ê4é2ê …óéý9gHÜÛdÁÖ×…àPU,v 6&´CÜÒsèïß„‡>ÿ$ᅵÇG%PuKžzûIñîËá¿þé†= ì˜ùHª6yvÇ^E¿Sã!>£ˆ}sØÇës]…?1Ü51r /À쯭èxX‡Š7‡ Íž$ÐM¶|a²,ÿ°÷„Nãý.ã‰ÍOpŽŸ;w¬ÿ¶û ºŒ]`ÃûE àçBJ–ê_´ùú¯|ɇé¼÷ô}f|5oŒÂWGKÉ´¡É85æ>ø­ðÂúåyìù“uxôå·zs‰ñ‚Pºâ_Êþúì3ê„O}¸˜Ü,¬ÿäÅ¥k–Cèö×ð,c.ýÖªJß½S&j­eX7l׎h¨}f<Ì…ÜnéÐÓõâ4Ôu W8<ž†¯œÄ(Ó—£ùý5%ðîa0DÇ}ÁÕÇ?À¢%ž`qWôÍ£/&èð˜¦P° o®ã7‡ˆqvêe­Omö€&¸7жÛ£:¢êÿ>å(õb'tËÒ!·µ`f^/¨HÐ|6˜«¨AùƒBwµGÓRTJ  m&ʼp)>ü§-ŽDÂÌ9û1,ê?ùÐðÀtÇBÒ“C°íþ_råòeì›]¦–À5é`XüSŸßœýFо¿1êˆ_v[œ/¼üË>Ää'¬kćE^dÃo£çàv_˜cL¶ùƒgîX¸™yôF>(º*ääþ²FºµkM膔{„jP]Na‚7c&ºü<,¼m =ŽŠ|‹œ½<'JWHÑž-@ªUéçŸß2ƒÛÇ9~lŒç“mÝh§ˆ8ß3ò:X­ü—m.¡âm%^é’‡Â'ÓxèÐGxëï<ê¿—ÈÈ㚢¨V&Â'—¨V®)õd/˜Äesj~íêÝn&«5CH‡íiœ?ï%¶ºÓjkŒ™³$%£P¥ê߼чözNkœø·¸ïa5+"gت5æ4í¸­¬zêûÆr•ÒBˆ°[Çb^U²öäè2/Öe´ý¶ úù>F¿Ï/àÅÃT~`³÷û¼ž)£žIPÿ{ ;ïoÏþÔõøÝpúÕ{p/І~2 ÿÏ‹¹ÂùîAèrHVäó÷Ñ|¦±'v|\ƒ;ÃØ„šáüeÚN:Êc#¿—ú6+IsÅ©× {ö$¼u#Œÿú­‰Äu†š0{Ït2Gk ,ÚŠãNŽ¢6F{ùÞ–mÄëùOÖíóêG"›”z Ó¢S¦õCGÂçˆÃÿ´v04~"øó¬.o¨ŸIsNL‚¡/ÜøË_7˜}b!èFY@l2f¸âãÚtlüdÌë æ¾ßƒ'_+0pÖ ²þ¥àÂl5ºq§ŽOï…îî»èôw??“òß¾9¯¿ÌæÅµâüäÔf¼p¥r@ÿŒß;Ëõ8Ùm7×½w¦e«’Vs~úêÁƒù±Ô•¹ÀV’Þ•ã¿-͹„úlî-¹‘«4ÌãôA394§¸ ïúmÂoÉ&ÐW+â¦de>ù5?±ùݘ±bÕ¬ÈëMnmz‚.wÔ£þ49qºŽ ¿Å‘ÛÄö„~E¢'ê µD›'_YK•-ÄøÙ›11rÖxÒ´u:ÿíGÓ#fó¥ªÏᄎ7w’ý§ ¦¢z»3ˆ×C[t{þŒ‡]Ƭ“ópíÞ~Ôê ]9†.»r;dh²1•ÍÙ@EU Àb^Ñ€ÿW‡dCK‡'`i‘š»~W¶7ß¼6§&w›¡ä¤O8X¿ºÏŒ‚·Ë”þ”‰J׊2¼Ê]«,qáIkgÐÉÍ¹ÔæYæ¶>ƒG"õBŸÁqpÎÊ‚ûIÉòQ{ªÀ#Ñ´]ã:|ÿ>”¾h–ç£ã’¯æ°X¹“L!¸ç’@9™cÉÓ¨iU¡^¶ÓñµÙt¬4)ÐŽ¤Î&t…r.+Ä+–Ò­Jù*ÃClkók¹Ÿh?ßÍv¬ÁMÏûð~n/Ÿz¯4°å2Át¦u:[Óv‚Kž„Ð’¤ÿר¦ñíO™°·–­ï¸ŠBõ;8z×z/¬„˜1™ØùÌ«u‡ƒ· ŸF¿ÏŽŒEöÖÂwݨžñÔ~ýb—µT´æºPïL? sh€m ×ÑxóOŒø«K³{л¬~™ù”‹Å5$~Ž:m]èÉî²Ià’<HûÀ½ý"¼áÌSíz5îªâšØTî¼^Žªè8²Éúð£ Ϭ¿†—cÀšˆ Ÿ5>…íÞàü|<"¯G©Tà¼]äßØð¦“ Ýh.d¿ßFÍË:ðæm$¼Ñ‡ÀfSª°¼X¸oÅÿÖòco˜^Ž4¦V]ÆûFS`WܲÎ6Åêð i»PbÎqÈøöó<¸Ø9º]_ ‡ê‹ðË#°æ¤»»²\dè—+ì€Á_®¶€nr»Ê´€õõ®òRŠfÞUe júüùEî·f5 Z¾ƒ™_LäË5éÂy~íÙ Ìcÿ~°8‚ùG÷›T}¨I,Æ uDfû51EGQƒ_øÈ?þ=„f$)ð…jÙìÆ?Ðˉ„Æ!Ä¡C3lI§Ï}˜Ýj‹á,PÌŠ®>µçO€ø'Z4÷§?TSàÿKê`èò¥ïóðàC8zõ4nº{ˆéV¾®§úòSàI'œñÕÁ¤Ä)BiM´%Š #è_ {Ø^áÉׯó@«8\\í@ëå=á²áQ\î`&ž"üþ¢³ìÙЕ8rÕ'Ô¥ÔÜFë#Û¸×uQ Ãü Éx>Þˆ<>Îw›¹RÛ–.\~ÖŠZjá¤p:=´`}ÿ ”ô‰Lã£wB¯¶¸Ú0?eCǧe¡vk-~à Á³‚“ÎÅ%f°ý¾uûp`ˆHÝ„·ñÄïÅs˜¿1ÜE™z]sã¾¥±ÿÃß2 vnÜ.‰Kð£ ί›ÉŸÎ“ “té¡¡·°?S—‰¼…ýËÔ¸åï‘|dzjLpRÇÈ­+ùxUs.:%ƒö‘¡ðgã þ,î5,3™nùŽ*¹Oww–À{H|ÖQ7=Ré_|^’Kÿæ•°<–º7X£wØKèD3 ÑwÑDº5â<”ýÄÇ7.£½Î¼ÛÈr •PC¥ n3ü* Û/FSf!‹þ±×èìáÍ“Ò-#y¥ù28ÛՎ韂ÎÙt’Øß q‰ã CÐ,;‹Ö?‹ão7¡cëƒû•ë 6Žjþ‘D½¯Œ¢+f³Ù‡Ü@ØTÁZ§Fv3£’}™¥CwŽˆ#kråøèV({wx>xŠ‘¥VìÔ´Oðdb—àÅc‚ŽPÖý›'ë…Ò{ØmÕáôñD~mv-,JýN‡ûÈ”ySà¸õž=ìjMJùé¦ÜKòœà‰s˜È=̲½KlÖãäF¥Pqú t¯-AÅ0c´%­g Pž×JZ ìhþ¾"²h«4ŠŽ’åæ3b¹«ežØô‡½¬ù§³ÓS—Î@½üÇ8×LŒ;¹ÚÑTƒ÷$Îí ZúkÀÿWiƇŸ„,y•4õøÅìW¬Fý¬Ó|Ͻki<>iÊ[»ñÂ3Ãéo¿ú KûÖ¾ø–†¾{ÂQ8s?˜ž'O»Ÿìÿb¨­ÏÊÓ.¢†‘¾ó]ʳÂwÒ·}ï°³¾æ„­Á­RˆJ‰xDì¬J:Èï-Äøê œç„aÚôØÂc ¿!{©$}í^sœ÷ÃÅÙ—pl–1›Â®0D7¼¥?²Yzúgïæ[¸Ö”ûÄàþx½Ó,X©¢Ar¢£‰í“#dî$-RÌlãÃ@{õ.fÕhƒ>ÛêÁ<›Òq£EápL3iÙpæmôd§>«ñu~ ”)Ë©”¼ðge(îÙi‚%aVÜlÅ"4\µÌÿi’î°¸²›}1±†ë»tphÇ«øw2:Œî­¿ñõ j¨x6‰Ú?^ðêî5°ÔN ±âa$UÁÏ… š jø»/yÄÒ†Ë:ÜÜ\–; Ñìö:kõXn¦¶ µfEwsÆ~k’“õ‹Ñu™×Öppsõƒ!ehÔº¡ÌèÇ`öríKpAó)à~ë FGÊð½}aì®EÌäã,R,?—¯xI±¸Ô‘dYÔAðžÑ¨Nuy‰òZ ±(t6Ã»ë ¹ë4nwu>¾QaËçwaa˜"mÓ6åj!–T:¦„Žð¼ U=Çøá§KPæm 4øá³CN±;§ øñÁ–ð¨nžŠ:‘mðÕãǶú 祅òžmî("Ø'JîãÛ˜a|‚ìR:aåSÒµ\š~™®?ÆD‚L‹ ô´DR›XÜÜ7.:l¥Ûö·Áè—K¨òí¹<§¯•{û»ðž 'èýEGù¾Êt*U`Gv•kó^ÏïDoS-¾ê˦ýoÕ·xÞÛ'”g‚ñáM0RN“'ì>B×½úNF 5£A['so}-~!J’6•Œæ'ª\0ùD ØN`0ò|ºÞ:ýÏðÅŽåüelî“÷ªµ”{‰aGì¢ù[›åôcÆ]ˆËùßúÿÐÉØÔ4cؼ½W˜ßR†Ç ÜðG»Ÿ´š>~1¿]ðq5°hÁïÚ±l@Ýxû=o¾z£]ë²v&íJ¨Ñ„pv;Ò´_Ûòr©aœ9ï»Yð]N2xýÛ_†{›áap4~P[NìMƒ %‡qÛfPŸwÑ~Š— INRÄÓ->pîR>ž™"‡šþ£ù…¾ëpQ½_}Fª³Íø:v¾=*TÝÑòjwôwCÇða\÷‹àß½jÛ~½Å§ZY@tÃèðý'a÷•dÄå dq€ œŠkF¯Ù "aCyþyýú*Þ^Þû;I«ÎC?·S`¸òÎý¬Ít‡›ñÜgëøæ×áݪwЈ·™Ûªc0~‹ÝeI¸ëë´Ðe(„<ßIŽM˜Ãç…èÒ¢å÷àØ]ÞÙŒUu¶¸üf¾wJ˜ÙW{kÆA^sª-~‡ò‡ÆÂÔ‰YÔ—Fת“(­A¼hr+Fooc;bß‘ׂå§hܯ ¼úƒ8?µ^‚¥Üan™<¨ÉQ›‡wÙ¥ÂÉ‚1îùÐòD[ é§Û¡q%qÚ•é>lÍ£!ôÕ£ Útèþ?å‹U–"tÈå¡Ô¹¡óû ð_–¯ <ë|OÔ,ZØ7ßzx7ÿ*¶ÏÇò"}ýý!þ~uûµñjlH0ãU¯¾Ãþë1ÿܼ|Ç5ÏfAÙŒ4¸5—Bg•,SˆÀ0//2¶û0æ¸õÁþñbÔPvXeÒ‹_ÅÙÆƒ½¸/í'ž^‡ÃêoãÚmÇ…ZCñ‘½3œú( •>àJµ.~vUÂ/¥gÑöi/|{Ÿ‚·náÈqäŠx+^xA<=Kñâ†åø\KÇþÃÿ[ÿk8ã¨pÑÝT x3œŸ¼"Nr5~ƒ¼’%vϼMFf63Û9KpjŽ7ý4i'Ô><ÊÎ=`•ºb|QZ.GæHÅ‘‡¾#híS=>ILÏY´„UH¢’4•¹x6Ì¢I £ÀDÍŸÛÎpäá…‰¼¦Dÿœww§¡‚šüJÿž‰e;(š‡ëä>Ú+¼g8g#„OzÃþ|BŸ|6á]]¨(%,°¨‚Øê8å w}üÙS*ýUò´ÆÂ¼†h™;—«±ÙÉpÜo-h4†µ=gÑÿ1š<˜ÿšýø_é[_&T[|‘…KöàÕ3ûaÇÊpì>ž[^EO>ÓdúÖãH¢Eõl–Ýr`Ø»þü…eéfà›{R;Ï_þºBŠm:ˆébîÞ÷(ªX¢[-Ù]Ÿ ¶9ùn9¦Ë¿êÐe&úüØÙµ‚k&ñ½Y$k¬WLâ‚h-Ø1§ŸuÃxjd²ëB3Q8%²›«àÐ!)~«1”ŒØLhô+2YñUíò•ÍxþË"Z}â6þ_ ¿¿KQå}Г‘,œº:yp)¿ršE|Uk9í.ÎÿôQð.S”^n ŸÀÿ¼¢(k<:X€¶*eÚïv€?Æ…< |}WYK{æÝð$‰é¦f ^ˆMâΗYfv Ð'“¨¢¡'Ø]…‹^<Ãå9ÿòÝ©™`î=ã, Q±ä:ú7^Ä5¼ƒtjõâêðAp _¶ÛÑc®opÛ^î:ÊŸJ Á9ô*ùAé-,ß8Š;Ýf•2“Rü§.{R=’Îuª…Ól@*1 ®oï©UZÆ4f&ß|‚ž'Á§"ó?]Ç}’s°Wˆ=º|agEž²•O'Ù×îÇç·¿Ö,ÔÁY]†´ïÚVT“€·¿…ØŸ¦`0"\o}a‰ž[Ðc–<Š~Ž&;©Êõpµ75*àuºhæ<êws8ë âï½´G¯nçfoS°rÛ t÷µƒjé¸rú 2òI*†ïƒþ#kŠ ÞÔÁ^Ì»mÎv‡IÆàð]Y¸Ýï0¿ xˉ<ô2J/kMá §ÅšÈòN¶@ý+tÊ̤—ÆFa|p=¾)ƒÇ:àùôFÔžÀ¡:Ñø5NúŸˆ{»ŽáïâäqJt¼nÁË=Y8¸€½þZŽÏª÷ÑÑ ºÙ7­‘tÂEuÞ¡ñ—”û k7?Áï9ptݬÄë^EéI`à£B³/…áÊmßÁxt&™Z0Ã3¥8|ÝAµSÚ—­Æ÷V+¸ˆ‡%ï˜ñsåyOàEûdú§ Î|k#e aøe›ü€ý“žÎì²v¼Q*áw lf ÞnK¶W—ÚÿöÞF+÷Òd3sˆYAÿz)Ó‘­)ìgL¦ð¹ÁNx;Æf î2«laöHõ“ç;¶¹Ã8)Wœhþ¿–‘/YËyv¥€.›}—¤.'ý«Ñõ¤è0i”{uª¶·GŒŠ(æ³¢$¨< .þ(›>o‚èÛO¨±[&°zò}Ú=Ì÷ÆzÍiåSši)‡çìdÁîÊ lв…%©É‘Ƙm¹ o¤fbØ2¸6³'åkóP?ê]¹Ê«Tøg1Þ¤t+®àÿ;côÀê©*j­ÀêP·Š“ãƒm€þlų7Ks›:?þ²Tœo銄!îÆxÚS”/8£Fk¿±û7tpæó3˜=d9,èÐÆéCN¢½ëS⨈äQíhÖVÁ‡̨·J?ëÐM›?ìä—Þ‚µ1b «tFiÇÂ|Õ«¨±P•ÝY2•Z|pô> ™âî«]&Ë.ýFlE/¼ÇGA× !ÈçsÖÓv—ʼnïTPIœ…RôèÚP¼–"T1÷G_¯£x:ú x%3÷Õmì®™ÜÈmùM­BM™ÿUQBBb–‘­±Ð4ÛœùŽSúNà¯1Ò|±Ã#LjÏo’¢œ¥¥«LšèMȲk$Ï„û#’zð—e»`Õ-13“EùgÛuž8%²ÁƒÊ›ªa­Ý14÷è@™Ü,¶«dÅ;Àøo0\këÄgm÷أ߽mäÚ?`Óæü†ŸCÊ¡ø•7M¼7*ÎÂ{‘Rc ‘\ ùêšt¾ið©h8XâW™Ã2Ã$Ù´ Åóòص)óKœW.€ºa&ÐwF–î~Êâý•¨2Ç™öA»ð>ÞÀÿ€S|>ò‘­Ý!Ê=>e“(Õy­Â7­fŠ|ùäåG`߈K _u=65¢®˜½¹>‹¹$¶³„÷ó`ò sØò×7/Æå?*S[ßU|ZìNx+‡Ï©Âø_­Äœ_D픵|Œ¡.4®mÞTÉ›®Âœcò¸,7‘ÏýfÏ·ÜÄ zÚò{§~š¸Õ|`ïFžÀÙ)ÔTe8/jǘʰi4mQ¥v¬ «›¿€õþ‡ÌéYŒ=)컸‡„È{³Q? .,œK#M÷Ÿ§hð-üÙ`T ®6Š|¯ÃO¡Ì˜ü?Åï³S#¬0jŸlî8 ?•¿ «<çb—e1qªÌêíMGÙŒº>]‚ûÒíø»\‘>÷HFó•ÅøÍ= 6ê‹‚té4ýa;êõ'@©¶ ©#ã订$|²âÐÈ;˜±æ4sý}ê4)Öq0þ#¸øåÆ^YŽ;ÞŸ†ôD'û•G~ù ðc‹-ñ ­g^`:9•Õ¯œ§Ú£Õà&éèGBÍr)ZQ—F†6ãéâ<ûcƒIþîh¬û /¹±#Z0þÝf,º'ø7vhšs÷:d€ˆôYòò÷|pÚ þ¿ýÏ^X­¼ƒöõ&DÚ ;1TôüÀ3ÌÑ× üž¨Pµ’ë0Ôs3\ïg¨:Åš#]ÿׇآ |ªö~Zï99ð]-t-ðÜÜkµ=›QÍd›'ÕA†Ä5Øéှ ñ'€Íô ¨6W‡gQRÙqŠ|2éñÅœD»ÿÓêZôÔé½ÌÏ4Óî.åªS*˜ö…=0ÓjËLøË|6Kò$Ï¿xÓ©\¸˜Hòà{G¨ñ ·ÄSz6Îî%Tg.¶¨T‰À Á^tã>]´˜ßFâ½ìí¼~â&ú0L›^Ÿ1ÿ?ÅŒáVU.†¿ÅÓõ‡Ñ36Ž©Ý5¤wpÍ‹³qÜ…(“+ÀòYj4}ª!S•ÆEÎf£^b‘à·[/ IÏ«ÿ.ášó#¡ga,y•²›tÌ; –—<ÁïÄ;r9]š¾4Ʋ«ø!ÃAü¦Z™ü]Û;VáwKm4õ|E®À%xµ:VŒ± ¿ï›Ã®âtÌrP¤³%2óUƒè… ia~e8¤Š¹Â±`¾UÔÌ¥¸‰¹&`BÆ8Ò¶’¾ðËE* èÚ0ÎßͯõÝãaùP“‚g¶íU§b|ÿË‘ZÏ‘Àßâ`«x´˜0,œö–Ü3 …Á®èÜv|=Þ5Ü!|¹M’ œâ±¶LwMàm›3Ñ… ZiX;r;O8þˆI´ªàè‡r õ$‚hŸàdÊ´Ãè¿¶dE©P¼ ®ÁœzÌ Nesl¦Â†1S`³ …ãí…dzœX«•¯îÚŒÉó2 øg;J6ª‡v€a‡qWÑ#²jWÎa Áލõú"|¿ òrÈ¿û³qÒG°j›KÝaB_Â*x·Ò¥åÍñ§úU’%wFÙ×ü»*àÅíIèåá4 'ϼ,p”í…#V£ð¤Ï;Äö0Ðô“$‘#…öc›çðe7‡Òä°I4€Ÿ'2¾ƒÕ¦þ8{n8¾5¡ïïvûÜ)>èÀ÷ ™0I~ë:-´ÕË]ôςنZÚ4_é„yKÔnq&§7²þouÙ®lè(HfÝ›¦ÐÅi <{ÑA ¬;©?¸l+XÏt#™Û娶Ó3X×Fª]sà…¢-¿xc*ú¼DXã°”Îph† ·PÜP†—5Û`vC5ÔÙ&,²’ff@BŒ VѤOÐ¥;ˆúŒÎ}¿Éå™Ëòß½¢hXª²¾ÎÊ…(‡GÌ~yîºäŠÝ°úyŽ;»ULXå¼ft¡˜fDMÚGñÖÓù§ ~ˆæ²üÛ£i*Æ'놰}dõø;8ùÒp\ ah|’¡¦žýåßË&_:InÅã7"ǺWÞ%ëu…ØqÙÄÖ±Ùã«èµÊñ ºuß2X”Ï[n[¾´¸oP·0Æ<þ.Ó¾á¬þyð<ÖéX´üËgûL—ACó˜ä!G÷Q¥¹ÍöŸïÕÁ›'ZÌdìM¬Ih%£b= kÈ9°øš?¯_õ–àØ~ÏüÙ?ú±kÁal2}Kî.ã' ¥oÍcÉ䌅ìÙíö·éd“] Q¸M‘îõކ­Q…姦aõ`i4yß ÓbØÝC{¨»‡MÆíøþÅJ^«9gïØGöÌ£o’fóµsè׈Kàó¼F°½¾WÛân£n6rçA<´ŠVÉ=ÆK vüÞ˜8:{ñ ÌX4šj×v°·rq´$£€¨ÿYÀ½DÈ¡¶kX5xMûu„Õ°…4ÌB•ÒîvüüI„\:¼“ âñ&úx°ºŽ}Wž+pIûð§x=½Ãžmà’\úÃi¶6»ËDÖÒÎC¼óúbn½Ä›áÞ°z~ LÝ0Ž›ä0 ήÆ=AíìιT<ìß—¢¤i®™ ¦WŸE_ód}×€¦ßÌ+ÆÆQ¾ø³`ÿþë×)¶”¼o‚i‹6p_Õ‡¤ŒêhÕBó ºYâ4þN0â‡öùð#GóûïŰ´/7Ÿ×¡[„Ãé‹K¡léžø¾u;¹úÃßk×àb¾Æœ—FÀ­ãÛhî–’ÔÆ~'îƒÞ“©¸Ûã [5ç=Ú:ÄÂÃâ_ø&.‘„¯õ Tæ|Ô:u’çn@N—/¬;õ §|0§—Ï?ÅÛ}¹X°p<ÜÛÙFJ|rÃDzçdžÀßb5ƒ5r*´pªý­-Â…ãð²ÝmbÖv—o«ƒ¿³}ÀÑï(ÝYîÂWB/ÿÅ'Z/®·>k>á»ÈöÛÑŽˆÈÿ¸¯’\ç³ n»Uƒ9a{qWÀ&þ¡#šÏ3QáÞGGòáþK1sC.Þ;H°f Á•?ÌiеÝÝw‘}aC¹áŠW´¤b1™öö9‰u+î‰Åmþ\ÁâÝÏñ®Î!ü ¿o¢ƒÕ¯à‚ü©T¸èˆêûÛØ£`›C¯]9LU/ÂݪK±Øê8Ïj„Fåy9h¡øá¯<²$N-ÔÂhú¿ßÿscŠ'Ùáå%Ìf¯3œwp¥–Lvl‡UÉ܃_O\ÁÝ–²n~Ÿ,K‰âÍë_Á…,"²:ÇÍ·$…OJ€ Â}A1ª&žÁŒá%иG Ι‰ëæïƒ®Õßáð²Y|yÚVÆó§îãêÆ~èŠ+&·¿3P[r Ý®úÀªÔ©PÑÖ†Þ­?ñq†ˆW‘°Ë•1‘÷?É·“‡ ²]t§\Yb§q±óþ|礟e âh,šhÑ«7PX©DÅLÜTmqÕø© .XƒëÞ' ×Yr%$¨† o@ÿ,lê"÷ã4aZÝ“¹‚‹ï9Eâôn~3šнFÍeaÜÎ$…5-NäJ™sa¨±:ñþ~2s{=«³ ÇÛt‰îªcxäÁ æÒ×ŽŽƒ£°Ì4Ÿiõ•¢`O ÝÖŒæWðöBqn©Ž2ާ1òÍrð~:šžWAm¯a-݃÷м{è_ôÚ³Ý2]hòXÀ”¼ñœf x%xðÚì¿Øø VN€›ÊR\aiâ…cS*~•¤çw–¡^ðp|° ¬ Ùeõ;ì°Ú#8^»b,dqñÁ"-V.0,]ÖOtæ?=¾“]õ@N]ÁÏŽa~) ?[NÓ×ÎipcßXÚ\1‹ŽÏÓ %ãCÀH²ï=@G§—*ÊŸw˳t½HX¨Ú]¯6âæ­ÇH{¸2–¦ÝbK¾ÊѱÂF¡þåÉ\Ìé*Ÿ³s„,xÉ-87iòøôœn¸9ø*ÞÙŸ€Ó»±t³3M‘LÁšOkÓL3…­&w!¢e ÇiþÅd87ý¦ º£iTl ¼Ñž e çaò¨GlBõÿÖÿº »ÆÄn#•5ÁÝëT‰L€XµÏ0§]Ü{ÝÙ»€tmš@gÚ2CÓ DgHµ`š\fM{ÂÒºaüÞ i¾€*S­êgüÀ ªº}Q³÷|¥FßÜóÅÝöùxzüYÈéi™}`ÏŸ`½T:ûU2•ºø#ºYBîyèŸfYO»WO£ Nׄùµ~8.`?¯H‡²üz\¹åYøƒ…åº@aŠ š3'ÈÙó!Ù¢8Wâ9ùìˆiø—ë”y”xãÄø¡X!ðdVõ1²"¬Ïîâ;D¶Ð†g8ÿëo1A>GÇÒÁöf[^ú´ŽÌŽ Ç—à¾Çï`ç¬SÈ/¢Ò@{×þý«–>§¬hf©Ûè¯Ð7hŸ[A—˜mäŠ+`º0±vSà1èϾ›÷ýa‡áQr< Ý4‹îpw‡ß±ÖäHðˆ4ƒ¥²˜pNu>yâÍÇÇ0/ 46,€q¢ò¤cÚS84ä‘{4–^_O#vÑóé‘\eI<šïë-XlîW0ôËÚàæëòB$’¿Ö8Ê~{Œ¦¦a{aËåsDÍ6vD×Vy,RXJT lÑéòì(ýJ’ÖOïÀšµYxvãpø7«,-&Ÿ<„çӥ陹÷¡v}.,/ùÄV‹À?hm¬ÀS§ &Xd_ÃrqN“ -v/‰|-G!sª.jõ‡Ó1ocáð’0ª1k?í”^;¯4âÁÃG¸ãô<¡Ðzï–ò£Ó¶fA†²6W5ýÇW¸ÿ¾=øš½$«FÔ±C‰#h}æk¨=o[ªŠ™»åWœ''ã?†ÛÕwpô«ñ<ýÉqœsÀ–¾t¢ö’Õïì úGƒáug6~šˆµ|˜¡ÎBötó¸:“ºŸÒ C¦Ñ"Í6¬Š;‡wvNäsÒ7×~9»úâÔ¡£ÕQCî¶¥mäG¦¤ÓåߟÄ¿µÔqÐèÖþTûú }Ö9ˆl3c‰=ª¨ûÚ…ô{Í#‹—Äà‡©Æ4ÈÏI5cÍ®dÓçF ½“1f´î½¢LålØ•=™¸)Fw¿ã*­ Ózò1èÍ,Çomúßú÷—[TèF')”á/!düpîôù9$ÿ‘¦§¦ÂR¹˜n‚Éc©[ÔDª’æ…¯µFó§bcøìrm<Ϋƒ9×ÇIG›[s(¿š‚9ÞV0}eqnXGÒ¦ÚÒQË/² [ò,LõŸ]ú—Eº-"ÉŸíñMbº~aßÊ…˜.åƒSöö³åýÚ2Ürúy*ƒ¾ðf]):¥0eþγ?+àê0øRóVúÚ³/ͺÜ5z)¯\Ý) üýþl:÷’þ»[;œ:éGðXõ}äfn6œŒ9¦î¹ö¿*m'¡ PÕ´OàS=¦*ñ'•qÖ÷ÓXßßw5,xÊIl™¼ƒÛy€Ç }ïN_3–SK¤&8A{Í[áSÝYTù¾,¼¾kJ쮆J]ež6Æ °Ñnë5 †¼¸»Ç´“’ýoHbÑOJ„•9û”íµëƒNE[xãÿ‰E•Ú“ã¥¢ÔøB!ˆ¹=„'“ÈÕÜéx¿Ä¢WŒÄeÎÌÐíÌÿi/„ÑxÊù)ô\̃€ùö m·Àî‡)íZ`‡Ýr_qüfKBV€aßœ¿õ›pï |ܼ<âGD¡ö@þ;\¾'Œç¤ìe(Vžëãßâö€xo )ĻɎ>}8›æÇß{ÞâË'8âÛܧpî{"msÞ@¾Ž¦÷–Ó½NM`ñ ¢wÔ“å'Ño8nSùÏþ÷ú¯‰öÒQÖfTGY u’3 èéB‹‘«ÑØä<œ2Ÿßy1ŸË·Vâ¹ïkarà({ïCïΙH-6ÌD§R/Œ\ŧHž ‰.é¼`{3”íyFߺe?Ì|%–|Œ†üÈNØ´è ^2uÀs;í¹Î#òtH ü~Õ'b÷ñ£m‡…SMCTá³9ÄШWø+{’À,þ7'…Œ)ŒÄ>t¥ïÆñµ™V\åYªŒ×åðÕN3ÞI»*@èÒHo7EïCEøþ–"ý[–‡Y|ÁIº‰]¢ý'ã=‹¿pSO”~G`\W– ññlzð° OõF/å mÌï]Kµ£Á×j1dê_vˆ;ò?611L—mpâ—vr]2Œš}˜HK%Œy\[±°KZ¿í®4Wã­?Fò;YZôBî*ˆüó¯ÑîÚ#N7ÄÞ½SÑt{Ÿ%DÉó›&_@"Ù‰Ž4”.ù+´­ðÏ<0gËN€q ¦|F¿úÃG§sè>7S¢:z.ÿ®qr4Ô¬‚/ƒŸØï9&£_Âë{•xýøY¶àîðÚS‡O²ýè»’th;½Û*”éNë(¾á-¼ý<”7¨ïbOÎnø]Ï.Æ­ ?îú¿gU/¨a£gвžtŽU(þÉ7fìÉúïZz^Õ’ïiÛ wSÚpvÌܨ Ö2wð»ú$ž1S¯×°YGFÐ#akȬkãèÙô|¼~ç>›Q£ËŽéದy°&C—g޹Ìfù“I>–?'ÂÁ§úó VÊâ‹¶¨–JÅ¿·UV§þ ùø§û„ÿFÎ.ß&غM™.ŸYÎì=ªž oe8æ¨ »8Ê?­Õ}ù®à•ù]’6KŠ Nàn÷«à;Û-ÇM²-ùÓö•äQ »ë;n*‘éZ'aêχà5úªpjN"Öu£Ç­gÂ’v,^q5æ©BÁŽÓüõO:ªã4þÚ³Û'ÒMóÌ`IY8Èæåà9þ6dÎÁmzuªg0öïDš°Z”n©hÀ§þ©8î¡>]ì¯Êg-X$t”÷²_`p}<`¿Šÿb4Ë:ŽUÿD!)œn?éž5—Ø/JŽ@MŒ ­dਲ਼ÎrMÁT5Só™÷¯†ÓṘY{W¿¡túŸpx7¹€ÖK.<:#tõøîi&tiö4~qKàRë'èÞÿ×ëlµq7 £çN æ«Ç:ÃÁcé˜5õèar~¿9'š2 x_½m"ÇË4®Á‰årÔ´*s6.E×@û«WðñÎ÷qævƒƒ$!ÐöÚU ¡kV¤óÝ[“xï\µh85ð“p˜zzQ™'Ç“~:€î†Õ¼ORc<“¾9Ûì_þ= µué51¾q»íÜ]Éc[jñà ºcü LKÅÝÔIø˜\‡ùE°™…Q¹Ž¼gðx !)ܳ#÷¨Šr»-t›Ení¼£N0}„8÷뎷5ø Mj¡"ÄIS“¹@SãWåo_|…w§íyÆ WÒ?4×>8AïZ͆vÙ,ÐhšÇëÙ3n)òfÝV§ÿõ4ßëž’§Ãµ“õäÖ¤nìÏvçC/,‚ÈÙ]02y9š4G‚Ê>?:Üÿ5n™ sKQøw~XH=Ҷв“Qd¡NŸ§ú* Èúÿÿ°Eð€áV?ÈÜ®H$Ž)qwåt¸ýL§ù«à¯öJ0ŠýÁ—Š…«£‰ «|3xÜá(ÞÀ ùEøØtë[u–*£EÛ_¨ˆØ‡¨·•´Û\gZŸÅðÖo޳¤ <0&D_Æ;ª&|‚tfàmUUê¹ë<ÞçU{#±L°ë¦ÙB§Ê,LžýŒJBiw2͈ã¨<ôøfLCuïb8ÿ© Ϧ(b@’ ¼ÿœ-Ô6Û t×à¥;R%‡uêÚ‚!F¢TTQ•¥¨ ãJŽ“`ZÆc8n§f? Ô÷Ú€ý*ý1½b¬gò™yçèÜ/Ó¸å-úØ÷í™ #ôÃø¹\70ÜšÇ4 duÒ¨ÐÕ„^2ާQ‡Àæ5hö‘¡öý|Ü»c/ÝÕ5ÇÙÉã/@Õuáák­ÐúLEeÐÓ1ׄßâqøûXÿ× ’|›1uî^¾yò4>c˜ Äì›Eƒv%ÒÂIè½!btžù p 8Ì ç/ IÎã˜QÜ}ð¾¤ÈçoðÁ˜ýÄ#ì<ûèOm]Ó ui4ý|ì^gÇyÇï$fÐTG²,HùÄ¥öö=#ïÇNÒÛÌC £ô’iúÇûÆ`àíи¢¶ÅèãŸkZÔŠ¶‘€UWQåÉW(ò¿EZfn@­Þiò(Ñxj« ¹™“«àtë~ÂkpĺI¼—PÓ!v áT¬Ú‘®^4‹˜oHWzŒå†ÜÊŸ®]ŒMŽîxÓ«ó?¨³Ûûo †ų‘«mH®›6L)˜<&Àzv#[raSx"ª­=†‹ã‡cƒyÙ¼B|-– Ï¢ñ¿{õž‰$Ñ*ŸÑww¤þˆç’Nâ©: Ú[ÐÈþZL6ÏÄ)1Ùð÷ÑHô™²žVÚï¢á¢ÿûýÏãàìã—›÷°_,QÉ_¿IÂãªw“2ɧ- ±2¨gÿ6º2{0tŒÅHCÑ -8(®ÁÒ7[±¹êƒèŒ‹–0¦ö0.l¾¾±ˆÇy°›’…W\6ݬ€u¹…8jóìÍ"8³DWFÍ#ªòqªs‘ð€û œq$KöMÄwžQ°è¥M3Æõçâ™vyÛš6 büh®‘ ´Ñçg>ü}m'Ž0ûK[Ðè¶Te?Ç‹ÿjé)ÎwXðÓt¨¾û“ħ¢ueµ¸Ú~2¿p5 æ&#‘ @³“¥ü׺ڋù.>.¨üÅlH¬Óvø´› gÛîÇå;Ü@ÄL”G½ŠÄÛýȺÀrØk5‚>»ó–Îgåûš©P„fËBIUà2úí`;\\§+çGâ¢; ƒ>ÁÃg¨:ö‹çiÁ¥ j²#ú}’t ½,ÁRÙ"WºÈG¡0!ªœøì5„°=£ñͶ1Túõ ºÞ8Jð]ì6tüR¥Ö†T~dHÿ«‡~¿áz¨]\@¹Ünøà°Š$õ¯Ç²iwWÆâXí"j౉îZÆÔ:b…ùþ¸Ú„>ÜcOº#‰gûd®±I›Äd³!E§‚Ù²¥p½Rª ¹ÇfÞÿóÔ¥àüíX<:ø4DÜŒE€¶]£Â®móÿFj£1jI6ôÆÜÅçÞøzÛMôñ°¤>³çÑ!‰s`ø¤…ÄgŒûW(Ôï*ôûúËdtÜ_â[Õ#W'±úg½øçœ?ÿ»ô6És¥î-ÄÛ;^‘~±¼ó‚$ó1óé³cðZ[Tï|„¾ŸFÒ$ÅOPøÂ÷ùñ§ è‹¢‡p?7‡Ø·ˆð±ni¸«K‰z%O¡›ž?ôÎßE4¾záø%Cë&6ƒu@éI<ŒqîGg{°Ê ­À‹òÉÊ|Þ)K°>ªÂ׊ˆÓ/’A˜2: /å?ƒµ=º´.OÈŽž^ÉWöÇCåð_¨oÍj¾ŸƒÔ—CèNlHX<§Û/ÆÓ±c Úþô}\`ŠüàwèxêBt˜¢ÌÆg¸Éïtÿ´ßì˜=é  S˜ÉdÓ`Ô¦pöR>œD.^äb»qµÞ :Ñ:œ›GWÞª†óKü0d†}9~þdÝk൷1ÔÀ°Žë@¯Ÿ†/Z‰FQÄ^™HZkn ôžs_•{ˆ„‹Ä"ŸCX\o‡O®™a¾ ÏzŒäAë,`Ƕ(bì>œï7ˆ€´U¢ðþúPî6|0³û»Ÿ»–Ñšƒñ}…8ßzFeÀþ?\šý…Ö ö°Â½ûß@Î_qzÃ_ÄׄÁØ]Yð.uw|‡ø\ñ†Õå Ù~e4­„‘om‚nVO—9‘úEˆ˜·%ƒÌáSÿ¼2c8ôHÞa““O ±j(¼÷¤Ð—¦Á,•øÿõQkŠkrU½¡¼òº•Ž Ç]EÞpæxÚNËÄ\Á8¾ÈC‚¶&gàj¯‘hTŽÍG~ÀÌ*Uòô÷ì3”»LµÝ7Åðp&TL\ƒ´ì 20²~(Ïü¦‡ ز ŒÌ÷é`«žBTV'l9§5  qqÛ€þ}ÞsD Óñ[ëu4±¬"ã^ëÑA£O`í™áˆ~møxÅItˆ5£Ë¾£þbC\(Ýeg…O?9ÂÍW8Ýc8OܨO'Ën*ÿ¸ ɦ_á÷É8ØV$Çò£¿ÀÍ ©“/Ï(¸ïÓÂŒ–VXîz#œïä§FéL陥ÃyrÊG‘×/¨»~¶¶J9 å Ãïɨ§»ƒwpѱaì`êb(xœ‡E:Ævù‚#µ>ߢ!Ì®J+UñâI8?Ę[¿õf£K'àƒ~Cx3¸›•ZÇëÛð:W‘n9ê(ôý«Âƒä(TÿX7À:ê¾ìr\)ËÙÐ…×.GÁÿ س_…öÕ%¢ÐJ“ï Ð¥±v©Ó¹û/²i3ÆÀžo"tÛµi85K™'KC¯¨ª.cÍór?±U=“HCå`üvâ;à’q‘–΢kŽŠÐõS¦Ò—pô‡,lÚ8õóÏ Uk´XÕ×AP÷´“‰y'CË(jú¯›H*d?^ Q¿Æ ®iñÅC»þÅ÷{Vdè@ÿv»¢WåH¬\'öá5ZíyÁ*í¦ó°w ôˆ–ÿlª‰“5ÍYPÈYK(Ĩ_gÁí×ð°ä¥[ Ø^Ó]¨#~Ïò&ÅÓ‚ûÁç‚Áé·É[͜ڹ?%`yÐ`ë¶?¿˜ÍÝ Gð#Ó2Ðhh1Œ¼¤kÅ<ÙÙµ‚e‹`øzmº_š³ß‹íɯ£Ú¯ŸãÕöhXñä6Ñ?3«'àÛ‘©8ïmvô¬äÒ¿ÄyÝ5YºeB.q¹ Z FÖ²®LûÀ7töÌ…ÂOÓ} 7P“+è÷aÞ =>Úÿ0”ú¯Àؤ|ýõ¿$¸ê,ƒg}Ö¦͵ä³õ,zô¬žwÐPüí&Ê L7¢^c ¹ü-N¸€DÎ[ø%ñMbø_D[–=(c÷51ý-%˜t¼ÍLûÊ:V(AUÁ]æõ=LØòg:y¢kÎÓCç÷(ð½tS¦ocžÝ0–Ü´ßMþë=>ïÚC¦ˆô JwIás‰ëÄIw{ùmøE<…7õ¹È× Ñ_‰ö…¹¨»t. Fvbó>¶1YŠïšœŠ=çÓÈ–ù£°J:Ëw€à‰Óáv»7l±U…¢øÓà{RŸG»Î&ª“ÒÀN Ìæ©÷ƒëÃhÉTy˜e][{}ÐDµ£º~Ç~u¨Ÿho¢µ›:”eA™¬..ŒŒF©)‰¬âT½ÀP1ï¥&ñ‘7©¦Q{%’Äì¯%!¡GÙÖÔ\îÑ S5vÐöÓ‘,äT  z}c)é0aãƒÿ/K7‚«£•!¹µ`rÒÒ-´[<ç΀™ËfkÂ0V¬Ö†Þ“ù&‘¬Óýû+|wãŸÎ¯|ŽsµèjÓ©,ÂR ’í˜F—ñÚ^Š[@¿0K>a¨ò‘0-}8>6ÜCZWÐòÀHΑGAíeÒaxˆ¶Ü»ƒs¯ÀX×YthÝ쪅O+ž Îèìb«?¿b9Y=lâÚðj5¼_6—}Zünо!Î%CÁ”F&óØ–IT,'k[IƒÅ4ú¹„×V8Ÿ‚¬§ÓØÃuθ~¢*4ÆÔŠ^‹êWÅEüñ|¹ÿÏNÛÅŠ›Õ1'2îÎxþ½ËIÅ]öªv8{ÊVû”ù1˜(òomáqG$¹ö¶£x%¯‘Œ_ìSV†€ŒMxûÊt1^!Œ4¦ç¾”ÁÚ-±(k~4nâ‘yKñj¡fW÷bäÄpUõxͺÆÖ( áã?ŸÁKûÃü_‚Ð5‘¬Í·Be‡]`öZŠ{šˆÐw³¶áµÇê OêQzFÎ=¹~¿uÁ5·3ðz˜gfÖnHrÎŽ0!á 8©aÑÇ2 Vâ†à§÷8œìÞGΑ‡ä‰ÜAvÍò›àÏ.Q&¦¹NW{àÿ»i æ¤2¬+K¦Ec‡B$ÃýgQ¿6 ³hæ³fìO*TcàÇ"Xü>OpDUúÍÊÇÏ?OBD¼º<`&*·ÙRR‰“?Û³— ;ÐzWˆ³¥Þ-§m…0wö#\laî¹sá”÷aéÜñµ._@Ó*íh~Ù^ôùTC,f€·„¿à·Â]¸à|Nô֑ɶs•gN Fk×fv~b©[Š…N÷GòˆÑÿÛÿÌq„=[;Hä_œXbÙB>â $¿Ö¹òdÂy™½¼+k1'wòêQ24=ù6šÌæ›vjÓ^‘Tr¸c4yY@˜ryúäDÈ‹ÓsJÁ!^–ßÚŠ¿¼ÞB|°Èžsæ‡ú0)x|‘›•[ÆqÏœ(¶vÛTÒº„ÎÊ傟ý8(£5þi.[g3ÊÇ„cm….o²ÉB¿Qfh#VLLÖRχ¯q–HSº¦SVá3è~¬éÜO×¹\ùÂ:¶J%”¢pþ-{ž™ñó’u`±æ~*£kFƒÏmG‰Á®è'˜ÿ Z‡u›%Àaûh&XÞ w3\`ÇÅçxW=_ qßK sL~ æ½¢0»ð4~igLcDÌirOë<ûiÞRýäöIytV DCß!tÑÖR˜¼ ²\h͵~HG“ÕÝÂðÅP½ªÓ|„ZÛa‚µ+ÌjÆî r(œû 튅«|¯‚åŽxš%ÉYdjÎ6|¾¿c!XŸvr6<Ã[1/ápö_âª,F\’O¯'ÓøÛ±Qè<ý§Pòm DÁa²½!ž È®Ï)­Ÿ‚ ­©°A,N˜³`)Í4º5ÿ·+Ðûª5q›š%á.8wÞ3˜ýì²°l™<Þ°t§/W ³»ÛQjƒ#æJqýXªŸ…ÅtùCC† ÓPðŠaî¤KÄø‚?xº_*&œÅ™ŽN¬J|2ª,›„¿>Qø¹d8ÝÐ3˜97hÁà]mä ‡ÚQûØÒ)ß!ä¨;„ÙÜ‚I–ÄCa4¸<ËÊçî!Q XÛÃ8<ôî;1¶Ö8u³ ÏÑmÿgûGnȬ‡@Õñ¿ÄýXŠ„;ð¨ño›ÇÝ$ÏÚ´¹ßÔñtp×A×¶üg÷ßT-.l0¤SuâÓ+Kþ§³.BS„ö"lSTŽTϦIGó»hþü»1~k?Ä7ø†Â?¹ÚƒFªòÚº%€ÝSh¿}#ªG_…sµÈ/Tp•¤i¸ò–Yá\Þ§åA»/Ý'öJ¡ôyf6uR&|] {®çêS$èÑsÝôôš“¸=¦¼ý¯`{Ø=r3dLˆšƒŽÃßWËèŸóêtï}4MZÎ…¢`ÐÇWðøðcì3ó ±ò¼pÖ|HñYL'_:‰zݪ|ÆÄ`h#üø¼EQLÞ|mÕýÝ옻>é1‘ÿðL·mÔuºÎ@ýcb_WÕÔŽr\†ë¶m'9KŽcM÷q"œ±ŽÔ´¢ïJ‹.oÿ€Àà#”Ú a{ÜÁî´)J”‡¡²¥+”IŸ$5b©°>Â/˜à» ek§jî£2ñô¨xÜr£Ÿ¢yCÐÐ…žCK« X8î°˜oýdŒ‹ bú}SPÙ¨LÜ­Ù…ælT¼:=6Žƒ+O¯ã–‡ù°riØšÑIÙöɆ+}¾ ûO°¿ß’ÁóéyðŸÍßLã.ϰÑ/{PÊÅSΉѷkâ öÇN0Tn…•kWPØó—-9Û0 ÎVú¬¾aø°f'.oß™÷z0O§d؈҇R«ùÅGCPei\”K%ë53OØî³2AÒ@‡›?ð‘"ÚôOÚmP3›¥Y:|ÿU 2¥ÛùÄÀö¨È”ˆC³†3ˆùv½Ú¬eávøêºNŠRG+1ãÕlô]R&œXú |«àƒÃ#˜H¨4y¬ža/‡rámþ´Ë›÷ÏY$Gñ9 ù‘ Kn`”*XNèÍá#á`ž7?¸E‘dV}ŲÈ~¦ÐSŒçÄ„ÿÛ|ÄGh6žÁó“ñ[Y Û}e ÿ¹_ƒç.ÁÂqÏ‹j¢ou,ªg`Š«,¿éª@ƒÊLhµ¤2O—ÐÍÁ{àU|Íz|ÆÕÂùC3èƒ_÷ø_q=Z\¶ó'à Á ¾h­6/ßö"ä¾àœ%L7DŒ'×ÜAgÕîØ à xÕúFrN¯…½û®Dß¿…tzeV†ÐúN/i¾¹ .vãj1ñÔ;/dgdBH5íßU˲WÚÁüÏ ° 4Vêã… ’ÿ0açöqG_O¸hu„Äý$°ry&5M™NÛpº(7â:CB¢Ñ‚Rá°mÔÿæ¿ í'ó±’|DÊC›SÇ6ÏåÐ=3JOu£î–Ü£1ðÙYôm@vTw5B û1Xpa[£=u–ðÿú¥‡›éÖ"^Ú5‰+¾é…Dñ³h¾€ïQ?K5¾M£ƒÚPëWœ !ÆtοłqÉ2uÌòz8ô§ Di)=1‰þMg8/ô´ËÄÒÃî½ÐWßóÒõ£Ázð6z°_’íx‚W¤c¨‘Íbáõå.üëæ$5õ3æ‰F±2ÍS<.$Ž¿»v‚<ä/o|‹ÌYA‚ë×hp£¥_0°ÿÚþE÷˜Òãô ß2.’ @rx Žrû%¤#ÙÅq›ôÏ”8b½ü3,/2§wNôÉ›7`:„÷Άõ.òté#Dœ?„HÖ»°P“‰LÉh!¨.Ú›ªº…#×Ç>á…JµM­wÉsý:(šzîÒe»½d1oÛ|jÑœNö[Ç¡ãõþá/º4Å0x[Á ×™Îõdœ8ãzKÙw†Ç‘û@âÐD¶æ‘¸ fÐY(¤¸íb6®Ý¹ |òÌ`l«¨HÈÀ‡á/j·®Óã?JÐabQ¦Õ¤›¿XQ·\0{6š_Zï)Œ$O»…ƒÐÒ®_Î×§ž7Gó¨=5hã=v@ÿL?t¼vgo7|ù\ø; ®™pÐî‰D³ï|–M¦m Á% ñ1â­‘Í+WÂoÃ4ø¿ü‘.ËzÊðÑ®L’™ƒÚû±Æ,’út;±kT3ê¹ £’œœ@8wá,š¿žËÌ6%A£^²0Äo8½I‚øÖ¾ïS8ÿjV¬‡†˜ àaü‘¶áúáyøNþ:ÚôEœS¤Ì:‡?%òtlLˆüï,¡VXe—L&Æ'³Õü{] 6©¥²Ïë~á#‹VX=…’À³úPtþï=0`¿»¢>z›?þÊîÿ_ŒßÒE¡`JzÁ4;ñI=¨÷L$kUßâ×NÔ¹»þ:Ù•߀A'‹Pµ"}{%ù}¿ûÐ9šY¿Á8×Lz<gû)ñKq>¬áZ4‰O«Øhª@.N‡UäùÛJÒ´7…o~aÆ7¯Ã}÷diÒËL[ó%Œ/’£é¦üÓŒï,pÓ!Tža/Ú‰²ÃUaëÊuôÿõ5Oôî$çï'ã/À‹¿µèŒÖôßøëªp÷ncL{ø,öý§Ÿ²µTÁyÝJ'rd þëö°¹þ°` N_ó"Â+qAê,òsžî=ŠqêžP¨Ü…7Èñ”øZ}ðN‹ÃOmÖ~4Ý^uaä›?¸èÕH~Í]ž×þ–£´Ã&Ez"q)YfB.xÆ£æ¢lüRÚÓêCɇ|òr‹4e±+ g @ y4öÌ €ä…wqXàjnC¬Ë^“i’:äâT þ°ñ˜éÔ’Œb-®Ø¤Dƒ×îCµZð^z3dzIr›X˜äRŽRþ.¬½#‘É_‹Cç‘è:´:Æ×bÆÅr|—ªK;êORÁÌLöaiÖ€þ]wfˆÉÀïÚÅ‚­ÃüaÜB%ü›0ŽÛï÷cÓÿÕR4¶ ¢ŸìcÏš±qu=μ·ˆm¾¤A+7¼@ÝcèV»ƒ´Ï$G5̂վUpâ•~ýLf_¶¾ÅáŠ|{H¯¸ÉÓѬiÍ8|Y„©bï‘E|—M4Ÿàü€T+.Æ Y¢@äô#øã`ÓH¿ƒôÔqÓ³²è…8ÛÚ P‡*•AÒQ€ëšŒxùôgÙf&¨lµ£~+cüÿx® ¹¶Ù 3;×ÚªµX±í: öãUÎU8Íé;d6ÒGÚSé¯Çᮾ/È †c€oîq‡ý—´`^þ>|5j.Í“/°ƒ¿á ¶«þÝ“ûÝQ·齎<ïØÇnf½&#ý|é¥o1÷§ÊÚìáÂçeðXù#;³M‰¶ü^HÇ>”¦'ÃÉ 3p\‰Xè…ùn£òÍ:Råh@²ódxóŸ~È7ZŸ72-éRlEÐBÐõoܯÑbö&J›mÉÎ7qj’)½¶C’n‘âCç¶¡ÝæéTRmmýžÌ†E€ÍÿûÍÎʼnÛ-`©¹4p‹01 ]¿˜ááéÎÀξÆ]q/0ß± ½ÏõˆaT¶"”Þx’ˆÒÏ&ãÛ ‡àÓª§ÈFs¶ê»&ß=Œ„ÅJà'JüÔ±\TþÇ_R`…Y/)î²æã©b¡"·¾WÉ‹óÌ. úèÃe¨:½K>j>³p¸ >]Ãïï:¯™AáŸ]Ð^³&®®ÁÊ ìhuÒEþ`´pë• ðFé ™:Ùˆ]oÂÆRè\ØWï½ffÝÔ¶K”§åÁ:—d|XÁØçb¨z¶å‰bþ˜Á¸¡ï€ý¯xÓcQ÷À ·¦Æ Áyìu¼S ;ŠÐôs˜ý—L íDï,‡“E±Ty¹&·Ñ‘¤ÅÓhÇUº$ԎLjðWÞ·™ÕnSî&|Ñ_“éßÉ/pû®¬¬‘¡þ»’ðܽo¸º®€¾™™¯zÔa¼Æhè{dG'½Ò¢~Ÿ~àÃ÷Rua§Öâ‰v-qû½P·P†¿Ø.˶:öÃØ†u8ÁÓp¼vk7ݨx€E~¡–oýùMeI辂åÇCù³äð°¤I“Sm·©ô ;Â/ß(ä­×ÓÒðDg¾DÉqÑÀwñÿÛMœŸÊ¹€rÕ1ÔTsœo6ãjßÑk»}qº¦&?–œ ÷ÞÃs;{!;ÿ ùyÌ’G³£8øC=ý Þ?¹Øò÷—£8·0vØ7‘&ÙÃð_ϱëý5üë"/zíõ)°òý ½hØúCð2) ËŸºò'ï{ a–¤ƒÜ–‰ÔÆ^›ÿ:ëÆ3ïbÿá÷ÏÆÒiuŠôÙg(Ÿ¯FG ¯Æ—SúÀwÐt>´k.;¡‡h^CvÿüLâµi:Þ4‚È oÀ¾¼EðVÇ¢öäŸ |UEΫÊ}Ÿ©Ò’à©ô騨ç;`¿ô¥O¬Uõ0J®+è?ªÆ»‹èaçEtöføLœqv‹ ?ª6–r‹?_oãøú…·ÈÓÇæ`&‘(ð97š4ÛÆãå¦TtÙŒ.‹€õ­cqTÝA Ú9—žªDZÿýã~`èüDÒþå·ý‡è™$MjüñÙŠ zäYÛì— Ášpω9à|ò!Ó=´_2ÄjËÑ-â-ÅÉ—P}p̱¹,¨;ð –_±æ)ŽJÜçÇk\:Ë’|ŽÅEìQÏYnô¶4ô2šé ¿eà¯}l®Ç 6JоûCê¿IÓµ1Gî,ΰSk2ØÞz Ø¢«†8ê&zº?Á7·Þ 5þVBwÓ"h FuˆpÄI” ¹Ž£/d¡—«šÝ<ƒÃLât¿>bpáÄP|c) ]?¸Jã½9Q8€b Gp]‰T|Tq›GKðÿþަŒ¦DdàÝÄLýûI<á~§,Žæþ«Ö@èÁ[ì£{7î¾0‘?¢>tܬ?èÒs·Ù§@gÔ`.iüßÎ8lÉ3Ò«³‚[ä-c•;òðþàLT8úœÖOá.½ûx™Íf ñ™A›J×q÷Œ³0RWŒÄ¶|DÓ­û|t…Ü»gÃït‰Ím*DØLƒ—mÇ/£åÀv¥$U˜}ÏîÈájBÔ¬ñÍÚÇÄ&;Œ¾æÁ°Ø•pݶ$<æÀ‹„öø¬ ôÔ¹o ž ¶ãÿà]ý8:Xƒ;( bá'a”S/?”Å`K q™71¾ñ üœ’¯v¾ƒªmó±áF­Ý@§ùúða£çóì1þüʹ™°2Ö èt‘›=R¬jK¦®Ǿ@‰Ÿ:Ù³¬šå«Æ rgáÚ‚çusNñ«ûñ¼­(›mBKÛã`AR].r“œúx‚zÕèÞCÑØò9BÍBáX·MÊ£I‹þ§ÿzç©9„_eÛòYªŠWЇ‚D^Ä4 ö€Ìá(·\‘ߙԞ·‚­Y1¦»á õŒ£A‹ƒW1Zý’Üš·IÆÛáš3 ,õ›2•ùÛM‡•&‡± ÿä¿÷%¤ç]ƒ!é(ç¹ Öμ–Â(ºN‘㬵è4² Û¨òñ碠Ûœ¾¹–` vÿq ®€©í0½..lÄ0Z¶¬f€cnÖãÞ°1ØÄg|‡óÏc޹ vn³âÚ}ÕøUS­3wÀyÏÛ6Þoùº{…ûxyÁýÚÿ³«þýÿ4žK»æáoù¥l±{©½'4¡I;“ø—Šû仑9aô/±ÁK;mxÅA=¤z…:q±/)ãÓ‡0ûîW|'#…[œé9éy˜eEýw÷¿;ã¨Òæ#`nÇ;sRˆ}3p«‹8æ"™øæžüpŒð…z kã‰9ž!¬Gšï2̳xê܃P`hÆ{´Îó²Ä¯­ÝB./îA9ùlš}-jüŸbÑ@SySNr›Mé‘ë†øZñbáTþz‹v~éÛ‡…»ÏñÕŸäèªW— îëçÿ²#)Þ¹Û†)/ô¹í‹BÜ¡.……ª‘Üz°?kùIU:aûUúúÀe²fH»ÔV §âU餓rüûuIjàíÀ¿~;#ÅñÁ®kñÆø.«À#à‡¾ ;ÏÅÆùˆC$dÙç›o òø5z²cŠ-ÉÐec„ªcøæQ’œ÷‡ôÁã7Iz‹<Îø3æÄ¿‡û×<θ&ŽN»âHå*vð ÜO·ÅT@ÜPzfL4¶ÏÕ»RúòØ|X,ɦÉ&‘T5-øGÐê›8dpX\…¦{ÿ;ÿ5÷M8ýù'&¯Îae×›R!\ý±†y †­'Im‡8}æWâ–ëÂ]Ê …pËP”7¸½g¥y¯ñ(I‡Õž8Ôã¸; 'ë=ù­™çÿÓ‹&{? #¶¾;HôzÖz*­$4ñ£ç×r0ñÇUg,hêK~q÷>²³mò¥TÃ~Mþ2.~A5¡Œ°ð’wÕ§„{þÞ&.+µTŸ¡Ôø\ø% 7¾Õƒ‘¾2_êúãÌu¸¤£?¸ØÖo¹‹ÞgÅøŸ©ztª@ÒK–ñ© ù×¾ •öÀÅ»/„ÿgôýƒdÛGÖp}®Qé"Ö/›1ë”öu bÊL1UÚŽ{âWóa¼·:p” Ÿ6©ÑͲGydÿMAãV1.²;žµ,ý áI‡¡mš­“JÅi^‘ÿ­ ’;î±ß^Ö(¬Ï#·Öü雸Êë½7ªõûFu ýÙãøµZ™?úîÀ[^'áädW,þlJÚö8Ñü]Õ Ò]ˆ {¥ÿÓ† Ù½ØЊóî]‡Ú‰ DwÜÆlålð“x–À&¨;ñ¦þ—ð#d+.( 33Åét}Qþ¸¨—4øŸþ§µÅ*öÿ쪸…#®Næ¿ò¿@–Æ|ª²¤ŠˆúÁÖ-w‚Q« ê¼Æ¢ÈëŒdUд% ¤½úˆçú•˜~6ãZ¢øÝWUa75\Ï;Ö)ƒ£¨ Ø‚íªt{íÿîÿ÷½Þ³KL0í˜&}zÀ’m‰ÁŒ²uÌÙS‡|ÐV£ÓìÌX—ôaèé]IÏm!ù—+Y˜NFvï!ŠP _Á§¦©<ØÐì¡aúš¡xöôeæýe5q2™Áî±À‹µfdHÀÐÿÎÎ.‰ÆÛF¢ýñ“¥ýƒ”ÆÑb3iC«8¹¶á›)? ®,B\„K+g`PJçÞ¾€Ô+‰}HøŠ&¯÷Á0->òÆ4~ùb×”“¦²ö@æþ–Åaìò,W)ŇHÎ’­tí Ý6󔪘ÿTá.õÿéÿÈï¹C2笄?®‚Žš4'®Æþfšsƒ~¹Ú•êIŒ¤ÿLÀ©V­ ñU˜Ú¤ÄbW(‘Ñ«î±ãV8ÒëQ²ÉÒ|¯ù8ð= ­Oçã«×¯p±h3[]@Äå ç?ËÂ*Ùr´®L„Ú eÞxýù#§ÂÉϵ4–RÈ`ˆ‚Ñ¡ú̽kV·V€]øq(ίÄ=!áG¯’s»Ž±’Ö*<¸½–Ü|‘öN±ÀÌì4dÄC¼ön”ÐcG%L_Ø( Y%t²"#&öl’ðnœ‚BÍþùÿGæ "y>V¨íÃy¡O„¡"Sm6ƒqH=¨ù´`â8˜÷ áá‘sDYº™$®•â.Ë-˜TT&teFÀ^<Èâv³”Æé`Ý8À““¼ iÉXXo#ÞîË‚:¥ì·ývÐlö #ŸXí炥ߧÃf‰5`sè8~)v‡Ÿn2DR{"%ƒ$¹ +“sñí×å‚ÿt©¥æà´ÏàÞ@Ì«d¡Á‚SPEgáüœ`öÇA^†Ç²Ç"á“ißX|¶E%bã®î9cPsÛ#xÝ+}˘¡èX60/lØúÏÿÆ[Áõi=™[”g—³ÿúŠgúSI¥¡$z¡ˆœ²â-Ýþ`Uß«ÎiÞ¸5 ℸ‚@çùV–;L…lýmÅ/_6†iwV óÆ§âø­²c:Ùn¤Á_ټŜwB2Ûä%ο¨ Þ²R¸þc wŸ-Óº¥ùÄAÒTô«;›?~ .n7à™û¸YísÖß9‡GôP@ù“xêMµ}‡›:Á×h,_.¶‰Èܺ,6ŽØ/à®:b;g7SìѸg†Ý2ÝÇKªðAÖ•0?á-Yár™¼<šü?ý÷²J(,Ψùj"]l@«Å`G“%ª”â£J.2í¥Ð^~üìàÓÁrlZ®Eg,ÝÿÆ‚‡Ì¾ª2˜oнßþøê×Íÿß³ìSåŒînR´£€)ÿÃè"ß6Œ.Õ`óß_c–ï]Ȥ`p±éïMZþ™#O pª&ýQò‰ƒ.M<0qãÑë@»,VþpÀèáOy/6:‚‹k¡K+.û‡q·5óËO¬ƒÚÛ'éø—¾¼Ä[Œ¾Úq‡¥Á¹&žO|[Þú½ÃßkdyâÅ“ÿü¯'{›}ý£òMŽ$Êlm™ƒ6öÎÜýÝRܰ/UËÐÛ~)üñ²N µ3‘Üøúø‰Ý¸ªBmÊó@ëŠ=Jdz½xD hŒc¬w½G"f¤ ´‚S\;Ö”À*±­”µÅóW¦Âhÿ(Øÿˆ¼ yÆŽ…}&MP§*:K1Òý Y8mÏ»›Š£Þ·‚¬q(ÎÍDËöf’Dû˨bøý–zÿ±;ìénøÔg&«+Ùi¯ ¤|Ws¾1ϵ‰iÁ¶%ÏØVƒGèš%B+¶ÝDwñ»0MD›¨+*¤`Hq>W™ð'f‰öAT¸=ýþæ*ˆŒ´¢bù.¯Û”JÛÚà˜×öDëª<¯R§±Ç°bŠÄo Äš5Êhß³ëˆt¤$ßs#GΉ!›Õ•¨êÓƒ¬>b𯇠‹S˜ù·`¬N£¸¤Sš>x”z þ¬¿öÎ7Y§ñ:KÚî㔥áIÌ ¼,j@ºøxÜÞ}L†Eà\£»Ðí¥Ä;Öjs·ú×®0a÷X8ÞðÊ5póáÁ­£±5!¥Ašfüz&êòôS’ó»oŽ$Hô©Y8fÝ[VT:NâÃûð‚Ûdúˆ\¨«ÈÞ":åð.-á_þŸ2¦²7AÜÄ‘tK]#,w ‡Ï–exø½ºé'‹Ÿé+°›hÃGõ{Áömj´çQY½} _9³Uj¿3ç‰|LÛ^lyX……·pêÄÏ03Lk¬uaäå1ø|qN_3Šo¾Ó ÓŠ²Xó—¡ü¢íþkW€K°‰´¿–‘Q6Tïçd农UyIÌá®-¢!P¡3o6 ÌI}§Mµ ;Ømrý›:ϼ†,WÄqC¨ðö{ìÚ…ïÖ\Óx`ÓÆ¢WÊŸ@ñMxù÷êÔϤK›R…µ‰ž¼¥Fú_þ»'W€Z[m‰|JÔ©\‡mŠæðqºÕ—=ƒ’»<]-à†#Ñøê±ÿ4šëªõÎ t<akDÆ^š —ò‡ƒI­) 8{BëÙ~S[º+Úž~”ˆÍÑx:QÕ°eVNÛNÔçÏÇ{èw}%{4†Z€[MÊöSüë Ú×ÀÏ—×`ýUlŒi‚#ó†pÍ”låÏáÈ%TRuæïe—ÃÔO“¡†Äc?Y£»ÿlÙÁÖú³xÓi@¯(àɱw\Çnð¢Ã.Ž™½Ù¤eÖsèt ïn¨Ãy£þù_ @¢N•ãÍ¿ çêyaÌ;Ûçò+cô µñ4ÎnûHÊŸå°S¦ÊÈOã#.¼ƒ÷·ð&ÅFvØD Ú?8Òìo“Æ+o­gU7ÇûÆCVX¯psp9æŒ'øØLš-½}–šï`ÿé3)ÜÍ_·c"î4Lé”°Ù½FlãUæqìÊâAtçxå*–ót¢×%BCÑo‰uá ±éTgsK]u¾n•æQjQôÍt#þ¾EÏÿÔe#ÏçŸr áñ’ö:sÛþb2ÂÁIh^÷…}÷É´vÅCøÛÿáŸ[XÓ“¹è8÷3d &þÉ''u¯âƒœôþüßxôsÜÿ×k¶ôz…%/¢·:²Ó‹×pñùgÁØw ø‹<Ãð{x¼W ^Ús ¢×2Ï!ÝЫʳ.Ë€ðùyTöÂÇuçɺ‹`Š—~¼âÁÍvÉÂð¬Iüeqä‡àÕ”tÌ™E_+C‡©Lh9·=J4Èj;̯Ö仯dU70þÊ/rª½¬°vŠÛ8¹âtî>ˆ³Âû±öåd°7©ÛàaÜA«2Š*†*мQfPx^š:#ÕKt1D÷É?û+h*Û«ĝݓ¡ABFþ¨‡SàýíÇÍ»›y¿À%o X.x:W‚kf ͳøàûœŠàé1‰g؉µûáõ¯^\zêTå§a¢ÛUðR+cz>Q{[¯7/YºRþ Ïe…Be® =`üN•:±¡×‡â¯ ‚Ç¿Ïá½VKܽè36ºÏä–¡TÈYÜÖTƒ1ZˆwÎï AƵÒÎ3ŒBÉEƒiV† ÑYJ\WÇá«`XÕꉪÆ?°Q! Ž^»€ªÛ{˜©ëhzuO(ù}c)‰kü÷ÿoFoðñ¸ã,;t2ÍK®d³hÑ%Òõx×9Ëcli¶Ïh”ñ!âSÖƒ¸ð.Û;‡ÐëzüDÁ>á³æIüûoyZ=ö;®qþ É¡«ÑìÉ R3Ë BÆÐ]åÇ`rè\{ô,Ìqìf+~†Ó qœþ`2nGYrf1½ò¶;öÝÃÌàt>Já°65¥ £Ã@\ï%h&ØŒ´FcwO^ŸRÆZ¿9Ð#B+°üªF?yG`s\%ª©ÊÐq£]¨ä„Bt6N‚¸ŸPi‹XF– žôfaþ¯«ðFº Dç£Lé—ï]ÿÏþ÷‹< ²U%kPT±€8^Me{BòžaQÄ÷e=\{õO%çq[µ Ø[×?…ó!=éñw 0ÑÎåð|²Ãó·à¢×¦\mÓ üòy"—_Õ ºcFÐÓ63è©[ýØmýûܱûîYœºÀ„KÖa¬ýêSvî£ñeü|ñ8jvV޼TªL¢Ûᮜ!ß[§Æ‹ï‡ÂÛƒ é…Tqºc£%×_Eö´ây¿wôqöäêU¢'³o˜§A¿ÕCÈY¯I³ztqrÊ$¶¬W”Ê<ØÏ—ÿ)5þû_ü|:„6'³Ž¨Xbœ×„ÉŠè×:ì©×´„Ç£ìóI‹»ÃÄúÉ ØÖô“ '®dwÌzeöËn(ÒY} pÎÖˆælÍÚy?f­ÃÔ $VB·ç/8?¯C¸ë´'iþ<Þ©2[SiðIù€·§+±Æq{x†ü1XóPƒ/©lÁ¤4 Ü=$]™Fû®Â‡=…dýƒçN’kgóÑû²iïÎWp@¹–Ü]ï.Èx‚ÕYX:=†™Ûd¢ˆGÔ)MYƒ…Ï,éÂYaxu"|×räe{> ¾Âo|“¾äþ¹S!ÅžÅ!¼ ¿ 3wÚÓ/Ç#¡3ºO›‰‘º¥r|CM.sýòSà´Äà®w½)…âk\øö#Æüf˜:µüц2®âxÀw4‘/‚¦ ]¾ö»9Íö û¿–“Oï}ð©”oë”ÁJñ0ørà>¾–.Œ¿á A·/àå÷öhòá7³]¬AÜWSþ~Z'Ì9×:u?A¬)–õ¡ß½gLüO*/™=œ‹Ô…–ÜÒÿô©a{ô) ð9ýn¹<ä?Á‰ë/sßw¥xÿ§%¯Wæë×Q8ù¦‚hß2ÅññbX<Úç?þÿØZCùbs¡ÍjyLWæ“Ï J7—ÒJ³ ²#7‡V#\eÆFÒTºÊ1lõ:ª}*ï0ÏÀS:jP+•‡#¶/¢}š]P¾Õ„{a8=O ïd•á\_ê8÷'ºúhððþ$~w…/±¬æùÉè ¦Tßk ôœ†öz¶´ç§ ­w²æ›Þâ³SŽ<èkšNùŸæ4vqOXÎùIz¶oÀÏ¿'¡Éˆ—`~ G©£wJQ¥/\0çÊLý…2OáHæIlÀçïp$›#¬‡àŠ@ ¾¨XšÿOÿüõØvÒ¿TÏ)þ€þÕ›Qýn<ý{pÝ“f|Uh|ÝâNQšhÅAwùrZu© ›GhŸ…ŒU[ªæêd\Ò¢ÿtŸAZ^ŒÈ=7î3Å]6ÿâ?çFƒpȶ*œiã†Gu]JúæÀ›’\›0 žë˜Ë~Á¹õ\Dï[ñd ¾«/N-}ÌN•’7yÇØñ«hÄaê´–/?Æ]hD”2 5`´¬7uL»eE+¡é¤7o|SG2¤9,µ£{ 7ngžFSÓ㪴w˲ñ™)n_fÆO´¬ ¶A'ÐÑG“N´»Ykä Îáˆðõ !Ü /“oáØ6g´ŒVÂÿMäÚ þøÚ"ñî¬ßˆÎëŠÀ3Y›/˜³§Ò4yÒaŒú¼Öu6 òí«ÿٿ̳в¢PgÀE¾0i£AÆæ<ø2ê)Ôed «åìØÿlûú Gµw0)‹Psq-î,Ãy:‹ñ‘åy!”yÚ§C=÷@×Û2Ñ"¼Ü? O"R1I=VÇAÜÚ³0wq6îßô–Íç«°ëë0vŸ[ðI} Öý°ÅOÖchoìlö±}(äνQç’ðÃ^?€À©F¼íÄ~tÔ%ðíx8l®ÉÁ…&Ù¢ƒùØ~’´8ŽÍŽÉX¿m'\­ÈBqÎGªÃðóîüwU>ëÔÝ®s»!kåa †ÙÇÿ’Z¾à~Ð>ˆ¿žWã›VE„Q ­Î‹ÿáÅ6dßÓÈ'ÁØ1ֈй­#k!ô¶Úç*ý÷ûŸ†ôŠû.?“é(ŸQ¼ëÐ &/éJµ®Ö±üq :î,:_O÷ÏÞ‡]ƒòyôÏp[ DÏ¼ëæŽçõº™ª·©ÉPçB^.IoHEðÞóñܯ許sðá C(Öˆ÷ŽìÇ’ôPl=S¬‡gÞÙ|ܾ~2ÎVüF : ¨»VâUGóÔäeØt5•£¨b¼ ={ä0ëî :?Xãpm›Ku VòõÛ”áü”|nËØä9‘êÞÜ’а6}†oðåÁ÷çȸÃiðÌì;Ü|' Æœ +v7’/Þt±Ä¾xÙ¼.©Ç“cbQ³h*ýýp8¾~Š2yÛæÁÏImÓaœnr‹4ímƒN½!ðl¨1û$‘š8 K„'÷ðpB;tO™B+Ÿ7ãY®•9‹Oô …oÆÝðhu/x·£7ü}É=é.6È}\©¯bÇ;´@>/±‚‚ƒipùïþ'µrÎ㑺rHß¾ª)f±ÿú‡ÇÛs¶¢ |[ÑîG¹¹?|¹Ûõêo4CŠ #yò¾$>eÈJøºßùòjq\ÜÍ¥¿›äÏÁ’Wb|ÙédpD!¨'E i= È·¤óm¥,Á·²·`·”Ãúh~O‚q“ôDÛFxµÈ¼îjÓHÞ½Ô‚nJø–9êüèøfôhô£d¢@; ×—3˜à+„.‹lI W†ªcycëëÁÿÓ¿úS&H»ÖKfoão­–ЄY'ñAr8 ]r -ÄܺåèmIÔîÕÇG×íiyœ4¶S“ν ÂÇôªðd‰$¶eÎuœP·ŸU,8LZ?HózAtëæâLÿm8a çÏåÿl‡"‡‘tùðLzéB¬Wà‘CÏ ”såøƒ3£l·¯8¾>ÞõÍFtKý ,5˜—¼´ãÎ×hׯ§pÖPÇUn€'¬·*¨E…¸‰…?!5ÜñcØ$‰á53ø¦'‘übÈ-DƒÜ—ûŠlv3á›Å{øôéGð¶|䯠ö;¸©R“o9X°u+®:5›‡wr—ÁtÛ3ضq )Yìõ¬ü$×ó‰å™³l©eþ|„ßuDï=5m5&›gÃøgôî}†'9ßIoÜKrì» •˜#Ô©&ÑË0Zò ü9F½_ [‰Ð®Â“Ì@zk9ñoíÇL÷z<¡ªÄ»¿ì ™J\âê; ‰2¼–HÒšjüSŠ>Ù@‰AÃW‰*ôIMÔ/¯g!k”hî ÎÌØ‹¼™zµªËÕiŠÊlch¸sR”ñ¦šÚ¿ü'+ÿ:ôDø õÏ,÷€œ:؇¯=—2ÙŠ<ÈXY„޶«HéIi2Y¾<Ô‡ ï›r'Éjµç0jýS2Þo± fÝHVcuÚŒå©Ïuø¶ÏìÛ¬Y½¶,ÔWPÍ—ÃhÉ„àb{`ìšç䧯^± D¥Ê…ÛŠå½ÖGô”96Ä⼌h"^H—´×ƒÄ‚z2Äc™@Ïö¨0ýÞryá S à…“g±ñª|y¢õîÕ蓞Nå'©Âå{—»úyîÊv"0V¯ äK"!I'SÐ{9„Ÿšyf8þïüç@L%î+{y“¥™TåQ>KVƒÿøÂ]6?âQcç ïx´3ÏÉL{¿'õ•¶à¾ÓêIg`/¬ÓáÍYkà·|8¼²ª@5 ú(K†î.Ç7ו©ÛÌk‚k{^‚½ê}Ê^¥ÿÍß#Œç£3-øÛozÔLf2[e%Iï˜åbõ†nRʼks­|jA’U€v¶[ ªO‡ jÞx¿¿ ÌCÍÀªþ6nÛñ‡yˆcÙêÓ¨>: L3L°uøH0ZEÍCöám“©¤mg*•Û³“zßA‹£ uâÁ:ñ‹d¸ØDZ4f,¹œ¸ÿßù§ïQ|²êvT«»AÏkð?¬ Ûâ«LÈ7’ô­M.ˆ´þ„”·94tÆu˜`$rL.^ú¬O'Gæ ]g =çÊY 醬JDù=Ô.°.^'£N•`ëóbæ–„ »b;)ÜY æÏrÁ~ëd®½=dî• är­¨»`:ÄÞSÿY|#6ÀÑ¡‡ø½Ìo|ç’Wã ÀØÑ¡næ»pæ±±‡MxBfh†ñ »tÞÂtlÞzoOÚoW½ nG§Ú×ЧþÜdTúk3¼w ã{Œè)…QtPY4&'àü7_ÿñßµ;?€AÎvØ8o1߀3Eš8wO‹q¥A®nèò݃õ¹ñ_M0ûü}¨OÚ_'Õ ï‹>8€#P2r ÎÉP†q2WPudLÛA^®” ƒâ÷ “O¦NžÃYɬó(s®Ž4þM¢ßºÒµ3÷Âdðab™P|¥àkÒFÜð}!íîÇ™›„ªÍá|îÆÝ¬yÇ)˜‘¸6Íü ¶)òÂwϧp=”;Pî0ë|ƒÖµê<Øñ~ KÇwT@«í Ž(DÆ|R1Û‹u k$9SÄßåìyا÷¸:R”‚³]þÏþÇv{áÐÐåÂ5§øš­Ëè“Rú³õÆÆL€x1úØru»‚£š³a—Ób~È,Ê/%‹wËÒ‡Fá>¡A·/«jä=„ "Ùë&qéž N§î!Þ¼ü\ôd;¡Äú—‘ö.h²w€ã 7ØçWERŠc‰¼{¨½X‚¿ô@ß-Spzw;KÀš‰dhèI|vV×çÃûÉ2 þ t|;UÐÝÄ ¶ï“àEÖ% 3:+^>%º ̰0½;ÌÃw¨ã+Æ­þ ƒã®²Ÿ«K‰Hˆ³‹LþìËEåž§0¹<ß¾ɾÝ>¢Ý¢®1¬¢¼òÿíøyœÍ(‡Ï¿5©•ìòô³9Mñð»—e!Ùi"Î<¢Í+¾©³eOe©a_yrM†?Ÿ“*ëàoq)ιkNƒÝåá²Ù9œ-ú¢ÊÜÐËãŒ`Ê,I˜çt^­R§åÇAYbF]ÿžo¶ºOîÍ>Ž_×îeO7Ü­žN4™‘·ß¸óü±‘¨&3§_PÅ™ïÃ^5o¸®HôiäÉGøbìu˜£Š&kí‰q¢(¦?©¯–·HB©ÊðÊŠEr4oÂ[LlH#‹— ÆZO€µîÀÌyG©%žèGœñûýÙ÷Çb’Ç4¾Ç$‚Ölü‹ú’Þ|ý$øuvÎùq¿Mä7•tøöYKù¶¹¦ôbÚ ¸;ó L›¨EíNçû\AH‹-ïxÆ×þP¥›–Ê r­x¶ôì³Ð§ÏZÔèßÕðáíJ®Ò¯O”1à9ÏÈ.2ž|¶ŒÖºB8½à:5Lóãæ—FáñŠ ôyK?œž.‹!­út‰£Ÿ^Ý„[?8àÃû|ë©q´íÕqŒ¾;˜?=A+ÞCáƒ8*1v¸»«ÓÔyq̪Kž'æ­¥ÌWîtä×Õáë-bì†Áˆùomw™¬3€ióXSÔ}¸j#ËÝ]Ê ü±?z‚áöàã³éú™Xas »Nçq»âyB£J…î…-w–ÐY³öÐy°§:¼#†ÐÊ5É8Ö½”)—õ¡¶â>”늄‹:Üü”!º€¿lù‚î3,¨í@LOyùë¦v`Õ!-ª¢Y×ÝáÅê-ð«Î†=š€ߨ@ä:%œ½p/¦uÌ…·‘·™ú"3:é¡ÉÖ´T7W}{FÆ6E3»Iؾ„wXm½Õ¬]Aqs Ξø &Í;cüĩƓ¿ìÃY3~/Á‘¤ÿýþÕÝæCRǰ•j̯Co½o噄f·[à2+K{-©Ì]mºµÄ@˜ù{,Ò=Œ#tyÜñbHÛ Nwl¡ó÷b¿ŽíµXÈ«½Ô¸Q–!S¾Œ!C›QÑd{þ0wÂÖ^]Ëveî{o3w5*Ðu‡SBó¸}8ëÏ*ὋµXwÖ ïùÙóÔõáô|0V.MŠYÑ¿§¯`ËÒáгWžzŽ~ƒÉÞtçÛϸàüaö¨f8ºèæakr *lº ~Æ÷¡hºðlò{èW‘ ²«Q¸h$•u|Ä,4Ã]Ü\ÿ×ÿòW!•ßQÄ“û7cco ˜|ÆiúAAÕ)Xï0ž9ÌÅtÇ^ö¼LЬëž)¿•]¿<‰BÛ§ò¸w±MÒ–¢/c*pú„غ-õ\bÊ3P<"G/ÌJeËV*À‹2kj~¯_çÚ=æÜ˜Ž`ÌÏx¼÷WÞ(@ø!~»«šOf}c¯@ñèCðd)¤aâ à|gÝõ² [ö'Ò˜ q§…Ú»°x”'¿žsWF‘®PI4àÂ;ŽB£ÇhnˆÃ{yyör2}5_yt1é ©º=RÝŒ¹âÓþÕ¿¨è›ä@ò Úóm,^· _5ÊñÆ[·p{` Þ߫ĥgŽ¥gS÷aÛî&¢.—Ä †›LËô$^eçëД5«pÒz \ò¾… wº%ÜN‡®ûØ@NO„»"ã1ˆû­ü\:Ó üÎ{™!ùäÔRgž±*Úe–{ ›®@õ=|1Öhì¿jÍ6ÝãKÖ–ÐæÕŸYé M8ñ“¼áÈíæFðùa­øt™D¡I"‘\Ew§â1YoQvÅOuÀ¿R×ɱ|T¾<¹rþŽ^u—ezÿ‚©·ãHɃtZþ¿û_¥EP%iLÖ}æ¸Ma¿Ø±<Í­Á£!ðQW+tàÝ=?0F°}'î\µn#žÄÈͥ0úW Nxô ^iŠÓÖ‹ûp\ìPúüü*7Ä”Œ69@N/ü Û þ’¨ãíì·ýwØ´G æ.šºGðý©ap9äîºú]°ñlØÈw`êÃ,©¼˜ùJdŽ&m_7×ã¶ã±Û³˜|ncµkqá¤TìoJES[30H[+œ5¸…HžI¹ûì‘æTÖ}v!t$m"Ó^ â’¹86|\O‘äv²A{ULü?üûªÇ÷]¸>€ÝGÑ_l¶¼¯‡®X1®Ð £âëR¾Øu¢ئÍ7È&ÅCpô–ͦ‚-O'aª³ ǶUð«k6­ŸE½Î¶2»›ílú–9øÊÃÑuçú×øí@{ƒ2ÌzÌNT³ìcÝŠ¤ìM-¬ü‹3Qbì*–ü°~•YÐy’f¼¤'Ëd½y’|‰‘8}·^ƒ7ö‡iK¡½ª”‡ìÇ‘­»ax×gܧÓL^´Aï•4S£‹%;^‹Ñ³ž ò½×¡•÷eü§@;[„+Kš`Zj™¿hÿ¿ý_¼¬+ B©B8Ÿ@wY0°8Ü…§ã|Pyn „‹íÀ‘%’Q ö6üe”¯z:†m"1y°'džµKvñ kk$ÿõù¦'va^‚#_¿s Ñ|•š6ì/¡Þ'¾ÃóR/ZéµFPž-Á.ô‡¡lÙ K]‚/€ÈˆÍ¸)çظF´ÃÊಿžØ…[ç+ÁûSªtÁým<õz±vv~‡»,³¯µºDMK›_¿æÉg­,À.ËÈïƒx8<:7´Ãã9³éY=MRpàY9ZŸšøoÿ/›:’m)߈Ñ_Ã+š€éOŸÃ‰â}`v©óü~ËŽk{Ã;§áÖ}Wțī˜›ëO õ%Ú‰Äqk:ø¯¼OFÌq¥?NLâaéÃøæÙB¼±6V¶ŠòYƒUÖ¿ðàV#¤ÝZ|ŠúHîŸÿ~[UÃIç9š3{ñ}3ìqP52ûâùöê$œÙo‡ –YÇ1ñy)¤=|H^¨¬Á+Ûz]K·SíztÆÄ_äËÅ_cäù½döªcF.›KhÞznÛÒÿ¯}Ы‹} ¡c€So˜°6(TÀh\±?ºô éhl~ËÖIb›Š(>mu§!ŠniX!°Ìò";p²Õl»6 ¶ˆjÑ‚a6tö°Ö&VûÝBU~n[h¹õã¬å¨/;˜Z½!`Wàʯ‡‹²“Á>8Mu#ÄïùðÏÿ'ŒÕ@͹¦NKª¬†…âØmdJÄ+nHÅ ~™`ºó<”çóŽ÷1Ç*tÆ»™|JœK{D>%iqåÔñšAís¼,ïÈåÆåIr$¸â«_§BïºìÅe·‘ n\÷3ØÇÃáJСÊíà…ÅJ<ÔŽ1Ô;Ð>z2ûÌáͰ9tâÍ},àõD<+)GÿvLAÇ[¸}Ž|¥ûGÃ:I¨Ø’ t×1HxoGŽÅôAÃåyàY«Cá$îüƒ.;Òà«Æ 2Íl;®}‘ oäˆM¨!€ÿßö¿›œÃk¢pù‘q0Ÿ­æñ­*Ôia*kÑßµxš¡˜²h·—ODÝÔKøCüú\{=){P;þ>øïÛN}6-¤7‡âÍøQôrïvêµ¼FjäÃé·“P)§Tš‡ Âë.¨¾”ŽÆùK ·êå:Ïôàí*²‡o¥YÞ^tþÑt\J´šÕ¹Ó#¬tšËænNfeÏa”×5ü’^A´Mèî' 䩤<í2Q¥ò–¡/Àœ§·áÄÞt¾nc03]ŽXHHปbÂ5?›N­y-nXÞÓö¢Ñuç¿õk×±hãVÂ;dHÅÍ6»›WÍÆðÁÃu!ü‡«Þ¯A?- ú£aù1ì¸-AOÊ"Y- ÀÀ÷Ùüy2Dõ¦Ð¥H€l3IpÌ`­†á¨Ó÷HOS#¼1ÞFº<”Ñ×ncªìª˜‹³#Ï8¹0¬Wœƒ[àæévd“á9,ΖL÷ã.3á¶mSÙ¸1“YÉ€O³¦¢4€ csLÚ¦Œ¾ÛˆäÛ–7ã³µ.s÷ÌCúþNÚWˆgÐuT"¾h; 2iÞÌKAÔ5>ô;>ó"ä–FýÛÿr÷,`÷”Yì„âpZ9À­w¸9Só¼˜ã¾Þ¥kg$ \z`“n©š ’u¾dóÚ«˜•¬‹­Y¹¡~ Ú"ÉÆg Ç b™, _`Bž^À¿£°û…‡q“µ {ê¥À×ÇáÆç/ x]‰Àv§ëlKô¡÷ÇN0N˜ÃŽÞq;®Ö6–½úR/zªr­ …CCNê9Aѽ6¡Þž#lÚµ¡0¡Úer%âÜûpxÔJhrª€ŽÔAܹ¸ j­\`{Ø$0š¦…zZ|{ñyLÍ5¡b;ÃÈ6嬨Oo<þÿ£‰ƒ{ª|í¾¢ b£‹—Ž­,‡;èe ”‘þ2Ò¹æüÆ·xwm"dy\aÊÃæ3ºÜhI$\žîÆ7&n€_ËVÁê—àLÝ9š2½lœq«Ñ^aç›Ç8ü©Å@üÙ1;Ó¸åó!\¿3\ܨ™?uýúÆKhp¢ˆÌj˜Ž:_Ñ ]ðcù8û, ¤×Ÿâ ZvÑ›f<ûŒ<ÜË7çùb¾Ë§cÊ#©c{Q]ÞËõxãî£*WBÿüLƒƒü©ÐåS È^n¤íò“3kƒDø‰Ïÿ»ÿ˜•ÅÚ£™kâ&>â†<¯u£†=`>E»û•ß­›ñ®ìæ áíænx·tä±Áð#Dt¬Í`ÃçTعøšÐ ÁÕ5¥ø®CP}À›ˆ9%©Mx«24Z£PÇ×o€*ðhÓnòù¬å¯•x_y?š5€% Â3€–<¥«v)óÏmYäs «OÄáÀül¯Ô<Ò!þSבW4(ËÂa+Öcx¸ü}?Ž”ˆÒm_Þ“›W°c!2ó1¦PþXïPÂÓM«Ða°%O˜pÒyày–tïýOÑ“«ØcÁ-ô+zøŸÎ2ŽÜX@ªšâèì§G˜MCfz$¢Ô_ôÎÇW;R£„Pk<M„Z{a¿`.†œƒçß>‚œý¿A.­¸¶$qú‰è°þ84ÏöaiCõÐ4½–}4Ÿö_O´À­ïÄ5+P{vÙÃÏ ùÿçèy.AÝcàRÆ Þ½øÓ2wh8œ;3Ðwó78Ù‹³;¾`] Öð¦ûP½¶S(r± ôÒYýQ9hö<)4¥Ê•/™WTŽtׯI9  :ß ôf<…Óåݰ•;೪÷ÿü¿[©AÐÔŠGGŽÅâE"|Kò=”]9<œ}ß÷á9>Kqék¼ë?—F©ÒsW¡[fè|.ì,‰|Úy} ¶â±m*~^Ð…/•jὌ?û~­€/¼®JßMG—k¥áˆa£ñõ^qºè½-útÃï¡Ã¬¿¬ë*å‘[äydk$¹y$‡?ì‡ï^™qÁ7"°iÅÐëöX¹)êØýfSnÞÀ»×ÂðçS¾Ñ9ÍÄWѳJTî,ýRØ&‡ ÙíKV®ýz];?L£}uâÜ/è:n>rŽ|)\ ½ QÄ7ŠúkËà^¹?0l¹1Í?¹+ÞRºr“Oö1¦ ¿N"ÓG+P;¥´Ÿ54Jò¥Ù{Ù†Gasˆ ºè³c PPÆìjcYo$c²[¢I”ÌWŒLiFÙ¼dAÅÊͼÓjÁ‘ÛàêØB|6Dˆê]°P×~èÇÁRM'ÒÓ*$uÒ¾8¢ó ‹xBN<åÛŸ/NO€ã%gÙ½À&£/‰CJä–béÙV,>÷#ȶ|àA[aÔóãäû"ûB„¥û !9KõŸ¯ÇÑñ·ØÑ-µDôö9’&kBÃv~À—Ï ùJ&ÓI¹Ó=»oÆâžš5‚öõ«PsUd÷ÿÁ5ûÂèvóóµî|ùhKáç,˜5‰*ŽàGó. ºRrÑyD$Nžv Â÷|ü½I§?À°ÖŒij“E!1ì°æNþ(8=¹>w¼\—è;'…|„ŸŸ`ZW˜°µÊˆ5å|s÷¶Úż÷~“pa׃Žb…Ì76bæ%®3¥ûdi´ãwaÓ,ýº‘ézÜéû~f Wÿî€ø/ÆØ(ãôŠpw›<ëîI®_“§É+[€òI9nùCú¿cÇ—¯àlº¸ç“ßQ˜Ädö`k•¼‹”«ÑD9`ñ úÁà¬àçj',ú—é.ªÅçk܉H­\ ¯n\Ã#$ÙŸxÿ,ò‡í>óˆé,1¥¯Y¤ß½G_/…Õ |÷}ü1Ëÿì!lÿj âIåø™­è°çËGvÃ¥ˆs¸2¢Œ]wÖä"ƒp•ÐM3¯—VOˆ™þEŠªêΰã‹Â1ÍG¾NÒ㾌~yÉ qû¨HxÚãÊ,–ûÃ4ì$3–ñò_‡!Sk&ÞÞô '<ØH‡ûÏLøª§²ž²­D<í«GiÐÝ]Pÿõ0é0ª11)dJâóùM;uTœ(ÃGEX±œÙC©_žtN¦‚%.óΞnÔ)Âðñêxu_(¦TáôšX9H6® Vçabëm6Äô8²ñêÌéû}˜½%Šúˆð¥é¶üÚ×jxxo>\lÂïëÒ’ÒR÷uƒ¤Š£þ Þ="H¾ßˆO4Îcc°œÏƒÓ“áÁw{:sq ï]ú·¯¥qÊ{é‹+pÃ)YœD'zHUÌÄÜjÔTö<V勦ó.öDê@´­?Øëlâ¿3uh¦¬2'¥RòüæÄZ“«Éß½ eÊ#Æ<ÂSÓ*Èèa—ŽÝN¯4&Â[Á¹.‚(‡EaðwzÏiÒ{Ýlìã°{ §çâçÞö“ÿÖ+3Ñš7·­¢‡®Ça_=¯ÓÒóóƒ{ðÔú(€£>ÂU ÆƒªûX¬hŒV¤¢~ö±Å‚çÛÈ«ŸÏA|ï!\ç2—d>ÙòF"°¬†Ç3Ö€®ìW¸ïÞÀ2³Fc—(ºãî&±¨T J§áËÂ}(}ÜÞL[DC$£ÚT¾dW0tO@‰éëANwq U¤ÉÖì¥h-ùvÊ”ê×ß$–KÑó£ZÆîƸ´`l/G݆R4]¤Û„Sá`³_¦!ŠÏŸ pÞÇŒ¹6 +ü”øï)r$Ó÷;›¬ú ÷-Œ±¢"`{y/VîÉeC?A€Î;rà‘/<ÔÕ¥¡foPü[ªˆ÷Ãaé¨j†ŽCÔøàŸô@¬Xz0¥Ðû$Òè, ^$1±aht…¹Dße+çDzyj{pÙÞ&0 ÂuÂÅr °ãè.˜}HŠ9-#D¿Šn¼„­Mç @N\ã> Î ä4µSY¸°o”à^¤,¬È\ACžûbÚ…åðô‡j/öl¯½€£µàÞ„[Âm¦úlóS(xtó&Ï¡‡®,ƒ¼9ÏÙ¾5K.™ƒGÆXqëÜ@>qE3š•‘*±n²>¡Äuûå/ð;ú(Œm…%{¥ í~-KnÞHãņãžÁ˜àŸ0}v­Á±k£ØÛGþÎv¸bP(oÁ+­·™È·—‡ðÏôÑÔк,Ü/Ý_÷£ÆwY(,+g—ŠÀ‹©Ð”{¦ ìõG|žA¿E>éyËßÜÆ}oÓaæa5rÅ]š«ÌÖã?¦Fý€ý}îGÙ婈ƒ÷;¯7KáÃSmHV’Í‹ó`f]¬·:BçݯýöCŸ\RƒÚ'Þ¢¹å%²ýÖ.<ø$>¼:LrÆÈòR›E #1¬ZñÛ*Ðè¬q±óÖ4o ¢Y°nd4³ùíLg©Gêð…„Ô>΀7½½hõë~½¶ƒHéŒã%Ç÷‚ÖºCø5qFŽyɵ–|õÂ×(£º›ÝªnÆZÃ*²p†,jKJÒE¿­P÷Ó/—Ê_«‡l“P=X”Ëü"ËìLÀI>¯-_‡fhðòÒtÿÐ?(¶ïz–Ÿù¥áš =]³ˆÂèkxQÖ† FÑ­rƒhN9}biO¿û¥â´¸n”muƒtË~7W˜+JAc$éï kús…&  q,ü­ ÃEƒk1v²P׃ðÛWëaAT^°Ðtµ5ÙF.³Ð” /‹†Ñ´›6|ÿóuxöÎ,˜Ÿ„’ʶTÈÝaám;ìº>ݺj 5kJÁwŒ,n[ä sªaÓõƒ‚å»·Z2Zƒ˜l%‰~܈•+$aÓ£ñtÂ3,ÿ~‡(“ó™tØ•›h(2ŠoÝb3äé'‹#ð3#êjªY{A ,ÿµr<¨¸«ÞMoÃ[9& 0« ú–ŽÀ“Ù,®é5Òù(vo4¹èòRå"1û˜_êƒAºÔz{Øu$* Κg)’¥þ(oµS*&bêê6˜ºv6^wá9cÖ­ÇS»LØy•Flœ¦Ì—u@‚C8íKÚÑÃdàèBOžÐÄÌgŸO,¾}(dJOÄ[¹+©±Žlîž´H%³ûð-o 1ÙÛu<Þ£æuz­ÉÔ|ƒ¼Yüe‹$Ú?à ïe„éß¶s‹Á=ù¦^âcŸ,xz~$},‰¿FÃ] e«WcI™ãá$ånúÀ@îøg.¡ÅVgIpù6vfÕ 0ý ›ƒ"ð—¥xìÖwfg_jª±GFE2‡S §èßy Î=õÅ%{F€jîZqað€]®ð¹r6ZŸŠç¦Ÿʯç0vêbZÿ‚£©ÝUA‹Ý>øs 3Íê Ï_>À«³TIê'Ò÷7ܪ¡Õ¬œ9¿ÅF\‘ÇŒ{50ÌjWÎÙŠ*¨XÐ%vÇØdÑ"øa¤ŸGÙäoñ =2<–Τ·LEí'_°{ô&ÔQæþ†‰øí‚Ï—RfmîŸWHŒU9~X4§nµe"Îê”`±ÑbVè=w(¶\<‹,Jœ^5íÄ¡¡9—Á’W ø©‡Ð<§ÇUì+÷ñ£·:öKÏõÓ!°"åÁ;±"ì·j…v¿0hoýĩ"Á¸ ò WhÿnMа-„ûª -oV¯Ë¶>R+=N.xOA™Ö?> ^`UÓó Ädyw‚>9¸êÇ=ùsÔh€A›v¸ìÜOa²?®–9„iÞ¬bÞ¶rîJx:ªúìeÑË_‹Îîáàa¬ÌµºÖ ¬%hbÍ`ü`ÞÅÄ>ò­C4p±¤ßæ$ Æj^Âé'ûQñÆm\úNî={Ÿ­[ª‡Ë—y“9cÑ{ Ç>“w"†Þ‹ùâ4'ê¶&“˜ïÇÆóàØg(gˆäl^ËÛèÊÅ H_á{Å{ˆß´8Î!/@Fðuôt’z¦ŠÁD•TÛø®ÑCÓ”ë~M RFƒè‚}ïˆà€( -"˜#¥ e OÑ·pLf%üùÝc¨}ð%jôñ©±¹‚HÉ&6Þ1 að0” Iƒ²•BX;Üv»5wÄ=i…0Ç}-/¿ 毟³ˆ#Ù|qF~ýºWȪcyÙ`å{²„íÚ\ÌÚßÃQñB4s ‘ãáöbx£¡&ÌÍÀßOðp™.ÝÕX“(ð1±†üÏއªù Óq˜åÇþÆl‘8¹;>‚x&†¤t¢è[`ûŠ…0g:éä. ^Ó¸ˆÕ'qf uƒcÂI\qO—+ƒqrX—ü3~­Ã£ù?YFÚJ¼Úç Ž×Ÿ°dEÀUi»¨ï˜dØ3) ¥GP—›6¸zß"*~Ù,ßJs¹=õTCÈ!9í²Dýdù°q¡eâcHð¸ÆÜOœg/ªR0DøŽåvÔbAŽ*+¶ìEÅðÅx"l2äÌ^ŒFóÓA</IYl¯e\CT‹Îˆ–¢ê²÷˜àä}â.ë%ìÚ‹:î\kI!šÈ Éœ4KÝ€ÒoàËq3ª|`]TßÃ&“悦r•™rà~÷/ZöÂM§æfË/¯£BeÎ:ü(§Å{×^“Ÿ!0¥>–®Œ#û2Äñ²Ïi¼;„—d|Á-C_’DS7j•áÃåâëÙªÌý$¥Â'®MB‘3Hó¬‘Tn» xD۲ƱX Æ{V¥ê›õèouWZúpÿuЗzNåEGÌéÅA\:¼yiønä6Þ*³‰lÖwmh: Š¿\¹×G(Ó¥G,£š›³©åQ`/Âîp>r^*$è ¥{FkÃ/³OlzÏt:Ó\ Ç…‘M aÚ):¨ýi]E@^9ÿšï©‘ÃßÖò•ÖâúÒtHv&kNwÁ›'[™yC":˜Çä ù‡D+fõË€ˆNm!ý“Œi¹ž( ÚO²øÑjÜtsûëyFþÊ„¾K7°zÓNøëë–OÏaû-_T W±Û'ØÇY«ø9«¡óC:ä=Ö¤Hð÷×#áÑßlÁ©Åkxàz ª”0•Zë Éc ùfŸ~|nwˆ'c[^á–-ý°4{4_=í†PwÚ68•&D³—acê  è´7“éïÓãXoÎ~\ôÔ†Úµ‰s‘}Œÿ‰âIT©h(Ü‘Oe² ~’-N ªka‚~0uÜÜð܈:1ùÂtCª¡{l÷Yó»gÕùÏåxuèrZôä:¼8dD¯ üQn™qý: z^»Ò6w@£AîÊ‘å}«ÆRØøâÁà¶£µ=W0õóbõf6nþhÎ?•,Á•ÝWÙ°UëèÓýztú¾ÍüÔ׿0Oì'ŠônÅ÷“ù†ä}´ty]¾D«7Ý`64àC[]Lþ\t·õdÑ~§Ï¶¦³~–˜©†|¶˜+Nx¨Á–]Qã _ømǽ¾Ââ:K»Z—FÒ@Þ1á ÷ãŸ;Sy¿Ñü‡èYX:k$³²=ÆãbÎAzòéãæ€Œá5h¹:V ©4]cÇ,€Yñ1`õ3¶YncÍ–ãèùIZÜéÃ!õù¾Q°C÷ùºøºÎÇí±ã)«¿Á;½©Dä‚͵éÀÁÔo÷_eMZŸûT°ûÍk-O„U3iŸ{›9…%B×sûáÝ\9.»P ^·Nǹ lŒÏhÙSB ωó·ÇB’ó)<—€ÖsÖâ–w°áX:¯-¶„»/ûH¿:¾<© GnÅÀÚß¹Ï7Ðà“ §ª`Áš1t‚n$#ÑÛ„9eÄúF8H¹ÅãŒáŽ(¿VÕW¿…?&é$»Ð”÷÷ b¬+ÿoÍ*6vÔ¬ê)>\íA(ßbSö øs?Cç´âØ)oáðÞZÂ<7Pݸ×ä›årWõ7Ö[¶¡We‰HæI$øC=êÏ-KÆ¢zû>¢.Ìa'2 »¢%ŸfýÄ©N"°_IÊj\y²\,/ñ¡›Œ©¹e$ûac£J÷ ÄLÖ oˆ¹@PÏ¢`U‹¹ ÓÞ®ÅOk”Á-Ɇî¸,‹Äøeßp>tA¦+/ƒ›ûçÐóoæb#¦c¿]+XãM¬ #ìÙ¤'Î| ¥s.l¨: =.Ø%+‚‘ûõiÇðÚœÈÜOÅ ß/™Â¥Þu þÉ‹^:‚ê|Ø;ÜÒ”Ìdå¦ÐöŽ-äa§(q{µ>Ä6lÓB~åZüFûü‚ŸÕ¸¿ÞÍfñïrAÄ©¦VŠó¸Ô¿8bäKVÙùEð#ÖŠ«.J`ñ§$¨VÝ$®¼â yu®¦l§Š3¥ùÓ‡²L5=9\O2cÒàþ”o¦ñ‡ŒWX@šþž…[æÁ,? îFãÊ)É¡šXˆµŽ¤´…’ºS ³x:øé3Ÿá7 ©7G¿hFûö*,{â‡{Ö±ððV²9äs\ª>zeRfá=-3úeg?\~6g”þÓÕû>H”?CÀ˜F¦ÝðŽR†•þÏAõèmæÚšŽ›Ööé[Sù­e(³ÎÒ÷;rÇw³ø-s¨}>PÏ‹bp·!~g‚fž|hRýãUä‹¿ÀÕ®:ï‹ ^C{pïL&¼­ÄwÇ­À³¯xçQœ.=w-Á+y°G‰F:OÁ‚ªí°5woÞ0’—ï¥lE“ËgòÐ gáKìMA®k— gljãtãAtW¡3sááo¢ˆrüÀ_îà&GšlÒÑyéx^œO%ÂxgRÄx—Ödij±0IXÛ~vþœŠbŠÕL͹›IÔ'­¤Ç;rëîÔ#/5–@à4_šsÜ„~QÛ‹Í3ëÀÜËŸ>@ÿ[‹^ùnÒr¸–^šŠ{£ UbUèëk?1“䑹_Ti_·µXô6üÂ<º˜®«’õ¢9è•RŽá³áôïc¸eø¼šdV°O’ºXSÛ…i…®ÜúæB¿Eð£{ݹo¡§çNªÙõÒ–Ú÷ :ôVáÐAŽøëv(„^SƒÝ­Ž°ÆNÖÏÀ~‘~ˆª~"×íøÅ+Ú\‡êqmO-î•3žôW…CÄ0A^Ác÷[I—i n±Ø‹ÏËVÀ®—åpWÝ€¹U~'—Ún²ª1xÓ$¥2àÅGˆÖá ^¦Éq^‰(5µ,Aµ€•0®(†8øˆ k÷¯ š]Ó ÿi/^Œ‰oç=ëOîCIä²DË“(Ï=Ïeà£Xcªü[^šÖÂ*ʽ®/ç¹G㇠`¼yL}E7·× ï{ž¶&On+¢ql3ÿº1Ïù' ¾fFÛŠ2©}ŲÒy(W5›ÇšõŠ…–_ i»X-Y²ç ûðX‡ÖvÖáýÇ?ÈÃIt긩üvž âÝÞƒøÝS“©^Ô+ÖêAéð‡{a°«,ªÁ‡-–ƒºú(Ü–&üÞ OìKM}hß5…è^YÄïŒÃ}m[àìNuT:g‰Ð¢B=ÎáI_î@Ü Y.âGßž< •¥CøîÝ0kÒAðxoFµÕNÂýÀûlš_5ó¸}Œ¶¤–A`cyÏÆðÙyXœA7ÎîµÂ*í\¸¾‚³”–|pNIÃÊqLÌ aÌOoz¯2HXy#‚7Ýš Bö ¾.ìf§' æ9$R¥hÐL)¾5¡f£àMèÝ”§çU³%÷Éû•û€ÿBxÍ– «Â9p¸ž[ÛÕ¨ïº/Âëâõ`{³Äh2ÖRáYcÔA8\„‡‹XÓùCq®]qõzX±x ora}–jŸ€Ý J´é­º°ïäy–úÚ–Ž×†âel{‘»a„kõÕYÈ;E´{0‰,²ñdýÃûX鈻8jx<3™­ g#‡³…·BÁ“bË–6ø®lLçj^†Fç‚’Öƒ°{VŒÕþÆÌw]‚=×GòÉ»Òàhà;©ŽÃCliÍô"Ü·d+U:q)ÞžŒkÒEaJÖ#hú;ŒêýQàE‹0·ó&}¢¢Uߨf‹=دVƒ•}0øH$ÝûXçîNþ ÁÿœZçÓ%A7›P;éÅ8ÁF‚†÷oO%O.Áë‚xp0Y—6܆aƒ!è]ËZ¾Œ‹«90[± 8ºG2­"@d÷ *Ó¦NLJ1H$†þ0Öþµ£Cš%©÷ë³m áŸíÐùå|þR _;©ÓBÛL«0‡éA¿r>L½u‚ÏŽâLˆŽÙ{Ö¹q'~R2¥_Æ<€]Ã%k¯á9¹ë5:118ú{<·GCЉ4ý¾ôödÒ1è ôœßMÕÃ7©ýX'¹ Ýý söcá;Å“ÌcZ«|0˜' Ýö2¸:K'ÆŒÅíÍž°aI:a;‚O7 ÙKÕœ·~ÛÆ¯xÝÇËp®íJáÎ…ù†{AòÛAühñi¡y¸ }f˜NŠqûÛ>Èp¯c‡Y‘ _.¡¾aóÀoÕ%ü])I{[ŠÈŒTiXxt!ÔI™Ahk2S½nÑuoáL€&*‘ç“òáé€t ¾O •.ÙÈ’é÷<<¿‹ñÛouy£‹ wJ *¯ÏRì£c OxW$¬Õdtç/ÜžêDŸÊÍe £.ˆå^¶¼|=íë‘¡]³gpŸœHzýÌfò¶õo“ðqïR`ŠEß:׃Ÿe¦ÿ¨ƒKŒTçûž+CP’,½T'ßn8Д3xöèR¾cá{²®-‘c^×P_5iþFÊ…ïM^JŸwÅsIÕãdÙ-e:b§(þøÖŽSÄ×ò©Ÿná!>ŽÌš’JW¿Œ‡5®2Ô%[~y\ÊçL¢m+âhEÂ1aÊLkKŸÎªÝxˆ›tœ¿sH_Ï‚šQ‚©aJêº^ü9´Ýà{Ú\þ];XØÑ)NáÔÜñ>YÉõÔ5Á ^Ÿò}tô“)hzM.“€ŸÃk ¤_ka` ˜)þó¦÷–èÒk[è«cy_=¬¶GÏ®5Ü;íß|ã%œ¹kCO”L¥Mò½Óº„['ÄïŪu;|³ø€9‘*Üᘤ›¶ÂZ?S“.0T£IýPë¿@˜÷=Ü*3`È9YºH+ŽØ”ëð]¾yÐsõ!TÝ|u×^Á×àóðªv ;¡k ‹ÖòòZÎ…~IpH'&ŒÓ ÇÝJyÙ¹̵8ŠkÑu¸#ïöIÛ= ® “-'»úvƒêóÇðçÀýø z9Zrœ2ù½òž-ï‚ Ñh2÷Ô <¬Lâ¸fŒ°Ä³—Œ7c”¦ÑœŒ¯8|G1ä=Î>éËb–Ö-{^,ôú™ ƒ H‘u'ÔÅV‘ånÆøPY‹§˜ð-fn[«Â;®Oá6‚}…¶L?1šÛÉÆUÍÞT¤èé°ÛäÔ$ª\_ €e{ØœK­5àiÛUñåäH.ë÷™U>ÝG•¶0žÓ\ïéCf6c-½r+ ò32aÏ irbl9Múöµ¶ø´>[¼Ù{øÌ^qCZìr'éÓ {E>+¯µüAîwàpu.û–ò³&@XN$ß&²Ó‘%vWà„3ߣWqsï89?gu@güe¨>·—d8«â$_hÙ!ǾÑRntÁÆ[1ÏÓ÷¢½{ž6â~)YºÌ»n¤›Ó°L'ê–_Eê<À6…ÐmÖËþöÛ™îvJŠJÕMEñ¸FáÝö8½àšç›ÓÓQ¢Øx×5ª0åÂ>>¼Îè@å’…äj5¹Õz™W^z\ޱù¨1Bž~`@¿Ëé‚™”øÕ– ÷çÃð{Œ Žx®“8ÿ’ET«!ßÀ‚ųhıÜ¥b+ ®­‡Ç£·“û‡ðƒÇñ ÛOÒ¾Ác ´°›]º´˜P‘‘qèûw [=lÞªvãwzÓí+»±áR&ÆñM—@Ì#7Xp¹‹¤6f*-ÒØïê`™ µÕkPqq=-ïîæy›€Î‹K?õÉìÚ¶í9âøl¶¥É/]+ ³on¿¦1?uøó¥j¼#C„'¯/‚µÝäù0PÎCñ«Ò>º©ä'éî{ŠÚ;OÒ˜GRôOôAz-º ™‡{[·ñ–Öù 0Õ†V™ #œ~áKŸ.æ¯ÿœÍì!ñhI¶§3+=yý"ð*™—f;íE]w„îß Ë¹ òÞŒ¯ßå¡~È#Xþ`×ô £žÖë„ã¢)_éÕ(8»^›*~›Iµ5xè˜åüýš »^qÿ%õ%éѼj^h;Žvå߆ø‘°eÇKÁÉY^´NáñQ"üª ´4^»q—V&\>l o¸Ò+a®tkƒ!½œ—¦`ÉZePž-N'äJô¯Áå 4K»5SÛÑq€G Ÿèq_o!lÁ£d xfæ¿‚ë•KXæËÂBÿ¯ R®ЮÊ3ŠÈ×j]—ÉX£-„Öï&\9î<3 ¢<±ö'®)Ø…"2ËÙ¦+òð¨K›nsÙ ¿_Ü’ÎL¨Y¸žOÓ¡Súb™¡ýIèý  ?éR‰ëÒTáÙbòËŒo0:ÊÉâ|pÁ„~RïÍc¾h£tNà<Ò€?MuàÎKµ¨øÇGÌL·‚Ù Ô½¼ñ0_Æ•XÍÄi¶TÓë&ÌÿÊÓGZV|…“ìóHA±ó`ôë(Æ|(d5ÆÃÐ` îmÕ%…?`„èrk–ÐoW éûdÈÏ?QCó­æÜÐlXêæ°ÒLYêmµz—-à¾&9ü‡ü°?ŸD@Úõû°àë ´Ît½{:%‹½¡ˆ"N[YËêj¶dñ-´”NÏΖãÒOoÀîþ§ˆ’¶ΩâO?7^»0€ú³+§Çaøê,\¾.–¿¨Énù[qíá=0X&?ʇIiŸ HøŠm3`~÷Eô©‰¢+‡ZÒsô*~6u'»Ž;â[›÷˜½Y_uŠƒCk~¨+ÃY;+±lrbm!¾p, #bæAÊ‘y|l‰+–ÉÐf,ÇóáÜ1Tjs.Œ±Ó¦>ᘻ÷¹ºÍ…w$¢ÅñjØåðî,5Å3ˆ˜b0¸'hÒ@Qœ|¬Öþ´¤r9Ùìà¼"’=‡þö3bWÇÆVÊÐûÎ9X‘ó’8Œy…¢p3 ƒ^ÖïÅä[jWëF-?ºÒ|Ùr pJÅã’pþcË>†×åA¢A•žlÈO p@ý8L:¦ùðá¬Ði i/ñâŽ~£±¡_Î+„þ§¸mÜÍÓ#ã¶ £ï´õ1ô«;j]ka3-¾ã‡e èMäþK—¡ë‹D|D—p»9ŒSNxó¬ÊLˆŒh¨ž6²œ„{†Pp²` nñ§!΂á>¸Z–\i—¤;vBdâøÙcàÏØ¿ø—ÏNwJQœÿåLÙ¦!£ü`ÆTÀ±Cd¨Ö8KzoTæ$ªÐ©Ë.âᕟá†ÕB~Ò ä½.Z\;†·ðª äÏ&¼´B’ßMߦ.ÂÕ®·á ¶-,  uŒøüIN<1Óƒ¨æÑþ†¹t™þ/Ý‚OÓɰaÆu÷wÑÝÆ\NÄ„)N«¢?’Óüò£ü†Ý9éAÛN8 @§™&±I|Ù¼=|ÄT¨•¦;C঵¬›À½¯ŸoJsÕ}éÃ!wˆ„Ìöݳ úc‰À#–—÷åͧC„DöÐקѨܒ+ëÈP¬”áMë IÄÉm¸.MŒ†å’W>gá[èYZê.J»zбÁ¡J'}z§¸GÛ5¤óÃçY¡(:e´¾öÇü骰Ã+”ÎGqû†_pþÏPìóuã—íøï&K~ýT SwºNþ7ð}êúxÈ&Z[ãˆm}€±cSþ¼bzwñœ»–l^9ÿxá*Nöà—Ë:qv–éz:Ï-*AÿcÃøQ¹ÃÔú‹ +JQ]ez9ÜnÍA¥e*0rñF2)ì#¹¿QÀu~œ¤­Y©]«9´¸ŠËTËAU¯E^z&w—õ®˜NN2(+ƒû@Ãþ˜|Ï"FÓ$¸é¾X®ŸKÆJõ3ë \!˜è}šØ%5þuºý†ŒITâ^‡ã?ØCÇ/‘ä¿4&££l"þÍŽÖlÒâm?´˜Ñ!^®sÄ ~ÙÅÎÜe® VÿÁ.¯/çËŒ3P1ÀŽÿ^Œæ®ÐoµÚÏ #gfU‘OÏ÷b‹ìGáýKy`®êÅîø0€­òmyëÅ(öpÅ2üÌpäè#p·TšÚ.‡Y·NcEe%7¥ÏosǵšÜ£Å€¸|ÝǦ5§ËoÍÑönÞÛÎÁ4#ŠtUzã÷äOÁVôíu]ëŸÁ°üä%ìM¨‡¡/lx¾X m¯Eí`8m!ŽÍ.f\{ìáØœë(¿1†žñw]L=YP¡ge¦ýpÉj<ìªxÉš_iÐâZE÷Sø®á9ø¿þÌäGÉÒ,Û á¢gÐ]¾o¢¨Ò«L¾/6 SS]çý GM³ z~?,QÁí»Gñ mÚ|ªµ·ytšè¸ã—¦ãгñ!–.ˆƒã·ó¡>Ì —¾¯D?ywtw–àö^T¦ “ªÉDó‡älÆEé—Àôz !zÙ)Ýæ†d»™f¹#Žš=Dð+nž%˯¿ZH‹‡¥µÛÎ@¡Fªf¾óaP)x‡/oäSÞªñ™b°É(ê¨Òäñ¯• ç’O>²W¤]ÂÐ9¹°£Õ~ ZD¡²FtÐR’PöÌgfÂê×à¾á(VÕª-Ó¨©–™°y¨XEÞÕa¾¹µ[* ±S“ hÊ[föÍ‚¿mÛ…ÅWÔhÅûE|´R>;º>–Ü(#f2"xï„ñÖàGe¡6·Ôç9à–T/œgÆ|bOz®Ö-ß1ƒùÿ ă= ÝñkŽX(˜>À F[ÎdÝãU g¸1oÔÐgû­¥yvf11µ4âßKî£÷ 1ê¿“Ìñ(²s‡ñ{¯Þc~"x½ÇB;íï!ÿ•žqT£ƒ³ýCY³­»¿›]­ø 9;aá„áü Ÿgû8Õw!ó#•è1‰vï¸*{³Å‡w[ÅÃÏŒ PnÑ!Çòåç3ÑVü)äïíÄ"]Œýð\‡î·4iÝRyÞÝ9‘«'žF=[K®#Ù€y·xþe;¬šþŠ„/…at¢Š'´+$|ë{ü¤¸‡úÛ‚•=_ɳéëñòÃãprh]à8ˆ$ÎH#GžÌä.žb8hBŠï ¹ë¡+ú¬|‰‰Á{áÉÅ8÷V?æ ÅéC‡fŒhbc7·¸{|î;*i5£¥A&l̘Õ3À­º„Ï‹Åi‰x+˜ËyÂøM{Á¢QµŽÊb¼BT.Í8msmЇ§šËÈÕ±…0¤èkÔpK…¬NL® jOŒË˜‚x,zç;K1˪”Èëü7^[p9Þƒ±[ÀÝü«ÌêƒüˆÕc¯Y\*ª‰\öôE³$VXgÃò/å°_4YËt~loÛ/>>î×8CÉãsä/$7&àe™ý0s£(_*.hû-üê|Ùù.ÕÚ6W|¸¶Ïv×îð“–t½'&à2Ê,{w=Nm¥ÍáØ¡xcnœ°`íX¯`ìýj‚7¢ß{M0j™Kw`&ªâ¯'Äïn2yüM™<¬tÃAcÏâèùñÌàQ¹ªyFÖ!­¥+ñü“m›Æ(ž¾ÆîzC|$y8¢6¨Æ}óLèë'uuây¤½,ìœQMlßµÀ*Ëü³Y UËØµ_ÐÿÉGb/ É·Æ‘èw±'}M€®™‚ó™ö°pV “¬^€·šZ‡“Žã2|Û¯=ð¥¸\Z'dÍA¾øëv¾¼^ QaPhSŠïtJñ©­%Íù9˜Åç£RMqö;ƒ?3ïýa¯QL®^‘xüå&B#kÈþ_ËÁP- ¾KISy·“h½RD2Àij¼ˆ¨$«ž·’oúiBþÅÞf³?’7ãs0¿¼ ¤(O 7ÖE¢æ…o8Ö(SkZ þ{¡@òÂ$z(©ý¥O’[ÇMIKF.­x€GþŒL­ôaRº·qq™è”¾54®=ÏÈáˆ#äQÏ9ð_?‹ÏÞ‡áÃ;ábÚ :q¥×Ë×§»Î-8uÃw»Z…Ýò¢ Yt]FŽ¢fhã›øñ¸ë« ×Jãʉ[ù›ØŠÏ_YÍq>ÿ²!X§p~¼VZÈÁç€ÝØ«¥ >Û#YôÑËxQÔ&ϦàTø}Úñ‘$O`¸ènÈ Ü*¾·0ö"é4á £d誂*\jù˜YþNŽŸg ;²~¡¸Ú/ölr þ]%¨Ö3âñn¬íCˆp·¤‘«äT/Áï¯á؇Hðko„'ûV‡GÕÀìýÞlg–ÑIAûdáæ6ä¯ÄlØe§Sîɸ¯ ªv,ëÌ#Þ—~3±C“ñÜÓ.l*Å̽¹Ãeºÿw.µµ¿‘uAQÔ|Ø zõ%”ëÜ!š ÓRñ3 |Q4\,Šà¶Þ´êh5ñÌ’¤MG~ Ú*B›ƒ4k;¶×,þN¼{ ͉õÐuo¹?× ª®-@‹<ß=¿6˜å ·uƇ?ø>›™ísÀô±Nžå7§Žuì"ëæßG‰ƒ[ñ’KN^áƒ*ùÐWÂTº>µ/ áÙg/¥þ$ec4ÌЛK“¸] !>< ÊmN¥…þt&uâéÓãòÞý˜"§€B3Eª¢ …U:³Ôχ„“Ð:|¥Ñ`œ[¡‰ó~ „·8¸ÿ”âwìOag*#óÞ`œFüh ‰Z«é¹ŽܿćV `¦ÇÉ)°$x+L“Þ@¼ ȕúlÚ,)8Ç»qøÝ;ìc»4–ZCç÷JvnH H¯ŸòF¬T5´˜‚r¯Ô­r)îÛ¼«$@T/V°p5vˆ‰±é(e4ˆ{¦F°­?•¹õÊ^˜ÿ»Ýã¸+‹&i?ÕèÚCg‚tÁFo>áÖG°MÓÁÒ/ÑzÈ8\©¬Ê­<íó:;—ƒì6ô‹{Ë6šôÇ4j{×…¸-Á˜è½˜[©À"r?]›ô2ð‚‡ms;Ìvøê‘¯]•¨Ô(Jg(Zâôä>êýX㯋_'­)–¯>ã÷ßù¬V5œõy˜Ó³õ»@|S*»´M^ ¡s„ôQƒšÜð¥í[ñvþ<êI—XªóÃß{Yã¬OBÑÛ·ÐäcR\v­2»|"¯gÀ²•)h8_ügœƒJ‘ ¾ûj"N=ÀËëFP“õvüÂWîIò °å n½¯Ë÷…áÛB%š¯|ˆçèÒ3þNtÚƒƒ` ܇—róîi¦Ü&÷Ί—¤k†]ó¯cM°$NQ O#ïÃ~‚ͨÑàïu\øðŸ ˜ñ[ÅyÿÿãèÊéz»(2Ïóï9÷=k¯õ<ëY7È€³xó%¦âôf;§+âtèîù?A?D˜LuØñ’] ‡½¼©¹Æi†gÃÆXÆÓªAÔ˜GœÚ<󤫿Wqøç\BÞXìl×"Î'VB»— ÓJ-}€8”rÞê^Ǵ⇸Øâ*Ð×߸<ç²ð–Íu†Š6à ë,[x‰£&ò•,‚ÉCï+• ñ~¹ÌÆ]MÈóÌžjª“«HööX:8ß1Ãa!ŠÇP§Ï¢žúÕà•$H£ðê3”wéîà™!1ò² |«Î&4¿‚«¥NMÛßc¯jì<,BZ™Ð‰þä˜ÐyS+—©°öî+` ¥çŠŸ)óaæ¦YTwb¾“äê¯0d“vªÈÈU1§üe\ÌeQpÿFºÀ½ˆ]ðý×£è-ÎOUà鹰ÕŸd<=ƒ _” *QjÒ`­k˜_¾¢Ögsút¶ý°¤Æ-¥íSßÀpßeÆãÍܪGE=­±¬u >v¡–⓵9h8ñªV‘•e™Ÿ7ð?󕿀Y.;bó×p«_"ëÉíG߃BôýÚ#@‡èNanî“ZóÜæòìyôMŸ ,:{®Ä 5ó-]ó©×è/ ~“5‰Á—ÙWÅ \à9×%ÏJú s!¹Ä`Ÿíè[É}mlGLò_³]WGà®ÖL‚£ñ¨"ŽŠsâÀ“o/»ó /Y]µŠ«²p—w*Ó˜öŽ»á¾ QÏ^B½¾.Å™b3ÉÚÒÀ·§ ŸYýe’ù^À§âÄåñô°á'®×àÁÖä”A5ÜCü2ó<Vm!S4EqúøTªþDöÔGÐ#¢¯ÙsÇÀ%£góÐê™—P,#Ø_ç8™AÀáW8{P€TS‚Ô›ÖÈÿë {”¯Ø%cþÆ.¦r¶ÞÐ}O¤,ÐüÀ/ÌÉç:,r?ÍjÞ¿åjÄA¯¦ÛFQµ»ÓØ€úd8bÆ‚ý¥;L¬–:\xv/r¡%íàXË¡þ¡A¸;m;Ût,›OaÙ²0'g’¿lú Í)š0žåJ~-ºÉôÏ!™?ÎAÚþL¨¬aiF“É1"I®Éa̎둽?ÖâEbÙ«Duæ-™ŒÏ©´Xžê¦±E»-˜y·HÚø²%³ýý4HêGBK¶aåòjâG)zDÀ£~߇¢¥sH­“ ®î…¤m*4½ CÖ8ï]ÄNŽseMz°Ö ¯ÑïÅŒWž? YXITÔ“Ùȸ[P¿wrog†ãWµ ¤*Ü‚ªî“yÅ›J%æg€B“&y(TÃu½.ÄŽ-•Õƒ èö'{Q*ê-Ú©ÆLj³»øU&¿&¨cÆÁRœöñÆÁõ5\ùØJt©£­]èâ Or‹ÂÙþ µ(¸D’î_XÐLhOäkl¾þ’u›-EV^/ÀsÉ¡×,ŠûŸ¯9AÓC-ŠñõÕ8HÈ䣚›éWÞ2LÈp‹B÷_j¤6½M?èàRýBèùH UT˼‹Ñw摈@Ì.á˜(ÑÍA¡Ë™˜GÇ8vU%mç¢ðªnFé‹3nÌõ%›b“±Fï=Šç=`/¾bÌħ`w?å/¸‚QSºá^¼.žÞ>ÈбÝ÷¨““¾4Ž5ÏFõtG¼_ @œúŠ¡p‡ Û¿oƒ–뽘§VäѪ©´«hõ:­L_6€¨>2 äš`Ö"eÆU*y;T‰e¨jn5M·©ï÷!ûT‡MŒ†=FNœb kéó B´ã×+°«ÅÉõœƒê¿b(Ïß —®ºÒ¹Õì4ósÀñ:‡uI«m8£8ãf ü^è>‹~šÈ‘—ià%ô“Y{2[gV£]¿$%K[™æØô’`9Ý&0ý*îÚ+Mbf®C±Ä ìQzâ‡Ûá§õjpݺƒÙ´H—‹ÝÂ]©[ y¼•ËÒ˜ÞO°öÎ>b­¤D-„,˜³wi»’ù}]^=‡‚+qç#IÜGjè%Á†-5ù{$bÛiºQYüó §ÐÆ…ÆäÞ÷©°ãÖKkS§SdUÛ´þÜÀfæ¼C2[…|Ä®–æðUaÐ4”"Î ÁÌZßÌë_‚yû˜„FA8³û]P1˜ºü3TJq÷—î²Ür{fÞåºÛý.·Y¸D’ŸàÕ›Òä¶à=î•×MÜÞ/°K¹¦u¾DÛg’Ôê÷zj9¶=“·ä•¢%ËCc¶I`A“:J,¥ñ§ðφK¸AC˜wêáöM#ÐïæVÏ/À¾• P{ý$ˆE\ËwëÙ«÷õAÿÁjæûÁ/¨ÞìÏÊ]þjyÆôöïzØçyŠjk°i|ÙÆ±„·š1Ø~fÛü“é‚gåh¯yVÉ]Û÷€‡fƒtJ#mÙ ùu‹ êã\«;´RØËS;At½ ]–¤{®s˜EÆ6tãµLú ûË>qÒ¿uAè‰é¸ûh7¶_apÓ¹Å4±S ŸžôÄWZTõò|<´65î s¿2'èÞ¦9›z‚ž sÉýÊ—ÀÙv¢"fâ‘€ôiEén)Ü{¦›’†ŒEÀ_[‚É&ôßu!H¿áõ2ê6sÃqûÝÉÙu|J^!Öm¥ÖR…ÌñÇfìäy ç~ý}™–N^Ø AvMꃡ4.r*øNrh¹QüÏÏ}è(á pßæEo&ðcYŸn˜Á’¯ÚÔ½,‡ÂÆÑòf ób˜!EÕ!€<ÈŒðHФèP¼ 2›q§þGŽùÁ5Ìò¶³`ùTŒ:øa´L.ØÎX@Z¯ÆÀµçÁȼŽdç¿|Âøõå‘öü$r3aHÊ%³þ2Bä•”žÞ¥Ã>t¯À~!"äD®;:ÁWßZ6ÿÙNF¡¶­üÏËŒ¥-Ø¢5…lq@Ñ;O`¦¨§úïc0û—æòàî>=œ·O‚賸¢L2+fáë‚|'¿bëjñ»2f@ÿ©«¾…C’·àÍ-*ð­™›XÇe†\ È1!g_ÖÊ¿Îêì÷f:WjR!¹yÄ7¢ûÖüf¯J&9¢À$‡à}¹P5R¡AŸ‘œÔFéA õéœä™_Éßß< "×€R쌵A6Ûy“¹eIl¾U"-YÇ:©’°©sPÛ܉䆜!á4…^ßxÞfÓîö2c:g‹Î߃áÛèµø»;€yùh3Jí_AÃ"¨I“Û™ZƯ\Kªy#Ùï½)û˜q[â)}‡Ûg`G g®·£ýΫ&g:|0>ŽãǾÂÝ2?=ŠLÛkèë'"8_Š0Iò4ÈR—z\»Áª%3v¿¿±EoªAlëSHŠ›Gº\àÃaÌìEO04¯Ö¥­"·=ò_³¯.Ü‚°£«qÉUÔvx%6÷¬CÅ|OnöA#f»Ã£€G¥A¶N¿¼ofîÞ,eÇÂVVOÇó´qBø$´Èm#Œlª­þPŽ¿O?f|åÅÈÌLŒˆæaø-wCåãqFut¦°ïà€rˆ7Ý‚5«…ɶÃôÊM[8ly ë…7ãÒOmŒŒ“|=Ó®\æ¾ÝÉŽáЦ8ø×z¿çé’™qk`ÚµEì…²I}îªO¬u™ÈCùÌ›ìÓpág$ׇsð»|­Ó"ó|AoU&®r%‡Öu°Rëò±÷š9UíÊe ­Èáþ,L;´3TØ[~,86þ,a ^\bŒr`MŸ ¹äÏôMD¬>ƒH9|gEìyq_¬*òÞʤ­§_þª¡H£.1;Û#¬ˆ…É4úÔj NKÚO§ia¬w3x-]N;5#°ïà8T?Ô„”ﳨþ!}²)hs‚‹½» {ø-8ˆã÷MU4ÈÚYÉncrÁ_†À0ì…C‹·£þÀeœ]×Ï|Y‰ò°}㼜ð…ódÔ=‹ ±“š³gT èæH+,*3a¥ÖøãÛÐ¥TÂm%w<׌.[× ;8cpÝ^޾H0Ö[­aÂN7Èc.ÚM'ò6GÐÔq„Mú¢I}m¡hæÎWvæ”Ð GlC'J˜Œ!†«(^ƒß+cØŸëbÀÛ‡œŒ?Ú”+½Ô†–á£móðÁ3S<s*³=ȇYÌÒÐtü‡.ô÷ãNÐÀZX`ö.¸ \À0pÀ,ø‘%¢$LR’lXô*„ ñòÖ›o¸þmó kwpe¢î°6‚¶ôµt‹ó¹¬d$dù9Óe¡T#.’´EÿÀ¬ŠCmSñuX0ª&6ÛÂyPàò [zÊ¡ën+¬zàNNôiÃÓæzT™éLî—Ò\y(u;L†ŸG’GÒûàЦcÔ!§x¿‡XÄô—š”ãÉ2[®™Îjlëò¡7· “±L²ã‚$=q4ª¬µ¤Ý"CøðÙkÈuR äèWŒzÒ‰' ˜F÷Ÿ°Ì(Ï8á» Šôo |’qÆ+ü¬µj.ü)ŠÇcH†Ïn"‹è]å(&v&•V_E x1mËù ÉkY%Dî@k?m¼¾«ðÎh‡s‡ñîª9dèïbòds>hž£ÜØü+(Ibéž3šðãáFº¡éuyUN.Æ-ÅGûŸƒœ±Ñ’y5zD³êt­#ÏFæQ§Ó”ÿo=dHœ"¿jÑÒ[wðqÐI2øã Í(ŸOtL&ùà™ïÔÒã>fÄC‘….öñ‡Òk«0ö§øÑÀúòHúY=t8SH@Ïx¶@î-´¡”G•ô¦g£„ß:4Q-Ä„WðAü\ö¢œ±ÊÀœ+P¢>‚ ¶`X¦œûœ‡ùUà×%I…,¬±>k€¹§ÙÁú‰‘ÁÀyÌŸñø²!†™Ýƒºæ¢‰’0&E–£Û.ôá‰Ä¸”<¼nèI0KÖ‘t™Æò`(8͵#AºÍ ÷Œ.¤Ç]ËVoúyà FÝ\G÷yã½ø|ܸ¥ ÙàTá<…öyåŒüáXVâŽãùý„ì˜ Òø™-Û Eü¤º@—±¾~?$N›‚Wi¾S¥ºb{AÝ%‚ípžŠê“š¯×eH cRoV€ú–¹427µÑ)(ŸÞ–Ù‰‰‡Mà §÷Ëi¢çáËèj¢Ž>ÉÚTn­=¾²]ƒC{[Ps"ŠsRb*,,L€ÿyØu=X!5¼;5?Á:4¥5h5_’Ì,2Â’bU²ÉiÝûn }ä~€ä»íÙvo\ºñc­‡Ž¯CÕ·¥ìÙ5øö_)û*ô-J½Yƒ©ñY¤ôÍ0g†Æf|üA‚\Êþ†¾†¡)¥:L¡m¥ø>¿lûs`Àø òoŠe6gÐŽ‚E8çæ祦‡çPwûp¯L'L¥-}ýŠÉ´ôe£ømÀålzõêôL~·ù ©ƒå´îÝ{<À-†Óç‚Éõ˜êªFùŽoƒgÈØHä¿ú3´ÊмÍ¢mJtPm Õ©9J‡¶[ÇlYBÇÏ<‰€J\µVu+R_—¥4+I…µ˜cŽl©Ul¥þ½ÛÚ÷ðæÕ?&éÑ<:èå‹1M„£’޲jñ×l¢ûå¾?+ŒƒÅp©¬½|ÈÝ®Œáóã! ¼z¤RX½¨>èšC«;¿ÂòÖ8ý¤¾ßô`µ„‰]•‘ J&Û shÍN†Dˆ3Ô’#Fgg0þiÊ¿XpÈv!üÓ“i¹"ì§u“:áA7íö$Ç\ÕÎUpd•RáÞfKX>ï hY>Â#]Óˆ¯kÑ8ò‚ª óдÝÿèœÒH£æÓ©ôjM ³é‚ÖÞø‡_‘TuIã„ä6Bü0àw$Ì< ŸÔÛ±ÉLj41siTYñ"‹í£R?LÖr‡ñg›¤Ç~g-yN±/ÃWãÐï)Ds¾1É׿ ‰ßžnæ»_ü¹ÇÛê «¸’ôó­ù›a*‚l¶5­öa•®íç,žWÊô¾ªÄ£ÍàÑ.A6ŠÊà ‘CôCå–²O¢Pò×l^³å?o2¨¨¬‚ÖñKXg¯Ì³`{‚:]5¥îë/DzE(;+JLLEÈíúû ÕŠ·ß:`eø"ÜÞÆEºVXQþ†.~4 +O±;_µÅG]à¿f:ù¯oá7ÂüLÁÁ+ÉÝHYvO„TÌçNîKmâÖ¸!¨“\F‡ãD,½‰[ëršè7Ä)TåÞqø‡ËÄè,ñÄû ½tXƒ<Šñ¥nÑ®dMÕ9"mâM‹©¦pãêÖ_ªaÞIE:à†EÏz‘>n»c 'L#¿(³Iê®7ŒÒO`uI·MfÙYèù óDµˆõÝ!˜¢¿fÁz’Ù3òž3²³„áü}ÀtxÍÇè#:èœÆË´û³Q·à ’ÒjÌ.;‰Uî¤ÞÉüÙ^„ò†Ùø€3‹ØHü³·PöìöFAÛ:—\iÃŽÕ‚dO×¼àÂÜÊMºa¶ô$,º¹€*ì-ŧ{qSK w[äC|aUƾåBPZîVPgGϵÂÙk3ñ?¿õé ìêãÜø'ÕÜ­¾aÌ«•º´°iiܽ’uݲ¥¡ïƒ.üh±„P>u¬Þ¯ì"Q/ë3hu-#ÛgS‘‰©´­È2®(£Ä_wüà4“Tÿ­€æ~Wôˆ{Ýn2$Õú*«ñ÷ÜW";cÃXíZ!’<å"síƒ7Эùsðtæ<ü´ î†Ù?WŒPËP‚Ó+-éŒY¨ØãŒÃö¯ñµ¸š®`2º°æx,Y3ó ˜Ž¡ôLö]ÒŒoèÅ#´H™ß4w’Äèí¶4Mèž ²Àòë¦$õø,z>‡—íYiw>KRé†Lì~­E"-§Ò«PJÛO쓌fÝ+Ú¯\Fîb&Ú¾c«âlÀBã4ºGÒê´.yJãÊÀP²gü{Úg/Û³‹”°÷µ)½l÷ìÓ¿¡Uîf#'‡‰k&Br˜³Ï˜,M¨‚[µyô4…3Ö^ S¥qÛß-PËF£R*mæ%ßò³©éÅŒD°#ÒYïi¤½=©Ü$FÕ}‡1×^p”™Çé;ÁO„Ï¢%†ûÈCa2oÚ·tr¦Â Yžeþx‘-Y’¤?ç<\:—K”dhœæ9ökêG®ãgQ²mÁ\¼ª³\0§°Z’sü<¤Hîäýw%Y+T0=ó(&öv›Š' •:]wNÆbÖºÕ´­ftžf^)¿Á–š$vÇ"¦_LƒV™Ðñ{Sh&÷¾xûœi£¶=k«¶D)}Òñ)†ø®§ú—l¨“å'¬KŒe} y˜ebÃŒ°ÏnÒñÏ Ì+–’K· ±‚¯–úëÚS#) \TëFmž|³6I¢ÕcNm¯è“ª-äô} üxŽŠ…)Ò/Ò¾*‹]rf=±/†¾g йv).~ƒQñú8T›¾ò¦0ï«0ÅzŒþ©dHÁû"yiß“ŸL@I<=å+H‡>GÁ¯Ÿ­Pø¼£ ÿA;Ö/ÁQÌYbÆQòÆ-ùžõy`Yûð¹ ŠúGùÎ0|4†çÏU1•›åX» R³%TMc `[%c~v++] a ðwØÓÓàf'KÇ0žõ+.­‘c8å’¬û¨½¾Š+{?åäÄk¶³c¿%hXª¾¹mÑÚ^˜ºd?#~ÖòFå²®N¾"©©Q†~Å…ô­“Ùr>jl2Qhë ×ýÑK´:øiRß¹“£N™Sa›Ø¡#âó=t¡.íXE\ÏT6üé}Ïh{ò‘øtêc[¨~*J$ÇCÀïÃd¶—`Ëîë°õj~(¾ƒF§N\ è™KTáPM4®WæNÜ™ùAÚAAô¾ïͼۛÌýøw1¨·ztúrBc¯£y/ ë€_ë£õ ªŸ×ÀTñ¤<ùø·™Ý.õÜe˜IÎp }EøÈæ· OC ¤®¦y5¨gJÊ>Õã‡/ÖôWî \`ÊPù¥3`¹.uè 7Rþh’ÃôT‚KDzvŽ2ľë'CŸÞbúUê •.Ï ÀDŸ¤¿¯¨Ó%û9$ifÛfº’Ô?­ æn{_Ö:N°ð3]ê/×GW•nà§Kï¯Á‚=‰P¦O¾KSf™/TW#ºÿ¡î®äºýzôþˆ±»TéVëy̧Ð[ë£CÇÔ™ÍÏ?°5g{±Àk6½ð[œþt^O-B7³ÝãÌTO’±%‚i[§C]JƵþí˜k{ S*ÊðæébN~éî“S·™fÿ/lÁûŸÜÛ_M0ÎMžvoÝÞF!Üü š©§CjÀ"<ñì7Û4xZ}ãà²e d7ŸÆËdÉ® E’YónèÀm&äÉç©D*ú<§3ÆÙvŽ¥,¤~×÷6üŽ‚œ"a­c y1!üË pË„ÇgA²¿®N…#{ÝABä:¸î]Ʀü½Þð´Ãvò³ø7МlI‹ãªYEb–UjÃfŸ6ØcmDNÔ*1÷7\"MëNÐî‰dkr#lÛ5‡:×£à)² ý^¾åÂyí¦Ý*„F®ËÇd5-ú'ñûœ÷:š¹ÛãnéYDS¦®‚!}!i whøoæµa)Üs½ŒŽ#ÁtænŸÔëïÞX&Í:gͶ:a_…û©‰º7ñØBpÈØŠÞj÷b ^*Mx~Ûr=,´è4§Gl†²7Ü,Ÿ‹ËÄSÐvè4×2P›ÀoŒVp»â™4ø@ÍÌÓèS儞òï™´èFèõ¦»á;vØš£8¶JyŸ/3á±ìäóñï!b ÞRÉ.þR)Ë%aUâb(æÓóA¶õïÈÞcÇØ8Á#C§ŸQ!?ÄI™Ê*ưÉJéô¿Li­ÅÝ8gÍIrýÛ¤ÿle/mX· LÉœùɨۃØ\Îå*¥…±ïXÙî[ø<]ÈzCÐ|ÎG¹ú&´¹œÓP¼Ü†HUEû-*è'I_­ŠÅáFs\ZžÔPPóÝ[B@B|T¶Þ_ûŸLã|áüdVòÑì(1PîdÏIÌ#æÓƒYßmx.T²»÷;6ô÷o‡û¤&ê )DÏ/¶$r Ëç×óÍ2là(³™ág™"²$0(Í„k²£b!ò[¼£¯ÄehÝ„!õõnÕOÇ«ëm ìÜeÜyhjUlÀÆyßX‹‚.Ø.w “ߨSÁ/üèx ~ð@ØænXyã>òd^$–«+q­//ÌØOUj±ö[Î2Vù Ì®w-¬á/?ì8:k‡Z!6ng­W#ÃÎdxcfƒ×ÄfV_r©-f «É÷~˜'~ý„îsIp©¾ ½&ØdFw€C•»î² ×:Âé°Z4z¢HlÖÉ2r†Ó¡{êðæ ɼƒÏ¢¡x|(„ù4e˜i 0Š»_Aߨ \Úÿˆ]ñ>îß@ã¶²g-ÛÿìúÈı/¿Þ(’â u+¿G€Ã·&²t.¹ÛxM2LǸ3²ô~ãQîÐêDvÙþ îÒ€YÄÖµ^u¨£ÖI>r)ó¨ ~dšõŒHÞ71r&F zÔÃÈ#. ~ôÊU qÛêaËâ[ì¶ÃõðÁéó]€‹þKïõ86X…ÿy’Í–´0¹÷bÍòN¶.ÜŠwžâ8lâSQYˆ¿M¸Î¦/G…ñ¿<èŠóá¿JæNÅ3Eu輸¨“¥oïIÚ|½Ò‡%¥(½ }#&DW›ä  … ³b)4+AhÈTHÔŸ‘”i‹×ux£´„Þ¸ÈÝ—9ÜÐ5ŸXÿéÇy'B˜ñ8^b^\Ó"¬ô¸€FoÑoÜŠÊI+àÍïNðEæsæô\ÒËßô‡ÉäøQݤ'ðü‡éֿͬÌÏ(Ø—Ÿå™¼ü.HÁxTçN%ÉÁ!pmüù-ƒ)[<`‡™íYøö¾]K/ºÌ§ö9­\‹GôòÛQÐÖ5Â× asüSÜ´D”,Xþ òŸ`øËö`‡nù–F5ï£[Ç(¼ÍŠ–íc½Ã¾°»Ûdá÷ts´ø…ù¢À<ø"BrŒ?Áëá•8Ãé¾]væ«E“Ž£Ø)ôt"0öa"«i› ÿDø‘}`×t(ã[ú=¶=ÁàAEâùü4Ú´EbŸ ZŸ™ƒâ¹b4úÁT\ë<^=™KCg@zBk³·á1ï`+9’às<Y–n+XÃíÂRt¼ÑGçF@¹É|z52—yB§l;fy€ÞX(fÜNf Gƒa{ën’úZ˜êK]A£¿±°YQƒ&܇ÍßD¶¥øK[ëZž¢”ƒ>®€s)Oð¨Ä<踠ï§Ç3÷SX›ùùµw~½oDV¶¬Äåõ†°ÏvãY6Û '‡áœš­Ì“§ßaϼ~†™0"uo«qךs _ö•Âý\Q¢õ1˜)q¤„*¸÷À¬”&Í…[`¶ñ>xHÖLâÀ8ºh¤à¼i"ä} ³¾D× °ïë4êmW ÑŸÙ çØÏrÞ¨X]Œ§Ò.cÓŠ»¨öQ‘ò¿IÀ‰\°·½Ïå•^D¬C¨ºVˆ½²&Ö_15åuÿåF3Moài›rV~î5fPV‚„Þf¸Vï YóõûazQŒcV‹Ù£e§ùÓ–ÉQHV¦ó¦Äý‡±Kmž÷rg>Ú`l¸9œ¼Ì."›¯àëš|NÿRÎaÙöÉ*¨[É*nÁÈè`ÔöVd¹È^ŽÃEÖÞ—öe’—7CFÜI6©7®ÏíD祱“ï-îX´™¾þà·úNá‘}`‘gLËfW2~UxÂZt ˆQE¿gMº4€ejø—þ—§ yEÑè´ñ £bð™å³„D~ÙC“”ˆdÊš,P¤<Ûõào‚Œn£¡­Ÿñ¿ÞU¿ƒáå*#š¡  CáRs Çù^nœNƒ¶:“aý`tË]Éþ‘—±ën츟i({ŒÞ“8¸ü{vžzˆ‘³QÆ{‚t ¡gÓ i¹Â9àÜ %ý1ìéãç¸Â÷ñ±ÁîùöÄ·ò-³b½ R…[o`}þô/Í"áïŠçµ;PxU:¸xšã‡{Æèj lôYŒ·saæ`°×ÑQ8·µâÖÃ;áÎD?bÁ®k› ᙬÿƒHæûÇe´ëF.ScIªw vti1#ÕM ÿù©ÙÇÂm ò³^ÃòùPø¯’=¦ä‰òÎäXp+ôÍ“;w>ƒ¿3Á ¾Ïû"‡É_©iK~rÊË7‘§‡ÄˆÝœ,<$KHÈ÷?Ü{¡ñÌúíULŸêEØ+,…‰ÏÛÁ0R …? Ý¨ ™'¿â^aÞ®~úb<è—Y‰–Ûr`ê…©tø¶ª&]c*ÄQÍÂdõÅ™4g’§m„ŽÇrp/NˆÝ&´˜êø¬ éB±7æW}·þ¶¥´>TáÏýóÈÞk‰°f§.!¤Ú¢-Ç×OÙÙ:#0¸p̼­Ckõ…°äôbÚ(σ'î a€ñKFYc_÷€ì{P;E†È\ëÀKQ3Ñ»î- %‡ã §@T þŒW¨%˜ã ÍræŠÚ|þ,ÎäR¶”³Ž—V” ¿¾4ãzuz#>êϦáW©Œ?ißÊ……üŽtìm#«ýÙ””ØÃMF˜½¤zÒ^­GÞåX±v-÷éËM°úÄCfÎ¥®VÎDáýÉ5nWŸ f{ÏS¸Ó¶Ü|âåM'ér‹Xñ&>¾½ [~2#«Õˆ¸û ˆŸò‡MåÃñÏ`Þ¬ü÷4µ|óÑÚðìÿ³”ÌȵÎyðÃRŽÚw°Þushȱ ¾g y>\Å.ðÙ%«ÇôËN˜5Ž•Äà¹Y™PÕ4öqsÈÔžÍcìóçW‰—Ñc° Ó"gûbpkS&k":“nùó¢î>‡A_òdM|¶ÿ~o®ÝµX+Ò{ ëž?æºl?J .âI÷+ì=Á×ì‚^XvÛÖ… ±Ü›¯àã© ÈÔ‚ ^&ñÙ¨ùWÉôìÒe§ìØ‘Fq¸¢e;ì 7Æ‘QøAë.Aׯ7 ½ÕÓv6ÀxK¢õ‡Ñe¬réSvéÞ6ýÉkvä¸"œý°•uæpNرI[[Y«#¦,ÿìÝ”ˆÛ2áj¡!¼y9ÊhTñmE©»ûfšƒÝ½Xàm =·ª¡õ«2ÐÂÞŸBl,rðÊïζðøãú0HX ÏC³ Ð<Å‹ÇÐÅI¿šÐÅñßo#yxÚv™)gïàªk²øÌXï¯^ƒyfÄC\¨òØÅc~J¶=A/Âx¨ù$»?R€®’M¾-¯Ð@®—½,²ñžRÆJ鈑*õ¨·î. =Þ…eŸ[qÓêætH#˜a‚;?ÚÑ%¿f_©fÆæöUÓB•ŒT‘ )yïˆZ]ˆoÒPøµû" à€Â\ðA„r‹Ó´¼$gV0Ö.Áï¹ ¬Uï7öFô¬›z®7AAñLê(ý’-kp ©|Š4%põP ~©spïQwôr`©FYYyV ¾E6cÏ"öúD½ ÛXy‡ Û4…&Ý`«ya÷JQê½®TÒÒñQãRj¯'Ã^Ÿ’Éò”¤a–s§®ô7¦M9@¿}T!¦KÎã.l‚:ÿ`”ï®ÇÁÙ9 r. ¼¢‡5ƉzÐC/ñ²ÈJÜî 9/º ãÑ'\Ý7Œ‡*ȯWpQÎkæt~2tì~÷ËÁl/!¶ 6† LmEÑ}BÔÑW=6+Ðgs]h“&½g¶‰JŸigw,MÕëèô&Ëê·éÐCøö^’ó9ðšØ1J0;¹Œ>Û¹‡7 &tÅ·, á!¿K®Á“;ت°û ~¿š•@í²KHt}¨\üRÆçÞlê×Q ·‹Ùì¾°ðjóªÛnw–ƒ•™s±ñÒZÒû¤ŒõС»›ƒ½X ¬¸½ílð}ò«§Î_›MsÞYÁ Ó‹Ìy]qŠz.ôŸÓr¥Ao™Í¹¸áÕ™þ†Ýsõ:{è— œðž‹ïŽŸ½œ4ÐpªfÎ7íÀE{1ha=ûåc!+â߯¸9ÀŸß©Lôšðé©dÝ.¢uç|ýX5=WÌštN®ÅHý¦Áü˜÷Û_ Éa:\w’UpÎi+y¶î T7MË`ét¶œÐ÷ÊÌñªEàšÁºÕ_`!ÿ>W{ˆ»@ý8Q¯øÄØ…ç³6ÄéÇ„h&Þ†ïk•å9bèžIÿXç‚s¸IÞ¥KV?{ÔUšì}¿‹¾_•‹Íï¥é¬&PÛšÇêéžA"®hH;ßGãèT+îqU2W‘Ÿò4ÍGާUŸçq· åI"lÙÐÇ­þ…»*ÒA¾~F^}Ũ*Úù,‰l²¢þÜ¥t×®¥dwo252 ¤ zNCEQz°~ ý—àc9Þ˜o£HÛe»Ån±¼¸’ ~N"ùW®Ááâ˸QôÔ‹câÎà'žD/o¼„ÁÂÁèÅ#D‹|òðÉsÊÞ=¶ì¿ÿNÔí„® Yb(Šò³Å„v )é@º\aÊ¿jÓm„ß‘Zí”$çù¸à«[ÇÎ5%ѱöpl›¾›MF—+Pý‡¡þ_à¹fR‘Æp̨æêF ôe%6SÇW^4İž¶‰{Â,‘ξY á™Uht¡‚t¦\dßßÄ­±¿Df~i÷ÿ’«^'螣†TÙЈþ}4ÁÞ:‹%ÎáD`š<^Xõ¹¾&#‰jÌøÜi‡È ÌbÊé*¢Õo'Vÿó›Êžpg²{¿2*8cx6]¡N?ÛÑköÿ‘%>ÿE¡@™Iô7!–xÓy'LÛaB.ÚGÍ¥ § PZ[œ ®{Ë&TΣŸ>ýeÜóähÞ fíIú•ºb¨“ óQ¤W9_Ħ‘Ö6Wúk4”òæÈÀµuÏ· ásezNq•b¼HÕM;êšz‘Û=Ó¢4Ýâ(¬5‚›_ºÑŸs3è…´p*8çȯGå¸p¹l*y1-€.8Þ]ÿ‹í\=„:2Y‡ã±L®öGx›¥!}¬ÎžïÀ7ÈÂú6q"mj<^åhê<‹|íÀÖÇŽ@w²;.™i@x¼‚¼C ,oâ.ŽÓòõðùµ ”ˆf²v°›ù¥¾¶?ÙË,Õv¤>íxà£;Œ5w 7û~³¼ Å» À!9…~›ÆŽ^gyõâàïviòª¯ƒù±¸6{ep×ìk†ÙSùáÜûaœ|Ï”Ùó‘5Z9X8] WÜ©íre¢9«Ù½¸·°د éÁ[²ðºTˆrvØbÀÑBF¥,ÞAS…7CÁÛ·¢ÝïùÏ%ìÙ)ì­ñ;ì¡íOƒî¬Çƒìò©*tY9j­Ú fwm™mV¶´í~9“RÜ‚»-ÅÞ«û /G ,²/aÍõ6\ua'‰ ¥ð¹û1ËîEõ4ŒZ< ’ÏÓÿ‚Vµ5rá2›…XÄç'o˜+6Še×/‚kÕÑ)–¦GÍ{ÃïíóêDP¥¾½Ò‡åá! Ñü”ye8©½å5Y€)0¼ö&|³•¥3÷°¦Âð,3™;~BŸdˆ¾Á¤ï‘l¤Í t(ËL®QßÖoM86ƒkñI¸{ÁÊѻّ“Úò§u#ˆÐ3µ´©êþ]ïŽ{~±‚ª`!î›î[Ò]i ¹U| µœˆ•ÉtÚsO‹ÈΙM¶fL!äÉ}´­6Â+Wüèàá|`¤‹˜€qü±N(oÇÖ'FýY†N± %ÂËÓ8ãƒBä¼Sãß«ˆcä(㙸OoÄíÉÃß óV@«îY–øAÆ4'´Jú‚â|'èoµÇhÚ{•¦6CÇ´)xÓc:=j£‰Ý&FÔaØýÏ?ýâàyXªN2W•ã<˽,߬w V7ˆãƒQ¸²ä>:cN“6–Aþñ¥¸uP䓪ñÈ Mêí¸7ϹBïSé‡îèeE>/ãbU£šÞTd¥bÍØÑCŸpßjÂ2cm(—ÎåàHÜ`~‡qG˜~i&­eåp­v%†í2@•ÖãXüø pÚøÉÆ™¥pèø.æî$žØmÃöê̆î‰T´û¹; ¶Á(‘‡™ýÌ» ?0%F—Zxºø§Ñã)AÀy®‰‡¤‰Hð5æÏnœóp!Ì« EN—$(Mj‰ª~襑K9cË´™¶±U<÷Õ[Ø«Oªl±aQ(Xøknœí„p4xõG¶of ³b±ßŸzhöSŒpO½……¯f“wÞ² ‘‘Ây<ôcÝ/Á>Òt o¤¡ÿŒÚkâj¯å©Çqòêã6{¹ÐæÞt¦ì¹ÎCòtÆöMpL;µ[W€P:Ý43€’j]òù >Ù^WŽkt^ÀÕè^  Mä—å%8þ£æ.6$²1v„g£'}*®ÃÕ>'BÅòÞC ¾=ÓUëJ´8×0÷ÐZ2&s”¬ü3ŸÎ²w‚§Õé™M—¡­Â…äŒwƒpØ%¦»Âˆ, µ!_‚êÉpa?,ZyŽœ%É6»ü翆Ȥ“8k|¼ƒ÷ƒ×£näëþ¿o¸p·0}#°„j]Î…¤Š…d¹ËR >©S_;´y½loZ}¤j}4—‡³ZOŠ<¾ËÃ*5›Ó=Ú Ôäïuv^Ù.øÓ‚ÏúÓLŒÆC°ÈÐ#ý¢³è’£éP®Sˆ«.‚þâð#b¸†Ëß×xôŠêCÃK&˧á/Ó/œg¶Æãè‹töÒÙLXVçÃ)ª%)º ``aɘdáárQ1£Ï>`–ñ7!÷ö)²å¤8½ÑÂzL¤‡å`§²;Y[œGÇ<Â?åÐÖZ1éüçp×£`°Ê=Ï<æJ“O¯FˆMV4GÄþ'óþYèÇ®+0ç#ˆnÍ-¬Šœ ö†ÃËûGáý¹p:š`ÊÞÙµDh&Ñáó{ãz΃[6d.S‚yµG«ÜéLßz.N;€?ÄìP;p'N{ó¯6ÂÁÛ¨3r…—¼@¾Þ/È9Ý#ç#Xá_Z.ñ’‘à²k·.ºÛ×Âó©ieL 3àTK\ΠNOÁhЦ­üÀ.º¢Kµl…¨Ã¡"LŠ»2ù\£¿þÚWÅyè-Ö ·(Úýº‡¯Ó¤èƒ¨rÎü rö–³’Ó†wŽ39-ixâÍ[ޝàz+á'nƒ×ÀÌd=.EÜÖ­¡Æ4í­²ÀšGò¶39wv¡Tš»dQþ1ã…¹šï@1¿‡Û×ËQäqdG¯5CbjLç³Kl BYÉ÷0<­ŸÉlôýÿ±….˜ð‚B|K òœ;kž'BAˆ5̽ÂÜ ¦•©O˜ØyZL©Âtø RÌzù¾‘oSˆKD#ÌåÃþåÜç?$é¦^tR±ÄfK–Ô¶2µùièAÜ3w!Ê;€Ó¡fVÆE4Ÿê ƒYpâT“»ä Üæ±>¡[(Èpž°›´ÐwUËq¼è5ÎMß‚vG“ØÃ >¢vþZ&™Ù¥WØm›jÙþÇIØk'I4Íç“íÛ|àÝÍ›°Ö8ž|Út†šnkÁ¡#puú®À9 ß`ÚÜŒÑoebß`Bô}G‘×õ tιw*`6£@Ïÿ2Ľ)r´$— æ±î|áÈžkÍÏâã\%ZöÞ™þz¡ˆ7öÁÙÃj(«ø\b[Ùð­ÿئÆiàùU7-#Ll"!ßf‰5-ôÃèzl)È…Þ±­¬h~l—IBv§Ù^s¥ó€¶òªÐþ‚ˆ{œ-˜Í¶Ø+À£_D{ÙԆຨ>§Dޏ_BoOµ†wOBZ4> Ë96\þh· ±Š‘êý°{«é<ñ™¢%$ÝóüyÝÄz/Ilr'ÙFñ½ÚüVŸNãÝ è¶ì¥¤#Nƒ-’¥Ç+¶ÓÝÛd¨ê´½œËËFQþ’6y÷»MÆdècÙ©Dvä‡Xc ;Ýÿ:ërp)ÞæŸÍ+uã·÷e $ÇtLð“î_ÀQ}ÍPf+Þ: ¶ãX¡8Œ‰úÈù>FB~b-çÒ~~’~Æ‹\Nñf[v Ð"ýzv¸` 9°j3é°NæìßñËúà­)䰕ЏŠÃžPYâL+æ0Åþ¶`zí ¼©ÝG*ß9’Ú¼«pªg&Í¿RCÛs¢è–8w8”¶º‹§Ò¼ÏíxkhñÍ€ U´¬x‡l¿’J5s°ˆCÇ%Ñ~A}²Õþ,üçiî5œýß1’wYšhÞ9ÅÙò‘CgvÅÁåD5úðW:­ ›JÜm`çB3òâT(½7FÞåÍ„µñpÁ0œÖmžOüxQÊù£k˜ÇXLI¤vç§Áê21xknHÔ¿<ä|ŽXC4-ñ<Ð{C¶¬¸º ýÏmU|™þ¾UM™Špò6FÖè90»ãö’þ¶4¨$¹4ìíö›„Èû½ïþÓ4<ßDNS§Ó9D¥itÛÂÀoC8uY8…ÄLdÒ;þ‘($-J×iêQSá8Ø:ë £¶!‰® 0ÅãÂÁB=bù]…*Ž+€ø-ÒaFÏ’­¦b(ÜgIä¬tˆ--„#&ObÿÅÏG0Q1sN*{;`¬“ Ïfa}öÚ±f Ýxv))´gNÎ}NÙ­Dµs 8óƒIZL&ã&GV úàÀgmÒ²¤ ¥·TOÖ³kòÖQUžaÐ]d“×B¬D-Èà{e²ó‹.Ó1­SŽ"y³éþ¦sx»®Wÿ±ÀÆ›ADzN#g(±7ÊN'+ƒ¦KíÊ6yºEÜ„xIZ½O‰T.¸Ž‡ƒ‹Ñh¤…M4¸ %x™ÑɸZ÷>áƒ[£™«Io{„!µB4:¤Œãsu=åõd9#ìÀýïìdöáó)TíÛiúóÜS\;f…ëdçBÇD1ܨk¥¾XA×;f±vÂøÆ$pž:ùÚÞbB|<ó‡šB6çî¹LÔï>düÖ²:>ÎБr„ôÍv™ärdäŸ/›88… '›Ð÷çèˆàMjX NÏ$¶É®$΂yÞÚ0z)“9¶{¼Yw›ìyƒSb±•3ObA?ÕtèãÀµh¿Ôˆ¿?ÌXß­ÆÃ'N‚Ëžl4V^†µÚlÂaxþBŽ,Bk­pîÇØ¯LÃ÷|˜«ÁÒ¨—ìY'/ô:¿ ÂïÀú¾ZøäÏ65‚\ל¾L”~×=DÆrëÙØã3É… z¸kÑ66aβt·5Õ¢h(‰I/žãúLo‹áAbÂ$o½;ÌhûÀnÙñUD¨[Âh;/‹ì–™èî5›îvBL¸U̹6\ ßÎ+û°'< ò…jÑ3P9S„9(…£gPf–'¬”Jµvœ`mùúñøŠ_œwC¡ÀPˆ–FØÑŒÑÛ´#v èHf¦Î+gžI»vI:ß,[Û0;'Š«ÃC]m5Éá›Nlà’9àúá”MM#K~T±ÿåHWÑq¼°Õægñi®²fÎö‰¡šÖRÏ*òwð +{3“D|Ú@dÃ$¨—‚í{=nÉ.ÆAÓíDÕô:LÖ¢Ú[†C•eˆøûgpwê3¬-â!Úaì@ë5¼dƒãŠ´k[ɳzLïeØÒɵÀ—Ï‘”£7‰™¼ypFÿ,Vjày²Ð¯Rz о‚œö/&¼aSÈ;“•$KLŒÚkûõ+ef¬fäQƒìD”||†x.æ4k%Á‹lMåòÜ]8Óû53Æ;ȪÌ1!ñÒÞXâ#Fí8’ë ±dëÓÏxçÈ8tÏ´€C15Œþßhöa•<¹Ö'Yû~~—Éß<…þÖiàûw“mÐ K«È¶½¯AÌí2žï>îO]ünpFB„ˆ§ãQògG1Õs’ N΂¤ÃØZ|?Bìšìh²>]ì<…–älxa« ~‡NÃ&ß`òûÚI2x|Y£fMòðJíVÒâÎg½ u,¦r©‚¸Âb'ùWT=èµ°p¢ä— í â©®O:}§ÿöUÿc4öc”ªcñpŒ qŸÄr‡›W 8Ë|`âÌÊñœëSSTÂîsb¨EÄ2ÎLÖÙ&U„ŸXéÕ0[»³As3_ÅøiNþ öÉgæ›"<ÖQÂâA^Z2(ІzÀ[x¦4I‹ÕBô“ÎO›ÝÌed„ô Há9˜¤>‡E®¹’>ÑO: q²{™ÛOŸà–‹5àÔ¹˜ì¿€ì™æ‹¸|x‚Õ{jH$6KQ‰½'ÑésôÂnˆÓ àhÄmJaɶ1`w¨ÂzþÃèökrE.¡ÞºD¶5TŠª°qÄGÒ‰<]9…&eÀ–WÉЮºc”âÅEü¤ÙúžÏ ´í°Þþ.S»O™îù¹ ž·"ýÎf&t±òyÉšžæ•Ü<¢+ž +:“†2 Òõ8™]o¿Ù‹sp6³ÿÕ¢s üç‹æ¼2¼ç²“;W’úOêD¸t4›ðÁîÃUXðLþýùÇ(§ 2]Â]x~Oýµ|S·Ÿª›Àeia2m­$–CÙŸ«`ðP‘Éøˆ‹È’ ñ4jÝU²Ã{ë[ŠÞÙÎlá§gPe…Ý«ÁHÄmÂàŠtÄ7½“¾õ8Ë=rÎBÍH$ó½†YÛ/ãÅppË#×O¼ÄŽXEj< …⺟?†3ôA„fcôT}hýº•õ™êGäÈ¿ÅYÌh×MH¿¬E -êþÙ¢@ô,Nõ¡­ŸµÈÚÂY- ný(lïËJ6þÁÀ½·ñ\»'²ÿIU{"T¤.a˜ð:úlà*5y7 ›†‚éÂ4#ú†ÝNRžó"gvÏøLñzÖS°$þž†'o€®I?g{:µ~~ƒ]X·ŠÔðÈÐ|–î¾Aÿ,†Y3¦ü¿æÔºlZ84©CÒHrï\*•J3»cÁ¬•¯_‚›û^± -SqÙé/äÏí{Ð$ªM/|4¦5 l©ÀX6][¦ I²±lN¯-)ôf¾z€LÑFz,%˜Ò…¤þrÌÝr½¹y)):Œ=u7±pÃܸ…ö$P-›5À9)Íü»‚lçÌL݋ێU‚ç]1òa"•ÛÄYN.޼€¸¢${†ÙmƼ̭ {¯eÓHñC¤°HrB‚ŒM04l°#ê‡Vót9{1{QK,Oü…ï½ßñ¸þ,¼W“<Ícèçã…óËɱ¦´z8Ž©Œ#YÆÛÈΩðn 9tæÏ _jòñÚH V¬dð³Œa|ƒá¢$8ºM†ü:åF~ôܱIz?Ã@'Êõ9û)ãhMš§‹‘ˆ¸Pœ€ŒÑå Èk>›RMÙŽZsrl‘ jÙD’ƒ‚rdÚçOLÌü¾u€uð®ç*÷¬ë…x8> Ì ïú †,.Cs9ü–}úŸ52ÚÖžÌ|‰sðö—›—‡ŒÏùØu¡è:6‹L—Ã×¹u°W²^Þu¥5 °jö\ô0ˆ^?K?û (­_‹ƒç6QË‹PbI$t^3J~V¶ç‚´ÜK½´Ñ›0qd×Ð%ìÅþÝm æ‘X9±ŒˆjpHÙÀq"orU>Mr@ædk¸“J©$j%û #ï. ¾†xÖþqü; ç]é‘({™åô£Ò+›‘5«pA)\j×…´'§YÁKçÈŠh)zžo1~¾À¾ÉÛkË@ã!nº†Ë|LÑšëä‹(Sqï\N»ÍÈ,Ê“àr@5¶q™í}‡úµT.úÓ›ý O<†`¾­D,&š.û± >ñva~Ãîã]Ä/Z‘&Ô•t¢ûWX|e6ûü}%ìÙr‚¶_aO~âЦm3É’\²á¹$U[ÿži¶iáž„C¬cò ƒ½.kÐt¨†¹-á GÖð¡ÛŒ¸sµþ>Ô")÷DÈŽ9¡¾øò¨&YRɳomȉ+Cæ@lµÓ¢K£Å‰¾Y|bAãõì¦U»isߨJ2£?-ÀÞ/çñOÃEÎË nˆ`fn5 _ iÌ×òbd£%ŒÞ1Åþ‘döþÔÛ¾Ÿ¹½Ä©…Oð]åzçNlÛó”ÙU^‰üm•´6+ î:ð2·éFº}TˆÚÚÅq ¨9^…ûB ¹7v¯û&MúÒx U¯ËÃ[õ_ðÜýTÜ>õxl!ôåYÊ|?ÖͤìT"ÕžÏP¬4Œ¥qSþ.àD}š¿yqÿÍ nDz=œm>ð ¶ÉÑúAÔÃÊ›»vø+wUÕÜ6­˜# ?`¦“ pŒ¦bÍRCBÌti©Ž/|È g÷eÓÛ/ÄðÜ"¦ùÞº8_&¨¦aœˆ)­í N3 ËúäDބй‘J»â•º#dÚP7óøô<úöœ#4¬ˆg–Éd‚Ín=º¦:[ˇ¸B•Æ(¡ý s7œÂër!ƶìÃu¨ùPäÆïa>ÿ¬Æäï+2ŒiÒ¦ïp š²‡ù¸ÐÎ~Çp»Ì´ ¡«h!¡0Q^ÅÝïÿ•ãµè!g g3]ÿàœÏ®tý ^Ê|Ú˦ª³0Õí2›½b;VfçÃm—›V¥%®Ð·F™V¬ñ"Kç—ãÅìG±Y$¬ 몡鳧`S ?Q›lãdÑnuy¯•‚óØê“:¤<6Š5¬+b7Þ÷`ݧ頨|—ÌÏÆ+R*h`l‚ß•ÐöïSæ ®9é¯`œ…סs«*®lwÄÙ³1#þ·Ï¼ÊFcK-þÕ{†ã¸ÿe.{v®ÃŸá $hzÊþƒðëÌîIaÙ¸åN)’G…³è 5aàÊÜÅWA‹¡pV2>ÿ÷•ã4oˆ[<(F# pÒ¹S!œ-CÇÔm3…ly£Jêw”k‚Ú ÐX"‡^Û$ xcl•¡(4jX\Àâ껨WÅ@ËÊX8bñ–>è Fóô™†lȽ6¼]7À¶¸£xó 4úš–0Î_I‚Ša(Ú %gÉbÕÎ$vú#qZ¦©ñ]µ0Çü5Çô{ŠÄ-À_¿–ƒúk!êùÔ7àõ9Nt¾„ ®ïv±Qh¼‚SY‰öëø¯÷)ü‰5€ ût(h*1Š"¡ÌBïöJþbªÉ:ày-NKQ‰ÚǽÍÈí{ßÏX1ß]c¼'Ḭ̂;׸aÐÛ#BçÎÄu‰c(" ’Yâto¸ãØË„í} WâÝàå.šºùÇKº¡jÕ?¬*Ée›ßçÇég8<㮳†¯ëøMH:)ߺç‘GàgÈêÞ‚.»às¯z¦éÝjˆXC¿mIÅ›Ñì©·ëUÈ3“XS1íž÷á“”¬{–7¢!ÄQ’Jg¡ÉbØ5[Vö›BÀ/?öÀ›j¼îãBöµ:£¦Å[ðЍdDDöCÐüÈñrÄ#bÙ·þ±8——´Ÿè„íªS±nñ&:ßä:(½œ‹µ×Àt¦=<²øÉö–߃St¬÷ûÊ’ºs™Ñ/Gå­Ç™æR/jîú ÿÉfJ)öîOXóàÚnó Ò{äAÿEH™Ñ/¼øþñj*U¶{RÎ'iÛcÀ¶u7~?ù–­ç#zòs±·Ÿ—ö~|Å^­#©ÁÅøìÇškÿ oKÜbRxˆxìbªaß*³Ñàµ5Ù»ê)çxÙeœ0²Ã“)[à/ßCìþ…Ú?Û˜Jbô(ÿuVø],ˆÝ]…bèf²›°êѶky6®w܃¢ó!åIþV<ÇŒ¿ºÎÌô·g7=ã>ŽC° ñ¨Š‡‹¥!bÃGìÙÁþVÔ…>ßzvgî°˜¿‹ô_³»¤‡ÙÍ鯙ö—²dÝßR”`Ú]}Y¥èzXàÓÌ|pù‹WFc¨QM\•‚´ó&¹ØLÌá§8¬ÄŠ_ Â~ï\ûãT|?‰žsY¿OÌK×a¦kµ6kbsã®)ÓM0'Íëöæ,äÉ‚‰™xJPô/å²ÙÉ0ÀÑ¿÷\ÁyIxãV!ìË:ˆ’›xè«‘‡˜—,OTÐ3á .<Íùs>™}qÍŽˆªS½§÷ÑÚÒ‰ñ;›£2j]aeJ>‹¾ ='^!«{ž;]J˜¼rOÅ)å±Xýxìâ±,ÀÓÉ?áîÜ&ŽAön›MBÏ à¦3¹÷Å ö‡µ`ùXŠÔ#üÙ=+p†ë4½Š^±¦d!(“·BûA²RìÄßù‰'©þVurb™:ôm-e¬·ÎF-µ*ÆCá±æLè”Le—¯¹w¼Åqwà(<ë·§ù¾Ã¬ è“–®z©6_ 0£X’66£SÆ[öô‡G8#R ç>QÅc´ò×*Ÿ×Ü ²TæxU1 ¸˜0/rx¿á­[Qà2œD­'gÂ`b-tn1¢ê{«ÀxM&ˆ UÂq¥bÛ¨j¦;j«äè°„W^ÃNåhå9ZýCL–\ÃÖ’¶ñ{7{ñZIÎýÍÓ bš;-{#‘/#òGXç«¥Ì÷Ê4µ`Þ,£ªQ>ÔMÉ…D®#µË›Ù5ÒuöMX+líYßå€Ñ÷rXêDùŸãŒcØ=Ø|P„h¥i“Íz¯à/[‹}&‘tÇë$ìé&K^ª3ã ܨ Å•KÖ“GGƒP*ù1Šg]A÷°ók"ö ™¶ÕŽH :3¸ùS3¯mhy†‡ƒÂ6wMÖÙ7-ÀÎÏâðx:•‘tµ3« ìn=›Ø‰{³ñ@o6Ý郳q~4çä}N`³?]šoAO:V€ê¡!æ¸yÃUþ†BÛ¼ C=ý8@›±K„¸8þÄú?6KmŸpÅL}±`HµOõ™ÍÜ©žþ¸ âV”€˜û>hç˜îE^~b/…þ§ÂXÏ…oàN×?*ó€ws•`dÏ+–¼z{œ¨ÖÕu“:@”a\apýy4-„‹/?2KŒ¬XÞâ Fî¦4-Þ çæ•à‡-•xzöu881—ïe:;U¡é].ÎÏ0Sñš¡<¾ËJÃCh¶È¼‹¦RqçqvIŒ%øç²×Åf°æ»_¢ÐÓGÌ·2eº:“‡¬õÓ¡W[ÅQ\öˆÎÏÂGl™Å ¯pü±dÍ>zÁÞNtuÛ r"¬Û©Vœ6E¹=Æ›æd+? ºÕŒúÓéÚ‡±|ÏÅ>CÉŸ&´–‘#é¯"0zH[¿”A³P üÊÁ>‚°"q•,%iÅ9Ü×P«‡&uµé"ˆp±\„…ºÔÖu5Uy‰ŠÃ:vµÓ\vbßÚ\Æ×ï^R}ÂÎm®…Å—pzVÔ8ždV.;HË?áûuò(šÙÀ¾ˆŸ]É£ŸëaÖÈr]ï¿ìg&bYšFê_æsÉ4=!ºpi$FJlB»•‹ÑMkiù¨Bg™Ñ·yñò2q²>Ýl%¡ø’÷:ïCp—ÎĬW ¶õ<)äc\?{Òƒ!þ1ÉÃ÷®¤k×ú>Nš”¬ÜƒmiPCz i¿ñFÆ:17á,W¼Ë䬺™_gѶO*˜Ô87vÝ„}#äSmM—N׃gïrã©¥$[ê.Œ?5 "2ÑÐpÕTž Þ-ÒsCš|ùÊ…„+ϰ¶v7Ë*Hó•ï ¡ÂE°¿~Ÿ±;ü :<Å—ba<Ý d_¨AÙF!¦l°7L÷¢>jß°ßpçµÃ,øq 4È’eüÉÈ_Õæ‰¸#÷÷4®&O»ú®csf éƒE(©i@ſtìH{4ÿÄ~iøro7à±0 ðýÂT=ÈÇgIa?ç›ñ¾¦‰V‚p´zXŠÒýæ`•‚œòbÇ>æB´0ñh¹Èvû›’ª[–‘Ž¥ø¥9 |žzƒoš(±àakV²h\ýŽ=½±ž-ú³<7ø`wж~O)4ÕGâj³nh“ï‚ý56½Ê»Àâ€cô3)÷ŠÂÖ–¯œ>¹|NȆ1®È ýh쀤ÃEÿ†ê±Õ§çsÕLÖßBüºã'lÛG¼^DÁÂüeøŒeÝWQ&u?Â…é½ i©Ï®²™~:„çÙU½5\ý:~¦AMl©ç‰û0D~Ú| ï3…ü[ ìXU5HšíýsH´U'œ,Rà=?FÃÈù}ºL[Ï•7ðf24MAç„ô칉 (…B´úåH‘ƒßòô‘b8>1_nìTZu± Â5•PN›ÂwIk¢ÙÊí&rMêø¶Q‰tÆ^`–“9T~à:³+È’=Æ>Çïo¯²©|`þ>¼ãþ|šXè~êŒás3à­ƒ»‚êØ¶2Yš}±„i(÷¡_# ‰@‹í]΄ŒËNžƒÓn»³ÿÁØÞ ôÜð zËã]`3O6H®,ÆŠkô¡âòÙy5º·8³’š2d»² ¶(ÅUœ8ƒÏ¯ÄO²‹qµ¥•Ù¶šóógwëÀ6¬ãÈȸÏñ›ùñC#>4£¯Õl²{†1}a&Ê&÷…€îÂdMÙr>ÓwU À}¦"ú>±f#—ƒ€Ä!¶¥‚‡ú|Ë¥Oß‚lR*È[bÓ©A¢SûqZñITK ÄwZ|D ð/n]hB„ß²“}èKkmZ2É–¸ƒGB¥hzVl˜³ƒ‘ñÎb¦ÉÐ8³KPz_lW0`¯kÕ³.X‰c\mª:ó·wÁÐî 8OU”Ä¿~Zg­áâðRTì#s^KÓ„¶3PUÓ×ð´iŠÈ)8Q( ébÌÕáqèÚ£ñ+N`ÞB>ˆØ½˜9bÔ Ô2Îæ·Ú Mº€QÀ]kÖwc‰MíìMTÚ±^ã nÐ멨Õ.NL;Œå›ÀÌÅÒ¢ØÕ»WÀ½­ÖäÆ^\{.‚¾µá…­'f‘š!Vó€9–̨Cžz¸5Í”Š·Ýƒ{ûãǺ !C{4Úÿ»&´×»ƒ©kïî•»ˆÒÂsøPó+û7t&íî7aÄbmAÿµ[mYŽÍJ_¾-Øùñ.»áÞš?ÜCf%´¤°*W=c¶s»á÷ªX·‘Á.[bòÞ…ЖÑpö€å8¦¸Ñ$­8ª`O7:Þxb¼ ·î„Ñâ<ó‚«š0«Cfzª3±Aý œØÄʪlcžˆFQ¥é«q¹« vÆuÂ\Wo"êýþ}ò„7“ܤiT„ ì xáû’ó²o!Yâ̹B¡DGe5Îzs› ›^° 8uïó0çˆ$ˆ½Pட.ά®0‚qo±Óòˆ873 Ÿ±Í£‘Ðÿ{àëCø¯Çž•à÷o¬Ñ´€þUjØwà/¬Ó“é—ÌHHà fžÎu”lZáq2tñ-/òlÉ5à3 †+„ï×8W2ŨæÌS¬ïÎ"t‹»Ê9 Ã=ÍÌÙ±LÜ6÷\”ü/—ÄîÂ/…ÂÄ|ÁÖªp®ìŸÿDñmÖ´¯/‡ý²v dú”½$ì<¯–Ðû9”}ƒ-YÌhµ:$q è‡6 |×4È@€s£ç{Æd3™)üžjì¾±d&=~ádqUI·¸Õt¾ƒf¯•@0á7†Ó¨Õ°æ¥ÑŸ>çþ8NŽÖ£¦U¸Ôå(^lD»îÌ?Íð9ÉDçhcUÊ)zaü"l5§GŽ-ú§ÒðÃEŽ–F©Ñµ±¸qŠ59µs=›»¯ï…}=‰S®\ ÇÒc„/$ êêz™ºÆk$kF3ö,¶¡unI4®7É?äâ×E Çë0µŸÃxH?Å[˧Ñ?4©êÒ´ý%$ÍH™.ô]OÆy:± þQŠhå¶£¨(øÄ Ÿv Ð?’l‚\A7³1üj¬¼°sƒç³û2'ãíÏ›à–mžK•„E¨ ¯*áê*­$ºu`YÍ)Áˆpö…¹>uì@F‘Â~Ó™$ØÐ¬JÜ·Z¾ßêFjÄ‘‡ø ÂRwh6Ψ:CÖ¿+œØT3œï¯·ãü(9¡‹ò—±«PŠäuœ¹<àW#H#—¯çÁk³©xá˜Þ ù€¤0ÊVãÑó-íö“ýë17yˆãñóðOó-pË á¼‚+]¿f‘2ð!âG8¤-Èœ q`ûXlïY ^²Ò¨> §Êqëk}ú3þ%g¥ùKΦ<¬ÝT¿ÝÝ„f§1;z•‰ûI ˜ò]õÊù#-ìÞq†®¨F% 쉉BÚuHQL‹­²½†¢Wö²+ ,™ê-º4­€zÙ‚ò<½Íºµ—¢$Ï7öÜ<>öíÃ:”xcÆô´1KnÍç¨0A&v.{•ߦÿUdʘM¸ê]«lÈÙm)LŸGõõ‹VDÙ¼üòG˜›5À{ä)Š´ÚÁ»¬‹ø§»ÿ^à¥)µp¯å>įÚ@ çÁSÊô‰b$Z¶dZŠÑ™aìëÑnØŸÀ»×žÅ­o~±V%xÔçdôG3Ï`¼gõOðRæ(. R¦IœÃ¸œ4â÷7{ð±ÞäqR@-Y>zîËB|Ú—ÍèT9²§üOc‹¸3nU¢ûÎÂp[Ì|1Áœ¬SŸë9t`ø+»æ‚a·q5'õÖ‡û1ð-w„Ùk<…„¸ì¤ëåabD"Êf¶Ã­ÁN¦ÿp,³ÿå|¯mçþn"+,ü™u¬ Ì8ù˜#+¬K$öþç¯f¥‚±‘G‰¾ç“ˆ/£à—VKjçÓ–ÀÝ8ÔHhô׿èP,:‰=M‹é—õ%0ç‰}]“þ–ÿç—Fß*÷)ò׫€¨8)¹?ü÷fÑ_8D?R…ùî;‡ì ¿×1Ù?\Á=yy–Çâæà³.Ð|øÚŰòÕ3¶ºf îô?A<Œ³3õáŒóòPÔû»5£b¦9‘ôÓb2@>‹ËœÍÊe+[¯"s_œÞ¼ÏKŸ¹6T«é5Ëö¨Ð7×™CÎY֛羵Ix­»ØÆv̆ÞYFÕÒô¿Læ«/b ÍIœQž 3ûv2-mA¡|5r^b„O">—&#œLÔ.›‹«”æÒ-Ë7±åÝË q´UÛŽAz‚gƒzû_~µlÕ>•HG+%6=V‰9µ9ø“·Òû éU΃±eÚ6_eÜ:U‚„ì¨d¾ù®‚“‡gÞ#¡³KÞZ«“xµ;¾ô"챉àÖ6'É®nô?qÊí«ìðºBŽÑ°œ¾´¬:éÛËI´·?wºí$•úÆh´ôgšLI Ú Ožÿ–<(¨*Â\ƒ>Æ!y/]´R—©Ú–Éü—áìzE‘V6¢â‚;ášô Ö¯Ö~ñò“}/ÞAå Wº ƒCÃóÓÞÓk1~ï6¬²õ…¹·L!4GfÓb¬Øñ ×ÃûSj'?“þ _{-HPz+ÌÒ¥cRiØø-~ !×ï_bÈе¿[0R:® –¤ËV‚ð²¼E>˜ÛŸö SY'¢\®‡FowÀ'h°ÁŸlÊvæÏDãÃJ›3À¾c9]Þ,ÎafíV€åJ¡ §|ŠfÌÁ¢ZQº§ã eHb²¼No,Gm/#¸Ô‚Þ­`Ç+('Fe’ ö/ÑNøƒ÷›KÙ§óàÁ=²ðY7f(üM÷­*t‹Ô2žú™1Ç¡ßç0"» ¾ÙEBÄ %bó•%+aQ(Ä÷Ùá L6üçy–zÖã]vÌþ”töê’$VPo*u‘ëfMåèÖÔ…DI¡ªÿz€ÝÊxŠF­Õ,7i‰>u 8½fí~la—}cË+PM.J¦JÑ@×3ÔÛ I4´¡2%þøä ‡Þ)óeÖJ³û× ‚£a*õß8³9áj­ ¸Ü²: ÀTå4}}¯—Y§õŒw±·êšSA6¦¢³÷]nE\øÈ½®Šôù.aªP$J^láEÇ-®¸íwGƒ½årò´Ù5g‘´N‡+²‘4s™ ˆ9Tà‹Uð¦ùÒ¹Ôtú»9°^³ »Ã‰•ü7&JQ ï:¼¦½ççßH tñºŽý§mh¹‹'9)Ú ·h(nìFËÇ&ðë¦'Û·Ò’÷æcïÙ .ćåk àecÐÓLÃFÜyIˆÆ›(ánž;L·†)öï6‚}§õ¹q‰Ç½»†P/<ÀTÝÛÈý{¿ïÅ›ÇEðN¡];…¨—GGps¯ˆù °3™‡îö§ŸFÖ³ó—­cŸkâüÎ ÃÛI ÆçøèÖ—@÷è’-]8cz*( =…ÞÆXˆW{Âh{¨ÐZyz›ç§¦©WŸÇaøy§ Áòø<ñæNã‡Wu¾øwÆr44Â(™IþvY„,XzE§Í=(»“\Ø÷Ÿ¿2­Ó@îÖL¨úˆk£o¢žæRb¦¶x¦6#M^DÃΑ93…ÉzT¥‹W.‡k^ÂÑç2 ¾Ï‚.ôÛà kÉV~wgu¢uÅ=ÜÆÌÒoBæÕêØ£Že]ø’/„“]j†ÚÎàVv€Þ¶ŸB›´.Sm7úÌCÿËy^&0‡®³º‡¯áF¯/Be7Iò_6õËÝX7c uŸÓœK‹ÉF¢FôvUâP×ovàS\P]'hcŒ c(Kç¼ñ¤ë•& câÒ]°Pë1Ȧb”» fý~Â|⟒¿½¸y¼Pzë3d}°àö¦tc‹—9 ¼¡ ¯ÕÁù~4žÜÏz|þ„ ö0†£SÇ b³+ª¼ZDçÌÙÏŒ{ºÐö9N(Ûõ¾$jÓË£?@*wò~ªÑCõRtâA įVFõÏAð¿¹ï ;M „‡ ¿Û V±,j¦ìc¥ŽŠ“¾(и~ׯ׸#–.çÆFš–ìjöb¿2Lßó›Ù¯€TZŽ´½¿ŒÇ° ?W$€ê´‘W±L”aâ &Dí4eôô̱¨-mbæ2¿¦©ÒEwkY?yzx¯6õÓwd³vøÃÕçh4~ Bï»r ¤øHí‘!1r¶tUì oÊÿjszíÇa†¿ÃJm¡l“*þ™[Dî>Âm~Ïq»º-‰=Æ…2–'ÜØâÞmB?m¹Kv¶h’µ›}iá=Î)þtþßÍ4_¯—+¯]ˆ1O˜,{)NO‚ =>Œ}#™+¶ŽT8n¢æ××’êÊn´²g8çfQ`J’×i·ÈNL~ ï/àTkkb_užOα¥rp¹õz!žÊ?Fº?àïåIŒF\'â¢1]íý§·Î¢‡T‚©CA&W6g;ûP^Š®j äÁùr|äÆì֜؛ÒaÙEôï|¶ U9ÛR·ÙdG\•CŠd©ç’sÄd•}ár¬J}ˆÚ›N €ÉÙSi:ÔÂO „Òì’ zÝz!ñÌõÇc‹‚Mµ$é6¶ ­ÕСF²I$ßÉ^ÍüýϾpwl©C¯ã˜'2„>Ïúàî3 *9´“(Úr0*—LTw`ì’@*|¡øžX…ÒjIô†ñ z$nDÍã¨$ÓÀ}QK®” ÿH‘¬Í¡$'[ž{‰ Ü¼käöyúîzý|äù– ÚoDHûKeº¤’‡žT³Ábÿ'\žÕ!dü`=q‘"™üè½}Qøâi/óô¼5^ù™)0.fÓÓLIˆ’ÑÞÁÆi¸²z&3éFµ!8ÿ’«–õ+z~ž2l9gSèú{Ï$ƒÚ(Öÿ?õÜ"Ó¨FÂM¨_RÂzŸ0¥ÆVSÉPS9œVO‚S¡§ˆû^uúub+:íz5~^«Ùl³y8w<ÅR9¶½ðBî1ð¼–Dó.cu·\aÿ<Ц£§¥Èû[7޹ uÅ-ØaùæŒ37Ò4àZm(©Ð“' ‡\0A£‰Tû§9õt%“xÒæðž(Ý4dæÎ$-ÝŽÐ-;›n’$³³Qîúu,~ …kˇÙùíITøê\´J“Eåú\8Ë n††6õ ÄËd-Ø>?‡·Ü{¨ö÷²;žI€×Îudo‡+¸UH’œSÃ÷ y¦¼@åŠxÀê<ãœÙ=g˜wj×Ùè©ôŠh‰v{ßî„ gB>ÂbW[t|ýâ 6ä‰W˜`|‡sm¸SmeIä‡@j›h ‘¶¿àMyÝQõå–Á4O-Ú|âž4&^c²¤Ö×NŠeÑ'± ¸lô¤)pqø¼±Ê³ÂÓÓÌè~²‚r|.±<Ödz팺yáã×çÑ=ás·ç3ì{3€eU’ìqsäŸü-/YÌ&Mcìo,@ §Åde’I´÷b’ÞT[³7ìBUG{°Ž?ôò,p ÷6{y6?¿sÜÔ‰Ç5RüùK2n¿£¨“¢ýcª5~1ÜÍie4ho<ÚÏl5ÎæIÝ;×Sð<Ë‹&9ô1%”î7£ Òtí¾vîHÉm°Ð^ñúÚ£ì»m5èz1‹ \ÔŽ G«PDâ kdÆO¾ç·— WèÊŽ¯Á%•«éïió»ß‘˜y–›xý(}(;†Å§_s™ýqÐìÿ 4¦±öÏ%¹>"ÚÛatÎGïey ÖÐçÝXPq“±Ùó‡ ßSËš(ë:‡´-k ®kŽP굄N,€·ýN4÷ìbx?¥+ÕlK:èåµ@ޝ1 u‚Øl0Ê‘9èN±hÒlOÛwÈbV÷6ú9O]vv÷3ƒŠ–)´{¶\ÒÊîjžÀéG^È“ý$pä;¶ïq!m*ð:ÿc3;‰ºVVccÒ|íð»XÌpÛ z£º¤Î3 µ¬Á´zdšÎVb¼Øc{5gí ã÷2ϧÚ1‡Ï¾B“µäëò…N¼ õ–ÇŠÚ6ÐÝ»¼å%释Ù4µä:>Ñ&&>FíiC› J}ßÅdœá®ÞsŸ«ÈW•ÚYËÜ…ÄÆQƒ¬ýQ<òJTDND‘$»«Ô¡ED憣Ϧ›ï)ŒÜú *°Eî̽ u¶Ý¨?õ%¤„o˜ä„©xœw ÜnÑÁÏX &â³Èœ»¢”Ÿ·^Ýù†ÓÚβ³#éÇ϶ÔZû=·iÍ&p?Ç8Ž¢QKÖéßQ¼3¸ô…ãp`ù*®t‡ý$f‹>gG(»= ¥-_Ài‰Ä3:äÜB£¡ ض4/.g%^f£ ïFbÞú¾­ÛDø“¬Pál;?A”±›3^$&A(ÉœÄýEð¹5§—àØ/&÷å°J©ÄJu3ò4ÞŠ¾å~eꜶ/f¼4};Q‡ ¾^øYj+uú(L‡Ï”áÕ­'¹¢ý« 'aC¶*à’³¡`°·7f·Â‹^ ,íR%ùŠM ©>ÊŠ~Ò†˜´hZú!æþ FA—Eµà8¨•ŠÒ’÷å´ÿ¶³ÕÏ™TŸ${L`ôWxdµ2šMÝþ5ƒÓ¶áz-nù<•ŠæO¥K¯ÆÂž®øNb3-ÐgWݳ$^+´È¬=èÕ½Ñ {W,7¹!vÏÂüõv)hÕGGÍ”­¨à6 Ìþ„ÏÜñìFèq™†¿¿ª’/ÂØëÍåŽå¡‹…Ê^lÀPó¬a|š/øF):䢕=nØšŽòËÞÁñ/áÇrA²^wêIV²ªfÑ"°9Ô}y%‰×¦ 0Ð&«ö€Ö‹,òÀT‚ðº=‚Ný°ò÷A†ö/A&Z—³¸[×9€W `(¹€FÄm¹Ëk83|dpñ¦w°Œç5þÈ0"Ó´…ÉÓ¾K˜r ×]ï‹®Œón_bl ͯ à• =£(87&`$½›)yî‚wžõ°¾¿Ô(ØDcÓªàÀþÅ%_íÚMŒ5uÛ½—˜ë‘)±¼òD–ÈCäÑ`âum¼á[îráAõ虡†ýcðC ©Ùwתa36?.’ÑtQaßòpør¯*Uà‡¢:س8wdÃÙÍÖä5º¦Šbá…QV°½ëB܈Qª mî݉iÉÛéNñ%DäÜ0qoÆêÌ-±9Ðú­u;pÛ‚S¬îæuhò&‚ùý#oÔŸÅêK°mí",uy‹xi ý8Ô ƒ¥]Ø3S‡y¥rZ“%Û3L@[ógýø›V .'±ÌŒ¼3ÿʺUß‹œL¦g•2IØêE]WÞ`ï^+“NW˜gkÚ¡‡·„ 5Œ`Ti8k°ö3ôŠ~Fþ Ð{µ~lÀÒ 1dcî1-[8ÐTh‡§*õHÂö“P{I„\×n…?VkPúã2f•j#ž-Néä ïêûÞo©^6jáh*Ö!¦=0–ç2>ù9Ÿ>HL‚1ÃJT,_‚-›­ <’§9N6¤u[+Áß”T(åÄ’ËÞ­L˜7³Îò~÷W #’Øñ^¸or‘{ÿù ô»†/v†²Ò»gY<<8ûï%:£Sé|·O'ØQPQzÈö2lµšËð/bç_Jox·-€f¦Ýgó—"›`.ÅòÜ9Iý÷ÖÁtçW܇‘òì»+»àöf3&úñ\Ì‹‡Ž‹£œG–q˜;òïðÉÐ,›Yt_'ã÷ì)ñ´¤ÓèçóÒìÖ˜\ÛËÇn̓{0Ïê6v$×€^g½T£¡ë„ÿc~êýó ×.aõí»ÄžžÔ`ÒVòä*ï»a¼µ÷¡½Ý,´ØÃ¾¸¥LyùHÞý3^ÔÇ sÕQ¤¿ºüߣºÂwøö1ùî7AÑþBVwÃ\ìÓÇÎØç61JÖ8óÏOæ»Z³–ÛÌ ¯·©®È…ï. ÔªÂ?ª˜›ã×á÷3`îK߃§Øü¦“9XÂÓö¡n@Ä®îa´ãëoæ[ÝFÔ¿øœ~‡rjöø ƒÊÛ@)Í•¾ÔÉ^B¬ò™8üþï7EEÅIŽøuTD €²@&×·ÓT³°i+?99Q wwàì铸Ìgn÷ˆÒdÙ\¯Gæ­„ôø‡ŒÐ«˜¦2y=k¹o[39EVlD³à£t\|ÍŠr¬Íœð!óTt¬*Ïfï/¸‚‚xóçÌ5Œ*¦‘hòýƒŸö[Åœn /®éåˆ#Dqÿ9LokDÛ¶0®TF,ŒO_H'Êû1°—Ef›Áܳì¦O@i(/É©Å5S‡á¶ø—Àî¶$› U¡à»8¹øOˆßÀ¯!ð#þ'³íZ8*HÆ>rky£:í54}[B§bÛ˵LòñoÈzÃõÅ©TQÙt°9àd¿1WÓ:B=ÇŽ^CoÿXlrܪŌy¦h“Y «ueàœ„0]æØƒ‡žj½W¹± ÆFßcO¢3v™Í …oçáùeúj–s®™âKó.®Ñ”0ròlq”| ]é³h´ÿUîºV Úôc1µ{ëÆú¬¿‹içmpšækû­ Ÿe;^Þáì.x×p"ç:l’#ÎuA8¥L…$ÔqÈó;!ÐÎzÑŠŸ!pðÒ 2M40ÞM•.ªÑ„ØS”gícŽç! ‘È.o¿Î½U•‚ó¦?ưB ÐûÒÉœœ_͹¹R—œR›ƒwƒv2!Ö°ójÃÙ-è(‰¸~†"Y;ë+Äl&õnaª‡êÕ0%¾±ØêžMÞ¼…ilµC+¹Dh_uŸ\UÊ–ùÃ|‹þÐy øÇáÙ)O,¿ ^¿;Ø…6ñ¸±i)kP”$ñè`Ë—ePwà3JýöƒË¶‡aï!Q„3ƒQq¢ÌeÄíáüöô2¬3®BÞ½#pm YѯˆvmžL­Å6ÇÉŠfÐMðÛU„,³èakÎÚÃ×÷¯Aš¿7ÅÆ0¾MÆt± ¦Kß©vYfÍÛx° ’f|e.Fø²)ipåôlö~p ^O‘‚îaºHl‚¼q^X<‡x¿HÎÀ[3_iÙ©BD3lœn|ˆ®$ 6H¯D<”ÅÌ ai¹@¹r+ˆ+Zâ’ϳIAÎGöá·§¬Å]òH0Z”ÉŸRdå3^Pb¦¡fÝTšÝ¾ì9Ð>µÍLMÏZ° zˆ¦–ÏAÂþ&laÿÍ›€õ´âS&ë?£™uJ°–jO·¾M’ùƒœð*;æ‰úO Zmóïãnžê^\Ñû t~'`ÃC§ ÇCÄM˜w—•†~©t(8GÎ{fØØ·]Á£²è÷UO?P§çgaFyªÀ‹×ƒNÙ ŽŽž^¬ô‚(Œ¹5óÜØ™[V¹‡ŒV+ЙÜfîZ_3ø4rß´€uðK\²Úßå1phÚL\÷‘é’%¦ýÙPèç€U¹:cx?ì$ó&i:{úB³ö Xü7 ›—Ìgixýe°9ÕδyΣWÆî±Ú®/q¡áu–ûÛ‘æ5m‡Cr¹h= {cHGùXùèpƒ°ÎMàXÄ‚D¹=iA‚~Ä÷Ov{½FIË›èYôž­^u”Ùø²ö·EãÞ¥uHÿÓçG8iû>`ÍÁ öo—®žþc.Ýá,^Þ™hzF\vÁ5{²[j±Ïò(‘drtó È8Yˆz!©øt¶&Ú«N!FÞ¯°Â§ J~€®ÀLû"EZµ4Èi³",’lßÍ-œg Bq¯À óýø)tì\Ÿú)îãêÁoUSì[Ï%r²ÿÇÁuÇsõ~q{—…ÊÎÈÊú<ç©T")…DC;í)2’ÈÞJDFF*‘øÜóHii iHK;¥I?ßß÷õÜ{Ÿó¾÷ž{Îûýz×{Q$¿Ÿr]5Ãph×uncÄ ´ûj’ÙûÀ]ÕŸ|<Ïm¼0•~vμ?‰¥— áR×5nùß@([¹ 5HZ §#ÒNnLï#“T–‚cér8óF‰o—Ãk;Ęù¿xY#‡31á68<…‘[‘p¨ÿ „gˆÃ]á0iO⬷LFó“%dœb*Ùó ô@qüS+i4¾âË/…¢ú:ØæUS¦p…Ë”pì¶*Œž)9ª-“x´‡ptf¤½$Ú0Ž´¯óaÖ¬v2|ÓC/üåOžÇ­8g?Nò-Mbȳ8)îêºþ­Â´kí N¡a"U—#óô–Žrø Pz°M|ÁúS:8yS£•UøòðX²D‹žø0 Û}$q¹õM\ö^„~φk"×q gFŸxd¯xóŠÂ!n÷ò­ÞŒ.šwE%ÆÓ2Nšø ¶‰ÒF[!¢7,ËÆNã2oÁ„ gÉ„š)XßÒAŠk¤iØx}jôZ‰©ºÒz%Y¬ÿ|Ïjç¡Õ!MÜyê05:K0êÁRXa5þÙØ²”)«0§´’Û³¯§ÍÁ±—LXL^_ÅU–å9ù¤+LÊ1Ø€úã6AERqÔ c«õIÖ£ «TW°=§aî75\½ÐR„Ÿsï÷sðØ\ù‚·ãQµØ†Ò¹“¡Ö5Âì@÷ÓœOÊú†œÐ›\Íû&3“]É ¢©¾Õ8«À‰|OåÈÀUð#¦pIö4™69žÄ”Šp×¹s·{¹™±:Øß ßýÈñå !«‰ïÞÛÈèØhG‡kD˜`¸ÎSK‚ÉU;ñRžôïÏ!ÂðzË <(;£¡•fpÿbˆGõ:²î-„”:±þÏ“)O» uòyªß YÿêBܹäÂDº)Þv”eâ;NÆú‹wìÇЙ*ó±ûr=ó³ñÏÎùÄ p!û&Q‹þàÀoQøkþ÷Ÿ6bD¦LüÉû’ÁI¸F ú¥˜kXv^;Êv[˜q—OóÞ>Ø oÁ¸M›‰ßÍ=Žií ØÏWxrêáaút´æF34˜eB_Ö@C¥ܲ1˜Ê®óo­©åÒ$ÙÕ•\ÿî>^j¸˜=žõËM„hñ•TvØÞ»þ·S4FùbIZм¸R} z=Ž];󞛵¡Õ®ËÄ!â4 ŽÆ¿o&9¬9bM?þr§#+ã@_dˆ¿Y _n?ó ·<û‹Ý¿ÎÁë ê0!ø tîÂ[ú_ó\óÙŸ†_Â1ØËÛTeïTô5º ¥äZxUc·Þ0×Xu”uÃ᥹KìG«–…ínÎ×%Øð«ÏÜç:þ‰sö=A^}P¦3…ÙÚÐÏpæw aO´‰ˆ·/ý²ß‚:†ÿ…±)tË»çØ¯Ù9}š¤§ô¶ylf/Aèí\#›LÂ’CÙ«=›Ù¶ xèp”…S¢{8Ó€[phk´^‘£Ö±ñž7qe:ã¾Ç ‘&˜¼Á¦Êеr’,z¯-}ÿŠtº}Æ’’EpÇ)™­N†-}íú ¹Iaðfþ'ÏZ[p†#;Ù²{>6MWfs¶‚xý$Z!Ö?Nwq»÷8\iÝ NF¸à÷M<ß”ÁâóUÙ—¦LnDØž8t×áîºu,åÐ~W„§Ö‘(÷¹`p}™yç \[. ¢MÇñžÂ¨NײÆ[gÂh`âj¦øìïÙ%{fgîE„#¬à­0;­.ß¾ïÉɳ“‡KÁ.©˜H¿µÇîû³Aú‰í\WK~-¸ùÇ%Ù’m |‰)Æ”Ž9 /öž†Îù þ¾9“V݆?'n‚L¬5lÒ=½8>Lî%Ob9~7axÂ3R—ñ §¹å¢õ¼LšÏd\ȱY¦–ó'[÷`Ú‹UÔnL/.ݼ dŽÃÓÚè†|cjª,ŸŸtsºÏäp[ÿ^Úw x 3ç†N8Ūƪ¢gæD¶ÿƒãoåÀz©Ý’³Ž–$d¢t ½›ÈÞ5€£©*î*CîyMƒÂàRdŽñB滯\Ö’û3N2‹x<[¢@Ù-1ZâÚÎÓ;È•yË@» Ó7¡[O;“&kqO‡8.§gãqðý8¼ÝëS›÷ƒà¤ïèú^OM¾„¼€Ý(*›)Å~½A‹ÈM@×L|ÀÞM fÒ¼Jôý¡Î\×ÕÀþÕ×qÓ®(–´Ô™UïP —|ã-64 5+fÐðŠxRTœv‡¸‚ß“þûŒÄiˢ꘣ÌìÀ&Ö}t„üÐNÏ‹¶Ã–Òc4îöÐWþ …>ìû 1:*Lƒ¤ÌaÄC25ÙU‰J.©1Pâ Fõý!kznWÄ:ó éjg±Ø.¯c§¸0µ{SÁ¾°&û¨0¿Ï«øÑãè*-ú«Ý”­owöÁÌúBÞ” 5u=ðàr,mŠ=ˆîéOpoH#'"ñܛI´s×3|k¡e³X ß™‰W™Ra¶ãý9̯²ážÔ>K—-¶ôbº¿r‡³Â0Ö<$¦¹ÕÓ¨ÊÆ~òùˆ>;ÿþ8n¬¾‰Oœ°´ýnñ"í0þãÌÇp¿ïNuŸÌ†6ìÂ5eµ }’yž†³ÝãáŹÙluóNÚód2}äl ÁÕN(™hMÞë˜ä£YÐ.ç¶Ú |é9Yü9–¾ðq!žC÷MÄñ‘ó ,…—™ ²"†Y–ÝC‡eE/|œ9߃ÙYÚl‘¶ Ì®^‹Ü€ì?anÒÝÓ»q³·\ÇßkQéDc([‹²m‚¬DŒC…QÝ–Ò³€Ìd¼zúä]1:„m›×ѨR³6iÒ tëÓÃçž7ù¿—T’~ƒøñáw®óô˜ÝxGjábh:®ðK"ªCÊЫ•‡÷K¾ÁZí!òrO®üá‚+ªÔÙ畽Ð^?wÅ|ç¬ÿxBí&—¸AŒšã w 4Øò:_º¯" $™½ã©³¬0ÞÏÞùv£uß³‡œW³¬Ç ï¶tòÅà$TM åg/%qÉ Üò©’ðo­ ¾|Ý@Þü&™Ö"¨f™“MEÁú[Ïݤ濸äË%îúï3_ëb8™/ËŽLlƒË~lw¾"ýgx ×­Ã; óY8¬k™Áênäú1¨2mCƒÞA‚¾ Œ#Ja½ú|vNȃÒUbÊFÎàÕN,èÒ±Šö¨cÁµI´6Ó?>$]?qzþP7®BTûˆy-A¡ëד§M‡29‡î ~²"ÕŽp/e((|D³Št¼^-Í"WJÒãùmd¹¤nò»Â·;† (̇*)b:a*É}ª g„‚É!}–ä÷ jÏÆpÅaVx’kç¶\³`+<ÙãÚh¨+ïàFñ¡sXÔÝ…·†Ð2Ô€‰›î%»]ΑxÓ´ªÎ4®Ñæ|<6†YÞi§«×/—ÇkhÈþû$§QšYº ²ñòîì©ÝIú´XŸ‹')DnÀŠlŽÿŠa',¤ÐÒ- Âúà‡žÜèž´7Íé §Ó—B[‰áK5H>³–ˆ} Lsî^Ø¥= I&DîáTÜsOˆ Šû°Ã°sEHH,Át—“u¼²N)ÜÏ'cÏÅÂ@Ýy?½sXÿþ\„ÎÏB7Gй/ÙsÙܯۦܣÄ,n¼ux!Mâó^IC˜«lêàbP*ˆsfYùaXzö Þ}ù„sPeמëÐ g䱿, VM¦oE IÀ·ÙL·²]Ã+apŠ·Ks6žè¾OΜËN›2©~6¹öOÛp%ýg1ZÎeáùi,Ã_™m¶&í‘PúÁ6œ‰¯ÓPÍÒk§N௉ÜâÎ0ÚüÉ»µš¼‚锘Ò4j@û×1Ί]ZÌ /ºñ„¼'0©û2$nÊnfÉ"ë;̲_>oüšØÙLØÑ#PÐ.J{E,¹w7»Hë4×Ç™Å[Àîo#œÃƉDáÒþ3”6ùoKK;oðÌ즳ÑcéÌ•´ñ• ±Wãã‚fҴ⸱'1-(”u^ªÝÎ ·}ÃÈÙatE ¥Ó5pòí $;?ˆ DŒå:µ³p©W+œnR¢öC˜û6a<´+†Û62—>/5¢N}ÜŒ9ÂôÂïÓð>ÔƒM±ÚiÞ´öÃn€Il㫳ðDaØîÉaŸ¥EÅÿ cŸ5Ó& ´ù…Ùsþ<9b •¥¥øäž#¦eÙàÐ_e¶lä&®&G«À*§ËxiÇKð9¼Žì… «Ïq×»LHäfP²§³©â̵ï@¸õ0OzB3u‘D¥³×8]{ºm?ºw1îk1Ç^µê†ì„^ò0µX0>Ä‘Oö9Ùì¸51›·ªê/raóÃR¸±þ¼x¼NÏ[€·Ë%Y˜ê!X¸Í“ÖÙʱî87ßG‘ãÕíÀ5w»ááÍDr%D€ŽQ¸E–xyPó1T¹à!;± :yß"zãÒ;nšó¤÷l |Ã#Þý¨t<45‘Šxïp*‡]¢ þóvݰþä)ü¨±Ÿªwæ„}ÝP³¿܃CÙkD¡ï®œ×!sï© ^ÍC…avûY¸%L׬ SQ³ca²j*4¬§ÿ&® í§ºà§k=(“bA!{X×êtvýuõ2É¡wté®ÅÍ µô6Y­¥ÖVbbL0ª—G²Þµ¿aûF}³Ï˜‰þ<®Â·I•ÅNV»á&èûêÀUtU}6[ܸ„þœõêÅ•˜‡âNLÔ`×'݃V’ˆ™çHãyšg"»3%[Õ-±þ¦ Ê‹ÇÒ¹ þ¸ÁRôß¿EK’ÊŽËÍ>iYëèÂîɬjÆ2šÕêÀÌ=šÐåÏ0úÈ$µ½xÙÆ÷pzì|66åt®0'½ðy—%|' cÚ±-¢ãDFÅl§õ ÍÖ\ƒØ‚=ìŽûtÔZ¸OF©³ü«ùdÏ|;®=æªôæÒÉvѰjÑ'œâŽEKÐäÏzöꎓH÷Ä£ï÷£Á€%KÙ Iî­8Áf œ¾Õú #õ+´‘ý/:ðÍÉ(&^þß ×eYÇC¸ïºªôÝNökoi(Žƒ &–”äMç΋ënk±|™VqÑ7‚ÃÁ",¨º€”‚íI|l7JãXq6ñC> y5‹s— ½e_p´ÌØ6pÖäÁ¹y˜ø)=O8oB~sYGØ__+fà4i¢ÖÝX Ê[Îð'§g[ºþâ[wq8Éž½sôbçÅ\ÙebÀCöÂÁÎFìkÎÅËW³ðÇEæv¦jjŸ€à‡Ó }7í®?Áb[ÜdVˆÒù÷ø1wŠð¹z\¼œã/XˆûäÖBë u*–6™y»›£×$„ç~;Hòt7´”³¥Þ×V2ÿàËœeJ)Îj”©‡_¸sªP#ç>tÄf¢ö-z|Æ\j¦°š ¿9»_Ýw²º šÁ^Ÿbxü ­{ƒŸ\e0†žÂ‹†iP·»– ÆZÅðvÝÎ Ÿük¶Ä¢Tcä•RéÏ!´PmlÆÔß³yBJÁÌàÊ#Îo› =Ó“‡¾ÕR°pê ¼>0Ƨ "p*á)_Ú\šÍÿâDË‡í ¾à™{ð:¹/¾Š.C*­Qk¯5¾yqKØ›LCÞÍûRtŒT >ä)³Ü!mZã=…m­Ñ²+'K?8_#ÊkVñ’œøÀ×ÈÕ«X¾îEl™ªAMÄD¨¥{"º­C™„«x.ø#YpG— î ± ü.f 7Ò@m²Wz4ŸÔ 4à¡*û®æá½йt{Uþ$]lhäÞB¦¤R —^é‚Òy<©z›S’¼“ªŒÙŸ¹=`&ž ãþ|í}úlÓÉò)ÚŒZÛ*ÓúìƒtcÝTÞè>\ÏŸSàýØ–YÝtg’3Yº>eÉS&°kÓ£ÙêS¬Ÿ¢N„Y¦pŒRBé+ÙôÆb¥™±Û~rr§—Ó#oNÓ¦ÒŽ;,ºQ ÑÍNx¦ð”"Øs  óß.£z†v\ÍÈjÚf¨À¾Ä0®Ö£ [#èÚE:,@Óšé¨9ái6û¡:Ë•yÅsz~$EíÙ“u+ÉpÓg\&ò‘lß™ŽAÛ$ضö[hšRùy‹YPhûs ý9ćŒ›Xþž- ¦=›æŸ§w”Gqúå¡O“KzÈ0Nüçì~›,ÏUdw§ý¤/žÍ`‹Aƒzý•dÙs”añóíìØ‚W0Küñ¡Óó<-£_C2a‚ÚLöbë p¼DkW©Òæã[Ùø±ežäÕ^Âæ]'¸ieqŸúXêy¼šv«‡âª¡»°rD‚YäP‰Û{°kÒ ·w;s$|~ý¢S=ÏÒ ³‹ÁÊ ˆÖµ)²YþÔ´k!Íé»ÊI\à×Ê¢Ð-:üÍ–>ªÛÇ‚öŠáÂñOIZ±/™Oq±s¬Ùx‰||~]¼ºpÛýFȺ<ØKΆÜ6ÈËfæç.“ˆ,78°ò*]WcÖ}? ÕBˆ]‰:‡?S–ÂÄç"`xc'«ŠœûÒ*ˆ×]qûé;W_Q,n)鉒¬vÙG<œtŒK»Ïcª=Y¸ç ,¨·ZPïÉv7š'„B³ãÑ&­ƒÇZë6G1ogùÀ;?L?[¢B®¬ˆ0_¤L&ùbŒ„3t\ £Ðºý·iâ¢òìí0Šƒ¶wcàßš+ÄÈ^–·QÇÕ5Bø'ò”ï§pIE†®Þy ²oŸà-°²d†áwÑGü*„~>€ º ¡S.”ßö®u/EÂ$øófCwP ¾æÍÁ¢B†Ódzšünþ¹›Ù°˜> In$G|oÂD/Æͦt@œ6Sðcí~}mG@–¹`®_·2n}ÅĽÑp,e.·Þf?(/Òƒ÷Žm(ö ç÷ÿ%ž«NÁ‚Â.Ød<óЯ„éâ9.ŽÖä{CØÉ˰âä <&|þ~w¡¾æóÑmíK¬ò(ä>$CBV?ž:5ž{Æï†ß¥õX«›ûÕ‚K·<}jíE²6žæ¿¿5—6î΂¡ž³ÐÔy”ÍαKqßÐú³ÝÄϽ£ŠÐ1)º¶ùÒÆxAVäÞ>„E{½ÎÔb}ý|&ðUÔÆðB^]ƒ´Å¾„L7cJ¾ˆî£h°¶…$yåE6t‹œ?{ocÕ£ÚÃaÊh./ §³¬hr×e¦.©Ì~i_†‡ê븬׈¿@ÖñFüÓpˤŽT¶nþLB ›(übO{ÌMؘÇ:$Ü: ÖÉ—pkØ£ªA\Ys ÜÍ5)ïi3¾aHïO£™©èô–Ç•íœã„ÖâÉ=éZ.•ŸKg&Å„¾¬œË:Ú—@ºD2to§[Ö¶àK¿.ØfµŸ&qo1ºû.Ü÷4¦Þ½t_â4Ø•ZÖ]ê%ÏýÙóekawˆ%Ûôº bí¤‚i¦ìÛ‘Èë×‡ÐÆ 6˜±ŠíqcÓÇ÷óZc»ym‰tªz›×|†¦$y²î´;˜ÖœÆ”Ø5EÓ¿Ÿ–1í¼t6ZNÑi kÇ1ƒ‹Mq©Ø³Ûì?ê· €wü(Û~0‘éΡÆJÛiÃN1Z|~ò^f‹-ÆÔ\}'´?â Y;à½/±V݉?=(‚Hþ×7Á;9aﬓøþî^M‘3ëݱþÌ~ÌÍ~ã>¾ÅüÁºl—r=(SÝ÷íX弈5>œAÖƒÂé<Øvþ ~.œGǬŸ€r/ÇÐ}Ó²y'"ŠI=_‡}ÿ7 VËGÃc‘Û<ùÅe¨6ï8?zÎ'âû»¥Ô×ðë™>œegÏ^ÅRʯ|KæÝ@²6¡Ë̳Ì&Òex:}{¬³ƒFnoýh;œ<Ë™Ùm•dÎó¢ÑmÌ\Û¶ìG\¬Ä®Sì/Ê?ôÃn%Üà%ÿº‡šXYñOIuïš2ž hæßmÄwƱ¼ƒFºœWÜ%R³iM²œHÁn?8šGÒ¸êB’2s>Ê&§®º‘ðŽ­ {—Çß15 &ËÌæš\(ü¾—–ý7 ÖæBkÁaVï¨M2‡ÈïögðâŸ-³9€}IPðjëÿ,E×ÏìåZž9‚jîNêþ/éÇÝ¿LØÀÇ'ðçÖ:°_†™LŠÄ{]I£] ÈëеkN¡çÛý|Ï&-zJþ&®2 Ó¢¹[¨Aó¡†Ûò%ªˆž>ÓÂ^s'ü®à®½ðÌ!)’´äÞ7’í.Ïþ\oGíœ#xú¨¾ú€*j¨¢ù‰l×â•Ϥ÷£Qvè?~.VvÀ’ËÈŸŸŸyŠ/å™ÌÙ0Z^Ž—.…ðö²$ú*­!pPŒõ™ÈTL{Ⱦ/ûPàÛ4¶uc)̺%Ͻ›fŽ>Qõæ4jrÞ‡dsß¹´Ÿblt~cÁ,ü4ó,†„–]Îó¡hÁCLô_A‡uh¢Z>~<³Ë÷ãïmº(ûc„ÓÛóëg¨CTî.ˆ'‰Š/ácú?\ß¡ÈÜNVrf’ã­B8gøtîÁñÔÐÜ´™bôòß/°•§ ?OË1}k7ÌäqðìxìŽÕ{èK¯‰dÕ²è1® ?£[n•ÀˆÇÖgòƒ\º+›"+qט4l—2c¼® »‘Ž3o.FÕSWáçÓœsP›jçÒUì”~h›ÁM(Ž{ÀŒÞY•Æe[ý ¢Ò?0µ<ƒ­ UCWîž"Mƒ£‹ÐÈ<ç^ç^~™M‹vÂZÛ«žì„&Áò´7, Ü|léS _öS$ŒÎVÙÊ6%úÐM>KhÀ¶ÓæOëí'F܃É)w°ù“${?Ò“YÐéa2,e^1&7à×ý²¬Cø&ßx mS˜Ü¤ŸèÜàÇœ™Úߘ5¦w-læ¿¿ã@ÚËê÷agZ!{-vÖ¦æ=ÔÈ(‘<»Ï©S#I—L°¾µ®<$ / Ù°÷A*^)ŒÞgaÝUtja‚ÏCÈ4¹‚Ó_‡Ùßút£Ó3x:þ-_êé/ puâÎŰ?ɧøÇÎ+P¼޳6ÒÃJïPîÜ$ùRÍ?¯:ÊÜÇ2¿%·ø´$—?Ë&=- íUž·š(acDWm–£w?랸cO¼î©å)g¨Ð bÔ"úUÚkÍ.¨±Ÿ½ŸqýtYÖ+!ÄýÏžM:ÎÇØ´Ñ<ÕÊép0ëÑ_$€Êý£Üw„ß9K•œöŒ¡2˜Pâ¾ïH€þ$\´QÖÚ¢}šM>PŒÂZRœÉ·kØáµ(vsŸ –SÅ1Òl3`íEFô¬ÆD&ýõ0[Z¹–÷7éÄî±¢Ë.Ìg>‘%¨úmÏïÂXrá\ë<‹ö(ÐýƒLûî‡?ïÀ²j{à{Ì„/Ƭ¡vxÜæ³eÿÍÇ¿‹e u5œgÎYl ®Å·ßú 7³¾»šò„îG›ë–tcƒÞàƒUéþœ«Û*KޱIŸ9Ôq¤ãÎÿÚó«ØÝ:tráE4îA¿ù0ƒváàñÝÜŠ;­ðcÄskpFR-ב4†é}«ÆâºÞîš…¨¿i*NiÿÓ>!Åuäý™HdÇ·?Àëyxר‹ œ+Æ®z]å4‚÷¢åVXžgÏGÒ×л&T:yäæ ={͉ª§_¥[IÀŒ¯drl¤‰ÇÐGµ²ðríò+¥‰?H_òXª–" LlXøe1L¾Wƒ XA •—î&fêØÝ±’º^>ÊUÐ6øô«‚¸e ­ûS¹Ô‡Û±%ÏŠÔ¹ÄãÞ—:´§ÚZ—Nbq¾qÁ—-Qã`8µŠÀÞ1VpH˜ÙHR—CPKáÐ d-d£PðK5<ð‘ ±™~OtƒÑš³¼u3B¹|¾;s+=%<Ví pa«!¶m §Á;÷bdi€{:ˆÎáÉ® ¦ÉU¦ìо#°Ì af•;â‹çÆÖ`ÈhªñçÌçœ:×£XÞ¿eìÜ«±4õ‰sÑÁí!¨©ÄTf«s¦§Qáf7§>0ž[þKÜ‚WA!ùóB˜ê_Ž@“o Gæ<óíÂýýbt¶¬#t?ãKš_$—ãŸÝèŠ×dß: nYzœù-VVåú=o8%Þ)¢ÑÚ*8t"¦³ I»ˆÎ¾,\¬|‰7Q+ Êþ‚XP9dxÄïÛ¹“V|°„¸óÓØŒT>ìyS2ÓIïåó°Óe„\n|GâÄšQDº^?ïàÉ&´‚—çøqñb_¢…âÜ‚vèûR{–‘[«â1ãè:–þôTKÅ\ϸ¼EŸQÅÞ2ƒ,ˆÎ|…1¿csù]ã‚=‡ë%Eè~¶‡Ó÷ì$3f‘Kámº!µ?ãOÇÅBÁzEæ¨õëx\Ïïú% U æ¢qãXV}a€û5= ~†Í!Ûâ7‘ƒKð7á&wõ«+¼2õeWs%QŒ?*FµíÈà1wßàÚc ô½š8NÞ.Ìy2YXžˆþOŒ™NάՔawt ™£ìî´x JDË©xذ³*Îõ.‡)zøüÐñQžû‹zû5V+[±ï.øÒ“&)5¡ªz:˜.W`ª±í˜'Ü v—ª`4ˆZg§]âÌnĻΠZÊÓ$Ÿ"¨.HÃÝ7dzH^*––É2ÁñW9µv!úúx:8ú-…9÷¯‚gû/Ovxí?÷u§QË« ‘Û²…i\¯/mÂk•Kð;kæ7LÎ&šësÓi '{|:ˆ+…9ôË³ëˆ NóŒ &® Àw =˜|ŽÒçŠé´¬· Þ}=j~áe/m.е æ>îÆ’?qnŸƒËV§ñÂíNñûl.@½p2Ü2”áf7‘¡^%ª‘ ·%î¥ú‘.\áû'¸Œ'Bgw^ ÿ¦~„ä[­Dw­) Ù«¯+âpk >œé‡Æ™›ð\×Tzßz!ì;æux“¢Ž‘OÅdé¢íœd­ý¬S]ÒáÙ³Pôd½æ‰8v‹›|\Ø@˜­’4-ªîà¢Ëaèæ¨1;ÊXl„ÿö™2~>ÏÃw·ì¨òcîÁ!aRI;È”XQ<óø|[Ýb¯Ý@x“'q» µÊ›ˆcîT¾M½ Õ3™óC§áÅpÛâhï“¢†Î•Ô+ðs¤„›ÄtéŠäqTAÿ<–ê,†AÑ $«ŒCÓ£ÙóÙ`ã;‹sþñ•ã=*…Ë8¸È{‚wháÀ ÌÝögIª£;ˆÒûqË%ö§F†^*k"_ÑÎqHZJ!YÏL-Ú£m¨óG‘Ëÿý†û};î²N·d¦PqÉXþé' pR„,×>‘ÂJ¨3­á\ö£ûjeöÊz÷Å¿|lŽp%ö_9µ¨ pè“aïÃKçˆka.H9qì‰òÈ·£Sø—=µHÈ–ùð¸Ù~¯üˆÑgDz[gSðRg9ÌvS!O{T¹d€‹˜+xã‰æckª˜ÙŒ!T„×yå å¹°SåáËT*¨y?Êça¦P¨ôL‚ÒÏÄØ¨n*íÃPÝÃà»T˜ÖÆë‚¶sj®[àÑýüËZpQût¥¿( »–ŒånJÒÛî|Ây¶5U“ë²äÄw?âäEµé¯Í[ð¸½(ˆ´ˆÒWC¸Hý3.Þ )úÏ`Gg›ù8ˆ5͹ŒFÇ+1?·‡;øy=œ<Æ <¼B¦íkG&Ÿ ûïžÅê™a¤†aÖrOx@ˆz¶+q=TJ “´ Ä#&üræÄü7á× Ù…bbÎ,qB-Tg£™·ß|‹K~²¥›êåa]ºP–Öòà~+êýúl]mÄÔE“äÞc\­X4¼:"ŽRBÁðý÷v¼™Ÿäpr×IÛ) NQ¦\øž$îˆH%.y  Þ<æK2!å(?UÃK–ÊÅÖóqšúV¶£g%ž:%‘.Jä]z9N¶Að*„‹3.)nú½MaëŒÞð‚s°ûûDØÙëÇö$gBß-x.ü¬KSQ/s<.‰í%M>ÉXÖnÅMžƒµEàåÑMZ•œ0¢>’?Có ´ß‚ªàTQÛ‚Of!÷œÉÎXo~Já¿©[¨ëç±Ì÷Ç¢ýÓ†s¯Ú‡IíwÉúÇ_¸Ø8IzééÌúfÀ™{Lcîãìì…Ø¥§ælkc*7ß2l¨HÀŒ‡×yþ Ù¢G¹#iÜS)QL–zÈñè8°OŽ&WçñzŒnÃ߉cpÀáÌñÚp=Ûžð̘.ë•~âÔÃN”ûT)· әݼ²kßyR»5ø¯U¡á‹©0uòOä}Nãzß׀ɿNn†´=lâc˜N¯Å$|ïaXë ò|ÃDÎ^8çÛéÒ%±ç¹ëwxÅÝæ ÂŒ“«˜“\¶sè¹St>¾¤ÅåQ«ì-ÊÑãÊ·ñ—Ëkº;,ªxOñ÷ß•ð5®÷™«7œREã‚ËdÄ(³p˜ÊVó%AÇè%ÌR €mqÓ`øF Î;»…A.Ê•ñ¿ËUòv?ÛL6mÜàÀá¨5„]®ÒTë² Ã"#}­a¼’(·0£G7sø;³n >â–Þx fß™U¬Êü°Åý{7Âî@L TÀVãïðˆ¥Ú±¨½ë+ZeœÇ+íÉdE÷ܱ/[³ü~Þ  KMÈ#k¼±¸]šêÑϧ„Û¿Ës8äî‘™°oZþh{QÒ¸¦GaŒ³zNÔþ¬†’IP£R¯RáDèdÒs¯OÙ’ívË@¾}‹kJpÎÄrÜ•’Õï­aã,Az,ºf½µB—ÚB¨èOãbÊ£0ñðOÞ“¹J\RäRy§ ÁË•Áþv )b“ˆû“xöî;W”e€l1½·üÞí¸³Ö_ƒþ›ÊøYåœüîƒJ—¼˜Â™Ÿ`dßó„ÖÛ£›ÄEõ'aÝFŒ›¡Ï!^‰ÍÎè¦ý*ý¯0]ñ(£f<º·|9ÑtÚrƒlö~FB}!˜°£á’õ úœL&y­ øµá.œ¹ü_,e\¿T'É©vâìÎÇéµßð‚” WÑ/K·Î0$¶c‡Ñ,\˜z}´â]ªZÈLßÏMû#O»”0gš|·›J}DJp¿¢#ˆr§o›áÖÕ™\mîY®l×86~H‹¾ü<6¼#ghÓß…ämïvc`5ç3£™Œ ªG™¤(ޕݽxz… ûFG`b§3N‰8 moä˜Ì×Jø·hĵnh,dõ²Z$õÆ6+Ò|ñn˜tw™§µ™W÷Û›¬êV¦«ç±vï]Ü‘¿{àÚÙd̹&ï;ø‹É½Il½Ïã¾®£¸3ï>^­Š%š#I°Æ–ÇÅÁº8<ïÁ4ˆfBgbà­¦ý8% w§£–k·ÝÍ–;pP„®L¡Ö7C¨¶™½Ç:ýÉz|ÞSƒ×î´¡·½ K“`ÇŤÁdƒ&Sµ ˜Ãç/k9­]0+Á\þ©¡O†õ¨Øƒ[QtýyþòjàWþv¸|¨ŽSYð3b½°³Ü¦råìû9A:ó«'þ»`‚sÙ¥ºÕÐö' :»$YÎÞL<3u hjA›txõñ2ßÚõ×è«ù ”úGã¾ækö«Qé³#ذé¦É…âŠ=¥\Kộܿµ£CR:óz¥DºÊWÑñ3s ê´ó½ CCV ÓÑ?°@dn_éÌàÕ2ºcdÅLuQyè¾CzÓ{þ½f_ºôÀ_ š:Æj²¿Ó– öF t½ÃÁ`“È·`¡óŽ“ƒG¾ð¤~=ƒ§É Jý¨¿W}žßÖÑ+­;é—tiúÚ¶„=ɤçzãààÅCØ»EÓlÖÓïÛ¶áÊ&7â>-]&áÝ:Œ7¬bjÑòôL|ÔV€ËÆàÎR„¯1ôÙÆ<Ñd’›ýØaïÌ@7œçoÓŠ6;zø2ÓiÆh¯ß%Ô÷}CáÐ^uˆjÁY}=Wåø8÷´?åue¦¦…ĜÙZ¬æÞ]˜ôbD8ÊA£å~—%¡öÊ0ÁÛJÔsÈöª)Ü©ê9 r޼øàFfgéãúœ±TLCžn¨DTüô™ç–=Æû%¼%}àNßDxDÜù/<8xü¡í’ôXå]GZgºó{ _úm=Îä€_Ó”ÊèÒ@¼¬X é'c¸s>Ýk‚Ì¿[ŽÞº¯ •ÕCœ¶Ç1T[X Os^Y¨¨]ôxJaω¤u$tº­£)G×@ÁÓëói„uYñóxøš,À–ß;oRÏ¡å®' 8F˜†djòßÛ ÓªÜÌyr·¾~›$3ãCÕ+2¸Š5ªxë1h­d¯¤Òq ™7‘J–fãÂëtýëå µ>7”åÓi7À…/9ºÖz’Ïør\ŠW¤Á˜z~eÅX¶Ð‡½^÷’ûajÈÞè¬Ã¦‰ìͯ“¼Óëàš½I4½½ŸÿXÄ -oB/×Ä^T€<ñc»r„iòÊP:¸t%s>„Þó®á®|ÞêéS±~ ¦ùè¶: VýÜÜKۋѼɜ™Í5bæm2ô?<§‚§Ã8Zðå)Ñ<Œxµð1ê­G¿™äÑ ù4­÷–˜?þ¨°GQ—ˆëƒã4oµ<]ŽÀÜB–zô¯À´Ý¨rVÚÏ÷¢˜³4É)ÏÆˆ¦RÎÊy׫ˤ sG¦©òçÅüÀUr“4,IZF}B™ó3Gv|™j§×ç°Åã&Bœ[ „g;T“̳»Ùûí+ÁBF…–xªB•P«ìÍbÆÏÿ7wG'—½ô³ÃßàÕØëè*VkqoävÃÖ¶òßlt£ • x¦á…¨÷ÔÒñž}? /f;iVYL»*òÙ›jQþÍf$:û‚'LÅæœ “V°Wõ˜ÙÈ09ZVˆ»Õhiµ.¶&´ÃIïŒ_lÅ>Éñð@Ý ­7FJIsµ ‰|¯Ë.Xк÷1`£™Jß;Îì¿¿ÀË$Ø?]M¶ÿÈ[èoŸE«äü™su/O6ГêO¢¯ ÿa}ÙQîF«Ò ‘§Ãk…?ùOAT~:»e™ÆxÎðs,¦jY—1lí*ÀÉЀ:æUFÙW?%vÊùùÔíŽ3[æ±U#+¹7×"¿Zåx©Ë,Ø·Ø^ÈrÃ~Ý+ÆéË–PphG¡)ßàÂøÓ\ÆÃX«~‡”öÿÀ­µÑ`¨òþ®…C_W²®Mvûç8Ú/)Š›uÞCº‡ˆµIRsiðL¬$4ž‚ã1ð.‡|~(ÿM© ¸Ö|‚Ö3#d™Ü$˜7éÏá¾ ~(Ü‹?7Xàùâôô§«P»Â’…-4e¥;•ˆÝh?ÚkVŒ¦þ±Ð0M”®ÍÜÎùU½ ¥uikFæéÛÒ‚ªÓâΰåH-÷×^ˆ{i>НäqѨEÇ—ÆDï#`鯕œæX>ù²eÄ饕¯È¡î¥´êˆFM—8—†‘¢N ¦3|ïvžÀ´ZîÉ’1³c䑌ÔqÆð´§žm­ã%ð ÷2xHP­‘w\¯úÖÿf¥Á&ilJa…ë<žÊ|>´h Ӥǃ$ss3žSª@Q•س^‚h&R¦0µ”jb±ÈÔǹÆuÇ ü‘=wJR’´Ù™bâÍíø#¥½·† 6¿Pœ)í…ófÖû>pÜ“qd+Iµ„Ä~,»«FÏýzA\:•è'Ak–u\Ç(ÛÐô4EZñ|³åöÂöü`0ÕJF˜NqÖå1tÑô`6Ö1Ý·;<®¶‹îïàþ;nêÔa°Å”kÕ€Ì+Ûáqõ%¾×Š'ðõk'Q©$GÒû¹w÷ÇR™—ÕüÆl+¼-wŒ¹whÒYòìq_·3+ëÀc ½ *ã‹ê§átàJ6¢àŒL(pý áK/p‹Bÿâ £t87š“¯ú×Gœmß¹È_Ü’±—3Š£í}k»0Ÿ¥§/Á­†iß“E 3dÌ=-.Aÿµºô\`J‹¸ƒâÖpzpÝuN¡&5l7ƒ¤ñ(§,fÎ[˜÷>&8Ãí}nš}¨ä;£àÍÐF¨mP•ÝS¡W±•é’¯«IáÅ6žÆêA¾Ð¯DÌLýÅ­¿û ÏŒö{ÙÌðφܱ{8œTü÷­tB*1Zß)O/Ú„.Oåé*G Øþo;lÚžÏMÞq–X_<‚}GéÓ%üúpϹ¬›_‚ÅÒ!x9Á–ýZžÌž—6óÌ99¶4bT©üÀŽ«ã¡au6\\ yr3¨ýCôLä®L×¹Ù¸bú0væŸ×{Šå“Á!.޳X¨LÖ¼µcg>Xt & 1C#¸öI.ïFál[X \Ê[˜U…çÉ‚Õc!ý~1ÌÕ†·7^r;µ¹¥BblàÞ)þ\ŒÑ’l®N<6< ‚™cmÈÏ‹-°Ú‡>zèw$«Ì‡pâ\öáÛ/”}r‚,Ÿó•;Û7ë‚áywnî‡ïš;XÞÏýL3ü-¶™vñ¿ÿÕ½± äßgqöð[%ìûûfÏ)æV'Yðšõ;Σ[œØú’vžXÎ…ý¢Ü›îÏy8éÏŒ¾~‰—÷û:Ìúñ·&á-hÅǃGñšõøñÀìT×Â;—ÅlŽ?À B¡ô_2ó¼¶œ>Kľñç_’¢Ùw&Á$³Sè:ÑŒ¾7ó¤Ï²ŠÀê™ukkºö é‰ux NšÞ*U‚óWÞsºÛY³ƒ HžâÛ7ådÍiüìoè4a˜œŸ>ò¯æƒOèbzñ=¿¼Þrd™}ö¿*1'²»8ºfï:ˆŽ³Ø}ÃYðX=Uv‰ƒgÛ¢×æí~Þ[ýQkSÓ[ qíûñ[¯ð²ðîùx>±øOeYóÛt\–ÖHH0Eµ«°sÞTzHú$½¤“@ˆË#»ý䊬ƒò¯VtŽ`87ÞÄ)?»ùCù³ÎsýÅR˜¦XÇ]/m · XöìúbIàSü%q‚cB»áƒò?ü÷Ø-ÅR´Î°£·¿\ÆBÑL2ÿE­ñ•¤s£XqÒs¢¨:†i¹†ÓóÒiÉ‹·øðpoBæ ²u™ ãó çþ=ÁŸj}³Ã>¦Ê«P$ È“,êÆÓR“HÎ@_6áÔhïbaÒ±³ªûDÍ'0¥4ãìAÁí9îãH€â\T^svË^tØGp±0mÑ˃Î× ¦¿ Å®%à¯%Á õa®ÿ4LÖŽâw,¤˜ÿzò¯$aaÈE¼õ†¨ómDÁ³»=ž^„I«SÀ{Š|\¥K›ÃqÆ5ë÷>_yB ÂDYPÐgøé«‰{_us§u…XNŸªWua¯°|ƒ¯e:~8Ò:÷ ÿ]"˜/¹ î‡ý¡n` Ð@¶³Ö§¿ã*–•€e• •ôIç-‘Òa•»àTB9}…Σ…ÅNH0QÀ9Q"Ô6§=½%ñÜÃçRìX¨'ÔWöi*”¯ù‡÷•¶Ò½X\ªHüú‹ßŸ»Òj7À>S=˜îªN½Nîb7ÍéöR]z¯Ö‚nIW¥~Eè”RÉŸ)yÏNãÕé˜ùÞèÞȱo7Da© ³š!†³þXcFj0}qpêD׬pæ›ûÈô{‚ØÏÀ8Þ›VŽßTâ c܃7r–ôÁÚeÔ2Ù'î䦕î¡5}ðraÑ^®ÍÜ­0ÆhÍŸ´¦Î÷dxä)¡=­ä[¤ÓK ±–cx}sê{KÁmbʸ±Ds\-Ûþ L·rç\äHú…0öbèäÏ@½¼µ´Fý.i•q°™ý<áò) fÝÃ)êôó.öK~ýh½žÏîI8ÓµflP>ß…¼Â‚’%¼Ò²aèÔƒïƳëM9ï“6è@É€û:Þ= Õ¼Êü'¯  S¼éi_Š_6¦ÀQ 1Róv-»p­y15œ•› V~Z‡³» Éê ?Pª ¤k!ý*MB£w!§oNßÿÖY³ÊOØ¢~˜©]ÕebT„uþZ Í¼Šžw³Ä?Þ, ̺OŒæŠ-½ßËéß µü‰+c°ì°ã—Ÿ’£áìà9f¹çŽi†žÚ dÎÚ $ö×ûx—hDc¢½Ý7§ÏÿN憌n£v5÷{˜þo5}/|‰+æ]1Gÿ}]Êùû@¡×Gyc6(+E½vüåc H—­•jk&I"¸ç²<Õ/)NÞ4÷w*>zÔënà±éŒ8Î?ç0<zZð…J;Þ&ÃõOXɲㄨUÞìïM¥ne0Ò3—‰ëëÚ ˆf`õ) ô|¹ŽùômGòä¾Ãs58Ù®COw\ØJ›r;ô¬ñ÷Ü­[L¢ø=·JA©(—ÇIRùi7 íà ªØ‹‚ä°ïl#f™Î€Z tÖÃÍz3ލ=?ÂÅ.‹€zÃdÐÞõ OJWãQýàïžÃ‚ÂÔrÎyœ SXc‰ Uùt”ÕÈÈÒͶ:hr<›Á‹\ßCeVö<-–K2ËY2 îcE˜ô!®'gåmSäW€ü÷™à÷ü( ¿ý mÄØ¸¸xÔÞ =šH=¿Ï†Ödeô‘ëWó$ÏZ@âxõï^÷„#o±^1 ßn/S@€¿…Äý–ÅÉ+­à¾T%ì „sϲ©®Í^úgæk®1ðÎ\Ó òû´Y‹ý (š­‚>º†p°ªÄyf챓3\à Ǟ&?˜À#Æ©(Í®©z˜òΔ n®ÂeJôÉÀRNG\€¹ÄnÄ¥¾18±í lâgá8Á<›¢Åjug±ÒÞ¿ð¹j&ó0ŽÄ!ï­ü4ƒËjÑÈž&ô#<š*ñŒûx þýêÂÉ%·ã«7i\ ŠËâ"¸–1žKÿÒ+z Üü2ˆµAüž ì§´Ç(kn…Ý2œÚgvh¤ ý$°nÇ{ô?lÖ¾gÿmX|‚œSeKï–±K¥®äìb°Â±¨Ü|ôzO¤»M@yÃd€£¯¸†qq,~¡M ù€NaðùXŽñŽ¥‡ãwF†NŠ;=ÝÅøaõmuvg~µ[!+ä>½ZèA«táx€úIRGÑO°÷çy6Ã×^ý©Åüsëï'ÐnKƒLÝn¶ú…4½`W‰¤-’­ÅÌ7UÆ>á8Ø Uõ´Yl+g§„¿Œg“îL5t,žÉl›è  vâÕA|)ÄÆÇ±„Þ0Åô2¨L¡;E‹¨}€îKàaÓ@ [ôå(ðBYÞÇRlí&þ¼Bb²¤•¯—ª@mæ£íÞEô‚õ ÷{k%.[³ŒÙäKBú¹7à}ÔW©gÔø‰à½÷Û ü‡Ý“xŽëS‹£Íõw@rnDs·tqЉ°§!gìÔ&Ó&(Ó”JÈwû ïÄ3¹û‚GI˜Ý\Óó×%Ø’øËûžãá÷R¸øÇdlÑ]DW†„áϨ“¯!ÍœwWêØÄ[¥>žÛÖ|âóÍaѶ÷܆ QÈLb . Ã“£sg¼ã'+¾á–­$^»h¥ðIÔçTh½g6ÉÙ#ĦÏt¥*ŽòÑ¥¸Ék3ßvÅ…CÚÒrœ2) óîxƒáá­Xuy;SøvŽo»Ô”êDÍ·“cWƒ±ÅÔŒÝ]·˜sû>ƒØ‹¸à1§ã c7™ñúw×”8/í)þwž3xûMÞ’gQà÷u®IæÔ-cWä¾q“«§S™ýÃpŸ?‘mŒSbægò!C4ì·€´…c銣Œ ¾ïáÖw¤v« » ….»0·ËŸÐoªä½Äü£{XÓïà,ÛÈ-[jOó7›2¡AØ3(ÆfZ{2NƒmpœÃ~žP W;b˜©P½V¶˜~”º êàˆ!‘ä­}ì^çÛ$iîÖidð.Ö$M¾ŽV0KªÀÇRs@ü~ýÒ^Жµ`­òÙhXs ÛK¼8Ñ1²¼7ö®@“—ŠL…­ÀCC=4ªÓæ­ G%»ñ£× Ï?‹_¢¸Kñ™ÿŸA–Ìš…'*Ì19G–|ÙL_)ÅòÁ"ŒÔ ƒd³k(¼"ŽŸÉ‚Ùƒ¤tÅ Ø!|…ß-ñ'•GòU€”OÙŽ…wµ˜Ò‡A Ÿ÷ŒÛúu;Ø'ÄàJõ?P-aCƒ&‰ÑšÂ,­Jœ÷MgWK¤Y4Ö#ÍR[gaÙN'þ³Åb¿÷ —L?׫µ•h]MÆ9Õ`¥·—ŒO~»‰«ß<0ý|™Okz{ ºü•áØÃ•P;÷ˆÑp¸à¥ [Fµ¶†ÂˆO#qÆðÅM]n=MKÝ’ÑÿÃ8PÛ/G½,ÅØÙºÈ(²`7sóÛWëæ«‚Úüm\’Øðï[ŽâX†™VJLÊb;ѪD•dövr.ky‹ê—?`÷  \/™EŒáEøSª/åÎñ‚Ø»]kÙÉ™—ÀœÏùI\d/kÃÙ²mÝ`›ô†|xÃÛ„â4.x>œŸ# Ï=?↎+P1·¾{E¢ô’.œ6Ùš†o"íwŸèµý@Ÿæ-ðGm5,“pc{O>‡©¯ éò ¾Ô¥gZ-Ž¢¼æRlvdý—à¤ô±lEj<«úa »‰Ócq\ã—™L.ò:‘ïUg BðË <åÈc¶Þºll`:œN¯bŽ÷¹¼Kw¨PŠÈ-¤t41Xø Ëþñf«Rÿ¿Ý 4A<Ò„`ª¥}[Œ*ǶrËïÀàW/ññB[ñžqéúü½í&l`ùNX]²œ{{IŽ“äƒÇÙEì÷–"(1žM 3Ä Ka„ÞdÌ.ØÆl›À^.x¿¶ãÒã?á飮©ò,ìÜÔìprÈ–X–öâ Cö&v9S+XÀŽÔ7ÁÜ)\ÇÂùì•‘1Œ¢s—l…¹s1G/¹Ô~l#€|§qÍ‹V¢ò )ts ÷¾T†Û¤þ.~VÀ´3Ð~ž%O>ÌÚåLX ÅƤ§Á”v¶bòVY¶¬ì—E´K©§~t„øÝÀ-¼`IJë$˜ûe)èIÒd»þ áäí7m ›\׉ ÊŸÁ:‘;äWÎxºÍí Â~+¦c4†7”°™6xVÁf{K<țÎx{bÊ?¶tÜö)ÀŠ¦î¹…žU `÷™ 8 ñ˜¼ñy@ÞL—m8ôLÝÞg½ÛÝ`y50w¡"îvO6S´3§LúiÕC8 {žKÛ±לæÖo=Ú4Ó¬3#ÄYà—ðà lž—ðgÅàb¿CP{{^Œ° jSÌhzE\JO^{Œ/¯`À„•œ>¿êÔÿb íOªÆÜ»´¡(‚Ná}¼Ì®JÍ]?Â’©8Îï ”µ VUle{¾Çû|SNjC7(„²"%ìlQ ‡ÆPÇÚ4Ìì÷÷j%7Üv•;é½’ jàœ)’ôΩ<¶ÿ¹ T{†ã&‹“8rª åw^Å‹ßìYZX>±’°c«GyžË"ª ¹è¸d,ºìÄ3ÆÂBÉ`Œ¼‘ I—ÀFÕ›ª_J¢¿k²Ñò¡(Km†¤eàÄ ZbN÷øÔ}þ9òòU‰*è±w§fæJwŸgÅ1`»^Šš–7@£æQ\=eT|“¦²­²\mÆC.MûÌ9 BÇ/¦f¬uI 73Q†~¯àãû'pqx*»ØÂœf–¸©˜?pcJžáÑoNz{Œ™{ˆÿwžk-ú…Ë JáÑöE˜f<žÁiÚ‰M²sÀpý,¾^ßy¢p@ ¥®~ÃÒª8”3Ÿð\}Ø´E¾4ߋᠴ­¿“XýœAe'ä¡pÝ0×xÓœüþu¯DбàŽþ3Ë»ûüðGp2ÄÎbAØÄ„aŸjÔ{9‚æIU@lÙ z©¼ä5ý¢ZÆÑÊÙeØð9i¹¦};or–V¦ÔæË,öC÷žÙf¹‹—2ÑåT¸ò6?y²\ˆ?ŇÚ? Þæa¸…^ÉÐ]xDWiÒ®Ô7·]Hý}Pñk$Q<Ö¯÷ï´È w4¼6‡£5ï ‹Ár´á7¬<)Ëò|·bºçxòo‰Î~‡´ÙðÀ4½­OKFµÚ€c|]sm2¶ã–-ÝüŽ7æñ¬HS¤‚ÃxC\Ý_£7žÄé©sàwàb®DÍ ntÙ± ½„¬ìJ‡ 7•¨Ýí6HŸŸÏ9; róþÅ‘„L ˜§ÛÉ)?³¥&•ÚlÅ^Sœ³W‹WÞ#ì¡A[°Ã`›6 D¾8Ѿ|`½…é úT[ñã d¬ÒÁ¦>u¿u ho1ηGõõº[𶬠7åo‚È߆ ¨˜ÌÞÈãìĉðÔö-Ìu»ÇEŽÓôÊfnê†h˜y St¦0›ß£|/ø09î´Ÿt| Å õ_¹›³îBŽéQ®f³"5ú‰sʸš—¹Ï 8«S•È<*#+¢<÷¼‚éA„N—ÂôðfXn/ƒVã_£ûî%˜9œaw£Íï4j&ÒÂ5dÕGM¶kÑMN#U’U…îÁ°7~x*Bž¾½o zE„Ȳ»üŸè=OŒ© kuÔ9X¹`†CÖ¨ÆrÚ'ÏÏ™ÜÓ¶wBÞ틘¸G6]*@A¢@vó©~ís oùŸÂÎÎðöÒ@>‰¸Õ‡«c`½¯óuÎâ«©Y1SEJ+¬fsþvtÿ‹ິf75$ÍIÄÁ2ŸQ,ºøä\Dêïóì3àÊ}å>û­c+Fô˜ ƒ<sp:­ÉÝH|UK lå^¨£©²j2Óž«0K •rAÎâ>ž÷`¼¡ã?¸Ä0ª÷°ÙGÔ*Ÿw.„àwÿpô¸XÂÛi½“*øÆ«ô;‚å°÷‘-½ê#?öã¯Q®³Ðs³ñ…_.£Îí%T¢à>'xcõ»ó†_3þ‚“æ¤'s[/擦ã`Ó˜[µÈƒš¦®çÔý'ð¦~úæÐ¥¨E»B,™Ø¼#\Ç£ôÿçrá¿qUM÷áÆAðN¢:a÷^#œÑt»F{蟕I ¿r&'§µ×pЉh\&FWViQÿp à‘…#¶œp÷^´S”g#2ÒÌÐi&ñYzƒÓ0K„a™‚…Y§~9üá‘ÃÌvF!éTÕY±ÛQæ´µ ®êNâË?¿„ÇôXü±ܾ †Ÿà‰/.V qª­s^ƒî­¶üŠã²¸qà!Ô½ #ÕóóàÇ%S⡟ʨ)DkÛ³8kp*»/¡o‹{¬= ÖÚ LþÂtö-¿bôÚïÿaňôðêÆX¾ú&Šeï ë¸)Ì#ÉB*3Ù•ïhAÜ"øõ-™ë}¨E­ÞÔ”øV¶ïàƒ ‹™Â³70ä¤ËŒ•iƒw1'_ Ø'À&l;§‡Üñ½s$Ë$jáUÕ èöô'Dž¶…§ôÕ­0~÷NLþÀó~½åº“*9´qƒü:ÊBäòª× ³Z—ÜèáôÅMøå ùïÆ‚²ëržÚ1I¿`4=–\—îàAA~°_É“;;“)Y©²˜¡GÜØØðÿ®å[jZcÑ Ú{‚äØexáçÜüã(î/ÈÀ˜óÙXÜ• /ÄÑ…<#1[òP•‹â/$Ãå³È\µ(¼8Þ‹^›5ÛÀ¥ZgrÝå’°5}TwŒC§ðÏ„_É1Ú9 ¶ç®Æ1Õ÷ðIè>hëÎMœ¸„hÿ¼Ì«º½{®î‡#iü;ü<žb‚¹!-„ g_bLØhV 7¿šB7w Ò†uñ—–Ùa£i ž¾š>­!çßâÈž&ÜíÓÏM.ˆ[r—$KyÑ÷ÝnÔa_ ލ^Bg‘£$äásná´øq-©ðÒ¸«£ïÝRŽæ’»wgsŸ-Ëá¤'ý#ü¨¡Xq™åþ¾]õ‡ŽgVØ)ÏcÿNá>Ú-‚a{¼*)ÄTû[ñ¿yæ_m½xtýJ¸£7 QgŽÁ`à. øz’/:(Jëï ų.B\V*^Nºã\'Љ•ZXt%ލP̓—ÉíœÃ˜–‹é N@ÛʸãÄ6\v¦ñ^ütšŠJd/Ïžv|†“¯£9… / $72Ÿ…âGŸpTg!="쟟€¿¶²s%7Á“­äÜñ×ðúäÞA7HRÑ‚)ô“öAø³hVª­…¸ˆ¯h4-’›Ø.@ó4fѹ^O¹‰sä©Ã¡6.Xtïj›=8 éRù¨þs û*?Â{YÖ5TveõÍ»øúu tˆÕïèq,Îî9øÜ‡š†<ŒOzˆ·#¦àŽ«ç1w¯ ×XO‹ßµ‚ìýÑ:~¿«—Ú3ƒNkÕbFLšªh£zM'\žÛï%âjÛ­¶êWÉ6IUÚä‡&û Èœb_ºÉènVwgÆË&ólþð°Æø«:Àmg=YªÌ¬wÊ>ï‚¥âKal Äà¸^q§¿Wä®ß~ŽËÆÓŸ ‡tÝ >\‹F½ á5­Ü€™Ñ4£a* E&©œÅé?O…–Åh#F2â€ãeòñäg(ï(ƒ#å°/ºï-‘ +—/‡ôÐN_ɘ}79 ËwœF²¤àÔeÔ²#8pÕ@§‘/ÖÔ ’4 ‡"'«ADŽÍ×úÆ5¼?Ïi¦›Rß‹—±(«½“öà†/¿ð¯Ù3¼=W”f“N+]ˆô÷ã·Üæ™Mª/Kã`ØYxz€Ã¾C6ø+%ÿÖ¦yb=˜;´•í¶HdKjnWÍàE õ4{€ºßUQV.¿™3³…{Ø–)ǹ«Ÿ^á«?zT NpâùbÄ ò æÞÚ‘2:Ü~ž4dbÙ"Y*qÄ_¦cæÀ|ú$d󛣯¢k`ÿWÞ{…rNÀb9™ýÈž­ò’fïÛbÛP tnŸ‡ßìn_6Œóä¡Óe ·ýæDZ*¡C7Q£;~ÃÊëéñ˜r@®™ýø‰³ Ç”IvR<éïÃ…}»…áËLX’¤ 8¯5Õ^¬È>›šc!zs”teìÒU¼ôð.db™F$L%Iç? ÿ Æ4‰¨µÍe] ¼~ض­!ØöZ>97€kØ÷Z_]d>É»‰–ºû²¶™Ÿš~ŸúëZü½‹b `Â2CØ}a߫ʑU÷zñ 7È̃o!ü¤ë˜xÙ„ÓÈ'a4|(ë~H£Ö£{¤¬»‚ûT vw¿¡êî{[(IéÈ Øêßå‚?.…ëvŠhÅ“o¯'Þy'làBôîéÔD0?3ž hÂŒ•2,÷C8™'š ÷o+p5£½rÕøB.ÛfôþÞmÜ‹Äd=›Ig}| qñ¨#7öÜúƒ²×šAÚ³ )36)]žêp"G–ÐyKXei5¤ýÿ + íŠ4Ù6œŽCãh¨‰>‹iƒòoü§£ÊUQ;û"|“ñͶSÌd£x¸ËB]¤yËY,ú~Öâqsy\w|Ф{4£É¦{[Qõb þžn;îåV‹ ¢_¿ °Óvh–¿ŸŽÛ6¦ìÅ2d'ÇnŒ§³ax.±Å5書§ç 8ÿ'¶lVõÜ6o•ÒÙéé~øÕ)KVüD‰=.?7ÐöÊ÷°û‹ Í^’ ʼ&ü—·ãì\.Ãü›/à”-.ΚO>-¢óÃv w›4‰‘ ‚€•‡xÆkÏÀœBÚ±ƒ¦õ£nŸ0»9lÇÞºcý£0NOu7]ðF«+D寺íêïy×PÑm‰û52°úÞbÎQ÷D¼±!§ßHÓ°ÔðØ`L#xƒ\TÉ!<ò¢Ž,·ÉäÒ_ZbAÎ;XLr‰Ä” ¼¬²â,×㤧)°ì_/ì›§Ï;r^M`Ñ}¶ûó¼§`ƒýºœ•>lr^@®WGÂwóí0{"“;{£ÁgwHçËÃñ§aœï»vrZD˜o‘¦¿›Ê‰|Ž4,’ c{oÂ{Q/Ñ÷÷t:Xœ Å¿ÅáL¢6{A«±Â—²ýû³ 5¸ºÙÛzûyª…ü‰‚YçÂá¯I8Í£EC Ô¯t&;)8®mÇ5²7±mr æïçœ÷WB{C÷öõM2FL L‹bK¡*œ˜xåïtB޹(œYt ·à¿žDb0Ó’ªÔIr z« aóqfZTžRÊLlÆîíêõÐØ™sJ5Aëy1œ³1§Sµãø;¤2ѵn,MÜc†Fû Ùw]uìW@Þ²,_PÉ\FsíöÉzük?‘ÕYÓÍ ý˜f¾.ä¾íµ)ㆽÌ`DÌê?NÅÇütªì·‡&ž8‰M¢c˜RY+*œ›C¯iz`ûjAÆÅ °·¾qైÏ÷˜Lɰ$W×çÆÊKŸP«—ɶ·:¸àÚöiXãþc«ñYÅË4ãËÇEx¬{1v\Ê#ó~F‘%udšÛd`èÔudUc bÝ=_<w²3ÀY+…îÖ÷âŒ÷Û›ˆ ,Îx&ºxeÒݱҸÿ­SøŽfÊéôç¢Xr_fö) †‚þi˶”JÓ÷:þÄŒD½þAú£"r  $M'0ú¿$µ"‹Ëý e£šæŸ;7VƒÅ$ݤaH»ÌnqI]Y°õ‡ ¡xØ9ÓƒÖü_Ö(2†åiwqïjwƒŠ….³½Áœ1ìÙG ¹$õÎvZœ=T‡Ñ‰ËhËÛTb]ƒÆ'|%Zª9ýYNÿy3CÈÈ=ŽkgÕÕš8æêbºGc Ûr”9|I¢Í…É8˽±±tµ›AÙ-”%Ï’¡éÎËÀªø0I<ÏVn¼Â9Z® RßñýTSqÀ2Îyƒ¼„7ü|¹ l )‚äµ>TFø!{°ƒ-^TÁܓቮ9SŽNÍAw™9 ùNX¹åÖòc=¦.tç2 ñ wRéõQNú6à-ôŸT£mù»iw³%ÓÐn!pú2Öuoæ{¼æýq>4âéþ}t,ͧ•Þ;é™axÑùz¶Ìd®ïñÒ÷Sh/½[´ªPPùoßÄ('7 ãÃ͆ÛkÙΘ9™*Ì_„OÆÏ¤+V×Õ€4â0¹u› ±k²³/]Ž&Eàms…bƒÈ³ƒ3èé7ÙØñá0Zí^@ÿ=ÄOA»|¢ƒc9†_FÞÎë°ÍK”I…OÂmu|©q àñ° §‹ŠÁÜõéãñ¬½Ã–Îi¨¦‰ÄŽ^¨ßÃV[ä³ÞŽ4\µó4¹?Fâ4ʨvH5ª„Èqµ‚S¸c“`’F8h<âû x²s§îCQÒ}òF­š/¿Ä”:Û¶:„™°xž µÙþò*b¸â Xüe;êO@êç¶úÅŸšp;ÞÂI7Ø0b²‹ºˆ®"û8±7¦ªcõúc½†ôÞ€âƒ}ÜœÄÔ§£|ª²ãôYhÜW|`› mý'`ö¡i”¯›IÆR‰-æ‘àˆvr²°nÉ“˜Ïf}¶ w÷KÃ5˜°MšÆZó~­Ìâúãó°iÀ«¼=àcÌC®ÅOŸ$G·@bÂ2ú³2‚³÷º‰nÈçïÒaë ¡Ô=]öÊa±Ü4 e›í™´ÊÌü:îSÖ©Ig8!%v·gféŠño$öÁF×eû@bS)¿$ByÝål»øsNÿ›9Íþ|›ïÑ‹·Múñų.ͧÖE-‡ëòȸæ·p’?•]š³»'ìÇà“oÐÌ\ˆ z²â¾dˆ?üÝÝÿ£rzý–(õéKeãÊ–0¡ôX×R†œ¥-çâg“¹t€gŠmd}°-€þ=1™-¸¹Œ=ϺƒÝšTs"Í:•ÓXõ† vYÁŸžóÍ¡‹‹Ê°ö>a¢Þ•è–šCÞ ².!KöÒ`5Ênáó&Ì2£ƒß Sù¾s(WL7Jýá²(6^‚aæ2¼wŠýJ•f~®Ç9'›c²ï_<ˆ‚NBlÅ3¼°z*,=6ŒBCó9nz¿qÞÃ0¥e3Æÿ“ÿK—qÓÖ$0X7~‡¿w‹áÁê.bVu™ßä¸eŠ¡åÝ¿ðÄTwBckÚÿÛ(üw謩€/KOÁæ?Úžvˆgg3›<¹éFœMþ1x®´­ŽójÊQ·ÿgØUŒGo‰Ó¾àO°Ìo1”´ãŸ†,µ<‹OÕdNF/ÁãéE”¾§J=&IÒ[ÆÛp¥SÈ´»àÔ1Å|: Ó‡Áêcöê¤cöµkpv:«[?—›²5›Ó)]æsײ€¥{¢F§;\¦·¯k±•Û3¡b†" -ÂvZLg¼Íã¨JU ß7%…ÖÏL`™CyíJìÒÂ9,R ƒ¶ýë@Í·^ð'™Í‹0£â}Pun%¾²˜ƒb•6Ô¸nÓcªt¬µ×Úˆ/Sd©îÑz&*+È^Ïv éÂô¿Ùåº{‰t#ÍÀ–ói^Ñi–ÈlèÛ vNI€>¸QŸìñ Ú<áP( šä‰.<Çvä`ÃÚì?|Ñõ´‡:»öÒÐü·8Ï"•‘Oæz…CÐxe(}.äMM`ŠÊ¦ô»»žøªÂb¾Ð× ‹ØIããôŠù0È–ÂEfuë6ÆÜàCïO–97¼Žš2;jaÚÍ3üŠSØô]æ4AY’Þý”ÉQ“FøDE¯»³cWüùÓ¶NÇŽ>;Pß0†Mºw–w!׃–Up¬F‘M±aCç!£Fž-ƒËèñJZ5•;Âëã–žMçŒÀ×îÔR@Žù>\a{1%Œí6©Ã‡ u·”—º§5_…Xól΂z8N‚6y ÁæõÀ=Pb‹•z9£Þ2èp»„Ñ—•è½Nþº}?G9ÂúÍTjï\&áýܹ¢p¶;Q¾L¨ýo™Óì¾Ë}Ó½‡xé°aõkø3¶b}ìÀÖBV¡3_ÿ‘¶áïGÍÒ³8…™×ð¹?s\h÷rý3»f‡ýbßÉl_]˜àÞIŠ–*c–Í;WÞã%A0x;WŸ•«ý„®´¤¢LÉ„;Qè«Ëx'„™Ê”\¸œ$ Ú7¦±£â¸¥¤õЌݻӊ바ȉÐÃ[¾pæ!¤e®ãÆYm¥Ÿ¿‹QS©Lȳµ¥†Jg æ« µRQeµ ßqD¨Oùž K,gãŸC†üT–²A„MP*ÆÉ.6¨•7œBñav²K†^7Ü :)Î`ËWœ¿óéÆYöÌSB\ $ÐÛq/}ûm6!ð=1%nL`-keØËSzºr3ˆª·ùxœ—ðM—.[Aõ³2мëoduΙSDβ™I Z4f¤v Ñ1r/T.ßwÍ{ -Û–©æŸA­÷ÛØ±°A8pÈ’ÅO\§œã8[º¢Å–] ƒû¢GÉ-þNh®?Š! ¿à$Ý?8oÉÌØ)Èœ§~}oÂIüv˜´î,˜ÏvdHSådî ŽÛŒrS¸k AƤܻ‰êþ“Ÿµ¾ ç”©ôÞ—4ãS'¯’·h àM¤×Å«!¹Ë†zûöqCÃsñLª óÛ»• vaÆOß²W&@Zþa”ç Ñ×¢×ñ˜@·‘þa÷o&ús'böQ¼y(“¯àUáW˜o›ìüt<·´Fn¯¡oÖ—r—B)¿¯¿~ Á‹ ™î¤Q½Û¹´kÀì¡ôû3µÿü¥yÇ|pè  éò®L|¹§ýø=—\f±™Pyâ%5å‰1§ùZF {‘ò´ðj¼7cœ¾_7Ró×yüâÑ«KLòÿ¿'2ÓÑeî›7Û,Ñx=Í|uƒHŒQd?²ñÚ^#¶^s+ ¦Wiðœ6òl’(ÅÔ"æoß ÂuchÎò ­·†žåŽRODzqÛ³ ú´XUÝT¸~q±ün¶ÂOž:ëù¢á’ñ¤åC){;,jw§.¥‹¹õ»4Øñ•™X'¨¢–à 0¤?s9sox’s‚ŸjrŒrù YºÜØ™ûÛoÃãµËÙ2ñ…ôå çQŽvÄAåÅ[ÜÁôÊݙ̕5P^£ #é?Új»‡ÞŒšb3×˱ÏcHͳqô€ÐK´àQU)Üç6&¿=W\ôàçg¶ÉU^žÞÍiliE¯…Gq{Š3e3Ö+‡ko\ŸKêÜßHºÔXŒùìyŽÂW®ÐçO ØÒ/W¡CçÏäéäú“¦-à|­—IÒìp3äôŽ‹ÛûNZ„wþ’¿ªû îQÄgY°õ1qî/_vnÀ™ „<äÇžV"r!+˜ Ïš]ón …¥wÇ3[Ú€Zþ0‹{Ƚo Âé²ôä‚)pÄ·ÇT^©·z-ÎlN¨±ÝÁzL#ïZ™< ¾÷Cvn&ç~]àõgüä2m;ñêl 0ªv¡Ñ‡LaJÁ:T¬É‡)+ŒØ–¸¦Û}£ÏšUÀ9«xôžÏÇÌMc©›º <¹!Ç-xlÉJEÖCov éꛎ7Ç™³ÖøÃà!r›Ëž9•¹Íú‹¯ÅÂhAXlp7dìs. É Òƒ?™-Ĺÿàúæj,[éÁbãHÜàPrºÂWÆs±>4ÅóZ†}â´BÆàËê±0A—ƒ¢À2rùæ_è’óÂé oxËe°ÌðÛ5°Ñ[]^'±F¬–78F…âßpCM©àL÷© mÈí%&Ä€F/YIŽ·ï£‹PÌ{;›çÏ”Cýpí·yüÃJ”FßçûŸx€×baŸÌs8÷.ÞÞƒQßAy·ªmTg¼÷åp£v,ݱfXب²°íÇéGköA÷&×ײÿü©Ýî+ó[£å°ªû yK¢¨¥æAéÿAæŽe ë%è’Â÷ðí^í'“èìW¡8{é/\àéK/Œ—âÚê{¸ow ü“´¢TVõËš× Ð5æ™À—V¤ Œ-Ñ}U$þxÅ„_>a‘{ûa×ÖUð95jôþ~p^$ÝÝò¡ÀS€÷Òq8Kç4‹,‚çn ©QK2{r©Š_˹’1™-0#!“ŠF¾ w âÒ]븕#nLèãeêb:  èéÒKÜ ÞõÿfµYTm<½£(C4ò™æÇdô«Tg–MqLäö*X*æE+å£ÂxAÊ7³ƒC.©´ôüMx^ã ö‘Wò9ÓVmöÖs.Ÿ®ÎåÑY»’[ùq%^7 Ýß“ÙéÔ½ç„7űYu¼Ôž<0>$IïýZAfútúäÜ–@Ç9ËЦ—Al1˜ðFüšÁõãXºÍû/ØNÔ¡!‡øæOíÉ«ñ ±ÃEžc»&@ßãwäü’aÈ:•B¿ÕdziÚÝ— ÕiJ—IÇ£Á~xU¦ÏŽn™ËÆœQ¢õ‘s‹ƒx¦2”²ÑQ |·ˆÆÜö¡Käió #·îØ Þ¶Èµ,ÃûDÈRÿQÞÒgJOéG‘ÝÃgaÂá •8¦LR¥m×0{ãR<øî0-Š\GÙ¶± òË:ñSJ¦¯”b×|VAÎå˜-Äqª»0`Ò/ؾ/=ñ £Ç$¤/gŒËÂÃSé/a{à+ÊQõúг³³¨žî™Hµ‰4NQe›z¡éÞÜþ&…u–_€qè\› ^ÖÑä§§Ø.|‰ g®#ÿlÄØ%»ùGãçÀM'¬º=„y ªTÑóÔlËÃab€^^ÜÅkÆÌkZfuoÃêg$¶RžW¼:‚VHÓƒõK¨œÍ xUý“;³ ¤`R¿´‰/§ÂeP˜+AmžësÖÓ£ØÐtÔ•5 K#óHD—'[8ÿ<î[‚«ï¶ÍÍdÅåRβ©ßÌgEîÜYëüÿF¶>Þ‹Šðg|T6$ѯzx+”¯à´L3&øD³êÍiÛïÝôϬ…tûÊ=\êã(v£÷}ô•cP퓵Õfžª’`Õ‘§U´Á|«oî÷*ÌŸý¶ßN&ÙÊù§c†qwßN¬8‘›ñé0Hír+¨Oqa ‘às¤aKÒ|TTÅ“ sk4O&:ƒî«[$=±—8Jjbú¬ ´3WŠšÏË€« àÅU{!Ì,«ÁÇëO ^±}ãBnÞ…µpŽð|óP}Û:,‘e{*'÷”®)3±ò34“¬WÈ»K W´àä ȱv’ëY‚£±ð°É1¸ö7‘û À’µ³@¦'— ЊÔ7ø3ô lØ1‡-ÿHšEÃÀ¢¨ž?Ž'ÝíMäÙµc¼ÅºaP[¾ ŸiÅ+û}ÿÁƯ“¿,äWo°¥¡#eÜÏ¢÷˜ÙÛÆ]™ [y;8Nf 9 ˆ«›ˆÇé0œ´§ˆë¯GK§?â^Nº„:mŠÜu· ,áßÇ]êÖ|7‹S t¨3ÍÙÑ™7¸_ÑQhê)Á†*…èCv÷ÎåPåo>lÛ)ŠVJÀëÏN§Jâþõæ°~•2çrü:œIháFñ’"A\ž›‡ÍQ¸x+' U‰9•.ì?¿g­×Ü•CkÙ¸}’øt’s=bZlÁãF¨2r$wÉú±íd‡¦>ºžEͳ…¤sk$±ž*Hë=.p×mÅòä5øÇð!Øí',S¥Ï_œBEjº!!k*FÏäóÆ¿‹ƒ+Ÿ·ã¦)4òG¬³zëÏ—À“¬kкŎN:•N.´Ä± F´¶»¯ý…ÖŸ|@H_ó6ÂÙΟ¸ÑF˜Íüna3désÏoúÎêUž\³ÞZ²*ã=y’åû–¿À´{pøÅWTáèK5ü‡sçe#›{Ì5¡wþ¢×³‹xïÇ4:þÖ{ÏçsÞÏyjlÉÛ†·$kÎ TšÀš´ìéQ^!Ž^;8.R²àÝ_¹¿R"äìªÁåópŠËWpËÔÿ«ó¼ÎG–»iJѲoX⬑5[|ž’–ÌNb»6ÿêœÂpl˜K§‡)ûͨe–8¿úó*]¸+Aïì脦?0¤þ™ÐRÆÜš>âyÕ"|V*N¼•hF­^2Žj(5 E×A·°Nbvø¿fïEíçÁ+ۚؽZ?">“;f—ñÚá6x5•¾;™V=S'o‹Õ—2> B—Þ@§Ë³yw•ܸ‘ö4‰X–ê`çom®™ðŒÈoW%íyŒÂ7ve˜÷_'ÉŸ¬­±?mIÝU@ÿÉž+Ö€¿šúØò}C¹[´67­ÀÂ[‹A%„S/[ãã±Øýc(”ÉŠÓÎß ü5忚´˜´È_| DÇ—¼ ¹Ixï?§€[Ì2áø°X¸­{,ŸÖþ†OÞ@“gýÂ<1QÌmSÙÝùPž 3CBñ€ž•Yf,¨pËa¿2ŠAüË,êzÓà¶9޲ ³v†Ü¥+¡yD9„îÙ$¸ñóË_X3u.‚¨Z>p³Ý&ø ô7ì=þ³}‘ Åøßì$ø.£Î{‹„Øß/J¬¹Yå”÷#. E¹u«y[[hŒVãÏãñÇÎ —½?.2ß ¸û§\0½Œó–âsgáiOâhsóžç“Ã{•ØFÄW&`òê»- ]±$Ø í¼?áÇ9b4Áÿ,d}ØÊÿŠW’-gîq48¿#µ'¥ór¢G•3Æñ—Š&8A}; vÍà$i‹È$oçvªû_°\¥Sp™«S6¤æ ò^”⦧ž¸nY)š®¯dÊV혫!N"l>±m M¨o¬ÁïŸêA%K>Ãì8™:Cÿt”ÁâŽD¡ôÔ\8÷|,¾¹=§üU&[l@ci!i—M½¿Ï¡fx;»¶T™|y·æîg’~²h»(;¶jÕ\Yµá?¿5Ô†çWZBÔ‘1ÂmÿÖáñ­U 1N|ïcÃæaVYTý­ u¢Üyá}8¯XJ4-ëÉëhÞ²8šõásó tÕ)Ô y¿àCÃ,ܶ@}”bG6\:@ ÷A¶Yì3›sR•¬?¢Ïw}‚þ‡p9l-ÏQ¡­ãjú'yñ·J¹`QN”êpõíóaBÚ –õ5/–cuEù®6–†/™„º…,‡£ÕÉRƒÑCؘ×Mnó÷²Ù%8*²—”W‘)Âsl§Sìký8&=1ƒ7Þ™@ã¼ßƒÒà>žíÍDÃF$2/8|‘䇠þh°vÛ œe̺ e¸¦YaÍðËo`´î ðúgÌš¬äùÓÌ0œÔ<…]y«I\ÅWñOäøV‹‰ç¢Ðu‡ ¯›÷ -•Üè5±nZŒ¡éYXë&ÎÝDÿ sÇyˆ¿3”g\3 ÿ ||9Smׇÿ‚‰ÂW?þµî6éŸ:û‹Lè¤Îípß4 ú~HP©ÎpXåeJkþ³£ù»á½æJü6@ÊU$dÑxß•%Ãðë£VVò³¯DÃèçð¢I‰ê-¾Hç6á¥U/á§ô0ªþ¬ý„'áÙ œù³YaÀ Ž_ ¡_CȸgâÓÀ´‰À•£²0Ïmnó5ÃÉ‹èF‡c‚áaöšËIB_‰àí‡:°=RnAH%+4 Ð5µMe¡BÚh>,[û; Õc¨ü½†ì:7ŒŸž^ͳ»ÛÚØçIe了:?Õi Þ—ÒØ¬ž³p~QqøKfôGûùý©:jRã({r¥{ÓÈdÑ6G}ó}Øýp3vjŒg¡#PßgN5gmËN]ýçŠA’…l¤v7ðMËáñ•rÁ_󋂬Í#Ù¡®øwñ3¨OœM9çÑuXŒêS­LvÀƒM–°CcøÒ]À^g©ãË£¶tÁ¾ ²Ó„ô…{ãGÅOì¾Å ²êícbV¤ ¿N]ÕÏàY z%£ §î•`l©(Åyipì/‡–ò·pÝò*+÷Â…gȆÊ*ü}ãô‰àO`Í•!žÐýe:jßš G„`;³ÚsðÓb®_÷„àò~¼2þ8”þ\…ÿy‘uDcAPáÊ‹n’b+Ò7÷5ôDåÁœR ú)A ^on!ß»p¬ó=\צ‰—âìB™š`BÕJỀ ¨À·»‡TÀÍ*W.Z³To-dCjwBÒålö–UçåᢲVüýÂM hÛ‰&çñÙ‹•,‚gÙUvòÍËŽ€ÃyE~$᫹2”I&ù¦'Y”Ê"=FÂÂ>`ÜôxvS‘ôÓëj&øRn d߯Sø @¾ŒƒúÍÑÀuïPO¡²‚r4Šë`# âà‘ŽwÛˆ_Øö‘ü.i!%oeøÃ'Ñð£z,ø%ŒAñiÔpzquæyOŠÉ‚.}ª1ŽNw"‡¬á£tÓٜ֌ÝöMϲܒã8íé[AmÇþÒ<@ÞßõDb)NÓÜ$ø…ª¼út6Kí‚gìò„f|~ê'¤õwbóqö×Bš&È]C‡yÌ8~ ¾/߇7ó¿€ÝI 8ºO—âº/±ï‘OÎU£T.Qï¡Ï!ÍÈGÚKñ)žï F׊ØÄÞ'ŒÛ÷’{+XhN2)=†®’ÊÆ°™•L15òSA³‰c{„ö&åh{7ÓéRk ž®Åö:d} ÿ\`I¾.nwì ‰ú*¼xù%dZ5 ¦T0§:êsKÊnÛC£ØK)þäÉXRœ2“?©…oºuè¿ü'ðB.JX&§GN®$?\ÌáÉÝ{ ±ÒŸµkW2Ýxj‡$¿0E†lTàëSø¤©ã˜ÀŠ&'!—äéAç…5eËûÈ|E‚g1ÿÞz9*&•ÅräâŒHe.n:‚FY¨òÂì}`™AІ¡Dm5ª„7¡¸ÎÌÕÑ Ó'ÁOÓŸà•¶;(w§ú¿ëóŒþ—`i¨N7Vá€Úu|°ø'éÔê9o^¸z 7Z²‚_®äR¸!·nŠª¦»üàì³`Ám}/¸_ªEíbƒQø¡5ëä0ÕŠ †Ò«¸ì”(å\ÏŠR@¿Û?XµAF$?€3U&‚þþ¡13|¯ˆÏMÐ3Èyd Æ»¶°­ºø¬RN‰ÃÖ z%ø±‰«ø‰äf¼öojNß ^¡ëcz[W…>š)O›V†Ñ]ÓÏÑOÇ=põåI8õ!)»#Ô¿mHRÏôA¤“‡íŠãöéðÕž§qqƒÕÌJ…_Ú#çáNÈ,¾¤m¼éÂíq”ðju}üu1=W&†mŸð… ο} v<¨äßfó+­sð|ñ¾øÌn^vo1¯]‡Æ.OáQ¶­¸ü 'Þþ‡+ï€ß=àÒ*sŒwÊ…ëkW í…ÚLñ]¸P¹©¾K?º?_Gú„¶èf:x'ÅéˆÛôˆiÍ|äC#cÒȃ†[4/¨„†œŠà©·ƒyãÀ-ò1-”Ÿ»Žïþ”9Íq<*ã)}pTp¢9U`\š†JJv\"ß»u wÇŠÒúŠPü:Ó5}{Ê6f†+á÷Ƀó¥¨Ûõ7â,î|¥C“Á`üœr˜ºªñùåÍh- ¿4ðÐ(n(î„MÚ¸mÀøÑ“HdõkŽê“'sO‘,ÛI‹ƒúÿÂÚŸø×~'\K0%žp<œ‰3.mÆU«_’œ»r´O÷5ߨ……eÃpK4|ho‡‘: ¨Qù×¶ L1ÆýV~P¿æŽú!,ù¥C'R3vêïBœ« .O“Ñ‘ý0®|$·ªœB–ï̽‡2‘‹‡8¶œ†yçëÈù|o>ìÍP:8^ ,ûû¯‡ÊaÜáW{x7®ž7 ¬røÛ5ïˆÆÅ£Âë¹Ö\/9˜:î-¨o+;'ƒqM½(+ kr’éD`½X-x‡ûòSS¿ÀåðH訣=â¶©CiÚ²M¸}ú%,Sëç·£ˆé&+þï{>uááÌ2,ÂàÔÈA­¾š4ïFÏ÷÷ ¢z&»frÎ- W?Ÿù.¸¼aœð9…õ[IÔ·u°±?Œß{ðQ)-ìÇUjzÛ+{^F=¼ãe”øëöÝ5m·¬ÃÊÒE¤_Å ÷&,a¾Ò¯Á‹Ï£3bc°uåP8X·†U| Íù:)Ù Õ=©æ_9Úò~%ÖÞ©„Û0›ìîZC¥/ªóÛºÝL{U ªxl¤éå‘P$‚.Ž™ðâx' ½¡Š§´ä¸+‡' tÅÞt{4t¾C[->Ëø!¾¾Ñ‡y{¯Á¨­–0Lû½¾*wlå 6ßÁ³ÙÀïF>|VêÀi7Çð§PùŽ‘È±Öo67§ ᯃ8ý¯V´³?F­¨d·¤ƒpÁÑ˸šì€Úú$«Öuv%4³Dǧ¿#i/]£bGÒÒÉ£ ¤4üçÛf÷·Cå…“ðdü&HQÄ…ÂR8)q†O‡Ý÷óPlcg±‰ešôç³X¾òPšlZžGœ¸ÝD r5æº7–ó’W\ýÁ–?ù{¢FôQ™õèýëÎÞm>F*Ç‹M£«kGsÕC ^ØӬ蟿1¤³a"µKË™Ž95|tÍ­5ãvq‰Žy±ÝÌÓK ÕîœÂ¡éuè;c'y»ñ ÛPÑ„Ùå!Ü."$–|o…z“*³+€÷Jž†=yÇ9û[Rã7c Îøû™ì4È$ó Lq[ÃV®ÄDi{û2жüŽ_|‚˜ðå<³ýPœDá}ûÔ÷Ò£IÊ_YOeÊÛáÅ›ù¸w¤ª¢*H¹ß|n–?v/[J£·½Ää®Ç fg!Xóþn-ÄáÑ_áàèR\sX Ï(Ò)ƒ|½ /N½; rco><Å:ÒšHÓ)i®f²¯8öàS“!T|oûé·׋ä`ØŠYøéîà gkóÕ*ÅKTP¹é'\¼7uÏÖ‘¸¤uôgÜh>}^[u¥÷1y²¨ïwÎÝ/;`å1ükåÆîýþ‹a•0»•µ\7æF¢Ó19ØjŶ‘“ªà°Ãl`G°WÇV.zJôžr’ºÚ¾-Wæ\¯*‹°ÇR7`FäHû¦CÏäÝN‹ÕÆ´®×ð_MèøŽExdï;0×̯™­7j­¤ù’œ8lèKhñ:­X¨ ç`ùý(äÆÂ]”éÌ'ƒ´²¢Æìº"e×;êÇq[›3ï/û± ÜÿÅ*/ÈEMúÏKe“g=†×ÙþÓpÊjXôÂVX±±ä÷I|ªß/‡8Qbfå,cÿbx&†NrEDaˆ ?øUƒ³`CÜ$C?RÔ5zŠr¯*£F-d9ÑèÜܨÉ!ðâÜið_lß–ì¥Å1öüÀÂËäl˜/J˜Šã³ÕéO%QºgæZ:ÜÏþî½Á2ÒÛ˜ûó—øJM•Êë®…ÉãZHê eØÔ©Îgí=2_¦Ç=S§"NTE¨ ðïªE¹Ûp+q4žÞ¹œÚ̼O“+]šãc€ô³2Dot`¶ÝqàíêÌw$ÁÑÔå´ ÄyT×EØ·zš@Éê;1ŸBáB,?½‰žD;Hñè í8Ç+’bÌ„aêãQäñZvp\*q×8?OOâ•"§¡©*f(Üg?ßÛÓÂäb’<Òä2j¤•’ÿVaŒÁrnk0ælûÆî$ÎâQº»x]oä„9™ñÂèØÕð{›Ùãä»ã{„?œ­è‰RØ}57=„:¿$ë¢p‡Œ$9:oŒ&¬¶ã:¤_Müš}SË—ãùŠøn™…·¯"_®Ú›ɦT69’U³,g*©NZÅd0oî]V£f=˜ç€ÝCI2såsò~Q1ùioƒc‡ÂNs?ì-Íeqç-øœ_' =Ç–Ûhj¢OšI_•ŒY—ý@àqÇV ›Ï §Ò?VÀw‰šÖ€QTýæ&™WÁRÚ•¹ÿò<8à wœãwEe‰ëe𦹖ˆr¦/3î°M¯Ç;>½9ž×̽Ù!øªp!é^ðQã9øMiÇ‹‹Çð­^Q8tU,Ù(aD/%$Ã3£$”øæÂ«çd⢠98?àõÿëP«]Ò¤C~a>ÐäïUþ6‹XÔÊ{èj2Ž'x™óätÛÿjD£ÚU_vp¢=^›öþ;Dhéj"ÒÁWýVäG×̓Bω`!ÊçVbàiúÖ"Ÿž>Gž¶ÁÕ2¨¬c ±í> ÝwˆxÆ„B™­:°(ŠÍ¼Ð†WvaܶàßÔùÚǰ· ïg+7„¼—‡W¾v¢aiá’n,½oHçn¬F«Ç×àƒÈ(ª÷¶Ÿ˜È·Ü?c ÒH÷ÊD\(o…“'„ÓMF"t/#/u{„¶æôÚÁôvGÐâM6¬nÄò—~0ïÛ'lìD^ _bx äzÏ2±›¢Beò42ö@^å%¶µÅVXØ`´>¤v}eâK$¨pG,æ¹E`€‘$œe»ÛÁ×S¸–L,’Ú‰Gò²iÀýrÇy¡tSï]òlÞyü«]‡E„ £ÈO7Xð‚Gæô‡Ä®`mAÛ2#ÈO‰÷U‰ =A ÂbÆcÿœl:ïU9Ü"µäæ³ àòF“lçñ¥z™ØÛÁÖØ,…Ž¥ÅôqÑdþxå¯61¡ÈêØáv†˜ÁPi;´»¥Iõ†ò˜¨ÞòB‰Z“¬L·¢¯”cÀñ‡ Nñ oºÅ1Öüºã¢7“yMRðËí#ˆ›Â›U®àC‡…°Éã6ŒÜ˜Fî^Èàò NÓêõ5;Ý\¨çÏØöR"ã–Ðw_fá×ýììÔ90ûˆ/>Ôžw,‡@œS$®3òÆ“’z4ô×zveðnEÔ_$žÒÇàt|¿Õ‡;ó– í¬·pËÀòj9÷æ s®Éï5ÞZÝÂËâQlòŒ›ðâ( û;–>Ö³ú5A(RODÓLèå³ÊüUäW²YcN7WæJC¨ÛÉu Ý4£3Àîí¬éÖ¨kòäÖ|Xõ© V ˆõØ6ƒ¹c_›n| NÏ€qE>BÔÑׄDmƒ’îxÜpä{éî ã¡,°{(½1›ãµôYtÀ“Ãå¨Óh`óQ(&£@Uuth©I4·3lÀÀYÆ¢¬™ÿÐP5î,.Sªr@žJI\Çã6õ(7Z>á e?jpÅZ*9D_/ µrô’}Y{àbµEÎpøÝYKJ|à×þ$X¸à ñúG’ŽÀ¦v˜¥ÑcâðÑ÷é¬ü[tÊ´ñ:ü¿ºÒKJºØe#ºvƒM’PþÒRüçúB³Åyþº[póQ(_¡ Æ[NÝE÷ŒÃxÐ&ú3%øŽÇÿÞ#ø¢ä7êd0¦AÚ°Ê1 µß­Æ÷±dî ¦<-‘5Ž:C Öaˆÿa⓹%G•âxËdX³0·} $Â%öTê€+ùym*¶€%5F3vÀøE çaˆ¯F/ƒÆ{`Ñ Þéÿ¶­ÉÚü¡ÍUˆ:iNmÿÕ2£Óÿ°ª! ¦Mº‚»l£ GÐýzêbŸ™þò1ÂìØ ƒ}n²ôu ím Kí¨ÔÒ™ìѱ«ÂFõzt.„¢MUðzŠ$ýQå“ì^à»ÒOØ‘¢BãÖçiÑ lˆ¤û[¶+›Š_‘)K¼k¶ÞÖÄÒÏÉèø5dÔ¶BæH\oqhÝ:D[ –˜0‘ÊQ°MF›X‘‘e‹ ët ~»Ã7ܾKÛûcéT¨áy¦+лz]3r(}³*…yxr´úÛ)èÑÅ+C•¨»I2½£œEOÚBLÜ#xµŽêÇLJ%} mDçõ42]-w¾î<7NÝ_”¯áìñ ü~·,µ[½˜ë?z‹jc+ Ýg"/øÓËÿó4;ëØññ§Þ' 2zØv}"•ßhÓNÔó)Ò™Â'#ãοšh¹k9w5»WÑ é‡`òšwì€L)ý>'‚ÒMÃןçÒ¶/¹üôãMÿ5íXf?§ÉU€dîvÙÄ’j·q4vDrÿìr<߆.6ë©“¤Þû˜ “‹hN°·z&Ðþv +—bù‰VáßÒ Š §'ŸÀ žïÀÕýgéTOWjûNŠëU¥2ië©Ö‚ Âí…Þàwó­ãîg^ý¥EE_’iñ$Fו:Ð'ß×`àÈv_kÉÕÔ´¨W¹&_ëÄçü)¥Ö¤¶n0êmÝwÆ›oo#dálrqi*h¤ùÂD¿óTì`2éKlüø©Üse0Ü1¿‹f<ä_Ñ –ÖfË.UϘB“þ ™¢õ鉷ò&š2)ì×Î$þŒþÆÏ{ø›}¬fd¾ßd®F§¥ÇÐssÇ£ãEn—¶ƒJVLÅšø¼õ|>{ØHáZ¯=§fÂæ\ƒ«]7pÉÍðêJ N¸Àû«a¡ë,|Dì¸ôs!¶É„ãg«‡(ÕDù½¾.R)ï 5Ã…"Óß²{7õhxÔ0úeû9æ~_®Œ)¤ËZÀê 3ô¹Í­«ðHÌæ?¿5J ·ä•3l4YñÅd¼æ·œŽ¿&ËVá”òü ¨·£Öú™4^]‘­³ç’Ñ"4ý„ —O  _ØUÛ8|½ƒÝ:Yx°Î”Îï¿‚ž—BH€¨*{5ŽóC“_¥Ar·1¿ä·# ÄøÞHyî{Þ”ÛºÊÒkÏU¸ìéÉÂg×ÁEI^Û)‡_”ðù‚/°ûü7ÁìšÜa$Js_—‘¸«–ðÙO³]n iB´Ðþ?ãpØ”ØÉÆ`ê©"Ô-ßZó#a¢ñjçÜ4n"—{«ÊnMAYñ(^¨n‡ÎBžû÷\ÊmG§ˆã(꽟½þ…ð`ûi4%0ø9žÿç§v.†qR–\Ö¾þ²….tðw<´-ô[ðb«'Ðâ{ÂiÜG#šƒDøƒ?§pºîp~(a ØÕV2ÍÀudâ5u^+÷:öÙ²Øf®v£“‹ÖðãÖb ¥èÀ2›ðà¤ô¹ûevÂ}€ª†R÷÷`?ÊäþÉÒº¯ éÔ%_Á}¸·P‹åž’rt¤V*?1´t,ì â7´É»í3Hk»—š{N'mæþå±88𯔲3’–T½lù$!F¼è¸ÂJ:¹Ñw~ì CëÝ-8&ÿ *M¯å¦¾µÙ!0=K÷žÔc¦>%äA_ÜniLÌÈæ=Ñ ¨û‚“ê1mP:ë!þÐáe'ãØ»gé<31ÞQ§Œ Ûuhâ¾zÌiŒ"J"e`ë@·ìP£~¿LÐ[¡ÒuBñ·l_3é= {ûEæ‡cfU)¶(%p¹»ðZ×dØT«ÄEjÖò?Ý0ìž%ãÂõ¯–S™ð¡Ü>>•–'>!^6´ä… RŒ!Bƒ4ˆXÇ[j©]ç綄У 7ÐÊ1éÔ5ù ýT¸Kå”Âg~KþÓåç7»ÁÒȨ´ãÆ… ©îîÔfÖ"\YãÈ_… RäΓvŒ¤¾oOëìé‹ö+ì«æZb©t †/ÿ‰dÚ.î—$¯ýlùN«°ºà ÄïßI÷lýA§íq@ÇC–X(º¾øà ÃÛÌä¤%ô½iC‘¹ßPQÍ’GÖÙc¤‘¿1V„ï6*ß j™˜`cס@Âö]íã®Ô)Þ'¾‹a‰½Ðáºß<®­æÙÈ'LòÜøgÉw]ÆÌ,òôUÊÚ¨ÓªKJ4V¼…í–>õÀŸY0TRšvêQñ[d_öÜ•Á-Æþƒ’%» íþÞ5>ŽmJj?)Êß`S\ ˆi-GúÁœïÝŲÿýÍÖÚàw0/šð„pÞ>iöiA阧Ä=,ÜùA T4+ÐíªGaVN ÆkÎëºì‚t:ÚAêr= m¥/\˜Ãâ,[pFB{8³Œ”¼Ž%'ÞÔ’(³|·X„7†M‚Í‹è¾=*|àç0úrª$¸ÆÏà•®%uŒ?ÈßÁîi¤AUøa¡TR!C—Ùô‘I2\Ü1侈PÍ )‹ç¼¨,­·ó¦‰k×òç“»ÈxßV×Á? zŠÇà°š5ô þLöÂ×2á¶­Ï{øav y +I%lgqEÃâ©9 ¦üK.†쨷²6æ7`ŒòÜç¿ ›Ö§À…wâ°DD´OÅR`¿ÊQ +AG^’Ö·OæïFb‹F*Ü*än[=1Ðâ(ØøT’h³eh4âÆ ›C•FèòU»I†QX«r„MÉ5qþbÞìÅpn‘Þü›lñœ‚É*“ÑdIûç½eœocÇá'øæ‰õ “ã娭m%ì‹áD¨w×ÈNƒÓ[Gcà?è ]ºªÐÚÜ›PëP•r $néògk5aº Ú±³¸ÃÊ~þVÇÅ%ï™lL<|¿§D?ÿ+ÔâGÇTáœ=…ì±õw0ºƒ÷Ú¬ˆ¦b&~³‘'Æ«(z~¹AŽoÆ’¿¸ó{1¬<·3ÒÁÐòc֯ɻÐ@ÖîʃO]„ó§/ãü ®ü_¦!½¿å5¦¬Éf"+Ç`AÙð­L÷îïDÞ9²×‰Óã·†cë‰T<ÒuVho²ôȆÁIOn¾· ßì,†QuEpÂV -oŽÀ=/Üqé•f,o.aR{¿ >Ì9 ËDèÜ?§ñ~ñxÜշ޾>=žžpÇC‹6ÿÛ…ö¹ýl¨ÔI˜—0¯ù‹ð톹p·e‰+ñ‡š›è¡«íiæÇÙS—³OoÀ•é¨ü¡k ì`½ñÖJÒœ¥y‚݋٭¡ zXŸ»¦Éí/gAÙL3¸ôF…–ªR©\NÇÏ¡¿û°²™%ìã`Þ[¿+Ÿ¸W΢9K~£8É&ƒÏè—ž ´>`&2Ù§Èú¬r-ã7lŽÑ?5d†Ógrq‚Õ>¶œO|QÂ6œ‚^ÜŽì ¡‹Â¿àŠêtpPÏäÇmV!ß}ŒzHSUysZç¼Åñ ëi,X°Æª5'Ñy£æó·: ø–T†ýÚ¶xáÅ¿î&Ž=¡ÆoÊ(ñn³E|~–¤¿H C²×1DZ/Q|ÿîk|Ž:Ïtùÿ¸Iò É=ÕðÊkßýe2|èèï­Ëx\úiÇ›ŠôÊÐFèm|*T·ðás“àŒˆK$Ïü µt?Ž‹þÜ’o è!‘ÑqæŒà×å=JúPó7‘~Ûž ŽŽæý7~Ð ïÏÒ «]ñï¶~ؽy*^>HEpµÆ×ÆÑ‡ðõ¶_¸"&¾Ì7ÅÃù¾ÏiPôn2¯`29|UÉ]Eýå¢i9YCš“[…»å—àµ}#é¥É|Ý! ~ “ý¥¦,‰Ê4gZÒ&kqÇYÒ^|õ¤XZº!¿?|“ï/¢‘k-˜çœå8Þ%˜ûhûÒ†•Ñ0D°½ýbAóìhxþÚ ŸOöƒÙåŸ..÷pjÄClÎ|‘¤e]éLIK†~?•z´Å“Õèˆ>ÿÅÒñÒ fנɪ dÇÍ`¾úwø¨Ø æg½ÁýÏfúoÁBn,ÑÏÞ佌Qª^—&Î5ã}©‡ú¼>ÜH˜ê?œ“wÅxE!œ 4Iò+:c°©i)U‰ùÄò¬ÃˆO°M…cï/Â*œGÚWcIç½ÿ)8²^ö¨’Ë4f|Á§¤ý$}²)¸×ÿ¾0Ïë^L+‡àƒg°Ð>®|»Ž©æB­úr¾PQ0ÈO>Ã÷6ô“’8ŒÙö F³!†"cxƒíüb¡YSàö›XY•/Tz°XøæéköÒzÞÏ£j§cæ—a\tg8Þ()"ÔC*cU¹\Å(¸¥|ÕÜTNÛ:ƒÏÈE”°égGa4 ¹bV­š@Ï[À§\]o(-éÀ-]ËáÓãˆ(¼÷ÈÑUâX_³ ç=Õ§Ú'áwÀîp>7î;ãâwºôîù-¸¤,×kkáÃËG?¶×‚ÜÓ$FõlidqþåM»K†²G#%y²Žo<{žu=†np~žý:/D×Bâʉ`á0š7Ÿ»ò‚ñxyWãï?˜æàá2 $^qõÏš@tÁ{È-Çþ)û™–ÓqÈS±áÙ  L0ÎôÜI¿>·ãöë[ñÚ‡§,úÜs-—ýæÒû5§`MµjT£åOÖ{6ðHLŸÜ^‚} ’Øù\îb•Jç\2¢+^\d;[àÖß[D]ñöŒ¸‰g]ì°Á[€™ÝDZZ7ßJû•Örñ{¬ôA-|{O* «¡XMWÛ ±e»=ýÛÏ.ò]XaëγìKâÅ Ù9žBf=Ú {t«f{iRÅù‘˜VK᣺1þ[R Æ£æì`p±‹ÂaŠŸ™ã‹Z50 úW¾"MUpÅ «„f¦S0e¾~õd³J‡Ac¸‡dÏà…/màsN1nþŒÏ’]p×ùYè~.‘‰mU¡~Ÿ' íêÉ ýù&– ¼!ïm#ésx¯ç£«}q¶âTC‚ƺÓÃbVP»' ÇÿI²¼Nã¬GÚÔ}Ýg´—|J®$1öõ©ÿé@ãV†¡Ül®%´ît‰"×y[ ƒÎKRŸ…ßálå0¬ Q¥2Í µ$NnZ‡Oô†Ó»ˆƒSÌÙ2]Û¡YAšKDP-ºêÐI#ÄÏÀÊ߈S³xü@%,W<.ÜYáWSï}JBG× §y´Û CÒ¡Kýù‘kJ?¨µmõu¨z/®Ïu}e×g‹½^ÔÄ.’ËàL{2z§êÃê”»^Ð`ß_gðˆ­àm߸k3N½ü!RÒ›a´1þ5_ïDZæTgWvžË/¬9å Ëçùá¹@Cú·ù&9#åƒ>CŽ2ËÇ÷qõˆ4W•fáê™>Xï-~¥ø¤½äÄ |¾!µ.“‚'R_°çèEØ»r:É3˜"ï3ÐõÂm¼u$.zlf'‚ɆG?j:›)5µÝ‰Þ¯wñ¬Å[€—ïüöˆ\0‚®Þ½èhi:=5[˜w»M'÷ CF›áÄåñ¨pد’çQv˜*> Ú7ýc9XW—‡î“âaƒª)÷Žà±³¸ÌjÚß|T}¯\”.‹ÄÝ'Ã4ç\,Ÿ0¶4aŸ®¤Uóî³â–<0z#øh¢®‡³þ瘳 *WÅaÐZ)vð¶Ä]HeÖOÊ'ƒæŒ ˆ¢«õðÀù$Yª„<qgOœ >ûôÙBÙix9à7>ÜRŒ)¹ßpZ±6ûµOþBºNèÓœ5Ñxªâ>frd/×KsáöäNæ1PtÄŠ÷º¼5F…NÚ2^Î*c§Ãe{ù^ž HÒÊ¢Ôɹ3¸ pèµ*—ºnEî™DE6r5/¬Á(þt†üÓ†oç™èò ئiŠÏVÂæŸ?ÈUO/Y¯Œ–Ÿ<þÍ)ø§r'¬êŽÆÞ'¡|eÎEø«;Šë:Kñ†wÓèï›Á|Ò:&;|/6Ù?½ÞôÀö,lä19waƒN`¸¸4Ù/E§úšÆVx %eLp¨”3]ykàÂm%îùº‹½>NÍ¥"7á†Î}øý4‘ü™‹Šdææ²sîï1bÑà®saÇöGÏGHÓH¢Ö<–Oð; §ýãÏOá‡ÿ6±.ã)kî!Nņ`gÿ!Ionl¥ ÛPWØ×MâüÃSNâº]IþÈìyÐ9Ð5v¼Uì½­do½iPØ4’øTáÖ­pÍÛDuó;ˆãœZ¬î"ÏŽ§@„–9ꥣíExW´´°ûÏløID©Œ<é™Fʳ&rïƒYŽÿ*úØ›áÉyr>*7·â‚¹^÷˜Áñ U¬§¢רMçËfЊEM‚ìWK Äßьªd>Ü)?'wÏOÙ‘F‚nd ÍÏj³GOšðCHØoˆÀ÷P¬ ¢w2}¥4„oa}èdiB±-½FÑÑËÌà•ôy&w5œ}nࣚÓ0ùÇSæv•RÕ˜kPñÖ|(èñi»SCsاCA0¼žtÀèåÇqë7âtz_¥<ò=Ãp¿Êkül 1ï<Ø»Cdç[eÖë~½§ñÂÅCQz´¸CüµX´nÂ8m#…‹Ã`‡p4±WxƼŒ{jza"xs_ÃPoˆIŠÇæ¨8,^§Áe^a-¿Ã¸¦*ÿ(£I×È,£`qC‚ÁüÃFüæñ5d¨PÃ8 tÖ‘#µtO×óÀ!¸eà1쇧ÆxÑK}=ìe»]ëîGãE ÙqÉùì]ÄEî aCµc¤ Î8hMÿŽniCešù‘ M‹šþåèá™~òåÐdü­´˜.Š«Ç€5\õâf>‰¯º¼ ìô¸Dç38—üWªÚp…/18̵x¨ÈЪׯ8]Á]ÞGøÙU‰Pç#d‘¯€§| Àù“Ðbj1IXfsG±„—î8Òþ Zä9ã/àÑM.Pgò zÖ'£‚û”ß6•=¿áÄc|Ðú‰#„½ˆ‡e¶â<Ռ©©Ãiƒ]4òì½ –ÛTwÕo2G÷«»jCÎ_z VóaÕP_ÐÐuƒ+¿¤ñXÚ3ì/ö'm6qlã[_>pÖE¿ÆžmÁxb„8]-C¯¯-}¡>þ A­0ua pã"òÀ·•¸þ~Žã+.ØúÛ¡ØÓ;N¦¸-äÚ»dáõ‚lRQ£sEt¡ñÁÜ–>Jfdã”]g 9½ZÐøoìûr û|kÐ?1]îÌÄPϵ¬ëã-L»yôº+ ôÒ µÞ" `i3ƒGfË0^3O¬_…e ðÑÁ%„…¡ß±jìo\áóN°2è˜}‹ÃëzPA9<ýjÅpLû_¶é†õqÓÁ¤Ù‹è¿i“xÐËd©´+ÄÙ©Ò¶s`WkËXòªH^^ÛÈn{H£D«¿ê”ӮΧه®Ÿ5ȸ)¡üäDuŒè·BƒŒhöx±*œÛöÞtw“Y¢ÜBȈ™˜Zp Í!·L"î>%Mûš Hr6Þ:¡DŸ8l`ŵ‹QCî9Y#šÁ–J·¡¦ò¾Áæ$=F©á,˜¨};†yòeÇ“ kÒ"T,À$Iè>\‹¿³m«ð—ûÇÐ}A¤Êføæ¶Ž @õ“¢Û$Î7ã~½)?`Ä ¸åtÈø«*Œ]wEE5ù¯KÙ¥âÉÔemD%FƒÔ!Öl)ÍuOá¥. þj£¬Øå)wl+cï;ãq»Ž÷þ9•·Â¢…¹02f:ßÞ:Wàî:ÿ(-„Ç^cõÄz/ôÛÿõ)9|Á*œ. ëGÇ;>¸¾«ý? õ¶jCl¬$w;.Ô¸çƒÒ{EéNç©,³}Cœ±¹þæ¹nÀo_àïChËé-øÅô0ïìR§~aŸQÌþ„¯E¶òû9"u.ëªèvÑ¥‚“{?³¢[µ<Ûm.éŸìO¤ŽÏ¢Ó|•°P·þ2‡fS(ØjDËŠ@qæqœž )\’µGxac çrÝ«×ÑF]5ßé$\zy'ßÿ„A”î–§çƒÿLâÒB/~ÕQ”‰×¢Û­½øŠKŸkÇîŰùÁ2®úf¤‚”“þþ»dÖ&:šçÁ1šÀUëBYé›lò¼Dknzs×0,1œDú“Uè¿ëÙNƒ¥ è€öŒZW¸ÒPeÖ8Û‹^ u§ôÈz­ï·by1¡æŸ `|„‘þÙ㾋PâÇÎES×ÉÌ:Mþis4Õ¯â«×ûÀŠ&‚âÏA×¹m†´¢ÁñÛxWhOµçóRv£r'Ð×q«é‚ï0~¡Pb"M½&ŠŠAçè´5É Ý: ÇÎb˭ŨÌÚvÈ[T&0k3æŸuù¶‰“øN}'¬´¾Ç,ìû¡.æ‘ׇ¯V”€û”f™.F?Ë© S„zI×è|¿aS1¤;›/ZßJÖóuŠ’üÅšñéÖ 7Ú§Q­coÎsøÙÛ5k3PnýT1u'Mž~m!wO±ÀJÉ®œ1ài<˜÷ÍÚ Ûç4€¼WLO¯…™{ˆ†S?Xv6BÙT7(;~ÞÀ1¿`Õ‚™°1´sòcàæ¸"T/_"0z= F_›NuBþ0»-«Àm® .YÁÏñ:¸à±—Ë©ñ7vp^i"ý1×{gIÀµN?¡ö°Õ*­íp›Ík"}; ÚýTø¦;ul·èÔšæ¼ðú©0ª¬†^Ú´–ÏYÖÒ2ªÎ<´år5 –h¢¬NðëB¸íéÃ[JÁ.[‚oòk‚8Õ#|ï4¾7D‘N™—ÌJ'áòê.<®Îb½èο±Ì ½ÞÝœÅ}‚¨\…&–´¦Ã>âÀÛQ´Jé(ë©D¥‘r”-<…z»)Ê©^ƒQ¿îÂÝ´éY°m2ZüTã L¯jÀÔ¯}P´õ-*¨lToW¥±{ä©…&©Ý‚£VäYù*bï5º 4žÌ• Kšè$K;– Þý2QŒ¬¾·o™ÐÙÌÞ«— ÿ7‚3Ÿ fmä´®sÍ+‘ÉëÅÙ¶¨hº‹IØLãóoCá‰kh´¢ýóAß³ŒQx¾«ŠË0)%3iýõ…hÝö…µX×=ƒÚÈû0b-ÃOiug)ñ™®,þ;*Ê•©÷Íéà}é'ì11#÷EaÀ± Ô¼<éëË0/%ƒjŒµæÕnG¶Yãƒ`šïs,S sA%g?¸sw+ a—I4žOdËËšX¤‘cùºTºü-LwL>Tû0÷ zw%âÖŽx»ìJöÎî8a…iû>@‡úòGE´#>M‹>¿b™Cû¡x‘5ß#HBÑkQ$h5éÝ_“gH€Ñe3ZSéqþ¢ ='¯ÖØBØn;bÛ²½|( *‹ÓˆÉÒÉt~ôlÜ“ø†¬Ì„Õõ0Rðf̬A‹'2|’i*Žèeù:³á±äbâÿ]œÝÈÆ5ó±!êËîÞ[ÏSÕMÁöså1Xr"ã›ñ âZø={®H㺥%(¿B‹'L2`bãNƒœK%“&ïÛ-ø5ía [q–OÑÂËæÍð¡L—¾ÒY§×jòBÛ²mÃQøü2RÐÕ±‰§†þäƒÕ{xc4«°ª}$ýpšåfAœdìêr…Ù—GѨo5·‰15iqþm’@ ºI„b:tULÆÝHâ×´«Jž›Ü„'>\#)ÇùeùŽ¥ôBúTºo« Ùã1\rŠåž'ò’üt”^Ýsþè áÝ^òæ} û!!I¯Óñlâ½FtZ²–.Ó¦­Ý6à:{´R¨¼åj0¸:‹+ý»ˆ·, žò"|ÝóWX]žÏûU§À¨Ý}ìÈËL~×BŽŠö݇gõÙ°ý·âw˜Ð²'Ø6i€Üi€«&[…÷ÿêCÜ;;®ý\Šæºoâx,œÚ}žM«¡•>N£ëŽâ%G¬¨[ù^nêm}cîÀ¤]h¦A¿¹$3º\»…|zp†ËîÁ½Onã²k„çܯCð69z\>w„‘­)ÖüZÀf8³n&ow‘ õ ðùòïÄß|žÔ­û ·Ò­¢Î|©®7uû°›>´Á›£¬yfr'¹5…OëÏýÍ ó»GÀÌÉò¼òl4îø§KÖ/¥êŸŽÁŸIˆ¼Å鹋—¨×¯>|í¼žûçOæõŸj‚š/Q©£‰tƒ§GfqçÝI´ÊEe#—ÐýÆŒJJq×Êá¸9%oÜ.€åv­,~‰¿3B›¤—€OÂYœ=zÏ–H¿«.bá3®üb€„ HÃÈkò4rˆ> $9€yâz~ùYü£Ç¯j´Áã«ãØö‚JüŸ‚ÙáL–]ÀÎ]I°Ñ÷ ¼–ÅèŒÑT[Ú„9,çwμÂ;֋ȱ«kyYŽý§ÛO¾kûóÎÂÇJñj±$}+!¥Û±Ór©Ì¬¦ –9·³xpÒÊÄ<ÏaTùØ zž4ãûù•`7WnÇ€ZóX¡‡fÖ”â®ëÓpÅû¸×ØŸ.¼(4XÍwl¸‹›'R#³&âŸð Ÿ¾úÈšÓʆÅbè(žV!K‡*ÂÃí§©›Ò x1Ý×=ÕåßÎ]¿·‚»–Òôëì*Ü·X„ EÛGÿý·™ÏkMÌèü¶ƒd“õ@Õ2Gjž!àž}5¸éD3ÚMÏ€‡þqä·ÊªÙ GsJz™s‡8ßZƒ¥L5÷™³LƒÔ|`)*¹DÁ\Tزç6xêœEÏs àQQ~›yÌä·xô©ÉO–Æ¢J8æá0~~X%hnºÿÍ¡ú ¾yNüÛ`ëb Å5pîÎ\U· rEÇÀúBh&ÚÁÑÌ÷O?Ö°¦nk]±@ñ9¼š­ÁÇçér—¬>òÜ4š%Æ}Á8‘8·k·Û=Àôâ¶“°e2PkÆÿ*&aRO­y¯ÂWÇŸÁ§S—b³<…_%´ýy¿ß²†ß{)J&—œÄ~åÕ´&Ó’lœ¡@þ2UT3¤ë ÊY`Œ€nó8 .ë¹hW:ŠšÐ÷¯4xç‡òÊX‘BnÈ"RqA‰Oú €™aòÉ'·.ãßÏÅ¢ëiIY-“Ï€ËÁâxòo¸^åÃ4énÞû8K¦OÅk½NÔdÑ]2ž ñGÍBÚ¸ó´q;ÀØ/ÖõO¬Ù=z¯U¢ãJg³ÐoS鯻Ãð¤èRØr_ÇþLDÙp#Þš¨BõÕO jóQòóZ IÞÝŽÆò…™Ñt˜d± ‚•›¨ÁηŒJ¸B÷Ýt›QIÌùª›±|Q]þ o ãÙóOÐà4ìÌ;ÄfòÊÕú²8·hl'6‹Åq M¡ÃuSPÆH‘÷eÿf‹Ñ·­H>¯ µe< | ÷Û¹ MÓqS4¥¾·bÂÎhá:·æŠO œÌmpZN\¿ƒñ>Ô|·½óf}¼¾‹žàÍ1Ú°v#‹+à›ìt¸ß¯³(»×‹6ž<fóÿ áËhtöÙE¦Äý^ I4›KÔd?ðÐsÀ=ÁˆßÕO€çÙ:Tq[*ü}1¤={Ù‹Íè°²­Fé°ò¥Éô\“8ÕéþA{Ãá}M ÊeÇàÔéaDÏŸ‹Ï̓4¹·ÌƒÈåæöš}i߉|ÈAèÈ>‡/m%¸öwyýzU9%C‡×æ[¿0È6WÁ‰Y"<¥e(ýY– O¯zÁ¢£ÍÐùa­ÐÝÄ|Ø ý§Ÿ@u¥ÉËM`{•1·øiFWÉ]`·?§BÒyy~l“ [ !4/êhó€’ñTÅ–Á]C¹´Â}´û¯}ÕÙcàï''>ï†:]7u*lSㄈÁï 8e0Nàv) L§¡I¾]Ä_Ì Ùy÷­§Ó߯Ð8£«ø÷Õh°9FC÷ëÐ{¿ŸƒžœÝ]¤Lv}qw^Ó’¾à?÷xXsr;Ýùk û0{6®÷šÄ‹þ u>£y ¥0$Œ…'ãvCø?îXz–Ðæe¾Âñm~éw8ÌøÔŠ1&’—¥­xÆ®g(ÓMѳ¸Do/®5Ù ›âFQ{½)xrµ÷ÝÈ%?¯‡DÍR\%y”Ý\Š×œ?BºÒgT»eXàžÊ×HKáñaªÜU1Ç\‘¡©“¤è²ºDü\ÎÓgkG…ß®ZÄ)z<ŸÑ¶‡Î à¦쨘 sæO#Ç:”Xî”Þ2M²zaòõ"hÁ!¸iý zÈ M ”bk¯–†ñF%‚ÑGBÏÒÃlaÁ:ëÞLr×s4n§ (V˜v«Ò°"a›§Ëðãë:ñËh zØ|ZÍa«XÈù3Œ_Sa»ÿW0XêH¯ì¤hw$­Í¨gÏ8ߤ$2y{¼Ð¤"¬îô¢Jå púÉÈPU8rÉ–ZT¥"]û<Þ«â9m?hÚ¥`¢ø~œkÀGïcû#¯‘ާÇÙ_“d†(¢õ8óözâþ–0|£•Ì=N¹S‰•–<7Í”fŒCãwYÓ&š˜)ݯç(@Åéé&Cç‹+Øür Š“@‘r;óíS˜ž­H-¶jÐèCSè¿3Åpΰ⟠†–þlžÃr@íý°>:–ˆ sxàUi¾ú¥!œì+¥?²ôëY~Ë*×ñÛ¤Ÿ°ÈL”ïÝ}’ïñ„“—á]ñ?˜ß|_h6•f$aRèrº)ÉÏ\Ÿ={3i ®¶¦t…Œ­ÚþªO £~݇«Qµdý±‘tà@  ©jy„ðŽÞÅ4bÃIV)•HÈå_piëErŒ›ð&¾4«e.ö<§—Ÿï§ÛFýf®†ð©ËŽàæÕwXk^&)”ä¥7}xaÈ'"ß” %Ýg±ññvtÜ'Å—­ÙB×þNÅ’ciÀýµèãS“û}ißRwn¡)KYøXjV|šG$5aG”Ÿ™ÚwÀÅgŸ ©Ï}¦÷ð MØ)œtŽ1D?¬®Wä†8ŸvóÔò,vlŽ*}Ÿ¸ NÂcöÒrÓÝWÙS³‡ää×`nï¹—.;ÙGZËq¿Ml[ÁJ˜ÇàY¯1xЇCïn;lÑÛF{ ÕagJ3^þ6‡®Z£DŸeJÂå?®\Qv¿3ì*lõP =¥º´ÅÀ@°~ÿ+,Y4œ›ÛM£U-þü?òÎic‘;K`횣Ü}ƒ,µÄ0°%-ê¡%mÃI²;àì(3ˆ6]‹UOÆi\Ã=!¡è¿Ê–öØq“|¾÷ÑCÒ8R™žÝÙ +œ\T8’Lœñ ~ˆóúÞñøŸ×:è:ת ™ùØÞýìøAÀ¥•8{‘*nºKÞ?‹É¡G!»´ß wfÕÕA‚;á]5¶Vs ²d¬ J}3l¶ ·<Ÿ øÊâËàj’G–Óqó͸è;Z'bÉ÷L[ˆÏ¾¶àEå<ñy?nݵ…h5!Õ{„‰{g‚Á[®ƒ¯%u‹x( ‰ew«WÀ—³‡Ò²º98+ñäbgM ç'ò;¶1‹ô‡`l…Ó#J…2o·ã@½:uw°¢„ÐüJœ¾Üº<”;á¹Ó™Ô .J¿Çí©±|³Kõ 799^¸R’yåTó#ã)z‡jÒÿ¼Äcó?:4ß,ÌXñø˜ÇÜ·ÇžõE¦“%RxËpjß‘§u~í(}ð,Æ?ð‡®ßsyÕ¤SÂkdø\qL±°Æ²Û¹Ê=W8­<”ÇMj…W&Àä 4;6¸ªFéePµó2ŒOû?ï¾!÷fCÙ2°MëÁ÷ÓjÈÑ;qpù½WDéðƸ`™55Êü(˜ƒ’æRè?¥ ´ËÐçßâƒã»4x¬£ ŒtÑ!Ö|çFäŒÆ¥~ª´ùÁW¼+ÇJ6¶£mo&ÌÔ¦Ê7£hO\¾¼s åæõ°5:¦¨×¢ÀúœìrÂÍ ¯aÅ–!TÒ<‚µÅlÛ!ŒHóë‹á†“2èâÚÛÅ×Ìló±këz4>ÓÉn ;r+ÉÝöq`») zI9¾^qWxbž'ú'SReç–¼Á›„ï_ÿÄa.ë1¸[»oC%ù%DjMœpÓ…kl¯—Mi~ºsòðŽÝ8Óªͯ*S㳉$Dd:™2A—üQ¦Z¹xóv2Q~ºöô£Ô¾÷ßIÔ]2‚»ì+fîæÇ`H~(}nõ÷ö%Âî³:5+\/áüçâPt´¼ÇgAm¾kÃ}cµ°lv*ÜíÍÍCè+çÁ˜¥XõБøÝ~.Í’BoÞVƒCB;²¼óVcåÞ—øøÅo´ù¬…±­sá§£8V-…ÖSŠtγ08=c_Öà. ;¡ÓýÌúgh¦˜B½‡wbÛ0~`f7û¶s7žq4IBH6œ—‡—Çths­"ùâìK‹,GÓȪX'á? Ø£u~XXÝHÎÞ½ †®ðrÉàþ4ŸG­Q…£o‡k%ºQåG2i|.Îéžsà,þ¾5ÝÄ­¯¥àb9âdé_‚¦Ð•ääâ6kÄ "7 A;7û°”ú8wYŠñ¥Ùë6ðîÑÊxJvw™§—lÎ;pç›ÛBU#¤¢NKŒ¤¨ù´hÞîO¢jÒ!mM>t—„±¾…q8EM‚jnÞ‚šbª|§¤*õœ1^†”uø(Ü* Ç–ßå˾0p;÷Ï~GjL…$MOƒHžS d` Œ=ˆÑ —ÙUsy’l}—Õê)òçwB‡r:©xÜ 1­,9hg?,­‹{‰]Õ Ê^^w¬é¥Þ¡tKå|H¨T†Ìyâpl\FͲ%ÇaÉF üÐP޵™¯Œ.W¬=Œ‹Žƒj!üw&þçGž_‰Ÿ–¥ÁÙ(TèYÊçÁTÎ.#ÞWÂD;¥è¾ó=ä¼÷0hӆϔñ“i(i¼'Ä QasÒ¦,aþÁý¬Hþ^Y@ž»À­ýê\UÄ‹KŽK`óÂ%)ù4 >™ý'Oäìc¯LÞÍ®-Ðufý9›žö3Õo*𮦚æ5Á¬[6ØÙà«g‰s +œ®;Šk¸yÃ뙋¹QG¢Àþ»ya7“|^`EY¾ã™Ž äߣ?(/Ý&lÐéƒÃ¯$¸êh)ªP>—üì¿ó_´€pô!íðrˆÌ‚ÊûeÀL•C‘ß2ðÅ"h-ÕˆsGpKýØÿO?z >ñKçEˆe¸¼œÀ}µ6Ôx_µj|1c 9—YÛ <ŠCÞ=ã¶\x¬Æ>+ÃÛÀ¼{:ø=Qþ©ÇÈ.+q(ô£oÊÉ}a›ô XÉv©ÿ ÏÞO¢æÅ®« «/ê\R gµˆÑOw­.ƒ7ÖÞbs?ßf^!éÉ0u¡&f¬6ã…pÖa}Z®†‡×uBúûD6ª6»žúòº#³¡q”2‹©n@{rœtžKgñ~ªpÝÈ‘ø}>‡tÊsLÈIãx”Ù] Y‰¿É„UmšjÆ·.YÁÛ3°½"Óß«àÍ'pWÁwíq|cò(˜êèΟ·–CÐÖp\é­…âÁPóƒ9ì†ÿât«k„×±m¬ÌF”ÔÊà¦d&å_Ź—âņ :yodÔã‹"7«D°èò²+Û÷Já.× »Ô'ó£çÄÐå§5[FÀ@Š= hQ¡ŸV#÷S~cX\î3âûGõCÝwèçÙ¼Îì'¾y¾dè ¶z/Û‹aøïb6ÌœÀ=¿QƒFöÑüukѯ1Hë‡ óD¸ïã8X¹øú¦îÊå3­ÛáëáBl²ÍÆNÿ MÁ{fÅÒP{Ol=sŒ—]™Œ§Þ2ìÜö ó¾ò\›~Àa-?#µŽ4±Ç,Í]Twbkö”§†!­$ å8ߪ#ŠëóÐô¼§@ü¬4>ÖƒU\‚}a¥püÂ70‘^Ê×`ËÙMtåývbí0ƒÆ‰]ÇðQ`<¼‰ôB‡ o™hs* P¯²Î‹ûꇛœé÷±Âi‚Sà'“ƒÇÃÁ;›*²`¤º<,9`‡X€”m9>©W¥±Ù,¡Ñ€Î3‘«)x½}zhæ@ÝoMzÊù1˜ÚØÀvkx”8”Ʊٳ>@€ù%°ž–ªÓ«qêãÏxÒ߈¯lÎÃÿ¼Õ;.›Å:ü›$ºu¹£«äèz¦È_ؽü|—‹×a‹GÛ:_‘;¦â_¯p{§Z)‹ÓíoƒØ\ì¾é#XhðJph&ŒrR¢²p%Œfæ ÙxY9¾n·ù=ç¤èpÿ=(6§ þ9 ß¸8¼%›(8wr4õ:'ÇcÎ]ƒ‡b@|P'e~ü…nE€öõBLxæM"RÑ3À DWƳ“õpóæ úìM$Æo>†ktư•‹`Í fø±-¯Ée£^ÌIlZaÌzÖ²w"=ƒë¨ –r%lØ!EÏ?5(…,øÛ*|ÕÞNVsë>kÏŸ©’tÙª<p»ÛtVÑ‹Õ&|¨A§pi|U;þÆñòS1ž1Õ Þ¬ÛEjcL`ÖÂ\2aÑv6W5FŸÞÉŸDí€×'&APËK´4âû‚£ìGΡOÆL~C±ïçÁùî4¼BÇ£#Æ#"‰’QrTôˆq̲Â+îWH„Ç ˜æ£ ¿ÝÛ 03ÊeŸb­BÓ$iºãƒ8Ÿ¶T –ˆ°»»„—>·âEÁ–г¶|´Ù+“£¡ñ8^\[`zäYFU°cÕRx#êB¥¼…ÿbY}*‘ÈÎè‡røÜEšK…ÓMiaN(οÇrLyÁvu$ºˆY÷2P£O’Vˆص©Ð¸6¥[ÇÀöݪhlšÉ¾VŠÑã‹l˜³­5ÓãA³7ñObb\ÕãzÍݦDü5¦ ëη@ŒÊ¸?ô$½»È"ç×ãš41^´o<®Ú‹CJókòQㇱïþ¯á¬|)ÔßÈDßogàòæi4ôʹԄJRÏmÙ‰M˜0¬ gv‹$í¶’§Qñõ¸QÌRl&RŸ!gزøq`*O¾ÓIÔÜõKÕÎÇ]QØ´.ž¿‰Cãg–$ãã5¦úÔ¼]RðÚ!®S½Œnÿ°‡Ö§£e¾Ýå(B‡‹WãX*‡³Ïþ¡Ø»[ ª*EyUíKRŸ/€×!§°(\’Ç<3ŸúËøzgÊÝ< 7¾dã»B1…Õ·ðè’LÇ”©ÅxHç!Œ‹Wí9P;{;ßíw…œR»±[Œø''X•ô§ú 檅«(Ë BÅûÎxÚö¡ Òׇ­É² Uõ©0Jõ>[æË‚O!ß\{¤û ó\½`öÁff|ÍòÙ†STù vë Û]ŸßkžÉY*´Ì으‰¤f'¾¾£‰êVà‘úB詳ç£ú 0ÿR|÷šøviã×°û gÕJ*ïÊCë¨cÌó ^5_È»/ŒF%Í[äåÖ¸S%˜­‰™–CñöïEBͪfü<æ%ÓÜØËò¾]…¶wPÃõ~ÔiׂG½âeà™!‚·Võ0üv×´Z°Aý8çc'ì¼³Æý: ÌDåŵ•)?ýl3~4Nƒý&Á úuP¼uD¡;~¹9’?ՔĄín8¥ÖŒv;JÂ3K€÷%–ôkFHÔ´ òöíÅ'Ba’SÔgáç%ztN¿ÝÐáÝZÄ{³ý­Ãª¦P'+2<<öÜ•ã-†Åà}ûM,>°„Þ°›MMFßjx˜ ½Œiÿ¥!=>‹Oâ1<øœ&¿üì«#_¶ Z‡£žE+b%â­m†|Ù£?ƒ¼1”+çKÓ©a}ð¡I”Jîñ¾ôä¿t‹(b‰ tÙ£ƒ¬§[—È•r–äi@×>1Ã8ÕlvIƒÖéó%- ”³† “„¥S$1·ÁŽïs^„–[ĹéØ}ù‡£éøå´OÄ ¶4šÑä­2t°½`¦îC]ôŠmªÔme·€ø5øv-ž¸ãÀ#ì ‘ƒ=¢ò4¦Ð–›}µàÅ;ÂUgq…Ÿ´ù•öŽbÞE’¼Ü¼tOž"·WŽáºIgHÃãg8Éó)þz,Må’b Hm<|:´„¤e·²ÐÐM´Äÿ*ØmÎ=8C¬Åa±¼ n~ är+\¡Á•‡ '¿×âoýJ$Da'ÄaÌuY6š­РײêXÏ¿í(¹u--ßÇŽ½É´ç@Tè6°¾ó 5h •ù$h|kï•ð«ò a¿ñb°ž5UðM߆Ëo»[çSAÅÛÜ"B“Ó„n†‡=q %ê7¸áÄ’"!5÷€Mƒï¿*F tߎá%eÓ`ùöUì¬jZ”ŠÑÓ§ ÑiZ"nóÑäæcá¢_„îÈ¡oÒxIÛ̵Y]m¹(àÅË¿¡Šÿ5f;ÿ%ܲŸÛ2p ÷…Kë´éü/£¹râVH 9»ÖèÐ`qrê¯.jtÄÂÞMß_ÀÚ 0ð=‡OG¾µû^Ü¿V‹>~}Z0þp±°,ü‚ðéžàÞΞÙ}½GpȘ”3†z}Q¡ÓìÕéÉ‘U8:ズ}¨2¦gö"þ–Içê›ÀÿÆuXá¡D÷«…°e“‡P/Ÿý,FÒãÞ˜è~Mrlpès¾ò›7lŒÌÿz?¼xàب ð«› Öá‡41è9Yä¾ðA凷¼”,/N&½›?Â^‡‘Ô×,”7¶f’ðÁÿy™qdh(‘ 'ß¾ÅÑÍž¼$à"…-?Va¶QÎç[±sèp|ÔrM½éŒª,Âë¾&|ÅìO(Úõþ|ŒÖZïàïQïËíßß{Y››k<™ž‰]L%—òèŒ]ëB|ò³žÃÊ,Ôéñ§NØ.½“ •ýQüÚ*âfeΛSœ4ùß4Ÿ†0öê5ülÖ…"§À £‡$¯b=ˆ½ÔàΗŸ2)Ó>œfUJR[3ñ¤¬/ÿùZ /VæA£ ÷z´€ =þS þÛ .;ë[Sn;ˆYk_¡ü/¾}±ïÄúǃ\Däî[˜îwÜNlG¹#ƒzPg+–B{3Q”‡¸Qs–%ß5S’«Ç¥€Þ‘½ôäGàßµ>1c|ÅTü&SåÃtÃ’Lög Æ*FÉR´h«+úiÅaÏ :ñÖd¸oy Tî/ã¦þˆBYZ5-Ù’xÜÓuî«SÓËM¸wËh³ðI]sŽ®MgOYÅJeèÉ+ùdñ: ȽÕ}2ÞW5Þ#©y®¥U§Ðû½ O ËÀªï ©¯^«üa¹OõùJï|¸;9Êßî!íeß_\…Ø›bÜc§4_trŸT…Þú^œÜBÉÓç1;0^ï1¤e™;HcøBÚtw ÚÝ›FË®áڟÙœr€ÝϹ¯àf{8ÞåÃ×”ÀVWÊŸ¾Ü.»óyÞ6Fò> õËž±µ‡@ä½ä?¬:úÄv~¯Këþ;ÄoV?Âá†/QA}Þ½ÉðMÝ;æ‘:†Ï4ÎÁýW´ÿóV³gªGà’í5|ÞïÁ?fⳌjx˜ódlv½ ƽ,q™{•)F†åUá%‚Ý› ‹|6¼‹ÁŸß©u‚>ÈU8ç³/üù“FwÀ*7C®:BžíF§]p+£ FR—‰¹°)S†&Ù)s§ö›ðæ|f¯>ƒûž~%é+õøHKì×ÿÞE÷˜æSº(ßE ÷_þM•® 'ëøR±G‚ßdÈÓfJ4/?G$ü×—ùt5’GÛué·Ã¾øGk.÷HFŽxđ։ÇàS!¾»=?Ý#8à³€%/§ÃÄjàïÝ›‚éE5á¢ÐV¿  ø£.t܇“µh˘¿`³³…ÝI¬Âµ3ïÁßS`ùf€®¡í$á¡ ùöxHR¦ú™ ù”òáßÁô¯Ñ0[¥N˜:M–:VÈP•›ùèP}¯Ïä‡Fîe¡‹‰HÝP0½ê+x°Hƒ'½ÙÎNl™ORˆR¹Ì¬Ëåµ(ßñ(“/ÀC¯³Cë!LÁ|Þ€ÄÊ™`kø˜È­XÍ,¤»HÐ!þ ^¿þ¶ j'Mçiy·ÃÎÂæ9Aãå.0M~‰ë  ñË/4TU—xy|vdQ/‡w *¢®Â«È~¨í‰†þ#S±oƒÞOôêàÁ›¿So”'àUµ²àw¡€ñtvê1¡¦UùÏðeXBöáN©Óèc­Çdâ‰ÝÄ$LYP]M`Í)8)´äkRü݃8~Ñdf`-n“Ý ?b¶Á‚” 8à˜ÉÒNèó„‘Ønrü‡{±¢5]ð3Y™ï½êFÞŠÜú”/ô/ʂۣ?³þ˜‘üêÔx\£ðÁ?Ûñþ_púLP%ý"þš"·¶cù¯/Àr¨Þê,XüÀ$dè²ý…Ïà/Õ8~þxï“ÏD£ÖãšbmÞ“ñ‰É=2ÃÌ=½˜¤ æC’àG•;4|‚¿jXtÕ‡¿ù,\8¨‘æ]”¤OV¢×CHÚ £‡#$]µ¦§¬æµê»‘”cWßÚnÉ…ÝÅ(õÕß_v,eGAr"à†ë©˜Õ|š.ІɷNÖýx‹úN>øtt·ðpP<ú²®ø/Z° ³õiâžI´fÓG|Ò}7×ÀnµÖ×wþ½ y R!ìð%ömY>H§BDÍ>¯|‰ÏJ½T Pb>¹°u,“Ñ: žs®€ý»Çà©éL>Fï¢ëÿåâÛú l1=2"½8©Çz̧a¶n0œÿjÊÛN¤ƒp¡©,"3GÓÓ•Ö¤ÍJ†ÏéBÛïaýZôkÊUÞ6à¶£”~;“CÕŽÌækÏlg|¤,½Ìd³pí›t¶H:èûyõ©üÞw n†íøg1Ë矖Bʧ‘tÏü;Xp*nÄ…ó¨‹t!HþZÎMcPäóM°³â⵩º)hšÍ§çÿ€@Ñ-”²oê2ò7CÏ9ǸMÆxú/ îXNÁðƒ%¸õÁ¬¹w ofá/â¬à™‹'ùj¢ÁËwÐ{û.3Ñ|€% ü@Í4…¾óv¦‹ZŸ“yg–Àa«L*‘¦Ìu¿‡ކ\lÆ7+fM“_yP»CÐtóºÞ­—hÀðê‘ôf‹>L9gA7Ní:rØ—ÇV4µÝ€îx:ÄßHÒ!ƒë'3˜Ò s¸:R•kûu³¯goãÍ^ozÎß‘ûÚã÷€…tõ7®6w9·›”¹ÂÑSò°¢&û§‰ò¥}Ì5k•ûnÉíVë/ü»ß¾l Ÿ×|#°bOšµ_~È·WBËD üûE”oø$K›»ôY¤ƒ5ÅU¨ûð4]&AÅžŒw_)úá”·;µZŽpÈZlæä@–vM›ÆÔër‹o¯`JÞ¾^w-¿ôh,Û>Bˆ3Fß!¡Uâüh®—À®à84$îÆÖCŒÌ¿…£ôsÈ´¥?ñ¦èR8îú˜• æËE³ùs‹1àÚB4Æyá}M+¼pëÞ †óeÏb)~µ¢ìÖ› Xb-Oâƒ×þ^†GõñðnèèIîuG\:LŒþ»ð ÄvEü穆¡ÚgaER {®· ú*¢°LºE÷[Á‰î9Ø3¨)/‰ã–UCðçGhêMSZʰg÷¨59³îèÛ\Eä½4¸k] 9ürÈ…(± ?ÏCZz©þÂ"',ˆõ¢ŽN‹ÐïÜA¥ŠÕÍu"V²|{Ü)¼ Onz·;TOTâ;÷'ÃÞ'åx¥X O‡7³xåk–`~™}™‡°í[ú9ꈆ2þÚ bOƒÌ®ƒ°póú6D„ÞZáMùB{:Ý>t¿ KF©±€{yHÇnÝž ²sP|ž;h³R¡(%ñ ×ÍÞÌCV<ª¾Ó²Ÿ¶Õ¦Óíøý)!üÌãlòS\›ÎÖwäïó#¨k¯"kš{›{®’¢×^‡ÞÊäó²ZñgŒ!ßÖjN•ÃïAÜÊòÍŠš_”–>\Åõæ‚鈇àp![}ä­ZûhèÉj¨‹0¤;åà6›¬Þ¨C}ûð%ÕS¸Œ¶ m~ ;â÷1·*Eîÿt ÿ¦MG4TÀÎŒÃäV…Û1pŠ]¨¶„¶y |öP·Ù€›ýèËXJ-ÆèS“µÏàø_1¶H6k&ÅB~w:„ D2ïAªŸÃ”ì˜Áù¶ yîrª2J‘ËÎÉŸ•­¢"ºKÑrÍiæ¿ìŠ }äVS߬xgÇ2>ýñ$0W»J¶±7 UÔyu‚,H½@C7Zñî‘É´âs÷˜u†ú!ÁtÙˆ5ïÓK`wËQ4¶:> Ÿ…zæÎô‚i?(Ë-4d¥a'ðäQºe=,W‘€'6¿™v‹(õ9àË_]JÀÛS7Ã<Û:¸¹R‚ç>."jê„>w<‚VÚiL|ºõXžŽJRpëÛ_´—‹d)OfÑâ; |f%x~3‚ˆçú03å->˜Ñ@ÞŒAµ¡­¸´5Œ<\z.O«ƒEñ7eCô¡<¾ö6o•ÃNoYÌó´¶âH_’¨ðˆéÔíÁ÷_“ÓáÚ4ð¼,mŒUÇÆfŒÝù•¾^…­—E¹ØÏPÚ¯^ ´3¤§,ÔGÍ{WYûØ[°Àd è|Û†™·°«G"CCKŠj|ÆÇçñUWØýc“aàcÆtŠPÉäÉ>ižDî¢k«(]7s(-¼u~ÝXL›bcҤŎVïƒÇ¥0:ùVº›æ°;2ÔåÓ(8¡ÍïÔ«×ûˆ‚. ÇΕ¢{wM£û&)³—¢fT¸H–½ân%ÀWö­:§Ó­ ÔäÁò€%tôzv<¬Mâå/#@D[ŽÎüÊ-†Ì…Ö¤¾ÓòDacûKÈJ,öú^CÇ¥¸MYZÄç’W“ObRÆH¾4&Pè›vžhè}Æ^ýoèµ`#d=ñ$'¯TÀF‰øÛSEèÅQ`˜bBCK&ñ£'RI¹FÚœôÁìµXqA‰~ž¨À›u²°ÏL—ßÛ'Æ7Ǥ…IÇÕå<~êz|»ö½à®ÑJÊ«bñæ³ Ú•—°”çœÀV"´àÚpJ;òXNcÌ?ñÈà·–äá.£L(·3€¾Siª~3|ˆ ¾iѦ_At” 7– …=þ'ˆÑÉxtú(Í#f¯¡lMùíÞ}œ¨MǾ¿ˆÒ¥P~7u,fÄgÏÕçZƒù½‰5hn¼˜wöªP½ùë!>á’ôQüòê\zõg./û*ޝêü`ê“gÌL£c&èPÇËø¹W“GnÊæ“™‘:tæóMkÚq²ëE|Ë·êÒTEgººï ·³¸Ìg äÕŒ‚(2䇿´ï,öÞ°¢3\3éLnJÕŸãŠthÝê}âëtk.YžH—xGòƒ=ø5v;ª¼0憩U°rq-×¾ƒ-“ù”¼µ¨º>§ÌIßM¾ÊÉÁzº©V‰Ö|Ü TX*Æ¿ì=Êï…N§žòÙߣybŒPÎ:Š_­¡ËÖð·«¹yÝ ,ï8 &”äOÅKžÀ°õÉLèÆ_d¿ôAË[07f Ä®)‡H*”Ë–Gag †lÀX¥ °jy9K NÅwÛ ©Ú·•TiÒ Ä¤Á|bò~“ÿ<Ƚ_áR»>ø zÞˆÀ›úg‰^ ”ޙ·ÞÿŠïKÑuÂý† ø¸.Ìë‰qJsñJÀâ¾wà„ËdDÞÇ•‹ÊÈ+C{þçñ 4˜2–†÷`‰6‘<Šm/¹ø±'ÆÈz³1A18?\žÉ¼’"ÚÃeéÑ7>ôénÂe¶.æ\ªéŽYh7 úá2Í%ðòùa6+Ê nꊊœBT¥F3§“¯ñ¯‰Ü©B<\¶›kªéÐO'~‚ÒÔ"ÖBg‚Òû«Ž½’ñ‰Ü1þåÜ(ºŸ½‡ÒTâß^’)kÍ3‰9·5Ã-çò”«Ní ¢Þˆð¶{¨É{]Nª¦Ã¥it¾¬=¾9ÍËxtþWÿù—Ù6¹8‚è»+5€ØŸC|‹BíÛÝH*‚ãÑÜÊÝG&íö£0©×—.¾#+¶ˆ¡r>œKw Ò]^dX±?ý183_'XS c9¾¿ºÛš¨Bô¦b÷÷º´‚í·PQZŽâÓ·bæ·˜»-_Ÿ„±›oãýS;ðËì3XäQI”zoAÌ,eËÖù’*ÍN"“1U¸Õ¹œÞޱíîU¨¨gãŽAV°â[á_÷OÞfÓj/’ÐÒà?ñš58£‚Ázöu!©Xt‚tDB¨ç]pÛ`Mv¾~Í–^¾,|’<žêhËã´¨(–üÎî÷kÐ%i7…‹ÃÓ0|åKüà«Ä÷^ wLœ–ÎŽ]ÿwGKAà—-ج¼ócÄ自¡X6ð”%´V—Eh?‡À9‰Á±þI\µg¨¼À#³FƒÏø;TÁ¶K)ðcÿ^¯ä(f‘¯ó{M¡Ù .;œƒ£‹Uq»WZÜíÕ±à¶æÔm´Ëó)óü-3þŒ§Zqít\šy ôcDðÑŽ…´â§*¨«}#|¢$$Ç÷kÏÃÔ­ÞtÖƒÉ|ìG;:"é£p½íQLîÇ­ÖAlÄçéÍcC¨ÞÆÄoˆ¸p[‡Ñ<É?>L›8Ìõþ‹:g¹¡õ(Õ:Ž+Wèq×­†|Ì<Êã×(r8?œ¶K¡”ßxbJ·\ÁÑíkIòìhº2ë ÀbYÜ\[ƒÁö—à𯼗 N›+¹æ_g,ÿü_X½‘ÕQôV¼;•è-'ö24t»&Ó?Š«ÝœH’&aýyxxÁ)$¹åpr†€.œ2“ú‹)¿ðJ<#»…œYWN§¡8÷2;Ìç̥(C*º~:—òë÷ujö«†žá/tK½ WÅäɚη¸å™)ù¹ù·im펄Ñ]£iø§<öŸ¯û}“3~ˆÀ_ÖÁôÎÊ52Üž´êó‚4L{|ƒ¬žÁRõÿ1æbG£Oíıó_ô’­8{Ñþúâ &Ú5ëP^µc{l\¹íÈ*vó™5­üŸ)bhUÉ"þþ„ÏpìIµÐȹv^Tá«_ä“0£C¯(CµÅ\°ÁR@EŽf5bÑàl ˜ÔLÇÉ…uðò⼩»ÎÏwНŏ·A¯‹µ(dc$‹QGû –?WçÓK镮ߌö§ƒÔ¹üXÅpÇX1„,=ÇÎ%g¬¼ ;Y–Gø…ð yb¼»o}(M—Z^Ý~Ç$+ðÝ´ûÐ]kÀU¿dÞñ焊Ç þ›s¹ÅV4L¤î|…!EÁû`T¼)wpÙ ¯ ×wÓÝç:Qðý4Üx™ŠÊ äÓô\¶då<~ué}áð¡Ü.Û¶ßoÄ{9IlRc(Ÿ œ@íd+„‡8ò0UÐ »Òkæp·*7ðÚ&ÊÿGÄ{Àcý~ÿãöÎÞ²ed¤eÅý:—Š”v)R´w¤mf4È–Œ2")›Â}«HS¥©(•–¤¥EôóþþŸþûqß^îkç\Ïó|^ó8[ÿ´ÂoMYf|w§Ï•c+Z‡Ñÿžžƒþ,7±Î†}­þðNí9Ývr'ëÎ9Ï9Ä{PÏ_‡™kù²@MªñÇqr)2Š-Ì ßNû£CÙ2“ 7É’-Ü,–•û·˜|¦iA®ddmÌ(!ÎY Ü:nfŸÅZícà™6À úòAÃú š^-äîÄÝæ¯"‘ðzòH«+÷•±ÇÖ˜éÿ½ŠKzü©ÂËrPZýÛ#Wã‚Xø1AƒìŽaê Ë`ßÙ©¬g”Ý½Ú >­º9?üYûÈ™h3– ò¥¦ì¢ƒ¹$0.|;®¸]}l6î…¾#ˆÔ5EfÒy CdI†ì0·®èg¶û4 <ƒ#ú¹bu«ÏfN›ÈÖÔtš2G•æ)Ñs¶[Ùâû]ø’ÕRëÉ‹1ý­!>íûMg>†U-ˆv ±…WZ›ñ®‹56‹E;ÎAy»y©‘†–{ÂÑ›…£FKAm-%\ªà4ãëø¾®¬~œu-`üû÷àôÔJؽ l7†ñ¦íU"ï”åÙ¢=caê#òßëØŸóˆJŒ|S£¶š¹Üš‡Ölxç ôS¼HÝ·¬çƒ8}z<¶4æv±Y;Ÿ6ƒÕü«ð¶a6ÝÑ,D_×÷ƒÛÇ4“u‚Ç£Q–|¤ÝeòäÎ2Mrôd_8#‘œ-ÜÏ·üæ=ÚéüˆØ)æÃ©K1¯š1,J‘ÃÁ«»é‰ˆêhhEß•Īõ¼ÚWóà½ø4r=HŸ+*³óûïá¡êÑDdMÿr²6 ¶¾Ã+[ÆaÌOCÞ–ßH_m^Q…)˜ÜÓŠ­Úfl×” ðc_%ýÊÕÀ˜¬mx¿Z\-À…oÌ!Ok'¬y@5&¬ËubœžŸó.K¥Y{#0|9fU/ãö«G;‰LÇv,güW~ð†FuM§F*‘0¸Ä‡“«¡cqÏÊVµ—Xv%rݶd •° Cü+“‹iM%pÕ-§ù6Kkq_µ4‹û•Ë…ß”' d9/ðœK´OÁŸ7¾p碭árª0o'(~Eh½ ùr‰®ÕÜ!—ör»,pò›P®Lþ;uŸ&Ç×,þB•;ÿÀÂwPhÇîíè•lG^$>®/×$تØÉ%ÚÕàáÚzNȨ̈ìXˆƒÙ9X`çÄ fø£«„!c³ÖRå¶éìHȼÓ×FŸÒEŸù˜üþ4ü×;zÍkYºèÈ/Úqö*î| ÓNîb&&œ‰Ârûe×(ùú'»ÇìÝ™ŽŸ_«¡ÔïLJãÓqýŒïøêö:®á¼"·¯ð,÷éá-ÌW1äüËðûcsíÞˆûEð;ë!gì9}²ߤ»çŒ!¡Èt ý§ CŸ CÕÝ™,à ÃTÛ{P­Ù…ÝKA9Iÿ¶Ù°gL0pÃ;N‘W‰w_XqM—ª@aí$tˆžFtz”!ÌÌ –ÈDéç€ÊzSóœxt\›Š_$)<­È¡1“1îÕœu2Æçuì_³fëÕ6MÁÃãÇ1½ó9i“ñì{]Ï:¹êò1Ôór9Ö‘E¤ÒÎùµô íæ?ôïôÛôÊÎmtþsqlK‚ªší ¡-MN¼ºu¢'±hLZ ªâ³óà€“ ´ì[…*)§QP5”>^nêû Í̇L/pÒ-ñK,›+ó Jòq:ð­Õ¸Ÿï4Qyeë&YLØ-JlŒNqç `Îã^þÓoðm~6}œ‹“ÜêA{1(„ì%9—&3¿Ñ…ÄéÏ(Ü·R¾èaÂ$uø*±¸“³˜Ótoàu?¦sâõ˜Ä ¸×y9EE;~!™:>‚Ýr?ˆmz®ä‡—‚ã¡ç³ äúJ¢ /ÈÓs’ey?J±za:÷kÏ8rÌÆr?o„{t:WiJ^¥ €c%‚ÿ §üO2o™‹û‚:ÃÓá…Z,3ÛV͉<\ÄK[)ÀÀZŒ$µ?@ã²ÙlÐ7¸Ó½߸éÀý!gȺ^Às¯™ƒ“¶A¿Œ&››/ »ÚÒò¸%³S sÝY@âU~»«!ï § ’ûpÕ|&=#•¥> †ç×–³ÝM“ÈÚkh“Šû³Œ-Ôߎ«Ó\°Ó`œ~*÷£añË (kÙ8ªd ¬r{E5Î5žæÙ÷€YÛñb];žÔ9ë_¿@Í㤛/‰Io’зâ%wœgÎÍã+³Aû$nµÔ3\[^‰ËJ-Àdú{š‹"DhÒW.<þ’`…ΈDô=:å° “m~Á.ÒgñÒW¡ôüp 9»‡eÈVCHÚžŽʹžÊ ¸Pðùa¬h}Œ*ê",¦è·+E ²I†ˆIÆ\‹$Û´~/üzh?Äsbwäšù·¼Ó±äg+GŸ@ÇsqÛ_ %ŽäýÐ_¬RÒG‰†Û˜2Sk+x+Y•Æ®C\œ%(ÀH…zNÞ;ÜÜÈñ¹WPüî_˜¼î]{í´Í9E_U¦4è¥WaÊ­'ŽaÁõxãÔ®n¡0 y(OpvàdU.bAv7o¦};LÞd Çg^Ãáq˜t¤>][¶×Ö3½Òx<ìꀾÌΜ…:r1ભA¯†¼¡–óTà˜ô^HÞÊgßÂÔñ1è¼+ ³zë¸ÔÈ|®T;›3_p·º|áÛ3è§ Ñ ¡¶™faÙä8,î¸Ü0ECAeÆ,¢ðZã'úìF2øÈVqlÊbÐñÜÀ×ýªH–/t"®Ù=³É¦ GN©”5;*é–'áìÈu{È­ovõS÷Þ*z®%…Óµ¯†Ýñ?QS¼ —Ù½ÀÕ+ñõ<º¨¨Ž{|ï}ÿ…§ÂHÌŠƒ‚µVd»T¼ ©€  ‚ä.©¥WÇOCƒùú¸©a9ȘNFmݲÿêº9 ¶ïÇ ^L9à”83"4vàÐV/þ]ûP¸˜sÌiä9¯NkWß´ TÖÊ1ûF P¶ùÊ[7<ˆñ©ûXYìDb-‹{kxÆ_¶@Ù–\Ïîƒhc÷•†í¹­›eÁÆqÍÞ{Ø,ˆ§år7Wz`ÏÎLîЃ:×Ï‘i]œFúžð…BRñÒ´ \ øþëçìz5.v ú?ºáèŽ]»K„ì²¾jƒ é¨êîÁ4áét¼‰ Ζ%q…`üò2<\–º[g°1R8g¬3¯dõ°œ7‹(d«€û“÷4bÌ4z½ënsŠ!]mYð·Oõü»qÌK9jŒcµ"çd_.‹éããUxüèáøDRWå‰.q ;á<½&0ìÿ73`K`kò\ËWb-9«)Of?¶PÞTÍV|à{|læ3O£"~Ml}=n DÂAyR4lÀdÏ•Nk¦ÐhÆ<&^fBú¸pÖDŒR µo `´Í”W‹@”þÀÝ¾Š¼i­#Ÿ×Óxžv8<‡5yXø6¡aÑ—¸vÃTî¥v7”¸æøyÒé¹[Ñ cŒ.“ÄÇ¢‡ÑÓ¢ŸnLñ…®ÓI sÃ̹Ö,ñŽSúó„Õ q#Üqœ»4iÈ(ÀÁ_FlüúY0÷Ú~l, ä¦Íˆ¦¼'k Ág"LŸ¥€™"yܦõŽ«Ý¦ [‡sH~Œç4fA¦õ˜Ð‰åšpówЩT"Bëa‡o¦ˆ±·‹_ëŽAÉßÍÉ|,j_†¾vQ߯ãÜÏó_¹MKwòÚ:{ð†×l*»‹x`&óS€¬ØcFÇ0yXwè3çcÀ1¶Rd¯óŒSz"Ζm)À¸·µpÄÖÛzuéÏ-zðàúuÌí3#ÕÝÉý£ï¬÷à¾?&§ ñSo^Þ¦@Ìg8pñŽ“Ê_ª2V•NÜ?Àí¹ó2—ææƒ¼qÍ8×ü3·PTj'ñë3y¯4>ÐE£pù$$•.¸š‰>D5C‘JNËdžç. ß>‹#é /=Åú‡ã¸¦•ÿõ‚F/ÇlüÐóÜŸ®@%mb9^ü¦÷WŒfÏ¿Œ‡_žx÷ïlÂæ%3z"ɵíÉÐ;áUXÙ¼KŠàênÏf¾T£Å‡sè£w/Àèï 4©zróè½w1N³ZµÈŠEê0òŒ·ðÙu|ðú;üùV„Û´ˆiîRº^ü#ºo¸5Ryv+¾á}§ùøóëmjb©¦ß¸Y÷4Áo["ø:Â[hpª‰í–öÛiÕ¯Y„;¤MÄÄ›À£|!¤{6S]IR&J"rÀ³¢þ˜I%¸I\žÝP¨Ç9PHͺaÿäÉøQC•Ú…’æòLH}‘ŠËn ÀÛñv(¹½®•÷ƒtè3ˆÛ4vîn…}R~ð÷rÓO˜¡OU üœ¹šõ¼Tý{÷úßp²4x‡KëÖñ§6ß§ŠAÄþ¬ˆ‹¦÷æ†ãj}ŶJ¼sºîœko=ìr2ôp‹ÃW.Ê?qß‹VQ¹äZþ¬ÔL´-§ß³Ra–ôQNêP*õ‘H‚gÛ‡iÐë#ô}Ø3\ýX˜Û·#ÔNÛÏ[ðÁ*Wz›—o;ÇòW5,DÕ”®›\ÑL—¸qguàÐu.za>|+h‡) o<$ûnCeê·Æ=_. S:Â¥W8}CØG^Ñ¥Ý9°©Ý]FM‰'4 øR.œùxŠ[ÕÖ7„Ùã* ¥¦SØ»ó`\ãu 1<C%räÅ'Wø¢›NÞÙ|‰´œÓ• »‚¥H¸ÐØv!˜þÚ ÎÆ-> 7­ôX²=ˆÇÆ©×H¾9°úÚÜW…Û[ÛpÕyi¬ë‘!¯b{¨‘O4)Q'Ê[S¹: ef?A{gO‚ßyw1s•.ÃÇBxQþdw«þ×O>~ý§É FéÇàÛ©Z웣1‚θ¼©ÁU£41ÖB³ ¥ÈâæßPW"Ÿ  Îe™b ü¼€œíæV/í-½#Ü_˜É—ÖS9XQ”fÁt] ªþ<Œ5²nh89b$Øs L„<}O­GO%ß½Þ%êÂØ*¸ }?Oßü†G‚a¬Áu,>mÄÚ‚Ã` UÃ} f°uÇd‰fâôÞ!0º®NT<ëØb›»|YßTæóÞÆÍfA6[ñgF<ã{ËÒŠÑwãO´ÕØFBŠÌÉÁ&r±|3ƶpŠùÓÉÈüÜéÌwôaI7'›É QŸbmÞrïãÎ~·X²Œe웵óÕ½ Á÷zqÍy:jÝÁ #=v>A„ZP=²ùÄR&¢¤Á 4 dn.ecÅÈËï㉇åR0ýCΞw$Êž©ä×—¦œÚ “Ö݃nƒV® ¾îsìj/FóA._1z1Îû %Ï|"¥Hî†l[º—ÿb¢&^^×ÁÍŒ +æ›±÷“B™ÛåDpp8Ž –ÝúkÌÙç îMšJ~¬Œ‡l'm(Ñ¿ƒ Zóa•ì4º9Žö¡ì4c¡S‰Øxeb÷Àœä¯‡U–×ð\‡$fê\$ç7K°/L—dé“ò^¸<ßo)AgÁ\ÔZ«N†_î#妯ä©;ÉÖI‡M~ž¬èƒ“õ¥õãÆÃøS,ú|¹ä›‘h)æJTef3¿EkÉÆ[KˆnϾ»¥2Ûÿy$¿{ˆ@¶Œ‘wfMóžàØ¥}¤ûÖ!òyòvL÷/µÛ˲uÔ‰Æi,©.Å5ÇÅà•àVjw‘Ó9wÅ›™ýl=‹Èza©è¸ï¥Müß'õÛ?Â,¯®Â~/{ñü7ɔـu³mI'Þâל¸/÷îCžNU{ÌïÂmË,¤˜Å/;æ6‡kpøíöÂB,·«˜A_U ×—‹§:Æá‹ aöqk qs,ƒÎîø§ q³;ƒÀIJÜà&U_6®áRûN´Èrù†2Ÿ°JÇ6ŽžÑcg­ØÐüzž¢ñ ²þÏ5]í|ÂÖxL$»ƒö[z”^_D&‰XGOØ+ŸlèŸ~•Ú$HBósC¢ èÁ‹QÐ?:xwjCÚ 5ø'¦¡€Z*7#ËÔqÔrS¸’$Áú†tH§ÁW®O2Œ”I‡â‡Ò ¬"ø7:Œ`ÖÇ0Æ`ïKyò÷/¥þ5 rî\[NåSŽ’U™ÓÈMcX²[‰×IP<˜ ëÕàðxó².ã‹,‰Z À1u¸¨ÁI×áuk1Þ(ÀD‹w<Þ¥ï(* Ên›™ÂmQVùt'..mewòe¶4þ YwÓàd>Áëï7Cü¤‡x9i5¹”1–9¬úKoÍ‘#Ü\Yò@Mh܉ɬŬb–*ùÌ ²ØÖô†ÿõpSÓ–‹JÎǹKÎÿ_ð„}.Ž.±G¸–Ìeô™è¶à­7¹û‘[gr‘+ü²‹Ü½æÀ8­WÜïºܓ׀¯;•y‚ÂB'ö{WÀ«n”!QðÐÀ ¾Z˜°©Þ³Xð+qÒ!Îã«î†_Ù <¯®â]~ÒD9¶úó*^Œž190 1 ÔÉúØý9Õãˆt ÕKžàÝ‚B·2@n½ ]-2žõ ÏFõ5±~Ùn®íMÔY(ÉÆMäTû¼¹¯âˆë{cÞ{«›“°H "W~£rJe´r¬™ScÉp¹­…}â×1Xd Ùï½M)¯èDæÿiF‰cSÈ×ìÖ0‘á•Z[ú·ðs°ô¼>û“:s²‹YUbç¦üÎâ~;í$6Ü7'¿t{¡>UƒÉ{|„^Þgl»xšh/¸ ×ÑaùxfùM—CuÿfNw÷N&h}“ìËù²ÖÆäÇÀý»‰%_¾Ž ¿Éૌ(ô¦¿á¦CŽí1"ª‡HÌ•n:ãÝx85/w¾…â54t(„‡1Ÿ9œV§9v»É¬þbŒ/O)§!Þ©åSØQƒKÈçÓ Ÿí:Aÿ1³¹ö’p®Ý г+¿p3ve¯ä6íßOvä.`.ù |ðÖö Ò‡—þBÝŠ{\ï>L)Á —0eÅi0(èM^&ÚƒÀštÚf•Œ›ä:¢ÀÒž£Îiøá˜üÿÕLÃî\Þö“ápu‘2K”–‚ …nΣñô«‹sÉ÷ÄôyRÌj¡œ}´Ãïv`ØVq‚³—q:ÛÄHêƒhTOËïÉæLìjø+ŽÍj†äÍ Žf!g½a€²'·?½µûdØäú(zÛø$ñÝmÎÍoÆ›³V¡!ªhëJõ=°ûu„^+‡ÂvîäÓ7¾p[ƒ*‹§È°«@ƒwª)~M~8ä'Oõü4nhàÌ8>=}§ƒ“ü0º‹kQi-1ìKçƒÉ›h*YÊN”Ãþ)¶ã` ‘èÉÆ…YÄä» NÑ€aQ ’{æ¨Ê]®G`zw,æ'WãЦMä“çÈž…GƒÉêÔttõ_†z¯Ç’ª1ô¡€„¿yLϼÜO$[®cD¦( =N¡þß¾á¼ïŠ,÷”4S›y o½‡–óÈ¢Vlë„‹òI<ófÇ¥Ù=«“ïg‡ È™=—ø!ßæ3“õ.0éØ-xvËœ‘Ëø<å5ŽÃ‚y°~Šܣ²sÙº‘\òq!9ëÕ8ƒåm7^ç©9KÐð×Z2v$æÛ<qŒåæd)’A£jZ¾\‚,Ñ •‰oQ¶v9÷KxéþÏö2­#êäÙ½l\?¦þ¨S“­xcK4˜y$3Ñs±Î¾åÁOß±øäF/:¤ÕÑ Ñ‹ùF;Vò:5UÀ„/DjÎÅLKäkF3XT3JVb“i·«Ñ˜פc÷#Eئñ?qG'Þ€¥ë7£Y=˜Ñ 1~øri]R‡7“…0Ýç#çyý½¾*B+RèwëW(m4š÷gh&oøï pβ`7oVñ§Í_3~¾À4µ{pÎñ(¤,º‡êלˆ…T)}êr u¯³•SObã|ñsÞ:ïL«Vw‚æ²GÌáo(\´eÚóÏ@¡ü6¶KÌ~ÿÆoG¾ NÒDgÔXóŠ‹tµ›·xØŒœŠÊ<>$º*^¸Lã+z݅عâ$GDˆ=,5!“TÏR•‚8nc8[šš…ç—KâôÊÐs|÷¤7*þuÅÃ«Òø ‹71ƒw¶¹@Š-ˆÞÎëE²s÷™±_3^]ýÊC#Á4ï0L»mÚK°ËtÝs.‘ow¾›²–“¯ÚAÆu5ñ­ˆ§Ó…¢ÁÓî2m±ÖfEZÇÑÓThªÃûǯaL‡/¶)Iã;Çë¸~&OË@÷‹Ãkv×ù5Wì2›lÒy̓ÒóüObvàÙÖ‚÷ çñç1=+ûÔVBþ³‰ø`Ö¥ÿz4ÃR÷#è¿5ç/pâ»Z¹qðÖ ÷y|Êäamý/ž‡ãºÆe»>7÷dk‹ÑÞw²Ø¹d x­ˆ@ÓH]þè„K¥7’’–¸r\?ŒZlͼìöc²Ìch¸ñF-Br͉ÃA–嘆%¥s8û˜!œ] "SùøG§¯EV`¿lÓÙs™ë@Þèïé«ÈÌUÍ*ÂÄ zú×Ñ=…[øñ',ÈöÐ &þÉ“x4™Ì 9„^þ…ìƒ@z4Ì µSƒ˜êâKD\TÙ½Nä›;Ã}«“xjÉ«hëVg–¥¤Æ¶Ð¥ÐTGí§"Žáª-n¬Eiþ8p†8¿êÒ.€¸É5ˆ7X‰RÇ£1jeIQ–yupýÂOtØÿ”{4N†,Z†2û–±³ð ¾ƒ_è7X~Ï|å¢Ïö¥øÓâ#ìÉçØÒc³!ÚV•Xïn„µÛcðsI+.[ÓFœº¦=¨ÉÞëø›õ¹OÅ‹‰¡ø|âÍŠË“ 25¼ÿj²mf¯eÎg˜ŒýW›ü€ZnU!£~VÁ[c€UlÉÃ4,M¹³jîD퉹þL}i[ƒé½qŵÍè$¹·m_Ez¾ã‰¢~üa}ùt p}µÜFu„ƒŸ×¡+¸Úô4úQHŸ…óÙîöpr©ªé’1ÄnPÕ똳ýnnýÎÃá:W{ó3·!âéNp'úS¶ÞƒÔö­†3i|jø]œUWzã•×'H$/¥²õg÷Ø¡âT¢­˜Ãöu"#{`׆®r7kíIù\!\?v.v¥2]Ð t$Õ›CHhv+³TÀªØ–+Óµò÷ðœq.žH¢ˆ¡P 1)Ì)ÎŒ}Z¬C¾a+¬óñŒs-éØ&ŒoÛÂèžË§ÁUT‘9´ãŽm&øƒ³eCÂkØ·Ã1`6[Ø>‡›X*ÄÂÍÿ¢çM¶ôÖl ë +5޲5_NÁ’†>Ó•Ä‹¾&‰æ±Û‰üÛùìí‚aXßÉÇù.±ÄqyñØÉL&o‡ È)R0Âíö<Ógy.kȹØ#DÑBˆ¥†’ºÕOÉ=AU"qÀ§3§˜­žõ…ŽÙŒÌ¯Dgt ƒÁŸgø<²dÙç«Ú¬aáNn¶—*ã>Í&/¥ú×Ó!ÔR ÇUç‘™öf(ãSCÊFäqýQ|&=I;žA¹±åÜ£g†¤Ê¹ ôŽ™Ó.ÃzÖ¾ÆÎMuƒ»ûxÔGÞáS £aÂìü;üÒBëD#&½Qô?gÃ/ãhÁX'òÑf»xò#œ›IŽN–âB¶}çÿÖ8@2‡™­nÙ¥m¶KœIwi6´vî`Öû|Yשt¢“Èæ/ºŽöOF“Ê^GnÆJ;¶nM-#<Ms4°û yP0¢Ž&‚Œw°ÅBž-ÈOóàðà½y¥„ý7\pìæ®äJ66Ûí‚«èAn:7&| ªxùш¹˜›—‹«Dð¿šì³Í—Á(8Ó´Ù,Û+\ƒîgîÉmØM„«sÎCŰùhe…šñ<¦âÑ"%9lE\g²à5šÞ¹ Ò–F¤ô¡[#šü“/Ù2]˜üœX¢6/®@PÆþà'|ü¡ §fH²‹sƒû¢|H½ê…É’µGPÝ iâ®dk+DoOÇûûdhËôLŒÀ@²Ý~>šÚ)ã¡ò 9íç'âgC˜²¬”5^Ûþc9V‚ì=ü×zq¬-Ûqf2%†ýf¹6ʈù”¨µ{5੽›´ð˜åçh.~O5™´N”Ž"ÕIϳylÿµE;Ú€¸i}Ò§ñ·£(+uc>xÀÔŽ¬îZÂèƒÊERÌç–>¹”W9~ÓaúN%,ÍÁJ/VºÔž‹›"IV,)§“uᮘ Ó«Ãgã›pÂYQæçØ«Xù›±#šz3J,¼‹ŠIJÄæa8ÖNŒÂ—³{p^âg¥øÊ<Œ<²\îpaMèš$Cª$ÕpåÆZjÿ¡ßEíÆ…7%°·]“c÷ú¹m›K±mµ)³¶Ê°£C¥Ø^¥‰O7ëÂúQ³auÚTðµZQìÅW]»Œìtqcò‹z`x¾&K¯XÍuIˆ‘Çw«áXÇj¸bÞ6"ñO"9Š¿}EY$ß~\Ã"+¶ã¬ ¿à…ñ"š1}#K¸*J²õÕ§|Ô?ƒOjÐठ¨Óý.l™¹´6½ Ë• ™Sÿq®¡æý ñ{n<4Ó¯î÷°‘— S]œñ÷B"{H†GñßTÜÄ•½óÁð™wÊô_0´ ®º’'‚óÙÁçàbÆÈÜã‡Óƒ;ù]~c˜¤l.O0Çž®w‚É/ÀÐìtö"XŒôÇÇÑôJ[²ü@õ•Øi^‹ø¦ïL‰räl”½V§ÂZéÀœHu#zGÇaÖ¦Vìm}Êï“"¯ª¤›¹ÃW>àþíÆ(7î=îÉ—áÒÊìYÐÕpøõö8äh¤¡Ótr¬Ó ®Õʧ`×?Dæh³íŠ’¤é¹;¦>g^#|¶kÜ;Ô& ظO–|tJçÒK‘Aå4®Æ/Óf+‘Ú=–yà \¡ˆÞJØîÀ‘ . pä¤Ù:£Ý©rx«±~S!`ž8ú<‹G™'ǹ:gA¸3/#¬›éã892zÔ"o| ݨr¥ž`7§#rÂø6ÅYpM´7Xƒ™/`_Œ!I5?z¬"-à§Å<»ûiV 1$KÙ¼Ù*85*нŽÏQè÷?y3ö§@ÙÀ)~c‹ ÷TåºJ5™ÙÖ)Òä—y.×Nàœ½æè’[´àj ÛêÉÖ|uDsM}2sÐ?Þ494p=ÝÜÃ5¼HɶW/¤ žÐ¯çHfÜ{htÈÅ,Έ‘ìqTò°_þ{W£äý[X¼üL îøo¯XñçF_r¢[>;1ÕÂ<¢Ó1_òÚObn “êo°äÏ8~޽0ÜÊr"-ògájqüx`@2¶ÆÃªn#.òÙe²eÖ/:õÖv7ŒôÆæÀøÖDœß¯H¶´‰óî&ÿá§ÍM ­Y™àg:`ÀΚ ¡“¶,]çÊ®>ÅŽíÂ…ˉ^&ÃÚªF6ê×îh-{u‚[›gQ3Ç#b™tÛ_°“U@ÁÄ\~à5gœzòp=ìÑâþºX²úåþ<­×!°5jˆŒ¯• ›²ÃÙ²&â{TŒ¬ú!Ït„>·Äk2ß8s-yòÔWú‚¬.\ƒç^´ñ$f ±ÂÓLqDzÍÊšU»ÿµZ9VQàÉ s±lˆæx(:‡  ©í=¸×­‘sú.Œ©ï£ßï×Aü>}vQ;Nï‘hØ—„K{â`ljTÚ¯»‹ùZ¦Ã7ñz6tÑ‘{^KžûPh»:žQ-ò¡­–˜Ųñ¿÷rYwøY,:á"FíV¦WNÚ±ÅÑŒþŒÄÃn`ÛÎlVrù.;ö".×v`E7hû—ºjk )„'%ÇØæäõ¤JÁ ñadŠDÂe¾2êiÀ&ƒslå®\¸\5Ìý“»ŒUÿ×{Y1ÿwýÙ>X`úäWÓþ¸7Ü­çè…Mñx,8žÍ9\~~ªLÌ\oDÄàä#Zp©²‘7ñøª°ï,[¸G Ê@ï.Bbdj°nÑ(èkyHSÖ§¢ˆŽ-8Ejq²‹çÁKEùú–¼“`AÆpÖf¹Ð°˜TŠ]æk Ï‚‡šh°Î}Œ•‰$3^1èÛ•Œ*1†H&¿·ÏI̱å&ÞŠeè÷v:w²# ¦>×#Å۾Å1mÜá¦zX£*I–ÅWàùùo¸Þ?!øMª‰»&—ௗªxzE4*¶üÀ¡Í0±¬äÃÀ61 –&²ý‡‰ÝÐWXžö “†Ô@+ó%-ÿ Â6Fæà¢¦ítë$ºê6é3jõêátÛ*Œ-†Ì?ù”÷T˜¬í°ÕÍ>¬Á4é¨Ï¸5Ä ¬ÞùCK®-»lƒ—l‚ÜÆéäÖÖμóEO¼B=ëpP‰ýivM¨Ûiíôß]„²æ±óGóƱ0MAö~ÈŒ¥7–-E…Ycɱm^ÌùŽ ™j§9¢ ÞPÞD‡2q¯p§à><1³È‰.þKC…™ZË3z g´—îiìÔPbGu”‹tɧ©°¬õÌ«]>Ñ è;¬ùðŽIž‚ÚËÑÛ]wo_†ÏxßèÕDDêbB;Þ8õwWq7«(ïÁ²KÖLêØ[pì]–ßuà·"ÚÈMÓ¸S5ÌÉÎ âæþ‹Úc±ßG–5â^̶9ެùüñ¿ÚfÎ¥dlï<tɱ#÷âlŸŸ'ýÀÀ籬̓™–&ÓHµ±äQã1<æ° òäHÒ9#мéÍMÕˆ9­TÒ0‚Š5ëðÏ+M²êe è cg@ *{<Â5#kNè“x?îvP[í¨ÁžÜEADÜGžž6Zw…7”%§èèº}èñº“”"Q¦íô¦c *ÚÂ3‰ïâ´ù›aPAç={…Z[!è¾6¬¶×#£·JqÛu¨f‹ ^ü2#²TÀlè Ï\N¥fÅ“•sQÆLõÚ"÷˃»ú£ãՔỗ7Àj× È3fãO@”kXM„” ›Ôœu°sã\ºcx7$ÅlåVv\ _ó.ã¨1î}U,mׂ¡N_úô› &—BmÃF¨ž/Ì o¥õ£h‰ 4¾ûŽ¢Ç*ðíêDªúô'´g\Á¯O1"¼¯œq¦fΉ˜8]_”Ç”;™ÜÜÒdîy[%wøÕw™K´Ža@YZ™… k…ÙpÕ žô;%¶>Ö‰µˆä+1Å=›œbƒR¶ð”Žæ\‚ÕYøèdˆ~釶ª#ó~ ×¤þÀþªdÜMU¨kÈô¿ÞFS_!b˜1:$²qø†!»<øšŽQÐeQçT1èö*+©Ëviû£q̾ƚMUZn) À“0cbþž$©L:Œb›ý®psfEàÁît½« «Þ¾€l(þ“’.sfmkÂàDœ2knÎ{y’´?ˆÅm>Å­Œ•¥Q¿®Õók FвúQ–$4%Wæì+y­³N6Χû¥¯AlӛÅqçW-Çp i‰œI>ÍÿˆF÷A_vïí=ÌdµIx¶[üý4÷ke3>?ŒÉÛ9Npà;hxÂb³rÎlÜMX»+¿½™HâW‘®ç“™™YN¹ÚÉ9lÿ§„Ø©-)0ܸžlù1žô½e0z¡-;—Ó­¥Õ}<µ¶—0lCÏþ€ÆÇeØÂÚl®kÕ(¶Tð-T7 £8~IΤ¯Ø‚°ï*47$A»÷`¤ƒã4—ÃÁ¿·ñR I|—~K¸zÀŽl‰a@ýÞ—è£}'X¬ÅG-jÜ¡&Œ|‡½Ï¢©E%®ZòLgÜÅuƒw!åïdâ9a._q×|Z [ë,¸³ +@”XÂÀÜ«8Q¶o„_†O¢1Ý"‚{ª£H¢ÌKÐnB2Œ¬ÁEÊÏÅðÓée[#¸Ö(eNÁž£“ñÖ)bÿt»|¿¦v0d‚aøðâL~YM*ÿâ±ê{²Žóû›Íç¯?ƒÑœKZÎÅ¥+;éµ|á^¾;Gߤ.&š10Íx4žô\EK¯KâêÆ\ûomÐKÇYɆôŽé[00 ‡âÇ3‹pë¡®UÉð&! Û¶Öa‹ÇWÎ*ʘÈö¥ã¤2NÈ¡œ}÷(W§ò¹…æ5pu‡iÛ'A$ï¿æúÓ•ÈÈ߈²®.ï8®îšEfôȲØeóÙ-9&%WŒÞÀh¯-Ønº —ŒzéÖHCp/£`ÊIãÉ ¿1ü§û屆57ª=z‹³ÜõgbDŒvÞ}Пg …íà\¡ŠS!é¿/ó¾ßÀ‡] äÀÙrðÞº ¿Ú Óý/¯áÓŠT¸ý%‡SôäÈ«P7vmx)u чI'3 NÒœíÑH„ñÕèó&¨æÐ·¹w #;™%÷âæ²Óø³~ÑÞXˆ®=ù¯ªœî^ÝSË¿¿¡Hã<­~9º.ak†Š[±¬ 6­î<\OäÊ´By¦«&ã™å÷y½Ýð`š U÷}H§Þ›Åb3ðí³P~Ÿb¼â_î°Ý-nåd1§–rö#yM7a¦vh“\ŒJÍÙ¡{g–/¢þçáʧV.pq,xd€Ï?(GÁ«0SÓ„cG± [ ÙtwxÐóØ T l½,Ȧy‡¢› É5‘AضV „nªÐ'®âÇD2ßG¼/܆ cèö៸FÁ–[N…y’{À¶öÔ}ç¨sŸC/q_ó®;™œÍCÑŒ~Üª6UêÂý8Kퟄ/ŽÃÉ‘œñuVçüµƒÖx8ð…gw>"¿k³—ŸâpÊoXûõ7nÆTd,†«-\aö•~œ(a³¯©’ŠwêÄü5ö\¼@ïŽ!]!ð¦\‹¾•¯¯}C÷{ÇBï}G®gáWTþÔ¨_¶4ïä”ëÈ”}OpEG/Žší¿¸n¸8_I÷ýæ®Îìãž•ÅÁdQ ¶òåìÑÑ`M³óx9ÏŸÂŒgªX›³î>êAÑ+&¬íÄe(?QÉÒuTÉ% ßçµÂºÇøv[.ÖOOËrcäHá Èg¡ÁÎüÑ;7“Š nDï»’WS¾r[&õs×î‹‘[kqL¿ÛÐS‰ÆÞWqÁžý°Õé)OãH0ÎTãF^økЃxt §úÛ t«Ì!”ªëQ1(B_>ý'î}¤¨ŒUåü¦¯Å'Y[ˆÒÇÕ”*žàü6B¾ìA,ø CÎTDBÁ=HÝ%Á¾îª†ÓçÓéQës°1MºšÎ¡ûrvB9Ïlã8fã ²ûoÐîÅMÐ9G†k'±·?¢ÞÎrxf­µ7á‹ò4n›b³ é¾%VPF]áÛº:î}xºx„ÁŸõë¸ Þun´3Ôpi$b¼-àïûàâÊ;S*JÔμK§P2)ŸüWo}ÍH©Ï¢-ÞóVÜ]‚®U®àné>׳R© ð²q4ruxIð0v¦.'úBWð].0}¹P³½jKàªûlÖOÀCûmË5¶؃òV´Ý¦N~`»CM¹ÏÛGÁ¼ðj.çó6óë»Æ3÷ag]NRNƒòuhQCyàE ‰|ëtr¦ò+ï‘O$ý^´ ¶]ëG²ð®89Y4šÝ »Âé]åàFÔiLONžõõÓà—WÑþS‹ôêù‹µïLá5Läï‘%Ašd²«=ÆGLÅ£±nMÕ¿qÛ1£BÇ?Ã+¯7¦H\ÆÎXu~lÛ(œSc Ï»`œ•ñ^C0ïÄ.¼ÞKœVÚ.!a­ùX¨µÄZ¹&oð Læ´ü$ð{ËeœœKÛÁÝË]Y§¥“VŠKÌ:ë*˜8Œb¾¡*äò}XüSî?‹eŠïç²£B‹ðkÔcÝlÌx(ÍÜ~ÇÛ2Æ +Ab6œäVíá&og¼w½…RácrðwõBôAwÂDqÒöôÓ˜W¸©1y¯0ç^ XDsve™V_‚]÷áÕÛ_\õœ@¶£·ƒã½|G]jÇñ%…éþ «¤Aâ/œ¤œ…!³È|[Øt¸X|˜®íݸ(“Û²[šÉn= ÂJõÔFÏiÎÀדð¸ž#‘6êÿ¤’zä'ãGï®Gú<8yXJ‰l8¥?4¿e¡Aš? úIä:v±[-Át{!ñÔ´%.s°¹!…ü›IåøEOž„4F’Gþ6fO¾WÜaiÂëàyüžäIv„K¦+W rçä¿ã©NlÞœ~¨~gK‰øWKØMòÝuÉÉûÄÏí'îg;+‡–Ã(õ³Ü±;9•K§8ïTià͸ÀÆ>ÝC"øc!ºÅŠœ‘º·ÌsyS!î’Í„µÖÀõQSYwØ5òÊ3]’ípÀÓ†¹ålaÊ÷ ˆD¤‰|½—͸0’7Ú²é‡ôg/=×%:37‹ì…iÍÄÞÛ$>LÛ]†$nÜ[jûrœè9A{gM§åñ³Iµ‡©m ô»B3lø¤E{ÊóyÓH§~`X“ÊæÇ„‘­[£øÝ‹ÅØcM Pÿò®&Ñð¡HîòÅêÜ#“‚à ~ƒ™¶éœ+Ó!þ¿Ï‚tØ4·Jâ«Bá–“7˜_ïÐí8éT–Ûᣠòè^±èÉ(<{ïgÚíÆšwzƒk9ÒmWc0IV–ÜâÇâÁ5vlD›‹ï´æö\» mÞ>ع÷=>ÊûƒIÅÆÄ­ù-ÌuâAka"º. æ%¨æÀšÅkÉ'#L|µ…|:ÌIÌJ‡ FÚ$ÚÒœŒMÑ`[Ï8ñß¹Ù¸öùq6¯»7ÿ¨³ŸWñƒ‹#{\vú“{‡F³{Ëóq·¹irº¶3¾AÌ *¾“eU§` Y•«,Ö#w7,…Þ€£# [ŒÕN©€¿7EÈ]6¸í1'ûㇿA-µ^Ì Ud²ÆSH¶ñDxÙ.ŽC¿Çq7øm#çU‚q›ç’Ýϵác–?´þðç|oÅÁÅ–õ®¯%O&’ù¨|ÿ>þŒØ§ŽŠ21Ÿ t¿ñ7?z¶9V>&ÜÉy|ü?¡Š {餖 Œw垌n€»›QR_€|dÀŽäÄs¥öèš©J@|»4øÊögòUGSnqóXæI²ˆwý|Î&m*$®ÎFÕ¡ Pùk.ëÕ‹‡Â…(yBì6ò"·@‹–1 .vÒÝ"Ï"cÖ‚©n%-±~Å*O¾^½€É]ü•ù$?6k¼0‚5ÏïÓgßµ`ÁùþÉÖNHÉ7!Lpn÷7TR^CÔD1ú|3ø ¨º«ŽNiV£¦Ç²OËÔI+±½ßÒ‡r’ä¶D%œ:–;~aáý¬é§÷GXž¨÷?ûm¢Ö£ìÞ2¸·;€mi2'çBÍ!¯å;cýÜÑÇ÷6—ôךœ|êD6)—¥-ï°ïÏ|üê Ç +¹°zïû´'æ-'[ÑÉ98œÄ±‚¬~ÃJ|ÛU‰§ˆþRÀ’1¶Iè$ê„G;ÏôbÚNý(9Â/–$"äÞCÜ9&b¬M4‹'²1#XíØºÇÎhD+ýcøuy)³HdtYÀÉÍàýÉ‚¤lzÁ­HìÃÂv}Ìî€J±Z —GŸ†¿ÊvØ+ÿ‚yÕa{SØß/Ôgër\=ºŒ3y×̽Ò\Á ÆÖ£ÎÊÿüß/ó.z €ÚÁœýþŠí¿€« `ͦ¾Æ9U¡˜rþ –~¸M©¸oH—U6Áž¢‡8·ë¡Ø˜8#|3rà½åA\b(C<Â$¨ú*ôÕþ}ôÕì;X®) Ù°oâ-O8ÄéÄÄ`œ¯ œ?šÌuÀWÅõn ²Ú¯}ÿâÿÝÆb[ ƒïæ’«Qî(¿õ¿Øî)w"¸õÆBžÏc˜³F‰[¡˜É9ÎÞ‚1T=~‹ ¿™g„óODÓ¯¾?} ûqî9˜*œÃY¤Oò7M;5vkBq2ÝU‚ŽJA=g]6êÓ/nïŠXä¦Î“‚z¸lSòȹ¶År‚ìÇ@ ÿ®¬8ûóÒFy®Ã‚QéèéÆŸÙtÚÿ´ƒÄ©#ܵÙó~sÏ…‘16DyátúâÊjì²áè9[ܶ•åwå6p`Ü7pÑ;ÏR®‚×| ¹F¼œ¥þon·Ä³«ö`¶F.>윻$ÏÃÒ×®@w+¥èP¸)Áç2­'`ô%Q²Î8¦ºOŸèž€;M·áÑ^\vx>„®•]{ðjC%ïö«8gŸéžÂ]>†u ½)Œ[ç‘‚§¬5ç(½'ΔBßÀø¶­¤èo4þÜqŽºMñ$‚k&“q®6°nÆaüÖf«–Bèûcdea4 ÈžGk7¢þ+O¡•Ýd=\úý î@<ñº—ɵ•Ä@¯Ñ:pcï¼ OeÙC⚸06Rªç2Ãmvò™ýŸýñ~[¸\+m[âÁïR”€Ä3rܽ¯‚¤p .\äs—#îÁß÷”¦Äuç"¹ÅÓòø›ûvs²¯!ªOž¼yWW‚±§«lŽ .ùÅýÁ¢á²@p13~3R]¦aÚÝ%쩃2™õÏùîÓDoI+Þ§÷u ©jÊ3-é³×¡ÉÒ‚-*:Ì<U?;þÑ+€ó/bò³>:ù„(ë;ÒÁRÎ*èþÇð\üdÊÉ9A3ÊÉ*e¡¸F-«Iv&òÎé ‚“U7:˲SE³ÙëIWá–Q.UuWf †Áìö«t;³^òï(µàíka¬tÊ<6±z*ô8¼”)8‰áý°V\=±õ»É:¹ïüîI¦LtV+ΈFŸèé¤Ôû[YêÛT˜³a!wøIwÞ3zôÄ©_É n`M >³ØM¢î„ñáá0¾º!Ÿ†¸2³ 0çˆÌ©¿C`"À.j˜qE¢%àKwJï†éë]Q¢ñ/ŽŒå‚¶üÃÿÕöQÜ£¤6þ•à›XÐÎ@PÞ‹<¾•…ÐËüà¦è‰ÿëµå˥Ι„’ ðÁ0î~ Éþ¤'â&mvòé)Øxí¼ NÏÊè©|ºg…9 ™®Iq›°Ò©†ßVô OVF@Á"p¿¨[´5XOÄi${D“FÎÄ_UÒ>+ŠçïDìüîþÓQ¬8'€ò¶‹° u É~µ ìÚàÿj˜™AÂO2!=Œ-¬P"ø(šµG7r^^ä–W0lLS&/{sQêé ,ÝU7¯ý…Ž,ò\ ›¾^Œùlh© ñ¬L¶ž›³ð Y÷ÒöþœEËÛ¹mêÂlfó>ÚªKÜçÜÁ­"¯ƒG;0ðƒt,Q _äÈ›}‹0XzÌ*Èå¥VàÓvª‡î%Í$’Õ–¦£ÝY ârûÆî‚«P$»y šõA˜¹~we!îÇ`ì |î7¾®U"ᇆ¹ò“‚ĺŚH¨¬dçæ¨’Íîžø,ñ9üèk ‘/D wGoŒË€ca0õt<mqfS>CÉúhæ½þ(,´”¦3õ ¹½ ’ AâÀ6#˜6<*‡Ó¢—i¿×0ç”ç §g9¢™7TEŽüã¿Gâ9T’+§qbLrGð7£¬ª,N½ñ{9‘Û§Ûœ çŽwy.)‰[›]àW~Hb3¿w´\¸SŠíIKO•·Ð±B‚¨øÛ²¿¿ýØ« sÁr­ ~ŒÞD¦dÉàõg“ùë3“üÓèxø,§ª½_)üÆ2‡õ(3ç·á^%×e›Cu‚'ÃàÀ„!8}éKÞäò&ܲk<2Ô‡">ØÛZŒOSnÁ·pC¨f'ÏoÆžÞ.8±t7nþ5úX§Ç1·ßÞ™¦PÁ©8Eë*·iöYÄu•ÿò߬ùÈÓÇ´Ðm7ÿ1¾pK@éÏìöe?l5îçb¤ûY-9ŽoR0&Kœ•î_É Û|ÇÔÄH¸0»¯Ìá²$Sðv/Ã5ɬgÓˆûÀ]ÎG"‹æe“‡Ò7Ȩš’büƒùó;vû¡3‘ꄉâi¬#I†ÜÊLbû÷ß 6¯“¡#Í‘í;¥Â‚ÒĘLôBf^{Â,·“¸Ýú„UeãÀ>1–ÿ~“n—#,·œ5ø‹?ª³`â%sŒ¿´”-éJ„™v/x¨/Âüõƒ¹Ö |…O÷œg™gÝIømcr$wñ¿óÏIiãQ䇿ëþ¶‰è}s¾:»‰VŽçãC»8üYs€ù «©xúa$¾äØ"qvüX;}›ǼÊ*èúK¥ÄÄþ4¤ïÐÁ¬_GØ¡§IˆM5}tª…ñ2 ÈòEÏhï¯ ¼*”C×Uë£Wy¹a?k[ Àv‡èÃÆëITüzRú¸•ŽH_ZÖ½Uê}ÄU¾¥ õ`>Y’d­:EZDÇ=«A¦;Ω֫±Å•Ù¾¼L6¥¶åþV05ŸxR,··‡U³„üm&áìyý.ç'ѺÏñ—m=¬hùwþφrqiþĉG¦9å™h3qÐÍ’)~]†£Mh™}7–ï‚+™J¬ùý°òz ‘gOcÄ€>9?÷pë&]Û$ƒb2JÔ»¦ôæýù10©8 §Ív¥ mêä@´k ‰§UË?âó—PàëBfþ°"·²›àzDçæòVÏ c?ÍTéibWK±ÝT—yÎVdU^ƒ [z¢_–bƒ¯ˆ Hàpa|›DV_©äf™è³‡ï³„“WA÷ËKDªH\¬9¶;h)ûU$Ä478¥Ÿ$r”‰¶ì˜T!ù/þ·&Hà‚è§t݉I,Ðw²Ÿø¢öĤ[ƒ%9[mû0¼÷>žígëÈ8eA2Ûq Q>м3˜>ßü7f„£Ç]ÊéԷߘëü;²Yèzn<2¿„[íÚéß¼kA[ç/½\xçîÊ6œ«„ýö³b‹ï<ö æsÄJ÷¢{çð¾7‘M{¶—ûQ2 Š|‚÷èÈÅt2¡‰à|ê=U^Ÿ02¯¤?ÑÁ:iJ_‡2]¼½[f“1Mîä•G—Õ_ z ~lßWyòlƒ'T/aæ61ÿ잯Á›ûG ƒ¶/f^´„m±¹ñ :F9ÖÔ´_p.áÑF$K¶RS_8žÇíë蚈V|`ìXàS4"aQîEÚüóŽZ¾ n\NáíÉN}š*$ÐJ…]­Ÿ'#é¡Ï °°_·.‹…gbÖdö4ÂyVƒm½,zŽÕO¹ó¾:dÅ‘ã /ž A Qàõl=hÒJ©Xx}c¼[ô 7¦~ƒ¼ñ’\îÍN(T}2›î…K¹G»TÀè¥KÕ¥Ai¬§þ¹#[ÛéÒŒóôVÜO.ótê?üí â›ùÄYÓ%Ùa½ëÐuU—Õ~9@òWŽ‚Û“¦’sò6œ©¢™Qò;wۜПä)Oþ¬U!~wqFÒ.ørE™n°:D»çúaZïPªI¡\jïrÿ ?”…ù‘˜Ç%ÑeïJÉÓ)5ÃŽ |‡ãìåIéçnðý= ¦;f;Ç&b©òsµ]›”6LÅõ&'1ýœ _mH¬$Õ‰ïïðe.÷š³Ç04c3j¿Áõ°‰Ì´Ÿqn[=ȹ£ú°vÊü:-.qKô!P_Š4ij¿Ä˜t à¸ÒLxÒýÿß^¬à+ëtңƹ0Ø$È2y$%˘[úG–ÑmÉž¢@Ê‚Àa!68û'5.XȺïÅPáìy,/m5á5a’â%, _ 9¦Z#˜“ÁRo!?DÀ“Ûîl÷’t²ÓîŽ8ÅÆÍJß9{™xÖm1–ë”â·áäAÜE|±s''/MaªønÑÆKµdVÜ·F;Óô™L ÞnþÕ©°ûA3†Ô¦0Ç#Øç»¤·y©©}pmp lýÔ„ÓˆµÍ%lï 'y0^ƒsmV$‡&0^!.Œ´cË6ýÃÿ{w÷q‹þpÅ|ê2EŒKÈøŒ²›sÑG: ’’GJcöÌ'ÆÊGpÿé,–µdR bæîÖäÒ;"”7–ÌYÖÁÛ‚È€ž»ßœ‹³‚á~³ûpÚG8e\/…µnè4 ÄÊßüÒ×F³t¡åRìßDU…É)Wg"Ò=ž|«¼o'¯bîÌc´»XgJrƱ‚%+XEèAþ¹MÎø¤Y‡¬‰¤P[%EN–'ï—¸±t›1°íˆÙœñ‡{4kåeÞ¶ì@I¬³aïÂÙ\}3¹†ÊzŸÿù[øNô-SÅZýJ*´’Ó?fóô ‚À«s=»ÿÑÝ–cóûY¨ØÙÛ¿ÞpŒc2Dxœ3•ÄŒª””™m—ƒ^ú¼Ö‰/4ãú- ¡þÇUäiŸ…ušC0¶ø5î¼ø²ñåû»Ô%'ŸþœøŠ>³O‡Á‹ª4H™Éj„[ G)nù9²²,ñBL•$]¶î0çvîZ°ŽW[}mFSû£IwT hè'¢ùfy3/œ;²,†ž€§ðDbã%AWÌq¬ÊM.|{´Ö(²ÎcrdîâÛœ­Â œ4¿ðŸýõ¼"àÙK°cè¶LòÞÇ’wh¨‹kÏ‚{þE~©T<ÛüÁ€]‹àqTü üY·ËnË@ebxž›‡Ò!a¼¡ç]lÏaäQ6F8ãO/ÊßCEmcyq¿øúËcÈó0œc7‹ëKíkâ°µÄêUä¡ý©·¸{:JøVÁ°ùAZ²4õÝ–â(­ Npß5ª¼W/W™bÙÆ š‹˜€èf¾³$¾“FöãÂä¥ÇrÚ3gp^]>äþriVn2‹B.ƒŸô7dÉz Üu«JtÛ¥ÂîEÔót£-¹×æË ðäQ<ô A(ÃF‡í5®äö}÷M²†3¹rdÓeA4úfJk:°ÿjJ&ú€ÞÜèöK´ÄqÚ§N¦!S”goÐû -ëÛx~¬+äµn†¦°:¼æ¸÷×1œP¹…V¬»BCQÇ5ŠZÆßl\Z¡À[ °êùެx‡<<*ÞqÆ%(U÷ SªRx;5ÁCðøÌE\cõÆŠ‡cg×vÌL·^¿9úé·Qƒ4ºzo/-yh@n«áõ~q¦¸¡áöiØÑ7ÚQów:žìÌ„#CzTÖø¢tJ®Xö‹J?î4æŠA•Ån,Jª ŽýNß¹ à´ø+X»>‚Ûä%ŒG·aκYxuÓ›öÿÛ‚;ü-àÅçÛø}À6ÇŽæ²ïk7ÊÍ|Ì­®¼Dw~8õ šó½˜þwohm­Ç¿K¦òÆEÜ·“˜ÖÆs°ýƒ&:ßZù¥¥´ñO;ï½Ï&\P 7sLXö2îUÄrŽ ä€É«^xµmxfN>ŽU"}ö÷1µ¼§ï“gÁ£ÃÛhCT>’üíx[ÏŸ² îG\ ”'܄Ĺv°“EÁ¤Ô³PætíÁ)“{ªàkN¼^l„¿_«ã¬_p5éœ+³fAD–í|(Ë bÔ† 01¾yîáÛe9ÿì ÈåË9øóO޾ŽK¬káíççð|©(÷Ò+ (gpëú# Sµ—zÿØïô¨Ýœã£…w=ìSõ¡N]oÑÁlzÜ?Ç ´E`à¹p˜uŠÏiÞŠÂæ¦¨sû<\×sþ,MSœUñ{0oÌšÖ„ErG!Z_›ºÛH“Ú'qxÊMôÑ2Ç5SتîW(&çÊ/ÄN;¦é®XšýħË˹Q8½Ð¸‹Éó!)bwñ<Œûù‚7oê-l>3^^æ&YÖÀµkzÐé-…†¡ëé÷ù·A!^„Mœ©ˆ§ƒýfGý»ÿþ–“¨Oçk.no9K]í äöGœwHwË©mGü˜Ý—S(sÿöo1#Õ'¦Av-¨Êfóܧá½öxÜör„3¯N敬σW糡žÔ Ãà•b ©IÐß®Soé@¶ ©$¿Â·G¼Eã]ð{ÄÐÚªÀ¦îñÅ]Kl°8t!j¬Hƒszëá¬ëv8HÖrµ ¤:®B/Ù‚emìô–{ýN®ß¯€E§“¹\# u®š|Ç-î«•g(âÙiþx·c+e'£ÅÔ}P‰SX¢gìc…#ïÿìwßÛÍí Ï€CÞ|lh§=‚ žûmÎ÷4p" Mn"ïÈô㥣IÜ]§S+ؼOáÓXs Vö8üÑŽ¥¶&ß~éÑ´´Ü¸‰ï¹{ © ¥²“>9”^v"‹•tI˳³ÐÞeÅúŽoà¶7Ž& ¿Ÿ€¸ÙCàÿ%”ÿä®÷4¡ìQôQXf'Dfžmà:67b­‰ {÷0pâž·kÈbq3~Ùðz´ÊŒÌ^õ‹¾Î „?ïCÓ•X¼u3ZYÔrÖÇfc_¶ ±õ Y“¸qz„ÈhK {ßð/þ7ö%€¦™u/ÖŸÖ侚8AÕìI0úÚ H|ei¯øA1X“ê .¹âwU4¼ÉŽ3h?FþL©OMžL…°ÞœÍ"ÞÆŽ9øª& æ›ÜàZ ?@h Ì>ÃõI˜ƒà˜SœÁ"=,¹ªž9)°vê|&¯ø^Yÿ” >…ß´”Ù£GšÐ¯Hd¾½„€"CÒ;#ê}1еÅ7=DOYFô>ÒW+ìÙÌyuü¬Üü;a„g(°û™“ISÑ5¸Ü.Ë•fñš“(9'*C»è:›\e½†XÞR€šµÿü/b­ ‰žÊÐø\—³T›Î•š~àÜb€ƒÒ8\þ1Šš}Ü„u­Õ(Ù]©Š¬rƒ)ž©ëÆÄq“È»ºV.SpÄûíÂæò‰0nú ¬žr®Ì΄É8cÝ$ŒÔxEó®½ßõ¢d½›8ÉßU[íàEáZºú]ʺù õ&_ZöªšKP¹÷†ÖNM“N7äî%ÐsáÌý›ÌM©R„D­&8Ô¥…Ï*'²³çßÒͳ@»MMV%ƒ²Ù{8L!÷îÙesMÂ?“‹ñêÃïè1’o ¿âÍy20A£VOÔÿwÿÿ2¬qÓÎ+Ôéý+ü;]ÞÞÍÖzv?kðÀâ8w«3Ê*Ç WÌj¼ýq,d<¤r¿ïq÷c)ü …K–’çëÀF†l.àï0ßbšðØ¢:zò´™~ÖªqG]îM7Âúîü³?¹$o{²+Iˆ™½vas'À•ôHì:àÇû²Ü†Ö¶fq?Öz©Éxüð n«•sn(ǧsEÞs1XjŠZBKWšM³O0N½ø~ÅKPU#Ç8L×Cgè—i/ðwǼÆç5Ø4©x@ žsÀ+{¢ÿñ_Ñ ³µ­àøí@Ô¿p,ž€ Ö)0Ii q4N!z7ðñåPŒÏt#tüC*kÁ’J‹PÕMŒ¨º„'ô-}ÔÉ?då-ÁQ·Ô©ª©›Z4×ìhœÚ(GnÚ.E±Ð}\íš*níq\;/.¼_‰ãÞìOj—qÖi{(PûƒsKfÞ×™8ó•>ól;ŽG$žÑûÿf:ó_—–oCÁ|Q ¬+ ‡ÙŠÕÈÆˆ±ö|*©G´˜;¾>¢ £ ª0øªœÏc¯¤S©_t?:¼…+XØM?¿ËüÇ7`ï£ø?û½¼WvÜÞ^ƒB5» ]©–ëýØ ×|ÕxuK¾ðër¤ wÞ~ ú»‡3<3ß¿Êâkã+êûBƒŸ+ÕóìqÎ,y&§—yuépýb=®_Éíž“BÃ\£Ãߦ޸@J¤W2w±~M7'¯sÒYK!>§D|=`m45‰¢7©Ü:o §Õ©§Š ¹¤qWd °ß©›SJð~Ï5b¿Òi¤s+¶_T$ñj~2À¡ÀhÔsÞ„ù¡\ÌÐkˆŸ[Ž÷Óg¼h:×ÏØ e¦‡éöÞÏÈx›ð¡DÆ?üó3ÔÇ ßŠÑqõ n½â"˜tzZ¦ŸÄuÒ1p'vÄõÅb_Ï~ȇҪ¿0ê¸2˜U„5! ¸H-ïW[pƒÎ+°ß"®÷òoÜUæÒ?ÓÏ÷œÉ*au´÷^Æüƒù\íÆí`ehJ¢ËëµKa‹”f¹²£_ßÃýw›9×ïN$Ð:G°¨k‘>óN=LÓ}tp™s-þp»Á×Y¢Ë÷I—„¯§ÜpnÖˆw·Éá‚0zñ)7’¯ýÄoÍEîQ³-Ê¯ÝÆUUÆ€ÑÕ<ýp9ù±–wñmüB?˜§¡mI<ä7©3úOüž?Él1üT;™Ê> ƒ E8ÍÃ’ÿ<&¼Nß½’‚Íã×qf¦ÿÃ?Û8z|N ÅÖu‰dÿ! ˆ¬·£>Mpp™Jô¥»ðéë]äÌŽÒ. OxóQ4küí´à”Ë£üÙ­lpk(8}Ž‚BCg¦“«€Œ¡8~ç,bO¸ªhP8ˆ~ ©àó2Žò‡ËA6q1e׃à³ÑkÜ£uŽäLÿˆb‡‰Õö­Õ„ÙÊ~ÁEûÞÐKœ7U¯ ¼À™L?5UÎâÑwdEqîœrV¨ï$ß—ÜCÙbšsQ‹œª=Í®jNÁ?ê³Q˜/Ê*×tÒ÷:<%Á,øqý2~[ÌÉ”{Âînáñ¿Þm,ÜöŸzºq O&ƒ”5˜ôwCdéh.•$QÁ¿«ÉßßÕ\¤Ôuþµ´Pò`E7~‡@µTü)w„37`ŠEí8nÛ1ˆÿŠzÝGPêÖ_¼nCïº/´OC±y DXô#†fõ;’øªDöÆ(_«…‘½³qÁí=`ñê÷EFŸd.‡ûj’$hT×–—æá™WÕtº’ r’…¤íÎ0Z]Ÿ¼œî@;z’å;–ý¹æìE ޾Yˆ§ëj¹U_°Hhô·. &øâùw¢è´ì•8ùl6ýó¼‚ \Yzš§»è(&F²ÛÙìI‡ád)´6/$#y¶ä¾=u y¾×NáÍÇ¥ÿ¼œ¾¥ÃAÝO41A „žtr^;ôØØvA²sü9ؘs–äø9ùŸrJ™òÛîÏmÅá ð>‘§îÆQëA¼|x6¬sPfÃéÊÄnF]š5 ¤KÊŸù \å/ÿ(‘n¢ëû8~ò/Gjî$ÃÒçí!z7óGgÔÐV¸‡!wù°`\ŽñŠŠ·sñ°ß'¼š9ƒ&Oº –BV¬Lw óÚ¯m¢g!8z4ño\ ¯Løçÿš«AµEŒ5VÛ@mâft1T$w¿DOÛÆ•˜~q$ø3HçM R†Q]óIœt›Öe‚UŸNCyÏ(vûè/ø›£aãHêìRê;Û“Ië°Ùr–íæ ñW 9wi’•mMÞŽ'W®®EïžÛ¼æ;•4rÌ&ÅíF£{DcùCø¾GŸ)‡/‡­S\1æ0îÖ<ç[ÂÎY –!‰°d¹1sÜÅ#Åýy‹RÞbÉ´rz6i=kJT¿º³—áÄzßYÖ2Ö‘l·4ÅöÐ{xíŽ5¹ä2€µ>ïÑìÃyæâQùÿUOWÁ ‹¹Ѓƻ5ˆQàY\±Z5“_²¤DAÒêNèwT™r¶¾è£|Û9Dƒ>´ƒg¯ƒ’¡?+ÞœË:Ÿ>À¾¥iŸæTÞÅ€ÉÐ}¤gÊ2åq®¬r ‰Á0¬?óøÑ±Ì§oQ0T&¯M²³Î©Äðá«r:–ïÓˆïƒD¬´¼o½ÁǶSsß“d®‘aY9T%4”ëð]´‘¥›.@³¸b'2ö¶«`? ¢wñRLI½G?.ôbɺärÖGP:‰M[ÂYú³†‹¨²c-Ä:Âtï„ño¬/ƒ<ïÑp"D‰|Ë´duVÉÔ¤× ÓËú`â)Ä -ê¸2ù>7’À³7;.ÒãpÒnq%Än¬;ƒ¬c¤i,%‚×J ô¹¤ÚG“Ó×±ðŒË¿ã€fOÄ1ø÷!¸÷~!j;]‚ÒUi8T†þà Lôu 7ÿà;ºé;Eù©hÚlgKÞÖº»øefH’¹³k‡©÷þãPݹFæbC‡l`êðÄÿöW4Š0l£,Qê6!;õY ?œñÞ¾º„˜k—[àÂG¹(}¯ÛtÆ“{ÊÿôOâ÷|~ù½z쉻žÑYtÚAO˜öă¦ì˜Â¸Šbœš­ fgèù?øÖê2¼È-oótvÊ.§½‘`>¢:äh§$y½¨‘¯4Wúwp׎φˆ WX·ÅˆÙõÖ@1ÃMŸàÑ·ˆÉÆíÔ~ø/èÁm4Ú+b쉺‹7©ýå2üL¡þp±)ÃÕYïðÜØÜ àƺ3ã§b$>Ò_¿4ˆ[tl!ûY†]ýçýìrfڻѯĕM{ò€jýˆÙÌDtX<@U/H@ßcòòogµd&Ͻ gXÛàøÏÿ¯”¸Ø ËùÅ6ˆ”ÎytzÝÂ5mS¡÷ 3ïÈ›¨5v&ñgVLfõö¤ä¹ vmìàú„qÿó˜t=&Û˜³À%å¸ñÎsxn2žùÉ—:ºP°JòÄ\7ÃÈÚz|ÿ¹ŠòÇóêNBî8IòB þžPÄt&'£°j1×`[MŸYLᥥôscbÙÏþ* Oáóþ,Ú¦ŸÆ‰Ï'GÌ`‹½M˜ØñO|©¶ËìnK¸ã°h. g£à½îBL0„ØF=Eò|so™eˆÏ½zCÐÁ‰l_¥ñú~Ÿ\™ÿâÿ×W3ä¤3†iö#Öç1ƒ¬–qÅÍÕ•(üY“]¶Íqj/q¬ÇxªbãMÉÑ€CôI¿=›Ü; {îÊЇññ¸çí%ôÌô` [òbÒtÌ»úGæˆÖùœÕéK˜Rcw]SiÌ5²}ËBܼ~”ÒìÛç0&¡Þ?ì¡¯Íæ±Å<32çä ¼‘è縔l5¸ /o]†äça(±c1U«²aÓ[(tãw\ ï·â®ù7°÷ô®ÿ öŽÒ"_oˆ³Kò`ër5N€6qÙ/Ós½¬}x?_ê…#kþ‹LûÃ=2–¤LßÁöì]Î6zìc•Á¹ð}›9sÑlÄÚ,ˆmŠàî¼_Ë|•–a@±;YÑõ-rlÓ%2÷?Œ”ÚÂUšÐ^îŠw–õÛ±PïõæI2ÏåÞÌU^‹¸ûï{`ÀÙ•½òÔ&/’rák+œÌÝÎùLïŽD¥Êf¶Oó©¼X^4ëiƒjOf=©=óƱ)¢ä³P>9­ÞERÖÈã3áù­›°ùÏJrsã{V`ŒØUI³á§\Ÿø)¬˜Iö÷ÂÊ1a`ûYvžYï‹l™¨Æ¤ög×½çOSŠç&ú4ƒÅÌAè{ “îèRѧwá°Ÿ¾/ÙËí7¼GíNUrÞ%Zpq‰šØ¡Vªôê•Þ«9n°»D¼. òâÂn”löÝrlÏ“ÀX˜È2[ÕÉ>Û|¼ùTmhËógtI»Ý ˜n»Œ„+©=Ê¥N¦‹B0hgLʘCŠE}!wëJ<×ÕÅ+ßøMçÔ¢¬”i_u’7wp ÷Íœ#ܽÔÓî¬8 ÷Ç«€ÇCo(R'xà`.½h\ˆ{#þp­™õÔÿ¥y•“¹û#ñä[EœþëGÿ‹ÿ 5ãQG5“ 2IÁ™©^8o¾-ÙÓŸŠ//¿¡†žB¸ç¸!˜£ÏRGÝÁ=s¤xŸ/И¢cØZs&;ím"d®ê ¾ÝÐY_øà ºÑèÅðy#G].q¯ïtã;› ¸ýs<×R[ãŒd™“g•ü‰îUD·Ì‚ô{¾ì†ˆ1ä˜)©õhbÝ$ËvÎÎG‰—IôÛ_œ°O(›v5˜#Aë‰æý6À5~T²ÞKM W}u%SŒq…꺫뗣챎–vŽâ`R!9qó'7³{®|†•Y©Ø¯³èŸý»c8ÿôé+ø©··_ú ·ÌʃMÐw³ “Þà€buqÌÌZŠ.vcÃdQ2.`2ùÐH&ÝáN¹@ß*¨°aOqbrK†©îšË©Oká^(„äS¼ôµ=êéŒs؆GÄQãøß=^¿vã÷_¶Jàp]9Šj×àäª Ø;s2+ù>JSanáã¿ öSɬÏ]ù0ÿ¡qɘ"\O*ñ\I/€ ›d‘kÀNÕFÞâƒwiîÌè¾'‡ïg<¤†ß?Ó ‚Òò¼%ÉdÿóTóñ£ö–‘Ù_ ðäa%ˆÎ3c&¦'Éqöœœ›1~Ä=È is–_?7+g`O…(y²ö Þ¯&û•çrÔ #ÿˆíó$8½JäqÇCî©©,{5Ç =R‡ƒ¤ ]Rɧñd̶ªptÈí@›´(&|ìºiD‘?%Ùš;^dI„;;Ð2—7±Ãžßc·«°\gI³%‹UÍ`¦ÊFìÎ+Iü{½¯ß¾?l>rKl^ ‰ú6 ±\ƒ[7qWZcˆqñ!¬Ü©Í\ÔÙó`>}zf}LŽr—TÿéÏ{úIY¦ ~+J®®œÃ6ûú0õU#„AÏ’{™g ­EȽmÌ>, eƧã¡QExåÌ7|ôƒŠzò»ÂÄe­Û´(ž?`ð¢UýÙÇ£›oÀ^5"'ʾsVοhÐ鼑±¦8^tÖ—õ¡mB;¾=Ç-ØVÅ5Y‡Ï:00ÜH~ ¥z»Ç’«wž@—œ8£›ÙwbSEj¿O”_YßìEfèð¾òX´,þ¹› 7žÔÀxÏ3lÐU•dÌ{yS–b¤ÏSž|Ž4W/ÒQlï'°¯üçÿe‚¿éþ†dìÙUGÇGËî©,† *¼O¡=Æ÷8É{ÉÌ‚èï» IQ–ÄûR.Ò3cXŠäZÜ©7‡*bȰ„]RfCO4èäµ:DÓ¾…›âƈá6¶²ˆ› NlÛ M^sáL4ñwküÒí_{uÙÁ y˜k=™u$!ˆ¨|ƾ±TQi4qšX‹¦Ç“ñ›ô°6'#ÜÂH\^vsÇ•“%È`¼ {9¹Œ7|ø7ÍO5ú„ m o¬­€ã} zDý =è½§5ßæmŒèÂÿ>÷î~C!}ÿ‚öÏ3—Þ¡|0a«8Õgž`åý˜öôÖì˜fŒ=ÜùÒï@à³7~™çÂð›+ÆÞ©Å BƒôPËIÜ0†Ã;b²Ôùš(ÿ³¹ÂÚjÈˆÜ âFñ÷œ?{Î2oßÇx_2ö–}ÅÛ¡bäŽü²¾ÖwÕ5AîFgÂ…O…fñ}`0šÜWZÁµ-Mr r­ƒÈí2ÌÚ=V^oáÖ…«2ÇÀzwW+”Z@D pÞwFUúøž Tú9—˜É8'@Ÿ=žµ.:/¥½nÄ>d ›ñÝŠ­¬>ÅÕvhàÕÇãX’øâúç®ÙF<÷ü)U>6Œþ-žæY‘ís€ÓI…½Y#œÃr/G“I³ ¯yî›5Õd倈þ4Ò]UßÒf`CüFöÅ;OÜÁT +:¿Ü”Óȇ› ôÊõ|þáödH1KƒFÜã(IþXœC9¼ËEh³]#2ãµ61+7"¼ûª”(šN< ‚ÆÜ6#Vö54qÚwˆ²&&÷¥ÐR».üðfkìØøé…Äzüo¸¾¦C΃5(2^£6;}y¹ÍwçοÉ{þóø»ÊzàÐÓ ¦jPŽ}‡ÈÞ­§ÿJ¸÷b7I0wQ tÙÍA~A9û¹á»Ùš@ž—Ø9'wòüå jWD°êìT²}ûº'OŠL|t‡ÓíŠ'M6–¶'¬®3 Ò6’O5îì¿zæ@ÍF& Иi M}ÜnÙ h½-¾|7ñç]èÔ¹%pºŒOÌfJ²0Ornü ˆÕ{HŽÛ}G¿Oå8ÇÅÇJ7â‚kÝÌè²8tfƱœ;®L[A²c:kË* Bnó˜ø°;S¹„RÏôáno< SJƒæýH³‹¶Àr6f$߇µÙZ쓞]5=…:]úÿ棶€Ð¶0þ·Ê÷X±ˆØïàj¯WsÛwp_R³È¶l Æ&eÒó–¶lų(löʇ,I36o©Ùw1d|_sÂßðΆRÑÇ?A¡¸Uν§ÙT`‡)=[Ä¡ÜCŽ—³‘_BÇpÆÅÊ4¸VîBúmgðeýËøïmFC¦¤8.•÷"G¶L"aÏu (ÛÿéÜDü´Õ†Ù߈FmqkrþâN2îÛÎ^µÝÑð«äÛÕad‹½–ÆÕZøeún¸¨›~jƒGû*è¶–ç´öµ*ûùø+g:å;·\è4B´'éŸ]ûïþû`¾šeå #“M 3:œ‰m©&[µó5/ÝÌÆL™MšbŒ˜°¢v—ª±TÇಂôȹ<¼ÕšŠm’d©gÇqi0Yq+mÓmGûòY१Ç4¹4Nµûçÿi_¯p³ŒOrv†K™j  ™²¸‚*Í y hÑ<öáB,·pt— <ØöŽè£¹ˆäÈJÃûüB\%N47ÞÃæ¯µõ7M&ïhïߘÔ^ŠºF ðtw:­ßsŒÎz§¶ñ![€Ä2D÷p–´â8ÎWÁ={ÛËæW€É‡ñàûSf_ A‘¡kN#óAÄfkLîïânOÔ€é¸íŸý·“fÐ<­ zÛp<ý¢>‚´Ò7Ñ¥p¿Â…,Õ!Ÿ¶`»¯')*žÊù\粚íØPm 7Ð÷©ó‹ÌÅuŠ+Ù¡ ŸèÅ]ªÔmëPš¨Ê6€8uïF­Á ëhåÝ–®¦ŸV®dO• xÍábü#j£`0pYn² §&»³ü#bpRz3|Õ'fì¦W'üSެõüGZ÷ñ!ýÊBÑÏË>~‚¯7—ã“ï|¸¼ó3žÕý ¾ëZqgD:ïо]ÿ¨ïëéýÿoï½KC‹†Œ Q½ÎóNe„"”U„¨¬ì-íM{’"‘$Šêu?ïˆÊ¦ˆ"2Bv(¿Þ¿ëúöùãu]]u¿Î9ó\Ç}žçôȾç¹küã[õŸÇU“²x—–BÙI&dÖ‡PµÜ5‚NÖäÿ?þñµ vľE‘X¾U“þà†¾çœüïáû9òdÅD »§®%‡?­ÄB«À9¦Ñ9ßÁìe_®%G¾^Æ«UY¬=3ݽö`’J2IžˆÄd.™¹ýÔ¸D³ugÈ1yo¦Ä¼pïö;`¤bDÞ D’…‹ÀE«?LHEšýw ‹šø_çš“ |†¯¦aþn'Ô³‰d!qÙäâ©C¤¸y9—ñŒj?©fÜœ $»%‘Íë>ÅElN[m›zž£ã“™ø¤Bx——‹Á*.†ôk¾á¹´W`CD7_÷›àÈþÇ[\ gÒfÒ‰Ã54tóB~Pt;}¾íט¯Ë]öâ•É,t[?L VEéèƒDuÀóòüÒº`Î\còÑs&ãŒÇ ]^D{\±|¸—¦>lÇF±r¬Ý]ˆ+ÊfqŸ-Šàj‰7$ ã¾ú0xë/Ç‚-à¸ä éà”^~}ÿ q‚/g³+~?a¶í!r§Ë˜5Oº iUj(±÷{5H¹ñßiZÄY¨¹µnÕ!|nT#5ßSÈ®)çh¶´ ;BŠli^Cº&ÍÂ=’¸¤y#ÆÚ}ÒÜjù*þ"Þ>xÐ++uŽØÿ|¨+hWDóbÁ«W=ö^'ÑýÂTì[ÿ­OÐWSoá¥àÞ¼ŸM~–Æ={©ÉV'ø¹HœSÌ)!?Xöâ1.7›„ªWG³sȳ>Þ¸ÄçF=õ‚žy+y¾\"Õ÷øÃÝwlÂmE£`O‚ 6g³uãÂß×f¤òÜS|j;ŽyÎ[\¤JUép‡%ÖÀ¬©§ñL™0'™ôÊ\g‘×¶¶ØÝ°ˆ«¹v #T»pÑP"+*¦¯*MÙÃ?ýtiÁIÛ!E/;ÎàEh‹’½ïúíÿ5<Á^qæa›‡-Kõ‰‡Vm³ûßûŸÖJvð«Ÿ^hÍ@í„l¬+)Å_Ÿyä“ù5¹÷Å´aCÔ_ð=:çL!ÌÂ×;²'OBØÛAÐôšÁ‚¤gO;d¹Ø!öKy èÚ¿§_sk¸t@ v ÕUy€?Ósªe:7…YŸd2~Üû ÎÏ$.ãžsE½é¸¨:h,ŸÉæžS‡¶¥Å ðÔƒõÜ2™~*I[Í]ÈÍxÿ¹w>ÅÔ9'üžd@|Âú¹ô©é0ë6ÇÛ¯5ÔÎd2QÞn)Ïzڻ骘‡ ÷ê„rÅøf©?{{[œÛŒFøO”=ï¨ÆWÔ¡´é.7ûÆ\ز[¼¾hõQŠ$¸îð?=ÇÝKÓGAIœš’ ö ÃÙG3ƒí:ÌW'ðš×îãug ÃD… ê˜[|ø°ßýwýD>ÑüúîÄ„áý„@¼JSaÍïSôMì%n0d?ës ;ÿô¸\pœ8ûhð7¨¯Fïbmüñ"‰Œk–牼¢Ímv¨0û6^ììàŠ½tÙ¢[íX§Äò6/ÍÙ8QA Œgn3YÉ_Âu•áœ1 ­;Û¡ìE,s¸ åæ»Ñ5§¡þÿð'ø,Ã5m)L=ë X}6¡æéòpKu1ga» µ•án¾0¹>;¶æ¬gA7Õ¸ƒOŸaθfªþúwÄ©Ý'ñÞU70k›l‡ßíÙ¦Éöà,!ˬKCYб!ÒCðµÅMŒðNE©u*Ü×k*̹8õb´Ñsµ[Ð?‘t‡ph}æ8­+œÅP<„Û^ŸÀƒë1ð”!1”%—¦ëòž9mÆO™ Lü­ÊMùg×êÂ&¦„JEZÌ–<@E‘£¨ «À2î$ §öaŒï–f{ëpÕË%hÖvÁÎí™ÌˆýK×A®ç%Ú–y oÙ-ÄgSŒeÝòùœwðGnh‰,_wÑР¾ËgÁ³*!ì_wë•På‹!%.†UGë cÕ7¼Q%N¨ÌrðÔxØ#BÖ™-ƒçIù\O{ Fÿ]—ÏÏ㋉ÍÃŽGï¨íÑ0€­Îœ½õ8<Ñš Ží®´Â‡œ?Cýn€Ä_uÐôh ÌR³c;NÎ`3ëPi@‹LqÈÁy¥èâ\ 3Ïs~q¸ÿðJR F3«T@Âà?Ç@<§gýšÏ"O-À¿úfÜ“y²ìn½<Šûo‰ÿÖ6cœ}k<r×^vÁŠõ¨}x'¾—#«×èÃàÍ d[†{kôã¯\àLRï™FL÷jT7&£Ø´Ûh|x::GHƒDk4¨êȲc_)ÍYÕÀí_ö M6°KªWðñ;5ܾC’óLzjð6=z@“|˜r–ÎXiOÿ[l Óÿƒ“‰~\Æ]H®ÅU—»¸ºf\ºeM¹û¾Uß¡¯ŠÎÁÛwp%­[û{e6Kp/ÀíﳩÎûéìëš™Ã\ׯ–½€€ysÉkµ>¸¿1$=·â:% ’vªK ŒØƒÇf,•ÑÇ/³ÿQËW ¸}:W } ZÏ™3‡¦øaÀ ·ˆ…³UõIY±°<8oí]õ~ãàå«nâk*ð6 ²ë÷÷p§“†ëç¤ßÔK\ƒÝð«Â•&ïñÌÆˆ‚“­¥qãGSNŠw Bc|©ª'Gä×s ozÛ§#0dÓÅ)hIÐ?ö|*ž( ³%0eèt)i²MO…™tÌ:ω¢ãr´U«Ò[]û¤~ˆ…¹=A6Ýt!Õ{(BÛ¾4Cøþ£pöF$¯»ZN?W"÷Œ*ÑîØÔ“}A­òçŒð?ãN'4áEÂÖ÷þ4)W ¯C¯ ôÜWþ·1X¤GM"Ósq•ã (¸ÿÎNÆœŒõö¼\h8†[À`Ô,L ’"ÆÉËØg)cÞåŽ?¨„&¥€ü»?8‡ ²DäÇYxw '~ÿ™Ç&±ýC'a¼sÊÐÍ u¹×}Ö YÝü°œ³4¤¢ O/¹L»|Óy“óK¹ãŸfÃóº7·é06kŠ€/4ÁÎ ²¤qýê´¬Ã2×Ñ€§·Aß!79ÃÀßçàÓ¹|˜€ÎÜGž~SlëUæ~¢4Ò'z÷3Zê·EÇÂ{I]v÷ÁT¬ž<—6Zs>;Âáá§#,w×F’¼ZƒðÀî[> âj'P§É”ýÖŠ´+Ëj„÷ñ£ØÑG:LÛì®e¯o€iyE{Y’ôWá«_ O+¡%Å£y[Ë’éÍcöÃ5ì2{ÐÿñBwrå<¶]FïÕܶ}6ØìQÀ:­ àÕW[Fve²‰85ãLÊT†ý»%Ñèf6 œªBâ÷’ Ç Å]hÜËÉ~XËÒ¥ô¾½©Ùc{÷w´Ríì]pxÑX¸eìÿ{„/ãLfçSKóø`V:þ¹Pƒ®7Z†íc jmJL¢×~ýÞEbÎÖ¯ü[>ÛðÓ·à!áÁ3_t¶Œ‚«—ŒþãÔ9å¢1L  mû=˜¼í:¿Bû9Ç¿« GÞËÉa:OŒIà›Í¨r¾#L„ˆ³[)¾^>ób¹“AËÀÎ-ù.ǸëM_áŠË$òcÿ°îmiÇZ…6Ίwí·› Kó”ÌAðT>~÷T„±‹gâ?çMÜoœ Ó»›8y«Pdþ˜-®+¿vÐò1¼A”–¾‡y?~ðÿëÅþ?üks.×/™^OoªØAÆØJ˜Ýx‡³ôö:ßnËåŠu¸Í*zTeÙÖî»üújJ§ž<>¬ ÖãYÿJZî áM”j>·Ä¢=2lÉ®ºh0X+ð¡ÒÚ˜ýl.Â/Ù ðí&¢¶õ4Ä®,ħ²…xv9/lÓÃÜY_éýõm´/A{ƒ‡86LÏíNÓ¤¿=RA;JÍzcùêùt×D±úÃrÊíQóYկݠÆcgs¹yx9fnýÈûY‡ëÜù+~Ôó~ÚA{Â)n®;ÎEdq©Š7±WB~ÿ–a.è´”W ¼™›òê}îoÀÎGhÕÏ]ÁN΃SI/޽A¿åTß ü×÷Ì%¾çôÂR`þ?i˜a¥AÚ6Ô¢Õ-$êôQ¤÷*1Õ})¸» .i ÀêÍñW6îv%÷vN`[^·â÷ëÿè·¡{°ßO‘Ø¿¶…û£’QåUÔò9§ZXg< ¯›=‡«¶2Äü¬w¦ÎÎÅŸ„·÷žñ_øŽb׳oѼ§¨ðCNÈþ ê±—ío©€‹8À¹Ü$ûp ¿dp9G™‘R¯üEK]qf|>Ïq¬ ïÛ@КÀ¥ßx„ÛçZ½rWŒp+„]ç¢àø[6>c4ΈN¢½u#ö—ünßñ&†é¦óG×;a‚– ê\Â+môxG¿ÚV ·MÀ{^H÷_>‰3¯ÝE“ …VœÑ£ïÑåz! z&Õ–à¾QÓaùæù`XšÁR—‡c´\N–›În}ÌÇí±œÿE`º³eX ϘזRß eˆÉn\¢gÕ¡M–_ݦ0W÷ÜC C¡¸B†ûoÝ÷Á©#þ¿2&‚gÞÔÆ ð·`Z6OQzÕ[ºurX}3gΙQL|q47{Ê~öv”•ÓWAò½›÷âòO’¤ÝåU>}ª7zS¡=Õ «’+ŸO­ Îà~åûäñó½ì¹Ýf†Ïšcn^2¨??ž|9xÈž€hƒ(XÂEúZ$%AƒVsdQ¦àÒIT?â(^™a³õ³@Gá.äŠ)€$S/_áûJTCÁä± –³Üž)†×ŒÖ1ÅWÜŠç™›¬2ËÝoÀ] äƒFùcNÿY±í‡ îId»š Ù© @tÓvŽÔ¿õxþç°Zªói Ûbl‡OWÙãJEI'–N%ú>Bb5ŸXÌ‚sªÁÇm/•–Å+þyÜÚõ1<…Á"žŸ«"™ ZF?F ²Þw‚„XÀÑ×cHô* Òe0—Í®®â·$ ±ü„gõû-ø7¼×u¾ª•«Öq›”òÑlÜ0N\üó®ÂÂ|ZÓ£Jø¹7é‘…­þ` ]ùìî ¹¶ÒUxÑ·™^U_ÀÅþ…çj ah‡%a«ä¡}ñepÿ#„q3IÉ–hž~EßÇ)oçò§-¶c:¡¬VìÏ&Îß“dÖ`©m4è]RÄ£®%T×à1§%—Žò/n¡\œ skºT\h7Œ4dkz%aÊÆâÈÛëe‰÷˜’ÖýæØ3]žÃÚI/ðÕ]ÈP“çêÚõÈ‹h æñüoiV dY'‘æÑ‰8Ô°~Ìv¡%9ãpÆæ&t\Ü »pæQcòPZ„¤{Q¹ô4ÌðÝŠ¬h,™úT—|˜ö’×îpÜ~ÔŽbãÑÓ*Ï^3l:Ë«ÚQß“àxñw"=¤öã½Zg³b[¿J³Ç3¤XFüDÖsý1„m¿‚;—ð¹]ËFâÓZþÞa-×@_høâYLNžÙD ¡øCm"i2_?y ǜù£[ð $ÀÌéQhY<’w‡¾["»§$Êà‹¤ùƱRÞ!vu:(èú@S© K‚«å‰`&"Å`ÙìË©ÅPËüúֳп“ýñ¸Õ$s#ÄfÜÁü¼Þ1©y8äïŽ Of¹_ÅËÁ€·Ïм²¼Ä?6æéŒfOw…¹óÙöÛ6L_zé ŸE¶^Ôg}!ñÄ;ɳ©1™]ªÊ:ËëyÅxAɧ×4²×Åâ#úoþÊxn§l1“”Áþe”féaèÁnúÙ,rO¬w¿_kwÏ!KÄ4I´h$jaÉùxäB8$=„ã“áô|è[°žŒ®2²·s…Îü{OV}B"wîóR|f³Yºv4ë¨ú“|“qÙ}QsÙ˜Mû%ßÃÞ«1ønôn̼£Ü­ŽK8eb5V3@™;¡`™—„¡RÑ6ð/>ú»{×R!‰nºÜ·£·&aYlLŠ€+i‘¤}×j|2z* Þ¹«ÙA òÅ,ø¥Û ð0gxgç>¥‡Ó¬mÁÿóðK¾Á¯iwl9x<™üåµâ®Åü“-Á+vú_P&Eü Îa÷.u5¦+7¹×$±w>ŽNVažŸzaïê^®cã:nÙ‡ÓHæ òÛPÝ>\B‹ÕÐ}p¬’?…ù=g@(«’lËæ@Î[kN­ÄFퟠ56ü© \í”!7–ŒæM½HÔÝ¥äˆS^Ý÷Öå€V« ùú(ÆGÔÂV] ¼¤˜Šú>§058íÿ÷:ëý"Û3PÂD"ï's5kâÇßFÌÏÑ –VÂÞî¼ëˆ¶­?ð[Çä‘矑-‰öëŽQú’.ÄqU›À<ì&ªTýûb)¦—[ˆÝ py ƒ¦ßü‚+LÛàgñöC>«þ5„c«¨^Ÿ)û>ä‚:{ti³† ¾ÃAÌhKÖß}4W “yÆÁäã· žBFs»æŽ›ßÿ¢S×ÞrŠ”YâM¦µ¥€jδÁ·£‰ô–2³ã„÷ùðß5nPOg¶`„œ5D/ËÃç»È›~‘ ‰¿Š–’Kؾ?!xíåØ+Z‹/?…sMâ[pjø>ô¾0T?vX+g~²Ñÿ~ªî 7¸ &‚1ÐÛ‘8yÅ4•Ñ ¿/°ýV£žÏ&%â¤ýü>©~‡úÞ쉌==[ DöØíâÿþ¥&ÉáôCÛÄ«”Ó˜ZÛ2Sî³_“¢q–Ïk\k3®¡c8÷Ú=.‹÷š$GZŠÍ¸jM”‹ÝÀ*Á•ÛñɈ<®>H¸ñÒ,±Çš}Oš‰mÒlÏ¿þ²/ñTzâMzýR7æ%lb<’àÔ”XxsôÖïl¤ÃèÈãßñºÊo žÃøèñ”ßuqk;ÑDú|ã¤m"ÑaN:¶ê0ŸÂùp`ë…‘ü—ïhÊï\yŽÚÒvðû÷4Lò¨Öµ‰lí=!øê²•lÐf¬›ó9{«Á ¦y’^<ó }¨Ô]â0/äB⸳ǶFXì¼”f¹šsM=ŸPüñp]Œ’#¦+“ÕžÂj&n= ÚƒÓ¥Y—‘çyn ¹{çÌdª>GÀåÒeœû­zêÎClÌ ¾ß?“ÍÜbCV˜ ›<€y!§•™LFÔ~¿ˆ+ ýñÑq’Ƥ}P¿³®D3ãÜÎI5/M¾OÇoûìÜ7œ¿œ#Îúi‰í‡%ñ…yvk'”í݃ÎÁ¿¹¯ñF˜.­h¿â» N¾lVñ¥X¹b÷BR˜+$ñDœr»Þº‚}<£‡×œæ‹ “Õ<ìÜ»yNUg .9aÌb'JåcáÓš#0ÞÚˆ F’£­º¤ib/·ýÄg¸<õWá!J®×fÂeu’¸GþÝÏð–e ã;þêc9º½kM&°¯åä¥Í.pÐm‚·±ÃçôFòÿ5ß:N¥ú:÷ð)¡GV¤cgb<¾ud)›ç‘ï݃ü:™Õet¢goÞ?Œ‹'¿ÄyÚzÐ2­”våuÒ.å¹(ÛžM5ºÆ’ƒïÅÙÒÓj¸){ÇtgvS™÷»Pz5žÄ_]û »K’:mŸ ý}`†Û<µÕ ,~ƃˆ «Vý±ÙÛ`U¢;ꩵƒ×/!®©ÛŒeÌù"‹^âµÀÿú¬ÁÞãD>) „»¸Äá9ã[§°lÚ,‚»CG¸£Y"¸ÕI±þ"(WõeN†ížì® ñ°ýŒð?• WQó_4¦¬ÞË:ßBÜ›Ë`óÎçó·lòƒ=éO¨¥‘³äI¿žJ,Ž(°=§3Ðy¥ñ‘x÷¥ŸÀʉ¬¹{¶¸E⊇+ëÏ-¤‡—uá»9j ¶6¢DÚ:üz¹ n¯àša1;w#Ÿ–'Š‚tÜ=\tÒ‹D —Jøõå xž;„W[AbÞEîÜ-¤;ïb¥ÇN'=…} 3æm(—%&)Aœö,ïÙE<¾^N¾[:ÓóáÄÎ’¬ãŒÁZ·dØõw¿è. î~c9&†Ø±ák£cS÷á§£#ù¯¾ù7=)–sfYhöÁ€í˜ù œŠåÙ…j \µ¨î]yBõ§a¥Öâ¥Û—M_ƒélÚÕDø¶Áž òô²ƒygÐ Ô“óœbËVZ÷`çÏ0N‘ýøÔ.Î!û·‚pûL˜cÔ5bÄèV4g=Gƒõf6Ñ=E1¼Šuw.b÷­ö‚€ö(›€^_§3™ôÝì™|ò}Qçn$ž­¨ÁùU·°×î+QîÇÒŠ³XÝ|Öy‰b^Š \Î ±Ý&P…Îq±âz j·ÚuØ£ÅFÄE/’ö¹÷Ò×K^Øÿ¿^c™§ðè]:<*&D-@ŸÈÌ'«?=c‘Ûåñ›z*½pcz*el©¸›Z6¬Ô²Ä²k†• ”¿‚93Ï“ •$°éI£Ío®Qbt !+'ÈÝ›Ê*P5ÚÉži_E×;YWz£‰áÆlXÙÐ U6×8‡Be&²¡ t4M±ôy,ñ(ŽÄŽÎ4ïµ [kë@éÈÁ¼ó°ü™Û½¾”‹~ÀÓ1NßˈĘÖá¨Ù…Dò§³”çL¥ú^Çéú+µ¸*` þ}õºžù‘”ítœ‚y¨ÑBÿž‰Þ‚ îoY(¦šœ‚¥ ÜcåëP˜q"nÊÁº›q°Ó+²?pñ«¤ˆ{ãCû·&hÛŒsÒÅðÒ°7?; ‚‰çQùëy*î–J÷Õ`ŠC^qGœüjaÝý¹Ü‡'Æ©jɔ־€žã²DýÔîZƒ:¸ʲ½ùƒt๠“¼ T´¢–ΞÛI7ëÒY†¤¤³ZÈ_ ŠlƽFh`;GÇÕœÜë|íñÑnéN²@Žõ^Šo>Xk*nŸ¼ómåÔë'A°S.³|¢Ì¤ö,ášeà 1ªšÂGôoÖS?G½*ü µNŒeF%⋪û´-ÔaOߟÁôåe(a;†™÷F^ívpøû„þõ꺥þÆ>­²ûDÔÜ^ V×ÿb¥À?®9Nlì¦×ÌãqýØP^ši<ÚàNdEÂöb7Î3ó,Ý1_€›])Ë/[ÞŒÁ?x£Ê›ø3»7¯ý'¼‘¯…e§ñÐ\ GàâMÞmE#~}ž1ñ¶m59`{ô/wce t›[5éB¹¾Eð©„ž›ÇÆÁVÏEÔ9‡Å›¢?£ßœ>Þûw#þßÃNsS~Mÿõ)sZò×›*ÂJOÌ KïÛp"¬ôEwbAž >»ÎÛ…PdÌ ùIÙĽ"ö,¯Ö£ãó °äïQX=Å=Þ 0­n<ü­;öɱö¤Ãô†‹:‹Ö«Âàkh­¹…v×/nï€^×QMò5~ÂòÇs0I_–¨|AÏ ‘ýLGS<êëlˆ;K²¶ÑìÈÜïë8E]Ü6ë6fßýF…Gy`Ûà-7%Ç¿*³ò²PÞ˜Ÿ®T«ñ"÷mž<»Ñû¯9ÒÀ¨jþý~œ¦À®×Y‚žM qî8A¦êy2€-p|º"‘3Seªûºpc»³¯U!/.:Yì\ÑÅýíðeƒc†µÅÄ ²é×Z2Ž'Bö=YÊ}ð—d­î/ ·°‰ÜlÔ¸®F†ìè•BÐ]•æüÈ„‹i-\ò£/0A¬ƒµöÌ'ò¯}™n\ ³Ÿ.Ø}E‰’ë£å‘ä»§#‹Ù5–¹3⻎üüVX28š81½ö *™|·¨@nã"¶Ot3œ2‡<i×9ÿ–)Lf¨ ®èµ“yIJñ=èÿO7@ž¾ïÿ¿a%Èm‰È«àÄíÏ 8P²œôl½8C?Ž5#[Íç‘Ýœ(Znº…E½tCJ;Ö´m&~ R蛂øn,`õ?oëÍtÎæV&”FÌG5ÐZ=›Äã(Ë—ÜþDxß±œ¬}hLžÒa9{Μdz“<\2]OÎ)æîu6ã䪴8T•nÂr•õd—3‹'‡ÞŽ8ôî*>óL ¶žW8ò°ËDÆ©0/Ü3ÞÆ”Ó]:lÊ<|´2+»£èî‡Ä©L g|©·~3ô½Ý >½I(thhÄþ×:r¢7ë ÝÌ™TÀ6?y£‰ÕÞæaÛ £>oîá”*öÁ8vצCÂÞüAQ’Qø»>ü‰ÁC:øp«*Ê?¸L_­—'7½×qkWá/”Ó–Õº—qqI#ÖÞÙG6OŠ`›÷xAý„ÃŒ/ÿRÿiÙù…œŸr(3¿Ò‡< û}ÞÙ‚»í@gÉ6î×ßOœôÒ=0÷H#´]š…^{@Î/ÎM ’ý×§-»ÅKµyôÇì,1Øgù÷m—‚ómgÁCû ,7Àýãns"zôIª& ½nÉÿÍói½ÒïXl“ºK– ¿y‚°{¾µ=Ÿ uâĹiü¾Y…vžî³©Ðß·¸|кÏM"³ø3Þ‹Ö(ÁŠ]•t­” Ú¥*’… Sɇg@ívž–9a¢8µÐÌ^xkšžgþ[ áJ ºäúsúo0m£;èÐÓð\˜Ï›ª{q˜OeRw õI7ïï¢h„?œÇ«ÉlÏOxü^1تˆ‘½íÖ`ü®´Ÿ®G³™oÐrÏý§§jÆ@»Ëú¨†?ë1ÁêNÔÑÅv¦q!?­Qùá9Hꢴ@¼lÄþš¦ œâõ3¨w|3}éŒo†ãsÂC3Ì9Á|ã’AWV˜ ^ŒÀÚ) ¿î‰ýÖÈ0\%ú†»xg˜/øD“É•‰°äñb\Û 2á†Ä:ëTù·?s}'ɬe8ª³‡ûàœM:Ö,$c¥VùwÊìj_.¹ñs& Îd;Ö†À¢×Q°¶á”1Pã\Ó¢ûîo!焜0ç² /™EÉå,øï9|©ŸÎ¼ù3òHß—F\ÐeNê…'°Ç;p•e;lt"Jœ<=Ê¢Ì̈ñ./v V±É•rHýlGö?¦ž Å÷“ñÜ}#r˜·›j|¦*ã…YÿÁr>è´7ÈÀÚò<¢„]þ=žuØœ†<Õ~ntÈRFœ§È܆'ÿxÓ·Tá±érì¹ì:¢^± ßy×à2O5>]ò"+^„`¯l”ó í[ׂa³9¹ý7ÞíÝJÖ}yÅù~`X*ãl¯ÿÅð÷ÿ Æ8ÚùØO& 9Ùd<‹7ð(D‡x”¶‘\æ}¦ŒŸê±ÎÇELÎZ”ÌÊXEƦéà/™…¬«lWïn¼{Ÿ!Û¹ªþ}àt¢pнÅNà¨ùÛFò_©DŸz¢ÛñhŒ›A{jaÂ[â¾NŒV˜¬‡bŹpûÍGú–F½±Æ¨mó¿þ6/Áð6ŸŠúa˜Œ3_• ;ªÄÈïVŒgKô·y±PP÷:Œ‰.·pñ¥¼ñÙ8_3˜ºêã6%}²¡Aô»œ‡Jó4»¡‹^7“‚æžh¶ˆöA1bãhí_»àÊ2kwU§5n±ëÀC4ƒ;Ã9ö¾ž¥FÆ~©CáÉK@Ì>q˜ë¯Äß3l°bÁUŒ>pKRô¹¿æE;´ n%ƒ±Å?ªrî†)‹à“€IloèXæ/HN|–a[‚¸;Y=°»u·Qª0XÆ— Ndg¯g޳#qc–éþQë#8§î>ÞñÇü ¬`TËx †Æ*tÐD‘ôŸÆF[¼)éŽ[¶öÃß‹·8ëƒ6´cŠ*oNâŸYÂò´Ñq}fà@flPäiÿn¡‡WkG¤Ô©ûa±àp½' W&*Ðó¼1p'KŠh.¯„¿Ž†LɧÂ?Æñ"ì;íM†£W0ÄøOQy†0xÈþƒFÍOüä¼FÙ¹’?{F*ÊÞúCÍÓsÁxÚAª±a¤Ý±g7Ÿ9£çÍÙdìñß`.W £8‘ûõØ»iêìûİ”uyèéœfˆ*Ó`ewÈhïýw CïÕâÝUJìò+\%cÍÄç]à^+fü7Ï™‰Û’YÎýرB?ϳg¢v,l0îdWBØß.þÐ|:-ÊŠíza‰&IõxåÚmð[{¸Ê³\–\U¾˜ÆÝ=…>™ 8± úÓµICå>HU›NBíOá§ã}ÐxGŽ]}¢Oÿ޾3Ôç’Ð߳ɋ(U– „ŽK 9+&‡ÌÔˆÕ_g²*­ãÎÂ쀇ܜ¸±p' ˜ãyÕß ;-€žÅÞÜY88›‹‹ž=ïohôrwæR- âLnœå&¥üAš^zN ¾ƒ²‹¹ÐñÍ)°Ê'9 +J’ 1©”·4W˜ëô«ó‘èp“šVh18ªÌz¹†HLû‰ÿõB§ÄJ°LÁ$RÜï {·ýÃ{ß-ȉ“³¨dòrhÚmOŽvœÁ?»ÉØCR`’3Ì•Á}ÉQí‘Íí”ÄY{»¡2fiÞïAÒ'“šã¦Ðým1O¬›ƒ÷xÓ"ƒ“ ˆ®%ì@§äÿ›«¹sž<óN‚+‡,É´¡J>s¼z¨ ?žón.‹¢«úšá²…3¯!é¿Ó)7>â½Ñü+"ç¿eø‘ ·µÉ–ïæl»Qhõ”R«é½°v°þÊyáˆ5èèÇtÙÆ&seœñ”ùL´@ŠD2<í+Ê}¤QW~.ü<67=еkÊêäÄ+ÌÈ´)×áÒfZ+¿Ä¥ ѧ?…WñaÖ€';– gfòȵ™Pwåb)?–ÒG¿Ábý\€x.ß÷S–Ê¢µüU.Îâ:W¿–=›"C\}K0yHˆÔZ\$c¾;qÇ-‰JÁ!üâ/Íj›S×E«Ù—ê-tìÒópìÛ¶Ù0‚”ß? Ñ.þüõÊ FôŸÔN(Üú /6†“Ê¥¹(­/Ïš¨mŸ6éá­ggò#¸›©~ÕÙzi%47™HâGùOCöŽ*Úèm5ç$Öp«·Ç`YÑX’—^ šÇf’M‡ÆÃÆþ#`nvJ*ŠqÙõX¨Hj…›èuG²gñ1Dtegßý±‰"ì¦Aøg¸Á«/k0Et ¹¤­€²qÛéÊ“—i‹ø%zfüÄÿ߯}®*mÄÿ%eE¨™Ä˜´g¼êàLõÄwÁ«ô8ÜÛ1„_~‡n¥h¶iÎÿú?^«eS÷·!´ç”Cš+Ú*¥‚„r·¯kŒ=½&•÷ñ"ʼnd‡5›sølT$1Jäù=:Ý3 íרïâ\(z×À½|aÀüú:aYÒE|;ÔN3òs€\ë¡`n¾a}Ò²HPüIß<ÅUž=8ÏN˜«ÐCõ uæ±K’èËcÙƒœÓ.(k¡—ª16eŸÊÒøSXò—ñ—·aÁ-÷ÐWy Ë›¢N®o¹€»kQ¬Á”àL¢}ÊŠXÄ~‚È›O¹ñׯÿ×³ã‡æÿ¬V| ˆsó‹à¿ùÓÿ‡¿#û.ÇkÊàîO'$Òé—ùñÜãÛðzµy=¤‚ú%;y®;ømãü!{àüp,|„ë/¿ŽYKÚ¦6Aƒñv©øúô"rüŽ ÎçæÏ0ÙŽBù(Õ$É >)’$ýFÞñK»ñÑÄY\Û™ÕdïíC¸³ýDÝR‰k`›+Dr.N u“`âé^ˆæôrüÓäÊŸåNÁ—á|Yvê*âc3ÐýÍ]÷Ù³ÏF‘©ýØ=f¹o÷˜ó~±žë?ÅÛ±˜Á¶M»H“œ[™×ÃUÉúÖ"˜"ßÏ-Ÿu’óÕƒSAù#öÿ4úçÖ~“·Èz ¼Y}7l“c,©„§63 îîÃ÷¾Jd‡Cnp#VÝ£˜íàdÚ¥aG.@ÎùÅ|k_'r¨þ¢µÚ—ñ,*báæ£ÍЛ”Ë›§ïð^fIáûG¾œÖ~>8MñÈpý|Çç5åÐBóŽIޝ¿æ”Ê^ Å)×wø/+­ðÐóvï9KûÝY¿K}hÝ:Ø*¢Ç„÷;¢Ø°®æ¼Ä†²uøgÒwàÇ_ÁöD¬ò—Å+Μ{Uuýv9#ržÝ ÎÃõé•dÿ—Ä1¾çàJœ}Æ‹n?Éçr‹ò‡y˨¼øû—­&%Ž©´°í—ü5d-¿Ü@tôÞõí³±êX&nœ‘'g¬æ¼å5‘†)¡«š'¬<íÇEÄÝįkf”+ ‘P<96ì^ÿ͆Þ;õû¿K»þòÍÎ`ïAhyz³¦p™…¼Ñ"’ôRÜ/8ÿ<–‘.˜þ};l»å{‹îAç ]¼²Q‡­+ÀfAú¯§§kÛe]Ã{îHy}'Á˜zð/LºÈemÆ5‹÷À¨?ö¨}´ŒOË+¹ yb¬™—‡1»P2I½Ü"¸‰& õœ8É^u«ïoà 7À—sârç&bH 3Fã\òÈýƳd×b|}¨’Ư†ÕóS‡9q9½úG{–%Â[ãiPÑ]ÿv»Ñß2\›ŠË°³Ã—/älZu©ÑîÃ#úßPÒ”WUoú§*Q({ïÈÅ»{ŸÃçOÏëYÍx·°¯-å8vC0:ʦ£‡¬J¨½çÊü!bóMŒØ]ƒ®£5Iñ§0p)pcóO_Â/†Ò¬äv.Ýèò4Þ!'‡õî A)èºçƦ,?El'X±ìyÊÌ÷æQZ/|¯eŠûUÆrS}bÿëoÜQq¸½3 Öö݆’V>7ç÷l° æ×ÊÐ]‰7©áþ;¸hêQîæÛ$|¶`æT–Q³ ¹–s¼‡yHôDvwï*üž¹–‹×Uÿ¯¯†,ñV%AE#ö/>åEÚ 8þÊ:ænäD&ò3K«Ài« Qâ°ó²8«ú ×gÀ”M­áÄÞ_øïA<ú숽ß+°nÝR®¹¼•Î=ªH—¬ Âö/œÖUwX±ª½_ZÙÒƒT¶’~ÈrºQ ì¢RÎìTŸf/8™¡6Ú¢é‹îÒp¥ž »1ú4¶¤oà)*žÁR¿…¨g®ÔãÓ'·qÁ|\"nÊ…eÃèâìT°¯ºQŽ™`¢öDæwBLµ²‚ÚýN`ÝåÀ[õ× ƒ_Ü¡¤“¿ðöŸÌΙÀø&²WWä?Ýkç9áeéœ6kNüŠ!1ðÊâLÍràŒ|2oòÏô€YêékƒŒ|>í®o*Æ^Ôÿ«(ûPmKžÖ”àó¢}óÍ0Û\‰lòS$üö¢¿D¹uîw⊸Jø¢|²¯€³YWZ‹Ý—naiÞžâÄ&m•`Ž[ñj£6wQóÄÁ|Jhí˜Ê»Œ‡¶>òU€æ«‡±Bé íÚð <ð>ùv /òhë­"¡wWã G–kéŒ-±}ô%›¼ï bÝ…Æ×@ºô8–ö•»‰0>÷&>z”=‚ÿ½w)Të>£=-³Ð[­™[©°™dü×k|ùåÜfÜ—!–.‡y¡ïL ¼‰ÃÌøFì¨Ü«²!`L,ÚšÎë >€瑳ºšÎ]|aÆY·qdðM !éL¤]UÝÐ5N˜^\$Ì亢jb0¬4j Q½:Lƒs…ßoýèçžl¬ù“nñ\ï7]4Þ3¥ò ¡û| Åþ>ûy©ñcÝTzé¢sjäÚ¨+»â% å¾N²G›Ù$~wúOˆd£/^ÇÕÏ·ÀÚíËPøÉ$Rírn-ƒ±\ ½.3j¤þ·ßT† ­Ÿ°éÞ+ž,ËÀëKYŸ¡>y9¥”;yk-D<×bŽÔüÙ¹¼>O*‰ûQò‡÷GȳEäÈÑk³ˆÆ¬Sð¸Þ˜çïÀ[-ãØ—íÅ ›xëmæì“‹Àcêd<ÿlq”%q ¿AÁG‰9®ê³»Ø6&ýðÇôÎ^UœÑº¶ †Â^ô€ »EX<ÿ+œ|lÆÆéÛÒ"sâ7¦½ç kvBü>Abxó®ÿtˆ|[)ÁÞí¦t™â (:–‹³®ø¢÷T|i„ónv,yA_‡ÆáíPGöX/‚BÑ"ØÓX?ÿ®Gêø–†P^Ê—¼×wÒœ‰4Æ’s—-°Ÿ_ Aär]5éžÄ–§‚£G¦®U#Y3§bN9¦¿ua©·¶2ÏÆXfš^Œy‰jìíÅh´¯AèöŠ…Ê}ç`Á›–féˆw.°7}+H£©*¶ÈE²=3£ðÉŸ;pÖö2!kf™9Š<ŽˆøoÆ3Þ{õ‚óˆû§4vS‰À,2½AÄMùÁéÏ\CJ;°i»Éœç‡¹+'dáÕYК—Ãx8}ï ìAjªßÖÇðõ§á”`jêÁT\Eû±Fs#õÊ“± !zkGC«ævpß>†œyTÄÅ-±årÝAêÂܨ:|òü'ÿ ÁV’&ãóÄ`æÁ:lÿù ¥òU¸ÍÆX³Ã¦®pyý‡awÖ\PÕp$•Þù×rSéç';p[ß³µÕ ß“T2ö2vim€µ c˜t^#Hœýˆ6ÑhØcn>ãª瓜M¡¶ÖŠøN>än^X[Ž‚Ù×ňÓÃçP>p ^|øÉÉ­ÎÑâºþ[5ý»œ7¼f(ʲýúmÀh!XGo«‰£ÈÀSqt^<¶¸þÏþ§c*aS4¶=~ˆAMÚ\áQü[oÌ=šZOýÜcA~“3OÕº_ºÅ«=Á —VSz¶ r…¤™ç.Ø÷b?zÏoÄÖ¢ Jo– dî®’&Ƴ"1Çâ,<þ*‰úQÁá7J¸_+³YŒåkÔÑ{ ³Ž¹BúoÔ:8ûûP1-®*³-•X¨]7T+wrbFÅhR’Æ{<ñ hÏð§žê>˜¹r/Ó‰c–åàìÝð™Y°j]c¼¯ÆÇZÿY\Ýñ3pD0 ý“ÀŸï¿‹Í,v¬ûoÃñÄ_Zl,ìGð¯Éº‚ö™fâS?y¬4N59Ê]zø +_8áößœÊlŒ<¶¤Ö ¦°4_,>ŽÂälíKš-¬F.FmÐU_¡óõfö¾Z«ªžp:ù°ÿw1iiΊÖòë5ã"Éôû©¸èk&øà?ùlÍjSaJï©Â=4Eá c¸}Qþvo&‚vE a’ÇãºïQµûIÐ’yÎlgÿÍŸÔR¦ZúipÔ9ðC[²HË“xžhr & 3«#ùå“`®'ú;÷êãpMTqÆŸ»Á›Fì^ƒ8ª»G}¿S0vål,èÔ` OU-N†ÊçlÓj#Ö“+Æ6iÂ5Wk eì"2^Ë—¶ÑŠŒñDhPz–«³ßŸUÀÕr÷Ï7>hÅàF{¸1£‰þeäùËdp8ŽÓÎ$qo~éƒÂA!ˆ^ ýr& òF‹7cÂó Ø»¡Ö¶-!åޞл€ÒþäHz·$éÚ£˜þý8F}ޤ4$Ç]‰¢gg≠Œ‹);„­®<ˆ5‹AIíÜ5 VؾÜ~þæÿÍÿÏ‘ßù.ÖÖšÕŠ²!lqTÁ€7«pÇ+'¶`:w0\ ÃË"áýˆÚJɤäbwÑ{¸Ø½—$£Ô/áÖˆdJüoÓQûáÜ~,Κ@¢¦CŸdx°ÊÓ×’/=^¸A) vèþÁÖ]¢äæùûœè«c8ñýEâ÷ò5Žz‡Ž­‚ ~àV[_¡±ç­¹Sÿ|¦zϯÀÑËþTªÐˆ•?ú„µs‰f횸.”ç± ä˜ô¸tÎ „ybmœ„›ÃyÒ./Ö÷ gÓA[ÃÇ~ Eî…ܵwdªÔ¡ÿoôÈåd)ŽT†ã¯uÜ(—×X3ûçoÓŠH’t+ޏ¸Öáj­GÃE—~Iïbÿ”ß SàøbœÀ.÷ afô$vmè%øtk0˨¸¤o!Kîù1…´Ûõú\­Ç®÷yZ,MÅäXíªyÜœM¶LUk Éö·„ШTî~‰`8ì;ƒ÷Îã-ÔÄÈO@3 ‘^;-ÅVž†óÖ‘¶œxœõR ¾“ æ;¿‘ÿE´îš÷ÂÞ«ùáºtrG&°ä}¸Sh ÞÉ fäœ1\_‚y_Qã·åˆÿçñ‡Ø˜¬ú®Ü`Ð.Côü¦Ç+¯âͳ™¨ï~Œg¼¿C* ½ð)wKbÈ><€Â Xúßß¼§{’äÉgP7Z˜ŒÞüÞÕt…ÈÛ](³‚—«£3&:qµqìßÀd8=†&bó&KµC2ŠÂv±$´S&Uœ1›¤•ÎÄpN^´†È.¸ äÕ&8éÿOEJ@ÏP?u˜?œ0×''œæƒÔ¾ Ò˜ÏÀ8¸œ(ͦñްœKâÕ:žžü‚ìõwU˜³S/»;2ã Ã\e·]zÀ=_¡Éýø2}ÿ·M§ÐmÏ,ÄA~])Úfy2¹íRð¦? œÓ=¡SæMý wMÓÀ|zõco]»œÜ¨_@Ru>lÛwƒXÆšxz~ªyR§t™-¸û¥N‘§~¥ÿfµÍXK–®sß'áý_SÙLcEü ’Íî#Ï?ŠR®Aÿꥸsº YôCð¿¹Îœè<š=g•¶ÇòÅ<È©´bÚYû•¾xÁµxGç‘qè9{“Ä&'‚x¹ñ¬RÙ‚ è! zWNÎ…%"ù0AË€u~XÁÞûpo»³¸—ÛÆ³mNaäšÊ6# F|1¾o>Û€æâ§}pûi vº¾áò6Ê“maéôþ^r¢aÜs q׊<¦Q)C¾zz°}j`‘4OÝ(Cî| yƒ‹àoîÌ‚´wüŒ.sËA~ës°^óŒÏÖuã¾e ô¸É{dÈüM#ö÷CÈÒµ—Àç䦔×ð}»þã/oíî£vkswdaëAbA?^îòÃSû±æ{ãÐ=¨ ãe»iœj./às\Y[Tçõ³ôM·'xª¯ÄÍ»ü©v•,’iEù¹ orÃÇ…d‹å¤ÔÌ˜È b?O(âÚy¨Kþ=X‹ûW}Eßk%L•“d›'mam»Aöâ+Ø2[‰˜^yÍ«ú|€õygàë‡_q§u!‰kÅùùb¤.È€v§„Ã7 f›§¹qnÇÚѹGŒŠ ø“%i…PÆKEÓ…üýzÁ˜µiLjýW,†8AΤñtýñ1²äQ‹#™£ªÅ®ŽI£Qv`ÛIŒvU'û?ŽgNø§Õ¸ö.=¢~ª O·C‰²9òs m»Çg^¸Ö" W5ÚÖ¢GTGŸ‚iKŽA@e=w¬9»\CAôJ/Ï8OC¿ÁÚ'`F@ ÎK/þšA½“âmP`«c°ûm¨å"wÛ…„¼ŒB·½"L&`"½èþ›^æ¥ Û2@RÿV˜ÍBÙH ¼uYô6‰²•ůAĪ‚¾ú"Œ¿l…pÃØ«ø¤OçVÕˆÛêÒiéÌ@¼iš8bÿ.«îâ­XȨз_æÖ†QcØÚ2AHzpËFrý— =è:FU\¥¶Ø‚xYhª*b€@ ü×]ü» Ý¥IòK{àÝvÆIm“ˆíÚ`öì6çÅ*0¶¡öÜÙÍ$-Ѝ÷§*jþ—Îá ÔÇ Àï! ¼q¾¤}4HÖÒ©¸÷¼ »éy‘kí¯ÇOÜÝ‚T0Ú¹—ÇOˆ`ÉrjrÏ”e<;€ƒ£îàq% ¹û(=ª:ÿ~ér¼pqXY N9§0¥- =» ¹Ý»y«SÛ;„Ѱ-ê°?óáHÿwêè¹³¸‘˘1†}?VMý;ßò®×ø‘²ŸœÌ%¶jS&:Ê5˜Ÿ„ÍšéD*Ïàâ]ª¤]ÎÓ¼ìˆÊï÷\ÜŽzøx[Žx¹Ãi³ ê‚ËT^nˆ' þ‹iŸäÑ_e*-4rÇö¸*œÚŸmIMŠ ù¤ªOòKñi›?ûä)8¬cTˆfün÷&%b£IÞ]Œ£¥ãžÀ«¼$ïPýÔ#$˜ãQ(¾¥q@ú«O¡p¤(«ÞœH-RÂiCÙf,,9÷¬ò˜óµoTÅ}=`/ùßΡyûÿ±J9‡ö3\x¿Ô‚Á…yØ6ó/dí·…dG}Œ<ÄFO)§ ZnÄüý#Úpâ3“0çHµà© íðñÎc(6¡Ÿ`s¯8ôåøàë?yøŽM`ÕŸ¦pVâGñÍ_ÓŸAU>¸#ì4–úWÔ/î›ß&q·´Ô9;û2šU– kM•™ÏÀvHSMdÈ¥e“Éä-òhi:TÚCáèCàt<Û>Þçï¦`Jü*X•3žÌ½Ý®9<š[!C¤Žã:‹í±°m=¶¼ÿƒ“#‘?e‘;(ö‰v½ú†oVþä?n[…ês·àonáz¼‘Æð#ˆ íÛ"°)<‚•™üÅQ %™²ó™£‰_j Iø½žø¥µ@áxqÞ¨œdNþ@5¼*zI~ûaþ»ì¿ÙÏìzq`á¢#éÉw+`P!“… ôÁõ¡s8m± Ê]ï¥ã­ áè¸pcõ…óªR"msvÃ’ ¨s$ ¸æl\èczÝ3Û~rˆ³ÜtŽ®Ïï͍“&Â×~> 潓àÍ\žÃ:-¯Cî¡Vt#ôÐÍä]ª|¿p "5\ÞÃÎ<±ÃÙ?Fƒ»”¹þîwïÂdöùÿ›h~i'îJûCÝŸÏd ïhsœlã»]­„¢m"úÆS€0tì2J÷^Àâ®\¼o ÿõ, ß«dK/؄ί§áäwë˜I|UúŒÙnF$Ñ3C>¦Á÷?¬ -’.4gYزf†|Ï’¥¾©ìR ù+"Äõ I³læMÌuÃȕԅÄCöÜÎelᦠhWÏfÅ9äèD+¶JHŸÉÔÔàÓàÕ°­å:¬ý”N,ªÊÉÜ‹3™yæ^ζä8½&™—¾»±;üØÆT[²G2“\ÖÍ)YêbÊÉdXWyš)ØõAœé©å\›»±2ììÿÿ¾CŠY’µ•i|»ÇÂ×Gð_‹‡¹´GÇQ]¥ö¹m¤þ…ë±A«r2áA»{wŃwò258ÆGøÓgkåñ¢ç NL߀8cÓ6±)³ƒP6L“xà2Öìп·M$«Lè™»¹¤Ä·B·’7g—1ó•$÷Ü)b—úÛø>†©}\É[ˆºî¨–YŠ£Ç›`’'¡ðz4{·žÑ¡<¶ÿÙ8Nå† Yõ¨€ äê²þg”ÜÂÆnŸE柚C%bDz°&[²sy2¤½÷…ƒ?W•579st`† S†‰ìsã;Þèסp·ÛÍd¤þ:E§øà˜GËV1<½nְƹ‰}3†¸PÑîHx+Þ/ÿ„×C7^&dÌ-VSÂ)N7 ÇÞ+Ý¢rÔ[| ž¬Td¦éäÄà4vü’™\'ÌNíúZoüL–ýL?ÍÜò-lsµ!’9ËÙìüBfõ\`JäÏÉ2ñ™wàOàcœ0Î³Ææ‘/ßg‘Í¥j\«x§·ñ~VCw…'‚TZ%D èŒà´ÌG_¿OGσ³"\©¾ iÝxª.ÌänZƒ²ï…€¥oå¶™±SZ[Q{ðT¤zaºü_¨±ÀzúžÈàäß–DûJÞ붆¹f›qÅÕ6ª)j†jú°úe8Žg’I£ ]>˜ÿÔ¯ kvìÆ‰»º¸M`Wå¯.À¢ò÷¨~ùfm¹ ®‡R åz·F7”XžÑ1tœqyµ|\¥‰ò5,ê8ˆ'ôÒß*Ç9å{"ìƒRÉ)gàô8Ü<Ïu¤íMS𡱙r>ž‹„K^º׷Gê¿Ï†±¬ý†!©«qÁÜg›ÁpÀ' Øâ²à@¸ø 7&]Áp[ÒïÐlÍJ,Ø1™l˜'ÈpXä;þÁøž§°kK<+Šc!K0Wý2îð}ðÜ4;3ìh(ØsÓi®,®Š¹{bÞϳܽþ0ÆòŒyM^“ñpA1SŽ÷§Ñï’¦¥Gᦽ:ëÙjJúnànÇ9“oLŠ˜mÒa—$HÊ’eppŠ ©ú2V.LBºé=¶&6’^97âezù{ë8ùÉ×ÉõR]¤ÎR–<ÁÙïUé’ö0²Í§†ó¸*ÊË9™‹ÊGžH½°B'ËTœú~1ì¬|Àbxçñ©þT”+#µùpÚ¥vs†uè3” Žb£Œê˜ÙÏ¥LåÅOš«»ŒŽ®#ç>ͧ]“^³ î_hÚŽ–à8€žó‡×Z/CCï]àf/Ì~% ÇÔP2þÊ °¸ˆ!…Z¤ä‹6‹Èfsf‚ÏRɫ٠ƒG¯Ù3Í/ìc™ñÔ¾<‹®ƒvý4øýl=Yøp+qù¼˜ül gÃçǦ̷HÆ."fg1wÜоOáòM+Vï¦Dþ®PäA˜ÓtoOé¯/ÿâÊ´ØJöG]a¤ÿÍm» mPÒÇwÙ'P`ËE.Xm-Ž)OåKėLËlª µ–ì»M=]¸yž>Z4ßý18O±BWU¹…º¸ž^-‚S–Íão^x˜– Õ§|ÀN6 ÎmÅKÛ‚ð™^( i¦ž·¯C”e?¨‘b_?ûû ÿºNk¹iss0ò¢+pŲ0ò%ʨ6·pëƒcóE™FŽzëÃ`F35JÁ}“œðôÐ{x?T‰IMÖtÏèhR­gó[ gfL\Ó*y+U7± Ṹ!»Ÿ=*Z6MÄO{GøßBw |ØM®¦£ô»çtcË}6î&¥¯µð•h.ZÐNÔr¢õ GÀd¼$.ÍAûãóÙaIxý†¢„ÉÞ¶ÛÔàÛ.òåó¢ÐûÊ¿„s+ˆÆýhnÒcePÏ›Ä*Ï‘±ïÛÐòªéÉL€±=if£*t}û1JLñUÌ;½‰]×®£Þ߯ôE2oß6øù‘>ÖéìÚKœ*x–5&SŸ¤½ÌY¡¹~Ô5¢Ÿ»&Ô¦`™ìØd¾‚…ÛüÜš¥åØÜ&/60ï5[—Fª78Á;©|T”¯‰ÿ3ÿR‘N[!5 °jÿÉøfb6 „J3•ªÛ`Õ™I[ªŽwëEÃxP”íÂ.ÐÞÎCm¿[œðv8¿JŸý³ÆxéFúû‹"É-킯ÿD9‘9 àò%]Bó“pæø ÐãÞl59äÜG‹ô+xF‹PÒÒŸ<Ø“ “–/'¹+Úa÷·Ãpk›(Ëýž´VçNñÿöçq¡{P“,%k´éƒ7G¸?[¹ª¯o¸òO‘ÿÊ‚œžïXØŽ•õ¡Üb‰ï\Cz(½z´ƒ¾l6†Fe=æ¾QjÒ̤§GøßëÃe¨ÈI\æh}Ó†î§;G¿Áøí Àÿ·/ÿŽá$îƒ2±b¶Lœ_ ¥RxÎÎÄm@˜U¿þ€Þf€öÔO4áð1”H?C‰¸i-¿ßè,øÕôcôR«õs cŽ6X@H°d(òÑÄî"¦®œA¸KšlÕ6CöD÷†üÚ§}÷áÆ†\èØ"È”´&àÝaP~¤»òû·k`<þ•?Î4ˆ‡ùz ˆrÖ!¬!ÍÔê*m8†¹ÑL§ï[nÃ,¾CG=}fi÷m˜ÅLOÁ±̈æPGÚpÄþ;þ΢ÂW“ñ§÷pòùA™Ã™ë¼i‹ÈÅÛÈCû[œ YÈJ ޲ÎUÁðÄd3q?=™-=;´¶ŒøÆ&“75ò¬2ò*~3*縯sø6Ø_®XÉ\b¿ÚkÆÛq7kâ)Áx-óî°þÛ°GÙ‘—ªBîŸOÆßÙHWŸ‚ÿú¢=ôHÝj+Þ¨t+V` ?Ú™gs ®9å΢Äè²âSìÙ×A¿S•ÍÞgI–(-«ñäç³SøÑg7i¿¢Árø™ìÆ™D€yvl§»lå8¸ú=™­~vùß¾OŸ|‡æ¿5#ø7¶Ä@IÍÙú–ûÆÐ©~ ÄýÈþQÉr ù#º %¸ZN`‹1IíDjVæóræF£ÚÒK\‚ÿY°óµ`RîIä»¶ ±ý°ó67BÌ18årßèÉ¥¢È{|Šä•ôÁ‹ TsÑ7ú{Ñq¼å¯A:äz¸}Ro¸I›"H¾e›Ì¦“Då+pðÃN`ú^Öæ°‘Üi<†=‚]`ú\”ÌßnÂv‰ÍAÛ"56Úw*Ë55€;&äíO²Ùw‰ÞwBÇ%èÜwºŽ™`VÓ!ßy=5Yxe']ËL¹$c 2.aêÿ-Ì™`)Ž3÷Œeªæí 1g/<Òà¸^þqÒ³¾ƒ/\ FÔIJ=fSáPK.tWqÛ ‰W)ÑñIä-Žû1öäþ€(uØ«ÃîFŠó+c!^|«š’ï^½‚¿6Êle÷(2A¦vá…¢RzåOý矅{BdYƒ³¨¢¯ˆ o‹9µŸ¸¯~ú:½ÁE‰—±b=Ïöq‘ùÛ#×±G™&äÃoDל記Èf´_ÁßB¬ê1Å!´*à6vÃ@£"h è“S‡¼åŸÐöwœ"„5J#ùß›ˆ³•6ÊøUG:œOÀo§J¼Dtõ+0£ ²7lfMŸLxFj¦Ä_é4èµCñ…<Œ+}–'ù™âY˜’‘ÌÍM$5W›CGPÿÑ´ÿ›-öÂò3/9šö’Wµm&{~9“i«Š³ÿæL¾tTf{35ås p¿Wœ_WŸ×ÁßD¦]Ú†¶fÚlnŸëÌÔ%Íëù]—qÒ»düp4Ú MÉÄò"ÎB¤ä5¢°¶è —èBmý¸T›O?„[=OÀ¬Y˜Øý<†{%äˆC×núãÉ&6): êüÿ÷ÿ_måЊ Ï8_“e|91Š?)\=õ—ß$Og‹Âôh1¥åL÷ÁºU?@p€ÃGk'ðfn·eï¦gÒà¬5졨51Ûóü·Ü¯^\ìƒi“nBÊ;E<á·˜üy{”KÝØL“§.ÄÓ°ÿ¨1g<™¬Ò¬Ã-ëQByË×Úž®› ü³P…µbÑÔÕä@§ŸÜËRæc­¸#Ù`Ä?c~^4u ¢”?¸j“ sïbFñ{¼W!Lö/{ŠEýRxú˜"óUÞK5ÔÈe?Ü?šÝ+&§’Á¶ÿÞ}ýßóÿîwùÏEsµùKÑè«NU|ŽÍ£äÉ›ÊfŠá¨èç y2¾†êÓI×@Z½®MjÃðÎ2 +è† ¿¨³ßaÜS › áö"Cد»ƒ[§ÜËœ§¡h¸0®÷’"A.x°|,¾)n…TÏ:gŸ©p¡·ÕÁËϬg1÷V õv´ú#FÂ¥_‚ív²4ÆZ`pîŒÛ6'+Ar#5–ôꬄ¥ƒoh“—2ëµÊB¥µxKžPÞ ˜we‰‹%¯·qøPJ€Ý?…÷ôBÀL[ƒ¿ÀâóÿG_ÁÇÖïYÏ],~s§Ø²e·ð—xÞ€Ñ÷øûÝy<Íøp©^jÓ¸;GÄÈvë|ìuÕf÷ÍøxþvJ‰ÝÄ Ûaªç*¼l;»¢ŽZÁÆZ5räÓ9­±^èO‚Î1éxYß•ŸO||?%ºNàå[æ,’nÖG—”¡÷Ò(úoLôÊyá·Æ{T&`ýþ÷ºýêR1bý9Ré½ýIܶ;ÓñII:ìjˆù²ñpçŽ|}Cñ­„r± <†4œwÕ/îaOì%ØèïIëŽH3gä•ä» N°u‚ÂäïPl†Ë£Ð‡—2 ¦:+„BYö]O8ü­*šá¶Ê-pgI»] ¢¥=\eÆLبÍ.·Â±e«¡'høü£®ÃïÒ©àÓ7›½©ŸÁîµ/Gñ y4O«‚û—S@›í…¢)ÚT{€Ñ·eàªU‡Òó(ÛØK¿«3W]¬kWcë”,X4o?<®Ÿü ]®äT <û‚kßÙÃüÓ—è—G¾è3NŠUøŠü˜@ƒ\[êØ„´ï{æ ô³à4îQ˜2«ºrˆ“ë³gÒßòNØE—»?¹ÉI>0¿ädØk’ùµx-|!3 ºIïûèÁñMÿu70«G1$_H…•ô£ P¹Vž¹f¸A“òg8´N“³Þn4²ÿ—´‡´Ž"ÎŽ¦F«˜qU9jM^E£Èlýû h’™E9¯¯ë£ÓãQÊä<í»‘D×ü–cñÿÒ'\ ®¥5— ùäc<^G°aÑ|bõTˆÔµ“é—#™§Ì7ö‘eoÖ)×X(þ`Íú*=ÁQÚ€¿Â4”]¸[‡¡ùº4>nµ!¢(ºå45‰Ë…ß‚wàä ÝãAøƒ?–—3QÜ¢©¡Ø?ÿ$Tö¹aZŠ> Û~]Æôë98VyÜ”«…Ö¿éŠc³ ¾\˜ü-‘d_ÄUȸ¤›ÞŒØ΂» g® š63ÑrJ7Š#—©s†;i‰^žõðÆù´I%c†é¨{ÓŽ§ …àŸ ÞOÓ$ï¥àÊãD{Åذ#‹v²ö‡èñÍ—QF¬>þØ€ >Ù™-òÑ ¢ŽUxX[ âjÐë‚a8¥¶Æuxþbœþ·ÅÃáB -éÓ½Å-ëØOÖNpCÅ/KÙš±"¤¸zô©~ÀÂò­dÿ ЧK ­6áåJ#¶ë{?Èïúé>\‡Úy­¢„˜½¦–3ãiÚ ÉRŒ@EÍXØI·ê«¢ï‰¸‘üW¶ÍF½.åøŽVŒ¾Sb åQ°®ÿ1ÒWéÿõ@Û·_Ž·tøôu$^³Ï,œŒÚÇq% rу[1`úaºà©9„ÇoáNÀÏÕ9öµ¥K¹Û"‘®a õbpS¬Ûô(Ç\xÏ•.÷ÃÎ/a\‚i6ø%JÀ„q°ºë.ÖÁ€U™¼7þbš…';Ê•\¼ÅÅì¸ÇÕÜ-ÃÊ ðѲ—îªp$…)VÔ¢Ù“º¯¾I¦3žVÖTRUÚt&˜"»Ü„ØçøxÐ 3bÿ–,†¬‹­œÅ³P<“Ÿ†Ö®ÝÔúi#MU!õ«‡¯m1]·§ §@°I*V·gŽØÿÆͰòm*eIjdƒk-Hùs4¤ «rUHi²0ñîÖÀuµi<7;ÖˆB`äu4¨˜öqßùåGŽÓ!…0/Išd+˜°åz $øT£Ž¡Ï‰áœU_/0v,QïT"•jÏ ªîÜJ~Fžøûª|Ùw—«TÓ¯náܨ“-\6{¾â):¼žz»ÆÁù矡üˆíjzŽæ g±(É7¸ÚR“ï/y—~…þiN°¸3DøròÏ1x®™þÓ'‚œ½nOüv!º‚R1ÿfJ$ÿF!¹L°~‘5bÿ‰[ úå&|ôº„‹Ëù_6öÀ¸]IxwÂA6K(ìX~ˆP·ê{À¨k'1ÿ±|`N¶‡áŒš²N¢ÿÍkžØ-úÇ1ÿz3ö)N$XŠÓd¹àO`mÛ#.}PŸìª‡™Úµü‹â'écÝí0º×ºLÛMç<ýFM²øÜÄÎNî–[-Œ÷KÅwÞ‚ Õg‚Ú“qœD%,§FèÐÿ:ïÅ‹Øbìù²„=8Ÿ]ÆÖr?ÎlqXž ¤²vÇ9!ÿãäÑÊiøÉQž¬ið+éèÛÛßç!Ö„æÙ‹Fðk<‘¤NÙ­Ü‚,}NV~ N¯TY](yÒˆÏàmK¼ì t{³ š}½Âét6Áþ+¹PúE¼WDâößÚdéûˆV[Œ"Á{Àz‡#¹>Æ|—Y‘üÛ·à ×Wn½´³ÖÁ¤1±ÜÙòT0ûÅ­ú¡Äûwn-zHwSu;„/%A'Ò”\ÚùŒÓiìÁõÛ^pnG²AaÖ.ªcÀ¡¹›.¯š4Â×”»ð¤°š3½‘ë „ù'Xœ>ù¸ÇÞ]IÌÞD·®”w­’;Ù«ò3ÇÁ»å·¸ó•HG{¸Z^‚%ËúGüßöÉÞzKe~ü˜Ë3ÜÂŒïÜ£ï’`3OÌvÂg“ üd˜úìÍÝO!Aì·µ5>ü»ìÛQjàE*-g’è¬ý´LH“˜ »'Nùi!(-iAtÂmˆÚ‰+x¸{!™g9œ gÒšœ>4ò¶ 9ʵ8õòr>KËâ‚Z9žƒ-3Y^ Ãçænz±.Áa^qÜadÿƒwy }óT¥cz>òrç,—tØMY¼‘g¯µEÉ÷fÌÿí#*9I‹pý›¡ðÀ\¾}}oh iõkÇæ•ædQ¸.úM/¬­€Õ2ñƲã4wo=¯|ÅÒÓS`?-Ãì%‹Q¡3²#ùpó³5;.ÿ?nÍõnËÙ‹5P@å%(ÿTÍÃû_ !kc Mº?óÅÉá„OÜæàx2Uæ·úr7³:aé…óÈ{D°{ú pÌJáŒýƒ¦Îá-Z…É W8ÞîOôœž(Qy¤G–zF‚Xo ÝÆ¹P0âÿ¹ôê(Îàv„x°¤Á+4ùðô[K¿›ó°ÿO8Û$XÀ®Ú¡†ï}êâwË~mÒ\võ$ªo¬ÃÖ»\¬Ð Š c¸-_ßbnd ]¤§Ë)º×ÁJ/wÜòÕ’»@÷ñ#ü`üŠî|*…ÁSNÂäþe²8ý¤…ì~ê R…ÜᘕªÇ½&2X¾Ož½ñÓF½sÃò£79›‡rçp“âZ~>Gç²áõ°jÿdv6À„9éÞÄIŠLDë ×±%‰³[«NÞÉÎâ.ÍùÈïipæz÷ëB£´·$lj]ø[ßWÎ _ö+Žè§SúÀ»ôûÏ™‘Ñ>“©Ç¼§°tÉ Vc«HÆ•kpÝ*Œ›øÊ=MŸ‚©V¯°ìV."#y–¹EÑÇéÓØ1õa¾T£Æäú‡cïz0ŒUbßWåïu6Øâ-Î:h;þõ¿C|Qb;÷2žÔ_ÈííÁÞÍ ôD”dÆ[âq»v\¢„Gßi’# ›˜…ƒ ž-‹†Øõ‚l–—%ètɲªz±a?”&Ϧp£Ã“§kÓðWÄhxCkÈ'w{‹«¼=™ÍÌÑÌ¥j1÷Øê,Ú‰†‹ÔÐcí8òx))²ˆ‰ÿê¥a°tU'þÞç{𽤌Ydƒ"ŽG¬ÔõX`G26»òá~S žU¡Õ2óØÞQá¸!êqpÐ#o ݰHL´•CÏÜý\Kz-ù&9ž8ìö$»º³GT‰,n©¤sª[p²¡9’øÉv~rAÇ›vYAæÊœ…W£šÀC} ‘¥ÆNF­Ç+ñÆ+M²Ðä ^Ÿú õx÷1ÅÞ‘´x‡À¬áì]„&±{\R¿4ï>Pÿõ?w¾LÂ9» OšwL„õ’3Ù›âWŒ†þ‚ªj†x8ìž%‹Ž²ÖøwÚ³üeK8ˆÎCû Qšðb»ˆÌýË¥|z»“ àÞTOðhàÆ,¡6ç8 /ªúJ‹8^´CžËhŒz}OªÄa÷\ˆ{“ .¸Ô#³™–Ê2ø³«ƒ·ƒ{ƒõ: °—„èÌÆâ$]bk·ü±Ë¦‰·náðyqŠôn8«C'Žƒ[+˜¦Aò}Ê\žÜcì•"çvS¿bipov"{¿¥cû³“?œ»Nq6<K .Îà ­0››XK]:$E‘„T$È¢£N×¢¡Ôg.¤!¿4œ¥žïÒßcGð_L+EƒÊ]\å‹Ñ˜ kô0ʳŸ¾ŒÖd3N¤èr¯Þ¿ç ãø(ivYj ê§¶Bïh ^ÕF竉åæ)Ì.1Œã^Í"Ÿ;uà‚ÌE¸ó¬^?¤ ô;÷ÜùÉáÒGöÿö+õ'ô¸Î1,_u…³þÿQN?‰¹¾!\ê´£~h>¸ò'óîtcx¥¼]{ \¶Èó?¼„|›ÊâúÉAïMúÞø&Y’x.Î]­!ÄÅJeÔ¿¢sr–ƒçWÚ§Þ2½`gü2ü£°d¤þIjÞ±Ÿî}‹Û¿ôv^z†fÛÚÁpÅqô´Ê Oræ­c¸ß|yvßï0öÿòæÙÓ¢Ÿ=¯2I¡|ÔòÄ­~h¨žKãdžQËAXæd¼XÌŠäÀô.qv°C¾Â–ԅ﵄ÛîóŠ:wóîõ…×÷´WƒiWïÄ/ø=ï;6/wÁlÙ0í« +ºô wïuaêq—s+·d’«Èˆ…Ž‹ó¹8{{R&,ÆÞűлe¶çE5!¢úŒ ”$É1¦¬:=óñxƒž~Œ÷âÏAfè4Ä3®YBíî8qþ÷‘ú§Û^JWö*‘PɃà½d ³Q[FÂî…àœŒJ\pýž—uÀ1z^ìQÞ­ÿfBsûTõYX]#êY%Cö¶qâ]ØÏÉRÙ®RÞ®LŠ6 Rí]Ø•ïOw ¸±V-I–²ë$´+nb‰!u¨“.Õ3ã1%›»ôïMã’ôoãÁÎtH[x‡ë_·ŒOŽF™Í lq‰;û` JWàÌI—xIµìÕöѤmN þMmÃö3ÞŒsññg!Œrq…íï¿.«¡eè<±xP®8ÞŽÍ\…sÊ.à’²ºà«íXðIš ¢â|Øåñ˜Ó]ì‡3ÒN¡ÇÖ:ËјKŸÄ6};ÄÛ£¡À¼ZMÑqÆFÚÎM" ¦°½¥…(y³ ¢½lq©Û+|ý’BУ•,ÑJZ¢/¢åøDX¶q 1ñqþrG2kÍÜgD¶–ˆ³œS‘°üv*Äíæ±\­’:ï£Áø§Qˆ6ù°ÛƒáývSÞgaå‘ü׺ð*_½ŒQ'íɬe*pD7X#MzoAÈmš(d†Ë~~¦gš“¶Ú8C§›ºÑµÒ6ÃZu¶G¶¢øþRíѳLÙ„wö¤æ§#õµÞ„E߆¹NcN6«¥r{G±»« ¹»®á\šþbê$A`+3•à΋ó ðÊá7½KVõü/ÌNÑ$75ç³Q&$~š$in”Á­KD åe,xúvmáJ”ípÒ˜?Î'„—Óa]AΔßáfž°&òšêÏUÌeýßàýõ£X˜‹lx[Ä}ù:âÿþ¸úû®Äù8:ï+ÇØÇ¹êÓʬ½>niÆÒ“9/ðÏ„8Ñ B?ûš³ú¦€Ê9<û¹ÎäeË%z…~åF]ÚÉÛ}-Îë“æA\¡bÆ5‡ÀãßEðùí ”Ý¥ÊNë—ð×ÛCÃû_x©î$5Y´0Áý9µi BíGÙÔÈk'ÌW g™!qX¤þ‡Êß3'Á2r"”‚¼c²{ÎYþu%a8çá\icˆØ ÷vWÁùŸ1¼’»RÜë  æavG¿nDÝà(Ô¡¨¨0ìoîð¦4¶9‹ÃÆè$Šœˆiš½èb¸˜y,•gÚ³ì!aÚ öM·•žøÌO­5fkc9C÷!z`Fb¾K³ÀØ_øÐI´ '÷šú\Añ‚-Låt0 “{›¯¦“._QòÜÅ’©.ºç`[ yÍ)¹0Í~<»ãæˆþ ÎÊNøà¤9yöÕSØàÌdtiϵôÿ½ÿtøÀzd¿*LŸ€%Ëò¡¢<ÂE+ý7ðâç6X0ÚœôÕ‰°í)Ð}ÒœmýZ‚>™¨ú!Œ]×JOÍ›ŠÇ×<ƒ‚u%0)l*ÇY‡Áë‚F˜zt[¢&Nï·ð±a@½«#n}rø‘æ4my9ŒõíÄiRaXwƒûìëNä…Ì@Aï4Œ=ÕKå A1 §ýÒÅŽ7àLv'ýªe ¿õ#ð[Ìdª±Ci´MÇo»T ±ðö︀ÒßËððRWÔ’çx[þœÄiÒÀµ‰\ÁƵ‚$bØ?ï5p÷qnk‚BÝÚ‘þ¿ýZÏh¡C 'zïÏ"õ§}†ÝˆÃæýͬ¯©²‰ú ‡ôÏs9½ÅàiaÞ²V¸uO¹¹¢M­K˜jû ةö6F²^Ó,Êq!n‚ÓÅȇdOõ3—¹jƒé°SëMT¦]ó:4d ;;'úLëM6ótðºtŽüý1o¥Â¡˜ qï‘{K­I\ŽTŽ‹m—>Tø¢Iÿ=Èen7¹OªHþÝÓäÄa|“âÃ2o°õ×sy·ßbNÉŠ$nj/ŽŸ[Á‰,ú SĴصâ>(­€=)#õŸ¼H†²·$Ópcæh;”ÆžÒ÷Üvèî4$yR™øóå´V:›ÜŸ†N}ôºxPN Ÿ+áoÇdn|”ëU¯b…“ »;Y…›p Žu›¾„ëÚ›Áè!JJ–¹tɨÍâæžË¬Nì‚âæQ¨’*OžŸ|Œor4Øž?ñläjjù÷Y³ÖÍ¢œèŸ£ñx©ï Zï'z¯¥àù£ÉÄÜ÷&]ÿŒ/&Æw»4HB¨°Ãÿálç–å¡¢ÿؘ%±0ëÍnøCCJ ¶U…m0bûE›P¶¡~؆¦ÙØ™æÆ’+u·ñâUâ§9À_ÇÏ…Ê]GÁ;=nŽIÀà½O¸šå94G«ŠT3 ôûTÆ»Žl²¥bpm¤ ÛwR‰tÿ™O–%h+Ï\Üññ#hÿ­ã—í4d ™’„¦õãY˜qS5Ê„Ê诸.F…¬™!»Ç°?ÇRð{×Õz^G0Ø6 ãAa2o[&÷àçñáÄ´Õ’À·hvîò>nôå$)TŒ_X9’ÿÜ5Àà‹,ÔŒY„y^=xþñhö¬Ì4…)_JŸð\âNœêûUNÓ9"  ¾â%jmK$ëgí'GìX|Ñ, }6?ƒu¯çãŠ;£ÁNâ(6¡-ØÆªÂbr3a8O ó1©E lÐ"wLwáæ•𮩠wÅ7Âç7Þp‹º Ís)RR IîçÊ’2's~·^#ß· ”ºä¢ªÐ#þ:]ùIFLÇ‚ aÌ[usis{ÃI—ûÜña Ó¿ø+wã¯%Ô_o™O§£Îã\•¥ôz$ÿ ­‹Ä§ùu|9ÏÌh°8ÿÑìýÄfüêŠWožÅw9§¸™­§ñs é?n̘·#)BôbÝÈokvÞ¬«6N`§æ]@¿ˆg¤J«ç®jä³Á¥²«æ±w>môªX®³\Èb_ìgòû»ñÄ´ÉÌÛ¼%{”˜ëžF<;wñºë„³&þ@E÷%Xýù:|Ê>ÎÛ>ôn8©3ïu=зmñ°íÛéjÍ®ä§ ³_6šò—,";Çös›äbXÑÎÌ'ÍŠXÞI¶Vx‰Å>DÐië)ïÉŸVƒÚ#û]ջɗ†; `Á4_#•Q'ƒ—¢®²4[¶d4IyÖ‡{z)ìÿèŠO•³AdöìÍÉJ‘ûo䙆ç ræ^Ó¸²ÅWÜ„Ë.Òìäó0,ÒÁdÊP²Z‰„wÂÑ䉤¹<K›Op‡“àwçÙÙ³· Oø>Û}à(sãŃãa]ñEâá®Av뾤ÝÇÉÌEƒÍ”/0û´{Ô:‘\LCî¸ ³v3Ÿ…6ìÛ›éè*”Å•瘚}úD:/ÏŒ;ÄÅÙÀ%ìmu2MÑbié¶²i+<ûv“Ÿ#ÈßåŽöëÚivù]HËßEû®çÃ)ŸtœWìǶ:Aª¥M»oŸ=ÂÝâ~¬­µW'»±©§Îs3¾ç3׫c™Ç†ËÈœ7“õ¡ºÌ`p-z½Z»Ž=‚ªssaæL“‘øó.‡‚ l8ï'Ã&ä.ƒí/ßrçuž€å@,+«ïÂi£5ñ¾Çt¬TÜL3%ŽÓ)=´*Õ‚=J8ƒ êœûd!8~³s"'“­ `žHïnú×K‘hˆPJIi—¢!?ïïãñóùïç~ó¼Ÿ×zžsëܯý[ŽéñÁ¤l”!ûr³óxšÔ,þ5Š¿)€#Óf¢ì­Düh¹EWþ!5 7Ðaíeœ§óM`Üñ]œO¦š„óœ‹ ©Ç¸´ iƒànªq:Vëò!©?{“ËâÎU4äV­îÃMkkàƒøZ°4Ú„öË2pâçpø0yl5ÈUøy°oºA\`É2¸•\Fx¥p¨B <övÁƒp-Zžý‰<Ùr ž\½Šýò½°ätL Nž-¯É‰ÇL¡30«¤“È6+bÔC¨Uµe‹v)BnØp†Ü'¢M›7]{|*½¸ݧÎÍQxqB¤ˆn:9b—-"·¨8·x}öt>Ú0—F'k³Î§1tÙF#š¥a‚ÁÎÉ &Èç¯J#} ™ ž×NR11AÈW›à/ÔUÄ%A&¸Ûr/ÛÜô»þÙQ ÍÕÕF1c~ W{Ù-Ÿ9ÓÒ+¹p"F¶×À¤¹]ø·Â3 Å`AýS<$1™Š_Þ„Çôê@fÌIÈ÷‘dÑ©;(çÛ ÑM¬ÖÇŒ©ç±ÛÇø 6¦m.HárÛa´_¤Ø§™j¬‘ËÉï*øU¡m™²¸]Zì32ïx ËBðôêƒìb­«;.Å^+ÿÂh&ò{n’¦zÍcÁ'*?DyP •ÏÛãèK— N¦Ò»ÈÁëÎìŽR!r3ó°ð—3ªQ¢zoü/ÿop…›‰1¤¨¸/\›É´ ŒXÇ–´½Ú Š0}ʼn¬6W†šsËq±ûv6×¶«v«²<é¼\¼Â4s•ˆL_96«Z³öG”ݦ/wªñ)VÂúöéhrï¨èÉ?Qö73¶ô£Cð;zì¾³’8<³f]¹©¸[¾ ;­ÿ’)§T˜ƒû!-úöó1š+emwñ‡Kvc·vŠ2Gó<[ÏzÍs%ZÖžEy™!bÕŽ¿¯Ã‚Auê } üÜóRI•½ }Ò˜³ne™)çɲf1j˜Ãê#éþ].$܇L_ÒDNªlå)‹A;QGΠ¸ƒD_=…¡’’ðvH^¶íÀgúmDD/ŒÞZÕ‡ˆ%´O*åV€õÍQµçûPÚà¬AâÑŠ-_x„¦ÊP#÷éd-ßœaY>jÜæºVŒÅ†=Û†c™zN¿]ñF/võ&qÇ-r0-%D㓆÷ÛpñùªÇô¬RQæ†:½¶¡ˆ'W¡ÍÑÖ¤&øçàY avmÇGÞ–Í…Ü‚R0U‘[jñÑö2¬)ZnŽ×àƒçXüå#çûh3ž› i þº –ÍÖ±ÿìÌåœ÷c¬v4Á‹ŸúIΦü;î©©6gÞ絩ë,qÚr >çx±sáãØl÷¹ žœDTúòý‘ ú Õiø392ÿÔC\YäŒö-XiP‰3å ËÇámXmïÔW³I×0(½ð ®äÇÂßÅl±h4à.mèWÇwááôè‘Rdõ“êÁ‚/˜Š‘$õø9žFÌE;­°]\ŠÖ´ª³©öx,s3mY2'Že›j/àÎñÜ&Ï—ÆŽÁUrüw<—Zêáãµxú}™ ×üO“þ©Pc÷âFôßí§†˜½v´‡{ÿŸÆ´\)Ìó8þ}wˆ kD]™@r!|.8½ÇížlìØµ´.ÎÒµ£ÀréCTáßDzU‰ð42¦“ɘòS ~Þ ÄOØ=^$¬4KÁtuö¡Ey;7á¿›³ðÎÓçÔR\¡zQ“÷Yt)Yî9›Öj†Àé«pLà'×1õ%Xo3À­>:ôò5ij¼¬¶?#üÃÞö ߉±J%w‰e!uC¦!yT–Œ‹Òz±¾õ7|üœþ ¦´{ìÞ˜õNbÃ5×|êÑ.¼BÇvgv~¦Òÿ+m…¿êê\˜s-:yõB¶’¾|Eé_õTÞ´3¸Äþ8þ¨»ŠTšÑžW ‹é' ¡ë9áÜ;˜9Ó¿'Ï}iîA™ïÆXsz€×ÿÏœÄê jnó•b½j ÂŽ`”ÐÛÌÉ-Ø _“ Ï`0±ëãüö¯¢Þ±3P8Ç^/¬á[”ëáo«Hˆ~\m‹Êáäq}v;û¼Íœû`ׇnÿ—«dÕ99.da 'ž5£9¿Å~îcqp.Ä÷§pé:9¾X@ N.n"[ÞÐYcÿùOP·ð¦ˆ¦AQå‰üi‰Sð·‡(Ý% ÑMx©¶}€U5608ã§#‘ž——ÁÏ¢ðõ醛ûâàSS+êÏOƒs ”hÞÍÕ`Ô=г]'Ä/vGÍE¡/¥¸Ë{­ó×ÀÎ+ôuì(Nbq;$o‹¥¾—ÈÕ³²ÔŠÓOîÈ•ÓÜ:r&>ÜÙç†à͸ylóÓLÔÕ…ƒëÏ¢…ZäÝ|ÉÅ6V€¥s&w ÊkvG}÷^èú‘Eü»&¯†Çï ˆîn{(2þAÎÒo)ÏÕ¸È)åcªMG™eR±nôHý{³|"yï$K¾ÍÀÜ•çpñŸ¹ä>ÿ ۖ£ÔñÔ6ð ÆW´p‚“6açÂdNI[‹>Ýsî,Í#ñíñ·7ÿ…/«ž`È’“˜þï=(\U`3€fÍûÄóJ¸—–B§Í|ìr{Z@X]¹= [œ?ƒÊŽ|2é¯XAMM?Všöbl„/]!¶š/zÁuG дOǃëÑ!O–&µ¬„Ÿoθ6-z·! 2êât>z—‰o÷[¢Ôét\[®½N #ÿV ÿby=]‰ºW`8¡}ùžDgÛmL¼Ð=âÿï«Çò*Bj¹•zo¸Ã ‡G²ñG“Þ¾l†ÇEØ¢m\TpG?ŠÁTKg–|p5ZMdxñê}®uæöНÕ7Pzîfë©Feo™àšÕð±-ú!§ÿ/ùí܆·þñQõƒ&§ж töÜ‚C‹Ö‘Açwœ£\ 5éÂ\G;8vp4¾µÔ§ÿÌpžjjÝúMŠ}ê¹0ë6˜tÖÏ|l€cKq¬¯,:Øõ’³à»óMþ¶/óàÉÝôWÛ X±$Oÿ$W+Ÿ¢dDLÍkà2*UØÍÊùuÜzí5ÌÃÀ«ÝAä_M÷'³žN[:Îo:§Àe€Øby88u3\eÌOsîã?Øôg¯ZÑðÒA–¾;pžIOÁ+s0 px;x¶U˜¶ø À¿³Ât~”ÌÙT>á*(룲֯q”ß:œq!RE@ñtušpa9û¡4 ¯)ÆÂ¢ùX]ô›ïÎ¥?e}ñå´ü¨'ôι™¸qË Þ”Šzzí'/öƒ«•ŸÃýå¿E¾ü ¨³³WûƒNȾâÏÛdÎÊ^?“‡óÌf5-úéw:8¨ýåçzFÁÙ‚ßø*v?p+᪃ ¹þX€g›Ð_öÁøXã,I|aÈî¬ýlk¼näDlµã9‹yn„λ;& Pú¹8}4¾k< 5¢ä§Ûb÷N¤éSYŠÃcÔz¾y4lEƒkšü@e#ü#f¸·£²ÃhïD’×Rë¤ø'Æg²- —ù“?\×O¿È/bD›]¦£é\!&Ux—s+šG S6å#œYßά{‚{%7²s.˜TO@®o1~ß Rz00m=¨êàÅ‹/!™7(c¶k6 ŸD0g¹2”ŒÎå%¼Lƒ„¡exy¸Æ8–oÀ2W”ƒ€¡³âèТh8# µ§"¡ó•+hAÇžpr<ô8«pÁïáÍð8*¶¿qá ²±×PšåÁLÚÔ8 §Bœ··ÌGœî9©Àzê>ñV,“á_Ìc6š6¯æ„Uó0fo._µÎ„ºÏYÎÉo|Þ·o¢)[¼«G üXùugè¼â6ê&áÓrYL|-Ò“I¤ºÝžb K¦Ng¿ÉvÑ>NÓèî|iN¨EÖu=ã°ï%ÏÞé:ÎN•¥WBnØ×Ÿs€1↠$Þž­Ç»c¾CÆö8¨ºÎÝPb3ü£aÆÇw0ýÙ9|Yüˆ?“gw\eèz1Öp!…¯ðû”[Ý/ì„þ 1hs¥}n%qÃkÁ®H°ºr’[ÉÌîñ€‹~¯ÃþòÛÐî^I¾*Á_öf4Ïè$•_zq­“1½<k¦¥dƒUº[]ËÝâÏÂ9‹!K¸QqàA&>ñzÄ-ñ¾@â¬ÉA¿ñìa]9È<ƒc]8+5_¾ëv[p9'D›¥RÀ±F–&œ=ÀñH1v®®ÍÞ†r…w_à ï<ÛðN q~…þ\çÒë¯/ýo¯ –Ìuùô<…Sf.Ä?%Á`àKÃØ}Ú1ÎN]¶‚O*ò`X¾”íúdMü•u@·Œ-ÒzÂà'XDßç&úèÙ›/æv¡Wa8ïN]Èv÷]Ã’5Gé›ïFî˜kÿKü2ˆ7Î|Ã…Ÿm©§Þ5îóåÅ„Á3H GöÞ;22ÿðÖ dpC]üí1þlðúx&µÀ~|9c7H³ ²g0¾( ÷{îfxì#>x¸—n£Nj󨤥,îõŸIýW©“Ô_'àkñDPáÉCÙ-à—'G iþ~Žgø.¤´¾n ë0ÔlSqwVݧ&æKéÛIçAÖê­üù+'q¦®5µ—1w2?ǧÕŠ°«k,kÿߞú´­XäÁ€.÷Æ÷¦¦Ô~þ‚ÿW<û—ÌݘKvOÍååFçbéUi ~W?´î¹§Ç–ÙkÃX—_È3ôÆæÂ$ûŠÈ½ÀHî–S#|ÑUÆ´‚R̸UšŸ^rm² Gzo¡5ŠMáJù[aûåçXo”D -Á…K纥FÓ7zçÉR;îs?’¨•j,£(’„ÖYB@Æ ´‘šNÕfÝ'ú-o¡÷Ïq[ytSIƒA=P™‰K[ɧÝ8×þ1ì¼û®IÑ)–¸Qjn–ŸÇÄžµÃ™ˆlnÏëHp< ñzø®î/w¼mRHKOž‡W¯ÿé±õxÍoýkÁ…¯`æµØ}O«­?pc·ØÁ]Czi,„„M‚ﻳx¿÷‰°3£ƒIÏÃ%' FNSñhÐfò[±¾Á™{ç Ÿ<Æ0½»˜··ÑÑ ÿÀÓknÃÇ „I7ÐÀû,¼JyK2‚u PYžö^Í®¯°T&û,> Ç9Ž".ùò4Õ¨'òÕš÷M&´¼yî]µ¼í-&4#l•MXHJöê– &¾mÜ¥‰Ç æ<¾9Ÿ€ŸCç³âyìd› hotæBKvþ܇Ì4höáZ.ÿÀÿžÿ0`tž7û˜=·N|,´ ¥àJ2™.™7 w¬~I^]ÿÍÝÉïÍŠ±4¡Á•NÙ6€ ‘ ±ðñUÞ_wvìk#ÜZ-Çfʤ~Ã? ®ñ[ïMgWžÀÞBtŸ™unõdäpO"Ʋ"¿RîœõZ$Iÿæà‘–¹þ`"MÈð#Z“…˜kÇVÔãÛ9š<êq™.ÐA®©ý»cBÔ̰“+Ý|JåÏÑ̯waðŒVH££Í_Xó¥£F1¼¥9‹l¾¬LÇ8>äÂŽn°·Ë"“Ò¨ôŸáš)j¦ú~ñk:ìý6q ‚ugQ£»»z½`Aó(\ T‰þ—½èPhîýŠ›‹Xóû;(¶Ù ÞÊl„/N¼6ÞL::\š\÷°Ç+½Á,gƒ<½â’Kl‚pÔ¦4wX·¹ÁfˆƒL‰ÕLæ°1ý”ö&M²¥ œ{"Ù±ÞØ‘ø¿èÙC\Rõ„«JZ‡S?/„mÇPØÓ '^ØÉ–‹å¤?eÁÛ>4ºûžWæW·ad©(»f@Þûç‚Æã ø|wŽ·…ޏӻ{®r+â ¡~é˜îR‡îÇ5ØÜÞ(Þ–æ£Ðù|-­ ¹†bF­pæ¾U%©ÄÞڅ̹BžŽžJOL¿…ë¨×¤Ì+²Þ­ˆ1£aγYL.€ñü‡±e­";<ñó‹~¿yÝÐpêÓ¼<Ž›—]ÀåÞ„¯Ø4[mܰÖÈD.ÇÁºlø˜ˆß6jgnÄùÿ Á¿7Ӛ߸æ>ÿÛšLúÚž¬îtlBßUOHËnyl#RôjY¯ (ÛðrÒgæÅñÄCtÚ}NÄæõ²´.§š®Y'Å*'ï MÞftÇ÷ãøvY´UˆÒø)繚«W 3m?ÐÌ€â’#ôf»,4~ ‹ìQ¦6îáàil̼I6«]%R‘oÈ¢oGðkù(@«üxQþž›§š¤NÞxxÎþL_9:¶%b¥Þ~q`e¦‰%î=ÜäbÔÌë1u9‰¾…Ídç¾ ÖôŒk6]‹ïš2Ó1êpä@%>2¢%‚Cpõô+ˆ²ƒ9¸Üý^†Ú¥çˆ©¼*žÊMlô‚¨Ç\wüyd>÷Cî÷¬cG3 sasÝ˾—¨‚‹Ì2ž÷´D˜u¯u ›Ýc!Pf, NõfÖžJL¹Ì“^ú«ÊRÅG¡kí¦Xëæø¿ù7½7³a»C ·Qâ;¼z*Ç¢gOƒío’lù6˜žsø©ÎTÈi ݬ¯Áy—è‘my_8?oyö4[šŽÂûàV´‚ɾ¾Ä½O«&Í‚kŽ)ØY»'Y£é.Iô•e5|pÁÏ{pEòü÷ùÓùÕ eQ§†diGñ XôN½}JÞ= ÄÉŸ9Ëgíß~σ §0v/!Þ’ói™Y;îºÆ`¿Þ096‡F ÐõÇ63Ç{w7K~š‡ƒGúá?‹¿9‚íäRø#PÔbeìt’Å2™ X6e ¸8ÿ®¦­ÿñe¤tföÈJÐýg_€ÔÓ2Xùʼn½äJ‹ÔÑ`m æm­æb¦À÷JYì¾áDwl³fßÚ¥˜Ó·6úïéAˆü }“Íèd³ #¨GH›‘в¯¤6&—¬ã©3q!¦òŸe‰{ÒäUêœ.ØqEÖØîÇ­T£·Ç ÁåÐÇhsøγӃ®ƒ± ¼ê0û¢z™8oD…^?ÅUVƒ0yÒ+î§ÌåŠ%Q Y‚¬{wõ V¹ @¢îø¡\~J­šû`} ç¥âµ>GV¥ìÀ*JзEؤaâ}÷‘ü>‹+*ˆÏç ¸7ŠÄÌí„óg7BþÜ7¼þ/apälÎðëõØ 7„Ÿ‘ßgFûÒ‚`Ô)w¶¹i>ü®Wcw ¦Ð:¾(6lîçf_< 2/µÙí¢8ø(‹‡]!ÎEº^ŽïW<˜èƒ)tÉéµ,$Ry>:ÂÍ?$Žs\¤á[‹(<g¥– xB‹ÐÍ…!œÔƒC©J­ká8×lT?ÖŸ@ãæo¤CH˜òÒæÓyNᇼ•>Mc Nbƒ~Œò(À’O8y¥&xÖ`Oò*vl÷Ƭ¬sØ})³ßš4ÂÿŒ…0¯d+èêÙKÝüê§¨È B|ÿššFŒ"$Øý|x*!ÍõüO—_ð 㪠—£Þ*·ˆdÏyª²Í”¦#¾7b±{Ƴ g)Ø2£ï7˲; ’lšž= Ÿ.…A?¥©Ã“Ïà˜@²°äÌsˆ*x‡Ï"7QÍ2ôœ€[Jw[j[8šfWoÅm®K þ¤*Óž-Â…èœòN´¬“!ƒ~ ø} •ꯄÿÞKÜê3·fÎ4®“+Í蒾ݴig[½¿,ÙÇxœu—+Û<ß‘ý™ö¿ùßÚÄFÿvœ"ŸÿöÀ~WÌ¥§AžL¾=þ Þ©àÕ¸1 $kaé„$î’j)þ,HÁ–BÐÔlÏÒ[’ì½CÎË¥¸wîL¦ %GëŒÆo—4ÄúƒÿŠÑàj>öþXGB"ΣnèqPè´ƒ]7ù;\á•E ôK3ÃÃæ3as€Û¿;z[s@å´6îÞ‚{'~€;¥6ð@(œwI÷4{ë“˵$k“§á·`ùK}XRɳÁ)D/Λ–ü´¡^«ÚPÖd)>ºº´i£‰c?wâ»zòêž% m“ÁŸ¡ÿ¼¢½a?—|߀fÎ^Ï8?+ú6ÞŸ]’QÎ÷Z©2ïkñâ•lþÍ+^`ºe¬ýÕOÆ<¹Þ«ïbSÄZVÖÕKÄ>Å0YñìæKáýhãü6hp+Æ[~hù`œý¥ÈífMƒ[·/à õã âË'O_váU9[}z”æ­ƒŽ{ºpâÀ{(SmÆ!ž ôöÃÅg–‚åòwxÞ:,|¨ÿkz)K•'¼L ÓÊW°Úõæ4X^•}Z-DfšâûeÚ°è—L»bAïöàþ‰pAà\üªKmÞ9³gŒàÿ2œÿ;»;¸•ɶ¼•+?“0¥T˜®&›‚<á£h>Ž*ÚÉ©"<¹zKû=\êTÇ?¯Y _9åÇ“¹ %p|‘8ÑÚàBØI–0÷÷:÷/þÅÐ\o˜åp ’»üQ£m8Ö¿=J®L.¯X½á îæÆáÙ†éäuõ#ü vF¸À©oìÑ«¸#»/ó%†ëeëÕÏ`­À~îü 1Ú¨ÝCíʆ]«oà„¼}xu‘;±mª€_›ZH±Y…é$lì-Hµ^n—W‡@N£§ …™°ßãÏÃVn|¸Ý$ŠjGÎÿænoÔ†©5vh°õ4šJ"< Ow»§¶º&\²U™cMM1`/ã[ˆŽï5:iéVÔ^Чýa[lËai|%ÎŽ˜ÈþØmÃqF{XÀ×îQ­”Û¾ElcÜIê~ÖƒÞJ …iORØR·.ÌÒdŸl2™„ÞeÜ–p*­WCÐ1Q0{bN'zÆ~_pÔ¾AŒç;ºþ˜ ½w_Û·ÚýæÙï6†Ä1ÿ°øsnÝîʼndÚaâÌhHo¸Â„š?à?Út­T'ÖN8Â"”wñq]¶tÎiG6Ÿ^¤+ü·ðÿ­¾Uv¯"– ÑAüï½?ÿ ¯Ú+Fï°£žPì:S‚×P…^ãNçWÂ0Ûí)>ÕzIÿo"&¯¿bãû³ °Ý‚Õ)…°?¢£A*É•5ìfÏûÀ"Án;é…;8/:±Õžuˆ¯ ß“÷¢äh{jUšFÝ× gب…îÔûy?÷<'u¥ðÈ©®âŽ }˜‰é i¡¿œC›=â–kÙ(8ôŠEwNgG6zbî1Zxd]$œÏ™wâ¼÷±ìëš<ņvt WÀ¯Ê`Ú“¨¾O€ž–¡sÎVÜÿÓÂßo­>ñ—Ø*s}q²¯¹ëGéì´f4yãÊ®¤æ²#}·Q’ …GÛ”èåîb<¬wˆ¾¼ÈŠ”îÀöö9Dnb Œod6îcñO˜,¤)’;ñ~ 7 ®¨&1Uqsã9î½î)²ÈJ\µdÏ®kA†ýbLŸ¡ÍVjJ³Å·Úpð¤3×›ò|Á<Ë÷ $_…+|“PÜq2çñ§‡ìÅöÞK‡Ÿ­è–ü}øiB*@ô¸ÉÌAë Ý? =q*½M|‡²H§œ].Hk.Ý'÷Æ­‚]‹3+ÞüÔf³æ8ÿïüÿ˜ñXêû˜?Ø-Èã.’Î6!:!a ¸ü(ÆW—žC‹=þ-Õâ4KCü|æÙ~Dcßw(°[„eÎÑB…¦î×w¶­r‹h؈O½  hùNˆ°ÂiׂaÑiEžŠ¬'=±FeŽl 5 8xi?Zý]w õQ{H—>o8WŒ‰ä:*§Ñ²‹cÑâw§½£äJo6±så¯ñï}KüÄTáî(”pv¤Ñä ¾ýÒ2=—¹äŒeœçgüÔVEÇÆáL¥0tÉßG¿MË[Ph0‡qGÝŸºüNx3ÂXΰ¹ÐÎÛrX›¾ZÌ .J H|ùÕoDÅk¤hЂM41{"S)7Æ4ÑDo×6´¨žé²ý­\÷C;Zòîðp­2[—:™.Õ4`f‡Ó(n}»3“¶½H)ºõ— mOØÅΙKÿi1?ÙjŒ²\ˆÂ'+è‚blÏ­ nÚ—;`0KÝHàNÈa3²†cé| |–ä„—I[˜Ë"Ž}¿ÖŽš—1¼^†=,¼C¿uÙÙ»¸Ü#.V>"ŽÛ ×@gâ^-Ð¥éßøþ«ª92=ö/L ¦¯Ñ?V.ªˆuÏ…}k}igE—NЃ1?ã¸×7–0'—&˜•z“W:q<Ôo±À´ «YmÌpW´ÌKæ!hÔ4¨º¢Ko­*`Íz›8Ÿ°Få:l­¤gµ•Ùœík*ÅÏ3§§àä&!–jxŸÌÝŠj—aê[[W€Sqר´óçé/6š†NÖba³SAv¼ÝæäÉ^f+ª°FR)Qü6ÿ@R¹±ÏÌ^Ûô›®?V6W˜LÜK#jþoÝeÚ§¨Ü]>ÌèóDí¿›¡|Ý=Îægk÷œÍ*oÊÑf½÷vÿÿŽÏ‘à4Í›¨³ËX`/ÉÖñ±»J”^¥IåÛ¡òËoÄ! *–‡Ð ?§ï~ ®2BœÐà°ææÔÜ ¿d§>¿‡ô"#Vt§gU)R•gñd½ÊG´wÙ‡ÂRqòÄyüG~A˜þÔñe9êvØßòÂ)¨‘lŠ/.ÆI“(>Á䇿 ·v-3LžÏñv\@Ó³,±/ïÿáÛ¨[Bëç9¼Œ3âxí· Ý¹¥ÝŸˆ€îž'|¦]‹õ¶QhÙ&Çæ{Oaw5ØÌV³üþø+B§ÎŸ:³gÔ¿ÊÑOðß] Öe+Ì©1 ?¤LÛ¨9'ú&šž ¼•³oAAUÕ‰ãî]:TEá+ÃòÕËp[FïÕ¦ Y$tñL:¶ä÷Ç> :K®“Þïа` ;§íá”EÅ 3U?Û`Ký•Ï"Žj„»)úìï.jüS£ÇÇÁݶâž—{ìMh踅Ôh¿=“šÓŽÆöc<ýÿ.ááÊexÿʘµ"nÆÑ)‘?@ñb;*¨¤ÂË’Q4ÿ¬:Ýê% Šsß“óRî<“Œh°¨"qQ£Ø­! z7•›¡uh$ÿŸäO{›W¡cù]T¼L6$#ýp®Ë}ïÝ.®Óí ±™%EË7·÷SŒ&•*R9õlÛjÁºúšHÚnxôjŒ_/ÏÕ®¤¨´ý^6t ß»ÁóÃN¼ /N} ZÒÆÔz™ýÝ,'ÈzžI^ Me…µjôòÅsðâÚ&¶÷ª½Öa§>ÇÖ¿°m¹ ;%'ÇŽL¾a ¨KÌ$hµKÃü¨îöˆßå~‹¨¢µvñÐvÿ;(m·„‹KDg©] ÞOT÷QºxûQXv ¦©\ÀÇŸoÏA'\ÍsÖe¿X+‚sž¨ò¢á¦ŠàÝô¾5‰²$6M™Fútñ£S ÏQ¥¥>‘°öÏWð\=H¾9à‹k™äàyaÜl£ÃKžòÿ­Š7Þ µG[¡ýt\AÌ…‰%k@M§þ]䜎±q_ïc]ÅÞŽË}jË9÷SɰÑkX÷>yÎÝø—D^çÄâmoQ¶¼ú]\ŸN†º—%­äÈÍûXèý ÛN(³Ä àØ‹ïn¨PÙ31àWý “>ÿ€Ò1q†2½“˜[¬« ¦oGsÁ¶óØmÉɰâÞÿúž¹¥£×áóé+ù’œ²Ãßû_“ýq0c‘ ZLëWokŒzÓiŸw ŸVÃçF39ÁçÀ_< Jgtrdz dyÞ$¼Ê4`n^ŽƒS›ýÀ[MÙ ¹2-‡-xwË_xý „ÚßgÕ! Ûú _î…U4kJ6‚Ï&[ºÇÑyu÷Q³O#¶Þ€÷U©ˆúfžÇ´AÐ`Ѹ´ü&>Ò°œÖíc_¥™˜O2,¯„å•8áÍœ²TÌê›`ÊÂõ`e UŸcÙúÇí¼MYÐï£Î:.f?fŽÜÿuzîbTW{ÎM°#xä˜î¡Æ¢­ìñxZãèƒeIç˜)ï1“ —ÞÚ¢Âá`°â-eËc8£‡Ïy¿±?³®ÚólhçxSvnC9Ò§á>§†;‚·¶Ø³µï’ŸÃ¾¶ s¢ëñòŽŒ²DÁNmºµóYEɃ4î ½ׄGêß™Ó×ðÓ5\³–ì=‚fZW¹ÍÓR¹¦Ï6øä—=íl>ªÃùŒ(îï=/|;cÇÝǦϥäÊ +ÐðÃOkôÀ»å¾“‰#¼½²Œ&ÌÁÎôóØ­~¯n…ÛíÆ0åC%îó …üï‘T·  ùM¸KiX×úžÀ±ãu¿q_kç±ÍÌêõ ÞéC±à|0~åÐbª/ôÈ›Ãùx÷àe~­u–.ÅÌêÂòùw¸Á] Dha¼Åý/gâò óDuqz`Ѩ·œÙbAj5”†AÒY\Ñ-uz­û̈ÿ¿’Ç­Øho,5P_\®{Ï…áÏìナ‘''É“þò·´­6a¾œg6ôe,ÃD¯Uìå6œ­2™¹6ñ²nzRk\YÁWy†z©7 Ò „¦|ÅæC:l{åT˜7·›L\+O“ßàòúGñ(݃ôŸ±‚›‚«NPœT]Ç 9©1· °äÅM<æéN—Æ?‚ˆ¹1øÁ¾8ò@xI:æFÉS£—~œÙòßäJ¤ XN £·|æîåç {ðY_?ŽÙ¡Ó·ë²WÖûák‡ ç"«vN®xÏÔ“%Í^Ãü­ÅÞ¯UüßóO É{/ø /NfAðóë℘õgƒÓÆ™2åVSšãõ˜l+úŒk§z©oÈ’›Çˆ¹ý?2N霙} š?¥ñ2ÖõcÿÞþvœÊq¾»;¹‡îõ˜1&|‡ˆ«ÛsÔxÚÂˬ;ÄéÊŒ{£m£AH†-È”`æŸÆ2û›ïà£òcîC»:;ìÝÉ[y¨"n9Øm /ÆÄ+>påÃ%îÛ·åôgQ0zvÄà#AêòB24˜`T2ÜÊ¿‹›ín€¯_öW£É¡Gɘ V¼ì•PèŸ0ýïõ–‰b¸çË‘þƒKÇÖmÜ';C3a„ÇÓ Ù?8I,§¤ÎE¡7çP¾ÊÆá@¸!K|«ÎþäŒA§P\[éÏŸ´¬¯¼RA“µȸñ-PôW‚‘P¡\i l‰7ÝT;öð«*Š¥grqccñ΄0Èê|Ä‘šÇnËÒ=)Ãúí5ÛÅ›˜“½¯^pÎ9`ºIŧr>9vÌtõ\:ïðª÷7šoô¶M×µ©Ñ³É\Möux1ÐK̹/pói©}¹•"§ŽÑ\š² n¸¾‚}l¿€ «?Ø'Iš±¦ûû`°t4¤l©ß_‰µR ðâ±óôŽh8Ìòø‚¯V:ЧMd¬©V¦ÞBí lÜ­Íî,æù¦]ŒHë×lžƒ‘ /ßÞÞ‡¿Âî]†èè;šÜóc-GzÀ<ï.u_‘>y=)ŽX+)ˆs÷ÌòèY‚©Ÿ6ò5’É:»—fß¶ýìr˜ÉÂÍOA´œ÷ÑG {Þüuϱj” s=­KŸ|Æúì\»j#à˜­°„ëÞ’¿ŽB¬˜ý¾¼Œb’¹ü+œ"¹Ä)Š3£î@îà¯@¾ÿ†ø€@«6¬á_Üè2˜¥DEæ7¡5¤ˆ¯(R gA—{Ô-îBwž;ôñ ¾·ˆÅÏE¡à_¹-×çXãlÓ ·¥–LʯícÅ£íÿù‚2© èhçÜ&\â.üãœÑ»¾ˆü_ƒ¾B²äd’ çv\‚ÿªQOvL%GS×;áEÛ@2aú]ØÞa &z ¶ü&©r¶Â‹/Ž2‡¦;ÜøK=D¬_Ÿ©eLG¥a;kµÉÆ¥·VÃhÞ~ðTZ€k?ý=ûb¼3Ç›^a%{ýÐÐÂËB÷r'ÂÞŸóáÎùü·æÓÿü€Ⱥa²Æ3¨ÛÍQ쩌 ñY]??)aòÕdÜe"É?M$“4fàÔuÁ¨³£Œ/Q‰§’ÂaÈù= EN_%Bc®â—ê|N âÊÜU¤ÅcGÓ˜Í`ë9Íl©àƒ8œ=—{™–N C¢Á}ZiÜ#„ñÉæ|˜pç9«y'HœÁPlÞÉ0Iö§©$h& '’k_Ãmo?JȈ« 9 ­:ÈRþxø¬-¢Ý°ÿS2Y®× ss°]CˆuºAdæÆréa]œô­  {ÿ ÷ýõø’ó£^Íáßì¦ \œvž´–/ʼng{È¥´Ó8pG>žPcÓϨ#ïë ×â˜DVü*#mCCÜ’•ŸðGë]å_— ±¦‹Ù¸·¯–üð™Kk ñâ8ÂM÷àw­:’Fiørí'TÆß\ºÉ<•¿‰ÖÄ>ÂB£“L°`:¹Eò•LFÓÎÇ\Ç»(T/Âf}ãðéÞ<ÈkS¤çb"ðŸKìM„˜ƒ;aŸ—=®õyÀ›ö\‡ÎØRÓ}/0Õ¢ý0M,¼÷ICBL-¤lc ¯jȈ<}ýg&Í•ÃC_+Qû@4¬Ê¯Ñ?Çcðiþ3Þ-ŸL¼+§=„¦©Qäè­7x¼ò žé8ˆãë³ÉçpûBBL>í³}Ô€; ¤ót¸‹mwàË8x{3 ~_½B¦»\ƒmYö°_áÁBÜ}x-Ø=†ë¥zl²ù þÆGYlI‹86æ­‹‰útü¾Õ4ŒJÏÅÙu¡ÿ+/ÅWS3@x›8š)]cL¶ð'Í› ›ÕÓøЦ³Ô¼ ¶÷f'/Î= µG-ƒ¯gyôÒ"Eª¥ÏÁÇY°¡ä$Q¿ž‹9Ãqá|”ù,^º¾ï`p¨ˆÜ7Ô‡•+hêöÿé?»š`ØPKO¿‡£'@H:¼{dö™Mh˜qûÎ8°ƒ—«¹eC2°dåÜi3.ùåíXµ|-Z‰‘5ñxîl)ÿ®Nañ×:£ ¼KN|ñ/¬‡ø¿$Ýĭe ç+”éÓc*¼ÙBö¶É‹g;àÎÞ©h±~:Äœ~…?ÖirÖ‹Q.HÙ÷æ ³àeëÊßåì÷M®\¯a\û´EfwárÇ›X(ÖðifÀg¦`n!IÇ=Çá‹HÑtq¶i¹¬ýë‹?S?ƒ58YÍjÝD—¼Pòã—:ó™®M•÷¾'ÖO–ÁBïäâiÎs¬/DÅ*A@O0N0ÚkÖŠaö˜29ä2½ë~¼WÜJRJx!*,¥ôRÅ+ùÿÛÏ—®›:›%'¡#ߎôž „#òwínHüŠ’•¨ûi »;3۾Ãkvá”ÞîÖÍÑ·â,ð®‹K‡—k(\¹– /V7bqbÿËxü+ZŠuÅç¸}g¬aíÞÿ]ÿ/¯ ­xe_Äõ—Ê”¾ 5¿…ñë/ ´ }Åʉ¹8!ú-JL{ŒæÛ`»lþŠ­ÃE›¦q³¢ÕÈ.Yöµ4´Ä÷Á‘…£Ù¿ðf\²W“°ÂÓcgÓñ‘dÛÓtòë²þ‹Váp‚w1À¼ž:CbíCTÔÆQ-Ö pæ¦ôÁ¼è1ŒÉ·Âu£:~¢Ž„z®€ÅƒÊ°’aé¦B<Ì&Òo~%0=9v;Z±³¿ÉI—NÔ~ +×`¦‹)7û¡ÝÚPÂÙ?Ì!Ûfƒ´ö|ÿ[Ûíïàæƒò-QÐa#õßRÍ™0Á¥—ÓóŽ93»´ š!4~·M„Ù^ÈŸbÁT¿ýN>Îð‹'û#¹k§ëxù³£ŸÄÀÛ xÔÑi¨ðVR„ÿú?í͇‡ë’ÀåÞ2ÈôcqÞJ(žk ñ²Ü‹¿¸äCÛq…qæ=»Áåûeë °gpØgÔB`ùR:Øpö{ÅÀ• NdõK‡òsu°X5îgÅãá÷§pô‘yœÆª×œëm%.C×EºÃÖ@4=³“ɦ7rÌß¿îL ¢ŸÆÂz`1â[îâŒÙ6Úï ›|GìV“?©P™N,ïu’Ü1Ï@½ê3Î]Ç=zÔµpÊq¶­¤”œÑ/×¼8Œ‰Ç¼!xKq»ß3,TÔcê[™DœÓƒxÁ8„³š¶‹¸ÌN€ƒ§ßá¼o›¸†Î»ð1á;ÇV*°®•†pöùN³c)ø$4ƒûà˜îùÌ•dÉ1Üò€ti…UïoÁ²ÓÛ±¯að~­Aó€»Xò£2ëÿžéœìrŽhíÅ~Ëå¸jU%j#ÍPïæS¬;–ƹä‡7¸•«ÈÓ¬ã°{ë)˜¸d!dœØ·û§óïö ŒØÿŠ%ëÈâý»¸3ßeÙØCžÔH_„ .Þ“e·Àþ`nB÷:îñ®‰ìÕ;;j˜:~#£ÖHAnödœ«1“=19Y*ªèTaÅÖ”^U±>^‡Ú8<Ó <3‚@DIžŽZuíâaÎ1šguÄ"lÀí ü3œH¾”Æ‹&òTTO‚Í^¹õÌ6“ÕpÚEn~2¤™Ó_'H…ã*¢CÇÑj»*ò2ê,Þ jm‚ëg6T,7øË»”W€÷dÊÀ«w:ùÝÒ„›îœ,/Œ˜À¤Ú¢p‘q9ñ›Š gÉÒo\JÏ¿>‚?Å«u·+AqD¼ÛQ…2áà'fÉÒélº vg$\äP+«àø;¸7w&=d†Îßä¸ Ñ#ß§ïBL1,–ÖØÿ» †DH±¯8õñCÛoE\åâvh XÇ+ÎÝ‚×.gpûº9©Žt¦n ¤µB ÒF; ö3ª¸JúÄ«~6Öq3Þ<³Êí4bô˜?†ºüt‚¡Õ¼Â-Rb粎kîÜxÀýŽ‹®åÒˆì]xûÎux Ë+ ¹OÇ.)» ?8 6çÈ".Â'šÿfi,K9þ¿çŸè[ÂŒàøïî}.dÂ1hrŠÄ%©ÙøúÕ\ Jw¿u%Í׋\Ætèʨ÷mÄ{oà 1ª}¾E×kÒeïð *UõèÒW?‰QV,rù‹Kgö ÀæÚžÇ`Ëwäâòcœ©ö (¾r ÇŽ†íû嬜Gç݃MB´Fó HøëÁÍGàïµ—p>ìûõ¼ \#…Ù±F$¼|¯n'¬^2Š;ú‰|‘aƒ‘æØñà"Ф- Ö3£É‡±hY"g½NŽzì,Eß›ö¸Øw*v¬f÷Š·Ñ÷Ú­#þ¾ÇBßïÁÒì!XÅ÷«GSÁ}¬ XÞµ‚‹'^ã„™2ĤAíJ÷mýC0]D–9팃kº›øüCo!Xþ|:A®ÉÍ%ú²(\ˆ]¶À͸(.{Û¨Ø÷†¤O¡ÒßC±z@†æ)å_·ðéVóøpûo6YÚ–ˆ^pÿvçâ]üVö<éš'  ½”~H+'Ç7⟔bì ,•.Ÿ5N <âoÞË-”¶‚×§DXkú2îyò?²¾ã &ÿ`£«ÍÉÆšy|ääÖ9½ÆóÕ‡ëQÊë1a’ÁúømHVVvý·Z¤ÃÑKj´ù‡ËÞ‘Kù%Òxòà1*4˜G·¿’¢Á%^˜r@ 3}‹iñàLXüÌ’j\Ïá^jí„9b‰Àçï%¯ÿýÃùéùds€Ó‰»C¾AË´lê`Ú…}ÆšK'A­(<™:™õÅ'·Î…äIjí t@„éýø_ÿ×DÆ×™ÚŽ‹v“ØIÜÑe üÓ æ¹ôƒiìc4Õ—å]ðÏdMŸ¤ÙëÑ_pƒV X²c³±(î¶* Ô¸†ªH*ФÝrÌçÀLw6‰±e-ZæÓÇû×ûñ@L™ÛXžlÒŽÅ“d±¥á“«1}&ãûw)R½“%h8¤™FV"¶×ƒ¥Û\”ôV¡dC¯ô@6¤ þÁ•š©Ã ÿ©ç5ÉØu†¬¾OdçðüˆA:ïàN6…Ûr6|ÍÌ+ >Á…)RÆÌ@-¹¯duÄþ_ø ðnìÁ×ßU!tímèÜŸŒßDá×äjNuA†žL—`÷*Ùµw× dõ;bJ§R0^B⵬É2 ¸Ñ9D6¹Kãì'Ïà§Qp¬ã6žr»Š›÷*³Úïè÷T‘V9¨`›)ÂþYê¬[T–ÊÎàöï¥s{¥9ãÛ!lÝ–ñô^èO4XŒMQ‚Æt݃ը¾Ì€ÙKbçœ8œ¼v“‘žÏú+baêœXu=†}Tesv©‘²¸£ø>k6±~)É~,¤l„éL­¼Â»µ`çåŠN«ƒØÚ,;L3º†Z–ÿoþÑÕ;dë*Ò5Ï8,e&ê§qµ©¬xmÁVÍ$.«Áû ¼ÿúŒYÈ)¼’ªÆ}H¶ïÑcl\›`Ϊ—]¥{$¿€yòBÕv†é+yâ¡Ù‘Ü#áH.×èûe} K¶¬¥nùÜÁç¾dÿ¡tövU]з úô ÝÎP<Ð)BCÌa_ÄJ¤B_pwÀupWÙçÒåpÖ¶5ìÕ^_f˳•éÖÌAã¦âtÞÊ‚ '4›ÇìÊ߇샎¬sß,[ýë¹B”¹nÆ8Ÿ/ ™îEÒ°ñõlšµy½ê:†ÍùÎ}jI¨8|?b$ÿ=èŸ üoÜY©lë¶2ÎS>*ù×A I “U_cq¡"Q äѼ=Ax¸û%nÜÒ¥yùhý¨î>4Á›´g65ÚÕÌâ×mufe!ÚPv@œ½ŽÛïGuãÅm¬(|}!8ŽêÈ„`µÀiÐ3e·–䣼6¹¯v4&Ï!¦á—Ȯصìü*=ÜÿêdJd‘ž°r@Ómìúäp<ºúé’<¾mC}ïipí«*ݽ ¿Îçѹç—Ãa›C0Sî.Ú»~€#ó¢¾d[îªÄ†l~’å­0Mê"6Ì[„ Ä»À%héHüK¾mÖ§¡ü/yü€¡÷t¬_ÅÓp¢J™ ý„Â`:ç§Y,GaP-ŠÀáçqZÓ8–QÖÃ]¼#Äœ½%ØËÿàÞµç|•vgrÒ«™û|0K_x³]Y˜¼g{'’‰û ‚ò˜é濦/M‚é£#@Ð̃óy6·/h…•ê`>ô<~“•¥¹0O¬JlÖ¢äÞ¨’EàGÕYÙSIxX\™Jž¤k Ã/gü”µ“K»¼®b­D0oÃü¥¸aê,”ùh…Æœï‹W ÞotÄ뢾D£¸Ë\ìFì_ýZrßfa¾ÝÛË42µè‹Ä üöt O9‰:Ç‹0éÇ}R|³ ÇÝíìÈŽjþÓ‰cá¦Ê =ÅĈ0ªÉD¬*˜žð*êõÊŒ¶ÿ™³çPQhÛ¼ åw°E´6ôú˜^Á›[×ÒoÄpÇ{°-q…#ãˆTBs¿?Œ#A ¾ëðŽ"ø™Jº¸¢Ø{ *´“h—k@U•,g3Ö„©ìÇÇq¶,i¸>W>™„6°•t.Uà_x·VÀxz!3˜:ö…üéòôŠF<~—ÆytO6^r„ÿU%â'»ÆHн~‚´ap ÞpoÁ€™¹âªµ 0åY÷ç6Ì5«æ®4  ÛP(ÆŽe)ü•dîÍå´ÕÝ^9êÑõ?œè ‘2 Ø#gÞ• YÚúåo 3ÜbË%¶6aEh'|ôƒ[¿ÃþÈñ‹äýëáXÜ^ºòCÉÃù Êt\ý;,[W‡oÄ#ðIÇ9ÔÙžA ž™Ó(§&R·ÞœiüûßóŸ}žžå¤žNÇg–°Tb€§·ËiŠÓ²Ö8*Úu·ÁߪDº­×‘}ó˜[Ípìöüýf¶k£äp,m…}ÖSQy`Úûߪص1’ß㛈ßåÂð§Ìeø<:—«<»úN^ÂÁÜ•h‘œ½×-qÂ>Iez…,n›ü-â}DúWháš?Ð{®MŠjIÖÀO€ç<ÊZÔ†óx6ÖçéÇ+8£»ú°ëZ&Šçìl>8qå`JÌ-2ÿÜÝŽ¦ ÙPR?µ"YˆÝJÞîw©¤ãÒq®%•ÃcO5Gø¯«±‚‰»`DûìÐ-bÓ¸ÞÅà |î¸×,0ý#÷m‡6Ý*fÇ„œí¹–Neª9ï Þñ¥*{‡kãPäÅiÒ9KìXÂOÜw²_®“g5MÙP×-‰6²+qòïpä­6ê´ºö‰/šHg6‡Kç&ÂrC6(¯ÈòêsÀçµ=99‚Ÿ4®—“«©Â×]Ó.ŽÄÿ;%3*JV㦌rìh_ůœ±ÙxXóuóE_ ²Eãåá⦗Ä9,šîœ×‰šáÉõ“³™·’%u\ýÍF£ÄüZw^gß…Óº‚GÐxi-Ï­6”º-íG-6ìÞYù‹³Úqû½iÅň¾¼šwTȆrå“qû½cöã5 0áN1|þ_¿5¯¦]\—óó.”ðYõ ûft•§Ôòn{Y³—’Ö°‹€í&éP!z–k:÷Sƒè}9®G§UP •?p$÷Ñw5¥YÛVS«¬o¼M§uGøò# ¶Íx˹7»ÒÉ&4êæ5ˆkiåý)ÿR7æmÀô‚Ø%oÁÙ9˜·¢óøT_‚i—±×b#¾ù~.á-÷në—×è¼ÝƼ(Á—îp¤ä8j:üª°~㌳Öö⼟ÕúïD™††*èì= Vº°sãtrÜ%Œä›é êŠ"öÏ…ç³Þ£ÜwºþÔ&®væx®åût{r™/s´‹<ôüÀ«Híã¢Â®@Ñ úæ‰àóÈC(ÿ"‘ðîjPÕw¿¡rÉ º,'ŽY~À 7xä]})ÿÉ…ÍlKÁ”ÌPxsr„ÿKYÉöš»½àÆ[-Èøy V®»¼Å“™T;î½úß·Óõn°{üÄù“a¿„ ͕˿# ÜõUé÷RÌ0íçŠuÔ©ÝÍ(U*ÂË€MY8Íë^{ )ûíèσDI2´?Ωø»ÚˆZŸ7'®­Cðå¥8ýí»}W3m]EúZG/r ™×DPzñÛ&°Œºü8 t ÞTE£hò® ègüß«wàñìX© Œ=gài¡2éíAÛ[]ð-~1ήi…ÙWñË¿\øë¦ÎN÷u£™éþ‚2ðÈ9êTyMÇÂ(I%öwá$êqq¼±²À ɲ9kw)0…ä͸£/tãwÉ¥0oé­ÿË›ºE'èEs«Œ&Bµ€äœÎmø3†ùvºâk·² ‡îJ¢43ÆÙ_$éöC‘P1¾†}¯;ïKMþ‰¼Áí<±Ž®;+†?/†ƒžéÖrå±t*85–½$!Økû ê¢<àQßa¹žL|rCQÝCwÆqUÞ$U=Oµf7g›CÜçnnpL=|Ï>jK]àöÞÓ¼£»•™ÃƒáZíéý,Ü‘±ÿ†jop_õšÈYÂþ˜Ù§Ö,g"cưI²ùxyòY¡¼ ÎUK±‡Æ'Qýã:Økž ]ãÅØ±a^Ç$&Ñ3|߬¦1¦Œ¤þ%¦7¶aÕ9„’¯»±`©•s9K¤¼ê@ÿêyf¶B–¯ c=ªú¬\å†îVb®Ï³˜tË]( e·7j“)³>RÍO»èûm",äþnL1iÃM T­•Ñwý Þ 0gŽ›ú±Ï`•2w¢²4—ÎøƒÇ£‰ZQœ>„ü Ûx*¸k>[ê2gÝ„ˆ×“iV³ =ýù8nG•¶ÕŒðÿ:f<^­u›sªLÍ#†cþ‹ëÃÆzœîú5ž.õËG§ ônêà™î¶lìVŒ–@µ«ã$@ƒ¬ì釣N,óz k]‘t"…“—Ê¡ ’œ9×FÎ`Æ äQQÒK½Sá[Øbú¨z¦þ³ä¾œ:LVxé÷ŵXòÆÒG`²÷q\rñ8¬ØûïIcܱTec.ð†û¨Kßç= Ú{4qxmú¡®>ºü&<½Ô>ö~x0*Å\ãﺨN¦ç€¶Îf>¬£× `/–b—†Í©!°ö´Ëÿ}…¹ú‰×ÑHë9V9M§Ôâg´ÿ,Ì]€«hÓ™rt4]xOؾeŒ?—³æ WÜ£Hÿ-‹ÀŸ¯ÛOߢÀòÞß…Ô3Ö˜Œ·r<á>¢8/úgì×AÜæ@%¸´ªí¿âŒÙ&ô±ó~Í‘ m½7§û_A׈&?¥‹`!÷OŒ÷"NÜеÛõüìRt¥êYûÝ;’4´ŸEÐÀñpY÷#<Ú%QZ©0y‚=ùkŠ…†c¨ÆIK˜9‹R™öQä¤Dñp®Šƒ —oÁñ¢HP4܈ª2óa”¸Áÿ;&@‰DŽÛ¯þÇ îÃû)‘‘2ƒˆ)yå c@ô¯'ܹJC&1¹û0ÐlBŽœsc6^·XŠj¼@ƒ¢-`•^„çà<óV,ù} _œ½êoãVëØ²â-ÞBoðRx¨x­^•óW¨­à6¾ÞÎB®Ÿ·½Çˆ±ÊÜóî!/¾ã:Ñj5¦2Eü»Þµ$⌜>°ö§Ý‡ÿnÈ;;ŠD$ÛAèÖfÈTÅoó7±“jKÀeôé‘ó_ƺW yõ|¢XâÇüµCØ¡%<0Kˆ~«]M}íUés™[t_’“Ÿe†óÿlº/B‰9-‘¦’FšômõG®à¶,Õ˜s¯ …É?à¶feE¦øzê¬LÿëEÎLj¸¾—ç Ü Ä>¯J„®£¾L¶u7Î <ƒ—‰°‡z=¼äþï‡æ°»½eØ¿'éЛ¸€ìT¶ÞøEmÔ@+ ¹šÉ€µŸ‚8ÏÖ…RÂçÚJœÑbq:¾¨¹Æw-¸öÕ¸›MÅ«'<耻(|*Òd»Rýðê¥Chàù_¨—CÇk3 oŠÄêCÒd¾õ8úS­Æ _ÁsÕs CÌp$‚´™¤}Ýúû°ãI7qö÷|NæP9 :þ’í_Á«¦R]ÂE›Å`݆Q”g‹woçß9]†EÛpK.mÇâr4+¾¼Ž"MLð›Áºôï‚}"Û1A¿~Äÿ¿ß&µm…VƒK†@|<åå-,BÓ`ªä7åþ<Çü2ìøÖ{â¼%‚”½¬Äi›ƒðÔœ»äÉ{¨i«‡@žIAWõ¶îÜ&–Yê[±¿¨ÿ¾­ƒ5ùªö’+øgÆTÔi¼Š%Ý?ß–vl$õõóÙ¬¦ìœN9µhÑñÏXlªÄÔ³Káè8¶yáh&õ¾Ã3ŸÙEk7ÑÍ;ÃØ<í]ì‚q.ïÛÚMôÀsÖ:­4 ¨AN,xLØ€©¶íp:ë(üXÂE- cÞûF³ß/ÜØ Ëã,åúëæÏý§gTˆ= ù]}nP¯/ÁÆí‡ý‚—ÁyÑUÌÝ”Ê]©$–a÷¹´Ð8I½Dí„`ôޏ}k;„š% Ö͜à ´¬‡;þ£`eÇ .²ØÎ° lkOk {aÏr³"˯ûÇ·b¬sò$úRÏ‚öŽÇ¯kšÀdµLà­ÿ™Ëã÷dp·…£ì©™`±õ'ٵ͜>M£˜]€èq •]×ãÆïmÇ}b/¡¥$ ºÄé,ÇŸäG³4»÷a×½S‰¶Zç'æÁ¡™NÀí²âÖnøG jõ0FdçÿAë¿@¶Q'ÙòÛœö¾ÉÁ±="”ÚÃã'¢Yµðp}ØÛ<½ÙÔ¯ÒÔýpÌê_‹Ÿ “àÕ¶O¸ûÀd ó&C×ÃR>Ài奡³²¼6Rc»9°(u„™ênä®õC¸'5õcÙrmjÞéÉįΠk^,ƒ£sÞÁí»©"·ÝÆîÄ%C\¤ê!(?3ÀùKGÒ9Ù¹? 6ïwÁ–ÐItï½°)³žËL”¯Ør¶# EëÖÞÓf¸y8¿ŠÕ¤ÿ& 1u'pDô6ÊÏ™ ¡ÍU(*Nmçb½4Éÿ¶âÓðè¨8|¢=‘þ3?Ê~‰b8§ÎšfFƒ¶ôb·Ž*ü ™Á¾4ŒG§Ðéœnë+и8…j$+r¶’ca© "XÆ]Ê¿‘!G`Ë‹ÍDÀ]e¹]lo’ å<’¹p.•çðø!ÐB#&>¬O—ž•©?rÌKæ Ô”lD^åûòwÐjj1dÜ›*«“¾.®ûRL­¾NüÝÙ}Ù|“9ÌÀB€®¢ž´Ëªͬ’¹æçûáØª@0`î6§žGNÓÃÿ7¯zï㪅0ýøD0~Öí– °êæ\N,l*ìÿ›=±X‘®HÌà6Ï+†qßʈ‹Î*’—"Åþ*D@éÍXüb ú·àº(¸o:ÊZ¤bÚG#÷ßw¼Õ›Žã÷•Žä_Ãj”ümJzÒ¶ÐÆ»aнð!1¶¬ÄƒfÑ\ÏhX'E³Å@s'ohr2KŽ“ Ëw€«H1Ì.§3 –Î"º‘Ú,¯x-Õ%ÿ«ÿÅßåÔ ¾“¼n=æó;Ä…<ɳ=•8áÃp¥gUagöIÎ?õî-;†¢§­†×: 1/c»ËWôÔ»?ìË›AñÎdô >wŠ/pÓ÷±…< Zt2œœ°ÀÕçúˆ[ð|8=tX ²·*6éçÃ~¨Wf­d?MÓ΃ãÄÿ›û Ý6±ü¬öàö}n^ía/ñ‡ÔÝ× ÚGÇs®×…¡Ì«„=ÍÆÿŠqÛïéÜÂxDk6‘ö”+Cÿë\pJ–¡7aƒ´õäçò’œP÷þns°Ôý;~[™0‚ÿþŠbSÐÆY¤A|¾3Ò‚$ß­@÷‡sŒ¶Àiê!ò¨šÛpÂ}^`tÿ®*Œ} ^>}óÏß:ö1ùx±™3¶[;×D’Kb¯AÖW& ×”[Žrç4B¯P'wrš3˜üká(2"¯(Ì]?ö^Jr…ųhE6H'ó¨ßÅ;¦«á½ö 4›¿çî¹FbÞZÐÃʰw›:˜»šT¿Ú„Ë °>Ç„ëÒÛœ#—㟠fÎ@Núdƒ œŠý\º#H‚þBi¢(loxÊé)öK²þwÿ[At ¼\4 “I΋oöŽNB>Iž®±ÅsÌ™ÿG­^]|Y[ŸÁÛR ØžÎm^YŽÍ«˜î4 æö¤›+ßTukÿqòî󨮙‹-ðƒAö‘œõÞKº¤©è«wÐ:w<³Ê £.!]¸…ºþ¼1›±«…2½Óqe¼ »¢ƒ‚÷b¨Ôj|UÓÅ}·—dY[½ay™~4OàÕŠä`úúŒ(ëS·k’Ô°°L;¿¥¥,#?žƒþàxj¸,‹¬üia¹üQEªloÝh6*¶n']‡1"ê,J÷ÍHü;eÚeÿ§\Ž•83µsÁxìa™d=Ž{úà$x‘ã@¿^ç¯¿Ô ;V¿!eEåx³B‹NÐaIj´w› SâÌ¡x¨”ç®QkzŒØF‡qlìy\|æ:.?`Am—ÐÍ ½áPÊhv@)ÍÅœà›¼:&Âd®Þ„×¹Áxåˆ$=q$}¦PË 9ÄÈ<ÞmªÆŽ {™Ëƒ¥ô•³ä8ýŦ4n…:óåªj;Ù_mf¸hxô£ B äfý>~¿ÝeNû‡,×V`¥>K9-ñ¨#EåR¯@ÞÂÛðá£ÏuváHþ·ßùŠÿàÅ.ðäºþ|ûLCp3˜¬¶#UO9É)?`ñpNÓ­BŸÄü„]m*Ììk_EI¨4ÿ8tµ˜ÅÝ’2Q–š}ÝVµ8óa09ë9žó܉æwN/9‰×žÿ ¡Žåphi„<Ñ í±’¬²ú¸-“Œ8†UgôpÝsY¾0Ÿ»Ú- :a§ ˱Šï°¢…©^€0cÆÖP™Œ8#,?9ÈÖŸSÇœ A¨Ò' ‹*!>f ¨;»Ò™s»ñvßL¶¡pù0ì ùg&#hCØ¢¦üGΆ%ó/“ǽWÐÛq3Ü“¬ÀA%K˜"–œ5ªŽàN ‘¼©uØ™°‹ù«dÐ}Gë0¶=äë€ß sG™]´”-êˆj’ÑEá8Ù.’'^ ¡ÄÔœi³= "o·ˆÓûW®PÝ ¡LîÔir¥û ^CQn¥µÙÌ¡ô¬©tï2ö%üg~`)H|\K¬¿ Öc#qêñøl+Èì72#5 úö¹,K® …ñ×8nê¾iðÉH‚Ê…'€[á>ú2TW+Ó¥çWb¬ÔZöÚáß06 n^r%3'ØÿÔuìO«Ò¹„‡ëñFß Øœ A1›ÑðæcØyôŽý23æ`6ŠZû¦àzïH6·¢´‚ÖÚ¡Ëì“PÂÍ‚Zxl÷¤@Ìÿ¢€³wô¢—ˆdHŽg7v'AtùM¾aÒ+T¼¸—ÿÃ@‡íßÞ*·“Æò=LÆ”ÂÊ•Zd—îAZ£Lº›sÀÏ2Z÷êÀ¼›Ìê2Q£vØöÎW¥ót‚’1·6 j8Æ´ãb:yù<ªVV%ú'@éh—ä#J—ßR`ánþT5åwråz|*šÖ{瀟û(êuã¬ÄGð7¼YŽëQ´zš¦ÜÅW:÷¹qƒFlT˜%UM‹ÂÇݸU¯ù)Q‚ìæc/h›æßlCÀ{]+8Ž çÊ–Lï92,ÆÎ™õ^ !zÛó Noóà¢Àߣ¿IG>_@ŠçÃD j«p…ì}½–ª[uà$1ªT;*Í{™o1[æ0–M¸– ù©A éþ»e,LcUAàÙD˜ò;6›p‹_YÒÂ#hé¶Žd›á›"l´ºÚp}û/ëêis&¡¹át쎽¹‚P#k­EýÜ Å<ûmKà«Â]ÏÀ¾ïWÈŒMñÑVsLºr‹ë.:HcŸ<à§ü–bÙçòáã¾@îšî{²h¾=NΣ†ïjHmí |™‘‡-|iq(9ñ~X#EL…ðçá#ü‹X†âºí“Y‹ë{1Šû¯G¹¹u?;ºa4W­ùç¯P¡rwþáωã8û2ø¶»‡¤Èæ¬n÷Ã\щëÜ`…3ÍIlèI8UÞèÃTý’x/¾áɲkž»ª`ýàbj7%œ/;œ_Ô?µb§ú+þ™´ex.®jßÔ x MÙˆ£kä`þ?Ò§wŒè ;Ó›v2Ô@¬“¤Ë]ÒÈÔxòÂÝŠùGþ‰©çQ®¿ó”!Ø2BÝh`šþí ®““S¦”ß.‹’^túѨ8ã)êmc|“Þ€OËHü{rï1§Y½ •ÎL¥UE"(¹„‚ç 7ËýRe/g£ÜW1ä%(Ã܉àÓ~¬÷^ù[›±”èàö«Y¨ºX€Ó}¼­ëý'eA¿µŒüóò%Ûwj—‡Ë‚M'Þ€_§ýR:“ ¾È™xÒ9%‘°¤E›mgkù¼;ã©ßÓ\p ä&ÌÅR‹ó`:8—ûóKŸšï~ û×) ÿ7°îýi|öèHO¾ˆÖ߃y#f7ù<™UóöVÀòo –`áÐKT ‚ƒ÷® °ådŒÜŸ…’±UDñõ[¬x–UßGðÏ’®‚ó£ÔP&Aƒ9?Xâ6¸*AâB7æ24šzŸ>ôäþõåu±!E=óö¾ŸK·˜E|CU©®S=s ›–A,Þþ·-ÊßvÖ“sk40‹]b2Ñj˜ZâK¥ç¦±›í´±µÎ·¡E÷p^€ÑŽ4#t‰}û¼=6µ&Ú©…À×øwª!C˜ró6øª¸®wybŽD:;ë| TÚf£oí<¯Ãrº\ØËÚ”M›L'ûnÇÿz¶{#ƒémY7ÝÿìŸØ±ÎŸ!íç|XæiL%ÆÎejYñRƒ•Øpö¼Ã²§:´êHLóaš¯°ïìÎU•¡Ö% ’pw}:¨ êÂèê•tlÃdúö߯«Ï‹#¸Cy(¿ü<Õü:›%úSü83‹ºÊ&c~jPº'ƒÓ°ÿ[œ`«¡™’Âô›é9üµ×‰]î:Ȳ–bhÉ=X#ÙÎÑœ†?üÒAzéQ(Q~Jß“t”0ŵªO¨ŠÇjü“#IE¾uãç‰×à^ÝZÒœN¯Z 3ë÷¶pâŽ?=¨¥ÏÆGÞ†ãöñlñ¿k#üGFãÌ€g\¸äVÜ©ôVÞ­‹ëÒXÝ0šÍÞï óGÇ/Éúd}ЈœÛ‡šýY@ÆÇâ^¬á¶vè³5[ßpç?uáyóìt $éºÔts |'éì\œ" Jü™nÂ+Y¼â…›µÂý?—Ë6r;y6C@8J’UqQȶ ”c´PþÅ.!£ ÜdÚ%ITbr„+^81Eo>ÅF üò¶—Kï1åa›lS£ŠoÎá`‚.y® ¿\¿ÀÕçJ8qÅeÚMfËOg.ó]À|’x«øòýß×ÀÅ5ÇFòßÄ–(’yW›y_‘×ÉݰÊïÏøÇ}¸çBM=NbYƒ,ú7>Á?ùß ñÜW´üp ÷<1Auhýª'è øé\éùÑ,îú8¶è¤œl3¤§÷,dó £ï›F ]×›<ñƒµ}°m Òtމé^‡€ m Ó¶&} åª:ÎÁ‚/•'°?Ù98ÉÄš-ùÛ™§¦±)ä_¸üjŽo/cã¹ë˰éõ;<{%þ¤YÐ=Ùz¬S~ZßÃõƒèÿî×ñÁ ³'‹R±ÞÔKê+œý=L8kGç¬`Ÿ»R£[ü^6OH™Ó7æÀ?œi^±.piÑ(ÞêÈfL>üF×ß„øYù¸Â' BŒ» "Q†MÓÈÃÅÛïbR”6óâ$Ø™˜Ã¼èƒ#ö¯|s¸ …”O›è¶¼/:9³˜Ï±ñtÿp.^ÿW îοÍ{tè™:ó ¿è-Ô0W¸®y6û•Cû 1ûnE—O-Ä…UȆE¨¸?™= Ô¦ú~¸²”î¶IæI’ŒÂ$zùÊ=Îcû%|ÁÕÂêi/‰Æ¿¯\Ýʧø¦î$¾¸Ây 6ß‚PÜk×§<…šcX]})÷åXE´ãÙz3ø¯¿úv¹®ò•®ï\lÆ$(ZïAJÏøÁÓFwX-ƒJ1“¡me7éø[rÙN˜Uzá_>HNä ÑÛ²Â46ó[mÙ˜upú©X¹A}VúFmyüµD´Ì‰vµ+ÕX0“}]†ëoQÓð q7šÉZÉÓ¤³Ïš%6?$¯Ö&«[añþsœ¸v%¨-C2{ÓáºØžƒÐ":ãŸ!wNPßZZ2U÷Ià5· Šnê°ØÝG1`·]žhAÛ¥ÎA‘ùqXQœ‡[ßjz&ê­óU’ô¤­$û³&B6ÿ%M=ZTûFZ$›³A<šÚ- þç„¡ûû'\–ÓÅ;µŽ6F¿„«eÜØÍdç:ó‘ø?÷s395ù ¹ ¬yÂf¿Ä\±h.ïü öëC5÷NW‘4üŘ[ßɚ¥Ô÷oìë BÅÝ3 `o2ïJïýv>,PFÇU×bÀ^U<ôÐ>ÿ´ÆªèHRdS*Ó—â;9gXüèü7‹Ùä™)›“Õ=üÒ{³iܲÞY Ó~Dƒ]¨5ÔÒc »»Aa^ïN¦ü"“çâõ ³ýsx ’ ûëú0ÈD†Mõ eK9ລøz{frQÇaØëøq<Ãí­·!*è4Ä´ÿ7Ëßñǰ rÔûK»jVnW8µõzL{‘:êßî‚t¯ X÷õh‰Odf_8,¬¬Èœ.@¯ÕÆÂ›éÿ{þUø}%ޭجMH?WâÓµðË|1viÂÄ/+ào_ Tì³%ò" !öøgüTgªÆ—ACç)È+œƒ+£›xûK ñGŠËOR75>†\6¦š¿@rŽ¿rAúãx·ð9ˆZe.ÖÄwG4Ô¶ïãæ}®„ÉS"¨?€7Æ,ƒN•ËÅÌC“P0ÞÿÌZÅ+R-C©TO¢~Ú†¾1ë@ÿ}®\õ¯ÇP³R™mQ°çþn±'˾ÝáÊLÖ@Üø?/6‰ÌZü&èE“_£qã]*Zýpûî^ž¨úx¢ wŸ‹êO€Ï]ÞTÍÇuºáÂâ$œûUJ]gS‘WQŠœEÙUq˜xÂ’–8ΡK†ô¨íD{ºqÁFîRz Ôo¹-5ü«ÏXÞ? œës;ÖáÙS_øðGkœ]1g~ ÷ ?Bðm…a-|”K; §ÏÎl8 Þzd$‰µ«†]I3¨og#I9ÿ…ûö®¼80mû!xÜ…Ê¡EðºHfËÒ‹yÂTöÈ10˜¿eår?ËAmÐ8ºÈæýßê×a‡ÂqtJM«æAç 4lP˜Žåe± çø &7pÙ;y¸òVŒÖJœã¥ë¨çöÓ´QÙZLé“e—!-¹%Ã@ýãkbUÙ€îî¯x ¡N¨‰0ãG4ƶË?K×”šƒGÁZÏtn…°Æö“\Ѐ_®|ƒm÷Ó­«Oଊ©´4H‰Š ˜°d‘CÔà3¥ÕÛhj ,Ú9„*N²ÿú¦kÇÊBƒ—»jœN£i¾tfFzÂÖÂÿz¡Aºå.¿Ïþ<û3þ3:³:Xl¨¨‹f«S°üá…ûØnAØ«a[¶cŸAœâ4 ÷: ¡–í$hÉnª}Ú齃0!ùÜ.¿Jªo£Ä~OÏôyZüïV'qô¢PS.gÂñ‹Ø‹×w˜gÔc”×H[·“Ø~z/m´‰DÃ\cfw׌žU*A…Åþt|›;4³˜¯÷ïõ>˜o*Ç3«¹‡¹@Í ÈÎE¯èpðö܉§î5sG“ö‘¥Mó¨mü´ÿàC­r#È4¡wðfê|vÀM–IkÎcýÆZ°¼¦î9ˆËµ{¸¬ØPXq6ë³»Àæ÷æ‚Åe ¾®ÙLì§Ç©ºÝ#üߨ–mÏy_&Ï€ŠFe:yE/1¢ífTwþSˆÞYijþpûìB§¬ûÇß+¼”}އs+ËÎ †_‡8‹ñéX?7iŽeÇðÓnÿ>ykõ‰çj3~»‚Ï|”xÁcJ d,\vÇs­k@&”“fweðjÑEú±v:í½Æ£ÃkBè Qùò¦NÇ>˲«è÷K†ÌòîDúìÅ/»È–ì¯ì„¡®^¾F¿½ºI„=OéÂ"¿™ô­ÕVbµºËºq«&PûfYtÐÚŽ·ôÁNáa¿ý8q„ÿï{¸×…ÁK8[Ž‚Át–8Så‚tœze…ü¸14g¸þi®½ ŽM•p0Z“™·´ï´Ñ£­±’Ôù¦&ΔJÄÃeR$o\6Në¨$©v3ÙÇ{n4ùÚ8˜>‡åþñƒˆÉûñîÌBPcAå¢lÉleZÝ8‹Z§WZÜIJ¬çª0§îÀ’gÞÀûVs¹ÿf\¸x]Ù" çÙ´C¯ðÌÖ lÏÙdSÛÇ9ˆŠáþr={çˆþ»yJ‡49‹ªÆ“ŠZ÷ä1]²¬®F¡‡þøòÝ^•}}a¶ãt™Ã9³p‰ô2åG6j-Ø)g©‡\æIŸ¦}zÊLã6eqR‘ì¼Ê4fÿkX_§5¨æ ,ÿü&8H'.^C£W^â ¶jï¾il‘!{m_ÀR`¶d&¬±Ø‚Þi&ôÛK=üCl˜¯±8ûl» …¤¢EŸ#íî9Ïäù¿@ÌyÓ; 2þO!æBÍ?{†»½0šfíû‡"Ý)Z¯ÆÈ®(R™4˜]dÚ ™ÖÎ׸¢"xÄþ_iîÇæÃ‹ ¦B—:øžƒå'–@ŽÞ:òtö^{BÍú¸¢ñ·ÁHÐŒ~ô6ÀÐÑó^jB¡LY‡ÁṠ¹mÓŪ~ÌRó£­ÊëiR+Ô¼ÑeKúx$€Õb¢½³q4{¼ú6g¸î0LßítrÞž‰ÐY{.Ú°Áªûè—)Šæ›éõÜŸ¼¥"10ïã*òH³óåžÃ©(Kê‘f@æÞ­Ø¥TÌj Ö³Y°"åœ]õçB2º/×GƒüWøBžeÜ©ã‡õ¾1v6 0Á²ë¤|ò9rúdòHü;< Èé Zàº'ÚTÁo/öÕÎaêÝÌ-,éeAê*á» 3`Ô¸z¸1ìëî?Ä¥ÓàÚ¼9ÿ¨/«éûþOó<Ò ʘ¹î^;e Š„ ‘1‘Œ‘h–&š4(%Jš‡»×&¡™)cÆBÉXèçý}ž_Ÿ?ÎsŸ{ïiŸój­õZ¯uÏ:ëðŒòxÜ'J'ãV{#Ðví윪‹nr³ø¡‘ndÛ­ yn5¨µ÷ «Ør¦ÿqQmÞÁûÂS ösúøœ~sƒÌ»û §ð²·]0O›ÊV†ð¿­x¿\(õ«²‹8E?©­NÞN½È$.fg‹Ã`Â)n+ÇŸ~ ‹ŒËÈ,-Œ”‘¢“€•á¶4iå|þ}\–Qço>ªÓGí¾¸gXÔ€þí>•—”Ððkú6@v‘n[|X°ëÑL°þ˜†~GÝaG{3„h÷ Þe$a˜¦}pÌ„èýÓiÞègﻣ“KðÄD ñ˜Sµ2í›Ï±õÒG°éû$ô·Ê,OE>ªÉT8éOµÀÀñÆï(ƒå'Çá¿õþon´n‘'ûy|¼÷ +ÂÙ'½ðëµ9n‰¥ˆý ìÞÃiÝuãáÃŒgܲƒ¨nˆÀ·QeøWc;Y^¯‚žu¸þêe+Î^µ©À Y;@G1H˜ N…à½ívðGË g¬ˆú0`i«áå€E0{‹>~W\B¤ö™¢Û’',  WP»ðkÝŸ®xp×N* ,ä€)ÏÚjK/¦e‚«c({´pß'úº¦\†œ†Lâ·¦Rbä0ÂP‡ …+ÊɃ¿YÐB;ª¸K‘Ç: ¤êòAê–> “2§ÅEiɳÊ«Àgâ‰£Ñ áy 7|ý§íéD•±n4Kê%æîœ@šÏ?†Ë.àÖH ºo·tÀÆ"Ú¶X4Ù‚‹Ô¯Åçú¹l鹨Z¿ •·:Ñ¿æ´9r ý¡ú÷¼ÁÌ™àà>•^™2Àÿ6ÇAÚš@ðüØo¦ˆ€{«š:ôBŒÎw°Ñˆ‚›Á½ý.¼%[·lÆ[q_Ð'û~Ý@ìuå#4Rx‡|01š`É—D¥É¡lÌë9ÐD†y¶Ü„æ—ØÂ'W˜Ÿ2M4ž‚‡å©Æ…¼.ÙƒÇÖÍ¡ÉB_zðépþò»#ÿûÍBxuÖ]ø–˜Ë æFÓÆ&wÞqî7¼JßÀͦÖÔüµ c½hYµ,]¾¿™•Osá9;>•¿·8™€TNÒÃúò›ûȺ°>ãò>ýïôôšr&¸ËE†:FSçùÉþ¯xîKzPEŒS±ù.tÊq7ôw´¢%ê;ùïî,¨ËVÍÿŽù1N¨ =‘*ÿt§F>ÆpÂf&}:;e^ØP/£˜c»WæØ¬!˜rá0v™Ó½™ù(*5…ñuÀ¯»yй0ÅkmÛ‰¿3»`U£=însÏE Ì}j$þÜô „ë£)¼h®çÃêþmÂ_zK¸®ÔLšì}ýÅ^ e‹qÙ§Åõó‹éDõ1({÷ .VÆmœFÓ«‹æ`QŸ=xpNé¦ü;çJ|H/2±“_?wNÃ<ÏxAŒUèþu6~8sÜ9´.Çú–.Yï«ìî# Pÿ9–ŸðøJêÏV Í[ÐïÓ*ˆP¼6‹­ƒÔêy—\ŽìætÜT÷Sîuãòã£`ÌvqØ–L–¹íQ8}h'|z† dh“r$V÷ß…¹Þžð¹²ùÆ’G±/leÿCòiu›ÓЩ‚Ÿ°&0—H1Ïå øÎ.ôúíÐ{X ÊѪ¡ ô¿¹Ðk„*Tù,2³]›÷]ÅcÑ£7Ȩ”ÿœò˜(z Cmôxnàö- íþ—*R³ÃrõŸËׂ٪{¦Ð„CZ\zäm–$R…ž`ÍH¬"Wà?‹È‰þ¼ë³yz³cfá{?NŦ;uvCÍhÇ·ùÙàÌ›îÄ£’Ó5<ªsš»ùþÀáÁÇè–¾+h¼1€·ˆ^ÁmWqÛáH®±W…e„ÓùS{‰›x"þp½,zo-}ÓC¤F¦á·÷þpØæ(txÆ“w¿¹šãiˆ:‘„Æ—%ì_® pÅk¸U Qkv"¦ùEÀè7s‰Š£<Òx cZN`KÚ~ü>mýûU-7l'I ç¿$"ëñéÒ 0¤í$ªˆF x N –©ã.ÊÁ8:I§_ù„Öó÷°¥­Ž(²B‹ß›4еÖâûÉNØwO€ê/cÉLšÓ€½@”—|„ o%ñî—tò˳v¥7ÛµˆˆPU;†[LeðZ/ÜË‘¤{>^, !YÞP|u,ÖègÏ—¢å®-‚2jØÖ½Z8XxîÌ™e™vp~›8é⋃G¥ðÿ”gI¬ñu¼[êuñÕÖÀÖgVt™ás¨*…_›¦“ì£OqïÞ@~‘µ/œNJ&ÖàÙ¯³=ã¿ ;ȃ±ŽÔhßSÌw§%©´ÿˆ ·òì«ëgŠ?S«ÐøêdÜyÆ äÇiÓ-cÛa[Ìüãz¿ÄµdÂD[™ÔÃxî¢Qˆ3•”O‰6w<}O0yë5lrn&ý‹BêömЋ¥ 0(k+5?®ÄÛÅpϲïDô^¸Ð@"ãßd‚åë@XÙyRÞ6Þöü[:v…;¶â4ûw ü1ý-9ãìÈDDøÕtÒ9b XHíÆŸ«0(¥<§`z¥<äè³îo`¿6~šiR³ºa”§1KîVÃ{—VAð²|x Ú Uï,¨íã×uËT,ùdCy¸±?æ(Z°7izü&ðÒxŽKšFaõÄ·°çü"#©F߫ȴÙýoSUˆ_ól8T¬Âv=ÀFöƒ“ãóQ26ÕÂÒ¡$1Œ¾®¿rªƒùމल„–/JÀÕN°“¿§Åùìê2 R"Tâ}!T®XÌÛ~Z Å|=pÄÃðÛ&‰MP‘§ý6§ð•ù'6[’cŽÈ+¤1ó±Ñí™Û9—®ºÓOZZtß‘ðì¤ùY2ÍÈIì•qafÓvÑeÆ÷Q²SŒ#„⨘˜ÿú¯ÙQ›XœÕŒ cªÁ[q·åUØ{ ®Åà›§sa¡ã{|%¶„Ój6ãì0,i±ÇqÉË€EêÒoäø€ýÿì†Gf-#+»‚I†½2—>ZíµâtÈwu²Ó*?Hl’‹x!(™lþÓÓkaF¢[PªŒ»{7É“ü˜ï \rUx’òÆ^‡)|*Èúõ`£ê6f“5C'Q¹bcZ,=ˆë“Íäø¹£‚°G£p|ÈÜ1^’¼~ü|†gSøscˆø˜Žë"Ñ¡Çcˆ£J0É[ŠCg¨ ‡‡`äŸ·Û 0ÀY™§ì{‚ .}¬\ÚßZ@¶ÝHX},ø ÜÝRÖSñ+d¡sñy vh†Ü1ò~P0܈½ÞÅ+pÿŒž–cHËjЦSB8¥Ö‚G&º€…V%fi-á «ÓÀîó)b%{‹LW±â)uð3¡53uµCà^õ/—Jœ!÷~á.©åt‰ˆ,L?9D-Ÿ’á 'sI‡@°Ÿ‘-PëüÍéú|ôï…¨Þf,ˆ}WŒ¦K£ëéW0!ш³Æ?¸ÿ¶¾ò[äÇÖrÁ¢#Á?õý/+×¼ôO.„îQç&‰Ø»9â6®ƒ7Úïÿ›÷LQ*áõdÌ™PÏF¯¶Çtm=²Ó@לE¥LìðšÜ"Ì0 !ÏõmxoóœØšŽõ­©ÔýãpÐÍÜ]Laõ‹?Äm¯,=æ`t·8…‘aôÊooÁØtW páG;ÂD Žo{·Ò—?çÐZ¢ËjJÙ·ï X«¯ˆ1>î¶mF캋ŸZ’àÎöÃ,.±D±Ák·@Ú«¹tÛ½Rôi½ÏúSÉ¥/­˜ºj9x¹‹âÊÙÑ4¼G‹KlÞñcÜykϱüi».²;­Èœ‚Bi¾… ߢ„øeÓHÞ<£¥ö’‚%š³ƒxós*ôL‘ç朄»maOf”kf1+†ÓËÏ{¡H‚¼\ ºíðdáb˜U †É‡ÓAä^Î*Uææ’ò;/ž CX4ž·ìQÀ[þ/Ùø÷Cøþ¡«ƒLiLØ5‡h‚Þ%|à8r®– S-CÈ­øUɯ<œÉì÷JA¤VË|}v…øÐê$ã€qc)^==B⯲9[rHÌйÇîb×ð2¶éÛ&ÁF‚÷¿€ jxaê½þ»²Yl©²£œá v0%—¡Ê3@¢3 ®Õä_ýÁÉNF¾…»€ÒmZ}ÉCǺÃé#$ílŒ`ÿ5'|8K¹ÜŒBKWèçñS'ñ¦QEð[2ò~ƒÝ£ ²KI—_m‹†«o+ û¦ >“ÈˇÐì‡溴"¸»‹ƒö)1ªñô8<>?œæ…×àì÷åäÎØ|ñ êdáí³óäIº6õ~ðú2æAÚ½><º† bž€Qj,›¤1Jî.¤wÎÕ„ŸDðç• ôÇJ[¸v§ –>ÑÆ‡—Ø"2i@ÿ$ýóãk@]ã+µ7¯W!èù}6íÔ;üû­‡¦"¯×ßA/Ðkäo’v·LpÒûc”;5è ÁEØÛ+Q«} ®Ægl9óÒiù*WÈì¿ ‹O9°µ§LɇeK1qîfôô6^Ü£ÊçÄÈ3Þ“Oôeب)à½I?)|îözÑ#M¹ÿé°Õw%ÓpÛÓ}ƒªžsD×ú2"͉¹7æ^-…]e×É$ kx÷Ò…“{Ä>g4a—ßPt=ü…ü77ZaX )õvàyÙÛÂZ žr‹œ—K‚¨…ÿŸ3e($ß­¯5ÇñÂÑÆ:—vŒV°¹óÓØÉ*¼­ÀS—ù¡ã½þn+ àTêöøÔh\ÃElض¡46Å F3ç7Õ‡C¸íúMߎ~h»T?Ž ­'+üÏZºß£ßZw³ÿ£?åžÇˆmVÄÛxßåYôß{8ò_9¿Ä²u;ÁvãoÕ•Ž·/žÀ΢s` ÖE¶ýQ¡ ŒøÜOÅäèüt¸´B‘Ƈ— ã\™\ºŠ$öÏ·©O+á“t*”ÍÉ„…1—þd³ÃobÖ&}*Ura ÿ•—[UÐCù™þ+.Ý€'= ‡'(Ò¶%ßP\~ž²€§ôxŸgA-3ˆŒÆ’1gÁ½ã+öŠ/Ñ0œ+ì®k¹/Èw£üÉ-PêÒ†§úw@ yÿïÿ‡ i\ /ÛÀSÔ”Š(XÐn¯;BÅÑWˆ±¦ÏZh$´¿z1{(Å;³éiz‡øÏÀýaÆpp‡»vÚŒw4y³Å[gm› /ú1|,׫Â{~œMžsÕ÷…‘›S.C¡› ,mˆ§­ÀÐsh”±ä‘·{8Ò±ÿ4|ýÿîÿ«?Žsµ¤aPQ=þf*:jž&sÄŽâ0Ç2öÝ– ·nH!£³`œœ Ø|>Á&çÿÓ%iJüWÙ ´m•²;çPcÆ6óÙD°ó§`Æ"j”lÅOFˆóª Ò˜­B37Ä ‚n†àôi*D¾3Ëm¢NÌp®É!"ûbŽSS€ý?Thì=>û•ëÎA2½U±ÄZ™ºßº ¬AÁ¨ q½à­cº`á-YÁáÆË¤ÖÀާéÂàºjH×­Añ†ÿŽçë¼ Æ^–$˜ßû§I*ëÊ'YàÛÐ¥x{‡4 V™Š…Ï’ìÿk£^_/É~hL¥ëL³!ñG"ïË“ÆÜºr¦xboš© ¨.–_xÿþ¯C¬™èˆ%×ï :Pn¶_L¹á¹G`ÊóØgöáÃUa˜³"·ŸAÛn Búƒèï£È·^Ñ%ÿÍ|V¯ø«ûla‚—=•=IÛTPºÜ‰íßÑEvYÓõ%®xGñ8ê/c¼ÐÕƒß?æÎu—Y°±YúÜrqv¤_!ƒõ¯àÑŸëàÑ h;r ”Â)UW¾Õy®^¥ï§ÖSÅ©”–:í- ¢~%†<ìÝVŒ^žCÌZ«ð—¤H”©ñl[P!–>JrøVT*?„+uîÍû^ø˜­âÛþ ;0»+l±âœ"mŒ…•þ#ñÊæ 1*`sž¼ƒY›Ôo²YÔU[Ø7W^Ýÿeñï`a.²cËàÒü²qÎ\¼â7–. e°QY/&Œ‚s›qɤõ¨%±Åå¬àÆi3rü¶ µÎ3v‹óòá8ô¼.˜¯~ú—/“}KÐm[-— š‹¾àËazÀþx‘!¾Y𽩗Ïc`r5J?`â–°rµOÌ!‡fÐÿfZ§î˜Fˇ ðÿ±i¨b€_f3öô!äýÐ¥¾ñrüÇÇ/$;î48=V£:WôyŒÒgœ£ªÄ¾4Å1ó…-¬ûH8q|JXI¤]¹uS{´!jÞ*~#ß«=ˆî™saPh-Dý>†Ïõï k+JàgŽ3.qǵ}…°_t+V,’À‡¯8(? óÝäI/dëß|À ®º¨å]KÆçWÀ.÷äòí\¶iúºnE‘ï†ÒÅ^²ôà‰x–¿É’ºÊø+©áéÔtÚÏäòÅ6P©ÂÓÏ&â”ÓFØ-¾™ööœ$'~ŠÐ Ãÿõ?Úª¢ÿZpþ%…»ùXþÚN.û—/Ï¡ï/ýÓ&ÙP»IæõʃÓs>-)ì_ýwOØvöÖ$zïÒD̯±æRª|èÄ4u< ¦¾³©çž nÜ&€Ÿ/NcøDÐ9º^lØ¡€ñÖ‹u§,wÐç7ÅAÉn VU.ao~eï½1ã8ùoßÇÚ³°Ã°iŸ;O²ãS ÎÀ]÷ÜçcÊ÷2£[gÁ’Æhèj<«G¢¶‚?\<™¶Õæò!2YÞCGþD4 ¾;…;…WÁm[ ¬|÷Wøðž)¶öß{ážàÀ»À|xѤxÝø‡ˆ…bk´ ý`ÎÉÏ~­u&_B4ù—W°Y´Æ* &¿¯…¿8ܼR…ו)qO×pa®iƒl{s~m{Ì绞Ãyu[ªZy‡‰î™Bg«^ÂE©UtÊ„]7…9Ä0¤‡/óÇGËm°u‡¿½ô™2í'ìÞ÷¦5 Þal5Á’ìxN};}| Ï×.ÅÒýAj6Œ¾ë^õ&^&×2«aÜNX7Yƒ®û¸•„˜ã—ûä¨öhP%I—Yl°ÿiã!m¼7†õf¹å*üÙ*m*3Rðϳ#xQé«)Ñlß.2þÞLf}»‰‹ËÀíêÔVv-ww¥Í6º¸iã:‡ÃqÔ&_ÇI¬¢Õá"è,/ëfа^Cúw–[¡0ÄÎÜJšòû—g]ʘåKÊ?—çËv ±¹1†F(6Âæ{ûᎸ N×xO*ÿšá²cz}Ó쵟 óœÔ ôc*î®FýªÑy"ÓP®§ï¾¥"åÞ°óÊßš¦5ǘ¹9Ÿø/>k仄™Ë»Nò½XqÀþF¿S¦¥`诤ôåcÐïÀc‘4JЀæÛXúf-ÿè†}ãª`}C4’¯Ø‡y±ðêÒRýÅÎ.àJ ¬¹ût/ó{ï4xI¥ÙºQ\ô¬–©Q½³‹¨ÿµµ\(ÅÙ”®³85V›×g—a©Þ ë@eO∟`äË+?O¥îutBÍVš6S™WIîÀsYÒôþ¶4´¸Ê}¬†pÞÌâ&®Âü͹ô¸bjò{<$$‚ŒÝp~l,þxdÌ£ÕJÈÏ«èiáü]ÚT~Z%-¿Fn½½jù–˜0ÆjÀþ¥n×È[¥|cR}ï%)¼Y‚Í¿P%<¸ò(m§&tïêDZbÊ»‚ר­´ñ‰,ÿëÿ™é-ÿN4dÖñ“"î|h½Jg árM(òU‚N;3š/¸iÏ-•&ÏÞw¸|‰×i1äZ6›øá-§¸tSrÈeQ@³˜mfÀÇjõ€FÏ>NDÒ'{e„o‡jÓÝ£ïñ3úaÖ}1^¸]Râ3T+`™v¶?“¥×»Ly…¦¿#‹2›ñÈã054¾ÝwŠ\U᥹Tªu&?Ü–Ž‘Ú {§}ÆÊòßžÚ—ì½ÆfPn·¥óÏÙÒ„=PýX aèSô˜ ÙÃwáÔsrðnÖ`&o ‡;¢`RýI>›Èw¾éÂÔ(ªñA/wÿæHOS Î/î"T~ù¯ÇY©ÂÝŒ‚i†Á¤õ±ÂÓŠßmQÂÉ~ æÞ½ÿÅ®¬iÆÝ$>¼' ƒ|’UF§Øº’2V°ýl·ái—ÂR¿@z­ç/,üÖîÐíë{o'îÜæMƒjQûÕY¦™yyàú÷o qj0O"R¢`¯ƒh¼în³H€çÏ á+O œÀšæuXï’o¶ˆMí"¦i|R]+;²¯è>††1ýÙqôüázÝ”Ÿv.ïnA™€hpV⚃ƒÑ=¤‘©n˜G® _Àã©2ìV®ÏTç³u“IÚéDJé$V¬(ÁØÀ¾î}.këaŽ#Ž´> §!ƒ˜»U³“:S¡£g,4iç.n1V ýü ŽÈ°â÷w9ãk.¼ÖT@^}|‹®]ètÎ>MÄ}jRtEK±¹²¬ér·Žý{ûÐdhê•…ª‹3á¦jH+ç¢jr4n-x ëf‘ýÇËgŒ¸¶Ì :|”Èkÿ‚úGᔽ»?{cð™×2>J”×[ èô=pî[Yw5:—ÜzòO΋s£³ÙäßþÐýJK[j`^÷ÁmK)Z9±›­Ö8Ç«ºP·ý‹^>˜–Út ˜ék¿Þܺr°bÒ)Ý%hl8ŸOZŸDú ÓùæÒ°N`A}÷ïègˆáB!<[­$ðµ¬Gw2wW|[‡É£ŽrGȉį$ã¹üó†Ý¾³çâß«4F°½ø¾ù.SŠþÅ"¦|§êI°½Z‹w„[ѹsÄhÓ8ºN±‚ÖÀÕ•xD=ŸuBú/{Й‚Î혟‚˲•ðÔ81zyÿ xh1¿_XÂ? •¬Ëžz)>FO×#Xüló]Icóo‡"¡½l"SnŒvR£h€Þ{aõL º§_sú&5¾4ip‹„?,òò*õ™€ŽÊdž»Ù¦Ø^díµŠXÖׂ÷5÷â… ¸0t1÷©ã¿¾lá³Ñ`²ÕˆK{Û@xUŒÉráË<¡Cíî•”ˆÿåê÷ˆ˜X JŠóyFsyû¬TXÕlûÏï^âFýTÚØ`Œ³fn€ß V|³é#(xSIAs[“ÊñYÔ¼'Üð…¢»gxOç|`"©ÝóãxÜe-„™­Ÿ5›¹Zï$¸M@ÏÅX.Q² ÃJe„/ÝoàÑ›ƒø“Óéi#+vFQ =ÎÒ¥ÓæqÏÍ(,ÍSW¦ §µ(`R—Ú!Bwö®†äò½À¾M¥ÓôPrÎSxªlÄׇτ¡ÁC¹F¬U©¬R†KŸ.Ee©¿Â¸?Þ$íY]»o$ ²}Æ’§þ« wªô,)Ü AÓ†¡øw:Á¸Z9œ›Týãoq¾Á¦ ø²[ðÚ¬y-iQ{È3œ“%£YÁš½8êu6,vãí/ën•Í¥¶O¦G…[@°ªEüË åh=YœßÝÿ¸zÿªÉv®S΀ÅÕ™8|é(´·þJrÆXqÕhœÿâëR¾ è*J§uãnõõ¨lg).ÚˆĽ{_°¤^//0ßVn¨yw¢Tp™þÎtKÙ<&]†ÞÓ®Ãyù2x6x3—úYꯠðë<`®Î}˪`É4Xj¨E›>Mà?©O)°tC:¿²¢ã¿ûÀ`uCªx᯽…‚KßY¡UqtŸ Óʦ3©,kzù•íå9‚6+¢˜uš¾Tâª%fÔuìEÎT'ñus‹ .ÿ=ÑsKxaÚ# óóܾá|ôâ¨cà󮊇ùl¦âkgÀœîZœ/þ 7©Â–_°ðM=—åkÊÄæº¿ÝÌ{bÛê,Ö_~½~ž†u ®Q#Êí?ƒ¯}3îQ7€Í¯NâŒwêhùe$Ý0ƒúK*ñ¿ ä&úú¯5/šïáÌ×9à¹sy©‰Ñ€ý×?Eâdà2ÃÐ5ò³]åž2\àW‰ ƒÕA¬ i„¿˨Ó×qCd··@åØ,X5û.ޏ&Ê?v`Yµv4T_,¹€Ã‚ÄÂðýÞ"ü¡ÑŒwòG“µÁ½ìuv;.Z¹|ÂÏQ ørl:ÖR¶ñr9øf?¼Ñ„…6ïÀñ÷õþ.´:yL8J-µyxÍaÜê‘‹®W^ %jè¡FgTa¢Ó¿ºåZ5„š+Ἤèñ%ü9½—ø/_=/°¡ùïà·¿•‰AÿÏ-ºŽÊ+fCWä@ü_[“l‹}I_¿@õ“Û0s}º~"}³‘#÷}„Í'6bÎêbtw •y(>þ,~—Å®ÁÝwŸpà sQ¨>ž~¸\“Ð)M—ýñÀßÚÉ&+Þ¦WVMFf0çÅYäÀw®.úQ˜Õ(FÇ® #) u8Ñ:Û› ËŸáÐ%oIæ¨lWîÁ>”µTx{b$+ž]ét|ñ*–©m…Þd`ÌmíºôÂf'ê3:ŽßÌ'¾éb½GK /³-&Áé($6:ô÷ÌññÈ ö÷¡™±¸]°îw.ZkFçÜ XX6XP;ÌXè|[5Þ ŠX•PgÁøã}U0}´ßn2ÄöVbV},=²†&[ ä?ñí`íPLæŒi@Íþût¢Çf¾]j]T+F\^w£iÔ­Ûä]è=2RãX_^A{ËDh„‰'ä”Xâõ‹Ñô»Ö*>]d÷ÙïÄ”?¹ñ¶Í:¼Y³l=‰‹þÙÆ„Pjp7›Îš^ÁŽD]$V–óhô#´½™Êÿœù ãÞëáè<ê?þŽ«ª£‚Ç–üÝõ28¼ØÞˆ‡IMCè™Æ¼a P#GSþB#îÿúÀ΀ õÿTU¥üJº^¦J¯¬6ä^MÞØl߆áFÁð%| 4iPɦ#5S/èPßA‘lwcå@ÿF«)N8‚³^ ¦µZÒlÿ‰Z–½ j=±®²VoYCŠ-S{;^¶Þ‘_]«§ÛõÑ4­^0òEö[IÄGT‚ì•\–>U‡ïš{ŒÞxaˆ¥ŽŠT¹_”Æm?ˆS ©è£@å°‰gÔNF¬/äxÍ9,Øù–‰Æðü¿J´££–qA“ÝîôT~º{͆WÃy ¾è\(?*ñ¢.Îf;÷?µ„Lö0–Ó'Y{«,¨}úwnÖi¶“®¸œ»5œ—`cõ Ú½ºÓvæ?]½MÕ "&VàSÛ¸ùâ/¬Ün3Pÿ ož >(‹#kè3·&²·z~r7uÊ÷Œ>rÙµ˜&ç c .à¼Øç¨Ú®oF<&vy ní»ÚçCq;éµ^à¢YŒÂ›Ã™MR0Sº“¾1WЮú*^v-"í¦ó©·Ñx<{p§pLÀQáégÿt­b¿‘"Ü¡pVðÂã‰TÒàÁ#޲ø‘Î8e°µ BÙÃ+øÆòsÂOG¢1µ5š­gb´sñîtµ~¸µA“Æ"~êÓ,²je;èŽ“Õ›Üø–‹‹PïÝ)Ÿk¶Ï‘gØ›gsÙþ¼höĶOYÅ.+Fð_ÔPGø« ’ß2Øc_úûf‰pÒ…C°²E•ce)Ùš ×9UQM )5 wÇ6Ro1 W~MÂy®ÁäçI'ð¼ý3T„Ãf?=ñÍ`—RˆGsWà£ü|8ûgôØ—3½öã˜ìŸâŸëðÕM j"\Jÿ„Žàs•ñ¶m•™ÿ¯¦Y‡=ŸD¹ëÑxxýÓÃ×=“ ·í^%´YšÏêÇÄÏ%Ÿà`Ü1Üì}v(D×u'ábÀQ¾Ç‘­“ 6®`óÑ)ëjª=ÆVÏµÄæU­ È6¯&þëm|0ñü@ü§ûˆñ\_,½c…k=/áê Yˆ3ÌcŠ•Ñó±$HfÛ±eƒÀÃU„WTÙ/˜É:q|š¸xbñÖmôÏ’eø¼ï9Ì|½œ4Øß‡’ŠŸåÃ%†r›ŸvTym>Zíò¦Z^²Ïn¥8Dò4ž1v§N{Çs3)hØã–ò"Ü\¿» Ùì…Ï@½§F÷–‘=Þ4ëÝGÁÉ"à‡Õóáqî2îÜ»‘ˉŒ$/,®’®Áþ<ßa…àÔü½Ø|?$C~ûc\,š„6ë+Ðü©6hË® Чw#×sÁÝóé÷øšëß½ÙÐk¼¦OžŽÛ’@-¬2Ç4¡ÌOÌ ÞD[N¥P“yFtZ•)~·Î§¾ÛT©Ïcš*2‚N¢Bó=ÖrçÔfZñ˜´[l·ë^µ×œ®¬Šâ¶+¦£¥ð.]7 KG] L|‘Ê[Ve`Þ{7.õ6Ç­¤”ï¹çÕôöÕO¸yëRx«|œ!ËwZÒ+“–SNïÂeÍÔÑߴñšóTzüÓBª³'šÃoqû6«bŸP·nÁ×ùð|þyZ«ê#(n¤;/z`qõ¤ý3âô\\hBDM»`c](³lÕæRš›@,r¿üBoFm§ÁëYÖÜÌÖ]ÌÄ®£áëg¤\Ç›YRñÌ ©Ma­²N°Nª€}3‹"»²Ÿ®.»Éz ž.$ß:BâA%`15L®*m¶Ñ創` ‡‹kŸ †œI-±á/Û‡×}”8Èõ>)PÿSž\ž9¸æüĶãIG3-.é ¦áA¤_½—U_¯<ÎIŸk`ªÃQÜóà*~;åµY¦”g,¥š-˜¾I½È²ÂcƃxA”$\}2Dî è_¥sÁéáL(TyïÓ6Òow#øç$%úæŒòMaüÌ‚½¬ÎÃ¥úcÂ5á§ô1ør~Šä/7=ZL2£Ó}èïa‰ÂCþÁPÂ}ÚÖáþ“Œˆ)‡s×7ðï=Ý”¶‰»E'À¨GÚüKw/;6J™ŠøhÓºY9ÄâÖ:‘.~JšwÏAçУ–ûÄù˜‹Cál}¤VEó¼¢hAƒÝ>útôk<™YõBجk(È9© ÐêÄm‡ÚóSÃÇÑ,›A4È&\¬³ßsýà9@7Î~Ãs.÷üEVšÖ Цnà¿ó5gaÚþ}¤À/Î}¡ó»áG‹?þ±¹Ê"æUcŽd®Õ²'ÁI®À«Î!p#ú.«:ÂT,øƒo‹`·óC\¶«5öOÁ®5ïàÏ­yÌ;ø'¤Îvù—‡D(i°ÂÊ£ŠTóPÙëòf –aBnBÄÐX4 Jsˆ%݇Cê×ÃaíÓð"þxxµBœf <šc+åÏ’Îm^{Ô…´]¹Œ_ÏpLNp£®·Eè±½Ö˜vVË¿†í³ 'ûW:ã-U#¨úš-]L+v]fg ×â3{Àrñg²wÌ ­÷¿ù?Etæ«nl.Dsm{PòR]¬m}!ö¬*#¿gœ†GsíyvrkŒ<Ƕ+4ƒÝ¸÷g?: “·«6z :|XŹQÔfd6ªéi ªÄ&˲6¸uÉ­Ÿÿ%uÏUx¶ù2ÈTô…_v÷°âªqY~ðò€ÛgõGã'¸@þ/ôý÷·ö6ü^Ÿ;6»U‚ö¹Ýx Ç–K’±£?„JÇO¥ay`ww 7 bÓ#ÉÏ–ÂÈÖM$K¶¸Î}ôðX³ZÙÆ‡ ¼õ¤;ªÅí±Á©êPÝ;ÿ¿Ty Oˆ©ÒÞ3–ø¬AJ5 üÏÌãtF‹ -³E$'°FK+ÇÍÔêáÍÚø¸ênU¤¶‚ó »Ûˆê+s\•’c¢çòã©3 蟮³–Fmbèé$ćó™Ak úúÂFÄsíQ‡©ìþHÌr:C³ó:ðÅÐ$þºIHË[äðFöd~ù²hêÿ!ºFjÿ $⿉ÿJgÞóã)*¼ÜÄ{>fÓ]›ó¾CqÍÙ4tþ›C·DY§õ\úÎJxr¯¦õ¥‘Ür :Ò'„Ç^9N“=‚!+ß@àºÑL}*øï·V0–ÊÅY—>3Üüré}¶¿Àkú`Ø ÏS'’ñ[æB4Þ%³žZýå’\!ã4ÚŸýÎÃÊað¤U|”˜><ð“M*,Áô]Á0b'Ü|2˜Góî†Rá· ¸àè4:ï’­¾Ñ;©òó$ΛÎ$Êõrô0ž_i‘ã#ÛÏ Ôõ^|öQ—Ïû(Γã¸Ü~BGÐå{©Œ¤%÷Ó¤ôïJaü¬Å?ͼªƒVD:°ƒ.³+î°i_ú„f;ƒ`ûåDªNãpãÙû¸ýô íÞÏ]çKs«Pm*ÿðñÿ¥NJÌQØÍ.m‚i߬àHÒ|2ÿÃw ~†4Â,YÏ’JîƒGö¼0š~¾bÂo¾¥}Øwç>ŽHc¹Å â³í Þô¹Ïj_%ÃÒ©;ñ±•"Fî󀇷6À,'X£Ìä­#ÛäBX`ÛlB Ø»fy›­@%Ï¿|}ª ÕFÊTýŠ vÜ™Âæîq Ï}üÀuÚ ,ºÜJ&mœËG;]fš«àûþx0Ò¨M_ ÑÖŠgÃÏŭغb*ŸËÆ•§1j³ÁV{(|Û8þL¯F¿%£ÈÃŽV²Êïî%B/ƒ™ìÉnÆÆ„T†€be8¾Û³¾ß¼p€ÿ÷Ô(pUƒ\xrWŠJÍXŽ 'CçÀ9‡ÅtË™ÃpôìeöQ- &ÝfoR®©£©°ÙuŒ± k²ÀêÚ\80F‡^ËÈ™þ˨ZRÁDN„“^ëì0õáãý‚àîV5z\ú>ø,Ø…ç?øq8´XèÛ »Ó xê`}>*$˜çïÖ¤¿îEÀò?ÞôîÇÁ´þÉvxØ~Fuö^óÛG=Bë:þÍ.ˆ ²jŸP¶þ—7›;ˆ´ C•)F¼o¡+_ýç%<ܲ„—$Ô~ØŒ[ÎÈâÃöá|eu ^ø¸U‚Å~éÿŸsv‹É‹eKºV §ø "¦Êj4Lì¾p–Õ g½O`c{8Œ;q#.Jómåùß'0¤ E(½;vëgãÈÀÜbø—Í DR|ü¹£U¤'ü[RÖ†0CúáÖ,LòI ~nøæè9l[dÆóm“AÙÊž?+9W‡¯{¡Çð,f÷&vzu¸f»Þ‰WÅ‚{0z|Èk|ÅÅß`ª¯4ýï>ÕM±CüÆ~ã¾h*õ DÒ…þ}Űïå rb-b[öT(9‡$„iÐ›Ó³Èæ]öð0~U> ½ýdø?=á6Z,LÀ¢,a·U ñÞÉ•û’'«"…EçEAMâ,ƒïG˜u¥+ÄÆ•aá112IIû‡õàÒ¿õ䲨!³ó|ÂF”Bjµ(}âMž³gšO‰Ý°r\Pºd%±ƒ¤ÀšöUpûþ[¼íŒjš£¸ð—%NÉÒ÷–€F‘ó¸'ŠlEZY‡cc–u˜?šæ¶§©× áóE¾WÙYÍá;ÎÁ—¨]Ô‘?æÈ„^³@ú=òtî‘åÇÒÎÆ·ð}— l—rÆØ¼iü•B%8-d¬FN„aÖ5°¯iðÿié‹1Æa-ÜÜÆðî q<_æÔ·ç঎ah¥Ë.Çè¯dêë L±¿‹—-Ž gGe³)ß§ÁÏW"ùH’v©6@€Öuæžb¶:‚³2Y••W¬!.ãõHé­ÜSÒD<¶æG6žž9öO-ƒtkBýW¨ƒíß&,‰»~à¾uÇñßgèîrô”kà„ek`Åp5.Ÿ˜‰~×`ýÇh6ýp7>šz—>K`kõîcë‚'$<û5jÔ,‡^•Ó0êWvÂî8ܱé `eãt¸d|W¦Âé•‹áˆêŠþ»úe,sl+!+ªLà[•£pÞÑ÷,$[‡¾ê‹Ä¥Ï¥YŒT<1øDzô<03ö>|Î*‡õ¥ðÌr”}ñÀ9Ò0ð„úŠíb•ÌñL6…M«”„Åë €Wx¼áq»FyèÓ©kàNÞsì,™=véoåcK!ág%˜ß^Â~Í ‡åûFR3GÀ¸¬y¨²¸ \G/í=füR”€ËþˆÆ—G"±.> ž–ÂF¥|8û~)ŠnLfµ ŠHÐÐ#à†Â³Q\ÿ†-’©Åðp{µpyÇ'f;ãû£/xy>œN‹øÿ¶ølïÖxüm7âßùm"{Ï Dåö~Ã0êsë6æWIãøº¬éûa`~eè»N=Ö†•s柌¾ãfëÌR9Ï«ßæÃ%­ëxeÿ\Ÿ.Ϝӄ³&qûéÊÅÓÇÛ‹pY’#­l’‡m‚*zrþ_z×&çìó‚1\¶y5Õ_z½2aÂt?”Û)Êï:vnù¦½2G€üÇñ4¶òìtž•Í@ôÜi¾àâdn°smŸ 'œ:±R×dû–£aA)~“«†¥Wó!¯Bè÷›Ñ -¨À;/ ôïê!'±Ö¤ÖZöàëËh´2*™KÓO äÞ C®³+›y…}9«tlþjWÓqÝ®zC»o“qëªXC×)Ö9"§Ø7“êž0gé{¸‰­–MZBâ"³Ægæ4UP[ÜóÁíxɽ…\Ž÷¥Mir{:UJÌ -jÑ0n-,ÚæÂ–Ø;r‘ø#„mí€õ?. Øþ¸¨sZÊ@᫟pá4>è;N[ÐÀ;Æü¹ës’Wˆk¬ìvŸv iqcèÉ}áØ5ï5.=)Ü8¹SØxà*¾š^Ï¢B´éø Ýø¿üx$æÜøË^ç‰1…KtRAlâ)X´Ù §“½ß`´Á(þ;Ý–-­Q†òâsðð­)Ýy¤â?fcóŠD-Ù>̆Oÿ‚ýkaä#Qè5 „lv’9ˆ®€¡F˜—Êw áãg7‚oK¢°ø±)“ˆ„gÒ“Ðêu8ˆgÄtcu²ÿ¦ “»ÕˆÃ?jùÏ äÚvè>IÙ¥ÞW˜¡Ww¯‚#ÒgYT}Œ \‚ó+¤¡çÅ ò2ĆO?VQÁøý ªþk§¨rÝ¢T©ü V”ÿ$´ÉBs¿8j—žÀoï ½—‡á§Ø@ü¨P©¹ _;W]:†yÞU¨t;ØÞ°«Gãõ™µ`çæ@ g§½ékQLízt×£ûésuþ³ãzY¬¹¤¦ë°'ï‡Â%ºÒ÷Ìô mS_˜’éÆ}ÏÂàÔJvïç èK“æ `åàvÞ4‚_&²#S@Í%žë"¿bÅQ6î0œ}ôFŸ6 ûãJIÜD%šûäl º ¯²¯ãûÖ|§Õ!rê_¬˜DE§ÿ÷\Ž}4Ýñ+û4V4F&áï"4s«óli(sRBë2£ÿ¿·ìh6˽À'=W„Æýqî÷ÛpKÕ§í«Âæqíìpq)>×¥§žM ¶÷±R]°ÿŸfê’_צïf:&ðwÞþtì0ÁêÉZ´lQ/Yì·˜®üÒ ‰Vù o`9v ÔíB=´{Èᯃxi© )£65’Ƕðg•,쀷I«xkéAð÷!ôAz$ËÍÙÇF_™Gãþ@j+¸ŒÇiÉ5xb†½®{\@æÖÙ|dõfVs¢üi&K7=…G~&|Ó¡[Pø2„TžÑ&>{ÔÒTºyÛ~ì^±d€ÿfv„ *ìþÕâÃBa“Âq”; ̇âæ•‚«÷Ä©)İYW¦Ñ°z+ÆúÏÁÖO.lø…oDäõ 4û<¼Ŧɡpíæ9¶ÖŒ¯<» ¶W* ùp‹õwfâW¥30£ý Çó­7÷/èg“Uò«wVŒÅl“ñxÀ=%lÚàÚ½ÓB“ s>› îËyÈ,ÐÞés +-–Ååâþe·I´è0riÙxj¸þ9y¨ÏáºÂÜE!h"²CN:¢S·O—n•baßnTÜm" Õ‡_Å­{VÐæ¤¨ú/c\43šGcVLÅS = Zðp€Èý"ÊOžaÁäpagÊ~¼>y4¼{Ø×ûCX­IîÎ`Ø£rZptÏt›ÕLÚÊeß­ :›ÜxèÙrØ*ÿJXÒ*;™£‚…KÃíWCéµv.&žý×Ç0C eî7Õ•¹‡†ÃCÙŒ¯”^Ñù‚|Ð^Ìúqœ… æÒÛûŹV›)Þ ÒC-Ýf”\gkŸuãÁqº|æ5/ú·Û²Þ¹àýG2Ü â,Ƚ ªke°D˜!Ð6šC%¨ø.°}?·¨žBÝhô¸/Èÿ‹CÏ¢ŽÒa Z MsYHÁÊ?aŒIŽÒç*W H¹ 6˜>„ì³àÚ0֪ײۓ3`Šé7ø˜ ‰º6cЛ´œv&Iuß÷˜òÙ”g«}b²†\|Î>,6XÙ HÜ."Eâ¼gÜhœTú fíi…˜Îrk¯¾µ†‡ ³õAô³^Lû­H'>Û_^$Qé'`_ì9Xì-Êî½™H—èCš2îOŠ…KõQÌîðR’C8ÆuÝ?!4&Ùc¥óL~pñ0Á„8ÿÙh²NŸçº&lþ€ÿ'ì‹E]Ù6–8^g…f2ÏìŸp4Ì?Ï9Bì¦ïd>ûd™È_ô‘rOÐÃU}uLµ||é®Äü»ƒþiÕ–ø 9#§ÞU¢Î%+>\¦ck¾‚‰…>îíÔ¢_GËÒ•Ý'Qwô4ΈSÄõm±ûH7n—GÍg† 4Î(¢Á%k¸T¾…t¦…BÄ,†Í޶÷I¸¤e%ø_<†bwú0x˜*u5zò‹ƒ‘Á¯µg@|âTÒÖš×V“ž•C˜»HlÏæžÁôyǧ½øuâ. !I3wÑ´”×üoöjÈ?z"ˆØ?™Ç&üÆkÙY 6ö-쓆{^˜çVÜ*>ì¼úÐP! ·¶Âgg28dSúrˆ$4p*,Õ»Vuà…¹)“ù‚šGw`Ùå³øÚ~±0Û/Lp>¾¶ôŒ¤â­èÕlI‹•øäO=ž”Ïß óΫâö[Iä¦ü\°>9‡M`#˜ýG PXë‚jÃÂ*˜Lgáf¾$ïú\6EŦZT|¬òßúøÃLN "_Qgš+6ÖÆã/Q-T¼Ä±sÇhˆº]}€§sŽ‘á ʈß¹ÿRx’.–I±P”»@©¦ýv‹f¬¢ Qºb2òôUÔÇò)Z³Nƽ$Qrí`¾Ç<z¶÷ m·mz¨O¯tÊѯ2¯¡±Kžê·zc×~Uª¦òV»ÑWwt1Hãºx)p~K“ ›pÙÒ낞 uÇéÞ›©œB+6¬«Ä¥"á<¸çlžÐG•ïþ·ú+BÓ(+ZoÍ=ŽŠÐ¦[¦t‰b³ÝeiÍlOºÄB†nFW5…“ŸçÌa«s79±ãŠ­èÁAøîHƒ6CnV–È~ôd1ßdo>¡á:Ü1`»[~`îö•eô–Áîœ`Ú3g1*pÝ‚*vaN½üOÿ Ø„”XôuHÊŒ:Æt³?HR"Nýõ«Ô×T,ˆÁç+àùJ p^ÁTF ÉŠô¡|â¼é`8Ykˬ…®]°2´‚–zá„Õ0u¨%LÝÚŽ%{—ó Bímõ¸ó™…¼FS^—à‘¬(ÓUãá\íÈv;¯µyi¿&™­Â™ÿêKÿê:ëàmœ»R‡‘ÖÇ´ÉõXßó‹át…*ßÜ B/7倞L Ög¬Bûú((»2o}DÙëŸü/SñÜ¡;èu:ßöPôIÇn).é¢ÅD^ƒNîtó²€E'£Ñ[a*»åK»ž ‹+d1ÉK,Lû‰‚{ûè÷3³«ûå©Ë¾ÕPwø¥`¯`%êÿ4ûô'8Ò{8¶}ýw üAÍET_œ‚ûGÁæür0ö¥RƒöÒÉMkHmÙA9ÃíWÔ’°£¹½­àNœ:·z¸"Ôæ 'p” p;þ ?—"G/ÿõàå¡u 2y÷,]Ì›¢^ÿËUëÁ½c=qt]V\„vxîåc¨Ò^Ê5²'èóê4âjr ûºH“Ù.Èï‚Á7x³É ÔðŸTñ“­ä@Ù3 ëû… ! Ô9/ì³ú…SŸÍ&aïOIðè€]?¿E*ðÚ„!tË©ØÉZ1~MÝŽÞI ïmˆË«Ñ0-]G=ÉÉ#ø«¬»²püˆÛÎ^Ña¹®ŸÈuÁz”«•/tÓEzðó ,in€Èqj|î‹ùüáæSôúÌdvàó ÚPf¿ÚàÛÆp®}Ï W¿ûˆ6î“à2ëf*,n+pñ_¼5½œ~»³pþε Y˜û@ÿÃÊÜ!¼pj zªñ KcéŸÂ¿ø0V‹Âý~2ÿ¬.õ+ØÍwâÀäašÇlRž’@M<Ââ}Œ0ñ[.='Ò‰7΢G‡)=¾µãïáíŠaÜP>m ,kãvK?¡}é`îyeï6³á×gÛbîÐ…Ô!·-l÷ò„Í©g >UÎ2…_²nh®ú›ù´…™ †×2Œ@ööþ2ewÞ´…~»Dti«f\¯gx‡”AüôVx´r:Ÿµò;¥ÿT¾‘¤£œ·ð×Ó;pØ?Ì/æærµ”bþùŒ/ÝõæÎ€þÉÕ%¸²f$Œ9Ô†ûV¡§² ±šÿŠѦ–…7ð¢]ü}µƒWï—¡ç§ß€¡ípk±5]T­Å»uDìØÈjÖºîøï¬eRá/@ê„>ÏUs  -ÅžònÊ©ÐMÅ«g\]ø+›–‘­DÆÜ‡D™zÈ3›–ñÔ^Ì®3ð5Ü/T‹ è³yøûÚÈ1ÊÅ]Æâ‚ÍázÓa°0{ Z/¢æ£ÜV«Áÿ|p=¡3Jü)Ûª%aˆÙq÷eî9©’d^†ª‹É9=0>[…wWë øÿ‡¹pW)‡lˆñ'/^g}}ù}œ•T ª_ÞÁæ7üSe?ê £æ®A…¥è«ñ«G^gg—ŠáxÛ@Xm&‹Wª w¾ƒ±•ö0în îšÓΆ‘'Žï æÚ3\r¤½^°ïûðӂϸ&ʶNr¿0õúc´ß`C{œÂWcóðÄÎQ/ìÝ·êmÇñÒg®4¤ %LA½]÷áŒÜ||gçáe°Ì¦ý%côÿ£#i›¬sVàð/f|yh˜ï˜Ï/¿°¥?ÆF±ìnW6¼®R.„‘f)[¾²y8(÷D3=kÞó-•=˜ìÚÛý¡ÀJ‘êfs— ä¥ßLxªåCÎÑK ËðgtX«-BûáïØ¤ûþ12(Îìõ ßdq÷nql Üå¡´|<Ï™'4-·ã徿tÇô6r·ž0Íå¶tÂ=‰»¸£w07;ÎæÏŠb÷Î/FÅÌ?IžéЉÓâÙoQT\–ËÉå¹KŸÔýü¶¤î„j´?½É^Í(#’YFôdbÚÿ|œf’+`‡ä.¸ÅÊ@ã±.8Íþ ÷6£°çÆR·T—w)¤PT \¤yð‹%]åBuvÜ.gÜa·åÁGCƒ_€û¥ix±ãxQ=ëf+Sç ê5£ ¬¯Ið–o;h`~ žÛÑÛЌī.bdùéï$¼LwƽÇ—Úœ`óËäé\ɘrjžÐû´?۠Ë”lIÜ’ïäÓJjÉÑ’~oþ¢Ö#Ö*Ð5ŽkxÓáK0o® Ô ÉÈš.ýl[^*¤èüÂG3œpTüQ|. ïJÜbÃ3¤øI]3¾ìõð­º çfŽ…85[êþù+^NN…ÀCï!¾¤…Uü<…3fƒ)]P=â:L:ŸŠG'ÈPµ²ÃL°<ó‹ñVÓBÚËß`¥uYÿbµ‹|¥WrðëãsvËçï9wѯ­‡Ü?ýŠWQç7®ðýVÓèÕŠÝ :ù&Þ~s§hLäoú¬ÀçÏÿôx`öPu\¡°;²fó7­¹‚«À~ß`^2±Êó Íh„‰ÍJĶ'ö8Ã)¶•PhA>ïÕ¢éaâüï„O„BÛÛp@1·}P ëÏFó–¦ ¬½{½ êÚc’S<“O5݃o7g ÿß/¸*½ ã4ŠA8JŸ=‹’s<0iî`¾3ò7ºø)ó {:XÃÑÓétxÄÔ¯©`¾ž/-_6†ïêŽB³³óÉÄ·ß„ñÌ©îxm'êìÿ§»ehŒ¯ðC*à£Òá÷©ÒüͶT|'¸GÞy+âœìéôœ‘:=¥Ù #ÓŸÃuc5â5¶Láôýíd_¥auòpÖŸOî^MƒáËB Pcé¡Ä^Ÿ©Æ3é͘qm”~ Â#/ 9Ÿç£/øËQ/’‡Ö°µÓ–±²|X§‚а³>[ÞĆ8K:rÝh’$Å[$¯Á® :زûÞ\×'ÔÜ‹e Qà(­É´œ!š»,èú±Õ°#¬ââÇC²ô 4Ø€öÞ—àÕ j0§?œøÜÂëÈž6êÕ˜üRÓåàðà 2òä"î^õ &wºÓ*ºWЯ&éòå빸?ïßÕ€†jÝÌÐÍÃFyÓñtf€"_*%èz6Mœgâ™ñUx°d„}þ‚Ï'cí0 –‡4ÁLA«Î‰³¸Ö,ªÈ¯âº§ÑÔ·@(Ôq«ú·æ6–^ ÓzµøšW£ààh®pfÚšL×Y¼Ã‰òÇ`{àþð¼!ìyûŸùYò{}9,äQÿûl‚z—sxBN̰äV!œËâ¹í `’ÓJ[7‰Ó Ã+aά,º4¸ÈÖyÝ*œþÔšûüüßýµG/AÀy¨†hú.þÑæ6Ïûíï«aÎY_4˘Ä~U®yyT|£ ¿là·[Ç+úËÂ8'8*O×(¾DÉ a ´•|l·¢ñÿbðæíÛpí×0ÜQó *ÂèGë“ ªcF?¤¹ð¦s»Á=W@ë(ÑSn®¸îR‡ æØ.i¨Æ‡T„¸3EøV¾>²op>ù,K ¾~0œ?¢'Fò‚7U`¬´Ve÷!eb;Ù'ò¿ÌÝ O³#©Ê· ü«Kƒ£Õé“Oî áx7ÊᩚÜZfßUÅG,(À_6¬øKÁÀ!RÄý¡(Óï„M¦Kîg{'N ˜Éc"ïbÙ I”ÆsÎÎÀéÇbñëÝðßÕÙŸ ÌÀ‰÷ý÷{Þ¦Üõ˜íñfuP|soš÷ÇÌ/!m¾Ùð*)íDÍ©Uá P|ïÃ+ö}'»G$SSóxšy_‹Ê쎣a’g0ßad†OãëÖkðâ} p{[ûCÒéi­û[\ ‡½;àºÉcLXvÙø¯¶]ÊšÁ]w·š»ŒÍ²‘V@ ëz™/2‡žŸÒŠÏf¡K?¨ã‡ö‘ÔúøNî5y+Í}•+”¸aM¥Lchöà ¨&XM»eð÷m4ˆÉáÇ{¯³†8xÓyJ ´½5¾L5ã?Ó$÷v>dö|¾¼¯ {v‹Òý†ìï]SþiöxºéóDêhUƒS\\è å0ítû¬%Ês¦ãÆMq<Ð<ˆªú} ¹1é}©p~)͘&=ôæ½ÃVë¡3øä¦Å|tûêJÿë¯þÿøçîC†ªÝ8S<OaÎÑÑnü’_pЫ‰òÖTáèm2fÛYÜܘGLÖªÑòdyjî1™”Éû¼| V‡—`æh3j~ß|Þ~«+ßõ/òÆ3rKÙvI:ÜÍXàpÑ‹Ê]›Â»s¢3·.¢ Gä©ÏìaÐq^Hžû>&Ñþ &½ØÓ&Ç—óøö”iìé‚@¦$¼ˆiÇÚÁðÞ¦2NÑLXöòo“†îH¯ }c2¿,s*wñosÙû•ªì8¼‚Åm"|ðÍÓÂ9‰#Ñôz4¶]…/|nôÿx˜„óë¶ ®N›AåŸÒÊÝr`Ø€:׿ OnÚë›Ð Æã^_#3+qqs!ºn­À”U¯·—™¾ŸÂ¯oOÀƒ]Ó©cGß3rk‚ ê7>ùáÕëjpÙú)6‚Ç=›e18{M„\ø‚ž‡'ÒqŸbÈ…AOÈd­Pq&‘Í;¡'h|t4ãˆÄà0øø&¡öS0~<—ÝOÂUÝçIúð[e¶4˜ðe]ïíö¬·â.éôèø hUåÇÎ圄õëR…è%do'jðÔ¦b7]|QŒv,?Î"§M°ÿ³?XeÖpÌsþK:Ó±þÕY´¹{&Hh`ÜYw’kGŽ”æ‚ât°<®OUGÀ¶²§°ÅÒZËS`´CÛ—·ÂÌ@}÷P^~£¦SG–ާ/Fá{Y5Mƒ3ÊCqf—çþNÕ«ÕU­ÄÝÕ°;AWï†ãù÷Ùø¼Çvèûò$ÙK´¢^¨Ùô.ÞЇ…™õh—!B#5éQ«(¸o¶” ƒÚ£w…E~]˜¹2[«¡%l…Pëw«XÕR·q|&z*Ð7QGHIc§™Á@üŸ3¹@ÊwÝe/7x¡ Œ§ôn$ivp…Ì€&’þß³[÷ã¿WЪ·;0è [týCôøò¶5W3UÜÜ/ÜóC‹:ú2¦ûØËsYŧhrH§ÿYO»ÆÇã½Ip%]Ÿ7E(@c† åÇ'Òü¢´~R’ã!IW*mc7åUxëMj™+¼¶¾ ¢7Ã*ò_>"ì’QãMPoO!‡ÂgáÙì©8Äôšå‰Ða‡kÉÒ]98¥ë ÈþãÜS!|T5ù¯×yä¶bXƒë%þ²Ö¯¸™ÔÊÙ ØkÛP8Y¹œ„(— õ¢(êzE¯¯…_‡ò)­-P‘ÓÂ>hæQ—…ö¼_á>lÑÂ'|OÍT#áuÙCÔ·g.WšYÊÛ7¬äÓÜTèù]N¸Úۇϕdxbî1~òGÞf1‹’èìÛ:˜Zþ¯¸‡R•F˜5é%ó»ôœÉ7Çñ·©·Ðvíl¾qÌ^2q› p&‘›ë%ôaŸ˜±¹_è,Ï?„¬f4Ob5YëÉO×_À­ç ©ùõÝàwþ#.Ð×ãÛgd“U)æà¸t›Ô_!èßu4FFÐaåÎ|bq \/|„úÆ Ôÿõ¶âøö§.únÅs½déB5SlûÕ†NQåàüS†+öÖäs¸]t*ê7ƒ=ô9ÌÅ® åÞþÿ7—ÆÙ¬£fˆâÆCxýÐß°zÎpn&™ŠF= (÷jßXxœtkÌ…ÄêgðmGÊ4ŠÀ#é)<´a&^Þ÷ÛÔS‘© ëßE0Ïr¦ïGWu¾,( rŸ¬á±Ñó@Í] s 8Æ×m²ÎT'+•iˆÐáNV¼C°‘™Ù×ã<›ü¿l7ÃÎ’L2ìå`&ìsǧ!1<É ï‚쟔¨À«×|dó˜?>¬Õ¢?ü· }þ$<ŒÆ%«´ 3Á«$jÇÍà«ÍÆÓ1i'XÅi½Ä|Ëï>ü3¿ÀE‹ hªçÁæ Od¤øÛ/,ò¹_:u'ùlñ—i>¨ƒ yÙÍœˆÛòœñp]„ðåì­‚—Vƒa°a »Ýq‹Ÿÿý¥_aç“øvé\Ã\ø”©D= ¿ ¢“ l>Ì-'ˆó©EɰÈã9Öx.\¾$”¾›ó{’³0ÚoÎ@ü;ú„ƒ‡m‚ø'ÆÄ„.ÂÙ/7Cût´-ébV†'ð̨Á¸û¦LÀ³`¶D‘wô7cŒ‰¬½Œ›­Ä‹®2­¼¹üèÃjÝîŽÆëÏaIQ7¬ŸlEeû¼pƒs0)½B‰Œf3)˜¤Jó–Ëò¾Sf0óÇW8‰uï±fû ˜ùg3/;˜^ýù> 3÷}k¸Gº:ù$gÎå” ÖÛŽßždÀã³æÔxE9æ™6‚ÄÌ ¡Â+/Tü) Æ §AtË\\TsŒ€Äjpocuþ0‰V`9=ö3™>uX% ëŠä?ã‘YÐ ­„V×4¹òŸ@|kö„}Ù¡Àïî5~y$6½½IôOÇâŸßéÄ=j¡pÏž‰ ¥L$?ýŠ͋Xêå÷,þ©7ˆék`îïÇv¢’‡Ø)ódüa/“Qƒ–gŲ³+dx©!ÜJG_¦XP§ÎôÒ™ PÌx(œ]Y‹Ò618vN,<»o‡»/ŒEûa¸Þè¾ËRï¨ñ´¨Ô Þ¶Iãxuþnm)Ü:/EýÃÑêEá µx\­€{çì1/BÍûìëà}¬`Ï8f8LƒêOS$äï/(Ýò™øÄ»ÂÂÕÐôªkÀþ¡Uv ²9¦má21gÂth6»z+qÎÖOhŸß˦ž ÖŸ»!q·=ùu®®¼E+=‹p¶Š õЗ£ªæ‡I}PŒv‹ýáÂÕ=øõz1¼T¶¤W÷ÉÑ’[ù$q°½4Ü„M|õ‹ý~óŸE¦½¿ÆþÐ+çÙ{’[—×jO¦í£Ý}öìK®=ì–‹âtnxì; Áêíd£ê1üqÀÑSL¬Uâå÷}¸ƒ®3¡8¹úóûÙfÉÑlDŠ-õ¹m ߖ̯;.½•ÞVC¼×^A€ÊoÖÖð}€ÿ“ãÕÀõª@ñŽ/·ºU,1lï2|ï¡Mít½ñ®»mEé „KæwÁY¿`*×õ U·K’óÃ^1ÁçD2¢ÅŒ¬Q禓u8=ð™ŒXêÍö6ìvìÏ…sÞø¸>€œ}?×YÞagWÝ­eç`žÄÿ¾˜ù÷³à§¶Vj²ïªàƒw»U²³œçáÒ¡)uî>äæéÁ45¬‰\9ulåÓüÎáüõÌðï%t©«Åøpk‰ëççb¸ÈÒ,s‘¼Ú‹Ä¯ަiCrlϽÕäPôïü÷DÌ®‚°ƒSAÖ@ï+á­ X¤ËkâàòZ:ÒÀL¼‹ô;¯„Ë= õf0­lxBW$ûA'Òpcp NûúŽYJ€9ê,oÉzä­ƒÊÝ7ARc&)ü´¤ëwðΠ\¬"ÆûOÛÝMm"åÑ:¯…Ô½@ôó~_¼gáàn Þ³QCh±&™Ü #äƒw¶›EÌè’u¢\úÌH0'^`ö̆$V³j‘·$ÏÆ—lŽEÇ£áðØ=§œÐã;ΈÁÁÙö¸ˆ…™ÁØsî$Yõz¬ðDÅbÌø¤:Àÿ-"âÃÅ`ÙÈîº7Iú8Ù*E/å’žcCYÖïY$~Ç.Ü/yŸ«£¾ê1ḩ&/ôù¿ï!Ó×—½¸Ý›LÐèÚ"lï[ˆ%Gȯ»\˜bÐ׋£Ù!™ƒäî8’n õÎð4ã:TY%amüdÞee‡N7v`WÌ_,ª…Eå@*˜W’)õ^C>×?¯«¼W òƒÞ² G)ü¸9MnÆK¦»0t[¤(¾!í=ÑOû=ÜŠ¸ Áíæÿt:nö%[Ý?±#9hä¬=I¾o:k÷DoÉAÜßô+©ØY ±ã àùiv}‡1Õv‹µ9͆éÏNÂÑ:®âc'<(_&xõNŸþcç«Q½Â:rWÒNeZ´z4œhM!nWÇòâö‘ý/‰öÉIãOŠÔÆ¥·fByžÝa ùs-©ºŠ8Ð ³¸[™¹t8 ¿¤ ܾüÓã·5èÅu+èW-\àEn¾/µÛð࿹x×ÿ' Ë42—á‰K÷Pÿ– /y¶Óf¡FIÃ…j\üo4ÈÆõÄ/‰ø¥< ïè%ƒö èˆX ÷’ ™hŸz^LBýSU°²L úúˆæÆlð?Úçς±þ#éX‘ xà÷7AÓØüþ•E˜é؇å…|Ø2?Ø,"ÎO†*‘Sè¹ aŒ{-™·N _o‰Æ¹"ÑlúÇT¢"¬!'¾Å—‹¥èðG%Ø7úƒ°xÍmRºÒ–—2¶[~i,¾«…ÈuA8YFŸ[Ÿ»ƒn¹®üßù£^mYØÒAÖ¾ø ¯L¶ÿËɧqy¾ÖÀõ_ÅlØ4HTob?™5k§rüðUgA²ÉD’w¶‘ø Ç ŸŸãç—öà±ÄžUnÓÔÁìSþ+õÑ5ÿm¶`u"Av¨Ïò±Õ7§ÉºÎ!T°3;Æz × )›XOΖìÄ9"Zx7ù6ôï—†Ý.BPhjp’ÐäZp{RÏÜ׆´½É˜ú²œ‰Ø¬‚°Ü$LÊÖd[ííq÷áÂËÂPÚoÕ¥Mgcѽy n®3\Ñ<üyñÞ¿ZG•6?‰A‘oièñ]šÏT°iàD“ñXðGá¹Þçv©Û|òÿF»òÙj”^½Œã ðáÄÓPûd D)nÃ5Ï‹P°t?(w£û½&Ó3ªcÁ|þ´{Š_æ²ãü_.:±_0ÑÈzì—‚êó8ÒëÛÁ¶ž.'Y›Zû}$èŽùpí¹¼ã=“xê„c|‰"ô]}FzªØ)¹ÿF«ËЮÓÑ%¨ƒî“±.3Óä º¢Œ¡üß+Ü×o€¯žÖ0_U•þœ¬Œ—ß§ÃWÉYì´as?è q’ÿrʧÓ9 Çt—’æcÖ \+ «·üÁ]W‹˜ÿ…Õ|×?ŒªvãK[Ée÷Û,ÝkÚ@ü¿{– ×'èâ/„ç'wAáúN8[V Ÿ•ìùÉ·jÜ·ª œ¥Ë]Ñ:[‘ElˆÇ‹»óõºÓ¾%bmÔ3lÖYÍ•Äncèô{×HO$çts`¸Ï3HýíÆC²½q´ŽMÖBxå¥ONê ÎÛ™˜þKŒØ»l¥3¢óPhIÿ­Og–§¦Ã"`m×)ô\/EËÂlÁ³çs:Ï$pÛÍâWðôÙûXÝö¤ŽDØ=m|†äˆÓ“ýûøø”É `!“蟘W`wå=tÀI8üu!é] ¡™'!÷O!Î(qeV¶jðjj„ç½ÁU¾«…ƒB§á`4Ò´Æ}ÿj¦/Ö ì‚Äi(äÔ…ƒ=Z–HQó7¡ÂG§¾aÙ§I0ïÇLòÓùþô ˜ÿP¢¨žãÌaÇþXÜ舿ýËydþ\`Æ™°°È:ö¶¨…æ Ö]c‹Ös&S°JdûÌùêû~¨ºc ÏØYÁ”Vú ÀÐV 65oz„=«´ðÂÚ`¶Õ¿­Ó€— l̇ð%(Ž~wCíWÆ4 u$?²úÈ@üËÛF©}o1_[OÿÄîœ,¼^Ö ºíxüœ fÅvÎçkÖŒÁËãýÙµåoë’¿m庅`¾›'w^"ä&$.ŒÀ?—2™è5ºõJ ·+ÈÜ;ßXÁq¹C øÎ.1²qÝ:Íí^÷†Û,äaL…$?¸"ô¥ ÀàþAÜQgÎM„&\BÝäÇŒTdH >«þö㈵|É% ¥„**ùè–2™þVÓ‚ÙRÑh¸k º¼†)fb ;Ž‚ ƒx¦|@ݪߘÿ3.„ßfíîlÜ[Þc{ª4ôoUwF×I ï¸ÍÆt™kDùú +³Å§€÷·É««‰¹È%{ Ïï1 OÞQvfƒ8ž=)‹‹óÆÂ×1¼GÌPx|L6\Zâ‹w¤Oã;uú`ÇnÖ(¬†­º|µ_ýXÃDEì ãS/Ù=݈º\6§ASâ¡#¿Mß ¯®º×M2AÜÿÅcÒ‚#0Ž5á/U'"—‰†÷âa>þ—‚°AŒÎ£·$Qÿ Á1‹“¥«ïá¡5ðuÄ v<ñSygDîA,“ÂúÒG“sûÔѻۄ4Þú×IP …´üëýc˜ÿ½ÛäHP=ªÍš@÷îjÇKÝjtÛ¶¶`Ÿ5½ öÉ¥ÀÖÉÓ·VvüÌë°ùßv¿R «Ûøè-Ë·æzAº4n^9H/ð‚᫇3W¯võýö^3 }¿›ñ¢w—Á·iÝP2 î-FYÏ£ðk‹ 9&žˆÑÝy8«¾v¼ë?~?uÄÀ¢Ñ#èÞÑëy’¿Ôo§ÎÇO’#%!,Dv ûªjFgLÄä,' ÏmO5ùñüµ‰¼Ùs _H ã–"r|„T9üÍœŽç/¬ÄÎA–ÂÁÓA¼š÷¿ùg–‹ƒÈ½Î©Oóøí—Ç @&7¤y’ ÌÒô¼_•*X³ü .UÉEåýÚÌ·~?i·;|K„ ·EüÊðPP*A÷×zñˆIóh­6¯òþFLMN wø6Ü>3›Ñ^`•‚‰w£ÿöÔ$ÝoɋӆF\%õ 1Ñù!{‚_/jðÁî ècW7 å®­šT—ÎÎ<™Ô¯ŽBÇׯüæêtøÛ¢ƒçNu’ÎÉžð¶C"ç~F™—àÀÁ£(91 e|û\} •ŸeH¢½ó OÑ¥¿šÂ„µ«ôÏŽOóÑ(GY?dŒ+Q§í_ym‹-ÑžnEÞÌ>ï–]R …AÖè7L›Ú[Ùò7¦ÐýÒ{­ú#¾fý†Ú4.]ÔŠåøÔßA¼äÇCˆKVå<àó ̃îŸ!q¬* ùHî”äÁCQqå4úæ&Á¬›GàD¶,7˜–HœLÕq×ðwP×0uUL-ât]ËOµƒö4]ª¾ö ßÛÝO¤¾ÖÁ¿sÀ¹W¯`PR$¾<Ö —q¼>Í • dJÙŒK>|ÄG6eŽ:ïÕ ¥Ë\pœPÿLUàî}°Ûáj y £’ÈÐøT¼º= ÅÉ~韖ªV¢wºS¡yº%Æ@Àä>¬? 5ßÞáòµ@T6ôWÆÂïÃGqVÐhK¹LV7ãÃAôQñTtO½‹ð^è<Üj?ˆ‰ kmwE¬+uñõßr|sé6Q~­J7©Ã4 O¾væm|Q(KOT#óKÙÕ·Ð5ÈTeÁ&·üêXM¶Ú'åÔö‚ý³G¬Öuï'‹™>ŽÜ| }á  ŸLCôÑã ×)S§‡ý¸Z+ž·Z½ûõéçrøä²û@ü«ãòôU,³ô íkÔh¬TJlÎÐ6çwïÝÃĽù´|ö^~íÔ2nUŒÿ¾á’ ‡Sê^B¼E?¯øõƒ. *3ï8œŠÏÆâ1ãÝûe†æâ|IÓ šØªÌ¿ îI!2|¥Ô4úPt ©Vå}Ë?b׌i|K²µ²áá#´ÇXñ€µáüó¬Ö›‰o2/âÒZùÿ6Ø1|¢•„wu6S‰=>äç¦"4¯¯d¯'Nf~<ÅÄæÏñÁ ®3¨xaßf£KGâ$œµOƒNUšÂÕFýM[¥é¡wštSDÈþÅŸE³?œºÅôxÃi³á•z8õ9–6Þê%ÇÞë²’IOq‹a„¶ÇDº­#LRÅxá¦@6Ñ;†´éòk>óiüyà[7ôÁg±pãÆ|˜kœP°Ãœ÷Ü>l,ïþ˜AÄ-ã°Ö@Ð?çGµjªaÖJ5ó²Vh˜PûQ tØœ¡ûÆ qá§ŸäoÔ!¡aïZxÕ=ŽK|\­“•øÛÔ·Œ(>a}Ÿç`læ1ôp•A÷¹†¸0C€ËšÅhŒÙ!.š%¬œsˆÝ Aóˆã˜ÄÁêKI‰èÿæßdé 'ÑZQ°ääKp +$‚<ÿéªeì¶’<÷e×0uz*~[6™|» w-E›AkAúŸ6·îR浂—SïàÆ¼`¶zö ê²ß‘ܽBÇ tú=CüðC….—å&«ƒxU„FÿY`;ùG xÀ£G~ Ýø5‘9“BÑé•(î1œô}Ï“v豉g;ðöÐÈ ŒÄQ_#!dP›x%œã¾Cñeg°ÉTew&Þ„Ó×pÕzsx~;‹ÿ ©%wQ»<êKjÑ1Y°62€×½9·&ãÔð¼^ÑŠ]<ã4 ÿµåÍa˵ƒpþ‡O>0Dh ÝI¤ZËŰq«d©–[ð'vü®¨)Íu­Á»ßhpíg¸R-JB·Åf¬úYäiCîe_Ç‚hÖtNaŸîŽÅÎKÈ¥C`Qügϼ–Zpg<ºèÉ_mMèûæGÖúÍ€c¯‘ƒwïá¯XYºFÞÞˆìb=òR0»¨•²/=Œ ›3°ä[?¶Èí!Ÿ×Km‡±›GDgŽ£W¦²þN{®ö±»>zãž!Ã1ªm:m]ÎÈ—‚‰[nã1ÍP¼½½†x™ýoþ}¨•ŒK „5øÇiø{A-~é>“eÏá¡&GÚ%aJ/´|Æg—wð}j&Td²<°`1c“Aøc!4ç™ÀŽ’Câç„GÞ¤’' KÁp÷;ÁQW¨Ü­Rw¤EŒÏ—XGûÔï¡Õ1ð«=ÌÃøâ(‚0õÂTPu üêûðï8Ö¹~¼ŒfåÛ%ù†[Á¤bK|:åÏ®'Á‹51mãòwF9€¬Þ<ŸŠÆŸGÒ[`~u{õ¤”)nÈ‚Þ õpfì;²ü¤*þ½z¬yá¿<~`¶ ÿ ®ÁÓ5køßÂæ»§žK¢èQ¹· \anŽPjd5«¼u…=H²:íh Ë°L<™móz;fªóÝÎqø¸}!‘Ý~ˆôfNá¹3¤ùÈ®lôý„iòÐþ•4ÍùÃŒ¦JsÏ»ïáÀ¿c²‹KˆÂFypXtC0ä@4äü$j-ÿׯüÊ&‘”ܦX¬Ìò(ϵ4¦+ßà³)QXæ&±Ö¿¸wÍA|6+ŸÇ\€¶þvf‰6t¤ƒ~Wß˹± 8¨éÐ.y®×P…iEêôåµmpy~ ~²»å¹ô|}ZùDZìw»~ŽÎSð2b(órŠ@Y~ýÂ쥿ÿþ5=) ÔÍC½]‹ðkë=öuöIvqØxj½÷=ªˆÙbw¿)hÝ…þ¢Éø¤µ÷ÎGßU»•ùÊAû1ig9”¶‡²Ý1xR_Í1·'žå7ÿé¦úïh‘µΔã_&<Ã]ñzâÜ·`j©I_­£+‚•ðï‚;xcû Øæ±”¿È\Nß½€¥fãøˆ·g„”¢e+ÌåÈ=GuÑiÓá<}‰{ˆÐÑ}õèÖ3Žžº2ƒ_Œ¢P[Ÿ­¯|¨u×Aö[‰_ˆçÆ‘zümh.ìñq€¶Ž<þ` o$)Œ~‹ïÒœñöè™tXG3šõ_ÀY½BL:´%'Õ Áçå°ë+†ÔÉQíÑB“sÔlÜø’ùâ|ŽXúÏŽûI‡ðM×&_(Äœãÿ›yõ« ‹®”ÀYûÇò ÍFò4/lÊÄhhm%¹>K”‘[¹Øg`LW)™Bç9CºÞi wÝ3ˆ¿í‹ÎŸFÓg2ÿõŒ‘/¹xY~ìçUÐÛ`…öqtÄÄMT5þ+$Ewºep—°‰¸eÅD$^ŽÜ¿è:Ù1â€àû‹}tç§:âÛ} ºmù²tÚê„Òãöà¤+ÛàýEšX‚ŠŸÒ…¥·íœð [´-…ž¹‹†ýˆ À#kgx«š-Ïðã#,KÉé;Ö¸ƒÚÐSIäßš`·¿,›Ir·®ƒ¹‹üÑÕì_â9åsFã±K˜uHE¨}RÎ]Ó…?UiAÑq–3¤íèæ–wØ'Ê5ÿ<·Õ~ñ›—D`¯ó|~}rŽT^Ì¥M™’}jŵ¯µÁäÇ_p” ]kŸ/öàÚMÇ ¬]B¸”Ê`LÃaìÃ<>ê2;êÃï›J´ðõ1˜µd',|¶jp ™² ¶ .,ƒK_ÃHn^6ZW‚Œ_´nLÛ^†‚sš=;Ö‚¬Ë­‡¥u([4 s iþd0õ)kù\×ç òLyBÍdž¾àœš>FèTÓM´v àiþ.ÁÏ·ÓaXä­1†î‘Ãî»êt–î8.»A€ñ̉дg‹·4’ÍßaìÈ*Æ„%ÃGEˆ¼:³E6}Åû>R?ëþJª¡&åª2Qø3EŽL×j†\uK¨x_uæ+ÔÌè[ƒVŒõÌ~„…‡#0¬÷8U®dÞ³ŽÃã-SqÜJ¸tOQ檴[TÜ¿Q^'Éðœ^¸ºrüú|2gžB“xdÿa½®Ê@ü·°9xt¸:“ÐkÇKGŽRì9„é¾^<ùÕjÚý!š÷ŸÂÛ†À›±MDM7ŠÛZ¶Ðã=’t‘C*b\I¶÷È 4nœ ²ãG°‹éÁà@ŠõïÂÊ+Ñx'¾•waÿ…~TŒ\ÊÜÈ«¬bIZ8æyÕÀ˜Yè§FÇoÂÚÚIÔ^Ì„k¢~–¸uç*¨ÒåÅž¸ÃÄ;Ö·ð-‚ÁƳ¹WËðSP¤³w ñ|M1mÝ©Bgöße¹íÓ¸óºö¿ô ¦O]Aoè èNßrfä1~­h6¸y¸€Ù»ù\ÖÞ’Ï,ûZyÌmQ~êH†»#èhŒãÊž,ýÀz~iã!ºtÙ^,eÂÖ—£ÙhúIÚî¯PeÓ…ÔêõXþ+~2dV€*ÙÜ¥àžT6Á£àæà‡´éŽëmu‡÷² ÌW–ÚlO¦·¿ÞÄïÏN€±Û&8}ÑÒ?zÃïIüQîu¢–̯¬¡~µPå«{œkª«eø² ? øÂ ZëÝ‹+Ü ñÆíÂrÑìUæC–H?íL ¾/e鋎ÐýûöC¬@·^•8·á«%±÷ö2ƒw“pAíP* φ¿2"´¦.Š·ç’Ã}¤Ëi(¾€;¸÷©w1å[B!jž)wœV(p_EUdI6tî•(ãÒ$Œ¢åÂ[Säè³9õXŽjú\m­3uhI¿Ÿ87ÞãS)þËü:z\¾ _úâq¥Î#œZs}ÉðËã¸äm$xûÕà‡Çîv›|Ü×<3‘Š¿lêG9Ö8Ü«?Ôàò¢9à—"Gm¶p¨4£.#b&÷>šÄÚoÜÂÆ»ˆ99…cþn¦ö®>\/ ÞJÀž Îø²\›—ïý ![`DI7V*l¦]5è³mO7†k'œÁ뙸ôr&6gËc¯×Xî±wñO:Âï}ó‚_;—óÒ±³| ™Ø¥ôÏžGpXa0X>Ú‡/}%À:V™/®´‡³Ý“èý d_kcØ™éÔâÜÕKbÆôâÊYÌFñv”½Æ‹ÛáÒšU¤Au4~=ô±Nsoº;Ô±È% ú‡-n™J|Þ§³£…+º‰bOæÖƒEE*ä¹7±Ž½U(²f 1ê„ד ápïÂü&ê›…×\EaSƒÕY‹\î$Ên˜‚òt°(v)ŸµÖ‡KCvH|"›IŽ›q'ºpÏ}À·dØC×ɤ5¦4ùJXœ0ÂÐ7z´¾­Ÿ•Ó1“çÁùÖ8œ¦ïÈUG,˜ òóx>i‘` K*ñö¥køÍåäs¤ÁoT,—9ëWDañCü®¹žŸ,Ï |§qíä<\íPB“³+þ©©°Þ;ˆ¤vÁ)KÓXaÞ`¾sd%Z’»ÛìÙžéiÓf)áÙ™.,êbæþ;?ÿ8)¶-¦×œñµ <¡qæï #¿%ƒ®Ý6ž×];h-¹x{‘À$Aƒjô1iù~.¾q«ïÅuMÓÐÈ<Ú:ž®§[ä‡rßcitŸH6|œ´ôn?‡ª˜0¢GòŽ¡ôåëLÚ°é'Þó­ÍÌròßìæU½º‚ÙƒxŠÉ™ÿGÄ{ÇõøýÿãÚi ¥©‘žçqT*dEdd•½Éh)í¡¢4(’2{žÇQF¤¥‘=Jˆlâçý½Ý~¯ÏÏ®ë\×9÷ëœsÜï×óq=hˆSø8±˜ë¿Ÿ : ®ôÕä$jåš@ͧä’:ŽTÂâ½6 ÆÜõ?³“—æÐºçÓkö$Ïâ9±õ‚תü­C<†é›ü4{&ǽøë¤hòÔ§ßâW#ŽYüÿMlr€qYdOΞ6§]}œ©•£y…Ú”Ý½Ž—ŸëCÈŸ}Biq&1ü›KWè°8ó 6ßÖÿmYP}»sÇr¿§ú|ƺú¢÷9¬ÇÉýP|á>N’É­wPû¡,_Ôv*íøàá0šœ¦ÏëNî„>¾*ÓòFæpÓA’λ=wZ8S ƒh¦ß Ú!jwç?=D “Ñc»+9á*ÁËícÙO?;ø¢F£Ïn¡ûï)%*†r5ÅDŒ±ÄÞ}wàà¶;Œ2Ix †^ž ±åKtÂÛ—:ÿá?ú´E8c”#X×€ËÏè ‡§9×0¹‡d`Ú„và‹6±Þ¦Ïž ¹S¸Ù'~§Ù˜íRûB¯<†³•®â‚ÂU  t³ ²çƒÔÔŸ¸rŽ tö%€¸U$˜ÙÅ}3#iç°%prÁ `‘Ôá fô狌\ mÆ6øM°áŠiÐøK4)™÷fÔÍï _1‡“¡èÄqì™fNïØâŒ”T¬×ª…+¿DÐ|W=sfͺšÂ^œ4~Þ|”}`ï0ô—ÚZ?† ‡WX½ë ~¹óßû_»yÅìráR¸ÿW‘Ÿ¨‰Âb‘h=¦ˆ¬ÀµS_Øû§‰ËW¾òGsP2¢ šOɧûØW±aP:¶íÖf‡œiçUyªz}>%Û‹1äÊ<3«͹… ¾†ÃëB¡Ó´wÌ»g)Æs}*ä¥K‡¶Óúí'àaÏsHÞ|¨_¡IÏT—áìãr˜x+«&=Æä-JÌï¿ùWÑ=Tãè=ƒŒlQ£WîÙRå­KH]‚3©…Ýc™¬’ ¯™2‚Ï—œÊ¾­Ž…²i"ñü Œ<ÙÄžLô§Æª\äˆSaJ™Ÿ ÓéÇÄTÓ ûõáÿåCCN K¾€ëÿígëÑ›þï<¶†Û8q…ys]~©gc³Ö{–±Ìô³Çñ"ýh>Ñ‘ûö„ùç=á¾Ö. ‘G!eèjx£'NE"ñùÝ$ªñ nÀºOÚø9‚¾Ó’æñ_™¹jp’@µ´5lø–PùXÖ?˜ Ê[Ÿ s[³0ýï~š·ïâúo‹JÛöïÙÛŒND©o×Ù£û;©9Ü‚Çcù?“ùUQ5^2s)»¯˧eÐð—¡P7ž½(BæÌH„WqJL{d¥p·ýYÊH”o»-(TÎ…Dí6òâº>ÿ»„×ÛrÍ!Ô¸"‹%°#ï9 :YízOú :‘%YÁÛþÝ7£Ÿx­ ³4ÿbKn”7µÇ7‰üßu\"Ï™f­sŰàõ´0Öß Õ¥«×ò#Ç#iuÙ>*|·¹Þú‘Ýh¥eËG/TÁë0ÄÓ_“þÓà«Îuf= ŠÛ¢{àì°ˆÿæ?bw$Y»[sçËÐ×+£ˆW¤}ø¥˜\b'˜mG‡È /®ÜI¯ŽQ… s<±ãŠ?•¿AuÓ" ‹NÓÊV5Cº@ñÿ{Vޱ×`—‰ íHë#‡†áNÊEÅùÿðþÿø{&kb`¥ƒ%=Xk@kr³h¯e1–xŠR£•êôäÈ«øõº '{¬Pù•5ºm”§Z‡„DÜâ5Öm•æc ¼A|þ ôK†«ûDh©•2Y5)„Ãîm©Uìφãêcy„[>I–¢ßÚ“‰u×K2x:Fp1¢‡5ʘ¡‚Š廆³X3{÷›‡Ðëèà–é4»*›•Xàë(3ã0d”Ï‚)oúP#ò+vîîG»µ¤æàHì:tvÞ£;"Ÿ n,ÜÇGZþÁÃÅ\-mo´{aÛ™¼¡YMÌîÒ²ÿôOª› [aW‡»¼CqÉ®x¬ö©ez{û°Ý¦ †g"Wçó¤É¹QCÀ¸û ¸ëm >û˜rï_îü‹¼œÁXØyx¼°oÏw÷Ÿ ç˜ n£E!éµ–úOàMq ø¸ ÷ÉÞÚj°­–&ý(œI>Cµ•é'¯Ÿ˜¸zLõù‰ö”T€W 7M/Ï—€‡%xûD þK}2h4•À¸;3yŽÞÛÄZýEùºyóh᪠HÙY û’8XZf±ê$ôß6ä¿WWC±l5úÞ½Btú\ù¶ÃGþã¿Û”ùšûBØæ–,W£×½Åùrí n¿0CF‘¼NbÇD›p»Éüie‡í-zÜl·Ff@bJ*Q-Àã¿àbíDl²WPcSûÃ¾Âæ‹«ØÚ¨ßP¨t &§fBWQ¶+áèÖКå@/y~‡f™eô°õl±ˆÅþ<¸©f'uhƒ$Ù¸U²f®w1n¼vŒEfÀØï Wä Ùå>y>̽ +ó ¡Ù}‡ãþh±¥ÛÞàH &6Qú‰]tý̽,—îÏâŠïÓ¡`øs¦pÍ/nNK¿<ÇWŸCqâû»háP¥’D”óµ\ åhÈ9B v‚Õ-ü¨½žÌ½=ÇÐCqŸðƒûÿ}ÿ<&õº`Ü÷ÓäL‡/l³å‚–µxÚz–߈'ëO°Õò…8÷´ØÎ0'b!>p=gÄy:⪠–ðô™4my-ØÒÂÎ ì`S¼ d¦É„>¶Ô-ï¶@'fö×®f>þ‰äé†Pæ‘ó\(ÿ³5öxß§–¨¿¿Çvyàƒ¶<Ú„DJ%²£ ã ñï´üm ?=øµ>stÏš^[ë3cðÉÂÌŠ-†É^¥¸Ê¥—©[VBAjô;VãÖ80R“§Ò¦ö 3ÛNºÓ¹—@†<É{d޵·¬ù§–0°¶1ø\‰6{ú¶F yÄg:öÁž›¡dù%†ÖaŠt²îkôïá3Û¢p@ GG}½OBV}fÆ';ɓͱòœYgN:› âÃ8ê1j”Æ)³Î±¶‡N$ï·/h_mÚ›ÿWŸ9æéTèjËV[òR߸öz ìË©€šm6XE£¡qEµ@²o0ÈÇz™x.þ˜”‡ÁÚÛf0§ø"–-3îòzlçY’¾z!på2:­Ûõ…wpÝ]E´Ú~|rÉHå¹Ð2[…‘äØ?ØçþÜfÇÅ•lWñÿ½ÿš¼³lÍŸL»‚uéFž·#Ÿ}Ð4£“ÒβڅÊTìaKM&ÚŠVºµÂŽKÂÜnî`½“‡¾¾ Û÷\ƾ€Gò0¶îùE¸]3‹îš áV6"•Ó[€CcsÐk÷7 ýIt¿ñS’{± +AëÅ+0*â¶ûVÀ°†äå Vt˜^&Â^Mœ² BÚÝ„s¢ *W Æ(ò'ÛNãÂsYСó“,šýúBk0Þ4 ú÷”±?ö;™U¥8HçNàÛU0}2––ƒ9júó,:N2Äž¥ùÿ½ÿ ÿ£WO¹Bj¼ —”[LΘ¢Ö-=<ç q¶_ÙÂÊþ+ý3^º”–ÏIY®SÙu8.t³Á‡Ä·µ9ê°]¾½„jrjG(®x~\ø¢¾Gh÷|~ù›ýòuƶqgsîžRDVE¹†ä+òz\1}äìÎÏ@ŒX)Nû7W³PÏ2§eOä¥àófºçÍ T6‚ïéôððt\|L”J¼Œ·ÞÂgOÆãܺ&²Éµ§ºÃ?ý@åÇéÒÓÁò|¸»ÍÚæÍŸM6 ¾ ®£gÞ[ò¦.sÎEÂ'kçÿâß-EWسvyk1Bö ÁQÓ½i>º“5öSáeGËžèIâï‹¢_˜&=§ý<‹’ DZD_ŠB@F7®ñɳòàÝäŸlE:“J3§Ž ÀÁ›6éãbsžeK2nÃÄ™¶8úä1<÷Ü›®Ð³àÕ¯£¹âŒë°ƒ¹ðº&­æã›Î ¤²G ƒÊÿã¿›QU`x£™íðš„‚eàÿE‘[ãÅ?¹@"ôÊàÄÌ“x EŸJœª„B¶’æ<Ø[ Ç áÕŠ¬QègÓÖé£ûbTõ@Ö±ø{ƾZÙ ¢§TáQ˜W¹H_jÅÐO{°¾s5*2áÝ~9ÿ4qJÔu“äa Ql“67:?‹_ðüN¼[Ö³´›Ãù»ê³¸©ö3vLéew¦X‚ÓQ¾Ìe.ò­Å‘‹VãB}9zk¿+Ï‘ Äõ¡ ©lü¨¾.›ÕFð!V‹aNç^`çwò ³ñ€îH¸6|›föTøÏÿ5Ú·c|ëJô\2 W|µÅE©+ql Ýú¦œíï¸ÃÞ:‚EÓNÂå2mnÿ¸´Æ}`u›ŽÁ‚1að"æ)þ­ÛÁf8'0ØžKþžålx•è?a$ϯ’ÀïÉg`£ÍbèÊdÙu=,øØ8FŸ«vñÐò£5â ü•[Ž¢»æÒ^Ã4¹’|Zðœ‹ï±tåËà'%<žðR0»y–ùMB‡dE<¼ç»üÜÅ‹ëÖáÙòÄåØ'r2 ì}PŽa³GƒÇ÷ßdjÄeÂz^Âú1b<ðÎÝšŸ÷uÀË(žMþ¿ïŸ›/ØAßå2ˆÏœÊ_¿B<$™ÎZÍp£Ùçi𮳠v{=iF+tÑß NšÙã]„þþýHà3* ^qã"•ªÜã”sj:†#S@}@šJ¬OÁÒî<4æ’ eª ü\W‡¾Ë÷,„ ¾Ó÷z·?j4Ó5½ tG….w>O"Ì &ý))5Zóø˜Ñ¤I/P眿ˣN_°šš VÆ·1pK>Æg« ÒD±»œÿ²t÷ÞE¹1è¡aƒÿË™–Ù´&Å¡Cµ²)aܰ,GF¶ô‹ý‡êGc>ùEî™øÜZïy6î6Ù‚7‹/“Fkœ:ßv¾ÿ†+¬ŒÑGâ»ÿ= kˆÛŸK5£ç'QfÉ~jãåM°zl(vMÇþJüNè9¤Ù;ÐÕu }På͓Ͽ«…×ÞıûþÒPäò“m¸óîN2G`ÇÏÿ‚oVô_9çKpªÿ¹dµdbIê$"¶³z/ÉòO Û„«ƒO³ íkéŒrkg ŸŽù8K7/¯+àϼóÐ~Çt2özmuBç“—ÙHQñ÷zD|$¼A5IȦìºþÿ—¹/':wæ!¦Œ& åJ‰pÝÓZÝ+Â*º„Ïr-øŠ-—Ù-ópïÔ1t‘X.D-lÖˆ=ÃÓd™pƒêOøµ1ŠÔï>ÄúÞž†Ö!è?l=©8€–×Ê€7õ@ëÖpðø¬È[“¿²uߌ”/ä[´ÔkV-‹'#a#×aZGŽ“W6±»Éj˃`Ã]=nóéK¸ÖÀ&ó îÖ|Qðô õs}!‹/n¨ÂÿW»zÇôX°:äÇ7îÝJ'}‰s®ÀkÌP°±Ü™×,A¯n/Ãp]BGʺÕ3€?Õ¹óÿ.Ð…aÅ{[Õ¨™°ÜØ‘¦Üñ¶îqâë þ€ãçzZzG {Š`ä­3 yÿ„†#O¤CѰ Ÿžƒ­âb\ü¯9²·ÿèyàÕcÉЦþ&1-‘ %al·ïdØ=t¯:zƒÅ\„½óã£Õdá?M;âH8¿»}>¤ã–«ÓOÂéÒÏžüäÜDÚ8K|~×ACV4¹Ù^ÿúoýÓÂ\R÷ ?ã ELqÕ­¸s¤6ò*N+T§±"0êû=¬uAãöãdú]q\¯ÙFc‡Ñ³zxõa„·÷éê©]Pöî)Z¦ÝƒÌZ(È€# qìe¨8ÝÔbÉGTËâ½_¾H~$ìó—¦ÃL§Ñ{þÁøt§-np&ç(PÇQ?ú?Ä‹{ËAÒfýœ$Xÿú ›—ý³OˆãÍ?Q#уFnJ€÷ÓOÀø #¸ò«!¨ÓÝ“äÌøÿjíƒç™tß“(°Ù—B›FÛaóÇ)ôxÏ>ôL¬¤çëù|¥B˜WaGCœ¡n«;hF¶{ ü<úÞý†4ò‹o„8ðC#»q–¸‘Þ,µDžKœ£k]ÕyÖ‘ý<Œ¯Æ¿_ ììNÁš[Qt‰‘Kñ´ói°3À•§ˆ)€g) pâ”›X•Í“§¬ ?ìriïƒË2ötƒî!Pl–åþ5Eè«=nL£~­Ýdœ·9Ýi¬I‚èò“}`—.ÎSW^`S’í©—¸¿øe5Ýûþo¿àA±Îµ7Aô[÷XìüuˆE¼b w±šú¥L}§#-ZEvÄodZî÷ñüq)ªäu;–Ü#‹Îzá£'Xç…¨?·š•ÚÒýo¿0•3J.J’ÑpÁÊ–GÿbSr(½cŠÞîóhûFsú ë+ã>¸Ì;šŒ]kI7~)!Á=qÆÏ«d—õaâ’c žªOðÂVM.°ˆ‡ ¡Øê*ògí¹ÄÞHð?M`³Ôz2ïT=(¿WæÓÌìa~Ë=8~2äîªÑï"ÍP!'Í/XEÙEKX¬“6ÏØ")ëÁúLÊ ûÿâ˘yE‘^{õöŒsvèÜw’y|G4xâ©á`ùw:ÒÚ8"Ä«b±‚(é•|S–25‘6åk~ÖÁý¸`‚ ÚŒ γœÌÛk¨ɖü“@>ĦÂ5Ë}ônÂ|6øËä:ÉÀ•~x)FÄ|†Q¶w°~í!P>=ÍO:ù^EÓ4êgiJû¥ ñp•5W“ÚÅò¦7ÃZÏQðxräž›€²æêü_¤@O(<èµ 'ß´b¢ê ö¼+‚ko `ºæ ú4óËÿò¬ù¥òؽÂý‚9¢ÊÿíÿE-,ù©ùïyùWî¥ï*õ)Í>½ðÙr9ÞØòW˜a×a(ksI/nÒ{à°Ë$˜^Ÿ‹Õ]ù‘%Ùì²Åq6u£"ÿÛŸìÿ¿Ëc~)rý‘ìŒZ#ˆúÝEý9vî“^ÐzwŸ“îê' þí#¿Fñ¯7šážÎ QêR×|îéÍüDú¸ÔmÉ'ô®ç#æ á‹‚ÇäÝòÏqÕãƒÜ¦R–BJZ/W¡ó3s£—ff€X½=‘¦Yî…7Âû?ƒ(ÛdM•?…`ʆpž&Þˆ©3'þÇÿ™e7Y§…Èüê¨Ï.Æ*³W`6ë¢ 2¼›ý}Ž) £pñ%x¡uš|~ÑìlæCÞ  V+»ñÀyOCuê‰ob©Pÿ˜~»kLÃ1ÊÀ•1’äk/èð@µÓ˜:߇ûE;Ò¦í1pû ÂlAñ…*c™†ü‹ ˜™S‰ïaÆ€€OÌMbnêð{|,õëU£é/Õq†ÒqÔ~rÃK.²%‰cqêtCzäysüÃï‘á–¹=ð¼NŠj)ÁÛ÷÷Ñp”?$éjRãú ͦpàé!æüþ,\o)óÑÀ©þ-ÿÍüÓ\øf¹¿§õ¹¬D–>}8=›íã´ÕéÆê{ð!´” 5;Äc?ËáE£gøncJçÑgk È­—SSàôÂa{o€ ¤­Ž¹8}$WjǃÇKÞãv5E©‰”jœ§¥ÞÞZ“—¿™B@’¾ùþ—xä:ÓIy¿1yÖ ôIÙ ¨tÉæÅÙ7àÖ¦J¸îM#á“ð­õYa†[6¦}°â?]¸UxœŸßv¯MxA’4¬Q,…ulºXÌÿÖNF{ë(Ü¿> è0úvÈbØôý®KÑø‡ÎÂöcMÚÃÿüËŽU¦r _G#dl°ü^(»õ(êtBƒ] ÒÙogñU…ŸÑªy(}º[š§_ðà§æZókú4êk¼Ú «},˜@J„j¼y…ÇmŠI}›-^ÌN«Äut6|~¬"ZéB¼‚‡U­q…á2šh· &ÿã¯mpÖêLy§‚ èõÎßh|N*â·3¡Ï/Öç/ç+¼ï¢Öð$ÜûÖ„®‚ÙsÜèÙÌ™|Ï30÷ÝÌ>þXïë|Éú-ßÁ!ú;¡ƒ¯V¸@Ôæ* Äþ¯v498r<]niüßüÇ:EÑ×÷åéó)øzftlæZS²Ù‚§ÞøMï(T€dÃ1àq|<ÿÛkO†ßa&*7„n]ˆîq+zzún¶<½›6ÍãÛgŠScãÿòŽ×úŽ¡EG èÉ×åphîB‚U¸Ã/%è8ü¬†*Ó™©ãÞBWÅ úz‡5nû/ìó¡Öc8Ïzdޏþx-H£÷f­‡´Uî¤E}‰ÐçµÌ4ú÷wRi+&6üŸ×ˆÈ§ýÏ“/Rc„ÙÊ:4nÛN®rÆ—žÜNe>FðX3þàÚP0_uœNm.!"þßpB®iÀ¨EIÄjª_›%üÒþ„¬ ø)øÒ.ŠŠ "w’[š“O°Q‹\È‘{ñ`a®V~ËU¨M¿»¼ª–™ïg•ݘ£F:gbSÿdˆºv¼úÍiÐúµ,¯ò9ÚZ‡íC&ÑïALÚìC'¼®«†rxR¢ŽnÚ^¸.[ «ÛÚá¼Ó¬M®þxFB°›Îž@åç ñûgp < kób÷‚‘p*|Œ»y ÝÝ6`‘¤=5™†5cf€È¬ÓìŠóÌÜ+>å[áYc?›y9Í¥òÜ ¬ûðù¿ÁËÔ1¼k4ÚàÀú¹øÑCŒ—]»®ý 7…Ê.æ|û wzz— xÅO ³„O`§%u‰ÑæžÃ¿C‰!¢ÊÓm¸¤ÕV„„r£p+,) ¦WTT©z—2?r3ê|šCìšË±ÏèH¬矇\Oµto‚­¿äÇ'n…›O/bö1 ^†ÕëðÚSÕ.+s.ÃÔà“Ðã5ÿÛ„c”ß$ë‘üŽJ)8½Þ"œ>Ó ZÊÄÀWü߸û +ׄã²ÍÅÈ7í¡3µ°Èwr|]~ ìO ÅqUO&óðÎ =ª,uÿ?þ !ÖÖ’9èjòþ·à¡­õ^­ÑÃÏ S¿œ†á3`¢© ô5áÏ+Ç”¢‡xøs%¸¬ùÄŽ¨ÿ`ÿÚÁÂâ²zñtaœþj,z(ÊŸØl‚‹þxC%õ’X †ŸÖ/‚3Vªð³cxñ¦DðCoH;ñ‡Q£¿^ñMÙ ‡d£}èø3•í‹Ø_"péhi^±”ÞÌ !aG;áëèføzržrø}rô×Ô|SŒ×ì£o5®Â„…¯”þï8Úlæêá"4MS“ ý¿ ɤ]lj½;/üví¿ùO³i‚ÃIÍdÏι`×2GÝbSÔuiâ"úØô nÎøŽ»ôæQó!4lÕNܸ¦L0ñï8þ§ ˆh÷-„Ö!7ñlv}qèÅ/ŸéÚLYz¢d=±F ^ïÒ`e/s•Âoø±t:_r<Žm?Fc“gsÅC <žÎؔÄ-elF•:±.Má“ÅêÉSé4PNÏ”œÈO¦­ƒÝ½Zô(1c*Kñ]Q?òõ¡`‹öÜ$x­ãÖ£K×zs’-+\Á7.þ$Î6€ß’pªC,a©ë%¶Põ–ºD,Š;ÅøoÿψY%öIgï¤v¡Ù?}>i_ÔwÕ)ÒsOså%‡¡w»7õ\± $hD•.?™Êaµ€“js¾ãâ)ÈU>€‡<øÍSL~Y$Ü¡Ê'Nò¼y«ø‹¹:ˆ›–[£õm1¼eÛŠïéý‡xqf3ìóµ^‹“—Ço²vèf¢nÀÜ}%ó FŽ=ÍZ*b`×jœ6¢œË/‘¬ÉxóK"‘<Ö³œøÖàbÜ Ë’ÇÑ’G‡ØñWɌ凥úš°Íºz6ÓÀj9L}œ‡M“ïàqÙÙ "}ü½u±ëæH(ýÒ {jRiy\~æ•KÕÝ}X¤¨Æ«ÆÌ³ßü·þ¯ËbãÂdÌÿ“+£6c¯Ë$:×Ë $H+ ûé¶Ð-· ·Ñ–Ê[àd£t¡Sûn¨þ$ ûk´øÏëðý¾f¼ðÖŠ>œ×ý³/r]pÕÖŽÖTiÓ¿Ø‚ƒ ˜²îļÄWóƒi\¯ݲ0–Üö€gñk¼ótùJsôä[Çòg)øi®(WÞ@ÍMö|o£ÔGEp‰àÑè#€þ¿*ô@ðy¿¶ÓPã%°|—Š+­ûñ¾Ÿ ¿ñOsojÊ÷lÏ„;—ÁCò ³ÉÙ€ûOlr¯ÃuŸNü‡?`á~41ÕäçKÆcô«\ûN Š{¤iÔ…1¸(Ï–;àÛà‘T_66z¿BåJ2wó4̹pö[Õ"ḭ́xMΤÑùÇÓgÓ¢ðhæb[~€þk‡ÃZáhR¹µò8ãÐj²"B¯ý‹O6ßš›óÂâ­;~Ðf+úØì89/à vÒ5º)‡ú<‡‰Ã¼O*‚F|…'C1]ý$5®˜Êçgè­yÝ{\‹Ûÿ¨ñÕR÷ðôù <ø äjò»Kß`ôqºÝúVÙ—£ô¯‡¶¸¯›}¼#†e}…uëz!E¹Üõ#ùÙ=Ú$+]…¹òGë[ÀyÔ*¿FG8§Í…_‰#w¿ 3ßï§2AË¡{âEøþ5÷ùo¤iO7 ù'X·g7*wfû>Èó0ö‹LˆI„ÊþȩР?®Ÿg}¶áp¨R³æëÒdØ1ªW̾.Üpî6d›*óÊÊSØA÷o¸ ;,£f~‚kÿ4V5­›]5¤6'vÀ»Ö4;¸##¬ù¹MÏQte ÿF§I3é¨[‚ßæ„¦‹ÆÕ¶´ÖþªßÓ¢;¶0,n˜©uęb ¦Nû {þÖ .=ˆF›§cù¼Ö½ì–ɼº.Æ·ŸŠÖ¡ty0c‡R1ÆIššFऋr|OÏ+,±},¨Îu¦mÏŒh¬ž_9:ƒ*T€YŒ-yc@ n+s9Ív:ñŸÆ?7Ë û¿ü"û¯Šò öS€.ï…O2‹¹êÆ©0ïôSVöL@ÍNàÛÜ+©ú~CL^>†÷­T‚ ƒÖ8ë‹'µ2¡­Ýü@Ÿ î2éƹá§¢U45ð &‡š_϶õTß÷ ˆèty“ìBª[·Ò7!ùBÇ¡y¸pR0ÿcNï Ä3²œ¯Ñ%¿-Æñ…}yäß| CˆÉéìÇO¸²XZ°§ Š¬«ì‚Ë1gà×\Mš¢t Q¹Ý{ éÆÒ»xóÒf] Aåï·-³Àu‹#Ñn]E¬Ž W¦ý„·Â;,Ùqžú|B0oÝ"<»÷I[)ØÖvô"¶q÷DØr;K Üs‡üùqž '4à›6=5w†Ü•犇˜é¶,rßì'\\ÙÃ*·=†½Ÿþâ‹GßÑ®q¶?;–‘ˆh<Š/9±® ¬$%>\w¤â½Œ¥vaJ|OMÉ<â]-6$÷²1DÀ=ßûãÌ{[àšI(•4ߪÉ'ÙÂùª”›ð`A4ÈX6âFÑeøiã9œr_œužb“Þï<Nƒ¾‹Åøé¢%Œ¾sQ0Órùª öµd!LLCÉkq%îN•¤ž!Y.ÊÎÅ]ÿìð‹886ÚÚ<$yÐÓpvÊ<°ÖÑÁelø¬Lå³d±ÏÒçu´ícWBëYý‡-4µ£;C×ÜrÖ ïƒ f͸¬Àd7`£ržHÏÞ°ÐçÁë‡ReWÌ´W‚Ôj+ŒòV¤±î"Äïº^ôv ý°Eç(7ºhŠõçVÁÛaJÔÜí¨ó¾û•Xvæ¨0$ï;“·ŠA£€iø N'×ÉÑ{»;1ókÊ&çraC[ì=Ãå®ý4øû£mãˆuý"ìÇo=ˆ§ûޱsRáì•ÿNœ,¿ƒvXF?Åùhþ}8ŠÔaü‰¿äºŠþ¯o¯ 9„Øý¿ØÄ»K‡Ì\:ŽÊö\‚)±]à³0/Þ÷€O&µðnC0ÆkÕø(wâÐÌ Ü0^”›½¾¥·eÁÀó0Sª¸•Þ|Æoü>ä(ú,#ç‡1èÍýço]1‰MáâËÍ[Õäb¢×_ù#}7‚¯ÿ6ÓàŠý%±°ûÌjZ¸Âž^í‹„ÏcÒðà‡#˜g1ŒJMåŽí‚µ{þÀ¼6i2ñÁ}ÂÇÕ°¦ÇœZýAS¦àcEI>t…]mªO'‡eq³)sàQ$ú6Lï¬Í,;ñUÑ£Ò‡­±äz¼¼Å–ºQWïÑ V—ˆ¹·ŒÈXknså{Û.èÜ”ç߇∼“xhFÄüv¥¿Îü&!,zÝX:.ßÂÝú p§^?|4àIüqÜ1i3üYy…Ì©u†~æÐ`ÓÞMÑt×Ù0Ô]ÔÁ>ɬVmšªÈÒÉæTÿRsî9‚‹='/¯AyÞlÒ2ÿ/‘ÙÂ䥸¢Ÿ ÿé%N¢|ࣜ^àAÄßg×I/zê¶“Ù2©ð[wžK bwVµƒß¹,x)2—}ýˆjúФT‡;^˧O¨Bž¿Í/ÁT{MšèÍçæa­Bó-Þ [‚àe£?äÅå9ãÑÞ>fmùËv”žE1a,ÕlLôæ2çÉ8þÇ+´–^S¯z†/å ¨À ¥š»H†î˜ÂØt›©¼ÚݼäÒiD‹wÙiL¿ЩIarÆ¡Õ_ ¾ÚF’nm›FthQå]«˜ÅªVèŒ "BÕ~8©¿Ÿˆìw¡ººÕdáÊ7äd;íÀ{{µéŽ,xv Æ®G\SJÝØ±à º·V¡w5¡b10iA=ÞVÂ-ögbÌ“>d7ç¡Ôí¸Z­)^p«:5:` ÇöàÞ#k\’O±ô8Eú1IŠ=Ú£C·t?!zœàkÑaE÷o¶·Œ8}Z‰^ûè[mÑ.ÁÙV}0Qì 7v%!zÝøsê`ÍÇO üË›‹èаœÖnæ³÷—cÄ—«ìRr¾Ðaì*p1Ù*5–¢fYdÞçyTrØ4ljíƒmêÔ¡:œò+ó™Ïå%´5Úk[‡ÁÒ’týöTLkJ%]vYZgàœ3«øñGôxŠû0- Ü›UYöÄ8äg•)qÏeG}nÿp_^Ë!Þµy$¿¤}ë+\ãèÁ—žôÕ@Ü–¯L|v'Ÿ–BöVH¸w ³-mAòe)¿-ÁÍÆnnaÓ-AùÏEðÆc,Œ ޵BWÇER2»-# oì18f†£îDÓ¯¯…ž½:‚#G½œÒ¤]*Yä츹œÆ¥òÄÉ· "~"¬V˜L µ°•âs@|É*r¹¨6Õ.Àe…äSž%ªÛ[À¼õ?ÑûÌ|ZU™CÚÖ൭þlôîùT»}‘ê‚”ƒ#éó Mø¬7 "Ä8î¼üO~‹ä_™>íU–†o‡}`ÉFºw¾.Ô…‰ mðõz>hNP‚pÕARÁp¹Az¶u˘Ï~ë³TÿíU¿-8âP æšf£Ý–*›–›B{Ù胩0[â_.ªDäÇQ­ßž¤>\›nÿù‚œö@¾´=6cpÉ‚æ%fÊ&Ö‚SSÅ,X;*=¸~KëaüÁ©PzâÎ ]À´¹ó«·`˜ÅKL:8@F„Ö ec̰\ºÔ§÷ÁK®¿è†,¯&Õ&“;?Ó˜ñÒ1°Z ¶Ø£Ç˜6Ø^’ª£ñìã¹üøï· ä²¶mò¦CEaäeN?8‘ ËÔyäÀ%|œä Z& °¸ö Ù8C:ÒdHÐ’!T,?•¶h¬†ô+ÎH6¹Ìù©Ãó‡¾÷Ñ9Pãs…øÇ®¡Öõq°¿Ö„]JÅÙÝ)hµw!}+r3Ãæ²^Ð>i9YÓþŽ$ÍœDoõÎ"U4xq˜6Íõ ²É¿ÀúWó/‡G¸Ñ¥»[èÒôÞY‚»'=Qü¹s\Û".œóm3$/?3׸S­»1´q–vYª±{·ÞÎ+Ä„-ê¸eú$|¤y^ü¢€á6(6}e¢j…|Ï­Óè57•-÷ÀÁ¹Í¸Eô,l¨o£¹Éü¡õO>ÒÇŠJÍ å¥³6Ò‰ƒ6|–{Uù§oì·ü†IþÎr$i~KvvMâÒ°®ŒX Óפðc×FÒO3¸ÎüêÞ²k ùðÞU|¦^ ™zFáp ol¦nÙB¾â°9¬~t’öͤ?B3°%ð3~lvö…w*&t±“7*ß~ŽÉôkÜU>$Ü“ZOð¢ëÕà™`w>ìñp0]âLïmÙD‹ÿ h›Ð%‰:ÔJû]ðÛShÜ;Š&õ\Á{&züéð)4rìD–ß«‡[jeÈ5… ’p‰ã×­[¹Å ¨}¦NÛ•q\oÅTšÎÝ›Öñ‡cGÒ®Œ¦®£ô÷òT’”Ìo*ã;²>BÏûóÌ/Ù¹ŸÏÞôÆç'`4ý ˆ¡¤˜äÑ S×KN 'Oo|)àfÕ?±Úc-4þ ïS»¥þÜÔÊe¶Œ¦÷K@¶;7šûO£&qUíUôž{ýx|?jXÞäÃ_åâß-ù)ÝHÞô`! _t‚ÚL—㉧…—'ˆböÚDª= š`©ßM†í¡L§ÀW&çÒT6 ”æ«c“Å.êäLûó{aDývqÍ{‘(PMüsˆ½öwä}˦òÝ[Kð¬Åb(š#qwÂxÊ{G:*ó*Ø9\§Ó=S` ×—|1¥?­ëXÍÐI4è–"½ÝšÁ²ƒ&¢qb18'ZP?û¡t^¹$Ë+€ÇRC(©»ÏÜÖÌÀ™vý4¶‰Uö]‹’q†´OBœÎ7V£ïÍ<ùÜ—].ß“V R¡ÈÂ\òé—"ýÔô€ö<ž3”Êé+Ò-íÑ´kYŠü‡æú|f{:zÚÒä˜4L¹‰û~üÆÌ[§põÌuôã¬=,óÏôû|•˜Í×m:ÏHî¡áôïÒc¨;´…¸d}°ÑJnß ïüEÙ[…U,ßñŠ­Wç3–À¹¯¶ì«c^öIø1z9hÙ¸ \íKÁÏĉ]ÇÌ™‹i‚žA û}…X8æ´ýòÀ8ï¨ú§%ÖÂç÷È×ÏdÇTI~ùí*09'‰G†•’Ÿ:'å jÿyâ?kHêÍ€ñ–Å8ø°œ,;ß#/ˆ‚Òþñ8ÑQË:YðYvp“€)ËÄá鱟d­½Ú§°sæÃiDÀ.ÒÞûZ°~Q?˜.ç(QžÆ+õè3m]WbówcËAÑõšïÇØZZ.¶¥5-r¬Ç9‰˜“WsWuZ9°±‹ZqO¿ßßt’Œ›ÆuWFrÉ„0QÃ…vï¿Ë_M€wöáüxŒ7Ÿ+Å2MúH&LWчFñ.drõhºö«=ÑÒ…ñA s®Šj'Ç@wßžþEÛ£³™}gÐÌW9ç`ÆŒe‚QrÇVt —Ì:X%_B&9Çѧi§¸BØOLª¹!\´x lì|Š©Î£é¥5+ù͉ÅH[ÞÁŒöHVùÔ.y¢"5p+a$='ÖF2Þ•Áç½¥¸v\úýéÁú§qoæn{hôz=ÇŠžÂbÆ™tþÞ¤ˆ·í#üÑXž(?–*+fÐÀ¬@,r¹*|•¯BGuÜç×9 ;‹8CìW=8Üñ€E©ë@æÞ1‚•ùLdÁÕÛmU°õÑþ2#‹8É­ì)îÉCëíE¬ùYL3K¦ùsO ÅçÐg’ÁMçÌäAeðoœ×xCÎ~Cl1=ã/ùÉñ´Åò‡ÑçÇMè¤åi¬Õ3Œx¿„?‹O‚L»3èc·Þ<Àx•ù¨ëfEæÇÝŶ”9˜·ù¤úóÍ_÷£Ï&4P w¡qChý7hø:„^]¤ÍE7Š€ò" ®¬<OîN‡9#•I[¬6Q;œï/_@ëõTùçãj¼Óø+›4åÈѺ“¯¡æ1¯O׿5P¤o@_¿'b+ Ÿ1"òóLèìüyÔ½q¯ö· õ0û‹`»×¶@áÑW¸ó‡ýw/Þ±@í¯÷¾ËmûÙŸÞ¹\?þ,ö¶ýbZƒj²—ÊñÇ… ‰ÞÞÈšþ6æ¨Q’ÔeJÞ'hÐZÎÆßs zÂÄ'“Ñ)u/uN‡¬[£[cÃ_^žJÿ4áƒNk$Ó2Y ¡£ü'Ñ®°ä wöWPpQÝ„ÊѹDçc3¾j?C¯G©}$±Kú“n€¯º%–NïÅ…Ûà£æhZæ6î×u‰™ A0Å—:X™Ã¯½—ÙÇ ÿ»”„+`Uð°÷õ†EÉ•(5\•–Íh^L1¥ºSËP|~ÁËsÓI×±DVpël<Ùw,…&XÙó{Žð÷S ?ð6¯NÁñ#$è²LGZ:m/Ȫ³÷9tÍÑBeQ{ð½° å pôPnKÜÎß)DIÛœž(£ï†ØÐûŸ'peªa9Ç6þÂbÑKT9äÔ-ÜGW[´–­Ä¬Ù×1»0ëâ[Ì0 äîéÑÅ‹`ø1úôš¿ÿ­ž•©â¥©–|¼U6$[^¢¾$Àí3S0ôK ßmΪ×e¹rðN¼d7•þ|ïÌ×=‰œ|ðýÂ÷OÕgiä‡aÃ…OÌÜNšÑ¹ˆ;óü©Òå:vÔf(]X-K^O¡mÖ üßR’ '|6àþK»ùæê»ì"¸9ó׉û„Ûƒ†¡š^(žQ¤²ã¿²£c ¸Ç’LJ`UF ¹4™äóB(nõÈNǯV#éÕ> r÷‚<úü¥.&ðñ iœÏT(žøÕž$àY+>&3“/[û_u\b »Ž‚6¯…{©ÃùìÁMÒ± IÆ7ö< Ê2¯g^Y Ïë¾bþ-~{F&·_n ‹2cØqXÝûöæÛâÖ}¶˜h§ÁBTHhº)=}w étdÞîøŽt„LâË‹¯€JÎb|ãµ^ ö¼Gí39Âàg^:307fËNHÐQM¥8)þ/D¿œ7õEhPÛCf¡ûO£p#2g‰¬¹ug‰8sí?³*Äû¥ÙjG;|`ã­5aùC'¾aóDö+¼ûçÄÂ|ÅJ<6_‘ÿ~sÊoã¬ÀÅô¦ÜCL=è‡/ÄÂÝS²è‘y$ÑöʤiÖoXñm5h%Yé/É8±y^yx ~5¡Ú¬vÜ1+˜lýµÝ„î8|Ç]¦.懷޸—íX·§Lì,ø¢V}Aï`>¦ŽZJCw|aIÍÂÕ¿Þáe©JT™Zg/ááÜkÒ)¨ùÓ£ÕãƒÕµáyž©NŒ¯qøˆL¤»?fÒ;-.WOÛÒ©r[hÕ£AÜ)•º)™´0R ÚV¥p‡²j¼—åFç ‡e‡b¹SN?(Êš“­%&½Œ³Ž×€§ÿ1¼tö<¶O4WG·1‚뽟 ]dYj Q¨I“òºq¡Ÿ9õ9©1™CÓÏ“ð5Ê|³‰ ½Pz_ p®ç¢ªKöQçT#ïT…G¹>§ÇºÀØè6«ô“sñw¬hJ6,—éB±U«Èýíëé¸GqFþz¶^c‰éBØÿu ^‹Sƒfü·_«Œâ•sòðúåËdvŸ7Õ»„ýÉ#Á5mßÞ܉2#*HÙÂb6œß•/Àå™¶P£þ nˆ'⋟c0=$ü•HÄî¢ œPò¼+{&ÁÁ0ºæD¾Mq­‚æÍx¨Ú”uiòÅ“»À¢ê,ÎÞlWvà¦k1bíwÜ»k9§§À`åL:ÿCߦ‘ûûü`ç? >/ä0fÊÊñÚßmDô½(¿9˜*$U¬%X@ö=“UÛTXúV“Î0¹„fs\L‚{ÑûGr\iÈ(.÷ÌN&— uŽ>gv’a0nw5yðBŒßyò }~Hsz?©fî'zÖ})yàw’oz†úŽi8E‹Àé7—áç×:æ•ckšNãAµÑ¼ke!î’)Åbö¸ícÈÜ`+ŠñòíØ/¾–×Ïu†±Ï@T|žÑá¯~)ÐQS¿ÙÄIäâr¸¸­•E¬›¾ÁM8Å3 ‡”ÝfN;Ö`¶ÝÔßS/h3à bÁ~UsLÉ:‚þ#c0lecCº¯"Aǧ¥ur\joœa`Ú5ÔÛô3c*\JÎrø,Ëv¼ÀÍáa«Õ$p­-€’ÁiW®eãâí…ЪkÈ×ÍT¢:+€¦™£òÔtS^”¡[Î0Ï8Ž^³¿b½ CõsW`†ý9,—ާôï8¡l@.ÜÑV3üÎE¶îÜj ;iÃo¯ràiÁ p»f5Æ ½ÕÇ—ñgÄŸ€Ó[_ òä!tû ¼X‘‰ñ1Û!`ÇqxûR‡Ê¨ñ½¯^³ìÑЦ¬Â£CÏ ÇÖR\U?È|F¨bh‹/ÚÚ©Á{ Q²èÙœÞ=ˆ‡Á´'þ¾ã3ósŒrî±VôЋ´1£Ae«"öYÅ‚Œ§oÛ@Žg©3•²>ØYƒ¸¦+_¶©!j°Fç4ø?þMôS‡pÃq(2]pmhõ?® û†ó3úØ·È*–%\ǶHkЀ£p×y!ZìØk¦éÂCñŒHÞ!Ð]ÝÃ\ÃdzoöaGÄ@yihœô-üjÃ)ÝïX®¿Š™«0V2n>Ye·p@³‡¨]†‘{ãÉí‹úÜyÓuô:ð LZa›o™ÁÌÀ .«1ÄÄ©®+N3,¼GÀ¦Å·k ‡:€éªÁô§–ðæÉi"¹ª ^‡f&UW&ÌrºÎA³xft£à‹oøž§pöù9–sK,·>ÅúŽD¬}”î2df<¶­i vï¡gi öøW°uÇsá鳋$ÿ¥/¼NbÚCka¦Ú2|s¨–ø*)p¹7oÙ© ø¹ø"ö4VcÉc 6äâ²Lv€Í6oÃk6“„ñæôò´t£ˆÍ#yRÏYß 1ª½"žôåLdù5züói_KHò„‘ìÙ†,äÏàçœ x?pšLn/,´0¹-£èä›”æ–Æá‘®\hÎŒ†æ}5¦'gÞÞÙäþ"z4^ôìJUÐ.À’~ËctÎk˜Ó7žG}úI¢cP~ðø”‰qßy |„[:ozÆKŒ§‘É9lÓÔ*Öá&OšgÁÊwiðÐ÷5,Ë*Ä=U'¸›×ìÌ;×dGÒg2s¸q]´~¨×¾„ bá´CاþtàÜœ‚;£íÿí»:8¯ÙFî›ø•ÎÁz«cìä?Þð0?Gõ•¡½çe_¡ÅŸmŒÀûž“Ð­Ùø{޾ž’¿¦;ð\ŸU‘‡ã[,ñ›æ24vÁȼÇýñõ(w13{"Nëq¤.M+X* ~Ž3îª+‡Ó%ÌvÏH<úrnšûAàüKg\=÷Þ ®Æw`ظ½x«ù…ð½ÃýN‚È÷Z\Ê2±µíĨföG1±?rúêØ°ÅYDoT¨[æÁ°lS®ðˆ…mGÒ·»GÓµ(‹Å°—w®‡éç>OçÖá âÌVѤjkxò^^—_Ä2-»™x¬_Íèéa|ã­ydÊQ1úÁ{¼‰íΠÍõKdéw#èÍÆÍ7®@·œ%{ÖdKlµÙð¾A»… &ÇÔí×ùÇ9ƒl{ä{øj¼ŸŸ{¤R?ÌðÀæ >ãÃ£Æ ƒÁ¡-ØøÐFìžïbá:šFå„åy‹øï‹{é–¡Q˜¦u T ²Øï&²xVºp6€ò®°¹ß çnÇ€BÉ^¬œ/ÇRpŸi<[dÉÇç:¡‡Q^‹„§"ˆkÞiíf¯g] Ab‡ 5¢hTó÷=plŠ¿ù1WPÑþÇ%ºk{80Ÿkù–`î|~uÅRŸ7 lÆ*"I®C7ýL|úz”ž-i‡~ëãèüt?m`¿ʫÒè/Ï@Bé$¹å[¢G*ØÏPXn ÅSn˜p‡ÇBvÂï53ª¥-v±˜Ú{<|ï’SûßúGhÇÂÙðeêtÁ»=pÔ5³œˆ|";½ä¡N; îÌÑägùß0697,Îü$¢quÁøåe‡’›d@ª@vÍÖ:;¼Þ‘€ýÂÙÏRäÛE>íL N\ì€KGà[nÝÆQ=ŽìôÒ £8”yú•\Ûl³ô%!ÒÉG¸vQ¬¸p RMÃ!wb,¬ Ë­±×\ζ¹"72¡k®Z`¯Q7ŒÀ“æCx}ÔGŸÄáã¹VÍjî$bÌEgáb§Q|vC%/Wçqµµð¥·v[?ó£{Ñ35/ìQäp$]`)¾ŽÙ¦‡™Ã½)…bk¿²µ“›…êu ôä þ[F„¶Åî„ÃÖÒüÅëe¸òÒyè ±a* ‹]f,ÉC¿ªÃü²˜4LŸ•ß`GG¢ÐÏ>Wp¼ ëׯ£g5;`Ö™d&Éç¬N†}L‚íÚÍ̬“ºðÝ!?´ÐmÇ|i tÏMÍaÜéã&ÌnȹdŒv’4ÛˆóïOcŽÂ/œÓ¯È­O|‚µïa/=rÁ™z ÛúIø÷côù± øu‹%UȈ oî² ï c›€¡cù»÷±Bs?1ÝæbW¼`u§8͉Žåï,bvÊÓùúKÙ‰†[dœÜ;ø‘\HÎüQä¹JcQ2{9žÿôîŽZÂã èPËjÜ/³œôÈĹUÕ:{ Ï »Çm´õ¨C:ˈËsæÃÖ¡±pXu8ŽÒû@bû#ÈŸàܫÂão§à)çT*yâÕšôÞ+›·>gŸuyõíC¨ùb¹ÚþUþÅ3›¿'pýJÖ±Ññ‡yŽV^ø0„7ºÐ”¢ zÍõ ¼Ÿ5 <öô -ë§pW1zʪô¾Q¿n .ÒòL0%q.ß9;•·&¨V¤B/v±› ôŸ‹([q† è”ãLµ‘°gêUXgãÆÓTú¯Ì—Þ‡ž…[øšì(z‡ÞÏ`HÌT~c£ Û\ÖJd¬Z!ú¨6›° ~CÉo[P5–ã–ª7ð§È˜Z©,á³™ Ê€IÊH“$zãQ¼ØŠ^kØí?]…®tÁ2þR¨îâ3ÿ°Yiø±ÙÄ’ÖãÇë>ôÄ“}Øl–AZ*vàÕ!„›¿^NÜæŒÅÄ'À3rŸ_XÁ×=KÅåC©wÁAXå÷„ܶÎÔV1õøs³®„¥m¢xʺ'Ÿ»„MÙaªJ ÏÊ*…•ãp·ENÔG/Už ‚Rõ³Õøôºs.愯“¤n.fGG…0¬‰™Ígi‹¡wx/S¸˜ ÕÖcÐÌ£NôJðõ~’ذ^ý@X4Ó-¦l€šñs—#L œÊ'P¦_—ªÚ¿g_ÄÆÃÔïC¹Õr',›ñ6‹ÁYlxËI¡‹Ù;HÝ—I-/&t„p#]tž‘ÏõÞ„“«%èÇŠüss2*N 3¿cÚ`7SòðÜ´}ô”Ùpp;#  Ð%Ïê1t«}¤E³fÝ€ï«rÁÚß»6­æÂÉK¨Æê/pup<íµ€Š=á Õ1Œ¼ªÂ%wOáaeICŸL ¯±â^]’òª„¿‡b÷ã/àubj‘/3ÏBàn)þqPþì.ƃ*Wá×åùdôfsž0Pþ ÏPÊ!|·â›±GSÝDxì]y¦[Bh‘•W8êEiÊ}AÉ+kn¹Â ’¯’Ý‹Øužs\æÒ@­Öì§…&Q؇[¾íƒUjwY÷ª“Ü·1Dð H¤¿ƒ0¼ò]Œ»‘Nû?ªëÔîôþ;7™ÆE†P• ^{ô^ôoF™×Sé‚]3ñ‰PžŠšÕƒþäó ¶ËÈýÍ„Ót©SyŽl+ÍkýØ^èÆg•<Áí¶’ü ,—iŠóøˆñ'¸âÂ}tP|*ĘìÅG ÜðÌæ5ÑŒ§è:¡Ì¯<Üyû;ãG|YW…#—ûÝÚ«©Õ†—ÐâçÍ/¤Gסɰõìu”ŽON[vÞ½ÚÚqÛa_Ç*%l[A=*N¹[s(NoøýÍE¸ü2CüS”.i’(o=–ß½ŒÑû"É®k®ãÄ}&jз6<µ=÷ŒÑ¥9tr$xÊSÌDx“z>[–õœéMß ¿iãã’¤šb Ñ)KòËœ¨IöPZ67UxoϦ—Ô„W<·s¹ˆñ`·ð8äíoƒB}WöòÛúSrMD&6= yZŒ¥T(û»fd­iÝ¡‰uŸÃÓÝ…0ùÖ xô3V‹¿Æœ#«ñÌÛ»B²Ó7Ú[œ¶g’·* <‚sy 8úÏgë¿säѨ—‚ÐàVl_Ö Í%‹±ÀIul5¹ÏÚkLaÚ>²{ÛA¾ý[kùµ–ÿZX›ˆ1E$pØŸQXm§@žoaLã=ÉiĵKÅt¼6’GÈ7c•óhh ‰e©+spɃH–ìÏ,£¢ë> /._ÇýrzíÆ<ÉÜ’lßPǦô4’Ãogá€,Çòšdx´yý­/ÍS«¨™—Ù}ŸÌøA µx€wKJ×LÿŽ–;1®k(ÙÜò ¤pÄC |òi$¿ó"ƒ¨ÎÞA»äé‘}—ÿÿXª×#§€é´rÓùWMÙËðŠH‚×^ex¾ì,ÐW?ñ¾S Ì(VÀ–—žDý#”}WåMOÑ l¦¼ù>göA7MmRñI°ñôÅÎËz¬¦ñ9Óñ‚Cü«pÒs!g‚*x<Êžœü™!“1âXíß5Ó.zsF µøÍ—kñë?t7è Vú~dcÄÊ@aÕo(Nù;ßVÁ˜¢‰øf·*Üp ;¿Ûclá”Ûjlð}^5–: ±ï&“[#®{Gœ?ÅáˆÝµ°6ª¾Íy³G¸m¶#lwþŒcÿÃc =b-YsF»¢=ùúë·ñë÷&—nË˰íŒ.}›ÌpÖJWìnj†…þ–tó߇(NTxòÈFÌJ|Á”7æ»ì*§hD'y¾€ì‰g hà=¶ õqð‰(Œ¼Ÿ« ¡£å'>ê÷`‡d¬íè}ÃNíqØo]“ÖËâù’J²w›%Ë®>u¬ùºoàõýXQ“ J•—Øð[Ëùí2¤¤C’Ûoi¾®„Sýé•»Útª´ ¿}E…šÊDñù?öÏo¦h‹ÒI˜–TFÿŠo ÿÃòó± JÍ­R˜ È·àǰìñ´µ ãg#:Pï¼?þV‰¹Ç<L¼mvê7ÑCªTjt‰ôÛÉØÛ ê>S°x²5ÔÚ‚â9“Àó¯"µ¶M.¼ÂâÈݨ¼I ’|fó1Ï󈽒4?”Q!Ü¿> œøyfëdƒ4BAc¹5ÜÐÇ:},èì -Øt%U´n{·Çñ?ã×<éâV,›Î?€ìŠ*0[yE7g¿B؃cØï¿98.½ÇÕÏâøÅ+ ñÀ¿- HJAïš×mšÞ«.‹šg¸ýIŽ?Or÷OšÍñ#'ª¥ÙÇÌg8닟¼Õ•&¯L€¡Q?áŠï\˜®Þ zbtK€ ßc¢D¼Ì--~nÔøë¼†N}£ÇbCIP™ zXEðÍ77rË3„ŽJ0äƒÛ÷Ñ…_×СáX)¶š·lÁiŽ8ïV:œÞYħô­ÁÜg¢ÜÇ]—÷šÿk»¥œ¯l¥&Z4q0Þ/²¥Å/­è›6¸9,‘Ÿvòæøò ½º ¶oÝG{¼–ã¸JY¼Ú4*×ûSǯHHz t>ì Ögð û—ò¾O±Tnè1𫯓è$zÄYzZwú ÁÔúÜç<¤Ìxt«Wn&xÃÌÏfôÃûM.´Ð¹ +R\ØÈ=¨^p |ƒäèœiO0Ñc&žo»ŠÊ×GðÝæ@µ[qÞê?xaA8^< ?ÖLäCÍ5ø&¯·äsbÙ¶ TYþÌW¡~³§Áær7¸‹BxÔt‰ÿ>ަä"¤e|Co÷ÈYZMú¬á¨ˆ(ÿº×KÀõ†.øÒÆÎS;ÉáÑh´Ø€wY\É\Ùb°×e¼ünLÃŽžù!½ [­€ZÛEøÞOP¯ä–¦š2o±3È¿Hñ yç ½JŽ? UDck [,t§áC&±¡GÁ÷3ø¦^—¼ð7z ££ébŸsr÷ÉÎAÒ"Fgöl‡âfú"Kª"&“dë‘dÕ·)°K­’Ã%à^ ‚ö†éX‘kJ¯ÝüJ¿^ã½m/Yàº46[u?´JœÆˆËKxŒäÔÈ’¤âé.Ó/¤×ä.Šðç0ª¤¯û Q»@’”ɤ£éDÉJôyd‚IMÊØ]»®}&Ϯϛ+¼!áô20·Û½+ó~ü6ìÊ•©Þ‚b¡ì‹#ø÷û?þ.ÁO–>DÏOÿ4{¢2NÈÏÌä3“·{éØ:´º¥ßî‹FïémÊU?“±óÄøoíh’êVƒG ‡YÛÓȾ¯nD²A,ŸœƒÐ†é\U"ªaÖÍñÂQ&•x§î;ì?2o¹Xq_­‘ü••4µJ2ã'»Â`ñ¾ñtÂò Œx¹cçÈãýÜýÄÞÿpÌýbbàÄ Ÿû±Z$ô Ñ£f2¶hÛcQ‚×Ö”ã2çÓy›Sñ@o'« µRÃé“–<¸5½†v'ðÜŠ:¸¶J@}‹Îbá~¶ÝiÎw¸Ä7íÌ Íz×pÝŽ/è]’û Gr¹Ã…›(Íà×Û”yÿÙQ4)÷[àŠ]+áz-…³ß¦l;Žh•p–PbÑ |¡¦ÄÛ"Gðÿyuž%u¼-úªÀÇán3]ß»”xöÃ=ìVr%î?œ®Ùñîÿh„°®£|Ô†zàßú¶$ŒË¯<&\ñ*.‡,fûøÑucí˜ÛÊbð,<‚®ï­¨ˆ÷fwF ØéWŽþW¯8rD`¿r/›=ÙoݸÓŠl¸õ…=мž¡á‚S8µx šV®™‡Pî3"}Õi2סïÞu6=ê¿%‹»Û éþh¸>@º~?ƒFM}ŸL!¨í%‹U[…OÊÛ!=b8ýpÈ †~ŸL‚g®Ã2öô´« Íy4Š6h-'ã èáÆYppÄ,8ÜcOg»WâqË2æi­É6DƒÓM€'R!Š2›Iîȧÿè/ûy ‰Ó‚C¤H]“ÀÇÐ ìI^![2#o%šÐš¾¼93Š4ÌGªÆòÇ{:pÌÜ(>ð˃ÖÿÁÌÒ3ä$- Ü*oåç,íhp[—8” FoäÓ­ù˜WàÄ{^gñaÔjhé˦¹žmði‡!oŸ,àû«¸Å¬Fˆ+ÝyiŽpê{x<öæÅžJ0˜ˆšÝE8å>T¶ÜCÿÊå|é¿Ðÿr5îƒEÑVšþ…€ a6N×çBÑU¼,©Oì+Ï’ß?MéIé›d¾$ZXðÊQáÜÿ_H훬Ðû$–?…?)òÉbS!úV+Ü-=ƒíÍäåøÇìre\ˆ 4éfýå¤=S‚ß棖^ÃÉ ¯aÃUnꙆ/Ž,!Ç. ±xˆ‹Ž§³—ÊøñNÉáSøÍ ~ä…C)z8©”ÔŽ;J\B2àæ_!t=‘†ÚÝt_Œ>13pȿؙá|è\žºû^œI«gÍ•·ðö7[&W`ÿx_4mï‚Âx[,˜~ çÉLâDXÆ 5 £%ض{8^“ÑâçGnǃï\yÙ“2{ÚVðÓ- ŸìÄà î.Wž—é?bá=çñ>}¯¨gïQÅ@œ.œó ^ø’BÓƒ¬ÂÖOë\eñ)ôw³#Ÿ1åÎ;nNŸFb°ž.q_á&¸,•ä}<Måy»i…} ¹Û–Mä{ñKh›úôF/ø Ã¥”èð:wï×ྞµz•Ýò}}V[ùã‹a°k„,³ö¬B~l?ÿu6{«¢Ia?âÔÚbbº0Ö`6qdãa¼•Ýã²SЦyŸåÛ‚ŽêùÌ;=˜7_Ÿ„#GÒÓ¬y–Ì^ðú)|‡¨°wa§µ†Ò´WHîÞ…™EÞ8æöZøÇ£Ñ>¨¬l†Q’p©2˜|íLÁ/"þè¿Éˆ6V.௹ò„¢¦[E©DöuH mÂ3ctqiU1Džø_Õ®UuUpû¹;fA|Êòý¥xßxbÿÇÍõß@Ñþ›°@]²N”ZkŽpO‹¬¸—_rßÂ¥(þÂÇ’O‹M@_qj°½|"B/Bï$š†$E±/8‘@¬Õ»¹ 1+Ÿ‘'æóý¡-U¹žfû-C¿%Qüå !®þ'IŸÑDzÙ ThJÌ ‡U˶C§ÏY¼!b»òY“• *þèC£‹¢Ü,c+=0@MeÍù5Ñd\LäùZÿr!œQP —¸:šhêo»P¯áóp;Et}“Á<¤ŽÙóóÛ¤ÁÐiJ½ƒJ¥P¬KÇΩl®Ã7&z?óg}b—³AäÙoÜ7óåßdoO¥`×›lœ5‘îP=FŧIù?ÍX> ´lVãéqÂCöM.Ng“Àæä+²ùAͱ™+¬ž«ä<¡·Q?Ç'ËÊán±Ë°Ujk•ë&ß~êщr’˜±&žlvŸlá’ÅlÞ»ä8ƺÜÔɵ°}WvÑÊÆ|(ì#ÏvN„†k†ììs:ëeé;¯I™†üÝ*¬p‰Æ×m1m÷P½·‹[gÌAM"ÇÉóEÔø‰=<®ŠËø ƒHòäiøÔVׯ†7?CìÛl6¾»Í õÉ„7Ü<$†%èB×`Èw%j2i©Ë~úü“,Î9…mž"Üû|2.Ý;/SÌŤe.|ìÅ"\rû2<ñK¯C·Ø#2OŸ\†¿²Ç{]æßÉÀØ. K³ùNÕõxIÔ™9›V㎠­¦lÇÀJ0÷ý›&ï†Uµ~¼XMOýÈÖÞàèÓïØäì PrȦ§ háñ„5ÔäåÏrÉJé}CðҸŒÝXçˆï%LùÔY¬ñóB’ºv\/‚‘ßiüŸHïK5 ¨*€ê©ñ–ÃáôØÉh~@b[ ©˜k[i™z2Lœc ÿË—®Râù19pñ6ÐÒô@°[9HL7‹€¬¨ÿ´D×è—áY›…äÒÑn ý¼ß˜½ 6 BG̃ÁÃy²kö[aíï1xâ€?î Ipî³'±„ÍNÂTcCþXӄĤgBÍksæ/F·iÆa·Ã8~gæ}¼pžmIug¡—œèzÇ0~òDLyÍæ9sí½‰j÷ærv5Kz°„1¶‚ÛÛĹâW0™)Ííú&Ó(ÁwŒ]ƒO%isg.\³ÚFæ‹Ã¨À4 YƒøùÛxvÇ w6ÅÖð©(7Ìîx©Ð6+jù· ÔìŽÃ¼Öa4±â.øF Š_`È‹°ÝgoL|¡Á¯áê¡ ØÑ Î¶ßÍÄË;à üù)"v:v;œ…ÇR‚/ G¬Ð§_ÒhôxE>í† ñ™:HÞB?|Þ EÃÖáËÊ«äϽÅlCão8òûvÝ &e9p§… îÜœƒ_­$¸—Í5¬9–<¼u÷1GznZÞy² –º¯tãà®§Ü×ÇÜ—cRv;í–ºQú# y§ ]$1Ìw_b³m–“\ImtÖ*˜ÐiÑã¹UD.$t⪓âÑØ •¿ýà{Å_¶´ ™+§²›ÉÇœWþˆõmà•‰Gec¡^B·øÕ²è‚hòT\”Ãây¨s~­NT§;~¯ "ƒn¤çî_AÿHœb¯AœRæñ5ÂVÀâÏ/÷`g‹7ýÛ§Ñ]Àdy™tð°G/Ìùú ²;£H¨Y Ë|ªÛBôÂ4è3À\»÷œ”Ý.ë•0ͳÚpîx Ä=Ï‚ÔwÅð¼§–èxˆ¥e²üÄ„Whè'CíÚ̧Ü)'N?Õhÿ‡–u: ô+ö°÷wTpBc¼®gâ-ÉKYQºæ¾Û!‹&8ðaW³Yay$lÔšþ*&LïÄÿæoØ–DLÎa¸)¿´ª‹Ê_…8g‰47+¶•ª|–qYØz®•¸ßÒ…ãža©ç}rÎtU²%SŽ“]qp‡ßxóÀ²·Ý%gO›C0³ï¶Â]í·'ÙPøõ¸õKàÄàÍXõù~?¿½]„…VŸÐ9¾ ÍãQ÷B« ³†oRaBZ,˜< ƒÕÿ´ë™šæüýü ÎÝ0 »qöSøW`jô“­Ÿ2MÁ4"~Ĉ^{ŸE¾é»Ù½ds—Š`·½Üþ<–ªäÐ&^ÿÆÂ©OE÷n5—ê¡ÕÚPï'[ƒŒñÁŽUXþC†[×/ K$³qÿCÒñø ™;ç-ˆÌ  ¿Î™ §%íZåàªIÈS|“ë/\ á q*Í/d¡Y÷ dO*éùû†{ÃJxoÌ(>"<ú£ŠàoÞ8š’¢ ¤wûùk.¯¢K¦Ø ãÙp¶Jý \÷ 8¨Ï_Þý@ÖŒ æ•5ÙÅ¥¹˜¥cÏwÖGÁË· â¸«Æ–bL]2ÚþºA~ÛˆBÜÐòš{ ºwfô¾^N¿—‹ñÕW°oÅ iêŸÔxýÊÜçÙÕ,Ï“å“ÈZaCãtÛ> ¾ý4¢N3ìžh\|. ô «À{v$¹èQ¥0cZ²¹ƒýº Ä5~_8å¹á¥UyV—¾9µ:‡Â‡#Ÿ£–G ™mt ¦<‚›^<^Ò̦F<¸&Á¯§¦TÌ@½*γ7s¨Jâß~îÌôÉ%UµpPzÖ}¹£© S^ŽV8,Îû€7UÃ4ÃÇ8äíMÐZ.ŸÞ€ÿ‹T6æL0Y©@Ç^±€þdŠ·+¸ÖòH¶îÓ1:ëÈ.|Q>dΛÀÿr˜jøàí_Â컥hT¹{’eQ÷è,Þ!FŲ¤ÉÔ m¶kŽw™;§m¸(¼½V‚ÿI-†ªÀb|³µ ÞÝV š WùÇkJÅìÉN}^oAw܆¹FÊ ¦½ŒIu׳H}0É?õ5 yFãŒÚ¼ åè[1~¿,'µÉcíİàS-v­l©GO@ÛédRpä j„\í×' cî…¡7†`àyˆû›ÛÅŽg§0$w$³¾˜½£Ð|O+ºIFâ`\Þ-¿‡ WMàôÏ}pe¯ >s©ÀøÙSa˜óGN1@Õ1Xâ~‡Í©ŒÁ¯Yï!6Æϵã¨kãè¾¯Ç vÏw\+Ê] Íé¶W„A¾QljU8šXFÐ’é“P 9CŒ N•v³›×]ô5©[ë¨=%ÅU=f’}9Ý8ñåU4ø ^£Î”L¹ÿ›5LÐQ‡¨¹Â¦¾"Ú‡_b¾†5n8xLh¥qÍeTc!t]ë.&ÂP÷~’rlç} %•+qÀ½ ô¦^ÛIɺ„röZ‡4¹ ¶nÅz>ÞYظëà‹çUèi´š-þцR¯`Õà lt:L¬£gàˆ³ÍìÒ;M´úÓ 8f<»$" ç|γ…ë{pÒ¦r\žÈ™•äâ:VÔË( ¼®MÇ% ?ຒ,Ì ¡²öÃè÷C¸«¼S“š¡«¹mrlj»†wÒà ã?°œ¨»¨¸W}&}b+nãKMƒƒÔ¸Ýí]͸Õeæ±cQÈn¾JÂV¹,¨R tnêcŽí¦&œè9‡<69‚ç.Zs;…ÅõᯰÉô8ª­U…§‚±(*y ;î†Ç^ïàæÿ¾«òÚM¥Ú/AÉß\sô ‘n©D«”z¨X ºOÕÉËi(¼* ËÜ×€Á‰šòžSŸkªÞ€³ÅjjbdJO,ìÀ®'†Ôt™ØØ¢ÍŸïdœy‰œ4Ÿ%ÁQ]BÜ´ß•¥µÁz<|ZHËZcKi8Q—Ä%ú~°íö›ÝÓ‚ièz#ž%ýN$ó$;°9Ô„'Ò °°¹ž³oeo:Ø€_W«Mèµ0£ÄÚàÕV%L~•ý¸7Š÷«pýuwÉcmzEp íÆÞƒê ª¸áx3Øê+ÒÀcÛñÙ8k¢ìÅÿìÀµfq‚½1_ÿ½Ž}Ø.ÉeWt¢öO}4Š[‹‘{.°#5fÔÆ¶ŠµGJSã“écÉ–á: Œð4\Ñ5¤gD€÷´\x!C¿ß_>pué ûrº†)^‘¤gï,£K»oÀ[Úß^–cØÁ›µpñ… Ú.™‡ñÉõÌÆé|·d¸·b?—&MØ;j8þ8uLÁU9nPx½|{] =ò8'%¿=îóùfèa¢I»¦QSéO,Øà2¼Ú¬#­°*5U:æ®4?·mä:uãñ äý-'ºÿÑtN–…á–®LXl¢†Ó•ìɧ¿þü±© ûä€ë{|¯gÇ. oBæK°I,%}q]._›´ø—?—q«ûul—À%ÇòH{w†pUØn•φ_ªÛéußCµ˜qö™snh OÒ èÃ\+Ã_§Xr}"7+{ŠÊ—ÐИå0HqÉñÑ023Šû?Ý,üÑ!Ëï6:³íÏNÒ‰ Šà…9ƒ§‡AÕ•LÚèŽ*R#!ººdø7ºþ…ø?,ã!ÚÍ [±TÕ6`®ÆU"ã ãÒm6\æ!Û9ñ(X¤æƒK§¬û1Œ®>úo|’cnwÎaol R%n:Ak'ÐbÕcüHöB2÷T.Ûy;É¥ïx5ê7©óY3ɤm¦ðnû¬~=Ž?-9€sTui_N8dÌÏ™I¤íý5ìÚ€`[kËÏ-Qå ›GÓ—Š‹Ù‡)OнþäÝ?öo|øiç6¬+φùùÕཧÎ&mÇÍÇ2ñV±ïôÕýÛ¸Îm rþËTBZçñŽ¿³èëYXwÃH¥êPÿ/nÂ.±ç°§ãÛ.bƒ2â+èÁ:Iž3Õµ§Ñ%[öÀ™5…ȃ?Á ¡1ÄM ŸTÎ@©sqäIMÈëtL/9Þ5€\$7ú½»¿¡ÌGÞ¾§YxË1”øV‡ãÄôHø¸çzžì!Ê#2ѳ ډ5Z÷‹|Œ¹JÞݘɺ×ɤb=Úç<:{éL[MžOÆx¿‹ìyv+–úÇàÁ?5ÕÄ«ÚÏqʪuø³o.¸k4vÛG;7óáyGqókÔÚ‘ ­‹µ9›vÍ'ÉXÍ@H'V£9çßúÜN®é«AˆÝkˆØ:•I¾y^ÎŒ´—ÁµÍ§ˆï9XsîÜÇ«gEȽ™´{£-”ÙÉúCùâÿW–Ó×EÓ<J*¥AóH*¡Þ³B!I!%$ó9ChÖ,Ò„4( †„BÔ{öA%E"‘!³Ì%$_¿ï÷yï}î½û ÷ì³×zžuW )4Ç»ŽÏï+‹sÊd‚ötòyÌ>²Vœâ’Øáp/Ôó“RÑw¬Ž æŒwÚ•­¼<Œ55èªæ4¾à„<ý‘¬Å'Ü'ÖPø›„7ö‡âúå,åâo2Dk ùLÛn#)žxã0õš/ úÁÚôñg;þkhæ+¨@´Ê1̶dž1ù95ÎdxRÔIÔ´á±ó­¹[üa‹•IqšXuû+ÄÖB g+Q©Îbã“ñ'ÿ0¼Ü¥/´]~©[-èÖ.ÆœÂDòîƒý ²õLKp¤{<ï v=×ÇUdž’±«qàâCp;âÄž:xð%‹†Àb¹r6úlNÿ¬€ž_á’¨þ–Ú·„+  `èm,dà7‘7¨œE‹U¿ØqÃD²´ü4VÍ1¥|e8Tbìö0õzгÒjW¼à§J§ŸR€??^¡Y¶¿YίX©ãoc°öIK·ü ) ÃÁÙDœ~þ¾Ì|Uß¿‚D›-Û¶}®}2—†?´§¡Aùз4ž·e¡Çßñ¬ãâ¶È.ªF3¿„x¦É‘i¶Šì\ïYìTZÈù÷6pß›®¼‘óövcéÅ4¸Zr>7™À£Yçáð^:¼ß £k$F­RÔn˜+¶ÍH%/TLøñ/öY@qå<˜4ñ ÉŸëCÝÊzÈëº@,¿2=¢Ó-‹öÐÐáüE¤¹Ó¦É½›ÓÙM5s[Å=Z¾±ýñ³á£“ =úg ôï[€›wΠau6èyû(ø—wÁÖ‡ƒû‘,œö!Múã7jrƒˆv é$÷‰=ÉN^!tÚ¾—˜{Fšzpëí|àø7f#&NïİÉÕ ¾O–FXˆ'÷Kóf¡}æøÞÆ¥õ»ÈÙ–ß` Ä§´ö¡íÆ¿pÉ`uÿf$|ò†Áϧ¹¼–3{zýžT˯²åïl(¾¦‰=ýܲ¢›Ý÷ìÆ°º3øvH$*üñÁؾ›¬¬i±ðõdœüÍ”ª Æ %žgr>%8Ë2‘³t¤á}w r1D$Ÿ8Þ¼‰Û'ÿùOsÚcšÉÚÊ4µq8}$ïÌ_i¢DÂPþÐy:7‰Ì‡ÙN*¨t »%bù‚rzé^ýsmZ! È}-E\ÞF™\>*JMá¿_àÊa§è;‹A‡®-àÿý=–+—á\½r6j¬"n½.Cƒ}s©ï°Oõ…xIhB'íÝ9ØŠû}ëarC(Xõ«á$¹lÐ솣bß¡Á`ýp8ösd…Ic)…R¾ïµ¾H Â/ ûPÊ[‡^­ÛÎN‰}Ä,[C*uòô¬;_“PË©—¼±¢û'ç°9»ŠÙ‚¤‰ÖåA# —aÅ+*ܯ‚Ÿ—AxÀhœáþãÜ@_3ð ùf8?É© '2P•ûCöÁš={Ñ[g,>ÏÄã«÷0×hS:´îXåÆ7–ÀÕ¬#Dû÷+æ±Vòý•é+-+ðN„ßß øáŽ"œÖBŸ¶¾‹¨N¦¾ð ÄÊ›9è8VÜÌk _J1+¸#äìèšó­¸èí5|A~ï› [CìðùÝ5TÎûHùía[5ÙhŒE»ˆx>âÝæc,¼ÿvÉ›cûû®5-œíqÓ¦›íR)[ˆÿæe‘‹'hfÍ20Û'ž@ƒçbêÖ/ u ø³Ú7(õ¡…ØO·¢gßuà˜2UnPÁ²È··ê|ž‚<½NÍè>{Âý×üŸŸ•ìRŸ=w¨˜½ÜûÞlŸG?ŽÓàxø.HÎÑß°IVŠšè{½f¸]0œ¾X±‘Ÿ¼£ÂtÛàÊðá¾áñð¡¤q]h(Å/ßÐí’Ø¥Uz­Ø2â5ÓꛋÌzÈ´Uqø³f:Î0׃·%i ’S ™fŸ„cœejVãh‘² & ÁC'‡Òåíš\×1“šS .g¸´«:/RõÆöûÀíŽ-w~¾ ªn¥ þ¥ÉÓæw¥Um˜¯æsÔ1Ä­Ï·‚–s#JʱžÉ¿v5¼½Œaí15:á~5fhKSç¸{Ø«-KÍzÝ„c·ý´É_ Ä noÒâ2šÍÎkO~`_#7Pö( b.»£õƒ®¼1÷¦‡Ñ‡RÉrOºXå;&½ͽÖyà®8!ª)ãÁ›áL*ëSѬÁe[7Ñ †RTL½”݉’Ã!#ÆòSrãèÝ‘àg‡" rÃwöw³¨*FºãÏÃ%K zOºF5Èѵ¡ôÝ#ôÑŠC‘Ý&ôÚEê™»V›È}c1 ÉÜ~Ãþ/Þ¼cÿy|íú •Û2éñ4YõÖžo«÷¡‹¢µè“m_ð¿ó›3E‘¯Ðõã°8 š»™ ã€lÊ|dG¡¯]–JüîÄ#7Äèµ3A w÷(38:‚ljï…-6sø:‹:Xq&ˆæ xÛ m))%{šzf ­Wéa«Êz¦¹\JùLÇeÀ¹É7Ñ X}Q–*«ŒD|q‡(Æ6BÚþi¼5Ì$vvAv匜²ˆ{•8°ÆÛÓ‹ 7l1üò£úÛš4˜äŸÖÀrKIºE߈¦¿Õewzc±3Ì œñŒ¼|ÏYÌ¥îk[qÕ u>“Lå ãhê˜q\âÔsøcíÏf=– £GšA{æ_hÛó›å·Ÿþ:¬ÞNàÕÀ!¾&Ä—Êi‡m“hÞæ“¼«E ÖíÌB9g6dQLtÿŠó°¤-ã¨ø€.Š\“¦åcS~>‡IÓK'¢AÕÞ½kˆà•µâà„ Vé)¸ð@¾zõ°¹g =r³ö婞û|ÿÓÖ<ŠÂþôTÒör?ïÊ``wŽ.»Œ•wÈD‰pn=ÃŒ+ħ‚= 3a$1g sv çíSÅApú11V1g*ÑAÞ$ 2²iøh†)¶(ͦ·Ìä‚{×±|uŸ½ã»@qçA£kŽ Q'6iÓÉíí©¬r ®yóÜ’•¥ÓßH–òGPÊâÐjj ~Š8.üTÐÉ\¬AªSJ ýRpwõJdk¿àèm•äM±&³‹íÀ©‡`TÖà‰ÂÏ^ 6.1ä9I]àw]@B‡LÇa6·ñÞ•eØYªÇ^íÃêQó™Pú¡óŒº`v¡ó† ^s,ßkÚÓâ—Á¶‘'pšíMœ=Ú…9û>C±ã}ÄÈö à#|q×—è;­C©Ë«ÝbîËÐtÁ Óö´¡Ïº&œ:ó2zÝêÁ“½)°yb<ÑêO½4RÐá]n§0©M‚†ç´‚¶ö5HæFÞßTæ©ávкï+ö( øÄ$m>"ðÓýu¬f£K ܯ3Eóó…A’!>ý(,r’£:9[X_i“°"å ™«ü±Rdmú‡½ŽÓ1U¡‘d=Þ‡¿[÷BÀ¦¸?; ï¤ÆÒpÓÈy;ÜežÂ¢D £ƒaÝ´M )ƒ• »aÒ¼3ìé¸zü±£„éÄë¢ôs[þyòe¼¶Ÿ~=Db|?£òÚ^l½3ªÆ+ƒÜ$ šû¨m0f9þ§«VmP¡ë+öÒvXÚ„©\þ±<½‘}MfC îÜâ.Ëàüûýl|üTøîX‡£ ø@ìx¼ú&”ÌJÆgâ×ÙÓDkºnj<aƳ}qøºçÂ'.¨>ZŒ+¨GïK¦ttM>H?šÌ-œ ¼º˜º´&íF#VBTHóÇÛeiêƒ|ÁYYYf5`ŽóÆÊPý[žìÊ¢è8Óœwïwâ bÜgª"Wmr¦K# !`ßjlùéJÞÿÇjÌ"I lìp’Æ=I7Ù#é!ÜgºæÏ1ÀÓ£nøµÊ´÷s®rÖÀŸ¶SÀyIjíÆQ›bHÔqî¼J–ª…Øòî§µ¬~v–¼>Á&´~Ê6ô“!¿^R ¦<3¬K0¿ö#«¾2–yÄöáò™~dÆY¼%nIŽ%ÅC2^eC§zpÙÔ~˜c¼Œÿ OÈWŠCì¢p>Ϋ¯œÇeM–=d ´XLÇ,j$«_¹Ó†IO¡çÎ5ÒF–Ó·ÉiìWR9*-3§Kóôè¾grÜòB4öªò»«*1nb*»½ö,ÛB.(áµ^Qò¤anؼWÚÉ"É\Éê/'ò$þÄvzzϺív$m¢æ<$=ùS7î5ÐWÇ“©rT ×—‰@M_xâÉŸ'?zW ›«¯!L9‚ÆaÔNbk- 7†ð#·Rù‚Å”—懑‰E…l®·;¾ì šþ¤+Î Ö¦¥¾ï*œõ#&ºXÂåÜÂZŒ‰f`†Ûì>˜€©sGÀ×çåø8ü0[a™Ÿ®¥òÌ&BËëÑ •~LòÈi©*þÍû$i÷cÓfGáqSzÍì= ©s店üph~0ÈÀBpðI\ŠÏ/®$ë«î‚‘å[pÁs9mï‹L: o~Izb1Üí)”š€aÎ*t¡Ý[éÄ Þì8D.-œXù7>æ‘ÙÅë‰×$¼üB¨­Kå Ÿ3ߌµ|ƒl+nÊãÕ]ä`maDÛÌ¿`œ²¿»¾†[ÿ Ÿ¶Ñc A˜eDÏfIÒi“g@†äþ·|µðüÃÿ~„ ãñÃð!¼¢Ì w,‰CûL{†‹êòg}31ÜI„.MSçb”iýÅöPƒöºœıJôñYúa† ÿ´ÅC§Zp_iž Ïæ_˜Vïd¬»þ_B°aêÁ‘)^XæÊì—CLïjhn4ârg/€÷ùVM~ˆW.Í¢{>ºð ïGãîè$nxø7)ô͇aözü¯ê&~ëÚ &ßA.²ìúÙ&Ìž}[ ‚qcã3Ôî¡‹ üäãÈ4Å7Ìnßn˜:ˆ¼¡¸ôð=ukÜkðki$ÓëâáÍ£zDèŸGÁCe1ŒÞx7¤S¥k€Ó{Â’$Ø1WMÿ Ëþ|nlˆ¤–Ò|ïãXzÄ1 {†©ÑQ&0iÌ4ó¿Á¤—ˆÒ÷‹qídЊ{#Žð/f0uóq¾øóª«vÊ §ã£Ù-äÝUöÇÌn4îÅ;L½‹XÕ=‡muZ«ÍJÐ.g+U)«ùùñ—Äóðh(¶‚J—F´]©Í”& 2«Oâ—šað~Êjz{ãæ¹lÍ2 dyPÎ\üA Ó€ÆÛÝeW$!âÝ0lYHWëñ§q‹–aJ›TïÃóm‘hk›ÌËoÁÄ¡ÆdÂÓ‰t¯‘:U¼âÌ®?º·«—ÿÃ:éqnûAVøÂ½XM>çà ºñµ:÷ùŽw¾ÞEQÿ>þ‹çG ÞÂ7¼zl<1°ç3Nï´;ŽßÆâoÅv0{9M•¾‚º÷z4Ôú ;§ÓžÙÉ<å} ¿P¡ÁC3…¾Ä….S‹âʵ¯]¿Ž€‘H%ÓˆÇÉ+ìȪUûظ)ÉxTcnSéækØþN‡‡1|é3,ž77ÉP©óÅøŸÆØPñ4Æ}ØÄ?=M¥âqVprÑ^”«ÛíøöÀ+jèM’ÝC+£×PuwžP)f½ÏïðÞ6ÏâîÝçðö‡é¨[8‡ßî×¾*s„Go_ÆnM£õo%iùðaÜjÏ4ì/¾€ä2ð…÷ßÌÆEҶǪ´mèØ¤“üñú`Øô²™¼K™ô2=ŽÆ´¸Ó÷ãõ±èRy„*‡]Á¾¨ƒtøõ§ëß„Zî|šÔt>a½X>[JßN‹¡ß¾eâìª:dÔȱ­Qs›í'F•'Ùö5sÈÕ¡ù¬w–(ßé¸l“¯õï‘»÷6l¦½TùÇuįI_4! ÎàD¹'Ü)Šû Õ h̾8 ó¯V³y¿¨òî±üÆwUù.’LÜw åËãXô…DöÁù&ºG“ÿ|§ïD°ºaŠÔg¼)^¿Ü…›>&÷—ØP¼qø±¨¹ÓŸÝ©©¿Ÿª(¨:} g‚jq¼JL„Zzóñ`óž¬ò…M%ÃøÊûñº"–¤eI³@_6¾è*N_ ¬¿µ[bÀæa s¼›Ç ÂΈ",î…gÀ»—ïœ ÍiPÌX¡¼ñçgcVÓ¹=XžÆ ìnûHØ÷+hå&…[œ‡—'³UQ·ApPîP˂շl¸v¯$V5ÊÃó-Jt§k ƒy†Ã´…´±¼¦¸=«8DÂfˆ]}9óêíó¤ïædó—SkQ×5˜OHT¥ã~w`±PÚµùb…d2öÉuøuÜ͇-†t\Ñy5º’®„VŒ¸z-o¤;#ŒÑùiõøkÏ/Í*ÇÆÅ±è§ÅW^)‡”ô2à'NŠ‹>TCÅ„+ ^âv€T•oÁÔGò\®Û ÿ¨ZcÙž‡p¯Zš4¹:ÂãÏb´çk±þ*Äí½á¡ÁH.¾¶É©pF5YÆ~Áý;ßÙRÕÁ'| ÿù:Ÿ?"Igø ‰IØ<þcC Hì f—¼QÌñÌÄ[EÇ éÎ",½FÒ ›/Á¯×‡ëùдþ:~W1…î#ùñúý°ÛU„VÈÏf‹#Ü`Ì»r¼u»›ft ×~¿bÆÉÌóŒ2~¨’¡ç-£IÌ’,|i3œì‡òÉF´ØÕ–`ÙÞSQÎô 1ÜÒ…K'ZqË!Q -ÃŒEèí=–ýЪbº•Ѝ*‹¼ÞWEîÛ…Tº+²u|hÒŽ;`W“ꤲ¼‰„ÛÌ¥®-VÔ$¯ú×§ #ÜM¹]À1¢z2 ƒÐôN=»Þ~j%$iWËù 2´þÓµÖpi‘+t”æC»%ÞùQ!±œm±Ø‰]ºoa¤ƒ/›a‰‡Pm‹¯Lp_’ÚwáúÞ3ØV)E×a»A/†Ô\‚Uy=áæŒIãÏÂØ¿càÕ0 Ú]oàö­• z66èyòµ²à¢×&5²×>ÒÀæ7@åe<Ú|rJU¹¨¡ßúÁuõ?ç+ð÷¢ÕB•äÓ<+fOÚ>‰f"mβ¡» “ˆ÷÷Ùíš\˜sÁ’ޏ¶“>»ZÌ&«ÿf¯Œ&Ï´vÐâ^ î¿û4»ÿÒŸ¶è9Ò3ÆÒ‹íxüç7PÓÖ£ÎÎôÄ}.z è ÏÜvì}ªèãIzÂÓúøŸ>U¬zj9ð ál®Ü¹tð^ÄŠç1Ìmj?%|o@ÿªù†ØÌ¼ û.á±×/ðÙ-Çi¬ôÈ’œñ+BðÓ¢ÕØ¸à:þç÷¼Î¦ m.dÇi{Ó»ÚÈW] Ìz??K¨]Uˆßº3l†¡aÝrX;SœScgåÆGvv¥‘ßRaÀÅwEŸ>®¹ô÷¹Û g/YÀ7 ª‘ÉŽu¨óŒ[[ ôpû¬w ·Ÿ;ÀwÙ*Sm)%:éÀm87Æ[­Nã¢ø½ÿã/\v8ÄdÞÞ&B3uܧdH‡üÃo1¼w¸3 ¥£tCaìË<\QÀ1úIȰ0ãøw¿÷¼ß…;'~N‹•()r·þ,ü£uŽ}‰#kñ'1fëK¾¡é4N.Fˆrò¾Ð8Ó‰ªb2>±šH7-?Îâf*@ïÐ¥5âiBáf¹9°ãÏ-ü0;­ÚSôùzišbYǦ-äöϰÇ|Èg™aX™ l¶²÷ÈðËŸà¡PVޤ·ïÞa•À÷­Õ½­„kKWÀäSÃióqiª"®OÍ­<øFÕ/¤×’ Xª;¿c4Þ?ý‹¥ÕQèþ“l·áÓ÷Çb>C wáö—Øec/ uÕÅŸ÷öŸ™k¹Nºèñ‰[™Ï’ñ0kÃxºäų&Ñ£•¨k{çÿ=ªý† bâcyèšö‘¼îC—îS›ƒÝ]QìF}'_ÑP =c ¿!¶”‹}o&µeŸ`C@ N3„ð“*P<Á¯W}ŽË ‚¸¦O%\­Ó†ºÕœ¼®‡Ñïß°qÕ}ðvÕo´Ú µôÌK;õ†üaM2Þ÷Ã:TèÎ-Úv=wYîÌŸú–0ÿ@o<¡ÏpÂ).]1FˆrŠüz¶võC6Dã9tÙ á¶âçyp9s–WèƒíîK ŒY„Gì$ÕÓðQkTÖd Rûܵº•-¨íƒˆ}}°±ÿ@~óN,Ìl„~ýëoQAA8N¯0ƒx÷`=^𢠝ˆßÊgp·±HßÃ|—˜‚’;‰z¯dÖ]Âñë¨_”:ŸÏû1®ì:8ÔŸÝ{‘lÕ´b(\œK…†`h?o, ¯J ,·†‡¶Â%ñá\(!wº6T-/"ý&×ÉáàØ–èCã2¢üSvòÝj>õDÚâámÞlQã$³·æäQ`>÷ž0~~e{8¾ì^y‹?-F°8G]²þ®®9DRÓÔ¬ Kÿ}…ýçDèɧ#©Lh?[íV ÿHÞhJÆÉK2QdÞÔVº‡²²oÙDÓùü›™ÆŸ>W·¿€²ËŸaɽ‰d|0ª*¶â ƶÏQ>ª ÷¥¸aj¦„¹.F a µ¿=‰ï–Ѧ—þý ®XÒ¤=z výS“À~î5Å<^ 5«N·iXRØ» ›@ñÆ]Ü+V'ø~2T§ ïÎ Þln¹”#pÝtŠì¼à ÌiËJÎ&Žr  3¡Î`¾éWFK® ÖŠ£N²4Å;g°éI*LžÙ’þÌ~D5Qy‡3n-‡ôgQ󩋸pýLÞÂu‰d4z‘Çd¤påYu.ñ¬Æ,„°«0yÆA>¶*”ÍOO_u3•P—k%çV’5›Ô`—ò "÷r,VV¥;ÇÝp§*¼²KŒ÷GÃw9žÐ$üK=öŒâ;å/Sj°ßF”Ç'Úðru\Ö-ïÿB¹Mñôã GRð—xûkð¬-G`¥Øh¡ÖbiöÂx×]wn³Î]:. 5Pd§ÆûŒÎ’V7-Ôž!ëŸ`JËbŒÉûc_Õ°zÏ*°ìù ÓžwBÞ×Ö×+R# ñ¶'nûdÍuÄ“Þ2\ºL“›‹y’Ñ.z´mS‘y=RMmð²a=Ñyt\XØößLT;¬È÷èû8aS6î¹N0[…î®åDaí’ÛäaòŽ8°nçðèÅÁ–Ê |â&FôeÕ…ó†+c•Ó-œ´+\0SCïÏ|Í<´ii²8‰ƒ 5ÁIWžÒÜA¦ÝIõcè{‘à›NƒfæA†·Nâê©‚Ï–Ó ýöcÈÈþ+0;ZÅePrE"9Q;%‚/ Þ„[bè¯oL­6ml +m0Ïg(zUbçù„ÁzX[{pGþv,i&6ÜEÃ6mnlÎyxÅ턬²Øm-}—®bΤFpõÓÁóÁsã$h|Ö;^Zã–šyl“  {”D ˜ÒP³Û8,N”ÿ\¥IÓãåiD9Ktè%2~ ü\>Y[xŸþ9®ìK(5YÐg¢¹Â´B6?¬âWÓÄju:j0?µ•OÜ« ÂŒygï1bÓüYXs­ºNx¡â{]š/øJî*¥J•¹ØJ~Aí°£è×¹Œí|"‰³¯C2=‡ùqf¸Td?.μ-[ŒJ•ºøh¾<»hLÆwÇ`º‹)ùì!ÆML3°$c=+Ú»Ÿ|šTÊÉ6TË@Έì/ø9XŽÇ?^ˆeeIÜ(ݜֵۡ܊‡ä£q(têí]­8Ú­Dubç©ÚIÜ4)g;û‚ÓIŒí|µâ%¨ ¦2óKŒ•ßÈÆe@ͳ,ê1l»üȼ¬ ot.Òg®¾ó ÏÚÜ>Ë@ÚÖ)ðOj$מ'ÆÇ™(ó¾Ì¶Í¹ÁfÚ4à–ñí ÚdÎîÛ­aU˰.Y â£Ã{ÓC°m¾ýVñŒ.\¬ÿz ÈÎG¬÷úS09‚N|a5¾ Zþ’˜U‘#äÒG2¡m˜ó€êÄkä¾{Îxï&ìÑÞã .P’Å“Ön0Ýv~šE6ÎW ~…ÙØ<~°Ò…æ$ ±Ëå'Œê—ám#©Ö½`ø>ã zleçté–±(;o?´Oò…S›‚ÂDi5ØØ{H  UŠ¿,ÿ  ÕÙ€U>¹ô^–ãŠ)4ö¤ vÒ£æ÷Ïâ&YQØ¿x;¾w4¥mÊüg‡-Ÿ“ѦkѪ¨¸´cUìJ'¬8É×Aå­,¸t¼!aÿð~ƒÝ¿ÊÞ¹Ÿq8#²o$9ÃÚÊ83ù9›òRÜ?呾qSpøÁTz [ŒŽtúÂŒ'®¦ ŒéFé3¸Êá´<3¡Q!õ(½0EOÙÁvÞ]Äks™pÀå*ŒÌǦØñêsqÛû·_Âg‰¯ƒÍÁ‚`‹x¾ù=ñ :bEŽ‚ÛR'0ºy9êFÉ Îlš MÄpWå(ÚüCßTàOëã˜Ö/G¯øËaÜm.Pr†oöÖ4±PƒW‹èR›mÇñü”¿¤`©£î•ͼ‹øŸ¾ùÕ†“°ížNÒJa›âÂ0+>ÕrËá­ƒ ê±\\:òÌ ¡[щ”9‹Ð×I^\^ Í% J!,#Ú¶7]L½¶lv¦:Ø(Ó¨rØp¯k–8m¥$Û…ÁMñÅüïä<Èx `5‚lúþ,ÃÊ¡Aònú?îmödÅë\ð{ÑòŽÃqD,¿^–~§ØÓ*?¬~€ð“$?Q„¶EK¬cÁÄI”œØ!Ž3AÜM–ŸoŠ3Ë7gûgøÂLÝT|˜QŠÉ"ÐCÍKŽgO¾ #oU¯v0D‡ôûp¥û6,|6ŒÌºµ„ÏÛJvë 'uÅK'ÖýN xJ œx3~°È„L×7ø¾m3DÏ© qKZPq\Ú-$ç'‡±Â(1^‹³ªÄí+Ÿ³é]ÒüNàoüms¸Ý)–~¸ŠÞËr.|!Z;ü˜ú¨HR­2G1iб¦ýŽóf7°Ãá…¸³Æ„E³DùÀ=Ðóò ~×XÇ*þ„ÝO‡Ð´±üŽ_+„@ÑÃ,pÏ]Eú­$:\‡Ïßa‰ê^à¦ÙÐöo„/©"tÂ8kŒúœ…/Õ%éÜÍöø"]Šï^Z ±£Þ’+{Lñ?_çÒ”O5%útÙÒ@l’΂×CçÙŠáÒÃ!Üñ›wÓ¼‰Ç¦D1*VÂwî3éݰùü¾Y6û‹Û@tÚ|zZË—8”÷i2Ñd9 ¿êÄ'U‡•£ èn­8¹n>YÈÜ:Ò÷&ÀX›^ጽ­8uŸ6º·ãÁ“pø‰Š«8S‡(¦¹f2¶¦â¾iÐÑ0ˆ“äȬYîªÅÆ™Ò &vür÷¨;ž»ž"ÜaÙ[èy›ƒ[E×cr@2ÿfðÓÌèéñ tJi/÷çÑÕjÔÛ"—Ÿ·-€/.9¸îþQ˜üûlÙ‹Q]éIižúë*IÚEšo‡¥ûv"ù\¬Í· )­C½à0¢4¡ƒMÎ3ÁÓó×уØoßpP±~DÌ·ë‚Ï÷—dÔ·*”šÜŠ£Û|xëþ©4õm ­¼9Èý§áÛ×ë1ýú' qÄ )1´ØòÃóTyþ—¡üZB |T;Ȳ§ù ”º%×y}Ÿ|Òbѧ³ñ¢ïfë6‡ÛÆâ??Nªšp4• PLž·i0§º ÂúCø}Š9='Tâub1ð`èH²l»?õÀÍ4™~ƒ,ÕÛ—묄©»†ÁÁÑ%ÌòÞª8Í=^‡Ÿ.anpUÄ•‚!ï¯3ß¾šò…£ßÁ°¨^RuBפ£ ‚(¼vãɦòZ9]¢ 蟓§°òÛ.ë5z?U¸øsóN‚.“-€}«±\'žWÌÎe“ìá¢ex¥Ðš&Qã3• 0Ñp÷ˆNcÐÉàyï 2Ý›±ÅØ€ZD©r‡ãë@¤ÙŽ7\£‘ÛLAS2šúRýµ5Ã,31¾„ã¸Çø¢G¿©?që9nýv2ﻌšü@1z Î÷ÞŒZ“V¡Bß=öôy%„?Æ//Re÷6Mçù9lÏÑAÎ\RAêÂ46Òæpb©Ý·G›Çüé¥sÌ:ð_îQ–èîE ÕëP$Â3Dd˜ÚòQt“ö"†Þ\n¯;•šó’¶{Ð7—mÉD-MÞ®qŠÜ*AK-æõæÕ°Ì|Ù’AS$=Nª™¿EžSàï4ò0"w7úMñ·ú´[Í‘NÛ{ˆÛæ—ñýIŽ|üþN8à2™[µ jVe{*_³zYmž/9‡‡rð¦€'ÏëfAA`Éûî?FÒjrðué*ŸÇ—òbÒ”#O ω±Ã…ñÜ~Á[è,›©½øÈýÀ6~:=¡ §nHÓå.°t³+âíVÞà}üÞßeÑÙ—q<Ñ£Rõ¿™ØÓGîóhcœÿxO°Õ’ðo—<èåcÍø÷^h4Ü­e­`wôÀÿõÆ¥!"W•Û.Âeˆß;¨âí8:Â3m\!ž/#ð‰e 8=ƒq&siq¶9/§,(V€k B觬Vg­<=Þ K窞ƒ‘RWë“dé6»`º0îD?W÷ó¹à#E7ÐWésxýYV_›GÇêŠPLÏÄi?ºÐñÓkWÞƒSttèºÔ‰üÔª=(Q@1ËS’|Æ<8fØÄTפsÛ3¦(»4UÏL#Ý^{áÕµ±´U¼ÖNËEß‹øƒxSÜôå&¬ª ‡¥±ŸÙ¤jC:jÅ$¬;Ó u*a‡¡ßÀm„(}R¿ŸšùtËò…çÏG‡žFoãCJÅyµ´9üY­ÍÏ Ç®RMªºU†Ÿ p»‘ C-f›?‡kf®…Á«ëû•)¦éc ÅlÝîO×ìÙËêS ûë¥(×<ÓŸ$Ý­ªø°­ ´ÇÉP­-x^R”Þúœ{ÿ0£³¨ß¼:ó¼æž€³_ª• ¥©V’ÜNñе.¢‰Ó›aõ’I(è뜞!ƒ1í]˜ò)š—k<€ o]ƒý™™ úÎÌù0®¼ò•pÊB3º\Uœ‰ª¯AW/#ôÚ‘Ë»hæúM`m¦‚=*K'ú6es‹z<Ìbu“ r¶i¨ùè†D‹£Vt”ÞaüܧÉ?ÖárÜ ;ÿ؇‚_–4åZ_ªçKÏ·ûÒð¬pš)2… Kd¨à8ŽkËôÈ*ŒN×6-‡aá§à•ž×.5ÂÃÊøÞ\ üLÒøMÕ*ê>*€×ìyʯ‰†Â )¸ïñ%á·Qü|TÍ9#†÷N^ÆàúÁ…Á½ty­2‹‹UÇMnéPÓ! }[ø»RÔ^5żî±Ôñ£ÙòJ¼³ñøŒ8ö/è…$i»«,o¼gHÝY>f·fAøM9"V®‚R )xôS;ëÌ[K¨y;yåu‘©³¨Çk؃41ºI,%Ó”0q™˜Ñ ›g¯¡ïoôàÔzQf¹'ÓÅúÐâßv¼øù›~VÜÔÝL¬û©³YY×°HÉ'³ÌÚ6‚†ûdƒœâBØ v‚ŠRÔ;fÛ8¿5ŸR¹Mèûeu?³!"‰pq4?úÈ‘*ª¯¤»g±¹ú“øe¦ ‡}ãѼ«Ï;9ò=£aÏ1x4~¶„E‘ÛwÝØ[/CÚrî'ÞÓVàÃLÓüÍå¼¾›=‰?Îk%LÚ1 ,Œ²á£ü>’”Ðs0ŠØÐ—K¦@òÑÝÜz—'›ðZº)u~p­¦ÇYŒóÊϤ0z>ÿÏ Ú.r ÷m~t… 7áÄŽ‹®Ñ™Ve ý|"ïHG“,^°_Ú£ùá£éhõ˜Ðös{èßµ"tÛ^ckÑy»ø_ß©|÷­p%¬”^rmMamÔ‘¦û—o#B÷Å4*'’w[6 Ü:Ãæùýv‰ñ%¡q¸ñ{04p4}”)IÍ>K6O> Í[ïOÉœ7µ5^aâ9Y>%%¶ýÌ[Š©4<À¯”¾ÉXI=KJ`WÈwÁò3ª°ë¦ ­^ví~ÃLµžaÊMR?ÅeoËñc{̰sûVô}#*ZNtÍî\˜‹ƒÌhå¬Xøq?K•>‚/… [¨G?Ÿǃ =p'/(œÞšÍ\¯ó ¹3ãìZ=²Ì#!P×ĉÒÁ¸Ä²ËŽßšïŽ&£ïa7ìÂ3mû!pÚb½ò Š:Xà© uh±¾ *ŒæU=0Ÿþ²‘ÔçÍdk”îT> ²U“ùßøA¼· v%^ñZüë]Mˆé>Ã|4t{žå=rÙþÉ ú[—°‘z†´ÒòSO±ÅêT[:þ¡6¸­9 Ž ô¾ñ\EQ‘¿+n“R¨å­!ÜøôØù´¤åèÌÒ8÷:zŽŒ O¦m£#Š7ÐY^Aų~¬(Iãò÷å8 3ƒžžÜØí Ÿ/æ‰iIüÅ'>ýó.þèÁ Hnðà®—Á¾Kø9¹ :zr+¼ÎZÍ §ÏÅ\½Eð¹XóCÞÜ÷q"Ž”¤n_­ðøè»¸wÄrÝŽƒýŒˆ—ŠÄŠ¡pP1 êfˆpÙµd‚M ›°ÔIAÍ«P}WOye…É'd«±ñ±³Z™‡?F—BBñ~ŒËö ‡®_Ç™Þ/™áÑÇ$91S=ÀuÇáëøqX·M먲âf®¢gJUo}}wEòoÃ_ø¸¡Š•!Øû5 ½?™·dIrñk:êû•Žå¿4A™-\]¦ÂŒ-Ï”GÙ°é *¬{3YÓQMˆôJ†¿â’kÆäè¾I‚¦„qÜ¡m”§Åù&¼¹c,o›¤Ém#eà6*ó{Ù¸ìU»~V&]ðà¥û÷áŽÝÖmÍ÷…êÒE‡´¨Çç/ؽú%¦õ£ ëœY‘"}ÛØé6=.tšL󳳉jÃpÌx½öKK›Àçy‹Ñ¼ 8޲£èz膧êôŽY;ì`Màš¨A/»œFKý×8cãh¢"GëåyíóL>céE¼]q‚:ßøˆßß á£æ‰2ÿ_jüŽùú)ìš6’Ö>œ!9_­Ð£¹³%¸¥×w˜ý%$\úÈû[Søzß»Ä2¨ ë‡Ãðñ¦\ßü©*:Ï>ˆœ&Û‘ØEbðu®ÛÓ([ê ¯‡©Ó[ÔyçuA—e"5súŽNǺÁÖìác)Ò m›Th¯Ç*º1®©»(ÎéÌܨ4¡_Di sL̼ ?6{QU­©´å§«p£r)¨ïð„±“^ž<Ú—˜BoyYБ3ö íÎÜA‹€eÔ`¨îjßd—&öÁ˱gñÆHîrv*–|Ûñtú'SMèѽ[Ùã;1BNœèÊQÏãCùÈ[Óøfø·ÿ´ÒO'È{­hr\ž./¡ãŒµW /¨IÂÇJ?°ìÏ’Ö-FFšóÙ=Ãé\— jøƒ-R8Ëñqìf:Ï!….-ú Ñãxfà|| (îkþœ£C*“é•Tµ6.lï„ mÀIMHâÔwlÿÄn9ÛŽ;VcÚk‡·õrq‰ƒ,è¨z»+NE¡’|Ý/AÅÒ¿²©\P+ËES+Q<ßÚïØÑ‹V T¨š·ý_ 1åûœv/}~]‚*Dh†MÖGme—Ô\· ‡Ü[+¨ùå<¢Ã‹<ú·ÏËÏÇÑíVüKÔvü•ƒB ð&Yó5M>w™#Ÿu<L ôóë Luï‚ç&rð"ÒŠ?‹áfr'°ãg=Œ½±ž|ÝÇŠ3.áAÝ^td júÑ7ûù¹Çw0ÑS »E?3ï?{I¹&⼉êüû+'O†ý{W¢Ñúú°Ý WVoճżÍ|Þ±OcŸÓÂÅišÄÿˆüI¢k gÓI“—ò¬¦N¨Â„9wf5ÀMßt_B¾(±gÛ2\=‚_€è(¡ˆ 3ò”2ÑÍæ"X¶ÈÐa]ªôbáLB”ù¼-Îè'r{×'œ®^|p9ßôv‚¹5_ à¤$]pl5}©hbW. Ž=Q„µV¡|WÑxÔ0­…¹ðg›.³3ý ylb¤-¸Mwİ¢‡Ih~Ô‹J¯´ä¢£=xâI{ž*ÉïkæcRp¼~*¿°u=¡x–¹iÍÇ?Ûƒ/#Á¦\ŒMzƒò'¸e Ûv¡Tnc{­Íxò”p/4 ÓN°–'±°j’³ö{H>»^Ä y°Ë4Õêÿâìc pב[ÄÆÒˆkµ<„Erô›Ó2<ê:Ö]P¡SÕ«˜“ݵהƒr=É£Ý ½`)÷)T¡Ž=6èbGEê”Ylø"\qã œhjBñç(¥{Xî:‘ZŒ Ä/W¯bÿ*3Z4|8Z—±Œ_°Ìy>vìt¦o£á¤ýypôžÎ—Ùm"ý{n oÙjeX,Å_ ÿN߆Ý£q²Ö1|©º˜~Ò=ŒÆÃ¹ 81›Ï$_)ã®ÉÖürC?Ó°œOÿ.²£g¾ÇÑÖ Œ›–s•Yz´ËG›ÇÒQ|íöiü£U<Í­§×¥§PÓr 2¶7H^Ñ<^ž0ØçÉoIÿFMˆ.߀…Ïuè½KÀµ8;lÕø‘p÷y%íÏ•ƒüEÑ`¨â@—m{ ŽK´øÊC+yÂ-êzf óË×åóbcëÔIôÚÕ¾}ô!$kžƒÅ>!äyˆ“ÇO‚— iüG-W4[Ë›Òbº5®_|R¢S\Þƒû‡tæÛ É´R—ñÝoÂé‰Wwàû²"*°;ÉWüXI^¹A±Í"|ZZ-$‹¬Ìëq¤Tñ Ž×¡Ïôh’r r3¡SegЬ²op¾¶”æõå€ÈÙT¹?ŸÙoÏW™&Ãß ëáZ©ٽݖOßYOªóýÉ…Ø|êY+3)£íT§‹Òé;§BÜc)ú6è=káá:áÔèÔP>Ûvwyñ’É>Õ‡ˆ=Ûé{›é´Ãñ=fV#(RšJ×âìgƳ´ùîiƒ¹4¾£¯Â´{pêâ“WM×Lv£¥ÅiKgî­X³ ìõ´Ë?±5¿ 蛑ïе1˜gM) /záÏc¾Ûk¸Á\Z6ÝŽêÈVòm¥GaÙ„ôVÑMr~z ¡#wñ‰* ¹vÌvƒÎ"EК½ˆØ÷¿iÐÜ´Á>Ô.Ä+MZSò5Ö¯€’‡n§Bs¯+:øÝb[é.~uh><›W†ÙÇ~àƒ%^h¯RŠN®Ûb‰Øa¹4ö4Úñ™[aɹ^»¿ãï­‰Ðl0Š«ÂoLwX‰ZÝðzÑ”õóâcTMùÚëw@ᥠ5f)$ú‚}è‰<ü ÏëC_ ØkB¯ÁBßD¡í¡fS"ÆËšÁˆ@{.¸oC¶ež5—Q´´Úœ*3%ZßkAs>}ÿyÙôd&d¬ŽÝ,Ž ¾Í0.a7zVÓ“¦Ó¾UG„V¯÷ÃÐ;Ë…ÛvhòOŸg£Ùbn5v6—Þt™üÍ]G³wx’ ,áøêu`™mÃ3ímèïÌgÆÙÔ‡¯A§ô3´>9ÙÎ`wÊ¢ÀûÇ*Þ~Ë„ÞNö¾_MöÝùBÚÞÖC¬Ý HÛšNטÁö›Phò^nl‡ñé1Ø4ý.¹¼ä f½D&„FÒ®«úüéÚƒý{ÍòÞXÃŒ'+ɨ¡üÝï$j÷W2òºüEðo¡Cd×7kAñƒ?‚°Ï˜f<‘ΡÔìo+™å*O›ÏöÃöZnwÀ˜ªÒŽÑãyèJtý6G=L¬zñÅ.&°'>°v¨ ©nÅr¡ËÔ~ùŸÄQFiØi­º50´D 4Î…Öþìëûc¸¶h C=¾Ìz,l¨… pQ¶´Ž ;|u@Øy½¡>­Ù KÏŸ6ÅĹQìæt¿º”Ø×ncÓÚ‚ù9kž¦0üÿéáËÍÉdþ¦ÐZ¿„ ä4~õY=ÚÖWÂÖ“Ž9QËu™ýoy.tÅÙÿÜ!, ¾(ˆoH‚]RUlíš½r¥à•H4ì‡ ÷áLç[(ñµ›\þÎ:²”`¨ô9è/‚dõc˜o󎙼^‡sZðÙ’ÑüvâfºõÞS¶õÞB6­ôJú®[=…RÔÔHZ=†LŠx%G—LÏ‚ÎÕ›é^ÖÕÆmüñ º ÃäªcótÞq<ÚºNÂıvÔ¾æf&‹zj)а¬ f….ÃýáC1Ѭ›OêRýøló÷qž#O×,ÏÇšÃPùÒžO0§zb„æ´VÒÚW«ÑjÓol••7ÈˆÎæAá`:c ·ë\€‹æˆð«_n¢sO é\6ˆÃ÷¿geÁWa‰ï/(X]Œ +©¿m¶ð‚oáÄmf%=uÉŠ_õqüOG ƪ+鮉Jéað(}vE« ~NZLœÜyƒª5žu—|¨rÏ-3uÕ© |á6JE Ì©ÕÝ'ôVð8:¡ÓO;\†“îË^ss$ï±_Ä3ný€!hM%?÷÷TwªwSŠšü §ÖO^°L­¡|]JN3J«×ß1æ¦66ox‹b6“3õ¤¯w졇K¥i­ÛyQŠ"Ï~IsÌ\ÂÎÍ8Ê”öˆð’duäþêTvÊ0~áR4}¶¸Äœb·ŒúÕw`˜™º÷ކïå•ð;' ÝÆlJ3âÁ¯¦Ón¹2æé> ’þ`ÖâÏØíµ¿ñŒC’0Ïö”w ¥uŽ»è5ù¼‘añf1äpa;{ú»æ¿¾¡‡ÿ7TÙRFtöÞƒ§&ztmêiâyÏ€¿5;¢RJ`ud ôÂ:Eµ`â÷Œ8pŸhž˜7å,ø«ŒDøžíHuFVЋ¡¢bëJVˆqQÑh:7Œzyéà·»V°`[0 —?¾ZðÞõãøÄÖø}sQŠêEt9“=¾¢•x vö`Hûg4¸=’ߟÆ/~Þ §mF/I`äŸ]ðÇf(lòµá¥1Øûy l»2›.|F‘1Y`˜‚ŸIò2EÂ`Y+ª!=Ö®™ Á¬©ð–O"¬[»/.âÅ5O@òé0ØRZ!üyD—®O‡MÛ?XÓ¦ƒùö ³'FñAÑ>ø~¦~«κئõ*Ç}ÛŠGp^ÈIhxìÆWý½Ïï±ükAxá¾x;Pf|#Ÿ·m†©…R¼èM ºá+¼”û=™p[û¼ HÖt|²ÈÖ%û úéaèNu¡õ.t[®/M™µ ýë»F ¯Yi@G<Ò«1•ëÆìͰµ8‚˜9Þ®+Ì_OzþO0>n ·wFñͱÊÜÔ_È<ûFcÚu´òNĶ×`èÒ=Âo»¯Aë…;äþ_[Öt{hk¤áµ”°œ'þÜæ:;Þ¹)hû§ÏWÍ“%Žw¦AmÕ èÝh‹Þñônáê¾ÝÂÚ°‹¸x§,òé·¡“¤â&|XÄ×_NÅÇÊïñú¼p¸·ï蹖≵-hÔsÉ…ï“lH¨ôÀËëƒã8IÆØx²Ú¤°B…½œ«FçOJÅ?S±s0gg¥\#÷kçÇð%s¬zÞ “~˜ÓÆÎQü]ÛP~BŸ`xÌz¬Ë*…åPµàöôד»YàScƒEžV Û?®Ù=¸è ƒ}¹o¡„Ç™Ÿ³|>d³egcø¡)&l²Ý òx×mÈHƒ=Î’ÜïÅp:sÞœÑæ¢ÇLQL* ?ŒåäŸÞ}¾ƒü3N†a—ƒ©Öú´©ú~oh¬ˆF·jÒ¾{ÎtHó<¼®q»vÍÀ CÃæc‰(>k<}· |OÀѾKø¢5šoØ&g²Ê^m²Î°"M‰xTáåÒÈGºƒï3ÊjÉ’E¾wÀlg L-CB§r«gÇ14©Á3xÜ'Ï«!N©x_Cÿc–$õ™"O/ø–Ãå‰1¢Ï¼!è6œ£úZáŠ~¹]úÏã™GwŠQÓ)è¼õ4su§]O÷ÑPþþz éôº†f¶àò—z(Òâ^Âúʵ¡²~HÑ#’ ï ÇcΊ$ôÓ(]ary%0âO ¹2'‚k¥i™§=éß@ÂÚ.a‘*ï•zÛŸÈp#x±Y”Û…ð¨´Tì 6?ÑÇš²YcóÀ¯wÀéâºýøÒL§é¤ÃÞí“hÉ`¾RÚ ×Tö3Ϲoà`„ »Û¥6S]ö ôi¹p–[þƒ¿mýðå`™µŠöùsY%wª1n‡ð¯h.Œ_T +J¾’I'åñUûMlj¼ …‹;Qßð)PÍ÷]ÔÃéw¶AéÈ4úDÞÚæÂƒ®çpàõ;x[4™¯Þ~æjJ€åSžêÿæâyaXÔ85ºÇáÁÍ!tšŒ /ºÁ¡2cèz-©Hò5ç"ÞRÔöÉIr¦È€ªN¼Áþتò¿ÇÀÍ=Ù¸çÑvܶCNl¬÷”6Ì[‡r$¨¨Í~Ôéí ³aŽêWvãl>fŠíejÆ‹`f‡°½y ÌUð„‚?S`Æ¥ÝÐtc,Ü&nã%PgåSÜüœye!Ìëñ¹ò,N‰õ:Ý,³­AµûAœT¡™ßrî{#Îùý†Ÿ")Ç©ü;ìŠ<ƒ×%˜žK,.5ÚEäg”£ýŽ~°º?—håã¿[*tÀÝ.ˇa#eP2õ)ìè· ’1ïÕ=è>çËBÇÐÆ=ý‚á?,àáªbT^&Š'Z¢ø¡Nn±w}½}]Ok/l¡ ¡ÃCLa‡ø œ<1S3ѰxºÊ`Ë<¦«ª†ËùóƃT­ÈÍ*$ÒW[PbÉV³6—™½üŠ5Ÿ³!©ñ&j] @¥C°oŒ X|ˆ„Ø—GÐ}ýÈŽ ü+…dXñ[¼s0’^J]Æ—ÍÚG ÞLgRœ¸[Ez¹ÑIÒÅðëÔ´_Žîq—…SP{³lésÀÑB]ü{©ƒnža‡ßwDâ¿x1ªWb‚¥Il¼x*ÜQÝÉÏ¡]'Ó aÎÒù`ܳ ïDîƒæû‘äÉ–#l?ˆ×¦¸#-ûÏz %jh=‰ËôV }ך±µ?ìIZfäìtnžØKVê¬Eü·SO´¢vÊU¼j«þÝÇ“=UlwÞ_çèîíð`Ì-´¨YùñuXíŒþ¹N8+‹´ ûz&=u­RW›:_VŒÑ9/Ǻð}ꢢϺ§àÞ[‰a]^猷.êð79ð7*vÏ ¹©‘B—k÷!3H‡tl6‡,•á|¶v º¿F=m>ê„<}aбƒ¼üHæçîñ‹Éõ½¶óË_7ðc 8} "Çâ~Bê;¾t’€›ßšLoÒ8²0öT<€6F$¿Åbz®;ëÿ6”V Sƒé׎cƒE4oóü5ƒXÕÚù¾=Páb9Áèî}C¹ó[vÅ‚ÈSvl·Ì6Û¥ùÏìØUƒ°TÜ*º áÊh4Îõ_›`–ü|˜ºÇ4'Ãþ±·!/0fgæœJýo¼"òß1¶R<¶ÜNÅæ›.\ý9Ô§¤¦Óû Òh’ù—yaùôS‹;;ó&Ϲ8«lŸ 6Aƒ¿¹ó Tà—–ÏðbÇRš³£”ä+xÖü÷]P’œ ü5å­–PU£ ¹G½1Ñâ#[é…Ïž!ÏÆ(Ó%ÿÜè!@òêxÛ–Í|ôµ ­/‚8ó[¸M5óFÄó&ƒËx>«±Õ?|šJoÂøè”4CϦ‡P&E·¯œÆg+ð*ÓSøïÀYfþÜdŸ±Ÿ•Ötù[7þ²b!Í)£÷Ÿ D›Aø*Mð܇±iPœu_Ÿ"X(¢ó:Dé»ëŠÔãÔ9<}c1ý~ù+qú?Í3.»¶ËÙ¥ß9)_©pHèýx•¾L ïðBTZ§Ë¥æÄÁÀËm¨•U ¡£ëpàðr¡”·!ßÛn€ï÷Òeè¬Ð0ô]èÊ?':1 í‹ägíñ¹ws±ÊM ýu÷ÀÌO˜jŸKú?FÂG•ZÒu¡žÍ*àÃtáèÆÍ¸55&ëOÀŸü¸…Ò¨ë:1ÍiˆZš4™é³ñ“iR‚¶ n‹Ð)ƒëð~‡¹À=ÙŒ†Ž˜Œk‡®÷ bð¦O7ÄI—5î&‡u6ÿ ð™_*É¿™„¤Ë‡A3¶†‘¾#>gíe`uÂbry%³µÿÈ«`ŒºW„1_:0w´ éåû°–À/Dêè0°w:Är?þ+!un›bûç:<ºñ»pí áMÁ‹•QøâVÜp%q!ŠœšŽÇÒOøÉvºfW6ÉŒðf Vêt”½9ú)žkÚ²žÒ¼b¤4äËÓõmœÔ6°¾Ož(ü¨ ­ñ;ñ°Â72&¾¦eV$;؃‚ˆq¼Õÿˆ†&×ìÙÔCŒ‡=dçF€ÿPtŸ(œÜØ-É '\&J‘¾ÛM ~6ƒ³LNÛ¬Ã0}ûhÚ9ìœÛáû-Í©»—Z)FÃÓntÌR[zd±»¨“ƒÛÿ7fÊÝJ@DZvx(è:Ä´'QýšD|çnÌ¿>¹ULç1U (ÀSëça÷î­8ÛÌDž˜Ó-ìÀÍáŒ}°…w+®þ{'/ç¾°°L’öÌÛ Ž9NpjB ŸÑ)#Vëò¡síè|iS¾¾² ¨xsÿ3{*ý¾3œþÚx,ê6sS©ýà¢Ââ>0x=AR¦´ä\@¯¶@¼8e'gñ ŒÉm¨üñ–ì ÞÆ—ö}GgéWpüN6[aK%.×¢Ð$™V{ã9a/{¦#޹K‹à?]ôÈÈ x¹•ó éí•®ôþ·`îTH ‹XI< ƒËS•ùaÕNÚ}4žO»:zuÇÁ´N zho þ§»îï?MüÜSë‚$Ì‚“—ÀÔùx%Á·Ÿ”‡ùC‘¡.ïŸ ;”î@І?~7I!á>š<:H’«õ›»_š¸9Á‚jÈØQoW+ÆOÀKüyhz˜`V*ÝÕŠ&õ™X¹4âÛ²[ 1o(쪊åU2eÐx±óÇžûñj³<ð;‰cóAFüÛ‰Õ$(O‡KìˆÁ¼TC|ÙåŠ öfTQÏšý\;$%³Q£ËTèÓ‡mµKPeà Ž]Sƒ²¤yÌçF¬õ‡£’¾4a.J>ÞDGÜ5£C²îá…[¹â–Zœô „¹déqÑ^$œÔéºXHµµ¢äv)–Ùj° gçÁ&ñxºÿG'YrÉï†äâOŸõ°Ru92Ú’ÞÒ°çæ_%ø¶Ðúû&xðå:èlÏ!rãØÎ ´àc·þþ •Ö›òK‡Ó·>X®¹o‘ñ­¼~ €:<ÚAMëµù„ªPZ'ª†IG*!wÄ[°Ð6Å« j¬e;|ñ3 ¿àmMÙ´L &¦EãØ2L÷1;]?’n+ýÀ𕱾~º jËXjð7Ç.$FŠÑëSÆ1¯3{é‘gc ²à0„lß+v@Òž³˜xüˆñ¶?Aù÷^ôYÄÎæõ=†kí?†ÿn-‡”ñDb]°+~[ö2äÁüμˆª©·±¦@‘~ս͆ÈW¨"þžðÍy׆MQ°;k.Ñ8HŒämqªøûüžà˾nj…Yáf´Ð)Vhvd$_?—‰V ŽÛž€|ÅxnÁ˜õK޳?¾!1ÌÐ.w«¾E9GMR›êBªN~‚à!¡äS÷uŒŸyÊΨÁýßXôô$vd³O€ê'?1µý¬\f צï#ÿyX)äX}~;„¿€/·:{oŸËË\q݇…´4܊Ĭ¾ÄÚ¿ xç%ŒÞie®†kþ¯3nO“ä{}]yLâWX‘‰ÏóÿÁ«ˆ_è*f‹«ßôãfú¹7œÿ|ßâoKÑCÑ0wú ž@táeçz:Õ%ˆM8&h³t™G“G–öx5ËÆØ…È×0*¦§,æNó›Y ÇOØ,„Ò´³pQæ‹P³r(¹þDƒ4»ÃBÙ8w]šv}Þ÷Ÿš+‹ 5 r!å;#æ†|w¤]¢‘ªÃœƒ'\C_÷èßk‰eŽkàð¢1ôäÄK±gër ë®f²“ICÓãzñÂá[ ¾Îø_5Gc«ÇÃÐq^ÝGvž·ãr/4¨DÝ#0jOÝÏØûµÙPc`´ÕGU+@ðÎ*Áðåe¬xüVv%ºIc!ú‘2O´´…ŸåY ìíÁ¦¨jâY“ ®|÷#£@òÉ>(ïfùÉSḃ/æ¯tcJ[Y‡r}Z9”+îO7–n§› «0`ß{T(îÇcïÎA‚A ~*Ê/ø51Ñ–Ûhü³™¼Î­…íº{ÁÇ@–OèÿEfUæâ¢=¯QRv5|Š.ÛAV¹™y«•\6–9‡:'€…Gìl™‹–¥bÜæ"qκ5Þ~|oÖ„â£0%S-¡1 ˜ý¯çIqÁýä b¿i9ñ'ʇß-ÁÇêŽÔì×~rdÇ:ë™ Õyø˜üøv˜ Óýù‹Qåðé¾>×-6¢m :)—ž·¿»Õ•ù{U„ ·#|}ÐnJ'êðm~^TèøõL—ðJÓþ4;ˆÇìËÃe¬hX½'Îu¹µ[WêvtV{~—Ü[‚YÕ謈4joIð§²ÜÔ/ü³6œ“Ø|ü¢ÇdúøùLm`ï¼V þF–'QpÕ²š«]¨^R*>4À‡ws=Ç— Vrÿ].Ãóüh£3?%=kïžÌŸ%GáŽüCÎF›\¡åõj8zYøøÙgÜz4’=-¬¹Þtc·6•Ì>€‘粸êÙbzàš U+þ ²‡.€þŒ4™à£.¥S§ó)]p‘ú·Îã’— õòX*¥˜½6xãµ…z:”JF=äϵZѳݘ9üA–RŽû¢•©Ð4‰vMŒÇç~ÔÓøuõõ’#„ÍJ+óOó2#úÂg7ø‹Æ¡Ç†|Ü5çÛÕ¬C'®?‚“´ùªó!1zõóu¾3v´ôÎsX÷@H£W¥ ¢ò€LoÔÙ-A[¿L9ßiÝéx¡¥·/_u‚óÊL´º¢ÄOû›w+BR¨$ýzõQ,S  ½÷AûÝQÔ6;—f½ý ¸ho v_½Š&Âyô~ò*|h6Ÿ×…û.?Å0«­<ï{*èõD—«ž\=kø¯¿‚Ò+OAòKg¾Zu5þ­B?ç8qЇšvŸðÊäá°×¥‡èlâïoºÒKeáäTF8Ùœò >ÌšO5í‚ ü•éÑKÙ‘>5vX{­‰^‹JÞ£ˆa!Þlý © G@ôðü¬@¦ª FÄíäMSyÞqIúöÝFp~#E5$5@ÆÃÖ)äa[ãé™v¨®»] ÀI­. è×Åæ‰yU±°4H‚ªÑèß3rEÞ’®¿lÈ›×ÈéP“EgAÃO‚ê¶í%ÃVˆó¤7X~  xÜÔ.|'ôØgÂe‰Q@¨;,kÎ_heÁ£É¦\bd œ:/€TãHöZçTüÚÕ=°npî~dÛ£À-&®çŠÎҾʑÔÑߘÌþYÄÓù7<½k'Ï ò¶¹èëtšãA‡Ï?DÚíù²™úôh‚7Ÿ«¶ž×ÑÏ¡¢_k­Ò¬\N=޵à®aËèÜ(¯4ÃÓ†‹Ë’ñýL'Ø0Q‰ùöÀ¿õ?`]õiÜ›òÿýé a—Ò1M»žŠo¸ÈÛcb¸³L?JœI¢—‡„ÑÏŠÎ8t¼&î4ªWuiºÙˆO¼6óa÷Íå«Üà¿`gì›âÌíF=À‰c´èPmUšáTo2qªÿŒÍù·…£'¿‹»³hõÑ׈K|àÇåB¶)ùv¹Œ’Wgrµ–^°Ù¿ŽïÈWÄc¸ßój¦ÜÖ_NBwKôé̱i/ñX;,χȓ NÑu_¯ã2«1´"|&¨ÕE’¦þ•\'§ˆA]Ô×A~›±´Ö¶’ÿžõ}Ù—_I“$³|~ãÌ)–Y¢Î•·L†‹ÎÒÓs¹}øuVÿE7F½o/CwÞ‚ŸsÞDëŒÀ!yô…™.]‘ÁÞVHÑá]ß±lN!o\WŽ-V¦tÞmÆùÖ|ŒIG82:8ºú¡µ_-GW­ÞŽÖw(—Tç¯RbéÏó—‰Þ®t<°ª¥Ö÷ 箃óûC’^:ëϵf]B¼¾‰.œªI‹j)®äR+Hq×Õ[¹_ˆl¿±+žMf&ßavV +k@@2[ow¯LÉŵKŽ2ëQàsÖ˜æ¤kP§,G¸7W“+;kòSZÓ0z± W˜Ò+_iãïiôãsþ¼a ×—b>—“1?Gðº2>`iS}4wmí0€Š£ö0É'_(¢ ½½ñë„<,ÇLû³ŠÒúfÄHöLÍš…o¤oV¯'Ÿ„Ã1) ~¿¯DüSIè/úN*â:*Ùž=n cšl¸¿$µ¾öFãá•ga†ÿy^ûÕ…—äY;×Þ”Á[*S¨×i̾¥H»3XÝ;6’jÐ!–IvžØÏãÞï‚Ñ¥24 LMÇ­n¢úG‡¯4Ô‡QÅù7Ðdz¦žœ¯ºÖõf€òŸíÔYú-:6$âÞ…ññÇZ¦îµ‰.ÍÍõý–Ж8¬È†¨…ÓåõKqœÇÈ|lÌÊv.‚—áPÌZáíĤ<_€:u.Pút(ê™JMâÕh|ÌQ_¨Äï5ýDIS9r2ÞŒ7òÆl/K:5‚†ß\LvºgУªe°íŸû³t*Û³b+÷œ¤Ã'Ä‹PƒQ÷ÉüÝ…pƒqéþõE/QMßD;ÇžE'ÿl¶à¡¢S¾­S#0&6+}•Ñ»i+Àü¯öüÞÞh° ;XåÌ,æ¬!»õu1h6c#ÿ¹`ÓPUúëx:®Œû‡ËËóØÃù’Ð÷%3çŸÇ~™ ˆ;NÀ“o…ÖmÛàQk6<šé®WÔy°ÇXš!%€±ñ\Pu“,^9O¨ið>ÕGìwÜN²Ã/ž™æìZ@ ‰ZE1eG*aV}(í³íÄ)£ÇáØ¡i䈞;¹;N–.Øó€N=„Õ¶W@ìÊ5¨I:ÏgMƹ‹sÀÚJý2Tø*בtÿ×ÁizE#‚/}– Õ'ÿ v$^†‘ÚÁðZ×Ë.|D„Nâî¢ta  ­ÒK{/?eÍW͸_âN𽾯ÔðôÄpIEçyUÛ=Ë„§‚DÊ, FïÜœNµ“y[¸®‚d(ÚéÆöÌ=Åòv¡ðÁr.3ÕNýzŽ=²7Xó´Õø=ü¨0uåevbMlý£Æ•Ýz`Þ[ ‘x ® sÈZö’Ýôh¥àç˜>I…ž~ý§´üDÁFw/º«ö^*Ja®ÝZ|ÌÎVaä³CØyÁ-9ÀOÎ\†ï¥DéÈ8Ú|wËsœE%kÒºŸÏq”z ,™²½²\PpQ gþAïÞAî¹å7^ëi%…»hˆë8óÃ}ð”°aKä©çÇP¬_E»ÔwÀ틎,´P’[Û!oûÒKߎñKyP[z^]a^û›éîeR?DyÃêr¸9o°Ö¤ZÑÈO^«žD–/ãéo¬ j…_ÞÔç’ô*M*qè ˜hhöêÔ¯Á–K|‡äFkfçX F^Ùô«ÜfZ?\À#bq¤sZüH¤¸V„–ñ±ûè6›;E:Œ£‰¢všç†bßýÅ8âî\šyßœú¬‚;ÿ…Ã’ˆ­‰Ê€=¹¥ÎUƱ"{.¢~Œ.›œ‡NF'…JbÇÁ§Ò„»¿€oí¿á_]?ä*2Aà13zËe:æÆ&Òæ bü Qh,¥%çr­ÂýüD|œûøÆ´ uÇù¼Ž±üQ„]µSŠåÿ •Mÿ°óßoØ¿pý"ðÔï'«%¥àµ±bßÛóU;ÓÀø£"-éIå^+ðä2#€çw¡âF *…~aîÊ#é¯qrü†uŽú+F£~vä4£í—sø÷V ¶ÄÃq¥o‚öô ƒ±J02ÌœÞS²ciKÌ 1Û¾Žñ¡)#‰Ì… Ú‰ç­ ¡Óa¨q-ÜÈ4ä‚Ó‘Ä"×û4\áiJ!“8Û"cHǘ‹¤hB#¤CW³6~˜T ¦#‹an­6äj´º+Êõå  Þxþ+D«‹AìnC,Û##ö-|í±Epñ÷Xpöù†GW¥ãmÑ;ÄÑF“Éš<ÄŽ1ã Zé¨] # Az¸½òu ?½Þ‹#dG`Vsº…9|z*ÅLÿÌà-U&|ðy²Ü0ÓwºÒËÎu@äVbvè?tèºÖë²Q¡ó&Û“ôšü‚zÔÚR„eØŠ;¦uÁÏptéÇø7Cx˜Á g(óéOÎÒCûoÀ¸žTÈ2ŒÂÆ„É|—R1¾}/tö7æ‡êŸ¢é,RúïׇéO^ãê!oqµÑuœdš= ,^K_&ÆlE)¥ß%KÎJjc Ù ÷Ó½b^|Ãv8Ü_›…*¼†Œà_?yRwÓ¥üüSegôiíbú(Ašîq FÍÛåͶ¼ý¨Ýß²—.ñ~ÃÆ‘ È“‹ ;°Í©(b ”«ÅÓù§q|d²ŒùÈh®4'š>Kw6n§F“Äè½&bnLðÖ´M &éÈcrƒ¹È“LQ”‹÷Øa Ëz†ßFäõ«ÃñÝ÷ádÚv]"+f ÷íï‚ô!SþLm5U_»²²D@þçh¼aQ‰Æñ@x5šì±)FÈæÔî…Kt2uÙªLW¾Øm©>òüzÂiÐhÛ ä@þ( £& Æ=»?2¼àr&ÐëI81$–î÷…UK_²Ì6X¼È´çÈPéá¥W÷`áˆ~q‰¤-Äè‡zô]¹}ãfÅ‹²¥i wà0úÎãžçWrÁ’HA°m##ÙÝèºKŽþ-è‚[÷FàŸô¿øn}:¼˜”N\FHòi]ÇðÇî*(ý{O°q¹žàwÔ9´¬=nFAö³3‘³™>Ÿò‹8.Aׂ¡<»3}ÂwAâÊ3$ïA yÞ{ ÕåĨ¥Z+|/úßç5â©—‡ñ­¾ŠË§°Á‚Ì-ú\ûÚ#tk›ÈÈbÒW#áö:=ŒŠêƒ–óîldß;¼š~zçU5y Ö¨ðÔ$3nÚ”KÌ—<Ã)Ù‹Q}Á˜&~§Ÿ>+0i×-̨ÈcÃF Ä] ÉÈ"È»-žžmËȩ́(º`.J²à+6Céò^¡½h1|./€µ#y÷KzÙJðË[“?íƒo}Ûð‡ŒŒÌ„êê±ðmÍ/¬5ÎÅ+Ñ?…»ÃÉü³' |‰µ™©Í*„²”—˜U° Ë%ÏáPÝ œ?¸Æ·d[f¤ã‰¡Æo;ºÁÕ탓—Î@ý—Û¨5ãw]ÂOÞ³±CV—ê‡òŒNq²\\ MSN±Šgq HêëcPÅRœàwÞ3‚QjIYÜR«¿ ÞÃÕ¾±M_:0ϬŠÔûJBÈY5²büWœ\ÅÆ©}FãCéДùÒöëãjñC8fJ"|–ã«n,Çíâþ0±ñë}tmFŽ…ëJ¨T^äïp^z˜èÇhòü«:4†ʫǥü †—Á‰--ww8&·£WÊ¡çö‡äÀ°dhO“9…9¬—?+ð=?v[O%&ÇçÂ…›Ù–•óÙõ“+pμ jhÔHÌÖÛÓ”àg@™"\Ljø>Ö¨ÇW>íðÌN.‡‚™ßÕ¹Çk2Þiò‚þzr"dWÌ â-œ{í&†>°!Í<k.ù‡¥G‚ä0}zÀ'€ø|ïÇ‘‰3Ç’V¿-˜œªÀ¸KÓ¡^M ¿K›tŠÂñê3émï(EÕø1\uª*1$Ãå`»R nø£Eÿnú³âÈ„^Ypü÷7F 2 ½:…%~âà1#‡_ Š’5^ò} —Ìc‘5@“¸:¬èÇ£{›É`<&¥áˆ>aGàžz_| Äꜫ8Y  CeBøÂgøÂ­M–¹ñþõ,Lþx͈¬G(sÇ›~×*Á+é-"é=_»á}‡µaËÓn¶ë䘲ä+<2%IJ€:Eœ2åîÔªb—¬ÒYŒ9ž·ÙTïsxnX8|Ú’ŒtŠ2M½`5mÉÐw]ëL :< ø'íÜ’êN^GìƒÜåöHÃð¯G>b¬wV„åûNa{‹‚óQmT,­Â|MÓ¾O–ŸÙŸû ‰s{ضdÀ˜g÷In†/j/¼Œ#ª„BGOg”e†“ÂäQNÿ|Ñ ÿ‚ÚˆÃÔaH|Öѯî·qóª›põÜ^<ûö!›ïÄ=.ŠÁ§Px¬hJÓŒ¢ááB{ÞîB¤~E—ßàÔ]ZYöU\A¿7à—¢_?¹¸¤ÿ <ص€/œñ ¬¿þø¿Êdwž9À$ƒ5øàe.:_‡Å©m°ø7‰Zv_`i•‹£d¾ÁS£×°¸ƒ^C*ü—ïnîÆáSÀ¶òˆ©üHÌr÷M2ù×v <ÿá㑱š!Gû„_˜©U±`Eûç[YçÛŽ¿{þ0Qc ô†CUy:–Âó¡*üÃzœÜ pÏNý£ëŽçê{ãöÞBFV($3£øœçˆÐ¢¥EÒ–öÔÙɈҰeg”J²>÷9¢B¥¥EÉhª”vý||þùÜó:÷9÷Þó¯×û>wó#» }†{å¡ÁbÞÙ‘•9dö} ¢àp“üý:ÌõµÉ±™C$L$§ª¢“Ís®}W7Ú?Ï‚Hµ—¼%’ÉØVU:'°™Ûhþ¦/Q¤®KdáøC«òn™¨²…··pãwaš¹—^Š®Ï…˜ñš+8'_ot}ˆËõÂÆc@óµ#jxá1¶N›âƒ°tW§ç›¢K©,·^Í=ÑÈ‘•ëX`U]3‹ç”ÃL¯¾’×¹3íÞ2"î»÷­ñ† Öç ¾;4„[0ã”!¤jÁYŸOÓƒ™_FÌ| ½ú¬a·xÌù¶‡d¶´“ömJ°ñr i¯oÛo‡9'Ò ~¿Hª)·Ac×$á›*"—q¯çXöXé1™Íë8Å=/¿‹ b¾Iƒ 9©ÓV£œ„ÕNK+¯®wóB8?åÞ°ß@7=úk?é´l}Lý[€Áªç¹®ÛGIÍP·$Ø‚NZNò×€öTüýËŒ®Ñ"â3 I€²]>,MeYᳩømñj82Ü ü1·á2Ìeš „©¤ÃXzýa®o¸6ý´-?Ž^1)ð³ì#rGËq–¸vIè'ø3îÜq¸vèXÿi‚m+eémˆÏã&_‘ƃ=Ü‚'607_’MÛ˜ÅÙçEã]5U(6µ&h™ÏÍ»+Mq›¹Ð7FÇ^†¨»¸Øü!.Ì; Í?_%vRê´vÏhýg%M Èö±^Í[ W”Z£Í67l¡^ø#} n]ì ÑnY½ØY|5ŽPÕG·x‡c&‚zÉ5¬WÅk­£}K>£«ª1¹ô̓,_ +O¹@ØEúéág°/Ê4»é[å/ ýµ4xSéŽá<¶N-zw¹Òê½RtÓzm*›MsŸ_%’',ñ¡Î'˜åF¨0lŠ ¥¼-¿¹©ç²AúÇ3²4þ(?{­*lR¼ŽéóæWqº-M>ÛH€Ï4Ê?èÖ›¾Cž.pQÍÿp¼Ò WÔ!ƒáÁ†ìŒ_öý9ÂLËÇӥѫÀFð×÷ ¨4fãrnІ=$¢Ô›..$ûmü¸³ôÝ2UbÃAúü[‹™}ÉLŒ»ä€§$²0]¤ö½4B½±ƒäBÙžLôR¼:`J§ßø„2z1Ü*»Гû:àml^íÁ#^h´Ô†¶ÏfO–¨2Q]s\svüy"Fû?¦CæN!˜ët ‹ôÄÙŵ‚l÷œRпõÇ…o瓆ÜPdHÎ>ÂßÖÁÖW×ó»î@€ý,ÜŸ€É‰›ÙƱÍèºXzë/ÔënV ãºÌðFþ?¾¸¬þ·¦'ù³‰¢åbˆt”c¿\©¼Ë/ʺ~½š&ž®Enš;3~ÆìEXû{㺸ç¦pùÀØ5bxêí;¾ùdC4±ØF~¤\¢‰k·²²¨“D³B§Ÿ+áfm\ŠBÍ>\av *µfb_S|U©¡Áy|hw\Vi#Œ»HTÏŒ§©ÉʸùŽ0Ûôõ=< Næå7µU±$Ïý0VÉÂîqßaütF áI—0Ó; ÿ?`ß*V]XGöž2G±óFÔ«õü•1äþìÑ€«.rt¿òžïÆÓ5½BV˜Ëç9´‡…¤è÷ÃÏÑ¡,¥Ö_àpò÷N€x'kVÇ ‡Þs2Òsi€b"·Â¹…+ý…ï=%ö2w!‚‹Ã ðù®&Ceeº¡J—®_û‚M˜ÓÚ‰ô¬ñ<Æïªæ¤ ?ḣe`cXXR1÷),M5Zæ™ÁçH˜ªy?Ýà8ó]MuÛõÇÑ‘zOjÝ#ÂÆ™ á#ÖÈ=NŸ^³”Y~UÞÞûsehD€LZJkg^EO~ȸ¾Š©Ÿ_Í.,ÙIe~úyA+þZ®Â|ÂBA¨ËŸÆ`á>-ztòÈ,ŒâBÿLdƒÍÀÌ¢zx…ÓBâ¸gÀÖyaÝY)ÖbÉ(Ï >îVb¿¸ôanôê xsk,Q9lÁ䯼!ª5Oñ©`U®&{Ò–²©½gàc,p]{ñͲ}À;œÏvq ;ÆÁad׋ñ'0äRžü†×OÝ¡ÿJ¡Å6—g¹±un4ÿ>º—j1ï7 Øyyx¾œŽï3ä™\„ìŠk*¤é}þjÙÿ_äȱ\¯u´¼$¾‘}ØZ{NµÀî1Ù2N”þÌN‚”Eûá'}Ê;nZ‚ãÆO£g¦%€dÉvzù:®NÂ,¬TpS¶{!Èšœ6À¼­í¸ëÅ|7Æ‘*0ôÙ€I†Ts‰›×3÷«õ`]…É“z&ÑMb#°yÃx(–ES§Üü{a܃ØhuljñÑŒš9¶óå.…pš¯}y;ƒÄÈâó¨Ë±§èS‰…%é2¿™Üˆª ?¦I•¿UØŠ‘0§ØU¿ÉFúl ûk¢Á´Qú®Yüªs§Cå5äT+NÓ,%ÆE‚4¬*<¼ÿƒãêO³ÓeYÊbV9!'FáôÉ_øS>£^Çé¡×IôùùÖÑë2‹ÞÚÀ6¼Jä¦ß¨Cµ‹&(ÛÒâÅpèj&;"SG'ÿ=û;öcs ÑZÓ…ã)·þÁšti íØuˆÍ™AË— 5ÔÅc‚tœ–“‚†’`Æ=£ö ”^ ´7Ö.ÎñØç°âǸ:oîà¯&ƒÃ…E1Év.œug›J‘Í!¬Ð÷"w~jû˜DüC'1ªRÚÖî,§aÿqK¾üœ|”H†Ôƒ8éÃ-,ƒØýÅ–[예×û)óvÛ€^•Z]Vá›`øœ3܆;£Oƒíƒnòî­/á/U&ýŸ+9sËsN7B²àm=¸ºQOý*ÚWÕ‘»•(¦)[l‡Ð²ê?_è¥x<»ï+K¿?EôP¢ó-¤íÈD1ÞcbÝcÊœ÷zp—h¾ú3› eiMë"Xímꥂ+˜°¹Ëæ‘ê[e8ïÓgÔñ åÌ>òÀ-\™ÉlWGÖtßË_&OO¤A’o¼ õÚ¹ŽÈd¿eïÔk §‘ˆšhÊ9“ÒŸñþÆÃtaÿVˆ_ÞN P)9[úüaÎOÎåþIãL;Iæ}®Ç÷á‡\M²Öclê2‡¹×`’ÎnPº¼ë‚^òìÚ'’Žñê°'êÓÇ¡dZö6äÐã–‡Ð÷Q¬Ûœ 5‹ÞÕù§Ñrk$DzÈã¶ ªLñ_-î;v&:‹0×Ûñ|÷7Gp½ïüI›B.%ß&‰¥BLRô ½£nizõŸC‚0Ëüœ:²V«ã¦ŸnLÈ!#H³Å퓨úŸVÌŠûÕ·\a|¨-8Œ;È*µ Øö´Í¨W•ÍÅ8Ž¥ µ¡œŸ÷ ²ûÃr´ÛÛáøDkß•„^<^ÍÐrmůׂRü Ø"ŸÉ´NëÑ ÙÛh"EEÎà~+/péfÅ 'pÍo;â²À€šŒ'ÿn}ÄÁhOv¸*nÉ ®ý ÛÕÃ;Çc8®-’œ»OžXMæ t¨7ìZ¡Á~èϤy»î‚ÐÊ©4°È˜õØ\äŽÕžE¯ 7¹ý†x÷¦~ {ŽB–zìð/KìFà úí=7áÓú0§µý›ðÓàX¶áÍ3ø:[‹É9Ç’93]à÷òV®ÇÆŠwl«Ê;Çyñk7/EmªáXGs‹6)£E¾-^h´FÅsyU¢Ê’'À‚Kµ`ûH’¦äCòýRØøv*1wV@w­ X£å ¦ñm0©7”ßñb€<õ|FÐá"ÞiÉåZI·ëÊ5fš?ž9MÒƒÁÔñôpýl¨þGT¿¯`êvúlœŽ“ÜØ‰+ß`ªbY ³sßd©ÜÃctñïl¨ËÐÇg|'úoN&xÞËÝÑ`ÛBÈXÒÕÎÇÙÓ®0ð;3ÄÒЭ.èÓð ×Õ0è ׯ¸No &øšs¶`Ú€;kzbNm>òÜ Ä4ýÍä¼|è¼GX7ð£œ>ãOWÀÖW¼K-Qì½C?r©¨lq¥º v¾7󪋦ÒÇÙfüÚüiÔuä3´küs³Šˆ‡À>étLl7X½ÎÄKî“Éæ dp›0|‚”Æ`ðô^˜ðvÜ 0 …»'’ˆ”lêºò<˜ÌÃNƒÉ,Õ÷:ú¬ë'‰ö{¨õšh' ›è*òâËG0]³Ÿ‡çÀ¦ aúw³ Šü VgW± §±rç±ô3‘‰ªLZ=‚•+ûØõ!Æ›¾ˆLœÆJ>\ĉµè½@tÏGÍ´¥OšÙK—’ëGgÑÈ™9Ð>ûÚVÍ *ÁŸP×­™‹Ge6×±ˆHZƒîZ]ÈÑ)"©ï*0º¿’„þ¹Ã_˜‹ SÙ DÙkœädÝuüÚq÷szìr ÏøÀåd'0yÏ'DÎ|,ýÛèDËŽ°u«’OÓXÍË^ü’¿oÓçßÎqQÇæÒÏû'° Ý:Ü2ã×]à*6Ðέb'ý†È̹›‡ÉjÛàßQ•=ÜuúÔµ¨¾j3ØÍú‹ïô°.ó.4…/„Þ[V,v 3>ȱ›ÕøYÙiTû·Ýp~¬P±>M¦vì ÖüòfMÍý˜cÜ¢’‡qo¢7Þqnà® ¯!gZÜQ%û0·GˆbØpôF%ÿÃÓ{]iúL·ñnM–„ۯѓNëQjtöù•CuuË"Zôèž©ú´[J~ÐN‡ûz8qG¦l÷î~@g>Añ£¤pÝ0[¾¯txÝ'õéVK#ú¥AÝùéôþ35ætƒùç$¨ºJœÓf¹ę́QŸ9ºøØÚCAi!oiH*ù¸Ò=7ÚÌ´–Þæ.<ÔæâÊqç§óí“…ËßrÇßlÕ*¸(UißžBΪ\8\è‡K5ƒ¨øx)&>ЈÇ\”Ù­ëS¹  Xç퟼9ð¹ç6Ïë½-ŽxLG97ÌG S?2”°oç~±KÐÅï;6M˜f[âôé¾&,¹vònW=FpvÇ.úoOgk“¿ÿ€ïŠ^ä6Øð”ׯåþÌwÿkƒüŠAôöe3ºIÁÒæ_ïïso›ý°>"Œ\Û«ÄÉMÁ¿–ÁÄý }(›Ž¡gXýG;æ%œÄEòîÈSì³ØÄ.¹ûsµ&>X¹qÏh5 i¥Ý-Ž÷u'ä6<@ •ƒôäÕÃ|ß³6t÷ˆ]šñoÑ]Pd4>Ÿx±!´ìÏÄã’éäæ> ;Ÿ Ÿ9p&Ð)' 74§Âݶ›˜ªÿ‹üãßæ7Þù€ëÚÀÎcaGÓïÛ –2ÔÇ/ZE¹bµCøñt=y%”3váËdC|þü0}ð¯ŽÞ¤ D. wúir”' SÞãØ(oÖÑ1êÑý¦Òáñ ¸ðB/þlô¤Vª[;¼eÊ5Ø¢OÊÜõ„l¼a› A™öÌ¢I¼¡}l³¿áH;¶M%ÂñeðµÔ’NØö‰h½%nÏs&·W²ª£ø½ã.µl™K;o³Õ…0!oS·¸NG¤ÂØÉ}YTüi'¼ém䦨hS%á#T¡rfÄų7n·M£ÆcÃØßK|Rë0 %ïíéÔ#éTà/ ¨]§_—¾àÒ;~CªZ9³|5™m_z¢ªòÈÓX ™¶Ž]¥‘ôßS9öÏ~.Û'°„í²;Ç9‹¼ÆÓ!Ï¡Q)ƒa´i2³·Œ^œ–ìü5më®úM`2Ï‹·]Ä6¯·%+L{à[‹,³z’ÊÕ©IóÁñ¼ŽÍ‘ P£Å °οAão)V]Pg‚ÙfãN8ñø0‹ØË¨O—]>ØK€À³ hH¸ú‚»˜AèiÔ‰™ÏMs*„ô}W `‘1|mŸBþz…ÏE¢,hK&f8&@úóBê?}ö˜-¢ß¾x²Ò&ЕƒœÆÆb柱‡ðÛñÙŽrøöë)¸fôbÒŽ;`šŽhrþIDЭjE¨T]Âü;çQÉëÃD÷ˆ(ñs'ø{\/Z–åCÜ·G<Ó_ÂÔÇã&,[ ÁR'*ÀÛ•|òÔh€gs°dóÐÅö ÿžðx:₳t\ÉÄM~ô©ƒ+ª9wrs×½d?Uö&¬TuàÖÍ*„M&Òtib%y¼×¦~þG”¹±åÖ=¸Oì7V=ž$Œb¨¿bøÔFr;îBj_ŽK6À¬ÖzÀ†*lšCìì§]ËôØŠ?×ñ¢OzH%Óê´Q*Éjê³â˜I(ï½–Ú;tÀË _P$ä,¸:¬‚³ ÔhÀm;âgq+}Ÿ¢þ|ºxÏ5Òùçä}Y^ŠÑÑZy ± c¿§`RV™;Ywz$®bÙ{kHSXŒ»;йí—þ üVÄ‹òûY{àX&¨bI‹í¯ÂvÓ`Ÿ$©›l2ô”œÄšvÖÖ¥I÷ûÿÃ;Bštqj3êËß©ïûá1Î:$LÑ®rNdâ ºÝ:†9öfK•ÏCˆØ|¬t:ýQÓí7”|-Dy¿S¤ÍJˆÎۨǶK®Ç/g²7Iܲ*”–íjÞªÝA4kCY²F™ÔÈQ™Û©hz;ˆk ÆZ¯ ~žæ~ž[eû.BϦuŒÖ3NÆ’q_põ›m8;t •2ÊbQ=)ôà‘DöN5ÖœÝÈ„&™²¥wO@äÏûmAg»ÀdÇR"öÙ“}=»ˆETÉãUIöý€mû²ÞïXûK±lÅôI±t×ù%tÈH&Àœ«Ù/½ ôÄ<-º\Iî:—Yv@Ý'¼ 1Û5M ßØ“Ž•æÑ¿ëwq)ºnÐ~´Ž{ã¹-ô~ÐptР—Ö®ÃÛ»ChpQ þ0 ¥ÒË ì½õðj ør¸ì´&ÊöSa–÷®M‚¢YÕG7xÙÙ@Ǭö†·,`íXþ“ª$nbN ÷H[™æåš‘äú!°}hÌÛ-ªÏŽxäÓ»»øªCö(]Œv‡´XÞ‡¯àü/ Õ…l™¾ÔTöf0Œú>ô¤õwÞà¦æö°v6–͘ŽL#PsÊ86Yá$\4†Ñ7`s­lùNÃC~£Ø'";{<ÞŒU¢»¿–²¹oAÀ®bØ|ï)ï^‘'6¨¬¦ÁÜÍE<Ý‚2Q·©{lw9‹¸TJ¿‰‡d8+ß?Ÿé!äÒ_òzZ9ž·‘6u úEXqÿmTά;)Žà)$CïehQ»ÁSHrcàõª¯8Ï=´Æ_†ä„;øxX…MœÍ‚“úC5x#ÞÛª%N*¬èéµ›H«ð$°ËoÝS uç6 –^ŠÇ´A™ˆ¾íQàÎl—ÆI‡ÓÉàÍ›ÚZtC5aSûXbz³ÿÙ‚©Š4«¹‡&F±¹ï|ô~…]“$èϾ¯Ü*]3ªÓªÅ¢.\Àô‹÷P.$uw©á7)-Ì{—__Ä¢°.Ì8ôïÓ âùv8}·,ˆ¸I²UÏá~ÒcÞq±£dfýoNÔâ—Õu”§‹J³õHÃH0Sf£æá‰)út–•0Y` u¥ð(O‡â|X„¯ `Óý"F> Ò‘:ÔšG¸¤ñùœÇx9:aÜÐØz„ Ý·¦LŒ¦â󸺻x¢/!½©ö¿|1km=W“XDÉMܱá'HÅ Âï1ZÐ× ¿;dñŸÓ-¬žnA~ ض¿áT¬2ŠM´§¯m•`û2]ê|PV[ âz\Í8âº{9V,'|×ý?ëœ%5†µ^–ÁÞ¨¯Ü­þ2|ü8×7n ±¹U²ð†gƒÓìÁÇú",ÿ\:‚gàüTcXvë“UK(ˆl§úæžO˜ÉÞ(AÈû¬ XªÅO˜¹¼{Guuõ­Hx¼7us["\è*^ÜýéN‰ sra–w!ûij¢Ÿ_J2Õ}:˜3 ü'ï…•Húºx<ø9>AõÉ}èð!wFbW.Ü5‘÷{ Þ@š¾éª†òæs4öä*æX¶ž…EzÒþ÷ýP^]ÚÍbèÑ'ÇþÔhñr6âyæ}óÆPѲr[} >)o…Tsš8}°a%'ûÑNU´@w#1vÕ=–´¤xRe5Eª$5D*=šaw¼$u=žLÖ£Q ®¹5[°Õ’5ªT¿qݸóÞlÜ™Ì<‘úÃÎÀöÉ8ç]~ð•§^ÂèÞbCÞÄ1…uÏøÓ?˜Qõ×?_ž­åø«ìOþ(ï=>KSCಗ*5Éú…Úâ­Üë=<€e:lû7!6ø&#Ök£ÙÙ8¶Ìf<.±½&ûbù$5èÕ䱤…ÙpÅã ¸ ^Æõ™ò,. c’9wZè8Ç…5Ô„q'Oií^ÔXó°Nk™4-8ز†Šô¤„»w±g\+šæ@õ ò+yTØy>šxŒu×(¡¡i Jµeg¶ï$JïÂiÆÚŒzšÂ'¿n2בfÓ r`\PˆŸF÷Õ«3&T¸®…¨° û¶…㢽£^þc Ô‹ì·ÎÅdQÈ 7fr N]@y{Aœ™ˆÕâ‹}qœþC0¹=Ž^)Â’Gࡈ ëÅ'<¸×,êåõ h@J8Ï®dÌVt‚Ç ¸ÛL'TúÐÈgj°s¢ |.ËôwaãÝM¬ý˜ê^Ï«+OćWd™ÉÒ=Pã¥ÏÌÉ'ã'=…Õ¿}èg‡d|Q^Ó[‚çS 7ao ?ùÑMžQØÍz!Ñ3¼~JLõü ,\ëÌìøÊNãÉÔÅ*§é±¼W] ¸"hb<–å§“ž”çÜ2A16±T•F­ÃÒ†ÛàœõŠõÖsÚB;ÈÎIÃÄX|'J‹Ù±»;ìÏÝþeâ]p#QåU烎.Üíé…Ú«œÖ§ ä]ÉúÀ‘ª=býrßø.±¹<³Yœm™!NàÒ0g—;Ë?®I¾W¦[¬ŒGŸíSä S…åÙæKO9õÕžpUÞ‘ù»Ä¨ÆË¾Í¨2Ý"kWCÐácN–Kàü~Ìs£×Ã9OXg|ŒÏ\úZyã'Óç§óß}e6¹A͆7¸wêTjÓ°„/µ#G’¬hÆ:5twTÅÆQŽ¿Wô/¿ÐgL¨íÎ’QUÏõ$\Âbô›E8|mKíÊ}ð—D<ç5»ïL–¯Wùð .wK0ó]­Ø<ø†ë}u­͇+bðЗ~D.Üð*ò&Ts/Ý6CͶfn¡§U;î!sÊÑUû$¼Ž Fµ;*°e’#=›èãNާ±û¤ n[Ì@pý?’@´ˆKù$SãŒÃ/W¢ü"zÀÊž¿ïåBâ&#¯ò δ¢#pïöΠ•Ýõ žS@ø^÷À|Û)èüsÆŠ_¤ç<`¯w:q{|‹ÐeŸ`éG€õf“`CWÝ0fêjùŽÓ¨ýa÷ˆ(¿–fÁ þ°^<uŽÏfÓZW±ö´,6m£/«»¿ t^'=Â3öaô¶ p‹õƒKòa8ϯ·jp?V<ã™,<!:/xí S˜ÏV Š¢YXë‚7¦ßƒƒS0Òh»}î±UöT÷jnH朗@Ÿ°$îË¡ù£[n¼2Ý…Ž¿‘¾?ðÔü¼9–έ#ÄÝÉØ1¼ãÁEØ?G›í¸<™¥ÌûKzl&³öT1xÝ›üãq‚õ˜ØëCkÞ¼Æ #_&ð~}}¸'?œz-ïPŒ}Aé)—a@1¢~kié8\D6k€–2N¿ØŒâî›pY"6 K³mq¶ôʆŸÄlT›F}÷†Æ=štöÆGà>¡£Þm}48]i@ÛyCà;1LŽi’#Ñ´4K‚.þÜÆ¯ÀXwÇZmt¡ÁÄ„E× à>Ÿ¾¦× q¹„VîE¸_ó;š8ʰ/w¤YCh·cxÛU ÅàóŒ5¸wŒ,çx?ËÉ1«ÄTR|qßÏT¬ü(ƒÉ²©Ø{@‰•Oø‰n¿”`ÏZªH¨ópn²^Àµ[°3—K0gy= ¿=„Qc\ÀÉÙ•Y‡À.õo$ xß)[°þ‚W6ÿ uÝëIpŠ2M7\ ÃqòÌq’kšuÒEDØ©¯Ê`8æ9¤{ÉïÏÐ)–Hã{Þ`aè 9¾áí× `§Fml¦`™ü wš›m>LŽÚûÄ p_óŒUv¤WÏ–@váMîáÄ*ØšQËTæÃYíägƒwž<€™ú'¸'áùº#ø2W›6qÚ¸¯"¾KP!ÓÃxì˜< u¾ç†\1ÕZ–É¥ß&Ûª—àY÷³­ÜÛY)`RZߪ*ZÛXÇžK¤u*Oòôúßý\“…Ê]¨Àgê'1PNŽºýÅw­›$~øðȃ9Vͯþâ±3½PAv3©hØÀ/4~Æ©š>GaYS&qäʼ͜ê‚.s²1ÃxÐ^5-®Çž‡Öî¼Çšâýáä·VlÿÇŸ5쎗Þ8bs%/HFÑýÄ¿?U.,B¥˜¼ã;n³‰ÙP®Ç2¬µÀó'ÆÏöã<û1IîÆŒ›ËGd‚Ë®!|sõ|V óã"0U¾§çð^éK³%×_r—R¥ðÄ—=`µ¸ƒ ³Þ€¾s3Ñ{^HnÈÃeášÔhs:Fî_LÝjŸó·ìÍÅåÍaâ™õ¨:©nø “µØ¥)ÛáÁ%î̦°Á4œ1¥.›[àKp!ŠœÃ§ßŸB˜Kc#\ ÝÏqÁ©¢0±E”ŠŽ ǧÑ÷·‚9·ØfxõƒÙeo1ä‘8ܘ^Ë)ÐïîÝ %!‰\ÞšPXÞ p u_@¦öÎÓ\„>þ°6êÃt:ƒ»8a,ããñq.Pº•>*BÃY(³YÎ]›»Î@×ßÓ¸n­8ž­)&·Öûõ¾8 ÊÞKnô÷r×up2‘#`ÄWb^ßv¡ß[S68a|o}m‘”Ÿ1Œ r]üì”*ôôûð‘ãž5Cð)æ½è4î{ý Ý‚óðø…A~üCSÚvu¬/Ý$î?ºH_†¯zƒñ±ÄO¦–;ðæ,µÒ¼ Wª0¿P¶ËÑ•¯Ø{ì¢!¹[ç´HBÔ ùó¥ ÷x[áùSRìõñ«`¤]LîU§çâÌ`ž{|ŸW‘˜k5Bno»º^©À½5‚qKi;Ê‹Ï`ugBQ(÷2ÿÇ™tÁÓ,¾Œ¥9šÏçZ?à‰H¶óÎM˜Njý+ñœ`<鋆óò`9¡&¥p°ôa¹“©• ›´õ ÿcµ7yA½±Y>¿»‚4>‹Âù·+áÀøå4qœTy˜ãº njt ôm}MËý±Óqÿë²³P5u/Ö~‰ BE#d¬cÙ›Q,*MfÝ„9[ Ñ5ryÜæ£ÙGyª?шýtŽäWþ«Â#îìÞÃ$Ô™cÉ£yùè}ÏÈM]³Æ×ÙÉ»7ñÛþz²¶øï )y–„ßR$µa|FT)ÿAßÙE0nÿm[ªÀöý:‡R5ëÀõ¦ôò{pÞÄ^ÛÖBé+sr¹/ºó7CÕéƒ àµ„¿2eJ]˜Ïª¿ndë•Á<©“Ü\G3î„ÿo;ð;'ÝÄöÔVü%&ËÛtû$®ÌšZ¿¢x&¤_ ÖÑsõR¬+£ ÕO—” =pŸxv¢®ûئúúÞüd8³f,ªŸKumìq`¸‚BÇÓwÖeûÈö¥B¢H>§h ‡rÔСü-t6 ¾0'`óˆ´ KaàDYBÃkyuÚs ×¹‘\nÛ‹}<¡èA“ÂrþÓ’5Ø’—‹“öþÆ‚}·!vš:‘ÞbfF6týlâ¾””bÔ .:c>öe½€¥l‰A½ :R2‘wm_"Ž™Æeï[Ž"­Œ;ê&o£ÿp£çà¹ùgvlåIÌ»€?Z–¬šË°Ñ­þ|ýŠm%AÑWŒéqcáܲ8\¥0 fŸõÁ/×°mÔ÷=Lök÷±\:Û7|!ò¾Ò¤3”‡?-Sà¿ßî2gn}+yey½¸ë¬û»!¿Dk6v›Ùïy×£°´õ®ms§7êŠPi™!¿æã‚Dg"Ÿ%Æ?®ÁfXô€ÁòPö4ô*jŽ àÑ:Iïnª¡‹6H±=iر÷ÿ0¢½=¸}v§|Е>x¬ÌV¹)‚{ª2^Y¥Â&:\¿pM>™…þt®‚”­ÒdF/Å™ãÆßD6jð‹½°îâm®ËHšlÉã~›æúTßï'žSUdV®`C¸ßÁfäãöLÝÄuÌ‘€Â)!Ìy{!'Öåƒy¾†Ozr¶à•áPëÖ¦mKÎBÓCmz3[„ J.ánéÅ㮲J.\à@ïThz|®KØa²¹.æ)Š]Dëm n]ÜäíÃû»¶ÛN!Ùð÷'×âEVùÀ“© &®CJ4Y¦Óé¼v-&¨<ãõrÁ#«_o·§SуÕ×Þ$×T@5nþ-†å9°7LÝÂÿË ÎÅ«¿´iôû ¸b®ÎeÏ—¥¾§ þËGó~ ÿE¶G.Üí7*0ÍqÿJû?Èüð“Äg„q!'Sci\h ·¯Q]ú˜U1ãh¯å8œãÔÏ\ûAçˆ![WQ‰Ò®Ã¶|ô™›Ñ€¯o…AãÅbtJ9…K/>†š;Ù }]tô£{ÊÔès/{´rÀ¦ÑÛÓá«A+±0Êßí\Œ¼ÒÁ²&4S8MÛ`¯½«wë!Vµö(ü¬ ´Þær{›¤iÔŽOØ‘_§Î€\Íœ­ÚQ¼x!d¯W¡zW9¾þ òùÕ|äÙUß6ÐwŸóaâ¹DSá#DŽ«ÇOZtÁ×G¤qLG½µÌ'7½îÕ4Ï«¾p wDp¯Dê³ÏzcÐ!{Ý Ý½%ùŸ M`îJ¬8™¿§þÅ+Æ0諈h°9¶áÌÉLTz:žu—Ùjô€ nzŸ]J£˜¶£¿Ý>·ãkPÏF%zʪøùérÅ3áçiºf d¹‹· ½Èq¬Hѥǯ„cSÒYè9Š3…ö€±Ô_|bJ‹öe¢¡ÎÐô1æ¿]$G37ÜæL?ÁHÖp»!H zÎã˜~Yx¶}?IiI'M‚´IYÜâ¥Yø{äTõ%pí— ÙÊ¿ñ‚çD,Hày<baçŸñ£6˜Ðé:lÇ«'ä¸Ö.Á•9ñÌ'×ÌÖØ²÷Ë·!{ÒÖH1«º"´ÞŽž!ý˜vðj_ÕG‡ãÚðz¼ õÝHƒ\`Ãì50f[ùóµš[“p››Ò$H "f`°Õ~îN÷ILZú·~åŒsH^?× &L[ÌW×\…Wޱ˜a¶Sƒ¾@i€ C7¬üÃM«Ž†0É94ºG‡üê¾^„Ù÷½…Ü• °£¼á™ý´j Î:¶¯˜Þ„•¯ÚˆJK,±>åŒ6§'¡‘…1>4aáqÊ$Ò_‰÷z|=8<gÖê:`nŸ²6òl]Ét;±²ˆ$øi¡v“+÷W?Ö(œã¶ËV㿲H|÷ÃÝ.DëÊ”™ç`-™ÁRø;Jâ®–µð*I”í­ÎÇòŽ1t¬[(¹¬.Á"ÞîÉqtÌ‚&ÜHø/çÌ¿lo˧ÉãÇ,#îqXL ˜L=¯<ƒ‚qK ðÐMRùã5I\·œnRÈÅ·®Fë»P¡@ ³kƒÁ‚³à:'\«+'!¥Î‰´Ü€<£Ïü9–NÌæà-<ƒG«¼E¹‹±º˜z„—°Dˆzìyƒµr:¬ý_7åW€A¡;UmœH6ÚÔó¤5CÕAÊ ÏþŒS”œ9ƒ_ÐYÌ ,Bk º~’¡“ <ÊÒ =P%2 綈Ú{^îñ)t¬¡:Œ?y…[Òê‰f»5èö¨^è{0Ùª‡xßZƒl9-Ìþh†7¯RØÊýƒ¸ÅDˆt®Ã™ºí°øfž­L„„!v«½ &ídÆÕ d÷œgæ5}æÝ‡Çc®p۫σÉÂ÷œË¾×8.±ëÜ$Щáy¶2sJáãñUYðt¦"5ã«C½ØgNoæ4´k¤¹+™¿g4 õ…Ãûs îT œSéVC¨>ÑŹ5}Ñí§`e]8Ù©ƒïŽÃ“">§a¸#g€ÃQ,Õ\‰)®qc&Åaû¬É”@˜Êì„ⵯñtÚÈݵ ´ßkq~É-«O‡ml59b‘CßwbÀ¼»°Ð)Õé´ÓUpsFrþl¸DøÒ…g`eœ?“R»Ü›ÜÙóp&l ­Õ~ŒÍaWë6?Q§2m˜ê&G_Y\ÁÃ:“ØÜJpê·;õ›wum|ìú é› WIíïS¨­ä‡Ïg`÷€gœ’Ο—¶—x/bÔïOî† ÇZÈ‹ Е à5•¹ ÔM"G‡Ó0?ZßÜ#{–—pkësPy©>ÉðØJÌ·“Ö~|ö Æu‘‰"÷ê½êŒ9í¹½ÜyzÞÉ™ˆ¿8ÏeqpŒ]©z­áp§¢BÿL$9•sñŠ¿5[6ùɦ;Y*аß}kÉ"Í4t>"ÅÌ;€Y9ùÐùÈžv|°aÿzÚðç\.¼f& IÆ—Ú{à`óê´Èж‡9ð…{}qF¤j,ñ!kwíƒÕˆð]\4ç·SßtÁ‡ì »à '´4ꕵXäÃ… ™À•?‡EsôØ#¥@ìÒc~eÙ{®¡ÑÃ)x©ÊŒ]Ÿþ òY±c± ÕlI}zJàšþyTšæÄRЧsÒ2áh• Ì.M ¥wô*ùJ¯>¡O|ùrž/ÚúÜ2D™õë¯ØuzßG1‡\wÿB¤R·£ o3ÿ«á#.{…2N/¹‹_Å”ñøG{¶{–*ûK,p©š7Óø+Í Eã¹1'‘¿]„IïIÇ›pYv¶Òs¸PéžoyÁM—’`Ç‹1Ù¼ ,øBkÓŠépy_ÓØÓ°øCS—Ké’5Ó&°ÅŸ’`æ£@ÓÊZçyãâ½gñ~øg¨¨7E‡À‹ä°] ½øK™ nÞMªg@ÀbSvØÓŒ)s$£$Žå†;s+¶ÌfÊ€·ÜòW&Bƒ´ àì.S2QÁYéЫåáÄÜ}üåb)xØs»• a6< ‡ÍiÑ“m°7…VÖœÇàè0¾$O¿]£¶îB4Þ9v¸“çZˆCs[¨óý™^*Wº0×ј•”^åoÿ…v¾s©¨öTª×ôî>x¼HBÐ)l²º23›y•qØM³Ý^#Y>“‚Á6Oãʈ;T:›±á»ø±K„ž°Ã³–·¯ôŒayoò¨ˆ &Ëóé‚×­\ïì “lÃ3ŠïÂÊ%j,¶e 1Ó~…WÞfRjÍüí%ÅÜЀC19öøg‰Ð´…§ðœ—<&ÍÐ¥C×oÁÔ§G1ß´ïï/Õîõxð”) ï6c7D8\ÈI,N€­'½Q÷w.Û„¯ˆ¿ÞYîÇÂø9},n§‹á· +ö6Ïüy¦ßpÊ|Eª© o›Õ™íÁ÷Üë+ø7ø:R~-(®×cu‘hüøÙþì4„Ëùùe0mÁ<Ì)Ý ªÏ^qîÊe ¸ÿ¬¹#Dã´´pfÎn÷™²Ø¤\8Æ'J4ØÁI0<Œü“b.nZ´qP £¶Š@rý2½j)óXó‰„uxs2-_¹¨N-x-»V®Ìûÿwmf¶©°Ãç-èüGÖ‸~n| DOyÆéî/ïoQr1!—ê¸i5Ìþß~ÂkæØCÊ~¤ÃŽ Uxòªòß9f¢4žÖë=s’¡¡ôç1˜ºý= ä´2¸tE£ñ:w:ç£û{¯]úùŠÍ˜áS‰eòZüå9°;õ+ü¼ŸEl&~ů®_Q%#ú¡ä¿ã;torE­ TT“Y6ÔÐ%wy†šL±TŒ?>Ò†Yû¥ãǰs³"ø7 ¼W…ÂÍËÁ?:îgXÓ‹grè¤]3ñ¯$•‹Âw‘8Òâo›A]„ <7²Æi©;¿önbwµÃ¸Cú±¸¸Ø”Îõ¼ ?÷_ öÏ.òæ÷dqßÛDXýE~¡qF|æ£ÒJ'º#è9š8ÌÚ™M •žBlŽ[pB˜MÊ‹"V;iEyì³Õ×Û~bÄé=¨ÕÊsV‰…%é°ëô0$ߟ…oNÂûë°±#—¹†BèauØâÓƒ‚ ¡èì4¼Qß°î1öI/ß*LêáÏ¿#ɦ½ÜÇíä=ß#”Æ%}çÞÇü™¿štwÏw0Ú/‰GK¯ÃÓÍ1øßÿ½÷kÁÄ‘ÃÌ+–œ¦y*ì]-Ê|[§Áò·yý¡'pdUý–ù1ìmU6ºß¼Å&õÜ@õ[ ÜÔ½*¤/o.UnÀ¢©EøXBåWœÃ5ŸUPw8¼ó}Ž@+ÿëC“æ^rÚXð ÎûÍ7ݱŸš²ÂGB×øB×0±—ù‘A—Á2ìg¥dLWèÊòo(žâ¦ ž†Ã™_Ȥ GܸFLVr)™¦"€Uǣϫ*ü±fõþ8ˆÖüîôt]<îMý¥üÙéöû0KlN ñ¡M;'Á©(_ž–º/zñÛ´ÄW£ÃU‡—9Y¢õÍUØó8O ?©@­³áðÊ0ÂiÚðøŽêÂØ9u&Ò”§ø]-ä–Á}áÌ+ ÇjvÂÇÊv²z2.0XO+7 `b…+λvŒºF¾dÆ. ¬‚¤è´ëѬ)QÌ]=ñœÆ[˜yUžbt $­ ǃZÏ9ÝÛQ`ùd ©;N±¨[ÚÌFO–ÄK\ƒË’fô =„ eA.Ý•i_^ÅE—Áó-ßIN­ »w½ ýÎ×ã6ýôú&Aøï[QÍæiÖâÔ¹DVÌ:…õSèå“WÁR­ŠÙ_á6J’žóIìÔû°é{Î%;8r›‹â×à tÛ’ò§þÏÞR&ó,™­ý¦GSÿžFÇ¿" í›^:ìéÆñ,òþ>¼kY€ #Y~å ˜úfˆF~ÑÆÃ#rô×(VÍ›˜Êè×8ºf¹“âÞƒ€{(îØÁÕ?í… ûjjÜôM6}Å¢ïÖLëJ*,¼yî/ÞCÃÄγrñM¸}å?î^Ðqø}mt$'Àš½Ô´á'v?Ù)'ØÙaiæôv©DCÍ@ '¿b=ýŽ›—âë%f²F1Ö>õÖŒ5f7ô÷Q¯îRnë” ,¾'“:7´A̯+PU{tŠWãÎ?øpª’¯ýV™¼è[Ì7JœõJ¥\•°1=çߌ+j.“¨åª8·®MóЬ+wïþŠ}Ðö’ ×Qh IïÀYͺ;œ¿Bž¼% —?‚ŠûcùrÁë‘)ÏæéÞÜ?d—[›°Êþ|óÎÅ¥ ›8Ùźô×A :¹³ÛŸvû%[ÁýàG¼X1ZÛt[’Œ6W Þ’ “!â©ÞY Åòï¾?FLGäpØ_ˆŠÜ,%ëãÊàòDø_À/»_BÜŠ?¸0W\Cóù œëÑoðDwωCÙ¦Óis^Í #ß¶ZPýšcÄÏw⎻ɲ}Uõc—)ÒOFŸAP; Ãì¬iÒr èX£Íéó Ö•£ÍæXÚ–ÇTPå5zï9³¨I0¡c¶Û@óáPR{ÀûÂNá» áËŠw.ÄŸ}ümF»Yÿ Ôr“a èýËC¯~1š¬ò kÏ‹‚tT/qÑÆ–»Püνy1‰ö¥•s¦é‡Ò¬];MË ð`ÖMÜ–/µ«d±à&BFãˆFÌhB›¯§9‹7W¸=Þ°¶“Ò‹‡ö’˜4G6{Y+<ߊƒ6ýñ𜳀~-€E%% uN“9Ç6’ E¡Ì Çñܘ• wQ‡64õs›C–°^fb`·JwG`ÿÌET{ƒ2ý˜»ºƲÅò@i°{/Ç37¹jQQg‚†«xéæÓäjÑ¢*žx4ú‚èë±ÔËè ßÂu5¨-Uۨװž¦¶ìaØÆý 処AsÐ)ØØt òk@ŠÄw ó„݆Âpc“ k•ÀG¨µ[âé•´JFm'Óö£º­Ë~÷»Æ­ë— ë÷€„†»æèN7Ëa6÷~A~;ka!ÓPǸÁëð,³J÷îÀK‘Shð–#ì½]2Þn.ü*Ú-NQÙ ›©ÎÄÒÞ×3éÍ¡Pù/–šø±”7Jl£ÁJÚÜi‹’ÈéÍŸÁîÄ"öòi<1õL¯²¸M§¨¯q3d^zIãw°·<-H¤ÞŸ¦(x噲٫^àPTV»¤2ǰµfDY! ÃW€÷ÑTû4ƒŸÆ´‡uëY@—Ȇr«¾DR™9UÛ«]_®qqgkp×ág¸Á‹‰Ù]d³2DiÇž1Œ·ºRfÐÓï5¨›ãzÆ :Ÿãp¯v/íœ2‰…}†#6¡ìÃÚLøn6—:͕ĉíøv¼0;|uþ·gêÂ4!׊>ÇÀÔpØS‹ÊÏÇAa UµÖf³zðî',k¬q^1@ö´ÎbǾ]+ ýÂöÕ.ÐÝÙ€Ö¡¸(á+{EáÇLw2Boá,e?˜wMŒÅ)O‚Ý?Œ`<}¸Ä“)Ø2‘°*Ðk˜O®6.ckSVã’…þp°RžZ…|†u»ãpã):²îjG@ÐÉ0ÛãF¥>À÷ÙXýE°ðz'é¾y­n’ã}<>ÞéÄÂLøú[”ýùê:q4C\“¾û¢Àì?åš9È jøKÌÑ8¾o¤ÃaÅÐðúí~ö çߊ&¶*QWR‹1ºgXʯ~’Ú”3ÊçvìõéLü´C¬¿©Ð}ç\¸s7Þ\áÉÕM¶¢¶ÿÂÀ0/—îå‹3`ûåë'‡#ó+JYÕ7&zMÙ)`Ç ¨ßÕÈ[óh2QÖf ‹sK¿<‡´¨°[¬¹Ï—¼œa÷×£‹](Þï “öK2ÿ–±ô6N<[ ×Òá4ü“ëzç  ? ÷+/°]Sq¦Cا¥¬üÌf\uÃ"Çð9i§|ÔÄÝ&q<Óvp¿9MUgû5®! 4¦¶Çߣgú8 •V†mG+àñññøc­6ÜKÁ/8ýëKQ›»€.£û§ìùDš¸S‹v4L…¾ÓÓÐ9J’)ìJÝyôµÄ-X+xþëU}ué5î‚ÖЫхlŸäî$×Ý´øó±œ<Þæ [.bwœÝ¡û“>öÃéÕ§=܆Kïác"ÅIɨ¾ÒŸd_æM躃†™ñ¯“~rÅQ¿ð¿žÌWCs±ðõ2ª\Õf–o!x¾&ºÚ,†W;CH”ôæ< Ã¥Øí'_{f±à6o)}­s5cõh|˜ãS±[²à£[‡3ÏûÑß³?À–¤Óx/³ðÿ}Ÿ ~ž†MN°÷ èHç î8ãSð7S„w¾C öß$·öªã–§`âN;š÷1ƒ$ý˜@´­w“…"aüB£¹\e:ÿ¾»Í´÷c³ÆN¥1SlÁ*p+¼3÷„Ók ÏÝ "/ŸÃ˜¥ÜŽêõ z||wÂÒåýøº8²¶D×õâj¦ÌI¸ ¾,ÐçNj4⇌6žãpl5$:¡ßÛ’fú¿„0uü]¶ïÌ@ sº[Rn7BÐ_Ó úýø¯~݉ë®T¡´òÌSù½>šìqþO Bÿõh†W4ùßïðX¾–õôµEËÍÔð;G¼a7'Lb jèÇ'>{ŽŠM™‡+vÈPjˆ³tÓAôçtÐîB\(­Dhˆ“ÀØrpx®NC/ɰð_J4ÌQŠ¥ÝÔá]ܱ‹çæDïN£‡XœL ØÚB¦$Åm[ï v§2<->ƒ Ž¿F¯—Ûi‹tÁfsj>ï)'øvßÿ{+¯[(Aï|8Ñ_Ï3xÀ’Œg²Yà6^Ÿwc°…Ÿðî&œ. g *ô`ݨF2 MÃ7/-©õ—»\øMÎ_F%Í—ðwÖÃpáX¾QU:èž„4™É4äÎ6ÖIYÁ~[z½e?ÌEð|êÕÕuÞ.ÀÂ5»¸kãèI© ØP»‚š>ú"ò{á½z%wV,ŠÊZàÙéÖЮðå;ò×_/r­—󹑋¿—"lR¡K–^%NF…äµhÔ{÷§æZÆ‹R¯V/¨0 £Þ·CgòOH;øšËŸ3ú|*ç’Z}sê”V;ù\£X$níûÓ2~“ÌéXéþkÜ~ßB²Ä'( oùÅ'Á~þ[Ütû ØüËÄY+ÅÁ5é·óõ85[€M4Z…zÇ~ÁÓ›ï8»’Xó­M–ŽpÛfAeº•’¡Ð´õ(ˆ-)&ã‰-К U¡Ä¶}ÝŠYyR,ö²+ÎØóK;g‚M—·aÇQ–c/ÁŒÂLhòüt]!µ¬ñQwMÂDyuð=v ºvs ÞŠ®Û!ÊvHºÐPÉhÚ奯üÀº¦Sì‰÷&ÖS-Š­Aú¸Ïg>›WÍN |€íRË!h¦9“û¨‡ëFý`ù•$ºP3º4æ`é8iºßý 9v/ ¿êÒÙf¼/ô›“ÙƒÛÏoànw¨Ò—¡ë·Çþƒ@q¼",,›Éþ¦œÁmý'0ÚG…Íî¼ ‚J²Ôç¦)©rd1§alY(1ˆ{{·90ïËÇ™ŸËC="-žoNÂà€.ÕߨLž]gwh0¡Ô:šÉ›?ó”/-ÂÄ‚†­€3Ç”ñhà3.jÜÓÖ<¸%»˜îãéÇÖÐÊÌÁ;ˆÓÇ+°¼Ç…|.<Ç}½ÿ /wJÒÝ–kÙ£. :ø+…i¸„×ÚM@)nYÝ–‹B©·±EÓ$•MÀPmüKçQ²n ?̲³)ÀÏ‚!nÞN Úº_iÊ¡ôáÉD^.ŠL¸çïV€ó¿x°ìŸËï+Û Û’eÉü½¯ÐâÃ-øv}=ñ–˜F¸YÓ`]&䙆êhM%Æ/gΑsÃgâÉÑìËCÖ/­EZ ©§»)gÖÚ„*O•ðé¯QþÝ¿.¨­%[l†ÈÎúœý ˆØðOªÌ¥áªO‰½{?¾ žÄn,‘Ų>‰9ÄêlN8qÕ ¸Ë?Î=L…ÜB š16jÒ;¹ÊÍÆ4.^¦Z1Îü ­U`ê ›pçÔ=dGÐ[¬}8vÅš°î«\u‡“mã]tOÀ™2×À æ8|âÿàî²+ïcä¦ÙÍáež5  eb“w’ëË—°ñïñÊ¡å܃½½¸3³ÃIÙÿÙ°>€ŠŸ±éæôò }Ø–—‚­SgÒõ=ìl˜+¬0ïÁpPù‘‡2cÏ0ñOÆôbT3~ßu‘¤ÌS¤9¾ÛÈŒ8¦h| /ß“AÉ;ÊtçýóØÔcGã3'BL§zdˆ¬9QD.ïSBMϱ`9W—½P¾‡GÊ_¡ÏÆåt^§:Ýûd?F¿v’QLЭËòðMÙEØiƦ§ªâñÐoý >a:×c÷…»8÷?aRàîÒþUè~o<úé§p?5¼HäNiúræQⲜGïè_åIo› !‰vÔJö,D,-…À¼“ä9Ž+_öƒ˜u%§^QR­§Pª·oì'鿝‰Ç»ùhaçÄ›¯7WÔB¯Ÿ3 ºgÅþyGò®X¥;ö¼ƒm16×1{»*æ“÷Õ»I OgˆÇ½câyÜ‚(v0s&[¾ï¯ZlÌhª‚åûöaÅÃ\È¿z†¨ÿ‘f^ûqã¥ÓqVؼÉ]õ¿ê´Ná< 8‡g7…ƒñ‰-˜™)n5:p6þÑ´<Ó4èÿ-Ëhw&^»¤íJÑ©¯!åàôUãcúÜÉô½Â_œ-%Ìli5<)º:ùx6Þ›_×=À»›‘À}^“ /Eƒ~¾2.ø£C»=—B~·<º»[_µZ »2ëéòtúòUThTƒ< ·ú²+.À]2Ÿ®~]¢øzÆtbòØ™öíéC¿Ø÷ÉëNp!…mè?Ýc¿¥Ãín0Fo56s°þc>nneÚAÖ“{—í3Rf=¯û`ýu¥7w¨-`۟ă붃›·=Ö¤I'MÄ-ž=økÅÊõàäë3p˧ _g€þ•Ù6 “Ýh\wSœF=öŽ×LLàOË=²´£ äӂɦÊWÜ ›“Ä|Ûq^¹s)¨sEdvàü1i6Ë_õ†»™iKßzô@®S8Õ6°Ã57þ¢‘ãGHø¸Ÿ•âe4})ä?~T ÍøˆäÉö‚@NÕ²ÕÇ1ýk1~"ûèÓ(&óææps$ÇŠ2àÓçi¸ôâ :ÒІY0$ʵ_ÈÔsóa {Œ‡–aÃŒ?ðo· Õý± „žO…-u¯ ïýRÊvr?çnÃƒÕ 5õ-ú™ÜÇ ;ëPbõvV÷¹³³ìd2é ;hI©Æ¶|?ª¸.›Yq…o¹ËÍ'áöçXzG<X|ÜbE«¾"MÒb+–JCÿí¨mÇÉe@ëŠ ÜÕÆn“ˆ9ÔE|!o¾dã-ŽSVä Ï!s‘g¡Uüi·b°ii <ؾ…&H’d™åð`“ö?îGóƒÃ'AªXŸfúiТ OA¿¢Çš ±sNJ´ìˆ$•±«„æd=\¢³?‹BèS{X½ò¿IÐH­T.* îoÄb‰ÐÉ6e¼õGqæ 9øbÕ¯mëøÃ¾àÀ¸(Lÿé ƒO•’ÿ‚ìWu¦Ñ#Èþ˜KÓ·†âpùÑø¼"-mìØÁÍ ðhÀêÅJ±ŒgD]O¸#Ç?ËŽÈrSûüaËmžöƒ¹LÖø:'Ÿ°ÿ®ÛÀÝHÚÇOùs”Š ëƒj‰+÷â!½ú†C|é´:5r y"Ë.XÈî8‚ûøÞnåøÐ4^_n£9œÍ°ê[g±» 0qm¾ãG¡z¦)²7ç9Ò°¾'D° ѱ? õ i¼ç*ÒbÏ.¶/ÃPQÚôõôÍý†>;íØ3}OVÚ«ÂZ&ÈÎ{×ÂËö…\/ÿ0znt¢Ã7RáÓÍ ü»ê JßUÆ ¿&ÒíNž,z±.ûcxÛ½„'ÑN¸æÓ)³4>KݳàûA¨G[hˆ;oüúÈ™¹¦t—~-|»-I4}Šù]ÎÏà§äUÎXP‰SesB—Óî„s$;¿4]çÒ/ó\¹“;ïcé ”LQ'™jyp¥ê ”ôg@ƒ˜ ]ø¡›vX2ëÎüû \¢Àf*ÀÊq$U¶œk²’ãϪҷ‡‘°¹”“:Œv*gȪÀÇ{)¨e[7DÓNJРz‡:vãp²3ÞØ mŒßÕ‚Ó£úPxÜLÐà?A“Óú´2fŽéç>h £€Uöx+.úÔˆ £PkÇxÈ9ü‡‹ñ3Ã-¿" VÇ«½¯rÿå¡ýÓÓpíùjÌÿà ¡“é’}›Ð[ã~ËM†YÓ¶BÈ‘v¸_|æˆ?D\œäTtÞ”ã=I€ÅÕŠ´Ý8lí½HþìUÅ^; :ç\: ‰#Èõ‹ÒóÄ‘–Œü“¦ 2[ƒD÷è8å-×ÁÄû'}@ï;ªð &|4À¼åY¤Ù·þ«µÒdaq'áWë%²ªz.F§¡Ös°±ê Y¤J2̶ƒžk=çm"Œ#i³Hÿ¬r©?Êî-%oçBL´&ü23fM÷Œ`ÌžG°öN<1[‰YXf'ß×¢_‚}$€5¹h÷ó9öŸUãËÇSE S˜ô‰³â–0Ç)pzZ­@OUjèåC÷=r£ãG~€‡µ ëÿ‰zgÑ»µ§°êñÃÎilØb1¨GIbRÌ?Œ¼¨d_'÷§ 4dmYOŽFneQ=Ê´z¥$Ûî¯ääfÕ á£šERÒÑqÐ'α0zŒ¬¸ìÍin)€éÀÇÇ’¯8ÉQàèa.rË\Ø%('.¨àš€;Ø•8•M».%óF©.²˜÷¯<ù ,zç~tÕÓ¢RöTcë!tÿáÎ{ÚÖ‚i‡ÿq+³6BIûÐv+ʼnÃÔôÈ5Øg±‚÷Â>õü“ÉHeÇ ”ëH *aÌTDЦÿ[H Ys7&;SבjîR|5–¨J³/3ò¦L¹‰.¼Ë }Ì”gvð/wl‰)Óš JO-yÉ8gþšÏvµÊÂäõw{uˆs¬®Ûø“t¿e/&÷Ý&¿ønr–SY¦D;¸¸ü¤¯Ø“Œ>ÚìåiÚnkLWd¬„'MÞtx÷;xgàŠ¡xÙ+K}u¹œaC¸^ùS­lè²ÙXit m¯Å sÙžå%dšÝ7ÎÓE¶M ÃÅ"<¢'‹t¯=n_u‘ðbRÛá‚—øë- æ³ú‰fàU± µÁÏ£6Äî˜'ó›{f¶ž»(ÄÔkõ™ÊéÝÜŽŠ/|?-VZº äK&³›Ó¶å.êø\›Þî3¡g‹±¤ø8ÚK(s'…΢é=Œ-}€a•Wx}?´èè|øYáÞðçì5çÁÇm¹Ëi‘$¾ ïE¨“/*gmÄrœhCÂøo­ŠRô´Õx{HŠùË—óÓŸëƒKÄ&È7‹ƒí•qdL\ª2ÃI§ÃAÃuôõáK>øO0¢¹Sy¾:Up,ÁŠ-þÖ…Í3 ¬îº0R‚­Zg wïÍ£…½’°¢a.¨ÉâÞÇ-]Å`ºéÎòøCR‚CéÖG2tç§ \“¥Cç\ÞNï •ã½˜gÜö™ÓÝÆÄf±5w20œž‡û¥”~Û~˜òÌCù ½©Fp8#'iv¿"=¬ {nüf?;”M/ÞÌÃm%ýxïꨮþQ¿Pü ôüUq£Ò¨« Ðbâ÷ã‰Nc;åG!Úè:—Ýøš•jžÂ{×*éé³ûh r›ÿn*–¡¦›³&T. Ã‚)5î…Å ‹P.B“cðúSq:§Qæ×@]Xì][Í&“VL²÷Ç’/ËÙå§.4Î`'f/‹;ž“ þÝúúÍbøV=ˆ¿ÌèÑó¬yF Y­àÏ~ƒ«Ó v3=NVÁßÃÜÜ€ú%û)¤x®è >×G¯§eøAj=þ—iÖ_/Ï–-õ ˯ÀQ•..×kûç@ƒÒ³AGh8ÍKaï¾w ˆ¤£ÞÎ pmÏ9š¨Í‡gUC|Þ"ôü¼•üÊÆO °u’©~@™q=lHbÿe™Ç̈«§3…ÓÂi Ìuì]«FÕÆ(R¯r 6ï’ý׋£œœ~/ ƒ&‰ÐNkYÒ&;ï,Œn,ŒÑm¯P§Ò•éa ½ØÙ…gWð߈AÚR[š»¾JþæBˆ—+¹ï‰R©hÈEsnâ?'ôp÷ SãÃmþì“Ì2ZQ"‚¶YùlÌ©l•Û5Z'ò”?¦# >ÎnXú§FÇ‚R‘Ö'aGṄµ*ëéb²d·ÄÐK LتùW©î‹P*,ÇÁ’Ç}t_î ¹âkK¯~åÝJÁ±IÉx%ê.Ý7ž|¸hc±Ì1¢xvšÛ†+ŸpÒ‚ð©ô&f»ôbñ®~—¨[ù´†çôQ¡1ô[´];^ Æ]è…îñ¼ {”á×­tÁmŽÌ:ŽENÁŠŒV¬ö‰ÂâiŽ,)·þÁÁ»„zÿ÷ìY~ùrâ~€â°†ÜßoŠv _P+¨/-ì RlIÖ-Ñ‹Á±Ì<3?VÛQAdzîÒäÛ‡áŸø=\ñ?u&êxŠ¾Ðœ&Ý‚CV´ãWÍa›ÄØ÷ËŸÉ‘µôÙ”A˜Ôù \ï%ÏÕ¾àÀ‰Oœ÷‡OxÛr»ç ¥ÓÈÜzÎiœ/÷ýL Þž)ù__gâ6ü’Óò¹‡q•xø»ÅCÚ¥,vj8Þ ½†)ýÜ#‘5Ü‹§¯IútjvLŽ.i=‹ìÔ|\(2›ÛòUv*~Áb«­0…ç‹Æh¸ìþ”»"JŽLì÷ üÙD7'® ÊtÈL†òj•©_£îÐ(…mw£Lh!½X¿ç#¬¬¢}‹ X¸êXªí§@ ¯îÿ¿ ËiPØÉ5ÈÚ¯á(J¢‰å½""ŸîHŸÖŸãŽ•q³×­åÅ4ïfçcmF.Ï0ÅóWh°ÐZ.èÎu(Rùë¶Oýôm'.ÀÜæeÃå…hA­©ÖÝé §·. !?Vˆ©FŠÐôí/ÉÕ­Iìäàg§â}P61J®‚Mªkh”¨#ÛrpžžL™N}3Ä—6ÙBW×¥ƒ‘ïe¸’ ±’NtËç• XÃ9&0§ÝSñûco¸ë+N/M¯Ç_É|ÜeîÁDÂ\¸’µlü'JÆ;yÐY¸Äév´D:Ìì£A(e6n Ví©Ùøîs-wó[)†(¿]ž&Ž_¥3?SÌ<èOKÏ[²`ÿ^ü_ˆŒn« ²ßC˜Ö'ýâ z¤¨¼£^æÆ±‹D€qú¢_8˜Ü±ªn֚¼§á4Zi øaß›(^}À“š‰ðÆ»\¾,nûgG©5\÷½r×{r5Eήaºk³a¡p=,e¹¤½S'sQ®é¸o‹-/}Žk hô9P¿v–Åu“‹j‡aþá ¤cpö% À¹È(<2 Êä c à¥0Í÷2Æé Ù¶‡gè­Cá;t”IŒ „))´Õ, ^n[Àyš1óÊLGÎxùÑçA·}G—¥ÿ£NðèÎ;gØÛˆx²"š_£ß#ÜѾ2®Õ·²õ—fÃÍ,SæÃMâ}|ïJ7:ê0I'8è¯Å” ßóÖ\q!JóaÿôoøÃGÅæ[P™øq,g¾:ëÜ=Ò:Â$¾ƒJÔó4Çå°ÔÉî=xEd‘»·bx³Î?æÍ]]ŠáKƒáãC]šÊ3Àÿ²ÏM+Rɲw‡HùB´ ñÇÁØÕdsw̯‚/× p‹?ë°Œƒ¯ˆ¡– ä®ÖÅ-:Rxæ'º¤h±‚ŸÁp¶í&¼áU‘ô­;aÕBq¾æQR*îÅuÈㄌch•–Ê=K-@sÏxn’øKøÔÛÀÝ;?6 ¦‚?÷`K®%«Å¹9 ^$}G[¦ˆ§øÖìÝg)vÒi=t¯]ã§Û²u›*àp@~êu¦]Û‰d÷Oü¤:zOj3CÏVþ¯œ‡8xÚ œ—q¼Óîx+C h€’•ï¹ñó¹?/nqb»?rûêðЫý0ÿº“ú’MlÆ™ÓÏ›syÎJÐqçžÁ„€LX8ïNgþñhÛSsôNÛN†Öù©µLÐtz9êCgÆd¼»%…ó:ˆID)4Oôrïa—½-«T“bÇk ¹Z•Làér2I«j«Ô› „á Ø&ÁÇ‘?â÷ÿÌ3t^™Œ§°"HŽnÑ"ºJ,§u)sÞÙCÌßAÄá1LUâî ²+ÀûÌZh[Øq gLi#×/lñ@‰»ö¨~ÙVåDr·;¸³|d?­ª^׊² Br9Uö^Ž»„8|T®µ~Ô-[ 7?'Òù›ùÆþF,®IŒŒû ÖOeðžæ1ðiÀž„=I»YXℊžÍÜgÆ@6oU?l„¡áæ°lhTßéÃÛf5 úŠli_Ú h0þDÂ;DA «ÿš'KçžèƒºÍ—`ñ·ùtWI•mYÊ>I5ÒÄèàqWÈòÕo ¶4Há Þ³G/{|Ŧ+Àžù³1 b@Ô!žÞù™Á¾_É„ÓcÈøhZý:‚U¥°Ïßϲ¢ðnçês ÖÇKü¸-ú9ž&¹å€cç~Ø>÷'V+€á…¨ù/…¹’äË Ñ?M+زk÷èÑgÁó}$MòþÈ7Ýy>¥Þ‡ê0ÜòÓ yÂÍTÆ—go!Wºˆåî “']çÎ÷™Ðg¨ÁH+6Ü:ËáóEx= øŽ?aDZfjkÔý[)Ûë·ëGŽÒ¦Ùmh¶H€ŽéÜŽ«üƒ_x=T€Ôdλ]‹)1½Ñí3àR³3ÆoÊ„õŽ8úã.^Z§*3ñÌ7?š»N :œ5èëœV”=»º%‘è ¥sÕü~î3)º9—n º¡ƒÜ‘‚¼èªvSê¾ÍˆNðþF¾©AEöAœLÖð>懒'3‰Yø/x¢ûKZ6é_–ø½þ.oië*òÓÿ7´æ7Šîµgå… 7±VÎü'IC½Ò5Œx¬E}<Ü!ƒ ±a—YÔ¾»ƒsœÈÝŽ¬GÓ ¾tñ„« é¿Šö¥äâŽ]pàf,»µç!;ÐcIeVC¯Õ°¤&š^™b„³²%xŸR`manYFƒ‡ë›/MF¯3 Ø+DfÆO¶¦}{G¸“Þ0Îþ6i6àÜ×ßá#%je`§º– z…S³×ŽìyW „¬ÞIŠ…ŸÏüÙP˜(óú³•˜¢ƒÅ¹Ò¦ÊK¶[…s³¡o…=¡úœ›z`Ùø'¼Snöìû· ®K§›sîs³…쀣ã¹8íXÞ53]šÃ)ÒwË”XÌ AÚ|©¼¾è&î7¿‡ÛV@­L!ì1ó%³îÛ‚ÅõC(5-Ü–,†‹ë•Ñ«¸ŠÿàÇf.vü>® ‡ ×’V[jôчÅ6HÒ½üz)³»[ÙÄ9‡ñx½ 3\ÎcN¿†ÔL]HX2­.86¸—‹7Ôb¼•ÙThx.~¦Á¶ ³ØÂOà1¿‰n Hc_•œ7¬ÊÁþÏGÙ:½Ùõ›èlˆÏ|‹©¢"ìŠþCþÞÉ5p™#ÝÛ9=…kgÓ&IQV^ý¦Ê·Òk/¦°¶$ôøû$ûŒ†ÎD8²HuÁ Ö#0»¹Ì‹UÙÅ)úô—<’O.ê‡[XïÄÝò:4´ØÄI§€ÌËökä"vO‡üÓËqÒÀ™¯TÈðöÕºöØ)|Š[þ;„Ï~ nŒgRz«˜è: öã‰>KJØÀUš¥àµ×ͼ'+·‚­z=ÞŸ ß•eÉXË]6¯Ãc­ÑpEð HlÄm XöZŒ½ 0`cçÖ€±Óu¾ÏòR´hýÄÕ?õeF8´QŽöžKcbÝ Ñ%³–ÚiâðÔü{¢Þð$4¤Éæ5;ØÄŠK`=6ƒœ+x¶#I8GD‡ÈZ«ò½äéMGU̲¢˜ƒ‡/Çžì†/½]¸{ÍÌœ½¾ö`ÑšGä UØóÆpDQ–Y­Þ†¯rn+ZO?Í#UÊS9 Îx•TW…æÂŠcÕøAÓŸþ²ÓÆ'Ž™x«tNûìE“™Ræ™ÌV1nÄó¹°ˆ2ƒ¹Ê< Ç<ñĶ.Ž',@ÿ¤§Âͧ•¸ãÆQH|ù &®Ú€gäMXÔ;yÊë,†Ï?…a‘O<¤&JÓ‚_LJj%©™5tvíÃÙ­ì¦*Uñðï·ƯdÿŒ\AkM%yxê~Íš{Ì;q×Ëñ`0"NGç¥óÍé¿[J´¬ø;šGN„©uNP=Ý‚ÿíß04žÕ¤/Ì[œ€/jg`èÎÉÔ¨H‚­ºâ…÷@~z._¬Èê_‹*7ÔšMå-? kÖH>p|w†/«¨cåO<ÑùŒÓÐ¥–ç*¹„˜(w Ò*Ñta3&ÓÅŒ$ʉå¹)äwåvøžŠO-`;ªàþ?³Í”bïöjýHظø4»¾-kƃƒh2›0}ÁÕ=TUòÓÿ[ŒöÎòS/Ôà‚»"ì:óf¦Í×pÊãÔaÌATôJ¢S'½€“MUTPí*M©[ËjÂÕhÈÑj6AW4¬0FŇtußăm¿qÑG;¶¢h èéfS#Kæ)vÍꆪé1ôÎy?ÜæÒ·"²Ô°]jZPû{{Yéòhâ¹mgý§&™63ÑÕrL^×ù;½'béóñ{hßp>{Z—IO–ÇAE6¿=‚Í?x’®±JùØ'¸6o*K¹äHþY =_+°Ã/™lýSÆY\Õ§ Ëè†y?I¥ëlø^˜Æ<”Ê qY+AÑÇ óAµúi:ùz çº_Ô£¥>ÇÌ–ˆ±­Á ßymåDYòú¾ôˆÏu(æž¡/f†ƒWÞdZ['Ë$öÖµM¶çZ· ²UuXbØ+~Ö­aÿG˜F¬Ï냑hwÊy8±ˆˆ÷|Ñ8Öxh »61}+HÏîˆ*ÒÏ£¹ÿ²ÔK?¢Ë´—œÇÊ0^3…ݺ±ŒZ%´cæ¾ã3G5¼+Ñ‘`3δ¡<>¼ ãèý!FÌGʨäHÜ8x‚+\F™ÞÛcô«ÕI–ÕÇöns€Ù›ãp“Â/­ïÛÅ–L,ÄדKñÀÛXÞusU!;5€n«³c^’©Ó>q=SíXßÙ Ö¹w3ÃÞ´fƒKÿ9Ξ»Á_µ7 |gÃŽ­ÝF„X|{ç V‚¹ÛÃÑÈ#‘‰^¼NÔί†Ë'LÀW_zª#‰y¥$SÉ<‹éE,Øz3\ Ç–‡+G½Wå…ØÑ±{FñùÞ/TñÞA>ãh‘‚®‰ñ„ÅŹ°Qp/mƒœ>Э7¦ûŽ,Ä(A¶}v:Ý]1ó¿2ï÷˜]ø=д-É„-,ú:øŽ¬øW ^PætÄþΚ²”?ÈÒÌó)}©Äî ÃÓk­`ÿ¿G¸õÜv¼ ÜOJ>$²"­‡ð£ü=^ þÝ:¸Ö¡˜ª&¦€À‹“Ü s3÷Eųû÷€d’ y¢FØù3¯`(ΞgZÁë¡ÓØàJÆJÎæŒ«ðWæ'ônß„=÷¦Påñ ¹:tn?%‹<è¯{¬8| 2'²™»²¹£~ÍÐ(M%e©ÃR7vâf1¾×¾Ï3 !ÉnLt¤šÏ’†YzX ûšYøÍYœÛÔ,ÎtŸjí8\ouÈ‚ ¦K2»Hg0–€ü$W¶D`2[r1‹{Òû‰û&è œ¹_"Ëö-2e1r,¶Z“+}uNìeO/øµô¢Iîj¹ÄÆ<°¥š{úH÷é$¨°"•µv4pg&Sÿ˜ _–ÁçgpCW_Uê1ɵŠä'¥˜³Žä±ô€ñ*Úv‘Lx%1õJ,Eø#yDÂq«áí±0¹2‰…n"G&B¢;¦IŽê¯æBüì@_j&ð¶XµbózYèu W‚PÊpåu’?C“cš\‰gyô}+¹«ÓÓ.·qÇç(,xf‰%ÝÀ[%ÛÅYeI6˜î'NÆá™ŠµdRD Œ­šBw^á¹}žÉ¦N«Åž#Ï01ù*¤ý\î–Bu0ÈžyB†´fâò­áÄ–5$ž{‹þQèc¾ˆK,UCËٻȹEN`5ã T` þ:rÞ_5äM)¾K®?Wd’â¸Úcp—Î'¼ærOÌ€;Aïy³6 ± Û5Á*B„¾Ù•BÝ¥þÂÕÓëhÿ” ´Ô| ÝÒˆ+ÞGñõX T¹= ÎONÃq_aöfÚÒåáƒ)OûÓ†êŠ~àg]ѧÚßHæàâWÔ÷² Ýî6ˆjRoƒh¾mH7¿oÝuÿ¹ë¼{ï÷=çÝÏó¬õ¬}ósp}øL¾³™z\ ÁqZ\&o|GNh¢Ý¥,ØäùÇUåA‘¤$ üavÅsQ>þŠÀ8HŽ«)ü fgGÀÐýcø¿ÇO@Aþ%®ñ}E†ÜÀö?úw]ÛØqýµx¼¦qð¹ýÅ)ûä…óµ·¸\({pŠÚ¥©ø«`¬yç-¸pRt£U ~Ñ>vÌý\øÖBF=ˆ³ÇKÐw=3b‘–o$­wðßLé£c'@–H¥ 6гïäHήZ»(•|¹= ¦DÚ±š ÜÜÿ“,z¦Ä{=gF¹xTd+kßTnÆp³è‰x¿“á‚ñ|YŸ@IQÅŠj÷yèV‡‹.Šd¿ ƒ3†P™UÛ@;D›–>M]t$èQÙVS/=N¯ÏÐÀ‚Á:µqÝ?ߌÍþK\A‚zèû—íxý¯"™'Ë<Òkt‘Ólñ‚éoÈ;¾§¯M ØÔùG,ã:“•±…u^¥î °ÂÊr¸¸l"N¨ú¥¿IèF}6)Û‡ºóåN üùՎɵýžè7î)ú ´ðNý46l³!ZÃìGzüí –rð¸ÍiÒ?OHg¯$¼K…*’ôSk0>M'i]‡±ãh»m ±ý37족2alH6ÜMÜõ[ŸÀÀüµÿ'´ýL®}|)LJþC´CíÀ\d6}ºþ¤«ñ¡—gñÉ5ç\ld«QarJ…>‡¹ÇËñÕj>wš ¿?1å¢q—ßj§IÿžTý±|Æ:¿ÇXØ(òtã1,®Eµ5È g}:wa+Š«ÝÆë_¼õs.¾ŽôË—LqÕZèjM…9ÑÄOÕn:;cß)Àw¸>Iën>Âæýêt¼~%»P;Ô釱 Y0ÜV/åùzúx(SÆ4F²¼®0^tk3ô<çf¾Åš*¸gõlA@D"ÿdëNßN÷»¨¾2\XYÕ É*¼v ¿÷"´éáœF° ÌÇÂXèJ+*2«ŸwŠÉ…ã?Êa‰ã_a‰!ÐÎáA¬úŸ#ƪ|Äå3'ÂÜËFüÆÝk¨_›ªÙ@ãÈÞJE>nž K;1D.â¿ðè>OÒf¼ÀK¯-IEïD<:ñ*îù·Žøî{ñ»¿@:—¢jSÁpýܨtÎj† ©çáÍÏ“hsìqR?·büáãz\"ü¥¤>zIËñ%ß#áÙ¥QÿŸÛÜgb‡ÿ" iÇX¿ïôº~!;ÄÕùšT6Í­‡þê½0yñZ®ùnêæÐ‚Šw ~n56ØÒ©¾À˜yuHͱA/‡I÷·£Ì¹.\þª€Üiq¤ÒWáù\i¾ÜNËÖøÀÆÅÉTžNëÔ ƒ¬³uÛ<ÁP=ï—Á+É9ü^ª>ûá$Ì´5GŸãËÙ˜‰º`?¬‹”/ñ ‡ednߣ†·ŽIpìÑuPdŠå¾—qâÓõd˜ëU|šu Ö.Z"XíÖ€†[B“Üp”º4 ^èDóusµ ìY˜-ˆi‘‰½ƒZdæZ<"1m§ÈŒbK[§òÅKð_üFñÝþ§Aù6O=J#Ðé]Ö–NÀÚºXÜ`IÿËÎÔ¶ˆ£ÒÞ½d×huzò÷1Ò½q+ìùu­·óš¨ß6†ß9¯»"FBÿ >M& ŸnWÌ,úF¶l{Ìâ:UèÕG ¨a’³C »n(Vœ‘Á nkÐüûPúy_~á ¦£S£2 k µ7U0|ínôãCÅSq¤z [ó Ääå`Ÿb$µ¹Òwú…µÍRýñð¾x˜h¿ªÇ-âÙ8jÊSÂ}+…6ÆÐŽ‹ûðhl2Ym*NÃŽ¶ã%õ'BñéCøö\{~(ø&Y^u-.ƒ0N…—<¶`Ö¯²0xýaü¼MŸþ‹ŒáCªcÔ iúG^Ž*\ùÎÂ9®Íþ÷G*Àñ“_…ìïã±Y>´Yß|‡G eø"Ç5$2ù· C&›¸ŠäµÖ{ Í[Z‰¡”¨O±"‡;ÓÈ÷@Ü·õ8þÕóÓש0µ.‚ämÕ V]1iòl Þ3à²î<¾}Ê ôŠúƒ}ÆønŸ( ]ü…¥f…Ÿzüu½ØúñÌp}8<'ÅgÙRsåÝ¢•{2¨ÛŠ÷dm±*~ û‡?=J@㺠þ=@•GјA-ïaæ{“\ ÷ö^Üc^íº!ìvD#ùyú.™+½Ÿ2fšC ÙU•›5t°/S.ü7Ÿ¶ïpÜ·=Pœ´ÿóKO–ÝÔP©=ý,w‹Ü~»êÁ/\%üÑð ¼§ÔA“Ö°}¯Ín{ÈÒ½²¾8ney׫™¿llV‰Ñ–_ȃ)X#ãyømœWû4’XÓvÜ¿CyW_Ã-ßö‘ý§@NÄÞi\=¦ûÐzd4z×ÁÉ×±KGžk½¿EÚ&½ÇüØñX½3ç2Q*0;Ïö½zˆ§{¢ÑÕ;bý¯2¡B©=ô ެŠÇ­»´D¶B|E4}ììΛ®økÒX”¿i 3ŽÔÂ¥ODö˜9m 5áÛo/ãµ5~löYª.S'äu¨0÷'ïÁ:ñ`þ488ƒ-¾ÔB‘AŽsÖ2>%€JÐ5¼Cïf=Å•#xÕß©¤gúl¸ýf•KU¢KMFÑI)è|jÄOÎ~òDëÙ–¦á>lNI+ô›'¶£„­8=tB™Jin‡¤ÐpÕ9 þ>Žæcš¢LØC¸[Gu<†Ržáă.• CT5XŽ¡K»…£ßô‘j1|ëgšCxñŽVt¶A?Øî‡¤ñ›€Ç<vlÔ¥µ>‹¨ój~hÔ?HÞ¸— ßÇZ oïH‚wNÿ@ã”"÷Û1—\ÎCÍüp.Jl„¿Ðl¾îT>„Ç_ùÒõ¡†ÄÛ1“zĉ@Ö«t.Š”*¡ÞQ‡q'ù~.;ú߈ӷ–àêÆ½¨fð‘Eú7 csÊ}W$Ê6ÓølmmJG Ï·_Å—^}lúƒ"Q[í›dAªú)VºC½méâ+àx¥ŠBIñ‰˜rM’ ÷Ê#'4®¢ ­Á{:x= Ç8„‚Ù¬ËÌä‹¿“#ŒwŒÇ{ÑêØëö€ Ÿ–E>?Ò£©sìáÆÇÿõŒ0Q¿DÍÖæÿ2—óéÁ&hWw¬·âäÕ‚óv:>öT1¿÷#èå€ 8äh&¸5Ä„ÀK`¸y.vMÉŒ½æB}GbáÅ»0FW”?xjÎç)ÍUx2z\Ã1$¨MžÖ÷Ž¡öZ\ Qí _ˆß˜Áš?åà¼ÜpZc¯/²ƒáWëv¼u)­Ã݈ÿ_)°L|J—ÇãóŸPïm^NÚ†«ä ¡ÜÑžåJšzQ>^ûqì;å„ÈÌ2æSÒÓüÚ#lûŒb§ßŒ÷6»Cd– _ö]F§xNf4½0>Ž÷6£ñ²J¼›ëâÌ©Ê\äõ2;§ífZ¡I…uXXAØÉÃÛ¦,øÙ•A÷Œi"õoUгù$ɬ…uUöôàlSœ&bKÛ$ÓòûàvÀxAªá\ŸB²à¿¹Ó[»¶2¯;¢˜ï¸†?m˜÷wÜÈpàÑîOá·aùÑd¬‹àN´ÔLâ¦ÏF3ó×d÷cj#ãÆ[uuh½ZÍýô¢twã1•JŒyõ–¬½ÈŸ('ÑÖ%ÆÜ÷£$߯Lxã*EšzxÂãUøÚuÆ`\Ðy¢e0îô{¨¨Ú#”žÒÃ^Õ܇W9¾wSDɦÁþD9šÞrG(!5‹ÓÖ-Ôuß~¦°f6UüµÈ<Àÿ|Ì7&òQ7Rg™gøq¹8/”­ÂvÇéÜÐS@S+ÀæÚ*3D7}â'¿w“¤Ai'÷Še+ôàçIÎPãK=¶ä/ÒïÀÏ ü@Þ<<†)¹¸é½:]òôoû 5tø~íaó hPíN*ÚV›M³ÑNn9ÆWm£Á“3©Ù˜ud¼ì<¹ó+|³•Gyåãð]†Ï:*à†.Êx5ü6UŸïŇzkÑ»ÏÁüÍÙìÙåÅø;h>Ný1Ì&µcK¿7ïW¤Go*ñ»f!”üX Z ú‚?û‹È›ý‹°ÆÑ‚k½ÛÄ*6:‚ɸ_x{Èe—#;æãµÙÝì‘o-êÎUÂ}W±üx*º°ô|î HÜs=øþ“ù×ùÐWbOàŸîlîò<i¹v7ß`õŽ£°tî&¾\ÊÚ¾³xÓ— ×½vÓS8–ëMðVÁ.eĨ—Ø—'²Ü°^É Bqj`kgÙðé'Œè¢ÌÁ}H(Æ[σxó».<ù<Uº¯ÀÎm)ðsŒ(Ír wÏ EÝúå8^w јiM¯ âÇïÓ§@!5 ¦>_>Ü9ÍR >.Ãõ3:M]äðý$ñ]4ÿâÁçüÒ„·q9¸¹+WxK~'ÜX}¦-®%"‘)í™Ð–5‘¶½ŽÀÙ§-èú- ~ºT†±Ži»áêX(í¡?<¯Àü%IŠxbV,KsM›[bà9"–iÏ=N}~ç3‘sÐ2ÅŒÎ;~æãÿû™UîàI|x²Ú[vãÁ}:hÖiícã0£TËmó Äç‰Ñ [G3÷ûBüonòŸ÷Âù iªµ<­}ùÑa4O3 t牙¨÷Y‰¬îõå™{ב–üã¹ ôÖGbòçn¾R ëRÇó{CïÝ¿_ð §?˜þƒl÷7ãG7…K7`ÛÍ<˜hÉÕ<…$B^‹F\]™>7ÁáøwpÝ'Êw^3Å¿%øB^ö)ü4û4f‚ ,/^¿T ª÷z`¦›ØŒýò¨á‘›%ùŽçè¶Ó—Ù‡ý—PÕUI°.µ7ý¼g·‰Ð˜´ Ð_3œª¯Ôܰ]' ŒvðÛ® JYÏ‹C Ïà±Û ~êœctƒLªaΛGºS ×J,ŸX r¾m0gÍPî²(¯uÅàì¦4BZ’%§tè£Ëætí'bÆl¢sk ZF‹Ò«=° gÍxÌþ‹‘§RD‹þf»¼^žõ)¼ëVÞo‡_»×óïo—3ßëE´±¯—U¹ ¯ñ˜ÅeäÝÂvÐØ·œªi)áÉ(:lÜu&ÐlÀe›×ñEë|ý4Î?ŸD,Má^sCA| ŸÒtv»YJx`K-ýYŸÛËhÿ2)ªS¥Å×~› Ëå¦Ñ’ÕøMü‰F¥ld|¦Kkðx˜àdÂßÎy£Â“¦Cìu©1kžÅoìMÎ:øßŒäzÍ_O±îw¶`’Âv@áˆÆ`qUD¼H…M3BÆå4<4׌V0¹êÂwWÎB×ÕµdDN'öu„Ÿg¾ÀТnÁÛ©ï°Ê7v:½a÷k ñ²ïy¬:«Ì£·odåÖÐÙïæòe–ÁÜ£l=¤“͘+ÎãÿÚbë n¨z»‰±K5ÀNª`ß)Ð^pó¨V‚K÷¼ˆÃ*îþc$ï(óŠ ìEÍT°¯x&èìšÅ7/ßë^Ä?ÍÛa„ÀnБ3ÑHf½;†õ~`¯ElÞ&• Ø%¡Ò[C>µÒºW<ÿñãàÝÐ)LìVØ-rÆ)‰ÞDùI ßsª •ÔnÁÁ_{0ïyŸ°÷D»¡øç-”ä6·‘Ãí;ðTò/Vªê ÑG+˜¡k<^YØAòô(;!•l2¾+Fafæ øÀÈà5ê"‰#R®Â>ÏÉj4xÎ嫵%‰gí[È\¬Ê—Fí‚æ•) ²z¤OŸƒj‘ŸqQÈ4ò´F•ûÛo¤Ç­5à‚ä(ôI i:/°äÉnr©?·ŒÜŠ©M8J|7ì?$”þ³+ ’”1w'þ4œÉ‰Ë)ì§ÁÜ¢±‚š§Î†ê’eÔ¨=—Ä*Ñ?+ô©òÂ`ŒÜ}Ê9ÐwËXÈ,g>lÆ5ÞwцÚþÍ€)zRüœÇdÚ~®‘5Õ Ñý Z?\œöÿ~‘pw Vòt¶xl,ýQ4†:¤†ðwŠWp¢¬½?üJ’f¼¸Ñ“V`ì¶>¸z/fLä7Ëþ°´ô‰|‘·={ Âg§â×ß„ÇúÆch*Ýú܇ú%¢íĵõïô6 ½†KÂlù=øDS ?çïKráƒ]ZùU ke”hx<º&›oN¥#RòùCá[¦«€ïX>~×òÃ5iÑF ^ødêNJ‚yZ=¸_.‹ØÊÍů×#ògD½$šTлEæ&‰R{ƒ×hñs8øíÔâïü ªš*ª¹Ìgÿ¦bë/T‹]ˆãJ>âþ•.¼wÙ ~zˆðM®§¨¿È]&i6¾èWC²¢²-øTÊ–ü‘ -ƒéæÔv”§ §U;ÓðYÁFše „ñÙ®f¡u¯1>vÇ*JŸ~Ï@SMÔ5ÝÀZ‹/Ù‰pEÏ~v6i9z8N&¯®úEª||sNƒn­) 9º—ܲ̀E³>;ˆŸ¢Ÿªq%è2ú4iÿd½JR¸ás³0u¦†‹Å¼¿?tb°j‰&F²GP¦Šn¾ ù´ó£éÙµ· ÙDœï¤ÿºêøÚƒ»øºBø¤+Ä–ÒEתaêÝRHþn»¼ãk¾IQ±à-tÕug.¬ÙÊ›·èÓº¶Ë˜1¨#ÝíNR´œÊÊ–‚£·¯É‚ yô+øÀóA|q8,P<@µæÃËäÛ”Tp·ú8òIQ…ÖgºÑb'e^:õ œ/݇Ïݡķ=Ù@ÛÒ5iÏLi˜`ƒÂuó¡JD Åeå¨M| Ës$ÎvnüÀÁÑ´î€'îyZIõ»Âi‰ËLþ˜¼ÇáŽF&¾‚œšÏ­ÝÂøçjé&CG\E“¡;Ñ!}Š{Èò§-HÄœJ¡Y¥c!BïܾjJçaoÍÁÀ£8z,°,›“©«fÎ>°›û^‡¾N!Üê.s‡-qJ µ“ÏrwÛlO¸7w‹öRùU̢ƧGÑK×BÝåwD©Ï©uŸbN2ò\iG(ÏÉ*eû÷¼/·1´¨{ÖoƒÅ÷äé›Ä>ôhއ¸‹[ðÐæ^¸”„´â ™XT mgo¦î`Aß2Ð]d/J€óx Ͼ–  'l"˯Çé*›„××þ¹] LL·d7ß`ê>ã]f­{Âz6«ó=C&á#OuªëíHmžº/ÁbúeTZx|§â”|ò\Á‰-SnÄ£ãíé9㸠ïX¦r.~´ˆ(ïÆˆsc-q“°« ÷ÂZk1KõàÅH¸ùu9¾^­ÎïÜÞ@Çý z’ÙÝ™·q:‡¹Ïp{Ô^,ï…aÓ œŽž'…åX u{eæ²äYz|ë[Mº|³1œ¿Ó†-·¯±²uëçÔ“Ì k2¬empùLܽeM†ðʃ=¸pcz>BUÝáìC£_ùí*ÞZëŒ7ÿ>Ç9¿0Âq5Hècã哌=çíªE.F6TEÊå[äx*úR‚Î*~§*äh-.g…RSá¶a¨œ·ÅÓâVôÐbÆJÎ|dâ«×óäO¸h]Ÿ)Sφ)MÉø_3]“JV‚ñ‰ÄvÔf.í?¾>MÖTGÓþ/ÙÜPÕïîàN¢|ÜMJGÌ ‹ Òy¨ö?x2Ë•xa¨ †çUgùµÝh7fOד¥9ÿÖsû€Þ´s÷NÇr­"H¾•­ýgƯ=Ü ±¹ШX‘§œ…Â#ô£—/ß"%K+ ”ém÷g$¸d$,Úùµ!†ŸqýîÒ0ž†¤Ìä O¦¡²÷Qò²š‘}±å<«Å/=4 Vkyᘱà ËÏ,ù &±£¦Ì·½Æ¶åÔ´ì<öÏ™Éß„žgÂ_Ù3™•T<°~‹œ@ ,ø×*IkÞ›òp©‡¬œÎæ:+áÔmC0Oüq™£éÁý°íSÇ 'îß:4}é²³qôë9zÝRö.î9[P¤qYì?Üã9‘ÞËÎeëÆ~ ãÖ¶‚äœÝ0ëpŽr§¼FŒ?×BÔw;Þ¼Y›Mýs\‡Û¾mÝMÖløÜN â:©ÞûºcgÏ>ˆ(F»=6Aä*}¯>+üú$¯>íä»Ç0èñ:²ÔÈoµ­/ŸK°-³÷¾±G‰UC©ì÷E°mi;Xü»Ò“4hÅíGxRò†ðÓokZ8ã‰H Ãö¡Ü˜^…Ǜ߰¬3þ\åŠ*½¸Š²šÕdî£"š[­EËž¯d{ÿů?j¨øö*\X³Î½vâÂóði|±nYŽ[KeñóKÆõi¿ .«k²üàÚºeiMÖæ¾w³º-ç7u4Á'ù7Ë‹õ¢GCrÙ¬ÛøÜé)^Z6’F'6‘Í¥£uÉpïâuøðì“K4‹g¢Qh3Œ~+_LN‹„‘/0ì­ÎFî¼­ Vý.ÜërÛTÔÎ9ÙùP ¼÷Ű;_ éI ÆN¯JdñS¡âØEÁ¥ÛexôÞn;Ø£te§Îh@Âìðt™=_92 . |¨‡äX~½•RRa±Œ}“QGvd)ÐnÅ)ìŽÄtyGN‚Sï÷ ÙÛZ,ÇWb¦ç|­!NÓ G`xù)<®3 =…·öïÀ›×pŠï8܈‘*m0ôž¹z‰~ëò íqVŒ$Ó•jÍéq1]ôUðQ^‚^ªþã¬V’9ÕÓéiXÊ–«î!ëN˜Áò!ÖÜIú)<¬ë‚_NaP«†£õ™Æ‹.6³üyú÷Üv›ÁœÛBµKÔo×§Ù¿&ðó!‡ð³R ®{‡³,cYõUÞ—.ÁWŒÖ¦+nŠÐöÏqÐZðR™ž>eûÕjX¡F6T\¾ƒÎcARä ¨ÜÃá²ü`¿‹–æÇ+ èìÂ7Ðäm?D"mýq‡J&Ú¹op¼½-¾1=~%Â…Í[7¦OòI{…ÆŒ†ŸGÅ™ß zòrèþ±6õ<øÊ³†,–Ξ; ÀÙ¸ÑÅÊøîù( z:œž3¿ƒßNÄX%K~ºé¢|b9nÿ»Œ¯‹’àvÁ¤ëõ^Ò$uNNÍ%+@µÿ:X¹ŽüpÄÈþ5˜óD.v¥ÂóOÏ…‡z¢bÌ þ/`näBôÃ\pÜ'‡ŸÅ‡…cé”¡ßÜ]pÓá É)šAKšn‚ÖÇ:enVË"ÿÌAÂâ””Ø+—±¦¯at0‰«¼3öà ªöt[e&Vû"»¥ù±s^ÿx <:j€íRp÷°dØC)È˜ïŠ¿ÞØàAy?ð| ÿ;ÄòÊIöæt¸|Z®5ì†õ&Œm^*¦{ø}±tA]b ½3 Í*ï„Aº·A%L’m ž ,aøk¶$òÄ?éG%Ý`Pšý»áÞf0‚Ò¬d‘†=×Lçc\ØÊà0ôýõ¢C.€Î9[:wZénA&õh9'sKÛ™zºÔø\'ÅU8%ÍU¸ý7s ÛaÂ{Ìé͸Rˆy» ÃL Ðr¯˜¦û »›e¡Ñ,3 yD9ëÀ*÷4ûæ4¢îoöcvûÒoG«Uê±Î2QÐl.OÛ*v€®ä[˜m±× äÃumxâÞôIÆOÛõqYùQ\qý3øMK= C8ðÄ^Hä7Ãçly´÷#ExxŸ.KÒ"l» øÛø‘ÅRG¡{©3|r?Ž:‰²|ý¾¡d­®%”̦P¥ ô Öa >ôŽô<šä×6‰ñ£É²Â–Cÿò{è}` ¹|Z5œ>[¨Ê(366¸œ6¤àÙÎänéáðƒYLéT«Z; îÊ”ão W÷éñfWœÄÑJÎVàôTDçÀиO˜±á"Ž×>ÜàÙžEIõK½ P\éøäKÀ𱮬wèr6_¾çÔu|SçI%°6ÔmäÖÁmƒZÔkP§wƒRpűa´==ò:ÅiÝ{Öø›Ü& rŽ‹Q‹í'05òÑ»ÖAæ¿êÄ_6«\~¨ÄãÅ–,Tû”†á{‰ŸNq£–ËÒqÄñ“à`ÈáÏíVrò³;J.NóuÒñêÏè>,Ê¥Fzï#þ}4”F­”@—³òÜ·÷mt“ª­ Gã‘ÄLáì€vÜ|;ƒHéç£áúzöÏ©G}ZD¸,¢œX©ôã±iÊtU¸'d/µ‡âÔÉ,dÙ9bºB†åçáâ²Ú4äÛ*ÑÇnipÙ8”»î‚zÊ-dËšÌ"=f¬¬æZöoÀë‰ ¤ä3=ŸÝ joˆ?H£½Á s~²èŒóÂê5˜±Ý‘ý|ÒÀÆ­ÙŸ^Â`¸¡4 r £ñÚÓ ü>N¾èÄïÌ>½z‡Í-\ µV“fa#20¸rϯ`Á›dHi€wšFnØìbßWŒ ³¯PcM ­o’šAžàõ†ˆN_¡ÊóÙ=u|gv€ÈV‚˜÷wWÒÃÊMÒX»×{îÀ·VCIa`&<¬\,¤i2à\?L°ò× F< ÈÏáÞ£´èW2‚ö”@ѸÛc1dE+sé(1*þi }l`D¯¤M€Y…u`ídŒ- *À1* *O%bÇÔ‹¸Ã \ú›q Þ¿¼µ¤UR™$%µ[&ˆÐ<_iÞ—ÿâ«2ûvºfåG Âjnëqé U¸hÁaø¨¥FËËò¦¸f’¯¯2-¨ìÀ4Ø`M@ìò¯Û q6Ç®ÚüÊël6G*ŒNQ±¦M;äXË¿ó°@'š,Z';BnÅ\“eÆÞø}Ç/¶gIVEf'–aëô^0{Á¹ª/˹Y&J蛯?jdáÂrC7Þ‰F:WØ%ÕÕ.Ïv݃)—PÙcfh} €–¹*`hÚBöcžÞ>W5*ó5=Ýäè¢h¢:Ê•5‰Aäæز.ïöÍA¹m w¥F2;‡Bhz»ìG‡ÂÏæKبð„v !þ±+°åÀd²dø]fï'¬Î Ü'·cÒa (öX8÷.â벡¼ VïOšÏÏ(ôæTÝ!C9„Ÿ´‚És+pîy:æÆ\þGÜî–þEý·kùòÑSíʰŒHñš'„$ =AêÁ 0œ¢ÂUÕΠ㪅X|ñ-Ã6š»³ÔähR¹ï v‘§­Þ,s3?Xœ!Ò7ˆŒáTº²öûrt½0,Ÿ-~kÊK'&"G‘g7&ÙëÒ5°&&³52C ÑÇF~»ËÚкéÍòŠÄ¢GãiöµqdÉ¢pÙqr[ÍÁ¥l]`_ë<'â$>žš•Þ>FŽÿy7”fý”áˆñ^²‚N?sSØê3Š-|ù*Òøg.§ bÛÜh j)Ï‹~¢|ü+¶0ÉšBÓù‘Öª–!†¯XÁÝ5¨ï×.áÛ9p‹Vûa“ð£k*¬©+"·Ë&á5îÅŸ;JW}¼õvñ®öXønÍ NºÁ ¸Íœ&$ÒhÇ=¼okî n’ÀèŠ÷à¦ó—µU4ƒ¿a.|ôH Öã6‘=Ÿ_Ðmòdˆ[ w*ÐàÉj ˜5:›¿x9žº)K»›`”{&¸ô€ò5̼ØËÜÝzÈí";ÿ¨ÐÀPy®\K„¯WÇb¤S#Î×âª)ðºÊ 7¥Šòƒ;ýqW¥$.³rùŒ2Ô½öz\ÍAŸk`Xô^Ø0{ O«‰…´qÛѯDò§€š¯C[ýÅÔ}ªÊl»ÝïgÒ¯"Ë>úá£%îEcmCˆÀl—4ͯV£w ’xag&;»{'ˆ9®æ£ú›œ n`Þ¹+ÀÝ÷¢£!Ïθ;M? £óÏt°Àxú$®¶”Á¿‡bô†èxXl÷‰o…e¨KGDÌÅ­åNC[Ùl\ÃËdÏSƒ¯è”Å ÔÙm{Eùyïg¸Àa+ï¶Ù £N--¥ |Kš:Ù$Ж§3è½ë0Μ¡ìZ<üñ ¾ÇçðfJ$(ÁéSZ°$zn å.@Bö*[úq"ÝcžARw€bÍ!i|Df3(h%±Q/Fò;;ááØ”=L»ïNÆÝ:F'j£Œì ñÔtÔ¬ãÏ›±1'ÐjÿbZåèL¸-çÞÜ¿M¦ B=øü¸¥ôßô¯¾‡‰rÎÌ„Y +# ±Ju ®“ÔDÃEß1U!>>S#uk¯`Ü*uúðè>Xx$=Êx•“ ýëø<º‡C™²-Œˆ÷æ·Z9\ƒ³*M™ =wFTCÙùÍô¦"-›QôÚË‘ôÞ2/øg¤ÇD—aωZÒ(vž™ô?ûõrç”sõ¢:Ì*NVåÿÕ³Už+"×*¥!ÎQŽ–~{Z7uý ´ˆœ‹‡ôu°wn#Ù%Äc#éIÍ©œ.ÇWm€û’ÁdQW­ÐèÃw,® „æÂDHJϹá&ÔN[Ú®Ÿb†U~T5ªÇß‘C1F˜²o^òƒQg#hx¾=è ¿ÓÉãùÐQ¨Á`qÉsøåT—.fâäähª¤dJÇùÉV j0L Ê'iºƒ}Ìæõ×¥OÈ -yðìÖY¦.¡VËÏ0×ù¿¡ðü˜lÊC%y²û+rx¿-­j†#Ã.`¨¬9ûz@v9Ä,‹„‡àaÛ<åü ÷Mÿ!,HX ¯ ˜hú<¡„¯[ÎaÏx÷öÔULÁåÇGâ"߃è÷~~xÇ厑 ðXž[t J¨ñ½¿êP£í(þ{+vЕ›–Áö™4…¨Íúã¼S&×1ãkU8oö=0­×Ÿa¥¤%/º§ Qy³™“tlž±×YM¥û‡pÉ!r,×N—Kw(òf ùÊÅC@ܦ t^3Q›<¬—º÷±ãº{¨’ŸïS¾»+›FX¡ž’ ʽõ±­µp®ó.v äè³cõ/¤[´T°3_†.¾ÿ Tö6Lî:ûlE¸ä™§‚'[Nܲá:Ù“ÕCéÚž*0Y¿ƒö½Á«a¶o×GÍW)pfêh¾äà^V(¢ãü·á—]ñxi± ÿ¬w‰Ym> ?NTáßþ xÐÝûjœXÉÔLüp†ŒyÕHjÇ‘ò'É‹í'I³† Ÿ©= õ'—bìÙô}þrbôͶ•ÇÅ•V¯)x«VŒÓ‡Ì‚Ó®lý•œÍ+e 60Ps þ<è#|k9¾£'îÆO¦íÀ£©­x,gssA¦ŠO 97 ÐcØPn5¹ÖoÙ†^¡8Cb1ZÛ ¨â šÎ}±ªôšàÁr-¬ ·ûýÁ7 ½fb¡…7ÍK{Kj´‡é”NüD³Á¾k=Ô7A«±¦< ^×v‰eZ^ÂI;ñtÓe2rÒd²óãP—}ãtĹ“›._ð4‘Ƽ_K/Tu‘“V\Vp¦Œ’¢Ó¾ÕàÅ7uPµOš´» VG»¡ãÍhÕÖå‘\ç%Tá7s.±"'žpâ¡E6´ôÇ~¨‘S#AC¯hï3!'ðö¸àã$Á3«Pa±X›.Ê8T¹xÝÇþú(ÑË“Li`Ì$ôZÙA;%øÙ¼XLÊõÀ͘›^æòøþò;ñ.\› }kôé¦]?X‰®(WSp†íYƒxRyŽíõ¼†©—¬xÅó@}¦µ›$uÖ=af‡ ¼$ÍàS¹—…Ìsóó‡¹pHUGc½õRjÕ$ÅKn‡*EŸø—Â%½«êÉà¾ÿq0ˆgß^$ƒZB±`p=˜® ¦i;¶ó9¥{ñïª=pÍ¥Ÿº×‘)ãð^†:jæÛÁi(XdÉ‚ŠW œ\hÉÅœ“Ø‘vq1Z‚,:9‡÷í¶`óç_gºõÉ[0zå"Þw¶2½ÌðØ–yP#lCã„‘6e—²\g¬ Aþµ.¤$PœðàùlÕŽóRŸ£ÊV%v%-Ñø#é 0ý4ÇQSJIv¡ Ÿº¬$z æ¸ó¬"\؂» DŸ‹ó=ß²MŸq‹œŸwþ6›Uô>Ц0ýfi6v¸w3s§w°ìÀ6èß…å¡«ù “ð§OÝ`‹µN@ó9ÄÔ³®è7môìW¤o:® Qv)Œæ…/aâBÈÌ1àK낸qì4ܪ°@pØT–ß(ßBðÜêOÐ6Ï™‚?ù@– óö ­8Zghž¹‰ÙVËñ×»^p“äÒÏTØ[ÅðGñœ•p‚‡KXø¥ÐÚ¬ÇçßUÇiôú¨$ÁaK¸ñ’õpEz“Ëüx *Õ£Måçâ”ÍDîÜbxé~.äNJ+hûÓbÐ?žÊ¢ðJî.œ½{<'óÈÒý;!Él%© qÄ©KUAõŸ3ž«…Ã|pü¼4´Ÿ'ˇ/¨†8êHov^ÀM›L¨Ø’np{Yæl5q ¢U˵éMurgE¥ð°ÆAkÈN¾A÷)Yæö’­ òÂÔ5ßY^`4´¿_'¾ §’5mÂin „1c´èÀÝ+‚c¦Æ<¤~ ~»"aSZ`¬ˆ(­`†`³Uƒ¶æÀÉ9Lý$6ηâb>cøƒ§paÇ;x;FžêÝè;ärö….lþ¨ ‹l)û]®ÎÕ>W@r—iðü‰NÂ& —TÄ+£D©ºèVÝ2ûîǹ<±ó­ÛLBŸï!ï·>E+=c\¡5ÀëNtC$ÎæùŽßÉAÅíxuÎ#Œ˜±{»ü{ îá‹‚ÍÐÍ´ÉgÍ=¬ÅÍtgš½É.¯7àïEãÜ™06V“Lj¶²¬ù/ðçpKÜ2a>8+N'+‚ÝŽœ~…tN4¦‰{&âÐY)H·À“!štÑ….`4€ bŸP"$~ÆRõ±ô¼ƒ$}Þ…‡”$8}£DÎO³Á¤åÚçRÆLý=°fƒ5(ïÅ|y=žum ;|䔌”ä#¯°ðéñdþ~ïÑ16Éø†J]ijŽxTBœ,yÆ)- W¡Ä4§8AÖD8q׫nKµlÜ·k }Ý%·þ°è[..ú G<×¢ÕkÏa|E›¹oð,Ÿ;@Ø4Jcd¹[à òr÷$Hùžéû$Ù%K‚¦Ê%¨öh.xá‰åƒÜ×­¯ž½wÄD/U–™þè¿{l»¬Ž¸IêÑÆ+LJ¿ƒ-§'›¤Ì4Tߨé9œ^7Ç#Ê%éñ3ñt‹7HÂ=ôþÏ”ÃÃó-¸àa#F–)ðÁø8«q'\ZïC§sà’ê-½oª°]Ñ”ç„üÉfPX{WpÎéœÌYÌTdžà築 oŠáHá´H|)X›wv.›Ìņ£…Ǧ£ ÕÇ“@1ý8¶Ï<…¦I&°½RJ(3g Y'SB"à‰€›íÞ»ù #¸‰‚ÈVå?×í3Ÿ±øÁò-®8œJ×Ͷ žDZ”¯ç϶VãR»£Ðcñ“s÷4ñÌjXûr ÕZøv<Ôâ#¶¶‘Ñù±Â Óåé ›‘´Rü,ðð´ÖHœ6Qžþ²§¸ñv­à;¡?‡·Ãeê¹ü Âît—†éßëèrŸÏ`êãË#<¼!x³.ì9¹“‰þ™2oÑ/ šTfÅ~AP´'F=°a´;wY«Sïõ€G. ,®Â&WQX7V“¯Æàòz1^²u<ÿµ+±ÁÇPtÊð†ýÝD9qÅï6ìÌ_pøÜÅ,Ógðã ÔÒ{(ÍsœG/G¬¨÷¼W`Ä¿>vñ´AÞ˜áÂ' fÃä)#¹K@),˜½Rúqú¾û,~m‘ön‚àO!|O–1¯ #8ëÂÔ˜ìËÕúvㆵҘ–”KFËœ`¶? špѽÞãîËSUrAj8D› ázµ8s½‘Cn}± Uî.8¹ó>ӈ˄®'ðx†1d®q„–¦>,OÚFT¶ b~Š{åX(ŸµïO/MÇQƒücwvÔâ3ô‹—f°ªÍáÄv©wš}¾ªðÌíåN…o„=FÂ÷3üÄ.G\u9µáæñŠT.e%4êà•ÑQ`þv½Bc‘ùžîP=¥{¿ÿƒ©žÒÔ»-YG^-Û‰&G¢\â~­£[6¥ÝŽÈX&ûåc -ƒ˜ðNôòä`ŠëÜut—ô†§"'ଓÚ¬4çO¿ëqš2›§d‰òS§B iíoRüu.mp~3m…³þ©ƒÆ”3ðþë|oÅåáÄ”‰Èݳ²hh–„—êBöЇxþ](Êsù”Õ|J¼69ò½pÚBþ*æµ@ÌÒŽ:?ØÆLü*¡§­):ò·7D©Ô E:<ì=üò«Åž£héTXggÄÕâ¹Å9Yz$x·«’¢Eý4rûÝŸ ßK’ÿœ•W7ÔrùOšlZCÖöOÆqæˆûhZÒ£™>üÚ_ ¼YvzÕ’ðÏø|1P rå ð£é&~¿7ž¼œ#3,øŒ ÚζÐagŠø®¸f"qÞ–OýÍ r•²oÚ û멟ç7·-䥻ÔyLs7¨ãÍgÝÆмc\z“À/6þ&7JðÜ$qv/ã Gé¡ôÆ£½T9Ô„h†ß¤·\,Ù´;-xu˺wr ÞÓ ÚR.qÇ6üUg#·ÙÒaôm·‚«,µy¸m~¨™Œ%ò;)US–«#ÑÞõ)÷Sv¡{¶”sã]×fìàÎ+ ²Îz»8ãJKgjÑæ%ÁüĶƒÅ#f4aèàÍn¦ÇOnâncÌéûÒ ¸¦oޏäÀ?kºóSÄøk)â[½ð¯f:¿ þ5 q»ÚR&1Ý&NýhÿŒ-öW ?ãºè,Ã1³HõññpÍÃÜÛò lŽ¡|_DlQãæ«ÜiݵÜâ¹ æ¬ô‡žÐ¡ÿŠ ¯õ÷ ½ªáôÏÛáQˆ ?Äæòõ‹„— _Â¬× Eó]ö\´¡÷—M!Òy}L!«j‰Pº+ýÃÓ¹YO¬Rf¸l¶'_ˆm¸!ï´ËûÂÕŒ‡¸@¡åä¯ ò ‡Ò^W)pùzMøOm3oIfô\a›^N¦æŒHÐ]®g_‹×Kñt³¨ôL¦½@Ï<%þÍ“YßlqN:Ñ&8‹ª§ Èb_’'/°<îSɸץ†<25¤Ã—äÔñ p;ÇhÅvW’°@jù\/Ã5\c;ã®ØpZ™>„ž4N²glÇF“ö†W¢ôgœ,LÞŒKö›‚Ax,îÒÝŠ!?ž  uâe²QØ¢"Áo¤xáŒáô¸÷3‡A»Qg VÂÅÍwqxy$¾1øU_`…õrô~' OØ} ËΟ¸û¸*®Ãï~ÞfF*宣9]Ï¥ŒŸÃýötaËo.Œ ! •6K±­î|^u]¦M™5¨ü­Pœ42‹¯â›ïàŸÙ2tÓ†`(õ–¢=â;àrÙs¼`øŠiî—ã~¦ÑåÅ g‹·rÃñýLdÔ n[”É»ë=Iâ^=Y4»,8½·“}No@¹ˆ©”M{ä9«Šð¦~?}pMj8Í“¯ÁüVMzÿœ;ýèC5CèÍO ÷GªƒòayÉ ø÷2«Ó2áîÓ¥h,sƒ”“«yÖ¯"´ò¿D•£çÎ8>:[Ÿ¯µØsf´¡œL¹®B1ãë4Þ" ÈùOB®ƒkà+&ø#F“kÍqÚ¯•Ô»ÿ&&æ?ÿkþáM T¯<‡ÉÌŸQ:+-ŸÀ$Œ‹²¢ºÙâøär+ ½§F'ÝÚGMÒÿ ¼î„æwwØ0½­ ß(OS&·ÁÜãû!fH—]µŒz°˜;m‡D±Õ’Šþ „Š_þXë’êbUynLù…Ö6‹?Ž…s¦ñI³Ciø %ª¤5‘|ôÔU\ ƒêÓíaïJ]_kâcÙ±”)¢žávdWèPøéÊ ÂWòŠpQ<:26Öå¹OÄ¡¦DDàzÚ ÀïoxKz+´IG2uóLˆŠ‰¡–šæµ¿†äh¯¦Öâóñ襋ø$í#û`Õ«xè½!ïLã©û]øãí·ÉÀþDj2þ¸s½÷ŒŸK_È­†Î1êØµs /îÃÜ6Ë”]´o÷>´nœºÔ@ö8ëâ¦ÅspD‰Hù`U” ý¶7Ž;nŸDnéÐöÖXX©·#dŽƒŽ¥>-p‹Á”?±pøZ4—Þ‹Òg çS9k¾ZB+¼&9¦°«i=ÏÂEšÀs8bÑžá8îO1Λ´6¤°¬ûqÝ îx626~·‚Ñ¢$)©oÁ 87ënûG\þHF¿s aö%¤§@‘æî_ÉÆ}턃[És -Œy*‚5×õiõ_Qlß<–-~fÈ×lX£ÇUX›)ƒ“>îþ¦Zxv\ôí8¥…¸þp ù¸Tç©M¡7¶ 7óMÈ$‰VÌ»aÁu©ýµ¸sJ݈kÙklÒû‰U“ÐpQ"Nr‡%>’×=æp d œg¸«M‚;]C}“`²A’öW"aÞ²zà²Ëlô”;™Z;¡?)‚ß«˜‡ Ýèx¼ÝÿžÆé8KZÞ1¤[#¦s݈«¤±o=Þ0±Aëgs!ѱUNõÃßÔcÂgkͱ80ƒx,ŸÊ˨y`_¶Lÿó8¯Ò?ÝZoHè/U:Èùy³8¥©ëè÷Øuಆ/XßWÖˆðÉ;`ÃÛ±.;-òpoñW¼!îG2œew? ÇÿÖØ'måÇEéë9»Pʦš~ëOb1 ¦óþ Õp~@„wA}ïªñ×Öz ž@6N¤Òƒø~»ó6;´Äw¯œ ÓÆ½wÙ×AEg¬ p©*Jøë¼êë(XÝ„-F™]{_\S +“àÑãO8áä%òVÞû'LÇ #v¹ÜU»ë1©ý…¶w=è±´‰tºXÓ_t„%MÐÁ8;zb½÷ñª ê»XÙws>¾rÃæã‡¼=ôÞf#íЩ© 6¦æ¹!°|cL|X]?þ§Õôn¿WJ¼ç™Ñ¸û0>©¦œŽ¥£wàÒ®tØî$ ?U¥1]ÓϨˆÐ¦Î˜üio}X¨¹ô.Ë®uÅïÃY¤Ù(0œ?¹˜ Eûöcï×ÝðVÞ’^ÝÁ%*#¹[\3LZš‹ÃE¨½‘w˜:–Þ›n{e¬2™&W»a¡{„®8‹*‰Ïú6¨PØE:Ç©ÑøæD]ƒÆðÕøn‘7¹Dç¿»¤>¸Ìð)Ä¡SœQñPé;;Ç¾ÂæÍw˜‘¼-tÌÎIJ=¾8õ‘ ]¥ ^ ¤p¢ÞMü[}sn Ž_óç’AIœ6g& 0/l:¿Qãè²Âךs¥RíßÊíó yîX¶~NrýÌîß9LsLcˆSÕ>¾¾©OtíÅü iÂøs±ôÑÈ+jÂZ›ày•4]£‡'^Ñ€–Hø+ZÿùŠƒ•? ­»ñqªKÃúö˨:ÎSN¹±- zòVƒ«c¤ÍÚf£Eˆú•?`ãG¾(… Ÿ¹gb›Ÿ1\O©ßpÙç°4·%üv¢Q„ }?4¹ówT/<¹‡žàÐôùüˆn êüæ÷ÇÁ¿âÔ>ñy¥~ß»?†@íÍæ™zünüé¿Åƒz> ¾<¸kî…·î ~t ƒ„éª0]Z•ú}^"Ì”2@ÿG7!úW‹ÜòK0s{Ñk×ãÞ7¸®l㟉+¾A§e…àÐá‡P#Ü~®„â,ð¨£ݵ{ì”/‚R}# ÛŠQ÷¦7ýdZÓÐñÄNðíQe7g5ĆWa”º.ĺµƒ…¥„žePüÜ µ  ¶p(:Í|ëRɹ lÈšy´ãF+b¦2Šþ­€ùºÃè¥Ë[Q¢A6lÌ…ÝÇA|‹åþe #‡a C¼.É?Ä—4|‘ùˆÞæØ¦'i0*QfXÚ±èµü¥^~W:…ÆIüÐe,zÓá>ŸC‚á‘NxpùVÜ©g; ;Xü½ÇÌ­¢ö¦[Aëëw‚ÌY‹PÔÿŠù-eC®d0?Å…`£}z_~#û| áOuèK•à¶Ü½¨SoN$úÊðöÈ\4äMAi³±äÜÐ`Tü–ÙP×lë:=:µe!]õØ'u”àˆÇÞ°ýö€px±<½Þòö¢MM"Œóð' ýZ|þD_þ¨€J½Ú8Ûq‡ E¬‘›Õ‰bùi’½R Ûi'ÞÔz†?Ó×yc9 ÷‡¿½ÏØãU¦oC¿µ)3é²Ï0´|¼-92_áT~R_…ŸÍ:ذ­!cMŸ£´PË»™$öo´þ­&êßwóŽ•£Rl”\S¥‰¡Í¸ê~ÙQ›††Æh3u!¸;l‡Ðî…\Ý\•÷e¹áj E¦1: «Ð¢Ä4 ¨Q澕Án×C¬p¯åãF-ÆÍ3ÔÙˆàÇi -Â-ŠÑ¬hCÑ\r„}îÓåJotáŒ0Ny“ WÇŸ#Ÿ\'‘sTà“ë%¢³:¢¨cË™>ÈJq„ ÁWAË{2jJö¾xÏ•d”÷f¾¤[†Ž™Í 3Š g“âá1°¬CH4fÝN_§H£}œxõ?þxhÚÜß„‡~ßG¿5y°³û%ºW`ƪvêˆ%õ;Ì1̾+.Ê¥ÅGȅ̳$êI˜§w㯉R<šG²ûMêÌõÀ7¸ú¾ÔœÜqåCu8Ô¡Å~6æ!lÿ‰:uX|çR"=}ÈûA›s¢ðw\=yPAú.6äštø“ ¨½^štïªxPBžÖϯƒ’Çûñqþzf“„ìn·$mQgØê9;aúùEÄÍö!J^Ž¥žÊÃÐSz,=QìFï¶½^9 Ëœ­_ñ:; wçoBÿ—-¸ð”=]þ—ؽ}ßpâ4HÕÏ'ÿy‚U»}a¾ÃPÚªöûÝ.Bw¤:ŒËV£7ÆÅ±ô–'ŒoÑ„Y“&sÿ;31ÂNœ&úÊ`¯êaØë¦HÓ<ÇcÓÁ%éY’#ÐJFbw?„7Ñ Ô?Бõx~À4+ ™²²Npá’µ¯„Ú—vCIš†M¾M¤ÇžÄåu)$ÀËWfõ¹Ä†ØÁz½ó,æ];- wÏÅÇc†’´ºYÐkOÛ3Ú]ÂïŒBƒØgØýg8}_ƒöŸM"?“&qݶx&r1]öë:vص’ª!dæçiÐu÷Ó)„eÐ:í^Û§ k,f±û“6â@Ä$HøéÛú¬°´3ÝÅàK.zÁ‹Vo)Q*¡nMùM„÷¿K0vïSÐ,‡IÞª|ùdw¨K·éúx~`ZgLòŸþ ϾëA@p ѼC1o`‚¾1<iP_Ò q™õ§“Ò1ØTtr'€£Ÿ!Ü‚8fŽê@Ù˜»Pù)v¦Ã<Ûr¦›j@G|ê„Ø§§arßXkd‹çY{@6l8bYè(mxüP†7UœiÈS~F|A…± ­«ô'5£D)žˆ…ù 4LLX¹ß.»0i ®¼­Ÿx&ÚˆqueáÄ9gÙ/ƒÓÐh&‡ã®ïµ}¸gï~X_~k…úxãR-lÓTaG¢ðäÉcø~kK0Ÿ%ü+¦N#úÄéÊ×Â5¿â¸™ÎhX¤C˜¿¤ ä}ú‡e¹J¬ÚÝ:åÏ8o¬ ‡/ßßâãÖ_Âù÷ÌajT.\y›ÏÂæ&Ö£ÍÜ$&¢‘Ú‰«è1Ér(l±bÛ‚„ÃAJ½-Þþóæi&@i½€7ùnmtžQ>ú­Á“æL§S'âÛ{ ÐbN)hÞÛ| i¯—,ôhøŒ1h¨'ŽZAûp¶%þÎAÿÒ‘B•7ÁìÕ*Yˆr=‚óo¾‚òšXn;Z“ŽûãЪ4œö¥‘¬;ú o˜Ã³ªj˜8yjh“¡•pyT-XœœVv’T«Jí´ùµm‚]¦K ^K‹„D+@ä@‰ ™VÎôúÔT³hØ¿ ¾õmÅå¾ìBË.77ƒãâ§Áëê1üæ°“$Œ‰dÃÿ-±:ÔÝËÝ×Ö°Ù>¨O{…‹-Ç‚\åç†*êÀÝEâ ÙU›°›Us¦³§?ÃYÐÕðÕÒŒoìTƃšpÝ:âÛ¢CJ7¶¢4“E‹¾C2\Ä ŠÝx"Û I_ÇJÃlðýõV ï”ýæ´Åö´YÎ'wegàáÎ×l²áL>4è 8ÄXòÏ® °Â² îÐâ;q`ë ªÀœŒ8Nt’ɯʰÑ>~,Æÿ‹UêƒRo†€kq­à“‡j(D“П¢T°ÅF\ÑN&Ee‘ ¼rC ¸°`§æ^e»IË_†õÙ`òWÖ=}òï²YÛ.ÁéïrÔmóЄßP8^BK÷³ˆæ"’ùjPË´ÉñncýXiêZ<^X)ÑËd6Ÿ„JÏarm"8|ñ½ßÉÈZ)úýƲ4» ‡v²m7WAÓÆ¸£ƒ§ÿ©Pñ v8æ¨)9:cž:÷ŽÉbΉøØy)ßpñ+³ñ³Gû¤Œ}sRpáæm¶ðøTXÑRï®×ƒg?hn¨ƒP %>ñ}ñW>»cŽìÒT4˜×‡ŸWÏ@YUtì k|é [ïçDPOÃÛ²ø40ÍD˜k@?Ž’á‹›ÉüGI(’iD3šÑ*#÷ô>¨1¾ ýé ¦Z”æ-s¯^BÀ]û(›ñ¾=ž}«Â3z"¿NÚP²"D Ì•n€u_Ym€ã+2ùo“ ¸=œRß'°FE#ÿ®¡ÔÔZËiCX]1ß‘¥ñY-ÓÉùFÌ®ìÅÉ”xUy&Œ•X ë%ðôâ|ðiû‡ŠÓèß“Ga¿gÝØ=›¾75âÆ?Gsk¹‰/(K]U`mÖ9råhzén„šÇбЀ¯êœÊ/yŠÕÃðCI!þ˜çÅB¶ÚrëËóø£!]ätb^Û¥—&áÁ#²Ô.XvϳÁå^¡dÝÔB¤¯RÙ~á9­útO°ôk(!i~!8µÛV˜ÿÔ’Ûo¨ÆÝ3¬`ô_¦ty)~8DŠÏ`eQœhkÆàžd—µ².·Ìfóü7x½©%M2idsÄ2¾ËUŠ¿šø°>jq2 |t ;^ÔCÓ”ÈÿüÓÚ@¸ì3çþRO4C[~×~€­ä;aÙý[0pK„zz%É¥!0õy#ÖéÂ-søÌãÏ„1ã§ò!Š'±Àz27K\2©“˜ÊúkìÀ^I,i]Ìß¼à÷‰9.W‚Ĥ0NË'Å o°þQ=ô;êã›’a|ü†…`£HµtÌCl-½8c8ÍÊ]Q6—ï‡Lý}è¤Î/M>„gÖ;Õ72H²o ¯®ï€’CPXk@´×œ'N'ê`”–(ÿZ¥E,ÍgÓåÃi•h-$/A]¶ï 7AéÛ42åÙHn©qŽªÙѱžȑŒT46<Õ3iè'i¾a¥Ý»àÅä[øÒþ1,^N³7ÒNŠNgsÍÆ´ïs3¤L ‡PceöHdÍWËÏðˆÙ%¸ëtïøfÂ}ush¦p4”ß×Çá§Ûðyª>Õš—F§ßù˜ÅBJ­^÷Äânƒxüy%€?1àýá^þ8:ñÂ)æ¼: ÏÌ]G_ZÚ Î7>oA/ïödˆ uŸVókÍq'êwcHi9a‰wà 3rÎm/Âÿ<Ñs¦lć—WÒÂ…™Ä–øsk»0ÏÍ}/èÑi>£oü[X>â’KIuùÒ³|¥“©Ò³"Œ|"‹ «ìiŸu+&ªÿÆ×K”¸õ‡zòë6mÌ0þ­]vLÀ£¥é†·"TåHÌôK⥣ì°4ö33»K,Õçñͺ®°4RT‡_`=7GÂðFmþç÷&<œ´d«Þ†›¶§Áò»ÂϘ܆šÝíÌëµ%Þ½ƒøáëm`M3[Exid6ΫÁ+>“øù€<Áß9ðHI ²¾¬Æ Ó¯‘ÿ|Ê »Ü¸B°;<7\F`,{‡/­ÚHÛ+ü}ч7^Ÿ™2“iÏÀ|g¹ÇWí!¥óL`øi: D5Mo²ûû ¨|Û#ð¸ÏÌpõ”w¸.´š:Ÿ‘kaÉR2ž¿ú[y»’ðÁD,´£Å~r°­/ _*‚ŠJ>¸ ÑÖ ßØÉ›ÛÀˆU€Ï7ÖõPŸ\Évý‚=[n‚׊éxåÉu0ôÒà³û« 8*)\}¹‹rè¿e;„c— B†,½–±—·þ”¢R9âØ1l76¯[@_‘_÷`—Š_•*KÂ_\‡;†wÁ!øq·l¼èÔ‰a c¨‡ 2aª@¼Ý—âë*ÈZ5œ&O¦ FV Ö0šÛIJ´½à²ÔBZéôDž¼…j‰ÃUß°³&? »sØvŠ,á½"‡uO^ß¹š~7…9ßJq~ã]Xµß‹+$W°ñKŸ“¸Á^,u7‘*¾pà1ú4òÃ6¼»ëJOBš<7Û¿‘nšxž¼ØÆåX.WòÖ'.ÏOâOe’ cÜ1˜?© æý< FS“xÙÆË$eV!Ú<0âVc–qïÑûèX³jý&—%åWÂf“ᬣȒç¼\‚jNIðr˜ ÞŠÎõÁŠ^c®ÞÞÍRO« âh*8í‚3wUø†MŸ¸½œ%ØpM“'Œ(«ƻ`ç3¨ñ2æ÷>ŸÀ¾ùâôýò9ùú›¶îÑ^B%©ÐÑ'àö 1nyŸÿÀÿêÅŸÿ^`É™Q4Yòž¸4Å‹8IÄY_ÂoªLmJî²ÏÔ Kã<]UŠñ)tÎ_+vì$'Ÿ„yþ#øÖ]vÜÅÁŸºu®#lI@5;óˆó±-q@?¸ÃÁ…Xͦáôä±ôÎ'ŸNž†hòmIYP3p ‚÷V¡`¡5-œ“‡÷Ÿ…è…²üб¨9ׂŸ˜2¯ W…‰brô»ñ(zò§eû¡ß,–õL€EoüxÀ8ð6|Àn|Ì'›_?Ãñy¸Å–ºâ¢®Z¢žÓJVæç³ÊþF¶äæ@)+Štgo*9·Þ—ýçÙæ+šåaA͈»Ê²*y¤ëQÖ4‰õÒ| x “Šõ-3±ã‚U.0jÿ;R·ßŽxê<ÉŒ³ù–£B¤š×ã3S1ºë»/lŒMÁ+3vváDÔáM8ßÝÇû¸ÀÁ7¶t™ß|VºÓ‘ñMd+Gëã²°NôžYC†ü ‚Ÿ Õè¯Ý˜ùû=:»òɤ<ŠQ³i‡E#¸{I[ÔG¸r[4X<ƒO—@:Ä‹=ñ/Eîugгïk’J/èà/Js?vÂsù (^bÎI˜–g·Áé‚×Xb°¯‰¥;Jõ ì×ç#¢F²i&óVã7 l#ËžôˆbŦz Õ_O•%§“rWjøI…lPÃ’„)´É-˜us¿)T£%]s,Ú taÜ)mþLõZÆÄßË0ªÈ#ÿe[f܃x½½h•ÉŽâz÷ÂÇ>è¨Ù"Øß Ûr¡Ye„p†œü ^TÇÙe˜’'E8Ÿ,L¬Ä¡éL,ô§ðÖ†Bìþ,G'*áº¡ÉøAç/) ~çÞÖ¢Øò <8Ê×%áý2Lí6¦=VÎøñf6F&pV~‰Æò¼‘&TØ ƒš÷רé¸`½Ÿrºµò. [Ž?&÷v5BTQ5 ;÷•÷}% Æhz7æ¿/ˆþYG{e1òü8õÞn’‚#f.tsu2m^µ’®«\º6[HþØ*ÜŸ²ê´{‹Ýtö .8dÂrK ™éã‹t.fžÆ/{‚OêÄѵRŠG˜ªc¸êb8ýz,½3ì9;cz vÐñ œà‚îZÜäø>è˜6”Ž7Œåº»OÅŠ‹Áçì12o2ÝQìÉ¿èë£ß¼}ðµÙœ[®4壾Èኧ"X÷Ø-.Á™WG²{#‡Så B-“,è½GC¡Dx‡ê?ØÔãBGˆšƒú«¤ëÝq0?”-œgü tæ¥S–БòÑtˆZ=óí •Ü=‚:¯‚Þ¼5ülq;‹›/Mj·Þ•Ok°[b:hHìd+Þ·‚Ûe?<Ÿt;v…Òùï^éM`uHŽùéÖcuwN÷“¬ŸÛïü$Qî®øþP‚Î¥‡0æäXTÎf¯²E)±úx'Ì8§ÀÍ^ÍþÿH–6 å¶5¸UNNÍèbŸGæÒ_ïª 4GŸþMñD/(›C••¹78Ð%ÛíYí•ÓpxÇGœÓ•Kp`V4|(ñs‰y ‚²ä0Ã.Žo*Ïoq¡µû/,y‚sËòyGfÖ ÿ»U^œ†ê™*ðjßÜÕÖ nz0ì(¹i$d*“ôh¤§S˜—õ6wœ‚®Gtá}yrfP‘V7fUß‹ŽíÙ©Ú”'Êð͇'àÅÖý,~: ™`,´iÏ%Xч ÄV”/ÓÁ3#ô°(#N· 'QS]œ<~cßEmž¨^€qØÉ¤nŒæ n´ìC.FÈͧ%ÏRüŒ„ÓZÌèfMC”«P‚gÆyèš*C»Ò†à©„J°W ÆO+qµôKXû!€íXõCð¢åé5€âS7¸; d·…O·&ƒ½ûx:JLÀ™rWƒ@N–î_ùšm:$/]Ó`ÝáRÔ2yÏî=2ÃþÅ_`÷Ò×(2¯ ÷Í™KÓÜcí-?¡õ "#.F7|wæ­&iñ¿daÓÓ ØR N•¢¾ƒÿÃn²e¼1þT8‘CcÃ4¡ð½ÆEt\ÔKnºú~–lË='¤ŽÃ_w\®ö'LËâ‡4bàkÁ4žx$oÏK¡ß –?;[à°i/æ}7B»O8öî}6®l1¯2ÜŸFôÀÆt[ZúBˆ¯øÔê!ÔôC:[µOž?_¤þÎ-ì”h~JzÓ—’à•+Ñ]£E êÔŒ#×½a:É0,b$}xÁN¨[Ds²Mˆä‘ffê™Ó’^ÀzÕ@¡¯B>î0¿Îÿv9Ÿå\àœ¤r×ýSœÿ&|ë÷çä=…ÍßÒÙ`²²j̱KçÐR츱”¯}ŒÐy5\^ëÒ«ñ9°˜·Ì°'•ã´¡@)ÏUÆgÁ;Øt^…׬<Âëí¹Ê­‡0` ÁÿÙ¾ÀW†ZÜrã2ïêsXÙ8…_wŽ–=ÄæÑOyÑùø@—ºtb%‘‚°^…ùGÁ–.y Zýºòrƒb—ú„¨ò) ùƒ+޼ôÐ(ºdéFðïß.c?Šÿ³ÚL[.¯c 4ÅøÈ>5:ß]+?ÌÀî0áüäáBÍyµ8õKÎ[t‘jLxG¤ïmÃ=ùø-tíT±ZIïÀ YpDi7ç«/²Cø¸'±C¥’Ÿ•ø…W^Ƹ·Ú-o:v¦})ÉÜÌ %6ßÉÄó07ö*ØÌÊcùÛ7c­™(ý´qÛþ¸²–ÔÂÊüp>âýt}07§Lb¦Ñ(ó½dg/Ž$c–ÓgAÜäzAÂÃÛÐ;Jîö*ÀïêXœ{¢†^úíÇìG ué …f´”öĶñwXi÷xôS—$¶£e¸ÈˆuÔ{‡ ÆaäùϽß!]Ûm6&sá]½¾“ ÜODÓsÆ¿»IþÆÌD_»ˆ_€÷B3ºsPg>ïŠ; hø‚8¬°âQ¦Âœ¾0ZB•>[tXêFªÚ¥¹Ûœ¹–õèŽ,³ÄsÚNXè§Â»dŠARf  ßDS&¶ÂÏ'à×07l)±…•¶5<ÃëžjRÿøéÔæ cs§EÏ0Eî"¨S©—°@ÑŽ—nÑe½{lç™^«Øeg› nŽ1Ñ©¯“Al‡åÆ|äôéôûâ‰ô?Ïsçß™(LßE ü xóƵÔFgj½§aœ°Ïê/ÐǸø/g9‰Ð¿b9¨„÷2³ñëySz4Ö‡îÓŸÆSÕ¥ÉÒåõp¡ßÌbW£v].3qµ¥ë«õh¥v7Î6 #ê‰ÊHFnÅ|†Ú9GáÊþ`ožîÙ ieÐáXÇæM”ç£å»¡ý—;~=I¸SÚC8$‹u#¡ÿú*"VyYºƒZ²V¿eo‚À(-\þE•/ܰlCïãÐ÷þP;؃/žQçÏj°_ýÕuaÈî4—·võì•Ä%&vå=û·åÄc7h{HWìÇúÆÜ¬ì»SÄEšGàÈÛ_YÕm[0Ó^‚d U+ ”:' òÈ*Áºe&@–ì&šùþ&ïòs…‘\yíð:ÐÃÆ2%L+T¥lhÐ5|úr°óE #.êñ*ç`›¶™þu?ªÓ”À¾¿AÉ„û휃ó£.aᙿxñQÍ“Éßûçqò{1ü–@pÃ@¼ïü›ÙLSI&>B”SÊ™âZ_ÜR6Œç=¼ ,öªð’FkÖî<†Î‘ç¹ÙßPÖb2_þižÇS¤;«Áµè9–_«DŠÜƒ?v×µNF,y žílíìÓø$ªŠÌÁþyî`œ¹ £Tþ‘•O_`¶ÕŒ?y +ƒ…(n!Me+® “]óAb¥WßóQh’¶l7äð+_½Q1l(¤=ÝÎkùcš:[…‡¬ø‘Ôƒzv5ÚºàÜ»¡t¶¨ŸëüÔKnÃ}»yŸ/SqbÝ|t÷ßäãGÓ^ ¡}°$QžÛ/轎üió4,ß¼@XÖØ‰ë´K˜Ä¬Uø~ã7œVs—-U¡çÄ HB6á=×磞› Ðoó Þ]†ë¹›u™hT·VÝèbÖÅOÉ€DT=%À;Ühð|yòPø½BÏ £m-^tlðö{AyQ™M¶¶¡š¹8n|ìÂF]&ýÍþt•›!M¶Š®X¸.•ZÀ?qƒYÅÏ0Ñî~jÀ„ý»è?-ÚHÛ¡êœ"5 è Kና¬ñ3›šfJSe¸ÏJ—e÷²µÈsãDˆQ=Ϭ²Dв67E¬Àæëˆ?ý÷o£,(öøï×ã÷ÿ·¨´£4¤©QIóy‡”™ÈL”‘ȬMC{hÑP¡B¤Pèy'³„B‰ˆ2C~½¾¿ÛÇ»žuu=Ïuî×yŒûý\ë1„<*„4à “o"ó3bEGÓQr6ðü–2ôè¢éRòãL-ÙœeMo¶¨Ò‚’XX;Z–h&:=Û/Jç>OÌžwƒÂÁÝT_å9úâ‘~nƒ3\qBÏ 67Îæç¥ó…O0 ¹|6Øz5|™ *»÷;0@ó ¬´WãgÆâ±‚«§ {t3\ð¥ÙÅ¿¡ ó4Ù[Žîú lÔ“0:æB,¼Ñrãkôm¹CÒk¡ÿp ¸.³Bú±Rbi5rmc¥jè=¯LÅžáÏ¡2ÂÊIWqnöüS*‰?]€ð CyÙÇB%õ.g€¬ú \¹=œkœM/g…I6Oaë-}ò-2˜N\q4jPrÎp¾×ý=žÒµä…›A+ÍZžüCæ½Ne¨Lw'ñ§ 0Ê|>m öïM—ÙY‘ºÜ¿XpÍ/ T1$,SÚÓîÞ \Ïò#þò nÍÇœx=þûÀøúìg)Êùâ»í0JÜ΋ç—\‘y´|ƒ^ùxU'÷Ý …}”ˆöOƹŽGùäêX¦ù:Õ%ÐI!ÜöV˜úÊ ÔšjQméWX4¥_uM€¥«Eèg+Z-ê n]C­]Ðy¤6Ÿó¸ÿVÞ&UöÉD 2½õ̓‹ %gÕàçÖü ½ycIWO:ýY(q›ÌTýíèvÑvÌnXÅVNÏáÚ7Ð]Ëg¢Ù}7°öÍØ) >mûAÜ÷ &ê§aæ£(;û<K™Âõà5˜m¥Émã÷ñ޼3,­ê0Öú(ÂÑ7 ~ÿˆöŽúÎXôC‘EA°t´úúó‹g‚½%Ð]ï€9Uxà‹"›ä1™ãè£ýP”J«ß‚v·ðGÔB–Q¥Äæ9øÀÎÆ!tïeÞSëD5ïŒàêѱX÷ð;iSÕ¤ÑýnÜ¡ÛÞ‰Ë éë|pžåo6¯ì,HþT£_ÝcRí÷¾Û³ÈjN·Åב‹fUpìh žŒ @ý¶l<º3ü·ÀÛ?%I}!rŠ^xY{ß²„¶)Æ Ó·²u'áÛå•ðñ9f<Ùˆ-U°@—Aó=¼+]Ê3*1îÚN4ï ÄÃ)oX ŽE¤%’øw?™þ±}˜ÖrßÌÇm Â:͘gzŸ‰­€{"™X’iÊS“tHÃçë`umŒ’÷>.h<2ãžÛãÛ:0Û=¦¾“àySð\Ü9§‰=Òð8q2w^ÒNtïÉs‰Gð(ZMþC*&Úᓸ£<ê„zXðê&ŽÖKEϧã©‹rvC¨¦C,c˪¡*ñ$kŒ$au9k×%‚rÉ ¦îÉÎä~€ >`Ñ+¤W‘›=7°61‚(÷œ ³ü”¸¼Æ;ø¤IPm{>Ö•… 2WJók–±¨², O´À¿—£Áʪø•l&} Œ4D»aÔ¶ßdµ`iuèwùÿz+ÃÅ]P»N•vÖæCšûBz®3GNü=¦«îƒ†Ø*;õæÓ ÄåÕÂÝÕ¹ä’h˜ÔzÞzrê¦mÝsJÓ­¨FõôÛ–eVdc¸dË9yæÚha~äpÇMé Sa™j;KGÆà¦ÓìÌÙ(¨# w€ç¸ÇŽ÷‹¦‘6ÃÁ[/“•î †ºQQ§€¯ûeÁ÷†,Þ¼]Nä¡oÜaì“lt»V=ãÉ.šÄí;aú-Ì{ù|Tøs—ÈöÝ„M,ÀÚeÖÕcƒÂaìS(kÀHß½d³íd<¶!{V:5K`šp0”ï¬ÄËïU`GÉ Èšr s¶éÓÜqãOŒŠÝ ^¸P[$ô3HßîÑø»¹„«õ >2ƒj—à¦y-<Þâ¯xº•m#\µ¾%>ǹf\¼òy4Q”–/]Š?rK`s-&„Ùà‘ù¯PSb ýúì.mr„¿»Æ0áwÂgÁÙ‰jt¯àwöMáôÑýµ(dWuÁÞäpÀÐ1àËËöˆàž=ÒÂgSLawÑG0q•Ômjƒ1¢®Â”6t‡ZîsGç·ÉŠÅvüˆH&”mõw VýK^iu@WÉ+²2\™f¥}Àsú#ø+g,Nò#·O¾ïDç)˰BÎ kŸ-ƒÄ|üqÕû¬rè]ŸNp:‘…}ºÊô­ƒívÎŽæÂ{ƒxxØqZ<\‰ëvéÀ®ö㘠|+5uHÆG¸åÂmùÔ÷¼-¦ ]…Ucc¹@„üÓâøuÝn®–ÅÃF¸Ð…ùš\² OXʳ¾å ±Î>¸Ø„ýúwð™×”6ýÆŸÂØ×»‹ë(Øù¦S磥$­8Ï[ÅÎOÉ€®dêTø[ZªWÚŸ‡±°mõa8Ó#Í5ŽáfE5¨-s§"Z@lóÞ±5%¤qg'Û¨Ç,â×zEøý1»ðuÊ8ÐokÅ£Öo f›3ÎŽ\ð Ku3àYf4ôÚÖ€±ý4:}£_,=ãõ¢ê¨Õ08uŒeíÙ ÷.<…›G{ÈsثԟA–‡‚¯옫ÉV¦ûÀ{ЯšÁH³Æ}¦÷ݵõBIßsòÂ]£§–²å"|hõ,tÙáM¯Q|ø„H`Ú" êû>Ǻæ@@@ŒÞ¶«~fÒØnqzG!—ùØ%°©ã¸ÌOòww”ÐÐb1¿ê€gká5QØ|» ç4©Á–{a‘ç*æ9O½è%V;¤¡×.šÌî3S,ùw…5RèKmG˜)2œ¤¦éÀÓ33qQQ4æ÷ÚAš‡;·sÏ*CUߌ_Îôç?g‘RÌË4‡O‡?¼á¿ºè9—sÈõaéì¦Ì ÁxïïLZÚü3»®Ãƒ9D°ý úˆîcgs Ñû˼ؙ÷öÏ‚uZw»1S -.ÒÌg¿!oLȱ‚%x0ìqk ñÚ”éd02XŠž/*b/æ$àtAð…›‰ÕâŸÄA̽«³ÆúŽ«èSö‡hæá˜Ætf°ã£¯h(˜õÊñw¸»v‰^ó¯{,Ç!Ûñìty¾uõPPHŠÞí³Å+6×™ó$ Ö­0‘-Ôæ{IKØ7„)ž®Å–m+sà‘'=aÿHIx¸LƒÇöú‘Sä!cØGâ¦Üˆ§b”qµQ OQïc…†ó©ðx»È¿ —¼QÆs»ä`ä×)tOÓh> §4Cã¯QüíêF4R½ü›>O!rðÉo<ÌË\·¢¹Ù„vAÙ”^¼MxˆÂGøKVb„æ96îåë¼eaãœãèxP’»öŠ¥@þCY‘O¡¯ˆ´Û2xÉWÁn‡“ÿÕoÅ5Îø÷ä)ÏŸ‡ã'/Ó)¥øgÁvLîÈ@ï“ïCUñëyx]Bßé÷äA¼×AF0è´²9 VTÅ<-6]÷ŽbÇðõprìÜ2| ‹îHaÓ[/0¿‘eØr­ Bá0‚aÒÔZ¥ç=‰ñP÷.“-«ëBñiÆÜà“>fêíeUñ6ô3f ÿ§(aÜž1¨¦J;B¯µ"YÀ$ø¹xÞvF‘m $öÛÞ³?ÇŠ0AO¼|WØ‚ó~éA~Ò<ö}Ê 2êœ ñ4ÌW¦M…„Ú^6õápn}à(œUg7w_GMúb\?l)tâù?~ï:=L,Så7}ðÜ‹I¸zâex1s2-?žÇòŽ`àò» %鉯ÝuaWÁBª~Ú—|õKÃ骟qßTúü:‡'ŽË….ŸxÄ!qúAs2b{®üK:Ñäú"̶ÙähÏÙ¯‘¥u°¬hDÆVËðb”wÊGøIБëCÑGÃ9ï—â¥3ÄàPÊ:pI‚&f~$JG¬Yáªx=ݨkµ›d,eNŸ:U©{d3ôÑâv—ÑrB5>R_{FóŸ&º*(rŸÏû×ï–I kU‰ë¨}è¹ìžÞó£×M"A¾nüÆç>1ö&NÙ»¶X豪V¬‡[u‹1LÄ× Ùi—MAæ)Ô]øF¹·à\<±ÌˆFŒÌaZcà‰sàí‹ÕÕÒ" *n/¾N‡Ú±¤ å HÍQDÉÍ«a×?$'ŒÃê¹Lbæol –¦BjÔñž--±ÐÇÛ±yÏdzºEššª6ãòÆ|'ÙIÏaqîw¡cË](å‹@ú÷7ȸýà^Š0RÁí¿:jVÇ&ˆãG4tȃ÷MdàšL¨s÷œ‡/~‚EÜ}p~[8ËFÏMÜ kb­PÝ+žµ·²¸ÐÉ|“h0ö'žÁ¿óC‰æñT [ DmeµSË]œ¾öº`Ðà/dññXÁšg)¤Øñ ½í †­›ñ[‰î/^àô9û`‚ëW|£E‡nºÅüw›ª¯4hë¢od\æ\ü´&Î…èCI„°Òz‡pXƒÉÚ³œ˜ "ûØ ÖwÑø\ lÊßAfÕÄ¡wd=ÙúL[ÿÚðJë>–²×Œ_syVÖ¯¡(SšNQ¬>›‚• ”‰Ÿs®kPFÝœ n,%|D$Aíb+äÍÍÂu/Õ@‰fÁè²+—úÿ¤&Œxƒ}%“Ù§ø±},g¯¶ƒÆþïP0Æ>-H¤7y€È±Õ´v¾9&Šæ@Ñ2wü»¸ 4Õßã¯öl” a‹¿à¼Bü±ê$á“ ³Ç+â÷1¢Ð¼1Š(Ñ~–Œmcµù¹Hsxî.Ü2r‡@;Õ’›LÍ€Ÿ/97¨ŽæŸ>b:ryäbŒ¿{#»ƒ{Y y)Lúó›^b&»â©jÌ`ï£áâÈtP<¿8œ®ãñK¬©éÃßÈÛO›ëQ¸»©g¬ÎÁ$“Oð6i#{äfK§®žÍ1¿L8<)º..æû·ëЃ §èÂ­Ñ 7â—­¿²õgE¸Æìkøù)Ysx),øy†:«±¶qcHë¦]<"5wŸ„µ7àœZŒµÄxPñrˆ’É Ui*/K£‰b<îÖvtEçÞ60X|0ýk6„·×ß‚BßWì,?K1’ò¶´yT¬ÐNôrC’æ&¨:r’lX¿¶Œ¡5{ñ¶OpªÃU¢{×óo…[ÿ°Ñ« Ùëk¿€Ä™Ò¶:|KåÁí0uª U øêB˜qjSÇÿ‘em·a•"‚Ba)m)p1ÝAùa0 0 ¿j»£—¡æ©AÜoêwXŸl ½Û5ù¼¬õÌm± ÷XØ„E? Ëº#ì–÷PÒ<øŽ¹]¯KȘ´QPûÕˆ;Ë‚åÚøÒ~ˆ]ü=‰x=ÓæÍŸ,°ïáD 1 +:eÐöñ”µSFʼn”ô…GƒÒLõÌ{ü”>ŽÌZ‰O§|Cßsù°åPèÉhq_U C²— pûƒÌfóm¼­2m€¬Å ×Fó˜ÛÝ0êÍ~¶{_6¦œÚZÌkÌbXóânàÿ¯pæn9y¸…ÙzMůª‰Q§>Ÿöˆvò§»G¢·çü}ê$æøWá–eï!v›1;ìÈç®fÕC®áÁã¿pôÓøcß9vh´ ï^`"Ô™WŽZÑS>r|²×(>wݪ²+˜lùPHÓõ]ùë„|<¿T‚;5AŽÆ¶ñW° óŸ$ ùžÿõÈï}%¸Ø†h^2g=•’>a©‚¦_Pì¬ O¹,ÎÌV¤ÐÿúD›|ƒb¦ÏѾȎtß_}Ãî‰;·9ò tÛRu~æÂµÿúH3'³¸µ-EÒ^ãñ yhæqFµß¸ÿNäǑ阻—@ý2n—^Ä?_¢œ.çxWÿ9ø|-U‹µ4ø&Ã\Îôì2‰uŒ‘ ÿ‘4¿™÷”æ1Óo·ØVσØ6΋۟r¥«ÿªü×·­çi+&P»¢™plW Þ8t‹«{Ñ´R‘JiÎç–›/Ó-“ EÉà 9,ÑjÀá[ÜÑ-}*ÞUàw`_B¬L“àïíkðI‡1y§MuWCË×XZxd=zé#ì;ÇÿN‚îȯlÅÊŒ ÔZùÒŠáO¢Ès;x{\Í+ÞA_Þû–léªÂ.èN ÁNŒá]úÖ¸ü÷=\ô.-²‹æÉÔÊË„¯.D#žœ…ÿj½ò¼Áî´8ýjV¡Þiä—íyøýØœ¾ˆÚoç½ï¡¢ë%ú&—a¥D1¼t2¡VJ•äˆq¨_t þÊ´Ú” ßN¢¯â¨te¤,˜ÍÑ0Cv2]¡‰UÇÜÁ39Ž~8‹Gð)Rq쳂:½Ƥ†?á÷ÙŽ]ÆÐ2û0^Ø2¿]ýC~8Ëó—i-0«è °®ø4U~êBC/î„M•ØÒ_/ð—öþê-c$iB[n}"õÿοޗJÚÍbk"|,ù†žVbþSr1k>^g ¨ŒÏK–Ð[Çç‡A…ù1PZ?òÖAtwø|–›ó´C¡˜Ò×@d:­à×ÄÂÙånX½* Šb>A®æFT¯à' q¬[;+¶¡³Êæ±¹6GÖï[ðÅÁ98a£:Я•ø±ÄƒŽ®ÿ ½µ¸…¢ 8,z=Ž/¿µ„íÖãí yëC5ª =.X<¨¦¡ÆÔ e6É º5B¶õ+uÎ+ÿíc::1·ý½2!ÇG\Y‰{=?²Í=– åáÍUè cˆVZðJ=æî-b ‚w › >§ÆÊõÁ”ˆÃ¨¸›Ðñ«N ó‹>¤){°ÿAî8)AÜ£`ý™Yìׄ=0ùŠ9;Ðÿj§˜¦És𤽕®ÃFu_À˜f1ê`ÕNŽ‹(à¸ð4Øgµšn7<3Æ¢IŠôR‘uÃÏJ$=NÍ~gëÖ<Ç1ÎÓqÏ‚=lçL2/P–;% CÕñ5Lɵóû”ÈŸIPŸ}OO»¾–tØ®0,nãêåéõR.’IÜ;’èPûðvœÉKêb×gàÓ;Äk¯ úç¢hòæ¥7dœs¼Yñ¾y~Á‡{Ùµ" rªŠØ¾-ǧÎa¼àÞyò”m%‹?ÔÊœBö_ wóû:ÁÃÚUôRéZS¼4-§ÒŒÏÐkÙB6§uµVîC»žX|¿(M”\}èóW¢UoÈÂô—°àÅ.þNCœÙ–Ûó­8Ðëõ— .‡)ò:13ûäª-½?Ã÷vÜtr ÷«÷@¡9â௸á”OÔ§£^íÏ÷¿!ÅÄž¯9q®F8íj+.J)áo…‰t†‰ R¿ ¡÷Ù‘ç —ƒÿ€ÊüP®:-O¸„üÒ-ðz½”¼¥Ëßlæ*džñçÍqä %šÑ¤ËçÅ/§;ÿÄû5bµWauQ—¯Î ~<Çä1 ùNW}–ü®žH„(óùç†rZ÷ÙÞ˜žZ€'ÏÃÙ„xˆÌ£6 ~$8y;ì A8¦xeìQíb<ì ÃÝ“îbC‹x–ïØ–‡Öö‘}$Œ%;7 £Ý·5CÐÇ. ú¬áHK<|øõ·t§ÁÖÅR~ƒåˆ^Ööl ,šëE£*Þá Äjüòê·Sò“5Œûn˜)ÐŒù3Øt} ª1î){óInîQàöM'0æž<þéIoM1Ðhi^9:ŠŸñÅ­‰ºüdûTâ9ôj-—¯Mq𤹅gðØ;X–Ïþˆqr o£›¤o ¼ªï`‘ídØ|r%¯<йgaZ˜¼@dþ+ø~÷ÎûíÏ{òuøÁV–U ŠOEhÅɉhR¸ ,uÊÞ¯…a‚9¨e‘Aª“éK›A´)ȋڟÄÇoMÂ{mðZ’Ngk‚“Áü~ĬڮKú% dJˆzbé€17šÉVfae©"ìù ¯¤pÿì(rÜJ––Ÿ3ÏòïЫ&Á!Ä— ‹`’[¼`ù!™®5‹+©q¹ä^Øs?lKýx‹#{QÑàúY…^ÐЪflj ›gÌçWoãá{éçcûùm5z®À‹/ÇåÔ*Ø›öÕ:³9sÑ©"ôI×>x}p6Þx­È&y*\œb*˜[vƒ){ªÓ%>²+ËàéËsx™¨Î=Jª<†ñº× iª ßs˶bœ¤ o,£b3ËØà Øã:‰¯ŽáUäM×îSC³½r¼óŒž´~ rw1óÙ.¨©cÅ£˜<ÿò¤n­>:[Ìù©»Cé“ÌjÓ=†Ê;>ƒ®‘#¸/eNU»Ùä¦fü[’)øk!ÎN?‹›¯×B÷µ…øãÖÖ0ˆWN y–ÖV‚s¥Þ°È7`Nþ!*cz­y0ˆ†µµÂ\ÆßžWŸï`²>P㮀—OÞ€Ã[Âà›|!Ù³uÍŽUáWÊ‘Ág¤hÑÜ}ðð|3™¨¾‰k§·‘O+…¡¸l]Ä©C©R¡F}E#—ŒàÙ."¸÷HÿÙÈGûe∗Éðsµ!Ý×ûYа͒¿¯ ã ý·à ˜ÒçŽp©4‰šÑm÷ݘÿúJ§–þÅÀoÈóºaü¤ƒ$]?ô ù«EŠÇsøœu–¬õÈà>;ÓñÛÈ$µb"õT[EBK,©ÏN5hÔ5 '©Þå<ã|~ø*”vìaúgÎðÛ2Cp~Z|¾-ø¯Wtñ*Q®°Ü _U …¯{µéêÛÀuæÁƒä׌\á¹ôvÌ6½Ž/¿žG—šãеL‚–~ôÇWnwIe˜4”<ò~ƒÔ¨Z°ù¡ÎÕÂýi‹J 6Ɖ5>RKA&ºýïXƒ\† †¿ÒÂÒçÃàñ&$!)cÆÃ–n²ãïQ|盋 bðZØ_ytY¸Æì ´×aî½Õ´=iô­+Äá–(o½5šh„ßéëŠ4ò–ôÐïŸÈ•£ùq3˜Óý'¤õ𧓬yô¬vÓÔ›+øFGàë2=ª²43²lùÉar|ýù`hÞq MNаT¨Ãˆ~¤§b¿Å‚w’€ö¥®á&[[p•‚/f•âfvÑ»õûGÔ ÓµªP¶}>ýþü0Ì F;;¯¡Ò”øj«9=A:-î6ì<軣Ñ0{>¥`ßÙ0>™.æ^¤ø÷Õçø¾]4+ÙºgÏùÛaüÍòTœl;¿_†¨²†ei|ôœa4±Ü”ßn“¤•§4Ë3Hðô^H¹ò廙?‹<â;¦ÐÀc]BAÙ°>]ºÓ˜GÚ$Lû+¹e÷Í­`Å¡BºsŽ,=Ú7Ÿ[¥·Bòœ.f6˜/¹'ü µ :‡ì¡N®£TŸ&u¼àk:L¹ØcyþðöL~<ו4м `ƒ]€Gv„j_ËUÕ»_^Ø“ýkÄé—Àbþ8T½}Ö/þ ó®‹PÅ(t×}0âc3tö' ÑZF7›,À!?õÑ~“,Lˆ&×V÷ãsñÏð©r1™_ÊWçœÇiZMüÏVö'i%l5öæF¹itR¢ —xtPPÝr˜ú]qåËß­ãÞžÅ8ÞMÎŒ^„;žZòõÁI\ÅQ„·ín7ó èŒéü#Ó¤nšat”åf>A|7>RIÅGGÇóè‰s@$l&Ÿ=ë!ó•O¿Yø¼Ð„÷¤…AÊ©í´0:'»KP£ ]~ÇT3s:ñ"ʼnÍCèé…‰¬[Ù’×T µy"¼ÿï 4œ1”·:[Ó+âYÐײ}y F-¿—bJq‡ôÍøÖ8Í KpvÃèd“¨Í"´»ÂaÌhwTkÊÀxw¾¾ï'ØØ8ã©\X·(Ÿ[zÞgù}Cé¥üfyç0LFéMƒøÐiŠØ:$•ÚÌþþ[•á^€6ä«Îéi~æ{½00ü!šŽ}5“ÑûÚaªj,ÃÏè‘=1Ò°Æ€ñ È௳›Òè¹¹äÙT¾hz+kß4‹+\¤­×aö_GÚËÂ{ûaâQ´ßÕ„iV.À«­øâ*{XÒlÎ ųÈÿÿ³oí–ð!¦‚®Qœ&Æ_»F÷(kÁ÷QtÂ#ª2ˆë]ZBm†Ð=Jµ˜\mBUûyá–8–JÒê¼kØòa8µ9Í0En¯íÄâþ¬Ç­®ñ~Ë%txf€[~0Õt/œ"«>ÀGÊy÷¥Pþu{/<µžÌšö+ VÕŠ‚u¦-Ÿ]Ãêн#.ã¤üd~8ÞÇÜG§}¿Î§ÎÕ€×tÿÍ&6Í„ÆÏÓçÁ÷ô–nñÝEèÁYŸñÈ:àGšò¦ó y÷¬»‚cŽïplà’AĹmˆµ\ƒŽ×k‚n×-øø—G]èj¦@VØØ´B²LG š;ï“’Í ùû.[ð¦ÒEÕÝŸ‘†OQ¯ýè‰Ïj,wé,šSKÛ‘×™QLâî>h_c¿Ö#¬PÎCÛ{³Pg’'ï›ý\Fe¶ˆƒ™\¾þ;šns4áµu_qÌ”)0ZØ ÊF`«qò•Ž@h•>œÌy>¬A·'ö°¾m*ÎT ñÏÔè—Áo™ôÄV´µÙ‡7bxàÛ¡QXËžß‘¦&>‰à¹9ž™MØc¤s°vÝNöäñ€=%ƒõ¬Ùø·úœÐÕù5˜VÜÐl7áÿðÓº^Vqg þßÚÙZµ`¾9ë>;àÆ<|^³„ eP½´ÑždšSV] ùS΀¶â<¢¹‚øá+’„ðñ‘!X[,ƒýÛúÉýšË?˜1tЄœÍ hdÓ—‰ò_jC¨üâ;Xú¥#&a¨ßP¾ôš+>X!Y£S¡¡³šž!C±™~¨ž¹g;|ÚV‘²×þ«ëÄÕõŽ©`UË<üÀ‡Ïë¥!AÉ"a+ÊhžõiS@¹i±@é: ·!“¥øñ¡|áa]0¹l‹ÞmÏI_ª w±¸¡¼h;û‡ÞyÜýu‰¾,P¼£Âüˆäk°3À‹!ñ9Ú]Úz…á/kزýø@ù1œØ(ØáÛ•QŸÀ{ôtÔp…âú%ßÀúÐCÖwk"¯8¢9-äõéç¤G!„Óx–zãÚaÚkË޳UATÇ3b˜£þzñÿj¥COBʳ|´6£îÒS8xê¼$¹Ì|‡ò0Sž3Y“Ëo¸„¯¼Z°CK,…ÚÐÚ¹‚L‚Y0sŠ*EU}0Òœm6‚}jpJ­(÷ë^Žbj×IvŠ»_^ˆ#sþÙ?|ûÉ,a¸ùY¾‚¿òx‰ÇÎ<ÁrKMj?82»eØ­e7àÛ¼¸‹æ@σ1PiìA•Eærݧ{¹q”&x­Ó§…"«iÒ8*Ñ/ÇÍb¿ ‹"†Óò¡çÁ§ñ!\ó¬V”Í<;µaóïdŽc‡G¸aûè)à«d‡©®‡‰ßÍu\ÃCŽØ ^sb‘-½ wä¶c®k·0{´"ýp°Zû‚ˆÎ$EHŸöÙ>_C[­è×î‹°°z2õÐg'ÖYÀã|ì¹;kJI®61ÉF‹çªüŒÔK˜~ì.ìK“ÿÿ[ÿ”Ä8ÞQ2IP| ¬ÂS©×I‘$KíoGÏšü²úg¨ü0’\̘F¦ëëÆñ–Eh>VÖéêÔHê˃&%*­:‰ÊÏ‘gœÆ®Êt¶Çx"V¡¯SýÊ4O+–GíËsþôâ†6y~ ÉIè>sŸ·u3÷Ïld¥ mvñ¡… ãéµuÞ¬pô82:Ï„š¬›… ‰0èüYŸÇ~í-p’z Ñqݸ¸i$µP¹Õ|ÕZ>Èñ)¦Ù®A-œG­·¼Vˆ}݇?_áÚ¿Ò0Îî§pXg3Z•LC5 þø¿Õ‡²;e‹`è¡í ïoÁvŒ¦‹‹&ѧ«@*ø.˜…éð9?Vñ/דaIž ŠiƒÅÕ_áÑÛX”ð?KŸ®ÈM;§p¿éòlµ åòßyd¡2qÉÈBñƒ ?0—¾¬ƒGþÀ„¶Rè­/§‘¤©ãðÁôvI>žz¨ŒÏo,¢s‹‰¹GÝ#±.7 6óœp—öQj"7…ïz6”ÚšªQûÓ§Øàñç1Á}Ð#Išï­ ×vìÇj51^¥uŠžî"Šm5¯4nœ’ä´{þœ(ú¸z“ç$ê’ùtKFІh-ª÷ëû?ûÿä: ÖÌ–1­Ìú ×äš³í_LéƒYÑ4å­ I8±Šg¨ÂÊ½ÌæÝÔÿŽãS-BCõ_²›¨›NØåü4ûn`ÍŒ‹Ø»;CD¡wÈ7˜üAT»¿±ò)Dß!Â#5èé-¼´×u¯ ãÊUiâhƒûôÉØ“˜up(/ñ“¦«­^ž¾ Å¢ä|ÂjðoõD.ZœŸ¼ŽÏœ…¹‚HüK A¦{%½õh5Û0[‘ß8ûN÷‹b€6¿Øð Àœ¬Ô° ù†Üsø2Í‹ÁçÍÿì?dƒZ]¿Íž»¿Åç/ {ÅrrkR¬6v$â“fqÕË6ü†Ó8ÜuF†æ­9†zaƒéçÒ½´ü\ÖŒ `¿J‡ÓîÚÐ{ý†`×™XÜ–3Æjrñ¥cÀô÷ðïyÏT^%ósвí‰14ã9>O û)í¦|ÉÖĶV…Ó±»I¬Ì$0”Úƒáó‹`­ÜUìm7¢çnòãK”qº¯s÷fÉÐX*ÎÅ·KR­}BHú­Áèžlœ´s??­’LÆ×އvÁM6!ò+Ìè«Ç¡]#qù¼«Ø —Äêûoâ±=C@Rõù?ü99'?ÃŽÂÜ©z|Æ[r}År@Ú]„îÝ¿„©š›•Î[WúàÄß>0ûä'\A{±Ûá²ðþ:Ô›†.éCgÕ8³¨3§5b‡Q Ií%ä@Þ4º½õ"®|ÀJ‚\¾-ô ÇÃù9Ð=Ì€g¡¢ëÎÒßÁMÛñ|Ì xj©ËŽ–ǽ™c`­u4Ùr@ß.ÝÇ·=òÂÑ_Ÿ±3#wà'­³P72šu<"ãz¯±«o ¯.Ä÷Ýù>·òïèóÑë­([œÀþ¼ZÏ=·äŽŸ bêuVí¦÷ÏÿwÛUãNáX£§Âÿz9?h·¡¯,`¿fèð#4xÍ%ŽímùÙ2ôØû}ÄöÕO¶¹ª“½*Fu2“2Rø]N„~~&pÖ„S¯d¡ÀF‰¸*|®qN'>Ò“…#G$ÃÑoÁ ¶ù·Sˆáü–z¿:&£Ý€e+¾ WéñŸ9Ìña8ˆ÷žeÚõË…}Ó>Ók ±"Æ]Ü@Ý1Yð©³OHeƒÿ ¹ëŸ÷ðjP“5Ó¦ó·Ü"Zëò/ߟv[uƒÎ)„z0ø&GÖöJàs7?ÿî'™M‚¦1*ð«bÔÒ«[èÀ÷PïøeåС@ŸŽÌ79˜’±y·àsW:®Hš ͆¿‰ÉÒvd2ï¿ÞÏ‚ Áý9?°ÏßéïÀÓ1èùi+¶„%ÁÖ Ôqæ~ºyç˜ÿz@£Þ:Cíëg9ÇçB™núÏ® Sˆñ¢¿ØªÍûÉr™#eO ûõEÀóæ`X{3 /Yã©WºØ·y±¬~‡™¬TŒÔ1çwf°Ü&cô9çKM #á1´„)ŽìV#z» z6ýÅÍ‹L0æãX{< ž…,›ù€ šÈÿ«–gá»›ÿðãÑ£ì€e,™úæïPaÇG=íªzs$Ûaü9SÌ?*Ï[¶2ã*à¤6‰U ¤iRÏQ÷ìy¡>ŠÆÔ·Ï–ŒÁŸz,ÈY†><´ J¦¦€ùD),žùß9Ø~®×)yê>lOOÆï ^½{3|DèÐÅ„29/.ø=‚W$[ ÃÒ¤xoJùsÏ×)%c1fŽkb智ü¿:í¶%)h—{”¯‚ëN9¡z|K³É=°êJ<˜y¯Á>ðîá X­ùÕó3°{ùP*‚T`“»‘@|×}òø/ y€Ÿ·'±Škßq˜Ó`(ø* ϶_&kb+°odT“ò¿ï@×(§y‡ ®ÕY²ý‘{pÔõ³`~K«ºÏ¬Œž‹– ´±ñé(€ëv ÁLÿÕLf¹¿ÛdÃ&OÈ_šu;ø‘ŸðôOá.ó‡pr±UtuÇ5/C ÿÃ(aœÉ[ð©ø¢M‡@Ï+˘‘ ìøæÇ³ùÑ0ôJÒ£åÖbªåZ|TÉG”ƒ¤›I`]” 5@«Ø¼ë¿{J4w°##ùÇné߈6)ÃÀÌ<†?ùpÎ\<íOÕAö«:šv²)1‰8!ÌJWäa—BqÕ)þùY޽°”]w.§¿žä7,þ@<Š|Ža K\ÉkDŸq‰D"ÄŽJÆñ.yð,ðµ¹ 7Mð”ç¡÷˜9tænÀƒx†XÄ[³~«>2µèv/ˆÃ/µ3áðê(ïÒÈcñ£î7Ò3ïi˜ðö|# û£0l‡´.›ÏHRô½ø†ƒ¢GðÓQ¢‚™¯^Bà¸ßÂ+Åàžï)Ж;ƒãú þ᯿5®*ÏDôn"³B­Ø+%þû.OÈü§ºÐwÂëà6™Ç¼9s†M ²#ž€M±}–¯ˆªË6âqíË8¡Û–¿RÓ`õM¡üª¦”ïÌïp¹Sôˆ½ƒ!}'é œ`kA}/IcÝÊâê‹L„+}ð%ïnÞÆÍ÷Ã)ßõ v¯„ÓŸ§¦ÍFáƒBôÈ£x¾äÙê·”‘¯×`‹ôL9õKr°4ñ ›¸Î‚BÂnrö-6ëKR‹ÆÜ(E”žÞˆ {Ìé¬þÜ÷úKrgÙìt<Šc{-a½ûQ”ܱ Õ«ØÿáÖi _Ã_&C°¶TO†ï¸ ›?¢çÚ p­õÕÍ”|˜Ö†ƒLÈ’'G°¤+ŒÐô½þm!–ÕÅî¢Xh½à&¿S®G²Ñ3ÄÙÑðB¸?“‘Sokç&§Ë1mYLÚ&Åkô…dónøëðQ°mònb8„«o Å´ø¡´S:×\!›&NÄ —0Ü_Û ßêsÁº•å˜×ï+l7‹V¯¡tú¹p°% aMx IñoSÔùÔ±¯Ùçšò|uYØr[m>¹·øc”!/íÈ=â¿Ëó_þ˜‰#Í™ÞÏ_x©NŸŠÿûÂX@ã^å<¤œ@Cð­ÎÃ1oW£ÌþHt5¾æ§Èþ«»!ýþ+fò¡®¦˜À²Û—Ð~ÖV¾Ê×»5 }v.Ò‡»ðbhl*´¹?Æòw_Kɉ‰„Íšá ‰‰Þ$wÍ_á§›‹ñÌÂü×Fšƒ”§,%J<à«ÜòîÀu·Lú^0tZË8Ò'¨¨’&õRÃQáf4œò^Œ–²Ð ‰³Å±]– ´?tÝ’‘Ÿ[€nO 6a8ÖýXÌ*¼ A²r¬oòoÿ«ãËRð5>!ìÿ, ïéA»N¦`uD6‰?‚Ú—âØá¹õ,<ù/¾s“äúƒ3‰ßÙpXþöžH!G¾×ã½’ë0ÜÜšg ÛÀ£Ú§£ì H|¿¦jźØ8£#P7¼˜”ØyÓÓ*p·ƒ1–= ò‹tpòƒ$¡íþóD&W _{âÇ…ê8g¥ ”‘à{¯íĪ a8!òžY:䄤(ýr#”9ù8° “Xøó–w[ùnÜyâ »9}$·ß€ŽÎ1äq÷¬ñRãš{ö¡E~‚ ·y XÄý«² èï%úJµÿâ¿ú•¶îŠ\¸xw_ùН¶°ö2=Á÷J4å| ÄHäC׺‡°vX*†ïRÅü¤‡hü&‚Í¿[£|…°ûk1y( .©Q‡á±Xtg½§›AZV-àŠÛ¾ã5A={]ƒúUc!U%¢êÄ©§•=øàœ±š÷¤ùº~}ªr"ŒõºáØõðzwZïܧ¯Éò½_¿!!MwbQt˜•ÌB–Âõ†Ù4À£ *¨páÔ+po{~ìpÁpë:Xms4ÆÁKÿÂïéÊXüÔ™©¸€ÑXM^ø,¾ø‡ßú‰¦xí@“ØRrIi;oÚ14lpu§~øýø-5Ÿcvßþb¿j™9ØâóÆ£ØÊ›ÿ}‡i¤Üå·oÝdß:;`A˜Ý·`<Ï· ³í‚Å £¨2$‘Œ«/ £B‘ŸµŠ »"®‰´Ë˜sT™>ÉR!mô8Ê­™ˆ ªBùâ&)XÌX¬ÈÔ.3%¢éû±±¹$æ¦à”¶Ín$ﲟŽù  gw(Ó±©ËàêvóS Ž›†£Õwß½dÃ-î¬ÓZošÙ…¢£Œë‰ó7ß× ‡¡6Û–»~œý?þÓ¿kŒÀbÆPˆüô—Ý^× ¶Žÿv´zŸÈ«Œ»ÀD¸.›kuÖÐ’ÎF´Ë›€êF’üqà|¾Cy'ìܰ—Þ¨ ËF¿ê/Päß K¾ˆrß¡ò¼½¶u6$ #v¼MÌ·‚¡ª¾Ô¯H¦|ž‰:/ãG¿·äÞZW¦ÿ¶Wέ\sÎv\Ôýt—*¦ çý»Úáäf.–…ô~®6>„‘ïⓉ­ðÎ%Œ:ÖV[uU£F‡¼0ôfß½¦5¹Š÷¾†ào\=ɈŸWÚ7ŸƒÍJ7ÞyhÆ8Éüóÿܤ@x°ä nÆãÓ»àTY+VWÂçÓ· Ëîb úf‰“lo»¤Å5ì6Â<©42êX6¬ˆÈg£$N2µ¿‡ð‚­(×SX‡ƒW•Õ¾©P¡ë²Ëް³û¥øáÉÖè¦Ð ²K_Yü€ÖD­±çðd¬Ž {þ_½4¾Z`½¿Øå>} Jz—ËžCšv*^þPGnª;S—Ûw™ª¯$޹Šóßýa"z[…êÉF|ÜÜ™¸VU&ìÄÔØè_ãîo¸þªÂ£¾Z &äãŒûÏà¢Ô(äÕˆU5èìèd‘EGþÙÿêäi,öQÎÚIKâý¹ŒÌ*qÕ Þ «Ç¶+­L$؆ì²"ÉÌ}ÏH>Ã~(44¿…öÒM¡ÊtÄ~Üqî5ÙèóT°ZC‘oXø’UÌpâÝ>•l~Ä;¦“ßÚ¢q©v Æ/©@e)qš+Ф›ž¦¡Êu*ÿ.’ÌæQ5í: 4°ÂJùÍ2Ê%^µa[þq0­“àJ‹Ùˆ‚Ì@J¦”gÃeC8ùþ3dŸ«`/m3áÌ' Áó›Éöì/—£·÷ƹÊó²Ï˨WÐÍkZ#¹úþëiíò¢WüZúÏþgŒ6"o+— -{º­s-²g»V\gœ“ÏvRyÿ™°êÁ.kr ÿ«k>Ø2ÏrKt?-ʳžœbûFÂÃù쨣+1¸Õ ³*GÑ÷´f?âßølz¹‹…,;…uÁló:g¤Ÿ9ßêŸ^ÛŸ~NŠ$§exñòpº³õ,}4ŠíµüDŽ™{ðOMbÜ)9˜Ÿ ÞÇß%°÷E”ýîåAr·Aiã6ÁyO:¤[p„æÑ)Ê„æšìÂÓ7þ’ŠëK¸újihj–à?ºJyjž4ž-ipzõò~Í×K1ßN_Ä"u*—ò_à?ÝšŸt³Ç?ư¶ælõ—Q=à¬~6IJr¿Žlé30Þ;…nk4ãG×(á²…õõWáÁ”,뺛Z¾JU„|™mCKVÙÑ•¯bpÌÈèk;Šº®ÊÅU,JúÌÐso4T톌­&x}ìQhÿ]€{Û„§½@rž'1×[‹“£‡qù››ùU7C(«Ô¬‡yÌõUä>šÇíû˜îhe¢º1 6î3bëÓ͈òHœ?W™|òÅÛ†ÒÊì©l¡ŽÙK¼*GWkóÌï‘ÿð+zƒ™¸,¿sŠnûF6ì>ŠkÓß\êÛoÄ÷°>•w$ÒF’Ê–_šÌiä=œa~À ¸\PF¹†ðG.ÂÑv¨´2¥v†£ø+éó8ÞQŸ+ľy:0dƒ==¼ÿ ~YjÊóíÊØÀ1¾¤`äù¢OO5€’ØLš¡™Aù¶\7³ì>jÒ°ÔFad%›ÔÅžÖý¿æoA”ºLÝõÇ~Cùçþnz„­¹¤.*G@5$hâmÜ­ò˜\‰\_òZšRÉÂ'Ì8ç#nÏê„\äû¯X¿kØ?þWÙ–:c¿›…èðø4‰·(æOÔ²±«NZd;ئÂQüpƒÿÕt©»Ž¢»#xµ­4=Ó¾™_¶¾—ŸŠÂ’£â H¨Ä™_꾪‹%HÿÅB¯(÷N7&[ÃXX3a+59ÊÿëË,žºϪÃÉl-UÇ~Tæ¹ë´è†nW:Õz,=SœŠvmª<øÃö¢O”ž½l@Ÿ–AþÔÌ^ ¯çâ117œu6Tõcy—2ÏÈ~‚»àLL4go/cŽ ?o®„Ûími{ðxzQQŒÿXÈò?ŽâÚÉKÿéŸp-1~f—Û¼þ½¾Š$MUS Èc&ýe"ÄmØEuC4ùu“³XÀ"Nš Æ_ÜûÓ•»î̆#=–òX“î7&Î3¥+/Lå¾Á+xšx8m\0Š?0DmÝSðÍ£•¨Êãp°ÂL(¯ŒâJkkQ  Ãcósø*º¼î&Ìs”¹Ž(aulyÔ—LúýíJ¬ó»Ÿ£åpuÙ~015ƒ³n㬢, õ—Øß û¤ÅJúy‘uKÞÿÎA)óÉu5ºjüUrfÖu4øCÒwcAL^|ùÙ¿hEµ°ÿíÿähfëdï3¹+5P™¾Æiê[øùyúYø 7féÞ7„ÂìN ÏZŠofÇOo’á)«Ð}…ÝaóO),'yÇrp·¿!·hj"šEcX³û!^TÇ\±.xÞœ yQ+1(Oû(œ†ý6ÜœçÃ#4Þ`Ù t8ÀÔ0hGq·(e—¤NžìÐÜÙý‚»õ ìΰ2õŸÊ´<.A¤ÓìÜÐFªeõÀaÝ%ø=X™&4ºAÃì0¸pàôGº™ºÂnöÂH73¼”E¶_2áz4˜µ.Œ‡%T-÷vàgÿ¿0„JþËÿ‡Vž†sÞ𪜗š³1âÚìêø?Äur Ë³Ê O eèsc°÷ò'X³µÔ/€Ê&¡`Zj½ðÍ)ì÷d¾>uq§´ìöJú h>½gŠrÇ»=#Ÿ›ÂŒIÜ`ηs‚y »!ë¦.t)ãôjbºrÄ»ÃCOÂoKCŠŠ_Ô+Ê¿”W ÝSžùÿ§¨0·p(¼ô2¥³¬¿à®€7P-\ ·–+ñœ¼G쉼¨°æ¯í‡bÚ:Ù~¨ü…°úl¸Õ¨Å#Ö~aûójPÜHHžïá¸:Î µŒ±ùäA¬UÍ„³OÐÿû) ´,ÀݯyéëŽz){3©rR_㨙âÔoÝBd5IgËjŒXóNë¬ã! ˜×Ù%AõR)úì†(%0À‰=!ñ{(é13Ã`±¨Ø XN¡GIßëlÆâèßð†I¼gwžhðÍô˜æ OlÕ­^¨0Ÿ [š-1jÞ¿ü7uÚq&½nÒýëq÷)ø:cwpá1î*cG7eÂìX´Z}Q•ZÇL‡óadiž|³VÝ}ù_-2Ñswàß\˜…f 0ç8iúr(pZ 륬øÒµ&tÙ¯¥DÌv¿b  ˜§\œÐÒô4¿¥7#Ž¡hPØÀ8¡ÂÊ.#²ÿÈS¨‘‚Ð<•z\}ñ¿R¹öþMµoï…kÞƒøAë‹x5V f¬{Iú—ptÊ´¹±k T¸ÉbÇ,¦(=G’÷è¶RÆ‘uÐrÎ ¶ ~…¯ ú·þŽ{•¹×î&Ä…§z߯Ct}P*yø'”Ï(É‚žôP´ÞVp`¦"¦=fÑs¹ÆNWêr@·õçRÃy6‚þãc`ô‰©t΂äÕóe¼¶q,òS`C®+ÖŠ‡*Ë»>òîž|â¾cáhê/|ÓÙód’?­ ¾¶ è«#» ùST;RùÚ¸°f5»1…¾ Ðë„3þ×ÏùVd<.¡õ®i¸áíHªËó°ð‡ \}Ä•±ÓY ê´o3&&OWÌI mô7ÞJVæ’ ó™uú!\¿ïñÒÔúÿ*¤ôpií0f­Ÿ o7âhÓ ðÛÏ&dÂ-)pû.„+«ðW‘혟s GϹŒžåÁ¸öè'ŸM ûמ‹Þ‡gL[ ?y®hfúC]¸Ed1rE]ŒæáävsÜeZ{; 8ÆR‘·3èA9Tzw.99Ý…çŒÕ§9ÒïÐx«—_eC»hÒg#®ÓlËOŒ5ärß©}3‚à©<.×™Hµ’·ñEªNœþRÁQëbÀ±Ý„~;Ç~˜Ò'N9drãÔíY(”SB£¦_†¶Ë #¾ Ó"ë±ÿn2 où—ÿö•³Y×ÞÙ»Š{ÐÃN[P-ä0}× ßžå×Sn³Ñ'd¸vÙHBÇGŸ¼›Ú~ÕånËZ±E±äÊ^czÄp2cYPó‰<ú£šjô÷úa H‹$gó…øÂô>ÉëócϾèƒÀN_íšMG- χàeëLtÛ[9×)YsL—÷k…cï¢[x|ù1(Xwç_üoªwû¯ÿ2[Üq‰Ì<ŽÅÕ%ìÛM¼œ,Â6f,nuoñÍÍZ‚oÊŒ)¯|„ï-FðíÙžW§Öd’¸ *ñ.&Þd±Þj0ÉÈQª×PåÓkè{6† ³‚ ü™4•Ý´Öì.Æ‘×å©òÇdÁã9 7>ݶï…!¦iД¤oõN }þäaÎï…°[ÝŒZ/Ž%wÂ> þqZ>*,Wƒ×›‘öJ18²•ÀW•\èü4ˆËÔ„Eï È+ëí-¸GõYs3¦ÌÝ(Üaô¿úÏ¢ñ$|îKÜf;\ÇJsi'W2¸cGiÞåMÍp§‹{`…ÂJ:¹\ƒË|œéP~,‹j˜m°)½ðÉ‘¿ñZÀß}.`þ¶ËzÎá¡•éÙI|Ý…xÝø ²üü©\ÚK³|~ƒ¥{~:Y3…©GcÔÐ|ÞuL—,ÿ'¶)°Çd½“È6¯„²™÷>_Œ®÷*£6ežÔÉ"ŠÄÌ£{6»À79'æñÑŸfMR§Mq¶xý~êÿ¶¤M+ÆÒ~µ üö‚}dÉO¨P'щFôàÛÿøŸ®r fe¸-z¼`µ"ª\Æ5Fï‰à¿4ky~i%æÖ&ç­HhðqzÏCÑU0ƳÞxÄC×DøÉ³P!-™È (hÕð›.›è¼Õn\rÿy(`iðb6õEk¥ ÿáM€Qyܰÿ)fÝ­Îl¡;¶ Å{¯j@çÆmLŸp—˜å@ãg{˜áœHn-}#Ô[¬Næ…\Çç‘ @Ó §zQ‘Ü™q5aª±]“ð r/¶‹?Ç‚k,Óë0]Tg °÷\É”ïHÒå5ø×z4o8…S&œ&‹JªÂ“(ÆM‡YQGqZ²3Qڲ˅la'ÖÿÁW^ÁåI°äQt&)Ò;3Âi“6Yzd½Ö£þ‚0Rp9‡-ìOÀ cÍ/•„~Û9  —æDw.ñÂ9gVB§¶¿œ©Ç~Çã+ø_ýëò'˜ûR}O‚âšÐ]Ž+´«°'åZ&„ÀüôC°ñt‰°¸é„JIñ½ß¤µÐîê¼ê¦©ZÌ9+‚ ˜«ßP̶ÔçÛl0_dFw]ËÆ(C ~úù‘ˆ³‡:ý7ÏŽ(réö, ¸…r†7àÒèb'ëü¬0øƒ!ý8üC” \Oxíq#÷€×ÁB A(6·V2ïŒÉ{fø5ŒÞì“fçï„cSå6\9+’9æ›t®“ 5›ÿ­UÛ/¼äÍT¬Æ<#c.ެă’:¡P>‘™4‹3'Õ¬KÎ1¿PºñÜ%¸æÀ3?½`7ÄWåÁ¯k/¡gþ ª-“€ƒæ'“ Žá¹öUäYd¸ÃÎY3±¥cæ®ØG÷mЇRsL ì×?ÀmÙ{Nól˜¯ÌîÈÂ¥Ó8¼å.Þ(?C%Ü´hþîøúí,z.ø$jW†ÊaõØ*©>‹]雽óÙ\ÅÙpb¢Púš¨@ALŽêÆ h#‹¬º!ÅOhË gðˆøçˆUëN¸N®pÖù)ò M%0ïJü¿ç?-»7˜©ãARú$%hÐ+ŠàzV ÝÅRIöìÎyxvXÇœ1Ò#³á¹ü •ˆ5U =Ü“Ön™H§µÆÂÈ0=üÜøˆ4¶aùèçp¾BœŠ~·Mw~øi8 ‹¹ 뼨õ“(á3].ךÀ4ºóþ“Ŭü! _—;4÷’qW©`½& º+Egò­$Ò*ñ6\V¨ÓÕ_‰¡j|uèl¤GE¯©¯Ü…4B°žûúgšÑœò&òZ%’;ô18|¡+öí¢{ä…,ëPWµÛ§Å\=AçÿK2hÁ÷gK±Áì3°UÄcÎN¼oÜF^±ë$Tèãñð<âpµhg#9ð, Ûýtèë±Cé©Ëèç¢ÍË—â"O–ðq=ëñÌnqz£¬×^RäN#cH\™ ßžñKS5x¿ø`âÙ¸w~¬`{jjÙ%÷!4›”C­ð€Ó“ 20/p$«ÕA³Ôl>Û?j€ o†Uç)î–Ú«c©¿ÆFn?ê8½¯š€ãŒ‚`u®)¾MÞ®}—8¹Jœžz¯µâg=Ó?‡FÓ±ËçÁsqܵօëF‰Òìè8رì–ôVüïùÇã)6è”:àß]7pö!qŒËŽÕ'ãÉÖ¼`Ì•±.–¹‡IàûŠ$4˸¡÷¢A+zê/ÅO‹OàÏËy,¨@œ›“ÛÂ5žÔ/ ”XN:/ͬh”ï&ØÚØAl†ƒš£f&Ý"ÏNaÓÔPðê=‹5WSá¦Áá¹µgñ[Ì_ÐŽ‰efu¼eq7¼9p J^°—ñ)s F;ŠÀ>ïÁ8öážP-V`~à)ÈY ÀD¨†Š °is.*7Iìƒ0ݺWÎ(ñqŸÙô‰18æÀlêX-‡‡ì·9 ÿðËœ›Ïî%ë[quÔz¢íý“1ÈØ‰­xm†£g(’2 ü–+Âao!ƒµ¸ãÔBÈé1Ã4Á}v|̾`æ òáz!ÆÖÑæv+>"}壓è¯iÔ¹p)}›—ÆE_‚ï½£8i… ½ÕÏËw£ÍƒIpvE!”ïèƒ:©ƒùÁŽn•ƒá–®\j~,ÊŠÎ&K\¹æy:ON‘ꮼ‚×ù8Ó…ž¾wâ^n‡Y7K°¿p<Ýôæ:‰ËÃ|EE]FGuƒÊqÈR_NL²÷PÁóüsÎ~Øð—M­ÿðÛÖ§ñn#'ôȃ¹ê_ðׂ‘TËßú¿V™ÐEž ¬V!\¨ä]g‹+PtC.Ekqh™…ÃG…ÀÑñKø€}8ç !}z°ã6ÅA±v8ìm{Å:¾áŽMZ`ߊIF.|BÀêùz1ÿ¬#Z}‰pEÛ‚Þÿ ÏÝ\¤Q¸vŸpÚ¤d.y¦ôŠÒwÊ«±‹Öâ—qiÜJäÛp„\wº7€9 ì†eàЊОkÈóƒàöòÑ0¶ÎŒ—Úž„¾ òüžK?ª}ÁJ"ŽœGï€/³î³ý‡_g_(¤FKÓG‹–á¡°=ìWy^ñ(ÆOßB.ë»ãâ],Ï.QUµã(¯*Í×§’‰%CQå÷<:÷s…'¢ðé›(i’Ëõv¹ j$ôx_ÁKRým8šžµÃ×[ Yå% 1šÎ <£ñw¥/ß!ñ;W­Æß`óÔC¬¾D‡_¨k¬ñ+CË«‡ qêò†hr °$Ñúƒá¤É_€©ÕÞ—õžáx©]ø19‹ÉÍ9€Ÿ#JqæÙ,VYûœ>â­\[n(;¢‡ÑÑ3 šáÉÜTð‹® ù/ÿ‹6¼dÞ£·°›J¦x«ot:Ù³ƒó¡?·MöÏŒÏ!&ºØuF‰6 ËcqšÀæ¬ô/Ù a"ÁwŒ ¾ŒZ&Ü„ÆÜŸˆC¨„à†§&µMoÃwgGRá»÷Äè‚?áÿ‚ý•Ò¤Ÿ$΀¹;á¹®ïpk~‡r¼è?S­äÛ¯úâhµí°3; ß®n LÑÆìm<Ìí<‰Eešð]·O8LãÖg‰ò"a?-©ÄžÃø?+BÍän‘è¢&Ä6ÖãÙÙ‘¤à{Þx‘ŠžÞÇ©?n8%&«Ö¡ò’s 2ñä„ °®ß·x„ÃÊcÒô|ó8d{už[ñ žÙ¨kجᑎ Äg«SðÛ⧨WhÇŸBèŠT(Ì8‡Uªs™«^=ÛM6`U\:D/Ð&×µŸ±?kæ âzÔøÕJ„UÞq0ÄNýðclêîxxµ¤kªâ—•)°;ë*nYÔ.Ç;ý‹¶GÙ«}¹ÑÇ+`|À§áÿ½wBÿý‘»T–@ÆZi\½zHr•­“±«ÕS(js‡~‘¢7“ß¾bjÖ1Úÿ”¼¬ØLÖ|Z.Hq›Æ®Ï¸ +» ñ‰– Q±e^? Ý{SÉìY“qWš€æÝ¸ sß7@ŒÄQì¬Ý.ctùï9f–)kÎKãªÞ@†‘=Ñ´8«—äaqQËååß ­Äšî:-»–Àï§gàR-¬ÌMÂauRè¶z/9²-‰5UFéá pÚj:p©…ªú ¨= £OºÒROÿ­¿«b<LHgƒ[VáÁÞD4=A”¦A–m$”oRÄ«OCˆÙSln‡Ù›”躊GX]÷ÔIb™ߺAšOŽób›úƒ’§ä¾KÖáÞ2[x˜¿ŸDÝ)ƒåòyŒ7G‘a)waTî~ô¸?·ÿ€-L¡b™=èŒQæ;“ñ g°AùÌÙxi‚8jÉr‘g˜úª© ;Ý–€È$ôn ÿ¡J¯uC³ïqJP.zéÁ›#±Ÿz!z çA¥°4a¯íE÷…Îôè¹:Hj,Á)/ÞÃ|Ã4ÿ|‚%ï9÷¿²ˆ&œÚAbÔfð1¯tè‡öÌ@c6´îÝ‚ïê'Ó‡ûýx¸\;&¨ŠÒ»wâ‰Ò»Lœ¾â¸“½´>~É¡[­ù ÿϨW쎵©–´Ðh-w“¢Î& ·¤†×Ùz+Iú˰uš²!êK»ÀMºj#ÑmÓ"Ü-ö·]jû.«è«Æo‚qypïç"~q¦#æÜc{ËÇ Š ¦Ã5éA³säЂs¤_õó^`JWõAtrFôŒƒíkNãë•f\hNõºÃ!q¢hõ•g+hÙ‹H~­¢u|Ù(åJ3ßk/ÅýÃDð×úJÞ$>ç_3ïýhbiGÍJEéñùcQKd-k5œÐn¼}oÏZ›!àC,N“½Cbw§“×,¤úÔy·4ú¬8Ïg Zû,é&9Øÿ¬wºzi×Zqn]†v±°UAšLêH¥á0^‚gѤp)+èšÃĽ‘5Ç'㲋<Ãð±ÊpFO½@±!“A¬'öåßÀ‰B„ÿ‡‡…ãäñgÀE°3æóNªÀþv¦ë¶f¡ó =æíÓk ZÉê3¿È‘&'8=?’v|Æ[r!<Àù'(®Ê‚(uho^ ~m ³÷¾;^ ïæŽ¢±ÃlP1J’ÎÒÚj‰aý0œ”ódQÌUœ§IÈr’6}'d}HjWŽáåª^3QqO®›pÆ=5DÕ—õl‚A)Íi„Ô\ó3•Öøˆý&Ͼ[cL =nÿF®?u:”îªS§+‡k¡æ¼ø)ê]¥º6úÀœs¡PêÝËú¾Nþ·ÿUòM‹´Ì¯"â!8׿#þ*^È®Edz™S_â®'‰øMê¶nu›¦Ý0æ Ÿ™Í†,¯Ç~n`ñà~ü4;÷YŠò½AèßdIm•ÞÂG¤°Îæ>ó…îš²R4”Ý.UEÙ;\äÓbØ}K™+]}m“…c+MQÿˆÞúªH›ËZɪ‰¿`åŒzTÌzÏŽiÜÁɾ·˜Òï·B‘/•×.ák:3”D¨‹X"~¹Gû,Lðšýgÿye>ðgæ{¶ßñ¦(=Ç1ʼwí}Gó³é ô‘ç¿gF£á˜EÐÓ&[ýÜqØeÔœªÁíÒ¦QsÐ÷wŒ@ñÞÌT;²ofó/g¾’M+·€á¹)ðHv#.Ñ Ÿ•ßà-ãèÛØ¢W‚wŒ7Õ½qà´r<êÝ9Â$2-xËW×MU\õ‹ºˆ˜„ ÝC?ÍrB…}]¬¤u ,ñ†«.²õõ<:ìr!®ÑYG9lÃý¿‚ßìP²-"’>Ïâ0º>.¾¹Œ.{ɺÝ3Áãï#Áž­ëøÍ/r|nŠô?ýb ø ÛE”L³¡Ï­p¨H³<3Epäù<ùsn>”`Wç­Ç{ón£è®Xy؃·cˆò¥0½Õ69űEj¸ÕÑŽ>¿~…Ý”¿n(ÇÊÿ2òÕ@†ä—±"ÃM¦|xŠ«T¹ÉŒ¾BnâaÁ“Æ$òð”*lÚW'\&öX Ò+Ç㾘¢Í¸vÁþ áôXîQrù§ \¨4 golE-«Sè&^/¬Œo‚ȺÓÛúëþ'ëÂk>4]^´c¯&ľÀnu ›¿îdÖu¡L²-Œ]ÔÒ¥Á+„uäö4.rjIT]8-..Ç$ù9o}’ý«‹¬UJN Ø\at)ê,=õ‚×ø³¬šÙyÊñ_¶btÂìS8'Kœý¹»”—œÇ£|ìÅÊ»D•Ü„ƒìÖ;UcèWCLÜð JÂÂÙŸ…>þf1ÕIuZ2ú~=º4Dó’i”‹{Ãf#Zô›á%EQVj ÁIšjPÃn?U§³¼z`ãÂ{`{% –‰ñÛÏ8I賩éøûÿåÿ¦ Ïr;4hT3Ìj$¸kïPþØã2õ62£õ’; ¡-x|lëgEZKA!}Êœ>söE.SOÓšàÚÔò.á®§$ydž‹ðK< U¦ÈAsmn½§ —I(Ö]ºˆ2ÙKÐñŽ"¹ûø8&/ËÁ¡æTÛ{¡Üøñððý˜êì‚iýݰx§t4ޤŽÚ5ä:‰ !#geCHçèUÐW•ן§Cd“¹ƒ¯:ø Ò+òHÜ*Y~vV8«NRâåyÑÖ+è› b옓QÝÚñ_ã£2Ÿyó<;jµ…>µÝÀb=±çCRš#ç–ÚuŸq˜íÚèÈÆït¥m)½8¨l<Š¿µBóv_²|P*|=ô:⎱ˆa _AåNÜEñ2ÄLu_Ð`I£nÃÑÇï„Å<¿+ÊÑ5ö£¨ç³¿$à]鸽K‚Ÿ~úñy4Õ~gÉv×섹5ø^ŸÌé¡'K´hý=+jQÒQ-aº†Ö»M£óbIê ª§5•ÖXQ›sgpÈÃËä€å}!¼Ð)CÙ²wÔÿŠ?Ÿ&bÁÇ\á–6ü{ÿ¯óÎÒæpvŸ ÁÖ!…®íx¼Ï›[¯ÿª@Þ%r‚Û\oÄñºBñ°âÑ@­¯€—ë“é~ ôÓŸ8pO”&©ƒ¦ ¾úåèñü§'ùÂÎ_MN ã5qÏÝ,¸“‰BM?î,ŒÅرC¹þ®dœ/ý‡íȩ̂Œ¡9ÕÐñèM´ýÊKýjQÜó%¸•4AìÛ:0ñ^œ4FÏï…¿ô‡—Æ âôÍ\RKɃ ²Tð‰ÁþS‰lËŠe´Bä#3 ãhl¼ä]g‘1w¡õzTMêeºY86ò?¥·þÙFî1LØòžµE‹«V|Ìþ|õý_¤ÊbÚ…'àö²©ø~Ëp:=ú Üåˆ56Cùذ!ÜnË/.é…&ôŒ³W´¥ ÓØ¯YŸHRÿEðõIâËV.İqGùªXE~l„3=Ñ0ƒ#)}qm˜¯ÐáÓì†^^K6ÀЄÃd£²;7•Óãw¥. ®Úì¡æGB1QvpMʲxúÆ)ˆöüÄ~ÌÄ­b&ÑçÕÑñÌFîRH… 3¹¸ØUPØ.ìYM?¾6†‚²Ý(œ·’Æ0¥+&¼Ç4£z~c0¼8*Bk£þåÿ à­‘cqØæýà?æ xyÚqÙž 苸ö¦x\–‡@×LçÆÓös«öat¦œ”9W3Á)O€On„ƒ÷÷0Œž»Š&hŠÓ mJìýq®æœ‹s¾¦ Îdã·s˜l>”†¬ÃúÆøxå¢p´akÛá -V¬³,ߎ«'®B—[^=ghoÅÆƒcø¶Óþ䑤9wóšËÃË:àô©¸©ÈnœœÍ×ÞàóšÐ&¬i“Êlxm¤D¯°qäÕÖµØ~¯Œ¼n>μÞŸ!Älêu§¬Óþ?¯K“?¶nE–¢š¢+ý,0¾´³ü4¶Ü¶Ž„p"†²„{¥£„vy§¯N×lÉêü„ý_ö¡ëŸ öÿÜ?5 ø.ÝRåÁŸyçÓó]éðAd)/|nÏ»ÃÒÈÕxþ«ª¥Ï·×¦)B<Ù4š¾õ“äÒ€Ê;5~/É€kÆñéš\íf+ŽèW‡K»_’¦QÚ‰C°ëÚ î¨9•Ý•óJìÓ·—Ø¥˜bùÃ0Y{%öÎ*XFjh¢Çßépèþ'ô”éÄÇoö£ô;7Øç¹Švyðrïþ¾ŸÆ'mæà³Y›¨Eìãw ’`“¸]¨Ôõs@B4&DšÒ†ChÓ¾xP0Ý¢Týyi° íèÖpišZ±›Í¥Ÿ_΂%ûÂ@¼Å܃nÁµ®9PzU‹îøát¶j$Üí«BùóOáÚ=yú 7ýþ7"Öp«Â‡Íˆħ â¡+èv)-D¥] ð yÌ1 å™5˜;±Ž{W`Ãkq*žøƒ s€è<]LûF1›‰¦œ—„a{m©Ü…4|}G‰î»°ÎU§ÛÛäàÆe3âú$&ÎÝÍî«e³Ä-§¡ø­^hÔeþÐ|´BbXÑ¥LáO6„í‡Þ¨G˜¤O›æ¯€{ú¯¡bîHºnì –íBqñÕSÈ;âÐÖÕUPòçfÛ×Bù‚8Ì0>f¿ÓAÛq!Þ HB3묫$mFa§äGð #'^JÓ~ñPtº·ëHðÄr ±·I¥âŸ—­N/Uó¡{}X-’á¯^,ànÝßaÃ6+xRTTò5n8œ…+3{ʘµý:[Kg1›¿ßðò ,/B-¿yáå‚tZթ‡Þ7ÜÖ{¿7YÿÓ¿7Ï} EE‡Ù«¨ç Û-E=vyÐÓ/³ ¿$Žï~?8’ý6áù/RLÄø’©è}•É'êa{|Ðájô\ÂOO– çÅ}Èß ?“Ž.Å^xuq5Ì Ú„å»~Áž© xX©kNtqŸ2æ-Vày'ª[úTè¨g?Àª>„ŸÚè µRüÂ+BÇÝ¿­‚+'â²ÊlîzTƒ·=˜MÅKe±àÑp)°Ä¼øEÄNí$rý ´ô—9Æ4¬=€nK\IïŸÅP܉É2°×<“.°ù·þÃÜ‚š ð»”2”×qLÔÃKö„|z> ‡]àΊT~(p$oÕ߀7˰Hd$ûï¸ ì†ÞØA\&pøå¢&s1‰…íþ­8Q> 2ÓiÍðcäcÅn‘‹3ÆŒ ¯ÞÌ#s­º‰=wDM|Èl>)˜3ùÕÀ<ÿ‡ÿ¯ùG¶gãWVe‚¶µzpì— Îj±£ÃÆO— o\{ô¬Z_‰ß?_Ç–Áäîü«¸h€W/í,'³Æ¶à %ø]¡„Q“ÑIU$ó-èãã¢ðñ©.µ»ãB~ÜPç+ëTècÙ%àš6šžñ^„w.ìDÓÇ f4Ž€¥nã}Í^aÅru¸øH ˆ3c.oçcZµÁ€îy‰ï͇pK€¾=vpǦ]‚æ³Aþr´Øïb?ÓÊ¡fáöt[<‰)ñ‚‚¹Ú¦® ¶áÑðî]'ªž=Ñ{X^m6ÅÝðì÷ÿì_¾w x§.Åq»ýI„†?ç6AªD†])}DVˆ¥ &&֠߸éL3â9Šzö6i ‹¨íÉPb¾ÕÆêQÌ}¾‹þÌ:±óîàôÞf”ùËiç©æV§ÀMKñ#• ±ë :¶˜¼ñ¾­xú³(ïÅP_œ íª’üäuž-j‚5S¤Cf*Y-GzÆ`7T <ÞæDÜݰ×H ¤ÿȱ\x÷~˜²IYב·˜aG{Ž6J!›”Ïcšô8qý `RŒeßO ÔŽÆv±±TGï0Þ\õºúq»#¾¾/]^ÿøÏÆ“j\³@•ι£…3ÓŸ¢œx/ù¶A*Ioä{ÁGÆÐÔw|X¸=m|•@·ÿµ…ÜM^±nÂû40}úkŒG­¬^°‘ÅÃu?ÃNÿ-4›WmÖ¹“ƒÿñßuR„ÆÞ"alŸ›>œ[´Ÿ?¿ K|™ú£É¸r`ß?™çí‡ð»ÑÔêÇ"z&Fnµ×³©)T$GñÚÝö0šŒ@o¡=µŸ&Î_²DÖ-y•쮽az_ðúŸ"’þÕcüš±E_v˜¥@öR\å=ž¬Ò¡Íìøð¾ùüÁ”b´þ–ýpâgÚ)Õ”etßù\t` 3a¨nC•8ªk£úoý-†zžTÆ}±)hXoêïÔ©oø jx’¤ Hûo3à:‹Ÿµ¾€é[.°AÒëqÚ‹XþeÕ\˜«y³Z÷—!}=ŒoÊð ÞÓÊØ‘“Ðâuñ[zWœ.$GÝm¹áŠ °96ƒK/®^1û›kÈÅÇÇãÒü«ä›÷ðPBÄ_ªB˜õ,’5/€Îزô,^ÃÈh ­žÍ,vX Œÿ@ ž8 ?þxÖ_DÐôûc‰˜Ï¼è×90ãS£\l±u¥¾/*…m'Ô¹§¾:·gý³Óbık<Œ›Š~1wªåt®á‰QÓéa=_õ ÌJ÷ÑÿJWùÎÝôÓZz)-î¼[È¯ÔšÓ ±ãøç'.ü…ìT;ÔÇ&ø‡»À`šx(›ßéô©7‹¹iÕXmA U£öV²tu÷Kð#ªxþ‘* »;â"¶#Õø‚'}£i¹ÕUºt¿&¿ªcI½d½xññ?€]ÛAL"Üiˆì þÅÝ:ÖÀÖ7/^ásç,£A—wÑ!…g¡+ë6u&Á§dqDcT¶ÿÀzãrXÖÍ3ÃwC¶ØL¼ûS \]þoÿGsç ao™Ú§žegæ@‘áU!×°ñš‡=«eÁð‡"m—!  ªA#m Ô{ÈFºÙ1ï+µP•ØC„ÊÚ48JƒM|7úòáÖölæ1j#-߮ȷ® Gù瘟©ÂÏ>+—-Bûà[zÓ‰g.‡ý+â=­$òáá1 œ¢K‹(´Q“ß›¯ÅWî>‰õÞ{ í-ÿ O¼ôx¾¼Ó©A÷ÖDãΓÀ>ÕŠÔ` Ž4Z€Zœ ¿¹a=ºl¡ƒÂa‘ŽwcÔïÙQPÔˆ§ºÚšP4êWrÓØ³ø7_„JŽL‹gñ ¹Á h .醾Ukhà@>x¼ÏlV’§¾íŸ jy™¼$›þ Hùësð“–žYdžœB½uO˜Ýí“øV(BL>:Aê£0iN/¾êi‡߆òoÕIïØ4à-çàBj&î.õùk è£Ê—‚»m ¿÷Þ³Ù †qJd¼öL¸¦ÆoNn"Íý€=TƒÆã2<ñäo¬r9,¿çĽg)ö§¡°? Ýôoã]ùËà¸ÙšïܼÔž]FÇ6´èía/7ŦètÜõ¡D¡Fãÿ½ÔV'|¸}0‹zðƒNØâÞmÁpll9«øµ˜~šÎ&~‹Ãhé Ü0’Ì*Ð\wÜ/Õ£/Z ¡ò æž:_j›&ؼw"4Jù‚­ÊU¸vÆ NTíÅÈg°´é÷ßòµL5é•™!¸BÌÕoåÀccSZ’ëÏçØ_eY1ÃÝ)#8/jY¨¶Ç \_ƒ±íVßmG÷Žáv+ÜI„Â;x›Úë'Ô $$aï¬&â|Ï ´·ÏÁK•&8íÂtܾr0.þù¦™FÁè»àÉð-xÕÍE#k4/bö?þã1IzN͆¼;ŒùÎ&ísãaûg+Lþ~ ¤ß%𿇙›¼ QÓ„Þ4úÕõ+*ÿažmÑl™F¾ì¸†£".`LQë²hÀÆ'Z4õ´"׬úÿˆúîx¬¿÷{”ˆ„²W¤a¦TîsD*2RiM ¢i++³„d†•â>×!ІöTš´´v?ïïãñóùÃãq»ï×8Ïû\×užÏ×ý|\ç.NnÞ º›è/«`ÞŽàñ6x­ýì›Oôò« ç öŸ—™Óæ|es „¬‡5Q~(žmÀÏ©EOÛ¯`ÝZF\«pö6€}³RQ,]šª_;7MG-™X>ÂÿóQ¯™¡Oç+‡ ´«ÿ © ·º–Bº~þC‡Á"<þ{5è›S©T3¬rÛ‹nÈ Ô¿µ{ž‚H^ ,Øü}µ® ’œ'óâE|vƒP2†ç¾µC‡¹\Ú4ŒÅìÔÁ‹}gI‡ây°p×àžŠWp"O#Ò)°ln%tj— ‚+èÓ~ó â&|ÚÖòß>Â}ðÂ_ÙNðM.‹&Áˆl}Þ9_W.A‡<NÔìéÆÈ/¬)؇ð×£¹–ŸØ³"ü8¯íó/=½ÈôEŽÑ$U}Ì_Y.N³á€Z0m­; Ã+Åê ›s¿±Jœtaü€JÅ£Øä÷U«e¨—^TKÐ/þEkÿ2¬%H§¶é@ý“/”Å{ÙQX‘`‹†^”7:Ýbuçâ |a$4î)ÀÄÈ¡tÎ&Ïó¢Cò†C2ððà"L5µã¥#éI“¡â ÀÕ¢’mý´› ˜.O>ƒlY¹)µ5½ÈW„@ïºfm!v &²¶„Z(ßëˆ3~[À¾©(m½Ž^ ¶ž¯¿Ç`ÕÄ—,ÜÀŠ&Œ Çhñ.Á8 L™èniá¯×‡¹ [ySŽÞãd˲^ÂÕ 0óçk³p~*ÞE'ކ´[£¨^;¡N˜Ñ)Ù`XŽQ1ex%Õn/Râ±Þ–ÐóêÈ~2y›dŸm;:i4ؘ…1—#@T+‡Ø¹ÔÁÂVEú¸nM]øˆ¦­xîG5,4úŒ¹³FÁ\¿¥¢Q §¤4ñÊ=ú´;ƒÅà %ª5Y¸b"ó÷´ï>ªV>lª|ªM}Æ$2Ý §|=›/\E‚>ÌÛ° f¼™È—}ÇwUbGš,õn'ñÎ6 ¿!ºx³Å1|cG&üw½°À¤¼vº¸ÿXÇà¾ƒÏØ- ̺Êò¼®8ž-´åó‹¤ë6zÉñŸŠÕØ>Ñš\Ç=½ú Çÿ }Uöþë¯Â‰Ý‹×Ûå¿ ¥JìâX_š6¯< +lO OÌv†çÖÒühG!¹ÖK¯j¯ÁÏ×¾BˆO2åK,dÜ#psÉ„±FëdúïWÓóœ|Œßn?ÕáÆÞ~Àí pÁY>ê%ŠY¢Á_saÈõYüœÔPJ“}IEó)Üw`¬rÑ¡«3¾á÷xWP°©gS3pïã…Ðã¾ Ö*Áë¡­ðÞÌŠ‚Qz‡YÊÝr¡¿‡sè-‘¤Ïköáò.SòñÉO–í8Þd14+oc©»E©¢#Ì|'À¢¹‘´ðþKh¹¿Æ´š Ôÿ£›‚ÑêöTöÔošø1´ÀJì\­ƒÆMßñý/ùº¶ôLJÝ…ºÃx€[ µ!øâîPZrÏ+a ÝþcU÷ØÎe;Ÿ üÖB¼t¶˜­ú^cú<™•z&þI2 Âß… :ÞJ¥âÓÑì[È:ž¸=˜§=’%3ì•ø Mm‚‰OŽÂœå“Qôù'hpÍw/”EWlÀñ|›~ýkDÓûùÇ’* °”Îfݺ ÅS»aêÔó‘­“ñ†Ÿ>1Ð>•vYì#§þÔ@ø y>*f"wZ@0ÐÉLÐL½wd þí©þ];õ_&ü Îb*ýš*År0·\œ„IÒ?¡›zÓ‡²‰¨«¥Š£Ž€ÞœïÀ“â`ÖÅ84Ý«û>é×Õ7ȘM4ŒNþ†Òˆªûl¨­V®Jïþƒ‰[ÏeP89Z°h°`ÛŽ t®èC*eÁvmV£çe:¹?Ó}ó ùço¢l›0·¹à`2„¹ÏYNL‚Sñ)‡ mE!Ü+£V=„Ý®Ålò2iaSÌdðwÊ÷éÓ™G71K³¨ü| ™}3ƒ8c|×rYÑ“·ãbñhxÔ¤Ì×ÿ$<y¤GH<ÿH1-þ«˜øYwR:}/zyÉê–=a–Y;áí¡{äxH#Ф¨“#Oá Þ©xia{Õ1 ¢oÈòWIPÕX´÷ìÅá…²àø&Eàyº‰x<õg^éîÐþ¶‹ÌÝ3ˆÏóþè\g6,€q¡Ã»¤:±ò×VÜüç\q‡±»¯aêâ±T"AVFú1ù5y`ùîùí6â¿­ ùy„ƒ:šÙÂóß ñ:“³³…Ãg×"ë+e `¾"B n™ƒgZ™Ìµz´¯Ð¦Ìèæ²Ç ØcÌ"ƒkqKÂ~f³\Šk[a^á4^á"gåEèë&°R]‰*q) c÷ -w &z PA]•°WÇ{Š :˜Ö—UA³éPû‘%…JÒ9ª÷Ù«WèûÞ¦<:*Ü>m~U>ÃW$AÍ_uœÿ󕀄–ÂGowt-~FL†÷ ã—aAK·ðʽ\˜ý`žíï*=¬YªN|î¹Sm‘˜ÑÞKJÏËÑ  ±üßö×ê|—À”É0Ï?\ ¸“™Ò‘$W!–ܹ ¢¦DGÄÿœÂÓQ'ȽDœ® p5¦V¦%£ÐÛt>»Jæ¢o?ß–ÃÖœwªk»õ ·OΤMg†Q ø²gÛ꫽W„Ï&¡ŽûWôÞߦ`Ç×)ð`ƒjjIgÿ '–ûR@Í=”ÅE¨Õi\1j>J&×±ÔïŸÙE;1>kÛHúi”$)O•å*ɇàVÐ"8&и¿õÒQÌbÿøÚœŸhü O†5À™™˜°Ã/Þ†Õòø¿|¾Æb+³ªˆ4T äš‹¶gcžõ3v_Ý<Õöà—M:´[y ÙR£K—Ì}€Sš#ÑøŒ7fï«'ùsrÀÍP†¦øâ¦ýðí×DðÞ~~Ì(ƒ€YðòZ3ýk 2¯µé9?Ú–} ã ØÇ¾Hø6­J…ïϤƒÇ ¡¿öÈÂX8f§R˜‘‹ Ñøßøîù¾ú=ìÚUðì̸¹¡‡õaó ëdøÕ@†Ò‹™¨ßÑ-´˜2fíÍÅœ;ÙñqúÍpØÚUƒÛž42[Ý`<á$ÞéÏ£ÿ¿ó%%Û¤êpâæ;ýH‹žï¿¯“Q›rñIL~Ïöá¹ “ýª ‡^Ùp›ðå|éJuîêÐH¨oŽŽ ¥¬&cù·M8ºý;T Ÿ¾5Žxâߟ¶|\À3¶D¯A@7¨òŽý1t[*KàSû&QxêæOqHÁ\Ü`7‚OKÒ£ýÚÓØ£tÏd¯ôlq‚w+Šù{ÓÉ{jPÖæ"ìœêƒi«æ@ù(E:‰úaÀùdÜ6»I²epÏø<ܦ9h N ;Œ`ŒSî–>…ZW¯‘ WÇp£k”Oü×½ÕÈÿW™Hæ!¶¼æ”fãŽ4S¬µˆ!Ë?Cc{,º§ô×Ïéœ(KOnÒá)IULÁ6 ¶½c­Ÿâñu¾6Y~Á¯>8Š_M=ÙàÇ:^_$nú­ù䉪=9+…[ï<üžê šYÖüe}R]ëõxüp.ÒÕÅA´á7Ì «Õ0mD!ɶf'óm b×m°‚ËŸQú^¦=M†©¨F4X‚®(6éÊqßt¸ž7„xì7†Ctì |ÁÚ•’Õç6€“ºòC¶¬¹)B›3yµÜ´rÜ@ý·ô,»,ˆ|’™ »³tùßBL+×u;Jqvªq[7Ë×@¸§ø]—ýÏÇLú´`ÜÆUd÷B+È—ï³uû¹C£íÐλ «~£ä·QØ›¹-ûí…mY¯Mg –@ٸåY Sê'ð›%Ä÷y'H=ú%”qÍ­S„‡¸;µ™TužC¡øGè ‡Å *#AÍÁÄ>þ9vZ ãªË›¨‚ìŸgð$i4.Sneó®“Ç‚L}i¨¿ is1$¾M¨¢° ­,Ïá–…j\Ê~7)Ï»‰Û+ŽÀÇ=cð»ZÌÄg—ŠÈ!¿÷<‘ï\{Ïz‚’+®±@£©(ãø‰léðÆî¬ï¬àúÚ—ÐØEM‚¿Ö·âS¸äÞA\ª$ÍïNÏã“æ°Ò¡äÏdÛ1WÐõØY,sA0ÌëÆÁŠïXÑ‘±h ¡,$zÒxýí[«µ2ÔûSù'˜¿OiЕïñòIQÈ_t׌_Äúõ\1Ø"};‡àðæ7øC,§N½ò;¹^aÁÂf’_ZpQ¹®ŸM nZÑý’“àrÁ=8t<UìGñéCÙï<(Ø« ©¶ÃË/AàñïdMÖÄ´’ÿàú§Š:Yã…`\öW™ÂŸuÄ/«qt)\£ÏHçüвƒ~é¬ÀVß0œ6ÁŒî{{¶\¥¶¥øjËNòo§>ëÔ“²3¬Ha(7vÉ@·”D8¶N’/\vKååé5i&qâ/ŽnOÂ"KP'bûÜŠ»Ž\*±¾ÖŶÁžl|Z+B}™5è>†*£´i d1ÖMQ¡S׫òÉÃqù¬;pnÿä©‘W°´'•¸¯p|fÀF:å±›‹¬þédò×B(ùN:|3Åm þïå9ìî§çì߆íh:2BS´Zèõu)Nu‚·Cè‰J! —Aô;N`Íõt˜†¬rîYLÓ«% FkÖÅUG‹Á¶ú ̪3¤w<:A{|Ä?|Jº¦CF§>|Ÿ+BWl°„ýþ—Q/æ‘°ÜÚ ?Ç|2LðÔé­äÓ~iÈN\¶¼@6ô*\Úg[&,Ãe½GaHÍ*ÁÒ¼Ô÷Žéo¨eÁ×ÇÁò„‘ÜÂ;ƒ’2ÏL6¼'mþÀ¹K¶îåƒ@â q3ï,Æ»ÿô©Þ[SnªxCZ_à´ãR×*Eƒ–( ð_w¯F’rcûT 9^J8ÊòúuATK3²¹ §³…°¨ ì<§Â'izÝl|< 1¦}‚CZ„ïIò9âê¨yÑ®Îl;LYj¦»xù.¾Ûÿ»ßz‚çÉZ8>ŽÅ“þÏ0ç0àÕû_Т\½üWáÖf€ïšcpŒÂ8kn%G®-^÷~ó³(˜ „Ï GÁí§_1åž4¿3¦µÿ|ÄçX¬˜IÕ= VÓ™f†ß#’ƒ»Öž —Š^`Cí7¶óξR…ÿŽC£: þß}u¦¬ÿƒŽ«ÀUãÂìÝÉäEÇ|Ø<ÑW6' Ûàeü”QqÓ ’£,q‚ûЦ„/¬×ÄÁ«iò‘ëð·-œ-e¨‡t®K€¼þï¡]¨OŸ0’c-ƒr°mèA¼æ’‹²Ç‘C"{ûëª5ÌÐ'÷ÆÒ±yQƒÎ›©NŸ>·å’' 9ì÷†ÔY_ÑëÕ2Ôšó¯¬YÌvJ’O¼PZØ ¥—<ðä™*öÛ ñú58®‰P‰NƒB±/³ uGËSÁV]ª¼ä.I|]ÛbH_Y›[òß„uÊš¨B ðÿ¬ÔÅk½ œ“¡ó´²ðHƒ¯I|&Ô9òpê“é˜ãu¾{lfAn1o›OpuClMR¢9;ϳ¹Ã`ôôÏBˬÕlºÙ᳞L¬6ÓÃWYfôÙ`¾x>Z¹O Óƒy33àõ„p,rbZ3T k¿ æ)ƒQ“ÞÃÆlMfmþŠåDž ".§‰†¤+Ív˜J¥­ 餜½Óû’xç» ì±_ûާÁùöáÐÓ|Ò Î㆕ñPi’“´e¨ýô V=š=˜ OT¤ É¥^-^9Œ¾wøÛ׈…u†ß¨ÿ+zcPø$µ Ví½Ä|T_°Ðõ(3JŒª·C yìíCh2½‰ãŠ_â)½½pþ÷5TÊŸF7žë ï·$yýj†ðîY $Ÿ …hË{h»Nžw¿kc ’˜.Ö&X3[•ÿ*Šà…‰G„÷¶œ„_ƒ óUˆõëÝãÖ0¿9ÌÀnë¬Þû ˜a¥ËVüLžüwaáv=|í0žih€¡×FR[õÖ9x¦|@¹-ÖôÌôÇ Ä{øo¦› QmqS°…ú’›L·`'ˆ9Í£Ÿ6JòeÍðêÉøúoûo3Œ·N#^êq¯ï?ô´.`c}ŸÃžØH™",Ïý\¤™›Õ©Ð×ãpâˆ?ÿy”i¼³½)“M-Õ­ù»+áyÅ7˜ót7ø¶ià½Þ Q Ïƒï¸ðAÒ:ÔêÂbÒP"KwìŒåQIsA÷µ#~x…tG^ÄÓ#Éý >¨ÅZ`xp+)¶–¼£lü\Š/’ÁÁŽôÅ[åÿó`Ï÷|O¦ê=™J›>†¥ø©¨Ó3ž~y´ .œÅµ…£¸[Àøk63…Ú‡!­Â‰•+c"%Iàz¯7:9À›Þ“åï³@²G’rÏ­mÃ>ï…õºÃ1ÿAž»û§ÊCÏl6ªÐö¹2¼z½NZ{ v÷ŽcÞJmpL ‘ï]ŽŸW¦ÃÑ#µD¿[ ‚EŽÂ57%€ÂìÛµ ø[2šzn2a'k—‚Ó$ÀÔa"|ûGm ƒ¨Û¾à2’ÿm ¥í1Ú‚§„–•ƒdäMAtûܰFεDÀØÅØ¢“ú¼8e$‹ÔÎÁ)ïAˆ¨~TSÇÄ9“¸´ñ3 ‚ƒk§“(¹]š'ÚÑûñT‰$ÿjdEQï:ž×mCÙê¿qŽ­ÐÿG7.Çß°xòQ¸.9½®+.Ù©$l¹Æ?¾IfÜŸ ¿®“HC'BÎiñn‡R7kMœcÀ×,`2žšì ‚¬üÏÃü'm ±PÒ积 ®»\°5 /m¾M¢6ÄѧrZtï»åÄzÇ8U1˜Ÿ‹rÖ~p‡ÍŠ ÀwÐvd”PêOè%LâzŽýü¢!˜ ËGpרü}p-õÀ@›&ˉEc©>´r[Êw<Ï„'«™…µî)!‰ç'ñk{vÈkÿ0»3Cwu0böH0ÎX’Ÿ;¢<€¢ìm¢ýTŸ‡/¢‡×Œ£¥SÏ Ä#ºòewº¾ *Îù=ïן—pòÄ,*6å2&hÓIï2«¨0s€ôßê\ý5_-°õwá÷ü˜µ){½<Ï>ã“iä—ïlþO¡š¼Œ¹['jciò]ò¡ÊsrñQÕ¿ÑhÏv2-@gœßŽÎ7$¨ã¸«°´¥\GÄ Ÿè\}ÿÊOE×c¹Ôâ·°0¡ÂCƒ–ч_ÒùëVüêfið¶°@íiQ$ìåž› Ü~W-‰Òâ^zÐ/UÖ8)G éù ä¿ò»I˜½h0¤í˜¹ê9/hS¤šW3¨jÉa1þÂǶ«y¡ž:%IZ|)ÍÆ)K/“{¾Û>j Žÿš;Œ/Ú–ÄÇ„3ï/pzIEWeoH{Ì{àS¸]ÞøÛªñIo¾€í¡šßa /GÑ;¿&õÍCÈÇ×ÅÄKV'D­Z¾0åKÐ_dŸx@ãVÇèo¾Â{aê£8m“a†ÕÇ’”pH:åÛÇÐq½(±W›Êµ„“÷®Â«g¯]Ÿ²wû€ø'Úëtªn è¯è!iÇfÃݱ“tÇ‚ k5W\euÙ‹â.ðØ1€г<˺Õ&¼º¿‹»<èçöa¼>ýì~=ˆ—íÝɵ1‡±CCýϽ…÷ÛÀ¨™‚)âüð´uv o˜¦±¯frøW"“H| `én'Ðÿ{Ü:íÛpìóÛ£÷’»“Xïì2´^ƒ¹qÚü‹ÀX8çÂFæF“; ɸ Þë¸Á¢MÍÌÂõ"ÎýÉÚô½(üa±^‚þŒ@¼ñ&hÂ6ãþgþ]X6Ѿ‡~'“Öß}ŸCï‘BAا/ä\ÅYa“Â`:dœ&ïžaN[£2Àet¹Wó¿þOq'’ÁS"XBB({RÚO`ûÞ[ì«e$4õ$ÀÞË߉e·ý>”Íô±§°LG§µ Γpï±¥ݹӈ-äøGï¸pK4©çÃ}p)è6Ó0>‡M—„(2稂¯qüíCˆÔ2'”8Í~ãÆy ÀÅ¢ùFÉ,LÉ—å‹ß«²æ§Þ´]õ,nÔÃTÇÏìàØ$8¸¿NxªÎ¦Ùƒ}°rž8ÛgAýÒ[ä0œ-[ãÁÍ®ØÃ µƒÄfµ7´ú‡ã‘q ­qê ¥Io¬/7;DöÄÿ¯ÿáLS” wf#¿nä%jöp{çt:`Å/^¢d®Ý8Øu8\vÜ{†[Á;ÿ~=òF‹ëù»ã*?ºWÍ~=—ì2ã&—%èŽi®4yÙ¡Aì7hu^‹‹ÏÆãƒ³¸ÇUt–Bg¿¯À›æ7@«ó 8Ùžc $rä¾w>Í$ÙÂÒRKªQmMý DùÁV »ŒVÒJD'ï/ù±Æz0žôU0ï›0°þÿ¯ßÅ{Ð&¿\!6‰o.8 ©MÓ„ŒóAÙϱ*Ƈz¢ŽýSÁ#‘óÄ~ågìîvd1³ oÍCäÃn ä?%ó‹û?a–ú)zÑû,l»+Â6Qnß¿ŽôÅó¶QSyפëpÀê9ôîÏ¥¥±:ÔðÑRö³â Lo.F%q1ø§pÙ‡n2tŠ*W׉§½uèÔ óRþ€ÇÁg8¡t½·ê(œŒ C¯šD¼ª‚ÔîœÎu'Ã'×}t·¤ÏÊËÃWãy–fÆTÔy‡Û´±j'Æžb:¯ØåiºëšòÀóG¦Áéw"lå»~u…5ɽÞK·!Èþ9²Ñ*øÝy7>7v9£í~Î|Ø"5ppÝLÚ7¯Do¿¡gYÌÊ€Ò81®óU“FäM ³~³­c§ðå ·r±Çø¼w&]óF²ž¦ñwwhÛ¥Ad|>²yâ#xü½?8:êû¾ÝjËÌÅgƒ÷>;ÊO•ÔÃõðñ‚Ôfoþõ°¼x¡JwÈ`GlsQæyÄlN#…÷ç$ûùtJh<^´þÞªÆ&æp¿P—ª)àï]Ã#  âŽàT`+È¥ QY8æ§è@ýk•“þ?Ïò3B Sþ¨l.ßL¤¹’åEæ©ÛÈ&Ö`ðÊÕtÄætÿ‚õ‚ðµ—½˜â‚¿ҹʶßö€è­‘ç?N£>cô¸Ò"Fb܇Ò3RQ‚ó Ô§±<Ý7œÆmèÄ=µ—È\½«˜£…=W“!û´þñäßnH€}œT~?H/©kò%SŽ“ï+÷’Y«°Éí![/– óLKð†âr-i8>^Œ¹%ipîÂX:ýaÑÝI×ï©fPòj†o›ëŽB]Ï(š?;LpßU‰Ž3‘]ºÐ9»Oàµq4îùUŽ·]qV¼7¿¡—ßÖâžEûØèçÑx$m4ñ;%¥%ùl kð0ÌCõí¨S‘r9ÃèÑ;w™ø§98kÝBÌ»¸V_܈ÅkAq¡.·–¡%oQfc%X-Ÿ‹FHÓßB†“¬ïàÂqÆÌqtöfH¾;Ï{‹¿›WÔùƒÁ/º @…:Šõà·æ ³> 6\z€æûÕéÝIUL3;U¨|"Šì¶Ÿgc»|°iéP~l•Æ{OÁ!Fp½è iP0ǃ¶éXýb—ͪVMŒ'Û{(xþ!bmDÔ^¡ÚŒ9ôûa * °á‚ ýtÌ“ðÂXº~rK:Î~°Šj47UfÖGð¿&ÍlÕóA|¼Y ÕðŸCÓ/–a›é¨~:”^* ¤¯ÞáZ! ´tËI¶[¦„D¦l5Öθÿöòù56üLs)ÎÓÁ™y!tª{4Œ–Î?%Žù?÷.œñ'N\2‡ë+ÖÂàô50lDìN»„åGL-œÈOlÙ„3㟲Uµ«áoÞYþxÓ9ŒÍ;G箆¦o"ü̈‰ü´ÖB6ûA?:õuÝÆÃ›^b±ÆevÿpîÀüçï Ä”ãˆô½Tíö 6W~úiû¯c*NÀÙC,´”Òzí[0´À$–¤Sv£ê—ƒÈ°xдœ*~•å[GÜ@G:C&Kà=„ç}­†+¯fÑÍó/Óü¾q\ßøìmBƒ'dNöKA¨ÑçKóõ=Ž,øy(üQGFÛñ¹Ë6a™ñAUô<ôóÌüé.5;P-[F¯MÔà meØ?F¸¿rlûqŠÏ3ƒõE9p7Æ?ZîÂÎè|¤a“€5\~P_€ð9’~Е¢"žtïaÞW1 —?ïÀß2ìq^æI¼²¬ÍÅñ—+ˆžˆÁÖ‚ù‚ß1‡ð‡ÌJ~)G†:ùóÿ9ü]ùn’©k ?U¨OáhAø­ $ï¨ ÍW~ß(ÓÍßš`ZÄMVë"öÀT«ùðÕ²ˆ¹…ô0—Õ#è½+çA~Y,Ú¥ ·¾ÑÌñÅ&z.\ƒ¾–*\hHkGÊÒ€-b\u¥$®|€Ž}Ñ4ÍS’vܵrv¹à`ó2𻤄yûð¯ÝI°éb0K°Ω££êvL‹_ƒõ1ô{sÖlCÑçëñ`½<ýÏ3ýyð:úA.i`ýó_² êç¯E-FààÕÇvÌ#$x×KHw¼ ‹þÞ–¶ÅCCÅ1¾^ò˜Ñäì>xZׇæbë•eÿçž"6h¬!xróRt_qEE™ÃM^yÁ•Ã|kÞ¶y:q X†–^³µKö’²œV‰: P©ÌyÉ%ô~ÞEÖd CÞ15§(¶û•"6º_(Òƒ­3„öƒOƒŠ@’GÊŽa‹;`ýè­pævèÝ#gúâqNòeÔÈ|άeo2ÿ´ªÞxã¶E&uË/eÒ@s)ZYMP-·Tu°)Dz`þ %xŽO$ú&¯¥xF6‰u£Ö».k{‰‰‹^ź]_ÑzÜ0n`†xý(‡ã†!P;&Œ¸õ¯çÖ¹ÓèßeP½ØA`·/—q·¢%tÙ¸:3ô~.BÅB2h?|x†Õ‹/ }HdÒôaÙlª›C •‡aiÜP¸› ¯NÓÝc#qÅѹ¼<ë9¹,Ý ÍZIPþIYæªpâŒwý #—|ƒ ‘ƒéó‹»„M†ÓUÔë½oÓï†x/qŒ-ŠfûÍ· ûñJ†^€CƒLø†ç+hv„(_lÌÙ­ñüo׃bˆ:u=÷.â±vôÁê2 fIMô!÷<~áÍ qjõ¡ ˆ]C£ñ¨1>m§w‘+ ߉Ïs¾ÙÉWæÛÒÑç’-#´Ø­âX~|t(77þ|Ççõ$ C™“ê%\*dÿ)XLW&`¯·¿0,—vN‹çΑNtŠTÝ@Ú™sÛ£[¸|¸*7ѸÆÓ¤Fðè²ü^¡OÚº¥îóÅš6°Î_„ß+ï×8¯xò8wîs[—íº¹Žf×Ð÷“~ÎBX72‹l»yÒ #¡+w°jÌq¤oô3êÿè“ù`b;xÂNlÍeŽý&ë3Ì`÷Ø2c‘ mêõBí s!ãjNßø,«P(ºmî:ìÍÞ2„Ö§9|úÈËl…½7œ‹U‚µåâôTËY2e±õUŸDŸ]Ü…½Ç©wZ'5áhM9<•èG:òL¡­z1¼¼yÍ6Ió9¦øœÀ ÷›Èqñx[zŠ‹rK Û,“«Z.ƒßF׋X“rµ.ˆ¿PÀËëó1âÎs²¤¡—ëÉîõ\W;“ óÑæùƒæ§˜ X2G”t´CÄ´øjt&艀}z4TvfÌÿCywÔ?䆇ÇNä¸Ëæ: fÓŠ¡tÞ40ózŠƒl¦ÐꘇóD¦“cW2qtdµ(Ü7—Á›4þ!ÙN¦»3Oá_ýá0ëD󟼋Œý¹É7=³°Òö0ã–Ãú"‹Éc—ÇÐÞm/<)6\Ä• ò@½Î›ªÒáþÊC1LfŸ0Ø×þ½ã»·róÒqtÌDÕÿ|Ú iÊ;Ÿÿ¯ÿ4×<œ&¼}óÕ·à—;‘|Oä4ø»V•­Ð¶CYqÚQÐwÙS˜ô~>Ïõid«×쥗ÆcÒ¾qóo}i"\9®‰ï¯^eUºgá¯ù¹:cýÄÅEœkæÁ¤¸ÑŽäv-”Šõr6\;~vÝ)\ îm¦ôùw18à> mÞIÓúÎ|ºëaF‚ÄÇrà;»gÚƒ¾%,¹Šz§­T ðgÉH£ÓÈÚ†, Ђ“^?Õ]“ðl«)~?ØŸ¯CŒ@vÌøvGh͉bO?t³»»®B¯Ïa8ú`0ª>ÛŸ¤@ÇúBö_¯êY_ÁIÁ°ù¼ ÷Ì^B3‹<(5:öüıWõiÁj j–4‘²S—ÙÚ´5xæP>é/Í m_‡MŠ)h:ç­°uÍ_Pݘž•‰`lpI¨<ˆh¶ÄuÓ ˜a7{šÛÇÜ- ȦOÊ´4؃R2éëÓKñs0f«=Ðt7&ÿï¹9Dã—>š·ºãcÏ£7-˜Þ<~ÿë3Ý7%•ÌY¥Žit§ önÖ*.ÃcÇ7påÉ¿!»Rj^fl÷ŽyˆŽEƒî^Tݨˆ–9ÕÌÖo > ÈD:LƒŽzu ]ÆÂ>§ï0âÚAn$1 Þ/“XÿDëçÚntó&ߢÎp»?gpOQ3.È߆{¶¬‡ÐÞäå¦!47PSiuZ³ú4Ü ‘ CŠÏU’§#ËŽa«6% ¥;«²ÁGdù–µþü´v*()®ç5b’´E!¿Ø çê¼Æ¦Ûi𮌛ám‹Ràè+¬¾ó€_0¡w\„ôŠ%àþJš†tºƒÿé0çú%¼üx’Ö)¨n±Ì;oLùÓ¶ÿæàCOàþµ¯Á‹ÞáC'/ç&æjôB>*n½¹9¯Á„Ÿ (9Em˜ííûày-Ø¥üXÿÊó!å8ô­ ¯³ ùÕé³ýþ‹™w‹Áà²1<›£ÀÇ^r§÷÷[pÏBø>}(5P£Gˆð‚L  Åvüék›…ÉhîÓ"{øý˜ò@›³11|~ëV&‘Áï`Û™7à`&(­lÝÃ"|P8aÏX§Eñç|c<žNz¶”>¹³¯ŒJáG„Šxc¥u Ax Zê=ÃG wÞ¹ëhþö^Ø[Î-ÿå ?.õ§wOÏ)ÛAy×3ø[T—÷‰°#½W›»à•E?'I¢µtgJn­¢é´øï]x 2!uO2þ»¡Áõ¤ ×è<ÃÞ/dåz”9[Kƒõz9úùÐjœ­y„T¹>–Ø#;"ŽñgÞ°yðG·Ç½=h“^Ž­e—`’}öÔ“?VÁË9ó¡Ã¿nÙ‰÷ƒ¶àVÅnø`q.)ÆA{‹N¿aˆÉ+ð«ä ÔÌ(u³iüÏÑðpúÛÃû›Ù·7OÉ¡—F¸I;\Ty‘‚µµ8C(8Œ¿; »O‡jÌï`¯÷5 CEðY®™pó¹¥ ¾{> ª¬¹ô&5”o€¶k·ÝÎRø=~Nô‚¡kdæÿhb!nS«†îÉ ¶ÿõ€n“Ó¤ÍÏ¿µ{/°y1F,hÄéR×pä˜D¼·î,>=…ÁuÛéöâ"¦~-jŸ¡„VÓ6,¤¦ [ÿyªhÑíýÚÁg¥ÄR £¿\À¼#1¨a¦XF Uµ´y“™ ôÎ[‚…¡/Ù¢+ͰcñWÐí›У²„‰>ÔÅtTqçåƒaë)ýõ¦{¬ã°èg&‘!àdÆÕÎm† K¿17,Ú¯)ñ^³¹¦¢8¾ïî;P µŠ!DÖa1=›”†§ ~ þû{h¿@46E`£Ç(:üØhº7Y ;ßó“þ­Eëì78$wÈÀú÷z‹;‘¶š†k‚lÉ»Â"ÔŸbK¸/ê¯›Ž¯’Áà™5þkÉ¥;hñ›uâôLúfìš ›Âõ©˜ÿq8rXžÝT@çPáú?1BaW $§hÒy”»©Íú¿¬ó4ž§<ÇqË›]‚Jw48íÃÌ_Fc|/äºOv»cˆ\<ö}8ÓT¡„äWŒì˜¶…ï1Gt|ð} Ýï”ÙËW¢´¹{ Ê[ê-²µ¨ÎÅhD]\sÌ@ ÏÀÚà*~¤“ø®=ÙÊ ›‰ôëÉ|Û?#¾o ¥sÖÔ’£ñçÏ}ÜÇ«{ þß’lHØ“‰1å h࣠:²˜—À MÁÓ;…xÓVF=þ¿¾Î1~³Ô¡[!iëK\û¬ž9Ý"Æn8lesTw ½¿ùöÓÐéû,˜:éü|Šû†B›M™ öïI81š¼:kRúØ7…'ŒH43±ÂIÄàu3Lømãm¢H×Yœü½´ôPSç8M‹Âæ¬,²o£_P}52ø&m 4n°ÞÜm’àUwÙý#æP#7¿gYrû` ºxõêoׇ5ßÚpš¸$ú¶B“gãÿö¯^ ÿŠN0¿¹—À.Ãë3¥¹é-¾4§ f¸cÿäÀõáî$Ea0\Ý–—dáê×oaæŒY,Ì Šýv•£æ~‚üs/0àÛP˜ íJ¯-›G²cáã÷"ع2Ì¢Acî°6{»±Œñ ôZŠž:-EÒ*¨4Â}QŽ- ¹­Ô(¸ß¯³¦÷6N®›‡×vÐåêÑ0|ëâùDVjÕÀ›IŸáÅ3-šå}¹³9¸õ=cÚÆ¢ßQ'ðöí±½””Šê?ó[)K–‘Eß~æ¢F”‹¾ŽÛõTiÙ;õ¿Þ^åŠjÙ³{g%§óQ|µ²AídÜŒB·û9è)sn‚òÜœ,¦P£2&¬{kG¶3©Îxf÷Ù›êrô[äDÂÝñ̶ùðdd3˜/¬ƒ–ä'äÛ¸ ´(£…¨K„áÍ(_VòFÛX<ê[âaõ]ðïÕg8|º7=6†Žù»±zò\>l=³vÅù_äëš÷cîžiè} S…X_êk¬%u[°ý?­dHnœ2ß´×UøèM䮼_Wön ¿ñÖH5Æ,zï9ÅC¬+ˆÖðó0DÆo ÿwÌ]Ê~‡a§¦£ÈŽZR÷c?tG"äY ¨ÄVnh"…©.CøqÝÂ¥;;ÙÈ·KpÖ|%Ú!¾Ž]þ>«å§Õ=l/?‡÷˜¿i1.M-âÓ¶®{ûèqš¨׺7Ò«N•¤xÅ0Ör‡·ÄAµãù dÙSÆAvn±¿ËæG˜ÒeñÁиª’?å‘öšàÞ­`×tïL©CÏ=·YÏýœ¢’‰–Ùá„7™´ox4Ï5¸kÅzÙ¬}kúµóu t‡3÷>H¹•¢³êx¾~Ç""±Kƒýž@èÞkZ1ÿ v¯–{¡Ý_ž&䞀·Ë?¢Nà]¨Z-‚3å;Øí’9Ptg,DÕÖ ;%æp®5‹—–¡ »ö‘ϯ&àÝ÷QtÍ6#¼x ‚%îÀ‘Ê.r,Y ìÇ×1_;ù§†wä×ÍýdSÓ5”±ú‹FÝà§%ÏkºÜðºèPª¢*BÅ”0×ûþ”~«$7çžÃÞ9!ÌÓ.C uOê-ßnWÑ€ªðåÖq¶Or,¡Äø/J\Ÿ½O`CÕAaª´oíçú*×ç½£!KÜ'ík†å]i¨W& LKXËÉ—ðè‘/t%Jò#Ú/âÿäØìA¥Ûš¨Ëoæ‰Òe‡MàÞ0¤nÂu!ÎÝ8uØ&ê3æ¸ úZØ^›† >Ûž*¾ÚO­¹…Ôx¾ DOî‚!'gÑ„£§±³b;»eöWÞZŒq÷™ëy~ó<@^OÖ¤nîúnÊWŒÁòŽpÛ’âêgqC} ¼?‹rO÷`tÝ+îûÛÚ—BÇÆíP¤$ÅK$†qË’\bââJ×¾ö¸dAP™Uê ÅÆ¶lÞí!Ãg Þ÷?=…—0õ¶• ì¾*GñXÈÖ€ìkgÀ)Ú(úÎf1 EâßïQ¼½  ꟠ò[$þœaC^ûÔþáßbž°°Û ²rŽ —e§U¼õ–"Zó†½Nîç½÷÷Fû·©ì@F!¦‚'zÞ0~-¢Ü×@³Á°ðp„ϸ_oWÇkƒm`¥C2Fnx‚«`1ÙCéƒa'qh®2¹`!ËË| θ(ÿW„{†aAß^A‘$nH rÙ‹¦Uø½£ÉxQJᄎJ”Üÿ°Q~#ɪI:ö÷døÛ`JçÙÞf E΢-:Ò+7ž°0- Ó: ™« ;føYß–ùwÉq†”e›qÆû_ìJò˜›g[®[â’q2”ßî‚Ë»±1g9˜^{NönÃP w\{l*ª÷hÀ¨§/Ù`ŵdÏØ5о%…íø.Íئ@fË0V¿ mÙ]Ìk{*nWdå £Á­9öKõ‡©¬Ã €¬ZÇÞ-|‚»¿tãp#êýI„$}‚Êo€Ì0 ƒ ÇÐ\¨@™!ß@qãj|õ~/Ù{o¿[Ž·vܦ_„¾w8MäcØ R‡w§çÁ-½vœWPÇ/pòvt†jÊð¿Ö²´¢ý`T®Díwð9á8(2àÃë™Ý¢zhò{€ÅoFÐÃxô“~…Ÿ],Èò™‚ÏëŽ ¹F^Ïl„oó¸#W¥#g¥ð–^ibôJ{NGcVõdXÑ™@ÊÏ,Äg²ÑÐÖ(,.&ìQ£«VæãÆ?IÄRæ"N4ð !Ïϳ±_±h±(”öããaùn8¨ªO-Wýƒµxç‰çˆÑaÛX-n¬l‚ŸÁH[„ÕÏÝ豫qxâ]x½=ÎyŽáÆ]­8ôi0MÛhÄïy%aÕ­y´ûiôˆ€ù}8Úaô¦-¨ï%p¾~.úKWÃ%,C1« ÄHµ›÷<Ãwÿtø’¨Pu$w—,ÂÖSáøc½Ðiž7<Fkû ÁÞQXâ %çš@öÇ`:sHû´_š.#Ò¨C[qj¶+ºo·½wÛvVÐZ¨ÿz_¶¸£U~!þ›7Ãa0 {ûßÌʧj|$¾ÅRüÁ2Üwÿ¾3jÃï{eè­ì€ÿH|Q˜ >÷ݰwõrbï¼[ Í-ÑQúÙ8qU$έŸ* ó«T¼mðÂo+ó¸+uè²á (ç6©÷¢P’´‘?=ùzÿÒðFð]pÃ;ƒÐ÷ñåÚåÞgayfÓ=Ÿ„µ?³åJxåv†Pßc>4¹ŒS’&á\Ž&‡Pöº9TwÞ#-2û`´çlÖ¨.K;Ž¥gr•á*Kf gøÊRѰxˆ—wƒ Sj»ô ¼]Ìó…¢ö1¼£0?ÿÐ?=©cx!¹ˆëøu?$ëÅm¡¾×ˆjrÀˆ?úTQAsÁQ<ÿh1*¾4 Æž|úJ¢,0àÙMÉÀRçÑkÆJ¨?¬G'>ùÙ7ñ‘Ÿ6±å!› Ä†Goàk扱š{¾øGÎv ÿ5õÀ™VMî§oOWž¼HÄ|ïàÉSW Dg¬¿] «öÏÇ]êä†Ó].è1Oï„j@æÈöš…'žoƒô>Øõ³ŸÄåÀÇ"Eü6“N?ÄG•}Ì|^.d©Àlõèk7 n &n;·°OIR¼\_–o™4òúôáI†$õµÏ©k´³}3f>Îß­EýHqýçã`üþ'øîËpúIZt ù‘µÁ×=]è\uYÚþâó1Ûx›é ¹ÉŽÎÚ.Î,£\hô¥EpøÉ+üûZƒuw§³)ÄÇ!ð^΋À/: ò 2à›y”­@«ƒì‚C>ªn®°‹ëí6`Çqи! §çìô#v†qp$N îÔ,å9RsèéN rý1„ÜîB ÛL<à{EˆIÃhÐ×lÅVØå' ‚vüQó0ìן¾n êf:dÇœËdSj)Kœÿ–¼† ì-o›OÄ&1ÜÇ`ÓXCü=/‘ˆ¨6‹Qµ‚˜ÜdÇæ¤ÃË€³Âѹá?ÿöÃ1•ìôDY¯¬1í_p}ü"{û7Ì 3ƒ?kpŽÙs`Á¯°æÖH¼qml ÿÿm6™›Î°Q=ÌY:$¼ÜÈŒËL€L«ÁÇ ðX¼ îýÂN/âM>àùò <ÆÄÇ%áÛŒvÉ^‚7¶YÛkà¹ÆøŠXœ¾ÐžJ™´ãº2)º>ÖLìèŸÏ§àÝó-D®áªïÊ ‡Z$ooaÁìV¡éxZÿ"¦ßR㛊Áÿ¹›v9L:Œyû.†¾+FÂÉ–-¸SMP*T¥ó®†é›!¡…=”Ò÷žyÔ¼a?”l˜ÄŬ¥÷Vc€Ä_4ñ\Ä*OÍÛDcëô©éY}¡Â“øìÁÁ ÑÉÔþdžço}ÿ`õ÷4Hí_ÿfG±Öv)4Z³åw–³õûñÇüƒl[Í]ŒrE:¬¦üaê.u–hŸÃdù ’7Ës‚¡áÄ!4°~ˆtfg$ÍmóÇm‹_á ê‘Ôf•+|zQ­ñ ##® Všb+çü!C,aµÖ‰à¿zm8¹K neìÃEYÊîXÜômòð½ô–¹½ú߈UgõîÛ©ïËÿíÞJøýK:&´ˆóxãCørS $õ¯UO} Ì‹ÂÃÓt œZÙWFÓ¥>ê|¹¡MøEw¶…m¨Oï!GãtvDÌúAf j§N@·Œz’UÅïºÂ^–œÅ‚âûh|Œ û V5¯Ù¹?·PÄÄ–ÊïÀªç:ø_¯èm–ÃÉE»UôÚ¿/¨½s._ Îó\·ÁŒ•Tzò1!2®­÷և듮5l6\×%'¢{D±âˆ¼{ÐL#(gÙ3Àÿz…UxÝ¡\¨±4O{m6øüÑ0ú,B£¶›F½7U7À¿‡[ø0ñlöÏNDmÍiâ þ9£vÔ½f¿Ìöƒwéy2gDd¯-agfZQѯ_QLe!šäGß~Ýɵç_&¡³Gq׌(öðz ½¦ã#í x®ëC(:7Ÿ¸4“Ú±‰°°ev kâ‰*¸î4Žþy±ž‘âï½ÎÁ9µ@GÆ1ÞåJoʱ¶!‡))ÓxfBó3JÐùò”‰<ÅÄÁðndÈE#03>ãMÈÀóŸ×"#ˆU¬5Y•zP(vÏÖ EÅ€é° ­â’•ñy€Ï”ñÝ™°¹f…çÇb½W»fšáÔéz¸ o%””Ž À;¤¹è ;s7‚¯]Ä·Ãuppé+Æc€YÍŒÛO[ñ¿~Ì2æÛqi£5¸û*T;ó¤àæTc~ý1.9‡íWÀl­LÚh2ˆ®êòänù.MeÏ-‚=Á±Çäì?i̹ G¶­Š —F£ë*=ÁÌ jôLP+¸‘-.oHÑ„ßèñ(Exï™)IW¼‹‹êÖ`ăc í;øŸøwµ[‰cðkŠ:]Š[‹¶ ÇÕ‡©Ì|Xc@5†6àÇñüå/€u®–ük6/R”¦®ÇjøÄv!õˆa+Žõs•’zñÛKÒ¾Ê ”lî`Pû]h¾zˆŠ›‹Óï~gñÓÚP>oãmˆZD¾îÓ¥ÿŽ«âÐñî¥+O3[åø«_Ú¸¸?–ÓÃxÿ5V²ÒT4ô3ÓLLÂ!â[q<†kàô/èñù‰ÿõ—nŸ6‡ï>‘¯ù=˜j¯C_š >‰Îè¹2¶îYáèé.ÅÐÜz‰?À·¾ÿ|ÏpüÖ/LH߆÷~¸ Äÿý‘{±üïö_ïäÏ­&\2ú3±j áSýycÅ“$ÏÅ"vê÷s—-ÉÁ³ƒé·¬úÛÄl¼Øèw{II0ójåë ÅéßÜXX0VzÜþëÉŒSç ça—`¥Š9ŸåmÍ'm¨ew¿£u‰3ølþGÿ”]R!Ðÿ ÿd÷Šï)k“»®ü¸ûIzïA(6œ*ˆ=Ÿ yª"Xå–NÏPKkoE:]Yž¦Ì\Étò—‘jûý.zþ}ƒæOὋwóœGJlæ„x¸z?ÞÎTƦ™îtãº1l‘TÈÀü‹uF,,«æfoØœµHÎn¢lO*”y¦âc ðûã8^sÃŒhà®M áKn?'”ºê;lpE×$.Z¸…®Í™Ç?¬X\’Çã6œƒ€æqT2ÒUí§B+ÅÉÚêÔµõ&[h;‹®_u‹ü;ŸKN‹Oæs"¿`*Oa5O†Ñ§ÇÈàK ùDÇ©rqt{BLüãÁ”U!²Ì‘Ϊ4ᇖkƒeϺIp ®ï]É£S jßZ¯ùå=ÝùVá0^C£Š^÷ÿ•´<ÖϧÕàâgº4X1ј_ù¢Ç\ÁuÁ¸íÛTš&’‹žkt©O€;¾˜¸[½ÊÉt•t—ÊfË3{wÔÁ¼kŠ|…æ1Û_ cTŠÀSo9kÍúŠ™s§ðçÑiè縔kJ[¢u¡þÃQ0k8‰·ž¬À-“càGáSÈœWväÁó¶­÷cÛÄÍð-a=-›"´y¦LµGÍdb§‹àÛ’³ÌÖj+ÙV¯ÊãÍþ ¿ì‰E§µÁøñod_{†3LDyÄçXJ¶„Šº”=2ŸŒ_bÄß%ŒeF'~ÃøqKè¡¿5ëŸîÄpòø±9=ö!jkÔø›W-,6ÕÖGi'±×‰ˆ–šöóË)¤ïÑ*”5ÞÊ3ŽèÁµ’D²4‰pÌ©ç1r*Üú•9ªUHÖŸy¹Œ&F¨‚‘_9d™©S¿ÏÚøâñ1öÞl÷?ò 2SÜ`Tþ¾_f-]müSxTË ÷µ?gçëá€E4Î|5”¯J(fݹßÈÎé•(ûÞW>]N÷ls$½eóÈßb}ÜpÍL+#‰†¡#-¦ã¨‡†;]‚´H'›“u?aÁ’*þ AàÿþÛëk–ŠCµÇ³û½`ç7]ZÞ: 'uÚüþù3æ>Í©yüP“.orFXS&GwŒàÄø¼”]lš28Þ ì‰ˆò`#%ºH¾w¨`^Ú(~ø‰)Šæ$Âú:ô~f0ïéµ§–†›ÐúŸÜ¥Š(±ª„º½Ûƒêþ~SM’oXK7Ž_„W¾Ã]Kê]¥’Ëxò)nÒl{íxÚb-•S‰F®(¹E•%n¾€ÑÇ]é¶±u˜|}3 ?ÉVJü––5ÆvÊR»WÑ\µGŸ¨ØÇ;N©RÕÈ]ü⽑üÃÜ4,q’äß¶¦îßÃßóv±?Ò¯‰DéØìm= 7ä¤Â“¯[qxO.íØôˆÕW4øøæ¿c‡¦¿ÉÎ÷ÉÕ0#öwÏd~+K¾\‹³ˆÐ\‡2X~åÜ9£‡SöMÀÿ$ü«íNÎùŠSá3_zßð´M'hŸÀ‹J ãìMËGêѧßF’E»îb@„{0:7-Qå·‚î@øSäÜ7czÁ¿œß·Ö=Í­GHë%zïrï6d­ò¡°xÜyd)… Þ݆†[Yàzºm”¸ðþÍ4zz†4{X7Ÿ*±1üêмrgÜy@N RDå3xç›/ù6‚£¡òÜhN5Õõ6eÚ¼4šL=ï Û–nFµ×ôÖ‚iôÇà}°`vŒ¥žÔ§nbÿ¬¸úý âÔÔÔŸ;ºìi”ï•4€¿jˆ8ÛxM•ÿçþ–ºùö£|ÍÕñ|Ò´ŒÈ°¥û˧ф%køý”_‰O_­«än%º¿NàO„–|GΔ_6ƒnž¼GϤ©« pF…‚]˲|vslßÿy _9u¡Ø)øó_N® ¿ ¯>d+öýÃ.hN¤ÎîIШ*ÄäÕ?aÝmŠWòmiËA×ÌÃ3>rB®€ný_}TøøûIL4±ÛÍiS-ý,„ê\Ml2)‚»Ë9÷ÂÜóÇ E~}6ù•­XÜ1°Q8 sWÃFS£gåh¢‘ÏrK4øˆØátÏ´Á|¼æ[ÐhˆÂ«† Üæl5z¯¢=ƒåhWàX|Xtœ(UbÀú* L ç²Ñ”p jÝJñįpþÞt3Úß[‚¶zšVÙþsÑ·œüqÒªl>b\ÏסJ2©DPàÁ6¤oc=ëFñŠDqŒ½˜wži² OóhÕËq¨±™ÐÂå‹q¸š"6m-¡Ã§Œç+«å Y!‚4^Ï_ïHÆÉ ¾4»î D5ï¢-æ­Øsö';wg™êðü¶0(»I#-Ã:¦*~ÁÌÒ·Øõ¹Áv›Îvµ%Wgtã²öþFIü…ø³·áŸZ¼® šŸ½a„ðÅç šr€ÞI¨«ž}û¬Ø<„Ï3ëf ôjÊ ÙŽìÅ“UìØöÁÈó_`[ãÀüŸz!d§ÍÄÈ¿~>>iØE<:L ÂcÍ©²zÓ>hÈ¿l ÆÎbtƒ4pÅ —^8«ÍHp$[­¦Ðñ‰%,]Ù–M<¸ŸŸ%n QþF÷Z±®±‹¨voýƒ ¯/‚“o‡bê±–àdÉÛÏJƒç[ú¶Ñ’oRŽNÁ~Ù¥H›æÆÁíøXÚ;±‰ttÚƒwÝ_rdÈ{Àé+a{B<†ÍÝ TOÁ×Г¸âå¯Bqv.* „ñÙI˜nòŒÜÞ›Ï%jõ7Üé½7œçLᎠLÇo6o)|þ:5mÄÿö?S—;+Ør«‘TŸ­…€iGÐÜa=ª¦0ñà¼G®±gÅLõõ14›Áq÷Ê+P¦«Ig?˜ƒ[_½`WÊjHÔ™Ó ~ª”TZÙ3²4Õ'›_qj{óI¥FÈY½Îö»§Ï…M3ØÇ)|뙸«mìþj?†fß,{Ôxõ *~—B¼ˆd˜kñ!òüàp¨÷:˜@ýK˜1ñ(rYÏÿHÁµ˜BŒ:㊚jبS ¥;¢ôžT-G•Ÿƒc?m!ÑKœeM=´kcØ~Yj³/³.±eº/y#œ;ò±ðâ°IÀ‡Jñ­-1ä¾Ña;„JûvAH¸)ý®Ó«Âkqêg=X&x‚jq›Àct Ž93N´\ÅñVÆ´ô¥‡ µÉdʾ82Y–Ku}gz‡1ñæXdC ¶a°ü}p¶: {ßleÿ“T3†Ö„…;ÏbúLUt“ŒC§•V¬­ñ;<–ïd}rdÔæ>œ‘ïIÎÔIÒc«?’ûÛödé´>VÏ&T¹@ÔÖ‰ä~f'‹}påe‚·ÁGáI3 ¥)Àºð?X¡ÔKÖösþ Oÿ°Ý.nÌxÞ7¡´Ÿ»au”Þ^Ýdo:·ñ»—V.Œ³‘ºoFåYr4«vÕk];i¾{ÿf þï;ŠÅ¯o‚]Ž(H^ƒöL{>e‚>ßÌÝxbÉ?X;OwÇñÏjðýŽAgU2;žüžlÿ5ž4Gñ5Éðàâ6ÁÅâXö*þ"“hnû­Ÿ!³öð­Ìw–ñxÔ ½‡AÝi(1ûeiü»‚C:ª}³Ò4dêKv_¡ž}•/ƒ•Î'஘‚ðêŒ!8h‚-ø¢HŸ~EÏÆð:¿ÈŸË/àþúAA$^ØŒ¡½$hz9šÿ–æï_Lƒ_»âò‹ñ¥‡ºË(ðµŠKðÕ$ô¢õP˜ É~©èÓ¸ó¯âÿ@ß ,›ûRð×Ï󓪠äVx†A}yq¾^ó;»éD_‹#§©Â»=îäíÁ™ÓwšFšâhëE\ò©®±ËG‘ò¸-þÔÝ<%> 7ƒ$›µäMFH]è„;(ÚW öo•Ylàb¸)º…V÷¨`Ö øÀÒ7,Çk5-ðHÓ]vÉñF•pôåL®£?§bqÊ‹Í4AÎù¥Ì5–Cí¸›Ìéâ!˜(™Ì¦HDAÃýx8vUG‡DÀu;Ü«SŸvô‘`ß)àww=\>ù]íÂ’Óπ òæöðÿ=ÿ¼97.Œa!Nð¹æKÿ§Lƒ=xnWÙ6X—ž¾¢ˆÑo5 Æ8ÞfE e~(ô´>ÌNŽÏª+‚äfí¿Já¥FÇéÛ°öÆqðOò&–5‰ÓÊD-0 …~3¶C؆é`ñµÈv‘ئb"F—NgŽKOà²Å[ÁôîœÔ0nLö£3rã åî&ußw³Ä::¤vDm®{¸!TØ$²éç‡ñü=XÐ-c[s!œ·CçmqÐõÁò\E2qŸ&T¨ áËœdT­:¦;œ xq:Ÿ­AO½8Üãrf þ-†T×ö¾9Ú¡0uN?GÎ?.KÉEÙ©xãH¨ÐeHŽEéOé h¶*/N‡ÓŒ©w¼(7n—çY ø(ÿ)p¯Îƒ7”ˆó?yo@¦æ=ÔÖ_„ 81Z›³eþ±WvŠx²å¨ èh™¢õOxB¶¤5i›à~z4Oè5M†Ûå(ó¢7àŽÿ\¼–gBϽ‹‡2MþçÈvàz%xãµ§7Ag…+U–øˆï§øSí"Œ®©Çqµú˜uË+÷aL­%2W4 ùÊ»èuüDj¡v›$’"ØyëÂ@ükŸY—„.3¡àÝ[’a±]ÊŠmubÏ‘Á²îpRL•¯ýÛüb!Q~q–(ÒæÑ'‰=}ÌèÚbáϸÙ`·÷7q€T(uŒ‡(i Ü ®¥Ÿ…g³ÊAHæÃmɱ4Íã3VÍ(GñÓê47¥‹,{áŽ{„ ’§€G_$Ønš#Óõp•n< Ó]Ÿc¾”!-8ªz?çÃßý\¸k· 3$§¼ Â÷‚0¤ÉöÃj˜Ýz€uÅ6áÏVt*1!k–àõ6;xá8Èt,Bcyì6ɺü¹€R?òþÇšsàÙhu<éc"ü·ÌŠ+Ž’@·^°¿ã)új ‡S¶oˆÎîlV'– ¹‹Þ²¿‹å©Ìy_X*îCÿºße¦Ã#ûYo!š¤ÿ"Í{ò‰Å) ºC1–Íx‰Ý+Þn?ÂÎù Qsk„°ë.¹üu-·”¥+ͽqüui‰ÇHizpœ=z7¶á¯ðyŸ67v fñòn8côQÒeôö¿ðÃÔ6DÏw½,zA*/¼²œçùW‚ÉóSx£~,Ó) ÅçIøc¹ ­¢ ¼ø ¯5$"þž\+Ç€M7û3‹á†Ý4*(\1ÿG"Ï¡ÌE+°·¹ƒ¯‡ZиùSáó;k*ºâ½m·n>hl\Ï¢þ8€¶ÙSüüG‚OÑå,JŠ¢zò"´ÛT‹Êû%øÈ˰Ùu"\í‘§š»‹ðÔ¤à }Á„ûàÒ}ÄãøËö”PýŽCŽDGæ1ì¶änO#§¨p£•Sعýè}¼ÿHÐ’ñúyÞUðµZESò‡ð m0Ès†8Ûǘ,å«cCHX|$ï^fÁIta…!ܨïDÑõdq|,ÄÞÝÄ=H*I) ’3§o4?Áì9!¸Ó¬ÝCé7ùßõnÐÿõdþðä€írÔó6”ÿ8!_(OîÇ5ÜÍθå–,«Mô¢ƒ–nb{½VClŠ.ßl™ ³³Ž+hòÚŽ²±¶ •5&¿»L¯—²[2ZXS ?Œær+~àÅ_õð°ç¼UÖâ¾âr°GëD]2„{/_’)‹òûŸA´óGƒÓ¡ÿž¬&x¦b¿Ð!p,Û -î¢÷· ,rÎŽ¶Í$ß¿ÞÅ+ÓÉNô!ò©É´¿ƒ0vBŒz÷Vû„ÒÝoã°æxúm-õSßÏÞ™0õêtP6íˆÿKçÂ0ó·9–ãôÆÍ|ï‹I¨ðŒ]ö6eÇ7 ÁÒë0د‰âgFaä—«¤ýópìUN¶g(Y;2XU&£å™9 ²áѵ1¤¶bû`Ú;-*‘;þ–Œ¢šÏáyÊ=Ú.1éÌ|é:ŽßÞ‡rõ)OqÁü(%®“®Ã;¾Å§·Ð"§BHí gÞ_…š³¦L}¢9o>:›JeW“uFAFGüÞssý~³GaR¸ûõlXuQŒÎ6¤F‡çpÅŸ Ïê‹BQM@ã³!J+óC# „Îõmpjÿ„þgêÑ‡ã­øˆ¼0øG,N±ß»'ÎO3[VU°Ž,Äg:±¨úòlVv †~õÐÿÆ)!½å˜Ët‘Ýâ¸Ég,3u?\{†Æ»jÀY&s^÷s£§SEñ^è:z|åöûçqÁªøeüóO„{=$ ^'µaAŸ+aäG%ì?†ýËjÞtç+Â¥Êh<ûý ŽÔB ¢+|ŽA÷3ØZcnëht˜/–[[o$«d¢Ô$1 Q¿Â+_àåiWàÕKŽ×+`¯‹> ¾bMç¶Œ†Ïçà «<|“'Íí0x~Lš¾x0ˆ;mÍu 4øI!³y\Çf^c«’ô_ÜxH Àà_<:lßSþfƒB´xШ\]+V¥·A¥ä$Úa ÁG|NÀ±²°ã•?UpÍwxpѪ,ж²‡m˜°]Ä.á‰Ë'øëˆ+ìÕþ4`íâ¸Í£{ÇðWA®(w§›TwáfFÔš݆/¿ºcqÏjB,…h7Y¬à±x½vØ÷¯wf4np<àS¸Lì î0m"^_%øµäÄÆ,ã¢Õ3Y øH~ËÛ•Ï>𞌚' Æa°S\—Þ˜(ˇ À„aOa¨é+¸•Kbì"1yj ƒð•¶9rWHÊÕñߥ/F–뺤[0x¢8Wú“ ]çG³™_¸vÆld¨8“O½å/å¸{Ò9hþÁÝöïãùãÌx‡IîØYkþùÒCo·ð®A7ÑR~Tèòõš¼yom¬y‹=ÑÑüÓÕ><Ô>Ÿ½2‹jMC‹–¬š<›ÊAÇN4Á%3§Ò/#æ€Û j>)½¶B²ô ÖÙò›=Ä ‡JÓUƒö¢ÏŽ£aµëØ÷µðºg(l?±…8¯øÆ·¹÷ñu¼×τ† ôáÁfø¯ou`ÞLH-:HÇ_esVüoÿŸg¯êÖæëâ$‹zÌ?7„Ÿ¹©Àï­ÍƒqŸ5é&‹(ürÝ >l~ ÓÄxGÇCôN¼ wŸ`ÜZ-48(ÅÍS4@ÙNÖl '¦· {i2ØÛî†eÒÓxU~!‰›§ 5Z1 N‘ôF®ž˜È²–céú…ÿ{&üjÆÚ„EPàsn¸iÒÅÞßQO¼ƒf®f®>Ÿñú" o•%¿Táýžÿ»ÏòÅÄf•+üÜ#@—ÚPÁï7qg×=öVt»àÝÞ™\$g.9{- /‘×ñç‹za^í+æ·Z‚úM'\š<°þ?é•F-»¿dü®xP˜T„…7¬ ©h:tµ&Bêc'Õ0.LƒF<º„_y’%¿cÉßžè½Øù_h¡ÁiÒ:ä#ÄüZÖÏ%pºª<5™í _»!wd% Ó®ÆÉ: ðè¿!Ü©n0ý|/lóÀ‘k›aSk®Nš‡."mh°õsmOŒl˜…!ÀBN“sŸSaã=ZûMžŽJ˜Oý™tø$V;G —Êàòÿõ+1£Ã&¦nµü…Ég/ßTþý] «´>y§“aož(ÜÝÞ+¬ÖÀÌŠãP¹á+ž>€_ðv©1› ÓŒø’·âÜÒd:djÓ6çTº7|1ûp ÏéOÏ4½Ãťĩ6ŸÝaÕvÇ@wÏl×!0pëcíÒ¨)¥ š¯‚âìVipšœœ`ÀõíÂÙ»Û6Øué.éÿœúäâòt š:ØþyÀ÷ýüÈda³ôløC*Évzf÷—ÿ|ÊËÜ"z–îž„¶|iáŠ.¬´¿ çäÖŽA"{ýF†ÎlöÄ?Êôè’8²9![Îb·Ã<¦=‘žš«E—þjï0ìÝ:ŒúÌ=Éf6´ãõßò¯€ã2[¥‘.õèúùèÊüL{;DgƒÌö‘,îP>hÅïûеË×ã¹À<ȼUV»¾ –áj¸õ9P™ïFIÒ@»™¼¾Æ€ï k‚ùZ™v’l0_ßån õÍ)àþIo©JpÃËέêTAý¾Læ);œÎퟩßj˜>Êõ&„ãþØÇØ2Wƒ¦gKóé#Nã)©DVõ<èü“£3ŽF¡Gâ4\¸bä\É·ŸzNDìª!»n-×uÞ¬°å—&ΡÉË‚IÜ\wxëb&™èùï1¤•ÌØØTœ"Üm‘efRCÜÖ-Šß~ÆÁP¹Þ“s™­T:Û?®z2Ãj$ÍX8Œ9Öq_%HªÃ7)IL¹3 —•Gl0 §­¢^Žá–Åc°’/\VH¶îƩۚñÝøùPñlj~°ZÊ]þn¹°~î:. öY|g~óÆáùù×I„æjÈ[sõiØ1¢R¿[±Óê¼\KvŽàÉ+AÃ/^2æëV˜Ó¯'C†}¾ õ¯_ÒWÌΤ“½ZT·9?À/WoÚ²ˆ¸JI€¢µá¸ýª+Œ¸ «oiS“˜]¸!ù Ùé}Œ¬\®K[¹CßW0Ÿr{_¾ÁŒÆ×Ø!Œ›b“ePá°Ÿo¢Ì5ê×ñÁë‹ ¬Á”+C.66…Ó§©Kñ»¨|=s›•$Æ‚…@7=¢ÎI{ÿ8B‘O´±"-ÌpãGy¾$¨Nwx;Ÿùo8î˜û½Ÿ]É4ëTúž ¬oŸÃ>Š#n‡B‚Ï0~MÁfØÊRåiOÉó¥Eðc+>¹_òïOà —ýXpr6ûÞ†¸ïª|Þ„Í:ÎõX¦}$æÈî­HÁ«V2¼ýÈR\hrß¿FïnÝÊž%&ã9¹ù8m —_zðÁ|=`Þ*ãwZà—iÀ¼­¸YçP®‘w%E&À˜ô÷ði~*(×›ÀÙNOfPÑ“Ú èUëT8L$éãߺ¸Ï1ÜUè•©çàÒíN³áŠ@bðvþþ€®ÙzEÉ«Óð /ߎþËùýÚU•—ùñÉ69?êU‘Aö{Hs§‚RÈ::”ßvéÞ7è¾ÕžÌ°À¿#4Xl‚Äÿ)s"W“ àÖø$øòáY°ê,¾Üu s+“áɃÑtÚUzù´<ÚþÄ7ˆöT±IK•úë­Ý9Kú×øþ׫™ß°c"EÙØTufòL 4jÅÓ,÷r3•F&r>JfQáXóÓøÉpìÜ%A[ÊnaG'ªâKKð6 ÷¬7Å›kB°äÓ”;拵O@Ù÷—è4ιîÑòh§‹§ËÑ„Ä{ÐPfKíâVëúpïÁbx-røÿ<×ÿX(s”rJÇëS[KÖºs×—”⽡¥ðbÎy¨¿w ÿ%lcñ¡YóZUú_ofXóó$\»½£¼è†ð‡,g¹8ŸîtîEç“ÈÑw¡sޝpÔ[ z1D WI€AÆ<èu’àûÞ{ñòáYhñ¤ü8Ó }¥ãàþ¹<ÞʪØmjO›vÍÇ#Êýo»(LÚu mFòƒ,•Èð+Š!ìiý ¼§Aâ÷݆Á+¢èM­}ðõÚEx©Êþ»×µËGAod-ÌlYK©mãÏwaŠ­í«Ç×*ñ ôØIò«ÍóX’’:-µø •¡¸Ü<gêåbƒÚbÈŸæÌ붦@uÀÕü.3Aó|2]! oßÛ÷·?J/ÞUÃðù÷V2ÚéHYý…æ¾05p1DUª9B&·‘ µxö¨]¼ìX>sê…ÒcLÚ·](‡‰A|wP$>_ÙŠ×7öüŸ?zd”Ì¡e÷¹MÒnÞÝ4šZl…ŠÅwy<ŒVÚ€åžKäÜa_\áû†‡Ít茛’tÉ»èR¨ë÷•â[ɼíŽ=˜(IJ‰ï0ùâY¶ec-¹|/›Ì\#ñTM4ôþO"?­¡ï¶ÖÁ…iÙ˜Ð5–W´ÞÀ¿¾NH®œµÕ|ªm!Õ7jãJw¥ùï‘ _ø'Bæ£køi·=Ÿ2î"]ÿÇ‘¦ìùij5´¦J f{ÝE±eWðÊ‹ ”PÝ #¼Í 0ð ÿÓ²˜ž°è„ôÖO(þü0ö]— Ç¿î¥¿ˆ êä'ñᯠÁèÒIwyº-pá 'UðKq'ûþö¨Gž¶.öããçó@?YÞÚ _{àÇ0CªnX„ê‘J|þÅŸð/à ,š€üº](?Ø<”˾Ká­ßñóî<’]˜OW¼GY±CLNí tŸ^ÍgÅg1Ï_˜ó‹–ü= 3d²ÈE×~½;a$U.*ÆttY,ÌnªÄG¹" ¨Xï zp\W/™;«gÏ¥³OJò²Ù•¸*:cZ§s©I³¨Äsj´*‹?Ó_Ù’z¼^"ÎÓ O€ÔS¾bëiØÙ݇ÏóÀºOŽ:‚¸5B☥ÃÀÙnµÐ*¤Ýcªñö¤¡¸¬!Ž7_ÙFÕ£›ùΊiô«î4¾mMØ(Ó í‡…-ño³§søWÃL/ùð·ô3s[› m© ˆ¡¦’´áT"©îic-óAû‰7\ÿ“ÿN¨ÿ#bnÀ“âtçA ]9„5 Ù82çAöÿÅ&•â/oèr)ë5ldB,Ø8c¤;dr¿H~?ãÎÿe…AñµTJ™ƒj×:ú—,Ç1öÔGe ]_Žþc®£uŸ³} U5Ì©Øc‡>³¨¿Ùà 0šê]žª¤‹+ã›qo¦B_Üb ]Ž´ÄãÖkzc…—½¸Í‡‘~ý¬‘‰YËšas÷Rõc$?´Ä ›…’·§²ùÒdG¡[8°©îšÄÊÊ”ÞÉÿ ;†¨à‡rMn™Ž÷»žÂv¹Ìúïœ|…é‡<Æ+™10þï§þÚ`Ì_Ž£³?’Šºæºv+ž»Ð ²ÏÙv«»`rx;^Ênƒ¸Ç&ÔðÃg"á2–¥I_CSæƒ>Ï€uaxÇ9˜a÷TŒNX‚œ¤éŽqçp­¹Íw´%ÑÔûŽ1ÕËMù?ÿtC?¦zÃ_dÃìŸ(¡|Vo¬&Öûoc“*Gµ€`Á½GI8÷Ñ=p0‘€çŸ–…eš<7ښśýÚú ’ô.bµj V}Sçu÷naRf1|ì×Þ«¯¦°‘~Ç0h…ƒƒYlF¤5w7ÇãjÑóß‘Ën Š…ö™×ø©|By$¦¥`°¿!½g}dʨÖ|:õn·ÎC[tÝåñ8:Àf=4øæQ§z#º4kìXÉ5Z@¶ä/üÍͺ?vBÿ:pVo2÷ˆÐ¤NÎûé WHy'¨ÄŸ£·4Vñ¾´Elä÷`VbÄ’›`àþe2R¸SHÙ=Ó‚& ‹¢E ¡~ø^áï«Î|»á!n•>œ×Ù©¢jŠmzUNLçÞcwÿ©Pg×c¼7èº`ë±vè‰÷}ýµÎÔðÿ’ÓQ¿þ¼ÝÉ÷3^/¿‹n³añ΄§†àÓC *n(˜‘524Çb#ê~ÃÓ¦­GÝf5þü[ \WkÂGŽ X¾>]h)ÞA\DÅ)£èšcKèÕõ³Ø^ þ!ЖvH'd^ øNÖ­¶ðoo}m¶˜Xä Šd_käÃ箪ÃGëWcÞÓNo÷ dk¯‚ÝÃ]œNÌìñWжÀôkä¦&ñõe¥T÷óiæQ 'ºdé…OàB§íõ¼DÝeöò‘Å\9t ?¯ÔЧVÍå3TÂøíXÅúwö°ºìJ@Û­Ÿñ¹èXRN–˜Ä’_?BÅÌT¡ã&hq†; ØÖ¤®:és±èµx(_£¾Æß G³X¤#¶Pù]ª6Ì·3E–á¾ãÛ­KÏA³íXÖå#œÍðȲ:D-çâ ¨rj}—"Orüoi9[>Ûq*Ü¢Í&clÓWF¡“úq´ß­MË&'¡tƒ5¹èCë››päÞ0"§ †írPe⃎ÆÊðøi9±ï8…JG‡Óf|N˜ŠÐÔ)ά¨Bçbs^Ü“†® ½˜©\3¹ì$N×Èüàÿ/çø‚Í ¸×gO÷Ô?!ëܧã×MÌüÚhü;²çÉ]ÂÀØl³E y;Ö/ûÌþûS˜ Í,š Ì®²¥Óã±Cé)ßTî;ô>4 ~ÈtÊq¿_jè9ÿܾ{ÞVù•1·{•‡›ì‚Ûgãry1„ÞkÐ;ìØŸ«Fáû!üä˜Dˆ£ÈüЇdçðxþÁ´Ÿn°¡%+ÓÐvÁ2,NÎÛ~ÉÒ+w‡ÓØr#ºhP-ÄHåCd™> ÿÛ ûz^cÖõ&ØIO©Á羞ɫV<æ»f©ógM‡à”˜'.’*ÁÆß‹òÿp»Ž p‚µUšÁ>»F$ÿèJÇP~Gÿ.ºPæz¿obçuQ,™Ï6\cn{¾A`ãÿ#ê»ã±þÞÿ³²GYÙ!²Q!îs í´$JF{ï=½³CdQTJ2îs’Z¤¡¡44454üzŸÏîûuîû<_×užÏçõz]¯#YÙW KÝ‹@óýKxuì‹oÚz“´yá[ÖÐÚƒSšíÁØ÷i¯%_J¬è×z¾H3S¯MçkæGñºâvhîÐEßÛu‚Ë•ùá³’TB©‘ýÝ„¨òú®­±…Õ?†âw¡"m\g@6fbÂÚ&b35ýü’€‹_>FÛò×B‹‚ýdõEC¨ÔÑGÃC©¤ý“&ŒmçSŽ“1uˆ»ÿ1÷‹Æ•ɰñŒ=?µM,:1§aÂê¹ 8Êél»¤F…šÐqI >ÀK»2Ì}2‚¤?Ó=•¸fVÌþ.G‹çHp«câ|ëZ‘Xx÷æ'ñ¥§À.§<çËðumrUšˆf#§¯ÅõLçÒsl`â§…|ušÑÁˆÐ¼7«ŒO=D[màÜfGþÐ}5¾•îWsÂ1cøoÁƒO8åJ\VËå^¢ÚÐa3‡<׬©xewœ_ À?IÃáç”á8-÷=ÝöÛŽôƒnn·q·èø)Ó»c­qißhþãW±p‹fÒ;=@r„Ï¿I‰fL‹, 2È¿xŠáÎæ<ÿ„>à÷?ø*­Çó5ÏŸ‹y’8ÌódËŸ&Ëfœ'¯_äÒµ5|ÿh1ô®¼H#”Åé¹þþê-ÂR•x÷ÆzcÎÅ(îRO¼oà#^%ÂÌàs䓺Žhüƒ«£*pÇ~r&÷þù©*˜…Êñ;áSüO^5Á»|{”‹ÉÒ™|ìs²FóêyŒ,;B†?í&RX‚f§€í¹7‚„i—q£ÉzÒhq‹í¶N—Ô]ÆÃ¡ ?Š9 ÄõJehG£/šx…ëGÀã`“pöw/èYµ…\:£Ìï¬v保fウø«p5}ñ|(Ù¼¡~®Ü/pŸnÃmBÛˆ ÉGj·‘»MsDËÙgPíÇ ! Ô|W<¬¾χÔTï +F³Mc±Ã,–ݺ(l …scwÿœ%Vú¡XØpC0B`M{—]…IÏÝð»Å‚g¶0Í«¶øÿñ4‡±òï50r„,câÜts ­Öa(’w JFáÙ FüÀÖn¼¼¨ˆ••FPÎ+pï¹Ó êô¶—(¬»†Lûç»aX $íZëü2±ÄKÀv¿b"!3èNwšTƒÄëø­ûËhîã3u& Îü}È ¨‹ÆŒ“Ïá³ôþög(/ȇ„Ö@ŒºõCØW7ž<ö¿)<ë:Ö}CÎóºä÷m¨ ŸB‹ãpØ5‡IF ø¨»äÄYxÅvÑæ¹‡h@†]òÆ™&ßbGk…k‡r‹E£èÎë65ýïþ¯[*çâàhâ‰-ʳ0ïO Êæ† lmèëÓËhà$Boš§ ùùë¨*`‚ýR |äÒ“ø¢Æ§ÝÚSR'â(Xr ¤wG¢ûì(š6©Rfr§—oÈfi ÚvȺï‚KŽJ¨Ózc–Í·‘vTêÑsAWËf˜aý›ˆ¿º†±7€{*ü„¤„\ü® ýVtY¸e¾H)da§§<ÌŽõ‚þIóa­i¢ÐàÕ}HËöÄÖ÷ÍpkÇRµê«¹“C† úþÏ/¨Ð¹‘ÒðÊzÚhÏK2׃ö3þøÂQZ2”p±ù"ù!¿œèm{î¸5ˆný¢KÇ•ê¢uu<¬‘–§‘ã©óÕ÷ègpr\üùËÒapÀt0Sª8[Ž+±5ñûéúûÒ¬ÙN†£v=š|û¨d¢aãŠhÐÎ’(iBçülÏñ%ø¶ÿ ZGòqû@[VNX+F¿X\‡€$Ex~®7(`‘¡ë†®ƒñ#lq»ˆWÛ3š›ˆp4ÚLçý«_ï¡æ:.„¡†ÔñZ w»àÜfUzW­'œci³àMõðêþFjúÕ ÿ‡ ÛÛ F«Ùúö(X(oùßþ/›{ƒ{ï<Ž_‚¦s±áÿjãDÝùܺ÷(.Îþ;²/¡þ¡ûìðäøýÓFÊrC6 VÈÒ5ßÀAkþºŸ‡­“û? gé!˜S º+ŒUïêéæ pÃq:îçj¯ÄIÇÙ«üßûe$&XµNÙn…µ$#¾½â?à3í¿,Õ^‡ë?ÞÊŽ¬Ä29irtŸ≯§ÁT÷Aì?˜ÿÌ‘Ãö.4^«DVÍDȯ5ü½¨ó"xg)ƒüÃëÅ–¼ë…*È=×€@ßœl²*Ô>Ÿ_÷QÜN„ïZR@:çò¾Œ7P9r$Ј  |¸ þYEXÚ-BËæ›àÉ]zõYÊMÁ+:Î2ë£'4‰KÐîÒHhÜ™ç<;„2¥q˜TsQ²ò"dv.%²F´+ÏLÐ/½FèòöYw~ÍŒ8ÿ9€ßꃖ†!pGCœîZ“Nãö…Âöaà*€Z‡µ¹MÅP.ŸÚB¦~o€'cÁÌ»Ûø‡ü­5'Æ÷³:0Oë¾»ž—>Êg;ï¾@é }Nk­!ßõ!„ #Ô¡¯·}†¶ðÈHB–éjH×$næÅƒ,‰Bü^6|Ým¸·ÞüÒ¦Ó ySÚ 1×—ub\_$©© …­™Y@fÎóf›§›Rõí†ÿYüqH-H/pÃjo7|Ø.ÏׄT`ÃÇuÁÛT"®tQ $‚ô­?Œ+d³B¦£ô²R<9m|ØjPsôÅ+ô:ˆo\µ>µCïiS~ô¼1¯Ã@Þõ1oEÜÇÖ‹Í$ L‹ÿÌ(÷ÕË`Ò3ž¥ùMø8I%›{Á²½•}]O’·ˆ“³zÇâï0lˆm…À îÑuìöƒØô‘tÏÞ&ýSfšú`Û§0®ã[MpÓöLù5 Ÿz$79€Ë·`\ٖƪZöÑ“5 0±U‚~øÛÛËðÆGSA}ïí{„Ò±'!WOB°¨7ŸŸŠz5yìóÌPcWðk~1¹j¨Äû‚çáuÝ"ô]%Â\¶]fÇ>c‹4=áÍ´Ë$]þ+ž°†á_NÀæ_â¼óÃr(ð¿ ®{_Ã~µ$X¶ð0¸Ýu¦¶œ¤ï·ÁÒ 4ÂÜ$xPð6šû‚_`Ñ“/0¶A ƒGŒÞÜþ_+&›¬íªŒL{ ¢KWšTlS9Œ{Ê2`ôõ0›z–ôêÿŽòRÅ‚¦§oá±ñ:èû 5û/²>û!ÓRŽ»;†,ñÎU´IÙß X”eÇâëÁr|OM1ykZèä zÖŽ\ýcËmãcáÜ=iþwÂuTxûŽ4ùŽÁeSሶÙ4å´/1xìÓf›5“è!L’ $«'‘ ӳᑵ<4}? ÉéUøys5 yÝEÜ{5kæÚ«ÃPû)xs¥.ßw²‹Œ¶ú€&Î8hù(zýŽÏIß„qƒÊñ]‚†¹û ò¨=ƒ91ôP©:- G/—ù{MK>¦¡ ¬ÜºÀäÆ&XîFê&—Þ†õÅð¼îìÖúBâ*ì¸YJ7YT‹¸±åïvÀÛtKª´^——\Ê'l÷0Šá ôõÚIxd³·3‚/_ŽÀÜ‹—ðû³§D!ªS0æÂ6t®ûO Y ÞêÏM¦<#¦ëØØ¤Ù4F¶˜©oTãS÷¿a¥ûüpAç@þN7Àµ›˜¦™·*Š!éðTø§û$ž=!F¯2L-C‰räÇïá¿×0Ä'5kÓž?ûÉ‚mQX­úäïÙ€XŸ®w9çr“aθ$ôšp eÅNCÂ’dÓÉñÐu*gˆ.dcÀÓKélÞËÁìíd«åç ®o<‚Sl— ä¿n@*ä§Zƒ®Y>~ý$ «®Eç’3вgÖK¦ki³GÙ` <…ïëF·Ô[ÒîGá ž,õ›Éš ŸÃ¡Îݨx¢Û¹ù;â™SM:—ê¬-áqw)õoúƒ×ÂæÞ—økËAÖ‘£Ì¢s¯£²¿ ú«»Ð¾•­Àzra¹¡7õp(Õ‘]W;Yk,%õæúôD­,¤­Oo½;ÿqÑ ØöQŠ.]ÚÀޤJ󩜓×*X É f1f }o; ?wø¥ÝÕÞýÉäl­ Ö5KñC# Á#­¾Í¯Èÿ5Í­,ûòúëlë[)gò|ìî {=ÎÐð·ôù­&áΫpÍjþ¼‚ÂßE//÷¬ÇÄl>áÇy¦yã{ßýŽÜŒöËb¡‡ú¡m×P¾FÄ…H\ŸˆÅDéúÃñAX"ÞzyT+>ÀûÌ/x¬Q–,rÍEìçâØ{Òü8‰NeÒP¡E‡½¹šõ¨Jû}¬íMe³Íî‚­Þv~Õ²V Qcš³~<"ô$à¹î5Ü–î/}ý@ÃzJú^ÄcIL8qlQk³‚aÙj¼þÂ*ž(1”ÞÓ‚ñv²ÜFaÄ€þ‘¾Â˜p œ"µ =³Ìñï¢)0*¨ Ÿ¬ÞËN*áÑW0ùMs%…1ŸF`·* 9ð'|Ë!B;…äe2K^§Mß´ùåç;Á<_¾©šò…» `ŠA¦“­V,þ–°¢ßÆysfùºÔ%X§D;ëHäƒXýàCxnÚ8V“†>4gåÛ•ôr¸Ju%9°[ˆwŸa—V=Á¿æò« HXã4ü%çAR+`¡¶8LXš…7¬øÙµkáÞ]38&õºj_ þûŽÔWûÔÚM·`¼¥À«mˆ0ÐMJIe ì´Aò$ˆm ##ICÍo¬$7 |â28V”¿X¡Mó/PYáÈöO'i²Qÿ9Å ïb[L?\ЇÏO¤âoÀL±ó‚ª|º3çNøþŽEÆ*`æÙƒèw^†ºuŸ ž­ RçÚȇXú4„©‰Ý¸5»^ð.άÉðìc{ÐÞ}ã»VCµç<ºN+Žfn‹€ôõ™ø}èkò¶r0¬Srå^·C˜ý‡èüùïܶ:=Þ¦H 7Eà¿yä.v’!.¥!2ÑÂ.ÄgÄ$¾­¯UsWÁï Õø(dÊÿ5]È×Q,n7ÄÌÙöÜ®]iäƒõõðµ‹oªfXóñ#©®J+VË®F^²½¬\!cÓ%|ŸîM-•Ó¡yÏršs-‘Ò‡_‚?àÝ}RüDh Nvãw¿ Q-¯H}ºk+\9D} lˆUÖW"òùì;¡Åƒ~¸QÛ²aÜPK†úÌñ^ÿ®Í+bQà–æ/Æ¢gb‡vÞž°L¸î@ÎsXOç¯'a˜ šS³ hm/[MÝáÎï[pÍûHw®!J-¬œÇ "HÖ†$œ±+<¿‘ÊL–d ¬?>¥´çâ¦[›`’ë°¼¸Óô…lÏkXe¼Ên¢qÆÙT*JN´–`Õ6q’˜‘]™w wß-P)›Ì‡W˜òÛ'5øòsÙô´W,jÜ'w&p—¦;÷Ä’k·N’ñ½uBë°)TøÖ’VNžÀsœêYg¼+×Ü@3\Ôø-…sp´Be³ÍØ‹7gÙ:WZ°` õœ’ ’!Ê<ìíd0JœHýïER“Ð7ÿj^˜sÚ 5&óWˬÈû&#nŸ‡nQ9+üÕ©c ¹ÿb4žUÅA ÑªË f_^ Ook ¬­›ªpèÞ}ˆQ #iÎþHŽ–ééfôMÆcòÆ"Ž·­~ˆNGxxi>zñæ–‰'#Ú;ØÔŒ4<ìîI'“³Ðߺ—¼ Â•ÛæPÉÌðr—_¼0?döO·ÿÁÛ(ĉӮ@‘ì?M°RåÛ[ÑYu6Ý“øCø%#,õèßÞAüƒC-|‹þENF³3kø¼ øí€Nör¡u[wü.$çæIÑ㟰æÎbv¿% †™÷ƒýêÍ\b¤1OÂ4§ó—ö—@þl#<1ÄëÁ‰—ÅÐÌÓK+àá¼úw±ÕS ÕWɉþ\K½€HMR¦z¶*è&ƒxHªÃþ$S‹õ†°pQ©V§v‘·ILàÃÄdœ©¥J­v•à†kƒ±ÉãHº†Ðž2®Í :‡À`fú0Ö÷Eàå¥ SÛ—€ÕÒVГ¾®"\oŒm«KäÏÞÙÓo&ɆˆgØ¿ÙBåÜym³-?0,ŠÖγâ¿veasϺýI&IX¯ÅÅÎKðëYxß]™ä¬çKÖ .¾KÛÛQÓ `Åöz”A$˳ñÛRCªP_û„™›Ë†ü3°þ«ê×ᣋqPû¬ŽË?†éA»ðQ±ö&Œ¤Ü#š—›}&抡09MšÛ¥ïpWIú :ú–Xp+žßÑJ®ßNæ¸"]/äâ+é\$†ÌýÁwÒZcÇí‡ÎÁwa×Ù×Ú³|;„?_ŸÄ{š”ùoz ŽMŒEeiôØC£×Eò—r•»EPš¬ ?E#°Þð0îÜŽ2Î;….Y‰è#öœßޔ̖fU2ÃV=ú¤Fƒ+ŠäðæO©›jËTç—­ó›‹BùžÍ‹YÀÏú¦Ø˜,¾Ú¡7¶¥áù‘|ºÑÿúß÷-¦eó.3ŒŽyeK^@Ȉi\¾<ƒ~(ó¥3ó n¾$—”ã²%árÃ{xâ)o./À×;Jà쯑ÌáÉëÊD?mx¼V _ñ—¤K¤â}HϧBú`YR²ë#V¶µƒuÛ;|ô0ëQÀµÌ@ŸÞ„¿_âØxÃZ$Ba% ŸLG(án´<: «j/à•Åóhr¦Éù 2Ìö ž#š˜É úebQv 7g?ŸO!ï¬^?_n þ¢q¨qµ›XëI‚<Ý€fñ¹r=„½Üô¿ýÿçýóÉŸ˜JËe4¨ÐÂs¦:ðH®“|{áCVïüæϸClÛ…°B,¬ï݃%`gTªÈާž*kÙc¥B|üÆ ½¬§ >Nygˆ‘½;εÉ%Ui .ä•qÑDt¨,UåËè‡ZÜ8{.××ÉÁmç á«éM\å|CPeƶ,<6%Øô)W,U„ŸH¼9ß?<|$3ˆ<‘üçO)Óœ9?ð8ßwÖÀù…kpŒ~® ô„8]e““5”éåËÑ×’ŒnþùëQ|‰NX•èPµVOÜDÏwÙrí~JcN¦B¹ Ð{-Î_~ZE\³u¨ÏíÏpó¶-µó9‰b´Óq Uö]Âyo nŸ§I¯ëHЫ:ã0LT–V›Ð÷¾/!ñºÄot¥öŽÁh³¿Øwq­KÅ5í Sf6“^¹ cë`žaæIêòõ}“ñuÝyÈ^aIGƒñîa40!†z&ÆÑü=V¤`ç<˜÷ð[¹õ4L¨~ê­Â Ùúÿ{þ·à4,-S‚ _"HuÈ-¢íAçÖágÛOÂûÕ]pÙõ=Ì4šˆOm‚ØÑlžw›å:©Óœ‘ŽØsÿä>¸]z TÉ0yÏ¢¦-‡×o!`½1, Àó¼¶L¿øX¥eñ<þ“#=ˆ?§§Éƶ¿Ü`޳/.€g‡rØ%oôúªçŠÏBäQž¾ø[õЕéìÙHÝ6äö’Tå -7ö^…§½æãÖ--¸bêyØ¿þ5t= ƒcÝÑ ± ç(™¢hÞF¡àÁ4¨™1üNuáY#ó¦Í@½Ã³ðRú›ü»¯,ƒ/-´æˆØ/<. =½³µ-ôÃáâ¤4¶*[Œ?ð¶…õè&?Ž5> ,6 ﻌ¡µjX·n.2½B.4¯„XçëBóBð [ÏöM~ÆzéÚ¾}$«¸'ÁÕT ½H7’¦M.F<â÷*<µ*?n{FþZ«p·ÙÎèï±—ÍËrÃí#©ËBvÍò>©lÇ*Šâ]Å“ ½{:¿¥+Ê4Ûp«¯—Xï¦Gà97àR…ÀýØ6ˆ2¾ ‡ö¹BS›J™Ebû4ºJç"¦ Bƒ[îÌûŸîôâ™Z ø}¤hÞ®`’¤=„®~?‘Þ>6ÅIø¿œ>(ΣðÐñ tb?o^¸”, Å!&YB›ï‡ˆlþ^]*¸]´¢w5ÁkÛð{Û9ôÙ»ºðËŸ}èúÞa¤È*wë8 «ÞŒË-@ùécœuö+JÞ ý?'‘'Ê3Áe‰!Ø—©òý¾Ÿ°ìµ/ |–Ä ÆʼnSñ˜Ž)Õr„)•Ÿ¥Uzn|‹º7V?†Ÿ¬|G–*Áì¶-ô¢†$Fûâ‘·n¬ÿ†sþim_7…F˜­„¼é­Âªó©×MÚ¹ }eFÓå{Êð÷áø@:„<j"ÉoŸ _uf:y´" ¤©Ñ#ÎÁÔW©zRêy¿ßIž"ÄÜ\9 ¯ºJf1$#µW…LϾ&¼õTœ 1TÔˆzÝ ÝßšÜÝð,N”l _J¢±¼¨¦;¿‡(bïlÁ%7f1/¥{lDzBœxJ‚_x æ¿ n3|Ï#êqºyé+þ€Áj@ÞÈoŇ œ8z›2s5@ð„ ¨šõmjÔ0b£-ÿWõrÿ¥oqCrú7¶CoÝ ”WHAó?cAG>¸ß~H?Wú}ØâŽ— lÀž˜æ£YV0K èÖο¬ýÝSâ¼øÛôx=qÞŠjÖC°Ts<ôô¦JçŸîêôë¤}ã¡£ßàüßú¨¥Þ˜§ýÏàú½h½¶/†|f!þ?±®Ò7uäNžždžï9°î\ŠC\aÓÿÈLÔ:¹‚«›@ÿ‡,×…ü‰ù(l½ºû6¾Ä]cOI³zèL¿éhrù8w>eý{PžK/˜Î[ã˸þ‡`È3-… ʸ›ÄZ^ju“ÕŒôÀãßäy¿¶2vl47Uó€ø¯aì…ŸgÊ0xÌð©Á–·…+džñczPcéD~éíD.,ŠÛ6Æã:—œe°@ 8;›_šŒ‹§–óêTCzuì)B²OaÃÔEõßEå͘t^‘?Èæ%VŽtxg}Áæ®7¥©òÃØ„œ äH6}gÃy×óCïph[‘*ø¦*ÉIà *¼uWOº%ºú:u¾§Cÿœ–çF›(„T½ÿ—Vxèý4ªî¢ƒ³îƒòUYÜh×.”ˆÄˆÉÚ<ÐI™ú´•“¸yÎ4ôô2HÙt•—ç®À©´ÔxÝ5´œÌ~˜BT<…tH¬!OÊEC÷H§{œØ¦ÀÂ]AüÕîýüsÖD¾Ü8u–ŒçåÏt¨¿ÌZ˜.Ø!húzÝߢ¦YÔŸýǧ'nóV83ÿMe'˜HX t#³‘|‹öœCÅ;ÁP•%C7g*ðÚk’¼ówzŽ £­¦ós´î²äã/ªÌy÷žftY¤Hw<ÇïT€÷ƒl¶þ[žàìúÌø.¸Ñ$dÔVnqm;6”^#kZ#`ëÃ<ÖÑ ¥ÑÅì’ätª”»,6HÐ=o%iõ7w¡þÑ£˜áåK’»poÄ-¨º1Ž/½só §=w¿!Íï|Ù pþË´™Hž9‹#C8ÎÙ8_ðxp ¹'Ç¿ý‘пÎ\k¸§p$“äñ×çZð[ŒÞ¡­ÝZ :°€wœ>R5CyÙ¯A°xÏSP °á›åBiΠ¡ÇŠ+¼Âm­è…ŽhØsE—nMõ‚úä.`e³©Ræ:ìO~É,† Ãóá§xˆû}XûÙ‰ž~Ú€ïó½ø®ð,Ø<ⓆIs–ð²HQt!ÏÀݯL”ˆ‹”-–SÍ1ðöÇT˜ùé:\z—™$§¢No ŒvÎÆÍÕhþ¶^4˜o)õ%pÚÌ £`½c®ÜæÅû>|`*³Üí|+äNâøàzhÍÿÇ_sAžÜ[ dóhN0ËΗ£/.k‘¨®ÔÚ&l¯€W%¸iè\{{š8ƒ;¨Á*ÿrð×zš6o8Õ]%Á_ù†9“vP•È©”¼YM×F¿…ë_Àòýð=y±°þo üq¾Ýñ÷¢û¸aTµ“Wà âÓÆi«Á]"…+®VYjÛqìÛwê‡õànIª*æAoŒ0cO¦7÷y{y¨~· Þ#ÛÔ±ñ&ÁtgÎ|úŠÐùŸqÿ,Ê÷TÙТC¡9©ØÃg貎T±ºì'pÑGT´¯Èÿ¯ýR4òý[VŸðŒÞ¿MâšjlEp&z‡,;!Tïüƒþž ™=¦Pú;‰Ï­D~¿kã‹“Í„‹Åá¿Þf”ìà—E8´?Æ•'‰ýá¡É¡Ûàz5…NøQÃî£bO`ÉÝãT×ô™`tñf}*›¾µ™Kz§-¤y‚ ´Ø‰¯|_¯Û`ãê´/"úËxÇ’*–ñ+~SiºXTƒK™CëéùlaÔ{ÿRÏäém‘‡dôu®šž§»Oò3üð¢Ñ–…FÛ=&Á¬*¬N¹0°ÿ¹^j.ÑÚŸˆo¶Ð¡¯¯“¡ÜøÅL8û¶ŸÀûDüÞä kóHá;<~|*î¸Ú Þ×:±Ù±îßšœG[FßÇÙ;ø·1«yÉÈlL™'F½;]ðÇ©°0à©; îÂú”ã :~• ©a¹×YÑ cÒ²?TB(w_³>]6 ›0èý2Œÿñ ¡Ÿó›Øw= ¸hŠ•ÒåÜúó×#oU—kFœ½M˜w#J°Æ¯ÃÏkBÝ© øìç<œê˜:ꘜØÃM¾©s¶-€z§)QSþ€ìjéc_ýkT9Œo÷˜¢‘äC\^ˆä\(}:9–aáfê>ÃMc¥!ïO:*9%þ~s>` kzmHRNFÀ‡ëwˆñ™¥‚)Í'Èͤæ(»–ÝP@¼øÜ– òò˜a‚º\K긄ØäŒ=³±ó“þz8Ï*ꋊ L&A-ßhñh¡_èѹmàküŽ,f¤ðK9¸<`^n4˜×ß„eF†|Ý’ èëmÂÃñÔ·0?éŒ\ÐBê/8r‰ËfDëc)öÂãÏâè6I›Ï=°ƒ_Xq½æäÓŒbïþ»¦+ŠÒö¾dòÈbŒª ¤x]ÐØtg¼³ Ç% ÔØ2ŸÉìDÐQs/–xé×ÀG_=¬U5á/Ú=™Å¼WìÁñÕèsI*Ï@ã¸KÐ8ü.koP„“†õ8ç§á êœ µr#ø!|3†;$ëKá›ñl§ÞDLlÿªæv¸^í(,¸½ƒbù[ow\ÿð.gb2—°gÍ.ܘ0–«•3ïàpl÷ð~ðRàkNáTÌíßÌhâ!OÞºó-x5ãºS¾Xôú%;½ãiщÅe»ÑKæþÀõoýF%öRÙiFH0™à‰«U‹ð–éc,H“GÉåÄzzœêOí›6ÁÎGŽ8õä\jò[Œ_ÓMïšVAØÍõÌj¸$›ü}.ÖŽËèÚ/¸±%‹ë´±.}>V”û_kx­ýø¼°mD=ù®s€t<˃óG*@¾jº“Ùô¿þfC¼ÖI›ˆoÁ³O«àØùPö8W™ŸîqÀI«w eE­p÷Z5x å¿ãvâŽGWÐÝ~Ï @iEþÓ5Ž­Š_cK Ũ˜è+(›Û‚¦ nDvÔrW8„ë—L… Êþ'ßg3ÑÁ×I¸Ø¥s•‚¶eÅ,Ö3vžŸÆ¯´#¯8FžËO:JZTèpuøyPŒÖ{é“s'SØÖ]î2f9O›_J½“NçÑöo ®‘„Œ’柣 ézM®äªÉ-¶;Õ¤ÄÃÉb´ÿtÏ\0„O^óœ/¾cn‡„¬yÂV¾Quµ?„Ž8>WÚ áゥ̷­l¢Å5”M0b¥w¹°bÔ]œi¤LU‡Bõºt²ßu ^ÚÈâµß‘ÔIo5ÜåÉ †½Ä%çmp‰w¥¾ÃüyÜ÷}Êvum$.ÏôàÕNúƵ¯3¨lÂ]6#_·RŽٗÉÍI`ä©MW®z [=aæwK>I¢D7g`Ñj/(ß0¶ál?;ž=Ö3 Oà}¹·P¤5 Ý×ñ…Ãø§+ øf×\~Omç?3ð>aˆ½"9ò&œm‹ùË:&¥vWN´K‡£¦òô•ïq^Ô|ˆ9.þP¶æ6—¸òұψۯ(¬Z{m™nz“nø¿¼-k™·G!ù£W‰fŽÃ¸Aþ ï·Ïš÷)óZµ4|Ñz ·,?…Z¶0C¥ì?Îדq>CißHy>*ú ªo²å«ö]yëE`f±9ªˆlg3b8‘«ŠïÚ¿³““Æ ËDþ ?l×Äåß&óÝwÉùZ˜¾‘u^õäÇ_•ã¾ÁÆX”¢H­öM#Òý²¼äúÜ·c&î:úƒSIà‰<>h?ì¬1„®GáçíR˜°r3‹VY 61Ø7gÞˆš"´5pàÏ’$i¤é8tå<ž¦r¾©¤z%ás¿Žb«_¿À//ë,8ôt0†m4ãŸÓ×ñ×_õùç/*ôüý…ha8uýUp¨SâuÒ*ü˜¡¾_¾›ôgA¹ëÜ÷k=>ôë+ɑ“ì`©ª}ÍZ—ˆIå‘pEÞÁ½NêÛŸCà«~­Åg˜x ¦…â0‹ 8¥¦¥=ªzÜ–‡mŸo±Võó$³¨? ·ÂúQJòÚ9ûޝ;ñé5iøù°ºæ‚­!žûúí¯“ ü_Ÿ– Å\¼ÐcÇxÓY‡.—~à ³îÎÿõ[;-•à_^¤Û=B·pÈ©Ö_SôO\ï ©pV‡N' 0ó½ e;“°óºø¯Ï%oGâÄ{v4Ôw ÞzÄÕß"’ß_ãéÉóñœÜq˜X&ÔjoBÕ©3àÞ¯7Â9[Ð8¢ äÖÃÝ—kL¦ZC×_2ìZI_Ò ç‚›Éà­ýNö¶‘` —Ù>äy6º5‡r+Yv±w/š‚›2ÇÀù ïÈ4©Ó°‚x‚¿k9›œB0c÷>n’ÿ‰}º6Êsáñ·`2L×Ïóa+!-£¹g:«iüÀwv€]Êá71Mð²Äâ±}Ð0ªf)kðáó øß¡G>šÚ`â³Cp\?†4ŽÂɧ.C­¥s£ ©*·FߨÞÏçŠè qdi^N™ Z» ¥Dœ•N¢®‹Ýù–¥&´e­ëüæIŠ´d)”5ê`¼ ‚´ß>E~ĹÐÓr´kÚ|Vø:þ zÁæ?1€axMTã1+w?ð,Nÿ±¢õÛð[h51lô†Ò ia ƒøY%T(ª o—  S2TVÌ ù%fò=ÿ0ÌK:Ž‹îâ];¨Þ’ÉN^øŽºOÏ€§ã)HX[³4¾˜äú_püiÀÿÖ™z"½dÂÂC1¹1Wú%ãs©pÖ(N®ØðÖaÞôBÃÖ8MÔ̾ˆvü«í¼«7¢Óy˜9ϛ쩉‚Dï8ø:F¯™5‘$tÇÊÀ¿àù4‹d¨ýΙüN»ñÜÜ_fˆ»/Ï#†Í!¬ÖæÀÿõF¿iÒ Íó›àþK]ºÇ~6w|o!™›è” Ü<äÚðBhkÂ4ŒëÙô囄iÄxü'æ?ÛˆnÝ»no€œàÐÚ$Tö¬g™¡F@æœÇ5 6}1¹o¤O-ìTùÉ[’´-tnÕQýÿñVÅñ·«ÑBø¼®Ì©EωmŸwH´*ñ¥yž|¶víIÆè×#É´Äù8vÕpœ8B…^”¥{K}ßãê'Y¸¤Ï§G+âîÖI,¾¦ž*x¦jZu±QQÓ …™“/Tá¨à1˜6z’ðÒ¡BÁè ±pMR.þ>ý* ;K’p»qîvÎÚI'9—²Yé Óµrnþ¸Rgbå]=x«×ÅçÃÐrŠ mßV. o+àÚê ¸\WÀ‡{¹ãg«(T/s «.¥ã6Ýí˜í]Òm•°%åò@þ[Œ%:צ"¤¾‡n'y”5»G’*6¡ñ#[\÷¡$%ꉤ¶’Ðê #§^ÿ‚²¡úÐ÷ì0›¾cªÌœ;dŠép—'—Øó¡Ù ³=VE<‘ÉbÔ‘ ›>¨ñó}ÑY8|ÂqZ¨<Ù‘¸‡I êÏ%=ìñ˜y1“~v‡¬ºQ0´wÿš­ÈÇe«Ã»?5NÿÍeæge¼·æ3H@ݦHç-J#n×IÇ 7nY½„ZpCe¢¢XT]Ö qÃco<ûÌŠNT¸E2­FAæ‚ðeY-NÕþ_ÿ+¬ˆÃÞh¬ºr†tÍ”»'˜ýxnº!Îoö…cÆ#5¸´Û.Ò>Ü>l&ºŽ£ÔÀ¹è\=M~/U¡A!ÆèxZ†Œ*ó#omÏ“€]¹øü»"n™n‰;.»¢‰g(ÞÈë#ÛíãÐY=ÄøÝs©aDvÚ"ï­L¨Ýü#˜ûS ÇO³eôpH~>Êì¼Mù_÷A¼ãaÊt|$g_個HôˆÌÃåýòü=TùÛ¡kf ³TY‡¡:ïØÖñ Ñ)Ë»òdhK^ / FíàÙêä}q >›6œm¸ÿ³hXñ[¾@(H`tMQxr žômÇôÓcxY^%Vl'“‡©ðª+ƒøË?WþñÊX~Éï%èš?E—kÐYÓ;ºÏ€þd-®%CL*^·l%(«7\‹çO·íøœ Í“EpD»üÞ·‚›éüGã‰TI]œJºcêtqjŸvÓ VƒjäN"câ‰÷Ò‰Jn"øå ¿ˆâˆž+ò¼ $kïÙ¤Ãg¹$ƒ+øÑ1 Mìò |j1 f-ò'%CõQvNН.Á뿊pÊhaE•æå¥vËc¼Ç½­-ø»­î íîÈf‹B¹Q¶?ù A¶ŒwÄiÛ5‰ËÇc¸¡Õ äÿ4Àí¼L’NˆnðœK…p~§vMõÐJ¨ö˜ØTCÿ¨÷¬D'êVêžèF§G µ¬qÔy&7( F]\žßœA({ä÷«Ð–è[øår‡ ¾9·fÅþÞÓšDrFÄ ®>Ád˜Ç–+€Â$-¼™ˆ'-ÅÐïÏPÌ:šÅ¦Í,4 ¾}ºß`„Å0¾;Ë™ÿÎ×…¯óLaî‡](ÞjÄGæKÑåN"¨oöƒÄgâ#EyV,&3ÿ·ÇNÁ¢ ø6,»v Â'†¶¤fVå)AÏn™i ²(–xöÊ$â¦ì­ÙÞI•Õ¬;ôðµù\7J:r”avÇNøü»Ÿ¸G gä°ó·æ`AæaÌù°¾8gAÙxÎ[˜`Äì:Â#3@}û¿7³Ïàó­ÑB$†¾9xºÿ»×`^® Ïxÿ"äý÷Ùh©Pr´9SüÂ,ë¿X•Öƒëbf£ÆÎÉX3£R†÷aBÉì-yMŽE;ã¶£DúÍ8ÁùéÆ4Ù'µfköy\­û¿ÿÿ{lª.ù÷ƒ³0Ü\s™LH…!QöL"# †Ë$K‘ãõÇÿ;$Üv W¯º€–o-jU¡bãO§ßÍd­n˜Àáêp^äK©\e›!†Ü˜[ж!¿QAË–ÖŽ´„»õ¸\nKs¥§7­ŸÙEhùZŽç±È?ßÝË‚'žALtß?î7§ÒßÍáÕè&Üq0g{»:µ´Ûc~8·íZjžÁ‚[æ°Rºk4K±Er,÷L YZ6üà0O¾ u?Ø®Û̆_òÃ1u¦ÔâN®ÊÓ£³ŸØþ7×ý;ÜþÖšPèƒø-î0‚à,.}YI–THðþëê¨åL‰ò¡r(ö›‚ v¤Š[AÁ@Õ~–àÁý+ÓL –SŒùϰ( þaC/ïé'›Ê«¡ÓOžv©‡’Š¡ÁÒù\6áŒx´œˆ\UA‡­½¨pɈ¾Ôª†-}— x6S¯ÉÌ…UQ—$(q¯ª%pçÍ%LÔÄi—ÆñÉcLPlš#䩇oCqäüxªv ¶BèÁ6¢ÚŠ—{-ÌÜަ×ܱ±¤ Êå2w/ÿ&K¿¨À±ó[@º‚Ò•Ãòÿí÷ßlEæ Aù’ðBœ`˜æÐ<õ ‡”­«m6‚ÔŸ 0§àc2ô—×]X¹y¾¨€Ò=ºgjhîþ„»×,`¯æ^B×è‡XtNï8Ž'3ô)9Ñ@&‹jãICüa4 3üZ±Éxº®Ÿ½¼³­bÑööjœÿæL2|Ý~½¨´k¼ºw—ûÚÁ¢B3º¹Î TæÊáúñ“ùž³~d÷b;- > sb;æYÐ!ƒ·‘%g~3¯Á£Hþ«P\åV†mó Ì¡×D¦àµ}ÅìžéWv!u…K§¡Þ¦… •þ_2Áeö(R3?Â’¢°»è^ùµBhùÓ»ô«É¢¥ j×0®”vòmz`þø~¨ôïf ƒmhLgfúMçw®jl¸lw,¼Ø™JL&uÁú?V|ë3¸s™ ¯s_@¿~¡Cþ¼†¡Ïå©èfCœb#Ê5ÎPC¹yd¤Õ\ˆ—Íšw`oæ =‡ÐùÝC@KG½ïÇAÒoAË&KLŸ– â—•èÑwvÌjŸ$ε àÚ|ëY©ŠÐÑD¦ýIûµxfAŸ¯°禌€u5mç Œ»EdpdÉ7ÂÛWâç‹*ù°ì èsË£Ðã‰(oÞuTà²LJš¡ôevÀHšGg$ ¬ø„£¿¾ m£úpŽ` L~“N÷ÇÁžÅ É¡Šð •èêEÊÜå’9ÿYßgKc$È~ZÆS6‘ѱv_ñO‘&þV™ÂÛi\6»ˆ¬=p³Öû£ÒgXÛ UÛ‚Q÷^ .Äa|¼—5†|þŠÝ&B×°9ü†ì þÂ-›Y9)s¥¦ýÜ$a¦¬ÆK¥ÇÐÜDLsˆEbbp¨É+<þD’ÿ”é„ɺ“É£¾sðî3Á¥‚ßðK.j ÿíÃ(á ï_3ÛåšÔ®)ÌŠNaÞE5ÛÒ ÿí0Fe_¶¤-»ož©xÈ$Ž­À‰OÙ¹?e°A›Ó[øtHcƒ·ñWÒüáY ¸öV‰”kgZ6ZØq” |wÞ/\ã÷nªˆÑý:’¸¦£Ã„³ààRŠÎ×[4bªj ñš8Œën!fò¼OSùmáb¼pßšîí’â§ï€Î²/¬}ÎTì_е‹|Ýv(Y­^ã—@±=üŸö„Tâ ];Êå2Ù:Í=äÒÆÈÈ º÷v#þM­‡¡ ¼¬ÛŽINjÅO^ÃÈov¦ªM¯aæL}n + ¾#_Àˆ¥ô 5­#ŸàÔž$ ¨.'¢Jšø°÷,M½‡±ÎÓÉ §^lÚß§Fb+cù˜”6«Ͷ^œ2ÿÓ5"xDt‹–ä)a¿Qñ\;ùJئ®‹øÕv:.Õ»ER4Þ`ÞJÛKà¬á,ôt‚ó==xèB}Ј˜H't ¦¿ìgÓ­]–0;y&НÅÚkañ¸u÷ÇéôöÖm”¼‘Ã/ÇÀb+‰iÄñމ¤!:K4raÝÍC´h®0ï¼x*”i“¢ƒ×{ aLxT˦±çG+Ò°7Qêë±Ñ§íeÓË3¤iÿÈV¶hÜ[¬› ÒRüšÖTžÝ\Ä£¿œÅt›0^ÑŽ?ŸÍ¯ìÇXìðš«éðX—ü¹W>÷µÁ(ð|µR“é¹'+ù¬qÚ¸¨ -$rYÿ“– ø+“(†js=“Уx‚|úZ„Ç€ZÕlÒUáܶ¢HbŒ/ZAEdÃCÃv¢`‹ë áæ"4º,ÏFjEþr VN£+‚r1&£;¦Ñ%ñ¥üâÇ7,8ë*¹ôϬÅÿ>WðnÅJÞ×¥Îã—DÐ=)wä*óWO±ÎYEŒV ei•ÁÜûÌIq #šúciоDlP¶à”JsëC˨ù‡ÖëϺc`ìw5Ç7ÅǹbðNºI2f€ÿ*O ’´Ô­EÈ[;šÇÌ_Á›´ùÍJe_€ù¥™Ì¼Á â–£yÉ9ÜÖ©i1kɲèŽÒH˜¥§JÏË×’ ø¯ZìO!œvþƒQÙk!i|1ºY(ì±ÒãÃçNŒŸQeÏkNŒñ«´éްtøþ6‘x•ï…PICÐýžÉÊ?œX¹–m€{ô SH%Ù²å 7á†PΛchw q’…«? XðŠáñ¿µt!†ùÌ" ÂÒÙ1í`¼p}$X?×åÇŸªRÍ¿Ü÷c,KJšs !ï|+„ö‘ÀŽäx›:È®vco_ßMEàªû¯¶ŒÇ³[{À¯*ÜòðúrQŽó¦³ûöàÉQRt­Üa¬<2ŽÇ>å bþY±ã‡q‡7ä‹~"(ÀÚ?v-û턣йã,PñGDO?H ‘ŽˆSšÞ°ÙÇãhêÛÁ¼€AñF\xMË¿ßEé@Ƚ“Œ÷ç³Ù-¡¸)äœ@÷Ý^v¤]Ž}ö‘wf|¾ä8¬ß¡4€?}ÖHA»?‡@çܼ"Y†Çv¥óÍE¨Ý¯wäÏîhô{‹·öŽ@¨|Úóf2φ¥pb—6ˆ¿”âw³CH층z1¾]é Ãs\!¨Ï'„ýË¥ÊÌ9kÂ“ÍæcR©«·ö$)ƒ‡¢ÜªõZ¨®5f ÿß-; J!aþåJ|º$¦ÙeRŸëáñzS>O9œ>k¬†ú}}¸|¿'m;¹eî…ð•e· nGoþ¡È»ýžr§ñsé»K|wå þ[,”W»Ì úcÿÕªH›Î"´Ÿ•…§a5.8~7Jm£·…‘J•ôåŒgÔù1.1±£ tÈ” ®þs3sÛsƒ¾Ö ôÐcY\7hºDôRB°íp1ú¸›®œÅ…ÊżõŽ-5<“ÊÏs¼:ó½SæO]>¾bóhPâsÌþ`ò˜´Ô²û¿^êÓãÅè«Í_™ökM‚O#ð?¾Ï”‚[Üc(ºî˽ŒÇФG0ç¬ï~¹“èŠd!žõèùs¶ ö£yïäéO 7–"Y +þV‚Òâí\­;'ã4n蹃nºóÐ7‹ŽÜ¢Éu–2LÈÍQµ|ÿSSº,j!¿i->½ƒUB‘ÛcA†‡:ž_?öm‰·Í^àBÕ½4Z'hoaZ—wB»Lé±ôQÉ4ºçH]ÕWDO×ä7ûO’+-þ˜Å'çiûÒyЯy øý-î{v–üâð«ï3È4¶;º,’!NºÓ!rëJ¢Y*9€œg(IýQ,¤f-pÏî-¦ÕM _ûI¹é õù;vŒüÿõÛNYÄ7èCÑë\›¯ªí`±|¤ ¯¶˜¤­–§ëå éà‰_ñÈ4+ÜæïÉ»møÜug TSŠ/Ÿ|3žÁ÷§·Ï©ƒ9D‹ÓØŸÈ:œ=ZÁßÜÇé¿ÆâjÝ÷xÔáoþç}&üųÐ{Öñõ¿û\‹üƒx"}.¹¢ÃÃ? ÀüÊt®˜TƒÏë!*ÙµÞéð̪™¼©| 4Ȧ᣸¢ÿÌ‘T¹O¢™æÁûC.Ó[3ˆ¦;~aìÊæ$¦ýÅ‹{‹¢†ì‚ˆe–|vIæ?º bãâ!A¯®@Â-øö“múvžf­Ç+9˜”öøIwŸ…Êê¿ñ·ÖU¸þCdvö£kT6¼:Â!$ë¶àð¼‘ܨp:TáÏŸ0ô˜-¸ÞIÛ|yZ3£?$©Ò½+hM²->ø|îE«ÒgŸ^ÇC+é•Nýü òûX¸Ov‡Hó¬}Xo .xÎGz­¡cc:É«W®´íÑg¼þ&ØÇÁðÜfPDÛlÆrÛȹÔ`¨îý 6§½‚¨¹]¶Î~\ô½1Ù¡Ã27) óÎfÝÁÍi+yÄÎ( ¦™|²ÌJróºÍÞ«ŽáÒ èçÝØqE”®-QäöFðW²ˆTÞS¢0äVV-çÑLx°n>°™Ì; rãZØŸyZ¼çêm2$íT‡0™‹9Pq ÷»GÒã" È|©xòKžë6¨ð Ó2ÉŠ±&¬jÀ`þGæ*¹Ýú„¦7šŸmµ¤¿ŒìyeÁMø¤hA·«øðþYÙ”:)²…ŽV Å;Äh~ïrýa[¬M÷ŽU¥î¿'ШРpfÔÌ~"n×xM²=ðøÙѽŒ[FÇ)*£¡eã4ä®A‹¿n£û| Ó}\xF•öUá_GœGÑ|]èPK³YkcøÉØYT{ﬖHB “jc…¥÷Sü8Œ?M>ÀU3ÙŽ£ºøi<#Þ4$kµ©òÉ\¶Ö–[Ëž`j]?¡fæ?>4wÿÙÆOù¼Xÿ.&*˜¿³3',úçÑcðœ5°3­0váEAÙˆ»°iË[¶[ÑŒ(ÿ¥°´µš¦Ž‚ÖÂ\”«¹Ä”kDù¢#Âö«ÁnÒZðÒs´DáŸÆmpçÍÒ½ÿ×'í\½\µŠ€ìþ’ÿê™›VÒ?¨tvý5‹áÌtö9x œtÝü*™b×9ð· gÇ•ý_œ,4ù9Ó©¼ ÅZмñ0L×™O¼Ïþ†¡¥‡x—OÊÜH .IñÕ.üÙ[K†<«¦NÕÁAr_¡þÇ8§„ã2têˆr|ð<gk÷¡ÿßûø³w¼CÅ9”[sŒ:¿rg5¦ñã^’Ž_°.UoѯøÆî²C%«Ž£œÒslþx V, …m* x²¦ppëÇ5b ¯ï‘ämæí°Ú½€¹­lCšu›e”~xÆŒ¨Ñ<=˜è¾…s$ù†ùÝìé!i®¤‘6¡}púd(û°*›=«ÝÁó=¨Dµ8lNn@£ÂKDLþ7Hù¤‚ýJâ0ۃ瘆“1ZÇp[Ÿ7ŸóZž/L9IΕöÙ%ì¾j%*Žy‡±v»À¤[¸Ø?eg>G²aª£è–å ð£lö?£S3ð¿í؉ƒV†ÈžìÄÚK,µÖ7Ä¢b[ø6"ÿþãÜW-"¤!%õ­Ð͵·ØÔ?m°S¯,ÃFµÕcœæе°cc»Î0/…¨5¹ÌÞ²‘0^~ÔO_@dјf¬ªÁ²‘lÚ:y>Ú]•WþJ\ŽêàìEÚôEpd®îÂW—ÙvÕ¯±U¢tƒûPZ5å.¶Žû7f1¡ú¬÷Þ?Öͤž6•H%fã§¼ tñØŒ}_aÈ”&T¨¼ÎµÊò\ëœË(*n;÷ËÒÔ´c€jpíŠ%¿é¸Ðéÿã?Ê<¬— Šë´áaëDTÊv‡uù?á㥯`hY{ÍÓ ÓçPÓí¨á’ÈãÇBðñEìMf±,ýŠûGH;I¶'ÑáÉIh?…ê÷.âñ-Ìoñð´^€óŸiÒtã+¬¸N5Ò|ÐDü³cú“»¤¤.ÞTh£ÇCR¾ñ·ð…‹.F?ËÓ"âÑ©i!¹qr žY #Krþ}~ 8ü‚“=w`ØËDb=W‡ßuÂÕ†Q"¥Ä¤hÑ]öƒ¸ôŠ>ð={Õl%xÜsò[Ç FŸDÎèÒ±ž[XÖÒªøÇܼ<"˜ë<(„ ´ù(å|úÜ,Ö(ž a ÜèÀ|š¬ò 4DáNùê÷nxM½¿‡ ù~’¹ÅWÂñè“Ð2äÝ¡–SíÆpoûy\õøuž$HÛ¤ì©Mß_\·аk"ZgÆÓzàåï}ä”4}œ!§Hòo¾“îz q…+àÁâPYoÇ{ž;` m"ÿop:‰Qä7W~' 1~ò0ú:¢çmosxþɳ}yßý›i͆ø€©ÿíµ urÿ$j¤6ÍK aæ~£B²=Ö'!à ¶aâ5{vÝQ’¨ü Æ Ù`7« Ôl ‹”ê„c‹‡p’WDoÁö£„Óº6AÏœ( w¦Ðaß¼q„Ç ÒØmLéRž:„ª<þ…ç®xâr¶çÎ톟÷gœÀï½`.ÌÀ¼4 œ<¥ŒÅ¢Ø›šdP»Ai¿ÿ<¸ÂN¦۳åì¹7næì-‚Ñ-FTöQ+U å3"Ï¿ß|úÔq£aÆo° £—IÒ¶ÑptŒ5{òu þE#z‹>ƒâJy8ãè‚›/å¢0±ÿ{ÞG*´>~ŠÇx£ËðKÚ‚ÿžlN—ß‚·Ér™Ì\œ‚os ˆæ^#\l× ?®O£I¯gñ´)2¾)MÖÅ ãU’®|Ö«PçëVŸÇúàXTö5#ÀÜðúMùaA7|¨1å¿zVí²¸•¥Â=:¯ È‚Zò~ç¤ûJàƒùjú:"§)Búcg¨š«O÷LÔäÔÛÿÕX[Ѹm!Æ)>€G噂A"µ²t&.8ôyÒ[Í‚!Ý»°zÿ˜Q²z ÿY¹ö(Ù[\y²Z¨´uzAul?!¾¥Îx4>ϼV¯V™1ã6(’=¹*ôŒ`𾲤·/ßÄÎ;¡ð#¤Ì‡©Æó2|NÚM•Æ~8%¸–q…fŠüþITˆ ã†#¡‘Ä|#¯À»'i¶”©ññ7ã@ë¾tq¶«1÷Th^ÇÛF©`¢«Km¸0»—¹´£Í³ ä§À’Öº‚’¯§©çÂÑõ;0©õ%™3m|ÜÒ‰À0ýÃ,é‹ÒÑh¼¾sµÝà[ÕOü;þ#ÍœïN\èÐ(:ËÊuµh6÷ ´-Í„ås-ÉÝâ÷dÎÕ*[Ž¡aGY^´žþ‘‹~Û=hÚUIî™±Š6=k"rgðÊ,mêËäùÓ×Yêƒg8ß’!«u`*òÝÌ×4 ›º"„9‚á/p˾°äú\P-É{.$C©ÂCô±¾‹ÒÃaìMEªakôÿâZí œ,C—YF«¥dñƒ.p×,ÀÃPîÜÎ=™1{É *ÎYãÁo˜Fšý´áßY¾‡çíSPÍqõ±’ôÛ÷ qð:4ü:Œ^òx€ö¶i|¤Á•ü߯ÕÅäݼÐýrö/_Ê”õ±lÜzPvx‡³þŠãsQsÊÏP Z‰šŽYÌpÈ[$‡HQ˜&{´ Óï&£Bï'|=çÈ;ÅF ƒÓêïqøxÞÒ^Í6 Ë…+[ÔñÁÊ=,Wc*^Èõ¼lxòGŽ}û4™DAªçrÁ¨F vçÞm”ÝøQxëx®Ü¼ˆé/YŠ-4ƒ-Õ2…ÓsåÆLЪrÀQÃÑ~ÜL×Ë©;ba~Q8-Æ^x5ƒÊ¤aüôã^|'÷ /P!—¾VböJq|¹JÀ­hØÔ+ÒÝ; \ÿÞîœ-ø¶{ìû”Î>ä" ~_ ³Æ:iwg2X%:].Ü7·Î±G¯O+q©ƒqù Îßž‹öþ÷2è=o®ë©Ä]÷,pæÐ|¶wª?;£½fsL“Ú¬ŽÖãagPzÃ<7J ŽïÒf ÅŠ}ê±)ù)ø^>‡{% '¾ÍÂŽ wt¹D~#?Ö_ÄÄ5eDüR ˆ¬qÚ²Z¸6ÈŠÞ Å:T£õ60ýK!SðýÀøžôëÅÉ´%Enú0²çÃé{p?u¬y¶üSï’Ëwn¢è²:&Ãm꟣Á:¾‹WÕ±ì~Ú@‘ÉÝFÉ[a´®¿µ*†<ŠJÆ•áâà8ô LÄÁÉ1*üÅJÜòÆžœØOxÄy2„$Ç£gRKò\ƒ#| &éz´L#ûW·ƒ„bõ¼L‚ûÏ;åü$‚“p‚RÜ™œ…³Bâ`G}/NYž /ÖßÀ¥šTm»òžtU…ûXÃé#~ìûX]9~7w\Ó63ëûÚÞŽ¦{ƒO,áKª[ ÞŠ²1ÃØžÃçñbŽV¹Éæ¶Ü4¾üHL}ñ€üDHÚ¬8pÿÓB^9l>Ë^˜17‹#5/*U1ì1ð„.ô|úϧ‰ æVIæüC– <5Ìà¿#]ñà§ ­GìqSᇳ¾ vœ˜ sÆ¢›ÏQ&zâ£`óÁ^6ëÛ]â4cxzIøãîçÍ8äj[PÆ\¾¶¢Ás ähJ3Y:JéÓ½‚±‚©Ø²ë²YÚpªìV1å(™ß "ÖêÒÔhÖêitÉ¿ÜÏ’¤ËZP£E„¿z,Åóù7–»¿™ÅÞã7Bé¼XGüq5Càæ£ƒß>©q×»ßS%xƒo!Z(Žæ›’Hg-’˜Øuä>œÄSž9¨j>­ÔBÉQø›7­š@O_XÿËcT0:í‰à†Š7ר $CÖ{ ²ßLƒÓ&‰ÀÒŒàW÷°h’ÛQšf?gf÷α!“œùÅ£Kù¬!M°4Ën«œÀÇ‘µxÈù% Ò?³U‹HÀšP§{â °þP<;¿óªÜŠ/V8-3^KÊð?š1Ó³–l‹v¯©É¿æ'çÑÒA¬Jk%†áßO}—p’3f6¤g¨hâsü² ÿ g›˜ËÂõ Ü[XˆßNØð{ÎbÏ™‹v‘4¯;NÅ=¼a¶NeõÚ¾bHïÑã¢ñ|[>âÀòü/üƒ½éM¶æýM4_ kGàÐA¢ÔÞ¶ƒÍßmÂÄH±z,ˇ¬§2tãÑ è?ô¤ŸpưÕ÷Aê ž ö9]w¯ßðâ¯%~¡pŠ7øîiGÍW&Üþëøõ½—M 7å]͵l¯Æ(ÞUaÌÕ–¼YNk¡ê›ýöú§Óá«c P<Œ–m]Š¡S¡küS‡5-ùä]¿'ªì3çÃWÖâÐ=e ý9¦>Âî9¯‰Øô{X”&¾D‰™ ‡þ„Sï)&ûIð—yÛá—á¶] @ï®þS£Þ‘Q?Y¥Q­0 † à'^ãqlã6TÝçI$XbxÊtœ6=€ë}S¦é1NüûÇ?äd鸱(¤{Ñoëg•KfWMi´W~ö×cR¢ù‰”Ç‚ëâ©Lu®˜ð«YÑü]Øx{*Ö¨HÁH©]ÿõ&ãÖƒº´Ä_†NøóŒ.04Ké†ec+Q»Q†fgþĪ+ÄtO)ÑjñãêÛ\ù\‹8:e(,²ý zÝï ³=î®ç3‚g çÅYÓàScaeh7ÄÚÊÑ?º— ¥ùŸlädªüÙ¾_9!Ü­õ…ý=)Ê_Ô,†º=>wMý2Yìüï„ =#î¢JìBj"Øî´c[÷† ±‡òD#áÈž3P´ŸnIƒ3ßv2ÿbX!±‚_0Œ3ßMäÂWèè{©\prWqUá‚X²,ê/ŠL‹CÏ/z0Eé  $úÛÐz¦f֙̕|ÉÔ~c‡7ƒZê8r¢`((öŠÃ¯a»Àúck_§L§ fnžÀmŠNtZ û›²¿Ž>øç‡¹Ò 7ì¸|î¦}÷Z•ô×muržÙÒɰ֮ƒ-.¸šÞw¬|x¬Ö:ñÿ£lÄKÕ뱌òŸ`ïík<ádðßóp~h(Û¯|.9nÄÙ9°¿q;›ºúJD/!‹hÔ€ÿO·ë†ÓQ³ÉÓ'r|åý¡ünG*øÌœ¾e£éiÃ÷‚¯Ç7Àøƒ³©™ÄNø´¿ŠEOU¦îeLãé^zUù5(1ƒD±ñÿ+¶8ÛÙ‘½švB ÂߎÓd³à&fGâÑ©´6N 5®Ï$·VÆÁΰøØ²R>ý÷«rß«àeò<80k&ÖíÃÖD=j—_ã5`‡Fä¼ÅHƶoã'Vö Ï%Ú£ãÀ§n‘Ç…aøWa2–×vÀi‹@Ú[ˆZê¹t²Ët.±R ‡\¹‹J»aÍË¥dC¯ר]9Cô/=ý³ °ˆpãÖöím©°µ¸™EH/á‘;àdñ#xÂ0ž ۨ—ÏaÞÓex¦þþdõ^äµÚ¼ÿɦj˜ š,Α8Ð_§E‰HÁC³ºj‚ÃÑ{…“]K ê·ÔNi€©…Wà^¼ô5‰Õ:‹#®É ¦Ý+œk•å£n >Uœ@½‰q0íÇXºô±Í®ØÎÖÛ¬æv“Ô‰gQºº+ÏŒ¥ý#Ç9ŽòM^Fßfë÷ŽJA‚v!¦Ãøúöë‚WgqƒŒ6]÷±îk ¬¾PCÖbÁ°ÿ³ÕÃT7P¶És—(I`öýîtËîŸLòÑiöôj¸X…ÂQIÌP:ž(±o&Œ9–þqæ÷ﮇmc¸ýǃÈ¢IÏç‚“ö“…ÓöÕ|]0F`^ï¾7£ Òi n_@¡ä˜¯£_fդϢŸs¦³¾°'8ýf=ŸtßÉ<'ƒ*Šˆw‰&ßáB®†çÃË­{£ÞŽàšë°[îWаO…¿$óða¿:ú3%J†Â‡snàúIM°5z)¸ ‰…ÇÚHňéà£t lR Ò¡íç?ÖyÁœµø+Ýy þŸÙ0ÞÕŠÁ§mÓéùóq8­w1:¬µ—~}̾PQo-A?ûáÙ¦óðTé>ÂðFSœgOLb›}&âGÅdÜ={ç}Êè8$"¯}Çb çÍŸE½uäý[¦ðâÒôçàż.4e—ÅÁCÎMôøºgr|KŒ,¶G[àe?]^8&A ŸàL«Ë0…ÇL-ú¿>ë{ï%©óô‘\©i2Ïœ| ·äÉrqéC {I¹­ßCä'…Ûcš¡ñ=ÃEÇÇBò^I: bìcuyw%j$“ `âëmðdíBzm­#ùzB…}<—Šu“áGW*•½sƒm­S¢©ã0á²4¿Y°ŒwÌþ&´Ñ[Ì…[ð¯ƒáÆÚ<ÝN––áh¯ñ@ðO‚{k@ýSL»pwœ~Œßž'Â$%{Zôïüw7ß½‹ó‘6Û ð¹c>)Èý¯ïgéláf/Ù å‘ti½W¼lÌ_Ù‘r»0ÌŒÝt¿¯<\Ñ¥¿¤·ÒÏ/ÔðÉò l* òͰR'ß¹­CÁëc°*ÊMËÁ a!ü¦‚-n(Å]M1ô‰o˜ðá‹|uÈvVÚ3 ÞöZóâÈOøbÐ7ÁÅ/·­÷^f›³aÿ¥38/uš°s>7X>$¾N—–…´]k¹àÃáñg²Zd,t{«’’­ÿ´dÅd˜µpŸ5.ö‰¹ÒÓ6bWy§øµ¿ìPî¤uˆˆ{Ó5•NÀçÌÈÿ½Ï&‘ïË ðØÞ¸ðà7³œåÍ<¯A—üM9V‡Ãùê-OqipÀ¿ºF…·M€e«gAŸÃ¼²òN½í ±~ñ,=ÐŽê2Á¦àcÜ鬛?)¾¨ÑaVâ´ï£ù 55~Šûð.µH’¶rîÜ*E½Ý…¸üH²§¢¼$2”7Çåâ!},w² ?]þ D5 ¨Ô q~ïÈ2ˆ„\èX‚Wv&ãƒB_ÔRM׿]ulé’r(ºô(¾‰åï‰"Ÿ7„ïn\Wç܃I5mDÇP—ßL>ì<ãü\ý³óÜÿ=êˆþƼ{¼!LÞÛs†éðúiŸHÏ>7Ì¢ˆçš¡´%ªn[·ƒk»Ï é(=ÝŒku[`îuozª«˜åªF`·æbX—Ž'»µ©‚™9†öbI 7™ä’T®ZYÄè`Ýc7°ï" ÀAi oÛY?ÐNg[¡NË=¤N³ ©}p.ºˆƒÂá™ÈáÊyS¹±¨¾É Ë21üh$^½"B÷J†™­¼na W!³hóÓ>(ôA,¼âÍm/iÐ'«àS·¨,[–ZšôÑ{C^“s˜'%qlüöq ÿ³.úãÅ uÂpµ}õ xÑ#áìÁ­ F­ßßÍOƒùn,CÛfüµ­ÇßM%)ÿjÌO¯£ëz%~àvZMðÅü ®G¿¾O¶øÏ8ZâÚ7Úhþi-ÜߨÎ&kËóR™>Øã¿“z»ì„ï³õ­ÑÓg«Ý¿9s9ͨÁøŸçHou^ÍÂ*?PÔ*nû’ï¤8FœOÞfÚ»†`oÞ5ö¦®_®âýA5l0 Æ)†ìô‰V\½H’Ÿ~§‹ÇKÄø{³çxêõ,Ø`¥Oƒ—»q³\¨¡ O•]`Óf__ø¿õYþ Z|9‡» ;IÅ7ò¸à!,YÂ|$’~%i®;zÖßÎeÊ[îá§ßAÂE~pBtl~I 7=`û›FàÕ„·ÐúH«cŸcZÔ26Dá½SâEw†‚RÝHü¯ÿyÝ [´\U>€ÿ÷ù­ðãâ;8³Ù—ó'lч+pÑ44D³±.E‰o”Ääô9 ¨ wÅ‚cµeu¤ƒÇòqü=sóõø¥&’\ó‚åƒ2€NúÌþ¼Pa¿[’9§÷óÔuqX eÍ-GÄ D†&÷ØW‹­I8Õ´€Ô¥Äã^½»,Àf&u:>‚.,zî§µèßï!„‘·2á¾¹ ï¿~‰iW^‚g !4³¾‚¼•R&é%îÕ£;.kãqÏö±X³VÒS³ÖšüÄ@ÖÅk@üé ^÷p¿¾ãšÏ {¿3/¿ûä\¸ãÿmVØOæØ¬±ÁN|¿nñšEÇ·Å[¯eðHÃÄÝwÉ‹08•iH%n6²‹‚Bøoï牕ep²À”xnåWH¸uÄ?MF­Ï¶¼ï¬<ß±äþµfeæ,›Áz\Gð²¨Åø~Ê8¯d‚Onõ±Ûm ôÀ\b? ¯>OE»÷qJY= ”±Ã¦ÜPtrºDèßÍ |<ËR€ÐæÖ#Oè¾´ pÚí.·PºO/wÿÞš1UpÔz!ËúàbSÇâ2íE Uj>Ïñ×cirõTxܾ,ØU9 ÿÒ¿RÙ™Eh(W ߆îf5Ûe¨Ï›14êíZtþÍÇïE»iIpfâÜ n.U8m}7ŠºÏ±á?OèbÆ4{ˆ.zÂøi=yüf3ß0¾€±6⮫ԸgÆ#°€î«/0ž÷0òOnÐ?HJFŠóÿö‘¾Þ³ZàsÛ ]†+Áz…ZòDƒm|œÇýB”ùäB jtøY9ã$ —ûÞ±ƒ‡•éâ><¨‡üÞ|ˆÜocÕJúL«A‹Ÿ{–F·7vã¶ð»þø¿~\Kþ¥í7¬þ%¿­¼-chTÇê½F𬄮ïÙÎfJ„–³’|ÿ¨ÁtËÞ*²D¿œe`ÔdÍ#yè{—Óü¯ lÐï±0¼Y sŒbþÅS’ª{ÎszO…¦bœs™ÓÈ¥dIî,ò0ËšwÔFÁ¢âÅøªë0´[£Nîüi½•>‹ß¶1¥:[÷ ï^>¼¡k þ`ëÁN­“b“:”é®^:«b,¿wm;Ë›W{Ì1(ñ,Û~z6í \†)¿ž_â1\t¦ Óß岤é/q‚H3¸M;“Lå³n'{+ ß¶žic9Z¬!¹Upgvé#´½*Byâ&xorë«@îMüιé4Ÿí…œ×Ãy™§ ©Ý’hÇvC‰%?¼* âoØÐ˜Ð¹hvm0?ü±rÿ­«ïíZ`DÚ‚n01ò•®Å1 æÒºMÈÖW“âGš¯€ªóIò,j1}ò¸™/ø(øâÂvûÖbëÅ¿`ã)I7l\úþ£ø7²K¶ת%Û#þ·ÿ¹RÒeœØWv„ã¡3Ñʾ,£Æ±àÙ¸eÏ/¨l‚p'øÐp G°aÓ…czI¯û\+—}tbZﮑ7Q3qǽ#Â'¥"Ü5[xC=жp<Åe>‚•¶ë B˜áyý` +`Cž‰Àé\ ‡ƒÔe¾5<©œÅGYHÀyŸ¯äœ¶-³å4LyÎbKsÈëû>ÔÇ=Ω`4¢ÓþÕR«¿³Ç«‹j‚s%xøˆéø÷iæ¼Å¼7Â\íd|Ô׊!)Ó8€(¯…ƒ+D`÷&5[Z/cö?R)ÃÙªü9r_D‚™#_ÅmÒdÁy—+¸=Ì2™ðoÕyöÓ)Ìg¢jJ¯ÙUŠÁgÉ4{}TÖQä&|¹`«Ï%Âó7þ¨÷Tä²C£ÐøÒd¾ÙS·7Œ&ø£‘õk'RËö³¸"°4Œ1Ý¢1°öŸÖzžÌá·Å9;‚˜ÎWªZ©‚É»Æ &¾ï"Už?qQõT\$ÞÅãEã_ï8v¤}>!¼È·j¥ø\[Në#¦SÑ#ép»HÜŒ‡ÛèL¶Ý3¡Þû ñŽ[”)éòW#¤¨ØQËøŸ[¸ƒŠäáþÎŽH¡oÖ£¤æ1¶1ÝÇ}X‚Ojåùà¼Sà³Cj·ÞGOõV¶"Õ”ŽZ?TâËe10òx/¨ÝËÇvEǨmÜ.>– NžÁk¿Eè%!¼Ú ò»¸æ³gDSé ô³¡l›}¶§´È^ìUW¤]Sîƒq¨qiÁØg•h·Ã†]’½EŽŽC•Ãeùž>:Ï8.œù׆ñéñêh2C@ãOtáö)yüó‰2Œ:“Àû"V±Eú3A×;wÁÞŠmøµ0Ío†Ñ« ì©Öð¡x%ãÏ¿‡wà<ýÓ¨½^“¶má~Fލg“6_Πzg¨à‚ÕyÈ>±ö‡˜3éâ{ ”.â²ÿ|‰–Ç5L³7„ñ dÎÆVôš<ˆª¯„(ÏÛðäö+bq½÷m» •ÛÃÿ½?_(¶?ö¥{ÃqûqH– íöM¡ú‹D¹EçFGáâXþ,öŽ…míOೃ ìœp˜WÈ*¿ñà–„Ÿ ÊXàÈ¡ø&Ê“­:5•ûÜȥƿL¨ÏÖç0ÅÍÌ}æ~€ÆGÇQ[ÌŦy·‘XïæàßÃËèOCyAïÜÿã:éÈÝ:¯ªÂÉÒÓ;„áNQyp ¼ÿª:û +“ÿ9ŽvÝ†Š‡6t­[6ªGœÏ8úxV=ùØUç˜ëí‘ÔURŠê´¤c~J5yaNM_ÈmÆïaßþ$¡â:S~oÁ<½¯E/ëE„ùªôÁ瓸³`(umÀè—¿È Ó“¸ƒÕ:Hy‘ˆ»š Mm Þý|˜ß÷”B¢×Œ­Ù)ô¹ üŸ/…Ö+öüÙõr¼¦ìŠf¡xêÑ60Ï6AË;¥h6Z ½ºŽÁñÁÓØÛI<}ÕmýLjڬ§‘qÉ`9± ¾‰=ˆñì1>/€í7€ãþ[ñœï z*t)vvÇò·ú]ÿs0wLÔã‰VIà5E“¯©×ãù8}Ô<|SØÆŽ³/0wâ>žõx1>ïÅǘR«8)Øk9‚†í4çÃv_„Ú^ÿí 7EjñsÍhž± sÑæûÞ„Ï¡Ö|\F0•™|füLd«QAUP#œÇ4é­ ÛÚŒ#Fãö«oƒà¼™7¼]1“GéÁûƒµL×½ &ä'™æoÙc¶ŠßÛÖ‡ñO9¿zÿ½‡n1‡Áò_=û$Iß½¦ oÜæÒÆGÞ´9~w~ÃE§fÁ[“~øõv1Xüà6½èŸÉÄÌ 8š¨ ]ö‚Ó ÄíÿuÝñ\ߟ콲GHFFf*¼ï¹‰¨4*m¥TÚZVvv„Ì$©´Sá}ÏMi ÚRIihk¥ŸÏïß?<Þ^ï—÷}ÝçûÜsÎóy×yù6Áг&Tæ%#iÌïe{À’Ð(þbU É•]²Xwz8úøÇì‰ÉX^áÃJ¢ëÙ”,Uä/$†¢Zk Ê­Ä[Ñ,Ü\”ÚŽ®aüâ…¶ xD¨Z\ý˜nH“Ërš[‰új$Ý7‹]$˜Wvßyˆ8é›YY̬ºßaî]ž’'Mâ~®€}·qâ¸{äúæC û· ßU߯§½£ùd·itÈG ÑÐäk΃×"Æþøþïþ‰ _ɸ¦J8°í.œÕ>2fR=õ ÙÃåÃÞeÇf0LO—ÇÑ´^礇~bç7òëk¶Òñ·áuó$v¸xwo: Î T­mÁü7½-Ó²®0·ôá¢rÁFþ[ùn¯]ýÞ«Oâ~v_XÕªsôE‘ãô ð)â‘0£Õ ç>z òE_ÙÓÊÏè½k/Q¿ÞI’£Bùû}\°k˜/•{ÙÒ;R©È9¾E:JÄæó=1üûó¯ ûm Tѧá+â@Ú •ìu‚êÙ„PfC œot¢[§\°ÿ-㧤Rs+)î̦ÏÁÙd½ç?žj‚ÊyÚØd%Gž]1¤ç-ùúºjèŠP¥¿GÑAG¥pªG.&gø#ìqãû×ßÀö­9üµÃgH0ü‚<$éKãT¶õÃmø(½?˜Z sâ@ôž ­EŸdéÒ‚ Mq›rÉ8>br†@SêÍ2»^—`¸Í,ú|8¡Í#^b½~©ësËû }œ7]/ø$…l}QO7zÜ®i\Í®4D€gào¦ñ`˜`á4%z­7¯;¬Éþm£¤Ð÷æ$ú|‘6]»z8üÿ;AMíj[Ø¢ÓƒñŠˆ(Ë<ÇöŽåK"Ð2`",:¸J>­ÀeOÊèíˆ^\¹D®N£•r¦$éñuAÔO?4¿?v©Ì¦ÖŸNÁÅ1ƒÝ.˜Î£‡›¢xäåYX?j"ŽZËÛ¬hh|8·™VÁçˆL§Ë»ž W„82Q ÕÑxöA’eq¶®¤‹·áã½ïp¿æ;0_óÖ˜/£í<èòr ^_ä̵ôåxè j×—†ÍC/qÕ Û0óQµà´ÛÞXß‚/2†SyBÞÇÍ[6ƒ™Ä-Ò–ÓÀîýè>F‡¯ó>?ÿÄØ‚ƒH›ß#ö!Ètœ¿ãØá|ìx¸®ÌF»½ýÿØ‹¶ôkð“ªôøúiÒ¼ËCœŸéO&ôu£øÓSÐàã–›Ó°5ç%¹±Ï‰o¾ƒdÝø‹EÌJXª/Åñ¿zæóW~»mhÒ‚\uºpÇ.dBIÒAöÀÚƒÏàɸù<³ô žë‹62ߘ]×0x9Û6¾M¥¶–`úÆ8¯±WÓ¸}düyž„¯, ùuiÍý¿Osò!‡Àòä8tòvÍÕã¶ eûÏ• åv϶S«;ÑønYÔÎÕ«¨"aóÒF˜w¦ Ín탄±~è¡–Œç´ôiÄßt*CÑòš"ù<˜û¨òP]k.¦? ²;kð³Ù98å¿„ïÎcÿ¸<+è FkÌ;3^®ËÕþÄ7#"ÙÛîtœ¶æ¾oš¥VBP¬˜!I¯@öC,þy[…¾ñ"ÒÕƒ‡oU¨_ñpT;AÝÞucúe¶A]”>4¸_EÍñÛÊ ržƒ1œÌ÷Dr×p]™3`ÿ¨qJJ ‘ñ0§!-ÜÈÂ>G\…†ÚTrÜ!|}êÌè8‚2"éð;ìœðte ,_†Ëö.cù©e(g‘‚ŠÉuÖø§ähRñ>U¼´IŸ¾’àžc‚pbâiøð׉zÜLm æÚ2n_P×{ΧBŠš¿ËQŒÅßoÈkwmÌ8HVÿ¿èQFrÜ8}7û÷¼‚_€]øÄ“Bð½ñr2mo6LØËá n9† .‘9Ó¢Ûéø-f5¯©ÁKþ¤ ÊÝ^TxòûìvÐÛ½í¸ûëjûóÜþ;|¬ÀrÓop?z nÝפ¯Õõø©Ìlp?¯Ðû‰j{oCɦD¦|tº‡£8_úµb(ž‹-ÃÍká£õWTóÕ¢Okea„¹;¾ /b©7÷‘]é‡ÀüÍG¤ÉFv|e…úð|R³ÂHCYúâM5y{Ä®Éngé5ãÉ¢‰ÒlkÙu|ìz·n©ª ÿ—„?ýÖaîÃAËÛ`Ò‘×(P8Ëântã‘£s¡øÂõ1°h/šW x®À¯ŸÌ —À¿Bõ–U£µ‚*ì fý‰¢i:°åš 7Ñ"†ªnø{ †™VØáóàRº 'ú¼|Nî}ƒ½¯®k¿Ù=0¦SŠ»¸T #‚­IÛõõ rëÊÆ†a·:8Ìr­á^9ÀÅ\Wîk¸–¹ç›1óÈhxs߬îd‹æ6àlƒ|¢¶-•¥f£pþ1,¹»—ìQåÇmqøóï¨Ü¥Ì- d!Rˉ§Ü÷ƒ»ñþ${ÈQ½HFéýƾ‹¨K–ï;گ΄ð‘ki§ÇH~¸ÓZ^Úa†ß|ò7™…ÛâÇV1xg‰êÊo u~!;Vò.ÔÔ㉊‡úÇn˜/Tß+ÃÁ[Š¸Ã¢3صQZë¶aðÓ—,êìcXwj;YsÙ ÃP¿ñ4|-·¦Ý#¸´º8Œ¦óE¢aM -=\ò‚àÅíh,‰ÊÂ¥ê]0t¾†ÿ0ѲY|ƒÊ~|ï~ îwϦ»µÅÀ{v I=©aE¡mñDpi(70¤Ýð8Ï-=Ŝٸæ¡ê/ðú7ãæebŸ¦2Õs3áù9ðˆáìóþ^ÛƒGú"pya¢üx©Dy™¸_׫ˆ+LKèè5aÐð$;ÝöÁ‰IÂ8ƒt,À¿¡é™â…{-ì0Tò1új ¿ö¥¢ý‹‘øôsŸ¾JŒJ:ú²8ÁLü~©ña›áÙáÝP9É·í7õmêTÄñ!yRëO7F°ƒãæ‚BB—medÖ ˜isÍ×=*ôÒ7Ij™fo'â§’U™ÆÃþ¿ÈH¥)àÙ‘+ò_2ëßlNA)³Q’£’Åtï›hèý²ÏdÜ‚M¶/àÒª›èYõt=+ñë’§8ù§ߪ ýׂ¼Ì‰|ëñЦjDÓÚCBìQvW?–áp¼¥:œëz× *'Àÿ%x<Æ«v µ§þF‰™ZtLú˜[ÖOÑ»÷Á#§7Phç&So‚ ¹Ÿ·ñݯà˜u¿<ï‹Ðà JRB8¦9E¶Oàï8<û&ìNއ³ 0Aþ®ŒCwKÒ¹sJ@Åû(^ÿ„ï'‚°Úo(ÓYánü‚l¼ Ñ0_äogí™9Ð9ÅR¸b‚€¹ß¿$Hù2ÞþÍ…”ïp‹®+äìQàÊ\”;s°]XpóÚNõàû–µâ=EI>Z´‡,]·ŒY]Í…¦«Í¸,á6êŽnš=/bšowâÞÝ ðû÷íA«pEt›w$yàºø7pðE :’닆Ûg‚wßTiè±7¸žŒ£m+U@!çY|õÿë¡û®ÔÃÜà›§åâí\!¾ø¡ Û,¾`µî8ž4”ãͪ#(å}x$åíóәͫo 9²Dp71ž„­tä._{@š¾ÀÿÆÑ—X‡ÃÇaÐdY.y3•¨9?q£K8<ñ’Û½Í$ýý?‘; Çž¿ÃCÇø¢ì=ø<ú8^2Èì E""aÚ§Hxçò¦ë› Þ5£?²fÓcÏFÒôu÷ñ_‹9ÝVy{ þM¾$J8ijë©`¯\»™qW Û_CŠK§¢ R—ëùPëàk ³f(l|õzKøèÍþ0! kFý÷õ¯É£ÊË([ñNõë³›[ËÐ0ð»T‡÷_ôǸ° dØe¨öôã6Û "Vˆ[Å£ÊU©:Ã_¼cÿ=œžòJP˜—VõØS=†G—vÁ¸ÄQ ±(½—†}Ø;*áÁÅRüDÝ/7·:m¸Øf’O€¥ŽuøÐ%¡nlÑq j…1äçùZ¬Z”ŽÝk'bû·&Vð¢–X&Mâ²ÎKáJŠ'ÌO'ü§%FŸyÆ3[ ìð¥-ì‘ËC´Ë\;ª“Á{‘o;‰Öë÷â_£é£Ãy¨÷΀+~HÀkRtú¥Ý˜”dÏ{¾³^µF˜,1–†í¨Gz)4ÔEà3ys~ï¯1ÿy©Å:O0½ .µæŽ]'ÉßÈ,ÆÅ.0½ó0\zÔ×&ßB»ÙØØíØìªMŒÓÂ,å}”{¥„}Lxµ ýÞŠuÉÕ¸w¹½S(˜wv*µ7~@L{úÈ¡>'Ðöü‰«|è –-°¥°N<f7šCÀ­»$±¦•(ŸöA7­8[á:°þ…™S0&΀$dëðú£÷!Ö|3¹9åsTòâA’¼ÿ˜;ÞrsHUÅAÑB#·08ßµØÍÅÒ ‡Ž¡ÿz_ ú‰Ø7ûóy —r«ÁÙK…p®ÀʳèݶÞsøþ’tWF%ž¶xƒýcÆÊÃ8ójèh„À{¿™ÌwQ©»4UÅêÿû8·ÿ¬'K:f@·í88L¯Ó;VàÍiƒ@áî œ¹6‡ÊZ°â¬±ÒR8y^YÎéÞµ8 [\B€¦×¡XÉ8|ÏÑæAFä÷ vòË!˜}Î- IwÇÒýk9å2™c=Š8¸Êià$Aøó̪}8fšýd狾ëòáF§`†¢5•72¤ÞËõhô—Y YuÕ,Ç‘?Òp‹Ó<^"áA¥ç‡²÷rtVÞœ|©š9 æ$îÁSŠJ ¬¨Å÷èåB˜×KTù»ZE$ðà®ùTfuŸ`[þx·O) ìÔÕNà:mŒ¯¿ƒRkTˆOë.~-;-•iå³Gè¡,Š–œ1Qªd #ã’„ÁôSLZHVÜZŠÙï¿° ‰éx­xv«[PðÿU½.ø6«œ‰<-$™‘Ît‡œ£[ý¯T4ýº õg͇âÉœ Î7áÖéÃaòýÇ‚¨3œ-Û~ÄŸ wµÜ†îwßáü1š“Kº‹*`Ú{¸â)Õ, }~€-nòü}j,ekwÆ"Ô‘eoNÿ×7š<ªÀªeë™ÝêIÜeÁ;voòoŒ>½Exìv;Ssèªëi”¡wŸ·3˽B,Ÿ§M/iKáªÄxh×{/˜aPgs¢„yVú ®ëA4œ2Œu…|%VÕ±ÿƞȇ£Âw—ãÙÌÌ=ÑY2Ø®:€ÿзD¢mrŸýQ«Ú*t·W98ô…R#Ç¡‚ä±$c¸ Naéb{ÖbMÎ5FÏ+$ߨ‚宫!ûîxBÄštÜ?Z›¿¿/ʯòä¾U>q“'µaâË<¨ÉÿÊöÅÌÇßDzñÇß¡nM~ þ(êAÚm;¾2ñ»µ=®_¼Õ6BLúõuÂ,é«2Jc»n3Ï ùP R>IÒôãu ß<™{Õœ!ñߢò$1>LM†ªVDaYÙ7ˆÚX×’2雿½b2Z”{Õ]Úu€*J¶©²B[O<(Dï­¦PWÆüˆòÖ<Ü*Åþ´šàGóhÔYg‹¡w ¡{•6˜ÛÒÀYÈÊ÷ÉdÚðk8æ””·Š_¨?Äw<'žÛáàݘ}”,Ø¿ Þ½U‡Ä²½P¾G•~’7ãÃeÊèËï1øe¢³°£7Nð%ü‘{w RÒi Æl·fÇ!ZE”šÖËq‡ü䪳&DGNÅæË¦¬*f6?¬î„&›#ñô·C}±Œ ¿¢ý†å|æ«8<ôIÛmì½ÝÌvú~6Râv÷]¬“O-†¼ÓÒ(–r6Ê]Á‡K[ñ—O¼@wb48îÐæÞººe}¨__fá¹ÛôùZiŠŸø·~=8ã|ºê¿nz ƒóÒPÛ÷|zŽjZsqƒôtü×Fd ÂÎl\ã·ž¾Ý’§l¬q-ßêVát…xüò >ñî4Œ£îj»ðÀê:ßPÁýL ƒ« S`AÌ;rXz¼ü¹“)=ŸÍ› \qv¬>«Fì|N/kàÖC<”½{‡ñ ûÑ]jܼzbV)ñ¼ŸÖpµdÍ™úLj¾Ácï’°vÖp|÷ +}Ìió#]>¯‰¡”îy¶Ú;% Og¥ÃÉÁqÿ }³7ÀãMv¨ïògK0Gåöøñ½»åp6&´A‰î Ö:>=•T¨áT+ZåÈã¾ç ô¯ÔãUàù·é7¿5Ѓֵ섕i¼¾8z}x.{3Pÿd:'Ÿ ‡}'#Hfðs ×’'ØÒ.Ä_sIñé<Ò=\æÝðæVs/@“®}âË=¬·óµ¥ø7ä"NµK$>_'ÐÞØ–I› Tø>šºØ$¸’ Æv±kÇó\ÝTzòï)?%¥RïâßU¾/Ô££ö9’£«LéµúÍt÷¸jXá¨ÈCËœ‰!.b´¹ †¼íƒ7ܸœ³at‘x´^"&ÒüµÆaÐPãßí²ÖèÐÏÝhÛq{´Œšo€ýÜ€¯mÃOJßðͳ¿„4 ¬½` ÿ0‰Æ÷â¿©q[ð¹ð$9e¿Œ?U‹Uóãù¼#cøéÜ1tôÒ7x`ãY8æx‹öõs©‡qF§“!ïÑsžL³äou‡òõáBðRn€éG U¬^=††äg“]C˸ËÄ7Dìë´dcyÄÕ±˜VãJÅ#Çůӹ‡œÈÆe¬~*ÅçXIÓÿzR/m^ Ú 0öákò¬ý$ÏÏV„qZ,Pfö¶ +¼¡ÁTƒ?Û¥e1#ù¤…¸áYÚõÔ$xÝdâ3UŸŸÎvÂ…Ö%¸$k&lŽÅçº4ó¥­ö±Àe“aü°y0¨„8§> ÖÇ_°Î.TdøMl÷¨Ç¯7nb¦øJ°ÜAf»ÀV$¬îûö]¸{Ñ øû9ÎmïÏëµ8Õ½†oº '©Ò•ƒÑ(a¦ÚÚP«Žkðñ×j*8ÌÄ^Ã{cdé³}£hor ,Í®#¯ƒäñqµ;w7öâ¬W1¡êeT¿5L}9È„'‘e+œxWÑ`j4k$º®éÝóš`Þns‘e+‘ô*ãvÁ#öR Ðó‡©óN²}&/¡âóé2ˆK?„Ê}©PVÇ•ÆÖ}m°¡ãµ6 àß*­Ž3/w^÷jÁÛ Z\à—ŒûãFº˜Šc0q£ ¼—< ¶eŠTY׳ùÓ.³0³ƒ‚‘QÕ¨h× ¡‚ej%dôÝDŒ•Ên¶?ƒl¿ÕÌYz:>|•¡åâü㡤ÿ•N+‡Faž6, ^¯¼KâÍo‘ÙÙ¨¥µF¸™oÆëßïû°ÏXæ’"|“­Æ‡Ÿˆ…UVÔż¢g\bw¤îiÌ)æÿ˜Ìüfm`¦«A¦XìJ–¯¥XۢǺbXÄ´r’e5¯M¤¬Ôœº+ƒõ~yô0ü_ÿÏ{-{ÙÙæ]¤Æ´}žæ°FÇhzv×4^’þ5ZÁßêD4Q^Ž(¿×+Ú Zx M¼`ù!&XùæysU¸ZÅŒÞz]€ñ¶Šðsœ€è'gb»ÿ2lÚ’Áø˜½ü:­¿§A‡–0èuøŽqÀ×í–48"TvÃ9¥‡dú,3Pä¯FK“g‘V4µà%é9•Á—3CË4!a#W ?ì!~|Ì—¤S£†ÐðÖH®1¡=Ô³`w‚팉âÒocÙ$SNµ$ù”ãÏS®HMü˜zg Mi×ÿõÕÞ4ì°±¡n«:òkrxç­3÷7‘½ôQĪvüäÏAk>$z–««„õSd±¨Ø”äGñŠJ;+Xæ冫y±z¿–±dûø3l_¾h¸ ›ÏÝb¢ËQ¶/žì)?!8ói‰º¯–ì‡õK§‚ÕÆJÖý.„*øDÃ(¥p”hP4áñu§pÍXq®ÛwŸ eõ‰LZŽ?Iéä_ÞðKøµ+…]g6Àw%då2¼ÿzÿÛÿ•Œ›öÜ q /™õo.˜üˆ­^zu ‡à¢ JïÑ÷Z Í:“j;lä³!†íáY+Chiê$Q{ÿ|@ݸgðôÉ+¸Ÿ<˜_­´c‹ ´Y G£ž‚Ÿo/þÝ2g9æq« ]¶'•á-Ñ"¸?­Þ5r:å±5??ÇŠªï9䪻l¯»×]·Äàw˜àûÃÑ(þO×÷À“axØA\{ÐpÜn8.møq›ŽcÇŽføèÿ^ =ä,¦÷SÌ‹Á>SD…Õ·Ñ)QŒ,*؃"Ÿ2°Ñf(}:, <Íð›¯Û ¶ÆÿØ‹þdÔ#9'í˜Ì׿´óÐ`²ø3u´Ô“ûüʬ6éÐû·_ Þ½´™»‚vë/åç5¾´ÀʱÃ89M¸Mqf9äbõøᘥê‚fþÿÒ†ÓƒˆHÌW¦ï] jŠ.©ðÂÔB·§9Æ\¶K“Í]#Cï°ûçðñ‹¸v·5wøÍŽÌP¤£.Kñyvõ`Ÿ¢Œâq2‚ORH\F ̹¢ÆoI,⟷%ÂJ|A~žv£y-‡ iÆZÔY¾º24.ùº×jØ¢WƒàÊUÊdÂѧuôþ=oŸ±3µW‰u¥Ï{W@7ìj®Ù.\YrFø¡Ì$|â™L„¾[stF(£È®#»d8}üŠÈ× i'±Ùq“À8ÖÄ×L¢ñ»ÿ;&×C»¸jÀaühQÍ ¯<‡uµ¢\f® M9.I¤ÝEP§4Ž9‰fpÕë¥pôåJ>3½´6Ê€Qðl^lHVÚî¡z+ü‰îøm¨{c~úYKÛf[Òu.Ç Âª\XEOí˜ Ž]†´aù{¶`í ÚyÌï|8WweŸ1}¶Ì[ŸÁ[Ç} #)–wàì ¹ó3’“oý_ÿó_ްÓkóHðӡϦ‘tÜþFU\ éýë(öu²#+ÁÚb(]ÛAÏQ½Ò…¸¿&T{¢I¬n>|Š%,ðú\lh ÆíWð&LÀë©6û³ûÚ4Iގɸ£I/rTÝ|ª¶¢a8ù3­‡l,V†ë_b™Þ‚l"¯õ×%€ØÏJÌs®žâˆ¿•¼ø‰[Ï]Ý,òÈŒ‰—pÅžyè( —9ò»S»ðá. ªõž…NäEÙæ«È­+#höK ØžÐ+h›?t7(²¤ð,|>¯‚°v‰'Ó>ñ¿þg ÷GâŽ)×1Aþ0lvRc0j]*Ë0Ž>QêÔÿ”·[Mžw«~»§òÌ/çÐaØê˜o‡6M3¹R¶*Ýð¸$k>ðU'ü|QF|íƒêA…(²ð*~¾WŠb\ðüªï‚ý¡‰ÃÓ~2üȘxÁ×gF¼ð¡2—ž35×Îĸ=rXa½3 &@â{ûº=L‘fOìÄ·ÛŽ }šÄùý 2dëäi,)@ãY¶|Þ–ÇPp¼Êñ?n“ Ê®¾6|æ_²ìn›W5 ›¼x÷l'ž¶öJn$Ï›ZØ'‘©$+A{ ÿo[ß‚¶‚×Ï4áŒÃó@_W›­¨¬1Iâ®kBã“H)‘¡cŸ]­ëá´ ˆ¿³´¬‡.9zðé'üx¬×qÉB[j<ˆ.ÜßN|¼cúI{a¹üœ›uÏ«¡ã¢!hêžd5õaô³ß-ü/Î`†² ýp;¹n|‘µy»’^’£34aÐYe´5ÇÿØËãÓ§ñLŽ:n=«N«çN€7»2@¥÷ Þz›ÙwÝA•¶è<ä®K,‚Èc·¡ºýyµx*¿0ç3ny³€îÛ²5,š•Ø`ŒGù€ÿ/‘é šžV_uéC‡Bá±òóÜu6?’:’^V[Äß>^Dš]³Á³Ù&žÈâ7ï|ÆyVMP5v==ù:’΢~ÂÉŠÌ!äàˆ<ñòþ¼$û/Ó§¯Éãz_ýu, ŽúÂçøKBOìàæi”ZÐ…íg,›ÙÈÙû¡eæ þÊ$žy/Dae† xø{¸óÝ•l¸¿Ÿ7{lã%õó¨¡÷XzMƒø¯JÇÉUa=êDÍÏ{wpfÐŒ÷ËÌø¢#Kñ÷gCvmŽLXsî_Í»f¯…çJ©ðbÂL¨Èµãã?°¿ýRY°6—§+3:qÂÓÇè™Ý–=_±Hœða0¶KËÓã»ï@EöQáíyºÜ¥d‹w¼P'ÓÏkGlÙ/6²°Ë6üìê$ˆ²Så×n8òî+)üS~jKC‹'ƒùν·àSVôÞŒLçûê yûni¨Þ Ã&òÏ›ƒa½ÕGì½C—*Àc¨å• R>—&÷"}ùZæÿ{P÷™H#œŒ©&/Æe;2ñf-nß:”'Êû·_àvúw¢ld‹‚%Ð!m%¾:Qþ2Tnïo¶Ðø=&ùm#ï÷Dѽ/üÿÙñ—8ôp,Éw÷ µã±=ÅAà0õ$ýÔ”Ë]/Ù*);¡XûÚënB§¤œ %f±ô®ÆM¬0Î…,£X8ŸK¹ZÁq«¦÷^ÛÀ¹—'sÀ‹<þHÛÔŸ‡·…û®ÇX»Íœ¼'2³¶òWÇÿ—Œ$ôž[{4Bȼ{ž¼ŽK¶Ïß ¼i*îJ ¯+yZ_;؆Ųٛá¾r·.FÝ·ù¢Ð@“ZGd­žÆì’®G†]Zù„8Ÿ™Ã“›ÕéϽ  #[‚ƒ¼-øàÆpØbÊÜÔ]Áeº *«úH6ë‡bÔJkÙÇ:¯¬£k‚BeÒ’>”¿»û68îòMÀŠGò°×/°¢ñ4þКËõæ)Àñ‘ rè·ÈñNÅprÕáv“b^gªÅ,·/§…=Ø~—ý´nƒ¬§-Ð=ŒúN¹ÕE{X«b>¨ßnnÆ¢éøÍ8s :|%žÉë„¿›]ití]2i« ný‚¢u8·¡&l¬%†¥ù§—Eßb ·' ðÿ†Ðœk~'dí€äñ†,Üį걠è;A'ý™RCïÀãð©p@׈:©UðÚ_š|ç)5l(õ¤ãvta©SáV÷Ôð>ÏÖXÒg?c9'ªøŽ²Itžkýxs1$j¡‡Vëó÷ÚùtÉ™XpÍ™N§ o±óIádÆó§£ÞÂŒ*}þ”9ÐÕóœÀóm\Ù'Ew>.#¿+ØúªŸ{'vAˆãS–Ycv-—àÝRez$b%¶¯¸ ‚±ØLt.J8—ÑŽòüÀ•x’çxNÖ¡«g]…£¹»I÷›Fjêúa?µ‡Ë»é%WH¾`ãò2#/_ºúÆ2úfE_P{ >?VÂMKEqŽÚhjwÞªn’áÝÁO©És~láÌ:5ª’ÀkÌgñsZa§® µ¾eHª‚Òañ¯²ºT«K‡š¾ØŽ÷$·oNz-õb¦E2ôoŠ/•ÙÕ K¬’yñ÷²õã=ˆnÇ´ªWÄgò öd¡•acø0!üõh¦³×ïg!cOÒå~·`náf9%†´m•æ^K¯ ÆÉm£óiÿ²çúÜ^ò K°Y„;äl1¢z$ä9î…€íOÈ•Ðx¯¹ˆ^ïP†Ú’lTŲ÷ª€š5 ¥‚8jëÖ‚ß”ÛqqÊ 8œ² nGÇ)ù\îWÔYºz+ͽ°º›±7KŠº›À‡âQx¤æ)Ä7ê‚WÔoq[É•ÜH¬bù›¯O a»ÿ"Üû¤\U¨a]*Ô]LÔ~܇–'AÖô13iÝf“åñ2_ŒùƒN± ªˆct¦0ëü,>m®+ìœÁ\ugð³ŒÒKnîøò;³}6‘úÏ ÔÎû˜ŽðÿñN{X³†++ŠCr²w!\W…íChÖZ;Z848jòCצÒG³;Ѿ-íä[ÑÖ¶ë4&âõ%l‡¯!™D.öÞ"úÓ`¯M<Ä%/&—‰ñQõ«°7ò5ðˆN²zî|rú;qœ5 ‹5ñj`33ë“çϧDà"Õøßý¬«~• 6ƒ§þ›ÂãÊsðøÖ2ÌOúËÚ±çÇ ¹‰Æ%ânONoäÚ;g¡èò œ®š ¯§ ¦´ñ%üp\ òZŠÏÙû€8~X¦R§ùr“<òZ¤†m<£KãMº`š…{ŸLÀ/3ˈŠßÎÄ7£¢qûheþƒ¬"³¯<…·i2´ã1Q+·…ŠE¸qž3f:¨ò2£þã‘6ú^šå'dȰòr¦ñX®M~ódxÑNc:mB5loP6ßÁWª§âçç1¤sèL :¾Šýéìra^Å!8R}ï-=A>z˜oö²ëÃP·h$¤%w ³æPÔÒKÇ)› Ð5XŒ7(Yc_ìa¢‚IÎíb˜`m*,)ˆÆ;Y9X8'›œùµÅO@ô ;j4ö(«³¼†ú/&cÌ&7á4íhµÓà³7¢Xl "f;°þ Mg3«:È;ŒõÙ"üÈš<ÁP“&<4i¨]üCìÜáKFŽu¹G¶ïÊaû_=Aõ§c0çà2ú›$¯S¾Ï~.hC›[10Ó"×–N£ÞŒ£ÃíWqÇ`&ÉC·?š:Nâ{lý!çÆX}CŠ&iq±«S8Ò#ktØ-¡ÚÈoXeÖYw~a1¤?ý…î_dé©þïéÀ§!DþíH\ߟ£e¼NÔé…#ÛHwˆÝéÝ™óO’ˆUø‚S‡ˆø*9ìTÿÊv¨áùš hëÔƒìªÿ¾ç+ÛÞ4‡ÿŒ ·s$ø_{íglhß…ÓÍžAhfw»€ë §S©½åÜs/yïþÏJžÃ=>p0º/‰)sƒ• ãþ–àÚMWpMRëüw”™]þ÷Äe Gÿ$óÕ(cª…j\µvOWØ@)K¡·üø°Õ!M%ˆ+=¯¥;ch ?ðZãêøY›Îm›ÊG}¿€½šÜáe¦¬þN}^šáe·™|üuiðXMçïÜF‡ ²Æ¥¬hÿ|YÍÓɼÑÒ†7<GO¦_‚ÇÙçáò¢/Ä/µ?#‚Y׳ù“Qåä]Ðxè…"³pÙœ<ô?ý/eàå«HóVvv£%™9á‹[…Ô}ôÂWu…bsd.lxA¨×»º›QQôÕ˜Á4sèÒüL²>/¦²’žXš`Îå\‹!bÿ*°µ­Äæm+àTn?¥6…\;%Ï«&I྇‰Tx0‹ýQq#£„3ùs¥ƒ¬ò«:VK¤¶3µyö¤x)¢ Áá¹Ðe ÀEŠÛóE Û »ë³@ü€6ŸUÌw4Ñ÷ÇÞ0ýÁa<ìG¿æÀü@›¢žàÁæT~:%\Z6¬–Ð¥ÇWƒ¬ógØá~X൥Á5 o zÍX øÿ’dW¢á2ç¿ëÛÿrñL–VkŽÅ-…ØØ-YdZù tX1Æ8ÈÒ‚ΞAŒ,•£+WnÇ—ËÆò)þ50ÝÝ”¾3~bUd…¬|<“Šr‰–œIœB¿ü £á…žo-!Ï9}‡ÚÒh³nR÷ôæk½ƒ¤§ã¡öákT9z ;6ÇbÕ³ Éuðæc/0ßÖ€2Çð.Ç.ܪ¤‰³ìã1ý·pã=4Åg+U“§²/âÙœ« &»ŠD/Žˆÿ¹‹&ãý„W°½Ç™öD´BòdM:ÈHÞû0ç=1`ÿrkqü¥±^ v *P©ûÎpÄ*˜éՋќߖïóêSí\¾4¡Y8h ¤ËþÛ¡J—E¬`½+JàÈñáóè:±“B£{0¶î2Ì¸Ö c]$qŠ0ŽíÈ^JÞNg*•ÂùOlÔý8¢5’ß¹îEí äð›ET›L/êÔÃÛ«Ex_QžiÄcŸfÁ¶íÙLeÎ÷å¹cþïu|ß©"Xõ઎Be“!ý;ù Þz2ˆþ7÷âÅü³øQv0Ñ'½› râÇqÝN¾Ãw#6‹ àç—,™ëÛ>ò¼S–»<Ì%»«i£ˆ4]]bŠ9qƬó÷Aó·‹L{æyvãZv¯YItóëV²ˆ8jÊRU21sáÖ3n5t¨*Ñwí¡P|í¯`^®¿—äž.$ê•…¬§ZûÝf¸õ—8î¾ Ö˜Tâ•ö!ÜÿFš[óèGlœþ7áïuŠØ2Ë /¯kÀ®ïð%ÆŽOòØ ¹"0µët·«‚2 #X|Ð®Üø|ü X™šZÎpœÓö MD …=¥Ï·=ÇòÍ b*,Ƶ±ºË‰¨Íì ” 7ÈÿÂ~KU à—;R Ç‚ Ö¸—,YR>æ<~¿kNwty¨ä}b#¹»Îö÷P–f"F{šO@«}ûaÅà#ðóÚá~OÉüwÿ'´ _ûu[˜qÜ Üo©v“M±E¶?8©ñ£·PÅv>žþûF¼ñ÷‚ŸÊ?ˆÙITmÛÝî ûûç,!óBi_mË]LkL/.&`«½õÙŸÆjÎ#¿ÂætHÐÖ§çnØ ¿UKõ™’8nr!9¬­Ä?KнÇWàŽŽR76é[âÓef±Ëñ›Y„Ä‹±/%šPŠ.êüw4â¢Ñ}îŽ-¾ “¸÷.Á¯‹[É“›AFn,~š<Ò ßã»jyî%åŒvÝÂŒk'±ñƒË€ýçž…ˆä áÒ*Eü9‚㦉ô§÷:¶q»?Nœ*B-5W â…ëÌ«'žï{[«¾ÂX¥œª_Ǫ¯±èû]<–©IGßR¯‘™óÔTÉ–1:c¢;ú¤ÊÓÃÅ.t×–5°yþ{<[‚J/lrÜ„wÞ±8N8øh7¾Pç1£D™ÖÕ*ø`bÆæ™kð5ê´GR@[Íkpr¿nn<œFjöùáÚqH½WÖ+Àšƒß xL<ï|ž©CéÕ5õ%ñžMZ…¢VŸÙìöbÜø9ãÿØ ®/ÃÚoIè4íÄÇÏÀÿwP³KÝ 4?ˆ¬«» ÇÐëÅ}à}®Ä°b3Œ(LÆáGO±2%/Ü6î~âEõÚÑú­™¹^ 8?¨ hª&**á”´B8·}:쟞 öÓÊq¥¢†nf óºÿÌnDyýt¨L}&ps*Ç)c_ Ü }÷Áرu\2 ‚š{ÅȾ6‚šÛITÇœ Ì`ÍÍdXß±Ž}´©'{×8Ðæàv"ã .í¨Ã‚C@ÇLyÍ}kÙÝø¿ T—Âç§ ³…’ipܽ‘¹DɳÔÇvtþ‘ã°,î%j¦ÿ\‘k¨ÿÚúóK’Æî9†Óä¨UIçƒÙ3Õ&¼ôe½`òÔÕÄ~š?|/ ÁÀ_*\i~†ÐgÓU<9f;Žè•§ C»õ¨éAÀ[jÝ´ž~ý‡+š…ð[;/<Õgûkøík $E:íí\Œý6”Û}ÜLþž%Ga]0+ êw9ó©ßç ïJnÃ$ÇS;#T¨tcRV1ã;y°g}³Èÿ?~dô3ÄIé#nXµŒŽ)$µ­÷à‡R“\»{Q–G9.Á©ÃiMZ.Ø2˜Ö/ŽEµ‚/s|èÑ€hüõÀŽ^`G¡´€A½µó‰´Ä¿:•(p·M¡ôÊET÷ähÞ¾V†Þ1¬CÉRt›_QŠ-êÄú£¿}-L„ òÖ°ðí.wž üxRÕF«øÇË™µ+¡sL²à°f6Ÿž+O…§Gñ)-¤ü«hŸK°â¶ŸîôÁe÷&ÒÁ|×YkÀµ€WÌ úößÐ÷M‚}¹âès{&¿T;r`ýë+$“*ƒ VçÏNj§Àæ{ zçÊ如ûè$´í™[K_íÆ] îcÍõz”7¹M$ߟDÛŸ ý[ditþl>5>^f4Fá“ߨ]È,'u‘³EøèXÚÉþ#Ãô î˜ ¨±þZýá¼ÂyÁZ÷GXž/Ãü¹³£_•„¥PcÖ.\’ÿWon™ ¶%NÆoÙĆèÎæ*—Ãâ„S¬YþüÙ:“/.JFí*Öþû ‹l]·ÚÛ µ\ƒ¯Ìm"#{bÀEM›n’™ ›ø“B[Hu¿ƒdW¹uh þo9rB0Æ·Hpè»"VY/ TÁ|p±þ d§Êö÷‡ö¹Øs´4Ý“¾õ‹4:#\¥=“WÓsp¥ò+ÚQä]>/ñnt$Vñúw¯Åóg‡æSBg_Qvh':•* ÔúÁP83 Æ:pX²ê.^”ØõÁKÉÙÖp*=žû›òÙûS>tѸr2P¨ª=ŒZ•K±õ{£à-s¦/&HŠòs¥¦àкûn£Ä%dèñ'B eæ;ñqÍhb kµxÈãLãp,ø›âŽYY \'‡þïamŸÄÀþ/Ù! ï4ãó§YÐEüÛ÷–®Ï㡜¼¯ÚAS’ ÙC<µ§BݦC$䉬½¸“ý¢­à¹æ ŒÊ¡ ökqí^ÒÏ3†ûe¥yxµ`/®8ú {cÀÌW•Ÿ8YËM€¢üü1’™Ÿ¸Öÿ7Z÷ô*©Œ.ÄÚ5»ñsÚaòlë/(\/K]–¼õ¾àïÌŽ²wÇâÙ79Ä"e%È«SŽ…‰ùèfmI«ûÎÀøU?Qoù,Û+G³–ùAÖ¾¹ðJÒ]½TˆÛ¼»`’à&®]þ\xÍ¢v þÏØÃŒT´Ýªí˱ðÐg6khÙcü1MrE$é{“8Ö,Øg>ŠùÜwÇâ…& 6ˌϿoâê÷ú þ]«>Ó¯B•¹6½Î=/Ýa†ûÇ…˜ŸM°0xÆÜMƒñ(–~$|ègª“±ü¢­(ûƒ}q0c©16U§ýïÃ×Iî”kÉÓyÉà®É1áµ¶ÛØ™<&OrÅz:´ÿoÁ~ëîWÌzöɯÅó]Þ…eùƒ” Ó"+ïma½òòø'2³7س+Çð +™äðW䮆&îzV‡Oõ»ð»æU±[~SáeÇPÕ¨q)¨î! EáX¦e¥/%¡ªe˜M{†ªâAs§Ó݉…7VC»æ>w,Ù“T5êµßZX~h"LŸŒÇº,ñõûkxZá–`™“6nü‡•±ó¦,¶´\àç¤Ìÿº|t»pÇdLCað1LNÌÁi›õ¨šÇi"’bÈ ²Ì@ZZšŸUGž4Bû׌òjG×=ønë'XôÖ ÛtµQ4ª¤*äø¹èxz³-ˆ :'œ~Üìë[0dm"™| M&gã¢'PµÃ—ÿíÂìùþ+ß wýÅd¯)‚›Mê&èb’f%ø~ô-¼pà 3¼6‘ëì—…‰p½ëO¶—@óì¢×&kÏ‘Îú_¤jÜ ¾ìî$h”ŠéÔkÂTû- 9<NÔ¦  h¯½ÿ4¥ ßZ¼"eúbtú‹ZædUÊFæ™Sc±ÛðBÚƒêóë¹”®™P“ò¢ J¤“/| k2“`—d*üv6äöòM p@—·õ˜ÃØYŠäƒã1Èÿïù®µóp{ù[Â?V/¦ÎÎG„´Ž¡hõ,\r=ÎW¶1çÊ8ñt&Èÿïùß÷ÆÇÁF½q0ÆD ³Wêò£Ÿ’GÒ´ÐJ½Îûþ>þ G…~KOï*åÄ“ñ½)xmçV*j@ }ŸA™“?ŠÉHrŸëߨ‡@ÂT@âðRhX¦JÃÇ$âÝÆ¹ %ÒKnV…§öGwLEHuâkÐmøLRepÛÂX\xî,4G‘£’ÑÑihxeV¼AÝ®« l&4ÂÀÓ mx^Ó~—ÇÂz3F¾MB÷!#¸ærà¯ÃË5?aåŸLr;2•­N•&«‚Œ¸êè8Ó^„.?,Á[Ss…×BŽà—÷Fá·ÁðäU<åAÖDЙ߿@c±v ODØâê¯þðçJÛðˆÕ¬ÔÅ®QRì×ùU 5ã7v'Äw‰Á/ =þéY®©„e¦£Çý•ð75 œæ yþLÑŠÒ8&Ù³ÕU¸£ÚXävþÔ}b77Òï3óÌÏÓÜVÝð µâÝ˧A8øý_Ö?©cÖ£³óXPsP óêŽã½Ë:4È9¿=P‡HÛé´Uv¤‹f°j­áüSP üŒï›¶X¿õ>øŸœÇ»ÞäÀœ6Ê\vØ„÷Ÿá×}™ìF÷HÈô~ÀF/ĶZ´ÂŸ³i°É¢Ž âP~v¦ÛðnWöu<¹|}9zMÏ7>xÃ>úÆQç¹ÏÁ=ê'äŠÿÜM1@² ¥çÉó5"Œ|SÜ ›ÿÙEÒó¼ëoÅþ‡L™âø¹xêŸ =¤Aå;Z‰ñô8¤eͤ~ý3˜6k-nÔ¹ªÝV㹇diön=¹¼6#ʃjÿH‡Ö%¾=­šZ/aþÑLç¼7£-è[/†Yïã™ÙcèæNº¼F—˜,æ§“ZàYú-_å¦Î,ÎÏ„ø~ŸE‡4{à|ªÏoŒp‰Q—IÚL{š<ß~}ô…Íý|px¬iØ‹uÑù‡'¢KîT÷DOT>eK>‡3¯‘ÍZ“΢xÁY¢û›Á\9ئ…G®wÕi±äΖ¨IÓÀØÃàȺøï·ª‰XûÝÎYE£H‡n’/EG‡`_¼5ŸûMÇÉ¡ï‰^À,<”Hëû¨=2n$˜Z® ¶Jghôç}ØúcÎóÛEÛ¥ÂÑêX{ò€Túާæâí ¸êª˜KÓ‰z= ´p<óZ÷½´ýѹd”o¢<è3Þ9`@ϼ݇[ ’ðeˆ"Ë]äMóoùI—ˆ .c†óEhEÃn2lãiUpì/*°ÿ¥*SjÐ9…¬KšHÔZ´©ãîøs•£[Í\pç'Ê éÔ¹Œ]9žÜWïaU}áDLý>FV`ÔÛP’U)GeŸÞÃݯ`ÝÝh:׿ç-Jǽ n``s¥¶ÎÅu6ñ¨?n!Þú8bf¶4gºrñ ¾t~<›÷›mµÇ&­fî9ZÔf°€5°S'¶ fd8e7f’Ñ{ªÈ%)KºèH)¶ê»ÂøKt+_àdSzlÕHöõ|*\sœ! šsŠ|$?ž>†-ߎ°µK¼¹sÇLª½ 6yèU÷…û™ÆkÈÄ¿³¹ôÍí·u²&ãž•”šÏã]qü¤’6Í áƒçëòªO2ÞmŸè•JÛ”,¨£A&/Ô"Ñ…›SÏn„¯î«øž.W±'f6šòèe*à+³@°Êì"¦’³·\c‘KyëÚ¯(ÒeƒIËÓhà·,:ý‡?S1 ¦¿8Ñ.ÔÔi)M©Så sKÐtÿMö`á|òm-šYÁ÷—¯ç§¾âãÃÀ"0‹¾™ÓEÓÛàq7žÆÕXñI:ã¸mf'Õß[ÁTÇhàqKö-f/Å“F.¸!¾Øð³–ÿ»ÿsäé@Ô¿)Œ»œháˆýržvÝ  Mç>¡…·2Ý(s,w<‡Žˈ{U»[é)=Ö?öK¼±eš!nG "•|»fÖã&wIH 9æ–¿†zéypÓ}'ñÖ&&þ» ü—T¸=éDÆôsÄ*¥Ûp%÷-L™)Nw«Æã~sàI,èꌡd¡Þšs»‡èå0ñáàþO'ºû¡¾Ë½Îü—˜³°ŠQt£Ò~¢yÿˆJÌhhZÀnAŒé<.€ÿÏ+§Žû™…á[ÖM¯ ÖMê6݆_ÿúf_°%—½†=Oæ ÔtT^%æ} ú"Yq†ØÒ‡lÆîÂ1³]-Èâ=×aÍ¡\û[—¶èñ½*"thÕT·¥*!Š0öþ7œw.&H ŸhzCWi"Œè¨B?í¯ Öó‚ÊŽÀæß"D(teY÷ü8jçÀÞþ¸]›”.´ú†á `áËZ|¶…Ñ >ønXìï¢faã”ílvôQðm±aæÎO Ÿœ†ýë€J‰-O$î0[\ » ”!1pî1úŽîç ÑRKŠºÕŽåßz€’™(Ww]‚æM…Óásù–o2è&J=ie%Nîñ/ud ì–‚ÕÅ;Ù?µÇ0a¹‘4Pá×T°V»Õ»±î°ò¤=í™Ù-ˆê×¼¡³ŸãGhG“£×Yáºýx¤ð,ÒÊ_²åjêŒf÷í/Cý þg‘ÝyÏ KN† Y¸­ÀGîÌÂ/q8u±½?a:ß|¬¦‚:Õ¯áO'l¥!Ö‰<ä*óbIwØ‘¥£©´Ÿ ýA Þ?…¨œR¼W B,¯ª£ú;1~<ì¢ÐQi°1œ¬+#›ÌM±qÔá‘ÝüѰ£ØáçMûq€Ùšlð_ü˜ns§Ã®ýŸ|#MœÜí¿’›Hø_O¼»TýêõB—& V².ƒÅ%Úýõ3Þø¾Ö…éCÊfÀ]nÉ0$(ˆ˜ðÏN» Ã\ÇâÜÅãh¯H$Ûà˜Ï É—?O†(#jùí’ÐeF)Nð´áÓ¦ùÀ‡îi|ûüXRíI…'l•a“Ùbà$t»µ8—8,¾)~à·a=3}òžÜò‘ v›°×Ä 1oW±‘]Ž¥O€:ÄÞ­+;„uó§ŠpÿE÷auV<Ñ 3Ÿ…P Þxs=„øÉ ZðÞ#F•^_ø_ýãÚ”º… .°{s2P{j¥p]z •ö¯…M§æñî®.–º£<Õ×`´>:ãÔ÷Y “aÃ*OÞìT„D6÷ ù„à)ôÊ/ç£86J„¶1ôJs <¬:ïmwr›¦|ÓÂjÏú‚úŠb´K톛$_¨qP}ÔÀnÔiPÉš+D{„9so;Ë2´aÿO ê–.ØãÀ½¦›ðQïž`¬•8Íþ– Û›$AȇÒwrà†Fœpð†pü=1–î}TJ<¦b´2ÿW¯VlÁË-€_†½©¿QºÕ]DǬÿ_™Z¤vêd\~‹»ÍÇiñyP\M~<ƼŽ29 ;xñc&gOZýÐWõòsK”×ÛÆãåK¢P#rßñÃÖ“P^ž€~Û¶àüŸ›!_^‡²Íœé>Ê&iJépa‡·¾þ †Õã°þø5ú…/³ñæ1º7™‡‰ õ0ÖåÛf…Ûç`¸Ý9v,HžÇÍø 'ZÍÁrÕì5~Îj$c4®BøÞÄùà^j{¯¯SÊmêaÃK‡p•Õu0§D„*o®D/Õ; ŽˆÈ›jø§ü +|Z ñ±­6L%õnùñ¿ö§›¤’Â\ARR*yõo¯=éÉ£CoÀ=§(¡Ã÷Lß–kÏ4Cˆô\WÅŽžóÑÃF|ÃÏÁ¸èe1ŠªºÓOÉVÈåcÓÄ)9žG 8zç(³¸?6´BI˼9 ï“»ÑyÓXx7ù³»-N{ãA›exÿÛ:ê¬5îï.‰·ïÛ“¹‰tÃV!>ÒëÃ÷Lé¨AW`Uvwß0'žšÍ§›”ârÏ TJó)X¼Š@ÕÕ)¼Hdž*øÂþs`¡·Íþ×´žÃ(G~md,uê[–üÿÀ}j{¶¬œEßf„—’KëÜïÿ×ËGPzoÖ¯)×ÕXÿÍŒ{==ެÒSÁÄþÉÒM3ÿ ­ß?Æ£Úyvd;®Û6˜JÕŒ ëZ­p­Ê`:I^¡nsñe(±”äš«ý¸R«^ÞŒ'nß…û0‹3\2טÆþâSVb¨ˆ _l¡Gï×aÈët¶uû0~å—*?¡}ƒ$EÝ©(2½dhƺñL 'ÔòðÝ5'úýÄë2S‘?Ìùÿñ&lÄe#'’/6àöË» O#œ¸JÞ`+KóéÝŒ¤ÃÁ@ÿ·‹£¢1ò0‚¬¹?Þ»’Ÿ¹Diùzüå Ä•¥†ðÞ.¬°AÙUz\0ÿ d¹ÎE‰°å×y"ÓåH˟ǽ/ÚXq·\ ¹‚è®ìPÒv’~é ÞóØ1F%ØTSÉ6¾7‡iR\oÎ ì˜.ÊŽ&ŠKFcѤñüvúìkv¥žóÚùÐ/ ì0Ìëµðø!k*:2/Í­Ãe’Cé‚©Ë ÷༲3¦î¥D<»A™§í`¥ÿ'ö6¡õ¼ô{O浉ò¥«%¨vQ*´†œ¦Ò¾7ÉÒ[ÑÜ7dßš<°ÿõ\Ü×ß?ƒ-Î>4ÄgÐÿ}=(» ‰EÒT*üœ› êÜÀÆ–4£œU.x¯egKŸcÎ’²ÿ>ƒoíÆ!÷u@y|‹@Wõ\óׄÞ=³è±Fœr\}ßF‚ôç+lß¹l ÍFlG‰-1>Eݼ¥×¶°î%`y—(_8ÎAP3 Ëê™»€ïØ¿âï¶Áõzf¥Ñ¯ †hÑYŸVƒ_¿©R­ÁÊ¥¦ôÔb9Ús׎ߒ½Åž‡Á,g-äNƒŸŽä‡ý¾oZ î„ÿ ƒíWÅø…eNìÌÔ8]ç÷ÿxå:Þâ\ä´&(Í›F7ê*seÿ±üP¾o5äÃ3€Ü|!Š…ëÞ›Óï{FÖ‰LסW‚‚éŽÅÊ|sÖr”Ý’ÍËòE.¾hŒ ŸOB›K/8(†»YÍYÓ“n ¿“äV}ƒaÜU:ïá2îV¼òi ¾?Ô«AØ…X¸qd6-ñÆÄ¨á¶Jyàvdü%ùfÏñÜi¼:µÈ˜÷Ý^ÁOm~‹ã–˜ÒÝ]…|Î0¶ ‘t5¾;ª®\ÉÄË)[øö»Î\e‹/œYæC»ÔîàϨnH='‡V3¦£òOnf¡<àÿ³ª3±qÓ$zò˜Þp<³°üMÛò#|É—áüpŸvÝ#q¾jù^¼&Ï~*£wfÆ1±ëKhâ9#®–À¯ÿׯÌå‡àéšm|ÂŽÍ|ú o~M4î %…³þ ˜ï?Šóþ â ‚å|è Ó:J‘ÜâÀçž! «ŸËÙOe´à+L±>ƒÞS’áÙ·cônâVÖw–_ÚxxzñŒŸ½ðÝy47ú4•‡ÑwpoÂ\T¨‘Å%[Öã’ϱîIú@þw-ùG´Æ"Õwäc ׂ¨Í*âÅç5¿Ç'}ްíŒ?sy?D5ÄЄkÉ£R/¡Á9Üf«E£ËwÑ‚½•àªÏŒ.ÀÃj<%Ò®—u€wp¸@k}0¾?Çðùœ&"&B™Ë),¹UI2ÝoÐΰ·x&o9iœŠksUA¥½–}>®zù,äõ"šsÌ%Œcõ—\œ_;ˆfùQ—¸ñt”Ô8𹂡éÄlÑpD‡Y`b´$ö ¶BÕ-‹ùޱpüÆ<¾×Ög‹W {¾kÓ3?º™¾%!Ÿ·;2+IœüâÝÀóß÷+›ãй› 'j±àtéF J&°MŠîpˆEÙ;êüðÁ]4½+–EN5@ëk£…§Ž\…°ÉAôLäQ4æ o¦h¢ï²½ðm·ä(BïÞÊÅ)kO‚Ô¨È7—ö=ÿ!Ùÿ’ýø" 2FAÜ6k¶àvܰ€G…ß'gýUùãHiz7a­Ö3•Oí–ç’¬hË 6AGš8Œp#—óMÑ2WšŽ»Û ô¬àë]ž YŠþoÚ¯³™ž÷rúK] ¼NS9[zvþuòKÛ—ë¿ÂI® ÐÐõFºÀ<±®ýïiûZøúÄbþ`=¨º]ðôÚn˜&gÂÎ/?ÈË|zà{$<Ž¡Ï–>‡¦éüŒ¤"}ïЂ±ù~«V§yÃÛÈ-¶èýi V9/)i%<½ç]6â+ÿ'èèÒÀ¯äM¬‚pö¬Q°-YÆ]«†çòÉ‚»ƒUñmÏ»º_ê®°®á5_ÂÏ:b{Ò:ÊÀ’÷üu§3—[ÃÊ18[lsÝîÖ›Â;Mé8¿G«¿,JúÿµÐ £†Ñ ¯Y½!×·›†«[ aºG ¬_-RgG{¿L£Úz’üÛý4aâCÜè£ÙŠêô5FÑgîIà“YDWêrhƒ—ÛV ã*-ZÞS‰ ¼§ùäãHWl”¦±æ[±vépþu–2º]l¬Xû!Þ£w‰Oš*Ë/ìgqËl©ÇßÚy%q þ‹~qk§1“¦9;'*;bAØu¨Út»?W_ É?„‹,¥ñÀð%üÙÕsnÏv0ø L¥Î]“¨.Pþ=˜ݶ”OøÚ†;QuO->ü{#~Å@áw®¾~o†°ðª„÷Î*Ǻ&h»Q†½vÐÝ;¬ø2©jð[Ò7:uè-ÿ0ACd4œýï™rËwãv{eþ]ÉHˆ3›¡Àpßp 6.*‚“C~á˜á†Ø£ª)ø½ì:wΦÃAFR^\óÎìŒn†Å0üêQ2'´ôÛ´ñW¥>¾ 3ÿ¿ ~Ʀ•AüÒ/Œ·É!C!+z/¶}3á…5öšattŒ:ïÕØHGÎiÄ©U…¿S¾£e§1:šíæO(Òø¦{× 7}ÝoZž­v¢—¨IÛ‹ÃùTÛ4’»L©nç3f¹€R’-Ìß`‚ú×ÐzÏQrKÄ€šÜìÆ¦¡×°¸*ÉýAtÌ’ìñσÂMý*ãVªI3ín5{ÎìÁùŠr<\¤ñE<®–ïEùoÚüÒØtðWô„¯×yÜå$.wíŸ[»vó}ˆŽÊ¡Â­Ûà×ë7øœìÿþôn’lþFø+%•Æ}Á·ãáʨ9|Ðæì–¬/~^+ÁÞŸV×q">Q@_ÜV¡³|ÆÃÞóE˜;Ưÿ³Ë…ƒ7åaãñôæ#‚™ãaU·¿³G‰/& éëCüš{ÆpVbË»]„°Ž³ÖxZ%WOÔÉTI%zê`4¼?¼õýªd‡Ÿü¹`fëLÈî¯K™Žç(fzLôžâyò{œ'ßõ¦–<F*.dÁZ‹=PF/Ñ;uQ×±ÊË©væ9~þM ªìm *× ôgŠ»ÁÔ)¢zOø¿†u,+±x+PÚ¾º_G XëÁˆÀ|2©6.–µ`É“ùôåÕJWÛÕ+©Â„$®xIYð¤ŸëÆF>ÁËóÐç¿‚WîÁÙG¥0(ª™6_¬‹©3…‚ÓÇÙ|·µüñ[ }M^;+ÃE ×é4\ék‚ÿ9À“uu±%[p‘‚µ0©‚Ôqª8ññ?°»jЧü©eŠ{Cž_Ú:Bá rÉ` Õ*S‚IƒúÐ)áèNo€®·?~¹†1š­eñâ'”8òÏ^ÞKÊn-„ù·Çóó)G1ìƒ)õ²°ÇöL’ކ·û¿ÒkG×%oà×»–à–…6tŒÔ/Ø]±Šwœ,§·êæÑÏNÇ¡Æe=æì:oT"ùìz ˜<”uzG'éRÅèqÜ4x0íåb Z_Ez_pu×tjc;„G?ó¥ºSú}óùk8Æ|•ûªÄ@´Ã˜–é¼ÃŽüŸÐ7t<~xœ§ÍÍÃÇVfüç«Z¾òúVäMMqœ;=ôîFÒ›ýqTöèJTû˜@õ]†Á©é»aÞV7.÷a7ZaÄ•ÛR¿,þI<µ.œ”ï(J“)5 á‹wÛP}Äû—¨ã.‡fØ´` •Y_Î ¿ á·½¹áà!f†]ÓþáO‘;0éê\øö׿\»ØåSh­ìçQhíMGUä Æ?žL?ާ¡vÕ V:fNXÏLÞ…„®<î§°–Jÿ•¦,} xò2úbÛ;œÐÙ$´‰±§Í[ï¹½“LÆíƒž£îÄpÁõŠ!8ýÜ?4~¸‹ëÖB=ZRQ©Bc¢-q©u+.Rá®=¡T¦>„Çnȼ«]Éû<¢x ƒ4^nWåõJý\>i&¥õ¬ð[{l9›¼ ×Û<ì?‚ÒÓ¾Ãrµ¦ýë¢ÈÌ6· jUú—¨|{žý2ç9.ÛàG®€ËŸµ¡³ÿ1vÔº€H rD¯Q¦¦ãFrµæa 2gŒ|^#€\ð2ŠmjN“5Ÿ²±ðŠù:ÕY?óf¸!U){µù’Íiý8}Ý_ãô‹Ãù³×aOMˆyŒ¡tyçúpI´ùÂc“ ÂØ¼‡Èñ£èƒïÖ¾\0;6KÒí8wA[˜â³•mÍH@©ýà…•(<¯Ã^KDC´Ù2¯Ráã×ì³ÅGp±! œcC¥èö=íð5ð ܰ¿ÛσPÝ6'ÌõÅ×¥Œ;]Hí=åä»ê\:%9vÇÌAõ’ž»œ¿6ÐKþ XRÄúßç§cïCØB\Ô1–Þ>}ëpyùDxbÛ&´ÝžÃ*ÇeãÉ]•ØaØÉû´éq›ºèÃPªÁó×÷á”S†Â“…’0Ö=çé…⿽Úp ý3j&íÅà P¸$FŒ6Êñ ÕÙ)nP°œÔ'®¤ CßÁøOb|¶Èœ°±JPó9Q²s—{;êžK#Û¤Dù\ù/¶™H ùÔ¶ÞÝJsssة߅k¤þ§ÿ\ýŸÙ§„ f¥Âý°w9þf§rVÀ*y)tX"KòšÔøéõ˜¼~.ô=J%¡‘—¡¨H£„ìíÊ=1Ö ËHçºJyJ†v—Ç÷Ó//Ũ7?Û^ØñUKß²w£BOé"Üjk‡N …Ø0º C+äÙŸ{Òèsj ŠìŠ•^Cá´©<- <€AÇàË]g0îÕmeΤ^¾º¹å38”¼@¥©*´gO¬ÏMËvHÒ¥8kÇ\Ã"çpØêïÕ¡r‰Ýœ ,Òé@MN›–0ÇÔË.üÞÏüöe¬wÖ- +€à ÷Úœ±š‰Î!ð­üŒ Їm—+A¯Ð“i 8K¼&?”_£Ë#}n­œŠ²h“vAð`¼§=!p1r03£j+Û‰ƒQ&®ÚfÏß§ ]A+eµ„z¥/á”D «©Äü&u2Ñ»Âkµ¨Ò”nk9ÒeWöõ¢åÂ1¼×2žXR‡ƒÝ8å{*Ëóä«~¿¯û¯þ9ZFžïÔ¡éùGÐt‚<-.Ä›I3 Ù"á«è½C¯2££É6ÙtДŸOÿÜ…ÇöGBŒTñ€þÓÔ!ÇáŠÉ)°k*°>+yœ;÷"æ{j‘9o6 JkÇÐU9#„Q׈³Â;Ñ8EHÍšBµ·fáLß8ºä•6]xï&HÁË«÷’±&p7d%´êo­¢3ìï¸Ôš¯†Šf µi,õ^^‹Ú‹>à“µ×ÉáÛaç¤éÓnX浜5Ñ¿äoÓBü><ìmJ×W o¥%·o$gº³ÙÓpÑëûh쬇?ö1“&¼þZ!éº>‚†™æ`߈¥pzIy, %aÚW5j(þ ]Ëºî ¢ÝßU°)RƒL®Øÿ×NŒëáµ}!ÁìLÑâÙî1ðÜ}FwÌ{Ï(â¼å Få«°ï“÷¢–ß¾êžÀõ±'°b®®$ð÷ì~oƒ†¦(jÁC,?JëÀ»Ó2ü½ëÖ7ÑöèÀ°'P.hÁÓQ5$ái*69͇¢•Rp€ùZ/¸ty&›¸ì.¬ù ŒõárÏdPRŒ3[…•ûwÁ¬oL ¸x6Q\Ý&|0zŒšÃo¢'k`ÉáNôòs>O-¶o'¶ó$¹P¬á~jÔYr3n}.…D{¸]ŠÆ4³”õsSAË·†=P0ÀŽ,yÜ÷ø”¯Q¤n_ŒŽy~lŠí4|ÂÝíÓéE´%3FüúG-Z&¬©=[–6õÐÔG2©;¬sê×â9×A"IMg« NËc ñÆxme&¼ÕYˆñ3J 6bÖÈ×aDZäŸä :àïžmá 6¿ØŸÆN&5Y—tÖ-„À\3>>\™´-Ñà .ôï¬þ2ÿöýŒ4›Sîâàñ0ÍÏüçöóÈÅĪp/´þ)E…š`ú‹àÖ‰'ˆ†O ¯ê”Oåo[¥è$©¨ã×ËÇ‘ò=‡pçµzü¸s už¶ e•vñ’añ•hÜïÃfôûPü_×D—9@ÑDkzýÒ)xþõL <[·<„ø®4ºG†öË<öB÷ˆ屯 újpöì ºçNº%Ë0©¿ûðdº8=g’¿í=Ñc£{3÷$„Ü-@Ò˜ÁŸ¤LÇiÓ·»i šMðù·©, pë€ýí9ã6› Èâ£Ñ˜µwŠª<ÂØªdØîõWñÅ`]e‚FÃå¸Í<Á¦‚ýÄëÄtOíµ><[' /­é§’Dx¿r Èî£ÉJ¸3ï4Þ Á¨=àÕŸ{‹OIÓ³²ñ"-…YyÉÂáY‚»óôù5•¿ ìíˆÂQ#¸r íˆ‹¨(OË)ÿ/X:á;˜/@ }hÛûŽTÁÇuw?ß„°^×Wð¾\_/ ¢S6`ÿ˜Âg²-lêÍã`ª;“‰”¦§‡CÀà^xy\CàÔ&EßÍ|€±Çmô¯…ñ ÁT¿8PŠK€`‰ü).Æ×?÷‡ÅcÓ¢P¶cçU¼´c‹WÄs·{pú‰hÄÅy{®pí`oåÎÊ—¯Â¡á´(ß©&‹™mÃhbÓièžøôFe’GwµáŠØ6ÖòÚ|ôÒ°uÿD˜¥›„Çkûyl…}+qNý\ÇæßÜŠzÉÏmÙ%ÊgÈÔáùð Ëfd=B©õYð çðjN.È XÿÛ­V’Üývm¡@¾>%Y’ÜhmŠ«™K$ø}&ÊKÂ/å¼'³…F[¡Zk ´Æ~ JJÔéáS˜Ï×áõ¦ ­Â¿À"þè'jH•¢Ý=Sº»×w Ñ&²#Ô3If“+ÚÎóaå˜uo"ê·ÜÙ‚3ì]{’‚k—®åËûy¹¤î¤Ç*è,ÊÄåv—ÙgévØ+ DÁìk|én|ɹ׬ƒ;. h}"ïV×`Ì&mþÏóäY¬¹P"Ñ:†žš -ƒ§Àü µì„‚.°Ïÿwÿ¿\ÈÜûÀt?–CÜ«/pO±˜ýû=žW<#öÞílIÒpòüÇ Üàÿv¤Úž2ÿï Ìª ô:÷öZ°þfN·now¿ña'ÀU7à« ­ÏŽ^ ëOijÛ#1T-?œ-_F#7D¾´âu‹axbÐ>2©^SÖþ‚㮺þqð¢ý:tȉ€‘õ5bœ|’évç‘á×^ãQ77úiˆ\2Šw¸/À“Šó°c_»^ž%”V ¥“þ{ܶyC:¾ÿÒI6›Ù7g¾jÈ Xïš&¼y^l`ýWxNã€w‡Íƒ°É«Zxc¿4'Vaïœ_—7dŠçž´VŠ×Ö»C„ìÒñòÓ²b_ÎÝÀˆÎ}°àãe2ÞË”}•Òâ‹nÔ€{Ó Vvo&ïpÃÔÌVð”߇¯…ßàûYÎÞæèð¦éñmÃ-"¿@–n^— GßïAìt€,e7n2ý#nß~Nê"Wz³á KF¹s‡°oûK˜úáfLÿ`»ªµPÍf0=pÞÖ¡‡ÎK‘”b2%*¾zir·µãAõv2ÜòkŃO–Áµ%azIŒÙ+໥µéô6øŸ¾Og 4'AÊ¥ilLcöZ0»Â2’‘ÝNVÚ…^.—°±-žÕ™»aFTzÜ/Æ Y*ücé` wk Æ“nÜ%GUgf€Ã¡*öp†,^ÿ|ÍߣA:Ü' àè½z1›¸\>„ùNÞu{Bá–Ô;õÎŒ—ÄÇà™Uô¬R§1W–ÁÁH *»¦ÿ‰›A¯½ÎÍí…§z¶|ΟÁdòBw>~u);vGzPÝŸìë³($&+ 4›È¶QûYìü á½™!üÄE2Ê>ºªòÜáêK ñܱÞþ[pGKX}{³žãŒ'Èa1'fk|ÇOobjVí?q¶p"ÜéëÄ‚†åð>% öJªó’Ì$böò+Ø ¡™·FáÐÙxIÙšÿœÂˆ®ËVø¿Ÿñ ·¿+Á Í m…}a—1Øm ½2®„Ãnc¨m–;¥ósKÃýÜüÂUÁ¥]٠ݼ êJfb]InnÍú“¸œ¹(¼›ïŠ}É^íÛH®Øœ#3fñž‰ ªlLwÄ G3±FØ¥ÇôÄÙSKÁ'B™SYNÿWɲE0í#â§ë3HŒëÞÿOMq…ÙÓ±u‡´m—bOÕ”ià®(þÉäœOˆ¯Å³§’QIͨ*ÃïkÑUÏØÇÌG8U3…ÕY+ãu%¬srS_Ýן€Ù¥TL2ÜR¡Ôp›[™¸+Ý©—&8`ÇXÊl°Ó½Uç¶xdÓ £Ï Gn18—ú–œzðÆÂ+°»y<üëÛŒz¹Ú\Þ> oWmb1ΦzòÑÛ4aÉ¢}0kÆÒvÚŽÆGäÀŽ–ë †Qm†Ð¢Ê??“†ïÇÈ›µúDÎ{%ýåaHv´þƒ²ùQäüÔNØ&1z¿äžÓìÝ·ô‚½þ¹ ë¥þÁ±ƒKpRN7œÔQƒ[Ï>ãä¿R¼ya1}ÿÙn€S¶À”ãñÁ1o>ýò#PÍ,ßFMºãÎþ¸ã;Êk ¦‹$€jëæÞ#ɣЙ—g/Ö@»x1rÎì«-ÊX‘©MmõʈýP1zØð©x7n_¹ŸEôk©ÐI9‚NÓ ØK®[=ävQus-ŽºgÇDfôsŒQ†øâìgPñ–½Z‡ø}Жë{ñØ?O¼¹¤Ä Lx0ñ&„ìÝÆ“óøèêl– ìÿ|›²…)lÚ;”N ´ãj#Ìhˆ÷KœÑì îÒ ¸¿'•½e 0d¸u6”N*ƒØrw<Ø< {¤Ð³òϳ7ïO#H%@™“ôe¢÷íÏɮƿƒîÂ…Ú‘¬ùâ`¢±ìNk…XbLµ[ñýÓ·dÒVcØå>„:úïîÅסîÙp~ʤ?Ì*BGç5ü¯ÿÈú•Þ\ÏS•ßÒÀ˜Ã(Ò¯ƒ¶NùŒM‡©Ï×:®¹Sž…È Rù®4]´ß×k‚W=ºo.°¼}³pØî 8}¢Ùu’ÔK²rhö~«Úð+!Z0_Ñ{_…[ÊI@>ãRŸU°9öœ=Ô†SüëqÁc-–¦Õ£ƒðÈþ,ƒðQÏTéÝ£íØôD.r®}™[‚ZŠçáEã~A·xPÿÓ|Å C‚ÉýûgˆñÑ‹ß;7ÿ3[^¡ý•rܲçÈÈu`Gÿ÷R÷3.ˆvÕãëÐhZž°ùyº$•Ô±Þ ¦ „ƒô}áTåY¸õx/Ù= g½¹H̾ †0éÚíêôL\-bF/|šŠsŸC²`(rטÚÄ|À`ƒ²pð5aaÈK¸µ¡Eñh LŽ@·×ù¤]k,j7Ç? *ø,æä Æ\öÆ»ñ‰ÈuÈ­h\õq/ΊÔh1Úg ì+RåËò´èþ(pùÚð_M7[ù»ÂÞBÑ= üÿoÓ(\ÖQÎ,´aùàôºýyëÁÈÔ˜n-㹇Ehçîüõ9žê)ÃåÁãyÏ¿zh½ºåFLæƒVÒwçÐÐk¢Ôd‚ö ‘#¾}Òôæ²®ŒÛz–/Ч'Ê ¹{Ž&lÊÀŸ¡¥C.Y¸á2}ø-ˆ2ëâs¶ôËÐexô¾&¯-‹£%+Ȥ£¢\]Až·çJAæ¼*Z4jo¯›H·ØÌ!óÔà)ç^ß’¹žëì½|…Ĭuáb][`ÛÏYÜÍâ9v;¾ót¸¸Ä”¾¡u™<½6¬åsnFñ‡Êzhû\–ùñõá»h1ËÛK÷¢¡ÑOX첺^:Ë^Ÿ+‚yk-ùžôÁ\ŒŒå?5‡ñ¬®¡¼+Cq þ›lV%ú_AÐ~h'¦ÄYR‘jh·ø½›Ý½Ø1j! ~öZÐm †ùû,Ù°ÆEø½í$‰­^ƒ—•Á[IK¢tÛ¶öícïB÷€z$z¶üV¾ÇA=äG‘÷_b^+&R/Èû‹öûÌøå+éáGSpi F5ãÑp~ˆjK\a™o½ð·£øn¡O”ÅhTï_²¥n-ZøüC¤”!¡%²¼3Ô$V3£©ùà´À¹[(P°ã¢{· K„ı9{üʼnãÐíÒo,·YÌ‹ê!¶ŸOªo ÄZ•¨¹¬1`ÿeƒÉþósÑn´ ŸÎŽœÐ#¹"+À7H’®S1ÂM«kIý´³$h§4×»¾†i´ƒ†ßIØ"ñ3Í‹áÒ2Û» c”ž`eM¢ëÖE‰˜Ó'ÊÇ…Kñëz¾Ø¨I5Ãè–Ò¥m ãNY`ÎéÏl‚ç~”\7œãwTß&÷”¾b:‹æ Ø£¤pð’@ìyñ /J̼i8BºO÷À´ ¢´u_½.ƒÆ?Q®ô/¶ø’ÈðL8¤Dúy÷Íë=léD1n;j?®çO~.‡U_^°ï­*¼qÌ>Xq´æ5Ḷtrúîo||Ò F­^J[‹eHb­,½®ÿŒ& ¸Š£;"™ö¤Z’1¯VÇTÁïK‚gZêüdz!æùDQa‰ŸPw[™¸Õ˜ÎÌ^@³4†ÑÇ:QYóÙß±Ê3—kÛñŠ;ëùÕYºFD•/¼v^P'¥Jw-ûuòÆ`Ö•cz‡ÐTûÐüþã“Ù= DJÈÈóð °,Ò§þêÏàÄ9+7ñ*¨J@I—Õû}7,³¤Uo±%þÁÝväÓÌ7hbs‡«q8˜ÊfúåAÚU;6éÛfRñè&*ßÇž\ûNéx]ÑúÎaÃq[.•¸•H|)‘Á#JAhÚ˜·7À”ÉÓ0[u”àeîõ–!ÍýüKIn,^X¤Ju¤ÆñƒGj©Üy Fâê§úøÒ¡m*XÊS'Ĭ QTißeÔÕ ýs!O.€ÁŸã¸G°Föd.Ù‹'߬D…b :!sÿ@ü_×$ÃT<úõÚpþgøAL†)ÒÓxð”Ù¤dó€Ü/ ¿ÜµøýN¨ÐÑ7cuhŽþJöcNf®™Ê¶ØÈSµ˜tÞ`U'íÏÑö¼çÊP|™†+ `ü|BÇŸ2a.î¹ûŽS.²USTø¬1ßñþ4Pw8 …sŒ µÔž‰­µgÒö[±u¥3j–¦~M¤ƒÒ·ÐŒÆË¬Ù½ Zþ´c}Ô[t— 1:7cÙÞ‹‹×=Å/ Ôxx¥÷€þ‘?Q'Lœ“NduO@÷9yÒÞÒ$L™,Ê;×f¢šÄu,ß®Me4åàW¦–àÝóP,^ÓdLøû]'ñùsyºF*bP‘$¥ÇŸOwÏš –‚¸÷uðÀY`ô;–õy$¯4Í>]Âo@áÍp¬Pg‹ WBýk3¼¿` ?t [øì„6ªìèb¯EE R[Œ¨ÈªÐáa2ÂÚVôâ¬;øiˆ §ÁôÇg9œoSŒí:ôØÈiPs>ƒ4¿\Í/”dÖ™ÖÄůoƒ„ÏA”k)ÃrºÜôh .>Ã.,s‚p«ºÿ®…Wžy/_x…vù+Á½µ µV…‚ó;B•hF©Ñs³²˜¤< ?féÂ:¿"_³ƒyT€ÌÔy°cýx\×5 Øßèÿ®c‡+\ÑW=þ7'â+ÇϾ³¤Ëí×3÷¿C +Ó*òÒ𻌿Œe)Dn²ý[1P—^â'HííEK#mZø²‰ªü}rG‘éI·Ô÷4¥{½ø“ä8J/…fœÄ£×é'í,LNë.ðœùKð]÷b꽘¯ùþâ\¢Å–æÖ²%ÁrÔ­c“ßgOÿÃsöVmN_3¥Cz.ó‹+æò›/ÃÌõv¤¼ø7äŸái¥ÄÞèÜz6ÇËO…«‘r )¡ËׯİõÉ–¼ÉœRÃÔ«LÞ!H]±æŠðÀ«i§©^åš!Mäïòñøn~L]úÃԨ¹y`ý×'F@›Ø[ô±£¢âaT÷ø¾îÑ)áò]ytí6>éàB¨?5~ œÊEfNÀ ² ™½¾™Mü{ÇÀ7X­ŸŸ»5šÜgöBlIÞNv «‚O¼iljüÙxüb®AÏ»{ÒÓkriÁœh¼u¹•Ž-Ñ ÛäU׵貟ŸØÕ‹ò»m<š½ÓNÅÃáO#ùÄ·qŒ³<Åyi®]‹™Jq<Í3uæ{7I`ŽŠ$OøðŒÕ˜ŠÓJ“O â¦LSnwâK‰/ä¦êM˜±Î;¦­¦‰êÉX¡u—½8rUw¾‡Gñ?ðR«å€ýâmÑ:n4øÌã3Ï«k\± Ê߸ҡüø÷8˜çMçC *1à€}¡dÏ—ÆÎçñf³pö )¼3}-;/#F÷.ÚB~Jî‚÷Cô.Û»AçâHìîÁõª=(ŒêcÚŸ„x¹ZŒgê>ÏêÕà«w­B#I©ýK&×CF™)dTí¢©{÷·W§¶þ«c¾T]‡‚é1­Z|-ÂcÞ·aËýñÜNv+|iÎ#íìø±½¸íÍ`êí—÷7Šò‚§Ëaß5.Ö+Í–<µLÎhf÷[«Ií¹òô‹;qL†¾ ?ð¿^/û5 ÑvdÈCȬø‰NK"´æ¸å5¶à{ã Ò¨ýßóŽ‚|K H´U ¿7i2yÇPm`DîŽ h‹Ías@Òí©ŒÈÇjsy’.Ùp¥r~Ä€sž{ûj 7kn"V…"¸mˆýžÿ]2®†ü‹9˜©üï =Œ}k…Ù2߀­éö¦â nˆ³eE$u‰2fëW“z~{@CëjˆœZˆQ/Þ“™gzPéÕ¢)ñ£lj`ö[˜µ÷;V†BÔ‘Lòçp"ø QâyS°oÊ)ÁÑf?Þ˜¿èën:)žpKh¶æüWGÜÑ¥Çñ2Žú¥Å\ÚE"ü0tú0x!ôg¸îXàC=-·Áº!Rô—zm}wÌÓ&£N=âGJÜp«1¤ ³/¢é ožÝx ï}b«‹ÑY¯axbÛ’™KŸ»‡ÓÿúI¯|#I[2£¡ÝV>ïðbâ»æ1O!l^ùÁZõ£!¯MÒµ)·;Œ[íÖÐ÷:ï‰ú7C°÷y׿ Ýs3ä*v(#»ÃcQq“#fYýs‡ÏûÒ`³“[lž¶áiC žEoO|0`ÿØz ,GÇœ‹$Ã{×/ Ľ»ÿˆ/§òùâ'»²D–²d/{$îœ!¥R”R(*J›í{öˆì¢$û©h!¢,ÑFi/í›öEõóýý^¯Ïïçuï}ž™9çÌ9ó>çýÜyͦ·Ô‰ öºâÔÔþÁKœ®·®¶Zr¡–¸îã:ÍŽŒÙ†ñ†Æ/4i=Ït½ —Ï'_±mD.G ê‰Ðs…ã‰ça„ùar[®NnÚÉÿú/¢GgÆc¹÷$êuv3??k#›¸Dœz<4á¢Í±øxç̽ŽDʈîZ&âLÎþ‚ú+ù·‰aüŒ¼M<+çüס€Ûu‡àSù0˜ ЇŸñŒ —Í«Ô×ñÒó9øu•Ýi( ú¦%ÿ+KreáÜèdØ<ò!¿qá¸(kkA[šÏ@¦f$œ‚›ìñÒgðâ¹üðÁXA5è¿_c.ësѹणóßû¿Žaº ³Ó¬ ¹õØe<…îBœÔ9’Bh§Ò[œ÷M™¨*Hç™llA&Xýk$kÖžÂn›¿ÇÙ×qÅìD¤>‡ ‚\ÐÂ:š¶ÍÏŸì2þûŒ-xŽ¡ÿã„• ø¿šDοËV$úEs™ÕqEˆ½œŽ‚I&xcèEˆ¿€£#éȌðtŠ½í€¨§ù`³ÍéD88Ùˆ›Î‰ÑeD‰ØD|áoÇàÕeô‘D§0ªPçÝÿÈ÷c^Tqœ#†Õ^¤Á_4aúK;þ¨£ ’™tݰ8û_!ÚM9ÄïKþg§ä'’wu,,…ñ!ÓêõßDVr=¿Š¡åÓyû1'H•}€ï.”âU)g6tÅ5áÐ`Ãæ½‚ $pÍÃlXkòIø’20¿\W¥=#Í…ÕK–³Â•»à³“8‡Âî÷SáZj(ŠE÷’OF äêÃ8\}ñ8‹ MÁþðoL0W…ŸO¾ï`p·êA]ÿ\Zµ-—¤Cs8D6F ÝÞ—²ÙÆrÁgånØ Ê •Ë"~9€mâ1By‘V6euØ• –2¸,µ†¨*G ~ß´`¡Uxê`ûë_Lꟷ˜àÌeRÔïÍ&|ãQ'D‡Ðš¹¤ôËI<3E„Û´ÚѤær^alI³²’0çgÌ”—£ŸßîÆ†³õì¹2ˈ‡…Ãcx£Ô*`‹Vp3 øžRˆGwLæ ë‡rïFk\z•=h¡Gbàä¡L¼òjç=Gù›¼~»_÷i!„LíÃ;íÓ°tILtÌbfGÌyÎÚ^¼W]Ûuz˜k§!,8­È¦½1å-£ð¢`'fª’ê°ÓÿſՔ&˜£´žlö™ŠÆ#]©å_ ¤“‚¯¶—{+Æc¯Øâ½Ì\H³Åúý œŠŒ"@ø5l¦.awÓgzŒ$í—w‚*ûÔ•š ů.À^š&¨¬>#˜·­™ô4?†òw@ÝO_ŒŸk;õ·¾qÙã1j“3ßrJ>8‘,+›©#„ÅAëÙ1ëÇägÒiÆÖÛ£M§_q~ ô¥8m‰ßŠ 1qèò&“ß æ;Žö€jr1¼ìú‰¯^æ¯×œ% òºáë9i¾Óí J\'¼áÑðCl®Š3^— ™ZÒ<òÕ´ÿâ?Ú:4oƒse?Ymm'\ß›²ó6Có?;hÐBìN&;Fÿ­ÀKÓeIì-D´ï=h¼ÿ ¾wN£íÅ RôŠ8žÜü)ÇdCk˜2MžÆ¬hþgæó•ÞþŽÇµÿ¢¸VèqXŸt=þBñå] ¶n½5Òm›á«¢´Þ!t'žaÚWàgÞ$°Wk€ ­Ñäº ìÕ8Š"ãÊÑü±?æt‡uUM¤I¿Ðß{ìe)ü”7{¶²„íwu2£#üÊOM~û9fO5ã™íðBï Ùø,¯zµüç¹4\úF­Z%¡Çz³0ëÞ|<Õˆ¸ªwCÛÜ|Œ¿R€Õó}èO§f^TÌBÿ™³ÃÒÌQÔYŠGúã#ålJv)y3«Šˆ—‡ˆêÄZv$_5Ñs2°³ÏÄusáûWjª¿º—ÙbÚ¤uè}à ƒWŸ"Ï,äA¸_´ËHãªÍ0{ÀóÆØS‰âLø3d¦ã.aÆá$³ÙG¤Ö∣¨£t¹]]Æ$M“á}ôRXŽ ëf— MædÉ~ÁÝEy8ªó!T’Zi4µ>'±É‹ñqF8¨ÚZÿçÿyW‘ Å.xc} ‹Â#ܨèê ðÉÝœNWÞ&0‰\/Ù,Ƶ÷ K Z4ÿ'è«}Å•áØ-ûM·Ÿ`†mE#ðXò>¬º)ÐÖ S¯æáÂr¯E‡ÄŠÒgICHï©|£y3®U3ƾm Ô)<’ŽŒãI~¾À™.¹9 >} ó]0%ê$Ûòþ\+÷©}Ï^•*Sݲó¸»å¼œbË›>o$÷›Ãa¢ámvæÊ0ú R%NÁí»ãPOFß OÁÆI“Ø©`ý°2Ak!]rX¤â7ÄÊÿó¿ä†×lÊàøG~@/§p:f™+Ÿ×;´¡±d7«c•Ä0ò Š@pÊ$H×¶f-O2Iª£þs+[%ù»Ç'IËOs.²2š×¹°s¢©“Ï–ì£þ¿}ÌÌë«*§$…á²*ÔÃù&™Õù¶I½ÆËׯ`ÜîÝÌ燺Y^ Ø £mèö²vrÕ”÷ÎáSÎlB!zÜíI1´ªð,—“ ˆ™Ae2^b`[=¾ÍvàÚ)ðkôPzÝ` ¯ë6¢Ž¯‹àÙaººV„f­ ¢ÿ~Àæý¹T%8 õ/Ãÿì쨟ðŸÿݵBÄ:žƒû| ü±èÉŸþœz| Ø´Z‡ Ìí¹ÏukÙù…=¸wf ¨Ÿ}Š¿û'ruá%òìJ1.”OÀ¾UQ³ÿ(J¬fnþ^+E™§ÛKX›$ÂçŠË€rÙXr¹è3‹dc„¢6BÛñQtòáW$fY8\²Vç÷}ËaŠl>®Ñq£æj} ›ž)˜ôoÎ{„©?X­¾­ÝWÊ6>ÛFž—‡¡HÜ#p»&ó«…âÃz äáhþ°b †T‡ìïËð«(&sOœ¶#ý숡“¦=ÆðËïyÖ'D§Õò`˜¸‰ï–½ÈʇFsË-V™>Zâ|©ê߆äÓ¶ éÞ…±qôýU h‚)£_ÒÈ¥ OÄ®Îú{ƒøØ#y¼œªöåPfŽ:Ex‡“9•ÄqÒ,Ú3ÝG/: Më¨Åã­€MÚÜ£9•îîæË‡GðãI*¤¤ßšJ?!Ôôl6Ü+2 ¿.$àóØQüJš; ;úŒ×£Ñ¡7/uÀ¡­3V„SW¾£]Éx2^e?޳¸ÃFMIeÓGœBKñp¾å¢)·,v†ÏùáÄtÄ!pÝ첑¿gãÇMõä²ã*Þp¹ˆÔhþd¯õBÁᔨv·Ã“gdžè$ ŒxRÛ+øu¶‚8B:LýXº9pät ¬Ø%Å<:·aÙÍ£ÐÕºåPyØSˆ»)Æí¾Š`Vä{f|U•G݃ןLÃ[s5HIª6dè§³Á -¬úZ¯! O‹*úŒA İ©ìt†¶Ã°kétvq4ξˆoš›‰qû`ÞóÑTõé+Ì'xühtKÓÓÂ?¬Óµ ß¼·¥BòƒmÔƒ™{~Þ~ˆU o¤gÄÅùÚæO°÷Î:þµE›ÐGUYKºäþb1UŒ$íxNY[ÓÓÈ:‡\GÕ´)9g3ºˆ£m‹9+6a´¤JžÕ.ÛÌOu)Ã"ßa\NÜÌ÷]ÆÚW¯ðÙÕNøpH.”•¤¥]W0zât*÷Q ½e\¸æDê~b6ùpbÏ3´ètç=7Bp¡é•P„±™z‚ŸN2Ü6Þ]×£›Ÿ«£þv>ÌÊ=Ö5"ŒT²¨D<5ÿ,>õœmÐÎA[3z¬Á«Þ±0Óñä”Ðc—†u[ÿ ûƒ×÷VW3þEœ?Ž yxû½PLóÄøîG¸Íexƒú÷û‚[jwð£F‹h«ÿª¹x«X÷ÑqÚÎ,”³•G•5f8ENƋиK쳡ù¢±(pvÉ=ÙçSöá™c¿„báN Ú“q‘Àìbò6»†Çžß?n8’µ cF|?þ“]Xþž(Ö¥çÞΠ7ìé§©¹¼ì— !6ät4à|_Vá‡×fÓ’4[ôÕ†¿¾fA“¸"ž˜tµXÓ jÓ9>Zþ£Ü©ÈáßLΒÀŒ½¹ß ³m¨ÌýuTC³œ¿Mï„=•î|Α†°)îüÔ®·ÐP1‹—ÍävðúïPꙕ_.§PŸëƒy_ÿ2?æ޽&EäÜñÏ‚Ï/þ¢ª›º–æ@Ü5+ ;< Joå{’çÐVëø–>Ó;«Àxl¤7Aç¯F ig"ñF‹;Íמ€,=nßð{lŽAð.=œÙítkÐ5‘ d‡p¼(iFN/¼ É£äèÁ’½¸ý%[ù® 6]½Šî¢²\&Y¶À{C=®Oûˆ5C¹gÒ8þ|i4æØ¾‹?Y¦`Â¥n®fQ³Ïá"OC| ¨¨a­°ÌJ †ï¡LÀê Tú&¼hä.qHcïûY t·á¹ò1a€"?R·‘Ïš’wÌ`ì’— ±Åßš™Q‰R,¡®0ª~ºàÒ£Ï?‘,ø°ß¾ùƒ×D錒$L* ‡4¬-®Åo8yŸi‚ràäùíàX×ÖÚÓ{aâõX×±˜û+þ±¬´èJqL–—Zmbî è[­±ôÞîNü“QB×Lç¿O¨Ñc®¡ƒØìÀþeIñ7fê¨Á$qiø0®Tÿ€œx?[JƒNŠ  –£äÁBM•zïÀM-À·YZô¦ázÅx:°§™ù®Ç' '¿u œó›mþ}øÕ ².R°Bq!ÞMGÌ>¦Ç?KìƒìÍ×ÉN‰J>=æ' <‹C£Ä8-r" Û3‚>üׇK¡Gˆvqìì\4½>‹¼z®ÊOêñï;”é˜s¹°µ ¼äiéÚóäËÁPtÈИé„sŽø‚•s*x‹ïÃ~ß÷8ù{4&néƒ4õWäÄÖlaS¬ qâiÒJ_¼ÿý …3 ~üùÙ(êïøNð±p(ªUšÌ=œ+±$w+>9\†ŸgÛ¡ƒ…˜é2¿¸¿_ðò]TIÆšBèüÓBv¿¿Ý`•tDtN°àg©8%r)(WàÛç*ðûֺةJÅŸeá–4QúHîªÞ/ƒ$hñ Gî_:µW{ü-ÓpÒKUéCï!\еó‹ÖÀ¼Ÿ»èÐM|éò¶Ïn$½W Ç'=_,ìŠQ5O/þar$¬VS`Ÿý ù À^ Ô"ð+>žnE·ØPËYâ”…Æá·™Sp¼ì%Ø2†NUëÔlwœÀ•®ÒÜòûfè¬ÃWFÈ“ÄÒA_­#uˆØgÊÓw½)ÛàM° Í»dÀ†Ÿíœ“þŸoAœÅ,Œ¬×TxØÅC‹£e䡜Ù²]ŽÏ-:Ïý¸³*Ęö¢T6ßoâ&µüMü\êo.+PVžU.<Áê‹xÅݘ?á¦È«\ʱÀ§~ד–Éä²ýxR~[îš|`ºouè^ÿ š“†[­¦s*Q^©Zd¾â3ܤø“Œã™#ùµå ¹å:Ý{ ¥³ÊáÇ©Pðò]†­^C¡¿^Ùù\˜/Óï䜰æÐT8¸Ê‰‰|ÈkªÂ–{›IçxqX²G„Î9‡š"1lø]<ó†éªÐÑgìa“éDÌ9zŽ/Z?”vuJÃŽÉ#©WêQþGŠ[Nû¼ñ;+‹äFh·?þ[@–ÆÝÆ^M7þ}¦úÙÂÇWÀ¡¥œìå»t¯³‰óë„%Ó^1žÔ…û 3G ~zá[Ž]uüåuTÕ#ߕŜ×/ÂQ?¯Àš¶Ö·Ñ…¥™ËËp«»3n[ÜIÂÜž ÍÓ„‡i3á‡ùh¼=á=zÞ²,¬2D=ºeD ÙÑõJ®Õª µ;êa÷òFô<µg¼^@§Æ-‚ˆª5¸wßúòa|>¹:»JØ¥£cp˜Æ~¨ ÈE‹ÃÉM‡r<Ôºì§ÝÇ[®Vølø0x6™þ{ÿ¼F$÷GžÅ ^e‹ò M&¤r§–ÈáÊß æm{B6nÞ!8yý%\¹Óâ}à›2xo•.Á$êÓÜ‚•MpÏæ:ûæ1 žI¥7¼¸r¶J›à‚dLÓôÀóÅÏÀ,!’y?†îò‚Ïëï‚”¶—þñ¥ÇнmƱŽ1ãŽ(ráö´]&Ë—KE­†´~ÑmH=Z åj´DÂ3Ûðû…Ïñ—Å4¼V&ôN…Þzºdêæ]° m=>am­@sæøßïymƒyÍ&f¹‰â²à8Mo?³\ú?ï4¢o¾d¡où"œšR ¹ñga¶Ö*¨·º‰«GÔ³W¢ñ¸Êµ€¼1ÍÏuÏ`½K–`zP-Ñ™{"~Gò—?úP­O‚ë&Ç ¯¼2„â=:ðby¸à£xI°¼ˆo÷]ÂÙCz°½p2™Ø;ûÏMñnбûòjÂã±ôj6I¹YåóÆs¥ ¦íñ}t»ØÎWCø±R§²¢á»ö^”ŒaOTè?z_Íûj¤P¹Õ…÷Yâ{'WRn á™Eh2œiGã¯-x4Ó’¯/;Ë2S³ÈÂëí°¨ój.ã»dæCÏЛ‚öÁu¸ï°ˆ@yëqxïÊ[õo ¦†Á/³Óx`ÿ{ôs]HSV߆ès¸â5šÚ½™_Yî÷¼^“!ÑôL¥$OÚ¿™a%º©éSÅ}ú`¢)Û*”‚¿w>À¬‘tò]/üëi÷áÎV’Æ.àð¶Ý”{ѲhCT}uµ-·ºô0¥U+FMaWÇ[à %jµð:+¦€Î q8pl Vz•AüŒ^ŒŸo@{m“˜Dn.™Þ&}äßYS®xÇo¬Á!ºrtÒúz 'CG]e7↿ü“l¨Ýã"Q‹£Œ}´ƒ§^N šŒéá5KþrMøéÄ<ìnªäì¦ôÜ2<³ºŽù}4¢þž[Q#'”L÷M‚Û¤ñ‹† 7žÌqêöðê@:Öñ-DI'ZèrŸ0üp¬–´O{ ž’`å¹ÛBæ­&ë•ðFzûˆÃe¿…Ô3W‰¢lÉvð<”‹f^mpmÖQ}÷^œla·,&ÀÒº8Ï«æ„Í*7'ׇÂ@`­àº­,”\8G|²ìõùDÕÚQ¨½õ${yónñL$¯«Þ;ÍïІY½÷Ù~ }¾­ß ‚ÓÖáʪsùu(}Ñ)x2r óê»ç–sÈ­Çû™hôeVÝÔ ófD_I¾{a—pçj]rî´wÚ<œzÂ$R؆kÎî‚‹EÅ$á ê…(àMäšþ4étÝ“XÀf)iѯOW⯇ã¨Ê;ØõÛ}JS!Ħ$¼K(f’ýÉ IÓoé³yçÍù’÷ûHØ|KzFíØ´z!I)€µ—hýßÁ|DDé¢~'¶k,o’”f+¯lÂM.óøïßK Ù{¤v-&÷o|'JN)p!½?J_öµ lù(Þ» Ete—$tIñÁØ5.äkp:qùñKøå”ªVÌ ëë7ÁÉûY0é“"fŸ3áÃf§Jïçx?òâÊX{²6{£Ú[ª6áñÇsðrh‰ÐENŸŠ®ÓïíN†ƒëô„Áˆï˜‚>ßÈÍ û—8UúYÎô¯yðŒ’|ïgNó²@N#fЧ úfÿ>7.«îÅŸõ'áeÇAºhý^ôÄ•ŽkäÕR·Ø«iÜÈP÷þ\ F¶5 l×á­Ãpì¬&±×дÈ}‹?Ï={áè…:¨ý[ˆ ¶ ˆ» ùÎ+óˆÿå@lÛ4~~ZcŒH¯ìSÀð­Ìbš‰Y ³V8ÇPY!ô~”õ>’PQéÎåt¥xE½&íLmÄ¡Çç`üƒ&öTJuí¼²›úNz€fÖ¡Ìo7ìýiË·å¿" –o‰rw *†BIãaü2q%.RP£2ëòðšƒÍ 4æ_O½ÄÙ%§ñ§R«ð½¬ÏK÷ëÐY­žäß‘'û³qY<Ìô\ƒãÆ;‚õp1‘o†Ù®6p}Ö8¦¾W3¡ -΀AXß!úÆ$îR˜Kÿ5m'÷ükðªá WÊ×¢—^Ʊ“GQËF~DÁšÿ™‡ 2LjMË׃q6ü„ò 0"r¨ª²œkøÎVáT1ý9~ý؆9kôù¤óü{ç3hПO Ódö_%¹Öë 2™Å§ºð!çï‘mná´½ÏÊß<_o]\¼¾“,øò\°IGÍÉ“çëmý[ˆÓõR1èˆ?gÓªcëéj¹z`VY˜ý³vÃ#›.v¼!]u~Ý6£ïxŒ…eYj~QýT9h8Š¥³&a‰L þá@Þ«?ÃsW…9zðSz$·hˆÃËÕyln‡ œ4n•¤C‡q“9qׯ3ÒÒÿþŽ§Ã«$~S2,Rë&Çd‘DÁ~ܲ7™ü}ý™UŒUâå›×â|çD”XÌǸî™KQ‘öµ‚w+XOÅ2R¶BðòëF´®€»•éW}Oè†ð¡Dn½ü@\kdøéš|XÑŸBCÏóͬ«í8=Š‚¥ÓprMk; ±^Ñì²ÿq–>‹ÚÕóÂí‹hÿ[=ªxKŠ _ŸI³¹ü¾ï\¦ÒöÜ÷»á²;?‰Èæ4X½~õЊ!¿œÌé„aN°Ñ}.nóÉÿî…nkóùXh·^Õ0?ú.LÒÃgsx±&HÜ\‡ºuqüVæ¾ÂšÆäœ‚OÆ©w²Ç>ái7aë·gpeÿá€ìpKy»Ò\Èö Ÿ©Ñ‡–âdéµbÚ”•‹$¾1ÕÁJ}·Áï[°ï Njløz­y~S£&ylÅĸtª‰­­¶$’3]0èï„w¡?}/žš‡«µ>C¤äLÛ=‰>sÞχŸU‚[ŠàJè{ܹhOŸhµT‡¯¾\ÇLw>‡+V¡íîÕ°öK6©q\6ö KÞpäÎ÷u‹ðüû‰8`«Ï¦éÀÒEñÐdßGM³ ¶a ¸?¯ogiß”ÉÇ›ÿ¸:Ï+ 6LmÀ/1L :«@Wg´êÁ™ ?†6,å¤!z/ÈÐ=âF¼h»ÛžÎ`ýݬA&Õû#l¤‰kzª‚, PèÄ'òôû[ñ÷ž iaëiìïÍxbž:¾Ù>È™ªqn¿(]ÿ-fÊYÀ‡`¸±,œû¿1àÍu¦tqßÞ"BÝ*xJŠ-?7î¼õ§ ²§ø%¾ÿÐS8?3BtÀØuÕ¼Ød3ºæ¾’¼4áßÅG ÀîmÞ§ÆåZBiñƒdâq0foŒï;’kDƒLAAD5»ØCƒŒù¯_”믟GM¹‰ÂW·º|Ç“0S©Û0ˆ_^¶;âDœeUmáé+þµ4(†G²?àaÉ›DRa&ôg³;ó¥éˆ2C>³&*„¹4À¡L \•L‚ÒKHïô•t¢ói\ÛmÇ%ï?Æä-W£À÷R4æ¬u:"ÅíõÓ…!úøY9 *÷zÑB_¶ã%ue6Ã/ ››K´ £t-¨Y —ά§i!ð·ó=®ò†xhðMrôRê1’¦pœ,ã7›Ká×Û,â¼SìšÞk~`ŠŠ&*67À:ã¿äÊÈL´8’»M‹À´ZSm¢A¤@ªaFîd¸}Wøƒ†w/v±‡ëhÿH‘íkòôÓÖ{àd!Ê£c ª!=°§au…"¦|E‘Œ8ÌŸ$ ç¿Å /¶”ÿÛ‹ß|\uï ‘j)®P< y¶{Ñ¥qœÛ1 GÌ!×H{5[6͌¤íVØÔ• ʹŸIî±X —Z‡)?vÀ¥‰G0S8ƒ÷ß®À‹G³aÍ JS_Àºt?Nß‹k-#Þf©i'gâ‚ËjÜèOœ2)ƒ¢|E|¤V!K4¡Õá(HŽ¢+/.d÷ß"«Êþ€ûîì™ÌëGÉ6¾P¯Žû˜Ã¤§vôù‹¼©`‚»cyæÓmpèïh¬ÿ{ºï;’_~K`ºJ1Û1{7Þ«ÂÚT*c ?|ú5³Û{+·x“’Q 0ÛÆžúÞï€Ès§ KmÎ +‚-§#¹GÃÁ±r)pgâÜL[›Ní·æ¾Ž÷åÁPåôO‘tâÁøi ›|–ð•Ð9.\»B`N¨ŽPÝׂœ¿9 Uö¢\Œ+y7g„Àf~ ,‡_û5 ‹‡ÙOÀYû.@ÞÕð$.héúó`«§Ž‡ÙÏæað—¿¹ª˜ö«^Á™ë±×ú wW¦GƒG‚æëL¼[r‘¤”ºñþH_Ü!~‡Pöó¾úÛÊ|¤7hž™Ê¿M !^‘Ðýf:ï7‰ü6_Á÷œ›â¦ –~VåûÿƒØÇqíí!˜cL†É÷’yoÀº`eï ¾•á>™[8ò‡,,FHFë Èì2ñw±ëmv|­~†²‡Gº[aྋ¤Y¬ ŽI~BŸ3r°ÿ…,ÛÙ'BF(? Z·ÌèêWFôÒé5à{.ž¼Ÿìô¢CŒ¯Š´±[‹÷|pƒÅ^8 mSLøŸ@qØ3|25mp÷Ðç÷ß©Ò÷bÓiÓÂfبÞz³kÈø¨™Ü×uü3=‹swM¢_%;p¦L4VÄ;Š ƒ:V…șКœÎŸŒñçìL ‡,4iÉÒˆÛ£ÏoZÇüÒ¹`k/Ž:]ú\jó^¾5®¶ìTàNá\Ëz]áý̦*R%]SzÕm#Ãí× ´0V¸hå|d¢.3/˜Ï½ã ©ò~lØ©¦LÂl ßÝ÷œå‡ÇŸú6лÑoèú°ÙF|ƒ} lª4þÚõ³5Wà _Ä’a8q£"3ò*l^*ê¤'´Á”ÌõÔë;o?ì98üó˜`wZïª'½¦ZPèÁÃ…3hõ"KHú—À»ä*ȵ>¶Çà&譱ῤ2!±6-ŒÿáÑú(X*.$S‹/âó¾ m¶ÀW&"˚ć9/àIò¦tøi1^ÛðÔIgõ¾ñ~7Ìx—JTK=HOD|]eD—ø"²È‹`8¸þgdÙñ9¼(‹ü¿­À TFçMÚË-¸×>k®ðº vF…}COC÷±ÐTðF4gÁ˜=Ãùã/ö8áiµpXFàÞ“{¸ùƒ ϼ7åGðÔ¦‡x`—€¿X¯‰‘gûÉ´uåø&{-·†žj¼aY…õKaS¿uÈüÂLVïÁšš)Ôs%Öù»$vC§e$v¾“¥»ÃQ}X ÷Úºnª¤ñ÷§¤¨ZöAžõ™ÌK þßXмñ¸ñ‡ u³äÉ·ã¡ÀÆ—Þuû·Œ¬©ÅÕ›øbv1ü´vú|â3îvu!Õôæ'4 a£Û5–Yä…Iù¯!7ó¶_9 ;Úĸþax«As^§Áí˜G¨uò/¸>«ŒIÙÉž“ds¨Î+Fï¸ÑP¥`WkbÙŸÈú‡ç9íW¦Ùãehïå›tÏGü4Ë…ŸýmO[Grɪ±>U‚ªœN…â3ÅP>l.•îz†ÝsÖóç­èpu.uþ' ®}œŽ¥OâáÇ—¼¸i4úh9b_»ÕÓ{ϦßyÀToÊðU'››P´f7Z_Í:¡fû^ä2µÌ`íh̹·—ÌNFçë —0½¬ùÿŠÂºÃÓÈ(×r²@¯º–¿dó;§Àœ™¥xc:å9ãÝáЯéÌÖ¸ŠÉNTæËŽKñÎ7»@Ü-ŠÑm2s.¹½øY§4ܺËcžEÁÔIYds:½©ÙŠ‘KQa¿æŽŠ†´O‡pßñHaì©õàªÚ†cvMàʃzž+.{ ËžÂ\+Q¨W2~ã…7dµ‰îÓÉP^äFË ‡‹Vžqr¤p¡¶ õ”V¢Ï·âv÷ ¸i¾-no”>ƒgW yý\u~³+Š©mÀšIèäí¾ãí}xÿ§Ïo¢ÅnA8âJ2ëòŽx'r\í<Žv–c»Æ,Xwd$]Þ*¢ßÌ6Ý2‡ Šæ°sÑ0œØg@¿~‰BýRy^Q5‡ãB¾ôŽ?䒌בD÷yüàŸGÐwË‚z†¤ñÎK+ñÆ»]ŽF‰G¡ÇÕˆú‰ˆðýeÆÜŠÓ»º©ŽnP'äõF„Sme=ž–¸¹n/œØ+úc¹–?ÞnR€œr;¬¾!Ë«†ß„6½Ô†)cÉ™b3¾£X†µ°å²'W`ׯdˆöxÈ”c$yý%H_ô”$¤­G5LnÕ=aå_|¸y)äïvä;çNç²uqk‚ ýÖöòµ1Xý&[ÎÂklJCÄeMöfô.¡ƒ2À7N¦€>ïÒýt?´Ë~Æß- 8Éß6­·Â\˜ ón{ÛîÍLJú`EôH^m¡A{wïBý+V‰ñòòüyš8í›M5¤-ù~•\7º2#‘—ÂIÓŠoÌmQnP:É/êëpûùkèuêŒsVÒ[¿ éçër0QÚ„/ŽáXÚ­ß«Ås©Ã¿ø$„bb!xÁ)œ3èý¡#xèIt3jÃÞW‰lá )è¯þÌ­çw±S÷Äyô}Ž!Ó#±×(ÕdûS‘i@Lê*øEZ²Ò¦´¢£™Èÿ‹¡óÔ'Qc[îo½ûFâXÍx:a6•û˜ŒýâÎþëÑ‹¡Kù°@E~~ÑrºÛþ>~»ñBZ™¹ÄCì)êÃG…/H4óÎQà ÙÛys9õ“4àÃj ø£è^fª,Jkk‡s×7º¼ô’.w^­ˆâ 2·dÉÏ´RöO¿ õ—A€­2¼º…WlUçdzxæG+öÈæ'ŠM™ƒ±Zœ2ÚmÀi­&ñ/4áR/ð{W ©øÀ \f]Í´Þ'ÒáÇwÒ;ö±8Íù"9Á¦s›I¸>ŽV^ÆE’p…ojN§7å€ýƒá\µ}:;.”ŧÜ.#}[õ~ .ö'Ã{Öð°¤ ºHé9äí§æ¨ÇgT‚ëN t×½H”GB%ï'‘2xÐ’Â;; ¸ýh,Éñ ©×öAè& zie*¾­ÇuZ¢q̱ƒò®âè–r¼·o4vúБ²ôOåZ䓨TØ+”m‚}[VQ5Q:×˃.o1£ßƒµèö;W`²’H«6bŸn/IŸñ›Ü¸3•Æú©cäòG°âg˜íRÀYÞºG Cÿ3t9Ý#{~A`]4Høòæ´Ûø§ˆMk…{æ"»ýÍÍáà¿4ÖÏŠæÕ6a¸ëÉØIÅ14µmwgÆôÙ 7Î W›Ü‡Àˆ X¯ûLìš1evè.ìÇ:iu°¿HQt™mµàO7¬†­¥§°wM=ž½#ü3æ$Ëœi@?Ï=ƒß5¿£ÇÈ1‚þ½áº˜ahôýÌœ=—+o¦»êßp= „—¯“šKKè˜!±Ð£á åózÁ:ôú8(òÒo’ÄÈ4¥M…§ìäU+ШÍÞtœæPÜ¢ ïY²¤ÃîÐè—Œog£Wû?˜põ9ºry|”ð˜ágÐ+å,ÿ…‚w_F;­ )Ǧ÷˜P:/ïÅ=>.çÙ&ñ…Ä|J£z›¿ ¼+Ãßò೑?½~- '*N¡Q± Üþ&CÅÛóéu¯i,ìQ<ȹ°M•­BÕÁùìw÷%1óyá‰ó`› o|ßÓ veºù‰!Ó³…:ß.SÙC7_Eb䊰¥ÒŠŸžéC¯ÍìÄ'Û®ßï¿‚{Ï OM?WÀhçK¸ü©ÏY›ƒÇV{Òç'®à×úXèjÔH;KÚk—§ÞV &Ÿ"]oQno)¥7gð¾Ì…Ü!É™ì);Hü|8ŸÖ]O`—!1ÊŽ&]˜íºÿC.ÿ¶* ›V¯‚Ã<±ßæ0n³Jã-¹‰ê®?àhH¿5”“+ÃÒA*ê,4´üÁL##zeÁ{Öòùìþ‰#¿-¢/š#aPGˆw l§IFt†Ñ3Ý!´b² ~:¡.XY ))édù[xEDÛ­`ì<.ò8 BäâŠG]è1AÌ)eåíÀ¸²ó_ ±¶TÈSÍ)E¸ÎÓ&ù‘”­þL~U(ŸìÍ<KÒÅë[Èóo-¸5î*KôÏäËãq[~dpNÌ-Üad¡ÈÜKd×ÐÔÂZ„§âùâëé‘kî$äK%üû üØÆ¦Ù€ÛŸÅì‰ãk¢6ÁË]pæŽòc‹×¥>LjøÞBܳª+ÆÜÉ^/QlGª¸Õ`ÚÞP3þ8ê¼ oê‰do—Ó¥}ƒ<â8ØÝ帥#ŠtÖé@bãRšÕ¾”žBv$–îZðU0§å k¶}ŽF¥‡™×sYñ˜‰Ö%Àä%л<;j¡jÎY¬\|ÆéîíF’¿«ˆ=Ø.M§?„½>BæÅƒPvN’÷VêðÙï†ÓùâáÖàœ‘³÷¡ì†7ö0¢'*obÕ?~®7•ÐÙý64>žƒ+8Žl¬Æ¢Íeôs–½Ú ‘#ñüž‚«¦…dت.xÔ= ̦·£Ë|°?u s=æÁPá[Â'ûÃÎ&/ž¨Íëž&Q)€ ÿ fõ$.» £ß”á¬N3ªIcWz1XlÅ…g´é¦ïAC³RÝ|©n³?x¨mdRÒà@ú–作­ä¡…IÓ=ïf¦Ÿ(ÿsh3Ù³øFeáƒëç‹}wbýýd(LÚ Ò£íhˆþl¸Q5“yHÁ¡wOðäF! ýq˜––@Õ¤ò§X¿ÈØèÇúmä<ØhJ£lFa¢™›{#Ñð"¸þh<ÿ~k Õ6 SEõI¶í42Ñd´úcru–Ï„{›4pÔG˜Ð:ÈÿîG«kóHŒŒ/ÿtÑ…GoVãë§â•]sÐ;É ¦DQµÞkĵníüÅÈÂÎa<äŸ!oZø—[ºM¯À·^=•å ã… ˜¡zàâ[5á=G}¸Ê‡$h¼¿›ï†±lj‰ýil:ˆ‰a(ãUG'P;ƸnòLØg§C¿KázZËøÂ7oÀ$ï9RçâOè[2Ž~Z{@8éš½Z÷ë*|ÙvÚéãž…³ËÃSaæŒh¾Ô=bdî‚^‹·]"NÝ›‡ÈN"“·<'{-M…’†£¸à¯7½, BáÖu8«²Ž5 §G†›Ð­á8ÿŸ=«»‚‹ƒ}Ÿ6o8H;œ§•?®PÑFOÏòåêÔhT}8ÿ9j4xF_ç¾K•øÕÙ¹4,9*5âç¯Êô‹Ò\ÞQñ >2æ ~úÒ‹¹ª–üŠç ÐŒÜFMj_R¿š*Ò¸ÔÿšdÈÏÏÛõž’”uæàÂÜ$j»s7®È”¡Ÿ’*ÁïÔ+(³¢K´5ðÞ§葌/~÷Ò)#Úi£¶8lÇÍdÿA°ü4îû*^2þ&ð2[2¿>“ÎR9+t/HÁ2ü“c8º­¡ÞõÁ\ù€1˜F_eÖ½Þ°±"¶¥¯ßå4©N…~TjÞ'NT¥8^-á~A ©ãŠ;(ùÑ]&¤¹vÊôæc¡gá=Hȱ„í‹pjÝO”êÅéƒx``MÓVtãXÜÏ ^Üc«£bè‰t¦‡ÊÉÐ9ÓÑÑj8Í?¨Jc‹¯á—-?AY¢N÷!4û[ŽeÐÙ¿ø·’÷ îE9ZÑlÙ0ßn+?zx ÛUØ+¬ØôIоØ;$üþq®·x }?R áè³c +Y/Æ'¼Áůݱ²n,n{”¤/A/„^$êUGùØ'˜ýKœ¶ÚºA÷¶Pô~ÓÂFH_ÀíKß@žÝ<¾=µ6[L!ã± q;xè>=.ðç9W­ø¢neò*HÞ÷áäæì@ñ3fŸYÍ©ñ˜X­[¼Ù¾~U>É¿‚ðFú"˜?ï¸Àça%ª]„gn’¨‘Ršs—“ý“ÉÀªfT_* ³­ô`‡Ÿ·˜'X¦ô>oùEn}°ä ÅÅðÔ­ òÌî,Êj9ò~%¬à¬4„Ãΰz:_“¹/ÝÌÄi¶1ÌWìšpÓkqXGNAð\<·k$º`]ÞAXà$GïÿÓ sKb54¾ú7o 祇Ópµk }%WV*Ž€{¤Hò~™÷XøÈ±ã/ðÄ–·x/H›ÏîÄÏüÌ!£ æà‚w¾¨<ò4~wºÅ>Ôé >Ë,‡ö@Yy—XE†ÁþÔs@S^Ÿ¾Ûàýg)&¿Š“Å$¡[Á†çëÄäCò`ååâÙlÚK°)k5üR‹Á? ÇL<ZÁ<øÙGü´a?¿7˜Ö%Âמ W逾³î¨éè©ã¥@Å`7œ«Y38‡è<™WøÍÕ‡ü4o‡E!®äíoÂö/CÉ©•fTá1ìrkô% — öÀ¨Â¤dó œ¸Â|CfÞ48Wÿ.4ì¯Ñ¥ô³™vh4¯¹¯L³•Kñ\ sfµ€\øsØp.Jól2J<ÔvúÔòÌw]A?Ýh0ÓW®™‚UòÇPUn |î‚ì®@ª¨Äƒ7‰Ó¼÷žØmÿµs+ a é–³G‰ô"®­Ê1ϾL«~BÌÖƒðÀm+n·õÇ}ÛhFl$œž:³ü£fÀ¥å–têÝ:3,¾Ž²¤é:åØ3V„ÏzgÍ¿›[s³ÃN¼µ- öÏ6äòšo„ÉN£ñ¯÷¯A½ûáö×pvÒyL~º,ÆoöÔ¾Ó}œ{‚žàþ]…¤b¶ •qª'ë»Çr-‰xh“Ÿ؈£çìÖg »î^ÿUá ¼È¯cd<kMuú麀ve,Òø>æšýdÜ¢ /›–Áh{è{ÀÌo#•…{1pK>„I@â] þñ–¼TÀì™CiáàW…׸bÒDX X‹q§ÆÐµc`àÐ^¡°Fy‚síy¤äìö]wî4Rbú%Š´³ö9s8X@o(€Ï±3¹óTG`Œ’¹"Æ|gknÞP 8Ý¿ú²Ü’Ó¤ÕI‹oKßHÌRýhÞíB§­a¨4Aš*½XˆöFÙ¨7o[zQÜn³Øð úØb­ÝPj¦x¾~Ã¥ß8Û#‘Ýß2šÌ­2"õñ¤lº7$¤ [vŒƒÜï°s– Ý»× ¤?Íe.™‚E-œ¨š&PsÖǯ…­Bc£‡8þµ?TòlÇEÂçjô?, µv-Pª¨€m£>À³­y8µ…lXÃnë8$â§–HÖ8] V ~Ý `\`Lßz–‘ÊvXðù=±^kˆeAM§Ám}<ÚèK$‹ŠXHš)ϘªõJRäR«<ü 4¥S–çóM1%ø –Úµ ·jã5»°zé­žxcï¡§ðh8†—(aâ¼P¨Ó›è´DûîH[;5‡r…ªåtïòò3µ©Ö•­8uLýüs$Ÿ'w˜Ý['ˆ¨Ÿó+ÑûJx¦v‘L‰ tkì%mËSDiäºH›Ð:¥Dï]žÀp«ÝþÞê¼À×c®C§„¹êòáG1ÚU„>>Eñ‘ úÞ NÛ%# ü'4@ë„\žPê—.«&Ìk˜6€2Ï-hቩl'BšöR»Ï6~‰‚•%o„ Ô´éuúÂ2¯3»¼5ú ÝvÈcUR„#{"qÖ¯KÂù·Iâ''º÷WÒO]aãmª~î7l™AOù€O7šS/íqthJ/;(¾‰ïÕ¶Àœ»-é²¥§a¹-ø ˼áù®Yà¿Í‹;˜ãžuÚ8<ÌŸJN¤Ëñf%h|?ŽÍûãaóŽ%¸UÙGuœ…cÑôü÷@ËÏÅ)?¨­L¹ü[›cÿÁhœ4ŽÛ¨‡ªK5%—ÒÐð«L¬ò-V´TÃ4—Ùà>ï²ðÂZGˆÿ”…MS…xZm?\ÑgÄñ¬#1¸êtüÊØòHšÝ|–˜øÌÓÚïÈÄ™ïa–ô¸«ù•-Lú ÆYÇHè®GLµëÊþ™cjºÈúÀŸà¸FÂT­ Ño( ®Û³7Ù¯ °Cy)‡aóÜ¡_·™ØJðá{Ú‰¡½Ï;âIÍÿ^Gƒlm¾Ùl L‘ŒÇ9çÇ@£ûGAÛôü­1 ÓÆà»åSè¿@50耚¶&©Mĵ.æ Á=¦v\æ§Ú±UÍ®|ÿtkž¦Ô>úe,NÎû.+c?¶¸Ñ›bÌég$¾°O©ýïÜ"‡ÏÞ©™hÃå`讕xºË _©уY‚§KÒРÿ »`<‹¸èð±¨îä/p‰—§ÅëbñÝÐcàY.ƒZþ7)^Ŭ¿6†øWÜÁeqdžæP,•ïÀ_o=àD‚9Ê·½Äº§¾.»Oàyõäý‘¢)ñ„—ª‚ìsÄk^‚c¸)Ú•Œ.3Ë&®]ì¤k+S¨LDÑyÙPqK…ÎÇ߃ëÏð®s´ÖL¦]ç…$7Ú•ÿæžÞ%ðpBe¼"áî´‚ï›y­¢÷­s¡mÒØ1¸NªQ(\Ò¦-éÔv—,Úˆk›[pÆÊ$ø2ÞŠì\¥~g¥ ½ÿ‡°ÝþLÖÂ2VŒÃF<†ÜÓz ÖºwŸÆ{ æÌ¯oÉH9:ýÍoᬠҰwC8´yn‡7úK˜ÞC#xGvá†RÜtÈZmÐÒOTw Í Ý;fØËõXê²:D\¾–•#›¿EŸ¶5-õÞ亳 ^(ãdÙÃ0BwÌX©HOÿz‚ÙM½xar™Ðgûaš è¨ò’Ûŧߕ Î[xΦ‰\ç§ 5y¼•¿\2=›RàôÚk 1ö.ª¿Ø]ïjxžKÒ…3?a[û?’VYÈÏþƒ×S6@ϦÞú黢_Ä1çÞ~ûرujl%I7ÍyÞï/ ÃE ª¾ë¤(«³æ¬çl×}K¸i¶Ãý=°W~þvnƒèØ6B|-Š®”¦#x2hl=“V³\»ýl”X'Øv{€·«Wxç“¶¨àûi?ɇ¨¬OãÞDzÄs[ÈÈœ4Ù/§Ï4ìÐxZ½ ¦ê—²Á\÷^N/@w““`ùáÍÜX}±MÑü{¤ÛáîÍïä¥<ím=€G¤^“(ë6ò4ËŠ‡ÍÓ R=°Óðñ»ì C>ìERµ˜ÚOZkN7£ñƒÕ¨"O~/¯Å×{ ÷mž0¹÷9昌ëâ0·¦Ïÿa+?7ôŽTd“J bÛ~Ì]Ò—ëÓ3æÉSÿ0p,ޏÓ§íuê âñ"í#ŠŠÙ•¦=dãŒø0}#JîŒó[÷‚iÊ%Ç#»GqO]üÖÜGZzåðÕÐð¹M“Üïæ‚ÝZ0ùCÕ/ªÅÓöfÏD|xkU_úŒœðºÎÚÿCvÐSǺ þÌÌ2t'a§¦ÎØ1”^O•!gwB¾BÏï‹ò2Š9>l"Û‡ÎÇÄ R¬d6ñ#iã×ÐÖ+Ž•tÝë!Â7j_Éîj%.QÜ@~<;k{4¨–f „ç,Ƙ„OdDD8^_iOÔhÌ܇0íÞpx®N–Ù7±º¢ƒ^|Uí®Ì ý`§öôÁ…ñYº_‰ïFìÞ÷Þ}Tã7Žn†â;)ÿ|=’Pòé7Áî `™¸gÈ7o=Þ?âwHHLi0;YËìì6(+US5ÐmÎw˜†që]°¢Íª¾‡×©TÿúV8.S-Ô9ñÃ!Záø×4ÀÊ•ù@Æ;>®ãù3Ùèqþ¸£[›‡w‚èæ§lÙ‰Ãxt›1×qˆæóƒAæwRt´éÖ¥ð·ÃAøbõA¦ #Ëpv[c-Æ´ùûQ²lÌX.Öè˃Ü‚hƒtèv5>_e ÏY= ’Ï7â²r)ºhÞVú¯Þê¦YÒMC¬ ‹ÚðóÉ äËãÂËO+þÀå5ú\Eæ&ªÂǽ1¨=ßé/äßO„ÃAƒTÜÆ&ˆ'Œðnî?Ò#n‚§Ýñ÷¤øú¥uè û转Á#]‹„;g™KqÀW®€ÆQ¼c¼.öŒÎÁOÆÓدEFpýÂ!ežr;Kq}@èqàOU@¢ø8XÕwžMÝ•Mj“ œÏ:â÷Yòôý£‘(n¼š~;ã³ç3ÁÔ8‹%šÓãÒ{ <Ö Š ™øêñ¼]Sš>I•-M¡àúád(ïW~­Ce¡7‘|Õ„Ôb’SˆÓþ.ù\þ0dYÓ£’ݰ©ú3t”=e•–¸c¼7nwªžÇØöZü¹m8µ.7ãÑ’áôg)žyá/*Nû 7EϱâÊ©  '?Ô±áÒ»pìÂ9Âö¯òÌúdKÃQ§ûN™· ú¡Àõ~ÊÐ óØ øÆ®üú Œ»þt.\ÄÏùÆèV‹¢b½,âÒebÒ®HÛäõС» GÞKd'Ã~ Û²ô±¸¾€Enâ=kpÓŠr\}¡\ÃÔ¹„Þô?„Ý’t„o;'ìñŠQôÓQþJl( [óÕÉóy+îOt„Ì­wÙîØp´Úží­> ‡F: 9¸íøƒs#¶–…ÿ€}>yË#ñ¯äÐç q¯Àò,øS£o¦òÖS©p¥AràðûépÛÜ9OæÂ…¶¶¾ÆŽŸ_ØEï½àÞíà¾5 ŸuBÆŽu0ïþ0aK¹ý}ßLö\e5{EÐjÔRø£ú?ÌC¿ì”ä"Ë'Àñ[:8Ú§¤Œq>»šÑã{) O?‡'ñÄ=ñ|ê6sCYÛõCæÅôšC·c*ÌT¼…æ ïùþÀ†6o¡÷‚÷QÕ$s:Ed€¡¾•dVÑ* í†üÏqeÜPåEÕ}ìxÛäo,Ù}$©s´Q¢0dk¬‘S€ÐùøèJ ˆï'‚½a'ñ뢳øeôOLv?CÒQ™É24hîT~Èqu ¨…GS¨â0 á‚uvü[x#ŽôldºÊ-$ªJONv¥CBú1Ô#š$#cºp¦«ZŸá¬„4fÅýÙ}æ21ÿý퇃vÎüI)~ú"u*¼¿`#è_9O#V~6ÞHnð²4å-¼”ªÃqÿÉDXº¨ 54»ÀPM”þ‰ä¢‹{ÁøcèxSQå ô{õAlËW¦š5„{=„OUkð¬Í ›@¿©f¤Ô² Eưxt&L·>ÊÊq&¥Æ‡'ïrl«‰Â­¦æ¸ŒpÞÝþ]o‡w ßÕ%Ï÷ñ%™ÊtÇÀcîèmB_ô¾„„$n8?†Jïgs¬•ù㮃üM¿ 4Ú;È)A(áDs †Ò&ÁÅú¾Ž2úçí{Ìšz–Ï|„÷ÏV7L8^H%§Ð»pùû] ƒ÷Øü  ×˜ ¥¢±]Ô:§)Réìzô[¬ôQ¤MÆÇ™Q±4mr6¬Q®À¼¨Þñ˜„ž*qüýI‚_›ýÞdáõè5Ìîˆ9—¹É[3 áéUv)Õ€òm1ì½C<ÂE1z)5OH?ʲÃàcOº`•T1è‰MãËÊ]0ôÓ½†+âyhãõ™ä¾ÜÀ´Uƒsî`’"”¾A,ûM‰íô3Ž jìWÏ&`ÕÍè°ºY032ŽìyDÐ~,ê—Gãpv'p®sÐ}é9,¶YÌ¢vìÁge;é£\¼£Ée¤’yß\3˜]Í1¬<en›ñ» ŽÃþG\pÖ½œL 3áv«¼ÉøÈ(lñýTŸ‚Û5}V³a-¼ X+43ÐÓ±” ~OBïÉÆb:ú•'ùoß’¬3uØgâˆqGn ò¬|ÙDÍEP ’ÿ*ÓI¸…'^œøã4VI²!ëÒ\˜'çA U2ñ„]9.ï:Aö<'¯öU:—j`–Â02Y„ §‡³ú³VÜÆ6èÂ$üôBín|&ÁuÑð±â+=úhßyÄš7&¾6» ÕňöÅÍd‡®¡¯Íùµ ÀèïZ·ú*¾‚NX÷²:6‹òÈ×KÈm7¦:¡•ýˆž g–ãNñb¨3ŸE·zxÆŒñ4a’,â@.~§¿/åwÍ¢ÉÝ[ `ã$ž2\ ƒâuà@ª<ßVïÇâNµ’Ù˜nó8Ñp5f¶aƒîF0¿užzLÜÇ:§&Ó—÷ 0÷?Ž ,ÅÅ«ÿ¯,頻ô_Ê{˜pö ¾½ÚÞ¿ ðÁé4P ÚΤ£*ðÑ 3dÝ’ù\:!ã"þ7ÔÅ)Ñæ1»¡tÎ{ÕìøO-ºúÎ/Á…ÝØ{N—–íy-m¢¸jÞYà]=B¯à$sÚhz#»_ø¾ß ;I‰ l±‚W>KÙ?…Tö"ï-óøý›š9á×»–¤«óëäÀiSÐËÿ#µ}çÉôïñÜkúþF4JÉhqÃüjög¾ô¼uÇãßÖßðk AÅÄ„¿P®ä£ÏÔ®–°øÓÁ¸è|1^ÓO®-ÝIÕÆÃåM}xö½&iÎuU@®v.T]vÀ‹—K…VŸ<“l¥Þ|jrÊWÎŽ'Rí^~çRQ;‰úª©0N}Wº0صx”Ê>²¿Ð^ûîËŽƒÕŸ¡sÀ5üš˜ _îô è4\ÙSÏÖÍØÈ4ÌáÒ‹ l\³‚LÆ=^C¨}Äh¼e½>‡/„e¶ÇàûÞ¿ht1‡›  ÃÎàçÖibArThÑ*a?n;çC“^å°žrSÀkš|Á´7!•µúˆâïñ1°çÊœ¿ß™ï²|ööà¤r :KùœqÜ}’×2X@ ›°;–£Ò{”»Zªþ‚ÚqËAßõ¼·[‹–:ºt±˜.Guñð,Ô ÌaÃrÕA…Oïá…ÂdÔŸ¿þÍ8„?ªçN[Hä;±×šàÅ%tSÕÆö6Çß0—¦ç ayß&Áe)Ðøm£û: CÖ+4ž?-=_ [õÇ)Á=°Îÿ&Œzë …§?â˜Yˆíðœi£É¿s§áýúJè9¦¶{±¨$ðžÉÈ¥_É€Ef.c’á¡dƒ†sXŒ ¥Õ˜?q};ßþ“·££÷ês÷úKX»Vš¯gÎÐÕÕ±zÇPnâ,z­q7ÌÚ®‚ÛÆFGía\õ¸1üq÷…û£åùÂy¡°Slݺ£WÐâs¹ýÞÀŸ £3fRó#Ãøªzi<öJ†=oÇ€Cö—óŒÙZãaK‡’Ê3“hA”±P4ó5¿lŠWdù WtøÏTuèÊ¡|ja_1 kÿ¡Ü½á¼§J mÔúéŽãå 횈I†R ¸èפ¯±€TOÉbñ}ÓÑÆ;Š;ÎÊ‚Â3Áüa¤µ×õçR­l‹Ë#æQaI£¤ž8M¼¯NÏ$7™h÷Á½‚tÐq]…ó\ø™¦ ©6‹FIÞ#3Ù½´ýTjî+"F–™Éßxp˜5G Cl ÿcDÄÎÖ³~‘Rë¿Â}° –ÿ­ƒb”ÚF‹0\}!-wÊÕ+aó—X®|]ÎχŽ ÁåŸà‘¶mXŸ“òac€í–-å…êð킬{’Ãì/ã¢ûFèäñ€Œy§BÕo5ãW*J{òš˜êìG0Ïë,|ªÇëêr‰ÛÔxÁÌ@¼caÄN„IÁÇ9Ka"xAZF0>JÏ ‰F§'èÕÖÒà—WTHùâ¸;‡páPBwº+ðK×'aÞŽ!´K\;Ëf58Á”Ô*ÿï¬Ðcúh<°ì(É[ú@°õÚ4dÑßJà{E%ql2n8ü¸˜I¥Òe¿â; öaíü†ŸfüâÖ±wàÕ@-ki»‘+ ÆÌ‚w¸>¡-Çò®:ôɸý9Y~Ö?‚Wú/ ‚µjî–€¶”f˜mIùá&¼.³K–áH•R¤ :†Ñ‹ «è̳]lyÀa^w<¼Ø¦C‡ vÐ5ymÄL™Ã'v‚Ãî€÷JYºÖ\Ò^µÏø¦_êFãâÅÀ©l&ø‡¹ã¥ù†Üo°½:¦à×Ú0èÜ4†7zÔ€ÎÑÜpÞ€·š«Hðø}P Òæ]¿iÒÝ3dËÛ)P`^ÀLZž³?Ž£yèi.Iïãž³çHÑÁNÁ­Ë`RðI÷ö5æv™òÞ˜s£–H éDk>ûSÇtÒQšô¹ÎHIš5DŒ7çC\cç6r ”ah`3¤[&Aƈÿ¹÷Q´U—×Y‚jÒB/È!…‹®ž†+ùcøS½QÂÕެçD?“~¢Ì×ß(‡DžÁNid‚¹K‹šx˜Ì"sàìÀEhlÉ$j%ÑT©a5Xïħ×GBjĶ˜T¡të"®îu­D? %<ݘÊöœ‡G&ÇÁA¶êö@ß#iz2Õš–,òåzQðùê_¼ZŽk7tBÁÖ#¨³GžJéÃïfßàœE,Q»OßûëqE›\ÙÇûò@ jê)õ¢‰T{Ý"§á‡góïí°øé5\íêÈ”t>à™Puž0›:vh¢UÙ~)u8Ý4{ NP‹e¶XóCÿ ÁgE#<<ÏQwä>jü»žZ­Ç3൰<¤ÍøãiAxuüorîôb´¯‚lCU–5Aš¯IøÃÆÚ. «øPùl¬š>|‰"]kàMÖP¸ýêS^sÜOò)ª©øÏ@Kx€_x‚§¦ÛÑù:ÛYüTy¾|Ìh¬ÓBwÿ¡t¤X÷¸¥…Wçó§}Æï „Ç]¢xÜ'‚†çB0ÉÀëÑ<¯ï$î™K}­Ä·p“/}‚¦ª]A¦Ö7†Ö¯nF­‰†°þHšÕœâÊ“¥ ýô˜Õù]|âð94p‰3]TM^ïZÂ{w¬œ0½u@$rÊyÈ`>O-ÍM¶×Ï1dîØ%$Ù[5DzɇÏá» =ôsÖ¥·-f í.zÂÍ=qø¿=DzmK¹ÕèE¨æéFÏŽ° "s/¡ð‡1]8,í.vŠõ+±i¬)7Ée÷û{ÉÏD=,?¹ί÷#Nݰy¸-ú.|‹{ãÞ`¯ôCLú·¤~v¨-¨HИÙ&tç4׋½é°|Æ{Ð]0†Û®¸À†í1‡Ë½ h¹ú ’®@cçËþ§ž°ûözÐóéô.\—ŽŽ³ƒ£ð¹j€×[K éV™0µúSÉù¢£3éEÙ „y à¤÷ÎÜ'!ÉíäâžfRšö âz=n2ù$\iÂÍ,ßýî„ Ö^4#á ž.L¤6ËQëØ/B=Õ—˜oÿ LþTâe·xh ™ÉÍ4äÉÞ“BfëæB—+¬ÂytD FÁ¹1†Ýs'F‚^9åU#këÑ+{™`ë.9úI·‹ÔèQu=HöÎuþŒÀ‘Ë‘ê z¹Åp8Õ‘xº„:'* =Åà—¤UððLÆû+ Û:Îà Þ°ªæ:~ñTç1ûGàž(W”ü|ïº âwþÛ o€Ø}èL粺Û`ËÂ8XÀn™ÏqœÐk-\;q+ÎÚjÀß´µ‘-•Z\{ˆ!kR>iO4©Á‘ÓìErs.;ŽC?Ër‹ïpe•ºL΂ ‰ûAãü,vuˆ,Íß&ȸŠOô®‘gNbZÞ©-WÙ‹ìŽcN|j͵Î=€µÛõèH¯0 •XpÁ˜o6âÊYÛΦAÔ¯ü5\Ùxº«Õ¸¸™-æ ?äÍÁ:`}ùcj&õ@ÑŽXÒ²>Û+>³#1Ðwt#:7S~”÷“Íá,pƒ—ò<ŠW nãöÏAè\¢È¾œþ†/w§;Òµ&4^ë¸Î7`å³¢@½òx‹‰ÓÍAºì6WßZf©g3·ŠÓ×'Kp|î¨}FpBâÄÁOÆ®º:Ñů0‡í?àÌÖRp4¹¶]§`²úÜ$x ª ?ÒÿÀ+]U2WaÇlI:úÌ¡c”)F£æ, c ¦Ò…‹¦`óê] çe9èr³7?+Ä~Žž²‚E̸†ÇäÑSÛÇPuû•ððød>ºß’ÆσԉQ$fÌnR7õ^¥rÒ:È»ŒEø‘Ãø‰/2ôt˜&U¸:”xs|ËÐW>jTDýȦœÂmm¨\¾ö>8G&ÿÅ“ûÀNŸT —¦~Ï/“Ǿcí1Ð2ý‰¼ì¬`^—æAgËY¼)é"ËLÎ+­ùHÛï¨ü(ž>§?dž‘½áÅÜx|šOoiS3üy2ˆïs …ߣÙF#7.H¬Âv»¯¨}É‘nÑÉBE *Ö¾—/ Ÿšôî,mvö¦Ì`MýŒü탠/G©nðJjtÙ¼G$CþE:K¬j Ü0Õ5ÄŽ¬À7¶ÂªÛR¸B0‡1ÔÂÀ=æ|DéJ>=0šm2¨DoœÇ·ÇÃòÃau^9Y-q²‹– ÜdY:íAfñ" ˜Ît€ªüÐb;®xû z¬Å_罆í‡Öð7·?†Ò¢è5pøò)¼#ÆÏ5'ð½Ë^áõORtçŒtú}Ñ1_e+¨lÕ[¹›p‹‹®x)ù8›?ቻ: ÇIÅЫeŽpsAƒðÜY>Lܜߞ[‰­EÁ\|¶&Í:7‰»»¡ÇP%s'ª[O” ¹ßÖ±<.y'jH¥)~• Ÿ­…˜ýF|Ÿäxþ¤ŒßÓÑ£;‚£Ò!+ä1›²¤ÜZè \–¿3‘5Môƒ½[ÌáÌý\¸™Ÿ!Œ~!´å¦Ä‚Vl7è¾É^o}€]g¯ãä2'ç· a]ƒ9?zÂàŸì–Ã?rÈÝw]ásKbN:¢Û¬"(J>‚æ’tKÁwÁ‹šW¸}£Nn^G¿ªÌB]9Ü3°xÞK¬¡W‘Ý< 7ãäIG÷Æõ!0DŽ:¾ü ?ëõqj÷gÒWk ql°F5OH·šâ½¢i\ñÚ\ÞäÔLÖIñ€4²™g§~\9Üž]•äq*iÐ׃‡G £{|Ù¯k_„QóÖ Vë!<)ó[è²QÒLmàîRñäÑoÐ>^‡Öl%™6çÑ29Öþ½ŽÅ"û±v» †‘\ªë©pJã-V^ÖÆzdNaˆ÷M¬ÓІLƒK¨}¯†Ø1É¥ÃaeU¼‰¬Æ×RÙµ3aÜ:Ô‹Ïx-ÄI>VÔû\".` ¹.¯Ö¨2Y¹nhW _*—¢Æô>ì¹^Záò卑Rœ¯Y#ÎÛF¿`VÄèŽÀ¯ i¯D ´Õx^™&÷Ž<¬Þ®Ù†C‘yÎUY°=¼K–ò}7æ³jƒè”^ˆ—Õ©Ù˜µÌ%ðˆ¦™Ðûåà|”¤Œ_Jwþy‹Å dè)nN'-Ï‹Î&àµù=lyæfÁƒŸéPýj*“—ícÓjJ`ÿ¬µN‹ÿ9Ï¿YÃG~W§};ò±Ücý 5äQ%§XÐ…bŒ Šd׿ÞFQ¼^:‡öŒA¥T3ÙÆþ¡t¯~©`Jy~[4‹Y[‹;ÖÓOr§àÝ»³øfúzÒ©[ŒÓW:BÒü•Ì#pmj‘ÃY£åxÎãGx°Ý’:þ,Tì…Çô‹ ô‡uΗ'˜ ®wƒé‚kM@k¼ Si ò‚'¸÷ 3VWAÜn%<œ®ê?‡ê»Òyõ«fÁýôhŒl€ÂØ;dÌ'C̽–‚ÛÿäpÛW7ØU+á µ)¤n°W‘ÂýSwÑFW–×, ć ÃO©PËc Ý?l?‰6ub|À׌†®w†—bàÚ§ Xîǽ\+Ø´9BrúϦç²E_µ¹·¾&³·+?5Ö ì‘¶öƒXÕFaÁ°Ëè|ñ,Ùµg-±ÎÇŠÅ‚KãèÔ÷óAñB2îÞR}g:ÙDåÉD_ßD6Ï+ Y),Xµ—UŽ eÆ_màmc*\HCVšñž±ûèÅBZºã ¼‘å+:ÎÔH)^*- 4ôÃ%J=¸®ÿ(Sl †9§qï1Ôè6¤2½Åd­M‘{…ÁW‘(¿1à7o(Ès|‰ÊKòV¦—h}‹ jà"1ÙŒ¦‡ôáD¥26¢³¦¿‡› <Çï¹b ñJX6DŠG0)>²- z:`CÁ ¦,ÌÀ+WLàådQ®´4ƒ\æâò¾S°·ë(êÆ@ŠV¢ƒÇ[OVÿµ€UÉCAV†<”¥1/b1l±Õ*YŠ·ÏëqÿÓ!qÉeAHéCxM‡À/=¾Nt _4jækªñ÷:4Ûp•l¯cíâËø ®0UM’‡ÖüÅ×Ëçó_÷Y›}0ç3÷b/ÓCáßÝÃÌ·p³à‘X8ªz*q™a‡ÑÆMü0Õ›þo?ôzåT1œúüô¿=ÆäÐFȶé„õî'`ôÒTªD–Ë  ëa/³ÿµ=Gð®f(¿õq$útbh&s›ž¾<žÆ¦O…ÇYüåEÈÝF§¨ s„R åËt1HÛž.Ô“£­‘­Äåè9´ü]G|u`ÛŒ,䡚ô¢€NZª%ÐÝÖ —îÖ!×Â)s™ðÊ‚ÏèQ´V‰n¦R–ÌÛc,_¦› 'bRÖ{ÜðHœê~ÒÀÍ^â`ýc1Úä=t—¥ÒÊÓHœÇ¢.œÃv—^ÀóO¾“#Ï…O¦·¢Þ”.Í‚˜|ã:’TNã`žõ§Eü€˜ŽÐ†YÑåà”+Äh«kdHˆ4(ìÎÄ%[ÜX]ÅØm?÷£‹o8V¿Eç5Êc÷¦ý,F±Wè“£NOÜ·¡gïçAsã'T—¬À‰saßûgÐtj*»>Á—g,éÐ&Pöå#¶^®¤î¢ð&s@ð+!‘mô;;ââAÜé~–n€€›nd|¾€;%Áß-ÝBeÕ‹ØçŠç`hbO¶^@ÛÔxØóõ LŒÈDn#Œ4xJz¶£–:U(lv£ðĈ 79{D‹äyÖ±PÄäøó©[1Ä›V€†GW wIÆÑ}»SѰ½Ÿ¸KÑÐRm>‘ £Ò& ¸i†‡ÎîƒÒ…}ä§äCc£Í§K}g§<Çó%çp¨ž8½*ºwÚóÌ. ˆüì Úð뤦_›„©GâN»§ ~w=ò–Y],‚ËÕhÛëe0´p7Ž‘¥äL°O°–ýŸè˜ÿE‚‡sGàÞg¼³M“úZˆvëôXã3'xw]YpöYˆÐqF¨Ew°OM#€T~ÀèAøQ{1½¯I½õ'pÍžô×37è¨ E­K× (" ÖÕN¡yM+-=‘Ë3qð~Ãeç¹ý:©Ir4þ8ó]lº™_€m~,d+ +@ê·:üÕUÃM"K˜×‡1¬lš$?ðì4ø§Åñab£H_ASÃ;¿ËdDG šV˜Q9cœŽ ˆž¿)·™aŠÙh¥:§¤AÉŒL§ùþWØ~¯ ëp/•äÁËÃ"Ü’Û’±2PœèJRs -ø+p¥>÷gƒkãlؤv>ë&¯<¡g÷p)à°ºõ:ln'ž!Œ”&…#3ïÿ„}è0ù/ˆß{Í6¿Å3‡’àæÌYdðƬÓ'FË!ð’ ªúÄ ¯X Ò_ì0q%snÎÆã-§ÀÎû(n¹ö Œ6,æ¸3ÎáW´ŸŠ3m³HQ´&º½lx¦~ wÝ3Å|®C²MZÁ~Ê;Lº3¤³Ù˜910R[Âéè‚vûÿw›öʱI¸òá\9XnÜ´f¬P•ד0®ò9ùt«Ÿô]K°þ㡟ÙíFºûÛaò!”:¿lVíã«'ÙʈlN±™Ûû G^äuË+¡Xa4Š #`±Ë€¾ú y5Ùpâ²sy°:¦‹¿Ë;Ðq]5s¶Û€KóbqøA(1ºäü¼[˜6Êj4_ºÇT)ñ§åÓXÂÛvÜør.Þ6žNÞP.ýÓê|¹ bS­¡¥2 –®ÈÜQ9$¼`_®®Ä7¾¼ ±Û hÿ—i˜·`>Ê&DBû¤6¶,Ú?ÿž áû&R ÏÛìø‹kxŰÌV²³½+™B³8/ß¶cžƒxêw4sTš0…OÛhŠ™Ar|ÌöuÈ–4Â6~Enÿ·7Y`Ô%Eç]¸ZàÝ—£isÖr|è&ÎeS|P¸Ú’Z‡ð‚iÑ·Á”&-kS:ô²ûbp.’€à&þ‹×pßÎûà­hˆñ³ŠÉè wÐTâ|{ÑIzRt`üOf¡<Œ-š ëŸõôŸCU0Aë5³”AoL-–?8ßVœٞÒ<]sï™ôµâLh¢:ÇÐ*þ•W@t±df•Â’†|ج1ÇÉ‚Aìs¸*¼>G -GE 6ù¡y’$f2ž4æ¤à–·%lÆËgÌ<)m«b0öwáêLfÞN¼ò[ÇzJñi&Õ Ó.KgŽ…¾")*?.ª5#Ø·èÏäù»v§ñ~Þs„ÌË^¼¬Ý†–Ê=aC“ú@¿|.ôMc¥|оMžÆÝD6¦Â2oIÚ#Ó‡S—à…‡§Y˜ót´-Œ÷Ð>Èjk©c¦0kßÃàì|ƒŒŠV¨Ið©e&ð¼?oÖËð“7ïž±Íøjž(œ<3‚;ˆT0¿­(æ¹úzÙŠCºmçH¢ô.L9Ý/\#Äa _“B‡ëâPZzëOyÀÞ=ƒµ¾««@"ÇKdÞ‡^ãç&ŸÇñµš<¬îŒZ‚ûZOÃôå  Ôh)lº“&ôxxyð^V÷¼z'É`¾e$[&"‚ð7¯}¿ "Ë™„¦èM~bêá$ª1·Õ7“BÉ´[´ ¶‰Ê@”°¼W¡ë×Q|P6[÷:ª»uh’ÉK|"b)±ôÄÚûµYØuuºîŒ£Û;ñit%n-ý+ÈÕp¬ÈI„‚5ïÁ}Â#L™%Áâåó¡~@+[t²þªeôøã/ì±è44{˜Oæ5æ#Å2À°`4 ÙšˆïZEq&Õ`—l‡8}O¸»7Mç9›TIÚ]˜Ÿ <öD[1c ní…1¶?¡/£C ®XÄ’Ùžö|û‚90“vÕ@¾Äa\a9€m¡QXÉÆ‘³3GpÅà™LZâOB¡oæS64¤|Ïv ®ŽÃ?—ÆË•h¨™lÎMÂ5PñýoöšVÁªÔ&á^³tqˆÓŽD¶5°X–ÃBÜrò»¦BUÇ:Ór¯LÔ‰u‘*ô£‡ß™3õoè vÞöõ$S?GÜ È´‡O‘d:ïJx̪†Š2ƒün,š—¾záƒQ ­1”õHP™ßà±[œÉiEÝÀìÓ4[ë„/Cà«» ´>…7 ÏåÚC Øü“PfŽ:½&y SIRõl ò½HúoJâ’mxJoØæöbá»=°k…$QKމó/yÚsä>;4ø š‚¦Ñ‰%ßà¸Ów6EZ oýŠåQ‡°c“/ÏæÇUpûe´=«Fz,¦òlAáÁ%ä“Ý+(ë¾  ™¸I©£gÑ—¿†á7›çØr\‘¾‘7ÀsÛ2`ÊÈ£ü?íò5Åÿi“kîïðƒø‚­óÂE¿›Lù»žÔ¸¬D+[‚Á(xMÙÈ`~áTPžºŒ^zj…1|iZz©Yò†¤K°v‡ µþ”ŽÉipú^+ìëu$W/­}ž`~i?Èm›AõØ? µµÄ¹ƒr;~2\sÿ$€]Õb4ï[ÃGÄ_„L¦Y¡ãh îü|%ÏmŸŠóù¡ôÑœáLÁ^ˆ†ÛýQíÇEʰnÌ¡0SŠZÃQ¤kbaä™%Ü7-„%ßÐôc÷¿¸à‘L=91N ÙÝ€>é Ø‘{²›G“mSQdQ,)µÏdÉ÷ÓØPZÅÖ-Œfó–¨âÐzœuå»ãè…‡Ÿ9ñ×s©B’ÐøÍUØh­ƒËW Ç+ÀŸœJˆÉÈïã§Ñ—{¦“‚:Ú*wKT¹X¨•ÿ`ÊOÀ'¸h;E ©‰Ä3¦ã±^,ƒ³ç¢iP{m“3FTîE*sç ³çfÝÊ| c,äØŠòúÏÙlïÝòYaP8[ï »ZØ…ï7E£áÈ÷•bOHò„UðiO<É_GZ›píä·5Y/˜i~l4?Þg¢ÐÛô8¦_ÿ…—c´é.ÿ×8é#^2mdåž³©rQ7Q¬5°à[ðçòw0œýª„ªYnü¼G%[ñý-¹¸á>žÊQæ§Žâmý>tsàmOðƒ¬!wîŠ7O‚[òYø3Q…äAÞJV D—ø !òúRž1ùà6_¾ðw1l·}ÌÄÛD¸”Ø^}m'@š~O‰VÐ8ˆ+Þ‡‡ƒb`QÈiâºò>N†›¥Ц¾Ìö/ãÙSûÁ¦~ ÕÊÆøäãü~+:Q- áëKUÖÛ·ŽgÃÈ¢úåÇÖê&˧6£GNðâãåð„´4 Át£`4ñ;D'îZ…õÇvãŇdÄé|tÿ*zû_O¼J{ª¥b"Óü}o¡1»¯.¸ÅQ{àï¯8>ÐÊïîNäG¿žåR{3àZ .ÈÍç£dݨXÔ_èš„FW¢ØuE!ŸæQÃôN…Žç¶ }*¤ÕA£\_ ´ÜÃcL L#pfgT¿lÃW[Î…N ˜ð¹’Ìç°Õo:n>êH‹FYÒñ\âi&j[?eúÅ¢ÔöÃ4$›Ü¸ÙËßðµU—ö-LÄ£Ïoß/ægÞïÅÑ3XÓ=O:~GÜ™ó ne¤Aΰ¤×|À-Ó«ð¥¯u:œxçñßc‹‰½QÕŽÕo_ ˢǠ‡jºíóÇ?÷’Iýñàâ;„ÿã}Ú3~bó‰a°wÓ5\Ÿ î4<øùj€ŒŸ‰&7–Ÿ Kœ#ñ SÑ7¿˜bÜy\f1›ºÕ~€ ©—ñ[œ!½#—I‹qóv9¾w®3Ý·÷8+,AƒŽÅ¸ñÐ$˜/®ºI‚öÊ£0Ú¸ì^­ 8P-ŸÂŸèÏ×[§ñ†âa±_‹£:t±PêpÆ>)Ϊ-ÁÎ-_䃻:˜K‡îærVå¥Nï*ñ~îÃÞУç%éúQ®€ ÷s1SÏN%–F Ýö•a¿Å7'ÿé]°Ç “\—Å[wÿÓŠTô?ÖŠRÊñX°*=âk‰¹±>Ü÷¨‚1÷ƒQ3%ûœ’qçB£0\﹃³¼ÁÞÚˆÿURâ{…§`¬F$·Žïÿ$¾æ:šyÍåÕ£rñkg[˜½\Óòñö5 Ð1Ú|Ò§£ÙšÔq™Œ˜ïRw¢Ì¼v˜Zr;<Ä©å)´¸ žm™"C+œó…ú´›iWÍõîmtì0^\5”®bî{Œz#…g¾Øã<-[Ú¿¶ºµýI§K$] k p-ßÃSÜaÓpW²V½Ì.¾ƒ³Ë¹È]™q ·\Ðå¹Âj|âiÆ¿ON÷¬û*z·YVS8ybÍk'ZPkÝÉt½µ9wJºPƒ¦yN>µò StVõ"òô·üú+MÝ–;¡¦÷l( tœwü7då­€«ïJ°N/™êÏžåÁ+áKh;Zª¯#¼ÑW°ÈÄšoóÍ@‰ÛZô¤aY½&íÒ£¼±™”ìoG“5-`ƒIZ»)ÌÆEXÓó?Ë'ݯÅÐ"sg%:qUÒ¤Ñ7’#XfêfÄLRnb´®mŸY²{WcĦ\ŒïJbÅ‚JXNo`ÍëÃX½{(ø Áýç­éËy ‘®DÓ6 DÁ=x·Ù½:ŠáÙî¯DAì=X±«°éš¹0áéHve™äwÊÓŸhqg•@{Í!~ýÊTêa¦BóÛ©ÍHEÚ 7Ÿ>¶Ý‡w[¾3½Ã¡¸L|8xŒQà~kˆùÙ»Â!íš(:ø¯ŽæžÃ/ׇr±åÃèkÕZá ›dÐP˜@—é(Ã0’OËGçãoÆÜæ±7ÏK,eN{o`ë=Up-[N.ùæSn~Á@eáu™H:båt®ùN(6â!yöà¶ÿ–¤ÑWƒùâT?Òv´•MŒ×¤±™¶ü§±(}0j1®™ŒýÜ?˜ùqÇ3‡ùÕÝ;aÊÍ-ôQÁaæ¥Âß{¾ò‘£½Cè‡ä,ȧðÀìÞÐ3äeÞçpÓ|MÌP1ä¾'Uq’Q:Zì3ÁÈì-B¸Ûåµ}9P‹OXé…÷¨¼}÷ªîCR`KNL 8s Þ¸N_ÙYÀyòi¶¦1’¬ß£Ë…[:HêÏOp|Å38¼¤{}AàËX©ø ´t”àQo<¦†qQ³Ñ<}¦*~ïi2ƒ¼Ï/ØÅs2ðßòÒŽ‚÷pÇ0…uæÚ|[†æî”€>K7zq)“Ða|Óf<µn%Ꜿ ÷ÕÔáß>kyq,nƃÎñ¡¼39˜¹ž1ãÁS<Ⱥ‹ñ¾òÙÚôfÖœä£ß~}fw E¹N¢/÷^Y›¨Óv:=æÑï›ÇZ‡ñ6Ë<ñ@+rÕYìàQ¶ëp¦ÇY>Qß‹’ÚkäâËP¿â }êª×çY"WŠs4!×Ú„r™ ð9ø ,| ?ÊÀbÇ>¶7ÿ—q¢× áÿÝcÉÎÇ“†Ø¸ Ý+ ø¡tí>7Ü5Vw§$Á¦i&|èó3‚9c7aW›>3kÅø¡“þ¼ñØ9¦uG‘Lþ¼–O q„ÞµþàÝgu†ƒ\‹6? S ÇÓ763± s]h~æ.#òbêüA±ý½_œ½ìÞN†eÓ?—9•' å¯nÈñ'b=زs=­g™øŸÚÍ\žŽ(ÒäXû¯o_ŸW¿¬¯øƒm¼”(ìüé+è›ß‹@Vض·ÿáé4knÍï.V¡z/Ñàñ"qXûrÀÍ< ƹŸýèukœ“¨è8æÞ†ž›ê6t‚}e.ÚŽGИ|†;k†óŠ/=`mº Ÿ'7Á¸/±|Šå!ö¦ÃŒ¦Îo&²§¥Fòôe;èjwlUÓÀ@Ú„'~Ë;X"û´ ~‡=%ú×âß\œ¶rž´i@­ˆWH¾¤ †÷ òh\ ÛÛ…Jº0­XcE*± ŸB‚†âs±÷¬}ó#49!ÖÛ)žÜæËÿXóæŠ«øsfk讟4x•… ŽY¤NûdpÒõÓ,VΞæ6&veÂ=ç°ês7*}\Iø *ºÔþ³1Yiºã[S¡iå]Á´²ðk’'ýðü6‰èÒý3%ððn ðϟƆMãúVkÈ‚À0\š‡]¯ùó°eá ˜Q÷œT]43úÍRvÀ›Œ8òAlÜ4Ó€=SL茞®êw55°&ø3›rã––ÿ†Nƒíh=ç2þØ1–/3ºÇŒö½@uuì„OÒ¡~=™“û/OÌÀoi?Y¤D‘Ø}g¾¬Â[Rš@¬ñƒØn.]§LËüL¹ôÒpã¼‘Þ †jä~‰¶/ÁÔ—³ycèD!)áÝêÎ÷*hÿ‹Í—kñîˆe¨ÜêÇ€ö>4¿jqÖ¥ó6_[HÃRfßjçRu¶ô_ 2~éfÔ~Åàšçöyÿ ¤µ3iÚÛ‡äÊÆ‹è‘2Š&ú6 ëºÃ©žÙ$¸¶vîHн+óøYLš+Kõ¦IaÄñPLÚô“´}Ófï/=“RàÝ2­ÝÂíFË_lžLMö; (MûOÓLãE\èÒîOlàÎ+P(Ï';W¢ÊÆAÑGÖïÒã»N&Cøf¸èöC8ª>–¾y¯£¿ËRŽ|Úë:œ¶Qã¯3gá ѸÈåŠpâ} ±ýažx Ä?û2É|£<þûð‰=Oaôƒ{ðóãDÜÜφ¬u…þ'ú`ÿu.3˘¿÷™É%X&¬á\G‰O_‹G‹’Ç_“C¢›‰›£È|Ì…6#QzlUá/¶ÄÔ‰¿¶L†7š…ÍK–!« +ÚG‚ª§$êðŠÐ\xWÿ *£ÕÈ”ìYàTª†arñðþ¥1•3œÇß:<†„Žðã×øØaE3âÄØ®ZQ(Š5äÿòÇçû÷3’ÛØ©õ3€´cÃxYp›j!ŠR½i`~r)j¯ìÄÛM‹pö`á¼jò›h¿†. ¡=H–ŸÜqŽŒ~è‰WIŠQ(s9œ†Jya8+Ö’8$|Ä‹M>8d{2α{#xý@šw* _Ì×¢¯Ê+áÎuw ,_yš³Ù·Ô~Ï8ì Žé1Øz\MWŽ·»]‡}Ôxšã3H(rCÃÂѨPÚ—ß ºãÉÄ\iºðn:)}?þ¿\@¦+‰ÜØòˆ™É¶Õœ½•t+óÕBïeªâ …ƒ£©þ¾Búø 7n§²¿Ë™s•:wö1û¿ž{]ÌÈ®¼¡jÚüzƒ&ÝõÚŠ[Ì!Â]p½ß€?ÖžA?gÛñy%?HRÿ,hU¡;Èt©RåÙ|Ù~ÐËÆ‹¹{cxµÝ¾Ú±Ö4½Å΃1ûSÿˆYÓm«c`­Ÿ¤  ´\òrº~o°ßã±îèô®ã®ßÇGà•1&Ô{›7O•™CDMoÂýÞ§è™øLÞz‹%,èiu“QgšXå¸è©=o½Q€¿W¿‚¦CðFõ0|uŠ'ºaÿž…^½@Íõï[ïÀï¸ 8[K•O˜¬CÈrõ '`Æ„ÜwûÄÌæùе“#¨ÕËóüÄÇ8ÜU@ó„°˜ÝëøÈ“ÃÀï»[„bÖp—¶ˆ/¢K4iø$ßHˆ÷uC½ÅþŒß ^çrÉQ mŽâÔ:©öãìLž6μ÷ &T.¦‚Â-t›¾ Ï|/¤+D²±Ëf‚ ÚT” ¤cã/n][Jï(õƒpeH{„™ofÐïÊ]ÿ×GÛ¯¬û77gòSæÑù‡HýmßvŒJhæY¥&‘Þñ‹¤ÝÆE8ò_- ¹QŒê£Äùˆì`ž¾ï¯ûhÆ÷%ßâÍtúu„–Â7œ+Á—OÞ…Ù!šäñ”(²ûÐ/Á:‰qøXÉG¶Æ³Ê_PcTD|#ŠÇ sf'b¯‚! ÒȃõŠòô§£,ÿzV ²¥á™»µøë=Îõ/‚‰GB™álŒ½0›<7°æ©XM$–›Ð5?±­o6â!s®åݦ_pɨApç]쓚*íšô§Þ=Âz~Wà‰· ìÚâì+D)OzpÔ.2r9ê¾ähvËž{ŽtËÐSÇ/BtI3,Sà³~£òíÙTªKÕŠÁÉåÞì8ïc¢ñ–t!•¥Çm¿ãmš/¼ÇN…μ|‚+éR¾¥y3Z¢ŠSž÷°×; pÏpg2§O¶xîœÔ‚ÉÝãù‘»{ØAa°zx¬Ûp§ÛÖÀ]¶uÒCöé©1)N„mÉ~´eË2:vÖ pþo^J ¿uÀ@].[ –ï†È€ý»wÓ‘vüI<`CðEÈIi&h¼~Û¥É3êd¨ äùŒG.O¦}xÝþnֺʗòàw¦pÛÒ˜ÎNÈs ¸² öêQã ü(&J—uG/á;xf èL„G".0/|:'³½aG›>Ný¼”J©«‰ž{“õóáj®µ-uibTO#‡õÌeŠéa€µCp¦éػ֋~î!8°b$|V?ù–м'×ml—áØ:r¶Ï‘=ˆq…'úwaoi^øÝÆÿã±»žªrÇs‹peÅuôj¯ Ý*¼Û ëÞ?Ìm«a–"•xÀíghÓ¡ÃsQÌõ€Ð~wVdÁòÉ»ÉÉóhu%’6@n–"Li©†_år»h»*Á4¥(bRÖцOÀü´&;~V`ªÂ¾-Ž#FçƒÀlÿ(^½b ¯}½¡Œwis·ñËaŸ»=Mú¸î®oDåaRœÒ`®¥ÿy8cùî$¸yw,¥tˆëÂ}°9¾‘N¦ ×hßRQĽ’”hËŸ¹ '\M»^yÑ9ßÓøò·NäCçlŸìE?ÍúÁ¾M!Ô*&Uèɧ[rÛØ·c#ø¹Ë}lŽtïJÞ³ò¹]³3ñÉퟭäA§yߪ-ôæÐEc×á.yw½ŒãŽy/ ì¹ËhpÃÅ`zÏyXö”±¡cTèµËXö^ÚÅ;ð+:’ÿé–a©]¾Ð?:•«¦‚1G5éZ·¸ÉU™v-8‡Â‰`|ò)ÉáP3µ|o ‡¤·b«Ux~ˆVŸT€Š¹Ë±»Þþókþ‚üöðü¾s#½tH ‰~:Þ9ÉÎ=½@ûÜ„÷Lþ<2§µíÚÔl¿?b"‚Rš°ê×"=­ÅÕÂÃÚi ‘9F§«òؘxô¡¹ÁIKX¾èœ\9;’A¹|ØL †¡m+É~}ŒJ²gEßjñóÜ,¸:gùÆ•éÍíÃAÚtuÖL@ÿÅ*§µ<á["†ù×cüÖõ°æ–T—ΡŸžà§q‚/–²ÝÛöó=½#pfô”¶~ƒ%Þ Bëfa_$øT¼Å0ÅÛ(;Ul4×~‡|Z)÷¿ï´ÇêÒç±íÊOÁ]Ý8н³ÆB˜áZÌÏ¿‹ÉpËO‡?Ô¦¥&X]ò;­øÍö¨ÿÄýwã`n‡"Î;ÚŠ_6lÀÞ yÞ%‚¥çàXÆÔÝ"Ã:w©àÈIcYç¶X»z /ês‡á8ƒFµ¡fõ7ækÔ,à×]`Ô‘e¨µ^‘ßlzË¿Eâ^îËWÞam=˜9 àz¿ûñ­Ý|¼P=†¹õ•¢¸ß#cpáæµðÑ6Š´ß0§ÛtaEÝD•&+%IO7ùCò;]>ù­2ÏŒˆÇBܲv›ñ‰ß*Yó xùqÄæŒ‚_}Sè«77…Ý*C@²S_(]9ˆáO?áÝJ@oX[-4Ʊ³ÊV p¿ãæ¡Á[ 0”\„m'ø¼ï€‡ØG¹­‰5x«x1ž^=‡{åŒÃc'“]±£\–Jm âZçÓ±VÞhEÀ)]|wûÊÿþ &ëDyÎááС†AƒÆ¼k¹%oOÉâ mϱ$­c{ÀùaÓÈ™ë<¼š$Ç¿'ê¦>|—©4þç÷èç ¼Ç>ÌFÍå[H]ücæp2”˜u{léþeCéÞ%G®/‡ã­Ó ãÖA|©$J“}œè¿œ˜ôr==“Ë®øËÓöbFtÛ·¶_ÄscGðdŸ\õSþç­¤V)šOáÑãá8¬Ûd¤?Ç_Á¸Qš#›e"ª.s¦wW1'¥¯‚ŸÌ%kUÒcH][Èd«;ÐéH'ûîpÚ¢§Ãws‡û—_£Ï݇‚9,6 SL^£Å /L~ñ³e˜pä×DŽJ°›Ä‚;o›Gm&œ„Å~,®i6·h€}Ùa5¢ŸCã6OhxäKe.”à‚,=*–8„vHW É.q¾D·…”Å;sZZй鋼ƒ0çþždÏ+õU(pýiÓ‹)W!ö‘ló‹%OûØ·ŽXÈJBÕ¿buõGñAC8Œ›¯Œ'À:iwò3\^¼ $ŸÆÃÄ7_Axä4J¤à¿sØÓ—%ý¦6Nž8%C|~îAîIš_TƒìžX=¼Œ§c÷ô˜ï¥FõÆ¢\øT8¾Ónx`.„c³ÃzòyZ(3}‹È CànS[ðâhWÙñ[ßo Ft äÌ´æÿé¯3_ §zÂ?°Êý0Ž¿W×FÚÔ͒4IIó`Z†)-ÉZ#‹˜ùÂû,æÆUx"JçÏ …?G‚‹I"q²Q<¤Ã²ðtÀT¼`zŠÀ¥–c1G¦³îL.Œî½J†§Ü ïx"[¾ó¦P¨ÀBãñÌC1x(Ý+<¤ÉzÊ›+­ù§ÀUpew&Xyç`òõ ü•óJ-ÂGá]ríã9`¥y,zU Ü~tõŸø¢ñú\8,J˶րöµ³$òf$)w‡¥ç ¾ÿÔAùmÏÈëéGPeïÚ‡õV‚ª×q|¾3¦H<Ä´&¶÷†Sb"ÂÝãÕ¸ÈõPRØ“á Ú8/a5 »êíÚDdy 6/5¦‹;P¯0мuç× ”èÙè4bŸ¥×sª!ÒO×ÉæöK‹›ÍÃG'–ÐSŠ›j’K¾9]ÿ°uòüoÿåž/ÄÓ9˜¯]ãO&«íã;Æ®‚M>AÇBsºwü^ŸƒcC¶ãóžL}|%o÷ˆ"ÕÅcœÖlÐ$®[Ó…öã¥üRÀˆju¾ãõ=è¢Æ/ýxŲ ÀN™4|Ü3ž¶ekò%›Åù Lä û±ö{6þõý†OGÅBÕ49övŸ:=Ǧóü®p:çJïA“͘›‚4•4{Âî–DV?²Ou‘0¸•¨Kb+<¹þÒyüÞdϵQkó|x¦Ý"x¸V¯[H«ïÏü…Ê-h½i-I¸$MOo_G›cà³0'‰Ð’ì”7\BW<ɤ/*üèÄHoæö§[ЦÊ50]v Í·Rüô-LåSyÊèUø5¼GÆA]õ>,Sé##Vü¨‘ö/´®ã#0Ëü,Ù¸A ðy¯`ïÚ‰8{õP(:7^pät‚`™ €ë2}üð\ƒß¿œÖ3 |jÓÏ´ÄWA®èny¹‡ø,rXgî`†-dôëºæF*fî])0—ô‚uzIpn\.Ž\:ÇnX &šƒŒr:¾vèÅí‡é®ß6lÅôÅ4jx:²wF´}ÅPAâÅ5ð5äFu¼$»MGÑÍ™Öm,»Î…årú|AËBøøTt¼„7÷( ·×Œž6Œïv*… ¿0ò]ƃ›þˆ€W£Uà„î;r×ç0ÆÄ§óŽ®>ÆÇ&ïç«¢ÜÜ`´¿-)ãèñõþdšK+)ÖøÀ’$ÞOŸ>‰ê¡õ¡|‘¯8¨åÁçåÉð~“*vÙ ²íždûò³ÌçOt¿ÙÜ8Ï|:Ä#³V¡§óH4Ó³¢·nA?%+|ñþ^sܦ©š´òø\Pl׈Nš ÕÇ »S΢±8$rXo|…”Æ .\]7œj„ ]VÁÄÙwðF!,‰òƒÜ» hÕSxÛŽ·ä»ØïŠ`0ß. ›"2¡¯8c6ØÁ¤7?˜åD³ U‹AáÇt>~on¸‚e[&Èï°¯Ñxuö²`p3 ómè÷yƒ¥ê=þ.€!¢ i¯ºN‘jÂSç+ñNß9&»ö*î‚÷‘ƒäÜ&ªæ;™N[1»£7ð!I1¯&ÏfÄái0¿¡Æê_¯¢"î I´’'h} ÷—=¬É¹', ˜¢Š|Æ•whÿÕå0×L§®MA÷_سCvxâó!\`ÐÀNgOùOSÌúÖâ!×,hXz„MJó¹Ã–ƒlß=Á75Ø*mÍ¡Àíƒ{`SÙ1P-¾g¿‚‘WTž=Oš›‹ñÎY:>d½Öq„—t…@¡ì{Ü;røh¾aCÄe¸ôjw||ì¿k˜Ö ÷‘-EoXÝM1¾»áFηå’æ°‡Äù½ÅZ 1JžŒ4”ÁµQ"ã.¨ã§,!ì(oD7÷Hp{”JÂõG`|¯ õú.ð µ#».úòps-\4m+^ šE7^ :æºÂDãchÙú …pqßQܽ° üúÑ$hÕ·£kŠ/AyðNì9¿ :÷‹ÓÛÉ5ñî ô8ê‚âx+ªå?/ì›Æ…ç}hXÒtšš‹3v½ ©Â2|áòJHL çk&´€£~O6ÇðW®øL ÅÞVÄÒý-B¸»5 Åe¤¨JÆ&j×¾ˆ~•´ÇÍP ûg ¥ö'´øÖyøsi 0\ºÆe/…™K¦àÊ¥ïÈ¥ür¡ü[ 5ßÑ4³nûKpra(92.Þ¤ñ—sÙ¶;ÙçSÚ,çØ¸°ç"ÏW§¿ËrÉò‚MðëfN2aO›9ž×ºBÌïßfcì幊ñA20ý&qy0óÆÎ&E;ÔqÞ³ï8sOÖó5²™0c…Õ–xÄ.©ó=3UØÊEC¸¼ìchkу’CùÛÉ©7SÓLN“ÙI™<9)9|b^U §¶ ß=wâM×Èœ­vŒŽ¸ ùÇ&£¡l#lí݈šažäü¹ PøH:ê`ù¯;dæ¹£¬âƒ>¾£AWèçÆ/ ÙàÕdJw®ùErÒo X/œî=$؃‡`áO æá©·ÓüxóY0Gþ-,ùx_`™?‰ú5Ÿ@U³=|[ø_ü¹f2Z¤ý&uæV¤0À¯<[͇‡¨rÎØ4‰n"¡¶‰ËÃJ“©ü[€w;Ì]"‡a½‹,H› ºÆ|ÿÆrëüW¡¬×Eh˜p§]{Ór!H:šêðû{v‚½Ùï×V„á!I_ËÚÑœ‡f¼G‘·@«@êÆ)Àï½5èdÐ Á>©è~ó&¦7Ô±*£ïDé'dš‰óÎg¡}_7ŠIDá¿X´@_ÊzÈ©iVüæÆxñê!¾ç¶ 7ÈzÇÛñr™}¨ºMkÂÍ>½àª(ÏõmÄòÍMtUðÃ*W]¾«Ü‚xYÿ`Š¡¦“²0|B ¦\Ó¢k"(vÿd,–¦K Wã¹Í"Ôòéw).Ávp‰ –ø·úÔ¦KP3+âE6A„ê}ØâmgQ }æÛ ˆ¹3—¹~:q•³.Ýtˆ.:fMÎXŠ’ÒïÀo²!¥î'謻ÓÉ…H%þÛK‚½º &úì§Ù‡­CÞÁ¹‘ÛYr¢:ÿàsŽuø_åChæ­ ‹—xüÁb9+Ts4fgÑK;\!ï©$ô„ ÇúéG!:q;³^ ®öOÀ ß¾ƒu¢(}Z\O&I0 PMã˜;OÇl¶¾'³|¬”|ÄúŽÁ•¹îxq]9¬?W «ŽÃyûhᎵÒN-Iy÷llß&‚þ=§É03páýbhÿ9 Hʲg$â ;×§NU+u{4 ×È—ž„¥¹pˆ.ÀűgZ…IY™â>3jq؇ÌʆøØ.èÝâsgÚ4ùõÔ&!­j—³ˆ“!xû•,~bÕ¤j?‡—µ$3y?iõŠÇÛù Þ媛_‚ÌqØðÞ ÕîÌ‚ÏÄþ¯ÉXÓ>cüRziûsø’!OóãçMçe¾`ùqxÚ÷‹M—F÷’cÔg‘wFéÎ$þà“6,–€K× È“ÆQcúá½â¡ªËT£~æô÷’O̹ùZÚ³²â?Mô$7‡ÃÂÐÚXf°{Z5ë€Ôª©´ùÏYœ{ Ë\4ùוÂÑüyÓ%üO?½s_ZÍkqŒa9}ÎL}Ÿ!m—Ï„‹~Rôð˱˜$“‡S uYç“>˜±p*tN÷½cÚ0¤L†gH jG1Í!0µKš”¾/ô;RAábèÀ󱫯Úáã·±çJ66¶Œá¥—7€àý; Ò¨%¢«‰Ù«q¨Zrg®Ï"ŠiÉûëóK¿=,JoBßâpžqÙ’jÏ’.Ýq|?†ÓèèQô»ƒ;¼”ÝÅKÜݱ^Å‚YC7Æe“Ÿ«ÝèÖRšÙò¼TJ•-—L@åoÒ8ûìt(:à_M®PÇü-ü²¢€/÷Ä퓱û‘×J® ›ßdƒAÑRžÉŽ‘ê}éðºþ9¶þ˜(¼¹PŽß¯+FÕè5T¿ÛƒÚŒ €ò¨uücÂZÔ“ÆWÄÖþø]D“ùê¼vjn¿¯F&Ö‚fNk/ÈÆ5ùj<;ÎÎ3Ÿn!Ò´Qæ w^k~|áÉ2TþýÞŒX‡­ÒPvûb´»»•~¯ßå"ôÅ¢sp³%‘E¾*BõjG¢hôþóv~ôÀ„ß±ã÷g·M%Å‚ƒ7ôùÁ²÷˜b E+wèd=<ä%FÅWœÃuûÿÀc§Ã ÿç“Hñ–W³ªC«æê SOHcDKÿ8î(ËX4›æý>›ñmË06L G¶êÐøì3H@ñOxÜÁƒ…]ÖâcUn“ƒãŸÂ‹ûô» T†Åàmwºà7ñQÞY¦@m6~`5Ö+ˆî¦CT¤5Ò¶TAïAny&ëJm¡·?ü¼’7‡ñJ|Z•¯Û2¾ßÂËlÚûp7y:„Ç|vÄÜÔ V2¡IS8–Ú%eáµcù%Ϲô¬~fßLŸ^g –†´¹ê|oʃáÓIÇqz¡:ÏüžŠ&&©¿Yø= žÛØ]à–'Ê dÜ\Zt5†›šËÇz)Peÿ±tÙç|L]­ÀKΦÁI›dj#õ‹œ?¼Û0cX^ÏÖæÖÒ0a ä:„×íþ‚ žà»»kéçýq°BR «âüTe°?ð»†tà¦%V¬8“ZG £¿ÆªÐª'å8LÁœ¿§AÍæ²ÎÌ`ØeYÂŽ¦Šãá+îw º4ßÂ…¿Ç¨ð |Cï0ê9Ëž¬Îƒ§ë½Ñâi(œŽåŸÜN±+k éE¹h>8¢‰Å™ÂCzÈ>Å)<ë±€¾ ÷¤–a´µú ,ß¼Þ69óa?¥¹ëʨ¾šƒÓ­…tOQŒ’‚ïþŒä“ÌxHóOPê‚ݦôFãNz:; û¦%ò&¹±?ÙgØÁÛ5kyé§íô‹u,þ}“ËÃŽià”âÒhpŒ{~ÕçwR{Ù‡¸hÓEP:§ìú‰3—ŠðæëÖô·€žé§jFa4²ÔFˆÊBâÜØoœÈžGù˧MpÎî;üú>\èȳ—.¤sÖLä㊌yõC8Ú=âÈ*ö1í/÷M¦zÃ#<-aÅjÉþL\îô…[[+ðÁ:7ú¢ÜÌ•öÑס8ÚO„榾nðXÚƒ£ L,Ç®Eç³ÖNÔÄÈ‹J–åz>‡„3tÍrz¶ „yF¿Ïóa¼½UŒ_QžÂ²q#·8H÷ä¸{ç*X櫈’ï‰åÜu¸CÖ…·ÝñÅøü»ÆSÔ·à.›óÑcÍArûìgülŠVQE¸¿Î•]ã ’CqBµ§`ï“/5ýßjn‰ïÆgÞ‘ø.óÈ #àÛ‰qàVˆÇQ§ï0Ø|¸ÄÜBôCÛ#/†£Ìj1ÞàVãô÷ðzˆÞšŒkØ¢k¡ÔjKXÍþJ+îûãÆ+¼cþ•$»­JY¡þOY 6’;ß­Âaåù’Ï319Û 6çRØðH¯úšö L"® z§eg¹4ú9ì0›´õ/¡{çÊñ½e?ÐÙ†òWçpg¤¾´ Çî|G¼‘Àõ ŠÈéè,-I6‹ÓSæÜ÷ã^1ŽÏðÆõ"­°´b/;ô_¼t¸EzžEÿk×àïÛ£°Ù#7Ù™Wýô}¾?D]× ÔñÔÊ(úÜ‚‰3¢dÔéžá`ÆsXû~&.ß2]/z€Ø«(nuÞ™T½5¤1—Fpݤ» Õ0‡_S{†îò¨~w*¿<ú>ž}§ÁïÎHeaÚ¼îÅZâQøã~Eí‘!jt0r]ž$%îç[ fsºõI‹,$¡ïBþ€ <ìÕ¤—6¢îŸc§$ù³×=°0ê‰ØI8Yú 6mÞQ¸ùû3³¸ N7Uù÷'3¨˜…h»·aDôG6~U3,˜ºûY²õš”7~}ÖrÄÍñ ;(>RïAzÅÛ·GÒWÙQ¬Q¡ƉZQm7gš>í¯Ó®½êg—¨ ¾O–ÆÁÉÂf5’{TJ³Énº¤®« >wMâ_jô‰y' _‘ j‡@®ÚZPì>&,Ümô¯ÎDgL„˜³âøC!DPsK„º÷‡¬–üé£ÒyGÓo× ©dF>Ý¿‡ Ç`ºYž ú-P¼TЇò¸§¼gÝ]×{ fƼµù0˜Šk¿|`o¿îƒÂú¬°¡ép KË÷Ö°÷ÄøþKMW‚”Ós¦cZ‹ŽÞüáÆóøÁr 7<`Å=ÌŽ€ÃÑ$x>8\ñLé_œÚ › “„¿ÇF²Ñ#™©ŸBø¿†˜ëÎ:v‹†Ñ wkùØ‘Nè)W G iìe}:ñˆ:¿qí.͘wùË!t”^>}põŽæ²ÅšÔзöc.ü­Oí*nã×Á`¯!îµdñ` yUŠUÉ8§˜O¯[ý@7íOjÐJÞ}˜Ì¶Úí§‡¥ pAÛV§JñÂ3®¹°&b¹öW”ÍáSœ¿Ãœ]gkäµ—Aë‹^Y0žîßvÖ¦¯¥×Sÿ¿YÐ>†×wãT÷<(Þ0Ö-«^\Êñ¸|;X½¶æýR/™ä9\`ëŽïßÄÐÅô쨃8fŒ(ßþE5Ê*!ï½€_Í‹â/§ã6®}]Årw†Ã2mß7ýÿÞ%A9‚˜'Ê‚¯ƒeLyûVJw¯až³Ù°å‹1é¼ ï_”Êypñ’ƒ,\þû '±|´žþ7g±ÞÛ¸¦3‹<&/dШ²‚ú˜Î-WÆ“°Y*ܯF*1éYÍ,)B…ûã†Ð¶\AŸÒaŒÉjÐJ‚=¡) ócèr¿Íôôpš¹$×PŸÏâ&üi}iªðv)|}¹˜É_Ë_Ïá8ï¢ N¹Á>¨/›/Q—…WX¡®½ûâÕŽð]5aÜÑQ‹&ßœE|œâÀ¦Ü;d©¯<Ÿv¿Cç¢Î™Á„o–t„G,}äR…+eE/Ùå`õÈ „ÿé`“fVÝŽÄ¿ ¯ØÎ±8[¡®|4°˜ÚŸÿÂ>޵ÙÇ0Æó û!£~\b7ÏÇ1céSXÔ/Fý*ÔaÁU#¶¨›PñóykŠ%]­ö‘hòÆâ {_$¡Ï>ÅÃà)ˆŠ~dóûRØ{K)ª6ú9XˆÒ²ø ¢óöå‚„X0)áåû* F’VØAàûlÏbcxRYsLkº}Áðªɧ½p°%˜;†(ãß»—!У.­?o¦+á¿ÁîÎsÒ¸ä ÷µ^6‡¢ÑIP"ºW)„þh x»ëü'.¡– ŠaïÿõËá‡Pþà§oÑb§‘ðô\øÌiK.~Ï6L1æò— À^דû_ÝkÍù®R‚[Þ܇ÓÅ|ØÙeÿÖlNÖ—À•*O˜–A (*9ÃÓ·^Ìrq‘ý%Ó2íÙkCc{7Òõ'04釠Œu`Çþ1¼ã¶8+ˆ¯ÏÀó¡{©¶ýsܤ›J/ï>懓‰xMëÔ2¶ ›¯=×”gìò‡ïDrÍA¶öàAÅ‹ø~ÙEÐ2£AþÇÑëQÌqë¯K‡¡ˆçpÒ9í$HlÞ­©³gAµÏŠ×zòù³Dé˜gÞý.AÐÿnvµÎ¢ _Ƚg'ðKþtþ3-žÎMNδeӗˤéóùfèw$}ÊÚˆµ¦—aŸ@¸`¯FçnãžcwA]Ú‚K” ‡-‘üq†‘G>B­?­œ¢JG>ú Óv‚Ç:QçþR|YaC»Š±Pzõ`3®ìF3‹Œ©9‹b/~Z‘Q!©š[‡ ¬aQõW6έ>ëàÇ®˜ó_GŸƒHÒª®/F\FÕ‘Ë>ïPËk„=väù¶0a•µw?‰aÓQ°Ì”8L•ÁwY%ØircF¼C«§18XñG¼‡=$‡Œ‘ǰJúíáxðù<½fiI›/Ÿ‚ÎÌ“ ·›á´æ*/³“Õ¥³Güy¸ÔaúÛî0Ì5¾ógHÑ„•ïñTâ?Ž<’?IÖ§ß; 'a0¤Ó¨&àâŸK· <¨ùâüOP2LÂ>´í0ùÊpÛ÷&tl=J÷*ìÞbÁ/÷Ʊº1¦Üîä>¶³{'kðæS½Ïzô™I4™q~ ¿UÚK'œÙÅ¿MžG1šª]¡Ã¿ì¡·&äÃ㸦ڕ{;Ñ Õøhy6;½p$jè+‚|Ô*Ú]Ñ=+*á ÉZÚâׄ†Sà£Ü&¸>Á‚KÙÉÂèòs8_FŒ_ü¦Äñ+tHQ1®Q=EÂÄ¡êoz|ùŸÞ¯ÌFw4ªÖãáߎñmOð!=ªì?-õOËíôu¶#Q¹¸˜6ùXÒ=i#èÍö%|Tý?Ìù|·ŠPï‡ñ4nÉ}TL ¦“’Ýéóµ¼ëÛ|ºªmÍnÎ"ÎZ€l+är†~\ÚÉ…ŠøZ°“þÚlEÉH¹{‰Šôž‚Y¥µ¤oc&M^ê›W%‘U»¾ƒD;åzÆ&ù´@Ó+ðëR|\Të‡Ü z  BY<Õü:ŽßXÏÆ‘|¹Û8¸SwOHÇbý‚í°>÷.~Úâ E_C…ó:á²Ü[ Æ×,GªZ¾3ïgõ)’=ží†¥·>;5ÝM‹\†?ªú<­ò}hQ Ù2ðöì Ü 7Í•¨ùëP8 •dF”è+óû´xöTjgP&Zžšr%s"­²•ŸÝê §.¨ðóÃ/²ävôŸäˆÿi¹“¬öÓü y:ÏìNNµ£Íh‘Ù+²"å0}RˆÐX!E;ò®±ô1 ¸$±¾M>T:–Z¤ï¢c ª»ùR>brüØÝÄ–»n¡³U“eIx*n&+nW¤ŸoŠ F±(¶çØÃ—š4| ²‡oÚáEç=ÛÇ×w¦“«¥ÇºQÙ·ÕRèÍŽ…\|H •_듵q0Ù“ïéüÚçu@t§(ºxS§ËД†ÛLÝøÆMF[~CéŽ"¢j§ÂK¯¡¦ýÂb|Ã99º2EŽI+N c•"¹Lx…`å·ħ¯×_†™“³‰Zø(^}Sn^—Èׂ¾îËÂejf’ƳŠP`¯ ãã%Ѽ£R¸ôR Œ1ì?]0>(mŽÅ³ÔÈ,ÃC˜ñ#ëí)Æ%}š¼ç§*ÝT¶‚ïû{Ã-`AËWÁP·÷ì·y2³-º‚êÈIE¼fàMO™îÕo¸Å¼ }È.ÇïUV8>ª7Œ%>RwØ„¡µ8aâtºìÃèH‚“«ôpÑ…QÔùì%nðÁ“¾˜§N7o_Dïô«ABÞLvt@‹—f‹Ò ëMè«U3©ÔÃXbšÆ[§*ÑmMQ류uæD÷§ëVýÁqÏkˆdM/3(“/0ÛQÕø®ù!\Ùò¯OÌô ΫÒé”í¢<íí/f³0Ý‚&ƒJŠ3ÆÂXš/Ò¦O¿ã†Ùú|ã÷QôöftÿÑFå5r¢±k­¹ªp—5ÏS„›° Á5öäSŒ>Æ,8€ƒZtŠN-ƒ|YOœÏšÌyI=˜ŽõLÞHÝóFUog^±¥Û'-ƒˆWÙ âž³†°7)[„AWàT‰Ìd™Äøúlð*• oÂ1[U¸ÄL=Ý@§=›öžÃ‹©½‚K“Sa×¼]èm$†‚³³PèïGä4·’Áé‚—‚lëÖ¦¢†½ƒVtþ£ßX:ý!ğȇ²/’ôoõ2ö,³›f %ñââ°M¯Š´=™‡Q÷Ëñëû\Vÿó™hÖ;/ÏfRi.|E¿Õ¤0ý•!ìDCÛq[†+$’ƒÉ˜S/‚Žkø7c~«Q•¸}{WÌÀÖ°E\>Ô|úÚ…1óîþž ä§$?'µ5ýûðÚMGš³Ö•÷UC–120Gà’v™mÜöQø¥u ‹Ä²õ?ʰg¤!}øÕˆ¿QIƒ’}#©aëi¸«{…¸wN  ìx›>¿=¤ß–¬¤´] +ÄÜ &t„Ä6Ø]ú Pn Oº ¡ ŠŸÎìfÑ¿ì@PTî§• ¸3SÇeÀ²’|Ø´Ä’:ÊßcwkÕéâ¡aĪa€\‘,€ÛÎÍ›RQš¾BË-/ØT÷㘲æ&H.‚¸C»©˜Œ=Û–ú»¤Œý”Âû3Wñú#¨AÕA,1¯Ã_ž¥´Ã­‚ïÏP måw…—öÅ€iǪ|9†¬dçÆ ‹èê±²TR¸Ý4X°»é-[m·þáØß6þ¤âˆðìcÒ·};Uè 2çÇÓ¨Äzr±t}'Š‚øð¬ZoR礴œsžì¸[‡ÅBE¢=ïUŒ=+ÊÑèŒ# w;ÂÊÛ9¶­Å3D ªG. €7—®H£(89L‡Xm Sås—ïØ0´çaë.@Ù™Ôê–.ÆvÔQ¹RmèøMç ¡+6þ¤™sîðõªÓÁþ¬ß»h䱄«[ïÐEÇ[pfA'y ©øºë4›å~Öç‹âövGš»?•¯ì;õû5¨A™&,6ýÊ\&PÍ?qŸè$šá&Ì“;Io®þûU*;íÊWð¨Õˆo:×Áâ^~Âá½I !}¬¾s”Lßrå­dø™nþ FŒ0Yj{[+OÆr³¾ÃxG ÷åB‹S ~ h¦™¿ÇæßÙéácø¶žý,gãBòö”VþO½ üô£q7ØPíUºÝ>™W,‘æ³½_à3©‹ž=Õy¦ÂãgÖ¯ñtAÄ ^²Ð,ÒçS$S™¥ÔBL[]õíxÎŽd”DckÅjÜÓšE®´LAé/ZôÂÈ1|ËÛ™ÿaóÄkcc°bFîÈFlÿ$Øé½–+z§;mÝqÀ&êOÀHÙD™FDþeËÝŒùÒ¿s˜sI»>‚K/×ãÄ-Àc¥ýU­IŸ8†aÀÜÓÌó§žýdÉœâoâ†eäH`7v–©RÙâ0ñãRZ渘ÏÎ{…ݘFt¼ÔÐew0Ÿ.Xß[ƒ'ÎàÓ]åèš“Aì»™&}´l,øV«ó¹ o°hè$ÞoFBž¡N²… qc Ÿþ;ó£ðøåµ¤:0ŽD9±šÎ.æ—, ݵ{yØNPÙéÄEÖýÁ0½‹Ô¶r†àuÁ³éf|Y©8o¹×@Š'vÁ¨S’Ter,ß³„œ59ÍówÔ±õ®ÃidïR¸±‰M †O©áøÈÒ &×¢ÒçÑPxÓ…)9q×MÜáb‰Sþñr -9úöU­Ä¡"ëàÓõ#¨úÞª5_’áAw‹`Ùâ7¤¨ã4ëõmg£¿FÁý'aÌPs•0å¶/¬ôàt½èU®PRC^ ÐLï„ko®a·ß*©aˆ%#Eøkó‰üX‡-þ×#,žƒºs©céü¼ÈÇ™~"#N4ÁêÓØ5iþ‡Ó!jpo](λz” ŸFAð¾øá¬55‰ ƒÕ Ïaù6_*sޑϽŸÉ6Éf€Ñ©b¨søÌz˜m¼–no‚Ń5w&âôOsX»Þ^z±á#6X¶©Õo±~ù,°“"œwa3†.‹„QËDè…ö—(6îÁmøÂVîËÎSÛr‘Pk·~(qëy f'S÷»œ,>óGhå’‡Ëmâ xš‹Ó[®ndˆÙCR­i„Í'DhEÞZPu÷äGÎHC⨧°9)ŽOò!̲J§iç{Xëš­˜Ö9 $´1½±00c>¿­Ã{›1´¸âŒñ·ùp˜}t9¹ïýõ£AI¼Ò] ›&ÂãßkÐWdž,€±wäAG˜‹Z‡ð´‹ÐÄŽèI®˜WãœÒ‘p%v?¨T*ÒªXÕ¯*áͪØP½gÎU‡k3‡pËk2°­ì-Žÿ”‰Ú˼ÙãCr*> <Æ]`iFz¬`™8JŽz.˜vó~HÄK6‚ë&(èÖ„-ëÄùÙRú¸å©š«‚ÔCâ®Õd¹Ž"›§>Š¿ý>á¨RcØ?L…C‚3O“Mºè;>ÄåŠQ®i9µ1M7’4ÏT¼öq Uö“ÂŒ¹ r{yW¤Î1·Y^§¼˜»j¬`Q˜wéžëåm»¨úÎx5b8™tú Ž>óŠ¥ó—ì¯ècÒ’4‰ÜéŽ!WF\‚ø„KX@oÃ7ˆùR`&§ÏOXûò'® ;h6¨çÈp=üDrnä3§’dÀ Ü<†ÑúÃùªœÅ4C³&Å…¢çã@|·ðÌJRã´'e†—ãêƒÉ8e¡¼1±Ãçî §í-ذÎÒÃ|üýw,vTª ïñ±»Ï¢çR*¼ÿæÍ–ƶÀÅÄ@":WÀá;À×Б×nt†Ó,ižq"(1}¸òø |0‡Y*ÿç -ì˜:’‡]Á;¡ã¨µ©6æ7¾.kJƒ4áI0SA$¥Óï/ — 㿵ŠÑ¯yMžž@*|?À™låÚ@ Dlfx Ò5ñùþ0Ÿ¥JãîJqÍ ŠÜ«N”¾8ÚÅÔøùr6M6 F¶§¢hæeЩß@³6žÍí´ƒ)éÒ¸¾s,Šuá’·à¹zÌ:úå[AbüZöZÖøú³¿kUxOç&è£ 7üÎÀÇ„‘|ƒn-*«½":·là†˜ð̹~|{^—J‰2ïu ØÛb´öËk"aAm¯Œ§ž”Á1CÈ’Ÿgàô_ÆÜä¨=1L]óO31É?‚Úe§xËå^¼~r³ Ùº ·Øûõ¨¨þ—Ü–ØJ¤œï°aVa'/Šõ›ÇþŸrrÃ[ˆ¿zˆRÓþñ¢V[þE¾~ÙM¢ÒÏÿÀð Ú¼O‰§ß¯€EK±Áð)FÝò¥ú}Âþ#ù_Û ã:,•É„ïeùŒÝ¾äÄãå`\¯Në'•ÚÈbÿ2’’Çî¯Là“·UÀ*>f¯ÀÍÖ:tcÚL³èÅM£úkÊ…+œï {"ìhÖýϾ'cíLð-Ø232Pû@eÍu],Û.Îg'ÈÁÁþ¯š)pz5N͉‡ãÆêàê´W…â•í»ÐyëKÁšÖ:4Rõçj‹¬éK .ã¿ÞèÓ¹ˆùæWPœŸN';§ëF-w«Àõ¾\.{?üÒ¥x>ê3®^É*Ø‘û7¾Ñƒ ¶Z€25Ô =ƒónÕÀ½›óQt²Ùç\g `ÅåKDE*‹(§ ïù/6çîd§º˜Àû1n0L…_zB¸8· ·Ü&Ï@ìx®Ÿ—‹Ò¡Ü)œü‘‹bC6ëò‚)Á`6æ„€Ž9Ÿ¤²QºG‹6|‰³?mƒuÅìÓš-Âÿ<—7)Ò´Y¬7öƒ¹FUĪ[ŽçŸR†‹ÁÙû OaµÓºg –ßïàj¯,81T…ÿM•Î[AF»­u1Õqà¿ä®“6û#ôzý‚Wöƒ¾Í8¼oû˜‰Ý†Rû&ÖKÁ¬ "tz|)Ï_t$Ì¡çYó‡áj. ¾(®n€a9Š'ËReêTM·ƒb¼ÙÍQ£iÿ#90IU‡Öî*¸ÝM¢æôÆßV¨¶Úsâ hòÜIFÁP1V|I…71ð{S)Ûx͇õ{|Bµ\WX⩉‚¤cØî Öæ1ÞO—Î"ã×yÑ÷ä¨ãwìñ8ÊvL½$œÐúN°ýÓk”·™ÝŠ=Fþh'coShK<"AáZà,úœ¨‡§|”EG˨÷öãê½»ÙV@;«À3?ï)©‘¬U/ÈÏO#É<›¨þ’ûCŠpTr“Pâ#¢Ëƒ@øbõÒQßÝW¸qJ3(œƒ6Îfs¶ÚgÜÛÚÒ¡QÙ„Dµ¥°å»×²/=e(òy¡ÓŸüûp²%u¬aþ¡Vú÷_UëÑŠÑŠØ[¶Z4䮊øÍ&özËS…ÓZ8Õ¤ ^LÓÆË‰7ðuçm²iÑ8ã|WEá°q†Â“¿úÙ§‹ãxߥN64í Ûý³ª·÷ƒíçzÌÝÃ'5‚î‘×p’'‚–É]\÷í TïŽÅ/jšTùi˜¼þsõÍì»säÎëÅ“káîÃpN“a&›Mà`íØ=¾†z¡ë—SáC¨àSº ñç&(Ýêƒ{nŽ®;‰&ü<ÌÆ7ï€ÓéjWPÏÇn]¬Dw·{pñï ,Y¦À“âÏkm(8Ý«¬/€é¯vu ˆõ³‘‰¬\-è­þ\]eîÍn}Öî Ó-®@f½ìOÊÅô ›È(>nî)Z™ÛöŸO3kò¼@žMˆtºã³#J†òS‘Aâ!ïÖrz”¼ ™º»èì­ß±¯f·°jju½ È?ÖdcúnúNy<:õYàÜqB¸ßzµ¤fã‘ ¥¬?z°£ƒøæ…+z*OÞÖ ÏÈÝÃ<|}È¥H'h:˜Ew^?Æ­ÒdÚt ¾³)ƒT4O溸ó–<õ>ÞÅJêÕùòTyª¿9 b%døÝU†tÚ ê]^†Û³!ôpùuë0¨dÚRÛñáÈ,¿²ŽR_xs^=ºõ‘m™Ë jQ¹(• «¿¢éû*òÎzÕNJÅŠÖ2Ÿa¯X»öEà!Þj[  ûñ€gÉŸUkµ@¾I7+O‚½©úü½]“©Õ¢£~mÀk#¡æj-Rµ¥ÉD°í¹í¹æ³OB«—ø(õ²=ùÝèpOœŸi(cš!ÅäÛpQPU…§tŽ“a(¼óDx©Á€ëñ&Ûg^Á·ýWàØÍdˆ–‚_—¶±a³â@àž/0M3ÅÆ=ºÄQê,¹'E—YÌ„¼/~8Õ3m]Ö@Ì üð5ö‰þþwÝBxQ½[#ÐÍá0ñ·Uá®/ñå_s45eáA/ÐÖYoùf€ÃyÚp5gç§›Ž·#\ÁŪ p'µæZëF¢ÚÂ>ÌÜRÆ®NûËß Vz§ƒÑ¼X(x§Aóz´xH¸Åñ¶eé—÷¹ì½âú ÇaöùËÌ9jËÈ4|/W¯°@A…¯ªûE¾}> QUû Âû&6†Üñ¡Sé€ÉBV¼ZN|-!O,mø¬ì[²ÔÞ|†°aötì‹xpÔ‡üqÏgé)ñ¡„ñ[â9*–`\E(¦™=ÇlEEúû¤‹û³&ËÌÅuÇ©WDðžE:ß[éÔA\Ö,öµü ‘3ä“6íㆎM°³ñÛšËà¶q2$礀Õü=,Zo ª\Ò¾×; ¥² Ž,ÒZ‘>¯ýcWR¯uSøöO„î5±$ J¬¯»g^2åÞ£èì?u0ø$VFFðßospÌä{nû îÕð˜X/™VkÉ.XÚÂù¬at¥òdZ_|QÈÇobÁ‹Þ—',‚šo³ùù½[¸‘{ê›f¢ €ucÒàÔ þxT-æÊg¡‰îQª'±†S³à ¥ûøv×ï  )(Öòfþ‡D鉅WQgŠ Õž$EB£Ñœi™ª0˜ôª7LHÔ¤ã¨Óé#ÜÅ_âþáÏš$ôh\ÿy>}ñä[ñ ™áç©*‘z—aG¾ ËŽíàµn“>x‚t³ó8c…½¥åÈû,Ï#GÀ_§ó:&û÷À¦mŽðòÙ*ò>Bí·žØWJÆ¿MØ»2x3ó!»Ùó}托°»¶².oá­±Y²lƒ`eÂðÙDáh~=ÔêHÑÓ½ønÏ×õc ÎE–Ä}ÃF=qZ]õìS˜8÷8ÊŸÝøÇc%ˆ™‹r¯î!üÒ~Efôì'”Ÿ‰gð_ÞltØ|¸3ÓãÒêà½X$4ÔÆ% §Ï7'q•¹Š’àù'yÖ©‡¢_÷À×ÈDXò° u;6“ÊZ ½~(ÚxÝÆqZÏɾÙÇàË’E<³y=žh›Í÷t‹Ñqä dz£@1Ûî˜fƒŸ×*°h2æuií«]Κß'â¸øxÔxô ]>½·›±Ýn oÍ?‰ÇÄ »ÊNs]ï/}…µjh/ç CÞŒ¦’2ad„G?¦Þ~Ilï샧>Ãx³glŠ?]#.! I:ŸQãe4¼V8~aãxçž)X~ð0ÜyC˜ÐáVª·âóØçðøY$yð@”Ó•ÇÞ2s0¯Dœ¿ .:}bwg>…æk é×Wæ·2„,U .™ÇáõLcö¦7xýœ4}‘íŠ&'‹aºF ï jÄ‘ (å| ¶ÆËð•qv¼Î÷*µY¯Ê÷ëò…û€¾_—LkëðÐ:]~z©+ë3ù"GAfÞ"X]À”E¸¹¯<äoç{…&ô¶hé=jƒ³“/¢®ûbÚ>wý…àï¡ëÃhê&aÈOt'AqU $…ñÜœþçýœpí‰P6ô<üù sªÔA}È\5nN]>‰¨”W ðÊ$N^ì‡Þßqä½0<››öOæCE‰SkÒ†ìO!%Õ„òA3ܹ +šk~œ‡+ožÃÅSOãÙiP%: •=‚ÑÙ¬òÂ6¿¦A –Õ3âêÎß{Šþ§‡fMŸbàˆ×Ì£c3½" GZRðT¾8¥ÞžÂ!bSø¿˜äm‡€Î·bË«ýx½Œ U{dŒYøóQ „­0£KûW3·ëÝÌdËH÷zMl¯ïƒifQñ¸NQqe9çYPE >9;Ë<ˆœOvÙ‹…{&¡’Ùl›aÇ3°XÉüžübØŸ‰s5w’RñÑ$·ÇÚžWâÉÈ.ÍÀ#»çñCÁ7pâ–BZ¶?]Ø~KÅRÁÎ8 >Ä%ã2Dž2_ ÎßbÌ^~qGF 6Ó‡xѧáüO×"Áóguìg§Džƒ®¡…$óën.›cÀgí<&Œ¯DT~XC"–:ZÍÃ(ÇeñÍÚƒôÆ+ª´ù/̶ü OØ¿š»î5v¯#í¹Ø£‚ÃÂ}wÛ cr^µœÀá®+ø;ƒ($5d€¹˜ˆU“6r÷—¡%`5UùrÜn΂C/ĨàÌ)ÁwñHH”8Œ»:J…Fd'.m‚Èyzë'²ƒ{ÍhuQ26 s^OZÑ`â*eÖƒ>š¼{C ˜®ß Â} ÚotéùbóþJ6fŒ( 7–â‹zóÐtà¦cNãŒÞ“‡ŽJêüØ”ãôBÂŒÇó4ÏñTåÀ"^À¥©û7ŸÌŒ¸ÄÌÌÐuÀGÅSéòÄéäZyÌ6YËŸüˆÀí'WðM$‡¼×áæÚÉžI—ׇ(ÓÃù2t‹ÉF2ñÒf,1„nVPçN¯ÓSÉŸê QÇû°9DÔÅ:@¹:?®¹&ˆy,A‡§… ÿó™.ÞЊS?ßÃËû—’Gƒ§ñÍÊl¸â²J»2Qßl2Û¥B¿â­­¸…¹µ|>Ö¾¼ –íIØŸ²[ØŒoÛ‰ý ªVë ;TOÀ·¾F§?¦›QC+®¨HÓºM¬*Ïæ‘#þáî \¶OŒÆ¤\€’ËÞ4/²‚K½ÄÆkó˜d3鳕¼«€º¹Ã ´p —ýD@f??UbÏõ´‚xÉAáÞ’J ¤|Ï…gè³á.ž““ ¯ŒÅyÞYñR„*Þ&b5¯ÿõÂO±Wa=Eɼöô4zUWÙ™Y1âtP×DWDað†zzÉ_™È‘½”Oy¯­V¼Á¸ŠÊ¸ëðîÂ>Œš,…޽‚‰Åoxgn?ûx„÷¾ã¼óV4BT€ëN‘òk’øj™8ýº´?(@ßpg0–ËÃwY’ü^e.Û=y9yr“#¿Q¯À ¯BØ4|:Ôׯ½€)W^âÅN2„qó®áõLYz×DŒò…ðñ¢ OëXÎÈØ2œ~> F‡ J#n€Ó»¹ü’[Ù³ì~ýyëãufüÒêNÅ{þÕÖêvŒžaÏ•Ôôa×ÝpPX—kÓðyÿLuñÀÃóO±3"„ÅGµ9yúVâ^“7R8©o9`±—Uf’ÜL)¾µT ë–UâÊš ´æÏ <ýrµqnƒ´ÏoˆÛ÷Wì|ž5&¾mTÁºw~¸ôS3ë©4 ö¶I0ð¢ ÷E‡“ w¯Y€Ã=ÛœG¢žm3)1 7u¸sŒUYİgËA”9“ ¸OF—ÁV½ÓdÜ”ÃÅl!â è.µàµ¶š8´ý4q° áþk0FcŸÐ<†úž/ƒµb˜ÆÛ1ÿmâî^º¾m©&•¨s‚«¿šÎ|6M¡V?ÃéWij.þÞ›[cŒÏPzsß ˆŒd÷•‹`V‘îóáËÒPýw?‘¡ ²1îN´¨ b/¼/Í»a÷ü/xnH)f‡ãøÓɽ¯0ÕI’FßÍ…ïÊ à^ÊúÍj/DùH~feßž†`ÓÛ8œp? ßI8ñ… R”? 7÷×`Ñúϱ±gÞfl=—Ìþj×ÂÞô´/3ãîCúÉXQ>ÑHš z»°|É9Ü^â‡g\69uíÛ Óë†ð…‘È‘p¼Ô‡–n<Ã.mãE;aÑöÑ´p åmZÎ4âPɬë…0ÿjÌ ûŽË%bé­ÙhîÞÁ¯.àÔ+%8ei8xŒ…[^…6jÉL >âÜ}ôÚý Æ+­áòÖÚ‚8k)žtï1èºÊAÌÕÅ´eÕ<^Iϯ<Éï=C‹F¼¢>«>#‰_iÄ¥Š.sÕÖÿë´‡%UñÓã0ã ß µˆ/5KÙáåYzk¨2mÖ"C© ZïpA ¥ÛÌ›F@Ó#ê°Xž.L˜IG_ÚÆìícÛ£膧nØ7VHÁùnÈÿ7/·ÎBèwòèçtp°:H~¯(âçEÂÒ¬Õ\Ôç ÜqÔå—ã÷]J´n‡!ùe^–•MÆ öBÚ9IºF+Óçëáå«'B7ÇC j…èÿž•Þú\3¶9€KKÒMZ|þ´ШPŽ}C­ù½QVÄy¡ªÄXòI½xôñ3ÌmÀ•½Y;„÷oýÊÂö …OGt@Bq&h”Üf'³ùZUc,Íå5w,éçUÒxÔ7‹²lù„‹‡iµ¦NiúKÜË.Îñþà>é ¬¸]ŠïÇs·e^s{¯ÛB¬í+Œ}ŠßQP´Y¸sF$<© ‹¯¨AÙIm Õ)ÖkŲ›ÃáIA›ÑhIᮑùçë¸BÝ:{p-6+†Ò9{FâE}>(”àø­ó%ï1—aï@wÂLáµãh—2‚¨y S6ºÐG’ãYÀTK(«™HîÕX€‘ï1\>¯3äÂÏ¢ÁÏ5³ØÏ!§`×5¶_ .¥ž. F?¼C# ‚náÞë_P÷qíÞO’ðiqƼ/ÿå ‡by£Âl(ÒÉ„˜ÏQÀK®Bü þvÜjâYLyÇ,2ëJ(5x®Ê#èñYÅ„™zžQ[½Ï”?P@ %®³ñÛ®²ªÜZÁ˜Ù7@ÏPŠj&Íeá‰ïH¯O(Ïnt~Ñß*\,Œæ7¬fð¾'ŽE½¯¨45ïw£ÿH.ÔF_WO´Y’Î眦ß:Åø4A5qŒnp_ˆŽHßî{ ¸{.ù€†¬áî²ÜŠÊÓ Öq仯Pn=b'6+oáÛòå¨ãôR0àÅ£>Z@U£Ýš¹“þu6§®MfLÁ$<ÅVЃƒºTä²íýoÏãýŒjüµ»…žàÝYQÿ"[Ò9Õè‡ê¡ôì&®v=„º‡Ðïê(úÕ[“}xaÉ¿;eaµ„.}}Y‡¯qzÀV¹&2ê|{1{þ/÷ЉT[ë LÕÛ@OïŠåŠÃ˸Ëç^Ðí^†P•¯²´P¦Ÿ‰àqX!tæ—~¾fR¾úhØ›>hÖƒã¨ÿí5òë¿cxä캅ÇÞ*ѧ9ù8.?ÝÞÃDÓ0Þ:Å’°¤!Â"î¬ß‚ÓlwÐŒm‹øâÄ©°9ýØÅà {Ô°¼Op bþ`lšÓ„â.RBÑ—Þ ?ù&_?€»›fƒË‡Ëž¨éx­ùFn|‚êîRøEàä×Çö eèÛ‰/'Ø‚uü(ÞS•IJåà?çx/lRìFˇ7Âïs`>ðoLG–@Yß+àÏ‹4gðåG11Ô‚¯úÒƒr' l‹À¡ÕKÙ¾Ç?Ɉµ «‚(&m¶ú± ½!KÝtKXbW!Û™ÍÚÆ©£íˆ ´ÆßÖX˜ÓÒ·†üø†ïø¥Íul«ÄsUèÝ®Z=ù ïZZ¤0ƒ®¤™!Ÿ³²‚ÎKР6"›HóÖ±~«„ŽöIÝÉ7¸ú·xÝ¡7ÆKÓ ›é¢‹ÎtéëQÜÆîÉH‹Àª{ó1sï!:ù#ƒ`W üZ Îï>!©œömâ/Œª Dk= énÇ¿Ãhcú0ú(m7ê¼FÚI;GD©¬Ö7eeš½RNûEÝ×¢üö®s5 w¿ÄæÆW(òj&¹ƒcN³}«Á“µ—Éão=PÕ‰~Sv1IËyPeÕÉaY¸ØC‹N1ÇQ) øIæ5„«æ±'ÎqX•3•Ê&ÞÜ”’,ÖÉ7YÁܶiÓòIW@ÅÿÆŒ±ç#…M‚ÖWAüÁ¦ëtt`3®?úTùfx.x _MyûØ)|Ÿã;wô­T æ †´3w=Ÿ»¦6‚¸ÊØiSùws±ä@oY+XÞ±‘*oã½Öñ ò ªri˜Ó~nº@ Ë–q•cqÜTU‹ N‹ea¸ÅSŒ›j¹sâÝ-Ôª„÷+í ×¦Žýw]]%‚üV=ÔëÄ…ͨ¤ð×Y”¡ˆ() œJ¯í;¦õá¾úK‚òÚ'äSãbwÅZÛC}g%òåãÑæ‚¯Ë©ñ÷^|KAiÕ¨ñÛ Cå /°²âxR±wêPÍ¢ùxÑ@”~~ ƒç´ÁÞóšttؾ¿k¥Ž"tÑi/,5 Jšñ~êk g4ØY{|ŸèÍåS§óOõÙìBw æÖŽ"m)[ªï¨O"SÞáïMj~V¹Ï›‹KõE¸ÏbS˜YÐ¥¶¯HàÏz<½º¾îþ…Ñy/ˆŽÔx1!ŠõuW ô¥‡Bû²Ç‚×ÖÔw¸Ð ëF;ÙÔ~ÇÌ) Pzíà{DFšÌ†ëqÓ­Qlfs(,X2TQÓ¢šNÔœÅÿd@¸¢9ºNgºÖ¡0â0LZÍÿTÞeºZ?IŽ“l¨?…«Gžc6âI“bµüèÁ+Sðט|wd&”G9}e^óò/¨Hý†C;8ø\Åca³áS[>îÑ]É»× ª8¢›ÃsH}*ó}oÀŸR;þJç0O4ÏeŠQüïc“ú?bå—ÛaÕU‚ -ĵ'•{?âý¯IüÕvYÞoM´çvA¼h¬»À±'7Ô&M×HiÆQ*__‚ŸDDHASñeFì”ExHz Õ8]Dg-‡KòÐÒÑ[è© ÐEÏ,…¦ÑÕLëñ}(žuµÏDû,ëêb§ÑŲíKºØÄõjôd®5½^>’¤Oÿ6Áào5XsvÍ»6‘NLôÀÙc/½ÍÒ®F‚ÃV)zàÉobúÖ,ösxæ§j]Áô×Ö¡‚‰ÕŸàÁŠòMØœéëXí÷Nrc¯¶fM QG¶{uÜ,¯Ã/êíxý”½>˜…ž–VF58¹œI¬ö 7Óærƒ*;ä*GÙhQúðþVµÓä_½Ã×jÔ­D Ä`S°3w·ùuf\S8«*ÉÝIƘþñ¤tvÝK¡ 8³Ü*àžóíä¿ï-,͹z‘3º·¨ÖìJÓ‡Ò½ÊôÝVÖ8ë<,÷QÃyïkg×€z8¥?V/6ú}¶â¬IÂä„©üÈ9G|õî"ÃeÉ@óáùûgx*{^Ýy†‰ìböÞ¸AR“E ã=¶{…3gdÅVA‚vÚ†#ŠPïãF 7åpçž5¤í” ¾UÐ>½‰èu¿¯þÚF*ˆ÷\Ò†áþ㸉ïúœ)Ûˈã[0år Œ)9‡¬L‚O´›Îîç„Öu¢Yˆ8$o:÷=DàÀ[5*;T’<öHw±6c[ºp’$›à* S¸•³ʾ“Èøìã]T˜€šOTøåäuM&Ùüü=¬d¸ÖUƒ$)¶l6® »îçàé!î`Û‘†6„‚YQXÖ;'Iñõèº^èg FäN†.»½üÉ%ú2ă×X wøÉòˆŸíl™S&Tþ›#¡÷\`DZ(å£FË[¯âÂ÷`È`6Ü\¾Pð/è¶æ¦sN’+“Vñõ-®XaN¦mlÂۋ!C£ç/b†¹¶$Ôâ3ÿƒ+¿ &ØŽÄ£Wf¢wù>’ ï\5JøAŽ>?×ûÑüï;ú™Ï4ó9÷`ðZ+Ù9‹“9~'7Q_F ØÁ×WŠÙú¡o±lý ~¼u:qm–MÌfK/F@õÍkDkµ8ÿÓ>ƒ~ÛzÎÚÒô‡ók •À¶»JtóÔtao"+\–ÿ(™Æ¯í\AÛ>%aßé+Nzê3ßÇXêÊf2Vœ'?†Ñ‡–Ò|)ILß}˜¤øŸGû!{ÁV+—Ýǧ³`¡•(r¸m 3Ähšù9¦5@ >íA'÷lYŽ¾Ü ûëû^¡†ó 2íT<6,…k<ƒ²1Âü.‘ûÓ]¯×¬”¡Ã£ÞãbŽ`¯tòn#PÞ˜@’´þwž@âÅ~snL(_…6,+œÉwsc’§pM{qZÓm]Ñr¾5U´I…5»} ¹VŸ™ãôc²ð—Òs™â¼]v3)Ü9‹LÝkDžÇ‚RÈ>ÁöÆ0ÐU~…ëØýž°æÔQ¢«…/¼EÉséCà}î.†6¹¿·"?q*\Fë`¸³™ 7”+×%ÁÑup£T5?G“ iñD9þø5‡ÒKÑ0b[†ê*ÃÈú ääÕC´³ÓŸj-ïû27öuý>6sÆzt~`K?ì4ÇŸî R­õ-ñǰìä¡~œµë8½Ð¯Nc+ÏÂìaÜÏt]Qø %ŽìL²ðÕ^Ý&š •äV¼¶M•ÿ\·L'[ÀâŽA²wÊ\4IŽn¯›$íEâ$§²íÛ¤ùÛÇ#ÐwGêU¥—”s`vVþИKŸ’ùPQô„ {mÊÓóÙXÁ/²|D*Ÿ„£º^ p¡1˜­¢Æµå°º¿¾$ ýi‚S5_Àc_89þ)6Œy­kæã\‰k¤óüeÔ¼À>'€vww9Ø¿KÄø‚,9jtÛœ¯¼&1.l¹ÜkáŽ|c¨-.Å`ñ8ÔÐmÂ÷ÇÕè·ÕéðíÚn~¿à#fD…Ñ—ŸíÈ+»~ˆ×Èå"+î© ~° ^+:¯’å#¿áºä«8oÅqü±&·-Ø ÉN¹‚ )ëÀb®“, 茬ÍpßTŒ÷$VB¿NÔ:6­]~¬Òg)¬óÞIòë ×3ä½u6¦ žW»kŒ§ÑrL,†v…:âÖ‹õ?_@â9#ºâ³>—°ÉÁ°pc>âFyýV^da'wÊàê=›Ï:-ÛaëIQ€ˆ4EÚ;I;-M”2"%++«¤¡Rñžû$’†¨”††Š¬¶¢õóýýõ¾Ÿç>Ïý¹ÏyÞç¾®ëó¹Þ{v…8O]0Ë'Yвæë‚¦â³déÝt¸¶5‰]̃ݎ3`‰©º÷ tŽ…¾þ#ø(O”j¾•ÄÛÖ§àËõP,&šhÿ|‡ kÏ’ú:œ¥¾¤2à"ˆ¼4ø/Fä†åÀ¬UXcÝŽŠÑ&L Í–u?€“-¹ìºf)±¬+m'È_sÇRªÖ:šÎS4†Ñg†ANæ 2l»>kz0 öÎsfñyX/DÙx3|aýcwBiÿvPf.[˜'d/?#^͵°s1#ç†{àë—¸óÝ:عç+,܆州“yÞ 0½©‚¹.ÊMN‡‡÷L(¨üÀ/ =ø²è 9ß´Tc­Éà½]U£Vd{gIÔ¹)“}EÙ‚­[1>áƒó«Ú˜“7‡Yöcæ‰bö;ïô®ûÄz-2àwY¾üz§={O]/â »LÒV—¶:kРx·ñS 0á¦n-²á(KSY‰SöÀB9oºæÀex9ä‰OX31eìšõp³ŠÁÇrëY󿥸lníÖC…)ÉìÚ)ÚÓu•¬*SçbšaìÍÔÔ¶+!›¼§°‡ʘ}Š)YÙlKñi)_t’4ì)¨«9ûZQͯ'8ySƒgcBðAê<òú%J^¶ƒ_“:؉Üz3½±êÂù·e¹Jƒ5§¦ì‚¥^üïÿÖwqÈÑ8yÙ\Þ8ú9äÿbWVøÃHÛ7ož Wê1ݹÞoùSµïÃ=ðoÞ…e­õpó¶?y¸k"5›6Œ*9rc—!d¶$‡]ŠsØßš‰dlÔ Ôª9‹ïeÉ£E›h­ý¬¡MGެÁ?Ï ÏK‹æk¨Ó¯Bñ–|#ÄjÀuò3zô„ûV€ x›6>x¼†÷_ÞA/M…ý6äbÚÀ%b|ÎÑWlãÙ©`^™‡×ÔÄȇ6 AwòˆÝºXÍÇ¡ÃM@é—L0úCºÎáC¯ˆ•~¯¯íÄ —“ûÔlöæbM—_J„ífG u3ªÙ¶éÞñ¸+ö1ù5ñ“ÞÎê:õ¦Ìƒ¥r_anâتPí|èŸV¤ÀâÞôW‡Õ•cÊJ2Uü{ætuêÈ’½a¬ú¸14 OIvàc‘·p{J$Ûzåá½dK°€õ¼cµŸÂaèðBöÕúæ¤T“1kOS¼Rɳi¼ºC›Š^™Š6jïHïÇOð~­lørܬGãûN—i‹1ð}4djË_Žá¾_÷ ÷Ãdლ´V®¯>; r™XzV™VXœúº®$?EhsZ‹³¨Ž ö¼­ŠnOÃÆ¦D»£45®°ì—ç™@Ó€]Ù§N†g›D¹Q\8´"YUmí›5ÁõÏh®ä}|Šâv°»% 5–µY°‚7a´SŽ`ù о½ÍOÇ¿7kÑsø"ïjMŒ‡Òòmhò5 âJ6³Cùo«®OUÆkéÛˆ˜ÍØy’9Å•ý°aÔmñ"ü:i$­1¢o{ÁfáMþäŠUxèÌŒ¹Šõ£ëqÔö×äiÈ“¥þIB‘mßr…—?çøb¨Þ8•bu”æ†ÀàZ¡Ó¢å0Är5¹1¾êKAã}0PŽâ—:;¾sà,ñylÏÇ©òžŽ\ë%}w·ÞéÔ EãeT¼·ØÿDŸátQó Pû™N¥jy¿"Bë&ýrИ·dt ð¬1m:½ÜÙc¨{¹߉^…3ÏE¡Ëd•’[›÷óëf²íá Ç?~ÂÃyñ°mê? ƒ÷Ø-gGSÖY}Œ„?ÇaÖ¯YÙWø5ü –ä”Ãå_ÎØÛ(KqK)È/º„Ï7†áÎ\<¯Õï^ÝÀõÙïÀ( ¦.>…ºÓ{ðÅÇðÞz”"ØþÄZ´èÉÄXx¨ vHí$³fMƶWYpàg´¥ƒMÇhXë´Í"àI:'ïÿ*ñ…}µ‚kél¥ù°ôë²|ƒ êU€}­¾¸ÃÀ‚d8ÿÂNT3ãlkJ),Ðú ïJ¢aL†63uµÀg÷ò'Vi#ë>€Î½éÌGªÆ…G²’°?,þpdóiƒœÃšßVV£7v‡àþdmœl”át²ê%°Äg¤=0DŸióÞ¦W Ñð€y÷§³¬± Ví£.g‡P-qO zt^G™±a>ùÐ{¢O~òÅqV˜´æ)øÔ£‘xØöp<¸í\ŽO‚(fÌó@ô8]\ùJ&$ÃfB×W„’L…žð»ˆ‡šqÅá xùÀ…µ}¿§ß¼÷Sïà’3:øÆç¨¿*@²$ ûã*31ZðvV"á¦ß'ÂÛ#0½ÜŒÅw ïtïÛQ©l4¼^)ÇìLr[1@j¼3àÒ ~kt3¨4×^ŠÿíÏhô(¦|­¬2­³¾±ÝΖôÂ’qüBí< J¤2 däRdãq*FM"´ºÿ] E>IT¸¤$gÐr½qÌê‚4Oúõ™ì: ³ ʨWd(ÄŸY†õr3³R“iÀ†fÜp€WÝ€ hߘ ÔÛØM–Ç/N|¼éW8¿yº§¬ü¾¼¿•ßóЃ~;`QY9{rQ@_( ¥¿ “‹Õ§•çð¿"¹Å~àñcÓ®‚Ã`ÏHÔº‡&©}0gÏa˜gÓ’¾ôì‘u\!æ35‹æO–Y@ËiòöÊ1ÞäMÖ9 Ã-®£Õ·Ulß§xl0#ÎLáÆÓ?à´7FpMI‹?ª R¿Sc˜Í©•Xž­ *¿ÉNò‡Gâ‚”SÂÜÏ*4@ÿ¿~ö2 <šk}ºX ›%¬qþ/ä“@kþJ,ä±ðõ)4jýW¡¨@šº|¾gc–‚ôvxƒ*±…dLÍåñ)Ü<=ŸOÎiFó¼!w?DûÅ#¸÷&zéj8ÆòÔåª|0V5t™G»ç÷É ÜcÃZ’ys,wPø=±_àó8~éÚ:~uE FŠžÃ¨¿ç;>2¿ AÙh¼øsº"?I-iJ·¥–Þ=lDÞhãzø§á`8%ýOAG½?{RžOâR¿ ¼BÑ“†¨l¦ÆKPíçÅ*Ú˜|æ»`Dê3ÐÝLöܹæ,È\Au¾ør§lºN0½ˆ ÿf»¸ôÜçŽáíðÑä:¾(ü…EûéÆÐ@2*+Ž‹î¿Ò—ô¹²Ã\¾¤Ý˜ÿ¨ö¡tÂbŸ lÄ9.(š†=ãXÇqº*ÔŠv+ÌÀCËÅñüvBg0ÁÓO8jý¸€YEyèwÀ”>ªVã“n>€ F`½ó ü´éeKæ]Æšm äÅÉßÎno ØËY¤ÑZ.vlÅ?k®ÃnJW|pæž·6ßIaòøï,ÚMS`É ð¡°zy6ÙU½˜*\#ô»kµàBªZ¿~ùOÄ%!ûáæ‚[ì|È(ªÑsί؂¿™€ý™á|ê‰Óà“þsKéѺpWGçN®çNÚ yZ¾=}?‡¶hÄ«W|Õ=j%ðÏî7ϯ V_ë6ªbQÛxë&G„ƒ^ë~,% ¨'‰]rYDIRÕ.Lš\ë¼hû˜mw Õ]|¨úæCØbtR§Nä«ÎDq™LkZ½Å€†NÚ̲ÇÊÒNÙµôþÚXx%=¦ør¡'ËÙiÄ_¤DÐûKžƒÞ·E¼²_+/‹µfT6øÖ@Óß›xÀNì9F*ëÓii»5í•…ûæ$² 7.~}to¬Áþp¸w²Òʧó¿7“1r¸rU@Zâ´Èa·ÇÔaž².3ÒÅB»x<뜋¦SAÖúÐu¬ÞkC÷’ôªUº8é“ ÿYóí¿<üSÒ"á?…-á†zÛ8¶çŽÆðƒv4Hã>è‚àZTôþ'Lš´…~¬ìf†¦}à&~>¬Tà'Çl$ýÝf4ÃáÙ³_ƒ^Ø>ŠËÎX÷(f½L‘÷¿.Á¾ ªgù¿Wæ ‡šfeÁq1'¼u}Z‹lœøpŒ-ú“ Yéë‡&ä®&°dÝ%äÔ’"2&ïnWR§[„e¿¶G¦T•”eGg]‰•¶¸Bu5‘ÍÓ㣴£}Ö¼®jëCê;_œNxoT5¡â4®r a³Ý¥1pRâùö^‡uÿ3¶÷ñ«Â~<(]uad»/ŠþLnÞÉ_åÊb\[’O'±b_oº»EìçÂôL9þ.v5è·ùá°#O`‚#³~ÁL= Þ¬& ù;EhÚiY²MîS ºBÆ=‚ùIòpó^|·X×-¾­néP¢ý(4 ÷ë‚ÑøÅr,' 02¨ê«ãþ$)jªr&°ËäÀg”uxÊîê8óþCø±·à@Õò¸–-ï–æ6Q¢akÍU’Z—ýâáçø,Çá‚'4¿¨ä0)ÐC¦l‡’:x`°Þ¤þû·ðfx‹óK?Vµ­•§%Ú+ ÀóvO €Ák‚Ù² ðT"½öZ•Ž ½-õ]cU¹¨© óÛúêßå”CðsËlÙ|vÛo&¦‚3,ÐTŒ.›ìÜØ§åg00|½+~RúUéä×»xˆÓ5êA?û:ª'Ibú±‰tUÅRèwË…R«"RÀFcšCü±,f>ëjñ…ä4óÇñÚV,rÑK(ׇ »_ãSúîC%¹pI‰.v¨.6¾É$>@§Õ©rRþ`Ïî‡/W8ž}<…;©´2Û–ü¯r? þ}ˆ<ˆä8Û¬ ö›€›Œ,<¶TæãÒÕè¼²PºîßFؽlˆ;áÍ“ÎÂí?«¨Yº;ݹÅñ^(´øŒ šüç‡[è52]"@Âô~X N­FO¤sÒºÐ(i,nä߯•;Ï;i»ª£ý §«€¥V‹ ÓKšxǦãb7JãO4I†îMlµäÒûthì¨è6V‘‹®‘’ÇI±yŸ¹ýÅS±÷ÊÈÚ©‰·Ù x Hצ1ñÍû«R¨)}qk8uûäË3?½ÛE1j+v©™±–ª° pÆDàå‹ñøLKî廙Ú›iÞ+Xë¨ÒQ¡äÒ÷Sø72ž”¬ƒÛnŽè¢­Žµ£è¾np¢KPåÈóA|³¨ó1ÛDV£9v›]„¶-b¸Ç) »3ç Ý«wñi¸··/®°¼2EºUofôÏáößaH/ƒ€Ï'Ér§à&hÇm—Â:™‘è{i'ú¨Kð¬*K¢~‘­)ö‡ýÅÃùláEl¡›X‘˜þ4“ÁÕcôéÎty”u…³lÕ_˜²d°IÛøuuMú[eNJ‰Pßë“hß»wX—]B«Ã ãË Lí?ýÃïwOÑ}+³è¾Žq|¨ü%š* 9§[ɾçoÀõM›õ,‚?Rx–Έte<¹}œ(Xìáv8€Æ¸C¸:ß”î) #=¦cn:¿£óû]‡Âžw` 褢> Ö³ÉñW#ìè dL?|†¾X‰13(ŠO‰à¯ UÀá=_ps•2´¢É—‘%vºÒ{wi¼Ùq®|p$ê5›á©1ð´Ï-v÷Üxœ-¹ŒÞ¢áÑI7$]0|äfžš³«G?%ï'&ŒÀðŸ·AãÅ]¸üH@­ÿ##íÞãèM>áb¸3ê¢Ðr —½†ˆö—xð¡q… ´E‡| #b¿6ð£Ã}³]x¬ù82÷Í|:üH 2™ a“½@åµ;wF‘–‰+CÙÊ{l±üeس¶ÆIÜó<:̾ý¬§ ø¥ô»_™ ¯n:m–á8Íl=²f|η… Íè_tœÂƒÑロ|¢M.¨Â£ œqÀTˆ â"˜PœCüÅû«fÿ±kÿ"èͼΖo^ŽÏjoá¢a±Xû;î¾ í®2ú­>^ù‰°¡z>”~Òæ¡µçá…Í8Øq¤ÌÖtBj÷wö½÷1Û´»¶üÖÁмN¿ì$zÅa*¶…ð,§µM';`ƒ­)ÝêËœB¿«7™L"ü¹Ü@×êP훵 ¹É“º¬òÄVêüæ„l–ùÔÈž—¦gëRýcsyFCê|ØIU{*ÈÔ® g5ØSk/¢óAËã ¬Y·†ý]£È}FÇ­S±vÿ˜OR–ó1°gú?t˜½—9ňóŽG˜Ù’‹‚Ù‰¾[Š5Šr¯A¾¦0û1HüIĵž_qÙr¹é).ÌœÀlo:òá,½}ÏÝ\ÓÆ‘׉R9Ý·Â_¾a€Ü$Á3—Rp®˜óÎÙ±®x„.â=§É!Õ}°=¹‹üÄ“¤3b>|÷’TýШ©žöœÀ{—1lð\åâsQxÙ‹–ET`¥å <-úöú ¥9ý!«ä Þ «B—=7‰ÖÁÍ ôC‘Ö¸Þ‡°Õ{07J”Z;籄Aš ÉҨѽ‚óE-¸¹{7~Þ”†Q•ÃùyÙÁ~é{ ƒ ðÑ8vlª¦ðÏ{]>½#‘÷g^ÄY_=X¿CŽ&þ„ˆñ±  ΃vñÑëëèÍâXø—E•)‘RXÃ… #èÄSciWØHîÛ7ŒÊVγEDË´–‹~} [ÊĨh ƒÜW üË':_lÜx,¸±‘¾¿›D¢þ~ ±²¡?'p‘#ûi‘õPú×e%=å~Œ'ËÇÂÓ[¡t`A95WSá0È…WùJ4qŒÏRJE™%2|×W»ið#k+œÊ™Ï&œáw'j’~CXp½ ã3=áŠè¨8SZŸ±ŸVù‰¡Ï Szù]>ØØÏäÒaWèçù¯W6Ï…–tUÚ§/&xãS+ž8ž Ú_â¯^\ZÇÀy“íaðRŠã7×ãña4¡²•nÙtžnN4ãú†Ëù’¯a©¤ìiƒEÔì ռöMüh¼Ìßÿ‚¯cƒ0T$,®€T‚#ݵ؟vymU_1Ëæmr~ÀGþ*ÀYÝ;¨xC6,ð ÃÛs©o6óø ü6P¥Ä§¨ùR’»vÓ·û’ùOÿáT:î<c, Ë•yŽ’,·sP¢«Féó¼À^¬Ë…mUTÿ:Âí_AÂÀ€VÂ@¿y+Á»=•~–E^º…Aþ Šözð£ï&r)Wu¨ì›Ã;JŸÁǹ´è©?é:€Òëv€¢×ÓZ‹™ûÓ¡ÃH”®p‹Ò³ adÞ_Üø\Ø1ë6(ñ¹<_7öŒÆãe¥ÁóÉR²DŽoy!ɯ§-…K¢°i²)j,Ù‰3{VaAöhê¥2:`ÞjWÚô š7UáÉ­8çR ²¾û—a¦B)ܽÏá:yÓ0¼8”Y/ ÄÙñõ0/å6‘¿¢Î{‡7W]´ø„§Ë·Ñ-çs Ë^þË“eE×Nç“4/9ŸH`°ÆF¼{ùûsa «ô¥¦ë‡iñ–´á¯ÀùN#pí­(f¨A§ßʧz %¤èÛI¾¨ox”ÄŽrÇGÅÐàõmA3£íÇ9Ó,÷Æ»õøî ÄÄ5Ó ZÐ.â8ÖZþ ãõ‡Qz$-LB:M“_ÓZµÎr¨©Z¡X—+_®Nïnö¡¥É‹ø’v®“/¡,_O‰aœl9¸ËñÈìr<£H›––“Pm.´úsžI¤5Óvã[rϪµ²-‡ãqWâ]ògq(_0z^š} #•±üY:»¥QŒ»Îa¬Gñ$oú`‰;5÷ âNõëÙòi:ªü<>íÊÇEUãÝcŽ¡‚$ƒ¬ÜwmœØ¥ËGOù %#xëŒßP£Ý 6 hŸ[¢soNU‡ž*5‡†l\…7ÿ^ª«‡QG-1œ¶½ Â/ÄC“Þ*,eDgýØ€ñ—'¡·cK`«­báZ¼€¨O ¢U–öüÄ8szåGÎÏ¿ ïÌ|lÑtæ!#z`Û"ÜbË™ˆæÿ=Ým㆘n!Ä–7³›ÎûYÞìX9’/W¥ÁõíeÉKED·ÛBc²™ƒ´;vû:žrœL³kBè7Éß‚šsiXîÇÅ®ÎÄÇ#\ü¯ ÝêIíÇ€„áFÚ1Ì›Z×Ö³ü˜åè(±ŽÚN¦Ð:C…Æ? V§”lå2â01úY4IߊjŸÇ0Çh)« x)Û¾«˜M†=3ꨴÊ´VÎ$ý&J{ZÑ}ìf–Ïžž›ˆ|~!dÝÏFåvßñ}àìáV˜xÚG»zÐl;\vif{ŠÁä {ñŸšÜ6¨Gÿoú²O /v;àñ C¸@ì=kØðKL¡HóY$UŒÍåvôÔùXAZŸøy>‡ß‚«¨ç}Ž‹µ.à•ÿ‚ß²XT{¥ººÆxJèGKõp¶4¸¹¬b£ÿìÀ?ƒYax  ·'¼Å™½9Xx.šçHàMVtYlÄÓ§°]CNã;on³áŠ´mgþ¼AÝ¬ËøGËl¿‹s×ø¼q35ƒô'Øæ¯À·M¥‡ö©Qb)Üù0 *¬èäüyô»ª58×û.)¨V Q¯`»Æ·ªWÚGp˜s,‘ È„GÂkÄ`Ì©«Âš –tþçlò>ã7‘Juï“âðk¯¾&C/(€Áp¾M„nئI¾ˆ’/«­…·dêpìx1®Ó¢Gô šS‡ñ%n®‚ª52Ük¸õâü˜EG7vöLg+FñƒZ‹1gCû„7–sÕÈ—3y°mÇã£îãì5òôØU¡ïîÇű¸¡ßlr¾C@^4KJñg.¢‡Z,iëDŸ[¢â"¸µØ‹ï§ôô˳˜ÿ،ޞ"A%‹lpÎùB ~Ëewáëc¸ÜbT¶D‚&s#îV1pa… ×3Ï5*ŸZªòI¦R4Ì=†ç¹à<ý§/ÇC†Õ#ˆ>v WxÞC.?“9=Õãz³¼h|ä>pt:„£ÿ2òýÅvl´Àz^>> †’=Ä|ç˜rêª7ƒ~»hj·gÞ\?œøñ«š³>†.›Œæ«úppyЖÌõ[Öñù7mù:ÁO9”Ë^µ¦ ü.=×®ç¤*çYûé¬Eþ*Ä?.6¡Ò_&pÿ³p1ׯyñ¼)Þ¤÷8»{#Ÿ'î`‚äðÑ]Âmß4à†ÄÉ|)5àñ%VÜ71œ'@Q %éA/ÈÔcò êm7ß8Gý1¤?©MæŽÒ‰¿àh±ɯ…e:T›Ïb'½íèmÉäãO£pR¥^;cÑõ¸פËaûäIðóV8ËÌ•äÚ_wbµÆV´‘'¬‘MÒÏÀ¿»÷õ>h=Â_ÓhÀÅßh»Bf¤2‹ˆ‹äùB#(‹¨!ÍÁ4%}þgÙ'<^5£¶ «Ó6¯'],3à .N–£ÏkŽA”bšuÂvW[˜8[&í¢åÁTGj¼Iƒ¿xÛ7ÂBZMi)šª *áÒŸ1ܵ%âôh]éBúqZ2kXâK‹ÞeóòI^¸þ˜ ¦©hàªäÇìšx-Ù¼<˜ŸÿÑŽîóÆðÖ…ßpXZ ëò‚YÖÒsWì`ô¬Iðtí"’l˜Guñ]¥^—D»³a ÚV'œ¡l@¥æšÁ ž )Q “êbã_SZ³ã4lv k]¾¯†/møŒº³‚U)>ÓÑõÿ¼ ©!ÜiN)¼½UDÒÄ“:ÉûB‹q©8Ïö„Fjƒo¡ œÝ; cë—Aue?+Δ‡¦ë–¨VñÿýþñeZ±àã"¦B¥Ÿ%é³ÝÂ^*c33Ä©Ô_ ®'wK Q'‰û­ñR—¡üŒ¿-ŽØ!àÇæ8â‡mdýYcº(`.7vá2‹Ýi\y&=²Öœ×`ãg<—…ýKkñÓš¾uN&ˆ–€ÛŸ\¸ÍƒÞ<¤Éå‡:€k™]aÞN´FÞÅ´*uªó÷.³)7à½Q~<>ÆŒÉÏ}ŽÊ燀yÞQvÉÿ2üÚùJx®Vž‹0¸ù937ô‘óÕ”n|ŽÆ‘¦$JAŽOøO† ‹ýñþþ7ÄóÞ4Åå­xû­—poç±G1A9 Ž^ûÆVŒËÓ|#ÒLQt~dýiñ¸½à p_^ jþ Bÿ»¬³åôê ŠÝòðÃ]\h<ýË»°µì)*=ž û¯JdIµ£·•Åî-þ ¿` ’ 禀RW $ù#©ÄDzwÆi‚i÷?¢x*뮲´Ë`f4Ãú?…8áf2npŸÆí~ëÑÿëB…Ì-|[»/>9 ¶Läž[5[ÜÁº¸T"åR„š»Ë°O³ï«ìÆÙa*ôTåGlQXsÜÇ¡‡‹"íªÖ¢Çö]'–aó]¢¾KŸ®_6@Þ´ÆâhË/\dÙf|e€8¾ü]Y÷éÁÀ:Ô¹ˆ­OÏ û•gLob {',Á¤ £'’YsØÏ`{ð5(|á 甈gúe´š% íEPÿƶ4~Då.VÊÞ7ö”¹q@çÎàW§JÒ‹L²Ç¸vˆ¬'wK^Ö—F‚&Õ),7¬¨wœëf)8º'⽊‰r»6ß0ë8ìÚPˆF¿O“ÓW¯Ãã‹vdùäƒÎC|pe×c²PS–‡¡ mþU2¬÷ 웊ñÚðñà1ØtÅZ˜ûõ<ݱ º¯³) …µxÖ%ecÿi;jµœ^ÑÛ3–^&iÕÚ¸¡ÝkÊ4Ê{ìiXþuÌÚ½k׬ã?êñç¾CùË? ø2Ù²L ÈäLϸÄt#oÃf¶ Ž+Çál)*¥Ê[†MGå'yÌdêOX¹Ë‚äìz|fîîRðìïx¹Î!¨¶•,01‡+sßB‹¸ [Šc"ŽAZ®”Í•D³Ì©Ð0<”¦`Jg$Jµ@R£6Yô$R6YSÿ3†t{ã!ì¯c»RÂü+'É|°Ïj­³f€=‘LZå>û)~¹"Ï_잇ë‡ÞG]1?T<ÛÊ»¦àŸ65&ŸÁnÝC»_3Ñî|hÜŠ‡DÕ2ÜÛBHvI*Ö=$¯½š±!Ç5n)áü¤f8½÷=[ò „yÐü/lmBp“×(Lè뇯›•覥:t„{<ìP~ŽÏ|hGú.r…øE¦ðíc ìþS.|÷k“n’#¾“3ñõ{zÌó<®?•"x2$‡=KÒc¿V0¼ü{=lÕ§‘ʰþÅI Rʆ®\)î¶ì.MqÇÝÑòtÁ¦ ð?5œ]0’NÙW‰ ?¡ùùdõOq*óm&$߆žWÙk÷ûð\š ¬<‚âÆåH×y£á 8½º ,÷BáÅ`\“©BÖ}c}õYÎ{P™÷|›Œù!bô¡É%Òá$É·’:x1ôªÊÃÂ(Ž#«ÅájÐàûT¿­V]#³B~°ë£öâ*³]¸6à ×ïy™8ÆËŠEÙÅ›G^LòfŹÉx2ÛžhÁò/±Ð££E‡jVÂ,‰ ¸`$e/u ñâ`/V²L`3vw $»î9O‘*Çíã èeÏÁ±y“ÐO·¶·ÂJHÇ´ñ˘§V¹9·L!ËŒäðb±%Ÿý›âÍ¿O„q×›ðèrq^ÐËÊvUàãägø~óòböç/l`ÃC þG¸‹mØ©. L¥gïþÂÐ-‡a·âBx6ÊÖë qþêÓX2]\çíG›¨›ø }z÷wÁ±G!°'²W.v¤´«þd¡ÝgDÃJŠ/X‚ûTÆ‚Uøg7Ö:ö„’Ñ™‹ñÌÒ ‚­ÉxìÊ?Vº>ŒRM(Ôµâ­ï-d`âV2uÕ'frÚ¦ª,w(¬M[VÃk;J¬¨€Û-Ðà\„ÔQ–OPú õÝ÷‰á›p¬Bã»»ñÜ1y]~-°Å~îAzýkSø&ˆûª–¦ã²ü³8:ÆMPt"Õ¾b{Îsáé>YN+û¡³¡“…­;‚5š¥ðoˆŸÛÒ‘Xnu ŵ°ÝÂQà๗~˜ó5&_` “E©Äû5•³¬Áåy*Ôr•$œmndªå+ø[I HüæÙ~—Ὼ‹çiðõÌ+älÁ¹¿g³¦lS¼¾ºr¾{@TžŒ2ÕÇ}¡p1;&ïµ"–£sÈ¦Ž«à¤“{®â"û1Pû8¾TäñŸ6Ð5ûê˜ôÝïLà%ÅïÄ3c“[ ûM’ßÈ'tñD2©>–ÇQEÑà]…è vnA¿›»@â›×:Àµ6ƒº£A„Üþœ ¿Ío±««ÔiP•'®ÜfAÚ^HÓ¿ù7P®%ŠúݼÇÔEè´—ca°v(ù§Ä-'ÛѶüOÜ ¬ Ð⸴ôG £WtèËÕ-øæG;¾Õš ÚRUl!Æ´.\Åm¥k.JJ¿}À˜ßg1sä}œzÙ>‚y×. ’Ë8.…FŸð¼ö8ø™7Èe"0ÿ·.>¦Ê¿~ýé‹£aõݵÃwÞ#?%ÅÁÐ]†‹‡ƒ­‰ÖÃzë28¶7sõ#*9â°˜ôÐ! …-*v°fÁorR|N1‰‹çL yù•ÚÓ™ŒÏ"°ÊoDߘY0þÀýöPY–úÌO%zKâŒ#Øñð)®š6ŽŒ»~dµb7§SÜÿ“^‹?ä¢Q{(<¼˜…sƒO‘†£ÃiRÔx&Ú‡ÿVWá@P„Íž†ÚꟉ¼Ì%æW?5®á„%f|Íã ôÒé"Ð Šßã^èš|Lv^bŸGŠÐ·Ý¡ä•3äIóæz]˜¤ÿ/ÑQ¥Êп~œ0£i» ²@li;;¹Ç Úš›áÁ–Q¸­X}6«—­BÇWÍÄde|>s Xq;“k“äó¿:c@ß[x;]â–Ž¡Kï¾bÇÄ—‚ö‚4Æ%…Õþª„º»@Jû=:S‚ƒë˜úM=æ·ï6Œñymöoñï5 8ÙìIû>Ä U¨& óšŽ{&÷°-‰º´1í=Žö'ç(p›^cHÖЃÀH¼¦?í¼Ù.8dŸB’’ŸÀÈÄãäzÃN|ã®gsØßiSùã8)ëÎG€õ"þãÍ+âu; ޜւIuâÜpîü4ð™ä<Ö8)V·ðµ¨ÎrÃöl†Ã7-yPž:_«\"xUc³ÃV²K¿AHá3<=û–7Nã‹VkóækßA#æ,׆‘W7C°¬ˆ û+ÒjßÃøAä£`n¼(ݯRˆ+¥‹¨Öåd¶Á"³Ðg¾Ë7>Þ•kÙ»†q ±,'ºÇ‹‘ðÛ:éøÈÊÏlç:ó%“Ñ´¤e3V\[y‡&ðÕO”xðj]~£)%‚3†‚úÔtRñx?É|Ïb_ž"oØ¡‘þ´¤m__…»Ü|ɾ}elè°¥¨°¥çŸ@›œò¶ƒ<\éþÕù!wƆz0e»ÞÐWõ¶aBôvèE9jý-Ü#Gð‚uÖså§” ÕàûyÄfž,M=¤ÍÇ'%A÷¦]’†8?v Zér¼›ÈÈ%Ÿg;¶lƒºœTݬK¿oíußÙ[½ü½t€é[¾Ç¦Üxˆ××CÉ_Á\S_zõE*åèô ‡¡`B‰ˆ\€9/†Ò,Ûi‚CŠÎâ;ñ˜ç¹–h&Ñ„Z×’ÙÂ;ÐÖŠ[²<À¬pœ»nËÄ›+ð–À†Ÿ]ƒ§¯¼±1ó 6ë Û?Íš{¦9:¼M£û›ÒÛÕç ÓÇ’æwfA©úW8žEy´S,î—Ñm,!#”Í~eŽ-ÅAÿù Ù¨Ù?qèÚPbxä!4F/§ …0}’]äU ¥g•Ù¤•Ìp\tÔÊâüÞLX¹í3ÑÍOí­f£Ï: \íVh¸N:cMõfÐæ´ùÎ3ôә߽p>Ê{)»ö:“<’ 'ÃãÕ˜»+~v;ãgÿX<¤æSzt¹¹´97ñØÒ+‡ðó~/àï“d2—çÅa¸ªø$츌ӇZðMš7Ï2¯„7_Tù:=I*W`ÎÏp]çWä9.òÙKx×ÀÌ4b/•EVýh×þ>íyE†NY6±}en³/Áª·N\åbÌÛíÁ'ˆTÁ™"úwþ*.pºkg\g&ªvpôO4¶tXuCžFÆá;TùÚά$$ÕøÎTÆå.¸½ÍÆcf&?lecþô²¥Ö|éGQZj¼Ô½ìƒ[ûN’+ëþ€Î*}Ú—·&ª=ãÂÑ4mìn/Œ+qêܫ˷þùŸ°ö˜ñQiÍÞÜN…sgÐŽ /ä¯:_°Aí†:H¬¾ï "±"Þ‡zWß@¹]3¸œ©•|÷ˆ=`¿©€œU󦩑 žZ‰=«ß3£ÆDl•-Çñ‡#áƒs9¬5¥¯e(‘ÆøðbLr/¸7PƒùÍ;©Sb¶‘ ¤{;|ùu ^WbâAonÙ¼†Ÿ×3!G"²Ágú$ôO&è²(çë†nJ®><¯6+Ä;1øD(öŸŠ‡×¹“€Ú\ò¾çìr)„Õ{bá¬y#.+Ɇ ]j¤`PlÄÙêÙp~ˆ%&Ý‚õÝ…LAº %ßâ’x9ºÊò:<¼\/ôŸcçP)èRT„{÷:À¡ø ~Œ1"'ˆü痆镣H­ÖDo^Ž;/Vá’‰¡­³KøâÉ %ÍÖŸYCæÚ«R¯Ù ôÂ|'ÔK¾ÉbU˜uú+v‰ª³=ÿq›¼hâ¹y*ÀØqj(‘nÁŽlö óºçƒ¤™ôå¿ÀOî9? ÄÑ6l,WG9þáâ6ë‹%¾?¥N#áî·P~÷ékÒ_;š2Œ ýøTÌ8ú‚x)äªfPáùcò£k<_*¨Åôë‹¡¼{/˜u™…¶|‡Ç¿ãìªôæo+§§,yɃõpUµFЭ-:ÛùâGó¨JD6¹fø;ãLi¡å3ðtWç¹±qÍÀ9Úxêjö³éÛÕ0^z4ùté6›ôçÈè„;reH©úÆJ"?³Gí:Â-㮳’éö¼\¯œÐ@ÁM>¤½‹dEÀõŠ =¬¨EEÝ€æ8°ªß†K1Æøêà&ÿá`jäG·l}ˆ:Víà?r ½? KÚ}F÷·AÏÕƒ[âzåp4W£Ç\`¼‰©ÓhÓ9¬7½Ú|iÜ%üŠ¥9l½jσ,@åL‰êºA¾îùMn.‹€¥r1üâvœh?…Ɔ–ËäœÂ[X.^Èšë^O̧w ði=ilàßù#ˆ~ž€‹o΢¯ˆsË/2¬æç–g™óųÀó´{ûù5 HU¥«c"±xÍFxX~^m—¢GïiÒEÊ£Aýa©PÔ3['ÜÅvŸF\÷î' ,»5:P»So.8IR.å–-#Œö`“¸5ÞdßYBñ/´¶Sâ—EíÙ¼•’ôÒ¦"\ÛeÕSOÀÕÜwÝHºi¾!Öo«Æ§Q™`hnHýŠ'³¾†˜u’œŸ†Ó÷f@ÿî; ae@÷MH@ §.¦óôþtÁÇÙþ|uÓ aäßÃìnÞ(+N€£¾ Lì·2}ïà ¼àõ}]r¸RrØn õUéƒ!“hõûB¼p¸®w¿ƒ;£Î ñ¨/_=•¯ö^]C_;ËïÑ…wŽ)°î¡-‰(Dƒ­!¨ß<‚n0}È–‰œFóŽFv?&÷È çm¦)<ÃTŸß÷HfG²9Ì^Ê ·ó°¹@eØp*”©fñ÷î :Ÿd`º•wÚ-Jë¬È®¯ÚìOp Q©«&²Ù·Aé2Õxø ¬bT Ö–¢Ñ¿©4“Ž2~þ‹º`ŽüFü½î„¬÷¦5ë†S—ÏbtÒ&úôiŠðÅå% pªÙùâÓ½,TDˆ»û†óÊ©rAÚË ÔÕM¼ÕsùÆÅŸaæõ<çØŠã·¼ƒcü¨üoþõ”U[âCM—hP'±PÐÖ}‚'5Øã™#°Ýv-º0•¹ÝÖfæøÓ•¾áTå1šZ¾”^-Ž‚w3×ÒÕ-Ùð°jýÉGnˆåwKqíŽ:2[Ìf%]7:uV2ýpÆ?¸=vªãº’o0}P[Ͼ¦ÈÓ†DÕ'àrÃ/¸â¦loñ§¾Â$èj%-&ß±¯ñ?ÿt:n¥ËFëÑOÍ`ëjˆò¨‚ñ`^Q${º€ŸÕDT7/„+ÛÅpåjsú$Ó g-WâwµM!áëúª~­à$Üi$¬,=·>Wà” ßªéR¼vA*mÓ¤“éX¿l3ŒÀËlÎjzôÀR ˆ‡‹3£pææW@7ÞeåS/“™qØôhpë5EÍ8?xr~’`jq´P~P§·†§ –,yÃÆYÑŠÍBðyêŒ×Î×ÅÉ E#~ìà1vZƒA&1…YsÅéçmãéÂ×/гb!¤œÞ ¯¾N¤! %d¿ f•5NÿÒ ² 9ضç&ø¨Q»C©ÑGòN¹”M z4Q0\H>¼¿‚ç ðÞ³ßÝ_«:Ï7œ ŠeoÀýõafüþ0„Ï‹eÞ4’Ä7w๯&07¸ ]µñVtk©íÁ— {`”u{2Q‡ÊéÅÚ6Pø/æWN‚‘E;h×ÊJ ¹ƒó+â tÖ(¼T3”ºùŒæ"j4@,J/ã© "ðy»šnM ‰ÛdÔßx/¯Æ…c=™ü“á\˯•8@fÌ ÅÕ; àY°;L’–E}£¯Î‡|-a‹“}«ù—…m…±‰8«ïn}èƒJÑëØã@EZò7 ¬÷vƒ^¤$¿[#,Ñ-ÜÙ¾ ÍUÍÑæÔ fc÷‹ôAr§yÑ”7‡H«Ï&<Лõ+Î’Ô—e8Jeœ]0ƒþìàS À®êà»j#X~ $ÙÙƒKñô¶§è'ƒß†êcÞuiÈ:#K'GÌ¥a¶)àö+ˆ/ÝFöNŸOV³pñ C¾úý3ViÜ ·CÌ–6¢Ó¸{÷ˆñ²¿3PðTè—󇌤òü}m®7}™Ç$à\®ZCŠ<¯e¿x-ÿ›E‰ ¡# hØšñÞAœ¬ Ù&ü«–JKÿþÄUM‰¸;¥˜Ív”¥Ög4©ÎÕGöu=Æm9E–-uCÅ{˜:ô5[Ò}f‡z†Â_å]p \’N>šÇkgIðªé—Ð?t4„¤w8/¬2‡eÅšTmt*ÿW‚‚­§àéM‘HlYƒŒ(uìÔ.¤fýÏ猦m`Ž€Y-áìˆ œë²òŸF¡¡â{ÜÖ^¥¹l%ˆ5¬Fó!óÁåÚÌ^72Sõè#-e~ɳ«»dyH˜ŒW ϶*ÓiYtò Kr­ ú'òƒÅʘ©cÏóÿ܆Ùí 3g2Œè ŸgEW/ßÍ/ÿ¾ë ÿ ë ³Í5xhÕG—ÃM“9ØFKÁPÑ_¸^p§ª*öOàB÷º½a3›Z Üà'ÖxùP³9ÆÐ¯ÿ™ÌKÔQå3žë‚WÞjZ›5:É,¾ê‡+ + WæÞîÆ§o ;Ê£=½pܨ78æ¬Dz‹ÀMƒ¡ÜçúFÌ9—ˆBË£lð6eQ_Ÿ/ßâ7—܇M‡Rïϯprò6õCm+C7å†áýÌ}‚[çChG”?Q"O·®]NÚFâòUÞüF1ùøÔÊb¬#q§Ÿ R-‚å!ÙÎCK6‹‡8¤â"Üö»H›/÷8‹¼ 㻂Œyß~áuŒ®»ŠžÕ›0{¹$õò½ c8r›µvÄò‡H\<^ æpNì0 ´7f{´Cxç僂_ûùÚ§êdbåà#Âë  þ™;ÿbeǧî#­—O“)eÄ¢ì%ƽ&ôt®>WUÕDž—A-wCñó¿ØÂOŠÜlˆ7©ÝEjØ9Ü=G–{½Gž¤ˆ‡S8ÝŸˆFÉ‹ø™¡F(»2rï|‚…&ÇàEd0ø4®ä[mãÖ5· $æ 8|ó!ŸGŸ ß*ÅùE%.«Dó_ ã! xuV5‡ÇBÝ¿ÑT÷Ö56¶|=¿¶=‹È, Æ 3ZVlÆ'·¾Á§'*ßJRƒ»ºtÄ™x=ü§‡Ðkä©Ì €‘>\âÙ PiMóžîÇÖ^YH\-Á—N€•ðB†”óSÀõ4ͼ¸žk}û|IÐÍ»ð·z?zþììp^Û¯Ã|Œ!/SŒáé‘:A‘o©Ü•õxþVÄ÷uÏ0¦Av ¨ðY/¿¹´Â’Ê—0üÄNâ4ü8|PQÁ¡ õ:í~%;F¸S“tl²Ò¢-Ÿ_Á“eéߢôø¤pÌšý2FGsƒÛ}pmZ =”G>M]OÕÅæbÖs|¢üضaÐ(±"&zó£oÖCìþ «­WÊ1¯ùúü׊É|Ó™xÐÚåÍMôîÂb•‹è矀ã‡qƒáû?Kºó×™ª}npS—á9H¸“Œ²†Z¼Â˜ãì©7ªþóI?éÍ¥ÇÀ¶ïª0þª'~ýîË<ß 9V°æ¼Š $/MfSg[MŒ|dùÓ±mÑã«Jò"÷!òø=¹¼š›7æcÅÖ¨ãŒOöaZî¢ÌÄü&8šD B$Hcs<¼¢CkϿDz¿*\i‹&ýöp8¿ã8êÞ–á‘©ï‰ß¼Ý(²; $uFòñE¸xžïöäôåØ>ÙÞ•¿0CÙ ëªàÜàá0ró>+MøT‡…¢*Ø2ØwFo;#À¿U“=¹„É/ T3AnŸÿ|±- ã(\Ç×à¿úva4Õç7ºÆÐUE†|{ëúöØ324à$»×±>uêáôc7!8á+vêÞc‰jlSèG·Ú‹¤yúRØðSÊ·_Á¶>%ºGs—”Á4.§?jÁgOZ?é14dPÑÅô°÷SkJæfU»ÖÿÀ×Bë1§{‚¤A¢9¿^JN—„öã`lx¬+ðÆ%¢I8ôA þI†™ëc3ºñRBµ76ÀƒÔSUš“Z ¤ö(”™Î'¥&´×›c®^Š](Y+ÉMtù„_ëY^´$þÜŠ•y_P8I„Û}É®üÕž ‹wã%};aå«K8Zù4°±_ëŒí°)gëðZ׌t8Ãs+%^Ý! ,Û`’ÙQººJ–æ†iã±Úå˜#èd3ŸÖÂICt¨žŸ·û©!¤Gž“‡ÃÑiu*Ú}¬*ã‹dlÏÇ—Wï±àüÍxdÂrx%YÁ’ ~@ºM›ù0¦Ér¾qCÇ>ÂMïºÀO_¶›ËуÇ?aèê9|óùz8Sñì®njFc«Üx·ž½Ú¢Ï\“M¸´ì0šìĶÙêþå09¡"õ¿r³˜ñüî¿uxµþ{ª@4s÷ÒþøèÎÇ$ø}Åh2gìj<ýF‹c‚ Ý»0MðnÊ–¢¬OÕT8ÎVl˃8Q¯J7íß­oâèë¡rTõ"o*Q¢]Š•ôdØ:$ ^ç ïÕŒùëåùðZ ÇuÂgCèšeüBÜJ³ùý[b|nÊÞtƒN±ðâªuùt¹#¸´³–hˆ·%¡Œv*mñN}N¿úmð¶F¸þ·äSwÃÏh꣗‰ò«iÒø °Nz†<= —O ö8f~'ÏCŠ<ƒÉô ´>‰b=uyWzžJàqëdfÕ4ÏþÊrÞžÆh}Qz"(v+FheŽ•?ÈŠï³°¨ö ™·f$¿üz½`ü›J0>ɦx¾ _‹0«w]³gLÑQ#U3]ñH³ªp¹Y(Œ“Á6ÿ0öU?v/k€„¸~H¶«ÅƒÆ«amÞ?¢W>ˆëîq¦0ÎŒ§ö4#ß_@6VŠV| §E ‹kS—0GHm™ÁsT°e!:ù1_ñM™_Ý~åÛ*q£‰´ãËQˆø0÷ÿìÙ‡{…A7åù¯»•p̤P³VÔ=,OGýDìÈPÇ3þ”êÌŸS¿Iñy'B¨N^lúËËãˆXw9ö=» çý[`ç}-¶}#®8b ¶0¾0ûÇfxù£ølètY¤ÍTZµ¹¹J![Ü|x¿(—š|bÊ›!tù)|½€½¼+¿_¸‚öÎlBEäêeÁ<÷è­“àžLæ$ZQ8cûJ~d‡œd»ž³óq¿qý^*óÊ=­q¼xÅ;<òz&jDˆð¾ ¥¾œn&á4®O‡oÜÿ¥Ö`Á µ„=“žƒiç*a•ý32Oaõ4Ô‚¬ÖØ2±VýU£QN|Ò'•¹‹tË!æL¦¶ ±ë¾=>1TÕ;!Ôµ‡~]ˆB‰S'!ÍÅ’‡þý z[0 Yr7zÐÜŒ›¨”p_Ln%_މ~¼À§_Å™\º=çÈnÊ%mˆüCžª~‹J j‚ãpãä2Wk*5Úfˆ³«•)¹9Œo[ëAzØH^ÐnÇF”ïáÓcÙèLsžû÷ðiS0¬3p:W‹TŽçUÂÙðY1íççÁü­æÎ¿*ø(•EÜ(ÁUçLeOu`_\2}»Ù³7B“¾Jö¸M‚y5b•’ê\áG6NÑ¤Ž Ci€eYXåL³Nž†BóDÁ¢Á³ì¹Lvé§0£ýªê‹ŽÑÎ/sPYü$j‰ÛÐáÏNÁñJ-ØzÚ“¤<Ý€÷´Ý!-IÈ^}Ž^¶Ò±Õ+¸GÚ_x¦sÊ6ÊpôÛBRO4€t¡Uª‡ø/‘x^/5Š*1…½6¥8ó·%ªFWEÂß#8ûÏ×<ÿNô•ÈÁóÅ×ðS‘"ýýâ;ÍR5æS;ã¹ërWnp'¬>Ýeÿì_óøawðäçÚÿ~ ž2ŒŸ›Çl.£Îõn”iŸFÝ!ká|o<ÙòZ §©¬…;¡tê•q<`4q‘Šgé#ÛØˆà6¦K `´Ù$2Á9à;Ùtý0D×'˜¯÷ØÂ¤œn?ƒ<í åûd7[ß05ËCìi‡½kñDÆÝÄ”?Á­ÛA°9ó:Þ8¿—JŸ‚«6bïR|gÜÖ£íé£<+Tª„RI…dó–u™lFëGNàÏbñèÏ׸[î2~ïË`j£R0îCktúÀÚ’'qoŸ×poûÐÊÞibtávV?Œ’‘ì,î6ÒÃûÊòü£÷o¬fËP¡æ,œëúGŽI-®øºO7¯Dñ­#ɺŠß8:Ë4' 5ŽûSÃ¥vèpý=,n6%ËÝb¶¹„Ïœ4›Æ´OgÍ_Ïb£ókhUÛ j;xNµ* ý¥A¤îpöªÉç­\MçÈN 1Xb}‚_IÝ‚Û d6[t¡$V…·7‰ó¤áuÄ ·CÄç± |v [ÜEü[àUáu¢û¨I¸¢ë8hT8ñ]¥ÝðÍÿ5»èQ‡î'2ùØØõLRÿ ô âìŠlËãpÜü6fÑÄëýf|–­ )òæccÒiuÞ?¨©Z‹ÖëÓ3s†³‚W¦Üdb(9¡uRGå`r€9vݺÏ^-(:©Í‡›Ü01·‡ÙžË¯¶pü‹vJ¶àõö—lÝÞIdòî»XÕ^Œ9ëKYÏû<9f?í÷æ8Þ:3ýE©dÿ*8RóO®Þ+\ùü0ž0ÜŒzqozq~gÂ.þôÊãzL®Ö¯<[fÂwý ……ÎÔ^t¬½¿/ådBZþuöKÕ…9©œZ2fð|–}øÒ¹ËÕoÂ`Nv#D»*g²—êsùjz‚¢‡ð(£ r×ú"[6~ ls6ûõ\F¶¥-? Œû0sÐ[“Ѥý°PN‘il‹ÅãíÀpõSlБæg›^¢ðãH<Ír¦'ðrËñ—7] UÇSWs• 8â{1”,ë™kÌÐ[áÅà x|ÜX~ì×XòÇ|<] ¯A+lÜ|m…±¤Qw'ú=k”ðä ešñK 6?Ñ«‘\ãÄÌ.ÈÆQ[ñÒÐÓd·8urçS…Çø`œºž €Ï+uxQêA\¢}šžêõäïÝ“’˜T´©ª$ŸôNAÛ¹9\ÿû˜µ& Í[.’ÔYû¨ØµpJN•Ü^~H‡:¿xi7ÛMFP›Î;¢èÎÝ{1YK zEe>Ù|“èSÖn–Œ3gß%k¦Ìâ“L#ÁúÓ>*Z†Éûrج¸(.fp 3õ~‚ÏŸé$äœùwŽ,u•æ)×®ƒdk(÷ ~GÂÇæÀ®Ó:têŒíÔE$J·ž·%¸`¡) êÇScB»ÄQÞ;ªÿÉÅ¢ëUª¸Á•F.6♞—pΧv±(ž6š‹ùwÀ¡¥`ºEn.2ìÓ²tìÒí´rš&ï¯Ëã¯âµÝ6EÕBÆj+Z¼õ}^fÊWØ¥‰-©ôâÔdÜ›|œÆx¥ï'ÜDÏmÿi ª“"NšÞJo>!ÖmÕxÃv[uT"tßÇavéØww ,ÒEW ÚÐaö}ÌèA‰L|øŽÇ‹L¡;X‹f$H‚ÿ¹Çè”ôl7†ñúmÚ÷ÆòÞÝÀSjAÔRƒ”øîÅÚ¬j’Tø…kÀ£Ïª¡¤ù™ß>ƒJ.!»š#áÐHQ­£F´hk$€«!b-‚DëJ©þÁ6<À—=p¢¾òh×\„㯶“å"ã°$ÿ 3™~?¥xÓê *‚•°&¯Þh­áPGÎǾå®ðî½&¿tïëYÀeç.¢¥_ĨÑß;=—$‡S>Lv óQf˜«‚ã- n­OàÓcî-–XíC¸ü‹hæ`ËåδA¾J/ËÕ£ËaŸãa´?ËRõXÚc ~tí0» ê¯áÎ>máí¦Ãh“\sóÊ1èº)ªßiE£ïk@øH—~PLv:nñ ÂÊ ÅL“kŒ/úÆ—>¹ëト¹"Ùž:XÑ&Æ×Þ›ø÷íb¶îïzU f©’AâéšSVH²¶ÃúÇ;é³ÅT=ј¾‘ŽVâôLùUf¿p „~{6;v¡¥¡ Ýá¦ÂSV‹Q‹cí`¶¸ $ê"'§g:oqä7Wõ©Y¤/}vë2¶L¾ë¦Ù@Ç4:¹ø& IÀªQ'ΆŸ«‹¦ŸfÄúÚ4êyö· uØ%FDSŽCpÄȪš#f4ɳçZÝTTÈƼO$f3/à¨íÕ pc(l^» vVr¹à“TØí€©“cˆÐWœŸ•Çóo‰Š™ÈôøaÌÒÇh°¼í²6óãj¦ô—ÁaXN§Á“DW\ôºX°·1 ¼²Ùĵž¤oœ•u‚7Ó©­G, œ¡GWy_ƒÏVÓyÁÏŸ¤Í¤›Œ[°Y?Š<ªûÁìKÙûâ4zîgи\AÂŽ­Àž )(ÃжчI€§Ÿ(ýÐ,ÎÜ/ÇSß³¨©V´`v T˜læßŒwuð£w¶…àš]Ê8YQ^ÈZ‡žKðÊ€_®Ó5$hÔ“Bh;Ö.ȇ=sî@·Œ.55€—¦³éšÍ`ŵb R&K‹™Ô;v3I µ¿ÈÓCûqB|6›yì®òw¤'®dc²‡ ‰ÜpìžS osÊþó GÍ{‰j‚½ð[MGäÉðƒ®±ãk~»*Æ·ûˆµ#ŸÀÕW‰@Æšz=”À»†r­ÍÆ`§v€UÈÝÁ¸càÝñÜc$qÉÖ ¦S´¬Nh°Tƒ\™· ®º¹ `±¨ muó{Ý8ªi7*XnňqÑ0µn6}¹S{å·¢²¥5ÕùǾC´d¶-ºŸ TÇÕ]ûñlD)H¿¥ó Ïs=Ns@š²¯7³‘[=ãL寬¤?ÎI‘«ã è’)ÛpÔ©­4¸ww×nŠÃ¸TÏ,¨T€e‘& Wõ û±þÖfqk þ[õ’}ü] /ï0ÊÉüh`'¾}Ó«õõàç˜ãd@o1HŒ„Ûí‹˜Ö­ãØ¹óæ _ç®sƒšh1·¼ ‚»‚~ vÜÞ zªAóUVŒ±1ÅL%=G? Æ:ÇàuìpºJû²7]ßë¼C¤·VDàÒˆ(È26@다¬Ò`õ¤£¸'*šäõ$?§´““UYý7nL’/›¡ÎΣyÈDvë·­yÜ BÜÏf’iÙB@Ó~òÞü®~v¾Hö`áŠÛln¡î°[«zCBZ3$‹|Ã…«Èøv¨¼«ôÉàñ (Tœ¸¯/òƒ5 í¬…=¥àw¶–`Ÿ}ì¡tG ¦lPä¡MF<4X—É*Ãæï§`Ϻ«¤üŒ 5´‚f*‚®©¸vc;©š+O{ŇR‡¹gàç½TÉòF×éÕî5جõž„xc—˜HùØ#±°oÿ=P8dJ Ôž‘£zXÓ\‚Ï;óѨö*tܶµ~˜äuÕZ°~L<¨Hãog}@¯*,º­ÎÓƒx°ÎØíÇõ#ãùâIÔýÕMáô:v#4”ôv9ƒqB,úÏI„qž'„fÍ«¨Ùþ¹O] ¹Ñ¸ò;ÐÖÄ-­~™…<øLÆž›ÊÏÙ}&ûQŠTÏûDTD¬G س{gaû7q®î?Ï>“¤ÆKjÙ‘¸\’ÐJ¤³ôù+¥œðVž›œ8‚ã»ËIoãÜ}S“[)?!¢+ÔyèÙÇðhQ5d®¯†U òd}‚³ž@ÞVŸÇƒ#ìdK¦/¸/Üæ9e¾h}¿„¿Ë¦ŸrÁ³¯â娩$øü~œ·Øü­V9 E?nšÿ¹ªNn>ØŠCæ.`;®äÖ\ba‡`è4ežZµLTcIîË%l¶2XôŸâ+'ô[Ú,æXŒhzdzq²¨§t›xÄÌ€Õ•¡LBs&qXŽæ/Ç\cêë›Jæu±ÃW—OÜíäÃ/Ä¿CÇ·ø'½€l,«€0}YZïÔÃþ«YÃiZÕv×÷,Õ2SêûX}OÔwÙMÜ||9õÛ_È"c%¸_øO¬ï=ŒÃþăÞ]:ü»Œ@IY©ÄJ½ÃyZæ-æ+­/œóà k/‰Í“¢ÍùÇ•N¼-ª :›ßͲç\®Œ9KöŽŒÂ[Ú\ðX¦”M¤Þ#6cÈá^6ÔôIz»ƒl³VâO—ª |αµïaß¿êe;–—Z:ГFbËÚûdœe1z(Øóó!*üÓñrLO CÇ yÚÑý l)Bˆ"ágóÙ-ûaü¡÷ŒA¼Ô‡õoÈÄU¡ðs¦9Íîrã]7N1÷Žª#÷ÏñËá?©Ï$ÊÆÆ¡kÏŸ‚ƒ»4gòZ2`‡q†â´GÒîý:ÈC»ƒàÙã °Ø<qJ¢tô¡ó?w'–lY¹Ìt&~.z†¯¬Ì¨â»XÛfÖï@¿àܽ£ƒ×ŸLñ?²´sZÆöÝqhùø!Þn•ŸÌ?6.º†&ÇKPëú \ëËrŒànõbtÝê|ü¾¡ýsw€Ï°H6 -ÇÑcÑf`bZŠßv“•ÛDiç³v–øt8Þ¹¸C(Ï8¹ãÖEÂý¯Á¿9˜Ò–@Õ ÈÜÐ ìRíÿé$÷œÌº2¨1Žt»tq^‘¡‰Ã-O£¬ÂR—RC{™°l^K²U$J½Ñðôº(Mî'Üäùõ‡ì¡a.·á|oΨäÈaØ ?a Ü{&ÄB®~(ùc¯Ãºe¼.~úy°¹5äÏwc¾àÏ/è´ígs2ñׇ‹Bµ\u,n_Kf®¦;õ–M[:k•O¦º Ü þì\)Ì}$±àÏ^ºœ-ÖÉE/÷H˜vÎÃëПëÎÀ„œÏ¨1u7ô.R¬ó­`¢•ÿòp?Zþ©a¿Â!¼ñ:_?㺇Á0Öñj Ÿ*e™·–CôsqjašCœÛêÁß7–\ûˆ5 ¥Ç•ÛaaùCTŒY‰3¾Iñó¶Êøê[:&•'‘\·ƒìºÑQö-J ¿‘œc¿Á¾q©u?qÇûÁÙÖ¿'J§E¾%·;ËÝ|ˆ" qzûpÚ?‰ìGåC .”`€V9¦øv 1¶- ÉZ ~3J™æ¤Æ §Ì4%/ι@µ«#d4}ÜœãÂÉÈ"´Ú!EÏU¼$ÔðÆìæU q7šÕU>@—Ê@\7ß®8eŽ`å—hPu]1¨…ÿ¢ðÁ8´?¼æÖnóf¨º×‚1ó’q‹ˆ1Ù?ˆ+cíøÅS¾iø{ðù@+ŒöVF¯½Z.W•Oб܋Ä|]ˆ@óä5ÜÛ±—yžÃ…#xõ´“Lwo$ì2aFßðû¿1¬ü¼5’zbÞzÓéÚa±7 g-LÁYí³Y̦ œ¯]áFðÄB]”ÇnÞ~ï¥ijùz6W#äÜEøªmž×š‚{.Úðƒ;æ B‘%¡çKðì< ¾$N —È;òOª‹1ÄìJ•ÜcÜ&=Ø‹$FSZ,dn÷bÓ˽°l›$SØ­‡Îó”á•ã+¶uÊ&œêÕCLî C£ã«.m(÷: g#Ü`³Î:Œ O0s~5üç5þõ¥Ÿ]‘£ì­gÇKàÚÒôîn"Y\‚)þÝs'×3ƒ@5þuNfírĈoí,~õT~zÚZÒßTÅF:+ÃÈ•M„'eeÒ2¼q¼ Üñ¬b'ùñÐ×àY·™,­Ã3?£ˆ¦X V¢Yc7“á ª8âA3&uß]íëÿÍ¢ÓÉð¨j&Ük€ëÌž±;#çÁ±Dø=ÄN’F¶YS“{ž˜*ôÒV‡´åúpµé6y°ë¯õGUòÕú£&·ÉcuK:OÁÏÈçb`:‡9Ý® Ÿ·w=~»ÿ0_E}.Ý:}z¡øiòç l™ú”ý7ÿyç¼lðK¥Ÿâ˜Ú2€±·æ"èm²ä_m®y¥xp­+T÷+Ñ¡†³Ø°‡gñ­ªjUÉÅòz‘6Ùw÷Ù{Q{Û3mÕ(ÈŽ9^æ¶ì¥Ÿ÷ÉžK½ñ’YÆmÊÙ¢Á»›Àâ=îë0’:©€Îhœ¿‡/ü÷ò=¦ÀÝú8ªv ÅŒuðÒÓ6ÖBdèámzà» ¯x¿2#¦ñ²5ŽüÒ¾ ìkÏ 7Ïa 4ù‡›é ®*ÎÒ·Óø—Qpl²}Þé?Ï0ˆõZØA÷˜ËÑÅ ‡™¥ÝQmÔãM«Nœ"rO3àë®ËÜàãvzˆÈ¿kfç ælW¬ÛS/Ü~n)òszMçØÎ´ž<û·!uò8D|¦šŸ4ñ³F »ª³7¾:ÍÕ d­í"(¸û¬98„;yðƒí‹¡J§ Ùª azþ}Wå¹ò|fŸŸ@D1 VЫ¢[Èà¨sÞ\:ùa+‘œâË?œI„]Ê`*û "r×Àõç PhGg.˽Ø$D#!¤–yCµæEß9îÌħ«Q²BÚtepáµÅ,0è>¨Þkj®($½‡·`‰ÜàQ0¦øT=ìg¶Âk«¤‰ÿ° PXìb “¥êSl®f‡ÄÀÝϱA ýn²Üc÷aL·É]?&ÁЭÎôÌŽ+3Ñ$à-ŽˆÏ&Œ8ˆU®w-±Ëãá­–$ù“ZzìøÛ­y$x'Ä&Ú€VG#4o¯`Uõƒ\n}(®À7ú±DÉk Œüª‰ÛöãaëUoço"•Û_âžý7Pßî"©=4޾ÄH5s8òZˆ{;Õ±tc:,› ·Û‰Ñâ<·V—ë-Tâú·îàÐÀÐw]^øØz õRû„Ý¿0Åý ·t æÌï&óÜ»¶psÅ´ûG„;å1­c z¾Úé|}›"=,2Žû»Ñ¦l/ÂÞ£ì ÿÖ³ØäS©½tœÀ‡¥ƒ9Òà:ï0êýIw/š‹zäU^¡}š•NÏÕ ŸñÓq[Òâ² ü¤Ïà˜×ŽÊ=‡ÀTËÇX?åæGü"áJc¨R›=|a4 dltîµ>ï‡Ï¥Ûu²upÞå™\:==7§ >á,æäÒ úã“PöÎmôy†óf‰Ðe˜Î¼F6þ’._o]Ïž´à‡‰ìç¾.XþÓu“ø"M€ã0dÝ úò$¶ŽÑå#,‡]s¬`Ú”¯8:ðŽ­8@νYL7Å‚6¸çÙ-hœ¼îÐæ÷×§Ú%/ ßn(‘sÃàÏZäfÉQî|‘ÐæçZZÐ|˜wýô¢ݕ䅦_ûo/vœª'èän¥˜:팓”åŠ+ ðöysSŽªòþ£DƒËç… l¶£[M8*À‰¯zšJö³¨[ËE²f´ ¬:‹.›¥èîª/Âãšàšs|w­†3'Ú¹¡ü|6\ÌuÖã+žM§Êü6¼Õ”£i~¹Å½éÜ'ÄŸŠœ<(ÉJälN 9ÛØ‰û$3pu /çcŸé'²7A––w¤_Ó»æÓ é7È"6'Xt‚ÃøTzæÙM¨ù gn­â^¦CyÜT+žu!Žíúª g°o#;aѤÉXiä76©ðáK¹Èjnºø8;ÕfN7LTã¨þ¶}þ´é%oº¯`Ó×Ã< @;:èÜ¡K„ÊJq”Z‘8n“—¦õQv§7ow1}“½'6¶M¦ä—¯šA²–Äç}á(ÕÂ$*öÒ‚¸I ÚkÎ¥µæãáÛØ=ßét|vVÕÀÐTÜbºƒ’g<ù‹º,«„Ýc&óéþ?±Ã%‹û”Šè!ö»¡ïü ásgóëÓóÌü­ÜBv96¥Œ á¿;qEÉ= ±îß7£3Ê`GT³½¶–DeÌšœAŸ«þƒŸîøøÆ%¼àN›N7ýðÆ“ÄéCC)Úú¶Ÿ´~*Âï ÷QDq,OK÷sVØåÕA;0µîÈjÈð‘©òÔñe‡ã9ܺ‹ŽlÂK5 ÀÙ£<,æÂðÇ¥Âå/_âó³a¨s¿šFŽÇíæ/S&“Q,Ÿ­²‚ñO€ÎÕP½ Ýx"ô¼[Š—ääaRßr"v4öj'“Ì^;L9èŽK”>ÁD‹£ÐQQM®Ÿ¬…3Ù³¡HþÉèfç÷ÅàæÎi8ó‘..x/ÎŽ-îÅKRnÌÐ5U0rà&Ñ8ɲbàÒ­Y\eÁBL͈…¥{Äù …×D:õyUJT'˜ƒÖ²QtÀJmž¯L…¡v3ÈôÖr(Í…3mÅpþ_+ëY‡Zov3§Ü XX¬Î·Ý<€ö¿Ó‰¹é(ÚÑB÷턳ÏCL‡=üÚ´ 6¦¿F‰ð|šþ=ë_¢×º4åÛŒ+®Õã ÌÃ'êjèæz•¥¾zˆ¯Õ½Éåq LòÚWh^%‰ŸßªÓŒ…’¸v¸¾ÀÍ ™èŒæÓ‚Ã󜳨ØÍ$ïOOaP:fÙ­—¥þÞ¸âñxª99“´[¨£œÅTxý9ŽUMXŠFõw0oý aÑ?¶…!ËÍ‘ÅmݺUó7žÃµis¹ÇÌmXbxrôtÖ¯81d „ާväsV~N¾0 ò㡌D±£&D?\ŠuÉÑ×íå ’Ëoôœ‡@ÇíÌØßÅC o½"–¸˜“­û¦ñzÏ ð¨œâ¬ß;ÈŽ£ß±ây+|öˆõMUÃ!OÖbJÁ9Á²¯Ðnæ)¼|SH gð ª\×t=¼ö Wöˆ^a¼o+ѺÖEÆ #ë»à˜²|„úo\û…öv¥'wõ ËÞ€% hÑK¾ãÊz˜Qû•è¾F¯†Á“.Vü'ëÀ•†™ Gžžr®žÔjT#½/aÔ÷ƒðáï8»_oòÙ_ZìȇЛ.¯c'Aâ ~9$óµ.ƒ}ÆmàÚ£êX\ˆ*5ô ¾ òÊ÷òö´×xNô3Þy¤Ï;Ö}$²ŸªáØz[ܹ7œÍºs,ÒpŨLÚ™îùöèðíAUöNEzþA(+=lêÉİ ¸yo/¤¶îŸö2Ÿ6 ¢g0á—-p‚-•08IŸ5]ÀWóV‘ѬuÑd~RÌ¿ïÝÖÇ‘èmGItÿMËS„ž)Ká„ÇRaÏ;e¼ív•½Ÿ‘ã}Ò'^<.O›.ßG¡ïj”ã•ÄÉâÎxÃf%w(ŸEÝ«2““i×\I®ÒÅï%hö’öªO÷Rh÷‚±4¦¦hÅR“ÅJ’†ã†kµ¸¥î2~MwƒlÅÍk/£:gÓ][vA†Ðÿæ2ŸÊãîç>ÕáñpmZ?Ø ¯Ã¢ÇJ|ç¼â¤dÇ3­ó¹Üo)^3[Ž>Åkæ$ƒºú øYdÇÿ-BÒ±K@Ǭü|’¤G,¢zoÃqEÎ[”û»•ï_!Æ×)õaÆhY&=øÛ™ÖÌŽðhzÿ€;ýîÅ'Ì%ç»òpãRÁߪz4=6’hAC«Á€Ã4x<žx÷Ä$p‹$3aˆoµú«²‹µù–)ÉD\5Öì("Çvâ_Ñžÿf.û'pþÐ$ÂN¯Çê†TX×èþz¡b©™ðŸ›E‡Ñ å[ƒbbTÒÑco.D/[Ü2`6]š´·x6Íô  >]؈*椆սŸ/¼ òXú„µpa¬\ÚèŽCãLðé²½‚sù‡±ªç»T‰Ð¶Esáëó—à:Èצó É‚¤/bTVï8)(㜱Äl± ]ù|.ýgCØáOtÿhÔØxNìÞ$8§vÕî퀩Úÿ@oÝì>’$¶ƒK²òÕ…Ë*È™±ÕhØö/öG{xÐZ½Ïœ†¦—0Ft:x½½‚çLÙõ) DCÖ¿Iƒ/9–üüç°mŸu‹T SNí„bW'XÙî /W&Ð>‰‡Øé$ÅÇŠ§ÂHÕxý/J,eiE©5Ýrn4·~bÀçl} ¿S„‚ëàzm#-ÒrÅûžÏ``Ñ/°ë‰ÏTÄxœU!¬9 WÔ™fÁ4¨»wÙyϼZf;Iöÿ2@• î.³F8Šrû¤õ°Äö>­ˆbJ(C•ÌÍIç•øÃz\½í yjýÿ›7}Ë/•Ýò›‰î |“P8y­4ðÅ3ž@Ò]¾*\sY(=:URKQ5j/šp”-] ƒýù±¤|Êmæ$“ž¯ˆQ#áëBÕi×ÓèWt£jŽƒ„”?ýË€*­Ã¿{Z‰›q"´Ôù±|¼‡™«ì™²‡¬sg¤÷îzKV)Gââ;QÌ`¨67 ’†˜¦ÜÏa sâ/žÏoƒr÷¡çâsÇgD¤h?»¿öîÂ%ÝTùž À= ®$€!ÑLÍR„¯½`O5»Âùë%–òÜÿêyn¥h·]Àû‡E€ÿº­¸ž|þý `øo¾æ¼Ý¢ÿÿL‡qZ ùEI¸1…o#ãé2½ pTí8Lœ¹µžîýÕêrÔÞâ!Iej4M3šŠ9t°åYü÷ļ$1”;F¯F™¿pû§5´ñö!xÉÕTD}H—vyN]¦*Ò½9%¸fëR¾iRÈèˆñœÊÌ¥p6 0’ïwå·ò/âyéÆ óì.Ž+Q1Ÿ0þqò«OR£ésÀÁ\“ÆÎ¹„Á£QðÝOM ˜ºKœ—ï«¥äµ4¦¡yVÚç:ó•žø‘Jg:ã¤2Ì é`“ `N„4öüÕ¥&„Ü 5Ý÷ÐïK(yƱM—"øºC?;ã¸G½::&Ÿ×š€%ý–tìÙ¥Tt‘3=ñe6‡Žø¹¤´þnCÕ‘rt"ÿ“³Uùèðâùù'ã§?Ç«vJT6* ÔemïCæ]1zÄU’/[pù[Ȱ®\ïù.²Ûÿu;ò~¯|øôT›/sÓǹÒÒz<ýòxîÞ¬A5§qï mbR7ˆK†¤aâ_xçèK²Å@'öÊ«ãï·;éŽ2°;ÄšG~?Äz+ÑÂòT¶pÛzl·Gèì›Ó.NൕàÍÍ¡ty¿5}&KUßXQËäjlß¿¾ â c˜ ëÞi ýïÊÐUÿúCmåÿï‘~Ù-àÿù™{›û0_bwØ›c7Ãû7pÖ\ƒþ;Ô:ñ˜ážË½áÜ­V 4ÆÞQáíÁäσŸ§²ÿ[ÇGìþ ¹³{áÃ#]nŒïý·Ž²Î~åñ+Øñ§ n‘áÞkacä.çÒµªtxo>:ZÛË›%¹¸—ßWãÂGZ‡ Ÿ—y\êIØ”°vÜõAñÂxé‘Tª"†o®XCn–ÝbªoU©õ& ú¸ú/9'N;:vÒ‰úØÎMâß~.…QkY½ÿZàˆ_˜`ÁswæÐ­Â¿(³õ2t]3 ó=-pbp,°}qb~”ÄÝœû_e7€oþ(>ÚJ—i¬¼‚ââCèüQÛàzÌòü÷U«|„º–s‰ö´Úõ)V׌«ÏYƒE Än/ÁÔ¢ ¬è†æJ“xΘwö8øI/'rb÷¡ãÂq^ð3 [sÊ&n÷O•‹‡óƒÏŸàBãÕüú Üðn"jÏpGw“ïÂoÖ3aQÆwÌu4ݳntPœòâ}MøõƒÜ¿«X‘ÕI:u„¦_«„*-~«s ÿó~2NÐyNtöæEüò·+èTßÍ w/å»rÊavµ2ïÔ­ÅW[•ù‰u´þy'~Û„û,%àÄ“ÑÔnC>~z÷ŠŒ×™ÅŸ¿ƒ^äŽá5æqÝ„š7 èù* ßÂnjžâí³ÓèöÏ©ÔõJš@ÙÁYøU1Œ—Lpá÷«¶Cö¯øMó,LÛ]&Ä`H|(\Óçý¢¼nÍ!î¦A]¿=†[yÓâ?Ðþ¬UèçŠz*øaˆ4’pf’ZºôÛ_~xOTïñµf\D©¬4?£Ovü½•=¹|vlKæ“ì¼è¡‘‡x‚îóªµ^XȦ:R{Ú…¾v  cgÊòSaal~þ#púYHöo…åi ü¯†æ ï‰Cì-ÂØÌô}róÏñ&w{¨Ÿõ ó-hAG>Ê7Mã åù?`YO&;_È7¿}/>:ó3PoÙ5TÿÊ÷ÜX†—e ùÇl[î®x f¥«TmŽXÌFšRݵ9pöû*lXVÃöä£RÑ ¾.ˆž6‹‡¢F¼)?–ùÏåѧˆÇOO’4>ˆ¦Ï;Gü½FÓÚê~œf] 74yàu”ÚP—ŸÅÓdUk<—E¾oâQ÷`ÝæAmß4ž^ ø‚—ÎòÍÒÐWùê§]†ónêèƒô>%~:óv‡©ôùs¨/ëbö7Ÿ 0B‹šäyðìÌÕ(¹J–ö<œÇ¢ìió¯fØötm"EÅö`tæZv§Ãv?6£ÅûûYy#[³à*Îö½MšJNãÑçá4yO(÷9]‹W[¯³7–à¤÷’t›ó¨u±ä)£èÖÂpøöö1îÔÕ¢#¥ò;kK!¼ðìi[Áé7¡±úV4ž!Ã* ùÌy èá]Í8"C„VoI{T™¬r3©œîŒ[ ìñÓ¹Pmù ‹¯|e‘\Ü4Ëν¬†<»Cä‘÷MÜõËÚZpµyž\ýÝ=£Øk)-BÂÑ÷#áõóXÖ7aÞï@&Ú=”NýD„Ó–OE8݈iy©°5Öš­‘þƒï‡?¬-𙩂;ëÔØ‹Üö(KNAã°cLx=‚@J\¾ãMlÜ.`çégÎæ «YÚîËŒ¨^ƒO‹WÀ¥—»`d¶ö g÷”Âg‡¼XK±eW ºŽÚ7*'Ñk·[YÐÖ'È¢ ¯=¤pw¼:oK|ê|êøe\³n71X½|Æþa¶/1d ÄN§ãJ&Æ{ö¼¢O³·—ñ}züœ‰5½±)—)ÈŸŸ½xx¤õ] ;Ö•þ½˜Ä'ôåâôéë™ö¼‹¤E—Ò{:‡A_[šª¾¯a^kF€Ä·_pÑä·¬ækïYëŠÅ0d›>=ðïúèƒ~½>uÉQà·U¢ïد¬çQ=8î“¥£*á€í2ÓȈZðÆ`[Z{… cUq׋1ðð‡%ßU—ªÉ7Hªh[m$µÆ¡Ú¶46ìD$ö²OW¾¡cÄ^(•;@óª}Pá}›úR‰ÿÇ_ú:Ó²ÆtÑéÌ^âq£nÃå!{ѧ­SìÕè>ëáxiÆF:±éÈwŸÂˆgÓ!Ál<ÝlM÷”I‘¢£o˜Òè»ð]ué ­H¤†§ìæõǸÿëFêܲ”¯¼)Gýõéô<=þ¯º ƒŸÖÃq‰ÃÜë‡u>º¦Ùò(Ck*\åHý ¹É‡ Pµ·Œ(/ïDó(m*5XwXØT:.Vš×õ‡†xhçv¨Ÿ¡GH.w›w uV…fÌëDK‰TÔ°ï¦æEÝxf·Ïz1š¼ßœÃÅÓJp§ñu –Q¸ôY2þ™´f·÷àlM+Þàm½=ˆëWå`ïÆQœ¤£EK8Ýø=Š*ü¨ÃQŸöáY§U„Z&ÙKž‚}PùHâöC¬… ”hçBù&{üâ±–8gƒáR…²&×ÕŠÒéÃÜYSyOnէ¸Ý§l²Bw?sÉ .³¶a•‹ÿ{SOpoá&¾Ncm¤:¾2(áÝñ»øó´‹¤»BžÎ|Þ‚3¶o Z5Xvl87 •¡_þ$CáÉ1¤ÂEŸÖÆbã‹ùÑMEüIélú }5hræ3ìóE1Æl-ÿ.›Á·žYGô¬©—^cºf|mýo˜+cÃ¥_gosgRW{ú.U‘þ>‰Æ†ÿ˜ÀÅÃb-9m6'fgRœ§­ã¨Ò–ƒù~í »y,wãBõèqügãò­C.ܽ»˜)ÀœÒÄ9ß {ŽØÑÎ|ÞšØð™ Ù¦+cP+JŸNújE×O ›8s&&öK´âÝÝ+høäpÁ÷&H‚ÐQ õf¢ŽwÏ¢ôqG*ÿ€œóxǬv²úÚ´éG¦ˆ†ñC6ÊP¦‰?<Á'¥ãè„íRxðêN\·O…º+q+?iZï0¶¾ƒ•Ïà÷,`«U¨ÈÏxÏVÄùÕsèµþ|œ¹Û töŒ­€t¨l8Ë FMÑêõ64éÍXÒõ5£Ö» ÕF=š!Uέ ñŽÚ=S€s÷)½|ë+”Jþ¥»ÖÀ’wèÁëa°öѺOQ0^<·&Ø‘é3q®ó_|·-š]Ødƒ¨>–(…ã÷º|â.Á“Þœcoæ¼#B«a„Ô 4kσF©Ñ¹ W`ìù¯h¯þöÎi‡U.ÉüÓoI>}Ë&2Éj=Ä,KÔ±Žz€Ù¦OPÜ•ŸJ áÅ“h¸“¢iAC©ÿws4w‹‡M2~$q™õÚByÕÜ%´ú»m Ø Âe;™Ça/ZeÖ ¢9ȈºpÝ|=Ûv6Ì<¦ÅûLhØð5˜¤‰Š5ŸàŒ»!L©nŒ?d h S…=Áõh¨}„”^áS—*ÁçÃút‰œ îKP‡må)¬³è3ίú&ýÁ§£Ì‹6Ö6BŒ¶Ö³×üø÷ŠY(â±·êlÇ1ö°¦I䞇Í×ÆÒ£ÝgðÁýq mú]ø/ÂW$Š ÚðóõË!Ö€~Aª4UÃT,Ëápü}´ÐKìÍS㥅8ÅëM?É\e¦ñ5c<ø£Áž3)ã•ó˜;áÁ)”ß³„–½0Óþ9·ë-¤|O+ÎnƒËêO*·ZHR¹š¸: ]XLu%÷ó`O~kQISãfJg·ÚÏ<ž$“ÞWÚpó3DÔ+óžÞ n3o ”ùodz‹>à¡j[þ>r¶0f-ŽŽ¥á:sqܘ– Ögqø¨Gð`Z)®jöå[ú“B™Vö}ØP®`>"ÌǼsQ;KÚ,E÷³UbmŒ¹A²/£îñz¸¯²^È»3†Ðï5Y¤+Âç• àjÍÍhþq%|]t¸ï< ~!B·°A`÷<°yÔ-|•P€oe¶±Ëy«xÓí{`áñ2xfk²Óa¦Z5+mÝŽÚŸïy$MÜ·ä‚ý®'ì]uTT_¥»SDZ A‘yçŠ"**ˆ] ŠÒ¡’’’Ò"H¨¨sÏÁEÀD%,Åîúø}¼µÞÌܹç½{Î;{ï5{Ý8Î>É®‹™by&×{M '~DËåL]e˜}íSšÃ–ÚêÂiÕ NÊÆž¦Ê…+ï`âŠ<ðíˆ#jBŠ˜þïÌv)©¡d†ËdN`¤”ûÏwý=&Œ~8$Ì|îÏÄ%®äî´“¨¥²3ÅÖ ½BwçQ&œ8lä·¿ç$Ò'CÌßQ½¾Q„·d •R¨ñz:Ï+…ûÖ=œ7+±äKŽXðó º<Óc]í³±hâΊP ÞÓÖÃtÕ;ܪ y ̘X=‡Í×ùÉK׃¢(øí†rÆìoÐQHKVe¥7i„,³n–e/¢RÊ™ø/E’%½€Ó6¤?DôŒƒääPò}ïFæß\Œmr*€‰¨±8< §A ¦“CÙá»à&ü‘ÃÜÂ*®-¬„·úòÑŒN¶>D&‡ƒ 7X(ÒŒKžœÀ& xPÄ ŸP ³xò…<1U|JWJî2^«=™_Þ ±+ÁrAü¹# ‚-Ó£ÒS‰U™ùoßg ©þÜÓM\…±Ñ(©ätŸÄ3=8 éäÉ5à /¹Ï³0îY<*5kÃø¡:˜å! †c’é¯;Ùàw©_&ÒÃM9F6ż,ïž]]'œ§Ç.~IEˆ»‡\–€'xŽp®­Þn ¸ùóZ6S4gT#$Ó„ï'h¯¹Yõ ¬þVãÖ¤V”ÿ´öVüÁñ± ¼, 7%þ„oÚK9·»ëèÎáx\÷‰ºÌ×׃÷Z³-òáŠkqÞd¦y•ÆíƒZ:0˜Ñµ]¦(×Ê•ç³v«¹©‡•0Ô×’t?:Æ„âÉ›ã× ¢5‘ѪÀ©ËÖBþ¥ó¼i—:`mµ.,éÃùÅn,Cñ_¥ÌƒkJ8©Å—`Ñ-4´Ú Ë3²Á{zý#-Ebç >LOOƒÁÜEðQ)¥Ïë±KÇÖ³Ö/d )Ý®ÊVp£ÂK;š“Žá™Pb½ŠfÂü7 ð—`ô&ÒÙ—R’áïVR)'Ö–\˜Od¦¢ÝñRt>Tqø~Ī'k°—wèi_)~­÷ þ½‹Æl]"fëÖàêJf!%JÆ%ëQÿØ0—ïSôaßã(Ù»UiÊ0_±ÏH`Êþgê< ÕíïÓSJÈsØ>Љ3^qçOç¤jæáéJäœe-Ò;õØ2æ7çù Hé’‚-´í™@lÍÛé½Ð=Ðãµ–”X+ƒÍG!Vp×8|æ‚‚ìúgJ\ùE5ŒœáòÞñÄ,Ö.î¥>~Ç€•Ÿp»Ž'Þ| »fàk&ôÉœµ]Ù,åXˆ™ÑÅåî=UOŠxyÛ÷AÛ#}ùe>9”n.WÅÏ1¾÷!˜ßx¦Äâþf{npͨÆÿÌýç×Þ³«+~OAõ"KVèO‡ö¡½Íq8uÙÿÙí¯Ï€Õi;L+mä–žWCS½~¯¹Yíû \†f°Us¹w»õqÜ#Xí¥Î¦ D¡“—õKz\3¹ãoRw¯h̃lš6¥–¶/_AA+QMö$„O®„=¹½h²²’ÆÅ¿ÁwU7¸’wá° u1̯FJI(yJäaTÞD•”b¸|ð×;í2w˜;ŠG‹¦‘’qsqPèý!9ÖÿI€,rh¡ÎtÔ»˜£Xݲ­‡÷'¢½Ónpø¢Í¦—¸a¦¢7ÕZBÚ+*=¯¾k*´¶Ñ?cßAîìNè¯Bÿ@$ìlÛWXc|ÊÿÑÁnrˆ:.7º–½ÚDwÌ.pPšã|Ø!i;¨ ×X´÷>z »‡ŸÀÝãû0$ØV ÉѯU‰ U¸þ)ã>iÝÆÇÇ£¸;Ǹޥ¡púÊžÙ-¸²l#gè¯Ioj—s5>¥tŠÉöÄ]”: ÃN¼MÂN§HLü»&,¹ŠÓ4H§J Íð¹BÓ+H~á}t±v%o<òPÅø;W}ÉŠ,tY~$FØÐI}Ùàr;"‡-CÌó¡¥Ç˜¿{ RÌmØûy Ü›…_Oƒ6•ƒÙÎ<›Ÿæ¤u²ºÐÀàVÄK!ß­ƒ­ûSÉÜF[Üm@¶E¢QX+Œl ÅYǰb‰Ú½Ú5]¼¾åraúÀ<Ðÿ7޾æ|Wé`ÈÚ/Ü ©2| O‡Zà>ëP½ü6õß® :/þQÓi³qÇ'{rÀq H¬¿ CÆ–hZõ‘(‚‡3¦²È;1¤`¯µ(´¸M#2C{Ð(}ÎZÿ¤|ë&lÎp`CVÓÑÐ] µ"£áÆ rý¹ «Û9‡Léy nSÀ1ÅÿöŽ&1åõ¨m‡Ÿ³·°ß5¨×á uÝ{Ð~^ÿAÁ:í½ð\b:TÿˆçDÉ[ ½\˜ûb¸´A6îÖÄKCŸéuépÊê-T­ØŠ©5WÛ:èû»ðIîz-QéôJÑ~ÁeœÖÖEø)nÍü¼ßòùÃKH`s,ìü Ö·ñŒiùþëQ,½aî÷hç S˜ÓèÉ+™·†,ÿWŒ9¡ K/ ÿ•pûÆošùò$š¦Cà°"[—^Se’Ðl’ÿU…ðC…Üã…ð`ÂjÙ· 2L&0³˜6¾—®(-ªÚÁÎݦÛ#$AT:—žÇyGäÑ,Ë>OhG· \Ý;6¯ª¥4Ó¦¤[TÕã>¾&õŸgŸ,N =¡´Ñ{¦vUÑÅ"#8½t›Çkë`.\‚Qô hAª-:¬ŠÇñ?j±HCé¾8z³7‡º?ý@M¥¦áŸƒ7é çè–”&LŸ¯4¥çÅÙâèœPxî#.~ZÎÉÊ‹³*£ýìÆLî˜ûNT™Ž~ÙÚ òs·½€ÊÖÏè²õY|ÓWãàqpÕê uõ{Ì›Kk®ë»ë@]Q›\ò_Áæyxl´V?Ÿ0§ì’ª5þÀ÷êÎ,@r=˜/Šƒ\kwîúõßxEz”ÌÒf¿.–5<“礎âûãµ|Í.'ô7N¦çµ¢¹ Ë8_ý›ÛrW‘¥ÔOg[Ãá›W5ÌRœ»gQ¹q˜»æwÓÑ]-æsc¶Y¡—MÎ(šŒÌYÚ´¯PïÙÕ,ÝÁ5Ø)’_+ÕˆWì1náÔç zl%?oJ'ZxÁh“ £ðöï[šƒ¿ðHÍ+Ê‹Ce(w«m f\[Τ%˜ñ€;ëôMà{ÖNƒb¡¼^/]®tÛØš=Àëœ"€í>ªìGëöúÚ1œT¶£fæs‘zðk—9¾Ù3­;t^^½Œ;?sÿ9ô­Eö¸RW§Xz2»gÜ¥×i"·Ãë=æ®~ãÞÞÄÆ5ê䣘…tü“Ü×0;pmÿËe·àɧé£sïßǾÁöŽK\®ƒ<{•¤Lœýâaµá4²›‰ÿkÅX9izß#.œ °Áð½Ó«rê`ÁØ't.®Ô í)ªP¡º g®úÀÞm€/wQ8q ²ÖvC”¡²D—¥œ‰ÄçÙ«û"`õ¥_§ÔsQ^&$K®ÒºÇå¿0d]‰§0"êfÈLÇ—sÚPÜi,›º:œòß=‚ûÑ3ÈŒü#¸à×<¨?‡Ý¤O@ x&6vã :±¯’™tÏž·\XÀnwž3Ì™& G\B¹«¡v4R m¡>¿Sx¾aûáûÌJª¼Ž ‚x=Kê?oóÿ÷´.ІË2»èØË<6Þ¹²/§á²ö ˆsìùU‰‹„B€¸–ÁǯFä\Äœq™9½o\[äLöFRh%ÈÆè4ÓÒÇS8[?1vïòXØ%m gwÎ"—/]¡"o ¸¾; |ë–~<¡ÈTƒuHU‰$z§ãtcu¶pI2Žiׯgûæ‘Ö_)ìø‚vîêÀwè¿u‹‚f/\ë¨Â1}Õ¸½¸ï÷eÆ»5Éúr+¢p&—wè"–Me{rªðhßt8æcF|Lf1ÇiÜe¹%p°|Ù¢—GÌÍ)F78òÍïY€eîglψæ²ý0o ? çÍEzò£!S5ÍžªSº3è˜pã9H„$1ù¾jn…0]÷,™{9,Æ´ ø÷úáïMY’=QU-FàýZܾô*^hWÀêŠ\´}} /G £¶P(œªHÅö–’ÆEø# •ØÏÛÒdx¼g'Vwø³ õ5påŠ+-¿Š‡üìï8D}G®£ §:)þyÜhL˜=k²?±pŠ*Œé—£†K2ø1_[­'¶ò<Î>¾6OÒµj+VnýBw¯,D–4—üç¯þ%INnŒÁÑ9xc®•£Ü«Õ 5þ#×âøj›…Y_S½™Q6?Óè«ÈLØ^7 çÛmÂqó3e—œÅÚe Ú¸ +ØAzgöb¶2õZO”Ç­›?ƒf‡*ˆìðÅ ¡)t»ƒ%{õ‘ ÿÎp:¬ÑÏ9Íð€6jD9ö7à(Þ-ÌCªìö ²'>wvìŽÓ¢$'9‘42BŠT™Ëâ¯Y“Ù•þÒQÁ'ß¾ò]ôĺéatH| ù’²W.´gëWgïw‹";«N‚ÿÄÐíËôˆÆ‹gÈ{^ ›—K°þœÏØ’^O™û@ç7l.4Ç9·Eàd;œÛôдNØ2Ùˆ„/j„ËãÌèõi¨ñô:=ô½á ,×wÔWÀއHÑEi.¤~š *I¥ÏŽÊË¥Ñ Ó‰¡ö6Gð ®?Tò…»ð®I_Ö?ܽˆ§Ôð{‡uÿæÂÑÚ™ìóê\þx KZzŠ{D1D² Ö‘a‘ßÞÂúÌX¨;#Ãö÷O™¸?-õž¿‚3ƒDˆ´Ân¢µkù—¤A­2ãø2­§¸ÄÉC8»ãxF“¤)›pÚ:ýŒybI~¬Ÿ„ºÚH®.ºƒã 1DSlb.¢ëŸ±$¸ùVö^Â+6Ãܬæ50c™!ÉX²±¸*P’ýŠ’ƒHcSòqÂ-„‰±•¸sÅçÏÀ«M‹Ø7½FúäÐjÑ^НŸL‚ùcX§R)/¬¯ì–‘GCÜŽ[Z¤,8˜›ª¿„Vm2CµScÉÙ `öˆîõá?ÀFµD ŸÕ"@Àx/uxv‘Î{'Kÿ‘eúc,aÿ;inå®O0ÕGŠÌš÷2/ÿ†‰äÌϳÇàº\ýpíi0[}Êdnàú.ü¡Ê‘ÑxVw/‰sW…OM8?r7rÛn 3 ÛyT!"÷È2þN\$fÉÚVÅ¢½B' K«sC¯wrFÜTÒ[Écâ›38ó¸né,5T…¦çÝ 4R ™~Àn:Ò­é?à•ÖlÜé9=‚dñ%c84$ÄÒìóAÑ  dÓæ€ÝNQ¾$H¼ÇXq¿´…YIûjv«ç _ü¶y7èÏŸ/í$AðœQ>Ä“vápFX4äL–æÎîY‹ï³çÃÅÃrp·s ’PÅfÁ³œúKÆérw¸{gÞc}‚ Î’‰¼húã¤<ûÓF;‚øà~6 ªë¾AÕÑ|¼¶†ZS\&¶†xÃ.ˆÖ²«å…Pöv ‹VûO‘9Ûôðb¶ëËè¦1\þºïøúÔCžÕ'IÖyqFj¸â‰ô:¢Ç|îZÓ‚Ó’89Å%I}µ«všÍk=n-ì!Ô9r1–rìf©.ýÍ}Ý«ÎèUQ2åí0ïè[¬0ŒbYzC "jƒ•{ø|ÇI½¯'ÇjÿÆà®Å¢øL [s™.ð`Mv/ø3½[q Ûwö‰0îµ6¾·NÏ/!˜ï¾…wõ–jN&ßÿ³‘}ÒӖǘŸ.é’ãä2^ »rNâ—ƒ1d©ï~¶{×<ˆ˜º®‡˜Ò%­3ØÃüv‹«Šhwü­Ša¥³aï4%¶âø”mCZl {÷MT[¥A;/À¸’(›–CpxK-©¡ÄÛ±!Sæjäaë¦bÌÌíã_Í×a^Õ¯À)Ü×N'²úÕ܉™%˜ CþóIÏ*õ…µ÷ípÝÞÜ{Þ#7,c8ñ Í«ÃD†i-Û‹çNƹ{þ¢×Çå¨Ù’Îzˆàzqö§r°õ»<;)þ 'ÅIrçx¯±|þ':ªÕÖ§1­yüa9. _.xÌbɳŒÉÎ7ÏÀ:ýXöÑÀƒ"üôØéqHÕN_âóœ¢ÝÞ·x%ðF¯×Ã3§Ëq­ðUÌï C™b$æs·®×ŸÇùQÍ* *¯Î¤Ëı׻ ¶l\Cþó8é,# ¡%œ>l?ÅÇþˆ#(³?m_¬†£ÂÛÈUá)ä÷}2¡x"Ì. %ÚI@Ýò-DŠ1æé'Î{¡l€â JL+À•¶NáâÝÎr?ÚÁsãiLýstCwóOš_DË×S±l¥)ž$I¾Í}&íGp‰¾çS-Dfè_çî/׆ªœ×HÎ\§:ç¿¡Ã8öèØL|a= ¦DèàçFUòõ… ·-†D¿LAk!ÛpDÔ-ØK—Um#23Ò¨_ç$ºÄü3ñòê£`²†iε&òÊÄö›ßÓ4švߥ]sjXÞ§ûÔýÑfþ͙ܛˆéE~'y‡Í¸æ Kñ[š+QÞíÌî•r¬5L‘IîgR±»‰‹û]Ì™¶÷žŒB ͱlD¾„?Þ.’ñvT­ö+4‚¼žO¸°›`g~ ÞÊ`¾ÓH”< þXE¢ý¦c£j9\z&¾NbÊ?kÁ¦÷#\”ÑC¿Ü Σ}1ªÖšo8浤k N@Ú•@|Ó´§TN Ž‹yžYãÁov=ÔÏR oe!+½I+r*up`s‹Ãå(ø9§›Õ`ÿ]ùϯ¹¶ ʶ܂[¶2ÈôÙk®|ÑupxÛ RZárÏrbjTB‹tÛq{Q2¤½Äÿ<Ö'c[©þa1t˜xª^Œ…‰ùAhµí–Ä à7-(—]@$Qý¹c¡%G’©zåâí;gÑåÅn¸‚?¥nàˆp máUAä%]v‹“ 1 9Ônà,öK…–½™\͹¸³ä>…Š[=±m–iM?‚>Ïg1Õâ’Ôù¶ÊÝ@7U3X˜uË Ù–­çpF,;¼^žÑÒtZŸ Â…¢ð%£Ï<ö"¦®a¸|ËnÛ²pþÉàU¨égDžo™@†kzèõL\S=Žôí1a½O¯r¾ŽñðúV|“lA¶Öâ¼æ¯¸äïT2Ÿ;8 ³GµØ£j5¢=,CEˆcçë0üºMÅüƒ…š¯`é9e2]o™«ßŠ[s Èâw"# F>!†³¶‘ƳYvBL˳E{kÒx@¢qiúgŒÕ&.8Ͼ„þãa±QÜóà‡ðgi*+ßÈk9Ž®bÚdyÂiÈNTeÿ¦FaÔ—äìŽ1$½. ý«¿BÔ(‹Éì¡ü©Î0­ò!MÖ¨ƒ#s\éøT}FU—ûðTÎâ±Ó¨?p êW/Ç)sÒÑRO’ŒY*‡›™éÕTÇ]Gòè˜^ö0åtÁާoéºOÁvY·b¦ËÓÿ<Òƒ¸k[$òÔG0UM+½;¹«w§bGÄ\’n#Ç$/K’6D ”ÿÿ= ÇÇÿ †¦€›ál²1' eš+PXuˆ.gÍ¿oó„ æã‡•Ep¦ç9Îc@º·”°‡icÈÂ*F"MFëxl*·2Õæ V©¹„mL¼Ž•923¬3Ž‚o›1as*HÕÕ DØñ ùöÁ“m}“Ëÿ¬iôÝö¯ç©›£ub½Gv“W'ç2Ýñ–ìJàKT­OñæcÔÌжéÊýÍ/‰µêûIˆ¾šü³“Õl€õÞ ™ñ‰T Þ ¼~VVµ‰¥E'Õcu| —ïÍ+ýmθ#$°½Œ¼¯ f¦ŸnÃÝS?8»«·©AM6½ºe 3è.Âygâôß1Øz=‚|[ NìÇ9À…¦q,ÈР0ƒ^Ž·_áÅ(<Û¹™9¼ggd¡Dgûá­EBrX¾T­.­¢ÏbI®• QŠ m?¶³—²À^'.ž±çŒ7äle¯c^ßf¢‘à©ùZ ¯[CÃ7’}UƒLT9A~©Ü‚¾¬JRPïCÀ(N®¼ŠºuQ¾?›øk¾ÅbsR&~‘·/t#ó›{ƒ{vÿF=/aÏ᥂ ÄlÉ1¦ØÏí¹:%ŒÎ5ì £sPû_U¸û.%\^S}óÓÐñÏ”ÝüðÅfˆðú¬Ç0ÉÁ&þ… îdÉ£‰lcŽ]ó. ùÃi³“al¡;ó˜k§¾Å´%}ÔüäqÜ«®JÂçïA˽Q°!'ÿ>Åi ß0H0 s2ÞcËŸä?´êp2”=y‚SßãC­NNa¹Dª2—šåì½@&Ïü!ÇÂæÂÃÜý9زµCT×c¬–¹Ø\Ìô/OrÄXÑrðÑT§NW&Ã%Eºsqn² ƒ“àHg|΢{-Û@·(fåÉà?çP[+I\®Ž`Ãϸ6“åÑ¿àxJm:Ñ 3g_ÅçGÜ\'Ð2¼Ü˜;÷-Wþ//íÏ!bË%ašPù6®”¹Û]Dк³.þtd¡M¸Æ xZðÀUši¶q¡›üàtu$~îž…ù¹e6YðÔ ]õ=@Kk!Üü†Ëôc!„¯CÂ}Ö0S±4ðÎxyËRñxbÕtG}¯(ÜzÎÏ9…I x'äÿóÆßX[Õ *¸#1‰ÝÛ Òî ïvþŽPÇ– æl{¤&ç¾ÌŒ(öÆÜÖÛx²sô+}82=³gsTތٻð­‘É´é‚_ç»ôH…/a!g´AAãt]•ÇjùXš«È¼Ãð߬cÐ6í¾±¼*þ ’naÒ¢,àù6ó| Ó‰ðÒTR²G…|Û€ó|ÏÑÀÉÿ¸ä?!¬p¹w,“öônÅ¿òsQ74ŠÚi¯C¿?ËXwõ-¸y¨³;¸ãR«ð¿øÕç£ù=-–´k-äöô hËR˜ð_¼U"Æ®øL,•”—ïs!àT¾ˆÌX–Žœ4†/s.q+»9P.{ƒ¹ÖkÉŒ9µ\Ç—nfÏ ÖW¤Ä¬ù6ÄçZ“°‹Cµ¡eÐ"‘L…³j°³»VÆJ‚Üädúmûh­GcùOIò±{£ë5¡Ôî5Lvm‡uÛ.rªwØÏ,OööÄ1šßæSwAåäwНM±¾ÃظfüZd)go£ß¢`ö>—÷ö² Æ×Ï`~¹“xçÞ/fBVÂM¹•`ÄŸuÈ—oñJˆ}ÖÆW†Aò…(Ó0äù޼‚° $z˼eäN*/‚f-tá7ZÁ¿×/D¢[¿aóA}ÜyLˆ•Q#SF60Ïü)BÁÿ“)w@Ï‘fçvKl"»½|ìù½3æ:³‚‘xh±Þ²–‹`›‚ ”ôЊÅÓÑrþRlë¼£LÌþœ{:‡Eé=sÍÇ%rè'HìÎìb­åć8³ËÑåäfÎfØ—‘{àv¹E|½Øß¼êâ“”¸¾“%ow6à/C!ÒäƒàÎèçÚºŽÁðñ °XˆøGr(÷¤œŸ•)\Åz¶öù7.TJÉ]+#VVѵåHÚp‚f:|2?G2-ºxÁóÈ_Ô)ŸÀËÞ.DêTì‰ÓãDRñ,Uûý ß8jø6Pv>ÁEò@æ ÁÞ(ir·è ŽHç1¯íÏqãoš—Æ6'–óLŠ$o[m”ÙŸØwpëÇœ½í!T<6¢¯Ò˜ùé‹Ü—|ö$ÝŽ™u“1‹…É‘ä´.L‚áæ*ìm܇B‚,÷K§X³Õ6K³äáé°¡`©ß] …IGiüyº $N‡BZn \˜{VëöŠLx´Ö“Ò’²qÒT3Z”¯/ÁÞ´HÌPÏýÝÿ&{ÃZ])ºX͉ÅXò+±ÂÍîî˨üÁÜ(ë„êÌùÿê%ÄVó%fäÃá)£ë±Á£·^¢Cm^ ðá$ïØ™ÃðT1òݲÀ³^>¿ßžê°% šdZ¿/;|²Ÿ|9ŽBèÁÅU啪TñÅF¢1àÐ3½^øîÑ…|T<»Ã›“ÞÙ[õXÉ×*X}8ƒ;Æ;«6=]¨~Á{©ú®î‘a.×?¨C–ÜÝB^Bß„X³ô,—p-·ÂMhGÁ¡ƒ“™¡Ø)N-Tª‘'=4ËK¡à|íVeëèè9w;î\›·6Œ;Á>~šËô>ÊPÍje’²¢˜jU\ä·8K p7ÖuÆÔbTك˾ßãºê穞‡Ü³™{ðéÔi´Y8LÉ¿†×wBÂ%u0©^lö¤ùÃG°=ó fîê‰×Æ ùª ˆ[s0 ¯"RØ‚“+pì¾U,Cʈzh“óãaër-"t—ýmâžEÏb¾ÃÝX¤AôÆê2å9‹È]ms6!åî#—¤„éÐ`ì7ŠmJlòŠÏ(¾á8ÖJaºE±|™ºz ŒÑ"-u>,d….ë¹w·ãЧwøÐÈÝÂu¨¦¶ëpOaÃbÏñ™‹ ·mÏRº)áú¡n ©,Ïk±°c|9~Ìú =·¶bj| Æ ßÂèüZ¼™…úù¸Ûv#!NÏaó¦tªñn¯á~Öc–‰ÿŽókÎÙq1Fµ™wÆ9ôðD!’+ eƒï£ËÉê:eçå4dïeaôßbl ŠÀÇ¿.sµ³^Loß 3SÐI³weEsK?ÔBÏç‹ðñbž=< lï ò„¡!(ÿzoÇ9Ú°ý¼»àFÍ×Ôs6………ÝtÛ¿ùêÍ¢ú˜žƒX>äÔüAƒÿJŒê©¤vºt«Ùÿì$ØÞCt˜ú¼Ë¥åÈí•nlvyv¶6B§h÷Ô`*þ“÷Å¡ˆ·ø÷W¿Ÿ; dèC¸²×œì h£Væ‰ðÉ —d$l@){Q,ñÝAõÇn€+îçáOÄ8!veŸû£êÃr®3¦[w}‚Dé#áxÔ.Ä‹·j¹¼Þ<ДØA†ÿÀNv ÿ²Ý .Ô…ñB£×±wßçÀ¤¡Ô H®ÈÐýBÊ„S ÄëK£hË…ld2éŒÓ‡iÀ»ƒÔwÅIlý8K*>㟃øïi¡R¢$ÊF؃‹Š)ó;ºŠ©~ùf†o?ærz´ ŒÓΠQÌžÉIïÂÁ7ávÓ]ôßù“lm€] %¸=O‘ü)qÕµ\¾¾ ÿ®6^‡†k`-ò–Wo~«´»1¢%“½ØeÇÏ`È1G™_ÑOÎØ×¾â!›Õðà„ìX7sT8ðhk  §¢æG‘¶Ü?Šñ+Û 3í tâÀ½Â°az*å^/XeŠF^plŠº^‚À„X…ª‚@3) ŽlåäwúãÖk&xÀ„ÕÑSCîÉû ØÖê›÷Š‘cŸp[¶Äg’ÄøðIHzz†º˜HBU…HÜšnîœGpPº•ÿ¯Ñ’T=¬@á/ÎLed´¦þ\áæŠ,Aãy¸ØýÛÂùŽ—áI÷&þÒðYyŽ©ÅS×ÞŠÊK‘öÆÙC¥·*ì¶(£©å0Sq >¹´Ž—¿w,‘ÌB±oMÐ%ˆ!Kb¡ÿòC„[K¹¨‡àfR2ìËöÙ¿¢§˜ÓX'€Ÿ6þæ Õ²±iþ+x:=ùE×8|†¡µÜàœd\vSäÿäcÇ £Ÿñîk©Àß¾×è"±_6»Ê… ¾³¹E×—à£×)à¨ÉAú’O(åëÆÝøX -…ÐrÍ84x¢Šv[°e}YZ–ÇW÷‚]¥U8^¹–‹ŒÖÅSiu\Q˜'^„u¸Æì®ãô5 h}s_ˉ¸ì¨p'Âg- dj:ð…PC%… Õˆ4°yfBv~>";‚`ÝóØÑßɯJ¢¤8f†™áPæü°á8o Æsy"øá9(ß±¸_Ò$ú¬(ÖÙÞC—‰¸çN\:çÏ—zœ†2en$}¡²å4=¾‡?ýX ¾HZg'ãñü/xÙÛ¢VÇc·‘é¸fm[¨¸_^[f‡éߩږB´’uæ~Fë´J@Rä[1ýƒšå¸§W9}ç¡ßó5w ÓÈOÕ¼×oŠ#V˜ðUʲ…5ü¥ß·q•pwëzBÚª!N`z)ýy<n|b0v—{Qù®vÅÚ"Ê;lZ†?×Ú£Ðü š¶?Ì„#é=]öÒΦÝ뀃« ¸Iö®tçH3ÈÿyAC¦c¥C¼Ý¦ÈJÞÏg?’Zy»øâlõìþÎ'ô9%ó ìá~á27á*Š¿Œæõú´À_A’©¢Ï²¾é:ª¬¿ÊÓ`V0>ÄßþüÕœÄ63¿²î˜8°²u†ìùôyq¼Ž— }‚§°öX+Ú.åCã$Yr¸o)Ž«‘Ækf8ñ{"¦¾í¡[\@¨âþ»GúÜËŠÆ¡Rm=OÍZxÝÃÐ36•ì «;ö0~Qc¹´é5´`&™âäHæLèžh€ªN•±YœîÏD&éø ^Ú%ƒFpLùªº á°¦¼ .ìb›»}Ðÿ°9苌ð¹Hw Úþ oÍô¢Í­8̇£=¯kïjG÷kŽð=]«²VÁ™Áelï³xÎI³ìD’`~ó%ÎÛé(Ui¾Þ ueŒÿfÉ.?Âòg÷aÏU]²dVõîÈ‚`ßô©ð)86×.rû:èŠÔxó¥£äýÑÞ(£Î£:[CÐê‹\ræÅr ~¸r[ï˜àªÍg¸}WÑaÙcxØ·š ­Øx¬„ÊߪLbÂÚ·q Î8øŠ·¨Kç i\¨=“iü‡ÅÎ×HmšŸ÷ÂcpdÊy´[»Dø‰Pô¿æî§Q?˜ë§|”èÍ %¢¤d{ ÷íôÖ:Gµòz©[BÁ]äMß½ V(Ë|^[e54¢{EÄ1~K º/@æõgá“d>7_]V*–¢•ŸUÛzƒE4ò¦¼þ›yà&Ù›öñýÄà¢Ñ ¨ZC®8T“@¿ÏˆE÷—qœê¦õøüL7Èm)ÆDÒÉ9·ðV¤û¥çâÜ3Û˹wö6Äfä:HÿóÁWe] ðì0§·3ž5-…øþt”rýÚçÑ-E€sð‰¿ªôISW1„jLçjU"§ëvœæÍUYÊlÿEMÜF~Å+¼çøïl¤ëS£:…¡ÙC0°}}ÉIX³m2Û¼¾ÆÏá¨Ø8&ÖŒÛàÒÇ-Øð¯oh½§=c»¡ºE §þ†{Y?9ÅyÞxmE<æeµÂG{l¸#³ŸSò¡t†!àÛ^¼·äO£@ào웬Åÿh¢Éõ¼›/QîZÎ\ñ5ŠTcál ×È.Ï/ ŽKŠã³­×áýìµ(°i6L]yÒW0n$¸ÊN‚õ•Ðâ]4¾óùо)%×›][À£½W½a9‰\ rxb£ùíâ_B½Qî“:‚«?‚›«5™ÖrG[»•,üun>[ž´ñ×|ÜžÒÆó‹¡Õüf]P@éÝ…ôÛ—÷˜4Lom–`^>§éei-ç/a•ì"M°ƒËNÉŠMt¯…>Ñ]ú4÷;Ûå5¾ïN¤LªðçûôwNÃÙ^ðHÜJb;–’Ÿ{ìi—¿÷Eå*Îåöq[Cä ºaõŸJLHÈñ)á>ù2ùï{geIÈöo(ùÛ’9J'±i|°¹/‰Õ÷¬†7&pÍ>i²Nò /\h ÌJ}Ž&§u‰êT]Rr©‡?«yŰº'Î^éÇ·-cØvŽd6|†Ž(>꺉“ïß÷;6O‘!§%‹áÆ‹ûôàÆÈ3„¯%ùÂS³¨l ¨\B8_‰>à ´Aø|ÃøÉ± $« ceâ gF77oå)ªTÿž0"¢ù>l®Ã2àÒcÏî¢anÍ<ÏÝŸ©ííøÝ5ƒ¼úw˜ñ'òwm}­ÏÜJ³lnç«6ðP—ããYPBl欔+`Ø þÓ+Žœüyyx%ëd;pù;2/:‚ËY­ ë¢ÓmA2/¡µYgôÙ”eí{ˆ'$DAÑý'ASi!6g½ìÛõŠËz˜cóî"Ë‹Ó[hÌ++îq£8k_£ƒ.› ¥ÂâV.òµ'×i*@R>Vr¥Î—psl¬m?­ëƒ˜´ÇYñ“Ò¸CÜF]¨¶Ò SÁðp7î=ãD×î7"o\¶Â¯É¨™÷.(]Wʽ¶ Ú/¤£xD7htȰ±o¢ÌSIÆ^ʃ¸û ¸[: ól­idòn¼³ÏøËf¡\³ Û·x€¿Í¹ÞµXF6‚‰L.æVúËO LøÍzÜûZNÚ£šÕ3”)Ëqã¹ãcȾuï1òu7ßdQº˜9â.õÑ?÷ž“/‰‡Ò`9"âò›ãyAØryT/TñÁ{ëM¬Ÿ2ùw˜êk3 Udu½qÜ%ž-Oÿ…9»^cç^[2G¿™'~,‰µÄ£F”?-ã}zÓMMΊ1”©Ä]^“%y­î¸\c<‹N¥%öKp…f¡øð5C_Y…³ÝÝmÿ¶±Œ'WÍdçÒvweRäÛÚÇïo`Â&769}69õÍ”Ûò^oÜè8ëÑ)´ïy ĉŒ@{P^™:±º†ähÏ^SÒ ^¶r*ŽžÓÂ%“Q÷v¬¢òìŸ#53IøØ—üúêilÈ~ê/'ÂSDàzf=¾Îû|é`~p:´[àüñ„Íóº‡_Î'âØK¢¤v‰ ‹Žƒów1"e ¦¢DZaþâºaps;>ÑóÛÛÈ H ÷2$'¸·QS Ø_+‚CbF¤qÊ3 ž¢ü=™¹à•š –[ªàà^j?1œ»69‹ËþuÖ݆53>@ûIIÇý þ(F‘{4~ë5 O¥ñ œ*Óüþc©·þä[“¥ÖËxÃìxï}!o¹.S{? ¯ºÎ ²³v ÍÇû4á²0üˆòg§Õ‚R¯ºŸÇ«jzêø°MŠ›$çÞXï>™,—ÚÉ}7¯ Ÿ«ûpÒÀŒ¸É™£–Ûc|U'Á~ê†ÂäO’¤ü‹±ÒL!{oc Ù_¨W°ƒ‘'‹à¤è8Æ3à ß?’ìý¿Eäá‹,IªŠor‡_ûÒL'3XÅnÝGÜ1qY½u ÊïHCÛðñnZf˜²Ïßf€ÉÜaà«ùS³ò+¦nƒÓ=‰PôøRŠ7ÏŽÀÆèVzÈæ>ÍPƒ&¿iTm…\ì˜Êê?tp"GŸÑ>½×ôìý~nƒÜÜ0ó4ðŒEIµÆüeå:·`à¬+Ù[4ÇN ÃÅn¤ÀÑ’ä6%± uP¶OˆnO¨á-º);í÷ãÇ­GáèàoTø!HŒ>’œé`µ6‚í 7bJßäHšãTbûõ>f×3Ý$Af¼ë0+aÑ÷®á©Ò$øç³ü Ø óÓG¹”ÊCú}Ò]ˆÐÒ`òÿÎÒÚx1–_i3Å¡÷j¹öÖŒå>ÞDÔшUÎ{‰G¥ë|§‚âéΜ„x=_‡›upêãû¨¾B•mp¯Ûx×3ư 5Œê·d¸ÿ‹³ä4Ùg­“¸T…ϸrïVÁµº$ó×IvhêIúËLÄ5ײ9áú¥sÔly%½þä;ẋºÄ òà¶™7¡W–¥‚K¦9ÐdKò¹(Št Þƒì{4˲f³Fu–¦3Eõ¾PÜõ< 75|BÓ¯õh—:«ÌÙ6 %Ö¦½ ÷ô”âŒã·™OÙT KXµž$êí¹F{2uÁÖKŽüCÉ« ö‹÷Ã9d«KÊï?…–Ý×pGX¸ò2 ÂðÞœ”Î/S@ÿµ \îšg:ÁÐ ò÷ÙÞªUZ쨬lTqÅÓž 2“…bo©P›Wµ{ÚzO¥¾i¢Äð¯vLÂÕR:Ó¥–„ÓŠáZ:¨è€OöžåícèŽ7Î\÷b²ñ7ï•;>î¿w”*¡øÖHuwääÄ&‹1÷`XÌœ{þîô–hAÊDZÜÓ$%¢öä6Ï M=œ¿’^ÝW ß,Á·? êŠ}ãÚÏ뙂ÜN2Fc ®íïêäúFDŸdSw ú»Î q¸•ÂÍÛMs/xã+w=4j¬â.?w€¾â;|±pØJ ×<Œï=sˆHx ê‹ØAXØdU4Š.£ù¨–§‹º¸eý#œ§6…wië¨~z~Õ’±oU;Š…ŽöÂ=Æñh²žqu šèìðºï¾+ùÌ:à‡›[\ó ~Ž3Áû¡TúáRX÷4Œ¾ŒÖÂñl¼ö=bÅõ˜e¤Û:2 :“*©z“(ÿ»…w¾3¿nb›E{ÅYgñœCqIb/,_Ô nu¯@òý”RÛ…u¶5ÜÒ£ƒ˜XsŽž“?]òcé½ßcÈNµ1£Ï@,w\ƒ‡cTÑÒì<¿Óg#fg1#zÙ+ºˆÈy*ã¿cêüORZÄÔú\#D }7°) ïñ€d'~?ʸ~h\"ùHQ)¾è¯…%fLå‘ ¹ÿ¯a³Qï_Útó,fåø:*’ÔÏ-Âåm“©õ¾ÊÆñU5ø¸H}“>hP#¢Å‹ÉF~ 6KšAÈ¿µä³–&9ê•Ñw91C ´¼¹½cíà±ò<œÉ?8ùc2¸µy ÊWÐè…o°}Û#þ䫹þùC0W44îÞB¡*U¢%ѹ6mà³z*˜»˜™¯¡M kx{gÎDß+ìwÍ|údž\²º7,í òVŒí*¤ ³aòùpÜŸ|Æ>>7—9“¦*â;Ó Û²>Á¢–÷8/Ljá‘ v}*¼Ä0°¿œ'+//›“Í{ñuà(o[ úªÖl›R˜‡‰1ÕÂx(©{€Cϵi÷ðOnî12œã…99›¡H»ˆ«s2#†MX¢ý86‡ãáðX ÜuG…œAF„f}ïÅýÉèéõ‚ï(2aïVX–V׬…˜¸ö-αçÿ lU*ÕK£cg_>O¿½±`ï^ ÔrpX& ›{zð}X/üV8€Ò¿CÙ…äêÓóû‰êqë¤<Øn_>(W‚¿VöxíÆ/îHÐî-¯D‹’°}Â[èîZ6Š¥›P€gNTb¤Ø¸wká̺Aj"¸Ë1lFA[Þl‡¥²Y,`žœøq Ÿ9… Ó‡iÓwlqÆß‘Ïyóº÷ƒá®éd»A'ª,|Æ%ûw@ÆÇðºà—±Ñ ã–ï k™Åšl¸È4qH'—ýš¾“œ¾y oZÙ0E)E–9GŠ«oÇ='>à§ç<œ9ØŽ/{[`«k ž‘ÿ‚³¤$pÁp$ašý¸×7™ÛÎMbÊ2HÉ 4ºõ‹ËW¼“þôC²¿©Ýi‹“w' ôæ'øÑæ7IBŒÕt”q? x í4ÝÕ¥m|“`g²Áé rê@Î×™údC1$+æ­„.X¯dÍ=ˆa SYˆ.]³o<—ôì,½ª` wãÄÙ9ê…Ž_ÆÒOÑ38U2?¨Nž§ÔrÊÊ`ìKÛþÂ=Å<äÌ]ÉaskV곟”?耕Ö+iê±÷ü¤ƒJ!^DîOLe& ÷‚E¿Ý0É””¤ÙÂã†Ùd±âu.åS+srJÅjaùæL¼`_I?éê‘@¢Ål2žêçW¸)Øæ\ÝË7ë?B`¸4¦Îåc‹E²Ð{ ½{*ÚØÖaDw}N€'Y0X;ȵS>¾œ,ÏêÆgàg^peQ%lwYc™4KJ—áÔ œ¨‰QoÅÉŠŸž ˜`ÍB^ÌŠ#uœú˜*<{/s¤HÐ öõw6L¼"΄gO!cÇ \Ò_¼&BÂC°+r3g¶6Þ|™Š«?Ì„g/¾qdžqÅÙ X.³c!®\›F®q^$Üt/·ô^#¾U çÉàxÀÕÊ£øw7²|ñBHX, þÛiqµ%|9 C…1P]ÔG'©&ÃþÙyüˆ—&€:që ÍäÃráÖ.QD¡lÇÊNž¥5ñå¼ b@H/ ãèM ûp¯/`¹*ÚdãiFÔïCYÚRÐ;cI:” {÷ÃfŽDB•b!ÞŸIìýàòܶBmá¤@3Dw·L”‹ÜG$dØâ¦õüçû?À¿Mrd’£XÞŸ‹6!RÄ5ñ*xY¯ÀBéDvÊa ž›_ˆÿêÛáË̓¬ö€[á- §&>ƒ×kþâÜ»Û`w‡»ÝçKéõÙ8œ1嘧˜;ÝÖÍ€¾Ãk¡œé²“zø_'bÙëøuÇt| -iª†ì­ŽiñƒqÊjÈ~]„ѨÖpŸÏ—"mÌGòkaÆ#sÖa±´ñfމºþR~»€«þ)ü!ü’¿é¦›û7ŸyC,˜-¶ ©ï-‰¥¬)çvÍŸúùɧ+™d³ò$vn™èÏ­æ_wžH–ÛªVO!.g¦½+)F.,?Í]š©Ã~ Û±i{T?;KsoáNã|®à˜2“øè‚ù!§Qiõ{Ìö¨Ä{©¢¤fž8«¯ƒÏ7d‰[K$Œ5RâÌW=çÍÊÀãn+âÓ,|$x ­ÏÓ¯W;àD¾:KÇáßgQà^.÷=eÌ»<—¸ Þàúî½äRßŃ{Hâèù¼>š«%o>±I3hRÎDöbÊ’«ø‘¶ÈfƒÑä<þ?™Ö]´;ä›Hîå)°Ð–ÓäÚ>wRý8„÷L'žúÓˆàiV´Bã:m¶ûÆxÒñøÂS’Ÿ¯ÎZöÙ°»Ÿa"Óïb_1Ýfs:=ŸíYI”V Á›¾5¬å£»7„íˆÝÎJòƒ@£\JÜèoŇÐßnM"’c™»±yÕ¾+å Ù¹º÷¸æ=‘ˆ¤;ûØ!u$Eî' ü²›É,ÖÆªÖ}ðmd<;wBè¥ëÛ¸mIq¹szC8yóÉDÞÝM>M·ÄçÍ©¤ì®%»œ7Ëþð`³ù„ÀJŒùhIzo‘êgêÜ–à¿·µ/o'»>e!¼ÊÆm+ÏãîÖJlˆñ±»¼ycg’U¾rÛò—‘ƒË&AZn!ÄO˜ ©¼Zp+O wŠóOd«æ+2w%’0wsï*‘Iïp ùFTWH&K. «òqÊù(ÔŠ’baq[É”yˆa5ò(óË|¼t ÝÓŒÉô÷s# ÁDÚKšl¶:‡.>„I+Ö·q#™ôlµ7}Å•{7QR²!»ñ|OÙ^Ý â¿ŸáÊÞóä°‰-¾0Ïz¢|‡ô<úÔ@+_·POöVµ¿Å“ŸDpÞXº´˾ š¹=6žJDšô0°ä&(²ß Š'ê7ïéÝTC8rÏ—Ó¥!ä›mÆï‰'2‡s4µ#{ö©P­;Á|Yï©Äܶ³f?å)¦üí¥@Êïä!S3‚“ft‚Ú<9f"Î[| —Šàå¹`QÇ`·ÍjÈjá’¥#8]lýaÁ6gJƒiþ¬¨};ø—¹î[2Üç~è\•ÅÓUpÄ‚yþ>[Š3cÖ]‹YCC*Ûòí(¼ýp—wA$MC8¶8Ä.=¸‰‰×¹]“Ù›ßøëä êé³³uɻ୰×àÌäž‘÷È!¯1„Z/W`êÉGðHÊ ¼?g S™'’ãêy[9Š]c«8µ±,âe1îÙÆjCöbÝÀXÒåiEn{Š’XáÇTê¶Kµ$˜`)Ê¿5Ž™âH$iº0ÈY*Òpñw Vú‹ÎÞ¨ÊÞØÁÕ4wf5üª´ÂIƒZœ¯Ð vÞ û5E¦!Õ|¹©,fj(·gÂQ5ua ‰“Ù,¼7ùæ•Æã|½yäÅÜ»\ðœ«xîJ3d/²íòR`²]ˆmŒnÆW]¬èÌ+À­³˜ó&1Æ{Äôc+©Üð!–p;‘´µ¿ñPÌ•*ýq”ÞxÞ¿…àßšØþø,]*.Ntö4c·‘µÑ´è%v=åÞêxsǃŸ“æ¥Eôc¶ûX£ÆbZRk«"º¡W‰­z˜I¾7{e²by8'øVê­Ä! M‚U_™O¾J °‰Ô…ËxCúÌĺ<ÅÜaúCYvz{þ×”%-í¢‘Ê©íP—›cƒGÕî ›ùnð¨@ÇòbÖ?rÇ„ñ,œOC÷Œ8þüq̉›jEì\4°Øp êXÌZlg±r;Èuœ¸?”Õ¼G87¶nózÖÙž…*m™lþsö–ØbÎeAòä“6Œ â2è¯4ÁÚÂÎ{x\™LDw¤âÓœ§xíÅrèÉ‘ù³ƒÉŠ¥pûóX¢‡ìL¹ŽÍ“¯.Áõ]ñ¸Eוû¥m…÷ø Xê¹Ý$g»?ëÊÚOBTÈž åø°1õ2 ˜L—9wБEúÞFµsËpËN”UK]:åé82A²¢âoHØfLVv[£:`ÝÅpÌϘÄ&³÷ènÉk/†X½< _®€qF$z4ÅéVëð^¬9ñ[` +e™×ŸüîÑÅy?=Ë})c Þ Ðü2´Þ0•˜.×$ß½uÈ¥6öõxÎËòô'òÄ!©meÀùk*€gDEé¶$†¯¶Áã½§q®±<$=`›¸_2減›FŒ|Ò û°‹ê/†¾h1¬ˆs‚'ª&Ìí C¿ìÎúôTšÕ†äÁÙgŒX©í˜ ]l½>ÁàÇÑxx¥3o¦h;ñÒ‡÷øs÷…y5;$Y‚ÌJúkH˜˜¬å›[!,ÒÄc&¦óC™±'I:(–ØÂ•©ÛƒRPÕþ±êžÂ±Ûèö'+ðàËÛœÌ&¼ƒõ~§ÑÕ>¾—ê°ÝÝ_q㷽к=ävãÝ‚dª«*S'‘kó5 ú/.À˜ép(i?„Ÿ¾Ó=‰G×opu²  *çwÊæÅ·C…‚<û9•Ž{cÚQÚ…×7ì"s¿Î#Vë>£œÉý8mӤطSÑ0î}5ÇIŽjVuâb«Šé]ºpaÖXRn´—†^§ÿyš‹âDÙÅ»Gð¢«V-æ"Fy‰²Êî¹Eo•,…XfËåHYã{Ÿ×x¯å!Lœ¹ÇÆìÀ‘&'Þx¡Õ(2¹ æ&¢ÎïXxå·Æc >ZZÎ h„áñ1¤zúfLV3Å´eb(P,_xgþláÖwÒŸÑûع ¾tÞÕ$¼èWÂ%‰"×òe©æ:=&–´n1Xøt!¹Ùðƒj½¸…«§Â˜ zp©2Ç/[îbrùH]÷˜“?]_V'À–É!Øqe*nÝ[†-ɸ8ó¨ºÄðƒ&¦ò­:•¸Ý#\Qg?È2‡Óv‡°€ü„×3SàÐ7 ëJÌÉDß6Xð\˜ûŠ«ò𻢹 Ó ƒLÑMj?Êitî¾£ëÂþò³šþq±§±Í9Ìæ¿Á¾& °«·÷^àoî9ÁMXfÁ*Ÿ„cÛâù631Lô#æEzBñðPÔ†›!_¸ “à"ǘVÍÊ¥2J"¤LP—ý]= !ڒбéèüí,=ÈV|Ì»÷,ÿ~‹&¤ÿ¡oû4‰«Þ#\°#ÄÒr[´7Á’Ùè­¡Ip}{ªcŸ3””Û@“¯2x–‹²Úƒ¿ñçý~èV4ã)nÂ¥Á†XÊÙk.¦þSdA{£ŽkQFS/ ֹȼQZËç*³vOß—¾5‘-(¢û _‚j¾#_nÄÊwy2M2OH•´8>¿@kbê7 ëçi<î.Çä%gbÿɱðm§9ÙüE„½ŸÑί5D)ž&5ÿò ïçÙBqb09jÍæÜ‚.ˆZ$tN*Øë«Á­î—ô4¬©Ú Ùß­Ûðî‚Ê{%Ùß)âÛd‡fªØ™ÎdÏ$Vç`’³;Y›*ÚÖ'óÜ–ŽÁüç|‹ ·8‰5I¼n;Šþ<‚y®x×`ÚÙ’³wóáÄ8#²ï^>¿_bå£$øë’Ñ;ɯœsð<±¦L1"=åßÁÅT˜}Þ<zÞz²ñË H«¤KtÕb¿ž×cÃqÖPÙ… WÁWZxÌXd ù¥ù£^2Ÿ.:&ˆ©JÄ¿y+[™{ûöôàõrÜd~xK>äÞ´çs:÷B6™J`«Ãò°ÆJHJ,'3À¦'plÁ82vA(qá_À®™SaÎâ͘æÌPÇÔ€5l aY'`në5LäZàðk¸õȆ}zžòêÎÃÄæŽ lØBðQ:‰Ô-pÚtzE‚OÿÁäõr0k[..[jDÊjj[ËR™bú1œ¿Í ìc5¹;_@½\ŽŸLÐn–ßMÃ[¶xï0Ó;ôÚŽ&¼¦s|–“3 ¿±U?½Áߊ—ñóîgèöy F5Äâ½v$áœ[|´†3šxä0”›?3 ™c3Xí¨lX!+HŽ­dWÔœi®…þQ×eùíÂ$¹ò1P…ŠD xvö8Hþľǡû\°0¾¬+æ¾$øã´f}b9š/¿NYL¼2ž–ÕsêI$8À¬áK:"y žkYE¼â¤A¸ß×yL–#r¶Âärÿiìì Ãr¢Ë‚cbÁÉUŠ;ùÈ7·(ÃÎîíØ¤5fü¤NsèÚ7ælm¨$¼¡xLè<~žÁ&-îD“þ$u9°hz3µÖÃ]Æ¡¨{½.y˜²¼§QðÙ,޵¥çp®ƒz,´Hv žO?mvA‰LÿªFÞ÷Ï ÿ22éŽp9føü5x½6¢ý‹íÁ£œÇq›Í~Jbg^ƒúÑ<ýÜE.[$÷^O'å3Gõ®Q›«Ãòf›’ÁˆÇ|}ÂÝ΢P{)œ=pˆÄ«‘õèè.È–^5cÏÏûÒz›&ÞBåXbie‹.)ëhõEsæ)»›„îÿ7*v‘ÄÃÃôÇ#ÞÆÕE•ry‘Ϲ¯å˜Ú§UÞqΫyL,Ý} Ža¢+E)û ÜþÕ0qòèwS&šDHw^Ì2’¦Sñþö7‡©²Ðk§Ã=€ajûý#p¿±|âlªênÁ¦sðxL(þslüÏçœæUŒ L¤†'Æ1½à&ZÎ%Xò_"Obú´£q¢ä\}µ¼¨CÁ½¢°Ñƒ­!‚dãö(êhÁ8~i6,µ™€›Þ™Ðÿ¼Í-[Æ“%S21ÏØÃX‘›döÚ€ëÔs !Hœt6ÓÙ:Ð$ô/Äú¬ çxQ¿¦€?&XˆÉfûÀŽ—JäÛ|3˜ p»ÏÁ{µ°çÒ|TI…ôá%äe†{ºÒ‚øÞzŠÍ>{-H*ß L­¦Áþ‚ãüBáQ­¤3Ú «àð¶)`˜ÙÁ?]½Žû¶õ6ÕÐbovÂþ4'{ ßÞt| SÎ{µÎºŸ~‚ðΩä÷ÃEðØ#¯{À& ÙÅÄ÷£‰]×âÇÖòùžŽ¶l¹ËˆIúzÍÖÄ@NŠíùèÁÊ箂IK[ñõ–)ÐÔߎö!¶ø.Pœ4¥ÀÞº<ìß3ÖÉ¡ßv«QÞȦ¸¥ÅÌnß?…or…;q€»€Jï 5@L/%Á̺³hÓãÍ™J$Lek?Le|U5"2 ]kkI>$É­‡»ŸÁrîÌt`s[ò¯ŸŽ£Ü ƒóp¤OÂe¥1»á+ïþø‹2Z=(¨*M.Æ{“==&>©'D²jw€#q0²©î™ €Œï*ÖX‰"#ýºm+ÎM„Œéø}d3xªÇai±(Y´ß~Kžä¹˜¿F%Ež9¶BœÇk¼,ûë4Ò`šÏp^/ #UT¸ê Ý(.uûöPî*âß­O!¿ðÔˆ Û°÷'¬:wo:ÀË«ÀÛ½àÄõÈsù ÆÔÞd JÝÅo࣭Æ-Ø'M"3‰Nq8D}z¾OF`ëªtJÿ¼±žb@ÁôßBõÛ<¨ÁÂX9t-¢Wx7Àcû:.{¼+y¿¤•Åú—…Á™aåÔãp ú¶ë†æ(’9 ü¤àL¾>1‹xßxÄÐÜÄ5ð˜iyNLìe8s´–++U¡}³K·ôÍWkX¥Ÿ|~`‹Óž…`õõ8^Éó`Vèwo¼ßJÏPÑ1ø-K†¤¾íáÜãþÑú÷áü:³(ÚµÈô|6¯þŠÑÊ“¦Z`Úê~yŽTkr·c†hÑ—%Äóa3Íâ•AÉ—8\¾pë:s“*\:‚m¶Œ>mÇ¿Ûɧ¡ ¬_(nró±,N+}·GpÊã-°¶«·¾Õƒä<¸Ë„i%OéóŘðç>¼¸v„;5kdgŠüç‰Æu”Ø©eüÇþZ¬àÖUº£9Lʈ&—'ÝÇnÀŠ?üÁ†®0 ²†úøûh'oH²Œ&aPÇýž•Åå ³Â|N\ùvJŠl7UÅÊùôävÜì2Ÿüû;Ž÷rß[nìgOÔŠJÄ@‡“˜l€ÿÖ¸‚çœVþ·Uhºÿr_Gè«9¹ ˜±M2ðæµ]øñìu´ÕóãFçã]Ý{Œ8D\»19àõ1€û™ßÎÀÓ¥OpÃæx.¨6„¼¶7 ÂWbðâE°'k×5ýà.º¶5Úk×òýû ~Ò5¼&ºôR3àñ¬\û9ì¬[»lÏ8/¾q‡)ÓÖË€’÷Žœtf(ÂcZ@?ù–ëÀ”mn uÌÈ{ŠSwpÇÅCÖÕ·4H_'…rü;Ï×±‘ªÞ{˜fìÍÁì'Ð/¤¹ïä™»äq¨à¼æÁçÖÈ»Tg‹ àys¶ê±Çx;æ™õ âD{„:;¥¸©‰3¸–׺Üú4%âqÝ‚ m-笶´p7¦¨r¿´S?Z nÀÙK´î®5>9^ZãÃT¸$¹Ž>ÚÆŸß`Qž›6>– /†7oÏ£¯}t*Ƶ¿QÄX˜L kikg ,kf––Bd¯Ð!®v`HV˜0÷ÄóxîI7·ªÈÏ/@äùLLxÉåܾ7ZrAëc Ö…‰“ÞÛq]ϰ5…¬ }-—PÕ¼$jp° ¥§ŽÞ÷ã xy.F.쇽Kqœ’te‡â‹ùaX¢¹˜™Zc®h ®ý·žít â$¼àø`X- 5»£qû‚éî6M°Óí'ïø„Élâ'ä2Žà‡µ’0ÕÔïeaŸx¾u¸ÉÓ{ cþn¢5Æîõ~££f¢ø{Ur\w•1ØuUÁÒçˆ(§7Mu>Í"7¾a…G†\PÄ4ŠÞp|„ßNÆÃÜÏ[é²¥{`¯òzáùdPŸú9ÿ\\;#/߇ {qpi¦ !¡Ö#èÒô{ù?¸f m ÷ÃaÆÞ‡xÁª:ž˜A˜V/ÞXô–7g×Tþ‡ù"ÄÉçOK¡Òp=¦¯*¤‡p~ÎX‰Õ}³‰œS@£ãwªŸb íX›í+¼8w¾¯X€ÏÝd–XË"^†³¿éŒ6y2ÏR†ÕÇLÀÎ-ÁPÜüg§õÁº ×8ÁAÄíÐk°´d!g¼ˆLüu]ŠÑ€kúljß°z¯D®§Q™§[Pñ­Ђ5µÉp%ï—3q8'“¦SçøcÇzƒ¾õkî·gÔšß î››ðÉY}RRN×·2YË•ön宲ôIP.n™NÆÓˆI´)V·*zq^c„™”Íe¸#Vˆƒ P;¦/«I±¾1£ì¢Sm=¾Q…ù\ÙÊ×`°PI€:IlÃÈk7ùWruH ÷¿¶ÃGËŽã­V<lÈŽ?4fªÝÜ˫¤aé ^[Öú|áðZFL.ÃÙIr¡ï-¼pÙGʧ|â6\Ñ«Ñj¤³—ãesi[evñªñ‰|AÃÏ‚­(n.*݃87½.‰ŒmÜÉ3+ß®‡gb*_¯î¡}f`_9‚›y èòÍM[m¨p“»ñä4}ª6›uÔóñóÙH(”½{gÍ„­PvhtÈ­*b/,ÃŒïj`¬ÌEXym ¶‰°(M-<3I“M¿{×<{Ëmšø>ú*’£ƒBäªO?[ðÌÕ â¹Ë¶S“ŽŸ´yöU®ðŠ9Û?òƒž¡J+UHÊÊt(í%øsA5-ß•ÊagÀ|¸"ü¶áÝR¨ W$ƒrÕtœ—#¿Æ­“[i4"ÛàÏO¶X»gæéé²në÷ø/© 7ÚgËz>?iž¹ í7cþj÷é>nšLäo,Fþ‰ExÁL”]øç×0×ô›ãæd—Wà!º“>+Ðàv–þ•xZX•xî–…Ú•¿¸#“,ØŒO.°í̵3åýñtå…1ìù“»~o+Û6·€]¡ÕFp²~(>ökú–´ËÀÔgÃ\ÓîVo>ß/U î—zâÏUS9ß9§ñä­^Œ3Q "©›ðìͯ´"ä%tõ)°Š"eæ’Zo+V3Ÿë‘ 6%ÚêXô3WmS#Žw7³MR¹ô€œ ñïH¥O=„‰û—RHŒ™D¶^W`:ûŒ˜óŽF VàÖã2àëWf½‡Ù‘+ñ·ovºœµ&mÌÍÞ:Þ£²œ yj¤Â;qÆ“:]•œR̨-i¨† Ù±LûS<‰4à–ÇC  õP/Ž ¯3ðû÷ýTàB =<.‘ë;«Ëþ½M¦ZÆÏ‹S©Ã³Éã“BŒµ|›­Šx9å#UKõÃÏî·°PiÿZêâÔbÌ/ríÓ~ ò.*¥¹Zäžê.Üo‘†3 ìI†ÞrˆèÔã””&`àIÒí¥K$\EÈ‹¸^(#±Åÿ /5¦ŠÁ¦KÈ´ô¯œJÊXòUs1ŠÚ·€œÊ÷!¨“ ”ÈGÝêLvùÅf¬w[û²Ý H,„Ç|Öòx¨ž³j¨¯þOøzcÉ\Q‰RÞCàZ3¶'ì…urøƒ´Sìr”$Žƒ#°Ã§Jìh ìˆ>l¿ÂÃ…åB¬zóa°Œ´ÄË>»ÿÀ›¬S)ä>ôkq’²Åt4&oË{Þ“½twÚZç‰~;ÌmO\ ] »ˆÝ,u"/mΜïà¹ZÖÝ»¯|Ù£7ÑC€\o¦Õb)¼K=¹O‡¢Ðê˜*3[íH$zX‹Ujn0aþ«!°Ï‹‘„·aŸPÏà,¸¥O¢nònßpbË~(bÃü98d‚ÄOÉþÂÔQ¸óÊ^ËÎþ «“y'”B`'-6s¡ke˜ÃçVœ±¿?ÿ=Oöþ´‡YNLx´&BS’í›îò'ìÄS_Ž`vA+)šÈ ÍeHÕ °¥g%dæŒÖìnþåh››ð8b91º’Λþ5÷TÜŹÊê,jm/ëà iª°= ½NCŸ»çÀ,ÛŽ};y 'o™…O÷[âþ;Û±·lôÍÒ"eÛmHÊù ¢°P º°ÎEF˜QRŒE‰aPáeƒæŸßBà™p8¼´½€«<¸€_«È©“H›åïÑ3¸ë§±¨ÕÁÂÎèÃÖ­#¿î'·ßT0í‰W!2J”d ɳèñÀ"–¿Å½ð=¸ |Äþr±Ê‚Ä‚ Ópg9¦óô1/å»ûõ$3F8{iMraÃf:oC6]6BµµWQCý"”îÏ"]'nÓïcøò2ìûýrœÂƒZ¹Èºï£k1‘´œŽ#µÃ³éñOƸd×r(¨˜ÇÚñÑ+WØ[4—\~®Aº¬®ÁN‰ZÔÙ“Æu.ÞÍVx]ÀñÞDÙk¿ÈÖCRë¼Íñ@ÍÍÉÄbz±)€e*÷ish(ø» HÛ`¦L Çf¡–Þ61»äæXw¯LHï¡× ô´·e°™óëñái4S¯àC,y¸ù©80…•!&ÿjAîG6”8©U‹.øåUŸ`Ù†ÃlðED_¶À±þD{©ÓSy@Œ¿Jp_«øt¹ð6ƒ[evÌbœ é> ÷åÈ ᵟ­h«ÅCSO²}oa΢8Ü9¶–½O&Âw–° Ã?˜³1½¼ÿá4'G8¨ÓÄYž–k\%éŽÅqœ@úQâ)J¢"üp¥îVfP“@ËÒÊé×››á‘V-M9½Ÿ/z+¾®ˆáyG1¾­.ï´Ûv.èf*¬¬ÊGaYi¢[Û‰«nûáxß@(ýò’gy&D'VãŸFäf§ ©V<ŠÒ÷}áÐîmPÖ0ÄE/X Ôäì8ÿ z.bÝ·ãxízYÅ,Iÿ§oÐAž€¥¢ÚËýåÿº>zÝ8³[„Íèt'‘Cb¤[ö8õwÔä:í·4y“5Q*Lbq)¨Œk†,“ý`½Á•§•!* w 8/9¶ÌŠî5ïiœ•âhÍ!Üi]O¥V ’/ì‘Òîç¸2|ûë4šÞa7šqßR}ÔŠ»g”—0Ïg"p¸(…OÈỹ™¡Wð•hä¬ F£“¹\ÆKü)bÍjb_Ú;(èÅÈæut(k%f²‡ ›¹Üs[0Á% * zv íŸ Ÿ2ÁoTKǦšb°PÍXîŠs ³è“!²KGUÝn ]+BÏM+CýjaX”¬ÊE‡GA¤@0õŠ)ÅU{\ÙsqÈ8þš³ò‰ýöÄHÖ……~ò€¹-³ðú2.ª÷3׺²JÚ’¿ÎÃË)0ùÜ(Fß\N—ûü¥æ.ú8Ãa,ÍpÁŸ5ŽìéUòCnK8…èØ1“td_ÆŽ|Inýä=_ Ïί0 ª_‘HÏ©0Ô´Š;¿ãŒõ8Âe†8~6vÀ1frdò¬$”Ú€~*€t äôîÆÖǯ`ô@¥}’øçÕ9£Â¶,?Ÿ8Äc•ü`ÿ·¾*@­ Ø}%·îèj2ã˜Éí&Ò¾id;(°k#pCá„‚Ilà‘=ó™B<™<ÓŒëZ©KßI„+æŽSŒ Tt)°÷>oHå6Ï_K’­o›ÕøããÈžfǪ3<ØúšDF)‡$÷–€”ÜdèùrŠŸS*A~ØGÒήT ²Å\àO7|µ·­›Ѥx±¾ñ kÊxÄI¼«„ŒpVòB솿knÓ_]ðo^~Žòd½MÅÐ^;švñøpqô•2Y÷nþ«·ùøH`:‰o¸»öjaÑMxë÷ w ¾ƒ–U*$yT£¦¢‹S±`Iì>“ ‘GB¹éÁ–ìå“`¶bØzÏgtmd}tøºœþH_埆4I1¦á AV[çãOÍÌ'%žÛºò>L‰]À©<¿n/ç’ +,ò×ðç0NùŽMžˆ3u€rÈFKñðâX ˆÏHÄ´Ìu,w”®qfÞvDHR¬Y=¿AÃsRn¬þJwŠÂ Ë—P]´fì2ç}¸öÜs­À+U}õSu<å0†[½Ü ’½2ñ þÆ”ûéLµ8[‡ÃÉÁ±*dØmÛD’ËuØ^±…?ºü’áÕ0êê”rñ‹pã> pH†eƒfMüU]«ùC[â·Ó¼ªKwÑÌ=’µŸUcYM”ýxJýØ"Ñ“ø9.Væ(²(‘+è¦Öë6v㺞hn{ çŸÏ…ÌûGøƒÂPkI4 Ï:ÀŒÒéý×Qªë 9à&ù"È=7{ˆA ÄiUc,ž`žäûÀ&”ºýRM‰N‡7™¿&Þšˆâ~Ù4¸¥òS׈áw¥žÃ‚Œr( Í…®‹9åðç`þe)>9 šµŠÈîn†ôÜ0ÈZ1ÚšàÒÜyðFV‹¹ù¬f^ ªõð[³¹IÇ´Y²ÊW.:T•,ô”% ê§@eŸ9ƒ¦ ظê*¿s8ŽkóD³ˆ`òM®{Á¼Q·“+ôú÷ alÔø6¼|þÞÀ­P”Ú½÷K5ãg(û¤r¾K±kâ‡àõ-öõ€3Kk£ïãÈŽH¬+âž6À jƒ’Ï< Ý¡gêñÆþ£ÜYߣ|ñÚEÌO fïãODí·"ìXPY˜& üžçêK,uø ]Ü ?¿ÞçökšŒâýäü´¿èÆ¿‚/=U‰…˜%¹v”è [°ÛÖÔ¶z9^ù¿|®”© S Ö¸jÂ\Â-4j<7qx2D.G©ÅcLPk:›<‰Ô˜YâÊx72:ž'ÐPK›>C‰¢Sðeæö¡x:ù®hKn¬Rfô ™wT:1Ù>ž*Ç‘C¾‰ÌϤSãÆ}a{Ðl×b^ÜÅéeóXºà1ô‹½ÄÞýUb“v‰Y'ò5µŽºô[òoVÌ E|qÖ:‰„ß}…Ù*Rì¢ü7^£ô7/»„“ËÙGgDÁŸŒ‰PvX“^; $ž†’dQì4gœÒDXUÅ “9gê0žˆkIË_ƒUdQÆ(ævã¾[*=½ÝåÊ`ó€s°æß9‰Z*ÃnÕF±^ñ}˜pÜ ÈÙVTAÍ6ñXñ¬k°š¼Âø·FÌ_%ä éroÍ40æ· ^웋gêV“+‰rt}~6ŒØ³ê®6~¾e…nl‡ô¸]Ì 4“¿À"ˆ­ú»YGÕRv%NœõG;à}) ÑÇj˜Ühß’¯wÂ^ñ¯œêÕ#D]`@,íNº ïë7û¹Çq™£"gžl†¾ë¼ ™¼‘ŒIAþ¹}]À[#ÃVâŒð¬Á;kàÉŽpîV÷?^ëÕǼ쒥(û^Æ'p‡ø³áwËÒlc–’ƒÖAãÑ#é2,-sdû»Í©–D8Ó®è3“D<ð–Ø\¡Å^ÃܲšµdnÀÀ•›É›±°Lb?6NO²˜±1)ÀWù4€]ÞÇÁÛóvú¢¦®ã­œ±Ÿ|9®Æ'_C¬#»Ö,‹é õèÖ µÛÏ o϶Ê5–ê5`?½ÜXÀͧ¼ÚÁ“°Òýˆ½WÇpÓ`PPwvé»_-¨Æµƒ“Åt|QèŒnã`‘µ,™¤; %Èžä‹4&à Ç3Nç‹ Äòîì@ƒú(nWï]1ÿÎ@‡Aî[r!ìŠA» …:v?Ž J‡e‡ñy  mÇ{/·^^­K?­™ÆmüËßd;l%w6N'ÏBÉñppÕIügN>¤MÂRˆÈ”„iÏRPÿ—­Çßí?MÜß—VÁV%¶Ô7g .a½Ÿ9¾•$ûÏÝÍçxÌáo;e"ïzb"÷°Á ÅtŸv±pÔ'cã,ÙÔÄû¼‹N1¼Ö îXªÌK¬.à­ÿòuïp)ëÖ¡ôŠ£D£¬Ÿ/ÑóþÉ\…ƒ&¹]x…­’ ¿æ"‚oC1î]×$r ""mˆ‡[ Yj× ‘¶0ªÃî8O¡Q¸÷qñð‘#ÅMš$Lß‹sq&òÕÑàÅ~ú™0åe# ±ªêW(“Æ;IÒ¿¹x}5†ñG1Tä2ŒXÇã•;=8g~kYukË‘~ËÕ"y9ìc´‘(YÞŽ{é7EÜwÑ“_•`¦gT˜žÞŒ;½€îr&BäÄåá+ñðw_| ÛÏùT/àc\ 6<²Ã—‡ôˆ¼ «(pæv]ËÃþœHrF{Šï? ­eÙ—ïÀ÷°ùK”9ãM³YÏ"Ifnud¾ÕÅÅót¥ûx¶æ¶/™>±˜Í {§ñwÈUÜé7„—~ç‚®§|ªÞOI ZZ‡±ÿ¹ìð¸XÆÿ“ŠÍkhè–yì‡=áßwc_¯þäuŽKÂ5·ŸƒÛ¾#™4ެò/âaý^²A(sž¬f6[x3®À ƒÕìÓíý쎑yœ28z½Ò¤àEIx×{p«\ȱº—ì·×F°ÿð„NòHÁåvyg7çþ†áùE˜'.‹ßżq›p HtëÂô¸— ,%È~mœ·Þ>¡2›sPáD :ê°Í*ȳê¡õ÷õ°ôpÝlJÖœý¿/y©^!ºîµÃu o íÀa;I˲ة/ÜDŸulK­ܘu˱$[8×"|¶ÎEÛ™BÎEt7ŒqÆføƒY0}åã°øÌQ\"Ù¨vÖ g.ÎÄ7™YpÍzŒ~ò”vv.ÓêL¸6Êíͬ… nÀ{–y ¯Øö^F‰¼M}…«}|påÙðÑÌlÝÊñ½’:ç3…ñÍ.®÷©P,EÄÄ9Øö•?xï$ çÓ:G1@˜š­Mϸ¼h€ì+¹îûrLoÊwðR•Ë:œ>¶—Õ5ƒÝm)²ä÷*’dÑ¿…ØÍ¡jèèi¡?ˆ‚Š”ùv} lÚ.uö‘].Âòg½ÄY%˜ªêê¾í6wh¡.›{þ3N›×ÇùM.…3†¬¤d5jß"2—2ɶ‹×àÍÆUL¯Ä™¥×p;ÎL`¦Nn¬²³KõAÿ†2¼TEdt¬IÜcäš6iØÉqÎ3¥Ø­×õ¬sR»–“ {¿ÂsÇL"ï)EjÇl#L¢f?I†=Wps ;·Oüô%ã}ÏàkÝ{ñ†€1œÿ±†?w²z/†—Ô«Nž¥3ûÑÆÊ`ÞVêÑ‘Mß x·2'IvpþÞEû¨Ÿœ¢ô,øoŽA/Š9RÖðt½*©œüŽÛu ÊŒ[HC®àÚ Àuµ%Û²j€‹€26í\`Ñ|4³Z®ò-°cþ ¬•„8I>úL“cËÛðà)cFqtúÈyû]5e1LÝ&‚ óG†èZø°;©¶Iž%-íw|5¯œ&×ÐÙ µÞsPJʈMåÒÿóKãn«|.ù·1xnùÉ©i“¡Ü3ÜÞú;ðséu|0¾ƒcc\á¹™'Ë=Eµìcp·s8¨C6ß2'^¹¯À¡ù.=ˆwSíðÒ]òbû{¼ü"Ý8 †«IŠa”Ž{ ›„]AMf?þ²Ì¦‚§ë 1ö üQÙŽCÛÉN×à¶j#\2"k„Áplˆ¸ ¯~±]SÙ¾†lrØz8ž÷‘¦˜sëŽü%ë>¿Cç¨I ¼u#‰ÙáµÄKt kM§Æ Ù>¯4fWAŸ{¶pSËTY‹ãR¨¤=ÜÓ¬ïøEvlh‘`'ìŸÑÙbðs¦´nÛEf*h£ª…îß™M¶]ɆÐÕHï‡Áâ»íøçÛ ÷õdv1àsó,Ô}M~I×ó߬Š!'”]ÿGÄ›@åø}ïÃÍšç¹Di B¢zÎ>!I3”LÉKh.¥J’TŠ õœ}ˆ’1BB’2%3‘âíó×·ß³Vë^õÜëœsÝ{º®{ívu›Öióð‚Ú t¾W¤Ð¦5„÷¯s%ù§õôæâJp·V¤KK~ÁÚ5¯Ù¿WÿXô8{¡V‚6Ýx»ujßÒô7ôŸ—=é=…º*Ýýºk2¾ÙÍôFÃÉäT"ÿCUƒËáÓ­C\õóIlªD÷V™Ðˆ5âÔCÆ„Kн‚ºÞð$æ"Ø®ˆÁ§%Çá¹¶3]Ù>†·¢öœÌlü-Á_\¿ÃþÌïÇlç3Œ–€«v8iºIèäñÑ€†ñbÙH>ÝÎ Ùe~\݇ºžPb{búëÊ‘\~½¾چΤÇþb–L¸ô™(ÞÙi@Ÿ~ãÿ͈¾Ý÷þ$$rÉQ?‰Eð>4S“ž¼²œ?oVƒÊ%¯Ñ:* <.$ÐÌ’‰ë7aÎlyö´é í$DÏŸ‹nG™É2ÜiS+ÄkƒG§:Ö ¤îWÐ_ÝÅBæÊð;—Áþ… \Cç6ÛfŽ&­Ÿ…îÏ%á†Ü8lcéyÏðp„¹ë®JwÚk•F’·iÿ@tÁPŽ«+—‚æI–ëQŠ·Å‘Œ+²xXF‘ê„ß…ŽsŠ4îø¶ÚRM3 ,.aé´ý¾k·zþ ÞD®-3¦øo:8” !;f!äMàZGMaqËd~ÿêp_ñ÷o}Í®-=¿sn¡ÌÜÜR¤ÉoöÂD©z,6YDí¶²œ~Žv·÷†¤diu),YÈš¥ýpû*-þçÆ z%c‰pÄ$ð°ÕâF¡ƒ¹Hÿ§ý^1´ÊGàqL¿wé9qêØ½'J›ò}W ñèM?Aø‡Xüó š`„?aþdV½÷yæQ.¯f•*™Ð9f2ŽÊÆÍÄôfÏCÿ#dnýqUµ¹-ìmÖRt^îç U0paˆ¤Çô»±LØÜŽ»få ûï!¿È'¯[ønÛ|´ ç9VÉøþï~2+Ñɺ» §O©(kÆç#×£ìCijžMK\qo\ÿu•oJ˜†áÛTI—Q ‘¨¯ƒ®ÊD\QoÎ!G)$脳sN“0éÆ0ž÷νý>ÔLT ƒTFSéqD^jH*óh™Q8ñ¡!Q¯œþÁ‚b’t3*Z;aŽ­$_]ÖÁR&]Ç­Ÿ×’ôîû$yD$ºù’ñß•ù#žìøÎôÿöóp]®íV¡GuxË¥TLôàú)*Ü|Sš}fï Á½EŒ6iZr÷Ó“a”ûxˆ™|‰7pä ¿¾#ì".¸©Äýôœ©Iñ6š=XœÍñ¦¶£¬øËÝhõK™LóÜöMõ€ÎìQ .'ÉÓ^œä¬ÂЪ,1}Õº óâ,,ƒ®Èi€×–ð5>úBAý/bÚ"E«)£·é80ø8>®ªÿݹ°Feíh¹ŒoŠdpNá ~®cKÑÖÅùg¾$ÌDÿ*=_Ád(;L5=bè­Oƒ©Å½ø$»î^Îæ.äÒÑ[qþ‹2{ªyûȉo[–‚ÅSÈ^zœï[ÎRðÝì?¸Òò Y¿†^KXO4¦âãS’<ÜE”×­ºI„™ÍøüÍP诃&¢•¬Þa (–¦[’’ÉϱŽ1ÀÃ{}Y_J;2Jþ7 ‹}Py†½‚Gµ‹pƘgù»ÞóÛÄù1c(™ OF÷²S·wâµètòÅsóà:<ÕÌ'FWÂܹÔHµ Fðþ5¸áÖE$ û(ó1Ü{²EÈ-»xj»=†9’²ËàÂ7-áè»BEÉ3Õ¦@Gî„-A÷Ñwy2õ}’‚މ÷˜Á!OrN]–™«&Á–ßÉðƒ—@äÿÿDìi}ÅTôƒ‰n⣥E0Ýâ!Œtß >™€DÊœõ¾ºFdÿ×/.æ¼G¶‡-T†h™ãð¢Ô ŽWkÁû#wAóŽ\¿>='T8×âýñ5ßJšøÙ¾ƒŽ!{Ác_;hÅ]S*viqóÅÁÀ2+A\"–®×Æe\¿>Õ­µ?ͼì PA¨Mà=Ü21-W«õ?Kxoý/¬ðy‰†‰øB¦¿–/2‡û¦‚Þccüž†ñScðÍ‹e¸Õü—0%ð¾|*ç¥ñ YþbaŒÕiË]*¦> ÿ‡?Û®|­>Êߒi!„ÔiÑUå> ¥½…È„Ks£çn°ynQúz®úÝ€£^6ít­pŒùÁÜÅZ¨ðc$,Ò…qRIxþ½ŒžwPØUÈ1çg#é|‡O¿ÝÆ¥Òz|Ðu}îqªïºb‚–ݱ-ÛÖ¿GqMYhŒ;ŽoVUA.¨sÙ¬±‚™2=x|c0n¯YP-~44E«Ðn}XS UÂ.œYC¢û¶Ák#M,ûÙo³Ýâ\3U¨ó¡wC«óÑÙe-(~Ašô™ÅàoÝ©üƒš.ÍI$¥µ¨Ä¬Üüg̳e“HîssjàïKgÄï /«Œ¹_G̼޾‹¸ùÈ,Mû„·ò…öç˜øõÇå&/ ëjÄê artÎ&`<¬—¤ Ñä;Öõ ‚]=Îò‘*Ç•²Eq3Q9Îyå-Ü<á„Mß5ѕ䰭$*ÞÙ Ë¿mâªWò¡SÛ¡ƒáÀílù¨­<ü© ·Tñá;߫ӵ`{[‡=mƒJÖ¡tä<>ßžö%Oâ]“¢èá…po{gUâÅðÿz»¹òª°SoF™’tǺm¸K#îü)eå>Š|Žp?ùþu †cå`y<¿ò Ó^EÌ$ àØ•_ìtÇ|[ÐAÚê´éÅ}¸pè ðߦÃ'YǾU±%ë*áÔŸÅ´ýt´`zÕP”´ 'VŠò)¶mx:¢d‚âÕÄ·d³m©5Ø `9AæyIãí=™ÐýËž58­§ôòá2"v´G×.8ú”Rå±ÛpJU5‘1“ä÷{íyègeæªNLIÕYN0ïu!zø†M¨òþdKÐÞ-%—AVär}áwô&çÅESW¡áxq¸è9’üÑaÿÃ邏ƒ&¥9ÇÑ^ð|Ó2Á ý2´Çh3~OAãï%½j–tyœ&/Û' ¿Þ®åG”x­”3¯¶ßÉß’Xuó|q8Âã/Ç­ ñ²[Î醣ֽc/S‚Ñ4‡6®…ß¿ÒÑåe>1{xþ~_Hu¾,Àü'ÚüÆá¡°¨| 3mŸßÁÇ×ï Ân,ºŒ×hà"Ó2´:TO’åðxŽ]’NöÜ}Šâ_ ØÃɰîÙœî)\òƒnàÛkF|¢Ÿ&½>ô›ÀFS_ºËãÇG?Ñ ˆÅx}'å…êŸ÷ ä?½Òodœ¯–@&dX:Çð%Z²üKÅCöæƒ!}³Õž+O;»ü-£Ï‰Q§ÊhR~p¿<èÛµ^ºt”ûùÁuwpÔ§"¬3MDeÜùÑ÷ZøP2}®Ý#÷ऺñ0Ç·™ÈÞ‹abÏ Èᤩ­æöó¸GgÔÒÐâB‘Ó(úm úâä”*‘ÿÎ$šiHwŒ ‡÷)kÈ¿÷æøö¢”C7>X6ž~6”Äu«€å8eÚ¯Ô`VÔ9œtwŒ=cÀ²cÌ©YÊTÌ8ù>6ùòwá5°D+F-‰Ó#¿ Ø^…)$>Ne m°ß®vÞ º%r<_Y«:ðöðA4·è "ÈØxÔMKöV'þý@4³Ú%EOMÍ7œqWxÀ~»&âKý[p}žÈ–¯ÄÆ£#0¡Í–:Ë)ƒÔ¨|ÖEó¶®ÿ†WLFÑWlñGÅ1¼= WƨOÎOÉ—Aš\wøâx>T]ŒøËgaîÙ ˜-âGõF:ƒ…µ ·?† , š}õilŒÝØb+¥èÍÛ}p«aH™BžœOgSõwcyÞ°š!p]xðÇPzw§-ú-Rú†}±ò¢oBÈ3=;øQ­K&:І³iÌê'¸yˆü‡LN³ÿúšež…aê‚!›EQ}Ñ ûÐ 7µqUg8¬î6§¿]z™þð5˜ô½%n]Bz±h©¥Ø—#Ø?è.œí¼!xQ0“:¯šL×MX²oq‰òžåýð¿o,ù&¯?p»o5ßœiÞÌ©Ø5f<\P£ýß¡á³@ÁFYoaØä¿]¶3;m=Ðñ'î §Òl»êéw–-: Æ}úñ_ý5ðñ×§³¦&CàYÇ‘rù´Qq µïÌ•u‡ófÇÿ¨Ž¼í¸E*¿Éãå'Iع¤•Ù=ù„‚ˆ¬|”?ÿm7B‚Ö PI’§›ÎÏâÊŸøFcS~EM ´¡ƒ„1o[kD…á¯Ùwñ•¼éÐ$*i·”½x‹/zÓ^A9ÞØJb—ó7An<ÙS‰¹Ü-fo‚jYʼû¸"'œöíHÄõ/bè¥p1®sG…¯Y= 4â¸îôJðXú:ñýK‚L½¬;7™fE´áÃgÜÐ¥…9ô.Ó–xA\&íáÞi}°fõc;k·?ð‚­ë‰¥ÓG½¥·ôçÍIKhê&Kz¦Wœ{Ì}‚¨œu6³ŠØAûžïãS7»ãU«WX—WH½ôéÿðÜv …yLÏΚ¾ëˆ, ¹‚“‡ˆÓ¨§ ¶Æ œü‹%ÛÔAìøPölzäÓ¬X3˜ÇÿÈÁýî/aÞ> lq€71*HÒã£>¸è¡lÿåà¼m+)=‰ž ½ÀÆ\üÁfUá3¢tÉáÍ{Ðô×7øCË¡Pè4¨Ÿ» œÓߊû®%¤€n¬¿s ÍF§Õ¹ÆügŸ·ê<ÂYÉGéªÍ ÕÓÖ"JÜÚoT®|Ff‰Àu—«ÿëSÆm׆ðÍC#È­H-ÀµõX´N‰Jÿ›KØÃGÄݶ,1¡3–…©sß!ÁÃvÅ¡ÃåUÌãˆÈÌ.V4bè”j´‘†=ð×÷¬ƒÇÏ]´ù[êÙ~Çû›o¾;»Œùƒå"øÊô$®«ƒ÷Ò¾¬hi0©örÄUWÿ0ýÚaýša%è,ÛËÒ3aˆí1 ÉÏ€÷[ƒ$<ÙdÍ*¬½¾žÌž¨ËÁñåfôèíñÕ1OÌåræðÿá<É#*ƒG‘Ê¿‚ó»¾à±4Yâ¬õУÁf3p¾Ð¥?éâ;4I€ÝäÖ]O¼‘Ù¯ÑçaeÎ?'ÿ›C9³= Ò³óáüÒ úß:ÊJW¡Î0Š*-‰ÄËM•$Üc/|ú™Æõ¶}aZ‰|ÊŒ&ðô¬Å-©Æ¬`‡Œñã…‹O£ï¾·è3L;gñç£3…öìôÉ9¸±éò\Ç#>Þd'®.?«6÷›†n„= hu9†ÌÚþ¾]‚‡‡]ðAâûEn0ÿz/)™È7Pás›ßÉ å— þ/zR‘xyÝ&×÷¡ë%Wüj† eû0ïn<ä²ef„ÏŒ°0yH^<KïÅáéœ×à·ø"97Á—ðÝÚ鬕„q¹,j"+ÆÿýR¤šß°YF î’ûþ>›èË£iiò|§kÄxìçt&:Ô‰ßkFǸ%h/1 4UùȰó˜ÿün• Ú­‹™XЊÑò8¦a/˜Ÿƒõ;N“7R»öϦ}SRQãP&½¼!æN÷¥¯–•£ê2]´™N¾OóÎ÷±³É>Ú¹–ÄäÝE…F[¢Øn©ºÿm¯b.=Cç•,?÷ »s¸‘ìMà,SU–·9ñÏOïâ®®Xar¿ÍCˆ-™ž0“ô®A³—vCÈ”:üæØeCŽ]3Ñ}¹ fD¹ÐÁ"q´oÔ'g–œÂÛó•iéâØjP *Á›éÝ %â«Y@>®ý×^òÕ‡ñ5*‹`Y±EÏ+¿n÷ž[–àÆCÏ/ñà³øYPNטßÀägìÈ‚(ÌÉ…k~ídsïW¼N²ð›ÊxlþÊaxß6úY5®(ê燑pñˆ$:¡K_ÔoÆ»4˜acß‚û»Ni„±¥/Ùƒ_Ãø*?<5Γí,‰æ cØrºƒ[ æn$<;uœlûÝëu;Ðn´¹^wÛÅž`lÕsrzX:ŠL9èðöOI¸1ÝŠì/ƒK7È¡yƒÿë7Æ+¢ô¥²“­­ßñ9L«z¦Ú?g›-®o²V•¿a6ò#ù¬™h«Y‚‹ÊPÍýÛyî~MŽ÷šÉÖ5¾(ãº/Š‘Ñ;‚„Ñc·Â‡¬kX31€,QÈ!úÞC9úêÁßÚäË¢y`××ó_/vUÀÍ!°\ÇŽï1¨ÄS“~û¤⻪êÿêÂ+Ä´îœªÆ £ bTˆM6â|fÇyöĨ{ù£ù¸µ² ”^L6¤Î.Æm½‚¾ïFñZñx8þÔŸ§fv¡è»Ô¨çVý: g¢.⇢~'ý‘ÌË&í¤'¬Vâ¨1ìþ|iX¼w$»l UùkøÂéËñ"<ƒ1·}èÅÑVø÷Ã`vÏr*®³?†ë÷%óí+®C’“yîï¾\â‰ÐZa%þîùS*“*oUnwB…*Þ\ÁØ«QÝY&Ô7½MÞ«ðä}Bx´j(9ŒFq‘°ýìSxkÃáüÌs0² †vùÒûÇ>ÜB«ÖÂ)Ñuh0s21šjÒ€lÚX%+3‰ÇÞ²åã¶¹ÒHµ#¸¸f0+(-……Ü$Å«J¡L9ÝZv¡FÜ1¼iYbŠ«ñÜèÖô¸†‡Ó¶oàrÜ.§3Šg+¦/ÝÌ<ò+È+· ðLQÂu%NNstËa{ÇA´r–Eþ6(ŽR‡áÊ1¾ÏÆŽNÆ^™ø®|/ü<)A?X@,õ¬ÐWå1‰VŽûp7û¹BvÛ‡-ñ[ ÍŽv¬ÿwV¸ýÞ*Z†ò?Ïà½ÙÛ† Å/O÷Q㹤áÕÃøoŽþÆ-èXÖL4ާÐabDuÚ_{ºóå†Ç•°±«)4™ ‡‚ð5°ùÄsÁëÙ]äVÀ^˜PFËålAÍt^qdÎJû±ùøöþw¶ä¹rÁRá+”Úòå—¤£§´;=¿Õ˜ï뜫ž æ£tRùãô!4ç ÚËð¤3ƒÕ÷Ž`×ÃE»êðõ…˜?1žÝ0>Ñ¥§]6÷™štþöJœ¨Ï¦+¿ £üJY[ï;’dãÌßéfÀ+!ØlLÂ÷¥Si¯×ÒßW’xÂ`Y~äÅ›þwzÙ;rëêŸêôàËØÕ_£6Ï=J²¿ÉðáÚ½P:üšÚ_÷êŸ,ãŽ×?ºš=YˆR;·€nÌI¬ï׿êi2ü¿~æ‚QcxèЇdÁi¸»"ÿt…‘“YtåI.žý½èOs&‘½~¼wÍ¡Aƒ)5±éÏ Íštôä#Daƒ(ÿfYõîyì¥íEW7ü]Ë^ÛŨÏ` ôáWO«¿£¶ Ó1æß®?'êçqCùì2ˆ¼‡xì¼½«s°e¦6¬X™µ…oÈ‹©ؽ€žªÏ}“ñ£+OcÃöÿ{ÿqÊ«œ|É«ãìã¬Èg/RVu~µÌŒoœnË•€KùÜ÷Ó¶dg‘¦æ`*£ÈéKœùÌ„¯íÏQAûó`³Ë?¬Üx×u) ÅŽ ޝîJ¾¢ž×>2*³?v >øI8%.|g3?wô>¡%#Â÷ S×= Mãp¿â6ºK1ƒE.Lfª‘]Dúð*Ú8é¿ßL!t{ì¾ö›ØþŽÞß}ùciúËX í='Ó› ÖAÿ™0Ûó UÓ~‰±i©x9è]‡Ñ¸xf2®¿E¶å¢ÆÜ¸ºDø?ü*­Äf° ý { 2öHðÚ¬zn•¯~geèoe=ž¡éAOމêçhr|¥÷lðP·£Å}÷Iæh]>k9eŽÜÉwCúßœg2%?’Wˆ¶@ûPòwÔJaûZ*_4”ž)÷¡2ïa¥Ÿ=]Û=–É\[À÷«„ái™t^¬u˜}3O‡7÷/@»ó-@ǘ>Äü tg”ÁUå)(á—¦_¡“£&‘ƒï€u_L¡\Ö5’žó¿KêwKPÚ©þAGñ|Às­±ïªâyoƒÂ¬ÜþÏ–‡~c3tvà{Qž?Ãx€ÿŠHÇ›äÑèÙLluá'W7’ù"¸½…ÞÞy ·¼êÄñ»ž€¾lÉ $Ê·spÊŠ?BWéeB§áãÀiÏ~R~OßBÛÕ’@×®î_n…ßgÓÖ„á|Xå48ò{+žqêv‹ˆÐ ‘’`¸r{¿Ò ÃOUâ÷ ×ÙêÅÆDäÒkL©ÍVü §ƒÆ&৤ï˜g0š»¨ºð‘2/á»›o±ÙÆÊˆ_¼ý7dÆþýýÑg䆢G_(—U(Èp™Í\[¢»r¯Ø…{ ìùÖ VH¤@ó|]ü,{i^Æžvíø8®–ÈÉØÀ³š èQÎl÷Íå·ä2`®I"ø D¨šl>¼5tD÷é¡ÐèBo\ßAG„uàÂíxÒ²Žî¢T"v»åPfw±ôÆw`ãfcdvÃÄŠB“ÚÀ½½M¸mQ ØEÉÐo™Õ¸l•ÏÏ= ê½QÐ|¾tïÇCfftÊ©{öiîþÅøÏk`(7 fl±à мìµ%÷1Äir·ñ¢ ›Yê1(ë,ÀyHñVÑêÙã¬lÃy(Mµa—§ùR#_Xfý•>m¼ ¡Û–܇ayÄ5Ywàý—ýø>Ö0u;Hzà¥øhZí,AÌIGÝ* ò[yüwµ>ô2‡éؼM—K}Z ŸßÇÒh3gÞÎ`ÕA ëW¦bά‘¸p¸°R!†·¤Æ8=¼³Š{.øí.a¨j½„‡„/GÛ‚S|çG²t˜ ž WÆÁ­”ùÜ'û(¾l÷f˶^‡ ×4©ø‹õøo†'•Z-ËGTŒ[Ëq ;ºv’æåê4ÕÛ†–õ‘Û/sðòî$&¯´’;ŒîÏ?«þ°+SÁŸùàÉDÿØ>êâia¦3¿“ñNS?v¢6ã0úº¥ÂŸGñ˜ >j€ÿL/²=ÂaÍý ˜>Ž‚)È]LÇf ´ÎºØÃvÐ×LÀ»Áátœú_v}ÌvVbŽ0o»)°E·ÁAf"z=Ãò·b­,d-%ŽÉ+À¬ lâ'ÓÅã(qö;Z¿VåÍaí‚/qoÈò„°Ä³ÂÙ÷²Ó”ÛØóŠ!Üub*œœ F?uã7&x0×Uz¨÷eõ‘#ôÔr_æ¾à5˜üØÆJĹØÚ0ìéÑ£þâÇ`á~IO“N­ý&èî’gú?WЛ݋`­ô.\Ú\¢Í6¶0ô×gæã"N)tÃñI8àÿß×="«o—Y«ŠÌš6'5‹Pã] hÚ/SH¬z‰ùÎ%‚ë¹+i£Þv>iJYÇA°X+|ÿƒl ©ÜÞkõâh弆þÛ¸<‹ž`Ö¦»°fË2n´­VªÑIN“ù‘CYdϤ(¯çD'Av·ʸ +D-èå›x7ñ1Ñ8]öÜâú'ðóX† 6G¡dËiÜFJ•Ê`và%±ÙµÖ¼…Jy zßHFæ]±7QýÛ"Þ0¥²›ÑüÒ0Ô)ÜÀåÆlÄð½i ’Û9 š¶û8Ý{zm þŸÞ¬¹¥Ë0àµ}nªM/-zЦÿòÑâ³,Y,ÆåVÞ,=:ˆÞ’ˆ'º¼"ü6:‹tà„h†x¯ÕûÒk‚dÑcöëÐR|ȼ_?å3ÙÛÚ0*t?;ùf ÷ÿZ 9™û6?!¢.Lpóš l#Ù¿ÃqÆ¢,¶:î(ôïÉnmÅsšràäÊ2èˆo ÓšXÝÜ·ÎŒ99ÎÅØ’!ðÖA@K{a„ü¥úÒ‹qh]…_B«[Î\Õê>¤î,ƸÕðõˆCL`+JìÅqšÁ¢Â£`7Ca¿ÑïdÌþ;ì0§ GëÑ-ò…Ï ØfÅ‘4_ê( è¼›§¯¤ÙrGÀ­*ôY žÞ8~·¿inÃп¬æfŃïn9ºx‹—’û†æsÿkù0Mì/l´9Œ—N÷â=Å3 üáìxQF¬»£»@íÆ2}ñ!œ­“ Þ.»ÉÏ6hP£Džš½ûìxíD+S÷£Mi»°Î.Ë,¾0C'0}“ƒÇÁYoäñø;,Û¾þëiVþvâ¿yÖ˜§X y¢<ÖXЉ¥‡YÁûõ´c»lŠÊÇöÁ8ïí«þ¿áH‘d×±ãa~f%û¯z«±«N¥Çª‚ä‚d:v&}¼q> ÆmÆ©½À<öcÒ9z50ƒ Æýr……XºL‡K^y‡"§p6iÆ'y‚Ä[è6ùç·Ê²¢«õ|~œ,½©) û²7@éj¶©Õ™×M¦"üÐ4 dÝ]˜ô¬¹ý#—TÃÚG!Eù¤µû jè‚@Ç÷Œ+<#‡d#¹QóW8 úüÒ²|Q†µa+wøÂ¢ôÜyæµ¼ü_ZO®Œi†?óçq•€£tí7i|ÚÞ[]B¢«KïñL•‹h£ÌëÛŽÁç} }kÝþ,ŸéÄ[Óê¤\|J8ô­£{Â|ñv’-òŠV“uèµgftó  öääÒÀžÜT¯³þ´c~Æü}×hzc¸ÍyšIÞl)fÛVÁÑÍIdâ?ü+¢Oeçrµɘ37=O•°<×qäåé© *Ç3¿žC£Ú^![ûÞ í†|\õUœGxT©¾ŠÎoƒ»Ç(sjŒÀjäL8˜áFe^¢”T¬“ù£ûñÓ«iHŸ \m Q¼~Ø@ü°A¤ŠÄ”Wâ·5è™ô‹oÙñ¡VkéQÏ´¸h]µg9ÜŸ­ÍwæD°núrŽ=ï™QÇ™Ò ê{áG©>ÿ¡ÇØøº‹¤núLòßüçø/ЬŠuá”.sze¼Š…Û{¸é‡7XýxÜY·£5ñJ{[þþT•ðúŠhinHmšgðO—¹ã·\ûæ]ÏéʴtõB1ÚÒ(NÿØ}ƒûƒ7qÙ§OØ]›,*PLq,PÊÅü×õpÂ"…Eî¾N´ÎÍûÄfâFòd¯ ‹ñ#%;U…B)3XS¼&,1°ÿ +âØ¹“u£°¿`&o«š »iûp9.®È·Ç ‰éYÂÿ}ØÄl3¹ø²¥,°^Ÿ~[5¥+uÀ)^ž¿Úm‚åÖ8ZÙ„ßÞŽù7-áåúo bVŠ¡ÚŠ¼AQ(êi̯Ï[CŸl%Ê{aÎ=ÂJÁ@ãû`ú¼H6Æø4¹îãg”µÁöqáÃuBëÕ‡@"¦·æ×,¦ÓÏßÞ‚˜› 3Nå´b2«€#7äé5‡ AñV>ô­‘“kØJ817šÖš¹ÁÁµ“1ùl-Ç>¸}Vì{ËΞG§‹ÀæãZ8æº`ëie^ùJ–ËþiÇ›N´îà4>îú$üÓÂ5wãnÅ‘ì·Øhê6A‰ßÕÔ¢!ošqkù:ë€Ýri†æ© §šþ„˜Øe<â•*·ï› ?´ñûqƒêWpñú êéÂÎõiÐ<Ë$}¯°µbtöî\¸‘ä½'$雺ô„é0ô¨U‚Z¿k"K÷»^`Oê¥øœ~úòü½Õ;.@½}{ß>|œƒ3Ê5°÷í>Xª÷ˆm$ •~Qäà~,ÁrVáñÓAºf&|‰^‰…·«WT ø°S!i6™K4RP! ©ûüþ‘"’´D ©¬‚È~ÿN}kÊ6^ÝK6_ó yóú¹¡˜%Ì"Ò9Íð(´o,g_L×Ð.ÕRvÝ»4l¸Àj'®ís1;oΛ® óÛo >lxET?;¢iùZ /“ÿG˜ó/YÐV²DÿëßÑqx$üósÄ)ì÷Æ^ˆWOü ù²íg…CD9SŽƒD‰põôJXû}üUB‡91h<Ø.íwâ?ß_Àÿö ´éȘ]Ø÷¬ï¿MDCWEx2¿ tœŸ §àŸý^ÿ>ö‡*CضޖîLsáÓ&Qõ,’h2Æ^“æ*7F;ê>†³—fãótiUŠXWAk±7=>Shc£*ˆŸÂ¡ò È;² ^a4­¯Íîm±‚s3¥xäÝoPfÝ쑼޿¯ÅÂ}øÙ}9œÿÜKníaß»«ÙÖÁ{Ðûy$.š¬¥;ÉœŸÐ«´„,ÜtE&éó>{%j/¡AÍe[Èœ75Øš~‡ˆö9ò5s³`m½|§Uí_AÈã¸5¬Tê®Xù'þG`ÛgŽ£[Çl¥‘®úwþª{­îgqÑ·@¼å°CC¾ƒéÛbðë›/Ð\ÎŒº«Š¨$U‹[çænFÀfÁ~š}ft¢®%Ÿó©šf–’ÖMlÒzoÜx€¿:]§OoÍž‚Åå ¹ìŸ`ñw{˜ëTN,÷g‘Ð- op-¸V›õm¡lB!LÓ¿JV[ïÁÒÌTw¶>Bkn:¡ÍÅ ìÔõ§ywöÁ6÷<,žG´ãdxðì†aMtcËWá¹àýäöXìߊ®Ú@Š¿57ZÀÞº«Ò7GVò WªQdoêÿ{Ø Ù¤E®øªó++L…µCù¤öx´“6 ÆßòˆÁ51:Êc=NŸ±nœ>3’á•€N8¨1Ãu¼ÃZÖ2'§?„_,áÝÜTødñ‡5.Ý+§²´±ä‚T 6¬²‚éÁ{˜29inÆ>팃«±ãÐÍ®’|/BÜš.÷ŽœÙgÍæ­•á;ü†ÿˆÅ¦v}ªz"‹’çð„éPRïAÍm2Hèö-°Þ,æéKò¨kC hsŽð_—*,\« ÷à©;0+ÇåÏ9Áhý"¡å˜T.ûc%»´|þÿK;ì‰ã¼>2C­Ø•u åOU£âñÃø¯Î‹~r+vkÓŸ6à¯ð‹°´ý¨dã3™ÓM.l^G;Ø4 A«&2^;PªŸ¬P¼Ò4g‡üÁAm[ÿ›çŒ¦û>Áßœ}xpªõþ%#o›òssVbRš[øÍ6æÎ€¯‹ÍhGú=øæ«AgyÇB½C1†”EÒÔŽ²dø’wOIhõ5ˆ{î>oß/ä&X£µˆnr¾3Ú+akk*VŒCß„zbf÷P'×P-ººò‚À!d 4©£ÜW~áy&ÙzC±iñ@üç–áòcìŽh Øb¥×\pCÀH˜¥ÅuOˆñO×kàÃÕD"v!δn'|Á:: É—µ?áïu`©>Åà÷„‰¸Dj4^XØ„½CR1h§6½P»7ßõ¡ëÇÙrµ¿ Á5ï&†™‡»ÛË’éØ0ªW}ˆÅê'8pÔ•N»s<¾Á=¥Çx¦ý-sÔòg¶Îl»éTYn„)jÍ`÷ÖFÙâ‰'.“)I‡!£ã$iº%Íç»ÈAµâTþ78‰é„‘ÿoFõJ—Atý‚(vï¸&z.NÁJ3™ÔŠqö== ›§ü&«Ô¼x³ô ”šWJš ðäk„ÍC¤yÌ¢ÍDÒânømAË®å—ÝcžJ•lv¨»¼f2´ü,ô}:ˆ·íû Ò#éþ?ÉxLüV¿ÖäËadƒÍî×M=íJ0ãÆiôïþ.PûR/Lh¡±úÃÀÀ7™äª-#©á›!³ð®¢kð“i±ðzß`ôø°/;&‘¥^Ø++Kö*Ñ*ÏrÌ®„(ý]Y[<™_ËaÖ•í òôz_(IMÄÓ6ƒûæ¦ù¡ßüã8ß0©ö"|`«G2ç9BJà”û÷Lü-”Ûq³êÃ`šš¹J÷¦0ñ÷Õý¤øöˆx½B +¿›âÄïR´z.#fKâÙK9sY„›ë²Ðm\‹ÞõèRó²)Ôãïv¼".ÉçNÚiò{Ià`3XùP’fKÑà ëáÄú¿¶<ÇÀÕæ°~®/vÈÅÇeù§Ï t¢_¢Ð:µ™]qM­è{°h%Ðú‡L3õ£+‚@ÖÏ?î[¥g$ù¯€ðØïŸaÙÌ®î7eXó+!Z<çµÆ:€ýBˆzï+ËÑéØñE„o_èÎòÕ$à™ñ«ü^¹æÍ¢³V·7äC΂h²ª¾’éï3 Ÿ¬¥QG}1Þ’>»¤ÿÁúNìØË|ó„ hjLÛ¾–€›þY‘2¨å¸äT&²œÎý>ÔáÀ^¡Ï| ~p¨!­5uåòË!L,¶×b¥• 1FƒîtràÏ®` ` Ï´èäCçÉSIüùÖ…*Ô=‡óâ°rpd(³òź|}F»©—L´³¨æg,ööðÿ°ÍO…RRÚøei…`S‹½>©œrÕxd[æÍÀ*Å89w>u”‚æ ZÐYr×=¾„mùÇqd”ýC÷­p}^ÛûÜþ¹ºÒ›/g£Úëx³É‰žë5 ß׬%~Ã[‰aá%6l¬Ú, B³Ò×(îj ·ýk‰Þ76BÝ…¾ Ëc–äÂmõDTŸOÈh4›t¤Úîª- ŸƒZç¦ã÷!¥˜GP懔)ó´Ÿj´Òw5Ú*lG·@{î> ø›–üOWãÉJÛ—oËÓpÞßžü×Wl·ß•?xÑFîþþ ãjZÈŽEbô£u3Ä9†Wâ°)a𠟯)šÄ ܳcB¶°ò XðîÏLÿ¨3ج¯sãg½‚aÛWì­M`uƯpún-~o§6ˆCÑ€ýeçï"O¾¿$ßcšÐèè ^è7sS?*ƒváßÏp=ài/!²)æKÒq‰PÀ¯[?š(!npúæŠñÐ>WXýi…Y»UGäÊÁ§byþpR!4ápl¿™ÏNL/çuã鋱GON_”åÄH ‡]ÁÓë‡r›6´³—žµ!ŠCx)W Â?AÌg%”ÚÉb*FÓo¥Õ¶·eiyÜCÈy„¾µ³‘ŠôÛ `ªÝ Vã~ ¨0lõt &§‘Cã!¥v÷~£ÏÇÅÈÑ&[3:þ|3üñ*aÇ¿¥ ä¿»¾@zÎ\|•ðØ{KÇÏÏO®!§¾†'šp¹z ~Ü{ Üó-hèÌOlé¢áLJÒà®tZ*³ åõÁZQœV{èqÑC»P«b=¶§\fÆÛ"™·ý(\’s Ïÿ¼ô(Ðe’£y‚·$NË©¡ÕËTù!§6X“[ž¼÷˹Ìd|šøå n‹ï‡Á‡+1kÁFš¢ÞbîAä¯÷JaØLŽ›¼Òþóp_ó\Tïçù+a²Ñ¬4[CÍ¢øOä|¼=l5§ç¤ãzFæùŽ…W|¿œÌxÿ7š‰’©åËÑqåeyð.<é×wSn½ó‘b|jF;¨Dï²Ö…½;¶2¹9¼09“ûÐ!̹–O¬·&×À£¥5°[95«‘ü ¹€úuÕĤà±Z(ËG¢Ì†p¹\_í?ŒÑu°¸_‹,îÓµ<ÈyW9' + Êq^ÀXðÊp¥Çfï¦mij®J ŸÆ®D>l.>ñF #[œ;꟠ð·#|l¾ %#†P‘ýÛÐæCTUŽóÌé°Oì;9?„òÏy°¼$ŒÞªÂê£Æ¼m|LûeO÷U¢nèz·>g®jc«VsÓÉB˜[›#\ñPž›ÞIÜÙÕp q¨n¥¯ž„ÖýêÔ|çI’{d… ™#zKÙŸ;EèYõÕü„ÔôëùœÏlY¶õ}ø±ŸÿN¦¾¥» Q6QðQË mÛáß7Åd÷®ž»5¦4­„GghŸÚ¨éÀ‰‡—QËÀh–’’‹ZypoC$ßÓ²ŒÏnz ?& øÿã—#P¢á³àeöºö‡85Õ­À{:tÑQü<{$[]QҦ¶¡ßÉñ´# ¡jË1|ùSWÖG?Vįšb“'ÐíÙ¨X"”¿)ÅÇ:/Bûšªuø§P!{•òû_9÷îvÂÅæ/Pdçl(;uœø¿XÌ×é&ÀÞŠåXyN‚Þ¼¸]&í¡hzìj§ÇÒ;^¸wýQz+XL¥UiØCt“¢>¦¿AØÎpØ“(8; æ–¸ƒX]1.ªDûr§ó¯a‡»Y¯¢BÍöNË­øUÙl,2Í2 ¢ð›gìÃÎlÀ_v#iáÅ0X4 Ÿaß¹zü¯š/Våê%m½‚ð ­lÐÂ,r¥ºET  X?—O£vñõdØã;ì@x ”,Q¤á³"Qïš/¾A1á¹&OȳüÔ³+ :¸û1 ,O7à»à™eÆ?gyòYŸÌ¹§[XjV2WØô‚$çÞƒøÅUK„¯ÈóR,yÐLÞ‡ŠÂ—*3È1:ŠSð>’ì²epbv8:5©cÅÂë,^Ùˆ[[ÜÏÊٚ¥ÍÂ}uÄpʶ»2U¹+ûÑb>PÿÞoU¾o”Œ§yÍ˱tÁhÞZàŬ"”ÁĶ·Éç“x˼»ï›ÝY…­“à‘Êýd²Owã¡u¬ñb9‚R¯ˆ—Å=ecø‚—îX@F¥‹? fðUhþWÖ›à[|úÅfLñ‡Õºý¼¥™ ^ŽÈ&;ufT{N‰£¿ é¡×Isù5¸[ìÔ³žé4 ¯µ\àÆæ^ò¢mÜO¸Å̼Jœ¨=à!'–Æð—¤"¤^KÆz¹ðßþ7ó†°‹²—««oP×°AÌb¼ÌqW$K{Tq’åÿýÿÇ•H²{¶(ý¸óöÿÆ—Nô×þDÔJ~r¦PÒrÓÇ©À¢ps*.ÞÿÍ~.©Á—ÒÀ-N çeƒø‡£¹Å™¡Øsyº™ÿÀo{r𣺔‚ÅçpNP”¶*b—ñ_rÜüû@þ¿f8 Óµ±.ñ[~kXOUå¢ËDØÉ¿NÍp"ä ü =HîýþŠ%×ÞAã݇ò0ˆ™I]¹²÷`Ê ú]k½zŸ>XKßÌ´£ÚÓ] r(2è-înGWÅYî…4X¢àƒ%ÁyÌæ#f ~ ¼o’» áÇKyúðS»üHppÐqœ÷ü<4 Ö§·3Xò»7 Æ¢qŽ›ƒ0v/ÿ·÷/VÂ^þj½'¯ª‹¡cnû™£îá7gMN´,qï_ær\;¦¢ÿbfÀ±UÇÈêOÁÏ,˜?Šýˆ-†R,䤀{‹0\\‘ù²\gøC<žÏÕÒ®ÁÒšÔ A•û½š_®UÿG d=’…yZt+)Á3"{ Èaæë솳Çäx®žº‰«‹]¡ ¾Oñäû>X¤#:€ÿÀŒc,R1~ïÈÀ€v0¾£…uˆ3´Yq„ݯ—&]õ–¼ïÅ zïðh¹‹CÝè’ÀùäÌ&p »LNü1Àø ̲ôÂYUû„œHçƒÇ‹%ZÔV·8|ýÆ:/¾Äa·°á4ÅÁúcèÖÀQ ¨ù^ŽLCÔ’ÿ´IÀìÓ# êô*¡gMrÇ¢ÿæGã‹äMì›¶/t_0ƒýÒÏ¡|Y ùµ¶_Z‡›žS8=ü›Ý¶Kž‰s­â3°Kd&&=‚ìÞarê*è6ú‡Û}¢îÂ&&–˜Å´a+>f²O”ük¼¸³ËÒÿæ4¯_]ÉšÌĹË_K¹D W.u¢e)ìN^$‰u†‘÷—²9›7¢lóhÚdRXÕ>\žwl˜ #‡É:eöÖ0eé$x-)gtÆñ ‰ÝxmêtÚ—”~+Ž’A>!8ë-Ýá³L8ñît¼Ò¹ç¿¾jÁËLlÇhîaì@íD¿’†S¾˜(6ˆ;L;#GÐ }#é»­œ9 âdx†ŽùÑØ‡ÏjWaOÇ0Ëhfu‹*Vók¿©YŒÚßžHצ· »vãÇyhçCPÞw „0;>ôÜÜü¯B ɬÑgÐ<Ñ^xd—<×ÿlÆœÃGö{±ooOM“ÅU?ÏÀóZmzÔ}$-X•†&êÇèbCÂúR혢Ü%üò¬–-8:îlwæNŸòðVÚiLö/Ø´dAZ[Ì3¡ùÛ´sl á¿mŠQn…m{oïüÆŒ¼§øþ•*T;I*¿¿Ânï3‚´Ž÷xh~ ¨U/Ã[|f¹ŠÅq—ÁÇ\–vK0‡u—úõ^!›xZ kŽ,b#5e„va2U¬ÔâœuÝÁç^Ég¼ŸGÑ”³qlkV^øGEßËûcü°×8~'ò2ÖÄù —Î, M×IÁ)˜±~"]ô" f}Š¥#£Œ¸àæ{6f.¡Ò3ç`®„i=‹K§¦oWp×=>Óv?‘çÉgcõ`Yžk=L˜cEërxuM¦`ñ'?.|¶†ºÙÆp‡CqIƒïÛÎÑ$LÁöÛî|ä ,ÿþT®Èò’ÌǸ<·Ÿ_‹€}†“q¾e!]ØèÍî¿ßÊŽ ‰…Ìrþ|“%׳;ÏÅÜ÷ÒÞRO_½u߯áš`)Üû@ž^½õš„Þ$“FðÅ[òWËÌà ±2•êY@_½­ÀÿçV @‡( 8}¡ŸKJã„!HfØr×<’Ÿ°†n^p†Ì¢cSžã›Õ˜¨Ä!°„‘Zëa‘Ñ|*LÛ+è~µ”7ŽYN[VZñùâ·éêÝ'Ñ£ö}×L~´èó–ËUxèÁVÝÞýÞžž«x±UË`ç>$#“®Éä*­&hš£@ç †A¾Ç`’â–ˆóâÉœCRЮ½›¿µ·Ç§¿u¨„k­¹ цlRa;®í>‡—¦-àÏ׉㳪O?ÿ ñGô(úî¼Ø%FÇéIÑ•~öò¨e©˜™`M«–7à×Ú¶ jßÚá ²ˆ¯®äC—D¡ûº:rÊéý¸Oe-áïNkn6遼çj±Øûú*Yá~î¯Þ ãNM„3*úyX ÚjÒ€áþ0äã.HøÌÞ˜€ž-³i}¾/ÌžæOwí!åOñŸÕ8ôÞ†ž}kHß?­†¾gíà¡rá4¼Iž:¿LÅ_Oo •>ÍŽ òb»?ÐkÏ»a¼øó¢3ˆñ*[jºñ;†Œbã ñ{¹æü©V#9™Ârµ_äCâ ÌŸ|—©X.DÝLlÛô„üºf¶’‚a¦ñ¿?}ù¾ó*sÓ¡IC7ȳz0;çÀþÞÀÏNÚ@ßüŒ‚i)8É*Ò·J°ßã2ª ½°׿ÛÅN·á—¤·‘Ù5ú´Î»ܧÿ†ÃU?1íÏA|mýÔß"óVÑ£ú`×çrlÒÔ‡¡[b˜~õKtÊžŽ_D<ðótqªÚ¸Žú‡Ò†‰Sù¥£¯`ßdÚዎŽEù»˜Ø3Œž9Æë`/NÌãçwg¡°­ƒý;'C_/[)д;€Ö0åÁ¶­ÑråHýW‡ï¼Q—Cx¬ïØ}y®ÍÌLÝ5ôß#É(x|s¤àÕ-ëüòSIdÍ"êd±ŒÞ^ãE_µ£\n!y~jÒÄ„Yè(ù‘l¯¼Å¼Æ?ä }Ú~ûZPvÇZA“«,DníÏöþz69_ÀœµÑ|Ÿ,_œúws”ñ°§’gßÃͶúKÓ–»Nªã¢»(=ßÔDjçÆ ÷màwUt™ŒŸ ÿ\*Åý—€ùXÜ· ^)V@§Ãº>¼FÖiq—ß1ùÚ–…“¡Ôèâúl\]+ëÚêÌ'kXîS!Î1yÉNé¨á îD‹ŸU’Y!1ï+JV ã¬ÐŸ—Áų0ß[”vêk€V§-_áãzza —qìò“Ææ'p,(šÛˆ¾dÕ©ºtõ7:ûeÞÙÍ ^™P>™—-/áµ5OŸq)IåÃKùC:xC,ÿ‰]‚Ú+ƒX{¨¬ç^ÉàóÅû9{¢ÁñuR>ä&G³q¾[ÈàC»éí Oá[Z7Û¸ô.ü˜»ˆ–ÿâÉ><íÎ%†ÑŽ8ø4G‡žCðü™x楣â|Ú)E{¾¼"á÷FÓ~|xýMذ+ «|,Hƒb;jk!t¸¿%’CX7Þo»bÓFðh%*K’„—ëN3¥¬½Ï”2šKU¦LãE7å¨×Ç“øJ´ ÿʹÀOçߨ¢¡D½µßRYÌNèü‰ò'f§}Ak¢½¾_ 7´¸ ä?‡›–ø(d2$wFnL¨j¿ºßK&^ûkðÎ ºh\P‡à¬›å…BÍT6¯L’)¼—&½«òÈfymx$;.ͪfFÑ5NƒÖ¸–¼2Ñ¤Ž—>²‡ƒ¹Ô—S°æÞPpheã瀙a9³Ñßg§Wâ¾Fiþ$!‰ ›¨‚ïgÞÄÝâtŸ¤:Í*Ä ±0Ò”X¯:ÎÁÌç âÕaÔ’©è£8 dz¢yØ7 ÆØ¡ÚÅÐu¢” 6CGEKªúS’Í‘‹Ë”xáFàVÄÑÝωÛüóÇG↶—Ì‚&ƒz”öíê&[Ë+ÁÕäôa‘øhCå¯çnýj–òF d4\áA¸4¯û¡Ær­n²飘Zäa,—䇖bey>¦?‹ÁHÐT_6êÅ.ZÚóÔ§fCô JÅo€{k Ù´© f ʃKhüw1²w{6Þ^œ¢ÉÅ}»XÅæ¿Ð¥4‡W~x­Í`Äm𸱛FÌÄ»Ë÷˜…$Êé4ZfDÐÔky¤˜èðÆÆJ\¤A£0R!†!þ“7ÃGÃø¥#lIó rjøLú{rµpøˆH<~ô*LÜ?¶”:€ï)ôâT!ûkÆ:üc‰s0Êç¸j\ÀÌ~¾±{ø*ô˜UÛ÷ó­#ŽÃ´¸L:?EîüáÂ&¥t˪z4ôÃjåa|Þç™8È;?9ò-Üš‡Ž½¦QË íŸvvH;y»ð’N~ÓT¤Iº|ôÞù`~n8Fhҭܪ·'=øO’ÃHŒ4uÇEƒ£PÂQŸÇ+ϦkáLKÖ¡žsqí¹wXý­7¾ªÀ”@º+4–n×Ö!g–ºAKŽ"»á…ÐVñ2ÎSã}À/x.}èVIçÇéãÍ<Ïh8¼ŠÅIö¶Ô;Ë…¾\\͆¶+ò¶ïVÔsj6 ìàtF<1ĵú–°úš»ÈÆÐ…Ýé°]û°ð$šñWùƒÞ…ôÙKú ±ŒåþÒ¦-ÑïÐm×§ôYÏÔ;—½ýzëǯK¨Ÿt‰»ÿ¶ÔƒûYt ›_â£5¦T²»ÇI𒘽ø?¦+ *˜ð³Ñ“ÖC™TÁ^TSRù©s€^1pµÇ?4oOÆc5 Û~5–ìÁã_‹YrÝcÁ†˜yÂÝqƒñßaº1öÒra¤ÃEëV‚ÆnCñÑ{qê¿x0¿v¹o¼ÈÄ|s3ö÷œ8•yšJâs⬣¯ÈŠ»æ`þýê¿“ã»5øäUxxÆ–;9\ê‹®[®âcƒd!ÉS“ÓÉÈÁ+YÕQ®.s¢ën³PËZ¦½E.7Ö±{ñrdçÎèï ÇÔÃ|éBù½Ø(NV|· °|Þ~À¿Óznˆ¹ä´økxد§ÎÎ>Ìu“—â«ëi×ú¢V1’&ÿ8È??è„×ïyï¨{LÊNë³{Ocø¦º<Œô)3¥1ü¤©aõÚ¼2šP¯O¿¬¹‚ìdÖ`E® *X:}%D9)Ð1.Ù¸LéQØb [[u nå˜Òû;ã€õ­É4Éú5Ú},€ «b8¤±†ôUÅ›Y’|гJØä›É¦½ñ .®±d£Èt~¬N“ÉX7Ѥo]¾¡MžÖÛv²—¢­8ôvÞIÞçšÁ#žK/}Å}U?à×û¡Ü¿{Þ@ü'ÞØÉ¶©ÞÀÌÕ‹`-êCåÙ‚ S§™Oï!¨½! ¤Ô˜J¼ †Ü†¶ùªï®^èoÄ£[¤™ß´,<$ÞíÌâÓo†TíøjÄfl¬CÇØ‘­Õ§êcý÷âìUz0á©5sùÂÄÇp¥œh¸«(Äù½neþÁµ“ÍOŽ"›„ }F‹?Ý2æ/^ ½ Tdi,¬Ý/ÞLbé§;™Hj%ø;ÊÒí…öTT¶ 7ç%Ehô»BØõÇ ¢mÐê¦?ü4í‚öÚ¸€Ü…Ô_u?ÒöÚøÂ± ¼fÜîü¶?AÑìKdé÷eP<|ÎoÄÖñÊdç.i~êñäùJd³>’­‹Uh‘ë$Ÿ36wÌ'oÊà¬r4—Á67a݆2TneðçW &Î&7¤clŠVÈUÓ2å*K¾³u+.cjl%x©/EsÔY8~[˜sOÑ÷¨º1.è AÝ K°ÐnHˆÔÛ¢”Å%òj×J¦kÍ|U¢y_®À Èhpz¶œmöÆgŽ&X™¬AÙˆ7¨unÔ sÖÇžºÇPî*Ëdth¥ª ³Ÿ3µkZ yP…œjþ¿ù?;Î#~?´ÁÝÑ‘Õ-sÁYwîÃF“ 0æn4X»´€ ºƒ¶è]ò'!jÒõá~å ¢à¹±zù¥sx×wŽèñËÔ(rol#³wÞÍ&¿ÏFùϤ~«¢ñÄigrNå6Žž* ŸÞLÆ<§¤Zµ‘¹5àƒ·ÝÃÑ#ûX€Sæ·þ‡?å`ûb =•¿û÷ÁÕ³…»‹ö8ž›Ú!2ô²„ K°b ãëÈÂÄ弢؎f^¬J”>q0扻óÔ1°oå ŠqÚ¡MTŒÇD ¡wÛñªl"†Vá†Âm¬{gàø}{ê´æ9ÿÞ’—Üá_´W-R¢*5»À̱„¼ýŽœ\òc®ífšg ¢ŽÍD±'³øÜ³é½ 5>?YœFWX »}fóBqM0°¸†WtD¹öç ð´~3Zd¨Ô KEÓ/±…# Aî™í6ÓÂ'_ŸãRܽg ÆOЇOµÅoC™§çxtWCÍ×GX‡Ö8¶]äü@þS_í ÏIc‹¡û{`¼Ì¥•‰)øÀ/€Ì>5 N­îf3¦lÎ9<ïý¥Óý4áCѶú•½8ÞË.zâR£°ªÃ#Ì>*Yàç:3xm6U×ð|-Ãd%(êA’&Eo„}f·‘•›ÊàJï”»}’E®è8lh†!á©üVÆlˆœS~ø,ã¬?ão9æG#1ð‘;/yð‰N–!þ|y£((µqÒvïI6ãÙ¿CùRY~sH2[Ð §ž¸Òo+kÀÕ,‹7§¢¸´OîzŽ3ñª‹=a3¶“çÝ"4ïülðžKEØrÉôFxWT ,}1ô$h9&ƒœѾA;Á÷ëhv_*ž-|ŠGUNŸûú¬ý L£ñ¶àŠEh?vƯë_¡ípk~qÏOvþA"ÉŒ4€.ÉlP´—©:Ù4_\ïa¿×Ó)Gœ1×ïÈGPLoú4ÿ']‡“³5=N³ya­`Ý0šÛ)¼ÁÚ#:|ÎõZ&õ¿ëaá 5Ú}·¤úµÈ °N²f»4 ±!ìªW&…‹Ýø–å8$á(^/z ›ÒO`W¢9Cw°G²a§Êv'õ­ÞØsz) bñ¸@ ³µÔè¾ò:᪖ÝLB9†V§êKô¨Çåx¬©,‡¢®Èx!J†~Œg£“léãZ´it{Òuê³qWÓ–oo=…A¸ÒǽzÑÂ"¬ú]‡±¦¬6xˆgˆàêIxUÙ ¾?£àª9¨—ûW úòÉ€ýÓçÝþ.x. »;Œ%hu[ùÆ5xûÓv²jÌpþJO–Š­´&ï Ü~æëdRYÑ_ù¾í¸õÁVV#Å뢇^2VºŠëåƒèRU ¼q]“§ÏY[4^è÷­ð¯GöÙ8ô·U½Kô?ˆwȸÉ ó¶æ£“58ýcÎÍ 9Úwnv>•áÚ»°ë™$}iw£lƒ¿õpv½˜Hއ9ÝuèüÆ•†¾Ý‰Ó¾ÍCOÁ9ø`µ½ ô„Ù ú4õ'N¼ª)´Ô%kŽ•AkÆjrÛ'ž¬|z —\Ÿ@VŽ«&;ϘÑoÄùñýÜ*¯|'s¶]$ߥpT£¦ŠE MéÁ·¦#ñoñ¼L8Ïq. ßö?ó9rhñ|Ö¥À 1ûàüˆr|yØ ƒY`·µ)“ròqZÅHÿ߀øS—!¤}S5, ³§°¤.ÇãC÷ÄÛX2B¤zwõ~ò#¡A³¦šŽãiÏå1ñS$y÷]‚/‰ç0'Q–ßÿf†6ƒiÞ´²AX˜ƒ3ŠUèä¿ÿ×ÿë;]àÔ5n,w~9kfÁ ýÕìe¸ &qß5ß›áÓæªš™~ìETq°£mªøý„| ©{†qãÊlØT½.‹üõ5•Púè8¿÷•(œ3 ¬E–ïéÁk®ïư›§`Ô¨&8úõvõ´å¥°àÍ>ri¼U5f")Íx£¥× ©€¨!× ƒw=°ça»o~^0åÚ¶M½Hpòj,-K"MÇòš1¢èÕÝ„3Æe£~œÂ®ñ s{%¿:/.þ(gßc„N ?œœ.ZcžãðØÞ‹¸ËµÝZúßrå“ÀVä ê>9õ\¸·` W3Áj„Å·Âq³nì¹&ÎßFëò‡ŠóøéÍ'pb}ÈTýoDM;Ÿ²úã_ÀkôZºæü6A¯ÉTÙô4ògR¯ç§Ñ„xò‹‰g!CÔ”7HûP«cJ|fë`~cBÇZòäÎ(h36Æo*ð—`_®‰<$qïÑ‚}9ãA¿ÙŽ­ÈÄ•âYxæö0*£âGÏ&·çìc>JFÍ4¡/6u0ÃJ§ÀÔv'Ñï‡üxî…OärJSµša¥0hi¶ß_Œ°f–$¡wâ˜÷¿XçS¬î‰0Ú¦ù awÒHf£j\*|pU£ºÚõ0þóûêßçq¡ŸÂà?ìÒçÂí\ŽžšõíW\Êóípåˆë8Ôy*ΙæDÃß5¡Âº…à^ žzr8Ž‘–Y x)áíf3îŸÿ|6|Ô ¶Ë$éF‡J8Å-aŸ?T|_r4ظ jxÏ Þl·DÑ×Èf¯¸eÊ=ð)¸ÏNÞG뺫NçïÀA «qÿzÖŸ8‚ûæw ¼LýÀ¼Î€:[¥°wÎü߉¥$uÂ0¾åÓoèz±“4*˜ÿì™êƒ;E²ÀBi&>Ø7„š Œ)3œÀ¤Z‚f9v²©çûHÏàSðói$¤Y;¹L\—±+¯{Ñ$ÿ<˜¿_ƒ×<ƒ®Z°eù_æ&>×ìNuÚ;¨–%ý^TH#{ZãØµÂ«Ð™ð ®šk‘÷&<á™# ¡Ÿø·Ú)Ó`%š®Ý‹Öç’¸HÒpbõ}øÞ_Æ/ösîĈ“{6ut±Dû…°®1C°v©}Bƒæ|c1—9÷ìêþÒà õèG­0Rc'÷Øp”üiɇ¥ûVâÓ Ûl“Åš¯™J¤øµúw&جû¶Ýôë! DŠ;¨UbŽs-ÎöM¯û¾ÄùHÑj¼ïöLx&w">þØN„¶êtÄœZP‘uçwžÄGö£èÕ±m¸sðWtzò“ˆç…±%Ö àëàJ;kÜàêà(*šæ ¥xÕcÓZ8…‹ª`Ôõ=ÐXü÷­‡í7ÉÎ>¼QòR— "ê[w}A¦<,ÉVYïàgõ¸{_¹ªPCFØŽäËNacýhº2Ø¥z|®u{kÄ¡ÞCM‹Éqm3&7¥ÚÀ‚¯§Ùþ1+ =@ŽJe &úŒ[ýßü·i‡pÔÑœŸP ‡dé¹E"ôV¡'V™ÉSoLâƒßÆ¡Vg Z^Ô¤§â~‘EŸÁ§ #òãö<,5#нñéGl\þLp )¿GófÖÔæúkÔôýIŠw¢ú–m¯¶’J¶±¥ûfçÉ™0nŒ—ÚÀ彞1™ìéÞ‡VóEééò¹¤y¶÷æ$N0^AV\=ŽA]ð|Ç}áÛ½ Pš æCHuü|pˆÍBÃMPï²”mõÀ²×úìŸôhª´bÛ “—6„ ²W[ÑåwÍQAZ ½<£«^.¨Êj Ä¿S+àšŒH8±”oošÅåÃ~‘Me6d¤¥·I…Ÿ×N^σ<ÿ!ô¿Ÿ gIkc Í‘¯g™Q۠øÅò3žKO „ؼAŠ‹ŽŸ ®hó:ÎØklÌ¿ìPáäu_´åÝѲg›L2ŽãØ„ ˆÚ9CÅ'Q•»Pp8zGCÁ yþ>q‚Ÿ6hG˜y#„ŽìÛEš4ý¨lÆM˜—´ÿ åKöç£Tã°oFh–.…k ψ„U>öSTá­Õ8b—)«í!.k#©ä‰$j¢=ƒ«¯˜ÖÚ²üfÉ‚B£.Yô‡øN`&FÁ[FüßöQ¼dðçÞTÚ½ä6ƒ,/¬Ü‡Î)peÑdêùø ®Ëc^ïF½ê^ÔÊ(ÌõÖ¦·4§ñ…“*±*r8HÈYP˜1»v¡ÜâiNÂtø˜­¥¨bÕŠ/+f±Óqo”µc· 1^‡Nî\m{V,›³'CÊÅ\öJ, "«@áC­Ö ºË/ÂMÓ¸s 7Í ¥ZŸ×ÐQKkñ¯„1 3jÁU{éÉÁK5©eÊ~ýE‘+f™‚H‡ÏgðÿÁç&Â¥.?¦zDµö8ÒO÷“¨—_*͉5â‘Û”ÙËÞoìï,V+ÑäêàÇÙ‘Tzk­Þ±ëçòõüXÁ9|òb—pq§_Ðx÷õBOºýˆz<ýY)]pQ…Žé‹K†ÅÐ}?Ã¥õ†4C5ÜÇòl¤$öÜIbÓC²ªkïF /®ðßYvøÑø.kOJ‡·î®UÇàÒ{š3ëDœ>ƒç¹• ÕP¾¤ œäÏZ 36Ìj™~¶ +˜¢g*ðÆ~EzÀE„î]/M¦¦Nu«“`êli:"¿ùKLZ]§34ÐÖÅo.º _®0ȞĻgÀ¬OBÏç BÙ„µ_k‚>µÍÐ~â<¾”™VkGÒcUªüïéi1 2ˆ`kYZÔ¯ÖÌ¡œÇ“Øé‰(2ô°Éü2ÈØN7Poíæ>ÎfêúÀ¿ § ¸4Ûf‡߈éˆÎÉýÍðűÿ= ã¼…NYÓ©~ûPžu¦ %]ÙÃE' ù`fÙ&ÀÃEfðÒ5²ë3àöZ䊭ø¨†Šs\ bÿ’a·Éx˜r) ›äÞB­É6«ýJ6ßð‡ü²$vûmPÛåï _®_ËJ­ÓÁo|” òÀlíÞçÔø¼ûï! v`îÖ¾hŸpdÕúP©ÆÅxð¿Þ‰Ý¬w‡=‘~V³RAíÚwÒwÆ’ÙŠRÂðJÎ7¹ ¶¸ß€ƒÕÆtÊaˆü(JÙuááwm»Œô3àŽ13è” {qnWÿz­?Ú‡ZÄqaŠ9•ÒÁ {ïâü‘BV}*žê®­ù}èóE£`ů5x~²½sq;Mð*ƒwS U}bBÿ #ÃEyíÖÓtÃyæ5|*(ßTvq¼²Ò“Ž<âÇ£¦IC“ï*6éÝ£Eºw³3ám¨¿úÈiì‚ãòƒaØÓB2Íû ]ÇБ‡!l˜ö@üŸhñ"§'ÄAؤ‡NÁ·1»è§Â¿#*ø&¿Ç9Ã)A$iØiAþ 0‘ˆg?Ý‚]¼5-íì¯;~ðÞti7#:¤qÿìRX|t&•9qÁ Øl¾[Õx£W8}¿a#Þ¾¹ÏûHÎÑx03¼"°M»LîdE»ÉI¨ZZ€Ëô„ŒIw3§§9èªX†Ò¶"Üs™3”̼.lˆœHy«w°5”ŽÁA‡5èý«kñ¯@'gÉû÷HñÝñ\ûÉwâs!¢Úär­Ž9Žâ¢.pr ²ÖV—jïÓ›HIЙüã>ij[>H,l ÏOkó…öŸH°L ³çA½fåNæê|ðá/ i ©¼’ŽøVU¥ÀCýô™­Ôtš^Ä>ýÙBWÉRܺ¢›mÿ9cb{üÔü –—m¤Ä©ú¬ .Wº†ÿó¤‹ÏÌÆ’ᣞõX.|ª’§£VMb÷máÐZMZï³ ® ŽÁ»K&tŠütî÷ú0É›°’–¯†—-Ñ0J‰;ç+ðU݉lÜkÝ»7ñ:íÛØmÑ s¾Ìæw®ú’wQd²1”JìåJXúkË Pôb-˜<È'PË̈́ؔ¿œš¬Ö¤b³0¥L’Ïrÿ&$ḛu±¯‘‚¦éþüñ†"‘/N©f4Luê—øeKðè):܍ޭə-œ~$-´A(ùè:ê4eÒðÄ\¦1ú,_ŽäHŠ ÔºÎÃ$K#¾Ìx?SþÛ#œ­qm›½°íÑ ÈœBW¾š ÒQÉšœ‡Ì@6~»~‡ÕG$…ë3„Lñòq¬9PH¦Æ^ýB”mü‡—âM9T¿Gg½7ã5‚î\7õð«z$Y>· Ûe¶€åç .ï( ±æœkåâœ9Ë쟭#¨¾ã!„Ã>±×›˜A“>lê}Aï?ƒ 𪄠È4(W¹eÅ—6$Ô÷Ièa»êõBøßñét©>u‘YˆS'ñÁs`®/û[†îy§‡Î³…øiÄÌ0—¡/;ïC|é-”;¬É§:*À‡¹üIÞkÒᵊδÌäCWÜÀ­~±dä6UˆÙÏVƒù¼(| H‚õÞ×@üZ¼ob˜¼ØˆMŸ|ƒ]9ÞK&oÐãï­å‘ùÔ‘Yé¢0µùÖ*9âTÍ#ÂÆv¸ÇCjË(ý‚œ')B³–ÃxpÚ¼qI2>¦“ë)1L¾¡‡4Þx€±K«0kø+˜@U¢Kt†³;W²ÆŠ¡øì¥ù—ÙÃÌN áŠEû0ët%Ô-Ñ¥c—Ûð¶OŸ`ÒrwTqƒL~D±ñ÷(·ð‡ðQj-õªrïBkþ"Ûï]פ"MpùeIvÆé<~yh£E*ñý NýUd¼ …Ϻ¼+O–®Ÿõ‹•'ΰÿž€ð³}Ž£ÕåÑi¥l,nGÍ;J¼+¬uhÚó-(’Ü ª]ð"a4„î•¢U' ]!±>‰S\´u*}p* ïŒE}ržbÓšçàa,¸seïPê% t’ŒµQ8ªÜ™Zö½e¾Ë[`÷Ý#ØÓ cG £ s*`÷™xaŽØ&0Ûšƒ–ÂH!îÕå2•píí \T·†Ð–Çé;i¯°i–&N9 §³_ÙŠjð>úÒ¾ ®Ó&ø^ìOœ¶þÄ…çc üÊ[òuœ£°oê4‹*ù¶@·“¯çÄ˯À¯v­™täE þ7ø0r£ÀL³ …®c 9gÐ1ÓI £i‰M/’QèÔŒf=]äR°ÐùÝWV2ì²P㜠Ø;fÍ[OïÓÏt-ñ€Œª;@:êÆSÅ üòÛŽ§ÞÜ ïBÇÊÝh<¤œyý6åŽm˜àŸâtâ—¼Àço¿šºfnÛ…‰=HÈÐ7øoú$-‡W¼”±çù“Lã?<^õ)«ßâT¦æ‹ðÏTE(Úp FKí… ÌuçÑľÅ.Aùøf8¿ýLH£(õÎŽUð„õüÔO> »³Ü!neí€ÿçù'èò|„G¦&òqJ/ÙøQô¾¥OwMç"^çÁ«ª ezsù.´wÃøs+‘«ÿºH3Ã^öÂÍ)y’<ƾvàŸ^°œ1òhñ0G”&óÏÚ\æ„‘•˜DK·Rž›ìEÝ3ý¾áàX·…çÎè$»]O≷ÙPY– »âÜð,¥÷ì£{&äÑ1H¯9—CÌÃ¥4{v-W~‹”¨§[.yˆÿºŸ“XÛ T{Ùx7Fò™—gQáUþ«Až½ÌÏ–ëÏy\±Å“ºº•Óóó\è­ïû¿Ïl…ÅÆQ·§…iéBqÑW;}(¿Ò¯«±¶=‹ —|fñ6Íxv´:ú”¦oŒ!ËF`ŒO ¤^ðâ’ãù¸#Õxgä+®u7‚ §3­Žã£7—¡õl)ÓzÉògîÅèú:.ÿ|^÷ å­áÈH)ºkÆ$ÜuFV+&`JìTK_‡g âðó¤,úáÑ:jƒOîÝ‚¥ß®A®œm‘îÁ€¥ÃèËøõÜJé3~X5‡oÓ§5={aœF&³¿ëJ£†¤/ÒjÀoG;ØÝAÏÉ*±­=xS%’ŽŽ/àVò túH„îChâÄó,²I—]ùZ ‚ ÕÔèPa~êu̲? RÛNC‘×c²0lúN‚ï®ÕbCšó˜0h1ˆÚ§æ¥­P%Ö—W0'bôÛµ‰eDéà53©xcÆÍëL6¿™½+Åš’P©_M’¢7v5ãÇà|Yý€ôï ŠÝxÑåWpÖr/ÖÃH¾?]–¯7ÆE·L'kþÈò^Û—„K¹Peå`zï7ImÉÂéÏÃëô LÉtǦ‹Í‚þûñ¤jqõêö!üÁ}n8ïZwïA÷,|òj-ŽÌ6°¿G‘?dŠ¿cv®{èóDbGƒüYù†û@^éŃ\õiE¨ïцsÃ}ÁgL=l¬ÉmŒk!BL›^9Béz§aï 'A°„<÷‘F¼Sd.7äèÝ#,νuøŸfÁc ÑCoŒ8r jÅÞãŠ^%Z´QÏMÄJÕ–³}Þ9 ßŸ`ýa¹”)fʼnкž‘|ÄCªÞ›ÏÚ7ÏC¼(ÁSF§Á–Ä»è\fÉòN¢ócrŠX #"u!C=LÖñ°{Ï`žm0\5VÅéw­øüÀ½hr¦ =Ì\ÞÈT:“äóæt&K-öcZ’íyö }W\ƪåìÏ!Æô±‚˽ŸÏ•6-àä*ÞømŒ6¯ºÀ{¹3h-’ )½&¾ÁäŽx8†ÃÖGÇñÞ’9ô˜Í*®qá2´ßS§ T:¡ÙØ¿ŒO±¯jçÆ ‰‡!m{ù•O›pcÐxê%fIŽºÆb°õ(ª©¼–n˜eÆ”Ž"ÍÑi,D{,ù}ÛŠÎéy¢³ IV™êÝ+ÅQ;îƒ÷ƒËØsý"-?<>~!´Yx CÐô· ¿„ÚÜàá.<0™P÷Kbô¥XÀþv4‡µv½ò(XþþÌmщï‘ñ¦v&§ùŠŠ~M¡ ÎÅB”^{~2WäÁôö,AÈœ" UíÁ"|~ßsœÞ£.WÍ»°ãÖn>Ba!W¸LccÙ<ª„Ýz<õg2¹=t©k4¢¿Ç¼!ù·gÒ«Núüû¯$Åé}Ö0q'ïJ[L_‡Ýé~¶M2åÿö< )Ž{aVß$Œ9ùm¼o㟓­,áÈF¶Ô‚çÙiôHI*«¶/ .ÕƒhWWÿT®Gƒn¡ƒªsÓMý\õê Xÿvÿ÷’ñG_f àÔs’è.Z I¹Ñà¬ù6¬ÁópÓ‹%ønu=:ž­9nÎUõ`]¾½f!BkG¼c퇜„ö׃ª ¨Ð¦@ûÆ–ÁÙórüïNuìéêbÃ’ý"›ÇÀvì3´œ¶±âVÎA¯à°Ø©8mÉûêŸy/1ÉÆûDAô¼q±r8ÕTg¼Òì ¶Ý™(XÞÙæxÒ·KX¹ù¯){3nf@mi5<9¾B(«|r‡ƒ­W#Ú;èP>Ü(±¹¼šÀ¸ú;0/AøÎ_=Ü^ *†™p.¿-ï\úžOØÈMõoƒô ˜­…Ëžkã‰øì ×…™ËeøãòðC=*Ö„à™À‰üR…15N &ǛӨÃßãPШO>§œ–Óˆs~à=7'òóÇ åïë‹ØôáÖ|]± è„EƒuÜW$¢ˆNÒòÜ«f/ænÓâÞE5$áW«ãÙ¹R¨vÛZ/Jl:Èïd¬è0Ü>Ø×3|¼YžV>VçÓÕ†âmWúùÛmpûzø¿3Wï xÅÂîcñá`x~ö<;±›¿¥SQcï½þ{tضcðñ–…ðåOmÑðs„.Ý}$}ØrøwÛ°ÍlÜÉ7=«Âwf²x1ü9ùðÝ5ÂUjçØƒÔI k®¦aŸÑBv(ÍœM|b`ÅÔq|ä™6¼h܆1kÓ`X¨.UlÝ 15°$;Š¿oÅê×åP>ܘ|>y‘è›­‹qánÙâ„oŸ‡àE«HÆS—vÊ›ŒN¡Tá2ÐJ„WM Xâ*M‡¤¿b‚ï@û¾‰¼Óû(îlW¦¯såè³ÆhU~†Lûù¸ì&MòÒ\\ù þD} 4Ußû¾y&3dVR"CîywJR”’h. 44˜ç(“!25 %î~7ŠBi’ŠT¤B*ÿ>¿µþ¾Ë:Ö]ëî{Î~î;ìçÙç=ï=ylNˆÁ„0G2ZWlsæ‘¿ßçaÝúè‘üä%Œ{yã©¶·>ó2“×~!r¶*Üe‹²Óãpóƒ¯hx£'O6ÆHe-T¼zä„ÿ¯&ùuÌ Œ “'S3ÎpGü>sŸÞ؈¥±^¹-ÆúÆË’+÷ÊI°¡Šã =t•gQ ¬‰$ý†5¢}¸mƒînx…Ê 6LHïݹ͞9=MÂúVäVö‚º`†ß $=Í0|^ŽB…SÝ´(Ež¹Çm™ßñÈÖŸTs–÷fpÂpæ_Á“¸M¥rú•¿7cDuï”m9•øžÃZf<‚_Õø+=x¼® · ‰ª²/ì{DìSÙ’†©ìö4¼g©ÉÎ.?A²äŠÈÑÐáïô˜û^ø•ŸMÜô™ O™¼%rlVëI®yú†‘üW·…‰U2§-þšäÉ^ñfpÞK>á½Ÿë ‰Ù³Sɘw`>Zf?…ë\³ ZgʱÚ(e6Nç÷@-ˆXöÉq¿‡s~¶^uöŽDmÿç¨q"Ú»$`eÂIÊa‚–âDËÏž|ûiÎÜ&è°âlM,¯žÏEäÀ9*Að㯎Z ˆö/Zqÿñ_MTgÝ¡ÇbX`ÙÈÕ‹¹âÑzìÊöy$ÉchíVÂDÉ6̺òe$›PâWªýíÅ.¬ò€ùêàŒ³)Ghüñ“ÔE‰òÅgôû¦)L6³èÏ?#÷ÿú§È¢æ²3òqŸZŽÅççÁß§¢dEðt-Ì‚>ë@Ðn?&t[º) Ò®<ƒ µ_àr~'W*LÊ[ßpŸ?si˜Ë;k¼b9ˆn¿·bm|0ypû6\ÄX!öióœ3ð‹>Ÿ†ž]9xë,άœ·»»xÑ›œð]Ú"tî ÊÇ9Ð¥É9½³€¾ŸWÑfGFϨÓÙÆöw#¢àÕÊå´ø|ÝÖ¡ÎßܤÅêâbJ$·® oo·q¡1Sɯ¶‡ü‹ß4É{¹:t,&=¨„‹µ«@îF¼âö‡œÁÿ¿z1•›åu’'ýôtÝÆ â£Ùá2;ˆöCöGTØ_ü,ÂúrŽÃçµ Xê´ÕlVKN¼3–øKM­VŠÜÀÓõ°!þ·ÔÞ„´2]4–È¢’—EXÑ–N,ÀO)2†<†å_„EEþ°/9zm‹d…3YÉô‹œlk_uÜr9Òn"s¥…³/Ô"oõÒÑ`å"rþå2^à†Wéxž Ëñ|ŒçÕ´1rfÝàUMSyˆ•ª¾Œ×î©ÙÏ92•BæZ|µd'÷íg%Žž¢¯$·°² øærxÎYŽ­— ÈÞñ¹ðQr4³Ṵ̀¨EšŒ½—ñßÜ!°^„Óbê§Å´ÞÛ½ŽßT;–¶üdU5ঀg4€ÅcÉ«ûcX£å˜‘ø¿ôg5ø„ì‡Ûc0«+„BröÄ‘ÿ«eÖYÄ1DÉ$‰-Ïfà­Å! ŸáËN×ãaÞ£6šôäI’Õ¹2$y›Ý‚©°×Ë÷Ý'—m ÇË¢`/J~~ŸKÒìkpðøy2‘•sjoMcݲaŽâ(ò¥§ „RÛL ²ª[—>ý+¦Ë¨žåUnô|MòEçÍ>5rJ¡ðÒXêQ¶ ⤲¹U¿-ÙºÓR¤øü8»¬›Sþ½ÜníæNéì¡qC%ðRÕ/:-Ro žr׸QŸ,˜Ö“Бõ_(ŒCõ-A@æÈØ…’“`:Þ´ÍÀö‚WÜyD¨‰~ßïI¬÷å°÷L#œù­3¦GÀW7O<4c®ú>Hûײ½÷taÆ*²h1å^ÑÆ¬q:ô¼™4/@®”KíÃý8α;‘óÐÑEŸ½]s›±:™+8á$ÅI9 ´_“¨¹lÂ÷ÂU‰Zœr\“õÏ)¤ºóTÉ©{Y° ¥ ¯J~„Æ)Õ\L×R0´”íž‹Ø>ÿ#z|üD]„âÐO,”K¤<)öhQ94m4¨½:ØèhKsa-LHèÇÑþÅ#÷ÿ§ÜÕã[ßãÌ&ÁçÆ|ÕÜDþ¥ð«iÊ™ûö³ %ˆ©È:j‡âÄn 8§*…>wù'›qòŒéLhð3ò½JŠ„†¹?·ºï0<÷®Æ£ óѼ=»…ÊÅp"÷¨ô„óPÿê07*v2YöÀ›—rf!ÇKÚH¯N':Þ¯áCí-øƒÓ±ÊyÒ¯ÞôZ[ÆÇ†ƒ¡ ''ä;Ì7T§Q ¯'üRíVÚÛVCÅN½ ·ë‘¹ÛéuØ–JzLb}D:‘æý%ðs‚1œUåIwqû/h?χî¨ß¼»FÄÿíCiTóÉ_NvT$¾hX …•¨Õͨ”¢Î$ÚnÂÆ8CâSò ë§_§V‡D±hÜ:Z,bÆO‰5!©Ù³Pi.+ywºFSwM(øÖ€3‡5ñŠ1»põÞÜ’6,õÅHnâ¬n¾ƒõ{¿Òços¦58ûƒ$ì©tÁÁ§ ˜£"òfH±‰‰{ß8ˆS²Ué—š¥ –ÁêêäN¸´‰²ŽQ¯ÿ¯núØa50¿]}“ 4ï&×5̲' ól%ò@ÌŽ)Š7s‹ÒSè±ô•0ïÇF8kºÅ(#÷—]Y1²ÿ‘Ws„Ó—Èãt6Oîo6º~MÅÒº‹8Z[‚øÏ̇ÞY ½8wŸÓâ¦d/CUÝið'ÔŠU§À3õwðó6fHŠ³Ç½7P]å7WvP^¯{ºÙ4Jí:—íqó¢p'u²B‹Œ‹zŠå 0«HrÆÏGG1M°7-ãß*I_²I\Êê_ø×ú§=?\ˆp¯1•¿H†tèmï¥f¤_Õ˜dßT#J;0a]o«U*>UxMõ P 5#ð¾XK«¾4ðŠÛÜ ¸îוÈ1£N*÷DÌœnr:߯ýïùß?_±1"œ[4– äVÎ^9ϧ0—þí”àXRDo¾4"*׬I|ž3Ȟ܃—ôìQçêz^ž¼:~Ñ?¶<‡®{áCA\´Û®g 4£—Ÿ°2ž¤„A{–Ä1=,Ÿ>œãÎȲ¾öjP%ÿ:î(“Ù¾ƒ óΖêÎÑ=ñRº\hQXMa|N6)‹ÒÆÖløÜ¸ÔVwyÍeIqñœÜúwÜK e¦™…òYmtfߨ1e!Ñyd⣬!¬VP"{ûåàg© lºl ËV·Áž·0£òËHþ«¸“Î[pw o—G¾¸–;“¾­%è6»k÷íë­‚$Ãm,è„|vn„:mäJB¿pÙ¼ÔÙº–ÝR3ÂÁ»@E%iˆú+xØÙ ßÕ~Ã6l—Ò"&ϸUróé÷¹'¨©ü¨¢¯ ¹³:]¦ÃÒžIœÃp^ï¯î‚•=¥øÅRšÜñžNñ‚ú8m±*ƒå» Áã–W1¿žŸÅC£HÉP Jm0ÂôÕ°þÁ ’T3š[QÐX}äc4Z¡Kq;›h´›ÍȈ¤»Ö—ÀUE=|9þ7çêÿžÿcðÂ#¸S·ðšë[ìÖG¸´ ÈBç³-`uþ Og(?äoƒkkî×}…´iZ;Ì7…¥\¼~n+° ku4eß$RzEãU/¢’\ ®Q“¢Íïwp ø©»„˜ƒ?œ&.ªát¥œÛùy½¢¡”³Û72–í!Î-°c}&~… äzh3ŽüJ­WÍÄjøË«3iÇcÛEQasÝ’I ´wA*-ûŽS¬ÅXîÓù .sÄûkP^^ ÂîM†Ùå˜{,”·XöZïøòžWàT’4«Ú“5‚ßõ–Np:Ç™h^ƒŽ+ÊèsÍ‹Ä&é°G®áÁ•ÑŒúuàñ·kpÇ_öáůïùgl³—ªl Bó­±`&¢[ºqú‚a^êˆfFø½ê轻¥'èѧ:Yüðï'8óà**Ž<Ø‘= ê²æ1¿… ~Ë~Þñ…Û„™jÄušÜp×tVbŸ‘5”kÿ䧇àGÁ‡0}âsÞ¦sSQoún\nî‡vNåqë¶€m—9­)|N‚q}c<\©Ñ%oVLŽÃTúMŽùÖÈͺdäs+žÜ]‡ünžL.ês‰mvëKÅ@üã9ÞçD%íÉÌÂl|>WžjtC›ÞúýÕU0ù½‰™L&I±íH9Ÿ{ÑRm®û+Ìñ?@©ÕpŽîé‚S×¶sãTÈ•7‚DÐàyús.÷Œ(Ýlú‘õqa¾Rì^i¤œË åÚ ãÆ4Qçë Ÿc¦±é3’qïéËðqærð^Χw–îÃn À·wéƒåPú’ ½%7nžR`)©ÆÜ3Ò{9œéHfÃÕ_òÄuC`õÀ™<šq,28qùSx¾Ã Ä•&1¹îu#ù¿¬Õ é¢T|©vßÖ@ñ<]nîæ~ƶ͠÷G’4Zmâ¶Ë«€Î«'ÖZÖ÷äH÷¬‰û vß§@ól1´=~F)½Ç›žE¼tM\çPŒgxh÷Ë =$¤mŽßƒzº Üò¦v‹Á±Ö^(n&á_Ú!aÿ+^e~8nOľ7é`jÆ.ÖëzЖ»‰'‹´ù¼†ÝèÑô¾x%BÚ‹¼RªÊÆÚl„exÁ w¸¨UÚäpÉ'^Žç>ø§ù’×âó/›Ÿ§¿öðà¬d~Ö.ÄÞ¢,³ü?þÁˆ+GàïOÞŽìë3Þùƒ6¬Õ«Š?û2”©Qü™—Źo0àüu#¨ì_}ܯøŸêÁÛÑm¸6ç·âeîu“½Õ[Pý€f•£‚’ötüÙÍß2q Û}Jl×þ¦EñÕ4dÖ|R=®6ûBµI ™Žö‘’Di«0»(Py¿0¢é24wüð'DF¹Õ›ßÓ%C±0íÚhð±ý¬L¸Æ%è/ý˜é=AÈ5ÝNûNbkªª9Ù+Íø´J”^ÐÑ©3Ø£Ï-åz&à9ÞHüOÎyÉ…$s·ÔPfS>̬ϣ›Ÿ[1©ªçÜþ=¡ë— ú*Áf6,ÃÀqæ¬@äëdzø/“xóM»ù&ìç™Ë°A dûåXTüQ˜±ã4/qÂæ‰ò$và6dvÎcI?åhet þø[+´X˲ÞÕÜy}cÆéÛ¶\xPV‹«29¼üp,۹ђ&/EÆrSà#çö›7ž×Œ2cÿqšO©Oùvm¾Ûb÷Ÿ¬ éÜð`³¢ª+µ¹c)vcÕr‚âðe±0õ“#K²«ðX¿&>Z1­êȆÎ| B]û'ŠNtÂL=hª‡•ÑÕ8îæfÌOBá G8¬}Ý0a©ð–ûðHš’ Gðr\ ižŠœ¼ôeܳQ”d™$A´}|®Ù^ ÏÁ*ƒ"°ÊõbŸ~ ó*…îb\É,îÞF'•ÀXàHæîû™^£Ûu3æ—9½.ÕÃEˆ»gáÑA'ØöäÆ$M¢‚.dÒ–eT dîÝ…3~Žæ-š}}6ÿ¦ÏC7p7d¶áœº{DJ±s_eðä_I,U“þ¾‡ž>¼±*jlwdl/¦ëÝîÃNÏÕäæÃûœ‰‰*Ö\µU†Ìº$A̶è”R/Ybb ü­VðÙ}D¿¡Vqg(ê8‚¿áš:¯X8fî¢e_«…l"1Ë=YÉ­.œ¯ÆGս㘅ƒ)Q«•©ùçÈc¹Ã°LÌŽHø}D¬ÊËBè´%©ß'öÒ¯¶w=ÁêÍ‘hjl‡û£öý©ÁÜS¥bÜ%©Ëì3 8ÁŠå^áݶ̿ʉžu8òjTaàUh/ µ7yŒï 'OÀž)®äÆŽ`TŒYBB¬G“™Âa«½9û4m³…ø;Û ©ò+7­<”gäÌÚan¥•K5mݶOÀuaâäã:eâh¨AR¿&á» ÍŒ¤wŽèkuINîM$Ô†\âNù¦aô„*ûïUì•a…å¯1:> k*3QG؆÷ ÔÚd/L*ÔÅU.gÑb½Û«÷ç²t±R8å«Áßo´ò›ð”¦$³ÒOáÿW§ü(+ 7O–`¶&Oá¯ýr°ë àŸýËþ¶{,t¹ ‘'%O¹ƒ/M˜¦ßLHï ¥3ïÃU—">¨Ld·ßäd¸pÒšÿ…Û¡;mWÌÆ3Ì’œÔ!; RAmúG|þ!sg­Ç W.-Ï&? åßVaÇUÒUôÎ$£Ï˜·Ðüþ¬|8bgë#äT„™­9ܘä÷ðú u¹ÚÏßš{ â£ßà¯vÜ‚v¹æ,Ϊ þ«N»tœ·˜âŽqå¨ñW‡ym ÞÀ/)ïÞoÝÏF±ò¿j$us¬¨›wÅ‚¨Z‹8XœÈcSWÙâÔÕ_!=þ‹~义ËHÁ®"¸&nO¶ü‚„Æçv*ózQçð JZç"¿ï(·ïàQCŽ]„š×ÅøV’øëöÞƒ§?q:o¦òw؃C¿7ûCb¹Ý/Í™n¿6ë=:–S43Åù:KÙ‰¹;¸#¥Ïñá´4ül—ÀÞí±ÿ ,Ò sŸrá"þxòÛ!”¿ôïÍÑ%"¾&lþüE–<ÌuÞ¡jæÔN–#u{z©Ó¶¥änt ·=Uó¼ÝX>1 ¹ê߆¹ÉgÔi ²c—°ìØ£ÃÚÝQ¡ LäIƒ@šå’ë£s÷òáÅÀ9üuø#¸":kàMÉ#œçÍL©É-‘bU’§¹Á ð`®>ác÷ù®,På4Æ®^C „™ÐíOø)W•Iu¤Áƒ†Ýð+¾¢Úæy*>ÿ-MîÎÎÄ×ß娤TU´±KA­ÜU›I¤ùœ7‰åwãÔ»ŸGìÏOK¹ã‘ÈÅ?â\xz$‘¤7,Ͱeà*³Sd)Å?yvµï‹ØÂãp§Ü4¶t¥¹ÒЊ)+_CôìF4™SRf½=i2{›À׿[8¦½Ÿ”ÙÇBoŒ|Q OÿAêÑP( ä,ÿ|ÀÝÑr¬þßiU@Þ»õÀæ/²ÌÇf%>)\Îd«ÿ¢éÖTxÇâ˜ÅýD_ Lk‘µÞNÒú†àco¹¢^c~~{™U· J,áeE%‡~Ì ¾Û¤ˆBý&¶à¢ar—F;AŒS.)£þ†ÿÛÿËÍâürUvÆLe×5”œ´ N½B®̘ÁgmÚ¢1 ö¾y ïÆ~EÙÅüOKb¸Œ7ðaKªŸËdb3è˜èPÈ[Ì}­ „ÝÞÏhÛšÔæc;}ý Nwâ‚pwM*¦Ñµ»îÐÇãhùj¾62fC9»éÂÅ+aö»xpë» Ç•£±×Y²v‹k·Ùx ýY~à ´ýö…jWÞæ¹t-€Jíîð8!Í‹RïœF¿:Y{Plxy¶õʃû²oT°©5¨ëáõN\ÿ0МöŠvü†MsFüÊÖ2Zc’Â/—ƒ»|G£œÎFúpÜ3䕽‚ïÚ1-9ÿ|³ [au•sø€­w­HÚ52ãæ8–{²›ßpY¯&ƒ¥x1¿gb Œz¢Ž5~·P‚+হ~æÍµrÉãÉç<3ܾà^]+Gދʯqy]©Ø¥¿•žö P~g1¼<7 œ5HÃ<äš¿ _,ÿ¿ºìâóFдï¼9!Jf>ÈÂI!+ÁVÔô£4¹“‰3Ð;h2þL%+Õùû|’Ásã]d»ŽbÒúDh.Vx§ÙPwì-·E½ö§¥ŽèŸ²èÇ [ÞÛ’«îð¼J<ÙÔ‚Nùa”­ë¤îkũط4¼4‡¹e˜³±{`Î#(Ú‹-ùSð@䘸!¶M’%ÙNNÕ¹9jdIV!÷àœ &'à[ýCÌù(63”›8F‰¼»ðêÉwêgpêj£K2÷±É9ùT¡Æ¤ŠÝ`èÅ2øž %µx}](¹ì"öœW‘{÷8ûÁ-L¹w7µ¥Ò²±áÎ ŽYÍh»-)ôB­D›ýë‡ß‰qv~ˆ¤apaYä ÿŸ ™·~)šÝ€%ç9þñý±0wwÖ‚fîÎànÆ‹‚þQ&`GÂóðQk'þ~i‡ƒóeYñ±Ñ˜à·.œA6좷3R¡=BˆìÝ} vZM=Oc:‹ûÆW©ºÆ‘//àžChØö–6â¢Mü cÇ`|…(Û=e*æßËõ+¨—ó]jÆAû³cPÛéLOzÇ*±‹,¶°î Uth=†Ãã¸a-âµ;:ßõƒFì`Õil5/£3…cøÞǰmën@ªž&~¶|18¥g6¢ú»õ¤)ú.-šÆÌßJ‘祸ð‰ã Íö5Ê^Ñ‚:ÉZ•£JrV1Nd» þù ¬:ðLÄM(.Sã?KIÀ£.ÒÞ{аò¾(bãÎôû##X¼ù-ìη'ûÂ2¸¿]ÿËó"N{ñó áôP,¸(ÂÜÆÿ¥™¾Vä#+<å=ƒÿæì*ÀÖ R*÷Ïï¤Àqv)>l¾¯bu£¤¹^á(Ô0íáŸÖ@¹1¯Ð]}ÎüîeLþWÿ¢/E=¯¬¡­‹Ja‚È:H[°“–ÏbiKf“å&øHJ„½p9Œã_àöå®ðÃL f8&r÷85’¶e_®0Ë¿ƒ¹šÁ03g4™?¤_MÈò[h¹8ƒý‡^[´Œºjaí¹ÒjÍØk¨ÿ£ tù0¦A™˜e)1Õ Øi äsàöƒŸpûçzM˜œÞ6[íUÓä…qháB´°RÀ™mÒWí%J,‚¸#[ùjkZ6(ºr[;ŸÐÖµ¹ÐõÎõߘ“&Ÿø+.¬¤’Çö“­ÝH¸y.FuøŽ›H¾¯Ä}â„¿ÒŸ¬cKØ´ütw"™+Kt“â‰QËSÌÎud÷ÀØ7 ¿:IЇ¢áÒ[ 9’‰­Î l¾-û_“ž7Rp½B„~ÍÅ3" ØaÙx–¨Šë‘Góp¢ÑÝ{”Ò«çypHƂٙìƒ%z;Ù²]ÍPÂû†nãÈÌMDûòD–¬%½/¢í³Q7t•dË$¦3·†¿b=Ǿ,³dí/ΑìC7ù/O²Á_•à¸eúûÙÍјO#28Ëô/œÅ('Öüv4»û,˜Æ~›@¿d—ÎÁ_}ª‰%ði¥i/? ÇJ· E7ïøzðÕŒAÛ³• êÇuí?à\¦ {ÜKN¡ÏqÜKµ(Ø#‚ äHQXè- EÕüM(u, .©Z‘'cÿÁµÊnœ¯¼Aòá8²Çæ òk^Í·gtLïTêc…½r-Ô·m*ŒŸ‰o„ãë½ð/í õÝÊMÚ$HtCKªU.Øã± Ïp‘áMt97B’Àüè*èë £ Ø9‘%p¾øúéÑEº¥¸ ÷¼ÙæïqKšw‚Þ™Oô¸oâÈþÇÝvøkèúOÖcé™ta| Éšôæ%Š2Cífªýl2ìçzª(ï.¢fI$U?ãÿ:!ŒŸÿгæp%ØÙ¯E¶e…€í°Fÿ½É‹Tï`ÈëzøÛ‘Be2ô¿:h¿ð$ÎÞÇ‹3;Qéo• n ~ˆÅueVð"Y—;AûïGÐǽù×#Ö'ç¼êD¡9à“SË©Nc–:»øs”Öâ­0uœ|GõÄ®`OÁÕŒ¹‡ã=¢©Ç±¯´¡_“[%qŸ…àO4þq-¶¾g¸¸: ìÓ‚ë;Á¸( «׌ào¬ãJ⫟apa×m\/®‡ÂÒQsœ;îJþ«Æó•¤î¹:l.…9£Îðv^!]ÉŽX¼æ,š%‰µÁÚ Iø³Hfžè§iù0O̘=ôÇU2o|ô&— ÙèàÔ’"8ïº .OGé´½¹¦L$Ú6|N¯D:Ÿ‰‘…Xìþ%;Á–`à¿lpÜÆ ˜Î"ÞûOé)òËi ´„úP+¢±è&½¯…åy‰`¾}+¬!ùéNÜ£Ä퉦\Ÿç7z¹é$.==óœñ D^¦‚Ê–½#þïu«×jŸÀº®fwõY9ë \t~€¯¿fàKýÑDÕn YQÀ*³3XZ…ë ÒÌï¾?ûqë8!3¯áÜÿ±áîJ.Î+•¨M>JÜØæ_òœcN$^R{ëŠ0Û÷>PÒ“ +)`ÈiȺìc"ÐøO4àˆJÍ#õEÁ0p/Ìaæ!B3²Ì!#~7Ï1l-0ƒßE*€£ÜØéÓÐ4q ‹è_„æ’[É£$f^Vô r/æ @÷8iº]df²ƒ§³0q½áå'âœëÏd\þ± ІâGžqQ]A•o<…Sö;èCêI~ÚÍP©‚+ËBYÿ&œwo:qÔxN ÛaVuoÖûb|œtƒ¶ào<Ò„áfKqmÚKÆSíÆÊëÊL{¨NYã—&Uvèx.ÌØL²Ö]@O¿YLÍPì\f’Ž™ùоp 7'»¥€¤l»5˜l–:B4‘Ö6Ä!N´4|çTcó“4‰ÌK–ïb/.›p›3VÏ 5άéúSܯ®Kþ»VŽa…µðT|aã‰ÚíCv<*:ïàÏc œô­Qìýº˜ûÿj@ovêsÎfù >[ð€±?ÈßøÎeW&ò笉I` £¦nÔéÞNs8ŸÜ ßAÏ\}ˆÏªÇ1¸e:<›‰Â¯Å™r…$›51†ÇÀÎCé´jy8¤­<½ï-Сç97Åï HLûʹßÈX?ý‚ }Í#¨õ:Šk×ñ¶üŠ‚]ÏÀ¼ÞÇ´tq =ñÙwµÔÒœê@ VKËyU˜nËŽ ¹–å0)3ßþŽ—Ùa0ߎðˆáUœÑ+F4¿Åù¢‘[÷ŠKs1¢û7%¢]U"rÅ ?.ûßþßömŸ¹Cóxà‰?êÁ€m±éª/ü)‹¡¿Žôãö0¡>WÿÑda­ÿè•­:Än]¤öj•Sëº3²J釺mXq+†SR“£µÁQØmíÎÝ9C½¶8@ÑÍ4ô|ºœNé–'¾bƒ8ÙJƒ|œõ 3Cš©¢ôM¼3”IËÛLx"°žKÁ V4ŠÄÔý@+å]Xhr$N+±'ÿj°Kç—qr—[œ‚î›2@ø|dÎàsŠQO¸…Õ‚¤6C+òSèNG iõ"Vq?Q±Ø–Þ×FïC‡àb‚±¡;ÖŒ=?‚õui¢ç¯Lòûyd]¡ Wüydýy)â;4=ž»”(„§ƒ÷%yöê:O%dš0£\W‘„Xž}ÂIê´Ošì lå“ga·Ëe+òˆ_¸wë):½¡CC{?¾¦h‡“‹S"`›q5ñá’´ãLåÃzè2X‚ùg¯£Ù™¼Úw ]W"ü—)ç´UUáÁÅ)ô±ìu*½„…o´¯¦ÏU&˜°Ð‡_èÚ«/ì¤e¶±yºáÈšÂßÉáÅÑ, s#Ùþ΂͞«C>óòþ{~mîàÎåŒÄÿµ3´Ëæa¸ÑAîŠ4aÂnò—7O‚ÉÇÞðÎÖdED/øysrW•(üÅe²ãÉã•Ôm‰Ó*š7+LÁ½•©±ÑvØK“¸äzMz׺Œ¿½ÝvÌðâMÌ–1$!—Ïöz¶ßó0K÷¡—¢ËàµV _q5ñ´ØíD7”Óa³411ךDæY°¾±|˜þ&ÿ¾Hbeö5žÞõ1«ùü^îèn§{šVáäèDÌì\‚§/Œb†Ïø§R”PfN1]¼.Ç(3áýéܪw¹alLXÏm„ÿ¾Š¥v‡Q%/5Ô¨œƒûÓœèÂú§X,»†þ‰C…Eœ¸éh²G$|-=IwNtd iÁà[­ÏªÛþ¯ŽÙþP¡7¯¤4nÖ€Ü2/8ÍèZ»íTB º¾…jEðvR õóÈ¥½.¤¤TØ]ŒÎLöŧ[•aûîwTÝ|¤û6cèæxˆÝ%‚ íÀ¦æE‚Ÿ‡!—®¶cõÆ`®Ó-ÌX[ÃcY®¥+ªt@ ÝÎÍKqüË.IºÖ®[ÝSŽáæwzôAm îšE» Ò0mün¸kbNb?þ©ÿS™·¼³æ`÷ÝóØÍÿƒš!SPqûbÞ‰“¡´2J–=ß\‡òÓ%Ù¸ºäòÇL¬z¼^]btÛ%Uf{b"NZèÇü׿ÁÒÎ «¢hrÖÕ㤉…óiÜøK¯\ŸÌddô؉€fü& scn@Ñ«^NÖ?”ìpkƒÏ¿&’òÏWyqŸ;@úÍ[ÎlòTr%?ZGùqÓ®à·1k™äÒ!°Ûÿ£üd1¦¿Îݰd'½áLe5÷潜ڞ…jö‡ÙÉ.CèÚt›§ù‚uf;J ©¿WÄ1»èº)³i_¤+ô_X‚¯›Gâÿ[ýT×lãWŽƒÚçè¬Xbá–ί{ ÁØ‹§[Ù™±Ü+Wx* ÌÆÙ͉ÎKŸ“—aê°vHHÝ„ÓÖ9bëä±øVCöfp«V¸¡Ý¯î‡J'ÝR­Â…ËLÝ/¸÷ÑqÎc¢8<_V³ ¹5©xR{+4pÅì°¥ÜE¾ Ü-I­üÊ`NZÿtJæžÒdA]¿é©B ü,¾ŽÉV–ÀÙÏW¡zìu®ÝjþÝüÔä_@Py7:é.ݺ-‡¾wYÂS}=ã’°/2ÝdÙj\¨QøÓ—œÅÑÿõÿÈ«Â%–&¼oN造¦ë°Xƒe>°Áš}£HÚÞIøÐÙƒ½yý—·ÇÖ›-h€‚18ej¯ýÿǶT¹~²å\• ìú¦×¾šúL‹€¤‡öìÂÌlîòœ"гp$ú’“I»ôv5òWDþ¯¹iÝ.¢<4¯^ƒÉ±óPØbüÙø ¦UâbVùÃïØ Ž[ž|‡ÁÙ…0{‹.ˆægAìŸzK£.ó¿€æÂ‘—áøœï@†R´ØÄäF0]q‘Íf'^®9ž Æ^{*Ãw;3Ö÷@—$TáÙÛ¼0*²;¸œè˼ÑQ`Yÿ/¤‰“"C˜|˜u¼]Æ~õ„ðé¡­ìúË@î’æJ†w…áûÙO#ûGËPãI(÷e­&©+@òæ—À†Oÿp¼O;äOÁžÒbèX2 ò_éáÄÍiH?›7±Xøv!ñ“#¬`Ÿ<*´P ùR˜Ÿ`N Ϻˆz{bÒɃÓÊçpfãrXZb †¡S7Gœœ_•;w#§Ìƒè“ë¹0J~=Kê¶ í‹¯`Á¢±Ìäš{µÖŠMµVáÖˆ3ؤ$K¬n¤§ƒÌÙu3nuÒM&ßÔ^Ä™ kË% §È;¡bO "Á] H]åw¨^?-?­F«#!°®¡ÅZd¹F³ÔbóðÉ0Ÿøpäˆÿ/­¬æ9Ùx`ê®3øìuûd–)°OLɬÙ<:ïH2J"<2r‡=¢—`}Ì!s[’w­´µÀ¥~¹#~|ÏåÞdɪ)BzÇ ‘™ãðkÕOº8Úœ [€&¬5PÕ~ ‘¶lkPÛ>3w¶Ð;}Ë1]J‰Ü•ù “Uö%÷’d?1v2Äâæ\Ä+"ÈõŽÕØÿøü.¸ h¯ËZvO*ÛRð`«Ú$¢ÓΞåÍxvì£^V|z‹%ÈÍŽ^Þ66 Ð_œéOòhfü*ÌÉG¶`xR0í¿)Š*žq‰CwSHõ(ÓY¬A@žqwž@É;-ºr¿ ¾ù2Æd ó>‚—1Ë”Ž!7‡âAVf˜¶`öíáuï'³¿5: RbUÙû×qè<Cï#qè½$ô Q}Kæuw4½b /îï­‚-¿ølQÿ^Hí³fW–ÆÐO¯F1…åÐ$G¬îž‚oyÖÜK¯þ‰mpäñEøÛgɼïÙ`ð|sv\vi²¶ÇºÔ¼)yžÓåˆ@:t/òbËO˰РUÁ;ƒ]mñÞú”ÍΠ[¬ÿwÿwSø Új~ï=‰“ÜaõþüÉï¯pWEñw ÍÂ… ¡}ì9TX•—"I`¡÷´×³Y^yààÙ ­&¹Îvè mÁË:6¬óž}¡mÁFÕà-¾sœgÔù ß“CËŠzîqÒcîàï °v[ .U`Ój¡vœ!›ÿ¾‘f®U€1wœHÁ£íªìÒ Uæ|Ö…ñ­bPqaû‰°¢ba2Û{ û’Roàð* &Í&±!©t„ÿ„ª&cOÈ1¸°:ÔJZ)ìŽàžOH ËÓ%™ÛÞ$HL?I'ÔH“C+!ðÚ1ðš2ÒÚfcæzuœ± ?LÀ-£Ê°¼LüS&“›&ãÀc@•¸ðëéÛ5WqL¶(ù&/¢\Žàç½O±7=K<ÞÀ$3Tß"Äߤeƒc-‚€ÎÙ cŽQ¯|€u^§Qh«*q”l‡[§JigåY,™‚5}ayD4ôS{RñÑ$”ò8‹yêlßá[ú[ ÿ«{~sÝ7þâ–]Y3¼ŽKð¼$uA¾2KZ™ŠÍãÅFòßJíd[+H÷­$Òa¾—ñöÝus·Âú}¢D¼S˜tìýÉEväâÂ+Ò0:ó*>QÌ˺‹1"’l[o.&»óÁ«?m~òÙ8†v+êQcñ š­1fÒàpL›%,7ät ýH,_„=H.àø’žÐtB‹ü;‰yvÃMœý–-´òê|ßw ýâŒH߉Hü‚0ÝÜ4q>ªÇJ¢Šÿ)\§_m·®æ –<…]>É0¹ÄŽÝ-b"›+a(a l”çœ%Ƴ͒_a«àjv'}"P tO…5v¨rMrÿ™Î`Z,r‘·#?B“K‘™]æn‰<ÀÑ `в£"é…È+x O»¯(ÃÿõÐ?e%«5s¿º”I·z*[f‚>•xLU…Vzíã2ç‚áÁ{Ð5t~ FpfúqÃ|o(AÐßäÏùºÈ“º] tiîeØãîŒûÊ?PÏ6pÀËÔ_A€Ì˜@D¾§À±ÃjLéh":Ÿ皊§âZÅ{!ËßæÑÀÄÉ£pºÛ&´˜³[ÕŽC+µaG¾ž¦siAÙI{ÜuG|Mµ°Ùü½uD õÙút¶Ú&Uÿü–M5œíÀ)þ†-ƒ˜¶ÐŒ×<ëOì *i÷˜ö›™«Sv§«ó¶ÂÏò;”ÁÝI„œÓZÎ4=âæÇ‚äærÊôÀ0=žá„süAyÌ]8ï7‰|Ê4b÷<%Á35}¿ÂôÒ¸¯¦ùxp­S5ÈÂË“Ø-×þÎ)©vF »0k¿ê²ä’+˜–‰ßWóuwö‚Š€5»ö:‡Nú!Î5Ìem—Ù¦™ Pû}L ¦C=»éªJu"¿0õç=à®>Ã×WmhóÄoho–ŠVãí†çL9ýÄÍã#ö×ßQÂxðžM;E}·ÞC'û :¥{}Ý{ 7†¸õ8î‡{—„Ž< /n«3Í ¡œü*u²ÿA\ÓîÙå¹àû7œoóÞJ«¦±[þgAtô tuÐ¥= ¦v\¡w¶^ Ú"<¬œ¿—=9iƆ,±­êÿð%S$.«2ðñ–L®Ð½×üK†½u 1+Ù‘YFàé7ñ6Uc³4÷²1é? éÖ,x•„%OQ—ÞLö!}ô¾Ì-²è€“¾LÂè"JŽ-Cw›Û§›‡×Þøáݘ«4~· ᯄ=Ý\nýWú½©7­5Fq…g(Þ(ÌJB÷ûãW@}ŽÙìšš‚¾T¬&ç×o`÷Ïòxõ o á|Lï³ãÇ­á¿+úðeõe8 ±0T^w ;sª%›ëÑÀâiuféhÒ¾Ò5Nÿ ¯s¼!™%C…X+úm˯ߙ°¦b h(Žfk—: µ3Síz0m]ÒÇ .êìÆª\ܾã4~OËåTݧûE ¹Â¯Å]6²,Ãà3§8Xi&bDbªéuËmÉý ;>pM6©\ÂýèԢƾ”ÖóÔ‹…éîG-˜X–MÇŸSƒÿßû³ðS¿.—º(³£»9ù“±_³‡wî}:M2YÁo·&ûg¤3»2yû|§¡Mñ#öï»x]¦Æs'SòðÛº»Ðë(ÍîÒ<û̹k`±»;:ßb"»DÙúFÐ}êßïp£’áô ðkû[t¨Y¢§ž€ /¯êüÁ·ÃiáBI&¹#˜ßP•£{/€¬ùùüæ4ò‹º^Ñá1ÈÆéá¹I9ð'7^Y–.cqòU>Ú-õ€³UÇ r_ .„µ ¶ j#HTµËñÛÖµø¦ºs×äAÝŒ•ÈÖ\†GGdHÄ70‹n‚ä¾^nö»:îåÑ“Ü=§g:Ði©‚ÄðIvlrã/ÌTc‘¦éœ=dØÿ–R ÿØ¢(NªG –?—„S3þÞ¨ ¶A¸¹ç ÷¯ò ¸}m^x<îüúöËÏÅ"×(hýhJ­=‘¾ã ‰¬i ³Ö¾62Œ+[Î<åíÁýq0_^x:ØÝVf?¾6‚žÐ¶až<[FàÑ£QÐýºžJ_‚)xÂÚÇÁÌ£Ã9·z‹ŸÉ͵Lå–Y‘ËsîÀ™¯»áÄŠ<üµRÜyW€™+àú‹o8?¶½la®a¥œ>)/«á6מ$ÙZvä«¡vÕ™’Áùh§bCLÇŽbã·h³ñ†G )hL"ïüïþi îÖp £Äqö j¶M•©›ô"’@ÞÌÿö¿Ë/aK¥ ³÷²»uÜPJ‹J*öÒ“N‰´NASÕU:2ˆË³C`ünŽ,±„½£SaŒc ú~ÑäEiê`Ž‘1¨êÙCÑËÎi5„×RxClݰÞŽàŠREˆÃ°.›þG…$¾‹Ä^YTä®.Ähý£]Ÿ`ÒüZxC€ÍSâ¿]ˆ´{e¾óàPj«:9ø!M+wP¿Å\°÷%n^^u ~‹ä\6ù&˜ó ÿBó×í¡›N(ÀÊ:#²M&™KûÕ=b¹ãW±îG­d]°ñpXÈ)±‡×5Hfèy¨ôh¥=Õp ÷OB²—»¼EáYë`¬½(>ë[QçÒñÐGeìzˆ % %ÏîUOµ? «Œ'à߉ý0ÅÜ>*¦€Íó0’ôú>¬žŸ€ ­Îá–ÀX¼sf!¤ŸÛG×Í?GC»§bÕ5y"œFpœÌuÞ‘8q˜jÿÊæ¬‡xÉ• ¾_–ö àG'<©rf˜ï'ëóJí¢{â_Øà3šœ°‡õº88S‰”nÇú,o_pÆÔa‡®3¶Ç°üjN0­&dŽÄ|æÙV?¢t*ÑöZ?7ñs).ÿàH§ä§`ñÞ5X´+öö÷·uŽe53ßÓ#焉ñie<úÇöމF÷iBXmw† =Ër|ÎWGW;‘êžpPw;ˆ¬x _vø· ÙæíÄf7wÂú°±ÄV9«¢oH$ÓIEY²_ûf\V#?2ü逵*«ÛÇfî~W–°M—2©ÕÒ*H31 I?c9±h=âÒ:Öæòaí/Íó/ïÛ¬7Ø=>‘þq›øã•]͈´,ÿ²ÅáŒ÷'‚¤þ3Áéò.nú‹úP%…¿ï• ç$IÐþÀ^Ðø…+fj1^Aì-]J­4Žû‹"øûwüV3¤ý×.Ø­û}¤©-ÓPÇÕâô¿Úh¾ µã…¡ þµÜ­‰j$¨ô7”vv6ÛÇXçò+¯A勨q¯‡;=5Mó?sWç->¿,<õYœÞ²30@C¸ŠB &?ÿ‡}õ‹mÐ9.’$ma’Z¾¸4 ´#0õ]4_µä6^2ŽäÎjÚ¡û‘p3ÏŸïóá'—"²ž‡>·×ûtŒn·—‡ÇýԒÀ£7`Íê–þó|™6ñÊl‡EчWœ¹è&îÜÕä†K0×Sâ³7ÎÁžoé¶Y:ï™bÔYköaNõÓ½Ó@9¸˜^TÞÌØ´‘)´„âB—Zxο…ž‹Ãa½»7ÔxE Çyì¸]=ôø¦•bFÈ;ÂÇ—öçqUˆ Kž {×Ñu7 jÛyŒpQ'7^åq¹Ûùè½¾]§ sÛžŸÆa`沚؜7#KJqï¬ËðùÍ,VaÊôÞÌ ›ÃøÃó†¡k {€})ºTH}Ú‡á±æäÊÊÃÈ×’DE9®åó|x}ìÈþÿìI46{g9qØBt UP|Ínð€Z=-ü#G¯½‚ÏMC4Æ&Å3½Èò2K~ütÞu`ò ßj4é•® ”zgËý½8ï§Žè²,Ê2®çÃ>Xã> ¯[•Ô,w‰j±‰G^ÃD•dèQDu~5Þ[‚?R|°ºÈÆÞ󒎺؎ÿÆsnû.`pªìXYÄ–ôÃäÝQl”§: jŽ3ÞRîd,ï8Å€lêWbKUOâ‡DôÄ”®^Í6ÞYDÞñ¡ U ™Ënâå.Íæ‹¬ã?Ú[0âÿ_F݆ÎÑ~\¾šëý‘!Wb"Ñ9ó—ÖÕý¦µÕñ^ªM®U¼âúS™ÐØ9Üö-Rlªû6»9šUë¨À/)ÞËžPZ½ÿ5nrµ…èµcq‡î8ÐW±DuÏ¥ gT7’RQ×ò*qúLåÁMüU6—¦¯${OUàþ.?¼û„ÝQb®ïÐ,õ9lŸ— ;œ€La;erM l§«(«þ6Þ<»~Ãü]ŸƒÊ«ëè › «‚aÕ‘!*òÌ“-mÅL—_àÝ/KãxüûV¿©q&#'Ì*/­!uópÃö;#þ?~ÂQþº>puÔLÏðæn˜+»Jn¥³äž6ö[¨mìCýÓòøìÊ':õv#•æÌˆÊ_òçe$|d¯±­Áš[õd<7ÓÆȼ’Õ\†GÖíIƆþˆŒk‡!·q¤Ë÷8oˆŒ©è1f"+z:†¸=ÂÒåž &¢Oæ1Ø÷:‘ÍJîã~”ø£k®;±ÑY†/{?ƒFO&¦ïã7KûŽì~Gý|þòßÊÁζ(°wV…W_BÚ\c˜=dEfNZ—R¹­gž£ù§B–©‚´•cwÿøÂªc{Gö¶ð?pZî‰(º¡¯Ì’åwΗ!Ž\ª­ Û5= Neh‘îƒÚ°D¿žß\\G<\ÊaÇ9_x¤2‚‡õVöŠ™Qê0ÕUnN]eõñix!ó=Ÿzú=­ÙÃÅ |£M˜é!F¬§Ù’¹ïç‚Y¡±—Œñ‘'`ú¤«¸Ó^€­·‚so…Øb™±¬ÍXh½“!ü±àʬc¬½ šD.´f2:ðU<“;Z›ûþɰ–yÕñ‚™è­^ÄuNbQiôæÝIìËÔ{ [Í2¶1ËóÌ›Lg³ûdzí÷ÐÔ72wñÿ…|[îJÔ4¶aŽé£´þvîN6Ÿ´K`S5h¤¢X¾©n¤ÿÕ-V߉ÖrY¬êY8δˆÃÐ[ó(W2È%âœ'˜úI޺ͦ\îÒµ¬u‰©ÖÔc>AI¼ õ*{®ÅöY šsÿЪ–ñÕu-KÑ23i¥È‚õÐ58+) ùyËQÂĈ¤GÇÐ Ù=ª›å6°»¥ÀÙÅšÈÌEX`=ºa3ìéK±?—¼Uò‘ŽÝ1Ì¥|ƒ©~> å|æ-ŠZO?~:ÖÚMèÿø.—|ð7·»Ø–h÷Ïgù'ô»ÐBÆÝ¸¼Ò R2U4±_«έ›‡¯ÛdP¨$¼ÆÅ`By8lýB£Åà®f/ç^ó H£‰}tþœóôð6a¾œBÒÚ2œ—C³W â#ý:Ê ÍÒxS5žÙÙˆ<ºaòÖbš°³S=Ÿ«vüŒš]ºpXŽ—žæÓ·åƒ´Oc"·±HŸ&fO†¡Æ,sL‡Îþøï óCÑzê%< ÆÙSÇþËÿº gìå´sãà×âåÐ0Óà•$+Uàq~Éóñð4y^ 'wJŽìšÇƒÇÉx_ê¶ŸPâvm,†æ˜w¨ô` ØoÆp…ƒø.áõï785zø*z5té»)ì˜âk¬pR¹÷ëÈmð¿»Çé¥Du¼sP„žùþô$±üF5÷bVæÏ FÁVw6ÐVÏ…1§þiJ Â×ñÿÿþí“˦Áuº¼T±Ü¡(”Ðߎ©O}اO/áµé˦p½qé¶]‹;2Žájƒk)%K~X®g:c$HOé/ºn‚™sc#QÛ”Š íëh}£•½öÎïÐ}ÑŒ²Mí»ž‘2I–ð¹œ/™ý[ƒ¦@áÊé¸*.‡ ¦á¨,hö¥’Ãë´Ù޼ìßËåGƒÔIæٰöýg¬^ä…µ[JÉ÷cb¥!Ï-Cè¤Lñ½6{k+|¿@®Ú¶‡ÂŠžDÈþù¯Däs©æÿõ™.ø=Ÿ·¶z Iú8âÿsß<æ4BïQ•µ-TqÎq°k³ÇÇ£{è•3ή"<ôOà‘ñò¤iï^˜6},&‰J°«k ™‰æ¨í¼Í :eƒOÈ“Óñ0íÍhh·Üã’ÉýnnåÀ/Î#ç/t~iF·g=tÅE)îúÙHîLÍd–xSŒ[šLš®?„“Ï¥qQA$|™yÆl©£ÆAüÃÛœƒœà¸/œ½nNø}œ¶[k1Þ|7ûÊ_ÉB×ë P ñøiV¯ß"†Ð„2ÔZØ "MAô}›”1 uôéq+vî­ù4k•Ñt…[!¡#ügÅÃo\È…ïô¸ñèKaýÛ©ÎP5è)J‘)‡AÔ‹opWÝõ«…H­ÿ$²ðU#®½,NLVêÐo_-‰ðKœVÍmY“DË=*p󵿔_< [ßLG‰Þ›}þ ›´O ^{‹ýÈÛ¬IÜŠɵÏ1Ìi”9oÁ­ ØZ9è|Rb¡ëãâòµ´Æ–¸KE‚®& ìŽÁÙü®áÐ,ö}?oCVU©ã³âø`–ö¡5QlÊ„EÊ*ä_­$ñëzOœéÅ%^lt­¹ø´Í䘖¦?OË0ŸI\?Ø­5âÿûä#p†£25y˜Ÿ»‚Ù^Þ…Äöx}†¢d¦³ õßÞäuãïWò„——pzI0æmðªì›ÏÖ.ã;×öòà êÑt©.eÒDéõÌÛøž¿ÛR”‡¾ómÜL‘ïâL#ÖNÃQüW¨u÷56¬NÀiý¨ŠÇêáõÙ‘•?òäÌÒšiJƒ6ûUóóÂÖ£_ù2–ÑnÃÞ9ª0Íc†¸Ê(˜;$kF¯L9Ëbm»³æ.ÛR8æ?†sf5Õ5Nõ¼­ ¦ÞEÚÂæËÉÁØã|ëÌ!$”þ0Óá?¹wø u@Gý ýsU?¹p‹ò´¨¯v šüq‡«y¬³X•­ $¡-™pü*@ï¬ÙäJò?<ë0ÍTrÄÇm_ g»5˪ARôîóì¬_B¿áÞfÃFn@‡á7ñã¤æCßKäÕA«œ&[ÚÚ‚cI©½=kyŸÎÌEVÂÙ5”þp¯C½nOÖt%ìN©²¯§>о¯µdÿ%QvqÂ8 Ð8s?“¢•øðq67Ïsø+}åO·Šxôg÷öœ¢¡ÛÞ¡©å|?-Äňzùn6çp(÷,Jœ(ª<IJ5Qüÿ¿øþä lá«épÞ¾Ú×Ë€ö†œ‡•*[6sù!%†Õk‚ðÆe Z~cn´s`<2ÿ–|ùñ×òך¨‰ùsîâÌëu"ËhÑ×½²0ÃU˜ÔüŠÆÓW¤ø«*«}»@¿é#¸Óÿ‡oXx ÞIr¥CbðqõD<í¨ÅùEè™0ž{¿ù8ŠdŸƒãžówý®^xâ¹:ž=®…±]¿©Ú0O;¾¯‚¿üÑ^×üª6y"Y)ÿŽF}„©iA`_vÕÜÔ9ó÷‹ðÌòòê$r„ŸÍÓžÌ?3‚]VzŒóW±ÿe£>ÊÞ-ú@ãAe}&ÎÜDØ^éÍ8—q#ÍÅ¡óŸRÙ6\ÙâIz wã¾àY5AÁ¶ñTJ\)½yÇÛöR_•ÀxñÀh’6§ïKá„7ñ²ä xŽq™aÞÑm0¯"t' ë«§h°ø>ü‰Ù+¤pwâ ¤ñ‹éälºß!<™Ö„ rñBÝÒR¿ÅÆHQ©±÷í 4`®—Oó¦OS™Ÿ9 Ù¦Ì×®ð‰SšJù»¼Puª8aÓàX™,QZׯ—Œ¤‚ˆ£”^óæt`÷fг­È>Ë1‘¤K‡ªƒqξ—tú4I–4 Žé¢DM5ןÄfCU×NÄÃi 5E“0§h.Ù—‰–OeIel6µpCdgäÑoN0[ë;V?J¥óÍ/cFÒš35çÑc) r¬ØI€6¾Ñ@E¿F¾RÌu7lßI»REHúªu܃ћÁV¥ŸeYãÊ—»±ÏÓ«õt¥‚Â5´Äâ#zý:­—„ÕÎÂìk[.çpþÏmòòÄõùߢ_’¥ªäð]RAJ¦$M¦’›q¹ëG‹ÀÛBœÌ>y Çm^BVɧs= íM…¾ÒÇ{¶ø¥J<¼=› ¶±ßÑöÝ&6ñ”-Ù|ù"ºŒŸÂ&ÝÌå?dŽ·Ë{p¿ÏtV¬£š‘ýçÙ:N¬Êh½Ÿ?ÐC'³à@} lëa°ä ±Ý~‰³oïîF™ë«3”›XÅ·M”ªDU.$µÃ\óc ž™À!^Íž³8«³ƒÝpœÂ®-ḚKßxí£YØ¾ÓøZT•Éoüj?MÉ€B0gP2‚_ëz.ÞŒ!¶çÁ«3›íB 23—ߎdžs ìW×[q=ùý!·•¿–x‡°ŠA_¢rá˩îCZÖ1œÏÕº·#L4\ÇÊÎŒ'ÚŸl&DcçÕŸØ»K”ÔZI'Íg8kÊžð‹Á£r4×.FµÔq,ð™/•ñç‘ïŸ_O—éK¬¨Cf„-;Å›tR—\èšCæƒø°Ø†äÕwƒ´½*¼É¸ÍPz»œSo2Á?AH_Vˆ‘%‹,á s0÷eåSÌ™›Ä?^Z X‡à7üoÎBñ±#þŸâr ‹ížQva%ª½åοØ¡FÖ vW/;‰XMõ€«¦iT;W“leͶÈÎa‹ xèfÊìi²LQeâþfLHB¡5÷ñÎ[žv?^‹Óú¯f|æÙã¡¿óÉœåGˆ@y=\Nç'ÉÄ¡dO>ÎQžNn6¢ò+1"´yKNÿ¬ÑÉgÐÚØ,3‘Ýu)^ƒæA'Scsèáw‹8}sˆøGËOa’n¸a(oK.g_˜:t +žèÀk’ƒŸ—ëÃ)ïX¼uƒ<Ï ×&rŒàqó–þcU~Ù w0ÞVÏ7h{;ö>¦Õ÷Áxp=ߪé”Íà‡wtL¡Ûå-çü´HPÓXR˜^‹Æ§Üª]ÃŽÓØÔérâáÅ“8PèxÅwš‹…«9|r¢îDšÐí:{¡ù¤ÉRÂ?–:¬ÚS–°ý_0"h,ZZʱÿê–gh×ÃïðúZî,÷ð‚6 ž˜„]:~œê[-VØÝôѬoc uÓmôt_^-Ž4áóxŒ¼ÊþW:±$&ž±B›*cv×UeÏÎdƒï·°Ôîûøö:Gž8L„µs 3Ù—<‚ÿVW;Aä]xyl?5w#ÓÔ"‡ý«5XŠAß f7gé;hGß]h&r.عw2Sȧ¯ŸÁ¶ ßÁëW4³Ö"\˸üåTž÷B‡þ¤{›"ûs  ÿŒ¯%ü%Q÷ák¸×I ª~À„w†¤'ü\ªbÿÕg·I,€‰ëÜØÞ°ö*kUgˆU‰&ëÓPN[„AôS4Š‚`÷phÌßÂV„ÐkV1}5Z%%’Ø~òdGÜ/×gEÇ>Žä¿Ö…Ðxž\¾ ošB@  µÙÀ†¼¿‰‘Šñ“‰Kò},œcÞGfÏ@é…ùøYé}·¤ÍϬìxƒ7$á"ÛÜãIšògQõý˜Z)‚Ÿœ½¹gHáçßtNbÎX€¯U"a|ó1ø]ñ£6˜ÃÙú+è=Q‰ìˆ¿/|¿{-Pú”[/×çÒ¥(®‘+š‹ù»´•ðVîºiR:¯üÈ[¾ÃεÞEýMf« Lœcuجó!4OÍZÚNS“™jÌöøwØ_ˇ23UÖ£v GëŽè¿Kˆ}®Â…WÂËOžÀÏɰrõò©í>/<Åɬ ½-~\k×°–”“%©áS‰¸õ²>x„?O#Qs¬Y°Ox,ŸJÒµ¤Ùƒú°Òå8è‘Q,VÎn Æ‘à'LÎÀáëQ)êI<ßháªúzhÖ‘BáŒû8UÑ€Lìwd××±HÙ,P_W §Þ¿Æq7éY±ཀYš6@v¼jdM ªÄèz®jnÉWúâ[1½Î¤é· IçÂ}œÿÚq,.âò¬‰À–¬1à¼s &Ì£?\GìÿdË#ê㻈N½¹œ™[nþàD®ôÙK°*ä‘Î+¹Ü޼0µ8Æþv}~8í ºXku úƒU©µ½Ù}g’´4ÕG]Ä&wªŽžâú¥Ú¸üýâä {ŒC‡„1øùšêEí@‰u¡à+0šì]PK¶‰1Þ;5v·gØa/˜fo‡«Í±¸Ö'?”£ k»8Ì›F ºé¤q"9ZÜ Åùlwür~D Ü$Ae]*¸…h1EÍ;4 #ÎZnbi_žã!cI&캹üùx°Úž$Y’ì/o±Cªnäþß>ÿTîÚGï[A[<€–fQö¤@•%•²¿–ñlm¨*›µú2UÓÄÕömFÅôÕ ñ¦Ú^ZaŸT<›ô¼l>UŸ~—‚^ÝQq]= œ«È8.¿Ä–¬ú8›=Àôž]€r +V™’ësÏñõ|Þиý¸%Cœø¤QäB˜®}vëóñ§S*ʼšÂþ;G¬Ý%n›óJÞþŠÇÜ¥mkÉSHnAŸ3hž 3eU2N cÚ½œ°. û$yõB\Iúi\÷<…Çu¹aD°œí¸‡wÞ„ øu²ÂÀÆÇýï÷ÿî³<8:É -D }öbê "™y–µ”©á9;iraa!'lq‹*/e›6€Aò}Jƒçße$¤Ì…éŠ á‡%%àò yðwþ5ÓäÁ÷Fø)™J×þÙÍ™9ï']¾5¬ðÉ'”>„£jtpÚ™›Ìóx¤–›³}SÈ1%D = çßÉçr?ã×äulµ¤ þN{ݵ‰|Šq:lA¾ÄÊî Ê_öQþË{£ÃŠ¦Ë“/K¯c­øa2ÎR€êõúÀØ1lˆ\E…e˜p×’ ( °­?ìþ¯¶»ròy\ñ|ñÿÙݳÏlåöýàö—…ê32÷~4ª^0†]OàºG u_|;U:áÀTàÄjðÓT%’÷ö ö‹ab&Á G+b¥±“}ð$Þû¯£|À;g8d+ÏvÙóC„4O«Á•óÞò[¬%Á;ùØõªOýT¾qÑS˜‰¶IûÉvýëèüýv¦¡Bþml)éàJãké•ã*ì£Õ®~ÿº °cjSж5Ÿ¹BÖz}²N\†ÿeˆ?CLd7rã'·9ü 5H„šH5–8aëx€`ø`€~e݇ŸŽ¬~ß`Ô‹å(ä®DhBŽ~ò–;,€IƒZdΰvºæ|^øZ‘M¿ôÉÏÎx¶l®27ƒœù"ÎüÒWš´R.ß?Lê&w…ëYÞãÜÙDÑ/ ^*h³¿ƒ‡y¯#28ÞR_è‹epòn q.GLæÁÊë)œ @VîL º¹Æ´¡ï1n¶Í¥ÇÆä„nN€«"£HsÀu3LE…[ÜŒÈݨ¡¡Ãî,yø_ïhÎ`b#š°H|;µ4”…Ø¢Ô4z}} wG_Pô;Ä•åÃ\ž _¢H7Ü5âV­ ‚Wçÿò—ELÁ7ö6¿w`¯®O‡¤.y²ê™°:ˆþªHƒïz¯éþ.!¢”—ƹ¬½ 3¯A_iUȃ»·¾sò«œq‰A4Ïä•ûºt4ëš 7ÇdÀÁ[Sù»<ç@÷…ù`}µolß åG<¸½jdQÇ5¸ÛSÌ]v¯¤R[w¡ÙûXh¢EЉ K³FŸ¿<îwa¿ïUz.¾FB®°áÉRóü2ª-/ÅÇ'`Ð\-Rý¡–niùÁ/9QEÅ-OaÀ|¨¤Øé aW~ÑÈØå¸tÓ-PôÁ/AØæ——ˆ‘œÒç\Ãôø‘üWé<šKØãÁ[WˆÑT›%ij°Ydó•kUâ0|àÕ瀷6³úƒ›‘Ù/È´rahNõ9ý™vu©Ÿõ· lQ)wtŸ[|Xò«Ä¯ò*ö“0½@l®4g„/ÐÖŸóàŸƒÄ_1iÄ¢úÚVü)†¼|-X²jjÛ:W UÈ:‚¯¿ØׯUÿ³‚ãÞÒD€iÄáï<&ÁIïù×ß Ùu ”¹üMÛza×4‹PÏ“úðÌïTì ÂvE-°Ìª…ÙÛïq-gfS“þÜüøóÓ7ìü‡{å^ñ"ľÓ÷·¿£÷Ì8HJþ‡\ǪÚt¥­3û’v“º¨þ€Ïw²¹µG¢Ðú†0yʶíÇQÌ+V’MtÁêôö™øÜÙ7z1ûgÌìZ8ÌošÀ^Fk±'÷ÌØñé ´8é!vÊûà¤Ã@0Òœ©n Iäå’ï뀉q“IŸŸ(ù<%—®–ˆãþv,Yÿ£‹ŽÓq/«i…KsÀQ¸ ‡¯O]Å›ÙòXûx&=êÀÙÏ/Ã#Ÿ+05ù"'ðq-, 2 sÛ¹CÃ9v©f—溊¾éË‚ ×u8ãù,†G_r›Ï§aM•Æ• ÇCì{g«BrÒÙ|¾!ˆf»ÝQû•;_}p$ÿ¯ïýÁMTy 7Ã긷5×!É²š›ŸØço5áçSð½n þh¼½ñÇñú,KÎÓü ÚjáåöL·Ç6p7AžŸ$SkúÁ¿¤E™Ò'Øc¤퉙0Ë@ˆñ- Iäùg ¤±»<ùTëÐ9ì² :0z‹Laµü~¤F"LÞNU¾2,дÂ<ÿB´é|„óîÕs/ûŒ0|ÜP›Mš}§²™ñÎü`k7(º{ˆê@®\!ÆÞgŒ%;5‚i\@-4¬—gQâg0æÌîÀôeäÒΫtùŽßþhÀÒ°ù#ùïGb>üþ…nHP ‰ K`·ÅRTü3÷~ÝÇ#n7ñA–5Ùj«Â%×+¢ƒã/nvåzرT•«ŒMVwg mY-ºìAë$¿r{:бIx<\!‚˜Né®T%-Ó!Ê[”áÞÜíT_lˆîŽÈ{j°‚+Bú7ï^õÎâ .@».©²÷•³#Åø1ù˜U¸ ƒ•Q«l>š”JÛ,ÉGÍ â3_÷„5÷eùcŒyµ“3MžÌÚ¾ìW‹sW/Å Öè¼®]¼Ë¿Y:š·uá@¿3}b=ïvÛ𻇶\ïÇÕ¼m‰½Ðëý‰ÎÏ?•³ØGéT‘¸Ï;WlJï_ª¼Žñ9œTײÁ¢ÚD#)-´å>»©1CÓ5xv¿9£‡zÅ à£Ycêx‹#*9h–Çëí0xJ]=ζäÃPCØWåpŠG½ˆÚ„ë¨l½ ªŸ¾ãHÀX™ßŒg1ß^óê²ñçaE‹/6 Lº»±@WÿÌKÇœ °¨BDý݇Æ3”!7¨ÿM gº¹çPÝy:Zù+½Ùб¢zÂj©šw6 íÆªOÀ¸òÏHü2YŒê¯ZqïÎ0 üÓܽwí\ÕeööÆ&xä)B*ü?çþ.8º¯—Îø;¬786H‚Z—?\úàV?ÉoGeûî—Ân…^œ°_Oä#az»$‹± c äEO2Þ[ú ¦†â='œÒ2ˆ]ÍqØð÷67vmÜí ‡¤0iòcÿX¼ûR€•»þñO(×UëÇE\êˇ\Th|~¸ƒíôÝ9¾äf^¾÷‹¡y£¶£(½@MÇeý*|N.™þþéȸˆ 7ΣÍëûèï¯D6ÉuÃÇÄ»UŸ2&sÖ‹d!vuà·›è{(Øvx.¿›NX6™+-ß[ýìB¸OZeˆÄÂdžŸUž»÷ª uv ؼêà½kfjÞw¹)çè‚ÇÜ·˜#0q£È3&6ç*·y÷MÚÖ¹îŸåó /f Ñí [™‚ÁW÷@Ó'E¬/–`»^Êø¿Ñ»û˜¶w¶†ÜG-W¨Q›!]"ã—,FÏ{pÞj>Mqþsýqó1Hû“›ö£Î9Žâ£óÊÇÀuÒMk¯Ùoñ¯CÃÓ}0ñ_-Ò’ ©ñ§éÝ>URÕ0…’¬a‰]ПÔÀ;èC´æM‚z²‚-$ᘨ9qÒq¡yŠ0þÇf•¹–MÃ:—ãÃó;¸µVäŠÃôWž‡ZÛ¿B……Í~›bE´8¦LTÿ†åk7Ñý+«¹)¹²ø­·ìo “È*)Ö·ÇËøÛKO·ÍŸ-¬k\òLMØ2ÅØÿÒ„ÕÊùá×Õ$Ó3R7CxUÎ-ˆ— ÏÆ0½«©¨øh Ù>žÝ٢η·†±Ÿ úàò/5›¤•Á ¿Ž–v¥ã 䘎Öúïîwûs¿$Qúþr-AÍÛá5Q™ “}%ApSU¼;(ÂJONfµªQø‰>ÒßeÖž‡ñQðhbL:1qîbdÎæ¥äaãº"®nIܦ[&C¥@æÈú¿Ä÷+=™È_l¶”†n6å7m_ÌÝß›k|5íJ·¤"'~BOÏãöL¿Š± ÞoŽãÏm0Ù}V(Uᤠµ°cH‹ŒÖÏ=‡.p « ðbR¯ð` ºŒA¾0~µŸÏnT‰2½yaïŠéÛ¯CÙîÙ-;¹dz ÎIGðê¿,€ÉZsù¬–¥í¿8b{½üO೫Šüƒ5‘cdŒ‹ïdÒ…e£ÈPF òNŽfoz6r?³'a×fo®Ç´ .ý˜Áin~H¹Obä’7(kÄÁ!•¨ÆÉ6 3ýÞÑÿúG_,¾0‚ÿª¹ M*›Äë|ðTöÂÅš‹ÈeÁ±lçr;²¢ó?ÚfâóQ´?W‘ôWçA{ƒ É*/§Coq—Oÿ‘¥žyr˜û{¬¥Ê¤¥û ¹§"Œâè4¹³=¸ìĹ'^sÙ#=Œ ÚÁ6M}!§^pmÙ-PjHؾ‚¬Öã/éY†dž¢áj"½_…D驳×V±1Í]x[؈XUl‡áùbHUVsÏ_”R›%ÓÀ¢Cß¾¿ ýïçaìkSvVÚäVqVʬÛÊb†µÅN‡Þ“Ññø1§¬ëZGð³”Õà|è Þ»üØŒïx½ûž*Þ‰=¿Â.~ ¨?æEx÷.§p®éÆÛWow' 2‹ÄPö.D™ú©ÛÄjztÂ1ºâtŠJM¥–&’äÒáÓ¸zî<ÿþçr˜Ägò؉Œh³Z o4çF§ ±m6+H›ï9ê{aÜš¯ÆO߈ .t`ªXX ·AÏ?;¶Ó~³¶Ô!n ð^© x_]ÿ¸ÝdX>«—Û+ ÅVk³à0c}¦Äe¢t4fuNßpGU;·ÐÊõâ<¨‹É6»Lf„ÿ¹=•ÏÜ@¾ö¯½PÞ½Äv?ázÊàwErRçÁ`å!®ôb0 Ä«1qcàÆãjÌCBm½Çt QnW W`ûî{â­ÍbÜý^>X¸ £ïÙ|Ð_dÏrkè®O÷©Sþ8îmí<\qþ7¨~É@Sã“0S`9™¹ÔœV5ŸAÉQÄõçtð•oäê$èš¼·OaÕESòsÜ}ï®Ä4æibm‘½:×,®‡Ê–ø‡ûÂm:Y^O2àêÙ~\ð*ä¿ÞÐvÏ7>€[£/¤š³Ð?.‚[+LgŸX)qVìˆÈÑú¯µ¢hØ jxé³B¨ŽËè€5O–à÷g ýºÓŽ¥#MLšÍ\ÎÞåÜŸX Ü/®'M£µƒ‰\X'êEÕÎB–P86”eß­¥hꊭ¤ô^4\9î×ïÄ`’ìN(I¾ƒënP#U"¡)ÊÍ2ñàOnŠ"Ÿrº ̇¿çñçÙý»ŠäÀµ¸^ÿ27Þ[…h-M2.d¿%-¹¢'ë0&®†Þ©–ô'Ü8ç¡Î,½m?¦fq¢øƒäç¡i¸ ѺÀ¾Ìµæ¶ Øàú\-ÒD0sêˆÿw¾P‡‹BÛñY…‰XÁ{ðϘ/—!{VÅЮïû˜wåy 7ãÌN[½fö=Ò"E«|É‘f+øЊ¥ÙÛjî·-¹œÞf¯ïÖŒç즟SÓY{ñlß Ÿ<ü8‰ùcY[Áä-weêGŽqüQ,s`§ï;ø±ã6ö=Ñ…QÂÄþL˜)¯%Ÿ„ƒPÃí üíÝ€›‚nƒ( eDLËb‰IÓm\rË„|¸¦M¼æ…baÊsœ•TIuäíXxÉ-pÙ­Bƒw†±Ï—.Àçæ™dN¶ö'œåJ3Îãͺ¤³{ìÿ¹×I[Ú•x ÔŒèù;³}· £J‘8Eóíœ3!åqpÉå%7GR˜<œÃqGÿNãzˆsfVåÕ5™FL2Ó•·vo/½¦Z c¬gÑî& šŒçZÇÖË#ÔwÁï«& ñú$^íÔaU'Ûqïõ Te|& äM«°¥÷Z`0)VÕ.‡¸+-ÐÒ¬ œ‰°m)OæŸ,;âFýŽ"Øt«“Uµí(Ë=A‹IãaÃótZ¼êç05NHŸ†°o~ ½5…ìd°Ÿ%òÓç&aY¹n¬Î…Ð{®ï—Ÿª£ÿG½ü®ã‹!€ë±E ¢ržv$ÁxódP0Ô§!yŽLÊF…º†õ€¯((|œ{Å~³¸µl,“œ‚Æ]„twÎåy\çþ0lrÔDÿ”üYãn—Ç‘˜ ޤ$çîý4ž;»‘{´Û›¸§çŠÉ:`èú /Ÿãœ7È æ%qn˜ANA=/›†þ£ ¼s3Öø»ð¦ÛI³f aò)QTÃøðDèðÅÕÉæol{¹Y5ik‹?‰_=.£ÎË“˜óØ=œJ z›2ƒ_ ž9'ãÍUYÀUd7‚ÿûã휹TVeùÁäÙ¶,ûþ~Ôâ\Ù¢ýÂLÑ)²zyÛÈoÕÀØ·‹pTëiÎØv/íFv©Ö&dZ“i¶â§€>Ú'zÿÚðõ>ŽhoÓ&º3q}ï/ÚXXê2É×ï6öL‚)¨]Ó5³˜Ð–0÷™@¿Ÿ³GG_Ð)KdSfbÌ´¸üà\4¶ý fê[ˆÃi½H檈~¸‰eïù —Ó•?Áôá(vÜA…ˆtŸµ†¨¾Þ†tî´!ƒ+™€ÒDYOo·´pcT˜˜‡[/¤ÏV$ü¯ÿ‹Iò< £Lè‰h ¯¬/aò2}.u!Z¾ZH¾7žGïælol v}*fjBW@ôº÷+ö0ýpQ+¨Ë~¤zWuX£@Í~Û‹^ŒóžDöüË‚v¥ ¢3Ù• ᙘRHr/Íw%ý­5¤r¢5y¯º‚ùÎÏ€ÙaêlÌ=|ÞàŽa‹2QæÅ5¨Þ4 ¸sʰ\T’ Œ|óúkÊ9™¾Tt‹Ä7{•ÑQ3–ÔTx2±“)X`ÌNŽ+d{fF‘ ¢¤Ñëh3a·|ÁW§ÃXÖGp6ql0¹¨|cû­R^>ÅP½­ððñc°ˆçí1¡[6œÀÊ÷S¨mHlÚ.E’f}…q%sÙºÓ1àâ*üÖì 5íèînƒ™w0þc9vÿôU4<îAÂèÙ(ÇTٶƾ<œá…X% ™CÅ¢A.uëBrj YQ/M2‘xüƒñí EÛ>1¶x©ÜQÐÅNáó`WÐ…þÓuA4óD„çòj’JªWUÿ@ÿ܇ô`¡&qØôŸmÀ¼ü´¹¾˜›ð{v¡UÙ%ð9o [}Óq•äz¼4kË“¢Ã/ã/ÝrT¼4²ÿ[q+œ‹¾å@¿mÔfA·k°ÁQs§A÷þ@ QÓ'§lyL~œ ˆÙü´»;¾ÈXñoÓœç[F·ÞêãO´q‚G'…éÂÍgAÌ( .OX OÞ—ëMüxlT ƒ(êÀã‰Ê¼[Åh°%qÒt¡÷÷"˜ÝÝtÙ î¿ÏÇ2—·-¨ö8K¾8ç/ÔÆw…’øqqœž¸žnOöâþëÝ·±Ø4¿æÐQnHúú0¿„ׯ“î!ÕÒO2©ô™•Ø8Éx]¹“·kã??h«K»¬s8«¬ ¼gÒ‡#ö/¶:†ÿõt¦Æ“°å«.'‘òM…Q¹ gâ1™z½åôBÉ‹Ÿ²°ÎúI»*޳/àãiƒ0sÞBÜøªš¿xV ÔïXMÆ»É13McþÀJ¹^<›¼–hþ3[ÞëQ! vK˜Ìø‰ûññÕâIÌYP–ĽŽASgQ6éË<¸‘·‹®õ=Ç-?Ü ïxâd+O&õ`U‡Ñ_ôf øÜåø¿gí£Gn<åk.m‚Ï÷/Á©pâ²~/ÖÄ͆ÏiúléŒáœô{4Sô”&w~H’f -nwuo_ WyÞp_Í|„ÿÍÔ³E 4÷’¹:Ò`3æ1Þ¿æ3œkHÏÃ…¸L |^ÂMöþh2܉Ñ"]%­\ó“LNpÜêz¥JužP2{ öm—`3ÆÄ§ó!£X™}™šq —÷ÑnOŠ8©?ñÍú€91ꬿa –7•Á «x±UQ³ÙÄúþ>Œ•S¡[_¨MQýxî­µ°–c‹7È’ýîرê9mŒü‡û‡µ—Tàq¼ÀlXf·5±Ût*º²A¥ãÝS#ÞÎfÈÓùˆíÙO1«ˆ¡×¥lÜä;{½Ùé¼hîÝ›ºû¿žßÄ=X„q7ÍpÏ¥<8®ÊÅ!pöˆ8)÷ÝZ]›,ÇÔ‹Ûi;Vh¢eÁÜ0å°ù•&È®:4Á陋¬ÍPµÍý ËÚaÁÅ ‰šûÈký1(l³ö­Sa¾§¶ÒÒKWá\T œ¤:ôÏQÜiÝZoÇ’‚Ÿ𥢃k5·B7#Aøå9fµ°‰G†@eFlm<Ãm-^Æ­iFõçžðä½ ÛsÛG³ˆú®rþx¨ÖçÆšW4,d4Ium¡ Ž—1z»5µ®V<¯C.¬€[Ùî°±Æ4¶ªáUukfÔó{n)7šÀÓJ¸Su ŸKâdæý^•…VÂôøeðTñ>—LÄëw§³ ^nraëÆÙqi.ª,""ƒ\5R!—Z£¹3›—ó~–Ä# ý”ž2‰¦Â!0´R‚é÷dðo¼’fV¼áªÇÍ'Mä0ìMÈç”-ÆàïÏ¢,w·>³}¯“Ç»oæDþ«7Ãîy¯æÿ58ïLÝwçœwF² Ï‘á×TRëMs?Ê7´Û‡³KQÅÞ õ>ÌÀÛ?ÀÃÛ—<‹`38a¶Qm¬›³EÈNÃì¹!(u¦ ºLÊy™üMV>¨¶î36Ù•£Ô•±Ä*( õE“qçt¶9\Ô¦Ýg½°­yúN8ýä«“8ɯ‡…‚,Q7€?V®¼lݹ7y 8ÝÓ’5Ÿ&kìRHÀdb4 I,mã ®—‹jâ1=t;èÇ…Y/C绡pKy)3n3Ä\Xþì³8m¬Âä}²la=2ÞƒŒγ”‘´ßðW×á€Áïöòo Kù—u0SOm¸u­æl÷º9Xò<úNì£ ™[°8¿ˆ»¼¶Œ|BÌ]i^Jæ\r6òld(ÚMÕü^j=E÷½’ÃìCŠ„…‰á˜£¸”ÖAÜq¸øÁ¦ŒntÃГ¹0?ÞeDÿÛ<‡Ô!Ü éx¨5“‡5=‚ä× aÜžÍþL„EËíéÍC…ðþ ûØ›¼+ãÍm{ Ï«,aã Oïh¤«a.ÙçÔŽ‡ìÙçãÎD…«¦¶"J<~I\X$B®Ÿ­‡Û8V¼Ø¿SVBtÓªkFéÂeô%!P²Â 3ÍCaã‹éÄá•)7Ýpz¡K&ºÏ ç%µQîègÞ”’8Tœô˜‹´Ê„èç³Àbæm4OyÕƒ98åñ)ÜÿÇ7^Á/9}0vW)Km!€ã²àêŠÕðVªô òií’6P|&XÃ誕Téh=¼½xh„ÿœuઠçãô”3ÌÜu>ËS;ÀÔ®ßf‚{ ˜lê{øë{g(6ƒ 7‡} ½Œ]Í—q—ájÒÂ; Fé|ònÒÒY ¬Öaóø|Å«|Y¤H>×I&çÅ$Xrk-D.Î!ñk“£kÌàuðîRš)ŸY÷ úÚ¹f¤*’}ž3HǬo8óP:ì·mÂÙ¢s‰ùÉKôö¿›èå0ó_è˨K8gW*Ú7…³¡é0]å ¼m,9ÓÑÇD°füÖóИ¹.Ãö>5cÎ6 rÙf ¼ì."n»¥Ùß5—kÜc܇FòßîCe¸ ç>¤ú\äùí\CŽ?{ùPƒ7ç•Øô~iô5t"kâ¤ÑóL¹ÆV‘[üR.çþUj:~ ßB ˜Žß¦Hª–„°€q*(ä~Œ¬ZU_ê4‰h\Xü'ñño°­îT–s åó‡¯e÷»kq‹åR…¶±íJßpŽ8Sw2c²2²Ì%ã(Ý™ÆÄ2'‚û¡ZŒý›Z/gq‚¶N_ÍÆ„EÃùZoܼaòøë¤{l"󸿌=¤î$7ÿ~¥Œ‚ÃüòÎvXæéBé牵ÇF¦/7ŸìüûŒÅnÁoÙ ŸÃ¦âϰ-¸ÇõMé €_w89wzZ.•[Vª ûíë@ªË´U¾àçOÅ(Û¡O¶g¿ÇŸ†G‰ó#ä}™=ž.ŸM1çM®/ÝAW9Õá»­ãà†Ž8N~îFú®KG¨­¸Ûãz˜Þiš‚A!£`CÓ:ø”A«]4º(m¦"Å^løzÄvŠ2«Tׯ¹¾/°ÿFWqA–íyý”ܺ·Æ£ðëpW4…çÊâ(•w•Œ­ðÇu´²6ÆàÀ{pzîÃáÏÂÙ[„ÁcÆw®ãÏ%PvoàYèr0&ð Š%ÅŽÄÿÄÞsÕ÷ÏßçpÆ¿‰tãW7–Ѹ±Úé­S‚c(qàØÔ:ôj/ã?UPÁ¥+ôÈÅyXä1,KuÉ„‚ANb¼*oYþéXÆ÷£wÞ_¤½ÿ\È¿¢H^ó€J<ÃŒ‡æ@wânøÜìÆBWá èt1 ÜõC°éD/'¤iź†üðŽa!¿1Vd.ºŠfr5èà„=¹JP>p™JŽ ½î‰¤fq>~-JÂ’(ÆEs1V¥§¤¥cDø9N)VÕ"›°eoù¸e)ÖÌ4Á„ŸÒdÝ+O7ú-¯ÃFôÿî½?°ÞžÇ$k„aÍnf÷‡eóŸâþ¤WÐùñ.® ”bdL[#úâê½`‚ Ññòc{žk‘¦·Æ?Ǻ#¥XÜ’#lÃÒ%dÏž“d¾O ›üj,:½©y¿O•½_u7Í&µÓ¸¢ê#Øu €7H:'§Ýc1ÉÄDöžëÆ·êdû”6ùf$±+™†“cÙ¦Š X¶}2(DÔ÷ßÍl‚à=L8Èšœ ÐyciÍЄ|¥×Ôàí2ˆ«øÀ.¨!‡7ÿDÕÂ3$Um ßÓ›ƒiçÖ0Y$¸w£Žv†%ü/ÿÿ™Fï3#ûû[™¿ªmµa=‘Sa`¾6&ï‹!ï=€þOhU•*ÛvK–ÅM:Ê.O+wžeR¯áe·,*t.‹ûõÇ´ Âg´¹›‰ÙÜlçÓìf¢§¸I†]+xÁ¿È÷$ª'¾k¦bþžÈµlÚêM˜sM ÿ(²ÕœH}ö,NëØb¤Ë­Î…þ£Éd~QÉÜϺKMÙíþ’¿>–~~ eù*ÿÍ•3~3ŸlÑ2įb,š: %,‰ÓŒ°ì¦*2–ý7þð¶>Üæü–¥™Us6v;ˆèf#"}Td?]è‹ úoñaÝeœ“ð˜œ|ÿþ¸Åà©À™lYb –kOdúEp¯³ùöhÞJG´BÏÂCðe'h.ä¦o½Èmà·ÑæÙBlÌÙbh”ø5žÇ1˜ßCÏ-|Î×Ì$WBôÉÎÆ³|ó­“X˜ã2X€Ê@ÊØ‘-ö?Ì_Ðx’¿ì¢<È–ã³eÐ~qèxUÃÖ…5œþÆ’XÀ©%½áoâ/ûŠ<0S"íàÃÅ'è*¨Ã ¥ÿƶ‹®û9/·E€ç¬A<ûN‰Ù&탛٢L•oš<+r/<„ª½ù9Âÿ§OÖÃ7¯Е‰È–G’ÅO«àBšÑ¹Ëlé¹É…¸Òц¾ÕIÛŒ£Î·8¥a6þ¸Â#Å[¹k cÕÞHÔWÖa¡ãÄY´J%ä8_‡Ô™ØÅH²ãÖhzöV †0×6{öŒÄÀ¤Þƒðÿˆúò°œ¾ïíæ¹”R4Ò¤‘ Õ³×F†2¤B"e.É"4jRšçˆ(C“zÎÚB¡ˆ +"2“éíó»®·ïÏ~Î9ûœû¬µïµî}­½Ïâ Æ¬j^ò—‡ ÿj`Á„P°R§ßìõQ¹ÅŒÞz±ŽÍ=þƒ$z—:ŽRSyêÕw æ~úF†‚—@Lî(öODGm¢2”&Kå³éïL©‹óf¨¸g ¯þ¢·ð?~îÁBªºP•{»ÖξgÊ»ë?šÝ?ñ¿ï=1Îä®ä?·…/ÉÖÐFܸð :Ç €„ž":$æ@Ç9/ìqÍ|üðO«.—ž€–«®r§= ú²&yºàŸsâ`/«@Mrëñ¹ÔÎÖfϧ.ÜDöÂÁÌt7ï Y¶(—KoÓ8-*•í€Ó²ñNk(9¶±†ó×üÛÏ‚ÓÿaÕÊ*2®ÿ \¾›ÊOJþCdê&CÅÙ|¶à0l˜Ëó69ˆ»VqímPaE,gxœ\}Kœ òQ‹×siþç|˜½¸LÚf’HÑ P¼f3¬ŒÆÀ×FY–~t¦¿É|÷/"7ëaï¨>,—g£“ÂÙФ-°lt/ùy„ö4çÆl| ,}:]Ç+†Ž!œôKÑÉ÷.Bp|<ëÄ¿“m¡úô\ø QG­“LpQô1R$Ý)EŒgpUŠ–e`}ÏE˜à¬FUWz2EÉXfLXÚâPÚû>ƒäþ®Ó)!%sj“Ž «¸ï2¡<ñ<ýs¢ _ üÅCk×Ò©4œSyÁÉé›÷ùoEƒ=–ζ‡cs8õÊdžñD’¹~<ÿa1±}¬ªé§QõŒ!ü™2 m>‚eɰM·rÄþÇ_œ…ñ-Ò{ê¤ßÕ!™ìe%EÜèL¸3ø,}MÜè?ÅÙäzå_¥µ†>ÖŒdjäÊ‹<¶ Çâ½i|9W<óóÞÎáØáFöæÅiÐhé-ú4ïK½÷~7U뿜βÝlEELÉ3dæ—‚a}ôT‘>)?À6Äÿá}¢xðÕ6 Xˆâ¢UdGÛÚ~”MgÇþ<ÝAGVRàs°2L TÓò9¿LúÁr*¿´á?ǫȜº œÒЈšN'ÈÜŽD8»x «;šÊÚš~áx–Ά Fq gâq³²!]mcƒ¯Ü!F ì•b5zï½Åª\.Ý÷²Vn€…îl IBµÄ#¤ÈOùç„è]“úGOÿ`÷þµ¡ƒè)ô'ÌÓ.“9MZÂ’BÑóZe8PŸMyÒÕ"ðsŸrP 0‘Þy”YW´"ŒŽª­$»Ä¢Ù&C§Ï¡}ß‹ú+G§³Îap^(wÞv‚V7º^u<óö©Dyée\ùÖ\Zý‰=¿w0cENFôßžmáAS=×]c5ø{ÂãaÑí<é~!NtÅ‚Nà\èÊлO’nlÊÂ6;€Á.l…¶[´i4[¯$Α5¼íä>Þdäò'eR.šŒO%ãyqÛÙ^©9ÌB–º| oR¤E¢˜1·[4âUfó¬iÞE’³0‚ïäfC‡Ÿ‡ìøé@]®,cgÞ]BôǶU}—¢Cu=Z«É¢Cp0&‹LÚ{~G²‚÷Eœû S´­i'SüåÐë^¹£*K‹D‚ÑxêžJX;hùƒ{¯„×½%ÄhIÖÿLJÀí.g°^<$ËègS™ÚÔâ+7ó^€åÎÌ„ƒö·@m£(ÃWApƒ2ûôFŒæ.Ëßo òóØ®W1Ü«ËkQ~Ífv@ '{ן<Çe'°!P„žä¼©ÄqafQ‰±—é´ Dš$©Ds‡µ}hÝñŒ½¬Ù—wëØÉ=;G]’aèæ-ðÙ,Ä>GÑ¿¬Ùƒ¯+qq¹(«25äï§=y·ÃR ïœ0›’!Ãâ`ì‡w¼ƒ©¡Tå×U{ËJ¾„Ôª±8áŠ3û4Jfì?ÎÿûþKK½ 4DÂæ”_$öE¬æy±Œ2mz\ó7Ù¿{H'gÖ‡Oƒsè2¦aôŸ †2½ûà»lMmC'çE3ˆœDLÞaßîV<جNo)so3Þp£ç¿ÅÂ+ì^h#W„öÕ FÊ jqòî­øÚt%šî[C³kC˜ö–3lNbë½îö¹ÐÆv¢|\ýŠãõ˜ás9-ÚØLP:ôéã’|üm õ-×D¹w:¬råhöpßUx­”Ë|tRèù˜ç`|6¿­×'y÷Q1óYôVôïüÕ—CÚ–Ò»YˆžmP‡q*¯QÌ „¯¬Óg >/P Ëbr¦ÿ˜´}>çt¿'ø-až;¯s+&jÓ¦VZÕYOv§R热¸ª€Š{ÆÑ[7õ‘TÉMšNuÅŒYóÄòÏsXÛbË!ßˈ]KØŠ—«#©Î¡q,9_’íVçÎÝ‹ߥB³»!gª‹câÙ8y²ërèǹbD±Ç¶¤t kÕ20™De‹~ââ!=NÐÈ ¸_–ô€¥9(7CL¯¤¯šÀf©ÿÃÃÛ'ñ¼æ5BK瘌ï"ß:ŽÓíJ ½Ý‘© JÁÏ8!júy„Tº³ÈËFôïóÈ<ñ(j›þÕ)é?à†2>ã|ù Þ“ãºb˜ô¥ÉT§L„q‹ÜNÝÉ ‡ªàÏ·‹A8ðtQâ+ÀÁã…cvƒ¡QýÐ ;ë0ëê'NßÞÍ:œªmÿ=‰º¸¤@ÃÁF(›VÀ×¾öt>ñÈX ¶ªÇƒéUŒÄᎽ¬Ü¿lev¿Ãõ¯> §+ƾ[ß'£Ïìc‹êŸr¹ÿ¶³ßm ìgV#qÑtçœ.õñNj‰ÐT!6sC=NS¸ˆÿÔòVz€õFqv¡j)ܹîݘŠ>)°šXy 9ˆ“eÁÀå iжcÏ¢Æw!L’<ÈÂY%ù1ø2…4 Á5n® Ûdñ»C<·Þ»‚/›ÒåÚÔm³º5–FoâøO¯[Ð'‚DÊ\—>7ÖÁ'‹´pÝÅD¶Ñjî«Kvq*;-ðÞã.Nê3òÉáúŸ…‚£ö0½T<ÿ­«Á^p;7øî Ix¤ƒÏÖŽ!Ñ.;@ÂF  LD‰[¦T¤ª962·/¡2«Ùm jèj‰ç/ ÂìéïxBÞó¨¸ÅDflûùò³øÌVS§Xt‰4Û¢W-Σ ¿·ÀýLð¼#ϯ AêÃ~ÕL8üEŒî9¡CŤ.@ˆŽ šµmOF¿§Ø¿7~ìøÒsg£ÃîÜÞ«hä]Ú¥'§²‹>gù&m{`©[ÊÍ òcNcÌ®ô;Ÿ«-K„NO>ŒÚXûѺ-!`Š#9q±„Ïûßþ} PSàXöq2çŠ.“OµD£-?QÜ-“wï‘0rX‰û%°¸Ê‹pyl)×¢­I»§¢Èn9vÃ,‰E,‚j‡W8nÒ rÿ„¿(ì4'jÉlö¹/C«PÏQaôäV!US!#ü ü²+À±/òøF;‚ðÏ}XP0‚ÂøÞo ¾ºRè7ù.X¤]#~]‚8f•>Ó~[q.;Ȼș¬sµÖÈ‹²¿´™ÒŽ Ly™ƒc¹ãÄþ‚"ŒÒOd)_óàNÇ ®Çá{Ý}ÜZ"Ç‚gŲq¹ ®Ì⃂°(·[ßû»øvÓ¯¡ïaO»Ã-ã)å옊Çk0²›IÝ“&ÀÑãàõOYv7ÚˆæŸ5#­ãe™äŒÙTyß&ô\lÍšÓçà˜Ã͸¡e;1¹Ó ‰šÛIBÁ föÞ ¿î½ÂoˆŠd«Ú®àø³ÿ@ð’:–ñR8ëÄ©ìÀ¢1ô|á9t±'ó¥àwþ¼Í³{¹£êBÔò§º5*ã« ¸ãå!L«ì¯ L_†a›;ðøÜT¸{mÞøþ†uH—õÃ>ýäMíöÌ¿—éO¦ÛÂêðÁ!X­L#½`ÆK1&>ÁŠîz© ýäðÙ'?ìŽkýñ¢t›Ò0/ç¼€\›5芕—ñÔƒY(ñRŸÉÎx€»Æ‘ ¯õÐ*H½,2á@‚ö*Á†ì´§;‹K»Ãá嫸E~ ±ÍÛµh…±?ˆ\õC›t¸ßÀåž¿€‰K®ÀÏKrTÁÃYîaÔê–‘þx3ãÚÈü‡PôX&6·dœ>]-óƶ [7•bHÝ.r/9 ª0¾ÓªMì¶þPmes¤žWd}†vøèûz6½ô(~=‘ŠcŒÞÀ•Öd¨äÝâ«Ý™Æólåsºj`ÀsÓj'ôŒVaë9[õlZsÝ„Šðcú´Ñçu/ˆãÕXá3Štm†ø‹‡£u~ñl¶ïÁúN}ºåI".§ýc˜køk‡.uIúkñhx]CL¬§á¦Íw¦Uá°%œS;ØÌ¸ÇÛ»Ð{½øx°½š]iÏžNxå.6ÿãCpÇ=]pr50S!vÉÉ•†MŸÀ振%2qçÉžÇ ÊJ›PëÛ1<ë+@í…Ç1ÙÚB6õÇT¦¨+K”TpcÔåA¶h#,ÑtfQÎs™Ã¤»¸x‰]t3c=ú1w—;³l=Š=¼ c¢~£ï`’…f©t®‹ý¨°›ú8žBñv$«YÀž üæ{Ç`†Í$vH³ BnéÐ5šJ,ƒ?–g·(’ùMÍN]!½!¾Jª}sNŸ¶Ùo£ó#à—Æºaï`u}ͪȃæÂíÜnÁÖÝÕ†Šñß°¶~ÚHþß5;«‚Žp/î †¹o×8afÚ…ÔìË6ùÍ㲚C¸¤?éh¤œ ÛÆ<‚‚u¸!=E‚^hJëÙÙÙÆœŒÅd*tó!o˹-Øód±¬4ČɊl¹W8\]Ì$â.êÏ’d^µ?ð\| ÑKÑĨ)›`¹ýìû[V3õNûÒ n^„Íú—ÐbÙ1ô[´„}3QÄ-’ÃïnІLê7ípju£xñ®.ò'š°þ¼'xºçÁñâìùaL2%Ø`pˆím¿Å¶´.?Ê¡KÊ–ivކ£êTÑ\™Î•dU7NñŽü^MÕ‹*ñÈŸËÜþT()I€ YyìYuÛÿ­ýû éLàö«;Ò{!“™»áEެŒB¥~1g©™^Ñ8}†;Î0éåtJôù;g(R‘Q³1!¬u½õ©`Ä ùìÀHcúÌ•bŠ+œ˜ÙCqð—øÆ}U4å–Æ[}¶E²ŽØörô†~ˆ&ž¿‚éšfJOåâw£¸Yp½PiÄ.抲!ݹLsƒ4È÷H²×’é,­F›ð×ýæºÍvð§ï(C‘Úmlïžn8|ýn/à©ÿ=ݺ¿aÚ¿n´h?OdËÊxÏfCÂwV)” "ËÁe/¥ïÅèÓ§§°}ïè‘ñ¯òÊ”i($á`Áz8Ý¥!»ÚȦQ·8©ÝL½Ý‰ê˜IÐm÷´èß)"tÑ~z|ïf¢þy/\µ= o„–aÓ·4¾Ç²6Î|± xÈMWWgåa¥°3IG«_…)—Ø›Y³ \ó5õž½ŽTo‘&›4QjòîâºHoº„ U²&åû³¡ó´%î~ñŽon¹Bj¸)žDïÆø ¡€Ï(²ôÏ«x§~?Åc+xÔïéêyT¦ïHƒÂm¬xç1ØýbÔQ=@SOö,HŠM §éÉø*´¿­¶£¯ã.Â5õÿ·ò³@Õ¯Xœ1»o(ÍBBřژåhºn[ÏoÆ ~´ñ­åe‰³9L{¶WƒÁ*AVq1ƒí¥O¸ W jŒ#lŒ/ŨõËQùY-\©Äd?ä\ìßÇZ)ºpÜsP€kœ˜Ž<Ê–¥±Ïeëy'~hÂÂö –tÑŒ>ò[îh’“~x¯ØœM’aWÿ†BáçÑ”h‘ûÁTjT:¹Ö"‡ù¢rÌ~Ng¹Æ—·[>•-õR¡m¯/³,v¼`XW£ëæ0µÆF~[Ü3üùÔ‡ú±¢Ì ¸¼á̪ê‡|ûêýï¼þ:ÌÖ ® ØK9[ÛdÖvF´~Ÿæ œô@¨,ƒùS#˜×½iÔÚù¾’hgŠÑ÷ ;hŽKíãN¼¿}Z?á­§(4ê Nˆ`ÿ¼G³FËqªU!¼ðÖ¡Äáɸ^©Œ8ýàGö´‡2ó}Nlµ¬.ãÖKÓëgóèê!+v*Ö—}ÿ=›]ÍkÂÝeØõû&ª–ÐþàXvùúf´\<éÉœ¤‰ÓÿÁêûîÌmåIhŽî`ÛV©s7ç/‡ßàÚþq¬Bw\²afÛ\XGÆ#â-žEO1âž™!ýxú~^ŠÓ†5ÕÿÇï¾ÿÌ¿Æõé=‡ç97pÃ%L+ÒDSnÀm(xÂu…Å“3ö :Ù„Ä&1Ïs ÄóÃ÷¥ì#ö b²Ç/h—¤ðájø>wxT?bÏìh®ÝKÐþ›ìj(Ö—<‰5ìôÆ¿PæÓc%ñ¾F~Al]îIå? î<7§Y³»Æ-|õÐÉì«ÍǺ†C‘øE9)1‚‡Z`þÃ^sZ>w+"šhuîÇÙ©µä}¡›ŸM…îŸèÒ2éøÈs+^§gÁZ^Žuy »¶i4 Ù¢ÂDÏÁÒÊe˜}£—Îìx‰Î븛Ù èßL¸–9¶£ù/Æ?‚Wg½˜zÆ2æbœ‚ Ÿqs®ƒR×¶ð³=“Ú¶îö+žîGâ“È'Ú½tx? ;ª‰Ûc‹ýRATc44ç1bÎâ[%™ªô æH(³£©Ò#š9¤Æ‘’…Tç)ŸÛÎB[™[ÐP¦ƒÿûȺ2X°±Uܘ)‡12¬˜¨¾/ÃæÔ;á"ܹH`_`5}…GøïÝcºbu*jîÖ…5ú¾ìpÐ\:%r[çi‹/CEYšÏoÎ$l=ö­_Ͼî•ã­]ÛNÎ~(çîÕM Eèµ¶oбæIjü¥ç­Då}¤Àà+î_¹ L²¥mF[ðúé6(ùÛÍíü“K.Ì…ë†Ûó~âÏ…[±Æ‹ñÛÈQ8%ifg3ù–nç€;&FûcÀPo9 å £Ì"OnY ~¾ÂÕ)žbÊ«Vâ,©Ç$ûO& ¼GL+¬ñÕ÷åL¬’Ò‰)1u{±×Çdzµ~âtÇyú0ÒõÊ%ÏQ€j\µ‰ÿÕñSVí>NúäŽÒ=KÄÿË-pùî('^ U»ÒéÛð­ðÍk•ülÊÅg âè,ø×æ®vÖh|ü/ßWBˆM±•c²;ážœ'º5µ`|Ç{Œ:Ó@n`1‘µ¥›§Õ ;¯ÎÌãã•4ÁNœŒz0~Ék¶hç$:#í'–ëšRW»Ë¤*/€[ "N7,žÈ¦ sŸÈB\tá4¬ÎÞ JWÞ«Þ¬Ô³‰·€¾ãBrßâ ÿÈ,ÇTΙóÜçøüK€af$(kH Ô‘ ½IPzÿ\ÍxŸ{ùÍû¡o!ñÿ_:[Iù\þËÙvPáû^ˆ`ù«XöþDïƒc™õ§hÒý¦¬ýŽýqSpT°-‘:N/þùÇwÁ‡-oµgý¡²q‰»ÆÕþYõf|Y ‹f߃PqlÑ^WÚúõ/L”œNS®äÀïmpî¸#·¿N?¦ºë°4=\XñêîH&qNå)¬JÍ ND-ÆßÈ%KM4àÀ·*‰Á+.q¢0ÞuHÅsöjà Š>V ᇖ/ZiNBùñ+éÔiZxÞá:zJDzâÔë˜ÍÓEr^´nQûþ’¬)Pûq<þÌHƒói-8ä±ÛNbŠÖ´hH—^ˆÃsvß;ÿg/`wC0ïèk²§-Ú-³iîäN¨Êt@}á|ñ˜;²Ê†Ó©4Yj6ºšVØè ûZzyüö/ù8+¾¾€Ü»?æ|þ"+6RÝèE,//‹}·+f¦ÉsY`Èؾ0‘Ù|ÎýØ^Û/&1‹°]TÛ%žÂ´}_¸sá®ÜÏîR"öîÚ¯H¤jµ–8þÊKл¶b·<ÛÁ‰è½ìŠtJ²¹†üSç ˆÍ‘‰ô¼N,5°ÇO¯ÆäÆ@v‚ _àE.ʆøÃóqL¹æh§…â߬Pî…ðxPO΃€Gwy³Ù™ûã íçÅZ!“J")aDÜNXÑÁ¦;å$¹+ØB-ä}:ÏMÚ‡x®EÂý¨ô5 ÷§CÕÄpÍŒæÇ,ïuN’éÖFÁ!¬&«7jDc«¶ý$›Æ{==™OßÂ=ï{ ¥›lÙ‘%§D¹S“uãáq’!»ùÔ—S>ÙT'ÞYޝfàaã~\SüO—¯E¾®+ú¶”žE*eÄ'×w€&*ò._?†5íAèŽÈÑ™ÿûþëc™äH nšü‚ww«2ôScßõ٫µú”ÿþ.«¾)þ‡A ?ŽI>ÈÁД ä“Ô<¯5—ÍÎA¢ž®fÿ*½àwérôêy?½þžÆYl h'ÝiÔˆÉOÂ~ï\oë-nÌí{½øïXüuÜÝšÁ_rÇî;A^· aÚäD+'‰D='œð XÃ7Ö?ÜÝZ!xÔq¸–äeŽ"©ïÅWn¡¼GÐW;¼ ¨Õ¸8º¨÷3¦ù]†)2uœºc ÷§x.}¸^’ÞmÇãMAr¦+=6IÅ ×À`hÛˆÿÿùZÇ@쟌c 5!KOôv^‚î_yæÝ¶Hkã 6Ø—¸{dð]=ƒÁ&%°ça*‡[3¾ øó¢6åþžòp½Ö·opcF¹‡H÷p¾<{¥ºËÕAœ–÷Kþo§è}b.‡‡}$Øß¡Åh¸ÂáÙr6Keï;Ü‚ê­Jl‘Q/$*Öb\îOüýÛŠ¾ÖÑÎyܯú”SüLêOv7G’E‡ ÒÈœ4»3߀Üï—œjíGn×HŽyN‡sÃsÙœzüAرa äÄ¿\\CæÈ¼†gupšõ¿ñß»ù¼U4â³ûºÂž ÎÕ¥êSîÂüCüñë/·Ãà7̯›C ÃÆð ž/fkÃiÐ *ög&]6ÿׯüy²ë ©þKE˜½HŸþÔ?ÌŒävÓïCñ4ï·9Í?r—T8=aÃÇq{vc$Cž ›Nþ€cé)ù¥7ì·™ýÑ(¼« ý¬X¿ƒ-mºs‚ùøª²wPÏ1ݳ[®±•¥®˜4½â„:ý¹„ÇŽÜlÅ©<ªéÊLÕDz°õ$M§Öußg‰·nÀ‡É±luD8ós>ÉVGަÁ =‡}:$šÄ?ŸðÓxqâür‚ÃßÏŒ˜äói¬Âp5Õ·^©·áLÃnWã\VýϵŒõfI3þBe¦uÁdT˜›†­|˜ä÷DÚ8#¿¦¢yþl­˜-wXÂNn2†ûŸ®rÛ&¦À¼9ŸÉçF^fÁOçᯋ»x•fÊ×Wk¥²Ï¦c;/Ž~K»L±ç/$Ç}Çä” Ôâb5IižÀn{Ê1‹®9ìWÉ?Ü´Jš»•4šìbáÑ|U2&êÉœù…+· §i‡MYä‘“`ÿ×½³bú«Ð;ÀùÓ#†ƒ,ÿ®=³õlÁoþ; ˵_òì ™É£?àÑ’I´ƒ¯qqIO vÆâP“ŒzWqó·mè5)ÖÌ †é™EøýÙF$AòScZ²mތ֦›Á’ØÞ™Ä Y[aï—4¬œº‘…žØE!ß•œL¼MgÑ/Ï}YÝ~ú™júãðRþ]ô7}MÉYÜÉi?¸ŸÛ;ÐÖèï#crϯŠhg#)äMÅîÞJ YØzý¾¬`é9*d=šu¤lÆ/3HÀìªc®‡Ý “7 ¸×-•‹È™Ç4‹úñäRàôíäFÕŽf¹?7aPôF:}yÙHþç4ÑÛV®æ¤—j³RÒlé?kˆµXNS!j¶3MnÛ‚µÜÆ»«YE¢{_ܭŸ àüt Äuž%ªŠQ¤s¨ Ë,ÅØÃy·¹=rêå`Uè Þ õ†cÛÌa¾^7Ù®eÊ9´Ë°¨›þ¸ŠÚÇ'QÇr#ºts%v˜L¯î’€ù«2±ú°ÙÆcÙ Ÿ9ìÎù‡h|׃tmcM}¸Ê’3}‰œ m,÷ä—"Ks±†SGüh䤳øöÈ9”ŒdÂÓ_â…Ë `²B(r ÷ARH)tÍh„"™\6þg"¬Ÿ›7ÿZwÖAqQÓñl²4{e4=‚5é¤å‘Xíã &W¹Ñ/ôØëBí{¼‡ïgŒ»=hÔR+ê±Ü DF¡O‚öŒï öÀ£Þ*Ó²o×!¼M‡:÷ F—%AÒÃPy*ÂÔ¾#jg Ø~›c¬¤Ý‹É–L£×7’®`#¸0‡¿îÅÀSQ,ûç nËh”¨M£,ÑCx.˜êD­>HGotcÍeÙÓ9Íp¬a6¼ KdžS3Xšz%,øðíw.G•„|.|N [uuã›R³ylÿì}Q¦Åf‰o_ŒØÿþÅ 8°AéG…ÒÞy¹(y¸’•¡›>‹Ñ1u©t½ø?ºrç~*h‚¦G–³ôâHô‘J¤KjÂiÊWøp%Ó*XcuòÆYYq›è’#µlÛ­¥(]tœ á}&‚Ž ÙêR7Lz²ž=)8Ç}£‚B—éé¦ï8È߇¥w,hïʱôÕ«y4»i3ë};*vÕŒ÷:…µd„¿{gá'¥ä˜s|ä³ÜKY`<+Më×h¢RšžT´Ó‡‡(³ÌÝ2Ô~òszkÃp{ˆ¦ì[DŸúÚR²ž?¦×M±ÿ—ÏfÄN0÷º6g[\·Â’™7ÿÅê2ËF¯¦mr'à|ÀkXÕùVžy¢‰u¨h·’Ží¬û[ü¯9×($ƒ;Élþ\ö .ý8WÁcÄä@- õ ¾ œλu1®¼_F„¿8ψÕΆz‡8èöæÁð=z ¼²CÜÎ['ȉ€5øâûVl” Ço]!ûœ¨¥´(-ÑÛÁò+Ûp÷¿xÖ®¾nIá‡i‚ˆûv,è¯7þBÌð&¦½±ÄÀԀ͘ DgºšñVÙÚ€õ@)¦~ÙK{ר_L(-•eÙOÙDðï9Æ•§E’ÇîÉ œ ª UÑR׳{%C¯¥gÁ¢ã¢L¾{2«‹ìÆf+a6ç™NŸeËg‰ÿ¦úîp{Ö"Nñ4+àÕG±ï =›Ïì K82Í‚ôx!"ØŸû»Ù÷G}¸;à+çòüÊ >‰ùAT«¹sd?ÖÀÙôäݽP³ëwp“ m™ƒž~é\ú˜BÈŠxAk¸ÔÉFœNAß:¿N­™ÌLÝß¡g–)¾ú°‹Ã;3²À£ò2*ØŸÀžj4ØÙ£äø`í÷wzõ` Ø?(“üÈ=¸ÁíßRÊvm‡Ï½ØŸ¨jè^w–4F{¢ÔÛ­0Ü€ú3ÃÙ–¼1xrÉ¢ö+›ýþâ€OçЛ«Gð7‹Á{m÷ñþ&mÌ'Ç÷½ÄûËðnÜO„©Íp†×Íw#šêáX~ô·¤P†ÚoÂ~üØÇIØxcÚ`1JÛå°Ñëå©Y´(•ôyÂùo³`:§ úÞlx.t›g18–ýWG]Ü~Æl~F2ëÙÒˆÝxçó ökæK¼/{oaÌŽ›ÆXz=\Tbjá­O5×ÔeŽ¢‰]ÏÈË¡JÞ®Ò›xfÅ! Íþ̽nM ·Ždá\ßý°hPÞ釕0ë³;ʃ%×B˜nà¾ì!=*ç2œÏYD+BÔ˜Ë.³ÿ_5ÅV: ¯gÃÐ%W*;ÿ +Ú—GŸ$#íÔf{c*™é¹ÿêïr¿ÇG¢ªõ_2§¸‘›¸Ç‡YyNþægNESè %®U‰ÑˇÛUì-iÀãJ–T§2Î(]Áö<‘f'SR艽iPn.Fïì½E' f~-þëѧªÃX÷¯çÑwm&fÃ*ÞTá`þ$L¹¹;4IÕ 7ÝÚ†“³ÙF©é°3‚ÿʯÁºOM«ÀÌ7Ž“º$%½­eì¤çkÐÚc•(ú°…“ynŒ·¶«’#aù ¤¹­‰‹E»-pú7B⧘ø“ÁåøcW-–M§7fUå! à󙃈2ì²È†9Çæàí>´œ‚’6¯0y³6·ûµ<øeNù.äòu|». ÿìÛíSÖឨó 9Uƒö™¥á’þ‡ð¬ø&V;}ÁEU,¢W—Ù- Á®{™dA:lýÇpé•}ë§]dÛΗøRó2ilº…¿®UB‡ÚfÎxã øvì?µæÞMV”p}Nɧ ;lÂ⨠9u+<÷ØHfƒ$.gû«¹…‹ëñÅËÑœÑ|Ql•žCÏ(ÓüÙ #ã_4Ø ›_"‹wž±û·»Î _ûŽÛµX ýfV¢˜ÑîXÅ/X~U—™™EáÄQ± 11”߯’`ÆJœðê ‹Û¥‰ÓäL)ßãÎ×uaÚ?ñªÌè×?K¾L€é&Îl¶ÝYjФ֢laÿú<ÔƒŒéÉŸ_੨,Üø±š½›Ç5/ÝÍâ›\Éd­^|ú|®FßåÌßOd§ÿãl'_CbË}=‰ÍÙ&Ny3‰µš›T˜õj!ÜwÆ…)YWãUë=0ÏZ®ìÆ]sö°y.7áºûR+ZË5œN–›Füÿ¦â&<¼} ¬»bN”kÐÂ=ò°ñúÈ2ˆÄûMVPg­Á.Ïz 7W·?¦À×Ò³î¹?õåqÚ%H•Œ ÷ôÙ 2•W¿ªÕ™fOù»º­îßê&|)é ×úçÁ®é~PVcŠ-j Ó}œ\m%jZ§0$mç„𹓾߸³É 1¶?F ³ÙyÃþÑ5Šõ§Â¢ØŸnÆ=V}B–MjO~ÞC¤ÎZ’.ZÌ¢#RIJnÛp{"Î ÑÆ¬¸•°Äë:¬+Ø oÒ[‡/aç–váÏauá/‰–a{$úGð›ÅóGWOñþY\ã¼§ÐÑÉåûŸ¯³Ú‘C:F­É,<¹÷Z) ³D‰ ¾¥é\æíj½ wóY³£».gãfå>4úôû'¸¶íôƒ¦2`°—³›û–œ´¼À¼ŸÉ‚ä«Üï› èv9þ•%rWÀ U b(äHQý¿¿ðÌ¥Ñ`•`>ÛZ9vCš*úi³¶×(2×¼Õsç–N»„ß•ñüÄY¸Ðq=•9?-~/00X’+aùäõwö’¤ÂB3¶^§ysÙÝŽôÙ…UÝŽðøãþ¨—æà׋ªÿΡ±ÔWªo% ·myò¢.l@fm¶_úïþž¡Ä픚ÈþŒº×Ode³²Ø»JzUU7÷'ÂòÝ©pXH \í°¾:c·†±ƒZpd×~Í»fÞï:u˜<–!·“ºa/D/_†ë‘¦lÃ.>”H¿DþDSP*™%˱On.¶EßÂßCGqëþ;øî¸;¹/wÌñªû-–DÊ•Y@W&VìU¢c죱oŒÓ˜7o@¶l5~ëŠÄ6ÿW5¿CÇ¢óñ©dÎvŒ ½Rˆ5Ûÿ—ÿv*mÃKVް«;&¾JD“Ƀ¤&«?gÙyî\=5÷ î™` M·¦ÃW®ž÷wI6ýÉ‚›ŽeÜ_ù½L“4ʪ¢D¬!5JiÃSÞkot¬íq¦ìØ6 ø²åÎö´÷È.Îøç#Vá0ÙáîôYغ°þú¤Òß-Óp¿²÷~Ó=è=ÌH„ºÅˆÒËbS©ù˜3Péô&x;aÿ9cL\@Ÿ¦wÁº­›èèï&ô÷õAZÂÜ€5Ï·€R¬,+.É¥ÕƒsžsssÂ*»ó¨_ù‚»½A”t\02ÿÕ¹ñ:a²¥ö‡ð¥ß{XŠI½)‡Æ°kÔFî¿*fëÂõQx`‡,T,?˜9¯l#¹û[Îó½¤1ð\²Š¬?ï^©uå‰ÅÜ‹»¹°ÑÓ„}9|•Û5Ê7sàY×Áí2x ;>ÁïÄ&ç"lèüÁÕÛ¨QÚù—÷ñâ<´E ;ðøÔ­ÊÆñGy›Æ “íb–¬¡»œKðeç}°h¿!•ž] –áè/4Þ=JÁ0€5ksaåàtÜÚè…d8GÐ|à_¬ô˜±¹êØÊ°Ýý ØÌÓ‰uâ¹dÓ¦²ùÄ4xº·ÿÏX!Zy<Ìc¢àç×A²æÖ' 9”F¾K§Ã§¯ˆdª:·Í´ßyݘ[=áz£^’—',HȤtØÑ¨Ž%±ö¬ð®ýèÆYûšðcñplmÉ@Ûö&HYêLŸiÞÃs¶³˜éÑP0õ.=̾ËÕUo©«\ðÚÔ?‘ù=/¥Í‰ð—+†¾“£X¨é@í;E8>u¶9!ÑŠ#»y!\MH ÅÀÍ 7$jµ;;F‹Pmûq¬ÓÎaûVØ ÌØV<9V˜3·¼A‚BôÄ„%#ñoSi+ÿn7JÛdÈlV§m6÷HŽãy’>e,ù¶€ÍÄ|‚8òð“)ì«“§"_°6%š7Æ£ü2r8ÄêÙñíTCZ¨6-X{’@“$½´E†Ž¦Ìëb#/ÉC…'¦c9=îû¥p|±PˆI('òƒâÅøSƒÆÑé¡£‰ÓÛ)ØsÖ}¼Pçðú .WÖ€IYš>%‹¼Í¸ŒcC¡[ÉXªõEžJYÑ áX³îA8ݵf³ÊK_ú”?|dE C÷úcX=ô77(Á÷k‘¾» DÆŸCwí`°¸ù,òÿ·þóÆŸ$)-ÆŠ÷ |¸^BÌ_u1•vT 7 £ˆiPšõ­„&Ùp¨¼3'f%ÃÕ^\N”ÚíFø/5þžås<ÏkpnØ .xÒÃÛ7. .ÛP ëQ¡ªQq»o}ö^Zw–'”ÄÇSIŸ ô“+ÑN]‹go™âÍt0±Ìâ9Ô íHÀ²G¨hÎ&®+!^™Ü‰ûDBLž³\`O¼.Ê0Ù}ö xaü¶PÝs㜀»%K…Œ¦‹VÞÉaºgt7loƒÏǪãi#Iv¸Öä’p­Z¯{iÙº,Æ«B“žÒ{?Õ¿¥sÊ‹ÙïÔûð>(·º¥s‘&›xïDTÁnª.}ôb³©Ñÿ ; 3““!h} ÄËP%£xÍÕž,[ÀL75ã‡QLáæ2Öy ¶_QЇ Ì«é—1W˜ìù(ŠS>MFmÅ_ øg'^ÍT‡™Vš8~¶'ýí0lI,â,Ñžjʇ ÅiÐ¥ 䟶‡‹‰Tö}NÌ“…éÖ­ßW‹Üêád÷)îq|˜ûcóß(Ò˜G±o×I¼õÚ»—8 ’›¥Á[öP Ï®³ƒ”x¹þ6ì×f¿Ä¬èö¼6¬ú,‰ºnJàÁ‚¶Ý„„[QdÉy¶7¿Ž§Øg‹!åøxXÃýüûÝóq…›ŠÍb+ì›0åMüWÓ,u£ÖáØÊxAx:[(áûvü€I_pßRUةъ¬Ã0^›ŸÀàâb¬b‹þ”Ò¹·`KT$Z¨ž†÷Vt¾‰ SúŸïE÷ÃïAÌN»\,hEÉ#6à—ÃÜeÏâ–Ú*ÌyÑNžhФ+—1eýYü¹ø ®t>õÝUp^á Ç)‚µ“,ÙJϘÈOaƒ_Ph~*î®^Ų"ß¡íù3¸;ú=ZŒ=ƒ²nêtÃÕ…˜kÆ„*óï<=F׈۱âÔlÙÿÕUã'ù#þ_]ëA„o1rßð2¨¿wÐûól›#Îf¼ÛŒé¹=Ð]ÌIèiÐ]¯q¿&§Ø'¢E‡5›K‚ttТõ—Ãø+ªÜ‡Ôbüõtû(§Îñ’ߣ¿’9õ_çÀê­ïƒAã1lî¸ +þ b¡‚ƒÿ|}¼1Àל6ƒÞ :Ó6œM«ØÜ¡^è’Œ^#n-õf_Ï¢?`Jjüpâ‘5)dÜÄé$ðëzH5³ ïwpù†ôÖï)¬ÒN•^“gveApp³ùq7¶µb-‚Ã~ÑI°ûÜßg£óÝãFüßÌÞ÷;o‡‡›ÀÿH—H0±æ(_zõA!DN¹†bÆS`\êIøãX‹|½cð`f Zü<À]¼'ÍrŒ«Q@¹ƒ¤ô<æ-¬çShÅ+o—Ðe£´ÙSÁ3˜ªëÊtÖ[õK¸û¡ÐsP„=­IÁ9".©C×®’eJ;æ³¼ aM'y\«Fe<¿òSþrËž`ÀŠ÷øÈ#‡)¤Ï:®Ù_ ›Æn&é’_é&ìß®*¸Þ2—^îËÇ­¾Öp«_jÞNÁÙMЬֶb?]#­™X˜XÍ>½ýï›Â+àÄã=>Ån?¿·™l“ýC¦²¥Vv ñ|}:)·•ŦÕKÉóg É&Þ½ºú[káÚ²Rü-*‹•Áf¨TLåg °ÄC"ü ûå,U;9ök0ã¥쨄‹zŸ3v°Ä(ª'²³&aóÂûðûð<ñ«–/=Ë\¨ÌæÛ²àGÙegL\Íi$€iê14¹õ¼ŸÈò{/•‚ˆY9w…ýÙóž8¬œ@Î?¿‹ E`s  Åyض ‰V”¡¬T‹Z ¾å8º«Ùsa”êòì¹.µŸo…w|œ«¯ƒœÒÿæ¿|Î/!ìã3ÞÁ7!äi­9&8¾D$G¶ÏúÿÙ·3ت‚¹{ĨñÌ,õ…ºtL…—Ñ'H—ÁXèmKU×7ÂÉçë!.H˜·#õé>QŠ+ŸÇÁ£=auêÀt‚+³d(z+~èã¥q¬ö|ý_Ð>ë$œ9.ÏÆºœGÓq0-y=´zÛz,k¦Õâ{G1ø¯&;¨$…[tÙ̬²¡Ë÷ÂÃý:h˜\ƒPùê3´`~¶«XÈÚ£bAk½©rç^eé3Ýž£hY-M_÷9‘ÞA@×ÇÑwÛެô¶ÔÇôñz(au€/ø:4Qâî38¢%Ãê¼ïÙ‹Õt`6U£ó2]©áÚ¼Ø;~z[üDFÏÂIù¶Tß9‚öÙ±KÁú˜ý’­ Ùât]ë5lAE}^‚ѳ|¬Êþ‚ßZ\ÁËá9lÒ|‰*¯àƒìÙ¬âùWL–& l¨FÇ­kÁ.¥¹ôÚ‡"Üh,ÂŽw|£TÖ;£Ÿ$éT"l Û8œë^ö¬‚';ÍXßÙïðqïXú´d+ÃÕç ¹Ry¼@f·C€zôO ã”WଦŸh[, tm>,š7ŠM¹k;«/FóB.;lÈQ¡ÓÈ'NwîVˆ^fÊŽh:ÐCÂNÐR& ²vÓ˜Ä*x<~øÛBÃm;ªðÛ‹X/Bÿ¼¼ 糟 @Œ|‹9 Ÿc[ØÅQj4³‰"Fìó&YâñV›é„d°Ÿ9žÜ·c¿É|÷‰°ìþ8Œ•"ÌβvΣO¹ô ü~¶È$‚Å_daö$Y6©,7N:Ì âY¹F>OéCU­Éø(©\Óð„?»_Ö m:ç©ÉBöÜKm¸´ƒí}9–޶ ÀæJ!¼³Ö9™QûŠ@6+÷ ~p¡ìrüÈøÏÏÑc±×'ÃÂìí…0há»`¿½:~wŸÉ·滯¾{¸k£Í¨@0é¿’lbÂ^òw‚#s¢Ð-)!gòÐ葸˜\IV”GqƒšÑP¶þ%Ü®XÎÎïj!i³gÐEÛ—sÛ$µ©§…uâÏ ›|Âqkîyøý[5…Vm³¤å«³ø2¢ž|Ý­/@õ©Ë·f–›|Xö±sTÊâšÏØJ Ói»s#š‚ž+NÐ/ï›°ýq>|äË­²ÉeãU´¹WžåĬ¤†A%èý;Ÿí°`ÍsüqúÞhô:ÎJ¥ýoýK{¾Z9ˆ×3lpƒE&°> ½3^UGaÑÖJèÛ Åg—㌡ԸÃÍÔ çKÓœS™\Ø.wâuà ‹/Õ¥Wçn ¶§£g¼ËTKƒ~É líœ?Pü§†Ì¿òg^qe»§rÚnF¬nö+¼w‹ØÎtaÏl¥pHا{J²>ÔPbÉV¶då ÿÕTs-mê´² ¾/ΤiFuØeGçÛ‚šðØ4J—yžýIlUÙWòìn|N;ˆ95y} ÷“ïs~CÔ¢lÅm.·Â¸[ñt~?lxpsdüÍî&8ë(çñöÙÔtŠ{ÿ|.TôVã‡1ÞTz]9xÚ<çäí$ðgÇŒèOD”™ÎqFA¸-ˆÐZ‹åØh˜‰~¨òwkXߨŒé÷Ñ-ÑÚØ¡Ž(·v%Ì”ë'¹™Ga™Ä‰[ ò_c‰É°¶cÓfƒdW4xŸF 0ŽÜ-áêè6ð©ña^š:x§Bˆ×rÕ˜^¾ð3ƒy:-p;Û‚Ï.Æ Vã%McfÒx ô‚9ü·G‡¶×œÀµI0JOÕ–Ù¢ÛQ¸Ùk‡ç.upS£è›Évlýó+°Y1ŸË{ývÿõœ|é(¤ŒVfZ»_ /ò,|ç:z¢¸‡Všìf!L—w¤’,ényotbbü…é¥.^¤~ ?˜DÛw\ãv½±£6æø¦ú¹˜,ë{7 õ^Þç.-O¢õ¹60YüB·é²g ܰ'“'À;Ýz%RÅ›½ç.|XoÀ){—ó_žŸÉ¹¼Þǎľǣ2ÿÀ䱆¼OF+õMÌmƒ®¤Oª|á‚ÒQHtãÃnÝ<¨tÓa:é‡ñð"‡ÌàBzègÌHþûöù3þšÃÓ¡D) ÿ:Ÿ 6ñ¸§™0ì¤SûûKŸÂŸî!¨ì©ÍÜÃ'²«. {á*^Ø´µ\y ªt÷[ º¬¾ü 'Ðe¬6L½Þ-ÁÄ:98³m):>?Â[PëNŠWõ’èy9Ü©‡Ùtv Ћ×$ð´U?¾Ó<‚Âe8ÏüJ-Y«nC%ãTXÜölºxó|ö{¢8¼žv“Heˆ“mEÙÿKÚ®ÎbIü/dÓ©µdÉ‚(3n ™\ŒµF6¸cb Ü-üÆIœ™Á›“ùÇÎ$gzuh¨åubéQGzôoþïû—¡)ÐF1º@>¥·ÃØ_X°oqÖà“? ðé¼O¼»½DJa+>“‘Ëmèšz=f×óígFpã ¢!Ú|:*^êEÃ/ªøn×\Ô|ù&ôk8¬ .Ã*Í/gy¶Þ^ÊRßÞûïbÓú¼)·üA¸é^Ú׌ OMdoÿ‘ó›žbž„ }DþÀÁPОK¿™&2£ÑPÖ]Ž/ªáú»¦lÔþxöYõ>ÞZã*}cÙð3”g ,3“øNûqÕ×<ÐqI‚Žd3Ò?‚«vd`þ„/œaB: ëîÿ?hÉ8¢˜+Ä_´M‰mü~ž\kj‡oÆ® x[ºYü"R“lQüe<íÞËûöôf9ˆâÍCƒ\Ð~cös }’(…kjŒ¨×gOø¾«²,UhQk$›Fc•š™èµ'®çö†}å ¼·ä$œFÑm«xlÆxyê$8 ê+ÇÂç@#^Z¬Ï>n.ªŸa³äìI' ²Ýº îi¡AàyE†|î2Àw¶SúÒeà³V‚V©"Ë{}¹³ÿY‘bwÁqí›Û€ctvÐØ‰|Ðÿ<Ÿç‡Âï{óyù-ڬϲ^e+ŒðMË9‡-“K¹tÿ±X ˦-f—z´éŒ1tA‘?í÷;K§X&Ó”Õ–l™ë •ˆ;Ç3=ªÂÖ\V ?š­¸9©É¬= Ø_'y<†;pžã9¢³¥˜i¦ñ.9<àì[­X¡Œ3V5‰Ò+3è¿ç7±¦ÅÌÁØ÷ H<ÿMâ*³Ü yxûQµ|¸„U¬çe­ùß{^ë…ôˆ¸.;+!†;§‘Ç—ÁÀÝïÜÁø Œo€ý‡ ©‡xvß>E¿ÞDeéÇY†˜ÛQM êë(xuì1ÿ¼N,þÎW‡[Qê‘$5âÿÆ‹ qZO l½%HM>œ@ÿb "«•+nqàãP3[0coazÎÑ®Îíñ©»óMÒÁ§3‰C‹»ì,ŒÓ½«¹)R—‰Ø;– Ûÿù˜ƒð+  »UÙ±šÛ¼ ”ÙÜo]·È ggMmØ”t1Öt@˜­­½Ä}/=[÷qŽ"Í©÷ŲVe¦‘ÔŒãTú¹Ü-GðÆ’J|X(‹[ç}Â|SòæY:ˆ•Ä¡]\ |Ó„ÇVpÂ4 mak/7p¡.”êÞ™J5Jê_·‘³…cèÉ'pÙæBÞHü‹ õ͆x0çþ©O_&ÌE{¡dÆlqf{C0rI$¹ê² ªó¨×·ßXQQNO_õƒMÁ¢¬vPœ½È¥9£„Ø\¡÷¨®óúOWJ%u%˜÷øeµ)@ë#Þ¸êø&ºóøZ7ÔÆ·˜<š=u)$7ö<È_àë? œÑç*—«0ÿ°L Å[ziçÑzLÅg³Ÿ)—þ¯þ¹'AlbŽï‡µec!Q¿zêÿò¸SÇ_}ÝŸ\_È<<íwLÅwZ.ls꤃ÐãM,T'ŠRÇ×rôlR!ˆ/âà_ðÌ+ÿ¤ŽÜº•àa'OõîÎf»·ÒNËlׯ rÐ|,ÌÂú—b˜$®COÄfsG/L™¡J0; ÊÊGmçÏ&Àœ£éޱoq»t éUÓ„½OÁù8ºyêÌýRŽÉ"תO¦á|§¤ã¬U׸Ž`{$G9?ÆFû:¾×Á) ÿfYìÝ; Ÿ|ФŠúfÿõGÿ»÷óNqæñ•ƒìÄ%ôùLŽ“]¹ÜÖZÒ•2€eóÙ±#zP'¢Ì VO¤÷æoÃÍí°fHzUÆP+­*Z¬ ¥ÕçFð‡*Lg®ÍưìÓ{8¥BÛÞ³+=O9§Î Ú©´vø©Q·1ì߃P,}ÙY"·Ñ8=¸§«ØäK¹¨p߉Ƭš…ëu.“ÇãËð…†-Qê@‘ãkYóR_ªi½ˆê%œa¤G+Îe–oºÀÛ³Ý-íGÍy­°g&FÝ@§½Qì v+Ö”ñÕ¾ìà™è~£„Œu—esüzqÜNºú–ø;1ž ÛéúCs“Yî±;°àÊ©aN\MÝo˜ aX:Ùv¤§°‹ñ> Ž¥Õ­ÝØjcLO0w¢|P†Ûîpedü×ÍÎÍVѱµ™0yQ+¸.ˆ„ÝVmÜ<YU+˜•Ý<Ö¿PŒ»°]¥tš°w•fX¶ë(òPA§kˆNÏœX*®goÞ‡só×(RÑý*Üêü úà|8Nz0×/”¦uOÒX‡q’=¯æ OžkœÛóÐe {Û¸?5Þôãít£u9»=:„^U¥WÜ·ïñÑÙzäÑ:Ôº±kš—»ýZ–gÈ>”ÞÀ}ò|nœ­>Þüð×d™Ñ”qÓŸžÉbK¯µ‚ûR:>}kq½ÇZ@h‘®ÐÄéVräžk$ýº>lDÿë˜çÊ_mæÂ>X³ç?£ÁþÇ8{˜aHõ|³¦’3?…å’¡R’Or'Ÿ'¤Ep‡8¹a /5±&ѦRÍUD\Ä“%¤¯fÝßzI½Ã\¹/ ÖŒZŒ»/@˜‡›¿¥‘‡×·gB´¾$•}ô•wÌ„ê\©ÝTÚ®9’ÿ4ØÒCr°ì¶$mÈ™Ê{4a8wR¥f6Q}_îºI ¸Y½Åb÷°ŸOUFǰ—×gS{¿.X¯¹V<#ú«eXk± Û¿°­–ÿÄvÍ¥œ­ýp ^‹ÊS€ä¾kÌCÍ”€‡ú¾…Äqyñ°öYJ£®Ë²†wGYÔªßh´m"¯ØMA¼†:^ŸŽÇá›}?ËjBͰëT&k?ºnTQcQ‹èÙZhuÃŒNßDz°ª4†o»i¶~œNKœAíýx„‚5¼@½<¶ÅP ΀ë?}hâÛдC§æþoÿçm°Épî(\I»˜0_zGäþÓb9'³™ë·5l‘ù v5W‡nçãͦôÝ¡1,çÌZmOóÆ2¸SM-s2èŇrT¸P ‹÷+Q­-] |ùÃ{8£ë¢D8Ó+β×Q•°|ªð›‡gÊ‚‰FÉ]y¿ŠÌŒ½Žë/ɰ©]—Á÷‡3n;9R·Þ£ü†+Xrû#¼…ÈÔêÉÝ 4W<…Sôîà¼ø[¼GKÿ[’Îßî|Œu]¥÷Ü ™ã¨d×¥Çß1U6ß…Êu:ÓõJÏ“!÷îdl ÷6sEC|}~hÄÿu¿Ì„ê)XÆû Sœcpå4q–ÆßL\§ãAïT®ø³$}ãq•›·ÃÅ™ÇÉ»E‘Ûkíp'`7ž-›JÖ•©°£úÀ7`t4N¦l8ÏöêÕÆéóñEÙ=îß»Tt_ŸýO Ñ; Z#¡ºŒÀxÇNäéšõ9œWÒ눨þbÍ©–ÏØsÈ€ÞiÉéføs útkº2­èˆ#²¿ºÀBÔ›5Ï<ω¨³n¶–&¥ŸÀ:ïs°dB>3j†¡Õšì«÷\,²}Õ‹§Òëe³ êi L¾…‚1¹0}±kòHñÿå·äÐë•5•]íÏ6‹JSeÝ;ÜÑæÔ‘ï„§±‰SB¶’2zòKx›zÑÅ>O$g>•¡ý‡LP#1Ÿ‘»¼Í ú¬âÜÂôß’ÑtÔŸœ¤…›ñSžº|›Ç3N1~Œ,*݆vסVM€ÉJ†AšiŽÎ–`™‚ÉhY¯kSæ“ø‰.ÄCì {1†7zÞy:kÿfìËØ‹}ÚÑWY½kpƦCþI£˜ô<:^¯o - µš Œ]ª!g†Po\?”·‚˜BKÚ±SšnŒ*Cƒï)>‰0oÔˆý÷<á*›ía«—·|«7º‚•ø¨á((º3qÍg<ÍTtè5y×/JŸNOæm¶¥W)€ô}WH(?A^° ë7úa«ÈD¶çñb´ØŸKoßCס˜©ý—Sy)H tDÙú-ydøN”<¡Îvò»·ý®ü>å4¡M*[43 Ïμ‰Ókz­Fì õc÷¢;D.-òq;¯½‰/x Rx’¤X©žú‚Ëѳ赤‚¦*LôxDߨ„ÊzS¨™) ¸m ¿îMe'«2=ý$–ûzÞ}{ÈßeüÁS©ô×”˜?£ë=˜ †VFl¯ÅXHÝ^ Ÿ ¹°t_Vj<ŠyÆ/¦«¿Ía>+è¢ßïqÐ8› hd1[-U|éÄ ü ˜ÔÕÌ¿õæ•uàòsú4êâN»g“¶`ïJÞs1K_¼m˱¬«úRDhÏó«Ì>ÒuÄþ’ª†yç&¾+I†ÝEŽ,ýŽUéË&^OšHŸÊ4–óæ+ùoßæZ§²è›+Kµb Ÿ÷“1µ‘ôËülV{Æ”Ú&SDZ´Ëº„ù9ó×¥¾%;ŽÑ…S•èÓ/3)ÏKƒ[$ξ¶„C΀ –• Í1gá¼3‹’*dÎ;Sayé<ê­<Š~÷Oj.E«mBb~$ôÃVº5´.øDatt óV8…R¿Å­³ç‹)~R$©Aì:›‡•gWSö´õ7¤]›2Ð_ã+l=x®xÁ¶ŠI4 †PÍ|{4#oÄþûö6a[††ÆD@[š†K·É­XÚÞ=ÔZ?„¶›ÐYJlî«ÉtÚÜ:”4zÃõ††³ˆ ³ðЬ¬ˆÂ`ŸK÷t>]b>¯+|‚’¡D¾nÀ/~veíAtñdÚ›¹Šm5…—ß»Àë{hsŒ‰še°Ù‘;Áa´­à$˜€èjö¡ï „ÆÓoAq¨1ÚµråÁëKÚiÀ^·¥•WðDÉ1¦=<¦îÈ-g!6“¨JJ8•yNBýv9è°Åúj96ËÚÓùžà‘µœù½˜„iîˆËîfÂ,C'vGÈfÄþ½î$½ôY!(Îz+)M}x…;*öÅg$‚tô9ìX¨Æî¾kÀ*ÕÈá¬M— ¬Û×vmÁt³S:µtŸê)¯q ‚Ãö‘ù„bœû X¹á9®ã€Õ­Â¬n¾:Gç Ó’ ®0êu"öA³°&LÆ0e1Þ“Ö!î«Ôbÿ•0ç&Š‚f8]ÑîýZʶW*á r…Êaë¾V° Ó÷k(Í8ßlýºnƒ_WÊ?ÐÒ†½>†,ë³(Õ»ú.eí†Ã¿ì¡cûx¶mÝlüï>:"Møw¶ÇHüêŽ#b_òíc¢9nW®7Ч[L’ÏFÍäÕäÈ2§›bð.²“pá±(8þ( ¡ìö4#j|Ó” &è³Ò Â4ay+|‘eqCé²ÙÊ4÷r8YvÖ…œ?♈{뙓i¦E(=ðÝz?ÁPnz n¡œ@ô<ú Ÿ-Áãk½<4£íƒ§áû¨ÍÔ>FŽw…à‚Öpêºg Vç¾ùzIò røü«Áгùj™·`‚åBZ¾Qn?ÉÉ×Ççš"±-„ðoæÑ7 *…ßïnd·žÁÿî»ëñ±‘ùßæŠ,Ì7è@á7C ¯u "KK€$ ÓÐQ¯1L]”Fß?ÈÊþ9±ª„<&*Ag¾xO^|‘ »^®¥f†€Âì; +|.€e–Ψ>¬‰^2t=žÀ}Q?Ä—ñ EñÔop%t µÞt†¸{_"¾àm-ƒy¢a…õ²pë@À%yfà·‚×?‚¹çü®¸ ŲE‡3Ù¥GÙçþg<½û¼ ‰ZœÇ/lܶ†ÜC÷°IøÕd}ܧÁR~fÓ®÷CÄYaÖjàÏé0f¿‚±÷®Áx9~)åp'¶ÿÛÛý§zÔo?&Ü·§+ï‹QßÕô£N×qb½îC$µ§Œ4ØÎÏPG³þî(‡ÞÍá`î2&}E3·Üç,¹ÁÝíކ׉AóØðQšµG;BÀ¥µ§sGSíKséêø'‡Þ³^G_šCGa8I18PåGŽªÐ›Gá_òÐð‹î€oH0MýcM—Ñ÷øñ°8 /^…~Á ñ©Pñ® yoà}ÃihÉO¤>²ÊôŒâFúé_Ÿ;CÊæ÷’åƒA÷nÌ(©áT8qŒ+¿Žs³©|×ÿðoÈ•…ðA?úmÒ¿£ùãù,rTžÐHƒ*Õ8É´0jÅQh‘}/?&Á*à9)›þ'nœÄÂçu“ˆq³È>ê…6}·A´EHΦ)÷îARX(y{oì”(ÂüÏnä¯t Íö£±®Çðà”"™ÜN.H© Õ‡ðvÝj–ø$•wÀ4’³ßÝÍÕ×Hc¡?ܼéK]#cH ÷¸á÷,v‘lø¾…Y#¥.É.Ju’@Ùa [%Š Ï¯dA‹ƒnÄ-¢3O>Âeþý˜1M'øtr.'tYˆ¡(MWkæªÇØŒÿ>«@Îÿú!"ÓÛÇíCÖ-ΫPȨF…ñGoŒÆ<—ãøž_Mò\LÙ67YÌ4xÚsÒ¹—'áé[*,².…Ûç_ÄeÌ4£áž¸½B‚­+xï–ï„gÏdAâr*ôÞyëÙƒ^ÍûÙò(qpϬyqqLš2‡9ôi±<—DÎéEf4Þàf|.ÀŸ 9½mMx}þsnî W¼ô2 ®E‡Ñ]O#i@B„X*€ÖœgÃýÏdÞçuØýuô€åQ¼?ç‹ðœ‡ÎZÑŽÃG¸‰ ÝuJe‹iôúõtõÎaŸîå2á½Èÿè ÉÞxš“Œg]õE8y†-}7.÷L…Ç— ÑhìàÎê„¡×è*ØBJßÊQk™¤9â7ùIyÑB_ÑR›Çvîú!G娂¼%SzÉ/FöÃ{"k õ¤áê135 —ØC°Îý]xlÜX|ux‰¹{ü.ØÐMíç¡Y>HUž¼ÙA¥þX¨‡MM×ù0xškzY÷Ë0ôSF[Ð1çd¨;SƒþÝéðEpÚ…—óŽî=R†cFÜóº­êá’²Ì/íÇSã©›õO¾¤ðˆýßúÿW»CÑ«|)®¸ã«”aOÎÒפûã?q—c¹…î¢ì`â4H×rO‚˜ws!ï¿:ãmæ'É÷î^BÂ<\ì1µ9&úTW¾°}ñÜ!ÅYI\‡ 5¦¬û×zªÍŽìXÌ 1£]1(>Ì·—,ñ,S,éY±RzŠ9mûá½Y~8» ß,|€!¹uäx_Žƒ ´Ïy>~Ûü‘ësn!_Y·¾.nÛ± í–„e»uéƒ`¶2DŸu•!økñ|Ð 5_îàéáo'šô·˜<óÍ0ãJT\ñ[1=Àý¯þg]‘¯î](tËœ"Ÿé²Se?^7:œ…=TÉZ³æ9dÂÖ?ðOC ÿšžóBGÉg¤ÖT>:¼Üj³„&\GÂY Åà¬õœOo4w4{6¾ µ‡9*lUœ"î¸Ç¸êç§@ªw6ŒÛË”[‹X蔃°´;|ìEžïñ/ü-qÚ×Õ”1ÂGükð»f\íôþXºàøÚ[Ò%ñ•É=عè§s£ ·ËÑØKˆ¶Ìrš$á@ÿMŽÆ5Wš.9"?Á€Îo{M ¿Ö&WŽKgÜ@‘½_`vŽÓþÈW›0øc:T4_à|ì+A& 2’$õ—bs^¶Ã­“dÌú]d†B9æ,4¡3Rb°jÝAjj̃—þù8ß~®ºõ¦ÇÒê)Õœó³ZL¸ZÆ«ðRäòùv¾Zl{Ö*tk7ØÚiÐ;“ÞãKÀ‹K‘Ö —8¯ÂÔêÇiÜZ¦Æìòиàÿbç;N6VžÞÜæ ¾¢Ë"®IÒ¡ÂL9ÈŒ-xy_+|ƒSþô¨ËzÞ(–5*YÃý¨ +]Êâ5ŸÂ}aI¼}î ’¿¤ng#g£vç¼™MÒô‘þò‘ñ/é ëFíæ]û>‰•j"ÅÕe\¦š'6–¥M6r0ý·mØRšŸJ¹ãÓZ¹•ç×¢¢Þ” ¼ƒ…Î?ê:v9àÙV:JFÑÛÚ¤òÇ%B¥™î­Õ8uÕ]üb¡ã¡:ak:œÍ⎼,ÅX5#\B²üQ'˜6˜ÒôCndÍÞk0úž Ôݯ›*_Äe~bGürxQ ÿ¸µ-ø$[“&ÎKO|Gæ½¢±µO¸tóvØfÈæMöd:ÏáD/í}X)Ô€Ás5Ð~aI\úꕎÃÎqià6-&Œðzh=<œ±‘»ßÓÅùï6ï“øhÿW8¯µ•ví¿“ß‘¢÷P"žÉlj¤˜töâûQ•h5c6¾Ýã§a x’N¢ÝëÝ<{Aaº8EÖþ€ƒ7äuøŒï'Ÿ&÷áRakœùWø þlT¤9«¼f þ^ÐûÈOçÚ‚ºÓj|5¯’ÏÞÇY|–U=[·W"Ï'‚»dÿ ³y×edô89—ñÌLWíõ¨I”»=ê?nÝ/ûÅw?!B•@—X‹ÒÎöÑ4ì€44Üb¦Šã¼ƒøìí8,*ßÀ^>Y÷W™½3XŠÛ_έ;àyGÌd.ÿ‚ØÕ7'°ÍE•͈`» MØô—ùȽg#ãÿ‘ÅP3:ÌàÖ€‘ÒXºs›þ`,üºø¤þ{Á­ †Kr‘dmt5ºøânÓaŽì2ïp–§ŠP™åá >Ó‹«ÖÅÒOm¼ òøRûŸè]æÄæqõ´•3ó À™Û¦'q3vzí^¨LþÉ5õv‘1[-¨Õ2Et#wd¦J¾_ƒ_6§Ã÷²sd}ÅOTùR‡2FCØwHŸ-¬ô€X¥?¼Ì€l¬?¦€ß-‰ºËEœ®ò¢›ðá{è<…Ž¢Èµ°®07ùçAk~)×:û²y ÍŸéΆ¯‰iÐÿÖÚž9I*ÆÁ”¾1ìä—õh~9–UŒ '± °?Ø]ç÷ðO™ÑÿÚU'4ñrã;kò¬ŽšÀï_£™W9£—³æ-ê~÷Øô“‰ËèÂ?ý'ì¯U蔢ðr¹9U‘]Àëϳç¶/èãØé‹{”@T=•|®‡oÁpþÐ-rÈÙMk*±ÕÎ¥¶dqÚéùÌ„ÈÒÙ ¹£™|LJSg£­ö`p3ÌÔ“¤NÏ0ÕU*.â^sJ=CAp³);ñ¡ˆ:ËtàáUôìVwVßxøM‚PælDË•OŽØŸ½¨ m›u0$Ó÷®KËМ}u­v)Ò™ø66 ϹD{u¯ã•µ‹ØÚ^=vd¾¬¶+åv¤÷q ŸGcz½2+ûö-•ÎÂÅ–üirIXøCíÛ×sþ/%qÊï½,år÷+L›Ê™HE&rø_õbÉsxùç52G²Â?ª‚ÞË8fü\Rº¡L¬,_6•-{æ5œÌG“Õî˜eû”ì9¯G¶ºßÇ¡-šº¶ì •97PHc5éÒÙƒ‰0É ;ÝÏAØ)iˆø[ûd¦ u ‹CçÖ´ðæóüO{ÞÑÍs¸3né$qç<Ð0%‰ïßq1}PçZ.Lʼ„â‘'PÞ[‘.gAMçq=½‘Zn‡¡„UèºÍ‚¹Í-À»š \G’uÈÕ¤+ÖVÀºøã¸ªì KpÌÃÒùøu¬ý4* 4Ör¼Õ§PØo!Kyù;ªÔá~¬ ¶ø BcòÀ±y¤OõsN^óÐkFw«G ™¼ ißPàœ’rhˆ4`ÕÇD™ì]¶:K®ÎdlZ0ŠŠÍֆǗ²f›qüOml±ÃE”‚È/V,Ìú 1,Æã~™#ú÷Ø¡í¤Çc˜ÍÝ íKsiŽÝðjGøïÇ ‘ݧÍ?ÕX½Ù jþè&FÏÀÇìYÁ¬µŒ&f²{ßÐ`óXê>.Ý~úÂLÅCôÌÂ5\B óº*/¿ÀÌÈN‘¾1KÃÖqÜ•èÅp6Ô›«ˆr@ÿHq*î'Â$³Œ˜Ž³& ³Áß·±Ae7º ûo­`+õBÁþm(·ãؾ·Š8óË^¯W ÞQ_` Ÿ°§Ûа;Gœ]½ ²O*1M5š[-’@î™FC9‰Sn?#iú;ðp Ê8ŒØ_óG ~º„·½.[[­Ù‘™”nôpd¸² ´Óêqàòx°³Á_%[è¾Y^Lå`} :‘jó"دÊÑ"ó\£`ÏŠÍp-ȾÃU{¹ây4ùo;ºü€_r'è‰Éìgx%¾1§è–í…£'ÊQíEc©›D5¼U¦A¸‡™›«<Í'ų?ÚÇÂnY[9Mû™Ëù¤›œ ߯ÁD¹"*yfè^KÓóà¹k5ܳL"g'å°U{„øëÖ.Ã׈Ëg²K8kÒZ¼*ö-ªÈ‚É@2ð-nåáÁªLÎâ0µ¬…/Ë(ÖÂñûÿWÓLøt\Ä~ ãîäQ·“"”kl&ίछÿ¸týStUÎVY– a7a#q€r¯½Læ–˜ ‡ÑQR‹ØbÕ N½z<½Ð?ÕlÍ|ÇgÓã{ßéË2#øm„¯ðÖ„‡7‚aJGÆ*ÅuõOPÇ' \2=àɺ:¸î¨.ÛZ3D.nÔ%w¿åãYu]j¼d{h°?ò]0\*ä³у6ú¬±U‹ uäbßæ]¼Û!%F„^MÀj'ª nlCÑ*öúƒ0(,a'SÀÒÌ tì'ڲϖ‰ï”‚%/"xzÓ¥X‡ÓR6)ö>J¸¥C\íE?“oÚò‡8_œã3vR¡v["݈S¿º`ªd+ÌöŠcGVës‹H ÿpë4ÈMâ]¯Ex"A×~^È3žâð±‘ù“—ÁêÒ'0“JDÇ»c‘hÃò•›é’îgð#í9iSK&k6mƬìXV#\ÂÎÎÊÁ5¥¬7¤…'€ÍD°p:‹+ð™±™žów£bã¨óa|ÿ*–™H¸¡ã—p†Iý¯.ù~˜ £fμÇû+HbF ÷Ïfî/ïrª’-ÈV©áå7aØ™íÃÌÇßFÙ`y3ØÅMöQ}À3dðÄüÔ@Ú5jÛ|ÐŽ’{sa-jQ·Ž%ÌZ:„§°pŸ#ܤ'òB\Ÿ~ÜŠ¥ˆ0݇BõM²|Þ²_ÂlíaÁ‘üçB¸ë[ø‡È|ëñØ†ç‹ '%5¬mhM<æ<ÅÙD O‘=Î+¾LÌç64'p^ÛúqàìŒÎ}F<²qs;¶ $1ƒmØ÷—:rU·“ÉjKøC¾ÂøärÞ|v7õñ”úLq¾õw®¸,–ØP¢Z·‹è¬e-&Gx“ ŠBÿˆÑ™&¦tèûJ,Ìek|º”åÖhÚ3œ]‰M䮨€‡2DØàéeÔtŠ0õ¼ëÏ.1Q:Eì-¬~ŽÄoøŒóöbhm+pcpú >ðWfÖbúLî"9Ò²‘¥oüM*$§²Y=©<ß6E–ì¾OYtÂg÷‹¸/bÜÝ’»Wagµ4»Äoä^?ôb1™š ÷µ â0¾ô Ül–eµ[oÀT4\âÐþ÷AV1öD>ƒå÷&âa-ð¹ïsÑmåb¶;c.Ö4•ø¿ÅgvM5ÏpÎtHºç&ŒeÓ&’¶Ý±¨†ƒolÁZ?69'ÑL£±l‹Ê¨X¿¦esp°Ä’úlœò>¿A6žÇæ\‡Óþ¶ÕîãÜc¸‰§cÆáßu!yl†½'~ÒÍB=ØWyõDø]¨¼N„Ô¬”Èg›ï¥C5ÃùA¢Ô÷|ð«`i Å š@­]×Ñ9ì€ä,Øð"'ør¡¶ë™‹îTÖ¨ ÈåTY’E9\Ï=L#n4céÌ lÕìlίú,–Ü0gŠ%Ùhuu+^[áCEzíGì/¬ò†Ë\{5ÂgZ©²=«…¨û5Cz=_òôT¨ÿtYöMc<ÞQ u¸ü¿= ÁdÊ#XcÇ“_3_Zli“ׄ&LPQ³ü»¡í›jo,~ÆòXlî•yPg¹÷ dîÜGOåб×ÈŠEJÔgÙgÒ-õðÿÍÁÑwÁñÖf4ŸªE*ç»Ð¸”î¿g÷_ÿš{éØ°Ë‹ ‹WéÆMÞ¸@û(y)}Wzއòp?ªyE|ƒ‹YÊW\d}Ô¡»-„Þh`ÜŸÛD`ýd¼´Wƒm³#Dµ=Ö®]{‡neÖoðXî=VÛýŠcgNÂæcïqÛ/‚%óÇSݵ—AJ8„•šÊa¨˜<]ì@gf °,hÆê×7Ñ xAó?‘â©•pIâ"˾íµrùüÕY‡8_£Ð0;C£“`ÕßX4¸3ª1Õöx®¯ÁþkçÉ›Û[!‚«NƒÎk1æ=Ö*;¨JÚKHªÉÀœKÜ\‰gØQ£©ÃvJV€ òl–<ǘ߹sZ¶ ³ãӾ᛺¤úß?Ø«ƒë7¯Áå÷§0)–I&ÿJBt¤hõý—\¯6õÚÿUC¤ÁõžûçÀ£Â9"ôHñ úª-Eù¹x3v³ÍfÉöa”[clÍ2zÖâõÌ{nF³Ž›˜sß#œ÷fÍ{ÇÖ¡߃ü5EvíÐBÚXxmüJ‚>ó½\g€} –:MëodÐ/G³›ææ,+ŃæTÜÆ(îgòb=ñá< ΞO#LV`S·.™:›vkPÕ•Þè@ Ἷ Oaúuñ$*ç¦U£ïUܹ~–ÌI¦†ãbGÆÿ=™¾:CǺïWähƒðltÝ·úN?߯0Äáí½н‹®~§I›/¥ž;GC{ç ˜ÕSŠVôF-×fƒK°Ž“ ¢±ú¤Æ=oIŽ"÷” Ù®ñŠ<‘ NÔ੤…Иú2W.#ÿí%´Þ“ÎÓÖ›µ\Àw%îÂ3!Ús\ˆy¯”¡J Œ±¥~÷cØÐD òÌ©—¹>á¶J± ïytÁ· dÞ¥ºÚäqÎìð)#ºA+“š^|+ÛêéÎ&u˜®nŽªïbÙ.ñhzŽpçžhR‘-aW´“ŸèüoDÿùMW朳>@S¾5iY¯„¬OšÕc¶®Ï1«¯=ßÇ‘—Z£ÙûCOȆÜõB,aÅðxÞú"úæó/íÙVU[讇˜äÿ¬Ë­â"œŒp¥É$”7: _”aú5»*@}úGá=_Z^‘¦‹r 'Yõƒ,wÛX¶«.ð›Ø7"æ‘…mùÆT]Ù‡}\Ëÿ>-’éÍ[G£$£8ÉÏ9 ¬]ÄiH‹ÀŒåt÷ž~,¯Bÿ>ªD= ºtô9¼_Êã‚8G.Æþ,Yc¼¨p¶±8®Àæ¹:c΂y#þïæs ¹#©þK–U]“ÀÍ7ôØÒ|-&/Kë–å~W¼ÀªäO肇ðt› ÿðNg¸U.Þ‰»@¸k“I•Äÿj˜¿f'3ý·p¼ÅªDÉzNÉ»ï¹hííÙh8O– \ÅÞðf ZÐ  ÎâÏ \Šì)?AUìI®¹Ïšu⥄øo¿g¯¦œ,†Õ>GÙ¡iTÈ •ÄÓÅMq \E‡¯ÅlÇý´5]Ço§°4;»îl¶Wù[ײMQ׈k>‰JLÄ]C·áÌÖ<2|L.ýßüצ{•è0WU}å0s`>ÎüqŠßµãÄäh*üZˆ,mE ÃkXú<šqÓ ±1y-Ÿ]}Ar«bNÜaœeIðôP¯Ïóta¯{¾‘ò'žÜøEú^²ª_¯d­áLÊU ¦·Ãsd@ëuʵç]³A¡_’~â~a¸~7Æ(žóê¾Ó«bzÆÔlu'ô¬ÜI=ÕÍÙðóÁ®uš/Êîi¼Ãu~“YÜÛkÝ¿:zJAØT˜48‡Ó±ôËδT> Óµ¨NMý\t·©Æ;ŒØøfEL~zsÿ®W“ØáÚ0¼0¥>”ÎÅGåXÛÞ‰&æaþ±EàrRêÊôYQd'5Šú½Ò¥ß]£kYHädðQ´S-I€ªm6Tcò: ¿«Êtÿ½…±‰†ìèÉNPl}˜§Nñó¨e°6ÝÒ¿šn•Eß.Ë¥P{³n y- 鋤wRz[q Ë}>‹mûw åÎ2úÎbÖV †ëø¼õ¬óLêVm§ÒqØ2¿rsáDµ0×`Ï$ºÆP§k@“[Á[ç$÷ÆPÍô×üvñ(¾õ?S63Ç“fü%M_FÁ=•½#þ?áo?húÊ÷3\Á/yÐ8‰Y”Ö¨Ò7c¯áè±ïqœVrôÊbIº¤)W6GéA´ým a:ìúï lOøf©ªƒ¹b.ì[ ·õ^0v9îM¤¶ë”ùo’}†¯ÙJwYËS½™Q>X—å)°„ Õ°fT;‰ 6b¶eÑÜpßð¥¾ÿ½Ê)xò¯ ÜMŸu|ªOÊÛò©¨›6“.߃ÃùïÏxðBüê™IËå¹çÍI(™&GO.E‡z{ pßyÕ;«ÒEgFÑöæú9—Þu“tðÿ·þûÛ”Kä©Ç5|Øw€^IWa=4 4ž‡ÆÖ§ê”>⨇q>qÌfð ÏvÝžž¬„ ív¬êj%¾]›_ÙÃøI¬£ã"ðMÿqžÌ–n L%-ÄŒó‘b÷‚gѨƒ‘¬ú…­K>ÉU<£¼ Ø–ÿŸz=åOg‚‰}0è¯íÎ'!s¶ ˆüÕcIªÝð©U‰“ÞÚnƒ_àÚ© :Øð‹h{@s5æ±Ëggâ$í[¸ÐC–JÕïä.wì`OëͨlÌq|Úü W r§7~Å‘l<©VÈíTˆƒ¼–HJguð—Á 0ZäŽ%ç°fŽñˆ5¤²jX6QêO>‡=–Ñý¶)ØôF¹‰  Èå*=³¿ÞÅ®›|A•áPÒPŠ©éIL÷z•—ÕúØÇÐcè±l#šÈÔ¬Âq|Ö} œš މªÌèÖ~š†{AÛN–ýûèMÝΪ±ðqTVú,¨uœ ~·qÇþÐÞŠF #h¸ˆ5³½¤ Iâátß~²ÅÌ‚ýŠ’ã­xÖ…n‡Á>¬aâ$Z³#ñWm¥¿Ö×p/¥cö2kª\Ú‹FF9è¤T¿$Œh“¨2m°IŤB#ó_ß§s×äÿ‘½@?¬¦´àuèÚ':ÎÐÿêˆ Z$™ñè÷w‚¼l5¦®ƒ¯ påL6où9ø›ROÛ×2ò8¸,Ú‹ŸfÔùg-ÇÄ?Q´üáO|Ó#ÆvÈEÓ©§³ ¨ ‘¬f焳¨‡û_\|joX_ ×'˲ÅÂyì7¹Â”Ù™ÂÒ0:ã8}¾å.»},.¤Ð¢ r‰Kßc0áy.¾ÍTæ¤qT±î/k>Û¹NØáxœ<—d  ±¿ôÒ/býÒù¸Éø86y—°×†:ìƒ/R çñþÕã#ø?Ú0ãC‡Ñy’,×Õƒ=+sÅm7ÿSïÏåÿý#YÙ{ï‘…çušDECZ¢)Ú¨¬ÌHÊHVƒ¬Dáy‡ÒFƒ¨´Œ$M)Òúy}n·¯÷ï·n×õ8ûuÖý\Ïó<fMšd×zñ›ÌÈû{/Øö“ópØG…ü.‘¦!g×`}µ7µžë —ÆJ‰ŠËw~ûœÏÞ`m[ÉÝ7^úpŸ0žOW‰áœ7sj™…ªz0S¨ ä$š8êYdP[d‹‚²Îh†µÙ·ã]Év›à—pÀû [á´W6fÛ,…¦ò&€õÛpPð8P¯ià*mI“ž±‹w?ƒÜo̳ŸçR:c<–,‘;Šãò9iFÌuÞNn>ïmލ(Êo&³·]aNfÝÆ=ú&øƒCïqEµÑåZ)~:N nFâì³n4¿ûdCm¡?iÉχ+[H¯÷iH[’„oˆ%‘“'.Ò¯p$Û­‡x©í^/¼Ú,ÃÍQÛN?ÜÙŒ«îpøÝ·”&ý©…ÀMAl{¹/ùò{é>#K¯\Çî‹Ø³Ì–ü+žNͧ 7ÿcnÄXÓM¿¤È…Ú<˜±2œî'¸Í  Ý…ÈHéhýÉ\‘s#‹"È}Ñ|gÑ”=Ké¾È®ë]-´þÛDµ½¬±Ürí·-ÀO5bpÆ”s| {Ö“íÆ?8º†zÔ¢e€]¿¦u‚ÿÆ•;!ËÕDã³%ìÀÂV²¾Ã¦mW&w3øÉÊ·Í(ªEöhdàÎÇQß%ŠÅêÏP‡û}–ÁÇS»aïTy\uâL‹â! _%P³·ºþGÿ<âÀ޳ëlØ´FWmN£“SÃY˜{kmv^–ÙŠa$qߨ>/>ÆœÙ=Àæ(œde/)‚È”vØ¿Z„Œu]g<ŒÌ‰þÍx4둦v „ðâO]ú›œÝ›Ç–µ¥g»êÐ{§žm£gÛÜpä¸:M6Ÿe?Ö°ÜÚm˜Fø2¸{ñCÅ\àë§ž"ÿûþKzÛ\r7žÊ!Ç_©µ_×R‡È-È\: ÿO¦÷ù…ÙO+Q¢g=q× òc³pó¿'¬}Þ?TzŠiâ“éçœfÎ>ñr&`G9>ɺŠk_á>ÓÂG¿gQME Ú®n‡_™r‡‹#ÊôZ2‹5P8kœ«$‰Û]Gb¨f qï#[7™å;;OzÀ¥h˜›Ç«¤%Í.tŠ2q^¼‹¹•97Üf±x£”ë«ÒÞ – ´Ø²§¹ÒòÑhšésÇ‚¯æ^k­ÙCÎä–¬ý±žÊEl£ ãú ÙøýÏï´@~G(šêE´ÿX㴞S§“µ— `ie®]D¥œÄçbYo}(UÄÒJÃ4,¾ÓŠž%{¨À…Sظ™¡·rš×Rë‡5àzãéÕ•%¦Q¤vR Õ:JÄÝÐSGÿý- eY“Éñ !dþ+KZ1r ”‹ÓÝ® $ö£8,2é‡ÛC÷±o0—õm™IsìO’)wl‰ô -’=$M´ÓäYlMù\"¨FÏ9ÉâÚ kÉÆ33qž‚ù½ )_þküîñ’’3Éâ¤#óîU!·VaÑ=‰üß´ó.Ö¯©`É\Ò°šý¹ªŒþÿu*‡éûØ9ô…&¬B)¥Ô·A‹^Ƀo§(nŒ‰¦²¿°î6ádª~XU›¤P—ÊQÅ_6ä¶³ Rèøjއýp˜tcjxg»JSÇ©¦è}¯ ’7, ^ócè‚k›éHÓ°½q‘{Î9ŒYíB¯úé€=¶Y½æx:Gã-½`wX–,kŠGÞ…btæÈVºÁÍ•têX­ÉÅFèâM}¸¦R“¼ú”޶/VÒÛZSIePé’_BÛÁõ·30”¤"^ø-RŸ³Ç¿ñ‘Bÿ¹øpß;N†|¸¬Ì„»Ü`fÎÇ/ œ<Àüš6Ä [­dr÷êgœßb^ÞQ2²W‰Üª¼Ï^‘='NJ3JÁøµôÜHÿÃÈ[îpع#%þÁý£û1ça7³éf>,¶T§§OÊÑG»² ëš |%Èx­’§¯§wáÖw–~e³;ãwå,曂vŒö¯ä]ÝUímìÙk‡ m1òg“}—Rk©ä…“\‰ÀÏœ]¼’tö M:õβ¿æ®¢LÀq1›Åíñ\ˆ-ØO $¦üoþ|<¸{•ÂÕ\†ØÉЖKGa¬Dkæea}QüÉËY Êuma^"è¸R¿Så¬bÉ4]Ù‰Š ;è`òt4Ýs„?³“Û6Ñ['g`‘”-m< Þl~Ÿ<¾x€FZ’ æ[Ô`‰¦§”ÐoãmL] Â…¶$´] ó=n€§\*8ˑӹGÈÙ·0»9 [6ƒ7ÔlÏFÓÏîtì™´¨9’ ׌ÉgÿZ%²)2.çÌzúC&HTróM «mÂI8œKÚóèåã/@ìó~ºë¡%½ì¼{"þ=ö†™ó ’E†Áyõ}¼³Ý•6N^-wgRËÊ$´Û/‡Ù§-Išq:r>‰cb'ÐÌ÷9˜ù¸2÷4ÉΫÑè.§L=§ŽÛâ% ÷޼F“ÄVh-?‹—Æ£ÆVú@y;ý=g7®<¼º†Pý@“ìýÂK›uvQyÉz°4[ðß h,sþç3É€K+³õn&ô,NB¡¦\È=õ¯!jþr9Þïª!¼5‚0F¦°a‘y< ’7JɃ«tX:¬æH`Cók¸ýD‡fXÄÄT…à$£jöq“û æ?ž¡ŸNéDý·%á»üïI0Ï1¡šCºðÔ[•¿¯¥ëE·“åG‡˜„¯¿±¬#òLvCW¹=ôV_„/SµáÙ`«ôàÉݹ¸ÒtÙ•þ >¶™‹l’Ý?Âåñæ´pÒ!–ž—ÀØE>tz˸°‰®ºQÀÝT9 çGö2ߦâåSiu˜U΂¶Â·X\Y ¢‰ôÇÜ`Þ׿u;ò:.ö|]9Ú‹Ð|e:‡=¥‚»fœFŽl U¹ñL„ÂoVж¾{Ÿû°Sc  ÛàØ á¸Í±%ùìÔ¨@*1Õk"þÛF×ã¢áåëÊ|–©Ü}&¡Ì[ISÆnQwðN:£³®x\Æ6›HPv‚ªŽ¿l¬®0ÔÈ©‚üÔ:x"\ˆ§÷é’¨ð`ª»Åëº)&¿xˆ{ž]Ä#÷/á_5+ª¤u¶…§€jo/Ü[‡¤M‹ª½ ×ß—®lôB£ã˨{d ×LbÄy„±Å¦–ôÏ’³hÅšv2P¤‚§6Z9;Öi† IÔ¤ŸÂá¿躵²h×Á¾ w„ß%™Ì—»0¨x É\fH£-õhÊïK쥀cP!»T‚¨€ýÿ¾ÿu>‰‘VŒ>{ΰr':¿¾l—÷!øÄuE}›«0|W—îY?$Kq&G?q/áƒmÊÜ=ƒ;ªúÙu.×ðŸ—Q ŸÿdI3?¾c'»„%&ô–k3slH‹¬ŽN@ãõ!ŒÛCgÔk{ÄŠŸ»Ïjø Ѫ (Ó| × ¸‚Æ‚'O²–è€Ë\1Ò#ü ‰$C"V A«ýe6Î) 6eۃ̴½ ûë.ûx<¾´<Õ¢¾v²po÷õìßù±ÚoË‚„à¤ëÎŽïKƒõºtã¿~l6©ýâ„ÿ;´°¸2TWëBÊŠ}Ô4ö8ä(1þ¿ð$fôC‰S<žLÆ­®ŽX6YÚ¹‹°`{<8¼øË\Œ­‡äbuˆkmÁÕYÑÌ@Ö=fì­üŒnÆk¾ m )YNød£ ºUÇRƒ€@ö ñL°ÿm†6ƒÍ°×Ëxƒ®cú’xÄ, ü6¹ãI%høé=.§…]\)ÂÎ]í¶‰¡w’}—„Ð]”‰›Û‘Q¾“j‹™w“YÂHP“ ‚ËüaO43íg#>ÛƒXÄ­‡Mz§áSÒô®õ5v`úE¼ú:iÿ«)°2ÖÛ´æÐ ×NËè æ!gôfãÚƒªXçjE7IjÑ3¢Ú$Í]‡è–+Óª¾~;øÕ™6V<˜ÀÏeJgó±ÙŸÁäY-û#óßgP/ò¦g+7ë:‰œ½³…~lüƒ!FÙ—_[`Åio,•2fçI÷;qºÉ‚ÁƵØŸwt쿌bs{8þ9x6œÌ<lj¶·À¥6®4ÄúümR‡ÄÖ‡ì¨ë6²~Z?®Ÿ·’˜ªbtÆ&Þÿüêf]¿Š£š¡µ§µŽÄ2zÇ99Û2ÀñˆMÊU$ö'WÒŸçƒ8 k¸ÿTééA_Ì[ºLó¦ïDyæ¾Ð6cÕ 4'å…Ù”ÝÏÈ䊑I–"$Ñþ]ÃH ù­w€óãÎ[hO[~Ç‹ðÉ)kŽÓâBæ‹èTlÝÅ™ú _ôåræiäï7~Ö£ìqU"SW-…á„«øÖ0 ÔÜx˜+ûŒ6²ßÙw7R˜i­¿¹†È9$ˆaûÙjA …â®…ûL;Ùþ=õLAÍd ºeOü£þ;ró]'‰Ít¶ÈÿS’¦ã{[ætâAü|« §…[RŽCNIχ}ʰ/%ОJ<*åÆŸ0f$ÊŒ°ï¡>fÌMDÑ3˜Vƒµ¹·™å*1ØRÏ|âfq$Ÿ{“eAˆíÕÆh1<´a5¤›ÖÖ`5ÔVžRpþ ìKˆæÔ?» b©‰Ê˜9þ|VÌà€üb+ºZþ [¸§—]š/€³Í•Ùîæeä„ Å¾Û©xèèvºíK ã$N+>f°7oÙø)ÄMœ<­|ÊÆxR|?¥ ’Ô‡˜õœ¸*Õ•cÞÇÁOg…àf‰5¹2aÿ·½àÖTÕCÀ•gÈûk5X÷þÁ%—Ù‡­*¤ûã2ôæ¶á^m?ü·-qó=…_[¡U k­@.®ÐÁï\j¶)ÖJÎżە°÷úE\»Öƒ*pƒ¸aæB…D Ìù«Å¦–¹1còôÁ~i*ÿe9Ú…Û߈3OG$á—D1¾ÛžBÇe±®~1«èUf÷jÚÖà ÚÜþ´?h¾" ŸÄá«7³8’øÎckS5 Tl5V\,aíOmËL6útüª G§}ØèÊÏZéó²ebw@ L‰V7¤aÃÌ ŒïÁ ý¯ž6ĺ­[¦ ¼…›s‘Eò÷¡rõ rw— 6~Y#±±ð¹!…~-x‚ÇEòpÇÁGðdëQÌ2gW>Dû½ «d‰kîÄ0×¢nC­èOÐÌ™…«åíÈŠMÉxnÙÆë¯%÷œìsh>´ƒäPcæN÷Ç··çÑ%’Ñ oV*z›ë’Íþó‘?{#£xL5?ãéIï`jßJøGc ,×8B5ÿKàmM‚:™ßlò43 ¿>—ý,‹Ï¿'`J¯ ÎýšÀlçà ƒŒŸù°ï+«Àks=&‘—i8‡ŸÂåOÌáï†úÿl¤Ï˜77‚SüZv³ wç3jaYðèØºÅ†Ë^ßÈCŸen›=:ÐÝæLg6~CU_+®xæK!ób éx~–µ~ø/ÊÕA““^-3ÇnÛ F’!X]ޘ愱 BxiÁ?\|]5ŽpVy~Æ¿–ä­Äcv•†1é²Aç~~²y¨?yÎ4€ôGYnÒ¥Wv°Ø¶µ&ÛÍ$3¾oÆ-ä85šôžuý;™¬áL˜rž Jú¥ÁÅ£¦8{ÊGôΣ½oc9þ2dÓÓ1ÆÉm ±)º€o$G&ôßôùÍ~•Ãcý5à­ÛÖk"°¹\‡4þ¬aÖÿzÿö~gfŒ?Ë/õgÁ­ýòZÓ´˜yð~w#³\.í3½Ç>Þð–]×·t·ˆ‚lß]%„ãcÈ(w·¢±ø!Yv½ rꄽJ˜)¡Xö¾œáë<Å|›Îê­µaGß?ƒi¼¸,±ÝK’7OÿâýÏÈãçáºÛ/´÷—¬ÏJæ±oÊæ“_¯<™A'´ûU ó=È›CG&øÿ‘i¦x>#¶´#y£“ÑÚÅ0ÿñ?Ô9÷…©Š‘EcçRf†T"ÞÄúTáSXlE/­à?7 ²Nˆ ˆ:†vÏ àæë"ªËOxm¯±òÔ’¼îœÇ5»û”õbÖƒ´áš!n>±¾÷†ƒüÒd‡Ëü×>€*ùÙtžÕzPžöê ïãôR\²½æ\Š¥—¼·â 2é3²ä ˆWÃÔûpÓ8«\‘o~{v·i§ÁåƒW É• ÒÞ)p@e)»ƒ!eñÛes2g²Ó¤V _Ϩ‘{å¦þß>|„+Þl@óÜ5¨e›5]“û…±Ou 1ÃVì¿ÃšŒ{qñ·¾ˆ"‹¨Ì¤rrù¦“çó!n™0eÒ-(¹l„‰žÕ `S Mº©4!2ñŽ™¹ò÷Ô×@¸[äμ"8$uœlK6#2é_põÐ9p m¼#¹q¬lãAà./DNþiâ2ž¥€óߺÝHWÊûµCº®²Rš˜yé"]çEo „Qq8¡DŒGIaï,:«½Ÿ{òÃ˘•DÂÇ”)öâKfBYz,Râ¥!þ?©¯"QWu˜Ð¿û ´Dl.5ã› .DUD¿ÿâà"öEvÀ+_]bz\Ödñ³YGŸ3n÷‹ kŸý¼: ïÕ`ÌûúÐé3ä5žçÝQ ç ‘;5—™ß¼žÒ¯¯™«[ð'˜Žl,Âñë0=>çš*‘Ç\7‡ßN€ºÉ9(ŠxƒvõñÌðlatx™!?xé×\mâd½žp†Ëð‰¼.å—¸->Ñ4Í´x?G_­=ðåW;w® }ž< þ‰`c8B¤ o-)¾ì IÓdÉEb«¤Ç‡h£år5o7‡˜±pqLeBÿyóf৘—|Ãt–âö8XëÇŠëÅá¡•ÚèZ9 1"5 ©ÒI¬0q\/L{×éPã±Z¶³;„ïbÊ$ØD*î‹Ð€~Й]ŠËšÍèHÿ'h}#Žö+è¼÷Údj¿…ËrŸù“ÑÍy˜Çç76»é:J$ˆ‘Û?d ‡ ýó›— j˜îŸÑ_K„z?›Âj«Qw!mÔ @”F‹Y±(5º®ã+Åw‘y¬XÈql6¦™ âIÙßI°§ÌŠly.S´Ë+^8vš0ÓZŠÉ——,Ö¥Ûsìšÿïü÷¹ƒjðît,ª]OÃo :TP¡ò.dVtGpû¯öAà¶ï°Õ´”¼˜qšT–ÅÑþ߸ŽÓ¥¡­+ƒlËÞLï™,¢Ñ^¤ÌñÕ8)LFÌ Íå‰ôÄI:bjA6ÆO'·Gâ¶Èxì÷ÃÃS2Q;#é™Ãyd°­ ïmÀ—«{ñ¡ÃjæmŽ2W™±g¤íÉÕ55ðß}6N¢ÿbÃIçFGr£mÜÏýÀÈñ/"êõÚd|Möh©Ð’$Õiôí¾#hH–dÚcrÆuưf y{’ Ä7dþÛǹ–<:u¯Æ„ý·ˆˆ‘ÑKsÁjËwf£Ž<9ù@žÚ®ñ¶á»q¾ª¶¬_|; A"o1̪š›(AÔ­­±µè4ã8e'MÞÕ·Ò:Ù#’é -§HMm!ûnsì^áwr´¹· 6=ò§~'‰}B(zn™ê«OÀÑç ño+=‡¡…‚³Iì3zÓÈH-.¦ùw’ÑE?•øŸxË¡dH²>+Óü˜Ú$xe·1»_C‡ÙzÄa[ hˆK‘@ù(<ÈÏOÌ?3/ãÈ ¹¿Ê}ÈÜô±&6?Zà•¬ñ5ÞDNúÓ®QÇÃæó'ú›eç7[+ëÌqþ‚'xÞç 9#SV¹»Y«ÆÇÜÝ/íÁ |˜ÝrÚŽ´½v§Êò?Ù×3¯8(³ë·'Òal!@]Ù Õa#²lÎ\±¸ÃPÑå°Œ¯Þoû‚ ö/PpÕt‰ò#噵t™y%>Û½EãèÈÓAäYx† ºÒ˜ùØŸcRNÿë¹Ö]LÇ>jQ‡ÎOøêŒ0©P³ ¯/çAqkzw’'¢ŽûãŒ@#G óâ˜ú%à+)‹Õ³HĹÎÿf7³åsïáðÔsxwökPNQ£œdK¢¹7gY]DÅþë¬Ë$uº2*˜4@ŒÕeì–· Ϲ¼°8_äw!̻˔ÌëÃMRñt0܆\ž9Äl”|+”á}`n|bM¬‹2@ÃK‡$-®¿$52å÷G`èãüÈtÇyø†,š”r“hNrÌk”¥z¹&¯ÛMØ¿¼×eà´tá§Àà –ª@½¢©‹´+ûù䎤cõ¾™øvž|MZÛ”@Ã=>z¢»¿Uc“ºTQ‹ß 6k‘åïq…Å8_;ôˆÉ²¸3“žÃñ—P¦Cô•‘ª‘Jf¤Ø‰é±‹#ýùq¬f_nñøÊ˜EÿÆ÷Ta´ÑºÞÒ løwâ³ÄK( +Aæ=4à í…Þ) ÷ÞØ«¯F~Û›àph±˦²ç¢èÀQ\Þ§Oÿëo~4å=èuï#zÄÝÕ÷²ý™tÇzH¼éGë¥aòus546¡ñ¦f ‹þ¸“]ÜBN]2‡¦‡ðPAµñø‘¥D—ª—åI1êËÌx¶ä˜ê:%b6-¯DÇØÍäðÑ"˜ú7ÍAÝû)£Úh„é¡óñýÆŠ ý«‡>ùW\ˆî:Á^Ù¼ÆdhÍL5’0æ]=:dâ nH̃ý?h6VW@X€åÏØNëíôÿÚJâñä‘ó? ö×x¯-…9 îÙxÐÑò ‚‘:9rU$‘)'þ{â¸'ï{ÀšôyD&ú#ÊWTÑû Ãp`'ÀÒÆT¢y¾¥7pKÖ|–Sñ!¸èíÂŒï•z=­¯Š"æjåuüÚwœ T˜B ¾ CYªä„ÿ‡†/…øWÆð½Òœú®ñcŸìþ„Ç96_JØ ¼ÅÔiÊ!z/dýóI•†ôÄQ§ËïñûËU´û»žË=†NÇZñz*Å Ç÷㤠®*J Qùlò.Šà‘è_ BmTV2»~øbbµñboÑ{*`§õ³8O좑«v‘´Õ6p[/."­7íéÑUŠôãyÏåÐm}ñ˜ÍäàœŠ)¸Y,¡ì|ù Ô9‘×jÎôaÖoxTÓKþØÊ1mY­–D6—ÃþœOè·ßˆ³9Å-ØÞûiiÿS¬ž<ÁbÂ$°oS~ÿ$Mœ¯]v|Â$éTÌ“¡Ç•îᮢ×Ü¥9R ©|„ÕGؘ°$ÆÈà œsU¡¾}"$ò¢"{#•üÚ…ßî2–¼åè%°#{@í|Ô^—ÃÂ4`ß`×ñbûºôÙä3@kŸC"_/´žnb2ï1ï£â!³~>ô¨Žb]È1¬Xö×_ËÅ[Ùâô$Ï çúïåè¾g¾ô‰aB·:.!:p¨¹öJJ‚âôv„ÅŠ®`<›&Ða‰LÖ¼·Çe°s]ïB&Ó‰žž‹ðô“A¦zÇÀ„ý›Ã 쯟Ÿö©ÒoC?™¨ÐÅàrø ®¹ìO²êvRŽ&Á»†p9(ˆ ÜoBΪŒÛ®]UÿÆO¶®m€ÿz»Ä¾ƒ2åœü0îÛ“}e¹l{¤2êl1…ϳF8K3¾³vL&\µt¥ß¸L‚Ÿ9Y<+‚ ÿTº‚±'Z†Šž2$_„…CkÇk#áƒÑïJÓîÚÀY•ÓÆÌ ×[@nT’\çÍ+Ê3iuûE6€Ñ׫¶Â.Fhœ½‚jýØstrA$ò4ö~ÑeÜâ˜ÁÛxüwÔZLɃ9°9|Ç„þã’Ù{AM@¿áµ#ŒeÓ5v’®&tF¼çDTÜÅõÁˆß^ß…Ø×–hü>R«Á« 5*(s¼C^±å¸Ÿük²ó2èM: –[»+‚òµ`^¾<³O™t-Å¡KÚå=—Trò™ü‚«àqFƒìº\"…5À 9>kPèÈÓ•£ì–q,Gšw1ó ä©™ë>¬‘;bɧ`zô&|°´]:4¥¿e?¬°¥ÔýhšhSÕo–8ué:fD4·Œî'BÁÛ'ÞÜûÕϬcÑKVŠº𣫓.1“vŸ¥ùðuº2ØéK¶)‘ö*½éä a²ŽG8ÃuõðïøeR=OD•ÌÁ™¶ÙP²}%‘,0"º&mÐôd#YfpŸºÇ ¯Õ—ÙWÚC°fð,ä$ïÏ3ßwdÑæ#…ÿÍd•‘0#<¦úm%‚WøaÑ@!ã5ç2Z<ánÒYN%ù¯‘WÈ9Í6ðSŸå]ø\\‚Þ’›L´ó¤ñÏ¢\’7%†>dÕ¾¡õ+€$Ó±ÊCxcC/ûjæ'uýO÷DØT’qÖlBÿ¨ËŽq!e —b­=ò½Vœ×&/W(ÓKrgpå‘J0 Í$G‚hÌçNÙ%òY?àb>K©a<‚1…Ôâj*¾vÌ%Û®|câÿÍ!o“?ÁX ñ^äY”%ý¼Šx/^Eîìîçºû‰coËf”Yœ_‹Þ=%¤&/ Þ©O„ró8Îo‹H̆Òù'—¼_ö¯Ì⃯rC0/H›„JgàÚ­ºTq nº­e²dŽÐ½Á_˜ÿzœ{' çG0¹œÐ)×§23²;áÎ[Â4ѵ‡¬,ÓÆÏ}øÌ÷΄þÃw>÷¦à_–&÷Á‰ú+xày÷Š\-D‰ r²•…è-Û.öeÇ5ÖH#?k©“kÝY0"] OñSGKöôó!æƒs#ä5©›=sá\|:½ë/_=ÁÅkc”“)|T §šƒØD•S8¯Ê“ÎÏ_~kà”œúþŠtõ³ŸÖÍ—5âTä~ [ýë8|sëdcx¶<)öù wĉúå<¿?’Üšï‰Á™d“¶a6­"ÇôèéÃî$Ï,+Œ©Ü,;ŽÀ†9ä¶ð¼¼¦e<ˆaH<Ý}²ß(ðMèÿ‰P>?Ê]é*äõRStŒëdæ»ÈA»üt²{þ ž®ìÀ=uìÊ&CÊðmdlü•Èùè½4àæ4âü²Ž#Òøcr¿<Ų̂i"!|cÙÊ7¨ª¾êôåIjÈFòåY6îŽ:ÇFŽóìÞ…÷°Z8˜N“›ANeÜÆ"5kèõéâPžb„Ÿ4ÉÂJ+ÚuÑÛø‰|•"ÕlÀ™û4 %åG&}<†îƒy«×ÓÜí‚\ÁnmÒ¼ýãÛv<²`ðe24,j1%m²kCºéš³(;BŒÈ?£!eì‹‘ÍùŸÿãØÍ§‹Î“NQ­Vz+ã&· 1œûm&>xL¢Æ]$dÖ&*|¦”ü­ ]ò/Ü?°‹Î¹>•lÖÔ »3¯²×]<=5Ò¨ž>vêT£ìÇYøò˜N“ôwãV¿¹˜uv€¬•"w#jy*âd®±Õçè'‡<2¬ò™œ¸Ð=53I™Í6º"î2ÐO7HÔFe¸¯OªýŠßdÛß­v£’~ì®&žÛqG÷>ê:¬Oƒ¾ì$­¼Š¸$$Žv'~õCÍ´ùé4ÂZ¾bªšƒ¡Mîu*¨¦ûtˆ}’ =5ŸYxŸ¼ÙK.åO&k&3ï÷]ftîÃÑ{~4gZYZc‹WÃRÙÝõå%Hî‹&fzWÉŽ#ÑôVö?Ø}Z•ü Z‹|Uþ4O>“DÉKPÿn~jGµÈ…B|Á}á¹Ì­5—Á¼\´!¶êûÓ©{;RØ­Ž0åR&ÞM†rMuòûo9ÆVíÉõ×ðज़àÔžHtUé÷f¤éˆ-:Ošû¯¢´õTš4ƒ‡¬ò_ÆÄŽ|ã6PçiÍä[ïJêÆw‘:̺IÜ÷XÑ5Ëú¡C±6†š×g @üìI"Q²}¿åÞm¸äB0¾kÎ償^H”E”ˆ–Æ$˜^õ‚¹˜¯M⦑>d™7yòµW'ì¥[RÿAuòK\YçM>ȇ=ƒƒxq`Ùþ¬/ÞM‡i‡qMvV[±¹'„ÛùÔii—>4Öh’¹î…ø¨‚MÏgàþ¿×!ði6~>ÜÊ~Ÿþø–9’JXÛþ¯ÿ»þU%`\2ÞüÚ% ·¸¢–Ԩל5U…]ËÖ`ìwê=Òˆ/U;ØÔÊ9´Xé”™è€Í¥Ï(Ñ0Ì4´zÐßóKPUÿ隇[Ò'³.¥[@ðB ˆ>ä,­ÉÄ$#Oš!wžÃ‰QÂ¥£ËØÚ†XHÈÆid yõ+à˜ »æõtÅÐ7¸ëX†~×÷°;VDbsùUØ‘–‚'%«¡ÅL‰¼9uÆïG®Š5'LÁ ×0hÅ+?1ž©x@z5f½h`¾×U¯ŽßÌß9òr¯å®"®E“i½F tfpî:šMðßÀM¹s%¦½Ü7«±qÆW¦ëòüòj2ŒN•#ëî$Á%µ³Xý¤õöÜ¥ýí3ˆuS?ÆËaÞRkt¹v~ÂS´™:΂¯ Iÿ~xšÞƸ}ì`"úî3;n&ƒCû9ÆÁa%󆻻ùð¸Â'f»8Ã$,¢gBöÃW»¨þ:›î÷ƒó?íYͧr4ïêþzT ¯å-!ßC¿2îvq–ÉŸïÉÂ@3¼­˜EÇùàŽþrŒzúeÿ_FkY G°X™¶íö&Y†pèð>\!©o4hÔ¤}t–€,év=Dý·l$ ÖŸ"çÕýè>ëbnÈÞeäIp>=WJNVºrU¾¾‡[ùèîYÐxÇ%bò¡ûô÷2úœµôñõz½Ý”˜½ßÿÚ,ñ³283˘Æˆ¤y|‡È‘ÒÌ›ûÂ/“E\áÛÖ“”çÓs˜ô5 ë_»Ò¡Ó¬xT,q÷Ü ¹-}ÀŸÎÈÌøÎ¹ûkp ÷Å·R¬*EšªDHìzYz­[žÜ\`A=ÔgÓ/bi¢‹Ùw? » ö¿kÑ16ºÑ^ØÏ}ýpº¼ad£PÏØ’v7ñÀ/{{¢•VÍ ®ÜÏĪëÛ¶:¬çåXb±ñ~ϼ¥Ðb !7‡âÉÒõqxÀÈ×pqàJ’“¥·YëC¿àj;’ mÒmù ô#¿£›ûvvõÓð|›qt—…”†•XÓžÏÅûçÖS•(qÖ\F‚–ùÍÁ(kí»„úëšP[§ovùÍäØò Z4þc?Ä*¿» M2þ7žy&Fù-Õè º°µ†óx‹c¯`ÛbqÚSÁG|óTVÔ¸pž¶¾ôGñ™0PlLFï¿æðäMUXÅG—¡Þ|YräN ˧ìÍþ··e³O‰U(9Û‡-:°ÒÀ”õabáß>{XWÁµ‘$Nã>S|+øyÛuëidáH½Â?ÃÜ(ÁD9:+Û<î™L‡ÎñBf‚21«žÐ¿ç\)"§{ZÖß„#i¾(6Ï‹®Øxù(cI“^9’;ÏùhÙæ/ì^…iäŽ÷}x"³&xE¡xfd›©•åílÂ&a`5#çDFÒ`½0}i3Ü8…çº µÏ&'ïªÂFá\xYËsû-6“®ÕÓH­¡]ÔŽ%?ÁŽu0p«‰éÏ— wƒÖƒÅ}pBö %—½Aô| {Z8˜d^Àë«py×(tTØÀ¦».ô×÷ýl˜™3ðüeMžžË0Qúþ¬J¾¹NwjLÞ¢J/g3T}YçîŒÿßùÇÙWØä΋lhw=»óÅYlŽM„Ö’%Ôì‚á½ðö.¤Eõ^ô­ U0¥‚isÑäö|LUI$fž Ц³ÖÞŽcâ.Ì!—ÌŸàÇñÓÒ—Œ™³=Õý*CTx:@9±…øìÉ¢U‰‹Œ"å'×÷ˆQí]3ˆÝi!jd¾œ2ûEéЖ2wË‚i»óÐa¡$GÖ£ ”ûuȉÁ5“£˜p×(X·m;ìÞ<«v&˜š{Ïâ^ùrŠ.s+Ʊͷؚ1S\ò}:‰Î4¥"{Ö3Ã呂¥wªÖ•±wئnÈÞ™Í.íèпòÍ|ï›M³…È›ÉwQV?—«É“|å¼ap‹ûôfgÓée;ߊÖÐ’±ãù!†f採Eôa4<ÜÈœþδ+“¿ºÝ?”ŒõD€H©:ÙA¹Ü5Ì;gC#ZB1Uù10ßy¡tmˆ=`eξ… ­2”‰bê8aìÎu|tt3ÚÅL¦µ×?Âþ=Ì9P#&ª†Ð9Ÿ¬kŸLþ-Á'yÛ! Þ€}P jç0»ô$&,8É4¼Ø …Ñ?±àëT;ÄÜ|<gïÃàWHêf¢yê$Љ8ÃhIûM|þÅÃw,m`…ß±)¥…8r,,Ÿ>Â÷ày+m|.Ð G*ü@×ã ­˜ý fGé㪱‡(˜f-Â{™sŸ7Á×ãÈÏ¿‰Pxò#›¤lEÂn°;®ÔÀlycyòb6äo¾ GÇX6Ý„“wì4¤¨žgeÞÄ_v6¤dÊý¬ üö^“~õá§ÿ e1ìÑ_X{ˈuüÁûäÃ!œõ8^ÃØ¤ŸìæäjU×-.µ„a ÐÐT„ØÅ†aá;ˆâ¢p§z2…ñlªÁõU1ÿwï9[vÂþÓ=âÔÌ\WðY€4®[¥DRÎÝ«y9zsž–ÿMÓk ÕwÛfWà`Æ<­â€zž¾x¨‡Ä¨<+FDDŒpçÒ}dàw>óf½1쿜uû£ ùj"ÄD\…<§D4ŒòG¯˜\ܹñ'*ÛÍÚvK¢Þ‚@ö½öH§|Àáä„1(^»Ý2’ôɼó%=OÇ@ym-î¹= Ó~¼†¦ÊI$ÞQ…„ ÏÂMúCèígïD²{OEЦ¡©äÖœËÔÂÊåFW ¦«§ jÒdÉÓªnNÍ÷ üg%eàe‰;œj3 oþÄSÎkò~÷x8– I¦G1>o/Št.B훸ÌÞµOa¿áü´6¶7#˜žT¾‹üˤá‰ÚyâÿTŠèm:Š÷ÞœS¾Ô>o1m*Q£¿sŠáÙÍ'лü6 -”åhMöúŸ&Tg1M¿¥G}HÀ!²ñù:hÓP‹o“1lÎKXøN f¦?Ä#«€6õ9à/õ%èü «SH=£[³.â³öpcÇ<ìl˜ECü—2¯WÛ~3n£PRQ}’ŸuÚO÷ë|HÝjõÑ©$uÛÿ>ÿº*áÌ5ÚÙO¾á¬‡ahu vµÇãäõÏQwÕf(EÇ ,Nî–%L‘ñ œüL¾T#[“;ÙùMûêäò_˜úo M¹-‰¿‹:pRv–|ÝÝAåp^!¥ŸbÏ…÷õj‘ªpEö{C0Ñ ÎÛ?ãébøE§ OZVÁ MzõÈ“ÆþÊ1f‘?^!KæßòúpLØ„ÚÔ:——Yv›5ZzÝñ ̺•ˆUùÌà—J&en&3=€Ÿ¤7lÀ;ÿšp¹­&m [D¯äO!®7­¡Ý²ÏÙD©™¤]z=:ǘP‹J<Ñ­ÍÔFwââýèó_~%µuxÑNºTë3邾oÅCØËïËT=aì/~pƒi‰UPXk„7G9çZ.à3‡\*°]“.hédÑ:¸Ø^Åð{>¢ØÜ4"æ+HòWg3¼(ö˜kÙ¢GÄö²ñŸ'“-+A}Ø® >ºø4ã¨äΚ aR-ø¾:DUœ#´*ðÖu y¥¦Fȼb¾ô^`®q…Ö.Ãù6à .…ìHIhÁù«—pŽ4ݽ*-ÇÒ|P±t'™¶í&s-ýnöBÜ÷™C.ñ(sÿþ5ý[aÊ}3æ]æ"ôÙÔ½éŸ ùŸx¥G³ž;WŸs<”»/¦¼¦à°_Ò%#sðú´ Q™ŽêX2Ô…¾?꒤ʫ‹I|¨ˆS¹Ýà/\ Uõldbæ§_ƒmS¤aÊ7Ir𨳠Çyüút–a4¬þ gæ¦Â²Ìã8¬¿¢›WŽ¯ç ¿ÃqzN#6çÁ²¿Ø¯ž\˜ºWþD>`$÷&Bã>Sð1¯fXâ<Õ80-¡a³vÜJä@æ`ãÒø—‰%˜šDTÜ’\=A¸­ò—N÷Ó·FöŸð¹žÍ}ÏÃZv‘q’K†×Åɇ«Õxo;÷dã±$&“BíMعWžr \p­=¯Ù¢û.[ú|eʨ(R‹ë×P*î _ð"¦ëaÁÇv×Ya/Ñ"#ò<äþÓhƒGÚh»ù6Ózg ázË]Vù~ ôü †Å½éÀ^Ó¤¼ßWâ_»Ý›Ô€oÝ$üxhô[’ñ}‚Ug!¾î]ÌÚ¥iÂ#óSL‡Ø`]ñßèÔÊœdŒ¤•ÙGªTwè32ÌKé k0ý¶ tÝ×á²1P|ÿ3{ïy8ȉ\Ÿ°ÿ¿ûœ=uW ;[†‚|“éëY?¨í uz®RœÞ_ YÛvPE‹Vxçt”šÄ¨Vÿ2ênBؼPì³&­Á|thk:ð?=‹ß–ïÁš•·0’§ ïª< ‚Ç„ÈÃòKÀÊ9DÕ?a£„“uåq¸g“i=éKåå´hÀðd:ô†½lÝ·†põý_°ÕF®ŠNÁëW•éöÊmPèÒ9ÁŸy.{[™Òöš&IK… ɤ=¬ÛôDËží²…¡?‡í†Drý¯Àg—KðÏö €Í‹ÉQqêqÛ’ýBáþ"MꦭÊ(lIïvnCúÖÁC–»hh {&©søÄéð½—諨Sg$Àu‰NéÍïÙàx7P;ž—k9*±/ä;#2 «Èmg×l¸©4¥“•[°àÎ\n’G 8ú:¬RÙ°‹ÑYÓ¼n\fûK'q®Wÿ3Œ u²Ä7#Ž}ôÊŠøÈ;sƒúMáùwp4`ˆÒæ*6~«Óÿæ_œ_ mÔ…úJÝe3©{gúïaþþÙásÁ7‚2½¬)¢„i¼Úƒ­;£1ÓðryÍ!T}¹Ø†ó¨Öƒ&v¢pìác„ñøŸ©¿y]‹V¶ÒÄ…úÍC›þ¡ú¦ܧoÖ±³îÕ¢–F4Ž– “šfô½Ý¸óä8ûUW×Aj¯-{³ß‡Ë9ÉKçIÀÜU·Í;+0o5ÝÖΤ8€ø:zâ<‡Üß{þêË"ï>GÕù ³‚òY¹ijŠé…•Ðk%Êî´t uO²aK§1}g ÏÖ5tü¯ÿ¯; ú6±Ê%͘ãA ¿K‘5gÏcû§Ün ·Ö©±ð1מÿüÖåyÐ4Ì~[­Nî]öÄPÝ´X¦–)Î#¡Ë+àè)M¸³’\¿X¼)é„ ;èÝàü3®4ÿÁÄgû…ØP5ÒνòèZ¥ó í»@HVU"þvÓÈP¡%4´)‘b™™ìüjb§»q&6ñ#û{y4¹rW…¤ÕÌ ¾Ç–ÑÉM×àÛž9tÔÊŽvö½ž¿¶Tࢉï1°¥¦ôò¿$¦JÇŽ;FÎE§jO¿°kJNp®fŸ¯W¢4uf3ÌKùUÊúÙÒ73®‚Ý| jX{ò7rZ#?ø"ѪƒÌžëGè+!+â˜LÄZ&AE*•·šÄt…5°í[µè[LB|$Ì—Ò!nÕçÙC̺°,½²ôyÐU1u}Сܶ”/" ƒ70çñ3vçÝsP°Ü…ô ij·Íh[ŽU"ëðÿ𞈄•Ž_,ŠÙ$—HÊ•èAóøt–«I.ˆößvK4"cfýL«rf¸Å † ¼˜ŠO=ˆý¼=ØØsfÊ.#ÖOQxÝ8c{//…á %;n»¢‰DF0Ô¥é«ÅSÁ÷î¸1%¸ðÄmL|=Äžòs¢æ²± ÁOK¿'·î¢Äør1þ9¼·NJb´•ÔÉG;*·u!ïçBß'G|µÙ˜ÆÏ-„ RÙh#¤Hj.ô³éþØW£On×í&ÂÂIt9‰d#ŸˆãÃüôh+?M=i ›¼¸œÅ)]ù¯y•(j?dÌçÌdÃDbL6ó5±"îùz)rojý•!„š…»¸“×€sQ¤§L•:/ÀT½ÌÛ§ÚÉÀ•$rÊ1t4Œ ¢qëfWšÝ—ŽËØy”¯d~ÜŠ"çäéªixä{>Þ‹„%6¿Òs$-€¨n²'—r°&ó't6Åü§h°&¤«#ØöäL²äf5<üûÛ7‡áþ¢¤[ÙI4 cÇÿK•¦ÐÎsº.|jRZ 8XoBäË¥Ióa>Zîñ”>=Æå<–ìÂS%Ø¥9ñùwwïALç‹b•¨ã"©&Ær3CÞlYLUô:ȾMÉä›eé:¸¼ôn¢½û²È¬>Mò4ò(MÿCÐyÍY´»§LVðž ֵѨlp‰JŸ®†7¾LŒ¹ /KdR+^­ºô±Oã¯}SÙs§˜e!‘d­Ï$ºiÝJÌ.oÑòó“Çh’ÏUÎÔåÊ#LZæI¶^E>׉‘¹ëû8†×$ˆ"7ç4‘í5*d_]s&ù-|êÙIüú8dÃÅtÇÆJjý´˜pJ7bÕ ^ÇŠ;s°ãµ9Q»ôÕ>àÓB¤èÞ(m¶IaÓþÂö‘㿦°’mEXõ ®4¨LƒïðÓÂk†Ô§:šjm±Â˜1EÚõÕ^ɤ¯ö¹’צ“¯±i…̽27üqö&ünïa®–ž&g޹¢âújÐxüû‚ÕùÜ89V#_þîÒánúèˆGäûqçc\\ñ»&!³ÌÒ~ÊÒ9›`¼ÎÿÍÆ2ý‹þ1ŠÃ«ÀæÃArë[/^œU϶¬þÂl;—Œ–‹C÷¿OÐrü%¦÷5³KÖ.£b⶘~4ÞHHÓgÞ‹ ñêdz¢Ð˜¼Î—sˆ}+OýïüÃ{ cïÛD|np¦&¯¡»O,F¡ç¡eÆ6Hõ/FÁíÖx_r1wÇüÜÑ×…Øô,y°áC“eÌ«5¥`¸p+iÄ{w¶‘[^áôzæ §ˆõœ\…Ï‚y&ú_6aÜ~ó°v—zÑsG.œ?ÂC·ŠÆCÄ×­8^„’÷[!—?3ª£ÒdýÐ<*>ÒÍî["„ðd=9òû#Dv^Þm… cñ÷2„Ìï°ƒ“ÒÏ™“hdŸ Í‹ù‰ÖÉ‹ôZés*>›ÝøE‚ØÈO£Y.toégôZ[‡Ã¿âìMV󣇨< òݶû`n,<×ÙJ?rèŠyûØ÷>šdôÊ4zì?†.P"‰ÓWÂÌÈóìÉPÖÌ;Ç.ý_ÿW©š*”ƒ]ÀIöJÝŠ©tå²·»u(X2-ÊÚôV0Þ_"H Õ÷0Dõ7{œ'F`f4GOKúe ‚ ûÎ^9‡ÅÌ<”:yáÞPéÎÃÒ3gØR¹ç z|4oÞO–~P¦±ë¶qUØ7¬×þ4Üøj¼.R§±¿X:úsŒ;}Ë#Pî[D›ôuæ3ð5t5Øu+áA…Eè<É ×)мÈõLÕS\ßv¾?q¡ëN sþi3¿·G0·žþeÃjCàaK!ݾuœ§˜7²ÕB§³ P™°ÿóQP^ ÉA§Û;eŸ)“êITxµ™sòêpOà¢ËÊ0‰.%¢*2 4}ÌÁ÷í¸¹[ƒ´ïšNj¿¾Æ«vç¡÷€üYáJdº¡ògkï%†î׃qÁjq8í<ÚûÚpk‚/©z¶ŒÎ¬Œ`+me0ÞÕÒþúÜ~g¼›>ÎÛ³›Žâ¾!º¸ñø¬Ìü¨Xxl*͈†µAIÖiºÁM~ï–€ vá‹(oeM^„Æb†¢-)A¹‚‚Ty¥ i°ç<·ó†{'_À8.P÷n`þHüƒ©Ûó&ôðmkãl¡ËÜQ?ÀÝO]éµ4Æ´J'mæ2: Ù¦’.&Vp;]þÆŠ¯½>^}Gïv?°¨Ä΀0Öýƒ†ìåG¿<ìîyïÀh£Ù¸AÙK:PãÍÛ·Ø£oñsikÙ.Ngû/…YÎQ°QÐß­ƒ<”§¥_1œÀÃÑ›ŽÙ.>PñqëkÐmñ´ïcîä¬iÙ5æÀ¨ú+Ê¢Û®Ïì¿%‚ð#Ízÿ-’´Â»_ÞÀÖB$Í`ýz´ˆÞh'A“„çlßLå’©kÀvV°KÀÁc4§¸=â4Ôï!‰Ûp"R'S'Ìc®kÝÆ˜UöÙž ò¦å,þëŸLã¢øÎ,rüÊ.RÑÌb£L V¶Í§ï4Uà¶–*šo‰¡š¿UéVÓ^²zì&]åf¼ÁTnÕ ¬52!/~˜‹¥½ïå@H~.ŒZÊÆ6°g éâµÎtëšW¬åÖ&r7ãýôȺ÷éë9„.¨GåCŠ ¨\tµW7¥·h \uW¥3~‡RÅu3@uå{èßãFþ“7/”‚ܪ’ó‚ªVO!u®%$}ì1ý ý’)¸L%.퀗«!R7âˆÅþ§(Kk 2à#Lƾ’4åÜ)t.IƲ¿¼äCýcöÝô(n½ÚðZ¥B7^¸ò.–ù^’ÍÙ’"Ú¾T. ld²V)AõµˆMÿ@ýð/Žªú$"~=Ÿ=› ZðÓß•(Ç·ÿD^q`ö„ÁËê£lèÖSè×y †^raßLš¶>έU£–Ù÷9ÓÇ Ðb`” ¾C™¨‡ |æ7þïqruª#Yh`Bx õéËu‹hé¥ã0à=ýx.óжÉGRóɺÔfŠ3ÄñÃXy4a‹—–¯rÄohå‚xÖr&ðw¬ÛÁHük…÷ ãƒ`#2d(Mògë`òŒ/Ð]aÎýFÆs4ßLíd™²ó^4ÞÚ”Nfö s’‘]õGƒàïýd,ôyB7&“óGtè°I<¤8žÃY2wÿ¹ŸÁuÛqâ' eÂôy¢:9VÎNÿhBrÝé³"bЄ|Ϙé÷¢QÝs0iØÅ0‚*ðÂÆ/Ô·0a)6¤n— çùÌ>]œ -ƒ‡a‰2¡Ÿ£)~žtÒYµ•ìÐËÁÚ¼¨ùÎŠŠ¸)¡&÷„ȪMkñVØcì0½ŒåªÏ'âÿEm'tª©Öv&W«€ä܈åœô»Mb>̧¡m˜ ¥ü¤õŸ)fî„ fïMú’JçϦD4“¦”œEÁŽL8!KÖi]fEÝg£ÎH8Ù®2†’ÎbXÊš¼Œ±°M"—?ÜÅ7k£©¸“4ýÄy WóÕÈ¿)ºtäÉ2šºc?®TT#Éë_ÄõÛebòMŠ>ú°…†Ž¥Ò®îl ¯ÁŠ%}ìš“æ´íMɲûÅpjLéê\g#ËM¦þZÓ³Ïà±u,†Ýe“QBä0å¨ÄRA ÈÕ]‚ëô¢&êW Aâ»ò<¬ÚpŒññàšÜéÄ_¼"ejè™ßŸÀi`#eúšê6Ä’[o±åÝ3¬üU€bu.°öÏYX^cOÏÿ;ŠØ[¡-ìYw¢7ÄCW9‚®Óú÷é5<6#€˜ÿégÅ£¦áû.“7‡‡^h¬ ÈfdU±¤Ú—aÿÓ.´­Ä/£†d¥Çq*y܇4˜00{éFPóÏ ë—˜RσIÔé¸>Žx ÏœÍIk#‹/ú h›01¿y¦< $æI}•áOÒ-ÑC)¿2‡ÈH’lØ)ŽûϦNÄÿðî vao;Û/vŽh¿F‰ß:ÔÓe>|¹´ $[Ýax@SŽ-£Ã7t1ñÛº¶Sd\×á 3O‚VÅP#­6&uœÿö‹IQtð¡–ïÎáÛF:fP÷]‹à;yŒé°Ék2Æ ŽŽôÉ0&e6ã)sPsQ!|¦§`ÆEV ŒCæë[á>Øt&†(,˜A£ùǘ:uú¾å"äɺâó©È§§H>ñSy5¢±V“«]ÿR?©ÓÞgûá(^ý*ÉüÒÉï¼Ã®88×å´pGá‚ÞS(¬ÅÚÆ+NðßÚ™³p½[þz.Å×>w|tTW\ž8…¦…ûÓ/µ¨<û<ôZD6†ÐÖÕ{9_f^¦Öv ò‘bøØÁ°õÌ£„rÔØ“‚&¿i›ï<¬T\DM'mÇ&#{R—€_úÂÅ+»àÕÞ^¦êÂq¶v$ŒÒýFð"“O¦j&+Hù°&¬"Ñ‚ú¤^Cþ<.b]LBÁpøóDÓò›¿ƒ›W¦Á¿"Ê?× R5GáõãmØ<œw»ßá›y¯1ö¥6œðæÁÇL¨tËŒ‹ßMšÚ´fçJú>\¹õÌŠpÛ3¡ÿo‡¦³ö¾§»Ä ÀÅV|­÷—›¡ó´*UI±ØKh?jZ²TÃø=®˜ ñ,Ù £˜ ·FÔ?àùN)º†‰F™Œ–˜ÝðM˜¾—º‹#'Ѫ`1pù9áG.¼:Ëz {Ó‡WÏâª-{a™‡N«Â/ ¦tßÙC̉åa¸Gâ¤¹š·ºŸÁ늿|‰¼vcØ7‰¸H/$~­éxñ2C¯ÜÒaNhB·öìü.c¶¾Ä¶ïB4?4 {¥ ¦âçHÀOHs©èºíÜÄY±°+Ú‡(ûU°×ÚkÊNøÿk%UcjžO?ÖšV¿Š)·mÇéˆè»[ïþ†(…ük XlNOwM¦2©L£>©xÌ~©Ma§çC¸‚*›xŸþ¾¹šç}LIZ. ¯!&XÚRåoÜ’ ¸}f^}Îv¯p€‡òd$–¾Â¯jV¤¤¹k2~@ÿqeRq_‚ /›Aêsì‰ä†(Z¾A…>ê•"£µuLË.ˆœ:—^5—§bUZ(± oÇú#2paN H­¨ƒÐ³ðòÚRæ7ú‚j™‡7DéǪïÀœmÇz)ù‰üwtÄ Â²ÛatÓ!Èܧ 2éëqEÇýÌœ¾‹Ót¼³å=Øu‹„n©Ç}ÎÉD øJ*Å‘ÃonQ©L#ZºÊ‘´ŽáÔžÆMré÷q~¤²’®­¾NTúUèÀv Z¼¥oVàœåqÔÄùÿíE•?O%Öû71£×NàÁ­“ÉÓ«CÃx±×¨§‘? áòŸ'À?ÒÃ>¹iÂ|i½E-]×’Û",õ’8M÷4ûq-üz |¢ÇàêZXmŠ ‰v~øÓ_6Pg¡¨MÅg "¹]½\ :σÌÿÎÿ;/šŒ³Ä¿±ŽuS‰s¨7¬¹œ‹~ab ó„µ) :ÕYHÇ_Ÿ² Öõ‡Ñ-Áf½7謻ƒF¥93w°4G›–7‹I¢MtË& Ü-*MÈIòà{8×¥O[ì"8®Åw°çÉ~z@8˜z+/ÆÃI©hyœ^øäK"¦dA×­ØÏ³Ž¿o%U~硳ږbu-}*6MTÊv;ý¹T‰¼pã%³k/’ôçÓè¬óÏñf˜µ{~dª/¢âŠx²+€¨;/%yÞ2e!Ú¤'"’¬—ø >šÑ”Îà”| ÓZÅ&â_›e[ð˜ËfåÃñUÃìà”*X¨ÉÏ5ß/ÎTé¿ÙT$sÙtì!x­  ¬'ÓˆKdR¾~1¢v¥°×÷¥7Û ñÑü+LýÊN¼8sQ¸ Ngã©mväÅ-&ŸÌŸ±çg'cz‘3ð© ‹«Aj¤~n>»°£ +Ʊ¡ŸGN>ÜÜÆer~ w§sÞÝ¹ŠÆðÐ{ëBé˜FÜ_E‚›å çiÃfî+ bu“вÒ<¶Ëž¬ÌŽBý[h&o‰ìÞÿú ™7ºTÅ™xüu üÁ=p¾Šwÿ¡½¶dom»È þ¨Ó¡Þ‹´è”í/9×§Áê{R¨¼òžâ4¬[A©T‘Zíz¶JÁêüixñÊ—®ýÄ{š#íß SÜÛyçï˜õ4ž×½4ãj›Ÿ'Ÿ¢wÁ Ñv¾î,zy—×J“¿ÝÌ‘ILa£¸ô…OOTØ?WNb s9(UDaxÚÜÍBë€Í»I°•M_p„V¹/£žïÁÔC:”’Fa’'©;:‹´šIÃÎŽVŒ¼vš5¿ð3WÿS"ÝZ ¡kCè´MzÔ{Ž w0h=?iÿRÙEp™ÿ)»ë— M<&»ÂAk]‰{PþB0tYÞ†â}Sˆþœë0xjŒx=Èy›üƒéíþÕÑô…g ð²Tïì#àî‹Ïµió…KȦhK­q¬ý”N˜yº„¾õCx­<]íN^€ùè™…¹?Ú°ú­.†Q}²Sß™^“ ’‘¶ôÙ‘Ê<¿CÑù£ Y3¥ºÜµ¡sy5ë34u)Z¹Êé%©q}~˜œ+ÆóÄÔÞRð`ï¡›%ÄÖ Š•œ‹Ç`§ý”QžíJUÚôè¹ï-ñ_üÈǨ†¾Ì^¼î?A)¬_}‚ÊoŠ`¿Îoøt­öGÏÁy^d÷曜9žÉ„MÆÞÃ+qî$^vçqOŒ9¼—ãñV€¸®¼ˆÂ8sD×Yq{|¯³c;¯ØCfñJib^{Ö «ÒиRR±o3‡Syf|ëâ~x]ËèÏ –ôòn‚¥Ýj4Ü-‹ùw‚n<Å,ùIŽ®­dk·¹£ü›•¸cµ"T¬Åðy= :E’˜ézP‡w·87Š1nk¤I†G5.>ø]?çDéM¡_¸¾;ŠaÞ>"õŽ¡ÊWNðÿ-§Á\M­L¦áèž0fVÏÖ(†ý=©(¿ïÎ'Þì:½tùÃýå]€*Ó p¡Þjè^ž†¶f´WÀoñ2¯ª$°Rt uœ´Ǿש?’Øo,É/¨Ú¸üäz8aa}ìµZºòÕª0J»•»­˜’/r4™XòBk sýÕt¶Åç*¶¡ƒÒfdÇíR8è’ƒoßAçڗпÿktû §·ª3tØYÇšá·Òâv1ÛÊΡþÓPTX-Frª½Æk²ÏœdN*¬ K¦WW ÑÓt=°öøÜæØDüoé„¶;NxØŒ‡ÚÏT"Š/§ÁÁoþ.´ µ9ùØ]æÉ„v'ÝþKO"y¶ü·ÞÚñŠú'Ç E~æ²ÝíElòÂiÌ»ÓXøqïó`ž]"(xVžÌŸ,Ì.¯™*p®  —š‘ 4†]afsçýÄ7Z»@i‰ÝI40MKæ=™¥ƒâÔK>»A„±I»ÄÚÛ~@û#s°±?-31·6êyOÓ¶<°âQ§Q3ޱŠyt¡O5V©è‡ïp ! _*N×àdòxØf¼æpÁÚÅú4Biê„þ ²Ün°†GR™S^é ë‹´É÷ýסT›—˜}MÁ|G~‚KuáQu´Š!çâærhФ}Š·˜ðÙ÷Xáwp³«.IêËs¸}M”¶þæBú"wV@ßCT12uÒîp»/Ÿ0Ó˜l]U¶^áWâ$°½Ÿyf¹ˆ.ÃR\óòiH¾ìÛñ–™4ð3ÔS¢›ªaï+κ…¯Ùúa}0^äùpâ¯u3–Á¸IKIäëOŒ˜º5‘Ô‡C‘z²€$RAò FWêC«•á’Ñ\\;Iþ9!5ÁOº';ˆ¾ az½ÂÉ'eðÓ -lªKÄ>õ{Ü ™¶t!\Ýÿ”ý☉¯Oât™x|ÏÉEiIÔ 1o™‡©GÐf@…ê¨/¥Œækô×óÿ¯'™.³ÿŽÎ /]¦uŒ: l¢U9©xß¡9)‡Ðm¥2MÌ™ LÅTXÿ,ø7³æá í‡6¼Úª“¯¡Ð-qZï'O~"êû []sØRy‡-ñM€ õ7ÁìÑXí=ˆü¿V°s“-P´¬käê8YEàØ«GyéS·+`½9÷\¦·L€ÿHÓþB‰L,LÌ…õÊÆ$—#HÊ·©ÁƒËêäšãmPïO‡HƒN\±%Ï\?Žk'5,ûvÆî‘µµ¯`éù'ܲc0l!Må!ûÖ”:n¥!õ%äÕ:yâ/ŒeÇ pþÙÛ¬J$†YËÓ%ÆõèËMBÉqò¢¹Žg=ÀéÁ×™‘êÛ°¶¤‹Þ[Gmÿ@ßYúÌÝî†0ÒJo 9²” B™Ÿ:"Œàb!È7äÐPî š“f ½À°ÊMdþ”7`07 V&¢èß}Ôt}$ræjûÃ帧–˨j·0³[uQD¶ÿªñÚì0ã˜ìDÒ”Åéì ›µ$uf¦’)Í µýÓ¡(¤y¾mñç¹Êe·ÔáùeÄf¡±_½ýV¾ åw3zá§g>+·eö<;™þÉpûÖsì@ÝI™Ø§0Îó&“É?”éj9~ÜÜ1ÊÊ=·õÿf€¿N[ñzÈ6‰¯²±-Ûž-:%ÀÎff¸²g /gêýr‚ÔmÀk᫬ Å1|+ök7PHï_Ч‹VøáÅ."·Pýá(^›Üex‚[ØoC”4Ñ…z3/jgàò³vù/#Ù’{MؼS̹—+‰×á HœFJ]jØo½il‘-®ù}xÅ–)øýŽ+ÍNŸ\•5øõÚPeFw—´ÁÙEV#ÑÎöNGs2ÿj#:¬$ý-:À•Þ—¥uÉÐ:C(Ö媚LvD°Ÿ__¡ZæÞÊ8æÊî·¨¶ÿþí×£¼ bôýÂ$tµ€Ë|×0~®*½p#Mñ²ç¬8›mÀñÌ7¬(=ŠX®OË%à‚ò4Ôä‰j‘ªhüÇbV.‡Ã=`³ñ2sR¡ø@&{Õï n‹7ežÏ盈1“2Ìo?fö&+bÐ#LÖLÿà ²³YãSÚ¸1P—>‘à çb1íJ!~íÜÇÖ÷hö!Êš¢Z÷ hÉ)~é6x&é¡í¬ó‹<{«‘ûóöbXÓÙÍ^”>„ü¦w:*C~ÝßË.yŠJ¿ úçYœ¤©ã1/haÅhìöñ=øm†§MئUͰµ‰L‡Ù?P‡§ÞÓžyÜ3O4±¡b•·Ã9pÝ3üÒ ²5-œ‹ÒÿÀþ?{µõüxÞÆœŸãH2PÛÅ‚†¥€4@¨÷é„ý/ßµ ªW»Á´_÷ñÄoœ?Î+åð±g%Ì·Yˆ…³FáÇVeWsî˜ËЬÚV¼;-Ÿ5Î;]ç~~;º4líRÈâ~¬ ýø©tüZôÉßEn9ƒYÙ*Ì)l/÷yÔëÄþøöw~pfàòJ|ªíFªwÌwÛ°½ñ ó}§Ý´è9ÄÉ;²\½£hÔ3…†Èä½~Ä<— ŸÀÿDò=ãÅ[‹ng÷3A¨1m-%UgqcÜ"œ@÷è'\ò›^¸„¡Bδ f.Ó;ø_ØÏ¼4øÙàÕ 90™9×ËS‡/càT:¿þsgz>?$Nv–¢Q£øYCëþϰìÂ1|àAïÊÝcJ¼2¬•;ÖÕã¯buú¡Pf:ë­ųˆü]F‡Í´Þño˜ú–—¬,¢ {â9]™nøy® ŒT¶±Çñh/ï‚I‹ß1ÇŸÀµå5`Û¶‰;fiÀÄ|¯q¸ÕSÁ“dqݤoøù¤NZ쇋ú¬'꟤y{8N“XëÎ*<ý:â‚Áãë>òêÃצ+ñÕŽy ¸ï0 \Çz¬ÒB)5ª³ì@Új²øü?ÔKt#q?Ž3™ß›Ù#ì&MMïd_Aá‚]÷CŠzåi“·ùß™ˆUPºQ•„|r‚Ê_ÓØ‡µÈ[³ýðc&?áwÕ$Ûaà¥yð@^ާ´ñÑâeÞËlE­€ zǸBV>Á0“ÏðVç<‘.¡x’%P OF—1ÛÉK2šâ¤w 5<Š,úIù[ÀgdIí:UuþÈDºñ{èÔù‰øw`ãdÔÙÉ ò?k#ÑÌ?ª¤<›Ÿ6]—=R §"ñ`ìÆð&6Œ/!0—Bνõì±YQdob «”šŒ‘›YEß.\¾}ž½w“ýõ5#…Tñ ÐZ싈C‰€&TŸuBcgãÃVT9*Õøás/ñxJ©’¨\ó ×nÝÈpý摬‘~èíС…MÁìg]Wx³ô”©+Töbæ|ԧ̲ïÌËñšïZ¸:y`µEO‹Ñ'ŽÂ·Ô~í éuþo ë ú±Hˆ¨>°%W×ð'pÄbñÄû¯ˆò+p{K{è› ü[ªÅÚ¿:çÜŽ«é¾ Õ²vÂ#¸Åf/×ó‡9>\²Ÿr~8tAïTMúžfͦ~û:!¾ÝM¼Ú`Gàflz6…fiÅÄÅêŒîv+6KÇŽ–ÏOÆi;uqV”/†´`ÌÜkܬlˆ ègùól.c&tAÇÍ‹ æF›ïsïˆNƒò0eÅq&×)“=š«L.pØ:;m*ŽO½ŒðÕ #ª `4ºÏàõ)'ØlÝh(®­ûqÛšlté£ÚVpH؃ÜqëÄÓWd'ôÿRÄ:lÔÌD@Þ4ð“ª5Óqu©5±ý®MÜ%²›zO…©œ±Å‡!ïò^XÍó£ë¡‚Ç­fÁ‡¡ÄvÕSfÊüéD¨p3Ùñ;~Ü…‚¥ w3ØãƒÑàÍÌ_Å£3û Ïß0sng¿ºÃß´8*"I»“MÑõ¸aLâA:töy5ÜhÅéçN²|ÁèÑ 6ª@¶‡—Âh‡­fží…·)FÄòÊTøóÕÆ„ÓÊÈä*oT_û'…‚›O5QSi7U ‹’4©ÆÇx¡ÿ0þÔ ýßüÛåbX¬…ÎiÉtX3’û°o\º%Šenbû ql"'ð7 È}e¢X{„]az_Ÿ3!^ilhyîêmBc§mà1 Ûö,Óqlγ°O”¼k÷¦7ÖN&iÖ#(œd;.gáëˆFè÷ŒƒŸ³*л*tDG؈†EÄæ^Q0t¤óüĸìÅtAÙFÜew-y«Hü áMï%¸¶B޽¯ ï´.Àž?j`?%„`ìiVÂI/FÀÞ{à¿þl^—(Ë{ {°œÔgaÍ×}”Yà —U‰ÖB]êsèû„þÌœ¡¦XTÌêáãƒÐx…™Ç•Å­Z%¬&7ñÆ ¬Â“« Ÿf?vQ!Ò`àÃÞßGíŸÕ3?è•¥™ù)$vÏE*® ¬ÓË5 ß•2¦yðWZ#3Ú,ìwànν̮ ä ªêsà»ôQÕà$Ér°Xõ¦ýËAÓDjóQ¥—×±íæJäÃWV¹bcYè„­ÛŠaQ“?MY MÝMtR?ç¬äk"}³äÿöý ÕD³fU/¸A¯\ðã1EÊœ9ÅÕÉ¢íyAd^Ç>v¾âfg\DôTMà/‰UƱfIê{\€ì¶£ýÓxHÅ_nörúÕ</J)ýÙd÷ialåâ€?¬Ûm„6÷%¨­v0u  “w¿–à¼h.6Xý7‹2‚Žž’¥ÂüáÛ%º8n6øºžõ¿ÛŠÚ%Ãàj›‘Ëk0wíú3úÝ/@5½’À’”óò.ghý+8 [Õthk…|ž!ÓXù9ï540{ê.@IË} ¨ëGvD.T¤w¾Î¢Ãy¤ò|{Q¾ÎóÎa7… 3¿ROƒ¼ü­¿DFÚ!¶<œ:¼»Æþ5Ñš°ÿR‰×̤âÓì…ä|Z9û+óIº»ìŽÀÌ9Vôƒì=æ «F¿J ÃÕÜpX¿žlÍ9KZÅ–"÷î RÝćÚJÐî´š¼¾Ó v7; "ÍŽ(äkŽç›|­} öEð2þ(F3ßôüË ôí&1;y’œÌèËÓÐÌÒŒ®:îBÆ 97~»Oí…tåþ~VÊdéí~ÆtÌà\˜'Œ©Sâ ½{ \ïígwûH“S/ú°.ª-:߀âÁõXÿó½®I·d'³Ó?¦à\Å ”,E³Ã<¨»éWºaîk˜ßà•%"äË¯Ž ý§7DsÕ³ï_§»ì0.m¾]¿ú/™|Gì;á@/ìÔ§”©ayI“3ŽQÖ_¡m†"\9°ˆúÎÅáUxVï5[øÚŸ­ÐBi÷Ó ¼"¡ôÕ¿\xDÔIûÏ” ²F•/u°nêÝFëöÄ=:6ž¨—壕£iôwêmLT÷£ŸDÇs™érô¾ù“=”*Lßîô¤|O±â^j¤³³÷.S‡ã3Ñü:õ Ìs‡a®ÇNrÏ'O›K"öá ycâߘ~9Y„Ÿj#a ek3;ð÷P(Co—Mð?M‡ø1Áv´9ÐGnk1+d ÙÔr,w›¢g®íf½Æ»cÛNÎ ÆÜ/OûGï sf¨L_…Ï à[ÅE3‡ô DH"+–0xË™M¶ÞP»M€p=áˆßøeÒÒÌ¢ÚaXu~ˆ:s”Ý,'÷×Ç<©gÌ?¯ÆWAö¸Mó5+vò:jð]†ßMl°ûK`–ùá‚ßÙúû“IñZ8>ðyß.cîŸ*Ç¿ƒ» Oj$ØÇ•Ÿ% Ï^Ák‘-LÆnc:ôÞjjN¢ºRþ„þæ,Ç@^æÂæï‚”e­LЧ®f»ýØü J<@Òa@W™ ÍZósZ†È0݉)Ü®àUøåù¦?z-ôl³§ßêVá)eø7ÇÙ\/Š{Žu"¯,Ù•+@=4ÊœÔéío[Vã*Ic"q ™å™'F?|Ó…#ª—PüÈR“=Â,Œ/Àg©§Ø­ZT'"gN> v|u>ޱd„£ à„¦9LYê¯9Gòª¸*.b¬–·Ñ–½ŽóM°OQ**7‚v’òôüã<øû]hzG×J1·=O3ßçCPÇ9ôß‘®Va«òUŽ@#O §]§E£Áp2„äÞÆ·ªÓ é¥45Úò”9é„Ñ…q8 ªñõòr¸R«O î*àIg~j²• ÷î†ì'˜¶Žó¨PÝÉ9µÒžnˆ3ÇÃoD¸†{ϳæ®Ñžû™©¶=ý‹â6Ì£>ì¸ÚÑR ÿE¨“¤y£ÛÀÚbµ$ Üò êyÆàq§u+ÔÅ)éó¢µ²¹ =8‹UÌRéy ÷ÆOÁC“WÑu¼¡ø?BÁ¿LæŸK$ÎS;*g Àù~3R†ütkRñü2ü™ìïZî³ÿš hÚò,fÅ"MºêÉ#æ’ÞYv_Í^y ?VÀBÃ}xé¦:,ô¬ç–|ÝK‡cà6n\æW‚$<³â9Û/ÔÏS©çK©à¬7¬¯HææŠnAZÈÖWH²)ïWÁù—¢4¼²ö¬á'/$´¹N*3²®R,ÿÓYÜǽ ÃïÂs1ÂDU§3ÁÛ5P?A_ÎìƒÝ_JÖ/c®Cߪ« l´ D÷iÓ·WæPßHgÔù˜|±ì›ÌüÝjIÃYivgˆ¼ÃwgAFϲG&Ç"WŸ6÷HCG¨|}#I{È…hArkš*žÌ¢lÏkÆ(|Îç¦Á,_rѬƒ] eEzO‡›¥†¸íá¸üÁŒ$6p·7DS‰W¼Äp¬ "%eØ 2¹llª(mSb6´±&ù𤰸0M¾jL/ìÈÄŒêQ¸¿§£IPÍG¨”e:}´¢¾ŽSÈúw-t-žKÃÐo¼DYþè ‡ lh<É= K~êPGÏc7‰Hb2Œ ñA­ÐR²ïÛcðyƒ†²áðÚ·„ã.0g²é`g uý†i_öãø}ŒžÎ z{^µÿ9ÝŽqÚñ4/÷°†.äÜ·"PµkÂ-w>à÷µw¾9de†»Ã•ða˜&Zί%ëÚß¡ZdÏË?¶^g¿ïºÍ†ð”cÏxítþ(g˹4Æünô½(€XÍ \x¦ý–j¢ +xHŒÚ ÈÓ㢄½à?‚í+ÖbäeE|t%…É vDx_ S=ðžu¨žéº!4ºb#¸­ÝæŒ%L WÜqWÝ]=êGqêù~0‹±ÆKuÓ@ðP"v½”ñRx Óuâø5¨ÒõsSY;µ8f½z¾6¥çp98ÈG9T9@Îímpí@CÓð$¸ÐTμ\o‡oÍüpa=·5Rš¼_óÕ3`ás–•ŠùÍ|œÊ‡KÞæ3Òr7`:G ¿v—1N.÷qw²C/_ ¸ Ðz‘¹¨a`ϬXwƒ µ`Ì8_u³æßo@D5ÎÔ†UaLSc8®–ûʬ߄ƒ¥_XÓQøSZB˜×Xºþ ÛSÒÎT¼ZÄŒ– 0Η72ï”$èŸtgX½Ý†UÍ:…{f¾Ááüäga½´DÙò$ÈŸ UW—Ùn–/ÿc ?÷÷»agØ4û¦HÇ‡ÂÆ}àôå3c7W ÃN³å¿xè ã$(iSÛm(Ó‚IûÑôÒe¦â†2<‰’¦>ÍÖôµhì]áÃ.¼ƒ©Ö/à×y=È@ŸüZÔ‹ÐG[3Ö÷î –o“ mx™€Íë°³U“6)ècòO-<Þ>ÊäÇÄBý] â¬yµ>ÇâÂÏwÐCÞ–ì‚À>.F½_†úÞ› %Ú‹ºÜIÛ„h…»Fš Å»7±góõظø)DNU‰­7…¶mò¯¸î,¨g¦}K¿`Cbñ±ñÁLz8%sG¶ÁÈtθŽÒ)%æL"mI' $N þEêávÂÅ3.·µ’Ûx(ù$s´(¯Î»ÄÜxãM¿ì\ÂY¯«‹¦‘¡äEö%v˜û.Tu²‡»ž¢à/‰LcNžM¥ýAÞŒxˆçMa^O> •¾±`±>–ú­fGÚÓ¡ú¹$›¿óXÍžBïç'‹T£]ñºñÓøó[BôLû±]„×½ez§¦ódÖ²^¯§B]ç°£ÖØ•ëñ¦¾Éý'KþÀçsº¤wMw#N³xøY•îpˆ±Ó£S›_u''‹³‡éoœ$­K»z‚g—-|ذ…ŽUqpºÿsºn[»å±¬E•udÖ úßU9bE-wØÐvÙ ”ˆ”0F¸ƒïØ©J1Bi?ã#3ƒSx#¦øß`n÷<ÀÝ• ¨eÃ0–YØûç<‚ /º.GWSŒj†Õ4µ3éié0¸’ *&ްÓç T.ä#¾ n3ÆñšŸOÔ7f %7R«a ¼ˆŠ Ùùû*;ò!“5ûÎ!"pÞÏ:Ï9=÷#çþ‡¸råUëã2 ¼ßd@y7ôá$µX4ˆ; ÉU×Ù´ìýœTƒ)’ØWžÏ)*Ktï0®¯Ü©íïÃ0¢Î*ê ¯ù?y(Ó7†àªLï×Ðh|NÝÔ¡{r:±‘GîÿãèÊã©|¾0Ê’=ûžìK(Â3Z„$)¤¨HT´hÓbi±¯ÙI$¢RÅ}ÏhWHZ¥]»6´/~¾¿¿îÜ÷3sÎÌ{î™ó<ŸÏ3wðð{K(îžÁrçÒ”Fs†'Ÿ€ŠÓ]œª?‘ºûßòkþ¹;1A^òe!íK'M›ÄÜ'öÁµîU¨>³YµŠi:(ƒœ„Ù¬‚G“³ñá@μ³ˆH8˜‘wá¨þG#†H´žÿÃÔÅ8=Ù–¼ ¡]ó£°{“(&nZF„ÊãŤ7¼•‡€I5KÈTCvH¸Ž„†eµ™WpúÒ ´©·` ÒÙÚrìú9:}ÍjÖ«*Ìœ¶×A¡q2oöù%ðuÖøg=–“$ÓXD–*Û“°·5 ƒö¦¿øó¦z–³Ýa¯É„ÇK)s(“öZPì0` ?ý8P‹C§”OÜG¯pº•kãʿų¢Þ"Œxš‰ÎϪñ†»7ÊÌ~DbéÓ1VLåÕQþ5­él½tˆg?!ÅûØô‚q´x±.ÓìÞ‹®¥dCßáú>_ªÀþÙ[i´áV:ÿ9ú(Á];lÿ+GW6ÿæŸ:=€ökg3õLIúíö>Ø•ÙIo.Êç„Rf°)“Ùæ¤û÷´£¨…Åi>}’ÎÊšç1—kE8~CötOX×Ão³Ýaw³Lho {¹-úÚ®†U÷àìVoX!u‰\ú¤Iz5Ù %oVÚ>þV çÕœHÐØWh¢gËzóE™l€ q³¨ìIö×a ,¼’ žŒ¦%tX-œ [–tàÁo¥Ì?$*^Âk~CèßD,¥ÓØïKOá×%sÚ½ì=^ÌKe±?çÒ{ÏLQÅX•V~¾Ž»nçÀú›;ÙÊtSÖ.LÎ~ ‹N¨â©]›™dòFf÷Ë–þ6™È´¬B[Ž­bËKlZLšޱoo¥aV[Uþ­Î˜îUŸ\Î?dõ4é£+ÓXu®0»®…x=G‡3V‚è­oÑg$Ÿ[ ´‹ç”>žÞþŒÄž²WäÎMwˆÌiB'í:,ÓØ ªåÔ˜–Ež²¦¹ øìoUY}2ðêÿ8˜7Y‚ªT¹ N½,0É?É€1»J¤¨s["U8)ÃÙ-Iƒ‡BÑMCŒYB“éŒÿægL!Tk1ºÙ@×–6€ðþ\¶èÞsl°Mͼ©ìjÿ^ÈÎÏB®74—øü @ëŠ@òeýTz²ñ-µ7¨…»Åã¡x^:~lA¾v Ju¬·EYÉ® dɲxÙÿ!\YHŸOûŠ~ÍV4ÂR‘I9ÄÁ[‹,¢àÑÀK¾W6`ŸÙ*\h+CüÜé¸×Öçí<ξÍ8H—_ZK6™ñÌLà=D_¥3ˆ,ÿÌû}Ø]_F½W>âKh<ä_¾u¿ÝwÅ׉åX¥Sއ·ü…¾‚| Ú-íØ<ë[ÜÍ“;ÁòËZɘoôŒ‡7¹²¸T@‚çâ…BLi³ÃÍOÀaíAø˜UÃ[ÅPùfÿÓ¶-0ö“ gªm…Q¯Ë¹%Ÿß£ÞÎj0ºÉHZ¨(ž¿˲~¢ý¿YhëÏs}š#oPî«&.]N]Ö,ÇÄ17¸/•Oxk‚œàÕøGzv%—þºÉ|h˜ÈCw—|±p-¢Ì'Æ~b“ÙÑd!ºxx¦}Ž‚<+;xRõvMAÓ`]¥µ¸gð)éôY…Ž/äÙÔ³~ø¯ñ5éÉðÁZ—ÉʨRºv&M=ÒÚ_—"S¹Ê½3·¦r15¸Wä†>Ñ£•ï°uÐÙÖ‡wqé®Ô-ÈÊ‘XGuzº<‡3 T&Ñ»eápR%ž[± ULŸÂí_>äÓ¿Ó‘=šZ^I0;× îÚ1¡õÑháö—\±²fÝgÌáé“*òäh6VÝ6cSLßÃS¿zl»ž“.*r³î“1Í™`>l¢MØë“+<@=é%^«‚¶i‰\ušsŸ„·ò–’°5»é{?¼Ôûà—À-XÈýHï"W6RÃ>1Xí¥È²?smS»ˆXéœe4Ž~5ÏÎí’¦ååØÙU˜~¤6‡“ìdÔ<¦MÛ æÓßEXÍ-k&+cÅŽ>Ê^|mCÙ³/Q%¼ Oûo¤çtoóeÿž„–3ãÉZÕ%XyÏÜÍZˆÇŠ8]¸û®]„ªeEäý‘x6žÌ¡9í©¬ºO—äß°€íþ¿qøQ4ŽúÄg«ì°zŸ7<<ߪ?f:Šv =6£Ý ijñ®«-Ц˜Ä‹i@U*p¶x ä+¨pà šFUÄå°ÏÏç¾ôîÅÔ°´N@«g Éó…)l'CýøL„ç Ã<½ ”°ˆ"Ï.7ØG·ï¦‚>Æl`âtLè$nžqØ¶ÌÆ½¾Çk<ñY¾ö¯ßJò ,‘c…~0¤3R‹Ü (õ ׳ÔÃرú¤GYL’bµçœiQ’½ò|,Ȱ€º„Gè^9îÝsd«ÌÕ˜]f>ÿØöz82ÀÇ·ÚÛùIVŸ`u… J.ä^¬4¤¥ÇÆÑ åJ|9nµvV¥ƒ9ô¬Î%’ö¾™}M~¿£røI"),«I—ɘ*Óm+&£õ7–×Å!$ ‘]Sì©uQtUE&xmðg-³~âänÞ”Òtç‡Ï˜¼Þ‚¾ ûÁ¾…¼†cgŽ2ÝõNz ¶ÿ8Œó™ õqgá»[à̧QLØ“EçìG !ö}2\lCiuúyæiݭʶ}ÀzÑIìJ×q´zØ>£‚ñ£åI¸ÔKHXÃ6¾û>Ì:Nšš:-œÕC…öV°¿;ž.ð•¤#ã7’ìëIýí(l>‘€®‡éå·5èñzSOa‰åÕ¼Z¹®Å3¨Ó>6ú³]—ǰÇ*†TÆP…ixßÀwžgIÁÃböܹB—Ù²Cj¾TAF“JÿZŽß¢VRg gÕ8ÀrïgáÓ†CaW VJŒ¸}ƒÕÕì§vj<„±QÁ4ÐoãºC˜ñ)øçH?Z™ÓWÂCäÅÐU¸&AXc¤4›.¿àº=˜PÌT¶õa·\7»Y„_•ƒ©Û(¶Nß´žy8N :ðã¯S ¶¤ñVŠt±rÂýŒSõ`QÛÇÐÓçÓfuL3&Û|‚±îÚZ´/ÜÁÖ'§‹Å~â„£qd$§‹ŒÃê}eµÈŒÇ]•0®ù!ì÷ÜÅ.©«²‚-•8å¯?[צHÓ—;ãÉÙ–ÄÌ,Ò±9=„ÕžúŒ;u7RË‹‚tŽÂ…€ÛPu)‡èèlÇOuÄù§.'õŠáó#Ä|Þ²2¼™üTz oRÃñfË ÑÑ„WMMdW*멳ij³›ñ„þ}¼ºß‘î}§ŽiçiÇËXÀY\YÍ=X {}e©Ä7H¯–£Ë¶nf7mÒy"¯lY…ýiö¬LE,V ÄHŸzˆ8ó^~™À"lIÐý—¤oØÄgTAù¯à(`½ñäúäžæŸ…ÔÎ8AMÞ=8ÂyÏîᦿÂz96©àün°Ù“ÇÇÒNüï®õqŸ_‘i3ŠÑö·(û-Zð†k”PáÛš³<¹ýãX«d2ûÞÁ|è;ÒU¹ùòå$꜋Ûv…œN‚çðxºû_¾­¤ç<¯@Ûtjû.-u6¾P„~8µšeMÖ¥úª€$,)±ùPÕq <”tèëþ¸wbýé·çnîãX^„4›¨®Ã´´MIºy3ü^Ä ¼­uFThÅ z¯a ŒìxHr¯[ tÐ 5ßPÄËŠð¾Ô3˜”ê" ñù~•Ü_øôÔÞ¸fr–Zû¿ÂG±­è¥q–¾:Ò‹2¿xßKvÁF¿h,žyJÒ ixm ´†è2…"ufíîN§Ÿ”ÅúûcØóñ\Þž*þêëxþ¦?šhhӋ׋h¡—;¿ûÑ;Îg–sþ€iFLs–¦ÆÇp®„=tø~LŒd/R¦°Ì_ËéÔ‘!P}ÇžÖM ÐÏ.7þa.¾ã˜Æ1ºòðMØ;. ÄàîgÒÔ7GŠ©Ÿ ¦o‚öЕJ ï¢,­µWgjµ~t÷_(=¸´¹†€<:ëÔRúÌX›¹÷UP,6…yÓh¿ÐDÚ^þÌ£•¨ŸI ÃS*nÊŒl²»’¬YÿAôö@ê¼ð(Ó¯>M—ùY°ø×jlÅg5ÜÔB˜òá•Ü’uÒlþ‰Fö¬ÿ6,³ð@«séŒXîü“k4X²'²Sâ!,ðÒt˜2‡óâÝå­·q¡Ÿ¸ ´U·Ïª¤_Ê?âÞÝzôò¯¼úk;2xûäáòs™ZÈ0?^Z¾íýÜüd¼ï†[¯ÕbïÎ~¾Eu\™¡‰?˜ê¼*^C¦¿b–=»7Ý’‹µS£FËÕ©û¯jâC,bwÂo’‡.# «B£º ·FŸ,Sb‹â È1>ü»s »ˆa^\k޾*~Ä_ýê!)LYȬÍrÑwh,{!ô #ç*²KhŠëo-eŸaƒ„–<Ô¢b¡Ùø[n2»×åÏ&¬s¥ív x°¿ƒ>¨°å1‚(lËdOªÓ¿ÑIÒòèGo‚„}@HîÌ—âC€cð(‡Ù¥%‚p&ÕEï½CÏ‘ ð{Šëî®! ú »÷ãî¡éåDýSžº]›AÊä脱I'®Ð7KN€ûÑ6ÿ¸ùø² „^.b¥ƒVбæ#¹<2Mñd?<ÃX™(øpçú· òJÜÂ’kSyO·³½g—àŠ¨gÜ›Çaš­³¿ðœ¸ÅH‘\édîÌøK\ÅÀNvä{3îyX Y„LÙK%õ¨T@%•MaFêù¸ñ¦<}8€ Lföâfp0õ¯"Ú‚ÚáFØK—oåZÚòx~zÆìÜ +n™È¹:Ò K—1wð>QÔA±^èúÐ OŠüÀéwÊÑÚî ù(%É,¦+sÓ÷z½ÐZ>ÚSÕá…ð«è*„{ÐŒ€qôùì~4 Òÿ¤qHT q‡UŸdÃ+nòàB½ãf«g±à s¨ÝìSxd󾆹!Ë{æ w¦­&“½áM•½v(Ž ÞÀÛß×¹•ßàŠ×l‘¹Ïýj}Ct>c:ókb1Læ?®ê@ÁÛÉöckàãZkzëj>´ø•‘±a½ÜUý­tQUìÚl/sÇÒoÙ…³¸÷ÃwØÛ'ÂÙÀ¹ør­H‡[ðœïg(ËW¢Yj¹¼êÙ-Øk ËÌìÒqàøqð;žZ-X†K·íBqù•(«€ý=·/êˆáãHIsUŸ¸¢lï”R¨ù) ºn¡Õ$AÈÙÆø™™‡Ü?|Ñ(N—Ç|à÷mËf¿Dë¢IUãmL\lΞœl†ž(#;<÷„±ý«óáØb[öðÜ>zG_Ãéï¤UÔí±2}n8šÏvêôÍÛ8…oÎrø“Áª+Œ½>³žðõÙ6—“8wÅ*ç® Î¾lï´ÝìCƒ#y¨ynË~—£Ÿ²¯ŸÔðØYx ÇîÒf7.ûÑÜÅþ4'|:î?ÝÅ­ŒÜM†¨Å¨­ÉÈ4PàÖaä+žƒ¸¯ãY€êb6n“ûb\]wj˜»i)vïòÙ›×qÝ[âö¯h»3û}íü—•öÐÂÆ€žaÀ³œiNk%ä©z¨Ý0ÛŒ66p¦ôÌZËý®CÅô«ÄÈ3–Ýÿ¢Ê¬ïኢÿqâÃ!~³ùæò >i6àÏÕŸÀÿ¢'¾K¶šp½ŸÞ¢˜š=`M§ýJ¥ñïê¡õµó~1Ð*½ÈŸíúÈõþ`¨1£Š¯(­€ÏMÑóÀ"¦ i )ß@‡-b×±‹ƒÙ€Ùa¦©ªË|s«0´æ$·4Yš^·3aáÙ&Ì÷ÁjUSÌ>-Ïd¯vøè¨h¾!‘)n¥ÅEûèײL¼±Q•N8ü‚”céa§r²UÁS@’EîôÇ®ÍÙ“Ö­P½½—6¼—ù<̃ŽÝªøa“'U]cFeí _ÀëY—HE›u=¹›&‰¡íü¯Ð;™Ïm85Åw²™õ˜æÄÅB"§±ªl"¢1‹Y~ªˆë‡M:Ÿx½YPêç.‹ÁwóW¤|_^Mîq\êù þdÁÞ'ßð?Û.½7a¦ët¬ÙãK7¶L„Éðf¼Â[®*ðce γT`ÝæÀörgL×çw>޾—š Åü(oÇøtb¢« M-²tÃx5–·KKSqʃ×Ο\¾º¬Î_ŽÍ'ñ¿¹MoÔbZß+9Ýõ:dýßÉ:%éŸßaÜêLoÚn®Êe¬k»¶M„¶ýÞAì}Ùž’i QÄÃý‹¤Xs€ÓjnÁïj½8l’ÆÈ‹lýð4Z§²AAøaëE;“ä¾Ûd]«?ø¬q¥5Óêp‰– L”Dá¶3(£ÞF¶¨Í¤_ÄU៤­—“`c+zÀwes8ÿ¢¶Õ@ ËKîFq×]îùsp,êãÖK¢´?Ü•lüWƒ«r*ÑW°™\áÂÞÀÉŽ@¶TqQK£Å¨ËÖä=‡Ê÷Ýà³p%›v| dÊ“âk .+FNçèáܪݸèÝœo<Þ—Ïf‡Å’в'†m7_Šû` )„Æ_àR»oâ¥+Pæ£Å+›óP‚M{gMgZ–¡ôÚó¸äû(»öï[]!íM4G×ú}6: Uî#ü;ŸZOhGÁÜaÀ©ç[^ýShë‚0ª*4–^tï»OD[[îóˆ0 ÐoÍ^qÚWî ý+ãçW;‰Ð½ÝpèmL²Rà”_wChæ z¸©™’‰fûôr0ðþSîä%ö|“™ä‹m§2 zÙG\YRGŸôþ®ú'ú O£wóÊPîBzu4Á•)»ó..œÅ0Öò–Šƒ´ÔrÐLš5fàN~ò¨ |´¾yõFqé·Å†ìiš½óö69òi?tV-ËcÚñtÏýRŽ_y’¿aJ ÄÿlÇÕ¯•Á¡'uê\ñ—J¤{dÛÆkÒæ,{ˆ?â›ã¡ýû1÷ø!N¼i vKT`TÛj8n»²ìÞ° Ó+rá¼P;þCåqÉu®{ÕáIŽñ¾8¿M¥iËp6äœ5¢ÿ\ã å€zè&zY'ƒ›ìƒ¯“¸y‰óP.¢Þî ǧ2y༠S¸î¹–Ü­qÜP¹õŸÁ¥^aäG²<ÌÌß‚í³ `d)¹ *̬•ÇÐa¹¬1t ;ʉ³¯†±pR¯í@^ø5Á“ídº|æy[Y>/æÐÒìéBf×ër}oâ¹ ¢1ÏÂílœrü¾¡_àÖÍ•x¤Z•6\:ÍÛ|8œŽêÙ;<(ÿÖ-Èi{‘wÞ8^VšÂž÷þ!‹vÙ²gzÓ°ñÇ \y#¿Ì[_J.AYV w=À ýÀ9… ‡î&îOáw(ìÀ;\ȸBW^øÒbLOü3Zë¥ðŠõa¸ósís øí¿ëˆ„´~ªÆ~O«Å*½y­UÏ%lÒ¥?TP™^7y>ï—~ä)n‘Ä,ÙÆžÅ´ÚåöÔ¤@‚Þã`ÛÎ_èžÅY%|Áí³k`ëP n”£¶ªª 5ǧÆIS·ãkà]…%Õ=™Ë§¹Š£{JÏv¡Þ‰¦en¹9([Ò ë$†¹OÁ2ìï¼Ý /G¦¨NÆ ’}üÜÅQxþù;ø¯ÎÍm#¯$YU_I<+Jcî$@½E>NïXÏLzê1ÌÔŽiõbSÓ×äíiêrà uªúQf“3?ëà1Òöì%oŒ´»m5@þÝ[k7çÁ¡U›àdÆnÂiyªÛJë·‰ãù…£X­) ü´ü¿Î¦›Gpj™œ™ãL0â¶Ádgkîûñ)4yÞ0¬ýU€—qÒ¾¿ð»ìN1Øm2{àð¢^ÎnãÜ^ÛFîüz&Ú€K^×`Ô×Y´á¬Ä=)YÉ-ðdj6÷»Y“þj# ¢x?ôãŽãØvÍQ^làŒ¢âwÉÆíéDÄì Hä©@x£œ9 »9íouè><—Wõ/×dGüžŒ±ä3_Üü8\Ý¿$NÄçöìT Êøk±ÂM7q˦¤ÖiW6×tÀ’­aäáÈT½ï"çnáþøãÀûш †Ä|â-tn=^Ÿ.p9}i¸åSºC¡ÌJ°Ò*’xþØcܰ]„^­±gåü(&(Å£÷Û§ñ?™ü&çt[pÑrQ¾FÎ|w‚þžÁ󙱯+Ýàд%|{ý`ºaê3”4ÿ…͆ÀmÊL’éh‡|-Eš% ,æ“ìkÌñf¥.åŽØ`À»épê‰$ ê!«)»zYölVgªÚý0Ç6¼5ï ¿w/îÝ/ã„Pøh-Qï™ËVÁN·)¼‹ûJ.cÒ@..× oÍ/àâj¨Ûò‚¿í|<úàÌ&£22>ˆQýüò›2ô}i=—>éøQ},¾Ú?ó.òaMºskÁrÓ¾“ï—vgóV9°³ÉçV4½†{S4ØUÙ hĄɲm7ð|†=È/Ôâ9ûøâ8ŸîÐ?;<0w)sÛÒþ®œð¬lP¥‡ÒEé`*¡ï”`¨ù<ƼƒY³®ƒ[´Ñß þ£9#{ËGŠ1Uõ+¦ÎD–/gæƒcjHiX€Nçˆqæþkw(ýÀîŽð0t#Ì99€Ê}ÜÇù[ñTÇ/[29ßá£ÌxzqÀˆ~+BÉ 3žïEæM9EÃ!uè0.tÙNž †¹¸×ä*…ªÓ Zã¼ d½[7o ¾ÕÚÃ{)à‰ò´ëÉRˆí³§•ôȪÉ/y—£·Àzú¥Â’tà§È}NwÁa8ZwïmèƒÛû;ñúp‚Ü#˜óHTwKÓÆõ=08i2™‡øëÄ]^˜\7ŽÙn l³ª¸ë‚zW«L„a©j"¯æ…šqÄæÕ"ìkƉY½á»ªªƒ´zv}qÀôÕ׸Ÿ›zxÙëOcH0Vþ{ËIœ=D¼y®#}‹“÷‡ ÍõPýÅ-LC m<žù ÆðfïrÜÛò…ÃBabe.t©5rÓgr^#kpÛ£¸¾]ÇÞhµ.ã£À›ï$VzľyÅg©:IÁ¦’m$»¤žþ=lü ö}FLwoÆ–‡™à69½†w3á§3qåÑ¥`¨•LÔo‹rSÐGµ /`+/Hø(n-õã$\.“÷òô€ïr¾g¾2‰JÇ=f~xê‘?ˆþÒ£x ”nkVâ®ø™èu¿Mì´Ùê'ñgÏ,ýªE%:])9•Nv]àNŽSàçQQg2ƒ–¬JDu•8Rðõotn0Ö°¿7“o‘-tî"-ê†cæ÷vv.h?”b—t"áfW‘Î-ᕤ9¡Í*%\iz{ƒËûZI¹µ>M=O£uà²ç6j3 õ'²U;à|ì4ꮺ‡ºß›W|ò‰|W2É–ÍÕ‘˜¨{†y¦lÓåZPK¼ ëŠ.àÌÛN¤Õn›ov§í­‡„'»ÐÀ5–®MÇiƒbôZ÷”RÝ!9Tò^Åm× G…[ð ]‹âþ–ì@ã0DïÁžùîÜÎEÚTœ ±¢âÉ$ûâ6pM_ ¥àÈ–3E:­î'I&‚ÛU¬›uS…l1ĉ½Ä¦Ì–¾~C4ŸŠ±©Ÿ•Ðsïsøv†a@Y.“C¿Kàh¨ÝìÞŠ ð/¢ #tÞãEzí| ¾aÎÆ7ÈÀÚ%—¹ç5ò¬Zd“·ó£*y Í„`¶»:«ruFÞÛ%ÔÉ&ìÒ‰]) þªÈRM³É¤ Iϯ…‹Kàu£l|ÆÛñgݽG–IžË‡¿9µJ…eÛ‡™ºªdb^r}`A‘˜¥’%´ó[(¼|¡ÂÔšÍI@«>änðá¬LˆJ€*ž4ÖÊJºöB¸ñ88¡_¿Ër?:[È£Viôè:JðúV2'ƃ;´D„Æi#/eÊܘD™Ë„Öñ`ö͆Š{_%ΜĎαìÓÙrXÕ ’8ËÁXuió<¦Àæê<ÄE¡“¡w±_–§ZÒ¸ˆñZ0Ý(S&Iã%ß8µÝ9ÌÇ‚Åzùqž—Æ0ý­x,œk5°ý b÷·¡Rù>L•{×Ng@ŒB!züEz¯aݺÛ–ö KNç;JÃf<«€«‡g±Á6 6é¬lù0€u8£¢“º»3`Z¯*[£læ ˆ”¦ám͘*á F7¯p›2 (Ïu!ì¿Àà ‡:|}êkKNãTx‘-JÝ^)±ÇN<<ÓXˆó¾m¢#ƒÛÙã?©¬p¾5+¸¡‹§ü“àmÆX=ë#Þ>2Þú{€üÞ,ÎÔOn"Zwø {Á‚êš?À¢w—p][ 7MCn¿á€1Ê¿IXÐ*пz‹›á6ÞýËU-åãÊQër.fípÀŠo&2> •Dâñf:wåpß0½Ÿío‹?-ííÿÐúWO}H—_½[`dqC×äöAiÒ´œÙ(^@Q¯ ~Åø™ð蛋ÄðßNÌí|ùÆØ/JzKÙ‰·J£¥áÕßžõAÂLå,.Ì€¥¢ž¤xu¿îO¤líÂÃÏ>“Í¥0ùž ¬^àGæéîAÝ5˜ûõ’äø¡ËáØ±š,Ã=u ãZn‘dÛ¼OÁŸ+㈡Ñ=´è 'r¿áæK·:ñœJ0*™_¦Ä‡xÛ*¯èœE߯þ¼áq `}Þ/ü}¬uªõ9Ø“ËE¨ÿâbÎqÔaШ\LшûјKöJØÒâë•$fñT¦ªñ€·oã*òÏXŸ Lo‚ÿtÔYÇ=¸Ê¿¦ÕwÄïÕÀÓSŒ+{Ã|Í£ å¸]{/•ß2qô™ïH×ÜGÎ'r•±t‹b|{NÇßÁ‰–v¬ÏLƒ¥d ¯m•±Mí*:§„/tE ÊY‡ƒÒ3xÂn: Ý*½_šc$‰Œƒ0mÝ^Œ5 d•Þ~QÎ]S‹«?ªÓãE1õy¸LgÏpÒ¶*N.ÖR¯JÐŽShТ¿à°ätžü-‡µ¨ÁAyÚbîÿÅ-üý¦&å’ヌ‰k¤7­X?žÙ÷ÈÀºÙ}H"S9Ûc×5vk\¯´(¢¯M¶VÄõÏîÜwB¯s¾ìx~u r‚@mCœu©…Õ—¼ƒsZ9ñ;y¡{^Ü‹‡ŽÞ.-G‹§¦¬u©?œ]4ƒµŒýG†ÚwãöÓcèö¸Z\[©€_ÍŽ`¶§4"¢M'}¿ o¦±5ê‘3ý;nÆÆn?ΰ ŘsØPßáÝÕÄVj˜Pç[à¯â*J§¹Q×4W¶Vüõýžƒ%SÖâJa+š”=–É O`ºqÃøÕc;÷1Z•ÊóÞòzgG³ª©~rhˆl…Œ‡"tŽÌrò¢Å˜]iw`‘ÁôúÛäÑìùp.L’n6Ù‡´Qx™ + ©ÁöÆ=8¾¡˜,Ê)ÂÇÂP?Ý·ÎïƒþúøjÊjXÿ£›ÿÔàùïG¹ý;øÔ÷cŽe›‚Þ`V±6¬iI$œ"_ç͇ڲtÍGqó‰FƒMž8É5$'©Óz»wœI¢2:èƒ÷OwqjYehzüŒ£‚°Ì¨6s>y®0+ÁèBHe=…iÇá‘ONÿ»· Ãøš4hG((û‚oC‘¼ü=ú”;׎>#9œEÓ<2è‡!»]¹¦§Ïy…ñôýZnÝÒ £Xƒ‰š4zf¹œÍ¡ïœÞþo°çs;ÞÈ›1ï×jËÒä`»L F¦@» ­/Áĸ¹pÔñìrå_Ô=%·ÌŒäUlâ9„tÓ‹0øÕœ …PWå oZÇnáLY8$J”_ñ4us0åïrøOcì¡ö‡Ì[>V¸FÁò‹‡áEûÜù@UdÄ4 cn¶;•=¼ö$Äo¬Öæ0?Иifí'‡"ÖÃuœ¼bæ?{Ì­i.±ŠØ»ŸC·ïiûÚ¾r7±gó-Þ^¯“X ?Ž-û¥ Ó>­±ºq–}žÆµÅ[TOܘ3Þ=‘½Û­M·ë¦³Þy¸z©=ûnµ”“7s*€>ð@C¥ßhÓÖ6®ïÐT8®øV·þ|/¾ÂŽEÓQ†äœ}ŽDz0ËÁo 'ºf®_R}5±æT'ÖâŸ(Z(ɱìv¨×ê\OÒÛ~’-óÒhìWUì»ñ woVf’ïɇYL©ÅoeYpçºòjÅk¹‰¸¶˜Y øÅ2“Ý×É/œÚÍ_S‹¿œ¿À…Š^â=bG—êý$S4“8‡Ú…Ø̼í‚èÈ"l¾uŒwö]ºïz âVbØ£,Ü(óåÎ]n*ÍÙ“Œ‹NicláE°I9ÎGaøZÐ[—K–̆ڷ¶ìËqÍ‹0à@ [òxZ*Pë7…Øôè6Öò­Ñ®ï'³·Òì÷ÓNØ£ HÕ•à,ï!皎×Ó¡Í„OÀü²:RhSL:…Òoö¢øé vh×|* ´“«ÙxKçs’¼aà½*ü¿fù’±›ñp#Ws½æ‰;2Õ»óhHJ2œzÒ‡?ñÐÅà „ýtàõ=LÉsd­ßa'Z³L·ÝÜÒ ËÙ‹ö6˜«7†]óñ Å‘›IüÆpú®Ïœ{rií&M¶ýµ1Ô¯¼¼ða¶_UÈ¡kŠ,JÚ‘>é ÉÖŸpÞÆ0ËtÄ—@ý‡¿€Gb6:ØLe‡^|ÁXêÀ{˜÷Š^¿ýs”³õ NdÆ\x ?õ9ºå{(ëîÃxÄØ•¯µœ °"söp…]›6Ð9½óhåwE¶Æs,²<nj%àä¹4Lþ>;fÛúÏAEèŒk©FO3%š‚æx*ý<œQ3E¥Y4 ·ÌõØÄNý<o‡V#7çmá6¼òŸ3ãéÞ1flSéü‘n„[Öª¼j?,Ïÿ M !Ò0á9{àÝQLÞ¸±7'Óu•£û±G\zè.Û}ámð ~ryŸÖ‰ðb… ¬^—ˆîSáæxIú¹BåÇ£Iþ1xÄo"©¹' þíM®µXÜQ¸ó2Q:ÌpÒµ»Ï–Œö!/¾UCø˜N¿9 Î&Â=aòF¼ Ÿ|™ÅRìf€îãY´·úßþÛi¬ÍáJØ>å#RK¢ÑZËÇlų-B¨Ýx[\ÂîÛàµól¸¯ë–¡Vÿnïƒß¸«ÝVÜ"ãžùwÛKÞbÛÙ yl!wës5™ÚgÉÞÚ\‡¿¶ÅB ¼ãÖÜ»Àf(ag¹ Üš/ÁþÓ2kº.¹½vìÞ¤›844•ø–+BY¦-ª(öðv¿‡§í¿°ÇÁ‚q•qI4mÎö§"æcim±­0b>c£È¹Þ0¿–Í|v“t&GßmÄñEôžÕ6\3 nGüÃBW=:n~üÌS‚ÏEØrx¨ªañÌ\HØ3Îð7–ñ6w<ç&)80ó’—‚á$N¨‚܈GÜÙ\wŒÏ›ˆ3S‚pûÎ8쵨Ç+.RØ÷IŠF,J‚s¶‰8Ax,þ3ú„¶¯æ4î]#›îÊ㚴è^U„•þ´×¡þM¼jî*j¬ŠC—ý¡h¢ù±õ"Œ«= cÖÅ£è¡w­ªí„ÿ¯uµ\†ÓZ°x§ôrü¿`œ.îÖa«FÒa(-†t•ÀLÚÒ2`·d7 wòˆZv£œÌ0ÐW–î1DÕ) \Ðþºe—íK}ªìýÌ$ºµTîÝò憯'BlÌxNzû9p<­Š+öËÆ4'v¡Kœ­)Reùd?¤-ý@®å/Å{UgYúCØU ô=è<5dklÃø•w}iþ7_ÙÖ ÿ|¶“¢ÑdBy. Oº;JÉ‹ƒ+`ç´ôú½ìü¬pªrJ–9ÛG‡UkÁ%Ñ® Z‚}æiø¦f÷beÝwÿ ÚGôciÙE¶ù¬7‹vQgáû˜Dm]'›ÃÒ}i³™›’&ÌÎ6ײ_ß|°—WO›?Ñ?µ0{‘[g•O. PuÓï°k±äÚøcý[}ÙK6Ldî†lŽEP[YÚcäBçíhM?èî“a[Y=.ÝåˆE}­(ìzŒë®ÜE~]žÈŒR›NÜað¬,¬ØÑˆ&LnšÈ® ÇР{z”5ƒ´©ù0SY€nïYBeå—s ›º0»é1œ®œN³×>&ò>ôÐ3šÖWDW\.`‰¿*Ù´k©|­þ›ºž6û–ÐiMR,ÂN‹Mñøz“R(o}=7°šlS¥W–)Ð×»h˜¾N•bk/n%³dÙáŽyó—Âus5¶ðV{,íÆV,ô…Ÿ˜Sâr˜%‘Coùwü;:~²»fSÕè€ïX²&?…<ži‹åâ±s:œ§ÜQ…ѳéüþ^Ø15FެÅ–ªp2ßþ\ZÍ ìY¼€0Ú•€‡Z„4†°4àPª!˜eÂñ&²ú‰4]l Ä®}Ù q°Ôt½»”ÑOtñ¦M:-y bN•°£ò0²ƒ”ï|¾ªç/<¤b72Té B\¬¨MüC1g÷QnéËîvÇlò„Ðú<îuŒ'¸ÊH±Xµ3XYÙj:ƒ¼•)n´*3 fó#Â9“• \µÿ¤ä2—µÆ€y6V²÷× äèž;U‰oåÕ)ï÷ ÎæìaØÒºg}xÌÕ© ¶nH4u«Œñ†Ìj–|`)ŠRÈMp‡ãQ{ëX6L|XÂ.3^”d¤K­Á÷ã¹ÏÓÿ¹Ý~Ì™CÖ©¦Á€¦û]4â•ð{n÷Ó—Góؘzþ6J=8 õ¶8C`5 ¶ƒé{ÎÀù˜ðP‡J\…Ë“ ÉË‚4âªuóKÿàðÛ=Ôfà8ö¼à2ðœ†-=q &u–ó;DêQZSfk™ÐAqGªÝ!HÓê‡ñý[Ú7|,ÎEcxh1UHaC·<Éžñ7±ó³û0WŒªŽì¢ïÍE©@!^]ƒ®ì“R9­ü‰?²rào£ Ó]Ý‚âj÷Á(5 Íçnw°£ð‘çV˜ÛfÏ.ÖÖPì΃l¹nZ?ÐÉt½ò9Ãt.hÓ\È~tâ¿à×ØÂº$z[L c 5Ùô>3\›Mï]Ÿ„É ô„AL]ñ6ÿµ#·\ÌØåi±Ä¡ÿÇÓ]ÏÄõX„Ò¤,-?“DW'@÷,>~»W ›ÉLñÚi”ÄWcåŒI,¤âî=Ö€§÷Ctb,ïkŒëŸ[RXù¹ èªåÏYTþEõGóŸ¥ÝÄõñ³P~™"]óQ‘…¯›ˆ{= Y´÷(iÛ¸üb¹™ÛƲ짷ð«w£³|¨@ñKtݘÅy+®e'ÖüÂÔk€·íØ‹?ï¡úr f°å‚“hP¼2ZmO M®±ëÇãËõ-àÓoK¨Ê¿­ìÔð%r5f„ÈÜ.ƒµG³à]èDVWËÊë#ñ—‹,]¤à„?,9Éôê®±h?S–iÇÒU‹Ÿ‘CŠì…è"Œ\:•‰,fʯw²…Gºé*Ÿ þÕBÔ&`+ØÏ~ {êæÒ³…7¡/ê&|L†Cë”èî U°RO‚ lÜó×9àzÉfZéÌÝ u² _ßÑ‹Å1³%Yò3VÙ¡I羃ËZÛhÛï§8,žf Iš¢Ž©s`VÂèù¤hÞ³!ÜN“ÁE*•h)‰AfçZf±…R§Sáà ØÍÅeÙvйz‘‹NÜ•‡µ9Ñ,ü§?|Ý= ‹’¯ñãʰåº/æ¬ïÀû‡5qíìÝÈ›¯Éiõ¿qÜY`„ääEÞš¯ é¨ îŸí/x~ÄÚvJÒöþ8RôÊ—½ ´†±Ç–¢ÎŒ pq2&Ü}‰}‰¥Øz÷Üëå§Î÷ÞÀc§TNbË^†›øM¸iý,öoh[^A“Wtb•ël(\›…»ÍÁ(d'~œ ó“®ñ?«?Æ%{™¾¥!ÞÚæIŽÆ¥ãZGz97µË‚•M?O@ù —˜ÀÀ€ˆ›t@udµÅ#üí/!y\¬HâUÚïESµ4Œn&=—ÞÁV©()º k¼b@§<]¢¶’)Í †#i5hzb6ÚêA̼íäUýú¹p?ȳ?X÷) ü[²Øv¯?ð~I58Å“à‹×Ñg§9í´&pa±<¨ä.æ=ÞŒÉTQÊÈŽ›«_ïÑêU*tLðdêöî†{†ÃdoÜËWïøB Ôá„+xç nÓ,N>ª'Â,Ü{ Œžp¯ÖŒEÛ gºdc=s¢œÌ:ÏQ^‡‡?´€JØ/üû¸S-ÿÁbK[ŒÛ$M³ã€ËT¤ŸOm  +yæË6àS.ìËzgX™•¿!Ã7Ì1âwøDObŠås©o ëèì#[ÓÚñç{îÁ÷¢V¨OK6Õ“+a-˜ŸÇ¡0ó`߯Y³¨šî¿óê£ë$•Âf$ñÙD\UBdw|“®PÇs“µêYÃå¦ÅÌk¥ý’|„œz>ÆIš÷Ë^´~uÌÀ#EÄh›Î6ÍÄß÷çBYÁЉö½û¹¡×9÷L;̵l‰ GÀ>DŸõÚ¹áFÕÿî´SÀ×vûpÝ׋ÜdEeªV{>'˜À’ý¹ ¾u4ÖÉø‚+…¼§z¸®eו™rrýŽÇ-¡GªV8­¿Žãì•´gÎ?îëñV•‰ª.R]ÆÕ8¥À›-m?hÂfǃþ4†'Ü¥Dk] IŠZ`ÑÛ€ßÞÞC£¬oÿiUùceá{üY:©ã3~ô!×\ÝÍ|˜%='QÀY­» V[ÚñV×qP虿ËÒn3¶ªjͬ–ƒÇü°óüô ŽÖO0XõËݹ·éé°avð#ÖŸ;'§Å¥9„ÀµÝÜ›“é¹ôµôU<ú©l¤ N°g ÷YÛŽAòàj6Ëè5ÇŸÔ7½È„ŽðhìIÐ:ÃÃÓB¬)ó™*xŒÓK¿*dã lÇ®ûuÄ©ØŽŽ¬É¤/7ë“§í7áU+{)c˶U ñ¶²ô‚ÓØR¾†¦Ëypý™ QŸŒŸ;w3ý°2Î\u/L.[ÍÓ^ H‹Î¤Òkî{¡<Ê)¥/' Ø{h3î°¿õ#Ð!Á‚DdIË‘G4Á«€½8¦ÌôãÎpé Ÿ>ùÈ㘡f43¨¦ ÖºÔs.¼ÅÐÁÙЭ±œØ/ÜC“?êQ÷?êôÈ9q¦µÍŽ)p`[%Tà«jµŠ¶£ê†<ê}Ø”Í +àß½+ÊâÊ2–±ýP¡™a‹Ø³X >ƒO&錅IN€§H5žúÎÍù"Gw©äóìhêõì Ô +È`‡ ÛA¾¯Œ®©>Á]ß–ÍÞä]jìv’þz6æeOCvì€Ãµe¦ñV‚­å"ÖkÐýÊ%¼²5üЧŅiPºyûãxŸ;>߉¨(=Å›c01c.ÎTC-c)º–^À¦ªç0R€}»µQI7‚º¯€§]ÒtÇ;\úùº§´h'CȵÌá[,€—F/йL˜eGNÇnõVx¶øtSbí|¥”¼!ðÃöï…%=ZxåîÝà¹ï—î®H/&øûŒ;) ã¼.@~Ø^´znŠ-{8èŠÙ1ôIo-Ý4„—DzüG†üPMW:2©ƒ²u‰ÙÝYtLH)T­k@ç9.Qn2ö¦á÷ábˆ¼ÀÓâ02ñ,Zº‘Ãí©Mž ½yF4^›õýð©þ;ÙTµ˜VàE<)¨„/÷>&jiA0õG9Ü4œD†íæÐ„½L s )½ƒ§ÿ ÿ!ÐÅ~; x¶jUŸ7ÛŒ™´'AÓIU¦¤Êî}1`ê ÎpÛ|OÑÁ)ØôÑ:Ì,'šÂü3"è’¦Þq4ëë\Öí-QnÖäuØNÞmÏ«¨°£†8;M&ºòËp_Ú˜#i +•D±7µ¯ØN §ÕKÐrÁkpæ[ÈeAøUo/ÝzN\6d¾Ë¾‚–tùòF ê@tº;ö>-ÔªIç {wm#ª¹¶qÖ5‚®ï7\Õ¤LOËôÛ_x´c dÙÛ'+˜Bg XúÎdSÏ^á²W§B¢ä~à‹Qˆ·-ÀŠ·@–)ÔÃó¹óÁ€Ÿ ÷9Q~n¹ âØÍ“H#Yò°Zˆ>Öº J.æ¼y hÿ¾—¤tÂq®ÆNîÆ*’Z•GÜ{x…´¾´)'’‘‚lgX"4¥|Á+Vlâ›Íž<=c{¹à‰ÝxxI?Y\fÁܶ@Þ¯r®•]r|¦lDÌ3Ÿ@a§|še—®coÊ¡Õîñà¿e$zBíðåôxñ¸øh \?>Çx{ã×hUê8Öþ,±»¢ùJôjS0²£­/êÀ³7¤uÓ g*ÅP¾á0fu‘<¬â|@±Ö@ò`¸Ÿ‹®bª:Ðq_‚>Rk…,wJ'‡š²ƒF3iddÞŠ†øœiÀ!ÏÀºnë¢Ãm.½ÝŽiy<Å­÷ê¨Û kÜ >ۚģç¼Ç¼õ‚x°9‘'›s“h{²?K‘°·8H²ÂÞñµ`˜<‡õÍÂȇ¼Ð?ª$óD÷K@Û¶äï"½Xë ­úøµW6Îh#¾K*ɉ1rìaëéVû_"Ðùwê-µ¦2£˜²ÿÜ«(oR¢¬ùåÜ;ÿ gº$ Œæ¤ÒΤ¹ÜùÊÙxd[ ®r»„Ñ€’+„7h ya!`qC•¾KŽæ6„‡_‹Ös#f'yI]rtœd ‘:Žš^Ì–a©ë>s[^ÊQ‘Ó•Äv´î{‹,´#'Üöâ‰û´v^6ºi˘«8áÕI|sáÖ‡â<»Z4ø*M—ü8 3S‹ùŸåé± ÇpAC wT±UWJÓ×2ˆ~î츬à ìë†¸šµ±¼þ÷P²K”G$Býç˜Ì…-óÀâ*â¤!`­±)D¼7w{ÓZ­9ølì,Úù—k üÌ­ËvÎçL…£¹3bð«‰Ïbž*¨7íã]9dkqƒ° ³½yV›@[Z™èÍT”prÇE]­¨˜! ö>l8%‰ÿ“¡Û§?tôrIâÉ”ßÄc§•™·Á 8?EŽ­÷wÊ ¡vp W“KK]¸W~ݸüûñÿßó•©÷ -Eˆ³Ê]¤núØÃ¯ÁW¼ŸÃ‡©†ÔuÑVò+ç2ÎÕŸƒÉ©×y£ß[ß¿‡TÝ,œêA—Ï §m+Émóƒ@l5Y‰óW´[@øm·8ÓÕ7¹…!ç¡74’Êèİ,W–¡ËN §(Txô…ýÖÁ©^Pø£·t=h5‘¾Ç½ÂVXæ,N¤‘Š.A0ô’¤Í>ž°Gö1´æøTÞ'\:æ"7äÕÓºÎ[…ü]».(,Æqî Øq€_ðý yg‚2t9O-?TIeŸ²{qã˜+ðqAn=hÁb]Y­ÿ÷Vò׆½8w¿¿„wû3ñ‡j¯g{(çg|ï¯ßùµ^šÝíþ‰ *lfD ´e¼Â¬Øê|k)ŠÒ¤Š,çJ*žô; ¹räZ¿&L×AÉäÉLwi8lºÁå_]Ç>¯²¡–$jÏX6¦´÷í·$›ß|äŽOf/ïz†„-Í‘Æ ƒºôù›ÜEFy’À²Ç|€¿óÓ…V™$!:Ùê¾ñ6Ä_1ìéxEjyllKEÙ¢\LJ2÷÷ÂjœŸ3›œ:,Âvˆ\i°03ÂÔšÐ\Èv.„•’{A± y;?GÁÌ*!X³r}­-NÆË²û?÷ÃúʧðÞâ&t{O@¥˜AòÐ'œý™¼ ®”â±WF $jKTÕâñú- ô SÆ­KZùÃβlNÌX|?‘ŒœY€‡¡ò>„×Þko¸½ßKÀWÛ<ô„éóœ ¾ƒý~X´x<Æ‘zÔ Õ"`00Ï‘¥<$êñ'Pc¡+ÆÒ.øÒ¨a7ÃPrítÔ*QåŠÊfÀçãW@àó(&(Á‹ !-nLXð…œ2¬¿®Õ¼Ý~žäž¡%wÞ“]?åYRײÿ¶˜5§sÇ­±Ýá6¾=*ë/}åöOÜK˜fòŽOÆ¿JjP3ÿ3çºV7­<ˆeûöpÝh k¦ªÑëÃGù!7rÐúÐxÚ{t¹ÌÀ§gÑÓcxpáˆ/w­sùôç*ˆl“…üpx$+@û¿7ãt7ˆ9S…SIïkyúÞ¨‰Ó*C%µN âŒPVäÖ •âí äÛÝ×pëÍ_|`v,¦¬b¾9¸í@ÆmœNWnœDŸíLÁò¹+qË:3Ø×ŽŸ‰Âƒf-öT×FÄÞâ/ɱÜèTøÑy×,— ‹Â 1R†õj{Pm×aT}y”TNh—rCöëåvÝLÜ›À&8ÃýßÊvÜÊ7œeÀ„¤uðP¸ ;7¼õ7rÊuiáÙZl–›F Ô¯Bjužil#튻ˆºK/Ñq!W?Ýá:=èVN•¹M"!*¡ØQÄŸÔžŽâOÁä´?{5¾‹óÙËEžk€~™Ì@e.<:¡rŒ›:X 5=­¼Ÿ+ï’?EÀÍèoDIä^Ÿ€…sEc–íl®VoÐfƒ$ýĤ§UÁEý&þ&U|X°ªÇâ†ñxxßVÍFÚ’¡nÿNþ/ÑfÈ´ÛN;ÿAè¯l²4ã #Qáï¼25æX€C·#ôtæÒOàõG[ÈÃ{øÍ#ˆý<¢NK$,ñÖFÄŸ/TaÁÔ>nr¢ wóÛRß”EÎx;a§™“¹Ô S¼ô‰^Vø­e{ö)“ë ÄéòCy˜5< E¥ª¸!¸ŒÛ4Þ£eÍ_<¹‹‡:/uhÆLe*Ú [Õv£_Ûdzav™?e/Úy cú(k+ðf®Q[`ªP<Þ¶1eÃNß8O«›Àwô¦ºü Oâ¡mf.Æ=ùi¹ï±¿J„ † ²Íá7áõ¤itÜù ô=°˜í{ +ÿnƽ½4òj+ ˡ̥)4w~{ÞÕÉY—¸²3Â0±y'­ÝomÅ?aÑØƒ¸˜(ðLò7Òy¡æ\ÍÆû¸Ê;‡/Þò˜gñ3‡«V»ÇŸÊ»Fƒ3ѼÊù–7¸º"ìälîú96?ÑœíÚš·ÇäÂé?w!a¿ sO;ˆ+ÞŸÆÙ›’pž‚ÞøÛäÞख़Jt;±‚óJÁøtM÷ú­04ʽ…ú‡çaùN7±ñ¾-“¢MFÔç•ÏÍm~•S…Y–󯞋𠇘½ô¨ýiȰŽ^ÎÅÃÑw1u¯:[¾—èUÞ„=ŸêpÓÝ{x<~ Ú¼Tâz¯è±GBÃ0eª8K² "¾ÙHû‡áê›BÞëƒø±×¼—ãÅÖqt¡).úæˆ/SKQ0¤sçgp·õ1·þ$xg_Ä•–3É€:œÒë‡Òñ¦|ÓÅz´m±39%;ÆUÕñ¾mÐçŸÙg ‰'/Âô;Y¸|NÀÈ{®×ù ܟߎo÷ PƒK€<œ¡²û0où?χ;WÞµjáüOƒPpY€z&Ùq‡n‡ Õ‡À~¹=¬™»›‰LÒA7ú{N3F拲ã£uw~„JÃòsèþ[§Pîz?èI¼A×Ô3`;ëšÿ©Àù»!wöBòg±!ƒuiÁoq.³ô ÜjÚ‹J5ϱïF1¼ç ÍÅh„ž៞'WbpC!íÝr–« x@ÊätÙƒ¤NÎÆò#rª6tÿØItSÑ(^ÉbfÓÎ1o‹ é–M6¤{€ÃeKêÀ¯ÅK±2tÇÇ=¸3µƒŸ”2a¡1ÔÓc<Øþ—½äbꊡ×X„Ûìǃ9gO㑌P¬œôüçй÷'áÒ(qX`bŠVöÆh$Їf“Z+Û…¨,ˆáôÈÜÑ,ËÊLáþµä;_•n+ô`¡ëR@ô¨ ܾëÀô/¸Ó¾¶ úâ0™¾){ÿäc¬‡+{µ›l›A“ùg0Ü¿üR„û[“qqHÈ1œL$˜ê¿ÜÓÂ"¨ê@eÅ"N;F“Z]›ñŠ,tÌ V–.e* Øm.’‹Rj!êç qÍ<{\ékB 6¯Fþ±Y¬™£¯x,òÎÆáY JO ý…Wöµ8sÁL´tƒ˜QÌ+|§.KÀ¿#˜óæT>C®Y¡JÊ"ìz0D,+¸FÍŸkÉö¾¿†'“©‹bje uw/d];†EÕ«è´|O²í²'=W´“æ7òœNêQ¡û/yÇb¿CãÆzÜžù!VBÇ·ø•@×"û>ŠïçK™ú¯$ ౿µÇ0ñ’0æÁÓÖþ00"A"Âæ³º8mú÷ù Ì^Ìœ“¸‘ Ÿ9幬Ae/³Z}n²õ'UáRwv^æÚŽuá§8Mjšx„«;³”•Oj@cù‹¸eiêe¨Ó¿?ÅÙ[q{pTz…Ƕ˜²F“°íÝ"*¸h Ýô0ƒ,þ‡¸ëð[Pës'[‹ÿûD‘McÙPß+|\rýf.¡&Zó9íSn4úl¤ðä!2j)Š}ŽÂþ§ÿàëDg˜_^︿A’Õ~âqûáEiÂ”×øl±Û:o”›‹3Y;¸û¾×ù °|Ÿöd&Ï¥¢ME!Ó‹Üý÷/pqe*1¶QDgLÒd.v³µ›a»ü=l‰€¯¢X»LÄì{‰;ÅncÕ×7ÜÌù7ØÕ Ùp)d,[6¹ž¿Åk.1¶ÿ‹èšsÉ 1Y/õ/¤;Źò[‘ žÃ—©4dÅ7ÐzöËÙ±—ÛNr‡O*Òw»çU-¤!*É4ARZ›wÀoeÚ™×ä°½q3$':,Ãî%šS]ªæGÌsê1Ñf 5™Ü>å50ôý6–ñÈ0¯ÖìCo][ÔŽÓV_±™Û––A'gç¢Ë–ù(2!ÆÞÅUGwÐÒ¾— F½…0÷TNmëeLzŸSEhy«,Äÿª†{_žB®Òt¶h°?FÄÓÏxÜ—Ì4¯ ³M±Ç¹¦«QEæ(t‰<ø]ªI³oâßÊFø¡1ƒ§Œò`7V±´¼’ƒñóV/V»xÛ÷WŠ®zT‚ÞáÂÌÅf3MÐ[Èvï°bìé)TuyŽ•5©õ3m8g°Õ*–±G Ù,êŠ'Ò~éfŸ__lÔÕæìvXÔŒEÑ/  ¹QËaß„gX>IšŸÜN.o$IÓ`ÖÝM¸¸MǶK²i¥QÌæ“$½¨#ÈâDïa‚¦27:Î1¸Aƒ^—:ÎEù”€ÜýíØÙï dH˜ížÓÄu[ŸÛ¼^|¯nÚ»­Šä©r]ïÈÞ]jð¸x>¬?åÜj¡µˆzt×"×w ®+0¡o¸‹ÍèÙý³Áàn9<66@"°'+Îê ±²˜âzØÒ› .u»¼\‘ÍË_ 뺡1íÙr3¯F|…!í(ŒI¢±ûàñ%è÷—Çþ¤8ꓳ³ñ ‹æ]çÎÕZ`ùtX­ü tôT =¸”$Èý➘¦ÀÜØHú½ˆ»j|‘¤O@ý®$t¾2Ý¿½ãNÚ¹Bò {ÆiŒê˜î‡Æ’€¸4l<úŽÜX!ÁÃxËQÂ× ;.3ô ª‡{qFâ9h¦—D¶‡”óî‚E±çåmô¸”ÆtùÐ/uaqZ-ŸZWaÓçA8gÌͼ5†mø½œ® µ¥ç‚ ø¯Šop© aáh-:às—sR©…1ApsN$¾dÙÀ§Ò4ôåGrúÇ6¶v©Íž'ɪýÙ¤ÛÚôî«ùèr–HžOyVT>ÞêlàÍŸÿc!ý0õ¾OqÅ¿_=áÓö«¤ ˜A OšÎßÏ~YÑ¡uYhÏ¿ Ru¼Ot¨°• PN߈W0¿á?)²Ö•µa€,Gv Óˬ ËëÔèŠÃ%áïÆ}?sÊ!Âÿz‹j§yAkú•i÷¾ìö‡ž‘1x}}øˆã·–¨=§Ïò·“7Bf¸Zs€¿”mõ\Hìþ®Ã¦]Rìï*Mžxï[(Îó †|qÃûEñ}Ÿ*@È7IÎ…÷¼{&±Åúý8¦Q‘|·{ˆG ï€TèoX=8W¬_ÁµÖžƒb=iøE&BM¯Küú£Ú/o· †w†°™qø¾b­µ2Âö7 ¬’`¢îì»´6KòýÁ ÊŸfšÖÕ\qÿ.NdÏÑÿüÀ²Z”—}Ä7n?ï¾ÄAM‰-uú7V]ÏáH½‡/²©0­ßœ«:ãˆ[lh‰âL˜<èBãnµž<öZ‘l5e–gIxÛW\‡³íÕù+þ ÒS?;á j°ÝRþÜÒ¢š–ø€ÓÔ "…Äš½"wUmàZÉtî²r :“@='×`Žq-—ÚoD³¢_B«8@Ûõ÷Ü1ËL¾»Þ>øºc!ݲ`Ýûk#]XŽkØôôr8á§B ’fÐÒ8eš}_ •š8YÇóèû§q=œS¹$ƒsKr±pa÷=ý>.3“jt[ÀTüñf?— ù©fáñ`ÞK×v÷õ9R[¤În gHg>è·vp7*:H÷bWti2e{$˜§Ã’~®ŒžòL‚6“º'¹ý3VÒß]³ÙÕ=sà®S>^Õ†%Øœµ.ôàÖo p¾Ö ó76OÃâAô®×iØ¥+Í2î‚È_P%÷.³¡Bï8O‡Ï\¯¹ž£´è8:KF/pËXÜ|/º£ã1<_gÎòÍZÀS*‘¯ ¾Ð¹É‚¾ »~À– öjM*9N¦îAó±¼Ü¬C^í]š¸vx=ÌœŸÂÑ«D'K}˜Q–ϪžMB?šBïÏט~œó=¹–ãÙ^ãNï_Ó35ÁŸÛA2W+¢@w0f;+`ÃloNtòrܵטånè& WBGŒ9-jÌ´xLyI+^<=ƒÞúêÆns!gL)¾> 홪¬ßΞ;Þÿó³®êóáí8ª«PgÎÀŠâ$NÌ”{}X”Î?ŒI+«a´²™M7Ø6p×o§¡Jˆ ·^S…>{žóÎôC“å"}p ,º‰c×çr ·Ã©IÖ°y 3®M‹ÄÙä“ɨ#v‡TüËâÒ›bÐĵti‹ ­ssÝSÅð<¹ëÜ®aÙ„ãã&€cÒs(ßÙ‡}S£ÏD„7OÏr³B¢ñŒòOÔ‹ó€‹ã¶àîÝÒ4ªEºãܰ#â%ù²«šgÆßÍËn#Fcè¨ææ’ÀyvÌøL&{¶R›ê­<ç(Ý &­¢øëý TÓ÷Pf—ø†gÝ:Ê3÷îÁÛÛ4p—Ô2ÜòA¾_Hæf‰îECÛ@a‰m%À”¼æw/JÃûiÔc¥½ú{<îÔv ö­ÆÂé»1’ð€­}q4©ÅÑÿÚo;²Sâª${‡ Ø÷&B¥O7Û ,û=ç&Ho Áw×4Yâ¢-¿Ç‡üêv¡]¦`æXÖÓŠ·$$1ãÄ  E‡ÐkY>vã¼ízl±¥á­Ÿÿ>»@Ë4 |utšJëÁÛe,ô…=ŠÚU‚Ø’[è žSáÉ2EêwL€M ÁÝÖ‚`Sð§|Á…ªñܧ»5x@õ ¸-à. !uþ°â›Ìi†›‚õ°<ÏŠrs8€½Ü;ýFØsfþÔ‚¤IÄyú,ìó«³X‚[fÕBãÃu8xj&Xy=Ç™Ûúpæþ04rù‹ `°ä3¿V®DÌÏáC'\éÕ¢-«™N[3~SŒú>Ïx ú®ÂÓéÉTNj*S¹n{¼…ýj‘£ Ÿ©ÒÆßt¶ ½Î߯,?"œ™…:N{©P’J,+¦§ÿíaeÓ“¨H…åqp*’×1GgoCú€Œøtêýõ,•-{ Ü‹W°@ªrÍXîöX˜o^×å[éɽöìà=j¢±‘åŠNÆž¿àwgÉ…JòløCûuu;+ºmÏu®)µÍ×§‹̰ê¾!‹«Ýq'Dz‰<Ú`ô¾¬kã¿IŸÉæ‘J¸üÔÜÁ‘m\Y‡ä¼›Œ¥•'@JǤv¯až1t½ÆF2Å.”¼¢‘õÍŽŠ·âØÕ÷¡“&јóQxáyæì¾w¥"ØïÀÛDôeVj¦P…1gXÄ4yšžXŸ*C® "\-aëÿ5â‘jc–Ø—Ç^¥Ïà&¿ÏdÑc‚½NDKnf}Å*(ÿß™Õ)\ã!CŒk‰ÅÞXšü6ƒ[‘‰ÙEòìïP9„§;üÀiÛQ<5í-‹Æ#¹ÒìÕ†DXs\æç® !ƒ¶ÿB¢NøþjsxmbÉÂÂÕYÕº!nÏl!Vü­’8‰\æ.FðI_óWÔòe°ªd3oCÚF|Ár!Úñ1—”â}«"#mÒì…€[ÝY‚éS=~ìÊ¿$/]gÐ5zª¸¸DŒ[-áWÛ«¸¡üǰ>,ŠþšÆiô&̾ќHØÇïíÜÖ‰GéÒqj¨}Y–}Wû©ÎúH{ A.È뽩¥œÁ›­42äõhƒâ=|½G‡œÙ'Kg{]äDÔåY÷¯8={Â; SÏZÒw>ˆêªaÉÅMœL´>Ì}w,ð¢çSðPj8(³:îýžVnq_¦ø§`ûœg$aãHXœ·Ðl9yp+7ŸâÝŠy­Nb¬ÿwI]-ÉòÆý„¡"vµê0$]Žã—sè', ¿Ûn“ö*¶õ~4 -ë3ÛAþ‰=)HJ¥j3%XÇ3Æß²”'üã¬ìâ©&ʱ—igÐ:y=özŠë;Þ£D–˲]&Ã)sÐbË66eGïõÈÑ£9Þÿî¯LR‹_; ãÖÁO]º—¿“ÞÙ²‰б)•øòG1Q¸hŠëŠ9Ë{0>â7j›ûÓÓÉ÷;›Áé\q¢¯¢sx¦ÂÍ\‡ÅõìO«hÔ-Ôª † «Á⊠›á1†üMâ~›ñhÛ§|ŒßwˆðVX1›çÍБ$Ížù9²ží Î#WÏ©#4Ã)šž0á¼;N`òÀ[Ð)Ò`Ñïs¸y®Î´=D‹=üºˆ;÷ÁƒqæX(ÆR½ÖÑ}ušÔ2• ûÙa¬w?¶Ì©à=æèªá@ø[j@v\:ƒížAûk7úw”ãKKÃã‚ÇÙ¥V¸ºF©4ë±5žãéÊÆ^¨·£<‰ö' {Æœû>ÿ9~ξ ô¾‡ `†8]ÐËì%ƒèNÍPH‹\IïÇa¡q2ìî¢TÆÏ…-9¾ˆ¶ªž†í»pß ÚK÷ç7Ž>M¸FŸîzJlü;À_ß¾8ÃU_yÏßo§¥â˜P„;uR5FpMÚòÿ´ÊÄü±Oö`vݳñW¹ûr㠼Ύp|ql„÷Àc?µÛò, ¨t«>ÝðF—¥È~àJ- 1pm0“æÏƒ„¸HÌÛ¶˜A’ÎTŠã§É#äÝÀNÈéiÀŠÚ“psÌRNemk a¦B<‘Eóó§¸nÿa€çŽ›ò¯Þ”›B,"Â…îOód^ØÍ ©tlŠÚËîý’£ë÷§Á—IJtFX nûü§>‚X8¬žö2ÏRÕÈõ`öÒ%T¡µS'%œ¼Ç˜°Õ‚ýøbF繊°=±f(Ò†…ðgÚe8üó(FÐ]]™¼Ý«\ÀÅÖ ×oZÍmúµ Å. ÐĹó`ÇC'Öàp öG`OÔk¬øS„Îñs|“ˆR4sô$‹ýLhäûnµÎfÆŒ¢­u3œŒòd&K\HÞÄz1@’‰ÚFƒŸÕœÙúŠ_?g)÷ÂÛ™>8&¶šIð+¿ 'Í¥¡90â×Çà÷CPû4ˆþÎQåÙ<Φ\ª|̆|—ƒ¸ð‡û×(EIõÞQL¶Ÿä¹WÂÆ•™ÜìŸ@&kšNÞIÍ?ƒw§Ç˜ÐZW1¶nöa¸¢ÏhÀÍkmèû¢x¼÷ä .ÈçÏÂé„_~Ìüë0¬¹@j Ý…Çx7(ª\/sû½Ïp-J—QLìÌ2”`‹#‹ð~ÑKÉ2bå÷0µsƒ$hë)þÜ•ý¸ÒÒ™wLSòžÆá•^Wþw «^‘7¿póØI•§¤êZŽ{œÂdïAË4³+—„£áÁt´®‰F9­¨¼ø.껂­e ¸uÈPOÅïà61cw?ã/ JÕÏ1´>,Ç4¹4‡°V%¢d–…®|„ä®SðÁJ”.M6f«Âö²¤“¹èuSŠÛs7^Â%,¨ñÆóÉToÝÎVâéè;3¢wøm0b# n)/aÑ/z|üv|Pc‹í6¢Ä´'Øõæ(y^¾”ÅÇò¯WåÂâE²Ì¼Õ޽ö9 ²Û]ðÊÐyÔÅ]`™’KÆ*³ ¡ Öm Öc‚K*aÔèË2ÝTô^3†•™zÀ§¹²¬d×Uˆo¾éZtH}6¤‹^EÕ˜I·p™zÉpåÜjT0«ÀŸ_ªH@÷>8&ÍN<´»;ª¸Ai9îûpllä ´ÎCÄzc¬ç1§'¡âÂx“½Ï߯åÁ„&è±Ó§a¹ÃT­vؤÈRßE”L¯’´twͲÝšÛs¨ôP ×›œÏMû¾wm¢åÞñ¢>e€lÝØÖø?L;€ c ²ÿ=Ú{]ƒw×Þ`$?™½*r¢œ@ Ïöc ÅU> {-œQçE?_qM·Ua;H3É (2c‡>=ÿw?gôÙ”ÞÌÇéÞYÊE* ÿþ”îO¦ò m à @Ûâ?}3l™  •s¥yŽùcöà,{–¥ÏáêZcÚ©¼é A8UÞÙ)¢tt ½Q¦:/V’&…|L‚µãáÏÓƒ$áe6ÄÜÑ¢³¦JÑCw‘8aeÖÛo‡—õôèù%ó`iƒ'MŸµŽøø)°)ž±œÍ Ñÿ4Ì,Î8™sN|ÎüOé±ÊÍžuÜÕ ùÜX2‹éR 6ärž;£ÀíÑ?.On5{ÝXÁM¹¼—LìãÎYNg}╼™ïM¨‚(ÛyT~´Ô£„h–aëë3HßÇßT®ÊT\ßÁÑ«éó›$$ZN*‹Žò§0´9ñƒ½”IuÉ$(M, V*QxgÂæÕfÉq·‚YîŽT4;˜úØh²ìý2ôÔo#ÖqÍžrŸÄq1}‚SÆÀ?“£Ð)¨@ÿ–é°Bƒx4¦7÷j!yìÄ%NuÅ}†§1ìý˜4k; ü ä.?ZcÿHÚgnDáÞTTI%¦û”1¦É½ßmÓêøf5»È»R\bK.Ùå3ü³!óÓ/ÌÜÈvW˜Âz…8Uù8¸‹„ßB*\5»­Æ³éŒ™q$y@–Î>ÎE.<ö#¨“ιs±}ë1ºF·ó#ITœÉÙ™BI™>/ûA OÓ[®L€›1ÒÄÄڽܬƒG´=ÀþS3üwVû©ú$jÉÔÃïˆþ½#G¤ªQæŽ$õs¬£On¨SÃSr”&û1²÷<ÃÎ+Áü/V8G‰ãüm׳#ư)S·»^ÓæŒ’^Òl]b0³™àÍuú€¢ž ¨/@ËŠht·¯Uæì$lôØ…ŸVVƒ—%8¦çÇK÷Äg'gáÛDèk~V7:$UÇçÚZlRI8÷8o„»21œ3C$¦—,é¤5v£av=ä5¦ss¥%Ð=щ;Û”ƒ *ÈçäÄ ÐÖ΀–4gþà šßÛŽÿ |4i¡èxf2WÖ²v¤®·óUóJpê5Ür!ýÒöpªÑpÛÀ 'y 2‹Jkªa}€|Ÿóãzì°Àõ0–N8Žª£qÍ­]K¦¥¼Æ¥¯~AðB»îv“;)R¬î»v-U#åé¡[ò”ß—Î?½Ï‡Eà1ÑT\rü%y/¶„^¸±.õËе¼ŽtÇ,ã|hiR@_'§ƒ—ÒÌpÇü+—‰Ñ&Y:òötOýŒ¦®¡0ªðãáY;ö~‚;Ø,Æ]>©@ÆHqßEìé-Ý?d߯k²¡k´¹³i£Ü?3,Fì!àF:ì4‰Áwf¸ÝÜívƒbÍ<´ß#›Ý¿£æ7K<¯”Ìß#òÍô‚úÝ‘»óUt©o‹ l¨Z‡÷­¿UXçÃá ýWÜJsA,™ ì^íx,[/ÍüÔ²!RQ‡nrËæµÆƒÙœˆ\çÒº9,×"Þ ï½ÍB—ýÏ éÄ_¼Õ¾–ê°Œê: 5¼@Ö‰ÞG½Í“ÑÖ, ·(&Á÷[g¸ƒÚ7ˆ…Ñ*GÌŠ#zïœQ»uºjÄ–íp¾ò2/!Æl$à“ ˆeÞÄ¥ÿÊQw£swƒI#‰Üßà1TE@"O¥½Í'QÃ¥†ºn Ì¦@ö3+£Õ³¥aËsnIˆ*\X> ßz=Ÿšh”6‹T‡Ï)G•ÙàÞz\´£ûÀí]qø¦j1 òâ>•¿"òPù3•MÝàT€ÿäRyh'¸ú€Î£#üÙóæƒ{äØüg <õ€·ÑÚßðÚ>æ‚CÓ!¥WŠg’3êó'“Aœ®1 bÿK¼;!Ö‘(jÍ'{vf€õóý§6uô}éâJ(tz„ëÜŽ“—³iĶ‚^·áýI?例[,y‚wÖS.ãê¤QL¥–·Ýáû[C¶fd.{ÿD®£.û/á„‚.àž]1ý¹Ä‰q¬V½s’ k³°Éf>HõeK8IV?í9ßëM,<¨5Å=N×Á[ð·¿ò)[uwe&bÖ™ëdÉ!}x,ÔHú“ÿ8ê:-ƒfÝ]ôÙ­$þƒº1$-•¸~A-ò6*Áët4˜nJ§uÞæôNÚa‡)‡õ^séïŽt¢¼x/,»DúÅÒ'“pû\Ç©Cå(â!@7ʽÃmóàïâ;,†Åϧá8]onÃ& ºyjÙ™éÏívS¤•©MpÚ„Göbá’ ¯Ð‰‹ç<†˜4NiÏw~œÕzÖÜùQJμ1 ºö)äÐGs¼|I’ž;HBƒ”Á¨.ž3YµNmv€'NPâŒz×øøey#VfG‘†7ÄZh\™n™OøÜ¡DSPmU†Ûã¦ÓêS–ÿr5* Þ†JceÐ3Ög3¾É½}}É~`Ë“±jMÄÁ¼Ò]çmã¾Þ8U)¯–cç¹:ÞIÃf.z]:n_8'ÏŽÆò—ó1xüOÍ,†¥ý©à™5žºþ¹Hò ƒñ“Ók²üМ¸À –ü9Œ!j+ ìñDVyw"}èÀâ›`kQ Š«º°çÕ9~´nÕŸsØhá—wèh ÿ=„>õލøø6”úÑð?BŒ¿þ¾Èõ¦_u+Á(M6ÕâL[.ám÷yÜ£öR^ùþï˜}~„[2M‹êFªÐŸÞ‚ÁDèÇ÷hƒ[§÷ÀÝL{JšBh­ÒXÜáÓƒ©#kÁ‘Ma+.D¡ÜϨ¶ÑŽ7i²¨æW˜ý:µüç¯pÏw8²e5×Ì@݆øYoy6u]]߀»§ÑüžYtEhgp ‚#vÒK³°ëí–>ÏŽóӢ—O@›Ð|Vór ;º==4YÁÔCøèÃx87‡îØR†î£¹©§E+íØÅ¢vÈ|õt_(±¦­’ LÞ¹#葯ÏFçÉõú‹°¶³:Ô A€†V¢ªº;æƒmÚšÜúGš,·ßœÓ}oÍÔûpݦóDÔÛD}–‘Ôž<Ø3#d¤q[o«³[G¬P<[€Í¨Æ P\ BèÁì/WZŽzŽàHâFöì°ÊË}†-̆gùy+,è© Þxcà±H,½†o¥²pþòBp¶ÍÉF\ƒ©Þ%qáxTç98>>O*4ŸÃžæ‹Äª±Ïqáy!Z0Ëþ÷1n© ±mô·Z+Iû®ä®|)ç„*np‡Þ4“÷C§pJG4ªç{'i²_ÿqz ÇÒ{IÁ×§‰÷¼»–xŒ/ĕɜ輜ù¢”;Iæ’Ã×ø ÙÉàcê8ô;5³Dưñ— 0fÍ® ª‰W÷ä4nš³'¶ òÛ„Ù̈Zí^ýìJ¨ T„¤5q¬&FÓz³¦ß½>^øùä>’¶´®dL¡+ùd&[V À?ß0ˆ/^ìóèç ª’2TÊ^*mÍ-§óž–÷q—xw ·`UäÐf¦Û´¡…óÿÎ]m›Qäâeðmâ¶_ú ñüÏ`(þN„ÚÑÆßfäÜÏc°"ÿY™ô˜{S|L¿yaþµ R¬“Œ ; .´ Bއskg’FË3ü¶EgßãÅú©©ÝBRñæ4W Ö¢‡Î'EÅ2|´P—â© lÌœ<þ†òIf°Þy½ŸÿŸ^Û4ö4È— °•=:­ßäÇãwû—h|å!IZ¡Æ–…M€oT„l5،‰…˜š(FgßœÃûò+̾$ VÂ>zàNÄ´Ãå»+8›·q][<ìø~Y~ôýz~ëi¾®,…ýX(¯ÇºGyêÌ-z¸oâ4Y=]LÑU½’ãý°Í.…OrlŸ;ÈéŽp#ÇŸÀèxxŸü“R´ L›²;~“Ð̯|eÙNy%ÈìØËfŸ8 ŸÄ!âÙ’Ðy«…‚p¢M±êñ¡?…þAŽê|ø°Š¿çã¶ÎN9Áƒ»Npñ¼%¬¦Ç‘æ½t§Ã"!ÜÕO‹ÈѯF(µÐ‰5ûþ ¯›ƒßÉ:¸˜ÇpÓÛ;äÙ k¯s¸l˜{V rþÉŽ8k÷s(êÉá†>JÂÎ{ËùÆGÙ:ÛàQ,¨B/D«ãƒ#ßÄÝïI§ÈÔ#EÐhÍÞZ 3—Ë¿Èó\ôúÎù“ñ¼-_zˆïÑPu%+FrSË‹q2ÿ¼ª»Á¥¶Ì$z—ëíÁéÞŽÌd d¤Ë2Þ­ýüÀÛÜä |rk ç9O„Ùj–sùË3ZW,þš2h“&ýO“ ªn#&k—ò4Fve_cTŒº3ßÚÃ@8Û$\„ bíÎ)ó `Xæ¨@É:R8M›7ýë.7c`;n*^=†¹\IƧì[ÛVj’én“IíÐ’Û« !?Cü§À÷ÝC&† ñþÇÑ•GcõµQ"dÊ<Q …2V¼ç9i@Ri¢™D*Í…F2UƨÈYHÉïyNi46¨4!M*¥I)êóûþz×½ë=Ï=çÞsŸ½÷Z{í[ù£ž]ƒäp¾ÚAŽM>.J_F¡+]…BÓÄöáü>´:ó޽Jº‰OÎuÀÄÝ/IÁ÷$” ˜Ë•§l††C›„OΟøÝúˆš¯÷@û/ž'Ó Gœð¯”&ž2kÙ?ô°c;ç’ß!ediÒ%ÔZt wò¤ŽøI93ÉÑÌ©^¸ÖF ¾©ð®óøå‡zqÅò>̘_˜¿“vŽ}kšGÓGÕOÈ¿YÄÇD‰K+&’7-—0W1žÝ÷l&ÛT²òçÞ¨õx µX¤ »^à¯S¹¨Úôm-õè)!Yéôü;¨ÑçêÝ?”þ8)FœÆ¾Œ]H¯U ¿Ðýûª`mÑU¶5°uçgò ÍÇP|ºÕ³œóSü¦:–vüÚK'4Uw+¢æCúê®<Ý¢`Á¿ßÍúpßÝxú\ç>O^«QÔe$›©C°Î¿7á:ë09©Ï]fÅáÄ‚Áòß8{¿ni(Çgf·HƒÄ %?Ž£^‘ڼ䩟8ý3é[,‡£ ,©EK0<¥ñ1¤< Bäˆu•J…J òŃ`6ÿΟ·rßT² ΋n¹ýyœ<ÖFx'3‹_­ÞŠî/ñí¾¢rD þ>Ü–Ö@ë´^l€a|ƒA-”©ëš‹¨Ý„/–eØ/7{…bSø†£ñÑUÁ‹žiÐsª .°¶—›àÑNS¾é‰ù´[ØÌH\ãU.P^²ˆfzéAÔ[È;6™Ó“¯¥©Þ°úé5PÁÖ˜«Ó ëö«Sá®›¸»Ã”¤Îbw.ë:Jù¼Bí[qš}”¤»³Þî¯FêÑÂ$]°àw9c«öŸçÍͲr™ªþ7àIÀüUá²{*Xûî¹P°¼¶ì‰âëN£‹¸æ'O0Äѱk†RÓ hñ]\óõ1Ì~g Ò$v„|g©sÑÌ0 ÕÃKPíÄ0êé‹·á†'füM¯#;¾ut>À¤küU_ßñ%I¶v·¿ÃâÒ%¸'·ÙÔ_„ëÊš|÷•¸¢~ƒp¶}=¶ú#øöF`È”hÖ~6?І¥ìMðpŽzre.¹6 #éÖÑšóR‘Œs öæ àÕ„bÍÚwŸáó‰CaÒVivϤÚœƒUN m•céϽߡICœ~ÉÖ¥C›³ ŸÚY¿w ÑØ»Õ%Nƒâ”!ô¢Ô_&3í-$ÛU“^Y;ì×8¢¹kðŠú$z<©þ¯×³h-(äY ­Æ¼ÅÍoÑVµ‰¤a4sЄÞO f ŸèO(=1ž†í”æ/@¹ž'LeãOL‘)…¿åÆ^ +^‘áSFcaÀ1öbÓ%<¸ýµ·ý‚Acùë“ôÀ×Tt´ÚOe¾: ÞϤ3äoüÁx™ 0ÈéÆ'… ûÚëgꬅÄUZ£°·Ý­ÁQm+ÜÄç¼û„.ÅùøÇzˆàÛ 1~WãÈe[Ò#!¸iÛg¬ß ”>#\Ÿsi-ÞÕ–… ‚x®ê±Š¾z!MŽÈzwñXšþ«£ãã™¶l2´ý™ª(yIš÷úFì߮毄ɬ”Cäà»>BarRa¹°–|4u‚´]aÞ,sUô{ßÅ…Ýæ<=fí_3”Ç©Ìà.ɃEŽ,-~ªð^Fþú’LúŸ&ÔhJ^F5›ƒõö¡ý£=üwk<õº'ÿ…À/«,›6oÙ¬DÍÍ,PmR(ù࿚Ö~ €sóç²è Þ Æ*¨×v¾{áZµMŸ›„Í#†.ð¸|.LÜâ‡Ö6ÉV6æ;^=‹Ií¶¦Ú0žO ûÃCï‚§ßiFç åšr*ôV»9÷-“çuIGÑËÿ¹oP®W¿`¥ƒ2Ý›ú—]Qä?5kqÑÔDXp"wìg2w‚¨¤¤ |hñ’­{a]ó¼ªàÊ-L¢pÈÞ‹úD *˜ÅJ1{û1bIôeÛ&8˜ £¹ ¤-&,îêû.”C§_,O0Ò¥;OšÓÑ88+³^c{o5Mþ®ltƒí× SA“Ïu©Å6üyB œ·Ž>÷f¢oF£ÐÇ”¦f³tàB€/n²äFsüiUw7=N•´C×>ŠÓ±éxÈ“âÈ !pnäñA¨EÃç•£ZëTáí}Ft­SåªUà1á6û#Ç¿,‘EïÞ´ ¶æ^«d¹æZЗŒ¿Ûð‘SÐ7h—÷º¨t¾ÄODz6-t«nœ™ºmC©K«$æ…äÛPh 0£~g¥©’/¿ý³üÚÂqUþP:»£‹MîyF’ÿ|F§úࢹÇ=o)®4q,)¼çׇ`Þeä‹×o6ü–®ý1ŽžrŠ/´m©Òú)ÐêcßegÊ“6ÇÂÃ7ê2;þñÏÆ„ È. F’9ß Èô|à×ÓàXþ*¦%LgEIÃÀaƒ Ý'2šæx–Ãx=‚rÑt/â¤QE¸aD<þ’W¥æñkhõîè]²Çnã“u:È,‹/èòZÅ`ã<%Ü›¸‡…Þn@ÏueèUqŽ;N¾4‰VØŒ=Ú#y×µøqå=[­¶<ï*úmóxæ¿Üxi"ô,Ú"­¦˜!+JözÂó¸;A›V© [&àÊO‰yµ÷ºøg áÆ5Ç`v€ =evž%ô_=7¿’£—xm·2—›ÜÃÒzà\Q+ñ¼ó öœ§ÅCpöÝ‹¨“þÞ>±fïr—’7‘é`ëa‚[ŠÒ®€fRúM‘&bžOÆóí[¯ƒËßѸ}euo¦Sõí)¨3}=ªYŠnYÌZ®‡•y·±þ¹ß±AÜŽ]ü£ïßÄXrÈ·P…›—pÿ{œ¤ tŽT6TT¦+nkѨðG0G°e23 ,-“åHñü en,žC·t’3Fÿâﬡó–¥úóèWñ ÕßË›_°jýxz{7®¿<ŽÏ ÚL;;¿Ãà|„gWY“ëåWÀ䎦:dÇ'LÈëÜdÌÞŒ <Ÿpý¼mÇrt÷á»ÛáßÌí¡ñ=)ÂíÊ#V>Åð>è.ìø% sšsó™nT"ç[p‡ÐÌ/zp ¤u¿b]ëY,Øð–¹ýq‚®DkúFü^ÍâNon9ζì‡ÑÛÞ‹Ù}(Þ²³BEiíð(S‰‹`N×SÔØ­ŠÉG÷Že%o4 Ü{@˜á3Ë.ù‚AÍAºgÿ„ vVfï¹½O{k=0{in)X"”©§êjŽ·áwÂíØ@ò:Þ|/·Óûøçr;tHÁöPe~}u2;3Z—û%Lç›FÒž ©ÐÖš",úƒÉk߀ùêÅ8U%Åä&ã‚=Ÿõl ®¾4‚<êØF÷NøkÍÕÍ(…Ù*"Üãë|z~ù}¶!-\¨ò˜]rErÞÚAoË Œ&Í,Òúþõ_F/÷ˆÐ†¦üy½-¾_çLñ­<^ó%|÷ VÝ4™˜m»Of°%Õ Î¶½ÀŒ¿ÝpÈÓ€Ÿ÷H‚“w­aõ*qúUͨZ{®õ¶2å]‚D|ùð4.)‚nMqxe -H:z »ß‡CŸC;ž_ñVxi§%VŪâëm*üXËt*õ¦¥ê¶  iÀ#'å¡ÿÇ´#ËÃoÜóË·JzÁfMËdþž¬q!uÌmÏwÈ«Aÿý¡ôMøbôº‘o/-ä:›&r9Ü=>‚è{…^ÂVæqÙá³Âhh»ÔÊÞ›EsÕ5Y¸Ú¾n¿J—Žöô]” ”™›ææç˜öV8¿6Z!D0±Y–ŽN¼R;¸iõ3¸}°C03‘¶žSh™`@¯^|ƒ$( EHÈä T-ìFîÐôõ ðL]3>¾\C=í’é̼þž=u¯8Šæï>À­tQ:qÃMvu¤$÷ŽïÇÃ|!²qݤg°XjfØ>É…“¶c!ðå,Ø}Z–¾&oG~€9kš±ÙgŒt¡ùïmñ’x&ºªb‡ ¶q{¥- üZV;þÁçÙ-0Oë2, ©…#û핌wWob=òà÷‹T|øîY{%¼fý;ò¥Å¼Æ),ºRdaÓ 9<©d*¬Zµ€•$äÔè•¿!)9ðQbœËxŒE'Á•§~°õ°>^ò©˜Þškÿ’f §«fñ[•ȇÐx¥¬Êa8În£a+÷@ötÊ-L¹ïP'þAV„ÿl¼CÊÂñcúm \9†ì¯¯eÅ'‡ ÷Ï3…L‹è0Ñ1¾oðá8oø«±@˜%uZòìp`í4ÚðÆ”W ;Á4ã'Œ"A\^ÅýŸ-hüäñ¼cèZØö÷4óöªÅ Y£¨Cf8‹ªa‡ÎÆ€0—CçÎý¬Ìj$ÿê4Ž6}hÉ}ÿˆçê<áü7ƒù ý¿ïxq¼-š¹õ‘"©18zûظ=˜ÿ÷ßõ_Õ¤ƒy2FüÛßHë˜Îsk%˜ýº(ÓªfWC:…zûõéíá¾ô©Ä›º§`£ãDZ)ÞŸ@1¾XLµ¡¿¿_Åz€ò!Œ ©ƒeÊGqŠš\\T‰Æõæ,>åä;µ )áž!UØÜꉽCËpöŠht;å Ç‚×S?† cÅøíÜÂY§KðYÇ,>A¢ }oicßÒ¨õB„'yˆp™ü㋊ Æ] ©vü~p¯%j]ª#j›,àõ0/¼Ý ‰žÉ¡Y3ïæ±¬‚“8mäWök¤¿8ó&ž:ãoÜÃ]*4h›2ڌƉatÕA3á-è5#¯¬ö$§½dØÈ`í_¤ñð „ß;ÔÈ.<‰+¦_"&ßáó£a¨.ë&p߃3n:£]j ù2þ¾ðº -–?aüËLîyÄÈ?r(ÿ*¾ý…~!O솰ÉÂé(öw1÷ |Eö7Èœ¨$²oÔl|ö¾ôYóç%ÙžÀmöÒkÏÖ,^F :ÐôÓ~núé(·¼v.ßIÒ?îø|›ÂÏi}@ÿ—ɼYÔžËZšâ„…‹Ð-õZŠt“ñÊt¼'߬GN·çð¥wÕ<°e·°ûw,ÔŸ Å9Ú6”ÚÙÃ9el' 7'Ú°®¢Ëðoç8Þ.©ËÄwèñ‹Ï”aî¡¡|»ýôù¡Ã/Ê bòåº9󞑌 ¯ëLóì]LK|„ÕI‚;~©nj÷¬rPfQ6ü»M³?æóè—M(öæ9l’ؾšpQÓÚŒøÀü*z‡9œ=LÌå‡Ð+ªK@zhÍ=‡÷@uêŒäé}èØfA3k2á¾Ô<ò­0ÚMäÙÌhÄÂS‹àRÆ.¶%¯Ê[ÓLJDP¥'Eøú‘€n»~7ö)Ã6A,‹…Ó‹íETT4£k{ѧZ…~Ö> eãœñöå °ûNÒ>ñ—4v³4/ Na~•SéB1¸ìA£Ô9ù1n7MÙ)Ê'NŒA×ûG)¼ñ€Ž©a\ödüŽ9†÷<)lŒp_ÀO¥[¢åé£Ðç?Žß±Qä“ÿóß1*SKùò«1´øSÓÞ¢N…u O/iݲŒŽ– gú½+X¥öa mC§ýbWû•tZK|¬«1úÚgòiK ªˆdÃóUÃxkA©RzæéøuR<†yëÑ¥i–èó©øM‡S¯2a†Å¾2~÷9õ¶M‚.Â<Ý æFt‡²Œš%U³~L(ÜÐYE£N>cºOô…®…’2³`Òyï=kØxŽÞ/ë'ÑK0J¬ Ô=ßòR&ÕׄŠGXü³ÛÈ-Ž1™D z®ÙâôP|Yðùôw,¬q<¿Ý‘§¿êsÓw÷ó`F¢=5™åm„Ïwl$˜Qê+6o¨l_Ó'8)jÇ3=ƒaõi^¼¶hØbøå¸Å°••k®²ôИ7³z‡äOœöܘ”x÷šíh©>†vÞû)Ì=T‡ÞZ÷ðLóD`%ztòs.”ZN]CÌá»ÐœzäVÛ?¯Å£û¿@cÊib&ƒ67´àÉÆPúãì3°K³¢!÷ÑGI•ׇÿ±k<®#”¸'OwßУmñ¤ß±ž½R\„e!_Yü÷¥4ÝU’ïK’¦å:j¬Ñ/Ìj¹Ú÷ÖÒÊÓn?/ž* ì³7ÙmË:|¸ÉûqŒÎžßϧ±×tùû5wÁqÌ)xõÂpþLú[¹ËÚa¨‹íé; ñ|ò¨i|N ¡±mëøÇÁ^!>—{ì„fÅÐ\×÷¨€œ[<ýY@føà(ÓýTÚ|ý÷Âbª¾Â9Ï]Û{qšS#xÎ¥:sø¿.ˆ2˜Lv{¢e'œÉÓ/Q¨¤tžúž<.£$iGG}g;„›­šÏ[­÷À.·×ôÏ"6yÐ&¿ÙTI°“Š˜A³ê0.6¶¶Þî!kKÀ#¹©ðEÖÕœÊW_‘¡¾^¢öæ wȺ…êôÞ{ 8ßznOG÷6læ]C³ù“ÿ¾éåDnÊãÛW+©q¼î´Œ)aà·˜VÎ= µ®òøÃ3mÜýéÛ£z¤Zÿ>À[3ÈžxZ„|D×_sù†9>Üi­¥ÓÏáÐ' nÑ>êž2ß&âÈóŽ*ð±Jyü—…]ILùàFoÖ¶Ú{|ÜÀ3̧ÓsŠ<ÏÚ›Nß±‹Ïhʆ›Û»©¡©Ïá™É&ØË¦Óvã:L“³ÁØ£éÈüÍü“³ ÖLndËT U=‹âg–ƒIt5Œ²æ¦ëÍÈD×(’`¡Ì‡\ÎÃÇu‰ÂCïgòߺrX–¯Éo)¼fÓƒtЯIˆ:;$ IÙŒ/³"­‡4qÛà ðtw¤PÏŠToú ¿·]úÈþ÷-œpïîGã÷XóÑ bzÛ\^ŸaÂ]ã¨|<—UYøà¾7¶ö%òR¹è:8‘p>ûÊ[ðnÆw0¹uÞmëŽ.'r%ñ´à¥˜)½n,ÂÍ Ë ö÷(”[1™ß7ž„ °‘´‰»Ú+dî…í¦ïj´goaŸõé½Q:4t` „KcÔy_,§÷0dÛt¹)Ç67À…÷…|øÑ&—Ã4]_“ëØµü ¹Ó‰÷+?áÒ8!¨ÏK‚vãdSÅ%‡Q]}¿xø8¤—Jó:«!|­V(ÝÚŠQ×l¹ÜÖézžuéoåÒs‡ð§»j`­–.Ýû,›œw1@7k¶û»“ÝE«L0Ëv¥ó>(ñ‰µ-¨œ^À{¿devv|Ê© ²ã,×™.º|3Í0  ëìÆâ £ANP„[ÖàNl ¿xÚc^3X B“c£ˆs3Ê‹^‡›ž=¸àæ^þr—]?Â…¯ô’¢ Rãq§t dÞ3™´ô¦ðÉÜZþ{L!Ù¾"‘ÏbÉOw}‡ã§4à¸òKøó'ž4¯´ä;¾dÑ5õKÈí6´³©•%/ú Â® ÝÍ×ûâ‡Ã$ÏE^¸¥—ÒÝú†Äiiß.+Emã[ÎzóÜ“S˜¿œm©ÚÃÿµypÉ©’ƒøãÏSÂÕP5Fͼ/±â¿q··½Ó¨M‡ßM]\¶ÀìM ÿl5»¤à^ÓKèw°†Æ•óI÷S×ׄk«‚©Øu1~<^»ï9‚ý1‡JñƒÐ c‹ó¡mŸ:ýšŸ´ó8ÜzÉ”” è¾';ðë—,–ôR‚zhs¥:{ºw¥$]r°}÷%Sn؇Ìy ÕƒšþqÂQÁ‹Æ_ˆGœ!ã¼5è^›NÃ4áîòpê1ù8{×áÉN~tä&âzÅ–ìØeL3/=Ñ,E:šÂGÉåàóÛKè"Ù£¸ñ×9¸^áÌÓûþ²±*’ÔÏÓ Wì¿ÃU©W7Øô\€íQ¸¸î)ÎN#1ªÑüÝìkÔ´Vç\§]KA\%Œ™™ ¨Ó¨7³xæ&Áñã…‘Ù®dTþ{œ0ATðRſۑŒê]°#1$}¾Ašî›;Ö§>¸ÅÄ’rYž½ìo‡]o Ê 5Yw•ºz +oý\^߃="Ë!ÁLVðàÒP>jÄ &?K‚µt aÉ,›°ÇN‰…óÕñï×H‰Ñã’Ÿ‰jÎÑÃ6æJmò„ŒFNá3’V –ér2X ³ØÓ¬sÓ0éÚI†G,iìû;˜¯÷vFf;‡`›i?. …­º‹Ð=ïÑ™ñWòË ö¿Ø7B°øk¬ý­JŽ5Î_n„7fáŸcŸà¿5G݃Կmà3Ì—Xð›ïÁ|ÅDŸ*¾kWÀR¥·à”ðu+?¸óòø5Ííß[®€¬§.hähs-ƒåàxv﯂ê )ü$|Yi𮪚¥@SA–o†{`Kïàß°øPËŒ,ÇâØû Rp³´_I–ÀvEO:ê…ë…0õ êzض°*áš’s`¥M ²þ›®ÈšÍ5þ)ÂŽ#Ñä?ïõ–®ƒø6ù#»1c ÿ0b!íû Ib7ÑgAnRü§»¬øÙÓg»FÒQ:tÉÖ£¼X5žè'•îÇ…°ü¬íU›Hü FÙlNâ(?«Íïañ3Éëïöp#õØT<%!×È„™‡qï¹~ò2(œ *an‘/µŸꞀé_~â§Ñi¸;gÍÊsÄ0ÔF‚oúu–¯Ä…“$°¨e,LnZ&3™Rñp>Æ1ó](þWçhw±YõN·•²³yâÓÙãg檭‹€<û‡Eçâè¹ÄÅð{²ÕzjBÖ|· #Sš`§îbÑQ…™ t»Ña¸Éqœ·Ê Çäiòk2c¨ƒ˜ ·“W wHRg“lÐè°ÄCð®åøbÊ`oÒý{VðA!/îÕEäv÷ ”>r;æçw¾Wåw†Ð%‘·¡<æ8´ȩgm*™ÊìóA$1 ÆlÇ¥{ð–¹âV¶bœi½UR*OýžrXíóO4;Ò QÄnÙž=•[ÔBìZÞ®0LPpq/Ž|pmš¥±zè 2ÒÕ”7}´ÆF“zx¥9Þú5‡æ:‚,1£Õã–¥¡§Ë¼ )ó¨¢ñä9¬ãÙUx¾.’rÐød%ÖáËŽáøýóQà½Ú”ßΆۊœ6ƒª$€Éë…,?jŒ‹N&e éXD7|·ÃSfE8Lö%Ùtu ÞŽĤއÞŠ¿8 —³Ÿ±u©"pk,žÏφëK²Yù£ ˜vÜ"/ü†lí™ünj¼¿ô›ÁöCàM¦éÆ“¸Öüñ`«†&Žßqïü©GÁ¾êâ]øU0OŽл"‰µg¿ ‚LO¡¿ªx(®¤X8ý=ûTv¼ ážÏ1¢ ŸXîÀäÝ‹ð©E,Ü.õ—^ ‹6ˆ ÆµUtBR'FÞÇt19dÝzh,²j‚£`¿¤ïßú™„ —puÝÜdþ]9Có!ò08Ä+LçÀ‰Îúñ’&¯CÉWÁ0µÏO…ø†GdùŽ\Ӯϛ۷ã¾Ò¹\'Wó}¥¸úØ?Ž[f¶áݽcx^‡¿Fè°p¯Ù#OÛ]yÁ& œš‡‰Ãâ­SChÄå«`6mäŽ`/Ôhè¥ó‚µ–šØð^Ü= 0­è&ì.&ô¿yõ\Œ†…›$éÈ¡;ÉßqyÄHå¦ðÔ„CÐwl Î^NÄ8â6œ4):ÙüC«?1ˆ…B’²/€eÕÍÅ Zª¼o‰Ûi#Æ7ø%ÂÉwbô±X7ŒänÃç atÊ$®ç¸—vŠ…‚ø>OÈñ”¦¼ß°é©ÞXn´œlë8¢ÍzàmÔP~cÕaüÓ Ov½_‡2Õ±ÄÂin]C¤õ¹ÇMXøUZ¥âŸàXO,™ÀƒÏi¡è¯åXåøœ¹;ÄÔˆnÞ e:™l˜U*ûˆs÷êØ¤Û‹šŸz!'ö4ÚÞéE¬Og“V„¡ÂAGü× »\1}›7”ÉÀSb†˜—gfÚ£aËÒ$vÿdŒü¨Îßß‹Á²=‘ ÿ6ö”ZQ»– Hœ1Ÿê£IS¥øËb !ÕÄd—›ðiuᮨ/>JÓ8s‘é"ã¯B÷ér8º3fðý‘åÑËÓI£cœ¶y$Üv(|­raŒóhèÖ®ŽqXö±‰Ü©Çp½7˜>¨ûNg%ÿø¼NŒ*,¨€o›N€±Üv¸ð“ÉJÝ#ײÇá/©¸ëÔMLÝ`‚í3 nÞ~ÚjÒ(>‡E†üß‹m;?g–\E÷¦‘tÎP©ó ß~ñí'ɸ7å˜ðQ—Þ‰žNÅ·w‘M’ÐdrƒåX…­Ÿß±žú¼%³Où_~Kü*.£aF×i”±j­R¸0³#LÀÿöA(^ô7üÎU>íFuE9þüi(åï¡«w=ê—¨ o…^ÅŸi“IVÎPêˆ/XSq¿Ö/ÆùfÔí@lЬzu«ëì¬=MUãynßLŒ «÷Ý']Þ®lŸËðÛwWPŸ%Jg†éðwË>ÃØéã`‰Ú~8¼"…˜ý)Ô7Û)Lûµ”èw¯¥¿\#Øô5Š\×ê1?€Èù3©ìŸ—È Þ£íO²ÀšVZ`ΙÓDÌCÞ¸&I7^ƒÁFç-C !Ñn4_{̳ow!BT¾ ‰0üV˜ŸžG×(@þ÷$p ¥þ6cAcYþó–PfG;+¬]^ ‘!hù(ÚUYàÂ[‚å»a»ì=2ei4Þ³zKüÔ%ùX·JÐø|ÿ*ÇMäd¶6Ì} Ã$£ok•psB/“1©é¹ NUV\‚k¥Þ¤ýq[_& :’Á "gOû½¸:ÆŒSpú$Cš+[Æ"“{07ìsMŽƒ5®Û` ®¤Ãk©ðª¾Ä‚±’M?}½}ÎÊ cajïs¸ÐS‹‹<irª{lë´v Ø¬˜µp-ËxB„É©øï{1~ûó=ÎjàùÀ#xV{>›’É£¿5³k3ÙtóõdÔðUä!~&‘rõX)zJPt Õ.øü6k kÁš4-f2ú6«ˆÑ¥–åhÚ³9äLä/BR¿CÙ °*ËÒâ’àÛü±ØQ b~+ø©[a‰õ ¬]y–ÕN{å—ÂÉï‰ôè¸}à}x%±xH¶dçG|òȉU|±Ïn\Ö°è]kb·B)5Á%¦à?e?õQœ‡G¾#¿Ÿí„çz8õ‡;Úk  nïfœªu…ÈGXðM˜à9’.í} «4o1Ï]VßS™xÂê<“5¼Šw"4PÉx6±î(&û“´!jÈ|3Gƒœ| Ž‚µŸ–A¨ä$rºÍÑ鬥$‹…³}‡¿`¦Á(<Õ=–_qUÄ–‚‘ÜÃè"Œüu·æî“ùæ·Ÿw {D¿azí ¨U·~\fÕ»~÷?ÜRè&õ ã ÇÓü%;y»ÑH~çTnqå~Ó7C™ø!41œRø†NC.’.ùJ¸¥ð ]æÈ‘˜9…ƒ|à¦yž`S~%27^Bô?kˆ¼…øêËa¡ÄÉ|¡¦%8ärÒ.ÏÚÓ¦ð´Wdk†#.òÍ'g2åitY~vŸ5¶ »Ž.Z÷`ùØT0fDïO]OÙG´Ò ÀˆæWp7°WÜdû¼~¡×Q;ˆ~v-·˜Ã†“ 8Ñ9þŽ ™Aþ“»œÛŸ›CÍ=Øæ˜HúœùºlÌDì^_Åv™0œvf>¼yѽ?ªÈÑBâ-ÛÔ†'à$Ubóâ-Vºå³'oB@gÃhµ1æ[No$S/â}OX³4<ýtȃ;ƒ@ò&Îü2™.–ŠbÃûz‰³ˆû/cZ`YoŸ~÷1YÙ‡]åP¼q>È~½U£sºZp ŸIø½ˆ¹ — ›f«Sñ›°^Pþƒðl„,ÞÚ“Æ3dpÄTvöèö¡oö©DSwÅÓ0>bŽiü[G~ÉÔ쟯‰Æ§ÀiëørÑ‚Nh>Êì±Pö ‰}…sGDàö;Öü9­‡Ë–À¾Ç’<*õˆt~ÀÔ†Çø®ã"¾î6âú‹.Âó­_±­Z“'OB|–úã·X¸tle}®ÿ”Ä·ûþó1óÎ>´Í´A‰Öøp~Ûòr îH„„…"té‡>|^­BÏ  ­6ôÉù³¬fs-¶ÂÆA.S5³óˆZC+æn|ÇTÔž…V“¸wN78SY^wØ2$¨Ü„¿xcKÉWû Ôµòæ1 |ýãh¶|5žþ8ƒ'ÿÏ3Væcˆé;ri| ߪԉÿ ¥éqºÀJÄ$œA¼$ ¶«}„å=çò^YT zsÙ¤øÎMŠ0uM1îÔ|Ž•m¡ptÄZ÷·›”ᬟ9xiQ”’Ê Õøó›Ï!Ô|4¿åz…ïSžt†¼ E7:á\ ÿÛYSâgnõâîŸ>ThŸu…ø„ì‰| iÂú…éìDþW™"KÌè9}.P’CM“ þúþSáB;AΕÕôkW[ÓD “LÉÇCú¸;«á¥Ñ´âÜNørLÞ^Åu¾ŠDÓ)u*D®»Aæ‹,•>Iæø¸”Ù‹·Â¯U1ØtPœÅ4ˆwj¬qÞjŽaÚ÷„zîGQÙÌ ÊσÒÑdv|ÃÂì[°âÐX>¬?™Ìsé-…Ò×:û¾Wý~y•<×»„žï6aÜ[ |ÒAV?&_t¬á¦Ñf=P>æfà¦æÏ?XÏîÈTÃ!yØjj¢¼fÿ¥¾‡é¿Cψ…´È‘(;d2xHúÂÛÅ…P•¸¦¯E¿¡Ó@:ï «€ü¿³{meä½¹%Ùã ™]CÊŒ§Òïç¼pò ºÕ(Ð>R_¿ü*‚ôÇø¥?¾þ±ÝJx8ÿ6F­ù ”M´¡J¢¼Uq þ°z%ŽB¸i “Ô¤aÉÄðxüçYž{N›šåç8µ÷¡»,È¢ûŠl–]\Þ!ËM^d±·^,iWv®kuE–Úü†ß.¬Ž“„ f¼Îz(%… ð©X’zÌ*‹’µÔeÖpÐ0ë"¢æÔ{ÆP®AƒÜš Ô!JÍáÄvš-ìv9‡F£’3…·#ƒÈ‹'µ¢×49ƒ•Eî e2„~ÀétŠc!óœn‚–˓谑¦Xå¬Z•›!Y®¾öNÝãïAN‹ÃV??|„ºM):ïÛ.Ì uÃ9íðx¿Æ–\ƒMÊI÷î?¨)ÿ2'ø£õ†5<ñh/ÊÏAkï¤ýÅ)Ed–fä~J‡+zËaþ‘í¸6ø;´ú:Ø}jÆ ©rüüt+:â¼N >@'×Ô¡îúcÌÀZ ?çÅ šºr&ÿya7K÷Ë5[ðsûWüånKµ.vá—©Åä¿ù\x”L§¬ù¼S¸ªñ4Ú/ÔÃQ†ûo˜Ìom²¢_[ƒ‰Ç TñYˆ×†Ès«8{/MÐËH…öjˆÒÀ½ú +{o/_‚~ã¦Ñ¹¾9à´{%÷#‡ñZ•4Îß‚G3jÄÂ’ÊKg?|ß]‡›¿4øàyv1q<7*°àî{ã^;)> C? £Å 9lÁ¸k8ót;ÈÈüÃWE­È7GòÍ£ORÓe¹|Üäï<þ7>6úIüóä€Kl€}µC¸pî¾²1¯¾6 øg%ä¸JR¹ÑÑPùÊŒfÿt$×ôJ`üÿç[{ýžÍåÏ"Ï•ó¬ð¾T·| ŸCVi¿çéÒÔkìynºß wË’žL+ 4 Û}ù¬¨Zró“=VþÎ~ò£½²¢¼WœðÞÍ7ÁFXÌ_L{GF_x2Vm€[|è4»!ܬj(Š2¥­k¶ò—Ãéú]GY^‘1Í´¶ƒOmõpÒˆ/Aqö &npf7 yIrÔ9)Ù„+ÆúÓùžShÎÁ«çkí”SßãL7W²;”óƒÎȈWÆtÉwx}Ê•Z¿šB¥ÅMxbâe¼”[Éò"y¾Ö¡ýn¢…Q^¯€0>N. ä=&ÐÖž´l×6j¼j%}erÞîTÅd…y}Y„/û›ÅŽHæËí=©K® 6UÃ_ |êû<‘ø â>÷ã·¯ºÐy5”˜B+QöÏC¼ninoÝÎOF×5ŸæžÜ,$í’çCgå_³¨™W%( …Z~©fLëz4Ã…>¾·ƒËÌ?‚ÉÅ¿…×îýëûÄoáœ|w-¼ôMŽ[yøå{Ì~æKûfÞ£ÝDÿŸW=»#žo\§RqÈZH'ßL¥»‹yýË„7YÀ¥ð,’& C:"ÞŒ Û'i¢§?®úph‰–3Ôk»,ñÈŸ½ ÿ ­VÀ}’b’×&ïÀŸ§–ᱪ ì¾;†Û”Ù“[ß;0`ÃRˆô¿CÚ¤…â.bGªvôŠñcÒ™ßÅ©Ìæœ$}/¡É`- =xÛØlSÉ×!pu]'¼uñ&3Å Ùép¨”S Ó¢X.-Ç• ™löûо՟}ãZ8î†:6åàé8iú/:ÆÒ+W¬Qî×xaõ³W°ðû7ò5Z™å ‹].Ž5ŠaaW¸ý‹y t?^—Ó²àÖ\‰7JÂñÂh˜®…¦™¤=y)çϯâ5•áðàÈo¸Ñ‰Û|Çã¹ö2ÔŠxjå¡÷I6¸Ý=-¹÷ÎÉ÷á « ²¥qáÁ«lUÝ+–º"€Ût’u¾¸MvtÉóýü‘Ùo_~[’ˆ}W~Sί‘ƒ¼\9ú¡eý»ñý¹k^lÙ›%ܱñ¨oØŽ?$ð…¼­ÀwbÛ`_—àlÙj¡ÿQ1*+ÏÊg\jÞÅ¿="B[f1øç¿µŸúðMò«øùÊN“®SíysqÒ©G*èÁß妨—öþÑv)ä .4žÉ‚Eˆ±I“\±>Š'QßY ôå¿ ´÷÷jæŽ7}ÕØŸÝ’ðn†F·3Ÿ5î\áÜmh/Y#6òGãâÐö‡ ¯+xéÏâ€ÇVÜÎXÁ·¡´Ë™Ð¹ÿôþ“Ʋ[Á»®ŒôÝ sín¡Æ¸§Ì>ðh|°€„ày1ª}ÒæS¡m}îÜÿ Iêœ5v1ž¨´‹ÿJø7á¶Ž1äkk~³¨=OÞ®!½kià•z(Í› ‡:€Ríy¦Nµ˜n¡«Ÿ?À®±ôìEc>zÃj~ÓItës¶«~;[?$ ^Žëevå’äûßh! ¥³çñÝ ‚ÆÌŸqcùùqM~ô Ü˶¡›—zÁ±18fò,nÐÔHžÝ eEGLÁöÞ ŽO›F×û]f“jã±{Òœq@nd\ÄÓÆÐnÿB&t“£ý±¿`ûOŽ Ùžo–ˆÐKÓ·ÁøÐé´ã}¾ßöì¸ÀZW̥ΩE~r”°g‡€Í¸§„ûlám˜1Kõ=‚»Þªó…Ù&t“vnãØ¸õ7Ëk:¥Ç -ÏÙ™tñGZü¢ %çàQ~¼îþk}ý®ßÝšSÆÑc1£¹MÅqG_«ÝuB‰ *åáìî­T¾ÿ˜kNÆ ½º4n]0û}ï*ξsG Ì•h¸…8WK–ãWÞ²a.FÔéÌdtgΗ܂0ÁQRµŸÂ޽|Å·j,iŸÇZ‚1±04KñõF’¨æž‰sT)Yça'ú¿zR×\#ª¬è†AN±þ‚]¿B ¼Ílµ×]‚Ú¶Jtø%?¼–Š¯Ò‹0Ðr­”£9¹ß Þ)döÇÀñ;Ùcð¢±ËbX M(íÖ'6Ê ž˜U pãUnÒæeÉý(X¯v_>À–“EÌ$Mµ,øî=¨š× ÈœŒ3‡Ä‚Ô¶R(+’㙾Ñ`uè#<¿£@G¬ÆWco°\‹(ž¢! V%ƒZÑö>´¾‘â7já×⡼ñv"Úô~‚ÌA迼5^À%ïX÷ê‹7×§I°~~úÿåÃÎÇ¢:¿£?\Ç º–rŸ]Kù‚Šy$ìð&a‹A”%&ƒÁPuÜ4E 5¿µ ïù‹ñŸ¿Àõ›ìï¦Í8ýp¡2¬nH[öà=³viƒwÇÒZÍ©ütàköI˜ƒY}b?Æ‘²(`êuËy˜á>ɪÖý•Di±]¤öÛmxdz†ó§óç£ôž`öwøžH«ðóeG*áÍ=åÓä @úŽ;½xσž8?3O¯§wo 6t8ð·“£ ñ°ͯ×烿ü²´€F-§'WªÃôš>áÖ9Jd—ÓEX¸Eláäk±:ØIña\úy:¬ÔãË&óåµspîYyþnéBÎCËÚ¹Ôô‰ ß+ ÒYq…Ûo[oÞJó—EOp÷Õvad5~e±8²1}›I‘É—,ñ|üˆô8 W^þ>1ºK2Îì¹ü{ؘ٠Í>µhÙy´©<ßF›W•â«kºÔùh;ûÛ ͹Mùa4„>½ÏœÌƒaé®6¦s~\ <‰š*|Û¨”¾Œí~ÿÕI±ÇIÍle e±Ò<~r1nºP‡.K'Ð85Œ]¿N–—BqˆÍÉbšÇ¡Né$"¶RÌnn0¾Î?7¤µñÖ#]¼2Èõ+=Ä©SÔQbŸò¯ ßG͸Íù°oÏb˜ÖþƒÎ+Ãß~yfj#ùN‹0:¤Ï }Éž‡Må)™bTÑBƒ:¼4cõ}ÉôðA¾Ót{„÷Y«’ žõ9ƒ‹ÝèˆyKyP…+íÜ&O/ß&MÄS»_ÂÃ(ÕWÄ ®ÚC‹—?ÆLûGÒÙ\¸Û¡CÿË„n’¸§‰²ð ,˜¯@=·E×Ls*»Ã‘ß~Ôc?ÖA†ÇÐÿ¤Â¦Ãyuº±³‹;´N¦Êñ˜ÙÃ\¯(ÑÇ%(/7C%ZÚ¹ÞúÎ¥*fSåé½Û6(¹ThÛÖc¸Ø5xL|“¦ñ”×6xm9ᨠÑùÓYG›à»ß  ±?Â^[‡²´gåùûéòæêê=ÓjÉÎS¸Í ƒî§KbyÛ¹ XÑ ;q‹$hSk_!ͳþŒÿ]+xçX*8¯Èsïžçã?‡9ê“Ñöý&î¼B—ïŸSÅ“Ítxù›pžj­Ù ̲?ï„žy<»l+/ÓkÂzç@Ü1tÜÜ¿ V/Æ£/!Y2¢¿˜ŠòBé YÄuÊ&º2³ìr`ˆ‘!­‹wåjƒÏéÖ©Y5'¥ýøÒe{¸§‘mÃuÎ2?ÜÅ «ßÀVå!|‰£ }uó–Àx÷7#þò«iYæÀk$¸Qíp¨¹+m¦Ñ‚(•«2sähd¨(mq‡ºÃâ4ËŸD¶ð³ðç;ZkòŽþˆà˜¶Ô†.2åÑÑ`¸)‘ïéŸÈµÞ¯ƒà³|“D÷ާ‹]WÒÆüÝ Çÿrª±d¾¸Ó K@“¾¹Ž%¶âüxðhšüROÍyÈÄwùñ=Š¿Ð"ö0ØWtcÜ%i*ÐØÊ·¾)ăã9“  ß¾‰`ŒÄ4ôºÄp¯ïWâÜ‚äú"9¾b• «‹g±+ãqªs!‰åè6_qʬJáÛòkð€1ïïï•ç))‡¡¤¤G/»¥&Kh±»6žy3‰Ž¿ÿ»s'Ó솅 £vŽš›·C¦ÿuŒ}Žß“÷Âý Ù÷iÇ©ýºqTû¤þ¾Q‰GŸIÒw¯´x»±Ÿ#îL‹×<Á¶ ]®y'ÛTÙÓËÏÀˆ™kð~\”œžÂ'ÑQez¹¯ æU›Ñ—kGñ LZPº:„'B9}î·ð›•/¹´ÂÂëóé•-™4x=Ý:Ï–÷Iµ¸4ÿzÀ‚ÎÑ ã‡Þ gÝÃèŸÛ¨éÅTN3ÅHóÏ™ôK1xdXc»¥ šT¥óô +Qâf%÷,ÓÅÏ‘Ñô‘{"u*)ã™)ƒï¤J3“†ðo‚x04†þ§_‹ÇI~·¸LÜyroro7_HW?käÆ Úð¶í/æÄH|a º£ùØjw¬8Q† z7ȼoa|dÞLx7{8‘à; eñœtÛl!ÉϼXηþ‚{N¾÷Ù~¿q)y¶˜>‰8Œ4é#Ï£ŸÝ?Ľ;éˆYð.m5ÜÕ÷÷ #*ƒúw/™…ûÒ?± IQ¾íç4ÌSç–Kj`ä„z4àW ¡ð8Ú^ Þ¥áqÁLºêj'Ó;“„†–B°‹ÈcÅAË~Cêyi>h}“—K°‘•Fhô¾ †í¸DæF¬ú«ÎkTªql˜1„|ò¦xj­®ÊôQQ>–Òmüõ<-Þ÷ЈK‡äCm¢]ÒªJF×üa—¿Î€/{‡àÞ¦xø±níy…lðn$»Íƒÿ^7~aì"S¹„íÊH“~A‘¿ŽôÊél¨4ˆ5ƒÉ7þAÙÝÇèR:¶ÇqÚ/I¾nÊy w ØA›;`qe4x/¾OZÖ†ãÙ¬È1h°ï4Ut\ÿ=>]9b7k î/g㆗ƒâê–d {G\!—¿\„³™Pf©•k æ&Üï±åúÎ èÎ—à•» k†óí'—Çe…dÙ˜ ú¢(…ýþÒITõ™ßR?Ș®‚ ûžC¯I þ¸Åñ»‹·8Vó¸ÿŽ[ÂCÇ*¸pOÎ;"BWœ¾Xò Ö&O ç'Ÿ„ï3ôsÀpjöu/Þùt®Çâ³mñ4Èè5$å*Ò‰JJÜÉn ?Ùc@§¯ºDøx°Ç]ùÜ5›—t£Ôí?¨³T %þ'fÖÁc}cþ'p7,¬(†gvÿHŠu!hŸ= ¶“dÀíîMذØžœS+CG\žÙÂBŸ5 ¿+¬¤ Ý8©Sžþ¦ß`ê82A›Í«Gµ­¿ßHŒ&çë Ûð}"x±u,ƒ½€»_YU *1®Â|ÕèäLM¬íKŒÇyc•lL Â{ê ¡uÕY<&R÷”©^œ ŽH-";yI SU¦š7.ÁÇcÁ¯wó·Õ—“W?Ðïöa¶5U‚ÛxÏÉx#i6ØtcðŽÉ|ÒÞñLÌu'®ØûN`b¹ÆÔƒ¡Ýdjuì'´É\'B«Û±"nÃ9Û´˜Ý¤í¨Ðt¶ZYàˆæ5hŸ«Î¢¢ÜÀ1«þ„Zp½µ0dä(Z»JÿxWŠŸø¢ì,VðnèUcßýC„üýÍËçðŠ”Û©UAïQ{Ñ\}=¡8­á м§Å)™èR¨b8Bž‡ïÓÝû« š©Ày•Dh¹‚ÖŽÄo;j}ÜOíûˆŽ.&´)1ެoÁ=góg²àœŸ +÷ýCɵšxÉ]™_*zC~[ÙMÖRæ‰&cißÌaüjÀ?pV? $ g­Ýεª˜ÿcÚ­“gU“4í>t1æ#Ä>ðâ׉ƒQ Ÿ'Þ{$ùð ~jB;¾ä(UÂ1ÔUÎn]Š ’áØYz›ˆÕGbçlR25^7¬äi*?@tY }k4‡æmÖç ¾,hÁ5Ø1µJwÓç„RÅ#Áì¡ùX:D|—š½tßÇñáW3‰ó[#Þ£1ŒW¿÷ á/µðå®LÌî^ir_ð¿scŒgÑ ¿íùóô­ #‚TéŠg¢¼æ4.œy .(ð½iA`(K?¸ ÓÂᜫZ|p}°½LŸŽÞ]€å[‡óÚ;7’†ïÓ\éŠY7qþ„ëø0É— %˜cD +YÓGãÇ“ŠTŒn#™>\Mxœç›T‘çyÞ¼Âl&]Ò´Ž^›½ç_[Ãã΄ÏK”Ù»Ù×0ÿÝwRäAô?’?[Uéçú·¨^rwÊÞFÑ Gh{[ûZJL«DE[Gx‘ßNûœMé'÷ZLxQGžœ¥î WðÇzøš§FßÏ‚j÷<”XÊ_“ÃøaN uŠA‹®°P^@Ï'Pï4S:á¨Õ©²àk®9ÓýN?Ñç­×UÀ3µêü`N$õ©= ©n\ÿ£.Ÿ9ú;0*ž,Ó嘮6Šö¦³î÷Ãh,ûiF¡ôùnæóÂãÁáÆZüÚmÂ~:ã IÎÜd%kÚ;ÖÑ…—RðbB?û[¡Ä]4±a äMÙO0Zà Ç7Œãk&”’–Uðý8ÃW’7pÌèGìÖ;G.-Õ%X³¨‡§íª¸ž‰Ùßñl%Ésš}rôçĤšw'd j¥,þ¼ª@?´XSƒƒž€…ÉÌ`a lˆº‰CŸXr“™M8SÛ‘2ñš}òtîËZvr£?fx3ÚóŸÃ$ðy¶=äßÑ¡z=a,:j-±ø‘Ü‚§^t¨  "ïOoǧG1àø.nê'; öw‹5â[ÎNƒ3ºÂ !ò4¢d g€±T¦à¾¨œ ”Á±mÑÌAzº?±¬d‹a]p3ÂTg˜ ßuc“1kâj<(_*xòÁµÞŠñÆ9³Ña¥êáWÀÙd{Ag#ø(P¦X)¹Íæ¦ ]çmé#“B68–›©³¿IÁñP :'r‚!nGáüìè×b÷ûóåwî“°˜[Ø«€è]Þ þ…ïqJåçš;{“àªÊ.lÿú–|~ÞMPÅU™ ö.&õ(23~CÂ2Ù¿ÙE¸ÄBKpà˜¿¶© ¿~JýÔ»q]éè>U„_öŸEõ !äOäÚUj_·—¡‚¸>lý R~‚¶ëÛÉV­Gà¿Ï—D'Á+}:K¾êÒA·ã„ïøq—KßácÌxjõù™ÀÍ/RpÓ£”$ }àpÜÚ)†j9øöÉT˜wË §lXÇ£¤˜‘><©ËÚ;‚ËÏÇäos+Þ¿¨Àw½êª]ptE!^VDÃtIlªÃ•Ü‚ ù¯(W}ƒ‚Éi˜l9’¼X€6uM 0ì&Œ;ºÕ¯!){ £ué íÍt`r%Û3 ñÛ=æöõwµÈAe¸iÃ_: ãµ^ zGì!+\„Xp¹"òYcy [N[!T-`<5ž/mW§B£P8#‚{§CW¯Cö]îÞTrb+<²MeÉhÈU³ñŽ…®Ö#zYÃAÿw5Y5Zš¼-bRÛ¾ ëÃïüÍøðÍ´Ìt€)1r¼ÆÊ®fÇžÉ5ºËª@írtÿ¸Èv%~‡íC+>¸ˆ€x¾8Øø_bIà<¿Jè¶G{féáöÑrPÿu+ª\ƒMfKù­Z3¢»…•â4Üô3Hî8ÿeCÛ¾K†®ÛÄÊv+è¬<ƒ¹‹ÚIeF=îSS€Ô5+ìæî¥ÊÔ*ÉAÑë“aSÞQÈ]hAF}»K4D²YĤ8;ï&Û¢¶ƒÌ]4_àðÓŸ=š(N =qħ¢~I‚jJÞFƒ¥¯aí¿Bø¬>ŸO ÞÉ\÷gâÞ°t^™=þ«?G>”ÃzÆâ¸/c„­ÑsÙ^©|0’ Ð>{', ¼ƒÂ´ƒ‚ýâŽ\›~oFÒÏJãøÂ}6t̆|¢wàš÷.‚¦¦jI¦N]CäiséîÚ‡l¹=@H{.Ë=¡Ä €ÙÊ[pắ>èÆ?ž9ìÓ|xu×7‰ÀœRüÈX \ž-œÜÑÏóø˜Ç{™›F¬š`k/[ËÚP³v5èt¿8ßm÷ ƒV}…ÛO¢§ˆ/}"Ž_%;'ÌÜàX²þÈ7 𔀛'¬ùBŒÎú@æ™Ñî¯F_èLƒDoO0ÙdÅk–}–új£²ù+6FFþ paÞ-t4ò W¾Â}Þå¤çŸ,÷S•JÇÀKÚ \” QµýäÝõõ|¦¯ÌM>½w´µ‚¹·™?lîQ¦Ù9D7ãÜ'k¸ýL#þ_võñRSúBi¼iJãîL£ãú´®sûw?í¯j¢ä)2nu/Ž./‚S’¹X¿ ²Ge%Ä-]¼Š»ÇŽ…´­°ìËJÔÙC Ôˆlo Kž;Æï,$åuãØ¸»yÄnÔ?7ø¸~*\”Z‹®gc¬Cp}G0éܹ²¦*Ó×dÑufÕÏ÷ØÂô'eP™nÌuþnGëº%d®|).¨²ÙÐ=ô9±ÿ. RJ¥ WðÖ—>ö^ýlË×” ÎaŸdn’3SÉïõ aèTEŒ ·C…F ¸dKáäê3ÄIZšª|³€ç)»gøœe¾Ô>'ì04/œtEüCµëVôÝJoØ÷KÆ|0ÁŒÛpç‹?Õò¬ÎH@ßqWaê,}tÛZŠ5ÙŸ±£^þú²¦ :A–NŠÐ‘ofAûã,BKGñ‚á8jµælŸÇ'LÛEæoT'^ÁIÌ"ñ ©º“†Êvl¹“«ÐoV¨Ë…η? 3ù¿\Ë¢™†4üN ŠG ¶Sg5‹ò`~¥§½VÁÙØÏø±I›-(ÙÈ´B‰˜ž(nþþÏ…¥â·¾l†óذ_‚”ÍÎC·Õ¸Ìp§îI°zž õôy@J5µUOñIÄKonHôR]îö&/&~Æw£IÔ˜aT$bX¹3ÿõYì|”:½Õ6¸oåÁ¤ªZòsA1ضêÀ ón¦2kX4б)RÇÙ1ad¦˜ó¬)qZG×¢_]RÁ[TƒúÞÙ šËœ +Ùyé:á´ S’x#•m~¿„]s‘„gý(,÷4ÅÛIWÀ±Ö ›Ÿ¬‚ðâ¸aNŽjU¢Ç<äYÔ‚.rZ¨Ìú/¦ ½Æuá¥[8?p%¥ÅЗwÓ!¡˜ö˜Ù“A§ØÚÀ©|ôòfÔè¶ÅÀº"ß} ~æo¢O5öPÃvâó` ølÈD­{¹Ì9røŽ.œ;‡#·Î¥Êv߈é}S¾æp2FŽ\‚róÐPhÀ‹ 1p¥=WÔÙQxþØPûðg,™M%š‹Q]Ð}“7éöÃ'^C¹ñƒ¤Ùà2ôu #¾kÒeؼ⎻æà<|PºÒœŸ¿”NŽk6ãퟘ3™6v⇢t|µ®‚|ÍóD™öµ;p²›Þ}¹]F\¤g69[Ὺ|ÁêÔÃÀŸçÍ[Álª|!ÇèÁ¬X3bùø0dNßËvŸNÄ #FðÌ—K&¬B툡¸m#¼5.ÜÛõÐm¢?»ÿ¢Hhëß%ÎÒèôqqñE¦4¿…¾'ðoÌ<»ýz7Ò6ó¹ICì“äÝÑ—°üÝbôüØ Óf?f"“ØË[±Vn?ÊJ›à›è‚Û{ïCÍdÒ;}• Xˆ5³ÇÀÉ´åXW• Æ ý±Zæ|¨%„åö˜RÈ?uAØ?ÿ$™:ÄÏ‹ìø=hZÇî— ãÇ~Vá¤BGpÞ»‚ö¼éZ_¬eQìôƘ=e ŸYçG_ß…Ýѧѧh o‘I·‚‘†Ô¼?‚l%xýû-^{Yøïoàq+*¹æ“¯]ÀSŠü?gz×Ö^xÙ(ñ_DQÝP“™æ¼!ê†÷QnÚ$jÕ:—§œ9÷/ @;ì$Û®ö‘}®\ʽ~Ç‘Q1«;ºGSªQˆ’6J|i’3•Š‹å‡ã›Ð⌌<üï–”á<‰*^¿(™-±ÿvS<¨dy(Îj˜†uKáòçB6 ÿ–YìÙAP—{û•Ðt˜Ôº çêgñ„¬W>oÀãŸý ‰¿–ƒØ½dð;gÎêv=a%Ë:ðзBèíxȪzTÙ‹UÃI©› ­·Š'[%yvîâîg-½k׎» 2OpTËæx~C±—-X#·”>)Bo½KÅ¿í]¸hÞ1( ¬(x¬î÷`jy¿û)NjÔàÊÏ䩿Ugîzù FÛæ‘•ÓýX˜ˆ,^ÍT€=dqÂê…¤ím 9U¦Lßu5£«¯2Ì;tNX_ôR—ᥞ©ÔN=ž-V—GW=ÆžÚ§1:9‰(Š|†(ïRzÙŠê÷ÚYÚÞ ø©fCæ®Ãʦ8"fÝòs?š^ÝartT÷+ˆ1Š„¯2pZ-áóž‹Ñø©ÿàÚ§õ°wË}²çåIÒ·8†Ì5?ªecÐ`v>Š‘ ÅŽ@nP%gÝfSî41צ|vð€(WdêÜÌí³•‹‹¯×Øk6D°]C_Ã`ìvøC¿ö¢’ßA<*^ sÖoÄÛ_Áúx¿+&÷_`“áW8%Þlï¡™ˆY¿d`ñ¸w˜n·M(–ÅåCS`¶¨:­9”û½ãØæB%°™'‹RÎcHƒL,ϼ¿/Ì—°žÕ Ð5;~Æsq7ÂO»kPÉí¶äÜÖ,²èÀl.7ï0Ÿ;l}rª”wþüI†Ñýôá/.©Oú0… /Hêìƒö4}=rÃŽ>÷QáaÄ?§ÁÈã6üïäh*¾ô:6N—çwóÚÜ9ÃÞ<—GýƒvÜbì\º…:ßàPÀ9&F/»B»öá‡P9v?ü[,Í;W• ½q=±I8ëg ¦D–)EFía=“ç£Ùµo$QJžÓ$_Œà^0|É4 äÇÑjönß?–{å=Âõ£ç¢ò%aZÅ2÷¤¼ë»‚9¦ J§ß#£¥KØõ=ú0vz?ÐæG¸ï˜æ/B«B¾3‰‚¬vX;ÄI‚áÝ20µ­E{í%xkäT¸v[œŸüµ¯8÷ ‡¿üÑŸynº))Ñ _Ÿù1Ùx!Vmû'(nt…1×]¡mÇ#¦?àÊv øãäàUøÚyÖÆ›Àác×±Kán‰ƒðWÇxTØr›YœSàæ±ù0aò|Úðö.;SÌ‚JfA€ ¨ß½Ò´`ñ)2”3¯‡ÜÀ­ðmpÏŸèØnNLñV™Æ‚bÒPzúg¹»w$…ƒC¨a‚6Tl‹—¤ŠJ\möûUdw à@â]8N÷`Nú[œ$ý†œö²Ç9ÿPT.NmlD¹0¶ã¢9Ý¥ÆS‹ßlŠ‚Ý†E°Åp UU|†ógÀ³îpåÎupêU"çç(aoÒ:£ÿ1äW¼ƒªOÑ%*‚Ÿ3\Mÿ¥þųQ9À¾©âøÚH¾­£,<·›÷Å[ñ’cÃ`»G4>S9FI—½ü,VÕ¼Á+ŽÄáµ|»‡.°”æïê)¼:W _ì¾bÿЃ¸fo­¼ÜOR|aV0›#½C¥gÉâ+#QD EBãM¨é³(X…kMùÏÌÁ{èŸI.ßõ†#õìí£Z¶ªEŽÊìP%FæYkåx«%¡cauf/‰¥*pÓãdè¯H{s–áÜG×IÅ5 ¢?bù§pï Qd;G.ôÇâúxÜÿXÊuØC£å°ÜÀ„ÙbIU¦ä³o·–@’š9®Ée '…¾Fe:#½–=›1ŒjV-€ÊU—`ç…ÇDu²ô~½<ˆKƒxàmG{¦©ÑC±4&i"K9Gqäû^ᦺyà{  œ¤BA9Â4Äé 3š<; Þ((Ãβ$ÌÞÕC®JCkî'VoDÐA2»†ÃƒWñ÷²xì4ðÇ‚sgYtßÐÚ2fíÚE>yLÁ§+ ÿÚG²ä¾$ôç]„c¯“0?b½2AC6¹³ÂwkÔ0ëH#ºöEÓéw¯àÑ› Ù”‘7`Aêløòpn›8¯@>ù(Fϵ²ˆ5µqs|SÓÃÉæ+RÔNd(uw.^éxŒâ"Ëkد©‰.ÝO…söûÜ~<=+®fjùoÔ¿üŸ×˜Ž5Û §ûF¢Ã$Â/Êô‚ÇUì_ Ø”+Â~Ç[ãO-7òÉI‚¿JÑfç­‡Á$­hæ2Õ…þ[Žî_f³{ÿ ~{ n[Q'8Sÿ”•_³ªçßájý&&½ä0½G~¢…ÅG¢²W›Ö JxëÁÝüãáãÄÖÃ`Ý]Ù.OG]~#p¼Áׯ Þ.yl´ÜOÞ¤»ð²÷ñ1E‚é&ãsFÞ…ë…Iôß±5à~75ë Ñmk~, Ï|ÄgÉ‹x›èo<òp7}­ð_‡a\~½–Ùö¯Z†µ]ý…^÷þ@ÏgM.q8‚{7)@±è}öò`›pò¼ßøËL‰N=Y ýï´¨“à9rŠÝÚ÷Øí@T? ö¹˜pн ?v†Ý²igN@|x±½‰×íÔ±Zë‘ßf‡ª6­L}Ù6p×=kdÎcé?}ÁvÛcЮ3’¨šòúß‹ÐÝy>·JÅUšßO|hœ¤-dßêljåãè…¯aÆ$gú|Õ ðÐÞ(?€ ¡/ îÆN/ªqý=Œ¾Õƒêö¤õp qÿLxÏÒèXSåù%©X£Ì§¼&ó¡_ iÅaeÝU~ËÇ„ÿpH„ÙÅ‘|¤Ø>ë%Ê;Ubåâ+ `S¾MšâLC2ùÖËì¯N_Å3UdØP|·Eú¯ZÓéo‚øñ?±X“ qÃcÈm¹d´g%üWS[–¥ ¿sà¯qX^˜Ì?‚üm ¿cn©–\·³&•…sÏ/³ Ðp:ÜѤ?ö-'áábÀS+aÙ×à6oZ$\ ¯VÌxú#ûnÀ¯¸ð{|Ç-ÐÅúO¢TÞª¦~ž€e–Á$¬ÿ1ŽZ4~PV¤Öâ—¡FôÓ`,˜T‚¹µp½b” uT Ù_!G_Ö?Âûg¡þ€=ŸøàVLÐ'¢íèÁ{„ƒ×…„…ÆTg_œŸG©È ®qÓû£„Õ …"›ÇàûèQ(2{¾þRH¾Ÿ²·àã»)Y¦‚:ÉGÀ1t)ÿð#—_š ª?–ðì[âÔûè >iS Ìpo!•ï.Añ%yÜ1ÏÒ>¦ƒ}ðG,M(‚jñ£ é=œç_“ê«Ópßånâjdê­ Ø”TÂÆgjrŸÖÕ‚‰×AçÛK°xÓwp]i…r° §Mä× šÀt¡$xÏà*'±ç3¿ã³BKLŸYŠM¦Ê˜£ù”ä³ÅÝCˆôŸshç;¥ƒ9Èç«°ôŒ·™í «ž'¹Þ;–dàÛ™ûIÔ:a÷Åv¾µV vD”­`Âýƒ*t«#+U„?[@»æ&Þ"úhxÔKýÀÍʈJDO^8²Am6ÔNoºÄ•Whѱ_›aèš÷ð!æÙ­0‰Nù! ²?Ô<6ÎÎP:6€U›‰öcæpÁ{oä íG›éqá\þ±w7­ÆØ©Ðßóšk/‰ “DÅ©B²ýÞ&Ï;L~ÃÖ[:,&ø'œžD+µ<è†? ìDž\rÚcîÁb±‹lÑüÄÁóñ¸b“7.*‡Ϭðî3g0žóö_‘¥âÿ`R{¿Ð³JHט†žÈ)táµfܼƙŽû|þà‰ `HþjÅWN<ŠóCÂè‰p_Ad{+ßÜšl—ä.ËÍï§ÐûûÅøƒBtžß5«yÉa·èŒeF|Ũ“p~û5H[:„ªµq¡þ2"sB‚7vRü®Éý‡I£Ÿ¾¯—?ÜãÉ£ÂÕü‚r4]˜ò—Ý7?Êïn=ÊÊmƒ[µx%Л?»²v:\`ÕÉóhêíutdPåù‘°Ï¿D~–£ly>_¿ÚWþ&ü®tŒo=̆\y•CtÖ=/ô}òƒÖ>ŠÁcO@›òš¯¤EñÐlo¡oô~zÿÞmê0v› æ¢Øt{j(C“´ÉŸtiúîY;Q»w† øïÑg1Üo øm9øÜ·3‘/N¸¯@ •ï÷ñåùßð‰xiyt—ëâ6keÚž¡Ê·vnÀ9ù¿ÑüÛ;ˆß𓱨r,Ï~é ^Ù¢ÌöÏ0P+øÄ¦æã¤úsPg±³]¾ã‘ÍN<_~=ÆKÞ„ÈÍçàlB Nì0æeµÃpJÅ=¸ó|=^É à¢Þ ƒª,”LP¤*‰H Ÿ ­¾º´è¿Ä6Å6²ýÊ,ÐKM$oÖõÁÛcíxOo;~™ ß/8¡ËYºþ*¥—ݨùÊUtñ¹G(ÑÙøŸo™h_$£Ü—Çmx2@Ê wAaÓNÌü#Öc†À“¿"Pk%’SG°ó-†ü >?£’M—ëà¨^”.jfgÌë*øvý:›—ã̽uW *jòþÚ³øj7º_ýËn½2¤Ê›o㇀ˆ½°¶GþÑý.ˆvîÁÃ(×Ô=)öhU¼v«Ýk„™1ÒàjzË3Éf÷MTô×[È80 f\'gÄ2ÑñB&ÀƒÝðÓ<G–ô Xsm<Ê4”Ž®ïƒëim/üÈnî“¥óË"¹¤ñuèÍN#¶c‡Óá3špMG!í³„íD–ŠðÓu5 qý>nm;Œê:ÑàÞ"MG mðÇöó¨b£ÈÓ¾…¡Xëyì/Vânb¡Îí3,±™SÝŠÐõ»Ž =ÏwåxàeU[¾ò^úéJð„ºR&æÛƒ­Br¢T”ʵ?€ òªÌŠӯ߀^F —ÜÒΞ´"Ç£á:•Éè|z?Õ…óÑ3®JüNÝ Ð°SÅЪ‹Ìºø2Ê|,$Ñ5¨\º®ap{³²>~3Oç/B½C‰îq¢ÿ"á´† }w<7žÆÇ»j!½À—;÷•“«ëæðõVãÍKþ$­Q0qÊÐièVÎpåR~X‹;?$^3Y”fxÛÈð†W0y¼3}±µŒž:?ÎOô‚»Û£¡¸t,­Û«3öñ#ën Ÿ­¨Å ™#¼À ƒê¦ùÒÀ×[p »|W¨âÓ¿óñz·“°ÝÆ"v<Æi‘Á`÷ œ Øãí¶Á eØúþ û{y8}S†Èöo}]#ð·ëÂ2û_ðaîKth:Ç2ÎèÝZ…]Î`…õzÜ&‰–Fx:[÷Îñä½ÊÒ·LžîFQ?].¿lí<«…ßjQóÎ X°';ÿ5`ëÏÐWìÏE¥uù§y¿O ¨¼¸ \0}4×5Þ$˜×_ððúh\?ŽÆ‹[‘»úpà2m;sXº¾5®í,kÑþ·K~kìYRÊþ©GËÎìsØ04qwÀ‰´®Þ™J«w_‡î¸"&šºœ¸Ì‹B{íÍ ->çΛJ'ms}#mºox=ò§T²˜ÕÀE8%1#ÿd¢_ÃUAÖèñøù’ -Jx‚VUFð&%¥zðú,B¥mžüÇSIèl®$9Ùêüy oõXÁÜ?$<ÒÏW^@“Ýa ºàQ_ßÀ-1ŽîïÞ’oqƒ\·Ô‰J¾ñ'­±è~3 VŸRD‰}ðã^+Œ‘çvÇÏ!þ&ñâ~ÉÑáge¨àT4äŽ^Nÿ…@`ß´8 ËëZÞâ㤣ȔÜÔ £\{Yƒu~¶vÃþ›Á;Ës&žˆõ¦ÏVDÍ#nkòè‰Û“ø*Åq¼t´%-LY‚3î†r‹ ~6œ.LÁ×L±âÙm 8£xÈÕeÓWðM±t–£èÇ­ø»=¦Ÿ¥Lè÷w¹óÙ!W\­Ë„–›+P}KæÆ³ }èÑ ìÀ‰)|ÊÍnãC±øŸ5Ë,ZOwÙ-¥ÛÞ–BÊGzØi>ë?ç‹:ƒÇn=Å‘§¥±Þ@Îñ^ N™‹`ÓÕôÌù%z6­gû<—ðŒJ]š¹ Ž·%jSÿ°gÂÃó“àä±lòqÃ3ØjB÷®YE4I@áx4ï8ÅÊ—¢˜M*ŽE}WdÂÞM9Ü!¨ “6ÁøÇJDiçvnV|“‘øHÞá¶múðn6K‘OÔmŸ’Aê§4“~š@M²õ°cq‹œc£&ßÇ\çýÝšFZ†€âØ{8mÜ~nr(¸)ÒýoƃræíM¯Iøðè¸DݺŠ|YßÕd3My=‡W—Cñ³ºí÷qú>r75¿‰h÷bÿøíð8ÊY¿wé^†G@}¤7`ø­4+]+zÓ°aQÎ^aÀ{rŽPã¹°2ÌÛžD\døÂ˜¹˜¶Ü.¶3ÏJtüã2_ªšY;ò©Ãû!Ò{w)MƒÕöñ'köðo_M¡*,…¼aÊt¢ý4liiG›Í Qu›Ó|Íæ*í/qZò *:¨3_uŒ¢ù‰cÀcð]ÿ“D¯ÙXRçcºôè^z¬*’Wá†|i–¦1”¿8Cá]"¿šºV­äâ<àê¨Û$ÞP™?î1Çõg úé(ô]lNîó`Ôÿ“€µZôUÔrèù1¢'7?ßùAñ·]°ª,Õ‹Íéš"|µÖlu“̺:~:|„°à4¦¦1…ÇææÀå¤c0kÔIlõ)£sWëpg¶clöÁ”*¼Ù®i‚ß™N¸BÆΚ9“¯Öµ…üWìbÕø­ï@&îÊ[žŽÃEg•(3V"n ·®0,ÉR‡I2ç]q×âË0öUd+ËÒŠÛXôq]4mü%¼øl2hç©òwá|b¯+ߟô­Ú;q©ègHÍ» ©ÉÚü·õA2|(ü>¨ëª\z;޶xœ‡Í»,hªÝDºÙð0› ÔtìLu<ê9®<%Ð.Û8j;Ê”önP[£ctÂÝBüÖ:EƒÜN?:,F?¾ä¶AyübØU²z@î›ì—‹0‡Gªü^ìl|i=…h\›Âþ\Ë@íû²4Tl œµ5¡Ûæšc¹]"Yvhkn¹OMæAï£Aá÷X:¤½ƒ÷,åëR‘×ßͨY/Xùé€Ì‚Jø.fÍîÏzƒ'•XS§<—)Þ>+~?³_¥n/â5.²{Í#õ?C™¯*_½ô¤ í¦’ðˆ×H:»r5í¾¤IË[é£-1P7ì2œW[À®mfþ»£ˆï@>$í­oˆlG‘ý«c¸awVL…ͲÛéÔ½9ƒÀ5gí0“¯Ð"­€ãŠÙŠŸ…päT)^\ÑÆ´§Å`èe<³Ú>]+„ãj0¾ò½ý¢¿Ç ó‚Ñ{7 6Ÿ@.9ÕœÈí½ å1äEÖúìG ™«kÁ4pÚ´„¯pŸÇ壦Ã^¿ v:—åÞÙ#C霥±Ø(Ò¶k¶ÑÓÓdéóØItêï¼IA…t#ÇJ×ÁŸIa“¹6Õ~>‰¬ï”àþŽ3Áx‚<}ú/ æèñ°ý[QOí µœqƒë_áà/}ÿƒµ6”’T6Jôû$}ÿ—ÌJ·¢ãÍÖ‚Q=zxNC£sxgþ8¾wZ*i›…­¦rù¸"ªÜ€V%2Pšn²§–ãðÆkèðK™KWöžÔh"™2ŽßQ§tcäÑšŸWN r©Rì‰ãážlÀü1[hB¿+ªù &`¥@C{îOtªr©§ð'Ú‘ql¸k]ôg‹ƒEŒ;O˜„ï’Gðô¬½|_‰D‹Ðñ5!wÇaÜîw{^¨“Y/àÂp\5lP+g²‚õ·ñáb>ªîÀÊZT;ø ¯Á3”&áâYŠ8'Yœ÷]ð#¹åpç['ŒKF/}-¾*w-¸üÝÆbŽW1Ó¡2h Ñç[u©ûIoxXf¿Î?ŸšO§•×j±1ô˜ŽµˆÐ(4]C†KÞÃïߣ¦‡>‹v§6‹¨‡Á(ë'yú*tà_;À„Iôrá-¡e‰–WvÕLÎÚîƒüב»aЙ?õLæò×ñªÏÒ´9c"äfÏoÎý¸wÃq”7ÄeE©ùG?ºMsÜxÿÉì9†:C¦ó]Ó¾,Ý×#ë‰Gp8·uƒ]«,y˜½.­Ú#¬U›Mð»s¬¨ŸM}E§ñ€i7ð¸gºÊ< 2/öa·`óàÜ_[ ðóVÚ öǾŸõÇ16{Ðïfèy*¸6Ú_oôÓhcbúÌgw¶cß :f‹6w… o“ |ÿ*Ñ}U‹n¹VX4>Öçó>œ¹2›H)¾ÃÔG©äGÿ¨]oŒ‘ kPpÄŒŽíËå¡ï àý4qÁa 9íÈÆ³f0”i1úôòÈŸ$¬mwÊ.DþÏdÑ;;È]¿­–!&/‚Ù¢¨1¨v÷vtkW›O^Å/tÕ²—˜²ìabSCÿñKçà–2CnÞà ­Có…ýU¼µ~ì)À²…ÝptgAÍp12fã}¼h—ßæ§¿“qÇræ¸çnÌ<…ƒk!%Ò/…Qßà˜†Z¼ûe½4QFY1ôÂÞ®&†âyöè03Ø£;x»â,¹«ÊU·¹QÅêå´¼ø#QõZ 2äé”–PÐÜôʹ_m•0>Dãõ‡ƒÈÚØ÷Rçï †Ï»Î`y€7-}%ÏOZ 'nMÄ8¿P¾Hœ4ïîõ%Œ›ºOs,Ïé¾b‡N]‡“ç“Jfpµ7\Öƒ/êáÉQYŒ{ðެÄBK’·¶ Tf˜S?…rw>”Ø+wàû†·¸âG.NÛ>o*ÑÃ[Bø+ø¬Ã’œÇ dcF$yúxŸ¼5›cû°gB>Ç%1h@:Qþá&yñ1šØÖÃ+Ïp¸øŽðe®£È8ÉI¸ÿ]ï÷ûyçÀœcö©Ö †ÿÃ^Ì šG¯û•ï`§P_â)Gͽ½C¨pqùïÀ,û9›Ó|?xØokn³7‚–±9:kÙ¹B€Ù^M˜»Ø–èîN%†Ÿ±ónýÅ vâõtúè Ìä÷J!éõÁŸG6ÒYÃG`ô· Ý9Qï¬àr¿ k¢ª²ÆPOô;eJ›ºöœ1’"Ëd`,ø;3 @u_'C¾ò2f‘Ñjxº2ù”éík…èvP·W¢G?qÆM ™5ü¯1¼ŸéTÑGÁ• -oëbŸ? ÏÃ…`ø±é¿ŒÖÁ‘œÜú@˜<Ÿã,êΨßí…s²`,¤Bt—1B)Iðã^$ß:ÇæÝ·—/WÐþØm&(Öý&ÀÙiX8`ËqyÔÏÜ:çTá]m;HOúSïu'á~Â+¦×æxijÚÿÔddŠbà@Á%Æ|Ò{èÉ0¿K 6G—ycš†œÈ™Øœn)¿%ØŽÍ+hOÎ(V áƒÝr`>+ºÝ‚±Þ^è÷®aV¸€–Çéh3c楰r3EÁü¨ãõF¯úB’~U•ÝPÆÇÞNdVè äô ÀžÂ>vý]UÀ%èøá8d=ªà~Ø)ñS%SEº\L,Q¶!9þÂø]Q„FíÆkŒ-­q^ S–ò°Rໃø*S”<3w#23@kn5ž»®ùÇ.A‚BÆÛíÂïþ†“ÍC!dzzêŸB¨—0}ÜeéÍh„îEø8ȆÐrQj?ÉK‡ T)è^e}tš±ò¡­Æ%xK÷s ¸ý¬æQá•ëÁp/Q¯ÙßÂNௌ¿à98Àq<}:õ>`݂ǜ§ý'Q7QÅ~^Ç9;½è•× eÉìŠU"N¿¢QÔr=íZ¶‘™×ÓÃ\™"F/¼7 ۔ⓣ@¦ÞÇWí[ ‹»Ky©bË6ºûG/½uaÀÀi\^WŠb~Jts0?qèÛŒ‡ó#íU¡ë¾·Á¾ òFŽ(”¸ã¹ZiÊÚ1´aU*ýÓ™JޯΧ[fÉ“wâd™ìÂ…àög)Ícö°- P2¸¸‚É8b÷æM·Á}<T]ïg[{%? Ê¿ÔÜŒå‹ùˆËz\ÿ^‡¦^«ÏŠhZÓv„Ž%L¡3j®€<·§ . ï§âû¾P¢ü™}–°~ríè¦E›`Ê®^ÎIÈÇþÕð÷¹#N¼TÓu»àBÖ]È6d›Š‰¾¨¹j¯C"ï5¢CÉfêé¬ 7ÇC0Xw:ÒY‹)Ëû0s ë§¶áEQ%½ÍØ{NÀðæåú·R¾çðcÔ‰#ýZ„üP·$aöLòÓùp¥1~ž» ¿7º¡²~)éžÆë+}ß™ü=ÅÅ×u•pñÎ<öÐm#*"g‡Ìz9Žºör˜¾Ôn­J„;ý©ÄŠù¸nê,ò<ñ ®²FJ ¾BóØS*|LJ¿Ä¥³wƒRÁ#t‹¨åÈ;šÖë:yýW«^ö9h‰;Hrû…éùßÎôÕõpXô7æVts¤&T!RÊîÄICÜŠ>h™Q ±Quì[SìÈ5%—`ŠÞe6`°î̾‰óFn°9 @ãÍ1¦Iü³) røŽ'ýŽÎ,çÁžLùklÀÿU[ʶóuàüòϳ[%pù‡‡Á†™ä³ësèSy‹Ž+ÁüþIï¸o*™sdT}Y‰ßªàš «]ý`6¯û²÷®ÓÒ&óψ€éÏZÖ'‰½¡îÄó`4Œ<_†Jžém³æŠâÔ»Ðl¡>UÌ‚Jߌ™†6\-Í¥ã3 X¥îNôQ|Ëî8QO? ܤ3ÒÈF˜\½žˆ{-¼à—ç/Úr8ªìÆÕNd\ܘàs‡çë3ȎîÌS qìCüBŸàž7iÌ܆,ÊkNl¥ÍÈ-õ/°…£!_™l•a ð$Þ}W¹«® âÚRúóZ“ód:¹pFnçxr òˆÇ^ºÏ҆ϡÌîàD¤6{Ygñ;ÎG³U¶3wV¤Âs·*üí° «lÇÙÒu³p/Lns¶FfÁ’¬rÜG81ÿ/›/ÓŒ‹½ìQó¯9YX3…|y o‹ Ø×/üéÎ,,\~ˆŽ­ìÅGºTBüÇnäÜYœ†/žÞÏN´•ò/—e$F>ãYo}féq²@&‰ÆÅjQŧ§Ø]ߣpGG†¬x‰¡ö²Ñý5ÜEfªÙ€Ï+Žº8?œçý ¢3ó‘û¤ÓµÉ\A=šÓýÝvU Õ¯êæÜôbä;ÉÀÚò°jò]‡àÀº‡o.óRéAÓƒ l2›‘ô øËø9ñ{8}J§+ÓgÐί@A%šm‘qÈá¦kN'Mû:ñç“<Úpñ8ú69aÍiêV2wmndkÔš‘'ñ[(xˆ$¶Ùq/k¢ë<5òþ´9=kûòÏ3K­Cé»×•Ü3ö!ŒÃXöfõnUœ3K‚ŽÜR%¯é°Ð3Ê]§ ÝšRqÇ»½øMj q~¡W4³¸E†ü/ Ü‹©ÄŒ{›®r'nÛ¨ïipûœ ×´HÂܵøêw~·8œ½ÄîÔ«:Ñ!‹™lBzÑfÛÑî2Ø÷PŸòþÇ•ü[/d@û,i*¦€Q~,ŽÎLj ¢‡ gCŸÅƒ½± M>ù}–cw«Gct+­‰dñ(Ö]æC½Ï)ÜÁ.3§Ëu’a2ƒ’ZªTÉ,ö¢#ß7B÷úÃlÔŸo슴(Xxm)½!Ë•±¨fKŸP‡yпEÃ<°qïw°Þ°ŽÛ§ß†ní8öã-3Ë@J‹¶õÌ^»2ì®d爿àèտ“¦6´—ÃíyvŽ”/€ÁJä^§%²4y]¦)¦¡ZÖT–7.óþìÞÜÁ•éu¦K\–RE»uì åh’¼soD’·Wcݬ ¶ý)#¸Ú‰ Ì µ‹aÍ¥ßì¸Ýoˆ™ó˜mµÔ “¸9“X¸£•#Ì)ç©€Ëðå„iÝsŒ=sv´Šà”ZBšÓ<˜WQƒ ëëŽÁš§ÐXé5{tS<v-D¹å‚`¤{ — ¼¾|õ…àDQòiƒ+Ѫ„ÇâÝ©Éf^²í½%{š‰St.3KŠÏÀ’ÖM¨’èÜîT¨±5c¶ø°Z~jÒ/Ú¢ïä{ Öj«ß°V|i†j듬÷ð)HpÿQ‡éök̯ÈAìª$r“>ۈɿPY(Oo?îÇFC%Þ†ƒÆP'AÞ][Êš¯?ŒŸþ|æ|Ê^@ž•0töŸDRøé› ¶0åL7¬Ú?M;°ŸJñyD,˜i¥£K²1¶ ò×Á~=)2`weœÂHÙNF¦pR?.bkÃßᯚ~N˜¢ažMÅb! l7Ìb3G7rÿ~›J;K]‰[‹ iƒ¸ ¹Þ~xËûêQÎp þ©œBn­'š[¾³k^[–ˆ8VÖÃŒš•ùCF¶>]xo‰¯¼ v1zdïê àŠó²,w'ð?ïF&¸¬.¯ÀªatÞ7¢`·‘MmÎ'¿ö“óÁÃø/Æ…†çE•OxÞ÷¥íõ2'lz¸žŽ G%ãZÎv}%ò§²eÉ“ñ:5v¡rÞû—n_ÒùE£¸æh$±»ã„F›¢QÅB3óci®ê%ðËëRÇñ‘æSöÆöh3]K>´€„ª 'Lù,¼-½²i2”ïÀ XGæâL“0þ± 8ÕÙÐùyZëO߻즆ÙTÊ5Ÿ±›]ŒC÷ï¡‘/?)°;N® òP½Ó9™Fx¶Î€h)ˆãìWrA+…ØÜÎA^]ŒÛ}ýl`L déû€OhÀ˜’Ë© 㬊ØE./Uƒ«ý—áú‡ð¶Ážø\õ ÝSfQ1yXîiY É“äìФNvw£íU•øg©D¿xþá$¬ªNösµ˜„Âr¨Ke ŠXÁ×nŒ…ìEŽÈ‹’’áÉV®>»;âØÜÝï!ÈF‚Xrd¨ÓíËЮÓES4–Ò³MÁd"_†¬ŸH÷bEür$¶ ¢‚âM:`Ô†3Z¯Ñ†b4«7—Ä:,Çw4f|)ýh~£V¡HLíͽ€kÌ»ñÈÏÓdsïDk‚Ý0cèò?üz€A~+HêF61Œ|OÙ»÷¸‘±á÷hXq Rž™à]#¤×õ1FÅ R}›RXöƒ †>¯Ø—F2ìð3#ج¾ä沸ä®%ÙtQÂ7‘†>æEáEº–­"%çÁù["ÄúõF~Žq·[G&`¼ê"ûîMhÝ™Ïö/}ùg ±‘LÀC>z¦s a†bÜ„8£˜w»nA™<'_rðý•Õ¤I6 ®îØ‹öÛ Òû%Ψ¼uòû1,ð8G½-B{£TÈfŸŽà­šv/§–mƒ®YñÌ©p‰ÝƒU·ÃpÝúåV X½ÁVka4»´^µüÅ3–Û¨é±ýìÈC\©D‰Î¦y‰ ¯!I­Žéý(‚!WK«ˆC°GFîºÏ  üiôYékÌÊ…Vm+†ö˜CÑBœ;˜ !5íh]ü ¸˜Ä¬Š›BÄ:Ìè ŽŸÊ8Tn!<ÿ‚é[³n®JQmb[uú§q2­&Zû½ö)Çç&¸2\ æèæÁWø4e&9µwrÎ2)ÀrÇàÁOQb)ê‹û¿éÒyzÚ¸|NžÒÁ 'å3#€ýµ¦ˆÇ²µä&¶”Mc²@–ŠÅæãØ +t±/ƒùù+É›ê(À+b´E~=‘Û¬C’ÿˆ@c²*gê³'dUA+©g%(ãëߟ9CV§Áùò)Œ¹¨DDSЗð¤Ò°íX+jñ§ïâÁ¿ú ?¾YìÊKÃçô2œÂqüÉ»KqhëqŒ8ò&N<@“ŸVð‘·<½ÈÈÇ?Ü 9Z$ÿñj°æ³?È=Žý‡ä°ª‘þu³BÝkZ`⬠A7›ÁV}œ™JíZý%‹9‡UOBLaòëk£ÖïƒèyÍ›kâÝsÞjª3!ðãà&'‚æôËÀ×ïþ#z—í¾t“©ý¦ª¾¨»æ,ø.@ƒVÙ‰MÙ´îÁb½]wz©Q!zê‘]”õ“t- ¾o§S¹“ÉøqËW®9™[ñÎŽgì _þ澋#6$à {éK ó¬Éç|2½/—Ý”iM¯,,Å·¯÷°?¶‘ å ã,J·¬·!\/’Õ¾gžúCÊ÷ï¦üJüÈyÁ¾éÛ 3ãH˜æÞ¬7 A×è«ùüs}v×À9Ž yv`=3ãí©>ƒï£ãÉö¶_è­ïJƒ»í°tã24mÆák0½Oöm (>—ìc ÉX>‚ì°mÙ tsŠ¥7¯o¤aúMdÌq9¨m³‘[‘¤±4—Ùo ~›‹á•Ñ|úê õ·§žE0!ª€½à—›2Éñ†>ï$ìÑ΃yy l¬¦}ìªFŽÍ¶ÏƒÌn¥äE˜>MaTáÕ“h¨'§Il{$1»WóØÝÞÔ\£gí³î^´±aý½è¯}äì8[ •ëžUÕ°ÛØƒêp¤çëTiGG$UyÈzOD ýj6Y<ޱtÛƒ s‹Éµ€)t‰T$iNrþã^xl6ˆò–á–>òêF!&H…b‰£¹wØ*ý.&ÿr'ø¯C¾>™Þµ¹ÿXw†-¬á݇%ddšž©Š¤êë¿BûÖù“ú¾†³B<[‡t˜„é³H`Ë?6t‡ \Kal?š¡à‹U88ç"¾¿§G§í|#Ç÷Bí`+[žQ+T’˜Ÿ¹983gÏaN ÀÞýq“ž>Ä”M`ßf]ýå8º™2í‰#Ÿx9«ëN⢰V¸yû(´`¯:Óü¢ÅlÖçuèêxm‘$ÝòâäÐUˆñ1 /Ú2ðzõ1v‘ýqо÷ÞyŠ0ùVÕð€{&ãàEVn¸ÈÇܹ}Ši; -[½h{½Ý=ˆþW™Q‡nø-HÌ>m úêdq@Æ:˜Ÿ«Š©L)zýXH¥WÄÀÖÙ.ÐìÈPÅ¥Îè䥅dþø…[Ðû‹{Ÿ‹ DGš\Z‡ÂÇPù«+5ꎠuÞíó¡3«™ØOß¾bætj‘ GÉÌÃÖ½¸.ã$fìL‹ýߘú»Ñ×Ç…^ÍB½ûr°%Á >˜RÓu‡ ¬P•zk§ á P²òtªaÄV!˜3†ñ7·ÓÍQäŸáu¨³Ïg\¦L¡*±æ˜1«tÀ§æôª®¶°a!2L×Ò4"*—š¦Ò{qÄ¿oR‡øD±oùØê«ÙQ×+œ{‚ÔIÕ„p!×¹BeoÃ׿§±>è<óì¼ t%דd¨v±%>²T¦g'âééÐ{ÌÖDûÀ!òa3M¹¥BEn¿Ä>Ùl8Û& §w¥žÞ# œ B3OE‘] jëáD3wyå–øu– 1¯zD£½ ˆ¾F/Äï'kr’øî“`¡L¶¹¤á¨užd¡}£)’(%NLÞm&–+ê¨qî{°•3"öeí°® ãçh’»Æ Hû2É}ø™M7t©+áÔÅö‰Ðdsý 2Û²•øðè@å/*~‰æ["³+Ÿþ]ÆGþä½’Z0d–*‘;sâꆎ[ I¹Æ{”¿FkÚ’ºˆÔ–?aó–ÙÙ¼XN\¬h;ç÷š9ÿú–‘W™Òœ7¾9ä·Þ-HO5&>å”6'³3ʘ6Ûïä…d ±ðyCÅkJÈÝ=âôªÜ)â<6À÷œ'ßËiÊ„ÙW߂щW©ªý:̿ùÐTGzž¼&›îœÅ™ï=Éû%ñ¶‚=i|ø^oP† ™T3Á›«íò×?ØI­–uÚÃåéБø‹]P€KMÞbÆÈoέy3ýë¨ð)}æ¹ ëÖÀ”‡Á@H“yD•‰ W¦Nº¬î‘N˜_#‚ízã()Rª“s3lCñéÀ}‰ýìü‡n°³S‘ÕÅ)¢aâ†B´òãkhó;ëŠùè•6qpZéF¿"ÆûßâðÙÔþáMà=2‡˜®lß.O®§iÓ_ß§ãмV48e‰J¼qXì± *æiBÌÃaæ]›Œk£qÓYB7¶²ô¦ÒÖÝ¢Xú«Š®­×"Ïzç@Ïx Zœ=Þö;ÍË1tm(µÊ\H8ÂBt4{ˆS3ÍðÇG“»rñ¤c8ÞÜs™htœd¹‰i83⺙F+&àfçqÖ¡:fŸ&Ê/¶Óá«Þô\È?®ÎOipXvµ …è'Ódèx@$šq¢¡VUïaãgvâŽqgj1® rö ÆëE„²açì÷øã~:þûÁºÄ-í Né5£³vœñE5Äæ?5,©$‘”V%€ÝmD&T› }aäÎZЭóòò1³»,Š.†Pž8çüI(°[FÛ3ìÈg«¸À€r§%¬Æ—êZäÓë`²séç”;Ø×Ù=µ™à¹ Œîå9Ї–©½vÔÞ]†üQ^é£ãÌ.{×OÔ@ý-yf(u*VâÌsYŸLo€eäõ7q=Ý9¡· àñ`,X´F³%·’c{AòÖ~´#$å•ìï«ËgqtçìIlý 2œŽ;´ñÔõ~VÄÄï)ªÀG&Y÷6ˆÿóÂŒáÉ[Ì’õixï£3ë¿ÈÕò‘\î(º.—…¹> mK´€&"¾ó´cÓ>@„|Á²jCÚú¤ n›MoºÒ¾f5:nÕȰÉÀ^äî*]FÕØÓ\™Ç¦ôæ±Tn¸ ZJa‘@ø¬&¢CqdåY„ça)=±ÉŠø.ѦM1÷Àá·æä|«–vTêæ ŽÇ´tö†çLº÷þZúyà&Ic×Î=?}J¹:'=¾'‹u ³ðšÎ.r}u+œ+JEõ£™d߀4µÍ| ­«ÑD_’´î3!+¢ø)‘ÌùóÜ•¨z¥ý½ÌŒZÙÞ@[ oi• þûÙ³6ºtÝ`µˆ´[Ï¡E|ˆåCl9g‡>2}˜ H¦ìIfë¸z¤óÕ§¶Ý2„¾¹8•'¹ÆtµÈ¾ñCL›Ť±ö~é/¢iø‰Hñit_;'s0,mÈ™üÕ4¼ÄŽDs£sß_€ˆàÃhÚ– fb;™Ušý`éI»“ÏbÞàoö²’¹»ô“â2“©÷°|}žØ¼ŠVMŽë´:jYÈNŽº~«/›q³r…Â’*oöD6Þko‹ÐÄCÖKðÚO4ر€x(ËP‘ËîpôÂÒ{ƒ¶B?>^ûRò`óÑëx%% OÂ$y3{txH«‘h¿Ÿs» ©ßÚFéâP6[z>û@¢Âaê´étíñ |y¦µß0U¡h$^¾ÿù¡y‚#y<‡,qm`Sô,àÖ¬3T7ȉ<û£Kïµ Ã›<%ºÿ‹8þŠÉpz¯cÒu}ô‰(Å´3‡aãïLO «ùÄÚòHÍ¡¿ãÌ芗=ìñìßiåxÑ8«m¾ê¨;·‰³Ö¡F7dCÿiNÅ‚F¦ý™Üºùæ0å-gvf£Š=‡¸¾=÷,"á¬À8‡;㢻&0ý\IÅ⇂Ýp½ÜFÿ­„é%aô©ÞBürÿëüh9™•ƒBï>3·\,õj'ÉNÊÖý4<05›=Û&B—ñÆ¢B‰ Ù©¾˜9Ÿð‘ ÿ±¤âšCräû…õ4OÜj_M;o«hýØœ]T/H ìn³÷ʺÑEè!ªLYؾ„#(ÊC£žF!/_,fºßÅE¼¥¸*s0¯ç|8Ì2­—gÑ÷ª±òàt‚¾ãLùï ò­<*AÔk”»r×*Ø0E˜þÎ.ƒÉäŸ8}µ9Ÿ^¼öëgS³ð{p=¤?Ö$ÖÏC„ã³"‚XËu¡Ìï·ÜÉسšŸVìÐ…oÒJT¦a:ôðú$e‰>óI⤮T'—8ÂÔÜ[‘nÌÑ%œ¯ŒzÂb"º1Õ$b eéLX¬BßsvOÇ>p´OÅ ’,N¯ÞMwJÛê…æÛ~³Zª\¹‘Ý^Kð1¯Q@1‰~v—Ëf[PÙâFýò#6ª’d‚o+é:чùª!íò¾éþ ¢STIña°S­c‡øê!äiÉä:ýƒnù ãåŒJï7î…C¾Pö/¼[À|ó\/èMŸ×$“:u/òtƒä^æ!{—Ó©ÃÞ°¬yÄ=9¡ (­O¢ës¿¡˜ô5ÜÈ» z>ˆtXJWáÅYs¨Ò]˜¥•ÍØ[ï¡c:btyè*¬mþùÄÍì¬SíÅ“fy0«TˆFv'3ÿTÑI€@@›4¾ŠRÃ)_m™3yÃÌ6e~6î;/MÞ~VLD³ë ±–bSiж6p ~ÆYl GU¿Üdö§Ûï+Ð?á2“~è>Îìå£ê’8s…î7?Îü—OÀÙš\˜ä!eSvìÛ 8øü$›ò3é9Ê11¡*: 0÷ú*°¹²­GÀXD€è‘.ˆ¨÷#3{£™ƒÞ'`÷fN‡àOWIº–£„ç—î`½–'_²ÑŸÛ"œ,ͦpß»ߦ¦‚Ò‰Fçßnì»&C՟̧s·žfE+9_Ô´À7Ïžò,ÔÅõ¶–ôå†80´÷pÔɰDL ;„ëE©ýÊùÐX™‹²—7Ëøjê4ï0Š,:ÏÞüü– Nbm6)Òÿ>Ç20B·¦s•Ó~2_Û¸rœ›¨ð^‘„&fâúú¸³ð3¦w™R¾SŸ™µº˜v–Ÿ¨½Íáb€ºÖãþ¯»¸÷u„àB1÷ÜÀÕþòž^Î=ÉQتiýI1uzVGôz0åw)î,)e®gÂÓQ*x Ÿx̆ kÌ9µÉ¡çfJ?Z3héã,[Ï^k®çJ½» áK¡áo.L–¢u]5hÅóo}g;<£±ÿ².´ø¢Å{0èP'Ú±SÙéRàGøZˆ(ÎÃ;ëæÐElêͦ‘Uõ¸t­1·«a¾ÞÑ"=?fA5G–œ-(e«\±Â B†Y¾£öœéÃδÅx)f\¹ G‚›ÐQT‡0š§üj˜ê®XLÓ³'škìh¸ùÒ^ǃÁ_ mÎñNâÍâŠtD¢a±8]ùTÌqœBªûyè‘àY øÛšîùÖ æÑ§Øèé‘ÌIejgûZ“qtÏç¶óƒµ4«$9ü' ¸”+cý£³lºˆI2"F-ì`ÔΧÚEx@¥–™ÊRÕ[›¹ÁE¬Åï[8+ׯ.NÁÐ5¡Àù³ Œ¹¬ƒ_&(];H¹¾dšÕ¶Ìx´,„¥o¢Ÿ(ìð¶b^6Ì"|SãÙ¨¯ZäÉîTúø\œ“tÇø¬HªÓ«NüÂrÇF`E·ÕçÏîöZ‚gjpË^¶ëÄ9®^Ä®©M¸½X8éMOÿÂU—xˆÐøJ5² ÓÌŠ~ü >;±÷M (È·ÂÀÓ“ “_³Ï݆Ët<~ß‹¼‰MeóßI³ïÎÂW~>ÔÑmDmËPRDö=Ž„ŽMç˜j^gìZ±“Íë§«Æ`Õ¬tÆñ³"›•l„dw%šÅóqë9I"²©öcmh ð<Щ G2S u÷V§ž3ƒ@ÂvŒ©~ v>JdJW Ûòƒ—.(vÅc¬ ™íÒ"³ë‘ã¦Jsþ0ÒœƒÀÑi3×_NmVÞeí}þ2ürÉH~܆¶‰œ4<} oN] ÿÂdÉ e4_ÄŽ*ƒ¤ž»Òñ(kˆ… }“Ö»ý§ŽŽ‘ÞðÌe:Ÿb@mBály qTA ñ;§â$æùo["ðS™$X‹Ñ¬Y*цR²óá¶çare¤IT&8dbß–på5}ØÅø¾–âÇñtW舉Á8^beÅý[!‹ÞöàûÉ ˜¥Žf×™1*¤ÏJ+·Ö0« eQ«“ââ…YLÐTgÈ<¿ô¬½˜ «”üȈŠHc_´7U&ÿL,pgl Ì —¤ÿ}ÃñªÅ¬¢èb÷ÙZ‘ŸK7sµr¿r CC¸ÏŒ0ínÿ §"„DÞÂ(é<˜zí"}'Otß“H#mÙ ©&Ñ[â@þ_c½ó ZÎú-×âõìÐKÀ”ãÒ´ñëZbuc[xÏUÂÉâ_¦b@ü"[9šŠ•ÛþávJ„¼`‡ôbâ—§´k9›V³QŸÛ@Óéó÷B ;©ÍHïÝB#G¡À{~â"UË:Ìna~µi@v›<‘32%çw•AßS%üñev¤€+SdÉWY¢¼º‹ùó ·çœÅ³:òDv^Z„ÌÀKj×ÔÈñS¢î&ÜÕƒ Ç·á…‘ÓlRF#;ú¤ÜÀþ•‹¸y[Ú„>BYJÐûh É—Báðþhûý„mcåºþ¬éN^ú¾r·_h˜‰2ne_ÊÀ}ò4Á£õ÷û`m¶à<²Þà8.~«GóŠXNñhsFøSP{Î¥3•½s˜ÚB1ZpóÇeãKæEôfqöLìtuÃ5þY… ÎP˜UÍ(:Ú"o¬ÈþGí“\q‚]|ð&Ú>ìëͰy—&þl{Ž‹»êq¿€,õQʃ;?Ðï¬<8ÂK¯]–¦¡—c°¼üº[…\ÊQ!AÄõMñ\Ó:úÙØï\l»!H™ãâcÀ,´¢UOcáZ¥!<µå¡3VL¡E¯p³+iüâ‡:O§‘&¿:Kç14:¿ÇÇ— âÂó´É_ˆT½=…UÛä(FGÃÌG_Т]NþªƒÙ#— ¥N–zY’ ]:¯§‡óöe6w®ÎCÔååÃM?"ñŠ–(YI×¶Û xâ¯,œtÔ¥q›_‚ƒÆOhv«C½WUÀºç0…»~€êƒ2R$ …êi°îÛZ²Ø¿3T§KênHSÕ33ÈǨLrÏi) sœHþlpP IÛwб\ÝÊhÉOL®ÍÁäËZ„’›þ`ð]„L•{Ë b9_w«q¤»Š1ä<Ëv„ÞEÇ2íº1ø*ŠëE@ ‘:Šö‚Òø:Œ:»ø¿c {l:öìˆù?ÀOïæÐ—Ëá¹M»š³ïÌéÇæop»WÇÆb˜}Ö•0g¡=]%žÌ½qì »ÂQ‰. Á‘#òvzsyxI½í½¸Kú(S¥iŽug$Éëc/ñ[øDWKÃh8Í2i'=µ’´.Û˜‹,¼ÅîfeöüÝQ€),xêf…eÝØá=J¸Æû'~«iâFïQ¥aùÓØÔ‘TtI¸€k&Š@7\ˆH­RclT}˜wzuÜë ^A\`“uù'Ä©‰‘ÇKz"~üwœhb.ùù1KVNÀ´©Aö‹ ­8ÕŒ·©3·ÄéºDãm¹¸ÒÉ3w!GŽsD-D!;è|…ñl÷Çzeòfß8³ÐnY¯ýlßó‡ÊàŽË%¨ å£ÇFF9Z—ׯušäâØ427¥‘ù® å-lÓñ8âÆÇ}jó¾Å.!ÍmvÚìð÷ác”ѧw&¹ÑÑ% _.ÅØŸ §‘@þen”’'–ŸÃiÇHw™ 0¶:tU´?Í:‡“[ý OÕ4òaTžÜOiÇä ßpN <¾¸ÈF‘M‚Ñäîìà¾éb¾Ù­£†zî°ñw-ªnH¢CfBDta6 ¯Ö 9ý´ùËTæ„!!² —qÞ+^,Ö¿Èh\ó¤Ü‘Ä6ll¯iÅÅ®¿¡º= OÜfó¼¨_C:ëõÍű€ùçGµÅDÉ#sžB:Ï,Z~ãsjš(¨%Wà¶C“Þí´;¶4BÙX?;öð0ê}©b Cš«>¹êžòÕZ¤éê6ÂZ‹õYp&P’F~Fךiô‚Úî‡yÊ1œ;÷X¬+OGÿeY¬KÐjn²†Ø™ LhøgFTÊ?ìr„#«ÃÀY…â{uÈ÷G…¸¡¦óÇDŒš=ûޝSæ:œ_ëH¾®œ]¶¢D|o öã¡¥cÑdïZÜ,…òfì¼&´ÚfIÛ`*=ØÈž*\ ‘ûºAº¥ò•èX|#'×­ ÚöK\šËµdU>âñs[I  *Ù¶î”Ç3³ãïäè£ äÕ !r!ÿTïd4»uÉMÕi$õÙy»Þó—wpÿ«“®ßr  vbíD0Ù:6—e—OÊŠG­\mèΫGíÏk_f»5y’„NCð)Ñ—­•8£~gi˜x<ûUSNüœ‡ggcÅ»‹h²e#¹-;‹X ]A»OCxÜÍŸîäÛ…5#Û©àÒNº#—}·7ö¾f {ŒiÎhWïØGJQ»O›@ñÏZæ!A=šk`ÿð,šz~5^»øªÊ“K¿W²û¥0{åÍqÎÐÌ?³‰úM'ý7méê[‰°kµ( ê“a{—¦²'6&Ñݱ‹Èª)õF%úÀ¡‚ŠÌË…T‰uÚöšãëãMÚo¥P“~<×o?]½àãjRõò,—ˆÂ€»žt‘Ç&ÃÁfä'O/~f{ßDС'Y‡8YZc¸‰8¡¬"ž¿/AÒt)÷¾Ý± KÍÈ'V‚29·aæ¶ã)¶÷F+Þ}jÆLW]€Öb5xCè%+âÿ€‘y†a:Íðãˆ"é?÷N,ùŽm1ZtNÛ;Ð K)Š!Õ¡7[–<¯7'ET6™7Ó aÖñ§lÔahã„ÝÆöäP„3ù,’Û óè¼óhªc)ð  ‹æePÕŒ!`“oB¤ÅLÜnI6Ïn¡·D7“3?ÃTÿ¦ÒŒÃ=Y$ÓÔN  ÛƒØ\ë@ˆXïŽ÷»ô˜”8SòmCÙrþ&(õcÕè1\0-Ó¶ðܺÿŒ¶Ý¶§é®c˜UÙŒÿlª9çäHv§!®A˜’eåÉ©Üh“û¬ÛeaÔÒ û"vSÂ*@NÍeX+'GïÆÃéw+IìÖPôR0FÓ_¯a¶l!ÑÌ™Fï1±tóŸÅtް'è×|äÌWaò¨ñ5þ»ýdž€Ú†Pì¶1ÀLa_4­œK­=Ã/31uº __C•ò´Ûä.sÂ!£ö,£îÎfô´ÜËìr—$?eWÒHÿtfÓKuP H¾eæ´;m€sâ·8ÜJøŠWÂòyeÆà€ýñL;c„A$@€u_£Íݯ(AóCKàµÊ®ÿ˜!kL¦Fþefò²Û¦ùLz®=øçiPÛ‘³÷a«Â8nýíF/ÍÄOÂb$0˜ÂÍ7—ð®oÛk)x&ޝ^%Aç*–£Ö{•e$°È;g2¿:».îü ÌÇ®´~ü¿Š^{G> Q¸àÇìÜÝA®d×àWnûl4»àC§çM!%^W`SÓÙÿê¡q¾Xµ§ÛŠÀûÑ=vUk®;Êýþ yýK-Ed³>Ô=A§½‰¨ý#fŠür°þÝá°Œ¨û½AŽd•0å=ò…íêÓCÕœ'øêͲ®‚i¾ÕÌÑ{o†:Ûïp3…²02éÔXï¬(§P¥Š8ö®úÔáÇB»wÏ›—|È¢ƒ9¬ÞH'³^UÞÝjNÅò³Õú¼4jÃ<ÊúÆ‘·¹ñ(#u’Ø*ôsŠtßpçŸÀ§(ÒcÆCýåèä~f¦`&T,ÌçœP!sû̡ۼtEŒ+=?{;Õò˜‡:‹¼èÏÌ)m/Z©P)¶$ñÐkw?1»=É;Ñb`ö]€ºÏó`Ù 'xf‹ó–©p›øÍQªQŒ¼_*Dß@l›Mß:¸¨~›‹©‚Ê 8]ÚЀ‚ß^¡Zç <¯w=¤Q„_Õ–Ê×°sÞmḬ²¢Z+Li_„"{zð_“!Ž;w3¶«>15ïñŸá|àL°gaîÁ»ÈNÉ|x­G¤âÆ3”ß÷³Ê5V-•cö‡Ô@JûMülkLüå È·_‰˜y_ŽÌȈAlj«¬@9²Õ·±F^ŠÞÏ]H_™K Tpö-_0ˆ_s×Ñá/õp¹©—œmfwÊyÒñG»HÔqgæKÞ^xs·Mg‘Ñl|1=̇w§Z”³åIߣÇ ‹–!îôúyÎÓ†||»KvBåa´Oˆ—V—^EÂêtN×"³Ü‡^-é`WÔ¾?2×¥„-ýÌOÒša\ÁI¬¾-g?jKðmfnqÇ~M^º«a5É.äT€gŠûÉBŸuT~oÕQ%>]7@ÿ€"—{ÞƒOp5{ŸÓUÃA#LYK èéJ\¹°37z‘·Þõì´ïGñìI I»ÈýçÊTÄõ£¥¤ãZÊo»ÉÎM½‹ë‡jAõ˜6S$)‡æ«s þÑ2æm¨ý¨êN¿ë™ÏYS¿è3{à–(Q8µâ.ÓI»È´Ä—ðìù)¶j4™1-R‡ÿ Éøî9'ªB²´(¬š£E"Z«ñòºédvµ:ìŠÈmóµË`žfîz,§ R”½‘Í ÇX“£Kî3×EWÑÉ<øÕÄg˜å“œ‹lLÒyfÎ0ÁÈ.M¢çÖIÓW ƒ=ørÌ€l’T#œªéÔ¤c ™µ×…Î:#OäŸ62‹ƒr…W"âãmL³p f|LÅ…ŸcI`¤°Q&¤öÇ;fz^ Ú¿– åæÖôqÁþ$Â,i$%/ŠQÎ=‚~ç;ˆÓ|˜U‰Wð›z'ðRéÞŸØ>S€îÞ¦FçÉ|…—|ý îê}AaËgfÏ]Ú7ÌÄ[.cWú᥉"nFÛ\Á˜0BYC¬ê&{ròútؽ+RU„Qd½%)úmB=³oœy“sè›|ÑT>²êž!ø4x Tm} ]³„ØTÑ»,iq¤3^Ï FŠ*è"“~z©¬Å/Ú6W®Ç&PN™ m¾ÔÏ«5Gq2ÿw${Üæ3[U‡jÄÌÆíWÿaËS;ŒžÒ†Ï›Þ²ãÙÓátP k¤‘Ä.« ŧ:êdäE$ŠüQB•/–p]§ž¾Ô7ò§é‘½@ÊÕ¯^] ±›*°öý ÎnZ9fòÜcw,)a&êŸß‡!¥CÌ‹¶0ñ‚‡¾ÍŽfó¶-&9n>ØóHNÆP=¹ÄâG:Ô$r|·þƒ~;˜?¡‡,:qìÔJ||Mœ >h…Õÿý—Çu*]˜wdê‹A5ª^œ¸V^|Ä«L˜Ü-߀9›ä‰ˆÙ+üD–KŽ™¦5ŸðÈÂ}º:ÌñÀÿ&Jªµœ©™Ð|LØM.ÛCʯ‹Ñþsfé¤IÌe÷º\Á$a3z{i¦iïÇÏ¡{13Þšïf9rv5Í"{üÿ±Þµ‹‰îµb,pöü°=¥ö¥µæk½y£<gBÃC-úªW¾&(€DFS¾x'å|~ÇI¯5…æËŠÜ¹[µðU¤6ˆmxIi aߌ£¨Ô½ù¬æqOî&ÇŠiG¼½\´˜±ކËE]¼V…i1ìÃS5ì2kº~¥>–òl'·º“}ÖÔ”7>.)¶ŸM±ÛЇ.×#ß]Cn /Ébò§~1yóž9¾ fØ~‚K©Ç©uï>°kþÄšM‹Å˜ÊY4³é'ë,ºê\<ÑQDFn•úeq(V®M§·ä©ý9$üˆ »ïKê ­d-×DÌ–þLh£æå9 u7¾ÏŒ€ºê$"ä=ЋL9*õ˜Fíhg¯—”‘Œï.0ým.±*ÌÅW)ôÔW9Ú2Ïæh=Ǽ)®ÆâNÃ<›ÉO¼%])ç½6Üñ¯‚ÖàÉ/p/Y‚ZËh@ÉÔšõp1ûtêN(«\¹Ãd†’d=HAÞ~^Šëp-ç¦G‹Òp~žäCz(¹¹jnm~óò3èËŠF.E TH’{®Ô _ꎟ$.šoñÑKs’Ë“†'—zrtj!Gîú½XJÏš “d[_º)þ•;pÖ¿& ê>4ûÃ4zÊý ®ü¡N7nšÍÆ Bgzöm… »ÔÉžR?è>fHnFƒ`°Q›¿_‚4k+fެ9–ÖËë'×È!H<ÖG^f˜“ô¥ª$÷3Uj±GãküÌ+£«øŽÇƒªUI’Ç~[HIÑEâ|è˜âH¢ëÃéh>>òžh1‘E¨ò„—t?Ô¦ùGôˆß‹>¨½ ¦Ès]ÓùHçMÔHBc¿Þ Ñö×ñ]½¤¶Þ¬Âã6&¸ÆI‘ô ÔgÀãQón ÑB…¹x}qË·Rˆ+Ù¼ná¡ÇdzA©!öݼÊMmžÕÃÍØøæ0çd/>-eÕä…Ø,¸ý"Aö½1î>ÛÉöÊÆì¸Ý¿PkŽU`wð(üwý¶é¹øïètÚtª×y½ctÍoàìå7ÑöT99xW‰0|Ø€À5ø.â¦G¿,c›R‘'V^iàƒ;jdÕŸy”·G|î…ú%(±eUáz­DTíŸah…þÍéLzh*Ùzn%šÆ ôo¼ÉØvÅwž³êK”ˆgô7].Mê?\„S$éÄçdöô“VÜ6©;î^¸„ƒµ×áQP(Ú¿¨Äë—ôÈ£‹}øv¼yÑßÁŒÞåLã¾ §ksɳyÊdQ]3‘¼¬ r52tª¶4|̨ÒS¦Ø¡öµÛ©“ò"Ö3]˜¤¬+Ä®EIÔØXNT¿ÄòIdMÝ&óys9zdþ]GµæÑªÔN|´w ]ãÞ€Qá8‰®ê`•ÍÎ2ä› ø%C%7Šm.Rdª˜ _r $ÞŠ­†Ì{tŽ}óæn.ýù0‚8d¼a£FÇqþO² àÓV©†M±”:^]G,î¦ëí4È×-ABk³Χ×Fr•\mˆ÷‚Çè›ð›´õÚqæØ ƒHNêàuWshò,w™8êýZGWèRÅùÇÐvŽ Ÿ{G,Ê'p$3gRCz‘ß³²IÑÛ…da2/†ð‰Mo;‚\ŠÞg0Ò·Y,Á›SlÐæëaòÛØî|ºŒDèò´?+¾MÿÉí=£X#OL^Ü‚Ø÷Џ5/’œXû™ ÍÖ$§‚Ÿ`O›ÌÍ<mEDë?ɉñ-S°hž*{"qUkwàô'ÊäcÍ 0Ö«ÇãwãÕÛÛɾ8b—ëLmC™Çá—™ê'ËèžßrÔ‚ÈR^m7rxÇ}¸ªñœ9[ÓŒÊëM©’ÉbRô=Flÿ2Å;ž³Vff¤¥Ã€Ä#ÿ9EÚ 5¤þ.„Óá&°ú‰)Õ÷(†µêœ½Ò¡4Lz7Èkˆ1SZÓ¥·ëÀÔɇ©ü]¹55ÔíM.Z?\Y²8£kÛÿ‰RO[ ,ø­OÖ®\Cú<‹¨FÜ]jU¹ŠÖÏÓ‡3åóèñÄ—ÜM¨þ$•Þæõ÷‘UìÔ7qܯ~ @‘ m¼“Ú3£×…•ƒý¯EPr÷^¨ò$;èÓ³çèó·/ÙKÓå  z’•"„¶Euc†ñVj[,.»ys.ÈÑ‹+Rpy‘#™^ßÏzøè©O™ÙF‘,¯Š+,—Škúj°¬niÊœFó“4©u¥/5\|ŸÞÚõ‘å~ZMÞ×DÏ ‰'·’Ÿ^‹qjC~–4£õ'i9SisÙ zwÍAì†blï8OòQ'å[õ83BNÓwe)úþá\™‹'”£ÈY‘´r¾ bàsš$i—L!+œõ0}Ãw*ûÙíGº‰tû{Q¶rž)I¿À ²çíY³eÛéµµ›Q\ù6ß0…]·î28ÕwЊZoËÀºnâ–Œó“„hJî4&HN—,5ÃÑ-méÍð[@Žº•§â£X%:Wñ.Ú†ýAï{|ß›Uøè o%r}HŸôt¹BJY.¹ôÜöÌ¡ÊUÕüëØÙ"2^—°¯þ*›ãxzÏSŒê±cÓMùI‡sôv‹Sãcø…Õ¤[ž>¨Ñ0¼©IŽž— ±±¦TŒ¦àƧÚ½éj Ó$þBLÜb‹U?\ñÛ "ù’.DŠ|:YžÕà\° ˜àPÆ£ìƒ=»¨Ì—iäùׇÿï·ü’u"{|z o˜á¹)È×[LŸî÷!ÆaÃx¾x5黲ݨPNu…!£¶;¿+s5¶h–ÁŰœ? úÝ›~r[GÌŸÂfc/vÍô˳è&Üóib0ͤ~Î~ÆçMþ_îiÐ,%uåðï.ž.“|“ÞúÕ«±>aÍÙ_M§mŹ2¯MyœÈjgW4:ËC,èOnî«^)Mâ9ÆÄÉFŽ]=J6©øƒX¾,ɸ ?šÝè¥u6tÃv32{Övê—:çÉg‡ö·±>zêäO¬˜ñ…BÒ@÷¦)e-þáÍeÝlÄ­äYÓ7FêÒ*2þò*(îwç}Ådó_3*~Ì•Î1ŸJ¿mÖÃçvÏiÑ79b½œä®¹#ÿ¦‘ö.šž¿dN\%ë¯ïG:¥ÎZEŽŒ%‘yªUðƒ'xßòÐÃ}l\Ü-¦'‰ŸÄ+îÂ߆2ä™Á оqæ—n¤¼{ øñš=¡JÛºÿ²á÷µéc¡x23<™\_mM.vì3?øÊ[…&¤Há³ä>Ì‘ê!ù¤‚Œ±ú$jm9¦?>kÕ±!Ö”DK ¡Y,Üçsgk–µ¡V¾ øm_C¥[mÙbÃ3h±Ç ƒ ´iÐ"w²`~‘%+©Ñ«^rZ­Èšòãä–q?Ò€‡Œú™Ö ó)u=ÃÙ¹$ÃL—š^êe>È3ëÆÇ˜Ðœ(žÕÍ ZþS ITkÛdÉ.+g:t;pçSUü%xg²!‹4È›<%<œfN –©Ð5‰ŸXÕ™Ò´w4ž‹Ê‘E†3Ƚ¨/òk’–ûšäñßKÀú@wàÒðÆ€âµlúEåm”–&^c–m‚ÏëƒDüøLåcÛþß=½Õž#ãih¬F?ETÕL2æ€kKe±Ä¿•\2$YöôÆãföß ?0xeç¾áúj{T•á!žî.Xß–Þ•ÎØ/~ˆ²WyIC¼$ˆh’PIšj…§ùµ“ô›$O®úÇŽxî,/ê=ÀZ^#p~ŸÇ˜Ù®§™Ü—ð9uˆ¤&3‹ÊbH~Íbêu_2 EȬùá¸Âî-¬xË6–Ì#|‹–àÌoVî_Òǹ»îpR²Ž@•ê'´~28çG°ž}‚øÕç@Uϔ˙Efd>f±2“]¸*·")òêôÞÎ q‚f·#ŒãÔL2œØÅTì¡öú¶„·ËˆV8ž€Íï­0Õm›Y7• ¯?Dƒ†Šðû@T^­G+ç²øÅ‘|˜ŽOVIÐüñÏh­>‚ÊwmÀ>ë$fœNÆZ¯LDqZÙ Ô%уôü™JòwÆÊ9³ÑzOT¾ µéê„Ïæ LÆÝÕJôä"cêÓ¥B¥Jb¨Ç‚?m!aö„×C¿p˜ôªÄÆö ?’GåbGÈIqºr@†ž»¨Cë>KÿKëÐåð<Ø6ZêˆÇš¿ðK6‰æÆ’¸ù¨²`ˆKtPþs¬C>€™ù=°²,g:?Þ†Û¢ÇÉ×WÛr›wE5V°SɆ(j3õ ~½)Lkå¨äAº:Eæe˓ٽˆîOï`Ü/è[’¡‡NÍrôð ’.ð2°>ž_áõquâX%ÉÞ±>— »qëøUÐY]Ã?Ú„coãðE¤ññá!.56$êÄ&h aÏÍ„ÎZj×ýe‡©ïþ"Â3¡ÜÕ°ËÒãD¨X¶ ·¨Ï”ÎâñÃYβ4h—3ñýÒBånOzÉäTe!Qå¥×oÁÀa.Lbe´þô`xêZ삲[ýÑÉ;Š”6Ò°²\»w““òè»™2享*NW&V~l|ÌOƣΕl79о‹g³ q9sVÂm¾#ÿçæJ𰺊,LíoÆÜä³ø56ŠKÅŒøÛÂY÷`êïl†>>/²JOÆ×Màðã$åìLÈÒI &k¿{`;±.ÞôþC›}Õœ§j$¬ìˆø!ú¾c¬Ÿàr}Œy‹—<ÒÉ£Ìêürmû³‹^*Ñ®=ðqµ:î~¶‹ (ÒĈžµ°Ø)†#¢â…âû¥1wÆ £¨Îø:”±Z«wÑá [çÿ» ÷¦QxAŸCèš•À]y ¹‹`–R)9Và’ýð@þ8~X£F§‹07˜°'&~»Í2¥N$ïè"ŽåtU¸ý£Õÿõ£ÁÒL˧07mJæ=‰8©Q'®þÅןt Ô4–µêâÖéÉ2Õ¨CÈ GóSÁø9j'ú,xŽCO"Ø¿J\µa*Ù÷Ðÿ´„a¥Q0ÔZg&1·-ŸmECcŸ¡ÚñÝ„½XH¼Í¥˜”ÝÂ4÷ù²+ =ÿK•\[íãón£°ž$°sùñvÀySŒ‡ûþ¥c•‰_~¢‚ÇK|wTŒ.p<ÄÏÀzž X36^¿û6|aVé  Ôq¶ìÙrrMË“|f51§* Â×; Ǭ”o[@ƒòÖ“ò6pÙ ”Äy%ŒZ‰$ñü~ƒ)pÐ!¯ûÌIN• ^ #þƵ÷ Òø£qóðA8!Ìy± ç’ÌìL{³(ݲg-7]†·ÏlÆKÏ}`M‚8™ÄÃÊÄtBÁÇPæÁ6hØþ«Ÿþm´ zæÑV÷Í$G øˆÂ=bÿ4™2Á^ÔH² ½Wà»'ú°¡q)Lt³Í£à¶œ$ÈjnD‡åO /À™-úœlWÇ ›Ô­À¨þp`&UϨ€­öʰ#X‰ôŽòÒþš¼ ÂÐd$E¾Ïh£#"JÜ0"F_È—â[ÕW¬«@#3y-=°œX¾ Ký©xïR0XÈôm1§I½Õx娿Ü\qº ŒÁe,ò½Æ¤„ Ò(1qbVX ºMšÀ.œNE›µT?²eÝJ:ÍX˜-8ÌdÅÞAÞH~’së*|ìøÀÐ{õp´-‹Z¾ï…EO¸™‚”wŸ ™U B™ÎCôJ3GÛ{ØÕ*±ýŒ9.`ÅØNsEœå·;GØÛ÷ØÕ5¸Äcº_r ÂUêd»Ž4ý»2ä÷¦±| ðU—³S!@;R~ƒ—¼%¤©°^$UVÒô8]OµuÔ©pþ1Üû(çgá²o’=äñª»6=R¸Ž,˜;ÌõÆ.å1¢ß¶S©*ä³kJMzØÛª¹2Z7¹'¶½*2%Ÿ}Ä·¶• ÆàN˜™ÌƒýiQ(â,KLã×’KGç’Ƕòôà&ò~ܯ×xÓïòp^•‡Ü c7¸ICö«-¬¶”&iÛx þëemØëÌ;˜?OGa±­9ºF®³¹+!.¿Œ½ÿ4=æg¢­î(sÊ®mžäc³­4&Üz€œC˜®¦Fì‡øH¨…+¥ú˜,óJ¨^õÝa‚ß”Íw„1L*@×Þ ¬ŠÄ"¨u„Zžk­Oç¡fÑO–÷T ;b)Ž–åé¼Að}Ž(n”ÞËñê)`Ï|þØÆcˆÀÏõ ¤˜Q£¯>ZR¾¬§à-u;šV²ó §àîez´~ËJ1«Eèôp' …eS9Gaͼë0Ôj/Eæ$ä\Z4a*÷¯Ù>°‡]»vÑÇ‚¹ÌMKÎÝÅDZüýs8/à¦_ò‚‚¶ÔþYŠÇÏ ®ò'˜±Gáä¼8úžD½Ä‡Ü‡•× xµ w÷$>üö•-^ÝÀ¬ÉI›ñå³!ßB,zø†YjlÈj9Ù“Gz‚“ÒÆÞ+˜B ¼Æ$Òì.Ðã4‹$â[ fr›¤è‹aà¤y²|» H{*‡³%â7³¦G&Œr™•Õ,.<î½'óÙ;ŸnBN±2žórd³ð9ÿŽûeð­\†þ£ qågœ†.âœé–ôŒh&Þ„¸s¹ MœP b3†¸ê %ؾVˆå­JFC‰b,6'1üdÉ«Pøc$O…ÚIéM7Jåžp«ãn¡oë}0pÏÇÍ Ø%‹ÜAóŸ,½÷z-×^OrÇÛ¢ch:§ ?µ¼„9¿Ä)ã~Œå½&Ç”û„Ó9÷Léü.yR)wðâc¨¸?ÓŽ4³¼‡·Â™K=*Ç|~BÈ/Àq¥·pfß8j|8–جOÖ ÓeoAb­"®òè`+¼Oá¦fU*âåGÓrª w†9ˆnÚNj;o°÷Âî$Ê(åeƒŠC&”=y%×-éE•"æÛ©pÜ ™M' ïàCµXä¥á¹¢)¤äê6èkBÁêœ4‹ãdF¹=nN.Ÿ;ŒØE&òƒð÷•FÜ»·;îðÒxn6h›Þ/ù%¬‘ö"jRës—°—ùëñ±WY¥Jö|t$ýϽه0ѯ”¬gPÅë( õâ ‰«œÓŠ*Xõ`>-ýÍòd…zé¼’³uänÇá£õÈᆲ‘&Pr—¢ÓOwÀ9qeb³l1‰ÖëüÞ~6üA,YP­Bz¢íÀ3ྯìçhŒÀñHWæÓmTèÖûËÂ$#wZ¾ÁQ—Ì×T-Úvã(ݱOŒ<¯:‰‡VðÒƒßÆûúÄex>M[˜_vZ¿\Š ¾ NDÖÌ–†ÅÞEžº³€>âÔµí¸õN&Ü«¸Ãœ¹ý‰mŠR&Á÷/๴qî‰~ªÓ6©ŸÂÈMOS¬Ðw'ÊuŽ´¼¥®÷¤ÆK½èQkkj—h#ï÷’“öÝTTä ë?Çd3²È+ Þ¥…ª#ªd5¿4ŽwE’Cv™–!7º!JœóËw Ü.—aØ¡ ˜Ñð޹-þ +é7¼æí;èIoDŸ‹[£ëúýdjÖü4[—21¤×fHú«iüÛ+x^A’.‡”MÔéW4Yx·y¦0bϳ`©‹*uÝ3 ë_~„ì9Oqå£ËUȽœ·+"øæ¦¾2¾f …ry´”ªæ—ÜL”r—±˜kHVd÷ þ>!R]u—ã¼M”jÉ‚ð!°áíI,(ׇ‘-ÚØ> Í<E.v§2, “±áÙ$U@{~Àб´.ÛëÂc@óÙ xæ)OÙÂ2tÞÖ…»a€ã8d€Þ‚5Ìôÿÿöá°3ÄÆ4ý³ˆQóôØŸÊ.[eÁ¥s/9õ,L`¹¿j– FÐèr âË“U:–l˜Bî3ÂÁëšô^€>Z$O!#3_¬ç¡1£Loßõ š¼ä6ufº/á^{²Ø6½Wñ½¿?”“.1ú—ÔàGö>ÖN€Ÿ$ûóÒ•®ŸÙÐkɘ²4ÏÔH¡é`^»(DKÜIã½hÎuMØ:k&º™oÆaþãS(1Ý z›(ê_ÊcO]ÁÂÌð~ö4xèºúÐ;{$D뼘¹ùÏ™9ÑðÕÆÊÆ}™¹’Ý`òâ"$µ‹âµÏ%œ¥´ŸÛÙ€ùÁÁ\ùoÅ ~‰…¿]-0$œ £Zí¾H‘;½à >öà·àÄž&8¡ØÿÍ¢" ³¨PK¨f0›³£ V`627¯Í [ðn=¤nœÎ~ÓãÅfévóäàÁÓg`¬7‡Ê=/gì«wà»8}h\+K;Ï‚•ù?A*2ŽKÏ£^Ëñ³È ²ÿn=)?j½ku—Té±wbìÛ¯šTûš,Úž¿Ëþâ@îåm#Ç–Ÿ‡AŽ „ª–fƒ{ã~ÐTæekf]5ix`³þL5­ mS™]>ølâ6¾Ü1^)è†ËòÎD ÜønrB9nµÄRÁ~!:G®ŒÍ”‡¡¤ 8*I+?Ù’Q¾/»;›t׿6=üX_Šfߘ}†ÑSÅIÐóõøôLÖ®ôæxæ>uï¹ÝC›èsÝ­'Út8þ5ÏnF÷Aøô.Ö^¯Å­=ÒTce'ó¡ÓšVÊÒY3FñA6¤)I²=6Ö4íã;Ü»:ž^=PÀÞ»£ª¡éÜŸ ð_¬àÓ¨ã‚ÙtDÛ‘ŲÚB¦¤F&ŒØœõž1ØÞœ°ù{'sq™!¬ÌR&-}eœ„tbÉœgíÝ\‰é7Yü¶ñµ¼yˆéªû¯ä'+Á®Mrví¥no$.ÂPP?¦–áüÇÉ8²y%lÿ«‰Á¹¯ÐN«^¹¥ Dž'ZÙîˆ<(4æPÍm‚àbz R¦mF<ÙûEôÈA_ ᷠץᢱˆ'sí½I8ª¸Ô‚Ë¢Lbn4ÜŸôñuÞÔt—y>X‚BFpd=]³ül)GÇ¡ùäæ~xb÷Žîru›œº4.¯3‚gÑÝ\<ýã3Û^ôÒŠJË?2‹ø`§æq6ê9ag¸âÀ?­ÿz#Óu+T¨À[¸Ú-Iéwxt€w_`åEüÈ‚Ìz2ß̉Rí4Úш'æÆÀÎëx6Y̤Ó[³…ˆÆ\{’-åCÏ}’§ñÁuxCô*ÃD}eû¶¶b«½'µts¢Z9¾Äó°sÀ'˜6B¼PA˜X/vË«Y`»-ëðîÐ^bt%‹Õ’ÞºtôB!úÇ—Ó%®LcESdÝ^¨Ëø`R¸2Œ.Û…Ü¢"2°Û¾Ó ÿåtü9ŸJÄg£ZÏ“˜AnJ˜0“Úmé‹1Ö?-›Šÿ~†~—3Ý9ßp?) ÛÆ^`;¯ š¸>eR½3Èé,a²ùÆy6)Yƒòò ëž|òªÿlz˜CNúN¥~3}<âOþ„&wzÒ('ÃØq”Û²•Üi«9=Œ$ä­îÂåv먋N ÌV•›{€ßOÓ8â9^G½44©ÅŽ8êî0‹h8o…‹sçaE Л6:ÄTÜ‹þþüKS<éá#tkž"$;ä1~_Æq;!aßA›µ%ÓD…:w‘sûÛ˜àÈN¼¸ý£ýN ¦q^ 9*€–Íì±jcê™Ø uÿl€?gú#œaÓä÷9ªX2?xß8³…Þ5`5ãæK¢j‰¬Î'/ˆÞªC7·Šb™Ø,¢V8& /¥›~úáÇ)dAÓtà»Ì!çÌÕ°;0*}uiÞÆSLIå_LU Á~ý£ØxãZø@^° \y¬M\Wœ@×Ö Ü$ù‰Ý`’Ò}ì#•îÃd\Ø´z ÛBä¡ÎÞþå}WWqd¤…•i_d,…ޏ4òÝqT+¬‚~ÄWSÒqyÍkPsôcœ€y% üìºi Oê‹Ym¦Uä÷ w´ÚÒ<·.1x éåC€¬ ôVs"çð&…Pó7¸3Ëœl^ˆ(Ñ~iý-X²S–HÜÙ+¡‚K,ä2ΆÀˆoÁäu3 €ÄÐ}–Š`‰'næÃG±• »]‘Š¿U¤Ûç¶bxG'stcªHvÃÕE—ñ\¼Í56£ó¦=ÄÓ{9ǾνO®1FÆ W|Qð×}È¿—UÒ8‹éú$@g'”þtŶW]ðÃæ«ÃP‚õËöð¹.·oî¥a›IÈòݘ*ÌC·œÀËûÏ’[ü„á6až¶"IZw€üù=¼ó`i¢ÞГ%ßÙ‘«sESÕ‹…˜Ø5GPþÖ ºÝæ3tšæ’Íž«9íOåˆá½´=„w1 ž"MÜÕž3çîj⋸¥Ö“îqª€ØÇ¢`?âN†ŒiÚ„1Zê 0Ñ'+Ðíl?i9¸ŠÞ¿7Þ/È žÑ­XÒ/go™’ó]‰dnP.:©v³¿¿¶F2j­Å%K>0uJ·A³2·_.…+–œ¢Ø{ ·ü¬fÛbmÙ•B´’—…“¶¢$ai¼P{ËÝ(œ†®i„÷ßJ8¶\ÖwòP¾2¸wuˆ;§@Ò]i¦WmÎ# &$\i„ùæÆC¿$;ÓÍ M(ÿI:±ð È 0˜ÿ®…·ÑGèê}Ñò¹5óê¯$cå¡MråËñ¢öÀµV-èf†V2ã¹ }Na¯¥s+^¹Áš}:4Ë;jÒ­`æ/m«>œ}ÊŒžæ¢_™/Q«ƒ+p—Géa;ˆÈêDxµÂ>Qg:Ì¿¯zú0ë«aýB[Z7(ÁÆÓ¿ømRóÉžìÅ”–éä’ä4|qã®=äà°ðLžû°†>f“zõøõ+?*="ó¬ôHòˆˆvcª781…‹gЩ‹1 µéÆ9atŸ»Ìµ‹ù›²‘J‰·`¨ 2™–}ÿ¿žÓìÖêNp1º™þ™™óCáü|aÐ ,%wõÒ†ý4ÚY‹<’’‡)ùËI¼¬žã“ ›mBáLEã7ÂØ†ñ¤$0¸’ž_”‰Î~÷ˆÀ¢xtŠœG—7°Ýi¨äò—)–2€“oEPÊx)9üZ‰\”[@y/âI›*Æhª$ÙQ#Bøúå¨À 6d Õ,}š÷×¶îTóÕ*¨n—‰³ïÁ ×\Lxè¡1LúX ZøÁ‘åïw„ÞX›Éþ±¤+’Ø¥‡miÉÌC¶ œä¾³a+wâ_!WÚm0 óVÈk\ŸªJ?óþë-Íí*Iâ]+DϤ?Ó’Ý*°ªi ‘3O¢ƒñgØÆïVDx†¹m–H¢c ¹à éø™D R{Ù°ýTöl8Ö§€xýÜwÌ›È=ÿVbâäšÞ{fåËBNo4¨¯¢AëCLÝÊIM³ƒØo($¦oåivI +Äûh8V¹ˆäGÖ 3ߘv¿æ4u, ‰á· ÷ÌQ$ûÖê%òÄÑX ÌCWÓÂ5’äó2-’0¨·fv‘˜&eâüê(kÜ=ŸŠÄÁDVý“ëK‹Ñ“‹ø˜£G"¶‹´Çô]ƒç}ßPïÏ'rVO‘.ºÁG·L¡^¬6‰ò1ÃîÑÿbvU«àŠé+i±hè&Õ0±XFþëW­~WgÂŽ„f(êàÞ}&G…6öÑ+ÕáC·|zSƒ Äè7ûUTб…U{XŒ¯w,¥Kù'¡ ƒõU0ÀÄOh 5í/¢˜í Γٓ«š´ßFöègÖÄ})ž3Y€›À³ìMùÃ9~¶Ëá2óïè6W+Tpéz(øu xÞI0¦.pmÿJTkàži±øâá˜zòwÈÊ‚¤1Ì'ÑçÐtp?j|ÏFqûLœ AÓž²£aQÔÛé(Ý·(Œøi íßÛÜžc¬Ÿ^*šÓ]ììÍzŒY‰HþÇ1|fì ˜=3Ëò© øLnQ¡ÊÙÜ·sáë^ê§óìvÃÆW-h›–V±Èܺ‚[å^`JÍúmÍ,ø·nñ;N·˜¬Eù'%È{£™y:©±ÚìI̽3Ì“ YtÚÞ4Ž÷ ókæ½@fWF­«wŠ 7Û‚ ÏÑ&í^H+ßL…>­c¶mP& Ý?`\ô)̪ð‡™ 0“”!«Ö‰³Éï¹¶SÈÞ{”‡¤Ð‡3Äȼæ÷Hÿ…OjL]èï¢:ÖЇdržoÿÂUteeÇýúo;½„rÝLjx«ÕLaC­ßâ®Ö&ÎT…ÞšŽ†¹ÍTþCµvÌbWo÷¦GÒðti }vw>Z?|ÆäšÉæô§³ÔãäÞHÁ^"Hfôî¾Ó íò„-ýÞ£2²Ôªí lÙÃWqèÞ k%ÌGŠê™Õ¹ së®Åˆ7_ !¡ŽÙ Ü̼™ù&ýL»M"í/b÷,[ »–ŠQº„CG÷/¤û“¯±+ŸŠbŒK#¢ýô#™ê;è£Þ z²øümXGŸ—÷´UKÎ'†ÁÈâÒoÜ鯆D;ç3$›6s-ö&Á¢dvðós °¡óÎËѵóHfÓ^ºœ•ÕÆ´âvðÛ B¦}<+í²Â-ÛpÛL 2çë|bÝbMìiÆI‚†«k7Bæ•¿dm—,‘ÛI;ÒO¡Ê6I:âÔJówYP^g]Ê«±ž ͽa‹Î“5~GÈ–£6p4fhreH[ÝZX˜)Hͦ¬¤!£j$Sl1YPíD¤Dn×ÓÀ™Özºé€5„ ú‘WjËÈ¿, = Îº߷šÞ¼(DšnB늵 ˜U ´“9M“çÇ“ï{.aB…û¾n/y9åyžµ‹¡:ƒm)>´±Í{;DÈqÁsdËâýÔ”y„žâD3,³ßG‹\¶Ò£Ç6'“̬'´J>¤Õ“˜ÁE#p¡J ?ûÅŒðà;mÅ-l‰-]óÁ†ÖŽþD¥ÓËiuX:±Í›J"ž’:Žt]å?¼m»É«Þ7¥X–2§‡liœßmØHsXn¥{x>zg±ïl"·Ú¨”RщõÅ‹?Q-EÄiãýD:÷ ÙjÑG£t9Ж·%H¨ØDª“Ý-u4®Ü….PÖbC#Äi™IŸù°`c൯ŒH¡I>©'§AÝ)è¿ZjÎÌ¿’°£.47‡ážÒgøì¾051fþëí.qŒó2ñìê[ôúÈpu7'þ$?.^ºsÂB£`aˆ ­ÿ6—&­°¤¿•›ðÁoC”‘$ÿõjì…™S.’ÚÕSñ¡†‰öãä9à ÈGM4¨è&ezâÕ"º¶ÆŸ$ÛQç5p'>’1 .#Éç—gÇ=äóE\ÿbšƒ¢söJISÛYÅÌûJ_2a‘ÊÝËàèû&R…¾¾m½ô_L¢é;ÐTQ »o¸†Ub›ù•Y¹vksFƒ+‰”á:rðýt²ú¸]«Ú #Ÿ¢85ÕéÊÇ/ÈÎînjÚ¹.ü&I¦ôÄÕ[8Ø—®K1+Žy1ž/ípî«0œý5G.b›/ + úÑõ(LUö§¿ìÖàž @=ÕK^ßÀÉï4sÕ&”¹íË&»^ƒu9sèÝ–Ó¨ýyVm<‡–›؇®{˜¼ã‡˜—‹ôÈÜI}–×[Œ-¼L×ré ZÂ5 2L§±&??AyÓirnF'¤îÆüè|ªnL{˦ß®.æ¿>ͽâëh¹z Ô\Ê¢V TÂ?·G;ùÞY‡ÛÝÚq½“Qò»¿^ÁB‡µðzC6´%þÑSSQwÿk6ÂÃù¿aÜžÅÝ`FÆü:Ùkä zÇ[FVä±]ŒÇ×q2—“3~ú„³ÕN{ØŽA§îáæß)úð5Ü06dœ^³ûE¼ÉLé;¬h×vlu&Áo*`î ]‡`WV6—0¦¶Në°&DŠ.yQL÷ðZÂãwtvíøÓ'N̹mP•v øøö@óï x¦"ewh0…9¨p¤¢f«ÐK!¨õ†ÇÕ#™üSÛ0„ë‹—ÈpÆlbYÜñtàÎé… â¡J•”Ñ áÕì²YDìI53¹Ï™·ÿÓ[jO,Ä¡‡uŒ–m<‘‰ö¤TŽR±dƒ6Ñ;Ñx&i;Õ¨gN„-eÅAHäzÏÁ÷ 0wøö/H€ÙF‘ ºäºÛ%2ÞL0*–P`Hå =¨ ‡;©}“Ò3HÅì@ÐÎ?ÏróBÑêø üñH‡½¡:ŠÁ½–0š.A+>Q+Ç] wU‡þW7ú+Š–yÓoô F9 Ls¶_ÃGéb¨(%KÓ¾o¢1uÒ¸öB>›zŒfoÂbå6[ò.6¯ÜFnÈ'“æ1èç.N“7ßÀ¹iœG°ßÔ§î.˜ÆxATU$5Y’È`Qv'y·R_0¿ÎIRÝèÌ8ö9Ë낽YxÚÑTÞKbFcä^O2r‡â“ñzÐ}¨Äˆ}ø‚½3˜{ÜZæìb+‚cÖl^æQ–ž_ âµAGË•Ý_ÀZ~Çìê·äVXp]%;Æ™JÕ$Ó g–· ³­rÄQç6üýЧ×2îï´55¤Ì8NQ€ž+üd©Âtïž3¹^ ÿ¯,|Õ S´dÉY%"›‘‚gó Ãñ ì&ý‹87RlUéì>’’jNyb^²]>Ù+íAÌð#yÚ=¸ =¹I˜¾ì×_% \c¡ÐAˆ>êŒÇ­7lY‰hjL³A±ÅžzÆáyü uSÛ>=ú!ã}§ ·®™ÿõjΨ·Fð3$:v{‘_" ô?=`+Òa©š™>gˆ9ÛÔÍfIRc‰7øãq“ÐwD×ÜfÌ™S]ñ@l²¢ú›§“Èé¿°$Õµ.«dNeÁ쪑@+¥V´.ˆ ‰»e`27}íeÿ¾`™`~*ç³ŽŠ›pÀDä1úúFнwè¬X1Œ¼¼“á׉ÄzÙìÑ-:åõLZ³Q;ë²áÊÃcPòÛ vú×Á¯Ég¶#à;uÉ}/ë^‚ýŒXÂ{w3l1îd¦5ØÐEç¢97 Î2œežìç¸,;N¹hЀÍÔ¨6‚t«ô€éK7.“£<_ˆqu(Ýq4 ËÎXÉivÐ=?Ï̱a7­ ¥{K¯À2ÍÄ(Ë²ÿ+÷"Gîãêý AìçCÖ©ü#âOew¬> Rúä~p9Ø È”ÍÔ_A|Ëb§q du¶`¡¶>µ&I¥8æéÈ‘Lv‰ˆ3¾#šš<Œ'£Yò°_€ jÂO9ßldÎMDòr;9¾±’Fž«£V9ßðÖ »KÛ /k{ºä0<°2%C·™¿BkéÒO§¡ó}14ï ‹‹¼ˆ‰Ï**#©‡I·ÂðÐ’8ßC?Å\ûÕŽ'Ÿ¯ÉY¢d¾Õçÿö±V3Ëéxû¦u‹ŸSÐÀ&Òýþ¹`oúœ|àöÑ.ƒ$ú;9d«‚ç-Â;fc«oð)ì>ìõw¥òW¸c+I¸V4ٴʼnÜì5€ÒÍåpfÎ(©³Ž k’îФ[cì/¥nzÈÝ?Ç`ƪXœ69^§ZOz¦lqyÊé+B¤÷v=sI<åRèïRWœ1E?g5 ÀrrpÑ8;ê0•.™b I|YéUkéž%`V5hGH@Ú›ÅÔwª8Ý*ÖŽç^‘½p_4 75ÇÂ’}ÖdT½“ÔÆýfc¢QW“¥°?À‰fïyŠAùÇiBv4Ę÷âÚ#:à²ßö¿ÞÍP»U‚|×Ö§.{™½]Þp?>äµpÌK÷`úÕ)døœ”— Æ÷âIÈÔYÔ>Që>§õß«É52[•hå2sb²ƒþ61æ< ’¤µVµè[Iî}ýÀ ºÚö] ~8TŠߥhS«= ÜDÕ+oÒ £ RQ—BÌMÖÓaß›,v€Ü͹€¯ÂäPdwšÃ^Mú{¤í|A^³…ÄÀYÓ¾g€‹×V¿…×+ÃáTÍ?¬ýöbÚSH­ïEp³ØI_m™­¹6TsYä_èœÖGgFÒ§¿Ï…-FÔ~} „ˆ,¥> ƒÕ<éI?ö{$¶ÁZCØ7[ÜýRAºÌž(GÌà›J|§äM7ì95Ö8ôy?Ûá1 ·OŸh³>8ÎnÚzŒ¹¢y—~WC‰•w%5Ñ;k³`¹$¹¶ñ†¥Óv>€¨‹Úت=Î= M¡=õ6¾ÁäŠ÷ÀëKF0v›ƒ·ÜßqÄ=í,]Hô-[Q¤]ŽF-aU›¶’£UE¨wx7;ÑõÏòÇÅ– ]ü=ǤS {>Žçügì™2íÑ-Xú·’¢oáp;Ø}—œJ§VN%¢»ø¹Kƒ #Rƒvl¬Æ‚´©¨dfGg¯jf.$™CÇ‹üVåÄ>r¤ÿ#êO zú¾ÿq\ó yžK³¤Y¥áy÷1— %d–ɘ±y.• Jšg¤hxÞ}’’P$”Ê<‹Èˆ_¯Ï}ßÿçZwuϽgØkOÇzîunÀÔr¯³1ûËxÜe‘[µ€®zÈpw'Y‘Mõ’äè¾Ý¤çкºµ Œ’Àt•-:dB£öáž¿¸ÿš:ŸÉ6J±h-®G¢Æäè[~öÛîd®éñYä™ùmØo#L·}‹Ç4ÎÿÉA'4&õGah¿9zÔ‘¸Þƒ4?B\ÞÖ1³}Nã¸kêJÁõn_XžË  œ:kNÎB§ÒÊ3 HÊÎ÷!÷Ò•çŒqZw`Î(u؉ª/‹˜–Ö×½2 —^Â…2ÝÖªD»<ö"¢×ôG†¬áøÅn¦;¸7.ºÇQ8zÇ/°Xúþ'³Hf>Öhƒ‰N §9Š>ÏãÄ~ ¥'¹Wåí饕2tÇ$—y¶ ¼˜ôûéo·Rçp]ˆ¾.K·eur½­”Ø-Gî@Žå ² ¯pNô–2¢‡K òÛIªg‹·‚£Ÿ=«ì•Ã-ð Ú6´SVœzdôcŠmãk/ŠÁ…ÊWñ‰›pÂŽŒ:ÏÇúÒɸt Š·–ƒGƆɹdéæGDᤠ¤dƒ=ÿxŽ7áÜÑ8hž•Ïä\j¿ŸÌ¯=ShwnóBä(úžýõ¥ZŒýRø!U ïOÉÓ3ZÐD_v Y³çŒÐe`Ÿ‹!ÿç ôPÄÍÌñ¦ìG”?韸yð‰[VC!çR%ÜÕ#;GÆÙÛ}f41è1k»Æœjý:»ýß~ëëuáÁñ0ã”2¹Ûé›ëu(úö|“PÁ5ꟙ¿Uß8ÅÑw9 Ô¢àN_ ”;„às®:µý]‚µ ‚¸Ør˜y–Êûo·WXÝyÃG/ã®lf7_M„åT<Š#ñª¹6§=:™¹õ¥½ÞµpëæÚQÎþ ÔÈ/´î}ðú¬<íZú€9ôõ!Ê-œI*"€¨N!範żÒÿñ°ç²,éù\ÌxѸ tù‰CÝ ÷©™:Y0=\Hà"†,ÔV¦îv²ÿWÃõv£%PîK"<«õdö莡¨_,æÂ‚ôxïaòÔì –[^ÅgSW’šOA^ù ×`KÙ¦ tÎx€ŸÃ2™4Áå¨k-C_ÖFLzi:|· ±^-ZÊàìï {okÞޅh_·¯[H’æyÁ÷zVK4Ç‘UÆþž6†v”÷ÑWü1æ+9’â!#hr8A÷§Ã÷¥ü$䄪£ð=ô ´/Œ†'ÕÈßA\°?¦b犜Ci9jº}>®OëA±Žçì¡/Ö4‰W>dˆm¡* WÊçVmž†{­c¾ƆºPpúx…Ýëð‰Ë¤ÂœXdßã|éÓP(ó‹qRЧã/ñ@ÊvøšÊÓ.ÞGÏÛ6°£Ž½ÝG¡"š—Þj®†P^ðöˆ ni+4Gæc>ÞÝølÄ 1yª6 Kè—MÆ_B1½gÊ"¸r±‰œ¡/w8ŠÜp6S­w¯3#ûÙ— -%òÑP‰ÎÞaF"FaÉÑ™D‰ù ®ÊmX¿é ô»sÜg'A„þF;^úÀµ.¦ÏôçÓõaZDf,ÞZ96zlñgÚ¢£Aõ‡Fï`˜Éç ÏÞXêÿ}ÏDÍ$o¶ÈByÙƫ،ŽGžg6šÿí8,Â`•R Ÿ5#OmÀùIì†øyÌhD¶T¯!ÎeWñ^}w9”­´k|ヰfU?ćâþ¦#XÝÁh®+g›¡¼ÂÈi©[ÌÇâ3$°F•ª«®›Lò×(‡Ã÷³9­ÙØÑ’„KFÓ@¥ú.Äw¢rõY…¤\¸¶A„ÞâœE±`!ZûA—,öœ‡²‚€_¯•`•x H*GÓmùS©cö({ÝQ‡Ü9ÃxÇ”3ö‹É.% jñãù¤¹…ªÌ‡ZÆ=F„–Þl‚ƪ§LÜÊß ª÷‡ùLÌIôÖ“tåšQ6tçu|©oŽéÓC8J/bµ×":}·6=Ã4SkغӉ¡?ïà k.wz™Èr‰$‘›ÀN%^“LbÜLVãÑ0ÊÓ€–×sà É?f°¾ T߈’‘ãú”ôsÈÊžièø¤““óá-d·K’;͉ð1ÈœfMq²£ðÑC{ú·¨ƒ L¬Gׯr×y Ãétò"i=]nNŸB¸aDz)B6åj]ŽeÞØM -.‚FÃjÖ‹¾@'šx›—º áZ´öòÄ@·Ó××±À?À÷§ìÂ7ê<´f2Vïâ킞=®Êowt÷,dž?6d¼§–²áÇÙõ@­‚ÙÓ÷$Hœ7?ù•´˜œ:ûcÆÅð¶§"5=_€Žœ¹tãÖíPf%Hn|Èg%–0¿ôˆþ{N­~ {oòÓÂÅnà×éÃ~±§7Íè·1 è µD`[ã(óK³Ÿ=›EÈ|{]Ú½RŽF_£å .bÅÓÓø6UŠ8Å¥1:~Qd™´N-èbîÁØ.\ê£8>½Ü'¿Eá“{ÞX'zo¿v.Y#@õ~fîa¬üªõo&1uì`¼ü‰^š'›0ð dÒ™YÎ}ÌR¾Óxí¦<¸•ŠÆn@Î6>¸áÉO…wDáœJÐû±®@é‡gÐ×óÈcLQÀ6‹ï,wø$®ÒÞJ†wÙ*±jfËÎLxX4)ÛÆ ;›ú¸˜ðW4µ9qƒW]D|î oR¤HÖ@ 2òóñ ®&øZ©±÷ÜS¡wšð¬„ž'Ò¦†=d§] Ý0/”ߊhq6jj¾ÅýNi(Ë ®{ ¥£óé³öÔæî ¦ÚàÖxd¡ôéEð!9‡Ý6x z„øi€EÄ fÃñÏ r-ˆÕ,—zâ ÔmQ¦««¡0@Ûûn3¸ê,WÛÎGÍ~‚mÒTÒý°Xñ'ΪìcºÖ3jDˆKŒ:9¼ÕŽýluk S·ø1=æÍ¼ˆ<ÙôL7‡ó7ttÙRÌð›Idüþ ¯›Is±ÆÙΰÁ÷n·ÏdjÔø¨dó º£M‚Þøz| CÑ?î>8¦C¾ù⛕ϠöåQ2ëàfÿ¸tîi¦?‹ Û¾±‰C1ìWݘ«ù¡ùYýq6Ü¡´m¬Éi5^*\§A÷}«¢V ðè|«»i&É* ¥…¥)ùœÞZ²äv­(9^ aôVò¡Jìó'ô+uô$Ä~ aTÅÈ+ñ<Öó¯MoÅ?x¢d5Þ㌀k|$ÔÍüÃþ¶î̧wñpRzî’¥ª:šbdªð§–1Yã¢A÷´¡{/*r'¶&ÂÖR+t;¼6Ç^G©\GÆ"Œ@ý±w`’™léa¢Ö}¯»bëbs6ˆçÌÎ Š»[9/Î,‚# Úäé¨y(=‹°ãôê핵{È«µÓØo!<$IЀtãŽÍŠCQ qáínX!ÎzöÍ *—cÐõd$ö^\Ç ¥ôCÌÈ.ÆY”Ì'*€ëuƒIå]HÇ¥!¸bÖe¦ÚP”D)m†Ëˆ¸ˆ:Ç0·„­‰¬ø>Э·ñõ:,W.ÿ;»:¦m™õ'‡¼ã€ Ýè¨Úª þ£{AŸ'÷úÿÎãå§b„覆Q“?ûéša^2’+Kê|ã‰qõPiå~^¦ŒvѱÕÐ’x/ʤ©C1*Ž¿¥sæTrà]6ü ØëoBcUγï仸©Óúp¼¶Ÿ½[›§n@ûu,¸}À]75IET ,ã#)yÿ؂ϗq]–•O¿îx°? 5´fâ€Ùbx/H¤ü'ñù'ú7%Ff¼\Ö—ï§="5žÜ—ŽíK²ag'Ó›’ÆL1ÿ…?sSpû—QXç߄ߌ£!vïAüýœaEÛ>°+&cÓP×-0Vß #jGúAµ¢ŽÉzs’±ù 団aâûTš|·ï<Œ=s~- ð}7NI@”]ñLƃÿë‚ÆS²®,ÃÁYž4áš0Ýyí2S·÷>îÊ9Hæí¢âO`Ûg]:z>fg“Ûcä`ÀyæIl|µžAÀp„©R¿«¡ô¾O-4éc/_¡Ø¦6Ìlj?„V5@d|>ª,U¤Å­'ñ»îG¸ö¬«>|e?ﻈŽÿáQ}Š÷òÂоG–U{»–nî‚WV§§¸ EëQüÁi0KÂì»Ðÿ¥9pò7›’‹£L!£`cIþ¾K¶Õ…ݧú€©âŒá¯ÜüxJ 6c_ƒ91Ïíæ¥‡§%¡ìx&†ÑóN©ðíæ~œp8]›6Á•yûˆ¼I'Ê™`¾Þ7¢‹F®ÐUejôJ®(ô<†EoMÉþòÑ/ü9ÝDüüCV“o#t´Ä°9”_oFçœ"óm4È ‹zfmŸIÝ4€³Cȉ(Wzj†D(u'—¯ ÛÙÛp»¹‘ºtD‘ì§—iZÀ:òeãQ&ñq8•·†Z:§¿2'ôÉd‹ÕƒóÉœû\võ v}ãtL™]M¤~è’cÆÇhä$—Z/ȳaÇ­úãÇü&}ïuúÐ~p8±-‘üëõ¡}[ˆs ?Hì ÒÖ>;&ãiÛ@L8ͬ&¯{TéJÇú_­p»ñ0ÆÝ3!ãŽTñôCÆÛ÷,Ù¿#„>)Õ`tMVSéÝï[Bù¯–@™™>9ä:6±¤ÒÚ ƒ¿®Æ$¿Jší.Òë‰Ú÷ &»h3z(+Cç-ðÝN€Þõy/cøéa’M®Á+{^*&TM窤Óý+›˜Sºï™Ë«É*‘34°ìÊ„ðÍáÛNR¢é£äÍ÷.xªÉÏšn[CwþûÀ°jÄÀʇ^Ú1 êÈyslKòÓˆíß n8úTàúÙ:ÒZqߗ߃•a²³\D¯;`“³.}°™„|¹Ìd6?;£$äúæ‚pÞ7U¢®«òØo'X÷SÄæÒS¼}¢„íîÅÞ™ÚôÀi}bW.YL„n™Ó¡L×ú¡•g|.Xm£~·9åÿ}£wN¹É.*Ƈ…×ðö@ŸÃÂñ~ðS‰Eõnoz;Ӕ뷕…–¤C  Q4oÆ) ×dqÆÊIvõo¦ ñsé›\1¦B…—Ü{!C‹“™Ö2d¢Y™úé%3vÁªävÄ4ö”¡4m™¢MWf#ƒó¶Qñe¿™áúèÉ3„µx‘oZYTEá+~[o'‰Ó‹W…p‰ñY‘ µÓ,H·”&ÓfMÿ¾Ò'¹qßàÁÝÎI¢wš}‘Ø„÷qû_ÍñíÂCœœüaT“Ÿ1Œß’ h1~ÈòG“u¡IXyè#sh;gÆ9¼Ø‡ƒ;Œ°:¥…:Žãš…86û.@s"þ>ÏGÔ&„a«ˆ9³K÷3ËÇÛáàð c(CåoD£aOöŽfŠ4HÛêŒ Ìgu«ïCÒãjæä«,¶ýØ*p_:…”Î¥‘.Fì¶×¡±IŠú¤·Ã𴸈ø²*ÞÑÔCõ Þ쀉©U@,àîK!쮀·úOà ä•< ÃR1Ø>¬ˆç¿?… kÈÁ<;²Î'’jdùweA}ñ,ªŸÓÀ|ÿ²Dwÿb2Ò¨þJra›ÙüS…캽Nÿ)B%ã ÉùÛðlô \ÍÆMx6³?Ü ³Vs™¥å0Ó!Ξ~4Ú^œ™ñN§yìá#fqJ¤ t2_¶;r¼?£Ýùó0//L±õÏZ¼½Qˆî }×Ì‹™Š±œp×ÙDrý¬¿_„é#6Ô&|ÈžGTèÂ_©yX"C#©ÿò »,ì »fþHlä Øc¾–8Ú…|ì„åuys‰í:ØÀ¡Öí0k|^ózæ%^Þ f¼tí©az:^sš§]^ÒÂØ™sIù9î ?CbàÈLáJ’ó:FPûJÒx¢€Q¥ë6\³£Y¸3k ±KÔäÌ®O€[ÿvr–Úƒo×z’ó÷ Ωû!Çèë­!”g‹³ê¹÷|ØÕS‡§‘™"písm¯v–±ê×›X»/ )jøqÖ5GÑÖ˜HÖüÅ)ŒcÙ‡caòõÿUrŒƒ®bˆùB9¾ íc’Ù»ÜW¸gõ ¯Þ³‹oÀ¶Š‹Œýòß9Ρ(KE.V“´½O°Úç:wb†$N{pžÀOXÞëD ‹5ðïÐqæaV(9Ë'G=ýŽBsÜ xp\‘ôþˆ'Ï.ªS ç<,[º˜£êNBŽ(áZ?kL½} :æÈGoBò§;dlV ܼKxdX‰P¹„û_9¡ññX3ã2> ;Ñ™’xÎMŽÙ€ÏÜ`dÅbÈ2A/¿d3J}gÉ]u<þ† 2B†t,³$œÏ^h>•8‹ÊP‡Á9 R“Šñ¶F´úaDY£ÒU ³~±Yé/à¥Ó%¤,©öm°1‹åþØPNìšZÿ;ë._šËxz:¡§ã0+z,º^]Ç)a»þCúãÛTä¯ ¯&6cŠ™®:öÜñmÁCèvâ!­·ÑUU™ð˸Èì‡ÐýK!{bÝ{a+ÊÝ»k†¹¶õ¨Ð¸—¼úòï²W˜Yr·AdØœ51&’…ÇÉþÖeÿÕ7£§Ï1H:úøþÃÇ‹ìZrúö²\Û1.OžmçÁ-¹ôþQlОO®8Þ0'/Fv“|è÷’ æÒíçPÀ_ƒ²eõP{™¸’T°4•d»ÍAÑ-uÕ/¬±óVÚò–°:‡þ¡À\;<Ü»cêåBwíîaf®½@‹ÛÝÀÉ$ŽluFØì¥œò¦Ê÷%Ty¼ƒ„Q~µgí)¥ã‡èþmì5=ªtÁ½V!7ŸÄ[‹€³É–„ÿù ËÄ#Èp©(QU‰€|nÙ“'ÉšÇÿƒŠÃΤÅ9Š\œ“ISÆB`±°0™­F—WêAÑ~7ê-w„.ýþÞ¼_ÄêèNÇ4ºÓñçÌ?H§)o¦Ñ3%ˆ¼ìt"WŽ×ŸŸar—ebû¹Ây»‚ŽÍÆg³³÷9R‘Ÿ—‘=AtùŽ’ ;>‚pô[œ—„ÌÇŸø÷tºv$³[¹70®èÑûŠssø&k0œ%‡à»'×1|’ÓÔÊ-f·E¡­ yvߘÜÒÌ|ù²äÿÎYÖ|Ì*óùÑÆ÷Œöü+”Q‰ŸÍ·ÐœB!¢¾B^àæ Ï·¿À«§ò:W0ÉÖ¿‘µ(»Âmá Ú¡Bdxlµ¿Âýí&Sç>gN¾Q Ï~¤`vJ¬ÿõ<\û¶£¼n 0²Q3SPM·¤+3kñÇÉæË/)”My”øñ G®øäJ¿|0šE`ÎF‚—û:.(h0?/N0é¦Ï¹÷ Ï@Ó÷»Ð&H¾{ŠáRÖ\ú¤ó߳͟þuÔØä@ÂØïÆ˜wóùè€É6üº8Žyë†íîÉàÙ¸¿=žCeøÑc‰…›âHÎEža—çE‘äº#xØ|¶=i`ZN‰‘%³íÉç¶ý˜ÃG_Yâ;ù;왢AXðøÞÛº—¬°ŽaÜÄýmš°«MG>¡ºw¾R¹U´»‰Š^€Sã³8üBF$jù ôôÆðìH¾&ƒýìAoK¢¼ª–÷ BqÙ¢`ÞÁ|;f†¾¶ƒÌ•BÚæîÅF'ÍÀ'AgûÓ Gg7ù䉒ej³8;û1j~[)E`F'Û«ÎGþz}eÒ9a΃Íí x1Ûú¥¦²Zf3èˮӌ¬ž &nU!æóORÓ…pádþ ñ£*vê­^dÐU JŸ °;Í·r¿ÎYÍQ»Àõýcõ¶WãF)ð`[ñæB.ÙõäQY$é\ ú»Ü`nÛ p´ÒCÑ[¬õ¯Xøô1½¹býiîºìÃÉéýÎð~ýÃ*ÔDƒrÙ4æsñZ*(£IJSù°j%g9É\H [ ejLîž×k=²užyÔë ßÙí‹ÅxûÙ[¦czùyœ­WÊA=­pz"}VvDžje72x[G˜¶Çé‘ßuÁäài˜ñ%}ú…Ò¹ã©ä̽DÔt6§ÉËcÉÜÚKÿ}g‰ï‹f"Dtiµç!2Ã5þÚ´X¼R­¤sôŦoœ¶wór)QÒ3!éÞ 0S°®q¦Î1¹^6ï…ž’N\Ì/šÑ›@—^™NõrÈû›uÄìýU"ºMõç[EbO´ÁÛZù–Å‹‹é‚ö˜¶.Ÿk=á ‘ÜŠzFg&W33ß'rz3[`þghÚ´+Eé…²Z<ÕBq{ù(¶ÜÿÆ\þÌGv{…g‹TgyhñGcM~0ØÃTþã>úë¦.y÷)Œþ84ƒñNÞBë¿B£ß2RÚ—3©ƒ×˜x¿†ÙZVEü„æä dîã:wÂóã Mž'GnòöÃæhZŸB¯ø˜Sƒ›AÄuWk³ùè=œŸ4ɯaå‹KtåýôÁaÊ{«ˆö§âã»áä]Þ ûÀ\‘¾ ½ÀÚ–O¥×|N_6Øf‚»¹g#n}FõOfaip"D[äu¯_ø¢ö6~é÷úàÂsQ¹[C­v‘¬¹Æx´;>hL@Ï¿L淌ѿ ÷\Q>²5žÁ…Ë'i>æ“‚ÉœuáÐ]fÎå³Ì‹SÓàÛçKL½j ¾p6C}LÅ¡+IÌ`25]7~Ï‚ƒâtZI3ÞðÔ%ê®rtl¿6]eÅÊ·¸ÓG>óáü»%ä™ ÖãùUÔ‘±t"ÏÞ±Ñh@ ƒÚ¼*ŒÅ‚«ð©€w_(ÓÏNZ Üs?ãaõ…Ô5f&Òuòô@×úÃÖ Û­Öã¢YAð3´Å¿…­³ÏCW5Ï5âŸ!+š¸nå‘c~mˆ$e¯®R×îÓø2)N€ ko½»óX‡—+ÉHÿYL[íü߇`$ãΙ>‹†k3¤•éô‹BôÅ #bºd.’ò™“üá×2͆(í.¢Im•¨ö%ƒ,:‹ï¿ÏCÓçW˜_é|sôçAÝÖ·ØùV“Æxº†o4ªGp ïaXÃï‹¢ã¶Tÿ¢¹_cÃüÝN«`µ½=ÙñKÈ£öùPQx†.æ=GÛî ’¤´<6PÍ„†ÔFâøh>É B}üªóVÞCémÑÔv`“5÷<ꫜôÂ@¶¡ö9¬~$OSꜨ·ƒ,/ÙAl÷{Ñç^Gàël8’Æ!b>x~ |ÚãB2 navàPw›*]r¨?—H¥aýaDaA:U‹šB["´áÙ÷gø¾:ˆ3¶à±/¢ÓÍä1‡ç[ÚÁœÿåE–ñN§ë¾¦3!vdÇø[ܶoƒ×ôÃ@~‚DöÄwû–þ€qÞº?ÈìÇqäzª+ t¦{83ðóvI콂f,Kþ%òƒŸ¡6¹¾›1À[khí6†>\(Ö²ÅÜü›òæ˜á¹Ëàk!ŠzèɶËT`©3„÷i’=W™x«s¬èï¥äÁ)ÌîE²˜$?ø5šR×=½`+v‰šÌœByžÜ+rjâ*ÉËšK¶_ÜAÒ­’™ÏCá¢óeb–|ËÏÀVá<®ó?zì/½ô4#{Ž Åz³Yf6ËÒßí ¹|¢—<Û‚•΂¡E& l€;G×Á¹ZÎŽg`ážRptäÝ3ÔÉ£¯;!jÿ²U¤QвdÔü#òÎ’#jg¶ƒ ß'¼½cÞ‚ˆÔUÜÓÄŠÜ•!—Tjâ[ÊŒùÎ'å;ø¨OZ(TÖšQS½«“¶ìL_Oå"‘!?Ö‘¸îJ2·ëZ?zƒ3¯KCq¬&ŸÇèÄgDÉf„}üCŽDÿS"¾óä©IZÊwÛ¾êÒÕ·^ãnÛ?¬ûìš&>þÄzì <)zƒçöÇ LøO´3Ý@Ͷ¥ƒ¤ÂRr1›Ò}:Žtl?õ*Ž&¯UÉ0Š™ñ‚t¢§ ŠŽí)pgÁ[§ rTdUÚÒÏÛÎÁ!‹ f_ÛdîÓQ 4P’ºÜ•$iN€:ÿlZ8r*ÖDÞ͘¼ Í[ÞÃ?‘›Pûøá*Ú¢ú*CêcMÞV퀿™ pŸ §sgìÉ?jÓE™*)ÔMdyjö“=âz ÷_8Šºó éïq9zP²—lÐ{Ǥ¹+‘·þ<Ôöp&é+CK>tW³àÔXlƒ§ÃSIbN3,0ƒD5ÒÛ;~bØMÜý=‚ÙýðÌUêäÎþGï½îe‹ŽVÀãêóLÉ•>–Ê/ÛÌi$sSÕšJ Ÿ¢÷uÊÙË;¸¬Ï?yŸd÷ Ð> 9¢wWœ^¹•ÂØù±ÅnB´4%þ:©?ÃÁ½Èœœ:•”[ð£†äiÊ9§FåÿÕý„V<¹ZˆêžEåü&8‹VKLŽLyçØ,ò0;”<ºm‰oÎ0å…J¨«"J¤Nèâi£P"Ëâ¾k6è’ǹúWݾP6—F僙Ã8Û™:qÍ(fýH¶$ª?‹½3&ÀЧ îym‡}Ö.œ#'Þ£âÍQü*·K"R9¯É•-ÇË)™º¦s°âª;ò骔²7¨žYÄ–DÈÐÔ9µ°äß4ªÕ'ïyi‚™(“€Í—%‰¿ Æn;CMWæ@½¡8ñâãv’ËÆv®‡ÊÀTÜÕòˆ=öÈ,®Ržû‡¾_=ìögÊÔŠsÂÓwÑwS!€L§‰¶S)ï¶eÔb¡)yê O¼jõè~=t²jÁk¿øè‹¿‡ Øð8,Êß º‘ñ ¸ÞŽ©@€ª5óQØ™¼hXOãÍ8M¡'±ªèF RöU4ÝþàÚï*f£•ãaž/ðäwÎ?»ãoç.1ú>»¾Ô­ÆGubxñä˜lIäÊâÿì33%a·¥_¤‰Ï™¾‹üTÊ£$Ö°tSØYf[š é×`Ÿä¯FaÙÛ܉‘”¿b M¸@Û7yÙp~»ªJuø£ÈCüý\Ž&ýÓ"¿L8×ZuXÑæï®CáI²XùµÚÉKÖ'iÑŽv>rŒSÁ®#Þk¤¸–O÷p´É©‚Ùl¢¤/‰¹<•˜å¿ãhOø¼’óÐíçéŸr2ÕÃJ«-è@Œ1ùæWIÎ/ÄðÁ›Ìûß°oÅLPl‘¦©ÇŸÂÂó'9½f©lì*¼ª1FÀ÷0=ê›t‡‰« §ó.éÀ¿5Ø‘´‰ÌOÖ!á‘$åC)+¼t7U¼w‚o³ç. s™ý„æE‘Œ¿ˆ*£{ ®hëÁ;\˜9OM^Hм@ pÊGne@<¯¹t$OŒ1x®ó+jz”Ž{ÔÜÚ²˜3y5Oµö¡r|—Ð’p!AÍ’z~œ Ò…¬—W õ‡÷ÛQ⣨B}/}ÄC£‰4oÿufùÚh<ÜkB_¯³Ù’÷q¡ÔRø»§ ri7ï7C¬£ÙeÅð+V„ê›ÓÄŸúÔjÛ lrR†M5V´qO76FÛ¡ÈïJò–Ñ&v‘ËèJ…pü\eLâ¥jQ52 ²+ýˆsÝtÉù )8ÉEnáKçLbg‘Ê:éô;ÿo¸¥2Äè2.}2°çÃYl«Öå~!Sa‘Ïxó4O`÷çüÅãeÜO¯w±u×8Fƒçaha2ÚœƒOv:ÃÖK0’} Íl Á½Ô„ªÝ›u_êÀÌF1Þom¿ÀצT¼.’œ ¦c8s=}¸kº7²ÛÈð=Ÿ b{òÙÈé°§* †Å¦Ó\áÝøº\.å3Ÿ§-¤Ÿüj˜SÕû˜-6àÝáF oDrÖÍ>Â*ú†ÑkçgQK[zïW<õ +NÛ£Æ''Œ¹]êoѪê>;ìˆô}Áþ^(|ªÕalŠZ1i¯ ºw ò1„ÓÁO¨C_<”¯X ®SêÀq±TÓ`ëlúî§/[4ßžJŽž€ë;—sæöò“¹ADݤnlXÇ¥fã5¢ÿz.SbçÓí$`¨4‘-»ÆCÏ»ÌFìÝÀÝQÝ˦½D˜Qi“-tÉ€IBò£RÖ@L÷åÉÈ9˜Z•>‰Ú5‰ÀŠçÌ›Ýý\›ºçìW¬>ø¹\î Ž·.Õà_Ij¸™Ôûþ4œfá@UÅÁŽjO¦$Çr’:l‚ω8|žô\=#”ð¸ÿÂu¾s8¥×OS-EØùê8Ø­NÂæÝöЙ70ZWîL¶þL‘–<ý±¶>ˆîÁÝ;Ä¡šÝÃ̉Äú¨0tPðE‰u{hǹÏxƒçsàà®9ŽËíþÂül€Õ ºÞc3J¸_d¯ŒÔÂâ#䯧—ÐמƒÇ„Ù§qäí±!7niÔR¥µE§Áku.šÂBÀ,>¦Üáûvñ/”³5"…ù»!vZNôJQçìL ~—sìa®ƒ;-e )™r›y¸',“T¨ßÝ3pK6Îñ·…4mùõ³¹Ç%VÖ3ÎS3ð'Wy~ÿzðâ€pØI°gCÄůÅc¨LÎ8pV¬O©fTé퀋[ŠÁfI$&ƒ{bÔÃt~ZõsOâ‚ÔZ8sU™IHp2VIâ©gspqŸë[y wF !`tk¤Ì<ÌÈN»†%æÁܨÞd®ÊðÆýo›°DW3¯”BÖìzÖñã5fËŒ|ؾ;¤<”DïþÝÌNrBW®@Ž [S—¤^(JžÂȼ©ã®d<·‰n²¾Îv]Ù‡Æçï‚Më ¦özWJƒov'˜Åá§ÐŸ¬‰à;,QÓ!{ï7á«yQê|Ÿcñ¢‘eªÔÑ;B ¾µïzi&}Âè¸]îϵ€OðJøÏàJùK°u¥ÓëZÉ~~¯Aµj¤hÑìMÖ¥¥ÚÑì6­Fœ)í¸í äýÇÝ<‘x>ó0<~7Œ[£¾ì„REÜa–ËÊèÌ„•/Üéï@COêÒªi®×\ÀW _1¹¶“é›0§÷ý‰wŸ jM¼ÀüÕ È'¼F<„¨m¶Í¿‡~¡Ûò“\ã•JŽÖ‘טCt¡¿/ý¶|?ѹïwã;mw©“Š•(|WïL¹‚Âsᩃ:p÷"w…h,ÛdOüüÄÉø‹ƒX¼= Eç& ÖÝ&ÖåÎuhZéL8×®§ôbEÀ½gîг+&øZeT¼ÀÁ«Öàc2´iÉÃéû"Èuª€gÓ¡cËzÔÉ}Áê—l·It í(±¯øÃü·—¯‹SPÚ3Ÿ—é0wמåÌ_àh´œËzœ5‡Úæ-}°”ó÷“1Œ?SÃW£ùŽž]Ì™_ÓØìÚ;LCH8;âûQZ~s¹Ñi…nçâaÆý10xwVx ±Û¹ øÞbÌÿ(D›û¼ g“©)Þ×=»aó‚H¨9$J#Ó®£ö I"¸zq¶OÁX2Ì;—j òCÊv>ÅÄ•ÙCü”]ûlyÖVá囌ÿ¾|¶ê€¼þaÅ”zÇú¤™ð‘½¦ÒÌ+Žþµ ‰Ãñm¬’üuæèÜopð¢h](nãÂM‹íð÷­ì²2¦í§'\#O c5à•W³±^ÿ=2¥#ßã)ÎŽâzÒšð vý+ÄeÙøx­6MÚC;â¬ñkòx¹¡ìÜFíÂS|à²ÿ•çÃoÓdu¹AÂì–ô…DEù#¶*>áŒD‹‡³ÅàR›Ïö%aûCöôOÚ!_  ¤AñÞÖ}áëX9…”^x÷?‹!$ddaïŒ8ì9- ò {X?c%¸ó/6Òå„çE#S?MxŒŠoŸÂ·ìPŽbo >1ó„:gÁwŸ =oùUDž²æ±¸NåþqjA­\e2"Dr7]¤ÇðÅŠ„'8]¼72Õ©|Pýv yüÆ’¸|s"ªK™¡]EP»{>¯Çwë À»&l|ÊGVMeqþ±4øsä=g“{?†Qƒµîp%ë¿qz8.5ÀV}é×Fx~K›Ž8ˆçÞÊ£©B'x®½ Mç,h«Új¦#l\!H“ìÃ`òÂk¬5û*O—gõ³§š.ƒ‹3xÎíý) 7ã+k^¼€­1Ò%g ¦ÓÏåüTßߦ?‰ˆ¥\NG<Ör•°ù 7Ó ~Õ .K‘w¹@²&2Y9ÁŒ’=쌋å™-… Ûh¸¸ö†ä|@¹­\¨NȬ>z @¥×fRÑŽ_£ãÇÂ|vÉQO[MÙúz~¨.ÆØÅ/ØÙ\‡é­3l›·:=Yg‹ÄÉý”]²ƒdTéí’’i _÷Ñ}þ¤Iæð0W!¬aÒ#·8%e¡Œ÷ƒ¯8þU…?bºc»ØMG›‰Þ5%Èß1›Ílæ´]4Ç ‰B$dÉMÔ©¾‚ùª`h/Ny‹ƒP^ë<ókŸ5ÀPñ*[(—ü ˜ )¶¤âË]¦ÉÖ½yký(ÛñªŒ¦Í…á:¼ž#LM]…ùXZ»ò2 ÿ A¡' îÞA6J'7dpœèäüÈ~Œ·º“P­á!ê;mäêü¼ cìyØÔÍ\>hOMy^1#úW°Q7å¤í‰CU3“»?›ÑÌ…ªf5ætÖZÚ{MƒÎžÎ@ü;ļ½Œñ=gàñì9¤§í û³ÆÒ‹fÒgo¶ÐO·Ÿ1)…ð‘T2çwj {W¼j´à­ÀÔ½zŽQ?­Cs3*1÷[8Ùs) ñ}#„rÆñjʲmÿ]\XàO$Zc)×:‡þ\‘H½yZ{71Ù›Ÿ;ºŽýhê,°$bàéÐUH:´ŠtÜ‘ålüàOUšñÅny&HnœwMâá„Tx’Aª’Ô‰ÎA¶7ë.„”ÞÆ•çuÈ­eO°ßî¾[¦E"Ï=ä:Ì”óP²õEÕÂÅxàèŸ"¼Ÿ<jûn3¦ç3@ûwÜ|s€uØÉOר­€…I øcúe8X(Ä©Üj sç Ð˼"P½i5^à[Šº–óYÝé½ì‹‘~Øß¾µb}nôDºÑ»Á)ôÜÔ8:µYiίE¤it/ñŠíF«Žä™Såb)³q£sc×sôŠX£aäΩ£°ýp%›v8•ÛÝÇ¡ýzÔTWì®ÿŽe¯ƒ¨àwsš,“ 'Íè3ÝU¤ØKr²ïú|!CðÒUW…hÀ)äT…æ>6Ä׳¹qè6¨ù[ÁüïhrrMó½¦T([jeèiþÈk_„‹Ó+ ´ìõ›y²o3¿vQîã;(q4†=}ÿ Ñ*3`ƒî03ÓPßÿߣ„B:(ŹWžªÂÝk{¶þ^ÒB %rä/Ô×Û!x¢’ëž$‹] /MNDꟻºmh9k9±÷I&Á˜É#ÌS ¶‘"¦ïÉíáý°ôž81??‡¨T“¶7" t/ˆL®—);ºzôú±´3Ž¢C×U©Í¾Ì1÷ }ZÉŽŽ üvû=9ü› I"AZºvO§[R»Fc¢¯wŽ1_-È>SaèûòŒ³fÙs©ÜɆ™6‚ÎH,ùÊc‚'”é‘?În©F6åñOHW•oæ>g¦ã=¼³/<ÉÆe’P&&#? ³ÖÓñ !M¸wš4¡p¾*vÏU¡›NF6‹È˜QÍ«8ñ7”&žt³ÄqàKù Ož ⡸~ôÞ5ƒ]lµ•}7K 6]šKe–rô‚ÜHö‹×Ðß²†vô­‡ÌeFŽ‘ïÁsª'¸ÉÀƒ4G±§‚DÅ3;ʺÐtXƒ¬ÌaíÎ:ƒµò¤æUÀø•#^QÒ#z'׿Í2„*1ˆ®šINV—Ãé™°.×3GWÒöíŸØê¥Ë›gÛÓŸ3l™¢´ÔïÐq¦ä]1ttaÜ–&œ§-•¿‹@Pð>ç~}5ÜÇê¾YG¾x~Ž#¸åu$F† ø¹(œ}¦‘ÿo%Úµ‡é»¹Ü‹Î³`ÓZ=†oí|z§˜·iVãj¸Öû„i3ìf5*ÃASo‚Ó07 Šÿ’©Ùìòhm¼S}Eïî ïã\É`Al¼©Ì8<šB÷r ÉÖÃæTùp£|•É©evÇBÅ6|û FÇÔ„VJ#Œ%ð‘‚ÒJàì<ÌòXfvD!¬õU¢Ð©‘HƒµuháÓ=>ÀO;?e1Z…2dJYY-·ÇLf²S›" õÂ"x³# _ê÷`[ƒ u½vÉ™ò¦(Q!Ê¿¥CVgy±o¦\„ÕY}ŒµãC6¨Î‚§Ì§!Gþëý íþïþ·_Ó¹ 0ßU‹–KÓ {­(ã‘£¹,®ã~©äˆÐøÓîI©¶*5¹Æ™(ÜB'6N¥¿Ð㣠š.Ô%î*üP*–XÇød ‘žÀìl1à¥ýp.é1¶ì3Çk»jQ¬.‚†ëÆã—dÚX³þ1ÈûIàH`þJ7¡eßÊ1ê9žúòuðq€=NÛ'J÷­!½= üh)-ÉR¹Å«‰¢e ûÞŸ‡¶ýÚ þœÀCÜ8·w+þ°_ Äì+sY"ŸÝñT¿?†’âT¶lß <{ɼ«}ê!¿ÇZ vÍ(ª·™Â¼-DÍX™ä/DΛûZÀñÎån)àCþŽôc‰ }Ÿ¥N‹¬‚HÍdœ1»ê IÞ둹W‚;ïØ@‹Ç*bU×uÎÿ½‡EGnObô_ìC—“¬Qê^tU§nvÒDù¸>ͽ%Hvem`Ïžô'ár“1]â/|™âŽ?^:)Û ¥ßÅ·†óÙì4²Þý4”V¤ã¿,Rp« ³æ™’³·1©g#ÍxéšRKº÷‘HÌ€ò}öÔ€Ýâê<ÔÏw P8‰ëÃA@Ã2Ïb ®Ù•Hæ_”±­²tøÍyx?½ =J2 ÔWˆ1©x„rÏþBœ8j_lB ¿o寫c‰ì( „…àyýítö³¹ÔY-šÀÒ!{Ò-?âæ?ékÿUÈßòÞxKSdGcoL^“¿G6TÐ a?Òë]‡É.²s¶*¬J´Æg{Ñç{<ìx «gP¹ÚxòU±š3ù> ï[Ì}sß6ÇÐu:yä†_øzA5– ÿÀ%Ë«Àç¿ÿõo™ÓÙϘݡtæšPêæ(N÷4}¢ù¨ñ:Išš¦OWîúň6ò I°>è÷0ožEÓ ÂÐ(E–¨žµDUÉ^0M^HF~g°<[7µ25 Ÿ‡ ú(‘>Å*¼:¸ŸŽe+P7þ4ší¼\L|á–×ZÆ­èªDú¬¨ „S[¡,øÕ7 ÖwÎ""_΂¨ÿeÎô™¥ôú™j JµöPÏnc²†g ¿è‚ÂÇ’é,kcÊ AUÏ µŒu¦àÏ$Al‘µ%znF$p÷&"Ö¹•îOGáZYV­ç"ÐâL€šÈð´,9=i—M9œ?L¶}×!«LÄé·Ã+éK;9zÎ'„kOrOœ£^¹ÐöÁæ‹M±L«$¶w3¨½ŸºÙe”÷ûIµY(i,% ¥bhY¶% Ô±^%“8åE+-xÏBÍâmÄNFV—Dê~5‹þÍ £kü©_çaÜT·˜9x¯~©N橆}x;óÛ~<™Ït¥;vgúm1úî–4¹ÁýŒÔi•Ÿ5‰ÝJ i­IÑaË n [ÉW àÉ YÉEœ ÐnŒ[l@¯Tnæ–mN Ú—î3ëü>âºVçQ¶ö»9.óõ!kΩÂM­Tb-¸—šΦ¾¯á@•2éO$êüÌËâ#Ä;,žÞu" ÿJðn×%ªV’‰ý‡ê°ëÑmœw&‰sè…3yþ¬|ïÁÖAd4‡…ÎœÕØ ®ˆ‹#j*hø2Mâ–ö.º€›Š³qê=2×Ñ®,`Žn+ÅE£ëà`‡)”¡Î—ãitl&-ÈO§‡7’‰k°ûÎ3šó£— @’Èû'Á™T„ ¿ä4öÍ¢ƒ€uL(]Å\§ïWŠrØÚ$ç‡'UÃuÌWæÏèyÒ×®M>Dï!+ˆ) ¦w/᪂8Î.iÛO@–wâ—sKIÇŽ3¨dHÜnceæÝ…¸Xzû¶ýèzù›Þ03džá ˆ'G„Rwwl‚Þç—@¾¨ÿ«¶_;“•¬ ¥«¨Q…ŸÈþ|ùއË#סîX2[Ç‚ø_ÿƒÁ®[àò—dˆ- ¥R]°Á¬–­^»¤gž#†ËÑÑåW°êá4<_‹­¶@ÉRÔ(ÛųŸJ‹ «ÀýÍä}× Æ|åm¦£²tnˆ ¡°†7Víhë7’µb貪 ˜Yãå—NórÏÃè=â®v‘“º8ßw® ~¾«šgO!tw·%‰«·¥_G¯á-1mòW÷}¥;„ü–9þÍQ¹<4Н¢$'†s˜‡<Ë@(†Á{Ïmé£êYdsÇ)ÜK‰¿G9¹§–vƒ!½½Ž¬™Ò‰¥áMº?ê±øü‘'zɇ¹H®%“å…‰p:d)]*zîË‹á´ýóY>ÎRòR 5sL‰bçMØýÜêé$0-¨Üç Dàg  9+ò ¬&yÇ*þ‰æñ¦$v‹?ÙT5šá,üòy˜Â|Yæ ëv'à›¿ÅèîaG,(ö]Z‹.ZD9ö<}B¿04f++ÿ÷®-'³%‰¾ô Ø$ÕÆ‘)z³=iªJÔ«ªð÷®z+†Ìûén Ø}è9ã¢'Lƒª qRª@ý¹wð\Àgöw²!Uš_ËD ¹AùžTN™„9iÒ‹Bç³EÕŒêè>¶áA| ÿÀ]wQ…ì“<åÈ™YˆD13»Ú˜Ûß0CÖ&Ì´”^PŒK>º-ÑøMו*>í â ½9Ì\Rx iš,>KçÁmE³Éãσ ­7f%Ž9—/bÔŸ¿l´®\-mêIîÐÏÌra¸ájƒVÒAýÄ+öOy)~`µî·@ìMfè*Ju©S_ï<ü*ù,ÒØ¢ïF4ó©"-.Ïcñ2o²V(”íC,šcýŽé4¼]‡½‚_LéZiæÈ-@éÓ‡©O¬ u¿+Š3"kÁyö)|S›Žùg§·«ÓñŠJ/¹v§ÙqÉ%{²›w3l.sc°Ðq5wÍr¥z{àFC3ÆÖT K® VÉeÎìäo']"Y­D–§ãÒ˺ _‹¤}S¤ùî™lsúC°ˆÝEíh€æö©dóÄ” ¥_ÔÜHì¸qtØAÜÿ¤‚BéAT÷£|‰•$êÔ7öï¡G°þÕRrÛì¹Å•×ï…nÓI.uÈ–nÿvЦ™•ÁæÝGˆ| rö2üt =9S{ñ6#sg-§µ•‘ø" á?ÞëüÍLYÞEVÍ‹ÄÂçâgBÛk6S×·³É¸vxUØ@HÒ,fLä,^}_D™sJPäëM_ˆ»ÑD¾RÌ[¯Ã8zWÁüS? Îâ-sooñ¨_IG^V  ¤Ò—çïáçôôÔ¨¨0‹tª®žUºVÓž¶ÉQF(\yìŠdÖ7F–†Rpº˜hɃ)¿îSÛDyæb‘ q¼¦F}†qóM\¤vîV îÎ[‰ô“xÍÿÌóåŸE6¤×Y¼ã3 «ŠlÉ&=-0RÐ$O¼´ðþú ±à9F4ÜD¡qÆ.4–vú­#/¬§Fg-H¿ð9ÐoV$gmBAßÇ€]€Ö§ÔIÈŠ¯(¿) ¾Ï› ©¾4¦ÌM c¿\_‹STzðYƒ)nYB£TW£ç]ÀQófÎmikªoýö-@Þ‹adf¥uZø…}’ý Tó`ã9)jùtùUKòšÓê»Ið» ”D©ñ’3"O cŠýýØxëT°Éh¾én¤g;éã­ï˜çµiª›©ò¹Þt xj-U¦c ©¶Ä5Ì"YõÌ_¨R¹ý™´mÜ…ÚF‹ÓþmÉØìËìÝè@Ù(a7!í¢°v'ÐÕq†lK¡UfeñT=sRk®žbGöÝia_¦SÉϺdf£7iW@UÑN>ªÙ~Äp¹÷rs'i´9ð|=ÓN²üžúä&ãx5¾ÉW¡Âù‡î‘¿Ð,‡\“8zU|HecùîP¨i9€™K6rM´…(}.GßFú  Ì€ÝJu¶øòŠ%9iÅÅ“ ú™¸îx¯}Ê[rh‚^»µÔLg’C5É?U|G"ñ”¢Þ>ËŽdÿÅÌðJîœ)Äâêk¸-æ„í!òÏÆäùCGÒ ,×M‚'vGáÎxÞlò€›M=›¢Vî`ðŒ©Û«M›ÀgÉ) Â,“ýI©m¡ôZ9u¤ª=ïÃw‡mh|v1¹ù1êß´q ÎÁéÈgÿW‡ì[׃r—Ö²²\c2F‚Ù°³A¨Æ^‡î;Ñ@Êsá-‡§Ñ†`*í…"òçqj±§‘Ï2—¸| Î`ó»PR~„#v~-úª[²?µocÓÏo˜™¦BOJ‘ë;ï3K†j9ò7ûaKL)H­‚Ã)tÎö$”ýÉþ3ÊEá2cz3ñØFœÃ§¡NáFü©ÿ>wˆ3WÌ£­ÝXÍ)™£è¼Ë–»¦9š6?EáqqüðA„ÊØŠ’‰è˜¶„hÀ¥¯½©ØofG9l‡SÎ1^ËÃá’r3f£BåE©ºÃ6ƒ­°Fs#Mš7À _sÀ•’'±øY1 -\D6D×cÚ™xØ3dEo«øCÀ!’{̘.½ ‹dâm2•ŒTckµÈÇ•gñíûs°Ñ[”«Ýë®Ñ˹sK‚¦ ͠ßyˆã¹ÔýlÒUó‰Æ7q@xuúݯb3¢0CõÊNŸÏt›}E[íj,›Ë|m ÑCTÿj¤³•€w&Ë4þÀ’Ñ È—?L›¯î`»©)FŸŒ€EYã¬øšXL[pš2톅LfÎ (Á2zHÛUb ëÙ.CqÒðcó{cÈnw ¶Ÿ–P£ª0æòÃ4èêfh·B ©7R‚¨åî˜ûd&\~ Õ»¾¨9Ä2Ê Ëªí ¹N÷|›ÎžT\ƒx>‡%ß³ÿžr«+ðÊ`>H~$’ÍÒ!/E©[è±?†×@7‰±ÞbIå?ÔbÑ8ßée??Âg} q³n²¼×ºê°¦þ*£ð&¿ÍÄ«­˜üê}4vš3äyƒ•ì>ÿuÖ?v¡á 8v–”aôæv87¡Hk†âp]Ê ôl÷Ácž ÌLI7ö ÓY,yœÈ˜ê¦ãõ°DôlÆ"ö ‚½nÐ+õ:š–ž¨3‚Òwý˜=¸kHÐ ×)Da@–Ý2Æýe%ǬßrÝ ­ Ý4¾ý=úspÓ¡$Ü—píÁN‹]UžŒ÷õ,ðÔcw Ó_’öð&ÜùàˆÇEøéâa6g†.tV€’‘X¾â=¼¹¢Á<¤-xD#—²<úS¶á)· l1#ÓËrÁ¥T.]ƒÍzÇ¡&Oš½<ÄÖÃüVº’Í¿4æ¯2„GJžÄ)u¼zx¥¬‡¸c¶ïXÍXq:ÓÂýÎÄ‚RA,—y7Ÿ‚ÝxâsKP–*vº:nú¹˜p¨ežðÑ Qcê˜,Dúeêpº6ÕÝþ½¥h^ˆ=ÌKÀ„r²¼ÂˆrB¿á¶¯ ¡ÞĈly—Ê|½ŒB¿}È*ÓÝô±o8^ ԥϣàÊ¿PLuatôxÉù¢UŒ·X7cÿE•Æ• ÓžgÞ°ÇÿjÖíA§0>û;Ùõ?ÚâÛyèÔ£?Àyi³ý™­Ûôßzÿ7¾ÀA•Ý‚]ÞÊ9^ÃN%&äÙGZãK·ËN§¥ßdACf5úýx· XÆ÷ë+Öþ¦å¯–'^ÓɃ‡;P>מnkgróÉÃįÈ2\¥)ޏbû4æ¹’$#ŸË.+¼a­Íx®t‚;ÄãÃ\¶þÌœl(À„!”³9Nåº^cƒßuF˨?DžÃënb¥0ú8¿^š-ª½f.Ä^°¡_ÿ††ËâäÙ$بÒÅŽž’!o3ñ¿y+:àç¼4¦Ö`#q×Ä¥©¢lÿ/ ¢á«I4þ¥Ržï>Ó“Xbµ ®ºf2?JfCäà:Ðp ¡¿·èÑçŸâÍ—:prfª^f„ÔØýN@»p»ûëWÆ( þqÛ!;Æ™¦û3hÕ ÒaÜ]N‘€v½¸uN”GŽáüdqZÞ%L½–fà³]·À·Û‡W 3­e¯9gÞ‹ºµzônùXKUã þ~¬ä°ž¦­¬Ë’i êeD÷¿Rà0õØ /Ðäžå—H‚H©8ñàü-t|¬k[)LÔÁ“qßõy S7ªÁŠ=xŒ?÷eïù€ºÙ:ðBC•²Íit¦I/–ÏÕ˺ š4c)¼H^íi ¬tÁì8x}—%â£í#¤'>îÇ#Û<¸ƒ!éDþ­**ÈÙÑÃÕIÈ*j’pmeè¬VÂòÇ!ýYèž°$ ·L'ù…-Ìœ]ÙŒn“Y»¬æ¬è¿“QkÁRºµÿ#d?ãqP´ÍÆ\¤NräSÙqXós›x3ÑtÁ™¥5ì{çi¤Gä{àL|Ù A ç¹ËÐçZŠTsñoØ:,J¢î?ovÿ¸GÖc¡–;Hè ãï òaÛ­‰‡5²JðÛr nž1€#ý³¸¾?‚Ø+MfÆý1V+í7DßăÛÖ¯´vm&-¡@ã– ­Ø!†SnΤ¾q~ÓEŽÙûÅÉv¿ ¸_êçe“˜_7–`J®¨óÇÀ-­¸íóuVÛ峺ØÄþÉþ>ÖZûnðô†û4¶ô6ß@<Û–6Äh%‹Q33öÚŽhN`$/Ú씤3mܱô¨0–zŸD7]¸¥žÁ)½>›¦ÎÏÄiÇîaSÒ"z?ÿ%˜îТMf¾ÙgFy=ª,míf<œbºæ3ô‚/j[/…i£Ýƒ9­1xÞm;ÚÇ´±¥W|ð©ìs4W ÄåOº1¤ðñöâǾhž°Áli7b&ñÑ1ä‹”¤‡Ò¶u°xƒ"‘7> IÙ«ØØæ;x?ílzÌÁy·p©úaòLz/;5>žðíÂcO,QÌaÑÝO¨¦C,ªùº²ãó–B£õZ’m‡éƒl÷@Øþcéz0ªbÔÿg*ì¦n¬>ôf–Ç`ªvR…>ܶò“ƒŸ9l  QJÈÜxz’6$,¢ÁÚ~àèžGs{ÿD(åYN÷ÝãøüåXõ2NEÙ‘¥ ˜0™÷_ý4ûûÎ(£˜‚Ó¡¨óGB—<ûÀG½n0ï¿™£Ø† 8e¿/Ûâ«‹—ðé÷×0ç‰ é@/rÝÖ/ŒC+T²%œÇÌùè9¬Ñ»6>5’:!d}"“½" ܈<zôÛB*Ùõmd\ Æ¤¬é€õøؼE‚Y«;{2vî„ã“ÜóNûV¬*ÁPËRÆJï.~ä2×wŸá 'ÌæXG[ÃÝxœvã4|™«´ûØR{bR™ì°1ñvùÍ}¼É5^\Á93oôKb¥ÞÂÆvð½ÉqØa] ›Ž‹ý…²$|@>["ÿœh&ý±,a‰1Ñ9UƒŠŠtݯûp÷`gßÊF0PŽ»²ã sWh=4mþŽN.šÔƒ/ÞQL­GWW9F”04ø&dÙŒWŠ=à!AK¦¼w¸»£ç«·¡ÊÏpÖ³$naZÇz2ŸžuiÃ}+ÄàÃz¤Lõ+†™Äæl†óv~M1nüÃ,Úv ž¾ì‡“13ƈø¾÷#!|ÔÖo¸x'à‰KP=|¯ÊÊ“;¯iôŠMd£a&o¢¦–Ù† ô~–ŽYS$Ð&9O]ÄS=–X¯ ‹ïæDιËÌ£u¯™Y’£Xczž|ÛHójáIô\·+ 甸àjŸñ\ýGê0h›*D¬Ö¡wŸõÂJ›·ìEÍDc&Ž'øÂHb£#IBÙµÌ;qúp3ö;YÓùÒ_ØóW¢àÄå`úëá||ºR”²ò‡šë–k¾5!.©âY7"tf-u{@+†×’?_ØÖVÁ\\©‘AbÔOn*ÑQ)ÂÎ2º¥Á™ôD“î­™0çz-–y‘!¡Tÿ#@Ã^Ã_~7Ôk=G¯É|cåÞ‹’ñÃKáèeü3Bó ¶‘5‡~€àn³þ\!Œë“!ÿ†ÁEk:ãJì2ïË0QæBo*jlzÃQ×€ rÌìÒ¬HæLf˧p°ý÷”Ý.Nþhǃ™i7›e„# ¿N£Nû·ažÞ)Î1³ÏÊ+Æj^Þó_ý1S¤ Y?ÆážÍ hÚnGW¤Ç’g^h=c ½m'JůS ¡ î[ãd|üá9âÂ]¥â…'z| +ú^€Ú9]Œ·²9°þsï€ ÉßçÛ2bÙÝ> Èé_o1Ö«¬ÿ wÍáòÉ\=¿ç4ðg»Í÷’MYSéý‹!Âìw\oZð7÷%ŸÁ˜ƒæøðKlë61|ÒZÏoƒˆz Î54¶O–49MÁÎ])Ìüºh"œÌ§öÚ4'I]åFè(ÐGÓk°4âšW8ˆNfvøÆ5W°›`éüPóe#®4ò''„ncÍjMòïÎÈï%!S>àmai|Ýš‹=;°B^޳zE`o° ^ý‘'oV˜g‰ ÎG€eY9Àmqb 2oÀÀî _|yï±Ók¤áÂÑX\ÐÖÀ„ë„£mQÌÎmßAØ|´ g¿^¢G“ÅáÝrK\¶ã$¦­fòìÑzš!)°&µÈëU•ðñ„ØÌèdUbƒagÉjo;OV^fcë\˜ëX—V9jbkGJö~bL^iÀ»³³È“Y”wåj’“ëC%~Èý†k˜‘Lmz×4›ýý½¿WXáØy¨´ôþ#Õð¬âGôGÓ(­LŒ3Þãçy›Éõœ ,l™ÏCÙàg‹Iq›1Zeyƒcu ‘3¥ø§Ò×´ñÎI4ßòb%EGÏ1â*N|×m§ýF0÷€4Œ(ä4Gß?B;·kÒ¸Wæ$nF › so] £k4Èêúùœœ7©hö, uzU˜¹³G!&€àöíòlòêtL²àÇJ9x¿ÙgçñâR!ir÷ê4êS‡# Ñ,OL32.G=λ£Á¨\(H®µ»Â™ßSØCG/ WòÌyèbßMhAÞa–YN½ž+Ó{ù$Ô>™ê¸wÖÜEíPº3ø1lÇLì¡@1·½ß…|J‰ˆ¯§Q!J¾D‚̤/mâ; QÒÄvå%Ü|ú63E, %×7§ù­ m¶ G+Ñå•s؉„VHÜt†èÝ›BÆ)]é-KîÿÈîº nŽOaäŒÉ2‹ù;®²LÁj•Ýd㊔k³Áa1FG/¢‘B-øuX”–zü†›þAàtËŠ‰UP þ-ªŒ÷ÁRpӊ‘|5b®³œx'Â,*HŒV]e÷n^Aþx_ÄæºðÊØHók‡PSþ.'ã_.†-S¦Véoquægv“ñ>·ž¼ìÖcþ0kÑ1z+êþŒ$û§tr¯Š^Ài½ªTjÃrRIÈÛy'èd?ºÚFB_)í¾Õˆ7¶ßd.{°Çÿœƒö/!䉹Fy8–JyÇ/ºáÒaN[<Ù°†tVP%_ì+I‡ <_4øo+ÏÕ[AáÔf}»5õ_Fùt@Ëlyº·‰4º¶WÜ™eU†´ù“5}¶Æµå^±óD†ˆi­Íæ”3T$‘Fê[¢]æH×JÅ(lE´wµ¹°§`áäþ¯07U‘÷«Ò §À˜ oFiÁ"vÆoO<&ßÄ®C”Ú\È0qœœ·ŒvŽýcâ'9^·åLzÍl^Ê»î‰sÈœ‡^$µO Õâï‚ÜÛ^ö©D9fž«eÇpàèl9TNCVšÓ2ó.ýh@.ÅåÁòÜR’´ —‰©ÑO.3H׿H¸I‚æYà£Þ@24é’Ôdºé¸AÅ—ìÍ %‘ú—pGØWˆ˜QŽsK£ˆ‘×SîXR;9\o…-NíÜ{®S©f—+e-Ñˆí²¸+k´ƒ[áÈ-OB}rŽ6, öIðÊÿ 1\1›ž9ð‰i\.Zy„q[á¿S`Mí£tx#®ŸÖL?lµ¤ÿŒ”Iè+¸•ÏCo<ÕC¯ceÌ­¼¯àÞ9kdÂ`Út1"ê‹ÅHPÁQ\qT‘\Y¨OUÊËàÑ’ tÒ%Ôr:œ{±áNL§@{â¦Ñb.êJÞÄ/C9ÄÛ4Žm™Z‚]v¿ánŠ^*^ r©[åNŠRGé¢$2ð"Ù°Å™üL£Ù?ÒháÎT–þæT„°RWv“‘g_Ñ:ù6š«A²}QÁüLMúÑ5”1ýÄK£îz@~g |ÝâŒç¯£Ç׫҆ äfåÄ9ôuP»¶À•¬ýnçô®Â2Ž"ªÕ 1ž gÁws1=v§G¥Aǃ/Æ?¯7~„¬V9\(u›ƒ5Èæ“'*Êôªû³æo¶a›€;\llÁЬÙl„î,VÁH›VkI“5p]Û Üz¢–qìX@Ú\+á|…%¹uü&»÷T*ãyùÓßè¢2+qÛóÍeeC7»Kƒ»J3XÊLÚüq 2-/ûgl¤WDÇaKèkXrê›w<”ñÉ|ûË€Ê~YpÖ_Iß-Ë)wy¬_[ ›Ÿ>c"S\Èó&ø8ðWYâ3ˆ ãÜÛ?ûwÐ{önt[³‚ ü Ôáã ÜV!Gì8ÇN<0`ÙËYÞÝ×Ù½Ýë9ïÄÅ©Ðoš:Ý™nj¸†ef½¬ú©£ð±4‹Y|/Ý`«›¾b°Ò4þO’Ä­þš¸žR§xèû¶ýç:<ü\»}é)‚TúW$؉’{F-:ž8f¾EžÒëlTŒ$Õ˜ª û߀ª4á ṽ ž“À.`V=(Bƒ5ƒ˜ö¢ƒéˆæ‰usqç‰n\{/ÿ‰—áõ@òÕJоÞ~‚ûq…2—›“‚gaZáöpÜS¼÷Ž ë¶–ãV¿-8êÃC*2®Á6ŠL™ñ?¼+…‰%_౪4Œß¢ßϧóx%iŠu j‹Ý´W¬¶ÅH˜Øì@¶šùpunòrv„’‰ý¡XuÌÔµ]@=Ñ’”¸4ïq/.‘??nÏCûjÂmk…ße—Ñ"–—HÌ> á°‰mŸpS@þ«öÀÇ|`üéwh™çRÛE‰ß='öâjXzÄ…*Ó©Ö<äÈ$9û«~’¼š)MŽ;¤2kÆg1©1,r„Ú1nÑA¼äÂüuÈiÒ ™‡·ssÁåîKPð’GÎ̉= 8'y›×6âר=’+£\©»h³ß"·ëF’È¡™ø-AU›1/m{¤†ÞQеŒï¬n­Þxø6 ,£ü'w㢹 [5“}w£]·ªRC}¬™Þe8TC¨Ç˜L®ã¨$o¯svUvߟ‡yU‹ñn™L,OûlvÄЛŽÊ™ÐöáoÜ€6KØÖŒm³¥©å“•?â­bD{èLlÂ›× )ÿÙrÊ(2ÆK]ÉIæóÖ”ÿß/ÄáU)H´«Á”%ÍŸ»Òqƒë "²:Ä)“Þ$8¸ü*§ ›àýXK¦båà|‰îö€6ÙãvŠÍ]p…w'ãÕøTt^U²0!"º ŒQ e×OP…¹ ¤½© úkÊa^Ð>ÖI‘³9¸óðQúÅì.æ©5`̧p6¦Ä®H2BKhî·ëÌxa¼qã'|Ab¤.cüÔQ£[/ÝÝ®Wlò®täŒÄ[&c‡l&¬ÈÁ·÷ΰÛ¯§S¢?âJ¾3è$ÿ6w P‘[Ig?úò“ñt'šjж»–“ÿ'¿f}cÎŽ} ãl×ÏÅ!ÏYTÄ»öÄáqû öl¡ íù,FwÕxÃÑf~\&W‡?_gwº/6?ŒSçv{2ĵ—púФ¶^…26 ¡9g?9\·5ø#M{f{@Û2yÒ’éGÃoN'¡óñÇmÎÙ ª,Fî`–”ë“'¶dú±b&>è=ÛÈg€K|1»*5 Çñ0kûú;Õ#™)_hùÿ¢ ÌKŽ~N›Jâ ¸ì9b]Å*­ZI÷ni寄í‡ó3¥HAݰÊû‰”™FW¾Ã¦WËþ§ÿ—üjì`v$îýw4$™±+,û¢Ê†è^”!V‰3CKa®^<ûÛq6Y±X†÷u6/-K…¨jtÞ®Av¿êG\$“F‡ éžô¯°¦ZôÑbFG:–ù¤HOm— a!ìïª~<³áü˜ ßÌ£åQ¬‹w¡ã÷,pJ+$çè­éùÄÎYŸ;ÐeM{nPûµËh|Ëj¢²WŽÖ?]AÐ/͇ê½êTj K–øU0ê×€»º†ü›!£Õ°ÿŠ;‰iû‘®’låª(ëWÔ ¾6§Ñ#O õª¹ûaLôCÛ¶ûì} ²+ñ"º¿e§xþÄž“~®F[ŸïB…®;(/˜J•‰ÓOÑ—¹¦WòÁå™39™·69¡µˆ$±7év )Äí¾K©‘þ2 ßõ Rž% ñ—ž;áwzÌÎZËGª4K0pè ÇRÒªÿÂÐÀs\ùUÑ4uÛw6W0 žŠ†ãúŸ’tK\6^ö‚Ss=Š4+ÀTßuDéE¼cDzyÝÓŸ$ì¡b¾ÿÙÿV)–}:ÿî;Û“!^±*´‹Iv µíFÿجªÄþñ höyëf­bË#¾°Âm·àl~Ô\[Ê ¬ SŸ„æG„!ð»ý¢„¦ÑЖp \5‹œïÙ‹7wÁõvŸó;`×by”ýYÆTÛ©Ò±ø‡L°‚;l¶2¤2sß°öJÄÅôzóî#ru?󽹇;cK4ýsâ‡T‘1}Lˆó¢žÿ ÈôŸ…¸*S¾]XM¶Ï¶©Õ7·¡cð!’ïþw‚…œËJ8Sc/ÍØ©Wž\€w:íóiTˆ ã÷ YfžÙgÄzzæ£äYòiYDÞð¡{k®0–v4µORö[r»ˆZd3lªët\.sûWö±8~…滆ˆ½3!þ[,³Â°‹99t˜Ú2ît V£¬û¤Gd1^3þŒ½_Ç qÖ1B#BØÏi*ŒÔ×ù¤£°—¿ 'Zû“;—Â*9Aêzà4™—ƒg_† ¸Ã¢þËÄzW<ÙptSÔõé`úç°¹nùW4ϧ_R¬ ÄȉˆM$`¢\(~  ›ï¶ÿÏþ ¬ÒX2P~î=ÓN³È¥…ѰëD ½Ò) f÷î¢ú“1[ ™›CÞ‰KPúñþ ›O'ûABƒ•›' œpÕt'Î<ס(W›„^iæ|©gãfÿc†Ö»±ÿT0îÍGœºÆ’ø4wA#Ù§âÃmßcßZA’|˼¦áœ‘¤Uw[-ÀT•s8×o#ù¤–ˆëN°âÌ'viC2ÞÜÇÅÚqÊÔ sþd ÔÃI<x"{J ÷¦-­tÓ£†ß­˜ž‘Fè¾gFvkÉ×géqßhòÄq Ž ~~®ñÿäÿs"{§¶2óõÝÙÙÏc1qv÷ËÇÐ}G‰,ñðÄrj¾™mÐn‘€¼IAä6ן<ÕÉD‹ÙßpÚ${óqå|i ß"VÅGõ¶¤3ç53·¿ƒùý¢d‡Ú4|Øh\Õ9øO7{Ö¸¢Ç¦i¤ÿHY¿9Mzë}ØýrI<¼užËÚ.º'}R`ý³»°ƒ] #ºÊÐ]S€Â1êä³åG<ƈCå;Þfî45Zވ틮chÕmøQsÝ^³ù›‰æø^Ì´°Ä= I¸\ÀxôOcd¦ÏYC.Äœ¥ÿOþ¢aT,F—°ÿÞ`Vq3·8i*±Héç$ªu#=å×Cù¯ø€_¤ ^¼8‰ÿ÷ˆáŠF't¥Ø·n˜ù™·z4’űÔ35€¬.Рݿ½è±¶7àí KTfòÐOHê‚\è¹8”ÕÿÆ0©1œk±‰š„Ä€³-/m»ß@ŽLÉa>‰y€Ö#j;0ÈùúÍ‚Öu'Ð'Ö“Ü5w |‹8I›4¨ÿ]CZÐWIÚ7¿Bý=6äM4¸ê±L׬¸ûùlˆìd]î¾´Ÿà.Ò ÁJÔ-\ˆúw[²'ŒÁ?GsªþÿÏÿKl¯ƒîWx1o¬æ¶“±õõè¡ûVl¢µbèÊó i[Îjò/æ01~μÍ1 od½¢Ë8žUD~¯c µL¥»ØUôö›ËxucJóÙ°Z-”î‹€â®|9/’,àð“À”hº#?è<Ã}¦]“±î0`ôIÊß”Ažüv#—~\¢¥æ€lM(-¢yd×M_®ÁE?›RKŲíä<óÔ¨ŸÎIé¤emdZ˲<ⶪÿxÆR¾¢­ ê|‚ñ§1ú‡õÉ}¦ôé}˜+Ά½Ëƒ‘©¡4OɆl®]û¿ø!¢P$2…ì½ù½S«Ø µ»ˆÁ;mºtU.bš1zA3ˆÙO%ÓÉ-\¨L{¤<ñÜ92u£LØ… ’!Ï>;Â=záÅ~a #ƒ!?í#WÓ÷,‘öé@¯Ã_0µW•”ö§á÷Ñ;x¸;Ì®/ÌU§M/«™‹»4è âù¾µêûñÝ´5ú9~Ì‹¥mŸž¦|&bŒ×”Sa÷¢1Œ¬Ò!©9Ó±OÎ×ì8ëc4ˆ÷bQò¢3 »‡àÊÅRSÐ!!þJô¯?=È¡E;§Ò7åžLÿ8T;¸ýOÿwν`ÄzÁúa3â#eMd®lÑ?>ø<³ áõW¬3¢MR6äé•\tUÍ 7/\àNï¤}Žû‰ôüOÐs³ŽWªMUþð“øwÍôË4S"¾~16_C+Íø`µ 2¹™jÂh@lj< wùzá·)iˆýŽ Ó$ÛJ¶oȃ4)DBŒ¯7)Ì.Õ¸/OB¬ÖÀÏ6 ’&1å ¦ÑÿjŸMoÞµÕl·NÀä8œ+:„Õš¼äÞ¶1”ÝÈbÉ|k®üï;xäÙ2ºïçv’°AHŒÆjï³ov[aŽãEüòk­aný;À®[%BÏÈë±’>õ°o©YÞíD´Š¡êèwnÃLº¦ á™™0#ø¸}—€˜ÅŽäÂBK²gü&>Pm¾“ {SÌaÚ™ùðÇm}SÎø ÞbWÆUÝ’ f,ì H¹ÉŸÅ£àz-†3#7å°j› aE|Üšš…ÓãÎb‹–,ªÕ$Ñ‚‡([t2ÇÁ™`ãðíæ¨£“õNšÎ¿Š„ª«IœSÿ°;fÑ¥J‰èsÀ’^ÛÌ¡ö8žÔJ3w‰=+Vú&òÞÁ›ÍÂ0ï„QV/…KNø?û±¯{n'çF+†,AÝ ß.}hýÔ#'5éù“,|z¡H¶gFô0”`˜sxðô®>ü¾wf7_c8U ŸpÄå61˜ñ&Jl+ /Ïî IB»ç@õCðæx;ˆogW߽䦘ʹÚX?‘uLÚ]U[øÞÅISÖåd¨ftSl­£OÏ‚ì[ej1ú—Uk‹† jÂìúV·ýf0-ª 6¯ Þsë n»+˜š—AëÿGÔwÇcùÿÛ3Ù{”]DÅ}—™Y44TÚR´•ÈÎLÊ(+$‘q_祥½¥´#³RZôóþ=_Ÿ?<<îû¾®×åéœó<Ïs]çun¿SÀg`D”>n¤ó~L¡§ILûbh—?±ŠSM*&Nø?÷{?Nr-Á(KÌÿ#ŸŽ]b%Lg1_G[Í]HÖ¹ÛЃ.QðªÝ¸Ä£ÿoŒͦKÿŬæ6ü3p g½fï><Íúù1¢ªwÐþY!lŒk¦2/öÓÃMŽ¢Û¿E'™JbÒ½÷6I(-)ºø1€ªºÙ¹c2[9?“<=º”zm•§:!Ýz2‰8#BVî C»»ÞÐ%MÞõ$7K›8.3&òÿõ&·úÌo"h1¯:*„É—Ÿf¤¸â踊S¦!FwɰÊnÃW‡öîûäÞafpraÄݦYR ø'a&1Ù©?޵žž“¤Þ•G°Bä1Ë/g?zõ—áígz@-‹Élw@^Í1HòðÀ~ºœ ¦Ð6o®2ÐEÕ\€ùð†kc °Ví!&;Ú‘½Nü2¾îá¦-„ÜÒpú| åµÞÎÇ5”hnäµç<ùÓ ú™kñ¼J\Z5 ¦êûQž|´ègÚàZÐ E<º?˜ÐçŽþ¿÷°'h¼~ÏôæpCg Á™S&䋉Ù,äA÷¹²üÎZ`2UäÖê±—R‰Êœ…díç]h÷l }ÕÃÐÛ*ÇèÒD¢-B®]ž™•l§F9´†>ÂË¿•è`ª«Ì¼ÀNùñr÷ì¿ÄýP¿Ü>%Hþ<9Çîß,Î}Ô}qZ‘óNq"_òáù<Ì¥E¢ÔÙ}2»ía ѱӧ’Û“p+¯$9`¥O¾F(q^/Ð¥µm9søèõüÔ3Óœ£öë óVp>ùÄšÒL¾ ›b¾CÙ­<8mùžÄ¡µØ©›+Sr&üßè‘©KÎcú'•â’;ÌÐòl´ÛžŠ G:셢γÌýÏñ¨À/xÞ#„gïO¦ñ"1ÌN \ˆ×¤Dz`ÿÒ°~~ž‹"œ5ˆáèBfÿxqN>Üt&9„®ÿô3³3:ÅKèŽÀwß„f-ßICMÁ…g²dÝò ¸}“QxǼAWtÛµºëÑ ×š0ÆêWÅk këdŠN¦¾®‡HÒN!fèÓ;¼´õs"´‰Ì¦&ÙpÀ°™ÝY»œ~ƒà)öL”6\ân#fζÔ=Q„$, ðÿô(~x¤ÅƒYCmœ€ƒM áЕR¶ãˆ$Íì.…¬9 ´ä¹1ËçjAO9œþc¾Ø%Õ¦Ò4b–06•m fìÇ–mg1úÄ Ø?Î{~jÒCW¦AtìMfMÝ$œM¨o&m¼ÿÌ7{ÑY“PïósèªÛ̾Z|œª\5Í}i­«$ç+÷ lGn?ù ;á•;‘TaÁG¼úí%gº]{!MX¶†(O"z'Þ‚¼¥½8p«TΑE霗cáï‚“`¤‚(ç`M‡½ÓúwñÓö¿êßw$ñ¡€ß„ýïéº2†2Uóï¢mÖwLX»Ž®ê3¥²—œq¿{ ~ &¢Äp±ê Hž“†‡’nbÊ€ͶodÒìN€@ÙZ\ FÍÃnpµ‡ ³ɹ¯Š#á^Xç>Äð§L!&BŸ±B»öAÆÆÍÜ9]Øœ~=orŠÝi‹|¶P¿{%žâL ›&S!Úƒç.Ñ^oMø”ç`ZÞS˜îŠœŸºô¿žê§+3V•¿¸}”1¤v>>vžLåFj!°sÃMɯЏtëÓ÷‹4vé®` ùž…f:åÐf*†¿e¤ÝwLàmM…QŸ<ŒŒÃÏËé™}¹kϰó\˜]ƯÁ¸h€9ù=“•+Œß4)òÚý ®‹yÔpóT2jÙ¿âº$³ÿVѳǑةýų7†]3VŬ ó{­©´úÄ|\*H¯èz‚PÞZ<ž_„Ç8Fìg•ÆÆ¥WÁðQ<»ïn9XL¹ˆ3ÖÛÁ¾+ÙlL|/˜ÈÅA›ÛØrŠ—:„wrî_…Öâpw6}¿e̓™åýì×Å”·w öÕÁ;Ê×1]Y’¸›éÒÂèjfHÕ…Æð¡¹{SQHµþHLøÿÍQv‹Ó3fVg?LÑ´'/#áßS2ö¡ö’{À˜5ã¯i®9”oêeTÞϦK^œè‹ËQ"ßžéúÈhÍ·›à_Ž(ipjdør˜Tñðç{‚.Gªá ¦ÊðÈekf9ˇ/’d±@2‚ÑqÓ_~,ÉA‹ÖiœkQªY#Ì„?» ¼¦4’j€[‡>>ºýN¾ßJ4ð³xýrô”ׯÐÞ“xxò\¿ *³(`5¤ö~·ð8®¦9™?eYŸpŽrþâæÒxbufCYÐ0ãsínÙ2iBÿžY7!uñŒŒ£=;%66½Å¨>ÉÄÛ™¹ê)ô^G ÊÝ”¦{È"òž'ç=aL¿fâàž¹DïÅf&Ïu 9Aa0a7WoM;Jô.ÁøÊ-ÌÍÇñ¢š54U;›ÿÊ®Á†v‹nŸ'ÎX|Ö¸y²Ê3ÏCÀ›t>ýM H¤á®ñ~Ö°&˜œYEþ™å3ñù~h5ǶïUÓ&V ¹ôáƒùØyíïAhÚRrã‚Ü&HoÄô²Å/DÈ?RÃNÌÊã 7’ð` =ër—9âð{Âÿ¹SÁTÜšmlÃþÅÞœoï\hNT¤_ˆCùôItÙÁ~nÊ=uüpeˆ ÊM$ ÇŽs.Ôãä’èTñô.çÑ{{„É¢ìÕ´Ç£µ}Ï\“ðü}Aj±F•˶C¢‘7}¾µ›‘èFy‡ÿ^&«×Ñ0øËþÒ>2?¦±Ÿ»ÈeûÅe9+¶1­ ðæÈ,ª/ÊY£Ï Q X{çW9Ö<šC<_-¸ˆÏôVƒîŸÛÌßQ+™õ¾äÆÚ+0Ûû>[‘ ¶uE U9wûGÒ}U¶èÌó½Ú„iÙ%Ù¿•L¹•ˆÒ[ôX'¿´lmc¢ ‘5J*¸{[73õÔoö<·“8ôipêc0ò‡þ&ãiç¬_ßYt5ZAZ=ÄÉ«áñx^B·cAXÜ›“§‰0•s½!ÌLŽDÿ\Kžj& }{Ö%Tz*—=Ý:w¿ÆyŸ¹zøi¼«3«,ÍG#—=—äO¸ð“9–]S‚•žb¸(a•É,”ݳyÉ<Y¢Sc )D÷âaü7ƒZµ§‚ÀìàûÖÁÚnžBC=™Í¯HÔåU¹|3÷¢Í!y²¸C$ÛrpòžGðl¾0 i<ž ݯºAñ£;¸5n½`jB­iŒ}paö3UœÏiU Ú¥EK9sˆ®ýðçð/-*Ôl‰²{Ë8/~Œ9Û„˜]ƒÔð™‰#1›Ù‡zÎ&°_jÊ‘½màÔp›;œZËÎ\v{"þSjA|¡3ÃÜ‹>“–#ŸÙ—¸äçKu3¨è 1Ö¾g`yI…íÜà ðõšK£¹WÁ_× ä_T¡ay$È}íbÂ2C1öâ{Ãt óQÙ¶q=ûÔ“¤w8­IPsYŒ“³óÐþ‰?ø ¯9=~öxbÆq¢ZîÞØ* 7ŸÀWqÇïÙIì¯çŸÀ¸Nfû¤Au˜Ûü^“ˆ<;ÂùÒþ“ÍnÅ×ᾋ–X¤rø—Â÷Ììc_ "œŸÙz‘ž;Ÿ}à˜ §ó\èòи`LŽÞ“¡ì[~ä ¶Æ’;¹ø_É΃‡£àÚ‰,˜Ä­vv«»ú ˜ hWòvnAÇZø2W bÔVAsï|äÞ”eìÅ!ÂÇüu¼1më*H\6›|×+fB´6a›½,¹ðùúRѱûZwL¥Ö;uÉOgæëéãäjúoP : ö-n½„3ïž`žæ®Çò³CÐbqš9[q•ÑÒZˆK_ÌGÞømŠ.û,cÁþuXP—ƒS$(üñ¯ÅZyð%MEzüØž•àÙ y¯ÄÈÒü4”|)Iÿ›¡ì¾M‡,Å@ËPç º3â-ø;`Ú×t1£½ÂtX~äì%dg” y×¥@¼†6áº+­(µ®–i–Ç+Úzpw±ÛÀ¿’Î?ÆKÜ™A¼·žYq–HÊЃ>%°é„7ý³â ±RK¢/~žF¹[/0«H•®»Ñ͈ô¼fK®8•½BPè¥ç¼Å[DŒLW7Æ;'S`Ißi‡MŽW™ÎíJôuÃÐ\¿€œÐ®€ÿfS˨>áü~^S,¾ð›å­ŸAÎnö!u]xh563ìfô4h꪿̚íö‰¸Ö¡“;jhKÛøŸqj f3E»§â,»kœk2ÃÌÂ=Œãº°|¾ùÔu™YóžE7aÌ‹§FóàãÍcœo\UÆáL8»°Z‡LíºJE¡ôß1úîmû³²ýÅ”á¥_Î4ßL¤G€FÀlš³ô<†<9Én?›O\]Ѹè:pqÀÌf:9¥N¤ á¡r‘wÕ'Ö{ÒqöÉð4­ ¾öú“¬óÖ¬ça²“ŸCØkà繂1s†õ•†õïý9b˜vf;{01tV/Ã9ŠôÒŽ 5eC'•ü†œ"5 ŠuÝŒ”‚†W—‚#¡ÈEH?x‡Ý<7£Í®Bsí3ÎÅ_xàÁ¸V}R†ë¹káòP |~BÙfoÊ÷ånŒÄ„s@áØE|÷< wÖÏÆÕŽÅÜ´±;aQˆ'~¯/?kÚv[•nñŸ ±K¾±2}¹¥ëÂZŸ7!Ó¬yÀYÏö|P‡¸Ît´ [³Ÿ‹àËNx.Ot×Ìdolàb³ôT0‹zÆjŠ/Gž]6RýMù~`ò¡¥øåîO´+UŸÈñªê .mR3‘à "œ×›T¨í̽ò¬ÏÕ¬ÈrqòÁz/ÊÎo%u’?E“\9¼6+H‘ V‚¸hdiŽÂ¢w±´às9‹ØšuŠq•ŒBý„¦5͘DOæþå!Ç£ÒÐJy{½gÍo¦¤óGú×6bø+ V\ƒü]ütüXF?£Ä öî[•æ¾¨Å›ÛЃæ;¹bÌ-66IœˆoÖ •U|ðP"r\K&së®l`åƒæðµLÒ»2rì…í¶íë7 Ø)‰Fb L• ùaÖXãÄóÿ1^{ü’© ^ì[¼MSáá=ÄàNïég3•%n´,Z¦ß{…Cò9Üîü9tDp ÆöHÎ Œç\²ˆ÷pãÜUÏ™ÿæ;×^ LȨ}ØŸCޤɱíGé—Ô4²F+„^‰OÃÝæÉøjt~ûî¤"2·ÐhaV4n'çü`qÕNâäø513¥vF½Ö_`Qž1$ìhbóï0Ø-£KJn–c®0 1×cIÒÂ4ú«y ¹Ûñ\óXüÌû mŠø©‡D>”Žª]1à§0‰þó9Œœm)ÌíÏ…XySi‚ÿÆ\·£•­ IK’\)+üÊH¡Ë´]ðm’ ÿÖ^'gR‰ƒÎ¤hä'ØlúÂT×(m>ôJÑ¿pw”%àâ¦-ŒZÿæµÑ fÉ {R\ªG×½‚¦Ôsæv&1ÿUͨ}J®èq<7¸‹î¯r ÉkãÐBL‘YØ» É´+{ Y+kÑ7§lÂÿWsfüQÅßž&ôôL(œ§‡÷9Š0}él¸³R Nff–Ânºz•0}1TIÞûÅ“þÑÕà»–îÖ†ÀJI¸¾!öÅÍ'@ãò×Ê´¥§Ùqh¡[µp!« >2½ÐÛÇunAØÚ(_îM¶§yÿé‰ õX¾o‡ÂÛ#eð!î jÂg7äÁ§Þ˜j¦ÊƒÉÌ \ÃWF~>¨+éYäóðÀDþëÊýÂhÌ¢Y®P¹ ·°Oè55ŠN'y Ù‘Là\ÒW§‡vïY%s’\ÍzO&MRæÜö°ã/?9é…+ pŽGÖÜüe‹àÀ‚ûhvø>SNîÀv'>Øßp§eŠãFÍä üÝjÙµ/¼áúP°ß™HkàU÷Š™'[òñqàJìÌQ#_°ÂG¸k0åFßoÁ,ŒøkKLñ*ÔЬ¸ˆr¸™soÃøoæî—¿Ð®ÚÉ>¬z‰þ[ÿ¯†„8›ÑsŒM¤0Ö'…™*¬Íü>”¾ŸÞÃÞ}p‘Ùš=ÆÝ¿Ú~ݶ"¿‰",aIöÕF‰!|z—»xæTjvQ’f±³Éø5ØÜ”pè×0(å _›*ØÃ\Ë­qé×3pE÷îê‡Ö¾ÌÝèUìŽbsìésÁ´’Ùƒšþÿ¶Ÿ~—š²v;ébn7\/ºŠ²åz¤®8œ±^CÈÂc¥\…˜htq¬„Ò á‡ô­|5Â<>ŒSÓ¤…×na@ùYVWmxú<>ÓæÁë÷àñ7°>ƒVŽÊtl<—,•qÀ9 •:g¯¶ƒÿ ²nM»ô >½>¤2Oœ~=YRrŽl.; AŽ/aýÅòn#µhj\o„K†¦ôÕƒ. ")rÔT™´~›Lc++Qì}%²Í„ø„TfÖ“]ð%`ÎrûÌ6Ø”²ëRé×}øáâóiüȘпŸûxéR¡yã²ró¡ÅÄúóBR±u{Ça¿v3c.J r¾ƒû‰(:':'¥^f£;MPqð6ÓõoŠ5â9Ϧ&(ÁÁž÷pÊ ž§LGU¡0Úûh2yéüÊËbÕ^¼¥¨Iž*“ªc+É)}†5ñ΋€Þ=ìל™ÊKüÖiQš£Ê0K_1ìñÕ$JH ±J-òøâ®©W#ú~~‹­†4:¦eí(ìÑr&cf…ã±Ú‹;gDñ+Z¸_ß ·OÄ›M“è6þVÐ và2|Ýûç¿X›°ÿÊ€½L]öRèH:I%½Ÿ`¨{PÊ;³—nÂ-fdp½,©3¤¥›¦’G^ ©BÑ#PÙ¦fl&Ž"éìã¬Pt]‹ÙŠ4×c1œ¾ S:~1ë/]ÃÖdwF´Ø–È´ø¡÷ÖA6úá,zJùèǹW†ÙŒ¿ô^tÆg ŸÉKøÖ¹ÛWÃR^ü³É’¤xÚ‘ÚÙjôl÷IÆÚù5z[‘WÉ+ÈM[WR}m¼¾}\Ξ|*L˜MÖ„Ã8ÓVÚÞ*¡“þâëĸ¿ò4ºûfqÖ\ö‚Ì9>D}çlèL}‹çzÿïù_‚>ê=K¶Ý7ûx7ðx¸™ûíd }ã°’$n‰ëÏÍ'Ïyp!…°¿Ì.¬ §½o^³³›çà˜Ùܰ”Ÿ\Ý#«òß0¢Zð’z:ØÍµ† ÚÌÓGVn"¶µoñÕ)F-Id,ޱ÷ûÄð*¦å¾ú¥E6i0«¯g¥Ýfúï+q^­0ÎIÅm‹лkm™œxNµÏ ˜ S€%Æ7ÝE©oZÄßÏœ(顜æ"¼b:4ž;âA¿3à~õ>í¶Ù üjÎ]*N]¯GáÛìàûäÎÙ6¡ÿboÝÆWFëøYXk§Žyß̉Øýr˜{3×g˜Ñú—Ihß`õ OmÚ^aŒ[Çg[d§J“—©ß¸ûþÚ2Ìzjï"¥°qçÁp¸f´kÓ»VÓÉÙ7é´qL‰Æ¸7àܱóø¥âäù¨W+žö2¤½­éÖ±ò ¶s¸›Ð¥þIx·"½a·®È4æP}5Gâ”x«ñL/™¾Á“^mˆ¥¶ÊSY¥ 7š¹«T¼¯²SÎÅGR=o5•ßß?yË©s_é•t´¸™}èCÚ—Þ`Œ¦ß$³·”ÓÐü úÿ7?/&ÿÛËêþL€ÞÇ¡ÜCç|0Oñºé&‚‚¯1 |y¶:Ù~â½ :ÕLþ%xþççæœtã©Tª$¾ü5$o¤E º% þ45àô¤ãÌýzà¼cÛ¶À‡`žl&˜\ŠkR5`ÇÛ/lÌQgâÇoG: £ÙÉ‹§Ó3–Q8ÒØ×5¬è¶Uá°a:ñr(†a»{èÑz–máƒó>`¶žxo/ÇìÅ—Á#{Õ8ŸúâÆ”3LÊ}%`u*«¿ Zžš“öݦðû® nð[ϨկЂé_Püå,Àó&ôŸNã&øþó+Ûr/™.z‰îXÁ±tÍdY1‹¬ðÍF©Ü夎=‚37œÁô·ÏðÇš@Ö}¥58ÝŽ&K½»ØYé/ ìÏ\Ò¹Ívõê’ŸŸl™E6gY~榛!l?äDîôäšoÀ1ƒHän‹JÙöU¯àêk#úë˜yã…ΊÉÌøzØ«»óÀ)«~è*mLÖ¨„ýü©?M§·–º¡]±¹sC•äo6U()½«¯Là¿ÿô+dÞJÄmþBdÿªëxJF†¬›N>\ØC‡;š¨w6Cÿ:¦FEøÿÏa®½™€£æSì„;…èË#gˆä¿ÃÄ·ã¨ÒPËø¡53Æk‡ñX °€×Ÿ—Ñ_®Ðœ&CrGx6#fy o󘳌ÇbjapІßÉÅ@Z»Ÿ’œw D$,Ÿ–†Óªó‰0‰æú%Ë’7ë·çD&yoq scU^¼ÄˆYÅøªé•Øý›ãi?ÊÆ– ƒßB_j!׆kNAÍß"*ÿšCOI¬DQ}-t·¥ª6Ä ðþóP|² &ô_P¢9e1™æê_¢*a¨<‹—”·¢vJÁTÆP‡Ùµ¶†„oLjü”3žUn„‚h>ú­ü"òšxÁ÷ÎÇÄöžy½Èí % ÚÓ%æ‰Ä=c+ØÝˆ&Jœp{r¹Üga:»d÷ÖÚMÄx¹5ù1ÏŠŒü%tö]ÚÜÜ®hÔ)ÛEV6(€ôõ:û¶„¯®"g…½`Ϲ(ræÇû4ÌÞ gú&åŠ4ª‘êÂôÄ;Ezuš<å;ÐB4ÇDÉ—ƒóHfáeÚÔ^K­~ ’Ýfz|N-®áå¥ ï^0ÕÕ°ºs2QÉ*àÿRÇY6²ÛAìžÞ¼?— ½>Cñ7£Õ7ñùâÕäA’0­Ÿò‚=äÂùÈîËS0.O›r’5©ñ± ö'ÿ=œsŠ!ƒqžXp¸Ì}™ÙgD€-{Î5â´ñʧQЖŠÈª’PéäólAroPéÚw·÷Ï!7¾Tá˜Ë´ýþ¾_÷‡³óÚqãž.Ž ³•<=)ŒI#|tø„'SôH¦‹ÉÂey˜2®; Wj¶g/M¯>l¨,)—6'WZHà+aºýC,á{P‡Ö¿<ÈóáDãu5üw­K'CÁÇ%dKgæDþÝŸ]ŃìÏ¡e„GÐ ì>€G\+|å1ËØ–Bg9†]ŸOñ‘ßRA×6” xîbÒ–¾[*¿¦•IÚÔ½L‘ôí’§Æ{c˜M_ \ïˆbuD=ÀÂí0öÛÆQŸõŽ EJ%²«R.²mÆætü²/•žNަ…¦ c _Ôà…®˜Ùò6çÂoådô»ƒŒÖiVôO+ÀÙ =¿Œ™ ¾3lñ¥u8ZÜŠ‹\À þ!TGœ`L¸‡p`s(ëàÎKëvÐÚ•ÿ¸ò±˜¸­š+À·!ùmø(ò¿ú7B›nóR ¥}¶Òüd_:õéÝJ;Cgƒö©iU§iç3m_ß: .Å‘$jŽ 5-›Ú´©OŸÖÅ”²! óh‹ ‡ZÛ·ÞÁ›Ø#IŸzÈÐŒ£:tô 5‰WH¦ öðß\çßl).2³¥ççÆÑg3ÍlÃÅú“Ø™k,餺p™lNšWN¡Lè¸öQ-ÄË9‹ÐÿÐp·ª“ëÁtm/ŠnÀrXéÍǹ÷x|WF)›6R.žËmO·'úóžsg‹E’™ëÔéV‹)jNýj‚ Tý3ÊyU±ÝíøþÍñ ÿ17!>;Õm·èî#kŒè±Í± SE¼ç+R8èI†‹`ÿÝ9´e{­šSM5…Ø c:0ª´›úW£nÃCî±s=IF_™×½$c‹¾ó![;(“îåüõ“'=uËq{¼ Õ q¦nUËPAJ¾É?Uƒ.´ºOz2g’uûJPaa#MîHÆ›‡ŽeþV$Y¬6„܃CºmHŽ^XãŒÊó#9îÇÚà‘¬uw—³;1Ÿ,û–LÝ7iÓ|õº‡kéIrf“û½†ùQ´Dô^cÖÅ’¨xŒþ ýlÓµ²z"ÿºD²¡¦„ù”0^ë¦'†ééÄ=ЖÄoÜÓß¼c]øþàHŒ0^!dÈüç³J³í‰`­Õ^ÀƒvG%aèBò%ZÐÂ˵œÏ)ÿÛÿ²GÛ–¹¿î¥­ˆ€;,}êˆû® I¥ 9]:„Ÿ¼Öa÷–®_‰=ÕóÜCÇ_CòO â=œŽP ]“ h’Öuœqà ê/3„Å«5ðHc yü$\/Þ­¨ÒU§í±ó »ßÉŽâr=øð954‰ÚŽ•¥Ysa-ëDüªðæE2<å7ŒšYà6 uV“™[™ug^¢Äªg8ÃÈøVo¥§Š#OqNζ'ƒ¿¹…b0 æ8™9ù.H£ 39¨YíÆšñ$£9xÝJð›‘Sò&öÆòöW)\nûðbS smÄó\¿0Åm¶¤oÅ?©ŒÅ®û²Ä¼þ%Ç,£«Qé…ò›DO½lÄ#R´Af2Ñ[©K×·¯b6”ΦKæàù‡ftZ¡&d¶?„ïï8Xù1Å‹&ÑûÁ³šO¦Yßa D-KÄ´;õ çítBf­À«éÓI\*Î]Fª»eiúÅò±*svç1J5ÛcŽþ7Ü7lQå§³ÇùÝi ²Kà’C«‘ÿíRæþÁXðû9,~~ýo^ô³¼|s*e™X¼•ŠŽ-Épû6»öi"þ¡Ù˜I‹¿…Â!þã~8ˆ–KßÀ·~dƒz6BÅhÈuÚâG…˜2žç¦l9«|EÙäÆ{hJ誜 Æ¢ï;Z7G±"û<'1ŽÆ}ÝJSSص©Løo òí¡I°Éaj¦µ]Ðü5>mGEÄo¡íº»‰€Ã¸ŽG)ÿÜ©túu2ðã/à|‹HŸÌ§ŸWÔáÌÔ\¬¸ó ±÷£UÉž*CVè® ÙÍs‡W+‘ŸusAÒ?pÄ ^YŒg8BËœ? ±Þƒˆ×Úà–tä§ôêaûÒ€ÐçYøS¾‰`¹­ }³ã0wóÂýðâV³³ÿ .6y%Òè¨Gg¾T¢Ònêœ;½Žô£”$ñÞÚŠ·ï$‘§ ®tZ¢ç÷ŽG(±`€³4]Ún| *%  WÐ׿ûö¥´À;~ÚGÁIÌžeŸÿ…:2ôÆ$°q;B2ݧиª38b @±g1mÝúÍu}é¯#¬O€žH¼T§J=ê7±µ„Ðf1W¹9à‚G¢xMÔ™:®YI‹3H¯¯(]œêIêHB¹Ûf:xó7²ÏÐJó!8^uÄÔrA¶ë9f|µ¢;|ªáè݃ñ¿ò^&\ FƒJÿ¿_øCÙÚ´à*ø—Kâ…>!(»©Ç3¶¥O>ço[ºÈWˆF —Ð)RJ„ål:X DÍ÷«%¬ÉÍtê)™¿î ’ø4q|°Q‹y'>•Š3)äúNÊØHD«‘݆ÉÜn<8ùÜø=…æ®XHöï¥ ﯱ[º<ðë¢FæÄ¡T×_H>­W‡é¢÷èöq.‘•¡®:;ÐÏ€‰²[E¥ƒý±swv‹Œ‚âø2´Ÿ„µ¦Cñ¤T9¿x©‚_…r£4=èxÉKÌrɰ#ËÏ Nà—?÷ŠÕÙ }nôLB ”_GŽ¡+ÇZ)šÔ(ì¦ ‹ƒE6ïO„ äì—Óø\G‘D´cƾàÔ_Œ]À ²·SŠé×Uhq“]åÏLûã…è’®H–vÕ0;Ú§’¯õ°7¤Îó~n”ZiHê÷ »QÜwì 6*Í#¼ïe1Á,ÊϨ;;—“­¢‡¾ðPýP^"—.BB]¶S‡ØmPX¶<«¼ÉjÞ4ÔÛÔ -µÈ ©"æÂ#8Tc–òÈÑ—µ› r¡!Xê.¡1ËzaCî'ŽŠ0í9ÌdjFNÔ?Nêpd¼nè Ê£w"§Ð2ÎS§Ï ×v}ÜàîE7Å|ç|\Y‹g?7 k|Þm USìi]܈üÇn›ÏG6»ó¿á ,=z#Ëüh讓Ð#Á…ÅÓµÉ볯ØÝ͵ø;ÿ/LÍÁEü§hNQ<Ùpí$i·ø&rxî?9µ|ý¾L ŸÛ›@˜l1Þ¬9ç'“bvÏû'ÜûÖZÐ;¦Kÿ­÷¦o· Ú¯TŠ;‹–5žƒ% XW— ½ƒ×`Oõ 2þkp<l¯¿…ù¾»àÄ~ì}üN»xBÿ ó ý÷É=c8°íáÖ{Ž0賿±öc|Ýç™J2 ×o ÝÞX{48ähîj˜¼èfË‹Q•B¸k›Îìžë]ö"ãœçF·5ݦ…{ë=A­2ˆ¶ùÎ" 'ã4^_bYØÄ½ÖÆG.uȓڇ|4ËcwËQ‡ˆ^ŽÇÉ¢¼4úÚixùb>û‚Ãq5’1›˜jð©Ç†(ÈÍÂV˜Ll=fÓ¨Ìu$&§בöÙ³Að~²4vK‘m%(7½9–Ýìi+4häTìOB«I¯ 2`'ûnQ.lèìæ¼v•×ÑøC³Ê˜Ï—[`óŸYô¾J8m»RÿÍe^¹¡‰£#5®³¶=ÃŽáxçØ< ã½dÔ§ëÒÈRz1›yõcÞ„spfÒ 6î˜Këö% jþa²:¥ƒ-è®E«É¯"Â$Ô¡…çdêón?&9oÀžâ-÷mø-úöŒ|àz¯Gë΋ÌeÊþŸ¿Š(û!e\s—Ïg•že'v¹qE®Oúg© »@„ÏÚªOãÔ ¨tŠê¥ÝÅ›ÆöÄÆx;¼ÂW¿CɬÂ?¬J­ñn˜–t}ºôzÿ›àÿ¬m&{±†¶_MÁC¥Âôj>ÁxçZÈÞ­OÏ*ÔâÑs3pÎu_Î÷CØô·ßWHÒðF§íwÐdÆtý5_~²ËZ£û‰T<-÷Õ6Ëâ®{¦tš×2\ìÿš ÏÙÄì¬(ýÜô 4WÊ m[Ø@ðøFïÖOvÉ,’ãºEÁﻃw NS&ÉÞœN¥éÐ×pw¸|gtWÛ¼}öº?k²ÿÍ Žîc™s¦±¥Ç—“ÞQ¶Tv „?™…=&°¡~ ˜¨p$øÿ19}|dν àÝ E¿ã'îÿYykžì»¹/?ûã—õДˆ.IºX¹SŽŒZô2>¶*ôÉÅiXe’K¿nHÀ,íxñm>®]©Kž«qÙSéên%’lþŽíc±yå<ñU †Ù#?æBq¡É(ãûâT;¶ê:9üÙ¼t†i)†ºí#›¯‡€õ“±ñš†&þƒ_55pÇ4ÞÜ{2/ñ—ÆqØBGŒúppþ <MHš“h\K}9Ï>U±ÙëåpLì`g=óßÌéo~¢{¾bõ½F\Aœø¼©ä\½Á–¯:‹—?ŠNà/: q[ÊŸGª+¯g¡Ì§ËxûÃj°—ÞÎüJA³`#HLÿ¹Á30iŸ Þ*JaG9Î`žY+çÀžÑ[œW#ÛP¡ÔÖûðcë;Q:ðŒ°Íƒ*¸ég8Ì܉üÓ?§n?\^‘ƒ9OIœyÞÿ_ÿ_Ŧðötê/(\yÈ"<½ *PðŽ`àÎ$ÄבŠÕ¼bbŒé…_f «<EAÓ¯‘UZx ™G0gáX>­­à¥îOÔÐÅfÉ%ex‘´0XnöW£×äl¨6d’‹0ö¡dÂÿ7Kg>˜>ƒ%‚›™«!š´'£e³â›§Oÿ›½Ìü9Ž´6ZO)‚·)%8ë„'ÆœÔ!…ñC¶5{1Bq ³J‚þµ'ù/ߣ‹ 9ÖZφìT¤÷?CÍžIäù¶.¨_ò§ÈÔYüšò‡[ÓöŽ«?-·÷ñR}KúZ‘—l ¡W—"$Þ/gpû LvOó±Ì”mwÑùŽ$ù«œŒÏMsQz×eü}NŽë±A :,îh–2ñ?Ê Ýd^/kÂ~Í2ÌúaE¾oadÍî£ù³oèT‹ŽÅz±?FÚlÐÿ"R»1þƒ=Óò3˜5¬YO§œLeþÎÀ©<”Š<ͬe5rºñÛ[šÖµìÎTà’*γðBE¥nÆ1t)”Õ߇± 3ÒÍ™‚¦_&É\aÒUÛƒi1Si¨u ‘:_§pQlíšV1Ž?@3VœN«Ä$¿tvÙÁ~¦(ºží«7‹cè¬å? ÝÆžv¬”ÄŒùŽôvM F“æSpdr5ûñÉcx]~.D¶Âúý\ä¿áAó±Èh?líŽÁúΤ/ÝŒnaeƒÁ_ì3×6<ÿõޝŰ™''òÿUt v„îï [êx7Š‹R£ó¿pÚ–(2,JF×g¢€¢*±Ñ2d°3àú'ȸ³{Ï‘‹ Ý«N4-è3—7Ø¿4ï¡1¢ü[löcºGdÉÖqûê#ºc.^lá úÞ$æP÷U¼~î#LÙˆý‡ñ•± nxdNü´wÓ:;M|*| +æ}…’]È8Üúa»fa“˜”^0 þ$î¼"[Á±¯á£1nÆìᵌÛå!üÃ4&^u*ÑM›$¨s²ýr8mL%q‘¹"- †(Ùð ýg[²ˆ æ‘…/Ö‘{oJÐߦ÷¢r©2ÉÙ¨ VãAÃa7S)J1µO“FjeA¯N5³ã?÷r€:ˆÒ‘ó•àWwÏ.Qd²Ý¢Y#kZmÍË •¾àLy©ÇN“-¾±Lüxg y{pæ˜og£ w°ãç€îÅÃÌ´jSPÎXŒ|mðóBb§H. oÀO9ÚÒ›àÌ]u"²ÿÃ3uŒíŽ$ç j¹Kdå1‰'Ei¢àÛKßûÐ\§‡¶I‚»é“IX,Õ3ÅÌhÊ"Ú¿‹zàÐ*Q¨º;<ÿ¾ík™0”cÒö À—öŸì·9 &u ŸíNdû„ŽcŒŠ'}/ÏeüŽIЮã tÓŠlæ÷ÝØ«FnŽ´â€• ÝC­TyYo¥‡øîLDXXsôõ¿‚·/ž:v‰·ß€ÎÖ ¶“ YÙÅ>h¶ÜŠ.òSEx?Iö| À°ÝwpôçöÒ© brÜ’ž˜âÃ(\åJ¼jg*æ÷1bØwOçƒ]E,ËÙñxÄÈãCÿ8ήÚd$`2 ‹P'slÜÉågñ¨ª& s¶%å×U™AéÀw¼Âm£ú\KêׯOåÿN<ÿŽ «‹—¡Øáóþ°¹xacðõIÑùwžÂhZ®@Žª¦WváƒÚ/ì4Û$ãÜ/ø; žÅ°}Swbr«[x±;ó:;g>–I‘bGÒ[gÁçúØ¡üwèÁˇ¢“˜ÿfGkp?âI1|ÖgM.y¨‘HÞ&˜¹Ð‡º í‚þ˃6=ÉÌW O4õhÛº"T‹qÇcC°‚«OÄs¯á}¯Îþ-§¬«ðƒ¬%56jCíæ;œ+³èçe\6„=S’µN“#=EËÈ1o~r~3‡ÙséÔÛýOÿå@7 Á²ypìõ¶q®+~Á±üïì˜ÒdØ|ïj`?ž³¥A5NË-ç ãÍt«ˆbúV)âQ5ª÷ê+â¤|\ëÇ®ß~ L¼Ð\ËfNéõpué)HoW&ãéåºtîb.3ö) eWEÀК&fC‚>H_^ù¯BIš<äOÙËJ¹ ‡¸oE ÁÑì)”^ùÉîùx¾& ‘Éûññ¨XèˆÀšjC(ž]̽¾º% îp³1÷´0ý†ÒUuJ3‘'Ö buyw?•R›q=á¶ÕÛÿà›ÅèùKáÆµ«ñ}‰(>¸p _˜ÊÒúí§qð^gÛìklèJm4É߃{&Ãã?Z$OÖšÃ{÷˰çëA†õàÃéaÃàÄs|ÿ0b'üññ¦"üX$Î Táe"4÷è&Κ±zhØM¬—×`]×o<>ƒ÷¶-Ç_>jî"Jø .ÀHU T9Ý´eƒUè¹mkqGH9f/:—û‘ÿz—WÝ'swО4EZ=@6.L!áÛè–hw«é=#%2–)Io½U&)‰31a(.ÜÜ…ku‡™’åãq”sJÌ'â±`kxE‡¹yá±mðÑ*ÔÍ K¦£ ·šÀt"]b§„g#VшèWPZ q£\Œ{q |óÌ ë˜-y¼9 z«±`J$ñéb½¯oÇ›]«iµ?ÓY)±Nºä—[«³ö*ìÖyÓ…øÉ•¥·˜ŒÿœixíI ¹ÑÍ>}vŸý;G—RÇúáš¶ÆEÑU™‘â”Ixëg@Ø×!ÎC{?òy¹*5X]ƒ÷\¦q-Þê1[žL¦ƒK÷‹KSñŸz&ÞíIä¼¼³&ð¥"½k™ÃGç§ÎÅ`U+ú{ã$ºM/f"ÿ eßbݼ¢ðxkwæŠB$zO˜g7m«/*Òn#)æµYgÃÔ F­v9~Õ¾‰»»™&G™MŸY‡5dìG*JÊ34{šƉðÕäp§Ö3®åÈ B½e´âþá ƒZ¤rL=ëó¼p ð#s÷ÈZôº/GñèB…²|ñ"ÓÌvà` /}°2ˆ]м‰­TjÄ·>µ m³—ke&Ê9áK›lÜñ¡Ž óõåμÈÎÍÐ!ï ÓeSwÂìövà/n{ßÍÒ™PŸQÿò´ñ÷×Û2{XuÉ ýk˜;3ÚlqÓ-Ò{3bšŸ7¼… ×S’Øz;Â'½LM¥7=¸›w‚ÿ›‘M:—Ã݈+wåÏ|æ.ãHç!0|° …WÀ¯˜2¢È 8·™¼P§þN¢±s®âàEMØ{ø «´ûa>ü}Çýí¢„’WC@­»ÿñÊÐê­ÇÈÊ ·Ø¯‹fcIðeÖ¡0u¶½6¥Uò·ái@›½3È¡°kà–=·sIcÐAð¼pPÌF´«:‘ÛBŸ1øâWêv½é#¥$è´,‘O¢j_îàuÿlÎ ŽpÓÿá,’b–‰3ì‹ÜBüpÿ&¹qé4g•á<öCHã±z¬+V$carXP׉‘wQvc9æ÷Žïå¡d(2d“ÕÂÜò3ÙÔá"¸ðý!„Ŭ…ÝÓfÖÊTœ¿|ÝÐ34„Ž{ÍI߸oŠþa6ØÃ>8W®½\®£öG^zÐ@ˆÝù Ê} •ѵ"¿òôi¥á|¢šæêtÿ¨*Ø…3OÒõPK…uS¸ÿ ïÔµ¡Í±v\⹄ZVÛãÑìN ò­AÍgRжT•ˆ eÃÏäÀµæV\Îæ GÉÿö?ØI×ãc3OfÞ.Ì6SÆÇK¥Am3âçó.ôv£ ½Ä»§-¸€^S h瑤îS´ñó‚OzظºBˆt \[2ƒÙ}l>zYœeïÎcöõ©ÿ±ÝÆ{eñüw•ä*ö¾rÔêØC«¸¬§…©W¶Ç}cÒd$ê¤JÁ‹ŸÑ8Ñ(>¯‘©øßœès6™Ì7µf fØÿz±¿6m±f óècóƒ¯À¤ßÇÑðûoXIjPmN%¸¹OcvžCÜøÇ‹+?*À\нÂ3õÐ~£2ÉÞŒ~I_À.aéDý[tyçéœ88ÓÒ † tŸfm–md·˜Df5A;”ã+£Ë4s×5\ðô=v«¾bç6ìÀX)zE~&Y|Ü’,æÍÃùï0ï—")S½Å¦=q¥xþ"û·l.MŽ,€pÇ]tP=êJ­…;¢GÍ ²c$ìˆûÏ s7~­R#·²£^Š‘×?yIÛßAœ×¨‰÷KÙör9ôH_Nö¼™„)矯!í3ó˜=Ë#Qzã+Ü^sc*á§ÉùF3çSdp‡îüêKxâÎ0;Ûl¨ýu1zHä;Êü eúÿñÐúë&òßSÿPF¹Òtf–¡^Ð}˜»a*µ5®FÞ7wØK?³½Ž²d³”Çx=Ÿ„_9Yxµë'Ì}¦Kr)»è¹6èÆPà?ÿž3óñ*öÁ1]rož8øl}JžËIM^ÖÈh€Î‰:8ºSJØs&Ôd¿ùm ËìÙôÔ6Ô_à ëRïbïirɃ¼o£åúO¸€Hu30ï#ãa4Çç Jgq‰¨;qÞv‚õÛPÀiâ1")zD¡)_D›‚Å RåÍ?…Ú–[ÞÅ㼫™ÿz»ßà#!ó/á¼S²Ë}ÆþK<áó³ñ\Iþ7yÛ)/¾ÂŸ5]7i\ŠÓJ§…ܬ!p”õ ßE»19Ian3×v/~Òö§u_ü˜d¥ã²j Húôâ¾q=ìÿ+ 7ÔvB´ZÉSÐ$Á’L±d49Ùöf󹑽—Þ€øo"p€‹÷>8â*yšL5Mˆ½)OËeWÜazS¾sþku0~8Œ›ÇT™W‚™%» ŸÉJL,mÇy:³éù‚júr<7¯¹ì…&g5©œ°É(." 'סLs NEq½oŽ¡ÃñÌ‚Ä+Ðä¦AaCåDügi¾=•'ì£ÔøèB«Âã©Xê2z*KˆÆ·=@“ý™Pü½»ñÚY3òLK‚þ×Ç¼ì’ {ÇÚŠ12eíû´ÈOÑa̽*D§0Ó =íói~˜>,ÂÅÒúLðÊ8Gu ‘^…-›C™-#I*'ñ^J(Ý2çÔ‰“€ÝÝXõ¬ck°k%-È‘\ ÚÑÁÊœÛϵùI®óN']†¦i²ï ,Ø÷Þ:ÑIËÆÐ™K0àãmŒº©GËß)2´r¼ñLf”xmpÚ…MÌÕ=qœÔ76äXŒ9Ÿ.ö.¼ð»ìÈ„ý[מƒYãZ¿í(CïBNbλtzÚý#XHð‘?Á3i}Ñzíï~’Ý| ‡Åé‰4ìŸ?$ö†Ä¾f¶0≻é~‘=Œö¬ÈØ ÏiÑ Y{¡?Lo—\ß„Æ7r¡ÔLjš)®¤êÒ¶Üaz×| y©îMwmɤ+v“¸ªŸLޤ‘,±bnõN§us+éÃßǰ¿+?\цŒ÷Í z¼èÓ  ­ é¾8zÈjK+—¨8²=©Û6ü¦ïÖ²øPF„Z羄á¢t¡K2åK #zoJ˜¡¥ÿ»ÿÇ«p†]"nÆðº?aCdBתhÒßð†‘ý,ü¹bðg…$úð}‚¦,V‘ü@¹ö ìäÈlõèÆ£ïßÕûß¹ñ-øl(‹ijQ¡Õ¦·°§Ïœu“…H¯‡ 7½ŸgÅg²Áâ6ZJÖ‚Ö”?˜Ý[Çùšˆ÷û¶á×qý/}|¬ñí6)b¿kù|4kvÂfæv]Äl­L†®§˜i©@ägj“ÿÞëÑ|rgN°cZô·õy6­\{¾Òv‰WŒÝÔFô½-/¾gA|/úømceÛ¿¡ÝØPE„Éf±Œ‰û¿ý†Œ`Tl}6ÄÎÖZ ü!…¬óv¹(c‡—ÆëÍ÷ì–\8'¥#sªØw79Y‹YsÀ„K"ôV ºÛ¦kð9”–ÅPÝû+II/E£[ÆŸ ‹Z…ÉϱŸ0ðç·åF&ä¼Ô#;¿ûƒò‘^åñZÇœÿÚ jߥhð\c 1ð$Rû—‘gžßEÚV_ Öpjk®—¦ÙÏÔIG-‡ÖH-'mqŸ™C¡G™5#*h5Ë,—ˆÑ7¬A _â;¹Ø(¢É-äW¤kûËõQqÒYüòr¾ÛžÉ‰ž°ÿ=9=NCU9·7^ŠÔtJÓÓK#¡ Øõ3¤hÒ91HšdJtƒ² l©uÿ+Ä}þåw¬`2,nÀ.÷6f} Uý J7À`îtÒ$BfÇbèÖÛpí›ûòO<ðÄ”ÁP¢55_¢6[³Ù•í˜-•DœR û—Óµq³I¿ 1’ìcò|'alT)ÌÝÌå*ìpôÃý£ Ñì—U‹s¦mÚnpDA J ~‚`Ð ptK™ˆQyzë¶õH“ƒÊôO›0‘æùÇrOó£Ý7#”:4S{9›«Û¹Ìï¡ÊêGúƒôìkN‚ð¯Gépl:‰µå%FUèž|3»Ñú]øý{ 1JêÄ!;‰õcY}™noI„¤í? KöÍbH…ô!"rƒ{üçvrÖqUN³™CU^¬„v¾>χÆFäÛº4ã/]Ö–‹fÜÇÉç¢[˜“8º‰q‹~JNXþ%¯WúÿeÚvìO“¸Ú ˜íB§Tc؆YD0ä ¯K Žd+yRD$-c¨ùÅ b¡ý Êÿ~d„ÅùpèB~ÏØ;®' Éb8’<§›T^ѧ]‚Dô¶!Ê~òà¿lEòªÀ–Y¶È ‡”4Hwû/n˜“<½ê°îûh†[ä^s²âO°K¦Ó}¿UXo¥ ìg{=v¬-Šì0P#Í—RÕû‰Ü™õ9Œm£"¥>çI¹­ tKÚÓÃòá$îÄlÔJ8ÀNVbV$]`9Ó¯à·I ôââ.vç/÷yæTªW AŽÄ¯$Fï´©®©0>_óöM释ÆÔ..¾÷í§?. 7ÝíPÃøÜË}v+>Ñ ·Frð}ÿWö L!Û 7-5í‚ÝAÔóýe&J5•þ×/Í#þ×Símr’êö¸sš'ìsåwîÀ™ÃX0VAòz“AYl*3ØÆ…ë³ÓG«o2â–÷`˜(Ñ™#ѸèA NkZK„R·à›é·ÑöÅi`™xÙÊn]$CjþwÄ5 ãb«aøé*J~çŠ×ƒ³©²X&'©j9¢þˆ9h~2\èÁÛ©è“í(œä©)ý&JMÌô%àý¯÷|¦IÝ0£9ŸJÆecQ~+ŒYͤ¦ncRøœË¦_C)‘[LëÙ»¸óåyxsh?dûÛö˨¾qþ|v̘Ú(ºÒÛïä¨ï!'ê'¡Dåc$Æÿ†ùÿò¦“ð·è6{·ä ø—Äëðß ¾Ä—¥•Èö&Ahn”ĉÒÏopÀº|çÍÌ á¥›£Èõ¥gƒ pÐ{••éÙ×C0ÐÒ™î¤>t2.k_Hcž'ž9]ļv4÷i‰ó …ë·• kõ Žî›‡h7æ”Ïd²¼mˆjŠ=ðÌ”'Ñí2ùÜl¯6y¯_Eþ$KÒ{xÉ:«3X´.ÿ5,FŸÃcÌ×Òw_Š‹ÇØ5×P,_†&ˆãÂk³psL#©R?MEÊuþwÿÃý/ªk¯ƒÛ¹,Ü–$Þ“ó Ëã ýú2²z°%<ƒ¾COÇò’æÅ…¨r„-\UÍ“0~!AbÄpûÜЭe:Õ.CjO"œ±-#¢+eíÞï"95ÖäÊ,–xXŸçþ©;‚—xèþçk`ÑÑ¥tìá1â}9ãçgp½tˆw”5]|Bþu¡÷T@VËxlÄ‘Ä65ô!ƒ³IÏui²~h Û}ø4•ËÝM3¥eè»õuäa£ú"<2üôzoI±yÎöÿÔ£}òÅÄE=Î ‡›ÑEäÕœV=qo‚ÿô_‡ÊÍâ(ýj' ›ˆk6L£fspßí#°IúìÝYŽyéPrã>ÿ AÈ ^r&è13¯X.¹¿3b3H˜h¼nAÂ3š™éwΕk3`§öº÷äIöÜâí5çåq¡ÕɆ;û„Émmñý$ö^‡)îåÑlE^¾>M:/$ŸNðC¹užw¤å¥O¯Þ½¦|tKãxþ’t ³SÈhýTÒtŽ­s†¼¸úø›:7—/ù9}/.¨3¡æ3ÅaÔÏ”ö‡n‡3OøY«ÉÜëŸOcûjÙuÀ’Èc—ˆøå<²&’—_á'!öªDåÐ.ò>ÿ>Ό֦á;¢À·æ,þ‰ Ä·röÿ3¶„qMÈgwy|fÒΖ‚Ô“c ÏV‡Ãöoµ ²¸Å¿4pE$áSÛ( VV2ÁÓ äÚêûÝ5z®²ûD£Á_:;?ºQÿ—ŸCžfˆä·¢aW£à’ña6Ëp>”×*cÈhÞ Uø\æ B±«Ú Í^ÅESò¹­1têPsã­Äl*̆FsÈ\^ŠÜ¡©P‰qù0b~H‘+úÊÈ(kâ¿W`ø2K¯Êõ¹Ï„ü€N;†xýdÃøsÙØG³HèL5æ`ðôd©É·… À£âŠ|ýÛlþÿ.÷S0sîÇq]ë#½¨oÒ*½)£Ù#ŒuX f<Å%ßßÀ£S“èÐ`îë"$àëf Û·Œ%g2ïu‚zA¸ô¹ßµ'>qïÀئ—©ç‹zo)j Ù‘&+ì ~ ÌÁ‡¸éŒ;3KcŒ£%ªD5jùiè¨ÆØÚÙDqC?ñPW£ZpáÑ!&û¨Ë¦4Æ?߆ž>Úë/mÅF½—(@õ‰Žw3¸ ¿ÃŒ5òdXRwto 6›O±»ÜUØmÒø"c9wÝ£tªRÈCÙ^ÍÆ¿† ÙgŠÄlú\”ù’:ñü§<ć{4>†ÑI…bu²7£d/q˜ìËþlKL3ìü ½Öu5C¢ÒnE _‡ÊRSòmRnRof9–¢v¬féE¿îÈÛ™DFã©gé<ÖÇñ‰ž»’ å9Ðdu.¼øXÉvÔ¬Áõ'Fq…Ërêø¾—GÞÁ\]úÕð GîÝdÒÑVNÇØË`ŸTAJËÔé“QÎòUç¨ê»³DOv Ý}êúeV“wKz)èÿÆKÛXXÓû‚}±w%,ÿþ ¢-YòÝцF®B@”-mþº^Ÿ Fƾ„ym[M"»<‰øƒ•ù¿l† l ‡%ûÄé“rD.»Ÿ³z} 次k{Š£-Ò»…¶ |cTÎX“Gp\v'‚âO8<)Khà8ö&g©ÛÁࣆd£åbâ_.{†¦C™Àb£ñ’´Ï“õ!~¸kí{˜µ«‹3¶ËBï䣻˜ý¹9œ(^ ‚¹´Þ/®£5ké{¥Ûèºa ªìaî6ͤz±™äóF,›Q¾¶¯Ð{ùHkšÁ¾•'Ïå‰áŒ£xîÉtÒ rÃ÷ù2w7Ú¡Æ,ròê;ÆãÂQ"éjDVOËCËe‚t-O#»mTv¢þ!æÌ»»GØË¿NàŒ¤Û°ošé·ÝF‚ÑÁårÝh¹ô'Xw|8>g˜‹uï˜ë ÅLØ™t¼Æ'ÁIör½L(nîÂE{O G‘ ÉÝ[3®sTè&ö/ ÜTa2…ñÛØhBðUŽ4£+èׯ.³çö᪗' µo*z~›Ik–PÎ]¯¾¼ý#Ä¢üç¦t(Ÿ)4_;Ф°mI&·cÕö›P·Ô|Œ|§EjÌ=ˆèËV¬tþâŒFÚFÂ/‰Ó•è:ñT0ã, ŒÃ+‰œ˜ÃM89Mp‚ÿ_ïeŸ–.æ u «ùƒ;ùx¹ëêM84ŸX’ó}gØ‹©‹é&¯%ðd ¤WeÁg*Gs†õI+¿þÌð„i¢ô©°‘K•¥fªÑ•\3Æsêmng}¤/îÜø8:+|ÐeeØï4üžÊeo)>_.nk;@®×*RáùŠdƒŒ,ÙÚRÄšél‚)ß Ø£M9ŒñSÖU†¦mÕ¢W_/B7ùϰèü.bÓá ÖÉ÷û{`ïÍ]ìÝÞ³xÝQ™Ž“é¼ir(Ö'Gæ/ óüÒð„â |=µ äö?†7ÊȽú–ñ)Ù‡“Î2Ï¿~AÁ=—Øyö“i§:)Pþ?âxÜ¿–‰™Ò€k'­@µ¥ûÀ£¼Θeƒ£˜)]$ù’U6ýßü“°Mø^ý$th*ÑúÁQ¨_ÇK[žc܆g%#êë”ÊômRCï`'Ù²~8}FÄgÑ—;ýñ&_0=kðœ½4,FVþ†ŸË@亷C¿‘]t<„,ô&[ a»ÉrÀ}‰LÝGO]ö‡¨«!·t6ùf8 }a‹hQäa&¨4ª-™S‚ÔÕ<lb¢‰ªWç–j!ÚkhÐþ9qÌôáIT÷§*± Áˆ§¼è”u…§ºÓÑq°”Ó žo¥qb¡%àI04¥ëey@.²&¿áÅ+ú øÇ?ËBæNàÏ’£6ÁLK¸•7bò ?©(u£eçé½Ö&&;L–xöÁ¶X¿â>.ëm€W¬h·¸/„ÝëCýmƒÜ°@Q¢¬¦-Y ìžÊt–€-ÙÂ'ó „S©Ô-H0ËÄ?+²™÷ Û<î7 ’dp ÏgrA,Z•¯.Àó/Âý_¿m¹XzÍö,Þ4 ýª[át™™µ vÕÚ4¨%Üfz²lñ‡³ ø=„×#”‘'2›kã÷ÛR0ÕQ…t(’ëé7@G­ÎŽF±ox<ŸqQ×£r&øÓÃbÙ›嘃ƶÌÎGiV“ö`^J)®T)å<¶æÖ—(͇]ºzG`‡1l<°…Yqúçì+;zóûu<$•˜ yû§c]»8QÝ\ 6?f“_æoØ:²em”æ»ÍÓ|±ì~? Ee=÷Т”ýæ¡§ž ©z˜ˆD ”Ãsûµ‰èõ¨ñ* Ưƒ¤Œ=-Hfìýkkk®H:ŸˆÐÓûÒ8–BGàRÍNÐê"H˜&mŠGÄ .Ã@T=[÷©ßM‘¦%/úØá×âtP:úË:Qä­2-÷ý8‘ÿ¦í™„)'¯£ÒÁå¤$áT™ÊÒEI¡œå‹´¸Nø/WJÑ›k%È ¿î©÷ÕtÒ„îkÏd”¿ZÓ†Ïúl.'‚®µ€ã¡.ôŠÆ ¬$¹çßɶˆƒð‘Ìʦ†[p \WÌÀ±<äõNÆ™o˜ÕùÎ2'ýâØ;ñòŒp–{Ý…ÈÝE.(|>FÄTC•‹½ëɸ9ÅryG:p’/UåŽ1™Ÿþ‘ë³X‘{µÌõÐs݈q¨;…! ÄèÀ}†1Ÿ:ªþÅ›ëß1÷ "ð¢Cœnöƒ Ò÷Pµc:=?¤þ¿ýO7yðfú<Ù™Êyñp>SÿúFxFa®y-ªßeÈåK÷@Pð%÷€аÁt¡\xÍùþàY‡ìö]Ýðo~6«õKŒx•ÔbÎÕ¯˜ÒÉKúg[`ÇG/Ì{ωµüw$üÙÑǧ8b‡³§üdáÄ<1rü¨Z”O#"Ñ“¨E‚ <½9M»UQkúgôÙz ÓÿAKÛ² ý—kf„NrÊÄÂài\}žýuè>¸ÄèpÊy@òbú¬zuªW$ÓÍñ¼x ÉÿìÂ|ŸìÓ¿3Fþ`¼‚ ñã¡m¦ ÞìÖ¸Òó¦tµÿ÷Ã:_Y ¹ëž¢öàaÜT;€B®çpdí1ð¨·`6ðÓm“ð€+‹‹ºq'³™F¬XÅ."hÓò÷Ê’®Ò¤+î ¯¼ˆöZ»Ã2WW8!ëMøB.ãÅŠmÄaͬ|¨@}V ²!Šopé#PwQ&1Ñq_-GbðsÀN‡Üâ¾ÇìÕWÀºP‚ô9 “L‘Ú ÿß±6{Ï|ƒîì$¸ñ©®ûtªÿe^™ecZÙTøžvæÇ4꾃¬éz”c C¶z~‚=Ã!ÈöUþa„«+¯Â‡à­–™ö:’>j]Oj§¹R4Ûiç§1rÙ+é÷aýôz†~;ÅðÞh‚S>Âô¡i7 ÈdÁ±¡vÈ«äx-߃CcE?½ <Ê@Ó"±IKSØ]=·0D°_‡ƒr Ž,SF»^è3ã ʸ/ää}xÂÖÍ…¹Wob·r ´ÞÕÖ0åógSçA86³’Y;«O¤MÜÿ¨|ô- :ÁŸZðåë"¼Zõ_œ¿Ì”èáÑf:6"Hs¹gñØv)vpkò~\·Z„±ß¥ÿAq#sàßSŽÇ¿xûÜ^¨’ZL×ÿ ÄÖõÈDê’ê¨:ð¯1#ÙML~~2H¥A©¶N¿ÎÎsÓĵï~2>J?±å–á®êgäÁèê4=p˜mšÉʾd——ijlä6zO%%­ãH¢£3^¾º =“Â9nùœ+–uëK…¿çy±œx0ï7¡5‡§±‘hô{–ËèïW„‘ºÐÜ$Œg[…0¿¶hBÿÙu²¹·>@áAj°÷¸öp@ýk°`¼ØP¡Õøzá¸}©SˆÒR“PÕûŽ¿Ïùù¸9U¹P|‡Ë•¢O7¬ÁL'û(æ+çpÜÆ·® î8=„Ës–@È>ô]¯Dþ5½@%ÿå¤ÀTƒ&ü]ˆzçxÉùù°¾ã x4†³æ «±ï?9ôô3ó.­MºöáËaË^u0Ît§J+„Áeb‡Ñ¨5êî‹*ôðçù_ l¥.3ð}>z”Â=3ããxÝnXßgEš>3'T ›’/„}ðPq8;gê4òõ«8ezZº¦,ER¾‡±sâ|_‰ÏN]àØ=?ŒjäéwqeVîkÎDü_”Ôa?j•svílE7u hþQ…éêxbЖînOwúîò&ðæ“¢%gÌÑmÎQlÐ#Zƒæô”í98öµˆõ„\¬; \C­³ÑŒå`€:øhh'0ä;~?ÔQ|þ0½_†Éš£ˆ«žÐ5ʉhç¦B’ÔfÑ Í~η…"ô°R3gŒÛ«Ê’Cì^~É»hÄ‹8ùP+;m]0Óë]†_.É2™ðÕد @v¿!~ÜfÌXl½ ì*cÏå?ÀßSÅÑÆŸ‚Hœ:ûõ==äÇ< :‚-E¼üo`ÿ,¡aÏÉ>ÈÁÌW™”ÝxªíéëÙ9rËÈçuß #È’ÆëÀìø…¬Å¤°åX,Y¾u]=dŠªš+@ý/eœ È~ã-pþ'»Þy7s~ÿr”óÓ€Üö  êž »žÅÃE¹óØrü3“v>ôLÿ¥Tžš‚ŽýÉô%³þ-ã~}qÛä ôÛÃÈ6Ñ_{ü˜˜5w Uå1z=äUªÜòçLG&í*^Ø?|´ÑbÛNçÛŒëzïpOG “lç"œâ'¨×7ýoþe|4j/få÷Ú•¹À¾|;ùt ]¾žl戰u/™í%ÌÉ€&”ˆ;?^\ƒŽÞëøQ:Ÿ]rô ݊ƽ üðá'Lóp-þS¹Ëè< $¿³C×…c~ç2¢kL©Ü,˜çUÓÖšCÂã­ø"C…Hû± ÓZ¸V9(SÓÞƒw1”P‰‡ÍÓ»—ôe7€º–q÷ÅâÁL޵Ü#¶5äÓ–2ù÷½df˜œ´ ä§TÒü>]…—séÖ—µãb玴»=~CØÁ&‚iðHw. wn<÷‘I¤=u%~Q.gÉ_š·*ÞGï€áò©4Æ+3 7“Š2O·£|bkÉ8çQ‰/ÅLu8Âåö "¬HÇcßÂþµ¤‰…¯ÍjômK S7}„=”'Bš¥ Ú¬LF:àiµ?ye-³§“ò#Áà{#ºØE1ëÞ‹‘bÞØ8¿ÑjÓ«Y›Ž z+ؼûü4­ðž:×€Õ ñrÑt¼¤Â³ìIæ—|Üï«Â]¶½‰±+ &b®ÁÜ9Á‰x¸èTDZc"S‰MõVT=)üþ"ÇvQl1¨Ž®$Ù¡‡Þê¤7J3ßÃ0wÒ"ÁjübɰS§aD³l4&ì?C]:Žšá¼½ôiãôú´GWaï³ô7=„S7¢;kX.^{”ÌÔ‡z@m÷YXnÕË5t¡Ó‡ZL{òcø­m=u¨¾@s=â/duÎ"Q‡Ò0¯¶L­Ïåzô“¸0-Y°Œ¨h‡©§74—*†g±0Ë}5j©Í w®ÐÉ ßÀÑ„5ô‚*™]’xA­Â,m4<‹áᔋ˜mPˆ…Kà½ë>öqF+<¯ù2: èÍõoÙÛ¿^³ U‹©Fµ1Ô(ÅÛŸøA,‰Üý%èáßÈ­fîýïû_†’óÙ¬£ºt¿í§9ÏvX=Ų¤©›ÐùÕĦþ%ì•;N®ûDBÂEØ}p>Q_JTÕ<ážÒEÒe:nÿó°]ÙÀKS‰Ç|+8³u!œvà¥Ö#PúÎXòP%‡pX›ú‡Ñ¹æˆÝ®Bôîª ¢c:€oÒ¥°î ƒ1czô_áå·ee-WqâôÃ-ïɰöÆ#6çŠ5ë|–þO?°çç~¢‡D¤áµ²=yø²žôÚ‘,5Bvµ®_ÓÚX‰eÇ`±|6Š»|L¯æ2®üɰ\=v‰&¢íüÿíÿm?ÿˆýóR#’¸ÐY›ûWƒÈc_\¶Uš*=ÝÈ)¼Œ_vxOº}ì‚%1àôÏD'q°qƒ+” G’Â]RäàíyäV§4¶eÁ£gmؽ)‘ÈH\c§'†´È0|¥³ —<`µ•huÐ fO_ŠÙŠBÄÙi8~}nîµxb“Ž¡’›Aÿë+ÆGF–*Þ^ùíî̇{ÍL^N/<ä¥õ›¹”?Uœ‡';”^õLMöIz„L>N²ÄV92¶5×¶¸osøîöû~Œ¡GaÙoµÁ¶P>²/'îlö™ðÿ Q h¦‚Y.±r.çÒ¶½¼$Ãô,š®+ÃøvûRŽÑ³„ÉÅ#Æì³×ï8GŠwÑ’ z.ë^ºð˜õúòÓÖíÓT¾‘F?öž`LÅé† :ìø¹ÄÿÖW8ûûný)ŽgÒžƒ¯m"®p"¿‡· SóÆkƲÔ;èù‘‡U™Çð×Ö²ÓfR\Ò»£ò6Ûnw`vñ²þ·iŸu ‡n©«øhº^¶€ywc PNžÉ*Ïb‚®2O·^eWn‹?ÒÁÌ/Hc †Ö¡ê3Öy[9> ×&××â¶©»'ðk¿äÃΕdgB>kCÀÇL6¤¢‡y3,Êèk_Æ~/iLžDf]›âšæÔÒõ"«sgÍzú%éæãÄ‚hdZ;w4-”åð­ —lÄ“aæT1²Ý¡ C .ötð¾, ¢kÍ– ;3Û×Eƒª³aþd9ô¨Ocƒ2‡îÚ¿M…Œ®X9ùQò™ÍY%D¶m5eÿ‰’'ÞÕ°î\ð2Ñ0y*µ÷›‰ÑIxÉç÷í²HF88 &EmÂ:Âñè2 ‹eÉóaúyÅsìþf ¡û:¡x{:U·ÙL.Ùÿoþ@’4°ºlÔ[3²ê³-ø.ËÆývóµ{Žðz,=ˆF‚{˜Õý§ ’_ØO¶U¬K‹ó¸Jã'ãõÔÆNm`® ¥G\éékÈù@sfžs.]y§ =(oµ8ÍõÞJw #±›j¸ëN5àãYˆáŒÄn0Ì8ÁÍÚÔ¶«T° °õ’œ’;æÏÖÓÝY09ù¶‰fCdX,=º€š´™’ˆ"vÒImª²לcÞ0N¿1êÊql_=¦ßþ‹² «É_ƒÛXÕküÍ\ÐÉ·e&ìŸW^;ÆaK·<ÓP$–—eXá¾ß°!ŽmÙøÔÝÄìºHtÏ|ç¶ûŒp´¬T™©ƒÍ\ˆýËÈ´)_ØšIú÷ˆö“1ã‰%¥›K9[H5GN‘e£Neê_t³kå䘆Žx»q®Õt±šƒ24G¬–ò¡SÊ?\žäÄä¾*cŒ;•EÂäû‰´ŠÈæV ¯g‹{X÷ø2tvev.(d? a̬ÿÍ3 ÷Å6/=8B¦¯JÅÓ‹P»—ý¶âVxAù=ËoÁâr_Ö=ê:ÞØLøÉêú(4?AJϽšó¬¸(‹íö»ð¾n@£wH+cq1Â>C9± .ÙW!Ð?·ÜÆÙ™Î0úÇ“|4XJ5Ö>e øŸÏ¡›ØP2ÔÊ8ØÈZún‚†™™MF&$ '‘–ú”ýøNmÏÄ9ëýX¡Ûk8&Ãÿà¼É=”³ø²\°Wß j’°uŠðæoƤï˜à}}Œ ñz=à£ÇkN'kd 2,葞ÿ}ÿsϿ̅¯y̶îaPðs Üã• <È®yš†9ÎZ¸Qp½½ü0@O,œÎid’ZÂè‚`&Øê3xÝ®…üù“éþJŸÌøñÔ2Ö{ A6)Ü’XZÖÃÈ«šSý-XµJ ~¿]ƒã|=\‹®:f Ë CÙpÇkÜÉá“p·˜íðÞ [¤Ÿƒ¼Ióz̆6 ÜG£'éöÛ >êO üòÀf«(¦‰½“ä)ïî°þt¬õ1'øÛIr-Ûݶ˜DÂüI—Ðêw+\ éÍ3ÿûœû%•ý­>BîÆé$¹^ž.»2‰ Íèâa0_;ŸÉ“ÆÅ=kH[ý0uÖåLØÿÙŒR +A¶66P¥sd¸¡ùÏ£–´±£îõeÑUg³hÝ’]£E¬ªf]žzCGÈrÒ¶µGÇs—® 8;ù,Ø‘G³RèHž,IÖ|2_ùí0+žI4>Ž/B̉¡//Õ»pÝ/%kä"à=KëݸÐT¸õ >» îè+QõïAôñ„åTj—÷€Ýj<‡V¼ƒ÷"ÜÎCÔéõ‹j¨Á壯K +'ºì$…õNŸqî—+ÚO¬ÇÖÛ2C6áÅ1 ²{·"åïF¦\s- X=ÁÅ‘®ìƒiQ˜ý« ³Òyl–¯e®¯ªãl÷N!=•¤ô7?Ñ[‹·‚•èêqôrl5«ó@E¥IÕ£b’mØŒÇu—‹ xlLoÎQ™?©Ìûð;’Ô«£±øÓ9nBK'ö*NÇù¦tÃQ2lVD‡ËðBê{0yMÝ—«ÓöÆ4gvwƒV4†,%ÞCÜ"¯åø±ß€ðrbUy,é)ÓxäW¢Í›.1»¶·AøØü`º–.üó—‰Pw·ZÈðïâªÜn€VAG:µÄ +!ËL&]‰ÀÑw ·óÇÄó¿¡·ËÀú­!œDq$C]^€™QÞI[Æ(6+×3i=nDÊÁ„(Èãt¢ŽQ!^4¤¸•©Ý5“"pŠ 6¢KøÈÈ Â,s9†Ik—᡼ÆJS^2³÷(Ýá_4í›:Ô|uél»PÛ k&áìI5 ¥ý7ïጽä!rí Œù<1ëѤD³D¥~à÷¼öáz‹°øßš)SÖÓ?çÇØo¢ÑÀsp%¦”O«­óð©µ(¥ïpÛ˰q÷?<îd 1õìp‹3¾µþ¹G×PϘ}p̃3ÿayàJ’:++‘‚ÆS{™Î…FôrE×nVmª*…uŸ$˜¦ÇA“šSyþP-8ü/üBŬªž'„O#r3ÈôœRòLM‡6|: ¡È}Ùôq­MWÂé“ qúöG8Ñ43¦ÕŠYôͤ-Äÿ%†’"ÿöo¥f3xY‡cTÍt7š¿ñ#·^|dÞÈ+’÷z¼TsS7z~ÚJÆ¢•H_õ-øo®g:¾½*àuȌӜ&E}¯$Óÿú¦¹ ÄåŒ7]î5 g£áœiÖ©BÏz ;ŸÞoø«Ég(|&¶ÿëÿ sÑü›Ó¹nõó¥)nDîü.ÂY$ŽŒ€l¾¹tœ0a•š—RÌlrîu9ÛašŽ}ßÚ1s³1•Û>7Š—»Ó»èàÖ5°Ua‚dº0ÃË f„FO6…¼0Ø„OÞP…•ôé¬%Ô=Q»ÆaOu »qŒ\ÿ=•¼Ó“‡q]t.éÞd@ä Vb5ºÿ³ 3rƒ ×ˬûkiòMdÝrćè’¡+æx€UÜr>!W+ áò_‡©Í%šŸ%`'ÍïH‚笧ÓÑ¿mѧÉå‰âeP|ú¿þöwú¬Ùgd1•Þ7Á{þSÆ+ì£a¯/åâá­Ïlüú}¸Ôé$Ò¾„J+‡á©ÝEVbÝ:˜ß Nš?ë0A¦äƒ(1]-@©o8‰º*Kó–{1"q¯áݪ_è{> g²Ññ©xùJ©(Ñ^ó™ßOrbÕ»8FóqöÇìæÔ…dJ¶,5}“-"`M×}üðÒ«»Ñyê¥øqm ^xß<:¤j\×™Ìc¡ï‹5³Yÿ.\ËbZ?ÙÐsÔ ÚF˜¾>‰)%{˜*G’2³‹-Õ %-¤bÙ[”¿èÇà?b MAà}ÿ6¾yŽF.SöEº×u‰èjBfëmpä»Ã¬»ôVs^c±&‘Ö»HdV=öÂkÞ6$ïíBªÃy×X”ô–en^‚ª3ÈTC£+¶ÔtêZlA]²ò ¾ìà®xœŽ¼û1Ç!™¾ák‚5UMȉð!0ª§ÿ7¥¨úz™Ô5ö<,a~ÖäcdÍ6 V‚ü 6#/º÷І®Óäuz$Ô>— +Žàßg4p±"·ea4ÿØ òšGqŸ×"nø|WÒ;OM¨Rìuôª`¸G˜i^^üç7^€»„;1ÌnY4Þú䞘2òê…»'žÎ—ˆeۻѮq+[W§OöTZ1~~ŽLUÿ}ÖŸŠÓöuóVhîºÇFÕä•[8»Ï¬Äž¹nX²T'ë™a…öt 2Æ›æ¼ÿ¨/§êûÞÏ”yž2&S !C2ܽ¶"2„¨H£4)ižËFãP&U²ÓðØ´§9nE6w¼{CœÏ}Ä7f¢L_Ï”Þrn€÷ÛØøûë™àA¨:raíö Î~JÚßíă§$Ç]çK_ÆN„דeé–k»é”ï™ÜÚÕ0¨5Mhƒ(k;¸1%® ïöd¡ßnºÎ†½O{Q3c1‹©B.–â§á<Ôœ¢Äš=@óV;~ØôZ;£P——šÇ#¸y³mYÙ”2°ø‘¯ýWcÒŠÓêIy] U”Õ þ뇾$`Æ–(%ÐïWÜàÿìÿ}; …¾êrÆ*ý8sñ(Ú5ÅœéÑ&1·#?R¾Ñó­Ô£:•¾¾=›~_t—ôûæ3ŸáÌ3¨À}Æ)cæ³¹£˜ô•H”1#lsÀNÍ8™‰ó¥ñ£€üˆûNlj7ayä-tØT¥+¼1ëžsާwWÙÐÑ6Qt扵ìkt;7ÿ— |ÌJc6¶Gø>þFléGø´ã±”dSßÖ±èõWðjÕ=îþ’&45†ý“gwõÀÞ[83z­¼t3õ»÷¶œÇ%…TÏî ›÷ê>gÕî…›ëìA¿†Œa¯­ŒØ6ÿ,íÄ×)>ìõµ@jâ«{j ³í)z–»Óè¶sx¦Õ’YÊ·à¥7r”ÊUÐåÚ<øªUÇn°E -Äi^3P9+ òa4Û~‘&®pŸ<ýX4Ã?¿ÄiöáSº ¦]ʠКȬ…9ûàÉ VÕ‡«ë» »â'N™¤Žõêû1óL.ß¿µ‹ÞöŸš› l·{ôM–͡ʽrÛAMgõaÖ݉tåÏnÔR±Çb÷ÉÏÞǰõo»~ Íé°Ì–ÞŸÓéܵ¼á¼_wà WR»Ù¼¼ !t¼¶€Î2:‹œ:Þ°²¥ßëÇÀ¼+Güßîärl·Å‚Ô.úiDîõãªêù 6Å/.Àš·žP@'¢Ë²[p¯I…9Ä@}Ñ™¸àÂNÎikhšN¢AO8YùLç9ëR Ú¿€ŸGºÉ•lbÿf;Üò9‹ªiäêl|ø#•«þÔŠƒjtg‘,}tj2-~;€“?tãm½ûç"ÂÅ\ÿËsà+£L™ô^)úZµÞ}‹éwbÁÅ)ÿ,@KFo  O$¨ùl}(Ù¼€žsÿ‹Òc™Ë‚ÌÐ,úó5ò´ûäãÇ(×¹&t’þ(“ÑgâàíZµÿ}ÿiôÓßÃXA_¾’“Y5sûø]×l¼ÂóÍ-㎪nâ|N×Á5Ù,ÅL [N¢…Ôâ[ ã¾ýàšBŠaCìfhúö‰Lxê ÏìÝN¶Áos%¢u@• ä*²‰I?qj„\]hë²83½KpC<‹SQ4ÛäIR4ÒÇ¡Ú'Ôû[-vާé]Q,bZ*L(O¡×VÚƒ—óp?sÞۧ휅d6 Ÿîƒs¢ÿbS“,]:E†,eYõG>.s;C§ÙŒ¢Ÿ©¼” xóy\{à)Z5>‡×b8’ÿªË ðˆéT~í‹%¼u4ÈÃÛflÝîÓ°óœß ÀŽV̧òA´Ôd+Þt¥+Î`Kë°vàI jWƒ¿°Q  ¿ë¸±Ñ3§°ØŠøƒ‚ŒSü»&jX`¾Êt.¬ÈŸv><ÀÍ]8™¹,Ë~ž±¡+gº°U—là”Aª/Î[¬uމ=ÆWC–ôVÝNnéüšƒòK®Es½uÜ!} ¸µ2‘»öd.zÜE:Ž=ƒcÄ ͸ÃU™,Æ'&¹Ä3P–2-ÈÙÌMzÒGå{QYÕ€žüV†73Kp=Ü'cÿ§ÿÿëSnØ™Šž’àPä0ç».#OZkqîøSx"4·Éq¼ÑW nq¦ #Gáîˬün+n4W'·‰Ã@?_ã8YTÖÕŸ.Þùü5§ÑcóΟ ¢Å<'–wþ7©ry‡Ú¦WI³N&oZ‚:wq¥=«^1Õ#¼xפ q «'Ëù‘Ÿ þJÖxj+Ïp]>ÎYÊyëæãéòG0SïˆöµÓ2,8ÈŸkŸoBUCĘܞù`vÝ}´‹4—bæ/{1þóø¢G‡Ãm›UX­f÷åè3\¿ÿ ·éÑîü}¥Q·lL›Îç®.½‡ž7x,çƒÓ“…‹™— î‹þÉ-€/IG!®ËëWʱE–Ю>€§á(¸<õäºÎ[@Ì+ :êN_ßöàø\nÚ£É ¾ŽG ÅéÔ¤LfÙjá(¡xxg:v8Ž%lÊYýè:\GŸÜ%g.s­™î ¢ÒÎ?‡\} ×êl„£W…©È6>ÊA8{ûa4ó7ÓfSÏŸ†Ñ2èùÃ×É+ªlv„ÖóŽÇNf aW Td,ÆG+Ðæ W¢1 >káÊ/¢Á¾ËØTëÇ(¨4’ÿå sóœH°´yKDKÛøŠ®a0ɨ fnÝ^²¸)¤„¼Áz«d zXÓÁIÑœÜ2xÛøJD‘¾³¹yõ³Ñéž'Û^PÏ{MÔ’ª¿ŒöÎü'áuäbQ¥+];`å_)ªÖýâo¾»ß¾…¿f€„³;adJ+гZš vg†PïÕ#\/K•c£¨TÅA¼'þù¹?QmûYì/A±²$|1èÆ|vªÆª¢“w²ÙòÌ´˜urw¼ñrb&\×c.*q78煢쉊ü]® L¤Û?Á?[»~ñ:eÃ`W—.µXÀ·oŠBÙiûѬ³|~»¢½àD¨]v’ߦÁò>!Ò\ªq†®6pa­Cuìhzj`KU%qm“`±«m› ìö7nšÂN:‘±ÇFˆRv»€ÔÊzéP(3y¡†×ñ7t#/§%3vÜ`[½`½œ–ÉÒ-Û–b”Ø6;mO%&è‚ÒBa0‹ õN§¸mkêj|¬Ó1&¨Nˆ­e[®Ôè½5§N«grêªI”<š…cþ@ŸXœ‹uYi¼ðÈû—…Î#jk^{É‚4³W&gæ²FÈð.› ÓX¸vxûÀ¯ÓD7£>×µC{Ѱ·±Ux³ûq²k„ðÛÃ"l¾lÄÚ®IPÎXv˜ðnÁŒµ+ÉäVehž*KÍêÀíúlÚð Ùesà³’î&À·lUÎhÝ6Pð˜C§1ðßXöí±uG´6œQù€¾Ïž¢lôFœé°ÒÎ`?›¯æÀ½¾ÛÜØi¢l\ò*Ø•¡DežÐ—dzIU¸Ú)Œ·¦Â·‡úlží9,^‹Î†8ß!ÇÿÍnÕÂ;žQñû?Ìô¦£[C »"`‚î'¦ðZxð+Æ ¬·O¢{6ÚBé,J¬#/Cñ’c|ë%°&AÏÓ+°úìSÀè…&œÍ&o0“tÃåÞ\âá8ýä<ù¬ü—wÛÁêýdïr¶@² f^ÈÇþK‰Ú©}˜{E²4ùSŒŒYðsÞ}ÕƒâÝ…j´&ê=> ?ÄÝî)ƒ°S 2È#±gÿ‡P˜*™ÓÙ ŸÚ‰ãDÎÎíK¯Û°yÒ$hîø[ójÔm¬ªø–Qój¶Æ"ß/…ƒÜ@ÞøûÏÈå´Æˆâõø½\Š˜'Áß b<úï”_´æ´Ù‘=Úü71þü©9ôJª3Ìíô€AÛ2èÝÎ&–»²èŠ×°ÌĈrF ´á&¶÷dÇß­&zõ7¡vôYÈ^kiO×£Õ!ir/6›vd+ºc†JVà”åöì‰!©DþƒŸQUÜ›µdÔþeìïöðp]窓 þ¦4Ãa+ØŒM†êwoðR³3è.›@í^]Ñâ^îX¸(í·9È“-XH œNÁîMˆíõ‡ÈÝ•e 'Lð¹×Aè‹üc|@"¸wÄÿm½ÎáÃôUxé2Ó4>ÊmšlHMórÑôÙ ®ü’OÇL5ã+ÝDçrºÉ€mwÙ¹e̼lÅnlßoÚSiõ¥[äþh7šÐsï•ÝÃôX܆“Åhì€&«‰acÇïA7|_ÄImô„yÓDèÅ©gÈÏÏâØìy.Z oÔŒ~0þ33cÑÇsÈ’¤‹œÚ;YT}/õÑÖT¶ëŠ(íœq•¿@ïÎu裹¾Ý‡Ðökÿ¬FFV`XO9Hv¤ÑÜo¦9cæ,-…â9øŠŸ K×$ñ‹òèHþ_,6ÿ¥å0žë†˜p¢6ë&¼>dË‚ÊVîE8¥sî=1Š“º=׫0£¹bhc…øz8¾È€&­“ r±Ïw¢!EËwAíÀ]lû¬˜óPÍ´›x«vøaÝ$Q6sÕd¶öž¹Yø†KnšÅøK§±<ȵkó‰´øF<ýµ‡Ÿh´’mW`ùåN<´f%lOâƒJ¸5Û~‹ fO¤úoüY⪰rakÍ­+Ù ª© ;Ž…°/[pª}ØxÙ:î Ðõé7¹eKò¸ýŠ (uY =îA[ñ ÒU‰#ø÷nI'ƒÇk`Ò>¬V˜Ëý Rá­xØj>ßÔKxQûñxö·T†ž{æÈÒ.åÝ 0ø‘/Üê: »VÀï“Ôø6ðÌ~€¸¯/Ý܉7ötò·¿)ƒ5Ã|Œç…*¸ƒåHïöd µ ¦ÔqΆãøÉäΙàx2îîƒ#MšlÕ˜YLÐå8:®¿I‹Áf}ßÜ:žTÙL‡¥·2ä©ß.]*à/‹m­£è ¾<7|>·üʺ[Éþ:Ä’2 ¥š”Ê£îIóú'…£Lì]ø5F’ÚlŠÇÛÖ‚#ñ?«5ffóp•FwèZ>Œkɾ!Ý®-ƒãµc‰‘5D5aÁwÎc}›ØÔûâb¶(:Ì‘iÉ[ßáiîèþg´Q±} ¥!l½2öïˆÇéâ]Ö`éÒ‡ó= нá ^«Äç:S˜øŸedõìúifÆÛÉ/ˆºÜ¶âk¨_ÑbìÄ'RW‹ i_­ÑLá¦×L†³7âáÐû4zì¶Œ]Æß°Lj¨Æöç!v/¤•»sl7=Ñ‹…­dÙ×"AÍù'®À<(°´$›!5‘ù¿MÙñ«O0·úB ìrÉ©êÐ;:È{Ç›\¶úû¬—äïc{Ìî !OƒcWö².—aŽÕ/¼“•‡~ŸÞ“™Ÿ÷£ð 3sAùßf´G~!ˆ¸6‘y½úx3j!^Í/ã*2Ñ‘(ü¦+Ì"…§ÑôF}¼ïéBÞ)P–8é&oãJ#}»Ï-Ú£@;/cR¢–»'ÉNq\øªd¸u3ãÆ8Aà"pT[ìC "ÞÀjùç\éäÎÿ—Y'M;³³!íÙðh›J=3áPNHþ#ݳž½:u‘dÌ×§­SeØ(•iÀ‰ª±F4àÇ(_ îW¹°qßùF.ÓYÖZUŒ.3 7ç íçãÑõG±n9Ø7&ð+NÎúþ8w@ŠV ½‡Ä© ð®•>óØ|ŸžHÃå}YŒw»cmÇR'2‰=?”ÍDTmÙ‚E30Wi3I£+~úBÇa?”‰{CDhè`d”n»nbæÙ+øü*kxû WÒHæç"¦"Ñø3Í Í÷jQòAëé3Œ¿q™Kôø_ÿ‡cò?Òü=Üu\襕´ÞÙ ¸ÈlJ6­Å†ç~ì›§$[UiÎb6ç`¨ÏlŒ­Ò¦Ë!õàuîï ®g-ï ³ÞùÕgï¦9’ ôÊ}dÏFÐP`š<¯ —(4âÛ+öAÔÌWU‡ ž3 }£ÇnLžIËxPïw qëåñ\ž‘4=>ƒ²-jËÙ—syÜâE߈‡ì|Œ =Î\tˆÃ”( ¾®Ï:çp•o&°lë—¤Õê#J@W5âtn%^íœËšÌ¶àÙå7Ðôº"{~£†wuŸóµš’¤gÿV£[ÍÔ‘ù’E+ðLÙaܤ(Må’ΣªûvÏœÚ Ž"“ï+BÑ_Iâ4k,û«ÙÀ²¼ÂÜŘBØ&&ê'Ææ•us–m]°­3•¸œ‘¢/®&bhäOòiS&,y<ŒÖ+ˆ=ydŸà’-¦Ld}Ì¿?U%;†ý°Ø!:QÊš[àö '>bº3àAh-¯ùÇ[®êÍWþÞ§œéÊxº¶^†òçßwÈi /øñ–ý+%«Z,X^Ÿ¨Ôɲ£ëQBR>‘¹ÈKÒVo «ðf˜ÍÞ  q}F*6/BlF/Öm– ÁI³FðŸð>Š\U¹G¢¤#a㤭LfB©ã¯”9Ìßt9Ñ\©Ï‡ ,«ôÍ3Áx£žôQ {æè1Ñ—qä§ÌdúÍQ„ZV̯ý͉FŽ›H¥¤Mpü䣴”H}5k(ûŒ)GpcïL¡ý[7ÐÖÀvÎç¬%ç0·‹ô>úÊÑþ†wvƒyŠé´ ¶¥K’9_ê†b‹:+iȶ Â+èýyžô`÷FN"Ìž¹-ÉÎ4‡:Îl¾½ (oÑb÷öçКò°g;,yÄò^“I kä~ËQémãÙÒk7¸ÑC;áp¤ãþG÷ÃÚi8ÿ\ ¬rc9wGÑÊ-*/– ;‡6²Š‰|?µøH¦Ƕ_tÌà¿'¿cIÜi+²B×’‰ž¯äñ³³¹¬ÖIìÖ.v©õ ¤¦k¦MG]߉£Íûx‡«³Äãã”ÑCÙÿzßv¶Þ{³»ÖÁ•4ßéËÒ e ·+ÌšS…÷·byºË9ZÈED—8lò8ËMjÀ¢p.•yëBÇâyuÆÞÂ-ºUpeÎêqDª±uV1±¹'¾¦¡¨Ã¶lÇKLœö•Ëx¬JÇt¾DÓyà>Âgo}&3ÿebèÖ¿8¾âÿ¾Oœq4äïdqRn”¯‡“guBEÓnlsô‡žî“•r8:_˜=ÌåÞ=\Î ¢RQ?Ë7¸§Â©Ws¹E‰ÊôÅ–—5NK¶aq­0‹Ÿ›DÓ$Ôišêmúį4“'ã2[;þê{Sðâe%§÷Øë=–ÒEšï岌¢<¨^s~g2ËW<¼)tÎ;ebøáµl›êtÚœJ¾âÃ_·¸MD…~nÃÊݽؖün¾âh»ÿœ¸ä ª¼N¤¯ô=™~yk^iI·«M±ÿa] ¦„]çæž#AÏáúŒƒXr´7J˜c—±ó2#r/¤Àt‰ìw(÷½6l°:Š¡ÊÜ#a@T‡VoÕ(šR}†Tâ¼6ÉÓߪì¬ÍnÞœ°Ÿ Ÿ[NŽ×]†wô QÈuº:þ¾óÒ”uÿå[Tà¼åÚl”ÉpþšwøvƒWn:xšÕÜQ¸@³ªõh±6^ ;\#4åií aO´è3†!cƳ¾UðýÄüx®Ïÿr¸“»T›‚§É4y{”?_ n†¥º½sÄÿg…¬€?Âx›Cp®Êpy;…OÅ=º‡øò‹ÁM_r4[Èö/ (\8žKn¾‡‹W5@M¡¼×¾rë ³( “GqºÝga÷Ô½¼â83Zºz¤Çmf-Ýÿ¸㊹5¶›aÝÝ/ ¯÷O“Á ‹«õ£¬ð¤Ýw\#Ö‚–êÂôÅ8CÖ6´~ŸOä¹éÇà‹'3hÿgiü¢Å®|; bÕ¿Hµz"t‹ÀÏ›bp×ëF\ÿ¢ìׯ«SŽówNw€#beD¨¸.¶ýCç¢j´\p•¹íýUp`Ý îŒbiøúxÄþ½ËœœŸ6s³\—S@¬Äг•àŒÒÎqÕ$™]œ>úÙƒ~°åĬôÊb8åÂÔŸ™³>™%¬ÛO,‹ÊæÆ.½…­ió¸¾Ç)èùZ¦ócPp&My:Ài/‰çž9{±OËð¥÷AN¨]ŠEoÝLû"Ptæzx|â#ºü DÏxÑÊDM`ÛšB» þÂÒS¢„áËåéùw£X²×æ—Èœþ iêwPzEwŒêÀÏjÆlUŒ"F§GâA.ý¡›¨Ð½·1&(L¥™~]2Ýd€OM_ŒøôäƒÃµF O]øß;à9wãÙâšÓfù º#‰AÄÂñ$Ì|òÍBÑõ¿ ;^§¹_ÅFÁ.\°ÊŠr½ñ4¶¼~.‹ŠKÚñý‹<­ÆCÚû˜÷Ôô-– ßr%èáµ£˜ÑÖáÁO„·UGªÕ.‘™ÃuIê{ÚïiHlVïàç yC0¶Bº90ÏÊ)ô‚ìlö©» cæÉÕk ÊK…‰î/›ÝBv®–vöTr„ †ë£“æqÞ¢,,óîþÏ®}W%i{¶‚ui<"'g½&÷<§Ñ ^£-ºÐ?ÏGð_]ܯKH£ ટ"v}¯Á­!ÝØvð g&ƦÜ>‚¾u„j6½$õ(¡,Ëæ¼¦œ÷ë|:-³%¿¸¢ÉwM&zjÎn `¥wBÜ{Q!Ч#Tp^Uõ¬ sìÓðD¹"·â´,MØudnÊBÃgÇ`©ò)üü)óãkQ‹„YxÐ~='uæ¥é³fÒ„ŒÉܘØÒòÞŠ:ËÞÄûm¾øÓ›Âw¹ÍSXÃØ,ãó nzþNkµÓ}×Ç’\ٲݧAîœ õÝá‰yòS[¶ø«<{ÙwŽØÿ,JWraÑ'èç?ŽôÉÓÒ<õ4—lóúKìñÚz^мð’ß“û3 qÃãm¸01ÃÁcGz}û$ã!¬ÉóaOžÜÁÉõ ú«…½KÊJs [Õ˜Ögåƒçâ}têuSZ`õÁåLö_5¹: E¬rÏN{ǽþô{f³|ý"'^†Byœ¨ÀO(ùÛ³Þþ€¾Ê}Dèd1))àgëÓˆ²v8¿G­U§=k| „€Ùßù‡õhû«pç/G"?îDú#ƒ÷´h-Ú¨Ê6n‰…›æ,ui žÉx4âÿ+y¨]¹Æ¿K&xÈŸ® +„Jºõ¿~c<žŽ/ÒÒu¹ÜƒW0ÑS™:Í4ñÍCü”äùtlÿZ|ë)ÂôexŸže¢|Û\âr|.ÛQjlKåW1ÞöNÏü€%‡qPŽ›Ù‰WÜ ÈîÛ§@¡¸pm³ñŽ~Öi§¿Â¸|z$Ý÷c27ytÈU(1ýˆ ^ ,KÅj£ !s‹ÐÇy‘s…ð†µ•J.™žK®£Êû*îŽz2Çßú}ìYÁYØ1ûW<]’ݽðƒ»+Í2w'a÷2 *Ä" â2ÈÀÛv²¢ÄÙLKzgš×»µ”y­ëæ5V­ÆÙáÒt®ËrŒ˜š£-‚AMÄ$îgᦊKhd±U*AÍÆÄ|lÎI©ÁÓ?6AÞ›<ž­†'µî M-5蚢‹ín6ü‹·sIþcFæn/ƒoùýSVá¨r'úlæX`î…h÷‹ž-GK”Kq¬O)ž PÙ½¦KNáöŽM¡³§¼w­Ühÿ(oÖÞ°Ýñ0ÿvÅCnúb-|zë®å±ß:|¼ÜeDC̵ñBµÛr÷±#"Ll8Ž>©¬F ÉXT¹¶‹÷t(ˆÚÀ×:ò„Ü9zãÏTñ¢ç\ü¨²`Üv°ð ÃGû®sçŠ,àS’ Û3°Üf±¡¡¸iµ?tf½FâöÇ©ÔÐq0/åe’ñÄhIu–¤=T†ÇÉo.žËòäFô‘p<ú¡Tž8­®¹œÇç#ø%#`÷/ôúº‡ÅŸ¢SЭ†è.{ví½‚>ñþ â~Ò›½·÷®³¸Íë±-xí½:›Ù…¯ÀÏö Í÷bÏ-RÐÛïjZ¼Ç£Ë•èÜ+á«› Ý6'ˆŠ¹yÑý›mh¹’¼v¾…ߨҩ^7aà–5­L‚—ŽÔøc1x²›Ù:ª¥\ÀYQ….|6G·ˆÒ7ë[¸1Mð¢í!æ[x²ëb®Pqp-7há¶vÙ3­x]ìä à»òÐ5¼ÅµuñˆC–‘=vnäûÿ•60ö†}³;6Ša¼¹æ_ÙŽ"[/Br„»Ù2Ž Τc?O¤›7P‹ƒwYzÎQêpA½ŸëÀþ1Ÿé¼vAHy¹…:ü(›ÑdLÏ VÄš³y›©›ßR(ük%¿UhbQÓdùJr¬Î  äg¼DGa OÙhuæ^w ‰ñ…‘¬—M'㔆ëH‰/`UQî*z‘Å®YÏ–ŸN†…µwqÞóp±ñ ñ²ßç#˜ºê[8¢<™^7.¥£÷G³ƒ…yp×e<+»“$¶Ã”Î d‡æ¬aý[HJ‹GøïÜb,Úɉ³D¿óóÙx½i'6W!¦8 ÑSÃ~§1#Ò:æ€åå@UÜÊ î§qóqÿ5¾íŸ#ôÆÏ ä ÏnŸ¢Nk³ØÉÅǸ*Ý)tko Z1S5س[ÕäFK:™sí”))°Ë{˜R÷!úÄW¿>”` ú¥¸qk®ùÕ«Ášo~ÐÖ3—®ß·ÐdNâ˜þ—¥²S%ù½®ý ¡öì»×3?Þzº~“+lLÅËbÉtZÛ¿ê–öDp¶ %] h@þ9RjÑÂh­‚•Ê6tÈ=…z‡ÇŒüþýYÕ$Ð8¢;¦¢æºж]nÊ{ZûI€.w–¦ìÂìW:ì¸ä8´SŒjoß•Þ\cž"Í ÈáÞ??…ßÀä3±Änp)èì®G]› åƒ&K¼yëÌíØÅëÜóà ¹ýZª4sþÀ_KJÁDÅ•jËz—ºk`X°Ž¾×ŽÖÉôy{%|r -\OÑ`ÿõ:ß¾¬KÏ;sòäÙôy #ª­ ö» þ\3eå‰ÿ‹Æ|çÝNåípÃv˜Û°ÎÛydJ~-i>¢èÞc›ídÎɵ°(z%Ý’æ};Žâ«XUö*ò3ÉúlÏÀ÷"/m…“)ö`9Ï~ƒ÷ *¶¾T|”¹ /-iCÍTöÑð5òu`¼=X×þŽ÷L>ºô›c®4»t!oØí¦?ëm˜pÏej±-” tÿÃGõ‚T8tßHü›,÷$—mÁ Y§ çñT6Ul½½$Ÿ½ýìÊ9©”cÒo2ñ›=]Ô`sDÓ1UP˜Õ&ÞÂø—7ášç(¢\­ŠïD†0üÍnÖ—4o~Äu•×X æiúÁË+w*°±ï,鱓RÔ¾ š|Ë:€Žw“Ï;žðŸ©ÕÛŸÍw\ä¦z±ãË6²ÿz‘áS;·;e$NO# &LÕ#ÎrªZô¦ÏmîsßKÞãR¸×@ž%šÙ³1í а= ¾ Üãt¿”€t²)ц“¿M Á—`Êù@®ØR„fÜ kKôiΡ©œ_U$œ¾¤Iã’ȵ—øªm%ãt©dòwTò•dJî¦Ì³ÉŒôœÇ5A÷ØÙ˜öXý5¾]9ˆƒA¡S¦•`OÊDþv¨T£û¢¤À(oî¯r;£“âÜ™þ’t‚ò'8¹ë9Ó²C2¬iEÝ3èÇÆðÙó>ìþaL'ö/ÃuTg­Ñ"𷿣cp2™Ðg–1õvKvñI x káU´yI$ .dŠ’­Ð'0ÏV§àÓÝ9#öO*J‰úsð5—æ~ Û |3@yÂ.ö¤Ys/P`ôiØ‘2ìx¿ M˜íú´Zî6¼Ü± w6Ðc-¿°ëƒ9]³UœªXã—†Øÿú‰jé< V…àñc#ª8Ã)§ðêx=ˆ¸¥Ê2SàáŠ[XÀ̉2¤™CÓ(~¸Êïˆ¦ÛøxhaÖËâ¯w)Œo°†¾úVÚ}jÛPC•.gp³  — È;1jXó—’‰~›˜Q4|tkÞ aÛØ`êÔçÊ6Ÿ˜HsÜ #y ÖK¤O½/Žðߪˆ;`­t¼Ãn³wû×àþR}zù˜ êEpsƒW¢+?†&Ò9ÌDÓŠ‰ù§öÁç¸ëw1nɾÆAlxK4ÙRë}ßx<½+d !šÖTxJ2Ù÷ö D¤}1TÐIÇÛ}OÈÕÙlÆä:<½e*q[ºK õÙºÁ}¼x å°E¿ÚJĨ¦ÁM¨¡ËðPý-Ä%QÕïêÁÚm?qMï~òºÅ/¸:¢óÜ—¸6€ÄÇ"œ´b#Ù™´f|èÇs…ëéxõÌ]¨ùßÃ!?B¶è­á2å¡ëûAøuCÌ•Žeæî¬Nþ Çš}XúËS§xeöc‘+èeVÌ9ω>’XÞ‡ªÈã+iþËÅ´‡gEçÔ¢ý~^–¶‰ëõ<1‚ÿ’.Cx¶Ûz ¦Ó%M3ðp˜2]8ö..˜¿ŽFå¹Á¶aè}Ÿ ©„#zÛš€§Õ†ª›…ا/¡kJ ÷HE˜M-°a'Ï}åþiì¶3J,ú£ »¹J +®À·µøê°0Ø'ÔsÆyûÉñ¥lÎa=N$Eß¾D¹¤*2#G–ï”–W´Îã¯Ia(úé |2ã­Y@ÿÊŒ§/rKI]|¾&;ä¸k¶ÊÁÜ›ñJƒÝ$ªzʘ—ó—m‚»™BxKµÇ­¢öGOržû,©äÇÑœâK¦óÊ’Mº ;Çyþwˆµ#H¯E¹MöæV-V¾{ B hÖU)h}–Ám& LŸ–L„}³à,ïxÜE7ä°Ò„4þ 2ç³d°úÀ-­ŸÇsËÙ†.)ÂÔYií$æKd%Ù­w±ÐÜ Sm×°ÑÖ¶ÌïŸÊ]Œ©=úÜæTCÐÎÏÇy‹Æ`‰­*›0°uvʲ¾©xfïBÚžCxv$lþaÝ<€y—cXLsª\õ‚³×\iMª3MiË¡ãLOá­Åj cp€E¸YÐá;ÂUá üµ’–}»ÍÕl ¡ÅН‘ÁÿgélT>³ô&’ÏslÙtiªŸÜ»…^¢ƒÐ/ì‹WÁâG“9â§@™”WÕˆe•ù ýð-þÈEÖœ Å–ÛÑùÜ8úïÇg¾hš}e Æ¬ÊØ79E4ø‚ɇσMçOÄKAdÒî›x_^ˆ̞ŽêÂjâFÁ• NÌçk;ÊtYqÒºhiu–÷mþh&e&'Òxnly‹ùÅ×ñO"&uQf|¦ k¯ž&[«÷ÁØIѸONéF[j|× VœÎ ; y_¿ÎBϽyÂâà‡_±¬ƒkbËpTìeˆò’É¿ôdpµŠ¹¯+EŸš?Áä6ðý¬é³ì$åg`¬Ä.f‘ ÖÔ-‚¯…¶Ð—(o¹£°pW9ç›iÏîV‘]w/‘?’ä°§-´Yø<V,ÛÆ¤¼®ÂyÓg`uoQYô “OÿÅÀë²Ì`÷€Ã¿[0æc GûÅᓇ?e,¹ÂgÒôÅ$Mz|Ï_þÒñºt⣠ôê0˜*érãúÃ/1V_¬Ožß®©;DZÅ-ö«ŽoÊÇÚGûðë$¶´­€»zVhÂÎíÙMÈn ^⬠| ás^qÏ®\”3m#ü·÷¤”/ÇW椥´ΦfÓê2–›ÀåÒXøo:¬)‰€+Rø1@‹Ì£2µ[ظž+Ð?˜Žaõ`Ô¬@ezÒ?ñRhwG§Ùˆ±Ûiù|m)æ[®ân*åpk¤·á˜0¼ƒæÿPsÖnX½" VæXsB¿ãæÅbÅÑ^.çåq7í¯eï}~б1Lür2‰ÜPȽÛÓ—¦yÂ=ÛS°ãó$fÕgÆFÝ ãý=K¶¶´À¾'·ñYâØ…'VOä2uã°n–-¾ø$ãTkøérzœòRí¶£„ÆšüßEqœOÊËÁ_}ŽÌ8AƤÀ•.Y¶ Æ…Ž*r†k‘g9&} †¼vžÆk™ŸJàY ªÔkáê—™œ¯ÃNwÓ_îç¿lWî ì4ö¢Gíu锓 XÒËtiÆù;ì)Ó8ÅýªþH‚o>Bå&eú®(]¾Î ãÿå`œÞI:`C»9{âqt“ýy×q×ñ]4¬ðˆ½dO@”éy”ƒec r/L¸é‚UxMï"nZŒŽÝ;ÙLµUÐP[г–¡ `Ì™9—®g<ƒ/ßñ’æÿÖÿ¹`ýŽL\Ä£2ŠÐxÊc¤ÜÙ VøÒÞPá¹{“Øl×|£i{¹IpneÛéÛçe$æÏ3T–Ôæ2ºNÓ½õ¼N)j¹} ß>ŸÎŽ"ÿ­éÌ·ZšrÇP«nX·lü‡jj’Ü5Ûqè$±˜ZdÁìKËáÇ3ܯ¸Šl]tÚ‡4Yëd#Œ•`×Bëùuõ•pöä12†L‚ï…è×Ûf´¦j+ŠÞa‹&±Zå(Ú:Q†ªB¿^=ÔZ9ÀYÕitàp¿õmØÇ6Kn"ï´ÚIÈÚõTÅý6i0ëÃUWeFøïMÒ pÚCüÞþ†ºku…èÊ×àÍ|Ö3–SH9Lö6 .…±\m\ùÔŠ–kÂG&Séʸ¡ßKÙ›’0v]Í™žu˜»²i9Ýlõvî‹á?V8ê8Þp-˜{…àÕLˆÖ5^]䯇œácpå®wpDí¬{,¦-ª[/}*Ÿ {ª°÷K.¢‚ì(RñL“–.@-ûõðÅõ ~›cÊì¶Ä‘=ó®bQ•9÷Ø/Ó—åãrólÌL±bíº>àr:…[…Ÿ‰½ýÐüV޳MØæ¯?9ËÀ¯\u„6ý`\7âÿ¡Î»8—€ažÌ„ØgMS.q+÷/a—¦Ý€[g3ab• ÝçÑÀ?¬:ïê PS1mîÊã<Ký.§e?ñ”Þcü¯×Y~t®ŽûÌM{°fiÎà,ö–¢¸¥L›} .Ê=Äw `(*=õ@nb*Ù"ózÜ+áûõam}9„«³8MÊÍhã9¬¬ÃåÁÇ¡6P„&ÜL7†®`ÍòŒ ˆ¥Ž-%üïo¡×GL;MÔ–ÿå~$¹ —ÂAòVeÖõr·~ -œIGïÞD/·•=l ^ Ëž}©ÿ#ü¯pÇÛËpéä}°fS- l¢‹&Ö@e”+¼®‡”—³ÞQké‚ÔvcÆxsbèfQÚ±iÔfÉNÜáñŠFýÃCfºÔãA!†hÒ¥û G>pýg ™‘@­¾·mÑ úêN9̱õçÎæôâžÕ‘«H3¶ŸCùùâìÚÕ$n²w{Ýò€Ì“Ø‚¯u½¹S+6C3zADP-Š2J—+m-0£÷Å(™<Æ&Ô›ÂJ=sÈuœë©>¢œ¾Ew™‚íbsØ™KšÔÚÂj6öÁÎÁF QŒ†Ä  Óüq¤ÿñj…8N"hY›H±ƒF0µˆ8vO= ?…³;ö£wòh›Lä¶}|Bç† ÎdÚvVÓŽ²dò#ñW°ö)\‰[E„­©ÍÍ0LP£?Ëð¡ðZz¶ vê aùWu?osOËÁ24àd n¼œú¸·Ö”RÓ²X–K&øú³Ú»FèTõ®I‹QgkvÉÎuo°¥Šàø{Ô¼(ÂC‡EÍØò$oãàyI#ÌÈ©`ÿöâ™qõx¸ø1-_щ¾¯¢óþkR1Ç’ ÿozc&Z´·bÂ’ú[V•l:gÆ»ì\Žq&Oá¢-s³Æ·ÏÀºÅÓx±¼ëxaÀO:E²ïùÁïZoî„[ÖŠ¡Ð29:F_£ßüÂßâ°Ë07—\æ¼ØÃ®)*°’îh«žÍ„𯱥–‘ðºô zN0bõz"ü="W¹Â{è&5ÊLñÙKÒôfC˜& g¾¢oôçµ:|¶ã5gÓþ¸Ÿ½Í.ÿêÁ[ÛµX²3uJkħù¹„¥Õ¤[ô>|œ2‹¾8Ù̽Σ$Føÿïõå0xB|ز³ç ¢‚˜>8R£Þ,I×¥êRãµÎP´~ë˜0³« pöëRj´b¼^wñÚlmj,ÿÏM¾ê>–îØûL ¦>^Oo_q¯ûÜr¡G¸š¸° Å>àõm?f‰/d{´]I2$Æ*ö¸A8é‹YÁ¹5‡1ÅU­Œ§Ðb•ÛøFãwM­Ê®&zj¥¬ÅJã3 qÂ?34} É çá«]ÿû{µhÏ6x‘V†bd™®‡/þ¾~º£4YÀ´òéÊIT5žýlÄ=ðÄ¥ˆ‘øÏ¹]ƒKš*p±j¯iU,Uùq ëËÞq—•}àÏéõ0yF'Ì­QgÜKdËÆ:¤.δVŒy¸ ä¬e鑃7Æ ¯Þ!q“â±aâ_ËL~’ê?¢3¾ˆy‰Ÿ¿$ƒþØbÈSQ§ƒŠ¡-Õ8Ùxpÿúµ¦×±˜˜ÎìFÙÕ`*‚²óÐTx.\ú›Q¶`´ ¥sæbg“|»ã”“¾*ô{¥}¨QA2¨£Ý¿ž€á2kÌÏÂÆ¸|ýO´/hBg‡FÂBsI­”çñì#ôŸ‚F›èþŸ¼`5@U‹ãêê&Œ;…RC:¿ä3)Ó‹ ÿª ·ð´ì{Ø?ӽ͌ëüƒªŸ›PrÕu¨2e—|£±‹ùÄ]DG)òÿª}W¬o•F•†£˜ïíÅyÉçƒÞDWn‡ƒöM`eG˜ã5 ֦Τ¾Àgè$wLy5›q|ò“IÞ.õUw¬ƒ'ù—Hs]1&¼_¿t`'óäÛ-•djUPlÃ#,÷¢¹®oàê¦gxÒ¹ógÏ¢@…Ö^9Àß]ÞßJãyi2±g³‘Ú_ÑÅ¡òX¶ëÓ|F¦LNbÒ+ª9ÍHçEÿØ5‘ÉWŽ u¾ ‹´Náíkˆ&v¿÷€YõV¾ÔT7梻ŸW«âÜÆ|Ø´t«k¾Œ/Ì!}Ã4ºC1•\­ˆÃA«³XØý¹ýÏ@Ëp*­AÚ=šžÖÕåíb´¾n9]™Ì3LŠ€¸ˆ®gè û|éÏfÆn›êG>Dµñþ[#ºdvoZë.º®Ó­æ‹R“TéÖ¦‹zÿ‘«›'ðf vvçX¼Òkøo j=õe8ï—=Ú‹– iÓL?›`L¿RZ× ï• ú¤ïf§™¦¸!µÈj„¤Š±dßs:wª 1:¤ÃJ ÂÊÑìkw ·çêø9®A¤¨®ËŒV$¡ëxqö\ã 9yy͵‘§aΕb×ÏÛã­KU˜y÷+úEb­© l=TÁdÅ·¶–FÆ íÞ4ðð|çdO¥ ã羇æàÀ$Ïybôœ™4ÆdÝvo|^§ ëÆ°Ê­̽s=ô[ÛÓÅñܨûuTXd,s‘Egš™0Õ3+è&Á oÓ¡‡ézµ­lœGüº¨IEÓ#éÓYvìÎv#˜ãù>„Ñý»"нsµ7Ža«Ýx䲈Þjå°‹4»Üˆ.¥bt@àòÌ&0Ѿh–½v-Ü}lB÷XGS^ÔBð‡H¶s̸üëîç‰w‹Ñ"‹‹¸(Q™‰x ;ñþ*Ó«R•¬RÓšZL}]ÿb²c}Ï Ì½Ð'Çrl)ÝËvJ3öüX.-ݺŸN>ó”ižŽtÌXAc¦qP4ÚšM»Òóÿû›-}Ã\§ï\w\<bJ®&–*ß¹gï€ (ÛP³èø ‡~˜¦ÆŽÏd£m‹1³Ø_CjØôÆ·ÆÃš“z,J<Do5á¶É¯`Zaµ¾2‘ûtà… pN­8“Ž6ƒæ«©\LÑfÖbáÈ–¼hÁ¤Ÿ ÷æ5\X‡]çðÅÙHºJYÎÉ6-xÄ~á…‡PRD”^®ÀÒ¥°£šÓråAçr<ÜÕƒé§È¯´©T!D3².âDOF^, tܼf ±ú°Ñ°Ú¢¯+GÁì¡RH”PsŒØ>@̾–ã–! æ±÷|.F™›B´¥;؃«0}^=±;%L§id`ýsä§ ò·ê¬ã¦Ïs­yl㮞Çoónl ¡ÉÁ…¼¶˜©è3dÆ·8‰_/«aYð$­S¼?Ž XÞù˜¯ú¥zÞ¯À¢ýµÜÙFEØÓYs¯6<}x—»X²û»‹F/¢ÄP„¶¾3bæ±µÐâ£HUœ„XRy-')¬EçÅ܇ÿz¦…ÿŠQÁ]ûhÑÛ4:mº;ýñï:ä¦&á‰øÿ¶DÄ\ˆ~œ=‚¿kê9¨ìú {§Ò¤%yliv=¨?ÿCÔŠš ß2‹)ú^å¶F:𙲖ÀN½^Áíå ±‡ƒxá¯à¥ÿ(”ÑcœÐ[œjPÁ“~§'~&‹ÞpkÇU£ÔIzµ*ׅ߆¢óõ(tî<«Ö…_ëàw8ŸèØüBå óõAE!ú½ófxˆ@™‹/m{‘KRæ¼ÇI‡‘üQñeoVÉÒû׉í#C<­,M;…ÑCNêliüIÜ0fn0ŽÆ’t#ªü@¢>ãÔ¯+qÆt”o’ ½z=ØzM ޝpÁ,ß7#õÏO• üÜßyØ,QÈR&îÅçÃ\¶±)Û ÷tƒÉÉ4X‚^ïh6HÒ›7wÕl½” {p•§)ÿ¾`<3ª$F_F¿S¥$$Ü“Íì¹£…ˆ“„¦³ûòé Û¶…Í3Ç'çÃØ7KV·’á'÷Pùi><· e–áOºå|*\zbD¿?Óœ#G®2#ÊM&j¢ÁVI‰5}ùïþع/†¾;ëÂ?µT¤%SÔ¸‹í¦²à}DåÅM4Û¦Aë5XûäµìÔ}]¶á†/h/° ÌccbUQòwçˆýE/w’qúñ<Ç‚"ˆ:Þ ýæÜi…ýìÙAýÕ¯Õ›0—sfôBf=ò“Õˆö~7¬x`L¯Å†Â—Úµ´êù;¾ äZ @¥2Š?«Q(¤¬ýäVÙ{ÉqÕ©çÜÉ3lr8…“Sjܺ“ÑFá¶Þ…3ì9½ðùÜzy)}ÇcW5XÖ p~öß9Dà‘ixO‚‡ 8mØú½Åê§ÁÉw§PôžÍÌYþÚ×ÐÖ/ßè·CLÉUHÊæ¾½LÁRϯ“g!ÿÊAcÂ(lôÀ§³‹¹Ÿï7ã%'Ö¯=¢ÿV+¡G··Ía÷°–œ\Óž/QÌŸ±3“™O‡?ëþ`Á ¾r üÝÑÚe¿G’Á6Q¶-p;u×úEOäÒ |_zíéuðü¯’öCóì”uvpâb*xû‘ Dð®žu4oZõ|œ÷Ûü½4ñÜË×8T8 ’g_ç _›âáx;ú(`š4ëSE•÷äG}"•qÞAw&Õ‘=^TêMûUU¿z€+É?¥94_s2sÌÎCûô.ÓÅK£¿òŽº™ÐwçfA‚g G€´ }üoÜ­[#é­År#øK…yÁ;xŽÿ®ûÑ«­Óè:í\{Po9žÀ Ñ†Ü·vJ#Mà/òº¹1ÒíDÛyaRÞrœôèy¬EHœÄ³_´!g¾“ø˜ íRãD Ähkov§O óT_á I5 *‹Ò-ï.8X¿4„ ÝËd•çA.:4K³ñÐ…,Ì«’£’¢^T®.ÄV¶€M\8m;éD æqtt­äK¸D;Ó´ŸÂð>†z•Íë†ß)-°°ú!,»,Æôœ»p=o:e™5Üquª£­FÏøÏ¥:«4imôbêÖö¤úÿì÷˜”ÁÝšF9§Oäg‚–½‰dÝÛ.³¼õoqî…bê;}[¹È _†D€êb)Z4Ìß©ãÂþGI¼Õdž¸Ññô›ÃlrzS[üÇôÜ„5¥ÍTá\:³;!Sÿަ¶Ë±îÄñØhû¢nŽ¥ÆvQؼþ8¤¾Rc&ª ÆÖÔûÌÃÁÅø´î. Þ Kn¼ÅÓ}²ôí}*gƒ³LؼÕûpÒ·‹]ÅÙ†óú°ƒ×…Ÿ4ÿ×C]íôw,:¾õd[¯¾‡ë~¡ï /fU{ ¾™üÂ>µŸðår"ðäÑÆíÑ#þ?}ÜRtÖ^†s‘k¿­àäÚÉTÀx".ß„²—@$O‹uÌ” ;³Rɳy8CÔ俞dD³IøÇûŽÑÐÉ ìÅì2X1q›9O‡;Rx È÷¼Ü³ RQÅÙ•©’Ù(«½îõ‡Ò+u&÷nçýåI+hQõ¿"¬Ùa{¯ÓÓ…É4c–6=û:æžø‰5wKÐëá8¶«ÆöEjô¤•3ÏÄ´6mço¸<YQáÐKÜiä¶o|<4žî=—›˜3ü2ˆuççз|6zÑqÜQ:âÿ¼ÜË`ápó7ü¯GX᜛×î[nÓÑ]3•Þ¹µ«×è³ë¡ U.›¾Íà=7?lgžo!œ@é­“LÁ×™~/ÍÖl`Í!¬ríÑš+}ëÙ äòºüáä›,h•æ÷®;ÍÞà çd§÷Ôc"½{·‡þû‡¸æ”æ&…oåQrËHm&¢‹Ÿàcï) U¥ÃmÒrg³ô†·§‚hVE»x1,´ i«Ý²ýñhž[Ç.^Çr‹Ðt˜Ü šñŽï8òe5a/ ×~ÊÑ?½î èªl9:4j,Õ*ÜOV_ý©ª4}¼â•TðõdœZ>W)E3抰{×µ­zìaf*íóFÅáœ-컌ñJøXÑ« Ï®Âe—¡R°dB˜Et è<Ž5‹¢8Õ™Z(ùI™þÚ8Ÿ¤4ŠŽ]Àî_+åÎm*ġٴó½/5½Êzû8Œ¦ìÔÌótñ§;hdÁ˜‘¸3¯åqÚŸP‚q_*q›È*:SÏêØOdó4æP~K'N²´fÛ¤_±–E"×§_We¦Åc™©N ÞÕÁ¯{méÉruú忢o72ÿ×9µ[Š«¡ºcÃ5ÒäÂBºº¹߆ÒâeBðÁ[—5J5ðOf)]±¬âÂCøöi'-·Ö¢ëÌ 0¤_‰÷'õ&ùÕ÷nBJCü÷YÖYšÊñ%üifü5ê¼M †3l¯ËÆù­ ”Ouèç1_`·ø(·Îÿ×Ðø¹úì‡à þiaª,ïή ƒúØFˆØ0^˜—‘äøjÅ\ZS ­âèØy\¡Ü1Òzõ™CÚÀvp2û0[/UÑž«…ðù_4HÚŒ£3?L"æ–ØU,ÇfÑÒá¡ù¡š6ÊL0f}é!¹·ÿ`Ôµ‰,uÏKÈ»›, ìà9˜*W‘ÖÙ%èô¢“÷ÃU’Î0Se}ÛíAe¶8í9ý Ÿ8gså;ÐîU$­ØœÌI;3ÓÙΕ©*ì†A·%Aéw†·ŒÇeOïϬé´öÇT¶l°Ú•|¹w^X¦p€âétš» ,Ô¼Ì~ß5ãš85¶mÖ;ÀUsiâž0l¹ÔH¤3ØÜÛê`ó} ãýÓ…ò÷ø}=K±QzL®…ofÁx´ð° §q–¯Ó9¿ŒÓJMI.±¿mÚtÞÖ÷ÛoLG/ÛÎ6ɽǶé¥ô$¦éÌnÙÛ`ŠÏ8®ûŸ{)G'zÊSß«rä¤v<ûÈ2Qáv¹Ú ÉöŠž¢ ¦$<ƒm"Ò ¢œ½k·HSÿ½ÌÂ/NmÙSΤœÆ;Xuñ(›õÛÎ^!O»ÔÈ”é{huÈt6Jlj*D©0WÁÝжc/Î3ôcŽ2ú-çwqÎøq1› ™ÔOïšË×*øF;Ø/ùÿ•,íæhŒÆ+,\ê3Gщ <ªÙû¼ÍÆ0ƒõ3˜Yênê`>Û›=bÿŒß:D˜[·‘Te&ýT„Ͳ€1Âþ8BÜtÙÖwë©÷yk¼ë¶»ÏjÀÃýA'ÐÈ¡}]ò²cÕŸC¼‹uø>Žf膒÷ ˜`œ#ë_°öEÙÐo\ÝPßvõÃÊs hà¾lúCÖ‹NDÉÑ/†ãv¾ñîqÃj7GÀ4õ*ðf1­o¨fǪïÃ÷H ÐË 'S‘˜Øî~‘®“¼À\W$Ò-v¸Ýê‚W4ÝcÀ&Ž_ÊÌÌ èç3ô«Ïè:èÅf½˜Œ›þhÁ¶Í†XÌq#üŸ­¿V×z㚣KÀâÒTjpA‘¾ñ¦>ÿ´ðõµ¶ÎâZ¥°‰“À%'œ¸užrlö廸7™Òþ'9¹ëÙ>ëlžkÈ}<ó!Š5Õ‡pŽ´*^×6º1çjÉYÐÏ}ñQ¤=³b%½Öæ…SºãúƒSX¶@×~ç9ðç2Ìž’[®­ˆw<2ð~Úlkðã%Ü×¥MLÅoˆ§Z]”íïÕ@û±pï¾5¬ÛòŠ¥â#žíÍ™M³Ì/qD®ÃÙó÷ÀñîÎÃèX>WÇ®  Rþê¿uŸÉ¨ˆ]8:1q„ÿÇÿ2Þ ãêñh»=]>÷ ŠÏ‘'7Ç%`Ãe1•ûÁ ¿G-»"¸þzÎYæˆãd³ˆ[L®4äðKŠ>3RŸL –G1[mNlÐnI4`+?¥B‘¬dêDp1þÑ׳ǾìˆFÛPzk&þ‚1’wp÷²|öU<ú"•°7q,ÛÙœ„׺áùÜÿr'³Û,Mu3ObGð4œ°é!Ï0àÙC/Ã†Û Ž ,ô wºíðZEÛ¥9¹Ÿ^øòψÚj„‘e°É(ÝOÂÝZgæX4ŸÊ~È€xó—>™ÿßµ)£fõacüÙ›]&õBÎäA|Ðûó¼¥H4^ñºã¨¹ÁŒw‰% ¼9–©JÇÏWÀCÒÔ#È[ÃÃ@CÖÿO!¶ç’0/çM;˜HêB‰‡,;µk{$.Mã7Mƒ•1[ÑãÒT€•Jxøkšƒ»l ßGǹ”w$}¬]µá;f(æ¡ÏÒEl÷hmúQÌíÉŸˆ-Ý9î27;y—÷ûˆµ';,µ!P[‘Ê/9ìŒq‡ÜtxqtXúG¡ºbç.×3š‹/¢~9°÷_®9‹²KÝFâ|Û)ìÚË…œ ‚ ³*²úÆe<`Æ—fó^\·›„éûÅ'à¿ÎlûŸ§¯d…‡·Òf“Ñ´ÿ÷Xo4Ž%G+ÒêÕ:L`ƒ-žÞŠ*Ì®S‚ª¦™bŽÑ~\3½†Î¢Þ_#ÛéE÷+Ta¹ÏXº&¾žÍr³>pÎc–¡–©:›ô¤Æz:º†™ø¯Ï¾>¾‚>Æ‚T%n3,÷*&…+ýpVànzpgþ#5"F-‡àî;Y:ºøþ>ØLf;,€{ s™~L/zOºŠŠ³TÙþÁ^³a+ÇS‡2GâßÕk2á|zÐÃÀ‡•6þãZdÊnÉÐ…ÍoQ·7‡Öû¥GûaiÂe¶Gm»7TÁõ7ù²Šº©Ô9¿j´j±‰™ÓeÜ |‘]Ì^_†ÂûÆQÍr/ºûØN\xׇ½ÕMa{–m¥úOÛ1õ»ÝÖY ßzd©9k¾èH5Îü†¯-¥8Ùz©¼4|“ BK«húì`.¦v1XoÑA·7åð{‘µj÷bÑ3Ó˜wf-Lø 5ßa˜Ë(Ü’&Ëä¦å°7ZvDO5‹çÀf{#VkÈ*Ãf°¼1‰¤zq.íÚDSJÿ·þó:“Lr"Ö„d¿é<6êƒ 5õ¼ k†ð½¾$}jkƺBxXø| 3nU¡ïöÀï/vq“t\àgt õÈ9®Û'â8 /6Öz*Í Õ¥Z"ké—Å)(¥ªEµçRëÊr,ºøv¿ 7§›Ñ©­çÑZé ª‚:›©-Ÿ ¦ixPá¬ÑôIfTJAQ ^~¸L( wÚà˜yìé3GfÓeäºÇÒDФñÒÒ8g[‹ò\ÏN]‘€;ûm`óÝ£ôEG>þù³›ÌÉÍbãb™÷ZYÞD<̔Η̇›WwŒÔ?=²IܪæJâ4Õš´¶²hMEzèv§©¯„Å­Ox‡nN„½çgãßvIx6’o+oBµJM¨ßÀ!îîr] <Ÿþ3‘'}’tíšOÊ&Û 6ªÒ›rÝܨ?:°mílØ3ÑUÛH;eXÞ9vwÅl&T ™{dؤåN´OÄ™ Cz$­A¡¦ |šÔ`M¯"Ù0A—Mã“ÆSøë†ïÉ"7Øú:.ª+3¡ãÁà°¡#¦Ý™ÌX~ÃýGxju>ßÿ‹L¬’†–ž™”hå½ ^ÁÞgÛ’[„šdŠÑãáJ4ñÙÿÖ¿þrégïtœðîxÁµ–ì㘭T!Zn¿ ¦¯Ó¨áô¯ø[;J^Kâ±B2ƒcO’<á|vo²¡å}"ÿÿýA"{¥×]€ðçL~'ç ¢‡f):ž¯dc4éžÔôòmj–rOɇÂ6ÝÉÔjŠýªiÍ4â¡»ª8Óø<æHìkKgæ÷´ÍqÇÙ&xµ®¼tÁdÊ*ÿ‘ÇÕªpµiÛì•YÐíûèrÅ™é44â'EuzùÅf®R`÷qXhâN­©ƒyÓÓé'3-ZÛó4ÙôA³‘çÈû rªpÝ 6ùL!\ž·”È…õ"Tû °QÌŽ» Š/Àåš/+ÝVÞÓ·1·ŸsÈä+9Q£¼ÿd v:’ û#„~ã«k>HUs8i&÷ÐÀcå>tS¢L;ÁÛ•а’½Z)8%[™Å¹ÄÂÏ¥¯0CT üÆ;@Ërq®F(‚«·-âwØÐ'Â×ðóÑxVõ¾ ‚^N¢Bê§±FWŽFsy¸¥_w‘%¤gÆGÜü´Þê®§S¿]ç ÞÉ3ÅW©t›‰ý“NBªýyT„q—š< :QŒŽõÄÿN¹uúó‘6ð±wÝpw÷M(ð7§EÛ2Q@W‘›™­ëP£¶ü?_äßµk‚´ ïßnÃ\Ï•tJ¦1Ýæö‚÷qÎnŠ¡[î[ÒSŽÍXø~6ÚÛ2}Ï¿dó9ê{á-n,vfûcœÑ²;®…-dË£ìàõ—§à[Ín^6FƸ¶L}lh&É·2Ù„?}àÙ%D/Ö-A÷4wõÜ VbkflÇ0ïӜήH>`Æv¼ƒœH-žµ’´a€È¨ ‚uW¾ä¹ÐEy°}ƒ*ñctû"þ7žKÇÒ¡-FüßFØ ––ò/5E3Bçreaà°ÐŠ•¹^%ËþL&©‚“°/¤¼™'ûU”ºœžÃ¹,}Œ_ãz9n`{¨OLu»æk¸Ü'ËéÆË|¿yù|ÿû˜„M©×µÒ`t½ ×çÁWÑ_HÎ?Áuš4äW.–÷ƒexí‘0»µû>zs9Ãûf¤cÆÅj áL`ÝJ¸ù¡&=ZG­¿IÒ™-ó¹¹ÂM ›iÎÉßFøoC‰F‹ôB¸ãó›àæ)²-Q™¾Ü¡OL~}s˜R6nœ\4=äæƒöó®©x™¢CÏ`數ãZæ[ƒ"_—ÂsS享ÅàÎ0.³_TÁA¯BHÜ·‚›´ÉˆSŽû‡BKt¡jË>ö&¬ÌÊa›´%âÌìd' Töƒ›Ì~ÕÈ]µô¶Ž†ß]rì^Ø#ÜŸqÞw‡Û¡Ññê|PšÀ ­$®C!Í=É›Èì¥É ‹!Ò—Àü=Sºè4¸§<œÁvõƒ¶w7!—õÿWÿ»?„›z 55^\;†oÕ%àÉ&ÍÙö7Æ5áÉñº4åæ?ö¸ç’k þƒìÖqï~9@“ËKâÞàŽÊçy›W›1ÙK%°bÚR:íÏLØ(rbæ8ªh wë‘.wÿÎ~Ð+ð‡¿Íš`vb5êKš‘_I© õ÷;¬}wìþŸ÷0ƒSÕV3›Wû᜿4sºç mÀóãáÏÉúèÃ-ð½“ié!ë"é2œ ;ŒØºã³±?EÚvËÒª)¶Dv½õuRB÷ðËè3)œJí=®§²0ãÛ9´؇]ß/sÿ½ÿ?ûç ^çK¬•åDHü×ÿLSTë±öºç·¼þ¤H1mRÍW”ïÆ=mN`7µBÝäéË/_á÷ûV¨øw -w(PE…›hð\Î-7'ž¢Y˜˜ØÂÍ.ýâÄVçµqÞ¼°Ÿ—PLXõ.9ƒ,E–ª´&ñ/ØY£]øÖê›Ú0Ȭg°YTž÷Ž>éƒ"â¼E}e8žsðð°Úà ØÇaMA0Y6ú!˜iï‡6MØ9zùS´¶ºÀUo(.¬7޹Bz£úÉt——|÷ê»à,#LÏ/ývAU€-ÝVC†_œ†…'ìÖŸÃb¤ÃÝöñÄòÝi0 ¯Fæbƒ®·qÑÊ<|^¾„+×ùUò_¯´åoõþû¼Í컾•›g«n–À\Ûè+i‡ëD…h£|¯ûóc ZŒ¯ÞÄ­õ:wÞ:cÎîÄÛü×wÌ«Z®IfÿÇ%«¬ÀÇw&ãƒT¬- eê[¸-Põ¡’¬mÓDž¦31›þ æöÌ垪ª‚Ȳ2äòç³;ÕÐìŠ0½aݽ¼J ibÿs¶J‹S•ûH¾ëiÒî…’´á~z½ŠRÉóé{;èì[ç´ÅÙžÂh´—¼f–ýÜð9Ìî³»ìy î/Rb™Ÿc™AÅOÜôPž]Õ³gÉQ¸?vêÜ¢p­YU>³”Å\D­·õ¨3‹-Ìƒð¥“ØŽ21¼œQŠ[z9_“ÿÿç˜L¼5…qÏŸ[°`Ưü†5QßÁpÔ´¥X|t(Þ¨r@â*ÇPùŸ`óQ‡•½çZ\ËpK[“q8ÿ“>X’;³ù"xõ 1­înƒº *íŒã”*‡µùØÓ›Å‘QÂÔì—î=z=Æ÷p6ysñü)ZvÈž®z 9£t©mViæóPCí!YÔµµ³=Qåìh–| ,tÅ邊öö7Ñ{² &¯Ý‚‡žÎÃ^-uªz1Ž|öІ€ÛO116’ÿ`Úòô_¾øÌ‘›*þìjH-. Èb}Í#ú××­šg`ÌyHWq Xô†!"v®Ô׸ ÒŸ«.xæ*°è®oà¯ÒŽh;§ •¤òßùq³Ò‘·+‰ùHwbc—쑼Àí›±ÌË…ÏeÁK­‹Åõ  ¿Ø%øX=n>ÁþŸ— ;êgqÏŒéÚ=ìÄ."j‹0~“>¼ùäÌimÞÇ%ÅUâ'•ß ø7¼$·¶kÑ*So²SUšÚ­<‹»²˜ë'kllç5vá’¯áì¨TR~4^g5¤Tê8^ì\ž³$PùRg4‚¿©ÑxþF~.ªížÉû¯¿¸µë!‘Øcv§bè³UìQÉœ›Åá˜É<ߨJ\½PŠ$®dæß´éîëùÜÐv#ü¾ô+‘^'ަû#Ðlß:žWA"ô)[ñk<6°Å[U˜¤®MÔuçöewâñ‹hó( ¾Yˆ±ùcñEšíØnÅõÌeMžžÂÁ?Éè¢ý&DSn{,ÌŒbGëÖ°UOй…6Ôx¥!æïªÆ&žNz8ÆÜ7€Iº³h¢ÝÈ;O*õøÿõIG¶‚ƒÇÍù—ôT©Ñ$)]·Œ09cñˆýGw À’ùUP´«†_·0˜uÆGÖ\ûíLú£6rðú÷0¹ÇÃÅMi)EsË.À·¶H¼/?šm›R…aR¼6¡^Xܳ›%ý«„5k,`á$sâ6N/ïë#Ç®§wŸøóôEØuªœjÐ볕`®_Pô2þ„£oÞxº¯Ê–˜œ% ^ûàÂæ{`岪+RëÂ,l±þ‹™ úJS6øbʽ’w?^UôÄ+ Ò¸ãôŒÜþ _Ùã5£tÕa6æ¢ëF±ƒÐ-—ϳ3n·gfä½)ª¯ûþöòè¯}3\>ÍÍ`âÌ©´sìp¾ôºçU1M.Æ3õÏy9TÖ*Ïbõw‹á\W:fz-ã:m)ýûµîQ^>ºc‡Íó÷Ñà$<š|’°öçõ¦gcJÙT–«´¦Ç¿Šk ©ð:l·ùD3Xë²þmzt)G#ÜǪ•¼{vUXé¶`«]„éø×¹ =(ÎèÔ§pj‡1•HTÀÓ{ZA¬P‘;É’2tL€•O»„¿[étËWðâÂb)_Er¿6s $æÑ×—Ý îÜ%Ȫã½K½‹÷mx,ÕK™ÊºŠàݸœÕZ‡טÀ“–3´Lý&®*<Â$á®[,ËÈw[Å]e·¡ãl%ê¿ wfsí¶’¨åIÁJ©Œo²(œ[»Â†)óp bVÊ#ð¬“羜äc^Ä$&,<–<+â° v=?bËka“š3 rrl÷;IØ´[ˆÜ5+Ç5ölövÚ;ó*Ïf¡ è·`c¶Åƒì®ÝÔX[„Ó«³e¶%–4‡7†í †)'þ0>÷ ÁÒMuœåëƒ+Y÷ ®;º G*Šmi#Bc‰(:þ,Á¾ünîjF4½òFŽÍ uýªO¤} ˆà?˜ZãsI]U+ßüäs¸1- Ö ŠÓÕW¦’ÓèdÒ†ïV¡]EÈ)xR6š ͳ„Òí\ŠüùœÉ¿Ç«³|êýoŠàßHìÙD#?ZÓɼ~ìõx¯çÌ€´2Q&ñ;˜BÊ*æ2' .&ä…k ¼|2žþŠZN3¯ŸáYšÉ³S߆÷7Æóu>HÑecÙZ¡î«n ìôS™ÿw#‰aéì¤|“©>Ì$xDcòh*üª’^óÁÇÇuIZ¯«cZy2›¼q,ì½ö›×L  ZXº¤ V÷.£û–Lœy!›nVö.k}¸óñòRÜ©Þy„#ÊNðq^µ5òa‚c ؉£†Ì0¤‰ZΙi½õ5¡k²}Táž1³ÊS‚µðË£jߘ°¡I™pfiºçáZ®–ÞžU’–Ô÷í tG€™ NÇîYP²ü&tWFPïÚœQ†cU©õ·}ôºÙz¿“ œ¯@®éË-.̽s`$ÿoxìÄmþÆrca +ì¥ûQ`aô°?¾;—ôi­ƒ-~î†-OûðܦÅLí¥ $ÊY²ùù¾ôôz+\ÅÄœ.=˦nÇûxÖ¶Öì þõ÷ó”+ùzºæ´omטÉê÷÷²Y™¹p´ |³cÁi0ž.=Nì›Q~m"y“´”¿{4bxµ+èÎ{O¤<Á”U¢ÔÐí vº¯¿ìÝ¡—üÇÕØåœ°“ ºzêÁéºr̨ó¾ÆÓð¬VÜ$QÆ;åógL§ÎÅ`x~’,¤¨d»fx6Hœç픘›΢ª{ßcèÞÿ§k[83û]d˼W|ñ‡6˜5^ŽœoÝûÄñI+w|ç>2m§/¯Ìâ¬l½µ†„>@ïpîþžŠÊKÁöÃ6nJøjz¸Sã×ÿæ¼gB­®(‘–™‡È^Ñsèî/Bßoÿ‡ðçÜvâøÿ…pÏńΓoçÊÚ²Ñ(nÜ›” Î £±Î)â¾àÞj¿FŸ˜éÉX5q ÆÓ\´æÏtk^XûžgÔñŒß©¿rgW£Ç½+p§"7/T¢Z×1ÁÈŸØ™Ç8v«ÿ= œÛ'#‹A}Ñ'Ðt6ÁÿôÚ ~Ź P{WÍéÇà;—I`ÜbÈsÒ •F1²Gý<ýV¢3Èà–¼§WbPqc[3)‚Ý)³fM%!Ì;b;Ð/Â6NÒ¥•5øXEýïØOÿÚ‹:m`”¾–›±ä¿ý¤ÚDµâoûp"™cÙ,“vC£š µÝƒ¼Èud5aáÕK¨ ìH/Mïî_¶z¯–¨Ð £AbG3ît첓ú•Žó¹´%h!ͯ^Çh‹$[Xr›%ŸNï¾TfNš²’épÃ;ƒÌ¿ô ¼«µ˜Kú&5Âÿ®§Ãµ!>Ÿ¿r¯¶‘åÑýäÃLmê¹ÌŸbÇC òÚ£ŸèØÁ‚@EºXÓŸñ^e¿Ö¬åV>”¤±ñ;+ÙÂ|sŠVñ±~4“jÄ%óähý8qrÏOÒ$,v朗¥ÔöQ1ù}~Ñäù›ð,i.krÓ·‰"sJr‰RŽÌ ÍgßßJÑîÅwIÕFU¬žs y©ðCì)­7¬š¼˜$r oŸ‚ç$¸‹V~l‡Ò&°¼î›¿±—îGx­Séè0=~A´¡ÝºÞ 6¿IÀí'à’áÿÏV1FÕ1OÑH÷gM¢°Ç|ÇŸ‹Ø6åhXòç *”`;Ëàzk¡ÒÌÙž*g_Õ™~¢¼cù¾RÚÖW‰èܼDÛŸºà÷ùø°)½#«HÂN;ιç¶Õ.ôÞ‘*(©£‹Ç$1œ“ÎÝøºž~84› äz£Ì½:¦v9^mcUñ}ïÅn{ÿ­ö»‡´îúD<ãaø¢…Üi«p°ÛÆÌ'²‰«\Á»£®=5d2|’p_J4uqŠûþœG¡ð¤[ŸîXwRÚâÔÎ1?Î"@tïÿ~ÿ0ɶå; Ì÷\Ï«Âáv×sXè :»Ù°Õïò¶• 32f"0qÇ+²Ðv ÷û¨7 }‰ìtÞÝÓ‹«ìm˜I±>›âÐáXðzØŽöƒ-k Q`›-±3Áqúñh¶Ô•u óüÞGÜÜ©b°qTÖ‡‹‘÷¾&ÌåE%<+ě볹rk¸"5YÈ^)»-æÁb¿\ •Þ?À¬¤'à^àŒ®ám<ãô먫(‡þŒ™{@Uƒ˜uá<÷=$žIdÚpÃcç³ïKù/ƒ=†kôAŒ·{Î-\-@­gšÁÛJ#ýA›.ÃkutÙx… {V­e' ©VþLpÉ]“é»×ñlÀW¦7ŽÇ“^Ü”ûÎÑ´UˆfÜ@±qnt§¿4ê|iDªÃ?¾Ëèå½xËé¼Ûªƒa›æàzñ{Ãç,w4xÀN?=>ÿºÁ§A&u+ÆYÅk52lãªñ4ßÒó÷¡Ð¹P§ÛAç…iDS&m’a‘Içe-—qÎ`œFY¢ ke/Á´mîŠÙ@»[%Ù¿#R ‡u™òúøÝ¹ Ôµ‘Â¥ö÷&5£Ø\9nþÁºÛ2Ce¢¨ÌÔGD.®×Åæ’«cëPVÂ?KíGi7ÐlI†O7'cy‡+ü™Œ>¶ŠhR¶ïEo y6·øŠ^S¨gŒõȘ'¬]öˆëÊVÇ'ó“I€¡ {oÌvžxD$7·àœ$Ü<ûD.áÿGþ©ÿõW „Z3‡Âálón¾l¼;" ²m-|c17Ú2ÿ,o§»‰VáüjO:;ä( 6|7ލ²F¥Z°Ag*%oL'›ÄpÅ’Ÿ¹†ëkPx¶óó­ât¤Ž’€Àóœó/ðºqídaúá3pÐÊ=b¦s£dXÆAfÍ ™?¹Oºøí$»üx­@ÚïÏ-]­‡CšajßØÓ˜—{æbHl ÷ñÂXvw¹ܘ+Árc<3d™UšÃY»q(HŸ«Tq )¶àWU ÏŽ>€Õ¯ËÉv %æ/¡ÉÖèÁß;Ä’§u"HïžÁ⃻x"9êÜ‚CEìÆœt:è~’J¾¡MßqKÔhš¯SÍÍ|cÏrzvÓ7?ác–h4Z±F9|òf;Tú£öœfüzñ¾ŽŸ€Ž{·CDž_ç»-s§Q B¦Q^ï™þ …ûÂx‹oŸ$»ÞÃÓµoá±Пù*¸àõs¼†?ÈÀ¥"®æ[?nÿûb÷yÒÍo¾a÷óið;2‚ÊO£ÅAúÍóôÅ=@õô:ÔRHÁœ|E6ê®:ÛÒ·½AäÅÉTwnèÚÒŽï*0é˜ \ç¸ûÇ$!i_®2ŸŠ´‚OwÜ©`“ò1pÅxè»4‹½úüŠìù k½ØýsG7~-0릆w£á=¦Ñ˜mÛé¿P%öV;æê}ிr§¯²£ï1Qºú·*51£ÿõ!‹´Z‚»ßfjX™…­oñÊÂ7Ò{£JhêÜLzà˜ ùYULc>¬ ï—ÕÒ{qg Ä+ñå&”víƒÔˆ±Ì6F—µf­™ðcÉi6½u2}#>ÈÞ×Õ3ç-rÔ(_„CÝêì“È~¶àÕ>ß³¸²»ébïϸK³“³ç/;ß 9b¿Hwñ,®Âdbb97KÚ‚šµÐ5yS€HfÂ!åɸe ?ïí ©'‹!,ç9xèéR3ÓùtÁ¥iøÂe=2¹ëVÜÅ åÙlåëé¨6ý:dDàøÏq䘛4[ÒΪ:Ï¢ªðRf%Í Ö¢­èð}¡¢!‡)<Ž›ú°—ýksV,^CS$ô0iü2h5{˵ Üc‹=Öâ«ããxQ&‡ÙšÎ¤«¥hþX-fý¸’jî¹Ãµ*ÜÅcq™W×/Ü&ÒCfd¿À×kÑû=|LS`ßý\Œ4\Åt—ΡGŒô¿‰u~¹Õç¹OZtpË*vû;š9ÊÓ.×±Ø]’—µ! t‚¨×vmÏ,;õ —<±d[7¹Ò„åŒ>uÀ+!²tËë!ؼÎÑ£S&¬¦èÝ粪sÓ&àK)&Þ/ÇÒ6P½¢ÑtéºìêœñÌëÆHJüŠÏlþ‘Ò˜G×htæ†ßskïgsï4“´«žŽÞ'º 4Øòc"ác¿ }pï|^8›¾³ÍÀZ­%löW9–ø1™Ê¨J°é7cQ7jëu?LæîcD{ÂÍj.)èj놻%`Vò:h)®Á_Îõ ¯á\q꺂ãŒg²Ç:Wðñ– Ð=‹ÁÀù:œh%Ä îÄâøp_uOxøßúÊHÔOáUµ¤×Aˆ¦>?ABÇ®dYÍ;Ù89cnþ’Bššž‚Û— ”aOµŸqò£G¡Ôè4\g~¿£.Ó5±À˯Ÿ‚Ñ .…ðÅ2ÀÖVƒµuçÓø½œ|í®uµB¾Y <>y•lIŸ‡Ä‘½ÙPࢠ-_;¸sÆoñD™!4 ;1MmG¼ãÆ]ë_A-ä÷²åG¡ÜÞÿ¼§göˆ`Wÿ/nù·VØ«>‚ÿÃMÙ0méº P˜^Y±˜ÌâÈÖŒñ4̤”Nh?É/êÔ¤ž7.àF¯‡(•åÆbSÙ_*œA'ëš²ÖòQ4t”Ó˜¾w³a&Ó,LwЂïítýÜ#Xºt.fø$ÒSì¯.Æ&[G¸¯&ÍÆÛ¦3%?LOËâ¶[qp{4UР·Lìéͼlðé¹ÃI7(áè-ZÌ7µœ ¦ùÓt½˜³S‘~t5J ©X¸$5ß«L âéG=T¨ˆ§¡è¸=6š¾=œŒFmmx¶ „Nûl ×Îãõ-9ìªñ7ê½ìÏþÌ1 s˜ $?*ªU°*s'=™²—:=÷„YõYø’šTlƒNVÎtß Hñu§QS®Ñ·®Q|Gíï¸AY–N^z…ö–äÑoë5Ù³ ý´3[Èé~ñtëù‹o­ÃèÉ™:t[è ÞIhÅø£ùìA¦Æõ ¼ƒ†ÑôTÿ\» iîºB/×€3ßAé¼ÓT|p*Ý¿.žMó¤ûF%s=ξtÁ½¥tøªtý0¯ø[.LÂEOm˜a¶3œùHŸ?šÀæêŒa.ΦôˆiÁÈü_½±0×–,I i:j”÷ ¾î\Ö°.rø£Yo€“\~ ŠŽœ Ýè*Hb º`}Xƒï,eJcn¼ålÎXЬ‰ªøÁ"³Ÿ‚È-¶R`3©\|%Uºfi äżÉÕeäÕ²Ed¢ª,g¼f=”–¾Ew+ º*«fÖ ÀžÂB2| ^[ÿŒ¦vÒ •òðX’8 H{F'\qÕ[ð·òbr=éd²q>ýÞp;ŽCË-øhö!žá($ëZe™êî\òàMu~/Îxƒ÷ G!5%FÓþgê°Ôè4ÿm@êˆþ_ý[®ÓúõÿˆúÞ÷ÿhhoÒžšŠhh½Îu£–dÏ"!IÈí­©´K¥©´^çº3RHF…BˆP¶_ïïãñëóGÿœs^÷9Ϯ뾞Ïç9×¹h½©ÀüØ}_ñŽ/½i"OôwûЇ1«0vŽ4äïc%ÖB¦` s®â#¬HÎE¥« ¹KnoÌRX9-„Ø%Ü`u;WâØò=¸ýT7®Ì¤ÌæÃ xçðМ™Ñ`¾æ³k¶%ÍÞ5ÆÉ²&Â'WcK“ ,x­O/ïD>åùä‚7ìÖÆ ÄÕ¿WÚ¥¢?Í÷µ×j)£+†Kt»°þ³ ¹þüÞmogßo8K-õ ˜Aч܊Êd™  »®ϵTH‘s±Î…Döóþ;àÙš«ò ˜õmË Ô8u"þÂ5{PeGs{€‡5×6¼tÃø™¯ðøÂ… ÷ `££%|>â>]SH•»®ä½¾1ä{nž/bâO]׋pÞo h‹¼ŠrÌÐXø„íý)N–’ÅU×~ƒÃœ/á9)|ô`þ!¦?¿›±Úõ jÖ\`¥ì”Ȧ*!8¸@‰/t£7¿5Û`¹63ÈÏÛ:ÌV³µ 5»˜'{Ú|*ð¦‡.ÈlÌÆ'FIÈÿq NvöÄÏœØqÌ׉%‚JÚ+4ªmâÁÙ/à•²]œIÄ àJ„wð¯Ì~—͸tÀ™ÔgĵÔþ‹×ß羫$ëµSÑï§0­ïÁ»;®r¿Ú•£Ö< ú9>‹Sõþ4ú/ü‚Ê ÑáÈ\¬¾“‚§ßÿäŒw'7§v³Å †0oR ZK.…°òQ6Wƒû¶ýÄGâ$ó~úb):â6uâ©×÷±«6¹ödÒ)„cÒæt! >%<Ñ3¨ÎþÛ(|y -3“!aÜ™´Êú;h‚†Pûìvݦ7Œdøbj§`Š:ÛÂà¦|b'¨Eö}üù<˜}úŸeË‘>U^šü­˜”óŒs­d.•-­›Ðÿ_ų ËGé? `oîM/û7ÀæRE¬9~ lÛŠmÎÜ)¦}Ñ{!#Y–rÛH‡g0GV›°máäì~Ò»®‘½ö$Œn²§náÁDç;Ùùœ§“†¿…É.» hL:ùÎArMÞ™NÚñ\ä“N"wßàLQ?3È T _EŸØð‘ÒPz+l7q"B?B oÛuÚ%Còõwk m6J÷ü‰­Ã[#˜3?‡nøñò8¦Ñnã Ä»éD4Z ±¢m\›;‘…é¦<$öx´;N#/«›«Bo+°ˆÞAÕ„­»&äÁ¬x¸&_ÿù&êß±S<ôÄUeè}ò†Õ9¤Ç,2ŸM/s÷ƒìÏl®»æØ?—î©~Ϙmãú‡°èÐmØ¿»®ÓeÃVS¶€~¸|”ìÜðÇ"KØï\„7´´ˆ‡| Ý¥xd±¼.ª“¸æ<!"O4cu±;ª¢ó-™ÖÙQh7ׄ4nýÂzÝ;ÍÍuÝD¬Öú`Mâ€ü0ûcæø') ¯åYž÷÷A­'¢Ž/µ,ªÀSÝH ‡zKj±?}ôàl‹!½tZ }ÖOæÚß|ŒÕy`XSõO–ÓWû/²öƒS©ûõÄKäóeÆŠ þO84|« ‰ú`¶ÞwS"þ 2º¡üPŽÚváØhºrq!h”ã¯çàUr›ùR±ý\Én>^,HÓ*¨žîFJ¾L–Ƴj·Î³›æo!?[•™«CÚTd­µp'hñþÞ—"9¸'à +}:‘ʦdŽàBæöï†\öÅã_h嵂–ø»%È×I!LÁ±K(~¸š»æÂ!´ëÀß®r¨­ãKÕnéÀ@ÜELüGŸÌ §QÞkié'º<®}WuÁñÑNfoÈ?\²èûIe>d>Q¤E»"¿×þO‘¿ü¤À·X–ìÝ9›|?Ê2 ­X9*O¦´‹ÐPžÀÿî-o‹°îâ (JrÅ”éFë³ÀJ|Ä€Ä °A©ß³á§ÇZxy³§‚ü6l9V‹úó&ò?^däÏqÆHÞ?§kæÌì&0ƒÍ‡ðñÚ³ZM”.ÙÃnáA¹Îô͆LP{*¨œæéÈÝmªÄõÝ6ÆB<6Ü4!x+AyòÉÐz(ü\5ˆþ8õù³xð_d8ˆø$0ò|SÑQ¶|Mw’½G²È ñVî–;JtñI¬Ó($F ¨ïßJ¿¹}†Ñî‹\¯û¢äGs6Þ6÷ƒC²¼tõ¼H¦æÕlšœzòRT¦Îo——²„ãC*Ý‘S’¨YÎçèOOE ®–%m·EðÊ@+xÜzÅdý}ƒ›šþ×ÿ«fÀ9óö—ÿï?Öü‚6ô]ÇÀG·`®lLU'Œõ¶ ±n7ƒÊLd¹*X…?a/Oö……˳_gSs} ªR G.hÀ½l°ÈPÃiQmxÝ/ ûwvsGVOÍΫPÁ›ÎH»VBb@ V+÷plߨ1ÉOÖ‘I‡Ò°ôîB…摚sš$ûR,vª‹’÷ãœóOø.#‰3íO ÀºéÕc½+ÐâˆóbVøirΉ<„×.òÄÜT‹f¬Ä`A-:ÇØÊæ¡¼§œu(¸‹?Cû'é.Ï<¦éžý‡ç½&âŸ&x»œ!*ïŽÆö ûù"n-U´ѷðÎxÉ‘úż£¸îÁ»%š—ùôÉÅ.žÁ MнP•¶öž•=ͨô/Šå5HyF,º¯ØÆzZ®ÇN³)dã³ RÝ,Gª¯kžG¼´Ì§­Î}8~N\öºmïÜ€V¬âöhŒ¯§Õ¤Cd`hR„{ˆp_ßè…‡ÊdU4^,°€ ]^`·V ›àÿ‘,t-g(·£Êò ËÝbóÒâ'³/d7˜ÉWÁÌÕlÑ mÊÑld%ϵY­‰#vËðÕ`è³é°±p'XL„’OÇá‡æ+¸e²•ÑÍþ”,[€~³µèÁgÌ*á|ìZ’Àíüä6_Xlz”©#ð‚÷=ß­dͽ¦ÑŠèû~áúæ@æ£åèyå7|P˜Éö~ÚƒA·õ!%YðýEs¾AH“Çž§þxžë€ú?ÑìÛι–0ÌK6„ã‹ÇþÃÒðJ!’•MmÄt¥n|_Æ¡{÷¶°|m°Î6bÿÍq…Lî²E4pû'–ßAƒòÜÄL=(E{޳ì&«191“„Ìí"Ÿú"˜ìë ¤¼ù*›X$‰ÕûpÓ2]l.©YÎkæTÓjÒ»äLçç‰ݫY}Ñý e!DŽŠ4£A¬$ý{æ(ê(üÆÍÚÑLMcú·M¦­ŽZ ‰ÀQJw§Ó½ºÉãyØ#+ß`Á«HCop2ÔÖ})×Pâ©"šð”‚øÀ:\½§§ŸÖ&s\XÂxo%qÏ’ÙVý󸫶uCðV’) ™’Éw ‘–#ÓýôhWÑt*ý9dBÿj=xŸ˜Ù°c·ÿÞDä“Ñc9˜‘- ̇² qÛbA»™ÓáÒ¥¥`Z—Ž·Ryê%˜A½eƒqÝ/¢ýïÃÙÛ‹"wãpáHˆˆ%Ï“àå.!ʯ}7ô(¿{,›·^ ¤±³#§ÑÄhrbͱ¹Aü{mäþ¨’4–büî>æ°í¿&Ðö‰g*–‰Q1û.ó#¬0%—Õ=Ø0ñjòõò,â{÷7JøCðË%ÄOÌ’j(³ù¼a{¯5UÞ)J3 ňmÈ\;õ/Z›[Ÿ³ýõ/z¾Ód®Šã¯œ“? ¿@Úýw˜ö¸ wúÄ}rˆ´÷-«zæ;ÿû†uÎþŠqýó±qD ï4¶@;:³Ç q¶Žî¯ñ qᇠW«œ‘_e‹“ÃÓÀ§=ž^ó†U.èu*ž660Ë~´pl¹:t‘d1îS4 û|Íhä…8x¾7ž9 w+ìÿ×üÌ=…BàÃýÇ4ØÚÿ#LîC5’t:‹“vÐö/ñ…9x@ì1Ú¹/Ææà•DÉË­Ë»‚ñãÞiìã4p[‘ˆ“žTsÿùCyw¹‰ü/÷›†þ{Û¸•÷K™5?…a‘Ë&Ωõõ¬ˆÖ<ª§=øoe·Ùʓ؅Š:ÄKÛï㫪ɨ,5îksÿ²³U$éS Ðþ¢^Çÿy{ÝÏ>sF¯µ/°îÜxÿ±‡ªÅލž• ×FtÁBÒ¦„Á¢È™TÝìL›”„‘wÂQ-n-­æOÇ}×6âÎ)’$eånÆ8Ç’£[š¿úoⲌÃpw<ï“ÝÒ˜‹äék)rýg¬¶ÿÙÚ²àpu&{LÞ–¼}ÔÄòµîy´\áÈœüÈt$©"ÏÞa–äÇ’g0áeå„þ¯ NÀÅüfðÈcÖ¯ûÙ—¡îñ[\ÑB!èƒèš°×N«Ð¥ƒßAóAîå;ËóÏÁeÑzìÎc¾Þ9Êtn¤–ßàBžJ\iË4îøŸOiAðJ Ø,‘ ïÝÀõ³itÑSðòÛKÿiÁG<Ø%ƘI–á=ÏL¬RÏÄ_Ý 0åý_pÊ ]Ÿì‰ÿž‹ añ’Õ}èÌäÊáŸïÄ}„ÏloÀqß.\ájÌíšâ@Ï*~a”÷v²Š×$àäÁŸlWúwvÉÔd0ŠUÄíjìÚŸƒÖ:XSÕ¥`»VdbþGp™M|!˜sùòUa¹q41þÑÈÊ»gq§þø‰®<ÙcõF/AËÔ„´Ba~ò× ؾ˜DÌú#îD),==/ï[‹áÞØtÚ}ò$ùU§{Ó¦ýödåCW+êé”ã‹àÃ* :ÿîv\<œ„â;vrýöÉÐk+VRw“غÐcjS¨i­ ]’ÙÆÑpjïæƒ?–à¹k loþb´—’Ã@ÛU†›55±B­–›hD1ˆ ]áܯmxüÞˆßÁ!Ii§ð§¢1Ñq®`%§ñ¡ÚçRðlšÀïÑ3Å}æÄì©dFÂ^lÌ»Ïuwl…£jElÐ;I"}alñÚÑíº°®-øgé"œøÀÌšòÕ`ŒU/Ã/—  ÈZw¼?ÑÏæãÛ¤P(mÿ€ÇÛÙ§«}Qò”â¡×]¿r–¶3«ÞƒãØ·]¯øBzPÒ#Ò†MŸÎn}ÌÇÔ†·ÁÏ´œiŽÃ’$$£ÿ‰»€R —3ÓÝ—ë°h9gæàkØÕëU¾qú¸þ÷C|~ú ›åbL–ª„ïZ_¡GmÜ;Ñ:=‚³y²¾U²‚…~€OÉ<¸¿ïýDý³³B翱` ¤gs§Ö^ß&/¶#3V…Aé^2zG‰=½ÛgTùû­DääLV5q&Õå_ BçAi® åò3&þ&¤ù~%¾­$P³ƒ½ç±c”К#÷Õ<²²F’nÈsÇoŸ@ùPÇÅÜ÷ÞïÇÈÍö娒_НŸÅÕ!‹áup鉌Žq(º †»ÒÁ{W.D¾*ãFð.÷fº(܇Sgg³qÛçàåÄ& ¾µšâý—øt4 믽±+ê›Y§K_Š0 '†ñîÝgìòƒ.÷¿õGJØ« `†˜!›-L/uÇÓgÌa—Ýa"rh¬äƒî.-*¨³„¶3Žy]œGõØ"»­L´ýM·TPèoßed¡t"»¦3ó']Œ¼{ ¯ îÀ¼4êGÝù½¡æ$r!–‡'39n–ŒŒuÙs_·æ¥ãÏgX|2íu'ƒ¿¾¤ùâ…µ‡§çRœ·LuŠÿ-§<éoæ(Êt@mm¶{ÚazÛ$âïæi-1I€tÿÎçüY¿O¯¾ ZøS¥ý©â_È©3%sì-ˆ©”-yyà4xOžÈÿ[Aàå&‡¡K‚Gþ-¦÷îц™vAYBÚ˜}¸hõf%õÊg_ì—¤}“N€?½ éÛêÛu0ìf"^¼à“44Ùμ½ðãÕiìå>€æÀV¦åO&3á¯V€â°g°dA6\:6ÄòÙóCÛ)’2O‚^a—“uëº1äH4ß °µ||qˆÇŽþ«°mG"žYþ½Õ­1[.QáU¤R£mÞÜHåž4NeãEö€Ãa}Ør¸ îÉu[1Å}ÓñZ[ ŒN'[ƒŽ5X¯GöfaT[6üR9+‹ÿ÷ý·ô„<æÐÉBFÙ¬þA…`û^†”ˆ7²Ód`ÖÁÝ4(M\C@¸í~(YÔŽ×Á~Ú'Æ¢’Ã}ÂP.# ¥†ñÚ’&VÖX—þ»¾‹.’È‹c"EåѵµOZ ’9×òÑë²”Þ³wª¿º' tó;¶Ý)g§ó›ÍÆÕìÙO‡¨ätG(Úâ‚+³ ˆb!¾PÜJ­üÉ|©D4!Š”Œ{ì  êµÝ‚_Àœ’œ‹«ŠP°r{ê¼4ùTà›OVẠq²ÿÌ>lkªcd…BUÊs.#žHœ;7‘ÿ#w½QyÀ˜éÓ‰„§›Ýp†N4ËÃÿWl­I®õŠÔ¤þËPèáIxÛû¶äêq§Å|G“÷9M‰i$tàÛö³¬ÀoHJºƒ)Ÿáûqdó´Þ|7&ûçìcM5á¨T>DXï@õzxá›Ä çía3ÞUbõ¦Ód^ë'ˆ«Pƒu-·Ù’KÕ?¥`©c7Ì1¢j!—ñÜ^Y’úùT«ÖHú ”l$ëB—ÿÛqL–ïüx/¯Üw…ï¾7¡àå}🿛ŽÈœ¡þ×)Hð—Aå_^zSÌ sÁóÓy‰¦|4±¸%Fo©ÜÄ• #ÜÏ|–p¶ú0SsÏ”êä¬ÂµßïBNâVƧƔò8~WÕ3*&æÊn^ºNäXÊ.#+ ^B‡™pâZ2ÀP Ì©P#—ÍøÉuÙd|S#LoUÝÀ {_Z‚ììD®áž¯p¾  lñÇÁ¯PЗD{|ǷΧ©é­ö¾Ï3æ™F “¤ÐËQ°çì™õl¼î@gqI,5£Å›Ø ÕAði+ÅõFŠšlÉ›{øß5ø—ž¦üç;ðŠ´7±’™ES <ýÉ+?¸7³Ž–R¶Í · ~MÛHžÌ3çjy¬Â+-²t}ZN’&}XÛ½Š¬\Ÿ Ý–ú «‰ýqúA¸è7u®¸US§ÒTÒÙ²Àec)³#Ój'l±|´b,˜Ö‹0×+œQ‹ëçøˆ¹z )Y»žzÖo'&æïÙ®žÐ¶æ;õ¦ ®e²¿n¤‚9g¹ùËN“ö’ç |ó>-YÀn·v¥F* ¤Hö Ú닚NÇSDöZ£0«@ŸÈçŽçx•<äG¯œêÅöwvƒžS4§gi&3'AƒÜÚ,A™î¿ì–¢&ìN¡WÓ ×1… <´„Õqä¸F1™×éD¿àÅÚ—h×½ #­£ #ì‹ÿ[ÿ6Cõ+Ç“ï«kSŒŸWDƒÕ¡Bg%ˆ¾®,9.YÌðd~`VrBþ+ʤç—=©§¿ ÿ£«U‘[l`(í=³IS æãÇÇEX« IÇÊ13Y›Ÿæ~eç߃»Z’Tf¬~'HÒ†GÙÆÚ`x†—škBÊ8㎧ZÔ{JN’XN:¨—ç4RF—Ó?×›¸o”˜f›LÈxž Ù7,`zK7³héR¼]µ„¤–—"ÉzÀvŠä#3]“Ô 6°99%XÐoÍH ßÁòÈ‹`«4˜n•§}6„úþÑ™Àæ¼6³Ì»†ëÞŒz÷b¡ÎV™u6]Ëu3»,ÜɧßÀøÚfõþ¿`Ûñ ˧BfÞ»ÁtiÃH<;ôê"JL§óÁ„ŒªùÂêNEè•Ì€™G‚ðXø)î­Ël®/P÷þY4ìK ºÜ’¥ßÌÂM§j!ìØ²læwÔÿòdJõ“ Q?q”Ù݃òŒí~0SÊ%É×:ÀÝר+3Éœr16qÎ3Ô­ØH 뚸 /5ð¢Ñpõ¸)³…©uAm÷Gpñá ÆPm[[ŸÞüìág{QŽ8j5‘ÿb;ºa£J&/Õg(X«Á‡Xë}lµ†)(½\ óÒFP¥ã W*ÃË;È>«é¬HÎ7H›ôÝ¿kÐÞá̯²Bú§2TÈRZ+uK“qÍ•5ÄàýKtdãSšàúÔØø´ˆ½ö= ÞÚÀÌó}»ÆŠ tÕ‚ù¨1©~ö ‘ÉŸ5`ï’|| rôÛ2%”ü5Žumbr¶ÆPõWEø[È•ýð" ¢dpášH\Õ× oVî…2·j¦òf‚ÿ|§yI„1ÊÑC©—šT«ûô¢0)¨HDž*)lȽ “§Ã‰áßœ´š*øÇ5§{Á¬ò}¸9õ4¬ªsÃä?ÇÈÓ5ò4`9?•T£χ…û%Ȇòb9¿ëo%a±õxîòÍqŽª†Ý}op‰9{n5QLRia ÷ÌdÖ­¿JÕÎûHүЈƵ§¡×Õ ÊžŸ wBïúKÔÇÀŠÎ[¨8i6þHb}ïGÃhN5{ëì6:?ÿ-v?“ê@D̰Mv˜(yé&H>WØ“ïvg±yØ‚Îz*ñ_±}{ï~üýþÚ÷1íëçÞ0{V5/ó ~G~²7?ÆêNRñ¥ŒùTš ·HÐ(ßd°[ }oEïÎ_ønÉU¬1~ü4ùæDóö“OYŸ˜wãºö¸V3¶m >w^°SŸKP?‰||ýÜs-¨p¥òËýÃÉ6e&ýðÕ\–ŒkHéç;iu±*5¿rªJa™‰3£hŒÿªÎ1Oô3!òö$x5?]4(Šç±Ycvœ–€íµ’P2Ú MãµlšÍG¬ribŽ^ìÇ¡Ìå ZRqQªË;>_ݰ…£Ýü.Ík÷¿×Ñ(t¥¢ËÙ¶c㺞÷1¶õ›&Þ©´SÚšFvlbt?„óäQ° ¡ÅS†˜™[ª°lÛpsqa=´ô˜ç¥^d×¢ÕdS°$)å˜S—%Aä”F1<èEËÎש?¸e æÜvZ2¿Žë¾ÇÊQÚúd^½Lp)PŒÙBèLEzäyÓbeN B݈¯½™3Ý7³¥§ù¹aßÜ ÿ%½ñ9ßÿ’@òÞJ2W]‘&.Dïæ-´uÁ&FË›‹¬#Ï?ܵ[EØ1äM› Oèß?;c°.¦Œžò¶k :§ŠÅè˯7(Ï¥ÄRA™/:›ü(‰£¡YJ0êöËRâæÏ²h¹ Gz‘÷Âö ÏÅogQ/‹,frŽ÷õ%’7).˜k0·£åhªwÕVs‡ÅAi’õá,IÛùJ.y2½eÜ’°˜ÇFïßHóÔè•w¢ä™Ý0”]ÀÇzûißpÄWÜnÐ̹]1XÛgÛ9{ìÄ)º:ÂÈóxH;¦ÎŒÿ¡à'Mò6þḗ™M­;8°hë¸fù'J‡\ÄÈ¿eÙøë®BPêm˜™ Oç\à£)¸ð?ÑØÀÀÈè5Ìú4 îöš¡ýt ÿÅ7­yÂî_|T7Ýf”_m"׿†ÀÔé¡ÿU#À»™å»B2ðÓUz]Ø&Û€SÛÊ™ârQb(z _w€ÏvÆ{=e>=R¡šÏd™ü-ï0iº¦ïî„”ÞhfšÄ ¸˜‹Ë7ÜÃáy|`´J‹»n«'ܱ‹dJ×ðà€é<:½Ó€) Æ|—Ø×VFBdÚM?áí‡`gÚc¨]u…õëñɲëÊ*ÌŠŠÄ’çŬƲɴU1cÜ©Mðß…÷™¬ ž _;‹Þ’´£—]a[¸)RVÈT°÷0ýÑH4Fa¶­¬7Ų×QHÁ=CŸâCoR‘ß S>~G¨} ¹g ÁÖp²õÆÁ ü>ï4áØÜ‹ ¸î­">ðËÃ5S>£‡Ô1R6v‹¼ZÔzê<¸¤î¹kÍOÆ(q:Š™¯vÀ©#¼ÔX.ô8¹‘쪨 C•ü`zÜh+O='Nÿ-vá8å±¹JÎÍ'þëg¢gÙy|!+‚¼éëóXWvWëÐËwãè%Y’eüÎ0¸rn qðAÿbGˆ¹Â;‡ˆâ£Ë\qz[þ†Ò‡ízôÈßNÝu”C×í÷á¤ÔÅŒ €Çlâüõ8œ=—YÕq‘kèOÙ­w!õè2 Éz 6—‚'ððúÐóæÑɆdìù%ÆäGåß}–q[Wƒ§ªpøí%´T›Mî]L66ëBá‹ÍôÙì,ômÙ@¥ÆŒá‹àyîïòk¬ë¥ËìÒ/öh”4½ÐÈ>1 &N÷Üi†õV"6@ØoA™OGhØÐ4òªÍ‘¼I§¡ßcHì‡$¹O/è¤Òñs¢Ÿé\òù¸6qæ ž'ʱþ²y'¾Žv·ºÒ¾§«Á´y >/€õg•h»‚ûRØ“Þ?¥F¿¤Ö¾<²Æïg^çyxxt*1ž¿Ü;oCíþÅ(Õ¾fm¤&¼ªd{_âÄü—>t›ðÔ‚œ»Aœ§®÷¹‡ŽT"ñ.‘v ¦Š›ó°Ë½ŸS£ÖGæv™ÞCøúc®qQGðÛ¶ógíe.¨ ƒ›÷O19’IàÜe@ çkÒë2ìå#ôÞ¯G5ÎÅû)ÍLªY1N}kWª…µ,—"“*Ä?»‚.uìDûàQðM˜ËÌá¡–O˜auz±$„¨~²£Uuñ°ÛXª’‡ðS ++eIȾC •^…ŠP<[›JnŸD5 úìâÜîѦ×ÎÆÀ-«$HÓoƒ ‚ï 5o©T 5}7&ôßÜâ(vR\ôFñ(ÍVÌ7~â÷AþÆ.¡KŒŸDуñè˜eN=M•ˆéÞ2¸j m ÿ%ä ÇIÐh5T?¨Aß÷g9éw19O$]‹\˜uÆ:Õá `,2%UL&O&æá³Y¸Ú¬Æ£sE5iÍá— ˜ƒG~>EÖA‘þ;ÏG u=¹Ïo©Ïõۤë3¼5¿é=‡Ã‘KñíK;¢ª е–Xƒ3Q6¸¿›ŒrN3ÅÍ›%‰“ª1>)'ÛË’}m†D3tIŸ*ŽžøOÜÿÔ³¯dWm\Õ.’¬èa¢ý¶ˆ /'› {ÐE¤¼ äƒT±á!«ô#Ênؤ=Ó1¨{Ç‹‡}ö£ÿeªÕ~޳wE+îoè…ÍçgB'vÒw‡t ÇQoÖP®ÇVÕ4ËQ$ æO0=gÙCº µì:^îéÁ}(G]Ñ󕨚ú4ù¥„>&î$âŒìZ¦›Vn‡Ñ9>D§é*¶oéÍ–$²Í åNdÓ@ñP¶+VjNÒ}Þqìbn*=é¬@_ Ï€‚?´-rµÎ‰Â™ýgÑ>Ý“dßü_ÿ/sü%H®Ú€+$zØç{N⢥ghê}fñedκ^òhW9–`çA)Â1‹ E<$N°„=ܼ€±~¬G]d¨ÙþXZøjuSiõ= "-­EoehЃõÞÌŠÍCÌ.@Ø2ÌS§]…ƒb‡9u‘¬$ÿ]|ê_g:ð—h™?dž™çÙeÒâÄû×l*Ya G zu8µ·'TR¨ŒÔêžÙE[¨BÓzz;îX¼Íüœ‘O^õŰc[Gà€è7t©œIë›ýÉãµsKŽxià*ŸëÌ‚u:KH“º Ù'^£‰ù|å>³ðÍz‹½ ‰ŒŒáväêغ´ƒÁ.†h5¹ÅŸÏñ‚þAìfW²‘2Ù(±ð4èô¨Bíé ²Š^½aG{vò }ź[¸[hf(—9ß§Câ¬DÉ6QfF Ò#‹JØ÷j÷Y%zF%†qwƒç&o!™iÿ€3Gw ¤ÖUÓÃÿ ë»>Ý:%oÕ˜“æݸÿÊ$"!bŒÞËÎ3‡Îò’ðgÆ4iåLÙüšÕØS¦í¡Çî2bÅ)I_Ew~ò!ÿR3Ñgß#„_“èX~ìŠÆ)üãªa=ñþßfåPœ±ý5ö¨M¦iœvÚyY*5Ã’*nù„%šùFè¼ÎHï9‡¹›ñ«H/f¶¿Ä €*˜î$¢¯šÐ×r3´H‡=¥Ãx]ø¶žÅÓÅ ´²g–Ô|‡§çÓ™È}÷Ytó>#Ø>o¯õÃKpó¨*ž8ø‘ÜÀ2Wù“¹kºÀéøî.Á3iïBëºØÜ«­bc¾ï Y5Á°kÓ_lœ•¨ïÂÈò.l-šFJÞçCÒ¤h2~´È­¥CÒ•Luž8þ­@¹[£qØ þ„5B÷™R¸ud3œxg#sJt"þ×gÛl¬Ueù¿n&/mãÊsô˜R1'<îâJnŒÝÃÃÁ嘣—ÃQª‡kǽ‚r ÞÓŒ@õSÌš$Ã,~ÇþÕ÷±; :ï†Û­v æûä¨ÖÀîÎwÍ0w‰©L¢þDެšÑ Mb¶´±(G¦‘­*\¶^À >¨ˆ“¯ò  +¬ûÚH—¦?ð| æ>ñ:vyÕUèá¿F óm9¼ùéÉ,ê}†-éL»lîDü÷•±;7¡PÌ&¼êÙÈš‹t`àh)*Óq ½_ xvêÀüöxU¶§¹Wüw SéºÛÏ2:ïdÈ2ZÀ,[qßûnÂ3ÀH?¹‰Ï¨‘ÐÊð`Þc”üv Ÿs莌í0/Ó„qœÜËþ žJ-ýFÏx Ð\Έ|šŽÊ§EIÈQ?ÒxcñÜ ƒÚb©`è0OÞD—ž_Í®p(Eì­˜-}†rnÌ'×ÇPÍ; ÜbÄqQµ%WqÃ!æò¸ÆÖÛ˜Œg6Žb®ì.CóÑ0–íD­Ð[6C¾«!.™½j¢þí›1Œ_+ç“â%x^}Lb½Ù­‘ëÙÍaKP·< ÛˆX«cuµ%]{t= ¿ÇK¸Ç6௳çqÉÉVðëÄý47/x7#ºo‡óöÙskRqËŒYt „•:¶¡C— Ù77gSÁå,èå#ë‚«ØØ²OøNõÊBHn÷sˆ¨ŸM>d× T© žmN—‘#"=䢣£®Ô‚§ueèÖr#œã¼¥ªÑçÓPòí>{Ë<F8ïïùR›ÈOÊ §ôat´–Š×Ñ•7ÒÈì¸Ï°>0‹y]:}‚ÿ ºüÙÇä ®\Hžmgâ:IêÜ©õчð:û8©®z»ÿØAó ²»!˜9­H®Îâ§ËﯣoOH³Œv®ënç½çqKñ)ø3îéºAø$ùôàŽiï fÄ$ q«¦]dzÁ™äó´§l]YÄÆÞd&ï-"ÑK¸† ^`6¨NDîn£uA“ØÕ‹pœJ¼nÌÁM¶U«ÔTíIÝAcî†k~A!ç.è­ëÀçË ï‹øfDVÚy‘áGÊ´^T ÿ-Ü@Æ2¦™gíP¹†Ð§OVÒÃêG&â¿AfŒýâÔ]c=dÉL©Óð{`5¹Õ ås8št]ù0˜æjˆSNš"õÝû—©k’•E·`±tòԔǶ6ÌýÑÆÄõ-Áĺ<ñ%—ˆS\˜ ¥~ý¢ãê‚îèæå‰o«-alã;³´¶¹wë*¬šCÀX=~¦ÜAÕ)¸-ëаGpT_ôu)û.%ž¾~aÄiSæBw«$aÚÌèš-ë`QR lñ¥°-Ææ¬‚Þ³KHÚÌ4&ì„Éúü'gX`D˜n=òžiî›MÇVÍ£®¼3›ØÄ£ïÖ„þ,É-³é © Ó%)HôÏ¥°w„Þö‰’)‡ËŠäß\Ú"Þ‡mkmŸMßÛKž}*ƒØ¾lƲð 3õò!²{xiß:Ÿýò^€†ÖZ“ YbÄÆ¶•ÈÒß­YÀ·ƒ˜d7²êŒ zÝ•§ÇT7Ò”‡=xc­ÊøÿZ“ZĨ2ÈÄ^ZQµeÆ9›çÜ>ö‘¹ÈÞ€¹¯9+Þ’…»Ã°N ÉÉdÁ­>Pçž"vœItÜ¡¡ÂSÌß-,ür9„6»TPfÙ ¢ $1O†ýåà¯>:-šÀ_xi7~ûd “ÍÇõ£ç¿xq© =ûN£ýt<ôȬ—]ˆƒ#\XÙ6‡¨ù_ƒŠi¢èTÔ•›ÐÀH Ó†f t¼*Ý7&EST_±†/g@qü3¸ägÇÌN3a /ÆàiÌŸ³FÑéœO%°÷¤8V~bšó-à¹@Çín0¿/›aÎôËœ÷þᱯGðv²*Ül~ÆN)¿ÊœÊÔ$«¦OÆéç´è<Ëz¨øÇO[ÕØù»³i¬‹#Öl`­™ ZIð¿rÅvÆE©Ì‚ƬéBŸI¨j!=‘ÿ’Áø£Y…ŒÞ'V¿ÇàéíP;ó=4í©dt ˆþ1œÒ® Ý:ÓÈÀÆdžT;^ÖH‚s¼lú–¹P¥û6wêcÖuöÚöýð«oÙó"ÛÐÚpµôÖ»«sf â^8;Š„¯„ž;@Cf&ÒR²-¦ÅÓ‹®%×ËžÔk]4Šo]„È€Ç ñDu³qf Gûòeô[¿[8ø;„$©WN>=? ÷û¤é9m¨R¨A%‘p|í§OãÒ3áÍ3)¼^1 ã#:ʦdà…ÎÇ9ϱbi"CÅo@¢¾&þš&KÆÇgÓmø3ØÆFúÓÛQ.ˆ°Ã¥×°9$–³×âòž™ôbí0ì°Âtƒ£ì ÿ)tè–$Ùós*äÅ©ãÕ²L†W§æ¼yÎè^Fa;¶)a]Ãncê©Áúî9 {¤å&ôo¦ŒÞ£³˜Ð*0Êç‡Ø³ï¸¶ÙCx–¯™‘à«©™ðNpººç°Ï?‘#½¼TâÈoØÍ±ážÎ×…«;U ¹³*åAôÜ ˆyZÕ»`Ýè<«” 6 za‹þQHŽ‚€Ë=ð)K÷;Ú£aÅMüq4©×>|­RÄ^zÊ®8kL]ö, {Öš‘&MK¸7îA Ç—Ìoþ¯›6í0†« ˜Yrj 0é} +ýÝÔÈÂc¹­Œ†G_jpüZÐx/wCû>”þ ¤¾Þ–H‹/Ö㾿˜üåߘ9bžHâR> ´<<ÀDÌ+aÓ¢Œ¨š6àQSÌ»×0üØW¶¼ “ƒðgÚcüRZ Â;vá¥h{šÕì€ßT[‰'”\ª;RV5v)í²ä';z2þ.ÒÐ_-AN]¿¤¨o†&9¸:æ)ãî·A\ºïûÛ™~ø9~ú/§ÆšsÂpd‹ð’4¸Ï©/}·‚àÙHÖ+„}1tØÔ/ EéÓðö’*Tý|y¿\°fOTvŒÏ©ëÉ$¯¢Œø¤HæÙGpvDn.„ÃÞ³&êÿëÊrVäXf·qXÉí64·3þ{ÃKþZ ᬘ¹ g›Óþ¢°Ž(žâPMÎNâ£5—­š|®áçi9røâ_ÕžD=|Äæ˜bfæüÓL”Å/P«¦ÑŽÝ³û!óæÇ7t¼|SãÄè'+?|ç.®ng÷–?<ß=ƒj¬¤ * cêD­(—Q%'Èá²!tsÑ#·B7ÑMó¥êßHóÒw T+ö“ùØ RLMé‹ÊÜìÌÝ{Dýt˜Рz°¥óæé‰3­6Xza3æ>Ìå¸þ¹N”èNå~4}:õìù&ú_þëW~(¡Íd~ âþËšDµ–=`¼ÏE³JZA"tªGâQ¥\༎ÏÌmáðñb®HùbÒ¶½þ˜=bsÎ\@›Ó³1úÊfÚ{Èt}¼rc9Ø„¤£¯~(W·Rœ¦óâ+ðP?UxÛ S¶ÿ}¿ü·q¹bÞÃìæünjϛÉäca#ž*Ó˜ö –9Úõdeúò¸[Lã»l©S'ˆ‰Cï*ut±üÅþ×»mükçÐûl”‰]Ÿ2ƒÍ™÷,Ûc¡ø|:Ôñ‹cÚ²9äŽ\3®zú†u3e'žÿ`Ë4}ð…Í aUê[ñJüRîrûDxÙà /ƒe)Á£J6üýZ¶/ð“–1í•…¹e×P^õ$æ¾­eò{3aU“&ÍŒ`\·¾G½^#<·k¹rñ4{¯&¿Í`–Fv¿5í•<ÐYØ3ìÎ2kåáÈõõŒ´K'ÞÝ|„«©•R `½î(¯²‚g[¹9¸ ¿Ê/‡;¿Qõn)Vc1â<% +¨ë«l›µ³°óK)#ì׆û¾EÀN¨cŸ?`Ò _ ­Ù<, ‡gö²Æ¦} SÊÊqïLào•®b·(XÓTäFºÑÉôáÌJØ©ý Eú?Á7?'š®§O,â¡KE|µÞDªâÖ‹@v~·IÕ ’Ï"¿_¡Ý@Æ,P%=2ÛP‘û¦™ W¾ñ1Êb¤í­Û.—#¢m4#­pS8}æs޾sA·×X{aÅ—?Øœ½‚•Tê嶤Y1ö–Aì~ÕHöû+m©aù<=ñ¿žhöIÜÛØbô X|g¦)ó¦Ú(¬-Ÿ٫ÁzÌR*Ài?»PìÏ¿Õìc™‡?r#ûâªLtsÏá˜8Woºïf.ü^=S¹¼œ2ð‡ÀtÈ_xïO¾Áƒïpñ²1FÇr+}«_©9©õLÞ9fÕ“û ü{+>Ý£GärM‰«òEx‚ÈAtÛô×Ê,e¼ýQ†½lh–.çóþ†d߬ÆSI²ÜGÚ—âFh] ÿ8óèÊI, _HlÄyÈ®5{ÿ¯_úÈ÷LøÚçµn·Ð2vÓ}¿r…æjä.GéÆÝr#”ÝÜÖ›·Ání;å¼ ýô+á€(?`G!ó.(}"ÿ‰â%Èâk€KóEè§<¸÷o5IξÏd™–bò{ö¾c¯Ñ£Š¯O`u£‚áÝ)4Ê‹$ܬhþ²xüÁ-} Û®£l(½ÜÓ¸|Aì'ç³îÂ/›ÛÌÑ—-°Î¹rz²ýWÄ™%™Ì’[ga$›ÖëÒ÷ž¡Pu_ˆ,¯=Ž™rtö–<Ñq´dÜi”á «¢êZÖBG:Þ>š ¹Oõè ß8vX¢ J~ÁÉÕÆ$/e%­h8NókO ÚRkz~ì(ñ<Ãp½0$kyý¨Ns¡£à©9Á¶!šÜ‡…‚¬KÚe Ò­`ƒ˜sÒNä TÄæ^ìÎÚ~ Î*‡ì§zÌã+p¥Î[îÝf[L5ŒT£½‘&ÜPšÒÐ{÷³S6¦pUöžb„[ÄaÁ‰çð/á5úníÇ0ÕV¶ºo˜mz­F…=ŒAeëÖ´Ÿ×€²KóÕØ³=ñyQ¯Â5;…аò.¸n‡7*nèã¹/>¹‚:¦×ÑÍf'ž±½„f'q¦|ŸÐ¤.LJÐç©ï)Ö´ræ%pÞõþ¾>MÖ µý`Ž}@×M’ó"'¬&†_cq†ÿU Ï+€¡'[˜Ÿ-5ì½+b 96 çv ìWU’z=÷XŠ]Ç'D®±ÄbW&Èy“u_¯¢Bî(Ö~/ÅOF‹8³2¢³f.3ˆekÙtk‰† ÞµèLå颸 æh̬}³ÔŸ³b"Bì³M5èÜP€kbMѪqÙ™•Ï¹ì¶ V+ű}]VôP¸õ}ÔÃ,>¹8þ8ôò÷ÀÝf&|GóèåFhôæ§Ì:=ðõo!25á u®ëES'lqi†X·&Œ¸s˜=7•¶NÛK¿§e`¿Ò:ræe9´©BÖJ–}S¢»@pQ+R›Ó:]Ñiûkf¹u(û{á®J>ÍÈræRµ…ÃìÀèlŒUÉÃ'a÷9¹Ÿ.a×:?&m3ÿø>ß ü²ŽNÂø˜ƒQËàá‡/xöRdíÈ€±c aÓ Ý—ÞçϺ¬Zºá‚_BdÙskºd“!}£\‚Ï“¿°Ëú=ÉÚ_Ìž/“éÖì9˜òƒâ¶»Í°< ÈýB'£ÌÓ]úüäTþ6ôÆ•ÞwáPv–_D¼ÏÑ£:»°¡ï³á•']Ù4€W2“úbT !°|£ùVÂK]Ï%1V/]©òª˜úm¼-çNÏS¦‰uÈB7]ZúÄœ(úÈѳϩ½‹1æ†9Gßmñ餃ÎX󯿀jÇÎÕÁððîtºþûÅ_ âäf0l|˦¬µ¦ÛŠÎѸMˆ/×¢Snœ§9Nkmö‹}‚×O ý¦¡UbôÕ ŽžIg%ýisg$ÆR$NGóà‡b'6¨,$3®Y“ eÛéÞ5õïöËÉdÛÊ|ãCO‰4ÑâÇñÄš/|Ÿ!¬-µt³±—öM ø•bèE¯®ñÁcädåbšMË¡—5'“bòQâJ&Dð‘Ýq]ÀùDœxÀp^2™´k=:K”:fåCáã (ïL£­B4—èÞÏM¥¦£ìô5tU¹4 ÿ–IVgièí¥Œñ›Gi[n}ä¸?:iz dlM {w[µù Æd8Ò³²K8¥µ+±Q,E]-h\¿M9QK«×N£ý»Èæã9cz‰ì­O¥³—•£–·:Ýj6¶><Á©:›QÍc:ÜJûg“çY•Þ(ÓžÂT~ýÅKÏe¬Ó¸®DHX¡¥cÕêzTÛ¾® L×äX¼ÃålžwïgÁ³KزÞ$öÕæ²8/.%[釩d•–>œÔÌ^9w—ý=ð˜·ò0çR(]·g ÈW}bf-DŽom{ˆÓ4”i^˜«tì&r_±<¦W˜…¡gh1dà5‰¥0¿>6|} ëu¸UÜVv…¼+­è¸ æ·L™}_YyáZLàífÅ×<'ýåù/v0›‰Ž­b&M…W¸TU„]V…õNg1Gf˜4&ÃÈ«xDñ#•QÆ]z²ºJgáí¡L„é©Bä9õ9ñWái/oè>¸ž¯¬Ý? V ÝO9FsVÍåð;Àn®‚·S8¨Ë¿€\~¿€<ø„ïäí |Ô—)­r%êûN±(RÏè&Î/Ko¾Á]4M§.AáGgÑðs¬ßkÄf'AGË*æQ é’¾=[ÒÁk²:‘Y<• ¼ÛTÄìœÞ³2¨,[À<3“kO)R1¸í›J šÓ'ð'EÉaÆïdì°¥ôªÎ~¡K–N!tS¸þ?I¼­:mä½Ãž6îÂÒ¹§™¦"GV02»÷I‘ð?×Y•1^jš·”tË™35™ŒÒ Ä~åPîêE±¤ñ¸31H†_™­0¿à‡ÛPöØk,K˜L™_˰ÄqÝ1Š'—…‚ñ¿mðÝj>1îµ#Õë¿áútoæüR }ç¬Û(‡:ÞÓˆL“,à6' Úèª+À²š8Cw\‹Ø QÞ×g`GJj8ÐIw™/<—ðU‰$Õ*-DíOSÈÞÀûì”~]<áÿçóû2[kŒ™•Æù¬þ*1z.ðjüæcûŸ²e¹;‰Z-I$äÔ©yÈúóJ>tþj«åÈž9Ò_2‚Å7ŒáòÏpÎL3 ãc¡Ç­n¥ï jã<î…Bg¡Þ}MP :HWQ”Ÿˆ˜ÑSîå]˜µ×O´i2¿µOãÏuÆôØs†øžE™&TømœX#B§dª‘ÕUøëÃ6p.°f.)ÊÐÚŒŸî!ŒðçN?z‚lwfà’þ0öºJ2·wÙ·—téò%Qf>{ä¾=׈ŸÅ1# QÖ=†•[ Î¶Ô+Üí¬†¡ù¿Ïõ>koµ…:éY°Íç‘?gTðhC5pnÿ€’{+Á(× ¦xõ@%O?Tœ-¹»‚DEÄŒ–\„NfÌG*Ž–çŒéŸ–é˜æ2™¼ç¾àÙAo·‰ÃÝ ;Óž.ê­üÿ7¾×ÞÉìªË©4°k%¸|îfJ¹“HŸ†µsgÅ™ Or6îÔ\†KÌè‚ qZ¶­›Ù4|[/écd¹7û6c¼1„öxé7¼·¹ƒ ÷¨-$gõã­YXi5 ß[ñRù+Ñä@<˜®@µçÒDwòGæ³4ˆ˜±AoVF¢—A Ë¿û)fNi‡½ °-XbUNÀ¬YfXåÄÁqê¤mù$úïb7Þp±ñ ¼‰Sô¦àì÷ZÄæc<^); ûwÏe^)­$Á)!lAš»\o*]·ÌŽê>`ηUàªÁ‰ø›à¹ úÞ´!r+t˜ {¢ðÑü“¬kÛ-Ô•ô ¨ûÇzf0qr¸pI5Ük Äogçâ¾_‘(ý! <¶ÿ ê4…d0£51ŠÄÑ.ž8hC}æ áù£X*¾ˆBL­C=rÂááv9#²YFJðY2U”¾ñ´¥/Bë93ëÓHþ¼Í-"úQ¢°ac#§aÙ÷‰ç?s"õ!g÷~Ô1ƒçZPýÐÒ†w_œq,´Sb3à”Wn¿R ˪N’ÁŸ-̇õ§™)·¸‘‘1 >(J3œ@ÒL”]ï%‹“E%HL·"ƒõZÄöÍ.Rù LÖ¼ƒÿz•>ˆà°¦#êý×Ûý,צ´bæöÉdVÐ%0ÌÔ%~õ“`¯‚a¯d¢Æ{%°3œË¬™…“Ô¾Aëx _²A†:æ7±–(«zßfj“E¶š´ïùŽõêàj˜GT7-&kù—‚püÿããpfN9rM4HÁžõäâö;ŒBi#87CÔü‡øë™ŠžcߘÉÚ¼KIÆÒ²+°‘S†Îñÿõ6ã¿×ðë+§íª»0é¦*Õ2ªâÞ^&1’¬·‚B{'ä—¡û²ká{à«ÉtrøH×îéQÖ¨\Ža6fB‹f"½¿!”d4Äc°àU*Ñ( #~AеH¸ðè<®—¹ÿõC[fËÃz£Nv‡ù+ö²¾>¸½uÆÒ¦Ïסî+ƒ üÚi‚p"m;÷”Ÿ0Ô$M‚…bV¬ü¦•äÌÌZèÏÊÂ7ªª$Bë4«õ£kÀ^æÛ˜u˜”å¹4&¡—½vC<òš¹êÒ‘–v¾‰ÑÏÖA­Jìžôë̉`Vt«5)“=—BôV\Kî ½‚ÏA˜vЇ^Ÿ/I$Õœ!ñ{$>Y6½Ú-0Ã4lŽR͜ۀgNããã²$Ú(€ËÌr§;„ž`³D-;PÕ‚âI¸hÇ܈wçp3jè;—ƪjã$ ~öÄõ›u·­z/I+¿Xqâ‚cØ!Ÿw¬ø¼bPßúxbþk;峑IáhJ¬HŸ uSö¶nt»ÖBâ{kÜ·Í¥7®­¤ «|è%1CzÊ·’Ô'™‘”ËGIÂè$jtœ´®ÚJ¯Ö'Òô² øiÁqxÈ×lÝWQ8Ež†\…ªb=“Þf Xµ| ¯Ö$k’cÉè±½èht†Xüý×RÜè ß|úf ‘nÚ„& ¨ax*»¢ø˜uÿa›šì˜'§aåM.îqOÀݦ7!´÷6¯ˆ„ˆõ>dÿ°2}’2ÆDéŽþ#Dde]›;‰¶ºhÑæåðò¢"÷ø![rzt)õØ94¿Ì°ßÕb%G‰F›)Pí„Gí"Õ»$‰6Õ’¤cÌ$½n¨ß"Nšæt¡DÌz*sàtLÿ‰oÎÂÍoá¢þÂw{ X´\†¯k±'Y”Ì{3~=7F™—wÐbÛ.ì‹¥mJžX£bO›D°ûün¢$ß,[N2Œ%qÚ?oʹ¤t{*i™¯L4vhà¾É3qºârXù9ˆ¼-XI¯ÚáÝ Ÿ¼Üü¾3L½a¾á©Cד*dB|ÂP/mâ°ÕvtBei%¦™Í ø·Q{‰I¨ã·žÖ5ûðôƒ üñ;ÀpB§¨E˜.±]N¶·@÷ÌÏzç.ؾÀÀ»½ä˜Ô0æÎ,cØ abyæ#ö(ç0_F¢{—œQ4Ǫ:I²`èƽÒÝ•è© ¤38ït&‘¸xpÛ…y·2šó&­œšî·b+Ù^ÝÂ-úiM%þç÷ ®¯ó<&Ù„R9ÉÈ_Y‰¹ËèžíÓèòöÐ.ª 3~Ôà†™Ý`ÿÁ€Ý@O2– EÉ7~ÎàTKÆ!î ß´ÅBä¦ç<ñMCütqü¸ëñcÖî¡Sg®ÂFûwàÏAмiÎÿÖÿÜÁ\0Wdë}ŸÛüØcË1¸¿³¿rÀ^…Ïg~}°&°É¢ì¶ÀÒ´u¿:+FÙ{ÞcPec ƒ‚±Ìè*ŠOŸqÖº0Ò¬8Þ†±{ÄD©ƒö$*]? 7Ö¿ƒ5Ý<ÔvLš,÷¡ f“„Ü`ƒ÷p9rLó-Ñ'áÑ®‰«`®Nëyxݺžþ;¦Bëp`–i3+9ë øx£ t*n š†s¢ôˆ÷ú_ÜòÅŽdöœd´óP£º—ø‰WüüSTͤ^š /ggsŸë‘s¯àÞáÝìºmêtÏò*8eÔ?ÿœm0<yKÉ¡—ʤ¢&Œ[EhÜS8'ç.‚É.¡Tpù2Úðw9nÏò¡Û·#¿nñÒEÝ‹qø• |2ÔT2áw£Žúã==Ê(aZ™*è8oA’µ#Ù>¡tT–ÊC¯û¬uPÎÊ•¡åMâ˜>Îæ?Qc—):8Ÿy6 EK$ùèÖ‰É\“{r^­V'ÎÂ¥§(ä³’Ñpö£t©»"Møç{Nuõ ‘Éóèâ§!„·qË›bÙ£õƒÀ#` Žù|å\8÷•²|Ù²õÿ¡Ðbþî<ñWk |ÁÐßÇç”ÍÌÐrÖ¼ï}ßDçÕþÀ…i©°LZœºüñ£Ú)7±{n”õM¸B­fñâù˜£Ô½W—îtàÐPR‰¡/ýèÌõÂdï6%Bâ²Éû%'&Mr{Êé´Qê'Q_ærNý÷¡ñQtpè |3ƒ ”q[×êÐO[‰å®CxèÁš³E¤qæÏùTÍT›ò¿ôÅ߳ˆÿž6mÚVªpB†èœ:cÍgH²¤;¶¯€¾k£·,7aêQ!2äZ ÿ4R¡|]zü‰&x Ý…D) ºùè¯ ý««± fKâßÞ8\Ð8›õ‹ÒÀúŠgàU‹ÝlGƒ™@è÷@à¿XrÓq:Ä¥H“;#*s†ßžÖ~,½¡A‹fëÐý³.âœÓÜ‚v~’¾?7Mç§ÜMnZèô;œˆ_vî óä:™ðfª%O¯'?Š'VmìIšÜü,"Hû¯Ü9êƒ ÿNÂïv{8tu2uô‡®²t_~¶³¥Ö¾= ;d¾¹cT²5É2oCž9ñD‘Ë¡Ví£LˆÙY92C!t•ÿzukžË4ú“0áx‚J-!äç–hú¿+§Ñ4j IUßJ·®“%Çy©Ó+ºK¨E&·€Çj‰ ý;U¬šÝ3ÆkõnÀ²_aöÁ_ÌŽÝ8pó7üÛ!FsoY“HeQ¸õk»¸ç,˜¢F –ÀþžVìÚkN&?îc‚wÐÅG{177,²æÊÆ,e#®Z3óÎ/…ÛÇ28ŸÿÝÇÍ;®Î‹ù$|Æ5ÌI áÎV§çNÀ®múð+ù8Ó’®…â.œ sÉðîµÔgµÙ îkN³3(Òu%µЯ®Rv:M¹W†¦rA¼Ô:o.`NÎI†}¦ùǯûîã‡ÂIVìVéÙ´ÃŒ!%7‚PÝ/Èø ¡z­¨y2l¢þé.Ò7Ùî^Ÿ^ìûËN9^Œù¿2Æhý7ß-ÏöˆœB­@†Šœ)ÆKs/;k)}HøhÛ)Q"ÑÇõôh|íf0PÆdèp`Üœ§¿j|¼Ì‹xÑm³'3æ¦|ðA]Œœ‡Ùä­ 9ÝP ·­a´‘ÜÖ‚í½ 4⊞wXþ=Œîšý‡ý[£gkP¨J ß\ÊC©Þø}¾?”° ¨ï&I+…‰ÒÓ“0eßCÜÝq…ÙÞý†­Üs Ê#¸A¢˜îyWPsèK‡ðòág°tþu˜w9nZ5ß¤ï €Þ÷¿þᵜ@|Ô” ‰W‘aÚ1BΧðÕ†uŒú¶@:}šú̽™¼¦d–S‘ß)ÃyPiM¯yæÇ%×HÞ’7pq› ñÎ̇€S9pÇNƒD÷úвJ;º.ØŒ)2}Š û¨¾ —3uŸ(m¼°Ž ¿Zž†BìѨÆk·(ý]>/ö\P0½0b›gSHß= Eq’—= õ¢µy¦¸E9ž^ë;¹à,4‹Ú𬦭|<·ôi'ªà€µ£>Ä´¤.«'7vÉßD R!^M§E¢»'æÿ‚–6ÌžPfËé0j˜Mæ$’Ý’Ô/ç Ñœ ß×L¢‹EÂÉàèuÆ~¹(M´–‡ª¬·p{^'8Ùœ†k­ÁR›6ƒM ༒ž–VŽÅÙO0>Th?c7ï !ÿõ>t~ÌÖQ©CÛI¢ù}/…ã4át%½W7ˆ›‡:•0$Ç~/¤+‡+˜óǹ¤0"ãV±¦up‹†²+‹Ï1qçÒgX\+EÊ_žã&YÍ&äÆqRú‚‡íô,á†KX‘ ý!àÝ³Š¼X0“üH[Ñ×R‡Zô€Úc˜š1DiM<ÿdåÖíBpë÷%¶íðI¸:õ/V´ À+S†Ÿ^$¢ºtÌ){#wíÀZÇ †­#X‹Ê’Aø8½ Ü~pbB ø†Î/áФõ‡`¯Øm¼s»§›—'ÿ!‰ÕxÎÖµ¬Eí·—™¢Â°pÝ<Ï„u5@} ŽÍYd‚[ÞÅ™<÷Ø™ª{ñÚ¿Ü`ßÍÏ\]óÊ[ÅØÄ‚”/qá¹n—iÓk‰£lìÔîÇQy_:¢ÙkIÒÑîkbþÀÒßÂ0p!ö×_F×E‰pUû&þ)ƒôë~ùïM9ïÛÕ±sÝ)PÖžCÔËÿá¶ø»Ø³P“õ°^;<¦Hþ^ô&šŽKé!•´*ò~¶äB_”þéÚCc÷9B˜U ´Lõ¤ß?à“ì"2΃ê)Øà'HŠÂè¦óf̾ëa´$¨CÎb*xv Y{è ¬:ãÂì]Ö†²Äèƒû¹P(÷O‰¸0wù/ƒ‹‡½åºŸn?v¾ø‰5­8Å:ŠÛÂÈÖkp¯8…¼è=Lþ ‰‘–«},–b|ÏW4uª…›Îö³N›„ Rtªö£øIó]|NqwË„ Öõ[LîªB²t¾êDþÿ¨öÄŠ¥3pï¡I¸`lŒáKÑ%‚ªÁøgýEŒl*Â×úó¸ë—ts–\€P‹Aft¬?7Ó©}¸XO•(nr.£Ð:õM*dGßg¿Õå³KœFnv3^´º^14‡ ëý œÑæLâÍûh&±x)y–.g7ay›ó/`¼ƒ),"ÃfmЕKž•ÏeÕû8ÖÖ‚ÒÂKñ¿5¤Ý뎨B|0%œÔ*GǼfl= îß.q9z¬]¿ U.ÌgåS.2vÏÑÕÀ‡ 5:…ÉÓ3.†wáe åΟ6‘ÿjÑ5ܙաøpîuhéׯ}Ú©ŒÁ{]V +·&aWìà}zŒÿ^† ‡a­Ü|*鯇1Á0ùîwöIâ{pZõÙYÁCš2öSø5›¾k߉º©ìæÞp𜠪èb¸ÒÍñZlÈøzãá¾&±©ž§¬Ç3Å'»zâ€$ݵLÖm!éÌå9Rð±ò>H.ýÇ9s¡‡IbƒÞ¦0K‡y‰˜µƒµsø^ü$v“­t:óÙMο|äúþȳ¦³˜H™_Ìcsb‘ B†ò gÕ Óïf0zwñs,ÏÄú—Ž»‚aÇd `“… ¿h:¦½ÁUbOñ¾Œ,·}˜‚g÷[¸x¯Rïçþ«ÝlΗoLÎÙ÷(ùÖ‹-m‡O›è¹+càð;M²# ùO0žlLL8Òk»Ü0«ïìKzÊéyj ’®ÀÑí†ä¿5œOɇÐÀ~¶çç9<ù.ÌTTèÅô¾†öàœîîNاbýéZæúrkPPyŠÃ]Iøš’¤HÏÿ®f—%âó+´~`%Œšwâëmð®p3ÈÆ€Ýn`àD§4â$)”·JßqÏ©L®ö›Ð¿Ol ÁìM:˜”ýÁ}?i÷Z.4h{„-^w¯ÁÉÚã1Ö&µ’Áh0¢“Ï.¡ÚÃîÔ'C–è,e,Î1ž° 6{Ƙp¯Â‘Ò«hxæ*#cütó§ýVØŸ[åÒªìZŽ2¨-?Ìòøeâ¯ÛhËX NóçE¥#ØM%A(Ù½¾˜BjO,ûNáÍ:‹¾õ{·]‡ÕI²°šo6þ–\JdÌ¥·íf1ù¾çáѽÉÄQÀöÎ}G©-û¸™­Kó§–ÉØ³a ]“ ]’ç‘ÿÔDålƒ üoìw0Ûžï…î}ž(DÓ)‰hf„¿ 8ø¾V‡®·Ý[4Àµã øå3xîuë±X*žÕl§#3>`NOÃe”áéúAvëµbÎgcn×)ö—L,m«c…ÿ>?dÞ¸:wß¡AÕ6X2§p¢þ'(+Áò!ì÷)àéh‡Ýj»˜gÞ\hÓ«]¦ØwLêœõÉYtî‘“¡î]+–™ð‘UûÔHk’ yT˜Ñâ ©RÂTþ:$òwÆó÷t?ÐÍ*¯Ã< ‚焨háÚEMÉsm*d*—2»—úÒØ]wð¹ãŒœ"Eƒ®¹‰+iäAŒ2ÕT$Drþsÿ°ÒúðoÒ$›NŒpwWÐïæÃÅŠØ· “ò8n#¡;„(ŸL÷V|{û¥ ®µZOn™·pF.ÖÑ? 0_²'“žé½â̽%¶ðñÝWöèˆÔÿ©ç2ŒîB}Hì¿€ÎS’Ñ"à fßzÏ(Šrùå0W6+Ãë?8b÷bØ+¥ p×Í­ô¨@;“qÌïšËŽ{…¸f‘–Þ9’ä÷[EÚ)çd2<¸5âµO™µÂä˼Aw—£¹ÉØzÌžõó×§Å;æ XΞ¹Š|åigK)÷Ê?Wú|Ÿ8=Rc„ßã-ˆ¤‚< 9?o÷^Fí^´0Š×Má\#À»²Ë`sô4VþíZÑì^'òÎ,b–zÆ Ž­.-–™GÅ÷f¢Ýå.¼4eco0ôB;|~"ÿ3Òã˜ÙfÆpÙû%šÖŸÔÌýÓ]?ø?åÙ¶•h¢ýyðõ@öôÙ“½W‚ú’M–KYuçÉÔ÷â%0•ÉfÒk­Øäc.èãs Šë_²¡á}cp£‚´¨Âß‹pôÚ-ö¼î2|ìÓ BÖ›PÎC ǼtPm¡Û aPV]ÀDœÙ3~â±:Ì—%cKvžö¦?`%»nêŸ ·© ‚×òÓü3™(z‡CFN%£º™;ܤÖµ¬—}8NúfÖ1½©Ùu6sX‡«3ÁGjuWÚ«·(üÓt‚ÿÓ­w³ïŠ…î&ýK„I‘x > :‰ª>ö†©]gÒ ’„çN5áò樢§¿slqÁËQ qò CDHÐ÷!8ñe',šOóëÁš×0ãûŒ¦ŒÈ}â`¯J ”…ðòZº.Q?Ú-¥]Å«ÿækö[J(ºƒž}ü$×/ 7¾g%‡s3ŽjÓÜwúänÙ8逬 X«16_N3w7ÿä´/ÔÄ€Owÿë±Æ¶#vÌÁ—­þÀdÀìnEZQ.ªG {cVhûoÝgâl8‚=ûÿNøý÷ï˜ðƒð‹ôVç@„¥F3 S…ˆÃ\j¸B—l üÌØ?ÖÄ+fQìû¯EØ´EÊFx™ŒˆÓ(SW×kw“^%)"ñsg•gÂÍ9ò$92&7@ˆ¯¹ÎÝCî õÌ$ë[Æ(Ÿà!Ó>`ª\„Èv‹{ðtõ,ð]&IOï@ºˆ_HŽº&Ò£!YPst8. šs\pãÂbÐ<>Ž kùÒ?Ë}Û~¸cI:©Ke^)Ï"ßJˆàÎ.(-xÉIÒgü» ~óØÝ‹Ç{Ë ˆ‚“1­Ö÷6'×OÔ¿s}Áÿq7ÐudTÂk-׆KÀÒëæ ¯È i7¹¸Z?»–¨ïΓøO1nè…}Ô&ê(ʉpº ’=e`â©}VþŒ}Ô ðJ8Æw#˜—K7“w¾h㼈³ï®$cî? /}4$Ëš?CLÕ0MøŠ57ayàR]Ãõ¸¢//BÉ»|3C‚®ïNe¬öÉÃ0GŠôˆGcšÏæìMX²;j+Øuk¸Pé[ '¥º1Yš‰“`ñÊÜ™é€2ü`@GŒì<» ûz‘=YáæmwÍB%çëø/䱉¦Ñì­àRp×A¹N;ºÊ3²ÒÄIîÁÛðà­„ÆL§+Œu¨ÿ4Gæß1q¦qãk<à5ƒò ™q‰É²:C%0ìô 7J'pâWÍà46‰QÇ'Útøñ öô¯lfµÝ؉š€mß%°XõÂÏ¢Nž&üú.l¸ÀtU¾‡hmªµ^Xwý8§C¿T^*~Kõç$ sÞ÷—:qþë¡Î0Ê€Í;ƒPp¡1»éh,^]ÁÞ×ÐÑQœ®R´gîþmÂ& 2K÷ÑŒü ¨‰æ%þ›cðÀ¾^fÍ®Qöí÷º‰ú—û+O~Øóv Ãñ‚hH޲¤“gŠCƒêwK#n´‹abŽ9‘À¬\¼«0ÎÇ’…Ví¸gê˜úÛ箽Ë̾.gãìÎþZÖÍ–(.#IÎCP|3‡ÓÞeÞQo™¸‚³¸ºÕ cRàêM}’}F ×]ç­¡÷\ÅýÊŒæ£_ÛìUêó-ÚæÄ¹ü¼ï6|µ|Œêy™'ÕiAçSVè\< ÿRÃCö—°ÍŠ€Lj-,ä…{“Š;=ŒÇ±À÷æÊ‹/ î»í}Â&b¡3²ß̹rõGJ&ê_aÓ ö §‡ÓäšK#aí×@˜?·%×0›ðէ۱Ŋ]µn±ÌûË<¤÷î/޵ÍB¬=÷V;ÑTè)TDÏ-w`}â”ø+@‹å´P |ã´ÞÔkÜ·:ôÈ«ålÁæ¼îñ€]¦ô\—û2R£ ù(ËâÊ:°º} ~ åŠÌ³BÇ™q—2bÝì²;sð`Ð:8²!ÝvB Îs#=~B‰Ö é,c2]ê¸iσ‰ÝÈ}x¹€ŸÞñnf{ ¿-(AUtûÇ!{^È“–ú/§õsùÁN©ŒÂ-{¯ààñ)ôØ %²©n1òé…øoUØKÖCàc®Ø"?ŸÃ‹o=l׌‹éZ´;sžÙ=ÎÝwØ1½F,’õáÔrüh¿†épŒmàÛ;&]÷Ä}o(‰x€{$`U Ì<¼ÞùZ ª)¬¹b%«{Èžu¾ÊO«ŒqêÍH–[ ¸õb­Ópv^8p‡ýˆHl"n9æB¼;5¡|Õ>òÎ#—T}=F~tc盩ÑGÖ¶ÌcE}²Cö{åÍ9ÖýÄFÅ®Vvް2æ2”HÊ]·âK ã üvšvT·ì*ŽÄЬ¡ÝÔŸ¸ÓgQä“¿&YÒ¾ _íYG^<|š§È±…Òtt½ LOvbí³ 91) ZÞžôú‡Ã˜n±Wrÿ´A®W¢=•‹ðŒÈ-xÑ®É/n³7€ß5‘szCC®×±*6ëQzx² Ìð9Éiסlq`;[ÍÈÕoû‰5o®Èºˆ¼F°}<ß¼3ŽRÖL‡Iÿ×× îyAå÷ ìßyd¹L»A›*>’I_ÃÙË쥨ü'‰>\^bMC¨CB(•Ò–€sâ^ø×¼Ne?¾M‡:ÔYv1]é`N$xDp¯Ì"çõ?Jœ@UÙn쬋¢ffVTÚN™ºä©ÒBQ Øœ¹«çï%Ï·èÒ¬})ì¹í+¹RòàÒÜ ìûÆB‡Öæ²Ì•ÎçÔ›êWÛϸHï*Õ8sŠxšÒ eýlIÆ%ŒØOáã¢=t˯WpA*”Øð>GQêÊìíÒ¢{çÌÄû>k1TOžù«Oz5cúªwøCÁ”^5yˆjY*¸ì{Z¤&½º;Ÿ73â/³èÙüqßÒ³Š}¥JÃü Yh ß‹ xÎ_ù~Bÿ¹_ŸD'9äAb׸Ç+™ÿ–^F'“)W,™ óOg egƒqÂyüû]åS¯¡À±0"¼)ª}(9ì?Èä_µ¤/Jr!ÇÍŽŽó꥽4M݉¼¯? 褑Ÿì‡KÈÑN¶óƒÆÍº”‹gZí®Àòx]âóÛ”Õida>õ4‘šTëo39cdJR;áõÝ(R¶H‡^µÔ!}ªùtW}ðÍé)òY: ÷›S¯°zàÛщú·bJ$cíù ¯üœ 1âÓH—t(,²¼È˜Hœÿz‚Ìãäë!¯w˜ÔBäÁî%´#Y¯8G`Ù¼Y°:Êšî{“¼ T1–!6Œkä¢QØ.Ÿ3fáæå¡8Xži‘¿ÆüœÅÎiûÂ-—Ò¡Q·éË)wqûòf¬ü :%B~[6±þŠ¶Â·ð„1…Ð÷oÅøZØõðWNb |¼¹Ž=¿œù¥4[—a¬ÒêáÜWÙ{ŽÝ^SfJàm29~§ŒyøhÙœ¦ú:óÉ¡‚)ÔÖe'þ}m Çòci¬ýÿ)Xe'—3’ÓÉÍ—Çñ~ûAÔ.v$_’ ¶Ø}EW‰Lø¯'úÚ¢ŸpÒd4 ¤áû,ìlÙJeJ‹p…úe|÷äûJ>”‰Tã#Ñ;Ÿ±Z¡SIÖ¡xÜaùºwñ‘{–'Ñ@Üv¹ÙÑ=osØ.Ï1(š“¢1Úîtÿcû[aOdJðèQ/eÊW;‚‘;‘Y:³8…gñ=2žá½מ̤öíyd,8 ܾº€ö±kØ»ÈÃY7¹Q±zļû,œƒjl'Ž`¶-Ú¥y¡rÈ”C–dcÔ8×Î[Z;ÿó`Ø!UM<ÈÕýxd®4Ñy«@—¿OE׺lyX ¡%+iä—Í0`X€êå?™õ› hb¤%7-¨JÇ>üÙIÛbmÂ]P“ïãPò¾;’üнŽ/1ÒèZÀxÉ›€¶¥‡„ýƒ[k¿âå]2T`Ê_ftl]Ö´™~ª) 5ï3Á£'Ý/v£õîxÈ™Ã4Í~ĺ݈†-"›Éܬ4ÐÒÎÅ ‚‡Çc«c‹jجviB®Í£{îòà–鯰ûÃT¦a†$Zq‘Z3wØcó‚¹Osœg:MTÕeæ\,˜À/Ú^Å“OǶj„Æ“¨¹ƒþ \MHfºÚ*Ðe‡säÖK´¬†Ÿûó^_ϳ‹½PUØ^ºŽ°1xŽžÎáάe™ØÀxåù™sª‡É|êÍðLÿw·ŠRIc+ðp—£¡ÅšäYÖÆ)Ξqs¨aF+TÉú„&ŽÏ•«Ïà_Ÿ=ÌŠ“gÑ[\| Kƒç§qàr(éÄÍÜü/k©ÞÃzö¿žê²uYpM5Ž¢I*pU7¿Ñ!|±>œñPhÂí¶¡ðßzÖ~Jât[F¸õìÀþE0r5ƒÕƒؽW„ø®ÔšÀÏ+| ºz£a¥Z>?ÀÚ›G»OiD0ÖšFÍ…©*ÛÈ€(å^ÚKÕÛ¢ Cq˜ÝÿÁ¼Ó„jeGö($p“vvƒ¶ÃE¬•¡!}÷ë„»NÓó½¸Ù1”(Ïa äMˆÛìHØñKžì(¤VQ87âÞùz,fÉÓ£SÞàÑâäSGV]N*âºQ~›,ÝÕþ„çô·oФ»½ðí bÿOÙбäÿvbƉm$ðÊè|»µ$kÏRø§‰V‹ÝÙù’DêÎTšgºj/û5¯ÝÎ`U¶ä²ùëÀ‚K4ÈÈ¢o¥]Q£½–úÛŒÅSx0PIg¢þ]utbN‰Á‘ü0õJ)®_p˜ÊÈanšy*’GýoRÁM3Éß©ÑTÕ#+…–Ó §&ã2ÙdÁkir*ì%‘ûØM5ŽgÀáõ<\¹NÙR]3ˆŽÏvh'OŸq9D[í>òOq#Ðÿή  –Ã%ÜocI°|à#\–×$K éµí» ]P~6‹à¬ú ZIuÀMýïPr9Œ­i›Kiã:ZñI ψÖRÏ©ËÉt¯"f@¸g/¦›¹9T()$×àé â¬tŠtUdzŽY¡ð»øÓà0¤ÛGCÛ7Ô½Ûõµ¥¢|øi!¡ì‚\¸qú/k(:ðŽ8’‡I×àþÚÇì{—WÜðR%8Tbsv„Ð[ÊSàôA.®ûûÿ÷ýÓÐ한ý¥è°6 cŽpYÍ÷a°ë¤˜ñ‘™Ò»B·›r¿É½`޲%àü9¿íýY(²<º¶ØRž_|téš‹ÐøÅŒmüªE´<_ÙÛPÅ_É÷ÑÖ[³A'> M»/`Þóshx$œ# ¼þ™9‡ÈØ&“¤ûo`ÖÃx1pŒ9™‰gûoBÒ¾ x‘iO½WÌ¥Ë#R! 0-Ôd׫­'Y î`Ó;ðìS&–s´HØÃø[3Œh_x€­¥!°Þj+=ã8Éšß— ‰s3/±¼‰ ·¶Å+Ðè‹qðˆÿÄó¿ýG‘Oè>YÞÏNßNÜÄX¿Ïp¦™ A¦šdíú3øQö ”¡ üôã³VPz»¿gýê…I©î"4=kÃ;añÇ«L6¦Ú|鬗T8*]||/üãG£÷¨Ò¤­ôÍ e¢Fâ3-Ȫ}áð¼zÊ*€ŒÃ³a;z¨¢Ñ²Ó´¿S®×3Ÿ…ÅhÉUüî™OEˆ÷6<ßRÓР×»L£qýdešUoIr$>³OHvuþáv …î|_dªÀ ¿ 6nÛÉ\ë9é`ÖøÿÔžÈÿË&–‡+GM´¦’IÍ—¨ÆbKL®bÜoª“OëŠéÒ­”yy1£ái·&}t]‘ˆ{Îf¬øËìbi†åUb³á11]D¿fÖæÂ\FŸº__K'™¥Q¯,R–ŽÌ&“Ç= çæ‘…¢/ÈÌMZ$P' mHw’ì[,FR;¦`¿J7Þé0ââcŒgþ4.·œ^ë«5SrúÄpCܾS8ç¡"MÕÒcüæ‘ae²’Áêº4îÒhøVA¾ó¦Ó…ÇÈÁyJ<ùàmétó´%6®¯HÙ't¹ð¿ïŸŽCk ‘Ñ!CœÅD_y í¸S ®Mg3pÔ??ökýûuhzÞ’]·KUæ«@ŽÆE¸Ö­AîL©¤Ž»§R—ómìݺ&,¬A(DƒîR †Ëõ±nh>‰ÊrîÍlçÔ)DÛæ"“<÷)›÷èÙ¡FU®n9,"5 ¢¸˜´8ú±÷ö ËøtL.%·šðqþY¸Øv”¼½¹‘ë ÷iuƒÀÉX±:‚ÕÑd9%ü8«ê+¢x›q½dÌηÍcÔºÛ0¬G›¨ÀlîZ˜lHO÷\D|¬1qÿKìâö]º"9P)OÅ®¡_¢ÕÉýšj¦#)ïÎÃaÇjÜ¿ñŽ}€yâÚD"Õb\»o‡xÒŠ’;;AÏS€)Ÿÿ‹ó+²¶]¸ÌÒEçÐÏB”ÖüaLßȃóœ[ºY‰„¤)“ÞseðsWؘÓÛ»LHÛô:ЈVf†‡9Wœ‚^äF*PÊå9y¾,")§9 ÎCËŒÚ4Ë‚»W; výñƒ¹;5鬤ÃTõù°Ö5Ò`EÞF³¬úé@ƒÏ7‘ q¾‘T[KÑí;uÕ,ªòØàÿÆ#.ÉÕûÿûþËT^yûʇåL¹¾ËQÉðr¦é·*1ï+‡ OÚ±SÍ•ûåëzÚ½Xž6®ÎÅIçײ|l(½·¼ceîkg=FñkéŠ<Ä[G ȼÇòÖãE©"üämMõŽt£ÛïògZj/Çå´ƒù]5îV&ÁÞ‚¿GñU`4‚µ)9I•ÿô`|U¹íô¼Áà{®ïäf3˜4#ŸÉ¦…‡n·"©è†1ÿEäâ÷H+¤¨ŸÅ4êuw.9»¾•¹ôg|¥S–)âÆ6t¾žŸÂOݸFDoðýÄü_m!ÇÚ퉄“s›@¼h,Üô¤çíwK‰ðžÆaWJÿ[zÍty²çJ÷õ O*¨ DÍDÃÉ™’>fËÕsì¯Þb*pt1ˆÝ9ÍX¹Èý[÷ óÌ¿?φXo §º»Ò±ºè9ñFÁŸ%|t[ôbðÞÇ>³•é7Ee)¥n*Ä U‰¬Ì(†ªâäåi0ùûƒ1í …ÍáÉСPMx]Ȉ^Í)ð¯o4$«ïõq_K“¨QpòõÚÞœM~&H“ÔÆó4áÖo\œw¿Z:Ò#2„ž­Œæ4¯:W=쨶ŠÈDü-ÓÐ+m\Ÿ›rqKR>ëSbÉÓÏ“=ëkg® ÓÙœG£xÓ¬CkhΣÍ䵕>î9¿x£2Ë}èPö|R¶Q/¤:áܨ”»{/ÂÛªE’ƒ?ùæ¿Iú+XV*M¥ÃWU©‰rјë@_Dœg!b6ü;òýe-ˆzìŒÿGÔw‡sùýÿg†ì™­ÈLFJÖë~žDR)44Ñ¢’4”¶=²G6!…Rd5ðºŸG%)£AQ‰¤!JCSùyÿ®ëÛçò÷Ë}sž÷óù|Œû:×yQÇ/‹¨ëHzÌÀ{Í•ˆþçåäÂ_m›Á3æpLc;h÷ûQÝåB7Íz.’ìîk|Œã¹\ªúð6‘¯í†ü\||Óúl¡~E_XÆ’gbܳ\áù[Ý…@¡×€Ô)ýÇÿÚ!2ÄØSƒY­¹€¾3Ä»|7 Æw6Œ$éSyŸ0fêìKørM0xžk©¿±±\ÌXø+² fbF‘ ËÚÛðLØKìýa‡CmÌØ„w¼Åâ9ãthÞhOʲŸB Øú²Jˆ:0þÄmwéupeF[½³z)ºÚu°ÿßÜøçz'y¯ÃÙG€g–œ™¸ /4_ùöx²›š~²ký1’W€î÷g>ÜZ®¿=©Ûb%ªw‡½z&¯ „™{§Q‰¯ûhÇÒ!dù&SÎ8+µ@o7/¥ÝÂWÿÅŸës‘9xcçO%/Î~Ä_·…À³ývž8Œ¬Å=t:_ Ž<˜Eª¢IGN-¥*Æ’T=Š Õ¢sÉ YjWÚϬ¿æDNÆe¡Wøgï_LU 70኷Ð,1•|œ‚mTQé‚QJ-ã"IwÄk‘ j±à9c zhWÑžŠ0újÖNLý³ä‰“†¿±íöu¬Š '¢oÎãPs §æ†$Î’Œû¡ãaSmiMæäoƒÊÛQdPˆf,¸#»Î³c‹Ð̇ßß–àˆ×m6sv,’ sù:á’SòÿÎ?3”„«“ë.?ÄÙ^6ÔIPuBç$PÓÛXçg蓈`ù•éǹ’ð#7/ñ­&%iŒÃ[TÙÓÁpž7¬ÅhWÿ=ÜáÏxumaÏf 3«ÿÛçÌ\ò³&‹&maÖìåŽOaÅÏ5dZâ¢sò'˜n½Ç©¿¸ Þ¾<…úÃEÜ™Iõ¸aßnFç~ x;5Àé;ŒÓ Ûã¿ÁœsA)„í6›áÊsàC poÌ ±n ÅxšXÑi¢fØÓxž>Íã´Xd0*zŒ˜K}««˜îÏár™ƒÙa ޶_®»MãïD?h|©¾ñœ.&ý$ƒ)³bû}Mœß OÍeÁä´1,g'|ó}ÈYãŽo¤ ¹´êð¬ 5ÈÜ™+07Z>mÈq°ÈmƒÉß™ª pÄY”¨Å ’9±•øâ¼!nJª‡sbt²ð?üó5Ieº]C0ìJ(v]´AQÃ7”hæ±\ç.öµÈkÆ«s'yšA¹KuHÍ“ÅtZM9rE¤f6A”ÅΩ)Uô]‹·Í€™Cuõâ-'º}ЙÒÈÅ{“Ã2Ç`Š×Y<{‘®ž9¡‘÷}@£­Tp›–ˆ¬$º²wi¯-ŠŒ"?gG€ûÉߌ‹%®‹ð&cÖTV&ÑæÈm/ºFÑ|õ¼Áž/Ï„Dó$jÚºàÁ 9oPíi# –#F÷[É¥3èbósÔü×¢ÌsÕï‚ä6AlìüŒ»<îÁ5'ûù7ÛÆ.°íÜ;Ô®^@­œS±y‡Š¢š§|Hëizw»6QI&Í©ú»®Ñ\¼ùH–ÑQ0%ªw¯…]O eå%ا~ )•Q塳•Jg³!ai¬KĪ 'wcÎ2 ¶á>õâu³=X3·[ðvŽ'8úç¡bˆé¢k^dCêÀt<ç/ ƒßlÙ“i’Ð|r¨;íØ*¼5œŒ|Dàéj"øuUz¨»÷tÃAßšµo=R¹ ¨Þ5˜¥êN½Ÿ›@ªè4(ØKŸþT kÅÏÔJnþßûÏsEýì±ÝÂ0gQ²º³pVÍ$Úé/I jf“›W²X¯—ñè^kz}v0óI¸jn¾×‡s1³g;éÆEF#ª>†óÓãë$@áT|;ºTC-È¡ý=ÒÑøSzñ“¦ $D5›Y÷E¦¨?C¡YªvðÇ”OŒÉz µiµ0®;ĈƇ¿6-&¦×ÒQy•9ÞÏÃð£4}Ãjº}uÊýù‰Êµ'á{í*\äòka#mT¿ÅÕåDsOö\aëox‡½Îñù>4ÀÅ»@¦Ç•¼~:±æäÎn›a¸ÊmAQaHkè'xÀÅwxy˜,î\B.\nÄÔÕ|t,w*ê) üã¿uV¶ìøïoŒïÜ>¬È+Ç;fï9ߌAvÐ oÖ"ÇUG•òøK-ƒ[ðÇëdAóðköû´p4½/BÕÃ.ãóºA ³“éûº˜®ÝÃÌå’l¸¦3 Ãù¡ô½f$TÈhõ6áI¥Up,}ô°E\±§ tw²8ámk‚£³ŒÉ¨Ú"¶å@4^µ"½{J˜U×f’†ºhòõf»f ½˜…_B9üÒ°b¹6óü…æ¿ú'Û‡™_˰ªþ³ÿWë·y/4%nÀëFÓhÚÙ´{s.\?dÞüàûú>ã†ó«¨Ó G¢.jÀ¾«Ëe¼o¯ ÛL„ÈÈÙëxINØ¥$ÓKcŸðxL‰ÙÜ´oBö½†»Â¼„×(ÿ$¤1“Ãd7áÕ†ðžÂ²($ ^^F®‹¦v8UdÊ(‘ Â$(—0Âã àÈó 1èD–÷â¾ÿŠš^™Ø OõÇ™³æ09<Ò¦£ø³x¼1òÄàë»óÉÚÎ2\vÚµþlƒÓÄüw5u®þx¼öçb÷Ó,°º $BÝ› ÚÔ£e• q_\‡~ LBOÄkÝA•A$ku L‘t¸%áLºµèY# .]œæ—ï™sõ]LYÙbLTޤs>´CÖœ$\ñ&…>Òj‡õ¢Ï˜çõQq$ ~¿¹€ów®c³«¡Ô%ÿj 3œY™¬¬çlÚy@fIIQC)ê¯X ’ñx`ÿ}LX2³7L!ùÛM¨›~ Ǩt>ÞË¢ÓÎ$ˆ¶ÞÜ…sHUd-§äÆ/á§÷àÌ•_¸ï^Ñ‹¨Nþ[ÏÿůR®Îdw©£€qÊÕ'á Ub4§W.l‚¹*‡ÑÿŽ8½ÃðCÃp1¬y¿„«°MŒzÚòbZ÷èÙËGÛ»²ááÎCxÚP Nû·â²bÜcëÐsPŒºRë>A 'Ë7iÓ›ŸÉÞ‹aþÕî ®Ê¤ÍG¾€€ÿj̈–#hÓÉ,œB`N›+ž´mÁuFPtÍòw|áÀ@¬} —³¨ÞÜ^¦ºJß•XÙu«©íöy¸&þ=“0w-Ã-â¡&¡78|ÆÞ›Ë®\ÍCx:á“­;Ùøˆ¸°ã g]b »ºhò¿ø/bÕ~/€M»ÖC«H0Þ}Îä Px㟖Ï3ÇO–N±8Nþ;'Zýèn¼*ÿí5Þq?L~GbDl¾Ö*Ç­~Ø‚f ä»âDIöïXx°k­R.]b3ë¡(> ŽîŠœðC͸ˆ·‘­þØ›Âþ°C[üêÏÛ(*íÄЊLVf<çøê: 6å<ùû‡³'çZ-¦-—ãYe{]Êñóõ1ˆ,„C‚ ~²žß¸ƒJ¨¨œ»ýoumÀ·FáöDO·\SäÐ*1˜›8ŒD(…ÓôÊü_üñêâ8î+Ž÷èÑÚ/ž[Š8Æ";1¿‘‹}’0Ñ^X JÃÍ :÷$ÍŽ˜¯@Þn9SN0D÷A>^}‚k»x-åqxÖ…¿q‘† ãÖó"wòΣ©Tÿç }u¤î¶3«j åõøõî¶ü“:¬ZN²1ýÆô«äÐd$Tm±mW-¸G\‚â†líù™P~Æ4gibÕí[`Q1…ξóˆY]¹Z“&“„•öho¿Äú¢‚óU\r gºE/;í‹6Ÿ3Åã|Ž`<EM`öV>ÚjÚ÷ÏÿÞ*@n<×äÚ{X껟¶îu$6·V2oö;Ñùgíàyf‰°kšð¥@œ §òM-°<×t~ ’âJUä— $?Ïû€ÞQ5.´áq×çXj·?)àØšÝDà»!óeÁfXV5ÔHä׳?>ßfŽöËÐIÓµ‰â+ây‘CËðútÜø8ò?¨RwÕ§Ì X±áÇÜ‚¤¬Z=Nï¥3ÅwBÙ³Ùìf¡Jøn7…<“WÁ’W"T£ÝŸ´ßÁ­EPžÅ!•?å±0Õ~œ &†àeó{ßÏ"/vÅáÑþäû¼#9ë,íØ‹'w’g‚§ ~g%4>’××¥Ø-×>(ÖëkLÅ‘sʈ콯ÃS>³v‘t¤´ ì,¶ ªU¡œÓ|sÛ$Ù9¬Â›ìÜžý.Ìຼva3Ã;3¾(z‚U åîÓÃÀ=ZÌ×ÑXýªŒ“$‘Ï·íyÉËÌ{£›ðvR.óù”¤héÀô,-"²o3]ÙŽH_†}‘³Ù†Ö'¬dòLˆÕ͇ßf½ð{‹&õ({ˆ1Ûd&jB¶íEL’µb&9CÏËÆbª8ÔN«ðâ^òýGÉ?ü—+kaÉx-WnÃÇÂ(P2 !•zÔs©(ßÕB­LïT1º¬l!U8ªMû?ËcëÁ,¶—ŽúÁ³ …ðàÆ4<8¼ ?]›Dž.l†¾ÃöÄöš4ù` ßn®†ö-ïÙMUÛñ°Ñ,Œ¨°áÜûgßÀ¸)ªp5 wÚûѶ~{Zネ,Ô§‰ÇJðán%Ú;5–xY m–u1Æ/Ò¡>ä Ÿ‹…ù5˜ÉCO§p!@æ3ÚØIAAÓ~´îXŠ÷á_©ÝŒÕ>Y–ÿÛl"p<‡M¿¼5ÞLü…R0Ñ )_»þõÿ¦Ïà©W>T)ꃬä&ú†o -ª)«xsówê;˜or€SðÎò¯TBkr4®$>›¬ÐÍ(„ò'^CÉCaFÈtÐËDËÙÈS¤FÕ WÑñóyhbÅÐÒØdÒó§ëVî'“w£ÂÑ|ð>±‹H¼~Æå¿„½È#„ý¶ü𴡸èáÝ~ä`y"m¢VÙö"3òV‹œ›3ŽJI¡îCuš–µ¼Õå'“WÉ0J-W8ÃÏ’™B-Q’?5ʾøÄò…“¼4Øæ3ìÚ2Œ[¿)’…ëጲ=´€>»ô„)û#ðOÿÌ’kco¾„>n.(n´Šý<$†/7ðPÃ(FHAŸžišA¦d,¬0~J6ø2ýŽäŠÖj§äAËü“0ôp4|Ú~;Ê8©K˜—ݪ°=ñ(H¦êÑ}_TYŸT F[F— ¾=’OgPýòɰf†3~ÕÛ‡O1½©!òõ«h›YÌôŽLÇ7/°73¼ 1.žY¶ý++ ÈÅ:£~¬PœLï/yÁÖ$‡öGæÎÓ ðy—…Á·µ™'cŒlM9Œ×v1¸eu°Þší`Åùoÿ5|ºtJ«cñá~Âî.?ôÿ|¿„ÃŽc¿¹¬S"›ü¼vëžÑŠÔnZš„eû99çPÁuaxy =ï5ì¾û•{uÑm¶4ÐÄ>®ÄúÓúD¡ÐòF?¼Æ‘Xxõû×bÊAد×ÀÞÿ íOùÙ)2µhZ—Îe6ã‘Y¢ìm¼s=^…€CÄe|ý>ò=;9ª¦q¼œ÷#ùÓòkLLÚKö²[#ËùXÉ)q=Š+.íâZaõý˜ˆ«E0aç<±£É<ã%¸ûi“·úkkD6®­b­§Rk³ì÷êèý_æp„Ý(ÔûÃ%ú37Þ±y%mL<’àeg*•“¹ÏüÕ¤ñçÅéÌÑDnœå8¾N_ÅM¶sf§¼çEŸ|s">Ëô,BËJKÚ¼â+_a¹])±ÔS8 ´ÔM`ª#/}õhù½æä^N8« %öD!ä ô˜ŸgŸ§5±Z‡wq#°3oñ2%/}Øm“/bS¦8Î…Ÿ”ñÑ”QØxž–¦éÐÐÃz090 UOâ™Ò]Œq>âvÁyL±úÕÿæÁ„ôfŸ&y£fIù¶Ã‰zqªÐ½o¦Éÿ‹Óä·LN÷j˜yö0^ Q§O,%‰âØêVÏ”¸on‹‘+óÚà,s‡s5á9ã|ÜŠúìØ†··lÄ€èKõ¬n.úÎål¾Ç¹¹"÷…Ck1jWËG¬ª peù/øAb9ÍýÑd;‡Ÿ.ê/Dg>ò0ã%¶½ nŠß€t->îÇ rÿðïãë°àØs¦áÝ-&gG1ÁÜPšö§3e9“©ØÚƒ”Ïä2·cz ¾;k¿‡Â~>LOÕ¥«®^†)$çõº¯@ŠÔœ'0ù8w™Åoü•ŸÃ>?ËvGMˆd©ÓÀC‰ÌéyÆH¹›ù,"GÒ¥/á!ÉGÞK§1þøâÓÈamóC`b=°¦'˜¼8²Ÿ$+½äÄDïÄÅ1iÀ,òe'æ±}D«I‚\. £%÷žâa_ :)щ¬¿Düƒw€†Ýú-ˆÄ—–÷­zæÿâ7}å“ã>ŸdºàŠL{ž% ûþ$¬±½ -_âQ&IˆößÊ ^BŸ_åÈ è‘¾ñz¶ì* ž>™=¯N¿þhþoï4Sc™CéÈ%:]m5]hü•‘µ3—.[qhÞ!Gÿ‹Ó2 &E½KL–²Ïú„Ë Ì–¼hˆº/ˆä0CNìµ'WžËЄÉLï7 ³ÊñÞ?8c›!ùyP†¶DþÅݦ¥ÐV#…£?;`ð`)wÕ¼4ÆõêFFsMÖðN#üo¡.O“Q¸çF4–=š¨ƒŸ¸ü-7c|b_ƒõ5}ÚXg‰§åæ¢â“`<0¿MÑ8' ïrdÄÊ*ú}íl5¹ŽJ7O͘>év+Á¶¦ntnQ%üK«Ø¸)‹ ülv¨K’KçðJæ /Q }’ *W ó`%fFÅÄ/P1˜û¯ÿ«×´¢ááxMxɺ³H³îÐïí1ù­9TžégŠdö°‚ªçaE¼5ä\[É0É@%>\‡Mbë`áx~]¥Î¬^-G]¦Ä'äëõoK°Ç¡šóD9å¬9S¹ò”wÉR(¹Ò ?ÔÂpóž…¤µcbž±tmƒ;µµ¨ô¤vnãåÙpR OV„ÁWñ7Xê¬Eç|âþ]¬È5üìJm]Ê`°ê.[°ø8[šNQd›ÉN(9ûêf[£ÌÅFÇíj÷*ÐÝë €×OTàïüéôy¿ hqáèæËìžÀ©ÿ;ÿãdºöÄ2Z"'QEC™„mø]«+P~Ë(SòØí™Étê.jêFð“€%'Ñ9Ë€öjñüÿ3¢óV,"V¹Ó9ó°,9•˜(ÿaž<ÓÄá¼~tý0“èt Æ2‡µÝI%g-Àzñ··C½?&aÑ-îƒ2eŽé…³7Ø<1!2Ö&EgøãA/yŠ….4{|69¬EJw"ã~ã.xÄè‘ÅùiÖ8”úL/h‘ó[CQȱ–}†½íº(øó3´‰š]wunMZJLÄÄ÷4²i:ÿùÍ©o8ßÄ"`•u-3®$EþL‰‡¯õ¯¹›胶$¶í£8ü¾ÚºEqÛGS¢>o3>™?•5jÛî ºåB ØÝI`x g"sL„Þ1Ò§A3ß½ãª0òøâûdº4æ2ô?ó½™5OÏâ¢YöÖ ~¢ä+KüÖ¡™›œÁ8TLÅë ×ââì2fûÈ_4™”ò¯þGM’á‘`/[™_Ê™™/MN÷?‚ÓI5¤FÝXåi’¦ƒÐ±VŸÆò–¡KÙèí #û•,p~9AÇ{yÐõd=°c*½|ó0ÞT &G>ÄqÆö#ÈÜ=èJ›ë²¡b™>®LôÂ\߸ÈTަŒŠ"üK¼”âDÖtœ‡ð’gÌò7mä‘X¾d²ùP#¸v+Ó@7røœ@½ùpt_¡Û›çÓå^‹`ѺTæÝýXæ@ak\Æý˜ƒ‡)…¦Ô,øÂÌš# gWÅÉ^iFÀiºUY—XÕLVÀˆ/™ÝÅÿûþokøzð6³M e§é“ðô² ™:šÍ6FôAάä÷½jØ’»óî'ã–Êøj¼tÚ!&þ ¸-ˆf•w¡çŠ$¼\îL¢­X¨”3tƇÎ-m†Á”•ª.…—eÈ ‰‚†éªŸ|š{N. ‹´yIŒÅvõÜ:S¨ÓO ÐSB›ùQ ÑžuÖ1`Uywœ9N—a»n&SîáGæ{”žö È1’¤å§á„§8hZ?ÅÒ‹J$H¥åï§¶Y‡ˆ‘¯1íO"SL€hTòÑ  ëV÷/ÿAÖ½¸"L^M3$Ÿ,¨¶¤.}¯rͯÝDdükqѧ>u¼8C¡V½ÇD³ pØB—Lj¹wufy8¹º&ŠL¶ÚD¿õQ3£½TK/víÖ¤åé@ü욉1‹¹û´ºÎ…@˜Ê=ÜSw_UòbíTžSN+§šý÷$}”ïÏvXÐq1A2ùšm’¯}ûñ¨~Ù7BY×¹ôf@ݦ¶‚fÞ¹ŽÙaëÉÞx¹"ŒúOÙÉô¡ º“ëž´â{%œ2$ª9ÒÄ©õ2ÛÚ¯‡©ƒ $íó2ؼpÉ?ü»ß:ƒùèáË=bž,Yt}2T•ÛÓâóZ˜)äÎé;щj?mÖ»l€ˆu.´1Tš®_X —çé‘a™VŽòRMR±å6t^—³à{hÂc@*LÓS_“ÑH(ŠÙ·“×ð «Ž9bW³ ‘« ÆüÈ@X,™ 7ç%£n—}9Ù/7eÀáDcÖBê6{-q Þ½ç î Ôï>ŽßÙH¾•ÆéSFÙ…’AØõ·O*%ÀÆ%‘œÛ_aÛf7°[ÅF¤oÅÇ«°zDˆfÆÍc²t–ìJÂï—ÒÁânXú±Ïó³Ï{6þóf_XUój(W1"¯}Ù7g èÇz¼´Ã…þ>äE{l̉ç:êçèF| ™£æ>ЗƒˆÖÆû¨_Ö-²ÍW˜<ñä2+uÆai5Ô½]÷ü`Rµ›úRÄ3&Ó%ú—ȆÕݸCCŠƒy ¤¦'¼æiÑ•_óóÜáªzVâ¹4Ì·&œ ³pïþ ¾ÿýåÇtÉ3Ë]Ô°&‘¨G.… s¨væ*]Fë eS6;–DM&gÙ9¸£S¶ÌadÚÛ˜Ö¯nÔu߿ĝ4me×_B…Ïþ$®#¯f‡ÅŠ¡D²¡ ³;.À‰îó$ê ÎÉ™ÑOwméº6qªˆ yw{)±¾ñ §tµÁÌ÷”'¶€Ó+:™.ë{óVD’jN6É¿ãŽO+v‚º)-±Ê%3„J°¼PŸÞ8þ‡½4$D&ß5"ú³ƒ Ú}*½{s'¾¾”µˆ¼Fwt–@ó’<:Üœ‘ÿâÞ‡¶²ïñîä)äEKl?ß•ßU讎£¬¢~rU3Ñé@-ª²g™Ÿ‚“‰.w%*Fc×êCÒ+OÂ#8ÂZ>ä¾¾¿ ó\"þ—ÓˆXp4^½wˆ:I xhK×|Š=Þ±‰8çÝÆ)½êô‘$i”ðò&°$¡V_óÑÇ0œž‘â¥Ögâñª¬98Á›Íë{<"ð3mºê­P9¶•¸ ;’Wº›è”ýJT𮄻îâÜSññç30°9,w•€ËùZ9Þt¡6u/äÃãQrtmyê?ü[73Ï&d|:츫Eš=6ÐYõhªX1˜½EŸžý´ÃížËããè¦ÞÅ“ws–N‡ W¶4J"žo`Ϯكëêpðq+¤¿ÖG¼e…7‡¡äß×8dêÈ9{ H¿‘•4!drpÛ O³ÔßÙ¸uì¡§ÇÙu{©Ìø 2ºdüwδÈJq >V›t͈îÜ zhåJç±0M¦®>ºÏôלåyÑÜNŽ[ìÇç´JÞ”gTâá€Ï$ñ{Ø=…ÿð¯-=üEv±s+É«[=¬ÇW+§v §0sHÒºO´ztÓâ6P7õA¶«üðTeÛ6œM:t¿Úd²(_ŽÜ튣ž\²|®9VÕg”‰ÍõSHÔ˜/óq"• õ «BHPÔ-¤SË¿¡‹­#îÌ]MòB&7LŒA»4Éz -z¿†ºµðÁ|e¢Ö¿‰ž jf6,¨€§¶Ád­ÑU®x_&ôD÷Òu’ÿ‰>œÔDb<œ¨Yõr*2Æ;ñ¼JÐ|ź"!ŽkÓð«Þ)¶Ô:›zŠq-~*ã?úTN}äŸþÛ”’@míL’úGƒZ¨(‚õã:fl¹ÜçÉ$æ¿¢èi=2©^:Ž34Œ+LY…¢`F-½Bk/¤Âè™äÝÁ;0ú9Ïõˆ‘¯gó¡Â݇žÚ7‚ë«yHÒ‹d,ò“æÈO/\ŒÅº>rè£íäµ¥S$âÊxcºr—*ý±>\ø]—˳–´©¿©Ù“F§0ýF[Iã;~úrÓÂÚÐÌ„(Fsq¾öìýë˜E9ì6‡YŸ!OŽýb{_”³oJåo3ßoœk*»ipc:ûMo†3–©³þé¿{£aükö÷F7,Ó.¿2&ÿ™EnùNæš)\DÁmHLív=8Ÿ|Ý]ÄvŽ\ÆMf¤÷s:ç4á³^i2­ë ¼pkM/ì´z…)y¥Ì”ÙK`’M+cûœµž4q{ ½·ó1á½ZÈ´j0ð:Ï ]ÅÙÂO>º€AÔ d·!¼Ý¯¸’›Ï!íŒA×oAÒ>Ä~>‚Ï¥§©•ß–ô›—Cì’SÓÈjÏcذ–É‹‹¥?#Œ0ЮŽxyÓè ì^º×*÷G½‚è*qœýyŠª]ÐÄÃ×.ÿ˱f+¼ù6ò ™ëu˜ÒY¶ÅÙ&Ù°~Ï+8µ T–ä¢ofÚ[íf{BÈÖQ6å×MF´³—}·ÛÎt‹)[w+ o,+awhQá߬¹…¼Æ¡ayØ;G¾¹°ÑL>d¶rvgj‘ y3pô§fuñнzü¤Ot~ãÀlK†¸ö·àëÁGì¤c8gN2”ŸŽ¦'ÒqxÍw–ç‹ 8m8 OÍæÓ7>£Ý`4÷ð%³Ac3´†BˆÑ(³£Ý’“aTv+¤*ZiAmìÐL3潊GæŒüÿ3…~h§]ŽO…#Èp|79§•MéÑA¦ý·±* U¿€¬Ò ’ádKží*Å›oõ±u+šF¹*D–ÆCK i¹ywîÚA2«jP¡Í?kÃ_›—‚Øasæ½úKfö$[*+Aë?2¢{QŠ£ö¥õõ1F´º±ÙóætM&žSiýÖŠTÒ7©¶5ê‘©þ%˜<ð J|™•÷“˜ŒaÜy ÑXÖ„½Y;pYhÚú¬@]«½`òN“ó~d‹vŠâcI ÖHåw»ü®"`úΪѯPòp2‰RZ÷¯þr‹]ifÿíI6'æ «ÈXÙËc'láûTé²6ýèC?¾¿ƒwl|91k’‰ê܉ç¿Õ´-s¥1—eèñ=èø> Ooæ~ÿ$DÖ|(£ ޾€DÉõô“‘&8¥r‹Àµ¹ –M£åWæÑ_Ê–D×/›,Ï^Jï§7Ñ“ÛèfÙÃ$üu5gƒ—ÍØÄE»;ÆTÎú)½ažƒr]_Áùk(á/^†ú§Óo§•ɇJ~êæ"†3ÅÉÙ‡;qÕøVIT”ñ ¤2¢ „º„ÑE ëÙ-·å™Õ›Ã©fV‘öIÇÑ2ÕñW]iâtî–Bä|Hsøa"¬Ÿn (ôE¿CzÏŽèHÃäÃn$ñ^{çª>µ;L,~!SfÓÞõ×A{Q:ì%<°bµ*êíîeºY¤¶ELßád¸¤ušª8h“öÆ\VºõmØÛ Íwß1+yȧë Ùà¼çý¥sÀf¾½~²-_¢ýU l«žuVõußÚ©I`ý^i-[0µ¾ç³!›ƒÏ’=m‚ìÇo+ Ï :µüÆ÷n·3MÂÙ­m4ò×bšÀgªÆ¢t™ço¥ u8¨úÿlÚ`‡ÜpÖ@—…ߘ#œc~q{£Ò36 àZö÷øíˆªŸo DïoäT¹Ë£TŸ!õç½ µö6DúœùTfÄœ“(B­EŸ¹–zæÂ.rçþÑær“´WÔݨàbî¶lîÌ/3H«¡: y´÷ŽjÀ¾€¯À#¸ž¸m»kÞªÒ¥›¨Zà†›_ð¼ÝÏázàw´ÝÛ€ƒa¥`e :©»¨èoxó}ŸOÂÖá#ølÞÄó[2údy¨»h+’&µü"¸k>‰„×ûRó-lð(£¶´5ù_þ£8_nÆ2º¼ï0{,’ Ÿ4¦ £·9ú¯L1Œ§ŽÌKceº†A|¡9î™JšKæß`q¢n¸ÂPŒÜG«·-øÎ‘«É¡qáOàÉιhýPù¿D‡Ï$.ly{ ìÆ´>7a¿›.¡É½xüˆ!O¹¢&§Ù–÷Îx è±Ë„–Žr+žxƒâ~w˜‹K?ÀÜüÅd«c7{´äÔ¤Î&ënl¥±k®ÃÂÍéè/T qóSØžCÄüV?ˆ-1é} YÑ 9 ÊlZu8H®¢©wMÈ17Éúÿ“ê)|¯ÎO7š‹£ ãx#H¥œñ½]HehîÐüZŸŒ’6&ìʯñèÈÞäÜ?Iµ,XÙß¡ܬA~ðûü@¶I¥ûéeæ–ø:8&¥J,[¡ËCðøº*’Ì,XÑyVê­³ù²à"D{—suc·’­ª>t´u!”¾Ãí…vÄÒ£\O·“í±Ô¬ï;FØÌ,8yÛ[p’)ë8 2[î`òöùØê©L|_m‚RMÜxk!â’Í`Ë%EÒ¹µ•]ì¾™–å™0 q4€¿‹)¾ür„é”B_«`fðäù_¼N c®€}Ÿbá˜Ùfø1õ x LEãêQÎÃï+ð+—ÅÊ8aúx0ŠÎ\×Ê4ËiSµÉ°SÒùd–MŒøˆŠŠ[>’YyßÃÍßñ‰ß<ÙÞÌXîNƒy¾¥j§"•]—xïíÛ Ž*útëí^X÷þ¶g‡…{Œ©ðý÷(¿t^x°”yh2+¬´èë÷ûhåüL|²¹J¦L'ß0”dEï}Œ¡Ëýø0ÿáa¢6z¿~Û‚š Žh´N‚¤u¬¥¸üˈAŸ\ý´j|3§&!Ad“Ô(Ìm¼{hû¿þ(€w3ùñi\=Æ £6fç¶h¸¶-ËOBåR0¬q¢·#éµ¹ñ«¶2NËKg’GDIdÐ>*û¹/#jŒUˆ©Þs–'gÉÜä ®®¥ Z¹l_FËÝ8®Q¾fRûLS&þ¶]™6áÏN!wZ`ã+¬É£[ø>z*¹o°6ÍÂ`þ·È_<\X¹no$_TÔè•^=»$A§Ç,'Þ'^càò:&fÛ^ÖÝxf¿]ŒÖ+ypEÀ3à1£‹³ÚÙsÛüÇ—9â:¹Ì:÷J´]òÏ„F‚×/¯ñ߈s­¤ùì­ïŒÄ7^¦G[”oiæHþTd3¶Üc<³×+õ[{f²ö¹Ð°Î“ÚÚ×2C.Àé/,ç~l"Ìœ¥ ŽÆ/Ùý‡¹(1uJ±ÎjHlµØç5¯Ø‹gøè_97p"ÀDXˆªL†¿LPò gc];ðGø+Èž?‹–&9“]Ù·P6@.çÓr£ÿ gj‰J«4í¨þÊQÒ3BÍÁ8À/«åň†àkFûe£Åm‡è“骺!<º{q¹w Nö¸°0Ÿø†žsM‰Ñ§±z} 0^¬úOÿ)ÎnbÏ_5çž97;wùLܘ€‚Óò±óÓÜ…)¬qØ>ø<4ZW”X•ŽAó‰gØrCÍÓΡÒât¨pkÀÆmíl‡1ñ­Âí~ñ(ºÃ˜)<’€’3 ™QLönvgMm…À%ÁÍ{4`Ðå fõ@uŠ!wB¦³O^.ÇùÜ2vžÅ¼7¸"½-ˆèªŸ^W]xµb˜Qôwðµü=+c¨D([ÖëEÈrÕíÉþå¹Pn,Ù)S˜«í¿Ñ¬`ø&.EåÌ›}~*Nz›„ß.ãòÓ‡IÒï.X|ÜšÓªfð/þDMy®dÛ:¨b]IËXúϾ§ªf£eñd2(8ƒ¹ôk}yëî»”ŽC›½‰óË" =I…mô]±ˆºPüü)‘{åe>º( ?eˆ¤ûafõÇK°SH†ˆ—¼fLyƒ9£Üš·pÄm²#I+ÌhüÄÜë—#6t÷Ö“ìø®l6âÛv8» ¼u0J Ç¡æpÖ7' jÓIj|=W˜ €JÓçxbã"úöé䙬L›íëÀan.¸õ-‚ø}‚feS¯eš4_’”#†áW~ ÄdÉÖ7‡.ý‡ÿ™+°—OÊ€~úxp{ý»x2·åI³ã5ˆz߆<=úøÀLž\H®bÞT†1%šsÙ=sÇñ4 Ãác–ÔëÈ*µ5]ê†:—Ÿˆ‚‹ŸÛ@Ĉ³Ç*ýñkPÍ|Ù¯¿¶%£šÒR2™ºSþA]ú}^tŒÊuO÷¡ìÔ8òà‰)Z¶_Á¦;áOÒijÙnŽ#ÇWà­® U /qÃçå$Ài‘*¢]‹nBPæ6àùmF®'ߥ{a¹õ§.`Õš»‡n݇„ÛŸl´…ÎG*DHS‚)ý¢‹ú‚º÷9¨ÿèÁ¥«Á{kÓ¸SŸ§]ïÓ³ž<ëÄ¡”»\‹‰^ìWñQøzO«/âÚÊ6t}p¥y˜¹¹™ÞÔzÉžh•+ ]ð3òûÎîP…²ËÃl°î ¼¥z ŽFÈáQ|ThVŽIìCÞË ÖÜU&þDƒÜÙ^›åÓKÈu‰6ýÊ˜í¼”•‹Ó`»™kxñì;æîùÓ s”ÁAIy²¸†Í %º’$¢Bµ-9½Áf8Õ1ÌÁóðænÇ/À<ü)>{­J”ÈbËSM›]‚µpî*.Xÿîön€Yy‹q™y#ss"'CÕp@} Ù:¼g¿ÇF*኶'¢•H?T \Ãôžpøzc+Lz Þã~(®Šy.rxê ÅÒ$â@1¼Öˆ~Cúaݺïÿ§ÞA ÓZ'ÆâÊ:2ù´) .Çfç²çí+@«ú(®V˜KWƒíµ©´tò>əƕþÒÃé¯ÀË.°±j;³0Y€Æ«HQ PÖíÙñ)“IDa:4aË#.[?‰tï0‡wƒ)h;ï.,^Þ?¹sPKb/M/…Ž¤Õ v§‰Qšƒ?“OÀ÷3|x´Î ¦¹ã%?Nû¶*Ø;Î ˜âD^ _¥DaPJœNN¬Í™ýXÇ@Ýìpèqf-õ#˜+|Vè^¬ŒÉ3" D“Sx×2Î9oÙÓŸþó¿;gà»CÎL¦û zLj)is;Áúê¿ÀÖÆ†#`O_Î˜à˜‚ÓW’êBGè:µÏç;Ó×"µ0uçfRiz"‡X}âüË–\Ù7 q ˜äìÈÝ3ìš¡&Э¡êk°¶±ûNbò_I|hIŒ2¥WâeO}‡sŠbl6Ä›J‘ÙöBÌ×{›É\¿óRjL„7p8õ±LWbdR`OÆ(ø[˜ÄLï„Q§$xtŒ”GË‘ÚàƒÔuà~ß;ƒ;!·ÿÇù§5z] ¯§. x?„Lü¶(ú§ÿîØ¨âÚ«Uìûbdçq6¥þ «ðLyø:¯žÜE+á³ÊÄŒžräâû;ÓQÞp-É€L–•Ý{òï¦báÖAÎ|¯ö}õt0ŽXM,e™•[N‚ÙÙK`j=‹9ðõ%º+ˆbn +LEȧgêG|Oÿœ•üUœízW0«Ã•¥¡¨ýÎìÑÇÜ6»==0Ñ‹ ®sv~…eö‘ög(ZBÙg(´MÓèæËc<–¾`Ý¥Üé»'ÜrÂ=1WÙO ßÀjz8np­Æö O¬…X«sî^ØÎ~»xð_ýKÉÜÅ¥|N0Î3…ÖÄ¿F›¤u0~ Ó˳çF° C>Ì$ßÕÄZB–8Ý…þ€£÷[Z$séBQÃŵh ¹Vø>Wôx蜩¦7¢›ø³`Á§o¿! m~¬Á[pÕ@]7ªÃ²ëñlÏs®u‚Û„U`£K»l.ÚM¢+óYÛçAôúUneíSˆéM/·Ó#›ŽZgˆ|®]šÓ _‚ŠauëEÌeÝÏ×÷¹oAä§°òïĘßÏ`å‘B8tß“ž*B¯£¤ÿ$ ” €¨s³ÈcÆàÿÍøç5@z«Ó&úsw’AÏK™ö¼\M¤ÌÆar÷Z¶ï— tòB%}gæ:JQžÛéû[{™Í's`ß•§¸&õ Ö÷}D!תR]ÉgDqo2CŒÄƒt¦|K3¦–dá³³:L«¤¬iêe¤²¨×ûä‹ñK(mØLžï‘F33]Ò4ë $=øŒ.¿`µ^ôƒoš5ý: «Lð¡hÍŒNÁ9‹GA~Ú8X•ž¤ÇÝ2høÑsPÝŠÚ—p–ý>‰ErÙ×$X‡;ÐER7ˆ²sÏÄUûj`•4YÝe~H~ü‡ÿœ,O[ã°½†;ÿV ;£5„q=¼Ýå=H[à!F`ÛtÆ$™m¦ßÂà¸×L…i|ÛæÏÕV'»‹´°J¦‘]#¤©>¯Ø/…à¿í*VêÚs8+÷au…×pÕVªè§H6¸µá;2—Ø8xÀ¨/h­­Ž™0«ê€yÈ–³"›Ñ/HC÷f=RýË <ÑýÁw;ká]FLoÐiðÛãÍFdž£Zth½{ÅuÜu¨¢®%£‡™¬S€ )V蚥C núáèʵ̞ÁT†Ç;ƒUNÁãaúðn[q+ÊâVÿË¿ë#)6Лš\µ`ç2)R[=‡NÚ€»êÿr¿ñ5ÁÀ¡e|ƒn™C8;„ž ýœ[œ®2^ºt£&Oü.ºÄ,jéBÁé5õCùxÝ¡ü]ËðÝ‚)RôÆöÃè%ì’)@ÏßÙ(7m•ÍÄè{Ú’~œKŸÌûßAY?'¦\l 6þm¹þ­4fù“VŸž “G hÿøú®µ!ÇÒZX˳#úzp¯›’¹aªää¤SX±ccùdh÷­ÉÔF>$˜…¨µWü×)‘àŸBtƒÏ òvUœ+UfBø?ý3Îað§›8[Sg µªŽäòÝǘÐ7‡H^{‡äå'д£‡:fÍ™64"ç9l™cD^7Ü€[¼#Œ=n6ƸǼ¬Ž)C3W¦ã®*äól{(ïäR°&¹¦9LC…–Ga ±éx‰}ÊÌ>¹ŠŒ-ú2T™KÎGÑèÓmüà3 —àög™—u„Jÿ˜„g—‰±ß(6YJ7ÿAŸ¤SlNmÛ6 -?ǂ뗰 wV`cö6òFþø©¡–&äûf^ª÷V…+ƒý·8,Õ·xµ©Íí¦ÿ音A=½Çù gä~Sr¶™æLN÷ŽàÒ†D?;Së§ÐB%Ê~0¿Â˜&ÅKV±…â™úJŸäï’ƒÏc–p/:8Ôr‘<üÊæ¡Ï;cÒøÚøR¯¢Õ‹Mô€¨õª˜Õw=aLÓŒHp$׳sɵY§h»¦õ^$ S›À·XTm¬ÀDA²œó ÒÞE1]¹Y¸kRù¾uÁ@EÒðHžŠÝ¢ÅÞ~ø-É„ËC ÷&WÂ(_8®„…Š£øÌé ®÷lÃÁùF˜o¥…e×Ëp{§4(¬T$M3ø0‘gÓ\éy*R¡Kf9=ÁžÓ‹è’”6ØP>‰ÌÊ‚®W¹—EŸÀ²¶|Ò®³‚ rÞC®ðKýÿ·A`8æ2¬³F2K.‡žXÖÈNZ5ÌzjIü9j¸íàVHkS¦SD¥áøõ~¨ÜxõT§BçÜi¾³–Õ\AÕ †”é±ÃÙ¯éŸè ¨åÃÓÀÉ&–+Ò<Ár"1|àÉ:¢¦Oß®u¤gOú‘CCëˆ§Ó ¢KúÙ²}uìËà…X_º¼vØÐ]ìØ7)[¨Å KÜäP‹N¯s‰ËÅ<ìëߎ¼AVX÷ ”JìûƒëΤ°|"é&I2sæt76£âuŬódzwæêîìÂÑÒDëÎ3ø«Ö‘‰›üë¶þ%GoþWÿ\é5xꦖݛ†‡¼Ìß_Žæx˜‘,G²gÝ/¼dU‹Ÿd* Ð# .3²;2ÐiõÜ59 ”®Êcñ™ ¬çÙ1xüí©s׿ú ºaÐ}=ƒò¨Ä“nþ °Î øØwؼT›ØÊÁrÅLæØ°µ»¬H-bá½™ÚÜŽ:Û¸3n'>¨¨8‡eZ¿À`[#zçºyAQºQ7ƒ½ú—Mµ< „*Pº´ WÚeîx Ó-“À"ùqfü÷¿sÕÃgI7ÿˆBq‹N´U¾~ùD–'Á¡Ã‰ÿð?Üt'Üž^w‚ QQ”how%oO¬&KÎâ§yôHO18 _A 7uØÎÙ—ÄdÀÿüzrþžñUÞ…M}g¡Ébˆ5µ˜ †³¤È§9Qà~v!lҹȞ…Æó¸Xô“÷"Î[ i•%Ð\ÜÚ¸xE$´Î2 jµX~ &ÒÞ–¼’׃ã“[°‚ÖÈqè™Y³h—l/ð é¡‘r¬x_©cß™4 'Îü‰ gu¯ˆ$ û™'¨¢µú¡ zè S¦1κž†EoŽAõôup¬Î;þ¦bfòôõ'ÀÃ×…§óPçÀz<ðãy¾J$éú…¤u<“dðbÕ¹Næ®ë2r9›L³O@ÒÂ5˜»£ ŠÝÔˆ¬Ÿ$ÑŸG+zÞƒ¢˜-–6Å!·n|·Õœ&žÅGDMö rŸŸä$ù$àgÁk¸çþöÑ×v2dW%>ÇíõWŽ@Å)Wp³³¡ïc‡˜WÏ®2~=Húû*ÖŸÞÁAVÝ7i.ˆù Ígä芬( ©1§ç2X·ô'(ÑЗ½eî]¤h|ΘÌ-M#^_½0kÇdÙ%D†5TÈâd œ7Ìü«‰ýGá«ÉK&XO‡TVi’¶Û2T¿,ÌÚ«Èù¢rlu$RU(ÔþõgÚ’s§dÉrè›ÚàULvº§¢ù¤Ûà/œ¦–sˆk£!|ohö>®ÁtuRñZöùÁtKSZl|Žã(Z'Ãè$Ì0¶#ãiù)G:$AžÕeûŸ_ ŸºWC•äQ:ÇYk.]ij¤ˆ}®µ>iàŽÚ±Wšè7(ÖÛN™ð¥.¦~&å¨fÊÞp‘ÃŽ!-à·'B|œZ‘0ŸGä¾, /òU1;ó<ºh4`þ]8Ø\Žž3þwþ½ºœ/_4‰NÛ&˽~ó>;ÓÍ*}½ßÙÓ}ßáïÔ¯xðã ²ós&\ˆ`jK>—æsØRf¼Ñ ’R¹KŸ:Ðcì ] 2¼Å¹çUHÁz~:ïð,°¿*Cïý,aZ7‚ÄÌœßw[‹±*Š—,]íHFò1ÜgØšJøG}@BT„Þú͇ rÔ¢gqŠú„i¯¿3¹??°3Ï~€š‹pþe>ÚóJKÞb~\½Ê,³¾È=ÈiÙM®Ôï°–^­]…X5ɼ­à œ€œõ$峘·V‚ˆ­¨[®ò?üÓ=ÿ’‘U¶'RΙdÿª“øã=µ\ä /ª i‘Ù,úíe%'–:ÀL+CæÃÚy¸/RœÅw£†ƒ¶Ì„'–Sÿ9 ¤f:åÍ ‡ÆÂaÇkîƒâÕ4.Ær4%è¡­îôË™«lI±:É4£>Òa/-¨4§3Û.96g»<[Õw¶ªÅbY] nr4¤A¡¼$ÖÀ,¯øyG>Yá)i&:D2.ˆd$-ÀË[ŽQo~˜Cîx —s'“µ(¨c“”ײü…!t~ôJ¦óÙ ´¨–þû‘L÷á#0 8Tòä‘ügè8Àv_þ1b,«“† Ax2Ž‘â¤ïo(Kr^°ÎQ&¤š§=ìo@ÍÔ°oã=r  µ†bèsß63œ·†რÅô‚õq˜²üŽI\¤Ê¾¿ˆË¸íY;ݨB^W‡=·ßÏAdeW$}üêŠ/}Ä&~:BÖMÞNë㤠ϡ@ôÀE”$«ÓÈX)¤œ›YË ‰ŠéLôyE:¹óÊŠ\k"5¥¹ì¢2 úÃ~LªI~þb¨Ñˆ1ùd´JFŽºäêR¥ýšô¹Úz¡ÆÏ/ÆÇªo Hïé?þ7èbrOš";7ÏmiƒŒ\X/X12ýßãI®›Ç2¥Ì¼u~’M–t‘fó¡n;à™Í¸œÖÕÍöÔûÍJòUÔžÏlbÞ)±P,Ùß¿š˜I¢CFšäó·¸{ç „Lä˪üZûçÃÑ‘©$`©j-ŵ0ÿ` dÏ´®ß&DSõæbµÐ0ch#Î4éuãÙ£j°ïü’gLbÖ”`ƒ¹ ý û_(?`ö)»ƒ~Î$R® µÏ›ŠŒ™Ówõ¢TiW'–Î ?Rôˆëoqbsf*ã|›ÕQ`Pæþ{¯%LðëË%Ä]+ òÔæ‘§—feGÖºÞ•(h”Ó<…0&àS=õÕ=Ã<ÃSvëØb»tL»vW”"{rÂÉêï°âh4.Œ†&³zöžP/L;N‹c¤ˆä² öÁ/Æã¬/YEŸYŸ†Pæ9–~.¤q6ªæN´jû~vÎŽa¶ðN鯠IaµMck™Òë ´'{9Rs¡„#³åïóþ!-ú£9•³™œ_†tìÚ\Ì͘Mn#y|·ˆ¹X`S„üÒ±téö`(G¤·R±åçÿó^kbwM»Î­Þ„É.‡¨ÿs;ò2y ‘f²6œŸ¼[XŽepîw4!Âô·Ã|ÕˆkÜ"ÉIþUløÖùŒ8Y,M9OÈáw·Ù´o‘ð)=—¸Û’e åéÌ÷´á–'Î~¿›ºÓ¼²5T™£â†väþ;/rbì)Q¿gOźfÑÔ th\Iãì±b˜«ϪÄ€È/YÑVˆ…§“ñvØ5l ¾ð·ÔžÄȲý*¨ý:³÷8=+]LìD’‘™1$¤»½›Y Úý`ÉÃùõåàl/ÀÖmmý§š¼¥8A&Ø Ÿ…$ॢ=ÞæF n@Ëü‰zÍGå:#üe2~÷aA”&¬ÜÁ>Þ¦Äx%ÜaOßxŠò|¾ìXk8©¬øŽÃýE`baƒ—#JÁcc1«©XxF‡üæ»ÉÖ«9Ñwj©hR nIˆ¶Ú¸u™»v¿/ݵ4#Óêû-£°¹9Þ®—%'^|€ÐkÒd®þòú­†ÅPqd5޾Š+V‚™ïœš$‡ÏV}LJÛ+ð¡ úq çœU†‹O܈÷Œ â%÷tz*°íÏCPWÄŠÑ!æ«J–ˆþ«ÿwÞÂåàTHê>«×7bÂܧÈ+"Lc2ôÈÎ"â{Ù.z}šÉè;‡+W- º7¥ˆëö‹H‰â-ú’ŸŸVŽ™ÐsýhFsM# ~ „f9Ò´×óé!Ç› #­K ûÅÈä}çɧÀ™D¶PWx¾ä|.b–WJ’=™ÏàIk žc‡÷QmrêŠ5¸q nYÐÏ_Û™z¯P¯Cˆ:†‚»tѵoU×…=¶Þbj¼cñ«‹í½ŽÉ¿à’û$ó°–sŠï2äʇ·gƒÈéÙ·¯H`“ךՖáÿð¯Á ÎoÃï_­X­Qyòb™{K˜‹{ðcÀ{nAÛ6æÝw_Ñü‰»ýÀÅì~ó0fÒ.êA¿ŸPH[`æ]”Œ Aç}¥x±â=¾q(Çþl®.æ$=¸ÈÒùv$múzæÉŠvvÕõa˜jÚ‰Cé=àâ¶tï9cñÌÌþ¥£`Q‘ΔßH¿]o!Ž_žÈè{CÕ¬iô‚Z/&[H‘1=úì~î÷™¯Wà¾/ZŒ¦§6:”)“-—è£Ö"Ƶ°וßŪа:ô ³@#œÙ0X‡©iûØmþwÍT›»ÿ«ÿåS÷²iW~ro3êä¸Ú<¿ÈCÞµ+’N1YšÍûÏް !dÙŠ »4`M„ ýÜ-B¶)Æ¡XLwăŸŽÎž 3Sà»äÒ‡xµD“’Ý*ìÓ©ÈX?gŶiÑc¾6D±¬^ur4—„aÿ‘-(ØXï¾™ÓÜöíÄv¡%-?±žÉÙ(ÁnÚy–}±5ž¬¨Z†×ÆopxìR00b ælŒÇUI„¤[ÌD¿“‰ý Ì>gÅ8œi`\¶ð1ýz4ø£;Q™} ÛƒmèÓ¯%¬üž V_+x_–âæùÁgKü¿ü·}Gî­ û¼Ž|ÌOÁßÓøaÏô°-ë/Û9áÛ,GçÅqtšÒ)¬žÂN»:ƒøŽMíGóÉ'Ka,ý6‰z˜ý…¹·Ÿ3Œ?LÁËI™ê»)›wYÞöhª¸í½zż?dàòôOŒcMæmb»F%Iß³G4ÙS˜Îh ³ž¢í©kÑxå^n\Kv²Áùs*ì÷ƹ2ØCû»q^<Ýcr¹uÆ޽;F?%O³.Òq£ˆÆ, .Ir´î°+±f„É‚çqL•ùl®Ã†(ÖØ;µˆut)dLbn=‹g^D½Ùd‡«:X¼T »–IQ­¶­6uóOÓÑßÀ*܃¦ù$cÎ x5Î,YC¶\ë‡V¦×æø’fø‰ lNQ £!‹±_?G.?÷Â4Hø';q&äêÃãG>´íˆYSû”±®9qK˜ÜoK°Ô*•iM\Æ™øôvÓrëi°v\„suh!þéXÂkÉö„/ðtu6ЧüôðýK¸ßõ~"ëPAÄ’¨MÔ¢ƒJð¶W¡Gýhœg¼ՊÂ{]1\¯Õ†¤òó3<·l'|ý…WO1̱13Ê‘,‡¹¯Ï¢Ÿ²¡ås‰È|I®™A+ßD‹Jsò" ÏdxÆS‚\‹tÖ mq!äÒÜntZ–‚¬i!8Z?‚ -Îd`~&Í]t6GÁÒÜŽÇãúÎò ÄWÓ?äZ¬ËZ“ ìgòuIËÝ{ŒC?ܬó"Ó2š`í&°/ÿÁùµosn±|Á¨aU:ݱÒ§Ï‚ßO ”ó eôµñH!K¦&"î¹ÈNQC¾TróO$svô-3UDöJ_ebÏ_Þ5—à¿ùô•âqáñ!vÿ‚Ÿß,&[e ÈÇ#aÌØç‹ðX¶˜žŒV•‘Ìrb yƒÁWFÆß<Å÷ęi4ÝÝò˜^ã;‰¹K^ Ÿj4ý˜ŽSÎm <þÀ/Ó_˜ÀGôOÄ+æP«E¹*[^ãÆÓި밅|ñr¤&Õ${qÕl#r*þ Úç~Ʊ÷ñ4v‚ëÏ‹ôrn%Z'ª#ŒôeZ“Çl¯óÀ*¾Â.í‡F©"""O B¦PÏ¿[©èÚ­$³·”ŠGÀ_»qøu:­ž²Õ{ÿâ” *Ø¢Â5Sh×à‰gs„x•ç_ma/[Ï*vßY*ÿî>òUrË å1çyô0ýŠgE‹9ó•ß"ÏÛe°ðÅBÖò¢0íý0ƒv»Ë£ÛZEÚ:5 Nžæ°W&³Læ10~¥‡íÉoðoV5[蘃{ÊSØÏWëð«Xër:˜JœƒS•˜‹Î­˜Ï³žjÌÇœmç1üΈHØ*z:*ÞÌ=ˆ"j¡s†={×{-œR~ Jg³áÅÌ,(>Þ†¦›¨ósé6›ÐCR´žÎå³eã[”&Æœ…ú”‰ïŽvÖ~cðY ±7Ru¨ÀÕÈqˆ…~eFîÌmœy±÷zÎ V”0kÂ5Hz5Ï™÷÷ÃÏUÊܾam¶L¥¯ |ƒ§Ýyàyk¾mf?†þPôÄ Æß.ê5vÿÅòd†¤4Ùü¶ ²ûr@§Ù‹ýL’ñ‡±-¸™áÞ¦¸¡Jt„Éî/¡lÚy¸[$KÅ.ÂÍm¤‡[{™Úi=pQ ¥“éâ~Ð^“ U³ÀH¦¤]‚èºÊ{²å¸:ì…7ñˆÝ\v+”ýÈ~,ËGõ¼OÀä,þ7þ«¡ †¨Øö><¡µ‡±#ƒh¨£bõÛ¸~0“aç/ì„ÃS@±KÙKIGY [¶07ò÷™&m+NÅ1wå£F»‹ãþ†c š²ŸÊ}žE¤8Ùô`ò˜ׄ}*GÐaw´öÝÀ:•Ùèsw6]ã~D?gCÁ¹³hµú,³´U†¦”MeÞM‘C'ýEäMåôò9ËÈ+"Þ­„hüË5vºËî3T¦iåÝ좟†4™SÏ "$lÉo¸J♯Õq½m6ü;-ÑKðòÕT<lB>UY] jKφ»B‚xÉ1šŽðñqö¹R³Nú*ª–Œ ²Ÿ=.0ìboØPZA"°Tü3>p’'¼µm0ën(¼UJ"_-™ƒn0;ž"ñúSý=Ö$ÀPm2`ßý(b~Â>ºw‡-ÍÔA¸i%G"®…–N¤wSNÒÙBbɾ[‡?9yäaábÒ‘þŒ6O¢‹û1¡ýÌÛZê|ŠY2k+Ƭëá¼jfßÜc“úè‹~gú§7’؇ À•$Q^—ɽ¯ÐàÒB+ÝL37ØÒ­ ¿`,¿“v’‡÷ÁÄŠ^Á|ÊÒ‡—£ 4ö¹9½¡úCõ~€‡W<}æíŽâÜ—zü x•… Ñ ¢¹ï¡·ç”Vw°ú@Ã\!x•a:¬h×´·‰BÈ„¸øÌ±ð[¸üz=o;ܼ0½Bžü ’ÆfK ²a—#³c¦5qX4Ħ砫áÖd^'ò²}) ¿,‰ÖY™0ÆiKèÍïßQÄ[ö„Q©\‹!Xv œâ§’aITü†:ì^ÖÍ|²T²Þ8Å; £é¯"\óç<óg‡ûåõ²õÑzN{æ]x~&™·Ûðùã_ìcñ;Pªš‰¬âO'úÝ]‡6¯“ £ÇÅÉÞè,ÎC;?Ôé»Ã&º9 á°|Ü&œùÄËŒŸ}/ùþ®@dzÝ1æM(î”_HÃw†¢þ{º4¸ <å¥%ó&ž %önŒ:iÙ«EœT¬˜îåúì‚û¶DÊå»a©/®5nƒÙFôÑèIHxÕ ¶f?2<™À“jHdSƒûŸ-(YâC6ç ‘âZØð†LÁÎH¸gü'æ ;—t çæÜ^†ÝExaÑ{ÈUØL¯Dß'RÉ™TöÌ„´`\Í÷¦O#Iû¶°£™,Œh)âl»bèzÜÂYvöäœEc—“TšÇññžàL›8S3‚Áϰñ“¨ëEžÆzÅífxt¶:q±ø€'%²É/–´ÔæU2yÇf¡Í< &R^o8«¯øsÆÞ¯÷¦ìYùü+šÙØŒcäÏ"BfUœbW­h‡ž]fôKäÒö\c‰?¸;<Ó¾.‚SÙ ÙŸ9³$fbê–ýtždSÊÉù%ÞÌÑ9”›Õëæ=c´bõiðù/°Îü rÞÙ‘öMQäú›W´|DÍ|!„Ã.€Ô¤Ôöïv:Ð3˜…qð÷ùqjþJ€õxÌYðS”V زG[21èX3Íü$X–“B°€‰a º=iøõÈur¥•*ËÈ~¦®ƒŽÜ}jkÓÌÉq«¥#¹qDÍpêÙ|xpGšº_¤s¬¤àï<¬v2!ÚÑEtæH[ð;šVòê GÄyµ¨`Ó"ÚYîDÇ„9Dc0’l‰Z w?å`‡›3Ô¬?EÝô.ê¤ËdÃMÂ[³Î”šÇµÏò ¼NÔOa)ùÔw:V’U_ÉôOhṏ®lí-û`ªuÿ& Vc_‘ù]Ãõ67ÕÈ&I=šÆŸÿ,gq«ÚŬ|zŠ æ1sö>À† RŒ]gî*zÌzéÑ¢'å:ü9”ÀÛ[A^§OåVà}õ&˜9hxû¨ÜeçlÄ?´ÖiìäyÉ•ØWǸɩ“ÏÎ[pò j)ò–ž%.ÅŽãÆòªö®½ ²i¸àÄ8X,Ö¥©ìl6Gë$ìôÒEÁu9ÄÍæ„›GÃÂ͆D*ņ­Ø ûÇ.×—t§ ÏÚaäZ¯!Ó§Ðàý§1It‰vhcVïŠÂ“Ûñ¼ãUô |ó{/CE|*ŒŸ×DùïÍõq8Öè…SJ‰ôÉd¢WaS>ËPg>Ü=…infV?Cë“O¡áì½ àÔ@¤ñ:™¤nß8\Ã|Tmb°˜&øÞƒº;©DoÆg´Éd$'{“Ï9VT¶üstÚ'ÐmòFQñlnA ‹ jWq·~ÞJ¶=N#—…Cý_^:èØLÛÖà\ÿ©D–è¤ã—m5ðô· -hÚGæv Cx–=E—ú+AÍ‘y¤k¹ [x– ^{u@p^îß®m?'“´+¸ëÐ(OYJ&Ö«Z7À’daÒž’FtNn‚Õ^äå5¸9ÜÊìÌsÝŠq;´‹j}P'mú u,¥y·¿Üd¿(‚¼p%~A Íó/A:'vÙ/ļü|;•îêá!²3‰Àó»°¾Ëνjc&ßɪ–LØWª1°>P–]Ù}“9/^Ÿ$Wc·Ûwv·®9Sg›¿DáUÑ{¸×·‹V´06²¼6k×eW"£Ø~ýúç“{v«YàÃZMª…ÇÏjqøøtqÃ5ïñõµ-ôúÇx¦àþoLò+ÁénW¸o.ÓoqÍ‹ ë›_XùÍ7™ÅÓu°è:´Ê6`N3—º\:Ílþ"LŒDUÀýÑMˆ3àA9×PøN #+Ϭw»ÝãCp>ù-æ›c­ÄulkR£Ï½ì©I}&ª:á>[×ïPø¬„7”´ jz8º*ð‘kVÁáL7”sR`'Ý¢áKµiç>×<ÏVñ·³»[2ت¤€½ÈŠˆÓ¨Ý䞥 -˜Ôˆ_Êé¹’›ÄíTµ‹zÚߺNCÌóñè A2¼{S}õu;:ýCiŽAîÊึϼ8‹AŠTm_:»|A) òžðÅìß ±`ºÕ ÷:Ýïª`ã+Š?.ýbL«æÐ¾ B„U…òC¿`ê&Ä©ƒÌò±¬Ÿ8ìZrŽ[ï@Ïpm¢?ý,:»XJß/ ù)á¾½Ô•w-Y8Ïš|)ÏœNâsµžý¶!±.•4ô–‰´ §·”‘¨7 ã-OŒïÁ¤ýýxFE(ýü…F–‚äet›ä¯½ 9­ ‡óºEHíþdbop„5ë8 {‹é“G²îŠ£)O#èm¹äûß%°èX#a’XÊ*É{Ø?Åç‰÷{òYͼµžJßœK¦—%ç!¯¨8-±tõEÇЀtÑCñá>ç<}ï‚«Spñì rŸg#×A}3í•Q'«;`„Ÿuµ“ÆŽŒƒDÈX“ªWî Né#õGÎèïR òzVî®{…OäN°&3¦Ó–Ç7äÝ¡Ęþ‘Ót,ø!<*m‡O©´ªkHäøÐõ6óhœ`4«ÿü(±+ßÃM´¸ÃloZCM¦åB­ÂNbö4jBTÉ=WSºŒðHÑLzð“1õH²¢I‡Oâ·«rÚéì¶'è°cô¼«¥Þ¿fÕçWFr^vý{âÈàÈR¸ê‘’ütéü Σœ#äÕæ'äîS¸î]‡‰óÓiĉ,’gÖF7~7§3¦ž$Z|éКˆKzFXE݃œ7¿#À}úcÌ=ÿ#8ú¤%Ú•N]ÁñP.«¿+]À6x(Ч¢þ8\¯´íiÄÙ;0=ôóû›"Ù÷ÐL·…ƒ?1ª¹ˆŸN`ÅÆ¹0rÜͺõˆ .ª!Й£þ¢€½Ãƒ‰1Õ°XÁÌ}:P°€<•´¦?Æèý ?Œ¹ð02^sÛ¡£)GJt7:ÒûYmlÜ:›´Ø?cT¾Ë’…KÆYwƒ[0G~ ¬~Ò >jÄÁWºnñÓå:!ånüXI­Ft1xr è(a¢ß§3¶ÕQËb)†£¡NêÊ#éê7@âT/OÔÛ0Ô—Ä™ânpÔÔ–>!b¶^èá%ƒ­z3àÒAÊfêGà:s%j(\ e+ºQ÷³!ù²ÙЬ½Iû¾³ÑZ¹4_VÎ <ó—“’ûŽä»ºÊœ‰¥½ݹÌ$ŸÉÑá§šÄOgôÐ÷œš÷WYçÑ|2ù–; /1#ÕqÒD17‹)ÚVŸÖ îšE¤l™ Éx± ŠB¬Éç„4roáuø7“òó¬@SÓIÛØm%=²oÏ“¤Ã÷(Óû#¼¾,HNÎÿﻩ­é·_ƒìeÅtêê)Ac†ï!ÿ¥édïf•²6¾=t”ZüÞŽ½tž`§“ö„)ðj“)Qo–‚?~³È%zbMáËó#h,& /tû0óŽ<½se)ÍZµƒ†*³½£I”$eãÄ=h°ycáÄB“¶?)èÒ¦G¸… Qì‡uâHÂ{0“ý€¯®Î&K¸+¨ì5>’hIÑ æðp×#Ø>íl¤|4T8ŸgmìË@¤Ó‚ý-™Ïv~JË{˜Ö…Û\Hû·BÖFч,NSÂ|Ÿ,Ô˳S³ÈåyÚd–ø œYúÌeH«“>=“lCr/¾©¤%à}è>+op×3aø³Z›ÜWáò8WøÞ%å¿Ø²ˆ”Mà%}æ Éì¼pÚ¤t ¶/žJ;µazN¬ëƒ!G¸ª>b§šMÚXͼ•$I¿‡Ø%ü 4@Ø/ÇäP\Pœ¬”TB k_6@&ûÂ饶©Twÿ8º6“Œ‡ß‚ì<&ÞLµŠ Þ«u¢ÁއiÒ¦{ÀŠAcúõpJ…7W "À ãK/’c¦j(\±–-*æ£þ¬Ño]ݲö"³m­<½`° ¿Õîbú5²@ç¡=¿H‹ Í[„ÚôÉ"9Â5>ò]çY¯…SéÞ!œjúƒÍH5Æ·ßyHr„ ɉ1bç}ÄÆeÑÒöâ½e왃mõMÁf¾kVµøâøÚf|çÃGt‘ˆ™ó¨W˜%ê~g M»߉ϽÂ%þXrl7âäR` ¾#…øU¡HM`•®T |yµ-o®ç¤ aÌñBô »çNV°ÇÞO%íî¶û®'âØ›0ª| 3‹O>dLÇÞ2˘-àDa7Ð_ÓŽ”ú¡¿ð¾);‚<¢]`ñ{ùT¹;´éß¹€±U³a[f?2*Dñ© ÍÉ­b{‡r9±Çݨ“é8º]÷`ÝÆ•É= R|MÚÍÉG½+õîQ¥â  ݇ÐD —ÏÙ–ôäóÃøÐ{¨©ªÃC Cäd.3Ö_–0újWY½ã½ÌÝ]1sì>—¾„цa8ôÊ•µß EçGAîoIÜó¤?kq!ÐA” .,gvì=/ _²“ŠƒÐs®ÞnQB“ôY6³`~šÉ‚èÏ—ìOré’&ªû¶±úNK §s-¨İòå+ê­e9%¡4Òö ®š~¯Uiƒ‡d-„XûÀ÷€,tœç‚âKqéÖ\,è„›/ïC¾u<³ÿT'¯y­†À§¥‚Á[/£Éóò7¤»’¾áWË›`1؃G“#ÑfiþÒ[I‹êQ®×–Þš5QûUÈëVŠz§Ë@Iø,ïqžôÕ¼Ç#zdκZ”µs¦sO±ê÷8É×™I²W™õÑ_Ø‚6Uœ2|Tò=‘ŸWwÆ€˜PÉGÍ{3†þÝLqÁ„ïÞƒÐË+N@¡@]ô€‡,›ðž/9~Ûi«ãRÐ/†'ÞËɶý/ðÔb:xȽªLu¨4}P-E¶¼dôuáÛÍkIñS1ºú¶*¸=ÁÈ”ZÖpžžŠÃijd¬h` Ê|U$MêÇé¢`.½pJÜó/…i»–AáÌË0SõV¤Æ²ÛZÞ‘‹94¨v ³YÆïùϤgnÚPAí©ä„Þ> 3TJ!ȵŠöZpè¿LØ9u¥·p¼žÒ¸3FÌCç®ñ=3ªcˆõ)º´üý0wïR7Ú5/‰ýõÊG5hùF7Ößããg$‡ŠÞSh•¿. Ï•¤»/fáÛ½êôôD-]¥1g.âUÙKœÆ¤•̧ɓȂ¸ULÕ ÇîðÐtMòíL lLh@A§QV5^­[|­/UBàþH<;¾>Ü<Àî1l†}k¼é,WÖÓø1ôò‡y?r¸w6Íß ßM¿¿éÁUgJI%¯,=&¿‚[Í‘Ï3Œú–¯ÀÄMî$x²òÙÄŒ4†æ†¯¦s‡KA< ïñO¢n]°a‹ v¯§Ó¯;˜EÖ'UÃã"KöíסLš™dº²žͪÐÚ(Oh&C]ùop5¹‚2ËÞ#|èÁ×b÷¡67­òÕ—‚`k¦û×/…grÞpäná ëÆoÊ—¡ÄpDzô$Ó³ºF^‚]_ƒØ-[>3¿/÷`³ Ó&jLKÖqÍ ÏÂ+‡èM1ÂÜç‡Ñ÷‘!T ®%ÙŽDë×(›Ï3Æê$ª³ ¥PÉ‘:Nä7uà|Iãâ@òeøþ GàÚÝΘù>ª¯Ör bD›9Šò‚îÑÉ$ËÀZô‹ëß´³*‰Öä¦o.#™`Q™§‰ýÕ}ŠÞÊŠ"3ΙП"„YœÅ\ÞÇiÏ^ž¿uÉ9aOÖ‚tcT~óP9“/«s}~Gbqj “j…>ÁçPÑàŽMÛCæ!Î~Λý'PÑW†×e ç>|–†»5;YÛÎ@°ôXJe&ÝF¹Ÿß»‡/Td2>±¯êÎØøoÏcÞ¶8áØ–X¦[ô¸éQ¯?sɹµg ¯@™4(¹A;Dˆ €ª ’CÞlöB/n\¶>Y¬Mš÷‰@hÇàõIsm1{vvr¦Ãï¯RØ»ôÜ^Ë‘߃8¡x­ÏȈÃêyÇ1wŸ¦”ó÷Ni8Y¨¾.,ažÌôF©SïàÞ%xéc‘UÉÜðÌwØrY†Ìs´¡®Ö4.. æN†¿2ÌÕƒ=ÌÎ0f|]ùsM„Ä|Dï$oöÅëDÖAø6l~]ƒa¼÷qâ^xå®LyÈZZ5YŒó¥¨ßõï¤μY$›fÁõ²Ô?)LpëtÊG•HBYsýwÞ°ªÅ%_AöÆ= QŒW…›à™‚ §]# ߯”»ØíÔ/dMm݉’°£÷Lšî½7 ó¿ï€„ÁIŒcÿ|rõJ$]%RÎÖ=êF}{{š4Ç•¤¹þEç÷Z{݃¾²cGwå¢TÀ6€,š.KŸ¸SÎ%´€ñ LËŨne²;^âŸ7±G~NÇàÐL•%1²Ølˆù¢ú’±±g“ã?Aí t©êäÜšïÞñï`DÌàažŽ+öV„ñ˜ Õ„Ÿím~#ã|Ô#ò1ÔLuÅu3™¨­mxgƒy÷.ï[Õ0¯„es'Ÿ[f47åöTÀi­aøS¥MØyó°¥Ê?Ø©2FÑù¬Ûê_Üö£ÆËÐÄeš°Ó5·bÚ†Ÿh¦%„Ys J>ô#"asáä«®G‹Ïñ¸°éFªL&g…©žÜWhSx=…é0mÁ ÎtLÁ}«ªÀ*é$«éÌøì=Åœû /¹—íuö¥%•±xðû 5gpc·9–)ŽoN)Ðw3™ÜD/fÈlŒá‹'îΕ œr×UÂñ¢i°­å~{§KþЦ¯}Íñ¯ÆTœÝJàƒ »gK=åè†cƘäOY_”ó­Ó-‡qéÑ ŒÜ´ýo°—N[2fîD9‘0^µ›EÚ—¸ïí^ 7†ƒåý·xCÊ”ÞN Å£kgàj•¿xKÜ ï+SÃÁà'Þ„Q÷¢µ~04[ÅÁïë;Q«à ®UhÄ/Mh ï2ìŸ,G·ôLyãpfζPÚdz·wYQ¸çÉüåo­Î+¨˜µÔ®*±Bü¹¸}E’Ô¼ ¯ÚX ñ‚÷‰ßPn¿ ÷OeÒ–fs—¢è ¹Ç ˯; Á" ú-Z˜#tN‚Í^Cƒ~áz[+HÙwŒµ–ÏÀº•„|H<Åô+ÆRQ±ÔúÕ)§1xL•Ìá„ä'3kÓ÷³æ’/×â믇É)-+¢&!G»ôö“•Zï<̦ÏdÜ©vÅÒs¤õ7?cU»!þënêòT†*ÇáÄXìþy$üëi*{D¿ l¢õ9Ì”ãmøzÖ!ÐŽìÂ'°Èï/†”;Aƒ5‹";‡˜Úáø©Áe½jXÑÔsd:~йï´×ÁØ—;ðëž„—݆½ Ë¿Æ<\· ÄÈ pv §ºQ˜’?Wá²òy¶ê– ÏN„ª¦lA;§&µ•5èöƒÖjX¿k#­¢ëp^ùDT®À¿‹Àø²1«qT€ èÛ’o¿’°ã²+xžê‡EG¥Ð×ËŒ|ß¿î›zÒ˜uY®ÃZà'¯/”à ¾y$Âøì•4DŸwÓð“Æ | 1ƒrŠ—ÐegT0ûþtÖ¦l\Ç]vkâ<7=Ö„2³ƒOA­=q’œUŸêá¦S4yЇ-w è·BÌœL›_r7®¬‰ÄQüüÒƒVÑ1˜ bÔŽ½ î>‚ƒïòÒ¨Þ‹,¸Â¨€Ö€4ó{̸‰À)2x±Ó€”Ç<ǰ7[hÑ4?x娯ŠåƒÆ–‡øõ8Ä÷Øã…¥¼†—9!¼šÑ_Æ.{× Éɧdgå ìkNÅƒÏØ´Mî(qsù3Ç›ä Ü`·¯ß@<þÊ“ãÖLÜ÷=ôËÁ6Ô¸ŽÓßÄCa;¡¯ùèò‘[õ¿ÍLè¤u 8ÕÈ~Gp†Îz‚ÛÉ­¸5KÛ ”é`Í*ÚfeB÷U£‡ßÜg†î²Ï'´P}ýJ|½¯×- _¬¥k[…éÀ_1r`Í-œ”Ò5¡u›ÑÆa»_Ü”oÌ£ûM¸2œõ¨çÔíø÷y šÄÀzÉ7Î+@É“Fö`ÂÚ÷¼o. "×þöp:޳IW:ÍÿG׎ÕûÆí•‘U²7…d&ã=÷#¥R²”–Q¢=(e¦RvvEÂ{îGTBÚ‰hR(44¥Ÿïï÷:ç:ïsžû~Æý¹?ŸëºÏ9Ôºt'q?î›>êq­$ˆË Cü»ò-Èò(’ñ;è·mW±êÚ ØµÞƒ\Úq{磘Oü]縌ö4%’ª DªÔêAÎlj 9Ö³³:Ù¹õt ¼b†$•Èž]ñ(þé×Ì<‚†â<©“ÅÔ^î†gë§Õ7Óñ­Ä<0¼,Ê[2‰M¯¹‡MvÀÏ]gÙ”'ÒÔc¡t×o‚ô¬“lÙìÙØ2êk2ȃZ j4ë5vÙKJ¶»£IÔ[& ™†âQF>t#øû =;|+YÞw÷8ÅiB•3ƒÌá$Âk!Ñœ"ggõáÕˆPFåã œ“X-12sM±÷Å® Y<Æù5æ‰8®×ƒáÍ3àIÆ|úObH7æ\F…Xbt‡‹;ÿÀZ{rgg8®ÙY‚Ûzf’–0R1w‰7žCÐNŒ\0¯Ã ¨<z‘@¶í³ ­pãÒvéƒaÜpä&MÎ.Ä/§§‘Ÿ¼ñTëøf®s¦ñÚŽ«æ¶û ?ÚÌTÆû“÷KvãÎÎs¤Á8€$²èÛ©híÐ_,ÝDJ;m™žH š±t#•¼p‘pj-hö·RnÛŒŸgÉssÕ’a®°·y<˜|ÜIâî@¸C8|ÀxÖkh n6rÃמÑÔgýqæ)G‹Ægå@Ào!ºAZ¯áXÏrL¿¸›ØXLo…Û3>Q7¨öÁbú¬‰âvïHÙ\Ée|¨Ì³l(Õ[Ï]2ËŠdíK%7ž×`ØÚ[0Ñ#G\/¢õ‘i´ËÅüZ¦Mß/·¡tR÷_X-K ’ßà£o/±"q»ì"«ãFù0U8‘2s¥_˜NÏs{I"_[G|õ ¨÷dŽYÑfK%?6ù¬Â¯~ôÖû #ø)ÿ¢CÓÒ·ÑBß•tÑák¤àâyP(™Nyõ˜­²¿9WjŸb±J®oœI­=4‰ø -2\°–,k Ç¡sªtãÚJÈùÀOB?šÐÖÕW¸/ó21Çq:íç› ëV®%Uêp¿Š ÑtŠãëåëé ›ctŸnøh½ŒGVìú{˜ut ÁèK¿àílÍËçÜã<[ßLýžBU»à®™ 0O=xŸ—¢‹ÿ:Ü›þáû¾¥æ©ÓwŠ`ñ¤î?ÔºžÆA™åÔ¦,’ÝwÈž$Ÿµ·ŒúØâ.Xqm˜ÙÒÇ^ŸN–õ¤Câ·V6AšÅU½fôÍ7Âê>ÃÎH}æ@Å*2ëö<²¦T‚¤VgÁΫÇÈÌütÒ_Æ6Æ7WI¢Û_MÈ~x<•DiTñ)vxKéÞr ï¡7BíÖGÎ…Ûë gùEc"º(åÄÞ3F«¸àx§ýòNf“)ÀfËáôSv_W.Q{†}.)t‘# ^bÐS¢ó$šÖ!7*’ú–¢CÃ{n‚Á‚cL0û8œƒ»L_âÁ},«iÓËŠf¼awkæB³›Y0%TÕÙ¾ú8®©©yD²Ùƒ[ÇÙ:!"ä­KäeƒÓÒ™pdª7çàco|ÿ- $Q½8gr«ˆ‡æ-|E…xK˜àÔæ£Œ%©gߕֱâ QìZÛ¶Ð~.´-ZHìå‡ñ ÖØ‰‹ª}ÈbÚ²•œŽ4"_\ªÙh«:7mfý|†±³®_¦G~uƒøÙß`±Í––ïb:\§Â'áx§™žÎn‡ £å`nÓ j›ãINU<÷î„jËH¨y~r‡¾¡ç ~ø0kÊ5^¢yáU >yO“g·=}ÍþÕåÐŒÒÇ̂пøs¶÷‘ êLÝÕËPû› ”×›«qyúhQÜѼϘ+î¦NŽ pùÇEÌœÇÄ]"‘_pÞÝgïï°¤ú³2è®à)䕬&Üžä6üÁ™¨ôd6UvV&Æ,ò¯“$ׄ ˜îèË p&¬ÇáÁ啸0üt糖š»7™[>€Îñ›L¯Ñl·–¿¥ŸÀï4lÿ É^KÌÆž¹O™±ù´b:F«Ξ—¥õcÆôÝÛp<û|{ð>½z) õýeIÍ«ëPsÀ‘š¿lcè%.û„—,íè³î˜ÉzÜ×§&3lð–<„v¦Dô‹ žÑkÃwäé¾r>2À<Ü‘AT §$wí7\™tÛö—ŽmŒ.‚²Ãh}t ÝÅ~?‡Ï°,+t‡œéÏ<á«}{±11uúñ€m’A“«Œé½ÕÄc•q ð¦‰KëÀD©ƒÑ˜5NæÁÿ˜>Ý%æÏ\²%§Ü\p’'3gæË1-oª˜üá ,þµçU…ÃCŸI}¼6l9‘I%fˆÃâ=4´z‚•ÐYïƒG§ ]ë·®^ë€7±ö+cì#N-v¿aw\ªÅ­^ cP{{ã×€ôÜOD»Ã ôˆ”D 9uÌ—n!e)z’o yuuÝ¿<›{â !j¸™ uTr¯ÑõØŒ~ÃtæUäU¬6t “càœ]õ“»õ¢$,×°$ºyoÁFŽ £¡…øóhsë–$Ù1ƒÙcšs5÷FâÒõÙ˜5ô›ÝãßÄÄö¥Òú,I6ÔÙ‹l]µî¯ÛÄrìþp^ÕI]ßK Æ„iÓçï/ãqˆÇ Ç·qVÝ…žõÏ u…ÜáÎ*2´+ äsŽùþ~7˜%BV'’þU*$²Xx2Ûˆì=Y’y±ÆÎî9ñÿ¡A^|ФóŸeÙ© ™¡úk-†ÛãDëNÿBþ±¦¸ì,DÌ­‡ázÞŠµÂéåJ´PÁ‚;CM­Þœ"+”©eçBض+‘nY‚*ŸË±h;e ×ýD¾Â yø+;«ƒŸéÏ;J\éLÍx(™ŸÜ“›`ÝãùHí**TÈÔ\{‡«tôž^LE*Á÷‰:æÉNH¬Æ„ VÔ+B›øˆ‚ÄëÇp®ñœÉ/Dd6¸AŽ¥Õ¦£^QzDŸƒË¯“hCàt Ý,DÖ;‘3yM蟠‹Æ®ZÄ}c(Df¯§ð&³‘Õ,k¯ÿȯŽ' èÍu^lÄþ0Œ«:iWP§’ uÉŠ 0‡‹  ?pV´²øîløV>d9ín8¼OœtÄY9çfYB5\q8ª™#ÌM³Q|~r=¾±%‚Öà—e‘¸y¨‰Mˆgáó1*ŸÖÁÞÔ;šZeseãݨ÷Å~0^ó4;YcAÒÿÈ éŽ"œÙòsE Â^ŒíÐ) ¢ΰB‡¡«3¼:ôé½ãK@¾'%® C½ÝB¸¨ÊCÖ}> »x×Ð"ñ\æôiʦ÷"K\ikÜl̸ô,}[8dàÈsþï)ßr )§¶RE÷¹˜×°„üw~n¹l9,¢z¹ø~—9;¥šþÙI¶\9MøŽØÒ«»\С†úëàÀ *¼?†m—4 g§ÐÁ•ghk½0ÍŸóg‰sèÓÀJrjÍ:ÆH¼ˆ„9 ™y§%~FÒ ¥gŸT‘È4ÎÛ¿"´9ÆÆ.dÑ9øUv*ˆœÇÀÑFºôŒ9üà!ØÎ²jü6܇번Üð;ˆ½p•õþ¨Nb»Xòiî(ºÉ›“D=eüyr a¾ãÎÎ ù/èVryŸûN›‡æ•ùÓêŠ|3”Ãæ~ºˆÉ¶¹˜‘ßÐ19HžK> U3¸t}ÆröûÞXº&G‰Þ>|uf‰Óü†dÍÁ›ìo×µTÙá9nâÉXÝB»ëªDn®1À‚©CÔÈw6)Z4•<¹iFšðÔé°ÁÚ–Lã°x!§l’?hQa—0úy÷CP /¢] ›ÉõInw²Ñ†Ö©eãž“\W…0÷ª+ÈIfÒ?ýñbîòe½?›¢üç¾(FŸ†nö¼Ðqz¸–‡æO—…)©ÝŒx梾]x¶ÑC“ÚmÅ }jU¶ræXÒ$ƒ‡äUÞ '~¯—ñd›ùTÿKödšÑcmHzŽ»\¦Ñ–£ÂÄwF)ƒßàŒí5Pö5„^ý-HëOûÑÙüx-Ôƒú%jZE¬ÎžAbfÐ#¢xá+þqE$¼5‰nEìÊ¡n®weÅ#$oWè,™Jvh k·A˜FÈ}aöŸáÒ¸™d™÷AXRãGÛÏ8¡æ}=t÷³@×„Ï ¬øš}«î »â²Ëæ6rt.ÌÁ9릳ë<5ÙÁ¢4èÎmEÑWiÎû—X̽šÐjQWJÞ¡Q 9¿T’÷%gJ‚U¬$Î=â…OêÈ…2%ª:¾‚ñºRõÝ®pÖ)MT^#sŸÄ·n€¸²"8§8ƒäÌÝ a+bÆ­³XäMÓ%ýHx²s. õ­òÄ.+æxAöy“©Þ:6G­’?Äœ0ð‚²úd½V23ý^(ÞÍiÃÕ¬ ½<,´÷£!ž"¤ê¡Á¤/¸ðI=Ô>íÆWªmÜéëØÝ"O¢xÉÈ›§œßúq°ë¹#qH¸ 缩Ö6z¦° nm?Nõ^„“m™äV–ÆßIe®Ä̱#ù{ñßãà•k}ÒµðåËbê› Û¶åC?SDÙÑ‘yüõˆùØÔí/Æà#½é«H÷ðXÃoƒj²HFܹ}ß< Ó>3szß1‰Ë]ˆÚTòzƒ1‰74ÅÐÖ H«=IßÙ«ð ƒÅδÏÞ•*ÝÔ§§c›¨Æ–\êò*^Ûq­JîãìÊ\šÈÛ¸®á,zŠÇA–רkt„ Ûeá·pY"¼›‡Ê×ü£šwcH¥r ˆª‚Gµ‰Ô|{- ,¿È™/}7 ž'÷>xÑ“Ìd<ò>d¦¤¡sfízË1ü£ÂxînÛZ:è’XJþ¦ÑàƒuðÓ¹{ö«@ùJk"6©çÛ¥yEþ`”kxÉ™A¤o‰*Uxð„£éÉYrü^(}¨´‰ØóWÓG¡¬hK8Q/^Bûþœ¦‡âÕȧ,²p«õ¹ú‚±7Q€ͧ)3 D6 ¢§û·6çä|­©M¹KòÉwï$0 6Âÿê±WýE~ д4`æmxð|†Î“ÊLKj±0“;S'†-O»:QÒ»è­ÅL›Ýeme}Þæqk75 ïQ¦`‘.q ÈG“~&ýK8ûâš9#™ —ê˜y±÷`±éøî9Áµ½¨MÔ ÉR¡FÉæ,Ö˜ (9%QåO6ZwÛ)ó¤KéÄ,t¿›Â‰íð!lÓwH^/Ž/šŸ2±wŒñ®ò%øº”ŸÌéYB¹QŠx¦í#g±ŒÌÞàaç\£«M?ù€ŸHÉ!CfηC`/µ’¹%pm*‹Eèí…(®QGñÜœWxˆû—ìþÙ}šÀJñ‘ºÃ(±©†áž9ˆ’7™s¼–¸Äj*¡£[·€Žñ.ræÍgl~ÎKžt¨‚Ëívؽ* '+ø¦Ó:QKªôÀ·›äþt®^)2¨quºüÂréŸø+š—dÉâ®~áJ¶›Ô³•ÌÝ©E¸¢Ë:_aìÄ,QLèsn#pÃäéÝ4>âäìI»þm‡'ÏÎ5DûmÄïg’¬÷-|aFÊ·C~’6~¿1ɽûbÉþ²ãnýnwîl¸>!L®ºT³Ž6NÈ:ã†ógà4'„ÉŒÊbŒk߀ÌIŽÌºHÒ:®E™ê`PñqZ²¼¬ØñÑ.ºGŸ8.<Å}ry=²4¸•NGYÉ8HÎ[ëÒºA=Θ¨]m@Tî‚+L½¬Êeœ+Ûé GBwÞ¹…ôO-ÜœnD|f’‚ÛŸ˜ÁÜ¥øa¬Ë'µÜæ^*5ÚW.âhÜœ6} ñÄ+ü˜yƒöh§Q‡Y3ˆÉ朼;Wi¶R½cËQÖrŒ;±Büþö•ñˆ ÅšÕ%øËEtÌEp»ƒ*(C˜†)UËs çÞ\“AÙé„÷4=Ÿ¯ƒ¯îœçj< &sWú‘cOÊQîàfRÒ°[Žž„ â«¡©]þ=6£:A5TLU‡’ÆÅÈþ«'ÉÇäX·?‰êÖ\-åj˜à-ÐØë*MùOΤ׃¢ŸH8|ÕJE±úJ&ªÂgøì#1|&h)ñžH¸ 3ÁŸZUÙ'ª‰²o7êÿ‰õ÷ö 7f`ø‘²eC îO¤7ÓèÔ,'ÌI‚I ûxùZÚ«°¾!šäÑÇGþáÜÁ”7Þ_ŽõâèÒL¶Ùõ8 KÞŠNS’?)eôe!ËìoŒcN~=G• éªN j&tsÝ>ï'i9¶tå¯)ö*ç÷ÁÇɵ½œ= hй]þQŒ(¯â,8GFîHÒiS¯ Î‡TÑSšÌ»)H„›RÏÝÒ$8YZv€hÓsÖÓsˆàKæf§3Ñè{ŠÜ»IdÛå"2ñ¨Žå[ëOÔùyiŸù5"§'IÓî¿„o_ÈÇŸá…Íz¼)3 ÝR•"Úùô*éH¤ÜfcÚËu†ã›c˜ÕMÁÌ¢K׉àMŠ?³ÍÁÒ©™QÚô³¤U©wØCˆ™Ç0‰N‘ ´-¿ã‰ܹšØ:š¥ÒjXt^LJ%©Ùø­†YÞ_8wÜ7^#òçR7§Mú0Ÿ¿Ì òL!Ë"÷àZÌßB5 µHãtOzßc¦ ’®T1¼»tŽë]…¾å+Øé ×Ó?ÑÛé_ž(ý3“È\¨AŽÁ&í«Ndû1%[= 7ÿ#ÔYï¡!ü+ ¹¥@v&aéWÐÓ|À.K· JæLScëh=#rÝé ³-”ðÌ #J’ILSy îð4¢ÓàÞÔ{cöçÊý`-Ô£èxö-¼‘¸„æÉüå¸m,aæUáײý´¨­^=QãÈNÂpí¾× 2g>8>!fÜ8\)\÷?Üdš>çÀL“±ÿ~¸}K Z¸lã4¾ébO1‚–ÁÅ$_ù»Q£™ã(obžB4ïžmET +b¹‚&وу§$Èà ;ˆì­ï8K³E–0¤|]~ÿ½=ë—€ 7ŒÄξ E¯nã^ùôë‚0æ#H§ìõ'±?öcGo$Ư„wû ¬A 5R%HBá[¦Í¾w\ñ§«[os_WZ‘Þd~œñ¡¿êQ¯µfÐ/]ÃÞÞ?Šû=¥Ùu¸G³‘caWÌ>mzlî\2lö–éje?±Püç!xé÷cªl É3šä£D=¢õæ™@x ¹Õöýéìg×S —µ¼âÿÌ ®¦¦Ù€Üê$=Ò„,^QËVwNµÝ•†|fí ;fÔs½rΑ;Ñ‘Ø1œF\’$<ã1´* >Îö&gnŸCÏÜó œ¶8?ú‚9©Í }mƒNoƒ_Îãpú8;R,~/¶@Ï­ð¬4'ƒ¶ éÞ¥‡¤HÑã4¬Ö‰@Õ˜9øÂUžž“b—âŽ'y\•{)¬â똺æ{ÄVž\Í7^h#Æ(˜h‘4þ•ôÇ·yD`þaFÍ#äïva~~8êž}† Å8!«ÁvnßI'dsÀoo#äöd‡ùCi€Š ­X¡Í^-ð¡–BMÌÂQ>*ÕL2 Hc¨ÚFeR×@èêprÖJ“\Úp v;ÓW„Çžûâ Wù…œÒÎ…áÅéd3|gÜÄΑ-Ú!q¾:­K­5 ö­rЏ©€YC%¬ý¢‚“cáLúH²dÊ.V¡g,6INgo¶+Ó&®Ž€OyLÝkcöçK^h¸¢Eçh—ÁܸØžÑO°çŽ‚LC›ÒTÇ& ¨Ñ•–.õ.%Þ}¢ì÷ÄÉú©7ÀÖóü$?¼ÂNhÛ€¼ª?9§ë‰{ó·ƒã]0ìžÇž˜åŽ¢ÞËIqJÆ6wqïE`ÂQ>FÌË=Ž¢¹a¹ºÇVa^Ji<-eÀXg»JM«zëFV¬ ÒT)Uá™~ço £¼rrðøb1£jUBœ‚Io\ {ÊK–÷ð’jž.|ºW…8üD…6ä=xŠÝiSÌIÐbæ;“ŽÑ4î…SåÇâL8p1 oF+í³ ‘×Äw{s— àÛùÒt¶Ç ŒšŒ³ÏóØgWÈÝŠwè°î ër¬N/äxñiÑ”[‹¾K²tÞÔg u_‹–üLÂñE%øAç ŠæòÐßÔàÒ$>üÅËj‡wླÐþÑ ^.“dó'qo‚Ï“´-:è…[×pŽˆoBëpašüÙó{ŽA¦˜2XŸ„Ä.så#Ú }çÆJ«Ñ%kdéžBs`…ÆÁei›Ýåêèd{† t¦aÖBšj¸^~‡Ýöâòº;¸£sè¿vxqå¤&›åI/IœåÆor¡&LŠÁX{Ù2;T:ÍÑTö>ÚLy÷Ç®Á¢ˆ\h^yù` g^Gƒ³âxÌu‡™…ÛÀæô5¼¿I ]Ö…ÀÀ¯aìÛ!Í~écÒ\…˜¾F4vc (>iÛ¯KÑOQƒÕŠƒî‹™ðîT>î‹LA]iW<Ñ@peÔ V{R'K¦ß`Tœâ˜ØCžFW7¼ËŸ‡ì*‘oacF(žZæ³[pÚ}I2ªÛJŸ®3-ݨ±¡‰|Þ‡Fm/Pkf6¸ˆHC{KóXVƒ¾&ã¯$cúƒa

        Öºt{Oý»SÝfœäžœ›Ï2B›¹yßÓÈ:Oÿúž Ж'½¬Ä%꘷ˆ5öU¦+¥7ÉÞ$v@œ¼ê@ú_O#¿ÎrñÂÍDì] áïêaÙ[)ÿ å—À±Ò”Úú/§‹ÇÑçW¹÷Ñ܈ÀßvxGÍœko¡óí¥èâój$Ô)¶™¶¢kN:ZB+4|:Ìf‹ rf¡%§ÝÀªügc…³ôàØ! ÌG}‘0!!B$Ö‘0rÖË–=п:¬Ë¡{¶=À³^õìÙ=ï¹(N±ÛrÒæ?àËÅ9:>ÌGa?à¹kE½¥Þ³â‡j¸i[Ùô›‡‰í186;â¿gxäÓÇü‹ßȽ@Û }ìjÖ0»èÑÛ"°¨Ù—gjaÛ!2Øs™[þrˆefîj¬Ïl³˜/œ07¥Òë1¹å1þËX@¾[;21ºVðîñG„k\h)š §Û­'±§ ¶«b"©Dù=B$}/ˆZe€ø¯´þç •cËphn/Nâ:#ï]§+›ñ×Y#j»ŸžV×¢ÃA¸¯oZý¹Ü9¬ðÙ9p½[ný±#åÒ'Ð7—7¬ÀŽ·qìÜhzÈVNauæë‹áà+·šçë9Ëùéž[Rôi¿)l,Â&¯ÃèêÃ2G`Žcpý+hëÈ5æiD›ì%Íž<ôÜÎ2úí¨õ˜$Ix¢û L2cÎßæ£ÓxIÕ; ÓmbbÇ’áªÜXƒútvà›#YºpÇ:ýµ;Tz¨ @t5Ü>ÝÈ|Ù<múÔiuÕi»ÖŸi¸pk?'Ox›äÈ:N66wPëPiÿ ?‡dCÒ²}ð5sœr¿Ž=Âæ BÆî?fgÜS¤¾GŒÏõ÷܇Ï5)»O P<ìÚÇÄ‹Äý¬”ï5vÑ¥Ÿ\ÏKéÎÐ Æ`Ãuö¼¿E²úÜ,˜ãÚ…zæi0SE‰–p£ûû³ØëWA¬„ •¶é`Öy}‡¦ÆºlûÄn²ïAßQ~í¶dîŠ0SM“áïÅËÄG|7³ÿù/¬ý®M+¥ºX¹i“øæ°—ñËNÏ9qž[™¼Ãû™´9èó`L³pf¯öåŒØÈY‚ËJ컓ۦ‰ƒ¡ÏQŽpßO'᳦¢ne›Åj‘·íP~% W~@¡u¡´âÉ"8ÿ¹¹]˜6_}ÿÿ:j¹“Ï! Ïåà•ž'à—b 9Ù;¡}…¹$ÅOSw¼è”Õ0¡ ‚ÒÄ^g3‘=%L¸O aÓ!\Ösº"ò0;ñ¾ýwOIºM¤†æY‘O]Lê=:8ex¶í·€ öæô˜cïJ¢Ÿ‹ã¶?q¬Ù=:2ÈÄ»_Äý~\>Ó`0|²†ò;™ä‚ÉôÑ5öjÑ&²uQø¶—œøäŽÍ‚,Èõß5Ajk™Â~(Éý¯N[ëãÜ_)çäáêãI| Á7EËÙO…ŠPÐúåIŽ‚FüÈqºõÒ˰:g ¶¢†ǘç1ͤ ï3VäþÊ!?t ž Òµ#‘ÿ‹(Þ—zƒ÷g:3Zì»'Ö¸úÝ|¨~wNæ{°™?:p9o)PÆeJAp¬Ü׸ø³Û£maÍã:ÆE]”Ô;°Œ%HRqÊ ·€¦F,ǰLƒnᎫ¬ñÂþlsÝ([’¯å€Wz'æì>ˆª>Îv›g.%¾ƒèc¸,¯˜ ²-„ˆ—ÇÙ´IìèòtÃz¼zÑC°üºù]gôža]’?³÷(m8{Ží•ƒ ?†¸/;ÿÙéé*ÅШT2# ŸœŒG³UoØŠ/Xù댯} Xš’ØgKQ|Ø ¿ô5­ÌqóqYúHÔ$Ó ‘wÆa¨ß!Iüùé¥Þ(þ/v®¹viL;¶v…`ˆ‘pðÈv(-§üÃÕM ÏìÀìU{ht×V†ÓˆMVÙìõwÑÿŸ$ióž‡réìÔõÃè¾ä,ÞÁ>ßHì÷žÂÜFœÐM–ÏŸ˜ÿjEÞÌW0—íd ¾½›QÇÿnizÀÿ+Û»s \xÇ\‹êgJ²Ã‚æ´óï1º;¤œ]žá+‘1ì¿ ËW‚Ï×Kè"‡'2UHÄ:c央º<›D9ðÓ/?Û©•} sèß.6Vz7ù¦®MKÌ%(·}þÄÐ_ùÙ($y‹)K©ÅM*I˜~ñ+s®²ßx "fJd˜ðRÍæÜŸ{KŒß1ÇZˆà–Pj™Ž—FדÄÄ>ÐòT''½e9À[3>R=Ȉß*'5dêÆåt­Â¨gF§©À}Y%<*ïiuÌW¼ÁËOü5ƒˆÀ×òíÉÌY ¶MƒÖl¿Ãü¾œxðo8´X—„è.£¯¿EQ¥–Tö×yy²}ä³Íµ ¶ûO¡»ê³©Ëß®{ܨ÷Í:ôg§øQ‰¸we#™w‚C¤CNÂÙ²j°vøš²õÝgÙÕÑàÔw „%üÀ`õÌË '<»+˜W† Шs +·Kæxw±]ÁºôΫJX¹¦ ý¯Ækö 2 ˃DÒ(sõÜ0PÅœÏË¡ÿncŽ¡%Í}©OÎÏ3ñáW˜}ºó¿3–ÏÊØ?!LéÉíÿÞNŒË£b”<¹“ˆ•ùóM/qÊúýxLf˜ù©.M×í)ÇÑiÂ`îûëÝ_W°Ÿ¹|Š—X]ÏfŒÝ4pñ̯Üü“n0On»N^\Myš-@Íã(•S(í fçEýå¾*?M›ŽŒÃ®ÈaQË‚±ö*’;R5çC¸ïS<ØÎHË-edÐŒM€Ì¾;l㥔úcW1}érZ°u*í)×c÷H©14ÿ®8Óú`RS 3½!ŠôâÛQä»ýí¿ÚcŒpäÝU8-¥‘3¦ºŽþ0Ç0iì’Uñh²ç㿞 +>$â•Ø\æÇßstâŸ. Ì|Î|ú»c®±Oý…ÌB_Úß;…Ñ ]sižü :egF{zÑ^I+ÈÚKçýj†°Dgœ2¡N |¦’ÔY“<ô ëòC×Ô×H…–}.‚ðŽd‘Ètl°§Á¦¢tôÎsŽÙu ßµêFÔ2òX©¥í ¨€s7 òBÃnó•¬ï9fé)[ÎÒ²çð7ïYü­‡ëÏ£iœ2z,ÂþÎã‡ks0p-…ËðÏLM²çÆR·›‡Hˆ÷abC3ádž“þËP¯éñÝ"4Ôs³Ô^†,.ö"±âr(ö9…Λ%¥õ¬çj:3¦òû3a§Ò,õå=œ”…LI;Ä‹)ÑWÞËh¹Ò>òi4cu½œ•O¸É&Æ£A>(Ülq"ßJ¦ë“úuØËkK‹–è‰Ø2öVð'r­kÝxkÕÝyÈÃeSèÉ?ÓÈ|k *øRÐ~Oè7ÚüL‰gºöó.º£\šÞ+Ê&w®’™bw âíºÇ¬›>;x‘ò†%“¾÷*ø=¸ÿ0Àq§pø¹ÿìÞÂö©}fmÖ¦ƒ{Ùúðù-|¬„Ûb©!ÿ;P·L Yk½¨Nq<ÛîœNK%ÛÉÕÛZ”û¥œ‘\‘Šbƒñ$tQ"™G˜ûÑÿXKN/ót’?”¹{ÁÁû‚äb` é]—HrŠ×áªßûA½R–SÒB_Ãr)ÅŒ9¬‹SÕBhŸ9™ô¬:‰p¨›!FRoYýÛI –wÁª_³èĉF®in ìÜ{‚]çLŠ¿¼‚× çYçwa¨ïñ§ïÒ¦ósöpÄVAfûrá÷Lº+)^ìTÆW³;@bû'T+ 'î;>‚ÎôßPt=7éÑa…°"Kî\懬¦7Ü Ó›0Ç‚ŸÜщC¾"^šw»ŽgîJÓð·éÐ;ï Dò0/#°rÞ)|´H,ïA<Ï/A#Ãfxþà¨Ìà¥7Ã~ãª{À,£ŸÌ3•¦mð0Y—-4{¿^5â›?O¾ƒ}AR´Àpw†Us¿zxãÇ­l`í}ÜcaI«%ÈiÝŸ˜¬þœ¹žÅÜ’€ ïnâþŒ"|Ïõ+8´×HÔ+>qO] À¶«í˜\Ì\ÎN¦VTÁ»oqxnÏwd¸-ÌnW¦Ñ/ð°wÆe8*½ì<—N˜âaP«‚¢=p±/Býf|þí«z–`H bžÖ3ã/Ó`ë 9’bbÀÈØäãxî Ôâ_ÂÓuÙ¥r31?¼mWßÅVÂOJ\}‘ôdÀ¤/(¸­#nKស9xˆÃ2öÒÖÜe÷ûØüD}†~ /äÃð÷þoLzà¡Ã÷˜ñðx8¹ÝÞ g:ähC'?MV}³E’߀$‰I\O4¦€W¯MžCDøÃnÞ}äé^„: sKbâA]21ã xž%¦e¨¾¯Ôï- æ÷ßs-zÔ¯K˜GIºd±‡›zSˆÎQŠC5ÓmÌõ+Â`¹Þ‚>Ö_L®[Is÷ï7¦õ}§i­C]é-Ä}~?L´|bçTCÌTI–Fò£Êœ—øø~ «Q¥Cõ÷óѱãfô»ª$ùÙ8Ÿ¸‘.ðÙØÈ.V I’7Ù…oíÁè|õhÝKœ!çÞ"|¯mÐ9äˆþWÁ£Å$qG137ÕŽJÒ°¹¯ à°… N’¢Ê^ÓÈùB)zcÏ0VZ ³'bàºM+®ŠZ^K Ï ÿ×p*¥»Þ…þ+ç±çrDöÏÇ7çQÞfMr·„ÆïsHÏ|<ÂùIË™òž}P²¿„söùæ°Rº§3ô’€b¢Ȫ¨vx-aDOgM¥Ò<Œó… l6zÂÝhgMË0œØðá`û¥4düWÁ·ÐZ\µë7Bò,«¯ù•}d”Ĥk?Åg‰ëÀÊp!šÊ%Fy1tI…æÝªGÌ» =[šZÊßÀ)ëpCY–¸ìƒdÏÀóZ™,'h¨ìkCxHDÆ Vñéw˜s2 2ôÆ[u fQ 3ô C”áܰˆ¼K4ÆÜe÷qNŒ<Š~¾‘µG]Lþ¹\9wGÙùQYnѽpÕ`Qú`Aä<ÊðyvÆ· ’ækFàm°˜ŽMHXr€øÉ•¢NÁs²îöWR‹Ÿòœèºt=¼ãø ƒ§þoÜ”yßþÿn…­Z”¤M#Ø]`žü†¯ÍfÁîòy߇á¬|iºÈj*‰r÷ž#­Ï#OmÖ`ЭGäÙöi‡#h}/ý÷+“Öﬧ÷erVucøÿl€ N&„´‡?vµ´Â’uWЦê6¬w¸ Å·‘±óE8}ÞùÖŠ ­BwòÍ<Õ¦ßùØÿ$ŽÉmÒ¢ú)”#ô 6g}G‹Iï`÷ž\¼$ʜҥ÷ü"ˆÅ¯ ªz)…<ô”¦ÞÇnÒ-oáK›(ZHÕ`ÛÚ~öAó)øÙÓ‰iàÖÚ‡f'5èÞpY²£TŽÞˆêFßU8¢Ï~7ªK«îÈa MS`‰Î°Ü*7y~f­8Çò½,a÷•9að)´^Eü£•ËŠ¹µ‚)°=Rë½g]œ‘÷Ðwøú’ UÚ\7?W¢“òM˜fö€UXÂÂ÷/8›•7bÓ Xþe;èjŸAŽýæ&Îeœ4¥è¢üPvÛò ú+ûñ¯ñ|ïŽ3)®SèÆ˜«ÏìÆ‡൴癲®Ú.W Ü4 ®Ip <.cý*q0¤Žíߊ¯Ä©{Â}Ìz\ƒƒ&ütlÊKöÆ<{pÝ‚Yi¡p§@[ã.ÃÞ‘qîÝÔLN÷é/Œä#š¶šJ„,#„ÓZOœàCÃOØ);NdO¥Ç–¶þ÷^fp=ÙÆy”؆×Ü%1»Ò‡’Ðé$îì%þ¾6u!óUÇŠòlσðiaô¢° i ^h÷cl;"¨©ƒ É|5Å“ÊÙ¸³F Ý°‚ßÕ]â˜ZiKy%â'Y3h€™Ûi9_6lMã¹I°ct%-úmICe~ú–AëÊðr@“ºçÖßþø¶`:m)¡™ÇK ý€:é¼3øŠ°O±þª§›pו—ùtKä.êÎ+së_2­Îî$á¢$I 8Gž=C›™“ZâOÈ6€ ëíñ›£$þŠñ‡‰¤HÍŸQ§Øßn%øÕZ÷½tÄ[#pâñv¿’3‘ê`7ç§“ Sáñ+YúLˉ±Û!A,þüAlj™4"¦ (jøÇÆŠ>¶¢U‡v%ï¥rãcÌÖYÿØâ@øßA8q •rð#cöí.ÐEá˜ÖÜ/ ¡50{¥ )ò CUAÎ>¾cXêZÅj{j£ƒ¼=Þ©¿À>6üK÷Þ $âs6×ÿ9ÏCÊR·`™v³l[³Ž·–lÙÒ@ïOÝBõ¤áG“(Ÿ‚_pCm.úŠm¡K}·Ðµ%(¬ñ#ÿºp…¯îdHçÓa©¿Øsu;,™b]±¶Ä÷¡4ÑæO î2üôYB4xîR‡§ªÏˆÓ¢EPhÆHHã2¡/td™ýPy èO\§SúAä­$I^ô ·UµƒF»–XA¤‡Ó´ UlÞ=¦Ü#T¬ñådŠ­ã>ÏÏ"»ËÂÉ25:îØGO|Ëe]gä ‰Íq¹ì7\ÕV úC[hÙx?ÂßHøÝor]f“]Ç\`¬ñ îœû‘¹1ZÃu”°£N÷PäæC’wJ†^›gAY#„²Ÿ C5†šÝ%çuÉÓ”B’`q…ðd® e14@Ó–µ)K;;’øo>žÊ(æ—]o޼»ûœ¼I< ŒøJbÅ«ŒÁŸ£è–¯xf©${h6­Œ¸ÍªçÞƒ-gAÓÃÐo…)Ó½ÈdèÑw|®¿‚‰…Ø>¨ÒZÉ×j]ZЊ|WWÒô rˆæJ³k¹ôïÌBòácž>2_nC§çl&îçÂh¢JþÜ0Ã~…K!»¦Tš85Ù4ì,¤£RšDÏ£’sÉó7rSû¸ƒñµLýŠ `<û{¤. •2 I¾ Íw€åMå­J©ñqÿû”’%>ß e‹“±Ìç&ØM+‚½yŒ°”8Ò±¡ósIVô>¨f /ÔÀy‰4­ô°ÑB¹ú¸Äö.òi¢úãòJ%WZsß8üÄ1Mb˜ý„Œ’ÞÆµT½ô+ì>ö&ý\Ñóô`o#ì\|–Uâ|çÔÖÊÓ&{{¹,B?{,¦žßÊè9!j•ÂÀ®‰pæ;W¿‰ ÄJPˆìú= ]&Èwä ®u«Gr¥§^¾ÁfŹŸp^èŽvÀ3ĉëÃ)两±ðÕ'4—ª—-%9Ê8×yIrµ)“ñã)î›})3çâÏ[h 1’£ïjSðFä-F+S~j•&Otð•êRø9=…ÕW`^ŒÎ.e/{ÌfânÏàÝsN€8éó©„Ç–@Ñ–«Ù%Aþª°ÒÚ³È~9MÈ´xÆ–_ ƒoGÊÐú§=^à³Ü^†.N¸oLƒ¾L9Í dT¢A™È í ÒÉ,Ø{Ã}ukªÀ– T~<áz  Ãq)šæJZ÷9׎;X=âFDÍ~£Å穜„¤-¦=ËòP»==7š£Žµ8¨ÝØN]2§AAS"!¤Œ2¡t7¶cW­uuUb%bÈ{Wj}[”*{¼åÄŸ:Ɗͽ€ÿ=?§Á»ƒÉ¿ÖA\WØEFeqäö¥ÛŠB–¿nšol_³ÿmuw@ûF;ò’Ñ¢Ê\=¨ÙpôW/¥6!Ö¤ýÛŽèÙ6lá]ξPJ…á’p&-ÏŽ¹³c9û~ÝáGÕEÒ±1 5D¶S=9¢™€¦âiH>¯%å··’°oæäŒä&òáÓ-(Tþ ‡s›³O’Ò(^*ü`&¾“Úùo±®vUèÈ=8÷'Ž×q™ÁÉ]\ S'5‘gág6¿R‘\Ø¥GÜʙޛ%8/4WØ*QÛRH5Mž””€‡ßNf<ÿf¬ÁõábTô;?Ù>Œ-*È¢Q _#ÚMÍпåwíÊ™¨û6Š>íÅj‰ðDâ"¼v߯}{í¶M߆NâO¹žC÷+=¸fÓ*¶ÝŒŸ^$ôí¼X2ïßg}‹&Éâäðˆ*Ná¥Mm`>Ͱ!#°ì¹…ǺÃG´yëDrÿÊà.ÉLXçÕò«·‚‹¬Ø8•b©Iy2§Dƒ'@§ Þ¼´#×¶‹Ò¥OoCˆ îé0·•I©‰jiÍ! Níé?.áŠ@”ÐŽ¦gø²§ùkñ©› UI– Ê‹<è/“ ¼¿_‡ª)›ƒâ6 ­9…ÝS€.Uò¦B§’¡AuLŽ€¨4G3“ }^Gé%÷9äåøyšãÒ ×çµ—ް;q¦ˆ¼€©"0ƒ‚Ñjy²uu¬-ÌB¯ð(èo:}/â&·l°ÛÆ<}Ø [·ÿ`^*B]‹?êj’þ_TX\ÏÜ´Ð#™Û”Á¦ò ,¾€—T¿Áì¯9x^¸K£ã‰}*t(„‹Ûî;°'Öλ½7°¬F ™CÉ8gÑox“ÑŠGn ëß!FjŸ¹3•—:þ­Å?R¹ÿ4îb .¼ 5f,Ïã ¸=´Fg¾ƒz3sÂ÷i§í|½öz )ºÈG’t×â·š%p 9yõ4'àÙVb9{6êy s„^£Ñößv«FÔÀ çc¼­€³“CÇ­“>oæüå>æàsWzi»N‹ƒÊj('Ò† ä_@wq‰’³g¶”¡¿Úà`€zЪ ¦GŒÊQëœåXtÏŽ´|üY‹^£×ã|v MÄIâÊRtïüóTβÉ[{Yžûÿ@§$‚š­±TÑ++]ì-µImÿYvÛÊZLv ‡™Þ>lžŸ2‘]<—†ú²ÝÂ&ôSèi¼_ÿ‹G’¾Éµ ׄЄtÜÿ2›ß`O.›Å'æ‹©„B:>”üŽæÎ§–zâ®h=ºæY"c´ -Uϧñó†dÛY·ó*?ÚÛ˜g·kÝÒ*ò³ãáÀËOÔòâ.’‘D^“EèÒ'jo´ü»,Ô íq°NL-×ä–üÚMtf2ÞŸdÈjá?ì›Þ<¢ÑpvóîØ©Móo-„í©©ÉO’þPÄ}×bѧâøÛ‰0ö[h[©3ì9B¸;β· }ÈÄ|l<›H”5žØ.lVòÀ±ä­ôõ³…ÔXu=´ Hk2øéü˜5t~Ülún½m%Ÿy´h-{[2Ä<³pÔû[&ƒj޲dz•‘Ä}úÍ;þ6戬ÎYtŠUæG4•ZB~õJ“§pÓ†<ìKàº$t-‰¡{„bð˜¹+ùiû­¦‡×‹OW!µëíÈ ¸AvÇÕ"fÛ¯ì+Ïœ#¾‹ùL§Ške¨P…1øA´%;¢ÍÉvx€/ÄciôϸìNg«ßâ>ì“'Œ­' p‘%æïzñ§ž¦Fµ‚b,]:a$Iý¼ê 'ZéΔOä7ÎÈYŤsëÆaçòóôréV³¢ç™–¡«+Â^×d˜º‡ìºbM;{a ´A•{$Tÿ yWo‚SÚMüð¯yíé´¤åM0}ïEFáºåá†ËÛC<8ËÆ š¢àÜ&i ÌRµÏhr´]´ÿ·ûg~Ý;CÎ)Ñ̰$¦ûwlŠ)…?9ë§H~Q…TËÌ”ƒÏ±z_5Níç§>¿Ã¬[†´YAno—!ú3¯2ïC副º(nÕ†- «8s.%E!|Á? §8Ý0Ÿûƒºe)Þš3?éÑÞéäó« ЉxÄäÝåÜÖáØ~maú¶°ì§¦T¿=ŠkзñG჈:ý*ë9éò3wWxHÀ—þl˜Ë>fö*Åx´ä.ûxÆö¡Lìþ+Ksnœ§zCq¥7ŸœáÖbÙé-¤Å´ +ýœ1@¬ ¬7ÅÒwá/Á/7Ü]l骙Q¸~ÚÛr”ÌM3!¥šp¢÷lÈU¤£cÚ ,)dŸuî|÷)ƒd¥`:¼\€DñÉP׃pý™b |õ¹l΢L<ÒÌ^X“ (…ÊŽ‚Ð-û ƒ¯¢Ï‡4ÚûЄÔyÅ`˜$—:BÉZ R¸z ýxM°ÌæäÇ0ܺ70í¬ ¹W™D_âúIÍǬF¬ñ«_ÁÆs*ém>TqÛÄJxÑ{Ñ¿Ø3ÃSqI§*yü¯Oxá}|λÌÈî_Ao­ùŽÿºÌi—uÞàÍýu™`=N¥Ìlèþ*V}/é wfS&FÁs¬”©ý°Œ¬Ùr†Më‚G3ÞÃ@‚á·ÆÆßÏàÌ!rtÝc¼ø»m‚ȯ©áâé(´`ÝÃ9×`X¾R¬â²—:ÜžÔz}ß3L!–Új4¥i ܶH‡[=0%MžôC¡™ ZCÐvõVšÓàHs}”˜»v?àí&i ‹ÀmÙËÑÇq-`´ˆþ>y¢,%Gç^¾ŒËƒ ‘}8©UoyPLDTßgOÞÍ:I®5çbì€í85ƒ(+ ñÍWØ®E‡¨N~2$ž%dgf(qq'¨¶šãì]ÁðÉ:†iz£A^etEÑè&ºz½¬OÊÇIc ´Hȳ$ÒJ€{¿˜Å×ï@ÚâT8Wöšñ$|yx2ç)Êõxck[2 ôÜEÍYŠ}‹éB؃^ŘÅPyP]+Qdô8}UÁf²3pƒW&§=Sïof;ζ¦ðb4¿,Nü÷?gvŸ‰d¿ç Ò´WÛÈ·¨!6Åú¿g<4 'q™Ô„}¸6uÌòk‹}ó¡O)ŽˆTL#÷\XCº§û+7ê²SZoÄ£á×¾è/:†[Î(³svk‘ÙRÑäõwv϶] D%ÆÅ‰ý ¸i™$ùûÁ–n:&ÎTÍDܽVO2ßeæÀã VHg&®y[ƒÖû’~í@˜ÕßÍf¶é‘ß6¸eÜŽ§Ð•üêH_…ÙSiëçð—JSÏz¶ìÑVXå<>G¨ÊT-öž9Y6’‰­!–)§‡uâìžëY \"CßXøàßVÿ‘WjÖëç8V.¸uÞ& CqWý¸jœ ±AÈ_x·?µop½øÜ]}™³€òiO:ï—+˜Z…÷Êó@ñ‰"XlÖX‚Z%Á Ä*¸Û¹L „å©×9i𨲗–ïRafï)f-?Ýßúµìʾ£pwïÐ9Na¯ì3†­…[/ÆØ+¦eLXnž¸'FîΪ†Q‡ÅÐzëûÉõ·uq³ÿ‘Ùz–]çÉÇäj-ij…)~lÆWóž¸ó ì hÀY§O£Ùk-(¾¨º_¥`—Ã<4¿u‚Ìs?}˜ö;‹SÙ.Ö_€wÎÊ’ößP(ñìΚbà¨îá/ ¼˜å¯:Ãv’*dŠpÅøaH¬C“ñtx¹N€Xß壦~áá^BÙìvYKýVÎ.¸ý¼nË«ÓÕÜÄ>i>Ã:ŸÁy>]µ”‚âÚ”㻄nçÚpŇ-v"kU™ÖwÆPR}–Ô”ÇS1ÚþìžÕ‚EþhÛPÁL=<ÄœµÏ‚Äm‰ìÂ#Žôï9a˜-x0ýËåuztu³ÍäœdªL¶‚ÒÞBàã\Ãå/¦1þÙï HGŸý{½i´ „„¥È˜ý+|é9ª åbà;5òûÌmزÁ|ç?Fñ»°³t³gyÇnÇ Î™¬«È‡…ØuO’þpN‚W‡ˆÛ‚¼¼¼·íÂTWefDÊŒ-b}~è’ñ_¸°5ÞÛ§Ââ5ó±{r¾EØP˜¶Ôšˆ:LäÁl¶UM› ‡{±_{J™¼-rDàÆ(wàÊS ^޳hÉ´—mqØ9¿ÀEã ÈZ³gît¡ù$ÏL>šÍ”–Í%êÉ\Ѭð‹ õ²ÇñøvBGÉîÛš™ìˆèæ“öõ…[Ü5U›0Ë~Ù· ’ï¸äã.|°Ê‡a‹gщü©xèks3Z“hõU€ÿU[øÄ+ÃõÙ“É_Ð’'ÄÇÁ•M3aÇMX(ŸF!¨šõ"÷í6üÉ+dONÌÆ£’ƤC¢Î:þâV­ŒgFÑ€=½ô6¾¸Ë>UÊÚEÌ%벎sÆÜ…¢¶µ8q× g†.çÍÎ@Ûè Ñj¬³Ò BKJÐòÒgv|êYXâùš³†SfUúØ~š÷å $ñlÄÅÉse}n1¾›ãà‰E7ð\Âù®x#í$«3MŠÉ²›Ã.túƼ-e×-OƒŸ/sAô}/;=r>½š9ŸÓŽåÞtò:{/p ]WU@·_1. 6ÛÜ8ä_r«ßÀ4}úpâw}æË}øòñZòô”<ÒR Ï×¾„¨Ó0×0÷jÅÒVŽä·þeÐÉŽgä '–n<ßÌF|Ud_+›Ó¹FnLû bø»8Õ°ø¬5¨=š¦RôW~ HÛÖL0Bky)¿Ä9˜ò9Ï?¿ÉvLÒgù«é ‰Ha9#’oŒÇõKðma¸‘Š%PWÊëíÎ)¤8P˜µBÿã]lòÕ+h/û™ydÍ}BÔü£žÛô‡íÊ‹æ¬T©€²©¸wÜ$–j±/4!¥ð.<¼¿w…'€±ê|µ˜µ[  #L¢Ü–Þá·]„²õÿ˜•jæ×Ì“²ö"®XlIÔ½çÂÌÃRA;Ú¸£ý‚Ĩý'vY•Ñû~ ÓÖDÀ¹Ýjôg(ÓÑû[Ò 1¨´3\ï°#Ѧx+ê“_‚™Ê ¸$ë16”ÝEÜpÚ[Å(·\f‹z¡iû_öž«)U¼ÅÏšl¤þOFQc©Î®¦ié\œQ ÓNŠ(Òƒ{32âZ·à^ç]Üãpœîÿák††Ñðƒó9R ?e¾ÑìÆËZ„5NgCîh(qÖ¦Zw}´¨a2nÛm`D9JNbt¡¨GÓkóØùç÷sò2WJ-ðÔjiú‘ŠqÊ^öÀƒ9ƒàò}=i‚arÅÊwìƒ`WºÙ5y*ür=_ã–7‹s£Sýµ‘o¹I¶«Åû†ÖÔ\\€žoÏÀQ»<°Tà%2Ñ›h\š¾:lF%ºA«ÚaàÙᎹÍK¡Ø( òjª¹›" ÒìòÓFÆ»Ue| §eF¤Ùê)¹0+½šµXOîú(ÒÚ+iÉxb¼‰Îu¡z%gaùz)8Ìj°GàHàf¹f4¦\{^´ß㉌s0ÈŒ–ã¥{¦ÌCA\ c IlÁ5F^ŸúubK† }=ÉnýaÁ½î"@“øÔÈZÞ3œ Z¡/‚Âr:deœÙ“ ?dÃaHw|RkùãRÌ9ýN\sÀ9ééÍ'LÄÖIMw MøÖ‘àߘêF‚«¥ÀùtzKbÏÂÖCwÇ%:½/}F¶Á±þ3øàÇ<6p ¢!»t!)R"“cª[–;yö‰³ÿFÈ÷ã•ÐÞ7wŸ…«°o7!óFeœ\žˆ?íÑêT"üÙ¬E;7úêõu8ë…§‹{W®LfçìT§‘·{Ñde»lßöƒÍj2!UÇH¼:Äœ6:%¿1àí65š…wnd‚Ñ”Ëüfâ{Ÿqú ]è¡Æj¶þBGwŽ T²ÕG'5Nò"ríA>gÞ¹D8þ­1ìw&]MppF¶ün`†r9+øÏC¤ÞrL鿃5ÿv1±çõѼù6Ìøù‡=$\Ι'ÿ(†½a‹eˆ×îóhé}¹üç!¬1.c“D_ÙMÔ}† ÍyÌRaojLõ¢r6ðdÀÊãu iz4´c4íÚÅ„ŒW€CŠ'”rmp÷NOvÍV_Œ«ÎæÀyl˜³ÕÚ0Î_È11¸øm,4¬'ôí¯Fˆw{ÍzÜq$é!|pSZÜé­ãס(z: óõ$ÛÅ9¸/†FýN&ÆâsiPÁT´Ø,†ï+r©ÉÈ-FOI„/f¹î~ Y†(µU˜wOÁïVЇk\`ðZ/<»´ß-4¤õytÿÜøÙXÆ^­Òzi?âM•³0þò) ·-é‡Ö}´j.ÍÎP£Ê"ðÛýJ«I“6· cZùñCŒ,Z›Ï¨®\ GùéOß%ô¼ð ’m¥J½×OU×¹ìn#9Bt#Ho‚1&vè’cƇiÒ|þ‰ic‹Ó.aÅA<ïVÇJIÑ?ær”×ÔˆæhÜÅ•<œÛ%Mìô¥˜]GàÚÉQ¦| yöå)RXÜÌ4¼;‚Ý>$Üq*w^L)(õfÂyÛï6,ôŸ© [ôµqsT=sãw$¾Ë¼5ϧҾUIÀDùÁÍ­éPçõB–yÁfmòp¸”9²t5DîUÄt»çXòtËýЧ ñÀ]äí°$Ó ½ñ.M"GliM’"}ž¿˜öÿR…G«ÝÑŠÔ2œËK r#ÙYRų.´~½:=7TRR3`Þ•%dþÙgøÞîV·õãå÷§¡ù„"--ß‹i¦fÎFPà ùð‘§í=±(% ääÚØWåŽdê{Wø”ÆCVkG¡?úÓjµn39oêZaÿÒGx'ˉ\\³ž×$q‹MÐ,Ášq ”!UÌx¥+H⇺™«ñ –nsiõá̾zE”¯´Â1)%X`­k¤áØ_( ŽÄ;&™L›· ,vdbZnf{ðç’¿ôGU2ú§ëÓß²=œ5”G šYü<<¯ 3ûkôÈÆG;ð…¨=®òI£n1-`«ÙÆéó4g~fl$Þãð§=”„,˜Eû·9ÑŒÎ1lÌÝGå¹°vgÔ¤M9²aÆ|óú3ÞyëLõÍáçˆ;ùíöнðÖ•ý,é϶$¼÷íßB«g]b³o ¢±åt—œ4±r Ä7¶† ~ßž¤½ˆí¯³&ñþ(Þl‰½³ˆê¦Zrû¥U]#Kï6í¥fÕ÷èôŽàX‰.Õ¹xù¾â½ËØõY”|«jdúJ6“›·ôèûú¹äšj,]qcyìüZ—³è1p[ûN„Úl:ƒgžÛSõN=ÎÝÕðÏÜvûª¢$g6팇ï< 1¾ø^_ ß/%›=Ò¡lÕÚÿªÂHq[ØÚ[£TWbâó æ¯Ñ¢ž“Üâ;ƒúË*Ò9×'ó˜Ï+œ>e#9aaGä FQñ¬#–QFÃĸ;EÎþMƒµS²ñÊY/² h-\†ZˆÜÅâZI[,>b×·w; ùÈdé‹Wyθ.ú!ìØl‰rá³pq›ø—Ø ¬Ùæ+Ž#o”.ö«Öqaú EŽé|kê$’Ç6/À(Ÿ ±‹—ú[ ƒ›ëbö ;‡ž®ÚN)KÈ{G/+%ãÔù—±¦M“ô”Æ.‹Ýp'߀œå™‚;ýô@i¾é¤vw¡ú± Æ1ÐP]BúŸÏ¡Ý{ŽÓdÍðÞäƒ Ú$“žøÍît¡¯/:ƒ©jtöRK"¸w~Ö£ý–ÑÔCWï«k“Ù ÷¨Ú®e8Ú¼„HR&Ù!Pt;Õ6“ÌÒÔÉûªa§².5ÉÀŸë+áö.ÔÖ_c&fg0=Ñ´­6ÃüÄLJνå8šÉ»£ƒÝÒHR»æQqÆ–Êò\§Ž]ï`]cßꀉËiª³ kÊt,6"Ví¦ôÏÒ 59‡×ëÃ0Í'…Ùs4z6ÀD‚ÇãÊ|œuú£sZ’^<ØÊ剔FX½”Õ˜ÂíØÐóŠøð„ëõ¯ o›!ß7oК+HrÅxèEXñMŒI¹ÂG_ª aóÃj¸špÞη0tÐŒ$I¥c˜|%׊Ýf®GxÁ)ùëlØm/6RL…•*xÏùì‹2§sØR tÚ.æ Ê€:qÇy«i¼z'Îúv æýsDŸPv¼G]ê³ ³#¾0ɱñÐù0³§¾ ¦ìW¡ tڷŸ>pÚù0'd:1¾0Uòv¾•>|¼¾„\ú^Ök“˜úi*¨ <äˆì1\á‰×¥Ï0lúExëNóvÒbÕf‡å-”çÏḻÛÜtˆ×7„À'OÑïøN\½s…L™–rÂA{XŽtçŠR+kæg÷|:ôa;Þš{c<6âæeMø`«qÊÃÖ+FtØ"‚øŸ&Åçãn«úöaÖ£pÔ~4“®q)ä˜Bxo.a(Å3?õ‚äúŸì¥Dòy^ÃŽQÊl°Gž_Ù°qùI|ú›8òÓàª|˜ÛÛéV7ØwVwaÄ8“ýyç2¨ìÏ.׆úµ±¹¦ ¾ü[OÂ6¾ÂÝû¾bùŒqÐóà§çXßãÓ0èa-à~>Xs®WÄC`­9å¬O^Ç 4¡ ˜ò5×Å18ý¶ ?Í‹þoV±—ÒB™Á# <‡Æ&n=økÍ÷Fr—+[ÒåG§“˜XzåˆÉí¶—ê’ð5e˜n|xål貉4FïØüp²±–[Læô<[œ KrçPvâ*X óÒü3¨÷U‡œ,ùíY´áÉBÔ¤Ð1ã8]sD™Uº JÔyÇG¿¨Aâ|ºl=O^‚­ËZ±e¥ ÝúŒ›uÉ¢Àë°…1Ÿ ©íWÐó‹ñvnAÛ’ép lØ>MÆ]Ë ×jÛ¡ô¤Žßùö~cwÝ Färÿ`mÑdÎ=:—,œ±†Í&ËÝ©Ÿz 3êv[Š‘üì¯éË©E»Aõé0èÏHfl¯Á—úbtŸ‘;©­»Ùæ]éøul%ð9ôÃÜSùØõM[qS¨4)]ôFuƒ©þܧ ùhBüé„UãèÉaàé¯Áæe `vÛ ·óÖu¦D=kâ\Æ÷§Á—tîç¡Nè¯i• É' IT~¾‰ 'e&yb‡ù¾ùœÍƒ*5¸w«6yd¿†Ú„i’@§ T…«E¯8Eyt|^ÄMìùÁˆËÑóçf³FÜþó$ªÓ"H‹"ÌÝAð;w•QV;¢‡¦mÙ\üäïJ<¢7£ò¦ÅÔ0o-¤|FõÔEèÛoF’#ZÀا”I;”3œ7’ks\P+HŽ^X… òdž‘4LúÂñ[ó޳.†Ÿ¹É‚ÚêÐU[ÛøhaÕgޢв ˆdÈk¶¥¦™»äÚf®‹z Ú½:Ѱtµ!;+ƒqF(+—a6l\€nX¹y)QÞNþt²¾yW…Î&_¢–±7žk‘[/ï⇀™pä¬+DšÇ±'vr¤Ë∟î¬;‘È]"#ÉYô*“$­AÇÀÚ¸ÐÍ­Nìè‚HXç<ƒ=(bƒ-"½òáúµ"hnù€«æÅà “L?¾‡]™êN5´£¹«»z™¯·* ôžåÍþ ¡{Ÿi–ôÛË2½vŽÝÝ¿,ðÏ"û5LéÈë 4]Y÷¾«RõGe¬ÊéÛ$LÍ{ᨕ}úÛŠ:<‚&Û+˜¾;BKÉú+Ëö l†MÜÌC%¿^â¾Ü®I£çDQƒ7øêg[:§ ÕdpOX Ž‘ĵ¥þÄÒr:´®c±X~^žVj& Þ»\¦°#ìÒ[Býº2Q¡ÅÅ,‰þ½n²¤¬L®àJÖ“¼ÖÙO—oTĺ±õTQãª}YEžþÒ%ÒÎÓéÜ—H÷³]3ÙîO7>Eð":¯þÊåxn6@˸£ÌJ‡cýnÑ[©LøâDzÔõÍ øòÚ¿|©¯aøæq4(]H6{³A“åͶÁòñ$4½”‰ŠÚ|Tçдò¿s=>3QJ,®º Ÿûº¹‡Bì¹7§¨0öJëé¦gE8¼/œ«NÀýi0~Ô¢²¿R°[äªH†Á‘ Îôª³9ó^€Ê¸BqQ®«ÃÀx!X¿ÜM7Záf[a²ái;¦ó¸bÏšfÔŤ=Ýøö‡,£uE ûŽ~DÇ+G¡£h=~ñëîl¸kÆÖ›ò%7;}.6@l 4Üè??7øì`FΨ8ÒéãFDö³©Eqšúæ¬(bü¼‡KO#ÏoóÏ S©Ý;²î*/¹ïù*qxàYÊ,ùÐ`ùµ•3<Ó ÄW<ÜÕë>[Ò|‰«@¯\YmC:«I™êwa$å¢àììá çÞQù̵÷1¤ŸOQa’سÿ^áÜÿcGš¤Ïc•Ì\º£¸bo¥sg籕/¥X¿ŸÁjf/»C. /\€r~îʺ{Þ™Ž±<ÅÐ4VQ‹†°ï0.¶aL×Tâ2_šU0†yÎÍPåWV{K±ìOÞÿT‡9¹Ç0Š43I{ñà]dò~+ñ¼[¨Q¸€ ?f‹™´-A¥v?xè!HßUYÐNÐ#“vÁ2Æc5IöJs”7ø óº6PúDŽ|ùõÓºM¨ù½ Æo•5;”ðƒ¹{ñ#6<¾‹¤§ƒÔ†6H­^R7S·  ý:Üï×Û ½ï<ôî}íí LÙ[ vA‚kÛVœ¾mþ0}Aº$³FŠì/LƒæGdS™ ¼½¹‰t¦ÈaLR$Ý1ºŠ³<ŒâÉGø¤IV\¾æLæT‡œ ¸™>žÌã#ÒBå´É£vGª’æ  tÍõ,*!aOìuo°G¢¾§>°Pí]^49·Œ»§¨ž^~Ž×ú#1ˆ%_§¶R²t‹ócìè1¡ÍÇëÑïË^Ÿ} LÌåHœpueZ/)#“v˜/]Èa§ÝÄ­ɯ» HîüÛÃçÊñpËÖŽÞß3“ðq=ð¸’?ZŽì¤Š¦aä†^}Û‡%ó!þžö.†;%J¸V6Žfϼ‚êA¥·eoŠÑu»–’6¹³ôiÛHÔ¸Ž­ÜP·s©ù[H§ý•¦ªî ÷ÞZP*=H¼/ ¯¥¤Èû·lÙ¤‰”·ê&Ùâ÷òŸÌ„3^Áϯ#è!@Qí^:­jîÇK&8‚ ¬!Èk:±5Èâ?ŒmæJô‘Û*LÅŸ-È­5º~çK6G×Ñü D*r IMÖ:25jÙ­'I“3Zàfìªx¹ŸtŠ/¡ÆaHÆw›bäi°Lfà•õ7.·L‚>5I³W³“ÿs©»ôkn {¿î“ôÚÈ2AÒ~¦ k8û°z¾3”mцSŸãõ/`uÖ‹»*–âéÛñ`g'sªùHKx±. XÏ£@«;‘w¯?­[z»AC@—ìœVˆ_Ÿj‘9kA»J•¬>‡o3ÿ¡…m “sá9ÚŠœÂ7óôY“Šì…á ðJ“ÁWa ٖù˗G¾Ïñ ÷.ÜDs1k [2üŽséAßtÜk†ô^„ KòSèyž ¡lj`È=IËiyÃRj÷`>óåß!ö€þ ø}F…6½:Fè‹"8±v-9yå1×ô=/ùµL‚ܸ!LK¸fÌz¡·`k3ˆ{]o2¡OW³#ÓSqÿŽÖBÜøßªSrŒ}±/u?‰£§é<¼²xžL÷¾…ïVÖtÆÁÍi+ÆLûf îÚ‘ÏñVÌãÔóTûZÚôNØMÆpà!ÁãÎ Ì?ƒ¢ZŒA¥‰=…{¢xÑ ÃÙWjðæ`5ëQ± O§F3QæŠÄ}³ þ××y£PŽ˜–’ìJ†ËPl©nú A2v8¿F’øÆYÂð6+œ­\nÒgqþ¼ .6£xVíü˜·þ% e¤@/eÔÖŒá’S<¤cÆ=ÐOAî£oY''êúá",ßå‡Î©T½È>ÝU¾q ¢Lï0Ê}3!• Y-û_X6ɳlÍÎî¶—¨ô>“ì\vüêSÈ[ÃXbQ²’X<ÜDÜZ:Å` õß«E¾E‘’äϸÇi!¬«3@Þðwýø[kE>øì£ËEƒþ³ aòضFˆ2 츔T„F16º®0f×ãÔ lüLl·#ã%¢Är³(Ì9>ƒôvfÀßýHÔùôêG Z=ý%‡Á&wæ1úݰ±«‘ñxPEÊÇ`}Óxa ŽÕüD›ˆw’¦’Y›ZÐýà&J4„hÛÀE"ž¼ƒ5¨žF×範£Rçÿ«?fÄm¢tŒ7í˜He*Úwâ‹õaÔ>t š.aO (“]òÎì‡C÷!dx6ûï¢"ÞS>‹_§_ƒoà@ÒB*»Óˆ²{˜*Sˆýò"ú*†Å]+ hé8óÔý)7¼ÎIÝNw\Û‹ó.Ô“§æûéžqÚ±ä:ùU¶–¾4x *ùnä…ª%ùÝÿ‹#ú·ŸÚ(·Óþ°ÕDù›i<9]Π|ž‡H¿:Àld9Ã]Ed®N¯‘¢yiåó‹iy)JæŸÿD­»ùà²MÍÿk«Ÿ¨èÂk÷!Ûw*1©ÁIÜIîì“D‹7Š42Ûy¥ûá ­<¤G9Ž2Â^âìâêlغf¹tç œÌ‡ê W²Ã =°ØZò7ÒìxÑg>u}rÜüm«¶³äÄ<*†¨i6ý0l07#¶ž¿Ùˆ/i¬ÖàEð3>Íxë’ÚÏ9(ý:™5ó^¢²¶ìGƒ&œ´In úÒ·u< >s5¼ ÐdµÚÝðÅÜlÚå9—4ï?Ïn_ Áâ÷p›¼³ÇÁ]rTÂÇÞ>œ… À…ÁI-¾xæ Òg;à ?à§>üû †µ^ìR’®œDW‹ndü̹7šNÑß^ì»5V8ý±/&ÿ¹„úƒ³0¥¾Q°Åþ¬˜™; ¥@L´}ÅkatW_ý¸œ-]ðÍ~^ù jº$ˆe’.ûù (Zðª£uç`ê¬øëVË>l| b„Ÿ¾­¯Å¹’9xÄ: }ŒÃ‰À üññ ±®r vþùhV.Ÿy©ëŽc4çP™P| ±{\È€§\û`GG ï‰RöØÍ•Ĥè5ìM]c‘ت˜rÆié*²ÿ¸ݺè>*뢇ù(“RoÇz§óÑý®íLˆMm0Áӧưf¯9®êW#&ïì.ûmFŸNïdéÖ†HÎʯÄ!ø–„-‚äÂÓÐÎÅÆÒ£ ©B{žµ2}^ŸáhÒ,ÒùÀ^&‹39˜CëzØdÉ Óö3\ã|É™BEê[àê¾.h58{ß2ŽrrpÉâl‘T#G§yãÑ.´ûR%Ï|€Æ·é~ùH(ÙW„/_\Ä´Þ5P,ÞëÃÕ˜g3Ðe¾:¼ýÀ¡W¿è1Ò™°÷qIõKF¡}˜{ã#¢¾M.¬û&ƒwo·1ýЦ$H, Ž›g4¿ÿ†¥¯~âцFèñbˆñïÄá`uºQ¤RÎ `{©9eÞ7Û=~ÃÇÐíîdÖW[j,`ì†]“³œÒC'3\¸Ìÿ-è'!Œ¸]‡¸ÚZFt×±ÛðUA‡jŒ!g–Á·+9•u1è±mYó{;¾½“†­W—âÈ!:kÔ—¦ïúŒ³Û|éQ aâϬ¹K*dúÝÜýüèª\ C×[ÑTþ-žÕòAßÊíðe¥=Ü)BïÜ aÓTÜÑy§ñU£ŠÝf 9•f]&œÊߨ·÷&H8”qOÎh‡ò [IùN>®Nm5ŽtÀšÜË$gÛ 4Àñ ”>‚ zšê󶃰·!½4V€ %tÎËÚRv *ÆÁMÓËÔz6xÎ #Ûj5¨mõ"2X§Š7åHhîaÒ6_p aãP7*J¬€Aý Tk#†qÛñÖ0tI¥ ƒ†´JË~°åÇ£º»¡q¦#y¬`FËr€®énŸ9’Ö2›r.†Áò_Š4陊£ôi=gEÎ^Ïb»Ì`ãJeê´E8#@åj±{¿7éH–ˆ@qž KØÍVÇP‰9 ܧ‹ ȪBòoá<’ø|1£Û^Æ=¿/[>©ÂH É• NtÕÓHXÝ)9y¤Cá d yBæÆ©d¼´™|‰;†WòÚ/Œ€Ä‘x!.F?4}ASö€Î|Ž÷/7 ‘s¤âÏáõ´PŒEf¿fYÚü\ÚnÂÊ'–t« uz1ŠyßK ë}Ѝ(N%9GCà§Î+Ü\Ïu|¼”õ£¡$0Ûƒ.øMw8Å«éüv6ýxGp”í;ÆŽž|Ë–,ä#w¼ih|£IèÅb¥“'õvn€"5~rÛO,JåkRR3‡X˜ŒÂû5LÓ k6æ´.‘LeÖ¿¹€Î–•LÁãNÆô/U™#jÇÕ‰Ç\óIþ@Qoúo¸wùs+ñ´ß&òšsý `Ê—RœÌ«îôû´i¤í-eÿújÛEøµÀŽ?žÐÖ#J}dÍh°Î_V? ®ËR#Ag ñ‡?‡œ[ºœ¯žDç]òШý)0½ŽÓ¶ßÁÓù²´MÙ”xì!Î&¥xlad_fA´ÄBØÜpޱ·Xÿ_ 6ÇÈ3;ÔkG‹·'@nÁzü’±€öI°'Žª·+Ï7üõuÁL½­,±Z{n[¦ÂÌV]8X3‚¾Óƒñ—ÔTjƼJb”+ìGN~šK›Õáqyêo`x¬°‹œÈáJèžÍÿ~[`pˆ61™U‚ÅÏ}!büŽÜá÷øü'™Áÿ‹¹+ÉöÇÚô£‡˜Vσoßc©x¿=mÇeEèz~šûžš$ÆÐ@‘ÛP=±–³rté©ÊØDºv+(š·Ü 6WüCÕqpãW0ldÇåipž#Þs Ζň¡mŒà»tµ,EÇÐ50X÷ŽeÑ»Üñ:DW’Ÿï"™½‹z§Õ ¿—@ý>5’ùq.†e~iød„ϼž2¥V2ô{ð¤ÖKÚM~&[‘ÔîL8¸÷7÷gê|råÏ~†:HÑï}³Ñëí|^š Ž›`Ú÷ørg^[tS¬~q%'sQ«cndâß|]EkÝ€ŸÏ‰m¹‰Ö§ÆQôX/ø&Ç>y?̸^iîõ7ÙÙ»‡Àñ+Ð óàxt-øVIrëdŸ™zµ3È¿¯Þ-!Ý«#pÆ ñÉ|…kOÝ yѽ¸@S’ÆÝ%wDüñö¾W zO$©EÅÝ: òÃù苟ÙY÷F@³á<Ä­¢sžÜc-Ÿ¡¡@ ,lŽeR/›ÁÙþyÄg©}¿Xä­ HÛZ2yã¼ B÷ÑÇç‚É¿?1d|ákü0 ¹\:3Š¡{ãuqx^v/ƒöµcx¨¦[jT ;ñ<5ÿˆDšÝÏhˆÏ÷¤Ñ2qÛÛ(±=–n KìÚ :ÄŽˆNjX“v˜«È­%óÀàhØï\‹7bÀ?}%æpÈ?bз?R_¥»L¸û–“õê)SÖ¶ªê?£Yý-8M¯’1Ü:øxkHc™4 yû}’Ò¡æI‹Î„øUvÎòn|ä¢KÄZQçí›Ð¦¡ˆñû6¼ˆ7&§›Ùí_Ï0ºWÒ)ÓmÑ­í9^þ|õ>B°h/çȵÉõVåaµ§pÒqý7ÎHËEB …’™î‰9X71Aî'™ã >”³º C#>1R ò¤YZ‘\•Êö¼¯ÕüÆ”®î2žäîÇÀtílª^z‹±‰ªfNäIÐ÷ïµÑÚoõÒ!Ïm]©Nzš8i02;ñäÊílí×û8nV¥óIÎIYx7£…kŸ| Z«ŠàwÀ Z·5–(äH— R…“â$×÷ ¦È×cë-kJ 7/<S¢1rns(ì{T(ǧ(qÃæ=‡ÙÜÚΓ ¾×NèÑ|3Wli^GÚ.߯KZ Ý‘²—ÞçÄ#§&u\Å>2_ìdw›0'‡î¢ßSa0½}ÿ*é‚Á~ †ú9Øyæëî·‰¹É¢Ô.BŒÒl—ÍbÌgÞ Š|W„éÈ-ßò— .Fžÿt†3|k¨xl î6¸ >kŸà‚EÅ ºZ’žýÙ~&´³n f®ÂœY§iÁbê~Ì/«m‚¥ÎOÙ€Î*¸:k “FÄ®ÁùLTí1ƒk+ž>ÆýÏ+`ŽÁ5ì\“I7pßÃ\MWuò¼+©˜…kn2dL”ê´ñâ¸ÚRˆS•€‰ÁG“û õHä%Yª\–ÿ¨þ´ Cרf?í×G´v6†Îx0íÈ.èFcÝQ¼î,O+Æ/Âuqvµ=‹æëƒSéö„¡éÓöôeëv~„;Š“Úøä>Ì—ÌÇÈ]Ÿàû§ìlˆ­7°qÔ OÞŸF¶ÞœÌ ç°áü*òl27ŸozÑæ$Ç——áYPÂ!=Ó&±ñSßò—;0…—\ûšNl¼Äái£(Ëë“Êúd0AÿžØæý= Yké2˲(ì˜I#¸Òð. {m7®ôÙEœ«ÆðÃ$þrÂk­¯1Š‘š¹c½ûÞ·W6„e!Œ­ñw5-B-ÄoÒ T{‚¯ÏgÀ¾­Äàîçߎ?عێ{3Ô…y1äÁ*n‡]7 7Ç«üEÜúKñ7,š¡!ónÎYœ’+‡»ÊÉþÈ~ÔóŸ\IªfFïtiÑÐ/;ÉßH5R¥›|ŤWDƒõ^‰ÓðÜH*ûûDÇK»xŽPQé6l8<i÷FpÖí›Pæ*Fþl@û­w‘?ÊoÕ™²#7¡ <Ÿmt!«­“aMz¸Y×¥S°òÂ6Jy Ùÿøà ™m o”NiÅ­†ÐÛ!DåþežU©}ÀCø—u³y{;¡æT*ÄÕj’¤íSØÞzf¹ÔsnA„ç°×ïÿÆ#ž©ìñ÷'™kóÅ¡#¨ƒ_ýå]ª%W þy©ýA1ºÝ)RÄÑúÐQV}QV/øÆ\üW‚žÍ縯LòÕZpúW>Â<4€+!sxÉm©(XeVÎG$á®w¿ÁÙ3ú ’.Íd†÷ùÀæê=Ð^¸œ4G<…ŽË"ä—/}Øð‚õôä¡zEn\KI0´Õåï̇"ì {4¨}…gíÊAÖÙð„äÚ‚r1Ș„^½ÌŒÌ¼ÉòkXAÁßÝ\Êñ¶as“Þ$A·é=Îeeˆm– ^†9ŒøC:pK•H¼[A2k÷%«èÇ ²@Á ö¼=K^{sžO SôèõäF|ÚÅT4g Šgç……x\z´÷B„ØvŒözTÏ«o‹¾BO0=Â&íyÁú¾øÆ eãkK9kçF#™eÌž E²«FbSƒÀ©ÜŸ3Øv W[\®ûë"ÖA?˜Å¥ÌÜ#ÿ¸ãô%[àPŒ§ÜfK¼[˜·^/±O‰®Ü?ʉ‰î— [@o¶7X X>¡|ÏUÁ}ªÖ0:6“ž…ÃókÈmncšeaØÅÞž¢ÊÔíšÂïÝ`zFî-"rçáÒ“ìýU\¸zê2&kƒ} ß6œx¤×`9@uÑ)A“–ù cóËU¹4…©ÑˆÄľt\)ªÁ~ô± 9'GÑ(I€¾MbgǨâÖ•Ç`JÃé†ÔËÓHì™ùÜõc즻Îìª?± úÓŽîÿÑÇ&¿åÁWœqv^:`¡Ÿz´F •›u- V~¬Äš¼~Æs¥ ¼ïl‚*ç\¦f’3®+ $®ä»Zì;ëý{!i5™ÍÙê…¿eêad©/Îÿ©L¼Wç æe0 µÐÍÿ^Ð-{çSÇá  Ùì‰û²Ü©»k,–fhê·T±2—‘ÚœN[2î³ñzJ´ÂìUðhC&{hø „=¸ÇSèüŠ„çPãÍÞ µçjßÀYÜM&гç%kTâ‰/?½` 5ÛÙ•›Œ Ìz%%9š4ç²½VY͏#æKÏAþ -äåu#\Ê'YR̺|ô³ª\`޾á‹hjæLR¿ö0:WæÃ»iÙ q±p’û,%eÜÿãèºÃ¹zß°ì²gfhØ«(e|Îó -JIiˆ(¥2*i˜ed…‘•$#)ñ9Ï«’ІÙi ¥¥âçûûã\çsóžç}ßsÞ÷yîûºîëþ0=Ëqdu"tt>À­5£ì± b*Gœ|.ýœð^Nš®¼S¿ŽmÅ3ábòø­]ÆFS(®'ãE ÷ÉG¨8ô¯Ëg3ÏeëbýÁÿön¸k£Ibò¦`¤¹69ñ w¼ Fä\Ì!Žb½8± nD×k·¡|ÚKäYjÁn¾¬BÿÓ*¿ºQA.+eáÊM™ÐžÁÊ­‰qU˜ÍM`—’ç¨Ñª ëä>@Hfùî#BŽ s¡ÖËžêVja…æ&9%M÷Òak‚œÊF°º«Hó¾Ï$~B¯àØ"1:Æ‚5‡ÂŽ%\Ž­ ÔßäæÈl%®3M“t¸§UFnÖÂZC>ú·d»}5sÈ–ŽÆÛ€@ð…ÑÎb¡»ÌW˼ÀgדÑlçt|þH˜LjfF[¥©¥j0íXNÿˆNæß›Xþ hNÍÉ4´‹'9‚$];‹’»Šd­ö;¸ƒÆ¯›@«]j“³X㚥è²ôãÐý+¾?9þ3¨e`ºŠåÂ/²õ†0‰èR¡CцøKÑ Å’™c‚Éq¯ÓàdÀK½Ø³`#k…$%‰\ˆéÅ9sÒòyæT¬A†øìy>G"È‘ %êùñØfã¬QQ˜#’ÁÐ\¡†]/¸©KÓç?åhÎK×Wx)„ÁSklˆÖ¶1xS£‰7ŠÏ"Ï-eú&ä,û4…­ã–Yn"IµLÏ Ü•­M¾0Æîê¢äPK|"_xbŸ¨4tLQ¢¿LòŒvA²¿Í„~\[É8^‚yEÑ0ûÄDÜ1$÷´öƒpf{oö: &£¹^+¦÷hà,³+ ¿g sñð"ÔºÁ6¤åqÿÓ+«¤‰`îLRÜÑÌ$mx]Ï äc"­a|ï‹÷+¨î¸]KÕ‰/„ѳšþXšð‹¹ë½Ü›ˆ¡Â­Ÿ¸jÇÂNÿ?¬¶q+ß\œ?¨I[M²`Ñɸ)» ŸÇ; wfÌ}‚Fnz¸ÒAˆ«k2<¢ëÙ?‡ç@4Ï ¼`zß1º¬”Ë<óÛÞŽ)ÅþmLk{8ŒÏKdø½bã÷IBs}!p—Ï¿ÙLÂ%Q²m¥5™Ý¬N\*¡XàEÝwU“ü5Ìßÿ—=ððtÉMx­K^bRÙOp9 .!eÏæâWWXù2<ïÌ¡>ïDH\Ö€Uá'#fý–5Xï<ÊæÅDAõ•<Æf ÖO€c3éþiY8vq.¨ìs£þjqS¬=¾~Òì•cPrÙ {üç`çî혥£Ò\bBÌ9tÚcy¨ZÛŽsþèCé•oØÌ“;©ŸlÚ)¯¢ÜDIÏM%ª·ÿób@û¯¡G9ü¤îò0ØÎI´årô¡‡PE¶ý`••»ÃÔf¬›õrj%,\3‚‰•¨ñ)^]¢ŠS™ËÍ‘–a¡h ö­´!p>žœiT¥/Ûì±§á"ÝŸÖ€Æá’¬Þ5qâx=>,ÉÁ¬ƒ’Ôxø+vüõ`SìÎAôÂYÔ¿@–ì˜ÓQ{PÄĉ,‰éÇ:ìR£K´X0ßJe.E²-ÑÌ„¡2U<•hˆ2ôr8˜kÙ^'-úhßUv~­0c{. +ÛN²zËÓ¹‹¢6’-_–ÒÔ8uÒpX€žt–'‘»O¢Í¹PÏîE7#}¿w}ì±D¼ƒ½ªÔÍ.|S§´>@÷'szsp6é1æ§ý9žd7¼ýÀ·0ƒe22ÏÜþ‚ÐŽmô·£¶ÅÖ¡o²uIüÕ:BÏ%<žK™ÝkÂÁÏ{)ñô/"^jR-fÔsçtîJÀñ‡ ób)3Æ5ƒÀý¹¸!ß}͇ 5/ GÚ÷âB9E´±YB®,¡ò§à'уD+`:érÅO+6»îz _B>Ša_Î#ãÙ§é˜ÿI–¿ÿ0£ãx ÿøZÉÅ¥ jcBædŠ—âKLÄžSÔ¼a ‚ðÐmr—yÍVH·š ±*ƒ­H{.JÓ×È0‚»”qÅÓL”û”Éý]Ã$ ±z–ø ¼Ù+æáÜ8q¼ýB†œ;» ŸÙ?fŸörr¥ŸÃ¹…SÁÄè<Êÿ`Ü4èÎrúóÖi*/8óç%°šN,Ê-eo„<…+ug9kÂÓ±lÝ Vo3ÀžÇ 9Á™™y”îÊ•btžÙ®ùýÅšø©ôÁÒeó`ÿCÔúû´EÙ–ó‡aç‰iôó¢ ŠS"¬ð ²h‹»7O§–ŸöÒ¦~#új"‚vì—9ét[Þ{–~à¡å}Ь'O7ü) GúeèÆ®Dš’¶••ߺ5 ºl‰^<ÊØY‡Óø‘y¤Ó0®})ænüÚÚ*KÉ¿½ùP¾i¿š)pÑÔ 7vZ¡Ù¢sà dg’SÏŽ!ïSIÂ[tC6]e~®á£Žo‡°Ãóáñ÷9 Øs€0ºÑ¸Ä$]?N¥fú IXe.3ýÝ2n˜É1|Oß…¯…7‹è‚ÀHÜJ!×n¹aŽ0ÚZ«±z‘í™Ë.œñ8×&Çu:‚voºÍŠ·ˆÑóù+HàÁÛôÇœDFY±h>îa8×9oغÎi:U¤•Ðüçh*AeÚˆ•Íf^êÔ± v»Ä\." yŽtHÇ“ëcÉÝ¢’`\¯ùO’$­Í±Ô…ð=C»ZÎ1†‰ü%¼ä†Þc\h-E·×¶¢ˆ¤6Ý51—鑽¹?ðI‹¼ìJÎEøc`ùL:{Ô®]^C"ôIKÂkƬل¸×nàÄŽ,&;Ô'Ø….k¡ZN ׈҈mÐÛ¢\«›©Ë~~UÂÑ:¾‘–aó»ýÈo6"‚†»i®ÌšÚAˆ¨5Åþ–é2SàæšÛxû¦ͽWà ½[ƒ¡ "䢺>ë†\¹Ùp}?œªÕ‡Q²ÎÊÁs+NmÄ#ÎK Oõ|xR‚ð뇑ç¼–hÅÿ?xrn"¼o‘Æê{ùuÓº¥QÛ_Œ 7“Jv]Ÿ¿›Iã·Æí‘ÌùøÓèq1 rnCÔ¯1¶Ã;v ;a™Ñ}Xœüz Ǹ]:Ñ VbNùy§S¿Û[™§–g±$û<<Ñ\HüåȬñxØríÖ£I C}UN`P½-õ¦ÞŒÞeuÍßÃìÕƒ¨f¡ 'Ür°÷m{MOX®vfkcþ z[üýâ™hòä0c±W ýn¿fOÔUbyåî{”Ã}$ç†UÊsX·ó[È“vkrÜñv”ýÆýB”Çù$c1ßýª`Î^Ó#mætÊ·Ýh¾:l—(`Ø3!ë§"M³4bNL àú´Kh62 á%Ë!ìµg¯B©›FöÏ­E¹¹‘tT5§µleˆq>•øñšÞŒ †Ú)öð©“¹K’rÔdhÇÖïÐ}+”mÝ C‘L÷×µlô‰4xC¦S©¤ÙԎב¦TýÃÈs³±ž‘!³<#àâájP§ ð;5 B©-™KÞïæ¥Ï5wãõåJ0¥nµÐ¿‰û¿à=rplq*ÿ^£\Êpk,áÚÇ4Øb™Çµ~iI½JnÃ;µ:fùÁ¬H>iÖÔ¦’Ó Ðú!½|Wƒ¶ÏþÄò¦·ÂHfúñ*A©v"þø—.@M"È<ÊH5>dƒš !Î?šà=Èm±}‰fg}¨ð#âp×ÿ*âÔ?ïдs¿nOFëáîæE`ԞNJ>x„ÜÂøþº"í˜å¡ÕËÁ#Ó {tBÜ!Yzé±|^_˄촂SÖ˜;û*Ê^<·þ"_U6j}ݪ[. ÕÖdýç÷ ±'€úG—Úh©Û@¦Ûi‰íÒP{£ý]¨ÄÒo`ql=G©§ jíeP¯ý30Ç„™ªøÕDŒï>¾µøÛ“éÞÃ=¬ad M]iB®ý‹gä¡ñ÷4¬û0…nñ—áxj<Áóó¾Y[\I¦>š–F¨ÑÇÂ3£wÌ¿»ªä›u0}&-@^ÔYbºî^ð±å;h2ÙÍ Ëå£ÕŒï¬ÏT(1>!©ßeèëà Ç#J½ó@%O‘.;.DÎ/yÃDÊRTÜK .‡ÃÁÅô‚¬0¼8,H®;¼°ŠÛZ…ä˜4xºç2¹…͸ôº<õøÖ¥R˘Ó2Øù»š’Á•xûÁ \Öš R«s¡Ï4 5Ìhüž.H¯N¥œ—Íh>_.ñÒ½s0>®Â$óèQ2së¢?2™²9£sY?gÊ.½vœ8ïM1s X±„‡V°„…%+HW] ¼®^IF§‹Ð®3åhá:΄6àö(@Ê‹–ÞóÈcÑP„ǸAoZJCeŠ4ågAä¾LI™á8ÄP#FŒgÙ“S.z_f+et©ýþP"°_œo1£yJøú˜"9p,áùz&¯â+ü_W½éBòY0‰;AØ—PÎ¥g/Øt›µäÁÂx…;:Ã!§×~ªå Ú]¢%Áv‰Îƾ­Ä=¸Þˆ¬F[Å«ì‘u¾è|q9}°p!ݲ:‹È´íC׈"Uhcdä? [½(õÿÊϦÓÄ­éïêþ(¬£¢ÕU¨-¥Jy Å ûR 'ÙÚ^ÞJ‡€ÓÈò¼ø¨HŠv;”C‹E4ƼºÅ•¸˜GÒ—Œ`ÕÐ ú©ë4nç7Ÿ`Ô3?äÿŒ&˜È.®ÙGû`=]ŒK’¨)³ž\<§N_4X©\O¦óà/<øÎŽVŠÖLêê#0÷ÞQóhÃé ¸FøËÚi®× ]XG2·hQÝûSÉâoGYñØcØtSÙ¥Ûu!ñÛ™Sä¾*P#z¡vw ¯ODûêådê“8V?®ˆÕœ2—­ÖŠ›{ô²M8±9þ¹Ð +%Æ;‚åíÛ4±”½Py„ˆº‹Qž*X1% LÝ~à›†[ àoÈ4Þ³bë[J8!j‹Ùݯ}©bH1®ÉœOU£fqäoýå  *9œ¤FŒ¤ ˜WAú« ºÁ‹ b¥¨Iž0qûe@§_`?›Ä¡\Ð8H†HÑWZ[è#©ùdÜâkÖs¹/¯!W:È«Bfl“£ ¦jt”éƒëÉçQÇc>8òÑ3˜‚Ù–åL{PÊœ*£‡®Çcù2¦ƒG;Ö[’+†M0y íËf™¨\ ™GÅóQõê":ßY˜=3×™œ;zÍôeûÞ Â2¯HZ˱o* +sWÛ‡ý„ääè½_æºã.QòJ¨ÍŽ'ŒS÷Ù¥çØÓŽ7€YòzröHZÿ=Œʱ'¯.ÄÏ¢¬d1ØÇW(†Ãù$'R2š›í‹€«+D ¨÷ðÆ5xWòˆïðÃ@~3Ãfÿ*«„¡‡€ÖBUíì…Á½mÌ·|Y¶ßÝïÖ…Õq¸Ù6§Ü„KÆ?áÂìb4½ª+B)&U“o/D°s~,¼ZØ Ÿ;в?°ëè!6]dÑï."=+É>4ø£)v¯át¾"yá+x/ÊpÜEèPòN²ôçkh ÜO] "\Y¹¾3Ô÷ðiTO¦V *tºm)}þR“µò4y‘‘`!h¤‡ ¾ÀáèV ÂMhHfuöéÐ)âäÐ:êzÅ>l³!f¿sá^¨Ý»m‘så°âkÔháÆ—`ÿ=›õ6¢§‘„‚‹ Á,q:ËØ¨™SýûR8뢵º|™fF ’—­^ät^ùÙU?4IÌòæñ5·@ËóÉéÕzt•6å ¼d䇢õ¿4jZ\F?‡9aóè›A޳”5=´b6Sæó÷L[JoY.â4¢œ»8µõˆÂ³©ªä¡/Ê Ï¢çN]Áï ÔµexæA·ëŸô_èËÀA‘ÅàwÇœߎg2°édDþNd…v†3þƒ|$fš=µAâlœèžD1â6‹Øåâ‹ä©Ö“mQίžîT d:»:ˆÜ úM.•®¡~x7q÷½Ó#·Ç\õL+rR´–[}¡¦w's|bÿAï{!Z#é¹d4Ñ$R7@ã|kŒ¬¾ "žSÉN}[²2i £®Ï¶Õl†©·µ1],ý?­3ªlï‚q÷3XHJïé3fÛÉÀÒ}Œ„æL¢t:ùžÿ_^!¶ã¦)M­åÞ\¨EõzsÌd’kýw±"g>VÕ°ÇNL#RzõôáEoÎÈÆ>ö€Ü>¸2ñÓ“¢– ¶“²n‘Ô iĺ3ºñ¶þ2ÆÖa;3xáUôæ'³ïÅÓ‡®˜Ö.2«s^Çô°bõI8pØW7ñ•Î^¬çJ ²\†$ìgÀ¡ÒtÜf5ã!¦ùr8\ª"_Ò0Qu&áÚ{£ ûMgÒýE é§0XþPŽ^+šIWýMÇ #[˜š»—Á9ÿcÐ{mþ• Q‹ùQ1V¤W ãº[iµI»&ëS7œL+äÜ=#‡9'±·#«¶òùò¹‹9Ê#ÝñìE«™¤ààYvÚŸ](%ÈC®¸Ì£áÅgÈ¿†"6ÄP f¢ŽÆè&ðÓ^8ãq 6Ô'Á©ÈwÌ÷ò8^†}¼ ñ„äyŒ¬ŸMyo¦0·d»!o‹„ÿîbö4Òhž'øÌ<u•;ñLÖYü»® Bÿ€Ïoç›ÅUì*¿Fa\’Ž’ãÙ¡Äè§:gÝåÓlÈ: ÚÒåŸ4ð»‹v?Ëdþ„ÐäRäÁØ?&èŒ8Ú7ûqúmc{ØÊ ­XƒpK›Þ|û Ã6èÓK/ß°R›}QëM¼ÑÞNN¼¾¦ÓŠP4¸ZÎÅ·¨M„:P…*î6YÇÓiŸd"n¿Dˆë{dfl-å0… ®_¨7La`iݳ|3‘1›`»(ÑúÐO࿸…ž’'ÄüD..\—K?Xës9¥¯àEélªm˜q¶´7³‹¢Õ¡jÚ;¶×‡Ëj ±‰‡qvo4'?2gbI]$Ú¾½ Ô$ û'h¹aŒ³â»»â*?[¾K„ÅCÑôö¡bw½ƒëöÅP5Uª£ÈC•?®#&y°¹RønþfÞg¾ƒŸÎÃâ^hì-M¶ú˜‘U7ùqà'?ý×òûî©0¿ŠÀuÖB‹à­tûù02­ÎŒœçΤ’·ž2¦F`Èòxñï$V(†êÃá0zB”D¨4ƒ†ðYÚ“™‰uCGhŽ€ 9¶4•øPDw©]àç>aŽ7…à,°lÀo)aŒ•Œ"©x9Š=ëN`~VÍ–Y‰ó3¸ ûá2ªl;AßFЧiTUò/ägIÕ}ÍŸ‡­í`Ï#¢âz‘ÕÚ¡@Ù[Kɾ±0º(é Ñ‘icRŸJAUÈrøÇ>Üui5|ž+Vóª§ëÅœ¥qŽÏX`÷¶ë°å~4œà¹@C#Ÿã„v6¤ª2Œ¯ž™ÃOï\߀Áv¯Ø”9 ð ª7Úon€°H~8^ÂOîÕ‰Ñù¯ÍÙ¿ê˜''Èzq7vÏ;Œûâ´ fL±$[ŸHSɦ3¬iÀBø4˜r²LÝ£h]Èо8~g‚ç±- V;Éã^›2/*âq—pÆïÇ«r5Y1µŽŸ.ƹ'£à¥ÿ8#û}*-|5ȈºÄЙ}©è¼lÉžÇG—q È•Ž,ýRÉ(74‚Õï!X¿2ÎìÖ!ÝìÐôôΠ}¦[Ä;lc6Ζa ¿åqDLÖÁ£Ï ,pÆ¢º0Fä‘á?¹z³B ÞãÃOZ8¾ºƒuÚïBfl .¥^D% ¨~…±°À‹¢ää®Þÿ<š™\N’5x°øÌ4ž¦I5G¦§!¬ï ivY["#õ3Š ½¬Á ~˜¹ë9†í“#[Fõéçø[èv]“6T2K÷cœµ6¹¤3„-–„lÿ„÷²¿ ï7ö8Òg²n#*d–ê?Ö;EÙz^“ñ}¥Dß5;ÑÉû¤Ë _—¢ 7ÒÌÔ!füè.Øm³ ­¼R¨ãiufú8þq=Ÿ \ñwð’óú+Ví oßÅÂçûò¤Õn[4Q—bÔŽ[7–Á3_Zúï=yô„iÊßÁY{îø;—.¸?¥~ï¸3Ôö¤‡I<ñnK““;É“˜h²/ëQßÃaÜ$`G1Y õœ¨¤éfw˜ÎV(Qø—›'ß_ƒM¨;ÛÍÇnåE-¬ÐWj¿&Žnuãÿ±RÄ#–‡–¸]D¾AÒY˜„wFu˜Ñ>²é?œèÛDqÍuš² ‚Žhßéle”iÚC2¶øB­\IUÆŠ½ Y÷Sº¢õˆgE1ò=ƒé&Œÿ”¯øV¤tÝgªù¡¤°éºÂ›«?±¯»aSè{FÉ_ó:Y•¡&|·p9ÿ˜V^1øÛg*ŒÇØ ëƒ^‡˜‹ÄƒÇ Ó÷^cžÊŽÃñ§1h<ªN‹,HøQX¥õçz‡²2Çï jû|šrõ \”Fÿåm£þÁäÚ^o<Ô NFú¥ˆÜž XÜ/N<‹Öbšô(¦¨Ò¹ŒÂ’ºùìÏif¤ôòSX ŸDФgÓÿ/L´àüêf¼-g ®Áh¹C’Š%~B¤áU&Ûºg)Æ}°‡Ï_c0#*r8çP#ÁмúÍuÏc´‡P~Zzng²ÖkµéséÊ÷{©’—-yœ9›Dd¤b¯x* ´%ýó/ãJ•E°Çï †¶­‡5"µtÛ;§Í;ÊtÄY³ó“òÉÆgH=_Â:ŨдÇáP} ÍÄ´iÅ{%t^ãw1Zå%›ž¿¶µ#è+ä~™I—9—S†8ÌŠ¥î›ÀÏ!Œ©Êèƒrk©Å®·“Üõ®9ìFù_=€W—Ⱥ«VøøÇ^¢e­®siÏéÇìv'’ÇÃÁWß°V;´Àûøi6ªwunð‡SZÄ¡*ßðûÒV¹x{wq:Ä^[ßüߊ™G›Ôè¹EdëJ[îý<+8Uœr{Vƒ8®¬°¤üêjF.Ïöh641‘ îïƒ S_àf÷SXYûOäÃþôèÿkÜo°S$|™M(G¬ÝµÔòáÈC\Ó§^cRTÓ)ˆò$`«°Zmÿ ç?Æ#ªóàFÌTöÁ„]#° ×üDµœlz«H~[„2Ë®brR1¶‡Â§õC(a׺QÞÔÐ]—êÞ«ÃÿÚl¯&ÿi¨K£äé+gøfÁq÷À)ÿÅ9§WÊZe­€¤6 òMN<õþBè–x ½‹L2ݺðóùJ6î.ß.â=̆§qŒí[!¬zý7_•äˆlÒ§ß-o°óycòùzâ%{© fÑôµa±àjÍ/‚A‹[XçÛZB¸î¹­.@-£û`§@¿«ø°]ï&±ëø È…Âc˜~Ú ©ŸbšÞ¤ÁðÖ_÷>¨äK“«_™FAøy$7¬#ç(®Ñ7 _¿ŸÀ£Y*tçä·_z×NlŸÏ,ßœùäF$ ™ ®+çSBÍA~6—^ÜÁ «–­$‚âŸ0òˆrfSûz.SÖ•Oìz 6Û jë³±í³õ— _÷ýfÿ8©1EQê›!•ü B–]Ý!øíÎAœ¼ÇÛ¤Œ–¥10Åé;ü*^AŠ\1¹ÿùB›ØÑç 3é‚Í=uq6™êW³‰v§ÿlAïéjà¹{ ,ë¯Oóúliwñ+üvM€Ì\~ $~;àI¯xœo®FŸDh2ï÷ú“kjAä¡`–ž '3}?³–Š!ôMÁ:ÓȦ¦õ4Lú.ׂ_ªÀŽ cÿÞ-çx‰éÙ©“Xëò¨A¡º¿Pš)ÄmïJÄó¿®3üšOг²•he p÷o“¢†:äɇQ|÷ €ùÂCWîçfÑÚy´ûÑÔ9™nÅ«HÉñ¥ô»…*tI5Ù:öÀ Ó\¦Óò‡¡D6‰+¸\—˜¯ÜÇ9´û6XÄY"†aé¸óÑ|½»ÓÛ%hÐuÒæbájPºSŒÑWNÆ?À¾w=¦XâªË¶?<~o7àrËó\ë—O«09˜öñøõ7â¹íàïžé0#äÓ]>“úõ/eÓ…sqä€'Ûýå «rxÖ5«ÐU-(þ/çœMu¿ùÂåõ&D,h3Wê4:ÊUá«!1ráî7˜µîŽžÚG‹x„©¹™#ž5æ¥uÛµÙM‚ý¨5(Œ=×$Ùéw˜ÞÆPrUçû§äÜ——'wB¥ØWºNkÞ<úH¯¯±»Ñ*,•£ú÷ X§Ï„ü|¶iŽ7êÝ(í†LÅâP²Î¨ŸùÑYÅþžíÉ®rü 297“/ ¨Ÿ8ñy°O½åd¶ÿb®—Û €d$te…3²/%Àjv9%öBŶT<7}’“óE0‰[a[‹-tÿSE‰ÏÊä¯åRV4Ü‚˜—¡Å7¥éØÁMäÒ°y©}òû¯ÂâOþ˜¿4 ]¬ÚcMêvÜ8Ó°JnAœÑaq rÂXGabýR®–ΧU휦‘Ì÷:hG4ÛWЗI°Àà'´Åß;iÑÂT©!œ³tx.ªHFAðzw:9&ßÍ H¸îOÞ}=O¾Y{¢a×tâ±Î…L¸ä©ÙaМŒw,f£ã>û^íÇ4p•æN~ÍcÖ9X=Pñj+CÀóãnpÛðáïÐqŽ@П0æsQ%بo!™;oAÒÑnføÉw&tÂ<Í?°KmÖ1v>ÄÃ[ÙµÝ[Ò Aàæ§BŒé$˜'üI`*EsÖ/'A%‘œ}Ýò8hUˆá/£aTlê­Ä–åÿ˜{™¼˜°%ö®¯ìÛUpy¯5]n4¹×ú©PÑGÖê|´mhÇs<9¾\gîc¨‘_ù¡0pt9.v8Ì~ÒÛŸ®Ýg:v¿BÁøº'ÌËOGmÞ÷R çü&ß“œí³Dƒî"fªë1„ðœR¶€¿hC¶åñ’Y~6hÔãFuÍèê\¯dÂP&[‰vÃW|ÿØƦ<ÄŒVuâyd6á 'õHz:+sŽÛ°âb°ëøÒ¿cŽÍ.¬³R< óÞ2ÕÏÑà]sIŸál:w Þõµ!U|ÃXZ¼…Ô­Q€&—6|ød ]ÿì1æþ¢ìbçrÖ{ï%ÖGè1|Û›àÊÈûë•AE"ù5„ü–x#¼D®EŠþ‰ê†ý|äÀ2O"jtŸþ¼ /ZjH‹_þ“i`&dQÿfbw–`E¯þ :ÅjlÄúõgÐôA.²:‡ÁÖßÙÒʤٓC.,¿ .·^à™{a°sËZj½X‰FWç¢ÜC{’~{.•© çuûàs0æ:+²Ð“4;¥1ÍS´P:¼s/ð“ê%Àï…‡ óá"âr [w0ó‹B!•ô2£>MÆ®±nLÿ†GœÁ|`}N¯^ò`Ã|™,[>nxC³Vñ,»–ÏŽ­™EÞš?AÇ´]ؾ2ÞO_Ĭ6¹ Ý›:á¿=FMO‚Ü}±lZ{³Í¤ ûìA›þrˆzÉbã`?™”³¶b9=¹ñ«r*n¾€r’äÎÍ8RËx ¡€*ê´hÕÁ{Iô‚P"Ç­;gÀ £fÞý!hªùƒ§…qóHž[ŒFóiÝÃÍ„dZ’œ­ç1öh=6½¹DWis™Õ‰Í›Löeôc<·©gN®‘ä_*¬î9GT[»€œ5Õ@9­©dÊ”v|4ø…M0wŽSÙçFr¼Húœ\H%zË’•æEÜ}Yªè#¸yŸ†#~ádëƒf| ×^ ¥µ¢d;lP°¡Ò trÜumy‘xá<ñÕ`®äzÏ–pðjþ†=ÇñÜ9ö[®>Ž(9±Ùßñ^íIœ1±‚.“p!Â6Ò”/*rÆùðk( Ê€ç¸vdÇÒ¬{±Y„$ò €ÈÊa„ƒüTb¶ åsíÀ¡ p:çµÇ‹ÒßM˜Š_ ý»2,š˜E/6˜P‰w8îDõÄÏ ‘AÆî‘‰NtÚðylæ?Å}ª7Êòþ£+N©’¿ã*d¸b)-s®ó½‚]8ŽÖ^±pÎhë“^„G·¡é–Í̲Ÿ»È£žä”É1òë>?‘K¶%'? “¥JÌúÃÂÔS:6·FÂȈ€âLÜ–3Îéþ胢M8!Ûü‚qF—ì„ð2ýVŸgî"Õ›úpªÝ nœ_ óþåX]ÿ†>å¼ø> ðÁ«Ï˜ø&«£èõŪ0ë²)îpà´Î…YfW˜ŸWÂÛìøû©žáFÞÅ—çŸÀ­ ÝLdˆ(­Ø²¦$3ß½4YÒ®Iû’Ùfg¹»úʱÍ=’{sߎˆ\Ì;e‚zØbîðô낆žxÚ©IoôóÂ…ç_aœ½†ª¢Xˆ§¸~¯Cã-:(ߎvN«¡¶ió"€Øóû”³Ð"äϹ&'C²»”I’¯(·ìü-<ðå$g–ÆNÐ2&—öv³m3„˜Ñ;ß#MµøãÙÜNm:e˜>|.@Ý•¤Èðq:öÛŽ¼9‰«oUà;‹öÿ¼˜‰ˆ‰)‹!¿m–ÒPM}zèŠ(;²ÔÈ—õÛT#¿ÿ°±5³PD^œõˆ àÍ>\QcÅþÛãðŸþ™0½J¬ÇK3Pá=Ï:¿DÉ!l Q‚)GÂÐéî-¶êå]”9âOÆctécˆsøÄ1 ¾Ètï5¦ N5¸sé}æ¾ þ§g®zÕÏöì™äÇ£Ñ Þ–½pý† ]ñÇœŸ+Ð`TÃ|ZêÇ]`VKþŒcι;L^tNH9q¿èSáA-Ú£vÏŽËÃÈ<öõ>E²!ð*–ù¹ÓQµ(“×ÄZfïb¿Ù[EŠð|V¾Æ›cÓØEYVd¥‘;ýv^®P%ë«§ÑNË j§¼Š;wfê’‹X–³gQ=c[z”³”ÃÿïÆwÛÁ›O¡Ìã[ê´êk܈[DI2OÍ`E%4¨÷¦t&”ûƒhO†ÅV…Ð`΋ñÂыж»ð?3óìP=3žý’½Æ× ÙÖtÆÜeÔá~\t ßU$;»wš® å«x¨ÝÑ ùÈíðAŽeµð ?-SÇŸ7bèË6xîüù³ãÙmÙjÔDJ†–»ˆÍœNs;Ö¤+ þ˜"ßwSÊ[®ˆsØõ‹F0äÙ N̓$£º.bÝù2ء؛h|#–}a´ê¾Ú{æY°^AàÒ8³âêT"=…f‰)Ѹq?>°âh1ŠKøP²ø)«)¥¾{|AþôrÎÄÄz [ 5éìmlOÒiœ±á.><Ë-¹•;ïÐ’oóÙôåkè1Ç0"ü{.ã-"_þ^¡ ¶®ie.ó55Œó6x!§)ƲdÔ¸[pzÚSpOÓ+æóø7\úà6Üq/B§_©X©ðŒ!V{éÄ??òWA•X|•a)Ú°Ä„뛬c >Ù¡OiçÜàZ:2'ñè@( úÚ‚f;5Ùöƒ“¸À*’míß G÷Ê“]ñÇ Ýæîÿ{»v¦++£©Oùf#á…ÿ$ON%«ÏÀ÷Í3¨þÅ>|™$Aš¥æ3–QØæþü"ÉçYS1ðxÜÔýÉÊ “é!ÂðRùBË+jhÏÇm´9|1™¦èD*,Û ¹Y†“vÀ‰¿Ì'a¯è¦Då¤]µ ¿,&3©ô›"úùB0iº)Nk_Såµ4z¸¨Ïh@?ú»9”ŽÌ'®rD.$ãMf¼wY]ž‡9Ë©bp =„ôáBYzÊþí­¼†ï])¹Q|«žÁ¼=åpj Æ,fŸ†?Æê+Rä\X<«°•-ÆhîÃKÅ&@¿Vf²¥›Â¯Ã×~G2;ÕŠÞhI¦ξC°—dYÁä¾c¨¯Ë RÈv¡^§ D_«¡ÕÛî3H§÷vÐÌ#WÁø'ûœKÛ£Z`Û3ó³ÔÁM…øcg#t].†ÕFÜS÷Üè¼ã;9t¯y¬wƒ¹ò†‡ ½7#ö£É0Ä -—¯aM`4Íò”â^T¯c3‘“rVpÔw IXäAºÈۃŤê÷-°tyÂùR¹—„ÉY3>¾ýÌ)8¢þWfž`7F°†õ™¸l ÇM‡a¾7qÅûÙpªG‘> e×/)»è§ ÷i·i^œ^f”ÀŒÑa|´¥ Ú3áÎE>¢½4EÖq¶†œ»à®÷¬2¹Ž¼‡‡ðt6öß)`x~«cÊÙ.ˆû0ÚèMrÚ 3ê HÎÜhP2Ú§n‡]»¦¿E{ÐUÖYŸ¦Cd–QFsbyÇuJQcj×1¤_EÉý4[úøÄ.*øqøŸò£)ú“×î¥Ôpq¦ µŸ (p²Êíè ásxÆ[šÊÍ&vz¨°z2—rgß¶E8aGôJ%iÇm iµ E«ïã»ØínâA’ùgÀ¥W!¤:ÕŠÏÁ4Ë«œdçZä§æi4ÃØ±0ò_,±¦+øQ8–j¥ÁÆýIŒo¬ ­ ®D^EŽ4¤)Ñ®MXóŒŸ\©Ô¢e©=àUˆ<’쩃衉o>ºûõ*4 JO턦1~¢y{ q’ŸЀ’c\ÃHsrG¬µg¨á‘àT,98į˜M< ’Àþ(O:‡î'A·€Ï„_EžÎ3P»$ÊJ÷J:Æ'­à¯í…Qé°å¾æÞ—1Õ,ˆ®5X ÓÕvÓã³ì°Zô£mÖýÛ"PÅm*ùój6Ö…TÁúêS§‘Àw‹ˆ»èF ^g T,Ë€¦þÙ„t^†¯y\ôöÔ£N±ÚQx’mƒhÑé¨ì¯OãyÚ=wX^G â#B)‰`£¢L6F÷Ã2£µTb ‰õy0ƒ—†Ã¸d*®3ã%nõÓÙ¥Édd¨]•íê?çG•(:u€4FlŒv¡{›g‘¡÷‚ô©ðüd¶™û8ÁƒªÏ³'šŸnP“psÒ^¼™ö-œ+ŸJÒNµk8ERŒ<>ßž ÚGóä÷“§›O’c‚ªuÖA똸Á¥DàËCîÁí¿ W£YÿâñýógçÓö¢Ç¬Ò{øRbBýÆ¢Pöü\Ò|Ü=Ÿ¶—ª—ÝžŠiôéýŒìªžG,(…#‰SàMxZž]µÕói¿Y*Ó«›Æéž£Ot½(¬ú³‘ìÛðKéØÝ\Ý,O*ÈÄÂäŒ 8ë/Þv¡º‚XçÊlÉ.Cò+ æÜ"²§ã¨ó^9¶+ì4ج$û·¸3›õgð‹X÷9¯=aÒéE?Àîá|k}™l=Ñ›(ÿÕšŸ*@ 4Bàò,#:úTƒ\”¼LÜ ¥UõQ¾Û‡¯ÝC±¬Sðz­ááá áû|švÌü œ¾ºpçøá®Brš®°ßgQ¹—9xÓrUº1¿ùžžkG ½OP¡fi¢SòŸ(ÜíåPû‹ËŽvއt±¦f3$ý4#bRéiæ1ãSÂG¯øÀŽ÷8zÿ?¯X[bòëD9ġڛ ÄKf!M °d>e÷U°ÏêÒÉgaª©ô wL?EWîÚN̪\©ÛÇ šþCžÈ*´±¼¦÷ðç¦+°rN>³9°„17ïáÄö—>eHF¸•[½‡áÿp•^}íÍQr˜£\‡÷އ¢Ù%.Š»¥Ò2íÄ9ü5 $ÎAY­5T(?ŒÞzö ‡æÑ»&òT1%#û&5û²ƒï^±çr;kV¡óÃPÝ> nAÉì åý´tB„~±ÃV^"aÒ‹óœ™×9d®z8;­_ìÒˆEÎMUXºìÎÒ£~޳hwÂa&È\—äÜ¡à ðw«[Ûò#Š.iô{²%ÍӀ݉ž¨Î Á€°'=&÷'ÛaÊ %"àá‚N/Np•×Zàk—B«âÃ,^í' q°½>£ÎbrøH=›¤·Jú°öèfPèo`*[OB« ¹†1ŸŒô *nÁ×:h3nF¤.µzñc?íþíOr/™àÔ_;€êÕý7O¦Ì] ΊeBa—½lê 9Âpqx QlOdÛÊ{@µn=òktâ¹ãGðüó&ÔþxùOœÁñÛ±Ô­ê¼QžIÔž+’Ã{¤H§ÆmNcfk?boõêR£G‹‰gé*újÅÎÜç ¹ò¯ÙÖ ¬m¾2„¨ÝÆR˜ôï"Syh1ìêx‹{Æ“Ø;Dé!õŸO—’ï›#±Êÿ=~ð\}kDJÝf“ðß~lªb'×?Ç’x½¦ï/L§»³^þ-<$uw{ÃÌ «æ&C™¬…ó†aºØZú'ð<ÛZvr c£_'3¼GšÏÎðúâ êXz ûÞÂÕ—~2Ég.°¼QÂÌ—¹î°í˜:u°•dΤhãªShÿ:WtÛч+ÓáNk*s-È”Å[©ñ3APx¼4ŠEÐg“µ^ÔG€¨H³}OŒñ—¸d½ð¤Ú‡­nììyCkTﱄðc<èþA¾=cŽ£BËéÀ¦c¸Áí:L›/Ftl¼‘•­ÿ{é îÊ î] ;²ÑâÑm7ÿfrÑñLz%kÑ3’Ûàï¿ut†»%®—: ãÓN‚íÓJØ>_ úçõÀ¼Í‡UL¥¯oBöx¾ør ¦·i“GÜ÷p>7 Ͻ5aÖ­$«ÓòóL)"ú,ƒXK4Á‚ªCPãû:KÂÐ7‰‡¾¼óCV5èÚ%˜·±Š#بG+Ö‰ZWOŸÞÚGKíç·cØzm¢õ…?VÒ„wÙ\t{²“ž×3½sÍìeÅ~z>Ê =5F9ÜÆ0öá+z‹ùÀè§&>ö‰8G†l:v=^n%Ó–ý‰šHˆpË5¯ Rw;—ÈÙ=!Š;—S£½ØwD™žTúÀv¤ºU'ð”+Œ-;;ÙW5ñªdþðeb†?GMrîm%QÌ £«Ó´ØÕÌѺê7¼ŽGíˆû½G$]À†hzŸ'â¡tmE±÷>‚ ~²|Óë÷?í‡Ê¨ûdP$#+)OüJýˆkà"(]1a]y¤&«¿MØÓa)~äF$‘±E¤c!Mުș²ÂЦ}T Hm+_q¢µês§2W_À§‘îä²™i \F L%»ÂPâÛcöÄ·Ø}öè:÷}dÉ¢ZpÔê‚ÿ´Õº~4Ø-FKÁC³(ò&õW>KGF’([öÙJÖÜ…n–~ËuiÜG÷—÷à'ÛœÁQ+&EC‚°|ë¨{`"\~ B4^ž…‚ð³dMxU UR˜UE<³ðæ0‡ºì¢›ub™µSàÃ#Ù‘¼žäwcW“e”¼œÙ n§ @æW>2Á¦8¾d½râôBÞ\©ËM15 ¼I£*/³ßI»ÚtUép$Ï{ëy7>fv9‘?ñJðéÙné}ÄÍ 3ÉÊÅ3hn^³IRŠö=ƒ¥^ø½`>®€÷€¼B4y§+‰¸ @:¶ì +8øNë¨fí`Jr‰’9ò{ÐÊZĘ8ÿ#J¨vA”là#¾Ë]ÀTÑØ-Áüç|j_ ^AI¤öEí•Ç¿>±°¶ŠŸøzG3y#ˉMm ]â«Of.!¼¿t lg8ÖZ »=%`Y¬"­š– '‚Ï1‡Ná¢øi`¸íãðr·Ò/ñ7¸þ™¢ŒHâ&øƸÜCUÍ•¬V•Ùy5˜áô¦áy…ƒðÂ1³DOŠäý‹ Í²)äÍ<æŽû˜˜³•dÉSZ` ß9ºò½0ý+mE—g´Q>çéäÈ•ml»Ab|Òœ–´«©-ù0ƒä 51%/Γ½Ïß#w[êàAGêpmk`n¨%¾Ï '}qÀÒé$*žÆÆ²kUÉuqÂSà íbsHI7Z BG[ÐFÁ˜”méC;e:kÁKæxA?š¯'M«Ð'óuèlzqø ­s\©´³d n²ƒ„Êøý¥›ó±V)ÒúhœµÙ ¯Í‚ëÇÆÙ·+(äÿBݶ“Ôî_;Lõ7¢öpÿd1Ö?bŒ—Ìe9Îâ¿]î0>¶ŒÌ=Œ2E½øwÊ‹…¦é‚ô=u$W±Ïøxé“Cruv'{yqŒ…€luÜ"íN~ï/åRèpiâŠHF3¯%’½k‹nð²­O”é÷}hïîG8†ö¤õI&Û'qœºíŒÂå[·éŽìPö(cv…6¢ß<ÐC[T=´‰n³Cæp$Ûí—@¿–iÓŠË•pŽ­_/`³hH¤’R1ꮓN¾Ÿ½€7uÈ–M˜Áy…ùŸT¨Ô×ë Ù‚òP>œ7\K.\OÃåsÏCpÁetJÝψLÏC[>Øÿ$—žÿNh›“!¹yÕeåæQÙÏÁUå1£>/ƒš¬Œ³½ù°ª0ú%D°´¦>5 û#[ åô ¯ÒÄ!ŒN[fKë°Õ)ߨûb¡Œ¦ÓLZ˜F…QX]ÝŒ–%¿ÙšÕ¦Ìƒ,1°®ÂÞ 8P¹„O—¢æ è½6?x»t?|Š_ÏH÷´³—®ìÀÓõ|ÄÕí¾!ó Í½~åÌ&êÖWY;s"úWÇ-' ×ÜÇæ×æXX~½‡-ù}µªv‚×êŠ$2pÎê’‹¯lg¦"¬D¨áSFi—Yt÷;èX|ˆè FÁ‹ù!ŒÌ½vf³Y Vë*6j Ýw9Úƒ}9·z‘Ù:÷#;ô³çÛy™Vã80áÈÄeIbß¾S0åò:ø:t ghw,sR†–kŸd9Š»@¶XŽln†#>1ŒÕ\EÚ{MŽÚÄÒ’w¹Ì¾˜Ð ¿NÓ×̰lµ»ÏöNí纞ì†x`šõÍ<_+Ê%m Ê2BôÊ›Sý Ëó>sÕÓš¬9•÷$s1f¸ÙùCQéئòÓÈA½ó°5вôI\• k±WÚÄå‘ãÎOM õ©[0‡^D&ãŸâ¬K»Ì¾`®™ËS`§W_Ÿ3Ù±Žì º†]B¡ ùÓ‘<ønÆJì9I·fn¥¥âçQPq¬ß…Ó'ø¨¯èÂí{ƒÂnàÞ£F’ñ¦êQ<%¶*Ô†!Ý̤–Ñ WÐ(g'2Å7†194/ꪦº“´9È×U×é“mºäw=nù¼ƒz—hÒÎoÔ¾¥Ž»ŽRwÏeñê×W©e€1uØLßÿcˆÌ‚½D­h/ô>xóÓÒ ùM`ý/@ô¶î…oå1Ó~¦sº±ðw.鮟UÇo‹Dnö;«“#GŽAY¨\ùmN,*®aZj4)}þL¶'Ѹ‹Lˆ%1 Yо{iN·}}Æ>ÿ'™š¤}wôñó‘ð¢¸E¶Ž#i«A‰¹RyM=Z%»”®>·Š†©ðp´W“Ú”v©V„)óG 8ç-Žõž%Ly‰Ü¯A°ãýÃ}Ç&n ¤‡4i¯Æð϶pbu:³fó1vÛ¡"F|h.í^N¾b¹ ‘Ù5ˆ7¹_ vÛ úƸˆþù n9Æ yQê_Áý·¤‰è–@³‚pÃ×wÌsµúðèNbÇ ¢ç ]»@ÞY›¦Z½e 8EÂðkúGûlͯGýÆà5ƒ¾éʵ/ kÍ 2µy+õ^£/sê¯ûÕ[iøý!‚zýÉsrû$1«¿) ât‹›³úi0Ìÿ•ò@‡"1梱³ ©çÖ‚]W1ð¦‡æÍÌ~šXë¢ä±fF%ΰh‚rÂÏí£ZÊŽè;E™ýýgasŽ ‚rZ!œ•s¢^‡Áár)gcyòyÇa‹qæö½ôOŒ]ê7Œieké]Yè»» ¿lYNÕ¬9ìÕI^zóG÷!„îÞcNþ›×ÝŒ©ØàYEGÿ½Ç]ñó÷ÙŒFAQ2ƒ9,ˆ” ½tøg“Ý^²¦|=œŒ×`Ç’?ÀB°!?–\‚xÛÓPzk3¬îÓ oÒ‹ï•Ç¼å „«\gÕ6æå5$|áMªw¯}>lÃ󋔨++A±Ë‡›f‚é£gŒåx8ùSÂKþó^-äDUº'±ó¦ÕÔÛ© >-öAo§ùxP1N/gª}ßÃ¥Ãؼ8œ¼Ý{SÖažÙ-ø´è½iÌí¿å(â;ʸ‡oçÎç$§ÁüÀ’êx ü”§RsZ ß~†Ó%/cA¨G”LŒU1‡KVјO:ô‚K »§Áöj,„mMü4~!5¿àÅ&š /5²íP8ì¹ ÅVOXàeæ)\Ó¥Ù×=áËt+OpÙEâùpëÐ"f—±!-VÊF™·ˆ-çïâвU°Pº Æ“cnãľ”žŸÍ9ïós#ÿr»nªÚÓ¨Þ­HŸŸE±oñÖ¬%D˜/mWvW á–°É0®;¯ÇÊ%Rtó+YÚ á kϲä´ì‡í°'3ï]†8¥X\öba­ýzo"ãv€î¹¤GÚÓe¯Nöud2×w1]œ{ ärÉL¸y'Ò.÷²Q,Îûô ~~¾ËhYHçL-GFÁ$.ídœgO'/©ü3W†àq*ø¯ýØÊWò„ ËÒ‰Ïéä!&ý*§E…Ø(ÅGSÿñ’9£(2W E”—€wkàÉ̲ڶ²ÕpQê<ëÞ6Rzâhú‹Inc$DŽÝ•§%#¹AÏŸb\G-XGÖ‚ïëoæ•Ôĺ©HÛÎx:1Fö2W¯#iÒƒìñƒŒ‘‚-ÑÙ¦HC¦ŒÀù[QÿùIcÙ…8¬ëˆ‚ôjHå8¬ÈŒ‹fþš(’/‰«)®ØO¬&ç²ÆÉ•Ö¿[MTßN¡÷o# ïêpMœ;*ýS ?6£d«=¹A›X^Cý±\«)ßÙÎjœ¨á!ª¿‰KX/û"r6ÝËv¸yÐòG`)ð\F¦Ð¢ùќɶÌiÛ|´y§ae;w-Õ×΃.°-ÃøÿaÌ7oëqkøOìßxŸSùµ†£ŽcÜ´þ F¬2‰å‹VçØútr^mŒ3Óo˜R3SÖþÄ&Ò’sFîžÄ# Bô.såV=E¦¢ Õ§4BVà‹eZtE¾<ÿÖ‡ù³™“oMð•»)¼Þ&DŽn]‡;fv‚ñ¿¹øa¢/?ëÍoŒ¶á\0I¦lo½'ÆŸ™€÷‰öøY"ïÜ®]ÑÅEì–°ýØ[ÿ†Ùÿ2‡Ÿ… …lŒé“:¿m'ïÛQùî û¸ý †·¿cí7ããë‡pž{³ß,Ý.3%[ŸZ¹_íBQq’bɸ¼]¶ëo2Ž+ÒQ|í+4mB·†佦I6:ôÉœ1Ø{‚Ã>†K/ÑESÁcÅ2¼óCiìj J¦ªr6_Mä´ÄÆÀëyG'9ÍIö?]÷éÎY\×Ò9àm>•Þ‘ûÊ‘ÔïA‹ &ØxL•n`6ª^f®f„/Ûà2­”=wN_¿ŽMgY'þ¥dQôjâ–VÀÌV³ ^bK”¦sØW¢MlL•!«kS<Í\øqõ‚ý(ñÛÛññ*ì½#Âåþ‚õ¦tÖ9g„6~ÜuW•ZŒT€Fós’€ÏG8gûÎàXJ.ëå[‚þ_¹ÐoHYÉö¨`ˆà–6 ¾OeV ¢ö¼0â0ú~]ÚG"?ã‚ë²ð–p°¸`6} ¼Üà·Z(hæN™y†9°Áœ®J©áx¾]ŒÅ×?AÙÇwPüm'h‹¡–û)”˹ "ºáë[ ,œCùíav‚Õ¶RÆ”¦¤4evÙÌ%ƒMz¤Ly ;_B†èÌf¿¾0­ìH«UDVŽõ°»ÖÑ[brÿ锹ª‘z´ãø3Øø[‰d1¿áKv d¬âÅãáŒ|:‘tœsç*“æÏz`±ó°––ä÷UkÆqùhÁK=²ŽÒ§­¢TÄO|>Ñ«£ÈµÚ.æIµ~9ʝ"2±¨•-Àyþ †LÎ’+÷Z){ÕàÉz>:ÔW #÷SàTp+V‡c#³¾¢ËA1üzç Ž_ÏgêNÖ³–ýÄošleî››5Dëb (‰à—žSôŸ³á«¢ØÍ¬<\Sc`¯Ò|ú0Od]‡ÏÇÁùCWÁKr?<Å›•è+#,ÿTuÐè“üš—¡3¯±n.€ÇÆw!Ka5Dþ¬Å鳜˜´lxàÏŽ½?ö‘Wqµò;榬1 ͸ˆ3n0Vú¬ÁD>Çü§Õþ#̨M‹Úp©’eS~î¤óÕ ' 3¿ ®v ¬àºsÜÇéyòxìL2wÃ_W&üy ómζjQ š¬Ñǯè¢Ô¶FÅ d—bt®Ú›Žƒ‹¼þ$êÃ71ùÌÁ޼InYƒ± P¶êùi*r÷›Á– '8öN–h5ƒÅ‹öÅ®T`Uât…+Œk¢44wâ|Í6±¶ƒ‰.føõv01õ¨*ö„q_-Ä” -¡‰Sep‹K,óòÊø{8’,Wªƒµç²à— ØT+pÛØ4X£0ÂiYaÂ>j‚ƶ·ØB?g s››Ùᆋvú’øÛ/0n÷^x=ʶð¿€ÜyqŒÓOm2ü£»þí;t”»ÍÜ¿9ÄŽ¼ÌÆFÇ*œõø «no€{–Á$~$8,˜ .bë@4Ý^šÏ=eÞ‹mG±) J¡_ÖôîÈñy9Ü…Û!è×>x‹É‚<ÛŒI×ûj<Ô-Kvlñ zßݨǺpY¦A79>fÎ_÷§7Ý’pMW$ÙXÔ‰CÇDþó‘f%íË1/{hgj“ý=>8ªLA¥»¿ð§€¬ó³äàBðL‘@©Ô_l«ù*"Ùƒ¯?÷„lXòÜ€nɇ² ÄÔÁ2LÙZÁÔ{2–Ï>>žHEWÿÈ®ÜHítoƒ`ZÓþ²ÏM±†O÷*°Àj á+àļ™N-/†S’~[Wš’·yB€N3±áå¨È‹„õ~vôèÁßÌ/“ùä¯[$=Í«Æ\· Ì6»ô8[=f†ÝÕ€ÇW™½?6Ÿ\{ú„1h”!ãµI¼È!œ’fM3ù†ÀJ´ªtU Ç6ðv(‚b[f26¬=¥LçÇ?F‘ìhz÷R;»ìf$ý­°Ž*|þйoþÂLqQ»ù™Q2¿ÊYY1›ýÛÔŠ5ÀsKž,™·N‹ÈÒÎZ¢­?•´Ž¥´'ÓiïqÊ1S¶Ja)Ç+ŒJh"‰“çÐÕÏD±Ä®³ãïãû5ls­!YÀâŠp|»Ôü5%hÐ[gLTB‘›à?e§5y_qlÛ% ù=|„ZËÓMsÃ’Ï`ø|t©E£r™põãá³öS£)M¿@8,¸K­Dè÷€ ìþ¬@—•« 裼ë¸TP³Þ÷¢cdæ1˜˜iðkh5ÌUëgýä¶ß‘ó%襹VÌ$”­ú׺U(ÔDÏ–÷b±V ü§åV)¸‹÷$_aÝÌ.¸ýâY*˜ÌIº‚L™Ólê½IœÚFˆïîä870iÆgávè(n¾À^œ§O÷ˆ2ÛæÐOV](ââÆŒÆßaG{˜ò’p¤}¾í` Îm'§ö·°JùQ¯?¯¿R¢ÆýK™±'Àþ®hE‡?™7sdÈhTs÷o Œ×Àxn'JO£ÿÎ(B£ÊÒ‡p|Ö [RkDÆV΃N‰°om!Ëz-€%?2«ÅÏAûÝžMç™%!ŽØQ`FúÏÇ“MöçØ£NEh°ì(Í %£D_¬T§Mzæ¤3NfYç£]¥©´-UÿÚD­ ú1H„!Š7À«ä!ûí½:3cÝ:ö©ø úÏ¡+·Åßs ÁYã"yF›XiÛ¢ì¥WL]¸&«ú]Œåˆ8ƒ³ûÌ_Ãð¸ÞD»¯WØ•IÏYÓî·Óò>q´hO+³8ªš97MwJ÷°×Œþ€§Í~öžH\mÂÁ¦GœÛå¸t§,q‚U”úd2wÎà`ë[%Tßu.Ítiz/ÃÍÒÃÄæ»xãß2¶`¡/þ-òkŸÁ_WcÝ?Q˜]þÄEfÓk椡w1Ý[®‚û¿˜à—$rÒh-ñv¬Ä»¥ßÎ”ãø¥Dšy½ÍM*@Á@sôÝ|ì{aï_Þi칇$›,ÛÊe¯ÅûrJ±?uï îÌ<Š9ö‰ÀŽb>¼æ¿eªô3F—Ж?rè1ïk•é7AFq£!®z´ 3rV¡ÿŽçl{Á$ç•©$AáíÐ:»¹'Vé±Zè¥猨ģ÷L©ƒ"Íú–ˆ©?žÃÉå/áZû\zàëE!<»ÂqrœœÂÌ8UA"Ò3àã>k⶘±Õ¥%RäÌüоZ iZÉlÑdè"z&À ŒæŒã÷‘ Â_U*Á¡Þwõº7Õ÷aíGgê_Dß6pƒ”@MHiðC¨W¬%vvФ½ ¬nبØ+0Ù~M÷lA½wÒŒñ!Zy~6_ÕqlR#T{°ìsèŹÓ:Ö:_ ï>…% è½Im5;…*ÊÑÔ±ö:|Ë ™Ä`©ø¸H­UÕ©YÜ,zúÞkY,Ž\ëd¿d5¶?„¶ã¥4jº0ÙÃç°Ì§†ñÈdúMC1½·<™~Þ™ Ð^K6ä°yÉP,¿6¬ÜˆS›g0C·á0ûã²·Õ}Žö†«ßsë*°÷ý¼1­-T‚ÚpSÎoØ™ÐÄÌ5<ßÙÒ“ÎM0Õ,•é|n†R“øÓϧ²6\o”£6‰º8‹áاPŠÓ¡ÞÑVd:—µêƒý¦Ã,¯“©w’!¤O”Ü2wý”Éë¼ù˜s ) ÖG0˜Oø&óþ{oúZæ(–©ŠÃpR$:*M'Ê|³©ÎÓPêž?„Ó£Ltþ*îdßLTâ2úúp'°óñßÕ`°)˜£3'Ì=Ç•m²‚¢«¹Û;î@á!a’ù6ÎdGÉãÇéð)T ÉÇÓÆÁqøÏƒ:ëåL<áyˆ†IaŸ.©fÒ]ùHnÓYô“¾ÅŽ4ƒw1Í–¥Òžš$·S….“pDG•ðÊ•¤–^jlE%(”×¢ôÔtw#WíuÝ1³’ˆ=•…îÑ&áϪh¤L†_b f.%r–ÙôWÐ"tXfJ«ïØÃ€?ÝÿuÊíBã×ñd«dmpËCþ¯ ˜­GDwàÝ«Dnƒ8ý»‚—tž(g–/²Åÿlh’µ* s‹†G5·`Ðc:=½o UNGõM×H¾TÝ4.NÛž„°'"É“NÒyÅ‚º ôâjË Ø£ƒ?ñ¾] ©ˆy‡ÏÎz‚ËfI8\lŒu«ÊPÂw6ÍwˆýÑ;°.c.æ.›…U!9U¼–”¹–b¶í"èz8›œ;¾ ¼ÖL8eÓ¤Þ5äOðßM‡ãçÖ’s7ìˆMCÙ<ÉñV8…v'•ɳ½÷ܘ÷Ÿÿ3- ÌaKFgÑâó³ê ~G27]ìé·²8ì6¶¥ÍZ£Üƒr™ØÄ½Äòáaê;!K¹UŸ•‹ðõÙ8ìZMºàëÍòúÏb"ñáóŸ^Û//†]Ú#Š¥|ìùü4ïëNüáSL³%Bq®K}}:‹€Š ÀD¼؆ÞKúƒçJq´ñ`o;4ö uù WSeIŸ^!þ|·ö«éLk|¤ÆnmŽFƒ¹ÑÈÅ/Ë)’ßß¼¸†ÓYkei*•yD2—¡K#,‘;ÆŽ¼sÄ3¾7a¶Ã=XíùŠÍ , +¼ôˆõ¨*\굤»BÙàs‘Dd— ú‹ ¦X²àhš¡©‹ GzÔ’ß(‘ûAؾWæø®%™ Ó<¡ç•}8ÂGÒZúQx´oé51³%&û]Zçæ B¾¼kƇ¯Ö m1§ÛË+°¡¥Š¹û.c—<ê+În”û€<«Ñÿí˶ ‰¾ t ‚ƈåð’¿y'Žs coàtb_Œ@gÞòªf%½ù{ Óû֤ƊQƒ/ÜKÆå*ygÁC_<^ C¹7aé" Êo` GÖÛÀ §R¦{x‚åïOo?&^úÉ®ì‘&IßBØÙâuuî–|ìvæ.§øÔCÖûßh]žÁÈL£n Ñ3òî‚™ádHQ½9{éšk¯áö:OˆÙ)Ë~;y CT%aÅ“w84= SÖÒ_~u̧©£Œ& waÚº\‘Y´,~-™MÏÞïcÏóÆpæ_!¥ÓùéÙ×ߨ“1f̰™ ±]Š–Â?Ùº¿·9emqøifùÈÂéz$ê(QÝîNŒÎ%°ïßkcm…I’À³Cé¯FC̾ùOÙdaûÈFêä¦ÅÏá•`Ü¿ã DîJdþ*Årï­‰‡¬YùxÔöÄd øi’ÎñœíÄø}Q&n²lïu˜ŒÍœv&ƒ1š4HÆœir~`¥±B–ðx³yã¨òç4 |¸J[BªP—#‰¾*^Ìßùr–!^s9Ž7oŽ[ÉÝŸO¸»fÀÅàC^vŸx(»òþbúVÿ +êÔ œu^\óæë̽3Ló)è‘` é¯ù‰ã»9dç1Z¼'‰sÞZN(}†bÙJV'е{˜"¢B„ÂVá@æ?VÐĺÎ]t£ùìÅ{©^Ÿ,•M+g· ñÐù¢MxcÍš[f™wk}õZ¡“/@Ù Oõ9[á/ìÈM`¥º²ç볡ñã ³gyÕ°vîÜ|ª±¡p+µÙÆÉ]Nþô]Åk‘rTì÷v®áùu´>q6¬¼`L¾¯\†É©%xÀÄ—Ü8é‹¥ŽŠ°fY.–t ÞÎKàüÚ›àøÃ|¶ÿ5[Ð; ßáðÎvÝÇMxù”)Ô%érA=m)“Q¢Ô£;·ðC¿‰ë?\Á”n¦qºÜÿžùÛñã§IäÓÉ\|á¥[Š1«q>]‚)²þèªÜ—³†Çé÷ÑFÓ{f~xŽOäT!S2åûZ0N÷8¬mÀþ]4cñ{¬O<Ïæ\CáÃýpàÊ,Ì,` £âÈoR¬ìOŽ_—$wðÕ£¹¤}< ¶Û²q[çîŸ àùô$¬jÍ‚×W™ÇÅWáß±Mè'u L¥²³ÈáC]ljÎi†oîuä}U‰îK“ɺhypð°:k0VÄx“¿\9ìŒäûžR4uø´Ï›Jü®ÖpßY?Acdv˜Ï ä´ýÁÂõÚêjÒx­2(ʆ±l º^r7ûÃGŒÝb¿dÒeÈëÑ©4ø3°r3 3è³éÁþ³챺 ÜkÌö²cøÕ™‹ðú䲌V²KÇÞÇcZëî (Dò>2òÝ Ð§²”Í-á%›Êˆd†!50ÙL¿žbffŸcËàÇt?ˆ‹ÙϱgʆÌÊ!ßNi«3Èž0· »|ó(ÓzïŸB$~ˆ‚ê¾yä.S…·åcßü¬`ã}ܪ,JŽ$dBXº¹z9V·¬'_NnA¹ï ó´†núN \gdµÖÿ¼¢ÅjdQ'ÑšŸÁ£Bú×i5sŽ‚ù Ê´àñÈkôcGæ“Í—½Ðk,‚º“VL>ôœwÚPÅlJ³uèekªü4’ÕRµ¤Ð!JŸ¥mÁáBØáÝŒ>ó'˜÷›ÇÀÞ`õiLÅ%'°Ûd*}º¦2c2€¹IßU…qìº/Øœ$š}ø‰>{oCß“¿à¯ L¿U&¹B´nå:¸7Éo…×Kr—˜m'j¯Ãyæ6ö$Êc¸n ¬ûy{ðWìÝ䃶‚" ‘OH´L®9iÏçüãCª°E:ò¯„'kpéÃxh¿™Ž³ ’Á¨$…—~®¿ÉŒÄ°À7ô—¸ÿfrÛ̾4‚ám¾P,LLk#ÈÒž¨a~v¼—$œS¨¶x c² ඈ*SÔog÷—1•<°å©»ÍgÙð=ï‚À ~ÒÆN£¯] ¹“ý²^åg ¡ ›™ñ­ |kžâž(}(r g&óä´Ì:»u}qo/$ŽÓéŒÙcb+†{8ó7³›Gq á.Œ(Ñ}Ï(»1ë*³Ï*\»>ÂðŠyd¨a-wŸa x$ŒcPr3³9ð4ˆÖÚPe¦™×Šlì'GJK±M.Šö(úOs úößk]Ål€tå2yMZÃ^†ÇÛ6P"Õ–rdçy eÚ†4Èh/eEçážn7˜ùkÝ{/¢Òø¨‰ÁSüÇ)ë~¸¶«Â}E ¹#–ñÙø¾È‚9ÞöÜß&š¤eª3µ ÝKüs,1d|1öôcî:S¨ÑÀ½þ=¬ÍŠ8¶|.#\¢AŽ7Ô³†î¢øPùÝ!†c³ë@í{n¸ù檬£…©dEg.0°y{´¢ƒ×…¶pÕrÓò£-ñÝŒ‚Âjpî>'àJ‡$é"ýYp$q9„DwgÒñË 4_m½{ ö®¯5hùð®»{¦ð·ÑèÚ%°{åabr1•yÎD¿¤"X1§Ÿ‰Q#=/Š1¼n12å|Ö– È“«Îp€_Lœ]H΢äòÃlˆ ËÀ”!ª‹Ï\Db¬‚é?û­p¬Ø”ª~™†¡ßÒQÑ2ŸœWnÁ`ÏôËÏÃDzh ÊÄžtÈÛOxÍÉ—ºn¸qdG^4,‰¢=kCÛ»w+ˆ4Ÿ°t@y·£Ð|Už„ž*ÆçÇÃáî«£ô˶3ÐÕ´™¾ùÄKGZÉ‹Þô›|*ð¥æ³ »¹k¤òHgÍ>ry}•ª®$_Eî²úëg“<Šô¡õq<ÆKúß]&zŸ¾Þv ›äR=‚˜®¦™Ç30zÔŒÌ-[M&‹{Úac³1ZLú߀1 W=âèSÃÄÙ¿Æs OHBç;t¬<]/õ.^˜B W£û/ãø‚XzÆ:™;ÊÖ+1×öóS2„Ùm Xã«Í(u+•ôYe’ÆÂH¢9ØÂœL]ɬ4-¦çtï1Uäó¯/²…3¡ñ›ü˜Ÿ„W<ƶ´ $ûq5g×@(ó·/<ý p¢ëîø!9‘ËCQ¥x+"$àŽ"ÖtÞwijQÛƒw‡…ÝR˜É †czÿ-ãýc:ëzì¸7ïá6PßT Ñ´gÐÚ­ù(oÓ6jûa*ÔÁÉâäÊ™hø=Ô¥æVøQ1SÇ|Ó¼ƒþL@F€ ùëfL;²%7<’ Šœ„h™ú¯o* ^?“p¢«`ùçºÅ幘.nyíNÔ‹a{#¯û &ý±ŒOfŸWJç&Ç÷ .*SxrLŽÕ³{€iÞ3¶‘ãÌ¥È<mð¶}šCï¼Y†ëöÁ|û`òÞ`-Ó/)ñïÑ`^zèÝCõ:-R¶b>•™¦L#üì‰ë7%rêŸ8Õ.ûeǰjGmE1«µ¸Ù¡UØ¿0!… ’øóBäÞq’²ûìLÇ3§bìž dþØ@H®ñjÄÚ·<äÿ zçü,Jn„a‡Duí¸Æº áR¼áM]Âzq}0‹SmVÒØÛq4.•þ7ÇKcjF’¾JgŸ˜/'“÷8{nÇ>ÔçS£·?µ3<mÐ4¼“Œ Íë~aƒQûÇ,}ŽÎß8HcÚÀrN>¦þda¼ÈÙøï˜a²N¼8!³ZÙ±ß 7÷»åb áÇE‹j˜¿^Íxz“$Iý®Ëu GžÊÝÂyc‹ w‰ Ô$|dByÛYÕaÚqÜíO¼@®6>îs$™Î:dwñ{ô3 %—S߀ûLW8(Viï9ƒÊ9!³œ8*³KØÑ¥Œé², ºi¬å‡êb4‰bÜÌ¿5ÔÜ ó<8ÿ-ºánÚ)Kت-8¤™ŒÒˆ?þDmÄìî l9ÆÞrøÄȸŠQEYÈ|ΘK_¡òÅ*´£Gc’Ï ìù—T¿0–¦*Eªº™f|ÙA%dËÙUüTJ.äÇWeO˜-D–¯5"I˜úV7ºkÈ› ‰Ocì|Þû3‡uá‹ 3‡ePvGt "Ù;¨H¹ö”?(Æê’›£Oèù¿ðêmúb"™£žANÝDY6×F«îaþNûÌû+@ ìÕÍž NS kCn!)`.ªEâØÛÈ«¶ƒDyo$¼÷ližàR²¿ß‡s[À­ûëÖõ”½hWù+þ1ŸzjÑs(Xâ™u2ë·cÕ­M‚ó3Àÿ¸(Ùå¬I3áYü`¸îú Rß"@Ö4ž%6QÏ`à¹&ý))C®ûŒù÷⤣èÖÖ°²šA•—ÔÀ Bšx`7¾Ê*§¢ôGØtúù‚µšµ€ò]Ú@ìR°´{:áQ-cËJÖMrß|R‘òeîe<ˆ¦ÝJr7IóÓÚ®zf`&BiÚÁª´Ï§Rf–è¸ã ýâ^†»,8u”AÆdκî@._þ/é¿aŠe鑱D?Užü˜y³îý+ÒžB/Mî7]³iÌýCm`>Âp§I@€ï#¶Pvˆ‰Öú {%šÑ&ã+¾¾lGg4)ÓoÅN¤­[|Ž…£ñ†¸º-™qp„ÛÍÛÍt­N"k+äh¥ù$o𣋦’#[¿³ƒïÖ’¿ËCÉXn#Ç\î9£«MÀ±º6k Ó óŸ à2\l“²„lº„,&J¥P«~vü w/gCÔqG2uk"íH•c¿ŒÖ0žÞ‰ôžñ?øÔwŸ7 ª±;Á°d'1|æÂmÍÞ…z¹øÊîº;€9¶¬NLðŸþÀæ«Är¤žuÈ“¢z-h Í…?ZpÁv‚šÃ@µ9±œ…æåhÚìÞq>xª´ˆ•ÿ¢<ïnàͤö(9ùO˜¤ßÒ#ù|ÓpV€2 ¯úaÏéÙ¦Vl,à¢o­!gÞ§/8*ëAü¼g·,ܱè'ó·ç.îôü‚³t@éwô·„<ј6}™,Ä:pÝÒBüÖjJDz‚¨Ü*V€—rgЭ‘WqÓ$'*œAnÄ7 Û”ëÄÖ{.¹)/G›¼#Ð=,’yÆñ"Ãlà[s`òý; í‘|u#ìªÄO‹¿1Ê^ÄÅOþó•a~3ŽÈ|žNxžÂ¶°\ÄôŠO¶ÿp€H\Y –•ðÑ\dBŠXÔ)Çõ ¼´¦3’õ–Èp|²@‹MéýûjÖák•ö^¼4Ñ(ïÆ‡á÷8ŸZ‹ašØeföˆ"ÙçhÕÀ\Çóצ‘½œºÂ˜&>#¯_nbÕ*­˜ úŽÏÚΤúÓBî#6ˆPß7G阞"™j  wæfÁÅÓ‹È@é\Ú|ÒøEã S6ø,Å>ÙøZ.‰]êÌGOÀOf–Kxq.¡‰ê}è¤cKMm…ˆy€9åèÔ3%4Ȳ#1>kîüåe¿™R{%~úeÅO¸$‘gLù¹†9V´÷ÜI&ËH—,—; 2úWÙ¦M®´ç¸;ôh:^1Ì£;|ðõ…àûÁÿr}ÇŸÍðŒÅfLà‹$´tá¡’+Ž[ÙʾDµcÐd<ƒÉmyaåÿL‘TÞCóÑ ç} §^g*ám;ë%Å1´J¦´§KÞ¤ÐH—f\kÔ×ÄOÀÚ¨x¡Åfv?.øp&•`ÃÉ£}òÜ?qg؈¼o ç께´? .Ñ‘eÜ|wã; /Úe«D¦9ÈQ<Ô #ùq°àù&zäß<à© %)C¢´Gü§Dcд€ys-°ÔLù:ïÀ©eJôáÖk̳ÝwàúÙ*¶ëw0d‰K€¤ËK¼8‰y•é„x m+AõJQúBVý“Ÿ³!=S©Þ`,}uw\y$ÅÄ¿ÞEn5…Ûš˜×¶ÒDÓT˸,âuð‰îY‹{c{ð÷µçP^¶òÚåÒásˆµ¹Œ¾ª*ì±ÚÑûàPúŸÅŒ¹¯±Ì[^OÞ†¥«ÍÙèÀ,+&ÀWÍŽ†„‚V,y¿…æ1ϱ›Ï1J^ ]‡“°kÃ8¸ŠøµP{™K˜•™‰Î<ª;®ŽÀk'ê™1qRÝü+8D½· > &Ð=—$V‹‘M`ß?“½MñúUeúøˆ3)*aO6Ï¥‘šAÌã`Ê>ø}V*”ì9AÒ^XówœùWŽ4—÷EÊ>ßaïæÙƒÝZSè3yrNðÖX€(¿ï…kJ4®ãø}ª€³™¿ñô6o^L%{U²¸Óžñà¢y mŸØÎ>üðý"HË“aÌÇEWei®ê[P½4…¤ïù6úExzÝðÖ«­K~3„ÞFÑà\[†1gèßÔa,TˆBûWp‰·'öÝšO¦®’‚g¶0uñKÿ“h¾:•ù™¿Œü<ãN<> CUá;x9¹–K|ÃÀç\ÑÏÌ’ƒ³ÛÊQ÷½$ëž® êõ›Ð§“Ž‹h£œ¢ ÙTÑ΀åËÛ±vÕg¬3‰ds‚õ¡å);­w5ªýIb}¢óñÖž•ôÄ@9{AÂd²î9bË‘6œ%:…¹#)GGrñäÆ0Üö@ªùîÅž!¬…u;h?›€ê€É\Ôø¸¯aL‹eHŸ¾4Þ{Á i 5+·Ý2F9µjxxq£¿…áœÐ ³û–YmLú:•aðÏùü𗇙‘ß ç_Š]ÉÖ&-ÄFu;ªè¸ w-ã ÚðBdW¶}TdNz—£øû—ø÷–)xt(qï"ìWžOƒ2>KÙ}—†X‰™ÓÈÑ„lNó¼{¨ã+DþüçÁËÌ*8Øð7÷­ÓüGçõ÷þq›ÈÙ[f)Y÷û:ECJFFÑ.M# 2²÷–‘"¢¤BKÜï딊©¤*ª–Qäçûûã~¼ß÷¹ïsÞ×Y×õ|=×㼉¨.îNÀÈv?:¢´ï+õÀF==ºN¢Þ×wÃÊ7Z4ïíbÆÐI—&ÞÑ`“,,QfÃ#²qtƒÄ}ƒòØw¸X§ŽÊmf'Ü3sfTÂïUã¸`à8Z5ÉÐØ8>¢ú}¶«¨ƒ¡¡3ÝCØV‰×#êðF›eƒñʰ79ú‡—~]¨OB¿)’ú³™}ê ïó‡]¸Ž:I8«{@–ÄV¬d„ý{'óöŸá ­ n mP²$™íefA„¾Ñ™L£¡ËİīG^öCïª\útm$k±€ü4¹Ã.ýƒJÝÏ0%0VîÊ@¦#‡Óu)ÞXNu.{±ý~ªt¡^&cÙz¤ëö2‹r%@Y)ŸJÜ Å{Za\÷™ð¾ÅX{kQϹ_pƒô¾­‡h¿*0Ý|v¨ÇÕ<Œà3¥¾>RÔ^v†”¨3­‘«X䟕—ÂXãd™¡*öŠ DË#‡±>]#°Ÿµ…ÀÛ­8PiBºZâà|ÍâuªËïçLqßm¸¼%›ùöÝ_<8‰;ÚVÁÛ­†ÄmÑ򠏱 w-‘Ü ™ýUÌ'_ºä6?,’¯d?òPfÍ ´8â†É§¤Éª¿w±ôY$sØk +pHZ¯#‡^iI[®ÌMS’ü¥w·×àÖ” n³¥ I²óFÓÆLß%Ê=có–Ñ Ûf㛀ipd"ÁÏ’‡“`ÇˬæýŒ*…¹åš© ×÷ôâ‘5K‹Ù=ÜIöÛÇIvÑÍ%ìyý;cQĬVÿ ÛÆ6§#<ŒMÈ€î´_­á_{Æx'.g÷>¿‚ãÝoX™ƒ›{£…U×Ô$.÷ÑrYÞ½¬ËlM󄤄J|=F™ÂŸ‘8qEŸ\Ô¨fþîó`ïë$0O¹–ô’ôWf„²65Ât•õað_s5y‘ŒMQw°-ê4”¿ù‰CòP"cz®Ê(/‰çò´$³K®É½O7qp–IkF¾EZ¯cÍÌ2+cŽLOÀ7Ù>´ÿW(­Õ~ÞÀP¹ñäçòYÆmõ.vÓþS>ª&tٵƅ¨³ºìD›jkæÓ{Ükuw˜l%?"<¸Ä½ö÷4€ä%="v~nÛØ°¿J_â±2>²Òb+´oåävg‹ µ¨;}”»´Fƒ†¹ùbqg$×€O.@‡Uk;9’Åø•«BÂ/q¬¦E⯷§˜ö/<œþ öË›µ­_˜sß_³÷»éÚ•Уå %u)¨]aAæíС+’ÇqíÕH<Ú —4Üžn&§à'tm¦ltÊ;L¨†Û®ûØÌvp] ãc"Ä`+«s^ŠÃÝõ åEˆì_Cì›g‚Bó¸ \øÉ“ Òü9‡•Qw%Èhã$±öD«¾‡ó{© Ì*}>Ñ÷Pd]2¬,áøíR œ/?~áûémðZªçØsßÞ‘:ödüɼ=÷/,ˆåŒ6ôz7Ì­­)Íf×φ™Ÿ œäng'½ÓákŒ%Ý›ç —J’ b¸ÂFižãt…:{$w?¦÷rÖÌ¢¢Bd[Ÿ $ÕÎæÆ*°çÛR`Ðc:õ˜[ÈYø9Ù«Xs=Z@ö•& µfû*_`îüüFŽùÈ|]|pAã&¦üI…Û3Nƒ.çÆÏУ ·[³Dž®?,Éx½‹?o¥PY“ –é;RJpzÛM¦sm *khà?Xÿ­ƒ 2¼G˜¼ôU†zHŠ“ü!{ÆàÐEàÊϦÂy×ñߌI“ ç}…–Ìpf“É~˜õ-–“Û‚Ëh=œÑØÆl[sœ P‰ÂÙÕ3¨ë‘}ˆB?àyж;?°³O=ÁÃgf²íëïA Ü„ñæAt=AdΧÍó ð‹{­¹"Oû²è{Dž:»iYó:ÁaVþ5¤ÍJÞ8§³ [EŠñó˜9þdhè_ÀkKÌhv˦ŸR§‚›þ¢ÚÓÙîÉä¨}7È››láà†*,¸‚Çn+à*íØÍû L­’™iQ¼¤~Žvǘà˜óyø¥JLì»Ø·GgñÇ ðad3ï¼E%½]ôo™- á!£Wĉ¯øЖVAºEé/Gú=¸âúN `g§õ‚R\sÛ&ˬ¹z†4|}ƒ•ݹ;ãjJ!aØæÌü+>>G£_’ó3:ñ‰[çQ߫˙gç=ñPè¸ðsœã¢â >‹7aUÃ60ßV„r’ï¡ZØ[Í‹ñÛ51|sX·|<‚²|·°Ä)“™?÷ümÜ=øðêÊiÄòë$¤í>s×Ý‚¥‚¤çÓmªÀ`í°½N‘NÕu~Á¨wf ?ûçá&‹4ŒÙ"„µç²pîÖÌÆï’µì t?,NãøB°íÔrfµËk à° Ý"t‰”{UÂß&cëg\Ï;¸9ü4Ã>ÍDÕOY¨¥2¥šqô®+¨sŸãËØ¥‘’ Ÿ\´Ï@•Ð æŒ*e'eÁw5gú»ÿȰ:T¤Ì×þàŠn'»[H‰>n}t”Š #qè„Vb¸{0ögL=Ï2”¢aýÍ+£¨†U‚húž÷<ÊŽa—NÂæ¬Rxªƒ÷8'€gLLÄåié³rÆ™+Iêþ+R@?íLßÖô´áQZúj+<º‡~"ú/áÅÚZ8Þ¸´ÝHa†×äã*·¸:#›ˆ³äXµþÆyqÇðŽÔoFÆ¡¢.â¾oÔ„÷ÓRpòë;,ù“Fîo6!´y í_’ —ªäªl[ÈÅì·˜A?92)%„§Æ·£»`yš9zP@›[oð’jwŸŠ&ÎŽ™DÃéÿÁ¥·êôM-ZÍÙKÊ ¨–ét¢°zŠÿCáuX $”­dÞ9IÏ~çüõP&×vòPrò2[¿ê5I˜j¿kr-˜§kÃô6F,ú#‘5?Ž>?oS!°x†ÔEÊšž#LMßb ÉÇ]a2²|3Ã\Ó†â¬r|%“ëæÏˆ…UQŸ9óz­Á¶W¶yÏ–žýˆ¡s ›,ÒO$ÈðòOì]¡ŸËzéŽÏeØ÷Ä 9K.R›;rWh3ϸ Â{;°Z<óLΓ6Å.ü˜šK~–ésïÁtâñ|ÇkU ÉTVcÖ¤…Óä‚E°}¼¼‡ŽãÕCjäXs0ØôJåshù»ê蘆üU¹$E$‡,⻇Ú&ôÃlN)×;ö?=dI¾)êÐÀJôah$þë•v¿ìg.KVÁ%Í)ÆhÛL·×¿aùÜÆáoð/$K×S+K :ÎbêÎñzOôïJ^ü}G›h ¤ã“âNÈŽÊfDšQľ£5“*@b‰¹¾¨L‚Õ‰fà S_¶™!±l[ìB߬Â}ïZAbÖ*:óB&vÇÂÁàÇ÷ŸÖ±æ' Z=ãlR™Þè·ðŽž…¬Á1|$ýêÇÄðÆ> h޲û³?ñjF3½€'pPâ#œžŸŽ,·‹«¡p»XVæ±²ÓÞïgG>4ýËÛ±¯N”—b‹UÌ_Bó¬}©MHj½+ƒ Ÿ·ÀK[x¢»’¤ÖXá…eã̹%ðü’‰Má(³áM‹‡ªÒ™8ßW‚®´¥÷ô$ˆÙŠð±Î ÌÎN£]K•é¾§[AßFœŠVó“7Ò£¬_Osf™, LßB÷уlxûÌM+&Òüàž_‹àùÖùS¾Àœë, Â×g¢øA5Œ´¨Àµ+ï@{à§gw:îôëb_aLŒHzÊ„¶—€ÛûÕÌŒùÉœS*ù(±ÕVÞa~ÝDÎò-£œ}ó£°_ËŸåÌ ƒ‡É<ôvc& ›Aˆ›x459HÞ% £A£8ý[ÂH0$ïŽìÂgµ‹°Ó]–jÆxƒçý{l[‰ éÞWÀjN>eTóÕÑz‰=äÚ)ÓÁ 4zÙø%sJ</·‡ÑCUÓɲ ©kD%.,2‚ƒÇ¯Â¼˜yÔpvîz¹€~:#Ê^szy÷¸ ?'‘4Vf9m*ÄbÃO0õ6¦m%íðÊD™N·‘¥§ƒb…¬<[½~Êïù½Åó§_@Â*ì¿ös·ßežø¯`L£VÒÿ|wåÿcìçovQ¸¨¤Bô÷² >=€ ­’†™ ¸è#ƒ6ñjôUíaæOwa‘鿯KÕÌÍXýN-L^i s_‚Ô'-šç-„ ÿ<‡Ü 0¢yí ŒKÓ3û¥©—‘S­tŒ.S  \bßÒzY¦áç ³µB»¹¢;,¨U×lü1ÌÝ}ÍŠŽt\Ç࿈ƼRÔuË|â+°>=áÂÎô„å'´ºû^«ôÁòô\6·O€Ä¾EvDz/´óÈŸš©˜Â‰—WÇ1iÝB<³G†¸–\EæÍ Øô«Q/ËÕèEóÞª“¸ê®q¡Ó”èÜŸ&T8iœ;|Æ‚@+Q6w¾e¬³ÞƒÜ}D³` Ÿ¦îúµ0È£35DKäŽÅ¬†Ÿ×k`ã¢$¼ÝšLÚ"wÐ/àÅšº+€Žö9È6å•–ÌFŸw ©·Þ®&¹>j§äÇå&·Ã:5$é7»»o–nc_­¯ÁN¼´y'~™CšŒ¶RÝ6Tú\Þ?dâÆlèÐÝï…ÝZh·˜šž _î¸3WþÀeomNÞ£ vXÝžj¼â§|'l¨Ê§[Ü¥g·"od<¶u¸ÄʳÃGJéƒ §ám»7~,˜ÏŠ~aÆïÓÛûéðŽ!œ6 qÇFCe 𥚑™G³±nBˆ8 H‘ÁrDˆïMJ?¹1Œ5W¦z5šøæÚ÷j˜ßó´®Ã5dz8Þ„ãb¦èqê²Ý“áWì‚´}äa1(®#û^îd¬ú:ÙMRrd©S)l¬¥çígaC#»tùêã,H¦ïƒçY÷PõO%H¹ƒõ׎ÓGA pé*—Ý]¤ Óï¨2tM–'DKê³ÃFeÈ/åØ{}‚-Í{ðíý¹ôô£“ŽøB‹üË›‡¡’är,ÐÌÒ\S¹š¶ÏŠ‚¦k÷Ù@Ý™ÐÞVL­F'¹£Ù;àÏ¡0”=F-¦øÝp9ñ<ò—±ô$ŠñÐRv’6 ^¥ùÎqŸ˜«ÁQú.LŽÎWߎQWðs gÊ?‰’Ÿ}éÐÛŽEÃÑÜ7—Œ¦®‚lx0®[ý›¥Ó9e-˜ÆSAgôEÄ#æz¾$uÙsìËæ’M75ÙpË8&òÐ+8xwîkðë-ÅÝ5 ‘¬@¾˜Å³*ÂIൣˆ-¦.dµ¢›P¼Xá³’JÆ< Š4žÄÿžHÂý™¹Ð¥áFæ¨-,Ij´iHˆ'KxËäÌÙ'®ÃdÞ8¬E:gÈâÇÆF…W‡¹pº -7o%¥r²3LŒü– Â;k±æâ0ä] Áןáãɘ%^GŒBfã°n3ȬÁïAÃLo,/u(ÝźßòNndú¢}䘧ùSåL}Z6TÜÍÇsN«Á½w#ª9˜ÒO¯NAßÉqìWZF‹«'@M-ž‘9û­ï ZÝ:@ÎøcÿÍ ìãHÚÞ—OÅQŠWïC€É&|Uk<…*`¬Ê]ŸA?ÝÊø©`ÎYÜ0}³ŽÖâӈ¼P*ö«”¿ž`×h¥€eèYèù| VÄÐ ~«éýàSpb\ógÞ‡„•/˜=Yìcœ`_–i³q+„°e§:*Ý,ǘi!°£ú'Òë: ®º­×Çô1¨¬éaþ îgV­T`ß?=„ñóÎ3váÄiÄÁÏÈG°‰·ìlË(^Ëby~xP&¥Ó¿l3¯9y*¿ÙíÑŠÔþž} i‹c¼‡aÀgùÔ© Oý¾áüÀrh1‹€OûÎã?]~ Ôÿ_Ÿpåì~Ɔ+Ìþ7R«óó@jq k>º”x0›È•¬Edð¿½t‹]3üúaG’ÌÖÒÉcvh4Åz_½ÀŽ'ñ“^/ÊU‘t µ#`V©5-Éâ#‚›¦Ê'ì¢-.ªƒÓÈÌw1L¼™þ|(E”.Ð?¯žtàÈš"ºÌl6ñù“tÕ°¢kŒÌ}Eèà©Aô^øŸW¾Eî+º”XÒÿ"vÆÃ‡*^m ›eXGMb fÈ>m°%•™:Çœ8ü7ü/‰Uå<*{û%\“·æn¼ú4+Í ù!:÷e Q>CNx¬"±í†dCö*¢É)¥wV(P_í]HªÖ ó) ô× š˜æƒ{ôdˆ€÷}’?ž ë7ÓŠoí䈵m(|†–%e0Òu‘} "D-çN§ø šÌwx~߇²6kAEMò2âaz$0gÆðEûn¶oa-”ñšaŸÇkÜ©wÚ’ÙÖiZLvo³:D‘ž)øÁ˜µ¬‡óoÒpD9*6JP·¥¤oD­gïÀ…ÖiÔÛ%“Ý|0.[ Î7²¹Ë˜+yhûÚRP °aÎMAæÔëç5>_…3‚XPB¾}QË4`V V²%¨K#GÌèéZ]z {#NÎp$—žÿÆ=‘SûüÎF óÓ`VO6^Ù iŠÞо ëŽGÂæÁ'Hkgs«‹äIçn7ˆÙ6 D½“„¦@Q8ïÄ!¥2®êö ’Ëœi΂l²,õ1«eȾxø†³á"1!òÙnÞå-Áã5úLî•ÎOñ\îM©Q81°ƒš:ú‘½ á¿©µ­¿ý'´7€ë×™Ô+¯âóaösq:9ÿµ( º“GçaöšFýt,µ[|ÞNaÊתþä§É{ÁJÏ›2Õôº?̯ïú¸F>2«Ôˆ¥Ûtò}»9¸c€Í”7"v›Nà”üG$I¶óv³n?sWl‚-ÉŠ$gö¿Á©+§4‡Ñ^z·®ÝJͦåÍÂh槸<ùØ{Z.trV¯T'éQÍÌ†ÃØ÷v1>)å'.;B…¥2xÈ…T ú%fׯq†“N6ø• 9°á³xҘ׮Ev[ÊaïÍðïù+t.>î£äý»¹ì2AªÀn a0£´ä=®vx‡bö‘ðja|Óx þ‡AZÅ­ñ›ÆZRÞö‘©,´ÇéÍ0ºR„jÜãâå—Ñ´A—ÞÎ$µ…_០yóšÙlCß{ó±ïwJËßç0ÁsN\ßÎò’EJD}m1K’Fáu‹3‰ZÝ[þ+c‚[Ï‘úø]¸kü1ûââ©ÁrŽÌ^MtWR3¯|R&·Í²×¢À“°„ó8Ên-ç¡[‰‘ÛJr*[›JŸ…»^¥¡¨öC6-¸{4øh~&õX¬@f Ï¢þ‹4i„ÿ4FëÁzõËwå}…¡«ó˜ç{ )‘¨ÂÀW–P½ÌŸ&øÓ‡œCñ¼DäÁ2ÞøIÒ”|“ ¡÷ó åúM f$Q±Û¥´uu‰–'¦×Èë7²d²8‰){™ ÓÓ#¨LñÚ¾~‰aí’8ÒÄO儃›‘.à¼Å-.L, iE é¥-²tAcvû$1cãûQùº:u”`Íf§CÕä/ÐjôDvq6®ê¿ ÖiÃ*÷utpjÍþ zŠá¶ Ì‚qY*òoÓ=VÉÆc ³žÆÌuÁ°wï`þV2²ÜÃMPª¶È©µh{M“Ð×Î(÷3š}Áøþ(“ë`ß©jù1õYTzêÙGVŠlÌò=X¿Ç§Êq§­ ù=&K^þÝŽmŒH|þ…6úªÿ³·ÑhR‹fFñ‚“ÙuGŠ$<¹ƒ¿xõ Z¦_&–ñú”ûKåÆäÈ;›éœ¹¿çÒo¸£?‹Éýj…-G°ô 5?À|pWD­ª¢ìn7æ‚e×L’_úÿ—ƒ¼ŠÇžÌ›x оX¤Ò§=ñœWV‘¤páWcÅ ï*GzÍÍœJƒ'ä8â¶²µâv¸s"p­,QÝD&X¼6Kp*ô»P§`iir¦ïžê¿Í²¼vÝÉ{Üù*”Üz ˜ÂnOº–³i÷7 e޶§­]ìî½clÔË>†{ò ÄÞû„q_¤¨çN ŸÕæÒÉÛAz|¹¿d„í^´±ƒªíLœÎ0Cu hºÂzL¢>(ÕbW¯i€gf’ƒo”° rÁOA€Þå=Hdä©Õ:Óë.î5—b”ŸE Oˆ*©ZÆ¢cû,<|Í€¦³¦ŸŒáŒÈÇxüæ|*ò¤î>ß‹ö«?ÃJ}=Pxýë:بŒåÜÍîÔÌgù¤æDO,bœŒÁ§~)„òeE”èÞ+WðùÝ_ðÏ` bÉ(»ãŠ5Ssçí¨½"•ξök98*õ;ÜK¿‚J’¤ËòM9bê iýÁËý^„»ùÝçÂy7g×È<ð0ØmÞ?¹¢9¯ÑjqH‹Uc·ßèÌ¥Úʤž÷=3Ç©ù_6s²fã¼{)¹”ãLíÇ‚‰^ë"8'¨«'Bð{͸`Á¦Ý‡Æç¡`zñ&èmœF;È+=…ü‹ðïÝQüïúCtIއ§m0¹Þ 5j$ çèF22ß~‰jÓ»c'i©a.³æ—þ°ÐâžK½×úÀ›/,žýžÍÜÊdÒŒ«Ý1Òpô%‘ó\†¤Ln¯uGɆõR0›Îb&”˜£³—GEÒ1Ù›? âúLÜé-ŒN»JØ€Y¥4B­ŠV~Q'š˜É­(œ{z/ q—Üö¥Iók µ¿…½˜¼¯™Øa›ÿ3øÖ'Cnÿ«ÃwÆI¸‚'NwÝ‹æ9L̘6ýýy¬þlJDŸîãnNˆf GŸAV…&ýžŠƒ×Éüʳ(ßÝí»±Ö „Q’!ÜéìCñVì²p&äˆ+3Ÿï*”®z—u ë¾,}ÙòºNÎ" 6ëÉ9Fƒ \·î  (šÇš‡qè,h?ïeS“‘£ÿ{×Ï]'˜.¿¶.y„’V‚Œ+Z ™ÞÏ…¸¦A€rºyÉ™KæÜa*¹@æü»Ç Ž6¡k¯U_gÈržà‰-xÍLžÅÿ‘£µÔIL™ÞÚüryÈùÁ}”§Z”,¶ä#·šËIJ~9¨Hõàü«–¤SDœþ8*Šºq_™ïnixPqHÆŸÓ4w%]”;&.6CQ3ÒjW ŒÇ˜ß8Ÿµž¨ß3éí{b +eÿ¢¾Î ’w©“ QÛIŸ0Cj·Ð7G½öª3gžWCÓÜVŒy7óväÉÝ·vº’™›Vàz¥8Ö¡9–žt‡;óGÁ-b=½Ìå£bkÒa¡~7ÔzíZá…“ÛvPß‘é4%â¬l¹Eþ´ª²ÛeïÓ{ÖéÓÅŽ’ÉÞ!¨íhO`˜ô|*f§áŒ‰˜rZ„(—7›Sµ2 Ý1 ·ÊO°u±wž“àµE“6y°q.­_à@@>xºì"°{?ÞS·‡èCU8ÐTƒY¢õðsZ ë{‘EgUQ:éÄ::<…B¥4Üß%€yëš èèœO]×Â_›µ¸  ƒýË“ÌqÙÊ•à` ß4ZÊ\#P?]Ö.ס¦_X¯¥“L ÈBžt£PÚ/oâ51B­lb5›·0£ž¨ºX®¶¤êÁ ðdT¶í˜AŽD˜Ó°yUÌ“¹·q¸û<ä° 59k&íQ×í4N¿´‚†«lÁ¹Ýèbj@rÔðyÔÂ’Aèd9Ù’ð’±Çñã£AVÜO”l®ð%9R=-ÈÊ|ÈHÃC[¡‘¶p¾[áÁÕKp¡ÂÛwÉœdvIТÛg¹ÿâÉ®xâµè!\5= ­rŒÖΘ7Ûʽ¡Š\"¹ÐøW_W`ÝÂÕaŒªÓA¢ôØøG¬Õ\J¥ëµ¥!xªÐ›8Nk¡Æ3ùˆÑ{{˜»ã3ç˜ÀG<ªc®nbÏ5ER÷sÿaå«nÖvÆØž ÓÇoáEÇ ý†`uf Ìù&LD\`²4yéB< •372o}úX·¾¬êÎöõåçÌ; òÏÁΕ²Y<òhëÅȯgñY{V+™—‘8OЊú9ÖâòÒff©±: 7¼<Â<òŽ^ ¦QN‡åÃ"´vÜBÎÔãy•]Nu?·,U: §ãd÷nðNÿ«%¼ô+Ç–|Ñ6¢þ± ñfé5ÆúÆKŒÜJØô–!î Ø F©G˜®¯ŠdmäaÉ¡|t9ÏlŽN²1ê/ÐÌØø8@Ǩ)Y!¡BG°èå\”Ó53@Òãþ1!ÿ}Ó!'âåé­“¸C™8ÎÇÎø[ˬ®ŸŽUØpQ³ñàædövof즃dgÝy<àƒÛ¿¯ÄYŽ­Uì2òÁæ ;C\†+¬J1Mß8ÒG­i¸Ë®7X-„+ž³÷L^sþ;z•]y¬š³ Wy³ÔÑM)ß6&°WgèaAÍ b( jy±p3±Š­+%?âN0+á êwãù» þøs´š¨€åI‡¡jž{vúÖ¶®˜<¯B|ÉL‚ ‹R¶h–Ž Ömbr<–â­­óá|ïV ýˆßÍô°BÍqObíü»4óá”?QF;yC˜»/úmHÃ4¶gf [¯¯ J˜ˆ9CXþ've×ã“»¸¯w-~˜Á]Óh›÷W‰ÿò—}×aMLCâ·¢ß^=¶†_’® þ…c{À]ä ^xÀÃ|nòǧߒÿ—û Åú|\C­˜]‡—ïðJGq:ûYs®þ,XtHRÖÿ i©ªÇÏ’}ÌY3=ÆÝ®Ÿ±YóNa¸é¹ÏÌ©72ˆÌ}¸cÒ ÄG«t:ÛÛ {D’xçòÿ"a¯¨=·cÌÛqsŠ7- Ú»Ók‹ûpå§up'ß òަ_“]È•gÉIqZqð4*_˜MÝÚnqj%~¢÷¸>zÃÃ,tüv_’ …Á·WQø¯ ªü"ða¬”ŠÓ¦.à>X•/Ä#s@âP#®·ÇðR°KS‚Y5ˆ’®Oá৺éO.y§ÐŽc“ÜðÓŸqòí,28­2ÞŸËUXkKèwÃT<-IÔŠ}±¾že³Æþ±+n‰ÑºFôù#ÎF:é¡:ÔÉ5ksÌV¥c.²ôÂ÷÷7НߊރÄe΃Ba‡9W¼Œ¡uþ4ràêZòo¾ÝÁ ÝÁ¼¿Ü”,ç#Lj±Á% ZtG ˆ>Á‡æç3‰ÊØojº™]ǧÃÓ‡‹ðVÏ~Ë×Z#×v+D{äsgç‹’O#ëarÿìTiD±Àz6/UœHÔ{³äÿåK7N<Û÷ ăÎÑó½8èäLb¿{rÝxä`‘4BÓ·(ð3%Èè¥ã Ë¿¶M‡UÖÙ§þq.í#{f ØÕk$Àó›pS0…›ðÉ /|ÜÆòî·§-ó4˜çF» ƾßWP¶Ýõ%È^ŸG6îÆþV¨™›F3Ú¯§ÓË‹ˆñÏ$rråp|Ã!|_ØŸW“Ôì(Fäà {*Þ5/‘$êÇÿØ=)„õ ŸCì¬X0LÚHbœÉÆ(&ñƒ‘zPÃz¾ì…m9Âä z,Ð/kÀ~¶$ÞX4ƒ¸¼çÅââZ¸J¿°ÆÙYŠÇÁ¾ê;Ýý®þ#G5÷­£^ÒF¶ÂïäàÙ÷VÆ<âë % / ¶áƒàƒX,ãÎÜS …±:t§i¬#ò¬GÜÈÌÅ‘“g@°8ÿ™ÙÓ¼ßÙoñõ9O¨¨,ÃŒ4 x¬¼Àˆ|?}goN%ÆEŒÛ¼h`¾¼»Æ JÞ€ä°'øžaÇu7xyá7¾aâ@üãq8{â®]êÉø¯õ!“S1 Ô*bÁLcþ/?yÓµ8:Ï,îÙŒ W3pÛûW øŸ5ii<†£9óèÛÝP#H®š‘ QâõË6>ú‹Û«29xp¯Y«hD^?};ÕÎlÊWÁ€Šº(¼ ×#‡@嘙÷‚NsÀTÑfŒ\VÄ´\L›b7_pÖ…‘Ý&pvÃîµ:Î4ÿFÞڳ虽’®t&åKI_¢-}{ç$i5ÅtšD´³ˆù´¬ï-^Eý½eA¢®þ‚ázùì«Ép6Ë#—l9 Cwx}ãÝ‘Ðn@¤ìôèHã ¬ÿ—ËtñÃóD°q9ÎÃx³bš A+R±9]€¬5¾‚Ý5Èqaþ%X@—/¨{ „ÄÙš¹×»D}:µ¿¢æ)”À˜W0¾L¿‡Æ³Í1èZØ[FÓŽM§™—Ž@=¥n°ï=Ûáéµfó¾è6ú‚BP{ÕfoÈCå>!*®›…ÏljP¤y3¸vÞ>Ã9Øh«ÂL°5—˦÷²v“†ôj'Ó¼ëd4èÎÍ¿X¤L<‘¶‘¬aN‚èÔ9vøEÃ5oNê1YÚÑô‘Y»D½µW•¦+P Í-عk¿¢§^-3‘+I^|P¦ë¹Óèâ¦l4ºrRì°Z9t­¥ÀAnœ=z ŸÉ3 Ú%©å²rˆ¼{ަę'vÝŒÂ2W7*rû-šSÍ$ÆEVÔ'¦dUB£Üôpt±U=È,¥2ÙæT÷¡Ó ɦ1:lþ‡5Å™êWmèXô=öé‚xâ¾z>lègü¦vGâ-UQLŸ;€a ¬öšy”¿ì5?(„[oæ« áðC²NÄÞÅ&Ó äá-iê0KšK¾àá5ÏÑ:ÛM%ÓIé"œÔ6e×v&n¢ýnb2:܆šòI͆­p½¾HVÄbÉ+¶Ø·î&BœŠñ÷-6LÓª{.T§Y“6ù­Cùhÿæs˜õÁ–ÌÀ=—„˜Î+·!°Ø–JZNä+XõgÆ4pf oocM){]džF¶¦±ûÄMð¸Ï1Œð䣯ýNã`ÐW˜‘ú mX òhã ˆfG±æ x æp µUqs`:S$`IZÊÜ »·ÑÚ˜—ÜÙsÙ ŸÕ4­é½"9ò«ßþ•FË醘$C6DÐbmGF¸ÐŒÚª0R÷Ckg5ûûƒ>¾+¦¹ÝíŒÓ®p]ÅGt–7£ßT[nwQ²ÆŠ°ÆÎ0þbñåɳ;wUž”\P‡«ò–P›­H$Ng÷H‹°±Ç¦ÓU'öÓÖƒo°Û¯ŽS8ÏxíÝÍYùw ¼? ñW|ðA¸=‰þt nZÁï.š!Úq;;¸·£‡ñ‹I{Ä'•d²±ºn IWßß®%r¯ÿŒÅ£ñØSeJ÷\—"z[qþ7^–^µƒ3£åì°f³í/]¦Tì/[ƒkò[YÙ•nØ<çÞæu€éå~P÷p§WêÎÝ*ÃŒW˜7à4o3û°Û{¶³/ñc‰}̇{ôQ‡_5æ”ÁZ’^¾Ói¦g=,q•¡]øéé4޹ Ò²iÐ:Ë‚=¬vÝÎÖöˆ'óÓðÝ—áþs9Êÿµ|5O£µ¡»g…°ýwáo«(­<œåçP`y(Èú_»s8´ò3ƒ½¡f¸3½‰-õŸOfG&ÑÆÌì+ÝrüŸmÖSdðºÝ†L‰e¬ >ô©1Kw…Ù÷ZÿÏ>Ô~Ιzî·µá—XúªÃå áÅ¥'h¢™LM5Bôc§éšÒ+) ÝÉ» TúÚzú8?y·áûóä¥Ñ ðüOàŒݤtµN<›6®äȲ$ܻΕÛ” mް½µ2ö«Ű)­½Nó‡Žã-ϧ,Ï´×lÊŹl·Ÿ]á"Bžqq¦u"nŸ=cƒy$‰2¶9$:,‡Ül\ CÎY„;4ê]øÅdÖ ²û>i‘IºðÐqZ¥eÊ,³È@¯¿NH§½„ÄâI;Ãa4Ïü:ÚÛ‚]0qý‘DŽp“Ü¥ËðZfÑïó……ÑßáF&ÙwP½n{š¿ûâ(s•›Ö³_åÙ¡¯ñ&ðÌEÖÃb6GàA*{$Á»±´D© Ï­,f7†ÝfËüg+>³éÁsð®ëLøt²„ñZŸ‹žGƒÀ}Œ‡žŒÞJú_·0ÉRû៙›+R kgXA‡ávÌl|{¬§aut›ËcHbƒçRß9ýè×ñ€Í» |*Bsó2yé^üİ5õÒ³èn7¸Õ+@ýžš±ò©<¸h/…ØsÌy¯“ø`X†‡‡ÉÝÊD±dÔý\C”]ЉGù¨&t[/ŸË’Q\Ò(-˨ê/Sš§z+mÃè'5 ¦“3ññc7¬¹?®é­ ¹B!xÛjÜœxš“ý±s…3°ò¦%å^ˆh—JÍ•2hð™?Ð3/…d¿ÿ®Ï+bNé¶¿2Øègæð¿þäWzQá7Àõ2î‚Q`b¹ýr,‰Š½y2O|–œ$rßáªÏwÜ|]“:xsH÷2Ò<ÎŒþ˜I•»Ù!”Êy“§¶&:8c{{˜JsY˜›MiìzjxÅ"…Íi`§)áù|´l§ÉÇgÐ×ZׂŠý2¡od§‘ùü뉹ŸlüÏd9©à´,UüÀ…. ýÍX7.ãú^‹¤9¡.”yPLwvµ0ÿuà6þû8UZ*çƒúIY]É|9Aï–L`Ò™f˜²µæi«µ6 N>K”½WѾD{˜8ñ]–âCö¥ÖÂö‹GHDÜ<ë·ŠªSÁwQj.7'›[ÈÆçz4ò“/•{°Ÿþ.SÑI9*û¢!á»i~ ©²ö¦Á]3È·½ tUÔg®ïB.¯è‡ˆ[3šëÿù¨úâ.N Kº²Ž7PíuntL‡-fÈHq937]w©g¾ö=„Au¢©0 uÓ÷°O«v‘æ»Ð=ÆOÌ{[Aîº35K×›šçrœyGæÐMôëèØÛ 3uŒÈgA8-|€®2md8["`£‡!.UmÂ9[6Àš·áÄÜå4|z¢y{)F¤JÑ·}b¤Tmž¼ç!¼[s±/8¥ÅšufÜ„^õV0õµ¤ü÷jáìálðei•øT’ì±ÿy„üzæ:xý§Cð¥-us)eSŒÞâÛ¾DŽÎP)“ß:êû6’i7Y§Ø"º“l½hJ^‡äâî™bÈåC6Ôߌ[¶bŠ¥R¸°¢$„„ÎCüÎ{ ×òB®¿]õ—æ>K‡@õOPò ×\ ™quÌ…Š$òWž­QïÄyí;ÌuÚƒŽÿ63®±«hâP(.¹u”˜ ŽNµåEÿ³’ÁgoæbŽ(â½?!qq•01£ú§éV?}ô¾ÉK…>¾ÇåÆlq¡.Z«K#ÎÁÛvåלÃèTCºÜ[”u[õ #ËѺ—ÖÌÖú•hué5»¶vêEÛ’Çn ß ⥑CRÖ ¡m/˜ŸÍUByÜ„¶0ú©-NZ“ö#ÎDÏBÂY&@öfd2+—3Ö™IÌÿÚÿ«WÊ3å¡Ý„èˆP\³WŠnt4„—œÆ}¼©¦ð'Õ …β‚ç]wpù’B<ÌÃ%éѰRÜö@˜>½oŠ*ŠåLúoj 1óÎǒБýÐNÊYv‡1;Œ¾LXñ'×Y;÷0¾Šo|‚41d ì1dû)ÖvÎs΋?p|‹Ýz]O|:G?,v#ÛšÓÑ٥̞Ŷø%Õš,w%:` ŸŒãm3HæQ9zq£/Hï4e-ô—ÔÆä¨D)³ÖEŽzV›A×X’É1†Ÿ%5yKé>ÁpÜK"= üòÍD•ÛóÉÈ# z/éi‹¾L,¥^Býíhâ§hÁŒ÷”“îyïÀA½ŒJÊÏ¡QA?s%ìi :sä;— ³–{ìÈ^ÏjöVÂcvg Ř ø-àIÿÌYY#ùèòé |ãm°qøÈt/iÅþÏåxlõ 0“"GÖŸþßÝPcsÿe#ìl(†³£3éëXep>RËxŸâ€ÿÁÝ ®"LÕ3©ÉoF¹ùí]±ÿô%‚ÚUS²už9<”žª+È–¥Ëè¨"5X>‹l<¹˜®¼Uy{Û`ûè~edÂìÈnWˆµÓ!§Å«Q„©ª‹€ÉuSk¼a 'ÕŒ¿æ#*_¬B£Õ¦|ëñ|’¢£EÜå¶ Ó6#xXaˆ“fç« ˜›¤D=˰q¾â˜]ÌìîxÁHï© nOîРúD¯ÿ*®4“$Z"Û9/î”cDÍÐÓ&-º_Áå+jôšê îíi?P3y Žpœð¼æ8n)´¢gTÕ‰Ñ}2ý_,m¹› ²¥›hÑÎ}ôO™/\l‘›3pFÑ‚8óñPÿ¼>æ¢}–º ÃÏÃðhVÓ²& M'qQùR8ú3=cUH¿s"DK­àð;i0[Þð6¶$4{‘L/ƒºTMÜ•L,þ‡ÛE‰VG=ÞøÆCœ>Ï¢N²Óñ¦%˜Ù‘ãGˆûÍBü¨®EuÍg’Å<àrÜlüLb‹\±«˜ò q`…OÓ?‡¦8|Új¦ qßï£×ZGö¸[¸Ÿ§ÅúhÇÿ‡õÙ€*L:p /ß+ƒ<†—èWÿfùO)Б€̰äÜ)GÓ9§Û™¤’7àq;xWsK¿àXÜ6G›½ª &ÿ[D´¥Ž™ÿU Å=ê¡àM5þžŸü#ñÍË¿l·êê·—,Ÿ& ûç¯åVîÊ$â9dR\ˆLDÕÁYM²;=ƒ|Ì@ï{ØÃ]°E-Ÿ4_(åì\øÖª¸€)´#rŒ‰ßUðÙú "=+!<žY2Ë@zÿAÉ“sidè²£+‡J[Ó¨yÖ8ª8•/xÛ"‚< Hc=Ÿ¼†¹G\È¡‰ØÑ`@«ŽÿnÔÈ™F¥ªGð¿ñYôµ®¥ý"tã…ùÌ·éñð¢ F Þ“ü]?€QÏ÷S­W¯á?ö çáL¤7t³ð´Ó+<×ì_”ãíšoÐws!}ü{6ÓÕà }Fò<ϪÑeÏEŽÎ2ËrYõqµçs,z½8Ûÿ†ãÊ/â´;¤\¤7Bìó<º6Ù9¨†FMfß4YîðD-‡ìÊ4óKÑ4 X“8 \,È ©Ù´Ä¸îüÌÝåC ¼Ë ¾¬ÚÃoFÙ“\ºcNÜÜ´–Þ$j쉿)øúÎZŽÚWiòdȈ¬=B7KÐjó °;75'Ê{HŠ–µkÑÅG\l‡#Üw¸÷IºDø’öþ ÒÇ©¸s…ÂñÒvsöŒ¢ÿ•Ȯ¨ÅÿYÆØ’ÊMy5Àn:ïG;¥#¹+oÆAÞ¸#Y–OÈéÖÙ@>l'2)Upl‹õ¤ ÚàTØÌnî;‡¶õp)©‰üRޤ·ƒÙÅÁ_±Ð¤ˆz*›‘Wßf‘ûþ{èÓGLªO` +DẺ2˜ó”ÎÎÂ=«£1lÓzòù/séu+ÉZq§Aæ±3¾¸á¶+ɰå"¢ÝFúw2¬ †-úôË£ŽûN5,4¨äVú›ž€ƒ¦ñäᜲ.ÁúB*ó¸ «ªŸa²Š uݰˆüý&zÊÑ€‘0sN8þ~×Äjªž%åi?pìôJïËçfhËûþ6Š áAftôÂÜÜÆ`› hlͤ¸ú1 U:BÈâ*lºˆ”–1 "Y´(o7^\qo•O­Á¤—Ü7“¦|×^º+˜·Ñ`û?Ø/N!Y.E®cä¦T6Z#´›9~sΡ T9‰ÖêoT¸Ÿz·ó±Tæ äË•™²¼ˆâÏ¥VÄz7/±=r –Ïv¦J—-Øš•*¸iHžvkO§Õ'YÜùÍ ŸªË“«»Ê™Ú‰x¼ý9‹ríáëñ¬ÄþxØ­ó‘©ÒÖ„oéãðäæJ"a®‰oÝÉ|î>–_ÆŠ~xäUƒaûµû4µ~5ý•' µ =£³]…Õ´_ ÇF@ÐP†”Ë[cÝÄA¶i¢˜øÓ(¬!Ér¾íÁ©­è™>#Kpç ºÁ—Çf,Ÿ…<Ú|x È“D Gð©V¤²Z6¬ÿ?ôþl@·4NÜì­È³‡o§7ߘ|÷thÚ™®4bo¾ J]oàì‹"É1e¬í äÛªìV/êÃ%IÊ $©¾Ÿí:Kv9ÑÖŽ“°Ä:žÆdÑ5C«ØsbÛ©ûÖ4ï[4LØú0þÒtµ¤ ¹§ÉOž-¶'Ñ=æt÷<#³„ ´N'NÊk«„ÖwK誊vXûµ sÃK{¼U|iÝkú›ç2¿Z—Ú!Kâ : 1+Æ?bBõs¼ÝTW˜?Ü€E•°%ƒ²üSÚeô« ñÛeFr«¶a·“ù晇QkÁdž#>SâibâU²ÿº%9îÁGºÜbñF˜ièÇO‹ñ•@:ù‘§ÂITí…Iá8<fCøav^0GiA ]ú„¿0¤ro.2Ášz~ú­¿úžé“(s6iKºx~3˦Wž_«hÅ£Ëø[íJ4Àèæ|¸Ôç‰û†Ào›ô… Óû»½èò`Þ[+,$Ðt‹£ü8ï±|y21ݨFs·î ½ï½ˆ÷¡Oë#£è‡Ïy¤ ù=­˜âØõmtñ·+ÌÃÎФGô¾Ã÷ ëá}º*Uôï¦Í.üt¢K´?Ü"^¡¹T¼6‰_q&£Û¡MËKà™gF9†Ущd°RÔ‰WŠM¬i„Y;TþU"i«P$óXí d¿ß'ëž=›§:°£p •XéG;T%è–¶´¦*à 7SoýŸõˆ‘ªØÉøh­$Ï%áa}tsf),’"‡U}ÉíWß¿G²G$^ëÖ0vÝqÔ2È”^¬xÇV4‰2«ÿ~Å=›Ñ7n-¸ˆynÅ žÉýþ@Ѝ½‡û^CË;9,Ž~ ©þ ~7›`Þè`C“ØÛ^ûaSóç½w$ÊKbñÙ@ò8Æ‚šn<…{ÒàÁÔþ¬-ÜC }ýIÓ’A¸³ä;´îŸù¿s˜q˵ÿPfÛCææ¡ï°·±LÏËQ½¹ãpd\ŒFpH±M79+Ù’çÐ:[‹©ÈÃ.{¦f¢Õ.ýn<8>µ$à–jDàû'ú|t&yþ΋TOˆïæeØtŸâ/Ô›áëßÌÂsŠ_ÑÅ' ùƒ6‚¯u º}v.‰©~¾ÜO+ŸãÂÎ-Ä,â0äd\–\„—gàÞl/rÌã(³¾÷|ñÕ¤M [ˆë;mb2¥[3ÛÙ‘ÁtlÆî“uípüž%YÌMúÿÜâÛköêgêä9Êò’ 1m½µ¿™“¿¡\ìøQ@QØTœ¬AÙLê>åËÏ EÃS²Ì•Ýö,†4ˆÃ7« HéLK«áÇz&®!&À ¬îr£‰Ð N-ä[Éü¹‡¡b;I®ÙbòIã1¼*[J»¬`ç9ýÅO%üȪǼ^Àü^QLµ»¥©8:@p`4Nð3™êØçyÔ_GÉߘ<ÁnØÇ7†)›OãÍõ3i¼L:šLP@[’ìÎO׎Ë`©}‡Sd»Žœ§ ßÙ™·nЃ–CtŸösX)C8«h¡éhTZFµ¤TˆO_¼ÝÈ$ùÜC§p |‡mé)1}Ø«ÉCøÌú€¿ÅÞá/ â­¿!pŽeò *…œ[û^ý Gƒ«¨.´Ÿí_Dõû½ÉgmZÄóÝøUŒ>O6“ö—hʲëœs1dWÑlâšh NÇÀô¾­äJÉE(ŽHfÚÞ#ë|;“òTÌ%ƒÑ˜ÚYKmÍþ‚ܧX4+¬bî^ûΆ¸>ÿF~¨:øâéÑ“˜¸TšYÄêfôÏ…1v©°. zç3ÝÍ´"Wø+Ñÿ6ñQ'©LªÒLIš`;ë ăBxr­ ]DˆE"ÄæËÒmÖE¨7äÅDpâƒ'“<6“þÜÅ^ u¥U½Ó!Ö¹,^N©|äS¤ú(JÒ2)1´²—'Yqx¢s¢ëá@‚åñ(ãÚ×Í'Ã’Ïñ{Û¬È7f&F\`háIü2™Â.»Áñƒ¯>ÿ¬†kÙM[r 0T‹¹!ž Ï–—AK£9¤Nú"‹ÈKñ¦}…áÿêµ/à;/º’»aÖ¡j¦oG'³,`Œ©šï‚/{Sqþºf÷>_âQ»ˆfÇŒ†ál;;‹<ÐlÆæ×Ѭí×^[)ÏKàßPÍzvÇ…Mû —“R>Žle8ìùRËžiôAE5à“Á)·£óëé—x FÆ¥Œ•ø)L”.L­;GhÀ¬*0ww€?)‡IJ’:1ä7g›7¥3Ó}mh¯®5©K×£)/NáÚ»2ôþ?›7ü{ž†ÿ¤²iål*"[`7‡„gæ2PrKGgŒÜ¶çUXAOD"Iב#§õm¨¬{h·*á¬ù ¨¿é4­½èM_-šC¯=žÒC/¸lЕ¶²Ô/T‘J^ *ß? Å sQd+­¸ãÌè¾ßMóýß:è:ËcfR‹%s¨zm#«%O辡Ì;b›qK1ò£ÞãPÊØòöÁµ‹út¯Ù÷ò8We²o^%aG§Ãƒ­d°Ež^)ëA·çq£¨'kªíÕð\íÜZ݃K}¤ˆü¹_¬ý ú¦mÍß݃˞ÉÐìÖS\¦IŸ|ÿƒ¥Ì•!Ô„øŒ4L4N‚ ™È>³4¶b‹1ú î‡m"h†K?þ¡fSé\ç(qg\›bé#4¯[Œò lÂùkLØÝ¹3apÑ"úgÒOÞ9ÎêŸýŽ‹Ýýio¨¬íê[yLBüf¸{6»¤ùÙÿòˆ™Ä.ÁlÿK·YЮI%Ø’Ií·¿eCÿ…ÑE•¥ìïA3âbj@Jr\ÉM¢Ã溇CPÄ)úoŠ9VÛçBKÀNÁÔzÇ}fÕÙoŒ•»Ù|Sº€ýo£#Mµ×„Ð".$ë}ã?„Ñ­ÛH–~Zü>“™çôhy ÃúJµ^_Dz4/v§û<2`æë¯Ó6£±=ö¤ÉÈ÷ºy͘öñ9ýÑü—Íg0ùupðªl²,fWIÓmsÓ ]! ¢æPêŸDBËÙêÅjäÜ#¼=ÔÔ(Ø~[Ü¥™/*ø÷óUö"düü)œ¯v’yñ¢ŸËÿÝ—]úhwŸp‡Ù^x½º‚³¬½’‘_zÅy"€n˜‹¶ Úû¥•íÌ•Fµ_óÿw¡ìs‡xà_|or~²áÏ_bSeã¨#Meþ"Ÿ» =*¡Nç:T3ý”1¶°Š='ÝŠKuïBбmì-Û« û·‹é:4ƒ*Ûšb»ÆUÆ[Ó‰¹,•͉˜%ów´ ¡Âõ.ÖçäJðªu¤zÅÿႲãøÃńֿ17¤üðÕ——¨õ7\·™GyCÙšÐSè$c€qê«áç{I<ý*–³x[& rß‚k.^çŠíºb£¿˜ŒvQüzîR{ M{J9Y‚üt‡2:Ë2Ç„IÉÛèØ ÿ;{YôùZP´½çyè.GErùªUûƒ¯¾%ã²SáXrk +‡Ò­…\íbÔÒ"+sì©ÇúB–~)*i¤ÒÉMIª‘ µo€ýw'"×;±š ³½†<Û­ Ve®ÔuŸ<Ûª@“rbˆä¦P‘æ!¯&æRþ›wàCkT¸†€¼’üT<Á´Ë'àÃãUcþw-‹†¼õ[àöê6Ü¢‹}“~táô+°¾ŠŒ§á1Ýp\½Ý¥T¢¥™%¹Â7ÔZ=ÄöÕ½xl¨ Ž50‰›ìHpÄvÍ;ŠéošÁgï\RW]Ⱦú{ evÝå6µkÓöîÌ`À à®™E:ÇÞ@貕ð”G–ˆö›%ã¢XÇrt—ânÐ:ðÎ5æ‚ã¥X<5GЬEÞüóP¶®Œ=¨—–“k3±–ø7õ(t7ƒ™êטG6Psó3«v¦˜]}Fû`ÏóôI›A=;GçÜu Ṗ¡”iW¨ ùèŒh”Á|i4æ4Ž,ÅÓc_ðóŸ“h®­Î¼Ñó`%æ§ÂÕ9䌊9—þï]A†õ`œ¾…\6œÖ'K@æ1Ëå)OÂí Ñ£«\÷Òâ‚ÓT¾p1õìµdä,×Sùßñ½ª8}Ѐ´4›ÝÙu‚D…¿dÏg]Å×#æœAu®|Ø~î¾ÂlN¸ÿæZ:ìbׯûÅŒ÷·Ãî‹g¨ÈßÛôß69²’ºÐµË&Yƒå liµú6ë]K3rê éùöœÛ°ú—û,Ì µ+ ¶¶;¡à¿Xг°#­sp¬œ+wBÀü"t¸–ÆUZ;¿Â¸ž ïúËô„ÈÐ×=˜?¥Ëa}~lüø‹¹Ù®KægÛ‚Ï‘äã¢WÌÞñ>xjï·r·’o‡Aýó,É'1F6ÜXC'z¦á±Á|ükcèb‡ö¶°ã÷:à,@lfqW‰!1.udîõÌcÌL#Á:ô3˜È,Fùé«éýK6éÙsÈØÚ„« nÆxsó]|ÐiB¾)’Ô®»¬-Ÿ}¼é«3‘…·¾Â·³bq\å(ÑÿFÙ=.dã¹ÍäV•tÞR„àW„íd`Oîyvšƒ1NÌÝBòžÃø½Vz|M®“$¡Í6¤ßµ}§âOÇ‚jèçðýçÄ´á!÷µæ$öV9Ãõ‚¸lb˜õìû Å Èl[k2ÛA‚^i†wk^á3£(,>¦Á®·öjœF£IØ;¶>_(áƒ7¥«˜á,3ò}Xƒ6›ß?‹LæêeYêż€ •ǰ|Ûzönâbªpá!x•JÒ䥅œ¶.9bûqGdX.ÇZ¹ ‘=2~e`_Ó î³O˜‡íoAsü3cgŒØØaHÍ6=ñ9>Š>ÏÙù¤ž¶šÓ"¥ä:o*™!û÷æ°B³n0¥Øƒwß][PÌ.8d•¢½à[(7'f’íGs37àú…3Ø»Z6è ã 5PüÒBŒÙò†¹iþîI—!]½DeõI¢Ùüû*+¦bT3pë£Lò?Äœ'Ÿ°òþiö·Q"Œ;Ø8 ”ÿÙŽïä#À¦·é-¥˜ùb|ÜÁXl6Ô4,¥žójñ½Êe´÷ ÷¹¬.Æ’sMÔ¿cp6ù‡·³´ÎåÊzXÎJ“¡Ÿ1‘á 7Ç·ìSÃàÕŒ˜ÒµÏ‹Ð×{çŠx´Ñ÷m}ÉüܶôÿbrÞ‰Ð÷q•];ƒÇÙ~9Üvq€yº%lº{¤=ÉMSÜ!¼,KÿÄø$Dv,£…@ ö’ÍUSwîÆÞ’¦ž_gʦÒPj5w>ùc@¸â‚tt̘|ïÛ Ë¿øÅ« Õ2:¾ý%t¦®ÁÛ?ßà`¡"µ™ÜBbÕʘ¶ñ>;áå¶2óSn¢EÖ&¦0‰{d»k¢1j?M™Î~Ÿ‘ÌÜ1W¤–;-ØiäÝ©^xÿâ,ö4"ŽBs ]\Žl“¿û2)Åx6fa/¶ªª±Iž·™Û¦O9+éö7"sÍäÿ+åp³¨‡ÝuËd¡F!/¹z(ͽµ -¼^³n|08¼ .¥² ¸/¿ý„Ë­ÙPl!K–[߆ë»HžÒ~8ÛaÚw™Â„Ïà·Áw•±’íjàV ,«qØàö­–Á!möZÛ<”ZãŽñªm-ž&‰éƒu; Iƒ‡?3ß^o²íy'âê[k ÝœÅú–­!ArðšT2œŸî “‹tH)ÍÀ?{­©R\ 3Çá7(J%âñ¹ xô†ém<´â³ùO»¯Ë¿¶Cþ3WÐvþ|³è5˜ ƒewO3«;:É•‡aÛUº[u>0œOŸ•瑼­¯Á®Ú§ßQ¥[Jò±×ûdŠcïÿ¥wN¬ÐB8x/ïÖ£ÿUÜ×›Híw¼ÃKóOÃz»£8¢ƒõ½ò¤Ý žS¸Ê¦ÑëÚ,ØsýÜËuC!Ýu`j0§n! Yð°N{Ù­Q¬Ùü X˜¢Å°)kÙ‹õOÉ‘+€•ª°³~ñ˜0$¢k7€›xçÉ÷pñùIÜÝD£¶)ºücÀdq²u§ KDw`þ6ds£r0øx›èÆGüÔ"éOÛ@i Êî½aãVF@ʶ œ‰”^pÓ÷Þ™Ê4àÛ ŠÓoÕ¢òg5Îâ<ÙÿÙÆ¹ˆ‡,®Â,cÜri³é:Ås!—˜¬ÓaàèñSzF¡8¿:™³ò"hØ@Òqbl\þÿ¥Cx'¼ó9Á*îzÙIiPàF´äˆó…pÙ~‚Õ™ÌCãQ~Ð~ÎŽÿ¼„Ò«“AUKš£ô¯Ø<[‡î^@¿Ï ë’9BY pHn/­¿©CKRø8½“è¼}óu]" мcןòw&·Ùêk”s5®da’æ,HžFSró!_F•nص÷ü7˜Nà>ÕÍðÊy+çx(K•¿Š²ysò°1s9pÊBð¼Ó`uºž ³aÁ†äÿ#L*¥Ì×LÜð݇‰>÷œmà3£-wl8ÓŽojàtßrÊ5MÛ,.ÿaÇÓô©öº,òC¯vµóÓ‰CòxÒâ&.¨ûÁž8ÿ†5‘P¦ñmˉ‰‡òYùÌå1AꦬEN¯°‡äE+ÉŽ±¿X]ÅC£Ê0ÿìœ@´V ÊâæÌsd_¾>ý˜Ý¨µÕ…Öwé“êŒHöôå6Vµë7rHŠŒï 'BÅ!$zÚ&,Õ,á8‘§9ÖTw€eV(ÓÇ™ ‘3bÌ̋Ч9ëäé“}ï!˜™ƒ¼/—“³àQÖ5¬ì0$ËeòÉD¬-ûr {ç!6®ò¾µ·Ð=6-5š°p­ íq¸Å‘•=ÄÚŠÒ±›Ç鿳a wþ\ê?Á®ÿøwÇ¢]­X1'Ô17+eÚŸP;í ÊÀ€uÔÿ®LYö42}çUöò°,9Öý&[PEÇfv[²"¬3¿Ë(¿;µÇèÊí›™õgMÀ}Ž.•̺­a´;MŽžü*ƒ+Î$`öþ~öPÞ't]³ž2ß'±ò$ûÅå5ž}m€âéÕ Qx Kz๫*•ëŸÄõ|Lö4¼“ëÇ”G)“ˆEÅæl '½{ ±¼ˆ+—[R“8¦gVmŠ$œFŸ|sg”$³àuš9y¾`T%`ßÁ8rÀ™ $²/ßZÓ\Á]xdá ^ÓšD+¢ŒàšCðè¤ý·v±j“$ïvð'A9˜sBˆ¾Ú m7$˜¡ÿRXÁ5@ÀÁ‡×(ÁäP6¶wMãv¼†¢€Œk ûy$”Š5ªb'¬48É9º:žý¶T‘~{¼„)ÎØ‹BT ßñ*.±Î#R3ˆ¯þ?NÃ!!8¿Éf?¤Ûwݧ‡”.’/—xh‹-‰Hø‹ ‡b8?dÏ4†JÊ£Oûȶ;IS” }VÃØÍpàœ*] KK¦XÒ n£Û¶à®4»B]äeÆÐÍúBô}®6)=Ne57r36é1dîf”BXðQ™”ìœÆ¼Ò2¢ĉÿÁböÏ}*éëH*\—cì*9bœÇÇQÜ:‡,û%Cî´o¥[²Om¦Sóº[¨¼'—UÔ‹f£´`ÙòltÑ€èæ´3h9ã3”Ã.ÕÏÀ [KhÁë º2$†¶ßRÅ]jÛiùvnÇl+ŒùâÉîô¹¡6¿| ëCvPUy’À²BE±dMv÷Æ€p=ÍéÝÇÈŽ?71ÿ[3gl˜’!¼öHn­·cÄ“bp™t;W{Ùt\eI}õ24ÿ.j®<Ïê×u±3¯$Õ`fRtÞÕƒî*è>jMˆh"™”)À»'&™T%y’=vô9O9“Þ ÞˆYÊΖ ;ÄÈ»¥Î8.ËCDûR¡³ë óFz´1zÕºØä†qŒIæzkhî0@³õíðXÕe¾}ƒ‹q0yë üx”¯x–1|b"øÓ~Õ¼[ËZˆIÑ©ÿsT_}Gþ1]ˆ]ìÈI¶ÏÂMN…àQ«Ã\ù{´ žÌqàØDœg·_þÏ·^eí·œƒÇsÎ3¢Âh²‡×p m΂†â+¶8Ľª)Ž’Ê401ÔĈÎÀ"&(#£ËÀV´’mÁE[ŠhÖm]r]Ò j”úá"÷4ò\w­Í0+;IÜùŠùzw¸Š\„™ça©?«¼¬xNž¾ƒj¤aå\*lMÙÔ–pëÆ=2.ßݸgp]å¬éìo¿Xïqp¹ü)ÛYù–}¤3»¿Îd÷›3ôˆ¥5‰îdåºÔ~R±bUp‰`Wp…¨Êò»ìCíÈüC}:NªõŠèÝ#T_ú•ÐæÎ§Ç Q>»÷O kv§õ¡cu¶´nõröéRîVY>º¶s=pÿë”ÝsPMû†u†ÓÆækŒfŒ7rGqâÄ?`[ˆ)îË—¢¶þidûDÈܘÆZ<ØQñŠ\|+¹ªøu±ç¶G¡¦\xòê âà9ãè²{sÏm;–T±Y<ÊæZÿ½of•\/°CŸþ°}Otèò³KÈò³÷ØÏ+Î¡ËÆ"Ônº ë¾bÇãFZí ¼ªýËÞqTeOÈ1ÕÇþ!÷©1¹š[Q³qWÒãFÅyûÉk‰:XןÁ (n@‰ÇìP“"×¥ö+¤@¶4ÿ=E(>Ïe˜}䘆*Uþx`j,sÁvF£o FºqÎÿ©ÁtíKàqû)l¿ØÇ|XýŒ ý™Ãêpno‹V¼óˆ´Ìtø]¨ß®½ÁI"Å豦IK¥‰Þ{k&Ýê-;ñSð*Ÿ)5ˆ›b'Ù ¦æY9cï¸Ú¿¯¢ËaEtºlÀM‰Ù`$ ôýátfõô]Ì»¬mpÙ»*eÁóØšfº5 ñ¿­2øF–‡kÍ#ŠWSùè[jí#Hÿ3¼fÁ§ØAÇvæÆ´µØ+lE‡%«À->ž£!øÃ5š&ÇÑ”÷‡i»ÿvºBÖ¡iäx[£ï²ßP¼Ošº-ëaw>¥ßB8M™4óB£kÝ_–Nùåܯd×w r!"ÖΨŽYÔÀG <)ÜÃ÷`´©“0w=È7þ>|-–Œý­—X ¦Ÿ© ÈÁY‡;ð&çnq” ßw?â¶1ÞTòH1;?r%þÙÎ>˜~W„X²±)ù$uh€1²Ü‚ó–„“Õ£Èø%§§úÇ ý³$èÚeÆ9ÁŽ Iê@¥ñ¼8ÛÓtÈÀ… âÓy|0¶ãü‹'…qÚȈÛËCäWTA•§W™Gž|}´ŸÄÝIF;ÿ»øJT‚¨ö‡š=€ó¿E“å»L©f“ù0ûãTy÷á\a-ŠÓ äµ¹ú“@ÙQîëW³À¬á¦ìBs²'Ò†è,°‡K ¼ô`Ú¦º×ïc5|kÁöüJ:}ã,ºÔÝtª¿gAêh MóÝŽ4$iÔpÒ#^ ’Õk¨W/µˆv¥ÿ¥ÒóG³á{û7fÏíMì³V%NöoqOÞ^öÊØlz–̓T­\¦®´‹¬ÕèÎsT~ùˆ&ÜuÄᤠÛþ–1êiƒÛ*\[î½ËŒ?'‰Õ»Åšÿ8 î¡K¸"ˬ¨Æî(\¼€ŸžüfL¾^úΞy)CŽm€‡CµØÖƒq¶¬àX ·0ªú)8¨®GK÷È“¾Ä‚¯,GLÕ\ÇJÞÙå4{«&¹æ ·ÂDéäÝ“0àh@–w‘¿Š:8CK;7é¼ótù¦?è;o%}+¶œ4­÷£Û¿ˆPù='¨t÷)bô9÷ùÓ=§n‘Ä=³ Cö¾]@ÅtRéûŽSx²‡—î°>‡šü§˜ÈŠzIṓÑJ½næàͳ‰µ&ð‰’MQä/^!7ŽÆÀú(É«MáôׯL2ã&­²•'ûE’·ãé8‹y}%7.£Î3nBT§åŸ=„V«ÎS&þvìoÂBÚôÍ£@r;Ø–JdM#ÁçøIë3c|¾-—®lã§=ë>¡eòJVìo! ¯&ËùÇŽ"aepø?>4n.ö üïÂh“Õ”fÉÓc† ñ²ÌÅöåpºe=UªËg¢ýaû]?z#ï]§•O¦?‡‰«©X• ‰êÒ!‚³½ˆÉÅda…‘ê¢ýî¹$m÷]v­ ù±à&Mµ·¦—wf²M³©ÙåÞF¿š$O¬œÜõ¸OçñT‘ÅßÎÓWßfá¼–Xæ·™5a&Ñ´hyõí .:ƒy\oÊ»«8[J©^•ö¯ÂÁ 3é²&’ẆÙRÝDä=lˆ¿î- Y§Dg¬$7æ(É›;è®ý.p«JŽtIÄÀ÷¶dˉ<ÖËKŸéçÅݳì~™‚rïaôþ\»„;Ö—Òñ¡Î0$É ­·?¶‡§ôÏ}uvs˜m5gà{þÉn€ë¯CÝ…b¦Û½>ËJ3U:AìÅ™U¤ ®ˆ/GH¹B¬æÄP?!qLS¸L~LïGCËY4¤Ä'ÞðМEà‹ž\¡ð œtÑXØ¿þ/ØåÎcÎ}ÉÀK ?¤mW·%`"¥F—ÙåÐ1ýTÔ–ÎÁà©qØøz¬(Åü·®4µ¾ƒu])Ž÷¿²¬Ä40¥F»_L#Y÷/ƒþš#ÔËûŠ®–Þˆ,ÿ!ÂùÎúNãFøºä¤Ì®Ã¡Â¥SZ\:#Øý»°à[{13N÷ÁÊ3ëHú6 пÜwÞ´þx)Œ®=‹'kâï¯ÉÄïÇ8,YŽ!ÆÓ’xû{‚ÚÎzHž•ÌYyì0äj>‹àÎ= r«yyzÕ Ù0i8öÄ›õ§ÂΤœg%‘šÞw@‡ž+҅жýL˜¡<­w8…c T|‰?=˜‡_t5É¡=ÝLªùkTˆÆÛŸ¤¨ÜiZ,P+Љde/VÕ‰s!ßëyÑÎèG×Άî%¥˜¸`Öì,Çwý t•A(üïŒæÿå(¿ W`«LùüÁiØþÚþ8Èî)Í'忞À—ÌU«íÔ:Á”HôÖ`Äð~úhÚ:ýš‰•Õð_¢-ºGˆŠ“^R6»§YTØ#†ñâgAJÑ»â 3øs q:'ËîÐó#ŸÏÆÀ*û­d^´ì\ûû:“ÏfáLÒ­ÖÐø/ò›œ"Ÿï8sU·Ä‘=;žË0UÒ6‹w¦zUKiPZ;{x³á²¦»3wìY7· ø$¼ fðàî€^hê#àÁ]ÅQ›}›H\\øLîÉ–rbΘý»ž$rφ¦W?gņèá€oÌŒs{q/ÆÑk/áG„NtŲîõ#è *³ˆö‡…dÚÙ¶¸œÙëLë6¹‡¥bôL~qö¾N‡œÿLéfté Ýšü:¦ ƒ´ôê?.j’•¨JÓ?¨e é„N;ðBú„ÍF%…ÔgÿêýÇlLË¥å¹uôLÌz±jˆìA‹‹È&&o¶RæÁŸñ¹š4®Þ3å+.X,¼4’T éÏÆâ˜+¸(æ+]sX×ý…mÁæÄöV\öR£ÊáÑÔõïk&bñ96»e ¹CøMW¸‡Ù#ó"§îEè—AÄ*^c/'Üû¥vŽ©jd¶þTŒÓÊÀ…4hS›ö]™ðÈ=„É®­ä§äp»V€›||ɦVmZÐ?þ-#‰g™åa)ð»>ƒ®zy‘Ñ*›aãzÔèêtÇüdLW‰“™/”©Ë€ýW: [y·ÑßÙ˜šµxKâgCãq<£Øs÷.ÄããK!`S üçÝýÕC°MÁž­èüâ ?¿ÚÂá‚Oø£twù©:f¯Â'ü´ósQ½†)ª'Œ„ã `ÖQ•…þг2 Þx€Sh¶ü9ã 9Ÿ–æéÓðÀ\Z}ÆÏï ÖË{Áù’¼ö}‡]{1µw5uzÆnS”"ExIÎÙ¹¸žGΉR&2°ŽsÆ·z%ІîGÉ,’œâº)2ÚÕ„R ±ìèÅqè|÷~v†`ÎH(œ<ÁàUEkœvÁXÛº6q)M“,'‘KΰSϾå.dKÃ!ÎhìKØo’„"´ Ey“ȳGMvk²â¡Nè)®)- ÉbѼUKÕi÷ªb=þî-Š÷ÝìTÑÀK…¦X̼> 1sÛ!í“Êòtz€cÊ<´1 š#f±šÆeãÑÎl¸ùŸÝ¯¦PTömÃ$ß:®ð¬cLtM£ìB±æHÄYq¡ØÌŒä¼vÀººÅ§©û[Ïả?˜ ˜îž0z…Òî0Ù#@þYìÑá"vwØbßp ËQým:·ÉKŽÐGð­H6ªøAàÞã̯#ep¤Ôîám„ˆUj`vÿÆ”?·×'‰ü|ß"s—)áÒ»øäX5#W×ÉY¥ÇÕý¼žþ¯íM¦_9v72˜²Ú½¬¦Ö¬™m‰—-בªGOáû¶|ýÞ„šž÷d¥8³É äiR1Sd3Ñ(ÿ&£x>•f×V£Èbcb£ò2kpoÓÐ_¢ÍTÌŸ…2q±À56ï„oà7¤AåT Áþ¾‘!5®$ðÕ'»»“ ¸= š+0!à>Ìy‰C²rX¤I/,Ìor„Á¾½ðj<ž¬ýŠÞ‹=!Sl:ý3ç%áûœlVÛS5LôèÐžãØ [ʶ|ô£g^­O}{òtïÌß{”<í3$š§ñënò%#ŠnG÷Xr <9mu m&!‹×Î|•ÂC~_ðëV)ÚSm‰›ßM0?–d@¬ÂNwøéc¢ w†yoÞ %É»1c2§Ó ®-âÄü{‚ÉY,<ð ± œ¬:½JtkT:SgÆGNû<†GëéŸAmRL¥At¦3I‘ä%¥JÕ0¶NŒÊÜ~Êí+¿Nß ZsçtØ'©Tà“4 W´–Âg•Zù,b®ÖCnÚMNý±!Ÿ•òaë]°y²ó$<œÊ/ù‰úk²©í¡%ì`œ5yÙO‡\†láHòÉJšD»¯¥?=W‘…Ò ¯ªžÎ¢{Úó)Oízâö®‘~ëÜD¤‚#KÂìé:ý¥ð5QjÚ’c³:3µiYÔK¸P]n·ã=š^´a³«_±¶¯M¡†F:ë´$ùöXu%éªð αó¼8WV“T…Ñȧƒ(P­ÜHbÝf,}Ëb¯ãB:Có ¾ö$BÖ+ÈSŸ&ü•­KO75£GY®;\Cô>l$·Ì°yÁ îô˜!<:Lj¬‹Ó¥n…“‡Z÷¨ ©­ãðØÿb}Ы7«QRî/ÄöfÓýª>˜!Ò‚*Yñ2¿ìˆCš[:½•Uú*A3¾„¤½­ðäi-ó™I¤Äyg} 'æºÝ–ôùÈbûmôÍœ\–fš¡ä¯Ù4©z+8§÷0 rˆô>¨Ãâ)V¾úŒ»²Ì Í•·³'tÚ˜;…žhyA›Fø‚À!Ið›ÒÎþÇ´i—¡+4,Ás]YXVÆv|¾‡K5>AÞåq¶{mpŠ‘Æ“Ìô³ê ú,Ÿô”«v“h,yQÎN•ÑX±0L±¸€j¿ÁÖ– XàóƒÑÞÅ·æÑßi^ø¥×þS™CVqaýSm¸­º†“òú$žó”#}Üãxû$=P2Ÿî<€½—†Ø%пÚ/T“»©zŒ²â|ê(û[Äl1ãr%µshÅŸ“½Ð¼i&U²e¦&LȪè[œH+î´ÁGESØóOˆîtâ°÷%¶‘ob°>:±šÉC}>CŸ{<ë×€l%OÌEš8UIÙXµ1Ìá!q7À‚¼ô‡‡5ù¥ü޹T)HW8ÿ` ¾YPWs‹ÿÏ¥¾$·zªN±#&úŒvÊ(þV Gƒäùä£ÓØ×êHî°#ìo^\!á€þÙëà~þ4¶6ª?<–„¢LEÐq•Åÿ#êÏéü¾ÿqÜìÑ!äí0Än>Š—düÈîß«ÿ7ZwE5ÏV‚æuW:%g„mò+Ýúé8.r_ôõ9¶„ß`¬TïàÞ–MläJ™ú„1x '=2åmhúaiø¯¦§Þ”¡&jüÔûé'œÞi¼J€œf²9Û—Î=ÅKÓ‡ €çº6îù"Ê>!ÁV³nˆ²¡'fˆPºÆ\k?³o’«ÙŒ5´¼.€¯=eëÛ ó=ºöèZ°÷—€©1ÓXn"ý##Af·/ƒcÐô›ùQ—i ÌÚGÿ<ÚG.4†‚3ûÈ ×XÔ:È…Zýô/Å7Ç6“‹nÃæ7%¸ZÁî[¥ãñƒþ„ŸÎ""ªÐg,‡6%8‘ÖÚ*4IBÐ^þ[÷ÙøYaD$í¯^†å`Mí?ËÑ×ZM¿9|c¶ç‰‘ñþçlœ®"1T9D®óžcU+O]:º)•<—Š ßE“ôÏëÈ–-ÅÄ|¹5c8ǃæf¦ o~'ë0Ý_þa3ÔㆋxÆ.ŽH*„µ›eˆÏèa˜ý>‘ë÷…7BæØ´9†kwáiãùd¼±ÝÍü‡Ecz,¹ûEðÔÄÑo»·’"ÃèΉƒêÍO1úò¨C•ÔØž—Æ-™h}÷E°Nf{õ˜=rͰ1µ„º¼ /J¹7Zà /½Ä¯ÂÌÿô³ž‚ã:e°Õ‹Àü{Ð9×–ißeâÊY[V.24#c‡4àÐÞlëµg¤f0…ÑÄø¥2Ü—p†Û¹L­óxýE†÷õlîëuš4û¢È}|²ñÞ,Gc+5±a÷Ÿø £®¤¥5 þ©PÞ×õœž/=ìØâ´}ãM8&P æ cÌ‘´z¦ÅÀžª˜Çö #q’çPBž›ao~„?à# Ù<äÁ>*(q²ñà¼zöc/);’Êî_ÇÚâ×ÖeXuã¿ÿ=åÅYwçâ¯Ôp¹Y†›DgÐ ¹GX}ŸZÆ+ƒ‡%‚Të£&µ™XKEÓéDöÚÝd¯ôaƒuªgö¡r¨n˜‰|cððy ÝÚŠü×~*f1I®6lDe *;éc„ÚÑÙ·oÅ©õ–õµÁçðâ•4hï×Ç}ÏpóơÉXün˜õYf€·ûD¨Ÿ­úC2òÁ7?˜5ªPÏíËèÀ‡'Ì­zEúÕ)$/d~—œ‡¿wÒ È>—]þ+–<ÿSÇT&Âèþ†s¶=‡×SIµKl–ߦúSšÖÚ`ö>i,1¢V^û™×ßÙ¡½ v!§%׳÷Ìî€ý’\ÎÁCÌæÏ/ÀCC·$1÷Õ<0ê®Ìû*wÞƒƒ÷8¸X’%9Ì”Q{úøù휉tJ“Þ3ø__hx”šÂ&}nåÂHÞó@µk;±øš"·þ-‹®@ k#«4Àâ­Ò2T_dBš× Cˆ“+I†<vnWíÇuh’ÉåŠlÔe]ÏÅrþ»^ù…å,`Aà |»n”=>ºœIY<ŸÙðu˜ÕüžÈvôzRí©›É×ÒìËá ì; gr«†ÅåòIø Ì—îÍ:ìxäi8¾ "ŠÃ콟Ð7f çÒÛñËÌ!î™òRœ²ð8ls¤_ó-qH„Ú|bÐáf/Ê}P£â¯áSÖX¦ÁG—ÆAŸ|û÷²9ðHDI ÚrNe6¢vítºì÷qÇjù|<ЬEžÙ4#ç‚.9±Q„šÜ"/ƒ9M¢üôÕïÝF®tçâC~~ýnDo^ýß8œ XÛß{É”‡´u83{µ©Èôwp uh<¹h ËB%è•’$íà̽ÓÉ®âëbÔŠ ÀÔÕšõ•ç#¥_ÂÈÚÖ Ï…TÌÓ·¤Š–3áÞ5}P]¥F}¤Ï1Z³BÃÑ%°ø}¯ßI£½ˆI­^¡-ŒPà Øùk&í 1§e’RôZÏW¸îq¯ Î#Jñûý`u¥™qÊV <öÄÑΗ)L/e•ná½8Yb:µje¢ðì&Y2Õõ.ÄÅÙ¦mÀ£r8XDy^±øûÖI&ÀeMýÎÞé­fô¯dÁÝåÊ$(ê &_+†’dM’%&Š>Vôek7*®×e7üâ/!… ¡ÝÉì´çÙ)Y2t傸pÚF<ÙÊLîùMXÞ¬Ew^¯ý#ˆfý-ø+7…9½+‹-Vú͸IÓ¼g ¨à1á¸ë’7»uæ[8B!W8Vß-SÅgaGQçÏa6î‚2‰T|‡~HÀµñ9pù¢6Þa‡mýðÓ¦™ŠQaËÄ#@É¿‘»ÿÏcÌJøüÿj“Ão/•&{‘ÓZ‰ùOš yN2F¯.@3+Gz\ïL©yÇdÇŒ³ç €. “Bw¯/‚Ùlª„QµŠëhØNïŽaª{Q(1ìþbK^˜œF{Õq÷¿½dg…ÑÙ›Ù.Ã(´9ÔjÕQìwcî6†ÑrÒ$VbÙ²©DÄ>ý½°òGíȺÌ%TÔ¸fþâ£å‡—ã×rq’v» §‘¢¦a¨RØDD6¯9ÁVæÌÀÐS ¸K᧬D/O° àiCÆ> £«–"sׇ¼Å¯0´.ŽnÎ}…·#oã)b¬{Ï]üdºÃ­¦Gþ|ÂWŸ5˜SùjtÏ£-è¬ÀmöýföTŠ“'ŠG8›ï±Q (-€92áÙÂý¸ö±Ì1Ê€²ÚÉq8»1«_˜´¿«dv('áoqØ÷öÈè ÿœ+Øó[•løáLæÎ–†8#4oÀõ?g’ä?™E3.GäÁFQ‘§ì3™sŽÿ¢èÈøÔ \޽¾ûSMÄH~ÕSh¨U¥Ë×Maö>—Âß%Í,¬6`ç'pOÄ”y¯aUˆuL;Á5K¥&sôèå‚÷L'£홊ÐÜ$ ì%Iè× I›´èEƒGÌ츸 º¢1™ù·> øÅÕ8g£î²?¥¶±Þ9©ߎ~Û ÿ€?ii¸‡é¡7™øö•'9oòP¢ÊÄWv;¾é|ðÎQ=T«$S[S`äª9ݸ ›…ñSZ&©>²“ÍÏêÁTG1ó…¨È¨`¬ÂyÚÛBÕþv2óÖÑZN Ù"†Û)›3:öQU¡‡°Äq ÍUÔeJ«ÐЧö˜UçJÒC¶³¼\iÒ$DóƒZñEÆjT¬ÍJ0eky qÛ¨.Õ¢brS@ñŸ £ÆÔ…_ƒÞ6þ Ÿ9Ð? ë }™(±Ê/©Óxõh,c¨ÝÝtTö¹Â=ßòŠÓØiFöΙÕm˜"²-Æ0;¾ÚÏ;«gž(YºåSapi6ÅEÁïKÇ1áÙ\óâèX̠ӣŨœÊ Û¨šmÒ¬Þ…³h§u"]·âþî LÅæ¤Îêêg€EôBú<¸‰)YÄí•ma®¹õA"Ç…t!|{ëHXD÷“6&¦`ÁÁ3lŠUxE©ƒ×ïÙ‘é&wmØ ÍÇ3œ0¼ïLvÊ8âèX*“àQëößÇw uŒÎI˜‘]WúÝ>VÝM‡ —GøóŠ:]a>Úz?¡M© x1•Zt¾‚s@a(*ß¼ˆüs¿g7ÜJu¿]¸W HˆõfS#Ïæ3è2Ãäý'cž›?ªÿ¤+–›ÑêÜ“ ?]3òº~½ä{ˆLÓÇÛûVÚh}Ë]ÁD‡¬ŵäæcP°±!!YŒ³´4IÎäÃÞ'/Àë§›Øð¯5¡ù^œ»‚Yft E­™üðÖü8Á:òü@½‰9tN½0†;S09ö–»Š ub*‹ì1›@b$˜›ªàªVæÿ³¯.¬€ý"ÝÉþhêÅûEÓI²´%§ÅO½÷q¸Vßq¸E—8¿Î$«L·Sæ q)ÕøÈfD¬>7ós y‘dß6%c«t[П3 –ƒ;©ƒŠé;ö†>]¹^•®Ê&sWcº†2ýUœ±âïÀ¬Õ~÷%‚½½×K JQØ· —ø|@ %•ÿ×Zot ·«@ˆN™EAH_4O9ÒÞ„nÖLÍ’5•¬%yþ{a ëF:f’p±ë{L‹Õ¾¼žÜÌ·£Ùâÿ¸ê"ÉðýÇ4‹R‰‹"$jA8d¼+…†œf,ò?ƒ¸Þ’ÄÖ´cðßc´¬f/l•£Ù+RçïW™[SÒßËu™Ûë€@¦]?bJžõ!á¿Ê ·Âš2k²£[‹ ëÍÇÝmñ ÃÊ!«¦²Mæ^~‹B³ƒ|›6`Ñ/‹ö°FÆÏ `îKÖ©k>‘S‡.·¸ïÈX"uÊ|KÀÊĆê/û‚ Öä^ùpã«Ýv™9á?—Ž.ÆËóñ¬Œ4]> „wnl=›ëýŠ9vWšlÃgýhró4˜…àßçÿê±¹‚,U¹ÅO ëN‚Æ^ü6#Š‘v-`‚ö¬F ë NnÇv7*œHg›Ÿfëι€Õ.¼›$N¼%ŠP[ú;ª„{àënÄoKçöí%gRw2-rU0<Û‘Ì¿%N¯.-W{IVcëçõÐKÖ)AC»VL•Âê‹ 8>ª$•mÀ’%N >)xsÂæË㢺¬Ð̯8±ù/lo`Òû%p,ì4Þ~¸’\Rä…´8¢Z‹k:OCÜÔjöwð*V±©¦t[“±Š*ÔßÌàß¾³ðÑDˆjÍ,„?æòäïôdæe®y±ÐOn„ŸºÊôÎÂt*éKGö;ШúL¡4ºîsý›-ÚœÅƵ‰äù1{²9O ï–n§K¯Î$r?åï+Ú»åì²àúÛÇp§]„4ª›#Ë{b‚<ьӇf‚ó9vïOÀõ•«ˆÜ÷“äûàV2ûìtªçuÛR"1QT“nØó^ݛǪº„Цü<øÑ¯ ÆÔl1Á;HÇÍ%4Jüvm<« b«G,œ›¦ÏæÌ Ú¶“å%ElÙP;zç´rï§ÐM¢4iq2¦þH¤ú¬,Y ð'Ü,œçŒ ‹Ó©§ö±"爘ª,uß{…>§ŽÄFi.7\Çt\õ¤5bW {J—!~ æÛfU:–C˜ì“TúBë gTÙ¶ÇL…éÚµPs~6yÏ‹©PXß¼OÓQY!ªê#-W‘I»àÂw%rÔçä Ï€±™Ï1ÂõÎJý‚)«9˜2ü²bè:Ç«èòñÜûrnÚT3s#Í@ÀÚ’».1¹"Told¾ÆÀÇ缤ÆÎ±s ƒÉhºå3ûþ‰•þ6ú™ò’Yû{@=.•½îu•ù¦0ÉR¢tÛÃlÇÔ4VsËfÖbáþs°I÷'%·dœŽ?_ºðTN=òÞÄÝÆÌ’YZî‹ÉFøä=†_ßë’ƒíhk¿‚ºíOÄM+`]ÛaÖÒô6ê{¹€ŸÀ|¢ThÄ1IÖ‚[|–Øøo ý÷i ‰šôW&N“Ò líßO^þvÅØ¬H´ Çøsñ„gB–V~“.24­Z’Dϳ£lZñÒ[áÍßLòþ|Ç‘Á-θâ¹~xou Ý´d1J4íÄå P¥AŒÄíþ™w˜ÝÒˆð>i¢ë)„ºK>‚óv>œ^yˆù éLÌ´¯À±É»oÅgöæð8Wê­$)×úÀ¸HJ‘)GQ8šÌ« ÀéôzÏ1úJ:`rî8¬Nýï;ryôã×u—¬¡l™ 1lÿ…|3èÅäþýnz+Cö:'"¸ØÐ§Õdù?+æÚ¹`b" 7Çä¡ä.x,ñ`ݪ“Nßö·z2 ˜é‹~²±ÐW¿„ŒŒ´°QeÓèüµ pîL(™€‰ÉeÆj.í~¸ •¥¦£¡–5œœÊG–¨{’ÿê“L$é¢GÁäê„*ŸÚÎádFøa¼Ÿù Ë®¦’Wy±Pœ§‰aJæð_g1¹µÄ&"‘|© î&ú4]>‘$—¦´Â2>S2ÿNx©0¯½…¿ZÙžŽ"6ô‹7©+šÔ D†"bñ᫯Lúój|û ÝµF›ß;5¶U8“{ñk ½3šô{ï f‡\¨Èp(kçËKç;C¹² ì«ÍÅŠ‰H²¾cwã÷ŸÍèhcé›B¹w[œ©Ùë§hg³LÏ&ÿîâv>o’Ëû®}e¾ºèчê¨àwä,èȺEñLŽ%dU{(µ _ìBU> °§/¦CN9“GÖ R+ó(’¾®YJfŸžBf%k,øK%»,È·3ÅÀ¸vbM ˜Ó׊6)1&¯Ãpï¬'È<ÝGè¥e䬜,ŒXÊ’/WªH¶ ÈŽýUǧ™ýa—0O5ƒMØ6ƒ|ÒŸÔ^Ï÷Ã"ó%tÅMR˜óÐ{BõÜÀW©¤?l&)ÉY ýóáæÐ£¦N¬Oº%žvצeøŽømTý±[E)T­p' l)xóÂ^bãWV _, `ËJU\¦ý¼`R§L!±M`CS:3G2R»ÎãHw ϦW^ùC^÷²ó‰.–±¡³š@ótkðQ$Êâ$ë³6¹ýL‹úº[¾ü„ gq°ºPÆþ×ãÙ‹;ÎŒkdá»(!òÛ;êdiËř̅S àÒ-Þè\À—2EP%rÕËgþÑçó:ãV’×[…‰áû¨öªΉ̡›HPçM>$ɪƒù¯´×š© »,S /kˆý¬¤N.üͮ˪æöß^Á~ö\Á Gí #)ß‘NñÁŽr)#›Î¾<<¿uêOýcí—ºcצ{èÊ{xÕ†M£*ý¦ðøÆ Xå« ²ÿûw9‹|ð(;Â*¨j…ÄDà‰§n2jñ_˜¢¹Û>úòø:Qû·· ³'§\€‘ö¤ý6ÉšÍa3úÓԔ͸àßXûh“GãÈ®¼õä¿9}ªŽmMƒ¬­œ“èoûXÑá!úG9¾Kî±;üb¡l¡3f§)2§åá©ÝLèŸcœáÎ,vS­m:ÂÆ ìCuîyLóËd낦“{IúxNE€lò:@fy‡Sµ¨?¢Éš˜RØ3™ öÉ]MWZýsoå&æ>ËfòjŽÂ–½e¸çU+=œ¶¡{™›Ë‹Øñ4/²W¹¯{Ò&{x\­ ý^éу^ga‹o$vÈÀ¥mxøh>þÍ*ƒz/g*mw²§Z‚ÙÁ,6!3•ýð¯’í.ŸJ8ZbTïcšÔ 0NºTPþ~‡ Ø ~÷¼éZÑÇèÍ ²ï…fÒæ¸.”\}AYÚq àiö:WÞœ²å›ÈkyuHÉ}Ϭržu»aÁ3†0^>4MßýÿzGŸ™:‰âˆN8lå&±AW™¼ä(ú‹‰i¸ÈLû¦Bµe_0ÜÄ=œ¡-³˜ÐûdH¯˜N=hNz7¶&;]Y!R¾ø˜²Ï·×3ËÿéRU¶|_`žòQl“’¦›d鼉ÆúÑQ<ù–^‹ÄÂ#'¨yžÌÓ{Ë=¤s–y; 7oÆU¿ã@²ê+li[åÁ÷ÁÀ²š‰Çætáþ›Ì…Ë¡4<â/:ežÎ ÉŸ&×ûξd‚kŽ©’˜>Ô8¤F¿kÚÂÆï³8|;ŸBÒóW°©ç)®^©MøB˜£ÂÐ¥G•žØQÍ^ò‰e½B3HŒè{æz^?óÉU½GKÝ~fþU*¨LIé“¶—ÁµÉîëУ¢•ì’¤RvîEeÆVD•Öœˆ$ó-õÈŠË'ÁŸ'‰Ø«#½‡£`½]/ì_ÚŽ‡u:ñØyšo–Yí¦ÄiÕÆ'ñÊ密-q\òC¹Ï­.ƒ†ii¡‚ sÓjúüCýá݃cûƒ oÿAºHu d6g¢kÁ dúˆWõ)Dg£çZ6Á_&…à¯}OªÐåùû ó?ôÞ@”Ó¨Ó!yô¯¹…JÎ{©å¯NÏRø´„h۬º_§@ÊzÛ¿7íì?šsˆ–A,¦*ÆÂþ7.äogÝsƒŒ!両Ý3l+fz2K7Nƒ4S.sÒ'ܤ¦—‰ðDæªâ¬éÂLóå¸ÈæâŠè·Lp@2ìVLc–ÿnaú7Ä«·sȆPè²}? šû+À;å$1p¡S‘Z®žÕtAû,Òx "ùŽ0r/mqOp8§Ò¨…-Øvž”fWdiBýâslÔŸ VÎcTå6ÃýÞ ü ÿïÏ¿‹Ó hÕªŽìAVuè,ØÚ)“ä]’¨ÎÐâƒ8¤C´¾l†c&¹Ð”³Ü­?³c/˜ê5ŽäÅ„ ¸+*3Ó¬1` E¹iobpÖö[¡ëî 좰úEîˆ2¿ù ®³a}µ„Ø·fžC3 ¤H’†«»èec úVbäv™’§"ò¤ïfÄà²Ó¯áȵßLè›ùðüægl\ŠÑ3É­·}¬‘ÿÌôÜ÷-—pløqÏ›nŽ÷`LÌp$Ù³—Ó-E£€»EÉÌÍ"ìt£Ky[X+ oŽû„'bsðLàßÈZKQÏÃ:`¾ð$kkàÌúó 'to@¸Üy&Öüã³i 9·e/ ˜¤Cº®™´ì¥ß2}–¯¸c¢g™#¾qœùŸ.ãxÞzZëìIx‚£¨\Òöï\<üÐ <­1¼]€È·ÊC9çîä˜&ìŸßgèZ%K2LµÁ:tRÛZýå\Øê \¶d\áÉÖ"(Œ<Ï<©> U ðÒY•, qÂCAË™û1àñYíÝÈNà\˜HÂõ¥ý8òaN•ˆA@ý&ªD1:¦Î*¸€÷®[pMƒÕ·+˜kødÊqÔý’ÏNˆýfÊ2Ô©ïÓô {ä¶JÓGi4[ :Œ©L^!Y~œ— ëì']+îQA_ó\œ ."q×)Šù¯Äýž/9w€±~q€FoßÇÊ[$Ð'Aj³X…0v¶ÔAÉžÎsIC÷)õ¤hé&ò{@‡rW—Q¸Ï{Ã~–wJ%}bpë¦ë¸é<µ¾IŸïÖ¢Y.ÊNåVë(ÏÔ]œ°ÎFbþ-’ØD/¦]’ƒ¸ÓhÔRA'½ Ц*Lêzª¹Z³ˆ™e,Í~¾†<),$#ºÈF›‰“ŒÚ8ZzÀ…¦¾ eD:VÑÛ­±Ï÷0l 0“§}ì@9e…^»R¯*.e¯!¥gZ!{¯¸”Rõ*øV¹™š?/c¾pòµÅ”.!eœÃÖµ-dpÕJЊOÇ›Ÿøëhy]þ»”ÀFïÙ ¶Q/ÿê¦'E¯ã±[ŒŸÙ5¼8oÛý—å´ÜK=ùð£g1 XéEîOäSÙÆBš}9FPSì >äÚ¸–?ô‰‡íÕ¬PìC¬)ÉÅ?Wwõ­ÇÈ=3B6Šà³$~ÛsÖçÂ^éÈ -‚T9~$…[ip3i ÈÆùk§R±Eð¨»ÿ«)~|í;Ãj&ç]9ìjVÁ—¾¥Ðsf:$ž !щ"$CÝß÷Æ¢–Â(”Ö.%µ‚ÏXžäqt˜Ö†7n›!÷`*ÖØA…w¬†iÂdçrIª³n­YnCíóK’×™¤Ìë¹À½åäyðYæÞg ÈÙ¸>ZºrŸzn™  UЦã»(óÔ‰ ÌÓ€“çê!¢eœ‘1û mÞVäro5»Ð÷êßÉ{üt®^a¿Ïn!Ì¶ÙÆ„›­"V¿WÁ5»©¿±–Î,g?»/FáòH0úRIŒ§ÆB˜xØ8Ó…ï`ÆvºÊzü…´å é‘:HŒ£¯êÑu6Èý¦Gƒ…ý‰Çû-.JoŸ¡¾Û×Óì3!të»1ü.O ?. ²»Vm!¶£R 4zýCv:†úaSÌyx¸NŒµ`ÌA6§Èñш¯Õ웨àa@MôäÈÙˆW¨å,K}-á––ð*ÊÆcòÚ8]ƈ»mó œ¾ÓN–¥ÜÚXëÂ,ifÎûÙBÆLX奫SÉþ²à+‹Û?ô•ì ¨./a·«d€º¦¡ø ù£HáÞaºì ¥nédÉH-‰YàOçþ3Öª™µÿ"iÑM@ÞíyŽŠ2õ´«© .|“ ÃB7^‡>€_ÁçˆËØ|”?½²£7W©Òáe#XûðÙ¥9ïsè3Žn”Þ¬v!k¾¡ÏÚfÓùýL@¶" ŠœOtÌKÉà½fp¹õ† ÈÌ Î[ñBÃMp[Ÿ9ÛæÑGÕ‰ÙÃï 0qžv~K¥× ÃsóXrñƒ}RúÆËé^ãÝtzñ rïl,N‘´)Ž|NÁ׬6)><„«x&ÈÙ¥ÔüF¼ÄPŠ–%'’ŸáA“(&pÃO¼\¯%´‰uºŠ?(ÄÒÄìÄ«|2Õà-ŽÉ^dŽØ&ƒø1Ç–ªkÜðiNèÛ·‚ª½Æ"‡`§~"yçv„ïMÅ•¼fôƪ솸,´{| V· A·ö\²gEî=; ï»Ú€J¢4qZAæ—`’mÖ“q|´|áb²M3€~R¹ >ë"1÷MbTP“'µRX—ºU̶ŒœÈŦØu4cî\zÊÊ‚¾¯~ÕÌ0æ[ gÒN,»wµŸ‡¸ð êöÄnŠÒ'åO}'¹¼)ë²…•® IÁž16 \ž™y|;H®£3áQMgùž¼fÌì®áŽ:Xpï]î†Lë>¼_¬ˆ"å‰ämƒ2êö"žòµ6¡•øü]ž@–ˆ“¥ýµÜœÖböÕCÒvô î[‘‹ãÛ3Øœ7e¤K¸˜ü{ÿ£œû!çÃJ ~c„ð°¦7½lþ”ÎR°ö>*Tô‚\ï²Ëÿ5÷êçjL85vÿ3f¯9gÓ¯º4DøîÛÿÚ÷HP˜N]œÀ½ Ÿ1RÔ ëû$™h7CÊ ƒàx<'ò¹xIïc„§ã,—‡lî‚*â´q*®ZMB{øè[é(ôòâ§úÛ©'”÷²‘[} ƾճcV¸ÍDXÿ¨„Ð öØ p šç~€Í«á®h.³ÿâÙØüÑç§éÇÞ²u'íè#‰Læ°ñ|êù½ŒÅê:¨ü¬Fô‚ûazÂ\ì–‹wsh«ôM8Ùóæ«ƒû„: ømAÿgøŒ RÈÅ-pf¤:m¯ÀÕÿfZEº„l èE6ã|ˆ®DÉï1XY/LõYæF…$U:)N]ÊBà¶ô!øþ`÷R¼™¨‹&ñY e³ÊðŒæ tKJä$ßIE×R16µ‚•vü„†c°àÍ-Fü£:Yuø&ñŸ‡ßwï2-ó'@¦h’»8›ÓÝ ß0;gÍ£mçâÑ7™$$Û ãç±_j˜ÿ£ ¼õñ•…9•t{ÇüÑ´häïš¾ »ïMjŽ ZÁÝÿï³Sýtœê„6$Ðò¾gÜeœ“lÂbL›äÓ¦ÿ¾ƒØEb1w%<“½ûç@ݬA”<@êz$ûWßqîÆc˜kØ3Oç!jÏ£@ô®nåIÀ¦õ#ø~†õTëñÂäÎcr[‰Ÿò–¾ÂË\¢ú×4§¶ÑÌ—nd‡¹ yeL—8Ρ‹/÷ÒyÕw!gæIʾùø´©]±54ÎÃ4k0^Ãþ|}Šªø‘È{¶TÁó\xê8ÿô>¨ ';÷ÖC[Áu2߱ޕpA áôJ=U q%úú›é°û3xôbéW8B~LòÐh;Xò¾žŒÓ$ê]•ý9‚ 6Aya |yð>N[ ÄžÔ¦QU9Ä\œÅ'[D¨xZåý ç^†ÑJÁ±y ÝöØT-øŸ êâ×í"ôO(¾­W¡ºs²q®Àw¸½‡<¹`‚iW…Ⱦ™çÀft õÿ¡Â†¾x(åØXʰ§“§Òq­àÙä‹^w‹˜œpYRYéL9²°Ëi6YkGûó‡ÿÌ›Ì7ž.ELÂ2eℇª/m[[ê~© ÌEœð¦E hšD²žF5å!phýÈ”5ƒ°ÉÌÞŠ§8ëŒçZÄvßv)êçÈô- g-Ò˜áÃ]Çáê»BàM±$ûlÆ:m}á§O%]“`tºÚZÅ‚WŸ^ÛòÛ‡çâÈ™ÌFÿŸ»PcË&vƒYüÕ…XXäãâº8vÓn~Rù$&¶½Dýß ;ô',,LÀÑ£©Õº o:jãÆ% ìå@9l¥¾´H6QùªA¼e³S[{!.—Hø/¤vwŽžS¡VØ/„5Byè-'Ltrý˜nk`{¦ÒPOK"}þ+×dÞ6Ð0(Ä*™h Gæ§–bûˆ#r5;áHw*Y%Ü„Ãõˆñ+œš,J\ ¾q4g«a¹‹¯½v°ÖEÖ$¾n‰ï™Mž©ÓG[–ï„#¯ ñêÁPshã½ÕÀì7?À?Rˆ =,„K…!fG  9 ¹¬Jž™˜Z8¡Ï¥ÁhrnãŠlÜé'Œµ–3­Uú%[A³þ7®ýäDÿàÉU§ðdL¹ªÈdúcÏ‘yàœù #F¼I²ÒŒ¾XÑú[àMo.Çû¦/±oc>¥·q¾ }ćÝ7°ï?·Ã-Ÿ9¿Þ3*71ª¤ *lHû[lÐ ;ÉÇuˆqb·kîdü^q –l'G$œ¯ÛžðÇ«Ò-'­q¶•Îܳ••»•1‹Ë†R{¨û¸&à_ ýI ToÆEø:çôgá§™uð®ë îò˜Ž·wIO£=Q´©MYã‡Õ½PU; ­Ö>@vOf¬ dÈÈQGÂ;Ûfkð‰šHÈïv Ë£ùMÆôîè¬=J€ýE~0%ÿ楰 ß`ë =Úsϧxâ#›vxÿ°†m:yÖu³WïÎ'MŸO‰&C¼´-ùü­ŠÎøàë `ö‡Yœ ³jz«EÙ1á+®\eœŽä`ì=9ùAkÔFìßýUÌ#ÓvÑš·Ga¤Agìc‡¥Áóþðn—&MÜF³_&µ ?.Œ©`¶nÊÄMkŸAA‚15??„wŠÞàê¡› ÆþØ:dàˆ=Gý;›Ž%£·Ú!xÀ§Ïª½¿í¡lì ªÒ¿Û3¹­ò ²eŠ£'z<»áœ¸µË[ê%é¢õZ¤²©~äöÀAP± †²zØ|bèŽ cÊc2hQ‰«™K̃“ ‰–£1½pÖ KÄS1•f°Sbº9„i¬ÇÓ_¥‰ï·Í8]mŽ?ÎE¹;Ҁ׎Ⴙï™_Îé´oç˜U÷‡.ï#6+| y¿²“³qÉÌVÖ ¢ ߆^3—ù´på¤ÜóAI1«Þb ÿ¿Eë/ÌØ‘[ ýæp9ž'ÀmŸ¾œ#ŠIü¿ðSL‘,€jž p¹À>’™Ã¼µ‚ìžðè”>vÓmœÇoëaàÞ($j3pâe;~¶vA§>¸ÝÖg‡S˜‘™ïàÊð §ò¿t}ã3ÑýŒ6Y¤%÷.*ߟOàçÚ5Œºœ6a;^~ ^]ÇvëÝÀsñ lßw„kÙ+Lÿq1þn,»0¯3—ز'"N€µ„ ní´àhòÓæúO¬ÉÒµ¤ à%žUØkW3·í©õëåô7Z1j­ÆLø¥õd¨—Ÿñ—Ã’vÝÛmÍE(hêãë)†Fä@Šmk>CG#ôhܪQÜö´d|Ú­æ[o»"àŸ£M4_8àÔM+q~µ1½ý”—ôk¼fÞ þ@™K¤è0h…˜Á¶ÍÐjÊ‹y7]ðVd=,|øx› ‰Üû8íCfqøð`÷zëÃF9–Aìþ£ð銟õ'¿›«Ù‰«ë ob:¾c ÔÏbãP).­Õ!¯SDèÑó*ª™»Å‘8µæFÒNIøtËYÐÞ=–íÃ2ëhàÛBÖ,- Ÿ’áü=2ù¬¼‚¨àÉç06e&/¿WnVq’ÏX‘?§³©›ó0‘gý+¾ÂŒ¶hÖßì:nyõ‚3:{3–tîÀ}*|ôüð{öØû$ºÙ¢‚ý%üŽi<Ì=Ǹ.Iž¬÷– s¦7aÍ" Rõu:ðìkÁãQÌÖû)0ãi%<8ü™u|h@­¬ÿ±Ü‡«È´GátÁª3̹«QxÊ rzˆâŒ,÷#ÜI-eÒöä±JÛhÙ0ðp g‹ãáZçEXñn*®_„"sì¼÷ žõSØ>ÂÌ­Ž`•ÎïÇÉÉ8¿é"ã½7ÞõàO!r¯à4<*O¤aƒÌ㌰a"®œ¿ [V2ŒïLêüÁ”Ø”cz›7Cü8™2™«zš5`¼N ~l²Án? ð˜«d‘³ãwàZh0>Û¢L©8}äÕ1áøK¶Vÿ7{¤ûˆ„¸‚‚â*ô²Mg3÷ÙÐÄÇzPõnˆzö¸ØGáG?Üš˜‚§õÈ’5­°öÏ"ʸ¹Ò5ó©’Ë çý^y0Ö"gaKšª'ìõT„ ¸o5·MƒE«`Jñf½݋ެݞÖr“>iõ£N¦#ŽEKéŠ/ŽÄ¬t&–³K×g¡ò²2ü`«ˆƒÇ‚“膰ë½ëõøì™þ”\K~Ãê¿Føù,9´èkPôÕ!Y˜¡ŸjàÚú ;u¦ÿð8ñfNÆ(á‡aºuBùóâáU‰"vñ Clúa¶<þ)&Ž¡ãÚã¥^Ýy…9¼ö.Ä][CM"æPSmú¬5š˜/•${³ѧՓH§ÄoìXî> ·ÃeeMÒë¥IZÅA{Û0òG’²UÛQlÖVòY• ÉWÐ>a3…›nG ûñzÖ¯Þ¼ñ@–îªb²y’°Ëû"jØ™Oòª¼É=0©›ï…¥ÆDп»]W12¦Ñøý™6éþi„‘g™U¶Btåw¶B=ù?vã£cÚœ—cq º!4—–ÁàFÊf__ ñWnÒ©ó0Ùi¹åÈî¨ ¾¼^8ÿ¸9t¯ñ-xÿ{Îæ= zI¸õ*žoK{WŸF\ &· 9‘‡Àdu/yÏ'R_HdŒÆàÏ»ï™ë‰þ$(k å”éÒbãXÒ“¿Œ„èÍ5“¾š¹y©pv¥ü±LÇ/×ºØØË˜¯óÒØnò´f$‰9à 㤜ưËu=º=í:³!ús}Z ãõh=Ê$ÁõVÂ?e6Ô¨øŸ²E ¸Ao¥Ÿ‚¢cÌûOçÙ{E¼‚LÞ/NĽŒÁ'>2Fw€}æyæþŽÏ8è㻿Õ1j•P#zY+TÑ%à"~–¦âdI{*VòÜ`Ž•3ðî³Õݶ”•°üÅÒÈfÖlI|Ù²g]6eÕì{à¢Ç”Õù ]g‹‘›X„)O×°f3 P?©O›ƒß N³Oøµ®+»ŸE¹ï]8s´V2[t¥™éîè¾/˜N!ÔïX ÿÙiøÅ˜DzØbõÓìó-¿J¶Ï_œœ߀qVõ(¶¦“Ù?ç0$ʱÇÄ®7‘ù¹}1f\ÁÒëïq¡`#ÔìÁ ñ´†y7í!ì•$½ÿæ/Ž×aß*;rÏ å­²0°êäN¿ŠŸ¦+ÐÞ&$Nñ:níOÆpe+Lç~6-a4Þø‘ÿ þ“ ãO®3jÙ&ÀÇ®`v¾}ŠMAØÅ'z ê/=â¦C| «XQþ1–·~µNfkÔ§@àÏd¬~ ë¦Ðï&Ô+à(J¦9:LÞ Î^ÆÌ½‹I{ö6,Ù’Ã>ÔI€‡)ñôyd${ñ¯ƒ„·±áç÷O2Ô3*™üþÕŒ{§ÓìƒÛ™½Žï™[êÏ8ý…/Q7¢ F€¯õ7ø%«EäÚ­hZF&+¿á WÐ}'~†ËÚâ1~õ6<ãó÷¯Å•Ö–tSÄ03{<éa†šÖ ‘•c¹djTȺgQ»ìW  ýùzÊ•-Ð {Ã>q×?oIê¯9ÆXNÿÆr…½×´Š”îùÏ<èÚ¤fxBŠ™Ú>k2µ°’Ýa%Aü¸Bdop1†I “š ŠZ|ÆßÊfHê‚ü_+òý¥:YÅñÅ&kºYk&)¹¶‹t~á!+ÆVRǸ+,Æj¹ò¹Ð Ðå¶NÜÀ«‰ ¤!ò,Èéûâ¸Ì4ø3XÉx$YãÞ$oöûð0£_K!`Îkˆœpç¾ZÒ6Þ„„KáCo2_=†1+#±Öß°Ó|%h¬~kCÛןi/GÅè@&å¼þƒl’ÑQv^õ¥¡ÜQAœÐ°€ƒlžà}¬\Å:µbÕÂ>œÑùÃ.ßcÏ)ã‘»&ô»Ë&]’Aóµ8±F†OÇ(õP6oB–™Ê¹+$ŽÀËïëA¡ã!ÛnKcþ5¢®d»ÍY‚xÕ¿Æš×Et|±4® J² h¸¥?ÌSe#·ú™Çññþ#¸5OµÈÓ]ÉLÒãó °ê5®*™Éæ” ¢Âë‡[Gô!øïg8·¢œñÙ:…dgKÇP¸©C…Éé©ðÄ̇(÷.'5Bô0©òy¯b`ÓªrèÒndÖì "#KÀh‡¹«x¦²s9ãb±¸©‡¿®<Ì{E‘ÛÉt¤ ‘mFsˆW¨0êëʰâ^Ž ã®NÜY®ã¶·|äªB(,ÙiA9+¥¹)ÝÆt±Äî±KðÝ¡&4—|ʺîgèÈ=ÆûK,”ÏÜ ÇÓ‚ˆÌH$ÞÏ¿ÁF”Á¨•)èÅ ÂíÇѯàãz-níÁ%ýX1ç¬q4&Þu/ñNs*øƒ*yzRžG,…¥Ê@–Ç®‰ü?è¼í0X-ÓÇÓ¨ׇŸÿˆ8ÝÊÍ.K’Ò„º)]@ßÖôQël)gʲûÖ-%ÝrÉ$HÅÏ<¨§×7%ÓãÊdnŽ6ù•X‹{4èéŒEPªŠË  Þná†k“›•Gé¼?Bз¶˜Ý™r‘õl}ËÎU%*²%Äo®¾)¡³>>Àéû¯³ÛïÜ„ÇIu(lýçvße\§ sæ‰k6H{c@^Ý üï€/O—|µO#asò'ìSÛ2EnÛÈi#ýw‰lÙ`LueDéOå1æˆñ ›ÚÒ Sÿ©Ðå{í‰ß»|lñ*îÏ$ tLHåX4ØùÀ§ÏcºH»¿m1q½¼,v÷àÛ¡dÈ[k‚K‰ê»›ȽÃL-æüÉÆ9¡õŒ@ómäÕCPž?›(߯GG“ÉžŒTfM¼¬oˆ 7M£»SáYÔŽ•FÒêÅ-«OþÙ"$Qþ·íÛWv÷þCìò}}hÒìA«Æ àË :]†ü~Í¡7'ð¥ìA>[3åé»­òìRW¦Ôr6ŠÊa[œG`…º39—v†ûu³+Y¸À 5•m!ààU6d£Óyó ,­+`3—äí¼ÄNƒPËc¸ÄbœQxòkã ë8ùú{’ó0«ÈG|¨–#E,i®ÏvâØyŠöÛN ¤€;þz. 4´‚X™o¡õ|ÿK7ÿdWÍ_OÖØ^‚ Ñ âõܜ̋<´ïÝL¼hG¹—:ày 2ýb£L*ecé.÷[hrAˆ,_X®ç§À“ñA4¸DjöÑgæoñeˆ;;ÿÚ)èðÞLÒ—W#¯?C~œNªû^0E Z4Sq5¶žÆÎÞùwï¡üöOŒQÓ´é+d*N/"®—t‰«Iz­ngä*áä„mƒi‰‰q§S¾Á®Šç°Å°Ù¿˜!û¶gCãckú)G#y›1Ó7…zÙ·À·Ly˜’/~2/\ŸCªÛGô5!oæÁ­=”‰ú‹b+­ññŽVîçOQpPûÊ7ãºsE(UÃÅYófJ1Y:’%ÅDý:Ȭ^Ñ…¢û¨í&!:¾þ5óøN=þ²æ¥'VgR_Ñzo\†”îgÌöìDÖHÇ:±<ÄŒ,}y7òÐyïíqû^Cô©§3á’P.Zß2%{&ÐàÀ‡øòKüÍp%2:PnsOqûðž‡!=ùa”ݰ›–oµ Û^ì¥Ùî§9•Ïk`þ¡uÜÍÓò©t…[¹å|zí)/žÀqjºÂCžgßZ™¯A•‡.PŸæÙ ŒVÑêÞ"0Mÿ MÍ“±€ÿ'jû•Ñ9‹ˆÇ®çŽe›¿á›0r«ñ'œÓ‰G©Aü›[Ê’ñqØxÅÛž}(|íIú»Ù`#ixxT{jÐñ€Xzç?Ù*YDž_OÛÌ!]nX£`DÙÎýpŠCV\Cµ2cعV M‹CHê¡ßR¢¶Ûžî8®‡/7§ƒƒç-‹]F3—ávñ¸je´k£æ>ܯ‚j–ò½Ì;y°}â÷aéŲ»0JìX¸2¡ÛUÈ–†E°« à_)\ÕžC6Œ­&²€ñ^p‘õ³wˆ‹ ñŸ©Í4Ïvg–ñ“°Ý‚¬CÞz»ÁGsÖ¹ÓæÝØ\aà)T¡ßìè%WKºàйyÊžÏãòÍwÀÞà©ô’Í#ÈÓÏÀ»gaí!]è8…EA5`Ý>«O¥aÌ´Šó¨zó$Än,eÆ“ì!È`IÞ0¼;z+n‚“š+µG>ú@$Ù¾T5Vyá¼Ë½¬Þ=¬–Îàjº¨ APˆ2ûðÄ=GvsQü8\ˆcÕAfÕ¤oîIcg­ ›ü`ï 1ØÀÛN¾âÔÞÊ)ƒ§^OÀÐÿ (T =~ !½H¼NíãLŽ?d³à`Ðòe¡Äîl¼{¯àƒÉsfª !&ÙÚbÉ&È•%ª.²è™âƒmÈü3ÍÅUå§!«ëò»&o)dÞÛÎ}ÄëgÑ%Íß°´Z8ªðÀöà+—‰üÓƒy÷BXûpU$¹Ë!sBœ2W‡Ëã0QÉü¨_LÕKãXþE*X<ý“µ,î¼æÃ_a—ñSq˺ñ“¥ <ø$aié‹…÷sŒ¨L¾Š?>Œ¶—6ækÝ‚?c3°&·ùß~A“F*q¬L·3º0ŠO…Ú1ýÇÄT‚YªeE8nf4Tú( ÿÑbžh=Á5§;8M͇iPdUßd…%ÊDiPÜ5¯ƒ&ÝJ»D!CǾv–â^ØÉ ãùè'·ðl§Rw­ˆÓÍß\d–þëHg aw¦ítTÅ™ìHA>¸ÝÙN¯¾S€`M6’?“Ixujú–±¿öòÁ¥ æÄù·ñšäî·F’±Ð‡»†ï£œ•0IªÝFõîhÒ”Èu„ÉLæø~5‚N‘åPE¿às9-ÚzÊžýTqÏì´":Kå0o¸Šª‰ÙvmrúgZÕG›7¯ðô»&X굃·‚}v ÇÑ܉\Ñ”€úµwÙ5sƒØ¿Où1–÷4D|!b·Ñy<®l©ì<²·8˜_7Ê•ùPÆN+ðñóï`‡eÔûWpÚm …¯w0ó×ÁÙ­b¤mßeÖ`ÅAvÆŽÅøQ7’ü|ïI3[Wmï\*ö'‹íH2Fý9$,̆(n[Ä<ìL´_‘œ¬ìHGGùç¸æT9”§.ÁJÿRÌAy¥edû­íÌ®@¶vƒÎ:p ƒí¸ûÎM-ƒÑ ªx?Ä•Ls‡êµó eJ#+=½Ferÿbì—Ê µ¢9àŽKè³9iôÑ-/bæaÃ,ÜG—®Þ vÞ%Dȼlü½8ßäÉ(ï?Hò~‚Ê;µY—EôZ€êœv"Ûb+fkïà„_çG¢àÆ÷8y›ôƒ³ï3í§‚`pÀˆN¸ÓÑcj´"@ŸüšÍGÝ;.î~e8?¼˜ñq7Å\+W"µøsºEŽc•ŰƇðrzù§Ãã¹&h_®ON˜®¹ªmøRs&ÖÚRÕj5b¦ÉbQÔv7 Õg›PÞTšŠÁ±©‚d¾ôtVDÓ‚ngƒQ¨æ7øl¤\)¢n<GgÃzÁÜceÇàýNMØ“ÝÍ®ª1!ƒÚ[˜½Y5]ˆìz¾ u>ßgvÊ%˱û¨à;µçåé£Sé8øÅe¸â´¦RVJì㤘²ÈI>Ù“NsóѵwðJŽb¨ÖLÚH¾ÇñòäÎŒ˜¼‘ŸqW’µÝ`¤J®˜f²#éBðô‘nsȇ+6•ðz½$ûÞ|œ÷·d3$ẪLÆqS1r «Ó÷‚Äj<ü>Äg@eõu¼h[€ ZkáPå5ÿ‹‡¾šÓ³OsµO=CŸ£o™ãæáXÇCOu²VÆ¥ŒŽñO¼yv> äAyAyvNª,ù~”z?ã%9qÌÕer$)6‰Éw‚B‡Øô_ŸQýk1éý¤=9®ëTUï„€¼¼46&´@€‚Ý™x²qÉÊåÐ#q¾n0G¿° X9}6™ØøºW:@ùÝh8UéHÕ~èP{_9R+ïÀŒ1×baø”¶Nîý=# ˜dFwÏÒpŒ±îÁÀ‚I.þr9’ÇY¤Jc„{厤}é¯Û+Ï À«í¡àpC˜&!ü6# ÷Û4ˆbäIŠ,èÄ€L|ñu:¨N,£ â¯ÐÄ´„,K¹Áï9ÜçŠRàiÄ<2®c%³éÜ%yØj&Í^²×"¡Á3ðŸ¢-•«†=5ã˜ù'™ôÉzâ€-/™©u©ÖbÖÊF…ü>öIR÷º{ÐÔmv'@æÍmÖèñ’&@:rèJù ÜÌé®düšÛªVÏ þ# Ç|œ˜ÊGHñ»z™=‰n©‰ðÍc;óÑá8ÎÝxA^–.­tbVÔŠAN<ÞR!s+03ý:¸z.rüdwyè²T<÷ï+ˆ¼“ìÙ4ùC%¤ßâ%7j’¸Wëø@êÃ+8ôî6·ÈCÌ“y·ûÍ)/ß9ÖBMÍæA–0º ‚^SGVæ˜4[\÷Ò)¦ˆW¦“&Df399f5Fn¤#å:K{î9؆ðãÇûãl޽¤õ®¸–2›tÉ}s-üê!è½ÛÅÖä6¡Ÿ¹œíŽ…çëuðìx2¸6¢OÊ G¶¬.•e2_¡C¯])2Âî÷«bùwxY#ê7í!÷Ï¢õ(Ìc[–ЪÊPÞ ïvLÅÓ뾡éâ|XÄ¡+‹fb}F?ç‹÷ñI½«M.ŸžGò¢Û‚é$çõUºJ,šæ™ü GfÌ£MµýlSÔNô·%ܰFÌ}˜¶ûdÆÏËÌȼhÂ?–í–È©‹άDúO {#EÚDÒô‚|,4Hæ)[êµË˜U>| ò½¶ç‚@ÐùË2Êüô… ‡žÛÚÎñíö¢s'µç­ñÃDqE ÝT8>N§n'0|@Œ$ý9OåJ2/d òB;öâž(˜÷ò+~VË%§Œuèt~/¢yû<:.r$ïLÎ’¨5bzQ<Û“LrÕÏÓÂó ¨^l&Ùžsœ-oÉâpg€˜¦+ºNBÅT t+9ÇÅïÚ|Dì².Ñ{>‹l.lÃow/ÑÅŸiWo}±ÿ2«½ù |­SùþMÐ6ö¾.Ö •a ]‹–=€ÚMËiPÚæóE%’+¥ˆxë)ò 1‡³rfuñe ¼Ü_&¢<ôºa •m•ƒÓÅA€Z±dr|fæ_„}dïøS¯WÁJ{KßaB’Nþc¿à`ÅÕµÄq™´¹í„¶5Ä/Zžd·L¡Žz°Zýø*ìÂÕ_“/Ñ$¦| Ù”œóëí*ÒT¨ÊoÃe7Cbö¹)btªà fåEU8™‘…}±DªØ/YĹÿîyCÀe5¸~,(Å/wLàÏß3ðÙz5NÞOåû¢È‚éû™‡q™øòáB/<Ë<Ñá%É™_°ã^&¼ˆ'*¼]pk4;¯²˜ïi íÒéŒNDèŘqüò¿0lﺴð+n_’ƒã%Yƒö$jT“pÛ’©é.MŸØó×åIK–.(O¦ËÎ}g™p-iG×[æLzˆ!»g/šVÀ›‰"ì–Àm_•ÈÈebcF¾úÆÌ1èÆ=>ŠÌ§&5¸Á+‡ŒñYËÊyýC™¯F¸~×ÖmΔʙÝk‰û_rf¶ hÇéOæÃÝL¨X“»c”Ƚ+¸¦þ9Fkd’ 'yÂÛn¶[wÂÏ%ïØàØN olñìÝ`/¥Iï‹‚ç×>V^Vœ²àMãÔt_Ë£C]@¯ºÃCŸÀŽë¡rb!Õõ¬jx;í#ΚÂJç;²Âo§Ð5kþrŽòÜc‚ÖîE#uìQ¦”Æ5ÆJè°íê? ÂŽ‚«xPZ—œÜaí@cú±sR/ü磫SèuÁ ²pÏx¹êxME=>òŠÞåð3«é]µã`.â‡[—6lŽ/D1"]•ç¨Á_}zÀŒ¤í'‰üW!é%/ù gÏ´¢GG.û«ûÄú+SSÇ…LΜwly{!ñ0y²‡ôȇKµàSùô'plZ%ýôe¾ 4ÙÓ›ä¤gà¿Im|QކY&™çÑ0׌Öt÷s˜ÕÑêÎ:bgó††q'Ž¢†„=9ñRVo§rîN¤I4†]┋K3v‚Þ‡jPÚI­zºAD-ŸÈ‹KÓ‹ÐO½žŒÖÄfà6m©ºÆI-¢}†¬˜ÞsVÝ­ ï ±ñ^Daø<[âüOZT£.Ÿ5èžJuB×Boò¾ >kÜg³ª!ÿÕ&ç¢c¶9‘áFyUÍɉ·ü43¬~n¸„só4óþ%Cv¥íöh˜Þ I†Dh}‚y<„eãNÔîÜIXšc@³'dˆ¨ —I€H~æ±ø/ôÙ’‰ÛÕ!LA]ÓOO¡Ïå–ÑÐÛGðÕߣøi'Z¾Š¥FQÙðå:翺hœÒbÈöDêQI±ïL¯S^cÞÚ+Ì»à^Fñž,Ùòñª–iŽâÂûúÔB`ä¯bªýo· pú8#˜©Dö˜ ™Fj _âÞ%rsZ8ðiøž JÞ§=„jÆ’ý¯&{ÒF¬†fx wõõ0C?j0³HÌåü~›gûщSËòh/1âþ´O€1™³pbá\ûºT«ÑÊ xÚøbÇ€uYt&‹Yøl>ƒiÈSëJ µÃM£\W8Ää¾èǾ޿lþxÚÄË㔆Ax_½‡¾Q¡iWÿ„U„ªúúM 7ýØ_Nþ#]¼⡢²Úx¥hIwÊacîÁózqØ Cìn°œ3gíȆý÷¡Ê  d’ìÿ•¥JZ£øÇ¿€16ÁœƒÉ$òk$Ý×£CöäÁ¶÷˜nÁOÎkžÄÝf‡áÝ,)’lðžÆÄÀ¥CAÌÿá¿ÿf f$û¢ÆçJ— «yÜ8Œ‡—b²3R¸rÇÐQo;;j}5flf-‚ÐE!ž6o?Š·ÄQ›YŠiv¥zŒQY—U–+ÈÛ[3ñÁ?W2†ðdÐVð‹­D†~egíûŽi+8°ùpêô{2ΧV‚˜H)ÇÆh ‹‘"îG84`HŠì~/@æôäáæÍûXMë,pH-£&–óøËz÷ ?¿Q!^e!£‚å™}ˆ L«fû¹£œ«Ÿ1³½Ší]æœ^lëÕbýŒé·›ððŽï¸V8”a¢â1om,þþº[ËñVÃÛ6P€žé™ðìm®<ñ5ªÀǯ+è}²žÜßž µ¤ r?ñÓÝŽ“I•ò¿÷%×[CdL7 ÄÒØë]ŒXU0­Ø«Êî;ËC¥Ü>-Ê[’ÊkäÉ#ð/ÇÈT£É5:Bæó\ãöÚN¥òòtÖ"yÈ}˜Hôš%Ø¿VETˆ/Ÿ­ž”€K÷j“¦(ÊF reä;¡.NÖêóP'Ç¿F9'AáÝ[ά¢b8™Ø;™˜é¶…Õsz Bø¥ãœ®I¦žØ° I'™KâA´óñº‘uP%¿Úä)M¿a9wÑáã ° ×'§îK‘ž8k:ЯJ>L€åj\¿ÿëÿÕ<¼ù°ñm,%jàái@¦M5j 9æ‘lsûù•|+J™áiô«­#ÞnGÞ+L’ý‘h=‘9Ìð”xN^ô*pl¹†³fYRa?vó£öýüSàvn t›hbú÷aX‘þœuOŸ†õúk«iOmÄìQ ÌJ¢Hº$`÷¾#l}| Úþ=Œ‡lƘÿjž§Ï™J÷ŠàõI]òøÝÿÉ£ñ‹mpÿ`ÞÉu'fó¸Û± W ÉSKeàÔ¶ݰÇ#{äAåÂ(»kéqÐ}é7}~–o/–š›Òq?Ü„ò*˜p™kÑ]þê&zâÄC#¨nÐF—‚^tʪÀ; ƒQÄžÅ;G¦õÑyd¾¶:õJ»&GÐó=Ðø˜8/·b6žéÇ«6!𵤇Ü:Éu÷zÒ²\ÆdÝiX¨èO§Î2ÃúI$i½)~Ú~¸Øï‘}ûõ§5ƒ©ßÌõ•˜žWÅXŸ%?žàí׎Ìõbì„2ÿÿö—Y(»öª9s«3~Î>ÎÖˆŽÿึK¹£ßÿ]†NןÌÎÓ ì˜Q ^®‚q‰ep(û¬ÕÓÅË ±>í “¾ú r§ZÑ”zUÊ—nÏ.®yßùÛÁ|fÛ§lήmß9ÃÃÐ2s[_¬Åœ#xõÝ"ºUô4÷£3i 1ú•ó…i  #sT ˜>>ëkä0<ŒõŸVøH/ƒÕ1Bs8˜›”Žw·HÓ¦»™¬Xíqp¾Àù³/ßîz B"â±]ì#T´ŠÇ–8êœ\DØ—Õ¨µ“‹*ÍØJËmÿÃ/”Ù Å» Ùæ‰Nðq9…-âÙèðj/S¬aI¬ï- Ý*h°Á„é\€õõ÷ÐÀë(ës¨ø;Ùñr=òäº2 ˜­Œk —£Ö%\wð;+éW‰¡¥/Á¬2î/ÃÇš¤dÛgfô`Æ4ªãMW'Zá9Ž{wã™eøIÆ×Z0ËïÉQÛ &×úqvFÉ;fFdåMšv9 6ðeP¯u8eº1cü°‡QþÁOˆã] 9°•‚23NJɾìÆ7Mðõi $ñwc1²îÄó{°Í^:ôí2Gˆí£¥Óÿß-³Þº BÊ™ørS Нc™šÜPÖ`{ œ,Õ¤ú *1^»ˆñªÑ%&3EHŸª1uAád'ZÉ|›¥c°Ü¾DnˆÐ³{=±µ‡Ì°ÇØGµ ?e3;7ô0uWyÎ)KÁ“ªñðüLÓ·CoÿˆÉ}ÚrɃñj&õáè¬dø é@Ìo‚§ß3œ³l!Ñ|»:r®°£/{`cÕZ¢Þ¹ÙhlJõ¿ž½£R˜6< ¢» vÀ;]¼ýá›'Tqaªä×›;P¾SMgbÙò"f‰Å ¶-ÄŽŽäÅão—ý/þ->ÀÇŒOK˜vŒ×mEΛéØlv“Ê[D¾ΔV¡ˆn·=[€ÞñÓâ\WSdJCaÉ¢\¼½Á€úM{Îæ­ƒïêç!tM¦ã‘«ñmôTl“ÿ5=öt ÏYŽËÅ<޽@ZNò¸N§fÝ«Xœð7ag|¹ ú"T2Ù’<¸\ˆÊJ¸ø@§â{5ÌYæJ\E}ÈÒq~Ú`”‹ƒ—pãé)ÄDñL[¨‰>Ú4|Î_XÖ§DCv:ê±çßÈáÄJž¬Y´”/¿<>å¼SéÁÅçðïT[÷~”müþÍQÿؾã»`³aoètÀ½)/¡wYáÁCo «€báiöWw Ú¬€œzÜû“Q‘äÐJ´ïàuȾâñã¨MþQ¶ºÐ„]ç<G•¤ ñ±+è˜ys¨™};½ëT´œ‚Ç÷lÈbéfVÇÖ—ˆ9€¹Ë´‰û4z17æÂ6 W™€—ZðÊóZ°Ö/Âc»ÃÕŒä±Nö…ax'JÓ­‰"°o²ã³š¨|§›^{âfØ5‡œO cçO½Î¤ý•¢?ê ñ?¿_É“NC8²i:ÙyËù¶îø¿Ü3Gq“LêRï·R¹ÞF-˜„}¬šÞœÝÎK ýÖ0uZ»™“kðx$y6ç4hñh²³÷6‚ŢɸÚQÑ6¶PÜ1¶zf@yù3Îþ ž°¾¨ÐÊ5™b  Dû6oue—ž‡°z><*ã‹y៘‹b|Xqü,$m˜CæB¼Í‚ð»ÕPûÞkÎlb﯌Áõ<«Çë†ôËBC°]g‰Û:Âñçw;œ'çÂzâÁXœ0Q¸#j…ë^éÀ^;Ô{É­Z üz’ÞS…9)[áËŽ.ˆ˜J†[¤þçÿ2ç3!OŶì=Ëõ¯îG›7¶§]o—ÉÄÆÓ~°^HõË >üœŒë×fÁò½1x"ß—+w> Ѐ~ëÏ`eŒÑ‹¹±Ü² =öÈ¥>¦.W%7.æb/õ>î¨+…¿x^"Ã'Ós‚a½1?u£ïa %_0%™îL÷–iDp×*#9“Í»Ä}2ÍzZÓï–1¸¹´nu†‘õ—„áç%|óÛäó+ã*Êœƒn¶(õܲó9GòÀ¢—àVs:„Y±©ù&¸Õ|LÓ$—jUÁæëâ3u×ÿðóåLÃÂÓÀý`?ÜëVgWÆ\kx–¥¸P:ãyÙ0 ¤_†nõ"ŒüCV3B”ˆƒcfêeÄá¨Ò"hPT…¬eCÆ^C¹Õ3˜EjÐý‡%þWÏ<Î%7ýb/p0¨e•¾ù ;nßbuÄá1m˜~åú}OCƒ›Šdõ«¬UA‰l’Ú#Kž~…(· ì‘Ü‚ûèÓàÍt§ö+¶nNS£&íÁïázpäúdãC×Ô¬ŽE+åDpÓžG›~7³W´‡Ù’= (ì[†s>¸$_Y,Ÿõ¿ü'òÚ×*@è¶·ðB·ŠË/¡A®‹1¤H†¨°<4×0æöGãõš­xíÙ;öòH¸†4±kEÕØA£þÉV×ÏÅ»lpõX‚ùƒYh¿÷*h±5ž-ªÞΨϗÅoÇópnœ*¸V Éo>ÜQ¥Lfî>Ö¸¦VNe\ÂN“{ÈùSU<‚+Ç à¹Š¸ãD‡8Ýlç@JËÏÀ‡o©ð¼f\{¢KWœI€;~°Aùý¿U|y< ¨ü<ú¼Æ=ŸÀ…™Þdúq XÎ'°Mç(ËM<³ÕˆõÅ=ä±k"ì”Ã×k„¨ÖgCÚÁž› i½Ÿ.ÓQ®ÏTò_Mt›Øqc_Å£¸ç4*éBQ{ØØðcVX1мœEë0½ÁXÛ Oï[J–Z þïüã}`7ºe Ü*8Ò)H×|Êíh:O:üÏàÎiÒh¾0Y5~ØÍ'{Ý»¸g§Îç…t¤î=ˆ|dÙçØèãÜBåHæ])ê²8¶ÜCó²-$àÀ"²;k5ÖY§3ËÃI”V/Sg-E]W%¢è`˜Iîù©[ɼªMÔ/ònÝs÷+|eâ”;8[ó}È 7´uGÜݲׇ|ß+Ãì>b@Í\îã=mt>ìB?¥Ö#Ñu`áÝ5¸ùôèï^D=‰7ÞÿÍ"“ø¦F«rë)3Î2O0þdLò5Wh A¡•Ýl¨>9¹gãf%LþþjejsgAÿŸÉœPËÙ?¿µNòÓâ%N8ÁÊü®ŽG’t˜O³ÇASý õ@¬6›^Åvõ-@q0/™—¦ÞEã[›™9* ±àïÿâtÚjPWT%—oYÑ(_cj[ MþiÎ%Ï×¹“ˆÂX¸œñ}x†q?ÿ8>›£ìv÷S`ajË®a¥mô˜Æó:d…äBÂóâêõlaÖ—ÁÂ0%ø»ŠEp3r‚mµ'¢Ifdõœ-èÚ w¾c®™\+Y²©x*ª­òbÅDæâÅ”tpº5‹ûçÖ¯8t·m¦õƒ’f*=ú«’ÜÿG½µÉÏ‹Ý "r’æ;MjBý\Ù 2{ò!vû&êEñ.™«^£Iô3¬ú‡¿Wf¡ªÁGLY«FgÄ:ÿ/ÿg-ƒ#7î¡¿ôôìrBå¾1ö#ïBÿM!›+*@g5šèŽI9­Ž¥öŸòp¯2ÊŽ]Aóù=Ë‘zÊýúÖëÏ[’´ïò”À‡a{⸑w–Õƒc^\/¼Ž‘Š =¥«Èæó;°ç3"ÎÈÖç%ì4À‹ânÄ7äöÿâßGÉbýiÉ¡e®´kV#~Þ5É=Ø'Ó¿aرŸhðx ]f@zò!àW |ëJ‡ø”PxËúz‡Òc²¤½À–†<ûLíÔd™mÇÖ±»ÑÌa¹ixelé[6ÆÜ]a™;Ù€uÝHŸ'g„^¥RÑÃÿ°Q¥I̽°’Œ4âáû2„Ï„£ž0R³U‹îÞCg‹yS¾ØzȘ‡6 Drq;èE?dB2$9¿Ã㈒Ü)RÝ«Jü&êV&õj ølðîQ8‡ w€×¡b*4v Ëœ«aפÎÚ»©êÿ;ÿÓ©ö“¨¢¹ŠŽmÇÍ·ùÀàr=Š·ÜgVö°‘ïN6Ž&Õ`þ³ë`zR–Vœ½'(8(­€U‰Ât¨ÙìßCÊR¬uµÆ»I²¨±€{ôçá]«¼°ÔžlÌá#mÚ>´58Ì_?„)»R³stÚÛYØÄXÚ 8˜æ¢E—½“"ÕÒŒ­ÑLZ-ëF†¥Adé,¢ù0Ó†µiÆôfXž)N^YˆÑÚÄ¥øîîR„zh »”j¬êß&~æT#5ö‹auäVRÁ®XìNíÁ¨€2¸Çp µÔœÓà¡&5ÜÿÃ_-ÇQxƒ9žºy:²Iq14*_DgÉßÂA‹%- Gô\çF8£äºjPUä¡:ÆóɃ]LÒ²sxþœ =hu‹Mü2ÂäýÃrÉRx²ç^;Ìš_ áWpcm:ûÍܼPMú B®heÈ-?Àáˆã³-ÏáöJ–‘-cZþ92Ü£Ÿ™_[báED$z®ÙBí Aau%ûÆo*J¾ÕgG–+—ù¤$[ÆÌfP•l8~9¶­˜ƒÚAe '0€>ˆš$ÅÏú˜v»ÇØür%×Aûª4f™;?]>øàø«¬ÙûõwÿÇ«Ø?£{P$ú 9_2ÉQ~ÕCHu =3\ŽfjGȈV3³Zó ^¨êŸÔ*{±)xÐJQáK:<Œß‡Ýw›˜{fùèz-úN!ubð¹mjm3'Br˜Ñõ›;»pÙB9:S¬~O%Q5ÉtѰ!Õ?Q‡:Í·á´R-„yV÷ÅôŽ¥ ówo\^P cM_ !ö ãÙiÁtõ!aŸ‚ñÂ2B²Þ_GÁ#qèïƒpÈZ€Ìý!F˜¥}˜ËFcÅó­x> œ÷x$}ð4$ ûD—ü/ÿÑ;AË8 ö6­%Qì`Qƒ 1JfJiˆ1× ³ØÝS¦ÐG-.tÍ©éHí÷q¯Ù-fPhÚ–î¡nÁé04÷ž’r£šßÜÑHG“nË §SëW€›í„w5p£u^¾xr= `¾ŽAâ³£“ì,ÖªøQ¼õ¢éôúâ°M˜ Ãed•Ójø±^„HÓj­:8pu¬_¡—:!ÄD<+–cê^³RªKÈí ø.¨£î¢Hö ,Űwî: ;β{ÆLØ÷àm¹;`u¯àÐÿü_z«|“¤œe‚Äžp(íÑe´PHð-'öY¬JØËwõýW[ ÏfU²ž ¯áKÑo«,cÜkO¡õÅ»Í`~œ¾<¦ ä¯&sNe{’Ð ¦ýåSŠÃ³“1v•<=®õ€IaòqÖ¿bXm%Á³íà{†ñÞªEößo†—~øû†(Í/dÊ:P º=f–Ÿqz±5îjü°(žØ°)¨%]…é®°Ñ’ò4r’¿ =XAjWñ޳:LO_F+OåÑ÷EÓ©Ì«W8ãh(ûñD*øÌ¸¯\å1JbëÿòŸ~îiÖþÅoλ|3rnw)ì!—µ“°-:½÷X“•£Üf}ùûäØl™CȶÁ/8RÓvÛ‹H¬ÝyÜ)'LçB¸†Ñ(ÈEÿßóˆ÷Šp"üï(H¿€«eÝæãI5’:µCÄó‡Ûá§D%‚ž2SÎ ’ºV´·&nuÄ2Ú ³n9P]Þ>ŒX” jªÄá¨?}QwZWËRqí:˜! Gb#„iôÏ8j½5›l#ÊÔø‡6}|®ËD÷ßUðóÂSvm®>1ùù‘å_åJ¦¥ëRÓøsôý’4ÜðTúûÿOÛ°•`™¦ŸŽ ¼ë>ˆÏþŒ…v*äz¥ÎL±¥·þý@½Q{âbN¤²f2¶»ìÈHóSß>›«”íD¼xg±óV»ÑÙSwÂGÁº*É÷΀àçÚŒCûwûK†zßT¡/Ë} õK1ÔÎ8Mâש€Ë¶fW· iºŽÙÇ€¤ásvj‡+µœx »os#F†˜9׿áCñtT3.ækS<¥HsƒhõQI Ð 3ã§CØF®ehÑ$É¿Œâñ+díïøêa€ 3XÑP–qŠr%úw…hÓ«}¬Ôà<&y['÷ƒ÷îÿ­]Û¸;˜p¾ 87^ŒV_iƒÔ¶kÜÇXžÔ˜~ÃéÞršIQüÍMWÏgvùMàNSÂû)¥ö=E®»#Ýî›BåaO…:®< ãs+ û|zñn¾—E#œ]™ŸÒFôxr ã²ÚVö]•J÷¥OψÓRåëpbÇœf;Àý¯NzÂ9Ò«™ÍQìW2ûA_–J¼5¢…ÙF¤ë;ç‘0-úWI†~ôyÚv¼ôUŽ.'¯¸ùë²nçÃÉðsOšªæC;Uˆ¬y­yǤv]Æ#÷/2žk¡FhJoþ?ýÓýe?òh¨1™×ðÕ-2ž«…³oÿÃÞÅÞdW—#»‹ož¾ÃóߦÐ/’gàÁåtK¾ÁÖÝ»íØõJ‰ÙkÖÄë÷zèœ.FnÝ5±..Soú†QýhÏÎ8JüUv±cÎð‘9‰e>±oÜÆûð¹è©/@GbŸî+X~o%]š:•o¼‰×¢à†ÞpWÁ‹»o²'zj%†º>[Ž?õ´é/£ËlˆC:8í'CKüÉT£r¸±åš}R¦‹®£wüyÖð‰4Í•[ŒÍËyP 3c•-ß§X°§„4=ÿÇÿ‡v;[k Ìý‚KÞ­Æ´#Ñ´×þµŸÅÚªð)@“޾¸ŒuÇÊXNÞø½MUü…‚<Üg@ûîb¼Ë2e+ Ö®ˆ©Žíæ¬x\‰ûýŒaŠ9Á=‰i‰šñœÛjMÏÖ%ò«N!ëûgÍRnÿíuôµÀgÖ’ïfƾh”©ë`Î;80Y)>ô£²î•'‹9­¸Ë”Ä/+™ÈqæÉüYðu Jj>c¦¥˜­}’Y¯J?gùÂÅN ó‡‚:úÑA ?ß6¤§ÇqW][€!Còd`¥ãÿá×î°bR [™=mß!¼¿q.}1âùtYÌvó;³|7ªÑQ 7Ïrçài{»ÄÆ¡v õýÎçín’ϛȷw®~±$„5Þ')šÉÞ+,Gñœ-]¼ò98H<Ǥ(¼6@L»°¿©—”äR¾+åäúJQìQZC+Ï?&JÛ·ÐóKNÓ†#WhÏ÷ód]\:Éú`FviÅ‘mWDȶ èâv3äf/yc$ GŒéá×Z$ôq9¶i!Ùš8›úÍÄ)¼_—ïØJ—¤ßä*wß %tÈè‚5Êÿ‹ëzvÃl•o̦Ë÷Û¶²–¾$>OE©¶Ò;ð<É!Y'µ Ò!i=À)ؼžë÷ø2›ÝHïøIUšvÉ™}Â]B¾ž¬bó¿za†R£yÛŒUñ˜CŽßΠåþydÙð!1™KýY °¨„Xð)›Ý¼ðØ×¼’ ÛgGÓµ&ÅTÅÎûÅÛˆ¡e±ªûÁ…ÐGÀ'ÁÞš¡J;'xésC )ÕËí1Í»˜3t‡ 6$8ŸäùJ•zdý½¯àH}k¯™ŠÛ¶™Ó@ë 7ðÎñ&–>z |p3z~ñçϾË)*A_Ç5ôcM–éx£B%åÑB¶=‰£bVSh¾.û6›bÔÎ'\èŠÏ&„È߆éD+t®éÃ`¾ÎiòZ¬7¤NÖ?˜¡\‡5›ñudkõǕܕTål^TRå'póöuÄùÚþÓÜ€%U£Ì§âtÜ%|ow³”ì2 ‰þϸ®·.à¨é™ük`sM95m·%™e…“¹žÃj%YPͳ?ð\­9¾«ø5ûq™ ÕüáwïÆ%7O š¤ ^>ÃQz~ ÅøiF˸&ø“¥-]ÿóÿoŠËÁf樗¢ ˜Ê EØ.·ËÄϪðüëØzï<²Ï«Ø}Ï–âEÏk8Ú6•^‚Ž/¬g7ÐÒ_mðay–]IÒsK–êŸ&ûêOp~lÏ"i1\ðw^FwêXAè~9rÐE”|ÊßKÖ-ëh0Õ"f%¶tw­“˜ÿÐÏ(rß·¹ÇÃýö£°©wžHK ÏõâpPû/È]p`Võ·àß’+X¹û Á×ñð;ø,);QÛ‰›ðse¦~(ÀÍ›¼P} ž=ÃÛÊ®ôF2m™é† 7xÑnâäk‡Nÿ—O¯©ì‡n¹EÈTî çÓ‹á=¿ÑÌßOÌü\²B„lrV GËWáÛe·™&‹uÀ7ß“ò¾Ĺ F¨òÉîÚ;ÜS·3þÇ£æÆAæêõØ¿ì]2W½qû(«D—ï»ïðÇ€?µ=ØDŠ'sILKíº4Ÿž{ÿ ÜM=Ø‘[EDÐ)”Ôh¶Ó.÷sô†T^ÿ [S­ Å9œ<¹û´UèqòÙÑÖ¬6ãjúf™)ðãRëoØÑïŠDÓæìm©÷£k¾Aó?pTæ<šï ê™Ärþ.5šFš¦^…ŠJyR}ç'=õÀ’,="H^üX‚{'ØÐ’·¨|d9q?vˆ,º«BJO3$`€PI÷øÿÅ?G>IŒé.ÆâQY®È?º3Râ!Wv2À—¡ŽëÞýÆùÈîÊ[†S·í¸&B¢ïKQ©MF¬Ž¹<–jÇãú[œSíŒLÌZ‚f,‡³AgÙY…;h°–!]þ,r*[°îf=.ÚÊOïäëçUŠøä›8TR¥©Á"ôÄŠ“XÕ¾NÜ-Àq™6tk›F;9/\ìÃÒÄ¥œæö6±ÿÆØçU3 VIÑþ!Aº°u”­T”${v@Õ6Ë–5NÎ Á•0ñ¤÷bÀv©Ô „YO?rûJaŒÍf°0Õø~­ùö¸|×{{±~šç@ö‘ݬ¿Î{œåâCç6fMêI—‹3ˆ®¹cœN¥§8±E"’SÝ"ÐäÁ:¢¾ŒÁv^^,?8n¸‡r‹ÒJ±Iû ík†ŠÞ'ð¯R‹Ä&„ìµ*È.‰i¢äúü58rø"³îÊ_x%íHŽoå\L‘¤…3ÅA!¢7BGþöÌŒT6lÄ)ÞgŽ}bÊ€ÑNWL®ŸId<…PRlk»Nú7r™ØõÊDš9.=œö»‡ÐÛ6-]ËÝsÒŽTðÓ=.åpYê>|ýÿ™ ¦3ËN 1óÛ”ÈýGûÁ?°µ6îcú¬žâœÀS°-³Ú®LÇJïåL«!²7ßḩ ~œgÁèwfž>ãgegjâ½wLUp ¤–yãì¨uøP%Ÿ­û¦“&Vì*dˆìõ½È¹.Äþ; ó§Ò¡3Ìó+§pàYô½2ƒÁGRÌåvx)Eº~e˜@†¼#@ª: Χ¼£§uÓ @/§•^Öˆå®,?Êü3¼0ùÃî #úÇfnºµ °¦ÝÖdô©Í𰼚Ì_g•¼ç  óüÕ ¯ž|¦:£Ô™fŠ6é¾ð˜ ,eB›âù/ûèæZnÆÆ$¶.à>›p±î?—Ň‚lùm&ó9‚…D+÷ë§éjå2îůB”yq“9)ó‚L>`Ô*iZ-u·}åYL× â“áÝb&ÅŸùç3§Q¨Ëžné"›yDïñ8²,q2SÊé`Û×°SÝíüé×sÜò5$¼µˆ­¯Ü@&PS¶pƒÓ»} Í/cö9YÒ ôë,*M.ÁÂ{‰”;û¤ÉÁᯠ†8шV5øÏÑgËæiÒ¿_Ó‰ÍÏóÃçû øäa‹½¿­èžÙ|>ÿG¶œT„¦òÓpü†.ÉÒ‹Ã’ &°Ú«“õÕ%à±õï0[c6ˆgÜ¢²ÇŠQ:ööäÿc1ÛLÈšÄל°ñþ‹ó^\ïó[`è©ÃÞ­Må­å䯋hÏ­¡¥%àôWu_FDëdÐZ -ëMI¸ìTxÚp‚—¹ë 5šFî܂׹ö¬}#Á´žQDÙ5„Y d¿Gws5’¢d{á.ðéGìûø\V “5s‡u9ÓfλŒä¿=e4óº?׫µÜZ“q—æ{*“0–´ÉÛО Y*1õ,©-ÀUº½ðî§fcv_ÃJ KrÞ¾•+Ó€ýu¹ >–ÁIƒÞ•)°s‘ ÙØy÷O¤k*.Ðrg](ŽÇVjÞÇY—’¹9æLò¯&“¬kƒ‘Íød­ 3wgÚCn,ï]Þz"Žþ&WaðÙ~<Ý@ÛÍ/Áö]\ÐU"”6€JÁŸ Ãʃ½°¿FWæÖí{ÙZ‡cÔèËôSR`æ.°ã®.)]âLy¡ÔoŒ­¼ï÷>À6ùTš9ÿc¿_ç®­Ñbn¬$φ¤6¶Á¾¥Ê°àl (Š£øßqìxï>NåçL¦fúÏ|‘†ð*kN]l[ë«? ‘‰û„Yz¹ñi® ¿÷ãÕ­Àz‚âÈè/hZä(â7é lçùÀ¡]]œW…3ÿ)ÿ5\ßQ«ûã ûBi?Ì1K›÷AtbgÂY;o¶~ýšÞzÊ®OjÞþ„_«‚Ç|A¶ð)SiÍ–? àtŠP»ô-ô¾÷Â9•ÃhÝiÀ \ˆJo5œÔé§±ÿ¦°Âdwø2}Îâx_µˆ¿éüWD2é㫲=^„àhqØ÷à5r\ØŽ0js· .UýÄm»LI£\ü‚¢¹ëòàeš2‹í†µ×6cò—Ø)*ÌÝ•Þ Ç`›»û:Žlãý»F}r-`êc=˜S‘Í/}ú–;ê` wz\XÑüƒ,Õæ)rþ®øë’&n³¬¤S>¦ûn´%U€ÁveV>?実_–J±€Þ«<+¿ÔìæØQfñ tw‘9:kØçWÁÐÇØéÀìAA“fŒØßJÌÅkRÞ†JZpäÞ '5Jœç’•JCøýÓhˆ6€i«ÏÐ%‡`÷E«ÈæögùvzÐÌhžò¯ÓxÌa U.Y_S¦~þ<˜yÎewENÑÆÍÍÜŒ¡c¸ª¢ì{ އL~ARL*î×û¯ŽÇžY>±¥`œuŽ;?·‡Î0‘fú‰Ëøú·¹¤%e¼mÎù‡5é²päb;ú„ಛØq:6˜rú|ÑÑ(7¹Çb¡Îà‹­æØ‰ tæ_yòe·Þù V&LÖX·Óš¾ ±ŽîΑçuZÑðZ–™œ¥ñÏ f¼Âݤ—“ž±Û=ÔÐôî7ëú ¹>¯„ŽÇcø,z*.Mwéƒþqk4ˆM£;iÀ¶l3ÚR³ öœ†£N5(˜hI¶Î¿‹ë²èÀÊß8Æü œ¸…çâoÒ‰c¿ðb„21sH{EIrÚ3Øq`1¸/ ƒër“ñúÕ,,X) /f¿úo=ÎlÎü¸ÏFdð„gªŒø¿ƒÐÞ› æ‡ @ã—!)?soÏ|„_"°*1®>aº;Û gµ'“Òà9œ€×~¨:{æî¸‹õx÷+^CáX+´‚+ï×ÁJ'–ãVß?W¢mƒ2ùqЋªOÍd?M$Žo¢¹s¾x4oŠhaØ\9°/Í”ÏkÖzÅzÆY°dËIØvl—ßžAÎNÏ€ñÖóB%‡€7v)3%f¬î%{_oÞßÏŽn¿ ŠÑý‚>zßÿ¯ß3—[Ö‹&îç/ŠcîÙ4ML€e=Ða‰U)p?â-dÉ'UÑ¥à8çìäK®·Õrí“Ø¢lvòl}i2ùù# «¬aâ¹{à»q+4ìꀷ“G\@z¢{ A<‚­ˆi| $©åç(|˜’ ü87s{ÚbëUÏ ¿ç³ë3aÇ÷Ô`w”;¸‡vÁK'}îj«‹›»Ó C‘6@÷;O±gý#ܨ™È|ï-œA௔€ê=o¡*GŽíœ $Ûô¨-ËXdÛ<_à·Þ{4ëÏs®ÌwøÜ¹¨÷åÌÙl¿û¸õ³Vš§àl¿ÅôOŠ}J$R˽Fìÿ¨´¢¾#Í€gð̈©—X‘b‡9(Õ– %} ñTöVN£a¹ ø˜· ýEâÑ÷À>¾b°)^£Oì^lÃÎül óÅ3sË1úËir}ª/hd>æ ¯®B3Cýÿï+½½gýð\ƒ‰½+µš.·Œ{êƒ× šAÌ2šE«m$¾O¡Za‰³ˆ[ú-‘ýÝvx¯Ä‰ûµ'pµÏ‹¿[l~¯‹!…èŠuEø¯í+ØHШ݂äÓ§íà—¥ g΄Ã;‰•Ü£É+™ØîËtå†KœäÍ:H^o€eŒ¡E»TŸIûÂäaÎÿî¿BC²@ÑäÄ•œB¡âP¶kI :íšÀþÌèÇWªWÑùo ¾µ’"ûÆ%¶/¦Ñš |ËWLýWÿ®Yâ›" ÜoñÌ?¥‚G»/SÁHû…—K}Ù??ufk¹ºû3á@òîÖ×q 7Ç£GÇÝ]0âX>·¸)‘ × ÓÆò|=&‚|ûPˆÈ¡Ë;½àËÃ0ýy6< Q'1>ÈãRÏ‚L^`êWõöÚ釞¹¹Õu)Q—°ó~ºÌÕg“BcwÐ9¶æPð´û;˜»ÿ+“ã(¶}îAøð$j„ÿþ›¹ õTÑvÈŸùLJ+Ç—Ððð%¾ÖIÕÈÐI=q°Á²äƒyƒü³LÆÚßñͼӰ×Ú§N‘ ÆØË•ÜøÑ/鯋–ìˆüˆ¨P T—âW€.a'³ë.ÿÔCçÒÿîb†H6n¹p–ë¿T¡.ºÄ\£ ï|èÃøúwÄúncUAI¸(}ÞÑj‚‰“ïrÝ¿EØîü£¸*^ˆ ¯ÿ;œÑB]öU ›…â­ =øqf9øo͇…1-ðxg¬‡b¬{6 "†óf‡Ç4ò.Ôˆ³Z/Ã~]üßû?—õ7ã»Í7¹#&Ñí2^Ä|Õ}¾pì"ìSb…{ÿÒ·|uí¶ÄÂ; P±À€¹6Œ'?.`“Pîêá:O«ͼ™D`÷o|Ù„hß’…§—ÆÂ`´0»?ùtƨá©ÜJ ;€ëÊy‹‹ ÿâ‘ÛÐ[³š‹"8ùm„yþjÅý/i@-C¡OVlƸ.>>EVnŠb`ú¦“8Ö¸nM;¡7jѬ[MÛÉŠÙ¨¯?\ÚÐ…+xü¯ Âcþ xlÀ¢¦¸¢èŸ)ô‘ÎvíÒ¬5| 3fÆ ÓÂ>îm½ÈHþ›‹ï$µ©õOŒ¸–‚*†¼}C«Ðù÷ø?ƒ‰ÿÅ™?Z ÄH O¹‰©Â<^AmÞæëe"ëêý‚Ò™ \i§Ö,ÀFÍÕãñ¤Rˆíšr<õ›á–Ó XÃÔøsåB¼Zµ—c†£Î@ýÛ¹ônå<ð2Ðb×¾‹‘9EíxÞê(nKIÂ=SY½e6LÚm G¾+íÀ2SiÕýÃtéTrw»“|SŠE}GÐGëöÏÖÅ–®y0.9úÀÅÙj°hKîµ·dÚÏ÷ÓZ¡l^Á™#páD7~ÛÐ ·#sGîÿcÖÛñ¯N‘£?j/@Ìâë8Fs ª'Mb*£mHÊÇ4bf`FÈôuìþEйR욺ëþ2…éûÊñêFUbê·mhÜ2Ž–æ—£ìr[üꈕ›ý xÿG½P†ÕúO¨i®%yô1~\ cªÇ°èœôÊg»[çMôÀ•îÿõ‰Æ„׳™ZÇ:èÿ‚&qŸ81UBÔñËÃ`™Î~ººU‰V_Ñ×e+X†±©[´è’qì¿ê·Ïg°½F1Ñ"âï¦J¬ç¤qÿÕ[Où¾ÕWp:ó&íÍ#üǧ¬ç'î„Ø óàÉ´~8 àIv–ß¡=Ý‚ /›„ÕÛ«©w²!Tw“üOWa‘ìÔÔ ÅROy^ÆéÀáéÌ¢BÊÎÜF»¹î7ÓŠ¼?·›4çò¿m‘f«GÁŠä>ëLn!8Þö!ä2þ›Åš’¿Ãâ+šze6V¤ñxs†¿ Ù 2; ŽÅ;°ù‰?á„V%Œû÷j6§{ .ãÉÓRĬz2š*¬;j+‰´I HC".|÷¼|êèÿ†÷‹­›AìÙ”8*ÁO<. ¿¬0àš ™¾d/ñ M†Fò¿ï’7pñÊIª¾#„.ÌÎâÎ?ÖÄéN5ôØÞÓœBˆ +'§xɽöÐá: 6U¹ÛËóHgN<eñG¸N÷©,¾´“<çê>›H¾º¢—Ñ3XMüŒˆcSžð„÷h£­§<«-Ë-dU|™G©øUÄ»6¦^g ÿÕ&æŽ#]"™¼Úœ­;œ¥‹yÛ®òþˆ4á„w>¬èÜtØô]Jò ¹6{죊—} â#-–û'Cc©Ð‘0œiÉý7Wljã4¸µÂìÒùâÉN“1¨¹àëþ=«§³Æ¶.f~‚l6í»Y ýo9™ðH ií£¢Z»9Ë] dºª",5>'ëp&<,ú^‹z¢àÎIQ²ÆèÈ5ý[¨žbD¾»¾þ¯y&½UgÐOÛ•vQ 1’ÕŸrF»çCK:l±'ȬOnÉÎíðå?žŸ{â§=§ašäuë[:aÝ]à/oàfžTcgµbzÇõÿz>S}ioÌ’1¡w{'’'õ¤Pê ½°2}„–ú§x¦)p‘ÎhÈ8ű¡³ÈkëPôÿi=áp܈ÿGi~Ó?^Ô'U”LKÀGŠ…¼½‚Ò\úo’”}ÿŠ~‚ÐʾԳu¸¼ ú6O"_ s3øZ,){.seò=ô‘H,™³H ßWÀ-ñ2^Ù¸P(}·Ámn@gnáF¯• Cë·Â‚eÕxIÿ(Û¬Hbæ`}S.æGª¯º‡Êà6:\qå­ï§–,³ÞT9ŠÝ­¿ÝAö°ªÉžWM%ÖK¥È'™¿P÷¾•»³Ã߃(N(ðÅ-*ätL?F‹âÚŸyh¸aG^ån‰ëÓÿj«¯?G:ÞöØ_"9¬–$¢/ àæó4î˵"ñ© '{üå…6{“ÇŸP‹³óàqA ­æNŸâŽ!/ÃÀ¨b*Dż¢ ­Ù±|iHÎ8HxÚ½À$÷ᥠnlzU*[àzÕ;GaqÂNÝ· 6Évƒ’T,¯Þy»é3ç‰òŸ=goÏ¥£ä„ àxùœÚèÍroüa×(Ìô¾}‡#G˜¿¸ÜM³”Aªl<³0›N>ôL‚‚u8½ÿ:ݹ‘ƒ÷áê3bÛWŠ·…¿P»¤Xx"y=Î úú÷€ø¾åÌk”Ø~þ¦ ܽ¤?\Í/u¼3¿‹jÞÓU ÷p“W„þL³éÁ8y€ÇÎÞMFûY–ì`®2†n':ѰÀDŸ|Ÿ:õϨ„»ë9wÒøú l1¦A«èsýÙl“b÷÷ȱò‰èzªœK‹1n-rÂ9¤{ŽÙ+÷›3ÁÍl”O;¼)jå—¾»ÆzZæ“o¡CÅi~ÐIE¦±DÈT “éÚ3‰òK²ùQ:ñn;l€Ç“áeži˜X qkŽáÏàéì­~ÿ…Môru lÃZ‰Ô|ƒ%­º»dþïýïeòäïªjð\uëbŸÃº˜ úxc¤ˆMš4™æpÄ^?a~²Æ ,®V¥_Ó‘x› véTrï§.ôu¯&cÂÒˆouȨª¥F¯nÀ[õ;tµö/N:f>yªÆ5³ð–IÛ$”L}•~¡‰S:[ÉŠãe’ðÅeóz»%@æð!ö7f)ce·0Ïi;àq‚ƒF)Ž7Dßà¦ü9l:H0µ’`º¡4,úàÙZ5äé8ñïX}Ưá‰dÎ’Hlÿ4ï¿~ÐdwŒ¹ÕmB¾­Ãv[y˜1Uµ !I-‘œŠ_×°gzÉ¢uáÚ4-ŠÌ™\À~í\ ×N¨“5ý<——N†ç„·Ó#èŠÑÇè§¹nLîþ´ýâ¿Àoú ([Ðû<"²ñÒ˜k˜lÕ‡%?£è&;˜¤ÂÂËÿðfLºÍíl:ƒ‹kãaÉë´S€õ~Jdª‘ ºþ¾<Ì]Ñðr¾­8ÙÓ5±Àò'”7‹aò¿2è>«zñ^ïv®zîW”Ýyu>Ç3ÏîÇÙ\äsu8§<†l»QŽ¢ëqÖŽVðö—&Ó÷D“€®L~3ŠlRhõFÆötâÝ3çq‡ÎSõv\\O¿-ÃÚ5Nÿõ’æB‹VÀÕÖ Õ=Rño8ÓmÚD}(¶ˆäŽàŸÿã*Þ¨åÔ©s}¢õvÕdÖûZÂÄÔÙ£-Íõæ}ð# ŒKX½­}RXIEèŸØÇ²ÍA$¬eF­dJŽ,> óÕ“ˆÎæ‘cýp ÿê@\³}ÈŽC×1wß9èÞ~ç6eÂýÐ÷¸áY¹Í‹âR{tôˆþÝ©$[Í’ð}€<ÜwzoÊ“ƒKbþÝ‹Xªør¯™°“Í¢Äi‚%³NxO&1Ya3̉ å°Í-ŒDf¾ãf9ǰ;}™Ôm 2vã_ÔH„X“*ØÞû›¶_ÝÀ¼­¾pÒ}ÚLašYÞÃà§Ž,tü¹ÉM%ƒ7º¡ÁÞ’¿þ¥ûôW“¤N¦hûŒWÑÖ j‰âÄïåZxé|’gV©Idè”ËZ¨ÒÿòáñÒoàfJ~V«“ví§páÛd¶bgŒÛá ×ÛHÕK½ÙéÂÚ‘üÿ²ë GŬY„¯ž»9†H¬‡ø·+ø$Yf¼ kXËVŸ„ÕŽr–"xÛoxŪ·  ô†UJºs·±%­c@95÷íA8}߀m«ºo1ãô—ð_ß傌tì=žŽsnT5ïˆqxZÌ ÇÑ„þ)¤g—™ÜY‚¡mÙª'ÞÔu¦+Ý+µBo “ ygš_¸Š³ .æ‚O¦¡éóWÎ…âÓ+·öWؘÇë|"Ã{a{µBôÆB’RS½/SØI5°ñ|Kµ?—³'Oïà€ô²7"’^\ß9’ÿƒZé¥ÉÚ’WQõM[ÀÒâ3Ýøu28ö–p‹;Zðøå[œ£úwüµe竬NÞ®&tn‚N˜·Ç—IA}í£àë³Öü=Åy%\€­zP¼4Š,> q{ðpýeürKŠ ¸ß¤é•R쓃ñ»*¡:Y|¿ÄŒ¬êr©(Ñ6“d3wOæ7lº'ZÝèLu ¯-¥Hkž.ܲb·NqIÚæ¬¥¯¶g®$ëœ*éázKÊK Á¯ªv̰g1”çG¡ùˇ¸¿b l÷G‘j°ìã&.úZ3 ßÈáûÞFÈç䣅£hQ~ýs¶.K*³YEuœÇ%8v;¾ÏjÂãÙúlbQÄ.p!ÅKbFÌØùT„ókó.‚ê5òYH óL ÙšbY²>è÷X}YºW~Žï†¢}Z¼+1Zl²G Õó‘$¿ @‰Ómܲù<35"kï/…wrÄìÕ º˜…âåP ÂH¯Ä)cú]j7‰ú?-èբŒˆáî–‡ì–dvéù§ò>ö܃™ƒmuO½ëÑ,â#î½Ù‹¯ZE1Ä)š›¸3­øDLÎåaø‹Ù`üJ{$þÏÞ:ARMàßÞ±lþ¨ØòJ ¶­Éåÿ̺Œ=%Ýh½ms®ƒ¤#ò¬äêXr¿f-¹š×îNÃî!½°&œÿG' lödb¼H gtÁ‰Ó|ü„.—ùßž¿¡³vY¡n¥)¶î5fÆI¨Í9;î0|†LbÏ%Cz->ÙŠ>Rgë·š¤Á›7 Øe)aê¨ë R^ì~Ž“»t—ß·T”ü›©ËŠ»áŽÃBb™åÃî|Tc²Ò¨j—›î3—Ûüå6ÿíØ­´ìhø[èwv¯§1I÷ðá¦Öœi¶i|Ú0RÿÎ^ ÃÕ¦HTØnGŠ:dA½(šŒßVL¸mpÌs=Ä-à ,¨G½òDHë‰mÐ%ê¶ÀTó§0/“ßTê‘.©s ‡' 3HjíOL™’†Ö_fñÇ]Å(å’uZ’l a˧ žÛQ¶ñâJæ¡·„SÿVÁî ¤à§!ûÝ}‚ÇóÂ~Íe†.ÄçD+š&ÏCçš2¥?›Ý-ÂÞ¹/dõf9DùxÆèœcbšÁXqÉ›eíµÇ5k‹àL„Ù1*m’\ØÙˆõà=õ¿úig»„Mz¶ŸLÜûœ÷èü+œôp#sçëœJ–ƒ´N3Õy;š³26ò/#8ÖçLBJŒ¼Ù;ž%|"Íãs`ÓP#³v$¶ëxyö–ì_W =¦±µÏžÆcú X¾Ë^8µo LMmÄ#ú¤ÿÇe!(Í<„¬0#'ŠaL ‡NîË.ézC7þÄóÓÝPAÞ‰äëmƒ ýtÙéLXòL>Lù„otö‚Ô¿øùù4–:o4k’â4ϰJϯ°Zô4Ìwô‹7àĹð#.dÚ¥í¸»º¾YÇy×\gŸR’ \ßGv­‹f ·&|åùNì¿u»'É^¡mñÑÔÔSlÚ+ËÞ½UC£“ÍxÎWœ)/´e/{4!óò;«t¾á ±ú2YèæÚº›ÅH¼{µš˜ƒCb°ß1.χ&:¬a‘\" ÿ†™½i¨} #7ŸÃÏsša’­*û²µ8ÎZˆ_îkCXÞ(δcncÛÙ‘ïCðû´4s?)ÄNáX¼$Ÿêüb°o ntG’~Ê¿*5#Ò× “ÖjÝ>œ{Ôgbë¼pW—©4<WaL_„~‰ðè£-”5‡±¢’¾ZúƒŒØíC˜@Îçüº6Épªí”Ï#2{é¶š.ìSjƒÇ á—›xíÁ Õ!ÅSš oy-•Ùûž·)e¿ëRtdO`!SôYçºÉìCl,¤ÿæs7Èp>ñ‰ÆÇcæBŸüB4>0ƒt5éB{´¯À+GVâm>Ù´§&å?ãVÐ"­ßÈÀNm%H8ó–6Íü‰ÊßΠP‡™ïxÖœjÄ×q^%–ªÝ¤3‡ùTüÝ+˜ê¨ÌÃߣp݃,ô«áÛàaåO¼mƒ¡¨çXį“Þ˰斯ÜÌÈ-pÏZ¥£É®K²#ùïòsøpæ#´hÄËR6̼Y›H4‡’ÂÌ0Ïs,{q$ ³J¿Ð ŸâàçÓ©DüS>ð>:2ÅÖ/0vå,p]=›:J*‘U?OQÑUÁ0æÊT¶¿8[­vIz:^ a¿tò¡§vß}ëCɇó‘ìÿ&Z™ú±‡cÌ Æk¹·º²Þ…·Œ5Ò5In&!M‹Dá‚Áq&ëcI=γe"w`x} ”RfsJ³ÈX%iV²³ Ö}y צ:Ö¸Có_ž±Êëà¼r)g}P ¢~2™Ó[­/7ía^Êɤø ‹7Á¿Mº ";#aŸòsò{¹*GHÛz¶qŸ9)?6 >VŠÙL]¬N.$dŸKçdŽ$èRp~M|ÜÀ¤*œççyš>¹¸ˆTÝÊ U=Ñwÿz¶h‹1ñ±޲“@ÞGÐê3O/§ò‹óp¥_*ù¥èÉz"ÓðÀ©LöõÛrâ¯Å’m» *ó Û¸Q†­(Jf3—¯$¶ •Iç¯õd»ðyrÒú ÜH»ÀîŒ#Æ*óÙ@òø”ƼTÉÔ£âdÕ,â·ä+„…—?~Ìÿ;Ž4½%Í×ìqàr6úöÏfÞñã©Æçôÿï(‘ö+à©“ ìÅ-DGukÚøšÞÞ¤þss†÷·„¦¿^åÅìãº~î±”ë.|gž‹ü×ï¹þ@–,³™®ÌH¹òg‰óŽß¸…aŸ%Øí›·±ãh7Ö?õ` ð®©O‰Ùn¡òžKņóÑ(ì•@ÞyÜí×{šL…¾ yt×gyTÉÆûëMoEH»ÇsÜ<Ö+»¡iVäÆìvúX Øy¦±S¢ QofŒùy–ž˜Æ~ì¸i²y(ë”IŒVÔ)Þjr‡ìÆÇ3 ¢?Oø=ÅÕ^r#öÿ(A­W rmã€dZ{ƒ®àwH6+°ÍáÌìˆ{ë2 O|ÈÙœ‚²@b?t@›Ç=Ð\ëƒkm€•d‘Ý-é.‡F¼pGœ]Jdò)ΤlÑXø*tâT±jFJº»L*Ñ;µ•[y^ž¬‘ 彌3aæ&AàÉy€LXÛ…]g€§¼){i®Ààºâ…V˜ø-ŸXN[¥%øiòaô tÀõoèä ;0{Í'tÍ‘ j‹6Ãá}HøŠó(F¥ð®€+¹£ÿ5¶íc:ÎbxtÆ–¸:ˆ%$J’©·BGøß$M{øü^„ÜÈÄí÷MŠ·9A¦i<}%Û'p!†ül…'Q´,^ˆ ßõW¶Øƒûމzàw‹+‘<{›˜/«t þƒ×êêA“ʆýDo„Ã>)]®óq"èû–Bä—g„ÞÉØœ ÙJéñ¤4îœC n”ÝLžð òÀè¯åe†UÃΫ™Øø=äj¯ü(Š·†zýëô{»y]Ä®–C˜µ7—ýdÑý‰+õŸr[eÊý&$ Ö ½–”ã¿M‡øù>|ÿqBpóÊÌúâ·²ŒFôÏéù½t¾š5÷Ä{5#—´˜»*Gò³nÀçsü¦Qêl”­Üž>“|šó³^ÛÔ†3é“aK¨wÕn3•>ÔÀß„cIÁým$fÖ3®,n?ÓƒÃh¤Ùk[ZñuÄ!è²dÕ¨øÁmxü›[gÁ`ÆÛ&àZ Y×ø;;Î/ðp^öŽSé.†=¢ ðiüJüp¸ ƒ–:c‰ú:Xü\‚$}îÄÈË6Üýž‡¼#0Äq"›_ÜÀ-剻8ÐŒ:EÞHg¡ôš0?½Œu=“ƒñ»EaFJ <­páÖÙ»áYÍhÎËÍz¿gúLÞö¯f¸^ð¨Ö7Áác[™÷‚Ÿdàˆ½ÎfÏH«/{Ñwv–ìʇƒ´oB×P˜ÀÍG±‡~3Áûäm Èâè2˜2F‡äd¸³l˜yq <Z‚7GáÃ˦œÉôIÈcPgî5œö²—¨S©«ÈÞU™P©Â …£ ¬Lß~”·Ý®þ&ú’»Cþ¬Æ{:;3a3êõ#Kþþb_¯€–B¤ ë3É<=ýK‘œÝâCžóêÖéOð߬«0ªÉ—\Ê„ÉÚöD9‰ò†× ñã˜oÂw,›É^|û0Â%Nöbó'9XÅ—€·ÝØÄ]@÷¢¥øˆH’ >;âëÊÏh“fÝD¦«Cǧ’:ãx÷SnHßn5?vù`=Ü ’d-%¶$< :Œ=ç±ÃÉ”ÝZYBö¿dk™ 18ø i{*„ßÉ“LE‹&Yn¹iÜZ‚AôºO7"á—æ°Ù“ãÑxòª1Úfù_B_ûv ~ ^ÛàRÜF,¼³„”–©‚Ëny—†S÷!µ3ŒlÜu“ý­QÁ»›L¡¾î2…Kt*<¤Å¥DJÕ…H®$ ‚£XÖßW#öíe÷´p7·`Úi)b¡÷‘²cêtæÖlº¡85CÝÁ~\¥½ÏºD4ž_ñ Y »¸EW×2AÉVèúœšZÍpÅ,ÍÓºñÍ›:p!\¨U/¬Œ‘œZ¨ÙâÆÉ>X„A¨EÖ5'bYv+z¾Ï¥že@ßËe*±àë{ÞØˆÑ¨ç:Þ^1¬—O,ǧ3ÞpMÉܽè±dÓGrµ~6T<=Š!›®QAéüqÛ —ÜÀ¹ßOA\Eû \ ¶ÿ à×Dâ{¸ üã¹„Ø PÚ4‰$Ä•€°ê=ÈñPçžž1’ÿ’RÚ ù’*šmñ‡+Ö…è'åÍ©™z@¦Ò ôýz››¬mÊ}çý@ׇ Âæø<¼Ä¯íq$nêÜõÌïfö‡OßZô˜ ·šîs·e@ÓÙ\ ½1À/É0gc¯$a¶û–{7AÀ¦÷Ø0ËÊ DȇSìFÉ]€ ´Ï³„)=óduŽà³ÆFîe/Ý]€LËÅBŸá¼ÚXÐR¹Û¶“ûÛ`%ÓÝ;N€uSÈ%]Ç‘‹°¿ä¿|büTÂãÆ1ŸS)(·&=>©1dÖ‰bð*œC/x^±ÿî›2ìä¢QdÚÉ}0XiíöÐ’D‡é yh2s 9´¥O» ò{·ð*‹Øg13Î30÷ÄS•Õx×B‘…=^Ìè\^ÿö¥d±“é][a÷äIÞt/.zÍEF¶±‡š4ps6WÔ’AÇNuµ>0¯¬ÓŽr§@hòD2ñ÷ ç–(Å ÆÈk§<¸ÑdIp )²ZA?¨µqœU˜¶ƒ­p©àô\ìáʘ&|“$F¢n ’Ñ©äò ÇNßLluÇ­k½4ï !¶¼W"íÁ!ìΤ0Özäèä¾ãçHŒÂm¡_GøÏE;Yüu74e}ᜢô¤²Õ[èÇ×Ðiy3ûVA‹˜!1‘çô¸sg*ñzwX¯ ƒÍ’z,ÒKgÊ%à] zsN wéI<­Y*[ ¦B—Ç,:µ ±L‹·c“ŸÀÚo–ðk»»Ú¼¯ƒYY.>¿mÿ˜Ú;šÕÆ]e't´HåÝbP<Ÿm½bD½¤±Râ7Jö5|•7&Ï>¨³•òÛ°èÛt’(Y¢sÒ`ø3ÿÍ—¿x2Ýã.­bÊží,šMƒ0_ЕUê ’É3I6мWV#úwGâ*¨­åTo;0þw‚]BKÀK|;Ý{ï7æ«:Ù»’ Ͻ° \‚Ù+¯_?­1:_”NÆ’Á-q»« ònbš­.9"Ë,ãcí_-´YzÜOµ6^m`S—G??k˜kúÇz2Ð~# õ& Yö±%¸Ç9‚I-ü~~M`—v^ä*ç•£×\"øQžÆˆ‘×"ì¢h8è÷ƒI,W´± ŒKÙ%݃h- DdצÒ$ÓE(¸j>3–œâ·€åšj {p^Z6âÖS:¬ÿšäX¿á¸øÿÛÛ¢0»,zÏ~ÂUMôn;·Ëà0ìÑS`cBæ‚ÒÎXÊàãlzá‚óœýÎug¯®`äžåP:#öDÇbÑíx,‡'ÅP.܉A‹;ÓÇs'®N ¿t?ƒÌƒ—vÖ˜Ë7úƒñ;0™”Ò÷ïjÁX0ïLYo”ÀId Y,2kÇQÍÏáÊúT¸u5ŸÛÉ{„ÿíKÕçþxú «Ž=…¿¡bmXô„áÇqvÉ{*ƽϢ[ƒå@·rÓŒÝI24LSåUIjˆ [¨ôÇåÙ€PQåHý“ñÇv^ו™´íÐRVYýï³mÜŽ v/ªMXü[Ï©L¢#]-p€;ðÓYˆ'ßÅ @Ÿaßþf]áN»À#b /¦\£¤îíÞÌÕJE¡GÌ?ºî‰=Ù™Ù¯”‚ðÐBA2øÀ‡SÛ¼Þ>¿AÅ÷¸Gn4¿]ˆý  ªoÐgEPMBóo”⪠ljó5lXa ¾!—qÅ9rdzWšÂP i÷\Q À'ßË螣&$â—ÁMÅôeŽ(sãÙÞ·€ùN²Ä¿`6yl2 j¼ø=7‘úŸþ?så wçj8ìn¹Ÿ^åÛn¼ù_Ý1lúéÛZðÆ|U¢'¿ŠöþW‡Ìñ~¦Ùt¤ÏŽÇpn);üw<}÷Ò{íĘðx#R¼@ .}`v'¶À£õ÷ày¼{y^–œ.)AÖ®Ý ™El^¿7l`w¨§q÷¼K¼–^ŒJ¡ùh‘šÏ©÷jÃMá!|– SE¿Ó_£'ð9Ï1hP:Ž$Ysí›ãÁ;i $‡|çr/âuÑ{>¢õÛÖÛàt¤ß;z€‚ÐEªª¨Ê²^ àû{·`nÇÚîwuLŇ£—ŒàsÿÜÔÉåç“hpÊ?Ù~â\× çøû\ÀÝ-Ñ\óÃÏ´Ïl:»´ý /dâúœŸúËm)Û3 äø÷~Gž¹Üff^\ƒ(s=õLž¶Ý@[dU`Wf  ”r…VZ¤#b5nÜnÈvçE£˜ÿ|j›û%Æã–E²n=Çí›y‚Š«ˆK*­°®°h„U;³êã k¹è€elNL<¾Æ¹xJ¹³pê,=~ä2¦oÿ“{Q:—‘a޲ þë-·0ΛûÛ'žÅ¹)ið:Ú -VKá6)SŒ¹j$›ï¦¦pÂ\Š`–ÎwNù±#&D¶pë¿«’ëYèÏ´ôw+¼pÅcµ `ÇÁ¥t`s ÿà6_Xñú.—^À-»S ˜òÙ8þô!ì©MAù¼pª‡ñÁ í\6)ÆLQ!clgáÌÑ3Fô¯¦ýYxòü;uòd/ä`T‚&™í_‚ ïGá32é‘·&s2”úË7HÒaËó8Ï.”ŸÍO‹¹£q×QiÍxh.zs†êÆÙ¸Iâ“ÍÛi÷ŽÇxNÊ^´£ÂÇ…8üz Ž.– î¿R0¯]žmù$¼jqÇ¥Îm•Ù…ܤuûÑà0Y\ÛLsç$cгd÷š€ÿ(Ž{=4ø ìÛ‹×Üñ)ûXOãØßž µ²ÂpÅKØ›|¯¾ð¨4¼°ÂÊšа/“¤.¢­c%¾ø#3D5ɱs#ùßgÏcn¢g4F&º{?æ5Gûñcp¤;Þ;í&7¿$B“ûœú5·>£C…¾çüµ^Ĥ{ ³^Swò¥˜ÃÙ/õ%jðrÕ^÷¿ñ¬fñ[X¡ðW»í ¸6þ@;¡d‹Ñ(ø7ó;Ž Éò¿ ð^@<^) ˜ŒXm_‚=9qÄaÑ'¼·‡Ò ~{1öâbÖrñfŽêão¨ÈT%¨]a)¬x³™)/Iƒ™¼ ,s 'oúÚ¹€Ù2&6£¤púÙùQ?ef·}&ñ³ÓgËœ«@þ˜soMck÷Ý'[wŽÄÿ‘Ûàåx4>må›~ÿŽ«ŒØÅ7?Ðnc§œgH²°sj.÷Øv29°l nž@ö/¤¼:x÷Q’t®¨Á;ª÷aÅ•ºÜI“ëÊYÍfÌâðljØnP ãOÂܵ”{ò_h’I+EÈž…aÌ*JÿþÖ†5Ãûy85œùð6·ïgT\r£¿.–"=õ’›âŽí÷/Ãå3Q¬ûü4š7•»5í¦Ä¯câçkù’¡ù\Þ_B¯ °ÙÆÛÁ#e4¹÷ª 5É]üpÃŒ„®Uecg¢_¥9×¥2æË%v+ëÁKÆpÄþkæHssöÄR¾ã¢è¶$ÿ‚U”´"óç¨>öŠ«¸tØ&[t9Çý½=•ÀïÞ;àþ«Û'nág sÐEñ3¨ jÞ†ä4ƒ»?ø•F0ylÙð•;ò/?[‚¹¸ÐÅ)8—ž©Ra©étšª¨~ã1…‚{¨·î4>ŒþÆ›ÒmÂ-˜±N:~ÂÊfU¢^7¡u–tÛõ×°¼î9„΋6/¦g‚ a߯©—òÇþ2ÑòPŠÙ[¯¶ÙÞêÞæ½“,B;1q6mt4†KÍæ¶“E³qgqà„îHüWŒOÙÄ+\éìAj(ø >ŸzÎý«ŒG¯‡—P-¡„ú»ÝÞ ýêbÄæ^0dWEƒxÀ0èÜG^=ÞÌ¿úGžGñ«¯èÓËDZ¹µg¹8©É¬$ z¾ÉƒèüO`Zb€jr=0‘4Þ3gÔ02"°¦ õ祰 ÷AÅÃg4–‘½×`Þ\oâfOÊ §±%M½0jS;tL[¹Ÿs†ýËŒ0'¶/Ü[Š »MÉÓ=cXiw98^˜Èúš„ØØ ˜|ÖlÌ;ƒ¿ÈST…»¿+ äß:ÐëäË Anröˆýo̾McÒ‚ðžUwÄ@së)pþL‹aÐÔSpB‘Á™Io0à^w'¼™Ž ý,k @Q =¬-°üÏ{κr'5–¸GÂàñ•³TeÂEôéß‹~íQõv¹uu 3:x˜…ä¿Â­e8“`¶Üõ¾RpbÓš­˜Ç_’¸™Q‡@!æuÚ–}uªä?·®ÏÄ"FXÀ  ½cÁÇ僣H·– ¾Ý~ˆæö˜á†cIö—TbœSG†þ¥°e×úQ$²‡ŽæàYaüò=—.¿ZEf¦ßÅ´S.–õÿå…ŸœI#ö·]R„³Ër¸ËÇ}ñ V˜·qùÇóäЀIòäw¾žƒµ£H“•4°\,ÿuTI“o'–Ñê£'ù¡Îž0“¢2á‹H³ÏÃôTYlÍm;'ì¿„4ïaý¢—\Ú©ÕÔzì¼Ùq…7z°®¾N  |SàÏÎ0rÝ¡¯×Àò»ÂÄÿp>ç/§…Õ bHž„ÅãËNQ2SÆ"¦«Clý$’>¯Ÿjj psªG¡ÃÒ6LñÆwòo¡ÏkÎ<‰y­=Ý„ w­BÓ¿«‰ãàþ¥¸Sð½~=­jýÈõÔh°Év.,j¢"q·OÝgJÂsaç({¶í½ Ëœ¤Å’£SãÀŒ‘˜¶²«N„âðx€Ïcˆ×|xòa7î:îçÕH¬y— Q·À¨t2³ûžØ—¢î³J8µÉ’\9B{Þ¥ñ.6f Ï…ÉÁÞÉ$äwÖHþß>Sþþtõ·C86e:é‰åú,%X_b Wa¯ÆæR)«q,´²…ŸvÙu€ÛòX7iù¤ó¢Ä‚zF ν©ŠÞ‰}àû7›Dð9»É+É9«ƒø¶f³Š‚ï:xþd:¡wó˜Ú{5âûWÒʤˆ›»-c½à„ÀtŒëqÜŸ‹¸¡æ§š¦@Œìžâü‡eðÛì< ?Acß8rV/q+EàÞ…8°?t¹£©X~q7–L›Iꪖ™(°Ì[ è’¨ «Ûb^Ì1üZWHO£‡/• è‹74*R†Ú¶ŸH_à•›·ÁnHˆlx¥Àö¤À% pö¶c`·} W3:¦þÛKkÚºœ›è {4–¡6™ yMÛ¨ƒ™%y¬n‰‹R´™×P,D |¹ÿ´–QBƒ+té=Evî‘›OŸZl„¿ã+qð„I–bFµÑ°í©PEåèïŒû{bÉq^ø\¦îAÄ¢"ØYx'Wb6q9£™‰#GyŠè›4d-yõ2lø`ƒ3r2aWó%4› [|<ñ“¸c똵9$öí½È½}r®bµõ$rÂóœxQ ¿Ë8½„çðªÓef(§²@7å:< â±è™Â¼R[pðmÚÚ6ÑJ"Å]¶P&üsÓঋùU(HÜóy,¤hŽÖ BY÷GüñIŒÍÍ¥‡¦Ñ 1mrÐ6õO°œµõÔ"ö8KPËá:¿µã™š»èb9í;…ÙÉ7f1ó~Ì|a0wP{]Ò¾ðƒ|ðÁžwôÔ¬&Œ¿nÄ4ÿÈ3£ Ú\ô­rÜ`¨NFÇÕáŽ7QİȀ۰|/Ñ:ž ·Nº±š©KèWSÌÚŸCÚŽ¿ÆÜUü÷ Qm‰ý¼‰‹ŠÙ!WX/²­¢É÷®B4-˜BŽŽâ‘q™ÄÖð [û¸š›Öü—ûÚ0‡$!æþ¶ï¬Î'Qø'Ù‰EèmG³»Û8˹Š4ëÞˆþ0Uc¿;e‰@«.¾¬•&[ª’8­'©PèèÇü÷9¥¥n\L7$¾HaC¯PZK–ºàвAXÈ{ÍN³Âcv^ì×É–¶ï3ý®ÆÜiÃLÖ‚pec’ÂBQÔÿ0hª€‘ùJ–ï›FzrÆópvd (w_„&YC&aeÆÞ—µc‡L ýÔWÿÿ5Ï; tᨙ6ù1Ùˆ™ ïÁ×¢Œm «øBî»Çâ7³á|øÌ…sfµø¾û39Hb"n²­º<–«Pdz+¤Áÿö“Ú2Ž™--ÄOAlÙò†‘óÿtš_^‹þ2Ú¸Ìi š×¾ç™þâ‹HA`tv0Žm¾[ ¦û$ÉÕþ_4á.…)·…©É†u˜ª6ŠÍ\ü·Ô¬ÁwsêÁöÁ\˜¡Kµ+·¾°*é=èO€¾®øôF…|/‹Dçí¬úÃkz{µ-L-&.æèhr·È0ÑçÐÅC l61OÕ"ÈØr32º¸®.œû‘>ïUa6»*!ßw:~'Afl›C^¾˜D‹2³A4«€/Ú_ß?–(ê…âÀúÙ¸ãý>\çæ >ÏžqÇ«Cä³!89»F•½¹ÿÎs=Æ_dˆí.û@úùnXƒT¼´×FÔƒç¯É䤣9úçîuEm6{JÈÄL#ÛlÙ–’¬|ý‚sJ>ƒò)P¥ë@•t§àyÒO;ïæ‘gž¥x2øˆUDþNà¸ïîž9œÇÛwÿ­Ãú‹Â1´Zš‡Ö=›`ÉÄÕh=ý× ·“ÙÜ©ÇQ´YÜeÔXMè¶se2¨©mæý+ÆµÅæÖá*‘¬Ú{'¼˜UÀÏ4“Û »÷n‹Šc-Q¦³ã+þy é­ ¯5/`Â2 ˜ÿnÆA ¯5ƒ!£™P)V1ÒÿÀÜE·ið[É̶nÁ—wf‚…XúTÆP™›âl 8‚ºd½¢·ÆvqµM±¼Î4›ÀÁˆñ,jy/„m9†WZBÙ3›lÖÔW}ÎïÛ23U™ýo<ªHðûg¶­+ŒÖ½yVa™Löå™üwC0¨û½ÅOû/ƒêÌ×`lÀÚ7 RÙK„ñ²9£t~õ|®bj>Õ“d+Üî—˜ p9ë0ürPa—¾û, víX‰¡1Y²É›ÞZE_íÇýyW—?á/ÿ]LE¾ÑêÚx~¢zÄÿǾ ŒcíL}†»ø¥ú'YñÓGøýò4ÜøÝŠÙÌó†Ç?¹eÓ*Ùbi]8[›KJV¾€¸?]è{I…pŠ«À/î9¾p $Ü!/œ;Q”ý˜%ÃþžOयÛræYÚ¬rÜñÓÉa |-M#–»%Ñëç4r$ù/êî(‚ a*d¯õh3^ΪW/d–ÆO›èïk04FŽT…¬ïË`‹ÔV’!fÈÞð•u Ñüä8¶åŠˆiùtÅ“âøéDè²%úëÄHùø9Ô“7åÃáö•àèÉ>;­ RÕFøOcÙ7zx¢#³k —‘š?èJž Ÿu¾%X“ˆ©Ûرoù ¾/èU Æôbò0Ÿ Òöîg%gxã?ßà…¢wA6­mè¥~¬‚ÎkÂ.é*àCï>Xu0ׇê˜T†9R÷©Õ†¾ ÉáœoİF¸(üô\ÕPÍ÷“õ†´Ÿ{ÈAgWx|*<—~ŒÏbãAš4cx€ »ãºŒ7ÿƒ'‘Õa0÷èG0ªiÄœ?ó1žFùÞx\Õ€5¨i¡B¡¨7%Ããø}ßLÉõ“»Ñø‘<;zz MÔæ˜z³ñˆÿÏ”¢x›gÈÕ\e®“DHû¦S00Y„>«v'û%òÙÉ‹ÙãÒyÜ~C!–÷Y™»d;’Ö°ÇW¢Ðs™ºõÿ¹Ã³|ñòæ¹Bkq1i£ê¤-n'ÙÚ×®þíØ"s,ç5â­»±¼F„ý[&IHÞª8#¬?ÚǾês™7BØŽ/³éIv S ºÃöª—ƒŸµ8U~U_õŒ;EõÉâd&?µò¬W±W÷mÐó”Y®“IRÚ$ɾ-Ü>w!ö`ž9S{ÖÝiÅ[_óâ¶ë¯›Š‰¥ÙkÄ’D¥XJ×j®7®‹©}GÆý8È6?ZKÕdïÂà9u8?Ý Ì?•rMR‘ÜÒèãØQ¦Ãâûg©?a¿âÙ‰/ö,ø‹uÉ wÎàÔÆÿýÿÓňûc¥Á`ÓU<áýã—¹°L×ýäOhQô: Ÿç3Pñ«dõg„0ºô X:L6HqxÿÕ:Ü쵄X ÐgN»à¼Ù$Nrê V-û’;ø?Ëlw"p)ye·› >aÄã¯4ûE n=«MžíÅ;ÝuäŽ4Ò#ŠÓ‰#φ¼Z6»L‹5feÒÜŽh:4Ûý¶‹°sÍAàÔm”ûâÊžüVûe±4¼¡1¿dqxz#©b6^¤†[3þ$)9¿ÄzüÉdË!CÌT>©â²L~,Ÿ›2 þë+=*6%œµ™ùj2rþ_ý‰s õ‡/Þ|8Ä}À äÉܵ@ì<pù5šì%¬ÔjxÐê•DÜù3FžÌ>$)³;Ñ@&ò ëâZœ!(F\{î¢ìÉôðâ¿8%üݗ³,ëQœ¥Yž×&ŽsÏcgãW¾ß7qþ >Ç%ág‘‹`dêÌŽýlÄÊë÷9­Ð_x¯Â–i˧òJo8²ëzm1_ˆ¼ˆ‹dÍGSqÚÃæ”O¼}7Éè õ¥'É¥Ãw6£»’XÄ#GTZü^ŸµÇÛ)á.UrsžHîâ_»åuÒ{FôïËý7Ñde|ª2$úÅœbødö*!†.Ø™€ñs.±g9.¸°Ý‰ ½‡“ïÄSaôú fjáÈ&ܦdÞ/x0Q“´n$L–mYu™§åI®­è‡-mþÀÝÇäxb¬ô¡:ËŸp•¿\;߯~D/Ùd‘KjñlGg::0ÄÓéñbB0K¦Ó TˆIDœõø_ntí¡L~ó·PXê[ÖwêèƒôlÉF&¬'Kª õÈèÙæäņZlP­¡®,¨›&ùÌùCü®˜%WÏ)©\P —°™ÿ]‹0viuçHþ[PÑÌË^™Ž£m¾ÁÙ¥ëIªg ÛÃo)A¥Çwp£ï8vj~$TYo‡Q Iî96³)a£¹‹ÛŸ¢Ç©—ˆ¿´È!ݸ}Z t^™Çþ¬šn¾›1ÂQŸÅºX3]5âÛA/¥?¦Nêpz¶,SÓ÷EåíòÄg÷yœýà<—|=2DäÐþènSðDŒÉ~OøTªá¼žÄîµÀ‡S´Ðëª%wÓ6ãÆg³@íòO1 ï[öÃþ½­õ_Uz™ÏÝA–·k>ù˜A˜ŠŸÛÍs!Ÿh¢‹>Ü«”…qäRµ|¼2ëÏ+x©¿TÉܺnðml¤*‡•¡Wy+8¦+—a"rÆ•ØlGoÄsŽ;#á•ô&bWY1Ïè¹-­XÛ*JVæºCÉ4¨;°ŽMIu'±³PrE4‹ “3@\ïn8ÔÁ÷½¬H$߀ì‘9xÙQ™¸k|Ã;~N±òôÛ €¡ÑhØöa&F‹ñ;kµ‰ZM |wuåà¸â3|ÕútÂ’ñêŽIL²±6¸g½¯²©ZD™ž,ÉvÌoÀ ÷0üŽ­ḵÝ\‘4õGܽÐUÑ1rP"øäŽîƒª«Ó!dëwü¿h>4f,ÇËOk±7èG;@Ò\]nχHÈ[Ç2cmt+Vñ%—c>áê3‡¸› ‘ eÞÉÕ*n§×–ô ?ðÌÇ(yæ9v}# %Ne(_äsxY>nc5înF^yÝâ'~”Œëý~dð Úøé£ì>_Ònw“§jt¶RY&W%˱¸p6¹Ù öI±–tï¨ä ïYfN=•$ï`éMjd°üa}÷3}ä·`ÎRÄT$Š{¾>Ÿ(OØ:Âÿ ž¸c³³ð¯ÒBÅ/°µ»çlzŒS|æ£Hž›7Ñ[ö?Þô& ” òË{xWok’ðd}ËœˆqziX4€Û³Ò Çyÿ¨L"\õ h z2 §5?ÇF¢«zàD×xâ›U§ðÐ^uÎöA íÝk‡%­áp £-³ª =ÓÎ)K² \!Rg'r8v1?|òofâÁ°·Ø²­“^«ïV!þXzˆ>º‡j3ß¡|T&x™ì€Òq[PÖÝž¬6dn§õñfÖrò½ÂšÔ§Ý©ÓéиäÜHþ“Yq‚VÌ­­ 6V%ríP­g1|NܪÛ^Îäï·ã¥°‰ðY[ˆ‹ð$·Ž‚ÂÜ;þä°’µ!¡Þ$ñg95¾ó’!ÌÏ9oƒ¼øhjk‡+/K’‚Rm2v7A‰”îºåct’îÁƒïe06¡‹·ŸÁ=•»€ËJÛxRßWùY0Bù '«Ñ„þNûY˜Ñyé9´î2n‘‚Wdz)º×A‹mΣ«Ö\¤ý+Ø+oa6;an«ÅÒµçâ홢lÀg Ï?†F‘ÐrýÎYò†ÎIta ÁÙD­ð$Ñ<âÿ*ìUZõ] ÷0¯5Ã95z*Ó9þ Û7¾g±MS™aÝc<^–†“$‰éYæŸûOÆ„KW 7–Å'žÄñŸó¸xøN)kß,ËŒýBÙàÍP?|!KbÎ;s&b½æá‹ÂÑÄNE™ˆªKsÛ³òGsp.ÿïøAk²v—³Ú¡A\6²¦hGÈ©Í"‘Òû¸íÝ]Ø}Ì…ýT,€ß.³á`g/–ª]fBü_pdZNýí× ÅÛt—ßþ·ìåµ!òø¼ ÈÍ0d± ‡Éç%Gدµoáî)×¾â'6ÚÁŸ4Ô‚Ýò¬î™=?©Ë•—_ð÷¼ׯ3Õ±ûy“0m«ÑhofÚÎÀÿ@æïÃvÅ;°PãËÔª?¯´yCËîXrÏ_œÛ&;ÇEØïµÉáÕº8±Ò~…Øõh}v¿n+ìÎ)[7?€*›r®ir›8Ÿ4ˆ‡ðw%Ž%C'Cý£cT?ìM ÇØÉã ~bÐlü{SØï•DuûY|jkÄ~†!n=M?g¢Â”°þšKv k_‹ÌQ+ÞSo{]"ùz>ycu{vp½s‡` OœD†kµ÷WÂÙï¼U!·þ2±øãœN8Îß{MWÔÀN·]üéQŽ¤Ì»Wïß„¿B h«A"½óUŠ<éüßýwÕîÃ0ÐiÆY®²!¯×Faõii¶ü ðn'sP¤«Ø– •Vu0ù”Ë*\ÊžÛ8S¦sɃ¦šòon_9ÎˉW…hx¸EŸmݰû ôÈÜŽ œì2Íê¼u<{®û´B'±±v· oS]4õ,fi–eïpNý|ü×ÐmÞyÙ%Édç•6Л‘ÄÛ‚Ø4å#¬âO)6fš°B|ߟE¥În`!ƒlûùFÀCá°Ð-- æbëÒ›è¤àÁÔ?¶ÑÅg}°¢P€™ìþG5[|I÷·3à¾óD^?‰W^LÁ8Ñ úd7ÞØSy’ÌQПҚÍU*ÇÆƒêlõìLú§°™:¦€K ÌeKc?Ð%‘‚p§í=}¾3êïyJŸ.ôf÷ömÁ¡Çgáé-öwQg•´Œð Ãßô¾âéüÝ(•ð†wÜ?Õ¢|`œ›du®Ð±!»íKè€EdçúàÏï“ Iç¾…[¼ˆ_¢1e‹+Aû¡›\e+~ÑY:ïê{O¶âùÍ?a›ÿ#Î!*œ[Ü>Äx,™ÔŒaìÑEðÜ¡Õ+§LéôßÔ¡EÕóˆê-2¢ŽTÉRåW 8]¼÷–‚­F[°{`&ß$0ŽØº£6³`"¿D™bS˸ ¾ñAhÌ;h7]ÀâôÖYwRÈ[DÖÍ]Æî¨4‘×›'P©’ßtKa,©þ¼—šÅlc³wUsÆ L™Uq n¨ånÌdþZàÚöç®›âz"ú‚0ÌÁÓ³Îr%³ÒØüe¶vð(§d¦A¾xqdÎõL¬² ¢Kª ´ $òèëkÉeÿoàþ`ˆû7þ ÷\k4xþöe¯ŽU£ž$ÙúE” ¬Ì‡;Û’þû;l•€1¯‡`Õ›ô‘çŸÁ‰õW…Õé€](´-Ðgò‹‹àèS3î •=Ý©fÂ&µæ Çî R»‰ßV*àÀ¾ñÄ¥px­üŒ—"óqÁ„½`­7…5|‚/ë~Ñ—éè)Õ«|+Li'Ö_@ƒ¾Oáç®&¾Àáô("Þ1iÚOcUŽ÷àÆîçé4ÖkÉQ͸ThnÞu~Ÿk†“¼ñìã•\©­g¿`š¨Jz6Ê“åʳý´‹&ω×áÍëÕpïB<>Õ-€2{U¦ûï¹õíI6,:o=n½ÌªÀ…¼^Lor„¡«Z#þÏûè‹ ³å8—­÷8(Ë¡ŠZóY¸Ýt>CŠø±â2'M`'íEÀºÂdXuGõ*/ ü…Ë–_ƒg)§ÐéÌFxœk†KŠš¹ï…=°Ü=ùï6g`ö §Š`pá1x¹k€±ç5“ÝÞµŠ=ž;íÿ`£ü;˜uÄ‚ü3“b¥~að²m1š~’#=²e?…4ú×ÁÁ÷îËša®9•ÍÖ‹‡]‡0’­Âš}\‹Îv"d˜sYŒc:“ܘ¤]Ű?Z’ßãñÑÅ&¬¶ÉĈ˜k¨WwCߘã07z™¥ÞÈK´Î¬·ðìD›CFÎÿˆ ªDÁà­Z††§fñjŽ,"ªõo©ìj3ò3„°· ãptä VõŠ‹7Ôe ‹F³VáŸK #ß…LÉs%2ãˆÝÇìur|7Kg‡ú»a}•0;é&Z>Q­?'‚2Twø{Ì)ÝþDêl!gÔïE¤6Å’©UÃþeÀŽæÓ›WOb¥Å 2ý¢3j∮6ñ~¸ˆÚ-$Ç%BpÎG¼</4Öƒo1Þùåð|ðåÉflw™Ã<0' .¥8g•-UßBIwh΋ƨïâT.A‚ü0¸ŒÇßèÄ¿¦ólpÿÑ€>¤±ùXÊ<õ‡òl/&nú—3p.vÝøÅ½ú~-ŒàfÖ°¼"Cj»7Ñ5e÷0ë€+º¯Â¦l\ÅxJÇx©CÿžRT9ê¶·pÙé2|$V ÑwfàÇÓÛQkõ0'Z°wФ#»-JÞÛžDK[èÞ¸›gl¢KÂ,/‚ÔYMÒ4ð\]óàÞØµøôb¼¶‚Ü÷Òò¡(²Ó'ƒÄŸ Ö^ÜlÌÆ\qQ;\›D šØÆÓؾ“³ØæÎ!¸Y%ÀfõýÓûOð/ÍÕÁפGôÿ·+Š´ò–;:êMõ5ø&fc±ûÓ(wø1ÊÍÚûß F-b:šdùõó‡zs1«F‘»}~`ŸÛËÍò΢NÎÂZ]87sTÞҀ˥x°hWL'9 â… zÄïe,N!0!ï=|n£ ¾êœ¯§_­ËÐêL$ÞIMÅ$·ŸXrÐ>Os‰ìu¨²{&Q¿¶„ü) Îí2äߥ¸2ò¿QJ,’šÑ½%–^`7¹öUѼÞipíÂY\‰›á©Ðk¸ä“Ûû§ïòc8m}êu‚'õ·‘¯TŒ[lÍGòŸòMö{ƒ󿳇¹±—pñI_­¼c?Ôà^Õ!:§QÐú²ûV#ž:d°€‚N]ôù/bÉö’¤)¸»·NòÂ2,q+¯Û±gD@Þî7—º| Ù«„ ’yðIú;o÷¿$r7Í·vµë£¦sEX¤\ W÷‚nÓ¥säµ ¨FØ¢›;á|XÔrsN0kZlÒŒx0éæêÌeA¿ÌÉ+š]]ÁÎkÁUAÖ0{ô]܈2æS¡ ÝŽZ ?£5E±Ê“ &Žf³«9,ûž ¥V´b­âˆÿ~¥Î7pu‡=Ù—2/¿íÂÐ^7$sº§åÉÍÁœñ…Ú5F"¤oÆOØ¢R@C‰d'µ¬HêeÁŸˆô÷ :»¯†Ò|Q®`ÌLÌÚjÌDOì‚Cª¤ŽôÀeâ˶k£MÕwH›=™ÄÚŸÂÔOÏø¿ÊN1ƒŽ,2•$bm"xÁ@׉çÃ÷¥dôúid‚IÕöR Õ>îº;A¹ÄKHJs‰UÕBx9טÚeõƒúlrs"žºx”ûS:†%§``e íã‹hî~ f¡8‡{>ú8v}Õ`<˜7’ÿMŽàYyÍ™ÖFÑkïcàþe¢!z„^­R£©‚LþØnü=íßé¡·zK¿ÎS•ó|¡Œóhí'H"…å¸ïuÄCù—wß‚óA‚¬úgž~ðŒÚôçЫx/Í£3d=à»í¬²<@ì…x½p<ñíÞ¯•ÇYOÀªÂ¥`÷òt`×)œ€‡@0÷ÔÕGâ=˜ÿ¦9ÃXÒ_øšè„Úp÷Yΰ(Æ™ÂOx]Ú >Vž»Áß¶c„äpñÜãA ¶裥äMî¼±>ˆmÖe²ÿ÷üó§Ìj¼rî>Ê„Úý–’ʆ²®¡V†Ä0ž‹žãÖrþ\”kÖÙ®‚øFr¤¢î-÷ÂõJºlœ|4Þ|ãÆFÍ7%§ziÎ>Í*²N{R9{Ÿèy‚…l,zé›ÏÎHÐ N½Zóqmº»¾Ú¾èN@_÷=,Kü8ZÞd§ËU`~`J‹Že½cìȪßw°–w‡­4ôíEÁ3¸]éœù0™¼[ü žÕR‹ñkÈâ븩ÐMÜA3ë0*}UcÞ¼ãOÒ^L6ôå‚NîtxÙºi$þ5·³ÏžÐõeìqÃÙ Lð¹ù&V’ó\œeaÇ[F?JÁõ埠ýL6ú&°îäï*8&ó ~eàòeª¼öâ¶~ò}¬ÐMµ6o©ïhÞíIÃÔ ÏU™ó^2Ù·8¥¾äcÜúM¢‡ØŒÐ,´ž@wÿ‘e¾Ã~õn9‹p‰ ®ŸÑëX6òrƲ“ƒØƒ¨¬z‰ÂÎKñ–•3i<Á°¾ãäº9p[§ía»©é†¿àø§6Þ‡0>Í¿Ÿ´½OÂð}Fìq u?¢ÅîŸOD²d„ÿÇ49àšäLÒáûŸ“°¦r™y8=U0í²3§)#ÉÚó¸=S/ã?î÷¬%Äíðþ&;î•M)|×ú‚¶‹àª“>Ö8؃­q%ì°á’MtˆÙ-%Ô´«“|@K¿Çrûº9ø”uSßäS~Ž-oáõ­‰˜%OƳ‹‰E¸ú‰>3–ÜÅÍÏÉ,d²~ElU´>ŒŸ#ÁöøíãäœÄ¯ –ðFI}„ÿ®ùdàpóäçðŒWF“QIüaòà”.ùú¦¿«è0ûQõÜâx5¦i¹Š8§|¥:àÄ*W倓ýR~˜™0¿'Œ›ìë[çkAá›< ãIþ~Žƒ£—BÈCÇ6î“èJ²v²3®@n§w§f-I­?L b”Ù¢óÃÚÈm`‹®±“g*¢péV=g4lKcŒ!&6éìÄü™è³GÉ]èEv`²×Æ_5†M;™ƒŸþ²·Dõ‘7îr”$»ŒÔQT=lÈÁuõm`þ6™M—ˆ¢Ô̇8®gC. –`8ÿŸ¤ÞðUßÂ×b¦t·„¾…¬çw ÜÍ€3Ù9È»÷ÿõN6Ým:kS1äÊÝhÚª8HÍÞIŠÚ•Pµ=´ìˆÂ qz´™Ç3ÿ°u%C•üæµ$ D¾Ókï~ãׂ ðÒbü\|¤ÖÓå Ý5U†ý$=øbq(Éÿ)Í.ÜT#] ?àòßpÅÚ·¬< ¾w¬ñŠwK¼¬KQ(ÉÚoÀ›=–‹ŸÂãOZ#ÅfÆ®Agq=\«äClï…³þ×ÿ3þþKúÜv'~¿¼¶'~„õÁ{™`ƒ!íºš›O•X÷{–ÂÒw»èƒ˜³<É?×áMÑ>ðë©ÁKQ2Ô$IÚ·'Föa@ý“@>Øš<8}ô·s…؃‹à}»$9L•Éó˹05< ^õ4€F zNË…š• ”ÕIòå÷§ÒØuP;C„ØòpT®sÒ;ûaÞ±sK›Mê´‰bW5ÉxR_°Š”ç«ý‹Øþà@ÀgsSÞá-+QíõкÙÊ©_†¼.j¼5Š ¥'i…uÜ«¹d¦ÉhLÂsNè@E@ú§*×ñô8мÓ!Æk"Xqé2дV¦—Á?ñÇ-þ3ÙSAiv›B®ì|ú“–²Þ†Ü…U")/ vÖcH€a!œUwã-Ü`Bú® 0ÞçR°)‹Çäþ;0®M›Ûû( ÂM~R­Ž÷8_‘c“éu9Æ&Ù°Ämx§ü:¾<æMæÍÃ~îÁßé¤Mr:Ù{N‹yY “%¿iªU|l?A+”`ÉR7´ Y‰¯s1­´ÿmßÈÑ$ÔÞ¡·}’rÆ=ϲpê§%Ôs…_ÆSï&l" S‚ð·ç=,ñÛÑÊ;±Ñ;•»tò"hubšvŒ¹±u$þs’ŽÃÉÑÊ8ók()©œÇvÈ܆oºöìø¹Ùd—}?4_5a%ó7ÃxöÏï0~Ù°‡|õ¨{ÊRrJ¹«0 n4ٓݲ3XÐ:1›×ÌõÌ-‡Ù!éøõèaX­ƒ©ç¹Ž¨YLúÑ\«_æh†Ï(º¦ú4öݹRç³å:u×ß +œ™QË_”ï÷—Ÿ!ÖÈÑ=k¨»2*EþĦq¦¤Å­ ƒœ9²ñá\“ùo^y€‰»Ó©ˆ•9[îƒfG¡*ä Ž•%'NžƒY½èþC aì4fÞbÇBBV3ûûÕ#ñf3ŠÜ5…kûn¾IíØüÔÏ5tavï\îԵ͠’ð‚>ÖK 9L=»Ž´ªöƒÞ)#ÿ/½¦,Ào{ïñÛ‚˜ð“•ìéÅXü5¸ƒù?:"Ñ mx<MšÎÍ"kêo;òñFÞØäŠŽÏÞÀÁ]çùvV1è7~““«`}† ëW:>±^VO¹xcrÑIœ¦èzRÉéñže=Ì()‚Ù[µˆLãXè¯lòS³&ºkO 9»ö—õ_Ïóx¥=”ù*_á²¶žøÕ5plâÆÂдþ*çýr"¢Ö¾þoؾL‡bønktÉ¿_aü©ZÜ«lË5jO¦aOêà˜/1Ÿ9ôòbàxðlæx_‰ æo‡ô½§HÈÎ[¹€½p$"e¥ø2î&÷-ÿÈNnYŽ+  G¶úÄ‚ˆGdhýÁå'Nsɽ GyDf3 íjö’õúRä÷·ñ/ÔJa£M\eâYp1RƒŸÍ\ÞÏ"^ƒãŸYÀ-ðT#‹•Åx_ß8Ý=£Ød ;Œ8Å[wGðϥǬkkp§ÝkÌ9tÄwãMqW¨·ÚMnmmÑ2þ²þ;Ë¿ ÷jça¤¾Ù9û.¼õ_ÄÛ¨®Ãríw±ƒ•’ìóŠmØ~â{"-Æt Ÿr‡v„‘ëWÇ‘/â¾ôY@>QJ$g3H–Ñ|o¸Œ]NpÂsËÙñ×, ¯•ÌÊ µ^õë"ÆEÎ@§¥ÛaÌ%R·u‘ŒKÞ= Cñ•Ø<.•}¯Œã#v²ÿ~?à‡ÓGפÈ[ÿ{¼ý[âIÜé0ÐòÅà=ŽÄçc[8c[ÿÍã,ü;‰ ½Ûþó/ƒÙ]=¨ì/@Q“Q¤Ñ%=&ª1¿®]h,BŽmÒÁíËîÁútgÖòTÌ…íy½ûlHÖMs²Õ±‹ÝÉÔð±„'5>](€ÖL¾;˜ŸV¿†°¥âì´c9;mìÍ*F/„“ÒAÄ¥áîæñõ`/šÄØÉñmè¤x _龂Ѥ |’%k;Ž3h8oOâtvØ^ƒY6IÂX×(b<-‚׸¨”ªŒ g9Ñ'Pª ·äOž5ÍNaz{Ö”ﹴo®6ytP•˜ü϶üRdZÚ#ö4:'|Âù§u—‘ ë.Ì^Y”#ÍOÓâWÕ ÷)“G'uIÕÑØ)aÃ|t¬ˆqìzó¹0_b÷V*TÁŽ ³hO_|Åv÷0èÏ bÿf¨“ÄÂwÎ,¿G–„¯&©ShÛ"võsÍ›˜Äfê=‚7¯Û¹O‰;0ÒWŠ™–—ÃyYQ–cº欴ƱrÓÙ†Åæ\غО¥Ä½Ý>.Grdôœ“Ø’M¯E±žê‹Ø¶0DšX°t'SvúàÍ =4ß8òR¹Û›½YeP7Lø4HŸMD½{…$Ùgö~•·ZìáEEšMßÔŸ=8b_¿‡wSÙö#o‰zÆTâä‚ן-gç5T?ã%Ð:Dü×¶cJÙHwÐaºë…Ù?îÐTiÒ$«Âʃ—ßmøÃØ¿µE_ ¶Z?¬ó—·®ùúKKغ¿n¤lL2NØëÃO^Lú$Ù"ï]ðlw4˜—} yڷŇ»ÑF’wØ¢ÅÚ÷ÅS„¿‹ÊqbBÜ·µÃÚï^Än]ip‰#Ïv‹à×xO8Öy‘«é{O\³p³`-Ó´KÕÞÝçTÚt8ßkØøQ÷ž¸í51#øÝ5gÁ„Ǻ¸üø9¬¶Ï#}ý58)8 ^Œ%öK擹’í8eT ÈÁ’s;™Áá‹dÈ —”«.! ΫˆXå-ZäÍ»dáÍ S°Œ‚†)lÛ%öa^Ü8` [-©jX Üf*ä„a#›×/ÏÜ–Ldú¯;@à’W?‚•óPß9Šñ¯ù³6¯xÇ“{H™A ”ÏEb§w …¾l©/¦в,$Åœ¾öˤ›~_"™s†µRŸ-kð:LôÇ䣵wóPm& Ö¨ôçØI.&]sÙo«Aþ›Œ%ä"{Ë$ÓFð»RŒê¡´¯I—}Û° †r2Buh¯—Í-?z—“ÜJ²o)0m÷y¸f¦.[|É‹qŠ‚¤~wUm§Kol†û‰tüK¼ö¬£Òå FdÀ¬÷…fQ³?UXü· k>˜áPzØUÖQ‡ŠÇ¸Ýàô«¥Wc(½ ·kO…ªø OØnFª ÆQoó/˜óRlúkÉí—ØÂJÌ~qƒ£íaûce¢érËÒ“pýšf8˜:Š…JN„’.”©ÂЙìv™“«%=fâðxÞ÷WpÄ`ž;³…|Zì3¢ÿä}jáÃÁÕüÄ&Llm:ú»±»‹‘—÷ƒZ.<ÇíÞ9ÃX²SÝ¡0k” ›dx?íbιñlϽ¿€åÖÊÌ;c˜û§YüW[̘¦Éöo‰€S-¯­k†UOÙ°¾?%®ë/Ô¡þ‡U¤p“Ùò­Ô:éó}Ìè:= ã¬u-˜$ùO=?…+&Ï'?|%qAât˜ÿ!¶þÀñÇ–!•íÂôÀ!˜a1´÷ ±õÏŽsK Œ`ʶŸ¼¾UcÁ½é¸w›®oÇÏ¥ +_É;2¹=›Pœ7ˆÓÿˆ¯ŽªâûÞ¦KiééFºîì*6JÚØ¨€‰E7Hƒ„"¢¢¢ Â}%DÅ@Ĥ Dìöåó[ëåûÇ]÷®Y³ÎœçîzöÌ3{4Ó§Òw!<ÿÃ_™—dqïòtÎú…óØŽWç8J—þíéŒHsGä[.8ï^Hž?”À­çp5ïEæ§r0žWI‚Äob¨øÒ±ÊÞDÊ1D}•ÎÏï@¾9É^juû5^ë¶d_ÑÀŧ°yËçbè(ë·sUgXA3)öMÖ_,…Ä^ë fÉ^eW¶>bt¾0'/÷qëbjá¨óB°›q{˹ˆ`¶ è}Y„aÃRh¾ï<>×¼„¿"îá’€f(UÄlös'»vÿe4783…‘ìuÞ'¨´H †ËñÌõÝÞp³.^œ¨ÃÖÀT ®d¯‹Ë6Œ’1‚Úu"Ø3Õ†”|jÀ{Z 3‚wû‡¸z9Ý #y|ìk1ø{3wpd6YµMLu‰û ~:GëWÃ1šÍ®Xz‚öóPƒ-’`n`ÂÒ9)ð´½ ú{Tˆáx¼­Û/ƼHæ…±ŒÎFN ¡›G±àÉ/ô«‡D]È=” '7o‡“sN Á?e0šåN\}Ö1Ý{f¢}RÝ?T.¥èã½n”ÅÔ3Ë µ‹¡OÂ’@o¯(YòÚDœY3´ëPs›KÃùvL–zêÁ«¯é–rÝ û­5 QÚÙ—ñU¸•(y¨S»×{©^é õ~nÐ-!ÑÄ&ÝÓפ_çéð c‚¦ä@•9rj‰/÷dm> Ò#+؃¾Ý(””J}~Ï¥u²AìõñØø‡]´xO ôª¥%™ëY£T†ê˜ÅÙt.×ýf(¦æ6³¹örôÁ’,*JWÐ(/w²¿‹)iê€?ÂwQÿþ#|•°‚ž8е—vc~iZ.Aó-Méºè*îuÐ&å~Ú4ó²mO5Û¾÷/R†#=ñØážFO…, nƾ4rŽà5ýûK&îø~‰…½Í–\©@1â}(‚T¾Ã!‘nhߢKxª’ ?@fô9ÐMõÔn-‹ò>Õx'W¥^üÃã{ìÈÕmða¶+ò‰H•€.úÎOúŒ;‚µð©òJúÏ7â÷˜P‘´}Ø:_–ûÄ’¶q>د4™áËt‡ë«zù&8[}ƶñ>*_w=IqYˆ×!žîú׃†ÆGàתžÒ€EPý´ˆ$]àCƒ†üzþ+£ÿá<$ùVÉ<ίN. ÛÒêÌÿfEX‘}²qÄ È¢¶õTs@?ॲ²Qdµî× ÿ_Ó÷Ôq»dÔt =_²hÐòfÆôœÎÜ¿Ü>IÓ ]lé³<íø‚5Ç®³2EÉÓãÕ:#:~t°s2Ð)j£¶ê^7>ÊZžfþlú„ß“&쯳V×í6ÂØü4@.Ët²bå¡Û¢œD¥l!üPsE˜xN¢mò¯¡ˆ'ŠnÛPJvß²#Óú0ùS2¬¯â…Øe¯@CPöô&p7)°µrDLCœöPYzt\ç…3éåÓ^Púî$ÓÇTÞx±±! ™È–3zvù™¥€‡_%‚†SÌ±Åø‘ÿzEMÁ¬Iƒ¨ª,C<¾§Âó szüdH["Òhd9 Ós/<—xÇNÔÿ¬ýeŒÿ9ÂÑzWw¶BÐ"˜ùŠ aÌÉW"ôÙ.cØÚ­È8(ËÓËÕiÄ*-"õ;ˆJÌÚ¿V§CÞ©¯(xq ôÆå`&õÂÛÅ@ÿk=±û÷Ï#S˜±ùXjÓýö„«ÅV¹`gçt:óEÄïå…þUàí/ܛۇÙücÏàÌQƒO¢élïaظï#ÊõJÃø9¶gZ~ŽçÑGS‰Õ~%*`gIk…D©·øU¤ŠÁà#h»7ÿÂolØÕÙŸ'ðûÕ`²ÅVV]À‡ÚÑÆö ÌrGø¦È }—ë×Cë~é×znZõaz£¸-Žöcó¼$péÏ„ü±ÉÄ9°´Ãz!ß®£^fCMó+||JI:¤Ìvl.ëe—í¿‡oäÑ2å×lØb ©dáú]?²¢ªŽ8 ¿® PëÃSHUÐE|7_†½Q~•Íüx†¤¬žO{²³`£òüÆàS¡ûM”"Cv-c–žÛLÌcá®Qw·ÁИô6º“ý+ápzŒw3CotIÞ‡@ú¯:,tTÈ«¯AôdÉÄü“¥÷Ÿ.c<̬ŸnL†]ç^ÏêŽÿOYZztFÒqø`³”Þ¤ÁÏ`ÎÕÎ0Ì;À\-ž ç5¥ÕúFVçù Üüu+qüfë×><rtYìMxp¤o›D ö°ùo7 U–DÕM£ÐßvÊ·¾Ó¾ø¥NÔö[…9÷9˜q‰YtJ ¿{ÕÁǵ2Ää.9~ô&³råP%yØù'É9Æ’ùÄÝø ²¬yîogU’Ôs¢'}¼}ˆg]7ãŠMŽ4àô0ÓâªÃLib¦-š„ëÄ…ÉpH!*^QÇ…s㙫K—ü÷ÍþXU 9Gq†âÜ%ª“åáÊ€7NZ\…ewr”?Üa¯cÐPˆBOªÎiÞ+Íeq|mHj]òñˬ[ìs[±hÇ÷&rô•ò\FjÈ‘Ž±Q(7\ˆí•`z]˜š F`-Ùδ>ºÉfoµ„@Ÿ½Ðµô$üѺ Viþ/«ñ˜³sV&´8ö0^}BôvàFt[1Ç?ªï:Ñ?«Y™ü‡¸}_"3~g/?nHôÚÑNj>Üœ=ztà—W?ËÓ4‚!Û@o]çò&Ü!R‡—ñÐÒÁSXrOV·ì­~ÎÂßÈ ‰eì/ÎÇim_Í›Ž`ÞxäùC@Já>sê§$¾›†XdÄÉæyüôÙMEúþy/†¿uƒð°"ð8è‡jWß2wÝ!a昽» íÂìªHhßïEóþÄ3–ù{!¤Aï,,G±æ›¬Ùæ )_ǹ0¬9ÁŽ2Ûo ³0ó,§3ã VŸÿ—Zfÿkö&áãhAÊ·¡ „¨â¼®Ë`â]@JzBàÆ~Ò~s-Œôì€@^n3ïr&ùå1V\*ŸU4®c—Ÿ€‹Y]Àfqõ»UhdÐü±+~¤àÅǸ[Ó¦£|5ï2åÆãœ`Œy5°•h_‹cšÜeiœŒ8Zè}Áú‰¸ìM0š+û3*·WRSÞQ¶Êâ4^ŸsÏ›¾cú´jàÇ¥Afü:DMµ t,£‘—’¡5ç#yò„Ý=M‹Þo´ æ?60ã*\ezdÂþ^j×ÙÇÍÇ H«´Vz@àŠ`ô(²ñ‘t8.½@/«yáwµ"84ìGÅœ]¨ÕjU¸¹ê ù´ï#wVšղˆ¯•°}½!U—µÇ»]71µs2=m‚Œ™¬1QLA«k›Y­_Qœš§ æ÷Z<ÿ§†9PgB‹^Ö5Ò6 ú¸“¥͈æ s¬ÿ—ΘïxQ_âptW'œø²€J_.ÀŽÃÒ¤R~)3_”q“ïû•¦±ÚL 8NÖ†_³à?ÍsãñtL[ŠIñW™-Ë/ר(ºîÈ.ö›c³å?Þ»§ÉÄÞQ¢º"ÉúÇŠS(rüÀX¬’$nw±¸ñ#35ì!×zárTÿæ§k ?髷Иœ-Ý…»ìq›ún°ØÁKš¯Ý†ÂâÈ:ÝÜl½Îl‹*†¾¿ñàüM‹J'„ÂýSx~{*Ï«Á!w êú õUNÂákåXõKÍë{à~‘0Ùði6W䡸Õè"+ž`8ùñ0Ô­D/…dáLƒC8ÄçU5á­]Dܺ„ŠêÜâù1©èÞHqfY¨E°ÚRKAÈšŸˆMY ãÇ¡BQ›ûA£ r#Ÿ þoþ£µ;ËÌÊ4€©ex½Âª”è-R††~ÿàr¥•ú]{3»Áÿ¨ ½mãÈ6Äs 7ó¹¶l’þ8¸Ù>m¼Š•¿ä‰úèk6ÕP“º iј }°œeINáp"¨:z/èáL~s41Ób­p¦„ yšr¹y`¶k-l±XN¯’%³îX¢Eãov3‡ $O0yÅ7áø§fgÖjª´ò=·EN’³"@’¹xu?spë9ð}‚$4V´öàÃîºI¬ÛXü7´!oÍU6ï…:Lžû¿?óŸ ð¦JSÕß=?uÔž½rÄ<Á‹Ì_‚ðõi¦ NÇ[§±Jê“mþ³|ñ»Ã~pÜRÂÈ›áÌpQ|»¹cãÓÐùܽ¼¯[Ã<:6—H}’!K½²!’òÒåRÎP4'žIRq„ºÑRv²êYnÍôã㸑K¼”à·ÒÃSP[·w¢Á¯<”ž?É8ûý]Ü Û„]Å¥›òðSÖìs4Ó޾o…Мüé'OTÓØy’~pñÇM|zø[ºã^_óÆÎ PÞb.ü’ŽGuc†$DÇÐi’ýl™ X\\ ÿZ&êßß²p¼&‚}SÓÁßáûjÑ8âÁCSî á‚¡ðïûCì:p¬Ùc+F`ÑùCø4åŸóÖ‘0æ–‡¶ÅƘíÝ ]/r!ã÷ðë#(¹nðïpðÿŸ+~2FCËÙx“ÌÛ0u =A[à»üâw&1ß Ûz$óÈ+Îi¶›áÎÖØñ¾ZŒ|y¤NïMƒÈŸ¯q=Ÿ ~'K×½d/¸gÃÇ€ ¸Ñ˜ÀÞ8tßPe U#4ZÙ òŸ¨ÓËîܓǵÁ£ж/ª{[Ø ÿaFÖY<)üœ]z´Qöåa|R=™ÓֳЧ¨õ`¹™ Ö®à¬ú=øÎlȇn¥ÊIµœó>âVã&Ô²a™Ûê@2Û-`Ô$60_cô#úÄë ¨Oåá¬é¾Î,§¥ø%,}"ÿOšr §î‹ÿ«i_¼%]¶ˆ‡Öq¼!M`Í¿¡Nªû*`œ½ûþ>¸éc¬Î~h¼ÌD²'9C8í£óhã]vÏhÓY’ [¹ºìe‡mp©×Ó]SÐŽhÒ´î?˜õ3 ¼1T·à?2þ§³n^ÅbÅ/+š;M–ðÔeÿ| g¯%̆êe™ð&ĘRļòBøWTÇ>I»Džï» *<1Ø-8Þ+»'<ñcÌ?(æ¨Ð¢ëáT.ja÷% ¶jÔuê“ç&W˜ú/7ðÀQrßS™Þ~’ƒúEñö \Ø<ÑÿFYÁ´¯&™[Œv9f´—ÖÂޙѰ/£6Õ)Òu§ŽÑõý»q‘Ð9æe¸(–nåtF+ÿA)ª°=xy\xJ—¼¹ÎHM+çz«Î†€î™4çZ4Ão× Ë_æ6]b²¿•™¶0eÿl&þ{ÍÉù{ß0Ûå*ŠO™êD²N¿GþcÁd[ïoÌëfäs!d’($«ÐÓ3q˃6œ"éËi=ëʰ+q6ÞU#ÓÝàz~ ŽùûžñïB¶¶Ìžª <‚÷h†®/ õô¼e³¿ ¤Îy ±˜À/ö‰5à-Xźß-‡Ï³ñ~\´í™K?H@>%øn‹ ®þs„Ë£ØçÖ%8«ì æÞùmÇ 3‡­¼èššSœ•&|8%—{aÛsÆ8»š™ÙàO&yŽ€Óü` ·jî)éOŰ¾ç ¼mjgZFJ9ï×8Ò §ûÕ­HmUÞóÖW]GË·qlׇ™0Cô iÓq£Çn¢Hº0® ˧wÛŽcãü 8Ût2lŸC˜ÄeñÔçl93cY(ló/@óüóXR=þÓ@§,º‹¢“ÆØÆM“Iþô«ÌL'#ÜU>‘ÿ_§Uis€Äý¶Æ¿îÀqAò¶´˜èF“ºÓarªIº'Z¿ç’:­j\pUŽ–h®•°ôˆYvJÎg,”?€Ð~;ò°:‹,µùëºû@ N“æ?ÎeþFl$IþKéûµs(ˆ°ÛÊÛ@}ò$¢¤y­zÙ—Im…F½ ¸÷×¶qþ·•ŽDZãÚdÒ #KÒ»N°XǼùv¾»»’üÇJ#°—¿µzð>ûtJž Ô€ûSµðÏfìa€;jJr„m©²­9ݱålÞÖÊ^ö£ •ž²ÒòeüÿÅõlù£àô­§–b’x4¾eU,8ïïдi¯Ù[…’ÔþîxÏwr á´d¶I`î]qâ7k)}Ú¡Cn­ZŠ» 9huw%ì¼Ö}§§ÑØõÖDÓX¥mˆ¤²)QO~‡¶³¿Í/£øà18<“¸­¯cÚD`Î"MtøÚÍ.úx  :;7ÔLâ.Ô gf!gnØv:ël+«œ~‡;Ï6_HÏ ¦7õèZmû3[Èi¬êùØOâÎøË¨C¯ÃÉ—Ñø.š”’¿áëØ/§í*Øi£AÌŸ¦àÖ9†þßQveO™7ã=¦b$3—•вCçhºzÑR2Ôº”Ž8Ûƒ§{>\:õå„ZpKº1SQ'§ëîAïÐMV¡Ã˜¶üžK3Øy¬7 ¯9¢Ç_À‡Ÿeàê¢GUٳǘ¦™“I°¬5YÁѳ¡WàÓ%EòýòrPT½“³áý¨ÕíT'2oWÓ§ª¹8òã4n¼eÅõÕ»ŠªúÓÈ/«P&O2 •×8Óù °£SyNƒµçoÐW<Åñ” ¥]èîI:Ì[Ž©r¹W*{ aü:dÛ<òØ;”ð77MÔKÎg&÷Ê‹ãù»'8ʵ Ð! öíň WhפªY&tÅ/Ðnw–ëè›@îˆÏ¢óMéÀrgówX²åœ¦Nl~_Ãá+©lD{cu/t%Çàìµ_ð„Ìs$.9ƒè$|‰2+’q}‹=dÞÜJ®”27Æ{”uü\¥B=-%M{ËÑxçX’—BÓÊSƒUÿLvVË › ¼@3\æi„$kr- Çð7ó ñ;¸…9?zª Ñ}®¤`Ë?æà“r¼,ýx¯í€=½øElÚæ=¸áœw|&ÙûHΞÝBÜ8«èÊseÐ2Žôt­ ‡ôµQ-î³`ÕIô½þtz©Xn»e°bÜ Úò)ƒÇ@âïeÀÔ «Ø7Ò™Ù;å鯍4ì•· Õž¤iô&¸Ÿ›¸ÿ½íPÛÿŽÿâ 9V<¤ï3— SãØ݃fi°ÂÛ#<¿2zt!D|Ià öJŒùå7ØpDWÉu¢HìPÔ^Èæ'Ÿ„C*Ù;WÍp¡÷OX°²WŠÈ’w…縇>'1T¼íia¿Ë8’ÇåÝ8ÍsFCcIÍ8Çd·it38æÂø5•â $?Þ>d‰6ùçlÍ0!÷mËaþùlúdO†íq„€‡ì޵›Às×yÐb¥Dk ñÔJFQȘ:8Q‡YÜtÊft¾ÂgÚðÍ`#&Ìœ†Cíà&ÿcâý'ãyü0z†‡56ñn;j¿/aæ›ìÀž¬ßPñ`å¢T©æ°,¸ÓXš†¿ZaZ®J˜àÉ; ¹?Ž>‘†»÷Ãdi;jøEÒÇû"“»—@­.î^³ A GŸ/õ¢K߆âËëè¿ç¬Ë5_ç4{"C„z@»n-ÛuG‘z¶¢!ï7ŽjX¾S ÃAi¤ÒË_rÒ¸ª5"äù[–}lcH58z°}é·6dÝzæ1;ºË=>ƒýZ;ø|ù%ÕȀÜê`Wîú„s%uh Hj‡qj/Mà¿åîls†$¹ÿh:µù]Å.~¨Lç Æ [oâxe)]0ãç¨X9~ë1‡)Ÿ³Aˆ? VUäâkxéÝ}ä/ö&¢[ÿÀ§i|P’;JE‘[.-ì©||ò¡óvzcþÎTØj7k—丳?Ïr˜™ øæq$Ë­Ú†rdCÍAÐvë[ WœeéMüKnj.ºêÁü´šLj“×Ñ»éû˜zʤRqñSiZ²Gœ.¾©Æñoñ‡b:M9ï|/w9êfžf·±¸eƒ>®“"ûÏHý¯~ô àU”0ׂñ}Oà/8Ç,`N‡ê ÐŸŸpYµ“3+5QI„)vjd>I…‘ûóénK;ºïðm8SªÁ¥Ýì¥G=(ôLQ”³VchCµóIܰ{ ù°"Ä…h—|2•!$öÂs0ë¢lüëæRµ¾àEUˆÓrd÷¯d[ÂÍËZöªp¨¢÷8,gÞ_3`Ÿ…õãÔ˜]¸µ| •ø Ç‹:AóÜm:òïrÛM3о*Œ.@é·ìˆ8Ã)kÐÀîopï‹T%iL~°Ÿ~Ýë1?!1 }–ÉØ‰3 €³çhü› ûq~i‘yÌ$]4ÓFqéY¸£<’5õ™~+Ú%fKžÝt¡ñûœÉ.;Y4ÓY >Ê¢xá>ìI·'2Þ3€I}šW8oÞߨˆÒ¬›…ƒ†TÛo‘~Yý“yylùÈaÞhÙƒµVƒìÂ÷¹Õ˜®¢0èã;ƒùélv¶ùvàùÇãÿSýqXV<žÃ·rÞ×D™ñØé³¿ø÷+6I‘ø6oG¥Ç_óæ”Ï"ŸˆÅ÷ûøTþ7C–ž’ÈÁÓÏhÚw~úó•6~ÉCÃKí Ü¹€]d1bÏ"ÇÄãœ?8‰IŸ•HSÏo°_SïÆ°Ï#Ø·•¼ÔÜ-Ÿ­ð"KÆ38³Þ•:¨Zeìÿæ®ßžW¯Ä®UÌÔ Rzsíij\iEo^²ÌUa<*xH”‡n·Hg¾Îä¡Þ٪ܯ½‡¹N¿Êàì|ozZ¿ãDÓˆè+zýB)ç6Ô'Ží· €ãÂÉpÍ)ŸõÕe¿Œ42±ƒ¸I1 j½„hÖ[5*Z±“†nQ¢oPÀ¹ûlyÉ‘ÆèÖ+NT ïÓõæ[¨ÇcF°4•Ì{Öƒ™s¾Âu;j‡¸§À—®H“#!äpñ‹2úè‘5qÞ^ɸ™ðù¿ý[²øÔÒŒèQ¡¾z"lhd¶ž4„î±øÂp]±¦å^ý¬÷H"]ßÕ($ÙÁ½2‰Ü®Ïf\¾Šš²ÎìaŠýIöÔß{àþ9žœ‰rO¿€{ÄVêtW ô”¼`sÆoèiW„=ŠQŒ–”'ä4ž„é;ŒØ¿KHÅi†®:<ëdc™[ÜË0²zmõlQZXœ_ïƒÀìsÎ_âÙתªTË®@ÌÒé (^Æ:Ÿz„¢/P£2 î9—°ð—ÙÝ3‰J¿^O‹1Iç<”{­ÇW-|´Iv.ùÚ:ßÒô=k|f&^ã„8µlƵ;Ù¹íâeøgIzµG™£U8®Ø5¢ïp†Ãmxþ$“;t€B¤ÉÑu€èÔÉD¸˜;bo1µŸrÊ6²³pý5}àYþæ¾¶æ´ÿTgív2ؼ+ ¬_O_OÁÜ®„>ÛnD¯7Ú“__[¡IèW+ÛŠ*:ˆ¡ÿ‚1àwsàxY¼°ŸK3瞟gè¾RvRÆœYÚÊeI˜q<ÞYò™½5åÖš“b‡6<¦Ò'¦Xõ‹­plÖ$49ÊH_¸Ç<”Œ;Sú'b%ñœc1Qÿã?®`oèAfO$ËѰ'¿ç”!9Ñ7É6‘hÚ42Îg&Õ“«ZiŒÅÏ]@¼@ߥÉtWËlúÖö ^êÉ™­ÚðåA$œ„fWÅèæBi,›öoï}KµÎÍñûN€5»¡F‡®_¢ù߈üfVo?¶ß*†Í©6ä·H䵂ûã‡Ýç8sÇfâóöH¿áƒ5ï×1ú…ÅDð×Zú¶ ­^p=em©Ÿü6,7ãw/O€“àUçð‡·x“~)gŒÔŽsE/"ÿ˜ÆDÿ»góNÓ¾Åxûâ":àèƒ Dß%‚Îvj&#û èÈŽê]ö„¹{>§mŒ »“%hM¹5ùOß|®gíV|†s+ÉͯATE6ol(§Ö€?ú þ ·;­‰À"þkµ»¸PX"AÏÛC_ÆÝ„¦V;*=ɈXUz“SKH3 Ƕ ô6bè>)1%‘þ-‡@þÙ´¯žË³£˜h‘tRíªK»´„ØÕÉ%Ô¨ª^ð g´M¡ëù‰ ½o&ïv¶éÞæQ[¬‰ŠÃkÏŒ;Wƹ6?öõL… ¦ û»\7¦zÝ/™¾»o¸†Ï!óöô}:™¼WœA¥¤¹ ›.LùÂÜ£¿^%ÒÏï-v¾—œ "âTÁ}o•I_<Í$w§î‚Ú¾nXü2¡aéÚ©à|¨ï¿ßœ%fš;­§|‚gÁ°¨Å›Þº.~UßaÏárü¾ªľòÒ+šš¸ÙZš~‰—p¶JuÁ+ožÂô >™»Ž>¿rj·›5x§äᎩsˆA”6dbÎ_)èN¨êuãܤ¨Î<ŽaMŽhŘѲ)o!?ך±_S޳=΀â/pòö_6É`Æþd›$¦£«½nxª™¡ä òÎk>Ž^d\rgVׯ“fîÜ›Êbû=2p_‚äwd2<3Èg>Ê9ž‚žõ{pno:'¸Çõùî°âÝ·ñÁ,yºÐJ6}ÙDþ %Q¸BÆÕô'J'KÑÿtÑÞðtŽWƒÜÀyxt žGaWÓòÕÏ[j.22ÓÄ_N2W€Ei-,ÿõ0zÝx5)y™¥\áhP óçé˜»× 'õZbЮ©,¦Bµ3pýnCöÁ«uDüŽ ¸Þ… ÓÈ^»õý»fà-W[òa†Ó„ÿoX¶’^³·j U"˜“ÿi—_pt¡×¨›½ IͼgÒc)„Ó™ÌT}XO£D‘ÿtÅS‚øè<ÅN{¯; Øjvé€ÔÁB|5:ÞÃsÆ™üF¨wá,­ÍøÉ¨]q§³…G™Ë…´lƒ¾91‰:‹Îù òkêôùp‡VÔ×@X“©4w«t¦¦±Ó ¡WTýä½ÈâüId]OoJÔ æÏ“øqi ìœòo™šsä–Œ¯[ˆË¯©SI?ʦ(<ÄIó}q,’. Ÿ=_³t3éÈ`ôß'ÒJß T.!;?m"λ.Òr!˜¶"9Ñüt*`aûÌ ñ¡‡o+™ØÁg¤~ªyhí —ø²hõ1sbùò ÝÝòš1LNbs¥í•cmÒ'ÓÌeÏñRbا¬"ãµ#ª¾ Oü<@†¬Éç®αš\Â7¬Ìt¬ñ%ß…\ ÷†®ž Æ$]^´i£S(u‚DT âùDSý©UFw’âB·tÀ>e༔Ç?“™áÛ̱ÃÁd­g7{/û&I A·õäoñm°Î±A§ÖÿÙ?ûÐÔ|p-¯­%_n/¡KNÍvï× ³H”m|ÑÊu¥Øó• ©“€Ãõ(`µf9¾ÀïÜPf?°¼!QôÜçx̳b|ž!G»šTVÒ¸5) {O„fô‰ÀŸ‚w˜ù`˜v%£~h<\×aÐIçÕ³¡ñÇUªÛ5úæoÄi/øÀ¡Âž×{1×oaÛw?ßEéúÓaƯ“xv8Lß\AÉÊQf‘ ]}æ(~œœÆÄQeKXïí‰ÜbÃôÛâéã{z‚xÚŒo`Û†‹¹+ëd Þ4 „“ôÉÎëVüç™>Qóˆ…2ûLÈ×?zràÍâ6ó[æÜ”€1)wˆ3×ã>¹ìÐá)tåêغó(mÑòC½#Ûˆ[/h±äÔ÷éô€Š"•XÛ‚û³jQ¢»㣡ãF0 ³¶¬ÙèdrnAÚ»[Ò-ÏL¹R×_aÚ¯L¸Ü46˸‘æÒ¤Í7,Žàv«ÚcdÎÚ-F4úâá˜Õú´5—x1)P!,ÏVzp$js˜[s®àMoòŸ^ÛI €ÜQ_I}ü÷arA®þ~"÷“0¹0Ê;ÖˆËÏÁ7ÛJ:s¿=[1aÿÕ…z(ñê)˜mO†ç­spéîAÔžû¬ü †B£\¶Tuõ=Á6ëvbN Jé”bÙAÒºL—kV[ɉˆC;ê¢êé$rèÐVΈÞ'vè^;¾ižÌðøÐI-xëo8ˆ[‘_·êœVm¤-—aÀ$œÍ9ðvøm…tšùf4+%‡e©øÄT±îY:íÎGŒ›í49Ű¼ ­½ÈrËÎA–‰}"ÁU;(FØÓ x‹é»ÿŒåL7¡N“„HËÁµ$*q'3byò/Nŧ žpðÐ3¼m\…b®+qàùæ‰ü··Ò–•7ZÅM^ù±ñ€;¹¾TŠ<Œ^F×\Å÷X9‡¿ íºžV;¢Üï ò©óâZ°§R”}Ÿu|È¡åoAȱ€×X:û,Ä–§ófÓu/GqäöA¶ÛÒܲ -X çÅl€õrÏ“†a…öûߟ«‘/'ŸátÿeôÃÁHÚqh;œ”Aí¬ô—3Ý,ÔÍ—…£‹n0*ýî(§ž„³~ÝÅɹKáÏF¦Î¾ öféá Å‹8­þ«“ÊG­2Þ2v·U¨¤}<þ€i¹Zô­±5=δ(O<ÿN)ªÇ;¢8(4B´JËaþàG–)4¢«ùUA +ƒð³_8´p[}AUàY8‹‰•ôñyÜ«¢A¶¿Z /bj˜f]ÌWÓaðûˆ:ßæ¼ÿæ<^Y¡›àJg™-¡ÓuàÜLq¶øS•ùÕ ¹?¦Ò·ÑY]»VjÒ°×SÉ6UÄŸY Pª2 ^v'±RƒCä…j H×”nôƒ=)*À[7™|å]öÅ$~u=.Ë8'©ÐJ w0pâù‡NâîwÛwàbšÈŽl–'Üb”ÕšE8Â{±å{ûs¸Ž»Z"ÓE…ˆ™ü{h¨,€yÎòè)/D/+±kæ*‘õ<˜=Ê‚0}œ¬­,ûˬV'BÏáä+VíBÆéSÅÉaø{ý8>7ŽÛéÉ¡Ã3$LJ iÊri\|ê{ÓhøÏo„ða=Íÿf4“ÞŽ*ØÙ{¤˜¿yìIý“pÈNš0.¿9ÑÝ£h<ÐÀÉú ÷ÜáÂe†. ̆Ú&a’šwTwɰö« ÈÂ]8ö«¯âÛO“¨kÅJè‘ _ LÜÿ‹OøÅÊ¿<„/Œ&S›ÚÆÎ±¥bxHs³-µ)7¡—>rØGêј´4ž&bccæfKr±UJštÉ$ù;̽ü(þ7æas¬z%=V²UgQ^ë¿Pul ñä‹ΜéÎÙ£ŒÁå=ð^ÀîÙ±ªûòÙ¼<ìàŽgèjNnuÇöCÂÚ¤G¾Á°î¬JÈäs´Zè¾T9*c|Ÿà@Î:ÂY•Û'ÿèfÚ÷žÁÙ+Khñ?8î-K¦~ž‡ouµ‰ÀÒÅtØÚ—n ¥Ë·“õuÞ9žƒnû·MøVþ-P1ëÆö%×ñˆüxo¨ý‹i{Î9ëI‡…ÕakY1æ]ºE55Lë‰fd‚¹Íùøz<~7lÎÃŽË;ˆ­°<ªn0¢?^™bï¼/쵃iÌX„<íä lcÏ*Íäº1kwMÁŵ\¦Þ5,<7FÿÀ')ÑäM‹95s2ûo}ºÈÉ…b½ìyÍ4t‘jÀ]k´¨‰N3Hû^`Šm?s5]ÜØËKqÒŸ!f(ç%kýZù·æpf¿*ãl‰ÒkWi4üEgO)‡c©f°æõ´Ì«‡ uöääÂϨsf'·Mä¿û²ê*w»¿àáâ-~ܲv^ÚRŒsSS!{2Íß2öV?„¨ÎxÆdL–Ü>Ä.ö¬„–ÂLÑš4Fáýw85åÔ×"Í mh³À!üOóÌéûŠûO߀ïûD`íK3²Jžå‚С©Dy¶ŠÓÅö+ÐèÈè8Ððûˆ2§šÂã¹µ#›5Ø·…ñ[¶‚Ki7x_¤MÕü$Õ0Œ}û¥œ¨åÂø:Üm+ã`Zùêñ}IJ~²äÍl‹ƒHv¦)ì‡Õ{D‰)ÚƒlŒyÛàFt½a"Eyˆ £Üøÿñϰ9˨Öw²aÛ¤[û¸ôq|æë†©RLÅßlÉ{„okÃH {Ë· žï!çÙצ3‰×>â» Ü·ç3˜ëÓm™Šçùp"ÝÂíºñÃÈÜåv‘QÑPÂö–çŒÎ1Ir·:ƒÃÏAe³é˜÷Š#ë¼”*þb½²>þêËäzM¦QŸÚÁ¯—û~*ï…ÿÓ^ÿ]±™9?ÎÝ9'ýƒhì*IÆÁíŠüñ ÂyªÛN óoݨµþ‹†{·V4°¦û‘¯ºJ;73Õé`gNŠÚ^ͳQ` 3ÁrXŽLZ>Ì ×íëpg-‘žæÁO¢¦ìVüºÒdÇûRîKA¢;i Q[2Ä6”(Ð)?¦Ñ´CBT2W›ÜºÁ[ã‘3?wÞyK5º¦?b6EàÒ®xîîŒhb ]ײ7ýó1]2>o°Á‘ÆD6:á~—{ÃÈí¹Îþ7·¹$¥ˆæ<ÌT”€’Ûw8áµc­¦³œIÓYzWŸ‰ÇD?QÚf<ß.M§ oÊà}í\œ~Y…DæÎ¡+fnƒB«$ëë‚E£ð‹–Ù#yÉùVLôLéõDMè›"0ÿGsX¹Ê',—a2+Ì·‘àÃ1lgL%H.˜Š=|fD¤8—}ŸŸ ÿ½Vþ×a#µý—Ï~}üg†Eƒ¬&б%;ÉQ¸§” Š#GÀêø†Z°sô´¨¢L[ã…zEhí %‚¾a¸b8«£øØ ­·ávÁðù<‰¦ ŸÀÂd¶ÊSäÓ Mâ¢ø7ý³GA•Ó²1C~§b€µek­u·A˜,5 *ãD}ˆµböüEþ)oµ†“—ähâÓ©ÔYç3«¹ î÷6±w”`ùö ºæž*m\r ü÷Àÿ{ºªœûÝÁ˜¨†3Oéø´BŒS=w7*{ìüŸÎx§l*™³ò9äŸzqoÎ2æ›Èú¯’tZí+ˆ|YŒÇ†“™l¯÷¬‹™ù¬ñú|Ÿp—‡Sºà-“¿â;¸Kýƒæþ*Ò¡ÂÏs õ©+nø¹ ŽÂpÛdí:HLÏAmîø5K€ø?ÄÇVõh]ô[xe„9ëÑÅ FèÂ?³Xÿ´žö< ™AŽn5&&Rôêõ§œŸÞIÔˆÓrUýuNÎð'¹‰ŠÌ-Õ¿ðA| &ªÒ›3±\üìákçJOÔ¿¨„FCñ­Ö|åoJ5ôÏÒNsKLƒæê†•xýŠ(¸YãÇØ‹Ž«`›I3x/.wh²TÍŒ$œŒe¥vG¶ùú³·m®:‘ÐYÔ±ÿ~´‰™&|ؽNtR;¸ÙÅ5ȃ/ÃMÐMAö?·ÇUcï˜w­Q¤î=í½Í’Èl Ž„)mWàл9Ü»cñxàmøñ‚>y}vÅB¥çìósèŸ>#ò9æ´ÜÝŽEý·6lá§æ·tÉÔ¿‡àůŸì¢å­ØYó±&ð¹½QüÙ=ÿº:¥Yûñ¦§>Ãë޲±øjm/döƒyÛ”¨oü[†'Û\ñ…]óÛ”v¡‰ž`æZ†÷'ãüÍY» Ò_Z|,n¡×®ëlvOkö2OÔ^Ât-wó‚kTÏßýC¯=üŒÚÎ7xÃØœáe–-þÊ~ä„Ca%¸™9þ ”Úßl!“¼ó lz•ý༿¿9>fC~šÓ›EÍÌnýlWXF7~Å{©á´WfÅÙ:!h«¦ŸfüG²hÈ©bÖ†ÚT ’] 8ÈJ™‘<¥o°s8´ê:Pñ‘×P:äÌÆ¾³Àƒó»ð½u2fó^ÓÛ4øƒ-š^Œ³çÐ+ÛOÀâ QpoË ,ïïÿ kÀƒ•V#]¹¸2%ƒ˜¿Ê‡<’¤þo:+ïˇ¿w^…/9xlÁðú‰—ú†À?ì-„É‘sk.Âò;¸b_$,È,‚\Xÿ|?ϤUÝÓYÝië襎cÌÃÍ$98 ܘ¨ÁBÓq4öçäÔµ1ò냬¨{óV˜4Y‡¬ýˆŽ:´Ü=îˆ1Ún¯˜¿¯²`Ó±+p®4ˆ:?d7\ŽŸ~±7눩ˆHn(gÞ¥†ÁÒ˜ŽË±•¤­*/ŸG7«XbîÉþ®‡+Q.HæIƒr‡3ûm»IÞ¢—`–|ýg«“Aëx´:ûÁ¹|z4äÿ©Ç»³c‘½çCü¶Ïc}r¢{— Ð ç7¹™`4ã3gí"线¹p<77î•£Rõ®ŒŒ·!yƒþ@cÚ)Vö^">=žžê`äËÿæ?†Lêfô3a:©°ñŸ >þ½W³“ð­É˜Ÿô™Õü;xÌ'Ñã3¸ÊpƒbùxÎZa‰©ý«FÏφ./%2û`®.z‰ºQhx;$ŒpÃÖ£ìè1px+ŒñçlÀùBød°ÇÎ裚Û,`5hJ5ü7Ÿù·…¸©Ï¥ww4{Âܼ* ~ ªOÆàÉå¿`³ t»vP‰Ð£–wÚØ¥[§0Ö'ctâKüœæÃ8¾gÈÉé0³ñæ­£rø×*-fØáÉÕn4¦Í\Ibïâ!péøßüã±4!Hº-FJbQûJ3;õð*„s×.9ci1Ø2RÊÖ,Öbçq£°'kÙ5Ò‡¥ËñÂè=ä‹úWrø¬ÉbwتQ‚\^8ïf ¯}ÔÉ÷3Ú$øî Ö?nAyp <M @3¿\»Ÿš‘Q5Y‚>ŸâgòØJc8œžäÌ™ÙÏJf²²æOàtÀ\¸Å{ôÕúòÒ~FÏa ¸G.ÅÖ¸G¨Õ!A´—·£ÁZ^*³·F`B¶¬šým?o!¯óŒh«€ \mSc‹í`Ÿû`x¿ò~ÛðkÌÝ6e&)`[&mCï0ÿ±]‘ôÅ”¶Â’'HÚÝß`ra>©ºÊC¨l(ôË*ycq»ÓU–·g¬ßêL÷þa¦lׂ­ñæè|˜³æª—]‡•A¿Ø7ãœužÁqv²ì4<8¯ÂÑ{ÿlÚ~‘ŸZIÙ²N1ÎÎmíÃÀ·s&Yr·Þ~º ¤»Ÿih~ˆ‹¯‰pFª¼‰ôs5r·RƒMÓ™Oa‹ˆ+O5â-pv'¡ …¨ó΀g}žhP™§:dð^ ^\¹d“sámüaädájESbËw„ý`EŽm¨nfDeÇó³aÿ,-2/ö*GÇå-ërû>{ø„QsýÂ8¬ñ')¿C¡Të&|o‚ü};q1Ç7UsþÎ"‡Nò«ïġ˷¯Jǹ×ÈP ¢ÈyºlÙØsÄ‚dz¦³‹O¢wì;``»9™ý@mø„IKóò di~ƒu2²¤›Fã3;;,ðˆck»“nK(ïLu²âôiœhŒ¹vaÎ>*ýø-f Æ igèâVܘ#CùÌjÁ,ý$¶u6cÀùT«Íœô®)d¾v7ƉZP…Œ.¸yró„ý[ßǃ,G‘>6÷¦CCqw¬1ëó¤ÚÁTZ݆¸,ü‚ŠãÜðù³ytä@¤Sá"-rõS*3åêÛ&ÑfûÁxýnÛ~ŠŸÚS¹w+ „ñþ òK4•)ˆt«²Prˆßã›òÀ©J ²dIÕE!:ºR…jy£§¼Ö¢Æì8FžTïbôú»qŸT2]ÝNÁçÕ¯arÅ0?"ç§%¯$ÍocÏ{r€¯õVU=žÀŸ± ›ñ»ÍCçö^l¼µjÜ4‘!g{kñœ¬åõ2¦âB3HÍc¬Œ\Dÿ>Ie¿Ñ$#¼_G¯*±•i˜©0…;éG‰x\¦W’;Øv?„Þ+Sã]tdîöK† VkKàS¡$hÇU¶Ð|ˆ½?È¥h,'R­Ÿ‰ K~ˆX³ CwÏÃ5ËÈØ[qÚ°¸¾µ)“Ùayè÷q'yîŸü,sÿŽ,“çk€3Õ5Ù'½4&¨L©$5ɾÀ8¼3SÊûèÐ_¾àEõztiØÍʻǀr¯2ŽãðÿfŒcÊð¡‹0MÝ ŒO<µ™‹œ 87¿s¼€ŽìßÍáÔ`]4¤ ÆAÇKŠÝ!¼PÕZÃÌœBÊæuÃûöù¨äÓ‡Ëe@ÅF©ÖZH×4¦á’)LìIgrB=“™ùi*}{æÌѦÄ×Ê„(mKƒð4Mò¢û6üÍÔïP"3îß¼ógávB ¬ùòñÕ¬‘o4V}ªÄí½eÌ|Áµ¸Ït9,s ó–?€ºûæãغ8‚µ9ô•â)*sö!»òË},_vÒM5éÓcG™¦žp&çK+ã1[ ÜNî¤Ô'òߊ§•lõ¡*®ö.U:Íhßä]d½–<Ùk’B¯ûïsÜxÅ%Äí­Õ»M%—¶Á\=È™u¿_ô… ¯‚¸¿y*Ùâ—Ã,RP£ TJýmðü…Hz™Û B°ìw;úÏ{ˆ9é0ØãNEZìû¬lÊ0´‡”37Mb:2&ʶc§–ù[«@WÈìàp'òôv=LžâB|îþ„ýs›ÏÛwEÁ7d/Uï¤`R!MÅ*w‘;ùlÉAe2'Òj-þ€ö‹˜ÈsñLóÈ1”›ãKùxqi¶ÑDýßųT”_cdx+È¥Š’Ì…sh÷ë1¬ÐˆdDŒ÷­¬?¦J¾Àéf?pFÏ<¢¸FŒ„NrÆù†'àòvKîpÌàf‚`ö{ÖÚX^_ÚÈ­_å©ÕP;6†`;—¸DÓõ‰¬À^* }†{Ëè!«b1Ī—ËcA õt_G·üó¤‰Ãyðß\ç÷9+`Cè4öõ%׼əu=Ü-©b¤g·1¾“‚ï"!(³Û#¯Ã÷’Xæ©JÞÕÑîõç˜XѤ-D‰ÄœµãOõ,»¤ DÛ≠µåäI}€Ã§B'ì¿El„[¼ ÌöqðÅyÒ_-H¾zñ‘JcÒàòµ{7‘¥NRðëÝÚ·î‘&j ŠgúÖÒŒ÷h¢QÊž3è,§¨µ_]©I?‡Ò–­³©?§¬z5éÃÌGÄvµ: úºŽ¾·Ù½õg‰ÈÖB2ö§ŸŠÍ€ 7Çé“ö“Ð# @\÷‘¨Ps*+ç€ÃE zAΞ$D|C‚ Ê3ú&j/ñ¬.Âȼ½ôcé!ì¿ü D6Å¡ä0õm8ýõa=ã›R«›`ø´;•ÜôÆ÷O~:H.‰té[™8¹ê÷Äó¿Ó¯Ï76Š[ÂÓ_BäÌjw®Ê¯Avÿ ØZå .ö{©ßŒ/hkaFVm²Ù!ÌõX¨¯3Æ„èï&-2<¤îmêTnÞs8¡¨HùÞ%"ïÆlPÐd3Æü‰¢ò œÚë•çtà© y}aœÌgoà鉸3L’ê/ß…7žàÙ‹M¬·Y2ÞbR¡q•>ľscކa¿½‰)ÿÈì8}J±*MðE& ÿ*L‚3í BTŒÔ«9ƒƒÂ9x½-mâý§¯ñüðR|K—%>ç{™­)õÈÐÁ.Kºö°¿ì žãõtÅ—l˜íý ¾­ƒ AºÿP:Ijò釨7̇ë?¯¡>m vñ4â:üV_hf rÏ™Ørd#„G=ÇÃïÐãñK<œ]ŠÚ& ¤TŒâüº¸‹ žÀYkÚ}†<áIÚO²œ«zÐÞïO[ÛB™Ï[޲=×sqÿïð‰úï•Ð }þÔA}3õy®Œ%²÷ñÖ19V¬§‚&K“#Á™ì1¥ Ü•#è!•Å4\Ì”òÿͦ¡ßÆ}÷©ÙÃoéß÷1ó墘œ;fxCÙŠ–¨oÂWÍ?ñƒ ?Zt–ã2é7 qO•Jb™=TËYð “1Ð U[è£âDš°ì=<òÅ;©—¹Z±aä³1[£v„øÚAÉÅôÊÃN¨,w£F9h>›!×|¾aÙÀ1È I'¥Ö0bþì&Í£ãx˜œt@7§ ·{J“¡ž%ôð7—šu­ô̓.îïîš üF·,¸¼/W30ý3ÊŸ• ß™¿Œ@E(m¸•Ls^Ý€O<1xfÁNºbù&8€'áæb1zen$Þ’ÞFÄÛ¾CÝ´—`T¾Ìý¢M Œ´Ÿ®¨q^ÌNKÀJÏ^<<óuÑ9ŽN]×A÷ÔzHç…cÇO2;ê˱óg,ÉL}‹ߘ _3(Z| „¦]Y':tVžÄdhÁ†#¬[3/©Û§‡n˜±ÞoÕ(Ñyã9íÂrwö Ë¢«¶á1ÙBßì¾ {ëû &2$v=ýù¢I—ýQrëM*îUÚOD‹õ'üß|W2\š¾¤Ž¾C“q¥\@<Δãõa¢öç3[ÌCvï[Ol~кÝðm×:¸çj€:3ôˆ÷û/0äÅOìÝaå´FÆfqÙw`jÊRƒ¢iD¿a yÑ{ ¼í£¨â ê´l¾+C®ß«E×+ºTéj+þ§™.Í[A9¾ƒ¨ß¥é&@µWIJs¡ñ¦gÙkY6—~[ŸL·r-èî)F¤$ø(æY;ù&ÓÓ§b@+y|ÃlÉËÑ™ä÷MØ+˜Šò®&ÿÓ¿d§€Â½cxµ8Œ¨™¤5IéâýÁé±'Ð^9âîäA–?ù /\¦ÐK¦â„G­„,'rÒÕDPÉ »c¦ºx ‘&;-2+ÐC»ȦËAç%pý¬³÷ï‡ÏŽ(cT€ ÿΡ¿NGÍŠ*¼©^;ÎæÑ^·ù æ®GÝî‚©¼ý›RgMµÈbi²½N‚º^:K‡ËÐy¹Ÿ Z<Ò+[@T©^®9Ê/­ù¹Á¾qdóòxôêP¢ÍJ 4³è º‘B¯êê‘ w'Ñ8ÇL†¿fñ}@à dcnáÿkã[ån²Ë•ôñëÓq¾5Þ{ú“X6Ë 4__Í'ª(—~KSõ7Œ¦oyäS |ù÷±aj#òl2Ç[Y18ó@Ûï÷3R…èþÌç)DJ-ö;–¬²%ߎnÁ…¿VÊ•Ðûú7°6‚•V‰@Ó)kèªmp´ä3»%ÆÄ¶~%æèHÒ‹›A³0‹ü4H£æ>¶‘>}Ü‹7žÆuÇ!»$‚yzê$ûÚð-zi9Á³Ìfö·A/û7<Ü÷BOàTÈhŧSÔñŽî &%Ô™z–Œ×² mÚtrpÿó,^lã5å.÷Jýo^3(ó¸’µ0[Ð l<öwº „ñˆ·^ =~J¢ÔåçaEŒyƒ¨ÙÔb˜œËêsÁˆÑZ\2ç|˜Ã,úEÚ¤Hêóp±B÷¼W ‰O@¾f*q½üÝÙéOÛyΰü§ÎÀáÏmp÷G2gÌPU~ÄjÊ5¡`¼ ..»îlµO˜ÎïƒÊóç ¼f•Z*Glv¬†/Êq`%daòt²ñÉúq}>óJ#Œ›•á?øi÷؃Èü” ç¦<ÅKjøGzŒæS—MôI®4­X(7áÿîÓŠ9Þ§3˜‡¢¡ÎÓ¶ÎaåÅ5áƒa øP¡KaçšμØDX;Ež>ôgQÑ?%&‰ÑRŸÍø- “Mç;®csñÁ8æE–yxün4[­ 3ó`‡+)Õ­àÆœ‹FÉq*u”É´>yWÆPoÍX:'˜•_½žÓ·Š‡ˆe7Áà øÚÀKþ[Éz\pÆÇ»¦q-º‰ç]Vÿt®åX&eñB¹f÷ɽðHÃÕ5isäCúyžYúÕ›ÿ΂ԾoLÓuÜn5ˆ×`ñoEšþ ÝÌv£^  |dÀDþûîˆöó΂Ń49r Î!45!…ö|Éu|¤Ež¶ I‘P³fQƒ?1¿âAùßœÁ­Æ¦A›™HÒ“Ö§Yè´¼E%}iäö\¿® ÿÓTŸ;6\^ˆ[OgÛÂ"­=øßµÃ´îaâF/¼¶/ 7ªbÕÈ'Ü÷P‰—¤Ó­Æçan·ÝÏ'“,ãÝÞ½„vwâó¤ŸÜö"ó rÑw›2Üg¬Øö­‡™e§ºÙ•ö—8©›ÿ°‡†™ ”7Ò'ü?ÊTÜ›:L¾cM¿´•âéÞ Ú\Gۤ㩫ºÔŠö Í'ŸVqç õ{NÀH(MJ=H#†nÁ¨¾•]E’Ûߣâš!<汈~ú»z7-·ÏÕô?õóNNžCÕv§‚Ýö{ 癄ìÞ…äLÚ3L}Þ8v*žÖš)ÒÁ>[4¯ ‡)Îíl}Ønzæ’ÙGÒèp’Ñ™—GÏ*O"Å–›©Á Iܻą”ïÿ ë¥éÑã!dß\bÇÙ>’ë‰O&‹wØ‘Ë3ɘˆÄ ²QÄ‚–k÷B—èLVúÿñºRÎuŒ’ÃK°br=\Ö×Ç9'Zð÷¦rШ-c,GS¡såUÈx¯NVR?CWi%L+×çÓÞK‚(Ȭ$ÂO%iúásøÕñùüÇ‹ÌË3ÀÁ-ë¡à™;$1²ÝðÖû8ÜKÜúß¼hàÓH¦ÿ® Ó{x´»nÙ§µºŒÀ,+\pv Í”‡DÛt È7œ­¦Lg¿Ô,<mcKDÂ迎ÝÌÏe-\íúu°<ü ù/Bm¼ ±·%¥Ï)t€þÜîD¼ºôˆÏ‹tÊ:~¦RØë#ÊZÞÿßü_wöQ¤4üöf Ê7½ò•Dgí TVdw°ã}MîG‘9*F>4%Âu㣜ôvú×0ÑX+2úù0U@”+µ°ÛvÊlíËhqÈùXEÈ=ÅòªnU21 c±Pýšÿâ¬swÙeºWqm‡N—ùÅ\xlÆð×”rùÞGnk4fïÿŠÝþ±TÒ^”¸­_Çla/¢šGž±Jf¤wz’™:쿹–ðS$Ž9é“ÊÌ÷ãÐs& ²—L&šëO’øËBd[¦2ÙðQ‚½=S*Vt2w;Ü¡tO”o(˜ðÿÌÆ:4bí!@g--}ÿ 'ÿhtþd=ŸŒþV!YTŠØj<`L¯ì" 6¥Îk×9ÐôšHüÖŸND|Çp߇%(õÕ‚~ùw.Ó§;Çt©Õð:ÜŸ|3/2¤âbRðö™þÑ•¡=üQœ¼ÙþÿOv/û`C1óyž/¬NNÓi®…ˆ%ôEýÀÊ÷—Pÿ@/ÌåW¦gÛþa«å+ôô ag¯ã½:ëÆ¡¹ápäx>ûæ}koAEÖê°ß®ä2¥Æ1¸kXœ#‡ì•ñ`£*5w_V¿ç㩾,Hót‡™ê¼Üüe2xÐõ3ºiÐÚ-º¢¼„L £>Ï2$[T¢Xêûüòî3 ~.¤C7 N¨8d‰Á²9œûîàrÞ˜f“²6‘ö'g˜Î´UÐìIÏÿ Åz‡û¸-ù[ÿ#Y¿>}þãtD‚ðqþ7ÿé}#²Û…X˺$î×¢ó°ä†^]ZêPì13=Ê>‰g/_â!…&bô[O‰n΃€'jl×`#®{•ÅýîÀG›•¥ÁàèOhl )JµhøQ™œY9NžvW]œ'û>BµêÜ8õ<<”¦/Œ&ãsupx¼G\±™øNÖÁ®Ë1dïAy&Ùâ†X-ÃT]/š5‹_š&ƒíü`RvJ[¢úà«Ó1˜¯OŽôKÑñës8_%HÈÑ Êô!'Îzs_%ó¡ià ºMÍ#êKØì`úQ¾§ ÚÅwÌ)ÿé?^ÜÊfÖLÃgË–ÓúµãqíO^ÌëÆü`ÓÙÂTg|fÞÕÁ—º?ÁjÙtªä² ®8'KÝÉ–ñ¼·kè2¼[[ÅøûCž³Ø BUŸ>›p$—¼3âî9ë€îÌkæ?=³HÝIæ½§1üü´–ýª¬a™ÇUàÄMÇ¢îh·Ü,Î-„y1«(ð‘óúâ5ð­Ucœ¾˜Ö’$/«Áõ¾^ðlY/lú+B&‘ŒÞ»Ì¹MUÐB\éÛnV¯ÇŸoÊ„üœ¨—Éf;êg‚bÕT>cHëŸsìµ®e°ç73^/â'âßC àI‚ ®,Ü+è‡Sç°ÁêÁR$êä+1ób3°·«ðý{Aö·ÿr¸ú·Ö—1æ%ùÓq§Õsè~e‡|Ï%¡¯Ü‚ë¤r“ûãÙɖ컽ÖÄeò®¦g{{AÆ¡†r>¡—q"éNy…Ïzà§#gp÷šoX~Ô¶§mƒþ§ \XL IÈrq²5nꜱ‡z9Gø»âz¯ªd%bƒ0ðœòøZ;¡%³‚±ÞQ ·R7CP‡íéî‡KOær Îê:€‹ÙIn ¸dÖe´?²Œ½di–uØ„ÿ7ÐÄŠÎrØ«QŽÃeG@f£(]V[ËmµfvGÁi\‚¼ÙúþNŒs¾Ç¤o2 µŸcáfs¼ÿ(HÒÏãÌ^g _VEñtkübw šc[á´ˆª‡y€ÑÆ7Ί·ÝÈì†!LŒíaeÃq(·—sÓiOc{Ëjæv^û2Õžú4¿7ÞAóÛÙdÿ‚“‡®Äí[wC¤éF,{ÅùŒ¾˜u„$3î2뎬…°m©BÉ5ÐÁ\EÞ;š° [ˆ.©ÉƒØ¨Îé§Ÿ9š>˜^*Ï4YN¢/³hyŸ§}î,Žî½ýö_ÑrM‰ìêT¥ß£bÿ›F—ÊGnž©î<‰¿&;ãµû~d‰L;uË~\øEÚžŒ‡ê*%Ž“ÈšH7Úrp:ùœùsŽýÓçÛpá'yžŸY¦¦šÈ1ÚkP£Ú1RúQ©¼Š=±û.;¼u-òÖG±ÛLÙ¥]ت¢lŽÄ¨˜¸Žq¼Žr¦Å¥0§$hù4.èßgÀZÔ‚vò?øíFl“Oþ~ãœq.›?•7ˆÓçzW™ˆ–RôK¨À*šƒ9ÿ2_Ÿ‡g¿vÒ;› °ëpû¿A»‘ËÄýﳫl±ð˜ß>*«Ç°¼¿ÔiUÅg´9@}§ÄJÚ·®t#kšÎ‚°ecߟŠ[nß‚çZóIëÁ§ÌÔÒÉÔûq),w×'¼š½(»ì4ÌÍ:Ã\”j¯´%hÎmÆm%ß dD"F—‘K7:GªýÛ¯¶DÀ2{Òèîç Ñ{нŒ*»®°E=Ý ¸¦šžN#s A‰1FèÆíSIjçd²êb6ιÇC>åããÒõLK¦%Uþ.@³¤½@öá'Ç5lkÕK÷1%âÙóǦ$÷ .Ñð,Æ ŸlˆÜm7ÁF¥ìè¼9_¹çÌÅØ(GX¤ÿÍráÁ¶‹(ÐvÖ!"²¸žYÂô¹XsØ^3ÊýwÍ|éã¬*p…ÂÞ;pëÖRÌO9ÆVœ|Ói@,|Ó±(G­mÄHÚ§4”Õ‚Ì9WqßgOhŠ’£§uì!¶^•<4°#«û¥ÐeI(y“-<›†ß?$Bä¤jöɲX¥mH§­Jc78’Jéö~EÕ§’{n­ÌÚ9i”cW Æå!ÌÁ†nŠa„±ì$:÷§"íÖéÇ K1’³Šw¼ØÇî™Hlø­ÈÖ þÿѵ’åå1‚º¥S@Â&œ ý^Äø‰“7×ôÈÈöåã}Û1F¨$”8 ýÂ¹Ë I¼êæà;)\8ݺÿ²ЉúE•ù9C•¸ NÇ5R˜(R Îzԣߖ”xõ 79F'Ÿp“Œ*àÖÔÂF‰O«±Ãd•ÔVîyápoæmh7<{õç G³g'Li}>„'›o0üBUÈ9￘,̃Àß ´{R*|xcJ5¾GbŽw?¾{)¶‘L›ÿe˜w£–\êð-ë²Õ¡ð×ò4, ƒ_CbŒzðbÆú g"ÿ« 3q#[˜½Îg@È\…l"Oq™@l âüð¸Æ0éa¨ „‹â9lÐm‚îú¿@Ų{z´a…%? á 5èý+°”ºÆQÛu ìŽEã÷/G0Zð–P"JØGƒ²ïŒÖçÓ…¯Ù™wù®N¼‚æÒôÉÄ6m*1V=Ê¢ÊDÒî$üÞQ‚ÝEȵKÁÕY ‘óL\»Ç:aðQ ¾9Û þÁ`ó¨Ý)ù$û­Ù~uú|!½Ò ü’à ؾ„é³ú_ã C·.~3§‹¿ÁÝŽø7W8ÀíÎÒ€ƒ¤üßG¼:W‚rÙG9ç9â³5˜½™?AÚo:ŽªO…¯øÙ–o:¤º>ƒð®‘ ~‹g`¦RpÚ»1ù^>ªÜ@Y?†=‡éaÔ2@i¦0¹Á¼áÎü~gçå3›3 á—ú(Fœþ€Ì÷”^•ŠI' ˆñtÔÖŒ"w§Q—û)doI$ý¶(Œ¾Ü¨@xæ÷9þßJOEu…HBr8ó—[¶’GaÖÄxç{Õ¿JŒâë˜$k?ÊrBÔïà9ã•0#ï:ï¢/ÊGÊ’Äwÿ{ÿ‹©nœ­üR᥉Áê¤yj)ñêf¹ÿ¦3¡%7p,?ÔùbA€;™ìv‹Ä·Kprå \ìA¡v±˜uœµÐ¼‡W[#@³q5 ô§08x˜ñÿ^Œ¢"à¹ô"{ö9³;âJžˆ‹_Ƶº‰ø>¤Š^xã÷p#k‚|Eâd¶m"fw~Ä‚ª\úXŒÌËø WÂ~ÜîQXö¤‚ý|±¦ñ{æ!Ö'z-[V”^·âPÓH «ÛÓÖl„M딵ßb¹LÿO½OàŒýN†*—p×J^Ö°nœ¸ë”ëPÜ 3nƒwç­F:õ#Ý«Ìf6Ô Z^:6lXŠ/SäÈøÂ¼í$rÓA{Êw˜±S~_O§^X Ýy_auÇy¶Äà Z{’†a29ô4(w¾úw ׋Q¾ØeŒÀ6eBNplHƒT_)œc«A>÷˜’¢H2¼_‰- †IKÔ1hµ2]}ß“¦—¦Aåûate kÚ Oƒy¨gìM<ý[ŒÎ8‘qÙnd®.[vwªKn>­—8‚QÒFXk‚™ó`ÓÕdbl*oõe&òÝÓÜj9Wìªnƒè¥-ÞåA߄ѹ±û!ÆS~¼FÍ'Oî]% ëá¦ïjœ5IÙw c|Ö(òÇnôű ¨É}$H„j]”¡çfŸ…UHÓûtæQo&ûãû\xûÒ„j?´ÁÛåÔQÕ…Äm !—Ÿ±í"SéÇšеF›ÒcÕÈ{é Õ›7ø­â¥“Å5褣+HÅ:'*{Âw AøeÂù2hªßa&MÖ윃l&Q^z5Ø”•¨õ¦jg©E´2i!+g7ƦÇÿ^ eÚyé!z•§Ãà•‰ø¿rù·Ñ3q[˜sK»!¨a,­oƵɱ¬ÁÉ,,›~åŸ-¡·¡ÍjެŒCþs°à~<žªÆÁKƬߞ= q6ª ù¨mþUØL½ÑK6v¯‡­Œ2Έ9Žu?Óœ®®À¾ùÍLýƒ¥ä“ó_æâ%È*8 ïŒF¹BAY¸âA0¾¼²‹ó°muÅPnêŠÏÂwa…r0óCÁ›Öé'8Ç[$ÃÍ{tŽV6˜hõã?5uð,3ÿOG ë|×bùÓDè/l€óòñŽåø±Ä™v9)Óˆ©Qì@P¨®™¨ßÏ”2zgË dáÝIÏÞðCW«øÔ÷†›ÿŒïIVà”“+ñÈüWðÖ³ž)·ñgþÓEýƯ<@÷y!–iøaîC|ÿj:ÞÎ-„Æ8òžW¯ˆ[ób6í± /!LzÚ/Fxe)lóŠÄE‰¶Œšt=Ó|IzNþÇÊ`¿CÃ5Mc¸’&^²ªé{TÀ±zol“¥ÅÓÕHã™§¨íèLUž<`t¤ÞAg-u—qƒ{k×±‹;¤HÂg RYr5~‘‰vüª¸Ûâ-fý3§F̾'CäÜè‰ïS'øŸºK =cy âE½Œc’-÷·üÀ—ø,x>õ,,e÷,£ºiTöÁLrfº-Ô~]—•æ ó0v@ÜŒnó+äüå¿„‹Îup¦} §3w›áþ`ÒÙãŠ&ëËÙþg1Lx[}çþ’/¸ŸÖÁœ|y:~>T/žC}^EÛš@ËMŽ¥p.è œȃ}gÖ jTö›Ð¢k?@x×"²ñ·ÝScAŽÏÓ¡wr_Òüh«Í°Ù”yÒ·}4è™Õ~xÌ.Ü94h”h®A KêeqBÄ9ýZŒ}ð6w"þË.ö1Á2Ç`sù³—ÙýFk°xÓÆY¥ °P‡¤^E}•oÃÑ –¿íX¸`OòÕC0ÇЈÖÝÀ×ïlñ×vro‘>}ñ¢{âǘÖwÓù¢Ö#_fƒ£2ù!æA Œ×™*´6ŸC¯ªÃXÃvÍ~„:©­Ø~„–çâ ß%$s2jzðCã6ÚZYû÷¶qã ©±›©™…Ò¯ŒÈjÊ?7Òî‹‘ãÁ¥©Òøï@#ºø*¶©N£·M¢Ë¾ÊàE?~"¹õ#FX*Àó¤Ím§Ò¶‰ø…åfaeÿ)Ö›âú<9ÚëŽMNäÔÂcà-Ï‹ûfv¢õôhfþè.¦Á`7»ìq%œï™q/`(’—´fûP럡ÔcÝ$Z7Õ†Ü~´š9iêÈø¾2bžÜ[ ÚÂöD‚i~Tˆ¢GU™¥ï5Iþöh]ó;åENJ>ÀÅpœÏŸÓ¶¶.hC•=gà™Ç!ÊÈ®¡B6ìÆ­NÄD~;ûÀu6鸣û]§3î‚Á4U°§JdÏ2ß„AYZO>r¨á­Àûʘ~Ùýé„ñ›¹ž³ )šEIØ£qê¿™ÕþŸ÷B•nÚO+µ¡ÿjmN—¡ jŒäV¦`/àä3VLß|[²÷Ÿ½´­Û7Îàî|ã Lsl~hJ?ÌøiNT~ýK*úÔ‰~¯á¥?]#°lEóO€ª­=žgƒY¿ÕŠTæ¯. øåHüËVƒÅ#¤»ÒTe;²Ií§À`ãI¶nc¸,×&Å×$é*B†®w`ËÈr\Ô“Ä8±ØTEí*èyö#c+¸‘–»'ÓC§E‰ÿEý«XÏC=ŒŸì7惾:ÛGà"ìx|š¸ß¹ƒ²í£¸Ãô麑F^=y;‘ÿN0‡˜~0"Å?äQéÑ'Ž}Éw±—ò9ôasX‹\€Çë׃ÑU^<’LElÚÙr—WPRxo§C´mŒ^‚JÕ\Ð[U"ìf¾ >Þcn@ÎcìžQ‚öuÑäÏ+œþ\cðÈ\Žq,¢£¾²Ä¥¬‚bÚV.1|Défß=t×£²¨L,ç3ÎÐå®™8ÍmdxYSë¦Hüµ•‡-½^BóÃU0 Ζûâœôö~ŧÔé&é8òäÛ]8Ûë™yþhhjÌ]T]ˆ#‡aë¥ÅÄÛ[?ü˘Ë-!ÇÇ΢ӓnìèÜAD}“ðާ‰šnM¥£„ˆàÐ<šÕ´œrˆ˜àN{²1îG78×ãMõIÏ/jÙäCÿù‹á…½¸Àg#ºò%±‹fna­m¥¨_ Öm­eÈ´?Ø©“+tæsk;2xä/Þù{ƒÎ±TÃ¥¯«a~kø¥OÂè'.4Iø 謵¢‡êÑ!/ÊF]ßCnßeÅÊè2ì»A4»Yð óGI¾O±'gU°;yÕ_VÁ¬“¼Æùä‹«#5È’Ñ FZVšËï"Óí5‰Õã2:ÖâÇÙGíñPm1ûÜ÷ “ÿ#–Œâ\†m“–ãKê@³ ȶ»ÛHßä]öw÷ÜÁ6¿jaÃÊŽÁ ’ 9;9¦zÜg%Ïåâ«+8ɨ‘µY 7~e„<Ž27îASÁç‚¥I3éÀ8Ÿ½ocZ§‡W8²ïÂQçÕbzÚN˜”¿mAwg9¢~“CgǪ“ÿæ>û„܃px Ì%AjøÁ^?š^³ÊÀÏ%4â ü•ù ¬=ÖÛ×ÐvYR+òº_±×à íc+=ò0W_†tž«bç’ kˆBéJ̽ÍYtšj×=Å·:Q4\Ô–Ì,>É]¶‹A¹.¬Šµ%U&Í]+“ ªÍDü×G; ndyÝIÄôæ[]< &}`eªIÃô" |O•}ÂÉÃ]çÉE•mö¦€™eS³@)Fl®þ¹&€ñGMhÅ>rQ–¡¼Ôàªx=Ñ„£KEi¼| ¨~4ÅÚÂfV‘6ñìlüOw½j¥#»"ËftG¾µ¥h®5…Ž:\¡›üˆ …0•…®Waaþ4L^\=4š*N-£ÅI Ô¶ð(1Ñ(aU·N¦¦yK¡Y„2ÿí)¤>‚ªnMÂÞÍûè¿çÎÄï¡"µ¿4È&>I `*M¦èÁ¦’ΉüeR:lÜ3ÞÆœd òÊ"4¹ —¿³Äû­—pËÏl80Èjfî™.À_ ¡_e)þP‹× N7È$ºÓæ=?Ñöì^è‚$ˆkM¡vî9ø£®ÙJ þò(S²Œµ YƒZ¼4ÇdäªÁ´ÚùäÁÁGìãWÛ±h‹ å×ð…!Å`WM憼ýOÃÌqs¸Œý¥pØÀ‰ ’ä§~¥ ^öá‡/©ëW°òÜIP4PÁ}×FYþ®læ»ÒÐ,ó€8™¨øã2vwÙ’ÑÆtõ}G«˜[ïóЬv%á¨ÿpþÿø¿T­AåR-Ì’¤|2÷@?pÚæL^wHRË9rëO!6ñ¹“’‘jn¯òŸµ¹ á]œ·£d¾¹5z¼Ä›éräë6kì.‹Â››æã¥Ì00rzû÷W¡ˆáx-§K…÷\CÝͳ¹iÇ9cüëqí^o\ôø3Èܹ õ6^ô@Átªa‘kOˆ¯£¨ŸÏ‘Ÿ]g2žâ³˜ùÍÔè^.VÔ¡Êþ¥tÙWzf¯9ÕÌiC‡;É”)‡éÅÇÆãm@:8%ýÃÛj´g~ ^TÆåkÏ¡n†4 º23ƒëT'òßóz|e(AgM’Éo/™}ä~AZs·áž±ZnË«­`väúÊdνÅ!T|R8ØåŸ&/Ë&ƒGãlËOÀ©SÐvñ~ºµf/óâD3¼¿ È6Ì'mzáìû£iD]P zêñ{ÓØS_×ÓEù°G²/Œ5¡ûÝgÌØÚ,h±ûÝGýƯkëpØ’‡¼þ u3÷ÃòM·q°OŒžÞ®zÄÈ"dR»89Á©@Õ oðµXLš/€¨ZI"ùu7–&²)R }ò»>²B³S+ mr ¿„Iø&í ÿàs†½Ì€ÎI¦þÇïŒãy“7æ’oü.e6Œb_ 7`0‘þ‹ìg‡/c›á-H¯¢ó6.¦ç\¥ñadeãÊ™îF=7Àm’‹PBªœÞ¶Šx[›ãÏøÏ$Oó2(V÷À²^Lÿ”FnëGžŸ¯0êÞ¬ZÞÈ‘>+ËÊ(¤€[ài,Õ(gŽ&Ðr%EºcèãðÞ \Ì —ûVQåÏq8¥ô: [âž‘xØÇ«|í¿½tñó’oÞá9Sê»ï%)½•Žåãy[üÇÉ>?¯ýΪÀA_[bózñDþûT©NÄ„Ïq=~”À¡·[hâ W2?¹†mÞ»‚¼òº‹Â^Eœ_^ðÂö,ߌMzDDÞYÐF—t4†…"½üCšnë¸Ã,—¹Ù(j½˜§úCg´<³2ƒñR¤_˜l2µ›>9©õò“1Tp {yaø½TRãôÕ–µÀœn¼·C‚Z½òEKéƒx©B”îÖtÆTW*Ð-G×¾Œ¾}Ck7ÕE@)=[õTJ&“]q•°¸o:àváì´ .ËqVŸ9O ˜³%u༽zkP«B¨%Gøþ§Ÿ~ê_ø–ñÛŸÉÖi¿„¯‡7S—‡v$9ø#†«È”ÓJôsâV7ä&ã®nFžra³ ÅüüLÀBúeƒ²Ú±oŸK^¾;Ìäâ!û»ÔQv« “vé.wˆ‡&³Ó&S·èxªÑÐA¶…ÅÆÍ´OYÌÏS¦Ñ|e¤¡ÔŠ®Šq$2«6Sg‘]4Á"–ÜMßî¿êGFƒT€óà>±êúï%B‹'ÑÓ yÿ¹c¡¯·F‡g1ÎÎwŠû]½Ñìjz÷M%ÌЃÂe‹ñ}×WVf{œ(84ÿC<òŒÙU0Ö®€ÃK0Pºí¾Mš­ÔÌ›àYƒÕ¢<¬4nº ÂFéŒÆxû½Ž €Eh4”*ͤ‹OÏ¢µ—~¡ò>3:´~'i%ý$¢«Œþ5ÚFuvïÂ\Õ0j¿ü%„-NäofhL^ŒÊ8C êþ% Çзk¿7Ð.;‡¾¨‚¬#Ñk?«­&Ç׋ÀšâŽÆ,éÔJ:IÖT¦Q¡.0ÙðhÈêQùŸñDjßJ0VDíÇàtµôظ“`K’Û`M¸iÐSõ×p5hÒw8îf‚¼iŒ 9ýYßèD€æ‚eÌ:9œF~LÜÿIH*>Qœê@›Ìj1sR:þ8’L+=¥hb¶™ûºŽî†ãw„ÈÞ]çaÿïéôd@,˜Kç¨gcYn8 ºIû~fÀr»æn'iNŒ™· É­žBòFª˜E+„ˆãk ÍÍzä¼ÊM–Çl mË?BÕ*/2-EÇ𛿪£6[ … ‹.«O¡i7³ð€Ýí÷ŒÕ?U|•"ûûqÉ–ÿGÔ[GUõ|ïã4Hw()­* R÷ìA DÅ@AEQ‹î”¤ áÎ 1ƒ°ìëëëó[?Þ°.ëž9gγöžg?{fß™Q¤êM"ŒææR™Êy\Ëôë¸}Þ2ôžèF¿„]lÜð+’Ú ÄCƒŒ†ºÎàQñ$p¹Û…sðS­%›äz\=ÿwþø«p)9ÃÉÄU@ÞÜãõp!‘˜ê6¥±ÒŠ?tسw¼ü «™ãæÅäÒ¶]¸~ÍjŽ÷°ð_ p/&áŽäq<²¢–kwÞî°5Y„=-ÙgÛʉnÅÐX:9*ÏéÀGҹ܋JG<³.ž¿5gÛO Áà¼Uhøw1þ*Å\•|P[w=êg] r=Ð&ê"OÐQ—¬”U]¸Ø¢I£¦¼ÄæêÄn…ÿybU&ßkMÉák3At!¿­#ˆÊݦ“‚o§ŸdGÇßâò̈â«ÎîËmPÈ™Í*þ·ÿm¹ÖWºl±(íø‚æ+ÎÀ­­bdNY8~÷σ 0E@ ß©¿ošR(ËV~”dKŸ ° W#I]½lò!Þïe™oîæW1–LS¿€±cr«·áÙ¿Íèd ¤Ärw^N†÷bæVÁµ'!ïï gÁç|¯´ƒeŠ;²Ó…6^†'%9ø¥k9j:÷•ÀùÍ=Ü—Z)Ó\TÄÙÒUöÌÏ ùIxMhç»=ˆ[á&Î,k÷ƒ‚Â=:ô/ç\0½äÞmÅ¡­àc½š¾S߃ [ðã× pô‹åÊGKâPß•ûŸ >‡Þ‰t}&é[zíNƒI ã²vU`Kr5¼3LFáArà®5H9Éq2­ãÏ¡þê tާ–ÍOÅß7Ü1R  .Ï6a_ôªð½Ê!ºuúEŒhÀ\Ã3¸§e·Û¸³7Ö£ðæw85ÿ,åGDâ©AnMCïzhKé— Ç>¬§kü#ˆù‰>P´œÃ&»±v%ÿDÍ tSœŒjÞdôÚ`ÉÄX‘ÔötÛ\ð¸ïD³YÐG÷í˜üaÙ“+AÊÞÊâ³ï·ðó‹q$|]=äÚL#]ϸ‰ø’÷äÄì‘õÏ&ùiø÷Ò5Þ* ’þN‡­óä±ySªàén[.é“ÛÐaÎb$JرÀ) ´ó ±Y§IœÜö•>%Ài6²T³ƒðnÌêÔJrJ9f•VÏž¼[ÄÞ (°]Å"ÔÛ$•5­4¿NÓ»¯É– {µyP4¹ôüj±ø‹3’<µbÙ¸c_$òKˆÕ1²N¦ºR_C‰«í j‚-“½ÁöD ;¨MÞY BøÞµ´]U’Îwç2vŸ‡Sr_ùÍ·Æ’~Ã:Óvq[ ðߤ–UUä±óÝHÝÁPÈj^‡[u}GôÜüWrÌóÜ50‰±¥+6²i-IL®æ-_r­PÔA•|kÎ$Y)lø¬ãÎoæfùß`¡õq,q@¸YöÔl¶Eä*¬q¤(v  ?-¨`k ‹Ø‚ßlê¾åÌh K†Trç“1wpÔ4F;qQön±ûJ³iìeWà‡ËbrÇ;Žg`Ã~YÓ{Y¨îýF<•ñ1^;–L~ˆµa]Ht:auîÌô°5?af·Ø :úüÈZÞŽœ~BóƒYÈFÜüÞô/Se&Ëó±àævÂW×"O9#×~k„ÿE.œá9ø¶BÌú|0âuBQå ëóÖDnê,Gÿ„¸ykÑÕˉd^P6Ë_Û…ƒÅÌgðïפ+“Ý}ßp˜Ìň+¦$ÍVÜàŠÿi‘üAêÑÕU£N£ð-KšqŽ®8ÈÉW*³wõQûþi[ì¡f±ó…€ƒ­„Þ_~¦IƒºûZÎ,vË3 J\bÉ™¼‹ØôCºýºp߯LòùÀ&—Ê“Î#=èïnϬwÍÆ'SÕ©SRïx w«ñVk%XøÑÄ™ !vúrS½ß쇾gdŠ›ÛÿíyápÒÜ Žì(ÄWkÔÈX¥Y¬}W:n’Â1ÛÆÁ¡xmNv8KZ¤ÛGZlx¤q!Ô^š³5å˰sÊþÕjn©U«‹_Ã\¹€&^; í\,öéf5=ŒÕ`U¾ÉPM,vµ(L£8)ýø6È1…mÖ¬«wLýeÏôÿ„ÝgÿBŒùjœ¶)“ UÄá¶/0éÀBb^Êùå£É O¶ÀЂ8Ý5#o/%ºcáă 9 VÙ²dÐ NÜø Û#@ヵ,C@œX§È`߇-I*aùà¡Å2óüovj¢‡N÷ ÄšçlH"èêóÁ–}9l‰ÛËØ'©çôã[!öï:] 1†XmüÍû¨¤Oþ®Êæìç “Ðß©$#Ä–SZ¯Þjbö$v©†¾™ ²×0|ð¬ÞtrÖ‘ÍÅ/y«^ Š(^Zn1£åqAŸsœÄ)Œºõ²Œ† ã¯À…`$‹;¹ÝgÁõ~: ïÿyjB¬eB6í븉óc÷aÆ89¶Ñ·“³•~DÕuO¡|ÿtMcUSÕкà5LgšNZ(ñ-šÙ=0 Gùt§XÚÞøßúßâäRØ8Ï{<ýÙ¤¹Q`^Cž<.´#uƒñjG.gF³÷çðŒº ƒqc0’N+³ù2 >dôÀ/aIô¾Òš³ê îh3 ž¥0¼Ðž;˜Ìò³ŽgSpÖÃ;…ó“owÜYíãtØœáDã¨yŸ^ú€‰‰KÉ·£>xjÀ‹°wCwC!6*=“S¸2ä%òõ 0¶ü -u Óc;¹‡(Éd—MbOŽÂS¿ïðóG. ÿ0ÀeJ¨³¼ÄûxôÌG07/ŸSÓ£ñ€©˜ÌÿVºb5ï´¾Ô7_=¹1˧¡qu+ä \ƒE)Àíü2»¡´Jz9£¸bïÆ+a€•—†î¯åÈTË4°ß¼W,m€UérD|´,±–Ô&J±v$øN®Œ Kg7òɳ‰óëñûg!ΣBÆ ^‘üMD¹ÀôÛȧÚ{àU…97ìN¯. Á ¿jiÂôuxZAxœ"‘Uô£—‡õ ƽýý’o¹áxcÕ—= !Ëîrƒ?ÖRãz f‘õ V<Ý ¿Åºð#ü‹c·ÙôÄ8ðRt±ÿÂÏé8ÚÝóÎ@´ð\PfÈ;’P_ “©G:€˜LãHJø'4#_â:4·¾E1 a–œ'»ó²ðÝ]µYüm—Ãé¶ ÿ4Ô*nöx12ßd2•û€óv'V- NcU â‚·ÛPê›(Ûw¤xaÇè²’{³˜k›Î•Mn‚›¡ÝM Ö(óç%œ¾´ŒÙ¾êçæqbò‹`îîpî/ŒÐ$æÓr±¾Á€>oâÜv5¥ð9v2»ì8Š9¯Ù‚Ž(²áZ5Rhÿ—÷ç¢êAv—Ö€$¨‘¾ßFðOKÀôªº¦S=î äfs¼jz‘=¦âE÷,‰Kíå8ßÙ,IÎ «~¼óaqp+‰†œßéèkzÒŠ°Þ«ïw ŸóUÙÜÏU°eœ=–ýæ]}4ÒÞÙç&¥rÞöT»hìXW@Yp$¬·–ˆ/W_;|¹º™—¦d‹w¸ÕœKY1þýØôåOÿMP ½Î'açmuüôøÆØ| ’‡"iC½!ü¾; G=Kàò%ck‘e[­¦ÑäõW9åÆyq4M¶iƒ¹!UôÖ'Y¦¹9†4MÉáôÉHü;ñ$msõ!ãc,1ívä}!Blqš»ÈôÉŒ> ˜ú7 ¦› í²Ñúpf”¯¤œ5ðöây9på[¶žxsvªÂ´ÚDZYŠ;13ø_\¨‡Oo¿ÃÖ€,|&6•¹Ì6 E´˜Þ£å$´ó:Æ®ö#¯#0ç˜ñQ/G‡,YÙ¹Ilì[tL„ZöUÒ‹9²¬ÿ¨ ûÙÏPr’nÌ炈]ÀŸD¦q'¤SŽƒIá_|l|;³aS‹>‘:íM”|^–ÌOn{‚(Yçq~6λs”ÑÈáöˆþ ¿7Îáਠx¹ŠÀ¨Ñðú¶"hwކúadÇ8r¹k?Ô/wog–yˆñFÄéÌOÐùô g=UÂB›ÃPxô-¼ybNÔ?¸À¾ÃÑÌÐP}ßÄ€™²ðMWàÕW³¹qÁ«8-þ|¶oíuX7#Dve›µ·³¤,ù0z˜N3ÄØÁ8øUˆ½P—€µn'`]e7†-Õðhã}”˜;†œûõ~•b± `JåˆýóvïŽÜ³ÂÏûWàçƒ31 ?ˆ=]ߌç}Aý0OH“ó&2›Üxcû£qzh)•œÈhäÚ\.·q;T]s·&SÐè_Ì÷¹©Ãæ´ïÁÌÁ)ìð*yl™(Êf$áJh¿äD8g.¾Õ³ÝáAÏ(z¶¦§]ø²?ÌXþz *v®eÂ1' ÖÔ©ÅTµfѼ×=±1&«½œmn½GŒPpJ/\ÿfǪÝd˜ÔZE¦=n{ º '¾Ð#¡£tÙþØWTO°”¢q}®®ÊE½w¶túVÖ¹[`ÆúG#ü§å…<š&‹S²qÓç÷°ð‰[Ó4hè5Bÿ?¸'¢UETì}$=£Ëÿ²ù7w2’{¿ý9¾ø­BŽüƒ¹…·9’|ÚáÎm­ÆÎ«á(p²ö×q³Úe`Éx ^”Îvz€‹…›Aè}.L²ækõQä–À+|ÎOfæ!ôÑ/ RO×s¯gPÍâwï<^L´¡Ão–‚øóWÜÇ/“ÀT+§*D+éx*³{>ád¾ M=òšî·’©mPuëÚݪǷs’YÔÉÑT¥T•ÑÁ¬ç,-,´á¿$i&‡·¯î ì²b˜)½˜ÿYe3?eÒ#Lˆ´FýSr›ÆÀ˜˜Ș¼œ´ßú·”ãž›.L¾é7ž^†Ë¿ìâœÖ^DµÝ:4Ò^•F wR1›pò,[=‘Oq“È£]ùL¥EƒœÚû¾=úRK"ȶ%‡±Ž¤ïr–‘ž?’ŒÍ7 ~Çaäö߸ò ÷å``÷Eh¬IÆN%Ôè.§[4 H®á4Ø—ÁäIÜ™0âVø/íÙ-Ί®¼ubÌiM>¨´äpO&+²’ƒ•4Z² j<Ũ{f~ૈŒŒÿïÕÓàùéÃè"ÈÝ,ú ŠAÅ8Å÷ôoëæ­YÏ#÷p¡=ãa8A‘xó†ðP½!sŽŠ[.Fñ€¾É(ð- ’ÿ ’k¸¥Y{p˜ÿWïçÌ"Ófâõmg©þg(k[ åA†Í8¾ÒøÕc—xcù]—ÕØ–äÙléGÌÓP$Å)Yd½D=uÜø”J”´pNwÜ€†ÐçŒPsü{P»óeÓáþƒ3ü^ày@èbAôǪHW׌¿,f®BIà¾; }ñ‚UIfÜ¥[¢TøK,Üê7Ñÿ§ì’ÐÃô8§~~DHò2c¦FX±•†DvìØ.À‚ÆkAOYww^ô=ÄqyåhíK‘æ´Ø œ;Ö”ýþ§å^wË“ßgqÓ­4ìÏxÅ ü£çä/|ÚÞ'Nž¼ø 6bXšFgˆ¯àvoS€…YEy˜@âûtЩí„À±æøýùT’1f*‰Xç·5¡÷c¸ÔˆypÕöãÙE… .ñë ÈûeËNn!×h>ýºíWgÀ ñ‡4#žíòT\°>©å¥yÃɺ.~©x](ŸRE ûT<Âß„ð~ž€¶æÄ4ë6\X¨Ï’¸4àk5¿|ú ø N¦wZ“gÖ7°Ãhû´Çš©®'¶gƒù_c¶éÆspÜV„þõk˜Pqܸ'KÒ5îüžDXÐä@~iˆBЊ…ð[$ÈXÅÚÅõlùÂ÷(±ð"›Ú0ѲG@ªöpézß¹Æ oöËt:®ì<‚Zošaå·[ÔÊ­bšþÒA¯qpnÉQ . 1{÷}°sçîŠÂ —ÁMA^àdñ_ø*¢Ú>zh0aøÎ·zåÁ㫆á3?°ëôVòìÝi´ XÄÊÜÇŒà{ÀÞA¦§Ö²S<¥Ëwi`P<¼{ßJ?a—‹2]3Ç –¯ÔÁ«;`ߟ÷ÿú0‡@["õž ɶb¥Ó;~ ÷€>+Ør{é” Ó^‹,õWæ$2øOöBغX´È×aû×›`í»ßtŠU$^ÿñŽy„]kYÉãä­—ƒfm?œ¬þ«Í™ñ}CbúhvåÌa­-¬DZèÁ­ðª*€{âyš»oz¯·¤sÛ›Îs‡kvQ½#Yè Æ•˽‰_G+Š`Ñ|'Ò~å, ÍqdÖq°]I Z¼)/ÛxÄÿ¥—qêãTàɸ48v)–3ŒU€íhÃ4'’•²FPë3@{ßhò·äI²ZYrE•Ýû†äX×þsŸ¨ãyëHä¦ øá×ÿÂÓ_|›ïE˜Ãƒ¶,"8ã,M߀¾‹}È$£ýÿ×ϤGãÉ^ >fEÐæþèVä@´v…¡Èã~<6gÎY}p žž¬»Ï|»àŽßñWYBßIÌHvÁuoáþzÎýV.,_óŽ^­KñèúdçÎ'LÐõ$\z¯À6¹ °fiÖž=ŽÚÄò]Kr*| «Å}$þùf,Â!ë n×$;šn‹`£kË.eïçŒ'&C‹™Á™H²öb†LðˆwZäÜSxÄÿ¡Û o#và®Î'x®9Ÿ?÷ýi<35Ô¤Qu»’TŽf‹õiìñ~pé]ÍvMº¹îS‰œ†9sY{˜;Lj™×ÍȤßçQß‘ÏÝ/ÞJ¹u>$ti9ìšOS{ùnÕî f»“Mª„î#“˜v™ _†?BI¹ó+˜ïÉœSñmïpyCìÌ4ÀuÓFáÌ U$‹;ÉúzÅðÓ]#z<Ðt>±Gksb|lÄÿå“ZPOø,Xœ‹…§åy§m°ûö&2ügž?UËu>y‚î&@žÿ*üýÏ-iû‹ã«AJæ œšªÌKq>Aå÷9‚ðI øÖ†‹RN'ÖU¥°#ãöÎð ÕžJ¤ðw1šI9‘å‡ðâc)²¨Jˆn¬/£?µ†aÆ@&l4ÄÖÖ`¾‰›7«†×ó)š»nqL²Kñt4xh0©Q(ì³ùcÁnç¥ÆrƒqXsB¦Ý bŸ~†Ÿáì้d[M1LëÍIg_‡Ü§‡±òr DŠþDñë3Yä£÷ 2Ñ’ýÊÁOU4ÉÃ& üA?µœûµ-¼á”(ΚƊÝ÷¢ŽžéN'gÄîÑ)«ÅHßÔ>‡QùŠ,ñ»9wæâKà}}‚ G„ØÓ4i&»>ŒdåbˆºêÈ{›K¶l‡£Ù“ûo)·Àó²Ü~4~µ‡Àé¶pþš+=j)É và‡òOPÛ(Âîû%uy²iÓU88}ivÈ£‰d1{äË4 #Øûïépµå ÿä½Ó°)(ˆ8^IÂËlãÉÏ ·O\×+Æa"¹‹abŠ$ú¦»8ØÉ©°ÒÍðÛZŠTǨ€qðÿÎïzé )÷¦GgÁòôe,Ýw‰ æë’’5ÛÙ˜\S¶X}{xÀˆ=Ý2•xÞ“#CßbØT±˜–kÿד^¾jåXR§òƒzvÑŒß_¸z)irP¢™Û®þ&`*Çíæ2s8¦±Ç”Š~ãë„M7³ QEíÔ¼Çó;ÜWÜëñ•p”Ù ì]AHò–"Ñb ¤ØfYøküò#'‚DPÿiï ZZ0a\6Æ*3SÑñðÂ^½¿fǶ¤Ë²¼á‡ôcået-‹å?Z ¡ûØÊ¶.”—bòNsO†þwþö QÆß¤G>º[·” Þ4„âéðJRÝž+ÀŠŽd°úOîlâö(`®[!Æ7‚”Î$ËÃV’µo6à;=%²,w©½lÁ‚.X“”€[\Œ<ê[pÆ›EY Ñcñ“t©®)àM~Xóè)ŽŸMÏÔÈ‘„7«™ÎýHpçÉLÍ ¶9·õ­Fõí³ÉëË ¤'÷—‡¥kPf½û£SÏîàÄŒ¦Ú cX³ŒûœEýå')d5 ‹w>öe«Eë¹æ‹60=øðù"VqÇÉæ ðÌ;ó–k²µ ÿ³ÿ¯ÅGàËÖ»T8*„?”}ôE$Ñu çþÕÆE¯"¹½bì,Kr–Z²õñ—a×ájôáºswü¹sØ7µ|xî•I;q¢¾,3üÉí¯ÀÞä*‘K¦ƒif½y%lœ†¹1ºÄ>{IkOá`óE Øñõ<Þ®Ñq¢AHÉÝ¥k>UC÷÷h8Þ®‡a¨†ÇÌØÖã9ÇíÁPvþ*t°p~ÆÆœ¾E¹np*|ÞË¢ÜÂøC.Ì0w/Šó±øÖÕ¦¦s[ãLØé‡ÿ>Ú±ìsHŽÔ`;¾úŒàÿ4ï÷½c%Z©J1—c³ñrv6(œNžoç±õ=™Ôâ‰#ì~µŒ;mɈ¾ÊòÒL팹}ŒƒÈ’oð|ž&9ôº€û¬ó„wUÃø²#ØÇá¾|7g8q€UXr9ÁQ `œd(ü­’bo®bÃ{ß?W†ñÖäŽßâÃí_>ìû5x'\ˆ%7æ1ÉÁqœF\ õÃŽœ¼6ŽÕiãÚ³ˆÎ¤©ó–ÿoýÎQK‰™ŠmdovÈrâÝòÑ”¬n{*¾q–‡?sOZ–BKI¬ß×EÎ"jÚ‰MlÌy˜Wºq$ÿ¹~ÿ>wS"…nÊ•#/œA¡Ÿ¬âüfªIKئÜdªœ± 3" Íî-(Š R†Ì¨*–ìÎe9ZP8ñc ‡ôÃßtqðºõÎ7'ém^Moön¤SDúP¯+~Äþ*¢ðšB0,É3e?ì‰i›/ Ô3îBÚþç?ið°C­é÷ s¶R0×tž‚Óš+Që–0»±]”;ÕÜÀ,oWÁÙ};ˆäÝ<˜ö×­{­@¶œÃ}[¯câzy¶/ÐÕZCˆ~B¶ì?Ìï‹xƒAÁäÚÂ&̳#þ’¢Ô²Y¶ë‘µ«7ñ~ß9LÞ²ãæ&9²+rÚ¬bb5ýð!תó õ•güó›Õd"¬v/5²P« ˜®Ä}ÇE¹Ô½aL¢.ºHàUÔÖÀjõ¬õÝ%4z8÷½&[:ÿ/ÄiK)ó‡÷ÅïGöÿ½á¾ÂöØÑòïnìpC0Ø>ïµì¸U"˜«‘ÃV®GÉ֋ߣsóè² :_?ælÐìÒ+jaJºek-+¹)1 ¤wÅCêXã…÷ '1·Æ0ü¢G\BO?ãyép©{:›÷^™<¶¡2ßpÁÄ88©9 6¤™Å·D8¡û+¸¡É“FÆÜ¥­Ô/lŽ­×$ûæañ°:Zåy0™ ©ÁC3pßIÔư”S]ü‡¸t£< }H‹ŠëÀz¹6ñÙr™‹½l !ÛâàFþ+(¶$‡»Ã€é˜ÀÖ,Q}î:ÜÔã>Š*Ò:;O’È5·f%Ý9|WƒÜ?aljüº ‚¢äÚ«lx& ¢<žôN>{‰ñ}-ÀÌ+Pcú^´2?Eº?hÊïIÐe3¾)¹“ѹ "u‡Ï“æ´:>RUŸËà©gË|ž­$iF‘2»)ÄakÔC8±Óà°+y$þmÛ” ‘m­hR¯ 6ÆëÈA¨˜z*6xÓeW2cïp"½i|b”õ½iZàç êWð<$cØŸû(iœÈ´ AA€ÏžOœ·ü†7“'WmXæà!`+çaøõí¸åúoš>>EÎäêÇzãºÛ‹Ø¼ªR:¼EJ>zèàåF‚ÎñÀoX4Âùؤ-G¾ÿ‚ã1ö #Ñ]•IËÇn$?þârú¾Âyòôa1t•Ï€—¯“à”£-üh_LVgàã‹“¨ò‡fXå8ˆ®ú±\úÑGpXö4XrtLµ/ý&?n„ÿšœ~r¹òQ(¬Î@aŽ/|ñ‘Þ²Ô'ÎbnDÂ.˜M|îˆr¿îãûÉAl±Ú7TÎS%ÎŒ‰”Ø ¬÷TÎÒh¤é!“¢cGàYç/¦q…{´ö<‘š¶“ï÷/o>{z&[Z7¼ä NœË„a‰yÙ@*↡©©•õX‘wû LÂðéhrêê0ï>@Ö ¶b‹š*ÉÝ7€Wg?†ŸKžB@ͶðÑvùê½(Lêlä™{”?™Ý†¶ìœ÷ž >`Ìži™³“_âw A¢üß‹!ù«2ˆëÚù`´iÕˆÿÇ|—æzÃtm &y-„œ7 ¾ÑqP`ç…¢Yá°Ãx1w40µ ð†[>^ Y3ÁÓ9hQ‘Ä¿kN¤·“_OØ…Ðñ¸¿ê7ÈOÜAÔg³¢×+ñY[-–Web˜Æ~ì_ɘNŒ7ÙÍb_«K›ˆb«éŸÖþÑW‘‰[žqÖÓ(©L,KÓš5'᫽S/• J»àGC(ŠlÁGO¦Ò“?.Aù ¸A^Žf?%vি©„סÃÍXKþÚæRP‚¤e.ÄH½„K:°•p[Ó-Ø4’ÿˆÄcÀ´K8{¦(&ŸzE òŠ ì©3fyØڭ j×Ú}(ýðÈýÌäjÃæ±om.ü¼Á»ØûÝ!')§IËøXη’)D¶75µ^ [(hÉÜr Yí„Bè’+âö•[`©Âab”èÂØ$æ2iÖÌÊáþD‡q"kÚÐ$Z‘ÜL¯ƒÅ"ÄÚÁ‡éTuð§»'1½šäžŽ 1ŒÄ° EàæÔJÍtg±ÙûĘȯ!¸kÓ÷¿ùÏÊØ6ñ,Ù$ʾ=@‘C£XÍù—sµÁÑâ·PíSÈÉ&¾oßáTý—0:ZƒˆlšK|“ù0¿ì&¯+¶R)Í­ö™š Ñkå0¦6¾Ÿ¨HÑ‘ú¿5·&3a%Vqb93ØWÀ¤k²$÷TöÊq²ÀÚsr3ޝ‚7{ö²tÿh"¼hˆvš Ó!Ç'YmLŒÚÞ2ùóäåšnv¿¦Ÿ‚ý¡Žƒó2Eà½×# rÉ«'¹Ø(Mæ'°Ý–È» '™ùxM&”õC?}²‡üëSœ¡M},ÞêÖb¡Ö<`añ–_s¬}ûñCÌMöĨœW¶h y5K«?ã~cqGÕ[¼ÚBY«ÑArM%šì‹û—7‚FB34{õAžÒP-ŸÂš¦^Á/ØpÛJ²‚]çpL[çâ%Ç¿]®Ìí{r{JðXý¿¸;|Žå{qû» Iö¼ð¹¾™)Õä² ›óaú2t>:›-m©Æ+/À§<߯m7&±»¹äB–)ºnßA–~ÉD…ÉsÉÝ­„ŒÎׄÊwiðµA†4.úJï°ÉPê´žü¨Ø»¼üð­ÊfxbÃt?ÇÍÙ…àvZ—¤º±÷Ñã› àô6!2CF’‘!©uø}Z-cs×Ñ~Þп¾ ¹ê!1tøÓýÜ:šôÕKÑ?±QýP2aCƒÓˆë¯ñx»âõˆþ[üä3¼ùµ¸}Ôgï&qý‹£X[ËBTµùˆ7 Õ¡¬ˆ@”¡&)­øÃiŽÿçoyãÙðr6m”5¯¯<3æÚQ= 7wÞ6 ð˜ˆÝýȤ!krçµ<ó IéB/gôèÀ æ™0{²#'”éNÊ kùYA.d¡0›sUš¤ÌN'öK'îÑX<¯ …¶ â ÛÛrª>[á¿>2Z騗“о*3Qšê=\2bÿ Æ…8F"õ%͈¾òâûÍ‚¨YOöžK€Þ&üϽ¦¸_RÁy~üŠ•¬”}ãÆ¬ßED ÷‚’p„s8zë$¦“{W³èyí‡`š½ŸI‘çºKXÕ="Ú‡R? ™ã…dOð(vÌt³4Æû)ø;Ì”Æì"Mªx5ä4ñEˆ„›:–¼%Š f“1Åàõ!RïƒSo­¸·Ý’d¡í1~Äç©tí±DIˆÀXeWnIuXl{†ë1P$…›æÁ²´ùèÔ»Ž§`ׂzº‡ñÊS6Ç3’SrþÜèÿóh=zŸ7"'o8‘rž1¸>ëà]]5†È«Ë¢ÔûóLÞV‚,xqˆD=ÌDN¬!KSYe“¹óá&ÙÁ#B À£³ ï#2“K¯`F¯ ±ØD¦†¤àÜÁh"²®‘sŒ«áÌLBvfÔÁ‚cGi¢Lû‘H¾G°%ç›ôÊâÉ$y†ìn{ïâÔd¦m‘®ÂÑA¿`Âgן¢¯ÀÖF Cç’4X~:•Í=9ƒuÕ‰0Uå±lñ©*^»½uTÈ…±™ëÛ<ʪ8ñçoE©¶©pBÁŒÿ6AšµƯ¡¦öä0ûÛ÷ñÝ ;7>™4.dkE åÜ röø®¶j•¹b3¶Fñ­u‚àÎÒUÌWk T”KÛ¡YTRW‡Ò¦Ã¼ó³™3LæKóÌÉ’& t93†TÍføøÌø™ÔÈoQ !`­2¢ .Æðî/·Ã°Â*’¹ø½ÀÃËp×_–Ì ”ݽ±Ö—Æ@~«0‹ì1¡.ÅO8§îhØÖËû}õaÓ¸HA²çˆ0뿾:纒¸ý0Ç—_tÙõü­|·î0Š=Æm_cMªïN†æY¡ø{P o_¦;DŒàlñ5úÖ%~XoÃvñ.rÉÖÖL1ç#”· Êñ«Ñµ²¦¿HÆè`Š©ç݉¶_&$¨™‘[×]Q°À›ö¯ýùž9¸Ì¢Ÿ¿ÁbDð¶ˆí¹eqðA]ž¹Ûþ Ã<ÏD‚zNìO’庇3s‹Aè¹¥›Óo®—É#ûBòÛU†æ|áàwïd-iÁ_“'ˆµiÞ('âû_3ûk§Nì$üˆçþa0×UayÖM<ÊŒé·NÀq—®¡èØ^üXQB¯©åÚå´Ø³•ù,}ÎQLÑõçæx‘¿vÙðöU ›ìµ‘TøâÂ-À½H°ý-®xÓÿÎ?ö½‡›÷¨N@C§h\%8 õàaçøâ|<ËýöJ”1¼¹6—®‘"[? “c% Þ@EÀœù!=¡ ÛgÄ‚ÊôÅœ£K;=ûyݧ}ã6?€}9‚ÿl®‹·ÜoBßÄ $šDRöʱ–aðIjº¨¼ãÚ”Nq7æÁÉ,K¬æ3õ^îͦIìSØmôuÕÂ-÷âPçZVoÏvó…¡!®çLú:OþµMWSV°²‰MWýyggjh[7»i›5¼Š ³Ï ‰ôQ•éìØ€·g×Ñá§“Fì¯;yœ=u‘ç¾'’*MúJ¹ðLh3Û‹ 0¥ó¹´ÖC¸ãt;û)þªõ‡±]غ|.gâÆË0wÛ_,Џù±kȶÏVÄÙò·Hé—ý-+GÏdj¿ W^™ „œ¥O6ÇAß9ä÷ÀÒíiØ»< |õoa¯·7± b7Z `Í78s;ŽYiÆÔ%b0ɽ•s¿!ÆÔV‡CÉšidRûãµÙRy!¦²W›uþ²"ï/‰áÍH¬ž¹‰‰¾´Ê“Äéà{Ò›eïÀ#Š‹°MB‡¤~x §¶2ïžÇ#øÏîñõUGÝhžøÇ á¯-á°FÙ€ÜYDÀ/|…S<'¢1y,YT'„HÏ–˜mÑ—ÿÀ dÔürÑ4ôGŸ Ê${ù|øêÁ@¬û¶EßãÔ—=pHloŒ¹óœ‰^ÍtiO‡ŽÉ´pŠ-{úw÷´LšýÔÏÓ›5ž=«dYý3Aìa½®Ÿ‡_´õÁìþhrvŸ6ÛRÄMº Ff$fwž©Ï„9K7q!Ÿ޳GøÏKk3|¿Ôà :áŠ{“{kL±Ìï Xfj2ï†9xômýP;†èY¯µÊy¾Æ$$Ø’™©¿‚mÛëðñM!Vè8†s5Š¥÷ °ÞíÀr7CTƒ-Öl?„!›Sá_¼£>~—Ų¼PS1†MŠô1ù„ýÕ·¹mªäꥅ˜¸½„Óò@抂éŸå¸=Úˆçÿ#®êÝðæû 8xšÚ7´ŸølÜŸ¿ôÔÔhxÚ5…wbŒYµ-æ¾:ßJ±ÜN˜ðß%oÄbs×DŠxõiøof ñùeT@ h¬œ?Ÿô\À–Ò#ü¿~‘×?}K>Äq&³k¸ösÖ\_þK\ÖC]¾Ù³k³l‘™þâ5¦$êu3wåk0.ß‘ ³Oº¹ Þ×ÇÞp¶«sù M·ÕH÷N¼?3œ*õ|Æ–:¸hñnïü!X²d;«‘/ ºü÷è"¶”†yGËráªåY˜øU•mQ Ã+‰—`¬¾ïPÓ_¨›¦ŠYr90eô ’Âk¦Ê‘¬úÈdrIÆ” ÷,'©ß5Ø›^h¶#ÞIeÑJóyL±AvÆÄáôR9ƒ çŒÙÅ_Òl(|-ʘC$ ÉŒ?¿GøoÏé'îÙ·Çp%†hnüC—>ÇÀÀWø?àŒÓ…¸çNt:´óíÍÆåËŸÁ¤¹í±ðOô¶ƒ4YKJQ`Ž8m?Èß BNLÓ!9o¿ñœ;v£¹† oU&‡gcà W¸Ò¾¬Æú®eìÞ–uLqÒ(¬þ`N¦}¾ÏEM¨kÓÂñ3É5ã¯(+Ëçz’_Ãm™ið)*¶Ìâö^^îuïàÆgrêíX6vH 6nÃÆÑ÷ôáù4ȃà9 —õªpi‡à‡`%x? r<ðó;âóø(¯†îÄÂüF[b¸m'Àùê9.úÉBˆšß‚,Â’oÉ —{Jò›œ<q©b1ÕSJ¢Ù!ˆvoFÛ»YXï—cXiÛ÷Gª ÕÇ`GÇ8{ŒwãØ*â­ nnâ¨ú«Ÿ^°ÛÑ.ipôÍeL¼}—[¿ã:ty6q÷tÌÉ$ã&ì80–ÍßÎIKFá·sKaì&¶ò£ |{XN‹¸6PR¹ ìÓ¿÷oásNªÛ’½Ús²/[Pîï{þ›ê/ØpLäñæ´>KŒl=:ô'rcv;r¢/-x¿}BïÛU¸Ùë9uéz7‚?dRÿêA1 ¬zå*+ð²è'zåêM¼<Žc¶ ®Cß_ <ä8Ý}BWýì…•6ß1?B•í\¢ÍêÓíQÊwúžY‚¿ÖŰºl9nnk5¼û6ŽÝÿ;“h¯M¥§ÀÙ¬püoO;íâ(Ðñ:M÷U®¥9F¢Ø0ï%ùõÀ[dYG›aò‚ïXtX Úb.£ØKî¡æ+pɃrìuà%¥ã–DÖÇ–~Û€I%„iˆ*3‰Íúø. ͘DœÄð±`,7=¹­»•áZb=—¡Å3]‹±”þKu¶!œ‘{0Â%û”Ô‡SÁ Þ«ê€[ç.qö Üû° gY$'”òÖÎ_ÆFÙÂH|Z5”ûnceÈŠÿjŽÉ£Nü?{`÷ '×{KšÜr…}ö÷ECIÔ‘wôi§1ç8ÞšëH¸³Ý ÷†…’Uûº!SÅÔ\v‘òõ¿šÆ™X¢S®ñ¢þÝÛÈ›¯âÀÅz)Ò¼ŸJðå•. å×Á˜mâWjÏ-'ciwY3Õ¦<Ø¡”À¿ñ:мy’î¾$Él÷\€ÓG}nòʃ*q 0Cv>¶­‡m:ap<åN¾_‰üôÎûï¾J'ôÖ;DNÃ-_ú‹ úýÛ<øtu2Ó*Æ’)¸Ýä5DðÓ°ìÒ2špµ§’`ÆÌ%j‘ tÍs1<²þ=¸„„vö Úþó WLæã€‰8Û(6•³ªˆõﳸYòdaÿöí™Î|)T2\ÛOÒ¶;iP³- °šmâàlbIE"ì¹fÜ³Šƒ¢%²´Ì‘ÛMÿ»þé_.YÉ äõ€ãñl\o½÷l¨¨žþ{„¾tÏ5EÙå{©xu~|Ó.iT 4fùWœ`ÿszÓ0®þá·ú îVf: =›5R¸Ç%f‰@Ô·I4ùÇ?©‡ v…s¶LVy(þц¥W⨓d86= €"5 šÒà—¦–âN7 ˜îxÙ8‘×ÓæàÊW´Î¬ÖçWpi’ZìlÿàRO*®2‡+p L¯"~ÞÁue>äYF€ÌÊ ìª\#‚…3Nï/Åè5twçA²jr/xdº`÷ÜÈl`F¾ ÓX½žŽ¹RG&ЧÿB¬Ø l©³bï>NÅ‚Éü‰““éŠBmX¦‚súnÃÅB1v?^‰h¬~FU›sFü¿£½„F¾v¡…7ù«÷ á^RB‹ua´ÙƒýÆSaÁÞåÜZZ˜“ ñÁŸpn²™úþ1œ¾&Æ6¿Ÿ†7vOdB•ã°³Jˆ‰¸%Âüƒ8üÇ4ޜӋ·I ‰Ç]v–@g½Ç½¶ d5ÛÚ©ÙgÅ™P‡üÿì?¦/üéâÒ¶Çó¢éè"!. R—Ø6ü^v&ç™r^þ‹>8nê~î–/Üõmðà釦¬Î œ½|.vÜÊ"í£u}Oéý•2¼[£¤v[æ6½^VŽ]YÁiÆ=ìMÇÌ'â÷:¡04|wØ Ïü,X»Én‚r v¸ Ò‰‹D)š™±öø:z­YŽÝÝý“Î^_ Þê0pUƒ|ÙµuöÌÁ¼-°Ê’cª•ÇðÞN!Às£pÅ7aæ½ì_Œ×=«vCìqÈXýïóëm€€ÌV±«^h‰¦IqÌáG*nÐ ¯æìÂýÆ×¨íì *'öˆšÚÿÖÿ?Åasß[CÖÅÉlðã7ø™îDªTŽÂN»rbv€x•&q;ŸÿÁDm䦌s'kú.¢wS&m²Ê0«YÜ;þ v™Nf=¯ê`™Ú”>Š˜G,d"“ÏÃÔWðC·—ÞÿnÏôJÁÈ|€Lê= <à玱µL˜Ù×ò¸ˆ_³¨ôãcD}—6 X>LS?8a·çô 1¼|žšeøá¯†AÌ¿z„‰\¹I ÿvqBlþ×JÚ@—ÿg"иv‘x GŸ•9ØuÑÑ жkõ¬¶}6ˆ-º‹3>oå ¼«:eÿK‹¥ôw¬}qU¬¾\€²?qýuPu›ð»Ï†³¬¢^Q£àÙ¿œë„,"Il?š‡n?q‰¼yU¬ËŽíy=d/LL‚Ê› lñö'úÅŒüÎNaÞǽpâÐnîï2giMO¸e+_­f|“`÷ þù7Ð|09jcƒÒŸ–p {&³£á}(áñ¢²®¡Þ27æ){¾£—…­³;Q:%ɦj©’ŒCά¨êUuœÁûÜ1™™};G;Ž<á¿’‹`Þå­°êö(¼ÞÎp¿û6ft<Ý|ÌõèOaÊÎ1ôcxšgÆ;þ*ö§•ຕgqûßB8ë’ˆÆæLDÿ4‡°3è>µŸ{Ý.g¸YåjìÒÖpæ s¡w¼<ë˜{‚îb¾<duŤ.ä/ dÃé…×ñt¦"ã߸½NÐfËÎÉ1±3£¹ÿj–UöAàLixnp —Ô¡œxhß:ÃUxâG¹ÃZ=ëƒÏBaÆE”¢‹˜a»[?m;jýM${,Œ˜Ò k¿¼åŸáCëß|ÝÛFæ¿ ;ÖÀ)±fÎBè-H{Ù ˜v,ˆ;Õá–X96öˆ8“ÛÜÊô´e:"Q¥ã÷cG;*É„‡B™ífz-É/ERôBå÷âƒÁh"èVŒÃ!;à=uêÔÂÖù†þâ —~`™›ZÀ „ãÈ´¹Èy×›hþs) ÍÜÍÔR*P÷C™0¦vI÷Û2ÄgüèæqÒ dAê–»X¥£jàmM j´ñu^@ò•ƒlpéð–<ÄTÄrH¦¦™u!Ÿw5CÈŒ^MVxïcçzÇà<‡îm§;vì1„ï1!»Szÿ·þ9eçqCÔ'eÐj“0rKæ$öLçè»çjLqN1Ý0™ˆµhX&ÊS4°µOÎácÇ\û ¥VØÔú¤UÄ`]²+ï±f6›ÐKzÝ8g\©G³;ëPF²…kT“g'OÆú~ wÿ¶€›E ÞØY ?—§ãu“šrÃoËÇPK^X&µ“œíÇ=¿oÒeûC14y‘õ“ûÐl oè˜ã Å\õÑY\Ý”ï]ô‚{uÜÞØ ÂïY[¹Û‚¿Àç~#ÎYâõ“†°q­7 eßÅõS¬ÐåkÌ>ñÇ÷éØß{{]xSŠªâ$¦sƒ9è‚ Ëë€{£Êa– ÀÛW;aàƒ>üy_„ò3áÆ ô¨Ó`Ά¯5—Ó+P!Ç–×Rͪu(g; CÖ<Qk4yö Jï2}œµ/"º3œ1®ó®kõ‚/ׂ¯#‘+’góßXÂþ§e|%7}®dýXC È{ŸPþjÍöÏE2̓á£Ì=¸ð¾ç?f8Ös<^ÜñÖŒzç'F’Λâ8kçþ›Tn~y 5½¯[“Õ±è‘9sYN¶œƒÞŠX<¼ü;›FÆ¿Òþ¬¨sázRâø§Î @Õa*–kB&í5';w,$ýj3Èõõ‘dÕ¥ljú¾\!Atx"¬YÏ÷”M''c`R8€Æ l» 9~i[J®¥BÊ•FÚ÷S“ªÃÃGû ï(Âí½”ýˆA©ëŠLPÇšïr*1:¯O>ŒÑä{ŸXÉmz FL“²Ñ`‰î&çÂjñú¬YlµOîܽŒ]aLÕŽ¡Ö=Ü(|š{šv‡ó"ÀzŸsŠ Ë5¡_´,¾û˜M|º¹ŽÏ¹7b"¤aÈf•^#ÿ½Ó÷-d›JõHýgpŠ;äÿJᤦ栗b­^)ˆ ê‚Fó^‚ÎÜ?ù;Ê 7qº§„Ùy‡›°à” ÷©ÿ'üw­þV¯Ã-¯âÀr ƒã@"z<ùV"Lö;/ç$¾×ã‡ïz èÆæÆ”â<Y—‹Š&–Ä@P‚LÑRáÝ´ï‚&|T–£…g3pÒëþý&Ìãp¹‡ÕWã¤HH–ðë“Àr³1ótˆµ ?ÐyíX»ä(ʵ&'ôw/z>Îþþƒk9ƒ;5À{ÔSpH‘gß'N?ÇÐjk 6:ÊDªú Œšò¿ùo:¿jéqsæ6rá7ÏÀö6;2qÕ_Ú~;ŸzZ{€×‹¥À«6ÆÏAZdÑõÍLü“Vç#&T•pö-xkå&^Éùš^ÂK +Ø2-96kô úö6Léƒq£qòåôWÂc^`¢ò“ÆÐgζ4ýëlheñ8Îiˆ\ÅóéÔpãeØüø§?7Ï€ ·8‡Ë³ýpý×ú~ÇD抳¹Å¶¶¨SÍ\MpbW.šêœÄ®'§ÈV-Cf¾P‡¶NgËŽéÒÑ™ìß58ºWÏŽ9‚:o;°½;Š-2WÁ_äë€^×â¹ü¨'P.´“AóUK!é¹.g'Á¼ÇéMw¬GŽ ¹ žç2f³ú øL/Â5UJ7zQÓÆ„¦ÛàÍ›HöZ…9(TþDýtKD1Sˆ^­MÚÓy䣺8.YîðÑ …^ãŠópó7cî~i+þ¡)ÜY¾5;²T–+˹™TÏÕ5Œr`‡¿eÁ¨}e:w9ûS†ÝyaÜFKUPÞ»‹”+Äòë$k z¾³1=À…MU`¥_ ˆü«Xxr6ü×ߪ€l˜ôÞžÆz±á•é¦õËÑ(ëËHþ÷VD’|ûDŸ_0Ä3g¨×üˆt™‹.K—ÁÉàÅdê·ë4dO;¼=EÀú’ Yã÷Š[²ë ¿¾Iéß& ô´ÿ*…Œc$CíÌúñ öŠ #Õ›ÍÔÆ—cùµã ÿPˆ¿´ƒùu…Á=qde¾!|2ÊÄé@ ÿ5tÉ‘¥ncˆ©ôÖpv%OJv ×Ô(ŒŸõÚЩæ<æųmkÎSï´˜Í+£UÉŸº¾¦‡9·á¸"Û•ø_M5ûó//®Vžˆz}:dHr5™ï\&Üy³?àù.[®p…»É—!_SƒFôo§¸=g÷0™îk~ò*æ„NoaGŽÐ;Éùäf†û,<̉L[ÁZ™zŒp¸£òœWfVÇ$3È’Zgþc»Tû”ß§x…×+ 'ä5Z/×þ¯gû¾‚û‡¢Fâÿ–pYš >åt%'¥ÁÎò.ˆ)Èåþîõ#Æïáâ r¨$;cA&a>fœ§cAɾÕ“ÁŒ‹}¸ŠVSi÷lˆçTÉŒ;ßÐÝf<•d˽›aÊ%An‘ìc|¦{”?8²ß_xþåâûk·°0æÈþ{Þú[˜òÕy—`õCÔþ^ˆ^m«(·ç0>˜€Öåã¾á¬¹b° Ö]äßHgyWU@N/,®˜Áå,vN[±i¯w -ë{S]‡ø5úññÆW\ê ºÃô®D°ò{‚ïŒFìµü<š¼‡Ÿ²GÙîéÿ´„ÚyÌ[]ÊØ‹V"\RÄý} ê+çs 4¦mT{Ü!ë®í'OÏf°gS›#+HߺVyO’Vo «$HmÍ{ˆ?th›6£òø„¼W„—N„qÿž YI‰lÙÝXÒ ÈV=¡å)MĿˑ%>)éºìhØ"¢¬LzêRYÅà"R+RJûe“–Òx2(4šmúžCl|nà©o¢Lç{*;ô1­êóØý{ÔÃå^=oà8vWý¢…Ÿí.ɘìx¡I/ÊŸd…>É`(ÉÖn.á¿ÓŠx£¥8\vã'^ï%G"w‚¬'eøiBm/jÊSÈÎÛ`?u“{M°;¬«˜ zKÙ˜&h#pƒžyäˆQ‰ïqþøPfyf/N[– ê•0É<šžÄ3Ÿ–ÇKÿ|P=öɹ‚?üÓ¿“ÜPËÆ’Ü¿€ ÂZàÒÆ2°H"S_b€ã.2%J“ÙC7ÞŸ®Í~üÄȸ6{`|Â~Hy&½avV£û‡,Øñ°#øÔGšIx˜¯Ípcm?¶nø7öæL#Û;šp®¹"ÚÞªÂÌ{›¹STHomþôÜÍ-ž-Mšvχœ½0yq6ÛÓ6ü™Bžüž‰é›¹O½k¸QiݰmLÍ­”!o¯<á+¼¸ÈÉ qðåèV»7 “JK 9|9K~ëĺšÇŒðš…ãym/úW£N[¾Äµ·3`¡ÿdòp³9;¶Ïž$¬ßÎŽøás; t ~³w‰‘öÆz¼wˆð¿ûÂ]ÿ@„q±P9ƒ­ø+ÅNÔ¦r _¡…’“埂þsÏÀù±7ä­3¼ ÜqXtF“Ìør–çúI›MzÐÇÒÁä %ÇB'²Ãt˳•`7º‹ ó_‹UKvƒj‚÷—öÑ’Øhdˆ6í»ÙŸÜ3ØÑUÉú]šˆV÷ ÞùûäGÆf從ÍaqÒÛÀï]=•”ìÖfŸ;ë¸4ͼÅBwÁ©§sdüoú~§TÁöK?^÷žÇÎ(†ˆòPV龙Ŷ=§.ÂÉSá#\ÄíIDèìr´g+ëܸÏô¤A$·ŸÚ±->°›ìâãšÂILì²#R;ÇE<>A1°ð)}?hÍlk™£^i›%ÎÖ¬rgGgMl>tj? ¬b›I°ç ²âë*âõb ¤ª‡±àá=xò‡ {÷æîî‰e—Eš N@¡ø}|q·ž§ÕÂ)O·Õá8˜œÀø,¨°4ß:ÿÚ™$pü0N®H&É—²È‡u3aÔÑRÒ¶“Œà¿ÎÕ~н=>lÙI)òìƒ ì÷žÂ~F)0³§=P##A6å•Â<‘K˜îËç„Tšÿ»7Týé_ˆšW …jOº'¾/Α[Þ¸=MÇ<:ŠF»ƒAè\¶m-ÇQ9¶ Ýë ÎË ›XOešÙäwöÕÂ|ÿX†ß|òùã<ç“òÙðøJ.’à\v~À–i\ï㵚çrRŸÁ¶ò*È~wž8%„£üÜÈ ß‰pºïP-l‚®Y‘‰W+èŽ5ù¸bþ©ÿž×èâ•ÌÝè—'’ªÐYš€Ã×5YÇÂyx½ÖŒŸ;æóßZRÄ©jÈ7`ЂùdãŠbrxíLr'Ò®yË'¥cµÍüo?èmß×£ÒžÐ5AŽÝëûÊ©˜¾åçú×Ò/iö¾x"ùq®´cʦ¸{ã‰]g7Z‰ê“ýWŠaÜ·7øÛÕ•è¼+`ë^FƒÝ QR£ ¹~º¸çŠ$ ïZÀ4ÀúÄÚ‡ñÁ+ÞŽsûQÏc<úO{ÈõXk“ýÑGñ¨b 6¯%%ã¿sîw~£`íXLjgÛ¿i±®CrP)ä 'h’­ç:áß{ƒÒ4y¢øc1;àõö[lÁѧ³Ó?:FðÛò„Ád¶äï׆Hˆ¸u'ÛÑß©ðáE¨Ø ²;“Þ¡ù¨›\è»v8Pw3>Äaëã¿(;¨D¦„B¼¡ ýǺدEB¼^uu‚Ç$H Uÿoi:ûÉ9{ÑÔ=Üü~ø;¥“˺o‹¥i‹aÿO˜Â“ÇëS–’– ¶0Ö±¤W¿£ß}¼x×jD¡úÂOê%DzºÒÙ/TQ{øÚ†}Dç!ul ÏuÏ.(ßy»òÓúÿéF.ê"l,Ú…~cßAÈsnü·D/ú_.qç ¼¤ÉξàU>wdþ+ª¸™†Ö›Â,Ã÷¸C­×öø½·Xs°–fï¶ 3êVsoïs‘Êø¬-†MâåÜ–ùjxž_ïÓPgÖ…1hÞÓ7¥| ^N[ )æŒüÍö¿V†uÝ*P¨·÷m B’ï…e1øº.ªD-a9—ë°ÆÈV6ü]˜ Ïòj`[ÜhÒúqW2ä ÃÃËQ}Ê2ÌK9Ž÷…IC@ôÿ(¡3Ù_q ðlÂö2ŒÓ›Âæ4@Ì;MŒÝ¢þKp[4ªE†ÎÒЛ¹ÜÿØî0 çõqR§oóM9{:ã˜Új}¸sn­Ü; YÑÝ1äXv~Ü~¾ÏǾáHxÙ=:‚ÿâ± .{ˆ(–M¥—‹Ÿà!áHzþèRÛ5 ÁÞ¯Ræ÷6Öà)‹ïT;P—ÞIŒ¼ ¹Ï‚“@>n`kìb%#ú瞬3hO2 ‡ðûÒeKðèû Ðì@~¦!6ØS§_ÝXáLaƒHƼϵýÞdQ˜=û3U‘Lon V=¢l§µóçË1æ#ŠÊqݰ³ø>w t3¹¯ð†ûRq“»QÑÃkëv§_´µ½ -´ci.¥ÜÓØ#w ‚GÁo¦Ëؘ§"ûë*—\¼×F<ÁG}ýÜ© rdâÜXqð '«†û´}2øJŠMƒ¨ UOo@ov0êrÇîgÁ´¯ysGKþ…C!=øÌI=å=IFÌ%Ðïï¿æÿÕ¿>»mB-vÇàçO0A|}:Ÿ7r:êräd‚Î^¨Æ=ÔHea?¯çq/èµ»»ÿ5ìH}À5÷šC Ën¨ ëÆÿþç¢[@<¾š_j%Vƒx0Ë€4”VRcY>gSµŒeyDxþ²à÷|ÍÕs3Û”Ùyò°ö-]=¨Èv¾ÁÔµ±˜5Ñ×7P•i¯àé§åœiâi¨Q¼ÌÝÌ©ã Él[nsžªXlŸÀJV]x¯†Ò'_sŸ¯Ë3ÿ"1ÌØ´>'HÓ[Rr¼ä9êµ€Ìÿ?’Æ+èÃ¥gnÓ•-Üų1éá2k×(ì¨@r ¹cá¼a^®|yKǕᮽãÉAÓn0SŠÙ^–g³ ­U…sˆ— ±ú@ÉGœV©JŒâEÙþOJ|wUÄnŸp Q&|?,g³…Î)üÂæØø¹Ž®²oùÚxZÇ›6Hgò¥|§ÑAIpT+—.ÕPsˆ-ƒ;µXvøX”¶4¥oÿʲÙoúá{Æ2âq}í9þÛžáú?_äâvôQã| ²š¾„ÔŽ¯`sB‹sßÕ­9¸çüÈI¹ƒÊsGøï•MØ’nÂ÷ÍàSQ.¤Ö{[pLÛa0ý{»Ü´ñ‰þ9(ý! Ö”@ŽÅ-HßpD:` $£êé,Åqh¬Ÿ…¹N||zè3>;SŘFq‡2Žpe¾ÑBäÅÔ|R™Êâ—£ÄÉŸ¸v]5~ÈÜÎݱHá<¦Z“§£š¹c›¿poá漽Hp’òAÍ? öÇU~„§e‰phU7,Àœ×óQ|Û7j©cÎnŽ©„ÊD"“­ÁüµRÝ÷’¬ü«0Úµ7ó.~RcNqÓÇJà÷e…xÚ{A·bdüçI<Äæ¨ tüs“N~µŒëÂÉç`áxÍßIàÛìæ‘Q´·u Ö­ÐÃí]ôÊ¥[è&¼–,r¾Ï x«ÁÕIñl{^.N½1ŠTLíÆ††£P÷Tšï3qyÀ!å3':ÁŸî¾»€Öç~äñip+º`b¿9ÛÉ­$K¦Å`@¹/Ky{oÑIØ“Ê]!ŸÖhñš¹5ù9Öñoç®Ã'ÀÓá•Ö?ƒƒ÷¬ÈìQRDxÖ?îª_‹îvã:ý$úhÉ îm`:NJðBnÍv¼ûd'6 W;´T)³uú2°È¬”xgøäý¼-Ŷ<ö8žî‡ ¿V‚VàµôælÉD¯§Á¼Ì†ôjÿ‹5¥÷pMìgÎøStìÀÙÎ:¸ÖÆÃ·ôîÏT2%¶G˜0ø´ŽóÑJ76wbÀÝü¬ ºÜ©øNúÍ{¦^hBãë¦PÇ¿Jg‹Ÿ¡_¦½ã Þ³`§W"¶Íè$×u«ÍÅSéôRôjÛÅ-·ÏFÃ"hS”ÂæžŠ ºümœ«çmឤ5™Å/åŸL Ãw“"ðJO>ÎT";Î@xa/” ÈvÕ¨¼-ÇìYuó=°}5…ý~7dVîÄ»}ëy†ò•èt|ûë Cô/9²ŸïŒáÍûÏà¡Àê¯ÜÂ÷ÞËÙ† @ãE5É‚;×ésåÚ‘ñÿØïõyÍÇ1g¹ÿ3¨S.Œ=ÛÅ™ÝÙEMŽÚþ¤LUôTDBƒo:×b-@ö4«áý£1DJÝ}ff‘›‹4IñùùLf_/• _×ïÙ3e95b¼å6=ÂK ã§xíØ÷¦ÑRÅU¹ì]!c{‡Jèä€N×â$¾>ÐnžÜ÷ĵìϺ*þ¹µ‰Üb¥lZR8+Xå/vgéÀ©ýôùF;x¼n ©¿sO<Fš<<\QŽ›OƒeËm[ 4Ôì .¶W ß$Y«h3äl» Þâ°Ûd"ÛwÊ””ÙmYÿÐ*-£]O&a̦œPîoùßžÄylž(™}×+µÉTUòßžÉ\°&ï\‹AÅÃ?©Ä”C\`æ>ls¦—>ºÑ™UÅÜn÷ñ¤$÷MåMC£G&¬8ΊôŒÍ_'f²cÌß>¼T¼Q'Ȧ|w„ ka0~Úyðß_u ·àœÐZ;;MÖxѾ@|©œžŸIù™$ø$eˆ«îÌŠ8\™ýïöѾ«á×@ŒÒsÄy1²ìåníJ®>úd_®ÆLé»ÐîbDz®j`tX6g0þ,]n¼SWðßÔ.E2¸k)>×V`ïwǾÃ'Ùòé…¼àWïAƸ ]Ø7¼”" Ç3·R¡wú¬?Tw˲Sݺ¤Öõ<a2·yÜW¦^½Î$º²v|j†ÒC›ñãÖ¯ÜÍ(AgFaê‘ÍÐåÈ#±•zôË–°ó¬*ªüÄòÎ>ÈÖk‚Ú´Îéµ9Ùk¥Êì” ó®žƒ?oš1Ü({g„âui xx’Ó}8»€«^ÁrÇR°œ‹­‘C\i~<ß¹ugП;GÁ™'Ñáí,Ö-b€~K99?)<¬cN«'m%9OsFü?þÅh¬Y1itÃc’`£p¯…ogîè ÊQÙ­Ä”hÓo9h›hDD‰ ßК­Iˆ;wÒ=¾ðé&g„ ‹â1³,ñ¹Ðr•wz«Z„A®àFFn$_j¥™ë”4ˆ¯¬Å œ£ž—¢'³ws·ºz6[òZªËðF°Ù鸛ÔxFÃ)YOöxZßåH<{v*ŸêGsÕ«“ñdqŽª/[Q¶€ÌWi£‡îҬͪä6äþ²¿pâýQfö]‰e^ÂKر4§NÁ¯OÃèu3h[’‰Y ÉÅ7ǰ·"wøç^˜¤c¦Ë%X2Žq?óŒXéB…™{0â\ÕQësx¼â2pçË9­›qÜ›ë8åbOü+¦ …Ÿ‚‰Ôj èI¿É[Xv¶Îõ~C»_”^ô~® àB4¿¢ƒyåéJ“ ¹É°å€öÈlæÈzƒœ®€níoŒ5 …+[Çaì÷å`BãðOÿw¨O¬¢­žêx^ÐüU3}é4®}°Æˆ}Ý»Ìød°·ð÷´kÔÄQ† L¦bè¥tü%‘Î#Þr”Áµ8ü¼báÁÌÃ<7î'}2ë $¾^@VN7g§{ óžNÛ¤Gz¤wBQQ7T|‘bï“5a’Êì5;Ê+zÀå`ýþ°gO]–»¯ƒÜ5 "é(Fªí+x÷.ű ’R&£÷‹c‘š¯ecúìZç¿Ç©Ìðû²Ëg,Ù³r¸ôZ¡·Ï%Œ±1bu•ÙZ1)bp*ž5†r®ÛÜIQþA§¹…åhy“âE–—¬È÷”…‚”ðp_1ò5<Ù`Gä‰l`NõYt­Ê]Þ;ÉØ¡ Lò@±!Ëþ«{ØÃ{¬¹€»:=·è‘–RE¼JßA‹F5MóÅÜ ±Ì\_ü–ÀrÞºún"J‹’s2øA«‡|Z¹ÐÃãÓ`swEÁ4É/àrg?$JˆP«ÔÜãëO8¥5cÈt~V÷…h»ã°©f>âÉžYKÊŽ‹á¼Mûq×ôÛX4Àq›²àiV†ŽÂ¤‹_ÁŠxÃøç¦ØÖÑý?~aˆ‹/…3ÿââÊ1ÓÈ›â ìÜr)ò_môÚâêþW€Ý¾ô“EÓ¿6øâ‘:{<åüÒ3ÀˆDƒþ[_x%Ïü¤Ë:‹x쵘]C¥1FDée!¬žÓÎ5frJ£¾ðo¼|E7Z²‰l¼5Þ—jnÕᦎm†ÑÇØ·E ~ê‰cl¤Ÿg ¹aÄE<¦¡ÊßãAƒu›àõ&ÙÆƒfh•r(ý6ý5—´}.ÛgÔõ=¯èpþëÃájÄ2H,þÌ•u\Æúz)Ö/¦‰:ë1ËŠw45í:Öù8€tK^_)Å:ìsÐh&v2©Rê† …d<{“s­g-UñŠùS̹ÛO?ÍLÙÕÏù§¬Ã ßÆŠ)ÍŸ€ FôØÁ¿ôÊïa¼²ä*-·ü‚[Ų騒…`©QÆEì6€O°?XÂɹ,{KEìª0qB=?ýõ øÝ£„=0: ÎÖ/XG'ý‚éÂ9L¨>–ªÙ³YD»4ˆ[ù´#m—Ó½B¥rð8½ÝlGòJëq³àxˆ9zÚAZDŠ™f€SÉ1,kM÷ÙûðFÀfëù\ûnn0öÒx¦wl! 5~ÛŒ‡â0¹)†%¯ÊÂþËB¬¾@ þ>‹Å'{äXûä(Èɾ MùðZ±Òµ„18¾žpΠ1ÀVšë‘̽k™Zü?{o9Îí^où:yáËóaÎìGtìŠV°Øo¤ú XºVïAôWP¿~~‹ãÀ€ 8[ŸŠÁAFlÁîjûZº°bzJ‡è‘_›œáøŸQì}ÙWÙ:²þ›ñ5Uïâ”=ýAuÙ)€ïêÞb@'>/üÊûtoY±:Cž¶`ÚýÝø)í* ÎÅžås˜þË/p^9ˆ-©J–ýV„ŸAÈ7»±d§Þ{è+ɾåÃÁÎ;oß—¿jÄlOa±ö‰—Qq Ž%*ëuØÉŠR8>—½â‡’;ÚÛÉ›ï‘L­"<Û‘\§újí{ÎÖv=ø¿sbL¸,ó &Ûá² «ûÑjlõL]¶mÂ$hI¾G"Z2IÛ›¥Ìí‹0SÉxŠ%K›`¿N;o£i!ÂrmnÌ»4|7’ÿål,£ïG§¡QÖYîÉãþ¥g¦¿g7òr¢ùâ?=q_k"åö½áö.›Âž;üáÜEº¸ç'$ჺK·zÈÙ•ž§o¼ÓYw¯?jÖj0\-À^ÊηgÓHDÃ0.mÞÎ9‹¡^Ë í<ĸ«QGÊBÃÝyìŒ^JÙæñÑól÷¯ÉR˜`ÉÎD²—‹ÊØ¡©¸èÊvt‡/NùóƒºiS‹á>TËJžL¢9ƒ|ñ,fù/g›¡˜É{¾4%d èÉØ<Ž-M„ç¹ "Ñõî´p‰Í7ÞŒðÖØ\|;7ë-ÁªM˜­°]H–™ßGqC-¦y}±›"C=žÇ< Ó?j«¶4œxU€åCJ´=©ˆ‰§_ i’Ý dÁ}-bʱѫ…O'­ÏQ°§à ,<ƒl.ÓÈ? 6Œc×¶%zsØ'ÙRòæÖJæ¢9‡Ìÿå‚g~t8˜ÌEŠ& FÉ HjŽ7wt‚ ªg‘äTKâÿ Æõ 4Kö|fŸº³Øn?ðfœeŽv°ç‘Yjº‡hë„ý%‡hþ¹"rø¾é~ðÕ…Ç“m‚’„÷Û½ÓKÇä_#ø¥ùyèò3Z#çš­$Q{ˆ²î!f‘;§·ˆí4dN“Ð@@÷$ë§Xq›öº’¼?Üx§ª§Ëš+0áÕTØÚJ¿Ë—ÑØC¡Ýp`O+¼\*D4ý±za $>žAò°Óåd»ßG…#¯À}ïFz®}2\ØèWì/ÐÃO/P¡çÖôó1ÒvàW²eâ²]àvÕ NÚ<@—ùËp׺„ˆC񯯱ŸìòSp籨Ê:Ð/¦ºkèn;WìßoÊöEZ°à­(äp„­Z´œÉÕȲ9µ¡ÿÓ¿\9ÙÓJü+Š—à”ÎôøÈ›í5¨¡— Ž%¿Pãj!²þ©°õý°á%g"™JF…¿¤¼Ú…Ôßp {û³Šßq)ÊáÏÓ!îžÅO<Û¿¯âذ4[‚+µ•ÍE°IE–o[Ïw »>'ã¶I׌… •×ÿåbäPîiÞè¾ °i)é‹d5ºÛÀ+î ?nOŒŽžËލM©Ï\©‹8lXgM×N,„Ý÷œE×»œÜAWùˆAö"2CÙŒš5Ç‚«G+(ÌI"§}õØm›.”Ÿ"Mà^<8î\ Áh›(+ßîÄšu®â–—ÜÉ”½DºË¯ÅÔO½ ¾)KÝ—Z·¬‡ô6/‡w!npgkÙþ¾Z]^‘í¿mÚVp<¢B·÷³äeWp«'Ífæ±/VÊh‘ŧµÈ¼¼×|ç–$¢o%c6‡‰æ…3÷ ¢¢hL?âÓ› ¤X̽xàð´Þ/Mò üØß€ç¨|ú¬lh·ÿ“ÏS²:Š'ªÀç?Šl§ú[ñüÅsŸãÀ.™*rSžÅó¿"¯§ÖÀæ#]Àýi£]•Álæ5ErÎßÓbqë c²òбú¼/øð)ž=SEs ±Z¼Ìyˆëã™·ø7T+óÁÒçêÄgzø4+PÕ|ô3¾>n'Ñ2}ëÿΚÜÔu[†ÄmHfÂê£á”]ä áŒÅZQwþN&™ñÆœ*Ç’G5ü·53†àKž‹âåhw¼-Àöé¨c‘Õd6·&”µùý%}ºèžRÌ»#>Dà›{Ôˆü)Øó_Ý4¶ÇŽS~iÏãÖ r]9‰; ¡®MtuÚ0Q6é<µz>ß‚HâsÉ„åÏéàÄrÖ`ˆ¹Ç3IõŸ64i/„Î.¬óòKTÒ‰€å(÷©oDÿž»"Š6‹RaÕ?ÝÇ2•j˜?õçà`ÔaÌze¡Ž©Ðµ;w²l\m¼€l}»œ›’q 4\­qîYœÿ§…Ú<0=`¹m!ËK-ƒ(WaxéMò7§Á÷ ÉÄÝ:еø0Æn …£Ü3;»þë-œ¿Œ“OüLM_Í#c3ÌÈê ½¨™Kâ­†Ãç»KpŠÌâõ¼voýu4ì²"¸ŸßÒµ6Ãfx O{åÙV©¨òžõN†kÉèTÅ=V²$ËÞ*³^ø:8‰4ÆepÇ|Oã´ƒWðuâÿö¿¾¿¯Ë6R¾æ¡Éèõ*€l²g"Ÿ´Ø’M. !F‰O& [¯ó•r•HmÀ#pnøÀÙžßÆJˆ³øj£¢™ŠP¯Ã‡êJô|6>²Ù>øØÍ‰•é‡Uãhâdp‡šï„*Ÿîq§ÜK©Öø[4ieŠº…á¹ÅŸÐÆÑ›7 Aó㛾M…dji#!hÔP]Ú ãÙp¯![0™ÉJ¨²2­ |…ÕÊ™dX>”n`@杻‘;/WÀw9"?ÛöXæ@öFaæo_JE«”Èã:S^ÿ5W­ÙIæD6àßÞðŠ;üz*í•¡W·mef¾ƒÛƒud©h'•,ð¦qÚU¨3:4ƒtYóéøjΖhêÛ¿¨EDV‘×,ÙB˜‹_4¬¸8¾0+ü)G†VZáºjL7T …s²é3þN–vê^{ æ/³ÖO‚DT/‰n± Áñ¡2Ø•)ë2 ‰í¯0øÍžÝlͺGLÿÅ·RQþÛ‰¼Y<˜=Ìøò[ë“¶‰¹\n  Z¸L¿«³±iÅøöƒ+4Œà„5ÝXð¤h2¥í+”sÿ4u†~gÅÌzòwVà•ÔËV6ÀÞG¨Ãðæ¯Yù'–dÝiŸÌÎ¥â5¼e µZEd]>ƒÇ~qxiË|]͉ä=-òé’"Yq}>s¶\I¢³³!çB-¸˜M †’_­y}.]UƒZ©C”e˜Y£#2ÉÇPdA\‚së£7²ÀØdhPn„ÿ4‹aʃ'ô¿:e¨ãÙÕœ@H*{¶–g9s÷ž½ÇždSËÏ"N[!Lâ4“JœLBÆÇ¥RÅX2“Üýͽx˜KþšˆÀKÏöÀå>œÿñ†Þ=‡Hjúƒ·f-i•öe3¿ù²mpÕnçå¿ÜÖÄBvÝ9Ÿ$¼–^K;Úž´¼Šm­›È¼9§ÿ]!ò;á7¶ÝÐ$ç–7ÃŽ² ,híYR¥MÖ fÒÏ€q³ØýQ¦,É_•8›!«¨Û]LŒÄR@[á.T[ù‘Òe¤rK5_쌩ž’ÄFFj$þ݃Ìʋ֮àÝ8Fþœñ!S[0˜œŽƒ-úd0ôfÀUúäÜ,¦Ð™ ž?qìYÒ]O™¢êe¾‰²·Áö)#37¾Ã€(M>u!¨ Od‹:2ɦŸ¶ì€Ò!¸½ZxG§°/[ÒÀ%=š¦ë°ÅÇRù¹†+äZgÕÀ˜N €Q ­k <[xfh~Eç€8´åÌXN†ñ>ç%XHO¬Ìÿ—–$`ÎzX@ipm"±.ÍN¨ªe¡çoÊ·ßq•W›Ì»$Û´ ÚŒýÃÈ'ÓÝ!¡EŒÝéýßüG¿GŒO¬kRoÊvÞ ÄÃwÚ`Qõv¼®WÊË0ZCõÊİáäoN¨¨b)Ã=ùK‚¡†H³WT™¿Ž]w!‹îLdïöøsƒaàšš ç7ÁÀÃ4+2,ï}ƒ÷}‡xZ¿6Á:caV!QEL‚¥w-Ê=ƒ–ãmØÞŽ~GÝaI– SýÃУïý´f*Ù!†üÔ•¤üÄJ²½¯ζ~窶ŸDk;G¼•C§ø¯æ½VùM';ŕ׻¢o_HرÙm oÆ*Ö·§nw'±oаgÛ<Ì(KÇU/ P·}2-¹œ@Þþã¶EIÐ5w3[ÿ’—ã!Ax;¡âCG6mý*²£I¹uÙ³·8øSsˆkî{:GÑE[œhÿËÓöÞ…ïÝ™µ…ybÚƒöY¬QÅMR:0É#­iGt)šÅ1§O&ètÜŽÅ[m Ó—±%~oèϋɜ×Üé8¿li&ÑdLíD©¤õNÑÇî™™´“Ä ³¯í5ð[w2{¡|ßÖÅ‘Á„–,_ód5¢üË{Äÿ矡:éoñÏÆ ÀÇ®¨3õEÉÿøAŽûD}w÷9çõº\ãù|žÇå:7¤uÉ)Ùðßâ£DÉ{mz¡Í‘o˜›E9tµB3,n´$ )ì¿ú¿B|&[t(ƒmº ÃÞOy¥£„:5;8­ƒ$‘—á\Ä•fEhõw6vg3\“‰öŠGqߥs1+A‡â¿`Ýúã¨å¨@goµÀQ?5ØœªÏv_OƒãºQŒêÒMDgJL-Ü—†¨uóñêÓÉ$ð¦=6ÝsÅ'?‚n®tµsYy£›LÁ§sì‘íç‘Y¡K•#xQ\$½vw3^’iÇÓ÷Óaá%rÍþÈÏ&NšŸT_îC®›þ»™aH(À!mZ”@ðø–×ì!WðŸvƒùÙ²Ÿ´FyÛýô«…¾ãý ] ñ/ÿÓ¾…ó³Èê莿ÔÓÙÿ›ùüî’,'¸d.áiOƒÛˆŠ'áj÷ fãú‡Œš¿;í[º8ßÞã?,Ÿ¨58™N¹x‚Ü,«fYa7ºVf*¹”N Æ@è?y§ªH×l¸T—ˆê6k©ëa ÇliùùΪõ²D/=§ßÙA«Ôß1gè±M´©º”lÿ‚ƒËYÜ%$O {‹@ìgõ ^…©ïôÐbí *ÈRÏ3¿pË+Ð*™‡®_êñƒO$xÓ¹‘R",Ø­Ž†³g¹ì›ÕKIC§92 ñÏÿ]ýwpËÁ öé–Ó8ºXŠä¯_†1¨Ç®xN?³*bŸ1Ï«”‘No>â#Ÿ86Èø g=¾ GlàÅm-öªý&ÂaéC}ÊC]ć£Ò ]^ Ãç¾3Šj_Á¨4)eWàÇ}±×YÎY=íØ› ýîø° ”…º³ë!ß{ŠfsÙ§¿îQ„õÓç·-O>ª¯)pùɘ'`е“O€æövø+)KŒcfÒ©2ö€ÔFÈó2¡[u`S^#kÙÈR­;x(¨ÔÆ¢Ëú[°!ì»C’ïæù?³V†åLän¯¦)úÌ•¤ŸšÕ°ýêFúi¿*ÑÕ,g…­•I©s)*; S¬ˆãk7Ò%-ËÆáL¼"Ðý 0;Í þ›½<2tVlp…oÍ&èß9 u¡½L%Œxˆ“Dåt¸ „º»åàŽØÙÈ)®ebu?³ù?¤¨n\ ë’%EŽ{êÓ›;Ðh¨šW¹@ÝÂóLeÕ|5T_{òë´ÍÌA³ËÛé£wß&4Œ1™<€Sª06>ŒÙ8s2uíMÄdéääê°=t‰D{¾`¢ß¿ÀœÝ@Yc?.O"£¶dÑüçðw~’,Ø ™©$¨D—YnüuøÜpþÓ2VR¸–µölÂYyq8cZëÝóõçŽ3ºyÒ~¨_ÎØ¥Ó…ѾT, £œîg•ØÎÕ§ºÑVtï¯v,X׎¢­ïì¿EDÑ¢¢$j›@“¯ œéNô8þ?)‰èíSì‰+§P@ÿ8é ó ZO\1HÏË·YêۧÑãϲòצfÑRߕǾ}p’,í3 ~Ü*çB…Ï]äêé œ9ÿ‡ç¡êÒRì—‹ñ»62ó—ÿ××]»ø~&s¯GˆfFC}×40]òÿni—Ñ)PÖ¶‰ìünK‡¯w²s;S¥{ga™Á^ÒöÀ´"´¨[º0‘¼¼™<érÁV}MZôÈ™¾kK†´šk"äL΄ Ãk¡@x~ †/iÐØQ1òv· Ñ̳E1K2¾ÿ7Ö.J¡Ëxpóž)¸äÙAØ÷7”z«âþH~(ç)†ÜÜYDpÐÝsÍ Ñx„{jhDÅ>ô˜tƒ+ù! 6à¥ÂÕQpµ#Ú§na¯ðF¡e¤5þÈðï•£ÑLlôÞCd@‹ÞÈ3¡>‹¹¬¥é"ÑfH{L¤T^po¨]Æ¢Dö?±òÄ´u¯!<° ¯Ö0_ôMhF—sja9ÆÇ Å¤lònåîÚ}›c`„OЬî=Ê–H)ÂÜ Ø)^ÔÈÔ/öŽ0SïOè_N;{#ì/ø¨C—®Qkj² ‹pƒ–¢´û³ÍðÓ¬hèß «Ã91ËàÝAq5O‘ú¯©¸³,œ¤8}Á‘©ÌßÎÛoº)þÙЀ;j^Á’™®¹qÆ™ ðV& Êθ&­‡Õ1¿±’árÍ¡K ÝÖ‚ÿí¥îº4=9 @_^;Àžo[<îaÿòÒÚb6Ëçû#ä >]ôW<d>ÔKM¦yOäæ[úRÝ›<\M6 È“#&‰ÁgO¶ÙåR«WȲþs§«M6U&+úFXñ½ÛÙÿ®Ýz…Â’îâæ<‚ABÒT"ÄšÃh𥠕õ°äTÜg¨ÿ‰˜"ªÌm-U%7x˜ , ­­¬£—²õi•Ø|té,‰;í£é±c‚Dác)¾¾üç.έûú¨-Ç7×’±!P ª‚éøœ\Ð\¾¾¿Ð¹HŸ>*¼Ã¼Úrê)XcF>ö´¹õ?û´%qiˆ0êÿÆ©OØóõ5˜=Zl)Âôy¡ÄÒ`YlúOìŸÞ¬Fm®'Îij€Æ™:T}ÁBj½)•}Œ»ß ÀÁÚôqŠ+µ4ȃåþ™pX½ôÚÎÛoøœÆ„W¹‡ñ~må#Diæ-)Eÿh6AÖžHŸT¢ÜQyV,l'~´ÓH©æíè#P¶ìbÚ‚Žâ¨E°Ã§–ϸ¬Ô‚ªúÇbG‚#c½ñ=Џ “?Œ iò£í¥>*Û˜ë[i·¡UïbŸë:ž4{ÈÖê‚£ÏOÐ) lDì1ˆæ1ø÷ü÷øâf·°~KDqÙZTç¡çÖ[‘I¶›ˆëÜ3tÉ>™D~•P‹:Y7û<È\(â¨í>I‚½ÿbu» ê|‰DÝCä€íwlŽœ‹Ò)(g3ìÿ„!EŒçObÙŒn—ÂûiWZ­?/™?¾µ\¹ZÝ1ùš¡Nޤ¶ù,øì¨E³³›¥L[Zl‰Þº0X÷ùìSNå 5ôT²þà>Á£©J@3j±M}69oÿÇN«2‹å6cïédXá·H w¢¢/=àä 2Ü‘œlíÿ½ÿµL’ø™Y»]0o÷yôŸ¦Õöï15±£Êøï ¸°õ0ç]Út’ƒ·쀄`xID/'Ï«ððúG¨Ä/DŠv'ca„](™ƒ3 ¥6;R©xYŒüS Ñžó¨ª_:êLA‡B1jñÜoiwáÁeš´9C”8ª‡° SQAè{-I#äHÊûHL1ü_ÿ§X&ó%æ ðë'@Ø\26EÙ÷‰Ð9O{f¨ç[rÐh¶ˆýÙ/J¼¦P¡û4òÚrŽ ãþ}'Ÿ›Ê¿ˆ…—âÄIá#líÒ#"³Hóó¯ŒwûdTç…_ùuìÏÐr¦ïÊCñ¼Š­·…IyJ ÞÌÔÆ¥ ¼]×€+Þ‡2ÿõMÿñºÁD|ÍàæˆÙ¢…_ ¼ >ŽõÖÂÞ¼%¤fyT«×ÃÓoÏ8Ë>:XEî~aè¾|E ìç<@µ.Üà ‚ZµpàÎdü¶(@ìDÝÛüNh¿f·üø‚eWmÉõ‚tÜoçÁÙ)ã ©¿áÓÎ3ìïyï±Uw´sè³WñúÚy¤ÜáoM;®SÉÆÓ_ͱ{Œ¶ä§¡~·à»Ú+P»0€“¾+ á8â¯ZäHën¼¶s1½ÆŽ,?2‚<Í·é…*#|rÂÎpF2*Iô‚-@ä DÙ25ݱ}'Mcd÷àä|%zZYgÆY3eV"D! _>zÿ͉žr@¾âUÁY'‡þáŸ|ÞQø¦âà1OLJMè£pEÒÖ/Kr·2t& IÑážz´7¼ƒ–á,ÆÍ»m×Ù½SváÚ: žrJlšS39Tex/Âòy¿ñ·¥yüðdSMaÆ«:2Œ„XÿªP~yiô$3œ<ñeKîóšéг—°Z¢5*2‰tr&6HÀ@è ÔHÒqeòò×r6vßYØrïNëà%¿å{jz]ƒYZ:ì kwÐs²8^Š<^IlËœauâ(UÉÃh…ɤ[Onˆ¾Ù[  ï$ëýRŽîÖü‡‹÷‹¶sð{ã3™9/ÝCØg…ÁaçDîÎÀ¤D'¸ÜVÁ4EÝC×%*ø¥2˜Ù^%ÂêVsÓŒóÙÒ âtó«ó`UY‹^‡_£†A"„-¾ÂˆŒïÄïóµÈ½y‡Ø:ágð28öN3%½/ߤfàR¡z¬øÃì3[ :šqâxí×#ÏØç³ÔéÁ)‚$VóºƒÈøfµbÛF²cËÉ·ß¼tü÷= ;éGÎ (ÒUnšÔÄÇ­¼=ÀüÙõ³/U3koÔ@šòãÚÉ“Ó@=ýƒäŠ‹Àüýxó‡ÝAìˆä¶.ý—ÿõ"­Üåð’¦ý]܆Þ•ðJCʼØCGã .r7 dð€¹œ™jÂ&k¬ù µ©)ß™Ñ}§0ãüfh0cí\AO¥þ~á…¾½8û05ì`¾Üauž<ÀÏ»W¡í=ñÞ€*ô@EŸr&ŒÓ*˜¾&Žõ©ñE‡V;ÌQ|똌qçLȘàÎ]˜1áÜeQƸí8;±_¦hùU˜çä B±âä\ö5·$êëŠ@bà.[ßøœàï€TpìÅÍœ]Ëm”äŽÍÇIlÆ(gÁœíÿâÿ¿>d¾‘(fÞ7Hæ‘Ε1Ú !&øî”õÚ‘Á©$Áð4ijZxn:®§÷“k¡¿Îædë°]‚™/ƒpC}> ~8‚‘×[!î&Y³ë,Ö>?LÍvµ3aúapï´ø‡QD2»§ü>Š¿ZAfƒå÷«ŒÝ†ft-Ã|%ô»·]š¬œ7“BioÐà=Ë +ám†Ù¨³˜ŽQ*)`DÅç~­7F±ùôJ˜bKŸ\= Ú.’ÿÝ.ËkÂé¼rº€ãyæçÜ¢F¦ &m‚Ã@Ù<ô½Ú‡Súá«ä ‡×ëZ™G{\^é’r7!ø™9×ÿvï¤]ÛØOïv€»±zLÖfT~šÜ–ÆØ­\‘!Ì­›œj²Þë©Ð€¥qpl'Å«S0ôžæ¿øß© [_܃W²"Xµâ(yÆSˆ;Æ=˜¨ÄqF=`6g6Õ{cg[óòY(%9Uã!Û¢ƒð@V;Á# §U §‹yÉMg Jÿøß9;EnÄ·Sh¬s%˜‘‘}{èÕm¸m·}ç׈*²D»X›.\|‘›yi9y–p—)8?ζ=žK†WCá÷8Ïd=Ä~TCG<Á¥o &Ò‚ü}$Iÿª?ÅkUÎøpÅÜö‹`X.ï“x¹lB‡5ÓÙtÞ—ðòæTô3ÜI÷kžB¾mG±T,—Ý|¼ ¶•#@µ+¹w·#_úÒÉ-®`a Àvwaÿ›G]æÒA»3|i:ïzpôŠc¥?Ê5G%Ú…<̓›ùSP€L!5õüD=“ÎÖâ-Ÿ˜»xmkgÐKd^.ùÉܹ¬²@ÿÔL\e'…vMŸ¡çÛ< ä 3òñ3÷Œ—EŒÈ”M`í Wž—b[ÅF䯀ü’™ùu1|ÏR§U)Aì é(d„"¨þ—ÙxÐâ4D=ÌNç! )Ç=mTˆû´ïá­ë‘ÉY»O”ôÑ?0¤»gªR™Ýçá‚ç„–îÓ'…/^0†—Y†…h©cò/ÿÓ­7Á!:™=S·†\²ÃTžIðÔÚUžwpVÔ³›[ø‰ÏÂTÜkù™=‰ŽÛG!%D•ò/©‡:—ή”#Xd„ÛTÉ̂ßl`’7))—'{‚ØnqøÇp~Œ/AmwZw!_V;‡ngËGãÔ Üÿ 5ÐùÂñàÓ®@Fßæ²IÑ)”Ü€- ´ÐÒt!-NYÇïâã¦ÝЬqÞ5;@JÚGþžyU¹P`iýc}l…O1.švœ¼1„I¾ ÜÒãá̺7ÚDþs ëkRÌŒ`x“Ê¿øÿÔ…YQLZ¸í9Aõú;лu®ÕM`wJ˜ƒö‡¬îac cG>™E²Ž—e¨¾Ây²Å%ˆy¡v‰èSÑI{±pü ÁÌ\½¨£¬¥Å ÷ªÁ9¸°­‘‰¿˜œmèÆú$<ôÖÍHÃ,_4| ²›Mño¹ÝPÆ8Ú…%-w¢£­R;o8˜>7Zê3ïàíÄ«°]¡ÃøˆWçtú'8 —ß–"Y¬ÿÅÿ¦ñìÜùMø@KŒ¦.ôÄ㟞ÃÛ}vÌÜá+̃¹‰dªñ;èYd>‹:öè5–Þ8_™Ó ìõT¡ósåŽ=÷Qž fÞÕªCrÖeV_u&Ô=S ë—öo"ì$‡qmÓ©Š™/÷W`$å<±X›,æÅñ¨A‡s~F4,p 1‹þ A «YŽö3Žþ$Q_Ÿ‚Þ¬'©–פò/ÎâóÙz¤G9^B(ì]Lä¼ßàá‘säy‚J½w†4“1¸TŽ?r>¡­“¹.k>ì@«LEú¼·˜úÊ´‰È}ÊþÅÊNUl]<‰}"RÀH7Râ^Üy2˜Im]IíF÷‰=hû`:séêr0èÿGw1'Õ~‹Þ†¯=ÚÄLH‚ÞJ{ãîÑdÙI‹êTÙQÛqÈÔŠÞü›I]„ÅÈ—:ì…5S™ßœ¼Ø²€~ÔÒ¥Åß–Òø aÐ9ŸŠ+uá Y=Õ—t˜¾¤Wë]`¦$É„Ê]K1¥O«dNpËí¦“¨¸pö)O)œÙÿ™1ͧ|.g1$ŸrâÞ3­âÑ/õ!ºÇt Ÿ?îûuÿ›ýlÔ]¼0¶Û‰¶¦ä‚¢èÿæ_ 6žÄ1í3Ð}7Ì P—ó‡q–Upg`Ç)"x‘Üðö€¡ªÐ)Ò‡ û„pp®^Þt“ÀH±§(î1„™å¡È­·"N–Ñ9œH\'‚'qhÂUô”j„*¡y76Í4CEë|85HkÃ’®ãè;j²j)IÒ˯ù\÷—¨R…U›©Fp(ó_Ÿ3ÿ։Ț†?*.ïWAZ¢œÔ £Æ™Kô‰‡V¥pr×3–+/2v‹pªM6‰¼ú•Yxg19zþ!è,­f/ûÛ¥fÒÈ–TÁñûaæ‘#ÿðßêôØVÀ²ãZZŽ$•—¤6íÀy%1P×°¨å_!¾ß%Amrývr'}É+Öˆ“UÃó¡)3þ¸&I®s$—F¬è‚WÅlüÍh¦.ý³êÃãhj§8eœoUz@O -orŸéÇS·GöܾKç j¡-l†±ãñdø„„®(;«Sô|Ÿãûà óUþ&t4 Pñg´æéfçÚÓd `b£ÉŽRMªr«œsçI4ÑÖ‰[ê&Ì µE~äÏhØyû*Q§åû%PøÕ~rb¿©qÖ$—Í Y‡«Aö­î¿ø/¾ô·ªø3ZÅÃx+eÂÃ@ó3cÇ. ͻϚ)¼›Õ)ßÃ3|Ðûp=œ } ß7ŒÇÙ¯Øk|wŠQVä^*Ê¿•…ɤ wMbÞ~7ƒ¯4É–òp¦¡BDÞ³â“Ï@ó¾s°ƒ«ýÿç6GÛ?ÆáWäÇÍdÆ )¹øz)™ ]ñ£z;ØÔQcô|šÎÜkïc*ÑíS ªíK…­s©‹½wœÅº…¯˜?ÙɸûJ)&Õ†Âò}AäÈã«ö¼sÑ3*[¶lbå>kÐ7ÙžtÇM{*&ÕÁùo_ÿg?Ùº5n{I,lŸkŒÇ›ÀàñCPó´§y^“˜ÌÉØ>µÞH±÷#¨æ%¢þ©ð¸h¿ë'딄ê÷ñåÝXL_š„}lŠY8¸Ƨà­YªâuVà ,m`‚² nW",~ÌÁä{VTjÖ<˜|þ¾ç£Ü[À{®´ûÇpÓíäP§a.‡€Ìó$ãÁ TŽ÷®äåf.¬ û¡D´¾;@z}+óá -œz’ñPñiïÙïÇçaÊ9°RV&¥Å´ÿ±.X” ºU(xdÒýßü‹/`/0—ÆähóeäÏ:ÍJ‹žCëõ T¢ ©e–lGÓOf÷§°èH!™·iû#ÉŽyÀ3Ôï¹ Z]г·Ô7ïƒÓ‰W Áo×-)_U,JÏÛ¡©™h¾2^–ëCVèÍzÍ$~< þ:ã 'E‰)ÐÚû©5g¶<]zv­4¡¯œvBÄ–_;vTM·â¹g 98`F¢´ËP@q OÍîãîŒÆ¦1²=ë Ì4¢.1§hC¶ñ1³§dé4u´ª¹Ê(Ä_ƾ֩ÀùóýP«GE^Èýóÿ©_3@¡$ñâÿŠí91¸ÿ¾—Z =Ê­$·X”n<…ÏôԈŇ6h\|’±÷üvï¬)¯›]#° ©dçÆFC!bïÀC ƒ”è‡Ù(ézžV5‹Ð›|<{¯…Yiü ÜÍL17Æ'ÿÍiFV—6p~cÞýí$ç«% ZgϼˆÅµÒW¹Å›M¨îYºéFG8W‡pÚy¨û¬Éĥ…lw@çïÎKCûtZ®é;™¤]°.Ç+wsµÀ^^ë"V6M £–Î;>“ØüQÅ\Åÿì¯O …3÷žsêÊaã×2æÒæÓÜ-\iºâÄî1®†M«àédQú©º¦\¥™ñ´R¡wóàºkiðß è•â¡T)i/é° ›ñ%f|„táÖBü‚“èÛêÕð`3—Éz±‘ó~¾#^´pÃÐòzÔ ‹+CJà[Y¤­l†üäŨ/™AÞÜ–§xËÏ>Æ»6, a f^‚E[#Ù;‡7/-Á׈õvÄû.'àÇT«ñ‹¬¹óîÃXù2zpî1Ž$ÌÅ/ør£$ºaÒó» g{‰=Ö´¸î% X5‰+ll™"Õ=Z‰µ±œ}‹àpæ$b¼£Ÿ½;¬·ÅT!{ÎDÚî%TðÁ=ŽºŠC—±lÛJâQNû¼TààÙ0Mèm܈‹Šò šÿü7CÚƒwèxîEá·âL‹O >Þ2Á­ŸËÓ;sàD“‘Y«EâžxaÃïãuJB’®ñ·8ùh$^P"éUÕœ ¾M°ú\"Û_ƒ¶6çX33p±åMÓœ”[¥®ÿ³ÿùƒÇìùu«@ÓÚ}td*‘½(N_]´¢3ZW“‡Js¸øËš¶+Š·O@£eÛ¿Ã)ö'ÁÞë&;Õ)ŽóOP×Ñ(ÓG¶Ö“p¿ª@ÑíÛ(¸¾4Ú®šÏhªãƒq‚½píÎTâ«ÈO„{ è˜^În†Që8n†K2}82"@vB?˜Ð((¢kL'~£r>¿pnç=ð=²šÕÑÒĉçéù˜‡Wƒ‰àtBžl %ºkùÀðÝ'ÎîAoj徜:ŠÅ‚‹ÜÄ)˜î—ÌaO©Ÿ$RÈC{ 2›]ÿÅÿ×ôÖo^!>*cúr²Ø«PÞ\´ZÔÀ¤ú1:^Ä×ê(6ßžNüÎÐçñ{«¹´?Ž9]`»ouCê§eÜíê‹[¾e¼LÁêCNÕŒ3i†îXND΋ÒÎÊ«8çX%~ôêUxbÞ)?1[ÝnãƒÉP+WÌhX¢{Ú(³gŒ ‡ãÇ©?™OË®Ãpi3¤ ’ÜÂIÜÎ]úà¤øÄ’¨Øúudæ"*Wˆ5M#ðx$¤#8ãçöHÉ#ÈØÍOÕB;¾Å÷ÝòÄdCŽæÜA½ê7ÿøqÁ4 œÉwÆÙ™cCväÀ³A6à×#,üU9o€© /}³àF—}Æé¾‚Ä0œþ`ÍÿÓÃÊ- ÃÏá¬Å|òNø)÷…H(É|ô†M÷I‚ò·ñ:Œ¢[õ4Z¸Y‘·DÚo^„¥‡† h —Œm‘EŽB|Î1&óv¾B7ÎÂanS ; 4 º!)8t€—l]¨NeL/Ó‚ÃlŠ[Ö%#nYëF†n‚ÛÊäÍ g"ÕðJÓ ïËñ±íÒû4v4]1u& Qe9iþ&œÞ>¶–Š&üã‹þb3÷q6yÝt!ù›;6öÂTe*ér˜S]¸ôÁmÌ}ÈËä Ùõ šŠæ¢Üã\¸;o fŸÄpƒçfþ†»ÕÔ÷Q'<*žÅþxE¥Õ?3¹W(ç¾W?¶ÅÎ 'äô……´B™·}|ìÛ½“h_Ý2uTˆ2ŠßØ1¯Ì a è½ÿ‡ÄȽG‹9N‡ ‰¦7ÁBûúØõÈséðøÅ¯ÿ¿¾·bjÊ$€ó©vô 0#ÃΡþ/ÄDù4x{É‹®<8QËã¢XÛå¶ôùh7ú å XÿÏÿŠîd®²*x‡ž¥šÄYÊ£mäÍ:O’à#NF>Qºë0ãgz +¯m¥M¢)d[ÐD ëŸ$‹Ng`ú¬p s>Óš!úÆ#ø5ó9<îÌ]Î UzKÁµ $Tó(Ÿ¡½i äï=k~v1›äkâRpìæQzÓ9–jn¥š¶eØêeo£*Éæö³ÌÝÕy¬å}J¤ àéœExCLš¸8;Ñ‘5ëpo{míÿÙ?2QOïϸ€›†«Až眿ÛoúÒÁýgÀë™(6Ì_Nȱ®Þ§H|ƒòê{hd˜‘NåУ³ƒ±Ä9æÆ Ðáê“”ÿ71í€ÝOòÉôy’̾Ú*v9 »\`DWCùQrcþî4U ‰‡4vú‘£¨}<‘“#ßÅŸ²ä÷‘A,¢hM¤VƱåkNâsWö<{Ìó¾QX «R‰Çá'°Aí>~Zy ”›ÑC¿ˆå(`9)è:-ä¶Ÿ`ºJYõÊ¿ 5iÄí¶í»?°lëdrÆnÉ¿üº<]ް¯Û;aýñ~ÍŒõºM"Þ’Á,¼qÒ/Î`BæŠã‡F=âþZ™fmfÎ|È,8·ê¶ ßãAÆQ/ñx3;üyéZÍ9ÔcN6¤§E‚vO6žùÈ|N C·MèÛÃÑô•½¬ŠÓ¥‰k+À4ü#<TÆâ°m¤J1/ÜR£’u 0ÀWˆr+µÉlŠq¹"äÙDm°2uE½‹S ®].§XГ˜}G^3†&`l±5MmúçÐ;îʽåñ÷Pí{«¡KaÖ¾‡`ûÊ”¬PNcÊx™í®FWåGÒŸ¥°ïÝ£¦zœVÊ®¤›ã_Ò/>|¤øÑRžºHLê¬p°à7„o:NŠÎ/‡qý66ë?uw£~}f${—Ò‡=X'!èô'ŒÜ>ÖB%œ¯Â‚’W ;IDôôh–F å)ªc6ŸC¥baT*¬‡þé²àÿû8ü3—za‡M=RMæF£Ã7Crh©mÙè÷ßù”gÉ æpª'œŽ8öÂ!ø»÷Ô¼YFFÜæ@9ì©®öÿgÿÍ+ixãB ÛÙ#Aô—NÐÜç¸ãñà~ï‡@¨Æ¼Ú'ÈhßbÓõ>²î‚ìŸ.7ò#šÿ3éèž$åoE… á,?~i‚Ãú±oO¹y¡j+\s´¡ÇµªØ$×ÏŒúx7YD5Z\é¼Þ]yý3˜í’¤gœ¹Ì÷¨hnÔéÕØPm‹k ÐͳÃIvÔKh޲Bt:f«ŠXÎièÝ«>±>‹&œÀ\¼ Ãòæd͸ïÁOƒÍlPÁä&DÜÝAøªÛñ3ÿú;Â…„6¯G]Cœ®.À”‘ Pã¸ðó{£ª°avÙ0ÿ¥1 R|Ç ÒÝJœ<<`¨Ñ ×.8‹±>»ñÍ‘=̃GsÈ3`\6&õz_Á9uä>¾u+dÄì™Þ>T‰ËtdȹhVÙ3•íö¹ o3kio”=°üöH;Ó×´§@Ô±ûè]Xm#Gç ‰Òy¡íð>h/r›#¨ Y5œ—)fGgü¬}0ë ((lÀú|ËkÀ¹°â$•¶V$j1j”Çaâ¦ãî¦Du2’ËûNƒõs}òâD#èkMŸ?Öã¼»IËÌgïǪò:lß~D'÷Q˜°ñßóŸÀ ïXëÏ/k çÒk…AÔ2æ m:Ÿ„#¾Ù0aŠw–`IC"Tø€¹‡Ž±×-l¿›n]ÆUrtPK†npCGo-Ê™s*~>Aá÷¡Ìã‡ñÜ ÀB¨6ÿË<ó_ oþì„͇U™®ón8åAéUÔdvÔ÷`Èé3×;ïË’4Ã8ÆM‹— 3n$:À‚ÄëµÀ®æ\Ê‚™J}@m©S<.†Xmk@;$Ž£H~&¼oÞ‡ $šÙ5Ѽ°jš ˆ-®ËÆUF¤ÈN†ò$"oO Ó®Åâ© 2E¶¤¶üEÚÿôÏb>˜»/wú»ÓÆ–´s²¦ƒÖ.¸éóØ6—ä®®äÒh7ªÓÕÈÌ64‚˜¹žÇ™èïTÀjUMð£+ËHï 1:¯`C„“`ùÐIN­B»9ÿ ¼çÏÄëMI=g-Y½†eC×Omâx>\˜jðÔÁ½7ØŸÌ6‹ àdŽ+µ³¡¿ï^Ç—¯µ1’æ„N«!ÏSx>:‰ùÙ|o^ 3ÜÅŒ¥U°I"ª„HÌ`úÖ'a3É…ÎWñÚ³\æÓ¥ Y¾ŠÁU:Dd§®%Ã6%bÖ_‚u*‹ 2ÿù_Óù<Ó?”Áœp:Eœäèœ`A:™ go•Óç zÊN~kÞJу‹ïZÌáh×{ æÓ[‡$™y)Fô£ýNFé!uX¼†–úžliqzòrÊœJôr[È%¾ ´W7 6kgÆÍADÝôL¾Iž ãÍ£_°e× jÍÐ&>GbP=i)%~—™A’ôšš>Ùº¿òfÉ:g°¸•GóýÐØÕ©Õ'¢¹Èåɨì kb–™I¸ãð–ðú´b÷½½ ÷›ˆÚær8ž‡t£ÀF¡”»úw7 ä>SÙ¥„o†”ÿå¿ða%XWœ ‡—ƒÒÚ)ví˜&èÿñ]üØmõâdEl ôE)’c!<äã×0¼,šøéÒ$m4^É0.†ÏXw«b8tPžÆòÑ(gOnÒÊP²[û*tš2¢u™T-^„ˆÖiA“.53‹§)¢%dI?…µ?ÛpÉÎT¨Ž¥¹|‘à·á I?‹G-¼É‚óG1Kσ\O½Í±/È"»ì·ü¨9½¿‹$„šÑýCäç£8´níÀf®7=ü"ƒõYzˆ˜HçÄ‘-Ù‰kçA^ó„6M?ÎT©O£¼Å0YÁñþuˆ[Bþói8kþO¬ÕÚÉ,%#̉¦•µÖ$Éä5ûÀV¼´*+ð–èUŒ°–¡e­x)Yß° ·,6`|v;[½÷ œ™iT~sæk®e?º Žüa$Ó¨3Ç ×âbÿ>àuÝ@ŠÉ¡Ýûì|ŸŸ°,?ÉúäI×^®Ä++àÄŽªÝÓÁ;ãùعJžVKýÕ±ÌyoFîNºÎ¶üfmC™Æuœ|®PÂTüx ¼’³ðëÕÖNa#÷Æ‚Ü{Uo(‹£™ò$£Ð»2ãlëÍ™äá”)ÇPø¨å?þÒõ-ûažHœi˜Lù»Õè»ïºDyduU4;ªc"̃=dùÈZǹ>”¶î%‹gxÿ©Øá”~˜oé½ûãØ+·à±'gaÏÓPüóÆ„léƒ.A z¼5ŒÑ›ÆÇ'=a4ìÎÔm)ú×=’Q_ëŽa£mì›]ŠTÐ]†Ä ´žnÜ%IVŠàŽ¿5øLj&Ÿ+L}T—bwœ­(¢?£VAMÏü=äÊ®*ÝLq·SfÑèKjf -3©€Xµ$ øðÌ ¦Ç2¸’î¢b_`Hþ³w©mè¿ø?ú‰‡wGµ&8O] '‡Â6‹éT*¿ —hT€^H VΓ€Ð7c|¤¯HÝ…ÍÓéþ-=Ì(¿)ø—-Ÿà»}ÐX+LóhrIµ$ŠëBk^ž{zưydSûå&õÒ#‰»[p{n(&Fwß…ã©Aœu©„fÔ0kžÚâÏ^š¤ÚEÏÀ3E–ØçæAÈÃ=0i±8dNpï¯|°÷ÑRðréàx\-ÄÅÇÙ?K$ÐëÞlvÉÇf•¶<ù¡ÞÛÆ§Òìø¯Q¡©k¿³©ÿ0–_¿B–‘]­³÷Ÿý^OP¼ð0žT¤©|Óà÷À0ÛxTßä²fJë¡ñp0ÉšQŠŸ÷ñRî ðÖ«a¯ïM&ÓN·±³\¹Tî]§8…Þlf·ˆœ„Â~^àNpÑZžwØyòsÊÆJ,tæœýÒ=γ`ºÍù[{ž¨PÅ+ÜËiæ„w¥ jëVàŸÏ·X)ï§0Ä+FžÏ¡V/zñ×{Œ>º ×6Çó±IDe9¹·X‰çð~~úàL9[Ìž˜*CüjUè%ïïl‹±)Y>°É* ÜkoÇÓ•y¡3>Ó^C;ËOþ³?è¡*ž bòwÉcÈVC*fmËÄ ŠÃTã{Я¸üƒ½ˆà+Ü–lM+ï›âƵ>Ô”ŽQ1™X͹ {C™Îbfña3j­ÞЧvÜ­õI¨ƒy½>øõRÌwÐÅäM[ÐÖ@“Y<Ö«s™ùõ($t¾t0¾õ ˆÂuqš§øŽ©qª70ñ2Wnq”OÌ•møÌK¿tòôÝøéþ Ƨè&“}ú F‹æà´wp¥þ uÔ°ªVd%ÏØV‹« §ÁáÚð»<zÞß™ˆ06Ìr:l[ÍþÃÑ÷iãùãÜlÃiÔ’ˆeäNÚƒ²L¤|Æ»M›˜Bé†+ò‚ÊŽxDÝê¿Ãè—}Ì Œ?ìÎK†.²¾f*0 /íáüÄßíöÀ_6ÏzØ©Ã~Ê̇ˆÌ 4Y“C6â1~¼Ø>™<[i•–ŒÔ–Lß«KqÚâxò±š:ÓTyÀU8åLNd =NÔa ºù¯À®Ôgê\ä®üÌ®ð dõº0íÖn”9’€»ô*ØéšÓpFG-¨\au[[nØ‹éq«Kt°újï?ÿ‹MR„ éÊ(ø$œj¡ºt´ÛaBa¬Éb}ö‹ 5£L—JŠ¹ÝºŒæ d N#ËÓ&‘WT4õQqËöw¼fï+M£«N6` D“½j8¯ˆ$á"°y«?SÛeK®ÛFáqš¯µ|×Z€ÎY+K’zõ°ÅÇ ~UŒA4àî‚ìõ×Y¤+; [ÿóNk³ûØšÄ{øs­5Ð5¢“>ºÓÙ1œøþ*fa9!¥vütÆÃ3p§i2Ü_-Oí>„qŽ\?…LÔ ÛxTñfž$Q¬5§ÙtIÑ›ÿøïÖ¸+ìdElÙRÆ}x7,2V3K…~AhøQˆ¾ýÞ LUD%;ÃKˆÝõ÷!L¶¡îJþ¸Îÿ2G·ÑžÆ\Èd4XU’mµNr Û*™C“ØKüsöBsht‡ï8žSA Å5qÛL;úà˜9ìâ Œ$ÉÚÒ½Dlþ»Þ98çßáÝ—­kG‹×í¶‡ÑÒ¹`õ'øïxøg:c®y¦Beá$z'mŠNÍÇ_;Ó{ƒcÜϾñðl‰˜s23Ó²mº—bæû:x<¬G¤>ÜÇ®ßáÄâ­Ñ¿ÿMÚ³—ÛÃlt  úê4ä¬*»}ø¤êžˆïüÑo Ç5ý™ÁÝSÈþ­š4¾£Ž=øç´WÙSÓgÇØÔ›{™¡’KØœ7oM&}b @þWÛ© VYSèí0Àò޶í~åuì4þl&îÌOP51†ë†|Ôj«©]z—˲V¶WÝ –«ÒC3ZY ïSœáa-ˆ?Êp° Î…í|œÎü“tbOÖ&ITÜþÀ…k¤îO+ '(eǾ°§c²±õýM¸dˆÓc¤`ΊÓÐwú\je¸vwué1ùÿ½ÿçv¡ †\•gV]ó f§pêµfìÚV²)$ §Eôr~Øu3ÖÇ:ñr`ûüµ&=a5¡¿zŽb}°ÎlþÌfWE0Î'1á·ïqBœÒàÓíDJ;™C…Œ×I:¡4R€+§Mb°½ÓàeËmö´ð~æcÍ-0¿›Ž—ª:Q­ƒ‡ðx~Çoƒ×Áy{&Îz‘š| úˆ+í6I‡ƒ {`ÞzMúîžZð‚ éùï-,Hh¿9T}™­_vŠè…EQŠã&Ë£Àw¯0ù!KœfÃ÷êê`Fúqï—º‰}w¡§‰è?ük4¿€_޽aãåö@ñÅÅt|¿1Æ;*ÐÆò4ïþõ癑íräAR/Æ\áe÷Ü¡}h4+Œ]Rx ^ vÛc•èiÊKÏ:B¼ÝtYw O=ñűЙ4ÛòÆ…wãÎõ¿ RÚÂ’˜î Öä@’#†òIɰLK™™ûKMrØpèê<¤›wF›OæìöØ}°F´¸uv$Ø;f½.dKv?³|”¿¢‰¨4u“üÆ*íÆZg(/Ëæ” &²Ÿû:ä2s`R GÖŸ—®ù˜›: Ȭگÿâ¿õÇ ænà,àöl…+¾QlTó Ü6xÁÞÞÙä±—>]澑¨V¤óN´±¯{3Éñˆ}¬nP$©Áåƒ0Û•‡ötŸGßy¾ïýÈ9šY÷Wÿmб0Š)ùáÁ˜MàÊËǨ=Þ],©Ps)w½/‘¬l†ï/.1[5‚Ù͛¨‡B²n'5ú>f«å²_ó•èI·ÕÒãLD5öAá¡L|·üPa >Û‹*‚ôìêHäˆäbɇ¬÷#ºá¸.=xR$SØÚíZdy³%Z™ŠQõ9öôvŽ ¨5þç'‰·Ì©‹õ,Çë1¾ÎaOn„÷]]ðóÑzêPD}Ú9Ǟ̇èaeÎ߀X¼s!hÕÙß”Ý–Ž«?Wbɵ©8$¶ æÎÁ±äsL¬r*½cp»k'4š*CyßdbÌε7å©ÁYŸ,¾ÛÂû¸Šá¤zø–;§¶–mmÁ˜[q¨7—ÂÒ[·`cجæ}ŠbDŠª$Á˜xw§üBdÕ$`ÙwsXãp§®ég—ôÀiM¨*‹†E£Y¯.Ä?cæÙù»ÿàL“ ØgmRµyÈä‹§`H.ï_þ«¥çAJˆ hùŠ‘Øj[+‘^e)zJ@ä,Ã)릓>û úÙJ€ÎOðÇ3&'iæëIÔÂk&H>P#§ËDIû¥&Ø·X%^‹Ô×S©í-ty+ 0d^.xRAš¤K˜G‹×‘á¦öK™, ^E—>â%¥^\ϼÚzÄïúØï\Å|Ñ ã?QOÓ;ñxÜxZáDè’›PQÄDò ‚u!üiU!'nð’£ö‹øGt%aö!°«Éµž…Ÿ¯Xƒ^EruÇZõ½‚º¦Ó•Õâ`²Î˜Ù.ùgÿ|ƒDNðÒ°e© ýxo»~é_´ŽÚNNÉp©…ª:R¥²SèÎð/À[fê¦Â¯Ž~f¬/µ÷‘O[é¹5lè!=ò}ùü¢Õ„ë>:O`ùP9¬Ä–MÉ`2WL¢rA*ôþÝ£4ÏNr/•€ò¶ÕÜ♌ƒÈé¿X~ªž®Õ¦›EÉÛ_ìYTÙÿž¦‚·QjŸÀÔ½'‘Lp^eåo(C¯lH…·CóéÎ|šuý(†pÀ|•¨äõ˜½µ>î}¹AoºÌ!Þ>ïàAq 9æ <í³c¤ ̉¥ÀvFó=?I7›à¹ÎîZƒ?”ÀÒí'ª]פów‘00e}’^¡vÎ5Øz"Öl÷e?]g6}ǬyqÏ.SRTGáªÄûe1ëÙ¨ˆçˆÞðÈEöªgÒ$-šTöošÏ¤¿…s/V#ªm=°^¦ùt8ý…ÒX1×mâ܇¨Òú%©1Ós­Ž5]wSþÞ…¾¾Ë̉1xWìJž‹™É­,\\Á‘õ• ÷œ”Éúk„Äñ£E -ý©. š|ÈJ̦S[‘]‚U>€ÔÿÞYFç²&F´ñq_5¸’G!ÂÄ¢MІ|чN:Ž4àYS.º×Ü)¯…ôJÛÜìX+üC1Õ«€Qâ ežVË»† vþP]Rwh‚®<8 ·ëÊp Â’n2ýƒW\¯2ŸŠ^ "Çp´IÆ.A<”þÎ7ã½:d^X{.b.Eo;šæ=±¿5e¬™´ åN€èÃ:D«¦²a!5=1 3vÙ±o ¨VÕ ’yÀÓ7]`£Þ÷²/Ú­™|/ÑeƒÝênD{®‚ã–³ÌQóÌ2fm¬ôñ©µcüõ|'÷ÃÔBxœr•«¿Lzg‹ÀðMªíð råK9.YcíR4ñÛA8–oѶc!jú_gŠ-—Œ=®Ô|®2Û½& *wM%£Ž¯q¯Úóä0¸l“öá¬V3˜Áù€Qo8óÖkcïÐ3¶ïk}Ëe6¼Ö¢}ßT©ë›zøo´¾duËzlØÂÀ¨rJÛ “.©Ðq :¿ìά 5¯ ;Öå=G M¶’p®4ñ%ž\·w³p­uYsQ…|ýdÈ–èÞ„öŸÑÂ3ö¤& nˆ:ùI,aÞ™'øô¨Í: ”Q¨ÀEÍüDÛ'¾ÜˆäT•¢17±iÒÑèµÍ–‡Ì…Zc†˜¹MhåÊ[Œ*¿0I³9Éi}ú…ñ~‡m~µðQjyhŠNJ™¡;Ì3=zgÛv|#’GÄ Òó5{8u¸†m½£N|J`ë×9œ(Jï¿è㿛Л»$0wiMÝC|S ý¯8ÞYÀê‘*;@oÇ ÎÁ0^*©pžîIi{Ÿ—XiÚœ¯Ûˆ±5±8zB…žl*¿»¿åf#:víþ÷½ts5è*´€Ïú\œ³q”½aF4½°\å-#EŸ+Pì KolÉû70â«­ýU†Dߢة×Ç6{Dé# àùU„’–ï0âáoÈŸ?†|2YGè^YZ‰‹@BÊF¶ÞeRüۙ݃¸R÷aŸ=õ9$ao¥èÓ_Oqû£¤.÷ãåÆÐUËoÁ7­c´Bä6cX¸›ÔQ&÷4H¦H©±ñd§o¢Çô;ÁîàJÚÑc¡üy`ç’‹C%ï3C9VaærúvQÎWYÄ&V/ÀѤmÀ‘üˆ¿çãÜÙJw+•ßäÜçëŠË·”3ÆO©^TÔt `´Õo¶ü ŠŸÕùÍ(9QƒB¼káÝ _2ÄÑ‚€r˜|dmÅÑÊiì¥&YºÂ4D’îáÞC:,è‚Yy.¸WTƒ.” øõo=øZ¦0‡×®òÊÂÜü·øg« ¸J)ãâñ<,÷IgLžmA_‰zÖá{:îÖ‹y£#p³6Œ‹¶P«¹06§ä¦Ýd„WÀ•Þ‹rÓÙSk]!¥×1èÛzì™a™PŽ}âF:´ãÔ,± #!¶à|9ǧuÂÝmnÚ/Ebœ²M÷ÕÑŒ4Cïmdî\B«+”óê1X V£€J!3ë}™Ü¾ ˆÃ¿U$¯à1ìWÛsÉá?Ì„]ì7®>n÷}·cNÒ­ÏðOQýû ºÔŸ`D´Ä4Ì"%>·pX¿,ßsÀ£O8B± ”ðÔÜc÷À®¶U3ìvîć†jp\°ļ¾rWª„±%¼ø¡¡¥3n`ê«ïXukÝÐ2æ=x„ƒ×·2æöCªÔ|šr•áó^ƒgÿ`jÖ|B6]‹2"ý S«J§í»ÁºXd7/ðƒ›äˆíe´`Gàèh(\ ¡¨¨œÇŠÇ€G5÷˜}3çø5ÖBû²CÊGš< F’óhC¯c1ç)7×Gž*iòÒoW§³/AÀUHg¶ž7c38«ºšÙ²+eb³™wo³ú‹êÐæ6[ª)AÖWüáÚÆ•qâNEK¿Kàï² ìÅæã;»XæùÕuÔòô1X>¿ƒyµó(>79Hnú)á*™ |´ÕdZÏP «"*K ÜÀÊsó@L•Ÿ>]ã‡äþ¢ýÍq(RôÅý²¥xâД¶Œco4„èèYÅ^zI¼Lú~Mzzj)Šnîd|ƒ[6LÜ^šö\Œââ#5H›ª1CÑRJUŸ+\üï·òº0ÑV“¼û¾„ˆd¸ãáM®”ë"ŒŸ'}cVÕmA{¼ŠgN´0¹³¦PË®% µñ*ˆj‡wÝX-§AãŻΓl»-D´ÿ„3+.(€ÏÓæÎ',^eâyîÞ4ÃGIÁÌâÍ*´2'&³Zî‚ÝO|ðKzo™Ö]©ñômp%[•F6ó£Ý¤Ò<žXôD“éeà^Î3ÈõUdW¢òçcسu瘺&S¢­>,¼”ÁþA6è¾ddùªPÇ—Eý便—ÆÊ5ÿÁ·§’qa_8c{Å†ÞˆŠÆ©\Iòþ}:ó«u3LÓ1€ÑÃåxr"tìßB'ýz…,Ö0¶‡±Ä®ýVºr³×vBß•:H/?@ÔËšá·{ò}‡É• ëqg&§³Â'¸¥g[P'·F;åill<<ÛVhWž8ÄM}J!Rp 6)£ÿ ’–ýŒyè‘È>a¤éÚ7öçÁuYÒ¸õ1ˆü]C¯Ž:“ƒç"èþ'X‹Ç(޵ÉÅàìL;;Zwp5 ûñ⯡Íäå Qª˜VVê0©V‚™ì]ÚqΰÔHŠ{k“Äþ ¸¤(½‡R°*K Ç/'€Táblº^™™; _"†ìngæI!:;±´L•Þ:‘È8';áÃåW˜‡×¸{_ç³ï¿w2²ÇX÷µÊи טÜ~“GpÔ!‚wé¡íïÙ uAL….‚ù8—ù=û5zƒóCéÎU D`j"^Ú3Å|Êô&ægLgØ6°§€Ý.K‡š‘åq‡µoxZu)£o«C#¶OøþÇ Z½©k·U8\Ógù¿å1‡‰)]î?êLå@ÆŸ_^ÈÈo¹ÄÖfw1ýë—’ûÙ_íÆÜÀMjNXûڄΚßÊ^^l†¢§ò@|°Ž„DNøå\ºû ½Ög`½‘Ž6^$‡§ªÓ.þeØàL¶÷Ðr· dZÙðø|ž®uçZä\ÍÖ­E®Û~R÷A›r6˜Ð'¯3Koy#ÿ“E(!êD.¯å%a›ø`ü÷*fPÃ…‘Û 2" T/ÿ® » Óö'ApòEÆÐiž Ý‚“#w8äÆêÒcÓÇP=h ›tæ“Sw€DÏbhJž‚!M2ÌMCUö{õ(ì¼p_wgœK‘œ¾Lv—{ Žõ·0BNúð~L›ìyVuówâŠlKNÂv?HucpKŸ{¹p ¶ñ;ÕNç-Ƈª¢dç<ˆyegký0ªñ5Sª„n^"4)…Kw±—Ï$³¿fæ`¯¥3[‘eLÞ·e”sx0eR錸ˆOÎ6²œüt;ÁJ ´s7=Ç"QG˜û<»²/⌞ÇXšäˆ¯ÖkQîñÞaÇŽ ¹³W줦«øYµûì7[ú]¸…¬ôÅ%SÖ`âS?ÜQýgâs&²íg/´EâØ‹[P!ê N†¬ Qþ qÏ™Xª8<Ú΋¾Q½p—_6/Ë1:v@ƒ”¥¸cýºh`ú¦à·ûàU@)ÌÐ€Æ u6€Ó€b™ãøà˜ („+q÷DÁ ËlúÅ|*Xö¯¥s£-pvºÉøö bý´¨©xã9ß|ÜâÈ—S°êñ5Ð?è¿x@èÅ$òö¡+”öE౯…ì2ž"H¸ATò.£Í{'¾rŒ;+¦§?Ç+mØtjò.]Žûƽ&âA”Æù7j—ÉãNA8yJxÚ¥P=•Ÿþ¨Q&ZqgÈ Ã›uŒó'[‡\(Mü¬&ß^M¡£[{8·ô¨Ïx StÁ-…¬ÿ$²à©%‰™–ÎÈ\jÑdðs,þ¼ôKšñ¤Œ }©Á¢öá•TyF-t§l¦<¦‚dŠcˆßÅ£bp¤¸U¤ß1¹Yõ°Ô‘ä( ÐÝgãéœMh´ãyй¦yN!¡sPâ®=cãOÊœ7Òin† =âÈÊ3+ËÒÉ+.³Oì“`SªAÈ”BýŠvîvóZöj‰..sþÄžþxl×Ï–¼Ë UcÎ6¾XIu§r õ–ÂOiêž×ÆLáµcº«Z±³H˜~41™Óm¸dùLRqR³Œ`Ä"ãFÙÖ³Iëë)ijÈmRñ™±0=»«‘y\Ð2_ÛŠ‡ú¡ý½vvÉyczóÔkv V˜™Ñ¼u ŸÜÄ Ÿ5‘ß±yûk 52Å閷oÏ(ûJá2 ë¯â¾½¢;Êüq[…+öøã÷»Ø#âGÚU×2n‘ÊOî´JÀØÈ%”·Ó¬€s{˜5žÓÁ> µ±g(ª±ï5kÑo÷ÐØÓ"ÄS¹’ë[3.:ße„× éÌ»l\èRήwõàw^V,¨Avv+NúdŒ»;Ná Œƒ’%l>mAÄÏŸceoc„s³–»–Û¹¤ÄÏ]g÷W©áî‚öÀ¿ìö è”§å­‘ÀYs…Y'L×HÉÁM‡¯Ð¼FðѨ})ø<Ù•6¦Ô]@o¯ÑàZoAý0ÊÉѺÅpW×±}Er\!³œmvn}Ñ¥[”ŒIm˜e•õäÐ(øÄ¨ûî…kÃáfš&qúV³îaâì`RœÎC}³òaT¡AImYA«ÎAñÏÇðeÕòeµsÕÉ]Àb7oç¼Ó"ºîi6l^¦J{ó¦~x¬ÏhfΕœ&k»'´ÖSöJévâ{x¿ÜÃC•«èÍeph½ãÎ΄SëBéñsj4;mnF”rîuÄk£éÎxORò]JNOƒcÅÐúInUU€ â+Äi£GrüÁªf€µ?MÚÖàÏêdVø  ®{ûrl¹ßÕH”E6ü9fCòÜF¹»/T¦)ôú¤}°ÕÔ¦Õï†æÑ¨¸I€.Ѻ½¬ýãòø[ÇÝ7*‘‘ó@õ…=I笢×n½„w“w3¼ Iä{;ÆÂWøð.¥÷H“ÛtÌ^ºøIÈ5GÆ·Í6¨z–Vžd|{7BQM=ÌÈnÁ¨…­(èmD|^ìÁeVyðe¨Œùêg@»©Ó»î"Ôë†4»òQ¿.-8Ööi2~éO¸T6€bCŽGâTžq ÕÌk—@2Ý™¸Wóg\F7Í8e'“œëæäià,& ìîøÄ8ÝúÂy?Ò¢f h¾ÿ ,Ê &Ñ*ñ8}‡´ÆRÈ–°¹ôÑÔUx½3‚¸õ‰‘+‘ñµ_dÒ øPû!eü¨m²‹¸#JM/°¯²Qÿ >œ^‘LŽÿdaÒ)[‡ùŠOñd ùî«‚eûÛ¨?–ô-¥OúNRYVÔVM–ÙP ™ w²ßß($,×ø(Xxº ·òv1¡ûË™ñ:%êRéÝù/Pìõ vK©­}¸œª®{nœ ø:÷;>Zù»Ÿ…Q¹ºOð×’ŸÙ/_Ë È#ù…êd÷~^nJ+%ýáõ yjÑŠ¼ÚªKgîœF¾‹¤“%ÈǸ“Ô[Å—-˜ÐŠ«+Øuy"vEA¼>¼“!FfyE²81rèBœÐêøâÏfãêœìÕ*)’²TæØÇH(YÐ\܈‡Ó±léul¦fða¡u¹å†BÞ!Äæ–=Í „Ÿ³°úõaΖ}jôGî”–‘%»{¶³ç+P0Wxáœo<޶Qa¡U+x6ébÁ¾ÇpÜð ,ŒI‚»#üÔ¥bIÉ ,=L}4[ñDÔ [çø‘5šþÄsL!Òbš 9@¦µ.ùhˆûeÏã‹î4øvw+Žò R)M_\­4Ú>”q-læÄXÎÇÙDä~,ÚÉ<†ÏŸÓ@a£!L;ØÌŽmi€Ö,l¾ÞþÜåmb:êÆ+½·Î@ÒnÆÝŨZø)ìYº€$s—Ѫiì…‘ÆKç®ÖÙëð'¸d2á}oC6âd%ù…þkYÍ´¹ô†Úaæþ¥(ø=})€®ZR¡ˆÍêWàפp2/Id bA„ùÃà“£ÌzÑ$¶ïø*•"N&e²s%à·-]`ëIÁß)ë§&`Jl=+º%95§Qô<É| ŠŒg wÎ\æ×óX3í*z…Q‡ÛŰj{Q{Õ‰­í÷Ø5?÷È …Ð(@¦¬€][®Ð[ãpOý}¦/Ð_xˆüJƒ—î§¡¥†T€¶Ç0ò/Z¹¾Ä,Š}ëe`£|³÷Õ#Ø0§ßg6 ³%é(¬ÁlîF?š…*íØIwΈ÷Jr¿ øèù‚tÎS#ŒËž //å3¤J·¶e«þ¾RK{_”;Ê¢è—høò.ìÖ:§%ï1‰O®p’oCÜ(02#2—¾É‚4©è1sý¸)yñ³Bý>Bˆ>yuçS ¡N-´@{—)34à 6ôÊSábq"¹(uJZµÖ“Qã¥æ(Ñ1QxÊê ·BëÛ„¨ûôßT Ê‚vŒ÷6ìäƒÇ?‡Á²®b½Ã~hK£•ª°eesÝA Œ6XÐk—d蔊X{݉3ø¨åWl†%`E¾<Ûonà HkdŽLj†¼\>’îȬ·#W'a‘ÐO0_m§Ó ¼Ü˜–o×FÑEäÝôµ¤öÈ_p^:™h†: |YFT¨7?g95 ˆäú—xš¯ŽóÆ`]³=9õ4»DîâÑû¨ÿÈ…‰j ÃjÇtX}œ›øÂ€4¾ˆÅGKßr„öÈӷеv!ù=® L‚òŠX~ó1;çû2jø¼4¥z ºu,_¾…‡â‰ôx).Má£)IKHŽÙë'¶™«n¢íöËÈ ³çx'1œž­©Ê„ºÛѼæFøTI¦.dö¼ß ~¨‘Å'lÈ[Ý?ȹeDÌ®F‘.kUR•ñ-ɸéX ¬PéÀC9¢w9 .‚•G Ú<ƒ½wê3ÓÎ{–6äg*ÒqËædæÐ½w¸ÅR”õÈxŽÖý«©…}«äNÇçÊк¾[8 þ¯_þÒvoÇlü½Q>¹I ûÇ X>GEÿÂi¡ ŽŽÄÉô/Õ¨‘‡ ¬e ;É_MV%JÒBºømÜG"…YóàYT“W†è=äÕý‰äÃ5ˆ¨`¿:°ü.1P#ŒèàsäɺöT-½Ž{ò`ù®ß(ôd*q­Õ ¢+ÿ@ê0èZÀgQ<œ6£™ ¾ˆÏdr¶#ƒîOÓíߺXã_û+IæÛ|0‹×ÃOQ¥ÇÀ8lcÍd7ã…'ƒ Üé%0¸ê>š?ý á“èB›[8ƒyl—…GÌga£ÉFèýʃºËBD° îó"(r‘Ãty±Æ³ kÃe˜y³í;`•ÙtXÜÆ±‘|üRÌï7¤éÃav_Tµ ¤YÛêØÐ1ÌS¢ÎŸî78ƒ;íãLâ+²§ŒÕbSñJt8crWmcÄcÎñÐ!PΩaw¿@·{|yè7ÞÓe$.»ÁEÃ%äN“<žKe¦GøašE4î{{üФ9µ?"سá­påö’ösÌ´ƒÑì‘é.½ E^zÃj?ðõ¯äÜÌZ†ò³]hX•Ž}=„Y!k¹8ñËæÐÃq(^Q mWIñ“éPõOìïÑìØ´/¤Ç°UáO³¬Hƒ !ò4ºY„^ÚL¢Š¹`[ßËZ:Â9aê žé ¾]ˆØ³‰Ì¼:yÚq‰í¿[Í*¯ï¾ëg`ÚI>ê:fz3¸Må-˜6{„}{]RAÉŽÃøòË4°Ê¿‹E‡ìÉHEUÿÎà>-ò"{!5û8 Â!å ù ¿å_€l7I*¶‚ü¤žàŸàl|áLñÕ‰›°CHþ¬®»‘Íó‚ó+·ŠÍ.?Ë(Äo‚55Â$Tº•¼—p}Òÿ{LkÌäQmbeËÁ&Ù7ËxQk¯?VΞå‚ ÒÒ4§ò¦ïèÄõw’Xf±ÜZчyß3zSÈ‹O´±LêD`îÇuìnßRx®÷êuÏ3l¸ü=¢ˆ­ÌF0œD$9Aô÷÷(é3©{H‘v=L)ºÒ´)óí Î:È:WîÅüÅYÜaÆ C¼µØß3\`eå0kqø"Zå¥ÀÊíšÜ^¯aÕ=œ‘õÛcK9á³çqv¾yŒb'ÒÙ³Óø´eÍp¡ç­Þ_ÅÄ} Q/Ï’¬dCR5Û‰ì´.­x ‚„䤷ў©,èú]AÏïŸqK÷V"¼ì©k„ÝglPér.®¨¦0ÚË~܇麤éû6xîQ‹ Ñ$a¼[Èä×?q§á Âi–¢Â ÑòÎ5´¹6ŸyBáò»' Ÿ«Äù¯ãL9¢Fâv~@SÁã(yŽêþÆœtw˜ýÔ Ü¯ãûÌí0Õú#x^ NÝFŒß:2Û÷‘ÞÚæt|³olWhPS- ò‹wnlÇÛ‘áò3Sâ©aF..Ô&›ÚoÓyʳH‚üWx&€§0‚8$æu2 ‡çœ³_Huå;`Ó9*<œ„ëzBüÈuR»ˆz?a[¯žØl1Ÿ Æû‰ítMòc¶7%“ÉýÇÙ$âz1¶©E¢ŠÔ"ܱ`1Õ¸¹œˆkÝe"l§õ¯ '@ÆÎ.Ç9—.áôÞZEN?^Gé·×äˆF|‰3 éï­ñX[½ÊElÈÆÝÓè¬ïY™‹€i±èM)ÚV¦nñ¿pñkÒtʈˆ'¨CöÂøÙ… ß‡ÿí™ËdRwÅ„TìzñãKp[¦zqgYéíBìT-‰Ñê&K—,ã•&oߺRç§AL̲Vì9Æ }3ݨåÖ<¬ü ¥fäÇÂó¼N‡-ÊȆ=´s|7ÝÀZ½×‚Ëiá%VŸäkÏ0»Àù~6^~>e§`ßà>d"™gÏú7ºU ÀéÊ0óêCã¥hDmÞh¢ÛÏÙ$#ÔšÈJbíY–ýð†!kü«¡¡W˜F¿ÍaW‚uÏî@ÿê댆€ h¼ÿ‚5·Ñ ‰„7dÊ‘bCg*ô:ú&;ÁŒÔnXÑ>•U¿Ã¢¥?©Rµ¤‰cRíÉÞŒÙX´ã$³`³<ád%“ŽFYš0il0œL;›GÙ;Gªð¶],æ Û ”¨ž MqÚÅLŸü ó ‰é“DæIF>³âÇfÆ4ù< ÷œX+m²; ×Ôj êÁ\ê(vNÎ ¡Oæ2úíq9·<ÆN³W#®³g­\ÈÛ4ðM_ ¸Ùöú×LfÜnG’ŒO–äMÂwЃÓTmÏ+ôhúó[wP¦¨ÏG—Á«ŸŸab?xf~]œ’‰i‘Æä[q#^¶ƒi/Gq<ç;HÜþ<ñ‚´¬ý-ˆø.¹»È»b4’'3žbãVyàYŒL Wðøâ²• (³B¢d°É¡?~ÑgƒÏ‘-ûô Tõ"´o´$ò x鿏\àÝ3;kï²Úqša:Ì9ƒÊÇÔñ®ß>âuÛ?žL–˜/®çàÝÈib( ¦íŸ˜žÔ^L{> 'Öåœò3¡å+Õ襻±ø÷°'펞LÜø”ðmÇ6¨Ÿ)HËùëR2H€¿,yW$@^ %ç6”ÃûKŠôøÆHˆxSÁéÊ+Aö{¸és‰¾OÍ"Õ}¿á⪻o|}pì:¼ÙïôÁøT±ðPe.}­„ÝMô£ÈAŽñ«lêž@RB¼T8`©yŒÜHFùé²0-΃"×£¯û>Fœ Ï%²A ¼¾ÂÔ2%ö©ñûˆjm²Ðß°–nË=C~fÔã,K?ôI¤ò—ú/.LXBªOÃX€8õÐsLM´ b_N¢Ò %ªæ?æ­ú8mP€6ýÕ!Ÿ•ïpz̈ìùhà1Í{A²üR.z•C"¦Ž±þˆi‚9ý°Ù›³pç¤.·mÛ=øúL¾XÓ¾ötEmc}ês€3¡[?wàz{yvvèp³€ì9ó!|ßFíÆOLI€SËo±ºšpJ´ÖU¢9÷Vnׄ?l¹~áÈ®¨@éÁXh02$»#ƒÙ†˜hHtÙDÜaš5§˜ >Ö·¹Å—Mç{5;[î>ˆŽ‘C›s·XVæé,ÒXùö:æPŨ\,]½”&xú7e)r©é|À^(}¦NÚ¶9Ó9Œ+•h±§J ò8–£L^ÍL võøäæL2Çù=kT­É&GÞg›xµ!0E%Û'A·YžŽ<þ½ñضZƒ8S2t’öÞøÈÜð¿…s¤åÉæŸBXR“ˆ6ø‰U„ü2 ìH8:©Ô²‘òw°¦íC­P²ßŽF8Ÿa·Wdg³ÝóW—ø­&&²ó™HË;<çËê1ûœóCð&‘–óáwîJöëÆëXoÞ8b\«:‘4æ´®¥ÁŠ¿ñÌÏEdñy zïé1êÜõ]ï¡}·)~ªëô&Õé&t²%÷cjq&|ŠR£—Ñà—+K­+ýéZ ²óÜ\"ûˆYÕ©CyM%è¼}|kœO þì!Å×±SVÓ i~æ~òÀ=Ú\ràñõR6CµšÅqD׬'Ïj—Ѓª}Ì69zKo˜,œîGyŒ5Hý¾÷烺ìÞîâPW%L‡=ÂÆÉ|Ä]ødÂbB¿Ð=¾‡Ù%»W²7ƒ¨iå"ÎÉ©‡™ îtzTú ˆî€ ÍÚôÙ§.¦ÝÄZw™àó²U\+Egb6å™õæ#z”äЮ—®´+z-é¶v asiTl›QÕTë³Æ_æîÅ{3$iF 2M•éÐÈ=Ð>Q‚çÖÔ€~?‰ÈŸ ËÝùÑ{ã1VÿìRlé“íÈŒ;iµäÈ’¥­lTa<3qÕŠK¤ûjm@ÕU‚]sâ#îo{ÆÔ-ÝH>ë{ÐŽŠ6fût ßÿLhÇnEzEClÒÃv\<ö !^m&›XÍE3 Øš¼ìÔ…%˜L󨄬,P»àŽûNv²¿ßçÁë·ìà.îöå—y«eÄ`ƒ usǽ(Mit³‚Ùo =ló{ØŽHÆ Sìu¤)îËÀ¡- «?ò,5š7+ŽJMV"6Õ2èͦ½§ñ2íušÄ$ìÊù—0oBw᲋ü¤nú:pÑ™Çèµ nâèâ žÏËnxªA6¨,ï&°´]‡zŸ˜ðû®܇'|èY£YäJëðû5•jU"¶“”5ãĹŒ_u#jÙ&±Ó·è£g™›2O¯ (0,.¥Îxˆ4{C~LV`VæCãƒí¤ßc”ÝÛ$HM +ÑÔ݉Š)H ÐKL7Lƒ)n^ìöó–ô9ßF]1ŒÑ:t›©å«'£«ŽÑ+nUX±I—.RèCÿ°-8kp Ùµœö«Ž%®t¶­:õ2à'5å@^»õÀüäÛÐpæäåI“ˆß,$rqšìqšÙŠ·}s ­}µ8àBÂjåÁþ½-U|OQ•lÄ»[Äižn4XÒ-/á†Í¹tŽg„¶7 »L#„ ̇ +Þ`#û²vpÿëy–ÏŽg)M¢â³ ™[Ú±¸Yvé›éOl D¯•rä½Uør%ÒÊ¡KS‹ì£=,§ã1ðeËÀŸmœ¨÷wáe¡+ûFœ2ÂôÛçµ°¾Ç $L8ôç¾À€$Ê”s'ñÃÛÀ×P²<C¶¿ÀJ£bZ.(OoZHÁRCQH°V¢¯ÖïBš+Àá8n¸ÅTnܳËìV1"?e¤mé”ÿöO&QÆ{jJ°hi‡ì ÃÅÛ«àyâfx¥[Á•ÔofÖ.ô¡oû G-HO ÅÏ/Y®G©3[Ï ¨0 ð¦"|ßñáw!}°?X _m¹ ´çcùý#Ô&écbAM¶^s%É®€Zà`è*:_âãÎÇ…Lë!<ó}+ÍVœD<±CýF^jî5ƒUúõ¬×¿ =»ûÁ2LJî[r‘ãB¸<…—†½ÕdwÊÊÒ¯§:ÀÌ0”X Z³õ"# üX…hÕ?cfY¹“¿‘ï '|.UÑ‚_ï£ý‹c¬ß¶åø§INíÚ b¬0í³š._Î]Ë…ÕU"´¥úãß1ÇŽWsûh såL8èy©ÁðÞ†JØ -o¢™‹-SÈ+õNÜÚŸÏ^ Ø’ÑC?XÔ“ÿ¯'Dz¶ÁÃà{ø–ÍÖ3œïWwÁ•ðmsÓß4Q¤²²©ØP£Dí­¦Pþq?ø¾^ Ù-(d;Îö¤ “Å»eéþæ ˜¬r*æhɃÁ£ðjk?àÈOnêP\t;²¨«ÛUÏdd–bMbÌV®g/ŸKÔ ðÊØbÑ´•ŽU`þY5fÎӨ󥃻Jd×ÿI«¥ ¶awáü™ræÐ‰"ÆæíV¦þD)<½µöB´ñÛxY¦I=ÎnÅI)Yp÷õ¨¼…WµÀâ½oPk…<|ßpfß…럂WÇNüs'Jh7{\¦š1Mà4ß ˆ”SÆ“«Z™£ÌR¼¶*”X÷‹Š¨0ûjûL'—Æ_1ñâáø¹‘…»+?A@·,‘Z%DNä‰ÂŽ2BoåE÷»³àx€9s®HÜÛ!‰¯_Å ÿZnmY=7ë(zâ© öþˆYæ-V|¼Äíž"I¹Ç.™z ö^×Ä?bšÐú+ÖÜþƒBorq~@£Ûñ˜=©ö¼Öï·|ó¯ƒÅ?mèF‡d˜±Û”î¹½°ÿ …–uö•n?Ûi3ƒÔÖÑ}a:m²-’m?J~ç©°÷IkºÑ™lŠ7†Üý;‰ð T‘\‡ÂOq·y)»úÒkh €ËGòPrú جœÁº/&çöéÓ¦/á«ÀLæ¾\øÆ›;,A‡’¹xðCb*å`qáa’ÓmKeM¿}gá÷S?Þ݉ŠÁ0³V¤†]µ~óâìKPÈKƒ¶h´ˆÎ€=NbJsYW)fäcžS‡b“8ðÉz³â6Çe «U°‹$['£êêOÜ{¥MÌþ-KH·Æ$0ÍS&ºOÏbܵ³à5° ;@ù&?Íð‘$‹ÍÊ`³>|ª fq˜ç"‹!ùÖkXøvÞ1bÖ×÷ƒŒq0»nP‚väà¦aÊlÞl„ñ™÷™“HI¤ŽÉÖxUS‡_®ÅkKˆßÊRP·— …İÛ]˜Š„|e2¶5~Ý¢Ù|ž$=– ë–²ž ¥ ïñòèŽK’1pÝW4ù[LJ<Éèÿz°¡Éƒ‡:Ï´‡ëÇtØ¿£ðƒt5ú ú3‹doƒé¾x|–èªïÐÌü VÝvdÝ·YŒ;ÝBˆèùóÐçå“àæõz&S2™Ñâû £"´@á2+Ÿ³Ž«Èø€ z$ó5sØÞÍÀã.)°Öæ9*½½ &z>êÈtúyõ%¦Ób&”š™P«© _ù‡Ódw‘Ãì/¨wòób¨•U7„Õ­‡Û²k_ õâ̹iBDDL‚^»ƒ‘E›p?Ÿ$öìá°Ï:0¹JÞÔüI;s~æ&z´ÙŠì8püÌÍ èÍ ëóW„V~úŽ>"d0!n‚O'Áw—¶ðÒ<³–“õá*ËèÓüŒÆaòj4~¡ Ηor4ª.€ÿ†¥d×§œO¢˜ñJæYÒEãòìÓ²‹ ÜßÇ–´W0kÍ#Ùó•T‡P¶þ\TÍdú“5-C¯å°S\«§̶ž6CËg¸Ílmø,A‚†y¨Þó)L×e*ê{eÛ pé­ç Z%J™)v´l™$>‰¡¥Ë磹à²çË/¦'bBG‰°6ì€Ù]® ”“Dt8Ñ“_*?ž‡wï&4Z?Ã×F6Í–`–Ý=‰iZ &ÿšSýv6<ùzÍÐ _×K‘çÝ_ß°*öó¯Kpò¾>q[%‚o|µA&¤®Žóéö1v@ù«]u DZ1Cîß0'0„8úè-óœù™Þ‚Õ~à n‡ÊnGqRìe0ðõÂM–§píÓhœ?ÿ)ì; ÑgØmK_²Ñ+à—ëfõ*M’m¢ûßI±úaE‡¶n@¿ÜðüqÏD$õŸÖÎ B¡Ä$rÛµú¤Aº½4É::;ƒP­·Õ¿ÂìŽÝvU•<‘;Ë(}ê‡_k@0q+‹1`„œwÐ…k!i¿-s|µ1 ¿j•N¨c÷ìXî@2².3]päëY´¿³vŠà2×"¼UÜÊ<=º†Ø‰I0¥ ¦ÑS>©ðáàN´iRÅÈZš30 §ê Âb ;ªÿr).z/zO…ÉÓ{=P=ªK×;Ÿ$=‹^ãüo DPV‡1{f Kò×á!w5¢_/N^h@Hi5º<ƒ‡6§¢`ã6°Ý.é¶,ó"$}‚ÞGý¸Où wῊK‹¢¯gòC­ƒm{í…^›nÁ‘õépË­ E–ßcó€MW))=%EnÆòW¶ÐÖô‘ûÞ(ºöHq1«¤UW–I2M¦4ÿ…­v¦·N¯!½¦¢äWÝtÔÝV¦²ièvRW榛äÖÀ¯Õ•°nB{¾;³€º„•\\¹=Bë9OVñc¬Ž;¹(}’yȹaÎ7fŇkì Û¦íòuìl— Aî¢ôS…<úý,v¨-7¡!ð÷JØ:)ŸÝëÁl azzF éö˜¦òõ®ñ1ò?³ g~AÏ}˜ÔTË´,¸Å.”¯bþÿÊîšjŽÕ<,¦ôÜe#—Õb|º1r$æb™‡=Ý9«œ©}!É~¼Íèm@¾g]\Π&œ0¦q33±fö¶ö°^‰(ðK_M7¹Ñêƒ8’C2zĺ´ bR»@Xú9^èåWè#Ÿc Ïä[4Ý£R5Æ‘„FÃNåÍx¶O)|ªi«O#”àcr­3ˆ‹zµŸ ö‘u¯NAw’κ?6$qšyNt§ÌÐnŽƒ„GÚ¸âùzl'‹9ú,ådØ$œÎR!úÌÏŒ–·fr«®KÐ/ÚoðÌ×C4¸ô7lŠÞˆ N­$µVéÔX®l~¦ 8S‰Ù 4Ùx3}øý市à‚:èx¼‰›a»›œV™I‡w؃Ç33öT4‹.>›Œa‹c9›õ ðù§ í^F2Ö8¢åXAH±uc]ó3\”A~c˜ŠH2,`ö?Üqès-y*gÎj¯fð­N¨Ñ’¶ëŽ*}¯~&û™°ýº*Íä¤5Šô@ù‹U`½Ö•/»ø5$ëö¡ÆÒ(:ý¼]½f™Äãä.` n”ƒð %hÞ¿t´êñœ— çÛ4‘ü^:…é¿"¯7\Ä\I°y—›lb±ýXsÄ>õsSUsew@ëG¶ÚÅsOC¥ätðZ‚msÆÒêu|û[ˆŠ,›J7Ø›²Ü²‰,m¦¥-µ¸"»U4óüuYÛÕ£±ÄŸ*MÂuwÀ®›e%®çÞÓf5¨{{?^hkV.Úe»_ü ç²í0m 4>°r­Çq/îh±¼é%\¦ìMª°Ëš¿{ÐÞÁzçïP‰¦“Ö2±ÌÍœó,i&u°Ç·†M_~®œ\sèzl ¡ƒܬìèöµ ò§×Ãl;kørø=¨¦ÉPòÉÖNd7oí„ÂM'±p½SÚF©aÆ^ºò*ÀÁ+‹¨Æ‰jh°»æMëi¨Íiô'Ö¦w›ÿ¼o&ÞˆkæN=·£ÂÝ:lè³4#*›qpHe´o9nô–=êeo:$™ ÷ßclÂYîË c<ϳ>«­hã! ¡;Àà€‹Ï-‚‡ßf§Áæ;á°Ñë*´d”2•%¹Ôl§«L\Â,Ý?òw{Lfë-xâ‘gLó^¼ÚòS×BDÁ$úkf.(=œÃ šgRÅÓ) ¸ò'ˆÄhòJg™’q¡@}êÞ~’Â^ÚÅÉsùàWÿ‚*m™x R¹RV‹5¢PÃÎ{†[‡l†k/,¾v”šÌU  +æ±.ã ª“1@rŸÇÄ-ÕhKïŸfTÅN7ýÄdûVøãÿ>êøñ˜­éWþpA&Þ®TÀHH “žÒgáŒ+ºˆéÊà¡ó!Ç‚:ÊUâÞL¹)ÇfªßáÌÿqß—kÊ ,yÏ–×É~ÉQü}›æEƒÐ 6­TÙ.Ú8…)¹‘öÐ%ÝM>6Ÿ# Q©:”=z¥‰Û[]h[ðkîT»)þ·:U©ûš ˜ÜªLÿæÊѱ¹¿Àðiqî[Už_!Ïlk}“Irï5ã[ŸhH8Ì6”ÍeÅö®¬øpõw-'*³œ%©öÐîuê”e­aâë‹Ø`¡/5^j‰WW­¥¤¾™;w$“XH"s×&öY3Œò{ÅY•l—"™@+?';cBÁ£ë1§­ˆ]>Åd²Z7¾^’…3oMf;–!·t¤\å&Z"kAþÜIÞÙuJ°á©;}Ѿo9°Œf®ª‚¾€;PQ5‹,ÆóR‰›» нWa½›è鳡tÙš~v†üÜèL®ø"öÛÈ¢¿Ïü›ÒÎ’‰ð¿¿úÂ]\3•êêkò»Ÿ KR;ýsÈa[ޝ³Œ‘p _Ýí 3Jƒ;‡dØó…¼ E5Í·tàÖ^.1Í…î“À§Ý¨I®põa+0·» 4WOõ'-0iUûuN>Ê›ÕY RTIÍ*g3ÊÙò†xæ;Y„e­ïç+œêâšr\Éñµ‘Üœ¨§àxz;VœšNƒ{#ÀîÄwR§T žÑìã7)Ê|-Y§fÊè3#üýC€ÞÊ©Ã_/Ø ï›6ã·Iå#T …°ºÁ©<õq£|+¯÷/ Y¹›~|*ÊJ²¹0‰϶Í݇_mÜØQ¶tE+©nöÙÙìXÜ^Âæ¬ ß„dŸÖá:ûÙÖgà~A æwj“ÿqœŠ±+Ë·bú: ìg½•o2eÍ šL¿ñ ,ÈyÊ?v$'ûoa6k`þC”¼ý‰|\žŽ¾ /ò]ƒyŽ »µ-]¿®‘;•-—sí¡Áý˜}Á C±tI@XÖ¯…ŽdÒÑ ìú¸<5^úg@áÔnÜ×Jv5'ÂÝÓ(¤?›.¸¾Nn‘aãüŸ‘ЗF—ï=TÖáû{/¸Œü:ð^à‰ŸÅøòÁ?þ*$Nºå¡fL*1”Ûv¼šÜ‰ òmº4¦y sÎòeÎÑÜÊ;º})è¹Cïv%Fª·€¶w3$}ƒ•#£¼ÇÍ ÇÆD±ßåœW™¾Ä3“¼±úk,¼éÁæm™k;ËÉí¥gˆe*¥ÅG®‘ÆWëPôm »Êäž?çeí@ó"ˆÖŽ|â6² §8¯‡“Ò‡ñ~Ø Î7ô<¾y)Ê-–Ѧ››¼ÑoF!;;a{f+J[–Xc¯zÏ'c/Y( ·¥O°ïbØÐ7`«ŽðÞïšÎnîB®úFíÿú ïj¬¢Ca·pÿQa¦¯,MÓÆ`ÁCZ,èïDBæ°XÛÑúò+ØÛ“AóÎCßjÔP9Ï4ol†½ÿâ¨ZÔtÛ´€j°~þݪý$|Ä UÍ:°+ù- ª2)à£ßÃã0öx6z•e`y‹™¨õ FZ,¨ñ 2³|´Ž3¾bñz6ìÀ¹°òß|.öAþޏ¿R»áv),·ßD½(‹\/AÝȳ?…ÀÅüOoòb`ñ vkŠÓë«Vá…˜6ã-Dkï#oC‹±Y,߆²°÷nLµQƒ~~0Ž-°aC.ÇPÞ=x©KӨLJy—¼ñCøi8»¦—C@íµÅ$Ÿ)axèýQM¡O>ÅÕqªêóéM¯cTei0îäû ör[J×µãäƒü%oªQBí,š?XA·˜ ³0ë]TçU ¸ȱæ.Àµß£ï^öq¾ÕA¬P{ ä…á¥8qj¨¿‘ é÷ Ñ°,]ÿ+^ !“ÌyÁïÙè=Æ”a¿F²ì-ÓI Ë;.|_8X›øö0vœM†Ú{ó舷$][¡ÊD鸱óàs¥;˾kÉ¢û é_ÛÐÿ¡–K”YÈšK͘Ú «Œyz¬XŸáfûì&[øb7 ¼q úV²ÕSσ’¯›"®IG¬ØqBEýD¹á#Yøöª » ´†Î™­Ì¶6@ÊãZø©ÁæEÿÁ¾Ö|*9y.{¿QŽHÝb›—‚duµÒÊ`³ïÑîæ¶T$?y@_r+ûGK]xpè¦ÊÞå¶IËAчÇè;:G·Œù¤Ì‘šü´¤ÙûõX±Çx°ËC/xÆã„ K6~7—Ÿáo&L°Z÷§”B*…ƒºìå'z¦Æ ¯GNói×0샪Zlæ>sX²ð*,¸ÔN|í°˜žÓWaÑè¯dN%µêàÒUÌx Éû¨ãÎ|ÂGc”ƒˆÉ»£ôç÷X#¹¿kH0ùU¨\v¾Q¡¼+0|”rcº¹1[Ëy`ÂËlppÍ‚^‰Q•ÿ•K0i€Ëçâ  ùþµ#¼¿)¢ì”Üüô/î½eåC±Ø•S ÆJ78Uzˆ<~ÎÝõ–«Ø›À)(lŽU‡¸vO°WIÙ®âUäÌb2Û–rêcY„g%wú­“ =MîÍüüšÓV{ÆõØÄÎ5egs§3ÓC[ÙÕ_ÙÜ­0®BG˜Yþ̃þ{ðis»•€vS£1þ§ K¿ŒµhF—ñ\±9<ϯÆñIsY¦¿) Þ3žEwqS2xð+x»þøyµ!üˆ÷Òí©±j‚li{®··Ýõû'sD‹)Ì~ C“èœÓYB‹$y?¬Á9>Ü·™é•ñWnNÄy&r\Wé(®\½­"âxìzèc3¬ÝjÓØF!Þ¢w»h¯"q”qg™ÿŒùçìÊ! >¢Áb]xTÊ_éq×m/@XOÓ¯•`–«‰˜~“Ó`wM.úìÕÔ­ëÉ‘Œûug³Ø¾-t½ ç³ÝáäÌu_úæým®=ÛŽeO—fŠ_`î° 5hÙCøü¡{¡Á^Ü(Å«åÇ W¨a¬(MØô»îEýpϼ™\gîj-påÚNh@I {Ø;ƒÃ¾üd¶ñ|¦% Ð6djÃçÁòC!ܽŠí~Û¶£Æ~QºÏp!´—¿åëóÖ1ùKætën1Ú[ø t,8X~…ª}5f¾ãÅØî:EÚŸðURÄ计ý‚Iܦp`Qž ŒÄÝÖlúóod?§»ÓÈX!ö­Ê–š…§¡ôºh`+zÌÍcOÅnŸ©t[T õüôœ¼8ÓqÓ±Y½7ús°· šî›µƒ9º©_z—Κø£±þs8:÷´© Ñé]žÌ`DŠ~Ð`ÍkÌÉé®9Pµì¾w£Ãë‡y;V Ë·à-–J9Wrg*Ê1)ÈN6XN£Y²—2‰ÜÔýwG5æ\ôó¯,û<¹¸à0S¨1‚âÙ)ô5Ð÷?gPÑVC¼køLcÞâÞë見ÕzvcŒé~æI?lÕý¡¦ôâÖÝì«M{ºx*†Co-/ñìhAf ­Jg N_8h´d¬!†ñÆÔêfh´©²g;ø“‡=Yp:ÿtíîKµy+™* j2ÍÇÕôü&WªuÍ‘=âÔo™SÏÏ8'½‹¨ø¨’l6=A7œÊ#ë„Ø4÷}ìYêuœûL“FÙ,£öióè^2÷I„òô§¨'4šzÏb{gþ5öEs[¶M#¸\±c·£äÑx»3f¹¸Âl‘\*=EŽÔÚ ÑüÓ[¸­[ñ˜zkÛ‹Y±mp.~;Õù²–ÎË gYKØå CÜjDƒåa¶òZÚTÍ \+b«ùXZ¹˜6ƈ k¨DýRšÐg‹V~ˆÉ“S}V/» \ª7‘­WÐ0å9ÐÉý4ùÆ® ðÝt3þÚåÄü³:¸»ÑcÀ{῜NØW‰ : ð,C˜õ˜Ž¡ÖNzllò;¼¿0„gôµ®íñÀóoŒ)ÿ|=m›ô UÆÇÃÁí͜ݪexa©Ìw±¦;f5`M„U=£ÀêtXå1AÎ:XŸ¬xG«ËàsÁRš­ 'øÓØÈåS¨¥ Oв`q¶ ÁݧPt:¹Á ^ñ.hˆkoþ“\BÉ'Aå´5Oé AWr±aË ì:w$Ìè©–8+*A1Xƒ*þiÆ¿éc‹\\¤G§Á7ð}5Ÿš<Éh¯afˆNªõÄX·ï ÒSjÀ1c hœg»Œw³‚è™lܪв5©œIC y8¶ ö¿ÛÚÒcYhÕè\V Ò©«ᦱ6È›ùæ¼æî§Üáë}£¹]ußx¿ òy>;X§‘M¹0…]ÅtZß{ÐÈH£%{s0ðW~ý&‰Q¯íq|á>Twhg»FraÎÕåv1Eèî5ïàAûŠG'¸9KW0åã›Ùн„¶Ü2†&ã˜KÇz _X9}K`Dý5'õû ¨ç…Ñ-GAÔòKðñújºnìk,’zÄÕʿƮëåÜ 6.q”ß#޲ð¢H¬¬ZEmýƒé)´Ïñ2î™-ÆæN‚ЖVX—‘Å„…®¡´³ Ž~6À¯4¥²ù¡(_µŒý= ËÔË ©g×\z;PšzOžÉS}ÆÆ8‘ßO‰Æ™ôæÌy´z‚&k,M")åKÐ2Æö” ¼ÕÎ>ßUâ®=)`‚=:ÔNÿ¶o>Î ï;Ãj¦±ÙAbìéš$6cûAöâúžâ2Wrù ?—Þ%bT|é3¾ãÌX¶G_Ÿ$y†öÌ¡‚çÆÁ,—0zÈ(ŒÍÑXKõË~€ñ‡K8ifýe–še§0~k%®7x§mÇÑ9ãÇRÿß)¬øÖn¶Lç-êU…ÎOà ˆá.–£3@ä¶vÒšQàš­Åšß0îÆüõe÷ž‘;°çÁ0Á£×`UÓ6Ös=n˜Š¢ŸïÁ±ˆƒø1? GN`ÅCLt¿rîÓ£Ðc•ôe›én6l'ˆö ¡â†>t|O™ì¿pÑu¥ þ©ñz%š±g-í}}ž ’ Ë#ÇÒî, ¾Þì±`;žÉ¿ô¢î{w’Qjûh;sãcîÔëïd®€Šg â“ÓY0ñæw2'½ž«IƳҪ°ÜÔšÓÞ“=/Š@'k –%¦ò Ÿº²üfŽÿäNúô…¬ÔÒ A¹øIÛÉëGÝ :'?ÎÝJE.ÖƒCw<×t!›wýÁ E²6 íe®ØOÚ?\áÚ»0ï¦-¶¯Š`+.¤ …SÓ‹2Ø/´™½cGµ6ør¥…(Ó=‰–L ;ìvÒ›m1¤92{WÜf©«ÔXª° P…;Œó˜ÚQ!v×&€¾¸<ËldÙ£™ Þ$ âDƒ­œ¦K?VÓ›¸”ìÛ4ŸÝ0 "SK¹êï“9©§Øqp ×Ñ¿;ß„€º]æt±¤+´–¡j— °£&üÅõåEÆì¡X0[áö?®ãˆKz#~ªÝÊà»K>HÇ+ËÁv½c ¹#™æÔÌcÚM-vïÝ4™ÕàV¦Œ+™ùÝn 7¢Ç‘²zSŒ™×†Q­¸³ù &gÛâ CvæqIT‘ÄÁîðÊłݷGü1Ôg[äÀ¨Ò‡šÛðA]äPó„ÌϤ4Ù4,?6€kû=àÚ§£hñöÿl¾$°,õ‰†ìOs<¯ìÖ) õû‡:%xqR4±sêΆqr»çÑ›GW“U*$ðµ8zŒcÂ…èÁÕ庠a¨Oy¹ÿ qå52WóMRP¢ã1ˆ6É€ŒÝn0°;Œ§ýŽpçã³0ø´µ1=‰#ÇÕÙŽVKp8ˆdËbœË¶âœ4hV¢Òòé Tw“#ïªÑFÜŒ9 ¤Â¶êlvíC4 ¸cB7¯`Y1c{‹:¿z1ïÙìÞ®„¯”¹Óþe ûƒo·å$Æ«¤ÿñ8³ª‚—:RÍd“ÜYbì.Lú´}²ò0üñ}lY®LNùÅMŸªG?ÿVb2_¦À•ùú¬gä49N‚Å|a¶i^7ýpÜçFùÚŸfèå=ÆÉæp“ɰ³E_ÉàÌ…l81ŽÝ®ÚN«,”h¤—?,/Q§;Ec¿ÌþÀŽ"dæó&)CÓ(‡¹‘Šð%*ð'uû#¯îápù^Úl±E+™A]_õY´ºó¿Éáw$lbã\*!º¿ö­Âàéüƒ³/2Ãè­ ¶j/»Z É d–±ï Ò¨Ú.§fÏÐñ4‘b€¶.ó°^ÉL+iŸH7ËL0AƒÜùtEàWZK¥çê?p'Ûׯgw4¦°Ïv3ØÎ‘+ü¥Ë£è ‘'Ô|ó{Üw ¿´­gæ†õا-ĉM†%bå,ßHf¼%Î'©œÂvj|¨¿ðñƒ~ÝÖ>ËV²é;*˜ÃÄmÌ!åÕ«!þjKȶhaìŽB“;Ü–Ù‡aw¶5n=ÛDz;éî·Ù¯|/Ù·>¡“Ýcy ~•œ¹ãGêÑu–éî4 VºS`õÚZŒ¥î×ãø°*èM#n +Pïš<À^|n—Érºx—Lí¦mÜH¬†?Cçï–‰d¯%íà²Î8¹¢‘+!¢ììäô¿ÚÎQ¨ûJ ‚g‡±s „±ì^;$íð ß¨QyzU[u1Ø?žú usšó¤É³fl^#Ÿ¨(í`déf3Y”8Ëtn‡KŸDh2~ÅuW•xŽç;¹ù]Ï8߀Üæ3¾äf¢8i›jŒf¿§² ¢‘F0]£šzš ïžúÓ˜Üì×áspôÔWN#¥BY;œÇÉ(îÓaºô‰Q˜BwüU‡1/àG÷ð×]Š é´pÉ¡z_à7øò ¶ØÑÛ²‘pF,Œž³‹1óY¬6ó%\¯‰†3ãà|ÛgPëG-×8±)Q_àÔsw¦ú¼‘8o¤±&ðjã ÜÜÜ F=·‰Že&xmÔ! YÎËd¨^ÙI?´¾á”ïΧ•SèÇÃ)\sG(—P›ÊnÜ”býÊ¿9G%ª5­;`[M0È%͆n¯y Û¤hðûDÍÈîÝÁa˄ɼ–ŸßáTÆ!XtvþXX€WÀ÷g!²G7ÁVs*y}n ;—-ñNãG%ž‡àO‹¸ëUGqø—,ýéJ‰Ód=½Xˆ'tw9öožB/%-¤S¶U“Ѿùg–§Ó¾u²ÜÙ D÷‡¨¾+äZ+î“Á“¦óÂÎ70 lŒâ›¿¥ØcÙ[wXRW ^>ƒ¢¨*Óú“IîLwb·ÎibÂÖA8e«E¯êÌ'ãsq#µ~:ƒ;¦»/ FÁ˵_¹¹o0ÑG>`å§DŠÐùò9§Æœ±¿q2ά1‡ç~ H@Z0Ì·U`úBÊìÉì¸l§!’4'}ÝZ,$jÕÚ_‚C{ù-E©âœ§Xþ5“¥Lñ†ÒÑ`Q·“ >û¦I²ó𫚎Jiçq‡Q<W¯È+…Sü÷R¥)pTê,§Ú¹ºµÍ¸Gqdn蟑¤­û5°b½ÃyÉĽä§(Þ)obqmY4\ÑßGR·snªìkà½K›nøû¯§ÞÀûߥi¦h8wàý:,ÛæÍÔ–Ù1§ì{ ±Á.lÿ ×_¨ÐëÞi0ú ç;ÿ"¹'ÿ³t `Ÿò*¸09ý\5:¦õvê Ãø²2KâãñgιSE|â`塱,VOySµh(iã²sj›¬O;Á× *k2QßPœnSÚÇõ°ÿ =å=:„…Ùßú$<œ,y<Ħº àaqì<ÓÏí¹‰Ûª’ðÞ輺.™+—ás~øÅH x®ú"ÎæH.ƒ \#çä¿q·u“áðôå¢iÍQ“Í^ p„zˆ¹¯«YŒvž¹W1pT:ïOÉ!·ï˜á±tIü“vK^…f®0Ú 6Þ?÷la³lpËŒo+¾ m–Á«ì“õ¢‘wrÖ²Ç\ nŒ„ýç#´~® wÿ®ÂÏq?ø*¿Sa|ÚF€ú½ÇíB¯ŽÅðú ’Ñ,ÿ¾7ÀùÏ"ðvé,T¥ ÇÝ"^fuÍ úÝ@·V’o…²×'0ÍuºI#/xcß[˜_r§i¤âðçZ,¨C—Ëmdß>í]éüoÿò—t*Ó[º¯Á©ê4±^܈òS–ã‹_ÜÒuMäÒ¹|LöàÊ·m`%'ò`Í)‚²ZlÏd°:y cÎÑulLê9p–d‰•HWö.8êþ¦½Q¤Þ;Øý¹ÊðFk:Ž«=ßÏNÂÄ]²I…UÏ¡XøW®bÏf÷ÙÛ!?â.ì|—‡«¶ðØýi‘¨TëD×­ìáÏl!o/â„ðËФý—÷;/#½rHº@95{&Nëš²'%—‰àãr<5èIoifrB.åXÝ”†»õ¡¼• ;6&“mT¬‚¯Ó©üÊ<éôb[ïKqZ§³Ú_)¼ƒs… lø~>¸ˆœ˜'ÌeÊЗß|9¦Ñën=ð}g-ô¯XÍîZ®§n?FpFU(wûñºµ®ž=ÛÛ‚–c(o0 Wéæ{³jpJ¹f7%v7¶gÝCõ÷YÀúqLÚðÉÜSðþoKK |#µCœ*S1JàXùæó«ê6ÚٟǨIêèôö‘ÈcI鯸'Ãyì}ݦüÇMŸû_î2šÏ=K¾Ô´ÒZ =ý¾ôúôlÔ½ö Ç[ï‘&GæDbØ‚k¢`0k Ê&½ã#¢Ü8;9 š¾ÝdµíT”|q—œCµWsÈg'o>¿ A EÁlúGΡ0®¬•f'â¯C*žciç'0-$ŒØÔ\KúZS þ|&ð$ ƒÎ¥âáßÒðYi¼/{{„89kñø"|!xŠðM>b]¼4µ1T¡Æ5í˜uµŒsµ %Ÿî¿çßóµùšôÎB¶~ãŽ'è“yžE”àûôk<Ÿxc–¶ÿ7f›ˆáñÛI ã’ø–l©ÿ‹‘0‰¿¸y‰6ù†‰‡áö»صò9x‡iP G öæE\|ú—Óɳèrçû$çóÿ^âŸ;Ǭ¸ì§Ñðê‘ä*'À›{=ÜŸCeRÔlÖX6°àׄƒujÐ1í8¾qð¤¶÷Jˆï'ÄËéôt›Qá9¨ùâ Áòk£Ïïð£Å?sáÅÚD0&z´8ç>Ù<¦åÀüƒW°~Ÿ S½¹š[ȻғÎ-¹ÆÉì£â8&ãæ¼ÅkW’Ÿ2Uجy„7Ï/¿ dž7ø+Î Œ[7‘ëdø·þ4 CS÷bHðYÂx«÷€tG;׿6#’ÜŒ\wDUüÅšÞjÌ\›\ð àLÿ½‡eu1ĹȊ™.ä“›Þ’GŸãÞ^«³žïäy€¬H›Ã"çñá“=}•ŒøW€ˆÅLÊÍÅø;z”ÇëÇ.·!4HŽÁ³à,B×÷¢îuÌNáD©É°•½"œUT âôD..ý>ÿ`zý8r‹æ õpô–»8§ˆœ¨ë$2Ü0ëK²Q_à“3ïÖ©pn¶’Q’ˆ‹Ï:´?qMôMÆ/‘Ê?y‰bLÚè!ê­8ƒSÅqÉ–Õ¸±±ƒÖ¹A‡½9;¼Ù‘m}ÿ¾•Á…Gßðýƒ\rÀ’W¶r3]@êA1>˜Æ¶9BZæœB—ßÀðšÕ›ß9Æ‚4U£E½iŸú'¸˜‘§—F׃¡_€™g–àeÅ]õ9ŠíF•œË €îCÜç/G2zׂvÔ"’S¦ U/ß¡ÚHÚtO¦ßó ñrD0^1tæž=¸ -J¨Ò>O•™Qòä0ÈÎX®æ–øîÞNTþçk[ð®k>È5œ‚Õ!Rÿ¿G¬Áî²/¨™SÍ™^ôî[ÈÍDÓ“H¢¡]ô8,j¯%ùiëñÆ­6höÌåïsÑn¹¿$_Sõ6ÜÛŸ6ž^¸aU=w–ªƒEÊ=È©UfQØH&,¦Â5F8G S+ëÆñ›yWê‚aåÝ8˜rn) 2ªõ—`6Žê¹¢Ã;È5¾Í"~v¶ÒrlͦlîlöXêù}?´b {¨¨[¸–‰bè/(G”rî|àˆ‹V¯§w›"qGË\5ÿ…í4p0,†elù&fIdµZ<*hCNÎl–טˆ!ïóyšÎ[QrÓ~8$ÿ%ßÀ=k”©‚y Üijƒƒx´¶•M+†îƒ31U¬ÇœÒB‰?ÉÔ¿#œƒçibCÉ¢Ù¯¸ù=‰øêÅýõa<Šâyû@RŒnœ3r²‚´:LÜTBÉÈ´x,ÛzgqaÏ÷£cŒ0¡íDz‰,Õ£æŸJáâþ-`Ùök¬½I\â#¼¬q7g¶¢à¦ÎÌ` Ç÷*nw˜|³Sê`%<É»!kr=ðÓ™o½OÒRÞr//@fR†K¢‡ü3œ®–@3¾ rãûsàD2¯±¤Sø1Ü•$#[9“u)—;ŸJ+¾åØêÓäg³Yˆâ{X5ïSûñç¬ée·w 3¹ÔïUÂÙú§ÍçeŸòfi÷fÿßÜëÍãÙþ;gñ•O,ð#µþ~ÄT¤ nöz³»»í¡r¹(sØçˆ½:>ܵÇM°H½®9;²†ŒyU‹ö‚ìšïøy)^÷iÀ¸­äT¡.ìù9ÒdË{ŽÍ§³1Æç$·S«‚·Øów {¡×ÛõŒuŒT¸ÜàâÿÑé~x3Cï_„¤wU‚ä,²ò“½™]À›lµñÒ¦Ó´Wº\îD?œ³µÌ)â^u£‚ !ºkñXŸ!.R—Åa9 v/6 &fîÙŸyeÇ›ôËK2‘¡t6ÚÄ”bÓýã©[݃žƒ…œÏÕ=tïÛ(Œÿ‚ûÇGB^:á Z¹±Ÿ¦p]SÞqIÎÃ¥¤¥lë‹Tx:2v”3_G¥álII 6n]Êo>÷®’‹•ûŽwT`çŽPc;¶s2V;¼Ã4OðÑjÁÙ’4–3ŽZÅe‡:¡ZËfØýx ê Sù¯[yA«¹i§Xjí\o–Äy½‘b5¹Ĉì*J‡&[M´Òû 7ÒÉÜ-µ\}‹3m}˜ çí2øÇíÀöØ~ãupùU…±ž,|sNÞjB–„¡¤Ûsük`Çîϼ 󃚸âé#¨ôy*XN<9Â|ÜüVÞÚEÆxVB{–>®Hâ±­…·øQ•ÚÔ¥V÷Ž9LÚo…ÒïA©´sÙk®2½Œl2qÏŠìfÜ (=û ÔGB„Íj¸çü_]j4Žj€36Àë8X Æ–dàÅ_• õä®ìê…Vå³ü9¶|3÷îƒØ•1ôÏêJÜ-¥F5&@K…ÌùFï}3ÂÔGëè’€màþýÑÿ•JÞŽðÍÖ¿¶ìÁÍ‚ äçÛwÜÏ&ÎÕ‚R)Iönë9+®N¿_Œ‡¦˜Ÿdòñ±TìÜin`"ÄoÆâ7~Øù§BNDà;U+Lp8ÂŽe8PÓ½íð_=蟄҈ïã™ÌC~>ý!_ »æKàgõ`ððb_Þr$7k!ý¯V´”ØT–áÀøùeJ,ó-³]G¯p…—Kifî:?óÿà^k¶¼Zý¦¿åæ¾fðuSx‰‹²“ü/@”c!Gc^è„Ð’-(vÞ™JE)2Ã?ÍÜqUqŠš(ò#¦Qó%vdÂÆSÔ~V"þq|ÀYìScÍå¨[£Ýs`5N5r«I¿çqʶøj×^0IäTïž¡zªh#ÐP5kõö±Éã è˜SqÔYp€nq>J¿úýÀ›’,odô‰ÒûV.àúé\Nì¢S ¯@³ËN¿´ž3=”GÚ2Ʊ“–Ó§Ï×ÓµŸ°Ë[šÞ©¥_­?áÙÄ-l­™Û¦[ žž¶¸pÁ!²TuÍÈJe}±3Gžtî`Á—“Ù…}o°©p1›rˆsØ[oÀeƒ)ÐxÁÞ\³‚¾Žn¢ù•p´rjö® ©×V Î‘hÒesuBgÛ6c:–æÃÖ!sÖzLŸªË}‚%¹ŠLá–4œtæÐ´:œ9ú΂+îeXbâE†ÅÍië•Ti½Ý+pAË-|ôa#5_íGn¼BûÇhq‰j{ƒׯЕ+Ø[Ûqäü¶½¬ò‹ z­HBÕ§‘œ´h$÷3q”kw=ÃÕ¹Z\pU^îYFƒµŽCO2m28†<À’-áGï,³nðº'5éTÞX†Þ«ÎÄÅÂKHÌH(l•QbÖ±kY¿Ü,ú,ÊÍô è‹IÝð}B?oà‡8Nòa¥e7á¶®ê%»w!NZŽ“QÌCn¾/Üœ$„%óÆÁ#¥RØ·é_•âêbZ1n`-µü|“?¤–AÞ&cý×¼¤ )ºÌJŒ½v[ˆöeÙÖ.îѬ1´\x?ÓrZïJÐý‰?Qø²ƒÍ+[ƒßÄ5¡jó-°š¡A]¿‡[|ƒ="[q³X)úò³ðê•B­üš‹zœ†›Ê'à•„ZÚl‚åCìu~ æ< ¢ vÍd @‘Í*ljþë|“mòèJÃdË«§\Ê„½ì°O wi‚!•“KEÇiA”—ïA߬[Àí^%ËN¥LD—|_2ô©/–0YR &SiŸÔwýëLÇ0‡Ö3¸mV5gPyMS@ºán?&OŸïzMÒeÞBßNKº½[ Ò`3Õ2=:ûi¸íP\Ù Óä¿Â¼·…XU´×<‹Ôv]¥}˜N?x˜­mˆäK9Ÿå²µðzQðXÇK¬ýJÂJÔL¯ŒTèÈÀî‰,tÜ-üúØ;g~"­Û{›oÞnź’]9I'›hs·}4!d‡À÷F·DW:q¯>l„?œØˆÜV°SV ÜG¾‰²ÜãÓx¼ ÔØ¥„Oî}ƒÙó x{$–7çþjê ®Ïè%#O¿L?:’+¨Ó¬zH—Â'ãØg±FÜØi‚Üm©éŒúbIÛ³íè¹ã>ÌV‘˜Ú‰©Ó™Û&c²â÷W( "ÜàcPº{ÿ«9l`hÁvzæ šï-\]ì Û‘D—bãÆ4l^SÅG›Pvâ¹"µî±*;Ùú'Á8ïf»­3ĦiØÒ«Û´Ñ»ÞÊyDz<‹Ý´ye>Žßy´¾ö°ÝQLOóN?˜ËLNO§v[‰©ÒÖæÇ¹»à¬=Þ·ƒ®Bâ}™KØ\:žÞÛ#ACdñ‘ê ˜£ÛÏŸ5ùî\¯LùºWÉÁ<ÜÛ=''ÿ÷ÿò \¾°‚¥LˆÅǨӣ_µ™ÂßLßb1µ ‘夥 obÉü&þqv÷öž¡Nñu܃¾ š÷g ÙÍl, !ÍËŸýžðJò €¼ÂÆ€|0ÜÕ†Â~ 7ò |nX Í!!°?è.È>+ƒ› œÍ²f÷IUéÞÇcs-ð¶îkÜP¨Â|+¶³ÿj7`ó'ª1y½R\W.I…Z…È“¯)`X<„ɺ{™ØAaÖ¢žO&ê¼”,f|¤Òf=½÷¢›´}'5é¯ù)«Êpk”7#t‹Ø¯ŠWä'2vü(ÆûÍàÄ'M$¾Áy.=xð@ á)rp`®Ýttåú Ø…ZØ1ìIku®b§t.gLo^x ο)\¼+6Z±Sß/â¬MºWB£ÆÂ*‰oäã¢8¶BÝ L£¦7^É,ÛÖ¬Zx²Zê¡·è VŒçó.ÙC!³a­õ`‘4›}šJÖ>b¸b‚­W1ƒ£R‘à›¹“Ý]¹–›§ãFÝz~â‘G^ð}Uió·ˆzÜ9 &.µÆ¸©@[:ðÕϰ۾“¿ E”E¸ÅàË_𬤡+;C‰_1¸+B{Ÿ^"†ç@ùÞ}¼úØ&¥"m¼æüÍôÖ†<Û-Â^g`ŽËg¾Ñ9#Ô_íÂJUBØ}¯H à±ÿ|£¼ §<9²t9¯;Hš¥°Âi‹˜ãË‹Çdš®J‚Ô™SYç‚N¨•ug÷bÒ£fÇðÇqSXÙIfn+`)‡Ã¹‘Wô› ë;¶€v?ŸÍ‘‚¸/:BöÀáU“Yþ‰J²(É_»‹ØDù&p!%aÿ`QŸ]œ†/¥Ú «X–D(Z7JÑï\…× zoíTbÝ·]g[ѹ`æFfMÚòOOÅd•,vÂêÔßòa³6Ä2ÃÑ|> <š‚¢±Ÿñ¦e&ݶð4hwÌ…«#ø0dóÞþ3¯Jâõ­KÙæ¤q´ÁÁ”VKŸ†WSoÐÍf9äѤSûqFã[ròn.Z=ÿA ióÌ8ë“ý<ׂ¶PeÊ®êì~s s?qÚìxL¤I“}>v/mM„¬êH*‘ƒp§jŽ]Œ5õj,ûîg”Þ*Lc?€'‚/àÞfêûÆ›-ÖšÅ4MCiÝÖ t×ÇzÎQÒ›Nk@‹[ºÈQ†FU.eOUŽ¡Á³·$g-GžŠ_Ò»_ÌÍ¿’ÖŸÀþLO¡Ò]*ðc"]te>½8ÿXZ—Ó%î‹hÀÇ`äΣFÔvf&3›±=f24r}‡¨Ta ê÷ÍѬvFM=%A—ÒUô—Wí9¢J3}BQlÄWgŠÐùÆ()?zåRXvn8Y)ó¥g²¿.ÚT[®³ÿ¬‡ð¬É€“‚i{Œ6êo¡÷¢˜OêB°È`ÁîúÔ­¿š¿æÌu¤À‹ší¬´#›—5¡çk®>Ð Ÿô‚Å]#Œ$ÊÄ¥`¦CQÜA¿^¿³85žÁ2Ô³F–Ùôû°³kÄiÔz\¬™ÂkñÛ­f\4ù¼v§öؿۊͬ|‡C_§b€­-“«;ÌIÄ9Òî÷EX&t¯9ÝÇ‚÷tÀäd nÁ}óN£•f ¹Wy€:¬2Ä`…;äxé*¸×&ÕÁâøûuȬž ’Ús™³O¶ÈãU¬p̘ö:„­}>Ê£‹2¨³É!Ú4lÑ$~^J“Fù‘ÇÒD¶XQ·Ë\Kme¸,K íŠ93*ø|ˆ°QXPt›’¦3CNà¢yÏaÞVe¶Y©ûÝ#Ã.ßLBSÕutã¯Ù¼å n嫬Т£Osªö‹hqHó%IòòTèY)Ãæ*xbýOEì©\B&„m ;—Ž£¿êÂáE{™ó^{µe<”[HÒ I•–÷IúÊFœºoÊíkå6;¾€ib%(²[6Î:…i[r±üçq2i— ë•ìä+ç ò7•o§!ßR1ûæjÚZoÿ†‚ø…•{!u”_¹÷½¿àù?Ɇ„°©¨?Qþ3‘5ù°öÜ+¬sQc+7áÒä'Òm=Üá>vºöïAØ2VAÒÙ8§“Üœö\È?ÝÈÞ.¹BV¸®£iÕKÁÙ´ ìg„™oPÿŽJúÈ+‰Ú¤\æÀÔTÌ8^×®’DkÃ(ñub}Ò‡hÌ7):%å" “N£>‹]þoä`~)^M{³š}–§[ÚôѵØ÷¸Ñ1²co €«rL@×£½¸m™-Qá/°²D™Ta*¶ÕMDQ_DŒfîØ ]¯>ÎI7fؤŊìYâÂí,×yömøãúwPÜåò—߃»æa”3Ú°ä\ùãB*Ó‹/_1‹ 3€‰1ÿ;7á ¡ŽEJ¼c6ÇðÌË0z«R¤—´³ ‡­d(n똓†Û-žÁF±#lí%ºeÑ\*Í&³®{8vE…yŸ  n}ø{c ¼´añk¾aë/AösL?Z^>«ŒgÑ?ßNƒˆwŽLcV ÜŽ"¸Ô[GßÒ Ô¡4×*þ ÇP,ÔnÂYSê±Àk<³ÿrS§—ò^n n8§¡Ý»Bõ÷ ´2­ϽáCÆ6ö]b;—{ÜŒN«  ‹^¡n° ½¿ÌÅÎDÎÒ³Œ+²pÃðL+¶Fc+Vó¨kº>s;OÂÝOãp¯*;©z j~âŽ=Ó‰ØÖþx"Y8„>®qœ™üa¾ßè8&hf`@ Ï,JÁé;å’¾TsoƒÕAˆã³ÓQ(ù}V¾Œãš›à‘«yÜØÆtY–<óXÞåY•6àPïwŸ†¡ÊÜ@v¶8‰ì= U{Ÿ`sÈ+Ü8[ž8ãMnå&> ;©Bv,ý8žî­sÁD?=Úro&Õ}š…•Ý!°õòb<7YŸ~±…вtÜ.Î ßqWn,ßÊ'ä€Úá•p¨# gê'ÃýÆû™òWç²¾åù¬óÄ<&÷ïL ¥û—³ ÕêôZÒ#./Iœ{äé3+úv6GBÔ©¼ÕzöJ*ÌxA¾m¡|íÍÈû©D;ö%rmL˜Og\?²‡\z,Êœ>µ@éàCRie;,Ž¡óåôÄ>êùËuÀ¶»Ê±E‘霧º5$§ž@¹<&åçK¢òµ@`ßtððÇÿlZK<àvv¨ÓúCc!‘6óp[ =IÍžÁ*‡µX…¢g3X˜œ„·KvS¯OL“çYîM »—Ã?ZG èÖ¦óÊ‘>æl <ñ5,îZÄŸ`µÓŽøQü:lJC–ªÁ†ÄÙe‡r|ÓbÄÚ«¿ò2þxaçÑ´¾¯„FŽ3àR§ ;h!Åþz?k¾îý— u`ö{)¦kÐÕëò8ÿ# Jþ2·z6ãúë§£âË)Ôdø$U4©Å†]L(ü"ûÍ÷Ið`ÆK)–e.CO;Žoì9øsâ:gë®Cmeg¡ÞŠ Â=¨ñOŒ‰˜†¡*ŸŸº.Šz<ãˆZòXþ阕?vŠÒM@;Hš†MðÄõ%¶¨U]†uòpÅÚÅ\u€u0taUî"´/c_Nmp¶¶Ùlj—“p²¥R‡%ÉÔ@ÓÓÃxOòù¼4¶Ô`Õ7Äèùó¦ß« ­§C³†1k©”¦{d‚¨mÂyöo¢…˜PÜ2d‹¼ux}¾‹Ùtˆ^6bΛcHéŒ|'£eÔü»5Äö͂ؼDÚyìÚ/}L¢f°ÃŸ¦âËË3Qç¹0;1(šAÛhO Cú^ Êeiæ>ÙŠÙyëb¶í}¸0G‹šÌIB˯‘ø%¬×ìŠFG±NàÙóõ’ìD<ãðËE þÕY©(:y[†Ïd’Hÿ…ÿ¿$úÁ¿Ùf4²ñ4¹õÀ—f½dvØÓãúLê¥3Ü]{„H´{ÁÝÚc–AÂ^N¤ë>¨`y¶SÜ9Ž›]¥ÆÝñºm£ðŽ!c7›ï/IG&ÛqKN¸²ç²Ðò½8íõP¢÷Mp¼Õ&HßÐÑtÀZŽKý£Ã\ç QyÕ.òoY1 ]h…¦ÊZhû"Êâ›ñÉsV6jk‹3ÕŒ:1%°ê¨Î(+€ÿúŒôa—¼X²• Ð]G™Àö1ô÷òHMÊðüùlLû>à·¥Á ½‚©×´Q4g& Ê9BäpÞøLAèÔÅ”šNø».™±§;7s6„?Ð` ÑÜß$öº¶€ˆV!œ>{œÿ¨Ã‚’õ™­… ;¹(ùŽ“/&МÎ^üYyò4¼ç\83 9ú\‰?îu"ÐÀðγp}«.o¨Ý”Jœç¶?°¥ªE øÇÓÖΚ@j^[±7N?ð­ùVRØÃOž,C'Åæ&‘yxB9kU}v}R¾ØTN²ú°Œ8޵dsÎ °cOb³ÄDØ9ÀîLaÜ ïÍ%hø“u¸°» MòOÁw­¹tV^ ΋¿Î‹Ýlg½-à¯ãQ¸: Lo² 3<,èLU2nïœ"yÌÍ>â%‡ÜšgŠ[oGâÞÔE˜ž4lu>K¾H‚6.ƒ"£<ªŠ4á×F‹aÙ¬PNá„>13íÆa¹¨‘«‚ëµÀ~r —0MJG€×ÏÜѱXò¾óŸOJ}uÈMm èÚ0–…»ÐIuydEÄaЯ”güsظ¡ZÌÞ·5›ðrv¶Ì 'ò†Gx"$?ÕOã‰Ûìj²œ3^z€3Òl#^GÇýé>îŠDXßÏný¾‰á“¨sÖx…²IYxaQ1ç98ƒ5Ïk……‡öÒ *Ë!eÛ~cÈέLovVàih±Ø²vHšÙê!äÖnw:¢}“ä4º°bõuÌ‹…¤T¡_  £‹É \ÝÅ…UfÏûÎ`¬¥6Ýä¯ÝüHò·Û\Pr"ü‰hR/„oDÃZƒ0¼>V ëöXP»3i ðç0Lz‚ê'“0î³&»[Æ—Ø[\ñ—yx6l°œFk}gBOä#Øzw<=ü ž$ýÎà=\™Ä©{á7¡©œÔóÉ4JZœzæâ•k2ÔáâqÒòð,Z”ßÝ7 Ùõ¹Lbxפ7»úý µš±å½©ô`s&èÝ"×ÏjÉzÒ  jsmx9:Ï<ãZ#êùS×óg³‡S¹·µí0×ä\õ§gWt¡i„ͯÅ%Y¢p6í¾Ü–öf”>ð8‚9†pAÝ‹ªÍ_±å´ê¥uoŸJ]—©£8ü„f¿0ì—„@§T²¿Í \Ö‡Sô®±¦vŠÿÀ¤RûD{xœY­hø ƒêêÎÝùV Ù{Ûftÿ"IW¾hA˲6þjŸ¿DåÚð¸+@÷ÃÚÚnܘzÝ„k¹Æ·JìQèN<¸ÙŽ÷áË~ˆ´G˜wö N½RŽ’v lì²ñ€rÙÊ»?Aÿ‘ [>´”øÓGî!§:p ÏØ4£Nÿ f#¸›óºÉׇÑDM[]N‘·Íõд̬náêºÏ°ÐW 7É©ãÚ‰‹Ùqµ.<ãƒ/B¼qKÇI˜6~yz·^¸CegÞdÞk”±×G˜â#9ºtÜi6ƒë2žns¾sS²Ópïï|àãö#a)ý]¦ƒ›&Ñ%M ´&Üo)²‡;ávg ¸Ô\5_]þ1ÝŒ0Sèjó›¼ÞÕÁwihNWÄ‚=TᬃaÙ±±Ð"º V¼«€Cßîð¬:<É)[_nˆ/&š§ ¨™i'=©ŠXŽ’ë q0åÀf|­¾µ¿áÒ9ü®Ï>PÆ­ç?„Gëɾ'IÇ !Hžñ 4EO£»e9e}oSç ¾Bѳ?P°·—3Lf1ýÒà$ÃÇ¨ëæøqŠ«œÆé %°sâü™ýZSWúŒ¡ÿ,Vâ2¯³dßófÞ§z[’ÜPÄY¶¸Ã‘£\·m ÏØ3Kd–Êu—‘ËŠÅ]z|½ƒ&8j¼VŸ‚Ò“ÖâÍqÂTïà)”Úp / ƒNŠ!ÕaüÏ›ÁÁo:+øØ†nUÈ*%ÀÍ¿ sןçfÛ.@ß7Ͱf¼$•ÿÃN©ÝšÉLñÄq;'BÁçhü8ù÷9ÃŒ#ÞÞ1•gq\”UòØ_5°)´˜÷¥Ô’)\î–$Mî<–ÿ{2¦ìc„4Û¯ôú3óìŽwöò<*³‘7å:™èñ‡˜Ýǽè1dï5np ÐU8jùBÌäÃw.¤~,ût–̰½E$ýïb­?ƒ{™:¸®P­ËCèi{h¸³ŒüÞKbÇôp3bo঻f,øÅXÚ)K„úA²‹)÷¢K÷ù7a¿Í?ˆÔ<Íͬ³‚Ùr·Ðrk7è9Å"ïbTþ:ÌõÕ ç® a^A™baö&M†³Ù¬’Iu"nM÷äEjĦô‹8ö}%ˆßû ë[¦rSÄÀå˜/ì Á`±,_êÚ$J]ŽõcÏãéôáÕ#tr›Íµ‰ LmÊïÕ~qªÓ=ëq›2Q©ä‘W3&’2¥Shøº‹Xüí¶]j M7!¸Î]ŠQôÞF®úà Û \[ä8 OzóOÎsÜá5Tݧ¿^/¦ƒiÌL~@½Û²†Ãö³h5#™SW>{qêöB`‹"Ó 2@nœ^-¿‰†ïíèõ‰ï8ÿÓKàiÇr¸¤ü’¸ÍD‚–a\ÿâWˆjµâÌWÖt|]?™‰xeQ2,Î]…v›R‘“ EcÍ\2T={0ž•Ú}}[¨Á|ÌËÐ"Iu•ÅØÏl™Gÿ‡Ñ-®Þ~,[y¨û‹ŸÜ˜ânÔÏ—mY2ÊÝör<ÉDâp®0C³fl·œ8í|&A„j’©ß÷€Ø"˜»³_ÿü€³ôÚ8‘UÓÐIy>XOI‡Pˆò¾¶›*‡æ6â3Ã:üì¨ KÇ¥’› x†³'%'«Ð±Vm(ÛK|à,‹óšl˜Ü]¶²m=£3£ùŠTÇ$€>×>ÅÅl:Æö›Èi¹šÒgY (»ÉËŸÿ$Uö£èŠ•pùÌA`O‘8n/…g“‰RÙ2êrd Îø{Ê:Õ™þyUvìn.Wª} ¯ßu§·-c7VžßóÐ!(…ö¤=…¦(”K Å3èçs-P¸&¢^R/ü.2c³ÎUqŠ~süsƒxÞÖõxÀ|.÷ðÀØüЖü~ž6—–‘yѸóïn±»×÷&^÷sü§kæ³½¦ÑlF÷Óá:N¨Ò¤"úÔ¦½€x}œÀ«Ð_›Åè¹£‹Ùi¥…É…Qœšñ^¬Ù6ƒ]ÿÜ(¤E¸åä*;Ë„]é-Å¡rZ·á1=4¤ÏâìÒ0zcdŠþ3ÀÔ‹ï¡ëãXVìÑÃ[ “GþÝ}Hö4Ós•x=ô+¿úìAjdt‚kr[Á¢/„ÀùC|Ò}pWæ:otè¯u»ñO'CKÁ®÷L·åÕ¾x» Üö0¢%¾GØ‚}PaV>ŽŸ•€Öɱ0°uåµ$±eÝÅDéù²Rý¬m°^ ‹Tt¢Z?SèïŠT¼ü´‡»?hÃÎNÇêóoˆTÚ.¶G –Ù›à£Î_(Þ?Aôئ¯?›?x@ˆ˜ öÝ:>âñy_˜­‹3 ¼‚0¼R½˜½Ë™ëD¯¯pi?¯;)ÉÕ¼§V 7ˆ¡„rìN¹šØvÓ|ç°Ì“눷\1Ϧókns>´ uÞݬ°ãظ¥ ^?ÞÃÆ ·o,•ÉTâ½Íëåu^We?+þ¢x’ §÷k®Á³`&;„k£!d@„…ÜàêKgaì5ºwV'øm{ËEn0b›Ö†â³3a°äØnøUäóOXÁÑ1¦Ü"|¬~yã¿\‡‚«)üì•u>QˆÒñ5lhü+\¦üšè7•ÍEgÚ$Æ…&l¢Muø|Ñtr»Ã8@ÐGSð¼Wˆ [SxrÞú,!²}lÍØX•©´:0–gNÇ„Ë.0©² ®~<ljÐÃqš¬m¿ùñ%$þ´BÙ£G¸â¡~ʾ…ŸNFІ2OªðÚµZˆšc4çzr'Wâ˜}Ñ]9ôt? ²ç?íÙÇ…)ôXÔW.¡ôµµ'µyƒg4& $,âßÎr7þ_,ÖoAáìv¢ôT‘óÕ(傮̦¦QßpÌš¿¸Ú^¿lW¢ÛÌSÉÊQý<’¥‡öënsëŠpç3‹áÕàÞÜ{©à’ktùGÒwÑ]ë3¸‘G¡òýGœpŠk¥NCÏáí3ü¹n>=}Ò’ELr ®â£õ %[ +7ZÒˆI-Ü¥_ò4Áµ¶ GQ·Ga0°Òž4o—¼X:[·ý–oCÅ-ÙyEÔú1ïÞ’¦Q^©´oŠüþ~—»ùú< €“qpàW>j'Mf²ë˹”Å@…ó•ØDÛ\]: ÷Í\O_I¥*g¾@êüe°¦K jŸÂª*³¼œÍ[*Ê”Z"ÉÚvx:ë4<±ÉÇ”>ìÇÕhx¹û {Ÿ†éC¸úˆ0{*.LÇ»™¢îVSV^{…§½ù2Ñx·š5ÏV¤y9R(wÔŠ >:„O˜õ2#æºL„ÝWbhh\†ûÛ“¸)Q#pI/ ‹ž¦@@ÂJz}&=ç*‡Ãï3qø©?QÌžFç†=Ãý{\kð nw²éÞO´¶¦‘ü¶4áÝš8•Ùki)óf–áj­0,ú)óîðoœ® ÕãsàNÈÔœhÊŽ‡ð‹%÷™úñƒ²ÄØ¿©ÇF9ÖU\<4ožr¼² œÇ][p]•γžÁŒn¼Õ¤gœpÊ6J¸Û,¶¢Ñ«&p—.M£žÓs_6ý_¶Ô _ýn_Ì{uD_QïÕ7È ÿ|œýew_œjÇ+¡â“|¬òPbNó·áá* RoürŽÿÄ Á4Òºu](…~¢Qxzq?]–îï8Šm‡ðÜi ¨™p½ÇVR…1‚ôG(P[MEºÙl Q€G DèúYš¨àéšò°t–‰ÕRår';° ¡4ßö ½>'”0YŒÚ§Aü[†û-{ááøt¦›*M'ïóåö™E‹v`SÞσG£ d•ŽlÆÑx†VbŒJ:}üØdfLC_ÂìÑ¡»¤^è9VìÅí0ŽÆÜæõðÒI©¦8Ý?s9Vv†—©#üÅõl—¼Õ_C”zsÙ™¬tê¶U—:ˆÔró‰;;‘¨N‡S'0îk •ÇÆoéh´Fм¾Ž¡l==QÍ”å´è•k¶€ñXÝQeÀ>_Mö‡G‘ùSáÃÊÝl…`5·èäCNýóSÚXÃbÚÖPÕTiÜ&oK—– ÀTaO~²¬WÃéA.Þ°’ I?F?5Í•‡ëáìýÅlÙzf0_‰ª,ý‡WrOÇopn8~È]Äú±XgRAÿØ2©‡Cà° ÿÔtj53~ϸN$›Õ؇Éðå>S°Í¦•l¸íwò18¹•Ö(üá<›ðÝÚo¼üàÓ83µçµXŽbÞo0öçÞøï¦©Š®hÙÛ3v-¡{÷¤‡SÉmµrâºÞdÍ|¨íŽ‹0±.’Ýaj©ýw΄†‰8³Îiyà×ÝKž€‚Í{ñ†» ýèQŠºÏ›}v¼à½ŸãIk§m¢ò£‚áÞ•h_XNGÏ$]Hµ7r[ö…CI–$½qß7©a2³£ïGú¸ ro»Ë /¬¶'õý±EgŽÎ˜…rž:Ô_m‹ùÃÖ8cݹxx$ž%õ¾ç÷¿>‰jT'þU‘W e™÷%ßd)–£ëÎãXàüŒœPÌÆè}~x*WR5=Ø{ý ¾c馒C’¡±ö+&2‘/Ž`¯‹0ë]Û r‡N’ïcˆç’LÈè¿âµÚ´òße|üÆÏWŠqï=4†dh`ëxv!õþÝÐCšØ'~ÓÛ'èÒšŒiáEv6­`§œ‰ÐÚÃ6‹Êм‘õlIî ÏIî!<Û¹Y+÷øC´9|1š.ÛŒÊãÓaE {öø.nþ¢„iËOC‰Ü¨¹9ó½Fƒ'h¡—ä6è|;Ô¢h¾¦ ÜÊO‹Ó“ÔÚ^•0~¸ˆ ÛRM=Vûk2[+|³m“äÎlá¯^$AÃ.uÂñ. T£Lÿk?èöfZ °yŽõP^­…Ï.•ãÛïYéø)Ü¢YðÛ–ˆò÷f×ï¼$…qw±Ã&€{vi!-ªd§Enñt²÷sÍ!úÜ’Ü"Ìú#?:ê²·e= wz6 ?ì³m9ù¯gná_^™ÆÕo„»a³qñç Ä.W53«Pßí„XȲÚöóÜE×|˜ºoêNçÒÖ>ÝR~´¸=Ki5w{o‰`OHaó;8¼«{Ú +0·t…1Õ\HoG’Œå«¨Ûò»|Ý’Ômå(oÊqP¾·´Ôýq^ä t(«sùÅ_@a”“™Ûì ;´ý˜ýBxØñdC™û'·Ã¯†|ý«ÅŠŽ Ñ“^âlëvK6ö9mwNã^ÜA6z‚@®(‹4¡¼d0¬x•ÄkR,ÿ?7¸}iF\5o+>>ÿ-3}ó8š³þ.pzËm¼IúËÿq7?~ä_ºËd’=…;1aÓET¶øƒÑ嘹ï8¹¦€ÕíªfQgðß©ƒ#çr¡ó^»ÜöôåŒÅŽÉÜÈZ |çLY~UŠø‰ï÷|G7¡­LöŠ.êÈ>‡¯Íä·×%rï5Úm ‰ð“prÆwÜ';ƒ‡äÏk9ƒq OoÙƒb0tÉr'S®zÜj~ÇdSLÚ\‚Û÷´óV˜ó¨é²ql†Ž[&K¿Ý1¶Ã¨×ÜAù$ÖÑrD¦ò0¨R ¾Ý9 †»³áQ—;NþKŠ[|`SÐLü0A[;;ð«ô:xõT_{k²ãOdèŠñºt¿ß:dVÁ½Zž‡[?|&+ÕQã´>75z {øqu•¤^³Xý½æ…“ù­~ˆ Nª¢_Ÿ*0LzÅÕ[rëo ÂóÏ„ÊVcYÊ컺œ-·=·©«±Åäúk‰CEë6Pß%Ä:?Û…h|çþ”-Å7ÁŠ´¼É…Ù½¯F=¥¥4)îþ¼ü”?¿k"®ßâˆ>·ðÃ~8”R‚úÓ pï‹eœÿ>îzŽîaï 6·;ÚíàlggG5÷r[mÎÕá$“þ D¯{5¸$n„y5=x·•2…ê˜|Hé)>dõ…B´Ù<5:bY•ÛYÒ~¯¥*ºp‰ˆ,Ü=B÷¬5€LM2áµ.ßC°›½ÞüDzçèJ®tï ú5ŽtÊüøÓP\#Á&ªoguÁŠÃåX˜ÀÖÌ”¸Tuõ ¬HÉ ÚDìÞ¼%Ñ Ð9¢OMÝ…° ¿ACn*jŒûIR·ÆÁ⧨’ÈvŸh oéKŒÛWÆV«GQ¡M²¬á/‹H¬ÁìKæl8' ²bÅñªI*¬|[ÍJ2ØÙ ʨö˘ͫK¶k¿yrm)Ó©‘ ×1¬ÓÒœÙZÌÄoª——3|6ã·Ùeüj«A»¬‰»p)¥o'×$ØŒ†¹|¡†/°l…8H+¡xÆm¨â$]jagª ª,øšÇ=pNØ0ÓP Gn‰áû¿MD¨Ë{Ûö"?Òc÷r…#®¬°­Úêéñ‹‡Y¡3J´-Ã[{¾bá;oV;׈ìõ‚ÿÐ×WOaÇf ƒåH(4’AËrÊm»ŒnHQãkÐ]÷ƒk^§KÝÍåqÍ|SxMa†é&lý£F¨®½k'ÉÓqE£ØÞvõ»Â#.UíÁbZ 1þ>HÎûÝÅIWB:“eŸ=.@ý÷2¾‘¡¾ Ð+2‰Ö«.á*'NÆi2Dýº![øó'Y·DŠNŠiä‚V?æfOd[Ÿå/¿‹+·í8,G_Õ‡ÉÛ1~¼‹Ís€S‡“È£7–xûà þůó`{z,¼>…¦¼ŸU&u¤Îîfv˰½ó«pZ„9]~,³Ýo…‹z¼i·v%ªœk‚Îã‡pxo5Ñò*ƒã“.‚ÖËxl ;‰—›{0½à"l÷Þ Úo>ò÷FKbbëyt_ mÛ³°7e <›ö~IñùkÙñ‚Å8TBÃþTܲ9Þ4vAÌ…XHËëÇö/0{°Àæ`Ö(þ]WaâeK8ßm§¡iÁedK"™œÊ|0ùü6ûùÐej p{¦{Êêá€ÔwúC}:["õ #ŸÒM|!4~ìܑſØ1óïÕ(¦iÈ®™+P 4£:7/‘¼àº‘Å¢ÖÎìnm2¼2.ÂîÒ^¨¨Â5ã,øëd”™lòº1ÁÜV]Ùž…ŽŽ»Î³ öï…¾'Æ*˜\eõ’ö¥ï%©b‡#•ÅanÓÍUäª÷J:M!—KŸ0­ÓäÜÅžž¶?xšxˆÀuƒ_äŸæ*:?”zXT‚ÙðEflx õwô¡ªåü§ú"l„è“›©ô²™*_þ­¶Cx;»9p gîÝÓ6ÐcAÙø³KÊŠõQ]v7Åâšís,ƒgGZڬͳçÍž$¦û{uyKå\Ñð í¿ñ ͤÿƒø,vvê–áLçÊér›6ÐSË…ÑŸÈV^3›Vôz–+,^ºN”}ƒ,L9ÏŽŸÁþ{ƒ®[ÓUÇÉÌŒ`*Ó+B‡³©Ø57Ô ;½ã‰ÇYa6Õi‹ùXÀBϱ#'=›õ¾3¶‘euWràô„8TÌ&<¯D[s6WÏ—9‹xÓ$A)öÎÐ l¹H¼Óå:*KÈkp|Øgˆ­¾7Mæ'ãLÇxÜf›ØWν«)à”ïÞ%{fGÓöb-Züm'ØÌ\¼eÙ¥S4EH–…êíƒ anê4ºiS=ù°û/oò4vö¯ñŸ2‘ =écè×6Ozm\Wq$–œÛ†³×-Å“`Ä›IxoýÈÅ+¢äÓ&Õ>À9Ñxþ¹›¾ó¿|z‡Dz-yYhr³ÀNñ ›QÌ}ÌR€•BØX{V´Üо~‡ßÛ§³†—ãè{~lE°)­± D%}'ÚƒгaH»ÃŽPõëµ8ï`jÓcÞ³.ÃÛË©àV¬‰Gæ¦CÔÒl¸’ð¯=  :q=ü绤hQÿl†è§„Ø-ˆ}t<`ŸÎ-˜<§Ä^Âl÷\ýÌŒ.Ýgž‹/“Ô¥ï`<ï6„ ¥Ò³§=iˆû^\v¿'-|…9¼õ¥‚ìmzQqnXù."ŽBð™ P–µˆ¯qSË“ÒÑæœ §à½w£*ëZ× MJ•71˜Ø¼¬E5lry‚ïriQE:îXëÄ|~AyËlZæÆSéŸÎÉÜ¡áÿ®p¯f®ý* v}£¨îº «_v’˜*2/ç>ù2Ë„}üDôˆqâCPÞ ºsE ì½Ût(î nýjGåäž‚_  Oó»¾*œ@·ú©Ñ­þoÈu3¶faë5½ ej–Ì«°‡qp:øº‡Æ»Ëva$"ªl7d×å&À’(ЫpâµUÀßòËÙª@'ÜœަɰM Ö”H*f=NΣ|¬ ¬ãùöúôgÅ=?SˆºÛš• VCtø/,æaÆÄ tÑm¶Ë©–hqéÔä“;#Ë‹óœÎÔÅs]zõ \äJµÖJ3³)Ñh_sM¿¤b'ûÁUçucÒå\Ñ8cº×3VþÞJÛ €EÙrh42fÂò£œæ~èÏýjs7d?hßaX~ÅÚ.àã'²ú–ÝŸDõSÝ`Ì×'œôR}š–«B#~ âÊß_ˆÅÓpºéQ†;¬„Z†ô˜(ÒY[¼Ù åŽbAX ý¹1Ÿ ×FÝAZ¹°fT'ƒEõx¶û4ݹ+Š®2öçK%R_8L·F°MV“ˆƒh=¯(H §0ƒXf[yœFn?GcÍÄY…·X–«à¿jµGî/QGý¹ÛlʼÂKz`pm;ˈ a{^ÒJ°„oï߀Az(¨X?Ç5I÷`YCÿïºtxñÍ‘®úrµªwá;ûXè×_pÈMžUÝÆú Ú°{¹>â!q’*Iû¯ƒ­¶*ìÞéN§7XÒ„@G¸~NyÌÅÍïâX‡Žë´Ä˜uËɾsKs^_‘qüIºtç£Jp¸tÞ)ŸšvxFÒš¿óË¡_Z¬v®Â·sª`ÚÚ,nŠÃGœí(7ØÐËœX®¼mȨOqàû#gX?Ä›sEaÓÂ!ËèÛ|žW[mÄ*zŸÃ"åR´?ªÛ«”i°¼&ĵ=à²×Á™^Ç9šÉ?òf¾ƒ‚4ô ¡•RÙŠ7ą̂è‘0¼·4rOðX̪©lá/YvæZ°M¾¡ð;ø2'œ»0Unü} øk¬1tg [”_@ïk½ð?Ëm š†%Ðg#Ke¥9¹“­ðÙÈù?\#?‡“/½„ÏSþÂOØÈ’ÍXœ‡$Þ4·cË p ¤%fóÙ©Ot`ÎسOþÚ,Æ}‡€º'XÂ=Oñz6{¸Ñÿl/ÒœÏj´aõ™thZk!¹}”?ç8{¢Šü÷Úì¡tT¼XL÷Þ—³ïðÛB{|õ—EÝaª~g t;µ{ò‰—8á[BRÿd¦_q¨HëÂ!cE–Ý]Dªƒ‚a1¾Åi_?c˜wj?(€²3µ|g·-0(âå3,˜ä»,Lq›¯vÂ÷‹¨uò6¦/l†õ‡6àƒ™ÓÑQän¿%APuð^;/B\¯¬ˆÆ úð¯Ëþœ'#‡¯â*×ETßú/qZ çì^‚Û]Q0Úðo1q¼Ë{¸:êê€fX¼Å°Ãb4ÝOÛ½ÈÙiFô¿çÚkÏyM"yÈ&‹³\GoÜ´aóap°nšz=r=Ìòß ’`I×%ÌZ¿öL¾…úéxn»·Ëv.˜wà—´®æ†sGÈîß9«Ô8#P º›ìmמs„{}ñÏ:Ñ'ƒ?÷"fL0A™iYÌOWƒö7´’s–ž8#}.p8¯Ôš¸ê„bߨD ¦ CAò ºû÷fnKàkžî¦K3i?>oõe —»pîwy&û'Ï<9£l0ÚdGL°Xrˆÿf×yl´t§ejoà4YbcÐtÜL(6åêluÈ¥/ïȲ%‹`áöïè÷¼í«sÆKv£ãµ"”œw˜=áÌhÕI5(}\…»æ²…nšøeö ÒÖ=„›Ö¿BÁµ‚—üÔqqü)˜Ì¥bmü†~m |nLOÍ•¤6î-´p$“7·u ÕëžGq|µÿ_7­§µ³^óE¿GÐʲ/8!°{v5ÓKÑ'Ù—WÔæ|ßʬnÚâ¹6ÈìxÃë©> ‡µ–°¥æÇhÒÌ«ô•¨<]K{þPvqÌ) 1²ë‰ì—õonï(ÔDü‚5á§Ù‘…3ùŸDÓ £2G:ñUÌf\'BÜ`é¾L’¶Ö ÞO“bÏd/@Óþx\=  3ì(¤ü”H×ië LUr.n0y†WwD“V©­4`ƒŽú¤ÿ︾‹5npg/¦ÑT±5Ômx2;8ç:þÉ4‚æ¹´·N––VýÅŠ¨Kðð¥*—Ö¬B%íæÀ‡Ò]´£)‘„,܈·wZ°-ŠÇ°d@™mزCá ¯­j~»˜EìþGê÷±;²žL¿êN*Óbwû0ÄhêÃ2X°Ø—Æ·†á«€dªVÙ÷Oœ)oiæfÞi€KS_àýä3@¢o‘¨c1"Y˜ï2õ.þ–ÙNõú“¹¯½¬¤î6˘$Î6ß¶¤íþÇ ¦q"7ä¬CãÝwq*¡kè›xkû;ªYûú æ¯ ãÿhÅš¡bºJq.¾N#ñ²_!î6¢éË%¸qy šð^âÖÇx#!{w¾nìãsW%l¹k?q6Ü‚ýR1&æHãKý8{Å0Y¦ÄksÏÁCËlxYÓµèãê(Ø¿ú¸Æ5%ølãY˜ü‚—^btà¿ú-%}ð ÙŸ|\iJW¹ÛÕ Ž¥v1Ÿ }µ }úž—–_¼•˜ç}c¾X'6©p':é.¡SYÞó\n–À}œhAÕ%†` 7‚<­æ¼¿\åÚR©UóKâ`,Ë”Œ°p§0öµô£ìï²Ês‰T×]è \Ìâ<¯ÒöÏÜ^naþP‹¥sw+0ÕO!Á_'/}‰ÚA7‰…L)|Q ÇõFk©ö$cð)ýÇw¾§Ü\sr6-é$‹’LéAnÍ®ã0sÀŽøÉ=ÿòµ×X£F—œ1ºc>“TüIzBž‚vÔL:pˆWþ¦Ãå1à|öDx ª¶Úôõ&G(Éý;ÃWÐMzÙ@åÁzå&¼âÈvCë~1Øá“¨ÿP£j©©¤¤#5>zÛJu¹E³=y3÷Ã:µjâðS‘-ÈXL·}=çâ؃’yðDÇ*lõè~\?Œ"xRöô¾ªµšwžiµ“ÍŠ‹˜ð¾Ÿ0ÛKÖmðdßÎ@žHܽeBâ3`»³5[#²*Î,Ékzùnkη½ÄÕÌÎæý¶L`†P¨ÒiíAhy›‰K™À7Wlx•ËÄNã䫇áD~F“Ã6> ü™ ’w˜õ¢¯Ä’S¢þ/³é÷±Kñh-Ypü18>ò&ÖráëIöVщn_Tƒoßöqµ2åÿÏEžúv‘¾1ž+í"Ÿs>>éPy!]ó£·®Cèm²«ÂšûšQg¸Ë>þ±"Ú}¬žleUÐ×âŠÇ<Å nDŸjuF€ûó(Rø7¶ÜŸb]èw™IÔzÁ Øk ‡µ=P3j2gèÕsÕŸá`ÃkÜ$nÈT6B›«+Œ§' “ŠëÊ vQŽÝsn„&ó/˜(õ Œrþ`aëºß£šÈ~P óû'‘ss¡r¨¬ÿg ê?'QÛ BTÏê1vlÚ wÆÃ ë.Ì]=Å\˜]ø ÐM5‡r®6¬¶ád–…ã¹¹#œèB¾ô×Nâ(¹ ~‡á:üo¡z¾+×Qn‡Ê“ã@1b6 ua丅jÖ\êÜMô¿”¤äðáü"æ&s* ^ଷ¬@.–œmƒ‰óN£Ao4˜Qêôã"Äuq—Š L]—ØjÇ_ï¶ß¾ῳùøýÀ´æó¸e9±(´óîˆz…øA— k¨Ò—ëÆ±œì;ؙΒöBövh z;óàÕÊKdLc,HHgò¾äÏH‚&ÂøQ,+àõ¯M˜ØJ'ö-å%dJÍ„WG{ µîB™öDx9‹Ð[êÔrr-¸ÜD§N]úãè;Ð) §»GµÀ 2³}„{‘7–®]ûÊGׇ¬Ó‡5Ó¯A]ËGn…¶-tl¥ßGÏXÌ Ígƒ(Cß[ª‰âðŸ±&ìútÄDRø»3€Ð§F^ÃýR®|³^˜¸¶{b0Þ 3¤5 Ÿ±Õ›êlå=¶ðe·:x¾Ô¸ ‹Ð­³ ÖŸœÝÑh–½~8¡‡zFà³µ tC¬°` ·"þ2fŽÁ¡dé 3þˆúÅgïc¿›ò™ÁæÅônÖ ôñ¢–oÎ@z†µö$TÎÄηÑ—}Ãnšâü°a¸¦ù˜±´9udt­±sm öC*ü—W¸Y¾?iŽ×.Ê©•#.‰âÃÑá|îWT.(L°„GÛeÈÏ®Vx©,ÃdÆÜáNŸŒÀ€ $x?ö ¼ø>éNbê'â1pi þþ ”˜Ž&û%°™› ù/¿ůo‰aA ìYÇõG6­Zv<£ž‘•»a–×iNîWÇö£mV$–êàÄLÈÿöÞ’æÞ‰1§MWˆ\‚3ÿ}¡­ùÄs>nÀ…zõpWeôIÒ†Ø#;»ë0Û¬1,a±&û{w (^ËëVüU›µùO l§O5– e½®ÿpLJ2¦Sæàí)ì“êK¸#1—Ízß) ÁÿÓ(v/Ê=ë¤áÉý?¾y4vè²ÆºpÜ@r¡WPˆ6¿i§…8¸BÂnžáb±ŸØýЇS&¡\ª ›., «”ºÑ\},xv‚?öà|¦—MïÚy­Û °ùã\ÎÕ@¯z/ +%¨¿h€™ô$'¸™Ø¸¦Ìþƒ]Sëqì A¦â±’¨ȱB@âôÃ^pm§TXžƒm¸4Jêcƒ~*Lªà¶]’§ÝóÍé¶z2K}$߃U§6\ðRÍ—Å%½«ùþà PUº{î½$o<‡GAòÿ½K»m[ˆÈüyèµâä¼}ûÌ´‚æ cû*üܤòôK^ðyk²Ñ°ÖŒ{}^-bÓ ˜Â¥@lU¸ŒÞåð_~õëéðäæNx7ìòáƒq®1rd‚‚ ”:ï™ý°ke,m©ûˆŠëgƒ†×Y4¼äÌ}zövób9ãç aÊÏa"ú`/N4“´¤>–ãŽ|*'ò0ªç,Øíù ¢j¸_KÑ1ì-ÿÎ]ªq1æ8ámƒ\Ì<>x‹ªÛ];?„0!¶õ·<ë3Vf©+kAy4ÆO “BÝôï<œ# †Z0­©v¦Ð”Ÿ›°ëÞðšwÏ—àž92¬ô6Û&>™n>äa¥ŸgòñAL3žÂ>kðÔÇVXö9™\ ÐþGgMÕ÷ÅqCæyÊ!R2f(ûû$ŠRQšKJ£&É<'sf*C!IxwŸRJ¨¤LiP"Ò(Òø÷û[ë­gwï9Û»çìýý®õYÝ®"D<ÿ¾E~ÏYø~6d½ïFQÓZüõ©샫Ñdö ç[òÓˆ”î r|i"Ì>¼nL%3Ƚôl³Çr²é|âƒGÌ1áÛ5În\‹hzÀˆÄ_§QO&ÏåÙH8#P #ðõgæ8[j¹’në†wÝ—P<&b5)ÿÈe¶mÈ–Ž«UBë°= ˆ­dÎIPõ\1Ðß.ê×\àuJ8]p6$ÅÆÁ)w6ö¼›Iø>âÔåÙŒ §°j¶Žæý`}qÂä™°éu Ó0? ¦.wb…sªp' :FK3·¨„¾:|M¦ý?ñš½9À½‘¥™d}Ñjã±5€þÞ<²_øÎhQC!¹)œ•.€Ó^m¢ÏrbÉfCi4y.…ÓrqÎúfp‹ŒD'!úÍ Î~ I3¨A8ÓðÝ–Ê8Oæ{*ú×›JoØ‚RŸûÙ½'\HðtK¢/þ ¾½Èfü3d,+ª·‘¬ÓFa²wÅ{ÛÅkÉ´µÂ4¤H üÖ%²Ƭ¨ÚQ.³ã©n}‚=gJ´»oO(Z%\â¸ýB-þq³‹y‰hà*ýEÿð]û0£ Ý‚²‚i¦4†rWŒ` "OÂ`}Ìf㦧x¼úÆD&sv_—E }ÄËß°ôß|fS‡öéò´ ‚q–Æ|²úçašÓÈòo-‡º^øÍ~om“ù¨Ò!$9WQDV•îV[Ľ¼šÕ¨Pq“nHÊÈcåÏXQ¹G£`• ÅL„Tà\ù›Ø›ñÏN£íº8Ca›9eYí¸9\‘ê}¼Ëz¹F …éúË ÐØÎ`„.•Ý­Õ¡!,7à ]ù€e%!`»J–9·çŸÅ¦É:ÎqÝCõôˆR’(ùxh:¼:,NB=aüÝ0Rü _S{W1€-ÛçÁ.ü¡†¹Nüô«Þ¸¿ß‹Ý°Løñk°ÃçˆÍ´2(NífïŒ~f‚_ò‘ð»€7\®¾ªÅ›´¶CŽèUjü±Ϋ\F¶ý ðþZOf[ËyéËÜh¶òt+l†hЪ >´d%éû$ÆsÆÈ}fß6Oæ:öèCê_„C6¦0»>n¶XÒ†Õ‡(7bMW Iv3 MtDïúP̪¼ƒÓRwsßdS^ æ@U>¤Ë’R/.õÖ¬†ú[àÔ+NeÏ}Å\%vþ¦MpZA›ÌÖ¼®#K±íø1zg¯F†Úm ö'4H™ã34Ó¯EY÷nXwŽŸ.Þ÷±ëŸô>¶iôæ!xÿ6 xqG•®ÂLǯlÀµ¼VÅʤqª&9©A Ö‹—?bœ_…V$è•.eŽ'2 ‡¨€üöGÈRl9öëëÁ€X*+Jl¢ËݱÖWŸý#4ƒ~Ín€¹¿Nqä÷wÃ)_î†Ö"ôškGÓ=·Á¥eÚä—ø\®èZœ£±Ïl!Òì,Úµn˜mþ%¶謶o¬Ò¡*è[û>lŽâÊþ V¶W®!–úðoÙ^ú>3V ³ 3„žÒ‰¤lIm( {™£›´®d)dWµBžm$‘=åÃršÌ„Þ(¾þ·€î ‘¡oj•IÂúldÊß\ˆÇ}ÖAÜõm0r}½² f>€\ERg_fù±¥j]ÐôSK’dèê«âÄïR ÜÙQ%ãè#¼ ̵.B[îKÆÊoñ ’æàŒÜôߨë3Œk¿jÐ;‰±äÆÆÔNFýaGj–cEޏRæÛ—b\ðhIuÏÀák™°cî~X{`mϹÍþ:ÿ î“$öB䯛a˜éõ "åî1¡d¤<Ô½VÅù|,˜}Ð!‰'… ß5 <¿GQ¥Oêèô¾Ú§úû´‰Æ¡2N 0eõLŒ"ðåÝuØ6Ó˜¾`÷°‡¢’Y·%i(ó´ˆnûsÎUA®¤‰Ø›+~îA>ÚÅ8PóåÕLׯ¤ŽÎ¸uŽ4¹ó{æ^Ó#ïF¡M ‚8ðÚ¢xg W.p ì)L‚ðê9¨!¶…-#°h•»Á¼†¤ås~ñÏþ;¹ði^2í™þ |ƒðíu ¢:ÇŠ~¼oóÖjÓŸzJÔõöE²Öpã­ƒq[ò0óíý%²Q"š4ä±°„Y››ÎXÌvñ¢k˜àÿjCFþ+üÇQÖ1… MÖÌ—Ÿø‡Ó­g—PÙ“÷16 Y™†¹¶÷±ó‡~/3¢ÁB·ÇC”σ ¤>Ùœ.{µ¥«v’¹}Ôö¸Guð4††< ‡×<`up;ÏmGWM¢yRžòl~ÍN«¹€ì :0'‹øDm&âî¯8ó¬h­…Ù;Ì;WÓNAi2½. çXN£¶ŽçiÓÌ4*¶©šÛÜIÌÈ_fŠ‚Û´»Ã&µ¹¬¬•ë»N¶ïS' cµP¿ÜÚ6@ª^ÙyG„°Ò§ðæÎ_ìÛ©º“cüt: .ŸÊhüÀµ&(0Ý™¤ï߃AI¤åÍ ¦ôÈlRv) ›o~Âß½{‰Æ”t²cKý§w0×™ÜèR‹ë]È_AЊ¦˜•JO?…ûu;¸·ï^‰©¸<Ï›ÄÚ{bð³`°LÂŽí‰ÐwrÝ1¬NÌíò±S—…¿áÎ^\fŸ‰ÅJo Ñ“´Þ¨ÒãßÂp`bˆ©}—‹ó­âA{ÿ¸¤<âšÉöãåÅ^ýΑ.Ši‚;ŠäГ`Öa3ÁUª®ÔógØ^ÎÀÂt,˜ÿÇzÌiؽcà¼w;ÈF¬ƒM†áX°V‚îÍ„È3šdö¨žK£_âàvGfðx“þí¼ÿÅÇ$§Åà"Å+œ½ý0¾ŽÌ.Õ¤vÏ$ ÂC‹´KÊP~ãý$yÇöq Á®Øåð×}lI(T¿Wóþq,C‰’Zöç,Dθ¸¿¨+vˆdÊ}E醤¯Lv¬pvà}‡l˜wãço4ý8.‡ÿ‰pjBÏY]¦iF9çµtèä~VÜ[v¶ü*µxyÙ"h'ù|´¾&•ñÝ6{\ˆ¾cÖ9•fŽ‹›­r ¸jOn’¥÷”˜–YÚ–±Žš]ßEnÉ…cSÓt”~,§·§3­ÏÖ±Ku¸p}»'È7Ú- &…ªxÍ}9œ²Ô¦§­‹ð n#ãBÓ¹¿ªR ªúLŸ|[V SŸýôî·ë°Ú°†IŸ¡M¯`úùŸ˜äLnyÍÁУ ÉÈú—ŠQ69Lœ‚$ý“+ŒglpÎ)VÈíµYJÇ>¢ð*'8˜ m>Í;—U°üÑâÄ:ó=lÊ bB â}%bçvÕáŒãâíH®Ïõ£w@K[™^ ^:ùŒÿ‚ò݃“טÒ'u~D K“~•ŒÀ?ìWyrô×Cü}n3É|^wB4‰ªŠ †‹Û<¼°Å†îìZIÔ»¸M ãjÞóÿÍI„d’Ù•¾Šhvy&iÚñ nŠÝÄ;—éË]Û0~Q%œl1å.ñW¹wÇ)Gþ<¹§äÂÌßô ´ØR¡NuÃo†‘¿È^ý%ÆÙþ W¸>ü„=¶þ) ´®¦‹¹oseIµg?ÌŽ¾ÈDUX’gÀÙ|‚«–øMnÕ€ÿE¸éÚÏÑ:9Š‚ÇßcùW ÚýÅ•$5cìYy’sb_HúЫo»ÐeÓj§Êª¯” 3JØœ¥dé³…4uƒ<*D›Ñ¡È*Ȭçƒßü/ u˺À’e¥ß2+¹ ¡lÇ=6®,†áx&CžS;*žÿ Üä.Hiý‚Ú†2äuû/îó¾NÔ ÛÏ8Ô7qæî´Ç­§9D²] Ä~>‡S‘T÷9Ku^2uÚØyKè!Ù½¤\u+Úû=UyoàTÎ ‰ÕÉ ÿÕ´•Ö€ŸŒ|üEÏqMÉöºyàÂSÈå_š…¡MÝh¿X&þ5b˜³`}q±:‚–álâløæ6é“ 8$ù´y9(ïi XŽ3O‡ZakF×%1hýîæ/ÚJù‡oÀÔŠD²°cùÖz*٪ٳaY€ ßÿÈîIxξþñ ²ƒ/b੃`Õ4vmeç«å°ûÅd-_¬ù¦Ð‹| DêA ý/æ}r™)öù 0{½/ Ã"§ŒW”3¹¤«@ã2_0m­¸}I7š­ß›.¿€~]^îµP3bsXù7àˆl –%ËÐß…ì?Khøi¯oúA¿‰!Ò»úÔÿ½ Ù.( 'œoccáO¶}»x-H  oš#œ9YÀüÇV¿ºêL$S4‰¬[+¦7§Â©È)ä#´+%¾xƒ’ÇN*ñn½­¦0éKבWÛ®s{ÈÃc·ÝxiïA0³“ UÜ›ÓúÐ4; Ý#ñšæð×ç§-5Áìø£ xýجn(’ h©çIæÌ´K|Ë<óídò­×cM×.”ÑËÆÉuÑˬRoÖâÕè$ü—邦ñ°æað £ñp€=R]]7ãÅàls%{ØÃ‚>x§€ò«!ÿ¦ v us×`Øæý,GÍdE+§£‚°~!ˆ°ú0v@žpÍtÑñ½¹í¬‰«˜©)…³üˆÒC>Òöþ,£fÌP Í?“qšôò­F,{Þ_ÂF·OØ ÂG¤µã±s0.}‚ò¿¡Øà*r´cQ/Vš¨­ÓßU÷à»æ>zÜ\ýÕ2Š® HÆ£¤ü…?lx\µLaácÎ%X}£ž¸OÃ}KÂIÉ‹ÈöØ ¬¯ 1‘W„«vhôÑn2w&6Ž3IÑŠç]CÜ"¹Z0“z+žBdž*0‘?Ï•SÕÅ´D~ª6'œN“¡w4©ç öD²=@rS>ÂXÌ:ÖÂËyYí:4ò›)¯ŸÛƒ48+OwÁçóŰoô493M޼3•!ÒaäY'ÃD,òWã› ÒI:“¢Èš¹j$:ÿ.õp³@Šñ9D-6«g¾Å/7„IJœ =A{aàÓ8ÌÞjŽ ¿bzŸN#ëtg®KçPø L$o:ÀrÃýDhª#s Ò kÉ^÷‹À\º +u'`xª®\-K$]–’—³ýáÜîDŠ_œüxsÖ”‰¹˽€¥º=Ñ´zP$räbå°û=ƒúß®ÄO¡\ì±×…/ñq°4€|Œr—ÅHÖá@šóè"Ìû®MaóØ[ó“zPº']IV^I£â¦‡¨í0‡¤6¥ 8·:_¯zS×Þg3vÞ‰Þ>vßmrãÜ|ºcüžù®Küìcþc¦Aïô,š"Oˇ½ˆS‘14_+£­é p©Ù sHÓñô‡Õ}×—âší世­-U#–Ê­ìÅ &è!ºaC‘¿¬B60¶ç¨×è8ù ’ÊçÕ[æ¬ê¹J"=È$Ý{Ù598-öòdN‰ÂšNW²Qö>£*«F#aÂшlZðuK¬§ÝàÉSYÆíêj·6~}Ž/›ÃðUïftÊFuXtnO¥Ü¤å¬úxÛØ”ˆÜÊ´(º“3:¸§®EÊžÚñ=e„W(ƒ!3víºìʳΛ σŽS¿„¼=Q 7¾óSÓLaz²t}¥ÃCøt7°¸Á®ßK™æhÜ¿7v¸-"­Á†ì³-ÿðvÙr¾ÛŠÙ¢¢BÄü:áÓÐ"¢\»èJ-¢+ªM~»$0ÇO bNÕëé³¨ï´¤Þ 1Ì%|"ËÀr/.øÄʱg¿l¡{mÖ᫚?°óëô#ÀΑb §’yGØ¡k´~|V"ÌÝß„©¿³7¾ÕãJ“ ‡+ZdI³ÛHŠjD­aõøÃÖ7J¨¯£Dî\÷|áQC3¬©Qaçî€Ã‚•µãné¤:F ¼ûo³{¿‰aË#\.`HnèÌ‚Æp2X¾Š:Цçs@âñ4RçÐÌúIÝâˆõþÂ]I¹ÏYÔÇ*ØMõL„Ÿ{È^¥vñ«Hõ¹6éj=#.,ÇpryH¡ÁTfÀ¼Š]ׂ$x£?½xú>NUï™äß…8†¶Ê WÔrrPê%¦0ÓÀ[yöÞD£¢ub.=Uñ­zàÉŒË8êÇÃèH̄иTA~ erQ¾ußYúää½tÑí0Èz¼†Òš°å2gfóRÎÕíä@ÛWØ(÷”ÆöÃ*YÖ!D˜æ§€ÐQº*Ê]±o6<MÕƒp{!*+Éåï8ת`ÐTŠì±ÝÅH´6aì; "©nCzõuh|¸69œ`B=Þ•°<ïŽOZ«öªú{η+ßA‹ûßx—ÂîèÅЩ˜@5‹ímÛ‚Rp§§>ØL§+¼Â äHuÐiÊw÷!~Üí…’†5þÄ—Þ3Ü ×`-ÚÛi°7ä¶Á E#j©ý;͈oº* ­#û=‡i¯Á[¼okI+•WÒ5Fë¨Ñ#cr©È¶½of›E>‚Ùâól—ÁWpœAu×»’iQLþîn’•PȘd  Ï™³P¨ŸokCúMÂÝLýMVW¹ñt;¡yl4fÆ„²ŽREtîo,ÛMåŒYçÂ.â庑އÀ'cº3^pé_éóLé·BïOñçt¸©lEäêËHÓþ6vü€ÿ¼5Òí…'/•‹Õ´ÿöbrÚ6˜ø†‡‘;þã¸ôœ.²ŸÎåKj§í‡»àʾOä$ç9—³­Ÿ^e.½½ £Éxg]9H÷É–uË@dó^\žr=P÷L Saî¶//ƒÅrQ±Åóß,$úÙPàô÷§h1À…DË”]åH¦\†i:^àJ¿CaÄ/X-%fNA¤‡oÑIKÝ3äŠK̶³G*r„¦ Œ÷*"Ç«[ƒth«¦CÆX&Ìë09Õò½„`üø(Ìfðåt^Ô`ª§r…–t¶ŽT2ÄögÀÙŽÖ ¨7˹Ïgì‚ðÒ8xÃÞdŠB¶ÑÓ]¦ _°$;󷉿ÎcîÒ¯2ø½¢‘›ÄçN´ƒ,+Q¢¾¹îó[0cuX»‚bì ÇàX~¬(g?™êÓ€b±õØ•¡†°#ö9þùD–p͈n²4i;4‹Jm¹ {âyið¾ïÌ+Ïóø×Ü¢ÚhÒû9J+Þ¾d®õ­£6õòÄù‡ìº«Æø¶…“)v|L§“Ï#Bäzê=ä©á§Í<³á¡÷9ÈX-B»¯Š]°!þÛL¨H÷#ÖX(þ~0ÌÞã^Ç‚/ÂàÏ#‡žE¢A¹%æ¦Û] Yò/öT[àÅ¥? êÎuöåD!ôìæ¥RÞÑà…!ìS#d¬'÷Î^ÁH,+ª‡m·ûàç˜Õç«BÎŽ9dÑûw°÷Å ÖöæeŒŒxÌ,ÈhfT%áéßB¬ù+º£p{UœÚ,‚v¬£’o^ ytfnofgá «;î¥e \ò—]ôK‘žYx ŽÍºË,óS§U;ï Za…èNÂT' ˆíe˜²h€ù´,‚‰JøÊñ: µw`XÞ‰ÔÂ÷ôIøLª~ìDîLaï `È×#d8a –Úh£gÚ£úç1ÇèRåð áóõç<ú#âÌ;²“×´÷ã-–q…)ûžA¢ŸæÝ‚ŽYŽdŒ× ygÏ¡ª²c쯪f´õ¸ÆÎò”¢Ë¿†ÁluÓ9ԻÆ6¬ÏÆáõb.^ö‡ÂdeÊßêÁ–näô` gÙ›§àþäÜ®…æ +ÖcŸX#d•]ÉÔxw“ÐèËDÞ Zt¶z=Ö’ˆÏ3¨÷1s@¤‰^iw‡[Õ&ãoÇê°FvÉ-zýQ pqRSj1q$èxÚÿLÇÚW‹qáÂRØ`{&ÇØeZÆäs„4‘šQHmµÖ°œG™øuôÎúýçB)6íÍ#á[a9Ñ¡DfÚÛb˜’KÛì‹1úÆÚâWKÝ]îáÑ™äè¦Yd×µVz»çm$ l/§“ë‚+Jøý²I,{Œ¼èä'El}ÎéS̪IUœCÉÒ[f4²Ð7¶¦ÁÕG:qd |îI'n¥•GLh±™™k©AF’…È×ÌÕ8úχT§FQ-Ac:jH_{þ¢1ÙsÈp\ žÈNÄáj€œŽ¾k éÕ½Ðâ…+:æÔCø-^R¢LL{¦‘l×>6*K‘||?‡ô´_$¯~²0’(D ØõPâ«Mç© u>\VªÎ„«'{zóÙT¹]yya9ëSÎ!—<ŒPtÝj²pP†ø&¾`ïx7à†ð'»H—̾˜z™¤â´ 5±½‚oo ШŸ ÀDiЉ‹+˜ÿ¸j¡×ô*‹îù3'Ž¥àŒø î ê½ñ¼Ù"ǔƦRç‹…cNn>…w:hè~Ÿ™ûIJMäP®  ¬eTèŒß±¢X‰äU9“W÷-¹Z|Ü$1vú%²ð„?”=y„ïWuãƒÒÓlSZ3ž›³ CJ ¨Rw ëß-Kr›—‚,ï4¡¤NÊ‘DᑵÜN ×mbÛn2›x3êVÏrw‘^Ö8hªP«£Et ˜Žü­øDîn>I{Slè²” 뀉DC×™¤Gû[ãFl¾í${¦n ·_fãzLÉ1¸Â?¸ b·€ËÜ“ sö7kkMÅõ‘ë–D2kNñž¢=íȉÏ'Àg"Ÿ>¼Š{–C:ÓÉ®}Ö«m þã¥ÙdmsÚa»³~MLžUsðæ} +`>ãÈÔž=L×í!Ê|b`õ\þÝÁ΂xßPÄ·¼äŽº¬%Ü:1EkU”³¯_qÙë‚S1îKJ-ÛJ߸ÑçKÎÙ6؆’U¼• {ªŒË¿c>Ô&5ëoBï?FÝöl7ZMÍ„¢JBI³2ý €›¦3ÃO1ñ¹\­Î EI3áWÕzÜæJÌoÃÅ——r$D¥ËÃϪºtó†½øâ¬ŠMU#j—¤Î³ÅA³7ŒüÝFƒÌè·ñï°TMž³¾|ík§Ç7ÎE;JbÒo3^Ï‹Añ"—ˆOØr-5Ö2»ŒÀô7ÛèìÒôT›JŽOÝ…Wìº@öŠ9§m@þN]ISunCŠÑfØ'„¬ IÄßî£ÁOÕ?ªšiуׂ1}Út¼=›(Jçþò9m ­jw&Ï £Û@™¿¹/£!ábY˜_k:‡ñ‹²½äK÷ÌüŒÚ žôöâ(ÆkµÞäúÉèÊù€:K”à¯ó`ámC{Z*!`È ÷5à}á"zdG"\<1r.’€¾\Vsò¬ÊͶ‡åcElÜãtΣEâýüæîŸÅä{à<$'zñ³»1?™{õ>@ù-ùl…æ\ðâ$âòk^Dy³ viœ G'÷ïÑ|ú)§G}à ±#ðaüF}ƒÛKœ³Üˆ–™ˆ¿ ¶ŒôÇ:Üw ¿F’$>Å9}l>¹s`3+Ÿ–¢¹ìX4*Ï<ÎTfö×M<ŒÉsÈaÏ ÌoH§A‡žsæÎÔ'ÛÞ(Ðâ$˜ÿò ÈOã£aißùo`cfJîWÚÂúÿþÿð´:Xu?Œ¾™7‹ôŽ`ññYн_œ¤æCAœÀ¤&I&E5A¸%<¥j’ÉË!:h>€>#ذ¼tÿT@$ywü©îjñú„íàHìt’ææÏ Ú¦ q àêEæÌ½†÷­5h.è±zŒX/B,.„âY“•äøý™ìË>"?ÖÅg'&oÅü1Àüv?ÃÊÜFƒWð—ÞA8¼Ò,jyr™žÇ"…øä¦6´±#I“¹Cê\&Þœ?ÌõÍ|…M}ìÕ>ôêàEøØ-ŒdJ¤dEÀEÞp/U¦Ýï¡QêYÔ¯½Œ¦ÐÞ3èɹ ØÿÄlæ¢ Î+ðéãͳ‚lÖT†ÁEÒU@NÈ3ç saª­!=(± .m>‹gFwSJÞ¼vl…™'e _þ±CY†x¡[“þšÚÎuJ;ø¢Ô5êž’¯Ä OãUð4LaϬ†]qm€>^ €Å)O`fÁRî>‡\[ÿ_¼|ˈÏþŽ7¼ìÉ̸$`9f\øÍð–<Çg©lf}!Z°?\<Œÿ¾ìgÛšº@ ë/~sêa#µ2ÛÖbWhñ½›±Ì,ˆ´?[ÿïV7 iúqÕ8NÆÁiü'ùÚ&Œv„™ë{ˆ¬µüÊ$þó‰íc¨óE›*md u|Ég±D\ƈڴ?D¹rTõ|Fe%0Ò£:ø7?3+%hÇI Ø0ʼ5íÀÏßöBzŽmŠŽYúˆ¦çý¿ßò5M'?oFnª\ÆVÑ3òÓŒ)Ï0ÁúìÅ-!{ @sña8ª½Ò¯w3L‘ò›Ô3Û›Bq™M#¾Z> Â6<Æ7wã±Ñçšù`o}6Ç^#艭 eìV«Àµ: uŒ>–_–&]%?XÓ¦Aؾ†‡ä­þÀ|ÝŠkrî°§^ ³O6Ž0K·aå]` "ð«’7¤”>eƒgåƒÉD,¸âÎ\_ýÍ‚ÖWünᚌ”ÃÚB¶û×¹ÿ^0EEÞVÙÀgÅÕ–±=ø0gϨÀÛÞ¶Ûö_ewÜ„¶3ØEÙïáËçlLQþÅ®hÁ‰BÐ9÷Ug¿¹.lèÌI4?žTÈ4CV…€Ùz V9Y'‡Ì9.nÛ™J)ºÀö"ªÞgD• bDöºYSß—ÙB°–´ER »Ž>À–¼fçKÎH—O‡±ª~>Qÿj‚G/aëç^ø[=Y_ž`çøcŒ ñ‡ÚA1Æö@£¿;ï.ØK—Ÿ»‹SË¿ÁÌý hXþ]ò'ÎT-†Ø›n œ2À|€9z !àW/ç+ì y1lÎ/UM'Á·nrExã`ÞÅóܜѸP"‰§<ÓXçûñxù³ ›l܇^÷Êpƒ`*Wãí\¸œÖ1™¿¦bÀ·^x?±€ìù% Ke]IšØ%PГ“,2¯Ö’"Ãðhd ô·Ê‚ËÃ`hVM©7O9Ÿ³fÒ7µütÖe1›¸rßqþ´óÒÕÖäò‚ëpƒf·}„bKØ?O…iÀV‚ÿ-%ñÇØpmôéÁ?'ÊYSzæY>˾V¡%sz±ÿÎ~ZVíƒàYj?zŠn#¡sÛaÿlE"ëSŒ5f³ÉÁw»ÈÅZ0`YÄ™öX“æÙ]d›xУqap|›»¹ñ$™ãv”˜Ë﨟¶Ø•¶jgB¶Ø"R›µn<¢›Wï„UÚÊôjk+ç° \y¸{R`ûùaÈ}7iyi‚ܪp(%?Îq™}Šp.ÕˆìlÇWÉŸkåSô‡€ Yœ´‰Ï):>sw”â‹“QØèêLÂéÅûþ̸TÅ e. ‘¶ÒôñŸ( AöxîÖ?ürô|ð„æÈítîæp)‡¸7B"šÎ,+‡ ±·Ì«Aü<å5nÉÄËÒ»Ù…‚â(û­µ_s9CÔèl›å”kšÍ͸ïù¼yÌ€¶'(\¤aY“ñ?hÁª±:±¬ ÙŒç7LeÝ4…AŪŸ©’&SÕðòùzäWä|yv _’$©þ¾$Ü6Ò£ ×A7ç,h§8ƒUÉno‚mYtþè{ðk݃§_$áèz!?tžÜüxžNô߀®±xdÎ ¥Ti}0Cÿ ž…[ƒ¡ø:'d‘ˆ–°XtZÜuÀO§ë¡ëú)b¹¯„µÿ™À˜)=¶Í‰ú3[1ãnœÞ?Þð‚SÈ`¼_:&~(`·m@ëä ºùú>ò»r ûªÿA·ùBÒÖûmiÆÔV¦ç=›æº.xªG‡•‰˜ëšWKQÓ‰it»l,ˆÝðÇ ²ªtΦtŸ]; ”¸óCÏ mÉ Wº]€áñàÁq(ô5¥ÕïBB<Âõ“dOv .³hdšo?D× •(þEް/‰ˆÎrJ¢Š%/0×C‰1‰Kîmù¿;qÛù^vñ eæÉ`.ª•“Á— œ¼1ÌÆ6þë¸uÁ-6U8›õ;X‰©löÜpöê¼Éïú°:åÎ÷X¶ž¦P­|ôÀöð ø‰2R›9‰jÕ¨4sKû)úŠãŸ‰ÓT൧B0ãÇLúÉC¤#Û ik8sìá*±Èó2‚w%Èè¾>Øøh Œ ì°Sð3¨zò¢[y Î?EzõÖå,#*)vŒ|š’D²<µÉæšØC]8»ò.üs Ã21Ì MsdÌ#6Ë !ÞlQ–þ ¯dÃÆÃðY‚©¸¿NgcÑ–8œö›^xÑK Y÷÷}¸}c LÂ!‰:,^â Aí}ýXŒ¦Ž_CO èû³¿Êõ¸»ÕáÀU´]’‰'ss¸Ÿ†Ò™æfyRqSš?­E+kš[IôŸ@î1{bòŸÞÈ­Ä"]j¶F¶úDáþ³IÃŽ" ?6… Ÿ Åm)Üð´EìûT ]ô‘=ѦÄÌú¾ ÚUƒ`5ßì]fÏ„65àDïT\µv;ú碎ä<:ûŠ œÿÝ îØçŠñÄu=zIß‘hÞ ¡g ÙüÍ@2¶•¤ÀßWŠ.’Êg+MmP´P V|Hä~ŽûÇZޱ¢ÝAø×ú2kÔЀÖ÷p Þ”Aß×l8t „¹x-~Ê¿ÅP™_Xrd.¶©Î còŽp–5õ‡G½WàuïR²O©7U©‰ÖòSà&PØ>Ûô&ªÿo7Dl¢-Ÿ¬ 7Á…Z^uGõj#¢°t ÚL3­­Ù u4U.áÁ˜.pøê‚YÁ4éÀmÔÒ}¾O%™±]l2µÅ§Ïs+¾ñ°"5ÅX¢-Y/+ÍåŸAå´ú˜°“¬K_°#ðà=? /KEW·|d*•ébr69à­*vÚš<4c c‘è½°üC9µÿñŽýÚ«~Ä2—o Ø©Ûйé/öì]O†|ºq¼ö ºÅL…¶© ™ŒßVe†<¨EÍê¸$þÿŸ¿X¤Šóš>²õ`Mò¯ÀÜÔeÜÔg£œôô(8râ>ò¾iÆÄa0òY–κâ‡>t9òeÔ¡zÏ}f¶Ž>ÕQ’¢èÁFò»ÂÐýB4S_Bçõ†0-æ_A5ÁzsÞ°[ª¢ÈÞ£'ð²ž"Y}w2ú¹¡1Òô÷ðyèøZÎ<^pìU¯á²BGšË bï—ÎÂŽÜvØaÚ îV&à{ï\}–AÍû1Â^ºúÏ?‡íüÚäݰÞm}ÑÇÕéÉŽ4¾Ð†,.ØŠlO¬ÿtÔÓØÌDÖQþ"±€Ø5·u𵉸¾/*%ŠgõZá}ÞLŒ¨ÞÇL;_Ì•LnaåRª1î{2ª¹”˜ôñÓüùW1(ž—,ÿ›ͯ7¢ßÖ“ôá™íl¤Ä_á<_eI×z”‘&WÄù mè¡íDX ŽlM ûV PÙïD*ù XÊgÒÉää^d§W[ÒÎK†“nà?콿…†ÝßJ×X’‰ÐŒ}Bw¶‡GM^Z3GŸ~å™Nwâ ¡PBn*ÑÏNsÝÂ}pž¨+»Ïо4]Ik—eZ†ÃH²?é3\Å)ÜŶ9I‰vd€¿·×HÓ»¦ H¯KŒ¯½*?À w§â–C/@ôT â4°"þµä_Y! ‰ÚNk2´Yq¥R°›µ‡–ÞC—KrÌàn,X ×¾ÏÛy<Ärú#hͳ¦‚/uÐrú*’×tŸþ×ú•¨ Éxrˆ¾ÖÒ¼µwPœÆý‰ÎnÇœ`à¶|/ü—éÃçãè¶H‘شdz.…ÀÖºšŽÍ"J2²·Î÷ב¹jÜÖž¤{A)µÞ=?jÃàì.'jØ×@r²øèÜO|ϵ‚$õ´[¹Z¢sè²mÖé!¤-ýÓ,áã¶½xh˜]?M.ZC*Ò¹°d£={°;÷ZO×û•C«i€¾(Ñy~Ÿ}pâ™ÝÕ Sr­è¼¾HVH#–ÜVžGC¦U«ý3‡Y;JT‘¸±÷\&¿ üªò ¤-Æ`äª-f%3ë?HM±”soòl Ç4°½ŽÊteb!ìÖ¹‹Œu˜ãºR4>¦?=؉‘sö _ˆQµLÆ–­¢¯¸ ^ª­e¿’t<|oý2M—ìá_IOe‚U [—’ÉðõÕÃ_Îz~ŒãñÔ ž®„}ÖÆŒC÷5|Q$N+}ú`[Í®.þnpEv ÉU’ùåWÉL:ëŸ#¤Èh¬¹†¹m Àb´tŽ Ð{Û@y"$× Ó!uúúº,ÜôR&a"?ЧX§ž‘ÁÀúz0ÄÀ}þÔ¡¯¾sâáçRq”i6¤OÅ»`—Ü>p:¿y—O—«þ…8¿q-ýqô;ǪπüDLoã¬eNL¥¼0Ö, ùûÓX÷šQ(~é}Ýé¬òPï.h|È„Ìr¦m³*O²¼' ª:rl÷̦ç¯Ä#_n6˜<· šsÎÓÁv¸¾á8 ïÛ É5““zÞ„6bÏ ?"Ö¥D·n] £=äõ#]úi*Ûª“@#$É1Éxó‰?þ×7ºåÊü`ù6ßôÀa)Òã M4ž!CU²`=©«ï' ã…ŸUöpüÁù aªh ±IøÊ®uÎBËîH"#—E¼bÁ«2¿|¼+ºK?õWƒ‰n3<(¦Fzn”½ÍÈŒvq¿½’ÂáÁ™4øÂ¤8ÚZ«,Ù)Áp&£½7ßìŸ+ÅH·pÑtRƒø÷"¼_N¯öž§†ço1—¾1ª~ºÔÔMŽ®ûŽ­· éåÌ|0[?©[rñýK"wí&sKhu¯w¢mØAôÙ”©Svc"VØÒ#Š|t½ ;¾k–¤Í3éÕÝ£˜Ú¯F—õ¥’?ZÞðÑ|ä)#ŸÞNƒ•2Ä4<‚ô¹‚¹¢¾\C’ÓÞ2™ñÍœk±/P2à| =O ¼€¯)¡tüŽœò› 3%ï°I ~€¶O ظiÐäڭУeŽ`ŇgŒäï:vIæ^sJ$‰Mì ^<RÀHNf­ÏgãŽÉZ‘b€¯¶Åýyy öç¦3†Û˜µ¾FìŒ/º¯i9ULxCy”ßâK1zâ‰+qÝT {‡c ÂfÝ,$Cî¹(Aµ¦Ù~H“qø„/tP›¶X®øŒÕY´Ü=6—Ö6ñâ9m3<3s>‰òVñizúf@ÑÊxšÖ²‚8m3¥;¨ô3QJ.¤ ?õÚ€Á#ÏÈuã5dÿlá¸}aÞÎuf;5È9£l|š*H ´æãׂ›Xõ¨ø ¯ÀÃÃÊtË4Öi_#ûËÈ•~°v7qï1ûzªaîšëÐÖgJáQ« ™»ð5»aè/¬¸Ä¡¿f*Щ[B0¶Â„|œzã÷: ÝvˆàÍÇ}è¯÷ÞL·Æ—gº¾¾l‚BµFtëyÜå«Kï_ªôw± {¶G˜6eæá¦k §)}éĵ»¾aYȶ.Y–HËž$¢‰ÿõ}æ®ë:Nw+ž!÷ì=‰K/på6ºÑ°]ÐÊûÕ\!0ÈÓTBqÌ[ªu¦Ð‚Ó'àÏ\®ŸñZʈL¥W‹01lg÷ë(\££KjŸ0&·>2Ssw’ë»äÉp¿/‘¸‰­m™ø¨i ™q€¡ÖY±gRÒpñÝ7à΃Mÿ"Ðõ·}[Â׿ÌÈ«}òäÖ_Ð@$ãß›0¦eŽ…Æå¬÷Õ¤ýA0Ü뙼ƒØÇGÿëÍÌpÈ—Ã4yJ-3Þ—OMúÚ•ÇÉ{?*;À¬˜ÃÐrþ`Sþ`\–-WÙåü´ä;F§÷ ¶—‘—÷#‹\¶!!zkæAZ{û üiv‡­ë…éîº|Z» ßmc¿Ô¼Ák.ƒBh-v×l'Ü9‘½cª«ÌPò«.9¤1­®Ü€þý¸]ö8™u›MŸ)®)³Hêú—ÀsÈ‘úùº3JY“u!Þ #¾À©i¦ÇIm¤!ràÀ¥³g c,§G=¨Ó39Ædc-{ø˜ÓbLbxÇØƒ~4ùc5½Þ| ¹W<0öê%2EÿcW7ƒ.úr—ÑGû¢t4¯ã?%CÖØ=ÜßõWÏ’†Ý-LÔ˜4u}¨Lª¬áèÇÏ>’3w¯¢ž‰ñ¶jq¡`x5FfÐXœFãÅñ¿ù¡õ’y af‹˜.{BÄ”9~ÆXX% ns”èê8š,MÆcHa·QºmFZo?ÆŸEÜ%zq0«‚ekØ«·ÌÑ!þ"òyhêïÅ>+¶ ûöüªqã,A³²>>Bt°=Ë|üNɇ†}äÂŒ«T¦c3òw”SÞô&vÈKhAé}ªÔ{‘9h›Á¶›=‚Ë«ÆáÖŸ|Z/þŽöºESe…[ø—÷6S˜ßL5hÈY)¢[ROê øÅo¦GUÒ©ØöY¶Ô•Ož®r;aªDŸçQ ¿©[u—[¶´€¶Ö ÒË®Bô[²3I‹úŒg´ˆÉÛV}ú–|c31áØy¼è’­÷;0Yïè¨L¡jWy‰GQ 1P¥:Û•¸°!’ìy¬ƒÓ+éþ¹kéÙ£= ÏÔNùÎP­Ÿ³1uù5h<_çúÓiÓ®™(±¢6Ê:0aÑxêñ[h^â íꦘ®´„ÌÛ à)›C­*±–>|¥mýĸt-ÅörªÒc‚rG•Ðó|'bìm¼Ó…ß ÙláÜúþÑr$‡¾õSC™èŒ¦%ž!$ôæoPyuŽj¾4¢RI¨ßT^gÝ7.=F)§Íôm–7;}à=}¡¿ž‘Œ`þiѦ|àÆ /of ûâ8Ú4•Î<y_*Ñ$¤ ÄÈþ'çi‚‡„4ÝÆÃññT)E†8FvDµŸý#¢ƒ© ÿ1ž¾YÄ¥*b•lÈåî]PôëúŒ´àZûXaöRÒ²Ü|W§a„[Uûõ„sNû ÞX2ê¿é§ŒLÏî|VvdIR&ñÄ•ÈÒãѰsi±¿f'š6Ðäê —¬#3ƒá® t¿w‰±äbÓÚ˜‘¸Ž[´gs™Ý~LãûpØóC‘úž¾ [¯ŠÐm ‹aÓ­B¶úv ùähvwå¨QŸ1lL[ Y7l.QÜý²¾¯ÄA“$ »î[{º7!s©™¹®OUýè‹PëoGe?›â£çàÐ!ªu¥’T d¨ÂTCêö˘.íº‰3’œà|#7ƒœ}A‰ù&¸¨0¤ˆš‘òQ°=±‡ú¾½É^Û +ŒÉÇÎäÝ…¥Ä¸x>=bÞîwܱIʆ>ß~4WÕA…p(õÞÄæ¨i16¥0x´„é?èOŸšJÌwdqÛ¸ªÄzÕ!¶ k^=›×†ãsxÀùáQغk&½¬·‰Î¨M ó3V“££WIÍ‚423¡Çvù‘ÛŽ 01¿»ZˆHÍ1‘#¯½Ýñ˜@,6Ú>@+wo”àì ·"wѼÄ”Òh!~gèf3™+܉%’¸8¯Œ»Uѵš)ãÀ/0…>L8Lmö·¢´F(i'ZÓÂò´Ñä+}܇—°/j#éùLZi‘EøŽfqªŸì'>íBä)‡¤µ®÷ÉdÏ^ "ÜdNæË™s"dúÍrºpF"M¬= ¬p/½Ž/?n#Jfñ fõòþv“%BŸ8ŠÚ ˆ^¢Ÿ)‘Ãé?˜P—]¸·i åQ§ÿñÂQ;ñÔÒz´)œÔϘ¯-Ctdq"ËŸvŒNˆõ3Ïjô©û¢£T7Þ–\±–¢I w üR„æ¼aŠKˆavÓ7!#×¹ltU4'C[šïåÔM¥Wôáo¥¥8ík¼[\HGK_²â}Œçëvå€0­²c][¢à‰£°­]é"BºïÀ©w›©»Ù&ºŒñgå æÒm®ÇPóTýP»›¬–~ÂâÁ$1©oÌ“È' ‚^yB=¿ ¦Q0x}ð|Æñ18¬.JŽí4"iª[aa¤( ç'×,õð¸p x´Ö3wâhuí¤SÖ'¿ã Øë²Û©÷ƒåTGú*[;îŠ&§ÈDãrÒ)•ÀŽ|Q¤_ÕäIÞŽ@6*K˜L~‹OÁ`ÃyØf£)•ŠôúþMôñŽI}"PH¼ƒî=mÒcL5—‘¥ÕèÛUAV›Qé6c<¹ˆN¨ª“mžD©RhʲY?燮åÄö{+nøÇKsu=Jvv¡ B¯§±ƒÝðÅŸŽñÂ9=!à§k™µû»¡Ô鮪hãÄâáué¶ò?­hÂÔG\™ÕËnw/c¾Ê„Hd>iŽ(;\"DŒ¾eâ/þtœÿP‘žh(ÀñL4×®‡Ôž…0}£*£ì¼•ü[£I—¿ {_lb£y¯°}JldÛ°ß`Ž·Ö/dBž³[lÕ¹‚õh–$†lœ̯‹`VôÆcÄlwt³°¦YÏZáÐû×Pv¾ ,ÂNÒѧÒÈošskg‹¨ËãÌKe.Ä­%o|eþÏ_ó§ Òåº×1séNò4g¬˜<óŠÃÍÈ}»*u0WP† YdpcÎÞ㌹Кw•xTxFt=ÇåYûéÙõݬ”j3Èö‰ãƒ† ØÑhχy`ʾµ8%ÏŽª”ù“pÿ%vLj*WF SsÜr¼ ‹*º‘7¬ÉÆÉB’ÌJÔe”…jàÊž÷ìJ)r öãk½—vþÒÃùš‰pfývš¿,˜Ì.Ç~·FhõOÆîUsé¬5÷qÁä~mz< <48$Õ²w*ŒàÎá§Œ§€=î+: —¶Ç9pkù4l-2 QÊqäÑ´‰úsÚ±¥ýÓÕHøëÏ ¾Jn&Œq’–‘:vçŸó£=Ÿ„”óÁêïÆ¨z/ÏY‡‚ª¨›÷(ᆣòÀ^Z×eI2ÏÓæ*3:áëLݫҨÄsòþ` ý¢ØŠ•âºt8í¹¹á6®÷d·ÜÍ ËK­(1ê­ÿ‚|Q\ñÖy‡‘KÂ?õ ùù‰Óý)äþRÎ%œêlŽ×V^fg·Ì×ÍdþhS”Á2Ñ{Ó/…(´ë v&†‘ò‚ ¤}ƪ{)“5»’L92ƒÞöôœCkqO6VÜ„œ0ëÃ0·`•–:‰ü{CéÜ TrPA€®7] U k&÷¨aŒžCQÜfl¼>¬½l@1¶Zn+‘—‡WÒuß…ÐìàAzwµ ¼P$†C-0¢ÎG“†ÀÆF’ f¨BLÛsvÚ¸+þ²Uv“.Ãl¶â›ý¹¡‘%£5 d%ì? :V:Æx®ÂêÛÔì¬?“i@í¦l%¾)ú$íÊyšÜû®4èÓQygÊNzÁ†Á5 ïž”äׂ™ÿRÉŒ5trŒ˜Îe=ÃHêc®­>çXÿT!"5[ñ…Oý0¦HŸY5²Þ µ‘‘ œͳެ  ò¿ôN!}Ý“>@Ïžœ¼MÅ_†ÇÎj¤=û5ìþO ÷á}¥(æšcÅÍ…L9Veãç¬}ðzç]˜¹ÆNŽÃmåû¸z%¾óB¡—$ÆJ“I›#I½§RêŽÝ¥«DpÝu¨œò ì{ÊñºY%:}ÌÇ…¦ÓÊÕߨ¦*t¥XøÈÑ‘½|Ta< 6%AL¸&Ü|} WU±• êíÄ ºòF`Á³¶ëR3^Ï´ëŸ!áÎ\DGW\è1Ÿä7ýÁÚ¸4îÐEØõpM< ›…I´­®HžžBi£<ÿ´ú¿þÀÌ Hˉ¥ô¸óDý¿‚' 4{-"Ë+¨õ úîö|-gÚt2cwáãøÉZ?s²Ÿ3 "$3º›)ÐRb_Ε…Ónõ•³xãe9¨¤@⥣9ç5Ë@ý©zû…ùÀÉD¥¼û¸cF'üY3•ºþR!†Eu¬eµ3ï{$“ èMcLÙ#5ïØÈ@5Z—u‡ý´Ý–Zj\:Ÿ\;_ÏêBû²väfÞCïžà£j‡n㽚ýª½à®ßW¶SÉŸx^Á’Û?™iþ~PqI $iKê<«×ê³ø!Ù2ƒ>u”UœÚ _”ÕèŠrI²fµ<}W†'íýñ¦Ít;]g£Œ“AÝ`&f5×BÛéo`÷i(Ñ{%Qliµ$T=Û‰m'ÄÙ¦GRs¦>«Îƒ’Ÿ)À,ðeæä?וñÈ×HïI“ÿ¬ýŒÝ¬Ï–1\÷2 gƒ~êÔd‹N.$ëBY' ºaÎsX.q~¬¦ÿ³I7`¦ oa…;oÀ¸y-®’Vckõ³yã&Ôâ Yd@>Á佸éý;æD Õ› §£ñçLæà9îîÚÉšõ-ˆ4_ÚÄxú­¦»cqjþQ*…G¯Ç zßž8õ‰ñ‰¿ ÷2 ]nsþKäFƒÒã"6Rä# ¦M‡)eD(ä'.÷¢ÿÅ´mx¼8òpä½6yÍ7À|ÒM…|ËL°w ]¶b)Wñðf2ÍÝ .ÆaË;z™÷ÈWŸ§üŽ© BöÌŠÇÀg}…÷ÑBÝ’ÌÔÙI;ÔÕˆâôýtc?—í>¨Dmg×ãŒw‹©¢Ó'ø`!Lîù8¡øŽîH)rjw7èx?«g]ƒÎ–¥¨» ŸÝ9懼Aó˜5«ŸãÄCüî4å0ÁêÔ:&˜¢U˜Éù ÛçÚÑW»åá—Þ¤†“ëƒÅÚ(°î þ×»ùÈËt4²ÄeÜ+OÈ ™,je>Çqïw(”ÿÌû§‘AW-rÕØ/¨ÞŪ_ILº}+s» ™þaµ|„°w0—±kÎÇÚ•šš‰›±&ŒS\Á˜”´ÂSIizâÄkäv=Äß×Ã#Gp‚?voûjsö¶Vջݕ‘´sf&äO¡kn>¾ÒÙH[2’:þ4üz {“—ŸV)æÁÉ žôpR6Ž»H0«¸bxLÖ]ùÒ€ ÈV¦3¯—Â<D÷ÝIò2a ñê {w<`U/h¡HÄF˜Å>ÅY3À>z´„¸¢Á븀Iãã× oté‹xÑ#L?ô°Ó=ë¿ó•°Qq³Pg½)Óõzù`ÜÄ^Ü5„‹?è¡Æ&°ù*#õ›—\}×ïÝNcFZ)þiÔècOë |UYó]w1JµÝ6ç`iÌH[u»mÆÌ‘GP.Nî>ý‚Qa]œLsL=Š;ÎA—÷†ìg©tÍó_l¤ÿ—zÓ†(äÐgø{ ¯§ÀÙŠ™pÎÙZî`×q–Ô p´z5h¶U±XÆy”n†Ãú†Lß’}мðsç¨ ¬öü›[¢ÙûBz¨ÝæŽùióÐïx:ÞÕYu[†ï&ããeAÕɸÕ!†V­žKÜ·Ÿ;ƒŒËûRNÕŒýÿ­Íün‹@ÙÊ$8sˆÔúKááp2ßU‰jÇ>DzÐóT±ü [œµ~½#Àéù=ŸÍ‹iª7”KÆ»¶HDÔ‰¶l=rç%‘ûêþDc”¡ît7”o!©×Çë|v¸wÔ«å0Ú>¡þ¨g_`h¥$y?ØËiçqý¯/4¦ie‚±Xn22%;]HPßvT4xŽù­Áàd“÷–°#Ö±ËR™þ¸Û…J…ëȶ¿êô›ëeX—4ã|¨Ñu1Ú#Â…ì[8ÅQwaF<ùui!Y>[œúG ÁÇÎÜMÑ£Q&‚;Ækp/ß fçf²ÞoµHë­?콬GßCø×2V¹à~ÙDzWïç°²š¢ôG¬1^ Ý­¼Äæù[YåÊhËÏÄé“ÚáÉŒo¼Éæn’$2Uªtñ®÷Pûî8ÙwWˆ^•øO’ϱVQ6æZXû×`Éð~ÈøéÓ¢cØ/[àSs'¤,7¶•¢¸•øäùs§bÜ»IœQN¹ “kqxt™ò2Ôúa<ËžÔbÏŸy޵w㸗©Xüˆi½¶ Dï1žTø“!Øqï!” Bñ&KFþU(wŠä56(KšX¦£Âã$Ô›?oèkžÉŸˆ’"PšV†÷œ` Û Ââài椈‚»òo@ç·ìN÷…'œ6øe¨@#3¢Yý)£Àûó â_ ¦ð˜;ø• ;yÛâR!˜/æá›é”yWÐñ,/ ƒôË!faw6~©FŒS¾³+¹€{Ö{¶’ ¼µh³Á“íÒ: ˸?Íp¶­.dã/Oãþ³ýËéÑR$×¼Àð”0f¼ìSÙ‚Æ[„@}Ùðñ½‹”sO]ÚÇÎë@iUÒ¼})wI{ª,µÀÐ7/Ù‡S¯àÑB| š"Í%ÀdÆ­çòpšÐG|o¯^¡Xi+ô>ס ÿÀ†§VàWpÂÄ`—êè­Là¤ugílw1Å—60mnÛ~²Ìa’e•ñÀ c8ដ©Yöм/»f@Ë >ó8/kÂÝsn80u'f͵ǥ¡ß°ã­¹·T€¶Ïå.QKeÚn °|þ‹a§Ñ˜–Ÿ [N0Ù³¶7”4émÂþ廡@þ säAf=üÎc*1Ö3•À^&Jã IÀß×ÌY'>J%¤Èh¡<¹‘º·Ø¦Â­ ePÖ…—‰ÿ|xQá¯Óûqò~PÊÅœÔYTjj%ž¶aÈ¥þFXi>zÆÄ'ý/Ð.ƒs$TΞٛ»Ågoc›#…È©÷âŒígPÎ>…Mª˜eâa Y ¼ò»Óßáà·—0¸p1º!B•,¡Kä‰%²§àtô¹ÁÁŸ&x¦ohm±¢ZÕ à´.…¯7„iÕkœ»ç6Œœ1C®i&,êŽd–¤ßFû'7 Rò“ýÊ‘Î_q'gíD>BŸ 0hI/„ï‡>±NÃà‘ø‘sùs¨­•€-#ÂcB‹¥+ñx–‘-_Möéñ“G°I ˜Ù13—-üW†éÔŽÏLÚ'fï¶&¬;ŒÑjÎ4CU™VùÓ·çÑ}zA˜q4 ó£yÁdM-ìáÑ¡Å;a™Z‹÷ÅÙ¤kbÿ£ž§ cÅmlZ{‚>(Bø=)ª¤ò ^‰VâÚ«-°õkÓq'KO‰Ñca¬{ôjò)±Œÿ~c¿JGéAôÏ–dðëbVŒ)ãb¯YÌ?¢íFôjà¸Ù4²\Y…è=,ææ¸Í£ßNÆC}h'|ý2ļŒ%û©}0÷4q ð`+«?±²gµq›Ó FA|*qyÙÎþÇIóÑ“Ì"©K87TØÿ܇}_™Û¡ÿØqu(”×Cã·òäâ<-ºó‹´û§³®ç3™U²¤Dá-{§oÙ¼QˆnÐÞÀöjÆã¥IP$9ÜmÛ¼€䆹½à'^Š;ºâpÛwÀÊýëpù»@xw€ž8ÞÄZUÔ8OM뾊ƻ¦áÍÕ½ ZsÇ·×ãÎu¢¸sê7¸r¯IDUÁdVÕâ–Dfx3VQ±Ò$ùújX' sVÔ1“ó–Õ®ôÒúLÎʇ'¹ÞïïqöiÓÐsþôµ{œšÔWƒ2Jœu÷ÒÑÁ°”ýq>ù·…½X"ˆKÄèï]YÜÇ…óˆCŒWäÜ !÷c%¹/G¦@v¨/ëâqoèµ¢PÎ-t¿¥f2í=:tÝi)H1!‘¾âÿ±ÜLcH3ÕÔpíSiš™ÙÀúîÒ¢3’5©æÊO¸dÉl8¼†ùY'êf2ëæHùš`â¡–¶u9Á~ÅC }µ¡÷—Š&³žæ³É­{Ò¨ÑYÓWæàùxGò£Uü ã~jù†µßÁäœÜ¼™aèt"Ïßœ ço&³æ›ƒ›-Š4à[ö)&0ÝÙœµˆÝú¸XSƒ¦…·c_G>kªY—EÂó×ì¶Z9Øò–‹9—ÈÉ@òôõeüq—ŸžOóžÜÛ¥0PÉ ƒÔíÇÔ{Èö´óa„1<Ü?“3³m/“½}Ö P׃·¸[1“ïpÐÆ™½ö4 ß'փݿ¹˜±f]’W‚bWnÁª úxÝ0ý¤«ŒËì&GÀëÞ8“³¾3áyPÞ:º¸KÃÜt]”:Œ²Ú ]iÎó6´ØPÕ2¡˜Þ=²²à1lJ§wýây²Íq:*SÊdz“_ÛŠÏæ¡ñŒßøt£o Ób6õzÿÇ;Ã’æjëÊöÊE¢¹Ã{^[ ñ¶¢Ä%ê2³>ï!óÚÀ„¾0w@­1ìÄÓ=`¬ÒŒAÂ|ôVí·úö†,LúðœRÓ͆ÏM`8gÚ¸*ÙÌìî…à,#€RñÂäÉE´GwÄËN£±<¥Ì _QLõ¢×î¤À{\»Ž·ûÂSúW­…åø,DÚRÉÔ´½¯wÓÀÁÕex'h³þòBò1  fë2ªÃB8ú8‰üÇ@ß<þ’]5ï,Ó~Š#•ÈàÂlºÌH7|'e¿[óט·gŸ0b2ÓèßõA¤ÀyS´à(-¢ }t “1ÆäÝ›G¶È_Àý=Pö¼ùIç°Ê±ÃP[…ecÈ–;!ܱ…kKµÙ¥A8~ñ.Öþ–!†~üÔt ¤?™RýƒP”c€ÖGk8ÿŠ1ïÄ{nÂÝMÀžPÿIÈ„h5„‘ëÇ8Ÿ3(V ê è™¹ ‡¿E~±á†­°ð·,y¬NöÇÑå‚V0%#‡èm6Ào+nÀ ¿)uñØð7¯—ÌcŒŸÎ呅争= ЭUH÷|ðÅ1Eš¶¨oî c­ò°ð½(óçiÞø*G*G&pÁîvôöÚH#buàÑtEÊ÷U‰üó?KšO¹á¨ã’ÿsÑ5ÎË fâ<ÚWɶŠ=‡ÏËIp~)6&¸Ó¿éq0ÓQ†F=ÙC/Z~Ç»J'Y×yÈX.‹ž|…€Ô'ð9XøVûYSºŠiÄÝ(.žÙÅý$¸H:ïQ¥=™_Xå…ì«é›¨Ð3òxÁY\V!ʦ:6â~~Iú´ÈΟ7FÿaY[“‚×îO%5Ž!xwäŠÒÌM‘dVÌÃÝ¡!h?ÆÁ!ÐpææêG7.zƒ Qãf)³/o½¸J”ìq™¬‚ö¨ãùniL§¿ãéÕ¨6¼ùƒ°¾÷¤l»>3¹ä Œ‰>@ç¶i(Ä3ŽA >h»GOŠ.‚aÞéTÛߊ|ëo~þ=XìUaZg`Þ3 Or7²!£0í|1çù9Û©Gyæ¬ËÂ1˜µë5®‘öÂëåÀ’›à/Z wî±—‹Ù Gy`³ývÆôÅ*Ö+Sœ‰M ‰¦$³²¶I4²cNC˜¤äF=ÆŸsÓ&=ÖŸgV˜i^Ç4ë ãÞ…Òœ6ÅËpLÁZ4üi‹ñ+xë| ßô,£åsçË_¾0¾sydÒ g7}fä«oŸú^f)ûø’òàŠ#y`é k®ËU !Ñ»!ö±öþ«¿7ÚŠÝŽ\\—ÔD‡ãYÃ{;)ÝS_§¯`· d3ÿå°=à;›-#[Ρ=ÏEj¿ò+Ú×Pšt ½¬RèRR{2¤HlG+º®ëÆi¦é“×J‘Äl¢?©…ŽºPý»GÈÏøo°¤‰‡†>¿Hw:ßdôoד@¿\ô›Å)NêDfòlï’r¦•Aâdô¡“àBŸ6úÐp·­´(ºŠÞØ¹Š„OÏñui`÷#ƒþÇ5ºò“›‘½xàH.å"F¼›KzðÕÒDiu*>/ùÄô»‘„uϰ¥þ3.cŸ×ZŽQÝgŸØëõ4ñíZÈ8oL¶½D}©pEì,ßÇï×è]p A?¦¨YÌ¿Ÿôï&ë´{@кÆ"kà w/›·È‹nð«½Æ^’€pò\ø±ëY°i‡:ˆ½^DnýÞGem”@ÒÔƒxwùÄ Hø7/ Pü³›.È?J‚d/P#î ¢sL‹ .&ìßmäŽìNäŸ__W.¢ò«»¨œµyJÜLÎÎù•ÄÏ€gš˜úÝææ<Ç•60æ!·ñôÆÿ¸kfp¨f~³¡{Â8©¢rÛΖà½,ø°^„ôìAütKužß!‰X”BÔnäÃõ[mÌİ®–7œeÿV‚á(XDfÃ1‡Ð} ž#„"da±~L(ƒÃØ+ßbûYœuφ޿U‡Û£®ÐXïO ›Â¬ |‰Ž+ñq¡¬‰Jb\…‰–x M|[{ù ØkZŒ°º½¼K:ÇX‚Õ-„eU éãì'Ð|Z”òþBÙô4Ö{wFÌxßOÝb¯Ô°]‰™ûªÖ„'ÞßôKQ‘Î)ëL Ü#‰Â–Ñí·Â°Û&GÎaIßV¸%÷ö=¦Wsø©ÌñFPYçï‹·‘ðW~pîW~ ˜å%.dAµ´Uwc¦ÙjXúo ¾?V¥Ñûo2³̉ˆÞux½Ð÷i ï=È|‘/û³yè©ói§~h{C“°íθ¼â WÑ8¶k˄ĥ¨ùÐߥJôójqwz4órP&–(ÒØ—ܼÏe(Ôwì’Ñv¿7(EÕÔ 5Y`¾z-ˆŸv`dŠ ¸yk#‘7^#ü”igVšųíÿcœÙYKÈâÌl¼×æÝáÑlMý{¦øq Öí?ËçldF­ fÓyHz~?;›RÙ0Wøsû4wìeùi²íÝŸ?ðãØžýœ³ãa{8=pyÙ"Ž]ã}ΣFä`ÙJõ–3ê•HÏ>Ò&K‡0€“îÞßpùŸZ4³|KîÏD¥Wx/€-ÊVd›Ïݳõj}ŠcadŸýKžg F×­¢J`º5«’:;AÚÓnÇM ¹‹¾âûArÚ,&¹«Ÿ¯†—»j©íPs¤‰ñžµžN) #Yo’ÀÔù6Œ:àIöûô±$¥K H/M-Ÿ¼¿ywpŒ}ø¯“Þ±‹¸vo‚íÑ[IÅ­””Ï ý“x£º„]#L›“NExÌ}áüË$˦bu:/^LýzŸœIn§ý÷/›øŠ‹Nêš~ÜmVŒükÚE¦æµþ\5Êt\æÛ¢×Ìâµ6äR)üý K ƒú¾Ù¶ÙGÛqΫè®R ÔD9ê'\å3!ïC¹Áët‰)ygˆZ(·%§¶¡TˆÔÝCÓŠdl( ï9¡8ûCͤ÷ BÏ;s¨®íI8>aK¼Ug;ßų}ÖÙÒ“¾ß˜ÇFz-ǵ áúxWÑg?óéäµÌ˜ÉXN­Ã¯µÞéÕœ¸MËGÅ!E‡0OÿqçÿíA¾¶PÖº¦_}qÓ‰‰},J‹ßĈ‹è H8´õ8}š3Ÿx¥Y(ƒí˜¾ :ܽ—„ß8øc¡ô[D¬Ÿ€®EÔàe+©ÔºËF˜äB£Mÿc kK²ve|¤jï¼%H׿ê“iyþljZë›G–î?Á>µ0apë~úcò¹þœðÃÞxs"}a ½yÆV«‹ƒÎQ1ìéÓ6ñÒú§!Ø9WÔû~g;„«@íéGæUð&ê%¿ŒYQ²…pÂâÙü¢.Ôÿ'FŽnûÍ)pÍ00³;ƒ+[à½ÁE4ßÿ5=€^½Šgâò ’G•Ît`+n•­³ÁëkûîG0›ØnBžð&BÜ>8Ö u¯ÍɧŸß!C;&ç§Î+»¡ gß[L®‰¥1Gßo·lKFûbðdžoe÷žN˜,ô$Z“E´ NMQÅ’À |þl,QY³¬2!C·‰¦«wÚtNK“CSyˆ^@È3Á5צӸè6˜õV„ùºc£Ê˜¿¿¾í$GŒgÀÝ3¯AYÿ'zkêW§ õ;С­ìì¼±÷o)㇠ßÕkÚ±”‡ð =Ãkg¾p£ Žw\<ÊÉØÃ-yjòC”ñ¾ÔÂ0¥—ÑŸ¨“e Ò¬†‘)ÝsBz_:Þ{)D÷0û;0yv,ÞÏÛM¶…)Ð=¦¼äžp–Ï x¼¬<(ÑÊýó¤ ÿZ¼ƒ\~)úÿöFµÜ ¨»PNNärDCn°àRÊx^¨ßuÓ©“›аù}ÓIp‰ x÷ïÃUoC¹ Ç¢!#ºšQɧŠ}A_á+Nýðx䑇éä_Ø„é*4pÆf»§Íã%J_´À¶F€~>¼žüð4#¦SAmÎLy>Lý®à©ªÀ¦œæ;w`ûGtV¢;úlÉÚž TÊ«ÁtžåzŒLëf¶f¸€çk²øÂ6"w6ødð_À]¨Ï _„nà¬gi¬u‚)釭ùø[M†®­0KEì¡àåxí\Üû{nS#3^Ï¢gOíÅcCM¬áüùdî Å[§°mSUÙw!õlÞƒP” =[Yøƒ«fÃdùâ¨uʰ8ôèÉ´qÛ¢`™íJz´ã"ÛµíÖ>„†–dfnŽ6Jž S×:0gb h˜‚ºí’„Ra¬häTÖvÛ1ê;·„Þ[6³åá‹Tܹ ןºB-þ¶1ÊÞÝЪX²áü¤‚jÐÓz? {ƒ榹‘ýGUé2z;R„4™Î£¢ôGÍCÔ 0$ºªçà%ÿR,ÜÅCÖX’s®AìÀ²,¶â€8QÝÁŒOgZ,¥H’o>}æø·žY€%«ZPí‡7!ç~†³ç'@ *Ç© H컃Î×$w`Ëžd*–wþ,ÔÀÿú\·ç©ÒAŸhªyã ¶ë»ò¤ÆY€XdŸ¢Å&ÛÈâ¾zvïNqjaÏ¡»ì¨”øq4/þ EGèÝ\rP.Ÿ/œAêK‘‘-Adéö#tv Å+î?1Ìö8pÖGÐÏí½ÌÖŠͦÝk†°íb5nwšÔ¸gêÙÂ'ÑÔöÏa¸¾U «æîÆÏÜ¿¸@¸‚é–'ící%DB¨AªóßãÑÕ°æÄa‚3ÒÈ £À0Ÿ+|C’Þ?·˜¬þù»Î\¦3žœ!/TMémþB¸z¤ w ïƒüÄõ`ìö¬žª My9 Û¦~cj—ó½ùÙ乃ʉxÇZfn§ªå×pÞÈ2’ÐøkÆüqc޹)¡ÿ.Gïê½ÔÓù kù³ŠH™;C`ÏRF0ã6ï|‰«"šJ>>ÜF9 éÈ¿UøÁ=#ú=œ¹_eN4ÇÒ¨,ÿk,q™M7O9M¦ï}ÞúÔƒy>Õð´6Ý–~[ôì@íá~ÖwÆOsq]óCH*žO÷F§  4àƒ0¬dƒ 2ûkf£á-^мΘkàñI­>¸·W?ôÓyìãíÑøòjÒ?ý.Pn'î%èÒÖt¹>üÀTü:ˆ{ç¤Û£ÎŽ=]gX;gß`}…Ï¢•U3¤ô‡™µUšŒîòbâöU \\[]ªÜa²²@Qÿ´¤ê9q'$ˆ«Õ}FôSH•¡à¤^˜iÌàwZÐ HÖ‰.Ý y”gÑV®°P ŠØeÁ¬í÷ð3“>á߇5+Ü1Àµ U=„él.ŒÏ·$Wd³%¤èxÕ<øR3©o0ƒýr”ºY¹Áæî¬àI²=!¼¿8Ðyî3pì1¬V)c\¼¬é˜– ×wq°³—QõƒMC;á«C¶Dµ°¦j¼ìñì1(z!AÒ–äÁ¥ĹR¨X» 4xRê*W³`Ö!N %éÀ’nÜ’=T9uÖGÂáøÆÃ¤ù×}VÐ:v)æ²_Ž3NʲT¤SÒ£}1` ?UêÄÜsfô€q-žtDU%éT”[Õ’ìµoè7Í$’Îa²’õ°! lÿ¡ÆZY÷å%$ß!•ìæ+&[Fîc¢K ÜÍ`žfÂŒõV¤¬6“”…1ÓÂ[¨Î`×ä^©¤3,z®mcŠEßá¼þ2š®¶…ÔL”Áª©ØßÛÅZeûஓ·XŸÈ(¸RŒŽÿÞ@ê?~µ~Ivx(œÎ®Â¢(EÌÝ.AÖÜœEäu}™E&g ïÙ hš=‹¬ƒ ÂDé’U/©ë xïGöLÛ@ò.ãJÙzˆ*´ e=‹G1z2­µWÜg”_¨Ã¥uñ0'Q^ì¼Þ3ÖQǸ*< x þº'ãÏÂÔ°À6Fð°?Kxɸm" ûò‰és Ma^pnÛŒ(èò¦Ô?})ÑݬÃ~·;nê×๯bÑ 2^èJÍ¿»»,›}í*òÔ-£ß:‰Œf5)Îág1É–+õñŠäúP“סT0G©ƒº¸Qõv`´c>£nö2b¾d9¾Ä…åSº '¶ËP5ÅsØþBŒîÚñ Ö†IPçïBÄêE½g†œwÀ+;’H|] iï¢ÙÐÏ+iKÞBóaXú\l—dÁࣚÿúðÑT\Z#K¢ÉdŒ¸à ùÂ߃ae1ôWÊ-t‹9 'oÃYÍáLø6 ¶u¾jÈ™Rýѵ¬]r/ë°µ´bíÀbùæôŒr¯áÐ\ y\þfIѽ+ë×jŠ@¤ÍJz¥1ƒp*C–u*uŒö½´#B™¦ŸÀ3T÷>paœç¼A k ­À²I´Â ©|Øâû-+ŸÍO§¬ö 7êÄé® ´-#Ñ{?àºãßph§;çã’%ø,ºmW¡A÷åè¶[,µ;V]ü†‡_ ŸðZ%1è„Ó/-Óè/Ñaw‘J¶míQ°e|ËkÙþŸaåÏýÐq^ŸDf˼Ÿ"Ôî0#î-Bô £MÏH±ë+ºðaH *Ó¦gOE‡÷5€;Â!aa€N;§2EvâÁ’sôÝ6e[I{ZË›Šwõàû0Or#Qvo!)ã”8 ÚšVðC|7&‹ÝÆÃ¥ôü9ª£.FÅÇÁ@†%Müƒ¹¿ S`*•›÷ »o=æ~ÕÇåQ“kv#89OjÍ<ª™Ì™ˆæÞ5¨q„zÏ‘„lûx|KÒîBL¨€<#ñF‰Xï)€1 ¬- æÂ„ †ìħ[ô±*Ö†¹öí9ŽÅï"Ú>_ðè 5º(9ò!T]ò&H–ƒÐ[72Zd€<¡=°Fø/;E9åVWwy³öš ±²F<æô}>nBí›XŸô„SùVœÝñ—ÁàÅ·°„>|ìªëÑL4t{ÁÝjy+ö~l[VCéëé¶€¤­ƒmjCÌc%Uš.q •+S%¡LÄ4)oP×~ ëA9gÚÿŠÁ'§»8ð7B½b ÷¦ ‘-ÎæŠ<Cõ‰;Ðpí # ¿miŸ çøÑ 9›½›s–¥çÙŸ~x$I™Ö¬+†×ËUiXÜ>Tnú ÇñaJgXß>o£¢pË+%ÊêIÁ>¯F,"€-×}Æ™c/C…Ãã«%C”]>ã¹­°Êo*éŒkDþ¿—AÄ!ÊFÃGÝçà„çFüºû7,nøÝ‹˜imTxáYXs8ˆÙcµÂë‡9Ýçm&Nàö{ÀÀQŠü{n xD˜d,® ÿãàºã±þ¾¸½“l™!Y Éxî¹*$¢"M•´4hXÉÊ–’Q(âùœ›BJ•QJ%í©åçûû㾞×óyî8Ÿ{ï9çý~½Þϳ }»Xâ¤.¸bg›§¡©G<3ÉÅÝÂìrðYLP “µRñÉ…+àžÁSq§üzú2E’ÎZ¼„ì:+F,+‰†Ï×ö£ÿõ§ÄÏ1Ã{*]9Až—}Iœ}(€÷íÆAïkxwHe¿Ysßoϧ†bˆY£2)]„¦¯‚øùFçðýçÓÐÝ å{íø'Ê`ºÇz\hΚQ¡f& “äŒ9ฌ\âØÛŽgæš2ÇC ÜÞëÀÝ´(â:äöÐq 4Ø­%çÐ6ï.»wJˆ Ù„0Aú:%V–îšXÌLXM—+¾'I¶½Ð0èÇì§3ø×.Ms<ŠqvïS\©@õd©üú§í>>Ѹ6ÖG¹Žmûñ€Óø5oû@JùFò}˜ü:\Ö—¡Ö5}ª—t™…^Ëõiúv«·³í4–mùƒ)Ã1Ìhè6Q÷ÓfÍ#YL“õ«>ó šæÓ¡µÜ7KG.%3„ý|:–›¯´ ·Þ…Æbì u)¼\ ‘Çš± ‘Ï(åŸØBç(¬éN@U„èrá‹°Ëxs¸éÊn¥M¢¥öWð¼U¼tÉ€ÐÝÔñËTâZÂ6n:l9nÝ)N¶}/Ž»U€²ñûéý>3´™wÎ3Ø…Æ©4â7ò×E‚sþyôyVˆïÅ<úôäÿ|ãz.ÀÕFžìàßê†;–v‡ø|$a:xC˜ío”gª¦Còàh,ỖǫÕpî³ßÐ?߈„Þ”`ŠÖr`ìîÆäæ˜3Ï>iÐêN†5³oâóãðžÛ<4™ƒ1òjLHLƒÿÙ'(ÄÜ'%'ðšiÉÄsŠÙ‚¿q+=iki3ÿà¶²£èæþð¡™§E9!‡ÏØG…¹½•¶øëùF;û6ÀçºEʼnҭ̽Àï®™G?Ì×"Q>ÓÙÄcx¾Y€“JÆ·CWàïÈ46ùp¨FÔ@ò)3¼ø(ÿ¤›Ò=7LéØmÿàyÂ?ÈèíÀb^&ÈØN ñéïy‰Ôñ×Ó£cCQ4~È Â¯+N¡\’û¼Ýl´7A[¬k¢D·=ZI`Š:¹IÒpUm²÷à?Ão Ï»Y §eeÁ€ýQ¸ÛpÏä ò÷ǽÏíŸ* íQõ8h› ¯;–³üzCt;W›«"1Â$²’¢Ö=0öØ„ã÷­a#_1WO‡ÙIiúùrŒœ=‚¢ïÊqfk?>þ»}h1÷æ,~¨É6Þ:«mh¼¼⤩iê0<=ÈKˆÚL•„íØó54\7—góoÂùŸrlÛ/aêÁÀé¯W<ޤN¬åæÏ}¦iñÕ×­ÔÅyc5a·é[\J–ñ†vO[‘Ÿyþa3Xø›—ñ) ¶ü0bͱM°é¼?.Ÿ¥…‘¿¬éÁcÀ…ߊ >#Éû­@?Ü b7îHÑY™cÙµW‰—°-^ø§qÚl}}§÷qÉ‚ T ,¬zÇÏK–§Áf Â믠w`*^§Â“)O]Ÿù‹ÿ†÷æ#$çj7ˆ¾¸‚m¾  MéÆÇ<å>½?N.Ì} ]±µ8nä1̜уki ý9D©ÉÒ‰ÔÍuœs&7”ÂÛk’l¨ïÒ¨ï¼Gó×óqp”sÇÜã–î²£ ¶žFçóRprëgà‡bZ/Çñ‡6Ò‡ðÝeÂEO9åB ÚÍŸÀê ôi©!e¿P#@Ÿëóš§þƵ³?ƒk“'­R²@<*}ÿ/f¬ïÆñ½Jtï*hßç)ô¥Ë .ó[ztúØ_°ô» Y®>꯿ ¥ù<Þn’ W+»aÿt䥒ï$è„ÕúôBâit(ǽš7ƒr¡“iÀ/Cl4 Æ÷_¶q—1ø¯‹uÎ#]Ÿ}[…W”ÿh -U:F BoBÈ”¿ø¼P î{àXáÉ4mc/ï_º.7îØ]®!²Ž¬ý§‹‡MÆþÿyÄÌx´[}—øpÏÈP…Mr½ëUçQãèÇœê[m.;-îô—óÚ8s y£üaìx6x]‘:~ÑÂsÜÁg뻼]å9!²üYvá­CdkN.§Z5€‹Éo;…$^’…< z€×dƒlÎo\3= Õ–,Ó|ú¸g[ù»Uã`çy=šœ¦Çñl¶á‹|l%[> ýîy8øõ g_¢„þ.Æ\m­n{ø·ïLEËþ1\[ÖYn‹çl¨9ž(nÙF‡U‡¹ÍãÞãÐ’­¤[W™FÖEðoþDæ§ã;ñ\˜ûT ν Í\õUt¾7‡y|ÜCÖLš'gÝ„UkÕ`IS÷úÙ.áä\ßÊ-=À£‡ÆlÄë¼Sö‰û¥êÉŸUùÐfq¿‹df®éÜÝžd»/-¨fþ"tóÃ4xöK“n&)ì(éBÙÝo¡ù팘º‡X¸ ð)¤j.œb—5Pí˜}òX‘kZgA¯ÄϘ]¾kÅÚô@ƒ•I?éÆÿl¸ß{¬~ZuÏñêIjK»Rq¡1ܬ°Ìµ¥‘ÿïUÝî(\™HER£É®)áe7“„4­ºÐ/,óß9p>8 N{oåN>I’£àrþeXñá'µ=[PöàšªD:./cI \ÑrIºC/*Ɖžt“ãxí¥Ä%”˜QÇ®è÷ÅšKáYR6žøR™éYC\ݺ—\·Ê#T/7bóT@8ë8Ü‚ERJ†M݇Ó>¡ëäP4·`wo,ÇeE•à^“Ÿ7CRúLJ°`­o"°@¥¯þË–húún7/ÚŠ2{GL¥¨ç8Øö 8ËñÜUË`Zb°=OH°6û…tj¥Z$Ä O;8dec¥°$ù¦Â’Mãˆ×ãøs—4:™î†;ìG9¸œÊ£MÛó!ë¼ „Ý–‡©;§±µÆÈkêJ!ËîU“÷mäY rQKæpÚwòæÝjÂFrÞó8õ5Ã\ü2íÛ˜þS¿º|l¾qœš0ßE‡³*âœ1Ô¸íÇÆ ¬ð>¢/(ÍŠî-Çã ÃßÙ¨v{ mT×ˈ¾1æcÖŒZ¹ "OȪ{OQXü=vì ¨Oß¼ “ü,Ù:YwÆÊÓ¸ƒu#\Ö÷IXS™»Ús_¾G³sá5¼Õ8ÏD¦³\û|ûׯÿ3w7ýwv.>Ì5†Ð·š)¬mûffC“©™Ê+nõ«‡¤þÄgX–«ÕUñ¥‘= Îî†ÃKFÐ0î ÝMPâ„ílãÖŠëÒ·‡8vî¢뎴“7ÌÓ¡ßk³ö"¼ÉÑ]xtd yÜ7]4õ¤ò^ZL=ï Jù·cËË8V`F›ï‚Tø)Z±Míî¯å­?Å‚]§ÑmÙñ´U1UüK‚u‹ôÁÓ=„†Ù¡ù†JèݪIWÙêÓ•—nQƒ€.xM9ó “áÝú}päV.¿°Ú`4W\óŠq´u¼4•—ªEW\ ±µ§{×ßGÿîûëoßÁÓüiÝ™ªjT6+_Dn…)ÏpVª™õ÷ ÏâÎ œÌx#Tœ÷‹5X±WrÖðùn,÷TД\/Æ6¾´ ‚tÕþ´ûéCg¯¬å‹\WÄ!:ƒ~…"2FpÌ÷¤­ÕI{mœÖcÑ2£S%>ƒ¢€??fQ:zìæ¥¯0ä{¶Og=á gx_T¥®¿Âá©ÄòáÅÐÞ')'xºcèÁgHäŒ4Ü/цÎjŽìuÞNh¨m†ÉW…Ù–+vKU‹Î#ÙA°Ë£æž’Ø9£þñ~^³¤‰c˜°ª¼\Ζ­Ñ…__* “Â/n#•œ9†?Ä™sS­Å©ïÞ6øðê „ËÑ/J¾øúü"f“WNëÈÞ¹HÃ1÷ ôõÅä¡å|ûÁƒiH†Ò«,f³#V†žà˶ K´ŒØ­S°É>g·å_hf¦Oó¡·ûÖpM2g¸R… ¬šw܇¯’‹âv›Öâ»Ý?!êj8šlܳ“lh‰]%-üÒ=é¬Cí8Sù(J?\—eY—êçڰ÷bƒ "¹4· ž}Ibߺѽåû ßöýá(î:/Æ{à]K%M²¹gü©L¶:–MêŠ#[sÃAPü"NÙF Xë~Ùk¯íeÏVÒ‡êO!ºõîÉe¯ %©~Tþtާ…ÿ¾ÁdkazûÔ#Zò™o3æ ÛûKèZ¿×J N.† Udü`.ðP`®Aj(ó5€va3N}ÁfÍcVçï®î—à_ÆòE³ñ çêù9rH‘M@aŠBªð·R“ܸ£ÄIœJ¿ÈN¢—/óaíõ Ø+Ðÿ~qxàñE|õGcÏs~÷,™Q® ÈÄ´ÀºÌäñRT¿§MÏ?÷‡as˜üh)MÈJĉn‚4øJ&<~s^/;…E×ÿö>FÄ"¥èN‡9dË¥Xö\Õ™–W£d ÎUš l4Ù:[FÞ?¿…=] xº|Ì»Hé ÚLøçÀÿá_®m«4;8â !‰ÌSóÍ®à£d×òЊLaÊP¢ŸIÏ„ëÏå(B¸ºW¼ïgj–‡‡»6’KΤÞœ~æŒY[ Â/"Fsµ5‰ ýÏ>;‹Ö#p4õ-lTû¡çÃéçÈq«˜¿R™}°Â/í*8ûZ9Â*ÏI0ò,JŸVãß—Õð%DšŠÞóc!F8‡PnÑAJKaŠ€ZÙ|çnå*‘aÏßøí{?žÐ{……eiLÑ2Ì[áÁõÅͦöï¡ØÌ‹+Œðc"âT$ü8T¼†N+a.yV>ÊÓ¤ÿÍyÀÀ‚‰p90{¾š~âÏ)f3¸¨w‹yã–^#ÛqOÓ@HÇ€-ÝÀ’÷·rYæ´i:9uû5yài…Éw5q¢Åj~g—ë<šÌkX#¢¿œ© ‡,^$‡Yz¢Ô+&Û7Žçd—=àG=¸‹ÖCi8Ü›ÁËy+ƒ.•œ•0_y$Ži…‚ºNéœtxÍ*~óŒ+$âí>ºòMÔòd[›ÏÑáHíG¢Ò“¡ÄK’= Gç›ã¨êï‹à^2š‡÷̱X¹sk|³žæ¾”«?ùÄ•Ë\)D½†Æc~ƒ[#i µR‡Â=öhá®ïÇ$®¨£ŠŒtÅR®ŠàœùÈŒ'0»¥?ÑÙU’-×€g-+ضÛ÷ˆÜRcs2ŠMMGm¦¦RãšÏ XŒnј¼¨žÈç/aesbQØd/æéKîp¼¦oîGì¬Å·}œ§vu­lØ ]_’oËžCPê_Ò;ç®}J¼.½ gÀ*OéÐí_: ÿÂaîäezÌ=€ >MÄgûÊáAåX¿ztÝu TÓó.;“ û5E¨fÍ#èè3eO“’±ð¼56•`ãwn­c;Ø{Çi˜#‰Þ?Üì?ýû ÓÅÃHb«6xï8‰CçÎÃòº· \ŸÎ‹ºõ¨½šF_;ŒàðpÔ rE q„Ó55ˆñ£1Þ9eöÄaS]Ú+Ô¢·¦'û­ÛC̬ÆÑ¶+Ö¬À|+ ‘› é0#Ô4çÂhsô¼§@o犰;æ¡N{ ýÛý Ý°Ž¶Ã\FÐIø7k¦epüÊ5º`4Ÿý\¯ÄEÇnÁ°—z,Õh3 z2‹½ýÚQiÈfu˜Q©éQìõ¹­à¿m9ÎŽ?@WùW£ÁÁ°½jú+Ü¢ÃëÑ®e¿KŸ“0¾.mmnþÁPû—êwùß÷IP¡_žtí vw4‡÷:3+œtí=¨™PX°«ÏÊôâ¶“'ði£8]%®Ãüt£¹Ã/gà˜k÷ÁNÛsšÞ4ÊkÚ@á•Y·Í‹i491™NGîØb%ú}m9òÏÝ@hú„³ÿž“(#j;¤Ž­Råèï3¶»ŽðO*2ãØ^Yýwçë0‰^=²„MØ#À^oTÅŽ;PÙÚŸ—v€Ì@3ˆYA¸€åq/R±}+ïà~%èeÅMŽäôÀ)«Þ„dÆÿǪ\S»BhiÐc>Fç¸hǧ8ç*wÛ}%K·ßBl eØâ ÈÙ®'Ð7V™=ûvœ®ÊÄÛ*SÊ­i­ ?¼ ØÉ¥ ]ºƒ"tS“0zÄQTžDzÛj~KwXÁÑxЛ€ñ³çÕ[¾Öb’Î;y ÷ÖÐññ¬øÝfH‘ ƒ-aq¤úI=òaqM6¬}f š§gÿ¡ß iú|Ò+’S$…KE ¹»ß‹áÏ"ø4ï=ú¶üƒf™@bä·­¼• ƒN¥aÔÙ0jTöÀæOR|ªKGwS7øíÂ^½ß‚UA†,õãKìGúäÕ.M:šèLhÄúÐvô‘ùeòÙÊÓ|ò¢F‚JO.1Áj7r¥i"}+b;¯>µ—»§Æj[à’ÎY²«z5=[m±ñ±ÌpV%›]žO“¦\Å쉕tç‹$–Rô+,6Ò%FÙLÙý*ž‹îÁm9‹ðRÅ Pæ‡Ó¡7ûájàÌØ ÆbP·Ò*Œ¶›Bgê‰Ð€ïÉèÚ3Äm¯ŒàÎß›ÙMFÜÊ7©4ïÓ#{zF‚í\=[3Û {R'úfµ’âWæôquè«ò B^âŒ1´²Í 6(cc '®T£ ÿ5ñš»,ÙÛã“8åì™.Dâ]X]2¥oÛqß M¨Nìø×Šm;tÁ3pý!º“^8šûÇPÁ¥5øµ:z.Aißj0ü>È”ïÃã¿–A¦Ô0DØ­t|–‘°wi£\L›Ý/ÌÚ’¯ÃÅ­·¸þÕGH®¿–GøÀ;œ*„VzÔwÖ0¬þq‰óú“M$u{áÞpna>33×:Vhît ƬÐcú£œæ±¼&Y™RIÖ~TÇùõœbVýåIƒø¹6-¥¦ÀB>M_»çìÛцÜÖ§mà¿OÞOxÖj.L«°þqåÌ6Äa‹u+¹üÜ„M”¼HÎV]áúwÞƒ¾ËE0 ü G*Ìé¡/c饡S¸»õéQ1ƒˆl5öwÑ*– Ê^4 Â…îØ%¶Æ¬FrÃwj,çç"d¡b ¹>'ú’/ûÊù¾óŸƒrŸÕ~J5vUb¡6ý*JüE,È„SÐv.~ávZË“ŽÞ‹°tÞ9ö픩ºÐ˜Únè^Y·ý@àÏ_Ðè5A³â*R;Á‰Æ^®Aµn1ºÛvªKeÀ}Ð6އ«cäYaj0Wi&Bo¿¿ŽÒáÚøä¬úåâÆ·Ž§?îD²ñÁÌ'@ŽJ†ùÁ“äRše%F}4ŽŒ`üðÚû¸ÒR2Eùàªu‡ðd±àE•_óIRÝêLt¢A¢7ÉÍåÇ‘»ó íWWb¨£h5¢—6O+,Ùýãõ ~ï ïš\3~þ¼O»ŒN[õh¤‚ ˜¨D3Å»0§¥&– ³_òmà°Ö›úeHœÿªî6?cÆQ¾Ü³ã´HØŒZˆUÒé'°wÅ;Þï;-ú‚Ï\é“_ ±@‡íhÃ¥§RÑ+õ•Ú.BëÏ(ñ­$Dü¤7CMcZ³i7³îÚˆÒä˜Óëdº¡o,•š±Œ~Q£‹?UÕ'.æÆ¨³m:“Ù…‹uÔcX–®N™ÎÎÝÊb?#¬À{É.<=`ÂŒŸŒëÁc¬,2œÞº‡4=Qϱ;Öú1ë—[ØHb F§Ýf ‘sé„aSš”Ààá'v/\k¸R­ÓiÛ©Wèº}˜žÞ1†Í> nOGÇOvf{¯ÙBóúËTÚ7µ‹3¹±•6®"g“Ú‹ï äºK™­‚ÂÖÔêX‘܉æ-Æ>÷°8ø5LpŽa¦Z4üº0­ŸÆæЕ¥]¨žv‘zGl¡ïóbá|ÂîÙ96¯-‹®SìĵeGà{T §à5–95ÜebþÙP1z¾Ûâ !!míõ +§ItÛÞÁº†Þ,õ‚g€"çDÿÝ‘¤"­×Áoî&ZÆ„ÏAÁ][ÙãË3Á5ø,Þ’˜E›å±£FóÙz½ŸPe}”­S\†ªî•´TÂŽ©vuÓ3ÒOðqŽÙý^i.gÍ®ƒÄ"q&<»]fßj@–‹‘¯°ÎÃÿH®mƒ4»;÷- ÿ!€õ½Ë°±dkîyKh·(³mu¢Õ§'Ð(~8ZN9É“vÌâÊ–é2©{1tŠPÑßúhž;? ídU&ëéÕ “è‚´¬ª3 l¬¥Žsr½é”3‡)<ÈAþŸë(Û¤À~_‰¦ÇäIúðlz¦z5Ä}} VÕþTþ± Z— ß‚Fcžô&Öâµ  énlP£²ŸŒéÚ9l¡Ì݈óãy4-fîï“u9G¸ÖëF÷u® ¦æ£@ÐDâo~ƒ¼»Êžâ¹¹½~¯‘{ô5ö™É¢êÐnÖø1ž¯›¢‡îb!öaãC ®U˜?t >È`®é1Œé¥dŸÿw^T˜+ygâ@﬩Åï×üé;á¸&fÊÖ}9`¯4°£Eã‚Ö} »ò¯Dö$ü-ö/ùÝи{&»Ò I–­p„­w²¹ÖæzÌz¨‹¿_â v=†Ãe¥Dù}$òNªR¯í2Œˆ¬Æ#v¥(ð ŽÅ:î§×üOàÑ ‹iÔcîü„SP|'Œ6GP_‰D²ø†¼X §óþðòç±6‹_`æNéñ`)z2Ò wL˜Dÿ¦Ü¡rs™VrF Ð8C­Mq+ws©óýh@°ß<–ºž™…5ýòÌãä¬ÜGÙ‡c˜õƒ4(ho0Ë)lgù!V"ÄÃŽ¢´Éb¹¥”è{ŠÐk:¹ës´Ù­O+F¹~[¶âQ¿ƒIù¥QϪéÙçv,ëísع‚Úf<†ßò4-æ¼T^IŸ¬mDÍÝé &£q Ò¬CiûŽ œß>}*º¢oµáLš£l ûüî8ZÏ;ÝÓ†ñKõn<óð¦Å¬§ãŒ- :&›w"Ü‚ÆÏ|ÌÞ?Ø9À5Œñû,rw õ‰‘e/ ä¼›.C3v˜ÒÚY¡$fï‰GÑ£uG™áѾ°WeÒ‹ë°ýØv²Kb<=ý±fjÅÂéŸi8|Oç.zB$ßåܾèRÕ<4œ¿ ’¬_‹¾1¼”±z`ç} FzÒдl Þïª$?ƒÕéÉŸÚ¸>E˜¦êâ‹‚Õoî‡w3¦ßÌÉUšTtÁ>Z+ö¾Nd}¶<ç ©¨¯3…Mmþ ;­áÖÌeŸ‡†íšøqX‰j憮ì#d¨; ·;öØ‹°ôøtw7…‡Ñ‚Ì3Ö†}Úw•Ñ®õ,WÜȲÝÌy'†×bVÎ$0w~ƒ¯7Ôk?žJ=ÍJ:)ÚŽÚ]‡Œé0öQÚ•Y²§Ýä«Q9änµ‚í®ËQmû[HÙ» öµk’†Š<îæ®Ëà¯)¢qp¹q w:SeEæö«ŒÿÚù9žº?–•OIÃ…î㱫R•ÚUÁ‰O~ %|e$á£F¨PvšÎ×B§ègÀËn/þÚbï_áTÓH˜z¯Öaê±ô¬?·³þ<^Xêþºl¼€ýücÓ[Äa‡ílxÑ1zvñL~¦˜×W¼”™KÓÖÇšKáð»q¬ZQE—MbÙI”]ÖÄ)Òäo÷ºŠoèq¶3o(%^Áâqn亚?Ÿ‹ýá¢tŒIÝðê"Ør$¤X„$™Õ—EÀ~F5ñÜÖAóéÖùðæÀ4Úݼ%oƒ<%4zÕÔU`ývãh`Dûó† ä¨H˾ÁÂyÔ_ñ‘_ï…9E¿C,àÛ˜ââDÛÍ  vªøè7¡·…lèÉ?äùMav×¶ôu'ãÓtJ݃óo¥n¥ËÆÊ‡AMºò…=–vá®wŠã¼ãy©Ê^ÌûQ7Çw"Û{Ęåv€²ÿZ]OÆ`ã8®•Í`‹Bq².|œÖB6i,§qNôˆŠ,3º>ÖÝSƹ×.“ð³´œ[P[L+h RØ:îûŽqÌ"´Ó?O⯾âË®¦/ ÛSaG*=Ææ´*Ói«C‰¼¡,[?Y›æ­‹Åï}ž4ýìD蘜Jƒª®¢hÙ!¼vÞ™ñuÔÅN° ÇmT©ýœçèi*Àtö-§¥1¿p[Z xÒf={’ÙÇæUЙÞ2nê4TîÕbW\³rÞ%b4ì+¥YÝ=IN²6-jýÉØÑ{<Öf&«;5€÷Þ§“œŸzìCØ[ˆ;r–KzéJÓ›wð–DùÞèbLL>ÊmºÓ‹%V«±³û,4ìÉ$b¡1è,õ›¼zóÄæaÝ![®&s ƱäÕP68ô,d§ apú|ºsÙI~‹¡ã¥Dò¥ÙÁî5n—¿,œ:ö€ <`LT‹Ðùk †½ Å™Bº°Ñ Ÿ{r*v‰£J‹,éQ}S¦8Y€h%ÜÆÆrÍøµ(‹½XéC«fpÐò»å¼ŠæÛUcÓÌD-“ÃNž,¢Zó§BÑ{F„”Žƒò±j^a sÜ6¡0ÜÀ{3ÏÙsþ[~Žh³»+“àsE)Ù’9ĵœÏ¹Ô|¦'i^F“Üp×™µãZoÃáq,;ñ,©šæÁdÕ$¨›Ác¬M; ár2L®£ ×vã¦Gq¬ÝëÊÜKåê;í_?|‚e› ü…,vü‚jýr˜ ”ûñ©Æ)ü÷BŸÌRè G8ÎrÃ!¬cëîTÃ<ùrüüK&–ÉBëô"È€ÓÑ ¬âj;Nz½^H:`vœÝV[ÿ&.c~[ïBöïl¸¨• éÉ•pcÜ»1™sjÕöÃÈò&´ñƒde%ºzî Îi¬œÑ׿VrÑ­A(sÛ¿n÷|äq‹©=¤¼È' ²â˜d˜Á=8¸G6º Ýð%ìõЦWö«Ò‘þ>¢,y«þñþpxìËK(ßH¿I‹0 Qw^†q7ƒÁy¯Ðh‹õ”°Þ­FÞDÕoˆZÆÚßOá~ ÇAØÐ“ñO«ÒW“ÖâúfÞ?7\®¹%•-˜Q„):îaãKví©bÔm}"yóO>1•f↋Y[B&oY×Oxtçûv Ëj’AùÍÇÿ´Ùä‘O2HÈ^‡ocZë½ÿ]ÇR%¶¤z<ø{m£«¾ÊP7=eúX#‰É­Fª¬ÌŽ´HcyG,*Ï€såwI†ãity6û¥ã¹Ž…‹ìÅàf˜ðr9ŒL«åDæƒë±lÔì ÷}dØÖò“øû§—?Qþ€Ývb¨º¬íÛ‚y ÒØï=–9ÛqÏ’Õ uòn~|/í¼Hæüp¢v5šTâp29ÚWEuH׃ëÐ{]‰½Qã™8Á„¾n8dú¾^o3‡“ÉE­Z¿n{Ÿ•a÷ªú/ ‹MÆÏñ9Ø»Z§ 3²Ü \Ÿ±¨yÉÕ´a8~1œ ‘{h~w9ä®C¸rà%ÜOçä=å…WÑe{añŠ×¨rôØåÜ‚®«Ypâ¿4¤7ŸéCD”&œž#@3îFƒÄ6ûî ƒøxóWx4°’Œ[Z"ÜßÙ«qñ!úzw2°fyÂE¼ûk pòKºàÊ ¶ÖBâŠæBÒÚ&”¨å|§l€Åf±0ÿñ/œ|DŸ'nÁõ7OâûØ¡ÛÎ{.©HÛ(vàN½÷ML\C¦¨»R“N¬Ìò&—¯lŒÌ2– dÉÓ2³²ï#Œ¨L€…mßñÞ-.W_ª‡ò°VU‹©ºEþÓI[½:…yMÖX›eHK/ÿ#ScgҰɉ³ï=\¯+LûöŸ„ÎÝzP‚±8æBªŠUqÍQç”N6£$©Ï×Lg¡ëS™ÜL4—ÿKJE–Bc'ˆ_H.¹¨±ÌQì=£3î~¸b³ÙtÞ º®À€õOÙ†aö}x ´VTÁƒ{²Ì;u5Ýwz9®°~^wÄÙèÚ$\s)Æ÷£Øëx&¤Kª'ãnN¡ê«‚©ˆ´,¥QÊØž/ —ä£ã“°3,'w·ß­ŸÒ;óøÜŽŸ9°H4ƒ•'ÑòfôI`È#RV™íN"X”ƒ~‘ Å9d‚‹å9²¥ìº‰\ÇsÎàú± z¢ +ÞsÎÍÖhçV…áŸ%iX8žQ™f/òÆ _–Pê\¯ðZ‰L»¨Ïd%'±ÅâGÑÅÏ=¹óoryCؽ/;ýÕ×лAJ¼µO꟬3`óx*»ÞýûØ‘;Ø<ñ‡˜Öû{3«vsH-ÇÉ3ÀÏ“µÏEèÄÕ c!Ì®$/ÀÊôL×Ú_>¬ç]I«‚¦Ö,¼·ëѰcwxð&=5Â6Á¨©G_D!“=ÍÚçÅâ¤Uqäëî’cpvì,‚¹óø÷(€³c9ŠSæÊ•vüÃ{c»¹æ:56ãA4,» ä‹á#”ß{ ‚kJA ó;* Eb‡o>»Û……ҹǟÁZÍ‹O¡âiÙ0·®‹ •}ÇK©Ïˆ\H¼U?Æõ%ÇqÁj²:Ã6ïGuž§¢eF"wûÛqèsý†3*ÏCøËÜÕ5wø_ ß^]f)XQAáð$÷ üá;ã˜!Ä>Ë;д²U­ÏrŸ’¹¹Ÿõ¸Ÿ¾ T뉳ÊxOwõAø•åtAÏ}õ¢Ÿd¢©½ (»‚Ê  F6ʲ½;´˜ L§Þ³ˆÅÜSDy¦+ü¨|¡9’Ôë}=± ½ ›Æ+²’ã3qð6G *f çù1¬ìˆ,ô1g¿q½„\ mÛc0dÝ‚Yt«Ü"8v¶—ïæî¾TDÉ]ŽØR`Éä¯ð=ÿ„C¶ÜO²».ãòÎBê½u ÝE;—æ@ëé8¬ZGW–ù3¡u+qÄ¿ŒL[q î»G<œÏà¦ì3pÄ?l³KÒäÚHû1‰½Z¦ÍÊt¹Ì©íP§»ÔOP!ƒg ¸ÂË‚¦™8Ó9ÚzxE}%ëŠxC,åÄù¯gïbÂñU캧‹zšŒU½ÇÁò¼<•ˆÉG“ö‹ø^æ;츩Lµ…eX? ÿMF«‚ôØÇ$<ÿt}Û´ >^Sbi#sÙ;£ÜÇœ9(°øj8ˆ¦²¬ÿþAذá(ö$ùpea‹ Ó®”w}/eÇ.‡ÒÈÇ/ ÎC…MOÃ5laÙ׋¸½‚¨ÞЄûnYa”ÛoÞîÛsØÑ}ïq•JdHV£ðÛð§6ÎO+]9®ŸÌøO3M Ž]&Îfr¬¢x7«HŒ"CyI0_‰gÄ­ææÿ¾ 7Kѱy=„uqó¿(RamÀÝ?ÑÐ[ZåÚ.èÀçÝôž½:ùº> ÛLÃѹÉeWØ%öâk4œ48 +gsEó;qPz>Ó›´‘NxQÊTô‡é©ëè‰þ2ÈÛÿ†»sˆUxˆ‘àvå&ìÝَǃßãù {ÌÚrò?KßïÝÔþÙnXóCŸæŽyH,tG¹zB>Ûw®‰Ç¡TU~V“'ˆü¹Âï_ƒyñÓ._âçë‘>ò”Ú†sÓ¾æâ\íQl³¸#½d†ÐIò14‹——<ÌÝ.ÎŽ›ùÐÿ4Ô—”XÒêïwas ¢Ðèœ.ì¼ Úµ>øyÿj*Õõ– J"Zª‚ðý§C `³îàËhvæ Žt‡cûH.Þÿu?Îõe‹FΡqÔ"ˆTšÁ;܉›N Ò÷Ó²ùßĔɨþ9¬üü}|éAùià¢æ,Fª%íøG¬šœxÁÕ@9†¸õB}Ø´-nÇdš5†Gõ dà£C„¬‹¥nEbì`ðTTìBÍrtä"´ÌÃæÿda.¶ìêìdxòjJä‚¶°]¬&Çnß\»Š‹9OþJêsüH}Úla¶æâX´Þ‡ýé‰Äª Ôá7›CàMÎpe6“lÀäÙò¼¬š>F kHcm½Às¼†Ý´bS†Ž‚ÞÚµÜòÏÃøÇj€ Ñf:)J´w”3=žÉ†/|Á—Ã1ôzÒT¶èo%†iÄ?™tÕUûÓ2RÔ¸77¾(ÀY¯±BÑÀw>žm¨ûxn™³³#¡ã®)º+Î@ŧOÀ¡ÞžÚN®æüóÍé´?kèßÙ‰¸ù£8ùì(F«Å•!Ñ›/ý_C½YZ´ò¸ ˜' ¾ý—aý0ø“óŸW’­™ìËŸfÂtkús0Ë; ib6|³’½Þ4[œ:ÐñÙ*g‚ªßúøkŽsüI—wÓЫ;Y˜ì I½ž ]}Üßöi(U¤Ãhu¿øõ-niͪ–1n8†'ÄXÌä1Ì8Tžå]5AÔ³xÓb>wÃò1÷õÔZêÛÛÎ{¦|y”óh1‰­.Ì€åÂÉûO1cUÿ®ÞÞ)ç?¸ûS½-Å_xÀÝ=ö¢ªvbš¬5÷h ™9­ÄWÐ%…lÓÚH õ‰ËQœBOZÓÙÝ“èËÙC¸vã2_Rƒ¾ð;Äþ|ô£·ë]E]š¼aMa9,†7÷J xy©”fð.ò+Ôm¹„9Ù`uô7Ä­ …ÅlÓXæB]¿+²#_áŒÄoLŒ*f?Ÿ¦â⃲P1i ;¼&=ýœ™@`8/`K4nG¸¯Ò]Õ‹°o9 F²>{aL˜É 4àÑN¡T“HdÃ!®¬¯v•oú]çØx…fé/$‹CP`á,*!rˆœÙYÑxÆa;kkȱþª¯ Îú‘,ý;¸há zœ¿ÞXÏnû Qûãè#Ý­T¤Õš­J¿ãç¡öåìǼb(Ô†oK6bè«2¬rôÀ×ÒêÞj¬Ý³š-—/BGeØå˜OåßÄÃýЋtÖ ˆò EÉÉç°wó~¶ý¸íÏsYkýãf0ù»x:æÔÜ'¸òGÎ¥QM-låÏ\˜\= ë°pó$ºÝ§ÔÜ×ÃÐ+høÜ@6„iÉ­N¸Öa9~E’¦n[#óŽAÓ€ýò0z"¬ØôeV±JŽ=»e ¶ÚÑuý¾ÐQz µ.‡²…ŽÕ\j»;"í˅̵f[*· I¢6u¸?†}Sþ÷—nÇ'ti©:r6³ãÝqÍü•w Nɘ M§eÛ HÔÒ¿ #'ª––£ÜBŒ®.‡;‡Zq¹ÿºëð_ø¹A]Ö~‹Gr`Í»¿\B:àH6†ãÇÇ/I‡[ VýàÐÚS”æ}þ€i`ëM°Ôȃ]`Í^IHÓË:=ˆbÂ,·x%þ§ÓŽô\Mv·±¦Ÿ 4Tè6>>^ ’òáSôNR¶f!·õ©¬]ªŠ_ïmÆ'IŽ8]j-k «<Ú{ø":ÌhÀ7?Àz6ýwÍ€‚‰æö—Õ úÆhLLʯcʰ÷_%ë Ó‚æÍðhC?¤Ìøòßœ`´ÌŠZ›Hƒ¹¥37tn*[»»æ-ž›U=iöµû¼uýP7³‡»/÷OŒf&ɯõ#§¥¹Á‹#Üõ‰;PW·ÿ‰­AxäÈ6Yí‚7Æì´þb6õÁ/<5¶Î5–¡P4p²5é0¾GösQsÛðÚKz(+„§|ưœ[%Ï‚NÈ€ÏægdݲÜw°QñPX´éxk7 ±­'^ äS)®ûlœ¾œ‰×£Vc“˜ nÏŽE½) .¤l„Â÷cp߈;%!qÜh_¦Xr e¿bËCÐÔÙN³›åÁè` «´ƒ3ßó„@µÔâiÖÚK´ÉɆ›i¼6$]$•vº ;q÷ÁwV]ÄŽnL‚G®ÃAÅÑÀ<ð VÿC±á‡{*«x3Ž&[{£öÌËÀiÒ›ÒKÉ£Ü_ã3 òƒYöi¶gÁzxœšÇíQ˜Í¾¦DÑ-I[ÑÛ°h/ƒÝ|øóT} G†üM±ÏL¡Ñ;Ҩߧ¯pû‡1^¾~:×%î„ùÈEæ¿8“«ù™Åzê˜Ý ¥7úñóˆ Ó¸’‹¾›œÙ®ï²lÅÄ#»âm}VXéŸ'FÉWüo<Œ>Ã+¾÷ùÐ_¯­Pŧ¯À®šžó©Wð²! EÃ7¢Ë>¨80zïù°uœ=údK4 Àó¿Û‰×•L kJÐIÛÿ¾žÅB.´@Ͱ0íŠÙÅ-ÿ{ª#™UT ‘ÕØKw‹Û°N+Ýâ &Q§àWº Û‘fÁN¨î`­ÌÙ¾¿ïÀ­-ÅËEØcÛRÞ² ¶n€G-:Ciˆ'.™I£/xƒrÓvJ×;JkÑçS)jù Ù‹~çÆâôYÊ=x‘$D{Ü¢A/y6fžèÃÛÕLÎå츷‡º’®_8N¶&ÐQÂNÉ<Âiû[¸UÛ£ÎÅÔŒbpö¼Ü=†Î´Îæ®×žØµøúý¼£©3 ¦7jðÆÙKìE@({ì¦Ûþ ­º6ÜÎ ú$ÿ :Ã-|Dn89afýX¨ 7½õitú+ê. ƒ‰¼FõIlç¯Ô—¼rŒ㦱¸±øÄЛ.TO„âîµ(¶¨ù2Iøìotl0%¶S¦2Õ= ø¦.šl›û “$Û—»ãÛUwp_8|jOkÕnÂXÁJòcü+HTž›sHE|ØÏóe;×xp§Ïc±]-9b¦Hì×K±‡å+¸©QÏy[œ;0eB>¼ÈÜSî˸&pÏ¡}Nx{u0K•ÎÂ`§W0Ó_…¾úbÕ:ûuÏ”’-¡ˆÓ‡Ì´0£ÏzekÑv—&[=(KÏŽwaɲ×X*/Eþ¾¯æLþ›är«+ÐÅ͵­DQµtã±8¶4eÛÿšGEc›ù߯İ™·>€yâ Ýæ‹g^ÿD?Í àÓ•‡å6Âl»~5‘®úØ@ÛNž„€]cþÎñ˜ZÆYTAÌ„÷Xå3–ÎŽÒbóïwÂG¹GxëôxZ²«~x°èv ¹[¿‚õ¾/Ø‘™LçãeM¶—.`¯æŠÂñlø´Í‡J¯YÍ}_ö]š4©ñzi·×"þP×Ö!è1Keç$#QÛqk›6b›×m3— ±Uáƒd°Å„E”cé*´üõoìÉØDW(rð2VÛ]‡oA3Ðßã0Y¨Oº¦ÑϲÐúFUc‡¹'KNdéòÁ¦z#˜ï'Èžî9Iâg£Í¿"t‰Çî›?†²¥fè”|˜Ûu´*œT¹Ï;Ähæ¹V§ˆ3T†¡èÕax¥7®>ÛÈ­u{Bd§ÜãÛŠlƒ¥³£F¯a·S“PÒÆåÒ Ýp„¹È¥ÞÛŽ!ç/‚кûÇaçˆôZ xšÁ¡ÿQfµZ=Ê™L&ëRWæu^›7=ÅÔf…bœŒ&û½!Ô[€gq 7tú—¼>ãTGàÖx}3‡™é  IÁsO¸ sgJô¯bÔ»)ln ¾ËEøU¶…>êÙ¦NgéÅ Uæ«á…þ}ËØ­7rôBìaüç¬ÊèW¦±àÄèÙxÐ÷vcPéÅÒûs9¦å”1]EvH'¬.¬#$é»»±ýz ¦¤/‚ÏŠ™Ì¸Zœ5Ì€/œ8}4~Ë.ù-ˆ÷âæ°ÌdsZÍIÀ‘F"˜}÷>íƒâß2Ìü’µ½ç‰’§ñË>1üOû¬åõ~e1= ¬g¹Ûp±SÕÞ¡o£,Jxˆ3ñãÒ4µY© ÈÁ󙙸2tÍ©*âë >Â_'ZaÐ]æéìAG¯ÙèœP4p*T7DZojÓHLÖlv§ßNÜ¿µu$Çã1ñÛªH皈0¯ù®õ!gôØ\}]úè=r<¼yÿé §ok€z•Ó\íÇoäÛ?U¶G¥nF‡ã ¨ \ö'²/…UjEó¥& à!H¥¿GbgüX-<Šþ²p¹JŽZÉ"ÚÄpû"]œ9ÝKågÑæ:Üb-î¤ã¾ Å-ðý%޶{]ØsÏãàÞ)ÉêËÜéÒ½âÔÍþ_¼A.XTÇ\*6á›_úêÀžºñúÎY¸ÞlÊxf¸¿‡–g nR±UÉMð>áœ%¾9¼’YîÐ'½‰ðã°-;|÷!T…aZ ƒÄ‚Ô“¿Ÿ¾pèb ‡¼)·S—]ªfgwÞ`¿¦¯â´.鱋ßTèÂÎö5›vSzMŽT[(ên¢z£Ð©e&4;f×—Q[å Ø­wÞϱ£_X’ñ ÊÚJ¿¸.¥»§Áñ|6:žkOz,§ƒúcZ“³’-‰ö§o×ÅÑ)a´ûy(·Gç빡MAT` Š—·v ÍMžÍp¢ÀÉ'¨:‡%œ»Kû{娰înÍ•Éäï¾\ú÷L4ôR/vcìjj½~6¯ãˆþê\Ž×w~õÅxZ§{ «-¢zí´ªAÝ^þú/³î¥Cxo1(ŒdpnãähÒ}u:£d8/˜G¦«%’V@Ì9^÷ÎÀŒ|e–6›2×ëì{=›ò<‘M´sGañZخĉŠ_pF,аóÂts¦éw:Žî9ÛP}ØD áÍ×ölñ’Ϥ'l!Hšn —.NbòàèL×HÇ3Q bV§äè¶&#RÜ%ÂZ¦WswuOÁLå1t¯é[rÐÉ&˜v@•É+ø¾J‚>œÖ!E\Gk|1üKGϾé¯^Í#™ÖéõQ7:ðÊï<¦OµYnŒ]ÙšËÄ޾ëÒ/”ÃÛÞùvl­§ýÔ‹v«Âû×DúJ Ø'?„is0üÀçV°Lˆ¦ÚU˜>¢¯Ïå±(:¬:ߎªùlÑý<@l ͘»¡4+ùó q “üÀvwYö%ÄÛÁ(Ž…ø³1ÍcÕ˜ßC–­h"&뤨¯¢ ýòÑ—=Þð–ï«“/øPØ.K¿p±(7ET„ôVÌ—ð„¡?\ÞÖ¿øcµúUÇÔrFÝŸ¸ˆæhðbäuE½¯‰ Ûrÿ9|z'Êžª»s9N{˜àêqÔ—çÊ›–gÍÌæ'ƒŽqÑH“à·ýÇôÕØÞóhлnîæÕd\_D‘¿>xf ì&/;\¿RºöÛæÖne¢9‹YYÅ…,ćõSÙǘN¥„+À>I’í8_À•«$Bʶ|h¾JðŒ9û–¨ñw”Ù|eCîɲC°]p55 ¾KÎF%dÉ =×keéàÛo¿`KZEïZÇí¼WNB¾`Ãë‡øµf|=#G×%Pï?¥¸Ö¾ž«Ç(X§ó¿qíøKöÂÕB*'BÒs/ḉup«Í «64ñÇ3I/+Ø×¸–©î¦)š yé'Aü´"ƒÿÕmnÅqœ¸#ÂZeÖqâ0N_¯X—`Œ³™PíÕ%¨û\¾?(†çú/È€ÕDZ©•õ:°­6‚‡M©´Oy-ÑIç_Ø‚ÍÈš£Q}á Ü ­K™N~]p;‚Öƒëet*»r[r郅©ôÃ"könølص ŸœviM6µ#žIh ?b3ÑÍaG,Ši¸e$³xŽvYJ£.û²P™•6¢Ël÷°aPÀ„Æ+l‘³$ÒÙÒLz‹%›• ÆR£Œ9ì¶ÂB˜&Kª£Á}]HÌ £çÙç®Ó´(ó;ÈußaNÓBèMeÕ¯ám$¦ãü!jíŽ3Ïnçþ[]®öræ†átï/auØ“E?nØGöÇŸbN“X¸`ËîåȵpV¨ÊÍ×¼M_&0_ksò U’·zv çë)ÁævTûH<·á¦/p.ƒÜtñB´ ŸF]c¸]÷æ @;!,¦ÌI²Gðê7²mÁE,¡þxE°åkÒð› ›º'‡]ºòU]€ˆ*òü*èMº œÿ@qy>½›Îr®¿@EÌ&6¢-ØzT•S/…ðÀ`KV 7bØçÔ½è3.;Í>arÐZjóp›´Ë›ŽtÉSÍKcahj«ÊÑ‚JµxSqˆfzÌÃè¿ã ød¨õÙÐ;â›qø—ÈÝœvºS޽÷ùƒ[ÚèaqtÝ R]ÙØü–£kž.rº½!· ú-©w 8Û½ê>~íï€íÉÆtÊÁ±Äë]›²¹‡Ló›ó§Å&ÿ¸ô ÄKRÁ§©â·3è¨hª¼èÿµ¿õåÁ”ï+ð?²aÙfRè¾mçWÀÚéïx7»¹;Sßr›æÍàWh¢×ðš:ý$β ½tUè÷“¹¬Ýh;¡³…­Íò†ge*tÿ®T˜ðμñ¬r«Áa£Ú×…æ£ÚÔïÚ_¼ë¶‹k=Ë [Ò‘êÔ½ù,´3¿˜uë÷OGã?àDü–Ñ‚þ´=::Öø<'†lÖÃÜ‹`?”ÈZï¯wÍÛéÏ…áïzwÎÉy Vl‹aªkæÒ ­‹¸VÃYœb”;e!üõàn,7€‹ñwAµ"77cÞƒ‹áf‚ n>eGÅð@ðöÙáz}ìv0dÕGªðûžs^9¯™*Ó% °e„ÿL;áóB8ªàÄš_ÄЗû¨F³ÍÔäÅ=8p¬ ½NÀ¼ölíR²ÂÛ—³”ûnùðÈÏ\3¤;' rzwO±Æý¼ZÙçs{.L ’¤×󎓵ß1¹ËÜ%³%<»mG¹Æix½”§ÿ±šÔ }Ä©SÅéAd8¢ô ¾¯˜Šk2…é­sP7Ñ€^zþrÎZÂI3)~Ø© ”õ4E廧Ѫ|D©ÿÆò¾+´ôòKNuîQÞW)ºC¸ kåWá·þ—Oòº6!s’§]¨Ïb¦æ3E|Êû݇š@ÑA¤é\s¬ ?D<æ×ÁÄé9´|˜Iê_Ù·¤¥"1ìĵ Öxþ¯3U޲§{Œ é/GOöòÜúbWS˜ŸLo¿F•ÈTÖ²¹Ç×ÉÂ:ó,ð6£5óšH‘0ÈÍTÆ—SréÛDu¬fˆ ¯Éòéå¬ROÍ·›ÐHÓíxîï>ºÙ© /ìålò8süKj òáö±ßxžÊÑõs|¥°œ€ÆLæçæ‡ò‚Ûp2‘3–gØç9ó$ {eGó4÷‰‹Kòdjç"85EÊ®6 +£÷ü7Á£œp¨MüBÜÔØ²£™T›î¤X^ĬÎ-c…ZEsa'ÄVÁ©Püš³—*0“Vf—vîÖ=àâ3+¹—#ä÷Npi}@u¯dbðd”º•†3f3‡cÿ°ê—wú8mL¼ ûÃ7°¯K"Éú¶ÕÿÕ ©éѰå­yù7˜¶HåÓéÕ¢t¸-tîGÁ ‘.ˆ·ÞÌúÀP›,©7§‘²¯ð»Ð{þçØ0¸_^†ï†Sëï²Ü·ÖÐÛ¾”Ú¸? ‹÷ò¯/B•éwaì úcû$:¡ä|}µóDj¸û3>qPEmç0ÿ·ŒÜkŒÔ‹/°Q×ñ¤þ¡´u2é;¿†’pãn_òùp*4ÎNÇ(\®,Îô¶œÅιAÑ®^½ñÁÙï¬)ø^´?±ci¬&6þ 'NÏ…)Wô_Tæ€Óó šá3Ž*?=ö:¿¡ï¢;¦Ä&ð.µÃªS©0õ`mn~M¬+3ì¿ÃÓÌN’ó{>ÓÞqžÎᑇ×rHŽY “¡K}í ;ŠcWÎ÷Ç—´l‚•.G¹’¦¼Ã »ð°µ,^ ¿Ž | Ûðç gc°ž’1ôÀNQf}` ÷ŸÞ9~ИFÕr¥vi4‰›ÈNË_dy'йˆ½ é–íᵎ ç´ÕýfPù!šS<…޵¸Œ5]îèÜüW¾7f&¥¾dÝÅ\ˆ½Iƒ=ýØõ•)¼íÄ4SGºÊYŽ–ª, ÿL§0OÃ;pu³oo’Ër\Žaïƒ@%²ùÿúeYq´¶ŸGR¬<„ÅùðN&.Ò<ߢ«p†a›ûipFŽ4X£7äãÏ%£ÜEÉ•ktƒèb²ŒBrÝü¼Îþ ßìÙÔùñ¬Á&½ø——dÀÃ5bì¿:ÑãO¥óä%䨟„­Ü‡i‘¹À›wìIcƒ#Ô¾ŸÌAjÑ °]½œô÷^“71í¶7n:+„KwØÓaÛDÌiÓ#¿´ ©zs òNΤëx# Ò Oý@Y–3'7?çìà¥Íhn ÙK$©Ð¯mPý¯;æñ¥?|ÄÝÏ®Ûå/¶þ])ikÅtÖhQ±ghÖ|”H|ÁOaÌߘñà0S_QŒ§ß†CãåFãx§ú}*Ye£˜—øy”›|Gª_ÇwUáÈ8SÜ꓉v.Q m‰÷׳X¼T¤URè9SA:ûB<œN¡s$ƒ^ðRºgØ}X2LŽkæ¢ØÃ`•HNÄr2!¤7B’q ›éÂùžLyš ÌNׂwöˆx9+“´g1çVÁ¥]P`tz_Ãô7®¤aÇ0H´·%Wð‹GD¸GÓ^aCÙ~Ç›=´À÷ä¯ÇüS.ö…•ÅëgJ4Næàò3«UYMg,YM_lÈ;Ô˜ã§e\q*~½¬Ný<Ž“÷=ºôH[;*åÅÃÖ’…0ûÆ$,¼7fÉ~äÉoÖ¦{–?߈Î~Çiú¹„Y/0ïÀSt.€?ABp6}:<žÁäþ>ÆÃs¿yN8JÞxÍ;VF, ™ÎNËÍ}—Kä»d:þWcÙLý+|Ó‹Ã1í¸,ÖyÓÇŠ°Ëúà÷R^n4£JþÕ\­Ú?xªaDC&(ãW×Ò³ö5É[›Š ÓR‰øFi{tÃï†7a°êN|ø>`p FºùÁ—D JÔ¤}›SñìßÛÁC¼6RÑ7?qW{¤‡u’ímVè6þ ³‘=M‡åÁÅñ)ˆ«þ¶%2憜Yc?zæ£o&/c§=Ž|¹‚Â[ýáŸê!ü阈ӂŘ”ão¾K’JèÛ~XûO…´ÿK ¥Ñ¸¥æý¢Þ\x—.¹1ùŠBâ{b ;v¬Rs ÅTV‡mrÙ¸qÝuœü,×Ï[ŠmK¾Cßã"ø™ØÍÍY~‹·ùÐE0Ê*Ä mÍØ@–0 £n׋8rIð",T9†µåF0à&ÌZ­Á9© +¾oºI¸«2–ìÎÂÌ[“ 'è©JahË=ʾ£ý¹œÛYx¯ … Ë0¶þ-qE49òÌ€VëiãpT®ŸÈt¢îÀíUÆÌS.,+ÀoÖÁ\Êä Ü"IŒñt17€k ôE–,ñmÇ?4s+lÏ?JŠc¯ë ^÷s’_*Níi³XÄ4>¯&ϧ+ƒcÁöÓËQïÖw¸QÁ‡ò´ïpÏ~ 5XÎ }(…ïÖ ^ˆƒ@Õ0ôRÞNŸŠ%À•®D oªýûÃy ÌÄÍy^ÜäÖ,·9f™eà‡ûQôŽ °ÍƒKàæmÚ÷šX]Bõ••@*z:sþcÞ‹|iüS+Ö¼I…èŽó¡ûv’G/¢±¶74+B ^¸¬Š¾ŽK|„Yns4.(èD›ªj¼SäÎß&á+m -ç1\Þ ;®k2':ôUÍ*1ÖO]Áž|â›ôøO¾Dâ åÊ»wƒW½à/XªÅZ{¿’ÓMU¸jZ.UðKæöxá{qJ#^Áþ׆$ä¹-ŽºËÙ¾=Îã· Ñ©9³™€¸2vµ­'ŸMBhÒ$?¶¿ð$¾½M‰üiÿX[™ .bGêÉÙMÅôõJJ¦†Ì‚k¢‘½¬âož©@7ÉgS…m)õg½Õi£éNtóx@4+–ñÄï®E¡3±vP{˜ó÷GÐ^_YkÌ;Ò4û^´`MiÁ-Y‡{ï ³D X§¶(U½µ•Ÿ.§Áú³è†€VNã¹+ÊÝûBKØ”­Aà¨. •¯O@Ý:qúµp:~\û„$Xí Z®l‡±kû‹HYAyÿ®ðËÝëx¿Æña•Ø_³ä-w` lI„MŠMœMït ÙÐ ˜?dËR>)Q_ß9, e& }›G+çž‘)Ãü&MiZžG‰Ï5E®Ñù+6*ÇJ³;ع‡ê ðßî–뉷X Vù"“›EÂ^Ü‚aS9(›6€gN¹’C¹¿ñá8q–š)džMSÈ¡#Ü”¡ÅàÄRp“è´Ï£‚4ºâܳ„3ÙNôÖ'u¶Gë<\vRa·'Äà¢Þyp>'‰ÔLg- ŤÄt²‹ÂÔþ´Éšl0z¶›³mö„ŒÛ‰Ôæ´<›µê :µ–TG¨.I…l»«HF¶Ø/Ÿ´­Òu¸¯ßòAè° =7šï{®XöþÌùx K:baËgQ¬Ù¨À ذÿôÇW›Ãò™­ü¿2#Ü@¦wÜý5,mὈSDn‘>mÞÏeOg?×NGË^qö>,þš¾â:¼ííŠÞá?»îäÛyp¥û Î] ©½0)Al{Èèw|ÖkRaé§«œ_–†ØAÍ.LŒ€q7ñùðc(Ì{ƒÓŸ€ÎÚ±´'t-¿ñ´ÛsAû2S€švÜääž?#Q¥?yù~á 8•¿òº:Íé‚-¶QÜÃY58õ¬;XúŽ+ZúŸ/óc5KSàÁš%lt]ÎSBЧÇWåü­Q½Â‹™Ú¼%1 œ*Á·KFÏW;‚ib‘¯Z±ïœ Ô!1‚ìÏ‚y²Ÿ=Í ^ƑՂWøù§ƒÆO¥jh·IÖEú®ÄvŒ‘cw®=¡%·®“´Ã”7—¥j´¡—|5ÆäÑ2q-ZÑ7žaÂTúµ$¶ÂÜ«ÖnôÕÙÌOߌ^—ÃtI¶nÝl6µï0½¶`ó_µ–2wظ„úŦ’µëÄÃw}½YŒž™r¿´{áu¡cÃrÙ)¬/à™êDÓã6ãhGùw⺿Mnál'œ†¤ÿÓJŸœ5ŸÚ-µžK¦¦åkÙÙcRŒþÛ„'õ©Ù‹ bqI‰Y‡uÁ˜Á•L+eÆ[‡vm¥ûŠq“‡}éî¤Ë¨])C·Â‚¬maäÉC^fŸ+èÞÌÄ¿Bz0FBÅuȵHpš/š7Ÿr£lè­Ê€ÿÿ¶1e,žÚPuöË?åÎí­‚ÝOBXʆNìpàžDJÑóÕP¹B‚ ÔÿïÊ`ðÝz*B‡Ë½+JÿÓ»=)!5Kkàô¹ß˜™*ÀZë›H°©&Àœ³œšwËÏÏ¢½óØéçé§Þ§pLHˆ ËÝ€® À]‘õ¤Ù¶Yü}ª¬%C–òƱּ#`’¤Mš øp1e›g=.5Þ§wwæCüþíô’i»j »_ÿ#Û^Ôa¸¬ÕOaVQüÉß_áæ}\tü‡ÿÛ“¹d~§Â¾õ„ó¶ì±Åkæ_àµR œVhDŸ 82^Ï$ˆ2‹ø¸WnüµœŠ6W§Ók7“‡Æ› ­Ñ€['CË ò¹•ºÐÎYˆ½]ÅtÔpÚ‹péæ?Äca$ÇÅXÒÉœ®zÁá¹¼±ôÃàÖÎL‡Ü¥¼¹ƒ?¸ŸzThÝxú_Mêís.⻥·ÁU#Ôæ¡ti¡$jD7uÉÓ)]f»Œ½@û{<®YP‰‡.Ýà|”X¬™t› ™òá4õÂSÜïP‡Æ F«šx¦@.ãÊžg“Gj–$fI/¾ë`_Ç Ï“x˜´²’­ý†az‘ð_}æÈú14oYl·0BÅê%Üšäw:F{#Dœ¿‰!—j‘¿.œ¸.ÁînžCÆO ×?¦â—tâïdÉ̦`úÞ7DDY‘4ôskÊvágel»u¢m–?ëVPÖˆÅGR覛—åíÂõ™¾è—š‹ Äb©ÿÕL8{•œÑøÊõiA•Ž£¤Od´íµƒáÕ°l¢k8¶CÕ8fc†¯¸‰ôo‰0Í] ÷q_K ×sTøÒ8:ÏÞ]T¼LŸÚ*2ï–#©lYÇ©LÙH7l*F5ãxêÕ¿#jÂÁ\ö!ÞLŸ@_߯ ±àä½ÔE»Ç-1¯à˜µ‡³Ôìá"ǬcÛ¤Î4ü7îôÄóvV™xѱˆnGÃSî¬ý},Ë¡¹’Ü`Àò¥ôhU³;Þ>>2$#ð½I 霓ÀþÈΤó¾ Ò'N‘¸ù\ýÕž4ÓMÞŠg=0Ú‡{²±”jáÌ^Y0§½ °Oî·îú¸zDФ;Ž#ÆÝÀE ˆ=xå×L‚™£|ca4Õíõ‡ÉeP²R Þæ$`YH>NàâAÒ@>(x±+¡镇v¸ÚFšùw½€±ÅVàµDЯܱák5qwBsxÄü4|•céÖ`ÜT3¸30»?õ£–%á´ØzòúU¹>Šû~^Ú‰íáòcò.²›ªðNî Ü3ùBEŽœbûßò½  ŠÛ¢˜ŠW>Û§~Ï¿ßA{À‘ªñô[¬-U(>ËDÙ¯2tåÃPÎì° =?„x´ê úªŒË䵡ÐTCzpW;kývƒ¤îšÂv=±¤}gD¯MkÆŠ?…ìMÝQ¨ÚìÇMþ<–u ›0Õíû¨®‡Ó 3¦>ŸdiÈš‚QÚÈ}‹óÄUÓÏйé²Ë2lLE¤™KS;ÕøŸæÚYÙ•Ž¹äº3°m30—¸;ô£‘y¨€ÒÉØn£v¼{E‘žñÓ S#ÿ’ê“öJ?. –F<œ^¸^^ô€h‘^¢¡îzU¢tÁ9æ×7ŠƒsÑÿy‰ÏWcAëzAdòTzþ KvsA¾C ÞvZnöGà¡;aƒGÛq…„$üâ.X¾%+÷soܬ0ëÌn¶3Wæ§$ZjŽï: ³?À½ËF$ÀR€åíf=ÖÀ’{"à²(Sî^‡1ç¡ç¡#ëÊ2¢Úœ6Þ“±E‘ÈNxÞ¨ƒG_=ÇqnYðzéXV{L“r`qŸg0Ñ) ðé ^¾Ë1§8ÿr8T9ÝÃ’½šìZñšyM®=…M™ Ð²Ï ¯¢«îzg6üäå¸N¢AÝǹ'³£Àýt9ònær«Åéµ÷½è:ã(ÌÚ‘IÆèó…Š$Ø!ºfµIÒ—Ó™úWäV8ÌîMSYµ)m}[ Š­wpmÔzÖædÈ"}€ñòqpôçC’zÅüûMiI®?7È·”¦'4ÆÐ¿óØ]]Eºõ|>U)‡MöáÝ'/ì›wŠÐe7^Âî”hªÖó7)9rCûÃá™ÉP¨y ¿Ÿ4¥·ƒìÿËtæ{¯æí¶¦ó"ì·ìÖ³7™²ò¿5ñ.ÿ)Ç’´FNÝG‡=•¹Ì%ò;¹²rœ÷Ú\NdETæf—{ÈÅð«‚ý1’lÖ“:,‚¾³i;݃[šóq™'pßB¦Q‹"¸fcBÉc7ó4¤Ût%9Ó4¶bW>mç ÓežHä\UéãO_ñxM;¸8ž@ŸëÙ„¦‚~‘HpÉo‚³WÂÅú³Øâ à´w,+ã/QßZò6ð1²ŠÃdeyÚÅÃÄý6ö?ÝtðFÅ8JLaº‘ 5\jË c“Øíö t/oÇ²Äø•/ÂMâf”= vì²'&?Ä]üš¡š–Е¿‰ëÊR 97(;¹ÇvO!¯<™úH'=ò!ÆËÊSotcüÌ Ü"4úߥ ˆÝ%Lÿ~“óüéÍÚr9!F;&Ók/?@js<ÛÒ.Í.¤Op˜;i"s{þ SVÒËYϸ‹‡× ¯¤›ù[ O:grŸ´©ªñn}ÁO a‡Oâð/Ê[M¼×3ޥ⦆ç ï¢Ñ:¨šÞUÏeß\J£zºÑ´Û‘›µ>–>è\«_ÚS÷^MtÊq‚²,f°k7sž>¦Ï ¢:ãé[,ñ†\6šªF³àßo8ÑøèöâäÌry( ߈\;bAozûaÒ¹ÜÓ]c û¼=`‘€öOIfQ5&¹†Wì˜tÃ.ynv&šSæ^¶Ç—!¦lkþÈLVƒ1“…˜TÇ3˜/ŽárÉžµŠp8ú6yÑ-GW<«åzÝâa¾*@NÖr*™gõø¨î!ÈsVPWuEz7ô²` ˜å(Íêüó`†éX0ʵ.œ<‹kD~¢uh&Ìÿ6í´ámîExk ßß¼ƒ£ÖÕÜ“Ý×xdeƒ›0Jõ->¼fÏ4“Wâ„}šF±iùvŒZñG\v“ú‚9Œ;‹ç¼_s7ÕY’r.<Â/“/ˆ•9üÊÈkhëoȦ/ìµ6(C¾ØÕwµl¦çuYi¹ùC ¡Ö¶©4e&Ó0ªÉNÔΡ†9\µ¤_†FqK?êì“d)þM¤4QŠžte—#þ UO"ÆÎ*f)! ðÜa1Ê>%ævÓXF[Íž~ û¯¤)½&Lÿ˜§S.{£E™hÚfRa*ÍÀÁŠO¡ñUë{Ðsû£°­t;¼'ï1¼ó> Ÿ΂ µYø÷eðD§¨õ‡¯×a¤ó2íK÷·RƒÅÄì™Go( Ñàɤ,è26;ÏgB±/`¡KW³›qŸvá^8ÖÙµà—oüìµçìÓUæp?ŽŸ„n—{¸bð"Ì­Ña“õ¹ÚhIúqÚýQl«U_äØŸÜYð=}ìú±FüÏ¡ÖÏ£$Ï+ƒs|úÜ ~Ì}³=ËÑ_gùpq¬ÓºÆwÿ÷ Ý/ˆ±¨ãðõNc–iÔW"ЍëŸEè÷ò;wò™¼-·åWúlãž.VôL”nêwªÓZ ›b érkWÊ•FarêOŒ˜aÀôŒEÉ5õúÆê/βWe 3'ÑŠ4iÛ(‡RÐF©âë ÓšSšïáêÞh<|y ¾¯\a!žôê‘\Ø3ψÝÏ}/í2ñ…Wˆù¢›ø*×5Ê^âÔ3®Ì p%¹Þ…µ6_`;6_%WÓî“+g QGê7)ç ôOí%TK?…¥™ÒòÙôŒz&L«ÚÈÙ«²Ifu´0g9L÷Ë„PG]ö@=‡æt#–Û½­øî­ ›b.‡;ׯ¤iϱúàcä{L¤éG#¨œÅv:¯Q†­,XÈ€fÀQÙ“Ìj»ó”ÍÅ{ÏÝPæD%DÄ'áj•;˜í„ᯫ@QÒí>‰¾K;ùwÓàYœ°Cä¬å´õ½;³©w¦íyƬûƒ5½6e9 ¼¯fÿä °>)á5SÍLÍ©ÛÓm<Ÿß 8Ë‘ãr‚`Ÿ Ú†‰ÚBúAv(ðS~N>DÎxRÑ|o¬¢.ýêlØ@™® ùüÕèàNm¶z¿m pÇmÃ×VmQ!ïWôÁ½kòã'Ä¢‡Â6öwœ3åÍ+–J1ñW7à€‘ ý*›Ãe];Êï¢É–6”·,gJ;°OýFlÚ»JÚT3•ÚGÀ“êèð¬Ö†þ‡Óèê}Â,x•Þ5¤aKÔK(ÊxÆëH8DÌ~&7UgÓØãc îÎxÉ[¶øAÖZX”¢Ê¼ç‹SœÎǧËYÝ¿H^Å•ø¢8›†®ÀmêËØäæA,YcEаšÅ*¸Ì¹‘56Øãb)¥NpÕ/û£ã¶'p:x înœE…§Îe¶1óÄPbf—‡ÄĨÜ]&W7ê‡Óêi·ƒ¼o¸ ¥Á†ã3H¨¯*ñA+ÇKèôòvi ²ö™´vÔîßËܹ—53ÙÝÔ$hY[ŠÖ:MÜo;-(:/Oͦÿiß­¦‡~TÀßwÑôÿ¼Ÿ.O?†Ldw¯ú€×ãdnÀ³ìgnàFjµ¡ÅC†e$”YàÄ”6˜Ò•cØÍÇñÒ•+˜ö•5ŽçËN0‘?P›vgo+‚µqZD|ÞWÌ(Û?ø0]; ûþYb[Õz¦Û]Œ5iÆŸ4¬|QŠ%Öß¡eÿctŸêH/ïÈb#µ…¤è„!ÓP¦ØýVr4G/gjsï´ýÞ³søîUá÷7QvI…Õìóý…ìÙšÃTvþ”òÕKÙ¤ÕÄ®–ÆìÒèza•Î}p^%–ÞºÃÒ,r«|ÌØmÝѳ©‚wKw°ikuh“èy¦¸õ t† gqo^,T`=@÷½;=] QÛæ‘$¬Àæ<3XÙJiPÀKZ>‰P©/4M`{kÀ6{¿ÇSba_z3•HÐ2£³8láÈ›xP~U6üWC:óÝVNð¯3ýASo±8ãý ômµ2~‹v³E©§ÿy·…my•ÄLO²!É3@Ý*ëÉ kÌž‘¦Î$pÁxºêÕûM›‚_[ Ú½‰Zþˆ£Õ‹[À’ûˆûb²»šç` oôýõç²Ç›àmï^ð>ð– |¼ âϪÓ>?zc‹+É^›o$,0„N‚¶ÿÜŒeîbTW_•Íy~›‹€Ýð7êáÙÈ–}¬§g_F³Ë›Ö³{6Ñ ûâ* µe³ç7ÃØ·4 „ÚhšL¨þîü~è2ÍÝ9ªÃÓé7ùEðÛq›vw XjȹûßÔY áÞ¯2Þ·PºÔl,šŽ@mÛNºoÙÌkáï%™‡Í²¡]û”fâ¿ÝÅÌÑ+]ï¸åbyôjc¡“¹ƨå¾d÷Æ“‡S?ÁÌz)¼ðW T[ehßø±Ì¿p*'ôê ™8^õ|áÃi8š°"VáÄ5¾¸dR/6_¼LÞÿsa)ê"4>`­ŸÆŽúd Ú­(´æÆ'Ú~WUÀ²ª$X7aC±^œ}UvD‰ÐÖfGNÄø1¼ô³'}}”Êz¼°¡7TŽ©× IÜUy,­Þº]+ñÉ'+fÍCN>Àï*Ó˜¦{,·Í ÂgK蟔ù88¾ø×ÄÙê)Ñðâó]þŒiÂtRÈObüw¥·ãúeA@@ |å#|¸²ƒ«8A{¾-„h–L¯x²ývÐ$^K,E% }¸Ÿ/b¶õnô¤ƒÚ;+²ä¯™˜}+9köe{;ÈÙû:eJ}¦U÷Î@ÁÕ pCç7ÖŽÛʞɌGM…Ãl$Ô‡«ö2dy_·P©Sjœ,G ®ÆúXX/LOœK…e¡˜q½6†×²+k6²%å—AGgKÙ‘»ªœáí yê¦×ÉûüÑ%a6Ý×cÌæW1™´ÈR¼_ŸkRr³˜{‹{$ûßnW`3”¦Qâ3‘å£ôA1¹n°OÌ‚¹v+áÒ8¾˜¬8Äœv%f——P?í®y_)ºíOhiàü^ozÿŽý³Zkã×Áïw%øJy,#VµðÇã=Iï™ ®Àu‹î§8ýý!pÆÜ Ídã•0uȆé+áLçìßy÷PïÀžæ#œXñ 0ßX ¥Ù‚‚×p©ú Žý°÷ùÆ­»ù•Í T]ãUÝÞc÷!®ï ÔVÙRïU¦8ãÔE,i¸”:«QÝí˜;VB¦_ÁÑLèÒúŽÛ±`™[-7X°‹M’—cf¿wS]Ý«Qt(ÿ¸¦Ãm.lº+x]ÍYäç°-u_gB¯?ÐFwÕ æÙr…œßÌçî;½ÄÚžÕxëå,zéè}pûUFªWZBäŸiìë#ûn)cz¤K×UÃS6ɨöa¿$O¤"¥?0´:‰vK æÏ÷Ž ¼¢ÔM·ìrFåW>¬b³*äÐÎi!—œMl YØ?yvýV9@Ð u³|pS4#xni'Ñ]ì4è£9û½˜%}ÝBZ—;Sé„S“ywù~_ád°¸üä/5ƒð™¯ WÈ£.Ka¶CPa=V.ÈÇy]`}÷vá½#~9¨åùLM¾#žå±r^} # éÀ»å´fÞn¦à] &ÿÕwÆê)qìkmE}Ø!'vM+„9fÕaÅ£zº¨G wmi€E©FL^{eB™mÿLÚ7Ž&;N¥«Î¹Ñ…Ã@Ç}snÇ––ÚRQ‹ßÐÛÊ.`¤WÆ“ÍV°#c òÚ;)ïâ?¢š.‹ÁË×Â0'E‡¹ML{Áyvóƒüž^ -Ýâ̺V‚Í£ÚLVB†Vö³âhþõÏÇ©¿¦6µþ·’…ùsŸËè®9ùøŸVú‚õw’ÄåÀ˜ñÓQuîQxýDõç' õ%ê ¯ËZK9I×AH}v‹ûøo²óáNüZL_™ZÁ õØÏç“Û×ýˆGÖæÿ4Ò,ÿy×½ÿ©}¢R»¡|ˆÚŽb*¯N9šX*•†Ñlý‰îp­<åõ[‘ß~@óªNb¼TÏ~Ø:”$âÀëìÔòe4¯l«çt‚^ò-P•ªÃ-´yÕ2°÷³²S…WËì÷cäÜ>$€ˆW$ݽû]×G‹•åèý{±LEØ9¬Ú‹‡›æÐYOŠí£Ž;â¼8ÖS¬I–\Š¢ïÖj­ñ.èóí:x¯NEs 6°c94ƒSæ07(" ïΟâæ>ŠMPeò|/úCD]¾îƒÎ=àl¾â÷ÉÂSGÄ¥¸{¦¼‚gÏÔYÕ¹Oüã‡(øpøèž0wï@.ØÐHí~Ã…ÜA2½ oçGƒC–8»ô^lë¸óŠ¢qÿç4SÔ§‹L¤·LõëKà°^ 8Þçk¶?€Ëë9ã[ ôêàù+‰}IS?’ñ'ñùÅÃ{-¶Ý)Š5vª¡”Ç2.ú¼ ÍÊOÁíÙ“™:§ éÓD  ì0ÆN¨†#Æ5ðÓÜ–ßmÎ`•}6“VûZÿáÚ_Iÿƒ7øûY᱕ 9QJ®¬ûQ_Õ÷½o2$3¡”1d(IñÞ}(…¢4 ©4Ïó< eÌ,CfIÈœé=û(¥IM’$•4i’¤þ>ÿµ¾ý¬õ®×rï=÷<ÎÝÏ~ž³ö»_ä®ê³ªŽ¿üÞ!ø¤HŽ“aKG­§R›ÑýÙÜS¢ þ[/º¨8’¬’~ŽÑ#þKýÈÜOì›Zƒ‘A>L÷ùf²*,‰[û×CŽÚ‹V;”ŒÙ…S?‡ã¥úÌ1ÇŽóéwÁ£J¤0ÝÖÏC3¹hîÈã Ü«º7âËf+ǰY¯ ÙGEI9ŽÎ«IƒÆIÉß®Û*ý4cö1¦pºi%*iƒ›[Ú¨F‡0™Z7Ÿ)¯TÁñ½Å༆’®ŒìO¨«bqíø÷¤NÝU÷z(‰zImÚ”ÙÄY’dfÚdÈÕœCä·Òê*Þç!äͺJèµoÀ_MØ¥|{M>ñ‡2 àSÏ!Ô †3ïɺéwqÑÁ&L[¨Ä’_lfÚÐcÊb¡¦g_þ ü¨ dé?ŒÌÇmj:8÷HV'üæ›NXŒãÇM7`ª?¦àÊŠv¬Ú̉w®`ÊáQà|5“œ·rk†ƒQÉä87vãhvã® ËX¹u,ùÓûŒž~9‡Ü;@àG–Ù-qGÇMevË«øu‡´˜ô‚Sؽ*»;§b¨©0+~‡Õö×yk Ó]ŽÃø0â%^¸¾~çñö™òp…þ\ºÍƪ쳹=0›,¼÷³=ÈÔ³Éü‹ë`üÒ¼Q0RÚüAkå=°êΆšĄ́½˪ÊÑAD‘Ä*û²¨ [ܼ‚ݹæÇ[Œ˜Û Öü¥Öd”ù­wo¦ §8V4ƒÜè Äб™·©gYœ¾•=Mç:—IÀ©y¥Ø»S˜åºíSã•LFSŒ5—ôâÂBìèWØí»‰ìÐ&óoßû@~ìÁ½[ØÌ ø<ù|Ïsg‰Ç I}ò5xÆO$¯­„!2à:§ÕBÞás‰: \¿]€°ê ÙÏptCìîm@é‰òÇÂŽ´Ã—gBäÓ$3V¸Ú–YÝFÿ>ŸóÝΡºd:>ó ƒÑg塿ù:~Œ¬0Ö{ª1é Æ¶O­ÎJ9Hަ£iC$ÿÉÆó»RÀÍÀ í–UãœÕÛ }Ét(ºæ¤ý¹-ß_‡°™ûÑ=×euÙ]W„9R³è¢i'±íÄÒº<½a'Ò‚Õr°nšìS‹A›=?áÖn òD„³–6Ø´ç0ü>:üq¯?ã£]w0ÎTŸñgG£Û)Ð,Ã9‘¶Þó¢ãÜܸZåTú"w5½äÍl£¿â«ô3ý£¶è7àd9qÜ¿Y þ«}Þ>õ >ͤ®’ðÞÖïxð¥I(VÇ®ù¸ã{1ú4¾ÃßÒNLí{0¼¯"’ŸÄÔב¢ýüC‰ìáõ…ÌpIþ>z]¤RøŽç½ÀàZ5è6΢Úƒ\IÒ:NfÇ1 ÛŸ‹[>¾ ÎçrèÃ]÷Ak$ÓÒÊÃè×n´g(‘‹x¨Ï¥¹¼CÃsi¼øÃܪ¯ð“‰34vq9¢2Ž?!ÒLWƹ)±3 +›Ôà’°.77´î™%‰!Èoì„W+¨z‹² Å}r¬¥i.;U}=å"ÀB}“¬FïF2*.’‰ðñ·Y>·ë§>^{ˆ¾mw`ô2>ç°.ÚgFAÄÃ\î”/â­%¢$®+ ‡#pùç—Pî ‚õvzLq­‰–©ÑKÇÀ¦Iamb—ž/ ågвû.zW N\!Ýòñ/=•™ð$Ø¡ÉZ8w`ÿ¤½l‹Â)"vþ 7/¹7>œ‰çmÕßôÈUpbÎ÷)?û».séâþë¿RS. '@F@_¹ ×X“•‚ÀqAí˜á5͸ögô›¸²)-zdÇŸ‡ ê&ÎEÔ‘µZÉ(õtûÝ…'eݹÑ~²$¡BŠ8 ÀvisÆ´¢P§è /=ï®+´ƒy> aoöøž)ïVº‘µëMÉ-­ƒ¸_͘ùc'sNS…y:ì±ñmê FäÒð¿ž×…[ÏÁ—Ü 8ß ˜²D /æ˜ñ“Õbê6étÊ×ÐÞ®NT–Mà/Öߌމpg$ç— K¡• èËÂèfwŒ‘¼ ÝBÐrü*¼ä ÙÚƒöKnÁ‰ ³hìÉ&Ü»®Šwpù9h©OãŸ+ô3Hí®sœé¢üâ×Å‘8iƪj åçaü,SC³‘¢ât9Ü­ÜË_Þ¸ÖÌõÇÑHþZ×Ót oqíÓÓòdÂM%|zä:˜¡#qè:‚ÕGâA°¡]³-ÁhU)VÌ™‹µzÔyf(?ð‚9×û„ÂÈý!»¨†[9x <çÞ¡†&O ™ÿ…ót®à>”Åà'¹CüUÑ7è äîìä“dˉн%[+¦bVÉ4ÔV¡·Æ™} 8¼ñà–ÑÄø2ñ;¼ B®SHpѲ›Í\—¡dJ¬€º-ϰ;G 8kd“@f÷+ß ¡Ò~‘Ðÿ7u-ù–œàðc¯s¼ïƒ’Sò9‡ÚL:ïÏ ®¹s–µWQ:{<ìú4T…äØÕîÅÜ‘k¢ub:Ùù÷LÂÁ( øâãE¹]‹ù¸óœ ‰=]Jç‡ÃöúT.fÒ~– Q¸ÆÕé98Ìý6µf7šÂáñÑ~¾áƒÃ0UþLô*®ë;*KÎMO»þÀDñMŽÄä™jŸÏý¯—3κþƒ¦i"—¯V€–CøJ½‚,-ÕÊZÍÈ×OolXÒ'ËàÓn/\3} Žy]ÂÙ@rÄǰ¬÷ÃôĨՠ¼*’ǘ£vÆ6T-Ûƒr*ÐS¤ ÓÖ8²¥ßÈŸYîœÖ×RÜ?yˆ*÷ª3‹"IØäµºQÐã'·°Æ™½²ÉæB$öâÖEÂXyÆ ²Ö1íþ ˜§šŒþ¦~`¼U3¼ SE“Œˆͽ%4ÕÙ ,›1B? ãæ_ƒ)'7ʈ<:sðŒ²zŽ¢3x;I±k¹Ìqû²jO4—ï¸Òƥóñ vÉ5Óä•Ó±j¯ 9xñ[µ ÿƒƒ³3ñ…¥<»æs¿¼§Ý—€ô{®yç+?jxã€îmÑ ‘°q Œßs–m½oCÔtÌðÊÆ™0âR¯aç¢Zþ•œP.5¾.GL#ŸÑ}§{1+w2Æ 1õå­(ä«‹ Êçl}æÔVœÆ¯+¬Ù°×Nm®2q¼X‡K{’õ[4ØdñðFF‹y=DÛåWpGl„ g‘¯‘òlÏ™a¸lòï–_àòùqó{CIÉÞHšz·–fmªÀ[ɓȭ÷JLº1Œñš õsž¤Ìá.ò׎ÀäÙ9üÀͫکŽð*/•ŒQ)„þÁíðÑ÷øžJdã&3çô3ÔMe–ÜQçž½kñÜsÔ¢?€çæbÉ ý©­þæßèkwŽëu'†pÓ–*‚Õ’ëëfºÜ£4`ï5IåiœßV¸¯þ;/L%ǿӠ“¼ÿTÖ‹c­ÓQ&cÒIïþRçþt`‹ÖÓ˜†ñ(¼o ÄeL¥ºŽ(“]„Ù÷í±&ÈÖxL䤲_R×r7 ]-‹ÇìžÃ¶{ÍP•×…YV{áXü>ph"Öglè†ëp ‘Ï\Ï-¶•"…½h=e„·\Lpæ´àØïm‚Oñ$¼„79úÄ—¼€Öï`' dEO'ç¸GÃMŒàª`søoúUÐ'»ÁÖÔZ¾ƒm;\ǯð¾)ôâñädÜaú cëßÒƒšýÜq9Ž”÷ÜÇåýo8¥/ÒÄÖ͆ÕôÈßyÔ''»›jP]4c3Ì‘gÿ$Û2¸7±åHúÜ`ó31R–ÄAý ØÞcS·q ™Š¶¾ý¯Žš×.ñ¿U »£QTÑU‹çùÂE>w°Ã—= Â(õ£üÚJW¶NU™¨o± g@ˆuö÷ÐËbÒL&ážjAÒ;[¸‹51Ä/: GÆãåJE}ëh¢yö?VbeF3!úíh¶ý:í%m¸ x0 *qÎÄéJ)½DÙè-v¼þ†œ\`EÊw¾€;‰ªP0,Ê&®x†ÛðŽ}~ÊóßT×¶MÅi…O@oçðph„#&ñÄÚm[»‘ƒßÂÀB¤Ûdû>]!£S,ØuûqdDz:óý0ñùpW¸2Îf9xWˆü×·ÙÈ«fü e:åú,×pê~ÿÄ­{NDc#áÐ_uËÍ(}ßÊ”Úå˜Ì~YòÔS“,šC:Ûç1¹ì¢ rƒŠ?{ŒÁÇ,XFB;m5ðçf{‚¡£CØê?o ãÎ)(7ˆ`¶³ÎዱJkZC® $‹†ç°ú|X™õÕiAHs3.PÓFü•_,YÌÉJŽíé•’ sêåŠO\Àú$üQÔ]©áßHU´ÚxÇÊb’“/y»,MÛÂ×þà‡EE°öÇvò^L‰ù0³ìj„õ^—q¯ögÚþº 5g†±ý/ÊÐnÝ\¶OXÎ{p{ÆÎD×"St}+R/ £ô_jÚÿê-ì=»Ž8« aö¹0¦Ø˜…ï³ï¡ÚòqÏ,¨ælñ]x9l]ÖùS’ûc0 »‚[Ò3Ÿü²YÈýWÏ-§'5’¡¥ò<—m‚úœ6jyG ¥£üØÝñ¾dÿßðë„͵ýølù*fª×B]ÅwPš”Ϫ¾F•/`s0Þ˜® ìñ ì dáY¤ÒÀíڤ˦ýÙM’ÒÉ›'pÁ°~J7"?÷‡¢67 îæ ñŸY£""õ±õ”»£ÇRÂâØïo6ÄQ¾†ûÝw L6AÈ9$ÉÛ ¹K–ˆŸ˜Êµì©‡—¹8¬u—Š~àjB®ãeù(¸µk iÚ“.æl›˜.Ï.ÊU¤¦ÁÖ1 °ÆeÞV™ËŸüN›üýqÜN ›ÆÝÆu&¤os*Ê­·ÄXãûDt™8ž«óüHÝt¢ÿD’µÞ9c¢–VW®¾ì ¸ F–KÙÃâ“°H^ ž¾q'f{¬™¤Ä<¥6Dv$‚–ñ¤á{í¹´šôM›Â\Újéæk0Â1èsã ºA½YChrJŠŸP…Íó"¹‘kpçvY6ãA³|0½“ÞǼxñÌÝM“h,·ÞÕlé8eb/Ðþøž†¢?k4œKÖÿú“uÈü@_´°ÐGßùÓHƒQ2;üFŸð/²õÕä”Ý ?jy3Ú¯HÖåWcí–:¤kRä ¾a ýÚŽ™ïýyúO3qÆÔÃd}Û0xˆçÔüÇ’ÅæÊÌ»à$؆×ÁÃ!"l9ƒ{¡£Ë¢µ‚ÉÈßYåÑ6Ì“+IÍÞþ’žyõÇmºKü}1qùæÀôñÞõѨ“ù«Ç¥à1ƒ³ø÷üˆ_YÊ-khÁ§Æl­ Ðñ|<¬ <„Gô1ôÍ$6ThÎ~•ë0-™¿tòqyªøÁ–‘>;œàŽ3bÓö©¯áÎíwØØÙÌM(ºÄô“Æ1§å¶8ñ÷.´IBåK@š]øÞ.¬ë®>vHÀ$g@±#븸QŸa†ýÐï{ÅûoÞ{äÓÑC°½³ ˆ™2Ýí¬IöùŸÕ˜/ücéòdgTúlLÊÒŽÐH ?+žr=³5Ø—]»]€uØŒãz:l8¯œÞ¢i2c´Œ6_À‰&nu‡ÅÎâÓEŠPè8Ò*â¬Ûñ`õ°&zâ¹Nœò»¬îÝ:YrWÔŒ»¾°/Ý…~#¹&q9ï“ “óèâÆ,³ÃŒ²lßqŠâKÒÒ¨‘(*>‰éÑ˨vŸ»šßσ`Árì ëC=0á‹÷ÑÔò,lwñ[0¯ü£GÝ€oß• XJž©¦UÑÝ쯒 ™þ¼~Œèߣ§Ù„ÌI¬2ºº‡+³ÃñY8jT(¾ýœÍz¸ RW²DÑìöÉÍtå­¿”TÁ¨FW2üv2áêR°zÖ®š'ƞΎ…ì­ÄÖºŽ³(xÁ=hIćÚoøîÇyìÆNaò%þ ®l»…&‚$i® ô/ö%lb¤/:ñaÛÈžÄÍ8P®ÂÄ‹ùÏ‹Ø%i#¿,¬’!§lElϨF±Š\,ÜWÇ[²mѳñ€U+d¸º¥dɶ¸æ%LkP$‡Z•¸ÊžnxpR‹z(PÈ~®D‚äÐ7&bÿ®"~¿9óà=5’k[”Ùô‡™ÄM5ß&ÿæ§‹FÃî¨ÉlÔÊG¨ðr3 ìÐ#fNÎ{avb57r¶[¼Œ ?hÅpåPf»^^íã&i’7ßš¹ÛƒIu^ï9öâù˜¦þfõ`<·‰M^ Î&;”÷ò¶!³ˆ”oÙCxºn=«‰;J¾'ÉáQâ„Ç66Bíç)à+·›•(yr¼œh¢ý½âl‹ª?6|ÐÃTèʪåw›ÎįÅ^°RÛ— Ô2• \pc!ÆsÇÓŸpüÆXÙ?@ìcË´Û ûXsu©ñŸ*4vSìN¢û֔½³pü'E¼’>½_ñ6>XÃ]šÙŽÅS÷ƒ6¼†ÙIjXéÕ‚K–ú“3¿Äa¼îmNÔñ/—™¾œ;Vü„®ÖHÁ-ÇÃó¸ßTtÅm4ûpŠþñî¤bÚ6ìþÙzIL‰k~¾€Í©ò&?Ô‰ ™3ÓLz®5áÄtVôæ«ËˆÄ}ûÕIþ'?6jírºI“é/W!ûíÃT+öBĕ۰ª?®¿Hd‡j–€b¼°Fî%ÅHJÙŒy1äû;r§µ S,ôØ¡ÐiLfvqÚ©K“4õ˜ÔÝz²VŸ…ÝVÀÆå'Iíb?®'`™·p9t+lj1fLtu8xik³"E·:+]{ÌÓ `ÉÊÝld¨ €%ÎøÂà Œ· 6£ ìî²~¼¿Õ‚½’&ÎGJ@¢ÈwM·&}SÆÁíÆÄZr9¹; ÖOÍd‡—žÂí|vb‚.µ0Úž ÀÆk;€M‚lHbFæXØM!ÞÌìŸKfí@]ÇHêRS‹r5@DÉk=B–¢ÇQUnfèñg®î‰æâV4Þ+ Ÿ¦³ioµÙ‘Ÿ)Ìôž5=7$½KÕíºy>0Es ÙßÇîïgìŽA#Ú|Ug&`í¯G~š ÁP½qƒÝºÙË;V·¥Ù$ás0àÊ]³âxipN¦Ý\^C\žÊ±ÝGßÙTo2'ªj±Ê*ÊG²Ru-NàJZ¼àë‡åJ"xYg ×Ö’}]¹¤Î×O®¼û}¿é¦01ΘÌ.¿°q?sI@*Jä@#ñ ¹œ,I^¬Æ’;òØAõÝŒ÷_½q¸2õ_y´ã¾‘,ÊñÁõ¨°†‹C’Ø ÒFµK¹’YgHözܨ’{·Õ;YD›wøAäˆÂ½ø}ì [HÝŸlÃ¥Z `ØqJ|X¤D!J7Àiià‚ÆCsðOú<&Œîè¶eë×ê³ÀÐ¥à$¹ƒÿ(C‹Η%9Fû¸ÚåØ×YEE>ï¿jCÖ¾^‹×\ÀbwHèRa?ìñÛß”’fedÁ +Òé(H昱O‡>ÑMß—’ö#sÏI([aÊò¦žbQóïCx¶ ×kó«’¤³ñÍ"-¶ñz¨Íbæ“cÀÐnÔ.a?oLj%øõn“µ<Çk ûîŠ/½W3KÛ7ÜË¢(ÿ¾€9”XÒƒ%Åp`w*̸oÂYy§c8³$ÒÍ ¸hR,DžkÀqò¦paÅe^ø˜wüø*fL^QÍE…¸Ò²þë÷Leõ˜¬ß%P5›‰gUÂÑZ+ƒ®^âKgˆ³ ²£‰‡dG%j­Óf¹%öp+ìNøžÂ‚W‹â¥#j:=æÞ¬&º ÅT@¹–Žf Ÿ¶“P•~¬µäHõ¸ $-:—M]ûoEAc@ŒvÌšaÂÌËhø0•LŸ7Šm.ºÂŒ‡0Wã ëüÊçÚÎ-dBýè©–›Ä|{ù^g ,³ÌãQì“eyý(œ$JÁýŒÔt*°]oQs…û‹‰üöl™çoYË×aw­â qBïÎó?Xùí‹^K6óåÈÄ›állª «ØôÇüP&FÝðp•¹+Q‰¿6Šáë-†dÌivæZ,>êx [îM`‰ŽSI¼b,H6s—½¯áëMëØ÷W÷¸Û¡ÂõZ‹®±ñ¶ª¬ƒ³"·»Ø¾èxâà'Ì,UŒÈ¯´ žë%v>ùH%Rk&Únwn#Þdi%÷–•×?6$Äǯœ-áNTõÀ~ÿNs«2SŒ9ËeåçBúɽdþD–¤/ÇÎÙ¬@om!wW…%lcAó’ðÛ7â*bÍÆGœb±‹ôë3ŸwöLÉÝÛ“Øë­!œãR["ûBUHe±+³†È­9W˜YÉs¦ÝaAÖ3“ÎJÆSknpÁÑ“ñðäløÉYV/FÈô:öèJ4ÞùçîPÃÈ9ghaÇt<~Q˜œ q!£[â9.ê,›åmËöï[г½/’“mk˜ÝÕhbËyƒœâ)Þô…˜ eêg€CbÔJøÂâœJ’99›ÿv„‡¢e¹)Ùèy_ö©0åÌx<é7aÃcO›Mo;&%½€Ýxÿ˜^›82b—0§×ÓYØÏo8û§4·oA'ήD¯Ñ­8œoÏ9¸9Âgß}¿s·#¨}Ô}FÎ å×÷ނܿ۸˜Ð|îM7w~ÌŒµp"öNmÛEƒäASw)Î …íæciAĶàâ/|°…L+D>³Ó#sÃþ|x„3T ÉŠ}òXçWËëÚ(LHO|Ë{À¨%ИñØ §½ràÊ¡DB$†Ž_cìVñãô/¡É…‰ènF¶tª±g÷n¢où¸”vŒM½í„r9è>â=rCر³›˜À—åhÿC‚D¶8á7z趃4?ûÞ‘@››tÆkRÄ?ÎòCäHt‹ iTÖä=c‹­åàÒ‰r8ã±êëxáÓo–Mb–¼A® ŸK+ÒR»‰{vÏ‘‰lŸÆÎÅI²Ó^zð«õ1þûƒ·Í·Ó¶O2™ûC] Qí7––ïp4"ˆÝë"»¿æ‰”–ñ>%ÛÜïäÏHØü=Üã÷.œ÷ŽSðf› XMWÙ0Ød^Wæym²·B«Ì_®õñlzwÉçOñ™Ž,i~)ÌúuÂÀ«Ä†$Îf°sÏ.³9®l膑ª 4Îer¨NÏ7ºbq ”܇C[=Ðaü ˜.9 ïÖkãX÷Õ|š{}%  ³ï‡+¥¯PØ[‡œC3—?äTE³Pa0W6/G¿’ÃÁöG´viÛ9—vä,—ò8ÖY÷Û«9瑘ýØ„4ÜÌ€âñPËk,A[2îÜwªÕêJñ1TàñZ.~ʸû’òTÀÿ üÙŸDWªCÑî1ìZJ)ÜKç¼ê–‘ÄÓ×ði 8½ÞÁÚ«þP½SÎtûWFr;±ôÝ 2üI„ˆŠ ¢cÀsZcî â†â$%<‹x˜å»ã-ܦ•µÁ,{?GVœC4e/ɳóùsn!¦í1§Ã#!ÿJ„„tçCç^ü¸ì }6÷/®“æå½»C·~ù̥ϰ…Iõ³TŠ”=¾ …'”Á{´³²u„³œ*úÝÁ/“óØêߣÑçïkP™ºŒ Ž›Èˆû1V¤ó wœXBx“}‰î†ü^Ÿ»è9#ŒŒ[p…5Œø3£ÉÉ,òÃØ\ãÄ{xÈš=ú¶•ôšq$)oé]+FöDDZ( ARP"ÁþƒâH™ÆdéÞFŽ57€ÌªSìlñ!¨çgrs®y°¨™£ÉÆ#÷ÏñÎ7ØêÕ{È÷cçù[ﯶžœX¾‰DmO!MlØ¥C?Iªö9Pÿ~†í¶m$»½np/ìV7ŠEÐ‰¦g{b?ý^¿0µª”a²æV°h¿xÑ"lq°€k[u‰„!·Y0Ó?ÞD†ŸÅ↞Ýä‰Óh;‘õ½|X}ΖìñÅÑyYÐñœƒ÷¢è¾‰Ÿ8³³7p·¼xsÖÚh²ëGYúôj2¼í d<Šd©!¤²ª„ñ5uˆÏJQ2]#Tf|ÆÕÎVOß ©xoIVz|2):ì«&ïÁui÷¸ˆõ‡ÀÆèÇÏÖa‡OÝBç|˜ã¨Ê¦šìcòOsáÇåÙæ-¨pq…–|¥gÜ\Ê+¾àqùÍlZj!»Uù”[v÷"þ¬Ž»÷Þ†V|ô5<Ë X¡Ê“ř͞-tÑX%àl”‰òÆósð.§|cL7æ½³!Ûƒ¼÷©\_•iÉßC©G=ÀOXƒwbO“}ð¾ÕÄ@™K$zÄe&wth_M5îçEcåe}¸þp±k9ÅšÍq«ñ’‘èÅIÄSü·fÐÿü|·^¢ù‰•Hj¥IW˜68É6àŽÝgñ¾Ö]Þµ‹KH¼í©ºH“N*½ž‚˜÷Elo̦hˆ’M£ì°¤7‘D4FAö¸Nòú@*ÞÍâHþýl?(µ ÜS§ÉÝÜk òk 'Mò,«ïŽùßžRãY6xô‰–g«±åµ‡ÉŠb$²%5 ’ƒò½PYb95:düì|~Ë…{ð{…ùº‰?¡7“òPÛL‚šIõšüïß̉ö“ùdÑ¥¥ìëˆï=šY¯^ÃúdO²÷c6Bìh6ué[ÔÞùOtMÀ¿ëîÆêAØÛÙ8쿜œq eÅu3/–ás•9d¥æ=:ë²=ó0{õÙ“A\GŠ%4̯Ӟ¸öc´r Þ5mÁ;¯ƒèï1‹™“5½œÇòfVÁ䜹,a‰r2d!…Êäã9v¶õ$¦ô…r9bèi‹Ç•‡1;4~Õ›Í æi‘·…!‘FRrä9½«1tíæq˜MpÂá·˜5Y‰Ì6Vg+-ná‰#‹°ó§1“Èt'é“ï=Üç> ‡ž“¥&ç?¡ÝOãÌ 0Á«´vÅA´¦Ùxù10lÃPÁ@˜áч7LÈ ÞVæiË–šnb+5ìØåM›øm•Fð¹q»Ò…y]£ÂÓçÏ€1“Ù©™ú$7JŸ8˜O³U¸Î®˜/õ•ö‹±gñçÑðVfGOdÞVáø,A²eëNnnÁ_øï•f5cJnò§¨Ðsï0xÀ…IÚplÓŽR˜_;Ì»¬ªˆ`!ÞžõhÅÿïs§ÁìàVYrÙpWšZ ‡7ù2g[3­¥oÎÑ1_œÿLÝ’ììtnFñàIÀƒ&K‰ÐªL0r:HOåÎdã,„ØÂeç©évtþ-ª½¥ ÜNÁü»ãaKÆ/V4£¥¥Îd‰™ÚÌÕ‚wJ†$úësôÿ껚fáéåoÀS•ë®'‰„Yx œ)ÎáBŽYàÙ|K²³zÈ&•ÐMó§³éÓÈÚƒ§!Ó5‹N–nä¬íE9þ–Iû8—×í¼Åi“Ay#øÄÊêô^´Ó˜}D§Í™Ì– OâÌödήñ*¶è7ãÐÅ(¿#Fgaâ^V„®ƒà¶˜7Vý.¯›"!ÊM=°Ó’7µN€ûïalÙUü“´R$XNâlÖ3¦ŽYü‘±Àt„ßÐÀ¡k6Dg! ÷_@µT]È[Hg$€©®?9\bE" 2@ï…8ýÔ´“l¬D±ÂrþëŸUà"µ^¸jÂfÚ€žÚZĬ/2’X_ÖE,UÂúW»Q¯ëJi '…‡6òõ¯ Ò=Ó͉[Ž [q×h bÃøþwÿÉ–(['ÍîÍâ³¹"¸È}9..û€O›û9‹ež´ôù2^¼YÉõ8\·cõ%”ºt•>ý ³‹õØú™ÃüÔSÉü_‘üƒ~S@âÜAºf;×°XŸ_/ËjLDz<ÿ`†¹RN vn\ƒú_àÎ+yÚ|˜<_­J v[°˜G<ˆ²MÄÆ¾Ÿx'  Ã.òaW¨7ÿFaù&Ìé=j»¸)‹üÝgz•¡´$ÏÈœ‡ãwÃÑ8À¾ÅcËì 1`Î^Š ìÞÛN–SïwYkñn2ؼIÁz”‹šõí¦b§Ê>³õÊ,±La©Ø%DTÚ ØÐ×L°\’?®T†¾[-?ÏÒ47)ÊóÌæfG?Áß¶LfO:gë ý޵¤åÎ_æèxxI‘¥z¤¡Ç={Ð++ç-i’@oÃtvŸʵí¼ðº§¼3Aß`Àç!íkØŒùËçò÷¿};\”ɇ/]œ^bÜÚXÇ×ûŽ5µÕœ„ΈgKu$ÆÏ! WÞ£Çõ'âš›¹ÐrS•÷ÎLë}1þ‹lCæ•G?™&cn¥)þL5¤Ÿ↫ñp7'‘·1¢& ;Û4ˆ› <ïHê²Ú_Vl‚ïvëaä\ju_.=-„p¹&¬ä»£²ÿ+\Ó‰¿ÛvòÌ*! V;J–À&…)DÄhûrUÔ<:¹»q¯ üP~©ò…=bá8;ó$˜TÍ ½“„Y¶Õ'»z?©,+ç„ãú!ÏÚSß|Ø×ˆÝp·º ïõg0Qyg®¹À’¯“'k#É1›e¤¥ì>hþ·g>KJ'’û/€ìl†à!îfñ Ø'N×–\ÃU‹÷°ä Yê›¶„¼ÍÛVÇSpðj8îóº ³~E¾£!™7eËÝ‘@ç*uáûKQ`ç×ý¨É?”#󇕉‚AvÝô'm\ÜŒ•dV'2dž}3‰l?QÀc’ݰèÁôác8Kp‚?¨ŽâxIñ —üÕ“Íkâq£<æ°·Ã ,_Ñ+m~ñé¢`|oœ·†2agV-tu㢺£Ô`ƒj-¨ï 2„'ïÊ9‰´ eUùÒ‚ÇfNÁ¹½Räïü¥ìpnÜ2ŸÏ©ìvÁ ?4[ÚI_çYD¶¸›Jà ÁHûU›r–sò‚C¥<ÔÑ¥°ÌáÞÅAä…wáÕ,â³/]ttˆ'càrjÐÐù:¡¾‰mKB½3*ЩGÏêƒïþuœÑâ@æ:\4|§>ÖÄ {KQ!ö+ç’ÿŽ9 Bê[<•›îฦ_ü-C)yu¶8d)4ظá¦@6½RÈöá±}`³D‹èm8I ô­ÐvåîÊ.ÆÏ†A´(»dåØßßAZd&û©±qsÕèª(̯šÅ•@øž`A áÙÙØ5õwbŒ?Qa~x i\Zr l:Šå¯³áû´Sô „³m©ÆËxÜ7z<4Ý¿[E›1ÃC”mu ÛzÑæŒ8+xv’Z®9ÌËÏŠ¡ )[cë$óiè¸$8ü`1È=…quRp¬Ð„iÇc–­(6Ê|&kàôèMx¬L÷Îåõë^A·|ôˆ¼Âüô“7·¤æé_äVú¼Æ‡£‰¢•7MõªmËǘ­dB7ìÐÂ{= \â4³Ç\ ÷¸Û¬ú+Uà¾{<Ѩøï탩šüFŒ|-Ì^¯ø„‰½ ¸¾K„|]"ʵç:³ÉB3YzÀJá~ØÓwðOáf0 ûÊ{U)Khs4Kþò˜ËŽŽÃ׋x+žÔáªqøãyYUh^g× …ùœë ÃVž,רóøáð5ð{Eou&aR˜=3‚Ù³sŸOr'-ÂâØ‚#­Só\mžò•Rúhuû^tj³ÂÅÖÒ¬’ãs6e*ìÔ‹Ùp`Ÿ%gÿì-jYVã7¥éèB.WŠ‘ÄTsúêš±ºÐ oÅòñâýWðR× ŽßáTCËÑXòz”,K¾º‹ï:üíÜ‹'*ÃèS6p°º÷hà|é«xpñFøÜ<‹~ÊMçÙø”¢Ýž‘gÅŵ£ðꌓPÝ¿™¨Ä‡bgiékšáõ'Áõ·[TÂÍqX ý6¯8íÒ÷’X]Þ,½h7m7ìEgyrzGH¯‡}§î£jR%öZ¨àÓ{Šì²†[:“œS‰;¯a…#üËu¨PØ7,ƒQÓ£àÖº—¼&o0ÛµÌoÜ!Aò|o%V‹œdmp=a hm "OcŸb©Å(м6‰¿DÓ¼£Ôdà>Ôí<«6aÔ]evÇd)¾«t¥cô±ï^™©­Î~ͲrxÈoMål/FÛ£¿ñµ¤1“­sOAaHVX…q>ìಫ ŒahßòGWÏÙ¸tÔàSEæzg9Òg´¯¢õVkTÎyo#OcæD¸·%cÓ¡¦ð-$ 6@ËD Âg0S½ç`¾:Ü6…cÝ­4èÜiø¸ß ºžj³#©kIØM2¬qœu haÇ“%e5• ]w Ä$þBw¾4|èäjJ®Q×í"(0m{’ãBîk¹ó·¤¼ä¦ÿi‰£HzZsÌM‡ÏnÉä#^¹*EéðþÞMãþŠ¢h„‡Oä>™Œ†?JDÝd"³5¹ÉIíÜå¿BYÊ6_bb`6lδdÞŠ ÞÃTÁú4÷SðmtdvFÃQÁ,Õj3ËÝ?†.<ú ô:èåŒi$`ƒ›ZžOeOeA ³â§ãŽ3¨ÕˆÀrÍt“¦lãcnÓf®¸ÜßП<¡ ¨˜„qÏ´ `ѽ(œâ)ƒßwÝ·5[Ùm }ÝæKÖû·£§`š«»’íbðúñg~;uºT`AÄgjåeN­l9—Ywhù¾+¸ûÄ|âø~ v-|ÊÅú.eë6©2ýÛ³‰é´!\éÍå7!­ðS2llHª®‹1õ¶›4óçd¢®sJ†Õ7Ç’P¯SP÷;Bè~þöÊz&ºÅžƒèÜ_ïüþØÏí7·§—lI…¹ {#¿Š©Ô_‚(-)ößcgpoN‚ÞÙ»(ÿÖê¿ÿýøV5WÂäs¦ugïEã`\ì“ç¢ðØëÇJu‘GóñæµydÏÆ$P’.ÃOíšp"¡Û'aá¥Y² îd݆ǒ»Ñ¸…qû{©IL"ºô;ÒÓ–_©Ç´ ÜÁÒ(xiN­Ýl¡Íò1µüÖv*šŽ‰4'¬”žº–'ooà«©WÂ¥4U®MCžÖ&|É!Tv"G£Óy×Os-BÐ$ìÍ 6vÃbSqÖUþ•¼“PÔüp)SqÝêuÐÂÿE4a†^‹ÀÕôÜL^ ö‡w@HÎ (ÍjÅ7Ô Üýø¿žvÊœŽãŠ|SAÃá!ñ“Æqyxüo(ð6És>Ù3Ø–*Ûž\SL_µC§ƒ÷òlNK;»UÕ!Òä%Z?—'18TS€ÄD–P_ÿ P¿‡XñA“%ワT/y›AoYzaªÀx¸pµ Úõ¸;•æ4$!=#l“ì» gÞ8ÔùFYþ '´µUJà¤Á„ð•?ÓÍ€¤æZr ÷¼}E¢k½Ý8aíX‘õêÌÞ‚W¶•âªèä]ûÃÕ÷›ó4ÀTœ¢Ûê x?Û“ºÌ9áKìø ¯Xâüü0 øû?ºfcÏM‡ÖŽJžxS<»v2ïé¾ÂqCÇPpœ9÷Ý— »Àäb`SÇN&ìB{ /ÏÆïÀKýS¸÷%cƈœÃc—/ÂÄôC¸9ú:¿gÊ9hŠIa2†£ˆ÷EŽ•&Ð:‹hëžu_CØ3t]eÅ¥N„=)‹ˆªüYìñ®[>ù0»Š£ÉñÜdØxµ‚;`»®;gCŽé|¬Lv?,Â¥W.âýï_AõÙ,Úú)¹G¼o§} ßrÂ?W² ÷çÅw§2ßÀS,V÷:œüjÉ"˧ÿ߸áÿ}ê*záÝâYަÎ͈IqðZiˆ›óºƒþQ¨…¶”00ºº÷ØDŽ>¬Ã«ÊÑxr×u8vl#Iü€8ú |YsŠ%¼h€çðîÇF8Pó‚ãG?.PŽÜˆ¯„SK¸]ç’˜fX3$Êë²ÓIE§7ܰKÀµšÊ$5\e˜<¡Sª±cÞ.ìÜ¡˜‘»Úð²]‚`½…3{jsLc0¸¢S%´`jü(ðwîGóqpHÉ”9¤XÁðšR|jdwÕGîõ³Ä]aÔHÎðUÑ—ôÀ» ,Ô/ÿ‡ÿ×KA¶¬?kóß ñx7–œMgT.eþ?¾CGñxÒ·‡·Õ`Ø`3'Ò¬Ä~ž#òÎøVæz,¨ÿ箊¡×Š1b¡,+_ÛÁ;9WÄx5´ÆÅM$Za¤G­ J.?Åg&³µ‹“Q·/[?ž6Ýí`ò4ш‹¬®¾íç…‚Æ${4úò¦ UµRL"L‚xéQð½”|8¢ÀKoÇKÎøæˆ ,¹nc>q´dê ÓylÕÕÇ8ß.Þì}w—#ÄTý _b2Û 1M³xL,×HµÑÿáwÍ FÝ·ÓàÙµÕì3ÃèU ]´—J¿ $ë&ÛДžx5b'é#á`~W>¯Dy¹&nÖLÇ_'LC*W[9Sîúðɨ„+7²Ù\/`~`ýò()XÊVNÜCîƒÑ kæIõ2ˆ˜MçóAõ¡ {¦+Ì^@"ËxaM~‹‰’ÙR£ØË‹@œóg“2p«5VóØqY?~!›l[ cOi²¢9²ûü Ò¨$»-gÔã‡Â΄û“Ù¹íììo®`½_ƒá§…&)¹­xiطÜüÿ—t”ð»…¿·’5 ;H{ý”œÄ›[ðWbÊÛ(žù×LŸÄܺžÃÌvðm[ bJ[9 EYÖª|Â…§skÞÞ›žÏÁ%Á™Ýz·ïÆ;«þ0'í‘wÇëxá¼3ñêÀA»Ñr–í½”ÍÞ¯RïÌÓ'½ßÒÈØO‹Èuï1vgŒŽp®ÜEE{ŽY¾a)Æw+ŒX}̆ËòÙÀC3ö¨VŒèÞžFf6g²Nk!&º9”M×Qbî§ÝÙÒæ,Ä*†,ý-Ȫª¢·Ã_´ »IÊúi”Õx6pX”9.Wú‡¿éü>*Ú¤¤&²¶îéü”»Ÿê֧ݱ¼i9ÑÆ`m_0|V$7Ï_`×¾‰¯Øy²d«ÚD¦RüoÚ¢ûò éæ \”êLzHÓ_ÁnÜÌ]iló9 mÄŽE ?¨Þ¨Z’ ]W±Æ üÎQ¤`ÆTââªÔø™H< Ò Å©'È’ÎCíT£z<„ú­@ß)ŠðIÔ€5Nª‚,?°¹“ö§±Ï£‚ÀÓ*åL PYúZI8aûV á%A[˜ açz±q½"›>­‹ª¿ËÏs¸¾|ß¿ø× ‡Úá7ÜO»a. J“Ò?ÂÉŒ{½;ˆÎ¸ºÑö…A\޼EÅ­žR2üU^+±?é?ùŠG$X€O%·§ßû¸D–9ý𢋙.ƽ¾¾;ƒ¹?E<>#ƒW"êÈ[ý£µkq ©óÛ7á¨Ìv¾Ò iü4àYÕœà»ó™ß@?U˜L¼Ì×üƒ_ddج=uœh•!wz–'¸< u¤‚©Ï:2 ’…žø·v ª¸Ðškðê¥d†M Ý"Ù—rîrc_¯D»{½XÑv¬‡v@Ë–RîµctìS`&‰þÿâßþK ž—tÇx×[t¥Ã ò<ÖŽú×…®þaÈR›ÊD#tX@¦3S®ˆeÃëØá1Wë4´_àÖ1‘ø ÿ (ÎaJ5Ùí½g¹m Åè²â&3žÎÔžäBÅúxT± ®¶%õ/%¸àËTøëVÐü’Kc™dò[L`LN5<º¶-:ç£ÚÌLús”=HÛMç¾{Çi^;ÿLêÐÐj+™ûç8<þ‰¥ö¢pn¦$“žk6_UŸ<ø’ñÃóxŒw)·žañúñïòG<^Ì[ÈÂ55røàvYùõßú¯ó E½-cÙüâóÜC³yœò‹d¼cÓ„ÍFåpK½/´Í"kŸÎ$£ø¡°÷•±ëIdKöDâÉì×e9F~.„Ú™Wq‡§…ÁÌ«4€ÑôÑħô:7Ä¿E§‰¶#¨¼gAêŽ#?Þçì³)ªß³Y±x1iPX Ó„Bà´{.U ³cWjðíÇËôUl!3Z¾€4­ÙÌ.UEÃîq þd-ß½ô®rß‚¼¿~lýúŸÜ›Gú¤Íz,‹MŠ€ª½#oÅVÉ ÂňÃI ï¯Ã;¸ë|ưö9~ÿô_Þ”©ôT©3Ù|ˆ˜Ž=ÆÂ4=¹Ï7r]bjLÏç:Z]”§¶+ ™çGÞÕY“Ç‘ Aw8 ‹ЛaDÏž¡KÖ>âíµ­O÷â§Ô¸‘ó–€}°;™šaI¾»˜Ôµà³Béα`þ#¦ó‰¡:îI8gµR¹É‰r«Á™‡¬‚¸Ë~äà¥K°ð†(Ëÿý“rR1Üx ™¥v.8ŠÛƒ=(a@Âìt˜N“"{<µ´Obv‡´0ÃÕ‹(~ æ[S1;;…ëNº…ækð€Ükøüù~‰\qé§¡g•1 ‰ïù‡ܺ<!>V çÕfÄIâ¯è…ü'1º¸Í«§ý  ÝØ9ÃkÜà¼)ìž³ o£‘ׯúØÄŬtW>pSȧ¸ý,oÄ÷Å~ìÆÖ }xø©ûr±[ƒzšÏw]ô˜c@Üx…àŽÆÌ,NŽIž1€Q6bìëè_hýÃföÅZ¥H>[2‰½zÎkžµjü¼~&…B”ìîì)oŽLÛ Þ‰Öì·=Ñœ· êfp­'ƒ 9œŠF:ã¹wçF±ÆÏÑx$a…ÇÃáÇWÁÇÈ€ç¦þ£%ÕØ|¥ŒÙ”ñô£ø½A‘%ìˆa•Kì »Ul(¨áÕÄÕâmKqr£ÿ4ŽälÅŒIŠzJøÂ‚…±Ðñ1>|@õÊ'°­¨¤žêðSÇLå~“¨«¶&®&kAž·þžì`cö£þöZ-ä˜ u&fGpö×YÄ;¹Š—ÆcÝâmÄé Ÿ†šx%2–ŒwéæTjç2•âüé=¾÷_E6™øã!?"<ù¸e¥C­A,H]|»ëǾ²0F†…Ø73È­ég ©r;—¢RM7¯ñ!ºì˜ê.x1= ¶ÝbÝF‰äR[Õ¿ü§‡ÜH쫨$0“3 Òl-qJL­C]èã¨D–á%8Tk Y~1‹¶´Á+óóXµ,~ö~Š·_ØpãÚìIß•jÈjÙLÌg`§ÿU|xë#k¿Lý`×»Jáyo ¯«ü2ˆ,xʹ;£ãO@ïÈÿʲښwo8ÜJÏÇgª~tÿ‡{(>Q•ÀçË´©`7ùy(˜.¥‹é•3éŒ÷Üxý$üùô>™Š°ÜŸ‰4ÖœH`ÆíÏ2¼Ã­YÁ<âAħã±#2dÀ&¾ EaµÀKðuYòOÿ ÿ÷¢»¬àF¬Û4F ÆKéôëæ&›µ‚m÷xÖõ<ލ¹ÃüìÛb |£µnF…(‰RI DÝŽ„F‘-ºáË\=fÞ‡fÎûÒë(’'ÌV”a„å8›nË–ïÒYn”ôÝÀ™îÕT4´Žž[Ž¢=¯Qåù4‘7!ãYIõ îê‚:ôÙ³„Ÿ¶»ýŒ‡·Ãóp©l(?BÅnI›pCk èÀŒI0ð7ÅÓÊð{W,Îã›âýõ>lŸù7œ/ç¥É (ôžËÕ#/v»“$Ñ™°îN/ˆØšÂçªÜë¯_6Ìi냉ädìP`É÷Ѱ7P‚ü9ëSc¹¥‡Oðçíº‹fI˜¡ >‹]ÈÇúƒ VRŸ×Æó·|žÂÆÆûƒç”:4ˆ‰å33ì"‡¦¢Ð ~‹:mö”è R»V¶í5Z|ÓA:f,¨±£.†ø»¼Ãc¶ÁGøó ßoƦ ÎÊ6¿•Ž…³¦²½³k1©ÐÓØ‘FGæû§j³×fÿâÿÝ6=ü¶ÃG]Õâí»bÊÕöÁ™þƒø¦? ;DþÔÆÕ‹Ø‰è3ÑMjK<;ns'ÏVâÔñSXR*«1ÇfŸ\‚FØ`ž,ÛuD—­¿‘3’çv’7Ò~,8KdÑ8¿wªÞSbyŸãY)­„îW£HÙ 0I¾»–ðÖõeU~¾ò-¿á·›b:¥Î´‘}5É0fɶPÏ„Y÷¼ƒsR†xöÁ(rÓd4y ¾“|Ût¼g%Àx«iÜxóg˜÷î 7{÷jòþ½›ªx‚üã 3úS©XiɰÝFl6G‘î7ÜÿðŸlLå7Y?ƒWµ˜ÍÒ\v>ĉ¼Ì‘‚7bñðÐ)"ŠÌUŠ\Ó*Âs®xü\ˆž2bÝ#š<ðÖIï1‹¶…3‰éW14ý³5 †ƒYì‘d¬­vÇQhúlŽï|rP± Ýw6ƒÔøîKÿA—9œBÙOzÌÒÞ_ƒ–‘N˜ªV† Û*`×Úå\ÆŽl«öt6‚˪‘¥cÍMщla¯J1±Øx3]A*º ‰hh!¸>p‡àÎë0­s!žéºÄÖëLÃÑ2jl®Æv¡b›Î7À ŽùxFÞŽÍs»ÅU¾ú‡_öž-eu@ì¥v&@º>C»à˽B)åXè:ˆÃÏÉ«¬1¤ySìUzÃIþpƒ¥ýò8]ÈÒV滼šÜP,‡ƒã¾cÓ—ˆmÙ62Þ7xÈ•}¿#M¶]l¬kÛ>š…‚Çbbàb°_Tz²wÏD‰æºSä±Ãž~Í»ÿš'†¡üöD=P>ÃtºÍÉô 7fþZ’u­ë‚q¼l¬¶`¶·OÃ)U(q­¥5çÙ{Y¦½VëÆ¤A­†>ÑŒ6 ّˉÓ"q2ð5ŒO~űVkì˜1U.bÿôßxIÎ|G?ú+îÙ¼ÑäžO$>ž>/yf 8§³üœÅ"ä¥ù¥šyAâ%`Oâ;îá›ö§n)Ⱦĭ#K¡,=„ƒƒÉÜ€ P±n‹›?‰Õ~pÅYN#¿}Ù¦r)² }=^) ÿzvƒÐ$\×~ƒÙàeca¶[¬‘®«{‹Ð=ûJ@âðT¦°ÝŠ,P dJµ‚püÉE^Ò}A¶OgÖ:g Ù⃸((Žõ½%ÍäÁðm$›_GØñ±4ýïñ CwÎcìâÇBÝR̯Wc¦§Tÿáÿz¤‹³Ô?„Ö‰«Y™ dcÂ=/6f…-žF®+]d_S¯° ÙK$¹W]ÇŸ—®},¢èoXÑ ¶n(åŽ+¦¡Å«Äwàå]·pòúD÷A&¸k^ãëfŒn åm=¹ù¿újÀÊ|£ÈDiû:ËEt¢®•ÆE|gCè|3mDÓæs—)(¬máÿ5ÀQÂgðàŠ œü7‹ÖìNäšJç‘G;Ʊ“›å¡ÓÙ‹(OXHÎìJ%o®îeCäð¡oI²‡¾.Ð.š©±Çoz¦.aמ21ô…å_÷üãAyfª‘F_(—C°y³9jeÖ‘qŸØ†Â>0šçËZ1‰lÑ[OæXK$¦øÂMwc6s—5Æ×ÈZ/uÂ{³ƒ¨Íîæ¤/ª2ë—fdÜg àN»Á²$1©ƒY| ›M%HlÛ.’=Û•>‚õ[¿àÅ’‹dÖ¬,¢ý\‘Õ{ ³UÛŠhÐòSàGU;?E×~„Ü%uðD˜Eÿ¨» q ÕßÒnŠQƒOÚ2‰Â,O–q÷6Ô-­%¥xìÉ Òwà:ÇOþÀY-|Å̓탵Ø^D?:^ÄëMíÿüŸ³p2È$ÜÇŒAQ:ùÕyHéoæ„ßÔé{Y‘GÜ.²¿·.˜Ý1pJcËQE÷÷·á{ÙxÌš¦‡âšàÜ}’0Ê+žÁôõJôÓ£ç4&;žÍz Ä†š÷ Àvzñ(N•„ã™dM`5Üõt_l[•¨Î| kG)±;¾–ü7fzl’ÞV²Þj<£!F.-¬Ä÷«Üðs 㮡ìÀTÚ<þGWasóFxt©;õ2…%HYC…ÒŽŸ³`oD˜ ÆÀ–IÔH¹/¶Ð¥Ú«ÑÝ¡v¨Zü㿨ÍM¨‘ r$—ÍÐÉãÌUI’ýHÂÉÌWf Y·÷IPô©ƒW†óÑa~23½¤Ã·™°,ßæ@Æô%ÌlX’nIœkn$2Å 'ÁÖ¼5l•žËn;̲lro¤‰É×9,ÓY†TV&]‹éÔÆðu¥)‰»cÄ®Œ%î§ŸÂù³RäHZ‹¸„7ž½âRØ(’Y9HÏþí§‰â’ÌþŽ1g•. ¯žÐ=¢)8¦*žh>Â:ïOÓ‰Uïm|8{æfAçg=Òo? ûD.Á µ `¹á6l‹UÇÎöîòžñ?ç¨#Fü݃K¹DII2Ýó":…áÝcçg{'\àÿö½¿@þ±èÁfË“pgÁlòùí,ûûÿû ñÄ|cºûñzôQþfO‚³%xåÓ³µfæ äW±7 Û (&•[eMšÄ÷’]d‰ê1ŽhݰaÇ&K2ï¡ÿª,.îø‹å>IYŽ(,:Ÿ‹† ‹™ÑÖœl§Œ«s$‰Ö²Ëh«<?òtüý@jÀ:­ž¡—´ wvÚV,7&n·œÍ™H~ßÓÁMã¥I€^¸-BËü‡¿va*.Üú’Ëz›‹Í£`”`D­ëáfk…WõKø#ÇxA9ˆG>ýÁ?U>´áo;|ˆšŽ¹÷Á«÷:šì˜Ž7E³ñèØpÇQœP9@9õbì"WqŒG_áõvvßAœ}æf Öô»°Ï ~ös>ÇåøcûU8ŸÓÙÏO7iHÅîêž8ÛmöYRê~;ˆ†óé¹%`x¯Þ1Å7XÕ!ÇFÆAçÜmÄuþOn>³°Éøû’-;ø®…·qÚä»}1õ9–Ë¥+“勇éãÖ~¼Ü²»ÛÅyõ©2åèÿžÿ|ë_èuEŬ¤+E™E~3ì´ÃEJy³õ§xøË"*èª@D÷öƒÃ”Ç(i Ìxë|«O [‚Ø®€uL÷X1èÔe†I‘T:K´Ê`3gç½¥Jäǰ/§QÏFW¢Ñ°ˆ©Û^†/K]I]E8Ä}8?•Ì8]ƒ¿Õ`Òªõä|LL=Ú‚‚k/ÂÚÂ.Hð ÷šJVäÃ鵬+ÜŽüW£0•å_&}ÝIGñiŽ¿2?žüç Ó±ëA§0¥ªä)°6W²û§˜ÂÖìc~ø{h/‹\¾>,¶bÒæÜ Ñ9üÙQ¡ÃQçY¯‰Ü­”3ø_óŽ'—àà2n¦ë:ëœ;å)Uû;‰Ù=Á¹Óøð£ñ ^i¿EßÙðábº:ÓÝ!G2¢#A¿RBåH›è"-™Ï-òšJ\´}WôVH“Å$ù6ùJìÉ-y–üô" ñ_ÓÌWáð— ¼¹T…D°O+K@íýä›dûúå$ùáJ‚1äa†*õ‡ã©sOÐe­Ó?ü¶Í_è[ ‚:E‰\ý~®:…ßv*Œ`>ƒ±c¢a»ÆØ÷ $ðUÔ_ˆõÕé4ÕeÞ ¼Èªi¡‘—®Àª/¸ÖÊUOƒ°Ë Xß/‡Œà¯N7Dñy\½±(ñ>: '5$XÑT«ýú™î§UÔF[†y§º’ÓnêìBš™Y#Äflr&¦öª8ýï]®Ká1ÝÚ«ñ‰¼U Åü]Ù/Àá?2× ãXçƒ(.ø­q1­´>›g ÷ÓÞõjè"X ²C»p¥¡¡§Çá›*¬Ê‹®þ‹ÿ‰7C¬µ¹uÞ‚©îèÆŽ–6Á•GN=Lä%?o©îÀœ³Å÷û² éÓFb3ó&oLcV§£Ä© ´ÿ‡mäìðÖ¹û Ö>úÉ%ý<ÀÝ¿Ø žF¯ ìF8™ùôû³ÁˆÄW¡•yÿmN?ý±ìzÐ|J:Ë R–Í"‹Í_àr#/&¿ý1Ô•bÉXa²¢x3Óߨ‡†®Ñ@GQÒRýªLÍØÊÏÂd¦}+ØXTÓçÇÔéëˆë°Iä Mljlä:–“ȥݩD…›cɦQõÜʯÁДœ²+Ê<óOÿt¾ž g[ORÆ ø*׋•œ˜ÀV[ÁžðkµnŸ!Ár:_fAÀÐÐT„7’Ùóî,è§Ë®1„ßNÑ»©a¢™{³‹u7¹6wyÒ7ØN¥yÂ+‰hèNØ”UÇè•s¡X› Å.tÌ&zÊ"Ìw½}2C[R£EZ/- ½çN㟱xÑ[œýòl¦Ë^ÜF{6[·nînÇíÛ½ùNï¹Îñ[ˆÙÅsèYê„Â^Ÿh]øk˜W–ŒZay£&r| f^$ê](¼OJ„¡IlNŸÜhäœ3Ê!µä»íÿð_-šw ¹C&U’M‚ä•×13d‘ß$¤ô–-’aZî?ŽödÏ;Ía[¦­g‘Õ— ·ã6Nɽdë7g2 9öJ{ŠÁ©^½aW#ý0æéxöºFŒwgx¼Qg/×aÞ'i¤rë˜ë]öúå^N‰CL_Ä~_îF‡‹ËˆËèÅl®S"Û¼ƒ ËíãÆm …=Z¹mC5<éúgè·yˆÖí’dûO¬¦mB'qõ³½Ìwt‡­iy‹Ôcøð/Bç´ËLíŽ$‹‘ËC•b:ô‹k»ôŽ:õîýÿÝ'Ž‚êû 8¿§s8F.µ^~÷ý0fäú¸= dC} ×¶ÌÖU†Aâ²Kàêó“wÙë>Ü ¾„öé«™é‰pvº±Ý3/‹ÄPÛ;ÁÐjáÌi[…¿»‘³W<¹¶WÁxE9 £5ŒÁ®Ñæ©ÝƒÃ Ñ *ÉnŸGåíN0g’Þˆ†¾O[Us!Àª’—øÇ„?Ë¡´"×u!ò}*ŒªÅ>Øø{v¿L˜ìÑr¡näØÌëžÇMŒÓĉ’'Pã­“Nâ–6cdÇ'Ì ¿€Ú“ @!26MÁ©­Ùÿâ¿@ëdv<Á± "ÌáYß/þ2ÿþ#Zsð.èg­ƒŒ•°ôãiüªþ”6Ùk°ýò,µaxDvvç~¡ÃÆä[Ž»¨7 ^†pÅBÓȪãpX%6Ü…WGï#ëí¼$‡+¸ª9•Ê!zÿVж!ø’¹Œœ¨8Íír5ƒ^MÌ]ÙDWìýÂ=H mþ›qd.$á—#'¶»ÏÄ5Pþí~L–dÜ´tAÖû¹‘;9õ"ÿb ¹/ø4íOAë“q¬}½Xx¦ñ5§é³ Aþ\ívÓk?µò4'}\È?üÏÚRùÁ± ù¾ž^-@û»z™„ÂËH¡‰()j9@=žq=á°pK<ß>™mú)ˆ ^˘Áìƒ8‹zTqFôY[´Kp¥BÕMeVô˃a\—2üW›¼óûKh«E9™Y[xV'ðE’..“ASx\é¼íyþDüëñ›j\i‡MûµðÙÓ'è` QãocÉxe¢œð€ ™-Ê&í$WŸ™ÒEWš±_~&M^öŒ¦M • æ‚^]%º÷¤{xì·(‘Tf¥f&(yZŸ{þÔá_üÏšUÅYÕ.»Ëf¤jM<ˆ¬˜ÁÆøñ€2x÷›w³@2^å¾^'Õ˜ˆ©kÅ‹ÆóÉÙ§Q`xô%-^Ɇ‹÷Àîë&øèÙ¸æ`ž~z¤¤w S8ó‡[¶<*ëa8!Í8yÒ¶·_Ov}ª¥·¿fÓtÿ~<ÐÚCMEf±%“ã5òLìã|´Û¿  ±à‚í`½c[1³ˆobÜ„¾¹Í˜y¢—r´ä¯ÖÓ-ZÏÀÒê›^ÄÛ?+é QV JÊM}ðÆÜ;8àä‡R§¿àÂïfü¾£HÅŽÿËo¾æÑMþâu}+²m„ûW´Ž‚”÷¾àYTÙÓºx7ö@°[¤ ïÆ¦Í—y›ûJ¸r¤yÇu²è~†L‡¬u2lúq9÷ƒi®‡'~LÑÀ Fµ/ýc’èTL’¥ÕÀY£­4qå¯Õ‚Ÿ“Ñíã4.‹­Â“‚A$*\{oÁö#9û•¬CƇìy9.®­Áäá:ø¼¡·G,d{½C1rof=?Gå>ÔÀ|ciÒÐ’L´-á Ö7¾˹ö¥~ äjÇŒqÓ¬/†Ìàºx¹&«ªsgúϲ–fê{–‡5Q¿­7¡Á=×gÊàÝMx¸NŠ÷®þ§ÿE7pøã{Š´TB¡XÜhËæâžŽG£ÕV¶¾ñ긯'±ä‡ ³\ó‹ï¡Sd=mív•ð|µ~q¾Ù›!â½+àø'Â`³”l7ÃCƽ!³? {û€\—.zΑf,á./Bù1ç44‰ÔnJƒæ¡L*>µ’Ëü#Áê Ö°^û0É ¦õe/Fæ¬AòGË€s¤KÒ2až›Уú_éß$iâÒáǽM“"Úgà@S¶ö!ïä¾r¦’OPûLê¿ç_m`%w´ ªµc±3*¤f߯.ÇDÛÅ![Ù¸¸¿ý¡Ü¢÷µä¦<(´Ý[¨Íõ=Zu/ta×(u\óx8Le‰_áúeIà„/ãœU w ¾Ìø‹ïÒ.rç¤dÙëÌÑÉ¥š ±:…'[?aºœ=çzµn—Â(÷`àœƒW¯€†¼“Mï…7rKÞu‚µ¥&ÏZÌ´nO!ë-™ÚÀsÛ‡Ò›9Á2 2V{3ú›f«WÞF w ¼âõŹÌèÆR9 un¡˜`M®¤G²c¹<ÿJ»îýÃ?ÊSëÔ¾ñkο‡˜Z0k4 gЄ«†LC4ívfY-Ó°Ø´“F¼š‚•ÝA»Îu¹^¿3lV–ÌéÎ Ó°uâ¾Uîàò n—Zsã2%˜üu²¢WÆðvÞ½Süör—¬É!W&pæ3ú†mÆÃ'ÿRý•iø-/ç/#ûæÉÓ´ùr,àö¦úgþØ0àa“t´(³l¦ü,øñ}éù¿\tƒQ[Tq}i/Õž!ËÚEª`¨Ò;'¯ƒ70ñ5…UyÝtÝ÷üŒ$>®EåB3üœ|ƒ2kTi¸÷nœfÿƶÖr“3q K…´ùMÅa[îÙåèMØó^7¸–OÏ=;ƒÂMp-Ïœöņbô¤8§îÉÔŸâIJV¢Îø%Ä{ÅGÌ^(Nn>õpãõO˜ÛÙƒU'žQ«‡1pÚ«„sìËAçxEîHÛ5šÉ$€ÙlÂß¶ 1y-á.ù/úÿzŠôOTx'Æ#ï³ýbdŽMÐÔÔp·À 0w>T^ûAÇù4ƒCÀ 6.Ù‰ÝÜÎK¾E¹¾rÐßϘ±K&¯%I§—ðçK¿¢+åä˜x¥ìJ¡¨rî]чхìÓyalÿP5%×xA°Ð5lHB^t:µc…†ÝLGúç_e“² á–ÈUX!à‚É‚DÿÍeU¬ÇÃ{`Ž¿"ŠY(B÷#øá;ŽJó¡¼®†Þ²TÀµ‰m‚‘!KÙ²)§Q¦E Ãç?:z—=U‘öÿ£‚¡NAWNȃ×ëæpqæ,.]Šß·à™}™;R+ÉîkË“ tþ†¾Fx—û„Î>ðïë¸b»íüºèÍm¿1¿Z„Õ³²±AôÂÃ@’tD€´M˜ÆM«ƒx·b ›‡Ÿ._…>¨&oD£E“¹wùòÐÚ(Â#+ü/Ñ¢¥sa|º.ˆqbcæ«’ˆEzäBùòøÐ<æQQ÷ÃS8‹Ía5Ýé¡­!<ê£NdÞ5Ci0+Õdƒõ*Ä4l,™x?žÔ[ ¿† üj}î¬c EdéÉ*}së«ü‹ÿé9鼞Úô|*5êÉ£*—S¡6Ø«Ñ)ʰÍP’9Þ> ®œÉ¬:PZ£Gå]„ÂK³l߸ çÃïùV [¹S%›`ÏÆ±\@Þ\¸.ü’»ÞeK/Î"Þ-¦`lú‡*Œp¯øÞSŸBH çÕ*ó¦ ý«Ëæ÷•‚sh¦ò1^0ƶ°†Í°ô^.ŽÌ‹â'‡•Zuðèåúñ,V¾‡Èý±Ø¦æÊîOz/OãúÏÈ€þ¤p:-Å Ï_ùÁ› Â8më¹LuJ9TeüÂg’¡ñuª›Ã’—Kÿù_8_ÇíÂLÎ#G—¼¯™Eö¯Êå‡ù=ç¸æ²Øí7›íˆ¯Âê^qn«±v«µÃ‡œktA·é<ä®C‘`ýð¨?¢øm)Ùñn"‘˜bw·¡W[À×ÄîæF(<ß 7ÑÂÙg5öwC-_L³ÝW¡`êc®ê8S6exeï^næAÙÆ›°²8æÅÛPÝ>Šu¥A(ãøK$‘þAwâê4 “æq¥ŽÞëÃÚ®xâä¾þXøÂ50°ï €Ã"|úüî=™‰sfrÛÐ …B¼UÅ¿ýÿe™ãAf¼Þ¼û×xßÀ’£¢l§~,ùé %GOA{ þÝÌšW¦r㟂\{&©{3ˆ‡Õé Ätòk0OýNg­Mwiþ/ÂG´Â•Ž@x{í~þ„tèú|Æ›@¶žL€9ê<–ß· ë÷ ã³`»D’͵"k`AŽ ^1œˆ%›|Þt-~…1«ÓÙj÷¾Ú¶ÜI°u® ¾` ®Ç¥èü šë_Y‡ÝÃÅÒtߟ"ú9›˜^7Ǿ¹=±«¾ˆR6ìZžËQ^EºWÞ“w‹ªGk—âå˜9 uÆŠz­µ%£9åã[áç¶åZ<¬u¿·û0aК̚ú %7®´’;ÿ[‡[æÒk‡_pë×ׂŒPUîmx/¶vܬÆUFñÿøò‚õpÄ)󞜅—Å…$Ìp/[çìBœ£´øuw‘?\'Œ7йû¤ %·š¼þ3²~“¡²Ë §ä]Bm³*¶‹ýåÞ‡QɤÆ^øŸ4äÞo½‹ƒµõð[ÿ ±]eNZ–ÞdmSþà—¶#ørY$y×gφgM`+Ÿ‹¯†ïp}º!ÏyÙVrLö$, —có^††Ö?hUº´ÆJ°`ïG<§!` nñœT€ æÆãñˆ0üQp,‚7Ò7' ØYK8PœÉÞgÔâ´QélÛéaNºjvÙË5g¾Ã=¨ÍúI¥›øÿòÿ][1xb[J7­pTÊ¡o(Á-ö&ìy©‹UZ–ÞBÄæG94Ÿ—`w Ù†‰‚`þ'ˆàfÍäqà‡ÚxYÜf†ØÈó[–‰WÂ_sŒHÓùï¶Ÿ¥¢É‘û¨ÆÅ X#ð §eóDnÚ’›Ê!Âюʸ9ó£à”t*¾?þM›éû+y6÷¸½tÎ ‚«f,}ÖN,œq ÇøÏe^Ààʧ  mË·Y’÷VÀŽ®3èå2‡ÔŽñ '9× ë°8ÿψ ŠDã†<3S`bWí`Y‘À?þ;}¹Ÿûµ£˜{{ýìü&I„ÝDXçe\(A5:Œ±hÅZ¯rÜkOf´- §tœáÍð~_*.bÅÏ KÌ7Jã«VY&® ‘¼ù‘=ÃÍÞàêÓa0ùÕ>&1Ù€;qh•˜øÖØ›gÇjó¤ûÐóB=|˜‘„jÕ½Ðùõ*. ¡Ü̸1ðaŸ3ѺéD¶µÐ– á§õm´.ÕE»ï¡4É= nÍ¢èëÔC7mØ•äÛð¶«Ì?}ä\x“ŸƒuDšE|ä¶Y^…—å¸yð M¬ˆ€cC§9 “ÞÏìã˜té> œdÊΑ‚{Z³1÷“Qý$@ žp&--0Ke {êÕEz-ÑBc"YP› ‘÷ÅÈDáïÀo)@ÉU‹YþÜï\`¿(yüÈ‘lœG4d:@ò½û¥/Dæï$0<í,•žû†÷g”7 ÏãaS@LÉV'£†›à#&ÕrÎd ]“Äšœds÷örŸ}'áÞŒyL~³9‘ïáuŒö¬/ƒî³çðlˆ­]#ôÝ?Žz·dH[ã ÒØ O8O)´ åÚ6ÆÂª)¡Xb5~u‚ä?ÌÀ3óFüý¨Ç8»}9Ømšõÿ¢¦¡–‘]ü0œnU[J4ÏÅS©PnN–¯¤2Ï (µFcYx"­KÜ”f²oœOnêÇ…à±9œØ£±@à €„ÄàÎá,ú{ƒ-LërgMJ§ðZ¼;¼ôç ~­¢Ç²¤ :,Øq&„|ûA•jAá뺮· ·HáÙBx»ˆHß%³v2+m]Á·‘æè) ÜãÁ4Fü½ô+²n”ûrw;{h6dü.®(êE/®;Šh1ž-¶ò ªbU¨òãþÜÛòþ×ACp žæ˜¹}™à}üßúß3ëúåôIJz©Ûº¯Ø}央½§1oܸÍÖÕôìK¸cÃ;nöÏZ˜þ ãwÅÁÑ¡GܦÊH¸ûæ%ˆ%ø±#û´XƒÎ5jh|·´ùr'ùûqûf~µÙNüp4Žù|PÃïw®Cà$'úÍ)…˶ǦIcY‡åD/…¶p(˜>Û{ÆM¸ÏñÇÇU‰¬á[˜æYãT6EE"“YH¢Òn ¿» þˆ½Â ÇœH|ÙYöãÝ4ö>*?oŸH/OÂ-_ñ¿Úîé…=8}É2,ÈÝL”N±ËY 0ðoÿóŽ~=}ðüf/Õ€µ—{qùgCöÛæ*¨NÎí»aÊ|+RÇ$êëœÈº-Spnw1ç7i6šìÄÔ]¦“ú¾çà5ÆüÖµ£Hµ°!ÏÏs,{~ï~ð:Î/1–Üs„zÛAâÖˆ§wî‡;ÒÇ1Vt?j„nf+¥¤ïðhRÿ1—1&M;­aPánz†óVxr’¦aŽðeüêd@[¹Ó›?Òn©íäzgÉÍ_Å\ÜG¼çž_W#‘ø„rÒžÑ\Wý~þÖ|Oö=t<‹p îÇZس\ ¶V%’~µÿø?ÙpL4±C›=ðöç ìŠÄ ¦é$fy3Ÿ$Ë6¦›r†ý1owØñ4iüik¦¼ïüµ'qöÍìúš2:¿à4³“ò‚%§‘[jI­]Ew‰>\×ð€óÚÇUXÍdòÙ(²)É Ò,BãC_y rñ¿Úå·Vø(O˜¼ö s³7a^ÁGГÏE­°"|èïÊFWøâ.ç‚z+aÒĵD Tñ̽ høÖWÝ€Õ‹ƒÙûÛóPxò Onþ0.Îç±Ôç_¡2ähX¥á æYüT¯7Д’¤ñßðåõ¿ÝAûsrqFÅñºÚH3–<8¢ózqP¾’ç±W€X®JÆLË•<ÓuG¸%L\û#w}æ}ðžòfÛÏu Ôé·›QˆÑg±x#—ú=Õ']çV˾ÉÜ;Àß§ƒ¶aðz?}¥ÓÇ|ÞŽ2åuT÷P,êð!üB†âÌäå ¸p¶æVxá«G~\Ï„J”{;‰°§Y«ý~⥠.¢£*ˆ4)è}ú…Ûå`ÆÙ³kt[wb­ª[ 6x …sQlÈ^Ç"6 ÕbOà»;ç¸*šŒO"þÃoìA·%¯ƒ ²Ü•åyôˆë [~†NÏ= OómÚ!tZ;¡vš¥©s' #=Ð#çcI¼68aA†œdïÇÁÂôX¬{¾œŒ*Ëç­V{sâ‰'à¹Û e„#¹ž©0Fö.ˆ?L%ò†ÏÌ&jC²$©쮾‹c¼/±îm*ä`‡,™–›Ã· ÜÌ’#*¸Íâ0¿¥?Ì!Ö?Îà1Å$ötÍdb+uVÉ5ã†=qÌàÅt°ì@›{1ÔlÖ hêÑfчçÀ=•wðû½_Ç â9§´+Ûüïù¿}¤6¦r‡{Û ðæ¾\‡5kÄÒ"aú²Ý¨õô&V"ƒ±ù4ðH-vߎÁmƒ_@rÔ:™D£ÞÛdñÒ¦×âý?ŒN‡gOüè^ë#œ}—38ñwL[ÈzÝÕ!uË;X»m:h­eÍç·€Á#AvvÅ&6.Aü-Ça`ð&bs2©˜`Oü—¯Âå¾ù\G»ûÔà‚ŸŸ__5õôè +ò‚wüšà…óÈ- çß®Æýœ¼þy>ªK³Ñ¼]à£'L.Þ%Ê+£àÑÅ·pg]|JÔU©ƒ4$¶øÿë*Ú ©•güp<ášt ûy7»ú–ØÚ²Âq¦LvF?¡ÕËžÂÛ[u0£9pÜvO!‚˜’ä¤ü ؇†û±x)˜$DäN C[‹5¦Ý #Ò+‘yãzÈt“™¨~$–u:±zª-Ò¹äãûاáÄoô-ºÛd%ÿVçv¼fÙ„ñ™ ý'w&g’#Þ)M‰ì¶Ñ 6›à‚ÐRtè5¦j?Ç{Caìñ´D¨‘ÃÅ¥PùaéZ*Œc½Rp¡¥98™ ½Dœ‚Íð»I(§ð”á»=üá-"ÿôŸ‡«>[rÑåg,;²5,)¸¹„5t¼æ‚¶™±““™×Þd¨GªëršÌ½/Hdå+àÂz6ù® ä­ dA}ëÉßÝãIû¸[Liël6¶‹ù¾¾ÚÀrÛ6¸Ôûš™£ç7Û-›„1§ç ¦Y"Î;È´« ¼óbc¨¦ *9NãöœžOÆðo᪩Ì5¬ª"xl­ß(¼ðÂ=ÂEvÖìÕû—ð6\„yÐGPùô|_rfG¸²Cy+™ièlÖû,莢s;žâVõr¨½=;5Å+3Vc†âÚ×úõg׃âMÜ~;ŸÕM‹/cÿž {š˜>šœœx÷§J‘0¹ù8êPê<¤÷=@¹±Öd«ÅvÔz3 tUÉ;Áõ¸QV†œ‡éJ3Ùþ¿ËÙ™×¹'ã½9×±xmA'í µÆÌ_& ¨L4µÎB¸èHL_ª=«Øfw61w1M Ç„U‰à%jJ7ØÀ$1dÖ±dØc>›aËj«S0áÙL9-NÊݸ+BRd‰}8•µŒÞ„ñGŽqóîlWGgîÑ–â²Ølq¡ êÀWØ:*¦Â0ïÇ¿ýßKÝ­¼ë‹]¨aÔRÞDíñ(.YIü¸ùs˜-˜‡ÏÝÌÁöA/×§ÞÆÝ} IB–kÀÄñJäfÈzª|í èDŽ¢Ö_ã—nIÎÐm ³]N7]¡ñ¢Ì('ÜÙø/Þìöc]¾s4ìymÁrŠÃeÕe¬cï3ðß°„|~=2{ÀäÕ<úd4“¹Ó€^CRdçÝ©Ì(Ô”œ”©Ã}×5áëÒ4îhèð¹ºäVž$úENä›v+ƧáÞeSÀ~Â>8´5œoNswkaï'm(P›C§Ôè’͆&ðfÊA¬l>ó/þ»<¢°q°™E‰•5âÍŸÌ7Öƒ—ì ­}—.‘"5ïA¼*ò…Îâ¬LBG<îéBOSW»Æåxª¢Vˆ1ÛY‡ Ÿ¶ã½äa…­$%e ´­;ÆÚ7²?þeLU¬nŸÀ-ìãl#[A|v÷ý½‘ühB6ô•s‚‚qd,¨«T"ô®I_— ‚eædq§#š·•BáKõË’û܃!¾9.Ùf@nVé’ Øt™| â_C¯d8~–%­ñ›†qeb|ë7%Á¿¾P ¿xÊoy‚ÛÓrÿáE_¢ðCgŒ§³Yå€[š•Ft–@GŒò¹B¬2QBѺtüê;  ´ç- Ñ?„A´L,UÃÐwY(ý ^ŒßÏñI?‘0ecüž(BÎ…Fß³È_w5rYH§î¹L>›-'Ó?Œhñ.ìÌø$VÒËÆŸ=…S&W¡Ñ÷÷ ìw”DjÖ“Ÿr)Ø(9€?U°á‰ªì½º8ï˜á3[VƒrñhQ”tÇ1yÏ;pÓÍÌ—œ'·»–’[¤ÖNK$/'á ~7G9˜½öc²=S˜ªðfÌ9û”¹Ã¼ü7~Í~€Ówy²KáÓ+cŠ å(?îÞÞ‚W G“SºÙשCP>ùü{!Ëë,…Ào,{çC¼”Lè¾Õ¬þ4Ÿ†­¹v~Ä|.{Ù,Ì:zÙŸ!î‡8îÿ'´i‘Us¼`Û‘sàaP ù¿ãQã~Ù°'Ñ:ÓƒT¦’G‘}˜rnHKVCñv¾×”¥ù/f“R73³n~‘N}ù‘û>ù2ýj"Å6±^û”›ø6Ø…]Þð—Ó2Ðd ~þ‚À7ÑóX¾Ç(G1r¹ZÈÊoÿôÏïóÜo)/îÉRWÝCkŸ)aºƒ*(˜,Â÷rOpRk,3)ãߘX'N‡Cë1Û)6Ø‹“ç_Ü¡=Ó€øvÎñ†05QcwæVàÓß*`^$Kv/3¦"±°¬@¯Tg£ÏŒvVX»à'ØQHÍwÅÔòðr¢:sO-åä«•¹xût<"pˆ{ZeÉbKR`™bNÈ‘&ÇäêѤJ›Íúœ…gã7ó?lýI'ò­1¤ï9M::øêjØóì-?®~&$-ûË/«8È•hÖ²Z'RxÚ‚ÚÁ%ç"îóÌ¿38á_þ3Èœ‡ƒWñ~=ˆv‰ýœn¿.){ôªÎ1ý7_1Y ÏTÖÒŠéìÕgeö$ú¯Þh+^Ÿ[œžÊv¯bŽ©’£—¶.[Lλ€sVk“¸ý?0^Ü‘xoS$››V³B‡ d(e*£YŸpÆÿ#êËÃjú¾ÿ›çIó<Ï©DÒ=kG%$É”9eLˆ¨4©”F¥Ð<”H¤ºgí !‘)d* ™~½¿ÏóëóÜ?î÷œ½÷:k­×z­}Ö]{áe¼áõ¢¾@ËùlœýUöU\õ9äÐʿ̅æ%ã|Õ Û¤¦?A+Z`oˆÓîs«ÄEÙHÍßܘÖ\9{ ü©‹ iBôŸ¬üº†gî52«rø‰øß»lò•>¸2 Ò±Gz:ÖKße{çy0Òw´ˆ™¶Þ„ÿÓõì­°žfÎÐmz`‘+‡¾¡$k8L‹ùÉJ£‚ît`Ùú2p>³*¯%¢uì_ës ðœ2«àž”öfc6&RË´@Œ  ²?±7Ë'iGàá†Ed÷Þrfþ·Çä߉[¨Ð µ÷¤iÚúÝx-­Œü¾cN&/ ™¹òÄùþ+ƺå0D-YLƒòîÏrrýFÖÀÕ‹,îO‚ÿz6 êOÎñŒûI’ëLÞö¨ÐO>ÎÀ’.º•h9T²²G€àù Nw–»Î,–ÖÀíöØÖdRÛëRé™Céòw/ƒoÞ]“yÀŽ6’œŽ6üüµ÷¾ÞŒ™Uišq/*^µ$?3°å¶?ÀÉü}HW~™OS9É`اN;?„ââ‘VW¤Nrü¤x§1ì[°šn¹'I„ÕGÑ#Eƒ™;5ÏïÊ! CÄh‘º 7²Û#fg/š”/L61!w¡±T¸P%Æb%ïîŸÅâ½Â £7å]8û“ˆxÉ!uZŒ>}b/tÆÖ‡ˆyð¦#ÐVèŸ'æ‰6Ôæ_®äÙßPêe?_‚ãÊ0údzˆ‰¸ZÌùÿòÿÕÃãß娭ÙXç_:4õ{#ô<ì@OãDèÚªOB4ëhq‰}Õ7zΕгRqøq1j(’mŽ“ÉËrIîã°_LÅÍ"XÉ6Ì$ývqd ŸÅ§5aì’ž@8’ð2j‚ <»°Šn§jœqù¾•âV[ÜþQ•èšñ‘Á_•×N”öìþ8KO쌵i]Q»í]¶/#§N+‚ÌÜ ö»Ÿ:è0:ìᛓ¨Ê¶WPùrží‡ç[# }æRb>­]}|]ñµçÛ0«Z0cðX9:Ò­‡Ú\nL¥v>_ãmôöÙURQB¡ñ=8z™‰{¨ƒÛÛ˜ò{‹È7ï£páê=¸sÁŸy«²Œ|©K‹;lèµ3x^göC‹d4tn`jsy ¹û“ƒdà þó_m±¡“sNÞÿÇÀj&vÅ-W?ãÀúsŒî•Ý4FM¿QÅê„ùèü ÈŸj#‰çņz•ÖbQ6—lÛ˜‹ë²´)UV#‡ñÕÞÉT¹©Bî4ÆÂ§4ò±<ºžÈiË?ð6¹½éó 6®I¨Úþ‡ÕmH"߇"Ià.C,ëYÆÍé¬p©{;e'òÝRv\ ã´HÖ-t¸q’Æs<›Ç»`›·ª:š#ñ»…º3¢‰Èž#xmû¼TÝÏuž©Er”œéäE>(©+HîFn¡™±]tqÄr’O‰ÏY„Ý’ÐäÝ'F~x!½ùž =Á ÷ù-Y*lð¦Y ª$$T„nûeÛÃë8ÝYfá£k˜z2ê>ÏÂ×Kda[Y<®þ(ÈÛiM3~^ƒ˜î!6ؽ²ÚÅðÛÁ ôTµ Ëo}Býánh˜ö¿÷ß•Zøúá’#v “&ù´Ãò!³lóJf ÷<öLg«E–Ãl¦¢ä)·®Á—}‘ÜÚl5òvv@âçw̱ˆ78´ðûdŸ:[Áíç-°ëûu|Ù?“3àrüÚµÈÉИ @,ò’ÆŸ½û¨cGbر-x9Âgž…¸YÂ{SP×A‹}W ‚Ól„Î8Ü—ô…)YزœîºìŽOsǘӱ68i2vCÆ÷<œó “ÞÖ¡ßœ%Ìø7|W îŸQT¶ôðé~QBã˜MÅ í>áÿ‹çfãEåùœÊ_WÙƒc ¸ù@6󢂗¨æð¢ÙÕ@,x «ò?1/B_ArC(®Z›É”w©Ò žr+ ‚Пpöá¢x8-üÞ¡ÊD™ý´â3Vu™ƒ›Œk¦(¹5Sœ“9u ÓrUãeêñkJ5ë‹Êdã7Mº¹y'šýÇ‹ë:íU¶×!Ùýœ¹¢¹‘æ ö³šóš˜%]•¸Ôá.X Úf¯ɤq?¾~!'_;ŠUuAxvÍ|úþ^Ûÿõ—–iÈÅë_A•“ÍÕ Áô+÷Á~2é«›EJàgè7øÖeÃONøÿä¯b¬Å2)¬8/:,©ÒŽTýÌ®n\£=›Yúâs:k6þŒ’!—™i /ZÞ7fSÏ_¥X]ðÝKx¹ GÍ <,‚Œ¥höËh4ã·¡·§}`ƒuë1ȳî±ß­ÈùáD®Vv|{Æž¿ÖÌ&HjA»‡*Þ˜D¬ƒn‚^'3eü™Ål°Bé¹ìHá1Œ}× Û­l‰Ë$vw‰)+w§“S~)•ÝÞñä*EÉ÷‹µPž<æÐé¦ÏŠë^€ð-[áÄG ©Ýðê§$ö]Õ^H­w¢›tV0¥Ë%©ñ o\Î\›à¿ß»Wè 4Ñ“°JøÓ­Êôâ¤~”‹1·Äˆ w€Ù§¹™˜øˆ ŸA<Þ¾i@¸õÍd÷º¼Zgq'¡ä ОŒÌO??,˜ ü2îm˜(º¿pÓ!…[‹ ÇáØDrÚ_›8®†`¿Ú9ÿšEh»6ßóŽ1ªŽ‡ 7ÿëZm@6_dxþñ@n‰/&Ê=બZqóR!²ç×}¢IÞó©dÒRB5wÏlÑh¶¦²Îâtaý|"dçF¿çíÒc`jù¬†#˜ó=æDíØi<Á£C¼Ò'ôj¯Á϶Ïÿ‚lígÞ.à éÀS<×Ü:Ze¨ª‰£ý x+»Ê'â¿¡Ç樼*nÏíÇšb¸ôeW­,ƒÖ¦ÎàšÍœw†5`Ó–C—3ütö³] ¨WןÙEvå_9"µË›Èë¶ÜRåîó¼Ë\þ™ S&;Ó»n@ ÔÏÚ-¦½žŽ ²<[8æÔå¢÷Û¬Ûàû«…RÖS¾®xª…ò9Opš(!“„NƒüÈkÆÍ™CÊ‚n`éÎB&xÝcèÚbBNœng‚3·2¢¶®DiÆæÞÕûܦŒRð­³cÔnw1nk`ŽÃ&ø¸¿#‚Eé’”‘ÂF_‘=[‰†¡ü”oAôþ1ÍÙm[ùÈAÛhšPãH4¬Ðèå-”<µZƒ¨}Ÿ‰míF¨±Ï–©}s4 ½XÒ E‹E³di“Cv.'¡¿ÅˆÄz.6§«0´ö¼ö )ãYƒºéLôºë sr„ñvfŸÙ‡’ßß\ˆ€D$”뇡êºZ¬ _Bx1-b3 =¾Š†ŒžgnãOo-I8Ÿ½˜Þò´¾ŸÞ”e¬9ûÀ-J‹ªl·¢G"›aíš1tÅ^Vp»•6w•Ï¥W爵¹Zxòówˆ*rç ?`ršk}˜zn”Ʋ@0M´ÅòþjVn­Þ˜³€$Žçb :×@çì|nqéq.ûXoÃËPÄO#žÏƒÏª[˜GÖÿ`§â{ˆšZÌÌUÛ©‚ÅÀØnc6©'bK×Úuóöº`øòJ°~.Ï-Z äÊÖ@OœFÙÆb“Ëm¬± àbÈ Ù‰Ò·êÆlÎþîÁ¢&V#š¥çàì}™’¿ì‡5Ü/zÝpG!Z4EÅ/¨ÅqêòPTÌßL!!3eè¤do¸ŸbÇLÏ¡sk²VFŠ‘)[Î1®éiÌ÷{¤¦¸bxÃ|ž´"ÿ³%^¸q$ÆðÇC©ÑVß©×a¾¤' ½*F?»tpËü†ë6G1÷k>ÙRaKën}d4µiôÇuÌMŠìåà•è®x C¿CÁŽ ôwçGB ú­X@·ìOÅÏ^3©æî¸§W•ˆMÚØgbËÞœ•Ò±ä¤ÃSöó†Ï°ßsƒ^Jr7Žxpæm»'†}™ßOæÂ‘ª™>ÀGN[Õ×Õõ: @l*°Ýô3t©Ì4oBÁ»,ålJæZô¬%–ß„I@t%löäò.:L^îð‡¾¥GðÒÆdåK ’b” kNÄ¿W[T;ÂvšêÐq»ÉUb4 Ô†Â1kf±ã4æ›[jüU&º»ÌÑ¢L -W çFç@õöMX|oYè~ ²zùé çå qH´l¸Ž½u¢Ä¢MˆÖoüͶK@ýæD¢|èŠTѱ‡X®*‚â¦;ˆ³ô2È^cÍm<…37H‘Î=è,p—5}¥FeGcFË´ïÿ…î+ï0ß1tMÂx¹þ[8¢C×îI¥ƒ Ôô\z¦ÇÑcZ–´ëf,1ßš×Çz9 …z$³"ž•Ò_<ôî 4ê¦$1Þ£C¦UÛOìþW÷ü2Õ¥\«Ù—‹yAcµ$+f²ŠZ@cï2Êì }¤ ”•N9Ü›êß¿3[Ê“©ön?ê7¬ä•aV§h™¦1M®³ÏΘ±ÉÁÓ¶š=2ò#ðèì|æ¿ZîÓ—[!~@.MÆ ÿ¿¨úNïÙ%Že`p(»>¨ÁŸy³éÇ‘!Fñ~&žQÿWm¿b×ÎÐxtÿ–ŠüŸ@wÏ{6ü§DK[ôTR¡®|æÛ!d¯$™þ1’¿7p_Yõb‰é(Uë÷Ñn¬¼PÏJVdĵÉš}X}´ÎKåÁˆ?5Ü&¼é+`Û½ýl½v+ê‰Ó„ÑŒWÉãrwæ¡ðȹ2kmÕJ§)Räväù©ö˜<\û…ŠQX. ÷žL‚†áµxháë`s%½±ËÙÍj¡æï5 m‡š<ÿ£¿öàËc§jËŽÓ‘½xä{¼ˆìÃÓ7zeÆM䨰·È´µjàÔÕ×8y´’®‘ÑÄI‘¯gÕœµ#ã×B²æ!¸¦3 ÷„VãÛ?7Ñìk'·eY«óã£'À†þŠ€¸;IDA³›]×5 åèݞˬÌiR>þŒ~N…+BC`´Ú_Ť²ñ1®ðj‡11ö4gj¢þÄœYÚeùêÄ2_÷Ä—Ã^/šôj5¼2™æìŸÏœôÿ:Ž·*´H;šÜkÏ]Þ5±MóæÂ‡Åžë?Î½Í“ÐØpˆýW=ž¯-àBºáø)^ÇJ.2¿KëPIãnRrb“’3a}ëBbu, d§ó“q²¨¹h?sm±&‘sÞNœ¿,ãðx `xT.ˆ³ƒ/ñ@Ö> ëŒlÙuNÀMÀöØßE#:ÄH-g°ïKóP9¯\ƒU-xùz-Iç1'üAYpõ|7óëgë·D 6µô.ÌJ¬ez^  xA2ÜV]ˆúý¨¼`"‰ßå°öì\vî¿°ù>CÛËû˜å¼ä@YŠäðÒFoCŸjE}uÈú\î¶€r|‹½ö‹Ã¦ŒsmèèâĬ›¨ÿÈãHñ¾Þ¯R8ޣ瀩kÀFÝ ªK.àâ$H¶²!Ç¡LR¥i/©sÌ«"=àóbÊ*‚¹ {|!zYÇYpäR>™fQ©‹Þ)+,¢˜ôµMÔoi{ÔŽRºÒ”õ2tÙׇ°R7Šìý“HOlù W³¼ FŽÀuã\îÏZC³i=­øÉ¾:q?f ’7|·ñ—löã³vÒ¢D»OŽÒÝ)œ ´¸;â-©Fð òXñÚÕ!ú-÷qMb½YÒ„Ö5½œ³>´¦ìåÃxठmˆÏÀÿ×#»Ø,ÚÈ=3jŠ¢JöøTLx½±#7ØbZµ¬ ¼ïyS•ß™¥fÎtþ©ê4 BÆfñÒc4HfêIæD7è8œÅœ§ òì.=½–ì¿e€™_ ø˜<™B:Nâ¨`–¿T¤Öí^Ì¥©é¸§ö8hd½g«dmèÁÓ/˜]sNãÑŠ#´aýfôäim®ÙlH$ïdUþCX:H£‰(ùÆâ¯Ã8/v~L¤Ø*Mã£ÌAüm*>·ó¹}ïQíZ¶öÐôÖËö[•dœH‘î ³BSˆÌéû‹!ebôÖ®ü ý[í‹Àû]ßð©­3ÓòD0Úƒò m`ê„Î0ï›Mɵ,ôœˆoWAůsp愼¨zˆb•h^i.û¸4¾HSѪVr)Øšµ®{Á>8åIvNz ÇŒué3ózqÃ&2ªLLóÁ*©×(Üe„Æ!q€.x»•\¸ LG&[’Çìac1VÖˆE¿Œhaó*l+¾Ó²ÜEôï%÷O¡á ÊAçä\ËJEµ›uÐy\ùüã Q3˜MO·¨S¾ÛAdÉôtœ½¾ ^¼dÛµÈéÄ«ÌÙ=?©üƒ(H—NƒÎ¦1ÇÄ«™Øß™ C쬳øêÆ?Ö<{6ÍË!ê²\ø|©ÃûvòäȪÌQ«alþ[†¬½&qê¦?42Õ÷uq1ß:8yål Ø@£UkàÛ¡z6-E o¹ÖÂÖôš–ÿ/ÿ–,?T0ë€K=…pºyñõ×g‹|÷ýƒ·‘ËX×+’t›Œ‘8ÎÂâ°f”ª¶ÀMõÈy h-KwT°>N§ °Ôoe´AŸ&pœ6÷¾H¢meO§¨Ñ›6Æxíä|âµ°½ÄÝÔe˜¶=] §ÑÉm‚t|p¶"ÏÊQ;ù[xÆq/y:º“–¤œ…ÎJŽç|ä® £‹nãÔKåÄ—/ «|héÓ*,Ï£Œä[oòµc½ñ7~&AïѺà c´Ï ÊÛúÙ¦µ³!î\x›=ÿüä™&+¨á†i¦æt§°/¦U˜ƒT®°ƒ­P,ýlChÿ(µv°g· ö“ñ5$”£¹b4œ[u :¿³M—ò˜ƒ~&ô[¡ܯ>±Â­h»î sF写aOcJ}“$È5R×j@÷?„so®@Ô¹£pÓ?‹*׎¿_i¹ óÁ§m*wg,ƒÒSåè`í!îOÉWLú DVkà™ýaCâ4\¼Gò,©‹äRf¡Õ:T¯NbcÞcJ°6“Ý9›®»½ˆ5–;Ï™´¾”}ºGŽô™¯fžI©‚î¦Ý±åãDþï—SÿÕ*Þ¼Ê^tÜEÒgIÁŠ{aêÕÌ(|]Û3KŒ–ˆ.Ǿaôh^O„”A£F)Z É‘ggÆmMÇŠ­Ù[Y{°ìk÷ðæ?h~Ÿ¬80ÄåÈÈÉyÚÔñt!QzÙËÜØ: Ú]äxøHŽÝ¿ÅÞÜÈM™×†el‚Ðfó‹ítdn&ᘆ€·T&Œ,Kæv*¢%D±ŽK0ëõ9VÇI½‘|µš: íbü¢DpÅl3§¼ÏŘS;2à‡’µw)$RI'QŠÇ{X=ƒnºt ^CéTKZ¡5ÿ2C˜ÂvsÐ:¹Ú›Ø3;ÕÑà¤qãïBG‘P½Î6j¤Á£çÙʦíLx¨a¿i+†uuÊÈõÒARØqdSíŸ>¯2êcKSMD°~,wKÛ‚†Å5(U!†²¦ôGÂ2VA÷Xôal:ëAöƒçß:f&ïÒÕšÄ\<’Ä:ÛáÂð“\µO-,^z_ñ¨gþ,+¢_=+P¤ðrgÏFóÓGáåiêT׉KfùÒDä§ŸÈTLÎ2f·»g€ÏH’¹sÿ“Ãݹ óa^ÛÖMØÿìÉâ0gÔ忺eø1›6›)3Õ'ŸàÆwäÑé\øùù«miA•W³Êÿ,89Ë„pqU9vœG(‰=DNkrŦjS7å’øU€<Ôï‚SÂÔ¥zì‘6$÷M¨³ûc\·"’©—ï@7±Lp”Z. 9žü°nU ;ôSƒæžÏC÷´E]¤;“W“!íòôýqÞÔÿÆgç…[Ã>±žAt(b7†ôÓ­‹¸”±Çuk霰º{xŒtö¢ê‡H¢Y¤÷é¡––íÛÍ8NÏb´ŠWÑáŽø{h"ÿIœBØ,¹«ŒÌ´õ¬ñ·hüS¼å`¹Ìcd«œàvÔn¢ÝG”?FOçÂÔ#ÕÌ̆ð¦'{‘YÎÇŠbËùÄH–b"Êžd„BÞ  ]âçkú1gý5¸T= þܱ ¦mw™<§4?6‰,šiFvèšqƒs‹ð×`081›0ÂY}²¤h\èGø’¨§þf×:¬À›MˆûWóД£Ë>nÅî[ðd¸ ®¼£Î'!õys`xwövOÆèO Y¸¬òg¨0ìØx~ð tyõãs_I g ´)6¿½?!ÿ¥£¼˜Â_ÈÎWú¢9a¤ÈŸ´NÜ/´¦>‹I‚‡.f›¡»ï!î¯S@©ÛˆÔFG÷œbJNÔC޽ zFŒ8×ÄPÝçÕlóë@Ü~g&]¸z3ºg2zŽ™à2ÝÓX(4k߆sJQÔ)ê:ú¿˜OÙÆ ’hDþ^æI5-=ÂÔ•-&§{8yîdºMý3lKr!ŽVLy‰ †ùp™´Â6çútlŠ Ìß`irqë<ô`§;ÍæT†©bü9AzÍÁ„^®ìfϾœŒ‰œ ÕŽÑ(Æ™…å?lé<í£xz}æÿMsœ ë2R`‘þs(¿çCT;'•± ¥ŸÅdÖïeÿΗƒ!ÚT®ÍŽ\¾Ñçû²˜+3V"ŠôHÀa¼üp;׫:ÝÜö®Þñ…»)Ñø6þ³øê6 ŠAë'NŒ˜cë#=‹iëßÖÒûó“ÚŸHŠï®€¢ Ày-P؇ý ‰x¥q äŸe[é2ÈØ0 €Ý–¦O,Êg“Û:1\›w_ =EêÊ|HÀä8†ÏNŒñÝs_þ†?ºß™ˆÓbO{. ÏïÇÌk)úÁ7Ïev`_È„ÿçKdá±nqLöI#‡£óè¯]r~Cò[&-wãHLð1È3O'Ä¢hä –xhôÁÕ¤+¤ ²W)f÷#‰„µsƒ¬3©Œ‹æwä iÕ=ݸ1«‡5ßvŸy4|ç%'´ yE¿B‰òCGf’Ý#_è: ~€ÃƒådCNŽDñ1Øçå}8ݼ ÄZÒÈNŸå¤ê˜6©l³Á_ÎQÛš8’í_ž%‘ƒæ$Ï\–|øÂCm>ΆèGÂtJO2µ­x+‚ÿq®eÓÀK50>7l) "qrèò¦ZRÑ © ÿ;ÿ@ýV ûŸA§ «ˆ^Lá=»¿hûÑcë7“—E?`«qðägS#†&wµ’Ôg5oà‡Á/[ØÖ“ˆä1¦g”r6b’%ýñW§“Z>Z|ð5h>Òùã1ÞÝ AÔjß‚Xu'M!Oÿ,±€?íkiûÖ6š+H=¯ÑÊ%¸ÓwÊÚùšgÍdÓëm 5«ƒXó“ú+?©KDÄÔv,‰¦%×2@ÁµósS6ŽjXÐá•UÐlï‰ÇƒšÙœ`ºõU­nðQ®‹/¡ÕíïàÂŒz¢j»ƒ“T&ìŸgÆ(^¾X {Ó;pd¬;8aFù<Ò=u]ÉÝ‹õ©S°Z;Ú½+1Mwå‰= FwLIÉLY:ßGäyÐ@ö)nNšLûäL€ÕþÂ|ÍÝLäm–âFQî²{Ú†LI›¿å¥éŠQÚq7æ<  c‰›“ÏQ¨ðw;|µ îý|~|áìVˆ$ç¢6Ògºô¼ù{0~ mÏ`ÚΧ¬c õï~ʦsèüŸ1”û“m®Jaló„Mz^^¢M\— ãbµÄx+u«YBÞN¢EvîlwéY\û|<7ªfÿ÷þ[ø0^ÿŠGŸ×B|íQL;Rƒï†/` GËQFH/ޤõ½ÄÅãÄ„—bßEQró)¹yW”¶ìÑ Múg!ÌÍŠ^ww£DÇᬯüƒ¸òZª‹Kâú!:sï!’eiVñã¸Öv Ù¥"(·ÄïÝóC§-ñ¸]b `ã˜~ "üJpú}WÜW…åšæôŒÕ%X{ÙÊ÷ƒ°°&Qh¡)ÍÐ%µMã>ùð<ìǺ¿¡ÿÛáÌ­ó5 ì¹ (FÂð¥VTya Ë<ȱÅ*$Áî|˜ ®§žCâÃ댟Ý4ÚVnǸ˜O¦ m(˜žÂXŠ*Ñ9Ü现]Ñï`»AçÕ‚Ìî¿ØÈcÀÆXyœBÓÜʼn…jÙ»Þ›?_F¼«—ƒÙ¡W˜á4Œ¼Ô©]6Õ*T§¼r|ôJÕF4w¬ý§LK9åŒÅïOhjfˆÑÙêTãÈ 4y74B§Ó’Ó[H˜@{d‰>1¾÷¿mücÒæÔáœΰv ƒCÀa]ˆŒþŠC+1*yÖd§|Ô„ÿ/ß™ÆlkV%âqôµÃ1ê>VGçœëÆD~j%HW§^€ŠŠXW'Ïœ½¿ O!åËÀœ=æ´úš5.‹ ûb7ÐQ§ñ푸æª=yý0<¿2Ž¡_ Ðo©98õM)åß…µ ¬ÓQê{J‘\Zÿœ¼ÝëF¶wÇÓÑö}8ÇC”{G”ÎnÀ¸¡mxŘŽ0ŠÓ8ØW"HM&;Ó´ØCló¿&pšŒw6éÐÖ™v6ä1®ù¹t¾‰]&t#JiÅ8§ñ^™I_,ÞAT}àû¬¥tÿyð=Sñè×ÿ⟨/l}ñ¨ÅÍ9‡é»ÿÊdŒêà´oø£4™ÈÚwsÈÆ8èr…m”L7ƒ}ìÝ#"ê¾§„³)üÄuêQTÔ)ù»w™gJ0­H“  »<Àmì&–+Ñ;uÏÑ-mu•tÃ]Ýýø:®V¼Ì³ŠÜûö3žkeF{²ÐæZ,þùsÊ´’Š­ )¨Ef­ê$^1õ~:‡˜»ð8ÆnAÀÝà!UÂ|úïŒÀïà™ÓJøà`ñëû³½Œu7ÕF›zÀ#z ‚Žøá†›sàrÕŸ ûÿÒéÞkî± G ð…¢M«âÒ…ì æùÊ7ðô¦ ¹—’Lþ^êdU³o£DS&® 9kûGÐós-Üx¯ ®‡ÎÀµÊoϘ‘WÖ\fz—Ù^Ø>ëôb.<2Û„ƒž¿àBÓ{ö|Ùµýx‹üÌI´.5þ›ƒcd êÙÌÏýÀ A½Gg™àÄ:vQ lx¿…½zfœ‹[Dc’/‚?窘¨âGùcÂ`Á/i\ps!=ö8;Ä ß¿"(æ& ž}/Ê…ÈÑ]{Ç@wÞTû.ÿ´³ÁK¤áÜ<|Ñ—FŽìúßùoz¿7³!¯âiÿ4BýS}£¬®G€…èM´¢|=¶D¯Ú¼s½©Øu0@®àO^º~i3Œh™²wxÆóõöz!X‚ü|ß‹Û^©²â)ü49Mù<9—Vß8–wÀ<ìZˆÏöœ„¢¬?"‘Mú Ðæ« d ¡„rvk`¸¡pÜ©G£õrIö|ý¤ Í.®£Yá‘hû6ŽÞ)ïÀ_§GÁXJ‰Rʦf¶’y!¿˜ã†¾ÄçýrAå›o8Cƒ˜°ª÷à8zqY>ܼ ZÜÊaiL€¾?~™yü”žàÉßéðaÇ~bïð’'-…½Ôà– |[¿Š¨Îã!9îÝ`õc1198Œ|Šaºª)D$CÌõ'po¯éMŒɯxï“ѳS€'xP9k2+o¤`zú:~1Áâ‡`®~ƒ\ˆÛ^4¾–~X'OVµ[Lì®qÃ!ÕPF`7i7û ³¿.$ºs½ñØÔÕÌý® ÔJ²¡j"–Dh0g|Ì^Fëp+ µ•‚‡AX|Å=áïqöÐ*\¯;ƒQv•Á:ÏsRðß¹¤ì=k°ÿXïP<ú†)á•BÅ‹Sˆè%iú-í òuÌ…™ÁbË»0+¢Wÿ‘&o¢Þ¢¸ö è°Š‚ª ûè\®A ž/dï•_A©œ\¶[:¬éF5ç™ú>s¶ÔAÕÁ°Ëä:DØÆá½;j”;9,‹> eì4æ­˜#ùñæ ºòºÃø¼¤G>ŽÝ|ž™À¿C×ñRÑIäêèÎÕõ´+I8:ù¡—"díçh”ñ(aŸi#ºƒ5êv¥¼¾•àbFÀGg]óv-LÈZ9b¶a;#¸ ?n’&§l è»m²¦ÖZÔZUýß'R³ ŸXÁÀ9#±»ÅÊ'´çÛp=…ñ}þ fÿþ‰M± ŽªTãÉ|²{¸ŠÑÞ±“]jx‡‹áߤ=d$è4Û5y!1Üöô& ‘ŠsÇÁ‘ñ‚{謓²ÔÞ¢[¤xHæû‡Ìó„³È“<Žs¸9$‰Æ®]°÷LÚxÿ3øˆÚÞÔx2f9áÿ«kMXßõðs‰uç*”ÿT H›­fW'µÓ}sPÍi)qf^`tC#¬s”uëº º'21RD…ö¨¨Ò‚ǾÔKæšO½‰´÷ì–9Dw¶>C<7›êJàÛk=º×î$fñÙ½mI¸—F¡ÅQuþƒ5¶°å>Lw¹dl3‡U›yð­Çö¨Ý>nMÌ#Ob‚+`FUrÞæ3õgÈ;£mpÁK€VWxÇÔ•Æ?)gìÓ´±ååRÌüp€>šü’çñ’ÐùºtlÓ¢SèÂѳHbÔóè'9[ºt‡ìÿ«’—95Læì/pÿ|6´©1dú"^òpqœ+ !==ï0åîfmóàΊýŒÌ#’)Xg25a½õ{ä\Ò`¤TŽ©lc"øG‡ºfB¼âfÇ£[ÌÔBÂYt¬ˆÔms!F+®À¤‹êl@òj¢„çèÊTmòéˆ]z¬o{ïÅ'?õ‰e˜ 1ÿ¬7™2t8ÒËgØ3å|ëÉœó¯{@™Ñ]ÉAÉQQfoàYú±&gU¤‘`žËÄmn!Ì÷<´x ؈4àYP!g;ü@ïàëP¤ªß(ªÛé’ý.·Ü{öÿ‚W›qÊO§Éý-½§Æ}J1vk$Óþ̶2á+d_ÁäŽqS×r­|TéÑEÚPî·,[DïuÁâ—˘ +Cü­GžÛd„º2æEòFZÆq<~æZ|ÁÍç¾0/û/ÍÚŸ}LsØ*Ýr¨Lè‚©×;´ãÿ™À µñÜÓR€Ëw¶„ycåÎíÙ›ÜKÁ©Ñ Ç<>¡Ö‚§ðJb¸ýûËœŽ]‡øDœÄ¬VBÑSÓèi¹ßœ!#5zf°†qóÛOÏp°:Þ›ì3r¡Íwûðãl>ê¾L–&)Œp¯/Iær˜Á ü?sëËѱ‡GÞsAEõV=” K³àñ‘9Ä2Dü¿ ¡U‰ Ó'óåZY³ÈXrø©3Žsë –LÅ¥¥9À€é¹o[V¤¯cÝ.>„Í9édÒèÚ óÌxmPjèT”Ä3XìúBúa4WŸšÎc¾|Bn^zÉÝAVá§­_9sW§æ«'ìÿgžß]ƒÅ>á¼JKº¥ð;ÖÇfDK•që¿X¼ðrìɭ¾h›ò ØråÎE‹eÓÖw! ¾xsVt \Þ2²<6J~Á=O«(ïcòoö^üØ} V'`Ø¡vÝìÓ¡EÎÎR‚Ü?M ­Ê!Î7>¡µöTÕ%ºß~Á¨ØUnD º÷zv½¤}˜nOo˜€o–)Õf&3%B 1'‘y¨.F]ML[ìR>qü st| ÈÞ‹œ&ßHfõ=3ê2³t-„‰¿Ž3ùQ» ~W˜ÑÅÂÓñ.g½Û9!?wª(Ì\RÄÿð§F7G1»*÷+Ó#­ú4`LˆÓ!£E.õ¦pî½X }ïáòuÈPgîßQöìû31óÁ%ö^@Ï= úä<]b4ÆŒ2ó>ÆjAMšü²ÏTΦoQžNº†öûê@¹9·2èúµ…ï2r¤lCÄ}Hmi~å‹"M`5õ7Xí¡×§¼ÇðôbT\ä !ÄU”Rƒ2ü¯Vú°µ%-¡ë!©åÎvþ «#òœ…‘W à0Uþ%?ßkX&÷P%øÄôrý¬ãѯØcÒ'ü_ò‡ wÈ[ÍgOÛÝàxð Vöä0çè“èB毼U»9|•˜ŒœE¼ôe‰:ý¯tá÷^ðÔ‘¢íËåÈZ][’¦ON¤%î I`cÅKž(›0nï»Ø5OäH¹õ ~‰cµ¶ï…E¯0Î/eY©"u2«L•ؘj²þk\Ñ+eŒÍøˆg^>dösnr[[Ÿ°†¯¿í’t(õ2@¾Xf½÷c¶*ÞŽšËFr÷<ÀÞ´OL±e'®YËÓ0BÞ%b”}ô‚”UÀƒáEËZ°ezgžÌ(;YvŠÏÈg§v4"S8ÿdþc‚öpV¤Âë1fªÕT`嬤’#ƤŒ*ŒãÔRÜ­êÌðzÕCDK´ªLcæØ’S“¦“›§á7ß8fýÝqN1ò—ãZš °U­`þis²%è8ˆèDá@å"zK·„I×™ƒk9–·§ø]_˜És|­‰Õfæ[3¼¥«OõÒƒt@1a_à”/0¦ßÊ 3ªÀOóóPvv5ÖÖãžëK°0ú#ÆD/›¾ðâ¸(fQL¿¹äu‹Ñ ÓûÏIŸ™W÷±þ(Å«ŸY·Æ…dì¢YÞ4aÿä£ã×ÿÕ&㱋bÐ(VÅ|Uæ%ž‘nT½i*æçK™&#’Ó¶“þ6 ¡Cãq¡Õ#ƒ ž†älmÊÞŸsÁ+m2Óe9o,PÐb ñ¶íb~.&I³ñ¹Ñyöý V¥K‰òmYL_ÞËÁd£)hÍMƒ{¼XÔ°~à›Y^ÔP¨M§ðP³-rá®&ó_]6Wð4\ÚE¾qzáMà]\u vÝP„–¿Ó'øod¿ X®Å¯—S“ó,“¼Ççyȹ NšSÀAo¸tî%N"ý g¨c^sŠ#o‚ìKš³VfÖ%£êrLôbKÏxÁåë°õk1<êOçX,ê‡/ö»‰d– ز ÇžsgžtÂÔý\ð}¿‡y XSü„!öŸÙËÏÆÀ@àÇÇfz´´X‹YÇ^Eöo¤³G¹åϦÓE께Œëw9smTÈ´•¹ †žäÝÜlÁ?–ïùix5 ÿ$ƒäØçíƒ/vŽ0nH@ùb#âòRŽºûŠÓgOBIô¦­öoÓÜ×rAfˆ­U¢3b.2O—K2ÂÕ¸kÝ´ á|rsý©p¸Ä8?R <»V1·Ù Du£5V`ë§_ƒ‚Çô‘‚}É“ÇyÈ—}ÑÊ!`&78@èÌÍpsì]ñ¦+–n'† ù3ÉeÝ‹0yÃIXÓ[…/WoF•®S¸Ü9¦Xü…ä*ð¡Ç תŽ0w$“õ6³Cµ0ƒ­sFœÎêkb🹒ÏC®Ž=Å÷ö“éìGÑ-Epª¦¥@>îIñXØz%ž¹¬*ÆÊ‘äi± ÙpļÍ{׆ 2Ó‘¦ÛÀ“™S¨äŒƒp™§"f+·#SH¥³<­J¤I3äðÕ¦¸û¬;Uö~_«†9óSŽcð+y’ö(šê_B°Uç‚+íÊ›DªCI®]%†ß2ÄA&Î:{‹ë£'äßyB‰sê°6%I-ß<}¹ÏóÀþ™ò$ª œM,»Kv “É:Ü2åÛÏ3cè”X¦ÕеÌÕ)®M®¤õaÝ87\w&ŒÊ™CŸ½?S$wW4´ø7̦֠D5_ÌžDËÏûÃàî\®ç>êoóÏ!Îût¹‡ ;¾W)e€Pê^ä¸ÉÜ™/@½üK`×Y¸ÿð$;èü¦$­Áƒ=PݨÆ|ýøq‰:Iç7§¿öêÂÖ¥ Lþ¬2‡ufç³¥+Þâ{&1Æ“RYŽq œg+'êŸvpn1ÑÇ0+ýìi¦Š8pwÑs‰ñÐûP•=gþ>ðï,f§Š.Èþƒ7®Õ»k3\š³›­ïØ/؃'}è­k«á@ÎOvëƒdrî “æ¿€n¿8ƒý•$#<ü4ÿézLæ¿ÂŽl¬ÇÌž¼~” ¼Ì'˜5ò×A¿2¾å{‘m:~èÊ|¾¸J”l˜mªúôAÚ!\!CdÜaÝ×K0må=8˜Q„;±r:GáFðU‡ ‰çpŽ£YåÕ„TøˆÚµ‚ÄiE ?ãC\cï€Y½ ÖU¹Òp²Ü-uBÿ/ãaóQgvsÎT*š~NŸw![WÎ…BL|X‡3wµÁ´% `"BÅý…èÀ©åh‘Gïu¹a‘`.³0‡—ºLºÝ®òÄÞø#¨€St[(U£«¹¸oé!²:AëyâÍS²ÍãRËýæ:Œtinþ¯nzfp86LÆÄ)³É/©hšžQH«Ç Ï> Òÿ”`דD¢Î’L¿}”þ´ÉFö7C7D¿y ´qç'X%Ї;oß·Hæè!C0Øò_í)ƒß—ºÙè‡ÃèxY‘öjÏ&%½ñ(ÙFiÅ*Žl;!¿æÃIøn›çìCÝåYŽjmO€9gÎJöpþýC¦ã«6Ø},b5¤ò@ï1?'é#õ| êwÁ‹ñœýA­–¹’§Ôà›ïw°»£NV]5$šS_ÀåúãÏJ‚N¾0ÀZýÞº]â°)ðÑ6Ž¢UЬÞãèY7çMÅë#*äÛÕ*H+¬;!)t¹•Ä™Mu±æÔ 8"E¾›ÌâlÔ'ŒÊ?¬Y®Ê¼Ñµ¼ØkUÉÚÝ!¬=.&ÉO¹g9*û…sÊÊ퓇ì< á.¡øÖ| $›ôã-~grek'ðk!uycLľ£È;c<–Žá”'Exz.ö­²dŸ–†^ˆ+,øÐaÙQLÓÂ}|™dPØëgùö­@ÙÙôû¶)ð‚9KºöÀæÝZÞ¿ÂÞ¿–”×(„—;“ôu«i›bg´$­­÷‚Ï|¯8Qωa>µX{š~àh´´bYƒ¹~.XQÁÜ˸ŦíKçˆ^Ô£ž:+áž}uÊ„º‹zÀÒ¹æ4RÙÒ‚b~׉Ay]dY—dÇa‹Ö‹‘‰„öÇÀnŒ •µó‰–÷*Ë“¸¿Õ¨žâÏ~êž Ÿ×Y‘tGEb­7 ê'aªœ¹v‡³ôýS¼fQIo)L¦™4ÖÚ˜úú'$z˜Ü1‰aövÀô— ¤ä–3Jœ/OðÿÅÇÚ1Ps »¿ÝuÞ_†ª¦Ãäû».rG/mç!däØüÞO‚,âˆÀqáøbíL 欫"³`ß TIfì°d×¥;Ò­‘×@qùôiœF¶-T¤ê÷ߢ®¿ Y$ÒŽtQ ­>{ …‹ß…14®Ö]†ÒÐÌëSïa–ˆ6¡A†tCª=¥ ’bãKø.è÷þï3xf·]5ë"œ JFý$9z¬+šöœGàg!³µ¯›qíGžx;Qý`*_ÇŒ‹ zƒ;:ꪲ=.xùìvó~ú\ø4F\d{{÷Mð¿7sg‚Æ Wv¡Õ¶?°vÑ:t^÷»%ÞõE§c~L Ü~ @tà àØ)½lQÊwF¿œ|޼çïÁÓF*«Ó ~ %Óv›™ã÷lÿÀÜ®s&·öl¡ýœ²©á©ð4ìP¨‡Yñ-Ì)÷½ŒüîK¸+¶†›¸ÛrÄå?ÿ_Ýuó†å¾äÖ.ì…é\Z³ Gj;`¿¯ ½Ø]A–½Ÿ‹ë~_‡2¹[XVv6Ëžâl<¿ô} HÃdfãf=jqº ¢ý¹Kú³‘h)E5"@¼áí^=Ösó1ˆMþÿ»|zÛÙÀÑŸz£}ìZb•2G¥qót=¬x"bÑxç\XuÆãRºg§BZ'û9[ŠÎ1‰>uaÈc•ÿj–Ù=¤ä.r4žÀ½BÊ+å„­iD·gëx®_Â丟°uŸóQLÿiQz¼Aš \uÀ †¨Í¹Æ~ñ93æ2«crᇇ1˜ÝȤ¯wfU¦2yóðü@=ò©kÒ}>À×CÇW?0o)gW%KÁ»éKè¼îŸØ¹@šì꿈×Ë¡ö¬  ås™$ wþÆ |›5ë Qø7aÿ¯Ã²ØISš!úEÝglH/îÂ%)Õ(µ6Š”çtÙ@œóÝa¥›>-ÜZ!rظànl›†1ajäÖì-äbðbsC‚*„jƒÃíaøx X·Õ½uÜ*©êVC÷ãuî0òõ"·ªË=2™ï —៸±áôÚ¬âæ¸. µ-fDêö²jÒIòúf y½Y“ðlÞ×7DÓ™tž|¥Ù°¦àë¯ÏÔ‰´3î-–Tzö.rDÇÿ[k“Á ès÷ÀÈ•¢$ý¦:Ä\kAî¡H6dí<¸Cqbÿ;UÚ‹7ÊbøŒÌ}™Ý Í «þ8ãÊûûˆŒ‡,•¯´§?Oe27Œ7q º›@Ù0 v“O|«èœkéÿÕ-CöËhX rþ뽊Có|éHûÚƒ€j+6ŸúÅ,Ž€ó£¸óoá›Ð]-m[æÑá¥*»D"4ë5´¯H'FîàëýUL]«øÝdãÉåKGäÝY1ÓôÕÞxZØ’J£ Rï-öÑ¿2gX•©ãÜñJ)1Ȇ,)„:­ï°¶+â;Ó`Þ³LFSl/¿‡;ýø:<¸ójDáø˜‰þÇÓî¼gÝNdŠpñŠAlða¦wð‘¡™¯ñă4Ôÿª…³¶2íN„“[7àýóe¸@ð ”ç]Æë·çáÝ— øÏ \ýf?1É7$kÕo0óAŒòÍ2¡N‰Ò%²…0/S„pkEȱëú„ÿ¸ ›JÏ^yÎŒ@ðÁ9$BØ‰ŽƒBS£™6[ t?BuZÈ“þäãòDX9z f_ÏQÊR©ôϰvd]~‹’î(¾²Š&<­ w6wA¿ÉK˜1œ‹<·eIÊaKü×ìF۶Е:¸eçMâ7[ƒhÿïü‡ËŸ Ö{÷ÜQ>Ò'Ô†…M„ èG÷¶BýóÇœœöÜz¥€™eÞÇËåBîœ,®G?qº4J•Š‘'Ötïàܨ²Ž>ª¾ wþÉ»ظ çñ³pÄà*hpæ’þ°µTë/'2OLˆ¨å,zD6ÇÌ"³ `E òdB€F1饽GÙWÙ1ðê)®×5G×­¬Ëó°ôÛ=/è²@™XÞyŒG3·‘E¿0ïžaFß «™È&¼m‚$%ø½PÞsùˆuùxeí¸ÍXÞ€IEªäé3ÿ¿lx€ŸXùÒ¬À>²»“a£u5ÉÉɷسùüt\,<èE7û¿Ã‘y8(úŠ=­iˆºÌ8ðæ­‘g?l¬Á”ú¢ò2R©Iįu‰Ô€$× X?©ˆþ Ì…¡zDÎr]ó K^üñû¼ûò•ùöD‘d|n£—E‹P<ˆŸ,ãnÂNïçx½ÀŒ½YÎnÌ’&V5/ñklû·¥6öE⨽%¼– 'Êÿ,XŸ{ðé:Gúö?¹MgdŽÀ0£ŽÁš°AÒ˜eç9‹tVÐöòX(Í·Æþ€T*ï)3ÿN¹Üæ¸oú¯?2”5AÐp}ôÞ€FoÎ`ÂÌåI©‘Ñ(«‡•Ih‰Æd~ÂyO@‡'æTŽœ;ɾªlã¨Û5±|>gÑQ@ Ïïå¡hz÷û|‡__ËA̽XuòÕ¶žÜœƒOªø'ð﮽ éÌ pµ¨œÆ ïä¸{ÐUßTˆ{lVbœ–µ¦gv•‘¥æ«ètÆ’N ,¢ŸRR ‰¥×¥yÉ6¯YÌ{+1vOÚcxÝ¿TŽ7âgíAú_ïègÝà¼þ#*)u’i6ò$®‹béåz4•¡+KK•-¤øW0 {½ÅµdïÌ\\f¤Æ6FªÓY+<è÷v­`–v§;âÀõÎÉDš¶”vÍT"2!îØçŒ!O™–Œ7ÌÙ{á$Ò(›~iI'Q·¬uÙBJš?bÜ´:¹‚Ä~ ¥Jõ‰w)h¿2!¿T¥‰»äA/&ûd({ žåÑdYƒ øî _3ŒÉ: ;ºš¶£Û/Ø‹Ö=ÁÕ_î8@‹ ›åvÒæÑ-/+iÀœdx]íMMŽ]óq)Ôåó|sƒ‡´-sÁ {2üè.ñZåBÜÒbÍÏëdì†1­1 ‰ž³Á/£íüñ˜]±Þ—–:Ö4‡(æî•C0åÊlºX‘²Gð1Íľì…4稭aÞÔô8˜+߃ÁI”,ÎQmÝ'Üf^Y®Õ0;°@‘ šQÁ¡94j³ÜœŸ#WŸ%à"j03¾<„o öj²/«G˜'•ø,e)SÍ?ˆ;C}qÓŽHbeQbg‘Ém“IØØ<šíM~¤Pº8Š]ì<¢ð2$u!ÿÒ¼›h嶀،VÁÇçÁl¾ •í/ 3`%ŽQ‚¼›OÙL!ÝeMߺä{ êŸÝsÙðjäN…$ù¥M~4~gÝW (ïr¡"t¸Õ…¨¸Ÿ¥?kØ)7ä(Ÿí0Ç*Ç| ÉÓßÁÖ^Š}´ì"æf£5¬ 9ö¢gþ&Þ^‹ÉÁÍš”7S›´þsFeÁ;Ì”¨d¦¢ù¶ŸÍo)ÑWF3a"ÖëÓ‡k IÙOXN4¢´ QÍÈDÝ2{BÝ\é  øK¿ Œöžùðßo¬Ø:uFõ…éF&„5[G~ßM`ÏݘLÒs³ÇÎ|G¾z5æå -¦U+vݨäún-ÁGié°þ&ÀµÍ´s$šùôÛWÕ¨’æÅ\Ž™S..N$¹RÔÑþ ìàÀÿ yŸa‡åJNõŽrò*af­Õ§:›ƒpÚw!2SÍšNr.fž)ä´j߃ »/`³É^L\ZAû%38û–£îÜ¥$ rÙv U~ÄœÒˤí§ìm[úsÝ©q¬»Ë¦]¼@l¾\„áUrL©eý÷²;ðPá¾ãìqßÅ-ÓšÑþÀIŒ %î+·€n£%}1&COº%’•×ÝéJ#mòð­yÏ¿Üç¦ã‡ep;S \:Ú J@‘ˆÞÖ!Ê«ÈÒÕí è-NžÍ]Jä÷-&N.#K_ßrÁåÁ\6òUFiyvÄ„ügæ²ù_=PÊäjþÛ‡+¼m˜5ÇA0¬@ôvd]I†|nó&ì@º µ/á¡â¿ïÿß*÷ÔÈǸ¯o%d܈„8±¤ï(î_{­—H@È­RærYø³†‘ˆâ <Ãw’AœÖƒÒxcŽõsÆ?ù`·Eì|¨ùëpû1ÿ¨¡K“È? AÔ¯\GWl(°Ç7ï sW5Ó/f‡­îmxáf Ìû̇‹r–’ºíªëöO¿|ã`}ú;{'o'6'HÑõ°s—p·ŠÛ”íFÎu"­BW°ÑÄl×Ù@ö¶&6ßðzK~ƒ:»Š›¦´BÂÜ›¬ì»#Øœs9èú·œÅ–“ñcÀc|Ÿü Ê›°¤%žχ~:cBþ¹…Éø4i;óС”¨œù×vê*ã÷Lt 8íÉ¡xÅô+#±Ä1{!Îw¿Î‰;gl¿8ú-ß‚[•µÓŸ3´àèÛWpß·›ñ^p‰L%Ǧ2…׃ñ[n8ŠÔMAß…Í0W3šˆ¬ub—žogûmlñ‡qã´c:y¿k&):]ÅY§P‰Ù¦ä—òzâÔq‰Äó–௕æD:BŸvTæ2Mžª”oVN¹’L·§Î¡…íl•r&3$óµ7q†OžáJüz…¿_w0?+÷±V£5è?ã>úJì@I÷=°öf¼Ü×4aÿ£t–W½©D+ó³—)ݲ–GiÓîŒz”ßw”ÝøEŸ¹QÑw¯çC–t¹àr†‰¾KèWAU¦ößt.Ñ e;‰N¾m,”$ͺÇqÝo2ë-ì.¡óVF §à[ØgIº˜’‹Ë#¡è¾<{ á1)Ê’Sþñì£ ‘ÌðÝp:¶ØƒÆ×mD™Jã¼V¤ªB‚æþTÂ…òÓÀó³%ñüÒƒ{oû¾4ŽØ"%Ö°¨§¶)©â¹â+°~¸ùï7ìý¤R7c)·;;WA͇ÄÄaŸ°/ÚJËMèÿ¤A*WwF,2!ÚSq§{(NÉ3xËîÄ¢ÙÂDQâo³ØþûøùŸ3 ‹¶$kVV°¯= ý’![®»¡ä¾bÌù”À¾¹x„ûQZ‘±ŽMÁÎKK‰íè_LïÂ!ºX]„´†ôã®Ø%qÒÒ°ûX¨ ÿ…OE%x[YžîèPÃØÆ.ŽM˜Q1k¥èRäÓ’$‚ÖýP2Ó /{¥È@œ‘˻Ɗâ#›¿ç´Ãcwgàѽgp¯« ð}\¯gŽcljn¸³…ŸNÖ0¥!Ó¤ ðW#œžÇd|kFc³9ñ¿âÖ^Æs« îÔ§Ÿ¦WÁìœ8x»g Y¿î óËè5•ØCçÿ»®ßžc¨ª}öà&+0²–ÎûRŽmgÙƒéç®I.,¼KÕòOÃÖ jôôM]˜©ÂZóûáî™®lú`&ûB'¶kÐfQzRn#ìß)KæŠà¯ï»ðéœ!œ¹ ‡‘ZGb.úŸ`ŒÎoI¬áFRç9Ÿ’£GÏdÁËc#0’§F=’QôËa|ç’ÍõKC0ŸiLÔŒ™{“*Ù9 >èbàÌ<>Øá±™l ¿ÀyÒêMJM”IG+ï>:aÿý; kù…ŸeÀˆ;µÅ6 ¸à©PÈÞyêb}iè¿§(så5#æ¿ãNYÐÏó³ÈÒÖp'Ôèðý…?¦@ºÚ2Ý(‚†ÍùÄz#–3ÈÙ~ªžß±Èr—Á{ÏQëˆ2Ù°w\PœƒfXƒR2ŒjÕ`¹ïuF n7eu~eU²7€ôñ“ 0ÀÉΤ‡E…ñø@þÛA•„¶ ·@z²ä»ÔýÎÎ|Ïùo­Ý*P=‡>ß^“™ŠDSâ/<4Ó'C'¼ðºf6ÖÞŇd!Øl¯À¦çGþ—ÿŸ,ËËñyé‹·‚æþÛì9}CRá®A.Рª×$À>n'‡/®ŒYðC†\¾ù‹™ù}£-©d‘$î^ÀC^¿‹&0·&Ï|ƒs .â[Ñ8¼Iå©ð}q&’áè_ÙWGÒa’g7³ã°;y`u€Js$1}˜Å³ž°‹Ô½À5V‰Ô9ZÀ³’)DÙb*–ô€¾ç [µ1”JPØÙ•m«eABp+»à‹d=fnr“Qpòb&Ëë>2ÇânGñÏq_ðê¥0Êùì†Ã&6„ÅÚntÑf1ÒäZ5Áÿößs†‡å¹¼ƒ¿[·ÔŽƒò\%˜Q.Dæ 6ptØ“Ù>™lº¨Yä—F¤xŒ÷êh ­2!ž ï9ÕÌK\å¾Ý9Š—Â$©A¦ïÊX$¼*·7³?ÞÈBÏþñ¼—q?ö•ƒÉÀ5°¬‹ó¤,ð•ëÛx?}=‚d×é°D°ëÃFÔ‘æ'·ÿ¡Ny>ì<'Œ%“ýXÁ+¿@»¯v^^?Bk™¯±}¨ÿm½æ%Ĩk!­SW&&ã®^R뙞$ãËàžFü)ªC‡›"´î$ÞJY3¡ÿ‘]¿aé6]Œíc³Ä7Ó®£ßO(ùv]ÓF÷cÂY(Ó5Ÿ5Ý'Œôº&9¸'öÑ#Žúö-C©‘´dòì{ì/ÖôâÞøß€ÓN3N›fÑÙ›s饰)¾ .ïàúf7À§õ9œaŽM´™J-V>g3ø#è’ñØQ³ ±óê‚z•idS°9õ‰û‰–·Ë@°6 –ü-Ó;£A!w;ðìS§|ß$óùi_©$á]Tƺ’-AO ´¾Ü ¨ž[;O#}¬<Œš®`±r“šμ ú9¡ÿŸ)Ä@~Ï ”ô­`»oeöÄóD±nÔ>¹kº°ó£.qq=ʼ߲öaxìªe.îûÅ8Þ=ƒÝoê±3)o,$¿ýkeó||îd »Ö€ «W³=È»Àkð+à<ôïÙ õŽO8ücsAUd64f™sêú¥Qh$‰øÜÁô/D3ÛWÀáÕ ¸|î~Öî‡ÆÑv8Û†·BáÆms8dèÉé÷Ù.“L]ù6¹î!3œo¥/d±w-.–íåò®ùÍæì‘%ðàâ|øF•ž©\ wÿfÀžŽ™HöhOèìêCY5MkÅ/$È™ä~fjÏYŽÇª@&d÷|¾W Ó¶Ó=¯gчÖc ì'&»Ð u¨³iÀ+>þ(ÆO<+?NÔ\»«•ç畊´æãÝsÁЏ„L&+í‡qÌ(†=¼‰6¯M…•üz´÷ ü¶9BG¦æâlëd߃µ¬^ÿYöUÓgÜ}X€ð‹Èç³[ˆ¨¯8iûù®—\„Jœ‰ï¿D )ÏYݪCôcÚs,¶P¥Sõ.Á#µÈ|k¤†!§9N߸ õŽ0ꇉŠÉ6R-°Bv0EúOXvÊ–“K¥þoÎ/ó94߯ b×DÃvMI<Û!ßÎÓËëqÑfový}SºÿkH)âËŒ«øXÛÂWf@ðòR¨ùl‚®¶Æñoó ‚¡‘{‘g› fN{[žâÙ¹™?K¤™mEÐ~Ò¼%`·7øTßÂÀÃçðEÎø$B{GôɃ5w¡»—n¹F¯cá7G–£~ÝÉ7§e‡Í4náq>VaD’r>8·qº´©®ýX1U9ÌÌì¼ÃüúÐ…£€ùS{µ>G§Û~iÿw.TW2GÔÔÑîÊNl _Cd-m™5‡N3 »N‚IÞ6”¹äD^\ß?¤pìV8·ÕÁ ꤘ¬ËZtˆ"y#¼ÂQ˜$í;sjÉ'c|o°’ʵ3•Q÷'ôÿ°°¢-™%2»¡iÞæŸèüå'›ø¯pVã7ðŸÄ6Í‚ÔùZÈveÁˆ69¾izº ²_›¥ ÿÞ 0Pù™*[€¾!U¯8’—©üÔcû?VCO|ý‚âšÒð) ‚]eªB®$²võv†ûÁyo)Ò°UÈ®³`°dgÞs¢B3¯@˜ó#к\ÉFÝÁÐøóû1¢õnÎÙåsæšÝb|CïUÊm0‚Ç×pû…«q™óm,s=ÃJÞé…m0«[‚nçÁ›¦Pû!~ºuÙRŽ Àfàû1ÁU¯J3Âç²`æÂ«8ƒ©bxG qíUÒkv•QÕü ç5p~›æ~ ± é8ÌÚfÍ%ÒÁNw8QþFX½=˜U–M†¤"vÍŸy>h<žy¬ áAQpðeª\Ngg4†2óŽ#å¨ÃLRÎYšü×yvÁ>ÛDìþ$L³¤¦cÏË1†{ö;$…Ç"/}‡Ú¼ x™¼d+8’-ÏãA§^Šî=„]ní va"xk Ì×"_îw3ô%F¶3°ûáhù²TÖw3ëD±«ï3™fUè³#ÇúüE!µ`æþß\>‰œ­^zϧR½hv n(L&—³°™öc(yËèι‡ïŸ".?/ÁŒÒ|fä‹: V¥?õsÁä‚ÝË“LÒŠq°9©÷7Èy» †uiŠã}”¶zýF1ì™ ^y’N–Ìð¬? ì&~°/Nd³½úð]Œ:ycBy;}˜eÖè½§ !­õf+i@Ýð1ÔÍ‹À3¦B¨ýê6~3v‚ÍIop½vÙïwK¦þA99!®H[k·Ã•ù¸Õ¡x‚èMSùpWñm‡ée•ŒÞµ<¼ç¶êç Æá ?^ØXw<œb@uS#%‘ ï´‘óºÅ;Ñ~œÿŸ©ØÂ‰vìŨÊ"p«þÉê‰ó1ÈÛ‰É[öá1U¶¢÷9.Ùx†ý ™wÙ?™ÚneL½ø“§ýkiŒÑÅe“žc‰r"~^]•Ÿ¥PÐYˆvÍ}®òç0oÍfÖ~ž,½•y &-Ã"o 8W¬L¿ŒDPµdü05†»}ëô,ûËy6ƒ¤™™b›™>ò=>‡oÖÝeù6oÄõÖ \µéð³ÕïÀÚŸ,ëÀz9'òïÔn \€ ºõáÝ9!ÿ¶Ÿ:ØSxDâ¢ê²ï¶Ã)ŠêÇ °AÎŒœr·¢O™4“÷£ŒÞq\knFpBæq퓞 `1Õ¤~¢Lþ´¸åí‡GË<±Ñ•—jˆœfM/-g!Gº×û%žÐÜÕ÷,IP¬Q_RŠÊ}þøÆ¼^}…²kÙ£¢‡öòLRRn‹G§má¹MœÛO€ÌT+T_¾*+DþýO°Õ#?s¥ñ†IŠyi ŸO x Ï O`GG<• Ö»êÀºZ‰^µ®KStž~bBV@Û¾K¸ñáÛ‰þÇ.o2ƒ¹0¶ñL*£Iæ±dŒs29àue.}Y©L5’.ã°A*=}ñ²)ql9×7ÛÛB˜S-ýy5åÔŽÃd½,¨°YŽ–÷á¿kÜ6F2 btñ:T?‚ÿfMe쥌ȹJ!*ùO?žp¡êÓãP|ù%&Nf Ô6às%gfŽQ;šš?Æ>ËX«èJUüc1úë ß8‚h±¯ÜðiÇO¦ó‘m˜Me­{aŸ•U8£O‹ZÄÉš*m®Ir"ž{ý¿&²7ø´€Ýׇ*Ï$ˆû3IÚÿ¿óOæu‹Âvy;6ï¸ýÕý”»u›ÕXÂV_ݦró9o“숯ƒk7'‘ÔY#¬ñ€8™6}ž1ÿ¸gªï`áÜ \8ÐWâfPëÏ!_æJÃbWÖv’8¹ø ,‹ã\—kìëi2$a¹ ‘*{ÂŽ]IfEÃÍiÚ›znþë6fFAìÒÈÅÛ b@¬åo€I¿OÁ[žãß“ÉÂ×Pe9¯óy@ʹ{P0rÎá±äm|½÷ˆ©¹1›¿<˜ÅìyÇ*?+³ÇyªâvOƒ_?Ý< !)†pF$œÏÄòOG'êŸ>ãƒ`ü^5†T÷£v«5=>ÆÔ¨Ý]XÖ[ šá“8§T9ì»)½ðj0‰m Ï=UºÛ9 &“Iß”NH°]¶›ù M”Âê'ïÊ'‚Blkk"ZÙ¯™Àÿ¨òXlŸÑräª=¾Ò´Áõ3xºWƒ Ù†ÀÝ]½ÌlévV>?“Û2ã7»¸Y öíà¼P;+Ý¥AêŠÙ'Ï0/¼léƒ0g¶©ùl=…èÍå¥r9<º¢ºü†Qð³7$Ïk‡6·Ål‡—;Öúˆ’i[óè‘kðæõ4úΪóÝ@øû2b3dˆ‹§k’öëŸpüZ˜þ]‡›>Á0ç\{a5KM?¬96_oFáÌoO¸cš°jÒ,ÿU§ˆÐ&"Ž÷žæÐ ëΰúΙñ<•µxƒ_ ÀìEÄWß’*èñMØÒcæ[N5캈Þ#òᇶ4îǽËîAËî”méÇó{¿!7SŠNiG“O˜Y7>3·6lƒŽ?¿¸]+Y¼÷d+=»Ä>_l‹·yðØém°ggºƒ¬4Ͱò„%k§ƒÃÙìB?AfI·!Ý]£[U"É›ïîÔLÉŽÊm÷¡Ý·L8ûßO".ÝoX3»,fK8›÷¢¿lZ~„óà\‰1£{e)[îèÛήÚx• 3· Ƙs%Ìò‘oì QCé+×m€)Ïðb£ÍQ7„ð¦Ëøê|õÿYùJ‚85§3·<†ˆBc*¥š‚WIS¿KzPõHÿ(o"÷vÀÝ5e4ª”CUŒãÓÕë0 ø/úluÁ‹k¡8d '“ÊsIoñÅÒãý«áÁ" Ö¹à)ìØ%Jæ%j4Ž|°ù7žó:‘ØØPºêÉc²=¦‚4Ç_ƒV€LñcÆïg×:œÃÝöLÂJ)*ìá‚g–È BPG ±·D•xxó7 ºÿÝ|0ú†Pøg#ýdÒŽ¿ŠÌàÍ ¹‡# Óµ ¥T¥IoÉ |»‹bÁý8Hù¢EZÃ”à„«Õ„þMçÃEww|¸ÛK>1]Ç4‘?Ó…°Ò¥ßބሗmÁIšüºÏTµ#û©†x/“`u”¶ë*Ñ7ÝÉx<ÇÜpWÇái¿8ÝÚ w|yúÀ«sj˜ˆüKü‰gFûzG ã!bE\ìÉù» ðõIú"à&rß…CõJô…1Íê0 7ËîÌ€¶7 ye#«œÞé9» ¿,ž°.KØ·Ák—iÔ[Ä>Œ±%BWSȲ_~Äæ²½ÿ¡-WÐû˜²ÛéÛW;±»¢€c{|2ÞOÖ#·6kÑ!ÿÛÿhîGÏY§¸»+5énðÂ/šÂP}9O™wOE’¤>wÒ)ýIH°ÂËLè½áŸ°[6«^~FÿLù“*´Ýî„4IÁºÿ°IÈ› Þ¤V9Áp½³†nŠ|„yÛ©˜ 9tLŸˆó†ƒÄirP£øÄ#ÓÑZÿ]/žVÊtºéù0Q°¦UÏÁv¾uP ø~ÃâMhRÝ®ÐLLvF“±,bb‘è÷â”/Q…uSK»‹Eéà ?ðTé~\€e+>±y•ÖÔ½¥Í[\‰•€ÑÄÿ_ž0¡XýásÏ©…¹)©wO8s£'a‡‹+m±a•«uÁ=÷.¤7gß,@·™øS‚ÅÈOÔ6h^3¤ ÛÈpÃdºÇp?ÖEé3š—çÐ]ôÔò“X7EŠÄŸþ ÛÝ4Èû{“ˆJèl}ç 6e–p×Oÿ%¹À=¯}àåê‡ 5¯AEá:\½å Ö{¬„ôS̽o¹Âv‡pn.±m…ܤÌ÷øF"–”ŸF­×ͨ¡­B¾^›„áÑòÎp*ÁÖ·aäÃ\R¡:™^S-„/÷ôéÓIg±|ÇdÐXÚQS;&ü¿w] “ºï4Wçkx‹PÁmî®;¦4eÎbŽMiWó]?²ßå î„²ø˜·Rvzc͸†À_K9*ô´¤ïŸ„ƒs‰ªf$V›ÀùälM «ùnž™•§^¡c2ë麋‰8 %Ñw>µ&4¯„zÑ ʧZ ÖP¶&‚ŒìTNuöfÆ%š`¨N>´…­(°Ü^’m˜¹Cˆö]ögo6EâÅP^º|w.³ Âá´¼Î/]Éž )GÞúl­Æûi¹ý»AŽgslÕß7(ÈÝFn­ã¥•¢—XWí[ò?“À‚m‚ðé¯<­™Ëy[5ˆºô¦ŽÅÕfÐ)=€×2êé½àkÄà[¢4íÐs\ pRè'X=-” ùõ7Î2»œÜɲ«´NåÖfÆ.RaʳԈ„ÙlÖtÕ” þ£(Å22ÁˆŠû`V¼œ¿•§Âÿ‚\p$äfÞe”ôâh—¼Á%Ix‘O‰Îö?ŠGæØÓ»eO ϸ2;®AªàIÍ]D=Å$ˆQõ¯å\x¥µÎUýÄ‚V!ðüpï¶€ðô£¸óu;,t܉ó/‰²z×¢)ñi/ÂAŠzF‡¨Cj1¬Å °X1†¬ôŠƒ‡ž¯?y ü™ ]3e‰[ÉE&ùz2ü,¨ålMZå/BÜÎÒC­—‘¬JçÖ¡Tb¶$§bÛÖ,ûJìž¡OŸ¥É5ËÐ]ÂpÿÅã§Ã‘äËpÈl5¢?K\àô|2©ùns.ÂÖá‹OV’µ;È¢í/`“y3Gýe§ÛJ€y¨¶†<ž<xe¦ß];ØoÆjUäƒÿŸuô¿ëÒËË õ²Ú¶Þ™õF ’ ýöñ@“ÏrúÛGŸŽç#©5x§ñ)\þG°Js:Ýd>‹¹µ|Ùþw1øÌð­_¿qÿ1ð;ù~œ¢{v§C¾Ê>\ÿü;$ð?»‚ÊÁëxÎmýw3kÏ>ÄÊÄÛ_¾;ˆÎ_Öå2Qr2}'jýû_ÿW‹8y°iÑÀ¤ˆ»ðð5S!vKcîÒ¯ ÕÆõ¡Cí·DÑZ(æá‡Ç³áàìL”•0 ‡¶¯ƒ°ïqÙ^-qòAã…ø@ñ m[UF/ÿ< e¢btôfäÆÒMgÏinAì=“\Ü­¢=óÑVw}rþÄnæÑÎéDï’yã% PxØ–¹ ø Dw B]6òªqÙ CR5"4m=Έºˆ%²d³÷[ܰĒ6[—3o¾­AžØäî£&ºq¡×3·?‹Ón~ÚYþ~‰D=Û\AF`;râ«&öÿìb QpÎ09½\ó‘Aqeªfᾉ! oÇOd©õ¶íXø2"•‹ñg& ZÏC4ôzñ°É;pŠÐ½­ò“qÑöú®Dí yœÇ3äñVÉ6{C¬N¼wñM~U½…ù§¡a•»|H®«Ï†þ’6¶us4L9× ÑO34×~‰ãKA\¹k{9ÑÙÌž3³ÎІ¶ýG‘4•Cü=:,5³eM‚ :ŽÎ„›G`Àæ'$>´'‡ªPîÓfÛ„ /aɦq>ñª¿`ìŽüïýï Ÿ'l~s.¨Õw;üµP"»Äo$黓©çþ±Ûz*áò—?øøˆ¹å\¯Ê¸®ßeïš¶°õªpÊ…I¸Õ§èsOèŒÁ l)r/÷ûPò+ æ7ã ×"ž4"çDè•á!tø¬ÂzÝíEÁe\.oüB¼‹ýEÕ̯qšä½ÄžÞ„sÖG`éx¾o²÷!#bÐ )7~¢ÑòµTãåæ•Zæ•Zᵞ<”I+‡þúx .÷\!ÏÝNÁ©'ŽõÙßXq-:·Ì ½ºmh-ŒËŠñ[«é²ì`s§þ¯ÿûOj…Þ%ÏÑøD?>eã2:©¨üÒ2Y/.ˆjüe…•ê9 žbØgØÅÜÉLcª´3žB& HèKYJ^¢;e‘\¡ml1y·ê¾ý²X®)®›$î.Fï™È[Ü˨®ky©X½UIHÒД¹ÔÊg'DnKd6Xu€øþ24!:ä{f,ÞÜÕ/…E0©ÏŸ²¡UïgÀbï=„3øÅŸÅ®ÿV¦fì-݃ªr#x!á&û‘kO·²ß`ÞÇ•°:G—LùX%·ÎAä=Y¶Òf>ìhÒœ‰øQèv; ©›p toÅYösé¯ÞZ’¨`L­_ v!<~”*· Ñ.ƒ¨épl¼eŸë€‘0ºj‘‹»€=¾QÔLë)Œ &áÐßW´èÄb®!MEo[ÐÙ!§èîíŸ!´ê8âÜQ¸±6ÈÁÖ'4êý:Âìu Ï5>€˜ t8Ò«?…ÈøºÐÑ´Êþ\¤æ«CÈ×C¤qËS¨­¿BÛ‚¨W>¬÷ñ£grèÑ,:ôë(³ç›,#¤a ?|òqó…~ŽÊa9’¶‘—g "ÿ«^óÜHÏî;:ÿöœ†¬©«@óò]æŒ} Sveïx“fC˜‰[w«à2ÌÙIŸ½¢ÉAgØ n$•'îÎ]ÌŽ”9`Ϻß(i!K™Çw!{š-þ<™Ú­¥z©  Xyi‘O¼Ó¤÷¬Ðií1Æ¥8–]¼B?mÙȶ_ DRô·%ÓwÛÈžíÙÈIóGó¾UÔ1± fõIQ×=€ÿNMŸïqÈy6‹®ôz@++\èÊ'éø©1Ša¦ ÓFÁ‹mmCfaWI~_.ƒ-[ÊI¥æ+’UïNdÓç¼»¨Ôºk œ:!¿¤k$¦5Îcï¾Bå‰Ãxçm {wÕ!¬å y|wq+'7ŸåÜ1(¤•¶¯ Ì,V¯Ï‡ë)à Ÿš2àõÒ÷l‚S/h5'Aîã9ô›w8¤©&Ì÷Œ)jc+’O`YýE¼;Í*‚äþÅ º¶ð#׽ŲŒ°S!ˆoLA£¡$©}*ûl4‰>g"éä=³i5ûvsìµ#ì¦ì»¡/`ÖŽŒO©I٣ĦVECü.˜S£€ºÒ¢¸óÓ*¢»Í˜}Œw5¡›Ãºa¾5~¹Dä2à«j$»MÈß;OR–Ep»„^â·  *°†Îõå£5Ó[0{ýtðå=ôñJƒmiØ bê(NÖ… Àj`*GŒ¸©h6à‡W‹ÐJÆÄ°³‡mNJ‚‚×`^½!öYÊ0P~ˆ5NP¥nRô¨‹Sze:g¹‡^|–…QC?Q”ÿäýÙ;>n û00‚Îºï ¢üëAüx*$™bÖzF¹L²ÙU„””ýâü¸ŠÖñR>˜Ø³“4ŠÅ=3lHÊJ(½u”<·BŽÁ½ð½®|Í7ÉÓë•);ËL¯ÁËÝ’ü·rÎJ¬ýóE~‚oöÞÔuÀ‰º¥¥ÏqÓž Q‹'iµÓ©èh1²·%áïÍvšV ZüÔ¥»ìÐR-5ìø¦@Ûb3âÑëI›3KðÐÏ)4ùÉVúb‰œ>þ ìB8ðõøyᜠÙS(]‡7R1´w%=# I¾¬¢ê‘dæK²>l!>§ñËÇü«(Fz ³+5O_÷ç—„"z¸a1þÈÜ€Ÿ.³¬œ=®P%{¦ž&'úº ua n6%M“'3f="{=Ù¼šË°ù­dxø’m›A¯ûÿÿHËŠg7xŸÇ[`µÉA²+L–•{aCÕ¼ŒqD8 nÕ¦Ç7¿¡ßf“U?:pGk>}Ô÷SòœéÓ)\½žTV:ÍL’>•Œ ¨<)‰îå_CÊùÀŸMCÛ% ˜V” ^ ìc] v ÍH1'Ò•¸)¢>Ÿ9¹¿2Φ“½^dåu-2’r»VÐÇ÷)f·P%ƒv´8ÇÖžÁ+á­¸Os5ИM¬xœIýŠðЯ[¿¸»¢†n›lIoЬcßu$E·òiJLêöd?T°t‡«Ñë‘!ÛçݙЃ@ ÛÇY…ßGÙ?r§P#+ˆ–ljÞ#jiMž=Œ¦ë³pÆ2zXîlç‚Nv<Ô,Ø•ãóê~`{ÓóмpÕ*Ã7ÞŸðmüdê{µ Ýbös¦nû̆ü€'¼j¬nV7ð>•%¿âO£¯,„'Î'‡Ý"ØÇÁêôþ³kpbËS öXD•Zsé³À,Ýøz¥dèä“ëé†+µh>Eš4íéfƒªYõë0šT†µi2Ì»¼JÖðôBÚöâ_òGÍÎ:N|fB#Ñ•2ä~Àÿ©«ÄC k#Lþ¥MøÿÍö<жcnÞÙÊNû.DïDDS·‡µ0 sÂ"½‰;ç2æ&4ÀjokÈÞÑ…¦ÉÁ°™>ÝqXŽÞx+Kü:ñZj9Í´¿¿êyîuç\çu^çº_÷¹ÙY_쯗½0vUˆÄÞý¥¿â¸ÝñÿôÐl?ÝÌH€«&´ Ú/¯göxË8"Bx·yƒú*!²M„~+E*uÚ…íÈ«„/»YïšJli7&+ÒRXÚÀ^}¼W¯*§Í×¼Q€ÛX ögE>«–󘙟~v~FY¶$tLq,Kfˆ%¥€ öcÃõAÜãt=^aKR9{0æ8¶è*Q¡ìw8û|;ì5¦`7EM#щ/¡àg2ø¥H¿¦vòÎÅ‹ŸÐëÈ͉ù_œ€goßyrpm;—³)EógÙÓ‹)|°è`H=ö¿6Ääbtvì$²qÃl~!KZÙz·‡Ø­1uÿ2±à_#sÈ 6ÿ „’†l49–Ì6 -†¾We¦@ín•r…sD‰x®4嬥ÿyóÙé‡* oS*Þk96ÎySøeÉÉ™Êt³Ž(Y ³ðáwgbÅ„q ÔcƯ4\˜wïæfpE·Ï æ#Çð«™*žk„ÀrôAÞA5¼5 Û,ï±½£/‰ KR£Oüä´ƒ6YÜžKw¯¹INMîæ†+Y<F³ž³.+#°ùåTznv--- ¦&j D}D–hŸ‚µç­ày÷O؉3[ÑÖdF½0§kwýƒ…ýÇéú¯²4¼¡‚š¼øÃݸLãíèé%`|ß?’½wø&. Ëj!кžtÝ{‰FŸâ'ð¿)P ÛŸ2­ºæàóõ)ìUŒ1IÁ[ü=LÁ¼)x¶l:}“µ¾b ü^¦ÇΠé£RàÐÄ>YC*þ=ƒUæaÞw8"u 8AÉ .Vy©¢o3q’¿ÌQ6/Æ»RÛ‰ÊÒÙÌCâo mÚ)lÜéï0¼è9ÝqfªÎÎÁ›¿0²äé½§SÙÅ£Pìà qÛc zÔœæÏ¤a¢/P>ê"ŒEîÅ=÷<¹VÃOp,"Ü]Î@Ø7.ÌŸYÂÚ%ìb*¼-Éøõ™³Yz˜êº•’^ê6œÏ‘PMGЛh±ê=s® vÂ~NáIì7YuÛõéÔî_8Yì& ŸùÂΕ2&I•±tÛ<ÊÚf=ÁôAó¸‹]ôAÛ´í¨Igðzh`å+/øq”yˆ ïÖn¯ YèçÎ c/Ïwüb±öó{ãR…ŹMfN¤ëP±,oWÿÈä\ DÏ7Eà×&Iš¢nµLuù§­cR{„èäqüDf Ù«6TŸ+’§Ù ™•Ð|Y›Y~b¬òçÃO+`‘£Nö²Æç2‘¶'7ÞîÁAX>x˜û²!^DJtÝÉ’e-ÍXj•=ÿ‹Âv0[´ç±¢|aSÉ"0@¥‹×!|exzí„À Ê´ñ‰"Ü+8€®OÆðçŽ t•Ûq\ô½¥-’OžÆÔ2~’ÙÁÔ•ÙJòßà ÅxXŸ§Ê.9QŒÓ.¿…Ò¯“@"t;–þ¾LÂøišà8¿qGýŽ`íÙÇ8Ãú$¼¼Õ wÅ¡ŒÀ9œÍ»ýãÃ\UHÓå96 µÐ§¶(Ь5²Ìl“–ï™Ú0¯„­ðŸV:3?;½ª”\M`hý³!¶ñI.ô†B¯„ºÙ™’ÿtÙÙôƉ%D\ôý¿Ç”ÀW«ËÜÖºG¬&7$ΙaJÆ[N  ê»n£ÎÂÓdRQÿ&“÷VøPs»¦õ6Há©™lÁ¹Õøzž#ê¯#§°wÉCß8Ëg1iÚ’&“Z€jXa7'W”©ã­Œ\I7ÜÆ–9œeìo\5âNä9cX`› “}³`oö)–ÿË58J¯°'¢žÒX0 þóhl`ÿ¦^Ç;?GPçäk¦æ_sFMŸMKÏà¶j\DÞ©Óiö.ôæÍeiäZº°©b¼šP%ÿfôœç潜È3‰ _ÀüRæÏW ª(û•½;JaŠ®3Y§lНû¿aø¹J˜t¿]– ¢úÇDôùösÍafú[â ^²êÔa !χ\ÉÍß9Ûp¦óæøJ+nC  ß‘CmŽèà)µ0¹ [¸×à†»*ùz)r'ßr®t΃ÌÌòpÞ ÜÆt®YÑO¹ï ¥¿ ÊeÕÉ—1 â¡ÝûnÕ3“n¾Ä=©  ‘R‡‚£*¤©*¬ÿƳ VâôäÆ[ìÙÖlõ{h³ƒB¡ûøðоZJ•ñx*Ç .u2F… ü§QC ÊYIn댩‘s¨•­:üÙC„æÚA½¼+µ&ÆÌtŽ˜ý'x\%Kgrv1:'Íi-ËO´ÎI£ÎÝYäö¥ZœÃU§çLÉìoš„(ËÌL.e4 ö°“¤Ñ6ú*Ôs—ÂÕ+B4½È#ÖhAJh6fÙÑ÷>BD9S˜|ª¼ ܇ƒ¬ÃÅzàÅ;øª$÷džmÍ70~á=Fcøþ{ÙÜÉN9Pv Õ„pGß\úiÓ d?…R­ÚÿtÛ¸qc<ÞQŽKî³Ü•Ú´òô„H®!ËU¡F³(Oë`ƒŽþïùû¹lÿÒÆ©ÌÛªCDWFã)—…ôdœ"9»Õ šeáp~!H^ãeC6ÀÐ¥PP zÀõÛ%{ޤÃÙ¦Îá)Jè9‹=ç%á3›Œen´üv={Îê|9½…3í·¦Ú Áì™etÃab©è ‹~Û’á„L–Íl‡½Fµ ¦”‚·ßFÂNÍIôÇöãðḠª6Cß@ÆH×`Ê£!𮜎“Ï2C‡4`üX´ͪ¬ŽÇÿôÍ™³ŠàΜƒÕ±‡]´Oí@¾öø«Oõ3ùbû—s™Ëå“PnÒ- [ò?ýKÏýHÛu¡¥ÿ‚ þNE©øÝìÅ3ýÌ7¥HäN•£ƒ“ΠSò;ŽpØlš­ñ›œ’ÁùŠæÙMZðÛh.6jœƒ+-Ñìõšô“†Ùýhw³u#Ü´52|ámyØXÒϪí„ÒO8+Ð͈¸OÑÌ*œºŽ¬Ô€Ø |®¿÷Íêžß_Ag'—±s·ÅèmVôÈéoj;ƒ}“;yŸ¯EfùtföqtÒ·o‘7¨üA ïýõÇ3ý¼Äþ™,-jŒ=––z' ר$³¢®ûàáøe†&¼Ý??|8˜7¨ŽËËöÖ;BD´O ÇÆDlGIÚÓ€q_¾fú\v1ÛÙiÔÎê¦í•19ºô`?cøU“Ì83«8âôn…òùNôéæD?L {&ëïMëéLË X*t ÉÞÏÌ̪”û5á.xýßt¦}}S~n+Ü4†÷.˜R{žÙ¶vÐöÚé#øÝD rW=Ãï¯@Ñ]¬åÞæŒ&¯üÃtÕ²Š:!¬Š4 ʸG3¸ˆmBìÆÔ0ú.»ÎÞõ…âTarÖøº^À}]7AJ5†ó~±iïVf9»Ä'æµFÞØ….×áĹ‚–SC7Øë lÀ ‡Xäð@ŒÞ.rae‘ädÑÞ}ðóÔ"ó^Œø¼üÈ,KÊÄï»j±àÙaÒàƒ›®BÁ jÈ?Ÿh…¦O<"ÙdnÛ:"ù>˜\ì+ÅM.ÙzÉ3È“ý†“#v ‹˜4¦Õs]ÝsÚç¢ü*;<¹$tj`¬±?¿ý;"G8¯¤Ïã£h)Z½üµrÞvøÎ “ÐGàΣ¤ñœåE?´ Àóª+Īª 5ªç㜽èSù§Îàxp èä¸lÔcÚ ÚÁj‚ÿLi,äB÷vv‹ÕŒ¹½×ïã´s÷ùi °yÉœ=ÿ=—­ Ïd nãò^÷€Üå,G|ö(¼/¯„¸eÒ—=,Æžõ¬bpæw‚ûlÜTð‰ {ƒ©(?¸ô”÷¡ìA:25Ì4êä9|/l¯f3ûÅ)“I† qiKÆÀ^ºI}ÙŸ¬N¸"¿ñý–_¬È{mZÞ¦ƒ–?}qü8›7¾CÉtYÂ[!ƒå­‹±0S†ý¡x oÎ5gâç«{ë™8üÅšN‘əըx,œYnUCÎ NðŸmy2 8ëã_ã•ó `F%;–«G]›DKÎOfn[*Ñ}ËêAj”e,þ<ÅÃaàrFEüNà²ã\Øi.NɩǠË3Žß$öÃä-j¤­¿˜'iÚ~‚ :•À®rJ„ú‡kØ]1j°Qõ’Xg¸1™_„¡’´3Sœ.¿j ƒý¸U)ç°}s÷s¶ Qâ·„žk¸ïû`£yJó‰Ð²+"c‘|2Ì,WÝ©Ñò©È=QÇ.ëöÀ8¬NLÜúLBÛBØ=¸œ}“SµV ƪ¯33ÛSþ·þÉIÆF±xî]ªáÜÍT¼mÄ NÛ™b‘XûC_ûæ0lxË÷£˜37´IhàšEwÑG8/DüÁµÇ„hQW77›ïLqw…¤‘è·D˜ÈV§áC¾6zÍL¬»oDI™Lt¶pH0°óIÓÈW}0ƒ} ‚ž2«K¯sþÓ-$ÄS!šfÙŒ¶ŽT©Ø …‡Ûà†ñî9‰Øv»¬Ý¸•‡„ˆJ¬-c?³Z÷´2–î‘`c?¯íxL¶í›ÜÆ,™ +ô¿Â‡®}P·GžH,ãnüÁƒÖîNø«Ô|>Î!âþDý¼Î¾—þLø¹µ0çw?Ú™ »‰?gÍuRxñ9.=-A üuØ xIÖPÍ–öÞK&B´I¯ÇfºÙRŒxïôe–Ö¹Ãï{ÚS‰ ª®ÄvU` ËŠ[ÁU2å¥l)ngO®’#YsR`Í¿NÎ!‹7L]–:çÓÖ,¼p§¦Ÿ\ÉJùEÐ?-©Ëº`Æ2Úˆ> ¹Ï·È«‹ÑWeFT^h>dkêlùœ¬X„b²’¤ú¼19¦õOYm¤O%Ôà‘z:;m¿6wÛ½ 6óº-Q a[ÅpíçÿéŸì+½±q¤ñ¬fö¬éD©©QXíb ^ÐrðNº!ƒIphX“™§ñ œ_ióX^Ú«†×ér.I½³‰ÂWL]–»3ààüf˜¯ûâOpÖͲà\6ò§BM.ʬ¢ÙxÀ» kªà½•ÃLÝ‹¹3QcÜ~œW޲=2Ìä|;ÎÉ´G»Ð2G‘ð{$¢x½=Ñ/hŃ6øµO€¥jâ+;‚6bT9z-hmÿÙ2¸%üù`ÇõG…Øõvþþ͹’:±8ÝUôLÿ‡ÉÍ tks·L•HŸ¯C#úì´:ùüZ‡ ºPÿ‡qðÞNæãq³0ÆÄØïª“ÁÂȦ5]†!ùäyúQú(ª 8z™9—°?nóÒ,qöC¶Ù¨%esížòs2ñ½Ö<Ì%Éx¡ÅŠT>Ð$ê“Ê@_ø”WÍ„€§i°ÿÂ6ìÔÐÚŽyß2ñ¡a IŒs…3#ˆx4œÓ{pÚ±lˆt©bÂKá¶ýÍÈ·Rnç.m³|HÏ$8 Ù†ÿ"téˆ@pl-Fÿ4S<ù0|"þmv6´q¾>ý›(þ©Ò§´é,›¶õÕ̬Göm.x×`ásOpÛL³>-·ÉíéáuØILOéc*#ÄÀ߇‡ \' ³èãßµP<}>uü Î†8ºcCðjÚbK>9¿êÿbó•TÚlYd*(uV%âC+0mO#ð‰þ…Ÿ’ÏqN—1|²ÌbøÇç‰Ïa:séKL¸ŽðcÎ'ÆÙÏëK+‘ÿés¼çÍËeû ômŒaäwáö#ÜÊò³Ì©9\M’%Ñ8áÿ™|ŒD6‡ %vUç™À·˜èžDý·㯩P½ò4£(˜‹j×®èå0é÷æÓÏWœ lÖŒT6â×UØœÅ5yðѯN?ßÿéŸÏñä3SÈzç1´pc û~³ûûÈ\b 4Ša­U¨ãüÛÅû„¤é9_’4-üxXu‰`”;aL;D‡!èš) z0ž¯7sÖ»V¢[‹>ƒÈÑWgw¤úAù£LˆiFoÏüòL$³jÀt•'éø-ƒóßÎa/&ÑÁ‰+Lpñ|ý~Bÿ_•ÔˤÃ#κRuÒêq '¸cíØ“ÆGîr1¹¨Š¤»ÄƒŸXùµ²V …ݶÓè-,B>³Áðª;œöå}gx¦eÑ}­ËȃñÜ¿[ÕÄ©÷M÷Ýâ£Ñºsq÷Ã4ÿÍ ìŽ3"ÓM$HÿI~DWüã d^¤+öiSØ—‹†7›ðï`ûÛgÉ3P¦ÅaÖ̆Àr´ʇ¾É¹Q ¸lO$¹éH•?âøy n†(õ{á‚ÃM÷€m»aÉ•Lé¨ y­{·î~†¢ ‰ßê·ØÞ[Š1ÌI©¡‰üWå[ÅøÛ5³•½ö`&þ€;Ƈ*ç\Å›ÍÖ4I Pg1rŠ…ðS¹¥-Õ ]t‘º[@¦×*j;ËŒMZ‹*J²ä¡+²·ÖŽ Ý¿Šî2=Ǽ¿o‡ç¥oâ,PÑ~Ĥ~XDWZþÃ1‡D]ÜC ZÙ¢°moÜÊÒB²ø6#..Îx¸Ð™7²`Ù?kPR”§ó4É€áî º\x¬6›Ë€|àJÈÓF©Û¸äÛxÝâ DžŒ†Fe´®x“}œ.¨½ŽZ°ïKYá.‡ÇP°D‹zG.y*Lóùè‰úçÚ)tìOgræ8!N?DŽu%y1¼t÷d>"øË†œNwgÕŠë Çɦö„‘ðR” æ§^ºÑl×3Òù¯ƒGžÝ%9!»å—ÖDù{ ¤à øtî0åÄ2ëà; H§É Øáüd’Œævì  ³&ŸÁ-_¾bN]`o‡åëï†MͰL"6ô©1g^܆§·²Ї±þ/]”kIÄ{‚ió†/Ø£4ŠÚïÃ$˜ÙÙæC”vÎfòÍòQ`k*üílæÔT'q]y¦‘†ñ ä}åYFl·Ë„ý/¯3¯çbmOmÃ{ÍÚ8<ç<ž¾EÿÓ4ëØ±ð4üm|~˜ƒ:¤wÀFxAR¾!8EXcMŒ“Ðó– ·:†®XÁÆíÑÃ-jËÑøû Zgï³Ù:WJ‹¼ Ǻ:'¼°5‹F„©ºð úÕV•lÜíIÀãme d©@ù\6RþÌÇÒG˜i“D;ž¤*+–²L1}) NÒ¾€äÒ kú=žŽï+¨¹6²{ùQÆúî'æGýï{ÐTç“`j-H“™©zzàHwµÜ}ZJŠB!¿YÄDþÿUn‰g¯x¡Ößë¤yšµ LäéYS!|ÐAŠÑ6ôÁrcò¹ˆÛÙ!Èไ±‰ÎÄ!´ºƈLOø¼O'¯l ýygAùÐÊ¿x9êÃÅÖ{´°`„VÑ­½úÔ­Âc®¥Ù§™½¹´¬Å ¬êvQŸO_ñs¦QN2 îV"`zô,så}–œJ‹j“§øò:–øO¸š=_«KÒ´I¥Óv¸ã;e{"þËjeNn‹aœ”‰“DITå¡e‹¿býykp#ûÑs{t~„Œ™œ‰û§ãʹ"§¹«½ÓQ`çLXZêAs¦Î'ó…ßbã¯εœy˜ýS†1ùzk9Rβ&;h;Iîg÷«‚ûáô-xbm‹·‹Œð}ÅæûÁFM{+ºŽ³Çyàõ1¸Z”;¾ý8H¯pg ò~3ã×µ=ðXn_nÀ[!Vèl¿”>eU‰¹x>ü+˃-Í”™#ŠXÿvûeÿ7Ü#Ì@•‹/íªð ¬Ý˜¿ç†·í†ù"d‹‡ž ma™Ùgl-;›anç-<®õ†ƒ£¤™×“ì“À©tÒš&XÝ8ƒðY7Ò—9r5ÇÑ8q <'ªtèÊoPºG~)àyYسö%ûæJó¹Å€TÖô€ºÍ6áéæGŒ1¹Z¸ª'’G Óe;ªx>Õ§W€¨æ~a`v’ð­föN¾A*õðËOü¯jÐeVÁÜ™3îÂì,_f¶“-4ضpŒøHß%>ZÆþ».Hr-t¨Oï-TÐ_IÒT[q³W$Q\)B¿_s޹sûŠ®%Ag:8êÊôDæìJ¶»ï\VÞ|=ðÛÙ°kG˜Î¶¯Œ}€9±¹5ÎáLæAîW êðaîDþ{ žç¯>eâÄÒé)·ñˆµ0žøgB7?ÀGµiÎ@ ¹QœCåçu2¢%Û0øÏð=­O2aŸU˜Ÿ ‰¹‚dó»oðôŸ:¾· '¯r'õOé3e@5 ~:Iôrè÷,š´å{a¿3üü1Ì~Ý^Dúthþ…BØrÊDküðO­ ÎþL˜ÒCÐ(JÕB+éYÑR”P^ _òDñ¨`-~d¹x{Q {û ʳ§ç|ÆÐï2¤¼$ ½jºXý÷Oqý5doÄΧΠB‰ß;¢± ‘<#|R奢Êg±M]ÌúcY!-mü3X;a‘¢¯ÛßÕÌWð®@ƒèÿc'øßõSù¸äZ1*Ýú?ÛXó:Irçº7ü ‡¾o -º °òîeÔ ÂÖï–‘‡¥Ñ(p© îÑà õÆFŸ¡› :„ë¹løX(†/nÿ„ä™<ð‰ƒ¾‚»¨¡â~=uVŽ¢çõ]${ ËT$ÉÓ¶ÚdLý³œ¹lÚ‹éMEèT Cžq;Qï”67^ÏÍróûâ&0rÀé")¬C”ôÚ—š‡Î ³ ¾Søþ0¾üÈÜ1—"^ùš¤´ãOÓé…‘ P|ÞD•„¢Q¥úxò³•=K'êߎ€žv"ôqèY8ÛÈGÃïÂ烾¤~†½u+/ý§(’MÑ:ôûÕTäô<‚2Kêü „á·ºK¢ªZfËSýØz8XϹì.D=|†¹Ææ„MO޳Æ))5ZÏÈAYz íh¡iã5óƒ€v4Øù³Ï‚»ÑVò§ZÜ»³1œ<Áó³÷á£!wZ2Ç—ê˜U‚F| >e£· Ò‡æƒf5Ž-#;#§ã.×c0«ì+ºT‘¬óŸGÙÛaØ¡u„+-Íbk8êE»û÷1bÇxÈÔ4uq8ûWçNàß+ß½ÛЍü½Íº]ÀƒÄu¹I­J¡%›º¹jköÀêö¿ÀJ$ç,ÀçgxPöš=û$Hœ'eBÚÕfÖÑè(šeä0Ÿ”ãámø>ÜÄJÒ•¿ö“¸Ô(ªs¿«^Ìs«V;ñ™iqˆlþ8ÀzïÃQ5†´$ˆP«î/Lp‚!©V•$º“ˆ‡c(Hkà\¥K˜xˆ$¬¡ûåþŸÞ™ññ&“Oö£¬‡M- !GWű;'û’ º©dX÷{p_{K¢{ Z.ÞÆLU«B‰õoÑüjý—ƒ±‡æùû6ïk_NÌÿh˺+`̒׌þžNø$±Šô×Ýa³¤úaîÍ…p²(“}&%GgìïûIÌ•7½lŸT¸}+c‹“xáÜÌ hÚ-†V8@£þy¦õñi#µÈ„?SÙ¬.PÞyÿ®ÛHœ‹¿Àz&ÃÎ|Ã0•?lSÕxÜà©jv¬¸ö/âÚMu4¾wèý&e+†÷Žð+u´¸ØÛf¸Ñ‰½LK© }_þŽí—5$F‰›iTD/Ü O÷k€¢)#µ_Ž<ëf"2°•§ Q½a#t$0·¿ª²®=„“ÏèñŸðê¿@`V‰ÐW*¯Àþ~$cÔÍ9àlŽýv*dÓFM*ÿ¤zbþ§?Ú‡c"äÙíÒÐ0žS!ðíõ¢~ÿ¡µA?óùßòµ7й0¿}æ˜`f–/å1{z㪿OÀ4x|˜yŠJ¿9HÝ¢‹‘=NW v¡Ú¢kpHH—VÖÄ€ÂÂ#à-½žŠÅ?"+(²b}ûéñä<”˜! ïyÎè¼p%kÓÔñpÓˆçs}1´™å ë0–,¼U x±èûÁétúÜTâ{ɘ›ëZ„#+‚ÈË$ô÷à%÷ýr˜Û±…dáÕNÃl?bõm+£0‰þú¶„sÿ¸9/BÜeáÖE–íS¯… ‹·˜%{Ó1Vg3ª‚ž%Ø]±ž[x\är9ïÇâ‡3áôúõ}paì-Ž\åß“q¤ðÁü¹|]ö- Õ«$'ìGµvVyMóS÷8ŽÚéãÖœ=¸ýÓlw«|\q݆–ºicîAªžòþ²‚l§ÖBÚøÙƒXMoÂm<ñôãÒÃØ˜2Sµ’ÑíBF{ñCL¶ø+­©%DúWEæÂÁœ&8™.ÿ4Œ§ÿ.ɇ.²ß=1ZRžlLYBn¤\ïTÇù+a]s¤7²ŽÃ™ÌiOºìL/óˆ³þÆ-¯3³HæÌ6nSýAY"ÄpåàJ\‹_úï3ÙK™€«ñ–sÕÖW§ïd1£HŸ *ÂíòûÀÀ2aÿ¿.Jgÿ»¢»‚½ôškc4Èú£GÉòûXK;S*¥Ë*‚=¯$ üj}=Sp1œJõ[ÁÄÃŽ¨K3zÁÞh»u˜Ë§´ƒ—ùP·Õ-¨Á·‚쾪DÿLÛB'õ=‚×÷$¨Ú=-bý$ž½3ƒyµ’´¡7ÈaÃÕ$<+mAµï±[“HÉGã9À^3ª¦9¹W©ûrxÁÚk—ZÐmûc‰kpѕɢ[ ´È³£­ìõ­‡1 ¶ ­ëŒ‰d r-.¢er€4n›ÊÜs^Cf.q#¯:¢1äðìœñ¿û_ÊÓqhôë<ÈZLÙIæ¯öa?·â:H$ï ç=3¸N» I—I” Tc°õMXÖîJ¦œ‘¯ßzðÝW¢ô“œüã1»å&Ôeaâ¨?;þ;h›N® ³æ¦Ò1UrfF8eo£‹J+‰À³dxÝfú/ˆo4"¡ã¸ªýwY¥ÓÏðWÃï%ž¸Z½Ï §’×)g DŽðr†É?ß*²éª< –’ÀåÏ–¢öÐUÒʸý§•†Æuý˜p¥•ö$ÒÏ>¼4ðQfÈ «Œ&…íÛ‘&ȸø}ÍÍg#×2\Áíj3öê”1±¥—ð4ok›oK×8Á²<w—Åef´®ÑŒiÒ"Þ šTN})Ø£ûâ?á{ëaèÝygYÓ½uï6ý6ÎñÄ0õ|¸yë œ «e/Á©ë?W½×Á2k^rAp% ½‡J‡‡1¼â1b‚§.:Ó7–çàáI Ú=es?j\ÞXÖUhÐ7‹¿À E;ðÐ;ÇQø+ÁèK¼f>HGE‹ŒíŸnÄ¢Xü„ð_¿g8Ë óë- {æ¹a†÷CY§7fû¼õ1%Zr7“hì!þC£Pµá{u¶5Z¯Dßœe›Ð(D޶ _ÁÂ)i˜8_”Þø;ÁÑ$ò£ágCpðä7àÌdRvÌÆ©ó™GÇnÂPÔEúÕ÷kÙ¿‚×rx–¿–ô-ýI·ÈYâ±j«‰üw,û{+ÿ=;}&‘/v °pÕ<¢/àgÝIp(ò9<ŠÎ¨mÀ™CªäüõY,çê-vD?šB|·0ów¼€”yjðÚ"µ3M¡Áä;:}¡ouœÈäx9ׄáL9ƒŽeÂäKÎ$ÆõL&#!°—j…Ÿ€¨=ï`<¼ Òâ,žÜð‹s(Ò‡6ÍæÒ]ÂÄcÙ$ˆ¥òøæ`›»d yIÂ)Ÿh û=†Û§ÈÑØg$—œ©ßBCT¬ÃÕ•ñå% ¢1"‰ ·UHgb¢ÿ²û–'±ÏÑxSÌÄóÿKÖBê¹.¦2x ³h›*ÙÕV RùÉ}®%1Ÿƒiãõñx,õDmÁèâè.°Ý]3à»z9=âBjÔ$ºª‘¿Ö&ä…Ý{™†Ÿœ©žt(õ^ܯ=Pzw!^W%ܘX\ƒ#…â(?ÕŒVH ÿú<ëÇqðš“•óãУ_øè`­&é‹ìd8ÏTé××RTŽï9hüÓ£ããžYƒàt‡ÁhKWª¦ôy¬<¨ƒ—éú­ADr(åûÇtØ.¦9³{§éx‡E‘˜å¸D͇Y9Xøìïÿ¾°…a—ê“õ¿Žƒýú4N“i ê÷€ÿkUr7ñ ð’¥Xí—IƒWdÀ…뇩*Ã-éÜ€únEÔqR;¨ñXFððU)u‚§A‚÷öØVзfC$ãÇ;,Q»ÔTäî|ßÓÅhýå„2°vãyhì™ÍT?žE·}Y tµ=<Û˜a–Ùæà Án߉Ö1jßzŒÄ÷.a{V„";û0lÿA—“˸n’y¹¡•Kä–SNa-þ§“ž¾tù5'ãé…sh±b9ºíÅ]²$¡­ 2p«‡)#ùÚ”ü©+œˆ­€RxvAΫ‘¹¹ty Ih¯Ü¡ú7ì›Ì‹fƒ©¸k]>˜K—CºV$Ysg'gv`\²$îJÁÔîÍpêêu¼µÁšV~]Dí«–Ã^/Pu1Sa!"¯Ÿ{¸á³.‰’démÃg(²S~·~™7eìfÑO˜ìbGSùÒø÷ç(È ®Ažó陃ßPç÷lêóu5(‰  ‰w¶ >É=/>Ø«" IŒ2ù¢$Gôì-hi€1)ØÔ ‡ÿîÂx´$å̸}¬¿39=}-µ°?÷¸óóßOFÆj&âÆ³ÌBÅ9èþÖkÆ>aýù!hžI7ìwd¦<|€3¬eñoÿwVËõ=›­Sü=¯XÐÕÇÖ¢öxm›T¼ŒÞêÚCz©Dÿ!Xã†bóݸΠÑü¬ÔÐâÉuÈÛ” g7Ç``Íi˜:[ sŸÛ»æÔHσø/"DÌn"óc(¾>ÖÞ@ÖfQÒ,¹ŠhU£å*+éJÿí¬ÜØ:’µ Ç?eL×ébñf%xtœZÁÌLšq<Þç‡@ê€<)] O¦Ït˜À?ß+±Àõ5£½U„®TçpsïžfÒ¶6±¦cÓ=*e CâÔ=éâ:ïP V¯"½ N-*rư³[=MœIÿ·lhÈœŠ¦BDr×;xú¦Øöì©>\h/IyÈçY¤QOˆ†ý6»së™Úúcøg¡“ŸüŠ=ŸíHZŸÆM’ÛÈ™àlT®É"×wZ ëxP»îBˇ)B0ïolðEíªõðaš%Qz°Ÿ©¼VŽ–½d,žv’d©WL¸ÒüˆH%#Mèîn-Û:‹¤ v²;%#Á`×(VÌÿŠKn*“V¿Ä‰ø7ÈÀ_KçÁ'}ÒåhŽ;þèÍÁ@4“ÀëkZùÈÐØ€§p|ók®Öw3Òq±ô?ÉÝ78ä•íZ*Sƒ˜`s‚·Î‚ç?$Éñw«9$”¶ôdBJ¹75ZðC7\wù5lC¥Þ¥Bw]÷'…>—x’sÁ«!úÇsXä¶ž†MÙE8cŠšðƒa^¬Æ5ýG`àÐ!ŒïógnNv£¦bT69ú–€Ì*=zX%ÛZJ!g`*öõö¡AO<íûÅGwôzb°LöhŒ±Ê;+pGo—­óçýôX©.Ý{¹+µMð#Å"¬ùulVeSÑ€ƒ(´¢,:v›ÑEæ—Q¹Ö ‡î^dçGËP×ß^ÌÂÐ)XQê ‡"™ï’±®j1Û^èL„¬Oc½¥;J¦bg¶ =©BÔŽYYÁxZÀs­eÓžxø™Þ òÖŠ”ÏY•9ë ëØ¢Q¾-Í{öS&W‚Üôùdö“ xÜc‰>ŒâQ³Hï)W²i?­´Ò‡wœ¬ÐOgÉdº]l.¹nöµâd$¡•ßUÅj\㥌M³'ö%uÐ-óCAåb-÷f+*ÊáKûA¼w‹óÅìï„ÿß[RöcÁfÈj;ò3ÃY»ZeÈ7=ÜÒ¿(g£!Þ-<ÊJ–ôˆÝ)äµPÂi/w§f ¾ð…ÿú6çnc’u3D¢oîOcOgÖ;ѹ †ÍOâ§¾V÷cƒ­¶r ¢jiÏ~ˆ˜ñ˜ì§˜ÑWî}멞¡yPÃVÌYÀ(^IE‡Gs‰Kg¦n¦ÿ•Àƒg*ôÈ Ðv$ת*ᣦ9óWÆ‚Üe.0Ž×ÁË}ØeMd5)3ßES…Ë!ÀY‡òˆ½e~ÞËcó/ Ñëî³gAŸc~éð„ý[ ¤ñO3èN 1ÒH.Õw§· ‡¿ x­5ö©’ ÂgÐ6à84ßb]öÍ&\˜–ù¶4÷X$ý· ?ßåážl›†Sò¢8“neQ É«¨fx¼ž±€9  ‹õËéÀò îøqÀ#sóÂËØ#ÎøH<¯4]ƒ¡©Ìý_@º—¥Rß7Ф´—Ñ>’†¦7æÍÍq°eg[X=^Û̾ÀüùÂGnfÏ'gK ‚J{Z©„¡ïÁj<è·Ò±ýéQ4½ õKxÉ×w{1[ˆË(^Â|®•‚Ë:Qø´'ƒMög‚\Ýé›Y œ'ér€É™ÌBF ÆjñAÔQ¸ªbNRWÛ’U#ÌÄ ¦ná]X’†ÚMÒȨþI»bøOw|ðÚ1.Ï %¢lZÂÞ ‰Å‹øáÖBk0tiâ†Æˆ¤øOÌŠl9åÌ~‚Rìñ²ÇöÂLøyk„Ñüë ;6HÑö]OØÉÇO²ýhËÕf~m¾ÁèZsÿë%-$÷ÚW¸À‡”"vDç$sî·:Æfl€ìM««”ÉŽpj[$Êì¶5t™ÍÉãxã?mŠããbxŽ®„ò¶aƲK¦LØo6ÅW…=b5Ùtûèüâ(ë5i:µk bÂLÏ‚éÔ§láTqrk¿:Ù\eH×`%„•ÆÒÍUjtݬLæ×Ëã¸ë‰7f^U&W÷ä1Ûëf‚Òë6qá 8~_Šì>bD£ªaÒŠÊËñü´9œÏ kpÚ…ÃðE² dFCgP;«io‚7zÿÁgHŠñE¿©bðJi9w¤TëÁú¤'½vº‡ŸdßÝ•·¿ÿ ó‡?|Þpï¥NEµÛÂTps:vþ8‚ ä·Âš^Aòºà‡Mû%*¾·Ÿ¹®ÒŠÅænø‰}÷[&OÜÿ¿”8zã–quBµÁ7Öò¿ð‚dËXÒ)]£;o£©×oP83ÎUˆÛaü¿~ÍN»îr<*AåÈ2¢qI¿üÝE³]TáÛòvæmÆœý†yÛxVÚ›±'{Ó¦VÐ^öšé(T‚íC8`}¾#8-§ªŒè§û`Iù'ì¹²ŠH‹Ž²‰aøz£#&…À·û\·žEeoA ùÕ£Fù¢åÜ>¼Ýg@8‡±‚ÛãÙ 7ÚF°zn&dä‘c «°Îs%·%¨…ÞAÒ~s9Q,ÝÏ4Íè!…‰ûßæaf¸¾ŒaVyªR*½å—1´éó_ŸdËJsrÆä-øËÉÀr«š–7×X»'^ŒJÉhµùWGÓ-}Bo†ÒÌ.8æ?<OàÒ.ä½×d:Ã_ F¾¦qÏ]úÉ\î¿…{â•๓I;€Öm­0ÕJ»ãúãxa±1ZäKãE»1ŒÚ^ z]qÅ䱎!u.8bU„ÛܸÛ_Ù±Ïi+P:gÁt,æëâVË>`þÙûâ­t:ºóʑø ¦ç†9opñ:kjÏwŽÛ)ÅK.™Fï] ›È‡EO1—¿f..Á»™'G.3Më•™ÎÛ—3zßþ«ÅùÀ×Þ±’É~ ƒQÓµ(¸Í¥&'Ø[Ú:ä7Îa«k4ðËØ78ø3ã4Í•ˆvsTk…µÒôåÁXfmýLPV¨;õVL|€pŽç–¾«f:S¤Éæ«ñÏôÛ°ùCÒÕìO {T™!Ï(èàäf¥p…§i5×Hð›•Á¹µm.¥L¤õµØ’ÃSéh/q ÛÀ¦)>)Îg0Mr:ùóòÄÛφñJ€²+æé”=äùŽoðTc/÷i£U¬ùÿ}öUvtxâŠÂ.fPþôg¡¡ÍMfà˜)q6ÿ<:|¥L“«ákÛ@cþ«=:ôÑ–ß™qÊHÂãàr©#á'ÌêAÍŒ“œ¤þ™$´wCæ’½ñjíM »®°-ãþrN ÊW,#×.Û|+ÖDvý?ì<KnNåɘ|P€NÚ)@v–ÇsÎF`’¤Ì4\®¿´o, ?~žÏxÿÀI­I$k忸‡x+K·˜ŸÌViubÒFࣂ)ý9‹*·)0zÛ¢AˆÛÌLz…¢>ñé6E:õ$öMÄÿÌÄW oñvì»…&‘]·86e†Ð#1«(_ÀRzÛñânÞ†ZΠ|WËìgRts?æ¿ä%œ¡PÚ=ÎÓû¶³OMö`h¨<~.ÿÇæÍ…Nº(VÖŠÕ‚ºà=`ÉJžÁC¸¥Ø_̰­O2ß¿‚V9´ÉÅ–w„‹Îº|4v‡61Xó“]Í̯Ȧ—}^BÝÍ8ª¢T'WÉë îœ ‡XÒoÛ8m_öÓÑ6SM6$_?ú Wò1Ê…‚®?ð”[†jå— ØÇ\1$.o;`—¸-±Ýø`ÂÿLÇ]ÎS> Ä@—D0¹éå}€ýû׈Ñë§¥¹Ú¾cx Ûþ½>‰£O„‰çìuÄ•,Âk´ý‚õv†EÇ çÂØ÷M''‘öe­Ðròû/I¾/dc|•ŸSY#…ïìGƒ}X®,Θ}ÃüŒf¥¦FÁžÛj¨¶X€¨{¼ä¸c5WÛ ðp9·ÁOJ Ç`æB#z8æ ûâÓw誯E¥Í^pêO%–le˦g`‡q+.,†Ì&f¯ œ“ÅöKPW] >?^Jv¿Ç¤Kp›3 n³ÃDüÿ×ËY³÷1†Ï¡õ7?ãb©pöŠÿ5t_VÒX?Õ¬ÉÀ@ÑòZ3ÜqubðjgQQ# ª9^w>JǨ–´Õi‰_¬‡ô^rÐu*ÙùôJ*ÑNƒBæNôUæ[ôf’%^ ÷¼Ñ¸:Ýßñ”é5Çþ<Øw™E7ËÇlò©L(¸3,¾*>áÿ¸ ;üÙ]Ü¢ÇYB¼í$q©ÐûÛ]h‚•©ª Ÿ7þ…ªµƒð°R€Ìû«„¾0)×Èï¨ã¸Wá&,zº–.3}ïd”ˆå®\Ì6ÀØÿ‹#®»>3)…N4©¦ÿ6%w<Ás¬Ñ˼‹)ܶޗ ‘— u<@ËMÇù’yxǰŸÜG¨QÝ’D؈ôÄÁ2Øð× 7yÉÐêpí‚^æU™IyðˆqòšJ´ïp‡ó¤húË0*Þ¦Bõ¯†uufÄöY½!:Î=¶vÃbt£oÛƒu¥òx݃ûÃ7MÄ»ÊKvðrœþx†UÝnc8Úö‡Éö~O-ëApúSæ]}èך0û)På‡Cpc.!g¬2:Ís1Í»çå]gx5ðt^l^k¢ñ¦°¥ªÏÝ×&CF,,‘PkµG1óX"š QX+KÛÃ)ãäu_.܉fIììcÎéÇÖä“a 褻0/ƒ|°ÙzùÕ-DÎmnÅ“£]Œ•u/«e¾‹Müöö,«Äªœ³{|*½Àv®¢_g·ØoqÉ¢ ¦ªS•ª%©±«¦Î†;D¨yÛ “=ÂRëÐ þÌèwŠ1s¿ö1â‡ÁQûET"ï ~œCwðÖâÒìpSIå<žû’­[R¿õÂÐÛi¸ë$îx™p-ëszJÑH“xm'ƒÚT-ó÷Ÿbx¨–ƒ³Þt’ž³-ûï³GÖacï0tˆ@ÊU¸±)€îž™È,\sŒ‰â42l¹­} C›÷ÛÓöù†Ð^’Fwü9„‡?2r‹z™šE8½ÙR1•Š+\Ǹy¿ØÞ˜1ôÝò?þëß +Ò¤árxÝà÷NJüÅÐôj°à‚ ¤á¯^”ìW€·Ûy©’º/óg?w{â3T}½•˜>$RY*Œ›Áñ½ó•=è93¦ñÑÂÍFàÓ=bNªƒ×åFå¸Ζo§>‹{8¡WÔéàÔˬòtš¿*œ#š(J :ÌXþÀ>È_Û‹Q‰ªs'0>YxÐf×c8ãTBÖ*{ÓÛo Zç hÏËÅÍ»3È[óbP,òÅÅMõlj’™µC8¨‹ây“-‡–ÝÃdž$jw’\âDN¯ÞHÏ›Ã/ö³ÃÉ üŠ7eE|)µN‰N^”¢¶Ø9çÿÁí†î‡cQôÊ5ª:M>ö¤›|—ãòµÓ™©ëËàIÇR rq¡Ÿ Í0˜ÄÞK¢=‘ËhUˆ I8/ÍtK`–iJ‘꥾Œu}' •n¢Vߎ£ã¶8|éoDdSqØL‚Ævžƒ­;ÛanI° ¦Ðn~ºîåeöáüHÎým Ô¢³}—Њâ[UÉH7—ÔÙô¯”/]bAt ]Éwã¹4âÆxŽ8§U¨Øi )ÉèþÖÜãߊß/âÝ–̬C÷ÓÌõ6~úzþÿÞÿ+ÑÈÆLþĆÅâ}»Ôì÷;Ì÷[H¦ê®¤›2Š0ÿëiòõÈ>hKá¯YlëFyòS³J™pai<¯igë^Þß<ðSên<Ù ¯ó’—¥â4Å"‡)“ò$|ÒÔëe<›w*Œ»£ñ‰%ý3'†žŒBËѼR†¦øq jE «®,K±ýøj©=^H_Š¿Ãè­U‹hÚUíy¼l)-TÆM­îŽò)æÃçfKÔTRpÆ„‡ªÇcL™ò,4¤‘ˆõ~/Á3j/T%ŸuA¨D·Ð¤å‚4ë”øDüÓïüt§ü3öa~8]Ᲊv9N¡=×Ûaœ4íðþ ŽA"äŒT*˜Ø¶á…¤²‚—ѵŒÞÅ<"—þ\÷?ñ²©ÿõ¦ï4,±±ð? «!ÆØ“£C--‚÷®|Ð}‘ÇCâ UFZ¦L‚w‹-‰†ü¸¨õ5îý=™&âíµ”˜in‚™:4ôã#è–L€m|«`FÖjÿ:¬æŠÓÔåsIÍŒvN‚Û5¼f;™´›(“ÐË 2wI25Њ$)¾ãfò›á±¯rô_4Î6Ú çO¡¶§¾Àây±hõÌf"þMU 8Ö7$q¸Ã‰ê.>u·7°Øðë‰/Ù¿-“Ä^áƒB¾r?<(…ô£ø vKÛO´º0,8à@#‚o ÄŽ,XùÎï<ûŠïý@yò1`ç…Qé}L±·ýµõ Ü|x*g“7ÁÙ¬0g†UÏãÞ”3ÄáV!¿ÚÑRYý„)>Þ@M¬è‰ Ò<”J×Ïîbôx 1Bú.#§t{i‹>«¬/a_î#!y¼‘H&UÃb¯xÒÅ7L®‰ë;¸òÚ`Q<z¼—³(#ÏÆ ïÓ˜2یԈ]n™÷3µâ ¡ÇðgÑCFVè6–Ç{ãEŒÿk„ÑûŽ`’ø\ÌŒ5ÇWÕ¸¼¾ö^.ǽ¡?ÑbÖw¼m7¡Í4ŸiGsÐ¥²ÿ,bª^ ÂÚ^ovûÇ»Ü:›UôÇãu`µ(‹ ŒÉþšŽðÑ2®É†éøyš þ5;ÈnÈeN®…¸åÂÄöƒ2-ßš†ù6ɘÈ]JÎs„ö¸‘¬Fª^Ì@‰r0|œŸZ‡åÀQ|ÆŽy=ÙµÐúr¥)/••¥×O²3o‹“zM ‰O„-qàÁ¬"ùq=è¤$Ml[lê5¬á”ÑxÎZm3ê$ážWþyù(µÎž¸ÿ˜O ý§2E)øc²öÔQˆÛ.‹ïé  ­ßÌ0¥p¹ ›Vx†›ª$HjµýSzv„ KPÅ’¡î:ºÐì(Ó1ÐϦE`"?/ŽåêÓ êFE÷³yASÈÊŒõp=ð<³ùÝ5Ô}E4û+çÓC\ÉGŠŠ3¨ãõ2ŽÇþz¸vÜ"Lõ1I~úIQ‹ªÈ<ájÄØš–‰Rg©ÿ½ÿöwz—¿,ófÜ‚ËÈÍ_°bÅEœæó½lÁÙÏ}ÉúîIÌï•' ’*qWÙ ‚EAüšZî¯GåJQºSà ìXXÁK'ÑÖTOòÂ_‚ò­>Âþ×:åû ä`‘â«€ù—›ÀyÇ@‹Ý£…T0Ùú¿‡ý®eà½ù'JÔÂ2ÞidÝ)T<¥‰=ÿc.Ñ”—šæÒWêÆë×L†çìšt¥šö¶˜êü»«OÐUϾ°Žs;.mû|o´q(¯Vs+ê(»'IìÛéÍž<˜©vè:Η¦ôçÀœß!ë·¯ ÏÑÐ[‚$jYÚFJ¹Óc)¨Õš‡÷oXÐ{ŽÄõqj¡–b':1àxùÓ}ŒÊ¯K¦Þ ´AØ€®üÇ7/`4œìß>_(œ§ÿé›û:Óv&yz{ ÙdáFBßKÒ»O棴aåzY’ɱlB¥ }¥\WB¿âÉ]a>GóϱøÂE }WæO¾}6$‘«~±+rt «ÔDýÌýPÁ8¿Æ̩Ƈ½pLì «¸þ h\)!Wµé:¤‚UÓ^qªý²)ïÅZæ‹Y õ³x ¾¿b9ö©Ï&øOQ¨³yÁ#T™»˜|x .ïÄØÎ½Å¸ëï9öK*ìµ{ªµ ô§f%£EÆ›™hN´Îk°ç^(Ríøhç¹çv/g{eÞ±ÍU;0óTzï"ÞíCøFUš(þÜŒñêÊÜð¾Ã¨r1“VíÌ6÷8Ô~Xr·´9u~.Ô¬2 w(É9!(Üh‚EùÏᛈ6 Q¡…jäŠZÞÚ¤\èÇ­r’hÒÛ¢ðá‹õÔ?§J?b÷XÜ$LÿÌ…’ì«Üïµ9¬£É90<š…Ž5›X̧W›‹ØÛvÿãÁxÛQJwÄÁÔmÈb“Tº:¬§æyÒ]|KHqÅiöØí¯Ð!íö_fÜT˜Õñ\‚å¨ÌCKâ–‘AjçtÒ€%ÚjòþKILõ™=ðøŠ3Ü~—Á&FÖ)vmc-ñ»¥0 Ù'C¦ùŠQ?ÍÏàœÝJ¶Êd²¦W¡¨º“œ¶éÆH©iàdéH¿gA¨ð1ˆU¸ ×Ã8ÖO‡X¢M*Oòƒàl†ž´>Šj œõWWÓÙËʘõY¯A:2‡Š'ÍŸªعõ9|ixÁÞ~m“gÎ+ͤ Vj ,iŒÑËyèÓ žDîð¨ÍJÇ8OT Ÿê0÷³‡áÎÚ`43œD-]ã˜ê?Ò°Èo~š2~=¹ƒ>n`Ψ¦RVU Ù8:Ù#NÒ,ÿ½ÿô¿ ÌN ÄMçÑýó¡µeú±†‰¶Rfø&ÞÔ=Œ{V©ÓYE.à $Otæ=$r¾ ÙŽ ñô%žQ^E„ƒx°<ɆnlàrN¦o$ cÞ3Ó¦«ÀÈtnMbÇ|4Ý›Ô^}êWÍ‘3ò·ß6 ®UÁkÊeüœ”„ÇeÍé…ÝJäòÏ©ÄçÑ68U| Ûóª lð-ûµ÷#òsîËç‘ð–«tëgi’¯áMëü½á†Ì¶û‰ UZk€‹ÍàæÂSLiA ýáGà/QNɆFO~,4φV«¸¢¥L¼E&ü/n1ƒrç8âZã¸þ¶™ðÞœ¡†ø¥ç‰ÕÚOäEeɹÐð“kFHÍdÿp!Ëôþû•ç=fæÚ5 [EÈš§9ð¸ÒˆViΦÕw—’*×Kwnl¦ûÚz ~¶:úÌ,rrî*É_ CYhI˜NUK~¢eëL"ÒãÇ´ ýÀr›…´r+µ„°Xß/]<ÀÙß0Ä*nø>·eé)ð““ÙÔ?óÿƒí_Àw†ð÷½Æ—Š‘/èéàDôUÕ&>wÞ`÷Q<(׀疹Fuš˜ÿkb½ñüË㬉?‰Ûs‡ êA°·‹³@é÷ik"ž>¶F•T˜dvÙ÷’4ž“?ù‘çf#h#2À‘ÂßÓÊpj§U‰6Ç*¬ºèEÍþl„[ÊÇØ³xÑØ.§=ºÆ*ê:£S?Êý˜]‚ÏÞ‹Ðþ"A,(¨Á½ñÓȉ…MxÁCŸ³#1ž]x¢Ä?­¤Oõ¬Ñ·’^›£uAϘm#NÚn²\ e¶VûË…O¸‚'¯ðd‚Ùu)ä>ÐD… Ú[¶Œ(^îÆ0ÙU¬¿1þï³j´ª 2€'òÇÄü/W¹ Ê]jœvGÐ;SúÞB†Ò |ÿÄ‹®rÒ#% !¢: ¿pr١ȇ°¸Áм¿Ýjk<¶€XÞ€ÿi›·?_ƒ žÝ¤kCR±M¡Ô„aEPœàv¤è-²\–* ̵-Ø-K·ý¦Vúš4f¦%àB_½ÎõÄÙ;ÈE©‘t{[ð–ÔÓ.½7¾K ? ÒÜDœ¡On*üÁ ¨?mAA…)¤24xÛñåLXqÐ/ì@=¡™(Â=H¬#¸­Q’ªÉÞu%Ž"i$3Q—nUì@M{á û·yÙJÃtX¶¶N ÿa×é é©û8ý×#Îïu³°1#d¼ÝÀ>]ÏRáÍÇQCᨘ{Ûo=.ú?½òEµd”z³Ä׬‡‹~<nZÅ`Õœj•(DÜÔìq»øRÌk”e/pÁ‰bŒúa ¬¹° ‹£ÉœÛ¯ÐÅyMò@ñ«Í¨™cD¶Î*bF_šƒbW“­±–}ÆD}‡«¯œÇ¯ÓÇvÇ’¾ ­pÏ@Dü}ð¿ýßÝM=—?Ä—ßV±ÂCT`[>Žw€24âÙiüš$HÒ¶ÞŸXÿ‘žÀ2³…áqä*ÚYƒ0ÏïS{, ïl? ³Ï Çäy`qC•½ÛÉú'gÂâ<ÊWÖÊ®pMcòÑè¯ qŒß¦MUØè)AüO“ÐeO°ŽŸÂ ´¥×Þ|ÃÃ{ëÀäX2ðLö"ëØX×È|[m@6-B­¼!ܸ.‰™ú^nf/£ý5*t÷}EøC³Áû¾7ž½–ˆ§šŒiÀ®¾»€Š¸ “ä}™Q=eªRÞÀv†åOàß M2ÓQ˜ê]P$Õž9Äÿð,â¸s”U3¶†Ú:U:ëJä¬÷"|Y‹X^'þN†\dKƒ«"ÉK9oH¹ò=ÝÖP§¦6ÎG’Œ_¥ì©"ße²ÿ.‡x.9N>»­¢Bã|ÕHÿQ¿°œ›,„û>/†/§ˆô”‡4â¹(Ý"õÂöÊžgè³zªiHõ§ýàñòæ¨~‚¸hÒ4å2$nm€²1=ˆÏKüFÊØZ©F\ïÓ‹óààEsòϾ¼¶…£ìe†­Œ øO3Í(5ް}=Ð;ÛŠ1â™×ZÂGók^à§å}ü'óì,ØXŨ ùaQš “žOçëd#Ý·“FSÃ1R$oJ¾á?Ö¹`#£›ECÉ{ºdec4)¹eI¸‡P\k²Çkäˆ}¨ªO¾wªÓûÆ; SW {×Ph<·FÎaj­"¬ŽËbµvS¼ù"•g¬`&I´Àó¦Ôuú€!žƒÚgO»ܙeÕýs3(ÔeƒÙ¤…tï}ú|ó^²qA~œ‹×äšAv^5æýÃì—ádêRArf³#ÑŠRÁÉ ˆ¦×AÚjÏòŒ˜Xÿxu©™ Æ5쑚s¬e³Tá'ó†q[R 13¥9)«µ(™Ò…QŸæ@ÍÛr<¶p ›ß‹ï/àù$+º£¤MfØ“ ŽRÀ]P?>'…ÀøÇü°¬“ |ñ–ãµ°‡‘üîÄ~x£ÉJøeá{^ÀÍ·àf\†kÕ dÃ(ìí脻ϡLŒ.)5§GjfÂíºTæ¡ÐA¶eÇ-ÚQ†–IY0m¡(‰¨“¤q¼Nø§¹ƒ[ÎbÖ®I%*pMØ4§Z‚eˆÛßCCQ/^±è…I3A¥5ÛþBŒ¡>-xTDò¶rѱ9F|JØcóQ/ö.\û«B.x(Òk†WÁå€%­ùîNt²|hH…½K,è—R¸êã¸aÉC÷üQ KBVOäéÖn[îx-¡2§ŠwÙ;y ¯•ˆM"‚ä•I¬Þ×o?‚Ãuü´2tU5é•#˜jC¯ß͸•%b[Î9P‚ZÈ|q—S¡¤Åâ8 f‘9e‡¾¢|‘´´ Š^.‹kgà7L¥zËz`ÇC úÄ!œ||½ãÍ«ÝuK鹩lÁÅòÈ9zzç“þÚ5´‘ÏÚìˆç,´7eCÄ’p”õ#{eªqHD“\ð€'+ÎàŽ]šè1—Cîþù?ÿŒ‚Ç…Y°>FžD¬ã<üm‹Õ·Xq·…PaΫ3þw† ÇæJZ÷EäÈôüsåˆ; RËz $ú¹³vùrî׿UįŒG̺ÄõÐ ¢yoîà­sÙ¿+Äqùäù„—g:IUÏ( *ŒasŸ ’¤½.D²°M»f‘[Ö nnJ6퀱#oÐP& Ú,@bÑR:Õ1·ó>ƒYF4ÁljÊïªbZÞÉ@è`9,Üp ¬h<æùM#´%vŸ?É|{pYºó„i]DVç¸Q¿éåì¢ógqv%/QTŒg‡+hi™òŸnRŠRá€äù–+o@ºKæ„ý¼—nÃpºhÞŒ†Fžih¦R ^óégÓ/œzʬ¢–†*š’‹kÛðô(Æí¹«ömÏv:Q º;/p½–ªÁã/Zxo¯=šþÓö”“9yú¢ý×4ÀÃ0-âùõæÎYN†ËÁÿ¼YQó· 1#©¤Jÿ2¼<°ô&‰’¼ÍWÀxKú¤‹’Ççâ ÎH“nŽÎ3“½(ùfÊ3ª^ŒµÒ$pQ=ˆ£1ö0eG"n"Çö ŒÄà½Ü)TgD ýUÉÎu3i÷=z>¹ß¿'r{3ò—×’¹‰2óÿRq7³–ŠƒÄênÿRÒYÍmÜBö«(’ø”|N Æ0ºW ʬH@S:ý6 }ÜéRŸ˜ž7S‰‹_+nú~Šum0ÄM{ʦŸ×¤£ŠB vë–ÂÛ~t|3æŽ1‡s<©çŠßxðÃx\z3ƒ…Peu<5 ÜÓ-=‰ÿ\µh=•8޹ÚÖvVüY*w…û@2†êøäáqô?A®Ë˜I‰'5ËitÅÃf¼ô|¾iš±…Wî°™9ËÙ?| ìLßBö?ÛÅÊô9ç_Ne¸Æ?ð £ä5ñü«×IqPïËA¾'7Ájÿ\æ¿þÈ·½Q鎛áÇOt—´¤/ BÏOÎŒ%ð8#ŒCvàÜyŠÌ6“a&²dúM&3MáÏ-™¨lº•]ù·„·x9äq’ ã¾>ˆnÎ¥e¸~ Î\¹ö úK¼‚y±Í€ÜioB·ŸæD¦E]^>‹ó;qÊ/ кd²+f ó8²¯Ÿ@¹Yþ°¦ Ž‰n†2C)È_½W]ºÍØßÎã7ްð_*³ùâAØÜ«È¶|ÅeO˜ÏQ1h>”3¾~Ù‚¤ðÑ`Sƒ1þÄã‰õïÎ l(Çq®×ñ5у‹òGµÉ+%Sì>s óÔËÁ¦Ô˜†Êm†åe2ôß9p~±5›ærÃ]xá›ÚÒ x%os‹Ëi°Ws ®;‘„Í鄈\oÃÈ—876Žp|b˜cÒaøMíó«6‘”KϤǾbFí]fqÛM¦¥á Zü%Zö±ä†k=2[5éF/bëxĶ3è~ªXÍæ6êÐâ–`ø}锞d3CthtÄ4â syϦ}.p÷CôYØ@BhÄÕ‡Ó»ÂDZSö ~oÚ‚õ™ÉÕÁšùÆOĹÐ6‹/S+â™E}øDXˆcÒ=çì=ÍÜ &Ö…ò´ë¢Že¢M³n€øw::·q} Cn‚¦à/šÙŽ"!5 ´å$Nkåš“aÛ¯FLö1¤ë/¸cñÂ=øõs#뻕T³Ø›¤‰UéÌô>O&w®ý·èª”ÊŸ _*-º“y¢È<ç5£¯}ÈîCÉF Ä{Õ쩵+Ñ\a:Fçør×Öó×~Âëô ¬–:›leúìµ>ð¨uƒ!ôWý3xP.FÚ°óŸg±ÅÛç²ÛV àÊCŠÔÜdæÿI´jf{f|ÿU/@¸U›u×§×cX,áצ\'Z9E€D<ÏÁ7ªaÚ‡}}M˜¬>a‹Þ§T‰—ÞÜ4ÚöüI 6¯€êî'.7•hÚ:Σ÷áÌ¢k 26‰)ˆ€†ˈ¶ìIr-)ù}^8çB¢ô ¶üÁ|ð­»è%Aß›§VÚr§u-ÄÇ/'þ­†å+l`}V'3²ü1þŽèdÃãÑíš2»Í[žü7Æ–åâô˦Ãà4õ!«~øzË¡h,Mœ%F|hæÃ2œ®³lØœ¯¨¹ôv´jÒg2íÀ·1tÿnLC¿” ­_jûÜ÷Þ{¶í;>Y¿Ù”|wP½ÉÁíŸ8:´•*~ËôŸh‚ŒÝÊô±óaèJX†Ò~S“ƒ".ACé&¼>·ÅýÁeuÍø9¿Êv¥¯Ü÷VØãî^ÿÄ90_Ü mšÄ͘†Ê~˜åÊâÙ”HfŠa>à‚e±`z6Ÿ+U-‡Æòiôßròm _4Œq&ï’'F]ê4î4¹”ó=øSœŒœŠÃüüÔrÿTv¯W øÇ‰CC¦=½îá6Ž7i°ýN!¦^:ioŠ)þ>™‡ ‰.õïûiz˜gVÎÌt¸‡›Þcß¾rXúMŸßeO¥\@g!5âx1Ÿ,S¤u;ßqŸ/ÕemÖ®!¯V1QVÝLе>µÈNbæŒÝó oPåÚBòòþGVäƒQ¯Å8ÄÂüuÇÑ0Z…‡DI…q&³û·>¾sÉã~yè5“9'NÝeU'çkW*’·ŽÚy¥Âí¼"¶dåuÚ£NU Ÿ‰bóû•hùº wnif½û÷aæúÏQ¡WmÝì`xÈ·øÂ>±¦öÄ]Әܼí GæfÃÞx5ø·U­bnJaÿ¿þ'§Â§r溩ϔ£Jöc±á>®žL$Ò ±r†/ºµ ÷ÜO÷r±tfùÙà ›ãâ‘›BZ†vá?/9ú—éÆþ™Ì÷î(*÷úÓý+óI¸ç,--€ªÛN¸\†Åˆíz´wÛUv}Úbˆô’ø ™Ä6M¢g—Þ#·Þ\ýˆdîå±{¡O’3ñ$w=,4§¾ åØïáÁýIðQrŸæ¦9”uý†{…Ö¡“ƒ1ëž×€Ék¸±e5±kV³=3–KääÒØ;‰Et"ä+ÓÇ‹4©ŒèKT™kÂÿ÷ƒ5á%aÙ˜ÅÞè_3>®X|Þ?ÂF¡8}ç#N½äùPE‹°FïÓQy—ÞŽ—†Àp9âðA•D,¸_’.1ÇDcÙÌ™e@ɆËZäúÎnöÜ’—pt*hÒ"ÃW =f"C·~zbå-ðcø3ôa±0OŽ¢—]-SŒ Aý·>ã´­žIæÿØÂ¦, Åñc¾K†¬Ðf|r‡ÿ"C³ïIÂÍ® ð»]:a†Ä-Q/&÷7a~± ¡1|Z܉;mŒ`©óŽÿ4Ò´H”®[ØŽå;M@Úî$¾>§4ÿhзšõü …û²èMVŒ<2@—1z°¿=~^†­~ ÅŸÊ¡À•›G_á xa“ÝÛ‡÷Nˆ€×üZT~À¬a;ŒN0*b-ãvf Hõjtžý%}LÉ4{üùx .š]>ù`üò#”Ä€sç­H ˆÛwn¼bÿJGÝwV$3Y­…ãÐ6ì[mM¼/ ‘Ñ“éO‘j\nE=+ž3;²£Ø¹¥'Ø)wžçà ´žL£Ú«¹;ÕŠAdGÙêîˆé)Æøõ¨«Ó¾–=_ªHKy¾²Ùú2øŸ/ %&PÔN‰xÔž‘ó1ŒÉ aâ~>„yz,”ÈÇWqwœÆ°˜~äÆˆƒÃ%èz4VΤ+^;à©¢@œÚêa 3éuÇm´)r'^MÓ‡üO³èšúdÎòøÄÂG³{ÔsÎØœ‡Qê'ÈÉ%/Mb9ž§ÝÉë/cÓä%äôœxæÏ·dØ«pÃ^D º)*VÛR½þËèpB‰üÆjoß g7Ä€€×$*Ù˜ˆö3Û`d†.ŠZ\%&ùäcÊ+Øl3…,y'‹¡&`¿L¾ ¢Ö¡ƒÔQÁüÎ.ž°ÿCÊ0cîÄu„8UaÀdo6ñ®ät•ã‡5l~¢ÑÒ¹x*è3F|OjY½°ÎËÇãíþ$‚WºÇöCŠš±X›Ž“o¯§‡k%¨» >·/eFdÍ`’ª)öBϪk¬{h[4AÝuWAíñu̪ÛÞh°ý"§ù¡™ó÷3XQ¤„ÓþïvôÅ2þ¡ŽØÔ-Ëö*á|Ð@­-û%ç…¨<°™tì&÷yªsÕò.n¸RÅ 5ß œ‘PÆ‘';œº‘ÿi-£ìgÃJõÞ„®†Ôwr5fØ™RU›ÿõXßÅ\rÅ„ŸWY]•xA†ƒV!“éFi{ø¯§s»½%$îZÉU—ŠÄO>Ypô’ÙÜ–ïŒ,`±ÆÈ{ QM©lÙˆG>„cã_Z3ž“ëXã‹çáeÄ sBkÚã“›ÌÚ…“ˆÚÕùhò¯—(©âæ«„L";xªðÁíé[ñõé\Ç764çœÎ9‡ÑcVp}w*ü}‚Õ$ öF %‚²öñ(¡žOï´­‚}ÈÙºžMM†Õ[dÉÃ_ÇýFqùÝTü襗áñÉuˆºDUÄabýSpçE+<ƒïsQzšªóžqÚBÌi¯Ô!’âCs|ŽQ¾÷-"qÞÖUqôYþ’4Äú±òÿ‚ßÜŽc2“„g«Î¡çÜCì©£18çIy]´7Ÿ]N ;§ÁHœ Ý–:‡ÇLiû’«û(çðSù#Õ*h@T̸©G¨ö ™]b„Þ‘ÁKê0ÃÁ¼Î bî°_äC§y…“|Ôêõnò³÷ ì©ÌAµ·[è¶6/ʱ³$)?‘Z×aþ~åÓ§çM};WZ<´›ëëÂÌ 3ƒƒKÉþ»>×A—°æÄ®kLÀ…rÐ8¼Œ¬^>€N§~0O’  ~q56õ‚û™­Lo¬]:*E׿…“9AppAþ6µ"yßs¹sšžÆbbÜ)ü¹u:Í*àöñ8š%6ÂpË7ÓÒ—² óM€¨N;Œ¿Šý§†g³åp㟠³@›LœF¹fÁ”‹é°zg^ð',æ|ÇöصÌËÅ è®ÈKÃÃdˆëÌnÈ7Â{.`E4ýìr®øŒsÐ,vŽüňmR`Û¥AÛÓ«ñ±®6}4©ŽYÞjÿ`‡o(¸K4Ã…ÈOõOùªiXÝ·„yÝ! vÎ÷˜ÌåûàÐÓ .³! wÿ¯4UJø ø§‘yZ+à¼~܉”F™Ú­¸»¦’jç"OÊ?FSÍõ·_d¶ô‰¶Üã;£ó|ᎆ{B“)T¡ovHù,'²²·ˆ.žÂGã×[‘¥3ãÿÓQ3{vêÒÐ#Û°b²ýô«ž©“^Gv™‘¼¾v(œ¿ž}åÅ\ÉVÜöTòbÃHh†6^oF1ó(°ê¦/,Gð:k@÷™ùGR$9h-mW[H5~?Å8ðOU4Þ‹“¨WÕ@—Møžo.ssd”,sبzÊü-&ÞI‚ŸâItÏÎ>Û='×à ˜ÊŒÜì³î*nÑ—ÙôïÖ6vÐ\v‡å[„Ⱦ–½$©ñV+χ=Ø_·– ®˜Oû.b¶ÌÚw LÓäNPŒ¢sÓ03£Û_ÕÓ_ÑkiÜÚsT#m.ýRù/}ÁÒá"Ì[ú½eI z “œvDÁÅ’FÝ’¢­Q Ø#¦D»PTéÊWÃ㋆h;4…l3¤·vøõ§$©þBjbX£ge {ú>³ñ¯ Îy1 J¦’ªU<äChÎÿ[Yýçhxàåd=:o¥0YÏ;—îyR ›ÿÎfc¬³9—†Pwñ¦Xx}y£ŸMÔ`ñéÓZ\?—.ðˆ [OÒ² s°ÔÈÖ2Z踔µÔ£ š»`8Hx_'º!3¨–n:|?wšçï…>´ØGp} B®«!1µœL#URàéÐl( >z-ûP®f= TÍ$äk m•œçäD­"Âe>¢üfZí¨¼¢CÔ8’Ø‹.T·`G%‘?H½K4¡°NˆÞ 7%…†¼ôŸí tO.…·¯2ÉÍ> rÍ:yÂÿk쇙— Xw ŽßYÑú¹pÆ-‰Ê.•&§vdž¸ý`Ÿ •Ø´ raïJÜPå !Â4?ÜŒ ™^ƒô[;]¢·Ø­Æwàêå) µý0C½Ìý[‚dèa?÷pÊþ”À‘ÅX§ › ƒYè ¬©Ãj¿hý7ÈxFœÆt A;D›o$ÐîW >a~s¾­à£o“æ’ ïèˆÆ5w(®%ëNmð]ÕÃýåð4w?ÜåÑ£ÎÇ¡#Nˆ|›–Aã·…£úwIr±é{’³î?7+£ÔÙ'ìOÔÝ®·_0 ‰¦Äq8Ãö û\ø((Ç\¨à# ‹<ÉvW#*ì·¤Ô“qùL–ûÉe™”?Ž_û7ѱ. ÿaÆøVBÂð:ËaݽXªyÑf?Ò¾¡ÎÌø ù"ÿõŸ ¨øª’êv ’‘ï'˜ZqúZQ™ñ9râZ)ñÙ²®GU f¸™míCxã _«Îø6-æÆÚeœMZ•¬Ë$'`#;=[‚´k-…í+opŽðo'a{w““10vC<𦠎$£]z%¾“[MÓÞŠ0÷~G¿ÓüÿÄÐèx°M[÷ ø’+˜*@}7ëÒ,ÙqÿxÇ“uÞFp[Kô͘Aò%_³yë&Ñ/ÑlÿÎ4è–ñ#a#q0`¦&kmÈ;Û)$ü­}­*GWó\—Òrðœ>À‰æª2´cÝSôœ¾‘cž³?Ê}ÇqN2O Ó?‹“JÇ£0åÒ:äl7¤/ÞMÂw|üð¥D‡\ë’$ÜÏ9Xÿr ^´ ¯zæÂ‰„3xuC´ï fÏÄÉ7‘f˜±5?½HÆ[60´Ùì?{ j4ƒ3íê$jguÎìNç†Á÷Òøï?½h"ÿ_/8<Âö`|O _e%ãKé⋱$(s}Ö¡Iì†yH£’4½/ŸGÇ®ê3vuaaçøo1Ý—{/2ˆ½m¸™lÝcÈú¥ÆÃ\›-ôÀ¹ lnîµiZ’O|}ñÐˬæâ¬ñKøèRA^^ȇ{þë¨Î”<¶kÔ:F’\ HõBzmUué(`çÌig—ß;Ê–ôŸ$®óOPíÌ|ô/i€€Ò2šùX Ëßâµ±SÖÖÉj _õ™ó¡{'mð§cgÕiPF2u>'ݺwk ?6˜beM Y¾-×§ßË·0Z'æµñ%îåQr,v.©~ˆ)\éÉež4ªf?>åø“ÒHj&FÖæ3{/¾e.o×à<3ˆ¤K†³8S§&A©ý$–gÊ*#ÁyíšSþ§ÿumÉHfwÅÅí× ŸéMØÿµÈ…Ï%ÖÝSiü¤òR¯vè™’þ‘o¬Á‘éô\è<²F™ðtl ³ŒÓIå%Mcȉ-\ü¶Û ®jý;y\ü²7™dSSô¢vS•hé÷™pd¤’,–Á5åÔ ¾C*UQæÃ9ø±]džr° ¨AþË«p/ßmv{Xì„ý#«Mè‡=ùÌÅðµxvM²‚¿ t¥ݺ𠄙Í"¾ZWØÂgÙ a=Ž<Õ¡†á£¬ÌµöݺÇÌŒÝÞd[™&ýfC•úØOÏpxìi(%ë6.BíÞÃŒô~I¯ñ í§„"ÜanÁ¹ºîOi¾esNuã\šÍÞ+£v Yà'MÉDüN/n—¾ÁªÞªa-¥Ÿîõ36–!°µÞ‡rv ×–"TjBÏ¿Ù{|7Òc"к„\dŽ¡!3›,½]G‡´4HoaŠür ÏF3PµHÞº>±ùþt'½|°dbþg{õâ²À!øuî¼^(ËFÒq»‰Q{¹“xû˜RɯŸÙå8šÈH:ÈÁÐE†Í:“‚ycÛqÒQAR×@Ÿ)Š“ùæï™û.œƑÜŸñÔÂ¥xöœ=yW˼ùü ýZíðÀýjÆ~ñ¸úÞ¼®" –iàŠÊ„Ì+ÃYΓñ‡"=nðM8{ ÓÅHŸ{ð/lääÍÖ‡8¸Œ?»X»XEºb«: >ÇC·æò“„p—éüm;ŒË?…à,«X´è? î·\W$ss­ˆ³î5ö­¿3c.é«…HÛšcù/A© –¼$Ä&~+2“ SM&ƒ“(™“÷°ú)Þ˜u‚¼}ˆ7ê”èb–¿õ’å œë±Œj zQñ˜…´Ùé<›ü2JÈ}œÇ¦àÌ”‹D´}:å‘Kí—ÑXîjEƒ­ï¾”ö¦uYWqøó2¦dJæÝuÇž˜÷02‰Šm5€y» ¨«ï†ût.|žoƤ¡ƒÎd?ЀÈíX½Â‹6^ù‰:²øHiQ:DS^úÿÀé¨}¸w{¦á­Yê{V<8hM¹EðƽÑ_œDög´&|?9;õ4çYäÿìýUÌ·ø÷aóѺžÊñ·ÚîtÄû úz”ã˜EVÙ_Á¢ÀB¦³w7•Ëjâ­GHžo&µZ±—´2kIúG¼¾:ûŽÈ¢§E%9úÍÔOVÁakèÏY7^Køl=²Š©äöZ'â×KØ-Ÿo1ÚÞ¶Dk԰ݟנí·ûXàCùß«“µ÷ÍÉê›S‰dò|[¥GGoI‘SjBóf‡XÑß8î&‡ÑÏ=¨¤vŽ=p·ˆÕ<öée¦;Vþ<> !ÃËI´¶,¹f®vÏ&-VnÄd‰m,]D7ÇÐÏ3û'ìŸ|D v{B½<5üö‘CªuA‰÷5R2 />¬jqzæFÁö§.Ù Ïp–¥Á(/MˆÖa%~"x¯›óÈņT¤=ä¸L§mÙþTº©ž=+Þ ´~=ŠÎþÆZ‰ŠØB)ب™'=B˜æÖGð¸ï1¤açµTCp¤6…aØôx„hí#>j‰äÉ<еgböcqjiäF{‚MèóskƒðtÈç¬ï7ƒ°£iÐú…S›á’l:•‹ÈµæÿGÔ—‡Sõ}ÿ›çyŽ$*cH‰{Ö&QŠBѨY¥’TJ£! ™§Œ)ó!÷¬]†Ò@£Ðûìõ¬µ^ëõÚgÝ}qôãJʱkÃ}ßmIÌuezBO€mp”! ºR4åˆ$ý;a¿Ik$ç“Þ!̪fµMÈë&=ÚüêýÑâó ö³.¦4ž&ÇÇ®£Rá58ù*Ó§páe˜%]~!𣾰 =Ž"é{s¹®Oq`ÝoÐÿëа+Ø€è_ aõ]¥rw$Iæý#Ôª„#ðí;[ñØ YñžÐÏ@Ñ?YLàGrh âÃZÇyÅ*úÞq1팅;€iÈ驪ûHécòÛÖ†¢Á–ׇêì–ÇÙË=‰”£,ÜÙ CkRauj"teE²•À·}´+NÖ™sáïë’È~úoÂ-‘&ÖõÛîÂÕÑx°ü0•Ì c5®\稖èâÔ _¦Û÷&¶Gm†­sráÍ­™äZrDÅ͇£%ÔyI:ê¿nc-ZgÞƒŒÆáçöÇ7Ìé¢õøõ½ž¿d ãÜf¿9I¸*DÕöüeéFâ ºø, ±Mœd O¥“05(d#âÙ%Ïfaþ@¶g6ÀrÉùtpæp^Ï¥eÇp…ÂÊ,ijrÌ„QµÂàgo0¥ó†õòvrhê ô»ŠÝìý3à;e¢²ð$í3¼¿ÚðZÏdr¿÷ÖýîƒÖ1î³I†tä¬ó5‚!!ƒ°íÛ¢5m}pé·ÝªmE_¢°¥œNƒháX¶±I9ñ"×»²*ÑüS2Ó:χõ`o¤ÄÄûÏÞ³ŸÁ3‰ý7îÑä»û¨ÚƆWf õI=ië3™ú¿Ùó,0íïYf´î­Þ½‹Î#Ëàa{šÔ¿•C›ÈµË0˜ºâìl‰Oí¢ÄWwÚÒ¼ƒ§dnBÖ0Xu8O·?éþôK¸Oƒ*ŒZÏ#¥Â¤â¹4õ ¡i†J̳fcºöT9Y¤4—tVgSQ›xFôM¦¹L‘ Ó`®äwèßMæÆøûÛ»Èö ez#,ŽŒ8…Ȱ©´ïø *º··Â·ž|\nUÌ®œ¹‹¦Hm'ÒmDëà1²oÎcÕ¼ëøeñ{K‘+¾Hö]³ 1MOðYsÛgeGÞÖÛ—ž$!²ä¼°?è<Ë…MëôÆãc­Y²‹4eeÖ.‹ ]kœ_ÿ8™›Î*$ÿrÖR½{ôIbœ®ãÙýdúÏЈËÃ"C7Úþe9ß‹»ÝGÃZ®“S0¤ü¢ k˜b@”–z“ó&üoWÔŒ£k qι!´ÿ”À–úª*1øáµ5uÉ¢7ô¿°žâc˜·"€Z…ºñ\oŽèÅÍóÜèÑ.qÜòíä(ç0ƒGˆf(±‘æg: =é“vM,(‡­O¤i_ó|öp ýÂî6 ¥uÀy«8xg¹Æž`ƒ~Ào¶}µ½ó>ß ÿfƒ·l¤SZÓ˜Úãª8ÝÓ…^™F–ºó’‡GϰÏ^pûÍéõ/S¨c>︞?¥æJ p%æå÷Á<ñ žéíÔ†¡•2´õîu|i΂Â7'0ðÓÀZ¿:ì],7áÿ;óAðIç:ÿ2êdÇêò,ct—VÀ”xyþ ;ó¦Éõ¶c8cûVSb“7žžÅùó€ì¿Ý(%Á¡€L+×ÓZ0×Jˆ.¼kÍ_qÇÏ’ƒ.»èúUÑÃ9Ϝעö¡Cn L#ÉƉ­Fo»¥´¿Ø^ÐG‹ŒéqÕmÔõóKèi¾& gç-'It\>çÂÉéðYbQ ÜÌzñµNAÄØD’óþc—;Ï “ZÃ9[—Üd_,¡¾_÷)ͨüv%Ö42ŸÓG ìe¬¹¸|œ;|d®¾xNZìÁÿ;ÿ¹{jI}`“5<~ŽáÜXðS¾È}\ÿZ.ÓåSRX¡9äÜ~bqµŽçbêƒ yéüªW$s»^à¦(]ú»{*UíJ''"xȾ£`»Q”¾)ŒeƒÔÔi:ÓÍd „â›PT/ç…ŸZ¼äÙËj¾½ˆ“nÿôwaÀ–Z†gÚ*5qgNç£6‚ ¦#@?þÒ"V”ƒúW Ž×1óP9¬0#‰%ÇPX@™|âÖF <©Â´+pþˆÙ¢t¾8õ™¶w.¡wÆöbâ5_0SßFZ^¦æGUÙÖòɸ»dúÄþwi÷~ØSq6táÉ–Ét$½ s®1¦ïðRÒ"6Ë-Cçÿ…i¤l,iذ Ì‘”¥k.Åó‰zV–ò—áÓ4*µá$v.“°GXÍÆ±/åò¡à¹(„xKÇ{êô˜Y6šM€3¥@äÚŽô<²Ì7ÍVœù%úgF2ì\3OôÌ‹× 8fÊOv™+@¾æ,RVp“©·ÌB÷åò蘴Š^]Ì,¹µñÏÙý!ŸÑÿS1dLÏžøýïFs8çp‰]ž´ŠHo´TDðâe ôBôE– $—;¹N6Œà'cpÜU¼æ/PÿDzv!KÙËi×÷bO"[îÃýÅcE$_…±S†çуü Bß²Î7†˜_9Ïà¨y#ÜÊO\žGpL/W¡ÎfoTø¾ŠuPhc×…‚uáúHa:Y¿÷ h'£A¨-èƒÉñÃì¢Àut³p,ÞìH›L<ÈâØCðA–ˆ×²í'Ù+C¡ôËuTwús5½ðø:G¼MÔÙi¾op¢.;¢œÂ\%ø§·,3ôÇÙ™`µõ6fòä‰z€§ªPùÖU¬J÷¢ñ/7´0G¿=ÃÌãC)ï‰Õ^-08o}ï¯cÂîVO(¬ZlBÅXk¨Ï9w=È—¨ÛY}ì3ó',yÍ>×Ö‡gÎp“­à¡‡¹_XÁÀ=Gœãy.éªpõ¿'cV s_i>]åUÏ𶛀Þß(ûì8ÌÝZÎÜÔ‹³¥ ©ø2úÚ/"MóŽ£ï6yZØ¿j…=IÙg^L¶<…>ROñé‹sìŸopõƒ”ççâ§Ó'˜×¶²ôqâ´ÿÿ˜èŽ|Ó÷À:WŠ?ûUˆÍñŸØíö]þàÂÉE¬Äš&q±,uà%~ë ŠˆG~`–æàÔøìqn3ÏG‚èÍ^Ø ¦EŠ¿_%¦Ž”CŸÙ-ìmW¤ õø÷βzÎI\êìOiÈ ’}óéíØµlkCn6aýÚ™¸ÿ—sÃü«½é|ÿ×í%É­äs¬‰H¬õ¦{Qè–:MÆ·'äiÇËß(«ò¢%*ñ½ä<²ëVnM^UÆr$ayóÒý,½§…®Êà²D"¿~„Å T1ð Ûn¾tÿ5ĪPÏq1F=× ºË`Û•Äì~+ÖÛ+QÝ:c²U9©‚‚btµq!Û þ ®GÑ|ý\´2B>]#"úb¹7ð T_®`œïu³vA3Hra0ÙiD}´¢‡4H<§E‹¶áŽ/|„ÇS™håï`µµëéžd7ؽ-Zï»ÃV­[IüŒr°õélôi’%{Ž[£{’þÞF2¾œ ¯\£qÍx\ÞËÜ@˜»ûÈqÞ—hýê2>¿=•x¯´¤sžm³î|‚мxÕ¦ü[€Ú^Ç”ï8 ;`V÷(Nñ ¦¹žï?t‹6€Æ!5x#!Oç_Þy¥uT#†Œ Ö4™?-¸Ñ¡’»RÁô­<ÅI5µî^ß…Ö“ç“­[È};²üá>ÐŒýþêþL‹÷|Ì8Û.„B§j(´¢ME󈽗+Òˆƒ K™•I©œØN9¬òZKîHð*L†n<Ú ¶ QÌÀB˜²a€‘æá•ó%Ìe‰ûâ†#ÓåhÿìÇŒ—G>‘v¡®[§à”¿é¬‘ <õȆaºùf(ó%êT>΃½©ô_ñ椔äÏS$Ñg0,ß²×GvNÔ¿m ý˜†ær¸Ö°’9×`F»öýå>ŽÐ%!óâøÿœ¨¾ûxLö(zj% ˜ÔYP IÅol¡Ãz)\óÝ ëN¬ffÑ»È9½šÌ5p!fËBw‹S¸ßM _©µÁ.w7б&ç?Tqß >gôΊqomÓÃÏ¢ºVîþŒk‹bdÔQèÛ+C£çøÂˆ}0Öª<Áw—\ð—¸m™2Ìôff’½¶38Súï³Ò·È «<<½‹0:ƒæ²˜>—ˆ£‹êÎ3Ae7ñrìÈ|ù˜á]öÚZg)O"Z›ž0«÷ÃýNdAI þ¼7wï‚ωLU€ñ2D›(œåi‡€÷–ñkâ#†n³‰Ó>UâÅ)À¹ç‘¥«ìšÏYh>õؤå3)tÈÑw‹&òÿ¶‡\h††Ý¸9ç•ѱ> ·°wìkز}9ž>8Ý/4æðÐ=EÐ{ªZžG°¯e&ì1ÔÄë?¢ÿ%öÌrM°ðªÅÌ“¢ÖëìÈ›bî©®$í#Žð:5Lƒó±ì@3®Øs zäÄÈ«#zÄt!/ëô {—\ŽSjd Ek5à “‹²7œÎü}ñŠÉá½oñ»ä|æóý4øÚî#c¥" S¾§ÚÓ¸­x&O~¢þ¼ÈÇúû¿qÁ¿Ì´a+jœäÌ̹uމðxˆtE¤ES¡ÂËàúÊ‘Ó$xb–L=Ì Ç׋нÁmHÕÖ`¾×i:="I’¨€ÌoRLÜ›ïX> #,§`QL5D§ ÑÔ¿…Ì>•2V‚Úe;Ðêâ^™ãtù*±ä!gﶘý}“pù%äéåW{˜ƒ:{aûÕ[ ¸ü.®ìk„‡¢ÚÄöùjø1,Ýß°CI”)ß%C3Úya°QìÇF{É…›ÓI}u%9°z-=˜ÈGr…ˆÈ¥u䱃(³ÑÐ+›øý¯3l0{D’–7·ã;kQ8Æ‹ÐÌ›Æ:ËÒ9‡ÃáQ¸7ûdõ|×ôúÜ<©Ü…I4,à ¬Üã@ÌF'òÐ&={?Nò½žŒ_ã4xl†˜Žû¸Áï)ô·-bKès8o£É¦}}Ìv  òµ$» ß÷Eã³ÕÎØTu}±ÄÜÁQV{MÛT¤LLtÔ³šCçsŠubÚ_Mrs×sÆ)zlÜ7ÂTÎÿkëgB?¯Ô†ºÜìú>šu­Ó·tá¥Äf·h$Y¾Ú›­ãw%£ àûyÌhÊ$ä¼s"ÿ5¤°U;þ±býS mÕ7LÞ~ÃÛ£é`òt`f2ÛšRêÒÙÚ³0/é.û^ÊBÆ6ê2(† b¿ «J‰ÑeG ’á†ØoÖlUîUK€/ 0ÔÈK×Ó•‹ÏàBNt¿x¯\ÒÇÝÏn°#Ól 6. R.#=°&g\;eÔ»H`V=ôßÞqðâôOv‰Ø-ˆß° œ§›àª…Ÿ˜Fw!êµÎû¾Æaë ï3kŒÖ‘¯ T™hÐRÓ{x8Ðg©2åw ǢѬÙð;j›ðXßQ<£ð¿þ7ÍÖìîYÇqéüq7ýÓØ¤@äK \ÊŸ©É_ Kµ.cý×3(… ÚúúRù¾XÐ\ø“UTß™:.$fK2 ÎèB™ óéy3²g„=T–ƒn¾b~FôAøKd~\'Õ]ÏØëëö»ø)xK—<’ö„, <ü’»âÛ#ÈvÓfÆÇpË"=Ø%¹½od„ûžòRc#ª1é k|ú<^'HÏ{²ÛÎHª­¥’ËIuÁ>4;¾…’WäXé(gÖ£ï`wŠz•ú“YaÑðuˆ*^€ûKc ã¡7»º‹Uý'ê_ÌÇ!îÉ^1øòiÓy?=«Jα·‹«˜\ O z¶”=1œÆÔ­-Ç– œ[fG¦Ýy ê>Ç©é¿j<_³Úpõ£CȈ}¤W>8d"µ Z­l®ž6ì1ZL”¹ªÄ9ƈè½/¦/´ÂS¾¤tÆ&oái#¸E«çÆÛ‘ß>)°«ßKnòàzÅ|lr†>ÊÒ„BL¾¤>´Äíe~¹Éà¹ÝFг€N]xœ¸ e nÄ­&˜ìaTè^Oû¶9:às5N²?:Wc€†:m½d‚ÿÏu¨W™>xݶõ:[ñ¶…csӓΑ*°Ê¶7´*²ü Íó«¡üÛlæV  é4›M5Ü¢1sy;¶lUÁG}¸ÄË›ým\Ÿ£N æeÝÌŽXSÒ?sVƒãGeö£÷ºÔÃ8¶ÞeíާÿE‹Ñ0̈®äH“û~áU—3œ”-b Ùµ¦Èûcæ½ ¶÷Ðoد^Å\žœ„Ñ)ÛI±Ð»£gX¤ àä] TWq+tªB“ <Üÿ®µz8¾ŽG\Ÿ²"ì³@I`3ad’ð¥œ8£|f&ͪ#j’>à§eG&%òLàÿói&涬[)EÆŽ¥ RývR¿E/ŠÀŸ ÑÒ?ò‘ÍKñÝ@Ó|Té’…¼tU› [ȼÄRˬäu:À®!‡^²~Æ?Ÿu}嬦ãµ=Ì“ÁfÞþmmûdœ^‰ö:Í ¢¶¬–ÍÄ·it‹Æ\îà߸áƒ:0¡{eáæÐZí´™|ݸÖö(Ð÷C«Ù8£d°»¶‚‘™nOºr*áºp2–ûp¡!ç5ÖnõbŸù®c, ”`g®‰-+fDþÀ#ïUè…ƒæ„ʲ½õGèÅ—NÄ;á[¢ºh"þMûŸB|U9Û_ó¾Vèþèªå›¥¶³å"D4¶–V&xÀ©,g²ÇÏŽŠ Œ·­0©PZI;ǹÿ£.rëS$}•«K–Î]ý#-d² ‡(_ÙJ4fè±R8ôôDÈ’WŠ5ìÌ'ÑÐÔ¤1áÿØdmpòÈÓk/1æW=¾ÜÚÏ´gÆW]Ûáõ“$äd9AÛ4¨¾­F ýÅÿ¤POuº'G£¼ªN‡QpªÚÝbx‹‚éëöQüÎãH¿Ÿ™NÓ XúEJ®VàŠÆÄŠÅ+d;´=îívDþƒ¨‚õ¼ôöúýX+ëŸÖ™‘âý•Xþ=’õ¹T)JäŽÕØ3ëÆF~©è©Ä…̓¦K³é€¡9“=K§Þý¯Ÿƒ‘œ0i>;‰æŸv ›u…é§Âë˜ã7™¯÷Rp׋^”w³ÖÙ¸_îïÂjýVðëÍ‚€íè±Á¢µ‘mG³ä$t«)­n¼Ê(\SŸÀ?íµcȼê„uÇâ™àWS@Ò¸‘ÑÖó`y•q]÷æÍâ\h=K²·—ÀIÕðl†'µ}ÍtÍy‹Å|Âà»Ò›ú»íe·oe3>…XŠ!ñ‚¤¤CÖHjp§{¿ãž±ÿˆ¡ƒŠ0uÙY’ey©øõÂ^tœ¥Ž¿î1hÿ\˜t8¥ãò'`äüÓº¥©x"þTAJs<ª¬•‡å<~`çö]×ab‹¯¡‘ßbàÞiG½@MŒÑ ³²¯Žm×¥0UǯÁ©)bç‘\/½OJ(­v‹žÈ®™!<Öšüëܨ»Ã¬’Fd­®qf,S¤¾†·ñæÁAÒÌH€˜âlZ³ ‡D·£\W˜IÊPTa¸Qü.ï²iUF(ÜòdÔ·ì¡)‰±øu’¸ø%¯¼ó°ΗŤ×w+ëÕYt/G& ¦éõ2÷NÒ‚‚xÎä^.b­ÿÉ’Æ“—@]òVnZ ïï„Q/³óèy†XWY‚ãÒœñùÜ™¢éƒha_Å|è§•¢Dä¤.ñÚ›NW¤óÀ߇Z7k5*¡‚_¢ñò‹jðý!Éô/œ°?·D”‘‡ëûŸßáÙ (ö–“!6•žÿ£FžžË`ºTÜp>bEÍ¢ioQhrÆ¥«â>õÛÐ`Vk. âöÂfÆFådªR¾.ûîÇÕÞæ†{{##%ñÕTWòöc´Ø² ÙÞ lå+|ÚÆ…#sŒã·ƒÆ±|4/Ò… {?ÁƒTN‘Ç<›°ˆ~î¡íÛQ-%V»Ýg·îЦO×¾Á¹›áùÛ4fÉ-Kªø^‚̳´¥ÿj$èhÓº™Ý}  ôÍf|>4«M€ßÇ™‡RÍܰ=‚~ÆLä¿<€Ci:à´- {.Ú’DM;X¬²þ<×ÛblVª íJšJÔó~3[–/À¼ø-Œ¿­'f=  fõ–ÄþA ÂÞJ±'ß­ýðä—­óf+ÐW, ú¹Rl@çãýÁør¯#IÐB ¶â‚íJÔwn)­‚ôñ‚7Œ+cÍÈú349Nˆ>˜×Åv­^ˆN;nCä¶JXó*-–3pűËÍçÀ)»ídÁMeâÊÖ C¥éìšIe-yÒ˜å§Ä|Z[3áÿ¡g8Wnņ=—Aë˜(Š)|FS6}•-KÛö¨’Q_RíFÛÎYgüç ‰w> hà!w·T@h·8ÝÅ—Œ,ÿb{E6ûáæ6ŒÒ®#ñÊim8¼BSמe¯-Ñ"‚¯[1ïöy<½¸ùVk‘:‘@üâ Ü ØDžçÎ ‹—÷­4†Ö¥u¤Æy*ŒVq¨îÇEÔ/sëL¯BäóéÔÞ¤IoÌc!Ú8}¿¾ {äÍý–´}Õqr9\‹øN#†:±Ic/õ‚ùÔN Ÿ{9Ò&Qðü·†æüÿá_ZÍZfîÇŸì³kœ†µ¯àûž‡0œàk†tÉ÷ Q ¢Û‰¦ ¸÷pì3·Ö€xù%Ð*]EEf4À¥W;¨MQ0Ytì3©+“é7·¦C?¶’ù!Òôˆÿ>ø{ YþÀ`Hçø`¥×lΡUÅRtæezæ¦.m8?’GøÉýgÐi( ‹ŒÍȱ{ 1ȵm]àq/ÛÕ>À¦î98¹]›m’Z@Äð©øxî’ 5Œ›Ÿ„›ôÁŸ×&¤c} êÍ;CbÀ@ó9v~wLÚº›ê¨¦ÆiZØ|yuݵ„”+ FB8Îq{‚žwOã£Au°ï½ÏÊ}ï%7ÜÄ {µIµk”ý} æ±ÏàTôÙ ÿ_ÿ¡NÅ<%`iÀcôµ‡w¡Û‰\Í|n¶€žžù í-ƒ©Ÿ]œÕÁ^÷u'Õ;Øß&‡A¤BŒ¼Ü#@»6¿‹ªV|#ð’ Ôx+?‚ËkgAp0³aùÆž° ¸§ŸYþÕN-Þ‡ö6åvŒ¬¨H¼ Þ_”èdUXê}ÌR@x0÷Ѽ.°÷”%ÕU©× 1¾£ÌÃ?Š$óÆ J>aW÷,8ts"þ Ê(]uŒüqüU`:NéèÀŽ3Ià>Nå}ÈÙßtÛôˆÊN5Âíë„‚g7¡È/µ`_@ÆF1*0[“?ýÌÉ£“!ÚÜo )[ó ~…)ÑÎTð„þ».HKÝÃðƒƒ4ê‘9ôíèTÞû‰O’cj§Ñ…‹GÐC´¹»«aôV6´taÄæ,îêo°UôÈ€Í)WØ1K“|÷<ƒWnIÒ£ù帴nj,pXK£Í+9¶„CåO|Ǿ7ã±B˜?0Kî2ÌÆ•Ó¨ÄÍH²Vu¸!Íò*ŠVMðŸÚ¬éÄ4ò Û¾í÷ÝÞT&s?ÕMvCƒõ—¡¦B†?þÇDº§¿EƒÚ6ô]ºŠ:)“¿ß’iƒ— kr&_K¶P‡¿À;ÿ ×; ¥Zû¨Û¥^´ø[ÅêöŸ'i_Ê!Då5ûRËœœiû…ç“7ýɸIx3=rЊ^WOåçóðÞ4G¶ÆÒŒ]µ•CN;ì§?7 al\4Ç18šN¿;ˆš‘Opí¦y$ý¹9uTž¸±lmV.|Ó›Þé=… Öªdi¶}J£k­G]Ì9jBÚ_|¾ž9ÀÚóƒC¦0ÍÞóˆÝwZ€x} ';â9þ§[qÍU"yx'©ˆÛOn>2¥Íí&Ðòà2âÛGO¶C_E:;mösv]ëÏ•æMKØ}ô8÷µ†­™õe–Óž‹ö4´,˜cÁ)ÂèÌeP=÷–õß=¢$°Üür6ÑË.Ÿ!2^’ž»,H 7ÇÃiS¡ÖVœþLL€;ž‘ m»¹>gKZ®Ñ$A‚g¡gš0IŒžDŠxŠà$¾Þ$KwG Ó¦MÁœ{Ü-¥NVWoÄ úÕì´ª›lXiHY܃V#)‚¥Zäa/C}ëÁ«¿”-”ƒY =ìê»á;M„ößæÁéwþ÷ûÏA—ráø³ç¸áé7ÆÁû'{%WÞ§e‚ÆtŠ>Jðõ}ûFH™îN• ÿªDÁ0 ×SÇuê?HônƒxÇX”Ëcqãè-æúqZ,R‚ó@å’¼ |áG¿U€¦X:3çXƯ2#Ò»§BÐouº°s çÃów•àåL+*­Én®¸dpuÂ0M¯/ý¹‹'úqX·ãMËàû“Bv³[$mÛ¾L¤?¡ëK9v.ß Û—¨Jÿ®?ò_3Óz€G[ª`ãëìOJá6Фy»kyÉøÂk3þ L‡gVÑO•ð-Kˆò÷XP‘—¡øéÒtqnËÆ³N)ó‰—ÅKH;p ¢ÆMÔ¿ Sê`×Ú÷œFŸ&\>M6XäBöÖ“øQA|{ž Æëª+”Ûׯ!.@N ¸ÛÞe¶yÊÐ4™:üòý}C¸Ú™3»"òÚAŒ˜í6Kç쮢Ÿ7“ê,ΆGÕ†ŒÕÃ|.ßÛÏà®6žT1SÁæÏSv«àSn¿›7=’&µ}˰j¾ñZÛׇæy¯Ïx2ê#ô(ï¦ÑÜÉ`kd‚wþäáµ€ÅthÄ“X'ç¿5:$RQËøˆ»ð1ƪ¸Åªï€(ÉO0“[AyîJ2oi0|®‡swóà÷«tÜ~Ñnâý×™âýûqËÁÙð¸è Üp\H7Þ§«SYËuà¦ÁeÍût0»ŠŽãÀ î†iÇHH½êPˆ6vó¨é–kP?rš=ªò>qÔ¦N¡eë 1ñ»œÛÐP¸ƒŒM1N|6´×"•šB%/7†XõÓ\Óˆpˆq@ÛéJh›®Ä¹&D. býJ/d7~fŸ]MVÏUƒöçépÐ_ˆ¬šˆãc``\ž ½Íþ¼fG¯ñ¿çê¨e¢ÉÚP3fAgc(©ê`ü’Ö±¸Ãå=p…$Ãø(q?…ñYÒ3¡:’"òªÃaßúdFìñ3ÈoˆoC-˜§q¨ç^š)ýðmM,E§gìçu[¦Œp®^h“Ýgñ´w4Æh€4þØä‰Î^©v‚EÏâØ(õ¿÷?­oc9Ý/Ì`Á«™`}&OžFÕXâ^Åľb¦ -гÞo.FÛ%ÁŒjVêþd¨ ¡_9œo7XkÕü>XÅjlîâvñNƒ½WÍÑõ±3jñJfk;>ÎzÎÍd˜ó ¨ÿ0½Ca˜oŒÞ™ôS°Id19.á‰u•õ̺½u@E.ÁŒßñ‡ª&Ûähƒb’0¶O×\ID¯F!ºóý2êÎig¹ XÎû§œK­ñ0P|Is¦±õ7œb#. P‰ÃAb½"Í×V[aôO9îÙ4ÆXÞœ«óLè¿_ ¤A¤¢-̳ w·5zÔE_`s†×ò´2¹1¶ŽUY#‰Åf³èƸ0î'õöwÊ0û5¬rž2އõè½Çrað°eZ°þ£ÒD¢eb“6«&¹½A]e8Ä)¡¾ø¼ÃŠü’ˆ÷ëL¨x§Lf/ûqn"ϰQ-àâŠÌ˜&½l‰»epýž͸õðb)PÇt»ùqç‹"•?Ô赟?Yµ·f¬óÒ3PéF´9-ppæOë²¾ÌQX¼w¬•B¯`ŠH7|é¼Äº˜©á­‹‰˜>`N÷Lš°_gßZ 3Q ²ÿå$ò)¾¼Œôi‡Ò'¬ì¸‹ jkÐ7èF‹A𔦊};ɱ)زw11ËšÌnÄ-©D?ØlËc m°³|av‡p÷#¶ÈÞ£qZ¦)5ãe$Îc¹hV[‰óó¶ ûozát¾F¼oúŽu$mW’ÃhœÊ©«èŸžFuT@«–î—óËðŸÅÖâÃEj»û;·ieؼŸ÷¿‚Må«0–_‹†dZâ²m§°yÐ \N–Yœnz˜x´óAzWõÇõoqF±ùö+߈Àxíkºw&}¶Õ­?³Çýz•¡@Y–9?ô›-ŠyoRaAïf‡ï §`R¯|{s1ð¬Ö%|j LÜ‹÷8oWáýËø©¾N›…€úTS:òÁ‹d¨Jt+q‡X{ldD;­©CÐIp•KÇб646Á³ø‰ Ï~š6“Ãæ[ÃÀ€09/D—ó*“Ó;‰f• ÿë^Ò„õš*õ[㧀᫗(²y'ï†ÄϋɧÌ2¸ñÎ gµ?żæI̶œ{°Ôj3,²×ËÍŠ,»à$™Iâ.û$4jÙɈ†ñ7CqþÚà ­¿æ½Zƒ Î:egã méæŒäK4ØgŸ7Awòx}^À9Wq›#·+´3n¡ng3æç‰Òn¤¯e&ÓsxÍÊ} –µ§Á§ˆ ‡{¹×ºßË•2„ãÖ}Á½ x4‰w¶aêüVöþÜØ  öšÉ“nä…p¦„‡Û¥)ìÃòdû‹fý˼‰üw›kV{tà#g)^ŧõ˜ ‘pë¦2V¼6ƒ [K"&¹‡=qBþëinƒ²kΰyÅ Ô÷6‡Ã9WÑu¹ “ìåɼò[Oÿ #öþ=‹†î•ÎÆe îpGxö2‹jY\§Ñî—/Â6±Œé;²jè>¼™Vˆ»ögË‚ÊpõEڦȀÊê (/z£`e\öö|½uù8*Kßê|ch¾;E¿D Ás@mö4þªkNaì'­ÓœßF›ðtÖ/¶d½9DÅພÅ0‰ð!Ïj öØzØsYhÂþo9Nèÿ–Ÿ[àgÊF}⢳}*±½; ‰jËÇõØrì1³'®GA6Ú–³¹µ‘•¾Á”ûÜ…>L`¿Õä@UZ?´·òRá/w!@?Ö=åÐŽjMÒ䪀FD޼\] ¶»n³›þÕƒåËbÖß]„¶Ò‹;® øUñj’ ³^˜“?]™ÉmŒ­s+V¾µ¤»Öœ‚Ù{âc×t²VAž€%”}êN‚Ómð\à>xéú1 Ø/–cÎz 2ÿ\ßÀíõcØazßZø™?«z‹‡Þ_CGb³yÏ/âKV˜vùU‘¯äàèõü/>ñ$»'ô).ß–Ã.)À»Ÿ=Ðí¼%†Ü~ ž/2¸Í“v1sf?;ÄßôïˆAÞ.îq¶D ;>2Uð¯qÀ®§Y59z&èFÃúèh„‡×ÑBŸËQX0 Ò/Æòo3ƒ¿ØØŠ+øÎ9žÇGù9ÃìÍŸ© ¼ MýêÆŸ§÷1Xä×ÏîÆ?¸üZm™“rëXG‡F- ƒôŠ›fÀ½¥“`³²+]tN±ÎÕ†[ûðù‡  éÛ÷ïå6–º ZW(Ú­näÚÉ á§"!B­¦mR®Ôý¥ØDþëk¸²ÑÅ:ØúOœó*C^¼+Çöa8g¦ «%¥IÆša×ÙÔàÊ):o§4|Ûù—\š‹wWÊÐmmúç³4#|=ãå碃CÙ›oD” ¥£ñË{¨}ô t &3‘y¸`à*/+e℘ƒ„%c®…2µ¶ƒÔ½ñº„6LÛÜ€ÞÝ·Àcš.J›Î…[ q¥9+d -yì­á‡°Z4ký=H܃F}F3ÔïØ‹wcœYcgI6bê2N¬ç%öázu":JHgÕ0 oNbÔã‚`~ö=øtG?›ÀÿaSÚ9¦Ç–”£Î…rœ]Vʨ]œI­®5âG«9ì‘c “>\cØGŠd•Ø(†ï9Çè†J‘-}"¹ñ2doØÎq^¢DJxV“ïÛÉJG V~ž§" BóiÄþêbèÑÔ!ÊSᔯ?X,»_Î¥¡óŸ×¨})õô4po•”µ¬z:,cŒP´’Á r$11Š\šK3\rËKžÊtåàò¯ì‡ö\ðŒA—/´ÝËŠ.þÃàcfÐV3^/“…á¿ëy­fà’ “s¾j?”“©†Âòm`|+¼£ÖLø„¿”6’Çë XÁXÑRÄΑ*À(ÿP>ç†JùtVG«KÅɺÍip`I1RvEîK ²¦ê+ÿdç®<²ÅB#ÜÛÄéF—ð™]Mì&çqß*@®ÿ9›y®û¶@óˆ è4O&Zep9k3qAâê#CµÞÆçõ±òÖB4H\A] Ç™¶¢t’Çx Ìp¤âúSHtÙ+ÔUÜŠ‹—ȲÕwZQ(Kj[­¡ÃùhzYû¾­´&h)ñ)+Â.ëÒV%Lí¸ À{Q“ Hxqbõm<ŠÒ¶’äQýʉüøï>ç¾æî6Oê*]̼d¿aÖ]W`ù?2;­îá}Ž,|>WÏ$[ða|”'ÚŽIuw &+Âd)}ÊY¨Â™´u1Ý·æüwúè-Ø¡«I‹‹Ú°°îŠ~P%=kbˆEA›#áDL ¤µvÂ5 IzòR Ã÷¼ n³sêÂ@(íã}Ñ$öª|]±Ÿ5‹?ȶۦ2 õ•È¿ÓU -^%–dEÐ]íA°çä}愺)Y·¥ ꥩî`»Ý‚ÒÞ-ÜGŽÂ¬q­èõ:ƒ.:üߟ ¥µl qZ™Ë¾1±°ÿO^ 3òédÇÍßõáPÙ£GDtiüCÀ;IÆf×:Ì`Ïáó‰åZ;cJé˜3»ÆàxÛZº²*$t…èªòß½w¯+Ò^sÒ”ìH'Éôs‡óAjÁ~ë èçäÚ†û‰—QuiÞnÀ-q9øÇâOáRØ=ÚÏ-òq$–%('Éò_!}òbØ\a†‡Ú%铃6$ío8ž¿œ gg½GM‘a|ÓmBj]aC84ÛM ß(ÓO÷œÁSø$Æ^ЂÁ«)´wç ‹ùhN“áü†ö„¸6%å–ñPã ·rÇy¸Üÿ~§Í#£•`¼(®É °ÍÑ|láÚkÈçz oúš£ˆZ"³ÖM˜ö¨;Ò9XHc¾Ëí̋Ÿæ0õß2"hÚÄ tÈ'‚%ìŠq œL£:òà» &ýâÂ;“˜ù¹ÅêzDF¯™ÊþÖ‘§ò^žìr(|Ft¶3hÐu °”2&‡‚§¾.vXJÙo:çÚ9n– {«ëüDü70Zðšÿëø¶l´¢ñRÚ6ì‘0#S.ÆX\&òë½cžQæúÙhLßÁtn¸Êäuò11÷‡³xQqáhíK¹ ãñût6Þ˜ûîþŽƒ±SXº("›û87'‹Âá˜mðݧ;OAF?5½“Ë“ áL#˜›9¶§HØ;YÚcÒ„†ýg`OÔ¨NŸ‹{v<çþPø‚a¢ÓHß`0ÌyÚˆ/žæáarò'KƒŸS)üxp†(·NA—éüôãRXžl0ÿÑòxÚ¿l\ÿ ºÏm&IMf¢cPm“jˆðÿžhÄ”IÁ‡Uįr!±:¾„×é‘-)ܺ.6­ÕFKßE¨÷G>¹¼Šîž¼{ºÙ[Gà†;/ܳ/&®­¿Ù§ Y¡jCzzé8æåÌeìùÂaÙe^&LãñXö÷Ì1/_OqŽjVÁ<5\pLŠ–0Œ¢zèæeCÒ@?¾ +ÇUÂñp£;‘=5kFo¤O¶˜’”©½Ð­€³à¯Î?¸7S•t¿¾ ¦æp¯~ ûy¡ =} ÌúÃQþWò„ÿI¿nÔã ®Éd®þÙ\Æ x%äÌìÀŸâ5eöטÓmN:H›¡vÍ Úñz^ØiõÛJ™üÖ<çÁ¬ê»¯/óÒ#lóuŸ<]0„Bóayx%Û¡LŒ×Fàq‹¬qYÆüÜcF‡®ÂPô´ç¼eã§;OQšä gÉ{¦jg$êEÀ“EÐ÷ë1Z:À³ ס¿ÆF-/`÷ïQ¶®9dކǠ$âXÛ ðq lü⋉³ÁF2´² ª¥æìýo^öâ†C¬üé¯0p™Ÿºÿˆ›°¿òGäÅTbéó×h݇žx? dêÒÍOµ©å½AÆ__Øm:€æC<ÄÈŸÅ'‘éÐÚ I¹ñeÌfXÈmhÂ~ã!ÎêƒoàÚqòJBˆtˆìfæK’…¯'Qñ›GàÕƒ­tŠ´t)‚ñùñ§q'ªD_Å‹g#þ¦gl:ûË‹“Qê© èmØ]RÄN_ñ‹–ƒ#ß>ÁÊ«*È.Ò » y¨w÷ ¼=2ŸpŽMYÌ©;u¶ À×6òÇ ‹ðÈ–i´·"ɶʸ®PÃ:g×9ßЬÐežÌ„¡i90Ùâß÷­ue=Vh2ß:¸é½"D,û$Tü… øá`HÿëM²y›Bvåâ«,|û†{'‘Ó2ilLÅ*ò²ü2Œe¦â £,Ý)F¯½ŽB÷Öƒ¸b,˜ÚþcÇ^Âéöð1x†}é}ÜC÷FaòÛX¶s7Ù?Íž~™]+w3ÌlCR~à4´¶pJ?Ec‡s›s‹—2K¦Ã±Ü|x_ˆd/þýŠ›§Ž¯i }/¤kµšÌ—£ïìqFj(®ç!^wöÑ&SÉgÝë4æ–õ“É%erûâYð«5a¿ÏÇ7¸}©| ¤±Ö1ôtó$+ÌÈýǧ¶¨P³Û1ô],¼öYEûÇùkM€5­3›K_Ìœç£è^›ódY[+,Nð#ÝiMìRúrëW¶XÕï¿?KgÝs똗××7^¡îÑI\lAEÛÁhÇZµ‡‹S΢I?)< ¥æÐ˜iªÔµ¡‚Qà4á¦rvýä²9 õÒÝL¤ÿhÒ3í¯„†ï¼àgñ(þ°Táõúa²âëž³ä‡ Ëi~‚Ìv/F™Ùܬ‹¿1çÅC°™œ1­õo ¡’YØ¢ WÔjÙ²ó-pç°&ž}ôF·ÆÔO6ð6é$×Éà˜J?Ĉ›|ä{·¸É£õåÃxꀼø• šNâ,l+k =Ѷ± 6Õ²¬0y—S’‹¼±³ªšé±º± 0Ñ^™þ¬ cêåáÕ^c¶:à3Ä©ÐOFéØ^š [ ìéÇÄ>|§Þ¹Ã¡ì‡ÅOšÃTí8ð‰…°òm4¾g½{Q•‘V¸ŽÞ£ÜU¾÷Ñ*)–™µsœ?ΊÂþë+ðÃò¯XÀžãî{Ø~ý;fŽòâùNyÜœ†xÌTqâýWÌÀatma„ÏUпAnd}î2æ@Ø-üð#ž|kw"Ÿ×u³'¾\g:ùæÑ_Œr硞‡Âႜ6®@Ùßç`}¢=%]† §/aZ©5»|Æ}6éÁ|ö¨•“öqÆÑÏïE¸§_¶rÚÂW£ß-àÍVe"t:qJËEˆ— ”ä<©<…€ô©²=<ÎQùM¥‰÷;ˆ¥¦$<î=|™¾’F¿ˆ3#Ã9÷%Ä©P?yÄ?ˆKœÚÀà«*¹Äs›¬ÞCIüW8>À–ìu_ïIfϾ¸…w+0IyLöñLè:0á{1X×!Ά.Ô"ewÝñwO˜Z¼¥ß¨Ttî]³¤9S}Iuº=³6c¹9ï1ô”Óåsðìý90Jíß¡TM€Û8«*øL‰à…Vh}Ú÷Ÿ ÝFüè²>Þ‚N¾‚ï©BÔ¬# ’ÎKƒo¯('$ã Û¤Á\Д'ÙMãzõÕ3fó47h Ê‹{JÙþ¬·˜U3™dé”ÃÆxQ*¹J ÆL?2Ë4%Or{Þt²Iѳ·|‡Ï´é¢ÂØñ’Â1ögCÈML«yÆ]¸V™<á;ȰúZ$zšTíš1QÿoÎa΋¿eôü…hÆþ*:œoEØ.òM&Šö~Œ% º~¸H…ÐAçlêµA‡NvÌÀ Y'|u{2Õíp$÷t:p4%m³¶²áb019Μ:{’¿’ÀÁCJÔÈø*óBª.If á«gÐý¬n©[ƒ‰þß°”Ù@FÏ/Áª¦Hœ²d#ýÔt†ª¬_Ó°&¬ ÌUó q• ¶ RÚ)O5Rãï¦ÄõåvÊ`šukbõÝòˆîjœBÔ¦~F ¤ÊM8nc~(Ñõ $—©3mv¿¹#WåQï¡ &Ï žˆÿþOë‘K§Ã÷¹ßi*b;^ÛnOÆn= :-”Ÿl>þ­ÊŠ`õ4ò+ÛôVm#¹yÁ¸ù¤©Ï^ ?\)N.¿A»¤¯¤9IÒ4{¯8éž6•~3¾Í$õ,§©r4ážFg$áÜ`K¬Ð¾‰¶ÔëJ ¨!âUv¢â¤pÖ¦óT2(dËpd5âÛpXr„ŸÉ/Æáð§ô%kðKÞgè0ÒɉlùIî$þǰF}}-Cö/y ýßC¡[Í‹(vTÁÕøæ üOD|tá xüâiå¹d±ÜM&Xú·ß»qž›*üSÙ ÿ_½¶Œ©5®ge{´˜l­vß\¸é.C\v+Â~7)"ź ½¼ ì·QcŸ€[ú‘äØöÏQÌöûK§bÌ"B5^_ÄTI5L4T‡p2ÚB›Ë¶—¿‡Xãa“¿Î8ƒíÞçÙUñ)ÌÛeãÏhud€t<=Ús~ˆòRïÓ‰Úý[8²×çà™^¶p›,V 0ä'ç,¶4¦®§À`v)xt°R2àÄíWÉÝŸKIÑŠOL³ó4ªë N³-h¡b$šÿ¨a&sÖ§`Ò‘­`øåíÄþWäÕB(•)¶~^r–Æ[m Æg…HN#KÍÍÀß`rt5í. ö¯ÄO’寯ßcÞ…d3=¾®¨¹Š‹óN]O×Ïö]˜ýôM·MEÛd£J4j•m#¿WG‘;é·Ë›Ê¶Ã¯LÚL'©/&6¼döÆM¢b¢ äìŸpæÔêà†+1,ýžœB½Ü·Àßva¢ªV€93ÔŸ¥Jü×áš?ö¸?ú+»Ž#LĪì)ïvC±ô…î¬v^Ý]¥ÓëÓ %'ŒÏ†â–ÕA …`ÙßX"±ä>–KæMÄ«ë::YëqC¥®4=ª` bÃKˆc’;+èSŠÿVzÑOù„ôÏ !âÍhU^:è½l Ì~YÚ möe× gú}î6£ÔrÌ%Š1÷@ä¥$É2±°Cñ/£¡sûÛ¾Z¿ôè¢7î¦cC=jR€.¨IÏ›“Ìñ1ú°->?~‰‚ãa®Œ=ݦîÕû8›;1*v+=q—Ý9Ô†K½auö ü1\Ålùy—ÑqÓ!îCÝVî5ÄAf:oŸÃk)¿ n6ñó}O–Ií÷ϳ÷‹Løÿ7¯ }â Òlœä~âþjÒѺ‡`px1éK€Þ‡vôП$fï³6è©ÎC-Aê÷}9võ5Àá…VTÐÈפ¿ƒë¦tæ›ppæ¯ãO®`'ù“òð‰Ÿ2}©ú ãÄ"YÞ18¤ÞÈÜ)Ó!>>ýLÀצ﷩@-®ÈÂFçk7+X¸_5>‡ñ3`x>¥y°‘í7Á NãæT¢6œ¦½)ã8Ÿq‡'É‚þPòp-â&y)¸®jBÈSn<ž=´GÙJÝ>à¥÷*àNŸ8yøÄ|¢ÿéÕ¼SøÄÜa«Ž‘¶½Û0`¾%»;u3ùÚÇ9˜!™´®™•ݳÝ.£”r)9Ž~_HåT·NÔ?Åìã#š¤+æMpâ'ž{ØB=Uâè̃ys÷s~ýãƒ{Ï'Quû¸ez™¬Ó&…QŸkøøÍ؀þDú\¸Ô&@ŤTfØ·>Úä¿ñ[¬öÓà ‰Ë¬WÔþö.Àʳ´QÛ™<ÚR‰[L£ úç¾:NÎ#-'OÃÙ.>Ú6 DÆ‚'¡ež#5}zIyñ’MàÕÿXÅ=·ñ›b*ÝÇŽBxˆ#(ê® ÀôRþÜò6€*èyà œ”¤‡ìó7gˆŽ\"¬«„êCOqù›42Ãâöå§Â†T… û¾å€YTê¬Ë€£ËXfD;–sXS‡Ü¬Ò¡× ½1ü{Fz2ù( FzòÒ×Çq÷ç]ø´6Ž×»’¾^§]"Øù.bÈI.£±osòNøMEœoî‡ÿ}Äîë ÙaVÃêø@ºR-2œ¡À FCöŸGäÏ%Ë¢†a…þ ˜–ÏàBS+ÂßVÀÇü>ùçæ†s>Õ|A³Èxô›¼‰=â´ŒÌä1¤Îº'àCè<|€<>ŒOè+VÒa1t^º™cLÃòr,"ÙÌ?̓ä—Ú4ß›ʬ\{r‚ÿ… ›¯3X'FÙ¡˜yph âÕ³!i> çÖchº|¶g^¬cè.U ™»LHÀgvz;Ž˜à¼öÝ$¹ä Ø×TÂS÷4î®éÙðÏ»÷ž:Ϻ µÏu™‰KH€ñ1¨Â)$ÖJ°öÕ´ÙÞÏ~9k…"áYli¹˜ÈÎ"™7Ÿ1Ž;؆-Õ»è‚M‰8òBLŒ#éÀ¡9䊃.ybûâÎbüýwL«Ã ðTA+6UᆹXù{®IýÁ]á¾Xùÿ¸zbm{zO²ÄM–1ª<Fÿ"üˆ5ÿÙVÀî#ÙëÙÏÎUxn•,Éœ+„ñ »Ri;³B‚ÛòÖ_z²ËÿÎwµ¦©ÔÛ÷ =ÔÌKò×0é×'“.ý’ŸDæ<†ê¬\dŠ*Ñ™©GÓ`GrÞà7žÞY×ë.aɪ,NæqÌ¥*Ÿ%ú$n† ÙØVá;a¸h6,•PfÜW•àÀäÉãùQ…†ód)÷ÃtÛgÒóæð¡~<ôq Ý„ ¡ÿÑu¨“9Í•YÙ1>?±áÈ6:¶ƒ‹ 'LèeCjþ¦6•{ãÈ|Mˆ½N~tþÀ+—pP ÜŒ{wÙÓõNéØC8U)N'oO¶¾úâ=·ú•9ùzþ »©~= zw•?·k]˜Á/ ê´ÔÏ:Å6(Le½†¨•f3êo€Q­M´ÿw0{!% ; Zƒ§“;×Ò÷ç–“>ÇX²ŸÆþCÇkéøXrÖó¸ÜuÓeñìój6žï»]=¶…°a›Ð>m?Þ²xëþŒ ù²ºTŸ?™ÝëŠM]ýsÌ“Ùî±T(}q7¬«ƒ¸÷¹øçÙ-â«9f36tðBp¾ÿ¦«é¸ùH ÜçËœ°ÿW|”‡ ÒÐA^ÊU=Ž#9„ºÿži:SÃÏŠd~L"6ä³:sGÁégèà ߘô%gÐPVä4^²QžÛáQ6—û(›ƒ1Í0œÂO×·\Ù‘¿ìŠ15ê–x[çAÅÁ5¸s¾i*—%Rñ4É&´ÁÁl.M­^G“4´àË÷_ÌÛì—àß+ŽõN]œ/Þ÷#_$0Ÿ¦ñã‡ÐZôõ~‡´KŸú½r‚æv–¹þ×”ìrß'üá†m’g£ïʰO¡ê÷?X–þÅNÚÒ°uÊäÝã?¸é‚2èoÇgmê´Mm"þ“´¦«EàÛ˜3N™•Ãô\ÊÄÒ¨ðX_Š3ïKÒžAX¹4,b­èNE.̾½ ŽÊÕ°C 9›z„ÉÆè"àÏmæ~;C[*Ð{ ÚTçÆ›RÉÉÙ?†׬ÿœŸ‡òÜá°ÀPhn@E:w‘©˜´šìÊÛÇ”_? ãŸÁ¢¯n ÷\“ÌPá!õÇ®à¿ÔÅ4J›Ì—v`E/¡1uÒôìA;Ú¬þ—U¼ 4Ï…X£PV17·pÄÌTI“먎ЃM2C8rLž>ŠXIª 3iþæZve¿,H p¨´UЄÿO5»ƒq[[ÓÈOÚΛ«U‚•e)È|X©­ô^` ôïÚ~ÿ@ëÂLì='MJÕÿÂиW‰…³!ÐÔΠÌ)t¿Œme¨é®4xqa:Õ]ZĤy,&aå<ÇÑd-­3èÁ=^õqçDqª‰8¾¿mBOïÀ§3ü!‰s•«÷@³í0ü¡L;¢®áA¹ûl}ºùZÿ‹Q/ÞD –{`Í_Âûê›|ûÞÅtr¦nþûŒæ¢AxÑév6݆XÓJ¸¾ÃY²â3ý—*9¥pcF2ò›'ôߦSÊгԉ½°‰floÁç,Ê=Põ™q|µWû©cjÕ%Xr§WÇ@„äbürp/{lÉSîcNÔ˜Aâ¹¶üÐJl»h ¹—Õ÷s=ü¬X‰¡Æêôy~ÆDü¶×b^x=»{³0Q/‘&[õ½±éìøµV˜ã;\…·øN2j’½ ƒ‹¦Û©ô…a,jLb>t´²6„¿÷ ü]äÜæTx‡ÂÄ¥Ö_¤úàËø«ðoŸøÂâÎ4kN»ý[3x|Фê?íÙ3Û !PK‚ž*eó…R1;r?ûý•$DðbôY\×UÃD^_B~[ðyØúëRðdÞF¢òS‰¬ÖYŽ“/Bï ŠÒ'¿âŸ§²p2Qìš¡B£Xsšÿ§w]5‡©Î9ð<Û }zaÂÿü¦Ìã_RÌß_Ù»¦b¾Ö16óÔMPïÏ`[EäÄM@Þmݵ 4.ȇý¡J7쟫Måé;Ù$b3¤õàè—ô«x†—XCüy¿••»ZÄŒ½Ûj½[ɧO^¨—hAü±•OÂñŠåJö°÷¦èæIì\æËZUóE†¹â˜Æè*ˆP‘¢FtW<"iÝpMKsQwÈ®‡²u×¹ô’ñg*›Qy8…cî£]è*z’ÑÞÔø§òЇ¼»`Ęd:°12O3É „iúçŒi¶`%¶_…´Rt¯m!f­êƒO» q—f†ÚÉñMžF¿ÎƒŒÂ(òÍT¿¾a*7m:ý‘%EXÈ*Ùræë-Kúús2s$cz«[SàíÍVÖU.”>e–nÚÌÝÝwBkºXyæY½àbŠrnl¯È1„"~³â¡:‘êuk»a„7#·œ q&qDËî(I•HB±©ŠŒd+ìœcN¯fàpü< #^¡¯2zÃOÂÖ2†9‹'òßç_¹õÙ¦Ãìi«ñÚfFËùÈYûúÆ,aø|=Ùu-ðöÄ"¬ÿ°˜6‰-ÇÁè%=}1²ÚM|BGò9ø}´í¤4ˆðÖpÞ2—æ·£è¦cظü,Þ]ö{é\ZÞÝË0ª§ñ†W£cbÊ—ÜŒ ,!$¤ m?o#«ë3ÑkY~¸p®òÉR—õ×ÙôÎL˜ì½ÜphŽî¾Q Ó~TÃËõrx'ï(Þýµg¯žE­„ÖƒÅüë°ÃœC·õÊ WM›µ¹r,˜¼8 BŠíЗVa…’pö–'{¤{"ÿ¿ ¬ÀwŠøõæ^æ óÝî Œ¡â%ÀSV‚=N70+ëî÷{/r×Ó7÷¦n]Þl³þUgžI‰Ü¨¯˜=§üg:à ,£mÒŠÉÛ‚à¡M.6[E‹èˆëTØô, ~\DÜ “‘U[HYF*Ð14ò¶69¹- f9!Ÿ§>ÍAû{ëèÀ³ùðcgÜû‘A‡2§RÞ5ŠäGƒ#=¥… 6²†wÃ(ɇ蜲 ¯ ?û»`NÛK¦°ë ~äªÓîÄË̹ùÆHyèí$VJ%qÓ Fqû Æš“‡Î­3™Ñ£ÓaÅ,MTLá2ù¢!už$Ô:æqG& Ý ~`_^m‰PÕ »†W"™ ›•d1D£gœÉ„!½ì¯c'))šð?#úŠqÜÒ ¡…“h»D {kÓ1šä+A³Ý´Ùcù‘ð“&“3ÜÙKÿîƒ@ud{›f™×½Òo¦;ÿwÎ2}覆v‚åV]z£f?6ËlÄ[¹ÂðfùL¦|ì#¨mç¡Gf­t™u4Mµƒò^£¥â(>îˆYu2°0£ä7ŠS¹ÆßÌòzØè2‡,âI÷EPÝõŒ‰¯ig=…í—^â<¡ù$67.UÒžsiç›ä‘Œß¿0 _¿qwùаÝ3ÙoLJu èEîáP] 'äüFÜð³JÄ®ÂÇík'ü¿¦Ù ”>Ì"¬ÜBsæã9~Z¡F®>¹óê__nHªG‚þ¿Dxu“ý=ÒÊ>ÑÁFF‚:L]‚òÐ_a$ù|Ç5ô‰CfÓqE˜áø›’kÁ4Œ¾‡wÕŠäOÕb¢ö”ÙWxdœWä°ƒ+ÄȪ(qªéYˆ ÅÛ×éL#ƒ,LûâhöÉìÏÖ¥ª¦¸Ê„ƒócyNσ€{ dÎú—¸Ìéû¦bK8ržÜÁ}?/±É—X‰3‘˜>d|%ÈþJ˜µþ« ïÅòßËÇ—ZÆäçÙ¿V·aÅXðÄþï†j/šwœÕyÒŒ–-†´&" co«Ã…¦O¬˜g«ÄÐ×çǵ¸£§hæ”L÷»Éy¸Æä"Þuy) XþÉKql4œUØâkL¦Áº5BtÖ”Aô,}€Rþrôñªu´èP DÏoÀ“ÁqÔäžÜÒòc×½Œ!ÿú^3wÅ‹òWã÷\1:›ýÆ-8F·­G›3„Yü¶„žS©£3ÿ;Cì-v°±7Â@Ah/œ¿\ò®¾´ûè/}ª“¥áòÐaBäSqù¡l™Z#´¨ ¯E&1™ºÄç#Î(S&Eü~þÇÑ•ÜøwÀ~œM¿„kÒ9!ݨJ‚àËH,]çñ¨¥s×í¡©¿@,¹Fo§ ±Ù¼¦d‰o!û&þ7é3`ŦKý¢œþ¢2Îø\ض´„á;YD|m?â‚3Kéøú­¹ÚÑ6««¬¦ù#<ß u×?dçï‹‚‚“`xž"1~½Œúé’ò篰¹)—ŽÛ4Qÿ…2-Q©t=挑ðJ âŒFdå‚hÀ8R¶[ÓÌå(z”‡.vùH†ŽŠ’c\®ªödÒSœ@6Xް^ÿÎ@½zãŽTìM k+øË?>€m'Çs8ÄY·fù¸2•ÊKT°Žvžž4î“MPªø.ì[ݪütC¤=]ÿ8†œ˜~“ë[Ï-\´.ŽÖï~Àμ؆[‹ˆ°Q$ ­Ç²»ý çªÉåÕs"%yyðߙ՟bŒ`ø²Ñ^ÚȘ}®£º=·éÏk“èäý‹‰uî úÕ‰Êx£ŽJ1hÌЛˆÿj×sØ÷Rï& H†©à©gÛrÒÙÔa"8W–/ºLAhŽ7yR«†4Ü )C¥ÎÁ’ºÓ¨½ì¬È*þM+Éz‹dfhè2ÊÎ:Ig ìet¬ȳp-:mõ[,¦õÜîTuÚ•ëA¸vü Îx²L‚H9õáƒ?ûQšYJ&ëþÆG›]éñ{ADª-†T­Ô «·Þ#é»ÉÕ ¤rÇyÖæ˜CÂshaÛháI'ªÔÌ®:G\õ»!:$¸»’PsÁþö„^û'gçì@—ü;A±iÌ2ãàÕ*7¨aƒ'âÿy÷jNì•ɰ†ùȘHÒ#úzÃ[öæ:öá± öbÃ,"›+‚¡rîÌE-rcìÌhgäg­Eý6òâ÷Éð4=ÿ ¼b³ŽCúò UÒ ‚¦dšîöà 0R ‚r»™tͲÊ:¼dW*›a¤ƒ:M!"i7Àï¶$«V0ꔇnO>†›MnÂÆe‹èØüfVnú\îÛ‚F0ñ‹5ݱH–^硯^wÃáÅ-¸¼k!ÉK>J‹žF co£lìæ›®Ç‘yÙÈD…·KÙ¿têýÀ»RôÀ{ïjø6S…;çsQï ü=­ÄËpƒj ”dzpaM.ù¾ANÄõZ6ªîwý¯ç˜cO±O c‰âéähÏVú³â!›·œ—˜§ÄãJÓuðØš‡»¼ âj©÷ªô^7Æ ©‘Õ+„H‘MíšÉÖÚM£ÿþJRÙëÿûšaðÒ£sCœÞ§FOs‡ŸëPõLà¨=fHU0×’~2É…îdrä\ÓQŒ*±_`[ÕT4ìèÅ+ð¾×ásÕédÊ£/0s”Ü0(;]‘-óàJQ?óÖ{5TðUBŸù]\ÝÄ\’(…“äà£Ä‰ø¿–Xs<¾²N¶¢ló6öã\nƒ_âÏx9 œ!BäÌQ~ËiìX× +Zsh›ÝQŒôêÁr¼Øô­òÀ}÷Q£ãáôrY2ª–4Wy1::¹ÝZ¼$™A=¢µ‰îéÐØì‚{Wâš§±ð­ï2¦ŽígÄÉŒ­ßÑîq^Ù·Š±$)Î5¥ï/˜Á[éÅn :¿ä$6‘{$¶ç#Þ9ÎV®£ìŠÇ\ó}5ÐxG›¦®\|Ï:Ù®¢åtmV.÷ô¤4Ì.VÇÏÈŽ·“=sÿá‹gôÐ5ó ûëÆµˆkÝÂÇ(sú„Qg/°É5Ðú,øµfÑïÌ2*¶ú<Ì/ˆBý¸-g5±ØÓÅ »ÿã¨jI¢ÁÈFºØã,Ý}"”–þ‰•xÀœ!_ÄZ°Åâ,{¤ð ³Í0Žö,EÉ#sa^b$q>³š 9ÚS«Z>лºž(÷¯ 'ŸŒçìó-ô]b ð‹‹Ñî:¤ä?ó·Qdb©öФ’3¨äÉ ˆnÀŸ<꺄,øû÷{(ÂÛ_,muÓ#Šúf-*wâü¸hïIÝÅ@%\Žp‚?€ßX Ôl×"GL i OfVÆÐÏhx¤¶2¯ŸëìIX- UƒÒ°A°(£&GÂ*ó‚äÝeäz½žØõ{®xâóìùEÞ`uáØun%z3)ô ¼Õ=À(r)•íz;O#P ¥EElí×QæµVì„ÿS‹Øj›¹Ç¾ÏF­ÕM ’ÇÀÖÕ7É’Ì/_»­ì±²Yq•)ô¡/g±o|¹Õ e ,ÓÁ÷BsZQ¤D–vÝ Žïß³¿,—'`ý3'š5ÅÚèÈÑÇ;b]‹/Y{k/쪸Œï]ñüÂçàÔ âŽf䡌"NŸ’Áî0›øüs¦1͓茯åàÀ#O>j‚[~š%_gî¸Ç’P¿!«ÇsMÈÕ0®åФùÂt›½*YÛýOo¨fëKIî{>ðý IƒLQ¿!tª^O¾X[‘·-'Aÿàõ û'%è î……w#¨ø…µ´–šsÂM ˜…_»ÈɸºòŒ¬ôCæÂ€÷Ï@os1Ñë³&²9×JõðñhuQö¤³<‰c’:ܶcl,´‰ìàørx UsçäHŸÆT3½=ga·aS•A+ ¾âU‡š†-|àü8¥ï“ñûèìé1ØþÙ~$Ä“—²p]² WðųÊ]tšÜ9HŽ$?/>f§(Ç‘?guÈ'½H*Û&…ó?&‘×EHð#`N­±¡ŽS–PŸj Ñ+ÂÞ™†dç6æ´ÐE­¡lË þ7Pü™ù·!âø“{îG0únáÙœÏ|¬à¡¿Üêa/_ÒzOú»;‘Í埄Kt%hî¨#Iÿrš>ñçÒÄÇ—iTÝ Z}þ>ÍåC±Ép"­™ð¾rfŽÅ¯§¹üçp~Àÿ#êÍãjú¾ÿñ&š4()š5 ™T¤î^; %¥"•PÈižg**¥ÒM„Ô=kKÈL‘±„2eóëõ}<~ïÏ÷q‡³ÏÙ{µ×ZÏç>ÏǾ霱«9kÆT5†¸\,õšA7¾ÊdiK¦²Ö±îÔ2R¼¿¯:]¡ Ón2›ÐM¬ÕQ”›¯E‡¸vié²åÍ+\8éŒÂtfÜeA/;\k·Ýù¦ ¶{úlTeŽÆ‹iÛ-–ç7 ¼˜EG¯C†îÃÑGnP>Æådkÿç¡ë 0ßö=wuÜ=,â9ì’^›7@oEL÷+àŽkéRe2ÈWÀÒ”œ×ƒ«dËÇàZFþ¢ù^_n†ÿ-ð=Á𾑗ÅÝÇÞæõìö¯'o†ä%*ôøegÒ6ö(ÜÔ£õÎãÙ‘ÖßP³¶rŸëƒ©eÈ6µBÄ&)V‘Hnn˜ŒQ¢DUWŸ­ÙÑB¼l á^Ùašñdð¡-Ò/ÖÃ霨¦Êÿ€J· Ñõ¦™üÔ&‰¥1™[áw{·èùx4v¦ÏVøÀš‰þÜ·ì5ÿã¿«ÿ‰‘_‡÷’Ðþ}¬¢[“É•¦“”ŒP²¸ÂÝE“÷cº‡R±|è>Ø7fôû žñÔm\97ŸZ½Qá¶øK¯YÉxÍw=^˜'6¦²£ê±“GØT£J®ÑSnU}…yq¯±«ýÑ ·÷Ó_~SØéàTfüg)ív}ŽóªªAd{i•]CÏhu3ŸUÌë©9{e°•Úü=„÷/{ cóIÎò)™ÒÞ¥f¬^l7Ni²wRÌBþŠÂYëßü‚UëIBt!]pÊ•úFOƒu^D?¾™› ¤n1%öÇ'Âíÿßþ¸wYÜ npÍbtk#Î!Ÿ!¯e[a†ééo!Lî+<_Cg¿QÅ»¥è¥ñ –ö Ñ—ù'Ðÿž'Fª¾@aI[òà`5:ºåý·?toU"ÏåƒE²-õí¨ÃLËç8úš#¸][ÔÑíõ#ÔŽ¼‰'¦&`ÅÖ„QÜ—Ï{¼²ͼÓÀÆP›zŸGÇ|úD—©bÂq{:‹¨Á÷™Ø¯UÏ«¸~Ý-Ã颦PBz Ú$”ën3æþ\òÇãÅûÈ‚£Ïù²"a]½Y´ òS²ßÉzŸŽÖÅÂÜЇÁÿÍÿ‹f“!ZØ’+Èá‘uSnéÁðK7‚†>]Ãuý|@„ÄÕ¸c;Ù@›!ýõó'´›o¥åV‚ø°í(žQgŸ\ÑÍòïh[WXf—íO¡Bö»Y›ÀéL‘g ÎòŸÏ/dÏ–@ús><Š¿ÈÿçDƲ®ÊZÐ ËÕüYG“Ö:“ëÛa†ò1”œB¤à¬A{:ÁI6Já¬ð<8{î¾9Н÷ÅÃM£|£üôh±ŠcSû/à‚¬l(ýêF·'%A]K÷­í,\`FpCÖœýÌ­¡·d|ìH%ɶþÿO[ù ~ÌkBËûÖ˜^°œÝ>Åê^ʲ_ªb,êL Ü»®+»9ËéKp¿Él:fÖø–x‚¹‹X2±=y¹ë2¼QO¦ïŸs­öÐqZ9tó+)jc^‚g4cŸm,ýOŸœpº·<€)k÷§Ýêô½0$ß Ã±Nnì•“:+kêÎâù‘½Ü)ƒËŒ˜Y\.ò\ã¢À»uM¬®,†)n) O²yÔ~N9Ê‹Ÿ¢ûömçœ|f²ø¡5¸Eã)©õÐaºç•Y~d(yu"›Õéäq•#T\N¦ìbn ›îñûü×r[.”ï¨ç’±ÓÍÓ¨ö_w–]"N×}«%*éôþÚ…à³}9Ø€‹ªÏ²I'YÓ1ŠfŸ%¨úxc´)˜LMîuqF»iUi½¶Ö6ÆÀÍ*¦b½¯¥GQÿÔ,ޥɳ™ÍÁ›P- Iz¿…KÕR´ÿRêÏöÅâ®N²þ—47¨Ž‚½_É’£zD ¯ y¢¹|«J â ~~T«²åÓÎÁ¶½ç!¥«Œ§ük=Uº\¥õiŸGÛ÷~ Ìщ`_°<žð¿üw½]¶}B‰KîTÇ;‚%Ê/¦wâ'±ç?ÓY†…;ŽÝ—Ïaf3žØ÷™ö.\[:‹ž¯x Ú÷êÑí`?; (Á„fΦUÑÓXÈÒiÃÇèÛTUº«ZŸ™·>‰‹™÷ç4_¬Ou ràµ}4‡NÙ'†ÈÑòË\Z½l¾ ‚*>7¸™ó®b~VUL5ÄÃåÕØº~ÆÿÛ3:ô0(½ÜÍtº®ÀÎŒõ°èì|æî˜ /ÿ­¡Ñ+~“­ºl±¡›{"àâÕ^n–~1;±NÏmÉ¡sƲ×jv,fçJî‹Ü$ÞSïãÈ ;þÏþÀ_§ù¹ì ö )sNã³÷ˆ³˜»7ÑeæØ`úãâ.@ZïIX5÷~)Àª”xÚÝ_ÃfÃsü2Ï^ÕXB=on+fcüŠ4Øg˜œïÀ¬ò'Žþþ’÷ÐCôCbióžQ‹5¸y<î”|.Î~iÊæV„ƒÍ«-˜½T“–H˜Ð§ªßÏÑOM[7eø,>ŽîŒaÿíã,âðì+Šàu¦µÏGÎS‹MKý ámÖddE,våa×TovBI’Pº-wìgªyãÿïù§þß6ax!öŒ{|oV~oáíLƒ¡[*|vEŸ†·2ÐÔ®â N¸Â¢¥š(<•樢N…ø~î€óçŽ È€#K²ÒXs²â7GVœ_óœ÷HÒ…ÎÎ'Ù™­@–I‘Ó”ÐéR Hwã$…#Ĭþ!ùêŠm¡0P_‘)ŸdŽs™W·¡f²(u{m@×ö»`‰Àu\®cÈŒÃ!.Àl¢R©î}3ö/@ ΙÀ‰9“˜ã÷z9û&œz@6ü«CGke¶gbÍWecÞN†P±AÔZójòàuÙ›ÿå¿lã:î¾’=dVšÃçÞmL«ì9žse÷ž—ݾü#´•;q›ËpHzÅÕo³€Êo©p=´j6^äÊÚtˆ²ÑyïËÔE/@Wt<5œüÂ*Xø*É8²}´ ¶½Ë66é³I/×Ññ‡¢éµÓNXÕ?F3oÁ»D%™­Ê t Hàf®^Šß&ÂÙësÙ{¦¶÷ë±x™SxA¦æÌÌ¿ÍÞåpG—…aÊoYY³‘œ™ªJ÷OÇ/ç ë˜-žÓÉ?Ë X­y~ÂuÞ«åy®³DQèƒ –I|7±ÿ­m¿¤oÔ€°ÓJìfE¹£#@kÖ"4~àJíK˜ØÑ€EöTbq:?¢2¶¼€*AYª>W Ï}6ƒ‘ËMaîegpP~+:†“ a‡Éú´µä³ÃyèÔZ¦VÐu mJ~Áþ±½Ü‹¥ ¼§×aÐ)óÄIýÒã0¡Üç¿c óuÜßf‰°³&A:6N ÞŸ½ Pf ßøiFWàjCÆ™}»ñŸ6>«có¦èr{ Î1 Ekî›va1N‡öQ>òžtŸ&dѾßé<²ÒP÷öã"a|£Å;ÿÇfÃŒž즹ÝRRgÆÆ•·Q£¥V„‚šçsIÞIWucb ÇþÓg¾?ŠMý-`õé|{¥^ñ°3á!‰^;“>¹“ÇÙ [Q¯K0X`>®Òi!F'€sH2>Ÿ“úO"à~s(Œæ˜ì•Í-_ @º-Yß3Q°Ú§C'|ÊY¹F¼°ø5šù,  ë’‘›×Ìb½çf:FâB‘zØç¾¿Vç ±² ›”~€^ÛTÕ²ìë5ˆ>þ š-æ[Ǥ´XÎd_5(Uœy ÉÿÅ’QÙltu/›³®Ù©ôuIêeeÎÜÑÁöO|.ÞµJ<Õ«Ú”·¤Üu’¹ï:6øÝm;Þûó}åXÈtbí32BK ¥Ô€uÌ|ŽŸ×ÐO‚éLt÷HKÒ`yIJ¬»û/ä^_‚OjNÁÖnÞÖ=àaôö……Ì÷Pþ±ïÖ^W"Q(è ȧ]--2¥á&èþƒ¶¿kY_€"½=ø²“pt pz…Ë(—c1ï×a6õ"£®®Ü€W2&¬¨ãí’u¦‡Œ(oü¸¹×¦‰™èa|%²þg¿\«-$$ÅãåòèVYõ—ûа0—8´4qw¢77/ðáÞ9-¢›3›‰Z´ +àµ‹Š¬¿ÄŒŠ+ ’‘Û%¼>Õ(˜¯^‰wß™l§Æ µØŸµò¸L#¦Š¾ó¿øö³]¶s6<ÏšâJáìù<°¨ Ò Ã§ë˜{Xf}OãQ­Á){¸ˆ¡rÜwc"=ÿ!³¶ÆÐÅ>ÝØ}`Ì$‚~ò!°X­ÁŸ¼ÿ(ú/OÅý’ `^³=óù)„þÃ=œ-1àK~œÝòäí¥•œon:/ßóªM= õü/ÿu©æ‘fÛx!S $…h®À(ÞÌ‘¦ Ri„Ÿ<5رƒ„* ±˜-ôر‹*È$ಣ<9ûVÐ/B£VŸ¿Ùµ·7 æ ÷9×5†~Jè#kU¼¬ñ6‹ÑüLçg1¶þ”¡ $‰ØÔ\ÈÙ–Êôn–s/×m©dWgL ëú€‰—ý Û{Ž ÙÐešeüõM•ðuuVΟie±n±8Ο2GÏIÁØp®‰ü§ÉŽ¿FêZÀøK©VzÏ¹Š™ƒˆîm’øë<'·¿[¤|ÿ‡ÿ\§F⃥m<¾Ž=¤4{©ê¿¸ÊÕäÚàg-1fêiFÇ„É3½°Û|V¿‚I} BŸ úLž<µÞ;Ð ÛW§Î·uÙø+¸áðEzÇA‘†|’¡ãÄü¹OG粫vÙ‡{¿q‡õTê\6™©®x;Œ{Qéx(ñ0¤ƒ¹ÔCñ<ÜkÊ|”S˜©ãêÕq*ú}€3Ø¢ë,:Ç3C$úHçÑß\¦¾,Í™3Y B™ÓA/9²^X gãèTY5æiòÄ}v—:ƒþ–ЍP[‹Â*ë¹Û÷Ò`ƒ Ùÿü_A¥qNª$ˆ´v‘+âÓ éËJ~w§53jlƒ%ÁàòÔr£ªIάh0Q3³Ó2±HÃÛî¢E#8 ¶WÍ{g&Ç®$ÈâçZ*»b ôÝÞv[þïk€ÿ4 KpY7-™HP,:}ŽcŒé¢iŽã¾¶œ6ŠGòÀƉ'çÎ=Å+¢]-/eaùD_ÌŒ ÿÀj1q6”q™“‹Hà†þ6€u6í=L¾zšrïÆüÂ°¿"z.,b’—ÁSM Œƒ~`´Ñ$ÐUgù y`º œLÎÓÆäö\þöËÿùÐzg–Øúã©üï¼sÙïáD˜=ý/äíýI¾4߇?‘I¦Ø#4®¹ÍéT,ƒ>£"î]ˆ,IȆ°Âjþ›W¡¨D}Ù°†+´+Ñ$il‡òwœV'z@ß^þ:bÂŽ<ûIÜBV°‰W·€ìx|× E%tEiqª³ï¦¬ÊŸ¹f.Æ4ß4œ± ÄÞdCú2YØu?e ûÂ*œ}Ú îé´Ú¸I#³~…Z&Ï~jGg%Yú­„A঱0&¶bÖéРãØòŸ×9=é\šÝ”jýJïh;ùÿÏ~pì%Só†$è=vÎ,Õ½…îÁIl‡å…XÔ(~<¨{áú̵ÏÅn?‘£·Sš ócœ?ªD¦`¸ƒ 3ôËe+#Ðó¦)$P=¿´NÀkÄ39‘{/q;Óm±Í+¿îûph¶/™ñ­üÓàP­4gv{¤¨æ¬L˜¿/ŒrG4N›²ô-–KnâÍU;‡róqó° ­_ZEj÷þó,I¶r¶2ݲ'þFÞ×r% qÀ›NX¸–û¡T‚Ö§¯à¹–çœyÍ{¼ÐÏm¹ç@b/üÏ~1oò`NFF8²çZA\V9þIT W='ñZ éØÅ h²ûþ-ކ7'ßsóDQ¿Í ÊÇIAò¬Î×&pk!ºÕèaͯA¸¾OÅ6¾µòþÙS¢hàòz¸;ÙŒŠŒ¿Ï%¾Vc‰!ýœ§o´G†ƒ¯lx•ŸƒýWŽa–›7¾ç<^ÿ†é3@dü nú=ZÚ ‰'ÄþWÿ,jIŸ…*ÿȤ5”E‘)nªó ÏÈw2ì “@%‡¹h7SˆUZ[ý§W†îúôU^0r¥w¼ÆµŠ´|0§ý¸Ã=:î [ï¬ÃMé ,ª²ëvplìQ2Õ9€Û­z ¤<á£ú1®ñ¶(«lŠæ¦¬`kFfœRŽõË áòuéýï&V?; ÛQ×õ'Ñ. äJÖÏ@•c]üB~‹ãóy†Ì¥ñ$ôÊ*ƒL¨d-EùyF¬`kÄì6†ùëNcß„‡ø÷Ès¼:é(Ê__B7/û̹¹CðËr°àÄÿìRoC Ìðñ”Ü4¯Ïhh¦ÆÆ­¿‚¹žÔ/ù$=µ[€Š¼‰fÍ6ë©߆¨Û°Gö¢lÊÞX ¥ôë·0\ŸË?±¢3êÔKxbrÈ9‘ ½ ŸtÇi]YP¸L›=&²éÞÁÚ)Ùtæ/|ÑsƒÅzg›ý©H9)6‹ebo9˜R| vä¾Ã{æÆµ1^>oF0œâ¤DÝU˜GŠ<ýö%cå5V¬bXœNQ>‚7S帀ž×t†²;Ê'¸’€ïÿ­ÿ½ÅÒJ0]ø®ÏUf5Þ2ìû%iæ4ë2þ·Ï³Ñbw>¾Ž/OF@ÏxTžÁ™n‹Vs„à›E=èt\ÄUŠïHÐ ¾£ŸF…ŽÏaž>êì¿¶óÏsï·>æ²B@Éõ „“yœôn9˜¯¢ËE_È@ÓvAz£³Ðzú±ZžÉ‚…˜ÁŠ_è©´Äì`¦åÃ{]zq¤G†>á–U¯¸ˆÇqÉîPdøn6müv‹õßÀC“ÀÿýÿgMX-4Vs«§§ÂP´ v›Krr¡j©:ø>lÁ‹JñDàïkð}UŠ+Òài¼1=%â@FŒù¸¥a*Ú¼¾N,×¢CËÞãt‹~œkgÏ¥Þ?‚Y¼j Â\VF³? ô«¡Šjv£dÏ@KJ±, Ûú‹Ó9–^³7â¯ïZäç•<·GSàÇ“‰ì×Åo Õ.†ÏÒR±3ä4äzKçcÇ‹`ôô™Iʪ9´»8 Ý›ø€“Ü3nÝÉ×£±`ÅØ—›P\nûàl[;HæØ+Ñ;órx½jìÙH*+ñÐÈMóõ?âã3^šõgÎ_ö 4ÖòiŤçüv:µÒÐcÿ颃û'2Xt—ù|§[EÜ× `η m¥>?íß=þ¹éø^ý6ÖŸ>OüXN³úø»Ô³¸ TnY æ\ÈëzÈ»;`ÿÉn‹V)þÕ]C?à0ÛiJë’ŠàkÎÞíUVÔÝfú–CŒZ1ž\ó ^ÞÿÂUͦW´åhÿRaP­i]3¶ì@1ŒY¿ƒòŸÄáíN-’QèDÜë÷Qék(46ŠŽ•É%ž*³ù‡ç-#‹N…³§[©c†ëÜ:މd‰0“ÿã?½K­pî•Ðî*á.'0ã®L*ª°VߪçLËÙïÊ.¬>£Í_=A,ň„ü;~˜6‹Îe°¶9…žÈR†—záÌ=v|Ž#uôScûO¦³$hSž(Ýû7‹i-dÜ£°%OŒÉgè±2“3”{fŒ»Rñ¬lÁŸ¬½è2mW0Sõ¬¢«^Z"y"奩ÛP"—‘ùrQ¬âþd\wø”m1g¼)ìRæýn'v] d¹Xúö9è?åVÙJ²Á¶SàŸs‹Ê§/Å×Rw@EǙʕÿŸÿߪ9Bþé (ùºê ¸§?c¶ˆ×(ïÓf-/ ­e*q}ùŒÿÎÃ×m}è-© º‰ÉpõC U¸SŒvŸÀÔ 5Öyô+|‹zϵÊ×ü …órñç¦kÜù«Ï`ÅZ!´øþPl!òK³Ñ2§GS/ Ü´ÅÅúª¨6öožJ!|Úî+žAÞhžùÀ?9ù0iüw6–kRß„ßüÑsÈØ2/8Ñ‹x¿À™Õ¾HƒkEš¸·1Ÿmûr»ÚiŽ–R%h³ð86/8ÇÝУ’?å±ýSz–†âé}(õ&Û®TÂñ'“^áR²m"½îð™üëXÿ][©í!¾øçÁø+ÿá¯JWj>ò—çðù(¼[׃‘ïpjÙ*X½Lÿë?mkxïƒn‹Shp•¶/ʃE’IDiØä3ÖŸNñÙF«Hp cYvμ™å_ñçÆ=Lòa8*ðÄÉùäظpì炉ÐWOúY?Hƒs•ýeÂ2§9àÍÓ¸Üé?ðò¶?œ )tŒ è2%/úën'Ù ÎMû{˜æ,ÖÄ%Ù]øgÂeÞØþT¶:.:p§Ù|ÀŽÅ‡Q^åÞkç%·²¾WÀ>çcçeHkÝÎÝÔcã­.ð…jÓi‰ÁØ·‹\ï|Ò³£Ù­æWx|^(+-<ÊyÉú°ä{qÿóÿÄÁE¡^HØiA±¶{;,ÁƼ£ÖÍØØuI8±½,¦4“Î)¤¤ïåc¨1b&å+p("‡ç´ÄX¸¡Á4s˜[qÇÌüÝ|Ül ;:æ(ô¤Žg5=§áÏô0Ôx0›¶Ê¼Ãy í¸/{+ˆaøBªµV‰fÏçÔÝAa¯çø»)’úèÉB¬O –FD±“f¨ ®Åþˆ` oE°‡ æ°·§Ù®ÛƒÜøÿA„NÌÙ€Úí³˜y¾U2_ËUŸHBoÏEl{ö>h’”b ÷L0]¨ÂôضM¬JØÏ‰ãþçÿ¯ý÷¸š Üò[øV‰|°ñ÷n¹#øÝmäD\è¬Ð Ò4›`êÒFèµx­RèëʪV‰AwµaŠÌ b¡5®F+V³wÊÂÌ3Ý‘>X/ÍR:³à‘“ {'9LŠÒšá«^Yj³žMY°= J`gíêÈ?»-l™¬»«zˆ©<€Ég?`ñ-øq¯E'CB2#—³–rëe»YVÎ+”6ФcŸ,¤<¦G­>}`:}Z@;„&°n‡)'ÄÈ,‹Óà5nXÀÎû £Î—ÝŽ9{…á×I-*ÀEþÿ[½†…ïSÆò ò¬ª§îΉÀæÞ?xF9žŸ¸‰]èm"ÂÓÙîKê«øcde |šK£æÏ`“”t™Ý¡m`q÷<‘ìÅn÷±‰ ̾»ŠtÚV¡’X$㶬eÂXâ¶²9IYLé\$Ûzr*·N¤²gKéÝ9GÈXIC\«ÏÁŽ»Ür2£Ú(‹“ý ûyáëtM&>q╵àÌbfõZŽDG;bZÝJ|}7â–õ¡Þä•lûžÃxn*°.z2Aš~ÛìLOé­ÂÛ˜æO=–}1œ­õoæ¤ î´¼®‹ú_ý;ø1Ž|:œÛïþSØ`ÂŒ~cÝcn‚NX&®U¡ï,Ë¡Ö×—]ò¬„ï➘¢~€°¬ Ô:ò6 õÞK}OF•)DQ& æÜ/D©+‚4Nh<ûý¤?]K6›³¼µŒ°ÀVN`¢ Ë‹”£g¯·±Ì…štYò3ŒM²À3ë;àÞ5–6ݧ9ÓĉjoE¿£ø¾¯žmõ‡om-œ‹çEŒ<(@¥Ö=ÃÿtÑ}¢cÑxêG.õƒ<., ¢«ODб‰²Øy&Ü |#~…`zÖ‡®›}Ñlt¬ * wÍ'rÝSþoýçÊÜ µø6wñás²-âX~$ÿÚŸ æð\–}F’NK„Š¡>&·ž,„=£|C~;&+Þ˜±m9—.Uü ‰t«a 3ûÊûe¹»0ðNf_Šhq?µP&0ÊËŠ¨‰Aéàtüz¡Ž¿:»,{õ©Æ/%þƒ1TWå%B6G®j ‹ß*lÉÛW°Å¯˜38p¼M¬ù æŸÐ¤p-—¸Ès’?ƒ§í³ÿôÖØ³fµÜG­7k1tu(ˆ5«þÏþÙ¿ñÏT\ß Ç,Ò ÉVÄ|ÒçI _'è"×uá IÉ(Å„•M Ì[í5·5„àz7 ÎÎSÍÌîà¶²0--Ž.‹—[¾ƒg—;èžÕ§¿Ü‘þI¤UsHBã4Ö~ï/ÄòoCùiêò}+=Ú£N=œM•cf£¸s鉃n‹p´[RÉÌÕJw˜’¦ÛWQ<”‘­g0fõöÐç8¹°hMä_¢‚T"{>“jsdgzû¸í‹WãŽZ ZRÛ2^pá{¸Q»ø"“ØöÙ¿‰©§ìÿìWß3ÂU›jqñÁ,úÝ2 ·ÎAÑ/ ßÛŠÐ_R Lê¦2ýš)Ω„&cÂùç`5÷ÔŽ— 3ÒGóq9·èÛuLžëC»K'Ðßù‚Ì*Oš_­Ãÿö”ÎQE¡§AŒ·>Û5¹‘²é´v±jz¼ SsðVøLÞ*SUx¿º²êÈŠj{°ªAØ–nû;æáýÎÐWRºRÁ°lÿ4;!,W.ÓÙåïuXåû1£üë/Yöp)}ؤDbV^ÂG›Qf€í9%Frp’l†4 ñtæVhÌ¥ús q‹8 *m%Žw¹²κåž¼z÷o¤__¨2•]‰\EB$U÷¿„+¿M£V}ÅdgQ®•°!ÕÇ`§ˆ›Vý…Ô¬P-©H¯ÿã~›ê³ØÉpmÃz-ó0 z‚Y Í6‚v÷ã(mWEBgßw‰Ð°5ÇpEßd¦³ZO¼ZIV(àCåRìXÐôà&Z‰¶ÂÚƒWñ„ðXœt“·NÃw]Û¸ÀîøÁ; ÛôÅÙ\?öJBŽìœw¶ÊÒ²}‹1ÁÖ›^n›&SÙ›×KàšÍÉæ™ñùPt{X™…¡q©yž¬†î!I¼M#Žì­EO}£v¬XŠ€ÍwXL¯½žëvÉ`ºÂ rFLèôpÜžÔd˜tTÌ­ÊÀ-Ï÷± ÇàæeÞE›E4ê¶Nø™É{½»ëdwá„_®,iʬݪÆ$ôF1RNþY›ûo£™^'ð‚;Z’opM?DY’‰[§¢Ê}@ÎZ“+&;¨öwr!ÞÇ‚Iß&E_ìé±õT«qÚ‚ ,$ï'ç·ùgûéNXH¹lKsVrˆ-"o ÷£1^UÚÇU%àß&C8*ß^-¢wÖ~ÂæË‹¸3<ÐsA öLFÿïç­n¦]à9‘:­ó;Åþeå4UÔØeïcü/'tññúD93½O?‰_ú°VL…÷O¨ÜšhaH]nž†‹Eéýæì|.ŸlXZýÊE¸wb3•Ѥ³dfÒD…gÔÝ2˜zï”â¬W²œIæ&iz¶ö*¦ßL!íšéødÂBvô:¥;wôÂrÍ£±,?•=„ GÇðÑÒù¬Ù_‚™’"jY!ÃĸàƒÀtÚ\7ºÑ9–ÔÍøl=ç¦0ãó¦='ÒΪ”ÿî5/¨oóì*à„óà(ÕŒf¥AVl«æ5îo· Z‰…чKwr'ÏÕ•úÓè\ÄÆ¸ncÿV¿àŽNßE¾¹€ÇêZ³ðqˆ3A£góqÅG´¼‡"V’ z½Yº2ŽKpf]ÛHh[&:_vÅí…:èèÖŸ×Þ†¼ðr<µ¸dv¢±Œ ‰UKo Õñcðܶv9‡rûq‡)ÓZûœî *}ru;V}äbüwуòïá|Ûrºû‰ó¼?žI™GÔrèÙ+¸k–?ù.{J>©ÂŠ[Gñ«D8‚0n mÏcP1,œ—8£ÀÉ{ènsRtM±¶[õ+ƒ±ó¯î´×'õ'¢¸»ÛÊ8ýþ r±â6ŽAÙ¯ÉxIõyÙ ¯V6Ò%h`XÛvCX¢Ûaí‚áì|ŠÝIw äÑÍJ«i÷5êí(OžXªÑGC+Ùë4zìÓ |Q1ÄKõq¡wH:~öꙕ°¡_úG8¹€ù¬ák&¾êºÈoLÖ¥ãU]y«¢É¯‡`ÆMbqßɾø×8ÓÊ‘Ê<é!öUštxé:°s)–í'zâôËË#ôÁ¶z<0rÎ:¤òDG}áßêtâ6Ø‹#dzˆVð¨ÎÓgÁõ:,öÎ~R™€ÚW^sŸe5è·~7¶’Šâ»9’”,˜7Ê‹O ;øãjyË¥áöŠ ,}¤,ìÀg¾yœl‘)î;µ…U)9rù½Ío]â™fÊj8W*E_vx°58˜½ºö{†S7¥ë ±@ŽÎÜq…íÿ3ŒÂ&’[C )DSK»y¥ºóñ~Þ=îŸô¹†eþ¬š&ⱘV?µ÷mió„jÎ8ZFXŸÿϽ˜5†ŠÝO@séS˜#€ùövL½àƒõWr+Óò`—Ãú»p&Ó:²ˆõ¾$´˜óŸ49#ÁsoCŸ 1­‰&´#NJ)ìayñ*tYm=çð­­"˜úÄ6ªsì7ب PÉ‹ÓiÎ.G ¶ÚÌ]?“Oóýü™ÏUk$F/˜zEÕ/}I÷qŒß8ÖÉOF“ ®lïâO°1a2 ëèbÚž²÷Ž‚Òî2šàs’uoGé¿PÐ@cÚÄXŒ¹'|³‰§g5ÐsBE°·Û ®Y6ÃùC(n=–ª¦¦«†Uél;Œ08‚.óÅIä¬%Xöø#®¼³.½H2Í&Ñ·C1Pó]‡ÆUašD Üš|v¸€¯P€@–½Û•^ø#@ÏjÛ’1A€ ·qE{ÎÁÓÉÖ—Æs _—²õGdiËó"N£xXkog;õÙ•½á\õŠhøÕÛ†–)³aãútýY óX(X‹²?:È·•xzX‘åŸH†ÀIÛ1kó= ôÁ¢Íÿ¸¹6ÌbŠä(ožÂµú‚ÖV7ÒQžÍÍs„ jè°Iˆ<+Ä?ï…qÒ¿Jô;Žˆ´·p²ôá$mÐá›°ßG³é$S/\g"c³™By§'̪ÇgËéœÜWu–cmÀN¾NB}ï1TvutºEìª÷òÿ>ŸŽ•^KpyÐt–3­ ¡Ê¶ Ì…Û»üQy\Ÿæ r»µAyM(7#©¬Í˜â‚Jì±Gã'l¥ÕC  ¹Ó˜Øþî†1ÏnaYó2Rû8×T!>ó©íƒ}DÌ.—KOU½> øÙ ÏÛ§aD_ —u±Ü̵ÁË®‘+NoiÖ’Ä_‚¶X¸ç*eDã%Ä _OÐàÊÐ\–À™½ÊÅcŠprA/¤H|àT:ðÇ29šl_ŒWþ­¢óS 0Ã-¤ƒ¢AùÏvj›)³—Ïe™4›Eè÷ê4Ùcµ9x o騥Í“ -ê1nXtV^ácÑÙ >¯À‘þøl&öZT%—Á¼“g L¢»¯+…‹Æ³™Ê9°¦¾û~Á,ÃÛ$¡Ï›žs¿IÔI*Ì«hƒÑápÈ7‚[à> %§°~|Nãi/GqhO³ƒÄH™þ'œ©¾ßMο]à W˜ÃŒm3±­H¿eÀw¿< "ùD\y-»ú#e¸i¼“>‚xzã{n‚Ö<,ÅBÛ«ñìPsËÊÅs˜ƒ[lœ,ÄÎ/@RpgW„ÇÒç*X®ÝŒgûÆS·¼w0nAÚŒ?BFauüY >hË­ØàÆßÆÇÌšˆ™Ä?¼¨äÓnO‚ÅêÛOãæUg@¥ˆ8= Îo£9‘c¨ú'¨=:‹N ŸÉFÆnÀeQ œˆïxÚúæ%Öˆ¹Ò™§Cè«‚l"³{1Þ²åÑžÄ46[Cï‡ûð¿~íESý}h0{3þ{`H¥Š‡¡ýZ=Í0ÌÂþІЕ~‘¬kM ~“pä_›# "³ÉrÞ),wÕ'V†ýÜ…}¶Ø°ê(Šþ¸ *™®ìäfMÞ³9;Ù‰ú (Ö‘FÊ]«8Û-±ä§ÎüxùóèØ‹¨Ý¢]\ž|)»!ÄŒãƒàìµZzíg Þ˜“Ã?øn& ¤bMÔ~èîäˆÔíýLÂj,ÍT¼EFz2Ù!4aZDÑŠ/ž}sŸìüˆéiÚ¬Ëä'HåÙö˜LÉ.ùÇžÔ"÷;ÿîõ8TÜ-Ï !o•°°â-ÿ¶‡}£{öjVCš´-‰ï^O“3ÂhQÓmvêÍztZ4ŽžõX„Û6RÿûcéÞ<}l3jÒG‘ýøx(’ 竳ó¬IØ)oƳ“c'ž%гsdéTa%v0í[³ý(žžÎvÄS¥F0pw9){ Mî…Ó€ê~èôË¢Ûî`çá$É5¸ükò´dÛn¸´»ÔÒf²o ÙÖ;Ùøæ¥”uþ_±q°Ñ亗:ražìê [;&bïoäÀ·ÙLoú;xz͇7¸=]z´3Fý?¬Äzª¯Bâ>–©“ÎØª0Zˆ,Ábû”$ÅxßóÀÍû:ÊØí¤~—÷­—ƒï´˜;p³‚†ý²ƒš¶OœiÈ4Þ¦ŠMtÞÄwoó:û~Ôk þšÈ›å7kYÛÇpzíðnxôNŸžòÇôö~ä;ì¼2‘ñ¶ÙC¥Š:¨:\'ÁGméìÝ|Wµi,lA*Ïnå~f¬ò D$Tñ²ÅvZM† PßûpûøT„ãÂx¨B¤E2!‰+N“dŸ2®Íéê(Öâ´@·ö˜Ô!1ÿ”Oʪ;IþlL·ÖÙ© —×ází+œïSšù+jºîÀ!}¶þï/ÎõÖXêºe)µ—ú‹Ú7\ùÓJ̹ƒ+SÀé>îˆÆU“ºñÊÝPÌmÌE`œ–o^­Cg“ÃÔfk6]̦¿Ê®¯†ósMG—#  B´Š,Û×É‹œi½[®~dÄnbV{©¬ÉÃß`mfÖQ@Šd£Î|"òÄ on4 ZÚêìü±<ì{LšÉ«ûí 4‡»d©¤^ l™Óš™ìЙӤ^¥Ù´˜Zr„+ŽÀ7KÂËrnÆ›'¸1{DéSƒ°º}˜¿øjN’©ÅC¦–¸>þvgœà¾ˆ†6Âc†×˜–³.³¿f3õ\ÿu…žOb+ƉR_¯ªÖ0àƒu ÜÌiÈrlÏS ðÓˇ?Ò¬¼Lò¶Æ‡¥ùÄ1áÏ»èIÅ( ™•×eÒ!eÂ:æ%§Ï´zrty#úôM¦ã­ÆÓòÛqØr~+^D[‹VY_ûûv +Ðx+_f+au"½N§ÕâúîIûÓ„aP8ñ;ÿgÙ\öð±.í)sàMÐOÀ]Ê•pÙ§ƒË]ÙåýÐæÀÜeÑ ¤ä´ Oìþ>~òã"þÕù8mÌ ŽîHáæÈ]@3å¹lJh™µ±4g¾ÃÒÎ% ½Ô†|?¨ÄnßHÀÏ{$èèydGB(+ê—ÁäßIlåNÄh&[·bU,(§ÏLLÐ@Æ›(dLfc]ÒÁûÙˆë/½ d™îr;poŒ«#ãñ 'ª3:gþ…•’«%çy×X0û\1Ý¥Š !-žÌ‹Nø$®©Z@ÏÄ~ëþ4Põ˜q7Á´¹¾6Z«>@k¨ì_^“— úŒúû‰*\ÚZˆGõ'ÓElºS Õ}ÿ—ç]ƒÆÕÌ6Í…}v‘`ñgßÑQ¾ë0žáN9ò«”ZÊÈ€íy»¥Ã.§qCÞ ,U‰‰¢KèY)*qû:ÿêm4\2†ükC=þŽeí—Ö²©®EèÅ[Yÿ $ïö‚׿åXêá„%w`CSìÖIdÙ]ÉDfï>üá>§¥ ÐOGк>Ɖ¬ñg‡–°—ÕYhôO¿)Qí¤Ó`eYÁßo><¿vjŸ±eLä.y›2·R*iZ ëûÆÒf%1:xo7Å4wÔm›†™™Ó¨¡‹ ÚÃì^;0q †ŽQ&l¬’:=vˆå=ïàÖÞÚŽÇ*&±ã³¨Ëy>_®ç &ˆœ Ü'²kãŽÑ«gö°¨g;=1Bö &ÑÄsÃ\ÑÎ^˜v|-j¯*¤®Ÿê¹œZSÚ¹ìì—HäçűŒà³¸¹ €ˆ1»?5@v€ÄL ¤Ço}Âi‹w‚‹Ö:Ë8‰œ—Ïr("Çÿ:ÁÉ•0Nd1ˆ\ä {E¨ý‘Fluº‚ý—j±g×9Nr}1^²Š ™˜qê‹N]‰,˯iâêsz¸%h³È!05ËÆ¸¤;dC!‹j•¦í?^qþ,ø¥6vÎÕ–hŠS´™¥N›¦¶Õ„êìc¿ûàíê(öïL0È ·Ñ-~Å0¼g)ćðqÆq9~ýÜÕ´ôÍXŠûìØä¹Æ°ÁT™ŽsJ'œIo.b‰LX¢ôAä·ˆúæD1ŸGçÄOC£ ,¿ü)Û·ô%šùv"íIcÆÄ¡ä…Jl´~Y4å;ß‚BÌ}î±ÏWÜüs,úm‹'¶Ëá©g³™Ô4nùsT±¼JVcLy><¾ ‡~o£½u<ôûaˆùeÉH± ½Î3£’g¡°é(¤Ì‡Oþ½8Öt,þ}¦L³…gƒUÖkx§¿€í©ÉÃÌ÷)¨u^aãéÜgš y9 7=ŒÄOŒñæÅ"NÚ?™›ŸÁ[ê'ÀÊý P>t7حʲ´aPŠhEÁJM¼º+ §ŽåŸþ± N˜ëÒ }7ùëRz¡3ålÝ=— nl±î¹õ˜x¼xB‚÷¥Ñ™Štbøw~Ϧe$¨ ,?e¡‡w¼úm€Å‚¢ôþ’>nié^˜›4ßÜ s›†¹/—éÅÒÉø¥Ây´Ömæl³vzÚ1 [oªƒ¬d‚<ÉšéOG9· Sȶ¦GÄYí]sè¿>ŒTµYîîoü¸éº¬.à,Æ| ƒ±ŸåØÂ^#fÏùpª É¥ÐO»%è&++Z´ü>Žxî§½¹“IfÔùºýÙ·Åc&°,÷;¸úÓAò|ï6&æx¿I²Ìލ4Cʺ¥)s¸(8]rv‹n⚈"«³“§Ÿ$Xܸkܨ7„öÅ‚øÙÙ`3Œfö¡”Õó?¸¢äv-õ !puOÍ×Å£6ùBGÝ…\·XäÓÉ­ß²VÚH7{œFÝöÜ× XçýÄÿb#üÅm/gQZ$ŒÝΑ ¾hlÜ”Áܲ‡ïyŒ€è)´U“©' üî5¢—¦L[kžrQ/MY/+ÝU)S|Œ‰ïע˫ ûû ÒfÝ€zAºti.f¨Q·oðx[=è‡ëN¹E¼õ]€Äç¢ÔRBnáŽ;BìÖ£QΤüŠ{!%ž{£óFŠ óaW­û–Ÿ“0Ë#Ù²;Çy§¢á¤ÇM,Û÷Œº]KRõO‘OÀ»lÌ]=g[¬¹‹û¤è©ñ !˜Ê)“ä^®}3­’á1?]7–âæϰÑà=Ž?vÞ‹€™¸{ RýO —F!ü¨p þxEÑÍvòà´e}‹Gæ8ô|òNw-ÄAn´?p:Y,­Å´7îA¿öf83&wÔro6œÀuk:±ÊïXjˆ90ºõ§°˜ú§ ÚÚ„º‘?y?í™Ó öKú4ú>ñaí`O™궸âaNóÑÏÀ¶ôl”ߣ^Frì¼ü5l˜4†ö……ÑYS„1%}"]QLØäõÙ¤\‹ÂËh.;à<—{@šŠpž ƒUo¾&SÕA `n2ìæ§³£'BPSß—žõ²ö:‚VµÊ0/yª¶ô;¶î“¦ã¥Y·_ÝŽIØsú9|¨Öf©ã”Èüñw¡Vy?Ë,k¥–»0Ù+®¦ª± Í,¸qÖeZÒI³_ãÏ[Ç <¶œ“8ž€µ×kˆÓš=ðêŒÓ_WIw­4aËxõ´ú¡.œ¿^HÚÙTè¡ðô¶€M߃%-~³o‰3V1Ž•ʰ'ÙÓÑÑJ}ö cWåö2Ñ@yòTµfˆ…Óx•òÔc.‹²ôg¹¿ÚQöZ6÷+ód.HÒé;¬È¹õqé“Rvÿ†/4ˆ´ÃÀ.öjÀŽ=Zfˆ>ïO³’*›>&W¢ÓÙßAq¶Ç>˜ »¿CÁÛ`¿f>ˆÎÄ&í&Ì,ó¤Ú)êôÖœsø©®§<›A·½ éßþbphöml†C7r)û«yzŽàÛ ÷Èb#ð½$”ºOäÞÕ„²SÓÃkq!ø­‚½ö£üÖw›°—IðW&¥1œÃ3+n–&·ùG8¾zš: ¹¨¶j‘=Z sR—Au ¯BOßö¾F-c¹; ðÆÀ~àþÅrß5¯ÁÏíaôÌ=ö#è&pñêL›ë$VE¦ÔÛ¡ WݬÛAiðŸ/ÄÌ$èžõ¿­OÄ\¢¡Wþ!þÆð‡Á¼º¶‰ÜÛÔEÜà.%”‡¾3À(€ZÉ8ôKW}[ɶæÂ(/eSm=(ßb3{Ê]Ò ¯öÁ³ÅæÔ‘³¦Â>Ö·öæÀÎãÖ´zs0ñ‰Ìs×q”þȦ¹ršµÈúQ.yõÆaí·¥Â) #a*îý[‡ë§â£Œ.¼Éî…tb‘ç|>ÁŒ´ZÈ^ÞôAaetü?šwk;^1u®žÏöÁRôµe†¤_úFÐËÒÒÔbÁr\j|×Êy¯ KØG>bñv¬û=™û†Ó=ãPjÉDVZÎ}ý ìyñZ¶zälœΤ/Ž¥o÷êâ…ø¥è/ç§ø>ú+aâ?_öo÷UîæIèV\H£Æíä¥r´FE€­ Ó¦;÷²Üά ™m•¼‰Q2?ágO&8¼—ýN}‹F laÞ’a8×5Œs`†ét v}à¦Ö:t4&PM›R.íójò\s½×@îžN÷N #÷Pñd-®W\BWŒb˜3Ixñì P3@5£0”P›ÍŽÞ:Jï¥Ä©~Ä1+ªTá‡6­Ã°VÅÛ¼,¿<(øÇnïÇO>“O?ühúWOúì{<›<=;7vv—]âüL‚‚wÞ¿“O3 O¯´8¢×’Ïp«µvºõ· íõG3«ñl¨i[úþ i[j}¤ãõPL‡¸I2plc‰¦l~æsxaÙÆ]™rsU?`†:šÛÀ]0>Œ3<1ÿó_²w—-ì;Š\j0Þº,É–ã/ Â׆‹…yƒ>;!q|UæáÌÇ×y×ç,`]i¥è£®Åþîxá5ùxÉʘE:ìÃÙ—Öàù-“Ù†Uöôú¾;x5¸§÷A÷´3\®G¥ÜeXNÜ>zîd ÷räž]žN¾m͇3uÑãuožÆDv¹2OT÷âýNÊì¹Ìí^q<3–@bL.±e!3çÓÅIö™@¦rt*ï&¿ÖbNX—ñ‹5µÑ6n,§º!Ü‚YCË*zWp´ý¼Ç[zÔ„JigÓ–]'ùÇ‹oqy»¦² °%Ã-‚ z.š­UŠfKbð¼Bö÷P8è{,&$Á¢‚%´ø£5õvéðõ 0ãçY\ætØ‹q°Yù ç¬qÜ–i™•ß±inºxš‘1ñúpæ† ¼ª›L÷‘H?÷ÿá9žkTãÿ¸Ì™ÿÌÔã:ìð›d7xõ°¢=6ãÙIùýü_´P»MÎy£¦!w!jµ"XûLa2_så'®â²æuìQl œ3 Î{sPqìztÖP¿gq)'“ai_ !êwP€wì×fpRÐä7š A=Ýüý&©ïªk$.I6 N!Kû¶Ñ•O6ÒÝl{¹Xb× àöAÌpçc8øk-½ó¦‘g³bÜ5›ùéVsÕA84¾…C­nx2<<(¹ôb:xÏy…½•3¡Íá 7õÜOŽü®‚t¯NXÓ»¢G6³…ï04Q *~ì j _éc§ îçu.q”w\¨â¶MÖg÷ÜÇÓáSQ°¡j³)2æMž{‰³KAýÏ2ôó®mð"å$§Vóß¡1ܺšLbçiBøO̾±·,?ƒÒ&IdŠÖ0h«…y‹ÿ@éº"°þìÇL¬2ðžƒ(»ÙíÇ©(œ)QFôu@"t×8¢îÌÍ ‘鏸\5ZªP•³×Ѥ3þý¸Óôvs×,/Àž”ñÔrõ$f1ߎýsm„wc@×0&¯]óL\Gkøk޳¬ãÏ\®É©¬høÃwÐ7§úÃãâ†uœúóltÞ„ù?â!¿pãæ™²ÆZœAð ˆ1Á›1¡là”>ËL‚céÕ¤ô– ˽s“·*Y2Êq“ç~Gó°ŸÜâ…í çz½ËÊÉ&uêýq6·j¶4»jq-Mã nÑ^|qbOð¾½õë'O Á Ÿ»éŠý“ÄiZì¸1SÝéTa/Rì@s·%µÔ‹!u—ʰxà7·fsÞ¬& &ãÕÞ—$Bl.¿¹¸2´ ý(l™ðhd@Óö—Ü–þ‰Ý‹Ý~OÐ|HˆÃöw'p³õîA½ÖàÅÀ/+W\^,‡É³³=±+[>s†™ŸÆ0»-öpË»Žl¢63Á$!š¶ gë㿚Œy«žã+Ïóp³JúÑéÞb4öʸrý0Þ X'«¥ &â>,ØvŒšy'Úѽ_Ö;=÷È .G§slS âj²K™y‡’¼nî|àR3% ï¤\ƒ&ï<:óâs¢ç«N æ¥á¥mwáXE0ÞÔº ã·åàñ¸<á_Ȩ|H^w> #6aóû?] Ú¾JxaÙ+à ŠÓ¹rXj˜mo¯$Kv”±Æ«\Lj_~ö®ùM»šAo“xo~ó{q¬!+N¡e¨ é¹ÎáÞs#Dvï ö¼)‡—(ö›«Ð·/àVÈ{Xâ®ÄV•,¢74L9Í]ïá¨z4êçss¯.@™µI°°À˜]>ö¿Öhã¦ËJô—Ë*°ñe§’•X¦ˆ: ø—Î%ö€Ã­Ø¿d+|ë<:F|Þ¡6¼bCE¯üà´Œä`Þêàâhÿë³xDðz[ ™¾!Ïž¾"Fã^ðÇ*‹ÑW…°"W…ØZ á8û“$ê”4õ™òÛQk®I »Œk㛲*ÉÝh9WŸzO.…ä–,t¾¬@ÿJ4rUq×ÀÍ—à}g)xZßEÞ—Ó#éip`Ò!ŒÖz 6 a0¨EzºÖ³ˆÃÊ ëtÖOƒÍµÁh±T…ÍßP çá–*æSœŒw²çbÔ‚màðpsÜèˆýù7°iwùbqË2ùÑí¸îõþ?í ìº(ȦÖrƒ Öu,ŠÞ ‡yì}\;üÛ& ÇržÁß_©4"PŒ¥Æõâõæ£ÆIәⲠ^iŽcV%ÂÞ‘bܣߕ—:IéÅÕ¤zNh.‚µµ«ð®x4ÄpÁã+‘é¸ÜeÙù¼ŸÐ”’NÒñŸ¥Gpo祢å¦üûÛø SõHÈ:)vööuX<øË÷ãT?ЛA•_…¼ôk ;ΙA˜ìK<ù€ü0O"pµâ¾Úi,mâøR>‘Ž›…)÷ðÏýP(cÍY,§Ú¸ÿcoûƒÌ1Žáº|ÅióeAž¡œ öìëÆõFËùÊw¹&ÆtTËMfâ’ál¢j²®5€¬œ~›‚L@ïŒ,Óß¶¿A&œüK…Û=ÞÜåGpoÁ *­„‡"ÕYPTw`j9ÎˉA ª‘ÎQøëƒ1“ôêDiyvHt ®û¤KS1²ùý§m8Ú'¸\_L¿›´ãع¯0ÙÌnÏߢF¦Pl¹ øŠÐub}÷À .4N抶-Ù©—иy4GLSâtw™@Žé<÷‡’O ÿ°ÎØU”'1“Óã¨æ&š'µž‰‹ÌÅ£3¹¿»×Y©OIæïNO·gV˜ÁËöØlÇ=ºÙ‡+6cõºõìƒÌD*’¥ kŽƒ“èeîl{/Ü : î½Éó;ðójx¹ E*ßC¢È÷ãò5®(ý8ûd Ô¹,¼°Ë˜Ý”£ŸÝO“=3ÏÃS•,X-1™•oNÁ‡ ÑèÒèüQ<ŒÑzœË¢ødƒ[«$Äün©SSHŸÅHµóD­¼Õødú[Œq~DŽ~T€? Ô4®y®íý2:¦~1“q RÏ`K—·ÄÍ•í©…„nË’¡*1›p‚¤9•ïËC™b Jݾñlò£eÅ"ü3t’{zÕþt_Á…Ýà”¹(J^:‰?•éuþwòôNj³G1Bë"Wühˆ§Œ(ê9ɹ¸_%¡CiüÛÛbè+—Y¼a·_0üînʙĒnÖâ‡Í÷ŽòÖ¿»·k,±lëI\±\’o|>Æ ZãÖæ3øóë5¼¶q=ÛZºî¿a½2UŠòKÞpÓn¾À‚ÓÀ$pˆ_^£NÞÈOƒ‡ùôØ­TZæ1™oÝÎ,ê‘ën=PWÈ“ŸDÃ#¸Ž÷FlÞ£Èf“{u¸òŠŒuOƒ††„WЋDcGyH’‡gñÌò±tM‘ ÷mV6Ø&Áüå¦ÜÀÁoÜÔJMÖk·Ëý°÷q!Ù=EN9 cðÏ7à¬'Oïô­…7›”aëgBÇgÙ²¤åQoðüXHZ^”@r×0ï¥á}Þ÷žLô¶èDù?áC¼‹'Ñ„ª Hý#ÄDæuÁ™#¢Q‡ƒ×·’w5ÜÙ ?'žWîvoþjB'Sh±Ð®-Á¦ ÿ¦ \8S”9™ [ÕÓ¦d”Á;2‡ŸÇqÒEðiâ"jÞpÏ­™Ä9ØÞà*çnd7ý¶ŽŒž’|[ô;1 ¿XÜÅ™F4ØjÓ•¦¯’ŠÈý/–¿ÇW¼Õ¡Wű?Î®Ÿ;‘;N¢¬ó™%UMøƒ¼À z^å ÞL]Aâ“Ú•è }ƒg̃×!7‰ÉÚ}lVV4øÈ\€³¯ \¬HEbÒYòâqtÇ·_0¢Ó€ÁƒŠ¨ïoƒëª ¸‡åŸIÍÌ8¦8yî~ãPæ­´$ÓàWø_|¯| 'ïîÁ‚Y%‹N’ª&ZØUú¿×²¥ÖÜ›˜e`í©ƒß S¸Emýp`… ½{ nžô ê#ðNèo0IÛ mÇ¢ é±Ûi®nu¤/ÖÞ˜#²xåª-FÆVp†çÀé‚4•ö¿†x¹¸7‡pÞ©‹8ØT=JÀë|ùBöá<غhÀfCÜá¨J~g5Âø™Q8$ìL>ëu‚èðÄ]5è‹skéÕ娷½ä}aûÄ7ÜPl ÏËüO¢ óähúÓOx'͘5OzˆÞõ»ÈŽÍ¹Äèx€åLT3fKE­Aå‹òž<…’ë¸g朢uŸœ?u߈oá®=¸‚qx\PÖm¿c²qŽÉAȬӠÃ?Ž´t®†35 ø` û%ÐòüT yS„[bnC¾ÀèŸl§mÕ*Øj~•{ý5…½]jÌôp:Ì#PŸ-Æ>^úHRß7bhÍj¼ª)Ÿ—¢u«ÕÀÙñ5x=iB›àü;” ­¡Ÿaë…9¸Tw9ˆ$j×õØÍª°$ÛŠúŸAC}ÂdÆUc{ýðO%¥_¾æ´ˆÂ9ôݵ‘wî†*Üü‘ËýŒçd³È3ÛaœàJéw…S0j›=—G#ÆíÅ„5gAËS•lô"lÕ"˜ï ; ö¡ ßôÜØ>‚Ã"±h ÎöX`Ôé¿X[3†ª/©³06éC}ËT!çÈKÜQJ¨å’ÞQLiÀÇS“XÐÿ¼„;¢.B…ŒôY¤mϸ|&îÜ´‘y^´ åV¸â’. ͈ÇÚŠXª|)§¦Žâð=y0ö±6[ËÈ€ ü0eÛ+Øíà,Üÿ¬ÿâvF,¦ª{gòƶY»û5ܳNrº²œ»Ýsh™nÃßã®ÀªëCدƒÜã™Ö(õm)s4u£]§˜râ-îÙ $+Mg±¡¬l ¯çlg¥>/ˆñYì~—†æ5d¥Ë É­?Ó¹8ž¼"àR,ÅýjWAíŠR,kKž9öƒâmÈ&¡íc)š¢k ­2lþ¢#ܬ+óI²Ô"ôŸGÝ-Ø•¹·àMèP½®z&ŸÁré$jùb ƒº·>Üì%,í^LZ­B«ïhÓê€@ˆ/IÁ+gœàUÙü47‚6SéËE¦ñ tonÆþcr>ç*h*.¿l þÜk@/ÒcJ>¿È…UðÚ & ±Ú{Y ¬~™—=m?š]¸'RýÐÿ’"j=¦ëàýŽhÊœn\zÿ;çb º“^dq¦øùØ\(kçµR(0…ÇÛjs»iACÓéK\‰ƒl!$G‡…ä.„°ØS˜êèOŽ\Äž¡ ̤Û¿N¸KäK³Ö§ñ ×1•þH]J+ ¼q³å^~ÎꇸC» &oïrØôS˜}sø‰_»àü¿™\wÿrxÆ{Bµ¶\µ+Ù@­dÍÙ»I¶Ìü—7ÎýÚÇI—1ågøCZ½œí±c’ŸE7ª°ðè2TŠ;Ž{ßáÉ+¨~ŽÐZqê’öš‹í+D¯Ö‡èSŽÀÛ¡_¼ÝÕd‘“œÐ›€?†Z­‡c¢ðžþ¦WX¶´l‹+©ÂÊ—ÚPƒ´¯Y’ÅlµCyÒ®ŠtŸ -¿¼“[¸¥“½á¢pò¨?Žž¬7Xµê8&þªjXµ›¶6gm“ ™‚ÈvÚÔ±´HÂĸ@âŸ6‰~ÌÎâ¸Ï1Ü.¹ø%+¦Ž_ÞáêpªÚ<Çd¸ŸØ–ý™|TþÎi{E1IŸhÖqrÞ:5Ä]?°œÌo?ÇSt°…J—šº=îÙ«°;²°óÁ?R¥t›<íÛ‹ R%é8_9˜h/O_ΜÀt·œ#[¯8±Ä‰ftäLÂnkÃ17@-i-:9Ú²ã,4Ãð·r%>M`Z£™[`{ß®IE­„WÝÀŒ§ÑÇ_}èÓo™´Dð>Ç%ÆÏ’þ°Ô5:r›™á_Eô ŸÁMZçŒ"Ÿq÷e4÷*!7Þ €ÁÔx]¾ ·oo^ B̃£$hf!¬ûÚŠØu cï>€ 1:0tþ‰lR±®mÌJñçeÍ„4îà¡™Ïhˆëzº,` 7kåm&1XŸ”FqfœÓùÊ) €uÙ T‘U¢ ¼ÅÇÓÃ{0fR– EÑ:r¸_«†÷·®‚VÇ«¬Ÿt?+çzí\éûVˆ7OywžpÍú¤&£T*7aÛë~4óñÁ!¦¬iC3^mF]0ц¹ñFÓð\ -Ûå˜àòFlRŽo»«(jæñåWŸ‚Nt{›jE§ê°©ÇßâÞ«Bðfð7Ö:7Á&›:”ØÓ‚÷ÇÌ¥O ÌØ× 4ÌtΕ§ñwy´ÓÖž>-Ž£G*ÆQ¯{ÀÕM¨‚óï0/S˜ì—Õã4-šÇ%Ã’$CP úÏÇ*RÓŠlÇÒsäHl&.Þ%K7 aÙº7œsù7t–á?J‘gÏ=%ÐâÍò§}ír†IŸ9‘¨ü}ï¹öà..­ÜIÞ?O…Ÿ&“ðÔ½Ñø¾ôsØ1×»oð̸UàõT”ýyUÃsÎ…_“ÇÛlè]Ÿ,êµGS~A½EÓ[÷LcÿiŸ-veÏZš7ÿ"NÇC+㨠Õð~ñJÛ’¨Å—n‚¯_qk˜á‘yl±Y0×bz‹ ª»êÿj‘moÃÇyGðó,ôwžÈ½?5žÒ`U*ÉŸDýfí?8±O³YcS>ÜŸö‡4o™@£}¼(Õ*ƶ ´Þq<ÈšœûOMJ•€“” wÏ¿¸ËãØ÷S©°o=|¾ÈmtÑ„ÍÞ&ÔÊô;;ÿo*=qõ ÒQ<4'qydÀõ -N7æàããQk¾•¨fed2v1q'QP‰ôÀåÝþärR3:&ãÐa®íÄËu-qü e'±úƒÒ¬yŠ%’¨™Ã‹ámëôl>ÏLBWÓƒj|ø”2h}o†¸-W¢ É­¼WŸ3Ao|314Ýíñ€êSy\HT/”’Ä5!Þâ Kò˜ºmø¯µbCç;8•šd‘NlþYkA«„0×AÔ·M¦7¢OrdSNø€ -„¼Š@”V3`ÒÇD­÷+rø2! rd&·›9˜l´ƒÒFp“NOH,g›¨âÁËÁ#IšÜ–c<ÁìÞe\3c,kØ !W¡õÙ :9‹õ×ÿ†Ž_­øËZ¢¼Ù>ñh˜âphÞ¬œ ‚-ÞéðÂ?Š.ÔÝ^мL˜~Ú<•ݘ¯Ê·)£v.X)ÇãXw€Îƒ/ÉË÷ëiñLîàÞ4âê» K¬a /éÁbð ¼Éy%Ä1ç‰ $"‡c—ÙÃЩp¿¬ò…cxqê<š+žé³¸K¹¸ÃìºÜµdâ X[GX–!˜g‹Ò7IXüÎëÀOLîÂkE&ðõ9×_<•úÞmÀ_Ç€]ÖÀÞ¢lÿÖH.áÜ1–D]Øwùåø¥k¸™ß^…±s.â´K_pö©>¢©gDˆž!¿'L‡À6î㲘¾¯4eo¡§¾{Â#vUʬêÏfh ¹ŠÛ‹éË/¡óÀ!ˆ~-I{>˲IÒ—@ñ÷\º²}9hù81ÙÃl¯x\ó£ÙrÌeð!Ùtb1\è_É,DwÂÝYa”ýêíC™ùôø²Tñ:ÿ­-¾Wc³gÐ32—ôÌ†Ž± ÜÑæ/xgæ(½7Œ«xð£$‡/:»\›ÚÐr&ÐbçOeÒëžb±®,ÛåӶꃟ9ƒ~ _B;:…Á S›æ(nAÃܹœ„„ ~Ð< SwàË~3àgfCˆûuRx˜-Aºìe7wõJ­Ä©¹Ç@I@”iŒù몎ƒrÇ^üzÈ”¨á4øqȆY©sŸ"›¶ÿAIZ¼S3ÒÁgV1»œoÂÕ OàzûAëÃ;nà%Ùâ:”ƒFÊ­0×O Z÷B}± ÝS§EåÛt™ îmüÔªI÷„—bãÄ}XXá u3ŽÃ¦–,N_[’Ÿ§Á‡Æ³Ü<¡N(îÀEï—ÓÜQî>T8ª5+HÈÇ\6±¢€ºÒe©øÜéÔo³/dýz‰*E¶°D>ÄOQ±6™lCg,@’3·›ì{ǶªÄ²§ódi„j ]daC—·ºÒ»ú0K§€{1ÊcÌ÷èÓ×YÝxã–‰- AŠÔu’vý7¤Tpb>~¬1EŒ¹Ï­©^JWX+R§Èl—{ž(±„õòÆs. \_‚Ñý.”È.aß:¤˜ƒ¯!\‚OÑI‰áÐP_‡Ç}Üæº|ç+H}!N_©'bà­¼{ɶü<ÅHªqP }&9ý"ôÉÅëè¼øÖzËP«äÉÜ“8çe/ð÷_+6 ƹ¯V‡þ ãè÷TêÖ4™i(™’Í6㘈Ä1XÊ=€¢ZfæCtNs©"kÿ}•¯³¥œöÁg™ÈÞ>€i¼14Þ=œ¸5ìݦÇ\ŠÇ'R;~6lÜ@‚ä bZ+^(¼¾áªTÀ%“|xJ ±D”èûšs?:J°OÞW{Çþº]´Vè×9K‚Wàqåv8ñe]$VÙxœÿšTìÇÔùÝ(âžfÃ}?v"¼˜ÿÕû ÷ís¾ÓùEßÛH;Úÿ¢UþI¼`nÎμc›wîæ¼þœ‚»Uïùbzµ °é,ŠÏjDÄ\p™ÒÅëJi†ðO¬Áä/Êú!ÞáXÀÞ¢¡œ!]X¹LLN¹gwàƒ\†¦’o½lg7ŠoTe·ú4±Ð˜n{¿è÷æP؉'XSWXÁ4wt¡qo-ø¿+#³Ê–ÁG9–ï¢N«J®`ÕóY ëjͪfy°Œåcø²£ñ™T¢FÝÑñp 7ï•áúhR6YrúÐ_ñ)ðµ0¦G}‡MÄhưÛ?O’Å«¿‘'G„Át²ª”!˜Ëq‘ó‹`ݸ~îÙSôß²:…“{¯;àÜtOH’K·ÝÑÀÂÉàu?–-ßµ¡p}å-\ñø2â/Z*ˆï•O`‚ÎkÐß'ˤe>sé‚ý±A×È@“¹Bø^®ìÉO\µ-ž[g ¶_GëÝ”4‘­ã¨Ñ:NSÏ §¬ [Î} MâÆ¥˜Á¤rU¦íQC²¹Á9±ÔðîUî¬;f6ÄÂìÏÀç‡ ×ô *K¨ÍOazE»ÜÞOKø«TÿO²Q§7;àߟµèÉãæó^Œøp ffTÇDV»AI—S}Eë²ùáBt‡L®²ø†Êdzù 5ܼÁã<ÓN³ +ÐJú‚q'þa÷H?O Ë–9Ò÷3$8gÌe1ò:àÖ§Í} <Íf;˜³ž“ˆFvƒ¸ve¶K`mÆ)4ýK4¡ïy?Ýï3a; PzRC¥ÔÎrÓ5ƒ‘{ [h #³C¨å°xò©“˜:ðD;ahRÛ9“†„×S™£÷a¾ïZžËŸÉxâB ÿÖŠ7ðP`#9~vó¦]µ\ˆ"ìÛ¹ - Ì 5¢Žúãá¡Ê0¾P%Ì-g:¾ü]DÝ–Ò#ZgH´Ž;泂Ö>,¤«Tª‘¯ð`¤<î™ 'æ´ÁRÍ34\/ûq¡™´•8p’W¯s8í-1£‹s*ålxb7`üžjLþörñì©?(8‚ÇÿzјߓXÄÈ2öÒÎã„èøÃ³ycå¸ÅñÛX€V>}^y†Ê» ã7Á"\­÷žó÷Ä=MZ>«ûk I¼sø·$§«æ[oÙ¦ÿúÀNþ+üë•wD›I }Æ/87 Ϊ穘¾Ä‡ccHëâ‡$GXž[ò¡ös=ñâÏ ¼ïÂ5¹j´`üvf÷¡ ÂÖÐÙ³Ýàwµ ?Ð[‘®ÏÖa^_¦ÒkÓí¹.ºyÉlÚ™ ºÍ+©hp »&KSƒvä¨ìF±Ð=øÁàTìKßξêyt³t,zåù¢ò³< Îx­&+0¤üDIf龃{¿"‚ÎÔ:ˆ¦ƒ.8ee4~~WÈ-=«jFøÔõ˜Ý ¬5N4‡ÖâÓ1ÉP”*çN‡£äØ*ž$ÌMÆàr58Ôˆ8¢úæýÜ¡…øüg1þ§)6ðO-éƒácÏ®5À,º§ÂOŽÜÄÄÕ‹ðü>5¯ëJõ‘«Íc!EãŽÓÎà±ìæ–ï ßþ þ!y]Ë_žsލÑÒª‰ÍõT9ÄӠ#z.WûÃÄ'¥èï}wÜ ¬ø>b3læŽç;xÒÞ‚5|-Öß)Å”g¼â”í²?-j¼¨5BV-dZ^¡=Ñ{ª_áçmÂÌàòÒQ|v*4ßâ˜úÎм>Í-#s<ñ…ÅX:4ǯ²æÍ÷©£BhMúÒçw~ÈâöZ¾Â­·cȽwô÷™5È?ð£Lè¡ì\˜LñÛ9!f‚þËMÜ´È2Òêàñ§V£ß¹íŒN¾ ûîfqJ25Üñ®Þ K²»*½èÝ•ˆƒ³!¬ÝO*`•{³@?8€îÛÇäöÜ¥™be´ÔpÄŽ±dÞ·ÎÂÜTyXþN––©H°ÉRVô·".?ÃôG¸=I“éR+úËÄ®<ˆWWéÒì»w 1të50îO:k½¤Ãóؾó1àxr½?†v¤Y±^¹æo› |ö˜Ç¸nvèÓo¨ñ™‡{n²+Цüµ3óÎ%²ª‘ìÑ­ÉÔiö7ìØQ‰!¹.üU\Ê< gþÁ¹0Ã>žz^Q¢m=̽[7Äèr·=ØN§`ØnèK3œ[qÓ™vœÝÿ†%6¯`·¤ÇZ=ß´Ÿø0Æ{™~’Z—“Ò>/f7ÆÕN ±ŽˆÅóaÈÅEC‡j!û«~Ž…Çþm£ß%hùDPù4‹úrû*ÒHZ 1ÕÙöDÖ+ÊQaý\6ÇÌŠJã[÷K=§› T1ÜÚúߨÏÞE?f^ Ø:2]?Ì åÕáÐúîìc >ð¿|¼€ŠU tøõsî&ýƒ/N ¡“;0äÆª·ž$+Ïx³ÐÃðU{+ýYkÇîj[ÒOס_ºV‹÷óoܶm‡±WcíêbÈÆ¿ Øô·³¹~IcÞô¢PØ3\+_Oë‹›ñÕ‡nû«|9ò3?ŸbAQ¦–ý£íŒ© ¾œ*"žцÖ;<‹wpÁeM⪇ÿ®Ês]¶cÇZj{t:÷% ô°7Ÿ"0õÛ$&rû7Oä€3Õ°ŸJÕ~¢Ü»É°²K€¹‹´øvÝ–î£s¹ÎC ÅLÁv8­ìpg Åé¤uÊLâÂÿ>ÃïÛ®Ô×m×1äËx1cipœ <1 ¡5ü²ÒLc· ì˜sŒ6ÑgÙ¥wÀjJ·oµ-×Ûrt6kЂÛ5hX®+ŸÀ<5úôŒ}ÓÄþM•ÅVAe:co;îRõaÛyãañ*P‹î ë6e²S‚ü®mZ,/9Ž»÷ Ìû·áÎÛ}(²°Ždˆ1o‹,Ú#ò‘ ýWˆ¶EtáñS8e¢:1É@û2I¶4é'ºާoݙ݉èÙÆÎïŽb¶îL4Ož®to„G‡·àÌ›áø^pYsëï5Ðì‹Ty`‰´>pçZîOd §#1yúz84I’ |²Â! š[( x!:ÑîûÂ-ÊÕ`03ö½‹ƒ+b`õä&Ü·mx t¢¶Ä.¡à×T2ï´Xã©©Ú,QV‡™¨ogÛV?çŸ&ë']÷3pÑÖŸÅI^ìþ‡»7˜Ã¶e¬uÅ3~HÒ OÆÐìÓÖ°°P†­ûÔƒ³Þb…ä8*ž ïce耟0ží?@Ú='‡¯äà‡œ8^àÆJ¢å’{ëÝh±–*zŸ»ÂcVÇ[Þh5¶ØïV`¥ZT¥æŠ4æ$JwALÏ,ö(#æ]”¢É³ä)Ø:°(ã=ì?½rƒœ4µ\1š/JuqûÅ0 ŠeGñQî_N'mí' ãÍ=v©`Ÿ5$™(_nºŽFYôýcaVô-~†ݨõ žw…ôÏ{ƒý•ª´dw9þ[ôŠË0Ñã*%޳2!Tì—4£Çˆ>;^ᇚ‘ºxwòxœ¨C­ªDýÉ™ð{ÜzÚ·t2u_ÑÏ]Qù‚5…è’Ìy´ú«%³‹®å&Ý;Éõ÷v”ÝF‹ÂV”®æ“bõkœX,ßµŽ.–Ì£Z…S©×íf¨ÌÀ5+iæ±Ãìjõj&wn˜guˆûÑîBO:¯‚¥q>t­Z>Ôð ÛúQ‚g”+Ž/‡ÃØÍ{𣽥'2)ûQ,:f%»üÓ‰½6¨¡•Œy–2¶îà¾> Êc¶Ñ1CkØ»¯ñØ>(O§í‡ …)L«§j´Ó#*îè²­§†×2ÉžÛð3W†NȤ:·tØ›H¦ú†…3¤BªäNÆì³üJ“Ï ±ê L¯Û ã50é >ý-…οý©Žb½žp óK Ù8ŸÌ®Êý3 hâC7lúz.ÄC×Wâe™"Xôñ"q™{¯M©Ç§¾GIÏø?°d0™ÍÎŒ€´»ËØUôòÔx´®î.ãQ‹­Èng”Ãô‘L.Ý(Þì fÏ_Y{ΟÄêY3=h¿Í€›qœ/ë0Þ³µÜ¦ûÜ} ‹ù7Â+ø<‘ÚïýÍYwðfŒ;Η&@×%X±ÊÜÍ`ö¹ÇhFÓ¤Ûìʘ_\Hা+’½8;?€q²¢+çQmCææ@"Ÿ,£ŽË_âéVZ#þËïªâÓYeLôd8fÉë1v3Gè\ê0ƒÏ<¯›R‡à:žÃ{7°XØÉÞ®F|­¨ ãç¼ã>)Ùû\ñøJ’RSВ䦵 Ð!wYZêÌÆJ ³ÐË´bëo0ÓçœÞŽ[WÞÁ©Š^ØÑ‘çÑT¹Â÷ØÐú ô1Yýæ ÚÒX¹ßð¦{ ܺoNåß Ek@Á ÞJ4ฎ¹p·…ËIâÖ9‹rñ¯|Á¬A•<CÇž¼I Öme׺f`fý¼Ñ/Ê͹²ƒóÕÇíß#Éw‘ZîB6›+“Ƶomb7Fë°«høÎ™N/L8A„}¤™U9Š$N¡Oï.eÛOº0¡ã´hš$ë^eD7ïããΉ²,õ4£þUÓÐå‹*ËÎZD‡?'²”q³ÙøOö,ëàkx!³ öMŽä©a+L`ɳRAUæ:¼ã­åW×ñSúX:`<^ç@ì‘qtlh4͸u«)=ŒÃ{ºa¥É\ñ² ÎU$²'噼²g×áå]\yÉ›IU¨±¥rÙD6ê#¸ðÁÐ×Í`³ß„-s£¶3xâu‚Ðy9Ôû&Ò¡Åüs&:èµø”éYо™¥Ä%é$=Ôi†Mß ùx1¾ß¢…±¯°EÂ[ñPëI7é%–->Ñ.aXŒã1ÏèI±¬„ÌÓGy=Ž/p\ÚY?0 *kNA¡m96A›¹Fq\gIÙxö,ZÊŒ‡ü縿¹„ìPØzô|=ábø5²z¦MÛçìÏÃ\‡nôÚ°Po.»“:¶ÖÔp"Ųè8ôJVÙ‘×bœú&{ Ïza×~ Úûú'(ŽaæË'Á§ú´Ü|nùƒöqük“‹ûê‹@Öô K~ÇÚÝסìa7¥Vˆ^ëf‡w¹ –xl_x…ë.Úf5çuÿ ×÷·–+üSF‹ÏsoB¾ók ¯¡BÄW0mÄóÔ:ËéÍeÇfµ¢‹Ðsù,ž ž—jAîWð~o@Ú5þÁ“¦ÃœÉŽ^4šñg}YÁÉWL¥!Ê+qD¿‰œJ»ƒ¡3§³È ó`ʽ>È Âg/¬0ëÄ]ô 9­ßrçê>¡%ðjs>tê´ù  º…ò¹øcŸ-õi(‚®7Isw$ÕO¾‰7›9N)’Ú¯Qä]1CåtúÀñ/—0¼ é)ô‰!õ O8Ÿ£ýÄÌ5ª1~ì%”²k­Q¯Éç6à•9 g-ã{úÍ& Ž@ŒÙa$ ßPÆý§¼^‹¿ˆc¦Ñz'7ˆûé£çªHô<šÎ#Æ¢Ž´p¹Ìð÷˜ûÄjcžÿí6•‰·` -«Äÿ4ÏußàXk ~Ðb%#pÃî÷Bø²æpœ²Vš¥tèà”ªGX¢)HuÓNrƒ–A(¼õ1¾Ãøq£í`]o ZΞ‡6«Qǽoˆ¥’ük–š`³5’È+Áæ .|=د‹™äO²bûIŽ;wŽôó¸_–a@M|Ñ;©ŽÎÿuJÈ´Ú'øjÎ3^÷È-8×/N‡Êã­ˆ<½½ž,øÃÃê`u?”)>žÇ¢T™MS'Z<¹€Á^”ç{–­QfÉOëÛÛ¦…cMW)HÈ—kˆ'¿Ž£2ëyÒAÌSiª§q_oʦážÐn‹=g"pÖZB÷0Lmÿ€ƒÒeð\^ŸÍ·T€7¥gpQ‰4}^ûGsõv¥%øÓƒq“\o’÷6q–N¥ojÁäʯnÇr~KÓ¦ješ\½ºV…§Þî•§«öIP°RbŸr2Á×3~;MíL‡ºÁ=Ò0–~ÃF~‡ŸKIïGC¼žÐqùçˆôœCø{ž84,£]9°{œ4]©¼v Ö1>çÈf­ŒdVÅ/ÅÂÚüS¨;{+Z¸±£7ù Ré XþÄéË Iì¸:Ž> /äÌC30¹?ŽbÒqa½}û#ßÙ WïU‡´~{ö3¿ 6ü‘€uÉ Ý‘FØ28œ¸ -R·ÒÏØÀ{ý¹ÿM[ÃÜœ¶Àçk¢4žŒþ…@ûƒ˜Ì–íØI·˜ÍÅã m)ÇmN*&ƒ®wŸàJ‰éÌc¯8Kú~÷ÅÞgö¶¶Ìo™ÛöØNÎå/ ‚8±ncým~´šõŸžl¶ËÑ÷½>1óyëMŽŒâþ!0Ç"|wu"·v.Ã…Dmh5ÌU:‚Yþq5bõº/(ùµ‡K¿p:tfЬßÐæîX"L£wųGk¹ŽGa ífÈÓ™¨@ŸÌYˆuæ-phÓe´rz‚ I\V µ©‚4SEŽã+®;|#ö¤!uýû+û¹mÎ ÙþUúlÌ´càñ&ŽÏІYñ_IHÁXzÙ«—óð?‹õWm¹~Ë!Øâ5ŠýîœhÞÐv®'è3áÂúUüÙ ïxmä~ñ-Åÿö@}ÎQ°­(".×Û0'†åe’Ô_üšŠ™0IáY=ü“ûy§Œ¬d9û/ ÉÏo½e‡lüTHX®…-»UØÇ”ÓVö¿õØ¿" 0” çjڞøƒßAc¼×(kæÇ~Áð†~à- guA*F›2á4xâ‘jQráÔÞ1«9ÛŽ}ÐLO ²ÖÐÎÌvôœ> 3¼§RÎüj‹ÕjÒiÖ.5ƒ]yÙŒÝíÓàu”jÛñej•h×Èæyv óŸ]Ä«:1„ü} âûáý›ÛZø-_0Ç^ÅE§g þ®:Ï©x$á›SŸy[céÙõ"ôæØöIX5c>¬yqNhÂäio¾2+Û¹÷….P_«š­t¿‘·¾…OÒf0£¹F˜o1È….‹Ùá7䥓1›â‘oýÙ±û;(7õ¡¾©®#ÇgæÊ§/`é6Y£8JÖ~jáJä’˜ï‰ølf8ž±å :B˽±X"F¹ýÙPa¡Foé±–²Æ7±(oœ‚£ÝÐo«4ËÑUqẇí{Õ))éç$ÂÕétU= ýªA—yYpª>#ÜH„{äéŠ)éÏ1{•;Ä4IÑ´DeÜ1"í7}ðs‘6Ëü"_¥ô؇YÒ ÐîF•û1áÃ®ÑøQb/²äèML¢@þ~ÇoŒÑOÃËXo¬Ÿ2Žíª³¦;ïI@"îMÃõ­l¥ºú¥Ç¦°YϪVL‡âë6¼âK·Pø‘›:³Ýû+àû¹Ä;/.*¿ç„O—ÃsÝ\𦲒vþÒ‚À'3Ù®’|2N§ëÓÝ……töœÈ~|[âžBNõQø²À—¥¾2Å^c¹½ÊÓY ½2št·ƒ‘#Ö¦Á%X6ÜmÀT„ÐÇ:<Üp$–T¶À6·ø´/G?€_?’ð # ]òŠÇüåTΙsK·ã¬“óñ£Kî^L»j‘¿ ¼*ƒéªx§ÏcMr¢4?Wžü§‡óFžÊÞ2Å壸¨ªú)hZ¸ãÀCwÜ cƒ¡!`ë 7ƒª`šÍ´wEâ`J?®¹Z‡bEáþšÍÚ™Àµ/Qgs“/b¦–/ÿÂñ>Rqd:ÞòVÒqòÅXº¿·;Âyim涸¶–Ë‹ì¨Ú¢|Ô9”…5¶¢ÁDâРŲîð¶÷âÁsQ¸êímœnE™tɾfQøÏѦJç‡xî¶s™WÈRÏ8¢ªtû³Lp¡V>ÈìíEý;³Mòç3Û7LÀ¤œwÀ”¶ž À-Üq¾îꊆµfÒ¢p1b$>Ì çfÀ™×KXê’4ì{mÀF¯A®Xôç?áÞðž(ÌÂ%;?£‘ñ ÿLË\¾Ëœ8qMÜéëÆžøGu6¤± +JÉèqÞÄžÜÆ¯@¯îü6À©õá¸üê'°Ù— Ùª.4 ì ç’ËÍl?9«óye}O¸£ZÚlô… ÇIÒXŒF [aæÚ™)bAhz>ê­^Ì6\²¬½Œ‘™ÓØ’ ðhÃU|™¯Ì2ž¯íRcj|m«¬ ç†Cû¸}ú`»}:{¬B¿‰WãÓŸß=Ž…#µ¹xÒŒÃ"MZúú”|m#–Hs†€•|ßÙBfl\‡Îl¢gŽÁ+JÙ••ðçòÈÈ3"{jÍaý¿UtµK,¼ÔûŠ´¡ÍYj”H’òƒV˜”J?Wâíá%Tڙǎ7Mÿï;±Ïpe×™)&àÁ¯ÚOWiï½ÐÚc€ð2¦Ò% ¥H€îC¤^Ž…¼§Ù˜b)ëE ©[~Ì‹ö„˜õ¢TåàO,xœ¥ÑãÐ+þ­îÓ\µŒ8;²¡_»ŠE›!KìΖ§3=Öqêzé¤u¸€ü»/2[ï O IåƒÝQB¯Ä5ZÀ-ݾ–.þM6jÔCÌÛ+PyoH6^Â}2¹8u³9HÅ‹‘Þÿí Í 3±¤´Ž·µ/Û*RŠÃ鬓øU+‰©î*|Ð,‡¾ÍçáWøwŒ{ŸÏX•rÁIxÎêÜ~÷&k=ä¶y!õ"tùø¦h؃Î÷­\Âù)ððy„¼áKnä ÖÎg3Æ'“Õñ©b'Â+;³þ?®;,§÷··´‡––´‡–Ô{î'BŠh(Ñ’dTí½‡†H´PIÙê=÷cD„HfY…Œ!düúþþ:ïu®ó>Ï}½ïsÆu>×]ÖaEúöîGÍEÜâ†[l·h S7ç˜?Ú ß_\F÷ð·¬­3v-½Íjé!»”“B¤–(€}™ Îý%M/ö. 'W‘¯bó°ÓÅü,!Wý·ÂÖoÙp°ÑŠ¦Î–`ùv”p;ÖæèÑ-‚›™9ƒB씽ÓÈŽÅ¿XÅ@V‰\¢)OR_”rækÓ·±ðxô1>Ì!­ã¹:Íá©™ü‰x .‹MHÞ6ÄŠþ­Ôd÷~rºÔ•Ä«þÅÛ…X:F ³ØzüÔÚ1•³~sÚŽD“’Ww±}þBøé±´ÏïÄg‘{ ÛnÖ?ÿájà˜Ï˜’d&¤Ÿà¢zKô!ÝSã -|kH}‡ë͇WZ"I‚j%”ìžNšÓÃþ¸#ù®ý³eÁT1¯…n£ì¾rØÿ}²>Õ@=øeB>ðÐïçH À¾'·YQe:ó‡=ÎR™JůVKÁì¥7º´ù» ™Bd,ÄèùÍ£Œiõ-Úiz k¾æC™Q-ôÍW¡ÅÓvYì˯§ÿe§ÞNÿtX<ç3ñ: +Zœ0sÓKfI“˜µÍƒ¸íÔ½WŠv[VQ_ÏóLsöˆyG²/ùx ã•\pNv;ÌèÄ((O˜Ïœ®œNÝÇǺ蚻’t7o6nJ¤ÖmˈGjé”$Ò0ÿÑòÒð¾lÁYÜUT²w (¾ÎÂrÿjt“úÓ§=GÝGÆt¦Z´TÕÁ+õ0šLºR’©ç=f"-_u<ÆÃ÷à—år4öÅÖŠÊ’¥†¹Äß½”Ø»û=³™uy¹É®€€^-ŒûOú_K.Âþq|•Ê/¤éóöAÕ‡X°ÙÌ8Ž-…ûyH¦œ4'«7L¾ã¨ÝOÂ+ÜLhu b¢†–’:Çsøß=›Í: -œCn¼nBžÕId¤Ò‘ eëÐOu½ì Û8Ìx—7ð)­ÕíùPåÖ²ððml³<Ë\Ù©†²¯°òõ|xS”ƒ;äˆàðb,Š”#ÊáÚ8üÎx¡ì0æô hƒÎ¯p<ÈS1M™ø§/§ 3aG ðá{;¸gKÖ®ŸJ<|Åö´-`:ì[¨W`é“_àú€ É„þeáðüØXt4nðÿz|5hëzžÜcú·2ʼné=O«Vhƒûæ.öHF:{îõ}(,†--ùì”Y¥xáÈjÌ{%O6^§¶KCY>ÁBèÀ#Uœ—ê;A„]V¿…[^Ò1~Uªýe ­…õkf’—Yëñ[(… Y­ì¥Þ8 ýä -^ ð÷ë¼úq;¨V ú‹ÍaØÁ¦¡ˆM–Ó%…»Î³vIݬ‡NÞ›ôªkƒé*á;ðéítv€<Ñ<ìDgÂÆ'1ŒÜ¤W‘.²ñeFxõú øêºÜ®$ãòà§èRæFgô¡jÖ¦ÔbÕazÏœ‡ŠýcË–’ 5©¶j.‘ʃ)?ž°MÚT¬t+ªþJ%&ìh¬´ ¡ªöô¿ùÍÏ^}‚šÎ:L4Ô¸§•Si÷Ñ‚Éû‹æ¡^Tqgý½ð: M¥c¡²$ÎYŠœVÒ`¤‹h8M„’‡Û©øö$êku·çÊã˽ǡû;‡åÞ3¦ÅnÏ ø¬ Q¶‰¶sŸWÈݹÿ ö.{Ú*հ¸Í`þ›M½t'^û½É£æ[\ÅuýÜ)Oî_ûDKª²ib¨5l²&ºúë‰Ï{uèUt€x³F0Ÿ Ìø~:m¦7EÉ[ìêGÝ«ãqÿ *œ&ÀÍÒ™ƒBžÈN[6åZÄÚul­0¿¾:·[1—îhrj¦ôÀÇu6—ÑE0_6›þ 3kÅV@éýÙÔ)4—9pÉ™®?õï“Àî¥ žš{ˆü"޼[ã™áD²^ƒš¹»â–3ü .Û;æÿm¯ Úô!]›å…q‘¿,ݧc€­÷½Û-øÏáŒu‹As‡¸‹l¦bsàóú)k):­+ƒÝ‹tIUM;|¼[ËÕ ×d^}£Á›6Ál®8!v°É9…à4*LÒ:`“—•²]ca¥ð¦}ÑÔ†c6óhb¾­ÉE1åz(ÖxƒqÂN8k|رƒ]céî-+™ËÖD§\NKŸ#õœ5àí*D,¯!,iÀ¶¥jDl‰!‰lú¯‹LIö±cÀÈií·ëÀâv)£â!A×9*Ñ$îw,§o­shËìL¦gb]¾ÔâÖîÀÞJò%ê2Õ˸Ò2dq‚NîF¢áò‘=å#NþšþcÞˆ†KA!Éï¹…Ý«æÓN¯.´^rW;̧ suéF¾ЛՃ"&ô”^Ûýw'‰ÑØO®üF]§•°ú—0¶—»ÑˆŽö_ßSd÷e’[|btWèÌ.¤—³:áL@4¹M¥)‘[Há»i4~«1Û¹ñ­s&ëË‚‰µµ%>Y™@m†¤éü@²ËÓŽ>ýúФnK42ÓŠ¡Õú®lr"ÆûçÇz-ûj7¢êêa0áÙ ñ‹æ½’Ð~2€zNjYs…RR¬BƒvYz¹•Ôåu²‰Ž²Ì‘¥þ–ɰX{1ª=ÆEÒAè:-»Ö%Á÷E7áðÊ)lx”.q¼îŠŠþñ¸ò`ºô–ƒvPacÈÙîF ¢3Ò{Dè –‹¬(§W wËob=Håƒd}+eK—Gag¡W«!f×Zª>o.É‘ZÊtW>`× Dbü§ùd†¼Yïr¶>»Ë9³~ýovóý)™$g×|ý¨cê÷ƒea\ó:ÎdØÅ@¢«Ö¥³±åœ¹F»PéçL¶»KŠDÿ‘¥Üw×áè‚eäþv#öп**§ï…þ–’Ô„%¨Ó;Ÿ6É«áé%¸¥4„,6ZBx¤ÍˆñæP2WpySHzаÜö)3CB“ûV"™~½‚O./¤?~¯ãÜ6S"a7˜¨³ôÎS úHÓónÿEî'|Á©ÂWG³@Th-$õ8”Í‹c_-| © À3 ·m|‡¼³ÓÙ¯jê2Äîúø¨Å%—¦ÁÛÆ)¤fÚ=û‘μØ.H¸[^°­·ÃX!Çv×H:¹â¯) Yßpÿ®å}¹xëÌuPÿ¤B‹‡ŠÙøÜ<ÜvI¸ËÉ£××Y4„c÷¦\d¦YZ#ÿå;“°{ZÕØÂÆ¸âû$?…S·;ÌóÇ24>W ;×M£ê?Ã5mQjÆ» Ç ™ói¾{ðS2Ëœô (>¨[g¾Àï5͸¼¶õøÉ¿SÏ Ü.e¸Ö̉ÐöMS>\?Š>;WK¢rä$ÔH@J`;ú^ÏP%ËšÑ0T .ø^~»¨X_8¸pÇ«|ÌqÌ^ªÜNŒÃÂ í» ø¦ìax2÷s½9±œ6†=ã¹sªÞ¶ß9$@ÅVÿ€M"5Ôa`:˜Å$vš‘HUUêõ–‡T¶¹‘-2Õ°žÕ œJ¦ëó5’Æ#VTÍ.Ÿ-Ià ä•#4Qəژ?Å´‰txˆ¿!Jt=ûy¹[\Eâ´‚d³«"àNߥE9“˜·›—.6T†7fàÖC$_M÷ŸŠdse6Bݘw¯…œ¶úÌV$¹Ráu pf^î¸{—i/¹ î Ž.óípøö_*´ûóqD–y4[ˆ£× ËCgDƒ˜ èÐgߤ`¶Ì46M%yB/p¥Ðtò: ’)©[HŽÞ!'gž¡Óm¬IòÌL§ãJbëÒ É‡éЇdj¼#_ým~^wywch|,ÍêÍÂ&ŸíT®VŒÌ ó ÏSZ!û/ñSw¡Uj<˜¬Û€Y*†l¾ßMÕeI’ÕÎЦ¤um§®s ú½± ŸV>ÃO|Xë™yôëiÚ÷݀܆5Ä»?T,`ßîQ"E/–Ðß·$q|ß5´¼1•ðYrÁ¦ÉžüáÉÀwcèÙ¥8þ#’ì)OÅãªU°;mV”)RýÀf¹<öZšã>d|Æ$ÿ8HSožé(ÕËQV¯pFÄRzG ¾î“¤u‡!çßCøåºË”6€N°,Ë‚·JÍø2ÊkTp.J”¡î1a:\Ö‡óÞdÒpÆ"‹¼³·Íx=+} |XÅep»<„^ƒ"^­DE泂Ŏ½ŠÕ…%Ð,yvQëLIR#ÞÊöÈQ¿Ÿ@péR²ìi)1[‡iQU5úòJéè’ß”ÂX-|CÓmè¿SYD_-‰<üŒï–¯iôøœC¬y®0ʦ̘  -n̆‡<—ñÍÈ2úY5n)]ÆÄã&ôæ—”Õ½!w}ÙôÄ~تi@ á>ÏÅE7QÔ)¤V˜Ðd£W`/Ö…–©°Ò¥”Z;ÛÁ4ÉBæfŸ‰c2OsLÉ:¶n;0²a èjö^;§CY»¼]ÉÖp.¡‡¦ - RÃÀÀdjòÏŸu뽡Éß9}”±aV9³±»î.ìÃû¼Û:•®—ƒE!id‘e»Jë+^þ;íÒ|û}9èzÕÃêÍ`Na:˜Z-çîs{€±*0Þ)—û"M›¼¬[@gL7£yÑã¸ãdý8ï<.ùzŸ¬ÆØ¬0¼Ž-ŒZìªzþ$,lW†Z¯9tÅ{9j×ݽL5`Œ¬yÑÈ$ä%Z±{ic« Î]NJãú*Z .7±>i³ÈêÚÿ€û(ðª3ûs [ÿn#ñÊ’Äìc³h{#65UÃÛÎ"Ò$ãÂ,«ÜFÿžƒÃSž#˜«²G,—“У– ^ÏÄ?3’±/~%v9I“„™7¸5ÅÔsƒ#}|J…êŒ>ÁYjƒØ™Z5šætï}inGèÃæOXßXA²–$d3àÇŽ´ñf£®ï¦~MˆÓ‹rO¹Û=”ijµ3X Ïý×¹ =ÏÑìøì¿~glÍ¡¿Mü‰¡Ê¤çïÎ Õ#àÌ1¢R×®1KOg“·Áðéw9­2)$6?¹°øöYŒ<Îî‘;I·z—Òµü ´À³Uo–ÁbÃqæ‹\3¦Çâ³Ç½x/ɾ}Q¢O.#…ß4élS o6fÒîïÅx/N€Ä¦û“{¦ÃêOÖ{k›pó /zã›$ÃûN“μÀšbgÙc“V6Ý­“N8_R¯­B°B¦œ4¯™‡iofÓ“õ*°¬r65½tr^¬cK)œ–uœO¾@]žw2—¦Þ†4‹·ìáX }©‚4ðfË¡‹¯_î&4ã-³òN ¬znGšn塉Ë1ö°¿Mj—… ·g³¼ï–‘£+ èŸã—™«™äuÛÒ‡b‘dô>ÝŽÍì¨ó‡R,úÈo?Öþ@Œ!{¾–À”<&N‚Ð-m«É²Ÿ>> ÃJýÓ¢Oã@ á ̲¶k6|Zö M/_‡±¬hvËBËu²Äx ØúŒ'ýmˆïþx²p·)yy?‚&‰ñ@Þ©%ô ¾8¹¬H‡ˆ®Ù¸<1³Çøétï7hÖ†¢×ðÄk²üÔ,pö#%iP?*3Tc@ÿ›írøsVúÛÙÔ.°ÉšìqÅ §9ôÔ69¢YtŒ>y¯2úm oôàxTå¡…Ì&xSt·¬2âöŽd3zqo{oƒ–{_ y1ÛŠf;¦a§õ-ÚŽu_´¡ÞL7©÷㹜ó^Rð#D…ô…ô²£ÄÁöŒ>>æ/iž®Ez[A~S‹ÃæÁyäv5°ªç'×*çv?MØvö`¶ÞÜŽ6?_ Ûh6]-†üyïaÖ3Oê€?×uqsÆU £êˆ÷V÷Û=‡ eœ¬›¡L4Á‘ 8ÿÃU¦Èö,+ë®Ì¾.ÉÇ{å— @·í&üø°K?ÞÿƔϴ¢Ÿ/çPÉ­ Ù÷@ÚúÎÝ¥¢E­<ÕrÇõçÝ!EúÅ‚ ·Î€êÿ"ðÅOŽ 1Ç­s¡Š_…z½¥æoà©\)={ÍüYyž´æÆºðC××캳xDS•6.¯Ã•&Id}OIÙ³‚VGØ£ï£F…³ƒëÒ—ì™äûkv±ìiòþ¹8‘mn±¦q-êO¡«YŒÞ¶ º~e)}¾™Ó8j¶AÏ¿jˆV^ÈiÔ;V ^¤³«™ýñ%‰;Ï…‡¹Ñ¡E0 »’oC[úQFDET5jèúSä¿|¶çâdrgß"ZP¹ ¿¬É†¿CÙp¾&ãà¯/ù\w®VÈ“¬_~äF‡=qH ÃOß`¶á|úâ›"8¬7FýNÄN°žÆýkà }_öïØ¦¶¹¯x ZN. MÛ×’EǶ‘ª&!Î}cD¼Ä€t©f2wÙ~r2u‘m¿€Á¯÷’‡›èͤ¤Ü’V¨ÞŪ›ó0ÖÀø@Öé·²±rOÁ®ï:­~] óȼªdj¶ùªV Þ9FîíÚ³µŠr\¸äªR=2Ý›\ê±#Ûº—øØW]a3šé°4zh“Ž,äþ :Ox¢¬.Ùž;]•¯ñ²¾(sï¨ 1m=Á*<Ò¦®rÅd8È^Ô¡-Çaw5£m>êVàÌbÊßð•7L¥ê&àCU¸r©1ÏjZÕ<›XíN'Ì%ª~wÜzIo÷iF7-nuíÄ~'8›Jž5ÙÓ›e\Œ,^ÍÄ,úÎ6M¹ÊôýúÆtí4¥á*ŠdÍ­2Ȱaž]øÌ¹­–ìmì÷R¢~QS™ÝºW rŸ+½»ŒC¼ÒŸ oÑq\˜A>]pÅg6ͤçKºF¯ÌŠ"×UigF Ïs Ï;’éKW}¢hÿÒ­ íKê`¼þ!æ›L¡¦ì žºtÇ>ÖôËn(âX9ûF¬ýLpyØ ü1ÅŸ4Ø„67·ÃÃE ó5\í#Æd%àÁß)”Y{3ë×2ÎjžÈ1Þ‰•.ÁLSæö ¦àÑ1üQvßÄXw¹×™–ç tYïüzg=ÈŽÅpwŒÎáìâðÀÜW¯aÎïh˜¡—ÌŠ¾ËÎÿr²ˆPϘt}´›ª„«bnØ{Øé}]º¸d÷ ïõT(¹fBmµ §úŒ ë†~³WÙÂêçíG×ÙXÚ¯7YŸ;»í‡%ŒŠÏ¦ãnD€[Øt-m­©ëk²÷ŽÞÊR$7ð¦Åx’9º‡¡ÿK izµ“?w0n“Zûä…Óœ¥šm8%ªä–ê’ˆ÷ÅÜ%â“8×£OŽÅµÒé'Óè¦árf™°,FGÕ,öx0Ötqhnô:2œÓ Iu§IŒÄ’ºD’l<I³î(S±èÇŒ³!A-¿‡ ÎàÜ{¹Tu¶¨yÃü‹^ÀHÙóàßÐf8Y™FÅVfodËáý,ì{†—¹O°æ¸05h™FUÔØs÷7RÇ ÿí9ðûF0_OÝe¾&›Õ¿7R„Ñ£ÇæN0[¾]TK‘Äy•‚t.™¾ú ŽðÆa´Í~ü$Ñ‡Ú Ð㨠<þCæuñ5“>½Ñƒ~ʸÉI©¸C_ˆÎÒøÆ m)‡‰Qgò'Þ„lžQƒËÍ÷³£‹nàö‘=thÒˆÑӷǦ¿àÜ}sšxá Sy›5:E÷éËÑ)Æ´ú¬±ð¼ËÏ.Æ5>îÚÈí HÁGo­é`Lf±Ø6Ù‹ »ïÂ&ý3¬hŒw:×.'ÂC#; =#=Ä®úÒBÔduoÁÓæ~¤¯²˜^¾$ü}>Ÿ‘>§Ê^ÝÿžäÐqÒ²®m©Â4:_£zj’íi¹Pbû{WÙ£2ÞtOî1x“ÎÌ‚Œ’2d)žÅf}1²§©;K`ÛÍ|j¸(†MlxŠV¡sèý}¬EÂ$þ YÑ— Ú`¤|mÝIßzßv¯³ MW—BÓsQê=e.ØM ‚ÜF&e¿,àß?ì`ê“›(¼ÓˆÚ×Ýcµš)àj„ëm™ ¬7ì–ƒåÚF®Û¦NìóÁ4åÙðïí&ò)ä l¸õÛå¤U'pý¥>”¹ù’­•è`ÆwÊ‘ó)¸wç;¼6Ê) 9bckI}MÆ-Ï3ˆ·ÞLæÙVª§q“}ï¾ëÐÕ¦ *¤ÿæ ”°ÒǬ9JÔ@½"ôҙ̎ùtáB~ÈÛ,é/Öåàf|RvØüÛðØoú Æ߉fßü˜þö,©Ð£¿DLÉHYŠ„Ï"ìÈY‡£ŸÞÁé>ItÛjDž7ìÂôdA’>ÇŒ¨L‚ËúÇÏFa²â¦ò?2Á6]¼ä¸š|Ò Á’éáC”7¬ðƒ½/vþ—¹vߔޥ¿²qº³pë¢"Tg%ÈÍ ù¡~¸É]´Ü*Áδ–ÿ<^žšÁyÐZn€å ³¨óÒé¤nš&Ä„ÞdSžÅãªq²vx;Ü}(Û0®Â€øf=ejò“*‚,ãÂ{”aíN²5Oð‘¦*šžµ!²5—ÀY:€I2ªaÆèC‡šÊ°…ËYíƒg‡O¢V‚£sç6÷Yò0èO 4à {IÚµžx½ï“È"l¼«Îü3ó>`kL;såýcX®a©cŽ,U'k 8ïoeb™ÓL‰=‡.ÓX°ˆÒ‡ía›€¯Ì>1>̦rx_¯_ùàì‹!l½wÎW9ƒþ71:œ{ã8®äúÍ7ohGö§µ³)»«™˜ØÓ |tîÛL}úzkvŽ»&ÑŒlJïeþ­ñ@í´DÖ@Eò¿<2“Ó~ŒÚŽÆÞ’QÜugO@ۇߌ…¦±=ÄÙÒ}åõ1ò¶/X;{iü/§¼ñš!k`0µþ&G]W `ÙSÖ2ø ó…[à`q4…³áþC¸tÖ…+Z(EžÊe•TþþÈ7¼)“þé×И»˜|¹nÎÜXãU¬3À=½ *Â3…¬íÙ¨3°Âÿ[XaÏŠ®°ÂÄ£Ø÷FWþ¼^"ttZ/Ćþ€ «[àË=o†u „—‘Ñdx¿,¼·jà\[ò ƒ?Ö2žêTíìa®êº ì95ˆÿB^CÅ œa²tLfÁNß? ÛT‰žíŒììL× 2â¯û¡4Ê÷"d¼gÓ¼¶Sž?ržåcôLB™FÙŒö À»VG’úhƒ­·†1/³·uFã,§eà7L“\Ÿãª >¶GDŒ†K €œ"¹>ÁGývàÿ3Ó·CÍ ÚÚ¶ÊÖ³wBç`ÑN;0R§þÙéh1ѱ/£K ©XCCò’™1·p’? –èD“9:tõýãÜoOwc?ÕÈï¹.xã턃¦ÔvgPMþ=TFæ¬#ÆÎw¤³b’ð”ñ¦ê^ JlÚ ¡ùªÔgöBææÓ ø¹i{cÝäUQ¤îá°”Š®N„Ë3´˜mrzðûÔ9¦Nt/wƒÅm–9ÓŠ7¿¢ç£Vt‰“F;-KСëÐôÚ"<ïiA/•C£m›8ç!N<ÍÁ–è˜ûæW&r¾V…³©w°Fé×±õB^¬yÛ|‚—¼Ýd_ÿ7S·žIŠég¸ý©LÉÃ3ø_þ9ÞŒerm´À:æÀkFixˆÉxß^Ú¹ ^s ز©}”À‹¥ìz¥§ðä8ü—}V33 å'Û%GùÈ~ÝùÐzè ÌR!ïšRÙÌ·Ð_Y;ͬ1FHv[­ ‡Ž Òg‡g–)¸2_‡Ö[–áÞ¯ïqDþéäÿn…·MpŒ •«‹ …‹¯‚PZüc¦èAŸŸE)°q|6üÆ/ÑÞªš‹’Ù‡aÓW¦às3kš°^¿œ`î Ç€èN.®+“CÁöÞõ3phÅVìU­ÄÓr`׈9Y¥$OßY™0Ÿ¶<ÄÔWì¾åò4e‰ Þ’9…?‹qøÊ¯qþ,=Ì(}0dë;/Â%4Lh•‡Æ½V“¾4œB{UíðkûZüõ,þËïø4ìîb¨ãàihÍ}ŒÛwœ‡ŽZ{ú/L°a=ÖµâãÔ)`. AE¦„Pž´'P‘Oº›Ûñ\h9Ì“‚³W€²ñglŠ.a{NÓÝÓíÙð³Æ¬eËnÿ’‰+Ë7ÁvÍŠªdäœþa¾ ³r †é÷…()[Ã5߈ÒËHrÒ_x.Ìl©¬„#FS m™ªT§“X;jÒ[úÔÈÃç`ÔÖg:bÍt!¦·6³ÔfÀE?Yb_Œýò¹§K"˜ë›Æ`fø~F8ÅŽ~aèÑc&0ƒgÄ dÍ+¾±±Ñ èäfHìf©Â  ³†n“’¼N¬9M"¤œiy¤!IedÈôï!DýÃBRžIî-ÊÚ'þTþš€åÄË#&FФiˆúGvÿæ|H¤O‚>CÓ5-ê²9‹é¹²“½ÄcƬY¦Ç=²ÓQ±ù„ÜKÍ84ÿj™µC]±õnR òo} –ï6l{•é ˆ ¥Çж©Œn¹¢•MRÐj›@çPY}’="FŸ,:wv$Ñ©ƒÒsâátŠi1ræ‹ñ™)©qºÁzØ 0› ß8O™È3vì©Íz4ÇwlWÞÆª—…ÃŽ%Úøäàʳ* 2‡•9pQŽu©)ŒC{X©Û­¨ªWÌ-û”Œ+ž¾yQ5ÈkœÈ8{$0Ÿïû€[¦³fÝ) +0;û†z£ÉÙ¨}+–»µù 3Ô0•–ØÝƒù_g`Ö¡H²ùÚv0{ÿ ÞßRrpy RÌÇÉž‘ÃUÁÒ+ÐÃ6úK1ÿÍ›þ)|# »@çŽ l—Ò$g‹’Á©I‡Ùˆ²ÂyNÿ*¦Tâ +1vžsa³)÷â%b÷˜ÞÂr´ÝÙ†iÇSÙH»³8T߀.ñ.ìЖ°Øõ7³\p3Ô4?f[ä ž ’ç¡(ð×.¤Šµ›F4-/2ÖlžCEy?š'D–P†‰¡Lx0ÛÜ|Èž¯‰t½¥8-°9ÅÙøXˆÞÞhŠzÔ’µr3§í›àkõ ލ™5ó>’T3µoW:ÆqìýE°.²¶ÅL%÷·a™Úˆu¥g>eB ¦a"l·âAvòûÜÀ*uh.ÏÂ?Ûfãñsñpð+Ûy@ ÅŽÝÇ÷/n@¯µ-ÙåuÏþ…3'cðð%#º4½ CS©}ýJ¢/7ÄNØ›µùáÈ{H¼ºý 9á|ÔÀD—4gjÓS2·pÉ2¬ùv•å7dƒŽ¥0Újé$÷œ‚s ¼\z€Ú6d§ &ÃJn;²mà'K¿Ÿ¹·\bš7Þ†»:®8`°—¼Ú‚F§ðtÜØåÔÌ–Ž0ú’Ñ3¨Ûc'¦ÞÆ“UÙµ‘:=Þ “Øø÷{;*›ê‘Uü¹4)ÏhòÙ~X_QŽýßHÌÅ\"6¡K¦ËacÍŸE1º¼J›U ß¨œ-VŹ{äéú_I ûç;Põ#d¨è‹¸/ó1zú¾g '}ÐñVIRß?ÌÉ"Iïû¯Ã”òÔ̘ôEGÀúM"GX+ž‘Ò-ÃO Ùu §Óܳ1fžýÍýÀö¹,@¥Šðɾ{‘€S7y€½j±ºÇá¸åÂe±ƒí[WÿA¯…R¸"Kìö0ƒ<§[èînI²~7ÀâQ Ù™!ù lÊ6C3qV¹É”sœ©ÓÁ×x1¡§n ƒñc÷ÐÃ6ë|lØ›)a`¼]°¯¢V‡g¶k ÌÝœr}È¿w€í[à…€¿¨¾-/Ö8™~ÃÑAìŒx…qSŠåLÅg]¿03>ù Û}2 rÔ´Ö‡õäÙW5>€è§©Ô]š—¾>ØüçÝ™ôÚ;nÆÞ!®QI(uºÉÁ±ÊS¸§1î9Ä—u4´íxŸÐ%–Ái4xÕFÁ”`Ü-Ò¬º^¶ÃÓÇ> ÑôßP¦¯™1»†´ 6ã'P¡èf؆Ü}×™õŸ%éêâ`\ìFGÇ‹æãÂùv4MîÊÂ+³iĬXöˆÑnÑrÁ}Xç› ½ËUˆì•ã(âOo ¨Ò³NAÎQqPl—‡`…elZï:›ô“Üö {ŒÍéû§‰Ø'×Ï’Êqìªé‚Ñ &”ÏøSvê&]•N¦d<„-§™æÍ·à¤ùV,´ÜF;5ó™=u˜zùÓk¥F¿?\@7áX55­Ô¾‰M4>¨Š<žãÂÜŸgŽHÑÎGè&™œ‚½78Š3I¼snà‰ú‘ñíVße’øN’É}‰¸ÜH4’A»CñD\d‚¬àG›Ó® ø£|ÛvÓÇ‹¬ˆß±—ìËIÏSr{ æMê‘Ý#õ¬¨Ùðò±aNö3éílvÖ/°È(e§?aœ[Òò¾RTps"BÇäqò>.’¥×›ã¡¢K°Öø*ÜzW†‡„c™®o2œ+uÁ(!±\LèûÏSÉ¡¨™ yjì+™‰Ö÷ÏBŠ‘ÿ®ge¡5p6ŸÛŽrÁÚ¨a|²=»á¬˜sœí§Ö þÌ+Ã7ùe 9^ ¦r7áÊÑx<Ü“zkY§¥LùÁN.Ø ®«¨aÖjR¼4e‡¾`²ÿAÆ!*ƒ+M'W·ÁМG”û‚yV{-m­ OµFôÊ7'7çåBéevVT4V´'õC ©·PQ/ç’1#ª;´¤6å“åjôªË]fÛ"CîÙ‚ÜÎ?Sé33úyñ1àÍÇök^Do©2Öl±¢n‚MÈýfH¾d>EßY d·ê¶ŽJ û }&/Řˆ­¡*¿G!0,ûb;¶Û“è¯*t“ëQŒ:#AÖ>¿c'×Àë¨ÜÎ"'¶„yïMÍÆ2ÉzÙåDÏò~ôÝÃ1^­F¦DVÁ¹$Èþ°ŠŠDCT—v“^nå*oÕÄ!†¿–0'fM#ƒ‹ÉÞ0{HŸ+†öwÀçË}Ø ÛÈêžÇuQúvýü½ÌàøYPÍ ¡GGZ¹k[;!1pRßä)±µ› ì£á[2™r‹‰Àì8 WUç@!5yÒIŽOˆÐñ!ÀŒeüÔ¿Ñ”pöÔ“êÍRôyÔWª·\~oXB«{`K¿ ]ÊÜÂÖ£ tèÞFòÛ>fMÇ<ÊÊiЉyäí‡AÎ_Dqf/f®8íëùÐCßíÏ`ø?kzY<M&^¢ùFQ䵆2õJ<‹÷ôÑ9ñD@U™îWÙ ‹Äú!²d„ûþüîéñTò¹®b+¦—ŠXm¶‰òuo¡®7¦£–„)¹y SrFÁ³à l/Ö¢›œÎCYn;.¾¸9óçRÕ)t=‘…¨èr2õ`>¿ˆÅéRä¥á:Úóâ«›dMVU6Ò‘zrÚóv:·rï]cÿä¶¡„è1Üh}9VäÛM$ý)Ú¤0¢‚Ž9òÓ?'ý€[›ß‚m‰ž¯¾v ó~ Jß¾‚¹ðsHÔžCšøò(ûBœ¾Ö÷!Û®{Ð £ë¹±Oª¨½-®ÿ%CÓ~-¥ ‹pý»xúOÍ–ÏÞÌù”dI¾ÅÓêG}ì|Å3ïUM¥ÓŒÈfn(>*†œ=3Hß´é¤;#MÅsØ®šQ04ï…á<[Òý>=¤7“ê‰8Í`rEö£cdNÿ=—»J“ oD‰Û—çì*’F^\ÀÎ)šÔ…J·g®"ø<…zŸ¾ŽÛè~·Jšž Ù`´Ÿì$o£„¡²y]ú¼ß¼sÁ §3by¢´6jíÝÀW6J$0|{/‚Ÿ¤¼’%NÝsˆ›¿'ٵɟÎu«‚Yž™äÈ1¦$ÜÜÇ9hÔSŒ[—œcgÕêp|â鯗N iΡ½÷AtÚ<*ÓÊt·7P›¥ÆtŸà)ؼˆÚö ”¦ Âl§sœÛñg8½m“Ö€V}¸ßó5“c>“î±;ÌñíßÀý Üð³‰V‚‰¼x™kñƶd“ç{œÙ-Á¤oÏU{£ŽÇL\_4f®S'³ /AUŠ&QºâÛ¾ISC‡øß¬g®Aspç;ün¾‹F¬Eÿ§ `ß›ô:~·÷<À‰²p3ñ!|¹ÂOT|ÿ²UJ4ʽhÁ;§OÌàY ë/O[ªº€Ä䓨`—¬3E-Aîg÷&xÛa¿)„‡ô¾ª¡"xÖo(㼇ûS…ñrôöè•éÔiŽõÙlÔz¸lo"…5û˜ó—Ü…Œ<ÙLGçö¢`_78K#áÎ^ÌvÁ"¼ÿ)”œ‰Ú„³/1?+,éœüðt¡.ô¡zÇм˜Ûj‘ŸkÖ€@Ž'sá@69¶:ïø”£‰Õ´IMœÆ=¨%CB†ÍÙ‚½Õèk3…TZ0_«E˜KqÙ¸MÿãÉ'AÃ{ÌH[Äf÷¾e³3•9?¤ß¢pÇyœå¾‘ê§lbXðóKgM.Ž EX U¦?Ðîý)ŒØ§oVàñ´L2ëÈ&ØH…ìÑTg¦úh‘c®¼d>žiK#îtÀ³‰4ò>µ~o)Û#”ÿ@) ªÔ±êþ}ĄӒ+&øâ‚ IÓ¹“¿Sªf8U¼RLëŸÂDÓ3Öã#žÈ^b‚ £ÊŠhŠÓ7×§@˜f¶ŒšÔ¬%yڒŨ¿¦GÀFb/+KçšeÁ#Q~ڹ݊ڭ!%àú’õØkÂÏ0K@ííoè›íGû:Æ tQžø<È:ªÖ@jó)îõ! :ÕÊŠžþ>ÁdJxÁ Kkê-ζûì‡Õºò8Ó°}U†q¥R»Zs!?îr¶r®Ò¹20§W´ët ýŬü9“ÆK¼‡ý‡fÀïÁéx)2Îr¦c¾þB¤ûŒ©ò¬, ½8~“£ °9þExÁl”‘ÚåC³ó‘ÐT5fíþwi'@“vƒÜ=ÌS—~f†!ñ|Øþb|:ùÊÄÇîX·b qʤʎ'\a½Vj’÷¢ º~ ¨U(’ 55úU·#þ ÀãnqÎçY‚̉û¹Ì­½}ÀSw,Ühuà¡B/*œÆ“æÁé Juúïàñ0jx>hüe÷Üb‚ßçàõ¦E”ß»=±‹—®nކðÐídþù»œ“}' ¹Z¯¹5€ª—**äê‚Țϰ˜#ßbFÀ.û$?ÚËk¹ÀhðOˆ«rCíñhœÂG– çÒ½w—R«-QJ2Ž–­ËçûÈšiŒ01Ç®SHÌîmðr¤¬§,"sò 0Ö@ãÆÿË83kR_²Þ›O‚Ó˜&ɬe~›¨¡Är=h›ŠÄí®Ð¨&¡IötÅ!)*,¦ y’óÐ`Ë%TÛ¬N8Æß˜ƒS„I“ÂpªR/þ«—ý¿·Ü9Æú<žA œpÖš\h<Îþ]/O£þ|Ľ3ˆÔ'^ü‘Ýnœ>À„†¹âæ8²¥ƒK¿þxË =#CT£Í9û…ÁRWϽFf˜x‘JSh€Ø auìr?áÂ}OXñÀ=ÔˆäbÍÆ¯ŒÿÌ+°töf¼t¾]NMêËd·ü&æ­Œ8YßÌÅfÈVõv8í¢E?þ•@¿-µ$o’ ¶u±Lry¾Ê¬c ÞhƒßkøZiMSÓzÀã«:y¼€¯ çR>ݸ;ÓÁÖºv8¦’‚¶dMp9±RU ŽõèëÍY ÷škÛAøý¬ÑJ¹Ó“©àÂ*lO:'1ì~x‘°¾…ýå,o¸ ºÔì±X&ÏÍ?dG¾9_„¼Û{0å¢,…¹‡™¸ªùTQþ(ÚöëÐ`ÉdÈ| ïöTá¡›û1v²¦¥~ÙÀ3<¤úRoo#lr˜Fý¶€ùw¹V‚TAµŒK ±-Ï ’j×"oŸ}¡š€»œ…ìÏÄ3ûQv¢¤uÓ˜ñ—»áɬ)TÖû(~®2¥ªëŽ³çŒ§aÜŸlÐÆƒ³·&Öܓp÷öQܱ•µy¥Jlo²Oà„Ûv‰ÌøsÏ¿¼ý÷¨ß¢,Î%Ãð<‘‡C6+Pg×Bf¿æ9›ô´CvJlÛp>îN˜MZly‘/5sVþ³Ö¯“üþ‹ý¼XŒ¹µ`[¦HÓ3·áÊVqðªw+ÒF5:O>ª…Ò±¶º†ÛrK*ïá”Ñ@NvÝÝò¿ìÉݱ)ð§.“ê§b8ׄÔÛê‘JòqD¥‹êDèéh5›Ã—® ?X-:¿]‹d<^ã _GçG dp‹ ÜŠè[$Mêßñ“±¬¥ÞSxRZ Ê÷/¡hÐ6r†ÃÁ/ˈ›>ùÒ–žŽ¸üíX‰cÍ›bTkl1-wk OaÙ{зÏui_ò\×#7w'Ñ 5ܦ[k`ÀBžN;Ò­fAIÙ¿¨Ùá:%m¸œu ‹;U°Âvôä&B³€‘ ‘¦O¾Õ‰àÑ™\i}ˆzW2»^lŸWÃð÷Ä!&„„¤ŒSlPÅxR׈—¥ø‰³a3Š-#ÇŒ\év éìé`³@‹ž¶}'æ_A2hŒ›Y+rƒ†$hˆØül*‚Ï§Í ÛŽv2ñeF0/6žú%Bû®ô3VuÖÐò^ìSÛC•Í*±fD‰j¬"xIu7µî#+¼ ™J}<¥RÇ8âÁ¿N%NFÁÜI÷ÛUC÷G©2«n.¥ü þàS­Ò´‘ŸFºùAb¡ ˆ-#™¦ñdù©x²fÚ\c•OõÚI‹À~òäq<Î~Ãiz.}©û‰ œ^=˜»ùÀ¿0ç¯XN&ì1æãŒ×û‹Pµ-"›ÙÅ3èm©^ °¦!îÛh€ïöï¢tüà.ˆù_“éÞ˜]ô¶"Ý4É?ñ8PàT‘3¶~d,;3ôŸ¬÷ËHˆ«&™‡‘l4§ ‹i:Û8Ü f÷cáW}Š"»Y!°´ÎæJVÀG1+Ò#׌2ªÉĹÍÖ1ahlU—¤v=Qô(. ç˺!nW’KoЩpÎK‹@Ø©h,SW‚û÷gÑ$ªB¢#§ ïÎ>¬|‰¤„IË“T¤ D Ù•‹g®©ALuÔ/†âÍÈökf¥åFÊ©Õd^;(ÒŽí ôÒ=©á§Ÿ½ÀeœpºÛè>]f—AÖ™ŒrD.H0^7™þåçp`çbªm¦H]¢ ál»Œã ±*\Ž#ÌÆwd™ÖK&1õÕÿ±”ŒèÁMØíŸN 7ÂÕßt¡²-N®… ~˜öJÊÞEÐ £UX4ç|9jNìšìÕê_8Cˆƒ9ú¡ÈÛò•Ê}†ÉÏpÇï.ûÅ^p+Dˆ“’,UajA©G’Äl顚ýÌôõäáQÄ5—)Ä7Á%ÔÉM Ãq (Ú LŠ]ðEw#Yª Ù¹Gl©ZÄ2ô~~‡=CèÌÁX&tA,Fì¨á^ÏšJ_œð ú Ò3;3¯^“$6<š†‰²Œ¶ o¿)Vé"—-‹÷RÕèvžbvúÒ‹8Æu$;‘ñ¯¦œ"ÿ•8'Ý‹ÙÚ«ŸdÂË!GúeÐu>œÃÀyôÀ^{âóE¯àjðlâsEX¿ôâl%ÑÚÆÒp».f÷Y¸$$OÖIЭg l¦/©b˘_Êfh·=•º{ß©¹oèù˜DGÛŽm5™håÿ7g„æõIòÉäÑhmÁmÏ÷àá÷Väôâ«,s_˜&^œ â1á¸D™þO¹8¹Ý~Z<¼;×wæSÛ+q´êÖ’º_ÄñaÇXT B;ÊÃýs©Mï1ª¨eC.$W²Wf•Ó÷oÖC†éˆ%Ч*°¥°žiõmø’BN¾ ]\°»îDÕêR˜ÅömÜ7e_˜âËÝíIÎíXm[E³^ê±þ[ôñò©jtêÌeírDèÏ>’‘ð~.FÏUÛA> |8½œœ «@ÚiM2†ÎátÖž6f9ëGºîƒ;ÏÁ’Ö^X®Ex°?<†=í»,¤wg$¢±ÀO<¹1WA‹®-#¿Á@jÉ<‰èód%”¾x†-™dö•ðYÇ”h*© L JiOrƒ'ýsNŽîâ…r®QdÂÍŠüÞó;o6²g¯›’üOÝH4ÓÈ×Ç/à]®+•³ö‚7ˆÊ6¡¦€¬êÞÊç§@Gø8PÛ‡¾váŽéYX&mJKütË xÚþH…-}rÔÓ\ÉFqí gU¯¾cþ¶øCæZ¢áF µhJ¦©þ3^YoÄI£C¤7¯†,­du†:ÕèʃS3…è”ݹðùò*jÓØs }½î@Äfºõæ,xóÇ*0‰“œáУFfD_ƒ`õøÉׇCÖd¹†2aãŽÑ¿ÉR`õ €É¿g€M_æÓÑ’¸¡tú…ihä%Vâîï¿X”2/ü Æü.ù Ú˜æ¤bœõa áíœFÞnOd£¯eÙq8Ý]Ž«ßâŸ`²¾[ŒX”vA´í>`…Ç¡ ÷4–¦ÿã^ÍÏÓàdK0zÇœÿ\QveH)³ö'³–øÀúˆíDæð2þà {ær[‡ÀTDÎÛU¶?ÓÚK7«96·°¾JyÒ U@ýJÊŒþû ¥Ö¯YmïHb<0Â6.X\#¾‰­ÌÝ[½(µ} çßç$œX× ý_qbÆÆ?œµud6ì4‘Ž•´V,.·õ?WÜzª²«*ÍœùÌ©]óé-þ)ðxùmî¸Ï–Ü{þÁNÈ“€¿­ÚÖhÓ»Ë}°÷ вNž¼û'½Áäî|ó€QÖ¥Pqq3¼~¿•©’*â…LdïÖ79TÑŠþ¸ÎC®9Š‘ »AÜ9žŒÍeáØõå Ø“¿ì?Aز3Žêî‘€ ‹GˆŠt]û÷*ÆñK=ô]ùšy@ÉÜ0ê*ö/Þ¾ <6ïàX·Q6çÊŒ<äšhÃ)&™ô‘[°þí=ávÍ<®;\í‹ÆM>§ 5ò)Üø2“Ú4ƒ¬èp¶‘ µ…ßἄS¤€WL˜¶+?pF°!å µ¥þ×—£§Ø.¹WU ©Eœ¦k ‘—zŠÔt Ô„ïvø'z‰¾{x˜JÒ6‹)®È…Ÿt¬Žd3Â4殸cmžkö4Žv6™Rõ¢DÂì­g¾¥¯¥"éå¨&F T’PlP³æ…¥Gi§Ž†Ýí c£t“ýt|Õ«Lƒn©Ó®s6øáV œÕ§úÂ1מð‡Ó•«SÐ|–þÉj ïÊî3ËÇ>´ohG¹J‚5Ú‰î‹Ï^å8Êv´l.>,YC¯ç2Öº}XØ'Kêôx©_‘™zRd73Ó³kaWÔylפ«-ÙU•×ÐÑH“Lñ5¦UoáåÒ`˜²ö#sEÛ˜ª×IÁíŽØv¶ ìäÔQyÛþ6ÿ‡A|rDT0€äVqeåéÙN@4ïT§/ε€BÙx²mßBF_E Þ-W%UëqCŽ<üQ„ ‹×¡SQ½»ïÚß™ÀEOâðq€üæ·§¿OI±‚·Àw5•ø¸gþá¡O…è'ÇSìùÀ¦:¹¾¥¦³êk*H%7Ô{—“”wˆH$§;µÀ/»ƒUýsƒû|Á4’6dLïýÙŽcáFT¢q }zx±ew!~J,Ñ–°¤ oýæÚÞ¸Žcª’ô™ÌTz-V‚Äþ˜F"­Mà³ü²ömƒ†ÄÔ¨4“ðÏÙdöØm>ÚG÷ «¸hò7ŽÅ—²1z˜ÒÈ •$+ÿ»lÈ’œÒ¾Œ·®Ÿaö¿;ÇáÕË Š#Ùy|iT>=r%eEº2ã7ëv9yØŒ¹¬ö $3âÏyXÿB2ÜÛj,¨°ÈMHqšIMKÑð•3}Ô#CF\—‘ÃSɃ †DR/¥÷É5+È‚»Ù8Wì(H¿=Ãýå¥2*Þ2{àòЕØâ‰g°aÊç¤Á?£lÀíïൿµçÚ¢†w#»øë<}óýð•¢7¤®ÚL’òñ‘¶:›¦m@kDDHÈØo,ÿ1›næáâài¼Ý|•›{ÝrßÀ‚»Zxô¨·snô˱G}»Àµ`düiêQø^ €î®ŸïóàŽ4 ¿ýø¸/a>þ)ÄuÖ½ óµ^žˆ¢A½¢ÔNTWØ?³“ç7Q? Æ\×;Ѥ·[`h|xžNÅ0½i¨¼š|î/'úåãP»7>m #MExàd>“5p Ït®&;ÆÎ°ú]ë Ö)‰±4ZLCׄƒá¨$M¬KÆõóš`ÌÜ ›‡±re:›ïg¯­ÆÍXfÕ}®ß3 ú­4ˆÊ§²íŸkóPCêºøŒ J/ŽÍ—#r”÷—:~…΄%˜ºX™ „Õâ¼Ø…ø×ÝœJSQ·b)½á4F8ZÐÄ}"Ôî×7|-š «éixý×ÜOoÄÜñ+ ÀšÈ íIrtßsAJVí %%þtúô$8;ªž‡CÅô›°îÎr8ŠWîÒ¦£²é¬™É^ºzA Öwó°°¤œ.ÃK%¬úÎnÖç§%É~©¡)òøz¯4ü¹tFR¦à¿ûOàtTÞT›‰¡žÁùñ¬°Z »Ýã.ÊÇýbV÷¤ ÇËåÔ>j:•\|òïþb:Æo°†ÎéwR LÊ@©÷!Þó‘$þÔk†/‚rÌR4L?Íhy¥ Ç-ͶÖ"ºÄA€Žò.ƒW[·q}óemTêØ&ƒnVaººþ³¥yS.2oGx(ö,Ç ñM(ºr#xº§Pñ¬LXð*ƒ-ù‰'Ež ëºgœ§E°}Ù.Ûû ^,žJW÷Q5"ü8~®›-G3Û@ºïÚeh~§ß‡‡˜á;R4!A†lþÒŒzû4H¶ÀFòx®ñ¸¼l3„’´iÀ»lfFüEœz‘]áv†ÈP\ûÆ:‚ýpC”‘ú&HÖ=| 5_ö’—óµès±Eìù?ûà˜âoœ{Pˆ<‹îcu?œƒŸµRôÞÃFvÚEì[ÇE©”õŒÆÆFQÜù2¶@væ˜ý……%i¨Q»Ê?ŠÑÞbÈJEãóq<ójˆi¶Ý†‚'¤©AL*sÏù5›-÷JߨX‡5L´$·«ÐYpwÓ…¾“ZG3s§:Dpíq¦9Õ­ŒUõ½lÑõEtr=Pçbï"øŒ•*}+3®cA·;m»ïÈÁOÝ̽«–Vñ’™—˃ý¢&°Øo ȯðfoðóÍÇ‘0½â „õéàüéí×fÛ±Ì~~˜[‚[†P°X<䈅“©¼5™¸4TŒ¦ò^Ī÷žhQü ;ïwb›†6ùV² ZÓ_Y+¨¯Ï/ÆCü& ¦­•¦¿äsžnŠ@°ë̈¶!(šþ¯[NνRØm}†[É ¢/0·ÅÞµ}æ‘`²¾sqIû9Øõ«_è#ØjBíó>ü‘§A*,_q~H]s›Û¬Aªð€5fí;ÄJ÷)Á-%Ì·3 lѤ~ÿy+?¾T NÞ©ûÁ&! vܪ€N×pPBŸ^'¼§}w¸Œý¡Ûèr±×])F­—Ùp~_1+Sp‚ü¥È÷Pu²¢ß¹îy˜ô»7cÇîql4W¤=¿Û˜½{Iʰ‚²vä“hy]‡µÇí™Î,-8¼t3É4´FýÓWÑË´þ½N!s~V2y>àÄDÓ¶XtÄF™à9Ű^á§`¹u#Óð§.ÜHÄЖéän:ùØ‘O»èÝasôeƒB÷¯ájÏCwÓf=ðÍJüÑíW®9@²Å³WC'LW‘ê©—Ú“"kØ9WËp_F¾é±™½Ú¸.ø «“%H®MagÍT¦Ÿã'½á½ýPÁ}n’ô¢Ø"Lˆ»ŠŽ½w¡î¬4M®Ë¤qœÙ¤HЯ¼þ…~§‡p‰U3×ñ+sá84ï âÉjón85!Go !ÏüóÔs¾9TçL‡&ÁþÑÃRÁÌ™½ØWõ¨'¥N…Z=*Ì2àÏÚ"¬ºk^C |[C‹ &}r ´d¹¿ëבß+ îj,ÔŦC­ßF4î\OOíÓÇÛVáÂW/ñ‰¥rtÍ™¹Oš±èK8ËQÎÖ5šD,Úƒ/¯|ÄIo>„î—n²ñ¢KŸØBôiyºd÷Lü&2…Ìû´þñÆó; wÆ YÜ~c“#•&Ÿ?¨Ÿiìj;(vú‡o\•ˆÏhï3 7ßž€­ÿÍ ß¬ 6 ùè2TÂÙ¹'…}:*@FÖ}Àê¡YT~ÌšvçBÏ!Xi¦þ™9Ì'ÉXVÍã=zX®fë• ¾ÇØÆ-¡žŸ7aÆ„öÄ@æÖ`/_ Bf t¹˜¹É6#óæ#öŒMÚX…%w%áÜÐZ¼0–Ê ëGÁtÙµt£Ï#&ýå5è¾òLTȦªcíÒã|´îà6jéúô”À]R—$‹ðŒà} üñã•È`NŽn!—÷aŒäVf¹”ãìÇŽÎ=ÉÞ÷0®U—¸…΂õ™«EžÝÞÁìÝaM®‡Ìs8 iççð.TˆÃó6 ÷J_ÄŸÒ`Q'ªu*|J,‚ÄÁ)Wü—±E]Їý]±x_+. syÚvŸÄöÝ匹ÄcæÁ‰0åû{|⬙ª2䯋,™wØ…6Ç/&©6¼ ¶3p½÷ ³žrSeŒÝq?ù*“þBj¦>Ó<<™¾)µ¸íü?ö_ÖU3” ÚŠ¥ú´ŸqÛ+AS–jùttÜAÈ s.Ü-Ù>fÔ£}:ê–nay»Âîœû°çïAì} /\.À@•ýåzy»X/µTÕ&—¯Ò%“Úp,"4×±bWdYçÏÙ+ÕÙ°ú•R¨Çû1?Ùc…WáêùfÙ¾cìæÕ°zv'›d*O‹·º¿WOÁSÞwÈ+Ø m½Àîr÷ÙPžñb»äãó¡ÏŒé$WêUvÀ»[MÌÐ #òo· KÖr~'xÂÔãnÄ'†ˆ~ïeúnôã•/rd®Y»Iò ûüjœý}¹zpT£BºŒAÏêòúù‘ÈУlóuòúHÚƒ¿ßÄ2 ·ÆÙåæ)8ãO*^¯fÏ'†ò®: ÉÿÊH½ËKQ.þ=2•ƦúÐìŽ0–bïp~h>ð§Âªñp6X«ÍÁ(z;£ƒ ˆ~/EEw̤;1q…lµ’8ëóqƒjdqæ€ Ü“»ÇUØÐƒiC]\Å 0z´<¢qÝ ²1Už»E3 }1#þÞ²†.r»ÒÕ¤ãK+Þõt!…Î$œ¯„±{bK¼‹ùçÞ|æïA›2'H“4Ç]j+‰ø»edƒB~{Ý×7mÃa-0}‡9‰‘ø ’{Ûi/·¥,ÌñË©ÈX°Šîµ9Oy¾êâ"ozC—àŸõŽDÑ6‡Ù÷x*±µØDú–k¥ª´]$Hw;‰ü¶/‘ïå,nx×#T_2 y—† ¢.—þ zàïŽÄa‚Õ¶ø»œ«þ ¯Á1uü¦«ˆ­9EðíMF©ÄüsØ,„»´!Do‚}–âG– Óêzƒ†/¤¨JnIµ¸ýÕé0ãúIöà­¯²ÆOoáÁ‹ÇM©ßbצI’6¾€µæ¥`7¶–>Ò ûHìâÙÄÁK‹ˆ½ƒï~3˜­b—q¾²ù-yZýy‹)ÔÖçû ê:ÎÙ)„{wãLòr»4éèLŸ}A´r‰éî9¹©tÜ9z®4‹¨'ò‘3Ê"×F£ÏO Ï¾×œ¸¶t&Ã0¡ã\#)gH>ЄÅÉ‚$ÉêZ;¯£¹o•iÚØCøvÿ:Æ*YШ¾¤/°]W6ÃG:°"P‡úÚ˜‘ŸÝ’ðãþ0Î[äÅöNÍÇê =¼´—uþ+J½Òêq×ðV¹Žæn¡ÌåJœà¹=Éùó:†WÅ1toÌOeO"ÏCÌö$ó¼#m'äÖqÈ]ö•óoÌÓÅ uìn„mhñc)†‹2ìI/wt8•T——BnËR˜â;®T½»R£˜u+º±û· ~~}œëÓôÖ3¨¥1‰ë6Ò{;¡´<óÓÿë}ð=Vª—3ýæ5fªH Qè_*Y¯_IŸà#… ³IÿF~rMr7yÝ…18«1É{f Y þYSH­RTji—:,©Q'½çO0û5é¼:z¡¨ŸwkdÖåÛ¡d"líÚ‚r&‹è…['ؘWs©ý¨‹Ã”å×ÁtÛtÜ×°då6ƒñ¬©ðLZžœÖÇ9Zy.¬cbì$ñjo;ìŽ'›'^àÝÑ{àÞº„H+TaÏV´ð£ââ"D¸ÍŒÞzgK6ó„—edÅ·’>/Z µAiúä›9LÞÍ’#ÿ^ºã'î4Ú>g¨lÒ!Ù¹™äæÓ1Ü£s‘1÷ó"ÒV'h¢™-®õi¹Sì„÷MNu죠ÇK¯,üÁA7À™:±x†j§£Üz‡ð-Én½,êRm¼£%Øâ·™è-:Ïž>æ†Î²!d+MW]Æm©.´kC-(}âºõ d&˼ѽƒº×šáƒU-pL!:ßšþ\@*>«ƒ×u#x{têÌ*€…Kö²‡Ýë¨å£XY—1Þa¤`q>,¼¦B¹žìþgJ é~´TEÈ›§ùà“2 ê #ð—Œ9X ääµ"«Á}áxဦ»Á“ÀtzáŸ;)EŸo%Ù9+ÐFä<óp¥ŸM>3šQ=03YŠx>íFIÕ×Lá f²N*õïÇ\÷¹ìª;â´#³VЮ¡ƒÊ¶‰üý·À×ë-ÛêQ{-ÅÊ/¢TvŽ 1ôìGþ¸_“"Vu`ê>ÉsŠpý”I¹gÆ}rÃU.ùblW{Ù¯?概þÉzx~#=ƒ5ȱ¢ï0{_%¾îƒAÇñéx=à#í±’40K†*Wž¤´Wë¥ß€tØzº$' #®àOÓP¿Ž{q­î& £5öšòÃÝ9ÔÙ6\‚rÉËôMœÄüùàáòîiç°þä2ªÞ8·l6áýZur?&ö|ÇbW‚;xAâs1ÎÛtŽ¡‰Òt,nS'=M>/í22Kë!ó9`&±æN…3Ï£!-†`ooz¥VŠøZ“_šµh*ÃôNç%ƒ… äëºäN«1-`ûjk˜û#ÈÎ+CìÈ]®dý8¦C÷IÉц¸$<ò•‹Ûnü_‰XÖäÅ@?5TÕ¡oÙ­péòJzÿp 8+šÃo þy â<›¸O‰G7sruÎLrj±¬>¦ORß¿Gƒmv‘§÷G´°m‰\.YÅÌI3£æ^³Éɶt4Ú–LøQF¥ô1üšÌòèxfº{?,êÙM_¼‰À*°Éèë‚?Ëã™{ù°öÕ&z~Ž$µ?GŸ=NÇ/ÏžÀùaY§DÈg‘Ñ5;Q ^¾W·£”°v}YB#Òmhç–röê6/Ìf|ü;s¹£ŒÿëÅ0ßa }¿}¡zÞq´ÿÃ9üµ¢OL#Ùm“"áí°ÃJJ·]"wßÄ·çô RÙðXÿ,y¿—¦?7$]7# »v~²:‚MÑ$®ø/Þýš›ç³ÉON¢’õl¢ªy N¬]BbŸƒO}89¹ú©õüĬɑ!›wòÓX‡Ut¢ë8{Ñx.ß–DdÏ?F¶àí/'çfÉÓU'îC÷xåDî¸.@“›óØïÇUÈ›]h@õ<1t7> P÷š½.t0ˆ‡:J˜óïû!™5#;§Qì°…ôY›`‘ï&¢¿¥9 ÉÙ뺨ú9‰.ÓjÁÅáâä™çidl[Ø}—…á{p÷ëìCØ>Ovº×cÍÄ)Lid’\xiÿïµô䤿-æ·¥!šÅ4XÑ“ž]–EâkQ®ë)žÖÙêä÷Óód˜•¼–Î*¦Ã´ºø,–—.:˜Aùdz¢Ò·ÙCû%©Vp3ì^ÍÃS…èÃþ8‡ÆkúÄðõ-|°å$S_µ†Ñ­è⊫dÂɶëßïc…„(ÍóÒ¡aŽ/÷f[øYÑ °%¦ÀÁÕ–ØÀÇÆ-84ÅŽ‹<À•¼Äü`?б6pGË—¾='WÿìaÞÚ®¶ø<×1džrphãÓùc×fÓGŸ,°c¿ioêbÞ_¦mPét ÞD¼'ŸZ½@àu vM!g©7½»ü9£\òÕEÂÐÊ锽"ù5Xç‹IŒ€Ý)(³"Í¥¬A?ûÍD›† *æèº'±›\¢èQò"Äšê’Œ "ˆY¤øp.|*R¥÷:ÁäÅÔ:m#ÊËlñг] ›œÍò°†°¶ö7ã§ 5n*0¿V‰hfotS6ãì^û’³äi¼Y Xìí¦ ðJR•Þ 9Ë*Ìð' c˜ùMhܤ€âû¹ªR£ªz¸+fDüöýcº$ž¢ÞóGП”ƒâ—¢ðÓD*t43¬xóL¦AמÞxŸ¼Ó=ÀÏôÙ¡Õ†ÊÇÁsƒ– Ýaù Î`³?ÜwÃCŸ˜2aQØ|Döë»ÓG¼KhÚ%Sšíë…üQGK øÌÅ®2G>ë÷L^\óç…ý9 îe0ëV5Ò΂.uL¯t5Žj–"ÂWv¾~%›xñ,þb·ÀÉï]Ø8O”9±^J¸kÉŠ?šTåòyPØ’ƒ¿f·àŸž©¸\Þoìp€ã²î˜§'OfªÉ“ A\¶m{ ¦Õ¨àÊøkÜòg¶?ŠmŠfëç/†Z@Ï9Î?ÉÁ(º‰ñ>–‹+N¢Ë]Vdå!T÷-á^i%áî«hìLwòOÿ>n¼+LrÎ=f¤ê~â½Ü»X6¢„ß«°æÏ hMZF§i3ÕÕnèËoÀ¹¼g&+‹ÂlÅ}g|ݧƒÏϰDú:I°P'ž KBú™Ùリ:åU\LJ¦m{¨þQ®`¾ í—~ÅZS »‰¨o,uô :µdf]ë÷–Ä¥ä Ebúx6ûNë<í¡ö=+¦Ñ—ÁÁ$§ev°ÆÐwÒdѦBØäÙÒ t¤’¡¯¾;³;¨“k„'?űÃ,¨Œ³î³1†ïÃñ€V2Ø7‰Â9Ãsìz+’”?•ÝýPî±g‡n7²VkèîwêDìïT’Á]±'ç ¬ ù°D›\8Ï=¡v×îÂ5WRÉ2›“S響²°Çè”z~GAó§4¸€J sÝ"ªAÍ©³ÔÜ ˜EƒŒ•Ù7]’ã›R•x#q¾ïUÄ„#ØïD¶þÒ¥ãaKIæsx›Ê]|çøÓ­ ÿ0_æ X<¥ÖZ™lãb_j7©³S– ¨:Â`Ñ}$=¢ôáÛ÷di™*Ü*È%sïÇCA"»r;14fÁËO‚L,•‚ôidUžIs…ïåÂ$aãs¢“ÇK‹Ek@Zj}Ë9À^^»™®UõÀyšŠÄÉo y”e3bòë<‡šœ"bèÐʪ­“ YûX¶BÃjëtá]óTº$+¹P§ÃôH:2½óÖÒ9\]"º´‚‘àå#ןˆRµ•òôi)=—Í~ÿõ—‰”Ò&ã#‡P猆)Ï!c:‚TÎÍ7®È§,JÈúKòk=áO¢(µ–¼òÂ8G/&7ˆ’êmBd}k&ø%yó¡·qîî>ÌO#ÜSäÞ0?½“ @6Kc›e#Þ‹tÝÙ Rmž@\´ó‰ÿÔl:"Ç'¹lpwü©˜É„œ">“þ¹¾b©¹=‹(lJGfÇtR˜ÕÑ^ƒ6CßÅq1²‹À4[ú#õÁ{À#K>Nª]ǬÃÝj$Ææ0GÉíìÒhÅÇMñ 5ÐÆ­ øKVÃγ“g1ä³k‡3±Z·çŸýε=”‹xýà­¹%õ-°&Κ«˜}-*ñógôGað‚ÕKÜŸ%í`C¨;h5HÃïÆéÑšÉ?¢Óæ®@sYn÷ÛC$ A‹,S/!§RƒÙ¸Ù¤´>Ît˃Fß”üQy)'S“ãyú"+4PËäü}…£“þöÃßLx¶DÕ$èE»8ÐÚAœÆÇØiKf1óœÇªæ;œî8 ¿Î5²éßW¯EÆðïœ-ºOòÉÚ¬ôÏ—ìöC9(?ÿóÛYƒŠ_-E·§½ 1¯žžS&;¤Z˜#oS Zd ܘÏÊ´˜Òyw[Áåg#W·ðî{ú’+éç ¦D}ê ñ#KæÆQ‹[°õm ®Þ&þÎf´Äžã™÷ãØ¿±˜œþpl¿é°76Í$Yrs(µ¿…õ/ð•ÅòNd)ü^Çȶ¹Ë<«IõköçÆ5´Lvý<%–^ó¤W5‰re³Ô†õštÃ×TÒ¤¼ *J¿¡D-P‚† ­ƒ±únôhü‰{WëÒ´Eîý¸"¼â7GC_¢ìãD˜“¦æÏ‘/ý:äù±çPœ¨Š¢–ðOT“¬­²ƒªk¶d•ØaÔO¸ ›Í`ê~&àÚF:ô¦— ÃûE¾èƒehéKó®§b‘.pî,¡ÙpÂ^ ”ŸK¶vC/”[úôö̆Ë=!˜ÂFKÌȂõx·9—쌢oÇsÁ±Ê6»»’!ÉPïHGãb'2íJ*Ô¿«@+v ilNîž:‘™Ik]´è±w¨#OÅïš’õ}Ýxíþ\ôHŽ$N}ÔýÍ7fî²¶¾1ž¼y• ÏÕ>¢ÖúFêj¦E84dèÔ§[p‰ô‘¥?,œæfVGâÌmÕ4„׋æ'yÂÉ{º¼>ù›~œµ2¥Q+W2{f’âãID#c+úx„ÙßYL(L`‡–¬¦ww½fü]yhËçU¢ÒßÖÛSc‡'èxþ¦,ư‹KÐíÒY¼¹Žý»€Sª®²J_”‹Í¸Cá|¬b÷ãß[`PIÎõ’×u9ä‹?‰ |íÕ¡¤öõStü.C¬o)Ñ5¼©TZR #ŽLÃs{ºÙŸ'þ0áSi_x¸F?‚ÄChnbI„oa@á Îj?òÉÚG³‚à¥C›*Æqèq©³>z”Ì §Ñ×H‰ª)|?*HŠú`úá4ª3¢L½º`õ}qÚ@¨C]wNBØ"BîØFÒöŠ++@Wlë`6y¼b+’p»ÅTa¼œ¶MO!üïÿBGIÑhjÃÎz;ªò:6µáGþ2Ï0BzOÂŽ¯ûéöÃrh}µšªæg‡^/$[·$ ÉéÌöò|â‚'Á{ÌtìXŒá ¹tYB"QØUAGÜærºú/ÁHwXÿq;ÑÙœCŒÚ|èôrAú_z™ £Fýa8â›MV¬Eêm%˜{Á:°ãÎcNõ“|3În÷DQþ£n¼•˜\„c›Wß|tÇè,ª*óˆ5+N£ÏÎõ¢ß9WºüÄÞ–lÈy8ŽŽUWaGéZÔ Êch«ìr¿ˆS–„àq-|«IWR9Ê=ÉA2e ã5ƒÌì? ½'>1aþÓÈN3â¿EŸ]/žÆfΙ‘Sé½àj¶)q*IL£+ÞøÃS'bÓaLÜÎÅA¡U4o]÷PL[K~¼4ÂZu¢lÿîl‡óß$ÉòÞi¬vC®hWÁì±ípõðCvéÚ· ïòÀü Lÿ¼šIÐ’A ƒfj“Ä(ÝŒò»6\Uúln&‰0õ÷U0ye–Lr‡ÄiÖÙM–eóÑÒˆÛð³Ý…8M!í…â$ßA’ÆV¥PÿŒxÆÛŠªšŸ$‰g1qêÛ mйK•¨âOÞä+îi[³ëd¸n:Xå'Pµ„*˜«òì÷XÐOòû‰¸îòžç%G5û“Yš z¯)òÂÛJµÿ圱wËFÖã8/ÚËÞ¬ Qj ÄO4âˆå#Ìem©Äø ¯ñˆl?vß8.¤#'˜ý–xÜY“Þ“?DíþecT¥1“Ð}‚;\–Óo/¦/ãf“s/–3sO:‘ ©c éw’m]Ó AOs÷> ª4‰ä@N*—iøæ^áYD¶ÉL‡‰kB$q ]@c}fÂÎu`€wÑ(|ÉjƒíAûözörùc¼¾¥{·n€ÕüìJÑUtOqôþ›Nf?›B݃Ã6iòA2ƒ)©]á·Ù™·òÙtŸxÒè=D «Úè®<&öPÝF|ñjn&y>É->n£ÈxMj9Ž=;žÇqj–;»@ŸTؾ`g\½BKe¦Ó=û íÜ /ÜÙkÎDWɌޙ¨ \ Ò±¸|ÁGHßv¨KÕ¥®v%tÈy­£ëðMå<龌î:y–m ‹"'޲­B™“¸Ž‘š½žl &²BS8ß2ixð40¾º<ÁÇÚ:ÿœ@y埌\%a¼EYAòm]+V/ˆÆÀ†ö¦k;áï9 F5ߨ»%›Q°pzÅT×ÑNVŽ˜{5ÃáõxS|!ÕzÔ3?®díFÑù縨×ùR³¿bÉÏ™ ö†‡DJ‰s 2•Z³”¸Ä›0&j$½É€\'/øuI”Ë.jœ²+ª kÒŸ á½: C® Ⱥµ 0Fô(Õf“i1|Ô½f*¥)MªN=cÕÞÄqgXÅQíÍ<Ä«_š 9Æ9”m†7ÂK°îPÈÛN%ÁfÒ7yþdX!~´c@ÙSLÕØG»Ž$€\æ!ˆ¬¤§Ü'˜ƒg™çGØÀ=²0§µÜ–Â÷¼ä—! -1å>Ÿâ‰#ø„¯<®Ãj{ ò{…:)«ãGo¿©x.–ß|8Æ”Ïs§­3‰÷kxâòš‰üÁ$l0fe¸Òhx¡íu$ç_gA&ç2¾œQB= ŸãäZ4«‘ƒš|ÜÊbƒ AI™ Ÿš‰ü•ì«ê#0˜ÕÃÎ9»œœm«b*ªÖÎù…6CtÑ£›LôÔÖ×ÌÌØZVag&·üÑFh0Ö [¡ŒjŒ~w¿ØÐå®3è›ç—ð˜Å8[’ËCx;oCÇøSŒ—}‚מôAÑ1vÂÛ€º«ïç¾>yBÍ›ñô÷/Œ;¯*ZÿrcÖ§ÆÇ0Uœ²© :?-ÀäËå¬Ç4¸uQ„¸ÞM4’HÙûRªÖªÌ<°˜ Ï_Ì%êù´|Q.É|Ìü—Ivnìfm¶¼Ã"+_\>¨Šzi¹@Ï=Çîyï R?ŽÉ+°"–ž€õ™A ¯F,œâ‰9¡ëŸ86Å©ÿZ–Òó¯è˜¯ /VDJŒnÀ©ýOXÍr+¶¤¤•½æÔ9…ò$bë dMbåýV(äµ£ê¶kÀ§Ø¥ ÿŒÓ`¸87=šFn2ò×Ëí#±ùök]'Äz‹‰RFó%òZ¦“¯Eã°|ËTê»?”ñüúoM§ÛW?fÕÜ q¦`£måH¯M>þø\'ä˜óÿÅðín]úò€q­ž}›¡þò´ˆ$¥Gléæ#\ð‹¿Ÿ<::.°ãè@–ŠÑáWO`SÏ8â ÏË\ø„'ÄûX³mb0v/..tÁ½nÇÁOó/›™” ì:Sü]¹n781´ÕQ¸MŽæÚ€Wˆ4irTž‹·2¼Jãh9$G…÷‰Ñ—äý°¼¡ÎºYˆ³·É(\{û…mí!r¿3ÚýÖ(P…ÂUdi¢Œ‡±Œùß{hªNLC®ÂJÊK4:PóÐs6U2«6I\*#‚éuUlË+Z:ò n/HÂòÜ 6 P‘˜ÍÑ ×ÇKY¹ŸÔt³† ¶@°»l~ÄâÕô@*úÞŠ< =ƒAmeäZ¨ Ž%U¯ÆðêÖb2¾á{à¢.^í´FÛj;²çG6ýÖˆÊÜVT‹Uÿèù0uÖRu“#Ûžâ´•"(3]ƒüòr¥ÏMƒÀ»¥„³rä ×#ÐQò1s¼»œªŽ¨8Ìø^‚ÅÌ3Ö”1¦ ¸í¾ÆÈèQÍ•ËÉ•Yâø6¦m2tüÛÈñFoÿ…Ì@û8³vëUVº5 õuCàm² hÆ+Q¸¤9÷ôHWb úZjÒ€†[°yS r:X8»tá\øÚ– [B_19êÖppó]¦nÆ<êß}ûÒ[@{w/² ò²G˜~z{V<ÔTãʉp>O÷FªBÙç§ÌÁî)dro»ÅâZðþ•;°/C—†Œ#üÓUAoo0=Õð6ƾ=½Œž]— Ó+3ý$P·8Lì¥àæ+¦»^çj(r÷¶¶YÍæ‡ö O­ö"Ï•hÑË\Éó¤<ÛW°­½÷pŽÚ ÑûÃ$4ÎçHMÄ’Í`× ãOF °ü-{Fè)³ºb*Un–c®/P¦ïàmçjLx” fNajXnÕ롸l_û~†û²sƒz`Åê ´>GTÉ3A2ÓÄŽ}·žÎ¿Ñ‡’™«rlÎù› [%OEÕL(·gíÙ¤A/D3ìÃs á¤.Ërë2ñ­¶ê©ýc™cC«á÷çïè· ·45 úÂÅ̆ÄCD‰lQ„Ÿù®¨%—]Æç¡0mtÃìõséÈ)IºWZB´à˜B  ‚’=aø`öC¨áÝŽ‚Æ -w>»b¸ÞÝÉ… wM™ê0£ù'DZµ~Ïü€’‰Ç¹ ú ±"ûg†Sꛎº÷²NkcSø+CVcÏß×ÌæHœJÒ™²ýSéŸ3³é¶c\¸ºZþ\µA›ÅpÊ?ðÚa(?'@›o”3bÇ‚™Æ<3Șó2öd&­Ò//­ùÔì§3r1f]ž2]9}lyY<·«1§í­üRºVúëgû_:¾‚âk6tÍ+¨Hf·Ù©P‘Ñ`”ÕQ=i T4µà­w¬ü:Â^~ÃÞP¢Ašg`ܰn‡=ÃKkx”•Ä@Aû뽟¬>™K&LWáéÙÙà|7 KÕ$`ïòÜ©RÎM%›`Ç[ö^Óœwh€7ÛN3›xi÷|âù2Ÿ¯Í„Ú«»˜¸_Ù컻ḽs6=õ!1©¼>„/®3Ÿø¨wÀxµyZ2ìã]hÁåRÇÚ}kŸƒvÛ6͇´íiäÃìi˜ô·’Ź—ÖÄs6ìÇÃtùµ²>âN+/ü—u ÷p>‰°mqáì5w2ç92Îw;8²fm°¼5NJÇbéò.®µä»~l °žçÕÞ¹`ìh»¼Ç‘O5Wï7‡ª$ÙÐÄžïfq%v’®ß×áÛýÓ Û÷.k*Q‹’ÔoIK7Š!==“Ó÷$C´´È¿ó:lKÖ(ùx‘{˸ƒößméôÅãÍäÑ O>E3Ü –wtTÁq!p´ªAiÑ•p퇚Uþƒuæ#`pÒ—z´±j'ÎãúA ¯u®ƒŽÆoÿ×ÊÜ•Æß¤’Õˆ™óñ‹ÙblløÎþ\ÚÓøIÌôøü— éô9éKæÐÍ{{àW^pÄ,é«-oÙç B¤úd"”¾7¥’Ï-1)Gg¬] C“šRKæ8³{ÇYø|×ö6±3o@¦òo¦±üFŽô°߉¡~ænx‰oUâ®{Bä4¿>î®[…ó3î“<Úzvöº>bSªCaD*÷jÚÒKçΘ6)˜!ILF8¬u¨9ŠÊŒÃe‰ Øâ™o.cŽÌ7œó1™½|¢TM(êæ`Ò£O°+–YäÉ zËñû‘`84ã%®‘΃5-\ÿI›†¯Ñ` ß*Q¥­˜¸ñL+3c÷ÅÉ?Ã*Ä5@Š$Õ71¿­bñõy?Xüü,8v]cß°§3‰Ý¥*\}á'®ŽŸŠù`ï~˜Ùrt\#ÒhÀæqV Wûh²öábDWw\[%Å)UÛ> …žå¸üüØÒÀ²ð¢æ=ʘ²h9½ÐiɸÆ$ …ÙG¶I¼–saôÈr‚ߎâ!ñ¹Dsþ {ùõRPuĹ'5é5§§LVlúÿ8ºò¸¿/ܾkßK»öTZ¤eÞs³$Š”5I¤P”=[hß÷M)I«¤ռ綑dK"»²‹¬ñëû›f>óÞÏ9÷¾sÏ=Ïó™gžiÎ8s®ÍS£¶ç·¾æía>~êbO”¨Ñö®Ed“× Niã :õs!‰—Ü\6Ÿ·˜kH¾„U›®ãð"rwC01÷ZÈæ´mƒ,™*Ü”’¢Yà¶S»–!*Œ—q$¾F×ý­º}×ðüþ·¨Q¤OÏù܇_·?°ƒ£ðÑC™8˜¼€ÈtÊLD³z{M'k½®ÚµÀ|A#rg–ÝÉ'Fv:éžë%܆;†Th– ==Qîä6~¾î+u²èëÑs°õ3ûË[¦ãò:+V—qùs÷œpǤ?|tóJ!’ýFþÉà'<Ÿ­ðÚáGø9Ä”{θÂTJ°p¡/=$jCemëa~ßnð¹ËV’•~×1ëe#ž“Ë×Ëuÿ2ÜÓ‹„¤øjÜf²“Ùp¯›$@Ä?_˜[¡Iùº–â’•h/‰û㾎ÐTª¤¯G‚{ýPVÀ–ø©ÓóVû™uÇ+aÈy“#ÏÅ ø’UÿšJŸ¦¬#‹ ’·—"ߨTê¨fJ–¹däÝ )ºZ‚ðç–7ðó*˜hßKÂÑîx:‘ú;Åâ ¬ñ‰LGÕ µð¾ÌŽ.MØ‹ Ϙß9rà³™q.’…KZÀyZe¬èi eð>ºÂÄ—Òfî2–·„„ úÞ†ì< ã‹ ýÐD1Ïq_¯-š)ðÓ J(»5'ºxµ>ÝÅP u#ØÖ˜&±› Û(E›E¤ÉÜ´;ÐaaŠ 6]̃÷§a_¢I[Lpú=FÆÈŒ±ºÆ¬Éó`ýŽá™ L)<ÎŒ,¡×ýu©Rˆ(层¦ìœKÄz}è`_˜ÅïýVpf"–™õ+˜5Y| eŠÙðwšàÚz\Õ'ï¿0`ým‹0÷|3}ÞŽÃ’áçj„ % y¤ø°e”+ªÊ õSlÌkùº™—î—v¢9üSÙ’úNÜÂ%zI%וkè)'œ`Ñ6ÔÓx .|Ì¿Ž^ˆxÉ?wè£ÍxrÝO2j~CˆüLF۶Щ~QsE3’¡ûøà|ã9¶yÙ9¼Uz ÎÅ{Bó—ÛN+DE±Óç6œ‹âøŸbˆè'8®ÇrÔgà BµHÞŠoÊþB§¤,›uËL6Ö`o† Ùõ@ŸH,1£‘ëÖÐ[â–Äë³!z$€Y¼m0‘#·ênÁ3çt¶{W=÷ô9®Ê~SÊô¹²½n¡Á5Œ[Û Ü°L”½Ú‚¶û‰ß§&øç¢A ¼gÓP_%ˆ?…ô¤«5Õñ¨Õ°Ÿ½áE÷7 |õIì?¯ää´û3ôýíf´/ *2 Àd¿ ^I¸ÿä§5^¸TI  î€YA2°§9Ù…ö«›„ïNÙq•noCSö13þ ÅvX’–ì-ðËǘ#¹P6æD™ ~ê^¹™X¨]KšÈЇ8òu³ÏÿÅÀ+ hxAŒ¾Ûzxª’ße4é[/ó~ÃqvÌà{ßK†þ,ªÅ ºHE[½FÙW3`Á©upßFNüU#³>^À¨=§aBó&³¤x6f7þàT ¥=CÊô†²yjš‹6VްZ’Ÿ+Éh_Ošª÷ Ÿ© ×rß}4º± ?µëÐó§k@v ô€9\ËKwˆL%%ñpu`UNÇC‹M8®¾Æ¡Sâ»a¢0|tD®dŠ){Þd>‰l"å|®Ô|Ðï…Ï#ŠeEÄ(q#ýÌñDˆ] kWÌ`õÞı„xÆÓ!ÚúbaXCî‹ðã[}R`ÉOäòO—•gÌ-椌€Ù ·u‡X"£Ï„×g"¡•igÅ…¦‘¿¯UÐòá7¦µ­¦6ã ÏW¶ý®{ça"mYFŸ q-VÃÀßu“8»r§ Ñ7»xÉÿf\´ËŸÉÙWÉQç„)û\È!5*Ûúš=Ûs.´c@÷T$ÇN²~±xòä(ð^ÈÄó!0óêZª¥Á¬oõêŽüZQ÷ðaî±äp¶¥W„\ê» üa£lËë¬Ú´Œ&ÿ”G„.(ª’'NbåVç{ˆ }‚?t“àÑÏ8(Ÿ¨ge¸P®‰l§Êq›©G*Ù½=Ó©áª,ø6XPóú7b9#ŨÒöÛ<ø9“—dqqÚy®‰è Žžñ¡Å[+àÞØ8þ7‡Í/[¸‹‡µÉGZ]<ƒ®øžŒsB>²šûŽ·µkˆÛ,Q¼û$·žIW}ºƒ9+âç‘oOÂonºønÚt¼ÿg/z‹Û$¦’w[nDM@}./{×@Ë>h†/gƒ˜s¨Ýß? ’·‹î]´ýd®ÔZBö–ϬÑÏå(sæ7&û–ÁÄÃÝãk«¾ïÆ_§n`ãà °(Ï–þ2죎“¨Zµ–r‰! ¾ K/ç0Ãf×!‡™_õâÄÝAвy¨ù‡2Ò´×ÚÞ†'×±ÆÛLsÒ°ó6Š%Ñe-' áÙ œÓó§¬Ã8l^—1ONBŒü ôNSqe zﺡkCþð¼#Gê"VP™€z¬¾Â–ÚháêZláñÄÑ‘`Îdb‰^(qó– ú ëqä°ÆpìH¥Ïì´ú¼Q)ëEN¿®À…2ÿ@Ö¬ú]åˆ<ïRX±}¦Cäà ²à5sûsÙuϲ3`KØyÔŠ2%?/° Â\òëI,y*oFäd‰”—95!òDÒŸ!B•Ý 9†+¾.§ŸŒ÷cN€5É[þÙæ^EöªL'ù…È=ìçù+è·@GJÒçRþÙ‚¸!P“zÏŽc‚ʲ^þ cxФ%~ÆWë,iÓÞ™ôd³•ºµ‡¸P­Ø1[™Žo¾HŽYÓz] ¹uÔ‰˜­ÑGûØ#Ðß¡B,å}0×z Нq§!io ºy’=i2» áx/5¸’Oʵ#¨â(TüX£zóñªÞGøOk}µÝ–|á|vZ=UžXæ¥Ðë¥?è¶Ð&x9ËÛ]Þ¡\Ø8\Ÿ{xrþeÌ^©É}t&–9’sˆñzOú7ž‡§÷º0¼±›®ÝÄ&¢EW£9­œ—F:¶á–QÌ.ùTNåCM:?¸”Dì1"Ê_jX{[YX«ò^:¼€ŒÃsIçÄ!Öu×y8&þÝÊ3aÛ»]x"–Cëä# êì©÷&¥^Å0ºþñWÃø®ß˜àRƒYÌa°l¨Ãà´‹m±†ÌXöÌjÒ)¢:ËŸJb=}uhŽyȺJ2»@<)Ÿ7Ÿ\ßù*ï@÷ü YËOŠ÷×ëWx˜Hìe߀žÈP9ØŽS½¿aÝ•SP¨O¿³_OoÅ+Šv°pÍ/Ʊ$ "Ú.1ÛÚG°Âñ¸æ(Ñþ³0Û2Û¦wãgqTùRŽû”r6i+ Ðò>\¤WÄÔHEѪTÀy\Ç‘í/g„f½ƒ¯ ËA&Åw\-b¹£°g7¼¬$tã”opÉX”-°·»5¨À Ó®£ß€§`HÛƒ9ê¡N$ äIóŒƒøü^*=µ‹ÚYW‘ƒs’aMó]æq¥!9%ws¿¼g¢WÓãMòTù¤äO=ʬjJ%Âëhß™ܧŠÎ—’›] ³4V•²xÆØ(TãÔ¶h’û‰ÉCÏ1Jj‚9.W çË´Ù®²õ”ÝösŸLþ3ØÁHö»ªVMòØŸC±Ì‡lurê¢$);г<ÓåwÙÀéütÆÓ9'¬q¿63W¯©‘ß“U3½×?ªv°Ñ ´Fcý¨މ†ñ¬œ¹ \¹°Š©Q$)‹”lp¦ ×®ƒªL ð³Úôå¿ú|-îzdK…vº¢çŠ(92…î)cÁÍDtµ u•›N4î~㊇YSW]ÈÆ«èÛÎ\ØÃKæÒ{j1p±_°%>ªE³x¶¨‡õ“Ë¡_œ®0»œè4§h²8“L<¡%á<äo“éyϸ ±¾ÓÆ’^4)=ÀÚÄd·:pÖ®ó$úó œWBÕ»¤ äöqlÏ]‚=<â䃯õzÉÆÔ%bª§(™¯‚kã³uÕƒI‡¿pÆÎÅãž–Jv‡Ó büE‹ˆïó"))3©µk—l‡úM ’0ÔKÅÈ.“ÿÏcãœ0ˆY9'-°þâyo![Õþ±É§•蔨ÛN§ 1VÞ hž@0½`ϥݞÀW¥û8!à‡Ý¾–‰€œïkÎïí`>ž åǼhåÕX ÕèTÂvY }ó.+绑óxÎ:÷™ y¤Ì7ɹW‘É÷¿Ê`œ!"‚7ÏÝ„ëÚ„JL5£?_[bÔ\:Ïúð×dzo ª ÃìgŸËRò‚ß‚d­gÅ~}ä,%*>w˜ùÎCÛT |ÙÄ¡>'Ñ­° 7LbËOâ˜dÁ¿ ûi¾uœ˜q}[[°5dFý¹Š¯|ÙÌbS"1ÿ óu®.)yn‡ú1ñë'\w0…1ØãƒßÄp òSn߇zš$ }6ìüÇñþðçÝøkŸÙÁ‹m›sBï%<º¼páÀ7¬üš†Ü 8Ô¤FS䄨Bs-ö¾N“4ë)sgº1È׬"‡ô©É˯ø¨+×Ù û‰wêý™f¦zÔ—º8ôpÿ8’eÆÏqÕdí]_•KÖ ãÖ *wè>d(†ROžf¦·@” $j‚É´NÊ·; ‡çŒTòTá*\¨Å_Å™î2ôžá8®åzQRi R—ƒÕµÏÿ" 4îá³`Ò—óœKÃ(IKvá‘y´¡uYþUƒ¬UV¤«dõ`Pùû;æ5Ž„ð“C/N95YÛ’þpWø;­šÉxcG/w5ÿfˆÛ¦@Þ8eÐ<;qºó@: u¢ {µ vŸÀ.ó…d¹ó²m/ý·ï.=îHªþbO, ‹L¨Ek-ºÑQÍœaXÀL»/G"cΰϽ„ ?s)åÉÈ÷GÈ›-½àþË {wù’]ÍÉTäÝ ³,ÁÔ— Èßu!A=i0_Ï Ôm1z«:õ¾1‡œ»RMdxtéÖÓ\¼5ÇŽæÌ8‹nfšäÔwx.©Ç—È`ä¿_ y0 ,ÛV0;DŽÀ¦iôùå5ˆSÔé&#uHÎæÔ®èÇsÁÇA1R™vÛkã_ÏlˆR%ÝkÓÐþ‚,ã邇`ˆRälëòÜ*$ýçwalò¬Iã°S}â°¤/v„k½Öê×¹J´CJ¶Ì¹‰²¹ìÿ&Vêæ_™ÞÂ,Šc¦½Ëgší0ÒŸa—î^»šsìO!{lêÍIX‡§¦À¼žÂð3ØžLÎYcQ*\j„+¾]d~ê'ã‘°pT ÷úK‘57}QÊ&„8¢ÄlHþ§oÞCB™¢Ö@Ö®tqÈawª`Ã=–¹¨ø‘;¡d‚ý=øÀÞ‰¬ ÝÃFLbÅ_ªËhj±mãã#v:tZœ'ù7…œø¦IÓ /‚“F=Çò Ñô®›2U™YJŸî{Œ)ò<ô²÷07/ Ç.(ŠƒŒc×0(^’Öžîl)j}ÅùûÈ…² /'s/¾BYn-d40þ~‹ ¯,Ý2µ ’$YiœçAÎP1¾kt¡¯’e‘Os%™q}=ö<ŠN™H¦µótȃÓCXu-ë¾æa]³w‡4a#u!x¯ Œì†¯*ÌtÒ"óêʘÕÃ5pí[<(w¡¢¡óÉ2A7減>¬$D›§²¦Ÿ»A§¬ üǺ°ÇËí³Ø¿HH¾ ~—ASö$ÐÍ‹‰Áˆù"šŠ ªôˆ¬<8\y*»HÖâý3;§õ”j,,1w¦÷Õxˆã½AÐ?‘\~ÎÀê º(‹KCméÙ¬æ›'‰îh_iç®?‡‡¾ùíë=™øîJvTÊ#ow Ž= ˜”‘L½Øée@ÿt×rõ»R[Ö^8ï8ÊÅ®žÃ<^ñ„ÍeÂNbøŸª³|ÌIü¥»éA$üÚkDMƒÅ DZ›}N ]¯ô@Ä»‹Ü<“ Éü.Äߟuêã=›˜GÎýìóqk°û‰ÂY™¸¨÷¸Y†’-Ýéš:Ô¨LDN͆µõëÈj;X­çBì~ó¡gîmTî0#!Û°5g;ýO׿µw™%‚ß‹Á@@.}'y†¸,[A®¿á¡Þ¯¢¨Ò;†TìlDɵŸ!RZ—ÐçÚôõë?,ŸúMæÈß×äÔž5t›å/ª?Ìe>¹C ŽØ2jƒeÿ­Mt©ûp)%âÔ0 \É$Ñû¢Â$ô¯×­Áœ9Õ ÏÆ“Ëo’ Ÿõh]C¶VGS-àâјÓÀýfåO›ã1Ff¸&à³ œ«`B 3·Á…ylð#vM¡ÁùðCe9¹³3;vi0Ÿï Ýqê§xØ ÖeL¥O\YJD‡‰ uÀ¼úôud6n½²U ¥Ãvº¤­ù'ӳІ,+ºî}€ÊéÓHñ?’ºðìß_ +®_UšäûœO¬ÂZ^úh9q` ˆjî¡Í€ÆK1Ð!Šðæ¨-y8ë2s`‘ Öél ã'aQ]¯J©üUhg?€žÇ}´?)Oýø 1·¶M…Ã’[™{'3°hÍêT¥àlKêå°gÉ¥À‚¥É™ÒÁz«d±+û°äB.ª,•ÃÍ‹èÐ4º*‚ ïºé4R˜BžLwƒiI»¨ž×LñSæ¦d–Cwû 櫘3M_kD×>»ŠƒêèŽ ºŒFÃ'|òó çÓˆm{Ì­JUÅ›×R0âmtœáPEÛhhè9 o13Sœì˜›îmfd›³ àĵS&`éacdÿ¼ŸWâG!Úi·Ž,–!—*/ÂoZ–SæÈÍ›’DŠU¯sv½ƒDþ,äX«1&7ö“õ'ô¨ÅæûŒî$æ°Ø+@úâî€O·:y¿/’LßÂØ^rÀ,½^¬ˆ‹&krô‰Ìƒ+Üs¼pfö¶ß"cp$\ÜI¯ã*'P÷˜ƒ`t”Mùï°¡¸3’/±;ÏÌ' ¾³×9H_éòNí`0X5J¡R¬5#vü lÜÉí+ÑCYñ: )&‚Ð1e¹ó}'î]g€çÅÓ¨ìh2 >ŸKðp1½©§I¦ž`Içê\8 ÍSäÉãíP¸_Š> ±eÖüÜË\<€Ã;ð—‘.µ°ªG‰îÄu]:Üm‚s˜~éòÙ~f ßDCÚÐtª'K{g’2fúMÚšn@öÍ>ï5Y·ê°ZäÞ½÷lÚáˆÅv¥ÃAŒÑÐ¦ŠŸ¬pÔÇ…dlÇ<»0™LýTI:®_ÆÚ'¸ÞS¶2žZMì?cwÒzFCî Y3]Qf÷NP¡2i4r#+áœüiÌ*çBšÇeÇÏmWhÖz²¹‡”ÜâXL%¶Ï™C,€dpî¸OýóžÂô‚8F’‡Ò%Ö§¹{ïJ‘8µ7ŒqÚrš¶šð–¹á½*]»ˆš‘ÈUrÃ0˜Ì65‚æœr̲'B÷%hSägVæ‘?a$0-¾¶ §KüBÈ—$þs:­²R$³µóñJþJHy]NSó=È-÷÷Øä¡D’}: !¢R:ô·Ë*’yNƒ |r¤î4¦;å+©Š†V€#i™êU4Òˆ¬FRTÒ”ÅH©Éâ±è0¹ªÙNÔvq)gØìX §–Ò¨QL†t!œ/÷Š•¯¼ ãò¿˜à¹zø/ô& ei‘‘*ìX<^³“ðú$l½½ºçhÄæ9­LE6b˜§Ç>ÁOÖ›iÄh $?5 “Ïxsì Ë«¹•Õ¸{4ìO2- Pª–àégúlg3»¿¶‹#](Œß×®Ào¾ ™=Fø§/‘[o³¦Vƒ©f(.ö~ËHäjÀßxV_“؉´‡­"ôýzi¢¼PŽlŸhÆ>÷R{›ÐÑäûo®¨€õïJãwvð¬4 w¼ 7j¤è¯ä?à¹t9²Õ’,K7¦RÙA|0í;=“Æì¥._Ò3sÔÈçù§‰Õù)ô؉Ó`Û“‹W–.dªî^gý;ÌH¥u»ƒqú¿aÉœ1&àú,Ækü4£/±ž?8BUfDÃýÍX?½Ÿ—ÛÒi?tðcF4ƒÂßd&èGÄ8 Ý\è×ÏZÄËû Ù0öR¥æ¢°o-™!‹µEá›ÉùÖY¼Ó)G7Ó¦ù;`îÏK ›iC[­cí|ø©ÖS?¦„¯v[qGÉ£üø{öÁ©ÔNHƒ±2ì^ÇC‹O4±iŸw¡ý˜,† <€ù÷O²-±N œ>…—ªÌ³h wÎÜcœC¸,cCÍÊÑyÑÚ$÷Y4¤Û™’ÕíŒÙg~r˜×šH긻@b‡ð–¿&™ÉgG+O=Ä —;pšg.û\® ŠÞR”âIå0˜t©'óÃé±Ç¶LUýt’'q_E¤úáí@üq•¢ézÕ â©DÕ’ÉÍö‡‘Z4®C‹.IòßµQMCò=T»ä8ô@h船Ñkï2ðà†ç õ6˜™®Œ7W¡ÊßýDnûR¼Ê2|vq¨skfC2ù:[¨WcÑþKæŠÅãV¶g]>†ãfWÈÊ÷õ`:C‡õ¾µ.ýà í{´@=|Ž/™EõWÂ4ž-°(B‹ŒÏ¤•á䇨ARðí-þ²£/Å„è ™Fô½R »Ì•'÷± ª{Ë£‹’Puw+\oçÞt‡‘¯ µœ+EœãÕÉÁ|%º¢z=P<ÔÏßöÔô@.åw"÷ŽqË´]áî‡ \0?')>š¼ G³5謋~ôä=C²•s^Ÿg—û-§oðÍQ'º;[h­î*¼D/â…ÊM” u:/A à Ê^\…·¶/ÿOûLBœÙÞ}Zä”LÿMÁ7K¶±Yeœ/BÓéóÉœÝÅ*¾ÝG§ª±Aì ¼~ Þ6‡‹ûÝ?'ªxÙKâ^2Z?Qmè'Æ^ZĽaú [¦ÓMúl¤|2õ®œÏÛ¦¶oÙ¯Ã0c—Í61"N/±Üm®‹cÞKåâiÁ*ŒW®`6ƒÈ  i¸ð¹*QÜ/Î•š»†š6àÈŒ=eG_|Cx%R’  ôQ9c¸;bÛ|Âsæ sÌU‹¾êfBžúÐfå)\†öÅ&´úZ<Õ<½2OºCY~Ì¿yꥱ—W.×nF£Õ-œã[”p`Êv¿#Y~mrÔbQDè#µô-\ÚpŽÛñ³Šé^šÏœöÈÅ?g¯àî±B –¯‡FÑ?Øwê;òò“ìöT@Ô„Ti ²mf,®™µ†¤ >…[×o£õ™DÖgÑ)Î+®ýqæ¾U½ 7î°Z‡™ÃWe1Hik_ꓤÙoÀ#O–¸V~ƒ'[ hÛ#uzóÐFöÍO3z|x9 ®G¶îc@|— <I–8¨âÓÞË\6¬]&÷t0ØÌGûºkð¬. ò­â[ʹ§™·ø¤ë Yt&mŽfqˆ5£?ŸÈ-‡¶ 1ð#ø–ÞžI¥jûA÷_;½µ„|ûó– \цjŠ8—7£¶0±üWA[»WOòLõ+>lQ½ yáìB˜µÌ±É ^ªû9‘“¾¢:ðóîPñá#ñºÆP9Ow˜=Áþ»©t8eœFFèb4FuÚÀ~…¯¸Ö—–,Ô%¢†‘NUåk)ÕÙ½…ʵ&Ûœx˜¹R–ÊNX“õNtJa29»“E\ìNª6­€|ÕFèâã!’‹ßàÎRT$ª $ýKÑ΀‡$%†Ñ[%ˆ¶ðd/ãaÄó*è{éK¸ùÞ4j®,NC}§ãåMÔO: ®Žû’³ïpfÏÜÝô´.˜«´nèö|ÌN)Bpû!Mâó Þ Ã-Ã`ªOBlUis'B÷aê¼Ö\7æÝ÷Ѐ¹ù8ƒUÌZAEnOp—̇ _¸Rô±ÓÏ–$²c+<•Ö ËÀ!õHò3%W-›O4âϰ±‡$hUœ:ñ7èÁ´Ï?²RÔ>ƒ3oi“Ê/îôê®h°Ÿ—|½6€µ•ôi7U×S c]rÔçº$ë2=*ÁÈë/¶Ï€+›îáÍ»ßQ½$ˆ~ËÖ¢ÃÒgRäG[$I[„ª_dN œ$õŽ4óh0»²W…z”§q쿎À‰µ²¤}$ŽõrwG}ÿ¸ð¬b¤<ðÚG fã•o Õõ žp,ùÁ<";c­Õý˜Çq aŽÆ¦Á{\Øõ0®:Ȭø™‰‘U*d¼õ9½S„ oFC’ )N®ø/¾WÓ¤üÅŠ”µ‚©C?YñuóHÃ=2äó |¯~4¤ŽäžíGãíØÊÃM0XhÇ<õ×§»•ðqÛe¬Ž…Ÿ_‡aG˜ §×å%Ûöoœ=PÕmO%æÒ»óEÐÜä0dÔì$nŒ±NºKgÃʇçðïÐøÏãú?-÷ºð0âxÄ“Ùkµš™“u_{Œé+^ãH\cHǃ¿2 )°úN$Ë]€‘’W¼.³º¬°à|zo‹äÖcU´%¹mL%ŸêÒu|z4—¢;ÃYUÇ ðh=—æ¢ý{pNÏÛxij(v•1ðÚ+ì}`{Ÿ7ˆ¬`t%£|$´&‘±úPŽŒeõš™3‰|ÖœûØÇëìhÿ46–W(dR´Öþ‡bX•×(ü´Ìöá‘Ò´ìËIf÷kSf“J¸Í$t]E‰ <¼ö(¤¾%ÿyGs4͈\gL5`2Ö­ÀØöF\¬l ð#™i¹õ¥‡4iÒÊ`0èÀÔ;G1P:v%ŠÒæ¾rÐ_õŸg2Z8ŒâãªUÀ£U SÛètV‚5¬BÇu*LrئtnŽ~ BÝ.!òh¡zØÁM_\7äÅÃ`©y½ûóŸÖYË‘Ÿq/:»Fz˜·Ãìæ¦¸Næ3cv"ßîDiI[Vú˜é!á¶Ø&û2v¦©' Y¦N4´·‚Ï£8”›ÄA™µ Öu–]ï$wu>½àxâÈb8½5ºsZðùÐg>×Ë}—h„Óvœ„W÷¦løÉþ絜8dÇW”ÂàW !ÁKfXXÓ£ÕY(is~-µÇ¦é¢¸ÇR‡½=ÌúºçrŸ)™Mò9Þ¨¾…åaÇá©cºzÃ@íãxúp­ÓN—j–=UÉê*ԢʠÍìÑ$7:®pòF¦2e‘'ÙØ)øÈnn¡7ñ¶ßqry{$˜ÌìÆ¿Ž°§wÀÜGûAü° Ú¤8¢Ï#¸¸Þ€>+s#û^a6ò“ Ôß±Ö*EÒ×m‘ؘ‚!…Kqƒ_spsS¨GÈB r’Öb{–&„qéøMºÈ¾> ‚OÔ賉©°XY‰ÝÁ¬¥êú„ëÙµYÇq©g YoèCþ¬¸‡÷C?ã“?7qÁ²ó|»Qº¶žÎÈe^gÓÝ?pZVb_ð«=Æ©=:Xûš‡ViœÄשSÈ)©“È™•Î^IO!áËlè¹[­E;IÚÈu8ýÉîTVÂiF†¼[~òv)Œd•['p} g—ÓÿrºÞv$›^Ù&Æ¡óÛ¢Ù€=Yð(õ8ðD€‚§ dä÷p|_×`Í(,€ †ÓçÃ\µ{ÌÑØo/zPâÐ 1‰p'ˆd-·°cjzxªù3ë+ÓÕ Я¹Š¹™3ù®±oóÖ3ÐnLeSö­˜ä·10Ù»æY £>Ó"°}mPþAºS™p9iċ˛­H û{6n±9µõÚ‰›©æµ¥”$ðR{4„®Û°bôÇD£ˆø¬+ž¯zÌúw™lÄË0úU?ؼkëáê¥KpU »ÌL¾æ]ÈžšÁý0¤&á™XvJ _w”€_¢¸/Ø  ÕgúnqkeÉ2§öÉf‹9ü‡½y9ÃoÏ&‡l£1Ml>ª  æåµXT²ŸN¸¡}BÅÙÇQ¾Cˆ´›ÄéçKYáé´9H[- éÂ>ÂèÎFFcé8Yé‰Öª…ŒúÔ»h$X úVAq@ˆL;ºƒ)ž÷œ™Ör„b_¸¨IЛòÔó‡.Ü< WÉ£ð1Qð³­aÌÌ4ayØ(œMÍ"w¾k ëÓLˆ/ˆ†9‘Mè\Qí»wcþÊGøh{¶ò»μ&hk6ÁkÓ…É×N Y3¶Ëæ<½w9ÙmG-S¢™ÜIuïÌ]fÞNp—†›Å âe mðJgë¨ÒÚº#ÌŸ/å0rë,3£í cÕ£K®ŠÖ˜º<$dÃ]©>o2dOØŠLò¼vPUŸG,[òq8k¸Ö%ãôä•ìŒKãìÆË%èå—ˬþ’À]mvmµo°ÿé§Ç?¹mP¾Ê€jI÷ã™üuLZ¿/I’ÝÌ,™â΄›¶¢ºZ/fÿÕ!·ÝXIÓýÌ¡=G ÿî\øu;ZH6?îdâß\çÄk àÉ_ý(Ù³Fg¯Ãç‚Xƒ¡—üÔ>ª¯ðÞü<ø¨¼ÿÓ8yœE'>ê*S¯lfyÞšSéçqûÖ|ÏIn5?¾M¿-FþøºCtf ‹9O=Oæ¡t~6]<¯¸>º0ýÚt\¦N *ª¡Ñ\–ÔÜ5'qó”¨Âî¬÷~]º…ÙŠŸ…èÙë_X®ù¨Ú­@®r™'ÙŠÄ)4ƒî‰V 7Ú€kD›Ü—°œüo-ÞÖO@}Cü=ÏàîMŠ$;Z ÆE¤ÉºEÊ•?ðŸo3ì­½ 7íR¹ ÖÉÔÍ!él‡•úçÈAÛ)$wS)#®MóʶC¯e$¯ŸNòójao©±›ÚÂä_qB§¿Àíµ1*‚Ãjóà?oj™KA·þ  ÆÜáY0ôЂ…‰Ðþ-¤iž0Œ¶Ù}š+[ˆìÁ&îc²W">\ìDik²´b—Ópªcþ²¿¨Ú9 U˜xH¢»èŦhl ¨¬]ƒs æ@O£DúŒ³Óÿ0yÂ$nkÐ O–gòñ9T‹iŸSJV–¢ç üv÷j딡y4±Oœþ}ÿ’ÙVâJa¢–“<; [\wÊXxw†›RZþl¾è’75ÖÞ/B›ült/Éd®æÜeûËUÈ›D1zX[–¸(ªµ¼ŸVÄqñ­}«?÷Þ™iJãê÷ÒªÌ)`¿ؽÒ/q¥s´gØ[¡3ÉÝ}ÿùÇ’Õahþ¤fMH3ïÎl‡¨Ñ4àU»ÞÇž„¦Ò”trH¿Š¹ÇSÍéz”Âì”(ÄöÄúR¿¶Ú¡¤t::Ç~Å¥Õðßërq!Ú}4˜¬=t“µa-/´ã¶´»»f>x svO¥WÛb s» ©íû´ÄIšg;dèIÓ=ŽõX%›;½×…Qµ&vu3S®.${øˆßèòZj%¸ý¸ê´Ö`*Sé#MÜe·àƒo_@ß:.øzÀ½Â9´Ì·†³~©½Æ+C>¥³`òóÕ¡; ™éÕøhКJ¼9Î9ºb5î—…±Ûɘ÷v?léÍçî¡­€É·îÅ—IiØùû;+º “lËPËç,f®þˆãuþ?~¿È"æø°<¡ ‹¹]BsñC… Yd^uáŒ|#üÜwŽá¯†ÇËé›}z$aeùkÑU›ŒA°r>zÚ“%C óï6IžiK3k—Ì<¸¦¶€»À¡¯bCd£èb;a¢9ÿ4ìiqÄ»ëÅh_ã fMÎ,úrg“³qhÒ k܈Ýk¨ìã'u²Ù?ß«AêÖ|ªöQøo·ãWïÍè¥Ò‰…ý`ÑM¨vP4Øï…ž”PÙx+üæÀqovÉl„í =–`ˆÏÏmd]ò/³‹™jÖ×6«’…[ÎÁáÞé´Á‡ìasȲœx,"‘}fù ]‘¤ß«=ð¼‘,+š‹„XÞGßë­ ÃkBè£É}º9•Þ$…•sœÉLåìëâ¥æ%•ôe¾ªÈÃô@-z}ekü{õÔnƒðvm,Œ}€¾ãzÞò$;FŸˆxr/Nždà(élFÖ ]¿ÀÈÕ]ÔPÉï4å€_³‚˜Ù˜ËÂqöâj ²·ÕŽìŠB3£El±ãm†ÈCýs[0×ìkeÓ8AšÆ`òÕ•®u $õ’¨_s•]uW á° ñ·u$]yfl_× üU¾ÛoÁðcYzT`&w6\vGíŸsžEqÈgqÐ9Â2£ ÉŠ{|($#E[áæŽãÐÐ+ +LØi¦’/¶›®Û:*oq`{/½÷ú ÄäB³¼&õ[R¡òÃô§Í5N€–"õ°Ý ¢xS&ß(š½žæö¶/‰¡iç tN)úrW“…ÓÆàÇâAHÜŽÎ#zÌ„˜t5WZÓ-aµÈê/R µLë ‚iª?ÅåAéhu©m²ØÃsrñÊ÷zLœ+M6ý²¤_gÇàÎs#èºæ%:NrvÉ©qŒÕ$Íš¬nÏ*ºÓyV[~À ‡W°êBY£½‘­ÙoA‡>ÌM)hÜßiÓ‰öþ2\_K¼Æ±i¯ b^óµëøëcä›Ü3¢ßãÇ—n4/ì'œl*Ežir™£‹=å팙á]6)Ù 'n`úúŸxôÍ(wÇõhfÞ5Á/;†.6qfîéDcò55²Dk+ìQ·C‘èÑiN²=¦À<Å‹(}R–舼Å›{ÜZCWj¢8ùyÄ›@gÞTp*ˆ÷“8v]>D4¢ÂßjAèóKØüyíwõ¥ÙÑ.4ÔÐÎGȪùɘui|Ò‡ìÖf›OŸët’©`h8Ÿò޾'C¤™0)ÌÝð DM'™E̽›"äÎrCºåÍI´1³ÁC99t÷ÛïÀz— Ïo-HõÍIŽ0³›|t«3=ãy‚ßfNÚN¹á†¹$Lä){•‡Ÿðê©3¼5OÐu9?Éû…Sç=†ÐܳN.—ÛA'Q†œõ½Ë¾9m'%Ëq®b›ñ“½5ûŽ^ebµ¥iÏ¢s0PwOï¦öÚñ0í2›yÛᎫ õ)ˆ‚›¶M8oë|hÖ¡`Iǯ=ùo {È9b-DlQ!£v9_”Cä0Ž™ò`Œ¹r=œÞ Ç£ÿôˆTu êÙH³{BŽÐ°)’tæ*?"¦&Š[žsbÏPñgÏpmA=®ï‹sZ1DlJ~g?‰5ĉñëvÛ£ *ùÉ„…÷@FM)ÌÿO»Œ)}„L/ÔF{×`Øs×_꜡'QÐAŒxªr]ºÅœ”–nå",ÊÈÂbC5´%Zì‡2CÉÓ‡V%`æW~üü/”L÷Qž¸0ôä!ô\K£l‡ Îný ÓÔ¦Ñ^•|zÜ X½]saJìwt>fGzà-W8àßÃ|6Y RÛa;¯õBòÈmx»áÝl"q\†¼~—r[½©RD.5>iN—Fä¡A™Ì;–‰WJäééI4Eî¬|ÂÙ5±Œ¯ÇØœ-D2à-d9ëS-9[êtW€¹ ±¸ìü”å°;é!ÌÙV¿U-qÁmâØô»õaê8 à!eY\vÉÌtÚ ·1È¥„³^­‡W›ààÊ%Ä2W‘ö‰Ñ…­gèï³­1žE(zã7›ë›à$#¡«ëÑ[r>¼Zž„I«\iË®è?‰ç¶(Ç¿³è%O„ Õ·ì6¿ÛXg÷5 Il9`à§Nö×ínŽëî×L¿ÇL¼ Ç´ë¾ôêL|'¤DFMI µµù)HƤ+Éœ)údþü—è¼Ñ~Êà»5ÿfˆý`eUþañö XîIñW uýÇ1x%‚†¸º8jÌ™þ2++ðѽ$x—ùž­ç¡Áékè2o›ì$è:õ~®¨¯4ª0IÙ|¼Õa ÒÉ‚ðmhî¼æJnx_‚ð‚) ô²Å­nCöíMä]¬09ØAÒ‡2h\Ùwx¸Vn1™TÔv9^iMn¼¹‹UÚdÛJwºç“+™eŸŠ Ydºî[v|µåç²ç(ºŸ“CÏ’öÀb}æOÒ¸Ö/‰g5é\‡4Š+…D Z¶øU0Ü…/?gþçïÌ•4V¹ZZýcógÝ@ý,kZ»ã½««N+OQ­­{é!§l¨j)A'å ¦ Môâé®õ'é³íª¤-?šl.(¢ož#ýµSÁþunÓ룷£ñãýø>? 7½ÞJeKûé…DºK›7³“Y9»bjþóTfºÌ˜÷ù”5l#lù/x¯œŠ{úT¡1°‘™ï§¶[€Îr»‚õApÿŒ9;.ÿþÎù‰¼’u°Ég:MiÃ+ibx1âDÿd¼S ÈU‹[Ø·Âõðzž"}œpŠgïCUjÕ´Lâ.Á Å`†ßP’DΡÞ„ïª-Ûº}{ÁÞzR [¬‚D«ùìE¿B€=6düâI¦á¶3oÖC¯[,ì )ƒÜÌzέ´ØÒ+G„C.qgñDs¾(çã6ûg…‡§BþÄúèE5û[6ÛüÎÁ𙹸t ù·©Ð}Ç~²Û|wÑC‡Ðˆ'ð­ªÃ7‰õر)Ý1½“Ñ1›O¿»U³Ó2Baú:xQ¯Dû^òq’W A¦õ*6¢‚˹œ–¶)ÔFÿ3´h7ÔÛ@¼ç|ºòîf4Ý‚¹ÌàY~3ãŸñ†ò#<°´c¿Mâ'sü¡~K¯"ò¾ÿØ‹s—‘w!»‘Û•B\^ñÓÅ#Vôx~(ÍR‘¡çüë°ÞÐóRÂÄ:z:´ž„¹—.a€çgX=2 }g‹’FKÈl¥ƒt³ÂL2"ÿÖ¡·ï¨ L!>wÀùùsè÷Zf—wLËã!AAsðxŸqæè8YÊå²sÚŠqoÛ[fIŠ:ýö¶’Q±'¿å8°¯R×ýLÆ·}Ø‹3¥ˆJj$JÌõ¤ßC1ñýw&çŸá‡N·¢OáC}-²ER¼Ýr†=)Óü~³éoåHÉ"¢8݆¼;/I%mŸ@•ù°=†c`ပD…Øì‰§«S×3ùJ2¶Ï™ºI¯"­ËÙwâ_˜Ç[ÔéÓ÷¶Ð $IuüÕI`— ±ŒGÏɃšÅŽ+§\Œ'Q뺖Ý,†ïrΣÒ+zûìRTÛ‡|I`l.J'ã’âG ÿ·dÕyÜqyYÏ3‹ô}ÚŸFÎÁëç±´õ²5mj²d®è½Á˜õ®Ø±à»Û„îªP I†bô¾Ê&ò=!ès! ì$߬£¢Ùrd±z>»øk,Mÿ¤ NKöaÙL†vþˆs1ag__Þ¶«èê3kÁ ¢½4ÏЭ^òäÙ3.Iüˆ*ë é™ÄtÞUX þ—´£@†4Îü·—rçT€sÊ|ò&D“&àwy$è£î]ƒf/œ1ä³$½îvš«Ÿ^®ªNTù_ .;Â>ì™ |a7 ~§šh@LVž‚/±±ðdî_΋ýD¢]”Êî#‘†À ¥;«ãlûflÍp8«OŽÎÓ'jë‹àèIyrøÛ,òxѼúÚ–|½ê äïà¹Ëëm4hÏ‘e¬[Ÿ”Ñá¬n0©k {O€uv-µ»ýˆ¾ Ó¦)ëØ+o‹iþ’ûúê6zÞ`볌H[ó>òOBŒØ´é1}>Àêà݆¤ü*[ÜÏ(f.ÂV%cY9ÂÆŸ8-ØjûŠÑ¼-ˆ QY¢dGîwð<•+•èZì7—j÷ØÎgÔ6¸‰ê«Ž3•í…kÿ Y©6ï:?¦?YOŠÆdà Î[¸pmYºç2—¢õùܪorðÊn ­=DOÿÓƒJñIni!L^ç®—ŽÙgÊ5% *óhbH)ghÞ=3VklJ›Ôñq¶zG*Ý2§ÖA&ùò> _Dzq\þ'ΕTì.ª&o†½HÙc^2:eYvè,x²—*éÌ$!›vÓƒ's¨}ª5]óö¿ïDˆíôt6RþIP7#±w3Àk¶4è U»´ú´bIïr7кž˜'ÊøÉYÉݰl ¾)«µÉéÖè«Cè’¹LäÝmÔCÉœºÔ…ÓCCM”ÚK"/ô‚¨„}æÔ¼R‰W&YÐì€Vóö¢È”Y„3p*¬/cÂ_Q¸ÔôæVbFš ©^“ ]“½DQ˜D\ cº"nÛ­þt‘ °õÊëèÛ:Âv‹’ã×·“È7ûqäS,ø-I­Öer_‡2Îáú°(’P¯ÕlÁÌhî}IK»ö[½°îmP³’‡\_IìT ‘o}f ]Æwrbä‹ê?p·ç§öCÌ{Kð„wœÏlqÁ_/ºöz#|©ˆÆ«•’ÐáØ|À†z ͤÕ÷ŒÑs{íI²pÉ"zVÏn(sZçºRוh»8›}vù=+#.€~‹ÄØ‚à5ØÙµ‡¼˜Š½O«qƒNF˜ /»›×4×úÎÁË«Ñ|b#ÖÿÝÇôÝt$.e›!9j;&ûqyCÇ›Ç)ô9ÃÊO)„H‘?àuüÿÞÇÇviDIø®Ù¸vM/$ùî´RŸbz\þ>>“¼™—Ô`À È¡K\^l††sÈZ©í“ý|Ýz}59§—D¿Ü:ž²ËàɅŬ¼R!6YÉaX¬›hâM÷n«`WEBK¿)p² yP| e¡jkŽ4ÔE•¸\ÍX,aŒAfä6ɧcS©Q¾ìnÌ:rŠ)œ[ ÓÈž‹'±¥y%ü\?Âæ. GþªU\Í©OðçúÍìËû§Pë7‡ú× ï£Óp:ùŠÖ*’™æÆøåZ'ü=‚=Y‚TàLbëÓŠuÄcx{¨€½epDdçbÕ qVâ}7 oåÂQÛ—0+ÿ Æ;Üå¦Vxæ³f¯äù‚•=o™-m&dI,„,aå=<ñ¬\=°»Š)’ Âñ0âzV]dŽÄ\…™¤põÂ.øÏ:wµ £bÂÅÖ¢Ap9÷…¹}ô2#uÞ‹ f$áXŽ d| KÿÙS×JAЪ@¸Ýþ“£ä° +¬Àú/Y{†yxûRUhnf;ûجÝôçã;Í‘ìßú‘ÍœTò1ñ[†‰ÂäwÑT¸°;y:ÀyýR°½ýŽûò7sqo{u`oŸ4ÌÜV {ûpwa6hWaϺqÐXf ¹ô1u¢–@ú¨í¾…v É’úÝDS~.«µ€EûbÑtùê ^–ô˜Ñòi¡M¸pX{úèCý¼8€|/Tb?8ϢϮi ûþŸöšM} {²Ot½…pç¹#ðǹP¢öœùZâƒ&Oᘓ–Æf<úÕkÏÇ,8ö’§!H&RÊÿA¯l,}°œ \?ÍzW|e/í¾ro,隘,Rcñ™»[/ƒ‰±þ‹nfRµøZè6‹¦ãsÅIÅw*°³òŸë“ ß.¡H«/7sÁw?ª´üÌ¿ØÎÌÑneSß?ǹþbÜ»¯Ó‰h2n¬$Wwþ…Ÿ½ÔžIu¤ŸÃï–L¿ŠŠû´Ée`2ÿÑybGx?âmÕâÿü¦Ù¾Í”‘Jö¤wBêÙ ;N]ϳ(VL¶@âÛP9{˜k{ÎÐù[_ûý€3|Ü£DÍJÝY›EüÔ$dVÏRB!F’L¼ÚL6oÇÇ-w™ûGÇ8÷v¬‚Où³‰™±™îxŒòÙ‘Hƒ$ÈÊ”tlßKý”=˜ˆwJT7j Qó®"ÿÔ(J˜ âeEHQH†cDIg7ÔÜBÜ»e©“:w«u»XCÛ­‰Ñ›XtÖ_D˜ü@Eï]Zs9†œ¹‹ÒR?9‚¢¥LÀp 9Ý1…üÞu s1ö¥—R —QèÑXN–ÝǼ’phü,so±ªøÁ¬îÅ‘Z°šs€sÞ/–(¶iÂ('ŠÜ—hþ|Ó't‡ðEô[N¹Üù°µ±Õ±’¥–¿ÿ2»Ãã©òø3˜µÛ…7Û£ÎÌÛìjãhú(Çž ÿÉÄFŒg{^î FBÇàFŒ ØËnÆï3Ñ×< „äÂáȉsŒè(Õe2^”px]iýL3tp|o·iâó:'–}¬Š&ÍKÚNÌL툌æ1ø=⋲®šäJÂfPxâE’7]azþœb#³,¡Ûr?fTá]Eºø1Ž{DsK;=Yåþ½øe¤¬æÞ…Êzˆ<ÄOmÉSÕ3Pó }–¤‚”‹,™ÌͭߪB<Ó:¡ïŸ1-rëBs¯Ì;N Ó¦¼“ny{†$®ˆ§¢øw§ À˜S£Ïþ–s5Õüàùf)l_·žšÚÔ±k7¦ÉO®ôYëf:ã‰,MôO„ÿ~Kû×|²þ§ðZK-l7âÁ±©Xu´p²Æ,qúò+p`U*šE)Ÿó*Ô¡'¾f Gž,#_ 3ˆŽW.ÕN¤í'5èŒcÇaÃþBÐb¦bìÇ1Ú´•Š$5 s`áŸ"tz¯OæfÔÓÞfZeÿí^_… ë|8a|©DáÆðôäµN´©å+»ëó*Ò.2…^ßzœû-õt-Ëc^òfï…ÐmT•mcÇÚðÀÂw ùTøÎ¹ñšE Øm G®%‘üÕ3ñþ3UކP ”娀dxwáÇbb»z!ñì;÷3¬¢Ù‡»ÙØ|QXñÍ’Üg]ðç%†ìœFwÙ"v†â «3 `;<¦‡Áí[XÃb•&Nnº×ȼmÛˆ.ƒ ^—qè×&öi{=<^ï†16ÒüÎoX{"Gö­fæE­‡ÈŸ ãó1öqvzíó ƹÌß§ÔŸ¤§LÖqµ„¡qˆ±¶;;y¾Žq¯—™°ïw¥áüwX¿”`2¨ {ü^q–éEÙ¯üRýè ³ˆüž˜ô"Žôdo‡¡Ìdœˆ³$z]€,cc÷1èþWEý§ä5ÂÖZ=šÕh-êqöÇ^î3o gtGÑæ!Fžki>Ÿ±· ®œäÓçÉz=NÈÒ äL÷-«B+úC¨Ê$™©•{He›6M‹äÓkÈ%Šh°ó=¦ŽÖÁ¶BpâUê\^N^XhÓŽ‹KHå²}X%ä>½#K6ïù³¶‹p9&øÕŸØÝŒÿyU›yC«ìaæåaòè÷¼z¢V±½TooœG³=æD3e ²-ÇÖOXÕËlªO»§_Å5/ Y|™ãÓ‘‹ÆÌ9Î1>ºA;—},éŠB¢È÷æ3lñòÀ«L;p”$èÏgq ûC—¬BÙ•[q‡ÁÌí"$ 8Ÿ•´ðƒ——ÖáƒWÍÌåà$h“«—“Ï_žà¿´¸ð^gGâÒ<¶@5·7c—€ù¢V€JcM¸±A†L›ðfOŸÍrÒüˆQR÷lí~v‰á|€±Þ#¬Ny6£1w |{½¼Ï± ¦‡ÅG8X–=â[µà«]/Šl|Íüª¸D­µòaaA«Õ$Έ©Ó÷;—‚ë…`*µ W ËOŸaä´&9uЈòMd7a½ggÀªucðŸ¶ÙÙé^Ù&…¾Ïå©èÛ.vEû,È þ©sÉúÙŽ`Ttž=vo ]‘=‹h>$~‹Ñö‡á±ùè?_ÆLIº¡\Gòë,/í¸ˆ£kCY±u©àôg=^3áòRø²9‚ÕÉò™MlRZûŸG´ð¼aLY+C^ÙAÖ†ƒˆ•›h‹˜g®~Ã3VŠ;¦ÅG•Þtá3Xm”Žñ±ÞdßêyDl dëþ2"~äh›I«ÊYyXuÇ”þJžäèú±X+»cô¼ñÄ;üÇ{>¯-…âÔphïÙYc¬öþæöó:vͯ(WèE_•îÊòsìªçáÛà»ê‡Oá^üÁ‹â"ßáÄÙ¯ƒ:÷ýÙAhÿt„¶,>‡ÕæG©iûO&âôwƉ;æôóf8²Ýg1ÿa);çÓ4¸áZáã_ÐâçvèÔ0„äœÅb#LSsî}š-gnA×ÃSÚ`ŠþñÒ}šª¨šÍhçKÐІZNï–ܶäØë½ÃwŸa¸p×µùÌþpczçäs¬œX‰eíEŒOk4®°ÿƒjꔓÃGΦÆ1Ç„h×ÍDYÁ*è[V_'Ï>ÁŸ™ÌPg[‘щ{ósÊ×0Ì×ÕÅÿù,ã4Îiöv_4±»u»Ìù¨ÒµùôêK"[»Ë"u°m®…²~Öô±.ÙaÖO˜QñETüËê2§Oë×°aU¨]W ïºDÉ M€ãŠRd»ç<ªçZ€ çwb®]M¨‰Ù3a@Ù´«¸{ÂÙSiøåÈ/¨µ\‹9ÇæP§u¦ÄÕ‹9Oà%'²KG—ÌZãD+ç1Àb·óìŽËwpAœž–ˆ»óPÞŸ·"X½/XîÅGŒ3'¯$€¦Ö>¢óÌ °&Oê=bOŸ×¦õÓV£ß¯ÓÀ³M|íLéŸ%¬nÀ´’¯œ+.®pØ÷1cY1ðÆwâÛÑphÓ?Ž“† ‹—Àƒ …x¹ô<8”.šõ fvx“ͼoð×Û@Üœ0ƒöšYiÊðb£ Þ6½ÈHYZÓýåpúí&hÙtVã©>Q“4 &³D‰ÌÛo œ¾Ü4M©G -äߊ×4 !w—ålgtk«¡jü9”¥{ò’YåDIb ¯Ã^ ” Š,±ª¾¼ùÎ9v^‚Oób‚¢8¾„«°ÏQŸHg;’'!FBŒŽo]ƒ|dI»+ÉŸä7Ê]ÂTPçÖŒ*K#Œxm~ÊßÛʨ{žb•_UBÏ \åíˆòÑBô£i2û^0 ]h¾…ý=>Ÿ)V"fÿÁŽ·°YËß>¼ÄÞ¸z×N‘¤Ñ#?9c~t´¶{’·-Áß/$©ÑRwàôñƒæò x¾K˜9Â÷Íöl–¯$Ùû;‡}‹¢ô©Áw4ùãÎx­·Ç‰È3`]t#Åóq‡b4è‰%® Y«™Ã•”"RýÉm!uÚ?pŸu¥±m'ÔiµFºà%÷Ò0äÆKlûõ ¾>òëXœèæ'×…›Ö?ÄL·,g2»kÀdýqT×µ“›Î³ÁO3IXq Ä’Aüra ÓgÕÀ—ds”Ä(È^M²V¦»Ø¹ ÐôÆî÷gïp‰×:f†Â v—DŸ“EÅBf”å÷(avì+ƒSoõP»Àiš Œ±_·Ï5L™õ\‘ä‰Sð9&ÍsÄÑ+tbÆ"Ôí $¿¼‹`ì¶yâ »ÍJ-Ñ€ú'åX4ZˆÒw ™sf#xèdÚ ©‚€{*¨–é“€ËJt{¥6–9õKâàºUȞìùê¨we H›‹AÖßX½|švy?Fðn†ß—ªqm¼ø¯!op™­ÅuìÆ0>Xê&@Cùíq‡×/È•hF¹98sï št MJä‚Ä–^‰"ªÓÊáñ}¬ðS]ŒœËa¯=.F‡ÇëñÀJæåšÂZ¿l°oâäü*˜f· aI+¬ÿ ùO óDÀžâvì”YIRÕœ‘•J¦‚ÀþÑK5Yóp\ÅÑ£º²Gɽ‰^°œjïÏ vµ<á‘-€#Á\+‚4·mЈ!¿$!N:¨f$žcmÌ.Lõ¨`ê¸ä—óW®èŸÅdA¦57ZDg¨[0–ËŠÙ=Iíðnðd´‡W $¤Øƒ]»j’×ì?ÏdéfqÕl “¹Éna,mHƒRöøMpG£d1y²&ßµ_žäqy T(çû§’ï 4žrC¥ñ5ßeŠbÁÒt8ùëj.A.Ì6м÷°Õe½»J™9õQŠÈ1çð¯Q®5þD~³nÎ|àéSÏltQïýÛ¹Ãs…Hצ¹Ô °È×1Æ>Y¯LC* æËfË’E’°—»-*GÙž §«ˆÑŠ|UÚÌ©‡8lnÅFž:ɦ¬?‹¦Û»ØUIò¸ú>D¿ý…lîºÀä¶NöÌ2H ±ƒdíÌåuqA—hèaóêoLó 1ÒÏùÀs¼Ëž\ýF7ÉP5»Û­¼ͨҢŽ*Ëú‘«¶íÂò1I!êZ¡(³y²äسB¼’>¿‡ÆaŽÀöÞÁSL£t;úMŸÁ`È X½ê,:¨A«m‚´¸²Žù@$17kxèòÃØõÖñLý±AüðªºC¿a{»1”šÚ0Ö{öq)ZÓ–üŸpWî¾Y)Íìïªâ~½êHb­RX%wKiàréØË1¸{˜ú´Bùv…=×¶Ú˜FPY¹¯Ó£j-ê¤Ûª’Y’^{?ã!ßn°Ýrç¶îƒ‰ì$°³SƒØ)X¨¿-hãÅŽòšñvc »ðò:ZòE’®‰«Д{9]1”‘Éå’öÇŠM8|6OØÈRÏ£©ä•G)3õÎl˜]†÷ýn1žŸ“ß·ŸÂG{†ëSŠ™+][¾<gOEëbˆËb[ãÀ†×K¬Àœ’EW•Oâ«bmÚßûŒº ]wú!î;~Y6N•E]§¼³°S.œÉ¨D•±pÜýÞœ¡ïïhR‘&¶ü°<}]êF…ó#¡Y€a;Ÿ]Ãi­ü°Ñc) ü{Ù7Š$æîàÜ=²œ¨]£)d}|Êy"0Î>/ؽ7ùÈŸ•ÞÄr¥ð¬> 5KÌy Wr!d|ç°Þo`×c Xrq]#ÌKw®,‡ Êì\ÌZUV1£^Si£ü p”"¢{c´Üò¶Ù?ßDOç&ÌܤzŸ ¿µ WÖ"k­ AbI{á¯,Ìß>‹üQã²—=Ÿh<˜‘‹ó²/qõ àbä p”v&·–~bªÂð¤ÑȪѸèóLÖ7=´m­†o2i(UAý W`td'D.yÉ ì‡¡&.8«Ò帬à"º¥æ3ÁeJä›b';³Cò„™Á‰#(–ú†=>²7ç”à¼a4Nÿ…0ã'\ü÷«ÏeîH »Ÿïûq›¸«_bêGðçµ÷0yÊMüiqŽFi(²6þc’:7ÀÓ)‡AýX0Hœ™‚¦+kpCcy׆÷ó”nÀØsÚÐéììÌ¥ yZ?´}ÿá)úNÇCÜìfŠ™ØÏJ°RF¯%ŧžÃ‘Ž¿Œý®G8á3 ú#ñwÙKT= k޽æ†-¢wòuh×a Zr¼õgåÀŒ™ØÞƒ¶23HµNmu‘ƒRÕ ðÈ`yúÿ® •‘l…`}uº`Ø€cvè«ešH¿ «C¶û\}†‡–kØAaµ7:~ZÈuó-…;çãqS yÙyøFÛžcƒ´8žM2ôoÀ_¸Ã¸±‡dàðœï Ù·˜J˯c¾Lÿƒ(fǾ¨_À½ò‹÷ê£Ñb Š%• ¢ó&ïaª/¹®ÿ†~0*Zpýý|’WÇ\˧ 8éÝ;À{Ú}R!vív,¥7^ú’}Gÿn‘Ê0!ÌP¥•×In{)üh‰•Þô´Ü6--$uÇYGÆ”l Æk¾+ÈÍ/Øÿ{Y<ö\GÁK 䥶Ù¯Ì!R²:xðáMê’¹‘×(fclØ™T!ç+÷}>m—Ï'e‡|¸“óbu›W¢ú»nz©ERn$a˜[èÿsÍ×T£µ¹–䪺# xz«‘‘&1*qðç%^¬À¸í3aͪ¯NêÂç0q=Gð,±"=eÓÿ¯QÙÿ–iŸ‚æ‹éko¼ßª@Ã×ýÁ»$üøéèÝáø[6Í5=Ü ÖcH„̬,ÅÛ¥\­ ™Ty[Vw›6¤^ʘ¯&­æGðÄÆ=ôrÓ_Öç öìxËnL!Fy‚ø"s’Ok £¯òz!û ^{MÃlxñ¶³25\WF¶É­µo¢Q+dÌ=“Eç[ƒú%2›Ý~+ fïµeè¡P,¢H]·oFaÑ4VóC.” 5À†,x廾«°¢{Rk`§X,¸¼gÞ·ªÞt§ë1æ‰T3²ï…è“q >J#§*RÑâR6\H “JcÉöÒi„¹ÒU_¬é g²æi"ºh?Çk·dÉÚ¦4|¦-Ã<6²w/ºdS)&_t ¾n&Wõ⥤{С <<÷ÑÂ3š\rq¦Ÿý éÊ×ztÛͪ¨š‹wB!ÝŽ!e;9ýM¢$_ÙÂwtøG›eƒKþefýÜA\þ\„nØâÞÇ™PiÍ!3¶±»÷L%¼‹ ¯e&Çãò<'&øí|fïÝ"XüÏœ~t‚œç»ñÏŒ6¼±Nƒ,ŸžæW¤ÖˆE‹þ 8rr%go “¡§o¡éj2G½Ä›hô¨Â­»šl‡þ|rjøäɆPXÔ¾,&9æ]÷9äûlŠ ‰qƒf„W“za+ñ—¬Ï¯ƒÊ˜œ³¦ ÷ƒŽ‡¶ì©'ÉýN$Ñò=žOu$ëHÀ¦;2ÔhW5œØN¿¾Á˜ºÔü\]éB—H`%5Íûƒ¯HÓ1tÞ#{¿µFÊЊ# ýõ(IÍL!-ƒB-¯æÍ4#¢ y‚UÚ Iº2Xü÷ ÖƒèExS4 ²“½Dý áõ>…æš}œd¾ ¼ùÇižK¥ežâ×û?/ùÓeq¢Šòài‚,Uy Ü÷ѧ]Ìð·™8î”@zÇäÈú}˩ܬ\º,›‹Ÿ¢^€Çß—mkIJ. ‘ÂÙ`lÄc1xºN•j }Eö¹Ïù9Š™#laÄ=¼hQoí$ÉT‘'V\Å}áfKÌnÂN²S¾š)ü¼»¸ë1ã¶eœ9=Ïšé¯Ïd-†·Ò÷•OØÖí°=fM¸&s$ôÉÖD}RÓó[k% _v¡gs-~]ÞË\JWcÔ¢Ò7ÕPÿ~þ5—c~÷²Rﱜçéœ*ŸhØzG‘Ìç3Änµ¨“–ÎùñðÛpr‚5•Û„R5½¬xÇKk´­âÆÎê焼: Šúè,{ gŸqÅӥ͸½Á[­OÀ-6êê*”ë>¦_0+ o3Â{+àqK–ògm~l,]ÌYô“‡Þ¿•†¡þX½!ŒM‘•!¯~lg æüb† ¬¡èmÆcÓnª²†nŸƒ¨ùDVÔ@D¹svõ$¯¦áã›4¢X„«p‡)š× ¿]¶‘gK˜­ÛNÀž7lìKIhº²Þù§Á•Ç(¥ø«ƒòŒ3{á|šä]ÁC¢tðöa¶ÐBÒ‚y!(J€²—Žã³Þ4ðÞľ¶&.„KO/1â“û»WL„<ê ~ô6ÀwÏ@ºÕžžKüËÌUÑÃý(™ÎðȽfáæ=vixú?_剱P)&j7‚•Œ3¥ÏÞ=>½ê÷X÷c‹‰ï”þ· S7ÔÁ°¹.“_ŠÛ ¯9ÕH0:R|ðÄYfÅ 0²‰¢(»¡ìã aãtXöMHì…_B‰ð¸Kš¼9$Bó軩sèÅWkáØ»mìØË³xÁ\– Hú¶˜Ö𕾹(úåÜ.^Ö);1üÿÆÁë[4Ö Ÿ‚Oe ø½äTwVCK”±ÛàH®½L¢ÖÒ#¨ÕåD·:7bĵë`<ÊðððyR5sûmè+±XR`ü²fLˆ\Uð%rSˆÐ½ýëyÜQ8Ǿ9ý™=â9îÔðøj#®CöND§FáyÍ{®™Ùk´sü’­&Ñ)$iÖŒáç…‹‚­ÐiÜS×ï¥5ù\–}ˆ‡½ÿ2ÏÂtˆ¾+µÏðÄýfŒ~œBµ€–×§a{ <8A§ áÄ©=t¿êo8¼$ì¯UÀƒØ§lÊwYWËfoü7½pØDÿÓ&{Ý¢Ì:¨¸p„\(ß@7lIâl~¡CŸVj3ÊÇæ@/y¼6”ܪp—#‹³•Iðê(r~ÿ~Òk×Íù‚ž‡¢ÉÐJ2¶ï8êyðÔ¾óNo£§#¥IBñ’æxC:.“Ñsé­«ä†P|§ 3£¦¿á…M" ÍÈ!ëgàœçã¿·¶.RÝÙJ´]H….ÎuF´êAÍôzð1üÃJŸæþËé‚…‹“QYçÊËIoxEïAþ&W²þ?³Ò2‘%Æ؈ÏMÜ+ 'Y6/Œ:ƃO§Ü@Û¶;ÜE›ìžø±/¢‡™Ys.aW#øª¦¡âf9ú0ã¼4¢Ó¬çÑ‹«”h—i;cu# šh‘7«kX¾[ë!á 8ÞÊîçú[Ž`ÊÃ3è?Ë£’$@Ç8ZŸ"÷{»U˜#Nÿöç®s©”¦å–|bìöÊR‘Ï­l+]I¶•iÐL67TŸ#SEéÍ}¡º{^®&Ç?ËF!ŒÜŠ‚GчÀY~ ʵ§õ†íppY%Ôœ•Æ[ñy {W•D~%²½ .GG£$™ÖGÿ8r Xƒöè;r›p"O1¾² 4I_Ÿ:àÞÆn£Ÿ ýø°ÙåHd¶°ªøë ÜшÏü@®ö5\-÷ ÝÜé‘;JÔ¾5Nìàž~;ÏC‡ît*UJúÍÓz3 Û)Šž«ïÀ|Þ !Â]Å)tù@ʤ‡húúŸßó¯è2ó&¦ŸÇ§Yûèú ýÎj©Øø$SI,νgOµ„SÛkX©Á5ø*`ñ½i@¬šзe‹pë`%f 5³ï5êá´Z6ü§'_ÁãÿiŸÉPÁ"tWÍÀ½J˜º²þÓEwÛ—À½rYüµ¸ nÚëéÓã9Pz°ŽÐCþUÎÏYƸ_–¢!{Tiöìa(*IÇbç«04 Bà¤À|ËA˜£Y[ñ(÷Ÿ80>ýxOo*›B˜™ŒÏ]§Óãå0+Kú:»Ùwæëмü‘ó”%.Ó²`ÓS*d„ŒVN½¯„Skcÿóv¬îD³ $h l¨ïA•6sòÜi+ùèt‘ýý¶Åýb5§,¢¿W·&íG?bˆnçñ,ýÿvâ®—Ãñ¬Dxé%MÌ·ÿâænªÅÁ|%`îਞ«f^¤¬µ,ï€v5øe(f™Q'7 ¯}޳®2ÏÑø]:3ê<xgg³… î¢@Ñ**¸þ¹¶n€ãØFqïóɸGçe…ìëM×ÉÁºæÕêk:•¸®¡"Aã¸ÕʃÝ÷AÊ–¢~èTšJùþ4àìo“çÜúAô}•Ë25P¡LòòCÿ#¼uk˜¿Ž–ßG°Fw7öçñFTn%óî³kø¡U%ó~‹ÒY¾[H«^æùˆÃ/—ðØ>Jþí± Ë—Z‘ô_W™ÈmgÙbÏmð\Λ|œÄw,ëðÁ°an³øŸÿr°ÃÖo-*¤~}”Ýl€ºyt/;3ðc6}QôÖJÆCOÈVj®¿~¾c¼»€û‰@òg}9[¹5£w 1/ JAsb=¸+йg–æ(‘÷ƱãÃÎxÑ YÿŽsr¶lÃÏKPñO;䮨_mfÍ7¢Œå+”¯”¥nÅq³ÍtÒ`ÆÖ–õrßÇÔ äQ'Hq’'ú•;˜e:ÁÌŽXãLuÂù8†zsþQåÚÌkq²=Ø…NŽa“7î"7 0ë‰ýØvK·ŸÃ£3üZ…çä·v©NJ4Y"*CCÉ *»=øz¹´zu6RÃwj0­ÛŒ rÙK_2icÊTxôØ€îÿ¡@vËd¼%ê£xoãž'Î*è(ÛAãâéÒD¤Õh¨„o p¥n€3ø1³‰2íY D,~^¢§œ ýÓIЯémÇr€ø´ræÐ}‰<Χú¶Û@t‡è¾y²d±º uþ¥D~:MÓ›fÞ×_±Àä6,úSJvnŸ…x+ðÞÓm$ph9ñP ¯K˜emäÙt“¦¥¿Àv+|}­i’Ï.bªŒ[p\tžìUŸë\ÈÄ…?ôÙ`žZ"Æ¿t>ùñ¼Î¥ö*겂þΉÇMç nI¥†N«É†{úÏß…9ùq¼¶þûWp+Ö‡Ó˜s1LÌërzîÖejd¢5ÉcfÑXû_¬ÝjgØ9\M#”5‰–ÐV,Ì+¡O¸< œÝ@ÞóÖ–8‘[Û÷‘ØÓêtï¿NÚ«Ûæ'óÕ¾g£§º-‘]s¢!Ej Ùb ³ö“zy=jkæ=ŽoÐ?»œœo¥XÆÅe‡‘Š*VJ1­³lÒÂ|n±à øð‰‡lµ~W"8…Ø—sOY¥£°˜¹'åþ|èú]†l´ÈF!ê[òža.ñ£Of-«™GŠ!8ÃA” l^AçêÝ‚ÈÕ3aÞ<3Òd¿”HZÉ‘„àXèÎ £nKg8$0ªÄè8Wˆ[cÏ±×ØOðªÁfέã–`uð>–ªÝrße?©žìw²1ÄÊ{?õhÙGµL•ÀýØQvÁ¨Ý5äˆÅâÏ¡âz"yUý±µàž8¨8´àÄ—oxi¤“̾ëO^ncúœÝè¬ÑxT*è…£3¬É½2!ÒP<›ô<þ€ÑÕ}8â¤}Jõ¡~”ÍØ«…ëú¹VþXí¹é?]2Üê÷bÖF}‚u¡3©ÑQм™«QÉðeo ä‡OÞÃÔé×TjÜ+ŽÃmŸÐêØŽ—ޱ#ï2ü+Æð…[4 .‹«ÂHÄ=yÂSoG–\s¦{lMÙƒF8sUëi»VÆÚΘþª©ÅS‡DIà'CÊ)sD§èì÷Ûà”m¼Ñ¥žË¨W| t·iAŒÖðk(b$Qh×[Àª!h²oä><AÛ×ÙQÿ¾×äbølçß'n¡Q$EaŸñ~è`^òЛ“üÝ}»'¡o9È·|6ñkôašoÚQíi9ÄôGÒÆ-oOø+-ú"çµ®Í`âf÷ƒ´ßÔ€d”-§Ì¸Ù̳“ö$`pÕU椓4]”‡ÂMÇéŽö4¨“6á ¶¥bÚÕZ:3n%5”  fÛðïÁýN±·›aF’l[ÏüaðšYL«îl£â‡§Ð ­x²ªG–š¼¹ìAØü254¤æ Êñ{#/ö¡=ukYsÅqƒ»æÚ%®Äß6Ü“`Ž»bêàޗǰ@GÈy‰U4ZûÞ'_šîÓ­_õÈ—‡Ù#:§imª=5ë¯ÂåeÌØ¼|ܯX@­¼ý ÿð[jë»­úÎÕ@§#òt¡J"·bšxˆãûw´tŸ•󎈸œƒÞ×"@Ûø&6Mª?@ÂV阥„ÛA¥3‡2ö‘£¾gaç¾'ÐÙ±|jè'ódjZ‡$ó¡6[ŽÙ~ÝþÇWjëH­½?3÷"¢Ù_nwÑù÷ÄÙG2zlY ©YƒväΛAP+Æ€|tn.?™ùÚÖ«RŒЇ1]qÚwÊ€©ì®a$uôH¥žýÄÔ0Ï7:Q··ÕôPÈö]øeÖlÇ[æ¡ v®=ÃûQ›µèh²)ø¸–å“mïÅðÏ)kúß^óßÀbi^ºÝí¦køûR<\ô¼ ï{:ãCM*!dÊŠ‹Ü/o|ÈÉK1n±TÌyß’0ïW“˜ªNUÉÛ?`æÏGšµÅèƒ œ•9„ç/,‚lŠ[!|é‡üJGˆ®µ þ«c‹^Üf&ó@¶Ÿ¹nC<'˜îÏ?D~ˆÿæ¾\ÉeÄw_`oWwOVó[°•!˜„Byè•™FçÛ ÓŸéÈÈÌfõžÓ¶'×A¨ÏœÜÈ%[Ÿ<¦Ó]¥q°'¯¼ "bgذ”oìJOmz}72%\E¶ö«+Yqþ‹£™{ ͡°:u.&»¼á(hÃ˱ÔãC»Ó;N$·À‰b{Ò°H…¾œû‚öæ³Q ðÅv15wäÅâÏ%è5u*)Û<Ê2Æg1çY=Ö7Žùsß‚AwÖÕvÑÝžjd2ceâOBúT O_Œ,ú /^cô®}c>Dí¥uïáX¿½>,Oß•çãV‰lXtÙ“lµju*º‹&¿´ZÞÛ¥9¬ bB˜0ë®q™Ú±Xóz%tÜÚ»‹ ˜¤Ð›ÜwO@R?íž‚;:(Æ5ä¦0_lsðÄ™YpUPÖò“丟¬à©^N®o7»9×mò †Oਖ#1üÏ>RËÀ¼YÏÑ&>Œ].›HË‚¤È%ã˸ðGFó!3§œ)÷¡ç`Ó¡ŒèÀ"F 4’Æ~?rêÑó¹/í Q†·ŸÙ×çÿÀ6)²0P挸ÑòøÓp»"óÏ=Ä ÉK iqüwű—ôTéšc­L­£k©žÎ½ësŽíßÈÜÈŽÝÓµɦÒMÊ%¬y’%4,‘¦UkI§ØR½\‡*(Ò-›6Á‰åÊ´“Úa¦s[}U“X†×ÃH¬ŒÅRǹõ¬ZÈ_F^Ø•LÝÐƒŠŸž°É 2xxµ £~B‡óÅ͆\½žÈH·OÅíӰØÝœéß.ˆ2…¬²•ã±p×<>öz,¹sVŠ&_y —gç@ÉÏb¼h=ŽüpÅ _„k7!e(–]¸o}sLÃM‰Cx3´*ï!œþB6ýÜFÎØI3ú±a˜-ɾœ*H/‡ÝK/¬Pùà_´¸†ï:ÍX¯E¬³m9èW%Ÿ³;aŒŠ“;¹¯Ù­&óéâ9+q*™‡×Œþ‚ðx=ç¾Ãu{$áùæR²ê“½t<ûîu±uF»0|’÷ûŒÖ³5e`Ãv4£éTÏ£çr¢À2î#yS¿wº¼$ÏÙÖY· é„¹r6Ê""]§0¹_‰¼¸« 2OhgÅ,zqú ø¼”\Šå'gcLÑ˱—æç8Ñ„?k1>ó'tIŸ ’Q×éb‘𲈇¶Dâ#¹*æÌ\wZ!¹•lk!uîûY_ùeDÓ”—tlY3Ã/R3ÿiÔ%m=:¤ÔãŠÎ,ÌX‚öqhqgžÍµ¤–øèÇyîôÀÛ ì½õÓȵ –µÍÿÄ<ÒÝŽºåddÌ„jÛ@‡y¦¤#o ¤¿–…Y×öÒõ¿R9µvSÛA10kO"1 n2C×Ò âiXÜÖŒãêËØt'"Ðà Óî­„0« zirÿŒõCaR K=Be†»Ù ‰f°+Ot0¿µ c×’÷ïúY+Q3æqn$Ž…XщÀ»xzðSàÜËX èÁûàÇÈ'¨Mwª¯Ç#o8lµ÷9ú}¹Øw–1X›ŒÑG8ø\›°…vp‹o“M®é›;\;÷ÌaQrDe)‰ž—_îÁ ):dx‡UùÅ*¶Áû=p©ªb’‹fƒ&"ŸÔ 73Ì!åg ¤N“ò¤h®ƒc'0»$„GÑYвVƒÐM2/rFžÔ%_&9sôÆSÈ2 æý•§&ßæÐi?ç‚ó¡é sñ3ÔÄ(2J>Fxå‚]£y´,pšD`kÉ<îˆÃƒ1q6Å_V§Ávü):샦KAÓÁ “í·€¡ð,ð÷W¤Wú/‡[?ƒ@l«˜&Æä¥ÞFߎEø©+ë’jg–óÓ£ÆÂlêõ.h; Ÿ1‡®÷ÝÍÖi$Þ;ù@Zƒ‘Ëï྿Ð$NÏBâz”M\ÎG\§ Âþ¿ÍPèD¾8S&Öfñ*cè2!p¼´·Ø„We蘺-Í7‚(‚~{-IÞ¯38÷à5FÁG¸w¶SyZáö†ªìhØ-  £;2—ÁîzÀ’u©ìð›ù¸{!?i$1òÔ‰ÅT^ò&ùô}Ñ£9öäÖ?ˆ¤~a_ƒ~òÇŠ8wˆ²z¨Púš){$A*Ó.NÖê¶ìÊ^=S‹c§T‰„ÛyØ}ú0M@]˜E‚§h±~‚Š4ï½–4Ü‚çý\'£€DÖk¾?¹{Iw¿vA´÷0àlޛл˜/cñ˜̸¡FÓ§Û•ü.<1?xC,sÿ<ì½ëMÂe¾`ÿ?ð+Ô"‚í5`¡¿œ«øhP_4Œœ~…õÝu°î‘ -µÎÇ%?3GbÈ¥°?lØÁ¬žY¶8ãk ãñ}§ w·\»èF2_T¢m8$UeÊ”#ØÞÅfxcS>fR}yãÚ,æ²ÕÒÞøm~;ò¯–¤y÷¹—2P¡÷9ªôÂì@AÆúáŸçm˜èûÀ™5[œž¾³ˆ Ž £xçl4¤v#wPÎkWÖO© ©¬Ë'^;Ñœ$ô(üÇt¦íä –ªsØ { ù(?- Capór1t‡k·‰¸#ߟþšÂçÓZ+‰N¡:‰4CžÜç À¼ãäjÍ%-·N°Ð8ý7޼¤Cl–Ú›Î`nÍK â›6ˆ ûCβýàMæ87eepõöàiËùøéC—ûQŽÞœúÖ\†(݃«zÃÀDOÿä`ÆÅ†wÃl’퀇#2t›ØTbÿL…ŽL8`æOŽH°6¦AfÐñ)¤…ñ±:+åÉnDÉùO`צrf‰ÌF Jæà·i2µë$œ½{mîÝ9:·RŽ-;ˆ*ë÷2jþçQÓŽ p·s/4Syjà™¹)ò—wÁ–È]ìýG˜î%@6˾Ǣê¹ôç ^ˆëšCk÷upÒøŽóêâÈ›·øéF!»X­ž=¥æÈD:Š’ƒšµ…]›@6A, …´Žõ8Gìc×) 7¤0l¯©8´*Ué5±æxÓS8™´œV725€øÅvú“8ò÷:›:,CvkRß½òäë]®mÍnæöÕfæì”)DR)Ÿ—¶Sw± çoau/Áwuœ¼è>xõõ¾Óêb–˜×Àþ«­+˜Aâîhð-¥u x`Ž;«¸>‹ þ–È.]/„Ñߊà\h3ÁCœŸyÂÿ8¸î¸¿/ÞÞ{OÚSI}žs !QQTB¨‘•Q´—J‹¶ŒJKiïÏs.!+!›ÈY™‰üúþþú<¯ûœçÞóyî¹ç¼ß¯×û9JÜ豆Ylê¯[œv'.ß½wxíƒÝT3‡$¼„ÜBuâúÀ˜„40Q\¸øë<*p&N9¸Tà7çwp.×u資É'Ÿ¸»¾óÎhþúø‰ÙÄË9víî,º×t;-ì‘e¢xöƒ¡d •ä¡âuòŒCxS–-K­Êv°nó„茂y8e@sÕÐ*t%#l=‰Y[K° r‚ÝyÁEŠ¡wÈq¦eßgØr‡‡è¬ýŽ¿ülÁXå=7;# eB2eIj¤cs&9þŸ¦ž†G¶>l¾“ N7%ç©)¹uS“1½$Dį;Sçò½dÈ çßãö(ªQ•öêL)š©zÙ·³Šcó¤ ߬É ÛTæ…£(Î.i„»;›áOv 츚‹~÷ñÜ2ö÷jkØaFjæêÁÑȇhqÿ®åßB—íxï¾çÎTðë!¼",B¦ÇIлeéªëÆôõê7Ðä†ÊöÒP³|*IÞòSvp`âj+ó>ö3ËÚú e²ÙÑ(ÞbÇ]Êh“Ûõ©øìKc›ÂOå"+À“ôÓÓÒ}v$œ ¹¸æ—#Ç(k:í{u<$ìÆëüñ4>“gVlË‚¯¼–T>ÎŒvT6 oÀ8F®…ð7jD:=E¬I§Îœ?Å—_Ê]íYÊfN¼`Cšða‘/´œõ5õWŽ4[Ç“Fz”ûÉs¼•o@'§•ôsLMé¼ÈŽ}V¦uötQ¤¹pð|(gbÇr™™+=È»[jð¼C‚<®Và ÷%ôÞØ|’ÆÂ¾Â&îó`!r%³™ñ³¯‚%©[ÇÑ_ÃwCæ$°(÷ŽhÞ{FäWi&ÞPIGÁ». òQŽh$µpÞ¼I©C²4Ü(lJÔÁ *§AÍzfVÉX²õ õ)áô)Yœk×î0ÒçýÑÚé9®#K¿H R+Çï âr 6GkÒ’Aú<¼˜•¹Ø¼®áò7ݘ—º†µÕÞŒ+4ÃðWÖ{Ø©Ð¹Þ 0lÊdžG¸Ñéõy ŸW>Ú^…yl|õý 5•^0}åvôÙ¡F^®= äÁO¼DöÁ½©ûàÎyðÎþ„‡ IìLMð~Á ‚ÂñRûFHy öUƒ~œVYì”~8Ú¼®u¤¼»• _¸?p|ÖQRãB­ïÄ ß¹ßù;Á1ÃX÷ž Q¼'P·5á™0´Æ¥‡H8}OUôÉ× ›ç/@–®³ ó[—ãÔoé  te9=õ‹Å'¼T/ÏŸŽÇ¬¡×8ê´o¤ –•,"í Ö²™t…u{@³žÝæòDW³Ž¸“p=ï2gånâmµì¥Ïðvü &…žìɸc@sÕ^cô¿Avÿ,Qš–ÁÆíÏÆuáD*ª–Š#¼6ix1Ï t範#™E:|¤ &+•h­ 0>!/WX>‡9‚0ÿÐIÌú„*!3Éz´/ªÄœìz°°H†®Ô_œJx»zñ²`½{ñþµ+²?<0­%S6àE>ú5èæJÌ!ƒ¦&¤‡ã +7½d…Û˜“k1aà&û¼NÅ„!Üõ 6”h£þÄwpnÙŒéÏUɹöJx¹ …Óá?áõcð2ÎÅïKqÁ—î°;6ìúUk!øM&*Œ=buk«HȺ:¸¡ÕÇ>׊bÈ]ݘ„¹íoðûÐeVÂê…ã/ã_¸$a3ëh£‡;[Oàë´;À·Ý‚½·Ê‰8¦(£ÃžL˜s²5Éxf8yY,{²å¯³™Z+©½¬&{@ì-[ýòˆ>®qQ .BÕW¾tï½”}!NĤšð™´Yš"ÍîœÊ âK+b¬Pó5ãûy9¬Ô£>vcR+¬œw޽ qoœ“%AAw¹Âœ«ìaiV%V„¬5®Áÿ~£¢ÔiÌãKLJEÜyÿœ™sù»~h/n۫Ǥ9ŠѼ[ØÔ¿ŠH*NÞ÷z´ø-/½u]‚œq¤ÚsGQV”ôþ>Æñ¤@ÙˆÍw;‚JÂDðl#†nŠF¡!0úZ‡U猩—yܳW"•¡Ø`{—5êbæ±9œÆJt{Ƶ3yŸ1XÀGþå. ¯\KXÕ‚é$ñ«}7JoÜàèŒ\@ë¼Lzª+#Fía†½±ŽN"UÓPÓüª‰@ÚÜç°uà!÷Ç% úlC)kZ¥Më2Ì© â›ÛÌoµ³L³´nR¢rÉ´^謮Ì×*×avYttBøÊjvE[º‡»‚̇A&âS4×ørˆœSG—²´¸wøçe3"5Æ7ÔŠyÄå¹Q"§ðRÕ$~?˜ƒ¿O]á4,”'ûw=ÇÝÖh=üòœnq…æH‘U¢½ÀL7cº.ïØÈG§Ü¢L‰ =tå/¹ {Æ}9¿ÎpV¢%ñûóƒ5ÝCÈ“ë}çyš{UBTfÑ{x*¯N ¹²ä¾Ž:éZ§ŽщøFç%ܽ1lýgòB:3Ì0¸½cX|ãÄ{ïW|pX~UhaíZ±9ú8‡Ýw¨žQºª{ÀÍ»™¹³'|×µ¢•Î/h[ªOËÇq¥È'£ðS.r—^AÛ¿ŠÄrm!ܽµ‘ûÎÂD©Ò,Eþ/ÍxÙ‰%O'óÊò%½Ï~­#gÃÿ¢Õ§dœØ¡D~ž…ù ¡¡Ç©ÕY!údg$úzÑ>ž2ܤMq‘Ÿ&> Âfû°âr3²Ã¨õÊ’­ü§Fן›Nn÷fÒi\/ä};•TÚ}»ðçCªxͨ•"Ùq ÿ”çcyù"äC匒å!\”pX)üÖôŶ æCÆn)†ûå׃»e\-BÇM´ÉºÂh¦zc ˜'š±â[ÌÈè#5ÛdC•ûÉ×IT-0€Œ¹@èX -Ý÷ƶ$sÒ­èQCHxF«$mèÚ0ãé¨éI"€G`k³#ÓØÿäè5‘TôoåƒàeªÔ¼K‚rC}©FO2hj‘—çêØñ¦*'ËûôqÂÕ¬x{Xæ¥`µ‘6Lî£ÈA*3Õ *£´á³n:,0ueŒïö€£ÜI8ôç(¼Â‰23*¨%E:³NÑD1žœä¦¼—£Éûn8®â€¼SüèƒÞDíöOÄc¼;UØ—{ƒVÒá!Arqóitp-…·¹ÔþYÄÝ ô¸-ì )zsílÔ#§GªQQ« :Ž^ò}'ÚgR5& ü:"Î/Á|'cªýà)w5ã{^bKì—çŠ'c1B>›yâã€ãË£p¿ZçÑ}ŽÔôAÜØ~z¸Òï¦&0Rß.ºAN© µ¾VËTUþ‚H]1YI®‰øSó[ßq¦Ë*žºŸýŒëÏ%IŽZ˜³ò7$Ú"ô¯œnH}…=ôö-t I ÷|ÅÑS銓j¤gQ×jƒù˜k {+°BMŠ &•À™ìðªLTs8Áì.– eýàRcI6£¥Õ¨ºðUèûÇÒÁ&¦­´„Ý;.eGÑKr)$®à¬ÿž»‡CÙÆérd‡Í"\:º·þ4¢wÞ›²cÿ±¯“‘|èE½¬éxÊo-¹úð!nY¹™™ÚŽSÝˈâ5}–ÿ€ŠÝV¸âî\2åÞõº€‰¸i… N8¼®Cø àÍ“M´6Öˆ4=ò 7æ-¢‘v3¨ÈªKX¿WŠvƒŸ·{àÅÚ40«2Á——\W7ãýØHÑ5ç]-xݯœ  ‡#HÃå³X´?˜ídÞ”4@û¼ H{áD&lµáÖ§v9™Á6}GLvÑy"xÔì—Æþwµ0åž!Æ/q„Óá’0¼/ŒFmÐ#beÊÈ»w vµ\†'7Ò/v s• 39±KÈü6Æ÷”}åÎ’'v²± N •™B"4%ÉJ]ZsÂ&âà‹Ü'Ô›ýˆå>üŽ‚Í÷Qß¾|ž²sþ(•5Ü€6ê½ Ø‚¾I•ƒ)üÎ7ÞÀ*½gÌ{yl´Œ¥­\}fwê9ŒXÚO-Ül‰zã?®Hê¬Y­æ¬ýSÚ¿­“fPü±)8äŒÚNiÔdô &ýŶæ6<µÑ—Xb?BNiù†ãèð@œ*1xùh>á. ¥î§Azv:coÖCÿhgÀ`°/ûPÅ^¦ãÓUOÁH¦>ôÙì»kÉû‡-0sx-•& ãoä1YÏÇ1÷<ÃMÜnF|ýøø¡˜FÓŸåˆØù8NÔo]RãOÌ p¸s€]”è‚/¢AÅ-d /—bô”Ür€—Éÿœâʼn¬Ç'av³ô}FhÃM|jþ•õZé ¡~ŠôÚEq:ãÅgp®‰Çe:G`Ê›;æ?Áœ´æ§Æ2[™$Oišw,‘|¹íƒŸ͈¸ØÜ|®‰ùeÜñÔ;¶{ÕqD  <öœãçLé­¹Ùŧ™‹¹Õ ”¦ÅX°Ë¨û¢ONO¾(Ò³oÙ^ƒy]Êt¯C¾®ÆÉq打¾H•®æ¯f<þ|ãøHfÂÅÄglâµZv\ö*jân~{èv£¯^Ÿà~Œ§ñ]’$3$ »&‚ðÜ×xfy¨5þ~Y1/ØÞSòTIK.ê–2Ï ^à&%\]úÇ¥Ð^m:5$ÞofÒÏ«Åiçô ÌþË~ª‚ú}Epsœá~ʈ‹®bü›÷‚µrÜ‚{Ó\éŸùø±#jfo"‘öùl·Æ0ÆoÑ ¢ì#+ò|ÎT9¡DÇt•¨é×RÔ&í QÅOž<ÅÑç&¨qP&Ö¥pOÕÅáÍ W`\IŽqå+€s£Ð‰¥==•!°¡Éž‡e@OF*{eÑöÉøYNßN½Š›;V¢¾9Y:E›tа7#+Yùc–pòíN»ÓA·ùÉÝ‚(â#ù‘iø™Å|ÈmÇîÛ²d©›3)üùíEI9'•ʹž$Þ®×á´Ò|YœG,´Œiå X!£ Û?œBóÒ \¶ú#Ô+[`ib-º¦¯Fm•#ðuP“r½›@»7-“sÁ'P—\Žd†ÔaíÂÃðÖ=‹ZjØÓ6—Lº4 ΣÃlC§^ rùɸDԞЎéôn&™xCvÑt¬V¢%‘P÷(B¾|aÄeY¸ÿw;U6½ƒ?N1ð}4f-cŽœ»Ãœ2reÞ4ºÑ„ß"ÔÈÉzÙóoô=«/¡$Äg n Ó¢*RFô ‹ÈM7Àì3¤ö=E¸h­Ý\£‚ï˜uàÉkFÊŽß`ÂJˆxRãÝIŸªñú^ jëë5YKî UT§ß숎ùŒw!¯ŠÌàÞ‚» ÛÐ_<Ò ?ü!°C2ÀûEL¯Gîëm$jóLüÙþ Ë_Í#’ºÆTc·þuqe½:x¡[þ˜…»ûµ{(Ý<Òcµ>Û²ÔhÌ®•@ù3aUZ/óo-åå·¤<ù h£ŠïoçìÝã†÷ž¥ÐßNeÔã³ù`7€¿½1=·,(×ù¾ØÑÙtÛ 7“¸X‡ªÜ¿ù uV|5ñ|qê]UÏ SIcw$Ì"ë=»è¨ø*°p»‹OÆ5Èqåx³£1 ô±e6ÚÏ¡m !„VÐ9¼E°gýNÜñøëà ücáá8W.žÕ ’¥Óç$aDjZþìÄúïú´½&³×e€|“XY–HI{²¼¾ãì’é6äÝŠGèý ‚”àÇž,r=Ú•ÞùÁvµ°w¤IÏïtªäÛÏtñ]fëoëQ«­~( ƒ×¯ãUÝfö¢ä3g¯®u¤çøda{\"ºÝW&qƒöž3ØM×Ð&<­W|8a%I$í/QÉOÅÔ+uôÙ>'remD„­!Š:Qäpˆ#œV> ¾°G ßÂθ`Ÿ5áí°jãv¶k¸€u›2—ÒˆNˆûý‹y´Æ9Á4/»ž~öÇ»‹ç“õt¸1S#ãgQ· Bñg2Î_2ŠQÞ 2»ºÄw§'í_¤@HÅR™¶L<⺗½œï…2ɘyî*„§[á°ð<»l é€=÷›ÙGAí¡¿›]iÜ ßN…ä­¶T°ô1\?“ĶÔÅÐ5{Zˆßft=} ×-¦STÌÉû±¥ôo_&fYµà7>ä¿;ŒÏdßáåÇÉ`{\ÒùЫ!,˜êE…]ð¹Ýmpë[E£çÀìœÒÐ\ŽI>ávæ«é²¤¯D¡`\®#A«Ÿ$yåíôîÂböÑajµcíŒ!‰ïôéé•ÁðHË–‰;þ·&²ˆa‡ÖÝam“ò íÊyfÅ]Xí©DâÓÑ›æã`×cGW4i’ƒCì±ö |°…ºÃfú~z ]4m#ýµ$†ÙÖgFóßÑLKuº²b ÷º 6‰Óè ¸‚šªÐ1~ ~›ä²q+7ãõCè—ÿή‹:Œ?j7ÓôÏí°ã«+¬PþŠ¥þ†01_Œ.¹‚?ö@·ÎÿýJÍŸ :‘¦²f ˜Ô8èuv³XBòzG9R wïs¸“=W@êQ ~sÛ ™3‰ò´b ý½-64€ÿ©|È~àM¾ÅÅÃf¿Z(ÒÙŠZMtddQÏÇôtrÝ…†v°•˜À¯Á®Óó—;’·ž¯íÃV,7ÅÛ廘€2ò¥v”*Ša»®Â¯oo1½øiéà*TOi‚˧Î1Eo_q/Xõ‚öwS|±Ÿ”D‰eS€~9O£â1lñÔ tà¤u^e'ïÅã±d–"Yx†)u¹ƒ¥{&yãGa¢¹J›e/ÁL°¥Ï7š‘Gn. ²~Š&Ò,_=Ìv< å=bÄq<œüŒ¥gÊ~1sZeÉ”™KðG»¹Òÿ·ˆà¯k¹8òhnû'ää0mãYF}š;}b Ùv»ÎI뜻¶{œ=×€Ÿ&q…ïuÝîŽÛì Ó}ýì‚§%6¯ÿ=˜BT¯¥Óüù§KS2­_ Bš€šä’?³§_k[u÷ xm‡ÛŸ–@ŸÍzRä2ÂŽð4bÏÏ ø‰j¤w¼=FxÉÂîh˜ZÂko3Û-N¡Ëþ3hjý…s¾ÂܤåŽc¿cÁ»ö2¼ä&ë"œÍ“<ïCÚq¼7õ&ñ’[ÒÔë“v/û óûV^#êý7t‚»ñ­¦.³°5™Ø›Ê&û7âXª4¾ÜÖ‹ß¶çÑÏcútÞ™ûx+f>G5â ³Xätü{Ézó®¥Í+ aã÷‹8ZöŽÙq:ЬܾžäŸ}Á(¨×C‰… -qù ÞáWÀÆà]¾c24ì\|`«ÌoÁ’}7p·0 ½–´Pa&½¬mG²ºzqû][´o­ A•*܅龪D¶ë(=o=³Ù’ð:žbƒÚqÏ :mM£c›Ò#®£ÃúpZù$¯¸È=b„ÉôUgTlŸÁ>×–ì!] Jºè*aCÎo…õSÿr?w*«³”QÎm• sgÍ¢Iû±uèWYŠùaÆGiڽǔ>æêÓÂ"W0Mev*²3OÍEÞT.ãê’Á×uAeÛx£ö,X ü:óè9i28 ¿§ç³Ç%v‘¢nºº1„¯¡…{wÑþk Ýî6À&™N1!ž'<åÈêí"Ï–Ή©$¥Mµ !ìÎ<ºí½w˜¹a!Mï¿|ŒËj×âÂ!ê¤þ›5*_ëSzàQ>ª«§G¯øa¿">›jáÙß1Ò²ƒÍܦMpD›æ¥Ï³ÁÙ4„U®Æ¤ì@=x5—Î^îI¶6O'ÛŒ`´R$]ó ÿ-÷£/Uîàë)n´þhñïÃèiÚ¤J¼¤gfpõ#.ì>uÒ®ý/ß«'§!Î,e³Šˆ–ýj Ðó Ãu2$IɤùD+TæÐþaØò ˆB Éä|Ã%g:iŒÊßÿ¾ÿÛ¿ªP»Þ¢Tæžµ§˜à¡sØQ×ú—„Èì´Èóª,—Ï¥ÁËÞ1éóRÐ`a qßΪ¨y±‰ jrŸ‡VF/¦æ÷hûô>¢t<‰rM:ɘu<ì6``;JÂUA[0óp…G¦Ûim1îø¹f4 0¾.ë!³¸vÜâeB(]ÎÝrÏ‘„‰3ÞC— }W=ûÆ)þx® ×ÿ°}<˜ñÕ<àߌuzjô€¡³Ì¤}%ÒØ„Ë*är|ÕEf´×³¼,™MÝ¥Ó“ˆ«2zU¼¡—ÏÙ÷]0ÃÙ›.ÑXBjj”Hµ»'ÉÔš²‹À®°†ý;V`)jü-Âíg\Þæƒ­Ÿ‡A|ø²áý°É"–Ü› Â÷–‚m†;ćÈà×Ü\8ðÁš~€fv±Üg´b½åX* Eø&qàí þàªMëB­P?ïÜo/b'&sž•C"9Ó±ƒJ®’ð² Pñó;îãÅž?æpµy:å»Þ…_T“ˆ¢C„ÌÑgìƒñ'LÔ·%ø‚Ù¿ïßÀàÄûx:³Ž‰ûb@¥ŠÖá{õ·Ì|ó%ŒF©"väOrÎ-°â#YÇ`^%AOÜ»ˆ»áJ0Yì 2a S|´gÇÙ;̉ðZVÚe-'¶/j’û/"M»î@ÀrÉIn§„ñ]xtÌDp/Ô鎰¯Nºo§.¶Ya5Œo±¤r~,=ý öjgÓÅ öÖŽ]w‰€‚@*í3ÍvÐIU µòè“r8Ç™ô×…^~ÅÁdën'U73&òS#ÞyÉ >PÊxt܆Bùj&S¼?heBd@#ÆþeŸ};Ãl]àOT`¯Ïl¦`§ÚDJ d ûÑ ã%v¿·g·] «{…©Ï¡ ÎCž¸æôꕬ QOHeÌ.P‰ÜO¯]•¦yúK`á€4y݇±ÎDÔü)ìÔy‡ø{á%O0κ¼i É}/ƒq½Fx+M{„AÄE‰ðÊÅ$Vžüp¹Ås«qf÷y’x¥yøHôiG:üÂëM7áÈ ÂÙÒE g±Î’µ ÇÉg³=ƒGk¶‚¨r%ómDw}Ô#¾êçqE²1ž©-e?€x_´ Œpf¹f‡Ñîy,Ä XÉ/ÇÞU|„†'¸‹n&÷%ˆù9ÈzqÐIyµ&l …÷ƒŒ©u ‹32HÏoêщ°×'§TÁ3ß…Ôäa¯mOz’ˆÇgšþ*…[©ŽY8ã×Ml„òAÙOƒ²!µPù¦“ýRxÉúåFUl£iB|½˜–š›Ð¼£P_=ˆцvá£ó…*/µbœ.Íl¢Ý^¡tÖm¢.¼žxåf°ÇÜ.±mÆt]ÑGX&ë… †`A a2qj-U*>±+—‰Æbš=Çn—Ò£<û‰©:µâ÷%ûsáYm/8ˆ_„ÁvBVZù]ÞDztã^X¬0À¤ö~ÆÜmräÛ6YrI~ é°ï`ÍN³ÆïÄiþŽ•p\²—ã~Ž–>U…œŒ’\"O×é-€3bɺé•,Ÿ¯=ºçÄ\O>~“eü8Âä銨^Š ïAÂù/„•ÄgUµh¦ÉOﬥÞú8t’Ü*W w–Ì bŠhÜþÅXûv'ùsB†$ÍÐ ÙMr {Q‚î9Q‚}Š9tç«mLP†ÇkN 5ïc#Ú'·ú”zm¢¾˜•é]£‹Œž Õ~¢É$µå°{øç¡­·È“w&d€GÏÙØÓ…{ã¸Ø¯Ï:Æl‡Œ»§àÈUy¸Œ”³‘E.ð|0ÿí’¢µàÒUGzLc'¼WÇïÑŨþ²®t г3À•‘°hçV¢Û{MXÊèjÇ;ÊVuáŸb>ªú1Ãâ(”W=ŸÍôèg¯­> ëŽSSíôXܦõà×bW²õ® m7‘¢w‰ æÝЩrM<0O"'qÛ™ ˆdÁã»Wàð­­gÕÍþü²„QY/ƒ-!"¤ÄgÑ2<~yððH™ñ#ú? d¤¯‹3‹f Sg"ø4^5CªÌj2ª¥Éì ‘£?Æ5I\N ”=£ÊÌ#UzqÓz°Üõ¾FO¥ž÷ÅAù)sùðV´.\Í$œKñ`’°›U:{ž‰œ«H`T‚j/£7úð¾mrgj@¢Îsöñ£vBZ].à;}x‡áÞò¤¦N—¯9JŸÕäpö-!²ï+pkR =œâ¿~(R¿Wšää«éðÆ^ŽT¿pZ `CwÊ(ÒC ù„½}aÄèK5ÔOû²š`t>ŒÎI1¤~Žd×ê>z RVOÅhïaFìŽ‰è …7Üö2í®€º‚j9½Ç%¶&4Ó5ì'k›™=Y–áHŸÍ[LÖ6 Ô$–îÙ“e^þ`.’KæÓ¤^YGûÉ †˜Ãµ­ö´xåØÇá"÷Œ^Þ„b ŽôQªÁ]ëeàuutœµ+]ØÒ3w°BØ]ž° ¥¼ õàkصŽÑ*³è¹”y™É²hæ^{ÄÙU<“Ú$¡ÁšL”ËX.»»¹éï`ÂV†ëDò‚hÔú€oTp»^âüiàî#@â¥é¢ÍÑ÷ä4z¿Oˆ¬6lÀ}Y pö¯ ÙÜpÕWrî•ò“¯ÏÂÉØÝðÊý(Ί Á¤Oasÿ4T} '¹h;à †Òס£[1™“Ø„]ÑocT oÇu N¹§KA—ÀæÔ×òéïW®Ì7E"°iª„šÁ\CTmkg÷¬d/\9ǹ¦y“S`xN ˜]¸Ç°Agádý sðKù:‰5ŽY"Gg7­=!JØÕsÙž«é³Â„Â|û'‹yV¦ˆ§R[ðŽp –õ!ûýnúÙ^'.Æn€?÷…!\¹ ÷ûˆãü]pnõì|Ë\¯x†o¼OBÏ$.ù¼¡ˆ½të-_M?õ„}C×ÐB4ž¨-ÞB š“A%å",stá.0&¿[3C ³ÈÿßÐîqŸü½Ã:oJ&–Á‹ñû®Â. þõ³›ŽÑN7¢pn„~¥…騿²…ùúü=†~ C׋!ÿÎ\Oÿ‹ÒrÏØ‘Øœ‰¶j”nŠÝnqzØ€ŸøÕ^f3r—’ô†NÅÌ"KqrãO ûÞ<‹9¸" _of·ŠiBÿ^sj©4¿h—2Ù¢(£Rû~<ƒ1wl"bÔ~¿ø¤¦ÃågûÉ/iWØX·œ=^8Œ Íž@‹€;IÖOfdíÆÿ4È_ïÈÁ¢ýèýCݶ3ÛËÎàš1rÅÖ€êmOï)³j üÔ?ŽašÙ84t”núÐÓR±67îAÚ¹üÐΜÎÀáàPj„'O7cÚ•vÈ8reìÃQ«øáŠ8ípÜ—Î=`}Óó© LŒâ™u'Xèû+@ë;_03FO1 ’©Œq†9¦¶®"Œ­'&¸$£¶g-˜ª=ë¹m÷w‰«Ò‹sX+Ѹ¤Ùh*LŒxÏAÕÈBø»‰¬èõÇg°±s:ÙŸ‚e£¹lgÒîÛ99¹–uÈ(fV­‡ƒ“µ÷Ó®­œç‘|ô°ó5,yZˆï7JÁß/¨P¯!|pQÎŽ½…¾ßS0»&òÊsµq ’ bq…)żäÙpƒg œhä!£5N9®ŠE'©¡¤(z\¿ºæÂôA+"̓x]÷Û<HRÙ…ç–ãn10Ý“Œm$Ù˜âp: ­B%–|ƒ3›¥1¤Wˆ“};«¸f$rË”LpfÒï7àÖN[Pu¸Õ#þä".}­ÏF8>×i1W€Vn<ÄUÌÙ@̶èãü5úè¹'žÞh c(:ý¤¯EÎø|có³v3/^8¶?Ù×…¹ÒRT׃Öª‡ÐÖö™¬®çn|9ÿhª„1uÚ]ÀUJ¥—èUÄOË ZÜœ Œ9ÏÚ„üeW.Lc>åG²º¢‹)Wÿ//Þ€SLÉ–´^8ûFãfÙн1vppû<ú=%€®ILC£;]èBØý"†äõáR¼üþlY# ñ:l¼û'Æ4GÙ@g8âô^¬u“'«áKà2"K,˜ Î?™éBµÌê_§@»VVÔ³ÅÛcàèíí8{uDD—ŸoÔoP’(–¤½’‰kúy|þšÝg/ràËñrjyà<á1‚ïwµ©¡„Ùt7 >ŒV€¾ïîôÒøÎX€ÔüÌc®G%£±’_^F\çDrÏÃZ,+Іðr´äàIüyIŸÜX8‚ÍùªÐ¥züx¢ìeòÇì>‹8 ò™½0|_Žý¨ÆçÅïÐìYûpû.d–#È‹92:+¦Áõ­|˜y®çóvÖà´í78ù³JàNÄeŽm¶é怲»¸f~Ũ=ð»>Ö«¢ËßÃìüÏñL¶@§Ón“v¦hµ–\‡Ùó´a\ô5ÖkÒ‘sÛÈšŽóLÃåT6&ýºåD;ÜN¡ôðT4‰7 1ɱã•®`ä(¯îσ›z ¯a]zÕ„úÜRcÆøñî^b?þU>Ê£[ÉHZKá[×Ù 4¶b½ö‚ëafƳãLÙ÷J˜+Š5ûUéÖð ×ƒv|,Fѯ¬¼Î?FhÐS/‰€·õ,z-¾9?u®»'a¸FØk¤â/ê©s™á£âàõí ¨N|çžÞ`DuW3ò7q›Ø°;D‡:£H§ó,èóˆÀCQp®D2Öd9Rÿ’ÚI~œH?d~„_pf{V·Í"#\e²¾Áí˃AìïE”> û”D˜d~IøOßìໞ¨w¸±ÉÁñTG·¿p¦Ò‰j]ä]-Ší 4ÌH‰¨ûÎ"Ë–c]ÀYì=° ÝEˆÝËNÍØÄúÝâ£4Å)­­Ä¿}»qaófÎåB^Xª²ó^ƒ[N?”ÃuÖ¬MŒÊ±¦ê3SaS^<=Øê¤|V߆VÀ~çmxZBmïm£)6ü´qég§Ñ¶ãhóÓŠÌLÊ‚¹&N{GŽCS-Þ=bA#ßïÇÆÑ4Uų^ºØÙk@Có YFê:³.¼‡UAjë6£²š]Wÿþ¨â¼ÁF¸™ñ¯ç<Ãl¥Ë0ó‡?®ëU$å9Ó`ËøÎÅ–mŒf—k³³Ò—ªRKƒûð› ËþâF•_=éÎs pW³sIæw„öT{}Ä rоo®‚3b—jÿÀ´%›qyC”,’ñRÉ+ˆÛp·çjþ:vS=a-•Ož{[Ò”t÷&]ƃ§†Y ib£Ç%÷3.B•LsDñ% Ý¥c‹²Auv‰ÊÕ e‡’`TêþzcBïN;¿cѨ]Ž|(zÈÖ†L°æü?Y’—¨æ"Í,>*B¬T†P¼-Öª™R/îqœ¿ÃÔ; å}L]y.Ê,K„5;âÃåóH‹O¬ûúDVU2ìž^X’ŸØóÖj/îûKÙýáT½ýiwÄò?ŠÒö³ˆÄp ³Æí7ŒÍ»ÁxÆ@®ÞÖÚKŽÉj~Ñ ‚“×lÉ{#òzš(óŸfúGNÇôp+Ô5¶A15¥âŽ ‰€V/(Ï ÃOÏá'?÷F@þøzlb9­ “{LiÜ™™Ä\è Œ!JK]àñÑH”|vñákÂÕ,чçKÿžÃÓÄèqÉe´û’8µ igF_p±&'ÑñÍóøŒé°gkcPs€¨;¿†“ó—ÂÂY]´3]Š¿)ƒ³Ž]=I^ P"M¼˜‹vÒWñΧ6(¤’‘ØUõÉ®P0wö,2%^J‘¥eKi¬H÷]ñcNƒ@&ý«Ä¼gtÜÀÿ ¸ÜhL½MqNy,{¬+¸%áOŽÅÇM ÔÈHiòì‡0²Sü0ºZ Àιï pÓ8ß2ÄÔZÇ4ÇT3ÍA“~ÍböäÍÄ>‹%¨:¥•3o´ÚµÇ×'8?—†ÿÐׂqø-…[eö³Nj‹€:PJメŸ*¤È´‘ެ³%á!‰h–9üÎëÄ’‘Ë Í{ l…øÓêT¯\–|?y›³é¢x´4pò¤4áËÍN—Øl5{tþñ~¨nÅáÓ8—ãŽú™ì¢z!rûû=¨˜{› º xd>äŸ\C¾· Ñ šG%¨m9Cs{™†%2ôEHøØ*hY y÷z…Gvø[ß9™Mpbéª-æ4ù0ºò‡!®/ ¶ˆ_‚cVžž‚ëïÓ›’„†X´£—É‘íÁxWxP’ÃL¯8DÜKæÒúÃ2ì„X žÔ~Ã|0”#eaÈÎod»öh€L´н}sØÚ‘jî&‹v¦g³"=R¾•X-ùŽÎ ?ÑW›¯Ì¾|1ì•Ö¥©•$æürJßn­lAéüæYßi*²«Ÿê!ç?-³ôŽ)dI´2ôlØAúÉ3¼­Nº t™¸Y äïŠ2Øù±˜Êå^C¾m~UÌýêÏCaq ¾¨§_;p¿‚&ï¥å÷Té×î:Ü®"£.“˜¥é7¼Yh@ \?3äEN,ºÇnNšE{ßvÂWq¶Zó{±* ³Ö‘#5uà%ÓN=¹ŒÁvOº#ÈÝö9¸ñTî>¶C´€“UX]*ƒ¬ž:”1§³4@BQ‡}p \#Ó ³êQ'$ˆÎŸJ™µJ²¤ãø(³nH–¬¼öR¤¯qÇÓí™{.µì¦e¾DäŽ'ûwÌfV‰’Ã) Ä¿‰Y—"Ìô¦„’}!_YMi>Vïùuü™ G×Ýzˆ™ñ¬ì}æÏQ7ÐŒâB™ÜN—P?ÔYÖ o¥;,ÊIƒÍdxª;Êñ P!C&Û0ê¼ µ\S‘ÕL‘T¼ŸQz¦t"Þ;Ö*áÒ7Ê4d{;3²fÎôT#ñ\.x¤»#û0ÖQ‹(<òŨ§áåTZ8$ãójÙe—t ¸†~9”b_¸-½ß±Ö·ã­Ú·Ì¹pÃo.„^F½æi0ã»a5}Âôfœe/mפ’†ÉÄ!» ¬ë^1)mÐÞútß?d#n†q};òÜC³~¼Ä'ü±$Ñă¼ôÈ^grÂò l5¤m`r†Ý!÷]dxžÀã-i«Í0Ä$;¯X’£ãvìãn%Âèxƒ@Q4Qu›E_æÐe(¸Åxc(ªlÊÇ» :û8ô?ûˆy|—pÕ9^:a,L>µ!ÁæÕÜ íâtOA+[üí"»9½·¶ØÃK~Z¾›o[º ÷Óa÷h:Ë'è…nþ.ZfN\žFöä§á¾|ZiI’íS0Ó(Ωn i|P·qõ8”Nö ”Àºê ˜sguíÚYß)Ža&Žj¯„?®ÓÁÛÏ•øØ…3¿8’µË›XÕ#Öì·ýõDãÚføo~áì«Q½˜ºvîW˂ζùÔ=FæuãàC-XúX‡ð­¥=ÑlöêCxóxL©M£·ó¡¼x‚Õ«]K¥ROÒ/ú²46Ö3`irÛ=š+¾rRÒÏ¥Û{±üºø+PÑñÓÑó¸ü(‡œßɱ{1kÝ'^4cØÉ,Øòx <1ä§±ï}ÈÃØŽCÞ…¹ôDxÎ{.æœFþhal™¨0©R3$·ãQøN&ÁÊ4”â˜ÒC¼Ÿ*H¸¯"€§C•Æ»GãÔc·ºõ.ƒÏF 鲪ýhfÉGάõ¦%²pîÇ4´åÊâ½ÜCtUp#¨‘X(ˆ75NcSo,ù¹œ“úuY¹d¶þU³™ç´‚5„ ` ÒͤÉñ¦Úôàú(|âšýìI±Ê`¶ºïcLfåNø³¬î%¬҃Şøô$m%:¹†æ©‘¹’ àay&ò­Ü\ËÄÆ²±‘x=ëQL„Š8Ø=ú¨ç™“ =ÄO·\Á¥mLÛ._zÁà Xd¦àn^œôƒ§V"û]jÙ«‹t«Y7½TžÏ ¢¿Í¾‚ÌF~¼ÜÂ’4uw> 7/çLm¿È\MÁmO;ÀïüCƪó-ð}K@Ñ•¿¸7ÃÈÙºrÌ{cØSê±ðÓñG§f?»É éýsǨ`©8(] ãŽl]Äܬ̂W©^¸<·ãºéóý¼d- ü™Å˜Fôà‘Oà-üj•ZÁʼn—ÚžH‚íy[©jù<¢ó÷0Ö~p‡žìz²4— nÐb̪j¡`…1*ˆ þ¼º“~›Ò'Ï]!à¬%ýÕ¥J~7ÅqÌo¸Ðw‘ÿ@yÏ"13ž´x‡ ØòxúHWŒ¾–Êf ý³Ôp7¶ÚEНyçáTj!®Ù¦ AŠ‚ðey?'”“‚'ÍüHbÂæÍü›0{ÄIZ$œy¤ƒK‚òáqð_Øîž€)Gn°“Ïá¾÷ÿPvõ„Oæ«m;%É¿ÐÓ\}5ì ¨‚½A¯™G¨Šž{Èý[1à¡(”|˜G´æõùŸºtöÕLÈö7FVªîMñ¡5C%ødÕktÔÁMÙpïÛó.5œœÉŒ¹ìÍq†ã÷I—DŸ‰G ei¢uDн»½v?Y î›`ä‚+i1›ñ»œÍ=VÔ“à¼ÂÛ™Ë.ÂCéXöíÁVøÊË ¾ë(AQrk“*™37’NjÅk9­XüÔ˜ÈâuföëUô"Œ¼&—gO'j·î°«RøÈ1ÂÍøŒù'á|Uw}Ò1ðþ†KEÉîúäÝG;Òp°:vJ‘ƒ{X…is!ÿmŠ»Î,¨#*¶ÿ ;@•ŒK”°Y7Ñ4jŒ½ÿå.ìÿ4ŽöY¯Y­À50Ké9;9ôm}Îþ{x… \;Y묪á@‚úª ‚£ÙAï¿ÚZ¨” Û\¦æÄ9øôo+&ò`þëj ZÊŪç¯Ðgk–ÍYAGúp™P-¾Íû«ŽvVÂd¾Ì4ͯaüD’Àýƨ{x„ÉÛPŠ ¹b$ZO×®êÃîºdæÛŒeSºÓœMúx¿ ;sC»;aÝ1m\}û>†Š`Ý®Sìø}l<ùŒ­j1c œÂ¾DRšL³˜˜„¤Ò™w¸¡Õæ8°m!æõKÇÍUÜ—Ïà{zùBÍñÒ=úe‡õËQ'ëïRƒ=}Ì•„ŒÝûÛà<{aVwÍ”-¬¤d,~»*I½¹É°²°­¥êЀ¯ŸÝ¼Ü†m5*†—óÀ‰úh²~ö2Èœ·ZŒÏd78 `à¾(U}/çî\ÿž):n‡'—ã‘)¹ÈÄûBȳë L‰@Á7|ñÉ„~Ù¸†d õ¤&à(E#_mºRå ¶©å•ÊÐÃAÀæcXVB^ä˜\¼@?‰´q?~ÓÀ«u¢¤¶å4#ëµ÷”£èÅit‹5™öyŒ½§TŽÇópŽpÍÎ|Š3ygÐØÄºR§?.>Œ¯ÛÃl;ô˜¬Å:›ÝA LšŠïß/Ÿ*¿]kpçúx$½Âi†¢/+Ç}¼Ç|é¼·W¡jÚ|Êê8Ó“sO³Éc5ì¶ôt¹bF†ïdâ ‘"Xóã;ô›%°²m1]h%qШ‚&-S8•¸M aJÖ;`ëASâÙü'¯9R&sèÉý·›¹GÐf.,jDuÝX\ºÓmíoÙôй°ùõšªBÎÅÇw+ìèö8!rÙ~”|í¹ÊÆø)ÓãÛ?öŒœ’Á§ŸÃañŽD¬ž*Cnì9ŒIXDËÇrˆùç£ð®¸Š¹ ÙÉnÈëÂêUù؈ó7ÒMÎk韡-̳ñãšÅ0£… wD›_=ƒU¹˜Ñžc¹´…ª¨áÔºGpº:›³.æ˳ÓÉ×Áñ¦÷°ú‡4Á|Èî¶Åg[·s÷‡*àè– üû"› ²¶ bùÙׯbÒÎú¾Ð ³›ãˆyâ/\ÊÓO–可nž¾@Õ÷¶âç{Þ°ú2UGd6µùýîT‹“FþÔº-˜¶.õ¢íõç d'Y1äâ+¡ªÙS´I›O§Ú V­W¹’'/ÿò¯ðÚÍcÔ‰ O.ã»8€^ÓéeçMÖ™×+©ð‚|ñàÜ¾Îæ.!SªÅˆrð ¾ø îª%Í,j[Ö„ê==È;z‰œÆi´šˆÝW£|š¤ì0}¬½í*^ÿ¸¥µx‘÷üÛÎß—Í£¿8ß8ʼN“u9z‹ÅàøkWÆp¹,¹ïq»ÜǰÁˆ—¼M²£Fe÷Å0~zí¤ªë$Ý7+Éžš)Ôj¾ N4—œ#Xh¤Fÿ‰ð°}-áJ¦Üúñ=~ÄZlxãl¼m'h?OB‹ˆ—ŒÖoÎâ™fl)›ä>ûà±á2J^S'ñ© ìö*R2” /߯3W5Té†àA<¤kãÎZ¸Ò®–™=eþvúÓWå:dÍÃl–PÜJu ¦&ðK¯ÏÝ¿;Ë„éæ¸Læ.€t£ÕtŸ*;+Ê›Ê'Í¥C®3ÐËŒ®˜`ø}‡îõÄæò<ü±¸f2nÃ#Y-8øÍV"GæÎ£î{MÐëøQXÌsŒÑð‚—S«5òµÎ„n8–F_*."ë¦nÆ­¥îdŸˆ)Ý)Lœrd·G°^î§0|ÝRy`;Ã³È ãLîãpý<ªSEöˆç@™G+ÔÌ‚ºÌ‚é™Cg–€O@%çÝ5'z]¦ƒ¶™NÅCæt0,•|õkA®Ãefa€1î3 ýˤqVs3Û!>~R|†›“Tè‰/Õ´ãü¢¦zœ•üþwŽË¾/Ndâ¢1\'CÔ•î"›`î¥V²(%RÉG!»>”œ×¤0ÚÔNr{/óH)°"b W& -oðŒôzjë¯LrÚÖ‘´+H‹[$:$ýÂ×­üÔýiY—Ï+FíδvTëxo‚n¼N¿t‚hù¬'Rû/ Eî#rªv5½ºñt"EªQ4tÉ´Žÿ D±ª¬€‚ ›ŸÎÂLfïñC¸.Óæ>L¦Ç–m ·é͆¥„½ÑFC|ôÇ*{âyt³UvÝóÉò‰&Ô¯r é{ÑÙ¼ÿà?ívÕ5;¬¾Çbäʶ̛¯G5uý¥3zYEðºÂ‚ÜÙJÇå/r×iŸa}Îð’÷l(väB¬[2c÷¹–¯çp *Å KC¶²7Åé¢ÐFQ· Õ.¯áß )›[ÅÔ:{OÜþ» k,È´~E&ÿ!ƒ×`ï]e¸¸8¿ŸžIï_•£9ay¤iT³€÷7®ãkG:±WŒ8ŠOÅ¥ê%håžDÌîVƒâ]u¢Xt‘ý”‹ëÚRiM§*DvÑŠ9Ö䞊>lÞeOº‚x@2ö+üÚ ΋“û=ªTÚö2ø»‰žMü”c— þÁPª0Ø.ƒk¿®Að]K223 ê*Ú‚—¼™°wÛ$ý§o&ŸFf >O ö „o³N¢û¿ópKæ0[+F9Ÿͱ—h·91ÇV¿pæÏ”Ç ¹M€úòó’*‘bXVãGÓúpË.\͘R«û¬Ó ˆ ˜äîÖøsj5Xüàà4Mªüj]ðî 9TãS¿hÎX¢V} aè8xô¾…Ç tEÌÎSxÿ2 ËaêeRa™€Ï—¿=§hXÖs ÇöàåO¹°³s37Ä› û;òîýŽ:¶$¯$ZéÉભÝpöÈ6:vB „ŽL%¾“<©OHˆ[eãÁÇ4+[šY¨¹jûrÀuv/x,¢Fv$þǼÎ.cµô+P!_Šþv3D÷UÖÙÃOFá–ßI¡® ݸӂˆhþ…oðth+u¼pã?=6gÜЖÊ*„‘÷ö–t¡F5œ.8FrÓѪâv¸^g峌!/ÍÂ3ÞsMná=Jds„ u¤)M7鿇ÿ%é»ÓLOn{LÔJçàüÌ5Ä>x9ǹ|cñÒ=[Hn3‚¬Îä@© =ïz~\´¥é•÷œ€³nYB‡b¾±óµùÎ:üÔ¤Ž‘ZäÓŒYtÂÇ—I0ô'«¼ÉD3”ÙS›}ŒFzG̽‡Ë7ßÁY÷D!UÆ=£¡[hÒ©]dǾ§8µMœŒÝ0Á)&\¶¾8“Šñ¥£õss)&Kg*y±—`éµ™åŒÏ¢u ˆšÊ^BþÞcgóþ„ÌE9ÄY\¶YGÓ)§Ïpžˆ-¦ÞˆgCGq÷åFjS"N“Ó!kËýè§ëM¸â2¢gm N?/OþzÅŸzÁr?‡ $ÕBÁkü›$ƒµ¯ÎƒžA9kø:–û‰{nžÊeëWì¢úÕ„û!f5Çï˜M oƒŽ[¼Ü¬ ¤áÛSFáè=N¢ }[hNG1ü§³ŽIº…§}蚉.˜²M j§ºKÝ8ÿû|²ëþF:í²µ·Õ¦i¡œéÞ7`LÚŽôhàäšÜe§+g•›¸EV’&-Îð­§`îû¥dw?4SÂòräË-_˜¿ÖŒ¬S¶`Ò;ß1#“|ÅBJL“ÌiîßõøoO5ßúR=Ï”Âë‘%tÓ$Öú7Kœ]>L–öƒS@š8Y9e.Ô'Öt9©yk õbÛˆÈê/ðÇN÷ïgψw£Î«Sh¼¶5Ï-£¯$IZ’:UQ¡gï?æ®~Ôʾø”O í¿ôâMxýh0UÞH‚݈Á· ªUÇ´E®ãÖ>ªàlö¡'’pÆ»ýt|” óØÑ¡Ýd}¹-=µx!–gÐO‡|€g©™´ïY&9N¦üDÿ[šÄ9Zœ†V3…ž—ðej[+´„:‰%½ÔïÊ’RL'÷'4ã•kðcŒ óÂâüŒ:Œ*[ß±…Ïø©Xlú¿ešEÜ`Žýkœÿa“¢†`v£%âyJ†ªß ÅÍþJð>FEkpßzþâ­ÜðG»€õ¾&Œ¯C˜Ö·7Á2³:\ a½7æ‹xøõftæ*’›æfxbk< Aµhv„C_=IÞ[ÚС°1 "äõtuFÂ1‡vä}CU‰÷ܦ`ir\²—õ>݉Æw¹x®ì•ØnH–&A]Ëw䈊ÑŠiäw»÷þ1óì‚_Ñä¦)Ë”9„¡PÑ2dï\fZË@FâjÎt…‹on£L±¶›di8„Íé ¹æ=vV™°†}ŸÙ {>úT’ý¿M‰ƒVÎ>Û³û­Íq‘¢³=­ô'(:~‡¸Ì#4 ØõÏ/À¬#æäeèÅn\ÂÓÎn¼hJÕ/¸Ó¶¼dûRöé*7[>›Þ¯6%GÖÍ!›g_e9ß qØ{+aOφ»•ÚÔ;¹ 1d*~\7™Cã¢ñòÔÅpÑc]4SŸZ×NÞØ™Tzdݱº‘}Â{4ÝYõ—uÌ1ÿõ¬æN!¥›¾C&‰Å¯ÿ2ú‚§¿•ý°u{ÚL¡xs‚sQm>QÄÚ!°¯õ~Þ ï[`ôÌ\¸í"$ê¬e¤=—¿Ótò#9– ó—ŽÀ Ù¿-eAÖ>™ðÍýÇ‘òßA—Û Óû‹V9ÙvXâYµåŽMûajô Z?‡„Šܘoë¤E×¥¬‡ÕJrÃJ ЧRq4Ú{ŸþžEûIà¢ÞËxÀÀ“LޱgùIt¨"|“^Nž¯´¢ñv©Æí ¹Ò¼Ã÷g0×V¢œ³}˜$D2Ö¢×ãÈù1–ÞΘGUyøpùšZªË3ÿÌêÂç¬þ:<ʈÏN¥‚!Ѥo!Íþ›ŒÊEµ$iĈ=×v]›îbMöˆ“'"oÛõÉï¼I¾{Ûšfžy × à›“<á™™ŠÇƒú½§—˜ç·¹ìؑϘ5ž†ºÅÐ;˃vŠ“5u]Nò÷b`áˆ8ØæáÁ4@WƒóÖ¤R}¤¨˜ß6º9Ä“®åc®&–’EÖ‹ˆ­â“ü¡’T9%±{Ž`«vü”=>=ñ´¿/³æÆ×Õa4y‹WõÔ"qMšìßNrw´=ó}!wmz÷5ûŸæùÂä{-­¿‚éB(¶ð:þ‡QÍ]Ê (‘!6[€ÙìÕÓ.£Ü´‡¬ÇÃÈ™-ÁËÇ ñÌ–ÒgH×ÝúǨlu'‰xª^Y“Ïþ ÔÖ# .çX&»£Ñ GܺŒX=¥VÃÖ þŽ÷s.Ð+˜pì(f)–ÓN5ê4]˜|v·ÁJàÚœìßàH×h ·Ý0—½¬×Nš,·®Ü‡ËéöT`É Ö24ú#$1ù ŽÏŠîŒcpˆ«Aö;hÂçlÏ¿+ Aô7'éî)ÄYØ—l= R¼Ô|¶ ÑIJ¡‚mÄ\O0{ lœ·“õ~* “ŒB>37n¿-ìÉÏ«PbáßaFs„tiˆ”<튆=ûžÂZ­U=I Mb9Dµ²ŽÖÉÿá)B– ¹¶o&‰-Š%Ÿ¬ÑemvÏ~%uZð@e-µ­”¦ÞÄŽ&cçó âÒJ'¶êT^¨ˆbÊçÇãßé3ÙÇßµqÊïƒÐõå%»îÝ0lX”Æ®=*NÄlÔÈáOä½¼ˆN¨‰ÃŠÏSðí›ÈüÒ£¿?BdŠiË!ïÛeÐDì!êõ˜ Rõ(üe1.3ê…‡zQ¸¡)—þ™O DÉ‹í<ŒòœíÌšk+ ¹ ¶]ä¡%¾ k§½ub9¸…ÒŸ@£­]ÍðÓ…Õ»àuÌI6ffÈÞÒ…Å£àѼ‘ýW&D.¯ªgJ·ùA«‹‘)ÎÁÝmÉØv¦­n3Ò3zàùþ`LRÄ™¾2tÐ` ¶XíÅQ.÷®Ø-,KÃôÓú¨üý/:¤Àî°†Øùfðp±ý–½ ë‰þ?^èHLâuIZíõ¥¦xRÿîÐàaØuZ¤šè’Ý®bx$0}•#ž 7„Ðóèë':bíÞpZa©ƒ³m÷“\è2ÏÂæ7ñûÇŸ,O¯vÍÉÀki⨙f %SÉÜÕ팄 -ä,\J9dáX®¿ãJ ev½4ç}ÎêÿkŒ+¯žÞ‡»q¹‡Zû6ÿßFÅ-ŠqÛÔ ÜÌ èf5=¨8•¯»Ä$u2×¥¢¤FÞ¹¢H]ùmà‰®0=nùY»¦ ßû‹Ró±ÃíåŸgD€ç6wÜÓ•G{L0‘·O‰¨Ï{ÖT¯WŽ Spåì °pK>w@ æ <“×D=rœyXv”á|ì„iþ§aOÚ ÚÚ¤ Û•o3S‹B…Ës0CCoª1š/SœŽð-…[eÄkë<º®Ô%¯ïˉ´_ÜK‹ñè7¨hÒ¡5Ž1á!äÏ·xò »ÌëyiSÜvš8ã˜%ŠÒhj bFBàïè£Ó<É×èMðŸÆzFu;ÐÒ’!,9™ÓÉ6G#ÈSýÂM¨ÁëÁEøeqö/üH?\M¢ d¢í‘Ck#kàÌ+'æÎ$çÁI¼»|1×zCo’Óg¸ú"?êœzË|cꎭ$’gÑÙKGØý­ì+Ítø–œ|j_ØùüþE@Âð Jcb‹Å¨sˆëxJó#8ÏLghAîAa$Ò._¥ß‡ÑÕh.ðºo¢;Ø÷³Ë/™Epù‰tôVŒ¼k…ËÜ!IQõ‹pHŽ0¢!¿YÛ21ºVO–Ý<>¼÷˜§Òլϭ85É݃ÇbQp­¬U‡‘çQ0Cxíù¡Í曋À¾ÌõÄaS=O‹x`óÓÃ8Q®DUt*8£‚«¡âm9ô%׃‹ñi¨½µóärA xÙ- ]èsD¨x¼€úñUX€‹‚"0zeŸIä°+̬›øœSzá0 §èÑÝÏTȼŒræ²èŒLŒAí,[6€í€K"é(ï.‰wët°pÑ,fítU¶þµ(õ–× "<9ý[Nü§‰&2v¥xîŸ1üjP…”ÜôðÛë=蘱m{¤Í:eAÛº´4qéYçÃìžn¡³ÏàŽf}z)¿LbáØÉ£èùÁ+ã7ÀqQg† ¦áïÙ`<8…µ®³½üY(®\I+›aõO5Îâj#úïàZzHâ v9Ïg4x±UKpÉz[aàϼy“ÌÙW â¢dnQcwß®|øçïsŒ`é‰VˆW†7ÙQ™o#xÏ÷˜:ÈÑ/Eí0Þdo» ŒÛ:’9`äð^L? ü6B´lBœ^‹ìé%ZxO†ýk<ƒ T*Ç%_2`ð´1üviŠÑ=ºJqÇá¶ø8øy@:™2ñy[U ¥7Ðë¡&y.<ûËxhæ*zX¶ƒƒCa}ÚŽ¥52ùs±Ûá Û,®ÄYÜ·×@Wî+ênÞóA4+Ãñ©myLÖ5„3b ºåô!áK«’Û͆ÌÄ7酪ï+šàfc2Îõwa/®EÏ*~ø^x‹Õ΃}Ðú;™u3^ Ê‚Udù÷lèÊÿô”š-CbWžØ}b´e&åX, Gù…ÿ ‘lçì^š©%I›"˜¼YÛð€/ñ»Ç>κÏÞš¢B§à…ú± Ê;üj9â´ I`3× ,I:29kAPQŽÌ)N&=Geñ†‡˜Ú†°Ûƒ¬aVê άœrr4¶üÎs bùœ'ýavÏ3bâö†P½892Eé::ééÂC‹nä¹X€3ÊÐ FnŒrËSVï´4©Gèe†1OÛšS|ÉTXHs¬ž²§C²È¿´ Ún( žw ˜?o©øZ=|œò”yµOô¡ù1†k`‘ Ö Ò-÷éÜÔnLˆqà´:|Äêõ3ÙÝÅñðV}'*‹·`ÔL_lÛÄÁ¥Ÿ¹¥.à.ÇÅù›"1Ëi|ཋÁXr%n}5äÈG1p¸;–ŠàæÓí\ó•Øöç(õF{ÓÖ°ûÞZâÅTº*œ…﫬¡xb7î‹ü gD-À[ωØg6³e_ÛñEµg/ãÞËaÙx$æ,„Ю­äߘ#oèΨçÝçv=‹³*t 6tS|7»‚Ï’z™ò‘/2¶pÅŒ—ˆ¾œ[cžCÙÞsìå]4ÃŤª6ãÜ«©èY*O+Œ2z8™Ö`Íþ…ÇÙä“×VcÑ"ºÀÍŠ ^1“ô—–Ž;=þç{zð:B« ;ôÈ„t‹ìÔLc†NŠÓCßçŸk‡À¿)Žš-{ç´WÍûzYÖ÷ûE2{s<.»9›ÞŸI4ÃD謓Ÿ æp(ü´o&Ñ&øla"É3GÎÚ‡hã-û1d¡ßLgAËš>˜‘‘†½bâ´ÔÍO<ß ÌžR¨Ø¯"ÚìÇ>ÌÏfœ›ý;ùŽÁÒ¥¿ñ“º6Ûý$‘ñ¯be|íqPê>c¡°ý·#‡Z0,¨–¾º‡{ÝéøóÃìÈù£ ¸Y 6íx‰ï\§*@xEa•Ñqv¤žŸLk< »¯HÀÖî²®÷œ’mŒvÐcnƒ+w1_¥ ‰R$‹ê—7à»Â78¸ ǯ1Û²ìéïù/ÁÎÉ„ìØz€,®DúçÄá¿Ø2T€é%Áà¯gFJÖì'*…äsÐY|ñ/¿–šâ·Ð`7Ðê(WûË‚#xC[²E2$ý¡(*²üdˆß–xòè’ú+áÀg:•é{ȈGwã`Í$vº…IÇm¡¡tþhÿÁî¯8}æ3q‘¾9Äh“k‹1ùb$T…ä°ÜÈn¶AÉŽêƒKØ!n‡;uO1§3AI¼…Õ:ªC¬÷þd̆õ;>³×¥Bêåð´å#3GJaÇæBÖ‰S![nÃæS:²ÊŠqËT¿ÂFÐë‘;7Øï¥íŒñ翜º°ÿí}†+:—ÜM/eö„ €ïÜPøO÷üFA” ´‡ÔÆ“¬ç~íE²º­i˜ž,´æ·`Õj^øôÁŠÎŒ‹‡Ü2$»£ æ¼xÇl >Æó°°Uo,žM_6an{sЫ“‰TÔà­µ’pÓ P6À¬sq==&–D¸J’Žîß3ôÎ@âÃ8LuQ¤µãð§½q\¾ŒÄ6ït· >wX%2Lî1¿î,î,²ë{#/ö µ Í0@fY÷| ¿—ÿboÃoÍM —ÿ­ç•§rTQ³–YöA§ðÄ““õeÄ¿•ÜmMb¼â0éçKN^ê8ܯ:/´2ª?~0¶Ïspùv~29†b?f0 w—û¾gñ¬²"Xv)€'ÄÊÄ’ ôŒ>O‚/AÒQ2¬`@Ä‚iÉ«Vü6¶‹®>›Ò¤1K³c#ň©zžÞûg'¼àª±¦š›DÈð€$]onEÍë0õˆ9üýü×ãÙ%û ¤†°¡Ç¢w4·×·–†ûLe'm˜&},!Füî&gŸ¯&~Ó–¡ÙKW\k^ˆKÁ™0CÒ·Z˜»¼G„ìÝü•Yg-‹rצÒyWýþëՌ߼fÑ 'ÔXݽïq½Ñ6çÎ ŒyêD/Ý1#þ®ê$ùs¦J÷L/9ÃÌùûú7úç9Ù ÜׇK´°› •7íÈ6ƒ;¸@ê/¤Ùì¦ý›òØàÏÙ´ƒÉÜè‰Ë˜ýB•¼Ò?¿&ÔÑäd;¬ 9]îôíuæ}‹í˜=yæw1JšçƒKÐ+6ñù8;U3>TM#ÂmPZÛ«6:O±&3_ÎbdéÃÈê:YkYž×X·G˜~^Sƒ2s³ðÓ³oLÀÆ3¬ë%E: æƒFkx9—IƒábWZq(“¸”Qömeyþ4ùCÕÚB¦5ú=ÓÂH£ú<Ô˜ì®ö%\§ÒYkê9ô•Â@73’~Z‡d,= ë¼ »å üµ¹ˆEyì"«P^r œ ‚m•1¬8ÆÇî…mtéíoNáK2µTÚü>n䱡Aq}Ð5çô‡&Á±ˆ8|<ÉuŒOøÐ=ßš`—“ ¬Í~D‰/w Û¡ÎCáûí ¥'ŒÉŸ^w8·Š Ê‹.0M ï`Uš'Í3&LSÖÕ–[i¦ë±ö‚ß©Erº¿VìFv„ýðMÖíæ¡ïëÂ`LVªÞe5·ï!ù†™Ÿµ·‘3XAÛš}‰$¿)Ñ»ñŸžšù›íIOøÆRþðSäIVv/W#+å-ÉÉ‘n(ÎV"’­åäNW4á“2" u9Tá u‡u­ZTÒÇ’lÙYE £Éµö·øõÏkŒgriœD9šìTY sæ¾þø{£ŽöÏ¥®ÝuôÎrÒ«aýæZ¿_î[? £–W‘Qé ä§ŒÝŲƒ‹é¥7`éto"šìÆtª)‘_d;îùÇ‚1Ÿ+šÝf·KSFUœªÞËfnû®#Ü'÷pßú2X“¦I¥®g cú¢ÞŽxܘ‡*b„ð|`|7+à¶*d®«>}~¨<èbª&¿5)^ ŠÚäé³:îÐ |¨Æ^k| šÅ¿Ø„ÑNÆHädp.v‚BÒJö‚Œ2ûU¥”^mL ûõhV«íl÷#yvú"U¼Ò𗈑f‡ÃjØîX}rx7¬;ÎÕzÂ5’Q"Á?à§Í…Ô^q>‘¤=jáô˜é}U?'₺ÖiÞaVN½“ý¯³‚ñi:W?Û2 ù‹ñÊöcì ¯1k7¢…Ù\¯0ë5RÉ-Ý^vþ±Cxv×ökç8Q$M…fpkè~|&M'Çàíks:0]÷èÈ‘ïÊdÊÞ|è’"öŒ<Œ6õ0®¯aÍ,c⛵–ý0k'm((eCbç¼¾É g®í“íÛ¹(›Ä¡…÷` ÷\È¿/ÒRðüÀ¶ê5l¶l #µ$.6ì$ ´§½Ð¥úð߉Cýñd.ç«ý´êèßJ×7ßćQ’˜¥R ª|?1¡¾žm™AODÏÄñJ!üq –‡‘Í¿Øõ_fì h"ϯacícfûæ?p-ä§>JS¾Ýp®b„ àQ'¦Ñýp¨SŠH”1g ”0¾]‰¬šŽwÃ^1~﨣Ö&FMϬnÆ ±Ö'ð>žÎ>‡¾„~fêü]ï Iç·Ð’Šî¼u}¨ž‚f>l_Â",(&=Wد+ôèV5!zý§ Vnʦ¾Jû±ìÚúXýsf’ ªÏFm¡Hü£™ƒßçéñÀnP‘–{j©C· Œ¡ð¾kh°‡‡TWÅÕŸžÂï3‹©È‚™˜´Q†ˆ|ºÉ,¹ÙSXþ£ÕZ8Ìk²é+ :¾ðP  1¶¾€÷ëÝa×6»TƒBÇæÖ´'̱Èúö¼(>\#EGö~g;–¦àÌ¿çðG_/lú“³ÓÆp­Ž ¹¡åO:*{¨bt;éû+I›U”&ÎÛýHœ§I˜?ü+ ø[þ £ò"³‹ªPûä“jöRøJA†SB4Mw³œ÷ UÕ…;„ °s+k([›ur°h[¯“j 0§°`º9üóé~ÔÙŸ ñK,iƒÃmH÷£ VÕ¢k03$'¶)¢_´ qFrj:f)b«Ž@^ˆ ¹P³–¬ žJÿë!ý¾$/¸g0w•7rÿK¡,—º£Pà%fA´òN¿Ãù—±‘à«…«a@Mýuˆ„Ñ~¼+øÛÊ›™3l7Œ.ïC#³Ó·‘ —¨ôaä¸m}úûúä;”Œz†/º@ýÎw,~èIíÇwãÅoT<ù˜ýQ͘§HæŸÑ§R|9ä+]ÌnºÍ./r`]ÿü 2˦ ÚoÑõf œ½äeñknÖï­ÔGQ–±ÍÈ íaÍœƒ7l‘èG5‹‡§GÐw^‹9]]ï™Û÷Wм¡,¼®ÉìëšBÕ“˜¨sžØTf… Ú˜¶ï¹°âêb²¾þû.2€ì¡‡Â™Ë5$}J»×Ü‘Zt»Ð¬C)dô@$U¹£‚¯ŽÅÑxúB+>_]@û©Z;V“‰H zOB…žý¨IŸ,Lfò[ H­c<È$­u4¾r§àÙŸ» b‘¦W\€÷ÅO™¾å"¤ð±8l?ÇÊDäP;Oy¸ YŽËRžÃ–t«}ºMYVšÒ:‰£9!ûà׭ɘ»¥ub>t|ÈII4YX%„_ð‹Õ.8³³ {ŽÿÃÈi`R> ™çãc˜ùåÜžýŒ©)mekJ ¦'a¿™:ÒŸUàÅ*‘Èie°z† µ“=‹¥Ûµá`Q(–ّؽátµ0‰Ù«O\¢žÀÌî,ò½ra³Ñ¤\¼( o_ÂL¼ý‰q |äe¢9n=H}oï§& 3¨^ü ŽÍ\qÙ #N“B5-ü¶.®ÌK¢ŸçÁÁ–­4:u„ÓŒpïƒbh;-MnÊï¡ÌõrˆýºU—Xb¬×^ò~Š>¸8\…ü[°L#†(}[ Ó¶7´ø™ymÄi=ÇšNY_3öž¡R¡&TºË†x•»Òæ^{#mÍ;©ïâ¦H¢”.—¸ƒ{5ÄÈPólò᛽Ùt7ÙS§cï¡áãcèÛJSæ¨9Ï_h~‡.É!•ó“ÜΟÙÐDƒ¼³séê- Á>^•ºiñ“oçDéám\:?8™L›#ˆ÷é’Î\ÿIŸ?±]ÒQsë¸ð} |©eN¨¤Æé”¯þ8œ‘¢ö£·9«#™§r$èܰ Š#•ã)ôëÒÉ"Æß§Ú»p¦É ´•ÙÂç•xMê(Ó¾¼¾,ŽfÊ®¦ƒï¿&x»A‚[UøÒÕ9Fý7ÙÜØNpøhB7ÈFý¿ÇóÁyƒðòÄØrÝü\«„·›’எ99/D½Â‚H‚ì<3MúÌh‚ ™ë`/¥ÖÛ»¹ƒ6ðÚt%ù©ËЋ³²™³÷ž2›²¨´R‚*¤™™Ï|ܹûô·ÆÂã8: êT/€ÊC&¥ŽS®ˆP¡+ÌŽr7âàÛßÏšOG+Ü+1•îÜù†õj9A~Þµ~·à³RƒD±3iá4Qò7ÐŒ$O³Á†¤]ðËN‡n^¡ByV4`üg!Zb— Ex”ý=k.-¹=‰¦ê㕟L%ÿ8îž]×ð‘#@ú®úA˜ú-êä¿\Ö¿{ÁZôØÂ#LÂ鳨®Cl²$—3‰ŠJŽ÷Á5dIa)ÔýÖ$c–ÙD¼#…‘MYHü 7¢Õ&ÑG”ɽ€§  ’†LÜyŽ2cD^lb,”I—‚Y;{:s-/>fóиYøGá${ãc(ºoï¿}~ÄÝgnP£Z±ö¨óÚ“.j,`|fs]@áÆ˜SÁü8YÇ.ù=ùÿN¯aŽÊЭRÔáê#0÷ËìIšMóuôPN¼ž5þ®Dé ²ãô4í‡?ï¢Û—:´ûøI2Ș Çö±¦äq6ÕsKCùHí«dx·^9éFù¿ó“CÐÁ{ aM#˜Õ`··x4ö8»aà;éëmGô91‘•|DÐO“¬]¾…Hlz nÿ¶CàWoÎëES¨çù"\ìcHÔD©ï]7hœ&Agyž?òô¨ÍNT^. /õÃéƒçÐaN#H_›OBí¨º.Ý#*D©±>)wPœÚ³•nþÝ îF×ñEí(rÊ3Ùça†ø9d!]öé-Ú}“!nG›iì\-ܹëÈ4ìd•¯Lâ]’ký®=»Cõ³¿ï¬! Ÿº@_-™DyäZ7Y|æ,ÄÒÏ`rð8Ô+z’=M1$£œ¥:7࿹ދçQ±yS©°¯mÙõO7qö‘*úx % þl‚ssÚR¿ ÃåBÉ÷)ô¤¥,\æÎÆ õ4êÍÛÕp¯*ÄŽ˜JÞ³:µ L§=×çüá¾È‘¤BÓ*YÏêËÖ!!Õ?`µÏ8:× ŒÌ¥/3žA‘Ú&’:ï§²i#=¾z9M²ã%gG=ÈÊÙZdú…ÛÀ¹"@ä»íAÃDŸÞ¬›K=Ž'±ŠÃ¤²ç ¤‘ý—µaÆæj¸–ÌK ÎÌA‰MMTñn3ãYaCõ2°o–±˜fA>ëÅRMË%¤åY‰d‡7©á®u+;%S¯Fcv÷ù`.[ÇQ~sï‡#ÿèf"•™S} èæÏk(3÷7?nÀ˜òˆÁ‹,sb·»‘T{‚NŠäÝ  ycrÈ™ÜKkg4~ªMÞ}Ša§ÿ:Šó¿¿GÏáh©mE?gÏ'A+2(]3…:<™Î|žþèQ>úößðF ²eæ~"< G~:×Qéè$6ÜNŽH)çà;À{H‡N´JQÛ©¯p‡r.4nìï?¹ Ì(’ã`ØèÛÍ]M¤˜¡Ÿ_Àó³æK—.‹ÁG.‚¤¸YŸÞžnL—oÆOC0îætZãlI?ìLç(å‘ûÅ©öüÕÄÎq açþ‚ÿzH× ;®*QK¹™Ñ%‹+D6aÉ?òæ OÚӾͥôNv!ãÆÿ‰é_@:g$EyÕ0k&‚õ -ªbrãþyCJ Yõ8†Õš‰véŽÀ'î‰9'cáä•x)h!3¥ò)Ü]Àѵ•Ç??¥©ˆ­8™/µ˜ähÃ3ã9ì’ÑeDýØWNÒê&Xi6ìhºÎ¦iÝÁ¯Ÿ7‘ÿ´Æ.ÏçARÈ-"ô(~W1…î{P5Sƒ>¯z o‡W‘nk}òU*fIã/yQ²n~3 NÀ±ÓxÐj>¶ßS!‰j‚ÔX™—ê -§n“ü6=| ˜™ƒž6Ô“ï È$Ÿ†ÞsZtùô7Û“RU.ŽíyÃ5Û_ÏŽ¥Bç¹ Zµ!ýÝvÎU/vyðuQ7޵)Óáß\^ò¹ööëH°ù¬&¹\ËœN&m›ÒñÌ3nP±8ÍØqeõvÑǵét[À,b}z~­þÆY°1ï6: ÐÒ5åô?-u›ƒ rbþÐXw†\RP²n-[M%VŠ _à“ûÇ}¢»ƒðüËiä'ƒ÷#©£Fý´;6ÇÖݼC„vÑ#ÈCbZÓG¾¶4 É>«_¬‡ê-Þ˜DÎßD¦3§8~Gi£ÇZâÿG v$0rýÆô%'ÜôvšmËìã!:?pÒ_VAv-,A÷Õ ÀgÖ4|’äËçH2ZöN;ø(qxJÃÜ¡·B^C뉛xí•«ëX û•ž‚ýã³ïªšá½ÅIby.hY Ñ(Ç^÷ðà7™œ‡õn­Gém`³Æ æKþ7sˆ‡öFògág¦(Tî•¡(Íó”¸üûkSQ r&ûú¹ñÑÒà|’ s²5éíoçPè‡ɼdMn|,cj=,ù‰fé_̺‘KZ}dFfð“WZô,Y ‡ì…÷åÏéél3–W@Áò—4ëºáYûJºÂi‰.?Nä;‡mvÔKï:ŒóÓº,oFÞ"Í-f@ÂG5lû*GëWNÇÓë`…}9V.”§G ØŽ Mºûò&à‹yŒ¿ÎI°ÅB»¸ñM›8é«(ö§¾`;¼žôƒA†Û? «ÿßj{qœg3ý ÈîØõÔ,E™ÞógtÆÖ@wV+îÏ®ÅÍEãp¦ÿ;÷@AÏ[v JÞ1cb‹µqgd=h4kÞ1lûéã}m6{è‚Ív fZ…:ìq”$Uqø{ÓvŒßœ+=Uñ—èz¨<% ÀÎ)ʧ‚AÛð·Î#H»´£œ¡õr«hÀhãõö³XéÈÈ?€]-68—Æ»j±©´.ج¤?Bé=æÙ¶ŸcF‰Ío8™D"]©ÄîÓðûÖ¶+«æÕBÁ¶cÄösyùõ+¬œuÝW¾AÅÌ~–· ÖºD’‰R}V¢r;™ñ/wŸ†¹ª5ÐT:µT›àÀl Z&– ëN­*ßcWÌ ¨ý.O©‘œQ@¤=£Gñ<+.y#ì¬)oI 1™˜Ç^Õ ÅÚ'V¯l Kýž±êiËÉÂ¥S¸-½¼°®¢LX³µ f6ûIÂ91®ñ´Ôo SµC™ ÞÀ™Ëÿâ)ÇEdCý?ÜôŸÚœo`ø[Ÿ€ù ám\ÆvY¸Ú…2²Ý x?õ£|üö¸ëCeΨö–‚Áè¥hkBj[0m`ì/¨`«cÞÌtf¥öv8 –Ìp8÷‹œ ¹.ƒ";.rf q[Ò&߉µ3¤:bFäaT#nrÂÏß$è×Á£Œw'6íŠFãKåŒbé<°+n2mÏBàZóÌÚĽ’¥¬aã"öØEI 1·&‡no$Ý] È}²•£]e¯3‘ñvš…³6Ódë,¤Ç!õ±sï×4ÖËJ²²8lã½…èuÌ v,â! »®³?~gà†'pºÌ\Šrdßñ"§Â&q²ž_‚2èÈm%ÊÑ©›-–4§<ú®ŠyåFxí*M·_ìg^Æ^…§ÁÎ4ÎH‚&nPg)ÿÆúán™±šüòÖ%‘ÿÖ`Ýþyðù¶:9ý¾ ý±º—^Ã'Þ©øIÕžœb‚yÈ-3Ⱥóð Ô^CîÕIÿî AÀ¦ ø+°Èøna½ëVBIÙöOy(ˆÔ\˜²œx¶ºþÛÖYÅœ´7§eãÌݹ¯ð´ýLòþÀQ<¯F™wlj¨L™wtÇgÿÍ'tÍú•¸ÕM•Œ\N%A³V2Ìègîz î …(Ê÷>v•h bÚŽˆG£Ã0CM’j ]¦㟘`•Y$—ýá(ÇŸdÄWÒ¨ŒbüJÒç^¶4/l7díta+GâçGOéùÓ_˜íK^CœA8¾T þŠ—ˆµÔyròx49±ù$VÜ[ËžöÄP³d¿X"t‹Þòãdíø~öؾøÒ>‚“Ù¹)PÝŸJy×!c'p«ž÷„uÌ&…ÿt©„øºç] išözÐýŠ /Ff—‹–#ÏëZX!0Ï )G,ŰÐEOîMfÛo¼<6 ›¿Þ&ý?Þrï–Á k=!€óçO¾Ï~¥6П9 «.æAŽväí§2^R_²Ž\8t›zýS¡WòQ1C-š¿cb¯³¡UJäÖ4Mâ>aEŽtïÁñ{ë˜3üÁÌTÁty¨œÝâ6Ôzò_|ff0¢žËñMAÕ!Ä)1ZèlÑ™ž¼ph¾#~]ùfÙÚ©6ij¿K‰.Û+M^ê‡7|ðÁ!îðhc+«Lëu]°+á7s˜ð¶Ë™éð$´ä×a¬®†f;H柔GII#2ŠqDîN>rC 87§èì%ži<¸âä<º¼H…Ô›’’¦éDîÏOÌ\*@æ½Bn§U3$û¾ž‚ø™‚5´kH÷Z¾Áž úäË‘#0‰Ö¹-&œ5™žïE—LL#¢×¶pÕ—ŸžDþ&q|8Û–œ«(Å×s–‡/êLÜ¢q6x ?µ<ý «"}꫌ŸÁׇ!ùã+7Žƒ‹™í“qìë o cê!Ð9(Cž±€1*„Æý€U644ç“WøOºV€ð±tZ¸\ˆl8³.Ÿ`8˜*pê<äéVy_úûM;ùÕŠêÝÀ—¥²pì¶Í-¼‹j¾%°+M®iSî©z9¸ko ʇ™ñAØêZ†Ûn¥à:×&ÆÀ²yçdÆy@I1 Ž‹õpG§ë35v°Cd?<šgêº/±ünZœÀ¸¶Vxõ1¯oËÎ>1Òe*C‡á+[]¼P‰ŽÆÔgædõ¢0²®u”]øXœ—¯¾= ¦5nº ôb+Q‚)dÙÃcÜ舅û'‹‰ðÿÉÌQù øhˆôI&?+ … 2ÎÈô\Eõ¢™lž'ÞgYÅ5®CÒ¸ú÷—34çTE3§¹vGs [ùÌšnïoEã|-à]5™¦úÙÆú{¡Î·Wh=Cö4†²{m–ÿ¾£·ïx×_!qéça¼$šÌ=² «rxhnÒiV¡MŠdÑçž2b­m¤.£kEvü\AÞoé›;—X©.#zÄ2„|x­H/ÊA×›{ ö»ŒÂyÒ¡u€è®”&½î4¼Š ›K¦ƒóhëפ}–ÃpfÎ7ܧòWË®RÅWG¹‘‰Ã8Ë´‹í8€OÿU4Ö¡4—Ÿ4¼žγXÓ-Ìzµƒ`}"IR»Åe˜u>®/gÏúüåc‚'Ž5ùÃç°&ËŸUV“£f«1©l1Î þoº”»€Q6•¥Ìb¸}A ý~ÃmfŽåb©3 `ƒ š œÆcwGjDÉжM ÕDyÌ.äe@™©gŒY+×¢vIY(”d?Uß§¹šÌ²C¹¨)ûûyQäÔ[¸ábÏîˆÀ’­o¡ÿ£:•b–ýõP6-ðeÎn8¨:ºÿÖV2ëK¸é‚B g]cµµÌmvb gíø‘Nr ™]_9LîÒå8¿^n=: =}³¤#úŸ ÿÍh´jœYrg/ÿß±yê%@\ .àÈ@6N›k?Ãä‘‹ k4Œ¼ƒ¡ØÛ/Ll…d°J;‰YæŸ òéæyË¡¤ÞïkBÙ%ªØ~¤M%öCþê7ÆÂ“Ãþ¤T%K¯ôF‘\/mÚT3 Ÿ¬ ©LÝ|ëo„G t°Œk†¦w4h4¯9Û_5 ‚ËeI›ÁÄõ­pìòRþÚ1 6 0üÙdzT›nº;¿ÈiÁ¹e¶<}O¹\5H¨’`7ú¿þõ`ŸºZ|—BÉ×ðzæ ¶2i=¿UéÃþÂ1ä].ȤVÙÕ—Myˆ™6Aôá ;w“1ù6ï ¸$²lÂâ$ø‘LWï©‚+ÃÊLþè æòzCà‹ØÁbÛ~Œ­4§÷ZÞà MûëògüD¦‘ýQI8Íe‚+µ+ ÖNN€Ð;Ç=˜GòSçEâ¸X^‹†y ÒÏß'˜AÇM}«¶KPo//âe‘a/SP`ÒA¼*ó”Ð1%YqsIÿgè#œ3yd6×ÛVŠ0­<‹È4¹lx9±‰äÁGœVhI„ÆZØ‘ÊTtcN(®¤ ž€Ã#+IÎí(ZÝÑ]I´lBªì¦R‡$]²€Ë "áÓ/#¸àº–h827]§ —¡šÖWæêŒNÎØ¶Î³@€Ìƽ¿a÷¯¹dIÖ3’ ê¥'°nà6ZÉO§ßÖ…†’­tßeLk$]–Ò¾åS0Rb&ósB™Ôº Âw-Íè™BÌ£ï6F‚àt–q(ñ$[˜QéU‘hö® ¿v#Ý÷YI^úL¢œ©¹0çßÅCy„4åi²b¯ìz O¿¥°|Gó¸WŠm}EtÉŠ%†ÄUò'\lÜG¾/Y× €*êFã“O ;góZ`¥úM¼eþ”ØàEìªÔ±†§¥>Ý-t–{ü‹7|šTGb‘KU6'ÂÉM}hðYŽ-åK×\ˆÆšï L¢õu9eÌo¬{}ÕiÛíiD¿7`‚· UÕ—¢izä²ü.2³V†D®ZDB½áõ%]¼ã}˜jub~ËÒvíÅEKuÀSä÷l×"Eõ‹ªþjügvÅm¸—,H<ÀñÊq‚™Û†ØÿÖ‡¾(®NRT˜”×Öã‚ÆlÿþbÛ¹¹˜@µ-´¿FNZþÁÿ®ýŶ}õ”™„—º4óL´Oc*…£8Z)0)b-Fh>n;à —nèT¸©íóoàò÷áÖ9º^u¶àtøÊOöÖ) €Ï.zö(Bøn'¢Aÿú×6\º!ÛĢ߇qmÍiƹƒe´ËA®ì:¥ó;ÕpÂv'[¶²«ý/båñnÖÏ;xÅçGÊqòòİkíÀ™KÃò`R­º Ϥç ¾6$Ó6O%1? hæíQÌWLÀ¸§Ž(sH žÐÄÉ;,0,.&Dq#ÏÔ°ÊI¦ô‰w-ÎÝ” …6rìâYÓa|ÑMN_ïSFèx*²kWã¦ûJLÛ2)"fÒÉí)äÃi2RFËE‰awhms‹ÖqFq•0)6— õéúx)·˜-<…Û¥ðƒ¿99Ybˆ–4—šoàv­ˆ«ÏÄbÉuPùK†ï¶èf†Ú:ó³ýÆ\ƒ‹8Ïà&1²TqïÈMùÈc‰çWðYB®ý\Î9à3=+ÇLhì_MãÙ›Á 7mç?f¼öÅ“%…Êxüüóü‹#hbvË‹âb×"T /‡³«à¬aZÍ|¶Çž³ªÊAt÷*îÞ3@tS"óÊEl*ªÙð²QH8-HÆû »0ÈXPsÅźDÌ17·Œ0:n@?MÖ îÖ¡À-öË3 r?ø9“Xp|É6cø]´ "©î«KÜIkÐV§j2ïƒXÈߨnþÃünI†ÈS¨eU0™w¶™Vš‹ž¯êØQ!òeLŒt[¸aQÚdX~j#ž½J‡‡pºì ü¼_VßÄ«²ºx,¾²ËõqÍŸH|ýk3{5<û¢@}Õ™PM.n¼°Z4Ïáô·qøY6¤õâz«ëÌŸUPûy 7Æd£«"JBt{9¶eÛ D߈m¨/„~ž+ìã­Lùó$FêÒ¹ú¬µà(ŒIuPôy¥'þØÊ=Œfjs†YOó0\e+H—xõ@ß…!FêâæõO[fÏÔvœŸ¿Šzˆía„ÙJpÙ¢ ÉÜ•ô¬\ÓP¯ûçžggñf‚èá›ÌA]øU8ÂhNÇ„?ú¼Èn,´¡¾ Gð(+ÀVÎ…­¾*D¬ðJ]šÎùp àJÙ0Ÿ_Î±à†ƒâ^;Ró}Œ Ÿ…Ÿ<ç!ku›s ‚?Dç?|ac §!É[žñÒñ§aÕƒTˆ*ÞD¶™–q2‰ýo1ô+(‡4I#8Âá<{è×ÝÎÎnqÓëËñTM3¤z åep´™d&_gµ‚Þ±nû Aþ–•ŒøÌ$°òï„ΜL|7ñÆä›YĪûÀ‚µWØÕ›¨Ø ÝËcý'd çð‚ÒãéädÈfPɆmÜéóðïØdUõÔÂCs÷A¢Ù|©uveaÇ+˜1Y‰pŸ@g·ñïøÃ®ÓúÃTûPv¢^åÎOa®í8‡¤ÉÖ-°:x6G_s/f¼þÈŒe¯Ä3Wº06,“Ñ“µ·]ÌL#y†B´«qׯ~æðÑ=—vÀ‘T~ôØ€ª?§Ã–Ÿ rSXçR\¦ÄO¢±ò®ùoç€ýïÙá‹%8hÝŽßWS}ÍŸõ—înãè:Ù’í¹¹7¥:Î`²¢0R€]ûØ'™FtÅ7†D¥ ¤0õΙqîµÓé’cÒ(åÁÞx‡ÒüMèbKV¼;Šs,/Aä«î''­³†lMQ2+°V;{£™ÇQâ£gÚ;MœÒ+@•â¹l­í{ö…Öv®yh5ì.– >Œ‘]Ç`ßÞ¸ÎéüÃäÏ?Ž¢BWØ]e h»%ªž]˼ Lc¤´C阒Y¸džfâÓo@ø£&µÐª,X®³—Rç%5:Ù so-|úÚÂæ?U€;µ«‰7Ÿ*»âœ&Une’g\…aAâÄ™ ¢Ë#`×”a.¸Þ=¿ ÊÒ.P?~_“&¨&—=rd#¹èic'¼³•yÛ¶øh‹Y\¸P}{<ˆØŸ‡X²˜‡v®U†;§ðšà.˜ƒçWU1ïê–ážà6ŽYƒP½«!†{‡A‡¯½"‰M[¼ îÁë¼þ ýíÞfoÕ¡"é,+áÖÈÔlä§¢;ïâ|V¾/—†[d°O;a÷ß<õpë 2uá|ÐÎ.Ý_ŠÍòLñ楳µòQ¤Ö¶)°éÜsÖÑü$ïÓµm:tµ5Å[Éÿ×û%ÎyÊ‘åJræÑ$BËhå« PÇUƒ/k qô~œt/‹$ÁÀlFWæ;w¶ó=Ÿ…MõfŽ:¤¬I/w&oç-;W‘9Ž7¬uiDH ú=c².VØZ”&C‡Q~þ(>çóDißO'T" z5ð{îfÎôËãhµ‘yÁ¼Ò’¤áp2ò}?Bö>]H%­‚)Å+ÉW¹]¸³!©æÔ¾lVËÉPõ×—¹ûzУó;5“¸ä· ÕzìE~É˜àŽˆSìÌÝ*¸¾M }‡Þv5<_xÍ`ÂÀ™yÑØvê)ÔOÓ#6¯ &³Éó*=ÜVŽv5sèG*Fß¶NÂÏFÙ`{Äœx?|vµk)¯c™±i*GŸ§ó”osfŸ$…4`ô­ø/¢æJ«h¡~YRÚM޼¢­Ì·¦q6Ã<€ÄÎ]Ž;̶ëÎtØžþ/CG$)8„3+—ÂìlÈjFV) €ùÉ”z1ú¹­> {å3œ¾ÃŽŒÕÍ4KNÞ6–¤™ñïÀÀÞ5µRAÖõ†æN@Ë8}™‹?õ͘6ð†å ÎƒàŠ¹L@ìkÔNNcÔm{˜€ìlöTK4[ðª’9S¦IVÛšƒÐø7È¿Ç|BP=<VtqñëÛ ò÷ØzßÒW˜acAëöKÉQcòÙø JjßÀ7‚K£;™l>íA‹/®#U®¼Äxû5˜QÌK#w®ÕÛ{ìT+Ö][…:ô$pf§ÎO˜•þlì5 yþ¹¶j6U0Äa2]Ppû·ZséÓcÓ ¤uô5éßcÁï÷`•ærNF&“_ÎóÈâñxæ¿~hÇ›Éð5 Öÿ1¢ýü;!-oßDËjêü³”„]™¥©{™¿ÇRÍb7Tæ%×v>fWmH$þÿùê©ð¾°æÍ‰§ùcytF/ÃrÅèëA÷Í›fyäó ô]¨­¯™;ï-ûf‘5µ›”@oAÇ–¯˜Ú°½íö—ÞÓk .ÿo}胗 p÷Á)¤žÛŽO×ÙÓóÂÔ%©ý‘l°Özˆº$ž|>i[Ó¸žíªg”Õà̺IÄ.R‚ɲt‡©×ªÀày&Øž”f[¾Æ2#²òçùXéÂ8./y^@ûÜqÔ6¶ ‰»qïŽEË|K8m¼ìn[ÐP? úR*ú|U啦Ɗ)`ëDðá–±­ÖÀJŸPÄz›E!·K™®™}Äõ„ÈÙ]«1¼ð<–Ê(ÑžÅDëÐZVÏÞ˜Æø¤ÀaÑRøVí ð¥3w|`‡ç^Åi½kÉãm¸áv5~{½Š†¼Õe«N“9‹WQ~Y4Ëà]:ìB4v—ã’òÜoá^íV*£ì Ô©6ÚTÑxM6÷™ L}Ž`q˜]ä%“ïQñ÷‘dù½D2ëh!YT(ͬg{%V‘Í“?`¡ÂR®øzQú_¿µæ°!ûÌšr2ó_ËÂÛ 49%GÙìÚ&²Ø3‘¾¼‚WÊïþ„»Þt…½½OOÉü *ù×饸$dU0Ù5y%áo^…cvFd~O©X»Š²>ÌH$ž­§¹¿Zý× ÍNý!‚m²á¼k.ÙTÙ‹ oÃÖäì—à.p_ÁŽÁùçbthòÅÇ‹;XsuØUu”Ý´²˜>txY2g¡Ñ)Žcú¦ Žä‡0"øÉ%Š˜*lÇ1ù4RÝNÞq½®7°§÷&RYž8˜+ÊOåfGPÏ£˜–Å =– vÕºdj^gÿUxS?¿¼D½—ðëÖC˜Ö,KÛGt‰-=Iþû¿Õ›Ó±g(#Ê–P±[üœfC÷§‘¥ól„ÃR"K½áKáU·2M+¹Å•\À²­~x£]•ŒÆåáæ¶PòøÜÝv ùè>l¹À9œH\RqG\·Ã¹q¨ø$E_}=ƒO«cØÏÙ~õU›sØiXwÙuøX¥ŒmXíkhgÇKF:Ñâô™LÀöÌçðkðäxkŠs6ôúÑ‘glàëAxºùûõg:FÅ ‚Ôú §hˆÊ¥°^ôûß¹·xœÅ”<3”ðˆáçÕô—¿›\áÞ3y2G¢îñE±»™¢AcX8»½:¿pw½^‹6©Ö|I‚¿F4,žÃ¬!…¨›vš¹3MƒéÛ'Í Z’æã^ðÔ¾™^_\Ñ{QíÃR8¦gA¿n""‰„g‘’^Iæ‹c'î(‘³>¦4Vð<\BT' ÑNí•Ôàä:ö`ù|ew¤>‰z˜¶ÚPü(J^¬`ǧ„£—xËK±„öŒ™D¦’7“Ø9¹Ñßn‚¤1þ㑽’Öx“ø3I{‚*¥¯ùHzêq¨G„æŽÙåøê ê%9<=ÐæýÒÝx&Ñœaö׃‹U$ógÝ'k„NÚ KngªÒ}wày“ ×\A¥ÃnÔoµ“ÄC=7D°½úé䄊–MdâÃG®°£´ wg³E«“˜ð`+,]ð 8ª0q3NŒ ‘œö0ÿ› D*eµçª’/ONãÉE0[tƒî<¡(² Òˆ®çyñ¾¹`“)Í=po»u‡ÊŽìÇb=Eª«Ê¹±øG±l7zó]M{cHÀx}É n¯w=¾Ù(ÎyùA —ˇbÖÉ=v:Š|ø´¤8Q=N++…Â+“¨ÈkèbDm%°ë¤Âض{8ZhL.|„‘‹çØïv ð.6Œ;¹j¢!xs­. ºW€{"ïÙh{9“Îè·$c®áúátT:”í'×Ë5¨v‚&e:Zë—î›^QJ0MSõ?v1ò†w ßþ=Çuò1z8­WÞ‘%ÿõ]ò ß¾GÌ«¹.4áÞFÊÃÙòÞCª`é“™ÄT» x’¦ÐºÚxˆX¶†–¦3wV¹3û+÷ÖÍh2çD§ýbBÞ|b»Ü' yINæ“§®žÇ‹3µ)ëäÏêÿÍé±5=}›µ(Åà³É¹O›èÔ^ÄÝÞ‰×XøÿìíSaÇO1_J6¼‚Ѽ‰d¾´•ïÏÚkèRëÃyX|t;yP;Ìõ¹_B4#DIõ‘©Ä³p=âK%âP’OÖ&¹‘¾Gñªÿü(²‘Þ]7‰Öî#æ®G˜Ž2ræøGìOæCÛªu$`§éÕ[Éø/åa~WŒCßžHè¸Ø ÏWZ”fO”²^Ó€Ýsèæ€Îg¬ƒêR¢ vìõ¢èV¡/¨l• MæíÈ4ý³ytê{9¨9)…']èÛbúû¶X§Èº«âô«QFز–D½vcBòÁ54‘º…›£°I9þþ¿TFϾ©þð¶1L};›ö¯Åd‡8ò=•X¨éµÉÛÉIÛ£¨x Q~Ëb4ªia´T‰Z÷žg$ïÆ#Ïç 矷ÌoCÑÓjP;fNþ<Ìc£6`äˆ0ùR¯OŒ“ðb°\_ú|œDèôž 0žkùWRAçÄÓVÀó0 ƒ6L±Ýp3 ]JÄ{Æ ·d-=r'†Š§BÝô¦äÉ}tÖCó½G`Ô3 ?®S¥{ƒõéa/Sý̸ٔ0 '±FDÔi.éz¶Ö~Ie4g<ÂÂÎt’X6åã‰Ä7*…ðø…\yQ)Ôvt¢ð†1†gÞNœª¥LçÖƒ ñЮwó¿bbBˆ/)⊒%£ËaÑI¼c˜LŒæãíò(²Ðºžèl³Ý'6¶/M·¹Ô?éáW %z*{öò|¢e»Ÿè 72ohŸéfƼ@žþwîS¯]É •¶ú÷])¨Ú²š†ŒñPÃ/Š˜ÝM¡äÛRÒb*ý‰sˆµe=%ó`çÚuDýTÄ\Hóùœè©²G¬`¹=<¼É¾# õ[Ðû5é„‘ÍìØÿ †5³e’8Aä}— xÃQHæÿðoïdÃ÷°ôøBÎR‘:¦÷o<ï=n/CÚ0O—ŠŒ ¾?yì‰^a8Ø3Ž×h*ÆìšJí«"ÉÇêì[“rj¶p;v¡.™%ú‚ 莢¾¯ÊÁN(÷°¼ [ -éì¥3Á¦:‹n 7ù?ñBŠo§D‘S5ZlUÞ>5“¹p«ú+åé¥};àø~MVúžÙ«±œÛeIx˜ ½2Õ’håBõì@”+  ¥—h•ó#.£:©Á(_ òËÞ`Î)tO#Y©@µÞføõqÖ_ÿ2cðbÝýÙ‹HÃC¤M/š­Ž¡ÿ‡ßÑp7ðð½ÂÜV¸à”ïçnb$…¡®Û«ó?¨í4{¸esâ—‘²¸|ãA,ïöXâDr|Ä5¨ÄV+Z7à‚««Ãý=ùœò'Oá«ÆjÒçF(ß]0n´ ?•!e*#ìò|r6'ÿɰËþJs$àä€6~±ŸO¤d߀BÊ%üsn6 ±+†£1 ¡üà*2ëÌÖ]xº5Q»¦³¸rg Ò•1¸TgˆÙQÝáîhqÈŠ»‘†½Ë“9?6Ý‚ ;A2¿VÕM%·å]9ß×O%‰'‹{[MÖ]#¢Ï}ñCÖ|ÎcK^(Ò‹ãÒ¸ãj6Ôô)£óŽOì«úìÿáúJw®Æìm±8Êã‹Ö?=‰pU!½?`S[>A±ÌkFu‡ «ìøçu¥Ð´­’$PU|qÜIÌ£ÏÆc¸Ý«¥&Þ1.ŒÆòæâd X›±µ‰Ð’XœÑp ¹uŒ ]£¼+¤¾çÚ3–'ítÂÊxÿ†#yz gv„–Η0¾ÿ(<ÛP3ò$é´]|õvÍfT8PØ®3`%‚Ž“ãê^¸²àœÙÃGÆïŸÂ«÷kqC[wÓS>$ûáÜÇ[œw· çy?ÿq{¾ÕV½D™©Qèðs/ú\=ñ/ÿ½=zÉ6ù½,~¿»†(WáÃXIâºï(äüID« hܘxRÂÔ1ûÖ•/!JÂæ´ÅaçQëöÎË7°!À‰Rº þ·š!kŽ ­AñI…ÀOøpÔ'˳np½>‡ °N;G$ ·’ Í´[äîç•ý`‘@¾ûIÐà©R¸ú¶-š8 f6?qÇìo0Õ{*s¾÷D&g#B¯?„Ø #¶—œM¨2³ŽC{=‰ÓgWÒ[ƒ'ñòl ¼{ „ZçÛÓ¥¶æÔ÷C5ו)Ä=ªaš}ÞTùÇÿøÛ6ôœãfÔ‰HÇš"mV Mš–.t“&Ôäêöò W:mHš¶—jƒ>|ôûº›ìãu§ðÐã›p)=/ͤ?S-È™­¾d_{'d¼0¦izÐ©ï ¾*x £›ÎÑ•[¢¨VO?+vÅ …O“{²¨üq:òH$÷E³{•ï`=QÖ¿°:­e0ßL¢œü™‘n1¢_veRÉÔN87P‰+#}˜½wC˜—Zq¦øUP¨òþo«(ð†:ÜΠ2oK)fò5ˆ¤s(”}J4è­µþ„{ï™—Jëý7“_=ÿù¿þŸ)ìUŸû(~Þ’˜ö8âÀ½`ºwò#ŽìIMºFÕJËÆ¥¯oº ݦұ­÷Q›žÍÞ@çn:ͲœØRº¯n3š´%2z^0ó9]Plw™s³Hé0ŒûnMç) Õï¾¹u†ôȰ©\Æ:)sÈ೸R±sæ³k ÌP©Ìˆêÿà†ËŽbðÏ~ ÞN3ÿÖ$Õ?»Qùë^rÀÇ‚¾½Æ•,¸@ñXм`rhá¿óû®x"ÏAç04QEï½Ý8¯Öš&]q#“µMHdE,}’\„)÷äɨ¹ö?þ·ê´pñáMxš3Äv=ÀÝ®sèþm"D³2•jè%¼³G¿pq?:YL"ä¹"‰z)õ‘ÒÅ:ð(#‹>wXHHO€šÙ7ðÝó…$$à(¦·^Å7SÈ G&~Õ­`+Üé;™DºaÕ|°Õ<·$Maß´à´Þ€Ä<$½[HÀ\Ÿñ 7Zˆ—çáÁG_Qpç&¼óÒƒH¥ùÇÿÄ-2t[ÞJHyÞ Y)™È ò| ¥;^ ÂÈ ,á§®‡my²_àâ-˜5Ò© DÖí·'BiâdÛ|Gê:G´²Ö€ñ/ÊÔ1gàÚ`º/íÍ ¢¿ÔSд¢¸„ɳ×QA !buÓ‡ùuši—W)ɦp±29úƒcäÿ>.ŠaÛ7H¶yv¸p 4¼‹D«kôE9»\ŒHñé€@ò1\Õ|§µÀ5CIª~G„ª¾\ç2ñT¹ݽÄUz|É‚N zœg1>¥ËÞ ûŠg6½ñ{‘Ÿfþÿ_KØçW ˜U OAÖ ÑgV“gê°¸þ/]D`LÙüøÏîFº'F6 »m!î¬ùŽ÷×值ë1¼%2ãŸÿ½ä} †mQº' bÎ/DùÜ*¦:ÞêW¢½‹uɉ­çðÇoWk$Íɨ¶›!¶|Ÿð¶E,=d6ÎYRòª~ØÐ+ß¡MÚC¦|œŸ8 Ñïöp4]—jü¤d{Ð ÑÛp‹G¼Ñ²ê;—ï‡ ÏÉàd°¦ÕŽã¥ ¼wÐŽ™Í¯AïüH€â¬4üµ^…Yþô;¨«N£—Ã"¸W¹êôg`$,>÷y¤ë®ÚVdžÇ8]#HÕ÷m‡%‡ü©‡Ši¨|Íî%–´ÉË„,ïTE=ë 0Êæ!ykÅð.7y§õÿuQxœ³¬¥AZé ^ªŽ„]á/`d—#£o.I5mäHò¤Rvò‘X.wÉ8 NÕ˜@„€ w6üh!ÂÂ2Äœ˜S“ì}¨ö·v>ðž©.=÷ÃÛðâ× f£X¨Ÿ×¦Z[ç ÜBl¹ô&­SdyâÎãÑÁ7ðpët¢SÒÁ¦Ø¿Ã|Püd;<ù©MÏñ0'ÄO°óE„©NÝ6ÿçÊß]Ú?u©éæxðÂïFSÉI}Ö¢½K,Iº•Œ†î=œU.’œCú? Ûë6,`î¤YÓë'/s“3,iŠŒñ¿øŸ¦-C¦òÍe[G¯aÕ1YeyCôŸÃáö`¢x‡¡³ËÓ¦NVظºïïWá"´fO?Ûô,·£Ì™¿ZSÄѪWìE£A<·°Ò~&Ù6mFõ9Ó©HY'=|‹Q_ÚÄ`Œ×sÀÈvKx®áÌ6^~Çïb{œh¼ŠÓÅÎll¥6õñžBÛÀtÇhœfOü‹Rˆ¿½#kº¶ϼ2'â¢dÞBRݨ‘°$x¤An.Þ¿5©ávUXï¥gVn§ëö—à8¿»m”ÝØœüîŸþ“ÍÌ»Q.G¬l­N[@ò2U¢nµƒ-늰‹Ïø·.>c/‹ŠÒÔa!¬ÍXDàâ6FqR*E`ƒÇN÷nO6²+jEua+"î=\ žíÜÈå´ûÏy¼|y6¬8îÓM1èàaTk`Œ†°Æ2/¥‹“cÚP¬ÌÆø¤#u7õÅ/­bØå×ߎ³•/ß±ÓJáäúÐG–Lÿ8× +ôüašÙ äq¨çìQž÷ÿÎvaÑ£Y¸jõIÔØ¯A%-71ó>§{×\Æï«TªÁêòW¿å#Šz)àVóRXžmÏþ^Èv±Æœ(S¸ÐÜiý¿Á~(œûùÍ(’­O@øëvüyS®ØëN^=ä£Cçwã‚ÞV”>*Mßöäļµa=áÿ5“Dü¾­B½sç^þüƒ ŽraD¾ç §¼ÙØ~˜<“¶¢Kà ù†/ð*E `B$†‹8Ð1±Ô]WˆÏ£±çO@é»ÔT͆MGIXàû6© –ò&Þ^þDæ».nû%JMV~øÿ gCœ×ÊÞì\ùZŒ‰fϽK‚…íö´h2GCû³ óë¨&/>,"ÖÜèiªAéócAèV4!Ùr^4aKú: —,À1§Ì|`$¨ @, 4Ɔú8E˜“©NjmƒHeè8Þ«,¦kß}Ǧi‡èWo3J%§“Ôƒó ®-µ°„SÒi®æ»ýí\K¡–vd™¼œwœ¬>Ÿ ï¼6‘M<ÀÛ=Þä®fFB˜¥ 1reƒ/ÜG‡6Ƨ­óè»{&iý[_|Û±€ÊiÿA§r5Œ;4ƒ>ßÍb·4síýbÎÁëözdmy þv9ƒ»;ø¨¯ÂÎ'õYÈÚ‚Ä 78éÁÐŽî‡ó9ñæ¼ø0ß]÷!ïì…è·æzðwº-QG±A³œÍ^r¢3yȬ–oètªÚÍéDM1ÛÛY?¤TH)¿Ie£ølV#ó,qóUL—%Ê‘${¶V NBr̬káäÎrcGÐ?ü\<˜Ä‰DØkI·ò3¿FŽ0‡§À4­§ $ Ûæ·Â÷†|´ŒðÁ?…+à{™]øwþ`ë™D'Ž/yš!L¾ZƒƒXkÞ4&Øónô%§Fo²ŸŸä²m';ð¾-/“ÔÁÖBtMû<)8‹ÝœNG]gŠ6òЇ;¦'ÂÀÆ.bÛ ©TZÓ²4€*æân‘ Nõ*Cæ­Æ^(û|vʪáL¯QpûVŠgL‡ Î]œ·fûÉF©ÞŸÕ9SßãO© °t*Œîô`„Ž1¬÷fÛ%×KÀ£èý?ügzŽ-olˆHg„Þß…CaÍ0$íÉ*êÂÊŠÏ\eK¸Ð L7„k¦ ­‰Ö¤Ê‚—zÇ5â”ÐÀ¿÷¬·éø\­D,QÇ¥aÀŸAû-áÖèî~‹kîÂ*G>å{RðüƒJ™¬kº}§“áþ†ôcòî^> &ÞºÓ«ûÜH w»{’°¹Í5$ƒÁ )úms ç•-J5 Î,3sŽXéG„$Ñô¬3ÐV¸ÎLfb§#ßÍiä@î I§S„!ò9ýÚgYZŒÑ+«þ÷ÉîM쥞Ű,w têÞ“^aŒu`óò¹qYaÉ$¿ØÏö«‰²oseÈ­ÕÅw¾û¥ÃT©›xpBzXÑy:øb(–Ù½:>Ý?’‡Ï35"ï9AÉ«`Æìd&|»7ã×'CR‹¡uæWÈo†<˜ŸPó}?ó÷ŸÄ-,B^~wz\r-JýÖ`__Öd¦Î¤Å;&8¡¾yXíyïÖ ÷ÁIî÷ 0ou% _À~µDlÈУMG¡Eg ”\”!gDô¹ *zÐ.ã6›n1 Åaܻ셵§Y÷S‡þÅÿÍe êë`ßîÖª \—Ô2æîbYvÓ‰^N—çN¢¯¯†*/÷ÑÒé^ ûø(^ÏÙ óÙ¥D^d´CÇ!zЈ²:l"¨ÈØÑI3êàÈ»©Xd‘‡m+ Ì׌$µžÀ/vÂìÝ_3Èl["Q±´¥>3µËå˜Ë­c`^ _‹ SåFüŠùéF=:Ñu­ßÙ—‰µñ¬‹#3ZÆÜ>»••X¼W³‰>Ö}ÏÈølAÃO>ô+˪XOÅeÌ o;’"ýŠyXèBUïÞóýoaÞ1 W¼$iúñ&È;£ñOÿô˜Óáô?lî°0y2± Ô%Ýéæ­‹×’Ó¾ˆ¯÷/g?,8@­sñ‘A+.Í܉.‡)ï•Qf…JºkèÏw´¾(†ON1Õb†TB)œ4D𱟼oAÑ‹aæ'€ND[’í¸aà¶p¢gU3"S£ýJd»j!›¿Z÷ FÏm;Ô/vγíýõ{Ù@z,?ãŸ$ÀýpKœnV3¡7ú»ñxÕsŒ u!uî-*KÊÜP)Çœöªï'ÎÁ¸}I{0K‹ÞñaÒfÈ¿™ˆaUùxäk4å}Ò] 1ÿøoß<“]¿lçUÙE|²ÿF:neÙ*Pþº‰#p¼ª˜¿5Gw^7NÃ3ò¤§z”uÙÝC˜ÏïOàpß "£:—ë+Då¹,³û­åy£Hëïl ¬tVÍ”BšfƒŽêZdÙæÏ¦%aœ6Ô¾}¶°‚3Ê$ žÇÔ«SÙ2ä¡yŽü°¾-ßìö`­ ê1"w Ìžï—¾3«¼Æ/jW°½¤žÉsŒaùÜRðü5’þ0wv0§§ˆÑö´xךÁ®n6 Š*¼Ä³xêy8“ +‹q_¾=‰ËþøÏÿ¼,ý̺{E=ÕÔ×"ëØ'èÕyÏü©„‰-¦œ.…É´ûŠ"|hÒ#5W,1Y\“ð-ÙȸÍõÉUÚD%,%^ÆEƒ˜—Ýnšœ}óL!©é,ZªM@n³0.ÿ÷Úºy9~Á³‰™‰$Lˆï€#K¸h¼«¹ÕY€¥oÞ£xÂ*êb$DL¼aöé{Üo+¿pågv K?~³¯böÆ­¥úi$ÄT‡›2ªK4¶ch‚ ¶ú&sF{‚=eV€Ÿ›Ã ãPÂã::(T#ÏSG\.eßC%ˆý‡læÆ©'ny†å«Öý÷üwÒ´³€/~ÂàÔxìø²¹Å‰LwÌdöcÔSïàîP)r¡9˜f‰õ`WZ8]æ’HjmÅB77"z¨ }QSÄä… è”Ӄ̜³™PÝ´Ÿó¤-H+¼c¿7Í%[>>änø¢Ž¿³}ÈØŸ«à¸šbärò[|ÞçD“oÅø6É—µ.Á}ÛHü¡§p~™!|iÓ¢ÏP᯾^‡tƒ ¹J¨] CþÈ'¬NeÙÒYý-GßNV%ÎVº„wþ.Xè%JM­ÈÃ;¨°®šäã2â¥1ú§á¾ÖÛ°Ìsÿô¯zš5.[Àƒ:¥õ¬Ô# ’¥Bß½ÚA÷:í ÚÞw‰*n ¸ Âg&ÑÄKÑ`¾÷>º>çjéoÑtDH}Ñcl’K"eÏã¨hÓ¦`„W>d0GèLâ.|¹i”iš‘€™Þ¯0âÙßþ%k§ÁÍ¥‡éE)òšç”Ÿ–†—Thy¹tG'A*65–FÞϼÁÙ:+™ âY©œLÓþ†š=ú9°šZtúÂõuÛHô®y$Ðø46Šld…íuh¤§•úõï'p*Mq¶cé©$2YmlŽØ1ˆ(/øWÿ~\w :.ÍÁ©]›È¤ÙìŒ5qDø¶‰¼M3×{`[Á$Ê+P¯gŸ„[t<˜ÜYñ•éì'`eô»ó&ÓñoD­R€Îû¼ˆîXÌrwIâ›Öì1ŸTöå†0»Õ kCf P²06˜JN[®¥½Á' î{8F˜ÒY‡‘¨®65Þ:†Nì¥ú”Êvb.²4MuFõ >r¤`³ª¿…IÛèFSGµ¨î€ Éx<‚Ï­åéV!#g¤A‰¶^ÈXDåjDÑqq7Çê΢ N6ÌÑ¥#ÛæQoºÙ¾béþӿשW±È¸ $O+‘É²ÆøìôsÎkgIÕÈø¶ò1îñÚÃ~9ò‰1ôÃç(ª3~Ýs°“ÑèôIj7l•»?¡¡±+}|ô>÷ Ùá(pS˜ì䳯²Ÿ*ô©Ü|è}"ŽlY„ëœÁ)þ)8–¼‰ÚÜA]†ÍÈ>“1ôWtƒ7ø˜‰ŽGp°XÄ„™¯OÓ•†U(÷Kš¼KŒfgX^¤OÝï3·%´È½üsŒd|0´ôi“Ãe»Ðêþ 24Ï ,?{}ƒ+žšôZÙq¸•÷UŽ› ÆzCö!ŸºýüÇÿYŠÓÿ,S&ÝÜ…8Òó³öà6å# ÐŽUâ¯éÙéf´µX+¿ëaõh3ñýÝUo°t#$lUAC彘öâ4µóƒP³PIÒ .ÇBì·FX0_—êŸS¡¼BòÉ÷ø’9 ½+âà]4îu\ŠI4„è¯ÀòdcÃx½0ñå~òÄ[ŠX>Õ‡M~f8ÜZD7š­‘Q0ÿ©=µ­Ôw^¦Û1xÇ—!KöÈ“­È­ºaD‹™Ô-ø(/ަzÆŠd¥çO¸ü’ÐSË$iÎ7Mz+á+k½„8®õÿw»<`œANÒ(ò* ÐÅb7!ºEˆ<™…z¼WÙq)ì²MfE ´ÞkƒdÑûèýIK}¢èÔ¯åÐ=ó Nl8%¬w°1ÈÇL¯V8…ï¯àcÛè/gŠ*­íXC®0 ‘U˜~¡–=W"EŒOªRøÒ‚OZŸb‘Ó,f+Ù¥ÍF›."‰ÀÍyëÉ­„äTrj´jPæè$ú>•Œ¸=‚íF4ÝX:~2âßsif…™¤ eàt±”õP<³Ü€yxå *HòÀ……‘Œ¯Éª1¥ ˜M— «äó¿üçû,ˆ«àcƒß¬S¡óQ;·wpä­,‰ÿë•x„-cž'pÎÑæôm¢~÷@}N ³û•)ac»ñg½IÛ˜Œm‰¸‹k/°3ÛG±âÊoc`"ºš=ÀüozXzHŽÔœ³¥qè„ýFòþÆÖÙGkë³?n~B™ø§œÇcÅ úä '|ÓE|z¡]è’˜î»L~E"32ëÓQÇ4"‰«nþ­á.¡ƒÚGü­}–iïñÃד)R3àž‡”vÌbí/@Þo¬7ObÎÙ¬!Á-sˆ½&çѪpLl„µ-Pú—ÿ§íƒÊ( rît<¤„-GÙ»¹Ð{qQë6¦ñ÷±Qjmþ}•q4ÿÅüTéãžu"kó×#ÄŠÒËÛ)îÈÞ)°Ñ‰ÝpåŽ3lV ƒòß zš"ì㣇xÍÉW1îy¡-düñ²r۪㑠w†"éYT?p„lŠhÅêÏ GDè)ùp")oNE| Éæã§[w¶ƒAÃ(Ž?¾“R È´OöÐ(T ß^òS©Ù î^s7:MÆçBœÜfB֌ӾÕ1ÌÝáPê±ö!¬o– n“à/>ꬹ†¾,›E;åTÿá'¾épk&aSw—ÏÇØR©ðœå'g³Êû²¡¢¼žvOnÐl \¶™K¶"¿ë±ÿ5­Ýî†ßÓã ¿y8cÂU=R(àËxN{ &‹y!lt‘Êw[¯%Ô·ÚeŽaâ(ü$ºè¡ u°<a»làKØGN±ûи¾˜§¿~E±Ã&ñÕJ´°n7ÔYO¦ÏÄy½–bñïqˆ‹Ðƒ›i,wçˆ,^Ÿð`“¯T³ÁcR0åÏ ¸°X†ö­Ï$–ª_ñK˜¤âD³³ÿÖ”y„Þ\ Ü_eÿø¿lš ü0Ãè€ it5%ËÖ/£O 9V¥…£ÉÄ*‚ñÇ!|ôâ#êšÈ-t^HʯÀãö8²mØŠþø±úVx’€m%ÞíºígÙ™EQpâ“ hò>E‡›?0¸éŽi“o;&³\©£ÌykV:`'´/Ö¤!ËÕyäIÌ‘È†Þ L:mH3cæA³®5ٻ܋Q¦ë„ Îé”b†¾öÎ’ NKpŸ½0:¸tÀñz°c‰Ñ8ð/ÿÅ‹lÃð‡…è1Åšì^©J®N²å¶ûÏ%’ï^cÌ”dôHÚO®ÙÀªE”aKa>~î¨`onR$UÞØ÷ÞdŒ`í;%òó-pE 7àÁÀNzã¸2]Ð!!#¯aêi’Ìö£zòe83ÿ³®¶ÊõS¿Ø7'a˜ÕwFpq&ݺiβ×å I̓ÌÑk™¥¤°W‰|L@ÿíš$êAõüäIDëæ’Ñ‹ŽÄÙ?¿íÓ¢³›§ÒŸ/Ÿƒ÷«pê‹‹!cÒ(°rp2#õÑ…ü©«”q ¿ø¤þ‹Xá vû☶$›œb±Cåx6ÔšZoE81y!f]AEKY®~æc&‹ÖÍ”ùÁd'Žs}#ÈÛ:¡žãefYü:t21„oys!—BABn¡Æ3Ðù /*ßúU*u¼À¯‡ý+óÞsï-½[_`ÞpáõÅCÄÝo>8gw>ÁKýspÝ|o:q_•>¼ÈO7³Ï.¢¤…«x¹”¹)0…-8x3Ìkz ‚ZxpJ¾Úrn¶1Ÿï‚\'88¦´NÂ:w‘›eÝP=µi]õ/þë1(' s®ž†èbIÖÞxÍÎ SÝNàu´EÇœâ0y‹€ú¼ü[4O†>j0Y“>£» q»9]­ÜÏ=aÂ*;(QWÄ™Hò &±Žó„éïºH¸Ï^`”=iJæ29u¾)Ò>ýdlîó×f¤ï»sò›1‰T€sËïâ­mºcþ$bÚǺÇ=ÑQ"~' ²üüßðZÞEø‘‘ƒ?í1>ßÙPw1’¨;ÇZa³Z*Q¶“Ä)Bý†_÷òHÚ+GªõHøW5X»ÆÄ¡œØñ?:Ë n‹2WÀç° ¹’.ÄÉ{ÙÈ*ìÞÍä7m…!šÈ²îú$ óñéÏú:׈k¬ò¦"¬uYH/])c2&_ qµþ<öþÒV6y¹-ÉäY æ’†¤65§þŒ²é“³è­y˜ù1ý®ö­ƒÊBfʆI˜»ª‚ôW×b¸û~öAçZ“=T‘NÖÔa÷6:0¯¶ª°Fô`öX-TÍ3ÃMÞD"ÛŸ­A—÷¸es2ÆTÂc¡›ŒFSÈ1SÐ9$?D #´ÏÃþv@Ü’ü~¦oóY–çÄÒÙÿðÛ(öÁé+ëqûOƶº BÛ•Ì ‡´iqº,=Z63’’H;nÈœ§Á!,íêæ!>4‡Öl`÷+¤Ö!Â0òe=÷8Ïc‚Ù˜”šFž)k°òŽSi‹ÖY›ZE–ÆÐœõDãËZü`0Qªzô@f/êhYÑ8¡60ιÇú.O§®ÛÖÓËtÊ=Œž Hóôª¨+±¢Û^±ìBzjóÚX˜:TJ“žïúôCóZ²_¤…Zd«Í{{UžÈ*¶à!É)4¬"¾M¥ýåY¸òêÊ6(•WH._wøç…}2Àþù†Å±—ággódP×'ßÁÆW€ÌHâ%¸¾d+]yÆ®™¸­Øæº”<-—¢å‚á$Ù2†ø®&®gðš‘]’¸µíßâyDm3Q'f¹nWÊ HHaýÉôç_‹{3x‰á<%P d—'ñãÑúzç§ɉP¤/VÏ£§kO㵓®taX4i+ÿDû_¤Ü¿5¸é6â+©FV(®ÂMˆӹ$ÙôÞõ™m3iaÄLÙÔÐð+øó;©¢î´ÓMvDZÎÆ‹ËY¸}¹\ö¿÷?¶®SYƒá#¬@v3cÒÖ}»Ç´äÑúéÄú”Q*¯câË*¨Ïô&ÔxœŠL_8Ycè îŠüxìþ$üUâ…߈ҥcðéU,;ߊô¡³äû4˜Ö4ˆÒo¸µîá,Ë“Wø"ØëÓwY´ÒÍ¥ƒÃÀ˜Ø9޾8[xuµ#qe>´";ŸyY ò¿Xé‡g˜ßÛmñàÛÉ$æ¬*8ÝÅúvPüé!Œã¹±~8'§KÑà{Øk]Þ(@™ÆÝ¯£”cÉîT¾Anß’ù›òÿø¿áÍ.4i9ñZBtÝÃ!ˆ3C6ÆïúÝ/€Ý»#ñvm0Ó³¨ÖöiP3ó`ócPð= 1ÚxËOrKµÐß]†7#»ýø,òDBqÚ7YŒò‚ÊÙœÅ,ý[ì»Ão¹+ß Ò—"ŰuQûEá ›·JŽÆªåáHòf®íV;úBe>I] ë,³¦’Ĩ_½ÝëHvnX‚®7Ðl_>9eŸ—e©½ áÌ`zôÔXÞéVàw£$Sò¨áâÌn _òÅÞ…:¯Y uôÓ­4À.™ï‹wÖÉâ·Ô' ýs?%ɺÿð÷¨lÏå}pVv&Mh~Åе¦|ßt)ÿº>¦dãRR™˜FnÎyLêC¼³«¾«j3šSÙà'%(Ф± 3_!]U†dQ³]P¡CŒÛVrê«ÆP6^}¾g gñ ‚ÎÓ(ë÷ñ_(Gu;f“@7:º_”Ì2(¥_:Êñ“@+^é?nú uï8in ÄúÏ9T}² rvâÝ¢ÐþIšõÃëµû\ñsŸ=¼n¾ËÉêÓ¤ŽšÏØa;! I»hÍwؽϋ¾79Âzó=aÃÃd0þg<Îûëï{|6ã¸a<ðgÅ?ÿß9˘ «3g®:ÇÚ)1ôš¶<¹5c™Ñ7¿®Õ!r!‰äÞ†=äœ÷/ò2ê)¸N #nõºÔ8g'UÈšEc|s™’ØŸp…† 3䞸XÌ´Åš™WÙÒ´z“+}wš¹´ß/ %!EteÀ92ÓÖ‹àÆ2št+‚Ò&AÂc …¬È‰ÕéÄkÙyYrŒaV?ƒìű|}êBHPç rªc>X†$ÐÓË3q÷ô´8h ÙÕ#C×5g{ñu&¹f¿„è©éð‚cÌÆ QBôWÝ¥I3âp–{î¿ü¿sÁ/ˆæÀG™ÍðG®ß¸ð“öä(z=—ú—ž…Æ/z´¢Q>:š“ÞgÔ}`+úH”æ†çBl?/Ngú˜ôøœ®äªY"£,dž!ÚßKeå_pp÷´½„¨SЩ3{3ä9Ó®ßÁLÞx„mÙÛ‰ Ó˜°GH2‚Õ{uémA=ÜwmüN–%µ”ú<Ž?û{¥±\÷?sçk\¶wtª;á‚d1ìñi¥EŽD°: ¾ywáŸgK˜©“’°Dð£²'ŸK…­3Ü`ÍKíúwjÃ]¦Úb5Ó»·ÞÞ€;kÛ¸­ ¦Äù½éHìw4Ò×—V°Âí·‘wº ð¯:ty×$b•'j»cp2;„M{Έ©Èbn©!/HSCk[’fE¹<™äö®|õV“ h3NâO؃¤l‘<]3dFsË·ˆÐ¹×A¶]‚\—Ô§[Ù/˜Ì:¯A07­À%9¼ì¢‡̃ Iê|¨ƒ­æ4ü‹¿WïwÛÓk؇»8>YØ£¶>»Øc»fx„Oö—ÑpàpÍß¿Ž\SE‡æ(nHÍžØCÔ´—pKUžèdÏ ußÃVû TJe€]ïaÄZ,†’êqšy»‚,¼„ºÖu§@Àõùkv¨QêºV@ÅåtÌïšD“óæ“à{Ð’å'›%ŽÂ·#j4û?ã{b ÏÏE·¿™œsùxcM“«eAƒ¬Fñ¾ôBÕÖ‘EK@áÝd;Nßæœ|ÌŸžšJž×ÙaC¶l© –EÂäËøgV(RôŸþõÈà—Å>¬e>Ù%ãDÆX×WØFîÞŸ€ÐÐ@ÐV4#—^ßa¿,¹‚ækƒ™€ƒë‰Øþ{°Ç¿û_ÚÛvI6NOîß„²és¨Rµü ³?Í'ó åéæ_Ìõþøø}&½˜-Ne¾V@â#eìW«-iò:î²à«Ddc'GhÚonvn4®Vç^’4§2.¤Ò3‹q¼õ»ÄKÚ¾?Ž×&‘%——rµ«\I\g':¦¢ãέ¸óÝ<"môˆ1]» ŒAÑï*rrk3ÖE2T¼y=Þø÷·Ø”ü¯Û±J¤'ÓåýQ4•‡×nã¹Àrj¸>ž~]1ĵ–!<©óèù SäîºID£¶2xámç9Z²g-7IVÜîƒfqêtÀã^!2³OÐG& äÕ–ìº1—,¸ü€>¼êFWTñЗÛxen Ë¿j;¼ËßccX½ð ]#Ý«ÔÈOíŸtë'ø»µFg‘’nBG¦bê=w¬˜¨^V$>Üqœ?’ •’3˜š/"°ŒŽ‚–ÔaH¸âB›ç-%'ˆžKicê¶’”è/$GgÖ’Êm}ÌÖüqNL«ñÿ—²R=¬úQˆÄh~kcKÅmáþ…{°Ž§ôÏU?üœ›F&’à}iŽ®©E‘RR¤o–¡ßVæÐ ò´¨N“|Y k¸ªäñ*òçh<ù±#†ð>à -·®ÿ^Îuª\<»¦Ð«btS̬|7ïº m/ kçsޱÌFòÐÞ˜\¹—º)ÇáÌA!º,½ =}7’©ü¦ØÅê’È“>ô¿þæDûœ³r½úl YìÁ,ú8=»öJÚäÊr_pù¾–~œÈ"Þ9ì¥ÏŒã¦wBä¾Né?ÿ˜s*¤”˜®Ì °cO¢Õ˜søÁ*2zü-Ó² ª_ØàÅbçñ§Ž3GïÛºè’ ºh© y÷¡ëŽ9>Î>Ã,)È„]—Ý0ÈQŸ.!ÆTãE3×J’ƒºVÕ0å Ùa²>Ÿðfg] t«8Ž›º˜uÓ~€¶ÇYÐqeE#ÂIÎ à6†cèþ[ðø»/˹C¹{‚Þ°ËlüYÞ8eÚ_çEÛ=ŽC‘d%úìu¦¦J–°zñ)6ìøw†M3…‘zkúß>§‡­°õÙv,›`1XÒVÙ:râ×0dVw†lÑ"×n¯û—ÿ-eÚPâ|4T¾`SvŸ`nxÂÊ•‚}¶Q“3UȾ$Û²lÉB÷6Øþ‰Å“oÒÐ|Õ]NUïEFÈ皌êãùÎ ¸³B—T>TA‘Ð\žÃœZ] D-Ï…¡¥·Ù‹{ÈãÎlbÊkK¿ï£uü‰ŸØŽ{–äÅH#ž»*Nßøƒ$ ¡Kª—üÓ)Ok =ç…9E®¶K‰ïàh¾ðÃì(îY@ánÈÞ”§ mÀ˺=tÖÝh˜÷WÔmÜ–b›­¶>ðk3î²”µÞÏŶ­Y°ƒ» »×œÁ»»æ’qõ¤tZFšô0 uûÙÇz:¤Õ^˜]Ù†^2Ç–ã›sÏ1|vóDù,oö†S7Äà‡øÞýÅà³°À;U‹oá ÌÏfk;Èík³¡Ôȇæ[F÷_õ‚?Ó:ØÛÜXУ}ezD]OŒöFÜâÔ—ÎÆ6“LMfßÀALxW R6Šÿð_±´eúr9s¾Ã«sìñ;mœß ΂âã ¨_!DÙaŠ£ ±Üú¶|ÔÙGÔ.éÓK6O’»¨ã]ÁÞÛ G’aîo5úuN’¹‡C&ãþöÏš3™ýÖ _5oÇ$Òöey7|¶l¥íµM¬ýi†´äÂC_¬Ù¹J>ºÒžÀ"V"%ÖOǕʇP»ü5jí¬g5í²àø^X„óâ;¬ëGد'FîÛ‹‰úUÐ+—G¾¯]Iöq{Áµ¨ÕÂO_Êýå@>DY,dÓ¸ÇÉÀÑïøõìòúõóF¶Kåßš>3öÁ…ö'lŠz%»ƒ”üÿ!yëuMrÌÀÎç(:ÿŽE©9D¡æ0šDù`Öf3¤»’ õíFí; ¿ÂVAÑOP=ýŶ±p=ÍíH¤Æ5]»5iÒ¼2(ì² ;o>f†¼Cé})˜<Áñþ]À$?S¥u`Èï#doÅöeÑ¥oqAõ^Œùµˆ]ÐXɼ}Ë Ý¿}¼ÁƲCtŽÁ"ü»-XaDTO‡rëTÁ™1urâ &ˆ6s@mçÜPÅ0X–\ÁôÞ=Áö.M¯ÎpZEƒö¹”‚ëù¥ºz+èvûaÂp%^h}”wêQ7 Ûú'V¶ kúy°|c$­6¦Þæ™Ðùé Ó^øQŸ`³¢UÈŒØs0µæëvn2ãí·¯oaŒ/¤0~òÙ8ùìax7…ƒÅúÇê™ó“Àµ ”\h†9jÝP$A–œVe™g/XqÝøÈ1€„êÜÇeäì|&O£Z»arÏð“Wbã“Ü1p0ŽëqBØÖàÙˆŽý…Ž\/Ö#YšŽ †£Ä¤•\…î«(£òu'’˜7Ks¹^×PáI³ðãJâThƒ øo´r¾N?ÈÊi‘¤íùL˜³Læg¢Tád|:Õm¢/šþåÿ ŠeLû˜ór) e˜‘óË•ápš=®i? SüéØtúuæùLãû&¶%P?mj‹þÈøû½ØôS›Y§˜†Æƒ¸9¸”Ó{DŒ Äuë %v×wÛE«¥Ðm/ ¸³á\Îôÿ«Ø+¹ ¦¬õ$¡áâDßëû}(¯œ’Ã>Ö oÆØÃÍÇg¹ŠG¶Áþ‰Ýx2­•ó¢O€:x(ÑïwøXF„FwpõìËñà¹`QV’‡ÆrÃ9…ñä³ùœ4{â,µ™¨S=íDͧ·Z°%b†TÍþ—ÿù¥³5B·Ùª{ä›ì(ÐàÆQM½ãĵ£ šîû³ÖÛÒÙm×®ÙÎë܆7³‰V¦*Í ” å®v0td#4êerÌdpÎ59zWÀ 6½Œƒc´³_q£ ß¾W€¯¶,½1°Ž~ßÜ Þ²SQLù)¾ù õÉ +ùì}Ë.©DõVqâ$›†–'Ÿs^œ(aÌâ~Â[î¶ÉVUëH²Ædš¼i>¹qÓŒ~ã5$Æ2pÞý1*çÅB–4Û?ÏA\0€:_ά=wç}…/¹CðË\šª½¹„ƒ·‹8á^ŸþõDÌNàÚ¯yÆdøÌæ­óøîsöáõHè6áGÇ:.º‡œaæû¸±3,œð>‚ ?>DçÌ2<1ó9¼wø¾Ã“ô+žBÓÑC„{Y® õከ:f¬y ðÙk‘‹‰Élçh.T÷aìÇûènáJŒøè ¯ Ìüm­iŸö%ô²š 1s;Á ϘþÝÎðÊpà×ñ£ìJ«É˜;[™¤Þ…3,9“dcˆjøÉ‚7º·PÿíÜpýž[vçl›D/Í1Ä[1Sq»©þ#O]e^tñàp/nC›ÏW²µhÏõ¾ÄÍI®èœÑƒ3—s6ÏÁ)wõIF‘Ó´ bC6¦&BLþ;X¯¹8É·ñuK$Ûá>™ü×ýI}\xVÍ>Ü·—:‰~Ç´ #Ìûà뢖Öð†>0†.Õ r)ãø?ü‹Lswu 3œ&×ïåŒCKÙ L–ýÁN Æãì[³É|QêºiûÑ"u§œd&ªÃ8ë ƒqÎêùÔzZ7ë@Ï0Û2šØC®SðõM}ÌLabRWÀØÊ2à:ÎD—¢Lü;¤y.Åó›pìö:Fk>ú­¨b†)ÂÏ11fŽ>±OxÌq©}é³/Ú*MÙBd§sü.cœ²6äŒiÑ‘6iø}•{—÷¤0¶u Æ™,ÁS ½ØI"´¤¾ˆÙXò¿õ4ìœÁU/lçÞCFˆ‡¾ŽŸ ­MCpéâö-3zu½R´€½h.@†\æ‚\Ï8úq¾;WŒB)­x›w1ÉxaM.À o†SëŨ¬ÔlRRR…6åføàËŽ£û&Öûûw ä'w¤à›ê瘓…’„÷ÎSæ]£â=ÏìÊšÜïÂ02Á$%2!¡å>~<÷ÉzmŠ;ËÀ4þS¸M§›ãò¨Íæï~ÜÜ( dݨţŸà­Û _DZžÉ¡Dt=81ŒQ®|X´žô sÅ=LÐ;¥ÿŸÿ Ê4åZ5QA^:7å+ø†ÌÁÁ¬{ß3”ñ˜ ‚K¡$}&$[Ý„µ†w b/k ,æVú£Þ+j1»¬ÞÞ½+'CcÆulÌE‹(~&¸,¦ü6cUKyhµŒ-ª{Â}Ë}€Ñùúð!1íîBÔ£l8¦9 Óg¬…ŸgÀ—F€~z I›¶ÐN …akÞ^2)(§[Î…×ö¢“}(W 6§q«LŠn€ô£`n¥\ÿ5‰<±KD¿ÂSÐÑpŠ«xŸ—ÔXúãíÄÁúÄ‚$¬LÔâ\vr‡çg#ÿáo¼üÆÊ7ÿÅ:nå}e49”ÁÆ„i0Ç¿ `¹xh¯aðÄ“ÃP§å‰:U$„#EÄ{`KSJŸ`—èe´ókêÏï±gCV‚Ãó$œŸ¥HÒ7Ûû‘$ ûêFÝ—‹§øl[í¦80؉™á›èL[>z%ÙjoØY¡aceDZ12ÕæpK׆®‘×Á&½«Ða´ÜZèÕ„V½þ×g_ü[/Ç·-ΙžxýÆ5î"¿ë0cÞùú,jìrϵg1ùC/ŽTûçù½@„ç;±_˜ãëjá{ŸÒ?ü£Š9ƒ¢–˜|}Äó1fø:ºö±ñuÂDÄðÁq3š¸ÎU»ùÈó›ðàï}H)q¤F>^TóØ*Ø]£N*Îj×'D2-‚Wà/ô[½€Lrw#"ADÞ~%ÞųØAs ׃¯Â6<¿Ú[ ؤí,ú:˜ûfƒ ^Zõ†±7Àîëâ¶‹ò0=d!åZƒOƒr©wD¾±&SË%A3O ìNѼ )ŸQÑòFÌwXN®å~dlÔÞ@‡ö,âäPù÷Þýu—™ëk–Óàùä mƒÔ"ø“Ö~a9!çØSÁ)õ§·½E2c/lÈ[‹0-ŽÆ•I¸0o5»Ñô(D»v³Q žž§„k“EɈ³ ~6 €êÔë˜Ü«ÎŽe€é™|¬éšÁÕZ??V'ƒGHy¡Ìp$‹¯æÁ¼}ÓÈÊ­žôÎódbäãO²äâ96î£ÚŒØË Á)iʼòìCÉ~L|nJÄç†1e9Â0˜Ø/Y.X+%áà¯3\×§k±ûõøò9 vÛßcbÿL¡CÐT›As›‡™Jm–#´T‰Ž?~YoÿcFT^Äžôs¨ÊàÉîèþwâKs$èv^ÙǺÖ$à†´KÈ»vH(H‘KùçYÞÉ5°¥Eü“1ŸZ 1¦E€~¨ºCÌdÖ@÷ ì›w'ÿ%šµK`hÉ]¦>?¯ÝÑ!e’ûÈÀ…ÙxÀê+(.^Å´7ÝàjO¦+¤Ÿ@®G ¸‹‰£¿àå¨9õÓü'_¥“¯V±Ü„} qe5s6¹ x ƒÝ Ýtî ˆõåãì¨>pbwÁ“?’àhƶ×Y1¦µÍX<Í<ºøOû&âdn ®éÿÅ — Ä)ò;!5›«]À¬•ûˆÏÝkaö›øôÛÿÞÆŒõ2›«vÀå;êÜç•ÇÐ\;?ü¼ KÞ>A2²”¼ojÄ÷œÒ~™ć\€®=~XóÉ™0H!ˆÂÈI­XkN~82Šv²xÚ~ ¨èˆÇðíì¶Eé .^“æ¿Çά¹çBXtºÜã8°”ÉÆÜsPYk‰«J¼Àù©­“&xö°öåÀ—1gæø`M§Ù—3Îø°ëè}Ïå7Ó1}m.<•§ŽsÄ©E„,8y„³)И8 àx#Tç7»Ât”ÒÙJKÏSù—ë°ëG:´£³2þåÿ¾ý¦Ø»>ÑïÒ¤]K‚ðâ+yzï³.dÛ˯-°¯f:+³%Šz‚tƒÚGˆü«‡‚s¨ÏŠÉ˜}ÜŸ^Ÿ"sÅÈÆ¥Ä¢~üü’ &Å_qÚì8¤IDZuié)aö¨y-šÜßUD«tÐÞê Ì;æBŽó>„çñ°æž+šQH¦LÇÇ…J¤Âhñ{ÓÃŒD~ÃEUQþ?×*ƈ¤?ùx?A×¶¬Ÿ=%3P˜yi$”ÂE2 v‰½Q¦E¶×o3ŒÁÌëBTh©(ØM‡¬Ä,6fô¤kˆ2fñ’ÅpÊñÄÕHhC“ùAô¸)J6£÷I2Çd¿®S¦#ïñƒIzÎËÇñœþùóàaä]|qõå?ü»O½ÃÄÆ–oÚWT›{%qV%‚r²ìÁ*uªâ»tTœ@c+:¯˜éL3"©í£Ø¼º ÕOi‘5³›`ý‹rÌÿxŠ´~ÞHDª®qüÊÅHàÓÙ42q'q[ ^>â¡rÓÉÑ3.TLØ™Qy›ù&/@¿óàÈÒjn«ª ÷¿„#Ñ$`u2{M³”Û›ÏïÝõ˜ÅüÝpXwIÿ’FÙ1ÿú éLó÷Œœ*–$ð-ô!rµ7¡û›ÝþløGÉÒ+…ñlïÁÅ u»·qÂIØñ1|˜?•1×ûÙÇðšùÿøïiÂ߾ùoÞ1ŸðAvT‹“;EqpÖ3 ìnqÞm¼©}®Áû¡ž¾¯ûëŸ\ÄiÊx>ëMs8 2&kØã…EÀï"Bw)S‹«ñ赩œÿú;GóØO“È«ýe$@å>¸¼|`®¨LÊ݃5¢ëè§Û¨-ä@cc¯³ÚkÞ2&&³éúä8’¹Ï4„¨îygTܲ•|îáR› á°&†Áþ~UPЮ†oa¡ØjO§Þ ¦g·1$ÔÓ$‘ÉædjÓ ú­ö=cIf& 2ìT7,|ÙùOÿƒÛ]ؼÁ½\ãÚëŒx€6M‘ ©²Nä™ájdß®E¼”ˆvË ‰Æa+Pµ-¯ßâ_ºÄ!fL ]—ʛΘfvÚI‘ ß)„oÑÖå“ݾ¸Sɸüó?ê¦û€1Ïæ$gÁExTÑØ0NçH‘ç/¦Bzç9ºcu5?ßJ éüòƒx–l$óOÈãï½óÐÍ@òM'¦ÍúÀÿP›¼ µÆ×“ùé§Q%p(¸Âù©ÒÇ4‹¤@Ù;ê{Δ™àá?k˜lK9Ú™ÈO>-’"fm¾ô‘c*>7¤'%¿¢þ,#:'`ŒŸzQs/•,#¡G„ }ÄÎ;Û¹zdi‡Éd ?4Q/-[ÉZø”sŽkbD©qx|‰ñ¶N€„ÂjR)ÚYÅ?ؤö<*áôðæµ—ƒæ+â4àâLº^ÞìlóG²'ºgW¢Ñc|yèÃßY)I_xP𯨾ZˆÇ‹Wcø·A¶U¦¦l©Àt­R|E˜»û¾À1y1¤ÓD`nÁ=P ÉÁ‰Ó\T)oÅŽ‡ˆëŠ4?»š.»q OEzØ-¸ÂÎ`”È’’g˜¢šÀ,6îÆØO5¸uM*‰-å8¯Â‹9y\Ÿ·Ó@Þn#çzã½ñoâZáI‰*(W¾…þ2ƒ~d}IÛ ²$†2tÅù8jé•B²÷|ÀëG:™¡Q:ìnHËÊŽÂd9=ˆ½=™ôלÀOVIðæÇ16xûÈߺÙâtË{-R5?žЏÊX>êFµyѸÿ`&ò~ŽG¯lÊÚ7ᢟQdãe%vÏ£RÖ"ÏŽÓ;ן,ZÀ”½‰aK·Î¢µ†q؞̡Ê7^ Ši{ŒŸY´z¡JÞ›0¥ŸáÈÌåðQ™‡hnµ ¸Mü6Þ`lO¥3{rÉãt;lo¯·ö:ƒãAtQOÞ?ü³¯¨£sÓf‡Ëƶh…Pöþ*¢cÒ ‹Iã†ðmxÎ,‘rÊc ž %U?YŽ  n›¹·Ìê…sÏ0w2 äÙÉÚ'=CÅ#L™¬#œ¼ %>òs[Þüƒn› ],J”™x~÷mÎÝîi yü.Ôº ëƒS`<PhìØL]4› óŒ ÕÛJpƒÀ-ü5ÝB&íÍ¿jYßYø2°®«ÎÀ-óRØœ\Y¢tÌ›¸òÁoõÓDÞÁáPCrßx>=aü§L@$LöL ‡€ Ò)· Êóýãv×uìSeÙc%4ñY‘¸éI>¼Ü·*  iöQòçR= %GTN|tG]ú )Þ7ös%­¦3*®b´£&,Ca¹~*vÉÖÀ¥üdæ–ô{ºog}cã£>‚o¼<\¾‡—>„á)?qäEúî;ÈæË]Äù>ÑôíÍY$L|xnô€”KñÆÐ~È$Ú»Æa`%+4t† ,¾‹zaM0ÒYå¾{¥Û¢ŒÉ›Ônæ³® )Ý ³™e´¿ô,‰¥[•ÂŒu2Ľ|9~Á„ùYâG3Þ^„cZß÷¿ïÿMªZ„Ê•hXq¾ø§ÒÙ‹W‚EsèA3.Ü:ˆœÆçhæk»å\9–>éÅ·÷ýpÝž§ 1Mòª÷ˆ+é²zÝ6D¾B™ê·2ÿÕ(÷·¬×7@}k:e¯sQÐŽ™ì+IªŽÆc€¡Šú-"j[”ˆÝߘzwÕiù›chaöµ:0t`È¢}ðÄÉ î7.8óî2y”‡Fôð†£¼mߟþ·õ”8®:š‹÷ÎÚ1®Á½x}áI2ôø$ÍÚâHƒÅ•éÄ”tÒÒ jsë©‚ßÜ߈9³ÌèÂà?e]òâ&¾½ÜAläÓÓ(L§ŠExè2°õÙDû•&Ñ5ϦîZ:à~ Y[’~l]»P“pYb•Ñ'¸2°8”‡=–ôÏŸ{‡#'Ô¨÷Å@üA‡@Þv. .2çpÓŸÂ#)Oz­bNébï¼Ùƒà8äÄ.‘A­íй1šðMÊ£I[4éUo ¶méx>ïýáœM-Œ’hëªzl—Éù?›ÈŸžSÄ]áï?¶YüfØekÀ|u-Î 8Fgzüb[néSrž8bÆÎ•ne¤s®Cg1¤—ÁCÓuéJÍ&\~á*ò‰fp¿}leŽ X’®[Òò)‘¬/â!ƒ{Ž¢Ø)Êr:iÞýàÿ›}ýð\Ù>ü˜º ÞÛEß«±.IY ؈³„h½»³Ãê>Î 8G/ᎥgÐÆ1·ï>̺” 1_øŽƒHÔ³¤R‡y0ÁÚdjÐà³ é¹ÄkHâ׺Qøž¿]ü@¦Ñ8¸Ô:Ÿ$¾›J6O¦}sLðS:¿ÍAm­#~(c‚9À÷¤- Ðñ'9ñ‘fµ/Øäo‰]ó´Ä/€‰= [ ~~u'ª|WpÙhsôC0ÛÛæF&ˆ={%šk{/a+t…`±«9žò›MÖ&ö1UsÊÇt<ÎaÓùBâßûþu}lŒœ^^ƒ²?Ëé 5\'NV•Làm YHŸÖË>¶ÎO±)$h¾Q°a[¦ÿ†T§*÷ÏIzwŠ?;õ|gÉ[7:‘dϸF Ó; s™‹5¥ì1¿ETäâ(³®g5mÇ—lÆ=Còßöï,‡íÜù…™Q™ÂTzÉ@ã¦ÃètQ„Ý‘™…Â:›ÉnÚK£‹ÈwÕ"î´l8û5æj]ÅÒ_/à»ÎH\§KUxÒðSV4®æ>n¦Sé³.UzDîÞi;BBž®'Û%›¡dÿ}˜[y’¸Ü¯B%Uú±úé?þûˆ9ÒR’–mÒŽRwų܄åþ¬Aµ*YÊÐÕîA¯ùÓÝ¡kñ@™9x¨A×Hà³Ô÷ðêÆªž9Ÿ†Vž&Ì£«xqÖn’CDpùŸiôxI*ûç„ÆžDü†äØØJU’ÕŽWèL'3ÖK=ãßDÃí=1ôÎÕúµÐ4Æ’¥ ¢ÿõA“•Gý §?úësáÁ†ËÜw<¼Ä3Æÿþ-,QÈ ßÈ–¯—Q`W;-v.9ÚÌyvÓ±ÇLIÙ Lã? ƒYOvæí´½ò\Œ(ªEÓþèx¶@óï¿Ã‹¿ÁÐÒØ±H ^T‹ÓxÁwÌ`Ó4®/U^2ö¾lmþÖOŸ3CÎôñt9’¥®M %W²Ê‹ùéõ;›‰¼Ò”TvÄÓqíÕÒͰ$îûðëJ¸³\‡ÔښÚKE¬ºñUzgMnéœãÖj¾ÁŽ]Ùx&å#?öøœÂò0†ÍÊ^ë<Îë)BÔxþr²—¯—«BÓ·b|ò*ÈÄÖð=ÙµØq^‰ÕYŽ­D•ÔÞªàÖ_æøüzƒ’ÏÃqúãh³BžÜ”=ŸM‰ð’V<>ýäóúÿ-5®x÷²mœªœØú"øûuP 2ñ×Tà•º}p`¾%hÇ%¢Q@7ü²7fg5 áÄêG`ü{€åôg©“­WßÁ4ÉI$~[7îöÏÀ×f@¯»8qúxwI‘íÁÇ ù»/$~ƒq•éØqª%f)ÁÚ[™ÏWÊgÇ…ß—Ch‘ u¿ŸBëôÿ×ÿA9Ï÷ï‚MOdi½¤Yì¥IELÕá[º7ÍÕô†%«ƒ©ïßzÕÿ޵öôÞYÚ¨nCR¶?`þ?¢¾;ë÷ûŸì½÷ŠÌ”‘ÊÊý:—P²Ri a´KŠT²B22Ê*)D"•Qq¿ÎUFi¨¨¬4µµ~ÞßÇãçó÷ý÷uîsó|>¯×¹ÏuÜbrTŽÃô›i¾µ™­ø?Ù¯CïBî)Hôz!ÿ~2vr\T$à§“îÊýÆ™™ò­µá¢Õ]0ܶm¿ŠEJP¶î5\Ñø _žã´Ï“¡vv%¨ï;̬>^…ÿõ‘žñd”1;Ѳ¬ÌáJXrâû+õ+7?ú-^x²ã6Ìà< üTÛ¾Vù‡ƒ°UvÈÁŽ- (+—G‡²'üÍÿ(¬²ngŸ¨Ç5{F‘ŽØ(C¿8ÐùÓBÙèšh`ÍCLkÃ@-5FéžÆ}GX:÷ »ú¿x«Á#$šú|å~uMAÏ9b5ºšxߺÃ|Ü$K,ÌÕ^4êq º¼kbÌšî1§)¡D× ììÌ`´O à÷½¿a¿ Ö{L¢cZ„m §<ÌJ¢—ñ*Ñ@T“°5 /¼JWßŰ)SØà´<¦cPŒ´^'½Ê’WÅ”w¡RdKÓ@å%x—IÏým!kg|EŸ‡iX•)IÒ”öÁ¿m™öKJ–²ïö1/Î"ngØR†Å!÷³ì«1 V“†ê3"®JÝëþÀ?Ãj6Ò¬+¢r©«˜4}ÇnÜ'A_<ˆF¿s¸µÒšºß= ŠcâäCöInü;Z~É-² Iã´¢º£0 •XR›uÑ\»›·Ø–R9zvÕUx;éþW#=Wd2üÜ#€ÆËÉåáDmÖo¸­gŒ“‹]Hâ‘¶ëÚvm„2žØ½¦%BÏü(2zHŒ“Ï"°\vZɲ¬x25.šMFݾá&AMûŸ\9W†¤€ÇÄU­ÿ ñ0R)êë½óYÐO¥°iZ?nÌ E‹_°6ïÐå_.žR‹eº¨r«?y!程(MTH')uXò>x”¦¿·Rèx(GÕôTÉ5ñàð^ Ö‹¢²eјžlG§$Ûvþ<ôpæDþûŽ1tÕ…5aMØþ^žlêÜU‡S9;÷ªPùÞX\Ý÷ ß„Ãzb¦{ß{vNv5ôûÚ€êgý2”3øÖ!1ý3ñØâe`qëÞ«R¦ruÚ06ë \iÓÄù§ö“ö3é¡ÍkI†j+®ÈŒÍ‚‘#H;ØF&ì[ (ž2£³Þ+¡˜€ÍÝ+BOxc–õjhÖ÷Zã8.“È{¾SM…ûÀTc î¿Ë;;yÚ<{5.{µ~¤êþÙGíýè¹µ‡u~õÒ+Öaé lÜ®‡¸ä§/Y`ùCɃ\­[Ì#ýNàÞ9Hú×/ƒ£- 7_% œ|ɶõA0ª¡¼þ$ˆ' ¬“ëÊJØ@Hgm8“tFáçæz +e¡¸Lì4ÐcOëÍ¢fsåˆETój†%vœø‡Î+äÐòÁOl¼‡o>V1c=ÜÏ`Vk Ì¿´Ìúø„.P¦r¤6]µ¤Õ£`‚»Xó^².ûži«a–É꣩ñ¸^»íL6Xä‹*ÜY[ Ò[WÐÙmªôÊÊØÏç#Ò}×ñ—K*vB×O‘À®B¶ŸeÈžÃç&žÿäGÃÜiݪ|4äÀGï4Dqrö]9j¹¶ØJΫũÌd\Ffä$~if–ÖU£µ· ø6ö'À½KOÇ™¸a­!®{™Ÿã5Îßaî‚/ÌG«iW:¿Ïän\‹V®Ap'ø.}æûmáSÜM?z.¥uUo ¡ú2è,P%ïf„Âìé‘0P‰|“¥ö‰Ñykä+Ob.›Ë‘ ¥JÄ‘,YåaNûÞ°¼5nìå÷K0Èø#nûr¿*Ý.òP¾ï#LÒ¥#h·9â×ë“‚ORŸYÅ1Ǩÿoÿ…ö«o †ÔÅ›n×1}ƒáIÐbi¦+NÌÉg­|Fcû)ØÑ´‚|Xw€™Șú: £i?㽫~ΡÇõ:¡§ýþ:{â¯óR‡¬{/ãªÞœÑÇÿÕV³w«QZë ØóR°T¸œÂ¯CôÎÓ;¸õÃrxhö‰»ßŒ7\…ÀêU\Ù…Ï¿™£t‰)Á#˜ºð,ž¸Ù´º®Žv¢gGšñ!wAS¼ÈØfP «¶2„vgáz ?˜Š7²–“‰©µQIøÇJÁ˜ýŒÒBû˜·íÃ0ðÑ€^xäÌFó áz¯}X5y5ˆÔêS§Ë×ðÁIf…œlÎñ€³KޱLÒ1¶ExÌ´âðÖÆK˜Ì/ÉŒNªaŸÏÜbÑ )£}J©ðWÙÌ/×¢¦Q0=Ž:§?áÏ•D¾ùÔê­ Z|‰tv2s&i d-² Žfc”¾#­–”¦Û]$éê¶Xð1žÂºú‘¦eo0¦ÿ ßO;O·áXé8{:±þÇ/ß#v¦($ÕÉ–‡:£Ì(NŸÄ®¯{™S²jäÀlºbïίÛs§!ÐS‹5ýªB#®ðÓÞƒ Dë¤ •ah)`–">…›4rnÜI¿,f¦ T0f°«^Ô¯¶¸*i9ÐŒ•׌pO…i¼Ùŵ?È®“Û 5ùˆð™ Ê'–€QƒjÔ®Ò‚TÛLƒó0osÛæv“?4bÐKfÉÆF¼bYƒ»f2D}­!ˆ=©Ã'+Ÿ1q,倨9œ¯#‚û÷‘ááÚ]D7@‡âÙÉã8¾u±îDþßò}„se£<^ÊK˜üƒíÞaÇ1Y:¬rœ³szÌm9Ë.Š½ÊŽµÚ0³$æ@²÷<´}ZŒ‹6ÈÓ«nøžï3³ƒÕ§×öpD÷^€’ý¦X±grûûØÙ˳¸çU悌ØNÖíúF¼ùu Yg&N‡lÁÓNEÌ¢0w> 'ÿÄevÀöa ŽHÕŒ¼YÉØŒ.„­ÇÈÈ®qޤäÎõ‹W bWØZ11š8#Š­·Ã´g‰ÌI±#œÒ`pî^EÂ5NaBT<ú«ü„•ß¿ƒúÞ2VÐÏ‘¼¾ž ùÝF쿇]uŒ]¢8ÿ21ç£'cŸ ¶)“%ò¢¤ò”CØÇÇk%—óܱnˆ‹»t"ý÷;3ðt V,–ýYèâž 1çpµ^ìƧKôÙkï6Ñ ì÷q/»‡Š›« 'œƒ¦þd¯Í… Ø–‹†DYoÙLüíZËjªADõ¾øµ¸óΡbÌKVàA3šÃíÚRh¼¶ £ nãªêÝÀ?‹x7eãì˜!äd©àº{íØuß–Y¿ ƒ®#£$®À& ŠãÝôÉdMs:’èƒQ.ÁqÿÕ‚/1öìNg/•›1©±½ÀŸà€7ÿþÁÈ Òúè1So÷“yÉ›bVáN{°»ò4¶¨ÀE*Õ–Ê>ÌìÇwN •²!UÞ'¨â4U2Én3W°šÛºØCa (µî%û”l„Ö2t“{7ä-|9˜¾Uj…þˆß ã~±¾Ô‡¯¾í#lÝLXôå7³mæ>–)ãc?òÜ›0C¥ŒÝciφ‡Ò#¡Á¸h=¤¾\¦qSé\ RSÈa¶;/¥2ìY¹z´l¤+ÓK©Sï)¬;ÿ‚›Ú¦©×QjÚ.ˆºönÂ~çRø­HÉir‡ë‡°Çº[ÜèG/-âY؆ÓgTÂá9;ÐÝQæ½:ÌÎuE•%4; ˜••ïÀÉ_ íœSï|T;?Bm÷Qü鯃Üû¢$>u)ö}l¥±ìBé“8Ié˜Ô¼Â‹¯˜Â—Ÿö"¶ì u‘ëã® jƒ8 i:¥%– Wý%ž•ŒÎ­ó°tÓTòx¥*ü·Ð)XüÁŸi#a­ÁØÂBôÓÂâl „v¢y‰<[¢2ô½£‹ï=€©¡+‰þÞ÷v ™QÛ³›M‚Ï0j~{Âþ8K}\WkgR25ÙWñl$‡î|Àâ÷“EÌ;;Oš¼ÝÌÿèBùóƒl»v®z•J£ÅÅIGYGév!^0¡~7NÇ'±41I«ça=>ïÃi ìØ•«ðÉÃ]Œ6_)Üùb@ßKµÌOŒÌ)H{£A êÇãåe?žø}ä·±5¹ 8™œà*z㮃·Øó:·»®xÆÞë%Ë·ßùàcE³ ¨‡k.2¾‚^›þTâÿâÑQ ³R‚œx ;¯ô&ºgˆ`\'!îæ»A>÷.}ò‡¹o~·?±Ÿà?O-¿0qæ²d¦å[˜¢mC_< yIK˜Ã†t¾ª$è«Wc1Ïf\ä}•v=Å»Ãk©fç_8bh@ L¸ú7õ`§?±ÈwbË¿ùÍd-ÖUH‘öœ×ÀýN!xïàl¬Âƒ•9«Ûp³¡B¾Ztü5›xÜB?ˆ£³ÎAƒçyä0[SL>ç\šv˜‰ìbGî?“¸‰ì`Jf\¤ÃckpÍ[¦`êJ*~ò!¤|¸‰jÛöX,®Ê¿ÈØ¿Ý<~¯NnÔ1~€—÷l‚¥T¡y éßÒaõñÿÝwÂÿ¥FÏØ³¹Ï˜— ®pVÝñÀîÌ=ÍÿëËœý.^|‰-ù\ ÷&¦þ·ú¸ÓîS×~(x‡ú„aK±m¼®BŽû^€2¦¡Ãr!"ŸÀ|—7$ËSt©J—dSÊçL*}–ŠÅÌ4[‘&WÏðØ*KzMŸ0Æ9­0ò„Ÿu¸‚ùÙJô³°*)R,ç„îv€Ø3pÒã¸VÏ„‚óñø4\ó,㪲û»¨Ø¿ ¯Øã°ÜLmwrÍÍ9Ì]³-Õ%¤gXËNˆ²a¦BÄa…>ÑÈsžà?±ia,¿T4îµ+Æ4—èØêóò°q}b´¬®¶ÔO…ÆÐ%èâs*J#Çׇyb‘žŸ à_DÒgŠ/2\ó»àb¼j9½Cßk9¢~êœ! >èw‰Ó‡»‰Q3…¦GðVZRN fUG!i@ކê$Àü˜1îÕ'5LA6TÈÒq¦xmc¾+ÏËg¼õëª[eáåfE¢°C™<]ßËr Þ3}/áC£:¬-=†+Ÿœ׉¾àzã0*ì²`•¼ƒ$×…+ñĘ+FML S•Ü4ðmÝ„ÿÇ"3³§5€ × Šü0çƒ!½ŸjÍë”°ó³´èí[ƒp=ïöJÑD9`þ|r!#g ä¨*´Ý]C+~a²7—ÅSŠtcIF‰“cŸbA¢H”~åBfq ”wâþ™SHoœ?) Q©W‰ì¿;3ˆv×ÙMaõ›… »ç9ر™Ž¬.†WášJA²oé’ès Ä^?Dá931Ì<ÜW‚Š*øÊϑ˿ƒñ1÷sé¤ó'?9î˜ÓîÏ¡[ð‘íö$u§"µ¼_ƒ²¤ñ›¾Œ$mïÂuûýÁ|é¥ û%â™ncAF®Xœ¨}u¥õ…sh+GúmæÓéŒ6¤ìâEF¨T w‚=ÙinGßD«Œ™$Bîû…ŸC¦÷jÒR›BêÜíz –âÖZÎÔ›Ûèî=èíRÁz²IøÔì1ÖN¡¦¹wXS«!†)®!-èÏbíº¾Üe:§åÃ2…¿(cŸ*þq-üßcÔ®Àí\{Iö|›?o* J60×ЊԶºàýàjøûöʵÐpwÅ1x2]j¹z´zêi|õú~_"Ê8¥b¯X;"tÚîm|,N¶ ¡ik8öÿz`ËnÛxÛìD©Úavý¯ýÔÝÑ€¦4 ш£“鬠x;[‹¸_ èxcÖ«êÑèeôAÊ'f ‡â‹}Ñ´Q!§¬}̼:ßCqPð^™ÜÚÏÀO±Ûø Hˆ®ÙaKom'Û'éÑóß?SSñÜ'gt˜.u²dÇò«Ø¯-òÔ &œ|Œ‰¯ªÉeÖ+ü4ý[ù–-»ÞÚ¹w'Éô–ß7Ëq(:ˆ€Œ,„¯F3íó0ÊgHÎ^F¿ÞÑ#U–»ÉûÌHº ÎÎ9:ŽÉÝÄßNòOí$ÍZún‚ÿn«L€°/½ç@wð‰á¸}µõ2ü_¿å‡;páÜ~duŽ2šá|TÌ’‡5Ü¥Dª·‘’£0m6ÝÔ =OÅéÕ9ª´À©o¢²fFäi’!ŽmåÅ]V•ô»ë\•º ×_+†œ1o"´¶„Yø]ìêøg£ç¸-Fî¤Ú¢ ¤Ñ–¬í¬žjYüî8k[ŸG¿Wà·®5†ô¨µùG!@¼š¶–ÙÌSˆ#hvi4Õ(ŸÏªî\OÊÊÆPt×Qj©Lʪ¥É†Üç …;iÙÏçpTç;'[èÂlç ü‹˜qM1õ!Pó7–o f‡ñ˜Ð\DŸú(н«0ø²İŠC0yu0­ýÏ×ý„®ñXÚò«­K˜ƒæ}øŸ.wv\çäïQ¢'ý}èOM öù /ÉÝ*ý_Oh<5m½Lþ`s€þLQ‚ ¢©tWB6-ÃM²Uh¯Ãü÷^¡Ç,?л žÀÉ#]ì94%«Ê¯0V#žäDÉÈû§H÷ЇQ&{üÀõ+àƒZÓiσâ¿ôèñÞ2¦ˆo'Ù|T’NGªjzù'¬3¥ –r4¹i54~Ë‚€àú¸¥O˜ Ç¢†@S§H»ZP«HZBï÷kbp =½0É â $w¾Y¢Þ/Ìú·-HîØBôåÿ=ÿé9% ^’šŸËø7ƒiQnÝjF#ÖçÃ-EÚÕàLm›£áøŸ—ìþübÈ+ta+kð÷ß98;Y'W|aÔêÆ±ì´ ,Øõ†äÝ R+9 `g£©5}b?À¢Í]+Á »ßŒ§Ññ%¿782/ÝÁ×)W øs|º6›Éq ƒ±vDrþfUfÌÝéÏü8[ÍÆÞÉg›æ™‘3ó>3­¿õ@F$¦Åî§Ý>8¬­NÓDŒs¦ÉdÃ#("NÌ^  ¥}øV²|ó`Fy »Ûk€Í\CÿDÈOØÿªÖ Ÿ–ƒšÛ²¤zI#£¡YÃjQç¡ÆMÇçR ¶üÒ¢Ÿ- è£nÔV~9S°ö g¿{#¥gÐv‰*ù¢)CL³ñoNî8÷>ÌÝoâë³LHúâϸ¸î.ß»Œ¶7O¦Ý›ß0·.õÏYÁ:0£2t›]Ù-¶‘L[ OvÔÏbNþ< ×LÍ%Î$§rÞ½fG«†°í! ÑvÁ.œvy OŠá¸°OªœéÅå“éžÄ¶äOHý’Fé H¥ÌÑísäE9¾ÊשBvl£ó„ ?Vž èÏSeS'Ö¿ß ½à”æ÷³Ç ðÐì&ÔV| Ý ÷paÚØ´¢*#Š lô$ÙAß¡îâ,4ÎOFâñ‹õ]V§ï0hÐ%¾D¾æÕbÈU{Î íSìÃ?f¤ãš%g‹ä$¢²á&ªöí¡!ÛœivÔ°¼ÁGx½tÈnUh‹ÜDcO%GŽÀ“¯e`þû gñytÿ¿u³;´%éó278rú0êØÎdöm~Ì*|«þI)´'q˜oR„Ú–kÑä•œÒ …¬¤*Öm0™áéyÉ$&Dz¤é$ûHvŽÍʶ3З üœb¨çSÀð¹]þ×à›D Ο×;Q­µšÕ^y”Ho †¦¤GØ6(„¶Ytá·ÈîoâºV\ÅêM:´ÒLúçÈ)ZbœÒtã°)áý!F‰ú’é…ǰªwåóãhˇb”Œ‡ïçà#A9ç L]{˜‰Sކk5&Å.àÔ¿;ɉYË`ö^U20¦Ljž³`Ôý’Ž…}d/P…Kþ tåS$[æ%ý *"/A¶ú\Á…íç Ð#êøÈwiC4\5•ùáD®‰DcÌí-x¯×‹<öîB»mˆjìdM¬á¾b,ƒ5dËÚ²qíY<‘ÿ÷þÉÁ´„·´˜ ~ãg_ì k´v)ûè—0yúóìÑÙ‚ìn¼*˜oõGÐnÎL8Wü+k9•W±w_H¯XDõ.Ìf6N¡¾Á ã¾ã&§©¥û» Àey?;÷ÇæA$ØküÃà»{èŒf/8–Ÿ…Æ–Óèþ™ÄñÆ[x š†ïx¾€Ûú]Ì5Ö^ÿ‘ƒ›3áΜŒzä9\™ÇÇ®¶ £Û'áCOÚsØ›­ýäG{…Bh• ñˆ[ÆÚn¯Æó ·`dÑUø¯¦{Å–B¸ù2Œ¸ü$g= !Údúéãø_üÉ ÚvNbf ö±w7Ð2G dñ¹Úø× oŒK¸ 埀[h¼  Ï3оÁ ú«ú`º3aD ›8ö Žá–¬Oãó)cbÞÃ…Z’;W¡hV!šÈ­¢i‚ôüö@H ýÁÒîQ¨ Gw‹Y¥÷aíŽ Ü·%œ‘íc÷IqbfJ’°©Ðü: =˜Dr÷ó#[H¢ÒËY•éGèfAw6,]_뀆§®ô»g2f¿xÇ,’]À94£Ÿ¹îS¶wgÑC[Aƒ97ï<6m>õN;ÄȬ  Kƒ28ÿßþOß'ÑïZƨ9*Î,Í{L O™Pº§µÊÁl•^͘…+¨J¾ŸMÞKT7ìfóí»àüÙüÕHÛR]€;³ÅT“Þ|vŒÕ¶Iƒj™Ípi­>ÚêçbÛ…'˜™ÙÌ„zy‡ë[Ðö·<Ùî9ˆS‰þì·&G7AUa ÄHìÆ¯8Ü&ž¿‡k¨êâaH^ц›ùthФ~¶ëÙ!š¬Œ²;^`çŽZíUç}OïσmåŠtþP5‘™äBþ”\±Uô‹fzúÕ¯YR²r'fäÎCûúX\°cbþëª*SQL”K¤VámôrDMz—òóˆ›ô[™üœ]O­šBÈL9i0¹VI‰­!•¼Bänýsò6mž£@Ÿ^YC®G½"WTˆ€¨þV[BEHÁ2:k.CßõÁ·aœy, ÇuiÔ•\¦J>þéÅПâšDrZ„Çè¡§bÒDþJ×ùK`ûúô@Ö)šÅѧþÓׂ+O%ѳH‡#1™@ÆúQ·iYÅG~‘\¸¼ªãn†ãØ\†M>€,äAœ=qoŽÛ©äÚr ¾ªœÝ?ÉHëaijñ% ÿ^Ö2WxÉý¢§dü5Yc"E{K“`‹Tô¿¯1ÜØ½*TŽÿ ç§Óõ1˜é˜ŠÉLûè|´{ß‚©ÁKibú\\Uxçåh’Ê©Îte_ u|ÎO¿ÛE,z•IñeGRkf@ݴЖßäKËbÂ}µ‘þ~´’ìÿVJÞÚ®Ÿ°_÷t T.¦fO ‘¿k]U3›hæo")ûUOU'©ÖvtƒýÁhuA,çÛ…¹btëÓ°fAŒ|» Š(BÔ„·á}ž/×K~BDTÍçH°’çäÈ™Óݲeòÿýežgb_¨Î22úñ´fÍžGÒáºÔÎì8:¡K>)Q±3Xë¶Ýúõ;N—#RB´ã÷æÚ±"/_Cßž\ÁÜ?QÂÔ®Kaf…×a{Ž1òKb† ȇ¿ë6Rštlf•5̉•axržÁÛ¢hø, C†ÞLèßqÍ‚ÉÕ俺afAªŒ-{€2Es0«]žî– §©Í 9?¼¾BAc§Ýì,UzVB†îÏ`îWªcäéƒäå  1ñHDŸŠ'ðøA;Ú¤ëâ¬'ägGiÈ€íÓ3ìŸ[Úôóˆ¥H/­„3Cž ?v¯Ì2¤ë§«Í1pØØ_âàãÕ×`Svt|þàãšÏ°::…[4OŠI,JÃåqÉD¤e?Ór;óQFã´ÊšAò¦xøå¬Õ£òtúv_¨|šŠ…Î’l WŽ|3™…û5jPíÀq+ˆ£šI©Øvðõß“þMÆsßYÁE›qá¾7LÇÐX¿õ4øfH‚íùÈîw‚¤É‘|kz$qœK-¡yö·X³T#»†Ütí!6ødÐçcØ¢çb4õú!ü•ûMý’ØÜ!4+Ë£ïÖàVI*[^ Ç릫†lVIټ—ò¶èò—þ|+@˳£I±’39³÷.ÝSŒ¶®~ä ìnȘDÂSEi¢þü%»D„—¸øO&·O\€Ë­6¤»ŠÂÓ%ÌM¥vV@Ч(‘tžrXz? B øÚ °íM®[ʬv"/¾ÉÃùGMàøÐ ~ÝxÌÉŸå QzûñmE%¸ìœD¯‘‚6å§Ø{>…V|SÈPŽ4-Yþªc^ã+-}Fâ”;0š+Eç­ÚOÔT9ä×øI`»cu Jm¢Ê/%qAò:nÿÆsX°|€ <‡»æbI€]¸[ê7n&ÓŸ_„7ßK€]öפL&rô˜aÑ™pÄ–òñcÊ@|8žMmŸ]@¸M¦}²§or$ ê@¶©ãÛ~AÎð})ðæÏ†š™^˜ OêæÏ'"Žªô}V9ì[(†Ã+`a0ñäüoÿ§Çc°ñzüŸÛÓöåp[$SÉ´SôâǵTèÑ ø¦èóBIÃ%ªÿ&Ÿã. tfîb’|5¼"2èÉ‹‰Áب¼•‘¸*Êö©Ô13äe˜ä²étáÚõ4Þ”‡ÞM‰ƒø¯k9¸ùý ÒfbWgÎßE½ÛQÕ[Žf<| —¶(ÒþGëiíN'Ònº ì8‚Ì觤÷B:Ž$_ÿÂHº~5ÅV0&w“]Ðêq×®.ŽÏŸzÑ㪪ö“‹ŽÀõО]¢$óûÙò™ y|TÆKš¼W—cø§?ƒëâ?‰ª«¯%kî‹Òw¢ªdðV ·6äx˜Οe.æQöÑsæHM+S¾ Bf ã ÃD4ûîÁUtQ¤S×>ƒ¾Mû1Qú8Ã.™6*BÉO:ã…?c´8”Ù²q üÙU21ÿç\¡-ÑÜ}çtÉËöÚ[n` ±ŸÚ ªÝþhô/Œv>låü×#zéÏ\Ì>µ•Þ“¢{JÉ•3»Qjícx<À´I”ÁÝ º béód%RøÝ˜þÞ€sÖ©ÑU¿¾¶fÖbB³ ±×þy£f¸&.ü|ré<»§ø}½ yFÒž;P­›ü´CYšø),boÇ죺ƩXs8œÚkO¡v092rmÝ™¦t¨0}fº´¹äõ`,Vï±#7Ï–°˜pÉq!Æ¯Š§^óèGÝÍäÑ])®Û'Gò¶yãþ¹Æ+Eƒé'lo¡Öfóð÷h*IÕ“eõm—“~¹·x!ƒÂK¥ƒÌŠè*üÍ@5€uÔ‡«ß ÆÛ–µ%iôå/Mø*§ßüh]¾$½’o>áíýÂÌ—+¦½a_ŸEéL L¨ÂÚÆôEÏ âÑ/£Eˆb¾Õ0OÙ’JèÁ6ÈÇ–ïìk9i6ýãAxëå@*Vã7œ²EÈù’´"~ô<ý™ ‹¸hǾƒ[1gzM ½í,[=`B %þ¡ï§ÐPR|u²,ÿàOXlÂ“Û &üÿdØ•½ê[Œ5;þâƒ(!¼/q6ôðàúÍôþ{b`JMåÖâÑòPäù›çú<·HУ·ãL4–úÄÃî‘qLYâ<~~<ì0éeÝ­£az},“qïЧ"2e·Ayª.KÚø_ý34ñ oG!×ð:™ýÒ³d0)ö,ªœø:+þ€îl9ŠQÔìŒâøz²ya£5 @õh<‹RÉ_’Ðöò ³n¨‘ùê»Ù–s¯¹'Köÿ$ ÁKߌþ…|hMzÃØlëfOŽ4°¶ö²4Eð^Miµãíå&ArãÉe†_{/{J¼\k´pÛ»«8iŠÝ:ÀB´–[ÙñþÃ'ò4Q='D½•¶Žë¨ ]‚ZÌóA¡Ç~âֹљ_û@ùïTâ’ žß¡÷¿ÿkéÙ‰ŸµÔÃôAVÀ§„Ýú¡ ¼ ȹõw1l“q~Õ‰lÖR?Åê®bÈžYL¼ˆ#ÓÇ£ãèh©­cBÖPn› #Çc »—8{•Ãg™©dÌ÷#›ôî tŸflOêÑÀsk¨ŒÉWPìW%¯Áª&*E¤6y°æÁaÎüA/÷q œß±-Ù =X—·‚_Ý!ùLÉŒ) 99 Ç®€Í >œ²-¾k•3ŠÇXMŸŸÜ›í¢$ñ÷+¶±X‚©õšDÎoÁÑFK¦‡» JWØâ«S0N™™¨ÿÔóÒâÔ'äqËDÉÖŽ¯¸03’½­~˜[ˆ96ƒ©Çá ñc–6õ3Û-åÈT¹rt7ïffm€¿ oî«°G*—ÀÂBUÒ9¯q!ÏY¹^€É¢å(©]ή§™=sxHý?EG7pvƼðr4pîâì,8Ά,,½‘f 1Éd"Ô¯BÑûPx³ˆÒ›¸wK—Æk`ú£›(3!¹ò‚Ôî.ìb?öldeMMqWõ±¤ègÏéZ«Ž=–~vQüÙL¯[ûM“‹“Šl0)I—˜í>ÆÙ%ÃZ/¼1a×ê»ÿ»ÜÔŠÀÚõèÖ2®MÉ6©†Ès§1¤"¿ÄÀðÒ,œ¬B?˜Î„ÌòÅdõeq"U¥JNÞŸÇn)Õ'·Æt‰£%ì*nÇ?:4èÄÐ8Þˆ£mjnÝtœ¯/¤?v©B»j;„>» %ÉÙ`vˆ²ûĺÐyq~ˆ?½ts5‘’À†"èòZ–˜ ±:Bê´úõVˆú䢾B⥪—½É³ãÜÁ÷Ò˜þæ->Ša¢ƒ<9Sº8¨t5 …ã3é•‘¦4ƒ4Ý÷'ÙI5pôg1ŒÓL¨øM÷ üמ,LSž´² O¸ïÜã~V.c¢»Õ¨å8¶å:ΗKƒm¢4kTœl÷N€B”2sH¼õ ô]<€*æÎØsSƒt>Z ßNMǼَLM”7¨&CsE±»µœç·=Ìß‚\¸ yð@îÜ:o×lÇWý‘¸åþ2P¯âŒ¿†IM¿„îíq¥˜ý²Ë OÝt¤ÁÂÓÈ÷QzÅuÕ‘jô½£K’[bhÞ3òycHíËF÷n(šÏGÏü~€ìÜÍxxtiÓü“ó1sE83ÎKƒìciæî:Œ]] ¡3Źÿß~­›ƒL¢˜õ8)ŽÛ>0ÿ^º‘éÈ3ý 6ôã5fMp¬œÒí¾ÿP·v}ù4úëÆ81V²tùH?¼éÔ&*ïõÀâî!Üîdåv9s–8Ñ}A™8¶MȨÙ®3"ª ˜´þTÛs#Ocqc¦Ý&IhÚ[6Ο¾Þf¯ÌfÞf¤aÉ—@òéo4$Eg²†±†ÄRDZ–™Ïý+Áâ]5¨nð8Œi)6,ÿáY´dÎt¹Ö®Ó’ع/„èÁRI˜Ë§ù¾„'B—š˜Ö³aÌ$ªÕŒ’.qÝ“>Ã*ZÁÃ1ÜžŠÓ¬Säƒñïÿ~œy?!¹ãëm.@œãõð—ƒH» …ú“îy…‘+ÞbNÛ›KÐŽû b¬bI·ï7XQ–ÇòŸ/£û@™ym÷k³nv$Q™Öpñ¤}ºYÎuۋÑæ¡ÛœHuä!V[ÈÕˆZ€e‰¸ñ)/~Xä‚ç§§á-çwð_j6ñëÔ8†a‘¡÷És |ŒíVºÃ‰IÆý¿TP´³ ÑØˆî[v€m?þœQºj Û‡/ 8‰[ %¦¢±Ôq¬P—£Ü3S'ø¿óÒl?Õ=ÈŠžJfÝV¬¤[‡ÛðñöÌ]7nÓPܵաæfSè.áLîÆB]ä±;eº¸ÙÓ=aæ3_ØocMíjƒIˆâCfÖ¡/Œî¦ýý¸›¹ÞíÝX¸»®æ7žÜ!CVzÜ`NôU2«õ&“. 1¶U„T¿1çväþ’û ´f¥Båé9Ü;ù•8æÆÿ¿ý¯ÊeH³Çk64¸ …l%9K=»à[UÈ<ÈßV”7Ù„ïIdÎië8,x=®9-d„z2°D; Î5¦_­ä©ÐckÚ÷`üz8ËÎK_~k¨Ãß3Ю¸“¦ÇëS×ÔJ¬ôQ¤Ã¬f¿`}šÄéªâRj;ç9›¶ò»2ù¼ºlˆÇÇ”ÍÜðÙc®U¼ýcô×ø¸ÐüÅ$XKLžÃ€ˆ}.<(vÀ'ëuŸñP:S×Åo²n3WƒõÉ4y„»kÙÑܵ¾UôIÇ|úž}§Øß‹&ð_BÇ{®'Cßt ^ÃðÏ¢ŒÚ|˜j)3KË)G!º zvÀÅ xÙ„_{ æäj\½æ®^EÇž9ÓÓzÐpœóµUŸa<]…Èó•ÆD9ݵi•&r# ™O7¿Â™³SØasc˜»s-¼8ͧÕDÙÞ¡8.HiÑ7ºbÔvùyÆ9F*‡à6%uVwvsþÕ Û\`ň¿4hq ­0Æ®¾É¹Ó©IK* NòåÇñöE½[µs”×à >EüQ^ }HäXPßp…)›®{`r‚&ý¯Ÿõÿ·ÿ—A­á<ئmCÅr Èðäõ aŠ%)údtYý•„ç³ikihTØä¸ü+k~â ¼o·¢ë.oåU~ìÈrÔñ_ñǑu?ñH‰2 ©€¢z(µÔЏ&¿Ý—™ygþáÔ»¦ä±öL¸,kO<ïäCÚ»í¸…ÜM6Bƒ¢û¨¡ô ?M"Á¡{as2˽:YŒÜá'³R²5kwƒÏ57Ð^‰­õû>Š ß±u^&þ0Ü~–TÙ@£‰›a¯…ZPÞþ+|Õ:Âò4£û¶€âqW´N— BË.£ÙîªéüÏã/ë´ü +Ô¹öÀç»jà¼Ñ_æþF?Ú~ÚbD±*iž?¹ ̯´ÂçW÷YÙEzh¬_Šý ö4«c®uìc½ŒÅÈéáÌ­ùï#ZáàÜV|ëÿ.ß9[}y ÄãiµiTñß*ö}lœŸA­ÉìêtG”**캀‚F¯Ùì›lq‚j–Nx×|aC†¦Ý“ùé´KÏ@ñÉ&Tô>·í°39WA µÐ1®!šó’ýy`–r“þÍäMâ0Î&»¯ˆÒØÄ#°ÃBú¯ÁÙc3‰©ïDþãW[άæë6àû7Q²é9g-¨ýg@fl eøï1™šä鿇S2 Zêà”’¤@{GX»Â³d$¢„ßrè.YaȼåN½Ïo¢ƒ’xÈëð¼YMgo-à\|9‚Ó‹Ç Ýí†*oaz;»ÀðÉÜ©¸ä_Ììjˆ‡L×lü®~ÛŽw©:bC|úÛqaýYFfå]Ìhž¹g7ÒÙ‹ß!V»0q=÷t¥Q¢3J¯€¦®"Hf ’Swaß[»ݚx9“œðíÄÜð;k3Î÷×\ý_ÿ “-lŸ«ßÇ,Œ:‹w¦2†ùS¨¨¦«å|ߎ¦1‚ߊ¡±ç“à7‰´[µCwZ æ? Aª¿}”Ýþá+ cb~¨™ûšùY†ìEX€™$Žsúð™3Æ^¿É‹KWMÇשKˆ¶‹á‘ÛEN®›‡—×Bþ 8qϳdÖ·¥ÂuéléÚø ¾†­cjaD+ ÞzW€ðÓq]q׉©+Êg  ¡û?N¯I5ŒÜP ûäí üVÜS9Ëì•Ib›n¦€àêjÔì?Ï®êšL¦¥6á5N(=Ósê…Þ°ïK¦MÄÿ¶¿Ù¸&nÜ‹¨æj „&B¿„žeÎ,> ýk…è¼_Y—WçáÙÆ#¸åW"ù˜#EGgéR¡§ƒØóŒ ­û„VÓص®/ ù65„6g% ò»£nÜÓ>Û‘ÞšÔXu©µMPûÙ•L­Çüû/q~Ÿu怳„ ¹ yÿë=waÆ'ØòK˜Šæ ò–ÍPÝËGËR›˜+­;‰„ž49©"D„§ªRõd-Ú¨eGÍþãÝ¥Ô°VžD<ïå|ºŠï®¥IG,þo|gèU¦ëe£n*2’ÏÁ‚äçöç÷„ã‘a†ù~ëFÃðâ‰@Æ×<„Ù²ø9+1ÌïÙúd^g Ý{«NEW²w|'¹²dg+c´–Öükbn-Θ$;d?•͆Að_½²r”0>ž5žŸÅ§³Ÿ–Ć’¬Á1iذk=®g·×^é¤ÂT•m)ýwæ´MŪéG‰¢\>Ž}«©„÷8åÊŽM‡ìÉ iÆEL¨³&T—Y÷L´1¼Èíö¦/­b¹Q”™*ý)ö¼F«BÉ¢ )t¤ªœ†dqˆìóf||€—<,KDŸÞ‡Ï¿æë¡ëœ}(”I7;y§x€²ß”à\:CæËœc‹oÛ“½']é•sk(³sYôb}·Š%œp>³~/¾KCÁÙlX4¤IECÈNÍå°R.u,Ò£]a‘Ümko‘•©ƒôªX9ýý^™Îw§ñ"æØøÎJº-h†Øgzrò&Ù÷´büû• ¿ò$9ÿ»ÔMzm2YÕ‡5ìó7ÓptÁlÎï T,ª;ÒÃÖÒjósÅ“<¤¼»Ž æ€Nž ³wdÞ¿;ÏϨBòc\f‰ílÚþRàmá`ðópV©ð &d×4¦$&ØÉúäcÈ˳Ü|+ð¯Å$8š—F:;Š¡ÍEÆîÿÛÿ{• Èhðcáþ>¼P$Ïœ¸ ‚“”ï°Ië9sK¿AS„»äv- U¨Ò›ûÇ`—(÷×ÊÔ1q ¼]ȵÕ(?§„ó1ô)ºFœd‹<[`Rí#غF‚Ú>‹c£wŸå®uÍev?1ãÚt¼$[‡ÏúÆìŽŸ|×ëzáHéMpóvBŸN&$ðÔ«ÀÍ·’ôIÄC¨ÕË„±­y¸M×ø"S!êÏÚ¿=‡NWƒÙ%Q2àyó…fÒß#ó IßoÆãÔë{èð ê ßcæ“ð^>ú²”f-^Iþ)ëÀíä« `rÿ°¿Ùl´¸QŽ—4«3ÃÈŸI[qý™øýòB²VV—4¶šgÃÀ—.×z í8Ì.׺ˆ¬÷-æ›0C§¾SÄöÊ[pCp&F=4;w1¿ž]<ã3Æ{ n™Ñ3kD©s¨Î8ÎW‘o“ÒaÃ-vxäàŽBTô‚Ñ»Áô‰é`Ü<èÁÁ%D‚ŸBÞ²ãt›ûMTëZ‚O¬oÀÖò»Çó|¼¨ãçÕ}Í‹XˆR–+’ƒVÓJÑ»c=©¹ ±Æ)ä’üRØimJ«¢•¨w©#ÜÏû éVµ$ö‚ w«•ìDþËŽæ<›y¦;Ï—òz&g’ ºÊÌûMÇ@im!Xg£Ì"{:g\Hôìá¡vJý¹™äõñ…êÑï)±b?Áï ×Y[é%ì‡'Þ`uº ›^Ãp­;,U²ÄW3yˆènÆ›£’dÚ6yTÌNþ`J´âQ8s€­Î¥;’=‰Å¦Ë¬\»9.²ù{³èæOêõF-žÿaµŸžXÑ ±stqmC5|¾2™¾70¤¢5c˜ÜAùœ^cý©élÈQ6 :š›Ï¹ ©ƒáîæE´ÊN‡]¼Q\XɪõtNØ_U-MCíjáàm)æžÇRÚ•²”(}û§¤—ÓöÍZdz];»¸{ NL$u¦cÍÆÍ×$Dft཈ü‰.räÐö¤ŒmÜpæôz.]¶íµÌhù­CvrÑ»(ï§v·K5|æÅ çaøÍs¶ˆÿ¿új…WRd†›6ó_ÝôÖÛK‰5µæÎ¨ï—Ô²ûòUö}|3£‘•Ö„ÁIŒf¶ÔÛÌ<~ÀK­Ÿ†¾yŠDgà„OÙ„-Æ]uÐþÚj™cMÞGb.ÍBhäF²g¾ïð#îû4©°ŒêÿYk~ Çä³pSÐRhÖVrBM!€o!½›`z@;{÷£4ˆ¤_ÆáÔÇøùófÍÔÇ`C”m|éë Prʨu`§÷O¼þôgæŽÐÛI„%³ŽÑuTCTƒ¼låa‹S !QE„X-’£rzì€ç,›ú_ßæ9æÅ;©Ó=SìVvbãjÈœ$E"üÛ}£iŘ ˜É2ùB-ôiëzVû’+dO;­ÿ—ùžqNh]Î…O"2põïxõh åèËá1qòg³$TúhѶneü4$E-Îî¢w„–Näÿ#å¶°ù¬2~ ÌÀË›ÆñéF MìGó± ²xM9¸~eÚ…è‰Æ§xQs…Ûâì,ÿD“èeâÀç¯1¦þælH„²EÚáß}Fî`Û\õMÄŸ‡}>y?ó|joBãÒ™¸ì½ ±[^Œf¤¡Ôˆ\ìIA)«rfžÈ&šiQGìS2ç¼Ü±€ÖÜ:[;ÏrJŒSØU#“Y‘±‹Ì9×j¢úÌ—¬äe½gô˜óúp4ø_«‘3? 0ˆ=Gu¿g]õ˜ù½%À3àN‡gPÕšBP'k؃ÿȉõïëæ¬Ýya<Éh“­#ËIáÉùôì›Ío•Š-<Ëeo&²š+…‰ÇX3p= Rmß3ðf-­ ˜KŸkK`÷'kŒUi‡ý1ôÕÑkÌù”ÓØ3Àú¾Óg’ò<ÿ¯nÚ'Ñf¾ãƸ‹?¦œ‡ˆ±(?|Jô¦ Üý Ã?Í1´S˜„ô42W*ü0q¡fxÝãlêÈaOÊÐ “ƒ _ÍæÑÙ#ÿ‡kn™Dÿj ÇIFÐnÅUwâZòÒRÀé;“ë•qšÑQ!¿Ã;Þt(ô¢‹ çL¬ÿš¿FG«oÌá“A±³fJ’>Q)ª.“ ‰·X—?«AÉÒ8s&Ó°ˆÃðŠc‹a~ tød.äÝ`èÜ$gRâÌÄ­5aμÙ4޽ÛIÖØJÚ)®Owg¥ƒLM?T¯Ï½°F‹.'¼Þ3“­hÖÒ@ðx‡óîÇÓ°#RDQõ ò–µcƒÕBÎ2­—lÓtaNÊØ}®,ý ;åÖáÓÊ)Ô@ü»rŽ"g‘l«Ý°¨:µ·‹£Û“ ZÕw ¨>…¿8R CM²á/?°»ÐéZð “„6~§Õìõð<ØX¡GB¤ú'òßç« P6ˆÕLûÙé!n4bA}J µà” €žJšóÓÚýï÷䨬ýØsÀˆ¬ Ôwó<([®FZˆQg?¦îÄv0,]Ä„uóBåƒDÞeÛîè‘ÔäÓ`ÖÄÀë4Kúsg³Ü] 7¸Eþòx8;8•Þœ4î{‡­Ø§£O$$=iÈæo˜Íh*¿(Í:ȸ¤‹Ìå$E)ÑuE*]ñÃŽiÒ¦ÕÉ í:̾¸²”Ìq,Açi.L˵y‡Ê˜þò;øéǽq¿ÞG•åñdyeŒVÇ(÷?Ü„â‹ìW=“‰ù?³ aý[kF"šÐÆk<”{Õ‰t]8CGÚ`~+—ÇS›^<:DŸÇ%¡Âzò0×Þ/zFª{ZÐÜ/‡¼®J-½mÉ]­ÕÄâÁ%w&‘•ž“_mÿW{,=6BL:\ðò`,÷A´Mµ½O,ýs¨Ù¥)ôÚebçcF?Î÷¢g… ¹k:Óèßïžd×õ{8©™—&¿ÿÓêÂÈÞ; ÌýA!X’<»U@öN£¾¤Êì7òoV }ë `‘úq"rÕœæéÆÎ_|Ôë=Pþ´xxï°ˆ>ï?—Ä„`u¸}¡ó?þû/ú<âøÒ@Gqcÿå7¨‘;tX*fDäf‘51ˆéº ]io¡ÜÓ\ÊÅüøfŠÚ0û)ؘstÐÿ™1ÕOg‰w¬ Ý—–÷Sö`æãvºv_2y¼ê,pÃÈñævLL¿Æžyx2.L#­"NÄç@Ö8÷A#©x:Ãz–Õ"±f%0¯v¬ÝdAÍäOÀæºH´šR‰Léj*éä‡P>-¸ü•ô#‰J]lZ¨ÉqÙ{“Õah}†yÙ É\ZxÏŽ~G“ØýôTZ(ˆÎ®Âƒ‚D$bëÿžÿòŠÓ‹Ñ…8ie =·3•½¹ä×Òç2ÆxóÕâ[Î<+òØ_Àã•-ZZŒÖ[ɿ̸ag 6ã=d÷¼Ãu¡¨»ø^,È‚¤¨e¤6Þ™¿ëàÚqE² ¾ˆãw:\jžE%†æ’=’¶ðxÄœ$ÞcѹQ}­9žY*+M†ê^HåèËA+ô³¿>…ד¤P³ó.Û—tj›aÖðf¬9ÒÅÌú‰ƒ¹~äÀ”óìyуàP›Ç5§aD(X9ü…™©p+UÖÄoƒty¨Õ‰K62íÞOðßGñƒš^Ÿd#xU>Çù;ðw{ˆÔŽÀ³kÉpÿZ#âkA }6röµ·7 ¬Üö똃‚b4i äTkÃ&q+j#Q‡/]ðªyZy“E‘æ˜Ó¤CΨaÅmœÈ:ÏF@ÚO7-? ½ã9äYætª×‘…iögÉÈ‹vx2?/4 3Íz¿1Ëë„‚­§šx"¢”lp?CêbůþçúW$ànÏ£¤ný´õñæ¤O:ÙúWÿÄáüøi@ׯJÓgÏæ²-?'ò_ºr ª}èU^’ªGÅ7}:ZM ›%ð^Q8½òþœ»–ÍÖ 1dÃJszïd"<³s£k”áEÎr:l9™ÌBj{x9UÖˆi‰èñ©¼ë螆UÔiú5&Û׿𠡴V×+Ú=·‚râ´_™åŒ*ÍYò°çh“5) ¬qª%)#7¹¿­Œé§£¦De· ¬sBòÖt¾¯†ƒÝ‘àøwœwKåz}U&½e ܰ+iÄzà ÷AdÄ|}‡ênŠ\ߟ©¸aÕ° ‚¥n‡iÕëA¶oÅWLû×Ùøÿí Â³Ò xbF-(ýÌ¨Ýø€»­×1z;¯cäh,þ\ãŒ—âÆµ¦|½UÈV›:ãæ.oâ«ÑÉ^S™MÛ ºlr朇3™…ìVÕ8’Æäý›L?”®Å€Å+hB+°mæj|³ã>ÙžåáOÀ&J’ŽøÕ‚¦áM,V1@ùGIÞì ¸ß tò"e’æbG³öì‚Ü?Öpç1³aþdä\>GÏÌu&ûÆhUÉd¸Î»,È ›¨:z" ²¾Š.ü§ŒÊRûY¾ëSèNY:µô 8úòÑE¼<ÔÚXu¶Mä¿§ÇÓØ»ç”è¼çy¸>ò%8¿Ì²S¯€««6Ãî%D|–0´¹ºÃõ÷e ,jCôÕ‰Ù} °ÜȤ®&[L‚ðë¤C`¶hZø “l]ÈÚ» [o.§ÿ^md Wa€´;sÕj!ë¯(A®6¤ójLH®Ôº·ã4¤éw©­äÔ_HàŠ’Jñb˜ã,Â4¼f—,<ÉUFãõkSဤ‰¸Öܘ?‹žú­oK£Öœ-ÃzmýÊfûM#*.-à\{ ¯ü…Ù¶ép+¯“IèWãËre÷ˆPsãÙ„­FÇÄOÔ›LMdjos»? À³óÓ ä¥0]ë¬ 7ÖHùö&üêÂâÃW½p,Kœjbè úNĈýêtw6ÜÚvƪ`·¢Ê¸FƒýOÒwîF´K¡ƒß&à SbÖÑ9/˜3S'ƒônú~ènœqþ_™¢÷ûpSˆ/N[u goÂD®ÎQÚ £KÐló¼ÒšŽù}Ì@oôÎE/²T;ÚŸ JÜn9¸^z!–ô‹“g÷:Ùª³s±½A‘Þ.áö–<: %BóªX*ðÕ—]qãÞ>¿Ù'•øï ßDþsº3i¿û\kôvJ€>Ï? ›y‹È·¥ÏHGHÉŽe®ÇôƒVª [ºÁ¿ zßó¤&³èððo¬úºŽéϳ ['LyÏaç\šŒ« ã™Ž+i˜z8Ã{5|_•EU¬æ3†óªˆÆCpÊ\„»nj`•Œ€}ëæ¿Ìm"µR_”v³f5‹1æ„iÖEÆè,¾X¿Ï>Ï"´Wìs3ç/Ôd–ÁÖÁkôøØv0¹XF ô³‘4 ØË™V£û—ñ¿Ï[«* Ã/W¥]¤Qo ñÀ oÈ•‡í ™ú¿õÛר hN–&¼Á¿ƒ„OŸLôƒéÑÊÌm;E¸”"JËîÐXia|±ù&^y³ˆ¬îô¤-'[˜†oÓaeÿ®|…ÿÌäh´û(Ž-3øßï¯/)öœ” }ZŸ?`8ë¿a™@Ëe/ܳÅx†,É[:c¨möÔ¢×\ºìç9ø¤=*Fße ZSa_Â5H0êž¡pˆj<Ä‘ù*‚åÕzœ†9ƒyôŸ\ë9ìξ’(E_=SÊ™7SA—ú’‚ÈVU¸+¿?ý¹¯¹ð&7 #ß—€åFO:PlI}4?AóÇíÄê—\÷ñ"f?ÄÈÓÇVÔǸo\ä!®U²4äÙaä;¿‘òýP£Öj›£²^”ã¥a¥2Dãs% Nu8sÃ1¦ûÀ#¦Õçû?$ó®ÇËŸ€HÇ^Žî¶löÓ{ËÆu&ðá|<^ÙÍ¿â¿VhÇmé˜2ô›‰í–€ ³¹0¸{3ïÖ?N(ѧE/¿€ûÓJxÊ¢×Ñ<¦©h;ä:MÇéß{@«Ë„ßîv+4HËsuÖ²ˆÎºœÅ†µl;«j(ê(Ç‚ °tµ/ùgz m5š"Ö´ùPµÈÍ¡#eÒ¬¹ Ò“–ÒóëO mUŠ8ù“’y+ˆ®õdæ»Ê̦¡Ïåx/ò5ËDM'c‚é¸í•%µµšK*GøAe‰,»(={•§‹Í…)<1ÿy ê§cN¿9XèX`S2¡Å ˜•CUvDó]óc—vÓ©‹*xoåL<{¬•tÊh`Ä&Qj¸r+ÕÞÒÁþVÏßWª˜ÞšÖôp<¼ØÇ¡ó’‘0}²µ«:‚¯ÁsweºZy ±>Ø‚;rÀóYšªËE§7²±•79ˆ™Z-ÁzøjÓ˜ê>&BȈè|¹†ê–íÜL/8ûà¼Ñ^N:ƒ¦p®<ý`iÍŽý–íšAèÌ[΃Íãù‚ƒéúâÄ`zL¿TÐtSÕV~SÆÞqøä}€Âñ ð‘~Ç}¨¹uÂÿú!6x%B‹Ûcg´K0Š?ut `oÃZø|QøeŽ<» ·8Vdͬ댂°­ù›®E’`X©Œ/cÚ#Ï@•ÖwFVǦÄïÅ‚z$B9›x¨Ã¬í°òD 80Iq!xWõ<>I™Êo«EÑyuè1ß™þXÜÎÚO-f²e°Ÿ/]°ÓÙ×ÌþkgßX*õ¢“ ŸD®yê¢'pf§]ž, Î…çÆË°Jk'“Zž„''mçÆOKárÛ׫‹à™aæÆøÀȳL\éµlüøç¦©î¹p¿^fÙ™°ôr>äÜJã|ìÅ¢õ™ð=Ÿ,ÞÍOß§eA㢻سE’äçš±Ó‹^pãB˜S“˜°£®øá¼òôAëºxœÇ,–ã*‡›ì¦¹mh-þCÌlÈq÷%ìáòdVßGÆÇc‡½ÓàÙäõ c’ {´:™§9“¸ï²ï㧤b‰u#6‡p¾óÇä-çaý2ëFÚ?b;§£s£ÅÖhœÔóÓß²îƘ¬O›^ŸåŽ_ÃÉô9ÎÄx>Ó/Xþð+˜¼¯)îCî”8)÷ŠÀÖò"X‡-‡Ã9IöïñÏ`ÆžØâîTÊAÖÞžÄúVÇ㦻èR¾Õ@²]à»:þ^ÂO¬;1E7ZgÕ°ŒŒg•×îÁ[Ùó1<ƒ…Íj$ô4ØÞ€]¥¾Äý³)½½ŒÝ+œ FáðÄ\_y—‚ï)A"Ç6±5vÕ¨áùÎFB}~[îD=øá—u*øº@•Ú® U|Ç`ÖkF¨žf²z`XÓ‚Üg ­Dzۆ-‚lì˜ 22QúÎ0#5ó%žò-†+ÖÊÐòzì rÃÎgÖÈ·qWÓMíì†ã4ñ·.]»‰’ÝÃðE;žŸiFëÎu0~Þ„ÿÝïÑÙÅÀ]lF½ê¤à™6/YélFå nããµñPYªóWÙ³ •«plêgî?á ¬›½”¬Ñ&y:Í(5®Çöï‰À6Í·& y íM¹Œw—¶pïòUà_ù¥pô…’=EÌFáyxQ1ç‹„çóPSp­ç¹–Æ[à0ßY”û[ByKl±‡§¦éÞ€²­þ(¶âZ|Š#8Ú²«ITÑpu<‚V»NÂÎ*$ó´$Û2-â2fÓ~ʃÇ6’yë?àöÉtäÝ+0º$NÎͼŒâòôWÇ+¶Ǧ¸Ä û™{é\Û”îY÷ã ‘4™9É®šÌd‹è‚ÇÛƒt›~[ÿ \éÞ I‹‰„Î?üuh+œ¯m€•×(óâØEë!Ì“›ëáYÓ9¬ ;úzRàs±î:¤…Ë•`w"­ G¥½xëh"l‰«åŽÚFÜ <ú« š3·0ºwÌ1·/(›7᧺vˆžaÌö„¤’t¶ÅÞ‡ìð9¯Ó5ÈçBGŠR¦mÁ›™aý!Æ\ö¼·~'°F3èºXsì _Œ¥²y°Wq7žØ6îr4dïG\9_æüÇÙˆUYÉ5Vº —¶W ƒv7´uã•àM¬èÇD<ôEÄÃg’¾9SÑl‡c#ø›ÁŸÿàîœ3À¯¸€Ü˜É®ØÂÎË !-S Šળ±Ø©ó=ÉÅ¥Â\™+ø›íà†Þ-e¶*Ð&ýƒ¸»M˜ÎÞœÅf_xˆLúuÖ;D†Hý£ûåWAí¬K?ǃÍLHwƒÌŒ› ]ÜWœ©‰â0>^¸m{ Ê ³£Ã‰l̇5`§dMº–è`Òèelâ9ŽÇ¤!¡ä#FjXR&Ý‘Djá,E ÒÊ•"Ñ÷EPÜûø„ÿƒß=Á5ø“Î7ãÊpW*§âBW¶÷ã†ÔL· ãX„HàËÍ{Èž‘·8)2Vj?ÆN÷qƃ¹ä4õû­åTÞmY|f \ |‡a ?rX­ú£nŸˆFFö&þS‚/´B)”¥‚õÉŒñ &¸w™0VèÏ›AeDRàÛÞ^¬åЖþd¥Ùiˆ_÷½öP—<þü$÷Ù(0`ÝÚ(øùˆq–žJÖ%‡Ñw¾äj´{tçð2-ÄW÷›°I1½¾ç,cÓYü;íàªÿ ÛFÅ —ã'2™TèÚMøýÚ¿lº¥ýa:êºìfT{àÓ¢ë ëmÉ’÷Áƒ>JTRå¤7/Æš¤B»;ê­\ªx˜í¿_Ć}ûˆyOýY¡ð4*¢ë]˜ òv4?ûÛ|_€¨·jA æQ6=_:ù$‰ä¯e üR’óùÌV)~œ+¨˜v.m¸ñðÖü(ã"Ý NÎjØx¿ä§Ø1Õ“¡‡;™*¼ÇÖ¼C-ûV–› ƒÇôitEûoG1òèEÁ‹yd‹“ ÅóSÊ~FÃ%µœ~æZª´Ð9ËWå_Âìí“èñM‹pŸîÿö¿N;ÏŠÏ/f¯ûI³ï€Êq›¥$XX† Ô5î|6ÇÔ aÿ$ònú5–çï$¢Ýq“c½)ç Ë’Z7SL¿w‹_®IŸ+9@‹Ûþxö]׃‡@,.yV]`æO["ç6“zoNŽü]NÕ›Õô§ÿáqüŸ&EÎ7؃;4Ÿvm¹tÈ™¬T#&ƒeœ5—¾19Áø:¬Ž;kçe0Ø—ŒoÝŸ£˜}"^^±^Ÿˆ&O—æ`…Mšù÷­3¥ šŒ-¿û¦ï4œ½ôŒ z ˘NbËòjÐ ocrŸk’փ̿õlö!E02±‡kñlnâæQ­©à³$Zãóf¨cöÄúçòm¬o[#Wdh?5gÒVÆàx»›t¢øœÓ„ÿf-]_ˆ¹'¦Ã½ÀV4÷·†ìSÉwósÈûJ.?JÙ&ä`Å Èòˤ|¿ãJU 5{€o¡$ټÃ\˜º˜öû…3ü7EˆÕ_²×æ ÇA¼-/féuœ¯ H–[Aà•®f }%(GûW´ÛWP—ó¥Hœn=¨þq$1U™Tµ¿W(Nƒï«µ‰«?%ߊ1´«X ùmÚìä]á˜Ô~Œ‘¸+IÄîOA±‚QNm ;ebf}/^±­ º¹ÿãÿµü xî`Lkã}i‚tÉ÷Ï!^Ã~ɳ(uJ‹ô¦ðÐ'–¢º'ˆ8åÞG¡±/LíoŸv ›íéßI:0%”bض;83?¬Ì*†öAÉÒŠnñ2§ZÍûð¹Ùu´½·˜:[¤’i׊°kÝbR4í2êßü‚ƒÅ:¤K{* 9ngÈ _:’z÷±ƒš¤v¶¨]ˆ¢ºæzEÕÃêw’;œ(\г¦6à[ujZ Ónd£^±ßÂÓ¹õÝ ­šÆ~ëódoªŠræ^Œ /þÆ¿¾<$èàW&ï›ìDýë¼§2ì A¥Sºâíb¸¾` 'g‘è/°ž‡Ÿ‰cË€ÿÞ4ú”™LÜ#èÔ™3PùÏbvʃ·`¤IÏßÝæÕ×ðŸ®Mø8µI;Acúe´ˆ-½êa¦Iý7£_ºˆØ…~Ä‘lXî‡kZp¬ä瓹jŒ²ÁWø¤âÂÄ»âGgœtßU ‹“楷{ñÛ2Aöޞݴ9„· àê5¥äÀXòµ¬õ²ÓS:¿A’±ÛíH¬¾«ãΕÙ$æ~Z½Ÿ5ÂO’Tªá—¬‰ ½ŽûK™•oÖIo&â_à™SÂê ŒJa)£¼Y†}d“ŒžÃ¤¹¢´ås':\:j/3àËæ8p‹p ÂçæbÞªrX¹ïkÓrYêrÔ~ÞcÝxöP!ÕcÔÍèûkƒ1~œN¿½ :wsh¶ÂFÒj!Hþdè࿇dhëcvµ’Ù²LÛFhÇó˜}$†¸åzÒïc§É½q<ïî $kCGØå*äøÎ9¤ÿÈt²-ǘ¼zpˆ¬ûG¾ªÐÆ…&Ä+˵ðÃäTˆÒÀ>r°u„ô„È;Aò›4HþíGøWx2ÙùÞ‡F¨ÇOÌÿ 5åУ–Éž»±š¨ ¹Ó¯'ëØß™S1 Wƒ6'v²!޹X•C½*¼ÀwA*6wq0î¨Ƈöa~F›ø,‰Z,ÊÕ£)4¿Ð› ZÒœr§š¢›OlÒ¡Çx¶ÐmÍNÄyïvÚ«åE|W¹’Í:UdW“99¥iHÞ©lb”?iÒÊ6¸·O“œf2ÈãÑ8ü9¤JÃsP¾Ýd`ëb÷Ð’>4Ö¢árà2øM®¿”]‚k.7C‚F#?•ªðAUŸ+ÄmÓ-åkˆÁŽ¿èÕÀ"ÿ63²=Ù–|¸p‡ô¾Oð§KÌÒ©º8àUƒšzP_{Ó§0¶̳iK¸üB‡a·ªU&»/e-¾ 2gœÀ´g«8LM؃æ?°ÖÐh,â§/Wapm‰áÒ‰lç”ëܵâÐÒj6mCeჸ9®»Ã6Ñ¢Øa´³~ CGeáEÓ1Ðz}“U8Ï×±ìë…ÚôN°‰ºá{cjô7‘&—„e­v°¹k:¶n•‡c—æ²+ø}éÂLÆ!~5ÜÙ§FûßN¥»{ØØ‡¥øÏZÉ·- ?ûܸûÁ_w£éý#Gì¨P‚¼‘Cþ-Ujæ Pö¬˜åÜ:Š\»jtó¬f2d:`ãö•vIÿÎ1¿{âà©·Ó&åÅ^l2‚økU`|*Â}ê™/ø¾0l °/•üѤï TFnsî+¹yÍHIÄüRc¼ ¯ Ô²kÀvÎléMdðŽ\öÙ¸6Šñû4 7ÓOá;þK¬gáFØì}¤·Ì¡Ñ³5¹î*àÿˆ¥{øIÅR×!ŽJ½,ʼæ!Oøeècv*ÀÔW d¶Ëxlý—ìËfWüÅ}¦ô‘˜Öó‘Íy—Ë Þ9‚vW?MÄ…Íö—_×çÜ}¼PÂ,ò²!Wýb–úù£z×6º{¶4-ZžŽNsÅp½V>Î Ëaìï—°…BWÏrOÀR¿^vþ«vØ7¿Ü6tñ7·as‘4“zĈ*Ï~'l ±Š ¢ßj´i”Ì <ñû>JåQïò¨öèK¾ñQ?ü$;4€¬újC¾2aÝ´02kA¹ûÀ„ë]/EJÚœh¬ø¨µm„èØ#Õ©fTõ0"“²…D:›â'E5̼’Ï\e%8p¤«ͧ¾`ƒ'ÏÃOß2œÛ+ˆ>ƒÅ°bçivÕîõþ/Ï?Í™ó†© P!Ûä)Æ›½ÇEBlßß6;sÖ°ÌúS/˜MãyOjé~û6Ï ¡‹Cp_o¬šOÅïL¢¥+ö±óôX“àoæ§O¼Õ9~!apWî,»°Æ›|;#Hןù ©‘Š4”À‚u“8ÏE–à¼{·À­Û„\½%Ž›Þ_AùÐÛÜÙq2„÷ÐW69#ZvØ“Ùjß±úM,å{`m®ÐCé2Þ-eµŸAºn9HvFÿ…dÝ$úèÐ\xo6•T>Š+Ú—ƒZÒ*âåò#gzaU¹Ö&-÷–ë;°‘»ÖO¬ÿ%,³Áö°4TÙ?««1Š£Îå˼#ÕØI¿D‰ ¢,z¨†M¶Ê$þjš&@¿K?Ù³èªwKçÑÃsWo/Ñ}¥L&§‰’– *tŠM4V]JÙê\fíËô“j.û{v=÷“Ø]ö¿s%øOb-ÝÇýéujé?ŽÄ*²ëO&ÃV«€x£¤¤ÓÌ7¾ôÔÇn¸`°œºÁ{_Žt\Ó³?ËÈGŠ!ª ¼¨“1 9…b+ É!ó,\¥z’ñ¨\¤ëÇaÀÅsœˆL}²pG.žßJf4Ç9oNøÿû²kè”ÄîÐÃõ9N”,Icø¿ЫÚ9lncõ: ‰¨F1”Ϊf¾Ž\D;™D<ü=h8l"™«OàÔƒYÐÚ?‚þl'oÏÛ‘Úø¬7Ùê—ïÂÕõÛÙ®‡âdϰ"èð õ88{…мÕô£æfíܳŽAÿrü¢kèã.wò+å9êûñ)ë_ŒÇñ½Ø±L‘N.>‰})œõ|îÄS„E‰YïPÀ¥”±ê]äÅY¼¼¿놟£ÆLéwEŒ,¹ R3§ÓøÕRôÆ–ù¤àƒ}vû [uq–Úüaî]œü¿ý†£ñdm4wáâ]Xó{”é-ýPÉ£ bçO1 dºBICé,4lb e™S{³Ã×eƒ†f&»}Ž&êœ<ƒ¹üêäÏu,Ÿso}Î6ó,&¿Ãùáé·(üô÷9Þ}ÇÅÖ¥"”ó­ øMŒHá¸.0áÉ€ZÓ<°(û;þ¿£Ú;›Ø¿º¦u¸Ô~­ë‹!¶]qLÍšnØdõâÿ¦²3ÖI‘ñãЧ— %cÜpM.¼{p>&,dâëê™å‰v+¿SȽ0‡Ä?Ú 1fÓ€MØ C»+1¦S ª¯Nøßdu:Äf'³tE*®}‹9˜F¦½üN]¢Dr~|ÚÙÌÿÇú-ÈÁÛÀ6…£g¥LŸúÉ +¥I-•üiG@ÈL!|iÂPùÏŠ^ò›F‚wÀ.>Aúæôy\¨`Ab•¢IM\6,ËŸ‡~œ@þjQ¶(Q\ÖkEc‡ ûÛu¸º¼T<,ÞÌÅØ{—›ð|"\—…Æs)xñ;”µæ1óz.0‰;¤°s•#z ÷âã¢äqía6ÿ·•;;™~ 6¤¦u‹™pß@ìvOfÄ“…¨Åõ6h;,_Çöã–ÙGÒÏ(lx^÷£ËÀæizþ[Rý¨ó³pÍã¿«Á¥›¢ôvp ä-¶‡Ù2 vÙUï@dÅ=滤%ùn Á<ôžqûõj+æ»àؽ€èýnãJmObC¬¿±KG–íw‹é¥ìƫތ¼Õnbða›ú Ìxc@xŽ´NØÿðüwæKÌŽ5©úçDÍC–í×ú‰çf,¤:`[_7ÝjM³Ÿ“Qd½Ë)r¦Mj“°]ë‰€Ö ~Ί‡‡.–”÷ëwz()š,9u-Tù‰fŒ=ûnå bý· ->Ø2ȬR‚±3‰ç$½µÃ™4¦œÚŒa±‹Éß1?")fJní6±ÛŠøyã&,]\~Ä w†ª“”A~üþ𖜠žAñÄè@=–Ú/ùQ™ä€Õ?\žWJ÷Y ûõéOÞ—§¨ÐÜ(0³¸B,¿JâþšÔˆH@AëÐ üﳞÅ\ÛYSÿ¢Û“RÉÇé$tg \æ(Q¡.+<°êgØ|ú i2dC׌ ÛÅò‰Üdž ˆB+ѽ¢“"¿`Ðj9Z>½‘(7hÓŸö¼Õ^‹¾¿·¶PºÞ9“)ѹ@K‰.,­ó£‡Dð‘K±Ì¹ë="=Cå4mz/ôoТ>{¸.i»Ó$úò9¿éŽùÓz7-J—·p¥‹èñÄ¥G¥H®L^]Áþ;å/01|Ê8Þ¨Qü208ò…Ýöâ |zð„³.³ôÈܶÙpÞÃgREryi#n ã¿O¬pñ…ëÕÈÞiIÌ_Ëï=óë8pý™!Q+ç¡·tDÑHö\àË‹A'œìŠEv0kŸê‘{ÇÓÙoû”éªd!òåÖgÎûdrTûnå2±«h—ë=¸m>Æx41U?õªß̹ÔÄãëoØÁ*ì¢á‰ø?Ýð—¡Y ôhF– M/T U’ÀT Œà^QNrKégž…$y‘)I–{Ë1›ÓÕÝ‹š¶‡mCw½XPØ.¯²äéΗ~´_߆î²mbˆ³šÔŸ#ï\é† úäh"/Â†Ž ÇsÏÇK§½"7C¸n'’ívIIêtéÅó–>ƒZ ìeç÷ê“=!@£ ZT ˜ŸgSïCú´öB=ÍMЦ¿ÿœdZÃæÐ3£ÛÉœ#A\‘ <пó"Gª©Þ ¡D¶<©#y½¬ u3ÕqùŒ=›éàÝØ`4™:¬'­Û‹Édñ—Tk×,ð¼þÓ‚6ÞÄa÷{œ’JÖNYNœ«%‰„›4{ ãŒ@òìM®t-†“QaäCH1­üª?וbòøâ`å¦ã…à&R³î+]ž ?Îa¿ul¢½†‰)ü~œ3Æù?~éa±­&—”¿JàúE¾ÁÛQäé ²ïîÿúÿØ­¹Æ`Z»6ò\ûùu«Jpp/çð—=™vì·E@ØQÕ¦¤G2øÉŒ¡—e/Àܶ$˜òj Õ%³äȯæ[—íŽÍÃKû®1 r‰Hvo£ò·¯¢Mw2Ê5™P?»|Ð9iN§LöF‰«ætlDW¶v@êÀMVÅ%»ñ ¤ÙCb*üјý7òòÎ1á§–°ßÌz°uŽ=m‰ýŒ/N5î(ÿ Ra»hù±2϶£º[Ë/¦‹S7þ`¯}a™™ç—À]ƒMÇÿ¾6öÒ¯{øgx½pˆ¡VvÿÛÿ›7ï ìL‹ÁuØšqîÔ(RÛgzÄ Øosqk‹0U×gRmµÉ¦sÇè †Önä§–˜ÐË;—`ŒÑEL¸¾ ª£›™;ÓZ©XáINáQ.önA|féŬ‹LôŠ ß²”¥xqzÕèêŽÅQ…68øm;W·7gnÀ¿ý5¨¥îOž^ñ$õó iÌl{öTåVÍYÊtüÄZk ¦9B_«ÃÄôh̸i†+ýÓH‘r 5¿Ò™_&ä]ÿ3ðŠ&:×Úдzq}#DZ/™ï 6£e/W^)æ«üþñÊcû¾ÿ¨ïާòÿ·WöžeŠTȈs_/ Ú$Ñ.4¨Œì•²*³Dʝ-i¡¤ÒPÑN‹ŠÆÏûûxü|þðà8ç¾_¯ç¹ÆóyÝ纯Ñõ€‘Ÿà[E,7fž"ó-Ôt/x£ò"#´й‰-ìæ.ºðU[ß÷}.±ëS{fÑ,<&ÇK<¾Þ…ažB0©\†ÿ|BØUbiL ÃGLÛWØ›`=TÍé®ü‡ÉÚI VnˆSÖ¦bEl5¬—ÜJ*IJ[Â,§ Û숇ýø¹Í¹è²’¼n’åD¯y€­Ï7r®ï¾#ü 8¯ÿ,¬ïy+_²G¿Îâ{¸ë{º(•à¾?põüaÜüµœñq<†¿ËQïv?<{Å ]Köã\ }xuDœJ|YO¶TraÏ¡,¶Jg+¹cΚlèCÝeÕp·b [_aËb¹«¢½©G½&¾Ê‹¿/nž¡ÏŒÆIÚàzL…<ÈÖ'Ö#¦f8œ‚ѽòô!6‚Êöf ù.p¢Hƒ¦mžNËç¥Ã5ž­6õ+â½·N†éG޲{ncðÒ[l°}÷4ç¶²Nqó‰ƒá\"›ÒËUß`:¿õ/OìÀã…AÈ9µ± –Ý•Åã›ÓÉÆ]R¯+ÉÌ¿ÐLv#ÚºRƒ`v·¬yÉ[Õ.ãùå)˜Qµþÿ[ñZØ2kt>}ŽºÌœFb–ú£—S9—— KÎ/¢ªòêtñçÌ¿æ&*ëwnlÿ‹·|Â1±rÑhÿÈþ=¤Aº½¨\Œ#ú…Øá6I;”n¢ú›ÕèÏüíxÓt<ìeÍ<ÓÈiƒHÈu4£—ÄÓˆáo sºª8ï’Ñþ« ‹éƒC×ï`GA/î¿= +Õçú%åÜ-3l”·šÀߟ€‰Z8 5Áû­­0²ÇÞØ‹›Š¤™…!ë±*Cþ…Þe=|%ér#z-Úç„[Vš´!u”³fóðnÒ V›×C®¹à‹NÏËqý\|üð76¤-ÃcűúBðø ?õ(:íDëÜEE§ß,oà ›Œ» ºSaœ/ÀÉ݃ Yý‘ýþSƒ®þåƒÛ¦éÒÈÓ_ÐÖ^îÜ1¤K[Óþü#¸ª\—^YC¯ï£¤7ñËûƒAÏrÑwK ~yý¾ßØC;ša†²4íË“g5 ³°ãË;.éb•êZáÇ[^zÌø&Þ¬)È‚ŸÃNË3 xá;°<1øïž75:/N·hÿf÷ÅÞGG+z{{tŠŒà®5|!B__NÆÖð¬Ü¯Lž“ †\ÖÇmf—ðk…8i`tY£i«ÇÏÉø·(€Ljñ¤?Û\é‚8ÒQJè–çZ˜»òÜ•J‡]Ñ ´¢Õ™L6¥YbbØùy5þt2f0°û1~6ŸY"$ :R(Ó&ÅkÔ‰ÍWi:ô|>ó&4›5üH^ø|¶qטDÌ1ÿ­Í¤åÓ'<$ùæp° Ý9ÙÌÖì3q~%|JÛÕa8Ó\{‚ÿ k£ Kf܃KPû×e8Y™‡ö&­ Rw™„Ï\:Ðúõl«¨g˪q’ ©¸F/æÙnÀÖ8zdã7Tþ(O¿}†~O–MLGÕÊE(y|é=ÇÈyïC¹¿Æh`¹÷pV%I:vt³ *†/žð]ž¨RqÌí`¦÷1_ŽeƒñÜ×[ c7Ç`ûðŽË‰vtªýÈÈìO„Ošòø&‚!!º6¨¼Äˆj÷9Œçúrˆ?õ‰Y7¿v\w¦Ð&á&¥û¸ü <µÆÍbSÈÐ{(òÙ‘ôÊS³=Að–¼õ=HdH&ß%ªçiߺ砧+ gxA`w¼® ÅÃý ±]‘*»×ãMIÒÕ³€øFêv‘UöÐ¥=žùøåSCK®“(ýÛsŒUº R‡ŸJ–€ðs®ìÃ)dt…-Iõý„~*RØÈùJ¨†â|òO7»<áÅ$m5cŠÊ/³ç4þÀoBXnmÏ\®ºË û½ÀìÌOÌà iÜžBÐ)DŸø,æfß;önÇ yµSËd‰Éœ°iS,Œ Ñ)Ió¹ö»­@­ÌŸœ\Î!Ú‡³cù˜½¾ìråì3ƒç“¶ Þ¾V£–R@óIc{HU˧î‹z‹è©%àj›ÊóífSßs?=ÎŽ“!Ϩ’KûÕiFi?JDñã³°G¬ €8Ãj˜À_?âŠÜ ¸îëÙ)¾_°.e hÝô[ÁG¸²ªñúГ—Fãõ›~è1hHzº ØW¬Èl« ;Ó >ØNÁG±­è™q…[êtžs»ò*#²U‚®sŽÃûs(ªð↠dŒêbf‹‰Ëg-Îp0Oq1pz h$O5´ßÈÇkL $X×H¦pÿåfÂÏqe›9ÕÓ¯Á?Rtx²“ߣDžüÕ¦×\eèÕ#“`颿¸1ÂoîYOþ[oöü5öWŸú™Iû7‰-¸6‡ž(Òµjt`èȴƀ¦ã¬#ÿvúrR g¬´~{.vå%4N_HdG`A ,nC¯|<üYŽL¥À|“aê3/Aáæ\;нj“èîÕ{áYè%Vúl5:hÁŸúP­¨F“1ÝãzmXGž‰å3]ªN(¼×…½;®‰# eþ­?™¯~ŸÐÝw2GÆë¹TœtOjµÏƒÉæ5 c“›Õ>aÄ5O(–ŽÀÙ$iÖí$¬qª†Ÿ&&¸RîþjÍ@ÓCuL›³ÅqMª:«t·eÿ¯øZhà+‚›JqÌLEGxj›@ùÖ¡òy~nl…^DHâ ; Q› ‡êf“õk~á©Vm&¶B §t/ÇŠ:]¶÷ò-,CÖ5îÅ{ý·81•Ï TÿŽNßÍ}Q5ŸXè :°ù&&Ç`ÔOzoîqtòêÆ24bú/¶ØªÑ¹PÈúÇÕ@zMó4o»åµ ?EîV´scVß‹Ùu¬³]æéƒÁ€Ão?ÆÙæõšý²UÆ÷Äé;­µáE×*ÎÚͽL¼­. EBhé”yöŠ>΃¶ÊÕx[êÁDþ??šÊÎkFºþ6 ÞÁ…“ßìñþªL|½ï$ e~±Ù#Y‹CBŠðÅ,8ü+qGÛ#Œ.ÛˆVá{€oŸ(oÓ£Týh^²"E9è¸x-¬úP…Bþp3u3ÙŽ/!P~7 Ƀàþz,¥ÓIýžÃÜ犚äoE)Δ|Ì6ðØÓ÷v’˜3’±Ý׿Ñì¡A}âðY›VaDZ ¬úÙÙà>Wœ°>»Í‘~ɱ&y¡rèéWº‘p:ûV‰G±EÊTX±}lßC¨3d­Æq’Ïܸ,±ÿ›;;,žSo¹½û>Ã;ïÇœû‘=LÏ«;c…‹S—ŒsÃ{f©·/l¨7†¤Ûs Rw:þf=ë:fö±¯\±ÕÃìѾ­4®ìÌ–·¤’SÀçcPÑŠó>?‚ì½—Øí1º8ª"‡ó;&Ñ驞Ï‘#ÜTg|³ú-ÓÆðÁ‰€dH `£ æ¢Ãâ(`¬DUì†àê2a5ŒÇIbðÌý!.|ÅGÕú¦P7y š³¸.-ІÕV·ðÝ$#´|è€ks“ñÚš;,O«9cðÈ ûl¾Àaà `\2“¨½ôË-à¸P­ +'ò_¾Øwüûæ³o™9ìòpÝ(=R›4Í%,èâÕM(šzœ‘ÜëH7Í‚΢5ÖÀëœYÔ}š'uKÊa?P#Æ5r7q¿®m:dôL?œR¼Ò–0ßÖÆáó±1Œ-ë†45EH8¹›mxeb+hÔ“LšZ.O® 8åI¸7ÿ¼›EçO`¯¬C“êeP¢Q‰VW¾M ’€/´AbY#8=ÙB. MáìŽ&ž}Äû‘ZŒ\Ä*œ’ J|ºb þ¼.qŒ™£¨N–-v'{õÿröšñ²á[.áÒ«³ØeÖuñ/(4Ιo~°iNŸØ$µ¯\—¿5L׊ýØ“¨„ê7¦ oä2Ú×gÁ”œº l#X¬ƒÊSÓ Lb.œtíBYûlM<¾ï8ü„iš}([›Ž«Ž=b»w$15C¼T¦þ6š¹³âCS™’ˆ¦a^è¸? ǵ÷ÅU3é䩇I–ÝR€B¾·9ÙúðÃ[— |mÖß…oÍxeë¤ñ?ÑŒï3Õx ‘ú6µæ0t•ÿ…UK@ú`ä‰÷áùÛËÛúTðZ$O‡ ì8þ.ò8yá öÉÖE”zщþ7Ó Vó ÔØ6‰ö>üÚ6•†-Ö¢V.W™éÃ’tE¨¬8Ù ³ÙGÉ͸êÉ,*1©Ýfþ§BVªà \½ÒÂÅ6€ô¥÷˜8ë zÛ¼d^DϤőŸð±@´¶sÇ=Bdühì>^KC9Gœ¼Ô͆•…ª$¢¦6Ÿ<Ó6Å01lÛ-v³Ê"èý0‚ª·Íh_ÈAà~ŠeάÓdú.Ô±ÎÚѰ¯ñ!3&sGæ¥àìÂ4"R ïºÁ¦ü—ˆÑ˜EFä§í T­ƒÒ}"TGX”ê›ÐèÓÚŸЦöÚ»p惉5ŠÐ¦{É f«ÛÃæ2±2µIËðÓsÁ3Éy‡c°ð¹4»>Þ‹tXZc’ëö°|¨ÀmTï–¤Bbsè¸ ¸ç“‚£·¯‚â%w«;ÎhK°ÿ—´ª6 3ãõQ,×-Û ÿ‘6jq>oZÊú þq6-åež”ª@[ëÖíæ>øñrí>G"¶§’~v7?]õ ’™+­LUôŽÝ^lâUÆœy# 8$Ú¶zÄgtÍž”É.ß:âѽڂü³ #Ômá¬;eœ¤¿Møµ™jñ¦ÁÓÛéB9îÊQiÚž+ƒlã X~Ѷ0tØN››¶êþäcyC:/½Ä^´8 ÖýÀ`ê "¨jÌ ç;€~E•œ½ÔôP¬Pc¯´ûãZ?U´|ÌbÃÛXô*´€ã“ÅØ¤“è<¬iãg nÝ`ªL²§¶¿cÇ.³ÖÊ1¬áPD~# ¤™_ƒU/êÿ¸Î¤xÝËcœÙdˆiÃÓzb-± {®dòr>‚É >ú7é8$ä«S•¨h¬ùÖyû±k\dò,qDsIº%>áÿíÚBìÉ•¢h=\Î+eÈÞD9!wæOû…'Ï3zAÏÁxy #ô-šùgjFW©í‚šŒv6+~d“Doc4¼-Ž“²ŠØ%¿~3ƒ•ˆPs=š8:Ã7–å~-„’¡ZØ6£‡{ùúC¼VÿõFKë|€Ÿ¯øˆäŒ[àQ¹ …WØR¥“}ðÛÜŒô•ú“ÆëŽÐ7pm|Oi˜ìu›ëÆû”ó|~ £(5 q+áÈp ŸÍc¼4@òºÎ1c0‰ÀäÉ<°ìüŒ¼ZŒW$òÐkc-~Ðn‚ ù  Óú–½-ÈOQKb¢þûæ÷ôÝ Œ2ß7Ûñó“zýz^Ø ½MüTæ£=Y–‹sè±—²ÄìOí’ɇÎÒX°ªå½ÝËNêÔAÃ+Ü•»°²ó8Î(Ö¡:§ãÔw¬QÚ!pLŠ€¦[á°jÏmt먧v:˜£E¦/B´þ…cæÞyìå:ôØ(zKU ®í7¢Ò!8?z'ýyë,iÒ-ycz‚"y³Â¨‹@2ý}e?ÄSž0UŠŠ@–&]¦?|µ`Ûþ¸Ý¶¿Jÿ½TpóIèüXOUU·Ñ$çˆwÀí-+&®ÿµ‰Æ2þBáhêöþiÐO«º™éÚÍ0ã†=þ¸ŸîŘÇÙp®¾ÓËίmi߆´’ß ]ð12f¬- H÷»»àê–…z¡—ñÃb#öj)¦¤I`Â*}úÉ3³”’@¦å¼2Ò³Q¯‘&s~]Ã1³t2ß‘ðœÅfÕµcV­H½4ÃÁÍíðgÞT*š·‚‘·0¤Ê³f“á‚5gÉÑ»ÛQm/^SM°áéák9½µ“YhѲYtþ×dT'ö6Ǭ…"œê ¼öL-¯by$¨´j*×k÷8p¬f"þ—›v·”µaü}p¨3¦ö¶E0М͖u›ªOàšO$‡¹Ð{‡Öì¢Zúq´ ¿ï›Níÿfrί#Ó’c€g\+ÌÜzM{'Áº]ÁlÑ×p7$Çtè&ƒH1>*YóÁÓwŠS蘬0,®„XYhu‰dÔ2XEé"Δâ2ˆ·´ö€Ë=‰'Õè»ë+™Æ ¡(»8J5ÞpìF–Òg¡«!0º®\5"óíòXÉÂrèö©G«ê¸ýÉ¿mò¢&+â°uãmð·}‹o}çãâ´cØ_u‰Ü®¸ð¿ùÇŒ6ξð”ÿ2›øÚC¢ q{Ò Ì=§@§Æ_À­nQæy¯´S}ë»°¶jcóa QÑ]K:~N…ÄŸ 8ý,g4%˜}I§ærõ­ÝÈš¥÷™;oÁÕ´DÒ®^Íþ8½Œ<ýbO‡@ ¼l¢Á%T^<‘$AŽ¡Îå¿(uK¼²´ÃÁZ´È{Æøº®Â¼FĹ˟ñ”g.~çg*13xcágÓilÿvfLê«GR¼| y+—šùñZÝÅWqëß›LEþzʳÀç. BæM=um;Yuóèþ½÷ähyÞnÖõú"ârV…8¯ž‡ýÝ+A¨Ê”—ÐEÓÉÒ²NÜR·MÚØ¨ 9ò/±ò_f=oâŒÌÝMynC5Ã<2T(­ŸuÈR;C¹ê8°˜‡ÒÊY¸Wä'8‰GàÚeÔ}¬ˆìÙÇÅà«ñôÈäÂÝôûT ÈßoDÎ̾ÂÍß9—ØX Pã3ñ~á-ð›ÔÀÝ8Úñ#‡ðXÇ“ºIŽÔ½@C5;à_³¸|ƒ›kcñÄ…ë$ûàÓBAºu5]lÕ¬b@ÚE—¸ùË“ðM‡˜Ž’RS>ß:4¢z¦ð‚$¡p6q=©xËâ–ÉBÄøß6ìlÝͤPVö1[ŹŽVÇQ+]ˆJí¢p'VŒªÐx›jΆ¾¸…¿yeéÌà\øcõ Íû,áãYºxnæÜ‚™¡˜^fMDL“!K¶ä£•mX%‚AíVއºqÙRŠÏ~÷ƒÞ*yâ¿ï6pnÌžS²t—Š~™ .,¬~ÎGj™Fvüulf‰d Zãs‡oìì,Î\«…Öç•ÆýõçÜYÇjä%C”­8±ËÎ…ðXÿ 8 L ¥þõvô=Ö•·šûút;7z-zúºÃ “ĉ"É~°xn]Ä»§ÕäbdDG½ÿ(ûó¾ M–2­åzTÑ™ŸëÀôšl¸<Ûf—$ƒ€p\\Á2Û²0úæ/¶¾á:6Ì€ÓÅrJˆ|ù¼œN™üÂæªãsî°ÆÍåtY~GßWs¨ ,ÝW‡’iÂ=Ñ`‡ÿžcst†Áò– q_@×¾s =g4ˆ|%×z—Y*·„Õ±¾ÊÊ®fõù$HÜnY*"åGª[^B‡Ô=X³X•=£½î& Êsmrb딉ü¿¡DÎ51S{mäC~¢³×Rþy9K. _¸£pË×~öÃŒ¢§ã¯YιkÅÐÄÁ{øf'ƒb9“ÈùßÊddL†ŽÞæ>O‚)Wæâuá£p:°’$©ÛØqR2VŠñörÐÎØSŽ•,[Ý¿YKpuA?ŽÌA^Ô:ѰQ繯Ʒ÷ÍíËYŸÍç0Œ¯³vv£K?ÎŪsi\Uö3´ñÞÁ1Ý×uÿ!¸wï%?:æ0ÒLèú´P5ºžø Ì!wçÎBsÚßÝÏдi¨m4†©ºöϬ+dgˆc“—ýòþ ¶=ýÂÝ?m§¤áE½X¥x€»óˆ>Lg7Óô·j2/¡BB¶©ð³%§@Ø[œ]tŒTùDaÕtòÃv u ˆÑ8 ¦sÀn(Ù„—à&}g(ä‚ä"^ʰ³H§âÖ‰É@óø öÇôV)"=Àõ+{X-'iòáßo ¨Ú|»%ñg¬1: É0³ïŒ 1p²á,¸5 Îf”0žjo¹Ÿìˆùönz%öOµ'øÑ{q2Xëe§À/ïÀÌ!ê&DyRG ®ëÑDüÈt…iç&Cz®Nj²¥éj”JNëĹÖí|Ì“ìÐxëk²¸jOÉ»À¬¹²Û”aD,a[È-to^I ¯Ð÷pJ+ì ºÕ¾êÓ+GáÚKò`G-{¥Ø˜<ê¸ 3+ràãƒQfÃF“¹Ù‘Ýt_€lz#FMŸ‡¿ÓŽÁ‡;xè­ nJä/ÅÃ…³¿@æ  Ëí¢‚œ¯²]}6Š'ÏÝì [© O¤×³ëz×RóæØõYˆŽeì%eViPï£IoqE‰ÿ›E¬öN3:ïÏ??ýÖÉ|Å)ÿÜɰó4ñz ²"dt&¹72ü„´è]‹Hß ƒ}háZkj¼R…(ZK¿M’$Ï,œð‘›~¶žNΞ žÐ¿ÿt³÷ ì˜!Õ_ø_òNw—ïV¤o¾ÜÎ$~ >dšô:t?£ÛfûLYz½Ü˜Uíì­ÎG¸=0ŽÕ‘’Œ§×vÝAùY§¾oÇ`ÿfúÔÚ êÚ“éNäåZÅ} ˆy½¶¯°§—W Ð/ *»Š¼éR!Ɔ܉øBÿ÷qç„Øº®^- ÆEêS¨3Ñ‚“86ýàp͆îÿt „HN¦6{ì¯ ÄZχƒâÁ-¾YzLÝ_bíV C{»¹Lš1Õ;gËöfIbI™4ܶ‹Á@Ñj$šóÁ&]›ø­¨‚çéW x0½ómmÖpgë/ùAò‡6G.êÍï/à;׎óBP€ˆk;â\la}Zˆ­c2\lúÇLõ<Éþº;ŸVXNƒó¡4JËÖ< ‚ÖiŽ$a—1+ŒÂñ=ÑHCòòîÈKÿckóѳËn¡æÕåÀ7aÿ /Ž``j?£•¾÷,/ÂÔ¬”Û'ÎÈ“ËÜ]¼l<„•¡Oi8j¦ßs­LºÇÌ>ÏîÅö]Ö œÓ‚M!¡8 'KŽd&²&Ñ‘”Wî:çþÌ„Ÿ3梺á/F‹aÁõ 1ß&ˆÎ6òfƒ‡Žà«½óñxq:¸70™‚î xc«ó—Èò¤.ó`0®ÚôŒhrê0¦{ËÎFåýÔEÐn÷6ƒC¶;ö¢•hP½¤SxÆÒ ­ãµ¨ß… éáP³þ8ìÊN&L–2só³ã— +'ð?LÙÁø¬o`r$T¨N>ûFÈ\²ôcüò^`Š^/®«ñÙÌ*vÛ^IKÐèIš·{˜}M—ÑGµ)DîëZÌÏCë¶8)VOq\–lÎ>³£ò/á¿~g£=¾øåáu¸¿…2ZYþ°ÆÈóëŒ3£¾Š;®GÊð­¡ 2A¨{Gî+x1ËkèÀßTxiFA`ÞIªTU«döNØ_t‡ *(–PñÊZ&æ3p1ýÄî=“¼‰7Q@Ô"f°ÛƒÊÁ/àûæ[*®y“ÇîvŸAo&¼ä=QÿÄ8åCº‹Ä °#/$I©ÎBøW`„=‹ÌIý]Iz(ò-=ü ˆz "Ÿ hVNÑ®‡¿9%ðžq'‘?Dé¢Ë6ÔìÒit_]€£Xüù; Zü> çd:Ô L¡V&å¸jîu–§VÚOd càHK`“ yߊ¨+?Ÿù¸ü *IÄf)°28ÊÎ^> µ.¹“·Ç#ñÒ ÞSG}n†R›] àa^EêD Î@ŒÞ-ÞHO'aómžÃ×À4üø»”ÝGÔ,mÙâŽSô“Œ&iR¶¤gUŠ!rÊ^eÂvoø¿WÌýßüw !*\ÊaŽÛy@Ç£iÄá/ ^nÞì¢ë—ï"ACMp|ø95¬9L9Ü)coH]¾«Q-ï#°f:¹s’689PÅft½•Ëm×aÅ Sr±3 CŒñyu}¡dYý*ü²]…z•ðæOuu‚—îï< ¹gl˜²áìä#°áh>\%2DéÙà7žÎÕ~›‰W¾Â÷’Ý4âS1vmV§ÏÎn¢'~ÅAv†)|ÞúkÞ«'¶èÆçG%'áSí äXô+èÉ; +j2È-…ÙtS© Ý©?'þijâÚê{\ÈNä?á=\4ï4˜)|@Ïm‘S¾‚ÎV_H”Û½ˆ¢ozgÆ1øøˆZCÖ?ÝB9¶ðùït¸d„ýÏ•IÙösKþyœ¶uôãÇ^»PmFöô^×mÖ!à»=þþÒ6 ÊÇ ¨CÀnÖsÎ5ÛQ‡Óf]AuÓÅ¢¤OvŸ '1¢öØÒT'ч3”;÷ HÁÑàtâö€¡Åï—óF8¼Ô äàÆý¤åŠ-k’mJ„û°h›,õ­¤?Dµqµ­"3ÜmHÜ”ãÈ+Ì{ŸLìþ‘ÀMÞyW¯¬„C¾ ü~6î¬Ývvx£#Íü0³§‘ ©N›SíipX¹£† õó, 7 GkÈæ” ù“yÉf÷ì{ÌþYd‹áeöâUR"þ ÚĨ>Fã_†m§€ð!>ïòƒí.vÓ«Id“½9yÕÏOZΉŸc³Â/·Øö3ŠkŽ2^é‚Ô €Ÿ‘pc”‘ëÆ‘ôjf2“·*KhÒS-ûÅ*à£E„åµâªÍ‡`rê3æy–2,‘á!3H"÷£ìÿ¾ÿÝöG ìl9À>>¸˜Føý@ÕH3*2ü•¤%Éß{Ò!ž€… !Òã!x+Æ¡ØUwš>*Y9BØÈNV#iiøåh-W]À÷EÓ¨©¡Ôy³(i7"À³î›ë?jÇëž%Xñ- oZÓž%gÁe“6¦,ÅÁ§þ¸1k2!BšXëîFŽ­G'ÿ¬ÜºÕŒÐ¼íÀÇý€Œ2tü•¸·Nšâ¤³TÉ(–>>oNýч)Ìøš°b¾ñm Àà¥Èži‹¡Ûÿì£CãܶqçbòùÀ|ì’šŒt ¨lê„þ«·šÁYÕà!—VŒól ;UcÁY¤#ù);“†c¹ZäªÉKR´0 ÙQ{†a­w­¦Ëµ·2wÚ™åuÿ×oì›kJ÷E~LW`‘„6/€ïw„&ðû/}ÛgBny$k¹pž´édåÏXC檞<† ?VÂÃþ¤5w±ú·ÒMy¡®‘CóCœé3 [ºeíO ^Ø€ÞšUŒtOÃAºs"ÏrqÒÆ›Ž3ŸdEk‘Ù•n`Ë|!Zÿi6eÒ[’…`¹Êˆë_Æ2ÕmæÍ5O؈9k©ÂºóPò,ªybA¹ñ |±´Àg\'\öÍŠîr^ [Z™¶v oî†àÓU£¬ù¹jsÒ jÚÙ8hˆ§Ý2qã“,0ÞwÿÖ‰ÐL°†Â•¹¨W$<‘ÿùCB˜UïßâåÆðzJ?;y±áëšMl”eò ¢ª‰Yrç7ìQÔ$`ñ>IŠÐó0•䚪=/®‰ñSlUeü²§‘s[ÙP©™DX2†S_ËÐO¢ˆ›;± ÞŠ¶å [÷·÷­ý1ü.ý!í‹Ø³õ¹9JKah¨‹rV—c¹&?}PëÊÎ5í§D.˜ýv†*©4,ÖW§Â±d7ÿàu…qcЭòüÏ瑎Hñø¦gÜ¡zã)ÉŽ…Ûj9êÏuɵVŠ8J”:äðÆEºXY’.·­žÈ>áœú½¥ðÛl ~ÓòdCò£Að»X«ç<Žgl®O¡âb“ ÆF†«Ò;³ñ•¨’ò lø5z^ÛŽÿÍŠþU}c\`ùÃü5»zö£òõ«ÌäYÊ´'~«/f[EƒÁÝOÙܧA(wñ-ØNÏ‚öœÝdúæL÷@4zº)Њ¥¹¬¨ ¶¾²cçe‚Ó¥4¨0ç§ÖoÙr·õhÝjÉ»,iaî1Y®CÖ˶0&›Æ×•Dû˜£°Ä©»ê%éÒ[½`t@œ*odúO†ÖÛWTrqÁ£8÷õáÿ]ÿ»É;*¸§ð0‡­@®F:4þºÎÝѼ’åèŸÎŒd–?ožÉ}ˆlGo”¥¹ä˼bPË„'—ÑŸºÑ…SôIár}:§œÂ×ð-q¯°çI/p-êA­P‚µŒ+eÿt(Ðêïuà? Û‚p³ðe ¸ùbüõ‡8ñ?|ñJÅk¬ô½ Áœ*ö­{-«ñ†H|éÞ[t×]a¬×•$ÂFæTXA Gÿt´Úøfp„>d¶‘‰³(¥‡È¦é° |%:øHà—y:ØÐ•‰UñpÚF™fhÛ`¤ÜoÎÈ몉üÏ—VŒž ß!-7ŽÑ…z7‰gw)¹gÖ oGZhœÇbœ Äø—9)Ðh¡GdÏ#×7M6¨u·m%¬”¨•²½ ëBvcŠõ”M俟ñŸmæýµc¤ß‚Õs…èÎ`2p™‡BP$ËŒ0}êü Y×Ssðê2°K–àC¥Š‡0?§›YÛnÆÚÛb¥ÞóDq (”Ų"XÔ –§ÛN_‚á~à®Ñ$‚²É`yEª¥wO²Ùñ}§‘ÝæU‚ç¯åa¹…aýa&ÿV¤}óçÓƒʤîl–•ÂVNÝYEfÓ'æÅÙhä f¯‰jiÃ:¾™=ÕcÍÀBC²ëç*³¬w>ƒ ómp-VœèEÓí}¤TMáï*výJkVÝô.4ûýž°ÿìZv¶ gÐÆ¨’æÕÁ©èÊnÞ{`:¹ºšŸ4ÏmA‹JDy,m|R‹ž«Šð¸y,{ø%Sv ùùA«šà¹Í(Ñp ÿľ=ÞøÑB”ÎþšÇ?f’e.ôÞWØ3^¿iùÂÑõPƒõ¦7Øô&\<ã‚*ÙbÒÆM}Ý€×EcÚõâHÊFø¬B`À’Tì¡;ÿñÃBLƒ%‰…p…3NuþÇô”%*Éí0¶EˆbRã»ØKÏðÑ:ÕXîóîsà‘¤JÔ÷1’çKõ**!N3¸?vNàà¬Êüé’{PAx)^µ<ËŠ•™ã 44|µ®d¾@ÝË4²)®>\tX²õǘ8ñ‘í«uѪq7¨i à7ÉðhçrZp?ì\ËáÁÔHúã\<è´GÓ¿A½è8וL»½¤?DWq=ªl~/ÿÓ…Ý †Or‘ÿ§¹ÐaI÷OÕ!<÷àØŸlÚÑTû\Ðx5ŒH(Ü”s$¦å«ÙwFY݈’Ò¢ƒb9ÌQ [ÜÖ%C÷ó*ÓôÄ"4”§ÿ\ƒ@pê-†_b¢¶DÒ®óÅØ”vv‚ÿN¶3Ö¯¾pÊš aÛƒEäìzY„²Mð´eí[†j·ŠÑYKuà|ó¦.œ cÖ?Ù=êT|¬.IiáöbR´&$yE¨O¯(i|{t×ûþx$†M¼ñ-ÆThŽ‹×.6aŒñ{n{ßY¼:Šíú²>…þ '‰:"dˆý˜¢M‡¹í`å©HÉÅ[Œ+÷Fõ(Ò‘­‚ôöù \þ˜JØSgpm¶ÉÚ+ Óg”Ô¯nôL•5½½ï(¤ryH/#†JEûá~é:ö—×FZýUVæBZôK|lë0¿¸þôžt²Y­2NôÀÁÉìu£X%WÁþü'èHJ=c0[q9ó†oB{9ó6®Ã®dCv¡Œ0ùþ6S|XŒ/B£`Öƒy *¢›‘÷¢(GCÖödîÃ÷=Ы;ž®”'íWRØr¹“ì“ eòbotË¿`òÀµÃFt Ë(?\ÅN†¶” h±µòÜIõ±«øâÌ:­(ôesàÚ”QÎÙ/òľV‘ü´À€·¾øî‰õ¯UŠf#z”¤Á“GØšzŸyŸÁ’ ³ŸÈ1ÅwC´Äì˜ÀŸãÚšºq†®¤Â2ÙXê~ô>Œ™ÊP/ÊZ%£Ë×Iä]!ÖŽòÀpæÐ’Å+ÈÞ=ðÃìÙ-Yqšd~È$ÌyŒLÆü"\µü(qq±"‡< ɾ½Ëâý2 f\!RÝw™"ïç¸ð3߯6u‹¿¡Àõ7h¹B¾j‡Ã×ÊÐÿòäݱwJH%êPù°²­M¬£Ù,ŸÊCú&ƒZ³ úˆÈšOZ¾žA¯–qeOÞùOè¿ß·¸ztñmªܬRÏçrðãßFLš7 ædv¶\}2^ÓÏï†#Ð0¾ÄÛÐË‹àÀ–n?XV~ìή¥±_§ÒéŠòtÏÕ ðåÆƒÝR6fú+˜í›ÃŒÔëCÑrOܤ!ËùþRžZñ€ÙsÓÞÆÏÁ{–Ô¿¬í¿¿©ð8nܸƒøžJdUwƒî´ìÖ#n\Eªò¶1ñp’a:ï hÝ:D‡ÍÕ.ìû·“ɵ |h߬@,0#±?íÁÊ¥”=^[E"yª1zésÖ±fóüm1iF÷„ÿ~úÇìyP ¸Mü ëàPá2È'ˆ"‘l¾E'+íC#ÊÕ@ÍyÛuz: =‡âdC&³m+&Û#iãò׸J#ë”™|m~ºKd~¼ðŽ _¢j‡Ùs/dð\.?‘o:7Z©ÐÀÊñð’]rP#¡’ؾ;ÂùîfBwƒñµ™)‚’¤Ñ§‹qŸ9õ߉‘Ø©si1›ËFõÙPý[«±fh ÌKÞ W¨ Ô© bçOñÕÚÛÌ`Æä®Ð§ø-3r§Ó ú]d*=g8„j}·Ñ¿`:5<¢3Qÿ.aA»3ưô¡7˜fK÷e*õIŒ Š:Ë~Þñ–+9,ʆ–(Ò¦šúµù~3Í"í+æ™Wy`èæMCvjÁ—|î+GîŽ%»\_pÎQ¢YŠäç‹ e’¯M›Øð­«“6ÁKñkš hY±¡ªT¢Ë˜J[‰ƒ~f?v#°Ô¤~6w@B—¬Ä-RWñ˜~ vmÀék%Hæˆ ØíŸR TÉ:©<ÈzÏ«\%Z'Žn<+_êÒëf¶ðrßäµe¡¬àT’uD‹ú‘5à3ÝŽæÞP˜ðÿZÉ'Ìïóáw»ˆT0†»),]E/…¼ÆWuÚprK6&Ì9‹û>0ôÜž*¼|7“^†ò¾+Xá)IðõèhÑÉzôSûn´)Id翌eM&ên(ÿñsç½;Ö·FãšoW؇>Ƹ±ãÜ+á'vulxH J>kÇÛ/!Û¼‡»W~oùÄÎs2§÷Þ`{_³)±p• fÍÍ M®`WƒõM†Õqy by oý¯îHƒ?òÅLPœC'9ž~%hààDÄLiJD7xêıB™_ZvŒ¦°R•<ùoåˆ ñI÷ów‘kÔÍló}‰×+ÊY‹±,øoŽrÏ©V¬•†£’q`2u+mŽ£ÇæÌÉêCØíº‚ªE|1•¸vÓ74ò´Ä¸›·Ù…šGðÑ6rBUŒÞ*˜Ó­öÓÈö½à©’Ã=-ª“$é¶ÈXüåÈKÍFb9ÓóøˆdÆdx¶gåq¹Ž»^ZÐ…\Eú\h7[=ŒîϨC9?uˆÈF%Ÿ“ìÎj#ªÁÝŠs5Óà„j4Iư†3o±Z aNxÌ®|ƒæ¼ÁE('§ÏÀ±þ",*¬žlP:È8ÿ;?Ñÿ©"ý>¾Ö¸ÁÜ’ÜôÝ >*ð""ˆìîÕã™/›EŠ•9=êÞ´:Uº5³q½‡pቖMÓU' ›9…eßÁ1-?æE­¾è nóÁ—K;á#¿}x¢Üò%ˆ^ÝöÅ‘%lc¿Â6ç‹£„:“}Ô|–͉– dEiÌQ FÇÑî¸$ÈKÛ/µã*ö6®H.†ÆàÄCÊ&ýcï­OÕ? ©”ç#(N¢ùÌlê×^1òJíL¨„\îM«æ’­ðv÷FúäI;œßâˆjcÿÓ?2ÞÌ}šøp+ãŸ4Ÿ±²Þ@U.ŠÓxÓ(†=üÝÖBÙZYè´?GºO3ç7]€µ¶ÖØ-7˜§gÀRÊÑÙ›½~¤Õµ¼eèÚàM«bþñ-C³º@ìˆÉ‡9r Ø‹0Ð!Þ?Ù2BÔ5Cµ{kÁö\*Î{¯ mF,J):rÏŸW}ÄR^vÖƒNp9º;#á¤È¾dæúÂQöå%n¡–Á0Ö»]…©"ܸ ƒä5˜”.r:™º£’Y Øa~x?‰°›´ê¸Æ?›`´8b¿ê|vÆT}î‹ß…`‹<.ðœ±f¿ý´¡âíG`ñayz@j½ ÓÞ)s:IbŽ«>Qò8ƒ»êß2UµsHÉ.C ] Ãqèc¸^>Jïo %»#‘l¿Pòíx„œÐ%Þï‹ñ¡‚´­ty"ÁV5mA¶V¸È±" †ZÜã älØòûF=#ûë<úüªDåx:åÏrª6´˜N_¿€È«ÊÓÙO4¨Í¤v,×YEãtMHã‘f”®YŽ<¹Ga›¤-¾HåojÁ`ò.ìímÅ);äÿ›âÔf©1½ºÅ?¨d³MµSaèG³³ã5=ys7Ë9‘_Þïqt˜™øþß@⦶«Ð“ g¨ÐØô?¨¸d1öŽÔ¡ÆJ?L\'„Qçgâó£ ôëª^¦õz<¹HQÝÕ–ðç­U?ò ’ºoÃóŽlÏÉÛ ÿ¥~\-g8‰®x¡à›t· ¦¾®‚ØMÿà¨g,.ŠZ ƒ íÔ5;Lű9”3‰bn‹Ä)q˜*yè¿9Ïô#“?)ŒFÝÚΆ½U@!b ¡r™L2ÝÒN–-*ûáÀæ<˜m2—ä^ÉÁT‰7€6 ´m9œxx˜N;‹ü³ !¾eÝ&ZÁÙ¸f7Œ¯ÉT b–ì“ ÿ?ìž¶Ô$ÎȆcë±÷øãÚ\l Õÿ¼6|¸àŠšfáïw–>“H~ŒÎÞZN‹—òº½Í'=Ñiƒ-³W¢=SŸ¡ƒ­7&5Ú>ËäZ¬ûv¯xé†×¦v,ÁòÙáÏì“G“QBm>ÉNÇI©p·…à[‰Ûøç±%ú$sx įQ#ÊÙk‚3KÏ1.ZÓI4”T]‚Ö+i ŸVÉ.^R‡V‚Û¸¶kaÂÁ´náÖ͈ܽd@Sb<ÉäŠzìñÛ5«šˆØSç;åŒã»tvæáÚ þ{¼ÉŠøO›Á´ÞJÄ"Û”–Œ—÷i°ÜYm¬Äºd&.¿ÕsI½ý³Ióœ^hç-€`§nî¥ëg ųõÊ]è-ÏÍ8pDdß]ƒ sHrÆ|:+²žmºÅs­ÒñA÷{ȼv[ Up§ë°ÐüŽ/vy¢ý.÷3ÑëX¿õÝô‰”,r+2^Éê´Fé"l5;Dzý+0{@8Š¡†K5–».Z Éž!®†žú0•Kê ›10=˜ÄÞ+ÇÞ—kÉÆáøà1ùâ9‡þü*ÆôQ'fžûñãäÿÍÿ” ÿfó.Sžš\=Š»eÈß­¾x¢zÎ\$L®H©Â]ö¾á£O®6Á›ߨ³çT1¡L›n+;ÏÔêDP™½s©ªÒ0ó…¯''}f¶"®ÛcèáÇÖäóË&p>`Æй +5ŸàÍ K@ÌíŒôwƒÖؾ›‡ÅÁ'è¾Qq¤[½Jášj‚õøó쳜§Õž'ÑÓqÖ5Q1<\˜ã}/ Aúˆ*©’Oäˆtv4jxö3G ß²Óµb˜ž {²¾lóx²8‰5"Ý®í8厺òöö2§·º³µ'ò_RS3÷±s>Øñ•™³ëN\Lm‡,Hêueø±Ç&ô ¶ˆ_c",A)¸‰n%ïºÒÈýk.P9ï+)ªD—ïãM¯¶CÕ%–ÛçB¬e•>2¿RuÉŸûÙØ!Dzó%hè˜^©ZGz±¸<”òĬDÉεä¯Çs- œþá#"WÔÉþªãpÛZ˜¼}|й7VÊn]…ƒóÔÈÉ1%úDHšý÷+©PEv2á|ŠŽþ“HLå ¸þ3‰¼()y*¹„×w—ï·=8ÝYOnçò° {2—,úq§ÄTu¶kâþ¯—¢x2ò¸ÅÊ]èm!KµDŒX~eò-B”,ÚÈGIÝUÆú©…ºÔíS ÞŸ‹•Ãiq4¸õí¢ñ‘Êdië76pÃ<Õ¦O"ÖÌbÞÏ="·1Dó=–m^€ Lå§Þ§ÆŒQí]Bç’ГbpkðÎøÙÈİg`aÚpüÒÀþ×W·jH_*‚Ëé¾^¾3]€ƒï‘{Û˜”eàªb+F[Ÿ£ðÍ–:s”1à·¢ wò7O_?e_o%C3ÿqÙ¶™ôßÕy¤»ØþUÍ¡/‚Ž¡ÜëÿÍ?ÚåSÈ5™ ¬X_8¹ó*s£‰ô=â$Û¸ýèh]B²Õ²ñ»è ºŠ=JòôCZJu[qš\ŸH•LO’|)IúñJ5äž\Al#¡ÀÝ›É]áKöË>göÒ!.z%L +Í\ë1&Ç*î¢éºä*®Åÿú“wßgÅ·$aÖù¥¤©O¤W~[÷ «4%v«’0òÅ~²„cGÛËĉqŸZ-säæHÿFŸÓhÍßT|k˜Š#öt§…7}¸§Kµöâ=Ù¹GeI¿C Ã[AÞº%™ù³;pÆÆ©dù¸ÿüêÀ<ÞÅ ÐûdeWQÑãA0Ûó¬cÐ<2žFePñ^IÂÕÕÁ¾ÝKiòïäúúzˆ»å~á”úÔK~-³ß¬€;Í…‹«æ¿g$ïºáA×W¸ùi+ÿ=äö¯‡[çRÐcÕs”¶u¥6 ªáµ\6FŒv€ç}:3†²µÙŠ· ˜Ñ<Èû¼¾LÇ9ž?@Z“wÝ_C ó]qÖ¡4К½ž6ÇÈ&o í`8^¿ØL?mvÑn/P8éÂå”ß¹'’ØyO‡¤‚Õz±iÞ>HÔt§6ŸÜí­ÝèyžÈ¥¿ø‰ë7æãÐ|¬lĆ0uÒñ~'‰Ü™Š9Ï`–÷ ȹ›ˆKåº`C9èû²:½ÀèH–åÀµ‹h¬: M:àí÷zæÿ'\>k}l‡Sö@‡âÔšJ®»õƒÛ4mjÿå5t¾WÅÏ»ò™x¹ç¿Xx€ôŒ,çZ‡ ™2µ›[(/…¾"†0ÅÐŽ«Z¿OHžÅŸÇdãˆ)(! ï ìñlÛeÔš² ¶F7ÂXŠ|/¯ÂñºdÇŸhx’ ×ÚŽâeF^Ÿ ²³~±J?¶Àóù8¸þ2~Øwmºh*sûíjð<)ANîJ'…›cˆšàêi¼šÞ“¿;f·bÕ‰(ë²ÀÀ­ª°.3—æ.†ÆCP·ÿÌÿõ{&Ã*ÇaÎü¶`¢( ‡ß¯B—¦ZvÆÆò1F\INcÜtËárí¼fäA*ÔŸÀñ/õôuz æÂuN«ò¦©¹†ºÿïtÍäÔ$gá¡5‚øds8n*€Íy˜pb7ض…ÙK‰±R ?'VK³k{÷ã‚gñÌÏ:¸ÚÞ‰ã?6ÜÀ fäL.-ß@‡óÁºg[H¸Í\fkB ŽŠ-%ë2Õ'â¿æÄ6¼ƒfQ?rœ,iòžgÌákixüª müô‹q¯Kư9¶à=(C ¶¯)‘ À»0‡\ÝžŽÓïËߢÔ&Gz«Ž2³ú“qºRm>\ _Ú%ɚʹ¸«¿a­uI÷×x‚YÞçðM™[þ»§ e£-šdó \i/f7”Ì/Ijؽœ35®kÔÍñxü#Tß…MV8Ojìð`X!:D¾ãføêCÙד\Îß(_u›§Á²íŠÄþê’‡»·tBÑ>bö0ž}–>•HL“# QU왈ÿ ýÈ»z%+_5ž×öífzO¥æ–ÕLµß*Ʀæ).êdç½—aãÝÄ™Ë ‰(<<7b‘eýê±WÌ¥wwi¢Ÿõ^Ò³ÿ$=Ø#‚.¾úTj…ɽö Ìׯ’“ó–Qn©:¹ôe«éBJÒ)ümÄÖK«'ð_ÛÈCZ­ž°•őн¸ŒŒ£H· 19Æ5…“`²i•« '¬ó© ûOÎFñ1èÕ_CtDñyÅ6ð0‡O½ñÍŽ) )f Ìî§ØÉ; ø´”˜ŽΊlƒ’4K³ò"¼Ì+BU}æˆÒEÄ€\0è‹‚Í“i'¿åàæÊ½@|-õ1)LøŸ(¢Ù<{Y6XEœÖ^'{3üYrW¶M¾Ì˜Úªé¢Ðóh.jðãÎé×$í. þGC"þb ¾Ë‰c–âŸÿë±VNÀsj@íÊÆJ3Uܱ¥iâúW‰5ÜZ“Çr-—ã§õɸì|$yëI=b­Ñâq„¾ë>£ÇpÆJ€„˜í":‰‰œß‹ž ª¡s­•V^ä¿þc/IyÒ©I æn„€øâœ.OõÐA¡ZœþX€ÙÍs’G'ÓB©°G>ü¸ÉŽò!4h¢¢8‡œTˆ'oÎm§sFgoé’æéÁKˆÍ!ðójÆÐ;W!±{ Nb€¹?^•ûÌÚluǪ1QÖb,ˆ¹.¼˜Lv¡a¬¥ýžŒÑßYp¤º Ã>m@Õ½4é+áÝ T AvØvÂø¾&òßµ›ÌÁübVe…³Ô! ­®ì 3« e_dABT9ëvð»¤,œn#%“‘ÀJL¦ñßöBL•1Y³t2u}À îEî wCÓÀjî¢ÛkÉ11Ê[S ɦR8ê_C¥i–°ü×!â7k{³í{Îô¦ÙØfYøPÄO‡“—/¢I­ ž{<ƒ©>ü ]ßñ’ì5üäõÜa¨™{‹ùû;Ú}Ø‹Ô=-<¯pyp;7èK¬øí€7Q€N&|îŽÖØØð‰ý¡Û†Û:²hiÊ á¡ýïû/Ux5ÁD;Æ;“´‚ähH<¼KN'ºõªDfÁi¶7'ŒnšT G–€È rænÃ+§³|?AñÇîP&4¤Iü^É]xñôbHæiöĈÌÞE³~€ªiª4Çv?˜ç“TéÍ¿»Ðè`~í=ÄÍ\dÃÜÔzË±Žž ƒ[«@Êì6NR)¥æÅñP"š :ÓméÝi3Yoã{˜Õ\‚þ„âö6qªük!uÝ–ˆ¿Ô…Èi¡ ŒdŠ)]‰E뉷¼)» ]‘ùù ªÝ¿á¥š"\ ßC‡Á‘ ÿ??†þº°ÒÉ„¢ãï¨ÿ” å蔣oQ}ÿnfAñ_æó¦ ðÀU §—¯‚y7ñè¯)ôë¯PxgÙ„ÏßÞk)ÎšŽªû,é¹å˜Q{‚F¹8MM”D‡§¢¸}ŽÿŸç· ·ð‰ÒèRa|j‰Õ~~Äâìß¿qé»Ã´2ì%¨jDíQ9"!YŒOØ’me˨ÊÐdâÀ[‰% x©â÷ÇÌ:–¹üëØùoF4~;«@>¨bd{:È¿Dç;¢}ÿû-ì$î ÐcVQ=`·Þ‡=C\Ε9ÞМ7¡ÿJŽ£©®"R;O_®Æ0‡=÷ç\™ˆÕŽšØlFî÷§S5þ1»Ë`çbbÇ÷xjËQ÷ï.vÏ>ä}’Gr}Ëè¬+I؆ZLþüÂÒä¿>g_‘dxª ¿,|J$i ç¹~®–ô¿§ß-°âŒ9 < +”šQûÙ ¬¾=›ôþT¥‰­Ç¨ê¬fýÁÄ9ú½õâ);ƒªSŸ¾¸aNûóþ(km?ÌÉxp˜Ø/™BÖpá>›ˆä²;Ú¼²¡Ï/óàÑ}ßÒ€Ï"°‰/€¹3'‹xÄHÙcù þ-%—Ó|C'šyZ—ù¼õn¼C¤ƒÑÊûÞp%‚uödþÛôrfÿ¸ Goæž±á8¦·W™ Y/AE'EÒ§iJÔÆ]— \ã†'‰ÐÂÞXÝl@>‹•á:õjœÏæÐ›ITŠ]Ç=:k!è&±s_ŽkšÝdÎè2xè§À¶éñ×obBE ¤r‘}³B˜”MÖ¡GòÓÿžpÍ£¯Ü„h¥›–ŽÁë:õO2Iº/Õ3 È¡{ûÉÖ¯o@µÂ»Ü£‹údI¬ ’z‰—¶Ñî ü'µÜ†ÑáO…Þë¥0ú"#ã?Yª¼†˜U_Âm3F±t®2l²Ù ë_憄õ2›ãµÈï^î⃹x‰ï(ê¥Ø¶¤ÜØ¥YŸ9¡÷˜çXƒ0£©¢¤OW&Ü5$ÒûùQy¹*==°…½wÚ ’y䈾t?[¼ñÙÌtÓe§Ñß B¶=0d®‰Ž¡ÕUgºg~·¶C™!Pñ2 C&iÑÉ­ìÌÁnxbÓÞ»Ò¸ÝäÏûãè'}‰9ôNù2)øååÐÚ°9(´~ ;ÕR€Ö- ÿð¸•g ö¤¸ ü7g‚™O+W¨ó0 ‘™I-øþ3¸/‹*±æ#5ŠzÁîó «¼Á¥@ë¦`bÿ$ØnÖÍU.ÞÉx£¨zÉYºz#œˆ3¦Gž¿„çN èXwÜwìå6qTñêÁ¨–%CÜwà~š'B/ÖKÓ‹MG™ä× 37o^f=®´ÁdN|gkFÊk¯‚f)Î^z–™ëWH—åôÃÏ]bàac… ûIiÏÚ›Çn²—$ƽ$ð/Þ¬b]öüÆþÖ.æ¼¥Ùdß@¤:oÏ$ ®ì úþe:ý Å &º¬o‚ÿ.†ÌD A[βºó$ï|ØË¶áÚR}¨-¤áÃã¢á ©È¾§£ç³ ÈPÝ,z±0=Ô>ã33zgÕYZ8 #r¯ÐO꼤YÑ”¤T1#ñ1!<™¦‹}€]ʼnÀÞAœéºw=lCÝ0ë\Ó3‰´irÌÚ¨{)é/DýãÆq=‘=SK×y:~I>œGÕ¤Qã@+²Ý¥ˆ>Π_7—²÷77S“f_xº3÷LŸIï'À•›´aº{ùÝé@^Žó£ã‰?ôŽå+fßP$v~îEÃæÜ‹T&øïæïv|ÑÎK2–¤ã–Ä8D·0ÿ*Hï7?c;Ü0÷ùb\¹r7ž&}6g©H4™2¯ˆ¶jRó¶P:–w‰>¾mKâè)R´,€F¨ÝaÔ…o2tNÑSæ"Ôõ /éÊŒaS>ÑèMitåú¿Üé¾LP§/Áär½è6½Ü*A]ç)SMU²bí ð*¹Áíä}Ž·:Z¤ÛQÃð$U»/LÌ>ÛÀv­([5€Cyiü$Q6-`.Ùó–Îk*Çìf<®€7èeë:8´-œf/ F_£khÔ×G½7<Ç·aNDë±ùDþŸ´(§ìZþ^v†±±ÏBËÁ@\Ê{Õw¼Dn³;Ùhräܸ/ÅãV·ÜžYÂlzð <´±oµ RrŸé3XÇzô%bD`5òJ¶ã²â” y v¶’„N¿äG‘¥‹˜ÜÜY(œÓöÞ>ÐæùS*ÓÖÛëþ›éÜR¼ô)ûB' ëÔRñ‰ÄYÈxgƒV•ãú»ù$Z¿žL´lÉà= ~ºÿnÜBM¦lDÓó^¨½Ý’l g’!õº˜]ŠdRE †ŒÍã'à}£ecèÕnL\rÿ›#¼•·&`cs2ù¯/úJVÅï1¬ó¸Ã4~ˆ¦ªvÁ©¨7ŒÁäÙôîAA"0G›J FÃîÛˆ£úC ëÅ¿ªÓIq¬3 kcžVW0çÂí8´ËŠü4 ™ ×`»‹.˜­BÏß— 7Äf´I…øÍ_ˆr·[££ú "²2‚f²{C¤±k…½²n|ß+n1s½7ü¯ÿ¡w_a%ÇË“ÄgW0r ’`ÒÒGL÷ÍTåO.þ`Lý¿-$ý‹—àÊ%A´;5£êã`7€æIÜ…7 þ…BeÙ¨µö Ên´Å¼Ly8ës#«ÓèêÝ̬S”=üp¹¦…`ÙÌ@æ´¸½¤›•IüÊŽ<ë×ß[*ÌäA"Hz×yÈyêD¶ ¶à7§¬‘A¼–’`–/=ç_g+V^fëåƒâÛߨ¬¾„-Tªµ)Ù×À¶fŠç€4HØø¶®Ê›·pî ®îE6qÛlZÚ1„úŸÌÉÃ9Pùû³¨­'ô¯o%³îD.hŒªË.Ø+¤ÝÒõæ9 ›08=` áèkb”l23…Ï€º@•Ì} Þ¶•;ãª7è:ªÓZ m"ø y_4AçrúîÕQp½2\öfÛ5ÊpÒLêöŒp=ÿJ‘3ŽÓáe·ý¡ú{yTá¨É2ê) ŒrÇ#¬O¾`Ù ƒðtvE¡ÖdÍ KüØyw²°þò£û”ݺŒ²üªÙà"™ÃÊ6N¡ñ=朽ÁâtÊý' ÿºŸIròÃó.ÀTÉ6vËw#r¤í “rZ>("—wªÁšµ³Á÷ߘe(9aÿóå‡à}x"›ð÷;5YmE#vÓa×8’GÖ^T¦×“2(g§0ñ\ƒjŸg‰cÛ #XÌâÊ(û‰wTËÈñ›+!ËUÔ‡#…gŽûÝZÆ9C­_[’kYÑôó.ºüøT¢p¬jyª¶f4W!†¬Óluç"„ðrÕÁšŸ£™»Iv‡?-¦ZAG™ßç±sàÖ:…~¸óu¬#£ÓZ@1¹ª’\ȤOñøòñ øÚ܃–ö‡hiå7ŽÍü*q #õoMÆÏiLDþªSƒÝ0߬€|Sc_T}žÀß ‘ %Y‚4ýìA†¹° Ïê K±p[cB^M¦ý<—Q´mñŠ]ÅÈyþ€¥ŽgˆM+C³Ü¯ =‡Ì_ýEma¯pNïš º™ÿ Òg |¾ÊØö‚¬ÄN¸rz}³Eˆ”تÁñº Ôr¸méWX_(MîZ ’ÙgêØ%Á0)î Þß›Î8/¹ åÁ¤yí H+Ó£âq¨þI…À}öö‘ ÃÑ…ÇXç°×Ìí:]øú§§<±sðÀû¾|´‹ŸWî<Çó8ãÚPì‹9Ô_g”³í·3ñ¬ à¿ÏåG@o*?õ æR³œÖ[ZÑyFØáQŠÊ"À}`˜17e£Þ©âÌs 8™ç¸Ls—9î%µ¶ßPäÏCÈS¦!Ùýø´|y¯Åòwg³Tq¸Ì˜m¢1·Vq”__‡Ï'h‰ÏGÊp n¯a‡ü:(IØú ͽWÚg"ßæ°÷øG(´ˆ£Ï*éãX[ ÚFd‹²Ú-OÁs7]ÁÊÙˆÜ=Õˆ^ßåÁ¡¹…yØA1II‚Þ9QÉ,=%JT\D©Àï' {¸šýæ$N Jw½Ó¯0Õ ü¸Åç%^Ož7aÿgïjØ×#ðgã ¨l‘ =-uàÕ¹‰® ~/¹¥0Ãt&­:üƒ–*°Ú}ß0ô£Þ®¨¢ /ÅÓ/`ëéfÚ?r\çÇ!Gƒ×!ã%kH¥väH'ØØŽk6†É]D—'¼CcŸýðþ0$NJãv«Ï¦oZ¾°%s»ñý¡X¬*øacq‚‹ƒiDÄ06‚Y¥ êmɹîPZHçUöW؆þ]Jü Ÿm½ ß¹ ²«o2+e~råm¾¡ìª4x=ÂKS§Ó†#pðùS†,Ô¦!ú2øÆŒ{¥ý/†ò5ëŸì»Ý°—'ˆnŠ}%SÊ8¦‚ï8­EæðK×dò}(Y;™òw5ÀõÉ Éƒ;Ûð'w!5P‚ OÏ0þ/Ù€'²4(N¶< ûX52ƒm€7ÑØt¡ |Ì÷{æsŽÍãv˜gdHi͔ʃ•W¿÷“U>¤ŠB‘P{ÝEEk¹K½û9S’áòcóé¬+w`Ú›pÖ3” 6ŒÎ'ÄΛlìvdÇØ0”Í`ŽvE±Ç,¹l¨ÿq¦îÄp©´$©&úôï?æØ=µ%µoàû¦7~ûóŸ Â6ƒÝÓjæÆi~\ÝH† HÌÓiðãT&q°#‹fÁž–Xú}:•³ëÆKNƒïEØQ–‚ß©“욥´ßm³¹vm[ÃCï+u´üÖ“$ë×>eü–ƽö籿?çMâP—¤fy}¨9srêfÐâz1¢¢^Тó\¢¹ÇÁhVè×¥aÁ¬0äWÝŠzûβÑÑÓilÜk U´ƒÅfšôNWæ?~Œq<ùøfã$²ýä/,\A]½V„×Ns@Äó*Û”ÈyŸF/ªIcëˆq=L2á´í1Æxç^r’'’FãtA]Û"&äq nÏc¥æ‡¢Áù%pÊÎ*®7@Be1ÆY©£»¦ÞÔUõþföio1+SÆ3aÿWzO&y¢6Ò_G…áo˜Õ4nˆ’Ë’$¥xWü£ñÎ×'„ÓöÃЯËqÚD׬øÂÕ|ål.ÁMWŒQ{9ºãÔsÎtï§ìu›yø7eÜ'Ö~¯“ð»ry;ÿ³àJJÆœ€¢oÂÔû·6éòâ£{ ºðËe ²q—ÝR?—.¸ÃâœH'ÒêÆG”üž 2o VD?ez™Ép™§^Ûç _ÄLØX>«JòºÞ"‹‘Ý‘¼–€—ð^™>]ª¢LRü<±•·ˆ za‚Í%á|ÊÍÏMä—m³À5§‚“>‹»Ãîâ‘‘J–à sUXœ¦Ù®ƒ„ƒÕøG¬±EÅE;Ö ?SVùp,¤U\Ëë‚De ª„_‚ÈÓ] •ˆŒg ΔŽ;“ÕhÈœã6a)ü›7ý ª\“ß¹°“ôÝhƒŠü´ðÂ/Z\îݶ&9 DÖ¢óÖOl—ýl˜3å˜æ73gÿ±*öP!´wÍ*€œÖ,HÐä êá_¶vW1äø£T·8\¤{}>¡‡Ô¦-÷Êû9óŽ+ƒkÚ øå³%vibÐÙ üMu"pò°…0³Žš¡_w1'jk+œµúÉ(µ ³Âv‰ø`–žò…È1wú_ó½ò(Àí/PSyöXr¦<¹ˆ!>p M1p·-î(DÍõÉPÂI|²Xó÷13‹ƒÉ$MZFsP¯“ªûžrˆì<ÜÓútkÓðÒe;ò•›Æ¹øìû^Ú†xéF Ïóvô7ð$¼G‚ˆè|=ª C¿솅¿¯á½Ú+¸(«›€w:!«Ò WJÐt%~2þŠ…5Õxÿ (ÕËPçÕ¦äÐÊÅt]£/ù²®oœ¸þ`TˆVOÝ‘¯pªÍ'»öìÙH8ë1›Ìi¶£-ß7ûé5øõÝKXñÏÎdP9ð \K[Ik.fàâSÂ3ö Õ§~æ^žÒ m|4òjƪu@6ÿr¨hˆÁ뚤êÐ'/¶ÃГ,úñèljp8ýÿú“Ý=Ï*-‚ê«#Ð(¥A³˜JÜ å=K€/^Ž˜QŽi«+香+Él‰3è›d|­–kâ~¤/*ç½§£ažd·5ÃÞûó•Aueòw—ž|RÆÏ£DîˆÍžaD~z|Ïu̲ æthùɉë¿*ºXµŠ¬«‰zßìJg=æÄÉ‹o[‚m³Ìèš*·# ×\ Aá‡Ø!­D»»ÛÆînÈ-ÇÖ üÅiïÑS<6Z=ƒÀí¥¸â]1n­Óƒû½ÒPs? ÍÃ)jíÈÇóa†œ»îøÅVžê ÝF³´s¸Rʚ؅yOß::§¸—\1$wÎ%0&Ž…8Õƒ—ü[£ +ÞÆ»ÖéìMhš3û îPc`ˆ‡0¹7Ž2Oí1ÿ„rVN2";}‡KéxåWló8ÍzÍÔ'¯.{Ã,ï<è7yÄ s šÈÿGÝ^1azÊ6 l¤®€¾‡ p6ËÑ÷ëGáb;*Y¬FdÃüéâ»?À½LŽ Æ@ùÝäËÏ!40> ›¦iù;\†ÁÛ;ÀÀp:S+ù„í™ÕÌX¾ âÔÝŸÊŒîÂó&4ãÄ4¬ÊÏÆ‘xx›OMiZqO‹™Í{š)“Í…¾\i¼`æaÝáé™sO#w<5O ãÇ“iS¥ØÃI3`´,ÞncÛô0‹p¦ W¡¥Ú%Fôæˆy‡szÜÀ»;øøg¡zÀd”é†-sT bª joÞË4_^KßDÆLÄ¿¬Û.ðì|ŽÆO¤qñ!ê&ŸŒ[ó°1ÁiðÍp ,;=n®0ûö¸g¿–M†Û{àˆ¬ÃÉ·-èÁì À5êfo(ÐÓ±ôD×mdïŒÀ6‰fXoiŒï€ò7ªôJ‹ÿõb/ŽÙóïatîoèNNG¾=L l¨*W1uwñÚáypí/ULy åN™ߧ¶,}‡…JA^ašZ³î_m0ͲŸ¶žáìQÖ§mšO~}²§%Œ½˜°ÿ(`|”Ï0-ëɲ׫ämŸ†mû1(x™sgñÀËÎfm¦)m˜™ùúÌzŽKÔèÇ? jÿOö±ùkx^¬ˆým¥4æÑ\xöð 8io&-ËRØ+‡~¡ÑlBgzì¡™…³ˆ”‡)V>õÁm¡€§6ÛЈî!´=Ú²ÖrTàŸ95ÓªÁS–«‰ì}}<«~„ž Ìç1Oæ­«Hóñ;ª d@Æ)®ˆÇpa”ÎÁß¿ï²÷auÎú€8™’ÈvûX¹IŠ‘×u;ËÕØµc“©G sbA!žR(™¸þ)¤{<× GÀILÝõFJÃ1+¼Š»ËÂß#aúÔÙ—ÑT,k©û®NŸØ‰ÓÍ‹@l—<áþm…gBªÈ.gÁé'c¦Ã—[ ²î×?†ôOljýRŤ[¬øL/ÏÂÖ/˜c‹I¯/bçõ×8‹)E•ù¾4zGgÕ³j­/¹÷‚áæ×7ܶg–+žÃþù*ä„íW–GT—œ<zs'ðwÕǰ N[Ù°f9¼üšCÜ}ä}©KݶkÅavÞ*Gî>Œ¿½.»`B]Ì0¿Æ<½Ÿ=®¢xßÔ„¦_ò‡ß™Ùôíwiš1ªA>â]˜:ú“}ž¢(_5ÞìKaL<¡n4 K¯?Ç­eò-1ß0IûòÁï (ÍßÑ /ÜFp÷Ó~F‹ê3Нããè<´H&´üŸ½jw³|E‰#8“;/€i,q݃êÜn|#٠ݲhqö »øÅ[hÞíÍ6<Ã‹Ž±;+ßcûóa{)Ewù¡BXììš6Áº%VØ2Weyè»·[çsÐ0ÄÞ¯¡ Ÿ3M,üïms@.’Å~^¢dh «±‚OˆÓãÎ%_¦©“ˆ ²ìÞ;íÍ&y+sZ ¯Ž`Däã–˜\ü,KNIÄwÂþuÃo'_qÛ ¼ŸoF^_7šÚhMýJµñõ¤ös]3ȶÀ9Ìl6Ï?k¬×#¾¢VIàz%VÖ©@-çYZšDø£/à¤e0©:•0ö¾L¬„Yrv6_áà®§‚Äç—!áæšáÊDz¸~¡[ÊKˆi2OÇkÕÿÿ˜pËýSÅòퟃº`ӳм•[…~ÜDö{ Ì”9ØbkBù­’mÖÆßÀe“¬9{˜éOzΊé„A.Uõh‹f߰އ Kç’x”¥;öuâqoax°G‘òˆ Œ¯aÄœ:€ùš¡äÔ‘h8Ÿ? ¶œå%ÌßÕTø#™g+@¾®êýo/œ@§§ð²­Ê:ؼý‰pÎIÆ<ÉÂÒ˜Jy}ˆy:õÖõIÀ†7¢Ào>Ö4øWt/YÖ• Ÿù;Á+é\–åfS4:G7CÆ;-¸ÒÕ÷Ç”q£—ôDþÃw-¬«ü&ŽúÙAæÐŠ0ã 'j5+b¤‹·¡ð«3¸Ìã4ÎÞoE¯ä@MÆB×ùQYrp{Ç^¼Í %Ak"÷½%ÓOÁPÆ &³í{ôˆuº‰é]ðwc.:n9Ï<ùõùÈ5—f<\Ð7¡–›zp~˜G!“—¾-DZ¥òd¥öT4WÕï O ,žE}ßF0 Ò9ðÎĉ¼Û½¿Ž‚³5âq =WÜ)jIŒ¨„(5:-™ëñ•ËBêÛåIXNÊ%ðàû£ELÔÙ.ÎX®^™F>6m¦êì$R¼A9[Ï ó2Iú‰$…Yƒžþì5_¸¡Þ®?O㚦¶EÜŶQÖu\ ¢ÿž¨¯–UÃóš$yI%Tœq ¶&ö¤x§åð¯cdFËaIà¿m÷E0Ôâ(Ôš_c«ØaœÛOï]Ì'ž6ÛZvC.Ç£§tãa™EÌý7³Q\†Á‡cbä.?­0|ï¦qÈå Eòi?-ý£€M×Ù£…C· ulþe-Ê߯LtgÓùÒ;ñÁá躰ˆÚøœ/>̯¿ÀÐ<š8sy“kŠo×5Q©™$U%lß"‡‹³gA î°â¥wz1CŸ^Á¹¥ÙÈP´(1¢[,«Æóš)t›Ac/û±æð^+‘yÚs[!fy8-XáC/ȵ \Š«Áœ¤—lÿÇçÓnâ ?/7èöLÚsZw~#ýEWÙ|^Zþ@›­(Œc²Î÷@öYj×µ îšL½åí<"ß6]„ç#H†°4L{¯VRiaZ™+HdÕ¼©lÑ’›ðƒS,åã\¶îk",וÉIÜûOM¡¸aY{V™Ö.ΧΧKAï¡‘… ØE„l“g1ç}B÷)“ÑÇŒ$ìŸI+ŒŠ©ÑðêÛà‚[ýÉÙæPëWáðtCò&ý"ö—Á{K:ÐeD ¦ÛÂå¸{¬`Ôûq•"Kc2‹8† Ü‚û|¶Nâø­\Í1mÎ&¸—ØÕt ì;–íƒ5XÖØ‡Úž2‡JçãŒÁr|sº |¤¤˜½‘‚¨²C†úoKÝ/Mé¡ã}˜“Зá\{%rç£:î›DíïÌ ½Ç›ÐÿÐxuˈüÙÏ2¿ëy`is=W ú¾Q°kîað;w Œ'ÙàûsÙÓÁ›!Ÿ#Iä_ø³+_Ï2ÎÙóê &׊gÛ+ oë[uk<Îþ}‚ß…ðá­ûðŠ›ÂŽ>šJÚj¸sœŸ^‰èÀ ïa©Ä9v ­‡·ßo°!6ôßqr¿“n¬ ÏvLðÿº¨<Ø’ËÄy§3 ƒ&Ó?+2_ÞKœÇÙ'@Ìi z”…þü¡ã²õôíd#S8ªeÅxür*û:ÆŤ‡¹¾¼óÀ¸>ì+~ÅFÈÿDÿUav7ÍúîÀ}ËÊK×ÀŽ).?…7,•Èõ°~°ÈkÅû64yE'>åùˆÏ»‹ðnÀw8pÝ’û»hõëxP]ìFdÃAâ•1ÉIg{ßÄÎÉãµ4¦nšO\Ü1/N‚êL&Ízηl,¿ÙÝ»¡ù¾=~Κ=ÓÛ‰ ¦½8pp z=Aõ71º~É„ý§Þ²ôÇØÿCV 0k¿œ—*Ôr³%ÛeS×Óî±w•poíË$3{#‰Ax&d­reUÚ·a ÊÅ5 ñÉùšËqÉìyïM:Aݺ˜e*¡òÜ'x¿V‘ .£ ®£mçT¶1žÌ2’¤_gÒ•âXÑd¿ï^C*³ÖQ]ÓìhX,³,vÝ|šÎ‚Wª+¹2­ŸóCx7>|œÈêY2!Ç ï™+~o¹Íª8?c=ë*ÙÔ ¡lU½Ô¦ƒæ“á/ßôxHjU-É–(uªaù“§8aÙ‹† öðÆoDÇázØ2ù*¼ø1¸mÉTóxc-#C”ßð’ Ý•ä [ÚÿhÀý}çY³ô+(6‡$7%``Ù::ÿ:∎жã®(‘¥WÙÁs;Àã»2}år–‰+ëÂ×Áu¸dO(ŒÎЀë-8›Eléö)OÀ3>¿ DÀá}&Üá«Çs½&ÜkW§ÿÍu¾cÊ ·0‡ô¦çï7ñéúä³1£¶?µHpM&6ï Ç÷ÃNؤ¬I3œØ7òñl>“ÝÚZ C®_Ù_ïw1ÜQ%"2Ò ùO!ï’Šžö¿Ï?'çàá“mŒòÔXÁ²uÖdŽÅaX´I›|F²ðÕdz*l5Y¹à*ùØNñòÛ‹ì5³Ì‹FaÒ–½‘êÇ¥B™¸}YIƒ¹h^HŠîg³³GÒÕ+òáÚÊI4Ö® 7Û“ Ä<¸sJ%XX>G…z4Æ3Û+ŠÑ\ë½Ðø¹&ŠC«á¡‚<­º·”ÍS;6Êÿý;ˆÁäš8Î m§NCä‡ë-•`IØâK6'“'®éè;{9õÛ-K¹¿ž#«E”RÖ° –ÛÉ“˜CágH]M'‹LÞbQÖÿ>¹Ä]»ñ+CtU“¡xl½ÐÒ¯Ÿ²—wϤ’­Ï°Æô™uϤ(¨>W‡5¸(YƒlæÿÚ`0þt›ûÕ]‚ôí­f·ÜuE»+ÉhtÙ*© ‚õ¥éKN£{Ïj:i@vxÁËãOá‘ï-X×%H¿Ò pz÷ï'Í!å.J ¹(ÈܸÏ*Kåp+ìÛ§[‹‰WÌÁ¤MíxkЄˆžt¢5¦kQ2׌âX :æèÄ£ä±M8qááŲԥˆvüžC´/ž ‹ögãÝpŠQ âÊ%‰Kx÷B6/Ø5¡ÿw}Ñd—û-ªÈ]ïFš‹Ü©|Y<&õß\g|ûŒ—[/ ßû®@Üæ3 ’ÿ…öƒ˜ôÕôñ›z{3?-úèŽ;^áãºPSð kÓ–Œ×cšD6ÿ }€«µnT=}M“ÿÂ^½ÏjE¼ã|kÑÿ§¹à³v))è±g%¯‚GÑM<#<#Ë—Çœ‰‰ÂU/²ð«{™vvzÞ|ñ+m¡yèµ£œóß¡µÙŪ¹Ú<ÙÙóÓál´>–ïçП*­pÙó FmŸE¤^5caH9öåž$>bÊù €?>¸Þku”÷¡M«s 6!JŽnåØœ.‡Ù/À$:0)WÝhmx'ôITÀ—ôÿz‰ë]ëpfûRq° º ëØÇꪤþ²³D]6Zž€þGÇ0¡ÕtvñÂ_’Dd †;_?zzôáð>Rý€™vò 'Õ- ¯F—”xõª&î0ÎÄÿfMßn˜BÜÚâ^ÏhxI!Á.—Ý©†uK^bμ™ôCù]lï®à®ÝNSo`òn;Ф‰øÏ{'¦Ïi€º xŠf3"#:4ãÐcvÙ~çÚMø¿rN ñÖ&=Ðy[ÔÞ‚ë"zKÌ—ÚíùˆÿÅ_˜»ÿ²ÿV݃ û1$Kl‡´ˆ1,¤ûÛýᤞ#IÕ醅`VÙÝéGgéOÀ-§Îkx!ZÅš¨¯â¥§^¦‚Ñ,î˦Òs÷•XήՌIÑZAª™ÐãJ7!YÞ›xI½Oa-¾…CºµùSåHåg¸ÓŽÎm´ZòØçŠËŸ=eluõA”ZpB qÎn]ÈVÉfM‚÷w/úðæ4ZÌpØßÛÔiäA9v©Ù-èˇÅräšpZP£{£¦ÅAòÓÔÒO|ƒ-3C€W¾ŠñÌi€ÑwÖèÚmlS¶/÷]Nd'O¿Ž…:7p`:…þlæV¬4Ξ‰¯¾È‘š¡6´Í»†Ï2ݼ'@¯q2ÎRõfÙgÂò4×ʑƂ_Ü™‰ø7M\ŠUb"¬êù)Ôõu<zÎF/£’ïFàÑY+Rù㌹tÕ\G$¢äoW ‰ÐŠ¢§Ö¯§å[³ÊB²äàîÛX_÷ œJg“8ï@°|C4[ð°´9!ìuèÓ³uî^NÖin£Fc$§ù$Iš•@בiô˜&Õ¾÷ÖÛ!5¼Ë/ÛºòW8ÛV ç ¨ª•2¼^kÃŒ%åG;ÙÿγwÛbÒ.ìI¢¬ªIÔû¸ê<ƒ¬-©ÅW†DîÚ)rà8™[÷ÞPGô–½Âì÷:0Kï ¹I3A{Çk× ÿ»ÿ±ðñ–æAS,’¼ˆ ™Nľ[êÚKfCª‰4÷Ëæ/¤-¨‹•¨¦³ÊÀª‚C¤ƒ²0~R!¬ŒØOò®-%ÉóªÁkï/8²„—þL±‰ˆ&c+Ũ‡Ð‹QTh¤¶Ý˜Âžf{qÓõfÙ±Z£}Þo^‡¿%Éó»5D}Év’¸‡Ñà:aÔôAZê(N7k¾@E³°Ù~9uí½rn5àÝËܯÔèo¥V¡Ô­Ù‡üšoIÍ›'CBv O¤2iþl]yþë<}å‚ôÏ-Y÷¯í/·›Xëx¶_Ëbgž+¦Qü\&-c7y³·v(Ž’YñØ~‘B3€ÞÁ×™›ä5uz¯GíŽì jùBôêõãèÜÅ@É|oxk¦„|†ÜÄv”¯Uði?N[BŽAÑâW¸sŸ™}A ?> nMŽ%<ªäX“ ›4÷>ÌýÀÜ.P#¦ÇùZohˆÚ†|Ý@ç>ÚI·¦Í MYÑôUL ¬\x…éÒп³âÿ2=ã'ñt3²ñ.„ñ3Ø¥\L·ŸNƒLÉ/}ì7;¬v õÉ:Ýo á”†žOPp1*Öcõ¦jÿÄ€yYÖK‡É¿¢OX†EøQû zÿŒ>“|r7lI6–¢%'†é×Ð9TêI³ÖaÚ~b€žY4žóÏZ3ðÄDž쿦‡5­IiØ\zâ›8-œ¿ŽŒÝÙO}ÖWßwt\«wÂl÷ûð¬Á”íT¡TDœÙq.ïQ%¼²PºAn8 ’ ëXÎb™Ý-Ñ{+ѱõu=ô›ÄÖµ1ce^´¦îã„ýUœdXóÊT6æ®'ÙÅîÆé£³˜ƒƒ“˜C²YäÀßeИ‘È¶ 3Y7,°Æa-Ð9Å͘í@ ?×ÁróËÐùû=œ 1£Ë’gâ&±|j:m þ<Ïï÷@ùŸ~xñÞí¼E™!pmÕÖõyó,”ŽôèSº éU8wà Ë3œY4ëy á ãßP!zgJ .(°'ÿ¨/«éûÞo4Ò\šçRHI©{Ön R¢!I„”ˆæQsD*ŠPiÖ€ºgm$2GRDTÆd òïó{žß§ç<ÝáܳÏ{×Úk½ï>ë®Ó{?‹½ÕÈñFÚ|=š¶½em˜OGOÂ9¾ÓXážÃ}àÍOfczä¿:LøüÓH˜ýrr¸ü-Êrt‰P×qLØF[¦7CšBÊþèc—Y±\Ìxœú#œmki凙hì û£1°¢Õø ¢“£¾æ 'âùbè0–¡_ò©ƒŒ“róå@{”<.(³‘¨TË¢Òò¯ÌJ?2Éû wäÏi0ÞLn®¹ iäBX³ýÏb:÷c8u›ú‡VŒ €z yÕ+"ãš2q…3½sä V!ï¬î`Éšj8½çöYžÇ#%÷qýßG ¥ csél•¥äXJÑÊKÅ?Á·Ø>=g:uÞ?œbð|õ&÷•Öl´s$ô+æÁ|‘ÿî«>¯ ¼žÈÿBVÓ™Ù‹º˜ì^Sòsv –þœL/‡r÷ü0ž|7Î/uZg,¼üÍñù¼tlþkûÓy'yþ²G¯ â¾?K¡æK, èÅbf§1ÆG×Ñ@m*KáÈ£¼¼Ž‡ž\r[4hÈØ1”Ü´ëKðcɘ&»œxŠQÇÛÚ´RE‡Å’Xí½™L!8{…ùÉâ5·!M©ÿÚÏ!ϧlãê}ñÄ‘g»ðæœrVt¹!sþñK´IÃ)ÂäWÒSVçŸ> t;G×,\ºo|žß,…ʇ? .ò$>› ">%°ÕoÁ„ýõùlØ ŠÌ.{E¬ýv ý·ñbòIlÖ'îG,@Åç Ëf…ÓŸ² ”ŠXQuÊ ž°!+wJ‘G§°ffðÅçâfã_ŒÿCêk7‰XþbíÖÇÏMÏÞF®É”kI#±F¼ŒÐÝ•¤·GÓO¨`m«\½- û“Ïã×Ü›pÊt)†OéÇWߘ™?!N[ ԭ͉Ó?cÒó±4fÕ`üÔKŒAú#¨xSÏý!¥Jz Pù9êøÌž<ªHÅýÏmdppÝÑ:,o³%Y«aùsü@%¨ðõ×Lx?å]Ý<Ñÿ§(­×—Ì¥¬¤8ä-ò„ÝŽñôáX]´Ï•È,ˆ&Ý÷븵‡s0êd˜ÝÓ"‘ïøiâÅDr?|œñƾà.‹Ê¡Y'äÈ2-dN—GRÏÑFöØ›,Ñ‹yœTn`|4ÏÙJµÄ¢ù4Š·3ÇcvÊN ”Ò‡%êMÌí|18ÝâLõ×…Ñg bÐ1ÉUáËgÎÄCdõ*M?e>€ñqIsM¶m>ß<7 Þ§Gî¡J,’ÎÄ_w¡ü÷p²ÝÞt3p÷àØÊÓÐÙ´Á.Ž,RÙƒ†yÉ*±?orï‚ÒDüɪÀ,ýTFÛ)I± çp"Ìâc>n#‚äÒV 2ûõÌ­@|ÐDâ#âX…9ã"  é@ÛmFÍO‘Æ™'ã^û ª6ƒ\äÓí©à ¾ î—iÁ?«é~+=̿ƿAϲôñþ|"Þt†nýuQã½À»°x†¦Ñœæ}ôZÃel²xÀV¶zõQÄÛpúÙ"ú¢N?í'Íj\õâ!Ξ⇱hø¦HúlD,é D» Dé2?”Ð#§hÓþ«k•™ý]úÔPìœüµß,‹í¸âÃ4œ!z•]}³ƒ=ÚW€fªÒ¤Èåbv.î4Ç•^±ì{º]ëqNïk4•K>0"'Ö]f©F"Ó!SºâÈílC8yx' (Ü@NÏ÷àto¶Ä{3kQ>o#JŸü °ñE ¢ÞP&­,ÐÃü»3ðÅ…”\ÿˆ±;2O|Èþ:ëDËuGÛW6X–8ÈùîHüÖtc“~9®U\Œ·)Ê”?༻¸Ÿ–}ifª~gêu´­c„Φ6¶¶ø]½=6L¡¬­Ëýßïï}ÁÖaUš°`3ÖX|¶Éÿ»˜x“Õ4] –Q¿vâeNФÈÜZ¦ŠGƵî¾Ç•Ь¥Cç°ÿ‹6Ù˜m žkÔÈ/5r-'á»®ÂÚ9%¡ûÂhx8u:qß?›œ3Z…~Ê¿A`Ò ²g?âN†g—C¡&QeŽ£›²‰2ãßé…Xpµ é`®ÊW!7?-F8hÞp†+0‡»{TÉËy1P·+ 6>Ĥd%¬zÊiuƒQ·M®`ÉÖ¡£åG4,°‡T>^Úè1ˆWjnàüÊ•ä’ÖÔ¸šƒþ?÷°Vkñôll³ñ×FÒ¤,G‰ŠûËN]} .¾Þ‚UW/Là7-cöO‘€ŸRI˜)jˆ/ ¾Cè3ˆª d/×s-ø·±ÿæ—3WÇókb—?‰Ò¾a:´·þ4ºc1LŠ¡nË€7à,TåIC‚Z åî胵W†™Q²jg6*ØbÈ®6Ž—å9p)ÒçÍòÔì|5æéºef3“cáKÇÇä6iÅÓFoØq7 7ÙD•}ÐÔWšÊÿkÄÖ“ü˜³¿ö,ã”4 ¢Á²*P©âƒ¬P ¢sùD}x ÍÚZä¾ùYFáN øˆ—°/-óá—L{éÃlŸÇÉá›EFOOäÿOnp–»gØò°•†#Ð!÷wõq™…_PÐ'/ÄÞ`¦ºw²kmÍÈBÞIhgê‰)Ú¹PÔî]õ&”>ÇĘË8vç ¬8Ò…–€îL—ÿ{ßvÝ ÆÿÕZfëÜ' ½8ÊvPf;Ė߀i/.\R6žW2&3Ÿ&ÚŠË`ŸTvC¿ò%<Ù7ÊÎ/ûÉi¼<„„kaÇÞXT~s´ŽŠÃ×Ùið§åülápª(›Z¸•;`WÍHv1^»—†O –}Ø5þý½Áªë¹ª½•бoîý!Kò>tâËSrñ³2æiÅL¬ÿÝV ‡NÇ8Æc…&órK+£½®‘<>/¿+å¨è‘c¨„ë{åá–¯7KC#É"ëЊ§Ž#ž[{‰I¸cN‚Ô”‰üïfÜþ¢yÖt àÊcð×€-‹,Á]¿€»\Š8ÄMêOðÞ’n&!oˆíìì‚Öëm6–`ÐÒ}XÿW‚ZXo¦ßk©ù²m™Iå!yš£XºZ‡*OgÅ|ùN’ŸÙ’ë÷8¦“TÁF¼šîW‹BëÑ[ ””†Ò|4êTŽãAþaúsE -¯ícGe‹Áav›œƒŠ1t‚ÿEU8¹ìª…aÀü. ¨ò`#âæÑ£'’pç©Ô7deÍd‰¯–ðÍØŠb‡î#ï"b?_™[)öêb´ÿ(MÓû͸Ã/ˆïº×8¨˜ ¹Wqó zÎî êkg;*!b4è_Fœ@ÿásNo õêÜ ;?‚/Ÿ;Só8<ØHeßã°®;gß Tëºú±fX¼!¯;×¾ßzìØ-F;æƒx÷¨÷:É6Lÿ ׃Õ8n?‹Q¶‡lf$è“KIà&â@2Ös93J‰ÛÖZè1ºý| NüÇÂ:©%ÁÁîKÌÅwΫç¾.®™fMxà{ìs>u9‹ª Xw*Ÿä·ÁØïXb  vøþ¡}µC„¨ŸÆ|Úx3ê7eɬº'D§ÝÁÚá"¸¨ÅKå_(Àçå‚Ä7C‰ã)ó dPp\ÈüY“«á9–¨>aÖdo$Ó7Y©õ:(hÿ’)Ód/lšIŽk0矱]q;Dè Ù~W¼O½1yÛigB,«ciC­»…SÆC–å|Òè¿™½g(rx5¤m‡ ¥9(ö*æx?öUHªý0áÿ5N±÷§Þg2qE âÏÎøàJ|7 ¢q7Jä†ÁÙÏS`Þ&;z½j•àÝÒ/yùÖL‡dIg¼ÖÊúùÞdnvJ£Áww8~q*ú¿¬C“&CvfO0‰Ÿ› Çõ§„&µ:»Œ^ØÏG6÷M¥Šoؽ'ZØÜsd²Y;äîå¥M|‡p)›3ä7ApØqôä{Ý™·™[îwÀaX€ª VÀÈ‹›p._j"þ›|Á7³ qÈ"½t6Ü̦¡Ï”dô­:ÏîÚì okÒ°¯3-Ú:ù‹^0©ünèù¯èËÁí#öÔµ#É(‡,¨óbþ|%5×䉗T üÑY ~ŸZ¾‚syÛvÚì0RK%èóAF×å&\òdCËlíøÄq뾓n·Åy»úpÊÂ^æ|ÊTب‡Û%Èg­¶9Ô<]ʾ=sßöí™Åœ‡tÀ»üì«Haú,^ÙÏêåªÂ¢hCV‰qÂ=[-`äÓ7 T²Ýøf&\’xu™*˜¼ðn¿uý/?ÃZ÷Å4Âý"ã~~![ù²™áy®Ÿ 2+ ¦Eós–w\wM­kÀúSר3y¢ôÓfoöëƒbò×™^åìÁì¦é¤Vý8Ûÿ(ùûS +yŒ½p¾š&{ïUö…B{®øóÓxfÓhô™ÓÏy^rûšáþ¦|Æÿòj–(bgÔq¼°÷n[ýÂøm¹†6]Öýoùi®¤5)üŒf#•¸Üä'ˆ29Š2 ;u/ ?ÂÞ©ÚÔï¢ Ýq–ý=ÌìÐC¶£Îž¾ëj‚…V%PpÞ6ouµ…àÜ´ Ú7aW8¤Á¢y7ÿïqY§9¤Ç…[A#, e7h ý9PÌð…¡ÒÄÌ6£J£qÏÙ ²½æ ‰µ™BN&AÓã£x¬¬‰ÙšÊç§‚ùÖÏàÿ`¯S@óOpÕÏœ2'nC“¹!ñÙ+I– 3¿ÇãbÚíÛŒsàU ¾ÍK:ë‘Õ÷äH¼®ÌÄúÏoOTèðÇœ@ozB¯þÈZZv_&ør®@¯ÿa&å0ÃÈû¹Ñþ-ÖçˆÙ5}¹v(#]9¶WÀºZ! ÃC×â0=!dÂõÇÚŒHBûc§]5ÔK+õú^©Ì-G›{!êûR:øê öú-åŠF¢B07ö¶ Z‹EµtßšuØn@á@¡*wAùvqp=n‡›=Ä-‹2èí+ª$]+ þì!%Žû`èã"Ëû„ñ_šF¼·¾Džà‡Dpfb"·i …ôN²™èrf\‹Â¾KP¤çèî3ǯ!Åè4/I9³ 3iw´LV!âO}ˆbøxžÔf2™œɬõï$°=æ Ócí3é‹G)xØQ†9|¶••:¡Mß'*Ðã Òô:ÏðÝ;€Kµñc'Þ<ƒÏµŒèαŒëˆÄÄ ,”WJq- è+·#  A¦U)ü¾¸bs= fœÀ  .ƒ84X5 ]‰“Û[Žáþåö¿?’:‰:ܸŒ[–‹|ñŒÆ-EæzãRLˆ¡©³KAò¹:ÀK Ñ•dìÐ6Ü *Ø P;¤K|¬2!Wé9þð’Ã`;VtÞ(ÜN¦ÃƒfÔûØ[Vå¥<6-Á×kTaì—6yܹ§ê‹ÀÀ©?àÔpŽU ‘Nù&81f@½ùÔè‚OƸ¹w*™^!o.‰ÒöýÏAe¹9FÔaºùj6¤È ç¬ñ$3çÁ»Oo˜Õ3¯ÃÆ®eè?Ušpr¯Ád>µuýU¨œ†BJtaˆ©¯x Ï‹s˜j“äÚ…ãö_qñÎåDö£©/jõ^b†Eé•wð‘G¼í”#CxùD<ÎOB}'>R¾?:_ɱB‚%¸þà,;™AT”Э÷Õax‘ uK-…7¯Áóg xµ˜ .)7´ _”*óHz8†eµƒ·Óª¨ÜŒ[Ål,ÇŸ†þfBî3TtÚ(éØëEL´^UæúWäÁöïèCT0ÿö6’Oܵ~s §‘?!ÝÐf¾‘|{‘Èèoï‡Yÿfs§¼Çƒ¤éÀÎÃvȲ>’ngð°;ÕõZe”‘›(¯‘:除2±þ³gË4ºü?$·YbüÂ><ñ< ïßöEG¯IŒ]Y5»íbîÅ”,f†²"éïº?­nsê<@ï¬6&+aì¶ñѺa†ù×n¡Ä0’™ìlÅ6í]ÙIqðå’35Ú­þ/X\×yy"y k¬qÀË€æ¯þÌ3>­ªÖ•ã/áK 5|¥ ‡p艽£‡/¹sÙvXë“A‹bæ#å—¸,ߒļ ¡¿7‰R•7çÈú=íx~þVº·c7l Zž’‰lÍ99º?#š¬Õf¥dŸ¢PÒ Z!éŽrškhftÒÄúdž­’ ¼¿.ª…üYO8MçWÁ>‹lö6•¢Ëc™ãëÞ2f2…4¾X‚æÄ¤²d]ý4ÆBRÁaë{¶! €yÞ±ž;é{¿J¨Ùú“3Øç@u…øé³Ž!ÓÕ~c`𺅞µ°gꛘ]mShQê~ØÿR_J¼‚ÓÃWQÛh©é„×íÞÐ`xªìúñ¬K¤:¡B¤c LÕ–NÖ–'/ïNÆ<7†ÌOżvÜ®Îõà*”S:‚ÙdəŹÐu.ßYe –U!$2ùjatæÊw¸ûëwØ}ÏcÂþ½ ¹ÜˆJoÜæ=‰~œ1™|z™ÎŠ%ô1ûëoÁÍùÿ°2í üTŠÇèЬͰ µQœMîË¿Ç$ÏD8:i';º#„ÚÑ)©”ýW¢¿¼I·óCXÙ#Hûnn"–ß¼Xå!Qêßu f6Ãã wšŠ칕žT³e>VEÒ²ÐÙã¹å:”?´cïKƒŒ*:>.ûìZ ªîw NR©X+QÚÇqúÔR˶ý.¬@&u®¥Õ¯lÒUƒ1ño"j4xrD¤Ã‹ˆ$áF Éš½˜γÄÕŸ¢`îFKZËs E÷ŒÙïrœÈÿjöŽì–dd?—Mž@¦]û…š ¶ƒCŠ´âÏhReD,Û/3oË}×ô‰¹ß±ŸX=dË›•¨ö+3ºiz±—Œ¥¬}-[§èIæµuP‘/é´ñ{͸¦yK#òÈ(3õ8v«7góN•°°øÛM'~ˆû©QÓ® ® ó~~áÁ~FÈ÷&‡VsHòÊM̯5zô¿éн\‹rH£ëªLÑ|mOO”'²Çî+ÎÕLÀß%–ÄåÕyR<¬„&¦Ô’’"'b±Þ‚É{‹GöR¿kÒøEë`†™8ž”UÌÚƒwjÞâÝF®Á¯/¸Õf&Y¦Å2«Ü þº¹ rÝõ‰¸S¤@³–ƒ¨µBc—̃± °Xd¶­ù¼ç¨ÌE™PEbz2Ý>Å1q]Öp§^Uôç !®Û=Ïì:CW¹X‘;3 I¹{,ŽòçNË`lÂ>àº0Y4NYŠ­?×ááëÕð97 0%I°yîRlnØ›½šcýM‡îI=Ã]Ñ=[®C™«8!ùÏÙÈ9YùïUç$}µ­Vˆ‘»tð•JÌÓã§I&˜1Vw—ó1!vZ±.ë_Dß=ú… ø¤˜3Fî\¥B†º‰st~ºjÉøå4üúÆþÌ’¢ßÊÞ@ÞÁ8ΖØ·1 –žÖŶs!¬Ù]péò?– Ò&Énãsè “Ymå!ǘ%Õ‰üÞ«0£o³®ÚÓËËZÖÜ_†±’ÇÙuQ‹X/Jc³/ªÁÚ'3ñ"'Ž,ŠÆãÊ9(,´ä„‹Ó«9ݧ9åbÌ3f;;½µŠIUL¹¬s >,»Mæ¢l@ÙDþ;*öû:ObeÌ6½}/U|À‹K2qêK íñŒqˆ‘"$ÞÓLß̦Ÿ†—–•ôæ$ºÇf”în{fä!j¼ÏU A$ì±*Ñ9q:®í$9-| £ZÊv9£*%÷˜ßÆt™‡-ÍãËdoKG3< ñ·-ÐÅfAä™OÍ ÒÇñ„1aù£V€’Ý7 ù|¼‘.…·žöÌK9¤ê“-YkáDÉ®}tô` ^Ô $ ñîD_kœœö ,u°ãÛó¯¢ü5ô^ÿnBÿŸWÉb)NÆú „ª«9‘¡E~`qi_tÄs.·GTtÍl®?™•vóü¬½ÛÀ.èœLc¼·@PóOFtQÖ]BÒ« ÉZò“&ï…ˆ}5h<‹®X™·fjÓá±tî ›ó w›VmLƒ"‡ êÕ ®±ÕmN,®lºËÍèB»e‡Ñ4)6¬^GyÒêÐnê'î{^m^Çyªí×Ò‰\ËõÀ¤e­XVÕÊVjΡKR­±fþBȦ“Áë“!ývîGyýA|óN’–N>ŒÎnFäìþIôýõ}Ð{äíÄú—峘­2ȤO2ÁÕi¾Cù„DZsA>as€Û³„¼*d*œFàÉž  ÝnAN¾H züzÄfãf°¿`O‡J–Pns¾S=‹íz¤àlî‘ÿdº±r” †ïqUÞKˆ8Ã` x‘m[‰gÄw’¿·§c§° ’EÁ+æt¿Þlž$D¢%7àCÑ̨²Bm»ZvÈMºåë·PÐIÏ‚ s¬pÓ8O¾'¤Ž…“ßâÚ[Ådèãøg¶–ƒ›ÇALÝý Eºv±ÁÓ€Fö.FU± wñ‚’øÞ[è’©ö±µñËÿíxCžNÑ#‰[—­æxq„lè¡%WZP¢ðõ\^ªq:Jô9ªŠsØÇ§S™Ï-gáW ÁÓ'ð¬i‚G0IJ3#LÒIÌ2Ú=ÁθŠ‹ÕÛ‹:uj0 þÞ+óUä’}YTšÊž |– äÆäµ°€çÒŒ$²¤ã5è[8°×Ρ}ùZ<²´ÔÒqö·Mã9‰C,šŒÛ!‘Ø<ëuËû4Ë•|z[2¶Ò_s4©Åê-û¶ÄѧKähÉa9Z{š¬Ôú_¬+QWú¬ÔÚ…"\%òl-ã–ŸaccÀÆb½ßb_žª‡=}óq'y÷®ˆY»ÏœzflÄÉ~­ô‘¼8Y¥© œÕéζ|ú•¿‹t=É2´#›§cذ;r*÷’‹dh"þ;¨Õ±Á™lõ·vfÉoeènÐ%3*,ñøÅcÌ®Ù3” CòMc:\é†W>ðÓÀ΋È?µM,ƒYë'ðáój¨ æ%©ecLºv!êä­`cWJPë|y2¾¾¶¨Ç-¹cløn^ô¿vûlŠIÂh…j ~©Ižú\éq®™³WÅ´PCæóàÅ>ºÃPžü3ZDlÚ`ßk1*Ø`Lµ~ñ§Ðõø!Þ’¹ÁÝÕ³ Ñ[™Ã> èäHû/g)µÁe†ü¤y‹"{ãKÚ¸‚¶$?aÇOÂz¾ûÀUßN.ç`VØm|º^³?(keHÚî\%>_a$î­%vµY¨9UÞf³5EÁôé2ûåY¢·e„y' ûº•Ða,|z¸jüÏcßž!àÉR¤kŽ ÓS1瘜· ô±ÈGx*ÝÀÍÛOÞm½ú“„hJý96¬Ãbë^á5óe¸ÿš+IÔÿ -.N¨’}¡åŠ]óä)q±Uß×,-HºÆÞjIǃíàÎϱdÅd7®Tmx ùbŠ}–…˜‘«—É›s¸ÏXÒš€sì›ë´Å­™{yÎjV}ÙJ—ñcþqa°^f‰¯ø´©ôÑ!X©-8ÿ¿ÎŸuöKÑÆ@˜Ø–?@·îsôí–zÆ3hÖie´·˜ãèQI*ùôµqä‡)òôÅåuÄ?æ®|tó-§—€¦ŸÉ€E—X僇áe f>t$—VåBH?ËúŸp¥ [¢ Ïçð‘7²ÎlŠ·}ùÊ;Ž‘cF:tÁÏaVåËF ùø'ð–žÇ™?¬[kaåIôtqžŒóHÞµ‚¢ö¸«y vái“ÓŒÑ^)Pïq¡qaïØSÕ3`‹E(ï‚\ÛÛãÜÁ]Oñ¾gþ<Ï&½zytºÂ6ô]VJ÷ïåÀÙ½P{R”~ÞÝ»t,È*;IzhÙA²:ó3”ÜÜRècGï á¾1'ìø°|ÂÿGwÈ0/k ·aæ˜dFÞ>H²¦íÁÓ—šICÏ}F6º.-¸ÃËÜé»ÌÕ—æøÀËð¿šc²Æ\Ò ‚IPO Ùúâ"=³‘ŸlPŽAë@mZ¾n*Þd=TP¢|19tþ~s}“7 X±Ž.ˆ…†ÕÍ3í=v7í§‹úÂÐóS)¸ïµêº©Äè‡VGí#Qû²!n¾Ì¿ WTbq8H0ÙËÉÚ Ôò¯2õа¦Sæåcçœßh[$†K“Ûp[X.³?ïšiF±©ò 9ýbÁ)ôEâL0QÉ'\/Y¦t{øDüûº778ž`¤ª¦3Ž¿?0Còƒpº½ TiÃŽ)Ô)*>ƒ1wœ‹o÷­&K¢Æ¹TR3®}$IøŒ>áØ˜'°ö „_þɵٔŽïe/A¡ñ –wƳ˘ÃLB«=5™lJ¶þtCï7 rœKÇœ)lÉÜ‚&“ØÅæ,scw/tý½ÊJIÜ!ÒÃüwì-§´h(ûÓI’¶¾|ËΊ~‹±É£PþH…z$¾³]ØÃC:ÍæÀ¡“çHΜ(rf BU²-ø n>?“LéèàÌÕoƒòêS¬žˆ xc¶zó²]Ú˜÷]›Ýþç>v\=vw½à øoqä’Çq¨æs&Õg÷‘[?(<™òè ¼øäÏêK¨Oqêüê3«[‹#›¡@â>îóà§kï¬$¿^£â¹83¢F¦Ø*ÓS#ØÚŽ.=ïábV³Y€¡šÕÜwÙʨ¿6š¼.„ùû‹±(õ8ë˜cGå>,5mxyþV²Ñ±ŠÑ([‚#ÄžÄ"o> ’=†6·¸d¶ÞߌÞ-,g¡Œ¤g7ÃÇ]ÄØÄxà9u7dzxpˆ»˜tÞ9ý–=i‚ÿ´8/­a¦ žÅ[À¦'3šŠ9ÐËGu¥"£<¾&IžOùÈÔž$ .ƒžA#>y!Ûû±ÏŽ…ÃÐEüA=™ çç ir,ÎÛwgôvâO³?°ẺձLƒkSéµ¶[ÈûMç?.Àùâ]8°ò *W>„]'ò!Tó)vêÅ€£™9ØmO.¯Ñ§íNôˈ-è}üËú1öÄA+ƒå—ÀËÜ´Uû‡ѳãîÛ¸]¸¹›OmÉ{§1©ûp×3ŒÓR9¢ì<´EO°îœàòýÊDýÚ%Xò"‘57ae:Ôi›* WÒÛ9l¥‚U¢õáùÐb>Æþ‰ûïut;}‰Q{š‹ ²Òdƒ¢>zº§~æ!’פÙ0ÏbÀ6BË£"è•>Œb%s@Tq5ùáhJO¿sùÈZF08ñ=³‚JyÜ„ß6¸!†E.<´¿ô8—gÃqö«ñ tÛ®L߉#aŸ“Që»8‰õå#†ØyµŒÌÍf<ž œš[x+í<Ü' 3•óÀûþMøÙñ“Ùã4€c]Wq¾P:ÎtþÉuò+‡?‚hEíCp³> s× MøÿãÇQ€©ýIB¡Ùnq‘æJr¨éYiú¥\™jVZáôƒ)ô¿ý^%ÎÅú…"4Ó3 TžCK½91Žq¤Y¢Ûæ KäГ’B¯¾€nxz—4ZÓn¯€8!oj|¹“VŸW$L5Éý6?Ô¹E…y•™·?AU!‰3þûçD¥§ôROYôUŸhª.'ž£P{Ö‡ií†e …Éøq ðìw‹b^¯©cë¼b9ÙIžÌ°ÉO<ñ1¶¥ý(?ñhTÇ­ÏÑÆ¶“|ZI11D®o5²ø‚¦{'Ö ÂN0Y™?°ú¦"õ MÆ÷D™M“¹ÃÃb‡@t½ÇK›\ Øõ{å/Õ'–ŒÀë•\x÷?îM 0õ㞖θÌ¥‘Óãßí0øe%YZ†-zÂp$Á’6Ju0Ìó೩óÇFÄiNÝwoÜŸÛp•ð#8n†Ç\np ç„Q¹•(Øtö)ÓŽ>êü9†ÅÞn„ƒ]¶jïÖÓczd®m7 lÜ31bØ+Ǧ܉Jä?<36N'oî/ƒåaèzľõ˜5g«½gI1˜1ÍpÂþ0ë#]¿ 'ïÒ§yflñë¿è|·u˜bÆ+Ѷúi¡þÍëÜ,›¹T¯é3üYb;ïÀŒ£»ÉÙ!â¦-g6få´Ä ¯àºeóIÆôÔ|úûzxÌ *™Ý0»´3Cw‚ì”tõÚ½hùèû!ø!ãú÷)¼uÊÃKÞ«ð5_>ŠT&óOBƒðW6õļP ç*epÌ…bÄ[Q2:w7+½½…ëàÖå PÒ\«7}Ý>"°LŒÆíMÆ´%†ÔþS<ÎéÝ„5s«°qËC´þaHŸ]Õ¢å  îê)_÷„þÉýÒÌä®6!ù»7`R ý­I–m¬Ã¿ñÏ!éþL~vµŸç°¶Çž2"F³(ߘ Qœ®Fo½[B=úÜH­s;V† 6RíågYB?ÀÞ›«.kӥ鉬¶V5«½ _úÁýÓ‘1Ä4“vÇ"!ºo±?õ,÷âÀ‡íðFñ:ž%”oU9wAågÌ»îK>ࣿ¿zR‡Å…Äñvë_Q ŽA[pPÿ,ú`%J8ƒMfbìÛÝ’°Õ5¶nŠfc߸ʖ<š­F~]ÝI–– ‹+Nâ/Þj8ñ!€>öv‚ÿ©8r"¼TñGn+˜fÞcÑø½J‚³š|„7…Œõa}úÉnÜÐxŠíO¹³ÚÀþ2),ip…”Xgî–™y8eå4O‰¢K‹®b^MœÉåØz®Æ™–õL…´Ž9ŽkÂÓñ÷ëö4‰Ã‡®G™P)1Œû׎¯4aÒäKqb!ì!krÖ÷¤¢ÃO»ôRÑÈ‹²¥ÿäÈ*¹~6Gg¼[w˜9io;Ø™ƒç­‹Ù´§kak^|[÷‡ ï°)^q­î )rè±%»â¡Y¹×V¬¿ž‡L‡oÙ¶ØîUÄÀö[pi$º¿XËþ~8ý÷¾ÀB*yø ¿§K"Çl0øþEÇørŸúF š€¦­*È?Yê:f1Û.ʃºã7œ9øçö„â×9ñðöâ¨:˜]Ÿ©-ÆÝ _¹ßÎ¥y tF‚~úpŒuºØZò×aßÜQ®ÇÂ+\ûã‡ñòÅ,²‘>]Ÿ@ׯ˜ÈéÞLÑØZŒ:¤מtãýø£¨÷ËÞzTžYw—¼bM,äèƒìMp'`ç¡Ò¼w~:{Rf2M¨ðc>Fêóâi´2¼½/ô°÷ëxú«PÉ3ÜT>^øÙ^Å qãú ߦfc鬸Õõæ= ƒ"˜£yкl7¦6ž…3{xélîQŠNg×ì9†¦· kJS—½€{gO&>¿º\ƒO’eì$\ã¡HžÎ—"üÉŒáÚI$?oÜÐ™Ž¬š">̲bŽ]Ÿ §z}…·–ÎË +I":“Ù‹÷„'øOJV(º%‹bCNãx¾Hg’m‰làªuí*öW¹¡sé²n:ƒ Ö ²/´dÑ8%L¨ÃyøyrÌÌÀÏë|p:97ö™@¼Ê,ºhŘ>ý¶æ]`nË`Ù!sHýz í™ ç˜‹XªúŽ Z}CwùLÄßupà'{®ø>¸ÍP$¼K§ÒsyÂÀÇÁ $òâ>Q38Z-ÖBË©Xj!>žŠú·®Cq‹ \$N½fzÐŸÖ „uð!G®§G3L }–%õ:ô‘Ýy*šDØÓVÃæÐ£xÎHˆµ^Ý3£jÀÁ¸eO/¦Ö’ŠÌ¹‹øD;:äËq°O“uª„ŸÖ¹p®ø ýt‰©7‰¡ñßOƒÛ99:<˜†¾ÄKn/Z eãÇ k·O#nX²ÿ¼`p9Ë_°¡ÃŽæädÄœûG7Wé¢õeMêuß„Üîg7/¬œÀï9w=|ïªæ´9Èì×~ÁˆË²rîn4`Ú!¦×ì9S0fÞ®do¹žCÁªçôÛ…±zqxžÉâñ y€Ne×÷Ë18xŠe¾ïÃGªù0óˆ5<ºstÿî†+ÞÃ|A=TP,ÂÃbwغƒWqiKôUs®¨]aÎÄ›ºÏÑý¥>½h7 SÞËßu_0M(bZ¯†·÷>â¿+³Ù$û“° _™ oÁ÷eÀatªæÄékò¡ñ®Z7™ÞõqÄš»ÏYÉN#r{c2(gCÄä \l— žsÃuIÄ.åéDüßv'„ÕX¢Ã”èòQÑ„!vVèCø2ž£Ÿ{•ãr> –‘F#óÙ\GývØbÍ -÷á¼o©FQ¿ôqT18FÖÀ‘…–„ý=Î/ÔeÇóµ õ¼BsOî÷Ñj6£l+>˜©FþžLàèv°7zëAùo,\gO+R-¹§fÃã4‚ÏÉ6fŽÌ¾»KéÆEQÌ ÷h8\Ьó2bu l*ˆãôÝóg×ñRþ’¹°Íº‰-~k "§·ÁEC5ðÚ˜çèÈgñÖ€,FoÂ+­ÚíœÉ=¼´ô\÷„ýßx·r4æ±›Ú¢‘ÍÆNü¢P#Ÿ†ÆI—qQ¿“` Ì¤2ÂÁ™7™?ÌéÉ.´™ŸËÌú•kàé¸îÁWws!oˆ—hv£çõ©dmдzÃøo'MfãÙ¹¨'© ë>F’ŠÃ’žè¿ß÷ @ûhëi“=¼TÂs\ôÏ‚RŸW :žwV*ƒÊò6°ÔûŽù©/8›gáòÇ èù)椵14ài‚öЇj´ßz=áo&»®òÑʘrÏH†ÑÌaŒnöÃ®š±‰ø_®·î­èçäKF²Ù%•¬çljhîËÚߣm¿™Ó3Õ;µ›µK£Ûh.•çKcÌ¿î¤g†¦qýN¥ÂŸOË銹”Õi«·è7:øÃ=Ù*@”õ÷± rÔþÃŽ%çhðƒÙ»§îÊgsÊ^êÑ:®+Îúrƒ©ÓÓ¡CÒàÆöPóωÝfÊ€ “šŒÂÂåÁ貨“uÕ4%¶€¯‚ÈõGÚtlä „{ÊÒ¥š°Ré#ÜÉ]½‰z¼q#“LÏC–Ršyö1'&@Y‡Y>’1eÓ0ÚN„|^67•iMð?åî$hj f>éLbÔ;³±‚H‚dõ+ -a~\xì  ÄÆšq6i@·W!\ŸG“‰3g^WXqg}eÞiCâoY7Ã4Iÿv/85ú‘­É÷åÝ(T†üS¸ÌW2¨ž¶Š–æÂ9Ëe ju0Dèк"FE¢˜Ù±ðç]„ Y0w/zŠÕߌƒ°#Ñ  XŸ; ~óM÷X )¹Â\ó˜DWÄ&¡ cÈ æè¿Í4Cw!Ö+5Bch<ü’zú¢Ê<}=WªÓSz*ìq§I¶#K§üã£È#¹rÂÿKûÖ¢ŽŽÙà= µgýb]¿Tpôþ¦²…áFB<.=<“½5Y‘ï~¹¢ çO ^òxJ#ô—¬  gÓC/ÓÑ=û6È ²É«dèÌçJxÇ*ã¸Ì£…v„Yj³uHuÍ<”lI”oóQÞ¬P¦«}Ä<;†¿ë5h&÷üͧ(­Õ‡#È©>‹YŠpO‚2wÕczyT˜n`¾8oÇŽÌ (žs¦¨âMô„6>Ü=w Š‹D0¦ú-oÆ}©ýi* ý+ÇÞ‹¸Þ÷!œ®T"…µ-xu«6ë~DpÂÿw-†«þ ·#òé(1K¸Ž‡žÍaNœìË»94©w eMÑ:zåP«ãjã‰JéãvJf 1þ??‚ƒïfº0‡îŠGiªG8Ÿ¹L‰çÃn(©‚“£ï![F™½é%߬2Q/Ô€NY‚;Æ¿©KÒQPfᨶú(Èx×Cø&aŒá#GF\©ä#y¨mùÇÔnBiUI…”ý@ø'­e#é,øÒñœ-Ø/Cö&&“wŸ:ad¯;ï 4•¾~øµhÑ×ï˜m®JÍšÞáø¹³ÁE×QÛ!ÔÿX„Á§K1m¨}Âÿ‡œ3æ¥ÿ¸WÆÆ8ÁÓiÌdÞÿî} æËOá‰b²÷w2óa´¯7.eÞþœ‰n¸-¢jS˜ ¾â‰Ê6>7 Hw?âì[»qÿŒw k¨c?‹qû£bƒéÉ$²»ƒØÓê…¸¦Ì„$Ž¡L²) ÿßýÏÍ7âì§_8|îÒ¤u¯:Ù>Õ‡þÝ‘Lgm:éˆ+l^/DÒmžìvÊ·;˜¾g+8¯N‚LñUÿ„=3.ÃØâ]˜ñå~n#I_ۙ˜5´æ†zÚå‘ô³&DÉ};™íüÖþzË|ë¿L—um 7gÑÙ<}ìd÷E¸xó-–Û¶˜L10!¾/ÂÂÑHTy¾Œ|7‡YÝédÒs b?æƒ_ú­›£K³ˆ~V¹+–L5štáÜìsäÙ@ »¡B’t$GeKÈX³il³Àô5fiôXôlÖ@çš5>Ĺçǵ÷ÚsóY‡(€×Rž.@·~Æs7MákZšÏ“‚5ƒ2ÜS«£¡3S º^ÕÍ, )VÃê;1~V·8$ìe3°W- us>À^Q%ìT ÃýO²ÈáiîäîdÆôˆóqàÌU&|mGXcO_nѵWX!éΩZ) m›:áìKO<ºš½n/H›6*ÐkæG`{Lèfùç50srOs>¯ðn‘øY?? áÕ‹¿aKÝîÊg6¸ÞOœ8lf¼düX‹èTU&h<„¡î[IÎUç½to,ãzõ ÷õ«hêók7óû•Þ„ÿ¯¼%g¤•`~µ©]2f>Ï€ì–7xà"å‰,‚‘Ñv¦hJ 6)L'ûdXPˆ…õ}#UÛ ³¥á7ÖŒ? ¤!@ƒä™ÜYÎjõdê{~2]ªº˜UòÓGŽv´påÈ.ä<Ça|ÆTdf2Æób ³aÈ—Êsѽ”¤z?`ÚÿåA’Fœ¹·¹Å­c'}äHðÔ ò°»o%%1óæFã?…¬­ÑÀG ¹õqwXáŸþDÖχø“föƒ­7Ùg‘†C›qýº/ðôL„E¦ƒÊm9¢2õ!“â®4¡ÿ­¼/`ùp4‘(äø¯™D.˜¬ú¯^™µ<› §x©†"ÑÙŸ9([É\¹j‡uµV }c ‹§ëŇ¯B°’©k<#Šrφža}ÄSßï¾daŸ =«\†÷ˆ Q˜VŠƒùiðin:¦µ_À%£e„­P¥®:¡Ôf“/~ÞÀi5ÉêëÚÔ¬â"QÎÍåj¬ßýÓII=?»æ„#–…­¶è70MÈ„þO$ËhÜkq–ï÷ŠÁ^6Øqs*c3Ú ½¯Àg©=6ÐÂQHSá¸ôTËãr,JÕ¹¡r´ÜÕ’^Ù&nãq¿6ÜŸWŒoÖ‡cïó[L®†&ûK÷„uÒb9n”ï²;$•a~¡>©2,™°¿VÙeæ¦ÏiÀI¸]«‹«dýÙ]ï^â¼ ³I×Â9äο5ô×€: q(-é÷Œï¡XšÎjÊ0—™ÌÍ‹¨Äúâ’{›ÄÃ~9ãâCF¤]@ îÚáö/ÂŒ~’½ûq9äØ[‘›“b¡)jBfDàÛ§£]t L^43ñ©Sp®æy½¢š¡•°ì ÐMT ä¯ëQNO¿%õÕºŒc…å¸õ{ŠÕïƒ=§/á YPM…7-BЭ½”Š}9D׋â7™o°èp-|qdêÓ…Ù;»|hò9Y6«+k~V›úSf|e2‹°x£g8©>éœÙ §ÙkžÿW§ ÕmºôѺj8«ÿŒ«p:yüµ ìy‘ÛßÁí;ÛÄ*mÜAlŸ_w^Mò°Ç‰5p¨àZ ’9`O¼ƒÕáÇ­¤è¦/œè¡ç„Éæjvhý<&sk¹"¥„sGàߌyœ¶=·ñÍN{šè»™3çéî§oÙÕ¯À÷ø|x(6­6J`µþ b¼œQð°¥’Ûã5•$xµyº]/µœÞFï.ûÆÕ°»È_Š*ÃÝìPŽ)ÙªüÂ×rá/yÌLæU˜à?fo»ñ¡©)}ó@”¶Kåà­%×ÀAÇ”žkk@‹LuƒM -­p…ŒœV»Ý–ë<˜ÎâƒsŠÑ”©ÀB%ìÃÃA¨ð`¬PÝ |½øøð æQót*Ì&Ù&:rþ­îÐöü>´±£9'5áQæ4Nrþ\ÐüfC¸·¢X^¦9d.XzâúðwÌ´w—Ýû¾Ô¡²½¯=Á&GAòú„,´ hmYb„z >|u ×)ǵ~¸ZÛh¡ïWf?ýÏÊoYy&Ù‹Û(·ÞàìÓÆÖIÓi¢8ØG–¥åàûŸ‰œ%ÙÛ;ðjd »f×# Û6«ÙS§‘ášýØg8ÁƒîàN•©Ìé¢zp¢XJe´Áó´>æÏ¹ÂLmæ`‰?¾[‚ÆAÌ‘üS½ó~1Û2uˆÉåYp¾***œiïèø+‘Ëm ÂiÍzðÐÞ˜Uˆº‚íª‡ /1¬Ûû•ͬ¥r$,ÿ®2:È6¾‡rW=´pÕ…ÔÅ{àŠ|ºÅ"»¿êë±¹9Võ”ãëéŠ89rʼíÇsê©xöïtºçx$FÛIÃÇ[yPi|†ýQ£Eª&«3Ç–ÜdíÚ¸Lv$m’ÆióH˜s6¬\|„v0Ù|¡ügàü ØÝ‰‹fL¦Q»f`Ø—"¬K–'±Ç¼éº™Õè¸>h-7'¿B0Ö—Î’ÛË»8uɇÁ÷§4Y:•4~ZE´sî³UAw°„ÕÅB´l[D:FÑHë?°EÊœH_Ú†ûufÑèÛgˆõfmò#Ù–*6;S‹ QP8ÍC?þ3§OVeÿ·Ó¢f.Lïa©ß±$èV„oÍI˜&x”êÂUO/†ïëq¿‘˜-øÈ˜Ä[`ü-9HjK'!òtúÊ ´0æ'† ~€[dà¯.1‰¯6Ÿl­¢ŽOç’=öøËÅ:A)[‰ÌZ-M_ í£æ*›èÒ—ªô”‹$v‚3Mí9 éÖw8ç*µÉ»Zwj¼®‡ÄïQùï}*úÌ“^ á#æëOú‰zföîŸpĦÞIn§ed³øFj¸=Í¥¸ç2~^K 6¢ÛX¬»ú/·IQ?'~"ýnÍ?”9vP"k+ÄiÒ·%É8´\—VÑPú­AžzXSõò­”U>Šw!N7bxnÏÆ¸Þ!>8ŽïJ„ìÜò¯´ÀpÅòàÓ6r@·.ªw¶Üž–KëÖäÐS|iþÙPêp[óü§P =5Œp÷4!â+}œm¼¹™~õ¾ƒ/y®²ïv´`³C3ËëgGßuŒ¹/Œ§¢Î£ß§!Ö#ûfk]Ä­53HÜÕbvμÐja›°ïúl²Èpdžâ²RõrŒñ»á¡KÂË’“;‡ pÍ_gHË÷hÁ…gнN‚&¾EÎÒb|tN€*ä¸C~dÌÜlnÂD¬¾ß´‰ÐÖŸÎØp_ó€Cè?ÂPÉÓ‹‰DI'{JZº|\@ï›ObOoÝ‚*¢xõß\šu¶²E²Âo™0¨1|Š~<îCß|¹1ñû‘®+ŒÅé“8¼ï&x9Z’žXüõ¶–Õú~‚ŒZ 7Kø`çAc~µàÊîy¬’Ô¸æç?ˆ»ºAN–,l÷_ kßÈ“éë‘·þ¬–þˆ+ ï*å¸ÿU,»aÉk6è—YÜÌø_ë‚–?Ïaµ=ЧAØÇ¼¦FÇãÖx|¸ x—ز|¯˜È¤íð+&™þ¶GðÜgænî ä9 k¨> YÓ© äÐ.»Éu ¶¦úkÜÿ~&ûçdšïbOk”ìÈG²´rº y^k§3æ’µ¿Õ©àé!uqÂÿ“5U€Ê±ÌpÈ?øý×›r™kTj±Mõ Å)ggNÕæúÂVHò<‡§wÍ 'z,IæoS’gzÜ–Ug ²<Èmµ…¤t(•ò„ÊPÑ¥þÈŸ‹çAöFcZrwQЋdnÏï5Œi±Æ¢>Æç(ׄî¸âOšª"ðOg9nüø–ùœ; 2¬·“çÑòÌ= 2N\¶ìÝlÖS™—^Ì©$Ÿƒ)±?…ôÌ~Xܲƒ:/s+@@æ+œÉXAW4Yâí +¨}ér¶ ûù»"8I²ô™¦ Ü\¤NþÙ¿I³² þ×~8=ïÔÁH3Ž˜ ÒO…'È”Ÿ™”½ZÂjÿ8C¯—ì§:;mÑž_˜ô½Óe?Eç*' ųbog¥x¬ÈPe>cp15ŸH]Všw˜¡T,Ì—¤ÏŒ‡€/¡‹g Äób¸†Ë«ËéMÛ($šRpsê*ÒPÚÅa›ï!ßcOØáIBÄ$¨Ýoy šäÈÏCÃºËØp°Tu„ €ËR»ÒWÛkRÏ+ÐÊí³ÙŽ{¶Ðýò5(uHÁ¬üåLõ¦e ý´iìÆ·"d’›t^µfTéñ´½0ÕàØ„ý¯ý.abwfþOW¿³™{ú/À¨áœ<ŸÇÒ[d¨Å/>8Ùöáw=\R÷]:m˜{¡sàn[:Þ°$Ã7as¯5¹,š‚ƒù¤Ÿ5'¯áÏðr×ŧOeÈûÀ»ÐP³ø¨@óÔùæE„×½‚Ú¼­¸lÿ0SOÉ4V$D îß[K¾dkRÝöì½Ã è0m7Ó+`•‹¸¸û­¸©^FçôøÇ÷WRh:ýÐu}giRmêQtš)ýɧ>N›/t…ÛÂz~öˆ¿}ø/’’Ê Ñ~áDü œ×̪º®MëìBßC0÷’3Ó¾h |ó6 «Ï.g µ¢ÛÅs Ÿ\Bñc’tæ®lN ³ f¥27w¡ãÂÓXqWƒ¬Ú„žnŠ$Çzþ×GúÈ»¸$»æ=·"K4ç×Uø_/æ'{íI?™îßˬ™ˆÛ^Æq7Åðr‹­äÐ*QúIA» r©ùó™`ü£ßÖ]®ñ’¯ ÚGVc£v­ûíeÓÄÉr:ƒ\X`Jv«bÄóŶê2ñ©XÜRˆcÖv§ÅV¦´êb Ý¡Á!¶I¦¤û;Cþ†°©òèiS1qý{òœÕ~ ½ßÄÃiÑRøyÛjßMǨÝ#øÌõ'àâ/60ŠÝg.ã©…·¡m‡9¥ŒÈ÷H\^ Š Ò¸:¿š¡ÑV-&W%¨ÈèWý ÀhI3 <+ùÏû²žÛ<þ«‹f9Õj`øl#ÎýÍÅ÷aòäGÀK,Ø ºÜYö˜ö¸ŽQâ¡©rî?|n?‹S\ßí›qލKæHUqK¦¼gŒy¬ÙŠÇñÀ»J†´ŽýÕ19ZUeG8µǶÂéµÈ´a£´XŒ:ÿ}ÉÝ+P†a¯;Ø‹ \?²‘±k@w+Oðÿ¾oI¬Áo*’Âf2ì/Ã%œMð5÷&& ï ¹k5è%eQ¬‰×F¢¹«Ú¯Ã§s9ølÓ6¼¹1·YRÞ+—»~*ä¼L톳¨áŠzüzÍ™˜dÀî'Œ¡öC†ý1 Š;† .Q‹HލÑÖÜä½y?¼à&‚И"1\±ŒºßÙ«Þ¡MÆytHH&N¦ÉÐg”)íð,h 3×ì~) " ¥hµmœúgF‹±Yü$¸|N¥­(Ÿ Ý™„5W¥º!šÌ®³?ØOóë1Á¡þÝ[KÏj…EÞ“'ðÿÊÛˆq® ýØ¥õnà¸lI4Öc:ÉuܦóUDéâ‚Ç;è‚zëÉH†kL­6"?$*r9 %¶Ù‘/µkÈý¶ö¶^sU†÷é|øùP75<šfŽÞ¢½¶2fôoH1û\D”Ý$Gqv/cÆSÃXOâƒöÅàå~É8%CÄWXs†Ä‡À§Ç–š=ØÌš^–§¿+¦ÑÖû·`KU2ûR•%=ð4ÿ<5YBv-r&?âòPùâØtc! ¯ÚFVjºÐû(*‘u›XëÑ1uP”¶c­ºG'ôÏö•Ôï¯,žT‹TÆsY.¿~„¸úËÓïöGÙ~Õ0 ν^J˜âõˆÕ?ªJwŽs‹RšO5y éñ‹€Æ d±&9¶V¦ãz´´ Ÿ—Â>Š1‡½ºiè_Æê‹!w¦n6ÉeÚŸÖ€x ÊÁ |Êô–v1ÞI¤'§t1`†õ¿5Q,.„†Äð⢔w(õl*¬>“@Bö.§ÏÀ'^žŽ<Ò¤Ì÷v¬ÞG>¸Óü¹rÔ—¯–MìàÚŒóƒ—ËÚ¡àc2ü{°”¸õ%þó¶`ß/zèª*©ë1Ÿ°Ö7M¨9†ïßÁ‚dÕ¼\6>DÄcŒ¨SÃC<áFeáµV wËmzzþƒ‰S‚ËñµÖP‹à>ojäõ^{L&†½!7¡Ž•÷?ŽÃŽülï•Ãè&àïÛžC´©8™û¾…]&.NCBƒX¡žk…cOPó•%›,¦I¹>×ÀhÊ}\6à…ÏEǵúɯ(¯Kñ·; ‹sÀ¸ŠÅ(>v —3ïN Ò°Ç×hVi*:I/".}d¾Ómo[4ÛëæA¾*Ï¡B¨ú9iöV–9 ´GéaÔøÒ=°vj"þW·70ÆÉTº‰å[˜HÓPdÏ<ÀºK‚tU¨ ñ­¹7V¯¡U-ÙªÄKùÛ6àdþ{0Ùê:Ú,(„ƒ^(éÉKNyÝF¡1–ã’ ’‚© Õx ÒëZà]­, õ>FþæÍ£6i˜åõïÐ1À9¼Ö;Ä“³BôFÕ†+¤I³F"!Þõ8w¾‘3?ÌxsštÌpõ¼ òÞ®í¢2`é-H«k“%¹:tRi &ˆ¸ã IkÜ?ÈƒÇ Î±?OÇŠoYÀg.Ž/«ñ‚|7÷š*/ÙFX¹o… U3¡Ãæ* â¤åŸ^Š ž óÛ&ÁŒkV”w<–ù+[ãù¥»Ð'¿µŒEe5><߈‹7ÕÃ$G¨æÉf7žRd2KÏ1WcÅHú¬6o2潘ƒñÃï`ºÔvvmäÈ¥K£Óœxc¾r’,ÒXA묳¾qîîÆ‹ï:¸ªiØTÅ]si Uhä˜fÞcùXÒ®cKáð“&\“³–k3« g=l‡ÈM„éΆ|6 üŸèS~IE2_ä ÞãüÊyƒeåLKÝ+^áFåjü¯§´ýÈQvä¸äàëAg!ílÇÿ9EO³ª+¢A¹ç>»G…ƒŸÐ,»rÜiöQ »s” ÿ©HzüùA^¼…õZt„Æ>>Å^y\@=ôïø 94ÐþÆml¶¥ÉE-0)\ŒÑÔWÀ‘áopïü,rG·“QLþ(/$¡ pèØ4Z¡+Í̉µÁÝä õè ¸$Ï{`g‹<•:éY:B´Â-•ðÏôM¸Ýh#)/P&Ó®tv2zîþï}<åLÕvôƒo7-·ðÁùŽMlJÆ0tþÒDòu7¼Ø…ê®5ÉÓ‡QïÀj¶NÅ—o%üoÍ&ìEy #×û-ľGº[ÚŸÞ‘~ ‘+¿pø¬ß0â[%‰ö½óŒæ‹*–÷Ósh2{è ©JâôÉÖ÷¬Ig:ëáãepsä güsÜ,c<êËø~sÊOÇuð¹<¬²£ç{ÝY×A!¢¬E²U>‚½WzN {º¥éƒ»óqj^/FtöCU€4~Û5Ƚ`ÒLei‚ÅMl²gÁúF » "›Ó~a=öý;ÄÌ{ª€Jç*àÛïÝð'Ùfªþ€Ào;AE[‰œ°7"Mçw’“ÉÌYK©Ðì—(âú¸7à ª û‡¸à؃ù R„ƒû烰|_‚^q·®äÈ{yްék¸?í¦†¦Bl*Ü ‡í¡§Û9¾8{öiîN©idÁ¦ŸìÕxž>8(A w´3 ò/°äÓ¤èzÉ5м¿‰{Ê}#96p‚å﹆oŽs‡urïÁþýs‘ôfgùjSëéÕØ¾ò6»}¦<¸ÿŠ Ó5•N9)À¼·€<‰‰÷ã9$4H™ @ü“ ¦÷òEïÂkÙ³0ЫE×OKѶ0éËTàš2I¯‰‹ÀBøx[ Õíà‚"ñìÀ{fà vòµâèòòí+PC`ó×5òçç~Ñ~‹ßŸ3ÉITÃΗ¼=³œXÿ±ÃTó,ð¼9£Ö,ôHÝç8å`Zéo¬žo€¥¬1càTË|mÀ>èéUÂ>•£¦DfÏŒfÿ-ºÁ¸õ{⌱qÛçÄáÚ¥x/ŒŸ3÷ÝfŒ*RŸ‡åHïB~òñÝO|Y‰vwEhÆ AΊø1fÆ÷QTÛgI•ÊEáK¸Nò“W^°ay)Û.ŒYköãTJ—ÖÖ‚(gKúí`}ú;Ä7mÐ)p“aíæÐÀ÷Õ:7,7¤öâ64U®€uíw¤×®váÐåðv^KŸtsrÊÐE& ×Ó]kþÇŠ"6ì¹ØÙHÒ C7ò¦LšÀG„›êxùTž:uˆvØÅÛG”¨KqÕéáÅš§SÉ8>¶øÂ²‰üÇ^ýÇFR¨çeO¶äƒZÂ,æ£Uéàf7çʇß÷9GÏ ÒÍRñ¬Ø_7°Ž¥ñÓ¨ïåjèØx N–ú’YIìÏâm.#N®«ŠÓ¦4ã‰7íp¿ŶgYý]®r}íl˜%+˜ë¤<#TèÎ}£ ÿÐí÷ò‚›ÿÜ·§‚Ä­éþÜ¡ öò´þ‡,òÚHPëgCpð€ûœQà­Žƒ£3ü `úð²¨cã£séÏ]Ü}÷K°8õ|t|ÉFÜpgOŽºQ¯CËÙÿÆüqM˜!nß¡ÃÞyLÄ;ú¿:1?/G”0‚4îßC¦TГ¦YfxÄÃâ"M¸6Çñ^$G' ãô–DzÀ”.ÿ«HbügÓ¤»Âdß4= Lm‚Îè‹°¥]G—aÎLýrÃ~=Ï\‘¦îæmŒÚüçw'Ûž‡âm"¤áò!|0í7÷º¦Qïí†öV¤Üð5lšû Þøí`6ÍÝ…SD€îù­K:¥±,{!-žñ…©îm$Ç{Îb®ª$e¶.ÅÓ *p,ýóšn¹ ]éAf:ÝäÔφ‹ÈÑögJØXàƒ f¨ÒÅÒ°6„"þ=»{ˆ›yÿ;«ó÷>ÿ«€ŸÌý ©åæb+áŸè;·žIþëÝü, «l×Áh”8Åï;ÙüøÒ| £Dª¸´Àþ¦a˜ýVŒn‚¾ïÙÀ†{û5§oé^\%Ù‰~§½hØ™tHϳƒa}’’—@ô¯ ¤²XdYiv$·õ3veIÜgGÍaŽU(µßÂì]رs`a.§8v*’ÚJh1ð%êÆ*h¥cOC÷<`T_ˆQŸýy0m™1‘²ÞÍ’…د®L<çÜbªER'øo¸q†?Qc½íÿ1²&Ù„y^ÀúŸefT%`ŒQ² ÇÙw˜¯^G¡,Ã÷46r÷nÍâþÕ˜ Ö‘ÀY³ÙÝRéÇëFô×CšY-Ew£$4\X`»õ€L~§Ï.ª’·¶Á {ð;aË]æ£[‚QòP"•Yr¸©eîw¡ͰP|6;>¸E© I,;® ;Ff³Ï•?µ\”W€ ›ðžþœÿþA|„V@̺W°þK$s„È)â[ÇlÙ£‡«%ð¹òvfÍxlø½K˜¨Þ^@GºvÛ‰üëÉæêSvŽ0áÓšÄ:~•a^ëb âYq:iZ8F¾qcAØóòì|³Wd¯l¡S1¯9oZ F‰è5ö ô.lépO‚ïÞ±L}_ìV@ð&ã?ç[ð/}ÒÄüïJ˜ÆN¹ ƒáÓ /Ò^XK×k¾d<¾ô½ Bj¨`+ÐW—SéúhC¢ç³”Þgc`ÓY›±…MŸGˆrà%¸«…GÄ6S•ôÝÌè`gwÌ-Ø¡ ®¬ÈŽÍDßjBR±_:_„Ú˜"=gØúk Gdbq™MÃ,µ€Ç*zX»›Ãî¢ëL bª]Á„´¦/ÝILX° jfõ ¬?ÑhŒÄ)ìu¶!ø<(ˆ·ˆ9ħä>ˆÓ|v-{‚­ƒ¨IvX›LC^é§œ~å»Tkm:u‹?ƒ'w9‘ ŠÉøuú®¡‰Ÿ üŽ›G®˜’øXNöÅð“_“4éñ„-¨_/Oêó3€¬(ã.‹ö§Ùk-å—Ø©&KŸE—á±L šù` ŽÓP.=Šñ.ât1 2ÚH |t«¾^œ¹õ<Ì©<Œ‰wFáæ¼\¬•V§u~z@¸% f²c™èíïGê & À£ÄÜÇä:öëé#oô ’Ù®@Ïÿ3Ãǘ™¡Cg~Šcnüþ‡úo7R{iÖ)Oœ¦Þ)ÅŸ’)ÿõ˜¯/1 ËøŸ5¤ßò¾AA3Ã3%Cv‹® øï:ÉbXåeüý‰qü† 7\[NÞŠÁE¯u/nyñ‹ÙqW¼pòØ ô+cù pίBø•Å(üKÂRŸg¬Z¹3jº\â>é1…¹Óa—ìyVîÈc8â6†Ù:VÐÖÇÞ¶SgV*쀓j Øq?KíFá¿Zj{u‚áb¦èôK™ŒŽôr×=¾‹Ã%7Àið¤î”§.®qèj²þ.ѠŇ­`‡¢ u ÿ‡4þ;ê?‹Æ~¹kEJð¦™N®§·~ᦙï˜Ûéwñ¿:éó:3iwv+#ôí 'â½ óé8‚ˆÉÿK“ ¶)–NéÄ{ŠÁ¨¼¹ìò@›Ä<>`Šfc|dþžLn¥°ñrΤ»­¢`é— ŸOÞ¯¿Çm9Q†žËö²B[ð©1:V|m±ÉÙ#{áv¬­XOQA[œ6zŠYíÍÿÕ-³ÐÍKöÔÝÇöäwTôì,âÊ'ö£ûIr|¥+*Õ¶äW|á6º‹’K£w˜G'ò°ÍL+–…Û.Ò§<5opQï4:o§ }IÎ@ЃûØjËÜKy Cw†áæ"3*ýf*í¿/Ì]{¯¶¥”Á¦#ëÈwCuœþs.‘SNŸà­W•Ù‘•æd¹xö^¬vøÎ*neŠc¶Á´'†ðu¡$]¥zÝø©„ñë³±t‚wA¼â ˜ýWœÈ*²ÕrNA›À6ø¯¯óíeЬߦ—Ðê™ ;¿2WÛ‰8´ÂœÆ@P6¸M›"ŠpÀ$Äí€ïƆä^k?ø+ÇÀÁ/àMž ]ü–ÚÏ$¾?Ò˜Îö(º£€y™_{'“‹+š¡ûÍsÝU }»£aC•Ê̤÷Z×Üg@ý74ä³XŽÁÖ tr8Bä.VÁÇÁ8úy)UäS£µ2»Xÿˈ$¿ÔDü ç°'-í %Ë‚JØ›‘ä»ä¦F<Íù÷¢yÇÁdÖ™™B9Ï»°O(“ð¦äµlù¾ >JÓri¦a°› Útž‰pùŠwÎb‰Ç>(©â2[Ê~bä_ Àbõ¬öÒ°pì±Óâ±—Èop§Wä¹Õ O« ΂¡¯<1zB¿f€vX]ó=Øû)ƒãÒà·Ø|°UÙKsý]Éõza¾6­·_ïGäñladß§j9òW%–)uM¸™GéJla4ãC˜÷{6sþ«§n9QŽB~ØÀß¹<ôz¹ír°ŸÀ/µ³‡=Ó/Ï%û‹C˜Üoo8ïgÑg¯RÅ-Π<»Ƕµ K_Ø»‚»´]‰¤* ‘å‘CL²×dò©Æ—<=¨w9©xî¯ÝS³ƒ¸î½BÜôtÌÈ'µpšÛ Ë-ªê«´t˜^ËÞ;ùç&ISÆ"æX Íœíä 䙯%>—¡ô‡ÚdòÑæ{Æäöð(Öl #¿×Å@OõBòyD„„‹¹‘õ|íÌ=Á(Tt:vÑ‚hýõÞ?óî­Aû¿œÇ:§Hß¡Él|ô.o»†[#+™³‡ÓØ9%mpœ^žðÿÚ‚tŠè.è_GpÅCtx‘C7Ï:އÒÐô|†y|˜=×ùÖ7Àô`|¦–óþ}ÄO–|T~á"zÄsŽÂì‹é‡¶]°_ò¸È qt×ñ“#«›¹Js͉CA©ó™çž®"Ę̂÷,ANaÄo?/‚ò‡Z[“õ ÎÚøî¯¢WÚNƒÞúÒ<–£ÛcóÎÍ¡WCÉŸ®rx¾`*9óêåþV#íŽ&ÄŸç õW9IÖzlw±Tºz‡™' Egh4€×¸‹&“ÈÂóTèˆ9÷¤o^_E3î~d¤õ–NØÿïæfÖ=Û_F9ͲF&V–$¯|ÍüÛ€7¹÷ðG|';ÝVðò¿{Îè´2 $›'£ê¢ôØŠ^aS˜—!Jt¦(5j¤~û5ðqín:C ,8'pøû]|Õ $«&ƒk[aÈ=׸)¯:~ÎåùïàäŽ4óH =3méÎa7*ݯKW÷f"_ƒ ½vJœ®J€#Qeô²Ö1°šy{ÈÑ…ØìtB¯cçYûÕ×ðw“&[tÀv!J«[ q¥*oµÁͦ9TÍ%Þ^Ã¥Ÿd²ŽÓô„0LÉó¥³çYNØ_»'ò·^„ÀY.¸v{,ºm±¡íDzP } øñn‚•Ô†jÞ Ä}ôd¾­èµ§ÎˤáC?“#¡zL 'Úg˜¥S邞ˆvèdMjh“æ °3Lo—4”Ûæ Îä¬îU³ýº)œ— ª3°ï6y0YFôßðºðç²mþT¼ùè‘#A=¥Ã‡ŸÏ!qK1Ynöœ]¿w;19ö ‡¾yqݶ)Áìn%xp'ŒDèl©UMðv@œŒŸÝÊä¸n4#£Ç”v Ÿ’Ó+rq£Ä`dR9ª–|ø‹㘠S/?ÆÎ=˜‰ÏçaØqcú§ï!ˆµãoƒŒÄú1xï,¢ÈÅidaê’Ö>™£l“ÉͲ¶§É­0iÅdê[I±$NÂÞo†Ý€{ÿʾ^RGñùKF_T”V|(⮉dÇÚ¦b Ü ˆÃ•3o‚ÍŒiDîƒ/ã#Ô×î)Ó·n2á§…¨ôÝY8UK‘ì¬æR¡ÄXá4Âø_«•]¸wÞM¼dr”»ÔÙ›ÞÜbOÜûp7¤}³ü©élêr¦™ÞæÊÐÞ¸°4a—7LOÞ.±k¡´jõTèò5›À;„_…V#ùཛ[×+Qí‹^ôÀ<'b|Ï\J§äÄbÜ^ªF¢rÉZß;ÌðáV=’ îýS‰ÀǸ²Á®wÄRݯ¸Ê˜;³Œ„Øò£›‡8n\E÷jg k†%#—<Ô|ñÏæ"dï_Þ?=>·ÐÑôBl´`þëÑü=\ÖPG²jÔ€œYÏO…ž¨“IÇâIÏÍï8äÿõ›´ Šß;•{’;T°Ís/hm Å' µôîM`õ­P5HŒO ƒÚ‡™‘/ó`Þ‚Ô‘ûŠ{oÛÀ¥.:ßìÖ Lìd6Ô‹Qµï‰hÏ÷ÞÎzBWL3 «?_awÆñþæ¹ÌÒMâ¤ú\&Ýù¹TÄXR™º6êÿ†.4Y»“Dfòrüh¾¸÷ÅãðåÅäV ³ZÁ÷ïa®¸=µ›Ë®{š@.H>ƒ»óÜ ìï –ç‚(-|æ@—•âìÙôó¦D¿£ã•'sÒ…ë0I* ŽîÙ‡>y¨ÕK;Ûé?Àœ†ûØôrj6­¦·N ¢wá\Z–}”µœi„"å‚DôÐJš_낲…ì³Ç,€ýWÆÝ&$Lse|q·îuà5=?‘ÿkžGÒ§Óìo« fÅó•XíRŠù&ŸÑã'¡êÿb̵Ä·µ/ÉËP‹ËŠ”{x!®¶Ó º^×Ù•qwO$”Ô3è°+þXt1 WMè‡ëÉèül'>Y{Ÿ}šXŠÏÕÄ©Ñ6>’±”îM¢fÛAµÆO(`?Ïþe8ç»=²ù(›R3Œµ?˜…jØ”ÂO½+ö2‹£lQú°e/º¯v1ò|Lï?\KùÚØÖ ×X¼ÌñÑCì—|Ïq~Wá_z Ng§.jÃ’ÝŽx®GN '°W©^K~–Äޙпx'†–qîsÂñBûy¦/ä Öÿøˆ‹4´©}^ÝØ>…–õöª^Øp÷!WÄ8•TÖ¤ÒÀÓy(«• ¡!b¹ƒùð·„]ñ*ÃRÀÕsÈä÷Pü¢ ¸ï¢h[Ò·‡Ú½ìÓœLTYÎ…2=s*h›DÕz`T¬™Q½Ÿ}¡ÈCµç~‡mfáàgn@>”ÖÂõØZPèÄù&úôVbíqAcvoÖ'”ð¡ó,Ù€ÜD¢“v›åù)L_ýýÈ,/!7ž­npÖL£C^ŸvÂÓÚ!TElÜ%Î†ºm¼öÏîìÅ„ŠðÄ( gL.Ã]SyÈ:ÓV”Ø{6·^l .Þ½¢ÑØR»aî(¸y>`Ô‰ø¯jñúçœ ©ZlñrP¯‰q‹æcèd’´ê –m¬d®Ú¸â¥£6Ø÷óHÒQvÏšè3C ¦ê‹ÑïWc5s:Ú$CÖ¬ºÎn{U§[imÝ=tú}Ü6Š’Œcóè¿n!Öî¼ $ïø ¶¢¶ìë>ÆÜJ–šD'AJˆ î‘ ÛmT`U@cñk+ÏèËÌŸM7˜Ä+v´xÏjÁA®µاj¢lz i¯ž˜ÿ×ÄP9eʸÇ,¥Êò4ÎpŒŠËdÀ×5Àvõ0öÃÓižj-ÚØzÀÕ¯ZdPâ>̙ͰA~&î!ü»Î‘µÉdÝ]µv½AºªÊ°fU¬35£Îg>ã†C}ß½”ÝI“6€®O!æóÞ!ûò^þ'å‚EåMð¨Ö€n[4—^úé@¢ªoƒ¤ø²`;T÷å£&j­ÐxÖŠ% :–teàN²nÑNökœu_ÝÙtFæ1¨Ëú ¼œxîJ3j fq'á¡A(îÐÜJ/æß…sœRz^ šðãågÁVN%¢&£hÊ <[ÌϽ¬YBÿª#./bËê½àpJq O#;/Π9o€žùgV­”¤?=I]æŒâ\i‚Ûå9ƒ«a½B:þœöz„À£¬J–ÑÞb~^‚¤?!¿ÂGÑ`åuüî+H^üœ }yiÌÛ‹c87؆hØ °þ?yÉósádºßuÈùHúÜZØÁQm2c²Ù1©uÎ-£ºcqq|~(%7£´h„;•9[¥zÐ>T‡ÞÉx³7äcÞÇ9ÄŸ‹¯—ÃÒd}é-€ö à^UaÓ9'Ö¿%Ë[@æ<½e¸¾ù•Áå«Qô² sÙÚ“ˆÕ'tq6æöÓ?S§¦Toz*î{æÝ;|»á3øI ¡Ðº“D#â9³ç¥$wp>oË…ªÓèÇ'™éãù¨V˜-ûû‰à÷it7ÿ…¿OS˜ÚõÁ¾aNrç‘]uhWÖØ1x•³lAó®*ìaç¤ú„ù“¨`ë Ñ4£ªÑð;f35Ýuë>¦@\˜+] ¾9˳ðÆ´U`z8nûT„2:æ4¹dˆµ¢{eŒÐÖÕŽ¼iA•\C€tmÂØýÿ­_’ÃÜœ÷˜e%Nâ­?Ð4ê wKÁëÓ–‡hrzYK–ÐÖå3ÑÙû»ñV;¼œæËzñ•½;À%d\Ôh›¸.Š(æ6¤¥3);ÅH¹÷ Æóv8µ^ˆô¥_´Uë³ÆÑ‡¥tº·Ñ?nE7óÜcì««a_º4±8Ií áNÎlÚ+NƒL_2åbØi¨Mº¾–Á¯¤³¸þÙnœï€ÚÏÌx€ós)Õéy±ÆŒhÔ4B]¸ Èsš3عÎÙèi' RIÓȱ_…Üc *ðdíY¦Éô1¾ =¯”Í'ðWlš†kUÀ¨Z{ ñÑâlß´ƒdäX xiõ@O‘ Ùub³¯á\ËH±j€*Kw3ÿT3PæË^p×yˆ™Ü¥Äôy>ž<» •ïîú$Rû,»&¾o$ˆ•~Ùýe…Ÿq2ÖâL­=ô¸Z½{M—=}|2n˜I¿´_%^3§P·!^jÅ{*Ÿ.‰RR1Ð$!ŠÝtP¸k̉˜ÁÔ{\À;zŠdëãz6èÉw¸öÖ—zŽ&ЙöFô¥î –<Ôj¹1»—Nªxo[#1<‘G>\/d¡Ò!žª%]úÿø“¦Ü‡’ÓQÀw=Š*†$^mäÑÌ[ìwÛr0ªcÊŽ—“ÿú%K„{ÇL,*ýD}gTÍó59KœA’ ’Ø©&Š¢€€Y1`BÌ ‚ä ’‘ ID$‰•j @s&ˆ€ Å€¢âËóÿð{?Ì9»}vª{j»ªîݽS㈡Ë覆|¡!L6ôpîP(éQ&rkZñåÈ*Âç¬èXR¢Çv*DàÖç1ÔËIŽdo«$—¶Ò'%«Øî¯tŸ›.Ⱦ„¡ÍV¬«¹2=úèŽEî¡ãE(µ>1ûÇ|hBóÖ-ÕœÎÊ%Äïøf2iüˆ$Ÿ›’ɪ‚Hò7‡ü§—Þ´ˆe­#ó÷ÝÁ U^2új Þ·–Q/í Ê3¸œÊüC*._ΓGëÿþÿ•žág§ 1¦×}iˆûQò&:žý ú“}û¨‘ ˜lH­×s§!þÚ䜖>ñ¹ïDò ÌɃU¦pÀ@‡Vm£ân±»MžCEá^Vkí/øâªŽ«ìYÑñIœ³ã<¤ZÂC‹ ¬V©"ɺ¶Öws,HÔ;T³¯…éѶt¶D/DWc¸ÎEO—‘v\÷ä»qä,³ü\¸íþͶ_Èö˰¸8|ú]H•vUöÎáŨ™ìýŒÄÜäì>iútš)kœ/N/aEáæÓÏ8æÁY C§ßÂùÊ«ÿËÿš¦&Ôè"ù2:5ì2<(=Ñ=«ÉÒî"\¼´ëßœ‡Æ¦b¼¦G¢“—Ðy¨ÜkG}wN!VV³I){–îˆ%7ñ17üµ7+šæˆW•òÁc’}<$M»Ú™V×ßÍ|Ö¥rjPRS–¨ÝÜÆzVÎ$W²AªRžø©“ä¶{ˆ¯ˆ°U%;ܽ‚{¤ÐÔùÊx ý.º= Êçòß÷ÏX^ÁbäݵŒùOCqÅ<âsO’L΋¾óÉÙNIzb!l‰/§ÁÏ~ãÙÌû¬Z›¬Z«OvŸÂ=£ ivølê?áÿÿŸÿ5ê˺ÿŒù¦²$œgA=ºš/ŽýÓŸ‹_6Ž1ž‚­ß…h^ÍN¦cB Ÿ›N)ðã®ÜÿØŒ}þ3‰;,ü#li߬ª¨Ÿ3x¹KÁ [”u>fG³ÚØ„¾%Ì)•nH•¦úÙáÍ\ãÿáVñÌòäæíÛ¹°ìË\|¸ò/+t9wnË,¢¾Ú–½“jA¯œLÄ-C ¸Ú& fJªáÒµ¹b2¼™šðmþÐÿõÎá®Ãcïêðc¤w<Õ ž’ o½Z2›B·.î[5+Fq3¿-ˆÅ oBϲ“°w†µZŸ†SýÇA¶›…ÐÅËIÞl=x#NBÆpýF¦Ãð#@÷Ör¢ÄÂÕè®0% 79‚'.ÕAôqQ¸:{ÌW/ŠáܶÅÀoÃðþ’ 1yÇàŸ•]g¹̦k’67i´‰ÄÕ^È„)¹âFc"¯ýÕšM(;¶Œ]y®ò+ÑÐwØ’Å~ùd"+‹EO½ÁçTz®kFGsÞÿÕÿ‘ê,0<)D_^´"]E˜Ú± hYÓË~ÿw€j_%dÓ¢oˆ¼I¤Ýè8Ôˆm¤…î™POWËßÅmñÑäJ7?åý$Ô¶âþyˆná@LÑEôj·Wñ†äõ[ZñCŒº|ñ¥ûô¡PQÀñµ©™˜Ú…—1«Lž.Ó£2{EÑ/]Åî<¤y-•lÁœ…ìÊ5EÔàà ©Ñ‡tp•ÉaVȨÃßµát¶“þ§“.˜.H÷”¨R©•Žøæ~<ø7ÿÁŠOÐÐ|¯O-æÞý¼ ÓçÛ“äàVÚœ» ïæ¦ÉK¬a•÷ÿïÿÁ}-ºáÎì%aЪfwia³b æ-J¦ÞÆeä{œýYâÆ}´í5W¥]™ÁFí¬5{ý\îyeCOl‡õ“8Ð×\•æ’;]oÁ¸oœùa÷™µr©~º;ŸÅ8+B×\`*g4àÏ«7XÁiu&­£‘½q9¹Òi@·E¨á—H)Þò …cÒ™Ágð'(æ&ac¶+£Ü³‚y™°¬ó8„>H€3› É2‹ßl]™8É × ‹7ÜĤ;[ÑïNÓiÏ—€PD<ZF=Œ/pÖtF‘|WZíØó¿ýïxD-‹ý˜’™h›uŸ±ÌþØ„Ú 7X7‰ì<¹Nv‡¤+)»šGû4±ÇÒÖàÁôì;OÂSüaSH°šUpe¹#d.F>A”íiÄõÎìŒÃ4Í/˜ÈnTá4ïð™Ì_ÝÐc¿^JV2kCv¢Ññ^èb €Þ¾ÊÖ¿àÁ5÷ªàÂÁ]tbÑj¨þØÚ=ÿÿìÈa÷ºôCY‚Ú–èCE¹nÐÑþ¾d|êüé¤#%òä ² É;H¿)mbÃù:€;M„ØWÌ¢¡%qèÚYŠÕC cŠâ‚*¸½Œ—îÞ°×ö̆×Î6tõƒàа˜\ýà‹ ­3Ñdæ-nHèàò.†Õ¼ÑpYÖ¯,)ïYŠäŠøWî‘E»I Îk¬¨E{~GH©9†!Ñìàa²în6è—©°—R<±^—~–üŽÊÅhxè>.”QFŸØ¥x¤ÛmÒ†? +@7|àÁ¿ ajEcú&ˆËå}œ ؤ–o‘"ûâ4ð…œ)ÕzE[½Æ†îûy ×>…5+Ì “ª‘ÃÿpãÑZ"¥³¯×’cJÀõ¯$}…kvU¤k§2¾Ë{…é"ÔVœn~üÌG è™Ïÿ@î¹ÑÝzÏ´ÐpuYœAÿ šÊõSˆä?þY'á8þL¼—.¤#Ÿs‰“É\úoÎsV@¤ž*­¥9ÊhrR/Α!žˆt(ž¬•QñƱ°–Zûd“ìê’ón2>S‡›upù­ ~à_@“øÑáÚrˆÜ›&èFBrsY~Q,šïBFå·’·G§²ço—ÒÛu{é–þ5`&±“ˆœÉ$eG/×µ ¤¥è/Û²µ<“äB|ûw/]¶}(šMÖhÐP¹h´Y‰„쉜"r##DÄîøkˆ;ÜÇhW¬× ùsv£qp> Ÿ3ƒˆn¤¿,ž¡]ø*šßeŒbkNŠXº)`š(h’E²ÿ¨Zu<–¿N¡Ó—£‘ÙØ–Lu CoÄ®Ö81æFßßoàìœ;ƒôgRïX"‡Ÿ®…Ò7U÷Ð1<œ”.ÀÎ{X¨<gùÏÐZòVd™Zl.ÂWñ«Gºt¤·:+^cÏðÚCÓP<4ýe3ïâþ'dMÅ*X>£ŽðÎÖ$AÿŽpˆÄÇгÜ<ÁgŒ×¹hXô‡ÇžÁÕ¢ä˜È³¯ÅÃàÀ;qòn Úa©Ju nc\hÜ÷H°Ô&å ”hÓ1a’pÑ-.T 8NéRÙùé8»¤,¾>†«Í¯qG<¾ÈËîäÏù±=(Ó˜N[ ­¡·À™h!æO%вupÅåî > :³ñ§H8ËW¨›’§†S1håÈ=°‚nÏWþ¿óuk̩Ňr¶Ú"‹Ṉ̃B3çÉ\¯O<è7Îój^ò¡ÆG÷/!Ê–°®æ\´Üž»ÓIm¤;áwFfxÍ\*¿ö «¾XU¶“. Ø)²ïÉÕpSm„ÈË7쓯à™‚hò×_‚ŽÖ&âÁeV>±¿Ÿý¾ÑÄ»CúÌ—§R™Þ­ÏvýA›‡ª0k߬ŒÇÜ5É´vÝ$oSÞ³M¢ñqW,éŒü ß´ÖÒ»õWÐ&(öV 1û¶û‘îø8jSó“»jÝm“™ëïDq,ŸJž]¾‡#§ìØÓóÏÿõ% õLŸ>“JdeÆc‰!o=¸@¯h`D‡³!B€¨ŠÅƒÑ`rД>ƒ­‚‰Û{ , ç]S*û,‡ ÈgçúO%U#’pfmˆ…¼fíÍd¨z–=£%óëýpѼ}Ä|²¾˜mŠÃ–h"贇ͻ ÃIý±’¸1löí>&³Þ–FŠÜB!;Ь„é¼$hÊmv–•?UÑXOŽÚ“…³ñŠÝxöx êüÁkMJ$®|&û^ù!¼Ò¶%³uŸ¶j Ž)»³1^ú‘;gôŠpùr,ùxƒ,X‹uð;®Œ³@Wç¯÷¦ogö3ïžB-OH¼÷‹ñ»Ö²£¼`2:,'ó®s¹5ô[JÑ×QgØéÉÌqR=;5ß³±×i³tckSJ§/>ŠÚf%ìÆçJpp-˜œCÎT¡N֛Ǻó‘ü¾ªÇ „ÐgYp"¡wí=ƒËÿõÃÀ½\ ò–e+æQŒ³B—-Q8ÛóGëD&¨\8wñPÉê³p˜¼xKêUèäÜÁÍc™ìòÔâµ²«ôÈp«ØT2“ì£á!›ÞÊ1P'|î3¡âW]Vò«lä=ˆg|íˆÙAúêG äü©¥W~¬¢²A”g/}Qt˜d ìˆãL͹ËdÉÀn¢.A³chzña¬¾„‚ネo³³»ÁgñkFz@ž˜NE .­¨„™F5$‰‘!AÓù±qá#Në´,wç±vPÙšM‚_J`™Ø_ ߅ɺvà±ìZÜTÆ%w¨z€)½ÙL潿Nù›³ÉóØK0¨XLÞ, [{HÉ2pfRð§à*úÝ#•þ|<‹è3¤n÷<ár™ù(µŽ„Dßhf‚÷2ܸ 5¾Yù‰LàV$+JÈ·D"½P2–wÛ®®‹°Èä T?O…çþr]73_ù½ÉŽVÔŸÄ'‘†—Aªß”ø)è™z^*ŒÎÙpÓÔŠ)Ù&‚pœ›Nµ ˆÿâ šcq‰É±.!c½@E=Íšˆ£/ÆŒs`Ù+>}¹eëÈzËGì³½*ômÓÆb{YtZž1jËT5bщäG¸;1°;Ûkÿb¼ô ÊéÄÎ’$¼ý붘*»ú.ÐôžC+w=gþ>4$’’'Xz±†¯ÄCÞÀ'Žø üíÁn¹éì»u(n7Á~‹zÃ^ ì‡g¢¡dùtY k¿å·àöê@rÄnÓ= aÁ¡aÍñ,Ȥ3™c®p÷^$HU™Cwî)\}JŠXíó„W²¨ìMÎÅ9lÉ%TM|–2È9ÀÕ¾eB®ùû fãC¼¹uó=ÄK5ê$@A¨ {߉ëJr¼È̆÷Ì’Å¥ì×?0Tœ>…óðdžº~à l¹%Öof?1îåfdÆ«PöCµä«Êƒö`Ø@,Þ™/ˆú[ŸršÓ㈵ Y}ø"^^ôí²¦L&zNÏfOk‘­9…^L|§'ì.¹Ì.[|œy“%Šç¢•iù²F¶±3lP;è³ÝáùhÕIouµ „QAí)äÌÉ'¨?3’>å!ß' ˆ^ú h=úFû"!!y´n5¡—Æy gÜi‰g«ÒlHrÕ² /ã4.~e@ÉK†)Ju‡‰T47È`#V¾iuj!óW7@«cÜò¢Þ³uÉnS'hñ‡¢ýLÈ×høÂ›M#é­š2=u1š½ˆû *™Jæ%#T‡Æß¢Ñmÿ ΘÔ/®:RuJ°äÉ'à¿y’=PלÎ|°&ç‚éÊTÝïEçKžƒ äo7f,*@Wã<üø(Š£iÊ“y}0ýî1Æ[)n+Äâ!±V\"-L#Í^"O.ŸI^>ê ÷r“ÁXÍ…ý9ev~»†7dï³ý²%˜îä„çxÉFµ@‡ü)ÏÀÌ·š¢„FÑ„ãD§mA-Ž}p3íÜ x‹7]šÅÝã ÓÎýèu…å_¦£¿CRë÷)°HœôáÐlº8(¬ÆïƒÐ{AЊ€5÷/1Ç—r%ù4Àïú+xÈD Â½"8ÿ=|ûGf¥sj–k0x¯ÿŽªm8çÍrrÐR‚qp»Þ_Öâò¢¸ùÜytþrtJn€½=ë߸2aòU`ï_hùYÝîéZ|¬5ƒ9¦{ ÖUÏoeA,Ç$¸¶oéç~ÙéòÐØ5 ×=˜Ç½fdáD³<<¾êÆGbà÷¼6÷î–d=ö±Ël–pPÑÆJùØÔÉXðT³Û£¨|é#'³5ܽø›é’û¥œ3 [QÕS„® WbgÏ goJÉ“Y~\ƾe¹S9¥.ÿ@ÛL—|‰ºS§Õ–R9Z®GýgÓM/ìÀb‡ ù8å°¬U …Œ·AtÅoVcY>î_«Ck®Iý©GŠš 0­¥{n(,·æT¨Á­…[°+1ÅßAµ…" G%È‘](ºóÜ©cîé[áŇ p¨¡V¬`ðã(/÷cn‡ïHsáM pbûáÜò!6izX˜°wGWB|ï:Ôœ‰s¥¸ÊÛA[¦Ë7pL‡Ïbè°>­O4¤Ç»k°=¹¿„•áç~&Á8‡ùá¼òÛfÀéµLõÄwÖî ?|9bƒŸ'ó‡…÷fþ¶õвX–¸Î.†Æ` Ù6¼™ä·Ù#ÌIùUÀu»/2Œ`$^œÆO³}Bp]W1\V‰£á™4­ú8êvsf½€[Â&à_?²,áúg`Ê{{jÇyÝ„¯sœý<¾­[B ÚÃ$“…ÀeZ eSÔ¸·SÒ ˜ P&Á† šÒäs^ôƯ.`ÎcË;¢yñ¹·Æ ÇíB€¨ ŽQ'wØ2æØ°üræ¤8ø4:¿¸Åj^\ïVfí¶=ÀW§œ( :À]àAa•ˆšä#¢£ÈóÏšägS'[¿`ôx,i\bǾ_µžªŒŠa|‚Î:v‘5Î7'Çó«JŒü;’H¼Öþƒo®áú+g@=B†Ìá½õIxF2v¸£DdÛ*Œ&׳ÒïΠÅkAÒX;Ìf=Éê¹¼jÐ&§,­©»–»x”}$À_ÏPòú1f—È,0ž=zd›˜;»1á(gSůO³wÿ ³]º}ìßÑq¦¬w»ƒóëòË€úNÿMÛ^æôXF妱Ïb ß«¼­‡kúðÜS/Øàã ïa¹™½§|©\GkTš$ò¾Çš½Ìcc÷PÒœƒoëÛ8ÑDgÕ3±EÛñÁÚvήÝLÇiè}íqaSÀëR J6‹Nî÷RëÇ€_9ôpêîU9Î|ï~ö´¿*ÍE5áóÌÕ˜Ú ºañ´ŠJë׋~Äôf%·ÇÏæ?8 NQÖ P~ƒ 3:ŒÞæz¤·4ß÷l # _ob–•~rêD«¯QQ&—Yñ%‚²*ÀiÀ˜ŸàÅW&Ú°sÕ}pØÍOËŽ³Ì’“ŽÄb¡ ]>û_‹‚ÌíSì¶½ð-À„8NÍÆµ¹iÉל¥îÄjÝØÆ”½ŒfÅÇ¿aŽ`츻OÔ›ÂÇw±)3‚uþg &.‚äO)þR~×:dj Iɇ䕃ç!ŽÿsgäÂá!; Ö$“T«—åðý…¨ÎR²äá5|XŸH­î†·á%LÂV{:2¤‡KÀ˧>ð}Ý-vÒ&Û~ì(E÷|j;èP' ;혇1ø,? 9˳c³µñæÂHt~ÎÖ'me™äy +;Ì0ñ8^Ê<(o…æÍƒøcæhÅ~hhªÇ ÌRÐüuÞe»auà?¼Ò3•„uuqï:-‚ì'œsQ:ÅÎOŽy²>G=ht°î×}Š­5°h®M?Äi{$Eöô.3G3(ç:£gÙZ¸3=Nÿ„ú. &eT YyoºÉ8 ?Fk‚êž™DLHNoß ‹é`hj)ºð P±ì§,“¸IO#6ögˆÅQQj8³$°j‹Ë¸b›ª˜WW²¿Cåao€kXi†×:µ–÷Œqöœ¥?n#F9ì}w!xè/^¥A›NÀ<þ/0!5ƒ|‹$*¥ œËÕèy'Mз.`:Þ)0²q°%c1ú$üÿMÍLÎÅ ™×ÇþùGPQ1wnƒ×Q‡¢0ícVoLã¾M÷ '~½Æ%L [¿T›Èf¿z)³Ê/àU»$Ñø‘ÀnZí€=åË©§¯/n7ú4hHj¦Zƒ²@7Ä”­Â”çÞ̆©«áÍEOɾ‡Ø?`úv&îÑT¢UŒ™•Ž4r= W¿ÌD‡c 97· øYEb«¯KÛõ^ÁRëMôr•-Õ‰ã%™3UéÀ¡»X,ÀGm*΂ˆBlwP¢³L²hy Yÿ%˿ٓÎàh I‹$ì¬X8ý¼’“yõ07ñKgüã‡ÐéóY#…2lÎ#+ÚðQ„7æZ@ T$„óÌAω{0/Á­/ÅV‘Iné±tû¢LíSüD˜Žÿk9Î60-þ†&š†ÄuÁyÜ MH¶Ð+Øu·‚J*AZ´¿uÊå—äõ¡%0~³½¯þŸçÚèQ….{åH{êzât#ŒíÍ„› `zÚZÿ›jù(!³:ƒÄêF›ú\’\v4º‰Ò‰ýlä¸ Ôž+AÞ.bm6ì¨R'ÌȨùéÇq`ê=\Q'|´ˆàt)jr(ƒ53"Âætrͬíc­ÖÔ%_‘¿w=é1ú ûÜé—>Sz/!‡Æß¥«sôú“ûŸ£¨ëáw ÑKv·p9:÷µn¡—Ÿ?Á¢ïÇ z †¤ußDÙNSŒš)†í¿²@NÌœž’#RÆI ”Ao*'~krÉɳÑ(û<’ÜÜ•ÏÑþÔ›Œ½É qræE+È(“ƒ\ý£îòë.y¨Ñ %[t¦, U·&yÝœ½"ŒD”‘\«&ÈL½‰·jïpÛêàNó&bSõž{½jqšøÙºE«Ó¢¨L0ˆ?ˉÈGÕÍ¿a»^î*TâKáósÐÑ5Äûýç ;|}¬Y‚ŸÓêXúÝt#Ñi•"ûƒgKÙ0*8^ƒ?áN/»‡NN‚DøóL° } 7/‘æ¶x䢙éЕ!E:yµ‰öß¿àRSˆ?ú£é¹d4²M¯› ð«X™Î½?[‚”M2õÍ'ïçÕ‚väqŒOãa5Ù-Ô{¡91˘6lÞ÷=ôR3«’Êg1×?(€ìçrvx,’µYZK{~âÅ¹ÚøáÒENKœ ‰‰‘bÌ“ã˜ïOÓ8Ñ+ßàÆZ{2Oÿ ÙñçÍÀ…ÎêÔ¾ö;óÔz¸»s3Œ›˜Óo=Â\MñK°'NŽ¹È‘ƒ¯îÔùq8®fIÕÌ"@®­ ÷-¶ÃEe©êÇ› /•€þ¾@Ï.F²IŒXµ ƽñºN%8ö‹0ÓÃkRÁãÇsîS{ˆáºT1ju!„/g£ÑÓó44›DÄh ‡)ôÉØìdg‘4ŒuDv°Í²|Ž¿ ~Á’¶0ÊÄÇ1~kÅÈv*y›µˆÄæ!fr­ÔNo>ª[/!m³Hòâ#¸sFÛ|3À&áâÄ!\Ùº¶¬ßk»wŠÄ«{^Úð òÇ YÒPzûk#§â(Ê£Öôhwç¥Ç3ûESYy+úzn3õßC˜¦oJ5 ^뙾Oð`¢0ümf~6ÅÃþˆÿîq;ÂJû—R¥±›PªGOó\cî·³…„®TØœTÓÕ…ù–­Ÿ÷µ´‹8Õeɉ\Ögæd«,§gUx;eÓq¦ü£â(3ÈK.ÉðRûþ!ìÞÑÇxúeBú´2T½žÝòU“<¹– )•FèûY–Øï–`5ïÕq_?`1ÞË™†ßö¦Ö°‰N«í‹B渰ª †µ”Á:] ò£âiF¤3+f2—” Gw.-¿Oô½uí4x‰w­½ðüQÊ‘ÍÆz‚äÓtB:¶~é!¹äòS~:ýlñ‹oÕoΣàE÷×}—(€éF˜F?vaj½V2Ž’8Ìþ=¬I?†7eo$hÂ/õ£ØvÐBÅ;'ùÌP¼â§qõv$.w5ý4ñ >ƒŒ³Pì¸oé™’ ÜxÒ¹"o}ˆîÖ2Ä¢ø:'N3Œ©‚2vð ¼Ôéaß\ÇÊô Ì|¯øÌй®sqŠÀ¼)wm•ëÂ[Û¶°å0h‹/üéI!þÕû¨þÉn|‹IÑ]gxª¢D…g^ꬫ¿%¨‘zóÄ'C?‰C+ž±Žt¨E윿ƒˆœèPíµƒ<¸ìËÌ?y6†kB`m<ýù‡!ši`2*Ýî@_Ë&NÓL^²üI1|!§3W¬‹Ùïî?ÁTï kÕ4…zØÇØ×këöà³XоšÅ*l™N)~ñ‡[ðþú™°ýñJ8-ñ†³+<Ÿ³;}%»qÑ jÜÝ gTú ýñö«¢+½¾ì"ÝR{ÿ¨;Ñ×] {ÇâöeogÊ÷‘VÝ%¬D¼ÖÁüòÏÔÅsóÑ¡mYäÂÔ\7Yc+Et!Xf=Å>ƒiÌý#¿Á·i:éoOµP-o &zçðÆX+ðš=FÎQ)â1E‡ú>‰„7>9‡‡? Ü‘ êX„ûŸÎ#M|½­íb›1&õ4Mý»’L<ÉÄi$’ý¹Ž›³‰,dÃ?ž…—îÙèzUƒé¸(HŒp‚MÖ¸‹…Mùœ;7KÉ#;yš½ô({|–éÒ¡êÃ'9¾ëкˆlV¸L’ü1$è)>X•7´hû!A2m®µ»Âýf¼òEGMb†xñ¤»I4i{¶ÝŸˆÁç.fHòQÃDsDW›À3Ù3~)࿳ß-v%‹CsXQý/ì-§9ô¥Ë2â¿R€foÔÄ]bŒ+×–*jïá„lÊÄü‡~øZå4Þòá!f™v¤^Ô‚¬öWÂaé0úXê‡" ïë4ÒóÝ z™3¬IL8sèƒ,¶†Þѳ¥U±­Xs–œ—3™ì®æµ°Ýç+úèê´WàË¿ñ8ðAŠû©¬Wæþd¿]L¹ýCöèup–8À}™&‹ö‰Ú$ys*Ù$UBÝŽ‰[ ØœEP|p‰Û>*Jª³Ë;ƒJY>ÿ¥*Ä¿ åø¼$ÇeH™Ùa 9Gï¬j‚-ÉâDÏÉ‘®\ó T»¼™Ä2PÜa6É›:Ùο0s»_æ)íøËý2ξ.CêŽ “–ýëhlÿw~ ÑÙœŠõ\5:´6Æä38òÏu‰Éykb§ÒÎp5´és?à\‡45 œG7í¶¦ .Pø©| “*ï4ùÝ­$×%[aFøGÞ¤/ȇÒ÷ø°zõ†ómÅ>ê¼#€n,8Áî©Ò'M~Øá¯†ªÓÙs[õ‰Ñ͇¹rGðÝ™¶¯¸¦BÙ*%ÈvèÓ[øøÃ(º9ƒž@Ù u²#y'-öígj8 Ù9`gv l³bMdfû·•µ§]ècÊ<Ø ?¶¸Óx Âù3Q³ÿãd,¿Ä¹^©pÁµÿnÊàe7">»F66` ÞR6IG™^WDÓ¹D¼œEm£ p8ôÛF pÿw¦¼e‚'ùà1ËYà³÷Î÷#P[-@¹J Ë§7óˆÒžzÊÌ{ú%Ñ鬅ëÉ`€†4M çöÀw½:<ÛUß4 ¨fº":h½al+gO÷K8û¯6J‘™¤öçfЧs;³š Fu9M,×'«¾kÑ7Ém°^r+ûøfX«Q¥35Ùêƒ*Ÿ*qâU5×Bá/k§jFø‡®25OÄYÛø@œU) š\”G©ß•ŒWu2ô® ‹¾séêçÃxÉ.ñ¿çîÂFåÝp#n1ýç(L ãŸÂPb9u¹w•–¨Ë“gQÏ`eP%Ä_襫¿ƒ#²×p«Qøm§yGøwŸ˜ä9–…Œ&¯ £íï „§Yr/õƒ¤‰Õsh"+ÿ¬%RZûiнð@'×Bªø&™0ÏâpE‡4DØÓ–m¥tAK&7y};|{K<æËᆞ-4eËKèß9Ý ¢ß”Ì—§Ñc¨ŸiMâlfãá‰4:~t€^¾Ü¹ªÁêB>8´¨] cþN,ÿûÝ"ŸœzäŠ1‘«Éüͺ4qcŠÝžAŽvƒg“6¶]@ß:}týÈ.ÛôjƒôNe÷”´†7î£×3_·Ç›ŠyxDºWr|É“2cÊ?´øÏ^‚/ç<éSw12*s‘øªl¥9SZ¡3²R ÓèáÓêÐüð2š?æoË»öß„JA¼“!ý4BöéÉQÇNú¯^˜Ô-èÓê}X—eH¶ù8êAZw®¡³îüƒ»ám´Þu?7pA=TvºÓ¾›8üä4V¬9J…ŒdÉž²*øÞ™¬¼¤Wâ-ó²d ÆKÙV à/|%ê‚ÉUô‡êkñêofS¥9~F‡¶ô˜›3›¨µ–*±Y)ãÌêD j½ör©aÅa÷K1âz/™ª„.¤ë7“0û*P挳wÏ18=v' 2Ç„EAã‘svË0|Q&‡ž-§–³ãØ€ ÊY Jg˜¹ÐMÞ^´?ÃNÛÄâ1½@”üŒÍ‘jÄé¶ =ªPɆHÿö9ê#ö˜(gwƒëYK:õg Œs㙢aWân(㛇·Éœêym ó§…—˜«?¹ÜOœéLKWxm1«ß&1¯æI°y $ؤ3f´ë`&ÎÕÏfHÃý<ÉÊ>2¡ç/TËO~±¿JÿÀÈ€%mÝpÃåpd'™'|+Nå“DÛZü.>UþÂÖƒøéýÉ+9h}r&l^Ÿ 6rñK¡8Oúçwž™QxVzÃO&‹á»Ì<<µ´ŒóÙÜž¤-ý v[Wa˜½1ÍïHe“×tàø„ú¥b‰ÎÌšE¤_‰P³aiÅöî?\û<^<‘zxÌ'娄©µÑ~ÈUhDS>1*!qš­m¥ž™ñ抹äùŠi4ë¬$½\žÈ>PÁ§ŸŒ1‘5Áá0^úǸVyu3òóúoI9aÝåf…Ô>ÐC KAúq?,3‚H*ܹ‡Ðo9çmöø˜²”úi¤p‰ŽbMª>Yo:…è—æ3'Þ»¡òýt4úK?{"Æß¡È½€ds‡QöÀÚº=à ì \DÏ'ì$ö¢ä˜û)ÈŸÉ8]R!µ×úq{åfoûš±³Âr…“ô„¿?ñï[‚{½âè½–fÓ )"xh'=¸”žtü “×÷ïáçóèÖOŒLÙF0ZHì;ÅÉ*•Ñ…9^ÏQe0geï ·;Îh %ÒÐfË)8§9…XÈ׃™âRíš§#«±½Ð™S•‘D†ÃÅ©ú¢R¾O~ŽÔ±\O³F…¸\s>Šdë’·­÷ã3áƒ_8©žA<¨@eo[*óL‰UÝxžîý8õ”wÒ6ÃL²þ‘É‚ià/ì¸Y<jbiÓ—4¨c*½-0™¯.ŸbûÜxÛÆ{Ð:Õù4ì¯Ö°Ž^±Ÿô×4šŸ‹uq /Ås«ÙÚ³ ´âÚYæGÙ7Ð3ìç·ÕHü³,z°"—ýjóDd‚É«ÕvpvÄ‚^ñ§£[yáô= û”Þ:²Œ½-ý:~iá=-Utog'ùñs OeÉ_K]Rù~*Yø#Z{ÞB§(ÑHå!%BÅc½’z9›â ^Nge'ب“íä{„~·6“Þ âŒ†5]]…¥Ä›8.Ê„(«8ð^¸ÆvsÇ»}Hœüf³)WÈ÷(l~‹J{â!ô˜$†KÇ™ê:Äf°›±ñïæœÔüÃzzŒ ˜Vn|WðÌÛ/xò²4ɼfH:cÊpÕô´Ý¯J×ñŸa=Oj=柃Ԙ2“]ŠçWœÇu6•xyà6îºù×î+ÆÚ’qè9,I·¼,ƒ“cŸáºK7Í“ƒ½›ñ²]K/+âîLر}pBý̓õ©ÐçÍCxs­ØJDâ‘Ïoþâç4º­õœ9‚ÿ½ÐÛCwŒBÿ ¢ü3¶ 9+¹„(COÐ|Z°Þ—ê šúý¢xôa ØU²½Nl¢ÆJv­©Þód<[qªð>’ý9¿3ÆçÌÁuõX¿äÞトZ–K˜ö•­“{©üÿŠÒóò˜ú 5ÐŽ.LσAt µ8d.B‚Z‘‘ú胭y[Q¨B†ë Ñ«¯ÿAÿorui$òü›ÌU5‹ˆ¦óy_ÁbWì­ûùKmñ4Oÿˆš*U ¸Ù›Œ™n…«—Ÿb÷©|öE1,12&S…í1{å(ê^LÄt›{¸ý“ á “R§óÐGSˇv?}Y£D÷lে ¨ÑV~ü³Lž4$œsÕà+·‰Ö]á!¹ yìÝå@skŸ1)Wðù÷•tar”_åEõ;)p?Èÿ¨cÛ¶/ÌÒ FDw×)¶¦x2âØ÷³0x~¿÷x2nûYC_Æu×N˜ØVÇ&Ûýá¼½ ý—\è³-©¬ù2m¢•dFÏÍ¢_’D‡è°Ý|dEíéI^©B_9M#ù—Qʤ„Ñ•ÜI릊ÑÓ#kȽñ8XþÕ ªÂŒè¡5ï¹ɦ´cxz_ýæjQqS©ô¸?!C]ú`sO )Ó Ù†vÔëh:q“kÂh-ÄÉóÖ»Ìú~p&¿µ~žý:žHîÿñ#½Ãò$øe* ß³ck[žsí…’ Í£7D6’ióÜi¨G6ŽöA“×ÊG™^·=WPΖЬ.à]£Dð§17iriùA8£S ·¨Ð|~. Î?Ø/¹”‰¹”Ž«B@_ ›á4 3ú¼9R…oa÷ž¹ê%S*!f_$ hºžF4|zü.„ Õ lüîóð·#ßÄ/¤ .ðÍB æn$ò½°O¡ܦ'áÜGj$“SMc¼o‚Ã&ezPÅv+©’DP¡Gž¸À;er_ýÒ¤A¸oàÏŒñO!šïeQùä%ÎâùÏ@À¸¿õ£ ^-`ÊUlˆoù<·ßFã˜(X'QEâ\Å ©;IVðolOÁ›ŽÑdhái8ý/›ÍíébeOIÀž3긬þØõšã†˜66kchÔ³QÑ7Sò×%Þ ”Â-ÙTºo±*÷®{3L<ßD6L—!Mö9VÿŠpSÉWüìØ –ó™±êL1¾ ÊÆ¥-KÁáñVfÿšÞVC¯A0­t¢Ã—ÞAIC'Tž­9+…a£-òPG^²€Åwi„ÖùA¯•¤‡XQ㕇PãåBð;׋J/°18øÚ¨Sóî´9¤g½çHCVŠ9cøÃ‡ÝVø¯|¾€÷|…Z/;ZØmH|¬LéÓ¤4ö¬ÂGöJç?γG±8_]”YË…âSJÌ}¹(XË#xsã5Ücÿ-œÃ´üZ‚¥¿¹è’-Nê¿éâ–£º$°½>ßÌŠŠndoÇö¿qx÷–V¿9‰ÍwÑÑŸ¬B¾à,ž$Ú»‹¬¿VϘŸÅkK˜»Y&0]h1í½E«œ‚Ð2"ýxüHVòt: <›”‘aHóˆ`ö¹fC‡,•HžC»ú=ÈÒýu0Ýü¼=ÑȦj#„Äí3«a4%eEéÓSè/îLÐÓ˜ †ùp߇ÃðŸžø™ÀÆæ°={ì" =ZŠ•údq -VZDþýüsea©y† àÞuáÚ1‡GM*$X°öOàQ³lš¼Ù ´m$h»Õa,RßÇÑç €Eö1yãL¢*Z‹æ†údD#÷Ÿ±Ä¬³/!ÆBŠÔîÒ$•¢’ôîw ¥ß ¹¹³sáQÑZ’Ã{+ÌV`—qØ÷÷i¤;î n³qÁšºLædáFæÃèfòpý öØÃYlÕÈU¼f[‚Ö…»ÀOãvEëáßK'È€çcîÚeðÒf*yóD ^E0É ¡p-{2C`‘( óÚ‡™’+˜ü·ÕxSÞ’„zA¡\±ý2WI0qp#ÈÐßʦ$ì®á ³ý_Ã_P£ _e¡ÒnI˜wgÛåÇó—Òã0rÚBlŽ"/"ûIß–·q8Ïy ¥»¡ûBg•0ß:hÖw,¾÷ùÛO@þ”g ójÔºóÈùƒ980·B:®¢g¿9¸i#^ÝËœÛRÌ ZþÀC‹%‰€Ì xí@M;g8ôçûÔ> š½-0+ù9œaAþÓ1ç‰z2«î[Òeç„ÈÓzL[Oœe¨sMkkš¿óqÆYư¨/ýÅþËP¢¶¶"¤ÐòH®ƒŠ¶0ç)éº/Œÿ¢(”u¯btŒEàtº>m5ç#Jø™²ã ¹ä&¼r\U½ôE1;<*¯ÿtÊÇôúئ\s‡­JÚTåôKè¯ö¢[¬bÑ)ÝŽyíÂ.s‚z¦QjC4H†»ÏÔñº‡0ä¯&TT_Ÿ^j´bkÍ`·M‡\Ózà– u8ü¼|9Ô©etp ÀiçVØýw9žŽ?_‚’ó1\ü{íÖÀM/ìØê`mvÑWÈâÛLÿÓ"¯½”În[#@+sùHå!CvZ;÷˜=¬~Êê§&#ß‘©ð÷I2Ì ñ…¿›’Q3WÊ›³ë"ÿ¡žo)Óûã [Z¯@&šeÈ>¿%pÇN‚Æ4S”ŽŒ¥RŸÛ™ßO›qV›±}¿” >ñm˜Ï©x¢Ëû:Ìg6ãþâ7ÌÖŽæËóÖñ2Õ,L=Å!"ê¢h:P£Šp@ã[šó‘ówÂŒ<2IÀòÖ˜nŽ×W†YúÙ÷x¸ïW Á‡sü˜ ®N†õȽcA mûãGrÙ]Êħºµ]ÀG‚&¸À§ ï{:°òî<4$ç=30 û×¼ÂUûïÀ¼z}X»kÝÝpŒ+ò»‡ ¥¾‚9LÅH;pT½Aïl17¨“‘Ø6<µ‹½n…IÖDLL–hø[â=ù4°²(=‹ P¶â®r“¾vgx¼ç/Û•4ÄØ¸°øÙZ›~ìç@l1iýÆË.Ó—¦Œh0*¶?qØ~ÙþP!˜qhÞŸ~ylã1ü˜>«``I v>EýW%4“T²cªêô—a%s|Ûtúbô;6r­IöD/Ó«‹Õ_§‘ªQ§ÿìeœ·d Ø²chø žUu !ŽÒ ÌÏ0oœNšÐcÞ)t:ûçªàŠ¥ml¶ïæÁqæåŸupgÙžò¶MbQ<…µâ™P]úƒµ.¹ ³mÏÀùO×0Îô4.“âeþf&B)GBÿtªøÞœ¯}…kt&† †ë~°~®~7µ¸»ÀÜ- ™ÁŽ*Ú“}¦ç0V… SíãàÇ+êÃ3†""*ÀÆ¿à6hÛ3WÔ°ÕÓƒhßÀLÎÚìù°m^Úµåã¢åzàv=‡æÎßÊ|¨ÄxO®†ËÃüÄÈ/ Ï,®…»THªæ>"ç—ņxûOÇúy%–Š:WÚê09Ît¾hÇç®ñ—­¬q³Úv§‚—¥,ÙÝ&Š•ÛÃÑóX¤D*‡¯% ùá(^¨— ÷f飅ià’6.2ô¤+wáœ8Nü‹Ž.oë«™ß~µxÆyü©8ŸífzÝ'd¯T ÓlЂ™ƒnàÚÂF \„+ñ”ŠøÒ?À¾†8¦öüJô:\ÇYIÿA|è5öÕÈtXWÐr9Ál^ûÒ·µËÿ±‹FÖ³k#„IÀýFÿ/7ñ¬ýà&IŒåÞ¢s»í^þ¹­Inþâ%qj†ä„„ Ù¬µϼ LË:kºÚçôíOaÎ;„Rï2¸Ò,M&B¤WsÇZÿbÍßæëÔ©ÌÚ¿¶pâÁäø¦q·9F+BôÌ£<øãÐ|Ôi„ŸŒ¤þÂô[ٜœcÝ8©Žï!gk73’œÊç,G•c‡1±(þÝaØ>`¶ç‰Q)·HXSüV¹0Sâj~fÊx;Ø€üýØ|æ ç4ƒRS ìôEfçæM‚;=út]½S×h˜8ËG‡ß”ÃÞ1xÔVžÔÂ[ô<Ô ;ô–°«»OcÖ§8´­PådùipS²já7eî18lÇ ŽŸÅO‚ÁÍf'§SÍ2óšØÈëbPÇ´bÙ9^ì µe>[ŸÆ1÷2¸ó™~ïÿʆD€RÎX‰6n/‚µa˜zx6 Í–£-ÃM™ërqØÉžs̉â6#æÀ‘xö˜D$Qxû.E6ƒÙ aF'“Á¯CsqgãtŸç‹¾Œ|L‰‘ ÷'ãôîá‹Ð?x•¹ÃÃGR ’™‰±ZðK™S¦ã\} ®êÓ^æc2!;{ÂÃ_è¡àHÞnZÀªùþB±.034!‡N.aÿj`çµÖåÀÌrƒh»ð3_ó{Cðð­—¸à¶)˰7qÕí(>s1å;wWÿÛ˦ª‡c2J(r1•ò»EÓAN±#Þ:ÏecÞqº—ÆÁu¯U˜¬éPœ°Œát$aå…“øvÚûzå³Ì·?‡ñûE/fÏ·d±‡Y²Õ ßçMò±)Éàò‘S¾ƒ«ú8„xćF,=ü³#x>µH««Ó鋃¹¬Z¹Ÿö vðäâ¥õ3¨S¸‘öxÊÍéÌ5¢´"í0óç ÁM¥á+Ñ#þÂÖœã ÛŒSY¿œ`<â§Gçÿ}ƒL,¼|¼ƒFXÑßÿ¾CÒ¥ót+V@g9SM!’2÷'ÂoÄý6†¤UP‹¬¹”F²„bèÝZ)2åt1{;“‚®ìl-^Oa¿ :§…ÒÓ)9xy}PÞŠ;²nbˆ€ ûxTƒÙæó“¥Ü™œh;T?Í>YW Û3 r¡$reA\y&§¦ì=7lÛS|u¬ƒ™Ÿ¯€Ç‰ ÔZ‹Ó£ãpó{hŠm%½rz§lóÄí*›h¿+ÖýΟó²Ä,Y‹\+º~7F ­azƒmÏáÅO6ÚãñÿÙ/|ÃM˜pdB/}‚¡Ô¼ðo'T:L·AO”îþÇ ·«’·ûÉäköéÃE¨© lzØGaEÞs6å”+£Z‹*›™ù>tþ¡^[”èæa-¼ßVó=¦‘‘h÷æ´.b Ýj>}°{ ÄûÏEÿ-즣ظÌt¶‡cÛ×OlÖ.IªnÉ®8 1R”s1÷?qëÃÈ(¦–£A…Íf÷À7\î~B†ødH³à*ðœà¡ã·a]c4¦+ÿf£ÊNÃpÅ~\¨sçWE²Y ât­Š Ý¡jD†~i’5”»V€†Flf dçA´È ö•ÙIÆÞ:–4$MeˆïM´7›D^p"ÍM¹L‰Ä¹Šc++Œ÷ÌÆô¦5tº nØëƒoÇÁÒÜ]ðúœ¨ Ö‘ ¶tèñÍÀ¶5ç¡%ø  o9ª´Ìc Ç—°š÷MÙ-gæSG©<°/עט6î.Q[H®än=™xO–‡nü†nC±äGì-º?Î0K"§ÓõO(êù³s¥—±ËÆýˆ0àžJ!¿?š‘Å*±"SFa›Ä6:wu ½LDµ.y*þ^O$2ûÓÒ) \—'‚v—ñÊsiêòEœn C=òü°{[6-ñÃ÷üèy¥·ìÓúÍàŠqAñ¦(:uL‹èú³ ®nâĉ–£Ä˜ -LÄê—]œèÏß°XLž¬L9ÜJÄíÔÉf^i"í¢ŒÕ¥Å«gãßcôÚëN{àIÞJm ³kd™ûVSÈ·Lu4mt¡ÞŽôù´)Xoú¢5>d4fF±‡ÒFعVrTºÞ^^G§;¨R{¸ËvË,\AO–àé}âhÊÝ;õSsy#É}ËG_^…ãbb”û†—U]Çe=ötï£np|"ÚëPõ¨#81º4ZÒ[æÁìàåOp[Z ‚©B[¼¾Îš\j*¸‚™»k ÝÌœÇwùɤ)ÆÚRãèHÌÖÛã »p§,˜dûž¾=ðÂ:™‘ºÿl> Q¹åx[\’žïËÅ9­íœÕ§¯À‘Dy²ÃŸ—º4%%ÓgÁ&wÒj„‰2‡6îR”=ö¯|`±?˜a—¹±ŸØ Ñ&ô`»g¶§)ç÷r s2/êô»ݔñõI$gÏ×1Î;cA7à1öx’…v‚LGƒõÜ‘Z>Jô=¹B_ó&ÒËWß80Znô»=¹5‰iÌ_cÎïÙ¸f(‘&éŠÙËSà‘ýì˜iußY3»h8¥°œÄèAº½(³dû$Ω–¡ÿižÏxÝ‹fKhž¡ Šù;©Éí»8[z~’ó€ó6SHñ·{ìÞ½óÉG«ý˜‘V„ƒ¢t¦aþ9ϼ>¡Cä^Ž"Ï7uzÍa_š”â™6í3^Ú< ÞÊù³ÃÆ¡÷­:ÖçyÑ£XSIÈŸd9.fTa.L(hÐ;†p%}¶üd ø>UÉbáÉŠ<¬xiEW65Â¥#téÏ™¬]˜+v”â¿ö3¸pãš8'V9,€“‹„±¯^’x¤â6‘¬vór®è#PñH“ȇ-Ç>€²@4­Ž„’ENŒ•~?ü, ¡äÔmØÊK«Üä©v©9Έ~‹E†®$«”Ô^ܶ³5É,]úH¢Ô‡‚¨¹þ{H[5Þñdé­Y„”òÀñqäï¡«¿ÖBcÑ*šôP”æµ 1ßO0AÔ®sõ¥~èHB‚òhʦ½¤°Î‘þ0Π¼[)~è£Á"ÓiŸ¿'¶WZóÅÌöG«Éú ü0þ›-ðù®_½hÇ*5T»‡'NÇAîôJ<~¼•yÕ¸ r®ôb?\Á«wlˆ«ì*²>ÿÕñà%øº;”˜<—¢]Á®¬«Üz§:ëˆ5áœíòtÝŒ÷phU<Óõ1üG^ƒÈ†NPÝÆrÐðå)çvW\Í1A»­Ö¸Â,]a& é¬èBasÚbs˜­õpo4Fö~‚žpóà¶^ÿ ³iñcf7³%¹Â´cé x¦MÙ›·p›Ý8Ó3-€¬«áCÉ_šï0cÆòËMˆfœT¢[wÏŲÁq˜÷V•½=?}N#®8Ý’Ó%(o² ¾önÂŽMFDñ¥ =Q䀢ױùî\˜ãEÛ1`¡…mEDòÑuàM®å$Énß;þ@ ë‹ QþÅOÐ+È ‚óO!h)Ò9MTúðL¢k[¦½ÃcW–Ù~ú÷8£°–{MéóU1gì¯b§?{ÌÜ™ØFÿü˜Æ::•Á©ø|¬lŒdÚ”k·8ªqLiSH4Õi×#aòYÐizqí~vk¨ÛµO€õó¤gvnÄì¡ÍÖ> ³…zY¡¯Bwę𛠑å T%»TÓTáœã08%‡É78ËfTà^¦ K@Ê{Ô?DtƒyÉ™²©(Çg@î˜ÁÃß3Xÿ¸%ã|~S…Ïo9³FE\6ç8–Õ*«Âô[Q$uJ‚Y—Q~ílîÙºå°!A”zŠÑÓ Arƒþr¹™,Aô×KÁC-[zß“¶{ƒÉÇÈ[~‚¥eç±þ"uܲÓ#ÆØ9SðâÒ²äиÊ7¸nÎ ¢WͰ-Ó?Ãö:1Œ1– ³V¨Ð‹uÈÉÚ2¶Z&„W›Ð÷RcxÉy Q8±Btqά­†m ï vÜyàÇžEì`–SQóØÊìx§C⥋QÓ‡‘~arRV´¼È—Ì[¶†)¯þ„Åq…4kUn PAQ SZéúŒ)ó]ÖrøsÃ=ÌýZ>ÖÛá˜Àc‡tñ—(àsŠzÕ©ÈV^øäñ–¢•+‚ÒåjêÞÄ^µì@÷"y⑳²³ÿi˜e•Á«T1ú6n =:_¼©àî½ëVDqEà?&g“ÃrKr9$‹MK= kÎ^ÇÍŽÉõ±mXóÉ_D¯†ÞÜÙ$îüFÚ—•‡[úŒhÆKUìõÁJí÷Ì…³£¸¨‚õ=“ƒ#Y?™o‘2¨^¨HÞ¿Me–LÏ„•‰ÕàU‚£?êáˆU š-æeu“bècWkfxî ZTSȰéȯñ·¤£‚¤çÃ5¼ù»‹b¥IKó þ%CóÝt¨8f€íSÞÃAý˜7ø9ßÀ¡õæ ø¡[p9t%*zn_Q¾â4ØH˜š¬^b/²â›B¿À¬ˆwx/3ΟEc9fbǺßñ‡íX¶5ßÂ_(f,½@;ž­@Á(iz:a3.Ÿ9 L/•rüg9Ó!¢É¸Ó€6ð"£‡yÿG0^HeZ]Iýø{ô6KbçTë dÁwâ|Eû'Q¤ÿ}»6ð“°í1o(e=·åN]¤ƒ¾Ú!éÍéÿ´Ý8b² §É3ŽJ“<"Ÿê-©‰NCj _Wr.‘Øco¡cû]y«AkÏZÓÓ“¼5±:¾Ÿ2?¯üˆ»Ž¬%¦³•{z&¬¯þ‚ïúaÂ4•ûVÞ¯a+|•A¥míx|Ìš<+"<ãáäÊŸó emzÁà‘5qµÁíÏç€á{{"VýêK“JæÉÂÖ¿¸†yeVš÷UèÄ(¾šwöÝK®µ¶ã }Ü4\òÅyPéEŽ_0"ZE7̨†úöaðÝÒ‹Æ÷xÈî@G8¶Õ¾óŠ °Ù¼òL‚ôÄ%ÕßÑÆ¨“1—£³•„Ù ;pùÀC–Éâ¡ÿi˜5viCÏðè`=+» K¯±û¦­Õ° ÝwK°ò•;m¨—¢s¨Y7ã2þz7ŽÍæÇ‰˜œ:j’©¥p¡œ8*6Ã…÷C©Ò$^ªö# tMÉ8CJÖ 8ÏQ6<¡'ç'ü±P,¸6>°¡.>—¡]lUn™¡ÿ&92ÃÌŒÚÜÛJƒ+DI¦6CÅM–ƒ«h¼C²†«NÇ•bY'¹\ìã.¢)EŽt{¦,•æ29–TUv'Ù|dÙÁ•‚÷w& °B’þ™BÚ,n@ÎÇr¬ýa†× 1Œ¥AðE‘RBÅ3ÅpÃC‹\ÜÈ=§6Tü:~¶¨q3ê½Á×µž'ƒ»÷äiv¢ ˆŠcB¦:=}-{'}'éAåv9Ó%-;àE v¼2§êëøÁyíUºŒ»ÃÆ;« 5¬´h ÷H,–¦¿¯386c1é.Tf]Ê`¬J™Zú¤ ‰ès9ñ0uüS Ù…ÛsZá]ç3¨b Ø* y’:M€Iâ¥é½‰˜·P™rÕYüîH8¼·`§í<*ôl!Óó¬S§År—mð¤RsýÙ_ÕJtjôúIZŒÞqÙÆú(ÚÝ Å/>1…Õm ã+ 5  éA ~I-† +IpàU´²}å'Ø4¯>ä^«AƒO½ø-rÛQŸË¦Gì…ÍáËÑ¢$‘Y²* nï‹æ¦äÔàºÍ»‰¦ø>ì:ÿŠc”–K•Iȳ@œþæa'yâ<öî3ÀìA»ÉÚåŽß/Œ3^)Jt…ÌÔKœƒÆ{tñÈUIvÃÇ88ÖÃ4æºR•‡w‰äM÷‚»ñX%Šùì'*]¼p±–xž£™žJ Ƴ—)¼Ë…¾GX§žLãûÅwû!,¹§AcÌ«ˆËŸß¼Ø;ǼfS¿Õ»ü[¡!ÃñTvDóV†{fýsœ>‹¼=رû® Ÿ†ž÷ ÆÀ‘â&î²8ªÈYAT š&eaOzÏé5|KÞŽÔ¬¶œ‹ 6 ÓÈæVp¨ó¤s&>s–Ãv%„ß\˜jìæ¡E‹Oâ¬çÄéÑì×máz/¨@3•&6Yw3œÏ5¥óBÞ¶N\á‡À^{&bQÏ)d„5—s2_äàÂÆ|$»¼˜Á‚µPsVιÒ\3batŸóàSä26™¯aëÎ÷L—…ˆ@´J*íÙѳ~/œð©$÷_«¾óøhвÏÖä…h%Ó«õ—ãc(HžE-‡r›**?2Ž?}ÞÁy31ŽÄ@ å¹›þ6½hëL‘åï–¤£Ú‰ý¬É4xOc5™¨Á*ߪDø^ã«WÞð!­‹æ&ï'©‘êôÓâ zø–Qõ›Ãaô\¯;ÕéZ‹•òßñ_XIÿ­‡YÕ •*È3 ¢‰Àñ9FM«Ç5¤ñ Ûñ½‰9é_bG–[GqCÏœÂyì5FÅ¡Ö"û>F[vkÍYþˆÝÔVÊ´÷‡kãÇ!ÿüÄnšfJ^ÍKÅó ®´ê‹—v¯†¨ç)¸ø0«ôÖj<ëQ6ª?x¨à÷{æä¶– ¦OÍÂGʤ©»˜Ùc){®\‚5þ6Ä箾*_ˆ¯Ð'«à˜N$ìÿš5™ëDI>Wœçá$J×ò¦²‚seIíbC²dK hWíÄh=AªgâÅÞPNåÔ¸Dºë/b™ã.ÒL˜aÕÔc"ïÂT%ør;Ëp Ѭ}„­ÑÅ5Ûù¸9¦Ô¦Å‡j^â¥*ëö;Öx QËß‚çÖpž)#ç’ñ´T>}S±ùþb„ö N{‰öâRÎ"ЍþIÂó<—úÛIaö϶&ýNÔT|6–Äð£ËÄ"lÍ ,Œ—B,·¿õ¹ÑwW3!=ñ`76j¶2$¨‰—Êï€-…Û û /IñP$kz@ ‹Qÿ÷*Z³žu6ƒ˜a87ï ;´t7Ü8]7Šñý·ÿ4ÛäIc&™*`Lõ@êŽ޶Ïq8ÂO\r$¯~ø-ÐGa¥ÂÔiV:QôéÅ/KɃZ$õi9³¸¥Ÿ9ò¦†ÞßÜÈ±ä‘ Ú_Q“Ùo‰Â`0åO#„ÿÆTè Š—æ’Òr·1•ÎæÂ;›tÒÝ!Iš%)w(poÕ1¢ êáiWlQQ¤ ù? çž/yEF5½iXE Qª(šÌãÑdKè4Š'â¦EÇq^v5L]«Dé A\ÁˆM9º–ˆß)%ŸE;Y]…ͳåíz—EFfÒ£sw¡aì2(^G’V«1v˃ZnËO¥ùÎCyß8,KÜ“qÞE¯É=¿åÎ}(™²—X ²ÆU¯™OKÏ@èå+«ÿaùÏV†3•ò+•p¼%øØÔÊZ;:… ˜®CïMïFq™\T)ˆE…Ï þ™ÿœ»Þà8Ó4í+ç?½tdÅ%ÆÓ$– ¬‰& áïзmÑô²ÂÁ”ÏlÈkvõ¿“—“Vm†Ù•ÑvUñÐhÅÞÛâÄ›kC®{½d…6{ÁéA˜·ÿ*Ÿ$‚Ça™x=žK³ó/OQ JrmžC_=pw€sãU¸Ï[æaÏ@MÔ–èßÏ&#«ŽÛŸÀË×.¤)+Âó“X©© ô—[n½õŽGÝf¶7¼Ç¥_wÃ~×nè +g:Ÿâ&ÞXzÊAŸÎù8…ÍÙY :ýYðýê%Ö’ìH"r­DgÅM¶Økùä^ŒÁ˜_ÏPZ¢u_$ Û·G‘ee °HiB³ÚáSW |]?ƒTñ8Ñσ‡ñ¸‹9à«ÅD· œ7Œ'û7‘ Ÿ¤YE™ÚÍÉâ¼ë{½ßëÉ× äÕ¯f8¸ ›ÙsY‡$›»öºdúºà1Ý“I÷<þ²k¶Cõ‹×ÐJ‡`¨}!0—®Kô¿ÍøØÄŸ®ë`¶ IãÙª,"ºßüîÆ!¡Hø(¹Ÿ{æe1ÖÝÍÞ[T†:9¿à¹Üoœ××7a£ÇOHÙkÖ§‘º•ÚÄ_#ÿÓiX8É¥+VGŒ›qÖFü¸ø'ž–8‚ 137؆©Ï±#© ñÐ ˜Ðñ;Ã]e~Ž}4lñ à\ÑùÉ=?ˆóOu¡Tç3&âj¡QˆÄau]šòÌô?ý5Ú½¢{ 3¯ß²ßŸ²;žrcf¢ùótü|æ2ÄóúbÈóTqçP²»”/ãÆD90jbfo^„•Ëßà6?Yb§Í(=7¢\ÉôzÙY¶ÇÒç¿g6ãA‡ú(ºŽ»9àŽ}s…ò+²ð,ü/„ý›Mu[ Ðû³&±sL«óÇ@ÙÌM8k‰¤šÚý“9•®ðÒM‡TíN#·‡xèÍqÄÒL¨õ‹o ù;ˆ’b²ÄðõIö“ùUvfxJ/1%_?€à†t®ÝG<>dMØ¥ûÙ†‡øÇç7òj¤áÖÙ¯þÍpÝ® ®ìç1võ£ý`ñ’!ÊæJИ™DçO]Çðü8‰übåèbRŽ|ÎÍèÍ©c†‡qâØz¬ÿ+MSãïb]¼&‹T‚¿Ñèm—ís¿üĈg¢ôs´8—.eD×ËÓ¶`êàý”){¶–uï§ÅOѾp)-S„HÅ8™Œã/ÀÁ]©¨à‘rg]„µ«NЙOá;;O@þ¯ø-Ÿtïû§ÊŽá/‡e4¯ù!gåÔy´ü·"Ù4Ç›Zwã°Hñ¿àEÞ¼à¡ZéDÔ=j?#âN3/“ˆ‡Èvzs, Ö<ο|ELŸåtÌ|„«Ú_ ÚåÁe:^ÙõF6žcƳ*P&|ÚÿïžF,§-%ƒ}zmÝf¹®zÖYÑÊO+Hž`#šØAßKútƒ:µÛ8tLÆ`jo6£ ûäR9wNø gäƒ)Yòë(‡éR¿Ã|Ø­¤ŠM•%('½¢¯‰Û ÷`ÊtKòÜמzQ ' “éÏ'SéàF¬%»h Ÿ MPªÆ‹I Mþù‘õÙ6•ÆéßV-šªí ¥›|X«ÀV÷¥Ô&H 猃þî!ø²V˜&¹Ì! K¸æßü¨‰ÿYœó`J9­$‡¬„‰ÿêf˜ižGÃ÷§A4?k‘¸NZÓµ÷§À=m€Ï…mðÕ®™ÖÑqùäIv‘)ûB™¬ "¨F·,á!ϬCÉìE`ßGutŨOÌo\œ£…gØ'ÙòôÀÖ&N¦Œ‚I‘5é;ò哺2Ô’¶Y˜¡Íß[XKƒÕˆîA±û¬‰Þ*|f,@j»¿ãüpº¢‡‡ ¬Ö!Ïâö'Ìþ¾KÒhó“r}º#æ|d˛ᨠ/8zˆ|]oE£.Fá“« :žÌÈ– {§ëd­F6=: .jq0Ü[Æ`²/}õ¼ã¸!ÆÓã3¦CËéy†|ýP‡z`×ÛÀ(î¤7Z°¥ç2s>z¼ˆ9Ϭù—GS³<´¯OŸìCØ6#ŠHªLs¤‡ešPxv¿:ƒœ5Õd§Ô]bìòqîÀ|ò(L›Þ8k…©/ºèiC_<àÆGÞç¥H’‹iZ„{8üÔ6ݬy–E?”é_1 $WÁIZ¤áu2\ Ò|>¬ôåà Âgß ±¢IdpL‡Î›Û‡z&´pPœk]J£LsE4Ü̃¾²+tIÍ)òQö6^žSLfÌ7§ñôëÙXœœ‡½µ8‰Xœ4¥*M¤ÕÓ¿ÈçºÎöA¾Ø8ˆ¬Mû¿ÑSÊe±®ç 3zNžI «‡µ|ÔØa*¾;ÝÀüûü_¦™P÷Íð@ˆ—&wŽ«‘÷ï‹èºÐ Ø"|•Ãô0 qíøYÆÌ8'®Î$›?]e§H%³óÖö‚a ¹wå<2¯Náü·æ Ø‚ª-Käe¶B—v|nš‰Ù ž8´r9™BtP·_ì ÅôßfÔTîI}7›¼nQ£[MYÃ[úð}¬‰Õ×ø W÷¼2Ï™4+ó:Z•ÀÏÙëÐðÖa“VS¦à× (x¼ÕëRH€ôsXÝ¡|z˜—¸7PÕO×°°ª?ˆÏ&Ve¸i¶ɵ3¦-%-Бpöªƒ–Ö|ÐÕXF7äo$½)Þç “³)ëÙ5a‚ôËaPkÀÔGdQp³í³r?I}¶…[hã­Ãðùã Ûú>N~ì†á|dKuñUïÃוbÜ–Öù¸4&\³-½“uÀóuå=_‰¦†|ÄÐk.Qú¶ž¶ÉcOY}‡/T³¶o7Õáw»ûÌ« Xøê}æ?†S¼Å¨ß-ØbG;Í-èe÷˜Z ˘[ŠK˜7êr¤­x Þ´ZÇŠÇÝÀ‹±P¤Æƒ¿8tåT[X—\œ¢jæÒ‡«¶1BÎÖ°Ç¿«â¥.\ŹÅD+F³2×cØ‚1)¼!îWÓ&k™e5|€£º8ODÜf®Ãˆg<éf‹9)ÖLP¸,¨Mîˆn]М"u¿?2›Ì×Ñ3«‡Ð5â^¹ð ±Ó£Çdß¡êUiœu N{Ò¯¢6Äãã2”¸þ ž”KNÖçýpýOÉ*œ‹_ÎDÑ~%-²8& mëÁ»í=,]£O¶ZÊÑÀ£Ð0í®ü.ZÌЮtæ—g® ¦s…Mó~ƒ’|$/äYr_å3¦Ãô9.lê8?œ óƒÏ™¥¸K‰‹k¹˜™(„|ú2Dá­7»|­ &¸«‘KR˜9ž K\ŒéçyzþJ‹~;TJî.ÂÂè2óŒZd  Ú–”ˆ¾‡4Ë«lªÓ6¬ÓÉgnš›À/I¢—±™ eÐ0>þŸ6Ü]ij¯¨±{çbtûp%YèÃCœÒpã&b‰ø­kÞýššî¢SŸ}þü',¿þÕM7“á=åœ5÷/±«×ž„›ç™}ûàoFf Z«ëF¾–ùa5yÊT•eâ÷>†¦Ýܹ ÏX3¦Œm3–¦ß ™çàÏ ÁÌ–¯ìü™Y8•ãM~X*‚¸›"v v²6+28 cŽÄÝâ2$zφ)a"ô¼Ö}üÜ¡EW/ŽÆy£xp…!s ©;0´>6³e㋞ë›Þ¬ÅWò€¹p‰y˜B`lu ã°_2.üþdÎÎkFS¶‹½ñFƒ½ÄËCÿt‡³‹šû˜Áݳéû ª¶f˜ÙåÁx/¬E'3Iz&K’äk 3O3åˆBb4–Í—€|›6V‰7î7¦»œg¶Ÿ'?]bY•F8èÃ.O‡X››¡Ý‰F¾è‹²^‡Œ°9© vï`hFF!ë•Þ gÆ3Ð'¸»¿Í§áÕïç·#Ѥcô-8ZÙ’4½íØ%<“ .W' £•0ÌÛ^ç”(ÌÎÇÜMl’{ 5ÅĉÇpí5A¿s%°tµ¹ÑÍÐßöCrc*$¯mc"e>±ïš.¡îóÇL;^&K¦Y⻌`Úv»¥Ü¼©t<®Ú“ |ÚÛÈÚÍ"ŒàÅ6øÙ~f˧’¯áO‘ûo5–<^AßVA»J1¾lÃïîÀ{룸*ì¤îîc¹ñaªÒ%¦úö,þÀ8LÜ&¢%? dY•›K´|¨}P7@¿ánf÷[L€¬žôçµ\T×I ž÷\0Q% É@&è- \Û1ÈÛ¥BŒsñ{z+¶•ö±¼ÝèÖ‡äØJº5ÿ¶XSïQSÒ±f/qyaMbžöÇêõð©]><Å›ÕÎ(o•HGijpáÆ*FÍ)5çœâ–¸Ü!NÆò8 y»oqèŒÂ)¤dø07¼Io©¯€)·ÄIca¼¨ˆf– zÁ<™£?S´'¬$’?ÎA¸È£é*ù0îC¤ƒâ”¤#cØP[‡S³KÀ®- $”|©_¾ ͌㣷NÙr¯¬ÃÁÎ7áûRŒ¸˜žŠšê3©í üõ×ø\tÀ©ïæ–dá-ųGSÙ”ÕoÙ¨:QðL4!—F×¢õ¢¹«*3°ìÊ|Ö\i)‰šË€AÆAš÷x7ÀÞÉàй‘oŽ,å¶û5Bˆ³”.¢ÅU½ \o‡÷*//œaäÎl\Uq-æÑWÆäüëm”Žìƒ6øÇä±é°ÜØŠ.ÛwFWpçï|±PFÓŸÂMÒ¢éF˜‚Óxë@-$5h;\Ùÿ´Ôõ¿Ù­æ¢ÔmO6d $à£` ¶Œþm5ùfMÆo¼dœnš¢ÁþMXrÌw ¨ÒÄ]DB뺷¥33‡Ýgj +}b ×ÿ<Úü‹øÍ-P$À‹=Z3h£ùyï[Uîï/øâzãÞ {bè”Åd=•ÀféyÜ|%?xÆG7ï¸Ê>¼ÕÍyü°â³Ï`òÃ¥L^«%U |Ž7à:Û#ì÷´¬æ6+̾ÿW/؆'c’h@Œ5òMy ÅÆ–S¶ Lzà$ïn&¢ð{ôç¦; k ®*Ù@§6…¶ଯï!*")y#Ê “¹öZÑ’7ð‰@&çFÄÄËaó.ÊR9C=¸=‰uBE²¹±œŸ¬‹ÖLòú*p›¾0f³`Žæ?¨• ûendFž&Ø7=…}гÈÒuºDm͌׼ÀNÚBÑIþ¼|™6Žê¦0Iþ!ñ9D/ñϤÊ-·áÜcâà à°n7wúÅj|Üz]8jâÄê’ „»×A» Ñúèb’0w%Bç®×§|ýÌEMšÚ²† ]9YïŸãõëµè!ÐÁžÑT Õu¤èœ/¼åJs†ÆX˧.ðÄè«Â'AÇkÙ*µŸ,-Š#ZAœvÞÅÔÎ’²Gä èö•šÄ©ãcõ:hD•)%¬jˆ9»=Lž®JB?Ó‡ðoû|2¿@‹ªmÎfÜ/òÀ×îT”ŠÁÄ9‚LÚouúÏ?žD˜é!ˆQV¾#ný„siÐü5¯d€šæ \ü<›íý˜À®ÞVÆÓ¶ŽMpe¿>^³™J¯Rh‰ 5Y¿u±Ô#j‡žÀ¨êL`¸sIÝ&.q“p¤·d¯þ_?iåÊ?pª›À#7VÌý8îxÞ„¥{-pË| (ª9õ§2‡×%Qÿ«kpù°!Ó„yà9}æ76®Hùå7ṓ(•+ÿÍÜ€~8WǤõDZïaÔßã"7^q 3ã·-‘^Ÿã;g¡ô‹Ã4ó;/®Ÿ?ζÜEÏWR—\`Ä\ÉÍSÈŸ#œ·)óЧÐW>¿,B«2Í¡xd¯+À³ìƒ(̧I6íàÀ…Ñ‹07Dÿ•‡N?Z`¢ÈF¿3Á,«xtc@¹3 퇟oE",¹ÁCžÿá<Ì~¹5©rn+h/(‚c&-\é°Ñ` 2]…8šº~7Ùàu÷~ü}§N‰ÌfŽùÀ­µ6ÑjfƬ)äͯ©¬z ;T©‘Vtz‡/ÝzÍO6%r÷ïž±›{™_K7à+ v‘¾6áTqÍú•ÐëT#ž3·žWc¨ãnÉ>=ÉK_:u³d† ©n_LV¦ ÀîÂØ›¸C‰Ñ‘ùbT¦q›×—pÇ_²Mnâ÷ 3²–§Ö‰¾N¾Gê?ͤõŸåȺ¨clò1z¤•àšºÃxQ »Ð …³÷^>ø­Æ»jAáÎ\ÂxÏ ­†ZTqÌwŠH“â`²fé¹'Küë¨:¿!É^Jèá&6jA$Ù{I€ è*’D¹̽P#'aáÝF"úá"´-¦[ü®€âso¸¼C‹6/gÏnŸGß‹JÒª*W¤{… mÕ‚³ãio^`5Î`Û^˜ÛÂjÙÎ$çš7ÒÀ^U˜—<…Y©nëOìa.ërÈĽPzEÂmVN'{vPž‡P©C.¿†V\è“È'øÖÔšèþñ#+¢ãÑäÃO&Ìéü?Ž®;Ëï #{ïì­ŒIæûÜGR’J %¥A4•Ù«e&‰ñ>÷‰ mT´£¥ùóý½ÿ¼ã<Ï9÷yßûÜ÷u}>×çz§mb}TbAoućm„©»¶|ád¤ÂÊgÀ9RÂ.ê\D'Þ‚dïJ+]@A-û†j:ûàŸ±>™îØü_  üf ¬ÌÕ‚ßéí ³ÈÆ¸ feôt´=PbǧÐ-L¾¸çḑ¡·c~{&Å ž`•vOàÔ]^L«bŸÈÞÒnÑŽ -J8²\Œ˜¿½p›«Ζ}©¦¢‚EŒêá@ð|IEÓU¡)g;Mi8ά݀Άv°74V‹Rý˜œýÔ—™]7ø£üQ¸¤Fß*€fµ,©ú+Å¢Eìeÿ…0×úä.–ÃÚ·žÖT@l’ÅH ï 8¨çL¤¦¢Y¾5Fånû;ŸÓ³™º:·cñE/²±¾þ~y̵X;ŸÚ{œgÍýÖƒÂÞzîðdrðr%¤ã£?RøøºŽ[œC]·xð èõ:I*ÓoDúRÖQ¡Ù’X9·øfiƒé‹\x¶K‡¬?χ‚ƒ^kѤ!‡zÉÒßaH›¯°_š%éGô!gÎ90ÿfÚÑÏ[ìè·æÇš™4¤³ƒîê²kCúÿÓ Óþ4ylZæLßE¯ћӉ>÷ƒ…òF=D»õé¿}¦Œä%;æ¥×2z°2›ßd<ݵ“ܯœfLàï’ ê½T o±¶bÔ•¨,Ö#v´³³ ±)å"L8Âì¨$v®N) ­4&n滩ԥü&O ¹Æôô}øuæn_K%®eSæ4/ìÄ$£»Ø?R×݉õeøýÌß U´~Ûn¯9Í<1u¾ìYKªÛ ¡, :çÂŒ_.¨ž JÞJo'ãç*¨ÈíAÌI¾åÀåèÝtè+“ eFõ0'Í“"2_ùèq…ó`¢Äb§ð([ôÔ†ˆjT¢Bîv L:ö®®°ëüe¸>ã±ü‘@Þxíâ¾´6¦.ºçpnè'‡ºóÙi­â´¨h:qb˜ùþÓŠ¼Z©9òj×ç„o.0ÅeÉ_‡¥PpI…LÉ*äúŸ‰ŒÐÿÚ»ý˜È›MnxlºoËèä8»Œt&ý’yŸ5Ús 5Ɔñ£ñ ²éìKfüÖl} E/|›Åÿó–®ÞKÖ¦Œ2ŽóÖîK¨«íY|¡x –™U3óºË8B‰˜I˜âXö?}ñŸ+ºT}gL¢°ü<:á Ïœ$c^cr!%—ßNÇíýzøAì x‰¶áSî/Fux1ÔãƒmŠtÙþxº´Û¼,_IþÙ2#›k·y¸ÃÖ$§oƒÌßÚ´ã‡Ö¥.&w®¹´'úáb!3ø{³T‚ÜHd™nô•¢÷Öçqïñz¶ÂŒÜ"ÂxÍêÄ»km^“\ælÈ ?b55› )æ&­{€KÜTñò[rl×+¬ 7ƒÚëêD+𠔦;²Ís.²&S¦eíÌ[ÙD4ZbC¥Ørì|ú½¢lewÀÉ×?¹ß;Rqñ@ XLJûªÂ©Æº­$¥j|´2aïRo\¼& æqb·ô ~Ÿõ®íýÁ)pïž& ,¹»}!ÊA–έQ |+¡¤ù=|("ôºÉRvLö"c½! yïl™½r˜ß ‹U…§0ü³„ÉL€_–>{ò™Õ5Ó #}gYõ ­øzÄ56‹Ré*¼) Þô£Ø`·bŠör®›ôµN çÇÚgØß«ÈÍØ'N¯†~Aù¾t´Y6ü…éÐÓ+XyðÎ<Å«¦Ò=ba06û+÷L’ èjn»oÈå^åQÀ½g3º)ÞdµPdÿB]‡3ð^b?Õto¿x¸yHŠlËS¢Slb^»žá…økoÆd~14î4,ýE Â"!7'Ž|Mà¶š1’ä÷ÇC/å‡ñY½hÁK7ž€™¯±+EŒþ„÷kiÑðÜ_oF£2kè½²SœføùÚo¼aöÊæMö‡ 3pÛm¦~›æÀ„L44u&?Ÿ#Ñ×§Ò¾BUR#ì ·ú"I`. g¾¥²¢Éçnr&OˆzL·À@¡˜arÎaÖÞKÜÁÕøwè%¨žG®LY@­?€Æ[§’¡O»ÀðM]½(‚ÞÞï·óègsÂíA;=æ­?ÏË_áÖ¨<×b‰ 2p)ç»CÕi+*açÍÚêÓ<ŽÒ/9ì½RKÜ—>¬ãµ ŠAˆ—Ÿî¯Á}>G—]˜µ=e”O'ÁÁ0é¤]7"’zö°uÊz¬;pdo²çÒOœÓ&ŽQÃt療&X\ Z¨,EUJ>£q¼ ªXF ÞÅsLáç«ÌÅ)ÇØß H»ìm 3ß™çZ“{ÏU!ùd,Üz£Çaþé“ÄC“uýWkeÓùp¹áÚÇTI^‚åÛ­ÛVžóEƒyj5‚%HÐÕÇh|ö.«sYŠt~¾ÇL39Â4¦é¡ hš©<öÈ9ѶÞDZÚ׉#wñzB*™]#Mj?o†Ó¾þøWQŒ|.Nj¯˜ÐĸHòô£$ÕXîKxv$Àx¿*Ãä®Dq¡ÿy?3éÕdá¬=°Åo Y‘99À¦"ì¨ç'YÝ“ìD]_Χªîça×§Y°ªí9v.=Hm¯u@§ÐnèË/ ò»*ažx:ñúÞKzþè;Ìà÷£w6Eå,w2'ÞŸî^q† ÿv%ç.¡û`=ví8#6~ðî hÎùÊ”ûQ0ÉØ9ö[~c_¥§Ðô%IÌ­dôt²Ù‰¹±"c¨xœ9S‹WOÅÓ‡›ÅÉÓ…€k“ÁÕ’=ÊQ¼ôPÉ1ЊÜÉ:ß1&¡–$Þ¬ øÊž°nÃñ¶M·ƒ`·çSVkÝ0™¢ oêuiÛ5 úíÜ Öl#ù{ô{Ț͎d㦂axp#¶Ï6Ã=súáoŒÙùó#¦ìÈBÿ5s¸vªôI¢6ÄG¨Ð°zq:}þy¶ã°‰ƒN"ezf·A…ˆ>mNŸ`ϧ€Zê)t›v„%[Žâç¥%lð7+89¡ƒµÌ Kô>z5ùÒ`çsÌ»3Ï9–”÷ÒBRs% gYaØ®ø7AÞ){PÚ(È8Wá0Eb7õó]JÏÔ솨¸ ȼӆŽ÷ÀŤ†íX xÌ}’$qA£<µSGמ„OäÁ® 3žéÒ¬Åq”{ňJVªÁIyyÒ¶^ŠL³|ÈêÝ>¹}l/ ûãœk®E8ׯlúØÉÎþÿyIcÅ¢ p^ý6˜6¿fƒ”ò 0 éèfO‘.Tp¹Ü_ï3\¶f !­Eàá+øÈš™LÉç»àðKhyÒo9Ž4f?H¶2ò óê/yÃBjˆ;d¿4£G5ÀnZ.²Ò} Öó{]ÿ²×÷/`­r4ùÏ·úáÎ8Ž»è=|jú4+ía¾Å 0c2i—ÅW¤¢nº} ™{FŸFí_D´r"ñ}ï8 ”HRð©a=ªdG‘3»H­ø>ö¹cn[=¤³-ûXùH ,á`¨½Þ?†ÆiZÈLƒ[÷þé[)Z=D7‡]8øÎ\5ˆÆxéúò\,žXM:_¨#yîJÆ~Ρ½¶W¸BÉ¢Àºyj2-±­ƒ€Se4ÏÀ`m¶ÜG›x«é¯ ÓHÓl-b`}Ä—³ReèïhOâ¿ñѲ«²D%z+ple%Fp¯é?5T'[YÒ›MmÊPaÅ h»Ï€XtËã™MÈ£P‰=F+è¾`9jÞ0Dø:S—Ø ¶à¬jTŽ2ÕzK©á›%tã$79m:H¥ÚS‰31ð6…&Jv1'Û^°{4Î0g®ç‚¬}Q-‚¡ïù ogA»¾,ûKÏl棡'ËÛfœUÌõÊtÉ¥k¸;lS¢?®ar°ÉüuéŸÎèÝ Êì\½å,W‡k¿BÑdÛä|å/ÛD<Ä{:8Zîgùe³à-t5Ÿ¦VA«éüè(=G‚„ù"£OÈ¿=Vømþ(<Ñ‹wV™‘… 2ˆ”B=² ‹g˜Ðƪuôê÷hò9p }ÿOŠHÜ {?D]>êË«Iç«ÓöT;8ú†$dLg¦3çB+عÑdWl6|®ŠK­·Ì”õväïN¨z¡¿*™ ›ö$ø½2YdÙ@µŠlqâÑknkM;Ź• ¢ç–hruUDýºÍnV$r£¸¸r›pÐ>p Í—À4áÎï­dè¤2‘ô§Ÿùš`ðB#ç?Ísvãjª´Â?VäÓ?ËȃÕ<ä,ß8Üi_Ƹ¯q$ ¹øäÑj²ÖÞ6*ΧšÑ8÷, Þ G_ß váO2÷غáë æ…À6ºé(?ö ¼¬Íi"Ê'éëð 4Çà9±p>\J»‡Gâdé2Uú(ØšäüÄyÞb _? C=ˆžr-¨[¶B¡^Ãóï(ø•|`~„Þ€ÊÊ4r]ô.{é¦6<úŽ£Ÿ¹ÿù+_hÍbzdÍÀ5â[ï‘D¶î…%§Òhyü%P ‘!žÿfÀþó~hx–%ßGE‰ÝzaX°½œžœŸJ²y¨Îù6(<â ·7× &è{÷;g¢kÍÈoqK7L­×$ú穌/M9nÉä¦]dc{È¢ƒC0Åú fpý©ÄP-ùrr¸ËÁùž)wO†0÷fb~˜4$’QŽáÉ?ÄHÄ.Å.§èó‘¯(—ü…û08œ<ÿšBÆDL±ÙV€$V·0§³òÑîûGLý²”D-©ˆaÙ¿‡¬l½Šß/=ƒþ :ãÅ'²-î'(ï=‚î¢ XrÊœä w—[_µýµÛC”´èö=LÍ"lhZ†ìn'°Y¡@¬ŽÌ¢]¡¢äZÿv"#ˆ”ì£#ûYj(oN— îÄý—ô0:HòžBg«ß…Lƒ]T½±„ò¤ÆfcèK×ÄçUê¤ìí|úæà?ìUwBѵ[éœöë´i#Ñ,È„LcYzóªÂòýXɯ "Ö%Øt*ŠN·þÁø=(‚ëK®ÁZ…Ý$Ïü#áý@—¨§cÙ$|Ív" nš-púé\ÐyT@ÎÊÏÁ•Y9+´»ÃÎËryj³q€}-"H&¾_†¼ g²×ð10äšï%<- ¤G8Í’86YC¿uƒ¹s$øË” ý¡#D¤3‘zÔ¥·¾œÁò!1w5¹#eKKMWÒå‚môŽT ¼h¤Äfýê5ûÜ=ÆKZÇzáÉ%{Êäå’„c×aåÇ,º¥ÿ'{#Q=ìÇÉ$Lµ…¶/~¹ëߓɹI„gçd^® š+ iÏþK0¦·“¦Ï»Ž)dड़à2or.¶®;[[åãw"ºð¼+ÞJ-v—€V⸹[ƒþH|Â]d(D‚nßdß¾Ãó¯„14(£®d3³ÊáX“>q[t„•ï_'CRñÖ/Ч/÷ìÂâ9X“†Žž]°?ŸB×$Î?üÍž¼š#Heš:ðÊfCú~Un-WÅæ—³9;ö+ЭoUqd~Ô,Ld-ÿi±• X³P +Òáûj?xJ$È®ŸÝLjè:ÚdsßímÀ}bÉèºM;•â^‘TŒÑíO™ Æ;¨ØÝv!ÃLrÚÚÆ}…mÕ›ðßÇŸàóâ9,ï‹e›Ã™¿)Óˆ~…j~cÅ¿_eæTíåÕÇu›©€e¹é[]7¾!½æÁ*T¹ÓÏ!ß`UY“š™-­ç`¯5•´-%{¦ÑÝd Y¢s ÂÄ#ÑÁò)ìà7GANwã{ÂHì”áö§IÃhÆ%ðnÒÆ$-=:Öó‹ãtû(3œBZèÒßèÛl†ÿÍqæšT²©Fö,å!1ÚÓÈžýØä:7bDÆ—2û¡âò@ö·§£‘üï-ÀØ]‹Á¦&•DHûàÚ.cRæ&L GÑ&]—*å Þ(Rí‹®Zе²ÛØð×>ž­ó—"St´ù•B«¬ãhŠ&=÷ô.á×('Ÿ -¨ð: Ò  E.þ…ç…š4]·ç¬ ÝUj4Ëù.QÜŸ_œË¡Ñhêx7“r9ᦄ U°}lí^cMö^O¡<"žT­|>±Ì~›g Š@vZ›@v‹-uµ×A›«)ÒÙ$^@ƒ~Ž]ŽÒfId†é%l|zžL·á)—гÿ׉ID#ƒU´2R4¹žö"¢²ùêú³ÉÇÀy,ŒÔc¶–á{EÒã¾£ªŽì]¨†>SGé…›|¼ŠY`Mn+̦§&`­NªË¸¿9‰¢—[Ù£M<[k0æ³ )=Šäq2òø n[º‚Š—|‚îûF°UÔ’ìÍz‚­½ƒP/º S, {ÖJ8qÖê (Þ=ÌÑü3ÈоÙtÓ=ÊÒâßpêâ¨m‘ úLsu$º4R«6²­J€ðÁmæ•€l?­<ªE"~ãöc ]Dº_ËÓ7oÁé¢%}þ‚ŸÖ%Ófµjôž/C¬ùdH½â1úˆÏ›.ç݇øf.ÛwãSxqó¹lt­ÄÑý,:w[“ÑDCzçŸ)['‡>ëmˆ¬˜ëtÕ¡uxjñRÉ¡_`¶&ŸïÅ5FïÀDK€Xl:‹·Ý瑤|0ÀûôÛ§É jV]/ ³®ŠSƒ{‡i¶øoÐY±^¬’ÅÙÏ•Q„8ÅÓOÁ}ofwp"ûo$ ìÒ%ñû‘ïpºG ž¾ñ…ê€[(z´-L*áߥMð2VˆšÉ߯ß]ØòW‡Èà izýv"½ø%o(˜~äü ìÏbž5à†wÜRɈ³EøŸ¦ù3G‡2ïËÑ_ª+*Úòɽʷã\Šæ§¢ð¡´7ÙBè­¡Íôdì2Ê0WóÓ ºöÊüƒûfäƒX ÕìÓg>)\ÆJáhv8ÊTÖ Ç[°m/•ú›ÇO=…Öóé'á\xwyx?¬¢-§¶ãÓK'óèï$oÿ u·¦‘¹†³àÂZ wW$z÷å€×RsüS<Ï<³N˃ÿÑ_ÌÉSÇhôÔvl?;—fæx²vkE%šýˆOËY¦`u/¦‡Ã†ç`dQ!Ge›fF&áLL²Ù ±]s©áŠÌKT»^Ãð¹ãðiWfàP4g´`öó4椄ñÞ²ˆZýŽ%ObKº!wr d— ’]ÖéÐöï^èÎÉ<3šºµ—ñ— ¯v'ð+U2érá˜Xð ¿ì3#‡ôŸ@÷)øóŸ‡ QD½¬Eö­aÊïs¼ßÛRõè߸í½*+¾l=Ú˜u±â¡ª0ä8Æì”Ú€¢£?¬I_º*‹qâó× W…ÔsÇEô¨—Ž4îcÎåÅM h‰ç Ò8ÿ3½` &fá¶Œü–® 7ÿ‰B[¿)®ÚÇa±ñž–d\*¼óÁòfžÝÊJéãø?î­Ö7}Í,6»4” =*E啊©qÃï³D•cé0_³WT bCéRò2ò(¹8yÞïgŠ wálÈ\€S ¼±Œdß Î\·Eàû Ÿíf±$Òˆpâ•áµD,ïýŠ•’ð»EoÝU"ÎmýH¶R¸°ý)¶|”¢b¡`±>ž>Qÿq$‰.KªwAh7UËä¥M§|h^R7î©—&ËVRÝšåD¸onܳ}(„ó)“Ë}S?ik·Ó’¨8Æi?{£s8* %ìÏ<ðmûYÐR«C`È*òÚ-àÖ}Sü˜.HÝî¢ÉÒ^è±´üúCi±‹5_ËCwlYˆÓ‡JñÂßµDjM,9sïÉ·ƒ³9Q˜ó0WI^R•èÀ2Öòƒß™øx>RÝz›™ÍÇßjÃÌï<¤^ú¼nÿÉ<ýÀÁ}—Àg·Ùì³%Ì“ËytÙ˜ÏÉÅ%r=Øwc˜=-¹ÚÏ‚€àeÐ0ÊN©M·ZLs’#Ã;ñc² ç³$ËÝõ@7¬ ›,vá¿Hº²r ŒD’0;KÒq÷ˆg6âPcú_b1ó-|dçNsæTOQôé°2ξ%ƒŸ|×â‚Ô[œdesæÊÓ ¶ó .›Ã /ùµèèoíná×î’Éó¦@y¾Ö³¯‚˜w¾üô¼â ÎN1grÀï.κ÷5XÓ¬q]:/O5å9Ï>Î¥¿4/rçíÃRzoÊ÷ÃàèæÉܶ‡²¸cÈÝ÷™9|@ÆV³Ng¾à“þ`÷v- Eº'ø†x01sâÈÜùƒÜ§½Ó±îX-ȺLÇó§ûÙù× ÉBç&<²Z‚áÃà†cÌ ‡Š~]‘î-ÈÞÕ&Zã¦ÌŽÛ­ðÊ^†þ§Ùþé‰(„F¡µD‚ Ï™FB×}ƒwñ’.'Öˆ†–7£×:Uú8° ÒoFàÎ}bôÊïEÿé—ÝÑÇÎÞǨb5…Ü¿€ê ùÔJx¬FoC1~”XË*-õ†ûÏΠo¹y»_¼S³YíU»ðLN-L·9Êä:¿ëÈfÖÈ,R¡;ð2]¸ðk&;öú þóF°Ã'ÓïrN\w@!{’Ô;'+xùÜOLüž!ö„‡3#QŸj˜ì OŽ€ÔÚ٬ϔH°`ð\Íb8.îFß,‘&Ú‹ÝHéáJnõÖ»°¦e¢íÄ=&yE YªóÆ7ÖbÓÌ#ìß2Ò`9ãÝ›™Õ}É`rÇ›ªû¬€'ð¿µ6? "×Bw‘l•œ'±ñ´-ü=ó rxÁÞ­ìªmAœâö­ølKÔ¹Á]¡F\PîS_„®ñFÖ ü 8|×¥·,Áe®;˜¼¶ë(Z£Í<ôyØ#Î’(>øËó‰³þÛÊûj 2¤ÆÜøY‰±ßø‰)«Ak `ãËÌŠšìW£¸-0þó”¶}îIç PvÕTöÄš&h}/G–¿r§nF¡ìŸ%Ì~™Í$=ø¾¿ýœ=»ø ÚåþÁ±'˜\ò¤/iÓ°cWPyðs¢k9øö,kß.Nm|~@Û9~úA7Š}’p ßCçs½ÈŸ¾Žðó`a´TFgá+™5ôÁjÓÉr“VAfE1;o¯5שÆoÝWñÜýjNÛØ.0ÿɦü² ö-­ÂLžë’i‹Ð¢m-};³ƒp<ÅH×zBÏ‹Á®‹Á¿ö-¨¤ãҷh6;Ç‚­Ò¡G7Ó«JbÀW;ž¸Þ…WÏæƒžùR¨}§m^o`r=ö 3Å[±XlÙþí+œ¾?Y[ï⦠¶ªlã5õ¡¦ß\IÕâà™|ü}aAŠôI·õ!vWßM+‘Æò ÓPò²>e ‚  évûéh) ]#^¯°¤ñâ1øOëœÚÇ[„BÅhv»j¡eð\Lœ`¢.ŽÀòÝ_@á^¸Gú`²», “)@“3 ÑÖî:6ÉK¡ÍûBhØ2u´€¥ÛU\¨P„Yàöµ0Gäz¹Ž›Û¥Y?3*f:û*n€ŒÜt#WþàµË%ÀŠíb?”̧w»ÒÙþFP›8 ÷; "Ó89o†Xzã§]³o˜‰?þˆm}”ƒâ¶7áßu-x¸º”é)ŠÀ¥SN“`»n¼…|Ò4Ù„C’cÖ1›#Uðý8Ñ`€Ém¶t³/­¾»œ”Ø-¯ú¯K4f|&89¢Ôî¥0Õ­aýÞæãå`Y|Ì’Ù}›À·ßÑé)õ°k´ž‰·tfªÔþ󓦟 6#®Û™>gK´2ùŒ™i0·ì4'ÎÜìØ‰¸ÿ4Îlˆ©!1Ì6ÄŽ+šÌÞÛQì Èq&±Õþ_Ë ¤˜b‡³pï{%¬uŸÆ¬ÔùÁ úŠâ»µÅxùÖOvÆòoÐlnŒáyÕ“\ ½ æÛ‰É<ó=nÏen˜FÃSÿáï­nûcÈ–¬c÷.pþ£N{2@ÞÀæJ`$3l4 –Å7²M1œ«ˆšãsÉöUÛhÞIòÑ¥>/e¤—¾F[‰^¸ž%BÜ 1ØVŒ®|¶‹í7Ú‡mNÚôíåÝPcŒFb\€Ó£E£•(ß%MŒü{áù ~Øy¤«+ZgéÜüO‚YäqTÿžf^…Ì„†à|6¹º‡íÁèÎt3ƒ§"á”:élˆ‚¼u«`Ú¡[ødÉƼzƒŽV`ò‚í|òò>nnùQÈÞjÁv]L"Ò³á?ͶßQ9F,,•vßÅ…¾m`["Båì=!ÈŇޢù )¼i[€ë]±ž‚©ds ]°ã©òŽ@fT‹—@VBžÜ¹›Þæ}e ëNw3—óð¤T.ž;Eß Õn%?‘¡KJÛÎLs¡I‹‰J¢/uÚý\Ç@ž¹(=릟Ôç2{Ub ðq/t ¼mËñ„®ËeÌú9°è}8~áN°%unoàLùaâyL‡n_{€¬?^„% 'y[Ûn îz‡3ÅÒ!¾Ð™p.:°ŠþÕ8n.BMM\©ÏÖ,Æ©U£g›‡N¹‡Á¦ù<ë“¥ûv-ý¿O5ض°Ty9”èB–ÞbnJŸÄë70ük Li>…SÞ\Ã’³¼èøã,jì„‹s4AĹƒ=ß°›†fŸfRØ™Ÿ_ƒnž4Z|7…(¸.¡Cé4^–¤ytÏCûÞãpkº>ç»S–b°a>óo– o=Õ*=°`Zò-©G‰æj°º£„Nö·anì}¬r+€Ð‘@´æÎ¡NKa×ç@æx/‹Y­¸IŠ$¿Ÿ ¤ÅsËÞ]Þ=¯™M;¸ì¿˜•ø%yË.»Í¬Уò\S² r>ú½^š?3IK@ú¡´&i˜ÿH‡º/Ní»ûA f[0áÙ¨G½C\ñÌY]ø¸ì4[áÅ.‰qA¹MiéÊxI‡Ñ•Z‘LVÞ)ОÄù~#GlãÆ§ÔÀAF“ïþúñä•R¿ƒ¡ë4ZW/›ø;€9 鎰ö@Ô}s+3Ék§‡ìl#è²à$¯€U«¾ÀŒ vP’}˜eGÌ ·;ÚÕºÀø„åïk‡÷ixàG18X “oR¼ôr*…yxÆoM: Ù–<2_÷l‹Å¾rx*\ÇŒ² ½«äØ#áðkdÍÜ«F³ö9`‘P 'NàÉ×;wÛ+.-Å„à©è±¾ ›a(ÒúÄÕ•N`“‚sÀÛƒeøF†¡L;†Mñÿé“w¯HÅôÏáÄV58#½E¯#,Ö6À(Ÿ|önt 6}l`îŸp¸´8bޱueh&íN›æXÑè}²Ô÷o›¸NŠ95”‚C»2iX†6½Ò“»?5”ðîÝ&ù 裵Yh#ÍKò;ôè¡Ð÷6"/ˆ ߦ œïÕ¥lÂN¶Í É”.=÷'ïgÿó…vœEÅÊÑ »Ò ÿ%O|åJ•éôÔ¸ßÍžÜ=WÁîñ¤Ü«%Ó/ƒÃ5Rδ!ŸånHË4®nÇ@ R½tèlxǼ>ú •”›ðdó#xW#‹Úe«Q膸­”Êx$.’3ð¦G×5a+YÌ(ƒéuc$)¯±xϘ+¯@7/|Ãü4 ~H:E׳ÿÉ9°wÿ‡[Ôl1=j!á›ì ®ZQgÃ6ØŸ1Nq'ß G&Ù™ ¨ ßy Qº™Z3±Dð"β‡ MÅ]dSñ9¿rË5á™%&ôŸ _ÁÏ2Ͷh¹Tõq¸×XTÊ€ð[áÑP4ˆmzÍÓæa3­7Â÷3(Ý:‹ŸAk^d*Ú‡@+à0ä m¤‰J&¤Ês)áéH`ëì†@¸µ²ÃðyÑFò^ÝäJÈlj\îîc7ÙþáÜz‰_¶a[¿éjbB2߀ý{ð«:BG;[ØŽq °²¢ß~›àŒûÉ qzí¨'ëxЕh¿=SZÜàÎÜöÛa%ºIa6¬×¯À™ßOÂSOpW6ƒYø¦j§¤¢•Úz ¥Lz†ÞaÀfm8EwÐÚ€àg–Û×äa’‚,j\Bof¦R½§ ¾i™ÉLÏS"g«%éïWüWԋꛩ„V¬²ò/åªYOéñ¥Œ’f[tÆ\ÝQs{̈JV: Íl‚XBHpÞIxÔ2ˆyó² MQÕ¨¶n©€ÊÈ xãªLäÅ[ÀS¶‡Ñ~¡NýuS@)B’}Ù«ñoéu5zäîùq¬.ô¦t¾y¨W&íYwÙ•8œn‰C*Õèxè9Häþä>õ³':Õ·AËUäòñ£x1 ¬F!GW›˜m×`8gÔÐî÷Uôªø E™‘ÐøP…ì{1³*…áæ'ÆqÑ=&h‘ð»bîi3¢‘»®ùÔ£Šë¨¸U æ9ÍìõG)p™¸`ÎF¡ÞwdI­ñ=Ζž8¢`Ñ ì#ùS¦G¦˜?<ÎýùßÿéúÍ"áòxŠç)~K›B£hsç~ ÈÂÃRÏ¡ü¼ ½s‹"Ú%™4&.+–ÃÛÛÓhß!iXë8¦ÔëÞq(´¸‡ç¯D¯˜§ Á%N¥8O± n÷$ý”ÙçÉhžÄnŸäVïÿJËë_q¯.TÂ;¶bd‘Á” CñНëI {1°@“¯ŽeV j‘ý³¦Ñßë `U| =“0›ÄÃBUÇÖüBtŸ7|+jÆç5÷έH\w[ŸÐ›Ç©\ù²l@Š<‰k¦îÁK‡Sà÷´Y±×Žä]:Á±5¸›£§Ã£â)äï‘W³{ x='ß ýôi¤ÿ²QS¶‘Í8dL7?šsˆŠg<Ü 7U¢^ï0°Û0}•Í_íFþmhbÒ8”ÿÅ#FË5ˆMvö†wò…h’Jßx.‚?¿zØu³µIɳºV'ZǶÁ©¯Òxýä¸lJ«ÞS±SIÓšçlq’.ÜJ Ê#¬º´ÉìŽ ¹­@¤‰[ZåÚT¹~Zž3æòsèÕוlö¹FøÏW:DVD_îÑÕóé¶à| è)£„d¸ó‡´6E“¸×wÄQÉ9b—%Uî’¿Oðka,ßæÛtÈÈf{²¹çeÀ^zËD°öZ$sü¬:Õ­W¡/ÊÎ1,GŸ†Û‰±‡ŠpÜ^Š^½éÍX]˜N3Ÿuà¡{Êð)ôù«cM¦m³€ óÊÉ?Vwì!•ß¶˜X뉀¶ç> ®1ÌÌJЮZFjÔ±àxó÷Š‹ÇDED^ëÙOÅ87Q–š,ù„·ÓÄ`Å>ý9 @ûµ»èBi¦º¦ ¤iô—8Ù4“hìÓ"¶ÓiÊ®/Ðýô.˜òêÁéþ=dÿQœÎ[Çtÿ½Æ:\|‰¦æ¶ôÐ L_$E¤[¦‘ªp.”yÅÕ3¨åoUúdç"lû½o–vq ¢´ð!œš ~qi䨲kðúÓs0¹à‹§üá„]¼8”÷ÚSè5»¼óýiTm ¤•>‡DŠèfù¤ª°º‡ì ³iμ À>]ºDÆlz4éÎ9ìØÂFæøÝéø~/nk©GM§£°Pÿ{øa›w}ÍÑ~¦3z„F`Ø;9šë @ÿœ>ЧèoþFØñ’`ß>?ž-Ä‚þ>nA¥8¬oH/ôG—s\МyŸs¸ñ;s5ÉOWסÖ1”Ã(%ñ?åˆ0ïH,Ž]ÃÜ)úÌ>N@å²ÇøùÀ9®vp3<ªk„ø{—ÛTFdhmB:rÍlhå¼þfØv -«ÍþópÆt>º¨Çr7õÊ“—ìÈòÃ*äc.% …Ù©sãÐgc 3¯ñ;òšŸ´¿íd“¾)¢ªæ}pok¯ßâTÒ-:¢âÚ#tùž_LÈi’ï¡…ÉfÕ(RwŽßœÎ®P^«®ñОµ# ïèÆÇ°™aQDD”Lùú OzÂôÁô¶‹#Fdd«¼|à Ë"èÔAiúUÍ ‹všÛ•×ñ¤Q05­Éƒ/Õñ¥§~þº¶<`Ã'ö0ÅOÞ¢ŸÊD|…Ý"_˜õ75q~Ãi¼͇lËË_+|àÜz4ÔƒSŠd¥¯#]ù@Ž–>s"^LµUÆxÔ1Òbœ³'zÙ9×QyþM0¸¾Žl?õ…Jgyý7ì[åÅØu vXSï‡ù8¶W˜JIxㆵðQµ­ 5ÉÖŠ: =n‡ØCW¤èÃZú íäKhÆ×Ù4ò×Rötî”CÛ.]ºaœ‹Ç‡f£À :_2™§´žé¶ Ž’9}§Jñv¢1=vvÈÔ¨sÌw+ÀžÏ8B1ÞX_`óßgÌÆp-B¤fáÔ‘¯À§7tèDe®Tu0—•š'L«ÅdHhu,œ÷ÜH$>;¢µ_7œ­cn"{äÏn‡—·]˜ï½Øøas²sÎnx©6ŒŠoñºJ ùX^ƒ¥tV†!sÅâ (¶4Þ$ü4"AJ¥îã‰=Ú8¥]Œ­ýG šO£îïÍt·S;„,®„#«Î¢ _¬[‘L¾>ÜN6n#Í«¾°8äeÂ[æŽC>—ÔdÇZþ0üùèÃQkÈ˽‰æ}{h»¬>™Ò"Ä>Óy¶ëàIê\_ÞŽÞÁ'pCÂ*Z>ÉÖyD ‹ï§ ¢m Ä]á0ì.2÷é(£·¯¾õ_B5ØõEE©ù"}â½Ø†‘\¢Èö¸«ƒŒÒTD+Pø«NE‘t¦ÕÔgÓ¼~Ø3c3½ñèŠ5l¤ßŽÒ"r9ÂöÜ Ôý‚OÞ¸Ñy¶ÙÙŒÊ/ôØ¿·âa¶» å!¡§àù oª¼yS…òÙ¢™ô®æ)øü‚Ÿ~&D—Ži“µ¬¹1ç4w°>ŸíT.®ÛjÙ1ô^,?ûQ÷:~Mû‰‡2û`!x>ú #+m˜þYñ—Ð0\4^@?Æé‹úf”3+”È­èe0ÈçL]›þ1c/sñû  ü XNÚæÐ§¶:ÄnôxÕ˜L"^˜}s~9|’©îa >Ô¼Ä_Æç–$™¼ ¶ï}§ï0 çOu&Eͼh^"J?y‘ñîL‡ç[ä-¸…â>“!Î}S¶·©;Lö«^Æ óçß5ì’ Ç¡óñdÞ¶åôdP9YÇ¶Ïø EÁùäÒk­|óSL¸Âú3èÚ&P8›é,Ê£÷ž.!þö rª㹬“„ã{BuúÙ™»ÿÀè·xê½{%mžš ¾÷¢˜Øª4VÆ—CT{2zÓ5˜Õ<$øþSlÙWNþVÿ:o בW¸t£7$š,glö'ÞÔFXxnNˆÒ «Ùãò1›¤7f[Ї&Éøé&c/_ô’‰1Õ 2œt޵¶þ«È# ý§éÚuÆTðU7 E“ÚÓ¬îµ~¨Š³Áص‹ÉÌŽz,ês%13©–êb¢µð/3Ö“CòB6µU³HÃ×Bæãµ^ŽêS72¼(ŠV¬<M®(Ë‘£ÚäË«Bä¿aL¸Ë™5­‰ yá³%ôüò>Ãö,¬…Ÿ…é©ÛùÐ÷J˜®vjFÆ¥p%O>Æ/½ ß^/ N7íIþíT¼“Õ‚²˜g'˜Â÷ó¨¾Ï1¸Ü›‡•JMøãKX½ëf¤=Rà͈ ß{Ž%‘KIðÑ\âñc!ôµ‚þDyò!ZˆXÚUZÙ— &Ú çDê0ûÄVlV\ÇxyÿƒrÃVTܬ î³ Ÿ£Eù@†:d7àÜ?›•óÆŒm?L£¿ÇhÜ™•(4ºŽ½qVŠš¶èÐ:Þu= -÷ëØóñhð€a%ì°Â¢l“Ç#ëéòG¯ñýÑ¿ „ýõõ‡çÎ9öˆß6jz׃l;’M²Ì½¦N¶›ICÇ/§«Çá¬F,nœMm”£ã´Ìzõ”¦´„0’#:d–‚Y$ņT|Äí‰n$ý0=6óëÎWB0‹¿²N/&{nݳ£DwE ðžïÅ+ŒµéÅÊêvÈ?¹mt0¡Gw3N¾4]BˆbåÔ]A‘PrÕ&…¾<Àvþä²®2ë!HÈp2¾9 ’¹†Þ±S ?ôËQÙj '¦RÂwê%;*$M5Ì/ÃãR8w{Íö˜‡uoÝéýª÷ð>o³!@TªP±¤WÈP¯,>õœÝvÁ§ã{ر ªÍ[Ë Ÿ]Z0Õ´,äecU¯AúæØüà6´—­FOAuºý 4J‘å-°`j%ÃwÚ, £Y¹¬·h:û(swú zËÍ… ´‡aÿ단z 'o£/¶ã¼µOq$[€~R“s'4Kq†Ú®>X\bMKß>ÇÙÅ×`K];cåÖmŠw8>ò¦T#K…I¸)C½Ò¦sÌF® 9o3ÂöòÒÃ2Ã¥¯áµø7fnd1c%ºKÿ5‹Ƴ²Ï*oÅÒg’wèÕIž]²ã<¶Ôòã÷Š Êê(5áo t©­8”Dö‘Uû‰ìA-Ææd#:êNcôÌ®Ñ#¥s /i;õr¿A¢3ì Àü=Ö‹ÈB”œ˜EÇWÐG;’éa1cr9n.áQн¦Þ®Õ¢­‡ÖÁ«ñËxpÑ3ÔZ¼ S‘C4*G9#³ü‰ךöõZÑ÷çU‰sK:™F è >onÆ›SpüÊ}Ûy…nÒ¦‹¾ÙÓ¿*qVt˜-Â$7#¦h ^¾× ‡­üéÉ—'ÑäKÝV|^î(‡ø´vnñ.\ε#+¿ZÐÂCbùj»XÓûýÂŽk:r‰ß*¢yèNŸÃôÖjQbÔ‘  WÉ?>nýÞdºv(–}Ò4ŒN.ütšG0Ùuñ4(/vŒŒ´fîßcW~ßC,§¯‘$òùŽ}§ñ–>: -¦ã¤ñáBúð€MïJ ñ Ôm~½äèчpô­ +gòÀŽ<Ÿ£ 2–Äd¾)» Ú3}¨Î–Rô¢Ñ`*SÆŒLÖßEp!_žX­±`[_Ëq^fX0Üã.ô?ßè¿Rà©EÅFqò¼<‚ÙübÄt™Áüˆg YB~–¾³k;*f“‚» ÀqŸ&y£m|†óŽë ±~$di&BëëLîÊ‹‘XheDc=ºÐ{E³¸kö¡k{V\éqfÓò½äá<7PR»Ž™a×QæS¼Ý˜HÌK˜yÞâô¼~*u&bû¦ <õá#}7¬r …ƒ¹y ¶k#‘ÚL§UC!ö×ì‘4žõdnÜ8Ä ÛkP÷›æö7Ž)’£w…A¡~‘’̇{…khãkÈ1ÕÁ7îúôHH Ž–¤cÇÂröý[eRö°}'¨H"õ±èß?öhsµ±»>ýï¯2»žF±ŸÜ(Õ…ý¢Ú¤ounBî¾…Û ŸQZ´†}wØkNs& Æ+W#V qn* ùµ¯Á,~¼à23ðÇï*$û‰EÏqy)“o‹€ñã…ø7”‹ûÝVƒ;å ùØbx)šÅÝ.«@ç“Tj-C«g °Û^ð€’­!ÌÜìõRfqÓò>ð Žl¸Î,KQ% •Eìö‘•:É œÝK/0Í;ÌIZàtÔIù}j3q /ÇIRŽ’Î¡—ÂW¬¸Hlµbɶø0hMš÷ÃþŽJ6Zÿ„¿fƒCôIÎêI›ÔIΆIcä¥J½=ó„NQRDntF3m¿Y^y hEã+wN %!¿r>5òÉÆ?O“é®ÉžhÝNï;‡ÌÚDz‚g)yÌÁ 8@¹}&Túñ*pæ%UþÜå67Éæ…’ »A–ù¨…£§ìi‘WNØÞÂʉd(›áõ™JäæQqº{ÎPq\Gr=ËÈŸ˜©è»®2R×T~û<’ˆð “åO'Àáª>QÐßÇ.óh$Ÿ|Ô±fÿ1j^{ˆ¹\ÊO@ÖÞ9äjM$å{ºžÊjËÐB“«9‘£„•3¡÷ß«“Ê^KòfõQº}X€¬¹|œîRv†ÞáÇÌ®Uºyº™ïy…^ÿÚæA4<&*D‘¸-©ô$§æÇ“ØŠb¥£Hù#’èÓ»­T¸ÊÝyÕ 7©ÓvÛ‘õÚ‚tQ–}V’üJìP¶}ü¼†¹eêÌ:¥ä n¢¶îTmï1TH»…á_DèQ‡¢³þ5È?|HÖ^Bÿ‡yðúâ(¬ÝJÚ ÿaÃŽdÐÜ9Ä•žQÀ´øý6ºÙ^\Fu“4'÷>ˆ÷6Àg›&8s…]öÁ‰?<Ô±ÔWkØ’x–þ[E½Dß0U÷ÍáE6…ý±¸p+9<¾„¬5Èä¼k)4æÞÇ9å‡`£59¶Œœ\×mBÞ°üŸQ³; ƒJ4f¦-I£j“ëzÑr˯ÜâJòÅôLÛ»T}ãñ­ÒG3KÄ»ÁÜŽk¹¥óh”O5]$(FoÊ„Ýá|4ñK)V0àì…ßo“£ÌUù3(Rƒô O:‚'?˜‰îËðÃä ³dF4©#×àb²=y˜ù:TÕ1;Ô÷¿ù9B¾¼ä#_1 ¿ýÎÆ§Og×ù0¦‰xÒÍ—Zz­ÂÌcŒäò­Àæ=d2Æ èÈ*`}É ³ó¸±xãÂÚ/Ú —$^âÏEp@çªUßÁÚ‹ºPoxŒ…_& ª–ÔâÞ±-D®J•8NÉ„®ÏH/ý;ÇT”ÁŒÐ v1«ƒN 5¯# ç`˜Á7oäAüá9t¢Õ…é:;W†Ù¹×§Ž¡ÞónÆlzV‰f;DÄÊÓe fbôQ#Êq›B“ÔtÁ%e*Yñe Ó®ÊNß´Ö6iP ÷7pùE»ÍSƒŽ/º…od!$æS(KX>ù]ýzfDWêÎA§•3‰Žé¸â¤C†ÜEàÁð4U¥•Â⤂…Ó;àaýg¸îr*G¹T› WbcŽšñXrZ‰Þ³9Ë<Ô·‚ÈÌh|Ýÿ ¡ÃgôgõÀ eq؇Àäo4š­Ò{v¼*ŽY÷^œõ} Þ–2Ó OD­¥'Ù¢/ŠäXÜ*&oÞw‘°4½¼öw[V!:Ó‡1fE€Ã¹,}ú¢¢z×er–]ÔÀW«êáR¼!ýö¢’]¿¢íU~†¬¸¬‰£Âíêd—Ô#fÖI;â/ÓÁ„s£ñ—×Zª8ÆG>ôER %}ZáÙ‚ZÈ6}뀿aJdŽ[S– „kÆþ[û÷ëuPêhFíQQú®ñ6ÄÙÚ]ð'ß„½häh;Ó¢–€CÛÚëÔ‰±·i[¦רÈ•FFñâÍ4X²¯·J\Ã)™NDòP+~¡#\ŸåÅì YbiìFS~•K/‚Ç AzseÃÿT‘¾¾;Åqí|kâ){ mM'¹ X 3°×¾*Ì¡ùãƒÐ¹“ÞÉ*"ÇÖ¨1…á¤#5€\v«g† “è§ a¤E{éXJŒ”'ÂÔÑP¿`½Ü8 ÒÞR‹©–ä¦JB½ vF•qnLLÞ³H Æ,Äà®Y49iJÞÎ;Oý'æ@HgtyÀÅ©)`å‡Ïf„'û˜Çäù¼‚w¿©Ñµßã©Õ¦Ef˜€§¾áëÈwx`?9Ü;…h¦‹‘Ñù¬ü! Íf§ùËɾ†D¢Wz3jþâì¾Ô§5\7“Õ2á°¤‡‰JF¿©à¤>‹IŠ4JUEß{Yð[úná­m Í8Gl‹•À[<÷’JF ·&ëéÜÂC³Í¢ÆDZßK„ðÔò’G¼¯¹O–ãôÎP°PšG/)™Óó=×pýlE²plœ; «çÒÜY›è¹7 T¢1ƒÆÕ~ÀõëŽÓ.5’§I¦bf×>p8ÿ N,ZOì‡ÍL²LXÛ¬–*”½»:žÞgæXŠPù]¼ôYa&9ïÄOTWs™BÓ­ø}öÌ”è„R HŒø&ï™®ÚJ®bœ&+NÚ­+˜%¤–Y‘©G¯2™jk˜ä7 @ÖlÀîÙoànÏ(^%…Ù#» vŸˆG££À'vE’1¬”ëÆÖÅÂô ç>°‚ִ̃LãÇào £^ ŒÎ9&V‘½O‰‰©åû!Ú—#@>Àó®)ÁÉð)äUp?Gh`ÎátΫž™ÔuÍa¼!¶ =†óp2^öT ?hL—á ÝíF‹2TX*‡Û: ØÅ®©Ø-Ex h²_$ϯÅï&;‰Úãéäƒ I·,ç¥G˜cæcÜu)qŒ\á&Ò.©MMªv“ûDè ¾½Û“ˆa‡¢¨êjEú;8BV@ÑÕ<ºèº1ªQ#÷…¦“,kI¸-Lbk¥ˆŸ0í´ÛÏ–f`ÕílV‚/ž Œ8H÷4 à½Rä÷„iû Ÿß|`ööB®^44—-½»žþƒ Ð0Jß×ãó »5.ÀŸÃN$ ¤hÜ»$7æÐ±lÎî[d[r±uOFáà0ÔÏ¢Õ¯‚ðïÑíh ÎÐý—îjŠ…±|e ê½˘C«&qÙ_¤<^èï^ ­¿Upÿ}ýWˆ® Û <š ðvÇ2Nþc+TÃé¨ÂñªN6 ó ~˜BD}´Ù‹Õ™H†m™²¤›ø;ý8óÆÞ”Œ½˜É^ìÓûÇ%5ù†™ÆŠ¦džŒ4ùTqO‡ÝÂá-BäÚ.^úèˆRÓKX/Á£\Žr,#µC†ìVrƒßQ/a…ZÍâÙÂin°¡‹^¦‚ï~Uf¿«qïfãùµˆFô£³TF»B¹}-Éï)ˈy³*– Çßãì„Þ-°îP¢)¾eìô‡·˜»‰hŸaEλŽZÇ>-kb_x'£È¼Q¦ÈVgòkÐæº«Ì³ç²T.?‚ÙvB‡\*°"g2âé¢/*L`oú ®Ç‹Œ9ã¡F…ôדÌm¡TÏF §M £¹a¨ây‚„Ö|ã–EÏAÁÛѤ{i:©ò¡ Þc="~ÁÉkrÔæÔ6²6Lœ©zÀ˜&¶"‡@«kfÁæš6‡«Å½0è+ƒu¦Kq7¿7åD(i\ù‰q2†Èz#x•ôÂK%Wآώìû¼¶ý#ŽÉAþ/Qò»ïhÇ•³VÉwdýÛ"IÉù‹LÅ–nfóë:6v™õˆaÎó¤º.­ìÞŸí¸4OþYF±´“°– 汇Œ½¤.wyÿA®ZI×ÏØ†¶OªpÖ^GLñ'[Ò‘_Qµ¬fY l5È/ñtXv\:u™°‡G÷å¡(ñ|;¾Î„Î9,ÎVûÈ–š˜“Ä,¾³ƒþKHg>·#•³ ­Ïw£›½,[k~¸âÍuûÀ¶õ#C}HàúŒ[ l¨½isŒÏ°ŠUþ 8žšàT¤†™V1vN]DZ¸ý=°N#Þ‘"mzÂÐZÄCŠÏ³§3BadA 㕵n çCÔïWÜñögÌÇ+RXùlOì5"Ý™ËXË®<ï1ãúmaŠö.òÓù:3úþ\Ûƒ2 –FdÎÆÔü¥ ð‹3ÞîSI‘O5sj·A®¹6Oö$?gkˆâ=ßJÑÍå$*¯$,^ÝRqí0eG[¹_‚Èê÷ᣔü9G¶Ïa¤úRÙ{§kPoçfʆXÐ5‡ðÍO ñÐ/Œ³f›ïðÓ?bWϼ°'ë¡5Ôw2Väðò™4#ç8<œu™•ºWÆ\Úè« ~oó‰‡z?ãöÕ€š§½Ã}·‡¹9·dVïËÁy ˜&­Cæç_‡í7Œ ¸s”ÙµH¦>Á»'ô™rûðÈ>røàh« ÃÅ?*A'ÇÁí~$‚3sUKŠh§œeÕà rÕÊ8KÆÖ€_0ÅÕS9K–STÞ¾îUœáØh¦ûz9XÜ~ yS‘œ˜EsÔøY‡ð*8kªLfl‡9³_2¯ËwÒ_Y¢y™ââx:=X—¿?ƒÛ»Œáî!C²ÞH…>+RÅNÊtzáùq¥›ReØK7ç’zóEôöÞ>®½ŠÈ ƒ;γÈÀü,UYA.Œ‘R[¢þ‚Å„›‘x-;·ž„OWŽ£ß3²ï©Y©ÉèhÇÃâ?02 YâžØa´É}o16-Ü%›_qøy ±j¯-¸$ýëîß8‡÷=L«ÄêLÔCX«éÙž&ûõ™ãqòœ¿Ö“§Ê›iOÑ[Üò<Å›”X- SRßϬ4› ^ßPcÎ~æúî<<ð²‚|;Án/À˪Ÿ°3]”N&ON<ÄôÐ8RoRá­y.›USíðõx9W§/ŸÙúÛ =îoDzüËÓ³…mëõD“ üt÷çn¼¨DµV§AC¶Ÿ«T$Éú¤2|¦ÈÁ·™²˜Úå«@QÑ…³{i+¶%‘=8ó èü3Cá¨e¤z9âóoîÌCg+fÑQºx‘üÍàп7^e.ä È!~úd©>åY#AÍ&Ø”v*’­Ji$xÛpçS#‡#,™“ÎNôð·‹Ìå¬Å”çÕã)¸„|J؆ӮÌ'‘í?Á5:•dPî²9°¼C®c_k»Ó|ß¹`šU Êî ßA¾Ü£û>DP±[ ßòD÷MüL~ýZÄÁ•Û'qÏ 0\üTæÍ6£#N «øF*•eHî² §ôTé³­×`^e$Ð9ز…Ÿª• ÄmUrìãg0sfèß×ïê0¾‡ü®ä¯é4Ø>…1µEñ¥ìÚ^˜˜ÓÇ•K¹Æ5|r–y%‘…ÞA?¹Èÿ~*Ô· Qº°–m[ ½!ÕÐ?Û™l„¦)Û¢õxȾº#pຠ}8ÿ*›"“rëØ¿hhš£ScgqÝ }~2£¦/Nf«,!ÝX›4€Ž'm8WÝ^B€ÅQ4Ï`Çž÷'|ŒM÷ìÀÝ¿ÚÜÖH°róØ_Fóéàr}ôëzGÓ#߇“ðÐæ¹õn˜}ú¨ 7͸ U1>ìÃ(èù»Ä2‘M]0Gwôµ¼G¡/ßÁh}$³¥]‘tý\IZÞ§2ŸóL¨³›8½!r„4Zí oŽ«Òaö üà‘%/s®@xæšú±W?²&ñÝžDu?½;ÇðãÜ|8gåƒfÕA¨~{ ¸»¯>L°¢ZFŒò™VHâ!ÔëVùÌæßv¹Ò ØªÜ î?.ÊgYÞÃî3í º}Q‹n¶Þ#û —ÂæÀv¶ø@¶«üD#]W(Úþ€]8í«‡sAåÓ´µñ¡µ®•xjù^Xp¯Ž„ͦò×´au l{ò^ív C¿¿ÀöIœvGiÎã{ƒ?Öý‚ÝÜ ”ùð“ wó§[»W°cËωc˜#ÿ‡©›'CVJÚÓ—p·¤•ö'õ#Ï4S"Ñi $C”ç ÐùfgJ{ñ¹ùH¬<Fû÷£ƒY Š–ûr†fÇ©=ÈÌ0Â÷öä:½8÷Z‚4mé"¯w5£æê8<»l}ôUŽû4S5ßé4¾ ‹  iŸ½†–ýyÉ­üæBMgSA½·p‘e¶ÞEÿ&Ä¡T¯¦Ãë$ø•¾y‡3ûÒ% ï bø(Ž=QnD6úì‚ÛÏSÐw«7”F 1ËœÍ)÷‘Q;¤œE·8Ù=Vôâ+/–/L—JÌ™CZKË8æ+Ĩ¿½ dÈƒŠ“0MÒxµYÔ-þÁlýöŒÙyw;ú# .Îã"ê(·–L­U@0¸ŸÉq¾À æº@ÊR«.L,7IÐk<ÌÆ©t· ²q¾j(ÑE%ì¹?m­‡º1 Ã åüE¨Z›Ã3]ŒpïÌdï.³‚uϦÒ*Ù°úÍL*ú6cý`Ÿžv¦{¯àú¢z45\‹¢ýpÿÞ£P>§ {Ìö0g·Ù³Ew–àjcKªY&þ‡}Ù]æ›ð³½±ûpâFyÐ\Ù8)‚Äÿž,ÍP‡%iß1¬S/ÙG±¦**(ÒÇñζ,Ú©•ë1ÔX¶•vmÄü7 ºï>ž™Ì¯ÌÝ?™Só6ÒÒÊDüÚñŸ¾x‚uqпå$fúT óo+»îàTXÒåL½#Ïá4ÉÓ [öîÌoÇ·²3 W&šð\Æ|Õ­ƒ‘;V\Z1.2} Ϭgw]»M•àT"Bï2PWº•„LŸFçGcàs5z](+eÓa‡n(æ'§BJÊ0ìd¢ªŽãp€­¨8‹ó\>sfåì¦2§6Qs0žõšÃÙ|ä2ôNüAøVj1 (%ÿw2Ég¹âÛŒÍ]ºœˆŽåT:¥h:iÏUÛU4ßσL©¡O†Ð~áfƒ<Îù‹¢ñ™µpGõ•1Üž³5Üê²ôj‡8ˆ¿O!×/IÐéûÍÉßYKVK¤åòÛ©†O04Uœœä[®_ï‘B>ÄTøÒ{Í~¬‰äJœ›üžxOb]¡ôÖÚ¯ÌpÐa¢wô·£>¤ù҈ȒØgæ‰coÁ­Ù"4âÛ‡Á¼(öÇûïLnrÛýu3úð´÷t”Jr$-#ɽÄôl WøB`Úm¶ÇdÛ°I0ÇÁ‰07.$¤ïq÷q¶L¾V"{óo²_9‘¬Í¦¿­6… Ôæ·}뽤­ðiÓ|{æN/ü9Êj]Îd[£…Y§¡& ZBƒ_È•¿ßpÔ;®.S™˜ ”: ßNÕ@-UzjŽý÷ vzJ’³‡V`H]?S˜xî{6·µ6Ä€ï 6äŒ(„ßC„Uø°X†Í0‡Âû7™ò?u`½"Äè3.‡ŽT¾evÅDƒ@T úí e\9µx6L‘<Ê&äÚ';hO—‚*åo¬×LUr£j6“÷ŒƒO¶–3¡ #QBÀºÍ×}+xú%cÌïtQ²¥pÛŒ3°Ä˜j,vbWžÅ–dPh3$šë õö(æÃ’|Æt®$/7&çl¦“o²Ù‚—&ca2yãÐoÒ/Æ<ƒÜ^ÝÝÈc Ü>Ì—á}9^güÉ[Ǻzä£ûÌÙº “ý#A¸Ð–ì㸣ëÎxü|LŠ„ Ü…¾ N(üø³•åLë·ª@gù(†Á ²Æ°ñ»X·wÀû³ÇpóB'þþA·õ4;)æqo¡Ýt2ÎËîµÚ ºnÙ Ÿ½Á^¿‘}Å…Ãg°qËYÖçøG¬±€­%:ô­ 9½ 9î[Ý…Ú˜̺ :eï)hb4©ãÖ!h¡‰OÅá>SÂ,Ê¡¸c/¤ˆ–búZ%:¾À›T ˆá…¿fô¢ùTâ‘{Ž-OÀSy'k|Q uVp¤–û༱áÕÄ8†L¹,¹¤ŒŽ¤¿"£6ôd&Û-÷WÛ|Æ ±çÌF#wˆ0" y¤@àU?FN¥®ï[°Nkú¬ZÉ*¼ù3/.%îœÂµŸO@â¢-´´^˜¼.ßKÓ¦’7‡º¨áir‹9!C·”SV]ý4œ4 §.Š‘¯F3ÈM¥(|¸’WÝÆ ¹J4À7~d§‹RDæ·sì…ž3äé6µŠQ¥wžÏÁijnô–»ɰŴÃOáѺjlŒnAaDO^„ÜOî‚ÇkŸ#ïP]ÒÄKÌøíˆíbwòp[¬e/éŸiý;\8—¦ÈõAì‘‹x-M•vlËs od×þxϸ/µÀ%ˆ[Ð7Ð  vfùøààbñX’þÌ>˹6ל5ÿÁAåg`ø‰oA:(ŽÍ#}¥©uu' ìþŒ+v‡ÑŠRŒ•´¥îýöb•pê6^Lk¶…/E®Pr:е"A÷¸"½¸á:³âs½jRwÓëàzÄ ¼ÇÞ€Ûó·€êñíœï2Ý8øWˆ”;ôÃñ^w:¦B¥ß~ÀüQ+°Û¥J“¼.ᶃ‰ý“â2[áQš+YtÀ‰¼uYÎùt$†ùÑgʱ“PGi¾pbS0ÞºÑÊŒdœÁ“w×ÁÚœ]Ød9ÈÕ¤oaìéK J| W†dH¡0ùSmKVåz™å_™ƒÞ"Ä4T…úõžÓSVXhS¹i °”Õ¦ü­³1mʯÆ?®8(>“l¨Zˆ†Lè=ØßrxÉwY"õû;ÜØ¾’™}¥Xo†géT ò(ãÏ{©­Ä˜+²²˜½Ó©•!Æqê´¿Ú5šþÁ.C<=û1 ]»ÈJŒ»Ð‡k9¨n¡É—Y‹ †D-Ä=÷®`÷Ø?ˆã¥{y9Q‘®ìM52sÛ5Xýç<}sùT²1y£ yysªƒ¾˜v›¹usûp•›Ãág`»uóJºeÞ»¢”0£vÍ‘ÉxõŽÓ\àµÁ§Uܼ³ù äŒÇJ É ™Nýg ØÈÚ)0…|qt°Ò>Ƽ;ø…yDÜc÷ù­%R³`@¸w&ãG9kúkËR÷[‰ÜÞ÷ÒH8nùÊ´wÓÐ MÐ~·VpÞÁïZCrõ?Zi+ÐÓÈÌî$&XwßO&œJ3Ò ™dÞ0;uúu6õù¨¸æÒ ;µIÜT?&Ãû:üâ2+Ü‚ÙE7ªŸŒî²#ì5“]éDF-ú¸ÓV¤áé`Ø•M•‚j˜õ‡ÓÛ:0sZ>x߽șœ›«=u\à'©ê^dàMìý½Kº¢Ùí4qgJ9÷›×bk? Ç”ÖÓÒ/%þ#ß*œÄÖ£×Xs¸pÏbÖÙõ²êì;üþ©‰;v ìNÂNßB ã5Pc+„>1Ÿà¬ýrzhÝ^ÍOÞ&v3Ö_h³í!Zóurw—âîÇaóÜùöeî´Z2Þ(‰Ð/|ð´×–šÆO!Áû_àÅkQÉÁ”nùå…Ö³%B]ÈXáÚÚV¹¤Àæ=«IðMwròwŽ®ÀÜÀÓw19?xfŒ¦Ó•çkÐ5çtëöü$Ž,,€tÁzV*@ƒl»íëpÎñ­ Wà^²Ƚ|³É†j$Ý[cPÇr_•©V+XÝ+Í» Ÿœég´vþ‚)¿¿Â°õ<Ú¯kMì3I(U ñaâÄ}ø:±µô'^½ð-âÜ­‘Çî¬hô~÷¯B&q>³ø…2[?‰ql\dèþ[pDS‰ÍÍÝÎüs±¤Úrá09Ž?8Òð7T†2B2ÄÓÃêäÀð‹øVw'é ;@Â^øƒ™‰­sêÁå¿#¨‹ÍÐ(Mgã†oàŒ´í°ò—áåå\‹9É„ëC;Sw²ÑF‹ûxæ &M± ¥÷âÇŸü0zÔî ºŠJ–VøqÍFŒæ_‚“×aåAaâ'׊Ž)LÝ…¹tKO<~|<ØïÑ0Óm6) ß@“Uúð²ø/œï\ŽÿíglÇI6d¿ý’ÉG OñÒ@U€€ºb\è¼”¼yµo–œÁåwÅèùžHÈñÿÆìjªsE•Êv2#^ß/Oc~òÒ‰œ)”ï‡~ºæG#_%îõ“èô Y÷âDÈ›ýf‹…éÂÓˆ¬›)®µfFnŒÀûãÊÎdfG>,|¯ a»ä8óbIÎR>&N ™ör&”?lÆU…ý¸ñD.–„Ú®KE§¹Šw‘Ž´TH•¡JC• uv ºƒ)$nø‰ u½HîkPºTÏ:–N Ÿ­y²Ç–f‡ >UªÕ߈ £iè¦'@Í:~²Å3 AmÉGÆ~hÊí5„^<Ë:Æáø8 îŽY¤í9É˨k;|IŸØ +;†¶iÇw¯Œm/eö¨~óœ|ÌáÉ:žfWôm$Sš€Úþqnê™]ôוnɼ} ¸R4¦Ê°V½ì¹=eìª)°{‡Ve=A ÛŒYøâ [–hDÕ— ý6·Yø±Z™$ •‚òòì–l]üe¿$Z籯õ³ê·ãúu˜µÌEXÝ­–(ª| ëÝØÀsET¯J¿r…ÚFE¼¼^™ˆÇt€c¨õ¾¡Ëžû0úR¾µÇ"u)¼B#ðáæ̰Ïdú” qøÕ(¨ðÆÐC?Cˆêm^²®¿™ÜÞª /Û§­Ø~\Œ ¥ž…Ë*¤>_x]ÈiÍãh¿ÉÄá{o zE­Gû0Äê$.8Ð6ÙC“V÷« dùŸËäuóQZtQ„0û@ÿ]öÃÑé´zðÚí\dLSgK’ÃÙÃŒìòì”L¡?.²”?lj,¶•ú$˜‘ªðDZãØIØ3©¨P¶¬zœàdkî“våžy˜€Þitþ O¢9[€f¬÷ÑöœñJõ 6µÅ­ ûb;|ߺPÑ‹Ÿª9¢K™ËjñÔGGzσŸò$ªáeË’ÆÊÓ ¯@3·â­Í¿©Š¢ }ªœD]Ä:ð²ñÌula?Ò&£…ñªç ˜? 5ŽG¢äLFá³C·ˆì§höú¿ÌŽÒ³aÒà%õ˽™týz{”YÁ¾Z‹x.½N³‚ð~t:®˜SËXOl&©\ÁjéJâ‚ OQÕíìÎ3OPiBƒ¤gi5”Ù +žýa®—s¡Ôî7”Z¼‡O5eì·‹N´kåÍ´'-*cÌëªÃ¤][ëruH}!ÌÓ[3ùÐ˦×Áf•­ýMîîìÆÜ)Ó©ÄÐùý1g`å¼5Ì!=-öªÚQò÷fõ50Q}kòÎ38§-I)ñ¥{Α÷k.Áàr>jªRˆï»/3ß )¯å¦8HW/‡]w"(?…[3wTi@Z7¼$KªšQZÓ…¶mì!mV!\þn},Ì|¦šxM.ŠZì¾I…¢O“¡YÉ̆ü@ºùÍ~òÒY”:²ëG¢ß-z|ûF<{ÓŒ~Âhå,„LïdS[š@skÄÈ‘ò~&Âß¼mkÉ ²x,üHÆáúaøó™ŸìŽf,óÙý‡iËóLH‹ÃÅß9Zº¿ ˜3 ¼Ê–‘Rì†_2ä># <ò-5„ª;Qža²†§ ¯–̇§.ƒõõÇìåždEÄ}æz÷~¸¦~rÝcàá òÍÁ´¸ùéW¤Ox[;~~Uf†šðˆÀ°½.PÎûΪjÇ¿mó‰œS ÍQˆG»‰ Ø BJ¶}‚™q½hõâ/Œ6ˆÐ‘DkÒ;}eË@ ÊY{Ç<Ò¡÷¤-9ó@”Y˜È!}ô°Íø&lRª‡SNý§ ÆÒÃZ´?[NŸ¼?bÏ€Xäj¼œ´n™¬¡zÀÃ(©ÝfRæm¦}¢A/+fÅZãß9qü^ H4ª‡¯b$Öf:ƒÛŒ0v¸š;þ[—sa\ŸÞ~l‚ßLÊÐnÁ CÞÌbûÇL§s:Þø±Ÿ}Ÿu›± „B©ïfKûaêûo0§i]·þ ;mª$½l¹“É‘ÈÁGã$(¦’­:ZÄþ?ˆø«ª>_ÒK¦¯=ƒ8k wÃÞŒje¬Y±Öš$³ÂâÃÎ&1s;Äü‡‰Ì«Ø÷rkÀM9oÏìYù26VôÈ?`œ™Eäþã~ί Ø/O4ISp404•/b«9yð!} å©sg!œ•¹ù-lx™Øu'¯ƒ¾X¸Ô•5gç2òm•9g§ßz OÞK`[r=s W†Èø(‘„í:´±¬‹y³Ó—h­ªÀ3t5%5Э¢ƒV³OsWðÊã‡' LÑ)UÆÖªg “¦gL×dßã'üÖ,vìÑ%Ëïà]yS2Ñu·Mr:3;šR6ÆjùZBd¯*Î}uªªíÈÕmy°×¤ÖliÆí¢ôKÏn|XoDW:@›Õ÷1|ãÙ×Û>±¾·°ÍO…î.¤¥_$©‡á’¸’>×Õ§üö.Ôò¦ 9k;Ÿlš·š8òåÞûÉAoã†Il´‡^©ý‹õûÊQÑÁ'jâÄà®$‰¸šÆ8µdRž\bÉr¿d»µ(]*DXÁ*³(>-#µ,·°‰Xlna Æó—4†ÖQ^¼^ÇXðt±—TCÛ†¶O£ \›á¦D;ÐÉEþ-Ï™s/·‘.uL«ßŒ|û –KZP·q>Ô¹ºA‘ÿk¨qÚÅôhI€èÂ_°fš±]ûŽ«¸í(”Vïbö&ÍEÃSÚØøh ø±¯_ø ¦—¹†ûŸa÷º8<óY—~¶>É9[ù Çå‹©þˆ*z»Aì´ëx~­ ùp ìå‹(_¹åtVAå1 šQÕ†Y–O`ѵ4ø™ð èßÂQܳw|’#øV Fî`´nÛ7Tyc³// É#Ì© 7¨[g>nõÞ1•Ì:/{j4\ïýš˜ÁeO€Ù«Ëòú»âÔ*OºD–—fÎÛ Å7ˆ‡M*ÅmÅmsFñUp?.75¦?>KÕ•ÏQû2îÃ;`>²Ÿ-^am?[qlp=¹ßv%ÕˆÓ€Ž!LM‘1nLGn¬<öz Ó•0,O#‚Ÿ°ºËãÐ5þ>ú†j0^Fc°°§ÊyÔ@`ýØg“epEM;:ªèP£=¸ïäfÙÈ\°ÝîûâŒíîØtîìeÚ,€=æ@Íç÷-âÓ*F.N3¢7A”Šz¾ƒ5ø}Ü™µŸèÂ-¯Á{ÏF\æiñë¿‚ÏT?zG(šêÉЛ~v$\[‡¼ý=¯bðÊB ~áÀ*8 ÞEl" q—¬híbgë¨`.\ýº-0õ 9ð}'oÅ—’¬³éŠÆ0¦AÏgÊ»+ñï|tÿJ0‰ÿ+»N®5ùæá_ºôçPFÓ¤ÑáÔ+=X,>ŽW£oݺ`ÿErü¢º l£´Û›ˆÄÄ¢Ë+'f£ÁÌ­LgÍy- {ÊôNq$?_7³}ˬÀ~åf¿á |”‰ûzÙëƒ;pþl,I·Âi{vÅHn›úõ« ~g„õ¼zÿ¬ÚD¯W‹a‹h§D ;;¢öAo@Çeàk,_®$¶Å Õ°FyÕ(ºë¾L× îY~LjJ-þxaBoÏ’¦vWz¹W´€•ÆXb9Ž–ƒJdä·(w™ùKf乛殌Z²àŠèiôÊIÁ ¾"Ãf3ÏCŠá¿=®Ù~ú9räC¨%|¥Qt–Þ|ü®|ÁaµL!ç,”r–*»‘£(&~ç|ZÖ‡V-ÃÝÆŒÁ|(ÉzÏ)öÎnã{~²^Òpëfxª ¦S›è¥ ‚eωìo»õY›ø¾i„•Žk°{K.ÓÌfL…“s*@øà'ðør‡iªõ¢çn°™Ï/Áõ׺Dk@”Ε[ˆööEpj®!9ìUãP|#’Ë©¿€çæ>Ç„— Ä,€MO~²»´y1óÅ$OŽ:Ûóq%a¨Ÿ°b2•ÛÁìUêóåBv­/»±2.t€‚œ"ŒÁ…ÏÁCú=+÷BŒ®ÀOR·°ÞJ7ÐÔt/|Ó$ sxñóžhü2ã¨kPû uŒâµ8(š¦‚¿ëïã×kl8Þvn¥ˆóÒø`%òf¥ž\ œi’)X]¬E ¶wüHa¬›—pMï8¸‡æ²í_C^v4Ède³ÍÖa“æ6€‚fN—x" o…Cš–Ôöhõ‰’ôuGHGR;– ×Ë(÷Ã5TµùÉTI„’ßQŒW#/¼É…õ÷½ÑÒû8é`²¿#s!å”Qh[Ùl²†1î˜ñ|jÊqýÄT»sš9ú+Ö£ùSüvÞÖVƒàJqºëx[öT…>ßÒÁv} ÄYš’„²“ØZëžè®‚€ÈVÒá. ØU±s-îãß—!µBœ”;qHò—ÿ'g™»Û˜”†óøŸ†Ú$û<ì Ë€ZoUVp-ÂÊaðÿ0ñ¬©õ¥NØÇéëôÃý— ÂzR)'4‹à§Wg%³*‹¬ð…G W^y1ù¸‰ÑX‚^nÂ_ù/XWÿ-è½Ú˜¹ï†Œüv†ÍÊfØyê§ØsÒp×%2—¾g.=ÛI{g/g&¾wâ¥}EmßüG°ã·]à_ˆÁ¼ÝÌÞ.Yœ^êïŠ\ÈÉäk“}w<9¢Dç®JÂwK ˜¾¾hØæâÓ¬¨LÕ0j~φ àÌK‰Îu%Ò(,L/Ôz€ý¥,”ÛØŠsÄMX› ÇàÿîvK6Íâ<ÖzËyšíÛfóÇ(ÂQŒgóÊkpqº1\=o Ê;¡Ð“qŠ  x£>}iû¡§O<üí§o’BÚê†â½è;ü 4‰5}¼b- Õ­¯1Œå);|t:l® Ї§8Œ…8«Æ{øh!D¸§Âp·ŽûÓ±Šõ¤¯›ŸÍ¼ÇCE¿œçžº.€žÏa‹|œ.MÞ êã—Ó°Õü*t/¿á{ðÖ/^’PY.¿çs¿ª ³5RdÖú øÎV–Tu'áŽK#Ì¢'°h@—ö+m‡ò;á»ÜdxO[ÐçÊØ5$•yäÅGM©è/1õ_íÓüQöá#438…C†—X‘^2Ýé"~¿yÒ5˜Ë$’¹u^ÄN¿>‚˶@ÀÌ| Ta‡Æ_f÷RÂ6¬®Þûnã +Ix(Æá±z¾ &i\#÷;ËŠndl®Ö³«K‹1xÅÌ9çI—ê'A»€ãõ¢?ËIÐ}BËáãÁJfgr¶Ÿ•$"Aíàfᝒ {Ÿ7’ÌhvR†ƒÌ†eDºUŽØ†Œ1w2%Á¬1Ìê<é~Þq¦ØY NètÀÆ£ìâÔ.È*Nå<ªhÄ"Þðkßš¢H¾ÔlcÕŸÝǪI¾÷l–{þž8=ø¹bŠÀßý,$3¥Öál¢´¼]r=O°ÕÁŒÌ/MelÓf’‚p}º¸ö¾nꄘ#mìÇu ô®’ñS^‹O¦ }ÜOÿ#éÌéxß?NÖ}—È ÙS8s?RQR´PR"íißȾïY²D–$Köˆ3÷ƒR"¥PÚK«¢’¥~>ßßùk®™gfž3gžû~¿¯ëu½‘Qß•à)áéøP3ŠÃLÃ3Ø®`sÅÀˆQ-ÂŒ{‹ñ÷ŸÌìI¼òl=£ÉÉfSö1ÁÇ^0Š S<µ±`ïUÖ²Oz^¤B_1ŸKö­ó€ƒ[ß ‡ÜdOmø‚‚w“¹ƒß¹ÐØÇŒîüæ²êð¦â:ôO7[žcöÜbΙñÂÞ P·7[è®]ËiLs<è¹ÿÞ’døÝt‚IŒ¢ÿì¨ÒÍéDô.‹{J¯¡QUQPy.¹º‘ ÏåYdjî)F&[íåóPµ5åcºw_^Hz¶‘³îÍyh½4çþ»‡ïH6Ù¸žTÃìÉl»?宜îÝì« _Ìã®—ðhÉ 4VÞ„"ªàWs: bß’¡û ­#€¹ÖZör™3ª´g(PÁo 4Ï̦¾7£ÏÝæ‘qqXæqè]õ>È‹÷#›ðöÝ9dΕZÜ{!†Ýð»]öâ6_]Ø\¥G‹:šñzp(,·m…ðû8]c5ʪôJ!NlÁi®ÿ˜¤«BäúUâÕ(Gt=Á çᔑ-Ì›7 Ú&¢´ª ý.þ;¦Å¦£®àt,•ùÀ>€ÕõS03𻥮•á5U¡Ã”¬m\`WœÛëÉú~ ?Ëë²ñÓÄéÉe“Z*,C:ãÁ¥|`ëa7Y)8T˜ÊX4k rŽE%>_öì¶)˜¶) pÀғبÓ‚óIÜW`ÓÒ/"Ö¿A• uõâ~ýÔ11Ù¨k'Js~þ†¦˜Ítå÷Öè+mÐÜJûÂ9o¿™`1§­ø˜ÖÜAöÙÔiïuìô0ÔÁaá:H‘â̱.n‹©x(~áœ3)º'¯ÂGì™ o¼`®[¦nr£»*ÿñtŽÇ +,Ðè³@Á¡xÐw+Õ’^MÎLöÑMn.àGö7ÿe®JÇYQóO$[Í!2’÷?6ߎ¯"I¼LíÍI]²ÙƒÆ]2¦‡Xx݃iÒmlÕYê0=©fàã=ØpLœùvl õü·œ½¦E‚\IáóËÔçaÊÒôGB3süåup/㌼þ“%4Ñqò’|Lv¸!¯¶}-öÅ>æ —Ç; ²é_w€×µÐék 9‘]Ÿñ°I |ÀæoqÛ§ÓPgíÅ=ï 2iï¨Òƒ,ö¾2œÊGÛ!Iö”J(Ìh®Cÿ‚ô>ö•uŒ ¹’õôκ™ÔnÛfšüR·ñÆÂ‹¢9ô5g”ùÜÔ³;4™òÅøÆÜš*a¨dR]:(ù”ŸîFN©¡Úï7n¯)&‹¤çÇ‘Œ±Ï~Ê›eŠa$çåD•CÔCaZÚ1ªÊmFÓÑEÿ]DÌhݸ)1œ£ÆxéÓ§lØú¡ÞµJØ$TÔÃP—2nÿ/ßÕ–Ìp'ÏÂé©ÕÚ\‘±rƒe ã6}Ù W®÷3 ék8¯ÛÅè'ßM0u¸„=& Ù2°GóÖn(Ä[%|$Äø„µ§ÐËÒ¾äÕK{ ÉW¡Rý¾´a‰9 ²-¤»{[©Ü©CÌOp'ëv̓Ÿ‹qüW/תöûáó3ÎʼsX×ù–Vä\'[^ˆÓÕü4ãÊUˆ#²ôó…›úÊ®­£ö­8ëGz/F,^6•Goƒ [¬]ÚŽ‘gñ§Û2¼t gåÕÆ¶Ó§1êÏ\*×X |L‰ñWÒÒÁ“~&ä–ÖT0ÿÐ {³xÉ­f7¸² ZN2º é“X ×ùMhý£ <ür)MýžÊ ‰4?D€>ÊØÅXÍfΟÀz!'R•ŸŽ‘¡AX’”Me܅Ш؆k«¦Mä9¿Ù°¯]LÞi òè8)øx¹IŠ^¼€ÿŽ{½y ‚;ÃQû|åoزŽìµ»{–ùÀŸØgl‚R'›±v¸iQ£Æz¯@ýÛxh7ÖBÍ\üj ¯±I~¶Àwä{)­¥\NïIOdö™1y"Ê;²’±Ìï'ÌiR!Û–YbþáÜT¹ln,#•áá[(æŽK/1‘Îñþ@½!i<Â}Š;ÓKñœ×v×ù¸VÚ æ\ØÊOáJð£n’ü0¡ÕŒåNT*tv•·â”èϘX±·ß ÃÕB«!l-—á·u$o9#ðá^-×®|>‰kA”ã§°¢F׸¥âðÀ¤ÿóÝfÏ4)~XŽËoT ‡Ï”øþņ¦1·‚û.ÿ;v $±ÂŠ”ß΀ÔúÝ„Æo_aâx4(o˜öç‘úƒ†„%÷Ücwþ$hyŠ~nå#×v¢›+S¹™ïú ¸0<þ™qÊlÔ¹í/tèªéÛ!dO ®ZlRL*WXPçgéli¨+šÇ&’t͇ΥgÞ‹Ò°21\NdhW´3 /êáJË/8$rš•ú¨M©Dýýè>œŠ‘b/ž<3o‡‹1-˜2Ó‰F~Õ£_Ž’Žò¤sTg€~îCîšTs6dl ~¿ÐÀ¢ô7°K¨Çwá›™¥½x²æøÝÞg3ס’[Oâ.|ƒƒj[¡ëWL³a6oOÇåÃépv†Ùs?‚i[ÞlI¯o‘_Î’ÐXS,¸Oxe1N‡pÑßsàyí!zNçA|òlW.ÇC'Ó-®ŠZ‡oƒÂôPFèÚ/øq1•ûSì lu‡€¥Ndå <½¿ž]p@‰;õ>÷Hò[®{-z‰ƒˆ1•:Š4kν°‹Ä‡Ù‘‹Ô¨äè%XN7£À²YŒ—錀•!ìÝgM¢PE'mH†Ãø9׃£²>‰QüÎÆå݇~ãçxsŸùhý˜­p8H'¢gÀ¨ëˆ¾å%깂de€:Z†}b&ùzý4¨+ñq›S?èÏ6>¢‘Ù¦ÏT°»mùzr+[i€î&á´Dã*ùndG•Îó3Ç—~Gž È%×L<œÔ6_óØ‹ÅËÈ’Gºdê¨hÅ+‘]1úä˜ÓºÇP–DSfd ¦P‹¯ŠdÜ#ÂÊàöP"d¼X I‰24ÑûðÛ;¡–Í7Æa/]ò{.Ó×`^b›i—R£SÑÂeDŒéºÍbÀÉGÓ߀=€²ù]èX©LâbIåÁo\_*¦ CVþðæˆ™¾†Û¹ÊÌŠ„ý÷™I›-:!n8w[[âÏC6]OýÝæ\q“ýd¢ÈŠFP¤ŠÎšÌ&RÜ;k D+j‚™ÏtÔ¬ÔÅÙ'µéÖ35x#zNˆ2…T6m‚Éy'É©gÃóÂY¹˜<é<8ùn»ÞŸ€W–îô"ËükZN4"ŸÂ-e ØÔ_Ãü’6µqˆ…_kY݃Txáuø®DÇ>o"/-ä*{¹³I)¾Äj¡ aã©À>MrCׂ.l¤‹{£`‹£.ýá› ûåhèߥD÷“ÛißAìUzÁuE(‰küŒ§ç}‚mrtíì)dÛü!¶qÊj6ÿ$‡n.D3*››¯Ï{ƒ»|ðÇ94ôN€§+g“ÃÇÓ¡7¦œqKNÆÄŽqœ D¼³ ”‡CàP¥ÞÌ4ìÕå'çM-ÉþU†$éô9Æ·7VµmbåùŸƒê£væx]69ãtsZ^5H>¾ÎiÙÆTúLö‘eu@‚VCJ·$|¹ÄÁ,¦O¤£ÑL”œØ¥°Æö=ë£ÕÊ.=ÇrŸœ¬Úy„ýð(V¯þ'…¬…”%{ÙÇÞ,¼ 'ß4²ïk³Ai] ü¶ÞÍG”h—…˜QñBðs¦ MÞ°Š ÏCÔœsp@¦Vƈ\†¸MñÌìƒoÑÊE‹Y½*^®ŸO;$6ß¡F°©ÂŒÅ‹ñøë&”ýçŽÇä«áùª#ز"‡{¶µyŒ£ß°DŒGÝc¥ì·À·U‘üH\M}ßu¢ ß}Š=íŒ*YÔr¦¥¥.MÆt¦v4ħ7ÅÃæd^<ÃoÌûVtAé³fXdSˆæ—)’†÷Ì/9–•¶ÿÂ]ScI/xÕ3ë“c“×8ÿç”P!ι̼¬óŽ{/ª$þíe'"§R#K;v‰³‰/“£òk°®ûJ³d‹ ,«æÎHžJÿc”ÓÇAîeÏDO°e;rÁñ-sºÉ‰Þ ¤»?€À•¡dvå%fj¬ Èl;‰Þoþb¨¬4È\¦qVðv"”ŠQ7ÈŽ#Œýµt±½‡&è£Ãdùƒ"²€¤‹ 2â+Hó¡j;ßš×PÁú•Äqƒá$R¸u“ýôQ·uÃôÛà¬Œí§½±C/ƒ>㜄ƒ9,ã{M/>Ù‘¯ÜÞ÷+a$ó++x¯ž#ø+-M§)aù¨t™—nûÜVú4HJû?–™yú·„)–¼þÞæPT)A•Kœéy3ô=žE_œx†çüJQK8Œz¿€šNiøØuœZ_S£ Ÿÿ‚]î;ÉÉ3Ï‘ï?Iq$V¦Q“…&Ô´´ô¢é_j÷R‰~T~Ã>ið$v:$ è½0²o_(_îDGÚò±bð+jGÓ; 𡚠-7E;W:b<Ôﺶ­ÅpzèäXó³vW%ˆø¦ÏÀ}ÁL êÀŸÊd‡È zÆ7žäŽbÃòcªñç£ÿ¶FÓî)jûp¹~ü.øõvàà‚ æÀQ‘&j#l{­Í{«ïìó£áͧz3ÛàØywô_¨@è½ tsdØnA1rÝ4³/ÁÝ”ZÐ_Ž¿ä ®4yÓ#LŽZÞÃÕâ4E4 íÌ£pæwi:è²¾¤?ƒáÃ5°Ç¨‡Õ‰ká ›AÔÊüÿØkr¢X‹ö¦¦¡óº§9=ò÷À>f/¯q6oÆÉy!UÖŸCÌ%È,Ÿ›lâî°¿îÊtÜÄ¥îkØk œ—Ãxoñb˜«CþíÄl[Û\Üdu­¥CÒ+yÆdq&»Š¢¼±"@œšMHÑñ›ðÛ9›§gB¶pÁöËV¨¸jBÏï€3“Ø£ s!¥/‚©}ÕÆœËœAß÷DÑí¦ödÊ©Éï( ½öÑbàVI1þv;9‡kŸÂ´øÌA­ëÌÅ—¦ÌÀ¾Ls}6ÂïÏáÍ¥«LÀT#ìCÔ<¸;ø­ ~œ©Å^÷q¶»4S»…v{-âd›Ãþ·Ùô™2 Sç´K\žÕ*ÌâÑ“ôÚ‹™ô{ymó×íêTdà*§fÆm¢ßgO?PåÈBòpj>›rÅ™ûÆ2”|) §õ·–3š¨y·,N1YïDè]¡ÄðÄì3ØÄð „A%;›AçÝLìHs½'‚³.CÙ^Að}uˆ¸óõcèü$Ú0Žš”ºpØ÷)Ó©C´*¹Ü“„gUËð÷ÙA­9[Øíöô¹έ)"õ ƒ­|âK ¦¼B¥êhè8Z ~ åÌÊ)§Øá=qxÚψr¾&ñÊéܯ“ ÿÉcšç ¬Whâô,„ûRXÈ+KW¼ßË@~÷¿L裼#h/Œí¥‡!)O ¾ý¥¬aù3h]×öm!‹»øa©b)>³hfþfOz¡YpEè&æ6ÇÓ?FP;}*í_‰;ÆX}!u¨×KœÃе2ôjy kx.ƒy ã¿*ˆXŒ?O½f[– ±m{ØOqÍóXnu]M­È#ß¹ô¤ƒ ›¦‘Š+=4èRIQzìiýûÚ˜òÚG’ã§Æñª™.œ¶D|²Á¬.÷G¦Òý¡ôMÊäöôh_E¶5Lá¼Ý»5“ØMÛ'ûú5ŠªyTÎT•6ÑlÎ(åBã¬Nõ­h¸ŽœÕ‰ë߇PüáF*u#,±㔈.„ß0Úú¹_ÿ=ÍMãýËζ}ÆþÛâÈ?ü‰þ;ø`d©8),‘ ½ZPOñm}>.­|ƒâüß¡;`9y46•1ëÿŒlœ4ýmü~o „£g¸Éñí`5‡YfáM:ÔÞu?´Xrw­ÚÄ¿Àåh¤Ó­{{¡û“'$ÈÐCFÞQ„ìÕ…C¿ÛqÆìG¨»Hß]{Ü“tž‹E:Ö~e Ÿ‡Ógÿ´Qbé´zQkl¶°D<„ø‡¤¢ÓÝÆÿö1+Ó^pì¦Æ_qIˆ1ß/b°¢›öë$ âCa1´ãn Ü~êÈl5 ¡GJ]ð±Íiz?õ-S"C„f^@qxöëíPÜ43fv3KÞ²öµì›A0ïÇì0/0{(ƒtÉYis²'æ,7Þþ=š¼R'×á6rÍMèåÇ)Lrí9<ög›P™Ì,/ÿ?EÀH'.ÏöÇ»".ÔùØ5hë¤;ŽÒfϳ`ѹ˜ðêŽàU•UT®Ò‰T+{ v´ùÉU½¹‹²O‘ŸÒZpâ$‘òÙ¥F*Äa§ ÕŸ§Bèà_t8 œs¹´åâk¬Sèb.b>ùm ZÛÁËPò?ÏÖ=ÞÏ^ÛÏzˆ=…u0ù ‹æÓêšÓ¸mæf C]Ï\aǃF`«Õ%쌅Â3Fö–MH :ŽôÒ‚õ£êôAa! >¥#ŸÈeŸå}>˜>þ´¢ÅÔr9}+ZLÒF䨈ž&ik>…¼Öôí…†¼uÆ»ŠoÁA¨ÙùY„ÞYÕ‡[GÎÓãïŰþÜTìºi–þ¯°_¡{VãåGÀýóUpÝ ŒÌˆU'1®ßa®E>£çØr¥°5D•\99„£è%‡¤l¹©²ÅcóÅÈlf?^¾.Š÷E ¨¶´Óº\™Èö ?ßAêéžK†ù©Où;W!ºÌanÜmÐ;BGn&CZÕ¹¹tKëO¨òi¶yUGÞ)N'ŠG«Qx›gmÉû½í,–ûÑ,V¿o&yvè<ýùgðæÛ^ K²´Z޹gÞ…6¼gÙŒ²PöìÔ,ð=8ùÿ<Á®8môÂå?oI¬ñVΦEkñÊ/DÑÖVi`n×þ€½ofÃV­#ܽ2AðOP—¶^Å·Qxýès^Çæ+é07¶ä ]%6’ ðò¿ûÀ;©eŒ ði`ë*(‚Q»Çaç-®8ù•lIêŒ/²@æ÷’#pÝÖ¾ÅmÍzh‘GëX;ª?1$ÜÓ8§æÈ‚!»ˆŸ[‰ÝßZ9¯ß³ÞÌzVEß¶ ´áû¨:XóáÌo^AR6GÑŸ#—é lzðî¡®Æ!ìß{3qìîEPëf9»ï›£Ì/utžÇKôv¼€]üôû×Ûlk”÷Ï#³Ò}É‘…|d‹Î(žáª‘;fѤâP<‰ð:Hrö‘È~-Â÷èr¦U¯f:ïË[hîÌ€»í $I¬ì¦+Ó<ÿYxÕœÐ}×ýae¸!ô„­&}ÁÓˆ‹½7{ÑãÿˆÅ3®oÉ GQZ’§F_†çZŒ‚ÄNù‰w¾§aÍTÄ´ßèÕWHÎ/óU£›ìš¸$’UoDgâýÚf‰»=\^=”‚ÌQ8C–úzò@_V4‘_DhçQq:Õèü:- V£ÑÆ#d×âæÉñ‘Ü%î7ØÞUrä=÷í¤ÆËFæ&Û¤³œÜ'ÃB³q¯8©¸4<üNsÊ»y¹³\jàOõZfÚè¤ý{†Û7½ñá :íå5Y»oÙá„%îØµ÷åˣϔL˜(ú_NòÙÛ”}µy>dú y/´9å3çdÞù7Ÿ¦Ã­*8ÚPÝ,ô7WÏü¾X¢w¼Ëª`.Î]ùSüQlðlïö`í¯Î›ÅgQQúµ­ ¼µé1aúoÂõŽŠÒOŸéÚ5 `©ÎsV‚†2I³ò×°Q›ýÏŸ1ÜWG¡gŒÁé ¸ÇT…F±ZlÏV[ÕhvE€Ë‘¢Î‚ñ8Ý[nÆwÌ×å[páX=Ì­ölc<”7Mƒ+ÉíÑ ?ëПQ4S`.sk ßçÏg~ô‚DטU/J4`ôg¦O2xÄMHyk }ZJæè)2.ÍѨíwƒôŒKâÌKÝðäìfªp§™éÿ¶]hZOD‘±þÃ0­ån\MÏ\ýÊ}|| )¶ÙHmCÒÁB‘‡ÿ8ÒVÁ§¯Î´ëZœŒ@«¯:dã‰uôyÅRü’+ˆMÿÝO¢rW‡:{G?ÇLÈ<•§^¿Ežet†¡ɬ~±Gõ‘=ï@£ë¬'{¬ sZ?/ ?³Àj?ùÊãÑ®éÔhc"¾LEѧ(t/mP¦ê•é5ޤÞ#Ê+È“%ú°Â’¡O<ÆÁþ8§`·ì^ó–½>¢N#Î%ÃÚ9–D¡aO„Sl…LUyô”ÈD7{ÎÝàHm‚OÒ¸ÎbÈ0p¡ÓÓŠÐ~“-dצ3·ë¦’O&o·=*$é‘Y˃0ûik$Úˆ"ܯð¥õ=ˆkݹY‡úW™ÓzÞÏìÙ‚etB¹,ï?‡mÆçq‰T0Œ™¡îÇóŒÝq.†ßßF„NxB…+Yhü›•zõB½*°ñC WÝe/y6À4=¶`­…#°óú"Æö•x]tg×!|¤ë¿qõcÑ4ÀCƒœ»p–£’£M.–JUÔÉ|ÎÜŸÏ$þ]‚éq¦ÔàÈöþ K|­§@^HtAð±Í¼s \6Ó'E [ðãºä&æÈÒý³RXµSÁ´H.R7 ›$™Ä6µ¯%y–ðÉô ljù‡9W}`—ãjàéŸM=o»ãÔg§‰ìŒëpèÔ ÌØŒë;O3ù–\KzêÉBÌ—ÓŸ/ ÓÂõhÖl‹|κä†F³˜ã)1h7~ D«íþ›+ 6&`vI<O#þëè­ëQ{`†Þ¯…ú—†(½ý,~ÝëN¾yô_¾5scãh°û†›8Kè•ò/h4ï=fœûgÉâ5ñpŸ¦„ýï1K{éº\ +"¢àÍ« øÓ¼EÚ…òè<†€ “~Rý%›ñe {»Kšx}Y æó=àФ/ªš÷•uÜ\Š}· ÙnžÔø[>#/ÓšYmÉ]¿(&ŒaûZežXóÎÂñàå°væZÀcªä³g%|êØla3Žys“qýH¼õ  C”†ñõIú½b#Ñ WÆo&uzÍJì_›B²Îš‘‘•[qVi ^í“£n†V²= {‹£âm£9x¯ÌÀµWd™ÿrŸÓ}½ð^„ô)’îJîÅ÷bàϣəæû ¶]Œþ6{¾´2o'ÒYø(†)BÛh¨äVrñ'Ã_«°sÓl¢³«”õC—qÜKí^—à‰høy[þ¾s%ƒ:®ð¯'‰d>FÛÅèsÇ ¬h, ;cɤœÌ%÷šÇQëÂ3Ô÷Ñ î™äÇ”iôÖ/äâ­‚`ò¡M”Š—èÑqãbܶµoìÕb¢ÄºBž­½ZK:BÏÂÆÁðgâ;\8—Ë–ÖÁÛ)Ò(xÎÚPÌ€q.ãºÝÏÀ7E‰\Tø žN’äô—XX×êB^ä{’HG r8ÀŸ¦èäÝÎÜî×;Ñbè4,‹Âg9Ìì>-ò+e6vFAïWvž³¨t=BAo'ñÍn=S?þÙÈû‹‡^àà.uIî”<Ôì²Â¡Ç`žsŒ®‹f&ìtÈOÎVþe*-MžÈ}¬ú6×Q¢{œŽËþ—ý£1s¯3»vA(T|v"¿tÁµ¨ï˜GߨàéæyŒš[ˆ§9Ýè¯R=z3éã‰\,Î\u…¯aìÈ)°Ïïen2)ü 8мKIŽS½à½Û“eÈ{¡yxýÉ29gëéulrƒ}ŠŽè^lbÑF8üÙ—)~ž …ãù2]Ú±h ÝvR‹ó¼NŸ]$u®¢Ÿ”ÚñdÃ]öÞŒü›yÍ#/pFÏ¿g'½AuÁ|¬5–&w\ÂZYÜœ_ŠŽ³gaÿ9P«þ z磥Áî*Ja- 7kELeà¤49RøÖ_Ïy ?GAÓtn/ùÊ}Ê t<×zÂÊ/KÙºýœ|ö8œ±mUGp•P tÌsƒ·GÍñ[&ìÙoLæŽÛØ ©C¢ä–Ò,WÖw27ÖàKUMð”eàwE}§‘3Œ¶áÍÐ$fÇM[( ¥§L<ñNÖC0öÃÝZ; ìB>›™›8XÆoÉD[ØÒ¯‹×am¾ Ž¿ËÅ?êqM˜"³}ì0´ÏωWp ÷¿ \ö Àeô]—ÉIÉJ ¾¬å^fsÅJñ׌¹x´´›™ÊL!Ó3¼ðõÌrÜ-8®ï[õ,öâÌ)Dfël²ùrß´µ ÑéAF ¬ï[¾ÇS²e¸Þ¿|èkÞx|+ÕþâŠ_ç¶Bþ,>F´¥®§î  ¨ÒÜx¦Q6Š$àÀíýPÉãm¾°DÞÛ‹ÆÀ[Ï€¶| ƒ‚Ö8 0 iídŸæØÿeûê… 7E†9ùqq>ù‰©ìP„ª ǰG¶çΓ†UNC‹¥$‰:wu[véÆqߺêP‘¶M¤èª´ð­ƒOz& Ð„{ÔËlÖÜQ‡Æ[‰¬ÌÃdæôŒÝÄW&’2t¨Fü2jb ^r_QáÁ%¼ÖðæŠ~ÞqpóÛáì­ÈS½±[óÖfL\“.ïÉ„=ò-\~ù|z6^‹Ì2êalJ`k›N}ÇËlˆ·&ïæm«Ò{aé°)y¿ä„V«ÓÍOV2.¿§UÇ^ÈÏzÕ¼Û’aÞm˜IìüƒaÀ´ï¼‘¥‚ÑbÉb&¬U–ì{+ sã?0jR"´>ÖÏú½ÅÏãaâ¡rŽÚRçÈNŒÕ²ÞW3Iå‡l¦à…0 N8‚¿‹Ž³]©+až¶gÇÄBòó«ûw5ü06"¶1ìc5iiN³Iªà¥¿ÒÔk Î;¬Aæ©K¾b°eÛkxö`x¼?@âoý}ǃÔãB ^2&_s\èÊëEÔlÞX{Ô‚oH°÷¶&Bé·Óôç0<>äÅ|ݹ–=l,NN'í„ņ’Äie*^ßöÿËg>xäfúýcrt[ôA¸Ñ{šìñçÁ­.FÿñÇP«zž:,„;_äè¹Y _ ¤6N¡ÒQe³1œÃlÒ%o×pK³oLB`äÇz®E8á¾+Çwb†ÐÕuÏ¬Õ _Ì–0~Þ¦0g2T¡Ô‚>ð[Z þÜ¢”'¬nàL°Ü<ô_Þ3¬³¡ŽÊ£¸í/zJ’C[rè»xt¨ƒçMƒ=Ò”K’HÆèGÖ"I™ W ð)Ǧ=µ®:Ô‘¦úAVÄ*’Õ]쉬¹'Ð"¤6ŽošC~]¿ ÇV̱·ndÛýF¸3æI/ÙÕ£´È8\ÜE Ù’Ö‡ŸB¯×da… Ø/OëÿySÛži¤­Å „jÚ¹qw+`y~;^‹—纄úÒµ|)x}‡?ÕÚ±Hù¤Ñ )KâÖLDï¹¾»Ái¹-<ÓÅ>[}ŒôךU<´ÅÚ€.}už ÑÀWQ–X ‘É]¬-K¸ÞÀ¶«fÜ=¥eìíoºTE3‘Èô!|ûÎÑyoIÃÁlô$•è(L%G ÃIú ÖÙôÞ{‘Ó^þ.˜ûqå«kSóø!Ê%grÌœ€ý—y˜u|>^h=Køâ½@¤6ÆgœÇõTNy5Ó=ÅXïΡm«#ÉFƒcd®h®¾þo>ºL®ó䂇}ZØì "¼ôhV²þ-ôòªwd‹ÌoXJo•ƒÏNsÒx2 óüèDÄ}H©Æ¶]I¬a‰qä ‹ªÇ…ý܃.²ßXïhØŒ£"bX°õ[³Ä£x ~Ò/§‚Yž(5C0ˆ¢š¹ôd¾ ý{NK™IòühèÑ2¿¸ 5:èŸ66Ÿ>EÌf‚Ï› ú¦ý®(Ÿ#–Z•a¹ã;L<2\‘2`{7’cz¶tAÄfÙAÄÂï%It¤º–Ö#Ÿ“}ñùh5œ`öq~ìVc˜mgȱ”ÍÐ|ó=Wž+2¿ÝÿrêzŸ’áóB sð/zwŸ¦’åØøÌ‡¾Ð²§{2-ÉÆ$[Vl¥-_C v’d©jÒOU‹äIþ?^œµB€öqÁË tÊÔϰ a/T¸$Ä~J”¡#ÏÍè:3H~ Ž}Ad¥ƒ«KL>ËÆæÙî×pŸüQ²êTâ»ÍL'™9é>yhiV?Ó[ èY§Cü³OP+«ùdàg.ÑÚš¿6 o|§xHÕ{ui¤i7ìÌ”¦ÿqÙVæ=H6 а:)â¼X1c=b[ƒ[yò@Qh‚á Þ„¡®Þ NðÕ2Tú~¶¹?ÿµòÔ´MšÌiÑÇþ¹9øK˜…@æ˜ghÀ­kæ`¶ ’9-=ƒXš?Ç~Ÿ0mp†“ۛшÿÆ £ž¾ŽPR`JB¿«á÷ÓÄŒhಳͬñµÇŒÍ˜|Œ_Bùõ"aYèè‹ȵ¥'¼\§ð‡ñ\°lÎL`¿n\‡§65¢€3®o¦[‡xTëÒè_+èÂy1`½GvèÁâ—@m†WÒ,¿'Jâ’ U²±\޾!FÐÿ‚6rn(r,Ãö€åySç ±ÉùwŠ(›æ€-Ü]!¹ÑP2”MÇbGÀò}>–W§³SSÙ×3Ñ¢M€¾¨­å¦\\I 6f0Û Ó0îh$è·•ÆʖǵÀÞÇwXÝÆsM~8RÉH¼ŸIÿã¢×›Réö"ðŸ5†á ž`÷@¿»åQ£(U:ô¸ ŠjÒa¼l1Õ\cÍN–Œ—ÞÆáÞçi¬©.Ô4œ…-íIpM›—>¶€•üœÌ·ÃëýC(ßó·×è±…Ò²% ?¡ÅdÃ\Kš³ŸJìwF™i\¥¦kúhsè­ýqù§Æ¯i0ùòã¼åÙÆ^ ÿÉŽä½g?,¼ˆQšÎ¨:‘‰}ö37?HÐ3agÐdž*Þ’U`z$.q󯵡J(hsDü¯2uüPþ&“Õ~ Ÿbè­_¬ÞRvï÷høÝÆC ©Ák è]•CÔOQoáf>!Øá¸‚È®;ͽaô¯¹­ßã§‘©7&ß‡ë œkÉrd`+ |~üDâ¨s~Á9”å7B‡õG`Âõ ®ßB¶Ñäfqrxêl?ÆÀÜ¡‹ÐP#Bm÷ŸÇ¸ž¸X:^?ÝL?&#§1ÿ¸ê>­Ùû(I™G¦½f‘§Ž háVzÏü>x׌ý ÃÓpeymØS–“zU¸#š ^‘§Žßƒ|]-2{<8íaw•@™9,[õH›¶ÙH‹>¶IM€´Uá4JXeÅB!º§±M‘1YŸA¿–4€¹Á6ŒJT¥ÁQ÷I]ø ¬×{ºòž'vƒêǶ¥eáÉèWìƒ[Uáï6i ?··iÐ!Õj[$O`%ö=‡ù&*$˜I‡h¥ø=Ô¯Èg;V2íg{Šg3›×ÝÆ¨;Šì;ÄnR?IÆ4âá…g5Èr\QìVò”ÉáÕ§ÀZ\‡(&,!aÚƒàwû ã;QÚ~B#Œ;?óÐõ&êlÑA˜-_H~Â’¥ÿ8ê‹$aÛÂrf`¾žúÍüòÝG[ö˜’k¶÷ qg î]O–Õû0Zaé( d^gǯ±–Þ_Ù‚‰`Ò ÊTæ+ùƒ)þ²ƒãÀp…ϱë—+À-9zDg)½žŽŒ½o Ùä»eOIѺÁð›N>N“ ›8“a\âw?¦ÿˆ†ôY•Ñ? sËáõ3¢9ÆÂ«iñÎTÒ#Ž·{Ãàm¸sz »,¸‰šK2ýHŸ— HI.¤ùï‚iâ?F¼Dü?–fž˜Îòò9ÐýF0!cAçÕ½frxž‘hœý(„]À´Íö`ºî| o¾ŒQÇ”éòoÔ69ŠŒ ^CŸx?ô â¡S„RéGëÇÜ}#äÈæ#Ô`ª4é(³£}“>­Et5Lž ãGò ÕH/ ű³¼f ¨ ìk‹Ç·äÈ™‚PÈûÌ}ã?ÂqÜ(NÅüα“c±Í)‰MÝs„î>‹ÿG˜yòIÜù'ƒáœ‡+ý9SæLÖ„~9¢—]…FŸgTŒ¯…U· IÂFYøÖg§±×o}BÝ•q€;—’š†+T_‚nÓ G>š?±ž7ìàìöE¸¥§†¿ÜÇóQ©ÀÙ³œmÑÛŽ·u”šý–ý€u¹Õ?¹š\/ÁñÜ:”®}Âü8¨AϹŽÀYZøŸJð¿c}ü:˜„Ç^xK9 oö±û$±ùÕwvò<öçγqX˜¾ð|žž D‚èÓεLÌgMø»ê/y|2*›™TKgøç¾îÿñ#‹‚¡Ó1ŒûéÌAÒ½‡^î$">á‰+Юy6uÞþ㳨¯Ã»/’ÛÕJøñʤonÒÂ%‡郅•¸W—ì¶h²-Ü¥D 2h«P¨]»‰üÝ€Ìþ_(q<šþÒÁÏú°bçr¹ÎŠ4¥Ápž%޲àÒ)H3Ê\Èß +Ü‚®PFÓ­·âô†/¤NI„›Å'è%Çðü·5}]Þ_£)cêtõ›Ùí«™âlxcûø8š`úK?:0ëkæÐ òNâÚk[¹GÏÉà‚ÕÔRºyÔçPñ!’À·‘9\’ÌýöI€Óyn ,zaF£Õò0FFž=vÇêoP”³™+h„õkqÝŠýnؘ%C‡ïVP¡CŒÅG<¢†A3»ôŒšØuòéO³2¼k)Gfµeá·Q~/w'ˆÁ¢È'ö“µ>t€°…btÁEI¬/“&ÄÑT~ m×f±÷ö6_yTp‹ ‚Ïö@nD¹/¼Ž}dDGÙÓm=LF³¶T‡±÷i°Í¡QsÔàfÉ–Ü€úe\>º€j C,¯0­ÚÐÆèøžÆO É×=ÊDrA)”ídèÔ`dºy‡ÀÅólÜLš>„Sý›`±ÜŒ¨ `æü2rË%¬ˆr"e"ÂP×°Ýæùbe*´ð¼¬dž+Á:¶¥8Ï0öµ1Z¨ÏÉ»v$·¡YànZ½‰¼;–Švy¤ìŸ;Öï§KÓ¡{B´$ô9«×„ãinTeS0<·2¡¾ÒrÔ|<‹Ú_K'ÁäYº'v O§Ç—ÑMÉ•&c²ô\òúÈR#Qr×YÙÑšu¯­ wWv ßkª³î(.Õ-£©ÆJTo'Qܾ‰Fñ[ОÊòÊé Ø¦Ã;SCr¸!þV½ÙEHÏbîè?ÿTú¦g)éÎÖ¦?®ì¤vîô™]˫ǤÈBˆæ)ÔÖ:Ç>´Þ‚ß¶ F`bsåÞxòáÊR*ÉÕ%'¯¼`æìÂÆÍŠä¼›#áwʤ—x’ÉC9Ž¿Íªæ—@ ~„ý¡ÎÂa,Ђ¯à³Ëe¢T¦G ?]GÛì$0ÌÜ‹Š%¦SÖ˜EütÑŽÅdbj{í×nÒ"~ƒ3”ÈUaò3A“š„ƒîgà¦:³lÓRöB‚!ÙØÌ ›eŒé…›$r žhM»4üo(©1œà©_ÃŒ˜Ú=W©VÌ fG{¬˜Á,b*Pžµ¦DaÓrº¾B´ÄÿýÇQ£fื“µÆÒ»R0G(õL`Z™6&®mav÷—rš³Ès˜ÿ6&¯Ç´r@ÅïæÌáƒÚ0_´e.40+®¦‘Ñë—ánìhÏ%—5¨d’7#š–­O8ñfpû ]Xñt?9úÎäÜspSò.R¸-ŠÊïa…áùƒ‹hC÷ën\½¯*!MÔ…Èïã§/yyÉþcÑà)Œ=…g¹h4óCüQÁi n~y;>~€B‰T¼2å «p`*‘¸Ûqýsȼ©G`“³7[ô/õ<°Ý$öZ=ÁÄé›°âÇZ4ç*àdCïm#œn1²ç Cœ85Ø«™‰oé\"È9AÎà^éb0 ÚÎ*r(~ý= ßÕ a‹V®Y؆/$éëÃ1T¼ø[—6ðK¤_ˆJÆvÒÝÀþ—U=ÅäO—%šŸ@”×£“:pº§z“©ä tupBûãÇ!ñ‚]=[ƒY^„òg½À¢•‡Š«vÂîuuxäC%῎í{[Ø„ Q$@ÝÕc¨Öd:¡nÄ9¡‘̸¿?6@CY&¨¥½eß^8†œ@qä5S%ýŽœ]•á$û–çÍìB:Ç<ЦG‡áш!zg¦5îÚ½ª²tè£Ö,L:„…kûÈŒ‘+äd4Gk¶:î^¶–¾ßr>¸<‚o=ûp”¯xDNâÊøûàýÍÊ%ÀÕmg {ÿzj´yÌ\ÿäMñÄñXã'Š_“ÎËËɧÞmD1÷-l¸9Ȇ‡ò´l ¿I„‚„é«ÈùD,nÒ/¥¼¤¥ !tcØ+¨½5•Ìø{Œ¦O™FS¿i“{í[¨&ì&;Ý·ÐÃþ? è^Uß|ÎÚ“§hœÖŒ ¼éÁe9]/DxËs°JJ‹<›Þ˽yêߘ‰é?ü©q´?MTo$1¥ª¶†~1{=ÇÙèÒaf•÷h£—¾ú»:råb*ÉéO"•[â¢ÓBVˆF ¿Ý?LOÁu¨h#÷í6“£ÓížÑmÇXt\L?O½Bû‡ymyù&ýŽ ;«—“#³%hÓ0¾¿‡^|Æò¨ü$[è(sk0¼e!DgØ TôbÊ_IzrU*ùΗ ×½…èuÓRÌù>…ü¾ÆL½p‘jåÝç–ÔOsôG4â½íÒT=zˆ™˜©D£[ãùÌÓä“Rô¬ óG­m ”âM·ä9s«¬_0Iâp\ÒšZ Ì% ±(\ùÞw2§z¯¡½·÷‰˜¬¹mÌÌÒÙdÚìó`sLœÈžoN#Áe‰? [#ÝÓçÓ«·N3ë¯ØÑ„+š¤ÙOŸœÇG•Û¶BÔœSørÅz[Ü ­»5ˆ¶Y+|ÙeD—^ÊdVwd`¬¾ Yç1‹Ê¶ÃVÞ$|Ûv•i_¢DøE ¸jéÍl³_)k"¸öÁL”þ5—œn)z<Úh²ˆç®ð3BÏ›fàüð 5òÚßJ•iñ‹/Å?Á(­}çæŠ“U¨Kíbö;i¬\§ü¿üèäq¼Ø¶m¡ˆ‘£¾SÈ©!)l³ð$ œ˜Å9“ÞYð"gûÊùÔLh¹sq´Cîá¼)`PRu¾è¿×}Ò[öÒ;êÿË ^§º4~L§n¶F”tVƒ’C/^ÎÌ¥Bâ‚ðþéÙæy›n1 "±ºÏ ~b\llg§YBÇÄ¢9ÿ¦'‚@Ø%›µÎBÂ=’Atp d?‘=¼ù²v­:€ 2ðják¸ðP–¸¤Ã|ù(Zí·¬w7`{Ö8ôÑ{Å9Ľêlªú®yèŠ=ò(ãŽkÞ³Þ隇3ŒÒY*AÕpÇýF¼žnEï7¦£ãfôNñ{ºl… —ä!ñз…MÁnžzüS³ž¶<ßjSò÷¨ÆvÚ{Üá•Ø{0t¾ø¨¿36EÄÂÞ˜l˜/¶ &2k–®eKÖÛoŽ-Ô©Ç^«,¨TœA,ÅsZuXÉ·ª´s¥>îÒG™ôj\¾û˜Ó¼žÜt‡Ô°zyoë5aÏf~2“w …Ÿ#Å쫸ðžQ!BŠ™ŒoÙÛ4À<ÉM—~íWÓo°ì2Å3Õ„‰…jH³ñÉ©P F´zÓZÎËó°ÂFƒîQxq'Щ›Ä)Þ¿¸ì zt÷`gô-v…‹ÊÔÏÄ©;²à¦Â Âî‡¹Š›é="\ SÂk HòØý}É~«uÃ\& ¥Ï¾c3¾D0Å9X×¶‡FÐïÀéWW9OîÜgÝnùþ@ ¯ˆû€HÞ cçŸìöC´ý‡'çfñJ­à‡Ã© cȃMŠÂD:÷3©¼¯P…!©Ž4/ä Þå™E¸Žœ³Vßð­$îöˆÑE·wÐ}+ëqPÝ™”9Bpž$Ýì$MùnÒŸ¨J. %?‚Oþý±d@Ä|ÐJ§ú¸ò Éœ6z¾o……db+÷„YäéÄê½rÇ‚´Ýþ%´»'’)qÓh s#DzŽ0sŠS1îÉâ¨O# ó¡2‰mñLŽà³*}HŽ•¬\+²‰þµØ&îSVD@Ægu&Áe;ðæßÀÏ7Cè~áù$«x9N\XKï®â¡1—É“>4 Õ*ò™¥ g@ùp;ˆ÷ˆRͯ±m$VýKÁë §’½¾:ðó‘#1=½lÛyˆÿ¹h楮{—϶pTATh q·¤ƒòQ mèâ^ 6°ñTrƒ¹^ÇÐiY<:©’ùMÚ„Íá§þ¦&ô¿üeÉ’0’}›ÉÖäÐÍg¿!ü=ÇvÕ½éÍsØkVáøãx)£Q¥ù`T>¸®„9+ÿc˜›¼Ò¥1»ª±Ùÿ*òÊœfIן3:î2ßwÅ’%Êupï` £Ê#ys?3ÕçqYÂLÚxv5»üò8>v‡qeu蛓÷ÄãëÛ#×^è~hMh^”èªÁi£\Î/רæÉëãXÔz\Ø[Æn:¦‚‹î'ñÛi­àú05‚Dެ&‡îÂ5áeˆ.Öt]š#~_Œ¤îÆR/]ô{—ˆ¦IX°$Ííg“ÍÕì›{çáMÁÁƒ«H ϲ6@Ÿ”Y^ãO˜mt‚„þšBÎt ³ÖÏ ³·F‘ûáKHèƒÇø.ÀOw¯î4CfÊÞR®KsS!‰éÒ‚§ÆRÐùªÓue©i'¨>ÁÍ‹òðÚYL™®KC_Á´ëÍÇ~Å+ƒWð ÷¬Ànçut Ž€¾ØD0ø¹Š:|âÅzM²ôÞtúF¤„¤uLÖy‚Õêa\ð¢ªwÅ#èøÐ Oà ]‚›¤°ø™"ýùÇ—9”.Gò¼Á9âèk}µz€1•chœÊ<4KÓ‹?ÁÀ÷"ónFØ»‚Åý¶üËÞÒUñ´`º-ì,ú}ÁŒ=uðÅy ŽUÏ Ž4 u„Âmê¨!´šØýц®ƒ ì¡¡Päÿý¡#CnÉ{‘ð=…Ì [~ªy?ƒÒB±C@æ¾ée­¶è÷ÏK¹ nU߆Á¡éG`ÖÖå(Sð…=ù.sõ4))®„þØ Ý?ƒžÎnaÂ÷hÃRå&›E[ÖÀš!$w}Áëɰ߸ ß, ál®cÍãÉx)9¦p=O0ÜËjÆŒ?9ðåwaÒýï§uãk:ž}lÆö"ŽÛSbpb&9,Eÿ¬,bÌʱ|¨Yk ©ùÓÚÕbìË^¡ÏnyØÆ>Ú`›šêØÿ²_|Ÿg’ºá®Á_ørÔ“Ê9VQ ×Zt±„]‰ù‡gãìš@0œôÔ•B?«#6tù_eBÄ”ñÊ”k0–ÖÃvîô"†YøÕ5uO¡s'¢ð Ñó¡Zˆ>(Q"Ù!~Š8ôs:@gëaœZ·ž|›eu{­X¿×U¤ë© IrɆWûÑG;˜À:Š’Þ9ÀCã5é6ŽÎ<òhä"Îã«·yP+M¾HÍ¢»w5O©®ÇGˆWÒúAð1L¬cDö82Bï.“»Ÿµq‡d;î÷æe#Ý"Qü¹"ªt³ü¨ž–ËÄ=ŸGk×ÒVòçvªžãóç’õïùà*ë@Õ`ºæL’Ú}ƒ;ÝSœ®Ç`á„, àÒûÔ]]C°ü­‹=)騧G¨æ„æ¥íFÙ†PŒuä”›à4ÿšì:Ôw|&¹ë wrô‰Q[$–—±Ùw§ã¢tNÛ# ˜ECªKÑùç^‚›ä•©uu{ê½"šÎÅØžüø‰ê´Ó¤g‡ Yâ£c…-&dÛóMì»5/˜wA³©ÛÙdüæ&[à {D‚Ü÷¹Ç¯0ËS1«IC˜_àqì xjL®…¹5ìÏ×Á°vM"[¦$E—„£Eä~rÃ÷«ëáäo°üÄüÀ*tî¼ÒŠc¦[º±ê‚M¬î£Æ¦í¦#0úò3ãŒ&Þ´ 6†U£$Oí…zñWZ9;lÊy}¦ˆ5wg¼ƒBðý9KâÝÿ–ƒEÉyvÇy~ :· .^žN×´}Å"ñðßÌ} ëjDZ|÷ ,@µ9EÌÌÕfäçËLãÍ\H)À-;޲mFTÒ6VmªÃ«és˜rò¸ñ=¶HØQF&b!ç˜} K4#B¶(œ‰üà"à«ËÊ™þ!Ðùw4Wi‘›‡Î ?&zy]»upÅ­g8L¯ ÊC0œ¬±é^g¡aÎHsÿÚ”Q¢Z¦ à•‡séƒ÷Ø[x3…מg“«™©[i©DQòNÆŽ˜14Ÿè†µ'„ÈF¹{°jª3†Ï[J”…2«•²0Ýa9ñçÉ·Ñ›Íè8]ÍùaØÈyÒCe‚@xhés_Oj“n=Tqë Í&î)™]ìÂÛÍ]Y6xóEvµÃo°: Qb̈ޗ$*[h—UptB —9¦¡¹$ í=¾æòªdµúvž©;‘Ú„¯¬É!¿µùµ–†2·{íÓÚàŽà”J'÷V°"ñ{ø<|Œi¬Y†<ódUó¿ãÂñ›ÌÁÞеOgdwÐÙGUáAõ^ú×Z› )9P£ñ`kt”3lßÉøs¾ìÄ ×,”­òÄAs²+K¦-¾yï9÷®‡GkhÆ °8Ü5|¡`† yZ øvdž—rl,ÌÙgþóèPï,ØkœÎíÝÆ‰.;‹ìãâ¼-„s<±õ»8^¯p#o.°ŠWxI\‰5nñUf–”-D»9+ÓU|£¹Š7Ïf ÷á =lg¶ÈðâHWNlnÀˆîT4i)#ô“îm"ä“oYó2ãÒÿå*/+þŒYv_0è°a;ÇæwCØÇƒ¸ö¼?c·ê ’‡ÔS¿+õmq_þJÊÒa­xe˜M|Óð}ƒ1šþ[H–£ßÂux”'v^e¸i¿EA%)ê?®š³_æ7¾ïÎÃÕ#Wñ¯N6S•ãN]„·Û(ghíqöàQr¸r¶÷ª·—mh;ÌAßul“y&ÜÍdîÕõrçK,A®r y¿CTWbÛ÷­x;…)7[8"¥¬×™ ì6Ç|]Ì5¢Åܸ¥K\Y.QSYÃìE>Izâï2rÇê1#¦úŽyשò§ß2ßNÍa6ßl©î·Ì9© vI¡£ Pr«yšJpŽÌT:G»ØfæÖ^\ÝÄK÷Mº¸mÞålî>3øÓrÎZ¿ÇÝ÷DiÂ4 вù±.ó=E éô&Ÿ»®úlS\…¾·æAü1Ybª£ ÓÎÛäýÉeß>Àx¹V<>Çr¼¯qâðK}s[‘+y6ÓµqÚÌÒY;i¼evߣê: -6ÛÍ”•WƒòHNß™Ù6§~†ƒó*yöòG~R^$M§OD²•ŽóàÉW!ò8è2~Î…Ùö’$ï¡)šæwRóù°Sf¨§è‘-£²Ìíý·™‹u*$K)›+½©=“c›fq¹Rrà‡Ñâf G¤-=Ð(d’¾ºÐL‡Ô p ÷—•ˆbVÉ)‰¸®M¿‰X˜±N’‡[ ¬E ª?l„ØôMàqn>ç?¶Ùpÿ;üì\´øÉôí8Ì.je¡ûÕEx–7.5ÿÁgI"SDÈ‚°Næo¢/aµ;Ø7W³½B¹ëêfSW/ºìíxšêLí§JŸ/1ÐÕÈ,ÕL$_ö‡Q™ nnYÄiøæÊ|²d‹“gÑ!u8xO]„÷¿0«[M>òta*ke]‡Œn3ë§FP‘Ù|¤!¾Ê/[±r“zÝà©)lä/¤àÓ6ëF%F¯ô*^^Ù/ZÙxÃ`Ü! FK£ÑOö÷ì!©ÒšÿeJsÛ–xÁ—Šû¬ë#Kòó\:.ïÄRZ(¸Ö¤›ca½þ½mÃÖ9¤pZ)§¥Ÿ‡R¡ã´éˆmžÑŒc»ƒÉÖCç dó*hÚ9ĸÕÌ$aw±«èïÚj6÷[4˜ækRã­ù˜˜¾=æØ +g|€É16­»Yf^‘.J¯g5åCñÎ=YâŸy"Ž{1ÛÎãÂÌxT÷íñÆs¾/q¹b8ŸüÄâ€iEúôGÑÒ»¦–yq¨vú Ožf,ÿ ¨\ØEzÙópTk˜íRYC=ô¶àª£Íðx£½#Ē૸¤%ê Ek ¸.U¤³ŽHQÙ¥G!ìˆ2çñÆxvzù6¶c…{!Q…‡žlÈà®9†º‰úÐ|ú ß<‡k—C¸ÈÜЪCö^ðãQ—Ø›¾cíŸÛpA~ã r fœuKÜ ÒÆOq~ò V¶ìgYn{É×}{Í4&B‹1}ÚGÒVë‘_j:´Èp?R¨†ï·à6¾ Ò¤Ûàç×ÉšÅ@š>e>Vì!×2pìÅRnåÈUwS⣲Ã}LËC"e8Ôd•=ù¼’¥¾‘üV•&OÖ“¤ïêôþîöú+j0[†pKeÈY¿mÄRw7ý³–«ÙAŸÝ¿MõsœÈ›Ý³IÌt{r[À^s"m"YX™<‚«¿¼‚¿'+HŸ†:¹g‚ •g胾Kp¬áicLh§c ¾ØV <Ÿ×0¥OWÑ1 LËo…Ruôä SÖ‚K¨ìþ<¶ÿÞ¸â=Ÿ\Ñ¥¨"7ùk§âº-q°Ç¶‘¥Éœ˜v0š|ލQO§P¬öÆß=+製+°,­ô¬?£cö "< "~jÒ'3œhsËsÆU¶vÜÉ,š»ܵ€ÊçÉõ%ÂO¤Çv³z‹˜õj:°¥rnøάl? ŽŸ¤èµKy0OC†æ¤Í§Ý¹L¾à š7Ø¡ÑK0?©nsþ0W™Óc†ñÜã’Zá3X„Sîci?\øõ!®«» ïÚC(zÅãƒç ;´–]ÞÆÈ B!yôµéü@È´Âd»hË%âýÕ¹Uì PÒ†#E¼6þÅìÍ‘´|ÐsFŽAÃñßDG{-Î7E=×Zl~ ´ú+;b}{Òãk°Ù®—Юå/Û"|.Äð_ S+]öWÄñÛ·Û òbtxW£äº;ùù|¨Å¥»`Ê9ßl“¡Ýë1H—¾bs3XT®§ÆÓlH‰ÒwÆýþsà;¯O¤Î¡óôU1å¹޳/†š£-6Q³ùÉʈ:]E'^;’ÃJÛÉLÅQvÅÕFˆÚ™eo˜'-SàÁzŸÿ˜gr-îJÎKCµEeðµU‚)é‹€¸ß+ÁÜ5 Ž Ÿ4Mžr Jnà¢a#šu-ŒÆ¿[N7Ü]Aoä’”„mÌûsøôÕ?Âìµß7†•¡ëä%¦Eø,G_ÂÆ"B×q—…,"Ss8°¡ø+éG‚õsðà>’ð@šô¿GþÔ­ôLøeà©þÈýíXϾ6 EÕGðéâ/æ?9ðHÜñ€­h¶%Çe¨U½ :jåØ, þýŸv0a»r|=ê‘g·"Õ9 ßÞøÁgm{2üü1ÜE ‚o8%W2¸~>tÛ.ƒÓ$é¾Íx[K˜Œ-7 ®—w£êcÂìq‡9Ýœ™Æ]Øq)ð?ΙÛ!lNNH"V(Œ¡~/àë¥&°¿ ¯?Ï$A.`¸á8eÒõ&u_(L¯˜Fü.”K-¡§O\¦­®'˜/xBü n{(L.Þ;Ç•=Áÿ{°ðI,.ŽÈ†ÜÏ…ôþçóðßqóR²àj ñQP ÛˆãòÁslûƘ%‚Óá‚êA8±…Cü¾ÍAƒs'éßø $åo75ûû>o¥ú¾cÀQ$â¿Ñ¥uj”s4û6TsghÉáÉ_3@±êÚx ¬bfÉáÓÔW¯•XžúÇkçPõîÜ}ùèÔÚÑ«ñ7¡¸p#)†EvøÒã \7üÝ©zè  |;„Áÿ`;dîDïBc<»¯Óž‰¾IÆÌeìÎ?à¼kôÄÛÕØï*ñdÀí |¨6b4–F‹e{ÉÛ;Òÿ@›¶ ;>Ô ‹OUcÛŸ¬ž|&ûeúF`îòóë Æ|n$ Íߢ<(ΠÛùaëNAú¬ÚT½sÉM¸b¯9<‡Ý¼ÿWN‹Pþœ“à±³b>Ãæ ¨ýEŸi²«›`®Æ-Xãˆåϯ0SÞˆé¹+©ÓutäWÃ+ã#ì×2ˆüð„™q°Ý_XÀª¾PVJ’—¶V‚kœø×¿Àï¬ò·á%!Zá‰Ybâ:RÎ._?Ž?/'ÎGžBÖìRìÊÀéå˜VÓŽÑËàÅî•`¨QžÓû%± ½Ú$Ýhc8¥÷™â}ÏБ'M“cCˆ*lÔ>À¾ÿf>’pb– â³å4£P<Ͷ‚ÜÄr´4 ƒ¡ø-œw릲BQpnbMÝ†ÌÆ_+ƒáfE+Ÿ-MÕÌâzf¨Ð¤ÛÂÄÙq#1Aì¢ÓH{­)Øùßh +Êeõs1ÙðþX0©yúT˜½NSˆ×޹Ð$âCLÓya{ßZ’‰x÷j ×Õ‰jÓ™7²¶†æxìå)01ãá äõâ½qM¢î‰ÙͨQµ kÌ:ðÉŽDèq·Ån“pª^.Œ/>†AÝ€*µ0*Ä^1êøS÷,ä%ÆG6RÕǃM/föÒÿò¦”|Ì¡UV$¦ê 9Ü6›.½Ô U<ÅpÎežœÔ.η§â̲„H4)]Ÿ :îS(ß»— ^Á<ûdBëD=pîM1HµËÇ·Ó8 óú:ì¹Ê>ï a%#½±Ùq3{àù¹ü>>ÓógÉ¢WÜe_.sw.½ ºE@{â*ý ûZ2È (à8gß«UT}8þ4³¤×¢yÒ"$&O1Çx)=¤üª–‚ý»{ðÐQÚúô ‡Ÿo=±\t jõ©‹n4tƒ÷‹fC²·.~ 6#k=õa:³®XˆµP'¿O¥àó_jpv^¤ºÅ€®s?{¬W‘»¢¹fý^´öLÖøCöØ× O§®ÁíǹbEZý}ä+˜½Š­¾;­Ù\êÚ¤¶»…ƒÿqÍܵ´·-QeŒ´Ž@×ÒÅ(S¡ ¦O³á_„;<ü1ÂZ„½bÊXc¶b»4éÆ:(̆«…¶è‘°ò>úÁÓ _!îürŒehŸ;ýÓø“=¬oÅœ%U:¸gñ¸Ñ1ÎÄø,aÛzp‘RÓa=‡ûÆÿò¦]\Œ¥×¦áý±x0ï6öU‚#;œxØ‚´.s›~Ä1_¯…Ö»¦6eõäóiï'Q¢î¦Fž{ša “ÂñI5$f‹v¢Úmšaž{ Œ°tÃ%XaTÀ™ºl:í˜) B)‡aì€~®½eÍ[ÙOÙÓ!ÃàÌw ÇëT1ÝÜÝ©¾î{´5ÖdˈÒP¥&¦ñÙûϤbÙ†œbÑç2Þµ!…K6€NG<œôÉÄ-ª´¾Ô~n'ó$Ϭ…ÄnP”´ðG@Sç>ü¹2ŠdGûX7N¹Ï$­XŠÚÂx¢Ìì˜y §jç±¥{æÐ7Ÿ²öÙL–ý)òι–Ý·ô[¯ârq¾Ÿ:}†ÕžQtMÇ%¸í‡ËD^ g}!4ž< ëþª°ê_ ¿¥Ôô©Åì*øåE6†0ÈþiVÓšÆj,ãe›o†1ÖËÀcš,TéîÃð×»à«À8ô,“$›V%³Ì‚Q×}£ºYßoKAæ£'=Ù8•ô¼ˆ¢Íõרi‰……ˆÓ½¹(Ðð N¶ŸdïÍÍ—÷n_3g©3ø ƒëØ9 G¹øó± •ô ¶èsCí@tË>Fu—=9yËŸö”¤Ò}îÑT´Y›”ÿ½‰}U?Øí?¬ègPý/g™¶ý9Ÿ}BËáu´n²w=rŽ!¿þ ãmE)œrë/ êOckM§G«=ä]À9ìB`½­ õ«I k**ؤîSäÑb-NóáHV®´º>]§3Ò¥Aj“Ýè{„íž¹ÔŠƒÑ«g=íy¹šþ—ͼóe=–ÞïÅ_cÑÊ>=Q’=¸…-à%!<äÊÊ4é¨7°¥Œôø´áÒµ]¤ü3™òõ ñëì ÆWÒQXÚ±µøþ€½Ï',Ô+†…²áxEk2MpªÐzb¦˜×±ŸwQï° ô¨ù‘G9¤¹im^q…}kCEø–’'«C©ÚYÛ}‰†˜5Õ«£ÔÉí[3È.N&ÌY¼™æì¶&gNV“;þÖКÍç¶Ýg}å€Ù˜íúø«í—G^Wͤ _·Ð^±úLfýð NU, ÃÕ“ÆÕº˜4²ô¶ev\5's[=Èß%³Z|¢7ÜùîÌ«6^bo^F#ç´ƒùð:û…@K^±8^ëÛEš¦ZÇîqtÖ £ÕSI\H·'лÚÊ´x:.’§»óvÃÒÆ›X»ŒÚ\}† N÷ÁiIÚ_´‰Z´ù‘kÑ ‰½°r.wÙôS`³û(z%ñ‘Ü·*ì—ð?ð%s·;߇·}ÒGvÝ|^zÍåìy‚ŸŒ—#g /ñÚÙ‡!Iיܿ×qÇæÇ îÇbô‡ï1˜£2…¦ §àÛ€ãðÔW%ë×Q%¤W•ØŸ<Þ„¿% Ø”ãô€D™0õÂO›±Ù9³Òx‰œˆ\ €ÐÎóà½<3 ÐæÔso˜F•«ŒÀåì.ܸøž÷mdãÓqÔØÏá„ïÀ„ãÕ¸¿N>®n`V˦½Ìb®ŽI’ÂØtiëAúë 3=8-O1w䀟O1µ¿$F»Œ€Ë€’² tø`•ŸQ!‰.ø†ÖеA!¤à«ýo¼ê™4î6ûOœgá†äð«éDzÑœ.8¨ÊT?r' ç_Ñ)¯àÃ?'©ëBQZRu€<õ¶&)ú9Uù‘ÄúNr|u4FÅ"›^&×OË‘åwré—wvØlÐ…‚n©Øå³ßdÁÙ/;hOè4úÚ[‰Š” 1»£¬m*ßzáü¾j¶ó¨©i~ĘªKÓkÿaó<í¯†~Ã'ÔÏȆþ‘y‹aß-i‘}MØ A–¿ ò…ÇÈ¢í­ÜÔÂ.ðš÷š}‘†¥<ÅœöuÒôÝâ…d|¦2ùêåÅJ£ŽwÐmkÀµ%_8­»œP\8Ž$YÀŽï!†¢û©­G›"¿˜þ›õyo¨DõžÄ©½+AÈ«ˆtÛÝãÔIž†,$b‘îŒ´ì ¸m 7¥óñð5GòÚçÛì#…g¨ËV<ÕŸý×ß“CùÇ`îÍŸñ ï|ðÓžìÛ{‘ŒÕÎ¥I|ÉÜFæ¼õ#κJXõtÕ§ú“ëwgŽ¢L8Š´$Ñov›ÀúÖM|`’I³KÛpz;D æá'ó†/N€Þý¼t{$H]ýI¶Ëi }bnõÇVÀŽ›üìú^S4W^ÌÄï»Éò®^IýËኖ4(²X~¯åÄ«8û¦Z’ WÜH¯²YG“˜ÖˆKè$•…Ýs: ü@?èÖôâzž‹ÌÐå@f½[3Ú–­È ÑÌë“OðJÔªó§n«Cdcs殸 e9kð™ÍÞl :ŒáÇÝ>\.ÑÊè8—½õ%Xtôyóå )Î…†#)ðd­‘õ Øëö¼9ýÔu²´@‹{ü˜8ö½…ãê¶ôY¸'­a†·4äèùòŒ3ßlrد¦*üF£"CúX§ERŠéª ELl¤‚s;¸/>o'<º/Ù]Ý!¹ôKy‰L ÝÄw™œ[nIåÚL‰˜… Ñö&G6W¡V¢ ,ÝËwÓ;g´°V3/ ŠsÔ¹æ}%Ray~YDžŒYmÊÏs—c¤µh.Õ°9ÌðTÅ“2=!ÚRñ¥Yÿúev*ûù~`=V½]ËYv˜¬ZU…·æ¡}®m=”ƒi{ß3¢ƒòhr. >-#r¡fÄÉö«íqW¦™âþƒá¨üÓœ2‹ùáüwøz\-xHšÍ?ö¡›]8*N¦œfŸÌ|…éÂûÉ£ŠED¹Ô˜{ö§$¹î͇ͧûíËé¢Ëã0»0ÝŒùɧš½ÌÝ'žl¢¥ÎUêøŠ·tƒfþ2Vœ/‘«ÓmGK¥à‡rÕÖ5ÁULþÈl˜âª@Š4ðò÷¸âV‹³t•AMG67„ƒQ“*xPÍ(›[Ò?K×ÞÖ`ª¶W›8‹¡‚¬:ý+øŠU=õˆõ_*J¤$ ÉÊÃdõ=[t”6¤RǯCÐna¼6§æ?zŠïbð£÷iØ-<‡úO“–ÜæaúH0…3j±æ¼ÎTçàX¾÷,ŠZïÂŒPiÇ{4ع¡N¦t™Rgr›ê_Œf¶Á*ºþü4²c•6U÷¢!³ ý²‡‡ÿÖ€Pn*=^Ô`g<-¶ŸJ°±^.Ó¤ fœÜ×€ ¯+AAö2:/_Þwñ Ü€înjÚ§AOE}ãöƒlø±¬Õ;kÙJß¾ë‡3/ pšîArù¢,í¾¡BûM!ål:õ[zƒ¥å‰Î¦J0®z‚i ªÌðð[ü“º‹|È»ÅûOÑ6_òïí?òî¶o¡Sw6‚ÊqŒÕð§uëÅÈÇàvÞêHžèÝguÎ}ÿ )wž,f¾³±yûZ’ åPío ðD,Œ›7ÃÕ-g ¯ 8Ý'VE7”T4Ÿ¡{N…Qï‘ô5Ÿ-?-A•d¥×b"·o5ØåCÖë ‡VÁ²+T£ÄЍïøÀ˜?¬õp¡JÏé½zeÝèÿþ‡í™~K-aÌIW¢—“ÇýX»}Xƒâ‹ì¢8$öeãþ•÷ØÕ^XòÛ]Y#LC£ÌèÌ­ÿ0зJž!ÖwzÉÎg¸hi‰ã—yèÅs¨þê¿ ¾-®¹§0ßÎ$%/c™ù90–ÑJš{Üq¿ÌIî–‰iÜŒ~23Ã4Gú‘8ÿ‚åÎ , ›O×yjÓ£êQÌuÙTrçß?αK›aêâR:&ØŠí&»q«›5YÒÐ&ô Ja=+UënF·éÊ`ãv^ÌË]5¢tÚiÐS^NÙW™+Žº ºé#k(yæÅ$ ©•™[Eñq‹/ž“á aDZîEtmÁé~ôC‘^[cHÆTpÛv ’’¿º(üƒlÛÖ€¿÷ŠÒ„rq²Qò DÝÆ¶\[÷^kãôÀw?Qaƒ)sÓìÅ–f¼sFœ‡×Á´µcð¥h3-†œ] P“Îá-d`—ux‹U§[Pɹ 5OÒŠÄFLßóˆk¿Ð<}“4¸_ ƽƒÑòO Ä9X²*g“]/÷#¦Õ¯ó¹+þ,£íïqúûÛ0ÿ\ ØŸÔÀ'I_8d$‘Õ}ª†ož±?áÆŸøÆ^œ=+Û#Ùѵ¯ÍeâpQÑ„Løð“l…vÐÔ0bôÚ½¨Ë|Ò÷kák÷AKÊV¯MnˆçÁа2)®îƒ³J½HÕ<™ÝF÷û7³ðNCy§½õx÷anü*Æýq%þ‘ФŸg® ïÛƒÀU#w=?Ã*‡~À›ÈÎ/0$oE'½–K q¼cL­¾e’¯Ý÷ØšrDH¾Þä1sf¤Àžð>Â#Æ!Oð\ÐeìÉŽ…Ûš§é >3âã°‘þßJ›äNÃßHÚúî9žúšÌ ±3ñ¡¾/5É÷ͽ¸?æ üé Qš‘K!s; Ïò5-†ô²¦ÿ O2œéœ×„7„igw6D!ì¼f‡W§Öá†ÍS©›^.fĨ“Þa:k—éüÌšÄâ4ÞZxì¦ãÇR!žëÆ2šz(Ť-Í"9‡XÇŒuGc¼ÜPZ½ Ô·aî•°êÉZ’× ‘ŠTÊU‡Œñ"Uó鉰y³>>Yí…?/’ïË/ƒ>Î'‹¼6bý¸¾lÇâà4Öív êÍ©•=ü¸‡/ÚK ²ÞUÐÛ¹ «¤0w¥1JŽÃ{ 6Á\#8 4>‰Ý1ºìG)ç!8ý &ÿRjÐÝᆊúBŜ׌Ůt¬ x…Ò[3©/xÔÙµž 5;lŸ‹§ºÌ‰¦ÀQ¨æ'"y˜µ…§ r† óT¾šÚõ¤jÊôÐ% pšÚ Æ_ q}$œÍ'W˜í¸àG„üv$vulä­OÈl9 ‹Ã"èåoZ¦ÀvzÖ¢_ 0ü$N~å¾gCI‘r~ðkeÅH“E'²Õs̨Vá5ˆÏW„ÂéOQí±>´ŸßE»w¡ÿ”X²è šÍ”¯%«9ßлÿs´¹ƒ)q Äÿ¤T ÉÔ›’䈉% ¢5¿{àT9Ü`<,§W˜E‹·Áã×—˜UwÐöî©æÇâ^ƒàžìJ—_¥šÛ™Îã: ]ð·w`ë§‹xÛ¯”\)E­ûqS³8áyI¹·B$È’ÓJ$|Ö=Xytoñã*¼äôáÈî-BWcæˆÁlښ̦»é?k@Cõnf‡†1LH=b[Eý™,e{¢Ñ’ˆ•gœèîcý¸·UŸý?Ž®;Ëïgde”½÷e$#Þç>fɈ´$4”´Ó.eï(‘½*ŠŠ”xŸûDDSHC)-£(-íŸïï÷zžë¼çœû~Îsîs>×õ¹î'±ô Ùtjj¥ðìÔ»°Ö:Fr™gþrt²·¤1¯Ùj¸²ª³IÌî  ªHoOslîm`üVBEó:26_aòþ¸4KТh3¼#Ó<5—á«ÿ6íƒ_Ïã…÷‡Q¸à$¦½«„K³YÔTÞ>6A{ÿ²í©âÄ{ÿ{ôŸeLâºrX¿£ g£c8ÍŸŒ¥U@—Ç_&M§ÌR·à&‘z èxÍTsÖá²6åÙzFGlÖøFÑ`>ø$¸¾¾%³Ó ˜Ê)ð¬ê0&Gy£ÚaxôÕ7ZÚ‡ÛnÂùÖÐqŸùxSâç@ž(®¶£>6“çL÷IÆye%£ZðÆÜÓèìâpÛO³ºAapÊJ›ž‰•³¾àŬAœ}_È¢IÈ_Ì9ÿßOˆ«Ù?X«7~Ì--L¥·±3¶¢Oà¾ÿÆA½h(úüJ‚ ^%ýe?{0ñ½èÍrØâU'A7²#x²{ŸNC±ò|\Ü(IÍ<Ñh¢ìtbØ¥QךN¨oDSøÎ¬Ümûªn1"Ýœú-çØò—ÉãÔ ºLN3Z¥9ÿjv9¸iä#ëÒØÉŒ2CÕ¾åäéÁbèõR‡OÓ™G ÙpýѦ7Â3™-§Añà|Z›xßÄà%écXúö-ôG^€„h°p­ÁÀîƒtóª5¯ gjó ¢ÃtíÁ÷C¢ì—ü,йüúvuÂÉ'3q{^1\мÃð=´§¯æ†‘3sHÿPÝWéÁætÝ5Fb›^“îÁÜ|yrG„\H!ˆÃµ­û1$ä"òüP¯w@rM53jº”ñ¬¨fƒƒs1ðsyI*ˆŸ±%ˆ±aEƒÚÑØ¬”+˜ùNz/ v.Ф÷æl"“u\­€ë9¡Ø±<]3%àaè]ü-¡ ón©AÔ‚Ëp^%ù[ën·gÐŒDI‘UÒËìV} &3²ªØ}ülÓ,½ù¼Ì€õX6ÎM=Üdö’eãvïÁ8ñ×p“Nƒ°é˨-— 3ÊViòРáróB6:­¢¦‘аa}8í¹Br?aÐp;:å:‚í]d[´Ø[H•ªð»Ž¶ìà&¨¸ƒ5Sø ¬ÝN‹=‰ßðá›ûr´îD-¼>ÅKûrC`Bó2šîËU½P4337Ö™Mªñ{YéëxpßTÜP’Aâ–äcX•68Í]Èœ‹ ³Ùi›ßrÚ?2XÓ¸åìÃéçp 86þ[If´¸’ AY`¤w™#ög=|u/0FÛ!€1'ñ5Ðöj-„g±ó?ãZÞ‡Mnª+i¯oß=Î^I» zg p†z"8‰ OãÀ JÖhvfjDÑõI­8õ¥üš¿f¹I%ƒ¹w=pE„^¸×o_ü4–ÍÖ¡“ÿ3ûoñâÖ˜0Xí¼ SƒÄdÜÇ™ßð›.o…GóoÁÞð8ê5|]€s˜ö÷c$1} Idý™·ñhY¼røÇ( ÖL‡ÔE1Ü)eÜÞP'xÿqm!q³óANé<#›–ÅŽÏ&SîI‘oÛP¡ã¨)­J·y‡G§€³V™ÝûÐî-éFKÁÈ9{ã¨m»S»;— Ù¶”á~/ÝEË ¢tZ§¥©{^°Ÿ…¥ÈÌý%,£{Wõ-…×ófÇ2TJ_šÖ™­F£4|—3Ÿ-³â2Gímˆ FdœË*™’œ µ³%‰ò¹¹¤ìÁyXÚòšsLâ9ξ̜-­…Qù$r¨w;½å‡ ñ9äzÈUfØÙD ¤í7ðå¢Dh5ÌÄ«cW1ý]$ÛÐô”¹ç¸>ŸD»­— îç ¾™…[ú… ï·¹(s¢-sÉ9+}ò›?’¼‰ÏÄO¤Ù™ççŠÚkñkSqWùV°ùw^¶òÒgïN¡¢%?ÕH´adºÓ{rÙе6‰½YNM›³àôëb5cϦ[1!1S07_È•²Y^ø«O=ãü8†ª"oØ}Xpú|­7„ù™°JD_\óÀ½ –ÓÈÙõ ÔôklâX{™:, — ñvúDºz•çÔÂ~[ŠÍ‡Øù»¹ŒÉú« `³Ž\’¦Éç"±®—TÞŠ;¾ÎÁb7.ô=\ÉøØŒ§×éˉ£¬Óó'À®¬‡R ò1,€èí¦ë×^ƒNÿ6ÆšœeVîø+~È v]œ<JGýXó{Õ(3]“¦¿rÁ†Ø÷ØXŠ êÀ}L“1¹è‡ÞÅ'9mrêÄ7-ŠÒ¨eô|$e÷dVqjf~dÛ[<á×Ò8Ød{ÜÂ4)¯@{Ë]vü“<¨òð`°˜ùýR™"/ÈØÆ2™»ãÙÆËñÑœu¤^9Ÿ™nf%ö‚ƒƒ6­úÅnèŽP{ÅN©“úèlFJMŠ.ú#@W¨DO×*ŽªÆ~µo·Iý/`©ÜˆVã×GŒ¨ÿvÓ¾Xß~ä·2ÜtAr̘RÎÁEbTê³=W°®_øŽ§ÖO¥¯âvƒ‘¶ ld†ùfA¿âc·Cb»½á¸ËY¼VÍ K#UÉï¥$Y>ÿZœƒ7m¸¨Žk¦.XÉsÅ>¢×“¸®¾O¤Cá‘à~ kÉ« |$>×À¾ôLøìKe"9}ëØ5m(èUˆßvžÀ1{Že9r·7;ž¯.¸=½:{¢Úe ZUu8©ÒdAƒš *ÒˆÒYÜÍ¿Ý!©ÁžŽ/a—+³~úKñ³F&^ ˆf¥-º¡Ë°+ùUA‹Jx¦÷q=WGá8ïtÆþüJR·VœÖÞ—†•z›±äÈyˆú+Lóݰèù;È/;r›ËÐéÈ)Ž•ê5øÃÑ€Œ×•°­ÿ÷·ú-ìWò$î2I¸aÅ*2ßr:áÎ1ºÀ%›ãÁðØyÆø–<“Ëã̾¯××ñlpj\¨Ã{CZ±S•´ìz ›£z·=®fúbHü·þÄ”¹lÓšrÜÓ²—š`³‡¾=¥ÊdóS“¾ž¦ÃúVhõX‰p(DL¼y£TæÊë2Ò»c7­ýŠay_¾A¯ +P8s€ÝÏ3ÈXOã²³…é¤=j¹É–Œq™[‰O Ëp&YÿqH -ÇE = $ü2Üp®áÈeeo&0ÙO62Ú~çñÒ³fÃÚ¾>üN3Èhô ¢œ±’Þ˜„ß#§qûÝûªèLƒ66ÀÓÝêÔ×V–4nˆ`ÜK¶ƒCïe|9×RÃ:QqÅ𵚄²ê÷Œê”_Üw\†ÄùõôÛH±ÁðL5äf¿ÄU_Ù-¸ì†1)nƱ7™µz.ðêöü†¨u:–‹`Ýš8rêÜ8W,o-ò¬%% õS–Ì0=·-x©„ö àýQ¿Ÿ­BË[¨}¥A*ÎLaï)N¥ÛÃQ`¡#›°† X"Bî‡Òò×a·ƒ%³â{F߉§§¤Ž“9—]‰ºÞ<è[¼ŒÝXíO„NTÃrÓéð"?úhfÞ_¬M^ûhÐ¥©;À\«˜ÔKnÄ•ˆÕ‘šœŽL¥û:½àëî'y÷= ¼P¤:¶áyéð&ÞF\§âCwNüÃÑ]E8â­ šÏ®q“n‹Ðs‡$ÙÄ/¯à¼ôöÅ)”k°š´5$÷ÐÝ«øíãâ þ<u¯‰›÷W%Š‘8û¥tû]¨‰â~[x êKWÑàêVr8ö$˜ÎwÄÝ¡ì_)úgª*•áÍ઺ISÑ~úLX„®„úÙd)ís#²O@ø…..9ľ±“áÖ ³W–X2"éñà²K=.ŸdÉ…Nöٹ̟/Îc©­ùÍÊÝÅ¥q1ŒUb"tÄÎb"–ਰ4=àn_ÇÞ“Xö¬ôdŒhɽ(èow€]÷bÀú_0õÖ©iZ2ìƒàÛ^¨oAqV7¤á äÚ1*o`¸ã´öÄßý±LêŠQ¦úð®Ã/6wþ%Ìÿòž»Ä 9¹ÓÄͶpBúÞ”îf s[ØVY&ö;/L§~$µgtŽôs¹†´õT/k“I÷¯p#—GsP²d~L‡Øö"8ÿøΜĽÞ_`ô>…“‘‘xÁ­us®0ºsû×M¥—ÿðU¯¨•äËÖíI¥n[áÅ•#„~hGíh"¼SŽ\ð¼Ë±/™K×n!ù(³£m»³™ô%ë_ÿžMì ü|àëÊ÷Ð)2W­ßNÊm"¡¹²äçã5è+4Š—ülàö‘(ÜóMÍ8îΙ2÷,¾àùÅŽ*%‘àk*/ú‘q~£N4îñR?Ïhº`Y!ù0ªLç$+SÏ ‹ðKd6Yû.›­ÈÈâ~ÓB§sŠÄâiÙ–ž½†Gaî^]z}ÛY·)Q› øÇ]d_>aÄÛ]ÉßõÒTï˜6qéahÂ.[ª÷vM‘žIWXÄ6É?¯€c°y’¯öžXŒí’Þ´aNìì‰fwîn„îtQc-å=”Nª?Æ1¢CÓðÕ[^Œ±É¥\·vF³#‚pn=ƒn³8p }Ñ ñý6S¼qÛ‰]&SÛ˜ Éþë™Áj]héã’W¿rhmÀÇÉ|¥MBg“H/ª¾Þ˜ mGý`×ý×`lº’,™~ÚLýIÑG0:EðžO`ödDàÝzQ*¼~Ü,d#:ÔX1¸Z¿4H_à}ft_Ò92äy³¼óQ„Sa5¯(³ç‹1 ¶O8SµÅûÐt¥5iÜöƒõ™– Ë%£éHíRª5‚— „‰Á—V¦ã¨6™/;…x+=ƒTËúÆ{ý‰Û{Œ½ÿ™r:®8¯§{ˈ¬€S±0’^žÛÉNy+ƒ?ôÓ«›#@þ¡-9®ƒlÜgLÅ_–ãÌ»‰d¬™=!NZ†*i~{v¹°¨ðç._\F»™bpý7ÌZqþ³w‘ýµ,’ôº”â²Ò4Ø Ñ ¨{7Ùûm>µ¼^‰ñ[=Ioú]ü,ò7˜"Ý+’Áeü0î1¿[’‚c¬XÅ æÐ™D”x<ŽwÍ—Ð[yÇaÕ'%ê«,ÄŽ«×Ö‹0§ç(± °!†G¢¤^ÁÃVXø–ÎäÁy¢=°–eW,˜Nâ1±~ì2,oJDÿ[eìh¤]/1çÁqVlm…ƒOî6=9ôôÂJá¥IôNr’Ÿ]g!Ãvó\?f‘´'ýV·ŽjûÒ€,RqaÈ%m`$ü¯@¨ž9ü á2+;/à·ÙUØw$ˆÞù¥j"ë1!§üÁ>"ïž[ÿvñ’³n—ðSÖc0Éà”¹€÷íXV´­â_U@ÞèA,Ú4ˆÊý¸ññvˆ| •O@Ÿ·MäïÅ€tÞ½x¯ObÊ,?¬zuƒaâÁ%¢ªétÖréÓ,´½³€,ó«fPr.ü>C—楡K] >i .L¤WÜ~ptO ‰îVÀî½Äô¹*Þr?Äz°§§ >âÈ`%^¶J£?¥ìÕ¯Mlâh,>1³o:ñvû°'Fº —žwX¦>Ž+æNþš‹q_ßCæ@L-.å†3»?襳s9;?L/ÃÂíØt9 ÿ¶)ÃâSS°3CšSówuÍ 6ú)Ln_"l? ï¼K´ÌøÎ}SðÕ²|Ù)’" –,Kœ$Ž=0”Ø‹£óà»ú4ô Ø‹‹ÑµdÏÔÙôGÎFâ~§ÓÎñ +ÕLá!â×aeç5†º_Á£‚§á¡ 7.”$Våe4Áøó<%‰õ–x‰…~exÐ]‚s¤–Ïðá÷v½ÁôšÒÝÃ4µ.d¡óÜš%Ân¶L"rÇNCÎÉ8l¥/K(7(‚^39‰ %úÔTK '2­iÊ·¨ðQŠ&­óá^Næ'A7ëè*µ˜[~ ‡Íbè=ÝÅÄòw/˜¥Š‘P./\ÚÙm☕K¾ÈÞÇ’;•h’™‚fê?¡÷™ ¤ør8^„<âµ'²ËÈÅÓ©˜I)ÕñþÃF>˜FLŽ2UN¿¯1¸¼Ú€.sb¨ÏœghÜ‚x9©­gCäÇóÐQÖôëÆrú¬¹Úc%5ŠPuð.šxeb7 Ïç똚G[h*¡b±LÎ .¬¹Ž™U”éÜW†‚'N±ÉŸÊà¿%Ì»ð÷ÄYÓ’Æ_lã6ð:¯GSpíI9r£ø/Ï4_<Ëüø¡DÞD¦:܇ OI™ÐV…MF@?jxS+³³t_2Upq¥øzèàv6.Fž+8;›4,($ƒé¿c– Ï§Cts¶“uê¼tDê7wÃ.Â?֎׿¬&Ûm¦Ÿ7ÛÑeúÅô¨Ñëfe¸õ}Ý`N—<. wÎN!+”4p¡ñ·ÃI5‡Ÿ*|c·wX°YååX2û0yŸk‹_宀§¹:ÎOœ€æ]ü0wy ÷jc zQ‚fúŠ¡¤'yà0€ë¥§¡w ¼¬ÞDoÌÝI•3â?‹™Ã2‘Ô{ß´–^M/éÅ"'C…OqxðßWVéà´Ë¸Ã¸ü+„ŠÇ6Ñà ȉ Çò®³1¡ ¤!ƒÇ×ËZ㋚idäu3ÚKð“u|#hñܸSlÈs>gR¦q[€Û –U;Pÿ²á$ÏïÁJãéä}ì1)1‡¾9¸LE=éËÙýL+³—nŠªÃð6+òNHàŤ]ÜË–a-y·sØéé"Ͼ8ÜkÁûGpÛ6.ß3˜Ðµ$~xñ€ãÀsÕšÉÿбÕî¿§«h·ÝuÎÌ7”oû†gí4£¢›¬ aæ=˜¾ÿ!8–'2 .šáÔ¬bp<‡Ñ»É»5¶ôxÕZæ¦as0(lççAÓýy £·žìŒ> ‡Çጸ6–æ«‘Ý·ƒÉUÍ»¬‚±{IN…særóðÃ+¶Cƒ.®ÁÙºÆÃ—Þ:)EŽûÞbºðÑCo9ôÏšX,‘Ïì·nA•ßáux6ù1™÷…ŒÄÈ-gõÉ=ø W…bôa ÚÁ_ˆåƒL¦.ØðÛl†g=T6áÏo–´îºùþ8ô3_²<Š{àÓÒsì“Ü·,ÿ›9ˆGW õ73Ú^{rÎh)…Y쎺 X¾Ww¹.Àù3‡0¢h#=Yþ¤}V3ï¶^çZLL%¼6þö"—ÛÑÄOä-ð%ׄ/‚÷Ìgèê,KV[l¡Ñ=»pù•»Ð=ÓlÎkaoïKùaÊò'<@ó?çáëÇA¼IÆpOÉ P+rÇl)’£$‡àXq=ÇÞ8.EŠSŽùQª¾ó3ŸSÀT.7„ ¨PØâ˜Á–óì‚OÙIìŸ##Ü f¸÷‡9ªÖ¨Ò„|~ân-JrÕ©jˆ8·Î¥Ÿ£½°=k·®.ÿd"¾}áFì7¦[sˆ{Né?fHßõDphBoã¿T-i uùuˆ,ýs‘{§[…2dÉ[_ÜÅ/K/-À¶§‰ßZqÚ[mH”Õ+H¨eQæó :ЂË-ºÙ×ßvàÞÁ”¶Ò;õn„žÑ kŸi’›“òÏEÛóîbDöà¦J:å ‰WÎ;pŸ©C§ˆÜ…:^oòÎí5hÆmU5P$bLt”G…DÑ.™Jï¦ ¿&!i5N=DNÝS':<ä1»‡T/˜@±Mñ0Cúˆ¼éqݧøßxqebõLˆ^®·§_m:wª‘vó¨ès˜><ÀÝ× „‹¬Ô`ä7÷˜PÞÞv³„1ò¸õ ùïŸeÔ—í¥;VÄCî¼›¤ø{üÝÕÄæ-%e%yôAZê•‚©6æõÂÔþýô„})9é箽‡éz?aÈ™RˆŒùÅ4Dòz<šuyñ·U² ?¾a®£!$Żҟ ´ÿì(ª8f“´¶ÄÓÐr!f²äm„-~j¤g¬ŸU´ž˜™;Æ* PUÏû° ºœüÞ Lªe÷ ÙE´óI"]?‘Lï̶§óR’]-(CypQÞAöìOvãád×&æG_)|é^ÉyçM¦¨¾Na¸iøšUâÝN½5èíI\£óc>}µ=Œÿ¶ƒÀãRäúyz–' ß|{ò…ªu;ŸòŽÂ©A x,F–Ë41Ϥ ɛϰvlȆ “²_,tiØ“ élÁÞí8ô'ŸÇbë­XòñLµ#£X”\5ßcðbX>SÇ·Íáã÷hö˃`V‘¥|Õ{áHÞgô=ùR®žâüY¤Ec.?aêËqn§-½ª-Jw/¾±\[Ö8hŒ-ß¼‘v‹íd.¿Î‡ôSX%‹›¾irž›O!Á¢—YñØÃlO^]‰ghÞÝyŸ®:/Ò¸‡Œçÿ‹8àk!>™FЭÙ¾õ˜þÛQ4»0ÙE¾Pxk>löý»7[aËÚPÞ?›ìõ?O×m]CãxEÚï^…GQÎé/'|¼‰yûä&Þû‚Lm&/V|‰« nd6*UÕ1í»EèR^ÖóöL ©a¦MfÄ5}0Pð1Ó*ïÂÊ*áf ªðâ 4%S±®ØŒ’Ýa“ܧ=™îP[NäœóúYðôìo¸®çEn:ã¢HMböõgN\/{†ß˜½YxÛZ#H”,NÿvÓUÛ iJ(} žÄ4¿Ú‚S°¬[ TyHPÄîÚwÓ©za«qtgæÒó‚ Ø>æCEOÝæœOÎgúöÀõJelΨŦk'I¥À öâÝŸ¸ÖáYöá&Øúecá“z¶ao,-6¢ñv¡„ûY‹¬n…‘¬%„çÛVHø£OûT…Á&“Ý 0\~‡\ÿ½„¸¿3·ºù#Èüt"g¬MÙoQö¤$,•úiÕ‚ó}97b_í {e &f·àœ[gXêU¢N§nú†‡T´È}¾Íµ€mvÍÌËï¸Åx ³öÝQ2ô'þ&ÜáÎé0@Éõïaõm7rbY:×™¬ÐK$Y#P鳯^ä_ò%“ã@iC(5M*Þ—è­ÄPƒyúôSÆ4úHI½Zû¿æL͹Gž8D\šÄw‰}àqzâ“8ñr‰"ܽkYG~‡µæðþÞ{¦&ç6 ÿ*|]sÙnj-@Ø–iOp{c49<mxhx‘5Ì•'þ:°Nªùøˆøs"¿XN¹±Ö^ÌÛœú÷l ß\@õÑ8øÍ´&wZ, ¤Èü²†kmoé Æï5TùÛOÆ–»™üÏ o!˳tš½hɹ|6ý).^ô6Gã´}ÿÕšwÂʱߠ–÷`ß¹)tH¾%GRñ‰¤"}<{–bÞm}ºyâ$E3w<£Ø(@Š×;ÑÌÏÑðmùEœ0]OÔ‡ñöº»€Rñ°ø×ZÔ=9£ýíL\D8÷†X?v6ô¹Y•xŽ!ÛÐÿJ_*¨¸]…(åÛa®m rT9_1úqO9M-#[ÍùHì¯ùÌa÷m0~ƒ_ÌfТ`žÕ¦¥;Çá 5!×N’÷¯.â’uÅð¼ö{ez^N†Ré«àyŸ‡ÑÁ4„Gøµk*M³Ñ…¨ù(œ‚­¶í$cjhµ ƒ…êgäè¢ûáê¦ÛþEP¸±ø<×’píQ|pì+Œ:.fEd“!r@Œ$Y+€yj?+>7ƧQÍŽ1ô2 æÑÅU ¤B@„¬¯*Âó×`EÊØxÿ ´_…‰vǦգœµ;û‡Ýãy•}›ˆ ïZ™Ç?cÐ £œ?-ƒu ;©ƒà]ünÝŽg˜~aȉècÔ"/@÷kWÊp§Ò5{òÁ@˜~JÿvÁ¦F>[²ó¥Œ'ómëù¬-~VáÉuŸ`¬4 ÷íµ£UgâÌ÷óQïj4x=¼{|[™åÉ;!KEKÔììöªÈÒUš›ðj‚ Q¹eÄvñŠÑ)Yë¾ØX2¿=sé¢KÝÌ=ÕX¹0œåwà¬ðKàrv÷ÄJ¢:ñ i•â`™:Þ& ‘¾‘niùÀœÎ ‚ä¶y¸k•#Y ?ýxÈú LaÈU\þé&³âÝB¼ýWƒ“»BÔ´öÑàzI²45Ά2îáþÇ™˜X*b=Åø"¬ •ïm‡A»Ïl‘±y!~jŸgÂ5Mò£%uÒž2+…µñ€À\¦ìBæëéÑo²S@×:.¾O?‰Œ #K›¶~©¦1þ¸Ø+ž£³p =ÙhB„º}°îQ«Ì ‹À‰ä(w ß7ÊãëºØmò>ˆdd«ÞâÍ/ؽ«›3g&:Þhày©˜ÚÏ…hÅSqúW, CM*ѣ↾•Ç=~+¨órZbýˆ½lCŽ´?fd…Eh»‚$•+’%-–R0n6ȤO«ÅEWhý·©ôÉŒH¸ ®“˸ vùäÓZ]R’OReú Ež‡ÎjáE¸Ohœ¼XÈãâ›ú´ís6v$çMæˆc`Ö¥K¦þk‡†ž¶¦y+j†Í&¼‹báתå¤âÓ|’5fN3»¼É†Õî4¤Àx‹TÂ:òŽ%‡µpZà{Ø¿¸žóP0ÅÚ¼HÎX>í¤íO‘”ß'pí½5 R†­¢›Iµ…(}ÚöB²D¨Î\ª¤bŽ÷•†Àù±ºÖÚ…t¦ƒÕF>:lzcا@Î&3qç[a•ª+¹ñ׆¾H“ŒêhØÀãCLÃrÙjöi#;-E‰½ù®}63Š%×@ú0/§åÈFÖµz(€±LAúã‡Y*[~±8·ð «h2Á=;ÞÈf;Š3‘?¹ô¬ìä• ÿDSY߸Mì³E혾·1=‹Ï¢§ Çx!|°Ö¤²ü\fXÏ›È|ïC'pú—pš°¶‚£ÿ\dÝꃩ™¸CdÇ`êG¶ï¯'±ýn€S’ØÝé×`_J5{ì¨%ŒµzR¿Ø‘;¸òÕYîÓÅ…¤¦ºùã&DR¿Ä“öÆoìÞÊåÔLÿ3ê„D‘Îb–¯í ›±hCùHs¾)uÌv¥‰ö,>Üq€„è¶B}Ùtºôr!®'äay¿þ<öu6þEP±FGÎBžBR»g6½g,KœP†1¸tjœè§ÃY¬´á 7)X >ÓŸ µL¿¹Jàöædøk©HÌÆ÷ƒi`|Sm˜<¢ÉVÓI^Ühó ®ã•õw˜­šÈ]‹ƒxÂñcâNˆ}g7{Ô\œÒä…ÌU³àÕ½ÄXHͦ?^èÐFDCVšXL]A>M‡ mz8\â Fk‡ÑEe+³tK¼Ke_Ì=LŸˆîd–×å3 þ|‚òè€#÷ü8÷eB¡õTÚ›ÛÍê°vd»Ñ fKM6øÿJΙ…Lc¼.z¶„ ‘—&%Ó1€lÅx½i”)‹Û<‚俱1C-Lr¾ Üü‹=+0€9[äè¾è¶%Gö«›#x=u6Šo![ë¸ìÂd¸%¸ÞPìƒxÅï8+Ìý×Cülö£:¤›73JóTY…¬éÔ2ÛF¼j)¡4,±º‹=ݱk–?íô>EÖth“‹»kÑÃDÏZRîK ˆOË‚âßeкH˜N¬òËú-`^„9‡kª·±«=þÓ¿rlŠÙü¸šwÙ? øW’•›žÀY¼#‚«ðú1~èqœB¢öjÒu÷›ñ{Ž4Göo¼ˆŒAû ˜ÜÃGÃ×¼gž~;„?Þ Ðì%ëÐ*t9)ß{’½rp·Ûfhñ¡g»TèÂàT˜iVÌá˜Õ±õhj¯;%7?½ˆëUF˜å ÁäíŽTöÏâ<ر]òí4ðœG4ªE¦œ—wñÐ sòÙà îï=û†¢¨ÿVì &ò2`:(ÉÆZøC‡´09¸‡ [z‰I¿ÅK¥FeIÜœB&¼'‰-Sø‰Î+h¾ÚYœ*â…ì§jŒ>¶>»j³÷ÿ™ñ¦$r·–3e.‰¬x…xŸCtŠÚquì5¼ý°Œ=—äÏtô¯#Ï^?ij)P>"‚ÍKÀºê[ج©C–6Z¥G7MÆ(ãùÊž¹;œÈÁ‡Ö(^(@LVòS¡[Mhwe:×ׯ€€j€FjÝx.;;àÛÛÐï‹·O6íyd÷«­—Y«ø‘1z+‡[ÒpÏ  Üq~û×"/‘ûê Þ“˜ ÿ•à—»–SÓˆƒç®bD‡f©ÝÅ× ÇÑJw þ99ÍDÞü§#fŠ¢ÍÀá…8ônƒå½ w¨u x‰—étühꎣ£¼Øºª™9üÉÊÏÀ… W°(¦^MbŽŠðGìŸß¶¬µ˜ŒU"ÊÇ똻D‰âî@ê½ÝpK•üÈs§ßgÚ·¾¹×MÏ’ãÎÓéû>#¸)ÏO†‹RóñJ?_Áœ„o0;AÊÖl©wG'¯£:x•5°™Œk9”Yüçäeâíé?Ñà¡ -LFÎw rY€ÃÆ4j¼OšÁzi‘DAR&/D[L@ûGøëv±{¦âq 2º+1ÐHdåU¦Ò|=I·ð¯à½ŒS¹%ç#LfÆ8Ú¬¥‡æcÔ«Ò3±+çm<7sEf¸.Hµá·ý)°ÇŽ"  kO¶êapÄSæ{Ö|ãI:ëæAsÁÒàdÿœ6xc2m€¶…æ´dp:ìz)zæÇ™ U«q `œ<°ö‹§ÁìÃÝì³ Aød逖ßÞ1Á ܨ…ÞÄx×e†OJ…t‡KâÁí°îƒ1 ´½‡¹WÌ Oûi戅ñXc˜Ó‘‡ßcéÁ?‘lÓ”MܬR†Š/*g…i“±÷%ìXo& É‘âŸà*Y‘qž(äf&³)G²Ù}Œ^¹s`à6 äçáM,Š ‰ñ!ä¹ÑüþÜþŸö˜#yW.›v ú{MhÛ’8N˜Ž-d…‰ç‘[¬´f Ìå•'»zÛánÉ_œœg~ËÒ¡²¹”­€/4ÅÍß‹«ƒoƒÆlÚJ¼/E¯Ð”³iÉØ7ÀFžz §5IË9dÏømcË™Æû c¸•왽 È#0Õí àü½§(ê*EôëŸöàü§¹ÏxHZ LˆNE=’2¤De­ã¥OG¦ã›)¯aƒ/í“z„ŽsãYžm6äͶ»XeÛƒ¶©¢Põd%†Êadò÷“¥S©Ç•àØ+OU¥©©½$]~ãZï€J…:šäÒÜpPÜÌ~rU 3ûÍÑÛÕªÖÕØ/8ts*u`šˆxúÁ¾ ÂäÏ)_¼°Ô >¨Ï¢Õ^fxžoÍŠÖ*;•l>AÙàTA:’cY›æ¡Á¨^°®ÅÿôÓ©f¦¤e­9è# ß̶’»h~æ"ã©ßß/mC±;aÙS8Ë”Á~«JtQ˜Av¯þÊòtÉ2|û8§Ã¤É£æJøz}Ó¿´•=ÃçÏ#_*ÞA®C'hÕÄÕ7©¤v·X>¢¬—E.¤ºUaDw÷Äõx|¸j*=¶K†>ܘ‚ߌÜ`½Ó:á¥á ÏA´¥.ÔLLjTK,½;7Eoýº— ä8¡¦_°Mºã0qUˆŒƒá…[¿9îCϲɸ5àÝÁ]aCB³˜®¯EWù=aM´aósCúp…II3at"¹i*£pëH TjV‚RÖCl?û™qü׉ߔ_à̶”i(¹éÞ–ÇØÝ°˜ñ|6‚ *¹ÈÚ;|eê‹Phói\]×zZª$ÈŽŸ(´¶ãóòûhØ!Î8Ùm$¯¯ßƒo|3™3H¯Öz&[W‘Z^4$aR„™ÿ(‹Qåáaó‰P¸?Í/¢é™ p¨f;ý=Oþ,¡7·à9»zpð^WצàÖ53ñëë™ôö¨áè=„¿56Ä¡/Žuþ´œ¦} §sL"™ë'f;ëÏ‘†µL@Ç\Ú{~ÜpicõFUAz§5lA÷Óð v!h¥ßa¶­“¤VvÖ,k¢»6‰‰ž2‹¦Â+þ½öŽ×€•éŠÕCÁ´L%š×‹1NÏÂé˜×;Ø ÅÐÈY‰ð¾ ‡õ²àíÐ>°«còþÄSůÛðÎs†ý±;v¤LþK›Ø¾,[ð³Æ€‚©4âL¬¯bØÁßKÀ.¤ƒ­Þ#ko50ÿƒp¡ßŒg¾)+žîCCL²¼è$Vž•{`lÓ=ÆÊ^œÈ”ò0N6qàëÅCÏÓ'»Ÿü`¼ÛîÇáT¨¥-æ^”é‚DI×1?\ØÝç;£¡¿g:ú:×qÈy´ŠÁQð+´Ê€Á‡ NÑ!iÈy¹{¾ô,Ìž7‰±c€7©˜Ñt:ÅHˆ’m³eVÝè5¿ÍVéôÖ ÁIììf–†ûÒé$#à#MŠ>Oòl›ià·ùç¤cÔÌ oÿ Ê-Äb1ÐîQ„yùÓÉKùœ}A ë <{8¦I’ÈœxŽŸÿqhܣɶïM'N:±ÏÚ£P뽎=ÄæƒCy*âWOîÙ÷Ò!E¥&¸OxVŽXÑαòÝ*뉆ׄC¿Í¡àÚâžãÊH¯ÿ xnAûœ:FûÒj𴮀¯§ÿqÓzýàô>ÀØ-S†O·âì=äÙ­»@!;5Þ ±gZSèÇš"3ÓUFxIî=i¼ÞÏÌ­ÐÆê:þòxËd“ë̞dzÉYÄSz|E>ÚT ·<ÚÙTß(õY W8~³ïÐpƇÅÀþçŸáYxòmn¹ŸˆÊM–÷•s¦Ä’Ä>˜B2+,èÎëê¤Q—Âl›™º>ìÇOYìc]ü|yÒˆ\4ûÂäݯÆéœYõ@#0ŸŠÑ—Ð6§dïÄÜ—´£ê¯ŽÁÐ%@®U¹S—Ë+’“ ù5¶ää| aíûÌÿjDb$6CÚ[kZúƈD}J€ÑªŒÉŒ(;I'dáÁ)Zà-O,4aõ‡.Øâµ•›}°Úð6.žÐ"÷Jbö$纼eŒ)?„ìa¡qt1Ó  ÷“q8ÊÀ¹Vzt†«®•‡aÇ",Ùjs¡™ÇÀ rÆ QííÌÊõ‚Ôýt û¢¿N(§«K!êÉH¸? O ÷sfµ¤Ã¬‡{±ãß<ÓS‰Å§ùH™3%Ǫé¿Dò[‰lÛˆU‡Ä©ðÔ:Kìa°0 }u艘¢TL<â9K{.±S_ÈâOaÊ-2¤A5pveýâó«ˆÓ)7¡rl<2[JžˆzÓ¥êC¨é@–ýþÅ8þÖ"çËüȱ»àé|_¼Ñ¦="ÑìHŽõíAõç·1°º ÿ41tg|I#Mc-Qr©Ô¨ß.¼<ú²;ÀèèÜwþžZYbÛ¥ñØ'kvyW$~0cò¥íY§P3ÒtèsY"½Éù—$Ø<Ö#AIºä÷5§Sôœ¸óäù}ñYÁNØ.Êx^Û…ÏÄõ`Ë´q”èl„ÎVbñG'ï<ú¤¥ÁÇ—ìÚ®6ìö¿Æ†ïÓ&íâ|ô?Í´·±7™õ\yÏ#çL¯)ŸÑ[Ç”½ñÀ€ÐóZä­R,m‰ÈÇwŽƒ”ëNßtz¢ê£éW j‡¢ çØwtÞ^Bô'•¥55Mκ‘ÞëYÓ?b krëFZÜÂc{‘='ã~æa¸êÒ‹' ½J.D; ½Ò³ P¤B«où^Ò¼Z•_‡ž"­M_Z†‰wp˜OŒü (A^½õÔ©_ £-Ê¡ó4²¼Aèkèáþ³ìaêªRµçèû‰5ô¤¦>)kùË®0Ûƒ¹£kÈs©d²ŠMC´ûõ‰;ǦÒ=‰Úe]r*£¦Þæ§ï‡µH‹­[6_‡Hçv€™Í`áj„Xú:ål)o>v„«Âí¡ ÷'Œ öø(lu-Æl;¸šWÊzdª‘O:a­Á:è˜>•Ä-ÔS_¸‹œ´iuš?òâïï¶Çqw¬8=}õû HŸ $n¾¨·€ÊÙkðqÅaã?ÄÜ0»F‹î˜€ø¥D’ºñÒc/F´r&ñbñ|ö–=Üô–}ã6—5!қ戟;°4W/×à?Eüž>ûÙVv>‡».¢ó'Y³B×k“Ï7¥HŸH¶sìPßY‹É/Åá+Ðuê8­’Šz>¢­°éܸfÑuû«ágû|ì}߯Š}*ÃíWYæT©)MZ‡§¥PG :_ÌÐS#·KV7uÝóƒCV‹H±üjzbI1öŽK@DJõ>¥ŠÍóøi{æRá7£Ö«@M¤g²Ê8Ÿ«‡ûo`ºÝs¦^‹~d ‹@èWFõÏ[V1d¾ë ZŽ†Ð¤{ZÝÀj«Qô—ü]»áÚM²éS<é «ÁÊxµÞóÑi‚)ÜɆOÓ`G"kom`nw¼èÜW)ôyüoŒ¸¬H7_+…ýo#Èßr/šŒýÐÝ´;e!}¸ä”[ŸAâïJl]N²Í,*ì›A²ödâ–ãP]r’į oÖ¹ÁÁ£…ÌœÀSô§í ²ÒÄ—Ä»]mª 懗Ê×ðO .ò.C»VqzýO,úlÑbòþþÅH?šœjJ…!ÏhNÛNj“N²ÒCšØz”E]£zj8SŸŽ· Ãî¥Oð¶¶<“1èÂ~=÷œ ÕyŒ)II P`À@Æn¦všéZ¸5<cÖÜ^Æ}Õ!hÄfÍ1&ðÅ çáe ˜ÿ„ñÉJFÌ•"V¶4ä¾1œÛ·ƒ™ûÒ‘a¾äöÀñ LËöDæì ØÍ݇ÿù:Ïkfãtî`ÅVgã$Žá^‘)Ü3Á2 ñç5&/ë…Çç´iÚ™¹ôÑžäWF5«š²ƒˆÏsc3§—qvyì…Æw/sR§YÀ–¸È.Ç^nDítsÒ2ú î 1!ßó!p­ {Ó”u†€lJ8–Ž; åèæMdÿ$†ˆÚ2VžúÍýTzWìÞÆ„ÔFã¾Ç^´3ª¿'DEWràž sgz»hr=´ Eð}_Ì}ª@—Õ1ãbßðéÛ9øéÝòðÍ.òÒD’б™ø:i «·¯wšeãsk¼÷34-t¤nDÿZHV6—@µcœäzÃaôD³=üädÔ îÛ ›0w® L;%N6bñ²»ü¥}òÓvl„Çjô›h€6÷´H²a_Ïö!k=\àái)zï‚’t>³×åü¼©N½o1üR ìW/X údo-†ÑóqhõOüh%LËï?‡°xSbU²Ž„M…¥yþ ;ÑøF‡Z{}6$1)ö2qpÙ¾ŸhQ÷’ÁÆ[ˆ‘ö¦a½1ÄÅ÷ïP/ q²\Ú¥M4@éƒÒpÈà5r(÷§‰PXM÷½·†ÓÒ¿í7¾ñbïäàØ_#rVÞŠ„K{³'çÝA©u'Ùl®?û¡t 9w²è£WsÀüÒ)6òÒTHM˜ƒ®]óÕ'HhþIÎ1ÃB˜}å:ä«%‘ì Ò[ñaöÄቱ¿x> ­‡À;áÒÈ&Ye2;ŒØÂ)‰Z>“@\vµ”÷pZ¾Q'A}3hGÒ|x JŸ½[ƒähO§ÓdSÉ sû %RñðkZìŒH"ƒ^ÅlE¢:é?¦…YC¡ÄãÑJŽ ×/,;6€‡¤F!ï­*Z'£!aqÝ[yΡ{ÿ º ך†–Ë“!Þt¸{¯>9â³áûlìOYª1{—=d78ƒÉf­0óŒ%±.úÞ!¯@£‚Tüs,2rÕXÄŸ@ôC DÜ;o$ë àU«V=õ! OöCáüë8p±‹ußN¯;à¿M%ìkË tú¢IüûF’ÔÆ˜ ;Xx½ÑyŽã;UoœP*âœ7$8;̵s75¾jÒ8(Ê6‘#Žw½ÔL6FûžSHcgi@ÌIAr=ƒŸ˜ÜÓ$¨ÑÏÊÖ‚jÍz(~ÂBas“<Ù°`{¦3ó+Ö%tÚñ‘ßÝÎÍýr'çcü—ôCö4Bî§ ¡û߯oDKK¼VÏÞX›m^›ÈÇ&>´]BŸjÀ›å!ÔÁ.Pè$UI açÝý+áü¯K™uÙÍX¾c Yz2”˜nÔ#wl ÏØgè¸o»ÿaX³&6§™mö÷“ŠPN–Ú ¹KM¡2XœyÎÁÕ¹pÓ0Ž­ÛM) TXý-óök S ÈAãÑA&ŵ î_íÀ• 0áZ@¤"'˜î÷;Ù’K:ð½&YKXö…IW•£‡½±½o*ôífßÛÑhò„©€Šz]°U?•W¥É¾ußp$XŸä-«`Î=‡“>Ç ãñ)´s¯ÇœHr­s-É«R„îÌ xën 7D·iïÀ8•ãPy ç ¸QãÜøï^3ø‹9}7AéÛ X%SÁ\+’#‹µŠ@C‘Íä´:O#Ž›E!Õ øÖ|åsu"S+ÈiÃv´<¼Ä”øPЍÑ÷›G™;ýOY§\aÂ[Ňžû‰Oçüú¡AÓù^¢¸+«OxÈw÷5Ä'3‚Ó/s ®æ”£áÄ º;hÄÎÖ$æ”ÝâdqúkìÈ'S•Ò0àøX|Éh›)»Ö¨F‰ÈyOÀ9t´§°÷ÇEÜðÆ‹œ©‰g¢µÎUôctˆyƒI¼9%âAŒa(© ´‚Y¹xó›8}°´’ßÀ^»49>¾ 'Úë^;H,NñÐ/Œྣð¸!µæÒ7‰ªð5±´KûPÑï¥&p~Ë"v´³þE°ÝÎÞ$î¶ ¨™î„-×tIDã œ¦,BUg Òâɧ_æ[r=Y]‡g<™X~V—¶Š†¾zŒI¹ :Lèê/áTêÏ4*2°˜Z¦'BÊuaz¿O”sþᾪ¯p¼ï,^&Kÿ˜_»zCG—þ-•¨xü+\|`„–ÿد’|äÑÍO bËGbYë'I„Š„¯OÝ1 é-uþ‘ÅLøó_¿m“Éž™C¨|ŽíMén: ¼€Œë%!|Iò£rrCÏ’ù­hL:mì©aÝcÜÖxŸ[ö]žœÝFf¯Œ žIvÄýÑâYÊW¢D|„–'€x”œ­2"uj@&½ TÏn'æ"jTÎ:¶]È&v:ï˜ Ñð¯k/#CL÷o«K”F2‹¬Ô0ÕÊ…H¾k_ Ab"v7œ¿‚âÑÇèQ§ÝÈ·GÝáNÚjúg‡%]45k]`½ÀC¨øsšTœ½/¼æž½u¸±º–Ù«ýä3 È‘üÿ4ØÐG‡ü;™®ô9Àwë8´> ºË¾È/‡• Ú ãù"NëÜ«8çU)4 ’Mc“X¶û<>?ªC3jĹ¼·4€øeÒH˜_7`† µy¦E¶?yÁ¶Ù›Qõfz­Ã—.ž ¯¦e³«¨’ÔÓ®ŸQÌ~Ò$M>ú™Û¿n>e§ Ë‘¦þ(t J‚Ï@¬l1Îk*G™Ó'p¯Š-*Ãc©&Ÿ¯…Y¤} ] Ðfêv²q¿yíD'çÃʃ²œ>kVK …ô·–Ym‰ñô ‰¦Mø ,7'²††~ÄÙø;£$Ü›Æ/r“ëª8~ A"›9G¸§²®<6#—½°Ãº ÏmCÓG¹èQƒäiºðÖ¾ql>ÍÑêF!ý´å¹SÞ”GnM©`EvnÀ‹Î¢J‰u@Yª4RûÁ~ÚsæN’#éù‘&®ÕìðypO¢~·ŽÐ߉äí%F;¡†±øÊ&Ù]†ý…¤ñ± ÔýÙŸލNênØÐŽK6Ñé6+€oÍ”F{âí’wN Çû:¸xɉȊ[PÑ)Z²c5Q4NaÛ6ÉÑì¸ó\‘g¬Á…dóÜ2r0ãìñÁ/‘ú¨;nBoÊFÕè.Šrè±T*¤øÎðÓ·V5 8?õöc1Ì>ï?Í3ÂôÈ”…óZ±ª-›:3Ù 3ùèZ/ƾVxNúªTp—âTƸT8×°2²£Ø*aHKÞPüÑ¡A ¯­q4A? 5Íö€Sú6LWu¥BÅ«`«xÞém‹‘ªõ*œ³@?ŠÃ×^.…¼ðùó]t©?F°u6Yë“§ •ÌÎ\krÖ`ψŸÄ׋˜*X®ý¤ŽÖÃìTIf÷ÉQÖÓççm¸ø*Ó•œkô@}8©~âF\´ÈºÔfæØgRñ¤™ñ [?{½ÈkÃÞØHÍÏoF@¸Úš«—ý ¹®¡8nÓÕœ9į9f ҞïE¶¹ª°3®¼¡I+ßc÷m ‡†{רq{LªÄ_l•«Ï%¯ €Ä‰¿B—žnÈÓ†ÙŠá¤/Tõý)cìúsõ`s «ï»6f³–-"ö*·ÐjË'&BãLq¨ç6È+WLR„Èå_3ar 7{äº ±IèälŽéI±ìUy–] ÇEšQÝoÛaT{:­žòU‡’¿±àzÄ {•ƒv¨.GJ~&½òé">? ORüÜY›.%ªQÙ®WŒÁí=Iš’ŠK·EÒy)ôÐÖÓ¸óL'2ÆÉxnõ¹ºƒ=–Ð Ï–`Bl=ÞÛc`G§ å8‹{^Åq:ˆ;aéI_*šQ¦¼³©¶új˜¾è"¼>Îõð§˜R ¾¦TGC9!Ls/©ß}ËÈ5]’=¼b® ±:mO0äiV¬ñ‡»ãil^¯0Hæ_@÷aÔ’ÙÎôŠKPC 5ða'Û/óœ±yÁÛ5Uxsùü` Œ2Ñ8?Ê“l:åH°†ÌÑ}b$‹×"³Ö_«ÏK¤ç· ýæKX/‘Bª.¤áЃ܃M¿}‰™« Ý1èJÝW£ØÂ¿88ø f½ÙJ—t’{n&¸aÆØÆSLUÂc!ШîNâ:gÎ\¸AŽ6Ϫçž;MæOmD>11â¼ã=Ë·o÷‹ƒLý<ÌÁdà^ ¼Öª„•m˜ ±¢¸nN$ôNr3‰–dˆù¡Žêµ«ç)ùÖ»v= ~~°ËL€n6Á4—`–s`I½5•þ7G­£ÞξÃ8Ø`jÝ/Ðx%@Ì?˜mW…`lZ'þªÓƒ²Ý§ðVÏ,ÜûzˆÔÆ×t12ß¶WÑ¥dÝñ,öχŒà@ðÚ8Ì‹jvŸ þ=‡ ò²¡ƒbÈ?àIž…{ÞaØ1A¨µmw-ÀÍ'êWô!Ì‹YxjQ=È4G'Ÿ_`lz k§·ã§Û·˜y|2Ø­ÀüËÉÆ/_+àÇ©4òUe ü3Ÿ¼uäxfÅMïÌÉ¿û«ð—æV–Ü*©uá8r„ÔwÖ1q HÞ:ejöz ñm𙩉S~Ø‘¡ù¢ìÙ»/`{|?Ö«Ò<ž#ÌÛYzdæ,-ú²þ ¦JñûQ ùWr¥Ö­$…Þ|ñòå¨HàêP,EûòIáĸ· ´óþ$WºPO4u”h·×Ø“2Âþ‘6€ßn¡8ûïfºQe6×q™2(]°ïñ:ʹezŸw±é7—Ðë»U‰Œ0íy5ŠŒ„0”{m‚{ Ê8'ôϟ̹Á]Ø2Èž¶Øìº0îÙ¬ô¨y/ܹ& ï|Ïúë/„'õéd\Ú ož‹pïy#’=Â:Z´A9–¸óѸY쟌1ð~ Nþ²GzÅŽúX±äF†Uâ+ÿµqRR;0¤Áƒ,½Q v+rÈ>KjÒ)O/?z#Yg£C”CVC Gºžw!ËOiÑY®Mxé›)ÍZ{•¥Ü˜”ã‚ä~| y°Qº“}lgp;- Á‹*1«Ëˆq '?-’ðД[p#þ ®üNV- a¯Ýœ #šþPk´€­–”Ýo“ kÞ°¯¯ÛÐ¥Q4! ÌRjðlÿêx(Ñi "d@ê¹Áa~~òoºúß‘&!GPZ >ÔÜÄöJ´êÓò®f9’ôz³¬Ò*¡2üQ´H[ŸJ-ÖfÕÆùé,ÿ$qn8 û7›œ|öOä§Ó ‘>N7o$ݺ‡d½ÉÀ¬“)4êä6ìü¸€ìÔ™C%Æûð§^![¤}RLXe½<6+ªXæ~;{~yF/N&Ža8:+–. ÂuäFŸKï]úê×éÒ ²ÄüâFÉø9sxÙgàÜ]‹Û¯ ³]û Î‰Ÿ\ižÏLÜ%Ò¾]èEø7ú3füK¢Ë­ éç«eûùfCÿÉ>î_!U²s‘¹—ªÏ¹xL›–Á çñÒø¾'Ò–°í rNê4BA\ü^v‰œwW¦'ÍNƒÔ…ù4^À޼(lmclÚÁÌî3B°w,—Ý:ô \ãq–†2×Aa%iX“Išý™/ d;Ÿ:÷ìÁ}„s:‘ÞýÁXÿ6„@öBÁôÿÆÂçvòƒ·6Û„Á§WÉôF戙΅/}üôÒ-*aË*¿z„¸©€.~½#Ä»Y¾_á@MJå3Ÿ_ÙNDº·+Ó»®kèæõ¼Ì©l*l5•GÖa,¢ÏèwäýhËIz^ˆ¾‡N“ä™´²´ y1¨“y©[èéMr#Ê„pšÏÒxÓÍœÝü'1U9}És‚yñ¥ïn؉¾gcšÄçýç|~úÅ$¾®NKÝ7\9Ç%Ôδ˜XT ÿiª „U`lŽ=u4îÅä‘fX'ÚÙT2 ç1Ïfùxÿ ªE‘òBòË{ ÑÒ¬a:ÑÛÃ>´˜‡‡4¶ˆbºÍN´˜iJcŒãÅx=ÈìŒvíY[9kwåÌHAÕ0cÿcn‘@·oe {‡~ãm ¨.Ö¢;­d`ísô>Mqß4(AMºKõ&×ÇìÞ”çÇ%ËDIT¼ªí§ã-p¾ËüRáÜ<„8&IÓË6bÌUS0ôÆ3RWY9‡,fxEpĒɬMÛQrW5¤„fϨȹéÓ²¹èz=œ“~ÈËœKçfCo|XÉO’÷s˜•{VÁ³²FÑñ"{èü=æHíPµ³ƒ`ë­tóÐCà¿ÇË”k^gßðÃá%÷¡rîR">ú =;ÃÁí±.)À®;6†à ¸TDbã°ô÷™S¯ãx‰œa£ÃtÐu…›8£WôC¶# ‘n¯Ñg}zÓ†>5tL?Éõ™ŠKö˜@f5¥Í™8Eì*6ò‡w˳Yir¶! c~¾ƒuÿ¦ÂAwuævï¼5Ó‚?£‰´ñvXÒO Ív’¦#z´T)“ÉZƒš·&9S¥µ9‘ÀmV=Ç“ñù ‚7Ä"pîÛÉX:pžHxà+ËæàLi–-y±“Û€8ozÇT7mU¯"Ö(0‡¹”Ê¢†ßZ „^ þ «fL™ñR\1ÜqXÈl=Aµ'ÑS3IŽÆ>öreS³öýßÚÛKk¾`€³0Æ»cEBžñ6§Y‰#pÖH„òT~jro ¥]BäHŒ2µ;]Á|{ÓÏìÖ²7IKB!‹LŽÏ,>˜}ñ5¤˜ÿ´ËYL8Õ–ýp\_–x¹0º YÜñ8¸Žr¸Ú*ßê#Ñí„}F‹é©gØ{ù¸Ÿ§²büDÝÚó4#ã=aׂ|úޝ ÏÕÇàÉ0w²G[Žöˆòsu;cé|åL\—ôOƫך™›‹]hø|G2¡:ƒz4 щë‹!¬ï1´{¿Åô;é„®VÖÅWÅiÚ5úúJž\vo~ÊàÚî΄±àÝ웪ú`K-ši rGW6cèÖÌ´²4¨ø%Ã:µ²;¼!îx5ŽÑ@j;¡³¾1Uò%¤]#ëê/àÄEÖöI‘Þÿˆ-±ÿÀŸCpšû Ò/}D•6Y[f@ôvÑ›6’ôiQ)ÎK`OžN…°˜û¤ÉPÕwcÀ[z<µ¨€8@êx€‡²µ¼PµžL? ”P¯VœºÞ‚êRcTí †ð¡lUÒu”Qx ròEXT{k$Î㽌«Xõeö&-…O·ÕéO§döÒôdô]²³Œ,É_)Êø(¦¢¼åmvñ½7ð¾ÂœþY¸ˆx%ü‚%äçOaš“ð—L¾»þ±X÷s Ù&È +dâqõ´æÐv;¦À¼%G Ã¯ _¾=„OŠÒ¾3—ñEFG#š`óÐ]™ÙŠÙz„†söÀLg¦÷àÃsÐ岜£å KŒXä»!EÂù‹Hoy èFV?,§WÄØÏ]ØøŽVwá«kUèÅÙO l¬ 8@Ÿ1žL€ÿJ±½ô–ÅæÿëŽíè]®¬’¼Ê{@¶Ngé°Ìñ¦$t?Õ ïb%¹«§³‘k01+€?ÿ½5!K¯œÀŸò«`Ë&YòÍû(ÆÍV%k{²›߇7&µlµ×äYï‹ÿJŸ°¾Òä¢ä_¦½¢›Ùr‘‰-z»ïÖ£=ì!Ó6½‹û×Ð|• Ô˜“-ðR =l9=d&ß« '…Ü|<Û4É]–ÝÆvó®ÿÄ0ôópþÔëŒ= q‹ˆ÷®e\·e¡ì•Ð(<BþeÎÅæ˜*àšU°ÎÑn¸h‘6e²f[æBíš)ÌÚ˜n)=wÝ;û<Œ[˜Ós¢œÊ%à­¬ÀŒ¢·xlöæÇâg ª77ûàˆhÃq™ç¸ù†{wÝK¶SÏœÊÖ)¸ ½ÛLiŇ_0γ‡ýkÁ~:Tæü¿Ë&ì‹ .äe½< ïÀM¿gÁ¥‡¦Ä6Ê _!/‚ïW·vg<Û­?ÀÍø¾•¸Fˆ‚TÛx<7ëWF"sŠ4Ç7£ßÊ*èßcNÖ¯Ë]0ó ^û÷Wž p9/éñPåÕ—˜]8šÖNvï† èÁeÏ3ŒEÓ®:Æ@ØË:fA¬žÞµ–\ ÄPÞè«^„÷6Z»—Eø L’¶¹§±Ê&-ìÍÖ<˜žSãÈâb¤ã]ÑÝHÏoÝ„u œÁ/|5>råJ)Ïáœr6zIfÃÊe™ õªžÉð ½Î|pêL,»M¨y>»³½;6çO7î›ËǪ j!Ï’ÃìÑ\öñ‘ø^Ę\4•"Ko°²¯½Ðx ó~;¶l©á®H"=ÿ¡ñ[6£³§ÿdÄ0'ýÜpo1Ù1Ŭß;É3ãí‰ÜßùäáÔkàZü$ê+ ìŸ8™[‘F¥Â.Á\xŠ1o…‰T»»bóg¦¶â5*m­žÄO9v'ª¾r²Œ˜É×2ÆVoQ‰•'°(0 “|Ê€Ç}ù°AŽüZô¹fI¶÷ÉQ¯@át)^¾†¥n·áðŽ2´>û›»O÷*~YÉ,^Ù‚a ‡áz©.Q“´œJo]ìB“kRt®g1ð<6 Îç˜ܟưââV¦©äì•#k l·ü€g{ŽRNo,w:Ÿ¹ÑÚ }´ÄN%•RÝì¨`6ï#;+§£í¿‡ìÈ+Er|î3˜«! γèñ»ãDE·€i-^OvÉó ž›~ÚíÖöU›,XEžî3í&˜›>ЬTˆã‘HpØ JÝcÇë ¿»“i5›F^¿Û¾¿²¿À†Ní3C£G’TËRr¤—ù]Ó¨˜Ý ’¼TêÔ”“")9:¥ïlpxÎ&µYa÷HHQKöṳ́Íç„ÇmÇ ùéüé/1TV­äqyJ M¼šƒõ±lo0µïTY[ŸëÌï]å¨ÜWC¬O”sË’TÉ…1\)£,óÈœ6ûZvÁDFñtg›Âë!;²DŽ10V 'Ö“×m!$_Y‹yè|;¥á _ ‰AÂTü®-ö_›Ú›!4÷8á[tæ¯6b®€ƒ]8‘V s¿ïw“njÈ®ZPûilìJYºØ¼–y|¼/Œ–á¼X {¹k€ùXÊ|]¹‰lyÄôn `ÖIÎd'çb¦þ‚Óº Hmª ñøâJ6¦í„ÏA?á&~,ü†¯/ÿ…ÒæYÄR.x>YžEþ3gVFÁÒ¿[¡Øés…žÙvƒ=7W›Ž=j@¾åÇY¾å²øèǘT„ä1FË®aÜP‰\Û›ËðGn¡ÂÝÖä?TË®É=äÂGv_0'ûGâ»/¿ uk2PËúŒ”–½ññ»`¢“Cf®`?î¥UBó¨ÔÆæ½ü¸?Ev¶fwîoGŸ HRÃÍ%Íh²Æ†È^O+ÇY ¬„5ݱd®ÜœþDŒHJ4¡¦„¾ ûÝ `>y&w‚ë•C3Ç€ˆ—”²ç¹‹H›‹sBàÖ.’,¸‹ö‰*ÐwSD©À \n«M²ŽÏ ¡‚HùJÄêq¨¤ÑeºZÜŽ¼’L¢Õ‡É&Öž<ó,l2Ñù:—ßAéÐf^“þ§wî6 °~– ØžÚM4ÿdãKÏXâRý¸Lz>——X®±dcOFrnôx“…ëh”4èØËÀ Ùa6+`Õì=4‰ Xòwã\²ÑWŽ:i. [ë“qIÜ £éŸ¿1—¹È$gÎ#kšè¡‘½Ô®5•ø»Wª¡Š¥,UûgNÔO€Á[:;;™ÜT¥Âk<é­™Yøãi19 SS j^¹¡ðš9²Ìƒ(÷YA¨û<ÛÞŠÌŒÙTAIÌ,™‰‚%f°èØKfÏïmx$%ªzžCegÖŠ•àg"á—ØdtÄ}u†Å%°8w;šLrŠU¸Ìᙾ8Çw!‰¨Ó wgWcgólá£û]´ÊwœyëñÚ"ûñ‡»9’ÚÌ’zì’mù葜|(8ƒ½# ªs t/¹ÉL™ØƒÝé§YîÍGÜ*G¬/L@™Cå |þ)÷s‰ÄæÍº/¬uÿBZÕUyö‰^ƒÃ—òpm¤.m‹¶æ‰Ï /´=iWO,[˜¨ >¢x®(A˜ uÖ*E ¼ [ùi«Åâ–D&ZÙt örõs°Ž¤Û¬ä‰qñZ0ìÑ„Yï”èD³8Ñ8Â^+؈M{çÑ‘§ƒ³¾ßÜW†ÆT!šŸ¬ƒw–øZäcÖãÞBȾÊaSo¿D£ÔWøqã: ‰Åuçq‡œõ~èLƒœc ¯û= ½0†¿fú›ê¸4~Y•%L¶”Ì¥3_N%VsgíNi“¬ƒ{éì ¯0vbGk?‡Ea>*ªc„§Üjí]OŸÇ±OÛ Sd-jFNÇʼnz„ï©=ÑMêÁf©Ð,}ßÍ.æÜæð¨H Ów"/›‡‚\È¿ äw8F3˜š]oHß|×&«Ý”èkí¹p`ƒ*Æé¨å" 4¹÷;½$qgý]xⲊšäkæØÚ$O_ÞÌÅ5½¨ö7?þ–MÞ5G2ñà®xš¾\žð. <#ŽØ¥‹Bëàßaºzç jËq„ë\¸’/¾šQqëfú,ükòHòV$Žu›ŒòB$–V7"玲¤ŸFø¨úÏ`º¦LˆÒâǘZºÜ®GÓYÅD1à WnZOc×m{þ2js( ÆLiˆ¢1è56¢–Övhnćš7`{ÿܼ(6É´Aû·éà“ýœ½»<Ož†Ñ;zØoq‰;”¢B%Ž-!ëïóП¡³ðÖ¬$¦ïøB2ú"V,ùÇÞºÂ!é½q\+Es\µp9®Y6 þ‹ %í:úiÀ×®µ(B3§M¢)êˆÃª¢ ’þ5w^ö}Õ˜ür]ë]Fêv}‡ŸN#Œ^c;7I< ×:N;pV÷Aª¯º™uf48)Ïcâç/Lˆ¸Hš^eîýM¢³Æ”ѰÜfsŸv¡Á×ëÐüpZ^ۉ뎮¡›òÕÀõb¿¤ o¾¼mʺÿöŠî%_|ýÉL¹ð©õ4|–?ζ¨Žã/²¦&éaÔŽI\½ÌKO#ûö à/îø=/^›l 17 ñ¡Ÿ|díȰ¿]V\K‡~fvJ~-Ý`òdl*Œ÷Ýl£ñeª9œO Ä2ó,ßâÙô£äP6[qF‡<ú•úÌýïÑP·²¡+{ ÉóÁuD®±o/¿;lÚhÑÍØI<¹‡,Éb¡úA0L÷ÄÕkí p†veÒcF|ÜÑÒôrûtÒ‘lŒÜñ÷pð¢<]bmB»?ñ“…3¸©]–КN,"‹1î‡SIå?:ólc!lB®òR£y/Ø btƇ^îªGŸ±Jÿ"\úýˆý¸o.•|ðþn£U`ûsâÄ[/š7 \¬G¹7ntUÐ]Ö"҇ܨˆCξQðö¨Šp5"ö»œ18±2û4èb¾w0S˜$ÿˆâ4‡ÿÕª†§‚3©ˆ¡Õ´Æuº¶ô™0:·`{úvS ž1&â…û­¤eÀ/ìKžÞb’І?þdnšnbU”ͨï݃ Ôd(K_5±wb^ÁõàxêámĬøèKVþûþ¦“¿¥qŒç(‚xïQ"}ü0´TWc#ß·WƒnŠÙ#tù¯`fí àîwD›ßatŒ£€¡ÿî‚»™^ѽÍ<œq‘Ñ—µ!îÛ.3jQwÐxà;·F‹ÎàßÁ®oýަG¨È‹‡ŒæµR–ég´þE3‹/ ‘g}²ð/Ež¨æÌųÇ8¿ó›pÙjÕ¦ 7wÞÛz¤ÄÅ¡hÛЮ¢}5eñù92»:Úôÿò?ÁÁ‡ªôîÔXr®Ø|q•†‹½`Ž1/–ív† kè‘3ˆ~M¨n†¾þˆF¾‚¸fÕwx x‹ ¿ÊM™™Ißàáî"}WÌ)}}Ú#O K•·ÐC[>=´²¯Å#YuÌøçqx¦h@¶…É‘ªí@oí|òîsÓÒ,#žcÿi˜«ïßÂ÷ßÓ¹š›5Iƒî5¬ûaÝáG™mOß0©ûm!.CŽâÀk˜v›S±ì¨¤ ž¬cì¾oÛéÓW}ÇB.{Ö¦èó;ð®ÿ!Þ¿½ž]²l5Ôì~ˆ¹cyjlŽ‘)RÚ'6×0~)Ð>K=g¡±L+^p]M?üÊ@— Äøí)öBë#8sY„å,ýƒ;ãYÆ®k9üSŸv¿ßI篘¦Ô¿[”NkoæNæÂmppî]ü"ú‡³~ñ/,I²$Ý)i ²Â„5gµu¶&Ö±ô÷/Ú¨.˱i˜ ¡4"c-H½§©À%%G°ª_F4ŸN§+ÝÆ u^ »r£«ºL>n„’³å "F"Tã†ü‰ÌuŸRð{–DÄîÑŽèã”´ˆÌ:Šaò§D?lÙH¶.ï…2ËrìTh¥ª¼RÄÐqRÕ,HŠâqL\ÌK]3œèèKØzN‡j"Ü%“¹ðZkû[nåÛ#Aì‡*ÐKÓ‘[QëB~Ñ_ì^ãåð¨\–tÕŒroŠÏ…màJ ¬–ƒð¶ %õªrToc{5ø"Ñ)˜€+^ÔOÈé "ßÛ<²j×::~Ù›ºeŠR¹,þséÀÖ’³bÄ7Û_VÎ>hËÅC¡q¨áÂ×Á? Qñ£w4 Â#i"$²•{°ÓÂr0÷ðZX߆a[–ââBE¬+„È{ƒhz×xæDb®á 2ç«->sÍ›ø©«ùTºc^3üeº½Þ@+iåŤ£÷Àñ*¿CJËrÕe }¶0—hD9¤P÷vK8mN’O›ÐÅ N„ÒU¦íåy\2Ï…NJ%·n’bÝíäíx$Uv¡Ï*açGKêÓ@¾ë»3 -÷²ËÖ'Åë4Hï)$ö{âÙ!…s䢯%òRè:dYSí DZuã3LÐø Ê È¢ǤNÍüLáÍãJä3ýz¤ù |8ãYØ©BÂd“ˆÀµ³ø%ƒÒ˜üå ¶¹œø8úÓìùôýr‰pޤ Æ„Iˆ¶.þŒ$¿ÜÀôKITðQ gí¡Y0ýC´Ëá念ls¸ ÊI‡;Úö|=©PÀ•ùdÇÕÙ$ï÷ ¬ýcOk7>…Ò¯Ø9HMHœ¦8„)щÛÃ2<+þ L—E@µx;,c*`“O4ü§¥Îè¥Ù¯“ˆ‘ ËÊåܱ?fAŽZ]„î¬|S(B¦EiнÚ1Ë2|÷'õÁ9(ûú8q0\DãoE9—caÙ¨=úæ&5BãPŸäÒÿtÓœÓÏY½Õ{it—wcõlÿ²šjyê3|Þ4œõ/Æ•ï,@è×kÆé`:. L#^éSž[­Ä˜|ƒÕÁ|„×méºâJ>_þ¿•çÀúÙbÔTQl»òt@A• ­Ž‹£Ëóˆ÷ÏÛ0_þ45ݶƒz…Uû›‹i€Y=]л”Ü "d¿©ª6_…F»X|)µ ¯%¿C–‘ŽS¨ÞÕݸ~ñN¢rù7È*L§QùÂÀ7¾‡ä¼ZA‚?Ö³QL yíwžêÎ!yòNôÃ.Xüv%|÷ü\à¾X)•N&ÁÂB´úB>3[;7GÒ9>Ý5Š—j’¿8{±#:„ëÅÑòÃ;b¹ª/\±Héó³xfÛO4) ÿµ/eg‘ÝyÑt¥Á¼¡ð–ÞùðcŠÈÄmsðX¥DGŽõÀj&çÌ5x^ÚJMHtZ,¹µ1…¦4{|› ÿÞS%£$úmÝBúGú=Ø”Hï©&IW.ÀŽéÎdYO:}qÉV•”•óÀá=Á0vž@o¥3DGÙaÓ­Hx[cKO~X5ÍWpåÄCè-(ÀÓŽöLüþPÑõ sÄÕ¡¾àç²»uÚÝ€+í„àÐÛið®ì kyýʾj]¿Ðöèê˜NO-'7CŽØó~: u?èãÏnèû¹^]-¢q÷»á[ýcŽªÓd¡AÜCþ.ú_Ýg‰@¨ÞmI¤rë±£ä ¸Å,¤vÝd5²óŽ*•ÁØß Ãñ¹bôc^ra•#PHô7 ë\¿eMøR¦€9~B€zï1\DŒ²Õ™]·aƪT™a]Ê\z=Ý“<Û17dØÑyîñ vµ€ž¬4ÆÍ—„IÂ;zUl%)Œwb_wÞeuì‹ñ¡{—¢Í gÛ_š_eC–ô‚À§½d׸'•»fƒ}cO™S¥Iîœitl†-ñ>³’>ßàE¯ Nž_bÔÝq'IúéJÖtÌ/„ÃYSÈ‹q9¢÷ûˆ¬«§½kLüïÜá„HIé…JØàÑÂÕJ¾‚ÍWÇX« 1xuôµëˤ‰:òpK#‰ìÚºŠ>nÌØ,`‹U,ðË©°44Ÿè ™B@*G±[†$¯A›É8|,þ î­; r×[0¹Cƒbãuxú·Þíþ/ùÍØåÜ眒Ä%ÈìÎÂÊ…ýl¡š8©Ïà äÀ=V¾aŒe‰âš°b/íw™Äz—Ocá}Sâºý+»©È÷.·å,ˆ:ÙÀšã'zá·]U îef;76ááh3¼òc„„ä3ÊK¦“i»ýðÈkM¦º4»‡þw³CÓÑQó)»ùñ&̂ӸCi%Æï×£‘O’¸¾]×¹‡ÎBÇæR2Ãù w@9–×þC{C™›îiá›…ôÁÚvÎâxHÌMq¿™DsÅmÐhÛºÏK£)Š-ðŸ†;3<N來FüÐ;MƒžåÀb³Ãø[)¬]·“ÿtŒÿ‰¢ùYð¡^G3Ø(áDø›Æ´ýñGâWJN®—AxÁCœ+ºç´ ùÍ7XX}p­2IsY#Æs¢cïŠYC˘¼àlI G=gL‚ ¬S+€úqäÅm†X——ϯDrÉ’¢aÕ‹YLy^FÓ4³9ô^É]÷œ¹´:ï¶øƒè)aœœsrMWГ'´È¬M®tÆŒ=$‹á#CÃr×…yˆÅ[Czÿy •œI3÷ûRG;uÐØz†™Öp ó,ÈX0éS¦×W`vwn'¡ÌN²s‹x“vÌO|ª&H‚ž9Óœ…Kp¨0š ŒÌ §ÜÓu“ï÷ïsè €¯Q[ðšF4^3½D—'Û’Ä´*j¸ñHøÝÁŸ&ô¸¿!±þõ?"Þžê÷ý··²÷LFÉ(’y^רÐPHB{ÚIÊ 2R2²BFIœ×uGHE´(©h‘Jhjü½?ÿÇ×ÏãqŽqÎ}?ÏuÝÏëù|ë\!äñ¶Xi'Al¿=EÕ³¾ðNW”6Ìú kª2Az’õ\›xíÅáüã¤0%Ž.ñ ÿõ@{Ï_Œïë’QhÎq0 ò‡0‡F&kY;›0êIô^ª‚μ3ä¥n9L‘zZ뤠¹X…,^R:BFdöêhªºB„MQFfE5h?Ó§SÏûô1fñú~ЊM`¿úó‘µ7.Ñ;C[É™ýâIœü —¶ŒÄÀL)Ahس EbTˆç§BÒžK覮­´3p-¨;×`‹ûˆÍhå\ šÛŠk¼~rò`Z]Ò"ÿÍIÞ¤¦ ±ýk à†éõÖ¡í~ØïcŸÔhRöëŸ?ô¾|•©¢td’+™Þ•ÁÑlüN÷æ@ÓÓÍäiY f¯²%Së†Ùù%k©jävòÆO7IY‘úW¨õâ.ÛQÛ‡yE Tjº0î½ÛAm$ W+b7<–fŒ#b˜„žE$:σªüºÁìO?Ϥ ï çù¶cÚåpŸ¾f‹bÞ°2+Qi=%¢3m1=V<æ~8_|ÚÚƒ²FáøÈ@—>_:Ÿ6¥_§ëèA„Ž<§:ì6'È}>´~‡3åøöêr35š‚zATþx“T ¯DbpÐøsÁ‡ÜZÚ‚áåx›g÷†] ÝPzå™ïB†m¡géZœœHN«ÁY“iß¶q ßÔˆÍþÉ~o#é­2¥Î Ò$m¥&¾m~Šf‰qÈW¢Ií­Méé§$¯2Ž 7¡°¥¥q÷Ù-n~äÏ“¥”÷¦$µúÔƒöT¬rBj„ ­1#ß–Í¢+›Èmžf6ê8¸`î“©dKñ%˜»×„¼Ú…3”©õ§øµ×Œ |¾¢›àd\¬µ¯ê©ä÷Ð5ØT‹=bÀ+[ƒ¼]™‚ÁÏàßu‰8k¤­iÁæ§pï©-º´Ýw©S&σøû2ŒØb7ª»á#V]v å†ôÚG²Èø(vz®gä7M‡o7¿3rQWØEW@)Ô”º½ðfzk'Ñå|ãyW4JZhÿXÈ|É„ä˜Fµ&ÿÍž.ZVŠKõ¯! ìÞO×m¢W”•éÉÙ3éÂsµkUIäÃTæJ¥Ys5ià¶ÃÖ(Ø-ôý zŽ©xmº^”³'c“(¾;¬I?4þÚÏp '§ÞR&¾úéLÞå'©XZj”AKtãakú¿^dϾ8ú_rBM-6ÿ"û/j*{êX%.û|ŠUM¥kœn¢ç½@v³¤(º,cZmÒß6Ö°VMVL-Þ"Uà 3€þ![”ûª„Fº‹È•¢d(Õ;%ÜÔhý¬E\ÍÉ_˜Dóljüqο§æfCÙÎXûc\\‡ò‡ÂÄÒáüùÿí o ¾‹rCl»ÆÙQÉÃ*d‘:ÆP$-¤ãÞ²­UèßpV• ÑSèÞ³:ôAùîÌ+ú°Êvn{3…)?;ylp¿c%F;Ös¦EY’ð¶ÜÕí„F&hÀòþÝL¦ªËdÀÜçs™+UáÙ×v¼ñó4•oÕAÓÞÓhyÔpˆ—4öá†]uÐmê §\PÌZH­ØÌì±Lf/ù͇kˆvÔLô¹ŠuÒ¿ñ¤æ^|6³⊮ êõ)¸¦"–;4@lMŒdg3ÏžˆÀr Òº¥˜k_NÞ|¸ÉZž2¦ s7a†q6ÔÏ`*=&sLSŽã5†åùÿ¿Ž]¹™iz¤‡Y¦¥Ø¾K‡¬ÉÉfŸMÎb@y&MÆ,±Æµ[DñPúqFçÁuÎ"uªPNtÒÛaã'v¸q¾çßSï3¯x¯2ñ£YX¦gD>e2•°ä¨ZuÏà½× X¢¼’ ôW¤¡ÜóÌŸb_wÿU.†­"[óÐ~6z.ZOƒˆþ¯×z|]z&U–sgæS¼a8ðˆÞ_¶‡õi_ÜI¤_d1½®ìGóš.1¯nÀù9S±Gé>ç¿€üÌæTqÂÑe0™õÌ7¢û#Iê$ ú.>ƒ3¸h þÿÆ­Ì e)H’À|o]Pý»G/’E{ñÛ[2ïÍi¶|Ü3¹çzà•¯àG:ÇöÁgh}†s>ÂgöËàÍ+Kˆ[ÕͼF¾ Lsøl(T ÃS?×C?¿ nÐ\‡{/Ö¢Ïßçu_vØÐ¦+ Óý¯­îðS¸e"Kîüe£@ŠÖÆñÑ9æõNIX6î¥yVzg_¾™—ŽóÏ~€/œÙí{§1yGÂËB=,h€äù\Ï*Ǩ±+èq£2nHàPó.ïȃåºøpÄ•áü)˱"ù=êÎ\ÀöÜå§ÿ‡¿àq&+s ªÞRù|ÄõñPŸä‹ÇŠÃpÒžƒ4‹¯óU–²LŠ }Ò|ÏFÊRŽi1.?/Ké 5_£A¢ÊEàÍ=m2åE4,{h†á‡ ÏÆ ïÚË–I³oV€ÐÎ"Tx[Qæ›I‡‚y'u‹y“< íf‡ÂCœñ—ÁAO ¥æz·wíqϺe¨¦$LÚZ²Anž¬Øj’ãTšOT™ õ¼”ͽ…&>Ò¬þtE:Ÿ“Œ±Ùþ.~ÜP¼Í8ˆ Öö°Nq°Q-nL{Íu•| kÃ~€Â«åû f"ÿÝÞ—qX»ÜS:% ¯ö:v„àë%¨¡o‹Ö!¦(÷´­Î+a5Ý}þÆ;Pvæórz;°›w¬§ ÓnŸ'È›UˆŸtE9[Ú®cöÇœyì5ÈOß ¼/%Э9 ?ëþæ°Ž¼ŒÁïÃ4êñê©[¼¦ž ?jÆž/<¼=˜F•ê±Õ“èÌÀA|æ"‚n–.¸Jc&QÏ”#¯ç “(Ï_ìûó|mZ2^òcLñ_ rø˜[œá@³ÍèY)q<[ýΦF€¸ïJ8õlü–(Óï½’è&ÒÀVŽY ÅU¤ø©þþCεìèJ4b{QØkÍzíC§d»K»H’Õh}Ä.‰v€ŽÉ÷ñ¨“à›Ì‹ß³`’ánHl/IâEÓ38ãÉ%v‹˜2yq`JVá*¹@¢jQ¥;÷°6‹î²aŸoƒ^Öaf^Zšòå`IW7sw\g/^õÍVR¢çËðéàNZPL‰å£{ è¡9G!Èú“mº‹y˜÷Y%„¤ ¸*þ,wÉÛvP+KfÓ•q|ŸÜŒM’:çޞƷá6œË¾ ]/ÖÀ%µÌ)Z›D’^dÀ›¼xjùæÿðTüâ6à¬Ú¨Î zâò6¢9õ='Ôv +q=ÚiÄãn¹µ¨Ç9M;ÌÊØÿz¦éö`k*ìPw"«¢áEÐLæ}¹>ý³@‹†h¨“)_ϱ÷$沫t“ðÚ¸¸Ëm5¤öE¢àÿú7¬»^Öëlpfs%ŠR¶M[†WX„:'žáVð[¾äf‚Èj>Úú}¡Ý‚•3{a!E©®^ðó?½k¯¢N–´þÙ\#>RÍ— ‹"oãŒ!UrxQ7ؾ¾„W¦u33”÷o¯°¼Ö úäÃÙÊô\</ƒKïTMà—”Ú€S0-ƒÆl–i:#5T]w©]èÒV~[ábºèý«‚oϤ1 )É QNz/Ä£ p,ä çûL5Böç2u³WB/‹×§eÆãž`÷šâ»Œu¿?—{- §—ƒ¿Àd’:E\2oc|ÚRæßÂñœS6§6ã9©^ö;sñF&½å×ó¥ë¾>]KªÅÀIE›†/× 5'?0ßD:ðqöi(þkç1#ަk22‹žsf >˜Èÿ7W¹¶'ô`EE;LyÆ‘=‰¤É 5bÊA±x9»XÑ€¶˜ˆÑ´Fi²ã´ñ¹uo´%±º&ªÌéìäýÒN¬³ ý/òqWô0ü7û9*¸ K¿EïÎø|WÓ-îO¿ë0­ó4HßÃ|Äïoa¯þþæ\$ ³ùè]µ°¥­‡1‹–"ÿ !ÂkªAÎ:ÿ‚¾ŽA4Jä%ŽÇf³Dm„ÑXj=3]hñôdÂïÄK~H˜“©n èb²ÉI‚œ½lŒ÷…§ "Ÿý‹ËГ7ï²Ãɰ&qϦsëydÉÿá¿øå(["×ÇøØx±ï*2¡-<¬6ßÅÍ1ørZ¨š†¢Á‡,¼übî|y‹ñ˜ÿæße™5½ìÚË•ßë`ÝIê¸=ƒÉç`ÕRx©#Òvrx>ûÓà “»l=ä%n«¡úÚvEòMìO˜Î¾ï–d,ô)3ò­Òç¼exb.ƒU([[ŠóHßÿæGÏŒGñæpg`-üî‚Ï$Kât…!Æ3|3‰“^ ñgؘ ú1/«¶ýf7k²¼Êwðkr&Žß§¢{?r¼—˜Ã±¤·œ˜IIŒÏáF,¾w<îbFy!ZCœê ž®ïY¸ñ2ƒY¦teWe™¢B—!Ù`œÉÎ d'Îÿm±L¾K&SÑ„¬ÿÛ¿xÚ3ú^ÞK/õi+Ò¡Q‹ú•)àîû6móÀ³ O€Ô¶Ûx<˜ŸŒðÏ"WåãÕck ëœËÝ2«ãLÖBÜRÿå×ôAˆJbô)$ëÕªÙI#dá£;ðÞvìË`4z–Ë@¼Eœnp¥]£ÿ·R#âü'Ÿ9½+Ÿ]±´ÎWù2þ1sÔžÂIž/vÆGË™ÃGΣÏóΌ>l³Ž˜¦QëÐȺâ§ëi¢§(QÚ¹{:v’†~LfðQ‰‘xv ³2h?¨‰ú¯oªeÞ7Ð[èŽ]‰Î;wÈOˆÀ·ç3X‚GØ{é,ñé6¾æŸü¡öñ˜«ká¹>™møª½€ «…™ÓÌ96{Ö£{&îM"£ñÂxûé3öÌõuøÂ ƒÑAVnc æ-å u]ä¨'ïóÀ½çx£T”~{ ?-?ËØ°™øæ¹ ¾æ»ÀžeÆÜ6à­¹—²Õ㤛ÁÄ J¿Å ¨TÑ칞2`v^f²âaËÌTз0'>yØdźùUáæ•bÌÏmç!jÃ9¬nŽ„›7'â¿`Ã|tx~Δǀu®,Î §C²´yº-¶9ÕåW™ ëmvgOÑy’ÔF«µî*±‡ªÎ‚»± }!µ™l_5€¿6Ÿ‚…é‚0ûƒ5š˜l 3úqIAŸIíÌÇ2Ô˜;ÈîŸY «åïB¼Êx ¹ §[l±zI ;”/®‘Q ç½•ˆâŽ fþÑ_ð"î,ûå4Užm‡¾×Cíf"½•]ꦂd¦’9³A=vN1ÇËùïP5]—öžºË|F+º…çœ|*Hºuf’¦ÇJ`°|éé…xÿ &YؘÈôuLàOËÀ×"Ç`ÑñT|tp} ~Ž p}Øïßl„M"yõv.]à HÔ]JÍÙ c_Ù -¦0£2 Ûƒ,HkÃMx4ë5ìb®Ãþ(âºÙ’LÞ³F_‰À©;¼$Eö(UOé…íêŸÁöŒÍzÖ¢H–Ü-رV…|u§çå[ñŠÌ3)¡wÌ÷axa Þøp•ܽD3²ìa,œ°VƒwÆ›Ö[bKæT5˜É6ÎŒãÜ͘LjV<ÂÖ›1œÆ¦½0øv/Ülþ Þå ÈÕ¦i¤fR4nî0ø¯—g&ä“Ä'¸÷ëŒ ÿ£·©¼CÐR/r LÉ‘”RXl6ÌÝ"ÙØ—‡?¬Üpgˆ .¶÷ ²PùÒ ÞQ÷N‰¢·/fäÇvÀžmÐÓÅtÕMfãø«àH² »¿‘½&0¤Ýà'$Ñ=¶AÑ‘äÞL*vfsn^ c»­71mmºïv¯Î£‚ò8øóK5§’Ïô,Í›}˜-ï$•:겺{`N&ÓòqÜh’b>,» w–kÐ VÂLê1â@'áç|t ¿ƒVkr±Gç Z„Á–šXy? ÊVOÇ"Es*©!3QÿfÖâ±ÞHVnoXÝ]˵´4&¾¡b¨ýO,éç¬]0û >L£i‡—>÷çØ£ ª÷Qœ‰€{;õÉ]‰G°=³“Ót_‚&Nâ#×vMFÁ§ hÇ¿oÌcäk¸ŒÏ•ÜþB¸>ýhà܂۵¢ ªã ‹>€w®ËqídzvU0ݺÇ|Þ@`Y#~Ús ä_·SksLŠÄR¤’FIXº@‚ŽJvÏrøöú¥Ï’ÉxÔþ¿ÙÒ‘ÖŽ4>èg®ë<Ø£/C¬½ÿ2±—MÙí —à‹ýÑðÛC—äOâ'ódÏq{¶~Ÿ¨ÿ¾p46`BÁ(\ZoM79¸F2üdyÀWB>æžÄ=7I‰údbèó¯M¢Ñi<Ä çC_é^Ú ÁG›ãHàúkD`¸Ïžò朋Cüõõ ˆž¤C¥¹´ áš-ÜŒm"/!8A‹ýq±ɦýäËå¸ö<¬1sr9Ý!¹óMÛ™è¿øÅýlûŠýÞІ#ó¾A슬f¥™e.F¯M%-N{Q±L&/ ¢‘/3颴-ÀÏ]Mî©£~NwÝ—DDT¢]Ö(;gwÓ_F7f¬$M ©*]JOˆÈô»¬Ç†+(3†Êçz`Fr‹`±BˆZmÉg”}Èè%’¶Î€|5Îí'ŒHÀQ*õüzJ0G¯¡×47š“~“ÀÿM8aä€/2‹óaë3´9€ÏßÖ-þ{—áˆNEEÉ2¼´PŠú=× V6ôG½?ü’$/²|€s< ®¶FP»¨îéiW0Âõ Û.KëæÖ1[+„yÞ3Ü_~F¯³å»:áß—S²}pÆm1¨™ÌCÑò)`±Üˆy AóK^3¯fžÆ±åÌî·Êܓҥp³O’Û%H *G'øojRÎèúÉnÍ:=BâDð¦ õλûëß±$s·I+%®¿®CjÊt¸À_Ì®oÕ%¿_>„>Ë`ý|Y¨s¾ “b’‹/,æ5ï¯qª~íajCoÁ Þ?,˜íNo?aGl¹p¨ù8ŽäÁ5Õë¸W@š0êÂÄnnÎÖV¤‹oD1VÇYa1𳙚ç™Ó©£a ´_Ð'yÜ›?àîî3üîÄšün€˜½›¯Žq®0cÛVU±šÕŸ S¤üyŽë·‹Ëþ™ŠÞ‹Å¨Â^c’¢L·ŠÒc<&ð{eä0ÒOþà‹ølY€aÖ¯ìþÌŽ@ {W,U_†7Ow Ú¿8îÓ¾kzRï©}fŽ1ØŸz¢h] [–rÀZ=œf DÀ^Þp ½{ÉÔúF¸4µ‡ë0M—ln¡Ýþo˜× ¼¤oµî<ÇôIWâb0ÔíT¦Ó“§âbÍMXl½–:;…ÏÀXß ŠZkQ2 ž.û±'ïxl7sŠ ¹;í;ÎMvA?Qiž4™ò¦jƒö™Z0E¾žCÑ$I‰ÚÅ>ƒ‹V̘Ë/nµ±=v˜D°Ž¿O2w¦Ë°žËÒ­µëI¦ûЉë_‘ç³QâÝßƱ|ÀKVé*Њ%zä}ϼ#ÚÈŒž=ÂS¢’íÉÜÅ$vF»Óq3íçXòî.çíZúšÌXú=e9vH¿\«@ÇLtßø …ÿœfv¹‚ç‘pÆs›6~ºÎlU9Ÿ6Ãçå{ñåûHL»W7{‡+õôÍ@¯k¸Áy>èmÄG]dç°ßS¶Ù}œ ÐþFQfùtŠlâÇâAuºËx+=ÎÒâ v±>¯|ˆ_uqhêEvº÷ rôÌTöÍZ”‘1£žp‹±e$_Ç2.Y &ðÿ¾WÎW1þx+óÓÂMœ®EKU„Éh°ý´W£®wš.Ý*F g^Åö@OZQ–k×ì2ó,éú€öÑ)Mü8¬ Š!uL¿ëAì[÷º«à£c=¿V òþ°F“íéì£,&½Œ¢{ýžáƒ{Ðj­(†Zˆ¡‘S/¸ù¬g> ôÍ_EÒû=Ÿx)„Àþ¡s¶^~™›Û?ŒG™œx>p™{Œ6ïÛ„n/‹1Ü^¶L–#‹‹·Ñ/G¶ ÜÚ!H6id£\ß3œïs!ª+ëµÓ`a¬=óxm†K`xÔÎàs›àÿíùÑèá]ƒ+ý7qÖnŒG{—Ø*]Ëèጻ`òf÷¿ É /X»2-øy!Õ~(P)é©tÉFW*¥r†šì¤ÒDáêQzíáthÓ avΕ"‡C± É ´ÏYи@pyþãᣒ*Ù îL»;,q‰ënš¸ßÅÿ>Á¸ÙSŠð9ßÀ#×Ù¥'vÑ Oí©›’ Ôÿ‹eZ1,ÿÇÿf;3ÒYûaçóÇÜÛWâ©ûÝ3¸~ÝúUR‹tÎØAö¾3¡²–¤ps =üG†çù ñ‹wƒYØdvÚ·9 -³r"þ¡|ô³ÌxC.ØACNØã’î4<©©í®)àÍð–=b?ê!sÂëAo  KžNÿo.4yáw‘îýùü µH+ǺÇRìgÕj2C… }=ÍÜLq'‘ÎÁ¸%œ<µ²Äuºêï4BrFàí†VVJ–‡žÜ¢N\~6“ É'¹­ôÂæJŒÿ®ùÉL×]CµþÊÑ™VGᲑñYÊP©q›ëIâ.A¢´K1qf0}dZëÌ· ¤†Éõ+Á´g§qê3 úuæfúmIØ×œ .?çA^j'(9£ÿ¾øãbα9ÓM1rÏw4 kÆ3žwaíHNÅWkô9ËÔÿá»=elêæ³LîÚÜ'=;qc#ÎXo…yZ¡,XndüaÞZ¹Âü[Ì ß(úDSŽŽ˜2ÿÍ€^scóüY):¬n` fè’Iݱ`¿Þ‡{ûÈoöU§6õQárµ‡âí¥mpA¨œ ч—S?€£†š ÍC+1E¢,À¬LRÿ——YMeYÌ©û¿BT³F†çÉ‚´ÚdÂ<½ÃUïmÄ@>.ÎÔO†=kü˜ÓIãºpÕä+c·Ñ2»Üë¡íÝ\ý¨p‚ÿßf¦ã`ñ+ÆeÕø-³œ~¥H£§‰ËýmøvŸq¥Z‰l¼@ìöÓ¸U’—œKà!Za¯IXãІOÖ‹Ð[7°™ìœˆž„mŒ™ÃôôÔÁ¦7±ä¿ÐF…qì³O3Y…š{zPÛOwˆGÛ>šÓA}Öþ‹ÛDÈ6¹7òŽqÛØ@}«0%OÐ^a›+„&î¦6{S‡c1)¤>h)ÝI·ZAÝL:÷§<{¯=Ÿ,WÛgÛJ<×k‘Ú $îN8§Ÿ¯›½Ý  “Ô­DI>Üú¨‡·;,IÃõÔ}íUxQùÇÿFÕ½áÃjè)™H[ó º»Ë‹>RT¢î¼ô}ÊG\<û¶ÏŠÂ%úgàûòxr¿Ö¼¿ÔÓ2ÿïl³6¹c‘Äî`'ù¸a= Úuºt°Ù˜è-"¡‡¨ó¸—¿¶:ŒY麗U½h&–LmÚúé3Á=õ=ÞÒŸÁJSèÂéáÐùå èIøÛ­éW¹t8PDú/*_A?¹f~ñÀªé’ÊM ¸W('žeë¶§ÒE0%ÚtT)fÚGáà›ÇÔÈ»?úø/¨>óÇCHÛ2F©/ »3K’WÑWzIÜg]IáÃÚãçñºF›s¦ü¾‰+#ÐnúªüÚeM0ù˜Ñ÷+³#ècçÍü×b«)±{àÊŠ »ù0gþ-!ò½e ~À›)ô87†IX¾‰N(‚ï Ý n–Éä]ÿGVî¼síé2hÞ±‚¼úº|öÕ4e,þv^ý|±œd¨;£BÎÞ‡•Û£1°g1êEgf¼[iй“ÂÈÛÝî$¨þ ”?ù€=cC°bœ·y¹Ø=[’HnÀs¥†×# þ›ýû Ò3¼Ô륙¶µœ³Ù^~K»‚(a|$¿ òŽÇ õ‡;êG eŽ),6UÃòMWÇùçi(uƪ󖄳\Šÿž¨ÿ…Î5£kõ¤Hê[K¢X†›‰’VõoXÕM…½TðMÖýZòÊ}*ž‡ƒêð[vó;*{1’Ýp[Ÿx|YÏ„i݇öMåLæhöå;@F\Sâ3"w‰È³Û§i¥´(9®ÜÄ,qøÃÕ.—aeÌÞ3Ëmñ jƤ­¥ñ-(~K´ö«ÐiUö0øB—òº×1z¾­äᔥ|ÚŒü»ª“³Ç˜”•jt}ñ%z3ãü~©F³bn g¥>­ÏõàJY=ïy™ˆ$ää¤Î 3¶¤Ú[¼¨û—ÓÌ·uÿ&ðOî’ÃUËÅ1ì•+ ÔžIçL¡§¶í&_Vå£Dt7–HO¢IåKèJ×`YëE>·wÀéÄ©D·Aÿ¦Gÿ6ÖA&Í¢‰âŽ(tòÚˆ—Ïm#¯:±:+$„ÑxË^ã¸Rƒ93ȳiáÔ}î:/¡ñJ&8×ùÁ4¦àM6˜Ï8ǘš1Okpå¿Ý$hÿ:–.Û"PÁV‹™¤Y\RÐÛ)IcНýïùÊz«À.`ٲܘ»• |Ø\˜w¯ ¥Â…PTӆ˚Ñã}IÄpòUP×=D‹W~ÄÈëÉKËüwÐ|ˆî¼À™ÊíæöÓ@ºEG7ï€'s[àÏÇP´\M§0ܸÑy·+Ñô ¥áG páÁ·xÜD•äfm%‰BæLdú /½“´ÿ†£V²îp­XøØaZ°?zâúgvOl®ícüIË9=pïdrkp VW²Pí؆‚u ÷én^)FS¢U¨Uõ0”ýÀuÞ`×÷òm1ó®[óã7”¬¢îmØçfQózëæª€'0t½§*®À6¸wY’þú-Fev ²×¥$ðÁö,ˆÙÓY¯ô}´Æé#ûµb+n]öMõLqVÝz{ÃGQ%B4žà/~\â|SP¼ˆ2¹1ÜÈÕ•ãÿ³›ã?$@•t`DìŒüĺf4“¿§ˆÖ0¾BÐ šúõÀ‰–%c2ÖñèH³Ûö@„”¨£±o ³ãÎnµæØ\‹I÷†AØ@ 7øÀ±±4ˆ<ŸŠKâ¦B矛¬ã¸wû¢.€ŠrsáP¿Y/<ëTè.%k²_å8Ùtì&jJóÒø¨f'%Dµß0–Ž‚Œ|Ê Ò‹ˆû|*òWôͺ9YåètÌ“^ÏX„›Öσ¾2Ea³Ð/Æ—yêwž|<Ó„O áaÕ ¦ö„ ‚åEœtRœ·áŽÈ›+‹W]‚ 9çòDL¥•шèÖ×0N°öqšÐK;†Y^˜6ÿ'Ÿún8 _×ÐüxQl)Ua÷~ÈáÎ ÐÇzÛŸ¤Á`Û|zùVø¥Í€¿(G´*!w ôŽ—Õ9½Šç"®H™ ¯½ëñßÕèºu?¼èØ@¹¿¿£ŽÎæsj4ohbBGŽ2jVW`~w³ª –k‰SerÙ0€žxlMgÙ¡€z«îþZÀ߀HàÆ®¡>vZF&ûDaÂâs-!?~ ¢kÈ5&±¨šmô–#Uë QäÜ1&Ýù$¹Ý­ Ÿß¼a «.bqK"b‡Ð?£Ì¤áf;ÁëÞ0‡ÿÁŒ)§±‚ðSû!±ÿÄ_ëÅ?÷7cKxÌZs]hB¿7‹ÓD”"¹¢üdÉi«®w³?åû˜¢wîx^–½Û†Çy_€tO8*WÜcÓï°ýšàõæ4><ØÃ”üJd«n?eÿÞ›Mµb `þ×.ùô ŲÁÆ«0òþ8u‚ –¿—Óð›»Mta”×^E)XðòÚĵÔ÷³Î"žÅë8p?KC‚“HjÉ>Xsüê{÷¢bíR¢¿à.ZØìŸˆÿ²»à‰±ëný˜ŸsÐPðÓwUÁ¾ôk s2Ý•ô?= %=¢tÙ? vMe2µØnHö•xƒÁÀvøœx{ôaNÁ8e±›l½)ÊÈØåcµÎ±Y9 Qª*…uT&ÈǽRU Ð3rŒHïæ°Å?“Éš€O´©>ŽnÒ½ƒ¿A‹Û>½çÚd°d~-y/u–³RõGuì˜ïscfÖ䟩<´Nà3GÑ9˳žÁ†{ÉÁSà1|ÔeÈåI>Tu¡inF°=eY# û!ûã ¼*›ßÚÙóÌ$&—¢&øïð‚m7¾Áª«%̾âT”ù*Nø_JÒŸóÈýmGáæòGܬfzra*±ý¶ 6ݸЏ"Њ<Ú”Hí2€˜¨ÕêYL<já«t=ʾ™†…¸J‘{ãÅj“ÞŽm÷žâ¥0SÜÓANüUŪk 8éêV:›/ƒ\Hjã®,¦#ž£lgæi¥ ÉÂ^ ÚÀ˜0ekê锫Îh‘‹¯T¼iõ.Yfã)ôæZVÎejù 阤,[±˜¶ ªÓe™+>tq²-)ÞTAO|FÏØŽrîíŠ[SiȺ{0˜’7¡ÿ\Ÿ>f¯¨æ`Êçeô߇ìù=Æ:¹ˆ‘6ž>¿Žâèüºê ¤<ý E=ø)|½Í̳ýÓ"L¸# Ÿï†Ýñ_ Æb3þ šF·]²¥›Ã¬à†ýiÈùÆüë.ÁPkÚôÊ~&á¹5«°£ÚXáÑ:‘ ’v³ËÖë^t0¥ïOÀÆ© Gžu%¡Å€­‰@ÄÌåYµ¶TxÜ›Ç}„¿Û/àƒµ}ÐÉ÷žM>øÃœRéµewØO¿¸¶1çç²\%ä1}ËòµGƒW’5¸ý€y¨]7cY_S‰ ýo“réí7`ººdÈþ_Ål·¸]¶ºGöÆ«Ux#èô•‘òc?0i–ÙúŠ|ÕËÌlÞÈxì:Êœ«Ã-§_Á¶…'éÞ±v»“ÃNË"­?ÃØ”­«0ªL+?ªEó¦R«zWZÑ݃+Ùï06ç4¼ÌžÆ6¦$ˆ¼8¹üT´¢&¹²ôT{<Ä|`ŽÛ]‡MØá¦;GÆmƒ éŠ#çæs¨Å´´óAZÕ\ƒ ›9Dèë>öã+¶}Ú._…Ðô¹ ã•èÏÂyðßí˵°\ö=n?ï§~›Èÿö›ñPÕgT‹mi×™Ä:ÀÙäiTGé=|ÜÝF@þU†Û¢Šàåj‹½JôÂó…p´ÉÞ®ÐÀKA—`ʵX6ä•)ÉØ¾ÖKÔÂÊ=g˜£=Êd^ô”º²ô#¬ÏN°ÑPÅW†¼4ùÑn<¦™Œ'²àüeƒÿÞgƒ:!Oàœ4ºOVg¿?â§47UŸÜ°û9Õß/¾ˆ)+d©‡e$“yû J»®¡Oö÷3ðÜÌ2)ã¶_°A·¥nÌÒHCÔ qhdQqÍVê²=ªýŽBâΦ‚ÿ $<;‰‹ ÒØ£Rü¿c±'ëúÜ1º‰Ûn?¥æÒÆ4Mâ¿j›¦ ÍÝ¥d¯ÊF2µ0_33áijB®Úì Ðõ ÷Ê¿f9z-ÿ$e¸0=ü\˜æ#¬‹_U.¢£xœã{Š u”hµö ÞøŽeÄ6Ær$ÜF5gÓ¯±žéç¡›×}&§C¶}σp[Ø'ú›ygb~œßDý[³”eÞ÷ž`¶el":y ià ®úúžÇlJ­•}ùÊÑð¥圻…5]y˜¼ûÇpc2g^!NˆQ¿NÁ»Ëµ˜÷Â:Å]h ÔAJž  ë°z´r~õA¢£2=œ¦NnT¢]Eðkw,Ù*´ï)U2JÖ$ÔXÛn÷5q”xÈ]\˜Ãí+ÜB¥ªàò¯œUŒ+ùûö4²S†(ÏëcþY¤1±kV25Ë¢€TÀèÉÍÌYÕ3Œ)JÒ#«O3çìä°s2âµÕ‚ÄO4g¸EŽf¬rbJ•CQ«éÿ]ÿfÇ9ºÏ†“ízîãßõùŒÎX!H^¢³~è2 Hµcˆ£¶(õù<•äˆ&ÁôräŽîç£é%§!P#n–#r³”Io¤Å„&è¡ ¿©AÄÐ_ÐPúÁ+žIv¯_Æ^ùšw|ײRϯ¡j›&¾Pù…ÍÙx-)+<…ÁqJ-F†;A¹ã2¸ËÚÁ!Ý.Lž%‡±/áÝç ¸ãßEÖ·"ÑæD3kl#ØÞÑwð‡ý Ž1iªÙ%Ï®ÂÝžý¨Ú ŠRTl­ ÊÔ‚Gñ༦pÓÄ”]­i ²7MÄ_Í\ŠÚǼgÔ•Ka£Uüº†i¾LåOü“w›ÓxŸ,é’K£"ÞG O÷$úC¼•‹·bÛc¬Ý˜#½´"\þ~dæ]ƒ¿¿,ð¶þ+;òŽ-pÏd?õ¤2žÚ3ÈÕ)1CÜŽæ­ŸLÕ¸¹ìðœ{2Ɇç¸ååtþÔÇ5¦ãpV»Òh=è„Ù!á°VUâ÷÷ Í…&·Y”¶ì ôŸ¤YÀ….ƒ0æÁeÒ:6ƒEõ$HY{ìîá½™…häÙ¯"#_Ï?ŒÊ”e}O]DÏ›ûaòïaPßûyå±z&Ú¿ƒUos&sJE¡úÚ ìº·žúMýý†ú4=:Æ.j“³¯Lˆ&IÙ*Aå„áçºxrÆM+‰@3UQjï×9BÓ× ¯Á&~*ÝöŽ—†Î=vÅÀÅü2›í>‡jncë¾ï–ÇßïP~Ù|È•8§eùác©.ýÛ LûŠÓj…Ð*$„®ú5tÏnB¶°Ÿ‰<†MZ9c<Ä}ö·úãcy†ñÿ†¿ '¡¼ö"ص¡Ä¢É/^°|¿>;ÀAŠÅâ}oij–—ýœÉ†z1¶²}N…®Ó/ÙÚ(wØ» 7}‘£Ã¼wÑfkL?¸?­›FÚ‚ΜX¨RÏêïâò±2ؽƒá™'FEÃoá>£Æ}XPæLºÿP.Ô]ãÂÌÙ’Ô~Ä…\xùܧ݆äú]äÏK>ˆßäš]\g'oXš‡Ø¸ýÜDÿ[ãû¿ìŽã)Üc¯%‘–(RËolïtšÃù‰…¼ ÁŠ“¼jiúeîiÆâ  ÜžYωߵ)oqH-=u&±Á‡îà¹ý ¸V¦«ÓùÉïSõàyj½>Èy­. =BÞ éñŠaŒ É”WÆö˜Æ Ýe3%œ¹Cbt{Þyì:¨„¶qàkëÊŽíbê^ò@(z|ï"Ì2÷»LÝilǰ Ø¿KÂõϘƒgÝ#U©» ÿ„þ“;¡É&ê¸Îw }ûaì’(<ø“ˆºrû‚ÿ`Å|\éOuNŸsFqæ‡8iƒçt#èÁE–°áDŠY‘ÔÄ Ü€y©öåDØš€œi9–ï)e–«Ï$š‹©!:B kŠ.x&ÿ|‰ï–)Rw?eê8W,ºnÈnTb ×áëD#дÕd6+Èsw%ªâü*Z¹DŽlio"îÕü°óÑ ’Ëb\}Í™êÝöW@ô•Ýc÷fü¨EöN&¸Z\†ýê4Ña14KƒFNÄÒþ,ˆužW²F”çAóDý{ßh E5YìXÂ-Ì“[IÏkáø³EŒAÉ.ÎQ]Mø¶9¨.Õj;Û¼ºê†ÊÀº^C náv!5ÊK§ªV–'¹…9ε&‚™Å¨½;OlUB™ÉÚôHº0ùwöÔ9æ3Ü/üä}õ_0å÷„àf2¹yÃs^Ÿ–9öãŸIš°k 7¾ã4JEÁþHŒîlÁh×ãÊìÖ*a=ÃÖ‚Ñåïèi{q¼æùBçýT ­ßCÍy º½Ì:ŠÿíÅæi7oм+›ÁÈÞŒf³lž‚áëÉzv7zÍýÉ.-ù§OOàêo‡w"+¡ˆË‚c¥™5¬Jôž„¢Ï‡›xþ¥qûÆCíî–°MŽ•xjúñš«o÷üÍ,­™Dÿ)¾ÅW-¦töÔ.V{˜2ü ̪í.(ðLÏE±’Ã_Øš²ÇŒDø6àó5Xu«—lßNwFX0[M]Ë_O uß&¦MãûqŸ´;¦ÃžŠäTf6Süþ*ëj°€ñœ*I,ÌÎáÛ¯ðEãdr^Lço^A­ÍN³E—<€•ს‹ðéÍ<ºèÀ)”tZ‡_=g®5²‚µÊŒJs'´¼Ú 꾋ÀÔf”}·ñ†(KOà?é€ÌÔinxIoõ»mK_]Ï÷âä_¥yña!JmK»Å¹ôÒ¬x$Á3ÉèœháÛ²älé}Z𯾟K?œÌÇïtÜ 7 'M’QàLy­dðd×Str<ékO½â à©§‹yB†D°þ){¸¸cîÞƒêè».BÖÈÉÐ-|eLGÔEÖ.yôÇüaNþ˜¾ÄŠÄaóãc¸´v#|ÏOÓHP[%_üµo 6÷•1W#ÊáΓç¬éôh|œ¼ˆ®€cwíØ—wÁÈã4Ø{ý@R° <ØEª#B颔+ü'¼g2¦mÝú?}€ ¿² µ( ™.–±ÊÆ÷è‹9ô³=Ú΀ë¬\FË^ ïŸÀ§ …ày_ézãÎT1šÿñÆ™øÒÙæ{kÊ·©ÐwY•O¨X:‚R‚X £WlóæéXÉiƒèÀÖzhÝ‘&r/ÎÁ5½EðÝ2g~}3ÜirÕbèY›À\W6„W àÛŒ×lñùxjdúÔfSšßŠª¸`ºýçrð5X8C‚1âì^ØÁ†U\À'µ‘ú|\ëðqzÍàÏ(?l`ÆøÎ3¾[t&âïâ¶…]x*l £`+†ÀØé´|œ Î%ù("YÕ"¥x¤=œ½öå Z·F@~œ4Y9pÞ`d>Û2âº4öáó-¾FSÈÓ^AÅÜ‚ˆ  <™‡5žwñr:!~'¯aXk8\!;—í"Ë«P"5‚Ý÷ûu€x¨ÉÏËå>Ê(¥æ«÷‘±=¸,ƒŠyçPàý0è—VcpñQvÕÞ¥ ¸H‹ÿ¤¦µæ_P€"˽tnÍTÚô¼“k|ò4!>:·,îJĪI-ägø Ö&­Ì.rðÓÇv‘ÏGçÄfakh3iô;‡T¬‚ Z —äÏ#†'®2÷ó¦’ÂIÄ5±½’ü©GFÌ2Í‚y±;è|ãˆK‡#ýW˜’}‚DQ;‡äUyÓÃÿ¢ÀP­”$›Ù“¥å‡ñ­ù$êÿù*$hé’äÀPzr$—Õ™åž6À-ÄaÖ>ºóg»;Ô›|\Fâ/Ó: K>øbÿç¸åÑF¨¥ 7èWß 4â£Ë[ON俤ï\¶ýå5¼½â,ü6«î«‚9¤]šL.Ý9 7¶å€ÞXwìì"Œ|'™f8Á8LZ /²5ɬ–­(»î<ôÜoÖüÅNº€ÏxmNÀì‚I0]± 'Å€y¡õg:´×…Ýé3¨‹Ó§UNõÚ× /öKâ 1£T‘,v#õ£0⎭…¾4Rm72"KÆŸ»‹‘^‡ñjìvÞPØ>0 |EiçrQÜo¬‰?߀Êßûlb—>½ŸÁ7ñþ‡äƒ_l€ÅAx —Ô|ð†¼&üÌ¿„Ëf¢ ‰ä©Áä2µ m2À=mSÀô?IG»£“iîo ²É_üà‚îbx=íu]í}9*¸í,L6T'6†kØ`íV¬œãO…îLÂpßi\÷-óÉE¥$çå”~µ{'˲ÏNàøcá@Ò 6EQFXx"ÿo®YÊìÝpF‡#àð–fX/x‡± Óïbô}“ùðéïè[üÎ>1;ƒ}[_áïUä¹ÝPóÂ’¡êâ°– |ãF¿ÈG“¬r µ¡3d2¹ßNõã-B·¬{0#!ûëÔéÒ#24íxL/_l§¦¥Eg“ïvÕ6epãz “©dókø&Ú _Ñ3lMQ&õ•=à8÷±“íjÀ±*Ýh} R´  CƒXävÀЇLF³J…¦$ü‚d½p÷“?~w\›·“ÀqºÉ<‚ùoýZ(äûa‚ÿnèíșxw\;‰DÏ&JÆ+Èj)-*ºÃÚöæÊ=ߎûëÙû£[`øMBÀlTû–ϸMMäÛMèàÕ©øîF=dv8JcLÚÏhRŸª‹›_…·2WÁsºÝÜ~÷êßadòÁ5Æ CTjèÆÕ<$­æ&˴ʱ+ñíÛÇÌDzdÈ{\fÛ [Yó¹óɲ•IxÕ>’¹Øáv+9ŸÆ=ï¿Ä»xXî"÷ÚŠvø´ùºm¶á³Imùc´û%JŸ‹Pç}§ñíÓ\0½¹ß„ÒÊK?—4mš÷e™¿Vc"ÿ÷¦"ôÙ‡Sîà•°TÝ?Óö˰$Õ¼ø”™½)è>‚?Ûž3}0ØÖà vµÀ‹Ïl¿ÀePÈ»€*Ö)Ìœƒr„ïº.#y¨š¹­ú½Qm ~Gyi}ž9ñ+•bî:yÓË‚¤Ôï(+Þ£DG _c~çLKï´[zù+¤,÷FËM´æ‹:=xÅäv‡јj¬]Y¼&®Y«„w=làçÉZŽè*=¼¡¼„˜ûo…G^£(+¶„®í(`§”\bŠ3ôH”öF¬«†Ø çÐ3ç!î¥ÞðvÁ,Ê/ïHCÎÄ–æ DñCêDü†¬ee>Ú£¼è+»l¼ˆ—Þ¼ÃÚHÜM’Ãáì:<Ò×B.¶¢’ÉÃx´ —œƒº›%xOã ly¯EöWo%¾Bä\x5H†DÇ<‹J¬2†·=ŒVÏ!ü“>™¼ÑÙÊž»«ƒ3¥mŸè’©¯v³;^j@ç]$ú Ì×™BäÛ°6x¿x„»ûR±Àhµ# ïæÜîö$^Ç-‰RlZ*FŸÈ={•È‚—’4 l>É‹Þ/Ÿ½†7´ƒðáöiN~ÅÒÎÎà<šdB콞°Ê'mÎBdÌ^r}N <¼–½`½·í6ÊgÏY¨ÐÄ„üóð¼{0õ9aŠž]‚0¶CÒš9_ë-@/ô(ºÏ\CôÏMG§K èû§­“#D}§Î‡Õ›ÏÃÉ‚ôÁòPT]ÙžKÃimÙl}í,²o@âþú£‡¯8™3]Fö-‡}ã:R¤ŽmÿWËn8Ðñý°<ïãÇÍÝ,yÓ&|£NÀ÷³:¬‘ý™Vó6ÔâKÁ"ø9”Üßrô»t>rXJ¯¿îÇ.Æ”ª”Û¯ÊbÚ-v s"`ß@¨í/¡j6SñJ}ƒöMøßGk׳´€¤ÆoÁÚr,0›Âl©«ãâ6œžªD“³ÍP±H{¾a*1ŠÇÂÚ•°Qωð6ñ0/O0°Rþ=²ÂgÈõ¢(µ&_¦@ÎÎÅDrÊ»Ïï2TZÆ Šÿ¸ž A4𓊠²$zQ$=ý@“9c9 ÆZhÎ3‰}w¥bq!¤v·½³´\gkÁbö-s¥ô s{W,s÷’Ý<Éø¿×è£{Ðg…Y Í¢¶R .dû%ÓIWñä­S%P‚jÛqÁ¸×-WÔÁåRäêUë-r•yÞý‰óò6Âç·qìÒ,aöÏI7ÒóÌä§_“ã ožÛ:¾Gë8ŽU‘$¼5›ŒrÔ5§¾Žw¶ MìҢǤ˜Ëd`¶ÑÄçBE¾uÂãΩXT2…ÄÆßÅî—öäay$š¬¿.2§à½ðK¨Ð:Å*Å;À—Yç1ù÷õMð˜r™¶zVoÄÄ®,<|g#ëÐgNgøèçipýU,}†ß7 PïYñ°g³Ù×Ïe>¼6£÷Érh>ïfcëödÖAl÷…Åvñ‘DÚÚÃì1ƒ÷îc̤\0_pb"þ…ÿÕÂF}<IHG4í5(ƒ„'fäºË;ôe§;’±3ÎÔÐîìû- ;šºHXn"Ñ:·Œ¬“»€|ÍG‰©Ú*²Ìņ^wBìo êÏÒ¼‘Y´Y*‘.ur¢êdÉš%¢‘ÕJO›éÉ­ŠÌá ÒD•ç-Þû7ˆÀɇe¨`ò>`£Eè·þds¡&´ÔóÐëÇÐÕ¦RÃùí—¼ñÇ›*Æø•ųV‡§ã""÷yEÁ0¾Éæ¾C›Å;á}ê°¼Þ†6`yx,~Ûòœym#Uw8ÜãîNJÅ5˜e25Ñ näôa€Î$&I/›+b>µNL1œ*Ô¿†”>Kj!üÙ¢ÕN?÷§…/2éJ}Oò|r'ÖwÆ'‰ÎdÒ==bÖFnn N["¹· %¨~›sqr½s+€ˆ¶Ô‹oðÐÖjºÍ®4@ÐP,QjuŹjæÌ'¹¼Ï{Œ}Íë@öŸp }WIÔ]{¸{7Þ|ÞÈqxq•ÔÉ‘ˆî8t~":¡RçKÑ);u˜¿üBÌO¿ÅªÆcrõy8Î Çq¸Ü)žm{ÈÃÜÆ ÐCÆO“®_À9ZÈéᆔw9|¸¬·ÕÎ kªayÖ]Æ ut_¶Ãܲô}íC n`•A> lb…$‘>õ§ 5°“IãÒŸ`Wý$²q«:U°xÿ*ÃPªh6:… Ò¦+Œ÷UÔ-ÉšjT˜k A°ÝMØÉïz±ûr,x]ÛN½…g/Ö“¿|D$–‡Üú‰¼† èPq«ðá]jš m}U´e®"=¢Ø0ñþOì²Pð=ÆÈðnƘ÷s¡Õú",òä§ó4ñì‡(êïÖÕ}èzâîÛ»ˆt5`NoŒÄø9eÀéžN[u0Ã%¨6` Üz®æ}¨Ñ8Šñ»öÑrB4ã••rß>Î!j”ÿÍ$RÐûu ¬6ãçeáqüÌXeA¶UXÒG'ëðž8ŒZ_Ú«ÀëˆöP$…Ÿ_²±nš„ïõþá¢S² ¼fœþlÅ»#ü¤m¿eZß­¡-̸ä!VSiÝžýäÕ-r#6¬qøÀ6íO{ÎceÐáÚ‰ø;½ƒI¢‚hõh~Y¥GcD7‘0¶ÛšãMwDœ…)[¼Éò ç9¹VÄëÐÂ(1úoË*ÅHw7SÀW\gãHÚÞ­A~~U„$ÑHŽ]˜ ½§Í˜mžM†Ê@õd!’±Až[ì§·j ™  w¦]c³åÊ7(ÖþÃä½ÜÁ Ì£â+å { 3aIçYìtÛ§ÖöcÑH]03 ܺw‡10$Q>”;ç;C3y>'‚Q9ð‹îõ¿ r‚«“M™;°þt%7ó³7=c5:ÿš¢qž*ºÍ]Œ?}Û!é¤ ­?=²ÅÁ¥Õ–øÁ‚8ø ¾Ýðö^ë]c†<®äŠªs¾v#ãùþ„nž ukMɉcÄ,áùj[`¢ú°jÝ>àUãîè—$1±žèm’ËL-`RÍÜ&ñßí­GÏößlT-/]àp‘½»â=Äh]º+„Șbº‹Œ; ûcüÈ™«Ä'>bBÿ ½Ág¹5vñº ¨plöÿ–">N&/+Ä0{v)™¸UTðÖ¾\ì+>F ä}¨OV&r¡fÒ;¼`äc=0k.Iú3€;ƒŽb¾¸Ø¥ ¡É^¼».÷^ÞCú&-?‹D&ä{aÉ#ÐÕº_׉㲑 ðúì&1{2 /Ãç# dËRPŠèÆÝ7Dho8}ÈþbÿAÛ? ©Ó»1êì¦d Ç„UèŸËS‰õýw[òm.Õ¼Oá”Øc®Íf‰:ŸWo¸âñk ·h6øÌîD§ýÞœG³ÚqÀ™—Ù\·v¢þ‰×ÜaGy1ýs2~¹÷×~lÀ{“ ˆëÇœ÷½GàG×c¨„M>Uà;*H¤ãžÁþ{×ÁÇ:“™éõxZ1$Êÿ´‰RÕu¼hîZñÑOÙà ìœO|¬I­9ù^gLÍŸ/e) ¢~Ò +šÎžÍÙÏï€ä€¯œ§«-IºY£}Û€¨þ­±ÑIláõ×Ü%YО*Oª«aÑûçìÖÅDI×^êrV÷$Å[Ó‰ äÓ¨8}}Ç“ßÝÃ}©•B¤àúGŒ»k,ÌyfÚ¾`7–Óä¸&àñ}±øõŒÿþùÖ«Xüµ£n‡,œõ~ŠÚ£ÌÛ'ƒs¬Þà)\ã9r›3èÖh.ÜTkÄcõêtñ6¬G÷§@Æ\™WhÇ+É– I´EÝXù*lfþ…:=†eÎ㊼^•¿¬qVC³þo07bQêZ!&õ±[£9]bô ?-¾¹ŒèêóÃŒ‹9ÀÈÑi߯k²I=Ú8@ß’U¨" ‹:®AæÃ-øù¾¼·[ÆæGû÷EÄDVš¬œ~»êŽã€Y+öKŒâk…íry6ÊW¡ó¶aO]>:ÅÙИ†ÿïú‡A=»¯~èÞ‹ N1 $G© Ÿ¯&+•©ÂãLV›Lï8’¥·<ÉýKGQ#à.Û.AZòùi^æc\·å,ý+Z ïšS w(ˆö®¯šþª ©¿ÅtLùŒŠ "ã¯Ù Ô«ûŽn;+áÕ' ý£˜ÏorøN £­xÿ 7KcÄm;Ò®æCþl”!»vüÄ˳á]ð|úFʉ°¯ã`Û9;r}ë^Øã–JŸäØQ“ [ðªà¶‡_E¹/X\# õjHÓC¯šé›§%ðbÁcƹp.®ׂí“NÁÖ›Ç&ø/$Z\Z󙑼FX;ê䬣ýèE~®Me-´ñùÀmôòå¥r3žs7é–1±#?уLLjo²¸¿ÝŒÖ’m$ýI–ûL‡ɹt-î!c !äËI˜ö¶ô_{‚Ðä\÷ò7ð锉^Ȱ¹¹lì2S\äyïûì$éÙedòˆ(éÚƒQwc™„,U¯DòcÙPqâe e‘fäQºÙò¡®Ys+z¸Kc{mjíß >— ±ÙåÑûzù/@Þ€¿hã/E¶ÿѧ*3¥HÕ4ðÐ «KÈc;g+MøŸŒÉ‹0ee>ƒÝ+aTJŸÔXÛgCÆÈÐ"î3 ~Èyƹswݬw&!£sã&.õw{>oÉPküxî+_¾ŸsË~ºÿ-À{Õã¯{:iÐù̼íÅõIÃpœû®ÏÓųæ3`N÷ |,x{OWâ!¿d¼¶‰åìφ÷9ÙWÖ~8Ø‘Žž¦aY£§Ö˼-¹»n3Ó^$1:kï³›,Ëq’n’5âh‰Pœ³ñNuÒ¡v}Òðžðãýå;ñ¿jVò=°§ûņ™Õn/qe²qPZCýÎcˆÛì‰ü?w"føÍti·ãð±Hj¼–Ì@¬܃=õôÉóåDLZŒ*®´Ä-ǰG‚â’ƒJÜɼ2¤¨O‚ÎrŒÆÝ“âÉõ—:DÞñ(wÔk¬?d†³ŽÐ ÚGaºq*t®9{Ø…¿l©AË!ø‘^̾Yñˆñq;Ž~ù³ÉѧÑ̪zaü\·&—W`¥qxp`®Üß·?¯C­‚!”o^…C à€Ä¸ñ´å9doI&¬8ªLÆr>3r{a¹wçžÙ¹,ÌnæÚ®0„MO·@½ µ‰b³T+!Á¶ ”#õ&ø/ô¸ æåúƒÕi’jswÿJ„F™´Ïà.g´è&Æv,f´EƒQ¹^>lþ§k—’‹W\P3ÃúŽ8B”P>No1"&Û…1FexüÙC>^ÈÆúî œwq È·5U´mÙæ°Ò¼_¡ÑÚÙX*¯/Ð÷÷Û#M¬ÆY^2t| Í–bVÞùÂ|¨Šß;ÌEÁéxÆâ„ÏŸO8ײñ·ÝyxµU¾]†¡Ñä¦q93wÇfKà‘ÃQfôQÊ#äS¦!ÚÇϳ<(p¤™›Æ·ZÃgÕE>‡ôP>dîZ<ÿ¥æAæÊ¤5ìœÚ(¦cFfymÁ®,cŒÛn…Y ³ÈÖÌ“qòViÃsÒ4e!}æ¬.ó`nWwârÊùmÛÇ=Ñè’,I£<`®(EGžY·ãF+Gcïµp¸:y5 ÎÆáô.,:àJ}äçš1‰òE—0oK´Q=[NEñÑ€Cü5¤H§ïJ§öî‚£Ù x«;¬ïoA©7lôŸT]H|Yqa;q,1!YOqËTÊÓ¹³«Çpã s&?Θ›€4NaªŽ0©:+IúªÃ࿵þ¤ê?¸Ý¶Êèïf+¶, _>ÿA Kk¦U¡o߉†®q©:¿‚ákŸÔr:J*à‰à 6¿³òêáSIsw—©(Gas+LptaöN¢»/L¡Ý0%&ŽS;_€<Þìë3h5WµN°ó&Ü9Ãly1Àĉúãò¦³èc~ÎÔè‚ ÷.ès8Œ„ëJ/eÃ5Ï€˜$½ëBì½év^VAVœ$8ÞáXD¤ÂøcAKH/Îéõ×ã27Q£[¯ªÑcÀЊÍApu×=Üçø€iýÊOM¼ßNðÿ2¯X,htç, | KÉo—hy8ÎkW ×Vg¿haUÂ4¨’Ó$Øðì Tâ¼³5Üó9âà3ç;„42wæø’ Çtìß7“öžƒGR/¡Û2%3ºÓ©<¤©Pƒ–l`®¾xŠ·º'#uÞF7i–2µÊ†v°Ÿ<â¨ÍùŒÇnïfíç?¾%tÑàˆ í`º±ØÉ®‘f×eÄ÷~`sË?²ÊWráèà °oY‡k>}gï&åàð¦RRóg6úkh{:S*óB‹å ÎÆž¡÷à=¶^ֈ⾀˜‘êDŒsC8šÑX똧ͧÃû£<ä¿^g+h…ô+„ÌM;N+ó¬È¬sê´¹cç&¾Gí#ט¢¦l–Ôйnc†˜‘ãÖ8w[+¼àðÁ†Õ¹O–ß‚3Q±Ð)/K†æ“/çš0WRæÍvÇÒM𠙿wfÜb2Å1äÝ48¼5W7ù‚†æß‹eñ¿üó,´²bo•кWD7Úü׋Œ-]©ì鸭˜öÚ†Äý¨·tT×~ìnŸÓ…µf#}¡Ÿ]êk‡óåœñûâG`Ú¢Iþ¸\gêø#aö²jÒ¸yÂ…~J¯bë^6°ÿõ7ïMªƒi†¹X<îBxèj¿Ðýô/HÙoÅ“M0o›*}°ù;f,e™PÏRXýï š¸Ü~S:ÿŒ%ÉŒ`•©ñ¸úa7Õ‡«ŽrèÁŒa ·õÀ?eªìŽ„îß·aÔÔG‰àÛw-À®ËÆäðЬq †y]—)uÌßt̸¦LÒ™gì!/dò sK6óšûf?T¥ÓJ°ç¼ŒØR1ìÌ}ªäb±"ÎØì^òá“ì ã|”ØL³]ìøw|çSÏÑ¥½ò$ùñä'µQîè–’ƒ­Ûäa|=ð]:ÈÄoAôÉÀ¦¢ûÐvñÛ{¬žØÓeʹÒ&g E4ˆ²ã[v‘ØV›Sre’==RÛ–ðâ;•ôm ƒu úTš•ǯ—Lðÿ’.!VI´ö¶ÅYXIǸ*ÊÆ4Ëï"¬wI‡Úk«Ñ£Ýˆ+§,NÏ\0 ŽÖ Fk{ѯ/ ¤àóEìœs9€"iìï\ö¬z-g`?†½}¿l³Ðú« JÕ'Vsf‘)u©¸–7—ã´ÈË®W?ÆÕ³+¯×?Ï0RÌÇêÒ40 <£~WÑøùTÒþù¼]ÃâÒàåF™áÔJ–óû< [ Y~úìÞãˆv¦áƒb¶\ƈpÔ#1`¦ 3õÉNv™¹&ä40[ôè±ÄÈ×¢™O‹ñ™÷Kæ·ïÿ›ÿ’66ƒ9*ÓÌ•®è`íËë°—•&‰k”‰¹Í6d@ˆ=xæ¬Ò ¶¹UÄHXO!ý*‘§Š6ÿ¼ŽQë”*;„'^~gº[Ÿ –[;:Ǭø<®˜Å=ó* k”ý©èZ ª$Åwõª“žUƒ^f: [9 ïç)âJ¡0õ<3Š£ði@ÛàÔËÐØ^¶Ìñ æ¿ >tî`Õ–ãkýRL]\‚5“‹˜ÒgJ¡ƒmžNÙÁmF0tI\X˜kË9ó˜)J¹„1Ú“ ÑoÙÈozì4ÿLÈwú"WŸážVsú#ÈrBÿòŸ3¨+œVd7Mæ-ë<Îär£˜µ 3â) 2xZò©Å÷‘wð6¾àW'¾ç#™oE¢N ÆÚçÌ¿5(>çYaB}¬=Éõך„.Þ†—v\cvü{a³ã™O—¢!L"„Xþ½Ê8™Z¡øæûع՘™VáîE× ea›¾Qüô÷:1K¡%-€Uóv oy0q{ `.ç¦{ÑïÒ=Ì‘ö8Ö‡?†å?W`—VeKs.¼„¼’¤tÙ$ªØ|á?Pe¸¼žF`p)sÅΟ{`ß><÷úŠIlE=é:<{çìDü×ÛÃ6-¸ÌÖ\Ry 2¯»ÀκL8’s»ºáÊ _´ŠngŸL=ÅèÿHþƒlцéþm6¤¾ÙǬV²ÙžygÓ E5Zì§€ÓhõXsüE°ú¶%½"§Í±wƒINNàT~Œu¹¿«$Šñ¯î%(°sfÊÏ8³I–èS¶o¶~bœ>„ë³Oá¡#kئö¶kÇ&x.§‹KN¤B«ÃžGÐúÅkÌœ%BJG…H¿Æ]èÕ&½ѰÂèèyÊx½¿Ìõ~34qþ;Uä°LÙ~À/êÞE¨÷,¿U°Ü¥“hÅÒ"îáÀbv]Ú+öF2ŽC¬sÜ Fªa>åw F•Q‚£‚Añ¬Þ^jèk%ï/C“?¸$.bØ¥±‹K¤MÉmÕ˜„J…¼¤ì‘=Èú®§uüôëVŒM^¹x3t*‘ýdKƒsF˜5ŸŸï&¶$^™¹:ó;{ùµ 5US&ÛÄ~@õF_ôÜŒ„úAºæÃ,ꡈw/ ‚˜‰éñ7#½ÿ˜È{¬Ú3ì¾e“äÒÁäÀ3¸~Qb¢þg9Æ1WßßcOLognô&ÁYÇÝäóÚ`\ÀÅa %vAÀdÒÿâJ\G;_਒>³'wžørÞ–p˜?šÐ[òó„.ÁFÕÆnó¶B¸ r?^b=kú÷cùáx×ÕË’…‡9»NÇã¹— ñàDmH…;²Ö‘ˆw0¶¹’ £•˜÷æ6sÊ—wèxì-Ò9Ø{®ÏmŽ %“&ðŸÖb[–±|WT™ÒbaB¯Ü]1=¼[Z¦Àù5ã¾ ʲ¢ñ\’ÜLÊNRßO*½Y\…íŸ)ƒ¬=­²1$þNpÄc9 Á~øÐ¨F‚~ôcþÇgœƒš)˜R²B } ÷©Uh:ªÇO‚Ì×0Î}Irï9»rÃRܽUÛ‡*™JÇk°jØ××xec">.uc#4èÙsY(ÿˆ_V³B ù!Ý A‚¥˜tó>¼2vc‹óÛðȯ¸üL|Lïd_9F2lít¿"Ž—Ëaˆ÷lÍñ­ìÏ{ºôç?Tú¼€„ˆŒ¢Ì° ]:m/f4çÁ§Ê ´j ˆ?ã%S-¤ÏâOA÷d謶 R^€y݈"“‡1çN-Û'E„çecy޳´aú y’Ö!+™ûVcœ”Ì?Xä¸÷~ÚIWIdA‘³/õ uÝúvXi@rÅàÅ÷^æß¦R¨œçÒj2ŒwÏ\PÓôâÚ˜VOøŸ·Sµ_lÉ!:çY+ûwã.º6õ5÷FõLrÌÖ™YpS€DþšLÜŒ rNÁè«|•½‚sŒ!êû”Q)2ÚoXaì )0<‘ŽÏ»Bˆ¾Û»Œ=8+˜„¤ÞC‹RM2tǼ¾]W7qŽ ßè ¶«Ð{Ÿ0mÈ˃r­ˆ¬ëk ò9:ÅÑ𑉥òÛÀyŸµÏ-e²ºÕ‚ÖX_gŒ×âìÍW¡Èš—Ö~7¢æ_¦ÏÆ;ð0B‚)¼¯EW7‘6;2þóqÒÞ¤ˆŠmG)q-BcŽ9¼Ø¢Û`Ï6ß þÛß»‹óEà8„uGq¯®ef Ð/—w0âƒöDrñÍR`ããŽ1š¥Ü)W’˜†™;ÙÿäñÁÜ&nD4¯:@‰ŠpЬè–Oßà~žTò€“ØÎ̯ÉÌPº!Ïý§Ög¶XÀ%{ :Ô"m³ ûÅzºf’<Ñ]Z‰ËùÐ¥sbÙ­ÿòÐUÉn¯xÛç'³»<ðÂ\züª-³ó™;½ül½•ÒÀ¼/šLä=K® Q—æf63˘.yÏ9®bòS3˜]¯Žq.Õ' œée¸òmå«;Çô.J'޵íøÓ­kâóOìV>\5«™KŽp>ï¾ÄôdKÁHÒ„±Q0êm–cá°úý&°pg‚·‡â«@ˆ|°B¶~ëdõÉcؽ¬MÖþc¿VëüŸð[  :‡‹93>êÐ7‹†p¯Þ'Üï Š‹¸æZ6ʉ‚´’$<®h`÷f%e•ªÙÜ%–ôñs3ê%cAæXS¿ïÍllùSõ>Ž¡§f²zìè¼®;LÉO¸;ð([ï^ƒë`Ãw9T«2 ‡%ûÐìÍ=ÔÊ:C…xë kC#N^³om¼Â)“%«)Aÿ¤5ôíx>ò5 –ùÚPµâ,4ºÌnµà!Å'Ó:Ř] NŠo vo$ ¾añ~[æEÇ'ú_ÿÄ40öïû2}RmauHVÅC•驨86™ ÍLÆ'·q‰»5i®ó¥†jÆDhÔÏù “êfŒbM.~®ƒ¼¶âóÚÐcŸ fI¬Å|ªPu *óÁÐu3–ê!õ~+rhõ¹úü‹,Îô¼v?†°Œ?˜Æ°shoè;Yy\4ÜhÀ®»´=ª„Î9 ð°á ¬ú·‡™ÖJ¯¾Ãfái‘è\ð G”3ÁìÒBú÷âq²¦¼‘DÅ—±ßÍÇ·ÍiÄMYŒógM9¹¶ò6ü–¯b 5ª&øÿ¼P>ß}7&¹²cýºÄQ‡õ\€¡7O3ëʤààäAvÕæëXz[…1¿°cÏ¡©„Ö?å!R—fƒ•þRzþokœÊONþ~ &¢ª´“N}#ƒ¢Ûúq…ëdrpã(ø‚µs¸t÷U¬ÛÁnÓaÏH$Û»Ÿ¼ ÐÀä³â$aß3˜b§1/ݦ¢×ËpæËý•0{ð ~1XšÍìÛ«ñ® È—Ö ïð\ž§J/Ó%rñlx@.„Üá÷oaZt®]¬\?¢DËǪáòÚxŸYÆÜÜ׉«Ü'ô€¨7N•aÎ’§s‚^Áù0iöæc~ü=¢¢e÷àæ¥y®Hl%N¾Ü*ß°EdyÐRZŸÝ¢HB“tØ®¾–Ú#ר šìŒÏkÁWþ ØÎÛ@oV±6Ëõáï nã%=è¿=•~>X‡é|ǰƒ®Çk;ë~É7±›—À»tÛžíšä²V¾ür·ËH2§9ºðãã »z!6ò³Ø©™y±_’¤6®…¿½GaÁ¡©$Àð £|Õ²¥N³4=f7¼Æ¶ÙØ-žSmÁ‡Ä“(ÑŠ‹6`R_2~úÜû'ø¯Ìu nrÚÆߪȾÁC"K…I§òy˜*¢ÂQ›v–-«™g§Ú’Ë¥Sðᆥ Í#O"j3˜¶ÜY0iì7¾“_Ç®óJ€Ž4CjÛuŽNì`ŒêÖ‘£?9ñ§SÁôÃ}|~àœ.0§B¯­ ™õïdÂn&éÑ Ó>ž;¯;£Bo(®O-ÅSqwQ M‚`•<[9î!VžüËV ÖÁ‚¼ 6úà{ŒøîEÿú}a5ó[pMÇwvÔæ»¨_ݬ$ˆî/Y*zŒ†…jÈÿ`ÿ«F7í‡x{Ï5Ζš_ŒdøuöQ¯-îâ‡Âìæ€Ë7æD3ÃóÙCQo9KfÓ¯’ƒ¬•Ó"F€cA…áÛd#R¢áŠ1áÒô¾úYÎ|Ö„$†4á)±FøSz–Ë÷5Üt?ÁË€ ÔÝE¾š]á>ïÕ$¦‚*ôµ·%ÙÃËÂO…䜆ÁúGè\!T¬¾áÏÀÏ6˜’aJÒJ¥¨ïÑ^ä?Gà¨a;-ÚÄ'KÅ;KØ¡+;éÜ€Hô(ª†ôá{9::5‘óý…>uApò ^{VA„™ Ü“+e”ˆ=ù®>—,کŪf B”¬=l?rUªcñ§úuºò™9g(F–8ˆ2ÛÁ‚.JÂö ¦÷jkÉ!r¯Ï؆ÞGÓÀÇqìÿ­Æ`§B¼[»×=ß?>ØS ꋵxm¡&õHÒ̤¸ÇÛ wé÷a}©|ò6ŠÖàÈüFæíÛÇü×ò+Œ©ö؈_]‚ð^ôSÐ|í‚ i•ð%^–,îæ¼hG\¾û Iðe„–JÑ{³9í²·a¬Å™l0!Ë´!ÍJ‡ÐZ Osj™GíA~ö%,ꦭòÉÜ|;u–¨…IºTbWûÅt.ªüƨg<„—lHð79Ü\"'-&SsI²Û}>DËÏ ³2¬BñËÃÛ»¢ìW³1v/ð|P.N °f\âP× aÑðF¼,¾•Õð0`ŒõdlUöG㦧¡¤uº'{±ÎÚŒAA•2éoŸùΨ^ÅtÖÌŸÈÿ3Q[°ßŸ%öà\³õ09[Œ¼Hy ˜ÐÉîjv'Ó÷}dŠ—ŸeJ^hå[A°.,÷ÁåÂ1úg¤®•âfà(ðÒýgU±yE&V°ŸÁª×–î¾N®;ÀæÃˈëÅ¥¸LGŸy "Hçv¨Ò¹ñS`~‹2åÛ"‚×îïƒü_xGésÀ, LV$ÓeçØÓ{4ëµB”(5-€ _θa¡-´f7*]¤óÏqwÏ…jõ-¸fŽ(å±°2Å r…_˜Ên[O‚øJ`Ë­ýСEFw³Í¾æÄr¬6KЉøïä gïD®cwéÒÀ²%ô–É0¨h>†¢µºàXÜy™È­[ðŸ˜| œA·ZTÁÃÝš$}»ºTN" s›ñsÔ'Ô’-`#§Œâ}ÏFo¶£WÑ»_Áð›KûGЬË?aiò>4YPÁÎ|…ÏîD’¦uhiØ‚ÕZ…è4U’òlÿއ§íCÎ}hþöîØ6Ì(Ü»ƒÁŒqÒψºoÌ&T£oÆ"Üçên‡çÐye(sé 9óψ~ºYÂŒyÎÀ?üØU›¶‘ÍAGq‹Þ368Û’6õ AòÊl¬ú`ʉQ?1¡ ™\ö*”Yü=ŽùÃióÓôïꀿ„¤[á$ 2ÄžQD¥ÛV`#ÊOæ¤Ý¾SC#Au[0¸Ÿ$Ù®¯#ðô…‹l²nH+ÉÞ` öÍùH¦`Œ½ÿ©ø /í]Ø,fñBß #2fö~Ü̇Ŷ ',t| YŸ]‡¯_Àñi3ÓNú]EE&ÕÂÏS°R ÔMn2o 7‚_ÔJ Nð·v»^æ ®Ÿ›ÈÝÝOVüNÆÍªØ“ „I²êÅ¥lð‘Ügëÿ°—6èMä?{ÀbØÜgb>Ü Ç¯ãžN8:+‚ž¯œ‰R–}¨]'Œg#™­?ëÐ7I ýͨ®º â’B»Ø˜>¿:… ó¥'’M¡VXÍnư6±§Ÿ»¢Y‘Qj¶)ýŒBÕ-3ºMã&º¾õ§^jJDjéNÚ2|ѹô’ý|—ŸNハç”v°v¥~X°j<š]ÅFK#‰á±?“1à%j€¹i$ê_eÓÒ¦!ONgãju`g'¡Î­÷ÌÒ?—p¾ë5ìÔüËD ìøöHãZ=mz«DŽLÙr—ýäéÊ”.½ƒSij&òFc>#öF3lîep9êBÓ/tÃö˜š£wש͢ êäë?hÜ~Œ¬*Ï!uÏpÑ ìÏëhL[2ý+)lŸÌ“‰ÈSÈH°†±cLZì6Ò–H"‹Y¹æsq ñë<ù†‡,K.§% ‡;Ùq=3š‚¯®Š‘w‡æqÎ9(Й• w+^—¶âwÍ@ò$2•ªN—%_äAæ²Fªá=èêß´=èÑEÎ|ÆùDdÅÂÙ6ƒ,÷ b¼çX`èÅ2”ê:BǺfÀ”os ¿­.1ÿŽ‹o”cá¯`úEïÿ]ÿ X=Ê*߃M2™ìâ‘Éôrä ?iM,0 7~]§Bσ)Ïœ)TCK–(ƒ¸¢Ï|/–m»r Ìñ ¦˜¹Pa­k@;çP¹áU°ÞO’ë’˹™Ü×<¤nªôÚ<†[Í,“dÍ%W·ýÁy¯¡Åm%}8Ï<5£šŸè>y²´Â’>´8>~Fhî”äúŽ™äo*úe ÙIP/²¤K¿›ïÉ ô¡ª#ÙöŽy]ëL·Avy›“¦ê¼–àw*@T®C£?MÖ ÇdR©·´ÝWÎî_¢A¦mú:Qÿy:޳›xtÙ9Z°Ý\fâIvØŒö•\‡1…XªP& ¿ßYvCÜnÍ]6¸÷nâéZ;ßmâ4+º o,4¢ÑΗ±]òú’¯·Ü!5Ò’ŒúJÓîçϸ3Š²Ù…m‚øzyÝÎGÿ°ãw$ô~©Ã]í[!®é¹ï½O¸9á· ïÀËê`Ò•bÇ:)ÔN K݆8âbûü‡<±z©Ê'ò“äÆ ÐXi@–|£&C6Dê\>Ö€÷ÎS ]èO›ï‘í Òtäøuð˜ϨöIÂÀ¶n™³7KêNÄ_"(‚q¶‰‚l•æ“e«¤­BÿûÝé ñ—ÞÜö'øiѸ{±˜ËQŠg ¤cPd]lßÉÝóñó_^:[)‹.Ÿå©âðSÐjQ$Ÿ¿‰ÐÝß°ia/È¿;ÿ¿žç3²c¬¥ñAXó÷+{.ô5fØäÕI+ê‘[Šå¸AŽž\daz„=âø•­ê[/jàÚe°ûr«“’§+·¹ý/1ºçkñ¸J +®›ï2Ï~H‘ö MølõOvÙE7È«â%m7xɆætU;¼o|Ãìÿt™½‡¥xæ« G¹ºˆûø+ÅÜÿ›ý õ罘µ¯úÑE“‡Q Ò¤«ÂOÓ·O­ˆÛ “xˆUÙ&ÄL­@3‹ö¾¼Êù·eîÞ&FxxèvA·1—vñ @ëêî»WJ´¬«˜9¬ I_×GKóµÄÁ?rxÃÀûï1x×y’îfzÀZʆxõ\CíõãõÔå†ñ/@Kÿªp§Æ»b/wùþÝu‹ Ç;¿fè’o×”h¼Érzfa?§¦4’DÞ“„Mâ´´Ãðµ…ƒ¬–Ý5S…<‹Aí¸¦Û„Ä;HÓ½9E°‡ÏìI$$ødÑDü¥¢å8~OŽ"_ãjyz“IÕ*>šÁô‚ŸvÑCwXäâzתqÎ"³#`Þó_Р¤DݯÁÿ¥Ÿ @!é>㛼ì1»C Â?…ÈïˆÁ²V'È%|÷WCtï|ÎÞ’î ébzT$O‰«;¿Úô— iB2'¾g(ŒÁ˜büé4Ì8ôu¢Æ@ÚøZOñöqòüÛc¶~kûª4[ %¨Xû>\t`wle¾¾,d|ön¡® Þ|Ýüà&Ê›–³ÕD˜Yÿz'T߇‡^BÔòz6s¿í9«˜ Ë|_ ÙorÑáÀn8©MNاbé‡íĨ`9ǸMƒöŒÀ¦je¦zp1ÖVÁ/¶bô{² ù,Mlãx&®-¨ÔÆ­SkA›^c-ŸJAñR“ŸM{‘˜Ö3ä:,OW'b¸¹®ÏÎ<Ä\kÉÀ+þé”þ ”M¡±{vã¢*Üä?Ÿìóä#â¾Ó©…-ØeÐufÓÏŸ{a‘íÀàŸ*Úù2ðMžda(›²»ÝŸ²?° ß콩û qŠáb2·ß‰tÅ$ãÊßY×?tN/Å ³Xͽ­ÐU³ âù2áúþH”²Fö=e”–v0¾UÔáì öò4Ù)KK7€dæQ Ãðëvè<î]Û¦£ò¸gÖÑòܸ”¹”ö »ËªØ¾ß<¨&^€ýW÷0ÁEHóAaÎj¾\¼½¦Ô»Í×¹øt”qþbRçpø,¡3Œ>€µ&c7SJ®áÂi²h·$,,š kÖ_sú KUyÀ÷‡K?·~ï$™n¬Ü˜/ÙXz…Ù_Є•a¬í¿6¨ ï®u‹%/çŠÐ«FØsΫ'ÎÿݹàyKŒŠ«,¢ì 5´½øÕ—]‚†ŽZ;ýH*tóî~‘ yŸp–|ØN;Á™ªìT Ý µ0ïÂe¼æš@Ýeæ¡t­íâþ6ü왋UBê¤o]ÒÿGÔwGsù¿ÿÛYÙ+[¶(›Â뾞!¡H¥´TÒRijhÙ{Uˆ2*!E‹ðz^OíRÑ´µ”v?ïï9?Ÿ?œ×qîù8×z<®ûy_7¤dçÂìSʬÓl&¾ûiÊJVÖp–´øAÉêìíáñX&s„· ÜœØ¸¡ßÓùtðæf60>€ ††ÑÅxîà{zÅõ".Øs Úm½éŠ­¸òÔGX²¥ƒN<þ‚öϘݷ}€·Öƒfø¶s  Sw=ƒ‰M¶™—¹ ÅNøq.Üö¤v?Ê×ñÿ5Üáÿrç÷7HDÀÏ ¶,öÂ1ô\Æ^o>Ž×-ùKtXâ71ÐÏâ¶<ú›:õˆÞ4/0öÒ#ý  /’¯ `EuÜ«ÓaŽðߌ&ÜrI ßÌŒ@9 †~zý—wÇ’°À1¾¤çb)¾OcÿôiXBáßìÀù XðÝ…¤Þ ¯`‡ìéQâáš,U2|_\fÅ[HRGâ5mÔÓ¥µïAŽE)[øüù-Üõ\þüP`§g ÓÅN\Ò'ÊjPär9³_ñr#þoT O60ü¨=–¥—Í#z ޤ:Ì\?}šF‡Û‘9ñ¼®¼oœçËi¤ÇæZ”˜²ÓEYò¥,Sè¼+B~¤Å›eçí`›4ïq3Æg&wõˆ·r<›¹y+ˆµM%™,JO›ä_¯…ÕaQ”n´_>ˆ¡ÙU0|njû]™¨ “’}ë˜GåxßC¸Iw¦³z©Cx±[–©†’¹_@“üX( ŽÁ›…¬¯xì˜+ÊL=dÁ¯© ·Ë~¥nmº$µË¼rYŸÒAX.7¿†©ßOqMþ˜j»ÇVDäÿø[gpÑØ±ÜØAWÌ?†W=¦]¶ã’˜xÜT:OçX£Í†9Xã8ƒwëìDpÉè¼Ã¿Æ=“D„dö¢’ V57æ¬yÍKÓÔAsû^šòÄ …ÄCmZ:¯àÒn£‘™XMjNÁï&°ë3ÏA^\0Li€Ùù«É`¹\ý¾OÛ%Óö{Ž×á1xÁ§xg”½¹¯Ž{‰–|ëBTæÈ8p}· Ä#Ï‚ñ=NX%‰~f¬EɃ¶ °iÇÒ¸¹¢îLON g¬¶gúõMPÓ³o¤ÿùçú"ø5æÿÖ@»¬z»ÐèÊìn¼E×é°ø”4ØšZ³å×¼ÕN•¸\sœ{ïÉ7ÔÕW Û_ΊGÜLêÂ.Âþ:ÝÀZÄ?£ö¯",÷ Á×»ßðÿÎ$“'˜Bn@%=òùh8ȲËϳÐhÞ-¼¡®ÁLf ‘¤Œ@ž÷D+ZY(Éž*ê‘?Å–ðþB*] ÖÄÉYMgÿ'g±½ø÷5ô-ZH® ’ã§¾qELÔ6®&¿N0…þ¬ŸêD^lzã cp¼ä2´“ŸÀn꟦ÿfaŧ’Aê©ðHþ{¿Ù ¤O…—ƒ[¡Wi–5Þä~.|ƒ§>˜cå+Y"é¯ÄvoÞ¶Í7PI·ͼÍmRbj–^[_ ë'àvoÀÃÛ&‚kµ!¤Ÿ\ ëzôÈçÏ{qOQ) ©/€Y“0|¾9}2ó'br›¯¾ó7ó×'°½-J45È·ºZmO‚¾\‡/Æ‘WƒÙt”¢=Éõ ãNoÏšÇGÀ¥1°óÊ~* Ìši¯Ë>8.jF'6΀ǮË@û-]ªkFü¡U"wÃ¤Ê fÂxaGÈ–äcÁÜdØù©×­Q$ÊÁE ofέï "¸Øœ[cÈ#ûmçÂŒ:}ò÷’v>>H6¹¤ßoq‰ yK®*p3¶z“®k—¹í÷0-/^ÔEâ›ZiövŠÔ«Ìçÿ 8›årö¾qнt&¶ëôƾtå„#h1EÎe[™°á„KRu:U~ˆµzõØýZªÙn´#ûo~³â«™¸Êk!|Lý ×ëç`DM8*w}=…Hî±·¸ÝºšsT”lß*Îfª``[6§0m.Ɖ‘‹Fì¿?m#Ý’+Š›&Y‡1ªdÿtû€ªt=:?ä*ðWèü7£™fêëÁ츗œtÂKå ø²ì%'¬qÒå`µy§pn$Ý…‰5ú°°'vw†móűeê5ä'Ë F³& Ï¢9«€¿9oí· ëÓÏ`xV&gJî\é.ÃLOÜóIè/}aÞµ8wØæßLH<¤™{pë„{|õwϱhç1$㉀ٰÆ2 Ä ù2d…ÍJ*²Rо¦ÈmPŸ ùuëàÏŠØb\ ö¢¯q½P$Žv¾__©0Õm¯á«øÿ >>n­v¹VŒ­(Ív­žF"/œÃ´) à.?…Š~±Ã]»A:ž–yù´2Pê{9y|)MU³ ÿýжòäQ‚è/Vaxf*þË™ŠC+;‘½NÅEÛvq“ë‹ùÅïNAÆ—õX-žÂw(ß7ìãy0¹äfyð`h°—˜^‡Ó6ÀÞGíôzØhFd6'ƒp|Øž†E®Ò”éðäqü+°&[‹‹Á_2Wö}Æ7£Ã‰¶¦3$•F¢Ï@#•N!ê¹Ò¬|¬ŽšgjÇ‘÷{g°Ð?—èUmu–ÌWÂ1w†FòŸ‘˜ '7–kšç`ø5Wü°ë ¸=œÛr!Uó:lq)¦~5Épå}>Z>VnH‡š0Æ]mÙHŽLÐbß Ûñº~Í‹€p/pwðÆK…³q‹ËX”(†È¬œ¥W'öÝÂòiÖ;†B½kpk›{=ê¯lö«çm»zÈ+g_Ñß™PaÔÚ}î(:oZE?,D•%Éнn4úê @•>â‰ÜôëÎlíõfÞ‡±¬AzÙxk&Œ9SŠÛŒoAJÕ#zv:t‡ï&óÄ`ÁÕï¼Mdz—dE>Ììƒ8‰έcdûqÍKCX"§£ã À¸Ó§ 0¶Þ•Ãe…hkùó4tÀw•$¹8N¿^JûD'fºx3š<_ÃWàIM*Ã6—¦ªLÄ·ÎFâ¿æN<,´¨ƒ1’‰¸'á ,ç¦q"{PÐ↜.„˜a–â2 µ'¸Ÿ„K"¡ xÕÖ2$²Y:¬ôÜÖ4o¬kÑ1~7#!ˆlz-ÊFÏØŠÁû.B›]$DU¾Âu‚ñ8zÝ,œx_¥mÛÝàô­7U¥â5xƒÛx’ÐåðE&(j Ŏ’©+øýÒj̹Í/ë'N‚ÒÐÀÈ“(wNbžJसŸo”Ý(ÿõ04Ž]ˆ£Ò®ÃýkCpõ€ g"ØÂ"-Ÿ0s”0îôIƒqá/é‰îi#ø­&^¦ßŠ/Ãè¥ÊPà’Ê–Î@›àªíŸ ^FÊxj­>U~ЬqqÔ;—9-…`¾\sP´ä(|Ù¦Í4á"¿Ã¸"RŒåÎà+½ö §,&P•®WÀ¹åwm:ÄYÙ¦BÔ†ÈȰ™§\àNÇb(OO†'¸Í¢]X^v~è¶ËÄoàTîÄÎ û2½KøíÓj 9¥.ÛÏÝê3cÒïÙ¥†nü$9‹TÄʯӓÉÔi”w¦3×6Cì^ŽIòÇ“oÇöâ‹I+¡æÑf^Š+÷íi^óìáLÄ4GòŸlÚþJïqpp=8|=ÉUÛ¾ žƒó!çª$k½éÌ\OÈá¼­¹9`–g›¸5É‚L¦uÑ/€7M§iX©<­“ÎÏÑ^†¾‹‘rÍÕ`Ñ'ÅÂU%ˆÊ÷ýà⫃ÏyÉÜçƒv ½P‘Ý:kß¶*òÜŸ é×ûq†ÖW<Û›I¼ËÖz{0é>üèRʳ;¨Î9e*év–¯yR|l·ÇUIüñQyx"UܪE Û5j­U˜¨™´Ã®¼ƒÁ«‹À^Æ“W©pè} 8¨=É3évA·Œ)ìŸd1=ºÂqÄþRÇj¸À»‚(÷ôѪ´'ŸâÅwŠP&¹U[A÷€% ¹!BJ0 :e£A2@Áßh®îHª{Ÿ#}ÛñƒI ˆJTp/ã `íÞœzÓ¼10a,¶¬yÃóûÔ Kàþ¬Ûð|À¶]•%_mopáaHµä'âÝŠ|ãæÃiÎ¥Û‚^´­ÁÄ@t¼ úTÑU Ç6Ñ:”ïœÏg÷äàr–¹ôï!¬¹ü†Æw.†£Í×i¼å$30áüuç÷^@ {kâÅyİã6´ÊÆÊ¿*¤t‡3TòJ8Ÿ·lIÁϬ¾Ò‘ç°ƒÈõrÌPÅI«›‘‚®™„¼‰‚¾þ!ø¡ö‡öôEJ×Ñ"ÂZ2@¸A¹½­À¥®S‰Ð¿{ÜK•¨í¶‚}ùsÕôµÀóýnÔ6üÀÝ/ˆaoßhÁ¾ü>´{ô"wºÓ¿‰Ãº4´·«"úåsØÛþïÔlÆl—_«·â-Oö83š-?û"Yïy’´¥ 2ìëqÍÒƒàõžïå'L‚4%ᤠ<äó U/ã”Ðãü½iÛV–Aþì$NýFªØú‘—¿ixP3Ht&§’c‰žèí‘ú·¨At¸þ¦ éŸQlútS´µ™kýVÇMÚò í Õõ.Zf¢ÚÓ°ÿÒAœûê4Ͻ1r§ÂÅ}ô[,3ÜÙ¿6wá ÓH·>æÏQ}ÚðÀ–üËgq¦à{¸è>|Ÿ0ðúYÐÐkÄ»7²ðv©%¼‘zBo]Ð[Ë_ú,.¥BùŠrì—`ñ«²à§¬¬ÿý—wø¢ÛõÖ‘I*És/‹6áé·ü©çŽñô6GÓþz>ÌŸ©ûŽ)ssšCP×Ç| PÏ¥§¹ek²ù‘ÓA¦Å~þå¹*,÷½1;ó—Ý0Òÿ=zÿåž×òª.€93Ç1²Eõ5/@ºÇ(.gè$—1ë¯ÎkÌô.ãŸyUJ?æ|¥W§¶ófX~iypDÁ æåÿ€Ïí÷¨BÛ;¨€P·é >¼@—km"Ÿë/ßv)(†Øý15ã þD&ÐJ¸óû°â†‰úõ—¾°«ÄÛó¹À—ÊLgàÇ=wä~‰ÀOïƒÞ¸šãh›jncä¶£1q÷?°²1"c¢ÇÅ)Ö½òh‹ÂŸÒ4i徟Àµ¬@MŒ‰Nñ`ׯQV&ç–ƒlïfîææ"LÝ©J¾ö§Ã^ÉPÞ+©Ï8•¥à€¨'lþIcó£ÐéÙx*]‘-ž2Â|ÖzûfÝå†ÊnC¼çknáD)–íb̦,ʧw`þÄqØ”$н¡%˜îj´%$91Aò·¸„X^^Æ©ë³Ê¢lGl*§«©Ë’úÌIl;½ÕþŽW<β…· ¡¼í"ÄûZ[Ït|!¿™}>kMClÁmÃK°¯ Œ»u¸ƒù圈ñ6Tš¿a„ÿ|…³h½ªkuš¹Ô1ähÿ^&ûá'ÆíV&Ü5òC?ŠO[ωdç‚5“0n“¬¹žˆ&_qçÀN_|Œ†<ÑGç ~ZƒÌ’íi‡åo·k›´T]L»bç™ ž{†å +qÕ`¢KÌÚ×€571Ð׃ÆdåÐøà:xXý0Åìú-ø$\”)=’a–)‘¨ZsŒN>Ò€ìTÏ¢6 V8²uG«àÆ8Òxÿúþ‹]z¸üèlL6X lÚñîÓS4È6‰¤}µýk°¼üoÊKÔq‰‘÷¯yVMûè(…xð÷èΜÂs ©ôRþÌÆ>r 힯EýàQlS+7zH—«îO%u:"Œú}ä·*dá/')67ãï°_Ab”‘uuÑ4Ï¢akCÞ¸ê oJ‡°¦Á‡Y (‘eÚÑ$pÈ„íÎFG‹q ¡E•¤ý‰&ë?ï‡Ò~jÌÓ t;“ÄUM´+ì4½XK¦„AÕ¸8Rë»–¤ÙÒî,Aøôa 4=ƒ±[ÅHʾøÿ›7}ò…»œ^×–ÂÚ[8»î!¯V3Rÿ­7™ãGŸ*>ïa áÁþH/ÒÖþéOdVfêÄwéT¶ëáuÚ ¯K~®½BÕÙ³šMg“W9X`_ÁTŽ›bÏÜvˆ©ŽfßBW²¤o'èY¢ÕjíÒxfª^ ç<’rY¢†1ìLÇ%»ÙÚ­1[5D=†^ÀÏ©çaxö¤ ï”­‚°õ¯aÖ¾œ}}g.~2®§k-dÉýÈçðkí)V¯Bí7/¹K“ÏñZ–±Ç"Ød6.Ï4þÍd.çÊaÍÌd0eêLÈ]…8ÌÅ¥»´à‹÷&Öõv+xócÿJiþO!"òМõî^I|.Ááq™lºšdj'°“ÒGX”CÄ>/†ð5¹lâu_öJ©µeaE/ûw#üÌ÷" »ÿÂò›`ð¨[ÿ%Š4G¨ÒÙGX!ÿùeéEîµMÂ)ƒYЀw!Bä‰f&|Û·‹Õl6!FV‡Ùl Ö=œçŸÜÂû[yG ìTé²:ˆEA«0qÛr~s*™¾-Í,ØB¹“tÐz |¿r¶ÑØÝ˜ÙÒL¬¸ÆS} c®ÕÃX,ÑžÍ<­LØE—¯œ£Ô3Ð>ŸOú" 1Z>eÿ¬mê4ø‘=~Ùøxïù4~Xú‚œúóœ»ÉÒð×èýxi Gòr‚àCz®z‹ó3   U•½MW!åuÉ®‹°ð5g0é½Û}N]QcïÚSãpUR–––c¾ãJ¯D ýC–òìÉê2ì ÈÅxz5·ávt=aI-‡8¿×wA¯Ç›\vŒr|»½[‚­X;š³v¾ª¦®ÄIª–\-ú ÃçdzÙ:Xi –>а§x±•cIdÉÐ=Üöã:ÎÓÞ‡£Ö’AÃ[4Ãl:1Zú-ܵ™Ž÷º‘øÿ¼Dìk'ÑêmæÐ$ô—¿X¥Vy¼Á•rˆ†ÒIpu¹¹øñ<¢æ¬®NÇ»ûñ>M”§O†ò0”è5OÞö~3¦t{<Ùñ¯¹‰T©·’̃ÕËœáó†zºÛù/~:·6¾~“¶M& §=Ä?/jpz~œpçÝwÛÂ}IS‡;ñ@øNlpV%OdW@ûJ?,~‘D¾x¸ÁQ ×A˳:xåÓ#Ø¥w ®/³§ÑP¸[—–±˜Ø XÊ×c¡§NÂÖÕ_@÷Å>4Ó àt¦Q|ïÝ=_0ëo“6ÒYÿ±`Òz<7õ<b–ÝÂß_ è—À-G¦í/äZ˹¥wP:VŒôïÏ€ðÊ,ÐçÌ[uÌ«”ØÕÀj¾‰Owy|†$™ðöÍ<¤1rnÄÿÍö8Åï‘öa7nH±5/ƒð¾¹<\_ŠJGAT` ܹºáBG7ªV¼Ò»Ë“*’Ñ'IxÎÖ“éÝ Âø­"l0î2Õÿúš'´? ¬ìˆûÉøvŽŸÙHƒÛPjîa¬šÄǼû¡q}&‹øóVÎÙžkù¢53Híãm›Ž +ëD<ƒ÷”‘þït#6ÌçÆb‘Ñ ´i‰À"Ý\®Þ..õÞIX<´,º†»¾À}7'p˜?¿ý¼‰éß{¡çÉ\fo˜Ûz/rõôȱ?‹™ÕÚ@Ø9Ö„}@¨ñ(ÇáÜ£{UEH©Ö"\z ÇÙ¿Ç"|ÿxK}¥™¬±YÍ—â ÄóhøTYV³Nž4=|ÔØëøha5Jh(âG]{è:õ“šáëÛ˜£q%Fí>§¢"˜[– ›¹·”…¶,¤1Ùd÷¸Qж2jDÿïfëiIáݦÄ_u{¹}7ÛÆv?ÜȶKóé²Eǹ=S…1£W%’Jî[e Z]ø‹F•ßiAw‚þËñžˆ‡wX‘Éò¸ÍÑþµn†(×h=æ)xBô˜;¨Ú¤Læ‡0¾¿×ax…¨1éfyò¦a±[ö¹¬àVš¿Â°Õxu­0]ƒÆ:¥pû×[ ãÛ¹wƒ‰Ðgú¥Œe.ø´¹wÏéÆRž9jð¼dŠ\ŽºI3Ô·†“I¯°¨j[sö.Ô ÙÙdcL˜gɾ$¥ÑÁ«–#ù/óß+˜Ó#‘ÕN ‚¢ì^At9N™#bà²{?‰¼^­‰ðùäU^…$ŽqDÝ×2x“XÎê ê"öß³à™(Aݤ˜÷¿\ÜòÁ‰}ÑZ â“çrkv\fÌCäÔ'I‡šhéåJ¾Á´mÔĦû«œ¼/¦ë_Ã8›Ë¨×üÿºÃ—¸Ÿxã¼vrFlW©8IVT@1ÿk˜ÿ=¼Í•Yæ‘ȧëA’:/µ|ö3HŠÝ€ Ò“ κ¹È劤ô²9oÏ>wÜüµ»ýã,—«±Ö Ä*ýÁÿmÛ™¦ŸhIÁ|P%Âz¬ûÙ¢CX=%lÞàÌ{óhOØXRS=š4ö~€\¡T츔—d}ÉÊÍh^r‚ïÆ—…Ðìz*(Íǽ._ñ¶®¿ú)Û‡{œ¥Yšäû «Je௰6XžõeU} \Ý…Û~å@£“!½sÞ –u/£½ß%™D¿+ºž“À‡Æ=ÿ8@a‘Þd »¾/#>ê¹ÝÏ8ã5áÃ9è÷FÉçàö…rÔá‰òCi23³ $Íë9:qˆÈ®"'|M©ýLL0þßûßeÊøðìjTšQŽ?ŽÊ³úÙÓØ¤ ÂíÍgºý½!u ¤W)’Oåùpñâr"P¯0‹­?;†˜™þäüVdïúG1‡ª0Aι¸®f†—Iãü­L~Güêó…D8†’Äè|#sËý‹Î=‰ÔN,¢47“ÁKq÷Îx6ëZQß›ˆŸÝ„È ã±dëìÜlbÈ4wûâÅßÝÐtɲxïÖ¿Pêå†:Eíøãú–ŸðŒšz¦öŸ†m¶?°nX?Z %Á4‘ x{; }uOaÈ#¼Ÿ©Çx«cäõО½xÄÿ çq¨2SuñŠ™VV·‚Mt&L˜sü«[°{Ù3\Ó}ûDZ›»øúcDÈÎÛrì«ôIpK­ÅÂÃÀ_|,ÛGÓ¸q¦µxú²ŒÞË=ˆûFÏçDcÍO!VõP3‰î9Üü‹Ü×–Ÿ8óÎ9 ß Í»ñ—`3þ7ç¹[}-^û|Wõ}öƒ‰ðúËvÜØëºf¨´p|ñxèʯCiÍh9¸i Û• ‹Éc”ÙdJO=1¼?ŠËÌe ¡’PùÓ…›Õþö's_¾¢Ú×d¼4E †ŠGò¿IR65¶9„n"Ü‚YØŸó—« ìFÛ]ëQD Òô' ­Âú5Ç“ŠKÂ_Ë_Ãê}}xÀÈ„K‚Ãv°b…¤/ý ;NPýœ«¼O¡ôéÎ#˜z¸Û?«³ñ/Bÿ[;Í骶£Ý¹˜µ!Gk^§Ü¥w0ÖìvmNf£âa—ÆNžBþ Ø­’Ñg­XIï¢ð{˜÷ýTÅ»gñm#Rý/Š<ç “ïÄqÇ„%;ÕQ2Ó’ÙM$^å?pÔâHîþ¸)ü·…Zœ|Qx­=ƒ'Öb|ât&?Ç‚Ëå䋨õˆÿ+UB“éñœºù–Õ©†½Í üi!(ÝÚKjY §sK‡Mº•‘+ðo®'`¦#NvBÓo˜`ÎOÞ‰Û&³UÒðàà:æcMŤkð©ä8–|¦ ~À]ÄêKÈWåQÕLobxö؇Žf›xP2%Zc‰Tß ¨r£xP&?¾&'íÇ7®q}ƒË8óûà®èŠ%.X6n ¶œÒ$Gç±›³FãÂp9¼žÖHïl‰Äuñoàð˜É(%#Išw+ÁÏÓ"L#T>qGP÷±¦v´¸…W"·×zÄÿ_xX—•ðæ‹9le¢6xIgÆñeP×›Šëë¢Hµs5©ÏãÎï58Ý2y¤«ÏåìšÍÔ‡ë_ùæ8.ÔƒG±˜uÄ¥DXσϰûj4øoùŽï˜ôM2ÿý1î¥n$t]…é!Rð÷ý”S‘eþ¾/AnÞ-pf âfàscÌhæ©w¸Ê¾X.Qgˆ[´‡­Îc-)SnXJŠ­ÜØf eŸɸIaxvÃMø“ÔÐ Œk²bô{ ¶NfÂÿûþ݉¿Ê·Ç€þ7Úûq ÷)Û<5šÏ¦oBñæ\øï Î?OÍS qîW¢§òæ.¯†g—€x¼qÔ8ÉÚ7ÍE•Y¡dtØTVÖ7–„m×g ¦Ë@Fç"¯ËSHRåáF’tOõ$*ß q·Ð 2v–9$ÊlÍóè”%Ÿ0PÅ™£&š¸•‡&Ò†äGp.ô¤î‚Wþ÷ÍžÜ@÷ÃVäìùP¶èñ5ÐônƒSÏìÙ•â06`QS£ÄÉ9Å.rkÅ“*¤Ñ¸–=·‘k…Ǫ5$<4¿|†—vŒä¿ïÚ}ìLÚ ƒ¦â•Vè**E–ç?Áò¤=h}%Ÿ­hºÝ®¹ô¿yËÏ5æòƒ:wŸ¹0_¡øžìÊ¢æÍ#VE“·ivÔÊmÖ³ç$~RÛƒ½oF3»5$aM=I:%C2ÓKQ¯b€·þÓ86y×Fzªz>±?æ5ö•Ö98hEþ4ð·MõÔSDrÒ|÷I +/%û?¿…ã/æ’û²iÌÕâ7”îÁþŒY9mc)º•lyUÖÆEƒn‘+,ªÄEA?Q®ùø,0 w’¢‰Ëêð&7 OmíÁ«)¶#õOÂò4§ssB ‘S¨À}2h]­Ìž¶ÇÖlIvWÉ‘Þ)ÀúŸ(››þ &]º‰/ò¹­9ï8¥È@ºåt-XMûŽî•Êäz«6‰s¸|ô½KVj?ÂÕ¤>NÛ¢öÃ÷ax^õ<óÃsÄ Os<Ú”ŽÂ õÔÿÙmÿí…ƒÐ2ŽïlªØDÇq¬å¶2ÓJ{ÉíIØtv#zFF°ŒU=lšŠðO“ÊË'àhæJ^Thôaæß\ÈöO» ®’Âêû=8f¢ ùܨÈ&½q€6¢Â-D3&‰ó‰“{&sÇ]hÑ!5§gÀÇ`pð8fx}…ô;… YˆÜâ¥ä‘á ¸qg'¼ÀVëypÎÏ“DŸ¬Dmu¤-wïs!kO3§M×0µv"[["‡c=½™Ò9ZÑä®7Šˆ·Ë9,Ú—ŒgÒ”qN⸠Á>u(xíËÿú_‰öܨJ§ú6rŸ~¶˜È ϵ#Ý´“;¹ÀŽïóã’œò±`w¿lVfŒå­(TÀ®ÈqÐù6‘“í B+v@ÊÃ24mv£Ü5öûÅ ¸Íë¼a›ìö+ã7å\€çAËf.Oð{ ,ì=‚ìí'îè;_ø ßtCª…0[¬›î,À¢®Ì],’“=&ŽßØŒàƒ(ÕÓˆßËdhIŸ*f‚j•Û°jf—u«œ6ýµb³Ž¥Á5ñ#¨ÉåP ™/Ûy5—>kw…¶º¹²õÑ2\N™1 VÐ;³Žày[[ëhذq¼9Y`¨¥OŽÒ]¿Sø–v’”§ËØlwr'Ñžœ~öj;±+ÿ‚ïèsȸ}“Ùý{óÈ#—,îãÀS¸3ØŠ\ê}\Ö¼ /ñ´À¼Ú^gìf6VtËë B¥ù;ØÏ¿Ñzë"üo­²¿¡į*F‘ƺìâR×­¨VhÌ ožM”MŒÁ4Hë;â‡ËÊm‘Ck=Ÿ"_¯‚°w³Q,Íå}Råw‡Ž†ÏÛwB•¿,»Þ¾  ÅäË!hT7-PŒ”éˆ(E>DÊ‚z¢ÌHÿ«=ùwñÐyx(®ÁŒˆ9Éô› jòAtÙ-}òçZ! mï¥õ؃uGya;“N!e;MØéPéĉÊV@r‘*›×׃E+ˆJSËJâ §i±ú3Û ´¤ºöM$Þ^p¹. ìÝ ´q@еKÀÛ÷àܛڸ?Aš¨ ïg‹„È{X ySY÷$'æþ÷,\)He—G'Bà_&õ¼ÿÿÖ]·ýÞÒBòL×Ä“Ló\Îk“˜ÜÏŸøù¨:ÙI÷ãŒ{ ìWJÙ¬uFç;°¾+Ùl½+¡¿¯²n‡Fêßî=ƒ°úÆA~ÞŒG¸à´)uzŒx¶ÕˆõIÿÀï%Ùª ôûŠWt×2·Á PºwâË[pê.?&~µ¾¿¬C•e‰dŠm¶fì#­²\bF!ü‰l†É"KÈ…1>LÏA…äÚVsQsÞÓ)ªdQê2nùqø;g".¼Ší:–°ëú%nYÒ66:n1Ùº=”œ5™Àv<6ùz¬>=Rl!p|"^Î-$+ïÅùûé$«ûqç½óŒ¹Dée—`¨ùjˆ=à6ê1ÕO…äÉÃèw .†X”œÀÚW–-R<‚Ú£9. öPÓ‹r0§B•) h²/ßÍÈmxEÈTǸgÕàÏ|ɘqóy!žµ¼‚Oæ°0óyœ­;ãu³qÅÛ$®oº)x‹†¢\u ¾8³‹4,ñåhØ™1?pöª_œo'¬ò¤ëG3N<2 N¬_A Æ ÇÝø—ôÓIDA=}Ø­#HЦWsÅcóàŽ ^3ä2žÿ¥OžC¾ßÙ7Î4[bÔ¾±DöÒ)ôƒn/Ô™Òù,ú·±ÛàôV¥¢[ÿ}øÙ%Ç—ÁßûÁ±cV»$B:ù_ýcfD_ì,(˜¼‡ŸB |[\úö&Ž’ƒ'§Ãùà[ü×–‘z¿Ûp¨Âída‰Q&̯›’6~€NÙõ}f˜1©JÿÍpÆ åEðaÚ|¢qùì»yΧþæÇÞ-!Ï•ì±ú>Œyƒ›\Vº„d.%sVäB‹ÿÕ† Å)¼®½¸¦¯• (þÈ{æù­7„Ò¡ÀºÓ0Bp<Ûô:™Š˜©§Â´Ø.‚ÿËGçݨ}! _ƒúîd''· «ÇSzÚ# ¼Dº`1‡Œ©ÞðìÐmœ‘”‚v2&p òΈýŸÜ¸ÛFaÁªœb‡ëú4 VÝÆ}n¹kžÅAL¹»£)AöÞkåMÓ-‡?‡éœÂßÑ}¬ŸÜÆÙ6‡èð±t¨¹‹m÷Óü5BìÛËWè(© ç:økxÞ s5^ /ÃñCøªØ7®‘e<‡z$*ìWZ7+f!ðR£\z$aÇñ™à®ê„rAÑìCÔK|l®ÌJÕ¿B†ÄAªä™G ·ÄÁò&H~úž»¾£—Up*9:}%ðŸÝAI¢w«+9F´²=ɯÄÉx&>¤íâý’ÑdÂ&=R–ƒÃÛÞŒðÿ­âjô«‰èë‘añýÏx§k4Þz”ÆïÝX…gi9ZÈÀÂýXqUÝí–¢ozIÉß8óE=¯Àé6]ñݘ3%s¨3ùÓÈ}ð2‚¹×AÑ+#r7¹'LMýo5Z9Wá±[dq–:;m\Þ_㈂p:ÄLQ$ÏWÜà<ŠoBòti’Óºª JÉ>ž!ýí{o8“m©Dž_áÒJpnW;~Z7ƒú®N–¯‹"ǵÆÑêï*l¦í& }± &½ãfų·K^‚L°*Û ïŪˆ2ãUÕ ·kWÞIÂ|1òþKÇô$¨]4ëû~›äöë„ð]õD*Þ'Df®ÇÉñ%øBà3<ö;Șö¬{Âiôµ|º6È:}Øäõûq’¤–„œC/›õx79v¦£@ÿ5B䜧XcVß’9c+îz×JÛØ®2û4Ý›¬ µãl`Ü€fý,ªìßÃؼnð+Í¿[Í›xZ“i¸­ÃyŸöã¢H_ÿõ&*ØÀÌI°ï¬:oÀE­äÔšú1C{!»Y6€m/ßѹcdˆõ]_vGZ‚½¼Ùcrsßß&ÉäcX²Dž½S80b§3úàü$q¸Þ§AGg)zÄÉsêRg@ZX‡zV<ç2~=áÃ+a…×9xÒ¤ih2­R|jçÒpàºË” Ö7ÜêGŠ$~l'zy`ý¾ôa—ŧámMÖ7Z§éÂÖÚ8æ™sÛ°jÅ‹QØÊžÅí¼ˆwâßbaý_œ\?–ŽŸü[ÏZ°Uy© ¸þíXê/Þ‚e[=Ì+­F; ;^¹çΕ|CémI\ÃÎ&Î(]_céÄ=x¦@Ÿ”Ü-²D.k™»eˆ¯Ölî3 }ø(GS×ð2|²o:&¦ßå~šúÃР2ç7‡ ~„wBãyW–“éS¹à†÷ަb[Û&à›g€èueܼ|6§öü+ŠLôÁàõêÄã¾ ìÖÒÒuxKã7ºO~³Ë%ñÃç~j¼v ð àµÉ ud2zm°eáBð°ñ"Ñ)Ÿ€œŽ")ƒ›ÏíIJ;y0=,~ÿ!é0œªgŠÞ9|yzWJYј›…ðòã°•]ËYÌñâ%~÷9Ù`¢øà%k½‚«_BÐ[Ð?Ø?Û¡ÖÒË4|:o‚G ~–Y-ºe£3K‘ŒäÖ‡Zð&AîÁn\Þw“¶ó§1ôù/ówÓ%í¨³Oý¿ÙÏ|ã:WöàðQ,ûöÎÑ.äÊŸª‚¢2>ò=D½ŒÈrÅbžâ‚2ܰå v¸s£bnàÃÁLLÙ|¥WvC§Š'¼lø…ï|€Ì·رS^¾¹ OÄ]÷SšÝYÝ­—ëQQ$¿ü?ý7óÆTÚf'ȵŠbø¥M0ÝÁ·¶“žg‰¦;"ÑPF4²¤†H˜ï‘ˆO2àìøe¬?Òœ•® ‡ ïǰQ9`ò_6¾U‘?qÛpÅ6Õ¡á–X/޳ҠͥÇv¡³qîk?‹Síˆæ¢÷üàëgiHÿzì,6%“C`¨ùQ8vë'.¹¬ ë“é_ ðþÕIätµi é€Ç—ãÛ£Ý Ù{‰Ü ÝÙ©AÒìI¸¨«xú€›³Võªp–ßoÚ¸­4}ÂMP`mh¡ÞÄå up£¯·¢@ÈÁüµ+·à½ó¼Æ­§©a~®ónÄ/|?ø[Õ§±8ç*¨y•cèµ÷\“3‹:µsçn‰ÃÒEË gÉxþßuŠp]ͽ®¹ŒóÝŽrŸóéÈ¡Whðý .1G¥]ÑôäV4NÊãÔm†èù:øù+÷kUaéYâqõ8=8ÃÉ8?æ:IP¹S-À]«·oua­Ù4öqôNÈ9‘†å¿ñ Ïäj¸òÐ+ôéû%ä·"o$þ'þ¡XYÍ¥m㱋!oqÍ[9 ±öâ¾_ÌÂUäù4¨¦x€µãGœrí>·"¨ æèh°S:\Ÿ“(Ùg~ «ü£ ¯z 33Ý5QèY¹ˆßÜéÉý7'z‰Õo`dTõ]è2u©Èr©„üŽûèb’‚s*°UÚŽ}ÛS KmÇ›ë‹qúïÝ0|-úÓ%Κ ÂçYÙ0$xù'æ³'£V!H(³E·ÍHRíùF['Û‘oÿ¸Ãk¯•<ÇÀöÀÓ°ã@·F.\Hãä—Ù\ëpÚ¹:ó7–+ƒGÞ¯Fúßz ›]WêÃ^) žº–ïV$’gÌȆ¨§øSî<‡Mã‰äÍßܪÉÖn)Y!¸ØÊÍ7›‚D¦Ò±⹪3nü!ù@zÖQ‘}ÓºŠfosc#Í骺 dÿ0‡Ó×€ËÝãpÅ8* J>ò3éè-Ù(¶ “ ©y<œž¿±óáoŠ10ͦeÛ`Õ£!Î+£ra`ž+Fô5# 'Ò‹œqY “®I’¡X N#oû¿C¥Á"¼Ýs&ït‚ï&2xõû}\±Ñ˜ìòI‡ lÒè»Ó…‹Ën½Ñÿŵgè¯zäHz>žš ÊÖ ºÁTç®D΃Óõi¤÷Û‘É|·§˜ÑgE²¤ßósÖÅÀ §&¹Ó+Šï¢¨ˆV!n¿UÏKµyÎ —J…ì5Oàr[‘Ëæ¥¶¸WF‘bÃÿp†2F¬©NIŸËYÖr 7ÔsaîUðæ¦&QiV‡–ð¿ôð%"±,W_žÉžKm¤ Ö®á7ô!K,zèyƒ— í{•{nƒŸ6ØòGž€¦2{vÁú0Ê*‡zé8 =ªÂàÒéÿÖ5ã’ÙR´uíyÜ_¹ $¦Á›¦iLk³:oÖÍY°Õc?ŽOóä–ÉÌâ2?˜3ya ,o»€©"«Qkv(ÎæÚ;ß?i|ƒ‘—”Éíw}.?äl¹­Ýô÷Z:üd|pC½‰5(9;Vw…ñGq|åèÿ·9ßËÕÜ/BŸÑ”'Ù>Î]…gg}pyyM•ýˆ*ïШq/÷T§b¦À£ÏQptþonŒãbvê@,ëË‚_™%Ø3¸²¦Ä ïU0ø-ˆŸÕÓ7•Kɺöb¨ùdŠŸ]dqéLop¾äÉÙjüáêÚWà”ÓixY_€…ö§áþ‚³Ü_yvruM:äÅnÁïÇ0:sløÑӂ݇u¦h;,ƒÉ…ŽxùšÚ=ÕGÑW\#òçýá{Ý)úÌM•ý#RÄg4ÇÍü™'ß4ÌnJ5Ïëq­z$ÿE´_EŸUx2­—W,\êÞ0”9 ¬.œ¾×drÛq2Éœó?{¯gO6{“ÚDÿ¹ºã=ÈËUâoiÞ–²Îc«Üɳ׹Šk ”XåœY\Ç’Qä×àJîÅ`›Tª/E WC¦ÎßÁL-·Â’cÑûð|œ¬”BVC‘YdÔÚsÝ ­'—KqDYAš é—aëk¥ÈFù40¿8 5úïrm¿†kèÖß´mÏ;Ü §ƒOçBÛ\sÌ)>ËßU0 _}ö%j¦‹ñö‰µ.aûr¡Æþ/§*“íK&Ó¿/±¿êòùÔMßšž9¢M³&ÃKw¶6} ܼ|’‹p2d'ƒw¼Ùysn_˜ùeÑd.>ÂäÓÇp÷‘Ь]‰BÜ<4“8Ƭ„Úå;1ö«Ù3a:l»– õ9׋¼×òÌÍoô÷ã=b‹“BI‚ð{8zä*(Gy³„élrèuü6á°ï5úJ«b¹¶:¹}÷–]…ìaúuéaYä]_VÃ}—±nð9wü] fvFóŸ©{pAÁ—15à Éé‚<£W¾lŵ/8-ìÚê7l&à÷ìÿÍ¿îï·ÆõfÍü¢jðóïz…óÄu~$¨…#–ÏFAÉáqdÂŒ,–wT˜\U_H’¹RüZ„2\¦ÞG6Šƒl¥5¬ç’óæ.Ð:O+Zvèº<ŠC³mºðpïµÿ[7}b¼&8\Ø„¯úªÈ£6+°Ú¸Ý ¼É|ÑËŽCzl*WÓ+Hbï3GA-¶©§w§mb?ÿ>æ9eÜá±ß’(­‘C‡ºÊQýÊE|Ë¿BË,¶Ãë™…°ö¹ípIKhü»& ¢VË3›Mš,"õ&zٲŲãaïØhhYYà ¦|ÇÿîEânñþ´Ç˜¯—æ¾NÂUg °ï¾)sñ)„b¹) Úð:[ƒ¬Ì„MM äïæ,6ñÌvöß åðÕåœôÝZ°6èå·Ñ(îGÀ ¨LçA§— ä¯Õe¿‡ã|ƒ‡ ži=·¿Y±LQkvRψÄjwâ%39f;ñ!N`äVÌ_SW"v») UMvÅ© ÕdF´Ü_Á“ Z × sï+@CVy&TBlr²œ¢^¦@ŵðOs<6w•%&/6 ·nGãííê,öP(^Ÿq€•N÷§êñœ^ëy8W£K¼7ɲ•Òÿ{þõäO3æ¯á é…6éÜ÷‹®?¸˜Ì¼ÖK,¤¿€™š6yy¢ÎxÛâ±LˆMŸqGփ÷Lf³;«šÀ#ÿ²­5ËwßF!½}ìÓÇñ¤7-—-`s¦²¹¦ÁF}:œ‘ùã>FÕÛuL§uQôX 0›¸}„ÿ­æŸ“žºvrµ[pŠÀ>½eRÁ4МÌÿK‚Å`üÜ$¦a>٫؉ g`å•VâÊÖc˜”Ë7–ñIRåó`t—.Ñs‹×cYÁAŽ¥9FÀ±•xÂÜ{¤½‰µ©I“Ç…ÿ›”ébÍWßÅèŸ{d%Þ†UC6;´ uºmÉZ ;ô²Z€ê·.¿+€«ÔHàócv¡@t&¬²dÂnÏqæ©7 cróÈúÅ!º¦Î×s³„ôq&jœy =^P>ó#'™ÿ4¾˜°ó6é¶W›y)—ÀÍn—¯½ÃÎßGý™Üš1+¹Ÿó“y¢›·ÐïgÁÚæ½²î&\¼·ˆÜ»)†CJvLÞ;›“\¹ˆ­ïÆ®i*äÓSq6yŒ “yHÇ«RõȮ͂Asö>8ŽÖ;Ær»"=Xú;VeHâ þ7ÿzüIði•.Ír|_¾,…¹îÜùk©¸zàë‡c²áþ»4j} 4g`œ¡(¯Ð8 o³­pǾ”²ÖWü€Ÿç¥ˆÃÕŽ^…ãòñoc$Ž'é+ÒAñ’WÔplßCÉ\}|_T†a&4/ýn‘¢£}á€b˜†Q¼\Ç#l˜ë½ Áº¿a[l.¾ö¼nåÀtí½`¥TY'Ó‡÷Åÿ'ûœë|]€ÏÒ7òsåæ@iþ]º_Ú‰àK]R_݈^¾À£S`p•,ܵ±Ægõ•˜QºÆMñÿj÷áÚ{gÞ›§Â¾”tÃÕ;1ì¶©,kˆgמ:û:Ñy¹² Õ¶Þ†3ҫؽ>wȸ˜ç2´e­Þnªf˜-v”/æâèêgTm´"éŽÀ—QØ’ùU`¢µæL>ÌzXlZ૊?ó:k@|ÌDZ;v›˜TÊ)Zr N(²5:‡øoÏOaýãeèü¸H|8‡.›¶ag5ñ"N˜w†¯Ë»}iÓ›B?ûÅëMJøvþeĬ4.oút–úy+8d4ã·u²ÐCwšÎç]¿¸•ÿ2Eªm£GøïÓ9z@½ÅÈÓùõœ¬¯*KôqáçCÑÐõøFä È+tÈËம暂îrÒ%Ÿ0mÙv~bÛhö~X6 ç²óÝ?鿾X:gÃul>¼²~Ãí‡õ¸Ý}Æà&KŠ&¦`œ’-+ŒêŸw5´“ÏäYgøZ¬¸;‡?6ÁÛ_(;a?¿…sâ].âÏñ§´I¹Åǽ ïpÿ$ öß|éÇ–£Øƒ¡ 8m³M‰—PåŒhþ»Þ#˜è²\+Ø>D…¯'é߯ÜÆÏ†à?æ8¸Ëïæ.ss1dÑx¢÷µ~Äþ7£qa2ýñW›Ìpц{Ëã™·ür²jìöËS’´Úlga³è¬]sm‰“ØTâ-jCæÕüFµ˜øÔ˜XNe÷Û‘ö‰D¤Î÷ïâ±Úa-¹rl=—<‰|6sÄdžqv˜29áÏ@ÿôNØ}¾åÇ‘ÔÿæK>˜L/ʶ±.w ¸ü•›iôùHüÒÈàÂbË3e‘‰SÈê\abF´cfgÅ$®ý€[´å4満mýÚ†úãcA÷5GžùàãÑÉèæ5ª%eð|·Ü/)çF]ZÍ…ï[Íú.î‰ÿü’ìh¯û¢¸ûsAøwéXò¾¸>BkEe¢§²#·U7,#džZ¾AÞâ¥Ã¬ëcy)c¬`ëÕż'H‰Ã4~ Ê´P<2ìk‡ù_kÞÀ­ÉØáoHS=Hïº÷¾Ùx«ž”`©·EÐë§,ù³ö e ,È þ.0Õ;<ÿ×tfõcª+âJª«hÑš Püø$NÖz­KØ’)“ØW²7¼ŸãQRkî׋LàwìÅÇZùkCç¢Û“Œwˆ`|ÂýVäX. ³ñ¹Íi()žÆ:aåþKF3ñqp ˜]sÇǽ~8>Ò “`×/eö>mn®1"ú§’O“ö’ß7cÞƒt˜ôÞž)º\#: A”WâuïUîg;ÎM¤©:Vd”x á\P–xJ_Ú0‘/7TÚ’OãSÉÍI tÝBŠ”n"±é{†9ÎWœô ƒýóyÊø&F¯fRÉ0rúÄ/ü±D˜Ð\L æØ>÷Ëü;Åâ¤vSg«âFÁrQj™ÉBª’ac|6öË*³gªªØßx‡·â{óûW¹ÎÞàòOöÄMšÀê¿«’8f4ô¿þ‡©•+FÍ¿FÛ “¹ÿVàµ*¼Kp?b½yfqrÌÁ,–¾;ÈdWg9«çáŒ2K¶é™?¾«N…dël¨wEËø0îýwPÛ‹MXMDYGj#ÞÑ— »“„˜­{~^“ü¹ßM­6;ÐUÛºàç)8Ð/ †Ó§•h)îY]uù_@f.mµyĬ“g™†öäÆå[x($ˆNïÉŸ6ƒ¶ÛŸó“$ïÂ/+YrIò>}kªGD=å«qPj}›«¼ú‰{5V( ¤QûQá•ïÎŽó œbó„¹·oߨïŸçP›x –D2jyQ¦]øƒQòî(ï%I’_og¾gÔP¨¨ƒÊÅ}€þ¹Ž,êâI²þÌ>Ü ½×åHrgŽöiáìNè1¿xf¸QšÔd÷Á¡ñ Pña jûôqò.VlÛìg·Àï¢.ÓÏñ"~®§ñѤdÜÒßÖ­£É·zO®wK³“‚×9úßõž¿Sb£ïtº<Ù|«QŠ ,@jO8~_ˆ….=ƒÅj2Ìßp=Yªοÿë\ BTYkÈ6Øü(íÌÙÙ¢ÕÐñÅÝ2­âúüäñÍÄ!䛦ðËnU<ÿ›%] )SA+lyà¡ Å»Ûé'S¬ªƒ¶ÜåÌ~^¹¶Ü÷næðrÊO^À|ÿL‹ÆÚ³É®B¤Œ‹"žœ ‹ÛÌÉჴ:9|2ƒ;µr?ÿæÂîðÈÏÙ蹂ÃEê~\~¯Ÿ¸*}±0ü?«œû˜ËH‘ƒÖžC`õ`9 ß²&öè1s Ñ×aÒÜ™0åØ?—¦.Äu§¶â¢¢C8úÞLd£AñZ 58àçë¿Áuö“áÛ‚anwÕ Y’èíIp¯/$‹!Pí ÄuåCá 2ú²×ˆþ›Ý%E2¯ÇâäY5ôhG"‹iK`N6‡¹'ζx½Ì‚]¸¢†lÀ9<¾ëÌ~,›Àªú·ÝÌâû8ŒªÎšÀ⦨a@‚ Nâ‘)vÍû²ôÿþšÔ«xÆ Ù5.ìT•;]õŽyBú«dÊ»¨vètÄêó¤¤|/±ül°åƒäûë‹ ƒ˜ÂN¢ÒÞÄ"ÜÈoÝ8”›n‚ ˜³¨ù¨kd‰]ÆZx¦ý3ž( üw¹ÔyÓ ÎaãLøóý$ýw{6–ÍÎ…})Ì×Ëý†õŠïKj¾$ƒyoFâ?úêbüo´ÓQr$SŠ÷äˆ_é27ޱ·8$3zðzö3¨Ô2Á2 wxQÌnµ‡°Î,2óÁ)r4¶ŸSHÖ%ÙKb—h­Ò$ÙÕØr×…D!X•4åupgŽ=…ýëNÁUùÓøóÃgüo~´É<)¶XµÞÈȰ¯äÚ3é5ñ L³æŸ ¹ ;¶1˜©O”Ü ÒÝŠì´—e…™“Xg†ܮ܌Ýzûˆ¬b«œ14"´˜ÿ:²æeÉ0àØÍò¥$âÄzô÷T·Bv¼àʼº‘\ó€þÏ+¸Âm°x’8Æ“+\=ɹ-˜¾O‹Kž£ÁÚj¨èñbárÏhÌß88v%|{‰†˜N öë^ÀºCýᄺvÅphV$!Ï^`.F£öT€C']ØN™ððÙ æ9q§sjÛðønc6ÖÏœJf_iܶ~„µqçÌ 9ß³q‹ÏñÀüÖÜÆ¿¿ÇkÀ¸yÊ’·`ðr'ö|›Ã x2ÃÚ{æX*³úˆDíá!”Ä„—µØ;§]6`ŠéÏü 7x¿¤ÒhÌ¢»˜fÊcf;›(߯–®é†øåqéñ^n#5© ¾çõ<ñ'ýAÁäïšÝnBúä+@îÍ>¢æ7ƒ],M`“Ô½Hß}ve—“`Î<E!„ÃKå”:¶¤£þ®Bð8~ŒÍ6zq©¨þ¨<5Yƒ|L©a{޼j|´²•¯t`ÏݧmŸòñnZ9w¤Í\kçØ³heb౎¼ÖïoI¶€Ÿq7‘°ê†+žf$þô>ìÊ•œ<ßx&ËN݆ê¾;ñј‰œ´ßhhÃuâöì–ÏK¢4ÉÈm93ÿOÞšƒ•x· Ȩyiu¯)Q½KæîQWÎáÓx!–hlL^eÑÂ&SüÛ*J0à8ˆÏ^Œÿ DÈ‹¤³pgçcšòù q†ôç¿y¢ýr¬Ýõ ´¾!¬qÏk.? .±8xàÉÖšr™‘Uù¾\fÖ*³Ú.%Üåœ}jáÆd—©øóÆ$\Õ[Í…ÌŽãMè R»bñªJºïÑÔ¿¿‡¸ÝxK©¡ q˜|m(ÄX;0µ³&«‚VQÃû\Ž]Y™J+ðkÕ*¬2Ž…o{ ËWî™ÙlŠLÁ*Ø4ÌSá6Ã^*Æ;ÓNì€ô;‰È[bÈüWøÁ¯–-×dYsyä ;É*$"à×óÙÜv Õà Ãú+Ë.|ûÛ‹Ñ΄‡Õ¸ñq$ûbó¬Ëuàà“ËÐÏv‘›çâpͼ\nß›YðÚy)ꥋ1³ÒNT;¯„äæ6?=WËÝ£:ñ‡ÐDè;¦›ʼn*Z¤¶³K­ À8|;ùär“ÎÖŠ[³ÓÀto\ÉIÀû&’åÌûúß3‚ÉQ\hÂZà‡4âª_7áôîF”þeæþUaÀ‹Ásoc¡øJôHý[«hŒRÎ0~•P³gô²8uò—¦/ý’èTµÙlðÑ2 Íeòkþw†îuuÌw—ƒÝ>lÔ‰#Xg6•îú¢G‡yæ‰Ù00]ZÄeáQøU8°€>ž{Ö¹`Òâ,˜ôgyqó*rpÑÚqL«` gµLŠ¿Äø$¶fRØåîÎö' `#Ðãò@5C›ý«>€Oþ5”%ñ&ž]Nü|—À½™P´[žØ®®¤ºÓ©ûÓÐûª Œn€S¦(IYaƒmºï <õ!ˆ[œÆk{ÈÇ¥ÏáÅ~ ¶þº)X²nmë~gQÇBÈ „W›—ü6™Þ4+F+Ý0ùù,Ú-ƒwÑ{Á´ì VÎ,±ó%§8ã3éˆ*Y›prÁ5ôU =uÎdËÅwÐ0ñ3»ëœi¤þ)u à¦ÃÂAœÝ^ÿ¦;ÌÁßïË(¯_ ¯TÃO§§â5‰h8©ú¯-XÁm)G&Õ·Ò«EÜâ‚hŽ",,éžß·s¿Ûâù|KpL½È]Yä‹…ÚPfý†gyó¾iêÀm¤Ýëlx÷™ï;­é¨8Ù3ž¸}?Îeúë“K,ˆB×Iø1ýùÿÖ?­ĹÉ{ƒª®>ÑÕ‡N#ç:ª˜LØZ¶µµgnß…%(EòêŸÃý7º¬¤XŽ»á«eSº´c>ÙœÌ%¤-%Óô™‰@<}8Û‹/Ó`6§Ùõö üãÒá^‚;鸸Ÿ¦Dþ()÷ç&\8Ý×°sã´É²ŒFš¸Kþ …Þe,6w6Iªk#3Ïn¢Ë&‹‘놔»Ö_EždÄñ=Z¶±A£?èñÞœø8‰N³˜Ñ©YÀDÄÙ嶱Ѐ½œêßN¬VÝOÞ>5fkŸü†SÛÊiºGnÈTaÿQ]ö(ùßÿ³H•³áBQð}$q‹Fh¿O}ÓÉçd ^Y GgцöôŸb>M›£Nƺgk¼óàЫBÚbN¾¦'ÁË‚ô%1G{í]0ÞáHê¥å)^xôuëvŽâÔR?Ñ‚‹É‰7qåËi¤NP“+u{Ͼ¡G%Å©›Ùtvõw ‘6&^—5¸“¿Ys­9ˆw¯ázÎȲæ¼L®ìñ2{T™*õ™Xì^ ž}F(8á ]ïùŽˆ+@ðxpõ7p˘t7ˆ‘–çÜ’H ²<‡·çþÐz¿ˆÛ¹?G~ÃíæùäŒ'þ€Ê÷Æ4¨Ë‰x+NëÒgX²ykz”HÕ>]kù7&.—D<§àÕ'ÍŒ2=ÙCÛONšóÇD\…wsT™cœ-—½§L·dáóŸ ¸cÁ&8mW È?HÏÀ;#þŸ¥¼{¬ƒ°^]p¿Š %‚Ð$ +æ?âÿLÃØI“ÈÒÚóœæË©ð3ÙùŸ]ßí†-÷ã«H VçK6zê³uÎúlÙÍ$ð¿î@ºqkí‡\ãè|'@J“àF«ËW4!»–NaóLõ`ÁnGrôòð”„W¢óÄ-Ô/| ó¼{ަêo"ÂZN¸PVƒËˆe»¹ÇÜ)ÊÓu¹ʃ>d®ëqZ¼H˜ Xâ-½§Ý»´·p?ú;ž=ü†¿_â54õH0ÓÎìž›'¶¨UÑðçÝØu[‹µ½1G244¢ÿ Œ_ðVÑý'B¹S‰ÂøA¨1Ø;LB¯SføT8‡Ü“u[vv?)‰œ†ü]&Ðüä<ý™%ÈÎMºgnÐZ¸‡ï@ªé6'Ý•[~Tå–2¿ƒH¿1.V…¯Éáo¾»mWá|îbär—¹j5ö×t=ã y‘ɵǸ´ÝË1^þ ŠWÚA;ž³Y·8çä«üž™ÜÃÊçôRÙ.xªßį¿ìÊ÷Ë’+Yt߆̀­å˜b}šMõåshÿ0¶û̬X=R±½„úÄ<€óâ™ò¾@(Ôíjüÿø_ÓÜùúX½ø55¼ªÀ}¶1á×îMÀš‚)Î;ì\ˆùÞeDãÛ)¾{s}WD«¦T¢×nb‰úÄ©Uîá‚UWã¾¼lˆ›ñ¯»Š‘Þö*ù°‹2ÒÁ“Hû´÷\ïà'ÎÔ3+ø…°Êùl¬\Á¾,Çä¥Â ÷É8WZ’]Rwoöcœn$3~;ØJ.$±¹óèæè#cUZþž·¦G öÖí"rš#ù/"õ5¿BÃÑ0Uݾ‹ÒE‹Iÿ€÷³ ë-ØU0ÄSã1eÍt|½.f]8…?Ï_çÏ™<[’FÓÝw=H\ÁM(n,ãÎun¡gŸDçÇB¬åÕM—¢ x¢e@-Z:`ÖÕ5°tyð,ú»Ðâl4™"²ˆ]ßN?Ô5ðnÙ¥¥• ²ð Æ™]«!²õµÌÎ?\1{ÁL( .‚À,#:Yp¤þIéHñÞ݃7]0ɶŠ~-‚z(Ã_5ȇ³¯µ½Æzßý<ö`<<5,å–®œEè)Ibðr \²—„‹ZÑÄð ..õw&­NPû>D"/Ðêíº¤q‘-l–¯&ÁOÙ©Ýü¢>\åŒ'û‘%é10àìíòÉjæå¸ñnG‹²»ûðSx9Tì&S—qÝ]Ñ<û…‡Àûˆ8ŽNýÌ/IÝ›‡ãæ°²Þð/ªiÌMc,(œªÁ§_<ÑÝt;¾¢ W¬p ëf¹àч¾xo_$þwä>Ì‘U#±%ãÙ¬­›Güß½¿§rÛeûP•Û|²“t¼Ô`ç8;¦¬]†Šw¹åÃ6Õ4caon£ûqùŒø:ß–ºÝ…æÅ쬋ñëL"%ö—ñÖt%’|j/wwÍLS}Žç½NAÆêv8k[OW?3†4Õe|ÇÒ Œ?pŒõÅfÑø/.ÀÓòd««0fù]l||»QîøX"÷Qgá“\ê³:øèìM*ê‘uõÔ[]ƒ=¿Ü1çãPõF綯€Ï»¸ êNdë(e²`À Ï™ËêªÀ.ßg¸2© —+±õÙäË5Ž3ÌEÕÖ®Â`ǵK“+‰Ð‡1™¡°û$P"—cªøcR&’‹ê3áêN¢ž OÖög}ó>, "›‚¢Oi”9¤ŽàŸoSeT8ͨ¥Ü£eËé”K²¬&r© „|‰L8hŸŸæ ±`©|x~!,¿ž¡7 N Ãäœ|±™ÓzlH¤å½ñȆº9»ˆ;öZP¾ áÉM_Ñ^Ä‹HÃÙÊ-\à;ªY5–èÁºyZäu€?¬¼¢Î[ývò7F€w¢0Êî÷m2Ëb'‘2>‚—€oÑn,¨?—÷ÉP±){Ðî ]Ú鬋ñ|·an;½ï-ŒŠuæï÷¼ÎÛžm /çy°¦ÚvžÊãéȺà¥)¯x³uCpú¯E¼áR3×þ_þ _Ô¨r9\gˆ‘¥–Ça[D4/z1aJΣ9¿ñ©8&WœM«?‰fUðûIŠư-W¡aç|°b ûÒ7Lâšø$þ¤+Q‚È©àËû ÙKJ]¾ÅI’†lce~÷œÀñIÕß®Ç>-áõ y“="¬Ù³.oÎ`§}M™Ry$š–qe®9 â$È®UÒ`{M8éÈþ­4#}%£I"w\òÐ&ã'¬2bvêR¿w’lÌWâ&ðs1îE… Ž,1x6íÌ•g—¯ñäâîr~Óö`Ë–ž×Íþ7Úç& =ÜÉ£9ô½µ7Vì¶…x™XX£t׿Í?íòz´0éhèƒ4¿ÃXˆgdÉVçûX–ŒÒ’†ÿÖó9_ =\[û--.z‰¦õp0&UKÑéc¼K¦on+§vPG—Ÿã–ð, 5±vFòg‹”c³Õä&p6 „c+,ÉãY!(™„‚…˜¨.@W^¢ƒÏeÀbƒaÎ êQtÔï ÇMS$Ür$+dHæ®|  'naÅ-ÄžgŸ¹›=2\Þ˜~4N£«J?ŒàOøtŠú/ì„°l%61Q‡}TÛCý¯µÀÉyðO˜::[ŸbYŽ Ù¤ÏWpâ3%¶T뾿ø¼¶<€w6OQ§î8f^©&sM½²,!:0€|Ë^„ož‘LA®»xïÒ9ˆ~dÁ\ßjСeºÌäáöúJݼ3»¿áI•z:(~ŽÏùå«Ãšñéx'X™_‹GO›Ç›"CÄã"Ñ(g<ì%È^„Ò˜¨òw..óŠÅ˜!/^’]8ÊM³ÌNa5Eœ^¬4‹ø>Ì5lÌáϱ¹xg _ Æ:{ýGúþËéót S™ŽÙŽ0éO åׇå@ÂÖn¿f‡ÜAÞÂMq7’§)Dt¿z±w©­_ÎaȘ+ 'È\fîÃÿ>îFàCîñoìõ™Ëš¿´àöÑO1(r58jË =ܱ—v섲º'aøé‰$j{×¢Z é®rd­v>ê ëÅŠ9ÁÄ´ë$(dù³_yƘš},\Küï…Ÿyï¶@o³"Q.MÂÒ«çð1ú0ët¬=3´JñC÷0…õ͸G/mXÓma?qY§9ˆ›E_à®M‡‰í6Ç‘üíQ;7¶D G­•`k œy?ž†ráík¨ýaPÜo<«Þ¾½KÉáÖ0Êà ýäº …M¼pJ½&^ë$ |iÌŸêÎÜp4/m§, :_ÔLÁˆ5ÜÓˆBÞ™B\îÐIþšÕbÌl»"ì–ÀÅüVƒÌÍ—yçŸÎcïo¢É°Úyûªüã1éÐxLU”d>[Å`÷¶Mðýż³4ÚþÅ£ñ”(°° '=éµém4gQ?(•#¯–Áƒ¶d‘P>$ÂiâB j*¹Ÿ0¤Nfý•AKèÊHŸüæ·=@>´—MÏ=ÿ»ú§”¡†ý3Ì‚¥¸õ¸š9—¢¬s8^¯’%Y†gðÑ#q¶üH<ÛˆÇQÄå´˜‚vÀM$üîTb÷¾OeE¦;¢2èÕXFoº õÏç’Qa±üKépøø Tbžävš±Œx…4ȹsóÙî=gyu˧pMÒBÌìc ·ˆ¡b,Ɖ‡ŸÁÉtö©\˜z®—â¯=x¯g©±½×bØ}1Œü£‚ }¤=fç2¾V·H°Ž‘ØåðFå‹‘3{‚qrm#¬:‚a‚¯Š\áëíZÎÆfà¢<íiêTi&f”oöCè+Ybæ4Â’•ÈõÝZøªì=ý²ÏŽ^»€ñ÷šÁ¢Ê X}ö2ˆ_0!“GÀUƒD6·§^LÌá›O¥Ïá6/Ì&å÷ãûW[ßê1—ßÚ„¢f)lÌBæ¦>‹ÌÓKaw„Ëxï67£ÈžkøgÿwüëîÇVfûâªdöDܶ¥¶!¯ËŒ\¬n„%‹jq7fãó”»´°g+Ñ.,&®n›¨ç†tp :ÇûÇC°»"ZÅšpu¹{xu/¦ÏHúM G}ÙMß}#ý›Ï sC%/FX‡ˆV*‘Í×–ÁÂ2å_/Mµ,ö÷çîTœó(÷ÆÈ±Ã†³¸z­ðq…É>(ÀÝýcI&ÆÕã¶H ºìcg=[ŸãClDz’»¥ SZå2!@ÁhxÝ/FÎVü@³#Þ8`úo¶Û`÷®± gà¼z *i ðÒQ‰ É®dY&oÙ¦%˜°`/OuáWÎWr˜ÏÞ€€G¨ø+Èš6Æã¢x¥Â¬§Œ‡C‡ýAæy2![N¤Z̘ÉÄdñ×{p>Ì€ÌK´@ûC¯@âÆ!° â"²·ŒàŸSšŒØ«M/ô(2EÑ 2j}Àp 3@ãÙ©£ˆƒWêµÜíÕç@©`M!oŒžb”M1Ø…«£æ’ºý„Ü ò‡5-æpù².ˆhü©ù:Öƒ°ùôŸke®w²;%Hýq)6hÿ‰KrpùáàÄέ°"“I°U¼D4ú¨G–_\ŒÕËŸà&ÌŒƒoÞã‹ÞѬ58 Z* dŒ°Ë‰g"døx˜œa7C²‰“§·dwîÒfÜÿi¼1 ƒ–ü£cç„+¹éc&_eØ¥ôrn™XÔi8¶ÿ·v¡8ÈÏYEÆÕtâQõÙd÷¸zð5»õ!(Ú8‹¤=²äâ*¶"vW·g:×ÕË2ôY™† _ðátì½!ÈJ›wá•U>ä×V/ÒçžI-*dCékž µ$Ûµ° k{ùú·þÐ…SÆ’ %Kâ#T‹{{QRA¶w~Ÿ-\úϳôð-`ƒÀ&ä„ ðt˜·áHԲȣgð@œÿ²Y˜\,Ã)Ι˜{i 1¬ÝÉþ†i“²}erš¢Åü{#ñ¯5CïD§€Úd®¿b“Ùs‰µŸ‘a×íHІ°îê"Üàû—ïÓ¡JÌ,¤ˆÊ%M¶é«SÜ1ž%UT³ž CLE¥˜»­Ÿ„!]Á¬£Íšý Z¯]H³õ ˜ÖàÀúx©¸L+ ²ê¬H™Ë+ª6TAïÈ~«8sù5–=‘$ÅW}ÈË“«Àøx{.ó µþ 3¢èG–±Õ¼Èu·vßý0Èèνìûìv¤SSo ûY8J«CvØ!æ– 7nÏ"[¢ydÕqå&¹ýj¨‘àDòùñX­,ŸH”ºÁó’ã#ú÷¶æTèJàpÖÐ4º1_ ÛÚ#¯Žû">fn¨å~Å(б Ý^¨à­æ–fáë)øk\9—} Nç¸|‡æŸ7ART‚lꇴ#åÐ:æúÜQ'õ­ö,ÙXʾÄss"üèµê©8µµ7/…:‡5€ù ´1Û ‹&tBŸb µMPlþøaí)FªüYk´MJ€÷3‡íhlÊ}½ÁÙÞ°e2¹ÑU«Ç‘ó¯’±Îvks,Ãtãµx»ëšÝžñÚdZ(Â÷`3ΰe/“ï‡ „aÅP)ÿ¿ø_Y^EVóqºµ ¾ª<€Émâdìœp8b0„?´‚†d‚œ({~¥Su¶Ñc÷@ýŠ›Øû%lHñ<Š”ÉÍûvÜ?vŽÌ¶áÔØtTžnK~æWðžå \ƒüQjÐ>¸ žÅ>[ ‡ïkcùÅ9\ÐLö%ÜG©‚ôÔ Ìë¢ LŸæÈLÓÖ€È4Xwò"ÓaA?‚É[Íϼi…êÜ{7Ƶ%鞢LO@Äb/˜Ê^pÏ:g@o1.ÏÝI â:\™*Áj”®â™¦•\Jq ?'Áœ§^æZþïû—ƒ'çáÊ¿(áý•†>‹‚¸.qW3‹ÏU#?Æ»ƒP½5°J9V2u2Ñ=|šŽ7Í Û_‡÷5§8™ É…•'@%W§{ ²hÁ+¸fÞ8Øcò‚†;6À–±Ž0¡´ {ÄÈyÕ_0[t:ŠƒÊû4jÂöo~K(aÛNãk‹›0«Ç–õS ›Ú¡nk]üd͵ÙÎïB›“¤Ëf÷ꡦtÄCb“o?ÝC~÷¹ƒ]!p—?¿ít¦Ÿ“²áZl:HnôÃL­°uÁR´ŽHèµð^ÄÁççȳ‚ûGÚ§ì¼Þ·ÍÀ² Â…<b ¸ä®Ê2é;yøÎÚÎk¬àÍÎ OÔðñánN]n]ãï¹Û lõIîã:]ò.ô–Ì#çΦcC`9”įÅUR;°¼fž âÎ6ºà×&>riY sþÑÅé3ˆU´|”edù z~úþ§®Poræý„íXË}¾U‚ŽÂñØòOZ¦cÐÓPÌâ2]%²']…¸Ób_' axËyø½EJkªè¡còLr{ =´"¦Ÿ­Áæ½18Ð%Î\ ˆÍ®O°Ù³×ç`A©Ýÿ§{DàhÁS ñ9@‚Æm~“IØ–X.l‹HlzE+Óû°þÐ{H AY+'04³ævŸÃÅ/÷bÑÛäÄû<'½bÔ[`Œ.ž¦l§ö–Þ§Àx;gl•%¾Þ¥Éì,bÙäntêz‹_f‹’Zé=‘ÈæMD9c5¸²ÅJlý†Ïš»ñHœÙyƒë=ÜË;`ÎÍ_úÀìú%\fZŒ!7%$ÈÓ]ÄÑé.ÌZ†[o0÷—|”[Ü _CrÞ܇÷m‡xk‡ôØÒûˆKKTÈ»{SØAö›¿¼ÔÊ’ÆŽøfF$ÜrFEY¢'6ŸÜ¸û~:ÎeÏë+%Aà2q4MðùŒêÍh ¦DJíô‰¨UünÔ%£>X‘]¬0okL I„²c\ˆÅl“òâŽÍœÆžã‡zõÓš·ð¿cR3–£à¤bX÷ÀÖæB‚*‹•šËÏRw9¿q#Ö;Ž£khÐXmvD­˜Œ¶Œ·ë`(øú.f¾îB,¤žOŸªÙÀõ¥·ð±Ì7ˆŽ f/Å\Hlî1h~¼ ê¾ÝÂ+•£Ipáz¢ä@^ŠÝ¡vÿ Qó³*É™w g|Tã&âo7’ÿg>ì‡M5·pT8î&-…'Âì̺(Üw?{¿–Mãnæ÷£ÿ[öÉE­ý¡D6j½†ÛZlÇ“/°QGÃòhQX;½›õgÄÿ[â,¸øüÐ8ë;.w¸ˆ¯›Þà“{ƒ¼γ±¶r7œ¸} Ÿî‰ä‡ì£kžp´9†ù_Taó?Ä%OqËÑ-tÏY øùq)'[È?|Sžû®¤I6'p[}'我.xÿ“$¹µU žxÓõïÃñó²tûÞàjõŽn1 ýÖS9~’ v9Š‘‰GKàÈÑ=ôØf\œq–ÊžÂCi(lPÌi ™’Šû©mü4Ø··ZOà^ÅýÀ¼®ð8Κ[àø‰Ë±›6‡!íp Œ©r£?í#Ø¢£xÍ3ƒˆ×öu#øÅìPØýƒK\¢Ùc[E׌OƒqpVe)’zßÛøZa<ÆËŒÃ{OaÇ»$X²p;…[ÏYãñ«q°Ñ^Öïuj\|숙Gôû‚ zõFøž4‰­J”`Üqòq˜3¸«.†s£Üá­D øÞ°á‘$U’ÙS¥ËñÂß\÷nZ{p}síØ(ÑVÔzâGÎP)ªÚlÎJúãÐZ"¿ÉnÄœU¢¤Ü¢Ö‹MC¡ßo8±#aßæSh~å,N‘M€å2LLÏ’Hé Á½‰^Ç3¼ièòÃõzþ¤;šÃ¯³f#ý¯»žÓAúíLªÛUE$uÜqðõMX·ªjì§ÂÝ®B¶Ç@XN¬ä$9Ef_ -^Ïñ•{ÍkS†¢ØöBÒ†]WdðpJ,§œ0}gh¤|¿/퀫ª{ض9Õøuêx<–AÞMQ‚Q»€š…év9Hÿˆ'QâPG”ŽH“cX6¨°y¿r`ÏÄx8uB’M=0„µÛÉÂÝa$ºÀ N¶k“ º†DÞß…LÌ€OW“Xƒx,›ß”Ñ2`^1à*Æ š,uãxnû©zò'‘]˜©NÄf¯”ÛGìÿ%Ù®ñí lÇÁQ l)Í6Çg0¥âå¨àN˜p³ëuìF2oï)²¥‡‹`bŠéº©…‹Ø%©Qä|f!ꤓ3jAìÉqLÝòd·í%_ö­ÁbM}.FÞ\jÈÛ8 ¿[ôàySqÞ>ΓµmdY™eØŸ·R.ü†™7mÙ[Z0ø dÑCŒ4´f.®5†ÌW´fźñ(xQŠh†¯„²·:Ð9¹í êaªÔ:vá÷z&Pp#¼"@› yÍç-ù‡Ûîzq›Œ>£žD$KÞ–KKÎ$»4ÍÑÓ9qn©Ñ®Iý*H©¶Ò¾¾ÌS8„sG÷ Û󺆺aŸú²]˜y]SãþD\Dùžq¤"s Ûð³n?£÷ýűQXލ¨ôrr;W1ñ`]MÒSÄHÿ«:Z'Ä4÷ç2…ƒÉÂaNøóúbØ(Šc,ÈɅ#Â!ÕH’=ûóݦ˓ɧÞc‡ÞP»=—z_!÷«¦°´»2äâÎ?.O–‚D^”X³(á;Ø—Ú‰}ùøµ{J¶h²?ê °iÃaìšrׯœÇm‚ãÁãl ³—k†ku*ð£CŽÅzx2ÜÿûR¬š¸Î_ÌQ&ÿæ£Q÷®š~`„ÿLy-BÚö©†h_]ÇN{.Åm›/BEÓS4‡¬Ç€¿èkzmUˆ’%_ E0²®%E3ËR3æªfÊoœÇq<^ç~ëq]×óz>ïsëžUÈAs'G£öøÿ†|0hT‘Š 2(n¬û–!ž&Ërþ‚ãÂq]É[—bçÝøóœ¼è«çL;|X”X'rRœ’¬m»¤Æê–jÓ¨?'ðKk¯ž÷ M?ijgÝ2˜±îzÜjæuÅzÁZ ¡V÷aãþÛÜw¾XŒb6O²àˆ£.ÜÈ^ S‚^rŸ1‰Ì·6bÒ¶œDõXîÜ© 4Qú”Ï’ÍßÙÉH[öÅh sy}÷0f%fî`Sg5Ÿ‹A¦(“d¯x†ºå» Ôï5 —ËÄúÚÛ°:!xÜrÆÌ‹äåS†Ç¬+pûÎÊü»åÁûýΔÇ@ˆº]Ù…N¯~q¦?™Áç:XT$Ö=¯1ïòRìL¼ÊÝáJ7%›Â¦ì|Ûÿ«9:žÊî2àŽ|É;¸9Ñ™³j±þ‡?¸ÂaÒß+B#/âÛxc(Xµ—\ր݆2¸þQ:lÑßHkšAùr ‘a³Šõin†(s ؃¾ãÓÑå…%jÞ™'Ç‘¾wadFÎ+î°ä œx(†ý˜ÄÜVû’½Í¿ˆñ¯ Ns‹ìõ§jE™P0Ö=:-Nç-,tú=ô œ™Ò[…Q –¯Î&èZò:Ò&óïùüoÿkJ@6g«<¿~Z)÷ÄËžŽëõ¤žf6Ø[ÈÆ–âÉ{¢Ðãð 5ké£grp;t ýñõ }¶x S|‚*=•x5‡‘ÉáÍü GˆLEÑ`IfiŽÑø–Ëžà8)ú%66Mý ¯›[y?5Ï‚¬ö)XØô@y.›™5,—cîâ½ÔºT—z-{ÈúµÛ‘çfSöæ:­«é÷±Ê,³m2Ým› !'ìQ]0’þÔ´ƒûì7¼”Ì%æÇ1È쇋Úßy\¥æ^8"ÀTfgÓƒ^­¤9b½•÷ Œ³SÈ…‰sFêÿÞkHü¾:Z?`öáIÐâø›V~‡%;hœO2<]æÃ)Üážž´ƒsBß¹—«çP‹S>Ъ2ÄÏ,¢iü«Ô èæ•céïððïÃßÀ¦Ãƒ}×$Ô¯tÕÿ,ÃRœgÂYwæ‘·‡)¯?ñÇaû×)tNä-.ù›1»¹Óše\’¥•r’ìçø4´š,J^hQ CÜoM·‡“'PèçãÞ_yôf²1Ní; á»òñÐ/ ÚøeB—5¨ÎK¦Š?Ä©íþãDëV.½Õ5À=nÕ†¹sõè†;±lþ”®ÿOz¹Ì…–¶Ôó¶}â¼ Œ_­Ä¦Çãéõ=!0«á2öH§Qëßõ°åz Û<è…ÿF9ÑÜ=~趯û|ÏrãÎ-»†Óq©R8< 7nhAô‰'ž³ çFÜâ„D€•ÕÚch— Íý[Ž'žMæ–þ°ç/;8Þô}‚†& p{ån\j$Ï’—oÀÓS©ýÚSuç_Mp”nÐbNMÊøhÁ]tß«Îæ1äXt/ŒÿG˜liÚ¼ŠésFlΓ'àXˆ)ã2ñkûo4²€²éP|ïGXž½Ìw"Æñ 4¨%uÄþwó¶‘Û¢¥P/‡Rßærü§5dé¡X\š1ŠzV¹ÓCga]ßFÖwÕ–lÖ±)C>QÆ£‡™dìVÌo¾ƒ­nÌ1ä§$‡G™æ•,º>k‚Aºã&û–ÿ¶.Û$ç‰Ö7PlXÚgßâ…YÒÀ,Ö瞊wvNÄ© óhàƒ0ÔŸlÂÎd_Á€â tÔ=O®š±¹K”à(§Í2_TÃôœÁ›c¼)‹aÕ¼ëT˽?ŇӶö÷¼¾1É$Ëu5þUµa|m.|+ÛˆSn ü`%¤WÄ—ÜâüO}/B~JÖa”· ÛíjjŸ?Ÿé†,Êh;Šñ×aKn>·š% *á¹Òbr;Â/ο >£Ú/àÂpí üâ‹'Þ…ÁšKäB_øåiÑ•ïs@8àXš5r›žìáOSÿ8¬ÃË`üãy »&CkôÁ{y7Ú4xÁðºÜ¡Í’TXFŠÕüz‹ †/QG{¬…{›ŸâðúœnÕkÜÒ®‰—ÊWðJ¨[d’ÈÚÆèR×êp©xé¶n"¿ ²áy$‡Þ@Z›Þ“Å„›aêHmiæ‰^ÙÊß@âÌ{#úoÖ>@ËWGðçmníÛ¢–þŒMž‘ iâÜèH²ñQ,ëë¯ÀUª2lÛq3^±>ŸäÚdA¦R `Ø*ºÑ*f>g–¥Ù0+?}œOúŒ²ÉÐÔ&Ðk¿õ“Ø­ècœrh63­<Ïî>±`Vû•ió)¦³Ýfä³£–«¸ÐÇ©¼Þ=[9G*Ã}h÷ãµf>!Û·Nd…gô迯ºôdÞl__Äåž>Š‹&æaM©$Ó÷éÂ9GVr_Müà̶fPq?‚³Ÿk± »ß Ú¬Í0zör¶L*€í_¤ÈºfŒM+îBo’¾íE‹öJÂ`Š+¨ž½ë"T¸µkÐëܰP†ä£…¸ö÷b8<`„mðŽ»UìŠõ‡ÉµGt=üŒ›ÊðrU/§÷J”»TxŽšÓ’¶8u}9sÛæfƒGFö?NñŠøÁžSÀ{q(œ~²²èuï x=I4¹h(Жà'|sÁŸ9IJô4XÜFåº ˜ÜÝwÉ£Ê;„K±…ˆ 2Ì–³ýø;çw™?Æ‹«áߦ«`ëü®_ÇfQ,IÿË}Wv`ËE#aÁý081f¼ÏæzSéˆýcW ÀKé¹ ËM ó'Dð¸JíÓÀß‹E1°s# 9j°t=\Ñc©Ç÷ »&^…²©2lŒI&¬¾ÄÈônelîÇ·ÝÒø.,Ì«OCO£9¤ˆá.¯dþá*xu’ Î]Š×ü!Áë0!…a£½$]¸ðùï>§ú8üî•‚p'§ëÒcy;véÁVbÂ}º1Õ,¥Øš_séê§ãØÛþ¥ønÿ*cVØ” Z•aUõ"X¯6tla6jT£¦ž,LgMc+vsŠ ÌQ'<€\ybÇ™ZHŒäÿÍ„¥°z`È×BhÊ]ÐÛ³'e¿$ Zoó§ )ay¸2{¨øž¸¹Š°¨Kg¹ÄéS¸­3úH¬Ãô49 ‰Û=ÀxÒ'ü3ž•$ì@ÅÜQÜôå-\ ]¾¸^fÜïÁâ‡b4 ]ŸvOâẾ6ßžÆßz,Ï՟æÛtj–åÒ[9õ/âl`ÃEô¿‚¾Ñ›‡ëÛx»º6=‘ÅdÎO¸‡%óÁsÿhvpF. Ñy@;Ý0Ûh47?¬î^Àƒê»!tâ,¾âò?œûj( A©#=Ðt.϶–cmbÁHýçéˆcBú^ð#]b=‹øóãïwBÿätð¶˜l_²j;ötùªqì~Ù‘‘o°´hùï=½Ë¨dÞÙãnÃOâý&±Xë ¿Åôtò}s+|:ÏXö ìCçâ¶®x<·‹[Ѐ°=P±b€Ÿ“¿Š…?ùAe+ÜÉYøLù¯@9ÌîÖ`ÀÅùl©w ûÑWŠ…&öP2J ^|)&wáð5\•“)޽ë+e¾ág¥#-G¿¾ÅëOÃқ⸶ã1†þNƒ†<º×5 }.®Äêäÿ}ÿ{7‹µå¹>©9ø¢WåZ“G‹·`€¹8:ý»Bw?ÃÐ9¶#a3Ìþ”Gölp¹¢ãµÜU¦·¿‚¼‹öàvµ„‹…é“Kw¡}z orH¸¬<ËÅ×̤›–•Á?õ/›Å“ øsÚ8)ž\¸;’üÑvÕK¾íê°Ôã †~ž‰i;Mqû·duóF|ÖÛÀs½>ƒ’fOR®¼ÅþÊâ¾ÜΈÆ\5ðh7e“d¼é{߯ü]WÛa¦ ®º—Dá6qú(ÁýQ4ì÷|æ¶}è{Ïçr«ö€Ç–|ñ*ŒÌŽÑBÉL“OÎõI/Á`~˜÷t ËÔ‰õôeÐ_¼ wŽìýk …›ÊA˜rí:†½ÒÙ-§ÿoÿ"ˆhyÑònª]»^JNãÓ#'È}ÌrTŒòŽÅþ çàÏîï0ÛA…¶¼'©ë«`³B"jï"¤bV$+åºݒ´bE«K]x‰Ëý_B'ÒæìCü&Ê sRy~Ï¢zßÍнi¢›æ¯8>¬çâ.ñAÒ¼O.§³ôÍz89ï ”mËÝútKà“}é8yÉ8V±§¶=ïss[n³j:owƒ$]—"NÍÆÊ*ø+£`Á’«Xö+ ôO¼â ^.ÃK©‘ø_W¾™¬»«Œ¿ DJ">4¹†¶LA7cigC®´¦§Ío‚¹ºZµéÀÐmIˆ±Ï,tdYˆÛuhèÍ£/.¸¥v ÿ@Ÿ?ý8g>µ›ž[_|E«Ëw`V×]’ãro¾xH5(“˜ÿ–´†ØçzøðãwPÎø…ÿ½¾3h¿ÜÛP I‰Gî‹Cºùl‡é+Ÿ`ìóƒ¼ ¾Lÿð(Ýþ‡ÿ¦…“¹s{&±4É}œb÷0<(å¾í¼¿6n_qÜ5“¿ÔY §õÀøg™ðרº¥=Ö¤i¡Áû7#ög'­äwë![ÅÙx¸ƒ¾íŠìÝb!zUù^–NDÿؘ\įTu„¦»¸nÇó\ɸ:hÐ8‡:ÛÈ[Ϲ$Øë/Ê=X 7¢`Øg*ûö>è…ß&¿žZ‚m• 3³ê‡®>°¡µÔýƒÈÂC7Ñ~‰·ûâ üV{†Û ð Q'GX°•¼èÐê!ò+m4Œgó—æCú Øo³ÇÈTâÏs`þ& ü—…ï²p7WØ7Zœ´nüÄYõvbhîqøóG&P®²Œ>f›¾àË ¥Pº×o˜Ê*¸Ï#ûÿ£Nµk¥à5yË0G7F¹ ºlE€ ˜Ï¶ÅÔÁc¨lc:.CXŽ8}÷ðîË©† å? ß÷7JÅtâ9i:m×yÝ"EŸï‘aéì/.ÐÌ)>)ÇãCGðï+òö‰$í–lD/eAêÚ9‹J˜}…ßGZ‰¬¨'º³–"Yšºs3XÈøÓ©[ýð¯äØ, «~ö¡¯Œ<êîÌÅ‹/;°$pȨđKæ»pͯSøKh9V|:ŠÉÎÍJ°Leü@$hˆáN?ˆ›ùÇENrövWðî¤GXΩ3û_‚08`6’ÿQå/QÁÞ´"ÍØjk.KË¿´‰A(óbã„eè“=Ú,TjÛ0ã<')Ò ssÅ·59Ûh z©B”žP3§šÑ ÅsÁ¤‹*lçóFõèªGP× ï µ1ÅÎgdÿï7× &½Ü³…Ü×9§ÉøÉÕøpûAøÚ ‰ èèèçpØ)-)†3b=ø¯Ê'ó•h}s> 2¥ŒœîåÛ¬b‰öÃnÙÓÜ«¡4¸Äˆ§ô!SÖ¼Âø›g`YÙQ¼¦aŽÎ;„YXƒ˜dZæoáDÒîÛ†@X“Z>-Ü/9cú_M5¶Í¥çe?À‹)ƒÄý¢(MsPæ¯ût ¢1å–:ý7Ë3»Ù ã½¢Ê-™x¿ùÉæè¡t… 'û1aÉh<\)ÎXØUÜñM‡½¾}'6P^mÖyjü ŠŠ#õO>"%dÄßB|l4ÕÚaKO­=…cZ±·ç6Š/ä"+1ïÖ2ø¯YµòÿÏ^5j"6€B!JðgQ$¿Ðfo»½!Q¼—[,o‚£a×1¾ìLd§W®æ¦×Å ×6møðó!*T­ƒ7i‚tíõI˜Ê)ÓŠˆÇÜ,3cz¹Ñ5³Ø„OÔÜ3®Í˜Š‡¦)òo>_dž¤Þa@¤^=ìÏ6m1e~\PV:•ÕY>†× ßcMáDüwTŸe¥trNÓ”¹ÔåC..6á×UçœB; ªWžÓ‘qÁ·ƒ¹t³‰#ø§ñs ,=ø뾞„žÛ£Q¨ œž ¢¯¥¿ ðœ¸²;’Ûë'‚yŽà¥”nNðfeº´vâU$uèå-Ÿqå” ”ó<‹)Ý÷1û±%8}ì†'ÿ2Ù  ¯½|ÏN¡gëØ­˜xú1rÜxl_H$ëZgHûs`¬Î=hŠg¯r¢™æô,lÍf—À»¾(*8Žº¾œ nÇñÝV%&Ôz…X5Af‰5ømS`õJiTó,a«-бt‹$³Ê½BBþô~⺕{X›ÖqZ6†m9…!¬RÍ•²¤k\ºÂÙûÍÒ¸ÜùµpLížÚG¶µ@ëœ.\—òߎ@ceöº4– tÅPÅ„­4Y|#$Ú°˜fSLNú‚=ùðiw=þ}„UÛîóÆt;@0/Ÿ0“÷2Ôr¾7šî|ƒ]ËŒˆQ›û:_¾ùQB%Tq÷ŸfâgÑË ÷ãS¶½Æ”Ý?áö¸N"¿Ò¿ªÔak{et Ÿ•ÛÐú . ½p.mZžü­ÊìO¯5›ç*‹ïªÏR¿‹hÎ+òlõJ2G¾‡È¿2§-ÀÇMŠsÿçÌJ^'^ÓÃýÈulŒïDjÛ#=¢*Vs*‡‰ÊcAzu¿—SbÈ_V[˹ñæaû}÷O™&OàºÎ›R¯ÕëÐQî=±,Ùcæ^ÇðÙððD*îKɆ%Ýgaõ§DÞcÓH¾]UÉ/øïÝŽ‘ŸÇ\.må•$O:\‘„¦sÞ— 9‰·PÇC˜–Šßç+ý0±¶í§ÏÂu3p´º ˜ñoñ¤†fÐûÓ`“”;Ò–Ëyþ!ǾoR_|eäBNI ÇŸÓÂ#Þ”ÝKüÞkqÚó‰øQÀˆþ>é j«ÏBgŸôuDZ‰‹_BƒìG´3¾…'gþÿg΂ø#®x³±‚´íþ‹¿§Ý·6Ex-äD‚l AÊ»ŒBå!÷ËWR~þwe@‰äûÂ+S}úÆÚîD^åŽÄ 0 ñ <§gZ,Ãf"޳ÊàfÎùŠÖììÏmÄ«Ý÷áô¿îù÷ãðúe,ïî'è*ù Ë^ãV?פï_@iÌn—ºÒ’}”VubZ6Pûúz,{î }÷¾ÂÉ ||f:Šº †`^åBPnIC _nôfi&ò³ŠÎÍëÀ'rÄk”)U’a«fÀU›·  OÅ÷ [Œ,õƒÌ¨ùßü· ¹FÞ¤›ÏH…œI ¼‡;?p’ó`þ•ç0Éy&‹IL!sê$èÛà—Ø]º4rî«ö‡èDZ ^ù¶¿#~íhñi?×})€½ÿÐIú<bëìBÔð’`î5úÔæµ“ Òd³àÅUaÎôíаm¸,œ ëN^§£¡qÃêl7ÈügÔ>oAC‹…äKGŸ4žšˆºâà¬5ôêî­8Ó^LsÆþw›ÙE× ÜËÕ!øpb7àý…ícK=~Ãôy.höËŸ¾lÄ_×€ü)–ÌÍÁ …ÖøÄ6ŸÛP<’ÿ'Ž-'AÂ=Dêt´ï—dC‚¢à/§ÃôÛÕhó]T—ŸŽÏœ?â<§t4Ò &‚cXOr Ü?@ÞH|S¶sjîøÕdž²â0^„‘¹tT–ÎàœhVÔB¼fY‹<’yö‘J”y¾iI˜?SÞT½ƒÃAr”*oáßñ\ÎT¼0…ŸV¸îÜ~—}!Q[m鯓æLqµ$4ýƒ¯kpí} ùj,ý¢¤¸æ6Ú™±¯×§°Gž²Lÿ»8¬v˜ì Þ :]ëq^ô|Îêi2$hÂkó@°¬ŸÊ”v¢~OFüo¢-Købµy .3`œbê[ ¸dÂÔÏázÇH1"Iß< &aJò໲Ëœö»ÜY„çŸ(R¦4‘Þ#œÐ:ï>™€EüƇ»˜xF1è½ †ùn¾¨³·t$ÿ—¿‹Ó>ã·oçÈ</Å&Ò̳¼+—êAÕÛÍæøÓ?-2¤=ouy6`G3¬˜V‚ïv›ã•¨*ô~”O²_³sv¡¬ÙTè’„¿3·±Ÿ‡b gZľsIY)N›ÕñÏépöÕ®®ÿ÷ _]ã³ÅÂlÆ1”þ„ã×J3+­E\ʦå‡À¥qÊ}ÿGîé+ÒÖ’ÕÄG8—ú¼¸Ž?ûßb¯ÿ4NmÅóŸ¡AßPé-+­^î«ñ~ôžEé}ж¹v|ît4Ø Zò’ë¨mKþ©¡áB ý6ø_øë`Ëžñ<\Yþ‚×;eí·×îã-2‰d×÷Îc6ŸV@§G ®>hÍmÃïuÆõJQ\èºA~bý,6kp{%)MNZ°NÍxö…/5ù'”Õ%´ü=¡Å¿€ˆüIü¼Uˆ ¨öý²¿FŽÅ‡­Ã…Ñx:VíìCS5önü]Ò»è9éo)éÓÆßBtmÚÍV–‡ñÇœ°ã®ì Jcš¹ÛëWbV×G¾Úw¶dQÕýûŽ4ámµ\^` S©àŽUpì¾3L)unÎdë“C™}Eš÷\DsÑìûÏ69gäˆ\¢ i!0m'µœÈ‚l¤ÐáÅ9´‰oE­ zx,÷IYó ÏykÛ·AhmÌß; ïçÁIW3ÈÐ'Ô7qÏVs›NóWä›1"¿üC/sÎÇvpÃÏxz¬Õg|G#îëÞdP0:ˆ3gÇF¯G88WŠ èá…ÒÃõ³Ð]y#JŽýµðbÅ9Þã•õÛèù&.Á¨þ~àÅ(Ê3%Å¡1\Œ}}Œû!Xdøßÿl‚DÑQÜΣװÊc šßL€†v5ó¢€-Ø ›×õŽà_PÒ rïq+:òªÀ¥I wÏæŽÎeÛY úhI³Šút¬Ü²„í×Ê‘ü8^¾˜nšp7ÜÙ §ç3äÉS¡â Œ ¦åÏ1¤G'ý­$2ÑL1ó!<«Å®œ… l5s'ã._¸÷áZuŠÂÒ`O8˜ wË|xoüw°^» üje‚×' ÒN¿@ ­ÑÿÌ üAŸu§°Gò { “‚Üå§!hµý"Z¨Ú“Îè–VËopÔšuÏåG÷aØôb11iŠÁ¢ C¼ßjê@VhÓEcÇCÛ»S0|ïû¾^>{Jp`u%.·ýÍMœ¿>q³¯iÓ _øsÏ 5_¸àuõ:¦tõÔo=ìƒ ´ãñ2~]¢7º6/„™fãp¥¢ ÷òÝMØ»­×å“Q-·áâB²Êj¥Äѽ‡Dé›3»0<Ò†>mÃÓgl©Šn ÌXü𨣴ŔBóWXjŸÉ‹ÿR ã§ebÜ(Ytذ­|ÀˆMà ªduG7?2bü֤΋”ØôkA\ðãp^´t—ÝÀ ¹îÜUs*ˆïμäN?}I–ü˜LIu5ô‘ƱSäöƒwö–‘øß› ËN u÷^Õæl¶½,ý^úî]И¨ÁÖ‰ÕPW—%(X®K÷U½¦Ë$®cDH-³.)¢f~wP±£ ‡~ÉÑÙ'<™€G*~>£EgNž…G~ަgQ™»ñu–¡CÞtö™níãl–á–Á—šÃêNè3Qõz$7™ÝrÛ‚§œÇqO&½å:¾ÚkN×ceFŽlR‹½Ð² uÌ:ùÆŸ«iü ÛCé¥÷•¨«/A7msßs«¨ÿÍI°ÒÈÚêº`[êIàGuP»«ÿ¸ÈÏâôãkèøqŽ›ViN¿Íÿ_þ7-¬Ç M¸Sl{u?†¾ÛÆ$mËÀ=- Z>§2± ¸gš0— @^ëL§I@Ù×QðsÐïá’ô¶¿ ÿÏÓ8ÜÑŽ^Q»áKÚ™&á³Aô¿¾ä{¡ ޶$³w——²/ °RÁ 6šÇ^ÜYL„7Á²oëy;gËRíWqé`2}n= •é€ÄßÐ@šO¯½˜Ã´ë£¹gbQ ;8Þ€ÌÙ6•l;ˆ?„Ö1k·œ´Öúîë:gÏQ#jŸaS#¿Á‡ñe4yJ»vók¹fHç;ÃÜ aPã]‚Ë.ÁœYgðZ»,Û<4:RCðŽûEz~»,ýžèǺðyyѽÃp¶ÚÁ…JÍIâ«å/¢C˜ñj)w´®H°1/-`«_ã?xNÇ'íÃÙï¹ä3"t˜/‚¹$3_"OåUnpVÆ–tÚÓc°vù¶sÝq'gêü׫½œË™&ìu\‘6ejÑw{5L¡úË‹qòãÅda‡®Qœãeº´xïÊÁ~—¶â×ð”¿f*üï÷_gŸúß–uà´AŒM«6dïÍ›°,ÿ>ɰ“f‰O°>]¦Ž×d"3#@I½‡gNãöÇ+2ùG™£| G+s޳ ï[ÔÒÙ0Öæ Ñm;Â{Ɔ]Ûê@·m lL3£ 3`¼Eÿëk®ÑE¢4Ú¸MÓw@ט{d­ü#ø«ÐÉyœÉá¯p1an6¾>®3³¾@•íK²½Ý 7I‘°K>púÎ{ìË9JâírÐjŽ Ë–fÎì¹µ­‡ýí–„ñGõJ|9š(‡5kÝ¢&5dì*=bP©Àøç£Fì/´*}_!ð“${á–ÄhR?ièp;ì& asºä°œ¾¸–Tä}Õ¦'›Èä‡Wñg×>Úÿ{/Ö¤£¯NrËÁãÆDï¦ Îz_ÌJWýæv?“”=›èÃÎÍñç9Q“9lžÉ_ ¯E<´b! aú÷0å“Xj á¼äÖ# _/{ „Î^öîüLÆíêßù++£`êäçè^ÛJRY=OúÐøöXˆ9Ïw«x-ž¿Ëbf‹alW`=Y}׿MpB™À~§WOñŸ®±A=?ðëbu¶¾d¥ö~„ÿ||ØÅ÷v)s~ù”âo—,\ÌCE=úÏa_PÄ8v¢4lá4et zÜ>HHü ײû!ªnÓR'þ×ÿbûΓo=¶Ä%žC®ÙLsÄ5«˜î]iöòÐ>kîNùËþá÷eËÐùe÷qÿ"&r¸žØ-IàW¯È…@z"V™<ýÚÖ³ÎJ4EZqtÏ>6~ÙòõfÛ>ÏòÝT¨®úLXS{2mêùÜ• þ±Ò̵?€7[û5wv™m{» bdòHºñ;ÔZÒÆ\ßÌcbmdÎÛ«8óÁlZݤÂMm7à(·¢\óÔ.?^^¹µÖxlÊü¥}?L¢¯’NÃ2ikøX­ÄNzÙÿ&³¹u矒ؕop®vQMG=UJmmY¿Ñx:t×5ì]HO¯5ÇNž¸Âs®^¾%÷5sG½žá\úÓÀ€››^B/œó¢}GÈ£å³iæ~I8ýìHºœAýÍÚÔì˜ÑæEs~£Ð—ô›ÍM0{Þ¢Zë›°¿úük_½ÙÔ#4Mæ t79/ÑÅìÒŠ]®ÉÃÆ³™4wßeTßO`ƒ Øz¥Ã5Í ÿ$µè‰akbÙ¨fmö'¦Bë 1úâRœ#Ê-AzÐË•néz‚¾ï³¹«WÎ#‚Ò${0æŽFÜ< 7ÛS˜•˜"†Ž:ÀÕò®±KÔØöž)ìíÄ©ì°çEüð{ .ÖtamÓ©´ø8ç? —ù/ãÓà|V#V¨Ð½ü:|%tö§’ó!s¨Ú¤èòþ2ô‰VÁ­ú+(lõÅíãaÈ*Ðýx€¶€×}3z¬žšsv±ËŠ—PôúfÖ!þ‡Å#8Ê^é;â†iá¸FPˆ íâÁËçÜìötã"üìZ AÓé5#æ:$M«/ýå²bMiîßz¬“¨`‘q ÙgO¸Ôëû¿©™o¸B_:ÒËŸ´yÿĆ}þË + :Àüõ\£~:?ÁÓ>“éMë30ú‘Jÿ >Ûþpã¢>q‰j tm¦¹Q~L¯ù¿³õ²5Mð˜a¾¹Ö I—#0é›;ik<’¾˜R¡3ÍÃn…“4„É¡?'aÊIЪÿÃêÜBòl,ßjþ÷|Ö]8±è¹-ú—8ôøÂ[\ÈÝÓM5}`ëÜJ°ØØ/ }ήízìijÉäôêM8i´–uÅ俨ðr´Þ/ c<¶@pÆBÖµ‡R#qÝ^ÖÙ¿p›3Ÿh]¤±m’äÌ‚'\~ñk¼M÷À½–4)5 ­´·°q7Îót%éñÞ4r:{)(­Qd•ö¿pùÛX:¨­ÊöÃüÙ6œæ˜Exöf —7Û $EÖâÉÚÅ ñõqÉ5âUJ•“S…pÂßÓ¸}Ë^8.³ ‚&»óöUˆèîµý¾§¢¡Ý¸Y¥Œ ßw²9®“I”ËO¢­Ê¯ŸäÔÄ=¨v=`ÏζFcý‡XÈò f3 'Â¥”ÝüØd=6w2»[³ôóÏTy?¦dàà³dlThÁBEÀðÄŸv®c{ F¾ƒÖ¯Åhc†5y'îlTfÛ—Üç£.“gYÎ0N…©Wjø‚FS¨qp?™º«Dûrî§sÁØówR¤S[–à-çP|tÈ•ùó’IBØü×8…Ο!ÃuÍ€/Îr7Wqà&{ Õï&B¸žŸÒ¡öá´Pl4ý,­'ƒÕÙþŸûaFÌšæI9!pï^¬€Uƒ‘p?ó3==†.Ùº“ó´ª…ç[ë¹’¾$84ê=<>ËâÌGøÏw?ã?-_Í{oãiÓ>øh­b¤†s4º¿àÌŸ:l%þÂ{«d™¤]9~û¼ŸÌOV„vìTþÛUï§ÿõ'¸·!Ù­M'fk ]u9'fpÎD€©Änx;^ß#¼Ø“ 𡽛—ǽA•¸¢;%-z¯á£=.-3·Ý"ǽ>ÀèÏÖsÔ›µ4€y/hæíëP÷a&¼Ó^;£>{¹ýº9#øç2I;¾-å òŽ!Ðwõ3ÞÐóý¯G˜4O™Dz*ã‚?ÿpÃøvHüû™?ŸÉAgw$NÐÏ!éê´@ü2¬“C_ñïü¿*n {ÇåÊm%ºÏ4F-ã÷-?ŽW$°%ö2„<*‚EGDh¯¶¶6iÐeÏ¡Ià+aÛ’¹¯OD¨ì|ï¥ož ÑÄ»¿˜}ÜiÅírMT§B“¸Íêjlp«0½’X…J7ÌØÎ3˜¡äcDúÒìNž7¸Žó˜YÝ€ÕseèåevþÒM’½ø¿÷ÝQ¸_kB•ÄΌ俩`#Ñ.¬‚ëßÍé´±lFàˆy, CÛîæk†u®û¸yýq$†u¯H $úqóŽÚ€ºy4”ÏÕex;øºˆ²#9§´ˆß[Z‚iåµÈù*ƒîç Î?ƒÅ玃Ú/®ò¹ ¤g™°ž@zpÑyHëé"j‡µ™ŸÔm|¸û"o¥Xl8<Õ*XÁú6ÎU2äSM6e¹3&-7§YEÓ@AE;{\±à˜ #s[¢&JÃþ±Kè\AK°õ¼…Â>Ô'.–5 óÃñÁ*“I˜ŸÝ‡o´àÈÓÓÜ–ƒ—avßÍûÅ÷Ÿ$uQbÄ:-™ÛZüŒÛzu/Þ¹å‚ %‚¹ í±ìÇML(DmÇãÄ— óü¼¨{_‹Y ÜãÔDzq7×òëL|ø€Å»<‘s¾™ðÄ®sÑ{Wàõ^ž¦¿?ñkLƇ–çp¡m¶ÝkÀ¶7`J‚ÄçîÆã+šyÛ‡&âItÆÞë8a¦<óXÌç‚Øe*’:Ì3áÞ†›è>{"=>; ´}„1î ,Ž’4u9ò¶ü¤Cüô-ÄI·&º‹²4eQX¶±îê¹ÒÈ{9¥·‡ñó˜½8¿» ~9‘‘þ—­¦·áñl èi¬‚8ÙqÜå¿~пTÌ„ý ¨Éx ÑNG4jÁÖµSA~€GEõãz¹ñ˜ºâ)º×„ mðr® )6úov4'×&I üäéîîGœˆíGr®Âõ#økæ×ñ$5U ¡é8—äþŒ¶µpý3­ 6d¸>«íƒ¯Q‰¸ùà ؓ›‚^7œ  Ü•]ª £%ïdÙÆèÝà铎æãøÛÂÌKHµå½óþ94Æ1ñ¸"á-^º­ö…ʸ|A\÷× çïÀâikI„£7Ñ-(„i<ê]WˆNUµ#öoÌ?€•\.Ki^eⲋ "çðù’4î®Ã2VÓçÖ¿ão5À¹h&¯>J1¡œ¸"U´¶Ó@s;~îÝö±ÁÆìûÃ0áÖ,öÇN’­É»Ç|¡8ñH áëå¸<*‘êJé±eFÍø8TˆÖñ&½ûàåÅH¬ñ wŽIýo/h,ãOÏ%¿.Á_ 5V´m¯kÃ-«±{ø¾­gý0ÇÉšëÿ‚Ý]JZ·±¥yL³ž Ç‚v‹‰HÒ¶k GÓé9…XìµwäÚ*œiç%ì¸1ÅŃ $eßgNåŽ*=x™Ð™åº¸î¸”h®ÁWoN€¨÷]rÝD‰~ÕL¡uí`þó4ò¹QtG· ÏÔå=LýîDí£Í™GÏðW9’¿¡P»5õ}´u…UÂÓÇmXñÈœKŸì7¢3V¿!C§`õ„bÞ¼•Äîð$üÙ+{­? cÅ-~¨JgCNqÓ“2ɉE— l™>Ḕ;!JÛê6¹ÐY•Bìûúó"^½Àˆ^EfþÒ€]»«ˆWïÙªIÄxÓhšZ0Šêmìã>åªjÔ0ÔXLÂi<#^ŽÙûΟV4ùí&´xÜ íz ÷5ÐHä1½5=3†ÂÛsÐ Rá÷s±ÿæA·lŸ×Ò¸ýz±øØÈ„þàc[äÉ.¦÷XŸOø‚GÆS‡¦$’ÖЄî»|hû‰#ävÁÿ~ÿ1íì>xlÖBt‡bN\ª¥äòÝø·>÷¿ÙÊð̨„œèNæžúåC÷£$—„wŽÿÍ[æ/ã5ÁË93Ñß*Ûf+BÇ›¼ƒ7‹˜D²ï£‡k›³6;½§ž«ËÅN0b{­ó¡ntÔoÍá™+°Køð´îŽ õ1âxÞÞWN†l7ej}D¤ÇhàÎYõ¸ù”5wxÓ!Xøí6~ªÅÔL!ºáú0J4ãmRà¯e.‹HõæŠ{îðže¦C›Ãtºàò)U !ÛÝàøØ üº£Kƒh¯B%h[UØ?/n)¶\|É—Œ­ä"®}ÄÎaîѪË:&¨Ñ[' ˜˜®;P rKl˜NÓtÖ$̬,f¡Cëa<¿‡mW_jTҲˢWÂä$*öãð.£Ì¥HvC£ÅŠúñÝèÐú³”ûv²ËëÑXMr·ÒEj{PøUÞS籺nZzÄÛ(|—ÿË"ìO5 tÿ~0o½žîétpêcX+$F{¤®awo ¿LY/“Ç¥S£õ[X¾\’Mš¼œ]—Pà'òö0ÝqÇp¾U ]´59´à÷UÃ>G/­.Á?ñý“€û’È[ûâªQ ®òO˜Xà*“ýøìe$7åž$[1ê€Ë& ¦)œïÆËP«×Ïûj 5—Ui‚FYf¹².ËbeÂ]Âö',(°ó™øåÔ&”­|©«ÅM6ÿÁ©'ùÝO”Xiÿ þƒñO¸É_í¸ïßÎàüŠÃzé0Tvv@ý“n˜0T»uìáý«Óø´k7¼ fÁ}ó,¨˜å gŸfsüÞpÙ7 ì\yb²Y”’‹jlëŒ î]Ø .Uň~¿& ãŨås5ÎÄ­ŠËÔÏAuŽƒÏgs¤zÄÿ¢mQ\bÏYè8\›÷ŸßO#¢ë±û=¥aJ»ÐéI&Î`®ÔdU9PóÜÚª™ìù}õQš½T5¡7¿;2«méð&½ÊšÑâG·Ð{Š"[ñþ¼µGaó$6[ëDËŠÒyå¸;rwpEL8{c¸†›±ò#Þ}.Ï$[ƒžAå•%xMuºG±ã» üüD~·ãV|èš„jû[¦X¨AIåøòŸõ_ÀU‘¹h3ʉè[Ä›ÿ…ì[ºX[œ}X»3}Y6¸t/í®C+ŸDâcSoúQ#‰[¶Rs¤þ»)B±é" P÷ óôÆoVûñ¦÷˜n(éc”ãþJg5–¥@CójpÃ~ž ½…ÙoÒAüÅ7ínÌ,bpé£ýÜ(­AP|"@s®z³ÙoFQ’*L5ÿ„ãר2¶füQ¨óND‘…ŸZöNÇCW%¡^ê>,™˜ÇóhceGW0UÑ>òâËsÜÿµ Ÿ^O„Ôü¤Ë¬¨dîrfXH[Ÿà™éû(SÜR&¡4n²3kãFw¨­6þ,lðY‡ÓŸŒã¤»aÉ<)ºüe.K{VCBv…à ½³¨²á2H‡ÏD ê#û÷U·ì^´–$Âq£Mè_)Cû£D™ÜÊíÑDó¹¥úo }ÿzµtÙ™Š±0gÉ,°l%kwc“X>Þ?· „l~`l» üi—Â/g²û2àJÎFÌ2«G-ëXöú.±Y-„Ô.‹[𲛞%Ç"C&ó&ê:Ò¨#ó9þÝûœøë$êáÏÀ~mD.ðÆÿz²ÇþÝ€§7¯d„?æ=Kï ‡ûÏ‹Œ…EVø´C”òl³p»ŠÔ—ÇcÐÌ8žõamøøÛ€}ZOØè­q(¾ˆLLÒ`:;DƒèÞ‚Ü=d¼Èžä)K¦ú@Š~:juB¤ôAì¡ôö›³ô¦ËúÜÂ2µmpŸëfzµwŠǺA •L\¢té4¶^M–¶pJÔ>ä6˜>x'W¯¤Å7†ýïw‡4×¥Óþ%øv»¥F_æÔMÀùš9,íÎ]—ÔâËëÃ6E ?ñ³î øõì(ö(\Æ´õŒ·Q$ ÷]Ö¡âMhYV% ;bÿ®•:øw¦"2óå¢_jâ§I,Õd Ú•G*²·€Á¿0Vº9‡ŸÔÎIF¥1›Vð²{/fo8†þ“RqÒŠ,œ™»'8Ò¼ cÈ•xº\d3·ù‹-+õÌå¿Êª]«ÙxñÕ#œŒª?ƒÞ'3–Çÿ GO °‹;Áå¹£Ùq ¶nМ‰¼›“ò0QJžNœµŽmš8™¾Ûr } À°µ¿›™Ó‹±ÚLÊYŒC£pÍ«z¬¶o\B#Þã&1)9@ÁZºj~3½÷Úä¿khÖåËèéxš©I¥ÐÓîýÜàóÏuÖçc›™ôEó6»ç_+YLaÞ2¢s}7Y÷{ç{Õý÷ZCûIÛÂú¬ÀÉ-ʼ™öÕ°=6Ÿæ<„ iÃׯesµÂé›Álš²Ö“(À˜½)ÿÍ]fŸVÿA/“"T˜Æ&λÍ]žÁbé%ûÃGÁ{²˜Í»EÛC…é]õ8ñn5³¸ã JwèÙˆÃø±Üƒ$Žy‹+ÝN}×Íìãø{t ‹îÂÃSkÀ«²ªO'³öªr|9KÞiœÄbœ 9‡‰m®â̬”›°çÆsëf½Bز©#þŸe´÷ÿNÓ;ZñY~ÖEñ`ÒÎfˆÿîÃýàƒøŒVru *<Æô݇q©ƒ©R ¦’>ÜÌHG¦UJ&Õc_öÁ™ÅËð°´>­á´èb¿ƒø4enÉkÇ´uïÁ¾ˆá3Å!NÃ{1:ÔôÂÑ7mÐl¦Ÿ8uê 3 K=iÃi‡Wh*;ƒi©@útC‡ãÔĘÒ;C:£ê'>fNŒ·ÁNó8ïØf ¹ƒ\LÁXŸÆÕv8ã z/䥀‘æZæ×³ ×H“ù1Ú¸lXϵ›É®á¿ÚÆ]œä¢rX,3 Ž~_Ïû„ÙeÙÙp¬Rq¾לd…ät¤W;IÒtuvhð?d/+سCíÁY*MƯS.¬©'Jj[„¸Ÿ °i>Ë_z³ Ó.q7o/Â-Ô!ò‘8{©ö nTq‡ä磡›WØjÄÎg4 Ê›`8à]Â~¥ýAå½±˜mì Y×þŠc¨ûõ\Ÿ8•Ú©QŸðR«¥‚{SÉÞ°ð©){î4loÓ<<¿ v4¼Ä›]°nJ6™* ¤m"]wëÞª>„þ/RaYCÙHüwÛœçÞôÂkÅlœS¬¯—+±M‡P0#Oäá­~*„ëváÆ™+ ýÂ(:eT!tÍú iÉkÒDXÒ;-ô ç<Ð$^‘½±¿¯ît¡ëz?øµs6z¿Q¥yÝ14Wr&z×P¡óðÞí”l’‚éo}aÓGMöÁ\™5FQYY¶6á3øŽ‰5·lèÇÛá$îæ4'Þ»‹Ô_ä¹|6šnøñÿ÷ï;n'Ž·¥+Ïl÷Ý^ÌW}ä4ò Z èדúloT›ˆŽ]Û@ur Æùø‘õÞÑPn–5Rÿ§Üî$+ìƒsþÿý/z±úPü›>ŒÇC(%º÷¥¸Â…1³¹ÐY¦p¶AŽ×@¯¡~V%÷z6ìiwÂ\›@x¹ÀÁ± ÷ÕáŸRC:_c ЮC·ŠŒã.ÙšpjO&áÙwHŽpåv®G»ËVºL2?†”ÞkDÓs§s$ÛA¸³ ,GÏesö¸€Æ“{\Ð&„f ºÏ?…c]í2!6²KjSy6RJÔû’-جYAì…“`YVTÏBÁö›è~F‡ µ”a}…ý(«ÍŽšÙÒeóYÅÇxWÏ|Äÿw/$¦;½`Þå<¸a4tÞÆHï=è7k8޲ôpÍx¦ ³-zlŸÁ½Ìž&”‰ÓÛñìÊ¡û0v¥xÜM±Ê=8Óº“©]NKK…ØÓó½`å D5"ÓØ¢3wPÎÎçóbu[§°ì‚ÔBëäš…ÓFû<²Õèh Üë>¬ós@§C ÷îêÿôÏéoz8¦øI²6`Ñg†Hó gTÜa§Šà–êH2§o³½AÓã N¤4ñÃ/ðó¾æsœ!,ð!nZtr3M¨»û"®úµm[Ÿ‹7ÑÅ3Š¿¸eÅaÕEܲ|€Ür~!ûðÝj&Cã`?„üfkŽ>ä\n6¢gèŒÃ«ÜðIJõNºÏ%I(”0Ö-ÜÏ Ž;ÃÞ¢MŠ^Êјg»±À\„½ùs–¼<—DÐoøÛH‚®/)ëŽÃ±qQ)w^çòÎ|†ö¼;hxÍÕ6™³›ŸÂýÅðØÃ|ÿ’ÒRqºI=~Ї3ÿ"aWÈkXm©@k¦–ëI§`™s\±—–ôޱñLã¹ -ÂÚwÚÐc’&ðË2›ÎÍ_µ¡‡azB{sY†þ×KíØ+B-ìȵŸ"°sÑ5/õqoIÔºr.c¾ Ž,`¯¨Ü–!.Bª´Ò~2+Úº†ºŽû‡'0Þù\ºb~,k‰îk¹[@hî¹4vâr%&©3ìn÷ÔiØW!ºÛ<“ç×W)8ÖX’>Ûï@ãª&Âìmìs…­Ì\O]6ÅWâf4Sm" 8gÊ–,Ü=’ÿl¯qžâ-'a¼´"»Ü÷ÃîÒ˜ãžè9ÑfÅa¿É8v©ô:‰¸ž ³o8±ã«j9çP=œ!x¿ÿXº¡“è¨óþ(!¦ƒ5ØûŸN.¾°úm5±ÑɆŠàúM¢x·àSáϬ–c±Î‚Äüú\x<þ¬¸HðFHÊúgÀQ­d¬>¿"õ' sßç£~í­ÇCÅlg¶×â浫¢fa4òm/[‹qºEÜ&[ü£ûê4Îñ6,¶a".ràmò€›ë?xþ»áÂc®f”#iúAÉstß?Òÿ?iîu÷É`M,æýÅ bÑ=í éÆ_8sn·¸fyÐìTéç?¸z£÷¶ü ˜ÎÔ€êÂ{°x´Ëñ÷  w!aÃPòDÕq<›ÆÀÅQÔzãÙ Š«ͬYèCx$’¹/¸w1µ0ùx'7úç,ºN6ýÒÐ'&‚4^‚Ÿ©'·cÖ|)ÁÖ ¼TÖŸrwÖÔ€·R!lNŠf‚?…©|Ýo#Eü2¹¢‹ehtLÊç¾$±i›þi¬´›Æª/ÄŠÉㆅøaàRdhïç_õ š“ršŒMë èia¼Ñ¸ ËÆ1nÈÐäð&2§š=ܼ…?pW{=¼èø÷·–ßL+¬êÃ7Ý"_wÖÁÓ]HSKÄø63q&=¯æ ß5NÀÕÏj(×™Œ.çêVüþWžLûÜE°o«_¥Å–ŸuÂÕ~üÂùóÙìmçÁ÷ª#‘‚¬g̲xa|̟Ȧ}AþêoÄ{1ÛàŠO ·Ó 9®½`[z ¬BO’;Êà”KºM,±é%˺†c.CÇEðfêmNöP5ö©„«ž']<–€‘Õ`-Bÿ;ÿ:AŠž(‹Aƒéf?É.•M7-qã¶dÀ§ ¶Ø`t ‚ œà^e¿¼]}ãÂ.qª]„}IK¤éÎkÙ½¥zì_ë\ÖiAm2È8„‡ Iòv6Ô7 wxZ`Ms0»tɇÝܼªö4 óí ^ÄC,ºöòÔ¿ mý’8å7ëø›Žw¿—Áµ…(¦<†­wªÆüŒLu‡ {*Œ;ÂʹÕó[üwFǵ“Øö˜b,©‹fO'üBϽ‘`”w—•O ü×›`Þ´Ê›§Nkï @ þ8Òù c$ÿÌÝÐæe&\åÿwmBÄmY/ꥭ7.Q½cþltÜað•óe/šD©þJOö2ÇŽnIZtÁz`!2e-yîôµ ò¡u>ý=ÌëöMäBóû™<ƒ)rjLè[*]pE„ÖŸ|¯š‘ê”Ý€Ž(£”nÓd*ï7s¾/g°´Í1 Êöo4†Ì/`å"z/å2‰}žLë—²ÔŠhº~Ó+nIî]þÎÁR¾ö‡L;nC™×<"0þ:Ú.Ê"âÓY¼B3µvù?œÞ¼’þ8"‰Ï&dz‚¤Wðrô<Ú ‡9—¢GêŸgåyÎæ¶Dì®á  “ŠtiyS4›"¹"½˜ª>“~W_?bÿ È_n×La|Qq\T»E2‡õáµðq4Ýé–Ÿ9+~%ÓþKWpUÐ;2ëþì<ŠëÑg@-/jbû9;P“±e“i#±#ïb“ÎMN1JoÏÞ Â. ð_ò‘õêå çE§<á\9·wÙrpp ¼ý- ;ß‹1“¾Ìæ¯dMÌÀ¿SŽÀÝŸG¸¿Â]¨]{£XöÌ2Xr'ŒRNà×¾rE8ÊÎúÓÖu/Ñ²Æ Ú›4˜ag=OTa^ ÆÃ¦-“Ù«ÃNì_J8ôš±­+Ê0gÀ‚)˃rs¹#ûƲãŸp«Îû][ø~O̓}+,ÙΉ{™Ðg!ôÄôèû Züý*ŒÙfËNjçÕiïM-$ñoÀSÌdá•å¼)‰] etæežÅM÷ö‚¼þØu¾öú%ã¹ «ó›4‚¿"rƒÁÆÀÑ"ÙŸýÙãGw¸/ZÎ¡ŽƒÜwYÊÖé=!/fÖBüÂÛ`;}8Çwœõƒ‹ÐôÊh\¸€n[ŸŠq›rØÐ¬"Ö;6®$lç¯Dv ~-.ÔÒg-ïæâíÀNÞØC_Ñrƒ<);Çç‚zš¹ã½vÌþq+|}pV?ålðÔ̦3uè?X„vÜ>ze¾9«ŸKó+òAç§#þ’`G»_ѱ†ìÐÊb¶Ão8—K6“¾+/蹄)Ìü¸.«u<ÆN²ñôœ?G”I9>r£¿OøÑ¶GxîÚ?ôöH¡ŠcGԿѪGx’[ÐÇE˜9¥ú“0_ *gjHÚw=æm:w³›`ܹ1°õÛnƒúu2e´%SˆËÁõ:—ÐSy3æ~Y‚.+3Q>¢Õ6¤ÐY—ÂðpÄP ? Î%ç xž,k«ËÖ¯ÊjLÅÁ0u-ê½ïƒO+}9»[¡ù{$³Ù‹º­‹…¢-ì¹%—fšc½éGÒþÇ”Ól±]´Àk^¦Ìk¼;®‘ç¬Ûv„ļÏoµÍv >ƒ#ænŒg´–þÜɼnÐO¥˜jÞrRâvuNâÂÇ9`dæA”»bÁ~ε¦ïKŽØ?ï¾>ñvŠ#­Q3¨óë~®ç¦ÓÉ+åJà­ÄTK“" L{D…™˜âné)-Zo(È@”°m÷àñXz˯Ýå̺k.{w ª7ª ÔÈÜ o¡H¢fÑA8ß`Á{‘#JcÂ$èÀý±Ð^¬ÄÜä=©¶ ¡Kgfw³5›ËÓ{^,÷âfvõv3+›Ò-û†ðõÖcè¯3Ro¨³ÕtÈŒþ U èîôöô.ºƒ<¿²¿š¦Àå &ÿUŒ~P¡”äðžà0_¶œòΣ1¦*ôîk ò]gK3þÞ´«¸O›µÐ5k9X½ ÑøD%î¿R£mñ׸vÍiÔ;Y˜öç¦Ã‚Ø/Ü× ¬™³¿HeƒÀÖxµ"Žx5š£yý0¾’LVkÛ"wo.ƒGRsY¨Îdò~T6\zÚÞ¡ð<¶‘kÍ+…÷¨ÀrÅ­99Ÿ÷08±†?§àî= o´äÔÄ0¥ùpæÑ$6$,Ê~&æ|ÿë•VÉù¼¨ç’VIÃ_Ý1ðÞ|3]-3Nrb «I3÷,£yEÊÔdƒ½n-Òücºn²¸Î}27û¿¾ç¡'þô¾7^9êI_xƱ?£[âG†¼GA…‹8ü ¾>ЬKT•žV†¹ÆqpºïþhEŸ©WAñëód¡„õ½ß':N°ìï^ðæ°6s 4AŸ湺‡ˆ)žÁÖ®:þŒD{Á-p¶ŸCEÈ*|w ¬¢^i¦.4|½_dD†ñÍTYº3`*úÍ`Ëýæò—ý²gJ*¹UY,ê¶íHýw¬êÏ[áûf ¶ÝØÉà4ÛÆSZZ—?gà¦'{xv¡nz4l}õ ýyÉœéLeÆ?:†–çT`Ç…,ÚmtŠ2Ò¬Ê!1 ³áN¬ Öξ‚_W„0›/,–“B-y Œ{D²±yÿÜ<ý^Š…$wp=˜˜z'°C&ô] ôŽ m±]Š/'õAAˆsÙeÌé}ÌG¾Y0,|g…«#YyÀ61ünüv‹ÒÇ`É3€Þê—<ÛI¶{‘ž[£B7qßç ÜŠ`•iO ëÖa%¯{ÿ–윊¥1.¸ÜÃgèÓꆃ¸ëz*m=‡ÿÍc^¿›Ï»°›†ìïç´÷ma]©1ÐT‰mÏS¢C«˜OÀe¬¶¯@ùR-x<ÿ(ª]ß ~ÚjÌøÅ‹a§@›}˜Üúl«Sz­æÓô?Þ;9²ý;åéì1`*ò’ZQ$§7¹S|O=){»poík¼W“Ì%ÆGËÙ*é¿`¹B/K–mÇ=8…ã‡1:¿ ?¿ç¼·Bá7-–u‘¿¬Ê•µ-W£MÏÃõ×íð~{W‹cÌ<¹à.Î7qbƒŸFìu[B«Ú°áT5ì^\¡Ïpj´8ÜÌrÁÕãÈŒ¸Ëyl—™:0`——{„Q øHuÊ2‹êƒäRŠ5k4½¦3'ânÓ7d§•Y t/šò@LnlÚjŠ¿ÅS]d ê”vãŸÇgÉÕH!š8NzwòWa;…¿À¥ÒVܾ­Ò½Ï {1ê¶­Ç)¯Ýò_ ¿‹Mðñ%îgDpý[!Xšðö*íE2ðôÿæBWí „Ù Ç1­ÀuÊÒà⾑ü—±y…ö1¸=ïM&¼K›kHÿüó`‘ý$¯<ÃC«r1yû)²zÉt4_ó‘ó›8ŸuEn'»:R¡›^…_=¶˜ŸŽ¢ù0þÀ)8ñÕ™í®“dŽC3ñàù¹<òî#nó· ¡—¨.•§O¥ß¢ñÐ-nõ`}qÓT¢¼Á®¶i§!×â!$E?Cé-š°:û-N R¦oÆqÜú#?PlWÇÿa‰‡‰Üx%ˆŠéEq/f;œ{‚‚ÜÖ¼xúõZá.jRÝÖv’õКz¦q÷Ÿ'r7?¸fm67q¹;|·œï"'ƒ²Ãú, u‡ùû¼ç¡¢ðC¤´¥nkíô%ónÇ¡PD:¬á|¬rAý¾æ©ñ”‚/ãßž´Ö‚/…¶ÜŽ'ex¢D .†7Kòq×ß6¸ÿ>Ï­øLœ«"|ŠçbA8±Ò£°ò¯®˜ E;L0?²%;Ž´¤öŸ&ñÓ·àn—wœ˜á6Øwð<÷Bó4™’·j(ñTŒÔ¿5ZP´×‹÷hÛK<ñï»Ë—ÒN0ʪré œ@·¸ÝG/-¨R2;lCx÷Qˆþò,…b0«y5Û\ Aƒ©tç@/? ¦^¤!gR‰o{6±°*x/µŽ+ù´…†™³ÂYëñmA2·À8 ˧÷òB*f@¥\"êÄM¥qSÇcÔC[¼-´Æ¥¢e–4 >w5˜ øˆpI-8ƒ9µP?®£«õqOõü5}:,˜zì'Â÷cŽô`‡=Ô ‹þ®¡[v ó· Tsà d}ÿÈ=ê-Äg¾ó¸}Ë÷ h[lv¾<’ÿÑÐÕr‘WÕšBæà®«@í4#¨²f4꺋ÑÞ”dê*&I+&Ý!Q5ëhبºÜá$Ôs¡ ºAIDÓç,â<Ê‚Œ“=Øwœ\¬ÝͼgȽ«Š¥Sñÿ0†ÿ´¢Œä`î¯\⦔‹"5Ï ëýT\2çÉY|ˆÛy^G‡ÒÛc骬5˜GW°¼¥1P3¶”HþyŠ"énLhãÎè Ð1 Ï£¶£+«æ!žÜ9»Hx`+f  g÷~0?Ãóðgšawl ¾\Œ×ÆÒ‚ypÂMpÛß9’ÿ¿¥þò·5zÀzÓ`Ԭʇ=Ÿ þ›ï nqpàs3gý¾•ÿ+W´Íâ²ÒXõ™BÔ¯t§k;ƒÞ|sýö"¾-{ÎI/ÏÞ™j³ãÃñ$Q†[¿-Æ)ÓÊI[ˆ¼™'/à!#VeýßoÇŠ¸FbyfIÉðV^JÁ ×jØ~J…%ϪV`õ²D<¿éyØÖLv/@óø]pS}!ît)]þ-9I^-¡Ïü³É®èa}s2 Kvᦠ@ Í‰*Œÿƒ²W0éO<;È“R¾ÅåÎi„DZ} ¬{ìë˘òã?ôü>J®ÊÇ®ùÜþô[x{¢É*¿€aUYîþXNk­EËöøDPQ²c>5°0ý0ÌÕ_ OÈgˆ9kì¬DXžÆkÕµ 5zãþïßT‡º²ë±.<îØ,ü¯™7CfÒ¸Bfýô |:x õ%1×䨋ª§¸KБ÷6õLÞ ^ËDœÒƒ¿«BXs•-+Ý+ÿ$†¹óÁ .0强£Oo?³$SÄôYHÍW”/s`÷6î„!¸èC<Þ¿-Ž¯Ò¦p•?åÙÜÎXª1ŠÞ‰“coUW1‹þ.u¢é&ûà]ž3ïÙuAZ³±×7;ñé|%k¬BÁ)vDÿ],X‹—´ÄÐÊR“ZÔñ”ÉX樅2s«!óLšò™“4KÀ/ç?£fÈ%<Óuþ¿~f^Кý§ƒ?~ÿ „,Þì²}HŠýˆK Ä‘lUÌúþMpkµnÈçxn©céÊþéÈe¨ hZ ”N#—’8rÍË„S3¿»þ™0ù«qüñçbáÂÕ °u˜åVÉÂNÍ;¸7v;9ýÍ“_\ Il2\à§°Ëø7½ÌÂ}­~¸éõ*øÓtÚŠf‘È¢T©ö6ÿ°†è}é-SLIéò­h+Âì;ñï·­°n¡nlY>‚ùæÈ¿ÿÚåŸk ¨ÏRazÿþÂǨ²äáX õœCïÛ@÷½h¼,EåébÅuhßcÙ<ÖrS…¹žý…™ŠYŸíhæå*Á¦¸ý­Æ0væž•±™8w2Œ>ÈÃWu(ß!Aí¼Ì9aþ}žk÷mT¨I~Hp–GsÅu¦tu¢!ïðËÒ§w .m…‚°õàðû>>º÷·A6Ü+CÐÇz¿ëHVß-›déM ܼzß¾¼ {¶ÎbWã‚à”æ\®¸é”¯‡üéÃ|µJ˜zœêƒ YXôqÙ¬:jÿØš,ØpqyD²ApÝa(:u{i±5ÜL&òþ 1|ZcwóuHòNzfbŸX±èÆŽ ¹dÉ'}t‹UÇ¡ÃlmR É;c¥¢0ôz5r¥þ¾ §™i³±Ÿ;‰¦¸îôyC£•ilÜ|ßO‡×åG Äpá¾~تÊ~ÙCû<)”ŸíFíšÉu‹näþ—z³â€¸ò$jÏñ†ï Ê4¹‹þ@Ki)ŠV°ó.ò,óê>¶žííYðãôS.Zàoüò‡Pá%È^Œ¡5_W’ü9)˜xèçVh(ÞÚü1q(šbÖR™Ð÷%‹T³Eó€1˜sú,Š+çìÎå€%–~„{n܃Ÿêlú¬Ó`tî&ÿý‚[°›]Øñ‡‹ÙwŒøxmAC¥Úÿ›ù\~»ïÏ #b0#ŸðÉíÌg\Ý &;WžK,m@+Ÿ 2•Ã[}ñPõõ±š«[>­ÃkSïrÿ¼ò°çw=òeá<ýœR0yë+Qçb*j&n‹Äé‘§£ð¹ßw2µPîE@Á=rÿŸü¨Z…TRå&)ˆdCèUs öwKÓx%q¶hÄ? IE?Ëj(\ÌÈê¦8ûBÙm#ËÎÄÝDÃ]öÓí1j']wqœJáÏá#XJÅ鹿j-M¹ú4aü ÔõlÃ?r­Ð6w4,Ò¿¤5Ï%<átîè3éW×ðétQúQƒ°Ï3òÀC°üo]À13( ÚÔíg<þìvsY*kŒž­ÓYnMíz¤Ì:î±æ¯×Ð$ñ6n-â X[Š?¦7Цx â†-.šS4àßùÊq–ö¨ùì+Ä&œºc,åϬ„›íÖÔòw8èë%¾ówâ΃³°>…ÂÉ›0râxLœ24ÿ2jr2KÕ¹1I»_¢ÎHÓÓ[Ïó6GqÓÖcr{þ˜÷Z=s‰°Ë6–ŸßBt?¢Êæ ‘…ëàˆÔÜ;w9†ß™ÇÒ>bLÿ*fumSÂØØ×å¨uâAË;V[ÖÆ‚ÇÔ÷`¦†<[±:ˆù(!Ǥ¢Ù×küÛnª¨oÕíi%PvA…LºN®/¡»~㺧êlì—•8MaDîXï_'úMBìq] íú&Êø FìêË=0v‡.]{`7§Óhß)Yêªe‹:ÏEèØ{Áeór¸}J‡žkÏnÍøßùßê${îg+EªŸŒ¦“%qÌ€Þg™œÈ‚X’æO!3–Á­0ÍL˜·©°e´ zLb»O¤Àç±KÀxÓ'h±"~"'¹ûÎðxƒÚñ‡RñgS&˜ÏZŠWM㮇ÀïGFs',߀ÜòDˆLÉÁ©ýP¯éÎmúSÃýêðfó‚*2Ì¡!ˇy{ó,ÌÌ…ö µ\mÕ þ1¶áÔÏú’uµRLôr,û¶c,Ë=ÂÐeì"bBŒ™Íº+ø#ø_ûß8z]]œ>/ÙJó5î¡Ó¢2¨]q¯þÉc¿o+®ƒ—žUÁß[Ï Xd5F×»²k®“iDà ˜aÓ‹KÏ-Æ™“èó+A®Oí‘>ŠÒZQ Á—©Lƒžä=Ï4â¡ —¶“$´ì=ñƒèœÕï[<ñú·IÐð^"”\iÍŸ=Àßö~ sìЭJÔ^h+Y=M‰ô©¨—[lé™#”¬¯À”X6¬û“ÇÑ¿k½Ð%­òà”OÒ´¢lóXnÆ„%šà¡†ë2–b­¢/ø²Ñ!4æƒ2Ì|„|[k¦ÔÔæsùøÜr~+º†vq„e\¿²´‘øçÍ»Å]œ½ûßôÿ×K *G‚ðþnæ;ø&?»HÞê®%¯ÌòYâýp:dÀvY1ëkǨ»Al¯žÇFyœEtŒÒÞË8­Ø‹%®§å)JÜT±A\ê¨ÄÒÎòz÷‰²ÈçÆô²×©¾D^)ƒO‹dØõ«vp·ýxY )¹¼ÿæN·¼¹•L’jÑùÚ8vš F¨£)?ãÕÎnTk©"«Þ9a˵9`>”‚ó?Ò g°”B?µ×ÁgÍZxžÝŽ5¥Q´ü[¯kö^ÚÛPLö¸‰Ãq.ž Ï^¦£¢5ÀHüËÖíÆ²ŒB,Øs„^‰Ka ¾¦záEÔa>ý ZÄü:} óVœÙ4¶®|²/á`Ùôá×·¸Ëò×aÊ‘s4To[•¾•ü÷'?ÀZ»¹øeéL&9õ¶ÊÖâßxxv1NñP Iùøz4V›1Å͇‘ì=ÉãŽfŸ»Øs„–m÷c6(þ|9Ý·m"͸Ÿfí~A”žÿÂq«spg½ öf~îÙn±‚‘3èÒ\ÂRC„ØîùÔNjU¾ÿ.¶}Sg67c NKHg:˜ÕÚR8l½‘Šžögz'¥Gâÿãxïv 3„EYö)9¼µù8Æ´&0µ4`-¯ãpBÚ#xú•wN7ó#~ŒgÏwì ñ·ôØÉçû¹˜=:¸¡¿E¶$ËÎpï–Îã—¹ÛP™¡ßhÝ?BÍáÔÝJ0•¼ID‹caþä",†øÛÞL¯ñ0Æè ç“)Å®þæ.=ŠÄþˆãp²û'Ü÷Næ_qv¼€[+ûáºÖ.ÛÃ;öà™Ú{еB*V“t’ ÝÏû4{;/K$“œb°¸u.wTh Š=M&¾S%Xßú¸û™$;x+•³¸.ŠÏ¯ÿoþѶM¡ ˜MB:5é¹&g¸ªœš”JO,”`^sbÉCõtòßå=‹¦òL­ ”A6 |Rå›ÝÇI3À:DŽ:XÕáÏ=x2 æ|jÅ£×M9‡æNŒ–†u“Ÿ‚¼C$y–Aö©\dŽ>Dt”á§9RtõÚ*ðÿUÕ4Çæ nrÓdƒò¸ãçøEù°{“õ}Àݾ>Ž9F`£|5÷ǯÁeÒœñœC¥ßMnoU–0Ъ4;V“v¤kD2ˆ·Ö|hœ›@ˆa‚3Ét>r,Þ5mC)Ù+ÉæììÙÿ9¯æ&¿"œø¯$¬[¢CÎ/•Ô㧸p?Õž€ûù~"RA+FÿÆ©iø{7úW7¤#¨{·.o”gãÂoÃDuV%2›­RC» bôá†Äe-@æÃ~TcÍŸkÕ §¥™…Ä|¸!}“\ÍffÏ31ä°5x8 ·7sr%VPr¨ ¿ˆÎ–fvë€.ksgŸ†¨Ãé#LÖ]ùUß!}ÂAܸ-Öäô’¾Í[Á ÂšÙì¢oÖtá™Ñ[1^P„Iê…ÀÔR Fý’©±Ø~˜0.m¸þ$à‡ÎnâsZ®1•ý[žŽ+w¡7…²¥K¯Á4\Œ¿–¯§ö§»Á\a+¯du[8ö ”Ž2áÊFe¡Í BV5ˆmøŠW? ×:Cuè‹ÐÆ;Ì\¹pòíÛ9¸Ûð¥eÌè¨àãx>½.›F!Ûñ™,].cF1!OôÚi?RÿªËδÜt‘¢P(ǬWËÒ1¬ó…4Ï*úo†3Ç>”²ÚL3ºC¶œFì6×´u¬Mã<|™9>%©Ò±Ž…p}Ë{ïÖRÙs &nñ N æt“ÃG˜¡ Ôh¡.õÿ%Áê+ÿámÕp˜F{â3±GðÅfwkõ5X|í<ºÝSf £æ°€f)&v$dBT9ÉA—Ž;zq¹Òsv¥1Ï wPÙw™Xþ„'kMqèøLê¢)s|Nbɦ͸À,ƒ\#[ÉŠùáÐøË€fu­ÅÔØÐyrœ8­Â~©èÃͼüÿµdÁ¾ö:<&°ˆõ)<æ8]tòj)[-H§œËÐÒecË gêpOä÷b¹R q” é¢±ƒÇ‹›QL¾®.â¢äû¡ª´ c¤jð*ès¦!ËO檵éŒòA<©–@´ÖŠPÑÜs0~]9Œ.¸‚;b8³F4Z1HÔ›¡‡Ó¢ÎW;qU°uÿÞÞ¿Š8Ua¶;ó"¯Ö¤…ë\ü•ï-åÄ5§_ç΀„˜6¿ðœŒÚ®} ‹Q¸·®Ajì|k<|ó ñï;ü¼²-ØÛ-BØW%p.^<‚ÿ͇õ³:9»©ÂlÝü ð£ý<Ú$M¡•"7HÐFñj’︆zåñ”5,ã8O1öF¦/Û'Å®L}‰Ù7/…ýhÎ*ræÀL- ¶Ú*2Þn€£‘çÐ’^áüe“Ù"Þ ™D±ÅGc¨&ybÎÀæóS¨Ž/² ‹yDÝì¾Ë2ó¼¸N(|j5e!*Ò`$N>K¬Ã«^Ÿ(m(É8ß‚ã^ÃÍ¿3àòiØKæ²@5Uª1¤Ä–)cÍûmÞ Y¿á&C|}üöž|€çõ–P§MIlñ ÿý»Üv–=¬…gYZìþ"1ðqÂ1[ôÐuJ,R!…[îp’îƒNò~m¸a¹&5hŽd•ÏüáÑí ñè)Â"ßÞà²õbÐVc9̽& –I¸ˆÒm„V+ã{ï_0]´ßòþ¢ÅòHá7ƒn»·w½É$K—^@¾ÚNì¬âå<߆Aåo9øJäôý¬7 ó~4[¹¹ 繕óagº;åØóKÃuõ¥ ®#Uøòl6çß°ÁP•yÎÀQ‡öÃõ±Ü“õ“èžAûc8öX0¸¼q¦ÍÁðõª_ \’0âÿé¿«¡uì!î#?Z/á”EN²¶Β碓ØëOS°Jªö ±§â•4Á[‘Í ?É"™š·Ø˜Õú©r‡²O‚à¶HZ«úÛJßÁ>»ÄH; /\»¾agñ¬gÞÚº›MÓ?‹¶ªã¨VH6³»Žõ–»°™ÞÇXäS},¾Ÿ -æJ 8v ¨ðeJ»YäçD,öÓc« \×-xB*‚g°·âì¼L1µ¹x×þ§\´]_»ÿ†Åô™Þö6@„½Û¶¥ž¤é±ãÌâû(×£Æ!0j#jûËPg¤þw‡ˆ ëåHî¿~euñ öù­0} âÊ®Ç}ƒoÑn4ÿØYhw-áÌÖ–àZ.È6IzS¿ë,ò`!~^fMAеÙ­o™Ô÷€tÜÚ«Œü‡§×:à‘@‹”‚bŽ2c›*0~u Ó_¢!Nc™™xŒ{Ãþ¤ãîæNôl1ŸL§Ó +ú²ÿf=“/‰˜W´f¨²äÕÊl­Û &OMe¡MÉ4õº{×½<ø{Vsqs‘Ÿüm ¸QF¼9ùN la,ï8òl³ùbQ Fð™Ë nöM–9à ~#ìÍ}[`ç·²b[Mõ* j}À(¹]¯>âneËOå;OJ£Oþ&“ö†tÖ×£á®ïYœös=þ4sÂ=“_bz©u48ö_u¨—Ĩ»Ü†^KÛð¸­%¿lƒ%û¶Ñ”Ýzr€žìáñí˜\úƒÔ|øC‚®îÞÌm+>7ú`ï>öâV5ü~x‚8ì}G¥ÿøÑsñÁ´sÕÒ9§Ýëcšwl’p•7z¶D0ïvÏÿÍ¿ŒÑ‡‚'˜s7‚¬d臻©—¼&=¾y/lÏú/ÚSXýÏ×(ÀEÀd¶²°®øQ9Õ8zbŒ9|3È–tˆµî+M¡â¯ÙW}¿U-Úͤ.ŽÇúI{aÊ#G\vfÞo k5äèû‰S [å3÷RÉ’^b…Û¦0ª¤È‚¾.G)³*ªhùKï£öÄ–œÁF'н)cÙË pú]. ÈÒ£~\Ú Åöñ ÏbÖ€øë<³ûo8ñèÅÂz†N"L³Ó›ûÔÆBÖ–CoW(ÉïäZóœð›éÿ~ÿ¼ë4ýtd6—–«A„L†àºSî›Æ4¢uI•)ªaÅÀE97öf©.n±MGËu¸f`,ñî<§Ø ¥Â{¸”] ·™"O¥Ä´@ùó~fZ× B‹1¦¶«åÿ1õæq5}ßÿx£æ‘hÒ,4§éîµK‰”9 *S’yHs¡¤¤„¢”&J‰¨»×6”!¡Ì2Ï!áÛëóxüzÿþèñèqîÝûœçY{=×óyîºû Ì6åEÑŽ¼Â"”uQ ÚZ°bûØþʈ¦ñûز¼4p.¯l@3£NÔ³Jÿ”…¦å‚ùi¸_eø¹6Hí¿ª…;ÿÜD§)P÷Ôexwv`ÿÇßQÄÝQÛÞ…Sxøù ôZKWžZJe­>‘9*ã‚ù„þœò];ŠW¶£Áqí°üŒ³ÚFðµ§5¸OÙ0ª¶L‚4}¯ƒùw÷£vøúlv>ñlËÇg »¸fâœJ;+u¾¢…¼,ÝÖÏ}óÓÄA-têYŠÝÓÏaåK_þñÈz"Ò[A|¾|!GVçš{1£?é+IuÈ#ᇜ÷°ãô*.?–Ö<èZE‘Í÷a˸œªñ†ž]¨O”×ú`©Ý>*1«‡7bç71¸>7‰gŽ¡©Û¬xã»QtÇá@ý߃WK퉹Qƒ[o†½¸*ݪ ¡Ñ:ס'¬ZÇ+>ôà'«˜}ë4û˜¡#J‡ÚÅñ¥/©ëŠUhõñ'ÔÏù&X**ǵŠeéÒñW° ·ËêQä¶HéeÙÔ-B é†n-*¶ãNh% W½G GDã¼R °¦aÚ±Ëâ%Aw/Â%NCðJÆXŒÝ6Šõ.}Ó2õ`–÷[ët{—.bk+Qw¸9ït•ÄËâÈ8Mø0ߟ‡…µ@ÿõâ6ƒ†¶O°Ôÿ¦]+b3׫òÁ¯v¢åšÔ }8€¿gš ­ƒi²ü›| –O4£Ó^£Š{>³ñøÀŽZëó•µ31uÍ HŠ…£!ÔP6‹ü›\Ì«'$“í7aW•& ß3”*õøòqÖ’C‘©‚×yÇÙˆ› ð| Ü¿,VTÃqœdSÎjý[A<ùžÜþÜWûЧ›–Àâï1µ&—Ù:l¦ÎFKQÞRƒ‰B0èi¹Rüã ¶…½Ctû}Ú+¬Ü:y´~«ÃÄCX»æ-8¨>½¿*ý{%¸Œ …ìß© ·é§ =Z3å{±Dɵbª½lz~øßþ_µ«{ÈÈÇéýÐÞsÔ„ôµ§éÅòq|]ÍO¶\<ì7ÏÂuÚâVÅAQ¡­qôÀG-éÂq»ÈUA_"2sÓ¸Ê߬ûý¼Bvñ_y—ðÇ´ëßëos~Ù#›ùüqkiÏÊð"+…fμˆWÁ„Ý›ˆ—•)„ªÓW‰¼n¼2s0€^kó¡?½Ü¹ÈP:õ–9Ùý3“ £ÎÑ*@fa>Óm¹KÇ.Σó˜mwÈ %'ß($ŠÑÁ4Bv ‹´ã”‡è®‚SX‹+¹ï*w‰á¦¤úg_¢o4 迯†®dÄWÌÚ3Š[×)ƒs‰ Hæ˜ò†Q/Ñã’ÈÉ‹#?Nî÷’ê’òÂäm‰Ø›Ñ”bÀÄù|ýæL\rK§ŽáóL÷ð”Þ½¼:ˆ¥mSä¿´\è 3^·e9^ZÛö .ǯY×@ÿ»%û­eJknÈ»»…‹ògŠÀéc jµÇÔßgáµ~×êeg©Òæuúáª5(Id‚Wé |¹qú@ü—v\&«¦(ã{Oe\2þ ÌÍ9Üu~( ù0RÚœ xˆKw å¿%‚QÂmá©‚x|öé5F٨ÃѸ5µÕWŸ7–¸Ø”‹&íA# ÈœÈTšö`š•Íiã¶RÞðöÓ'œ$–@¤¿yðUO·æu^px~¬ÛýMNôµº}i.|}ŸÜÑÇñ¹Ýø$åÁÏû=ãi<®)ÎOŠ8LkÀͱi°ÌPÿªî€Œ &`>+ˆý]˜’:½d›Ç*ÈÓÈž–’-S—‘»çà•({íÿ6øp|ñ –Ý7àÿ¿ ¶àª¾Ó(︃۪EÏØCXº9Ã'<ÀØÕïp—ó6PØ=œ¦dáŽÈ ¾ÙÑ€Þ¸9™Ë=Nœ 5pä¨7Âé§ÂIÖOÑô‡8‘õ;FšîÇSççq‡!”[ÀÈ)çQCmLöý„êÔoÕ$ºåÆ4¼65º\²™b !$­–‡ÔŒ-ðªî<ëHñ„™>x¢WœÛýÕ¢Æiÿ܆ýÛN÷iGâyéÍçåKéõeÃiÜ’tAUc2Š^¶éÏ•¡XÓs“d†õ‰×yGÈ6˜±w§MqU0Mÿ½—ß3ÝÙÅw‚ôo~³èü"°Yt>¦Œ~ ÏåûˆoÂ-ÌKÞ‚yYý^Òp8*ßv¿#·£òÇÝtô©Áô1-RVC߸ôú’ä¸qh.Ïl;ƒSƒ˜ñúíx¥á#¸¾Õå ›æ¢Ó'#þéÞ,\t ¾¯5‡bÓë°§¨ˆ8áV_¼÷Æ©ñ‹©O¯"¨Ëïbü#ùG{Þ‚ †@èYQÌ—?tq“(eûEÁ£O’Üÿ£Ï-Ѹ Ç›Vø’¢¤$l~ð‹yŸZ&pV‹çC|±XšÐæwªðäw‰ù7›žã©Pž¿™ÞQ΃ÁÞ$´¼y`ý³åÚ°Í÷ Ë<ýnùkñ‡ÖqTÿc3+»ZŒMÛèˆg—Á}îzüÉØ‘”s»üñQF<ürØ SýÂp|¶j¾µG!´à&ñžíJå#/ÂØ†£ÐX˜嫌a—ŒÏ’/Xyˆ0‹ ,¨˜uç|Ô§Cß(qËÊÑôíÓH&Þ÷“<Ðë•{ˆðȯûøL`Nç”ïƒ+kBœ™œê=ÐðlBÞR0mcþNÇ<œ›Èö4y¼O…'»‚ð…>Õþ\ ›üÓ¶ðå`›ãAmfÇ ßyt“Ê×lWþ±;E>ÿþ~w^‹öð7•‚ î5l¬Q è§‚¨s…PùÔšžž2ÿ;fÞ˜½“&áÏG¾ìCT=³(•&+w°Qq»0¶k$†™v‘Œê8|ÙeÒÜp¤¡*MˆÎ¬Ýq‚ß‚"“hMêíÁìæPhîOÉů¤±SªZ¸×0L>OE£úO¨“Q êšõøY'Tp&™^‹lQ›± ¬Q¦Eêb´.v2ÛŠ/Ú8ˆ÷ÜÀÇ#Ï€â4)h|6Ò#Õhg¿Þ;¾l^ºx¯™ç33Ý"2Ž‘ëÛýÐr»*žx+Äâa‘õÿߥB¶¡Ñr†'£* HÏã¡CO·Û™yŒŠˆáA¯öe¶5ÐÂ_Ù‹ÓoW/ÀâOsPaÊirÃF‘N=?—Ön»€}gBø¦ÇBÁ¥7ÂÝ_ÎÒ—á¸òŒ/M;ˆÏµ”ùäuOHü Á|í²0ñÀW8±©Ã÷mbÆsŒù}Ëç8ôÔ1&–‡·Å¥ýsà”´ë¤zÅ`þöÕMuæ j¸ñÌ€ p8õ>ÖbA‹ïÂÖˆ[¤¾~*u$Ï¡Äð7+q/†p+ !X7o—‘χºÜê !jänfäsÎN‹«}è¸oðÿ%6ŒC͘t˜õÚ íǦà,Ði½e–ï‰Ål|yý&³ÛgH/ÚóÈOÙæ€\2ȼ•,ØÐÁNGÜgw‚8J–ÛÀÅÓ² ‘è r)õ8þR1>&ƒÙSÉÇéqHfûd8œ ŠÁdá´“.ìzšÇZtƒI…m= œ ¼ »Ûg\{Œf~ íÄ2ºùx ·bA¨Η£úç>6Æé‚`Óû¬-½Ø(ÙW(ߧȃïÀɧ䍿gt2’Á¦½ &fᘳF0^å4nós¢r†bÛ§‘c.1°þÎÒ'Ú£qvì’zèfnÉA3©ï,os9Y¾Ôž+zõ»þ{—¹[æJ/£ƒ÷ãz;M8´Ý•ŠÿÒâOf•A·Œ1ä¼\zŒ†²¨ÿb˜¼2Â=kìøîó¯Ù…£‹Ö dÃ.ºa¢/mÍkB«EûxïÛ—˜¼²‚Dî+Ç[Ïó`k” _¼Ô™àëDKí®ªÉNåa†¶:’\7<{Ô“ÞÍN ™ë¶B–H&L=)Lü˜AL¢CèÎU÷òÏö± °}Á²MéÐ= ç„ë ­2i0Í?áÍ¢âdþ»½(Öˆ5r(ó¹ñC4¯Ô]#N^†­©¨-Þ¯u:_‚ôàG ³—C@{&»lÿ=Ÿ^dÉe9ððŠ/ü|•IýKßî?‘G,¶ÒŠ_³™^¸`!þÞ8:Ýg7¤$04JYšÍ'oãÒUšö`:W€é¡f{TøsF!¾¼éSŸä±{%é¸óŠtÞ›apô=‚|°.Íiö=GU˜Ñ)>2X¾È€g6€ _î€ØãÁú·µ±·[µ¡8}2u3.{?ÁÂî`+ _—L@¡—"Uy°Qÿ [#µT~Uùcè\ì…˜bà.<¯p“ŸœÀÒ>¼Gqþ´% Á§a-{cL¯.~Çtχ½RcÙ1¯åpW÷:.ZÍÿ<‚Õ!ŸA8¿ Ïyœ.Û.L zÅÔ»ƒ¸²wxvkìõ×1âX2±?‡oò§ÑÊ(QTQ†Ió2à¼U,ô•ì„Î-š‚kFÊÄÅn;ô—4Hs;;ᆰäÔMÜîˆ.’˜éQ'¸»,û|x þË_cö_Í ñàxÿ2Ÿ$u½Ö÷t@Cù¸á(OÄ`hÓz;ÓgX– Wt‰Ó-2Ô{ð(’”êEÅB²®Çap];ë ±“&£ùYWí[8?Gï‰>žgÊ^zg¶[.cø¯H¢+÷N p‘¼|Ø‹qWÛ 7>³c¾u_‚Ü8üýÃCšú0P¤öŒÌµãp÷þ6虚Æj»þ´OÉÅý‡á¬8Ú®*Æ<)M:,ÜŽêÉn…oŒXÔM„1º¹ä˳U¬gã2Œÿ,‚Þ%›éé»»øß¦óöéÆ]¦oÙýéÛùÐår¼î€'éJ¹ÃÞººbüè K¾ù—,®þ‚ÏǨ€Lfºt’ZÿÙÒŽðè]ò¯Äoiû‰å\m<<¨³e–àÕDwXüm:owãC€ByF1ZÍI‚ÙÑÇÀcçGüÚv ÆÝÄájà3|-üéý ¢o8±¼bB­ÃßadÔ„bä’ãvf¦Ÿ7ó °£ÛñEK/Û'û”ÿŽv;ôC®4Gðú·ÎÔÇ¡M^]Y­¡ð«µLð)”fáÙ98"X† ~`ÿ³²l€ÿÆ>NšÞÖÁ}ŸH–wa&þÍçÛ'®Ä™–´É² ´¿o¾ý¸]YÂ&5èçß­X«i­(=˜ ¯¾úê>tP³D®Ë‡žøñFÀñƒ¢÷gŒNÐk899ûöÎÁç_|Ø(Ëý,ÿ¥0.Åfi˜ðyZeØ9’?ÊY@]¿Ü€ÂyؘèÈvja¬š/N¶îCµÕœÈŸÜ‹[ÆÒš”øôU+¬íçLû"h;“—¯IaÞ¦x] 3”áÓºƒä¢Fü]ÁÍKÐ1ׇó‹›Àñ÷áõÏk³ Ìë{ÂîÄf‘è{Y0-s'+4/ã‹+ ‡ˆ³û7&²ÚGÁÉC“<ùêÎ — S ·àƒ¤¹´ðI!¸ s·lÅZïG˜³D>žÏÀe™5¿EhêO8‰ìÄ´ºÍôšK)JºjÓÝÖG`×#3øµY‰ŠLÀ–ý»P~¼+ߺ7•”¹ìBÓÉ€¥"ìßÍC`š°ŸÌj[;ýhºÐ÷Ñ騣C>*ÏEƒ «lUV-þú: 7Ë4‡·Œù“›¢øN0bïúiZ±[ñÄq<”\'™ƒ•ù´ÆçÐ]ó kG€‹á@þ/Ü<4j“d…Q<íĈßèH} €>ˆÔMEbaŽÝ!£QÅf=XÁ¯ŽŒÅ¾Í“ ib..ì=‡áÆU‚yW Ð1Öä.fÁÒØ&ôõ¹ˆgŽ5íMßÐãÔUô^,Š+K7Ñj•¼Ã…Pz;[ðýï/‘’a[P=æ+vMšÎe¾hðßOÞ2™qbðØ¢E^§—CÉxë·ÌPƒ?q»C²¨uÇž)® A×eiŒâª¦ä·‚.¬nH¨kLÂg[FcÑjIáÑ0D¯A7qÁ‚\¾³Y“÷¿%fßðGIyâï»l‰…ô¼44™ƒøŽÙ èH€¢ñcôH®¿Q…ŠÅ˜cé°ÿ§6l?qÂÿÌE³<л,‰ï_Y÷ùÐZ/èž#IfùÁ—c^¸cçÝt°¸†™ÓæÏ$×.Ö&¹“ËvÚìØ>S:qímx\Š¢§%¸ãbIúfø_v¿J‚'µ€'öêôšbó?ÈE~^Ä3ö¢ÑEüûAYèÚçÉë‡î¹ÛêxB»]øe÷6&Ó:œŒ|}.¯»G|§Ú¡Û¤ò»Ùì%i°sx*¦íÔÀè×­@æ74 ðÿÅ6­QÒ°(NxùI‡¢-D|V|û.”§OÈëÒ=Û‘-S$xTéŒ ¿ óÜèEg¾eo1†9és·–"|% ™wO€ÅÙ,”‰†3DÀ«<tµ^ãûmnøýñ}ŒõûádŨDbð€Ý…ÇdÔƒuz)•›ÄcÒ̊ɆÖ÷R4i¬%o¾vã›1+ý½ðîûdzc})y¤Ä?ªð²2ô›ž?ˆ†•aa±UßÚÜàaó™üé@¡‘–ÔfV–"|°ÒCæe_Ë–­ÂO¯4,ØçC?k© Ä?áÅ2<:eiNïYq©)=8k9’îúpú&Þ–®ôÇA§ÎBä†" “,QôÆ7wbÍ•ZÁëº?·½‡ïåâ³ãg@·ŽÁƒ{mDRæùžçK÷5Ã\:gÇÒ™Í5:±WͰ-¦T;i‰Ý~>áz(ŸúvßdÖCæ?Ûf«“{ߌè{‰·xâÆZ˜Ä~B¸ÉB´ô‘ yTŠ›¤nåkËeùô{MÜ|æ1øú%•–íATÐÆ1{&B«Ù*úH/ L\ã0ò@ƒ°­kœ›ð‰7Õ©Rí¯"¼YÛ\›ð÷nÏ”ÖKÀBe;>ÑEã§mq[ejÂ5ãÚñL÷ bõMœ—YÓ é·Ø{¿üÕ ¶‡w‚¹@‘O0sà›'w’)ÅSP'ª¯»¹Q17…¯uncqÜ⛇ƒ3EèŽfl¸CVvîåÎd'eC}¢sÿü ‚Oõ­d¸õ3Tø‡!vp)oÚEúØÕaºðÁA’^‰øŽ{—»¢q’:.²%þQ LÿðX>×ñ[4ü-þÙ2„Ï—<Ânvçã¼À…pãv®ž/F=WkÑ€7Ò虂6Ñ&Tͧ çmžà^éŸð]®Níöý'ó´°@%ƒVò`˜/¹öÔ”:6í`cJ¶± ¶{Ï™ ø¡zQºü‡9/ë^À3;ѲΉHz¢ª]3ª\A'ìÉÏc]„‹cê’)°ºi4Í|1„o¹¬†î#´Áü€íÜ?žÇEkÑY“*~l'5û*‰»û0ì5¤®ïÅè$Å2ìnßGk‚}x‚C4çåªLÚõ˜8¨ó<Ù—ý÷¸¶¿;‰ù#¿ÂOÿT81Åê½&!wjaVõR:ë¸Ý2ª­ì}Xßµ±¨6"ˆÎ$Bd#O $Nõâø 4bÔe< ³¬?‚v„špñ¥hiÓBRÔ³IjJ#ZDý^½‘<þ<Žšeíw nñn<]](‡Û®:‚R­=].Á/#ƒ+ÕùÜ༕ú9®_œ&û(u.6eKäqòWe’½îÅã!¾7’Wz-„! Â_'¡÷Ý]xò›.<FÃç&PoŠâ[ |À/è#x+߇Aºó˜ÞÈ?n!ŸÐ!Ç_ÚŽúúøgÏÈ?˜fîCYdž+xsðxâÒ ªÿdø¨âø˜¬ Û'¬Â…›ËõÀÄ{º<ÃÁ’G=My{Û¡Ü>zàó¯åU»!eš^&Éwiô¤ ˜‡´×¼Õþ®¡×ÙPºÒâ ͨBáw^¸?Qض0œÏ2´Æû³‚iÅ}ì=ÁÀäD5S§Ekõsð™AM, ÅÈH¿¶*a¼ò(ª•¥Aý™¼Y ÷È›ÅüÇŽôˆÕH^]5„5Ì6£¢m;xò¾ãnÚkóI u!|ë`Z½;•+êyòy6´,h«¥u-F4áâ*Æ oþG¿†œïU˜ÄÝôë㸯˜¤7‘¾ë‘c— 'gÚƒÚ½,<Ü{s =-jŸ‘ز›»6žÚÙ »àEì n^s F̼D NœÃú›1Ð0û(<ɦTÊ~#÷£þR#ÐÉÀ"]þ .ÕxjÊ50ÅãAø-Ä–ÏzT‚¢ í9”:ÎßÉŠ¹"Xja_@\L ¦G[¡Âº—°$¤O0©D–¥Œ©<Î?Šc£ƒÞâwϽ,Jp ®­åÍýyûÓŒL3–Ç}{†ó÷î ùÿ±@WÖ“9#¢QfÄ#ðšªIïþØI>—Úì;(æqó¨P–qóL-ü´þ1~¹8²JVaS¡'L{fÆ!D L˜3úôÀ~ÌÁs•w nf)Þ²Ž<½$Æ÷ݨe5]®—8T "«ƒƒþlc Õ¹ÍÆë¨NÍè2WMø=L?ç°mƒòÜÔ‰³]›Zð…Õ×´ÿDÑ@Ú€§Ï±æ6ߌñï„É(±ëîŽ[ÈãöÉ@™eT"û ƒ®H*ûu“ªN6+ƒçóµx®×†¾4ÿ†ïNÿ€G³Uð3® ±Ç²V“_ø|Â,8›-N®wú³t™tø¹[ÇòèŠá·[ Ȧ†tˆLö"Zç#Л×a†Ýfq0¼ËGlWGùƒèk1ôu<€{ýN ¯Û«Á¥lý~TࡇO€—þê½ò'{ûl2Ÿ´ü;éqSw%n±¼=—5ÁÿŠ_^èF]~Œ$M¿}Ø¥U«@ç±1ÖÈîÇÎY ™ï2~ZdKƒWð ÜÞUŚŭèÏöÐm&góƒqÙ´ßx­_ãh÷™óîˆ_xðÃ1é”'UÙ‚²ö&UzvÙ¬ÀßÙûŠýýš õX«µŽ¦>Gm‡ ½ݦ0lK~Ú¬I[ö nUÿ\R±äw þ¡Þ#u Ó”¢ž)ÊtÝòë‚I®˜c’Eµ uÊÑÁ?Øq³4–{ê$– ¸\ɜ֒‰r©ð^ã;ý=ZñßÄàÖŽË81ÈëSEÁz ~ÙIþ-ú‰[EG¡Î}÷«tª¸Œs>©fgÚcQ§æ¯`¿s5^™.;àÿ*/-$9z•`3û,¨gþ<÷ Ëî ¦O“½Á£õ%°áÁ‚[dõ±áðð@,ÞÐI .¦Ò®ˆj&깂øÂcø¶l!Æ'­á¦N˜ñÛóßUFâqÊ«•8¾­×þ1£¥µÐÞ9×ôëäî‰\Cùœ¼Ô r¢#iùètšv$Š:Ù íÇĨA`#Û<û.|]ófΕ§5vC¹¿m?z×ï%1f{ñ@S;ö³â<ÀŸdî¨ýF¥¸î|•-Xz³·ÌÕä÷jÔ‰‘øJ¼u §ê™÷ã3©²^™o¸`ÀÿY`*ùÍ{.°323!ì’˜—ª¶ZÒÉÊ¢8sµìõÝ»@†Ÿ$<ãô>\™Ùˆ—eEÏ+LÏc$ňðêÖd¾-%+)gmø8|?éŽñáÓ<ÂéàªñÄwcÏ+Á¨UиΈ?š¡ECCí¹Š­ ú’OàO‡?N+½Kvj_A—Á ’­ PŒ¶w¿€SÑ&œØ—ãt±>ò°çX¼$ƒ%hÃÛ|TÕìÁª‡ÐÇÃíÆ?”°(m‹£ ­Š|†ÞxÝ çÞùò€÷žè=ñ&ÎÙ¬J¢té/¾¦æ$JFFñkÙgHûÓ½|e“}a3êÕä³£& mnïåSZý€-žŸvjÒô÷ID6Âæ$ø’|¹ënsGyÄ-k¤vX0ƒ2”‹!H?`C«¶>Âêé“€Yá—'­Ð?ëœàÐ9Gì6Ñ…Æ÷U ~¥ , Îà†Õ”Ž\~>~ïDã]NÜdî~~'bçg5>gD.ƾ˜-‘1Smò¯F­%¶.pgF¦Û Ýáü[°lZv>>XÃvÄòD©zxþEVÜ['èY‘:ËËáBeùÚâÅ_Ü„mZW ø£6Ybú3VD¢Ç¿&bÞÅâ'þÙä8œ²IÊ|è‚X2öïþéæ<²<.â{°÷lù(ATTÒØð-2ì¿s·][áÖT-4Gð?ÓÆ@I„+“û}“T®BžÉhºu‘ÿÿL‹÷,xsU¼7ãʶÙì¨Ä7Vê›’ÕpÁ¡¥ðsRveï‚Êqh´¹¿(…ÆÙcÐ&.ÈÓb[ƒéôÓØ$X¥­Œ§MOã†áù|coñžœŽ›¿§€¥Ã¼7o$sµ ‡»’°åöE2ku |8Q…Iñ(2í>¢6—çÌ2æ1¢ƒ˜×íƒ0SmÉ~£sGŸÄ uªü´©#×.žÆ)qé_«qƒÍó†5K1{‰zÌK`ànRëÏ%WämƸ3‘Òï®! ’ª‚aÊjT!ÃŽÝãÌ¢r¾ü/ÿýjfNÇêßΠE2yH¡7[AÂïýièöÕ„ƒ;fÂ?Ixœ=ýp¿•ν—§¤°+Á ©{çv³Úá.Tᦷ¸’ŠáÖOq°G:5§f†© f·-”(s[ì.³À?µp‹A£ ÎÀ£OåPÿ–Çéu’ôsÉ,ÅìT¬‚eGö€zw \ÎsÂr¨tñhùW@_AIg°;þz›–á¶K9üLŸ7~Õ"ßx2äé¤QÚþA‡/Ý¥Ä~ûp‡ƒF wøº°©Õ\øh̽SÌ7ØÝÅNj(à[ê̵;wóëñÿ°úë&(4ØŠÛ6ÝK›`î¸$²Cën,誔XÿW×% ƒÏXµTNkóäyVÖûÚw‘ÙxfcÌÇZ-ànrT_|5_õst:7¢Éìƒü†å¼çéÀëƒ @k«‡ {ÆNœp"noŸÃA­Ymø…‹5$ûÇ\%#à£ñh[p²úöð¤‘ü¨F|Évá|…«,ÜÄWê–qû†<¬D¦ì øˆÊ¡ ’ûŸËØ 9‡‚¹Åz).?&˜6ŠüÀ „r˜|ÄíûëToÂeŠÐ°³ÇQåU5X¬O¤¿®˜ÑÑop§Àå—ƒ»ãn~5;fM¹EÏoüß÷ÿe²që“!¸-µë/Û£HþFöâh¶NVà™©<ørw–8Ž’šE‚¡÷¤ævàå5jyÇŠwo?AW‹-fošèÕY;y„Ïé0¢›æ¨pß|/^º!èÈ¥º¢èaéç,þÍt¬>QÇdwr{ñ8p•Hås'»èμI%‹;oØ1Äi£¤%—§ÚüÈ=°0ºÈn…›Óë½ø«ê.ŠM„†ìt´Ÿ 1>ƒÈ ßT#`u¾nÁÏ+jáû—tÄÄùÔ͆6 Nz‹¥î‡x° ÐÊ]'xD™M× Ë&ÿ¯ÿñp­,¶AŠ`¸åg¾|'Þ¼ô÷}ÈC'™0uÙo¬*ÁpÈOpnšA´øà£‡ÁðìUüè%F\Ùˆ®‘€{9:^g76Ñ€Ênô°á•{ÑKsdè»Îר¾2ŸÙúsõRC:§Z–¬N…CvûÉÌæÃ(ô9„*ÂéxfÝ á=Uh9»†N-õÄm’züø±Z0Táe³è§‹×ñç»f!Š4™ðNí`GN»¼V#¹Ã øÙ¡{¨ZÙXšj-‡íQž™á.“ʶÉÅ’ù¬Ìæ&kÞêÌÉ’·ìÿ1Gs´Kd·å{Xà4ð¶Ú&8¤©ÌwÔ|g=ÝþP±;×úÉ1¿1“!o8e1‰BÈ•3 ÕCp¢SÖ¦I Ucs±. jñMJ4êñ6¾Ň$ÍgƇS„øV\£™mªSè¢1·ÕGð£Úñ°î ˜WóS>Öt²E¿¾Ù  ‚Ôí|ü;e4 Ë„§RÓpP7¶Ä?‚ãá_a\D .IúNÔDvÁ+&J×çŽå NÁ¼´=DP}”Ý[Ç/mœ‹J¦Cy¢C 5n\íÖEû˜‘Ä6p Oð]†”*íÅ’ƒñß¶ö¢›Ìù³°óQ>JÍðÄôdúwT.¤÷0²ïåW¶O:ï´,Ã’—Åðø¢°säyø’IЯÓR`Ú÷[89¢™gm—§OÖ}\LÖê*¢¢ÈT°s¿MµÕ°7£cP)ZzvÃèuÃ`éúixDê6t?¿ÌšáÊâZh}Ä “ÚK`ë3 >ÿÒV«óÝVžŸÅlT„}4°,šý÷{Ä$ÐéN2è+ÑwÌA0FJCÏà¹À4,œÿzTxïhOЯw ²Óÿû~` dèlÇØ×òôcåu"¾§µ"ÜôïôÚX3a2”ËÃgØG$V&ÀFM~2äœÛEu³³!Lÿñ>C‚4“ðs¾"÷L÷@›ÆD–‘œãöyÂÃ’/8XZk¿fÂêKêÌõ}¦kÚ‰h>7‚2ÿ v¬Kö*®×\‹yî Œþ^¦hlJóë—P…Oסû5o±½ávÆt#ý"}¤ï¶ØE)}€¨C]p\SœNÒÐæ:cRáû#д^çÑXÕ•úø‰â \IÞnrïÅg bÉ>7Mvÿ¾†&/£AƧàí+¹ÿ¯vð>«Lnf³ŒïÙ=ˆ«b˜E<)æô*Ó’¶ãL,o"»igÏŸSiúÑù<]ðáÆÀçÿÕqÚ8!GqáEÈvEu–(Q›ü•¼£Ã…›'^»GÐò¬^(<®Båò fÌȨÝêÖ§*ÏgÇ,§Õ«aDVš½ði…%(ïŠàÎ\O~ǽSÛéL›‡ ¾ÛŠG?–„½î;à廨½]Œ÷ÏÍ-‹qŸEðŠN†ú¾‡ ³qúÙnãÇ‚d©¦‡ë(C½êµÑ`ï7Ô?L?LÞ‡†ï§Põ~ÞÓ¾¬‰>!Û©¯•nulÇÇÎIôOÉ8jy«ÕiAÄtZ žÇ³ ÇòU/ôøÑ~üæ=ñõ¿ýÝv8{ë{r1—Üw ‚ì,ÇQAþê6¬ù745ŸÀ¥ÔÁTm£l‚ð~„}4¯ y㘷ô/Œ;«€•ËåHÀ¿”þü°„gs%`Ú¬M¨Ó1ŒÞû5_ ·¶ç'£íËI\?©7IAÕhÀ oŠðiÚ ì‘Éǧ .¸m±(mÚ0ƒ—ȇÃòÁï¤Té¶·ðVå-<4‡e~ŽüŸ¹Ý9Û}õ-¨évq,X6åÌM\=Gã÷òã8ï_:YeK`]\-Lû[P×Aø“Xæöì/Ù°tÏÿ]£\Éÿ~ÿí\õeö1Ð•ÖÆÁò­aÒ¢ tc¸6>Z÷x6>™ ž¡nèöö D[ós³©„ê[ru~ r×µ´$K“î<âÁRªrÐøV"LiWõ~sà£âKlŸ,Cw쓆øÕÍ }ÃhNh'ù’ƽåÐ:÷„Ðe¹7ìK[ͲU²áäÁÙüe°¨/ôÇòs:4mtST| ƒêÊQÅ=“eÇð á®ðÚoÏŠk‚{‘—Peõì ˜å© ÖocLn[ÿÓÞû¥‰£ñBŠWTœÂ5ÆOÅ—™C!lB¸ÀÕˆ¿Ÿ<€ÿæò&d-Ûà~×gŒþöôk›gw_@f%•£Ûñ­^fÏ‹K®çØøNîþ^ ·,×CŸ ÛáLÔxœ>'vM«T1*6u+=ß E{ò¦7Yd°cÄüž‰s9ó¤êLpøE-Æ4k«*gáÓ`DR2kTIi[: 7ú菉Ÿ'} rä(\}’ŠN_޲9ºÚ0'¦‘-}Ö%°µi¸·(Òs5먬g,|Ž?ÕÿúŒÏ±€¥rÒP-Q Ö§ÀÄ]ípñt½^ ó>ÁÜ—‰K:07ÒžÊ ùqÄv–«q»ÿ‰!Û•Ðð`"Öè'Ã=·x^zÒÙŸN^pm¥!^{e»"«`ÚÆqüîÒÓ ‹v8{Rs–$ÂûuÌIs-]RMžL3€Žô!ìz… u#í¤èãhYõÆ?…F,¾øÂUW¦-ÅäõåøPt0U9š š“ƒ°¼7~ôJñQ-¶hê=„¯;¢ÃÔe¨Ó½zŒi¹ çås`ö´‰8¹T–ÂCaß$Ujwé.t+. ÑwšÑ*ó{¶Ýo3§¶%chäæì,Ê&Dñu—8äµoxu£%~ˆ Ó–l=¨Ðç%ã9“lÈ É@«QÔ r¿”/åTxÀæR;/ˆÏv]{Ž ;ÞÂ~åÌ?+Ê=šËG°p⣴rcŒPaª<üןü__ôBM¦Ó±\½½Q ö ïŸïç&Ëï 3óaEa,¨y®eŸÝ´ûï½4wn6X™mä®?åé.ÃKÌi_ ¾Ê‹ÎªÙ—þXÒ/Æ×°ö\.#‹µ§Óôßñpx¤—(tϰèýÜ2'úöÖlÓ¢> ÕÐäÄŸ=!® †à·¶ypª¥†lò:3 àÙà4µÉ‰áÙý:q\°`_Ï%"%½³ÞL¨ÈÊS©„xb+è~s¥ñ¾;ñCº5ŒMúÎ,fÔ¢ÓœIt¶Ó9|e=ˆ¿¨Ç#‘#в°æˆÃŒ¶» Áš{†åÁßEƒ¸âÈ"(XúFßR sïcPŸ‘Ætu†ýŸ"Ðñ=7þƧÿŒàÏh{TÛóD¥ ô U1ÄâXîåÒ/5x¸] ûñÁ„:WÂÃ3{˜Ì¬§Xèòw‰‘è] leê-¬»ó‘´>1€oàMIj›>¾[ðäì#0Õô,޼ß9ÿI³Ñö´ͬBǹ¤íÞ+·ý={„KþÌf²‘ÝäÊÙR²Ê"ö¹Tãl©|žUã/s̹xƒ%ÏýæK*>[Áð­Ï᪻=ýH”õ¬¸ø HQó½Z îÚ0mûðySƒs›æòMáÙx,ÿ± duD¹«ñmã˜è›ûp**kþì …{Gò Xý ì;‹åÿ¾À½ÔÏcòöþÚÙ8š.ý;’§Ù¨ðbÍË(CͬêËy£˜Ï2ï•ö:]#Ç ¶ÂÄzg"¬ $Ÿ²‰E¶tÉÖz, 0¯eÚþQ¾WIÏ_uvkË!ftĽýaž7œEE6œãdB‰-~” ƒäùQ2zÚõ‘^!ñò|î*¾S…M)d«]7v½q„´ýû ahJëé@*é"Âs;Ø[ÃØß/àY‘2}S .]Dž÷K—:“%Ùgð¤I-sΟŠuúÆuÕ ¦÷M'ÅŸ2˜ÿõ%tOb ão ‰6Z‚nZwQòZ!/û%ø,½‡ 鼈{tÐ2ñ¨7L”vÔbý¸/ 32¬lFü×'í:_ñ#è¶ï·ê­D"¯n`ý×ο€÷»o°W{—³l¾äV[ñK:\ëãèROø¿^c[ËhWþ™Ôý ç^g²CEnˆIÀ×O–;œt¡EªrtmàNúÈ,„ü›žÂ]XN¬Ãæ²¾ÝÕ˜c+Ð>3ˆo~fŽC&ø ØãKF0#*ˆ»ÏÏd¶ðil »9 v(·Bø§¸7P€–»Ò$¿$H?Ýï· ¼Ñ±'…UžYžùaá›Õ¸Ç"‰[\ÄÿŒú‚Që/Àô€;,´´…í´—jµ£†í~ÐLûISÒ±|õ:XvÈ™¾ÞœKv©"Ùª4ÿwŸBÉ}DòtÖ|ÔUÔþ¿>c‘Õ.xÖp1bÊ(ÈoÅå¥8Œ…ÎY÷Á mŸ¼ä,ÀÍ3ìfK éÆ ®I7C~ÜWò°þ¹S`H¯Ä섲À,øÔuU§¶á”[xPòÃmèU^vˆ 9» çÄ_Ç¢tßÈ6à æo‡ë IpÙí#Î_1Œ«ÈÔ³L9®§¤…+7¥@Ý 1nºV+‚Æo9¾³H†¤°€»i°ãN;È®5Å&) Ú¦µŸ ¯lŒ;+Šã¢ºH╨qà<Šhô¢ûUS¼£{p þ[¦ ë£V¡? êN Qæö G±íû`Cúi6$¹ ¤uóAùhÀådÙÙS¸^1J+°$O”Ï(9ç6Ysƒ†—rÒÇ~)êHyŠ(}ñm"ŒzûÇÀBN¾hPºµŒ¿ßЃU3ÆðKö}ûllµÀì~L>Ó•w|ºÚñÓÔO,kߦQ…„àoÌ¿5Œ3©}P1Â,΂V… ݪ­ŠrµßáìÁ0<µÁˆ§N‹º_yÐûQeÎ*3Ý¡õð|ëQÛíÆÏ»…û{ èïŸsâ¿8|4±MLƒo—&c2ûÄ„7?‘Ø×pý‹"6Z–}ÌdÉè Ýv¶w‰{}¬ýâ—SÏCý«ÁtÏ*}vJa‘0æp"æWb›7q°xìÙxnEƒ ½HNÙÌ×á#QtÌd~]q-:g߃+Û(8¤¢ËEç€ãýç¸sƒ|tÖ>YÊÙ°Ý̆Ü^^±É¤ã©&íZžHº¼Sq¡¢ßÓ6 ’<ð¼Äå†Ùr¸h‚ ©¢4øZ=Þ7N‚Áb£À«£½æ)ãå¸ßø7a#|õ9‚ ó_bvL|œêÊ\MˆU™<Ìk] l­ÓCÛÕ(öL’wüŽ´Nš.ÿ›*<¬{Žè}ez7ªAÏ ÏÓ‰çJf“ò¢´;ø>¹1ù-®òÆOù3ˆ¦Ïyöôä¸ßê XzÍäMÊðˆùÐL-Ç3ûñLÖLžvõ+›zî)nÇý Óç‰)kÝÍ/‘ÄÉÓýo3±nF.|Ý„³—„k[} éF+¸´ÄœÿŒY]=óAÞc?^¡‡Sê߀Á´:ŒÉ®f¶}»™•¥ÿ÷ã\}«Mcßü€ÆWØiWú¥â]½`G,nœY £Ãª0kØü·ô 6;lBIMž›º‹h.9&Á{±éŠlŸøŠÇ Ö\“Á…µÇ¡øúLæ5è#6M'3Ž%ƒÁèT2¨« ^ø€…þ‰¸Ê´ o›íWŒ¨ª«ðC‡Waõ¸JP}Èìµñ–0öK6µcvP}üðÛ3©+ñsÍùèyaüÚ“Ý©¥Ìç?¿Z³²¯ ¬/ÅÄñÕ|7¼`„/Û ©?ƒÍ+>À Õ³¬‹kS]= ²·ß×G¶Æ …»$*Û‡³æ?#ë§¶¢´Ä}¬šT^û"àêêìiWfŸOœSʺ'$^˜ ÞØ‚où ÕIÞ¥‹x¦`áXm Í ×ò üŽ%±ˆÆ±z­>!Z¸*;·8æ@PÉAØ“¶iµüß·JW”ªÁ;Ũë$oš¸Ù­zF¡„h7y4=ŽäͱŽ4œôa6F¯1ZO—ÆbDäv~£( ÿiÅ éï`óa9ŠÑØçÔo2ž^x¯á¡”÷©ã÷*àe"ôj¦Â‡·[ðE‹ôëð»¬yPÅ—{}…·Æ[~ç  ¥î«^ïשÂu°‹„¬0ç¢ôèíŽn´oõå érÝ«äJо ì×möxèhj¹IŸîð¬þ9îýÇá÷7]®V%‰Ãˆ ¦Üå_ žáæôcí ò»Š¢30Á_Œ‡Oyƒov¦ÒJci8©Ä¿î€ó¬L'VÜa¦÷3F^®€§Ã·Ò²‚B¹=˜¿[/Ó¥wÀAó:C_g¥ QU,ORxþ­ª}ÉÍ&ô2©?äkö×0‘Ü7BÎ_[ƒÃÿ¶ºÙ†Ç|÷úr,ÖÏù¡mýï³Ã¾wj¤wšªœ¨!ÏÜÞá´õ˜{ðÉY—Ën¦’ÜVCå·-¤mV3œ²JËZ1JêüwŽlíz°]ãŠz5Ùðw–1â\½ºa*Æ/Té’½L¢Ð†‹–Yñq¡¦¸'Gž »™f‘$ò¸>%°+Ô• ³`ÅmzǬʧ²2¹à5&K˜xŒå‘›üÁ–®ÀÓϤ٠£“`ÕÂO¸É‘ªÏÞ2°þ%{‡Áûmnpî\ª8JÒ±™Öx¹6UÐ/…†Âa}l8 ý0âqô ¬úÍN¹‹É¿cPº8Í5aõ{cü–7ŸŒú »^9@ÙíhŠ®(=Ë·•£c¢Ob߇øo¥:uÛ<;ÃdáÌûÓðöe)”ºV࿼1жÙĦí§'X³ú;«±3ý'ÄÕ㣭 ½ú <­;Œ¥/ïaòŒ«0åJ9Î4Ã>ô0ÏâÉô¯b¶p³íâ½ZtªkY€ëPš|);ð U¢?Ó.À¹ÐqdÍ(uÈúeE7jdðŸãÕn·â¢L˜³ÆÆÂ•€þOÌ…¼B-lÈUå¾ïÊPn]*}geÅŸéðޝ3p¥‚y7—ïuò„Q·ã¿É/öŒíÉïd+÷¢’[Óë ¡\ÚãÖ¹°øFê¸Rø)l8=Y4–/+Ã-2\èЂç¶â£%Øôê™”›AÄäR tÃ*¾]=5[ ¨éÀŒ¡ƒæKòì‰÷ñ㉥tÙUôš¸–ŽØmˆWnCvÜZ<’KÚ=ÂÌuUðºa2{ÿÚŽN³õ‡ï…{ùü•Åp}Õ’´VžÏß|õ]ú?֌ϳ&‚B(UyñïÖÁ—Œh‰ð³¿Wè¶c­67ëyQ‹“ >ù œ{«ÅCƒ `TXªTr¢5IŽ×ň`ð…Y¤µø2&„¸ó:9e¾ìt¦ÌQæ*•'Æo†ººIØÙ, Oì“Èm2œë¯b‰Ð÷¯Ô†¸‡¯HvÚn¢ÛÝ`'kÂËúܨÌEÔ&µðLãñ¹m&ÑØ8þ¤àV®(Ÿb/$_ ¦Ñ«NcÚ’= fbÀ%rEøùäý¸M/–òpe?'Ü%å^âtýÜ÷°l~Ûlù´{Žƒ·zñØ?p ýßïßNy,t°$î­”AÇ;¿°JÓÚ[~`SŸÀäÛa8ú†÷Ÿ—‹o{ øQ2M͸Šï'~EÂy¡w «í¿sý¡eÔ+vè³<šÕCçHji¿¿ÍØMmŸ¥Àê=ذº7…èdíÆCŽâëælš½ç.ÙQ èI‚µŸ·ãÙãó¹•ý5Òf˜…†ØîZ‡ê^zbÎù†˜}Ϙٶ <ÆV“·s9^.ªÎ×®Ž*—ë£ŒÅø'—Íx_¿ýÚ2Ù«_çà’ñ#ðWÂÇ5ƒaV§½zâ:û!Þ†9²ääÔX:Ô®íAomÜ…?eŒhm»l›¹‘¾³Ä× a’D,þõŒ©Pxþepk:Q:LŠÚ-i׈=ø÷ævxzVV˜ëró‚)ôPéP¾7›+9i’̺ԯE‡÷î ¦+¬×AñûPb9‡ø‹Ç"œLuàµúPc^€¯²^ãØ=#¸ˆR îU«¬&|pÞü{»’siØU®ò¶E®Àþƒœc¿M[Ø¿mI´=q-H§Aó´h²z.‰¯ÈÅg™RüZå5ÁŠŽ\;Ò_Äà¾9°2|*Ýau ’t²½]XH{Ç‹ò¦Æ3$¡ì<9U,‹&ë1îÇLnjœ%ŒÈ5„o+§ ÄßkæC¦pŤEÇÍ$„¡¾úÄihÓœkÂv2'þN.“Õ™<…¯“—€ñÄz²ð® I-qÀ~ÚBÅ÷ò\Éÿš "˜ã¿[2õváèã=èè|æé’5ïãµ¶X<ÞsšÕßãµ·óÞ(\7yÓ¾Uñ9œp!G~„yý\1+ZV¶)¡Csñª-F³•œÄÚéÀ5[ð<,Î5}•ùžB1AˆÖu¬üF¤_® .¾#©–G"v_÷$=Ù³÷cµÙ!Pû 93¨¢ÈøxR‘j‰Äƒo—.¸U톧w½êŸqäFœå”ðPÎI¦¶æ1,‰·G¨Á«]¤IG—Z5=‡_ÑLâÖì«h˜17/b—:Lø›n<ÙEŽß7?ÙãÂøZdQaÒØÿ˜‘ÓÅ®{o$ìra§3Á晹pèa:žk[†·ùÑÞ'¦ ‘?ûT„j“ø„‡ 0óÚL£Ú|ÿÔè<¯†vmw;¥²ûæhÒ†M«Ùì”&2è§N Á¶A¸§{-òé"¸dµ – úäiǾ>0r~Í´vMå7¯à¥Ñºüd²5<ªrv©´HÆqÇÆð_ôù¿ ?Váðž ´RÝÁ~ýuÄq6 ùÜóyÖjÂ|ýK‚‘» úëýhhd6tÒ6k*rf#›ý|”¨vÐù?\Ø%fùw4ÿ"¢JìÊO[£ön 4örà+MàòSOìœÕ)èy¶ŒKüäÂ'ásaž‚fþÉl6 âÖ»A¸IÅû_k¸¼sÏ^y…Åû%ÁpÃötwZå Öùrmæzø»!n½D×à›‚kï¦Pß3>x­Ô‰š”ñÊH[xâLü|? 6áO¡ ¹ôŽ‚L­9ÿ²žt§¡Oö?iÝ»‹5}°5Ÿ[Aêg ÎI×™°YÿŽQøÂÆ\XÏ·GŠÓ7oà¸f1:ZØæ‘ _¾[™I(ä@}„cxÝ•-øñ®ŒÀÑj,nœýøTòûÈù݆ñ«5 }Q¹sZ—Œ=…5W/ƒI&(¿pßøá|V‡¯?3Áyu[4þ4Y|~29ÿ%W…[¡ˆ]¯ñ¢—Fü·‚­˜dÀW‘›ß”QV<žð[ˆ¾uÚT-û:svÉÚäh;<šß}ˆ½0Q«¿1Yn–âÝr;à›O Α¥×â}S6¼Vž{üuxÙàY¸¤ûÜ^Ž¡íãØ†ÅÓ¨Ôv \ø5´ïÜ„?+ô鳩Ãù£x*}A…WnŒÃg÷ÂÑâ"÷ñnÁ…ÿtyñùÙ¼ºó8ܶçÄ#A Ì,—ð?=Õ˜ïe…¹‹'bï|0Ô¤Äx45¯X /o1xú~»`Û,þ Y“ëá7­µèŠ;yd~šíä7eÚsr‰P©ߢë>g $ –Öt²pÔX¸~ïá(º¡ÐŸn#ÀÞgï›Q‚6®cÃÞ% ;}¸íe½6öèO{~Bs@ÿè彄‚™'àJûP²¼Zœ¾¾§ÅL÷°ä§9¯NO¾ø knŒ§¢mÆpåÉeø^6‘G­«EK³ ²Åyà-^ä:’JnÏ‚énOáê-9ÈóÖAV2ÕƒO|fÃWþXBë<â–±¦´ÖaêWÛQCëéhé–Ìî†]$O p~£3îº y#3ˆÆáp0ŠÏS9JìËá‹Æ#8±Ï’.ëÞ‚š²btÓ8Þ¹s0=ù3=.»óC»™ð»ï#twì%ß,e~ª7ÈìcNdæšb:í¯ì4…±GPøb/«ý;~€ÿý¾î'¯ÚÔÀr÷]ÐÖ$`1-æf^ÂgYxîH^¨aÇMgâû¢×8jCªÀ0u•h×$Ë׊ómó¸Õ"FíÍp Óârt™’Šíã ¾£­ Ìæú³òÄ:|Ù÷/|~úWïáJ¿eBéuáTF†9BÉ®&(š’HÙ-¬^O>¬Ñ¦»âíƒÓADü$K‰‚WeWáŽme éé/S›ÛEéóÙªtpÏ>Á~ VïΆ/t“Ú ™sÅàg«í[ÆÚ¯Ü…ä [//e—bÅgü³ÒËÔàŸ–Gᶆ#dwû¦üÑ{wc¯oY³dˆŒ)gŸâØ`U:úÍ*Z® ÂS˜BÓÙ¯ ÇÑcù;Ð~¨ÎëkðÛâƒ(g¼œÜ=fÁ_oس‡õ6HúÚS‘ºWøô%Dlb¯r[pÔ=záP!<\~Ó£¯‚Tc Hï}wýûã+Ôçñ³î’–KÙhü£mw¡JÖwœØçM.ÒÜ«r#œ)˜ M%ƒýø8’¼ë†JWàõ`'ö"Ø„^õ²¿sÜ€E¸¯ÅZ¹R¢wÀ‡ß“XFµãÁ²èMÎ\Sƒ)“áFżR®Òª@óÃ|>cÉïTL™¼5ÏX à7¡ ¨ó›¶üE«¯_0ŒæÞJDˆ'-­ðTT–öÎaƒ½ˆÔ {ˆ°<–=®túȩ𢹷ûïÇ%þº[θßÃÒµ×xý *·!ówÝ…¥‡xOúxÑC•üêù‹ê…̽ړ‡„P=Åiø™çàç¶h¸*Èš$> ÔFßÂÜI|™—ßý5vÝuAÑÀpV¯ÈÓqêeIzw“Qb¦ÔkïŒSÆ ‡9Ó (]¢1SŠ;Ý·â‚Û#ø½ÚׇºÐm¦±Â¼S:5¾À è<9¤Œ^œš6ÀÿƯò`Ô…ø´p-LYmJÃ$1bÇ*`“Ø\:Á¸ÕÅhÁ ?~€¯¨ô§1&<©d&}¸¦×-çß{ìÈ蛨w,m«Cè·«‰DÁk9üCŒMã Kô¸ªùOôøx L6žc#f¬¡|÷1þf1>Ú4–„E>À‹¿VÒngg:õänìÅÜYT£#}¡#?+,“™v­¼g±` $ñ¿•"tüQuZ¶£ƒu¥eЃë$øöžU´1+ æÆ¦bÑÒƒ‚()žt\ûW<ÁìµCyjýºúEyñÿÓ¿—ªÆb¤dtžì“»8ŒÛCþ†*óaŽº¡áïzofP8? ¡:·+ñªL~¨(ES–",ÂTauûn"™Y€éFú°(êOýʎ;~ÁŽ·qÂ÷ÆÒ°Ãs'6Žo·?e÷³­ú9>šœoGEíâøÏÓßÈû{ž;ÒšfÍÛN>¦Ž€ª®]\ôV ˆ›GpWõ‘4LàË…> ,²½¯ >S ²ç)8–<Ä‹fGÀ;voµ›²£ñÞ]9װȆþÁc“hß°EôÚjuÔ˜[Ž×÷ ¢gG3ÁñúúîWîþ†v)zPç˜ÀòÐ"¢óVSïÊñ#^qÄ1RƒÆÊÔ° ZÑþ‹8½å½ïÍ+A»Æù4ßf(ØõçS§!¶T©Ð†¹±,yp8†%š@gV»ü)N¼Ÿ 1R¦Øø“³äâ›ükgÛ¿_"$•©áwpMË þr:<&s7xé¬åïƒÒˆ©®9U-Û 6§âãÕ3øuã|÷_èÔß¾g鈥}ð0F ¢.ĵ·SȤ)xyÓe8¾Uº;®¿uk6“ãß6•àÊ~þ±7QSþ :Åxɵ 6®z^Ù»oÿÄR |ž4÷¦]$Ù“S`­øVþÚr Mnãξ›ˆÿþ§ ¸ Õ6·éq^MЏÊé4úTéôìIÁƒÕ |Õë>œaC5ßÏ¢"Gë úàQ.ÄI€!ç¡ÕwŠ`¯‹$óŠžOûŒöG¼ T E‡5ÉáC‰ÜǺ‚ØIç’e-÷¨éÞ"x[°6’× V~ R~Öü×MÕe¦Q%¥1Ô¥â7TZèÓ¼žÑ<äÛ2[ …wFr£àŒ›t•Xmà¾IxwÅžZ­K¡®‡WÓÏUðãÔvê°Úˆ§xX èßÿöE¶®©t›UäÊï&%³/‘‚G²òÐ6dÃ’YÙ­«×ÏL6_â/\úï•é‚ÿöj¦ Ó2¹ÑÝaÔ\d<¬ÖŸF¿.Láí'¥£‚¸XÔÞX¥Àë”b ÌTƒN<¸1¹ j§OÆ}åöâôõ¯ñ|¹Ä::cÿm²pÕ‘(r¾.Û&ݼÍO¨Âå'Mèrq…ó¿¢Æó‰å;¹î¢ý4r¾Ÿ <›2ÀÿÇ”n»ÿZŠÖÚÎ䢸lb÷óžkžì^L9 ­kùà¯c™ÚË6q®?[¥#zcùÜ#íø–ŠðÇðŸ¯>üU<ßæ<"OêÉüØzüH]aÒSuü6õ2x¿Aå›ã@Ç#‘Ú^eßf¤’úðßIŠ_&1í>nQ=¿²Â†ô‡–:fˆžÄ'>o¡éTÐË´]ù™u/H{âQ+ºŽço×FÍ i°Üà{ØôÆÞSxÍ!VÚ2ëÔM( çòŽ1ÕYúl1^óý¯”=lÀ§—ÄÿFh4$»ž‡³JGŒv‚¤71m°w–Õ¤õ+ùÚ¿VTzä œYt>Å®Fe)CLQâèH×O¾ ³w¥â£ã#Áåps¬ZÃÓÛ5¨NŽ Q[×Àféð³t5-³ÀÍjÑ&pü½ž~>­!fôÙ…¨n]N|Tà*b¹U—o©®6+”bÿ5ã·lk8yÝžZ•ŽCÓß‘1=·¾ühr"«\CØê\qÉbþ~$ŽÆrÓ¤—ìö¥ÇÐxäƒðÔ':ÃQ£/]™œZò:È ¸Ö]¼{r'[̓QûÞ¸üßy¾ÀJë®§úf°µê·ðhÌŠ{ {úÀ|F<Æ:ºüêðt˜9/‘ }Üpÿ~ž[¦©M½Ϧ¬DßJ\æ­ ›5îâ,'ðÂ.%£Ÿt„¤º£‘ß( ¦â¶µ…hŽ)e>Ô³?æ™wFÒR]ï†IQ+é´gK!½©FQ_Lî~ ?‚6±í÷Yßìy°<Û Æ?@™3ÆÄÿêðÔùñQ‡ûýÞ¶FJ DÊòÖ áåGtÌmø,áŸä±eåMvÙÞ§„UÁüK5Àf_ÅD¹’øÛ«ÄëIØÅ?`{eü:ôöá{*I¿•«R±Z)*­3Dàcüž¤†ì£ië¦àëÞü¦ë-R—§¦Û:p÷Üb<úY‰‹þÆ9K°¼2íU/èr3âÚ¡_غýŠýþÚÏa¹KRHû}' ,»‰Á¥Âòr}¦¹ØB8ãî}òKø„ßœÀÍjdvä­7 éÇÓ¿aCþnPÚp‚g?†/ „çŽ.¥Nómñ›é/ì±?ƒ/”›aQæSþêЬ;Æt™Ùò¡í)úµŠÒƒ8ÔKóãn‡x›Ï ážµÃiÀõÿí'ÜŸ•Ô‘ jmpKñ¦÷âÓ*‹n•ÏmÕ›á[óbPê–D˜ŠxeQ ^L”ä‡ýã@,X;¬‚Ú¢ûð»'ƒ·ŠÈQçó)Ð2×fn¶â9?´œw³ÓE™¤íx5H¥Ñ&_=½çŽ;^2‰ØËÔ¬j3ž˜rçë[ø»ê8ôÝR¬¦ˆuüë÷/>ɶ N)6r³û»Ñ#F¤ÿ£¸“ò^‘–B×N Ç_Ýxž¡>·ñf?^žÄýU4…ÖaÑ0yá~Ñi*Ÿ7öú™òEßÛÙÇMºØ9ñ½ä´sàóÏÐï8)Ò»zëù™¦¾Ý;¥Â…VfÀÝaïÑä„Ú·í"_÷ÆÐCl8MNTâÃZçps ¦3taû'-ؾŸ¸ÖÅëRáæ}éäÄ&{­\ŒJNY|p´, \åI{¼$ò˜E5Ïá*tz²,x ¢ìPÚçN¢‡ÁþQ6–ºî$Me‹éw­ ®f³N$òÚ*J'7ê ¯ïŸ ½ö)üRïböcö=žÃ_ŒÍÄ Å÷ðJI„¯hP•ŠE¨øf,]ö^ ·_•†ãHùÌå‚ú;§qr 4d¿xþý/K{¿þÜ¿t^(¡r ¼Ç™U³¨ú„;¡¨ å7šÁé¾?³KµÍ­”Šœ}„¢¢ü}S!ÊŠ'ƒÆç7h…™P“ŽC¸xVŒ°Mýz:j°oîj4aËt¾ñnen&ólyí§ëB÷ðp´ "›]=¡JôžÕÈdžaÍPýå0–nˆÇÛ[íèÊ¥;ð×ú4Ü^òÔ­v‚Ý×Þ ˆ30P£Ó5“@6Z‚fMÛ [•’ŸMe ¯øì+ÚqhNýqc ö¥†—Ì­¨†%×K`¬[ßïiŸà?¿ ÐSY<Æê£ZÕKhÈqä÷·>%r~Ù !kf#ß‚m×y<e óÖ5à„m‘x2xžœ ûÂR÷ү“.°|6‹Þy`J.]ÑÁAñðmünAÖ†hê¶üÎ}’NX’‰Æ§°5]×LKÀ²! Ð~r|Xò_?2ýóI…f襂—ÖlüÛ´2Uà #ׄJ;ØÄëC1gÎ*¼]@*œf¡õB^Ù˜ÃÌi3”ônâª&ÛÿÃÀ˜±],MŸôÝfèéÀ°»_ -¦Œý[D7;ÐðYÛiæ£úKi8È×U³ÌÐì’”ì7PS¤1ž`ÿW@‘7·a÷A#[Ð͆w×Á¸M¨VcJ/oŸÎK›ôðì`[^8þT/™ Åá¡|ßR)úöÒì¡!Gšqu±;—j»€Ã”¦Àœ‚þ>Ü’ªNÙYߢI©-9ø–;dý3‡Ñ[Ùç‹ÅõΩðØê0xP‚¤ç,?T„Þ›ëÌŸn"”ûŽãc7]h…6_WçÅ÷®#ýcˆFÕQ<‘,ʇ†%ƒ~F:8|Akå³äêýM´ Põ¡Ø(G/â þ³KPÇñ;àÇ]?6}ÆHhùk†ë_”a’^<˜•IóLúTP#Ê3‡ƒö;DñÂ.¬U]ɶe¥â”jq¸ÿɕ˥œÃ¼›pâáƒø2ï"FK¢ÐL•ó£íÌB{'Œº©Akt ùíŽÑØ}íÔ†:ã§}­„bÈ«6®“ÌžX7£rŽ7äÉxrç-.\³ÛŽ>-œF´§-£~KŸ¦9Ã?&;Éð±Åx(> ÿ ²AãwB±T ïíÕ§‹1ÎÌ ;–)rí!—±ú¬7$4D³ä1\ÌãNˆ@ÝÞý);rv›2ÄöL„)‘ÍìmbN³ pÑÆ¦qX–¯(§7F/‚'ÆCùþåø«>Z8ÒËî¨> 3,'2‰›(=Ó§h…¡yË-6xÜH¡Bý`2ÿ„9ìÈùMôΔ±ä¯Šhù6S,Ã9knÃÕ€,ò‡êíQÂýs¾€‚§1Q8„9‹æ!ïÿ›‹äl+Á¸U§À<ö.Æ^:éqê`fxVªqië;ØŒ¾\˜=*ûòæóˆÓ_؈ ¹ØØäÇ=ļ1¹¢wËVBÑ3nܤŠÿõ[[6§â¡{1zH*^ÖWðÿ®£¥ùÜÒ,é] £ÎtÃjSp-ùÇúʰ²B'/­end, @ÅdXÿP‡Þ|p”ýK€k á£ôwôQ@š !ó½ßÊeèÈ™/X{Û7ÌéèÏ΂¸¿QœŸ,ÈÅýÉß„G–Ñ”EQ0£4îH\D‘ ñáãh˜¸Ú ë^ñˇ¸èaô €Ç¡§P¨àªªÉßSVpÓy\ñƒdøÀE¯™˜¶ÔƒF W¡Ã×W£cÜcì,‰g¦Ž-Xšv¾ú$àÕB<=ù +­Z-?œQ«ÆE;1ä£î@ü{ãqüË wŒ€ËU+±ëú/TªpÃ+÷Ðpg4¼[9CÄDøzYÊtSÕè-Û‡Båk'Yó´[ 0¬á[ô ,ôP€‰·OãÌ…CðÓ½,Òó¹Z8U«b¦­?×uY ãϨ’¦ÏÒtbwDÿi†aoÁh˜¼@Öï8œM… þÖªFˆâkÅOXæ³¶_Å1ƒ2a¢KFw†©æ1Dpë/ù*¬À=?7๛XéèJùNXý$ c2µ¸Ü¸*aÿ¹YaÇ1ò;ê‹®Zæöey2t§ùá‹(9º*[ £Ïc†â¸ÒuÑ€þÒy‘ŒNuf“&˜òŽàÕ¤ ø;T ’g¦Ÿp½ântÓ†ºìÙ=90òu®Å_¯¥øÜ¼A¼ê`û6®_ #Aïüq6°réC·ÏKÊ ]OžN¼ßÀ¶|_É…Ê0§èŠ ì¯8=}ë˜kU¡^ ‡ÈsRôx¥~Þ!Fè{ᆄ4ü·×’w 'Òs%Ýp9X^|ÖIÞèŽÆïrnpuëIhŸ±gV†ÒíÝ-à:M‰®_5ˆ:Ô*°0ٿؽà1î‘'¦?Î;P‚Q#ÚðéôÉõêÛ£¨sþTïô?ÿë¸Ú[°`û,öÔw¤øCÃÄhÒ½©&LŸK;¾ÁѪ]ðO4€Nñ‰¤Þóù<<5| T–™ÒÈ<~ÆŽåJj‚Ö4->rõhú|Z&^²·sQzëXœ³ÿ«~t¨ï>Þ¤á­-¿ Ù+iÇÑP’¶šM™ëG3²ïƒßcaM­7’G:,9FŒë¦U‹<·+À;— qdläõøÓ££ÜÑÂT6?Nonm€;Kä¹rã4<¸ð(JK°ËÑñÞÇØî5/ñÃÜEØpÕåõïBÕÁb<³Ÿÿm3ðÏéÊœ;=­ºôÀÚМ¤ øÕœF–äúƒò{9¶©àû¥€Í1’´âc%•³„Ý÷n-^!T#f(µ,<Ķxu០¾LJÒ›Ù¼üƒ¿ÊCÑ}(Ÿ*Ó¿ÔáÜ´vTú¸g(‡BåLIšë>„ýó—ͤC˜õjŽ›¡îqH¬2Á§¦ pà1S;³{ûëðøm!çÉ8ÙÖËîï—¡çãta¾Qt¿éµ,Jߺ ¥Ígæð“1c³æñ éq°RÅŠ.ü– ÿ†ü@ù‘‘°kæ,n÷<VOÃfk?ž7Ç–ît4þßþ·þnDÙ\æ¹´à’"Ì”¹ïöW‡›wáFý+dÄÒ!ø5 ”IÉÓƒd]ƒÿiŸˆUöÇݶú)â!iYn³d ·þlî7\¦Ã…ˆÛxá¢6?¾=—§¥6Pð.÷+bQÂ\öüR 8ÛàMáÙÏL¼z ] Cè?ÿOر G’Y “™`½}Ü\(l^0šêݳàC/¾ƒ+Q8ÓÂÏ?ë5IP3ÿT§<ÃO…jti¨/U/(ð±ê†ôטgØud“Û1Ïó°^%‹Í£ñÃ([(½| [u.’qæƒø¾µÇàÕ`#úþ+‡‡_´[1Í·G„áîkAÉB{ þ…-;Á5ðÕ¾7,=î,Kïz‡¡J…úû»™¯¼1WåJ×y@T1¹7ì[0{=¸NŠçcš`pç#–ÓØ‡ÿô¦â²U:¾V‹žyrô¶–ÂË|Óx< iÄ{c Š«¸mH¿ßZK?âÐ<]—ª *ˆhœ—ì™Èå™pçYÓqhTvÎå;æaìÓh>ýÏg¢¤ÐÁ>ÿ9K–…€¦k¡­ñ«•] G®‘Íýœ3·Ë–N¼r­ªpà/—Åy“2žàåUŸÎ± Õ~è2d$ß$½ •S„ñJ»ayEü@þ êS íV.Z×_fŠ£Ãá]F).ñíÃüÂíX¶[ˆzǼIÄøfRƒA£qÞ³_ðûàøÓÈ罉Ãý㟓 §_@»õ9O…—&«±òþ7';Ú§“vä¡nÙ9,ÏãK¬ôXì)ºF=,¥^ãàÍAôIÍÿø¿Å¸\¾aßomjE)wE—;×#ÃO@Í™t+ÈÓ‹š{¹­×!ºÃÓŽM8„Ž)ßÁ¿~Ëg³¥#¹‰]"È…4ƒæf1¾[Ï7Å,ÆÁ‚¹¼¨ñ^žiˆU’áí);Úl›A$%7Â’úrl•q§÷Gn…Dx¦UHe“þ°ù¶úüïðЪëÆgºLáÇm‡±Ûag™ûJEîz:‰ÿôsÀäËÓ)à¹ï0e[UyYm.ˆzzA¢æA˜p‚ãâ§sQôB>19´†n‘÷ÃÙAÙdÎ/<·¥ýÊ`Æî1äÒ©: Küoó­(Œš÷^\/ÃÙ¾§ díG˜' ¦:ú¸=ÌWÈÊN²9i…´»5VÜŠ'ŸçW‘1¯bÐq_³øÉ‰ÃN]Z4¥¦ÌÇÎ>sAî.)Z9Ë™¾k•™;ÉZëýì×–a¦Š=n#ÉLs{²¡ÓNVÃïó€¿¶È ŸJqƒàËÖ[ýš:•Œùuâ}»0cê$̺ã &Oçaò›8Xß Ö!ýcØÌúzrge3»ÿ5›»pëÃØ—ëŇ s§ðžUGáôŸâ`?z3™êã@n¨Ú²¯ -@%÷Ï?ž©B›·ý¼¯-Ư» ÷8v§í†é›5ùÛ7`tF„NÕåvŸ.5ĸ¼Sƒçó ™çqË*0ØVn†ZOQ>mB/ùþ1ûýr. – Úñ°êš*½csUØ1îÅ|t¢ò-Ð{”’Àì“!86 o¦?$ûû¡mu/“JŒ§AYƒhú±XÒBfàÿî›ÃWǹµ0î‘ý|øNs?‰ÿÞl¤ëÏ,½Jœ>pbëî–2“ËÓàÓ k®k^M&úšÑ&‘êY—K/Œ±Ö§@|ÊS\wOŒçH)Ñ!^#ù¸‡nÔ3K‚ÞÛ=††˜8BÑÏÍ`~ù-¾ÚsÝ›>Ù©ßp0¬†°¾ÑE’JÊ,š®)ƒc¿‚xǽ+d’Ù? ñéèpÍ9Ahòmÿ¬¦Eo×=Ýá‡ô.‹žÂP:už=»äi5¿Ld.Ì µ§.âõ2 ª7®ž|¯ÆŠ‰ùñå3•ÑGzBVó_©Àók…ðj÷ €'£xj(åÒJ£@î ƒw¢éj)%>ý_= ¯-‚ÍùGñw^3W³ÄÂgkqê6cŸ ê-âÖiNÅü‰µXÝQÜ0î´8½¬örÆMæ~ ^ppOû¶+‰f/ƒÁ5gÐ>LšÚ8ÒA÷‡‚Æi>jdºôëÆ»÷൓$ôÞ‚òpIãY´ÏÅqØö¾‘lr»ucOºJÍþXÓ²Ì+Pú7³Î›ó±¶h7d/JØì"ñò`Mó ü^bÂÇ]fï´3ÂÓE³°VDßöý–©çÂ'“:rþð-p‰±,™#ö`øÐ^´ˆ|ÅLtÇQ©ß¦N†Œ—cÆóápX.uÎl¼þ—½´—†¹}PÝãA–¼Ò&«Dð†ègaU[#~uÚ ¿½áNô)PH eþÒgaA´ 7ŒÃ©†ð®Y’¬µ‚Ý*2æxÏaë^ç7Ûÿà–äÃå© ŽLÇ—Œé×Û³ñý–½\Lü,lõý†}ì÷ý~MɾÂåÅGaÞŒ½pbÿnöðÄrKÒOèV#Ž}½$qÎuæ–ö&f‚tY+Ù9çÜ­™Ï£sËqâ}L¾|3“,±«îY%ôÆäOƒQt£,J§†AC‹¤̃ó=ûÐP9¿·‡Ý7 õñ1L~!ÉäÇ÷WÛ>L¾mABžÝb `[m7ãØÑLo— ݱ7’So⇗Q­½ _¾:ŠûLÄp·Ä, =ª¬´â±‚upRhC7.'—Þ ï/br2´™•[4hNØ#]=ݪ–àÕÉÛxìÒaïËe˜>þ&8ö¢Ù¿¸ Ê\Ÿú£@úØ2ñ?ƒyNÎ 4¿ ||©ùv£RW3éi{¡KS­Ö»Á›þäûåjXÓkJßͨíÁ¡ƒI¤ÞTþϵúÇ€ïc‚ëÉ U÷\äˆ;w©#IŸk‚þ¡º„4}Én‚e¹¬9 [ҋɶ¯âÔèL4ܾëÀ5÷EðÊcT÷þB´;ºÃD>CÏ?V0eæÚ ¹XJ]>*•–üU¦??eü¾›Qz¤W Ö_j…*Kn'}œ&oIcÍ6C)y’À/8ÉÑc$!¬œk½Êæ1ݽDŸªÑáöÕìpÚiRÞ8ŒOÏ‘åjwWrÑè½Â[CÍy­ݼ!Oí«ñûˆ˜È<ò¨kǃ[Š `5\ÊVH0çԳ®Ÿ1ÀÞÅà~©€“º‘Iz©ðRf¨uá™§‚+‘=0ìB8¿1‘º'“~¾Ârw5®6a)¤œa¬ßg§åep¹ -"÷h lyíƒÌ ™—ëj0!l½¥cEœ„[WÑÈÀͨs¯ß}ùÛÁ-‡åôçênÌ©ÌMä#¨œÛZ”³°œè¥ÜêY:Lº™Œ:‰¶˜P;›ê¸SM‹è$õE8ÒîÓi¹ƒG-øÊ5§1ÌÕk)jø·ÝŸ?wG2$2íV_Åêêœ}õáK¶Üƒ!Ï}áiŒ"|Töv_Á÷%ãp×Íïõwž`yqžwêÀKsÛ}1d—îers!øu<‡¢E;0|² -ÛÛ…¿jUxÄ-.RþÉKÄJ‹Yð³ç9kÓ¶ÓòPsE˜`Û6Hu*"›Ì*˜mJË™w•97°š€X8—‡V˜£ï½ëÂkkªXŽß²S2 ŠŒäþcáÞÏ+Qoü ˜ß5 L:d©Ñšbg¹/2B¾bÙø|yf âvqpjŒ¸¼Žk2¸rƒô\b™ÜÂMC~:|ø·‡{傸I1tª®-t¦±%ë„ú¡b¼p/¶x£ßF½½í_±-b(š- A·qf|o]"ËIÖ¢¹ï÷ð[ btœƒ8öÞ Àk¶ïÉM£²ã£'ªˆkCŒ‘*è i€÷Ën Â!sôÛ?”®ùþ„œ‡ÓU·0õcÛ2æ/^~q¶3“¨ÜÕàÆZé<‰UaäÎü·ö¹± vν/,xêö“ðà­2ݶò üã¿A°8¯¯*ÆzóLëÎw5Ñû¡éîyìª~&>Þƒ’3êak’|{½‚·¨ÐÆÓÿÈ8\ˆ{Å^±^¦¯’ÑcPúŒ+ñ¸| Ce\ñÇŽpÌi:>ôM|ÏÆNŸÄ3k¿’ ¿k¾l„¿NFˆ¯ã‰w¿Œ¿È6ŸÙÁׯ%:vÃ1÷>0±^ aÏ@„]"[tZž¸Uö{|og:'º‘¤…^‡§õi•Rdç~gfÇ›áííøjïÅÏNÓ¢›í[0­2nBæ1U®Qöš1tÖ—UÜ¢í*Ü[Æ¿<ñ ­/¬)˧ey†(eVOg\!ï1ÍÑÂMº$Ä…éµ¼À£5dÄI^>l!¶ïïC¥M³¹ué{ ×™[I¿‰ò OU®8^ŒO{7‡ ª@þ¬/d ûxL„D}ÌK·àÓÏŠ<±X…˜'³UÙÑ8ñà Pó.drÃö4Ìm5çN+N²†U0pB2T¾¥Ê„¿>ƒi>Cø8¹˧ õ"§Ù(Áô‚8\뵚Ÿƒ,/3¬t)"é «±³¹†må » I&È-éÔ]ö/.½„§ÑrøîâeÈRUåË7%óË‹êÙõ߉0åqî‘ñ&žŠ“èá­™àKžàŠ)»á3Jž?sÔ×£ÜÈ©üÄòfOòS”¹c š2†ŠUH<»¢ams9žÄˆÚi8™ÿ›¢ÐgªtUëÿwm0sÙÜdñµÒzXBH<Ù´J wÛU“âÊh¸´vFïy‡'gø‘:ë™\mÓ)”»lDË6d@øƒ¸Ï{ tІ 6˜ËQ¼˜Ý»zÇʉ‚M­+Y >¢£•1²ÁÏTŒÔÜEÔÊÛ+Îs—¤ÇÊ*v{µ¨›å½LìuVÀlÓD0Ø§Š óâ!jé?œÞæÄúôÍÐ2ØS(Åí ¾‘}Ós]¤:$A»ëö”ÙACJ…0ÌcÜ]ø ÷µ×‹ßulö ÜÞ‰ÎêÐX¨…ÎÕ1ÈÇ'¾b@žoðý–zìä¾ë÷aÁ»˜B¾§S«ßÓéÞÆxl»ÝÆ2mºÁ<ÿ Œ¼í 67 áÁ\5¾Qû.:h‚þÓÀýÓ2¼þ)“¼ŽÀÄ@)úàÄ9ö#rMÖÒä®V›pÉmkz|ºø,ºÚz &®ÅYú‰äáµJŒG´nSÆ-É-¿Ô—BQw÷:ú‘?9¶ïÐU¼Fm þø—‡By {nO~â6­ê$Y*¹)1##F·®¨£ìñb¾j–4üZa8–z­ÿFr&ïdÈM€“õÃh­ím¢qa—ûøF°¸î 6mÝÀ5œ†ÓýKÃûÆïäž^;Ù»™£üÊuRrZb ˜»ðm{O€¡×A¢V0Þ,óB{{¸ß³Þ½Â?s³Ö/'©Þ…äðŸzvÖº‚½¸ÝŒÏv×0ûõ¤kš=†:áŠkÆTìq sp£×lºH "+omcê3{Øì-*|ñêyäpðG¦i‘v'  »Ú~çýÂýß ûã)çVí[÷^—ÀÄž|XøÕŽ::Óy†Ø^Àö/€ç__bü>3¶z'üÐzO^/Óã³f¾„Ô…Édoø’p¦, g'¢ùB%‡Ð_Ës™Aä÷U¼‘êÇÓëγ0™ˆÄ wµJcý5}îwt;¯Úé€Ó“$èÐ._¼ü5wÀ”Ýìqý%Ö|£ï]Ƚ2γ3Î ðÍGƒ6íÃ=HiÞAæ6ÙJ›.ò—ý’ÿæ†ÑµW@µÑ€òÑdõS%y“$¼±ŸÍÜÔ*0¤#sÖgFŠE±Ò®GHú^BÎ|u¨Ùð ·¯Åͽ趃Ú|Ãðx0Íöçžé»øákK©·ÆQ¾,b Tí¬…üã+Ê×ñÒ\[>hw8X_Û3/•³+FR<Ê߆ †ó1 éÔ€¿,lÄ®st¨QáÃ$ß·ô¾äÖ.ÕùuFí 3tNàmy{þfôkxv!‚Ë›ùð‰á+¨£A.{}~=_¸³l| 3–˜ÑÉ÷.A‰ÊPî±l ¾y1…X.ÁßG ç6ü–£QC°©ïÙyºÏ¢>žB,>-K{á ­ů^n![†/àþÄ¡¬´)®ï:Ç}€+U«Ð½<èê<À­óý:òv£î­^V8âD©ßÈåŽâ»ÄV¸°7ï†,c>9‡HšT,Ï›é&Õ$É?ýõÏúrõ—Êèq¬äéuØvÇ–Ož`ÊýΞÂ$ÝØÿ^(oMtËz×ÃâË≔Œ?.É.}Pƒ¨7§`]O>Yt2Uj‡.¯o>ÉBåÐjzûœyTN_Ç›GÍIE¤ J¤4ç9wß½î]wïÏw­ï³—F‹ñÄÔ›õ;ùub$ï¿„‘íjD ÿP¸ý•¬óã½þ8¾H¿<Í ›žE€½ˆ,ýC ^QÁŽö&3ìX¦²JWØ¿kå!¼rv8ŸÞè ™kGñ‰äñ¹u4ZNóG&@hC=|iÏ!Û_Û²Ï ¸è£/ÝýÊRØ"y”:õ¬âßVå3¯C°Ã(›31ee¨ÃÖlã†e¡°Å'KéHð'Qh ë„4ÑJ×Y5®³i÷ª(xýu*ôÉã_<Òùû-¤Ôàžð}ÉVÕ›ƒ¾­ùeMEEqùÐ@.×1×4¿ÿ©ÊTIߟï_ìGH« ÷ð-¢{XûÏ{Xrb"—ò“8ÿ·ë àî‹;hrÿ|²1Šþ‰ D·ú±c$<•ÊÝ]I”:Ø,±h–T„ãeNôÅ j¿Æ7õ>Äß“ÜéÏý!°ÃØ[{ý!Á(œ2†FŽ¡‡{‹hHàqòg×¾øÀTn¾@Z ϱ?ÚÛÙø¨ˆ¿ÒÃÊø·€`nóÑ–Éþãñ¬ˆð®ýŸ˜Þu_wý«ñ7$ùIk^rÈ ô‡íæ’Q!”;φкaüò5=ì­¢ì¡0*¯§Hüm¹aÄ-Ü4}6}tL–-Çâ»"l“äm¦|¹˜õkâÓ¿Øð yx?{Úªr±dNß%-£.WÙ«˜g˜µ”zNÎäš_ª¡nW*Ÿn"ÊûÅV“4½)¼ó[!<ñšÇ•›€¿>Š6[ñù‹1sŸj|ë­‰¡w³AÁ{ŒèüNVl6¢'°5JbüƒØvzª3‡Ê–÷±bÛat±õ¼P¼ÍÌ"±Ü; ]¿%W‡S×€cð¢Z†òe½ìSJ`{n}†•[Ë™9Sæj;S¡kúyTô¢JºüçCSÚ²h4#‰ºð¦uÊÛÞ"÷æA«—åpÓ/ŽÞ¶²ÄûnjàµK6Z=ÛGíD×C‚ÞX>_&× {+py{—¯ `59O±uÛBZ¿0¦AÙÙÕø(þÐÁÀøréýV¨÷¿ ‡÷uZ}/ÔCœ±õ„Ø#~TßÊC“Ó|JïÖ7üa, ¿ÜÔ‡®’“#Á/Ęí¹uoŒOÄcr„›_Ô‘ÕÞ¾\î™ã;Hdx8‹ÉÕÅ–ÞV¯5’^½Pöæ&Ç•'°€„ZfÿÇï¹­°-ø ‚KžÓÎAzŽ(²D1º9¨þá»Ç_©KÛ’bíq$CÍŠº¯—áÒß«€¯èÅ…Ÿ#‰wdÜ1n"Ó’R!îÆ¿wdzš­) êþ‹$/´¶= „/c¯BZ–òWç.¯ÆIë‚A>+Æ®v½+\=i°*¿^ÍüÃWq÷Š~ÐJó‚?Öjôá$G,ÒŒä'?VB°]"¯N®þæ³òTÿKgpøpc¸å(ÁÝB à¡›Ûq9›OóZ'Ëo²ß 3@ñp0¬qÄÕ94kA \Yù%¯Ý½ä¹ÙñVX¾¨„ ÏN‚›R™jû (¯ÂGÚ ùY þâ½½³¡óÍxìoKn[ð bÛð€AõˆŸŠ:eya{}ŽB¼"v툆žf9z¿lŸúLd<zCøë«ê8-7-KòooÀ-ïVˆ–úíÆ"þñ¡UÓ†Ñsãô©mÁþjòVèo ¢‘#Îâ[´ðŒç+?*‘ìþJAÐê\ÿj%GÁhnöà÷MÄx^õdºû:´8ÄÌz\ãÊ;“FôZ©öß±íšmÚ0ÔÈ,~vŸúy*ó3Ü¢³6A^u±‘K GÝdòéé˜nô˜¨…Ò¤ªX:lg:?ï}2?A-ä •øùÒTö!»¦þʉpÔ„5˜ú^ LÃð#jðè!˨}ÅljP ¹¦m°É¢²j y@Z f„r—ï¡R¸öÛÔN°þŽjÀ¨ƒwÃÂÑ } §Áô;'`T5@û¬­p&s#¿0d8/?öÂfyr)ŸLòø£!=R|‹îjxÂ<$òþi"cš,T‡Ï®§±£º²Gó)AG0)ØU÷èÒ¹ÝÐÙ#jÕ ð×4e*èҥ걣¸VžÊ?-3ê&HÛÛnU {šƒè¼Ê,tyl{ý—,Úþ”~]ˆ–!—ðËø(øö\Åãóé3é³t㬳\Õì%¶wœ &çÊ =9ó0,KŠŽ°©Ãdw ŽZ×U:ê¦ñÐ1¢ëx.óK¡8vWêHc}#váRS/VøuÝR¡†1©Ûø>ƒ"ôY7 ŽQ×›¿˜0é+N¸ztËæá%V |i޳uÁ;» XÎÀJÜÿæ>>:ú”¤ê„ ìQªsî= og¾È Vð]Âï™®ÀÍ6±ø'Dw:Ðe…ͶßRÌù½™X¼S… ²×A„± ž¨®VÁX7b+3ú3æm:¯öâÞ‡QC1à•5'1ä×ôÏÚR¶™õ?ÜýàÌ…Éü£Ýv(o݃Öí KôwÒkËÒY’{ ÜÝ¢+½0ç† ó4>‡~Ñ/Is¨]æ;<2å;’ñÇ‚R;—+©¢Åß.ž‚>Ýë¨³É \û÷>K„rZú ÓægÕN B—ÈBœW÷=’›¡áO%.ˆ7£åuÇÙ‹JøkÝVr-ìHû…`©Î ÎÄwÃÑøÃ:üióv~Ì>¥Á%ž-*‰iß±Û[UhAí'2Új#ù,óÝþâæ;¢¸Ëª›4Çõàêãš¼¹k+ô,úPo»«·…72ÁÉ'0w¹1:·ÅA®Å Ø¢"Æ'^~ &ù¦Óa¡z-ċŅë)ü^¡“L&Ñîü‘%·ÏpPÄ—Æ]/ÃÈGÞAóé§ ±½Îì#‡3†ð×Gq§*Ô¾¯ˆO‰F~j"Éo7Cå;UÑ+B¶Êı*6m)‘9V ï>+ Ç|Y÷6ãWñ½PwØ ;dÃ裡ãù”ëÇñnÀD|Ùô7÷ýÅêÁ~L>”$ÍâÂÕÆÐzK Í?Jaÿ8ä U³f ûŸ“®ûZ'ƒñœÓPžþmrtëº&|Òv/ʸÒ×±#F!ôÀ”¶ß$\Z+$ŽâÙŠœ«tqÏh~ÊÈäNÎÀîªC|Û‚ 40ø1JϳĦc=‚¦>våc·úà»ä½ð_,‘„*Èû3Šö._ÇžÀ¤®üá §ïÈÁÍË|ãêrhRôä}ûÑ—)©à¿V‹»hðk:7ÁíY0“|ý çjœÜ:5*eÃQÛXrk-¹u×ß×ày‰TºU¢4†Ñ2ôÓVºüéYHž¥Á6ûó‹N’je :ÇÈ a™¹= °änAŠü¢æ1̶y‰ögBª ‹ot\À[^/ÃÅ[hÁùã‚z© üün=¾C¨©Õb¾€fv¤Ó:ÙÜó¥'×VŽ«2x_æxöexCÿœ)˜)jß¹¶#ÖŽk˜?Ç“, ºÏÌæŠÁÂã„ìÙHßû\„EoIà÷íè~_C÷¡SpìÙû}sr˜Ë“Iø÷ZŸv!„ŽZÐ…k|¶ bü]HŸí‹‹úfój?s:Ê!™Úß©§É]Åà]øXxe[7Z gÿÛÿ$̉ï‚S^#ù†ß1¼8/„¬þe£Ƅ3ûƒî²Mžn¨YNÂê }š(ÕýzÞWûR³¥é¨•aÇ£ž?'q1·ò /è— *¡’h\9‰Úl<Ο‰r¥©VüÏFáT9Ñ…¸\4ˆŠ‚uܰ|7YÑDÙBgÜÒÁYŸ5_cç©Gª£Ý§U¼a«š}æyÞvÌMððÅþ$-œ^vÃƒå§øçS2|xÒ4wÂÇ[Ìâ[–®¦ÇjòSMùµéZü§ÏaØ/ìaa†ðäš$+[šgÉÞÇ#¹N0,Á›VßèÆãRp:¹Œªjô@õI ú¢f·šµ"…¹à÷ó>©Ç s»`)ág<ÖâZ£ÍÔ÷ìZ¹ÖŠ­êÛHóeQílh…ì©Ë¸±Œ×9,‡ÎOñ~b\£¹xoÅt.{%ÞÕ.AùíqöxCç/Mˆ¡ª§Cè…[L±¢¸K>¼µ´¥—]¥È…N>ª?Ù„ƒ/qñ#¾µË xfÀ*¥hX¬U^ï5jܘ¹î…•£Ï]zùÀt~u‡ ˜ðD<²êYõà9=±ˆ\¢†WÂ¸Ì úGЯOýu*Ü£t N m‚©vâ¼~ñ,s<Fi"ü!tÿ(]~³Üœ®ò›&,%Ù»Åx̶ ßOc~¼o˜À7|N…%›Äù“£ºø_¬WËWÃpÙc˜h“ƒYSÒÈÄ`2Åt%É«Èåågãù‰’|\V*üù,ÆåÞJ%âŠçëñŒî4‘¾Œ…ʉtŸG:Ù1°?~=…ƒ‰ñr$± qà˜~¼–@ïˆ Rt¡¯n—“#t¾ /îCfÜnWàÈ_Œ1ÅuÇð¿GBlàÉ@›×hçÔÃ]>é—7u=‹!×°æk" Šâ5š túpª0éÞËçϧëPéÌIäÚ¥H™NƒË«ï {Qœ¿x>…=¼>›PÜŒFÖµøâùYL¼f£ö…`ö%1ºî¤ý-µ?ÍšÌß6?#JŽ© gÚjE›Ð¼±™®^Ø:=põf¢ðÝŽqÕ ûùIûhz²WÞË©%ßòN7C6›"xÍêdè¾qÛVñçÔ;¥ÉóL)-úUµ¼ãf¡QÊöta‰tX@ò¦,æ¡mh„YT)ÒŽ¤Õ83}$o߸Ÿ~¸/Ãå&±w<|ãwŒþt%öòÓþ©|¤â689œ«YÍGu«wàx. 2ö¡ãÁ3\ntw¼î ~<‚÷„YñçOaŽjýj8‘^Ý ‡^ú•X~h¥'½=8E¢—Í5•«@e¿„GñÍô­ÇnìTQ¢~¹ðiëh˜ÁTª9ò±þ>á„#™ŽÐžîäÛýŸ‚Mƒ!u‚íÍ6rááRº-´öË¡'Ç«ò£zÞt݃*ˆüRJª…‚ðía¸«­‚çÜq³#CÉ Ì_ü„Tü’ ôÛSV¹ø)«/…¾R‡àIðf²ÔJ…^‘§—µØ·áøeI14zzС·Gð'ÙIè5ã®ðVU:¼_{߉Ÿ•ƒçØ–ÆR39ž× ÇU‹[WŽwBqJv]¼ ¹Ïâ &¶FœÜIó6Á®ç£Ø|W¯Ï/äÇûÀÏ-ç òôìÁb«¦@ŠG¯$.ä—Ìàp¹)½Ð­>Á«ÈâÎÓ$¥ñˆšʼnÚ0ñfÚÖ: +ßÛ`Ø×¹¸ê@LûGô»ù âÔ½àì¤{hÿ9—8H' o¥„ò'™$çfŠö—Õ{^i ®õ8B#{¦žCK½0¡Èôåx¡ü07²ÀÃï¤ÙïòÕÜû›j(á°¥ˆji;v…åBö¸&,Ë&†ì .oüÅ’F¿BÇ53pj¾ÔöÁ;\XÿvÄ(ê÷n8 ¼0‘·%«Á¼!ó̓g‰i³‰ ÷™ žVæÐ ï:¸Æ­q<#{“þ«Ý¢¡öAÛ›‰7'õ Äé¨MÝû_LAÕ¼å´lu´„Réîõo§À3þÚ¢GÒNX€äÈïpÅ­„š)¸À™ô(ŸðO÷t½­JŒ¾™Š5sñörufS#G§Èû@ÁªxÁ] ÷ÊÇO ¢ÑøàÀ <(ŸE€óp-˜»p ^ù¥K3¸ð¡—§±ò—.ü£”M ¹[¿‚î–~ÌÉ<í¨„§qñ¸gõZ‘ŸÀ®v%bì é]ßW\'cò>³zù^ÈõS¤òò„ §O”|Téù»¾pV(Aw݆֫éä¨9¥uÓvà ¥L,ˆ Æa>‹ÿó°=wíc ±Up/Üœ¶ÍH¨ï,Mf¯êñú+¡ð²¤5˜¯ý‚§6x“²Ý×Î’´GŒ»“lkX.ÚÁzã'¸þY—[^É6-@ß'b´ þkZøÏu‡ë/pšó²+°Ieœxrçg¹³ë{ ¡äÓ+²»Æç_Ź6£ ø j?¯Ø ¹"¼`_+³¬o„<§ ìHËI"Ñ} ŠÄw»Ý¹÷eAo*® ='îóu$´5O€bƒÙ|¯ò ˆkCviñ©Öa¤À×”þ—ï² >…R¦†õ¯k™V¼ Ÿõ8GÛ‹ò>Ùm|·ŸÍ2ÂKÏ1 HŒ¤{=ÊÙŸ•,ô­"„‡»²ƒU_ñ^À1:6T‚vìsà U:”>·bðuR ì?±šá ¶¡yJ÷Ð÷\ ݱo”8$òg}u¸QÅœËçc¢–ðzS°yu€Ÿnµç¾6“‹FïøÒ¬3<3d]°¡žÜÊâãßmâ÷Ìà·È0ËI¡?ofÑaîÖ`˜üoßÈúº™Ë´aÌÜ<¾õ}Ê„êCu×+Ù5‚޹ƒîyE´ñêX?˜‹uIêxv‚5ÍË,CAÁ<©q–8ß*"«F±Ìlc*µç-žNøŽÃv 5)ÛÄçì:£7&¡~«hµ:¡Ï*/>÷ÐB˜¡œW¤¥aœL'üQ g…–°úoW½ì>Mº±¼MÌ’éÒì™<ݵ5àºÚŒ “cøê‡=íV Ä܉cãÖQ£môŒÓ,ˆÊ[ÀÆQ›dº^¦tñ—v|vlOÊGšÌçaê0iÜ]ý¾çFàònCªViÌgµÃøt¦_F4§;wÜJ“Tʵqã!Vú9®ÿ1†ÿ–ZƒK‹­øÐ›!u—I1Mº†?[á£ÇPRl1™®²  Ö¡…þ"|=AšK<ÖjºS5ê2ƇÜ}9—;Xhò¿ƒÁÂZQkH±àžÇLð Y$As0<&×X¨“¯¦éBWŸ2# ŽIǘ3—Õƒ[ŠÊl T>Á»>{W{ÉŒàìB-¾r8xÂJT:±r¿Õ¬sa™«pß­½|aßjöÓ}:>pUàŠCQ«\:¢ï Œ¤§QbÎÝw¾‚"~Ú˦±3Ëܱ;ᓚäOÎ…%?‚Øb¥…Pê¥ y{‡ÐØujzéð¾¦Ö-ÈÀï¹Ô.Hg•Ýîý®Ä{é*®íoÈ#®ÕC`g6œÜ:öîúf⩼¾î« @ˆ]¿‰_Œí‡}êüÍ.bô —OXˆÙ&%‚‚ øÖ¥G$y©Tã±ýîûŸå ’Ê;l3µ}×­Ð+æNËÝ髉]¢4ñw!¿ù¸|å"¨hñYá¥#=ìãœv\5g#~ðžK-þqiÛŒ>&s&\‚*Ù³M-ÐlŠ·j® Óç$€õ#Y`âØô§rÔWÿjˆ_Ÿ483Þž‘ã+ÝÀoÒg¸Ìçj8³á :ÏÁoºZüã¾ÑÔY)ÝT½ù¨YùÄçÙLÌO(††GVp\†>XwoÞYÄöüÉÀø½—ázÔrZº·Û& …òì^ƒôÂXÞ‘’€1§šàÁÊ`"¬Í¥¢2…{î$`–wlYÚ1a%µnc:ëo¯ºŠ÷7óçõÍÄxq0òQê` -K ±¡ß6FâèŽZ®¥[N±w2¿1:ˆÃ¤¯ñÓ=à÷æ‚—möíÁ/¼ ,ï¦W6a@‹—Š[¿^çã{ÕÛx¿4:³áó¢ÉlË» }èõÙîtƒò¿ì#?@•sÆtø#âë”Ì®ÎÙ@vÉã~¯ÒG;SþXÇ‚üõ¸b­­°cêEa§în°Im‡ý¢“i´Ã(Ì5ŠG… "/8D¬o¸CxBÛi6™ª2+h[Á{ÒžáNÑ üÔX}îÕS…‡gê™U±0p¼•›OfÞYL•D’ˆ‚ë ¢{úJެÁÛ¥a|©p<·N’~múˆns+à­A™{ì y´’]Sê'ÎRã*PóΊTá+|²á£Ô\êÖec·é¯.¦)ØÀÅàf6-X‘†Ü î)8‘-£¼q4–õâ$§ÃàºÍvœ …ži7ð×û"^³:ú}1vª6“£³? “T&VaSBÔŽ€ÔsËaÄÛŒYo ß4ôÈ´GQìâsíjHŒµ6ôë½ç¸(â5Ls©º¬F¼±/S¸Ï+ÚÓO#™2'; ½ ñ`O¸ýÓ±tÉ[{Üý\‡Ê;cO÷£”X6k¿°¿O¶ñ¾AÔòÒç¨qFøúá*žÊ…é›}`v©_}rºð±odýâ ¢|FŠ?•[¯%º’ 'Fû,]áеXÖu õÔnqx€§Âí4VÕœ—øñ¯&çí’Ö¨P’DšC¦â=›Õ•§ÐT§%ÒñT"Çâš‘¼þrPçëuЖŸ -{H–Ø,ò7oíƒPÒŸCfÖp™þ¡4Ìô!™§×%¼—w€½s ÃËgÆãlÃi˜v£Lý§€Æ&]ºèÞ\>tá!pŽœÉô}40|Å ;*aÎëϾ%LÆ’É~^ÏÖå,ÂxA6 ˆ‹`]â{á¾»“±èw .(z‰ðú$,}½:íé·-rÔ°º?sä[a¤ú=<vhø•¢ÊíXt[ãÍ®lŠÃü¤9ðø7~¬Ÿ¯¢ôä¬ý˜GþÅ!¸ÄòzKñÙµÕ­#ñ§W4NÇw©¯Ðdõ˜µ~tÄμ½žÿÇñ_î ÛÒÙ`8" õ1ëȧ:óÔVö¥zz'¡{Î:N-žokM¬õÞŒÁ‡’ÓjkY¼Á&•îyW°äl:]tÆóNüÛ´¤ßó¹8ŠïmÕ £J,ÞNÄ³Ë (ÿc eàBª!D.³¦ ž"~Rôú««Ì§G‹m½†*B¦ñЈˆo«ÂÞÙËà·EžÝÜF…âÄQg`lÁd¢»ZŠÝ4‚xÕIæ‚67Ø&‹¯à[¹J˜æÚ!˜¢ÜÉÞTjã¼=í$tRÜšˆª£uè¨6iž4ë(I½í…Q;Çð@qÕ„ü¸¢HW8c¡ë$Hc÷B¢àkA<¬Ð²û×­0d¸6ýà "‘p?q.­«räßû6ƒxÜWðõø¾d6¨*ö—°ôs$4U=JKôÕY¤ßmvJ77Ú³—iSaŸ‚<—|QÊîK–CƒW*Júlôç¼Ä™š|·–=×úY&7á_nÞšÉòµà’î5/ßã´ƒŸÙZë³lÝy#þºx”áâ b|—A»h+áp}­C±þ_ðÕñ¦|ÜŒ¿Gn‚e_^²òšà/ÌáųÕh¿š wÿÖƒ^Óª æØEöhßs ÛãâÿÕ£þåàfØK\½ÎÂÁ)i¡O"átG ^ð<ƒa_‹Hœð*É{qY £T Gzàðìt4 ô‡¡OdHU‘ sê@Ï!»°¯]‘)±ã¥g{ îê]°ë݇"7°÷ã‡Ði;7ÂT nyç½/ÇË÷ß^Nud-ó>b…²!6¼Ebö‡âI$¶¨„̺’Šßú_£ÂüÝðCy*ØsÎIS›¢cxzûDêà¨ÂÞs×à*Œx:š*u…²…rE`]¼™URå r–¤ó“5tõô¢Rm?„„ËÂàÔÃ|ãY_t2="]¦„£þT¢lí'\·Ô‰Ô$ð/—eÙæ¥7@Én@ócvj[vnJ³“Óå›âÁë<fê *üVb0–k~" 4¿“Ïÿžå¤æFQŒ°ýÐÖƒOÊÏòfS§l#ù>ÿî»oQkˆ`%úá‰× ü{hÖjeVóø„{V°†˜ð3ÛÁþí)¼~,Ž(ºäc¹ÎDºõõ!|4øVÏQ£óª“s{æð ’ê¥Á]ãÌØíÊLí\Ìž\Y‰’wB¡)¤ÎŒ–¦yd³šéíAŒ,›‚")Mu³ßn#ê_ZPE/.œÛ{>³²þìèøPÚÿI†¿Þ'Ï=²*`„ µÓZS†ÞR0ÝRŸTe²Ë ãñV°Uþp~-ú ;‚Ä©I‡OÌÎ3×§,×–Ò4ÅiPõo”Jóu׿±ÍS6aíÛ\;k-dŸÅ1ŸÀ=ôk}îʼn­'ãXMEjuÇ[`¸ EÐ ËòÅaÔv/¼Q1„ â@iòW¸~•¢¾f"vú˜Øí‘[$x¹ÓŠJGü"#bgTÌ^}îû4³…/Eøá£â4Jn OöŽC±wœ[’`NÅ ˜:Ý€_Ù¨€õÖñÉ7nöªŠÏŽGç{;ð?λAã—:?e¬ÄW¼Hâ%ãÀÝ 5ôÙp>ÅøÖ™¸\½„ìMz*w†A`zißgïr7 ³óž²þ‘év»’a[øºôúc¶={ìÿöWÉ´’ª|-šåàŒÃ×þÆjí2–xè'&”K¢s‹ÉÆ6ւͤ¼úä+„Îí‡T”ª, ä B™*N]÷_VÙLVmµà"sZÉý $[͉Q¡9•î ËÞƒ½Ù ¼ïõs<·º pø7²ÛõšàÏËxö¨J…(ýyˆ3·8 négýî«À!Á– ¶ßÖ”¢ž ú¼¨»v ¥ÌÁWþ3G–V®ÿ+¸¨”þûOØY%Ò8ï5šæ‹R]ûqü°½,¿¶¤Ÿ¤ s°¤¹‡X)ñcóƒ°­ ßoŒoµ£šéð»s‘=ö^Í'ÔõâE#ÔÓÀ î˜«R›^‚Ôv†ÕÓ1Ç^±ì5»ßêB/H¼A‹ãðô’ö¶eÔL”¦cßï“·§“º ··µ¸ÓZ÷ ‘¿.9˜ök#òóÇAóÈ ìþ‰¼±\5mqň `õ0› FY wîÔáêð’Dß}†ïTGÒ[…²ìôRMzÓQnë_ÁkWS0ŠXòŸ³ã…Z†láÜp¹ŽÕÛ²cÙD±J;;LcšM}Ä£s¿(¢†çN¥Xó èÝ5ŸE‚íŸÑ¿ã½UBbû8¼äÝ``ýpè™Ç?:ÒoÂNغb÷ù2›Íî[‡ÂKÈìלz‹˜j•5 ÌÇ0.H^Áu·Þ³K øg¦_»±¦m˜„ß>Ôx'J'5H GøWöù¯1yš8w~ãÄæw¦ŠÍGóÊ ãí…«1üà â”ÈÈ´^(Jû#\qT“nºsªgbi—,j.Q¹0˜üjD¹‡Æäfýo‹ íÚdúX©´MRÇîS-0Aö< ß§Éÿ³Î¬ t±É ´ö§—GCh‰Ò1úíîTØùΟfüôfzûf*š6 '±ûóaŒÈ7Xº© zq,‹ÊeËF²ÕÝål‚ø,&E]ùóû`9m> ˜;œô¤¢ÑŽ,É,æIï‚MßúáOú ~Ï`5XvÜ" ŽÐ"a;H’ØOOEz®Užýz’Ç¿;✮¤sAýÇm»!·oÃŽ b´|s¼0|åýÓ—g¨€ú`(}:ô.Ëòï¬*ô‚«uW=õ­IÊôûª t¢øKüŽû;Æ¡ùŒq¼øÄ XÊ*`×ò|ì:]ħ=øFfQqð“]Fï^0äzÛmùÓéÜ)Ï™µM˜J$G¾p»b¦çxn…5¡­ùhÖýÇ®”Ê¢X½6ï+‡Vÿ‹ÌKj Œ{q TTÑ"€¼ÓÏu=ß“üyÖ°&m5vX½Ä8þTжq dõ„cIämX5ëZ¸š“O•Øg±›ðta?ùÞ™V=MR¼ÇBŠÕvÞ_«gEÿ²g ƒ¬W:’Kýþ†©OàzCö€œvé%Ê'°¬~)ÞÔ‘ŠI|wY& W£-G`Ä$q¢û ~F4܉š+X˜ßºçã`žúMðš Â%ÃÔyù®ldb$.ä%LÏ| p—ü Åý÷&ÒÅsç`_`—öpäoùpßcäZ`™`{Ù(ÙöÇ´Þ%ñå¾pcíR>Ø,Éý+³‰4UuiÅÛŠ|©ó X÷ÔŒ¦,6ƒ…¯¯AWy¾DNh=²‡o‚ '0uóIøqh@ètÙ NŸ¹E÷EÉDZù,~ë)ìi¾ÏÄBy¦c¦ a7}–ÙÍ–ájŠWÁgîXzóÇØ3Î]0êË î0 Coý7ÛÁ‡NB}£ŒøÓ;ɼ>y&/íó†ª3‰Ô°ô^ÿžÌÏ¿2¤Ê+¨é†CüØï¡0á[3zïJ¤2=Ñ4ÿë\NI!…Ûz\çæ)\’»Š´õLdgß¾b#×.âÊò'YMF(Û3‡‹ @$uö?5¡ûd¨Í6m¾}A®ß£ 7 2¸ó˜W¶"|A—2·Ùtˆ¨%`_P¿êNÎÉ ñÀ­ŸhW‰ã–ܦ/4¾ y _:;Tm§Ñõ']øôÚ¸·~ ~ÖŸÅæ*ÐMj²‚â1c¡üÀ?Þgž?IœûŸË†©³=ȨÈÌú¥8?f1•ø­´…Y‰ð=Dœ|q ]Y$”„Kñ t*ظgĈÙ®Êß’86¥bw˜†‰±¹Ì]l-`Ï$Ôíöâ½?ïáÏï}¸œsÁ'þcêh^—+ÁwŸ}šÓ¾ÁÈ\hò£ž=ÂHÉ—ƒ7ˆªžË~„üIVX2'£¿+ñµ¾»é‚áÆÐÎwÀï°ß•‚2ß#1]2 ‡Îöá7²ž‘ÃëOØN.‘ü^ÇØžŽ[Pc6~ãAE+wõ`ಥÜÿ€l£¼6Ý€%¯*!wƒ4pNq©c£ Û}(¾Hø$L˜Ó‰Ò×PüÔºv“­ç ý8cÏ÷‡°Iöl<0ÖŠÃûØ9ð6 —/}E”½å¨¿ÎL¾Fb(¯‚ûæ %‹Nþ"3ÆÉQwói4Û8hLgº%õùæfñæÿ-„…ëLؽË3qàÕyô[š+¼hý]zÞÎ6 Wh¸0ÞÖÞpwK4,ë{ÏÚ%WbÁñ)pïüQLóZŒC1¦U Ì–;u[Hð7w'cƒ¯z¥+P·«×É•=ÎÛ…UÐ6*6U.á— §ñǯŸÂ/Vè]~Ü¿(ñª¿%hQ¤n²‡ë6ãåði =[ƒ}èeFµ×¡g«÷JѤ=}qÃ¥áÔ"ç,Îs/‡àö ²"` ÌÝ›ŒÑ! Ø·‹¬ôÀ!Uœ:«iã_ûÕ¿8o—ÁèÁ Jèd´‡N_{Šð'žÏá̓C¸¢ ŸLqdk®Ç¾Îüô™áü„f!iKõ‚ôžðíþj6çùX(Ó[NŠ¹Â†f_Tìó òÕ¸áj%L´¬À°oò¸Ë|>|ЦM¡C#â¨Â~on¹y W\;œë=JÀ)›^[l.Ù0j%Š­÷Çê[‰øO‡O¼hæU³1GŽ€Úµ3l²ÈlÌR¡¥Ÿ¤¹¤ÔL˜°Îšïµx‹ÙÈ×ýÀŒÊ—¨f½“Ïs?#ÿÕ©Çû añTqxU´Ž­lºÄåLfb»'óu)ï0d¸7 Ù»ï;þ®‹‹2¥Â…¿eàTÊ|èR Ý3[™ÏµA\¬îIÚÇÌçêfÀì.ÁÓÊœu r[“„ô“°ý>¼c½+mIpåÚ^Ft§ã—&&Sëiz÷­1µ5Ú‚#”Š1Ë)‘©5ÀÖ–]8#/ß픦Ktër%^yf.úÔ 5txn¾Ë¾Ùƒû°©øÊá<93!K {fÚ¾)GuYgÚo7b´é&ë-ÂC .ì]Ho„SÛepZo+>ÏÒE÷)ø<ð ¦éa‰ËaµE.Ñ<û)òC0åI<©ÈŒÁ¿Tè«îcpây:Š+†p Býö*Q› ì«´¿5J”N>øÞN¬^’6¥ÃÞ§Ãävü‰@~/ïÆ% ¿‚²žd<±oV$d£Ÿ¿>ÑîÖ¼õ¥/ûÖC‡&WÞÁ¸Nyª:ö±&CAÙ…¿”ÍÍ\…4­£ØÐ–M÷Ë?Äá3Þcí—rÜ¿&†¿”KèM¾Ž3Æh“7Oâñ¸¿¸ü¹)eŸ¡Kíi2‰D©îÝàð¦˜xM^@ "ÙÆC$ð‚>åVBW¹*~ØIø‚K4J«×u3³ˆÏ ð3`o&ý€•ÛÍùË¥…ø·t$üMÐçîú}¥&™f|”[¼9Ém£Ïàƒè:Ü»Yt’ÞIÙÿ¾|;qÅ^Ú· }rDì‹òƒhÔ+.æàDÛTNÀ2•u4»r/xäß}QN"pMÈ»/fCêƒt*ú ßg$cîÈÇlùÈ ,=ß ïÇ\ìoCmìŽÿ•‰ÅÖËhÕGÔ©—ÁÄ$‡-6(F‘>ü¸wÙî!Ê¿”†§ó¨ê4 RÔÊØÊMTÿüd¬MÖäNU™øØ¤kæ_ï4ÄuæH©ñy YøSBå©-8YJ&ïÔ¡rÇïÃEëLФJ|¹žo?¢OG ¤³t'ów«!pÍ–69¼õ6Òã^+Ið–8Æ µ0vèhßZJÄúØËVmJågÙòËa°ë@,Ó8Ž“oƒN’9O‘"¯zaî0Y˜v Ydü¸ŒXµ›~4kjÒ7û­øìÓA°Zí(ŠŒIÆ/Å#éŸßj]¹NëÀÓ¦=h°Ë›êMfK$šõÜ㜾ÿXCœaº¶Oô“áógññSÉ¥¿ãùÌ^L‹Öä÷âJàiÙaÔ™0:Ÿ«²ÁÁ)ø[`Ch*ötž ³ô¸”ƒ$‡¿ÂŒ¦DZk*z?(³É·8Z’[ÓÂâÛþô@;L¹ ÂÄ\ÂpzßU˜ÔúŠ…<· ð˜bW§¡û…_¤ìœ*¿°äÞŠríi¨vYožÃäyÍìgò+r¸Çš/53Å =1 ØúñôÉÑ‘|Eá&ÁVmÞr: ¶¬k‚+9ʼ~K`××v¼Õw;tÀ‚–aOeMþZó3ÛÚŽÿò¶m6Ì÷íC÷¡Æ¬Iä̉ªξ/ÆÉ?®hDùV_XWqŽÉù70ó¿L"A’®4NåW[VóQ¿‚éï|û`Úu¾måNåZ\Mo2÷˜W pÀ€za? _ÁÚÆŒ/åá!20­Aƒ3/kz^^‚ÒÙŽÔÉûÇ…Çá—|]AR¹Â= 8þö<¨]Ësžð¿–dÓ_w®».…Þ0UÇIy!Äyˆ¬mÖç.?ĸgà lš=…oØmF BÝùãy"TÓ~(}’£ nµvØ+}”ÆÇŽ¢›ÎUpñGèžêc\º>^ˆ!7»>³yÇ÷Rq¯ïxWk7À¬Ã¸oú\Ä/ã$ž®àUcjhNÕi“ä³±/2iÚñ8®Ü¸ÿZÄÍ T ­q),$Æžï1'1FÐyå'}uZ”­D—ëí ‰[ðüWÚR–µó•!ã^5ÕéÊ+Õ7¸ôë,ª?ÜœG¬Sçkvþ×O…Wײ­Q®|uõò(éúˆVâ£91ü¹Møºk4¾³¡O-x^¨-:P-Öü·Ù ø6|(_`°!ç\ÜÿGUò¥(êÞbA¿N!ògžkоϯ*«òŒ_¥Ðôx<¾žê‘3ŠÀ©ßœ¯ßñ.JˆÉïõhàôß+Ä«‘r()+ØÛr¸ÍµãŸ,†°†Ú«Ø"ÌŒ÷KêïÛ•gÐÞK’N ÷ÞX„3‡­·;0S*ù,¡K.Þ$W‹Fb¢ôJpõïEÁò|¦^0Œ.ëêÀ¯UÐYÄ æ©ð¨?Ñvk|í\ zóËÈñ˜^ôNÝJjîaÎî¹DѸ 4¡o,…îÞR680vêÒ#Œù’ßš<â-œóY๳œMye@· oňȱf¬|? »½“Ù°êôôU)T?£… Ò:0ó²4 MïL¤¾Ï¯Âµ«Æ¸e¥/Ù4tõÒÒf2¯Ü¹­ÍF˜÷l.,sû"Šçñ¶ñKXqô.Ž];Y¨Æž}ÃÜÅŽòyж¨ŸäRás·ä¿üþL¨†sÎõpÄõzßüÄžÇýãùŒ,¢{ý;»§Û‚_2Ò¹ìþ_ð8x^þs ‹9ÃÌõi¨!’ ÐjÑ0ï!t±}(uÍnÁºÓàøþ• ·¾ ¾Ìýͦ#ášy4<®Pkavm ‹R£•+rIíÎ7x§æŠÉÌåCþ¾ì4[Ìw>H Û,°Ï²‰ŽsQæçö܇)o³…LJõ€G@§]Àô‰ÜMN²Ý–Ò$; Ú/—.Ð8k$üfÔÂ’ìèÄÛgp¨r)^]#ÊÿÄtÛ9õÍ%ÛöÚó„ÁjÞ¾s/[âLËÆïÄßC·ÁùD~ .&ΘÀšB,y¿[¼ÚßÂë~@¿ÑþýÐLšsV›W—ã×r‡Ò]KãØ-kq¯îðÝ^Âñ|Œnÿ>d×Wp£WC/Òò$òcÔÞ]½.žÒêwvôÛ| ªçxÖ6wO |¯ç´êýuÒ¾h>˜}½Ig®½ \=v-}—¡Å×f†ƒËž³|³ÒrMñ |³Çç2~8Ä­’·ÝJA·ÝíèfçCßýcÈÃï¢8zŸÃ5âüéÂ+8<à,n‰Â¤Ù(Y o͸GƒX$ˆÂ ówП€…ƒ|ÏŠ)Ôé–µOzÀÌUKàÄï_ðw]7Ø'¢_‹ßbkBÑ9 ú%®Ã.',µøqÚüRñ­Hêг»|5 ØÉÝÂUËãÞ'1ôæêýü˜ðBÃQèòÇð›Ú,4f=|Œõ⇠Úý5’ï0ùåðöB%‘»ûUX²$ïo7¡1û5Èó¿WP¦0]X—vþ¿ë³ÜgЏì¥Êx½bq½+`šÃE¶†‹À”·Ø))²ûõ9̸¦M‰nÏmø`^|Nù 3^KñÎ-%T=PŽn|/Az…AÐw¼>i_÷ÄÃ0?òXLÎ@‘õZ¥–Î*ÌÑ$V‘¿›,<­Lɯ™‡q¿ ð¥2ÙdÕÔzòú“-u-ÜjCÍYGÏD0J縻 ëÍ¢aæ°n¦³·’Ý×0Ä[*ò¨ÄMü²T,ï(ÿNVnÙÎÖ­‹€}Ê+põ«(ŒTnÃñç,èPio:Yýœ0ԡǹ#¯ .߀<'S,^‹‚+ó¨Ù—yðëåc;»–|ßôtÈtÕ¢¿wm¦Ù.ÆÌj_ 'ÞÔU»€¿ÙG_Òphšoûž¯<œÒi=ãäRæ³JÜsª €ÃM7WaËæ1ôléeO•©}øO¶ïüBP03?º÷M°ðñè&ôÚðÓ>XâÏ»Ý0Tñ¸pÇû|\Т-½î ä„OKò„cú°èùò qñÙ4šºgÊp“s?ÿóL“ŒNIziQ5“5ª‚wb ¿MŠ×6…àÒà.ˆ:¹" *À®÷DYÞ·ç£é¿Øõ“\mÀ 5&M˜Ml²?®!zð4óܪ+!Û+Ï¢$?l;Ð(‹ÿò‚yKqÕÖÿ²…}{v†(Îîb¯úÈ’"Oæ²1L¸#ÐÌ$ãòÔøÏg|±Â˜QÞ®r«Æ³§ôa½Ÿ$Žœ1”¾ø  ,ô§@oå˜ûG…ßÇۨŇ‹"ãÛÞ‚Ït?LÚ'¯ë] ÌBš+¿ ‡ë¸Ètù~¬ËÄ:)j}KMçbhª–Ý- ƒ]_ˆÅæq|þôb(;ñ¶\s¥ú_‡¢|—7Þ_]# r§ÚWD1 ¾žy>Ä-‡ù“ ^tý0‹uÞã'écý8©ë^ÂK±kpJÑÞ{ŠÖ}2Ü(è:J$…7wDm•Jgñ@«mxöz³À$ã/¾| •í0êÊEòxFœÙ¨Ê'wÍ¡N9ùxF+›#f[ÜéóÓiŒ”ݙ،åF+1ÈùwhsŸÛwðÑ '¾æÓxž¼²¿/×¢÷nL…ä8LWAòA»—$>6ÆÆ=±Ü{ÔFg#Œ;Ew†“¯sž }ô¸uY üWó.ï¹Ë)u=‘E<ŽáúÁÑhq†tï>€ÿrÓAþâ´fˆ¢d%½ç2‚˜¨eß7¯æÓcbø©óv0) vò´Œ˜¢ÛÅ{LD‡ËHÙ÷vÎÜ”fßÈ„¶™üNK+~ÿºhŸ©†ñ*¢ =}&^*.†lc|ŸÏ¿O`÷ž°=£ ^;ò¦˜µôëzj–Ü(¤f¸Ð}>¶]þË4]ÇËoÖs›ubüVØnzbÉjèô+Ec«‰¸Óú Ízv ã+›`̵2öý­5-œ— OUùOy`®RŒãÖ£[ÓNã@áU¸²±~§owï>mÎÖ'B8¹ŽYø)ãÔeúðN÷äÃ~(³ñ2£P;1èÁdZ4?eCåøõÄÛò:ïô¥µÙ­YÕ‚+¬é3Í0zÞA »ÛCxja¯@µw#f©÷â¡å"\§µ‘|êÒÞ¹Éxs¶$t=WŠmñT{>þù6ŠçÓAZT0Œµ„ûg ™D˘·ð05ë¸fÝ!ð[v-þ,À™s°æé^|©ù¯6%À"õ#ÐçIDädù÷ÀzX–Æa›å8ú¢øh$“ï¹¼¾q‰´²u©Sê ìK;ˆçnï'OÖm ‚]z$6ÉÄÎMf'?¥¢ô7hkTÀk3Ù+2¤©»–D?’à ú£[Õ~²¡"#XïdÄ6i0jY*ôÔ†À£Ðôú̉ZääÍDæ¢/E¯.ʧÝntíÁ,;ÞU…¯GV1¾I†.û[. oÜØ‹^îm,sD49¨aˆF±Ú¸è„•}a[Ò^aÂLJÐ2C ¿¯ˆ&Þ.£×N"C[þóEÃëKGùbÃ2Lùì{ÅÄ©ûÙJxÖd×è\úètpñ aÁ%¢¨ê«Ð#Š+aËÂ(0pTå¢ãFp1­U¨˜uüÖëòSuAmx¾NÚ[@‡o§ôÈÃòÚx¸“&îC/aróJaëµ\öÛþ<¹=F–ÎÎOµ›ýHðÊ›`áâÇòœÅøøX?vÁôÌ£ÀL7—, Gºðe{^Øeëö“Ç™%¬¢U‰¾ŒC_Ä(ú*®“í-«%Ë›Uiùs%0h7A(ïö'î‰Ïªš`ßyVÕŽãsFk³¹;æà~Ë2L˜¸ #C÷ÐÛÂt’ÓaLÑ: cÕÿà¬ÖkÌ=%^ûòhÅ"< ”³¦l¶Gà‘¡Ùeп¥éþöóö;POÀ9³-°-ñù=þþ¥ã]>“,ë»è>AÎW9ñ"TÚñ®pm¿%]̻ۜŒÁcy4”më &ŸÛѹ!N'©•Åuð\PŠ7s3pñ¹Z\Vº˜ø^`לüpnMܲ,Ä¿¤ Ü Ô14è7f‚æg[~if/¹"¥ÆGªo§–î@b– úXÂ7ŒPÅ]¯ ìþia“|>8™_÷Àö(†5@ùœç=ø–d®è‚ÎgŽ v£Vÿv2o"Íãb^acr1„x'á缕‰u¿>àÅÉ·Iò Qœñ^¨rJ—¿_ÑŒª-J—R‹á‰7d¸ZªÐib3Œý4åÿþæKŽË˜|ÙSœ¾ƒmŸ8:žHÄÔ¨"°Is€&¥w8vø&¢ŽRÔ¸‚á+»A©ð³F‡Nyª‰ÓnÇîÃÇžŽ'P­L…ïþý¯´âÇ£Ö(k2-;`Ê«É\KTžÔøˆý}‘­ëOg`˜´.½,É—×{~[”æ [‹sMà'^Ëñ{GAÁå&h—™Lö‡fÁ¾¶(ÜW Ç.„á|Sª8¢“ °bs=žÔsFÏö±U‰+7Žà9]å¤×ƒK{0¨G r3ò ã˜,n´ÜÝxQÍ ø Ä䟰qÉ3Üèaýß>´ÍÔ¢§·áǽLKnŸnt_êokû <8²x4DÔþ룖^0å_‹Ý; ÔÛ@[k"ìôQøÐ÷ô mAÕíÒ\s»!yyQ…+×|`Ÿ/áhÂXÚ¯^‹?–ņÃù 1:B(6b.ýO7h¿`yiÁL5Ü×NÙÈ é“Kš|û·"xòål¢¤C:ö_0ƒ‡¯×¡éŠQðM¾œ%N<-$¯~‚òÅm£ùã.5>uÔ}X5½jÈsÃaé,*áø¹kñþ—µÚðÈÀÛ˜Z,A~¼µÀÝÝöƒ^¦4µŽP¡Ô AÚs:~Í…§Ì–Ògî·Yµ¬3V=]Éo• þnp‡]àA9΀àþ‚zÿ&¸{A¸\7 y¦ÂU¦Ëþ÷l>¡Lo]s$Ù¢õ8ÅpWŠ¶Ç 9G ešŽ`²YÇLü´C&¤´¢¾4Õ¿%ùÉLêÍ|çàJ¢Âõhr¡ üþ>´FxÑÙvW§7&‚ÔCê\ñ 6äúâ™±6 ²î®ò¿ˆ?|m‰ÆÏôïÌá°ûi#\žùBØ–ÛãÚ`'g[ÏÔ‹‡@5Z²óh|_ïTãüÀ×Pe÷¬¾?¸~]ކ‚l%ìT>CBŸÍ™Fiþc4ýh?•¾?Ǫó<ÓQ0°ô9èXˆ0ãùû)Òp;Ãy-¶›sLáÈÔZÈT¡+2¨cí>vßA‚¦zƒÍ6‰8J§N-c1æã‰ÖJ|ß«É}'ÂÃøÜ²¹H.¤Wÿàï_‡ÐSîZù¢Œ¾²„Üep/v „%G?÷\tX˜s`¿5iøœ]«µ­øËawѱ½Ÿ ŸnGën¦?”ϳä Ü—|ÃjbØÝyÖª¢ õã`™l6ì­†»^rC+)4JqŘë»aÑ… aEG¬•„ë7,¦–wGóáÎVÂþ(ˆ—˜ŒáY»Ñ5ÎŒ¥ÍäSE‹6F\]#¸®L0ŽnG›Ñð®U‰ír1sãitgÑ"–WSÅ/ÄyÿôöÀ±Dx.¹žý ÜÙaû•„ÊÓI8O° ú$5•5à­u‹4§Æpá’)øgŠ‚}·ÿê·Wð¨ÔÉ,”àÖ²ËY9Õ¥ëãvá±|¢œ¬É;À²…Ð\åú¼Q£UY¿„é ×YÀ©RAâØûó58ºÁÙ%}˜ßÓ‹×t¡æÁ)h{R†5ªðdÛ.ºv|´ÀŸH²åžÖ›Â›‚[dQës»µãå°·û#Ø)LÅ Y›©ÓêIÜb¦ ]Ù2Jÿ-öȵ¢¹“¾µèççÁù ¥ešIr#hsâÀ­úáÛA¾!ò énÊ¢ÿé…4Åih¿™æ-‡–ϳ¸ç5j¦à‰º‹õø «!\añ-üºwºÔ;°ŽNBœyŠÝ0Vï“ÀªïÃ/šÙô§ƒHi„f›vòà›?.¾‚LØ<öh,Çî@wë©RßÅ®ÂQ7`Vï=òhÿŽ ÕNÜfL<™•7m˜ÍkÒ,ùŽ»K°e“ÿÅãÙœ^9~e~»6ž/W/Ž5M…¨Ùó9ÿ8Ûú]ÂÆ‰vh2Û#«nÁñû$îYˆ>ä”i²Ý ì¬g*à !ÙnóÍ^¼rá_6áp/eúÎóÚvcò–±t†j:_úÓ VnüŒ±URTÁ@š—ï:K*¥]¹ G”ÖÜcGÐNjå²K‡× i`/:›·Àà0„÷$ù½î ¸%ê̱.À3ÞsQO~/Ó<üXéœFÕçB&µrÄŽ¿Ú›Ø‘wê—®¿"U7ðÁ=tŒ]¶ÑhŸÝÈŽ»,]7E:Åm,1=& ¦Á ëºü¹Ï`÷Hkzä»1îQ¾Èê¯Á%1iø©Šo ੼Q˜ÿ¿$iYëpß˸Z¶Znsp¯{Ï.åœg/ÆŸÃÓfR™-Gñ©ËbP~ÿ£¾,ë ûýYœû]ñÆ¢Þ%˜µÕŠ_TJÂ%›°»)³RèdÙžº´øê_È(ÇA’üw¾_ûnMR3€7ï.áãóÝ8}Ìþ¥OÏ Å É–ø Š r»7XwYsÙq޼u_|YSœ·BhØYÛ¸­—ðÍÞ!¼Ö÷*Yá1…xõ›S¿UÇH¢£+ÈToQw¤ª¤Ë¸ 1S0A³´¾L†¡v?ðÏÇhWØ€Q[ÊHHÿ{Ü®°‚>¦"<ðq Öå?^ºt½e7+6Ù¨Ÿz7ÚAVõh¢¼¾—ܧj¹[xÓi>9`ãFž5Ô²R®¼òÃýc³`Ûú8ñ˜×L™ ·”¡ôîa¸ò‘7N3¤E©¸'ЀÈâzµ T®±…ƒÇèÐ5?±Öu>ÿðS;—ª³S¯5á²ÞDX”°‡’PäLj¿öLÏM¥Â` ìëLá#>†Òa­àøÖ™N³ “m†Rçñ4 w¡ÍÑ0úòá:úìïFœ8/—$ÄÁ•µÌ;(—G[6bÎõBÈÜÂÇ×lÄÖ©I°Ås5ÿûë<¹ü*Vv”`ÿßi¼Ë-•ÿ¼VÃvÕp•ŒDf­!ÑÁæxé‰=:^D쎤‡ùHº}†Î^%FKn ¬otå+w1Ãu°y„__$Á£rcps<¼táBB0Ì­ÙÃgi˜òC‡ÿ’½—¾ÂUaäB9øt4‡=ÑùÏØò’k' Þê øãFº±‹‘Æ4›*ÇÖY€\¨&ï2~eÜ'³-¥Û®æcâfM|þE޶lp€µƒcPê& [ûz*$1vXÏPˆƒV{î_?Œ†x!„ž€›ÕA0™* Ÿ¯^Aª™§CjFŠñ±vtÄ÷¹mŽ^%¿O‘« ².k}zìÁyÃuüêÏ a‚ü-ÌÀ;ëWa÷‘³¬=q4>Ì5å‘›´¨_Ø!˜ñ%œVn#=Òö ­^ÛÃ6Y­Áé?îà­[ݸÚC“.«À‘†ðmb?ð»êg_ž7á’ö^ððãT˜wr=MÐ ëv+ЃmCÀ¸+žÈTœ„Ô`/AÒÍV¼üÀ‚‹ÆÉ%õÿùŒÓ–ÖãSYªR’ûGÿ%6çÖC}Õv8 AmǨòñ?ìdæ\:5 •W„Gpz|Ü S£ÿbBz°6ˆ®Ï‡W‰ô¾F^¶ŸÁïßäìçy-_C®TjRG:ŠnØÑ)üy΂vñtwµ ”ŸÅmkù¶glsúC´=r‡ÿwë|<!™;V¨â•5·àQQY$(@Gÿ‹¤xm7Þ2¬tßÂíIà}ÞßÙ'\§µK73‡Š´ãÇâȧ³õéRår"óÛÃöQ+èÀ…4®6z¿ÊÇ«cØàx˜#èg®KøÆPg.õa,½?â>ɼ}€ù®:çÄ–@ïÝ1X8×’¿½ÑVÜÃÂlcËH žÜ¯ÀW×(ag*}oƒ1ÛT°]ÛŽ¶>G•õCÆâTd§âpHz(1׈᧧èÓ!·ÿ±T­ q5ãSá¯+³Ùàõ³Ðr¹Côì¹ÒþÓ4yýj>±,Û8µƒâïj°üq+ÕÃK u‰¼ôƒ'µÆ‚ëÂ߸Ü+_U~ãÁ<ЛcOZNƒÏ¸›Ç„ R@½Q“‰þò›i÷¹ªµhæ$NEVIÁñaü¶­€ÎR›Ç§}ŒÂ‡)ümÌi¬‘ Á{·Óé£ñ)°Ž£Üf¾V–žk±çš°$ÿ%†ühÄ–ÑÅtê(sðYO{Œ€wÏÖ…†3£”nßÉ'™$óǽ¡8Fœnÿ¼ï. ‡»>©lžƒUõæ)½aß®½Bû1B”²($3Mª ærä¤ Á­¿Ä¨C‹4ý[Q†9[aìâYpu×kü“ÝM Öùâp[rÝK‰>Ë ‡¯I l¾n›AC¿–: Ä.;„oÖI¢æ»`¬VS€“ê_Èd¹“Ðï_˦†êf \êï@K_®ÂË×[`½ýS"õ®*á’M–tÙ¾õè0SϘ¿ÄíKÖ ƒu×jp$Íà¹GÐòvðúDy¨x1¯nÞŽsIK!Þ¾@põkkÁŽSux)G‹ÆYAî¾Xèÿ| b&ô®)Ý•ðï[áo{yÎõcá`±¶à¡#b #š'X¶ïêq€}ABTÖ¡öK.á¯ÝŸHÕ61˜½[0îË"Ÿë SÅK逓É{;òàeKY© H–‰‡Ë÷àí%§HwÅ úñ‹(9‡÷ODà•Á8s{#{lRäu ÐÚ¸f@¦þÚGO( ”§’ï§òæäB¸pņÚiI Î74Uy‹¾Î­i'&N/ ap8{o M‹¶0I±0$¼è‹«_±Øù·ð¥a:›(ÃÏéìËû¬¸ïüh˜)iûÅOã’‘$¹`v‹Åàˆ†AtX‰·/ ü·°æJ&m7äÒ+L)13â)¥{©ûÌqL‹Èü3n°Æa>vÿ2䎼ñ¬F.t?ÒáJªÛð¤_cù‹àð>vîV.¬ÈùŽÁWaãí?Pà~žx>'–ã¥øògÑølM<þB{¼4»¿¼œ½éµ|-úÁL”F_ާé":;=šëþÅåñ>&»Ã9O• $øÝô`kkráÚÑŸðÛ÷~vq¤{&q±€»$aI!.‘@–¤w‹=ÄSo†Ögm$¯š‹WöÈÐòÒHá“°l{/^ò ÕéP$1þdFñbê˹$½å¼œO¬9‡õ;ù%OIhÞ£$ Ÿì°9 ÷ê~–H”Ò £¥ùÏ?‰Ðw4¾ÝkÆ3Ù>ÛËáàç• £SÀN®Àò{Û©ßKaùïéüSp/Ž5¸ÌÖ݃Û×ÂþÕ–`*jŒÚ5r´Øþ•üÏ¡Mc6¼w‚d+Y:æŽ6-L…uz¦ÜÈ'Åäei4$§æJàòŒ¸ïº „íš §æ†²Èº!ÓëqèTü¼Î쨟F¯YëQ«:IªÞw¢¢zQúÏP²b„ÿÕjÏ­jc/êñØ¡áT|êuÊÄæÂ*åHþÀM›Îd º£‚~*ÐÕÒtÀs(•Í<„º2üzÄjþLb&Yâ“FËÌ1uŠÁ¡ï¢*÷=×úÀd‹UŽšϪeiäüjhU–¥]ÖäZˆLÓ˜B÷]ŒÂÝQ~¦„”õ-¢£$0Zå8ÄDãR£b"ùÙ C;¶Ð÷‡BßûŠôË÷k™…ºäp´ÄXLáµ5uðfÑ)Ü´¨å*ey´t!ô7Æa¯\04uZœþ€45ÑÄ ½–-=}¿}Ez×dÃßÝ$kÔìžîvz] ÿ÷ ópåX$L=*A†.€j¥˜âr TЧ€ú[K‡F”Ñik* àD0穃ØÐJ8Ôœ­¾7›»Nó€‹+DéŠkÙ&W&kk„ r _œùÌ|ò» €%d~ÜîŸÓãwiúÜ÷põoj\䶉ÃðC£ o“µ§Ý ¡ ¢õx2z!ðúVöûæI.¯Flún€œd¶Ü•D5ù,ÌTË»vÂ…X)NKCpÔWC¾R|Þ¯*ÃÉEpXQÇ|ª'FšÓ )Pº¦©Ð˜³cùv›lÁKòåñs<ÿ-Ÿù†Zså³ÑþÚ}8 ”¢ïO½ƒZ…-lo{«eúcf¡øáý´}ëYh¾×€•W¢ñéæ#dCZ¢À¨n¤üœ›åÜðÊïch™ž¥Ñ<Ù‚ Û…ØÞ¸‚d€?üEB¦nÅß'¡z¯-™P]'ìùÍó?ËÐ!ZKqÄIK~À8µÓ5Q½%ߨ½ƒ odHï@tªÒ¦’í1õ}€OHz‰¡Cí@’׿wðÔ 5†a%©‚Å2q8±ìvý÷:X$Ý S?Xµ'zÄ’!¿Ç#=Yþ«OíìC ¯JŸl³Ä[ãÑýßc[¥‰k{Á륛o¶„ʇÇâ<ý…Lncv¥Œçý¾3¸„Õ 82÷!Æ.ïfSâè‰è8œî÷ê??´  ª›vŸ³6Qê”NCUçÃök64v¹B²!ßÒ@K+p͛뤧ä=»X}Þg?¶£^ËÃÉ ‡O‚÷[çi§YláTt²®ÅÀDÄÇ•xn9¢è©PöÚ\‹‹Ì~Н—=ÃLöÇZG£@¦¦@…}51÷G¸ßÇ·O'Ëî2KmWü^t˜©,L‰¯Çî&;~¸ázç58R¯‰fÖ ï Î&•¢ö¨>bVð€ý9|]-íŒjÜsMƒeº¨PÏuàÛö[à¾x•›6Ž:ül@ëØõüÇin™«uÑ~W"³7‚¼¯C¹0ØÇ×ò0& mÛö “"{QýÒ_[«Ï—="áÆTMÖ•ž\ÅS›îã¶OOYÄ™þL‰±K¾2ügÒ nöOóŒœÌ¯”'Cý­uTûÝúw³ š´¨ þ}êE"Ê1o4vÖýÇøéN¸¸V„ñö4c[ LÖ?…·´¦RHEºfd èE¤âñ¨ tÎ÷.ßhÓ?E‚nËx;EÁlZ­¼ƒo£J¢­œé®‰³hð#üy¢›_éA§%¤På<ÒÓ„ý—¯2“"_.*YAŸ ¥ZiÝXÆéˆ<üs)rÉ4ö« ™ª\ËŸgî…æ_± ­˜ Í“º1åÔ02}ÿr¬þÇšnß²ø‰/òÔ}¢]’ O;;[Èø™ÓyÁV#ÿ7«ÌãÛãÑHþWß©FÂ^‡»‡èÔVrxè¡êùQT|ç6&KŸ¯8G¹Žnì÷ bKNÀíͶ<#Øc·ò³CÅÊ)`Ò˯42ؘvožn£Æ'áS£Š:Ç3µ¦…4¤é„ƒ<ûìI§õ|¥?’piRLKÆõ¥áiy-û°!7ì6㋞ƒWWŽñ C¨ˆw7+^ÎÍçjÓO/ÃQ|Zu¨9Ë×Yð"£8lÔ¿ëg$pÛñÕüÏ==:cZLº÷“4œ¤×BR@¹ï7&…`nÌ5zik¬4 ð”!ÍPZÃ+YüÕ³àêÆWpd•"WþFÙ··¡4­}2ˆ~®Ó‘ë1èípÐV=om.ƒz±-‚Æä08¢¹‹mýø—üõÓ§n1«éÑ€M÷ç+˜^Î#]ÁÂo´)ù›i!„Y›üOÂèÒ'0}]4L´Ó¥»®O§‡Äs±¨­—iÌü†3z<àQæ|/EöLƒõ‰;àüÌ™˜ïyç ;‰Ç ¥3<xÁJ¨Û`‹¯·þSãU|åtMúsè²ús@¤OW81}ý8S¯E´OãW¢“ùAsLqÀÒ]é‘Cªè÷\ FI‡š U >õ"ŠôåÔo:øˆ)«Ý¡×P½-€¾§„iŸÎadzÕp|q‹ÀGå Ñ;¯ÄUêÈÌ‹¥è3`IfH¸âˆïw'0à÷?-åp —þ1åMq%ä€_,¬Yˆ‚ŸÕé¦ÚFÌû…Ìs¿Ìoí®xSŒÝ™ýd~–¾öÉCž²×–~Zž4?]Æ ë— G&6’ã6…õ3¿&@¸Ö¨Ï,Â'k}®À‡` ·8Ãá/V`T‹c/n gN?g}eØ«²†käá̪ëä¢mS]ònM…ªÖq8d†(ý=£†|Ü9ž¸Æ#Ï©n¼~˜4ÂXzÌ<wKWì´BZ=7‡],&8xî‰À!wH‘ýÏŒ†—Û¡}ç?M>E/y Ž¥N0ñ…(öm>ЉãŸí*1ƒ¶øÕÝdн&«@Ÿê|)¿ÎoµÅ³ílÀi’:n+ôž=ÿ‰¼ËÅJ’¦½ˆß-Z‡Õ+·På;Ѩ¾¼‘ü™.ÂÓ½èöÂDj~¤3OCØÇh;ÒñåÍø¥þW¸´e#|-ë†æ ×E«Þ$7þfsI“غ#™+ŸŠÅÛ»Ýø¨ÅR¤æF ^ËÁš†`Á•º t\•4ÿ¾)R—ÿÑY¸ë¬Kµ«ÖãÜÙWñð¯\(KȕݦèHʰ±ô²í4™°8úí•ôÆ€¸|¾êywpïÂó.>ÌTî¸ËÛRsw>fÊôØ•Å&ÜB¿ŸJÎíÀÍOÑÝV†Æ¯ÿ×7ð ]5xp¼2ýØšÔ¼Éò KØÑÙÉ<ô©öÔáÒÓ‰¾ã<½#—øn/A•±+áêìF<ÔO:BÃX¾o–7¼Ü&Ï/HOà×Ó&AIð:šö´ÿœ¹ –¿Fðzþ3Fc=Åi„ë<šóþ÷þ»ˆ/3%n»ÛáÖ«?ÀÊK@iNÉØÑÍScð¾ùz"&»œùÕÙŽÜáé&êzç ‘Õº í‹\ f*~úc8¼£ÖQÓMí0wä8.b¾ˆõ$w ŠüTqüe)Žå¢0ôŠ3 ‹nK jÐYŽ=ãà ŸfüZÞ 7»wÁõÔ~°ß†OOb±—œÅo䡎:¹¸_vyõàûåo âw;l-ýïÿÌðqb:[Ô¦†ÂÀLd –íÏ»#E¡´Å•Ê;nF7-KZþÀ’6ùMÃ[V-Äb¸¾ržÈã')‘tt€=¨´kn5›Ê[Í\¤æ0ÿÏ¿|Îo]-œÇóBr©sØI;‡)?aeìŠ7Äá¶ÌRaâ;q^zþ>üv€kvÍ®{‹Ð5v²9à×— ¶JGyð¹`R{ý4¤5.¥/åñ{/¢qüA!c–k™„} ™%¸‹¢™Ïq³ÿ-áèÜFØ2ÍžNÄÐCZ 0íD ÿ†Ëü¤éˆ<~ ½—DYŠòè´Óø¾†Ax·OY»ÜóûpÕ¨áзJ‘_«,emWð‰¬-D¼‡ú…IÌÉÄv¤áƒIcè_o0Ø÷ õW ÂÆ‰cv…Ÿá3(œô ñ鎖0<‡-÷‡´£Yä„ûû™}ø>ú¹¡j~Œ¢z;÷©ñ¬?€òÌHcذ/Ï Ãš•Úì·ÞC¶hE}b k0¶ƒåm­¸®i Õ·Í€æ[‰R´)~½¹• ½|ˆ®:-ءϣLLÉÁ|tíl&ã{²Øø—?A§Pì< »~¿BÐÙÊ©ú’ðÓééJ\ºÀ•«š—CW«×ôßÇoC«(#:6Ú_ui#=›WÄ!N¼¯@•ž½…“ °á±=6¿JƒáZÿ´3XðóIïp™ŠÚ™iñ»·ëh¼Ê4סߠÄ+Œi÷€“OqwiÄŸxåÁìX2&öœ¯¥ÁwßHbµ¦ølr&êôÀœc)Ø—ÝXW`Ì aÍ^ºVi Òp: þvÿôFT=z&vÀÔ¤Ó`²ó™ÑÄÊ¢fñºqtá¼P^NúKàÊ&¾ùýYTÚœŒåå|î²ã‚K~£è±B'žúΙ¦G$ðK1w±(ÔIhÖp +œ8{@ÃÛqÁECâ·7ÂUµDzs«ºÎ?œ?£ l&¿˜5•ßÙËØ/yC>§9ƒÈ,xëÑÓz®]XÝo¼ÐÿDG.‘â#ýÁo•/]=Ê'VÒ]b#èMñ`9­6›Œ%uèt£E|Sü~,¤¶¹ÚœË ¦’98u¶!¥ƒÕ7+‚áísàÙ;úãùl*¢*ÚïMiÒóžú™ýŽsðûOhwaÞϽøyt!ˆŠí¦RºÅül›ý4­ ŸL:DÎïÀôû{éïÆ1<ÿU>H”­ä3gˆðÿæ=û²t|²˜6øÕ°ºª0>æš1ZKÝÀnŸ¹8~«ÈÝÆ¡—V²žY%x`É(X¹m& »z· ÙûÇc? ¯:UãXõatH8w”ŽÁ[©%è!{ ¶mèE³gÄ' §r"?™Î=}CÒHöaùp\¼£ ´¾%ÂŽCÊtòŠ$œ˜2¥U¥qi«–º-~‘ï…¥½¸Ð%‘Ü?SH’ªãðWÄ$¾üÂmòAG†ÿðˆô¿ ƒËE¢ü‰*ø×¼ÿÿ éµÜü»ÂxsÍöÔôf°ÃøpÌ\€¦vt‘>&=Óù•· ñE!Lª¦üm—':L àz^ºÜûÊXìŸ6¾8K¤À´p!(èûa;WÂ~åM”e£¶–°§"IôY±m‰½\ïâ)Ž¢j#ñ¸s-ê™=g>3Ñâ)ÌüÕf¥E•'ÎdÅKîâÚ[®3üqny)M&¬ïöO˜q; 2µl¦«Èê?'ªuñhéa@U~ÖÐÍg¶pKó@è¸?ÃÛ Xúe*ììÞ‰\PX‘„cw-§sf¸àBW ¶áÚT<§‚QN>ÂþÏÐéyŽþPlc-©¸‘'¸i߀1~üðöÓT¶vß7Lý- +ö<±ð\ƒB*uc6¨>ŸˆŠnfBQ;g–¥×L¶³Ñôí÷gÄxñÖ|à|Ÿ0 OYÊ/H;Øžçö7wØõåf»¢Ù¶‹VÔ¹_—fïZGÎÌC‰ƒ{`?&€Ç{„É1 äIó^0}2*Žö‡oI|¯øjt¸§N_XXðKZÐnÉß²¢›Wéó,é\pi« ƒì† ¦Îà÷Aï‰ÿ×›t¿Ì¨W”߯öËèÑmóÁÔY3h€OWÜÃu‹¬i¡ˆ?·)¨Ac3j˜iÅon"ϾeÑ&¼ý•n?|÷™Ó³¨Hb$É22~ìbðþ6’·M©ƒYnéÀ¦&"kÊ&¢] ¨yl–¥‰G¹µ¹¹€)+|"^FpÉæC°l¦'U \ίé¹bàT,ìTå´á ü,ð¯OùÝúÁÞÉ^FߟÉÇôRL38%pšüQxô¨(W^ö[¿º4Ü"°ÀY•^š}ˆähÔÂÆÙGaŽì'\²Ïþ­±4) öL¡¯´B Å:æÁðøâ™Ð`¥J©ÑÒuXœ—Ÿ9 “Üi½³<ÕÚ¼%Œ±ñŸ ðF®,÷‡ˆknáêëŸ ©ê`ÓËeàÙuŽé—bºà2e,™q¬&ðO›ËIöé¹øqN3Yýe4)~k ó¯v±Ò§Òtà^'9pM¯âï#h~ÓÖÐS%œ<ìRÉ5¸ ÁàC}Œè”à&ßHÈQ³çÃCþ›×LvñáßM‰êœKhÐ*E¿¶»Àý6uÞàõ >Ø„ÓS™F”­‘ä¾=é§êáðmÿŽú ý`ø)¹ª6KÀÊuv]Íd™b»8'‹\n–§Ûß¡i®2¨i؉©ì^Þl®ÁO®¡Ç—_µ• ³¤©Kê0?)‡Í×µA©‡!P´ç=¾3|.0ÚµçµÅoŸSHú9xó%‡Ôj½Æ/XÉã<'áÆÖW¸%ÙÏMüoýïa«±fÓ!"š.ü"ûÊ®§C¿ÖIØy6#=!aåOø»t".tÁ-÷Æ]Žádׯ^¼ÝÝýybܪa,<ðÎp9œk=‚f«'2]mÁ iUþ>ç>ÎJ('Ÿ/ö’1.?@J}•]§ð ¿T°½R1Úµ[â¸/Bí37ì:ÇÿÓZ¶­©ÄJ°¶¶g£<ÁxóJèÓoH8|$1ëFã¢åO…§GšÃù^¬ÚÂçV™±Ž~;®rÓ–œ~‚‹Ä ß$5Ôúë¿ ‡Ì|Ž{¦ÉðÓsÂpe›Š_:$|¤qGÿþò±¼q೦5º±HRïëã’{†¢šë}<:¥ ®I‹ÐM6_qvš:õw&ø `ÿ­óDaÎ/l8º·­%Gi0÷˜éV­Eáhþ.C¼™£C>meùÇÁÒ‘p“>ÊEûc1Óþä&^FåªÒåž9žTrŽ¿þìÙcÀãó&ÃËÄ.«\†²ý ˜[qôÙéPé ¡x÷3ˆÁÆ‚a¼©9ëßÎÄG ÄMæ6Œ³%Ts°Ò}Îà“‰R(aÁ‰åÂ{ž«›>JÃÜ»r\rx1¬,ʑ륤¡ïiK$¸Åæ4ù=²Ö™ÚC\ÆHìöæÉ¡x¶î {ý¹m“]ñÓ14(¿Bà&ÂF]¨Ä®eSáôUú)3 Ó7L¢¹é«ñ±]$ÖO þZAl•ÿE¦áéÉ*‡{ýÏI‡ƒŸx)•< 0âé_\ðxbë9bÓûØšt'ô;~’U‰÷°—Ïc .gA½&BBߢxZ4tŠ…¼»—ñ¸e2_¼¤Š.Œb´ R¶Bõ‚x_u…·D ä¤?n¥ÌÜ {Ùi)˜5Ô‚¯J±×s—ñ¾‹ g錚/0î„Þ«2í׳jÄ¥qNdõÅG¤Ñuÿ²¶ŽÚDCåÅ•`r.i8±„ζlªÝ¿A~Ä~Ÿ!Èp^ýþ›-Pq€e'n`Ïïbú 'ˆnÛ<‘/ˆÍAu:Mx–c6¤ßU¢»MàR:c˜Ê{}¡c°9Æ8õkñÕ,1ºç–^¤‰gÞ%ã t„×û< ¨KëÌäß½0÷û.ȱ[Ê-ú#¹öÜ“`}#U"ùóeÒôë¯`ø{óœø%| #ï³yN]ÿùº…_§Í¤9c–À“CtLä rtÛ!$A\ÊRqÈŠ(¸q7MÒf,må[ö1â8œjðÜØe†Û]¯àКõ8$;yç2°x—#Ålée·BÚ—îîÎ(¢(-ÜzS dÉ¢dú\ø2µ–¾?‡Ó¥4yŽÊ3òͺIX^’þ7æûD²‰§©¤Êaæwµ ãÇ¥¯k…Сt–V§¤‘7C¾õ[ÏgÑb˜‘8‡ÇÇ|e·šRݰޱ–ŸKgåÏ…Óʘ>P‰Ùc\Q9': %ɾÁã8Ó×vl‹Á¢i%B+¿· Ü« Aw‹™D)û°XŠ¥™%O΃¹â˜é'ÔoyK”&/Ã;˜èëL¸å†W×GÁdW¶ùñ]|‘s‡›žƒåq|{P±ýQ¶¯F“óðAÓjœçEO´ýdóò"¡(á$ñ6ÛÈ{ìÖÐmß~ ïË/íT5^ì;=e`õûL“jB笈$?n ‡¡êtQës2qD6.j…[ÇòÞ_mXçÐ ›?€¥^~´V˜¡ß<àþÈÿãèÊéúº°™Ì3™EJ$CÅ=k§ÒdHIiÍi.³¤LRDÊPî^[JÉPŠˆ•ŠDhP>¿ïûÜ{ÎÞgïµï¹{­÷}ž÷¼—ܯíÁ]öçɳëìäEÝÐ@]ž)9Kï°ý»‹\ ÉÜÇTVéB’íjøPB“$5Èõ-п6Ö=Í„gSH£Ô[øhiÀÞãs‹–ðwyGÏ”Ãw Š›n_ƒßýâ–ûú³=ZóÐî_=wÚ¤ƒ®Ý{‰6]ïà‚VA@YÂÿý˜OÆž  ™ˆCt µxQ©Ue –è²¾­¦pPèÍpARúf=_\YŸÅ>€šïß8õ/+0ae&.õ\‡M×W’‡ŽZäR¹ Ùëׂ­û-á¡O:7SýÔÖàn¿ 2ɪµÒv2Û£1Ü…§“™&Q€ ãa÷ãvx= NÞ¤,F… ˜¯B^ú5Ôîä™t€F\nß_nkâ¾Y!X!±¦Å=á,xàcÁÆ^#YŸÛ_¶Ÿ/ïø‹ËšZÍ:Có/7‚B¿3—ŸxÉi­~ŠŠì?Ýsí¤Ãôòxèå} \ç”6Îýi ÷äõ NñÅZ°V„‹fÓÒñ;1 ä6ˆÄrìdg>~®É£x9iJ,›©%R=œV;áÔhsHK˜Cš†ìÌW~æÜÅ`†ÓG4;t #·Îå)½= †½üCÍ/ð†[Ú½¶ÅžŽp¼¿I‡ùªJ‘ö;0¡ïÿre;çµùw^ý&ê›¶p›«²éy3 æ+PÎÍúZ¿´ÂñùFô'a&éÀö0°Xk§Šªù—úÁQé6£Cæ<½Ž½k4™ÁX•üŠ›PùÏÎ{Ã1~éKIØ‹3÷mNC¥}'ÁÛÌÚÏk‘äób\¨‘,[ø?J€Î'y|:‚Û™°…•íøMU3D˜•ý223µ^¾Q³ÉíúxxF„$ãKŸ£øøãCÈf çZ“Éã‰<…™!$a¾5,M©³}72–µáL;øLJŠÁ†¸ô,Š»eù(Mæ@àâUxÏìÄßÜ‚ºßÉíÔ4Å_zö܆EâŸé9)hM$¹Ë`NÆf®V¯çðe ë¼þ¹ R[É<ý7Üóõ×ñnÒ} é#ÏÖáÔ« A.5s«Y2|3“„HybÂÔËðî«áÖ/a…‚#´•çB4bxMwºÏñêŽObw½Ê鲨Bv3.aô>˲–®h»hô5o^slAûZ˜þ#»ùY‰¼5ý¡¿~aMW}\~ Ï¿Fɬ«PÛÊ×£Äÿ\:Ò>#å ``M”¶ê°9ÂfléùÓ4~¦:­é‡Ù² û{>ù4Öiä,ñ÷‹Ø´Œ5&«ÌœÀãv .ZÒ _žø’â}cɸ»:$p׎+ý†Ã¶ú¨µPl˜¼•¥rõ˜ñ3^Úï‡3á¾ÄÛpöõ¥Ì ܲULª®îGÒp7àø¹H“êÓÏ1ìúAX^W<â9q¨Å§ÆàR…tbf•šì G‹àØúÉÐXÐÄOÚ𠛥ѫ!ÂôȑܠÊîRT?jT “®¹¨ÛjS^ø§¿Ê8ÿÅ H³›ÍMU'²Î¤ØHGdB‰E~$܉Çù˜ˆ1Vryz„6…“kÿl1>&š?§Kƒ|‘‡Ï,÷ÂÌWÁº-o1†çGüؘ’ºÿôÞdG¶9}ô¤‘–µòZ•â¡S6‰yöEÇŸÒP’\L6¸M‡Æ!(~ù€Þ½jÄXËrV’Ô‚zóðÚ´Ö¦Á¼cɬLÀ DÉ_Vu­–±ó¼‰U²‹8¶…„ÿtGÁþÄtq:=¦žõX¸h©ÿ&‹iÁVxðﮫë0[/ v $ÎǛݾÎZ¡üÿ|¨Å}·2qºµR‰èôøaÁâžêÃÙ`ï_ͽÀ¿m±Lã¢>²ÿËßqj!NÜNDu˜ Â…ý§Xû SæDtYåßDÜ–ªJ¦í`B±È^ç3D-'Ýê¦rÜß`îyÛ}½›Â~§ôr*¯½`t.Nðèà–-ZNÂŒÙoÃpìÌSÞœ]áƒk U~"bçñæ;Î>Í-º'Àf샕~~Ì"è ‹¸·5çÝŸ7–ðhð5íQBû1ÓȉªÝÌÅu3¶÷.ñÌ9d~.0ásme“Æv½ü0¬–ØŽc^bÃÞüY3—,,JÅ,B׸qºkà Ý™ôt9ù'qWÅ–Ñû{²3G¬±tE¦-?Es#Ì*þRUñp“n‚€|ü)-E}_O&$ʃüÐ×!y5_©TüZ¸xl3sÜ€òYÛ–£ÆJ/¦{{3,|Éúî˜2›gé0Ëj5ç5𬵣!°o¡?‘Мo—/`ÚY¼ŽŸàÙR²Ë8 Þ Žp.×MX²¨sÚŒyñø a”Sï2 ¾l×j,Uð`êÿtÙšó²Lθÿw™“ÇÃÖÄ•×ïd&jûµh}ìdÙ®&rÏôüÁèšeb…^çq`ÜòJ7•-tß͇e‘;¿¾ƒ{à\­»Üÿ~”lLOùü²Ðð66h± ߇¹]|ero¡!«ØóTOq×3.PöÁ\0µ 7‘·v!ÿÖ-Q˜Ãþ޿߇Xë.Ì³Ž³ÇÂ}&Ð^¯Â6­ºŠ¢R·Áârðe+¸ÿyŽß¾lÒÖÊyn@…ŸÄ.Àl¦áÎcâ¤áX!5 Jaïö—Ò’@eÔz¾ǘY@ŸÆ ¢#¸Ý„üðØÇ ²³Ãœà}C'³ç'€÷Î% X« ®úD3ê&võù¹èؾìþÀm¿QÃÅTMŵ"Ç1:õ2Æm»T¨{*Ì"ŒÈ@ÂXÐIÓgW‡¡#¸˜Ã ^XnÉ+é0!¾3ÉÌ÷ÏéÜË|ˆˆ¥tñÔ_pU]…;¤Àæÿó¤çraLCë‹g`íê8-2Š»ÕS‚ë§o„z÷C0Gõ9OŒÃÜËð…µyxÈKÏ=Ò2}v2¢”Ë{3$,ÕQÒá$ hÖã©©\–‘-)3ý>ks*ÓýÔØ~ÎOkN>)†ºqm}‡±î*ìnÆ2výK&]øŒK' À.sÚkÈS+éǸˆV*7y6çé’M&÷¿Åõ^ªðÊ›\QcQã d <{oKÖ-߉U-ˆjïèâyyôf`gŸf}‚(¨ØA5æ©Â›ƒº,Ñ`ãÃûغ8ûßz‚F·tß Âu{Ø{ÓXîãÜp*醳gs»½ù_ùçÑ6:”]?³—G™Àë&'áø &{9Ž+ Jåä’VýºìZo +'—+ø`ù{^šÕ3 ðdLù,}qÄ–¨xS Œ §M,añ¤=°áŽ=ŽvM^µ•ìoBf˜‡ªý­xWÜ‘\;`D¾¤·æùD™LPÛÁÒ'Ä#­påà$ö©ø1ç¿Z•ª}%c¼“X¾õ9xš¥Â^=Ò`jF0wóu’üÚBd ÁE·†¶¾íãDîªásï¸ý£S}Êç-®4f{å=yÍoRѳQ†ø óàĸxqYúÛ¾Šà³ýô‚Õ<ü•u‚³º1û Ü?¼m„“øÎ‡¡Bà¨P ¹æs÷ÈzÚ]£ÈGâ!\°žnRÅ„¹õx3Û ̇Íóæ°ã¤põ/1‰±ûÀêÒj Ѱje7LÔ½€Sã;Auù+ܵc7±${¨ná4á9ù†WC”ºÛ+ÍÓ]íbJn)4ÐiÅë‰ö¡sü4®€÷2~Ô\~OïgOãÿîHâyl«ãDÓ­øÃù‡Øáeξä%¾ü¹j—0©?>¬Í|>Ó¨ÙÊdW`1§ðl! \¨m7¢x„n±")6¸{ž›")ÃT¦I§Aö1y¨>†ž¼‚Éœ]§Ý2`Nc$X¤é}"8ºn®ôU˜5÷wùŽ -σ?1Ð9yqž¢Ïîšš‚ÅÀE¼“¢B”ïë2¿?͸6î,=gÈ>eïA‹¥¡xi¡IV…ñ'%É´à>л˜„·¥¯ÀÅ xRH†H qÆÒ— ·2„/{ø=²G„­_'Êlá1·Ú·„þ§±6 ÅÕÑ'øQÕå´A{ y¹V-0™öö)0Ár×!‰¶™Ãz„äÉôª1du´ Û}Tm³0Am\|šKoJ‡cÈÒ¹¸|½ jïûÄûto1ô;k2êÐ;Ï„Ám³pdã¹s8`IÛ9ÞâÉdbÐcúù"«y…©JU$ÿ›®ÿƒ‹ŠSñôÙ>¨þ÷„û¹y&žƒ£2`fí58¨ò”ÈÐL…=kŽƒ(Δ۰j&tN‚‚ê»xÀQŸ]]›‚³ ª`¯x5ª~8À~‰ÆâŸ1¸íHî1šçÀU$ªãÛ'©Pø0›:»–Ñ謢ô9 î-«ƒÙm. ý'—·íî>òÝÆŠ[#+Ê´Ùxõ—t(T÷Uìgç¬ IÍ4O(9ú™ÿaÉpÙ¹ '¤¢ãù{h“`Nšì­ Sa éùz˜ÜÛ´€ô\NÆÏ¦7àé5RPÀÜïGPYíPöIóÿ£ž!¶*ÅBŒX8vÑu˜Ä~]?Ä™m»‹s毃šŒN-X‡Ú+nÖõ-ìêF{8ÛÍàì÷»°ÔÚ þ:fQ‘si¬ äî¿+ËÌLô™èCm̸ªMvs{xþšïÁ,e >ÊÏK'Qâv% öíýDà “4 Q&-=D'ïõ²ú°ä@îæ~TÒTcÒ’(r, |mX¡ÐDbrp7ÕÌ6þ3’…#’_±UO;žðX™ç6N3ûŠ][.qðaù§@“¾™‘B&Ÿ!—JïÑ·kËÁ*0šyS$ë)Ê„·îeèÀ¿¥´ "†‘AdФ , «8—¹,³5 *®—³°< "«0‚„jH„®X•Ï“,]ÌÞ8ÆZ¶ ²‰ÜáxŽ™NL \M˜ XlÉ ÕÉ™ô}¿ ùjó, $j·_Ñ+ɃôpK8³å£ëÉtUõ#Y—÷ã@L3ÓÉsõZ Ž£{­*ð.^1d)%Ðg¯ÀV,aʱӘkpY²ûû.A¬]åHèãñlÃGavôuçøl ,ÈÒ 3m‚Ñyú]ø°äîPba{Ãú¬y(í#’`Ýï…(ä1Õy Wk?ÄÌ´¯“8µSTI½¿Þ$ߣÁá¬}`J.fkŽ0÷ÀXÖžLÂÓ–°!ë²ÀÏŠMÓsÁ×¾$ÅÑÛZ•þóºæšË@?ýŸµUáú»ll|+ êºdóH †ol¥F´*ïôqeñšÄ™· ›v{á¡l!¶RßœùGþ‰•Ñll¹ŸäÍ<Î{±1Q󈌛 ~ï\‰Þ©8Á¤Höy9}""IáÚdÁë±Øo“ï©RáBä}¨9ù{ïò¡pÞ ÿQ>åÀî÷%“è³­ü¡ßš¤úôyüý¾&¾úÈ‹˜y„%àÞ¯ÏðõñQy3'eáSÌ8áõ/pe <êWÄÄ ,pé-n(ðç¤"Aò¼–‘‡âvìU[ מdÁ—½ÀÓ±ÈìГñ$‡j…1¾hÍ ·')Òç@æSòrrÐ-§žêü^1ÍOa¦”6<~Æ=ÚÞ ³ÅA-ÕVŸûËõytR1az¥°owïæ¯ºa·<¯â7“&úî¼-û:!”Í3J†Ÿ¸Ÿ;à;©²¯æŒ¢_‚¦ÜXk l¬4c.¢¸tø1NL‘cÃ¤ÉÆÍ*43ůítÁÝWµa†;‡‚™Bd à;d$ßSß 8s° \Ÿ.c~{ €[Äx‹—’òAiÒ2G“ÉÑ#j_ÐwNWœ÷åü”˜­¶fÑ9$Sî:Íœ‰iá šhÊ~¯üI?þ’"¼avq‰ ‘r‚…z=0í¾ ®¾q˜­Òu`.©»!±æ>¹`fYªdÏ‘P<7dóþàê7“Xq0¾^Ò‰©Á‹`]_01«Ó&2ßO“K«—³–B,Øø9I{z¾«†Ú´O4FÏ –þ{Än,\ϧ=}rˆÜšàÆêt-Ùº¡CDíª‰— fË_Ì^´«žú!ud^{ƒuí31¯#•å¥ê`î´¼ÆÅú%šÔöf$´Í#{¼E‰ñFEf;O€5g+’ìÐV´ä&™½]sv¬†%éÎ+ƒ{ Ùè½[„ þ0`34Eز6uvZ½yòIìé[Mvg»-‘+Í;¹:Ls¤ÿPÀKºúÌ-¦ˆmÛËöðÜH¿ˆãV»&‡”+“`‹U_2º*ŒË•’-ü'ç^C…2h¯6gøô }8aßuÈþS®(üìíÑ#?jŽc×jÒ”<…§€ë[xŒ¹=oáFŠƒÈŽXwx외Ežùí|ÄÛù«%¬–!æ®Ç™ÔûRòÞ½ûE¢ öâ.Ïañ¼›Aݦ³gAªLûû²©¹‹=¹Þ ]å&i Ï®Sˆe–ZòÐuè ´ŽaË¢’a8„G>Y…ÁèZq:ŸÆ8ã…©¹Üó'AÛüž]…T?¼úYG_I“ AMP_?,ãCøB5±ðíÖ5øí‘oöˆ“)d1e¿49ÜÄMè›ÅJŸ¨’ÍNb¢›‰Þt{aq˜ó‰JÏÄÍOäÙ£9÷Àn“¨çTA±æx¼hhmš—@Ì+wIEà˜nQòïy±"ˆ+}$Å|†äPéV&ßy“.w"ï+Ï\´†ŽÔÀ¶o3ÀòÇ ˜¿Üü¢)Øõgâ5£·8C»Š3wåƒý©T¶ÃC‹[q’€ú·i({Û‚÷§“‡%ʸuc~/JhóžÖ2UlŸ»“¹g~ÅÏG¢ìª“ÜŸ^ ræœ%[rääUOd3Ú÷sË_$ÉÛ¼*Ÿ‡]VƒPœ“ÓÓŽ’ì® wûÙY#5eP:)ï…wz üó|¼ï/‘pK€$·3X¹P”ua+Rűk†9ïÜçrꨚ¦×lÅo£`Õéì­ó\"¬ À.¨“O¶ŸÑûÅmÖ5Å‚<-8Ä®Œ C¹†ËÜxU%¶ài:ò²éoAOò%Ñœ‰ÎƦ‡×q‹Þ%.—¥‰¤”,Û^¢GbÆSüO3-å=‰]>*Võ°NeŒ"Û%§‘GòOÑ·u½½ƒ±íUl³h@¥ƒïáŠàM:iX‚”\üˆÆ…͇`•1Y• Jóu˜òÈ îÐ#žÉð2N=å9ˆ\Á+Ú èèýsôö£i.•øS¶þMÐ'O¦´Á±bIØn°…e„¸³‡„¸ð¾ÅtÙ+[&ô#ï¹È€`ûŒ]ÿÛyº ù!ó©Ýç³Ø”3šVpÚiIôL[*¶Ý~ h(\‹˜óbP&òFû¶C»T=Z}þJU'z¢AføÔ‡"={Žn1$ÚçaÊwÃÿ{O[œ4þÊ"œÞ§ËÞWVë·€Tßo@Å–ÆîhDó… <8PMq"×äPÞ¶˜÷&x‰ÎÚ¨¡cÊnío€'¾7éœ9mð`å:õj?jË]ÄÅ›ýpá.âyî+ݟ0àu6W…‚ÇQ'Ò)]_)ô(?í| ß3×Ráũ蚵žè3â,‡¦’tÿCxáÅEÚý(–ÛpHzÕäŽúgc Áqª¬Ð)Íoí_VãS9¶ð« Ä<•#äŒÜÜÀ”K„Hÿ–vl,šÎ’Kýçÿl÷Ê£`÷d´^»„ù-¸†5ÓSè³S‡ˆ¦Ü¢õ݈=ë—„;{QþÛy°ùÅêoý„–¯hfýN÷ ·âhi§CMñðØú•YÔõ…o`Ç4cOJ.{½Žè üáN6w²{È]ÅNrb¡ƒq2é¤z‚#‰Úե߲‚^w¸öX ›ÞWáS±}Xwe =´8„G½%ç×3Mo!¶åöo¼z+ãýÃ¡ÓÆ¬vw”ç/!•"ýÔzX”9øo$¶Òõ¬'2Žñ<2ÈNŠ L Æa{ëˆÍ'{­lØö©½X!q BoÀbŒA&_aZ%Ší˜Y>AÜ\œÊ2|þ›ÛÂÓµ “— ³_Þäu»?^pü‡—¿~¦<'¶¥} Üo%c2̉¿ÒCö©kµ“Ó!lìRõä:–…e’i½Tf^dûWC¨4÷–°Wo£À%ýŒŸÑóïÆžÍ—gRÍfìÓŽéøðÎáëL%çM<ø~±¹Ðf•fgsã;n’b3W¯…³…ÐÙ¡FÊä±kàFz_Lå&úX¢ˆLûØfªÆÑ˜ßc!69›~t—ÄI×%ÁÿçxL)”á/ ¾Cv^Ve«?Ž€ôî&Æÿ}›Î>ÿ fyd[æÆ¤ZäXß”9HÄ5Ëð4Gâ7ÏÆøE}TCK ½„ÍáN« !ñÁœñíxÌ6q†ÍsCË?gÐzŠúYR˜È)D¦c2ˆ3g=oàªüTs.¼ò+Ícvo°ÿ¼)+ö5aùóé—#Ç9:y/ü§•ž¤iÁJaú·˜-ðÿà ÞvÆ×P‡ëJuô¶A‘ù ãx x&³þp‡½¸°³{Ø\³UÐ}ÂT]ÍåOw€ìjWÔ”“cŽÉ2—yë =d ÊH¦Oœû¶&ð_¸‘¶g®N×°%qiÚヿö P@"|X~ ¤-_òÿŽÀ#‚Þ¡«à·ÎÓUÍ]_Äk¨’˜’1  õ×\êA]™:ê~f ÛVw›ºÈÍ`?õ0Gt=i°eUgŸàöÀ@6îë’bYMk±ûðpÚ îÆô$öA}º›WÓØ‡¡¼¸c÷Ð’5‚ɦ­Ð3Þý|Fž§'‚a­¸±NE3YvYÉ ìJž-ÉïSIÔÜâ1 Y‡1ÇoÂÌ™Žî¯=¤þã¼w؆möà˜Sšûc× +¾¦Àâ1Ä´Ê€ô6*þÿþf§Z¨|³1œ–Æ ¸¸jí÷ÉLåTl›F…Ít‰C­2Î׌GÍÂ:üÜØÁ^ô’›“¸‰•ž…ÎÓð‹“ÀOs9FM¡7î®ÀÄ2 \mÅÁ½Û‰yƒû³M–Å¥2[J8óƒðr™ ‘½›OÇê:ὌÐ6«­¾PÛŠÞû‚iU¹ Ë.1%]¶K±gõI,"1xâS,Të«Ñ|óè(ùSuŸß:‘19°9Þn¾LåÛX„î#?€¹h3«¥M°°¨w)ó>XŸ.®.xá´ øÖùéYU8mjß%ÄaÐ*½Z“ªŽE^ZÌÏJŽ\˜=â^LíA'x2ìDNIW€yÏ0g“5 ÷Ï>Á²2^ᬃÚt—¥¹¯µŠ©U[@¨m^´¿Ï}YÆ"¦ÂÓOÝp*ò8TôÛAäï‡àYR{ÅO_Œ•÷ñ Çd ;VñÜO 3 ‚†sY¨Çz"®Ø f0:öœçˆ©° ™Ý”zx‹†øwOøq‚ô%na‚›©âWf·g2ù#»„¥³`äMéBGYÂÆ½cfiwÐw&m~=t/È©³Âd’ò#w²¸,í×f¶ºyçyiÎŒúÃÅÌS„µY¡àâ9ðHÄ›jéÛõ2ìÊà"²¸PZKEð›{&}9S(­²ÀŠñ*dæ“ðÄ.€u5uq…µž$Øû…ݪ_и; -ƒá”µ3ñvÒdþžÓ蚎d¨|Ÿíb¦.×ä`®bÒ8滇MÝ1„'Äĉ|ˆ/yU5…Û&;™›’çÊ›¥ÛlçÏ_ð[בÉí©‘·›ì!G¿xWòK uH{>`ž1{d#‹¯6~Æ–Ÿq0~â"æ¸Y wû‚IIâÔ:¸u£î½K†»G.C§ïKØ8]¦9$ÐÁ—5 oo«²wíö`êóSÖO úJ×9å»T(äDÜK†­Ä¸í¦%Á¦@ŽÄÆŠÄO(ý/odOâHˆ,ÿ »ÖjÃ8»4싟Ï5£µÆÝNIIÍçNáò|¤Hû¡,ª;³fJ~À çÏá3Ÿä¥[?zèÐÆ‡WñÙç*Ø'Å‘åòÄïkiláÈþ”½¼Ø¨Bhy ¥Ö(s^Ÿ9wæÁ{]øÞ²‚xÏi°·ê\ŸüÊÎ~…;w5ÞÍ Õ×sq®‚7µ{Ü1VFÍÔ 3yŒí’WÐZ÷SÅ?x®?1~w!Ð|Žã?5À9ÀÒUÙ!@rž'âÖzq¢zHšÊðæ¶½}“&–ØÙŽQ‡o—ܸî“Ñà!óCÙ@„)Ü~ [?ætÁô‚FЙ×U9X÷‚¿§£çá¶W"Ö\¿ÊuFÞâ*¿‘ŠC¸«¦—Ûe›vN… õlÊ)OpŸw ·IáK«F.£ÇwÏ%¯®Ä¡Éžv¼ßÇÉbnÎШ À‡æb­P:Èç×Òœ‚̪Mš ¾ÂæòG83w); ôñŽ}Áø¯c1'l9O¹Ð wJ[27î&M0`Û²Ï`¿E$?µ¸‡·ƒÅ’øwè-Tš†½F‘P7å*ÙðN•4fžçfL6¬Pt²Ž,¼ó^€iz R[qíö›•u oÑ÷l)®‰–f¶çÊÁ÷²”Ëç@µ|È|™‰›GÒÅ™QðÒ$ìD¥É¡HÜüá"*êŸÅÀmBLq¥©©}3µ•ªu(±ÏgÝàŸX?~ú@¨çf ¬R‘&¡“-aóz_ô·ù€öxùëf‘ƒ ZŒ¹0ªû¿q’[¯üœ‹î£Óþ¨1)iw¦héP{r/´Gç€â•k˜nÏ?½® —gàzƒ1ìƒHØ¿(äšV³]ÛáYVXúxž!;ú-$Æ®"2"q®E7U•bËï27'Œ»i•×!&5œäº•c¿óQðè RÍ‚øŸ'³~…Ï&+YS‰$äH±`çø9·2ýÞÀÝSlgE¶ÔxÜ/æB¶ßš × ¬1)é9n--å._m…3ªûàê­ƒø}yî° þ¢Lê:aïÛ‚óºËå»mü‘<ž”Z®úXËý™?¬%Ó962C=›²|OdõŽ‚A"Hf½ôtì‹Çäsç@q×1îó”}œÁzö}œ*) k'rÐhhÊ=vXbïÇTS²ìš§à\ÀbŠ7>© âMŸI‰V°me;QC°#+’н!^i,¤§UiÃÅD¼;s6³â³‡0Nìí—$þ%?×Ïœ-Ù–Ž'„îðvyn!½×ôXÁwmº…^ 1M³žã‚¿ \ä}¯ÏáÄÅ×pCå’ÄCÙÂú˜ü¡~± 8¬þ ª”žàÞéKÉ¥çØé±ܶ&W"ÄÏÑÁPjoâʽVf³7)ãÅ2Iâ¥SGf±ø9öâËkNU?Šn6ކoMM|• ëÝjxÃ~¿°bà ˆæ. »¢A|d+?$} ;7ÊóÅ ^sÿ¶x`öó™\N¦"ÍÞvölÃp_v\"V¹¯EVó¼#¿R.ÓùU ¡MÌ›+,ÉÈ/c’)íˆK¢.ÑvÕ‰¼ÕBäÞÅ»Øíï5…p±p>Öldi³áà½æXŽ'­FE‡!žâï7t‡–*náÎíæ²62«uË0ÿ­=‰âˆÄÝIPíÌE*‡²Ïísá~¹n(†‰%øfs!¦ˆ5@íÛnîïoI,W ¡ÿh%„9n'wø1àeŒé?.‚¹¸![é&ˆsÄØ ð9úcËnóþ4^û ÿéìÂåîyh”ý ·˃‘°;i(ÌšEªÑI} ÑóL'Ãú[ˆÚV'â"±”í”zÂmZ×ÍkpS¢êßàF7aørS‹mŸÊ×ýäÖYÑòàJIÎ1>Íû¹Nu™ˆ1ß]ãðÊà ü4rŠ$xå¢.Φïµ2ñ ?±òƒ¥ªT)y-ô§áæ®"ˆ”ð"å&‡ajùz¿  ’žÿÅÚÛ¸dU-6blÍÚÆ¤ãw#Ú×rƒç9fõ#™+,žRbËp±¨ ñ;;oÕG“wqà²qúí[F\ÛÒ á}#ÄG©£âé¾ÚHª²Ð–üös¦UšdYZ?8g[ÃïŠdúŠ%x¨ 0ø±¿Å•V¸þ‘")³`‘¥(«õ îè…к¬ƒûÒŠá+©x„ÀŽiDt–8=0c<2ùʭ雉²“Dáþ€ìQ Ãæ;AÐ]ü€£)á|ËŽÕ4Ø3 B×ÚÃ~ÅðÚ/ŠK÷3Ü-)e†+NÛÐ|š}ÓRÆuâ5ùpnçÇ=0þéGZµ. ë óB‚9yÐý¢šûðÝ– }ö¦;œ¨k¶u&cö¢Ñ{ª.Æ·Š’%ÑÓ èEÕux-b;0û0ETžìÝýnÜÛÎß½öj?SAÃÙQð|keÁ–M̳ð `-‡S¦³³çs€{kƒ¿ÊÍñôˆlË`Á¬Zò8ƒÌ¦ãEÃ\(8TDe. Ýåm¼®ñð½eD…±däîÖßÉwþû”"óZÈ,ä\=Z±Ø¸÷j¢¬ûEî«” ‰YëÈÆž€¦»JñÏŒ4˜¨U„ƒò±­¶ >Ø â¾Ñÿ­¡lƒ‰—}@eôˆ;7—~2ar³Ë¹¶[iÇâ]Ü!í™h›IRcQÌWŽi ¯úMƒTGœ£uõ@Jàê±Ù$ÿ”4*DD²¢óðo˜#©_1+š+F„6Ç£ê+"uY˜TÏŒãK¦qõÕ)üEk™¾ñŒU‹á=ü1Ÿ–ïÇ<=“ýhû/FJ¨Ó¶Q µl\¹‰”ä÷¬Þh1†üó^¬QE‘5¨[±pÌý¤„þï°¢}î²íá†Öjý_÷lRØLÃòÇ`_ÒmÇAZº2~È­ç5ȵr!UøäÈï tìõ@'¹ç¸Óõ<}?–µå.„Ÿåü¬'Ïß_Íú“ èm, ¬ë Nì$öA²Äyñ¡9Ÿ ŠÜæÄ®æ—¹‹ðˆ9¬»n‰#S¢°òƒ$óûtBî;±¡€wxÝÕ_ïlƯæŸ0vå lšªFdä@™„á5n©ßãýŸÎ™lŒA²g¢5>ç ÀûpÐXDºNTÁVõ…ôB–ûùFÎÇ“[¥/Ñë|†Œ\¢é[dÈ–÷rdÜ5´}uƒªßâ>ù½¬]âVeåñnžÅ¶¶èà\²ŒçÎ<Æ…Áüý]ìnì –d&> *[§^d% ™hIªÓjÀZâ/Ì›‚ .‹7Zcè¤Ùö0>? œ}Vajo>ãŸÙÓŽÄcbÛ6Éé:ø+¦+v1ë^ÞçÒñhäbÎbîiSþF'Ðs{E뮘±üW3ØÊ˜8¸x/òvd@yŒºq”~+ …½QÔþâfWOˆØÛùX•áÀn䨰Á%«ÈŽè+; ÅßõÉH¶=‰·&E^`¸øn…à-n÷ÁÖÿŽÁÓÙÂ.Ðõ2_!M‘‰øºÂé‹"d`K¼Hˆ)_2ó.f¯)xdAó•ÆžÑl¼b*Á~ DRÇ­“9±¾Bè:Îãü|'0í×Þ¨mxÓw¤)âžã å—y‚´"/ø(žwÿ9»çŸyGLÊøWC„íh‡?Nx_ £mTê² àäŸô”™·Ék².sËçi½ø.ç¶ÿš?ÊõÎÓÿ4Ìš‘h³ŒGzWÑE>vÈãîpÕÛ¹EælÂÌ8zŽ®ïx —jasÏk0tµäŒÌÎãæMZdEM;Dì‚Õ¢ÑÿA˜´”ñðŒC.³€Ìo‰(æ7‘¹=AMù:lΆßÔ­þ$ÜŽBÿ¥½ð¨¾‹;³"™ëIO‚Ö•¸_û@¶±‰’âD"1ŒkzâñÌE\¨Þ„[vUr+.8ÀðIebhKî]•À)i„¤X¿¦9,³2½À囹)¨ËF1æ ?ãþãÑBGÒ3æ(Iù"ÈojæìýçЖ‚dú¶õpÏw¤‡rfÒöåϱ={ûð>Á®QýxÌÐeÕº(³æ®‘y¿‹ÿ œí\°6€0ˆaKs[`ç¶Ï ÚÉ4tѪ|´vÅ¿¢7Îjà#El]æ\ü9.”4×Iáìï§ùßf ‘Õe8w;òµä>l¯ ¢Ö~Zlƒé0¼tPÅYæx©´ Í•lˆÚ»Lx© ÕF¦ÐX7-PÞ€²4X¨¯!Þ=©OòV½…ìM’hjÆ&ÖÄÁ‡$#Pr ÓÝùÏW 1ÿ±]ôâªCÐw{*™Ÿ·ƒ,Qׯ4¾ôÖ»âÖ{¡Ä2Š;wöì Nmóy£øEOò4ïµh<‹)¶eÓå\ (EŸ ›«’û—LHÒ‘ÍÐ4ø$ÝMùVz߸jHb>#ÇQbÃ.zÒH¸Ó‡oö9ò‚®vÒž§gXËÙ7ø¬Úþ nOPf«dŒØö¥iäÈYø1©k¥×T¦)*ÃùïAe:ôð²âZþ¦÷G™Úí9øÃm·Tä8×vè*Œ=ü’ÿ¦BƒmÚ—ˆ.ç’ß¡“Iï¦0¸²H›E'„c×±•lÊÎ%дÕ•ã0!½`æ«-I ï'3>}âü?=Ä¿ç1þ{ðÚªCá'˜ Â^ºScã,†¤‰‰ÔïÒ6|‘Ø ;'á} eR“Í<Èk£ |_£ÚúØ+nI¤}CYSr7¾“+D±©2¤là9Ö 'ãöޏÔ??{킯:.Üò¿þÐ÷éOßZ_»W›Ô(Ü.j ×§ãš1©xëÏSL³eÐ(xоoõÄÉòÌe~!ŽÌó[û~0-Œà‚ìúñœ…5¸ÜºI 9’W¾[9°Lf2=g=’eGnY’™S!ðu%J9üã*fÀÒ¤Dj´Ã³Tïà+ý¼È=¿²“'»ŒØ—úEdnÖix—¡ÏÄ64¢ÚñÒgͼ†v+Ò}»Oê£~EÎKg,˜xá–jþ¦¢KÿódæT6àÀ4Mö)è/d¸½£“Fäý… ¼s\€ò§‹²Yfõ´>؆Fgë³5ªe™æñ°ñ½3NúŽû©ŽK‚ Îùpçx0L]ÌMêßDJŸþÞoAŠ×â¾OF¬´, s†^AИKܤ'<ù Y|)JðÇϾMù?…г¦löÒë4õHSetÕVû›‰“9ËÞP8)°Wöâ'Ñu ÔÿÒCÁwxnKâA¾YŒ…í‡æ»NPôÐ Æ?¬ û„HãŸJèì%'ñà‹³àôTbÿŒaoZ3Q%þ-¿)4’©9ÿÃ;^øÖ}µ[Äè¿îsÔßF‡¦ü¶#S¯Çrqë†+«óâ¡¥V-_ ³bÑÜá¼n¬‚Ìi“ý0£kßãÓ4!²<3‚». ã3ðθüòI—oëêâ÷>CZ“<{Ybuƒ?Q‚6ýW΃RîF:ůk ¹×xh*uÐEœ=.œÓ‹gâJl òo¥þ¬QÚÈvÜŸÃ{v& FÜÿÑí-¨Údß*ôÙÔøNZxèlˆi´â/Œ7gûçb¯/ƒ(ù÷xe( ES´ð½y*TH¼ý<¡IIGëw¼Ãs!gãvaÙÕ”øGïzwbW,÷— Ý<‹h6ÞûÖ2n‡ÉÆ4aæT¨ÏÄëÒˆ}yÙRÖÁi9–Aügcö=ê }Öys¯»ëzìáïì Ø½!žíq˜x¼Šå©¬‰@Ïs•hÊù°GÛ=éXŸGøû–Ѩ®€—¾`ù•y¸R+ˆíËq§"lŠ~âUºÍG“ŽË?k r×çRçÐ|Tõùˆ“F¬p®ÔATê±å½ ²a+^¶Á»°(*aË#ê¤ +S›yâN¹±/ìxNÿÌa™‚(sS³bïœÛq·ÆA¬ÉÇásyÕÆ£Æ Zr2›0é@[Òz{îÜÍÖ\›I昹ӣȭ/Ð%Oï/¶ú~ ¥ÞÊò7½Å{ûqaàUìŸÇ… œ¤Ó/9‘;®¡h)νxú ÿõÏ…KEk1xœ'+ÝI"T™]ª²¤~ã~ú“÷fk ‰­°ñ?ÐËáÁ °yè@ÚÿHáŒýxs#…›[,™î£!ÔöˆÅ÷ XÅßb.äÏBæzè ,Û´“é¬N‚s¿'À’É`öÄE€É? aR Ýà/Lì³ÌapÙÛêETýÍ@뽸V»`®×;ÞRË˰©G”j á=›™‚Ž|ÈÏc.ìs¯9)­úǽ‚ù }àL)Ø‘ûNrߓߑ°ƒsÙCõ1ì¾÷tòñ‚ Ôy«á.õvô?MnW93Û)ß0oy÷ï§fîsÙ%<ÆŽ³&YÏÏÏq»ˆöD<òmNŸËeñ`]~0Ót~LìC•H÷@ºÍþsž³›¡Ò,¶4”I•þ›‘7ø¬Ij¶k‹ù©z‘Û¬¸c›ìˆØÃžvÑaf<Á—å+I‘93ÂÉòWîlÉõ«ÜסæêŒ†åLˆŸÀ~«Ü"r…#lƪZÚ´všIrÞ'¾Á¹¾Él¶ðIøÏwù?²ô<#˜øöÌ~âP)ꔉ»fåàUh¦³³K¡u‚Yw9 F?#OÉ®þx‰·6+‘½Þ]ð¦ÄˆY´Sä[øÙ»›zKe‚ŸŠ-÷ÑU[øÓþÐÇõÜdv'µ î¿Ò#—ìû¹7u6²_”@i ÿ¬ÝmПs>Á3l½²ˆÛè\Bó/ ¨(Ô¿´Ä”Rk¸À³óï`×YG|]ùcèøyï0(«•›=è2©øïŒ=V !ç#ŽãžKƒC`÷hwÔÏ@¯J¸l±œ|úÈR¿þ¥5ùï_ïÀ¨¹îtζ*ý`+[¿ÓQT2ŠÜÐ=÷ef‘ ‚¬:Ô–­PI„ÀYŘ.aKÖz Ãõe¬M(ÀÅ'¶`ĺ^¸—ëGötn!¶FK@ëÝ8Ö2v"é;)Æ|WÉáfÇ+¤à{%&ç>‡ßÓsàâ!I8xÅ^‹ˆbç½$8(ô.­R$ÿi§Ý—} ½ ý¼¼óh¡›7ZÅ/fÚN<\¤6ææ¥ÀñL9øX*aΧañ+e²åz¼{}_dJÂë—`o¸óìkGÅA?ÒÿÒÜ.“fŸ\óx~íÁôÇœzàî×¼ˆ+~'óìpe»â'‘ø»WqükaÚÝí%ÞJøíò¡4Äö7–/¯Qù*üÞ|c­DˆpúYÌ>æOž…'©ƒ\­L2Ìܾ‚ûªó™>9¼ 4f Ó.iaÜý« £”ÈŽQþR9ÀåúÈrÿy/Ÿ“(¬LNè¤àUr®ì"âñv=ûĹË…PéÌNÐ6xÊÙÿqÆ«ñÜsã=,ºÈŸ-Ø_w­ˆ~²?~9ç†Ó¤¿ã<ÎÎÁB˜úò¼ñ´gdÁhc*Ìú6–XO\ŠDûè§rg)°eª!ØL <§§B“] ¯ïX3E‡ul Å»¿ùü-üÐBó,YŸêG$Tbç÷OtƳîÔBNüØîÀ¢,4n4Uc×·\ü&„óŸ9’ŠàÛà>þÛ¿:‚)«E²Ów¸´ç)ž,iÄw —È|×ø>QŒMáæ0˜ãÊNû/‚<òŒ;„y¨²ü›»V½m8Å ¦í®M!x|éK÷‚>QL`Mé_ß¡qd›Ò.6W,ø,‡­‘Åäí>­p®Ãí³þPÏ?ùò½ùC¥ó¹uç­!ÈoÊêÃXÒ½Ýì\b ”K±c³&᜖"vô‚$»¯>ž¬öcÓqï?sw?äZ¯Ï'>YnD÷Оѹr«Ý’˜ rÿé¡Wlc»Ëñ£•ó/Ã4¯‹\Uã4vŶ«Ø¦Ì¶­ùÊ {>Áƒ]pSé(Ù$ÔåV«aŒ°*ù—°Ÿ½Í}Žz+£ÙJ•Xfu—­ØqœŒÆÅÏýÛÖ©’ä¶l q2ºÈö¬c3î’º=_PrŠ9 §Þѧ¹Ð–L#@ƒmàV’ó ?h±W}W¥BO…^[Oóf\ØÉÍ–ý‹*½·1¶?€þ§ŽÝ«Ï6èÙ´tƒ*?#þŠÃPMlÕ'2?G*å,d+óŽ“{‹aÏa&{$ ì¯[Ó(#5rmÑ•óQ/¤ Æ.þ±{´°æBg¸@‰¼jK'52íxy£¹&l‹ùÏ0vÏþ¯ÊI0¸)H±©€_ÒÑ÷ÙiPÈ5Áô1ùð:]…$s{P{r)v9)±éªÖ›l†ÆEå(õhÍ5rk'¬¡/BOƒbõ š{o¸l’âK­¢Ã’`Je8<0¸’3+à¯_ü„ÿy:[mMÄPÏZ^tÆx°I=ÂãÐz…±øƒ ›~Rš%8êÿçËÌ•¥UñâưžÖãÐ##O¶,HFC¿[ø6æ1l˜‘À ¨‰IüQêÈ5Þw[/ò,Zž”L߈:DZ+ê͸?A÷9j›.‡1{j·dž/*"‹–OC\£3 ~g¿=ʈl1X¿2\(Gå ³qûùI¯ïK¾D"oª7þ4ËuL|€5YšøgÝžüòEôèd¢-xeÇeà…ÃuôÏjaø'–lNý»ó ;«Jd®ÂûW¹ÞÜÓ¼ îQ üô"SX^ï::`ðÇTºlO¶7:²Î¸tˆþp“›`û Þ8«Þ.RêÔÎÖþù¯$àÍ$GT;Ð “§'³“¤k¶&xà…–>áC%7òü·ùh_?»{FÏ‘¹½¹d’» ”T§·<¾côê™ÿ—¬ ¿„µ~ÅÔžÿ¯m" [Ùp}= y›è’]7nì/GÉPoîvÞNœù5ÚÔ¦E*”Ø`ó]|?Y“|H§1kØ“.1¼^>k•÷‘•¶D,e'+l+ãÕ°oK‘0“,!ÛÜi W5¹6–#–«¸·²$}vù5ô64úpbÆÅ§Jm‹JÈšC swÜ"Á_[‘•¿t#…|Qøñš>ßLR¥¼˜’¬¬)¶d÷ÒÑä•!n}·œŒwOáÌ‚©-:tÛõ[¬r½NâWÌ%N_ÓÙ› GÖ¬Ž(Á?Ë}qoáìo +Y”Ÿ+{T(†šïoƒß̱ìÕ1GV~½#55Øb?[²H²—ÉÅ’1Ûâ0¸¼’ü"CtLÿ0ß<Ÿ½Ž¿£x6ĉ œŠW4ÜØ²¥éôiù,棺Žv7ÊS§6cXv/¤…Ń•äÝùb '¶M·ëdÝ«7³‡Š#(ÿg98‰d2Nÿz—~JO…É~†P£ý´„æ±'yéXµéh5jÐ}R¬ìo , [jW3l:kÖ˜ •çT®¯Dò\Çû sÉnY/ôqgwTãÎtEšÛü7©µÑW =‰é}ýnàŸCÜÏN‚²-  ló ×­ïÛýø¢õ.\¼›Nµ­ÇN»TKÚÂl×db{ª |ù7ˆK®Bâ#}îŠy¯NMçéIÛo›àîpî+òF³ËÎL$°KSÔèï{rlÌËj®ðQ,þeÑh{ÍîO›Ár.Ai˜S{X ëWíÆŸÛ‘œÝÆ‘N÷§ W0Ÿ/~zû<­$.Ä£‚ž ‰e=-&$ v‚ŽH¢ìý0&ü¹Ïß®áj„ŸÂ‰… °Û§qÇDön…i=ÚŸOÈ“Gc´È­+p'*cÏÇ0í–D»aåkðÌ\„ÅìÜÌ —–ƒI\}qr>˜Ê8ÀîÝOÁø¸:œ¿}·±GV>De^$Yëú×µ/FŸ_k°³DGeæç/ÀŒÒŠyÊ›1iK/Y »qó©I ¾V’¹ÖO5ÄñÌ€YlçóãÚa¬êL¢cð‹»Z|“â˜(.²åÌ×Åð,tdk/ÛÁ?׿´r|l>ÃîTeöe™ì;óJ‹À«5ɼ÷¶âÓšìí»iØQ¾œ=l^Îø'¸ï áMÿB68WÍnô†)‹A´¼‘+ÙËÿ´€Þ£yÊJ‚¸×m¤?8õã§@j¬0Q9ï‚÷5è½(U° \I· s¿ªoò~Ì5G±k×ÁÎ},HO[ ¿6Á«ïn(PýÓÜX¬?IÌ|Æí„³nÞÃ:DÛS€?¸ås [æ¥a5öyËVú°ñ.M^؃ )sÈî¬pÞ9(õC¯§Žlù_ &&$É2?~Å ¯64×–`Q4 Ù°O'÷\‰fŽÊ{ؘ­fhÄS„x‡…ÜŠÃå0kÖ/Œ¿]€“²ñHè=nõþn0ýHX¥æLÂ)—Ófêe¢Š¿êú`ÀÑŒÕéל·hªš}??R;916Ußfþƒõ2ÿ¸á“±\áMQ°›ûjî…Âã¾­w [r.Ð^ň‘`ÎKí7€ÚGâ¤C·SrÄû†`6á*«Z™@†~ÆÑV™*ÅS§ØŠ:/R¸ y÷–¡Ù¹päž]!’kO㟖¬`m/5üÌZm™`"O~š;ÑD+ÄëF-jMÖ¾Mdµ™©\F ªõ&'aãÎY3i‹*æø°3µÅˆ@Üzè\hÃ:·¤3粩ѥSÔÈ2‰g!pDWý§ kÇk°'x)®ÉŇ nÉúh¦óä5ªNÞO¾»Çë1Î?˜5Ç£“÷>Ò6‡š‰1õ¾«|ßlæ&¯¼æ²ÇÉD"§ž÷ð#5¶1e/ô"ðÓ6Cžú:a¶è”0™yì»x~->y;ˆI÷n@ÏÑÛÜ«½¥ôÚŸ°ÍÔ˜Õ/WD(DèPè®BE(¸½Ä ’ÐÃ’ÐAžúG'±t+%HÛ„¦›Ûq4V6 ‹7ïS™Ô{œôİyÂ,’¹ú2ˆ€î4a–´I’p÷&û¦qK‡w®{ I‡r²“Bù&FkÔÆ ´æ\ޝċcHNÃòÁº„–}Ây_5Ø® •(wæ ”ÌK!žùb¼öŒŸç©!»-+‹W“¼á³”.H=[ Ï·éáú"|||2+k@‰¬£ìçÞ_˜(ZÈõæ"«‹{ØØ|f¡ŒêÁ‡ÙÕšøå¤*Ñnçq—q­Ú޲¦Õq`ý%ËnRx|~yk[3‹™¢$³p9)ØíÀùÇuT8Âì¡ÀJ[Îlž¬;WÆ]}äHÚˆóW憮Oaß/6üZ„¼ç%þ½ñ8yU([~$ ¤¾M!KüÎ_s|Ÿ )ùE\”V$œ' rî½ î"‰/ Q@ì'%>ýÑ ýün…ܯÜÓáSÐ&T ã3u=Þý ×uñFÌûx2®(‘"·êBpÕÃ9P.u cöîØ¶3|—æ8.nŒ dzxàà-þ´¶:Ž Àq»èë rd¢“7NZÊ üÄ¥§cx>Q}Ðÿ¹£R•ˆ—¯;… ¥ç ;Ûömx‡½²ËШe÷)NÈ&1¶kS(ÿP'ÆË³£CãP\dþ™îGG’´Ù5$Å“Tqz½Ù&RÓ*×§Øà†McIÉÌlxÓ§ƒ² þü\›3ž¬÷ÏNÖóê)x²âÆ2', ~=9Î=}X¯Ç[0ÿfw–7å*wãö.T0½ þ»å¹ú#ÖDP „¯LŽx ó§¾¢[²¬¹?(®Ý¸…mÄD[òÙ>Ï:ÒõƬu2oôº:n Á¯ì%d&Cи8Ie²¥”YÒª¾ÒÏ>轺U-°T4_KÛãÎÜßB„Ë6&¯†°ÓµGqrF?ÿSW |EÝ{÷ðRÊ 4œ¶‰õšm£#È®qkG×t;ïØ°¿7W^‚Ïx`Òp=§|”Ôv±·ßžðÂå+pÅvìiãɉ @Hå1<î¿Þv°P€ÝòÒeÍßVR­©[¸ÿÞŲî€õ` 840îµ!ÿ͵xαå$nÅÍ^'àé !â:˜ u+ ÈPÀn¿°&™sÓ>èûÃÙsÜW§ß(›<õÓVCòÝ+ |kJÍÙ‰¥V9øSÙ£ƒaéMS¸¼ø<—ëßÄÛÙy’Yü¥ žé“>Ëz:¹s̸´‡o“|·ôðHuÆ|\Ýý¯€<ù‹2ßoÚ9ižÀ–?¦øp‰.ˆ³Ó£8~SÕI1¤6ÉÒXq˘¤ï¼ —3ðØ¢(nÁ΢`£ö›ûC†™„þ I¶&d•³,y£B¶.)å”6 ’U-7o%·è©%ïhÐ[˜*ÐÊoØC0J œ S%²£p5u>YÂ-(Ô"Êø÷÷\‚±ñq $ɇs«³¨ÖzŽÎð_aŠñpê°3á×qù…XÞ«&ôìÒSÜŠœè~},¢±JZÌKOsŸʰ³mVk|‚^ˆbû-áùa+Ì;?÷*’•×Òhé4ðɬÆç~k¸Z0„ù}ÉÜܼ:ã±6[.‰~¿'™Ë€‚ßTâ=mˆêù‰±ëeSpb_d/À zÝp¶ýzô=D÷ÊžÖÍ5ü¿š'Áq÷lwý˵„0ª öÊÕ¿óvîÃ[» HwH+Äרá¶ÓRD¹ó0<Û7 R¢9R¯.ÁÏúø ²Añ^$n} ;K°µ êAè‘ñú²„Õð_r'+¶ ñ5„Èî°°TÀ_+ä0øæ/¼-êK\©ÕÂ/¸K…šx‰Ü ÄÖ<‡t/xÙð wi©A1;øeñ•6ÿâ 쾎:—¼aïƒqðñõ"&â_Kÿ5ôò&ŒæESìÄÖаøΊ«‘µ/–@¼ \î}‹JgŸÒw¶=ô[ÂÛKÜT/"°jN}í 9Æ'på>4‰U Ó¹,¾p1w¼…¬'¡ˆŸ['JäÎá§Ýrä/‰ønufþ—òA~æ%Ø-*HDVÒðŠvz>ü-™^ŽEæ"\©¼{´m;µ¨ÌH›UK[ a¬q\»’ Ê3“ÍkÑâW(‰ÚúG޽©ŽfKmGkdÕEŽïZÃã?ý‹¸¬’Ú0æ§ê?%§Ò5Q-_‘è^‚uWWÁ¦éjø^`ÈÏÕc$˜vÞŒÀÒÉáÌ;»€ï|å†~Ð#ó–§C‹\ü=Õ a—>PýCÏ`Ùô ‘r?­³§6é¸G/9ÁÄû•ï‡äÐry šçÂÚí¢¬ÍxæWlMé!*s#•7ÕÚ ¿üè§{®)±S1ˆ¼¢wY9^=Œx³H'(Z38¾g ]£KƒéE{C’›¨ŒÞk6`ˑɖ Õ6 Èæ_.&©â¡íñzyV.ˆY:°|}+4Z~-·m†¸9»Ø¥ícYã l Dm®Ä¡Ç U&š#Ævž0ö,r›ÿâëÙzÌué ^”ùƒÏNˆC”ÍVœn¾ž™I¡KÞðéTc¼²*9Q%D;m¤ô”$ÉÉ”án­ŸK/åGB®à{pš2 oz€^Ú§ùSÆåðyŠÂƒQü]ï+÷î¥ 7l¨Ž/? Ù¿{‘«pá_“–… \‡7J‚¹Éu‹è! b”—DÏÝ»Ï1Ô¾CŸ…nÞËfõÉÃý[‘ðÍ8§ë¤ò×βz­›øëþ{úoGõú‹ÚÕ·ñB ©ZAÈ‹5í\¸¦KtAµµ²ð)”‡¦=ãŸÿ.>¢8µÎ<H³M;ñ¤s;”*ʱª\Eì[–‡-&ÙhÌ‹ÆbÇ[˜Ë©„O!œÙŽÕÜ·‰Ùè°‡*+ ÿâO¾Múèyz€ÄØó÷ð¼¬W]¢\HFX:ާu¡ƒÐà4ÀWß NSa¶ö;PÖ¹ŠÓ»NÀA«s0û‚ãö>âWOQ&ÖÆ{qÂØNÔ^¦JºŸË½¢ æp7º'ÐÕÏUÉaã8žþ»LÞŸ´‹`û­ïëÞàMSVÅÃ?yŸwÈs§æ-àŽÜVÛÜòDqgð{[86éCÄúôÔ® 8Î[`E÷£1ø†¨å×L†ÞØëœÀÚ6\²æŒŒ‘'Dn4.pâøŠ¢tƒ² ó}ØÂ+ºmÆž‹)ÏñKÑAT6ÊDz5v¿dð̆Ӝç¿Pi Ÿ®Âí[-t×1®q•.šÎ™G8¶ñœ\ù+óC äàI´1¬Â¥áãØ< e[#Áld±>âÄd;ì|_+Áä‡ätI9U‹XÁ«çLÖ·§¢èƒXz¾iãsª^ƒ{Ï&À¢¹Íp||2>ŽË`%k¡o†gÞÏ5m5%}“™Û5º¡ÓŽÄïïň•¹ü=ªrø)S]»º ‚±µ,}2pìÜÊÏ-@9¯‡üðßMp¸Çê»ùÐaè„gÖnÆñm ;cSÏåœ.c`ÄÀ¤> “Ylò5®~áþÑ·Ñ`έ,›Ë]^|ö¹/!"cs¸¡Ãaðt —»h§ŽÅ™HOjþÁƒò—FóÂ|Â+Äá·kÖdlÿsT»÷ ÈÒœK¯°“øg âOmà×äCäÞ™8¶8Ï9Å;ü íùä€ë0šJbaʨêãHÔÖñ08¥W彤Çóqëúë4­ë·uý4ÞãI°¼³íÓX˳+øi~;N6í ~ª:ði¦4óHñ‚S£8†§‚kN‡¤îŸà3¦´üäˆhæ1È0Q â;8þ—©J¤ZJIìSe×kßÑ Fbtn:v=›È—³ZB ü4È{iRÒOvû.†ûse 3€‚‰€’ÅÔߣE¥ÏàÞ ÆWvJ†Ö+ºd5A³\)Ï5Æ\‹‰P²s)ÎLª¡ÍÂlm§ l¼Œ§~g@ó¹HÒmcËZãàÔÒ˜é)Êœ_B5|¶Kwÿœ òªáü£éz$biý¾r $¬Æ?©ç!Í\\§s3I îŽÖzè6ÞŒ¾Á) ¸€°ì š«z,ä}àŠ<Çt5ØCAp8¼ «và%¯ 8®‹…-ñtùmH’n„=»[ 9X˜­Ô_—»Ïài“Vºë» yo çîòþQ`3‰]¥1Ÿ ßǽÒh:8ff´¦áU+I_ñqrÎè„h÷œ±æö99Üü-¹ƒŽßà€Àx–è¥ÏlÄbP蓜/̦¶É‰&+Æ$³é䤎WqšN—+g‡Ñ‚¹D†à·­¼õGÚ9£b`;xBäì¦ZÞ¸ÕÒÌ`Û û(MZgÕBo‹5™pÒúbȤ¥¥ »J„­ì³‡–1·à¡ ¹7¸–¹I é•Bën¥Òî‘÷8¨Q?_aƒ÷ö÷ÅV˜ñý ÈÖqwv…@™º8ŽŸ>÷T.ežDáÀbKøÿ‚u8,‚ZH¢Ov¤È`¡·S7êÃùÏ"!æl^™W ¥ï?à3Kð÷’n E¾ï»ŒZ^Ú8cÖ.Òþd{c8‹D/öäöîrk>îÈþ'øÊõÀó}'‰t„ ÉújÂÚåìpZ¹Þ‘›·FÛ%Oq໼š ¿z™±ôDE2lÌv_ÓeÁ[$¡àrþš#@L>ÈÒÕ‡Qà×.ªÉ«¤­ëWq«£–¡Wq'\ב×}˜ôø¼‘@cŽôð®} `JÝo8Á󙸷.öçâKÉ×£µÜ‘ N ÏeWè®bÜ›nè1)/ô,Ã.ãÈBçupA!ú•.³“ß.An˜骹oÕdثٛ¨·‹4qÓC:ÓÃ1&)œªÞ¤—}µ9ÏÍ&ìÄâ6éÂÝ c8¯AÌ¿¶Vf̺ šË™«N¶ÑIçÂËzºâ¸+¹¬¿Kµ4§·GÁ É8ý,kÝð¼œ0‰2{wãϳŸ5õý¤ìE¢*NïºÉ J•'$‰ÛòùÜät¶bJ·`Û“¥¨¬ÝGM§²„¹qœvÁ9\:\B_þúÉnOà»[þЦM__âûî­$ö¦/<¨°Çóq?èK†tËÇ› ÿ7†û‘’ȉ²c_tÉB4òîà4V÷Ø£Z–âPv ßkÃævEÑ¥7œÈœ¼þƒ‚³ç¨EgÊíÃŽŠÃÞ|îUc(»ž/ÄâE?rš²£øËð" tNaÜ¡5DÝ·LÄЇ Nn7>• gg=ÈwùBnœyfüw™Nˆ½©FxëµY‹º#¨H>æ®LuÇ·Û ¡Áù8(›žíôƒP´º Ç…]kVâ°eï‚”œ Ž#+{ ÐV} ¼4MÅó_áíÃHP+";>TÂó¯Ñõ£:35þŽ…®Ü¾³Û0´¾Ž·ò¹)±·9‚z2¬çÜ ½~¯‹Û[Ôa—S#ü‰á¸† ëwþæ·äü„ƒ§Å™zöJò{åVæ^ü 7Kãî¶:ÀåA,xˆ‡UuÁû^ú¹ÄӨֽر¯ö»A~S9ÚX¾€„¸Þõ.Ôٕɹս=žn7¨1 ‚‚&“ן²ÉZ› Dð*!_¶tÃVé¢í¬Ç­Ó=̶%ë]à´„.úF"ô'­`Üy‰Zø ÆÆþ´`zíäóþHØÛæÅÊÇ“­žÂ즰1!üzjê˜A|L4‰ÅÂÃLòÜöqÝwîÝUer÷K šV?}=½ê¿EH¢3½¾üi× ç…,€‚–¸ù 8 †Å‘…a¢@ó’ù'·‡Âh?,žÑ‰Ï4ç’%ôI¾ýt·|þ[áý™0bßXέÐ ºgšà¨ç~’ÿ%‹ÿÚd)°‚"|hý‹þçˆïI#Só>K>ê_ 3Û6r [ÔØÎãÊ,aé®c;zÞÁÞn?bTV…«ìÏ’ ?Ø«/ŸùsOX@xõJ¬Û¶’÷ÃNW;&1ÙŒ} ø‚^ÃQlÇx["/üž¬W…‰ 7‘•ÓõØûˆh0ÖƒÕ?re Ò&0WG"¹Š‡³§Óhû?ôØOuˆŸ€’o>UîžØˆþòÈ)›Å£¿•¥xog?=õÛ% Ãã ç1z_0^*,Ãëù)÷yo?C7¹‹ ¼§ Û_q29îP‡]œöëW¸bî,ìä*qÑø!ºf§ÿâeÍ0f|nè7%žkÒß/U˜¦òa²ïk>MxìÌ&Ž÷!ë‚YàMÂO—fgÿ ÁAAöqæqè³BƒµÛ`RýV4¿nŒ-ïƒ@Dg”·–ÆÓæí!hùØ¥lÅ<ùaN©¥ Óº¸Ä°³˜ú{ ÏÝþ+¿­ÚxóÛ¹áÕXšÅw4˜ä=Y¢0w:Ñ_üdŠ ëÃ{8\bÃÄ5·À[_]r xQÛþ¥„TXl€41¼Q‚ks:y©5Ñ„ÿã¬LðùÓöà Dn^ÙèÄôï~)û˘™¯Í6_„üƒ¥0ÚFçva;»Ô¡Iõ è;‹€œõøÒy>´•SÓ½°¸×”:+ª@—ål¶ÿÊ/þwθhúpó§ƪ±9¸ðê2Öv*Wqf‚š,dò Fža =¦€è\96 í!žI£ëNIò˜ÿ ·íÃæeNd­fh»3ž4>Q!ÎçÇåPèi{ÉnÙ‡vyç¹£ÅJ$hÕ mVgîÖ £ü j§ÕAÀ?Mò^;–º´¤ãä7!8Iî—''r à¸X1”™ÕpvZ’ ãq6nF-"×ZÁž7ú9˜ÜÁ.0ÿptÄß‚ã±ËÔ1î ºmvÃ-acH²ß|0¹C³4óð¯Í>ÆÆ 2­DsU×¢ÿRö¼‚Þ¹õfh`õµu8sG(Z­(†è}ñ§…Lì]B”N¯"¹¼GpÚ…j§Âbƒ .ctþŸ‚ÒdíãiätA)ù4 ¿²"÷ƒûø’þw9å‚—POœàsÝ$¸îr —¤©àÒÆzh´¾H#‡ÜÈ»O] yÆÛ–Ê%•UB¹ðfH†Ýw*aŸ™äsØ{¨ØgIüåB)ïÀÔ˜)~Jòø>q#Þœca‘çÑ?^JõØk_]V­dÇŠGñçÆÁ&žhΧÊ%ZztsC.jÝù‚É¿ò±ûv9ºl"‰hꨌ*ýŒæy Ñ ª²ß°«[›.ÿ8ƒÄ{jò–çàÖSn$^ø9ö_÷â>É—r|‡ÃÓ™èùp7î88óp?o úK¿p,n·çž0ƒVX^„ŸåeQëR2çñð„j+@ù#5öv1M{†åRøf~-¼·wnà•ñãÉ»ßwPü|š· ‚×I[â{[„„Ø[²–åKÁSDЬµ<ƒQËÑŽoN §\¢«_ Áâ£môï÷HÎYH”˜6vÁÐ6'.*?ž tíâjºê©uŠ™òwªŒ=Fü7ßáçmJÀYg HÔ¶¥lÓE?þ‹nq|¿ì,¼jIb[¸ÊÔf,2Ò$Z3Ç“áùväÝt_2”tÚfNaÌebxe¯>‰Æ>Ë]0 ¶V«Œ'«®!r]êdù²±ìùf¶D£†t·Šˆ#@ò¶¹õ' ·å&có—Å´¯O›Ø¨y3£[ð.ñÖÜSbßÿ"÷½t Ù5O†Œó ¡—ÌBõ_­àY(ͺ}5ÙáìháNVE¼ƒL[WÒôË‘<Û’Ë&™²6£œù–ÐŒà°P%ùçaÏž‹›qºóö3žÅræs{3o¤Àûi89¿â=Wà{ÜÕˆâê¥ääC h9$Ç*òûy¹±ýx{X.¿x’ rܺ¸Gx(€¡ÆÝ¸òÏ7ŸÂ¬!(8‘âê¯æ Òó ÕŸJÓ‰P’ŽEåx£Zæ|´¦B/æ1ãßyØøn”©>šLjeçÃý( ¶ÿg4qìøH¬”Ä`ŸA؆_PÚº úŒ® x’ãUÒlQ¯ L^J*³n¢m|Gŵ5¶L¼ËƒTŸ½DZq¿éQ1cz&8ŠS±® Q7ëá…—iÿ=‘¨®ûiÇMê„íSXèØX¢á†÷‹ÃQ”—KÃ߆ ‹ƒXïÙhìЂ²»«$—à¿Ú—(ЬJ*ÍÜ0û—(óò,à{u¥>«P~`X®Ÿz2' zY<Ü“$eˆš¨yñ@¢¹ÝøïÏk¸pHŠì]ÉWçÜᓽ'Ãê’V¼]üƒþ¾¦„áÓ‚ØÛdQv{w|š¸NÞüƒZiÊìáÞPÄj$ð°%5ÿ>óšðÑ¡D.áÀX\k~õÆ+•|s8Ê/ÇM¶Î¼#ÞÁ¢¯ÓÚù ‡±?>ºk—ÚQº¬×Õ‰}ý†¶1[釿‹‘“xÀù>¶Ä¶Ô\÷§ÃR³qBQêM‡öy‡yY]O°˜ÆÐ¥3À¯¸N»¿ êVÏÁ‹’céX¡˜6G‹L üˆïÏÈ’²Ó‚´µ9 nÝ8JŽÍY þÖlÏæuø|<Eß©GavÃv˜~Íœ-óÞËÕ¼¾®§’!þŸ9{z[ {ÿYóö«’Xb$ŒIñòüç‹3å™ÝŸG `ÿªSæðg®‹ÆÄ9àZ …|ºÖz7åÞ¤ÃH|($Úüþý–Vòà{« Ãõ ÇíáU¤>¥*þW°e~3޽ZoçYw53ÆáÖvb#t·% » SÕxöRm7ºVô°ð°›˜s÷È©"¼0ïã†÷´BÚj´’oÃM,œ\¼tS‚ÙÎYWX.ØÏ÷vÖœïÐ<Â;eðß³x§Yä"ü!gAîG Wí2Ëg™*Díîêã%„pIžEr¯.]Ë,yòl½+ Æ\$Kò†©‘ï62÷_;´Š f$° {&{ÅŒY¦ ƒKŒ+tœOÅ·óÃX¶ýfSöymXn¹#¾Ã¥+h’鹌ұu$Ÿ¦UMôY탓RÀãrŠÏ¥ëìƒÓ—SˆÔã8¼4p›wÜxÌsšI²Çàùé’ìåksäY–Óõv·¡Èl,Y#v·Toͼ”äAßOS#탟±.(§¿Ö" Ì÷‘[T­˜³‚•t‰Îá:Ëo Ï©Ðø•Cð%§”U¯á*9Úpë+6ûà2™Wø¼ùÿƒ­Î ]ÊD‹-¸ßÃáQn'hNå±îh¿0™3]aÅD6Àßw1iź‚[ˆç¬ÎaŸX÷½x)qKÏ… žÆ4Îôî×,›~ˆË›h¦~Qe7(·A¹¨û&z‚Þ|s÷µáƒ©W¡§e?gVuŒUý¤{çodkç;áOX8ãxz ÂF¼)åWðnò xøq*sPA9i\èÞ„y+ßÐÊÀ",NOà¾ÍSf&B“ñˆøg|6ŽÍÝ™r{ß'wb£§ÈÅüƒÓ5I\ã&¶c½»þ$׌<Æ ‡npû”ÅËŽáç}¼d¸–¢} y‡ß¶{1àç°]é*Óé]†{+…ÙÓ˜ëœú#}%4‡´\ƒH¿T"cÙÏ] š_)]QÀ…óq‚ÂÄ$«O=-ƒÂnDzê~25¨…w*éXÉkç¼+Šød—¬óœÀ—LM$o–]ãDŸ÷c®S)$^DýgŒ_?|zkEØ÷rö09 ]äø\ÆZU¢²8„ì4ŒÃÓHj­>h­ÌÓ?I¸°å8 ¶¡_ï"»îM%YæRd¥Û_Þ¹–:^º—5¬¼mË@3ŒüKbTN…šF,U8ñRt‰ Ä‚…FÝŸKž|¦FB<îEÊeìš› ½û8¯ šl´Úþ›Š6ÿ@Qå»à4P¹¶Æì›K$ÜÔî^]°!³LÍñ±Ó| ,óòNV?Ÿ]«Åž×tÚ^? ‚³\ÈCY´rù _Ô31ýN<î{ÖvsÍMìÛ B]«à6¼Ä[¸“ø¿ŸÖ=°àÇ.gL Í4ÿ9ú³7k«™U·0mCçµîLOBœœ-y Ϋ5qÈÅz”Ã㬻Ø`Êù¼Dz–‚Ew!}ã÷PüO}¸M]ËÅwÅX×´€ô؆GúqSA¸^¢ýàrCLhõ=y6ðó&ÈLy‰…ø$ë4÷©o°[H ü¥ì¸•–ºäÉ—ãpÑñ.îIí¦²×Ú`ƒš*nHV`Ó¦Ï%!;/Á¢Yix@eÌK9ŸürîØÊh.ûŽy xÒIkÁ¹JÓ~r÷ÅthžÕ8Ø|É ×L0%Å&`ÕŽýè v‹Dñè^wf×ã‰g«âàÖ[s²È:›ûba`µ/”ž×'f!o¨Tz÷yÙ-ü1o&ˆðžpùû*ížOd7ÆÇ:#öòŒ0[|3™º{ êrÿÂ¥q8~D‰µlÏÄ¢ bÄÖû d9äs«,œ ÿÅ×Aí X Ô-z%{r‡+¾µœw¼¿ü.Øã….¼GGÁ)8ò.f£ÒâAÜ|y:túœãJ"ì¨ØŠõhï‘„²ÕÉLƒ×uA+vVuè°>wq¼v$—M_°§§f//¶ê°ïæÊ¤"æ ¾_ÙÀi­Î 3zì`ßòmøëÙ þXÕ‰äuÎ2¸£eÃÍÎÁ;9×ø›ð g9rœ`ÿTgÅäØº‹¹ø,EŸ¼·"g\`©Hwds> ¤Š³ì£·!-N‹¤–sRòÚ\pH&?i€¢ÝŽpoØG¶ï à6¤<†U%p舙½õ7œÔÅŠcÙòûqXG‚j}ÀEë×ôu3Lx=~|8„÷ž¡Uk9fŽÌ„„×—ÀBxO:ê`EžI2! UñЫw“%ÌL¥…Óm‡“¾sOÖû°À¢ç`°K‰¬ÿLEÃÂ\I8xVˆì;®Lz÷Äç|ê¿1@úõm+YÁ¦]²a³îJ‘ÔæDr1rì:Æ»¯L&L``âKš~cïlað÷I»ÓtÍKy’fÿߊZBÕ cVoš@’Ò1X¸ƒ«–ñC÷7ü`¼è^I …e‰í¦w˜xþN ™cK±vÓ ýqŠ„”ñ¨i·8QÓre)§B šŽr÷ Oòhú|–oüNÌø SÅqzN_¤àî Ÿƒc«¶³ìø§ð¸Ò€´{¤ã|S-&ÔÒ -;¥¹èר³sا3€»¶¼DÚ,7~QÍ? Gà°‘)û0×m½gĉ~yû}öcÌ…øaY"l½:›E;L\¦îAY^$ì¹(ÂÞÔaiá¹³ÓDÿ¯‡–{c®²u9j?§v¬ZÄ~ÌR'vœ$nþõÄæUsÍRÒPpΓÛÚ.Âó·IÓ›´n.d}“ÉÍÀFz@°†Ä°ªÊ߆]¿D˜¡ÉF¶¿F—]^sjÏoe¶Ù®äÖTeL²Ê…E—Jðšž+w ¥õÚ’è’“|®Çø3Œ¢·ëOÞUJpÎ󤋠ô.Ê6á1üŒc!°·2¹é¦G>¨ß@ÿ{!ØÒ®ÏŠ%É ^)Mª¨Ç sDð¬äJ˜*Lþ%]…ê;§±Ê0 Ê )àÆ9˜7{:Y¶W³¦†‘Þœèõ¤ºÙ }úÐ&´»ç—áã™@ÎúƒyµßpœþgîPæ¶7â,Õ·Üôgt\8‹\ï¾ ’™lP¤–F¼´gÏBÂÈ©“PäÞ¸¿º“ëíP$KÞ0”Л cºÇ@®ïz¼/’Z$×àÁ}‡í‚:×Áø„‰l•«?Ýp,ûó¦™ÓóŽJ½¿Å_ò]›ý›"×ÎÏÁiAÑœëì»ôÎþ˜öx>ñÚΣ~c•qUÏ(Æ~fBªžCÅ€#:… Ò_ÀÓ­uxÙ÷*]sîT»Tƒ­Žƒ•í°a¬)9¨3•Ö!ÝŽè ¼‰,ù~r´I/®`ö÷c#ˆ’¾ßòäMe:¯éƒK-?*b©0³ú+Ý`4•rûÄÉ­Éñ¼}¶ƒè^k wx/P%.H?Óa®'ÌHÙuT: Ü­ðu=Yé˜Û«‹’ãøPž³€‰Ÿ¥UÜ,ü½rò?*q‚Üû3o•¥áüîc˜X§Jœ«DØ-Ý|HvœÎÏåkª°]¹>è=­7e{±7“ìHóS¼ûLŸ ¿…iгðcwêk6âg—8l¶¾-ÏRñ~®¹lªÈ.Æq©Ï÷íõïaÍ5>ÿaÐ¥ªD«áç9‹J¥mdèM]$P &£Uõw¶ ,³,ãë^xE¿4uúøÛ˜˜)ŠVÒ„§þ»°ø-®þ¦Œ‚nÓÐe’˜®qŽÿ°7ÛÔnpg—`žÿ%”zÿ~(› `ŽÖeø6¨Œy2IlZm0Λ#MŒ$аôî9|å¹ îó@q¿.¹°@ƒ,Z<‡ ,ËÆ9Íj£óðLórØ–¯ËrÆ]YÏõÔVZMø"¬YõÊzºã}ž:¬ô—ÃeÛ`³T2ÿÀ-!R±ý$þ°»ÄµW‰1—Û#à¥=“þ”Xº×q|;1=sG¦ãÅOùjˆ¹oæcÀh}Zÿ*†¿R"’X›Äáó=Ù¸ÄË¿âÉ|k,ý?ý4_l Û_Ãõt-â„çß…A­\xwt„–AÅ5²3Tˆ†ºÂù5ó›ô‘*öíAþo>½·3¹ùJ¸oĉˆ ½Çå[,±Ò…â²þ:¯²šÉ¢{±xܪ dGsÕ²YæìΤ·vË_`¯G8F­O{¶£g5ÁU‹TÙu±IhO´¨êQw¶_² G/h,=vó~Ó¸ƒúbùа£óæfp{>nçäös§lk À’= ØË Ž °dè)Û0§wó©ôØ@ôœb‡…‡Ðó×´¾3nL•"®5g0ôaê¯sC—¢…|y²¦!X˜.„éÍ£ñ>ØM¥öÿ¥šÕÀAð4À×G¨zœ7Ê/{ÁúûYkzÉw°6Dµõ›iŠò8š3UŸ-dã3²qêüç輡ÕËW¸>fLÅ´ö‰èZ⎧#0r›û¤ë¾K@ØËZèR–+k®S×MÎŽ'§›°/‡b‡‰$ìk¼Eo\uäx‚(ñù(zO‚aÝ\löO†Ç²†l’hþÿõÑW3gAÅ%lªé˜˜@Mƒ1§fô’UPƒ]+­àžÏu’؆ëxÓÀö¾6Ô7Á±“‰^T.Õn"î“û@í—<\Š|€îUyh0ÿ¸µ‚ð±4wMÑ¿åʪ Àå¹X´µŽs¼ÓË£Iì>qëƒo _cMâg ÓH§[¢–£ãŽ_82‹ã¦n^§®®aûnÌÀ_çåqsÒÞ¯ã³ðÁïT¸>¬„K–çrU É¢§7èx¥Uà3?˜·£êž×¼o—49Ey9õÄÄìãÊÛàÌ,†}«Úéê-_1v .Ø«»ä˜WŠ>ïž‚ù«ŸñïÌxH ö-…±^¹1x-ݰ»óÜwpY!98ÓÔ†žYxÈnNå$´]}”û²'ðG¶RÕ‹Çè«MFléÊ…¸d}Øõ_C²¨_}ý‡ÅYÅ<º¬æ5ÿš@&ØÓg J2´¹ÉlM†ñËÎtžKða©lûVr«ÇA¼Ýrét:w´‹Ò‰_Á FxD‡ò×ý5d"ÎoàN¶9;;®“!¯E’H‘ÐþÃÏljvþÐq¼ ›™42׈,;ƒôôJ*¸«—û}IŒÎÑÅð-GˆO—iŸ&ÌV_WcåAÏ[Oæ¬E³mG9Œ)èæó2ر¡…;í̃Æ`R/Àêθ2ã\ÈQÛEÂL[áŒ`CkÜÀM» d×ÁËxë0ý3s'žcµ²#\õp0†t<‚ÏFûiòã8kû{ÇÏ{ÅÙ ®œk^Œ•½˜up <{æÏg©«11Èšm$˜ú¤™DúŸ$=¬_ÏBSa›“ ïBŸ;iœ<8¾üи»¿‹ü™\… #?ã%Û"¸V²´sÊ07;œ;¥Ž^QHŽt8à…Ö\\7~>˜ý&!<âô³ ×;·ðwì©„1‹UèUמö™k×±ÀÁÁ(<’»k_r+Фäk²²ÁïØÿ8ˆ?N?" ªw4CKNnɃÓêâ°à¶[~‡Œ®ßª2Õ& 8Ó©§/ãáå$~ãi¸o¿Â¯Ã“¿  jb+v¿>Þbáa BÁžÄ8Cœ^.|ˆß'HеŸ!Àò1H²lîÓ«¸¾mY6”Cæ.ht⦮;ŒÓ‹œ™ê”i˜_ù= üè‚ðùx,d y hÍ6 %Vs±6` jÊÀû áÜáТ¶áüýc9Ê·õà|sÝS° c«ÿ%ÊâÆšað™tžñzEšf#D¿…+ÀKºvím8òï”q/Ö´N¤ÏÞ}‚ŠwÂLè”4MÓNmžù*ƒ(N¼ [,^éž641¤¯b‚ð˹8 ï#0ºÆxœ†—·§‘à *ìÛ*¼šôÓû«PiØn?wŒ2ðóy-nétÂ*¼a®Î8žñ‹ü«p¨'}çCö»‰ôr®Ê°é’*Ëìµ¹™¿pZ†m|yœÞÜ, d“¦u­€s‡‡8±FÊ„gÜÅÝAéô¦üÒ+}‚ì<ÐJ™\ce)j–s¶!|Ú¾ÙЭpç¶êfmÐÜv¶}I>©W`²åèùøÝ¥C‰MÄc¾sTÄ;‡?ÊèIïï¤Æn< Ù†«×·óg’ Ý*"ø© ×ï°Âædªµü:ްS¤X!WW~AáËË蘿vô{i.¹¿n/(_Wf«lO`` “2šMƒ·çp£'skgÿƒïãYÌÄ—fcà¢U7?fûa<žýu“$÷VõCEO·o¡;ù0„>H‰å“Os±èý%X6˜z{%xl÷%oU  8»LûÎᤢ.pŒÅôãHz9€ã¢çÓMIblßf uÆ)`oSåæ‚ß?/lóû® qá¼%Ôâl8]/wcT+Ha¿@4œ9 ž¾øìž&ï GÝï™°zE˜ýßM<ﳡÜ&®fÁ;2¸¦3ƘCÜÙo¸)AœJüމ'¹òÇ·a¶ñlPߥI²¦³ÕÕ ˆu„mx ;ެ„³;8ãÕõ8E÷¾PíÆ§ö]à¼U“TkÃôOxÿi*V{Šó× Ñ‡SVÂÌàÖ~6ƒ]ܰIiê¶dubñrÀXvqšwLþ"NJ,€¢Ç¢ -,1yâCnÉUTR!Æ‹6`’Ékr’gçÒ*ÈÑ ˆ«–p>HõŽ) üà$ ¿šÖûªpŒ³;êËT¢õKXÔ%ŸÓÿó;£|e< {à‡U*ÈòMè°¾žìÛ}KAšgŽS‚¤YKÖ1~ÍYÆë~—û®¢×X à}läD╈g”(TËû`ë} ÊûÆCÙe8¹?MYWK´ècG }ÏssŽK°î›³©WÂqZìç¾9¿‡Ü« ,UM‹v©ã3ëPz½Êˆþ¾ÈcηóÐ9¤¸Yøw-vnÖcëêKqE›c¨ÌÑíÈ3=‚4”vþ’a…_àÍiq¹€ÎÎÕ£/¬eÙÒ¦*ø³é$ýf‹‰³WÁƒS+Ácz *ð/£Æ§Büú+’s(ŠÃS¶!_%¿Ôgãkš nAèXêúånN(€SE`ÖXY¬×¬AƒÞ{SÕ·êôž®H—¿ÆÌ„Ž›è w‡…I4§JOhɳÇ3bPæc)Uù:™V]j­Ì@2Þ '+žÃ¹KrxÛ¶»Q냢\Ü«[pÎÌœjï‹À¶Eoˆ¼eñÍ7¦^NM0ûŠ'Ü|Z€ g¹’OÆPÌ©€°çãö` øõh!½xCŽl¿”kiv…‚‰“¨Ð—B4@ëÙóq8Zœùò® Û³8ÜOO·4•Ì"÷æ&à)sgvÚ߀^-c-²¿APÀ7?×ÂÞçÿpZ kLàs)ºº Ã ˆŠ^®{ÊÇïb—°h ¿hOÁû×úaÁR¦Qÿ™ì”ƒ||Ð Ëõ·ð“56QÍÅ×án{Z|:C†Ívb¹Û¸óÔ+Œ aææ9 Ó]ƒT¨é·TîÀ’Î¥ÞˆIÀ溹t®ËXvvÁ8êÐOçKr¸ê-+ûõÝ~gá5.˜ÒÇÃäf] kUï&/"]"LžSimgJCq¨ë8q»ú#P­ã+ÌÑ3 »÷™Ò›WaÒPôÙ¾%Sfˆ#™Ä^Æï ªë!Øn3•yÉ‹00„³é'!h‚6¸Í¯q¸RµãÖÿÛH{¶®N³v1g?þ’%ú¬Ì/„6öë`Ї.Û?W‘žœwûö$Š»þö¼Æ:5½Ù* U¼Ùbˆ›wJÒw¿Ù­y÷p¯[y}—"Êcrì üù*E{*–s…*ÌdÉËÖÏûÅñáê_Xs£³®ŠÃÂæøg+ˆåÀuùœ?Õ€Vß_ŒœgâÔHQ¢ÍÇ2Qÿùrìè6&®Éòne½sÑøØ!2ûHn›m€ÿnNã\ü½Ñë‚·¡(¢ëȰGŒpF<ïm.ju-:KòßðI­2?Pø>†8£fø"Aæ èëc?jòË’+&q1âÔûÇà‚†4ãÕMcdYÛw7:ööÀõ>¯ót nÙõvegr=ñïáv¾¬”{²}2„uiC¦Ív\§»‡¼ÉYbëÚÐ6ö¼?k@k%io•Óγg‘JÐÃãt½˜Ú×ö¥u &°ÈžRhš«<#Ù U:Eå J¶“¹´§­—_ùUì'ê‚üfXQ/N§¿ïáìÊÁENÖ¼9ÇÀ–Ó¨ÚE'æjHM·ºaü=vê“ Ù]Æö;±¼wõñÝ#çó°èK3œ^@ë×oæTîI0·\TèeÒjبä;Ê}¸šð5GŽ­¬G±ñè–urOMÆƱ1»µÙ.ó\* ¼í ðTœÍ_2…/ˆÇY§0ù­†ìMÐVò︽^§Ç6Ý…€—fÌÀêšRjý¬ ¾ÍýÁÕdÙ°½gðxš1ý¯ Ñ{š u4§ÅôÇ0ŸØ‹ ñ>:ÖóÒ÷Ép#Q) ræô3;üb1™‘…½·`yI­€õ¶bmS.(8ë³ø÷Œ×cB_ÿ‰)h¢;– 7ÇAë&ô´ƶÎ,Üú2ÆâÛÓñŒÚ~æ;S™žº˜+Nz¡@â(×8ÀYgg£ðë5,ÿõ1Þ¿K7ñý½ÖœêzÝYäwá}|ÚW‡N?1~ª&›ä¿=xÑ ù çѼ®‡HÁ eöóûNÔ<–êݵ§ãgs¸æ…kôe™N‡Xèj—<‰u>IgÂïuÙª;Ùäuc¨»e(xbïÝÍîL@-ˆÍ7+OZPõÛxï]% ÿw׸ÿ!M>Ì¢w.{;÷—sfÇÐöµ©¼§V?HØÞ\R°w>~÷;ÑR­çÀVʽE“jyê+V Mñ 4Cï,dÇ‹;XÂÕ©4MLƒÖüÄ R/pS¡C¾ì[{/lZ.Gw5%Bäªk¨Z† í¯£Å+d˜GR#ùµD—O›NÃ7Œjb¡ßøÍí)ŽŽåMI·åK©G²ùß4Ù½Àζ9˜z_Ö€¡ö¸©›ˆݘÚ|E,Wƶ^råIüm†’EӘݸ±LpI&ñI°‡@‡ƒÜ§Ø› y`,ÝL- ²™¡bJôžât?ëÒùCå|N‚@«D üâÇñ¨hY%·j½ í\û½é‚ìh½#›'5É]<ïÈÝoá ›6¬¨E~¡–D…²”Œ®;Õ{b¸•µ¢°¡,õþX±%—Šq±µ»ñè5¨Ð´Öà6½8›[ùAÒ³ÎߌƧ×BÁ¬ôK{æ"øí§„[ ÿBQzâ§{ѪC¯ÜðbÅOz@J®Ÿ ÒˆhýºÕƒë ¸Ža—g‰Ss˜É'RîbIåg&ÃN‹/hXŽ•Ÿ_ã;¨ö‹‡°Ö퇓£QXXžn3o'Ûîš`ÀÒtðjŸÀêc| ·¯m%rÈ¥¦Ô¦¢óÇáòØ÷(Êk­_ÀkºÚ6°[+Ÿ“ºÍ£ê=ÈîÛrÿyŸ×ÛWׇ Ôã‘jÎÞy1öºôWö OäÜEÇŠ´üD ~Øò‘0QÊÿõʆ®ŠP§;6Jbê…ôVèlf.ú#c×PïR'Z6×”*_ÔíjìÂ`.>¼Ÿ€‘¡^T¯m;}å'¹sU‡‚ã ¯¨ˆ;u H­ÇŽwÕq æ.>Т[\ž‚ôg f±‹±jZ;(ùÍd{N;u%.êÂ<ÜÚ/NË0TS¼È ‰ÝJ“I¼ênñ•8p¾ïE./½ÇxU`~ÇMÌnèÀ!áßœcÂy/ádÝf°Ça§!È?W^˜+ⲩÝ5&ÝH=ýX€¥dÝçÅÒE¸4ø¬&Ê‘=8Ÿ´еN“ ¾NŸ.¬R¥¡_‚ñ¸—;U;± '‰^à=ÿÊ ùhƒˆÛ#NiåyîÀUeø¤m/fÿ…ïL˜Ð!ÔÞ&¾†×Aã[wÎ¥U‘™~ü lÑKþ9w’ÝÓZøGŸZ›2kš»P„s+Éš‡Ç’+5çy‰:7àÑ‹$|Zó€Ô·¶’ {õÙWÁÿ«[Ë”7CuíE¼^A¶¾¬Â•³ÿò&åÍ„§kÁÌµŽ´{™+‰ ß{>wÁä°B|uÖ›:Lôu‡óëwѼ8y#üýÁé"<Üi‚Gt„À:G¶”œÃ ¦cèÄ¿‹aÖå!t<Ôˆü‚6îë•7X~D“Û½0áš%™0l‹VzÐg/ŽÁ‡ªl|¾ª®ìž@䊯¶Ê.¼oË’Ð}P€-‹ú¿—Ù¬@ íîÅú’εI•Ξ‡'úÉýˆXS‹:ó…ZKAtÅܽ^ Ö©&`ôþ8ºî@¬ÙÀúáVœ™êŽ×d `ssþN …Þ·ñ̓*ùÇ™b„;ž®¼ÀµhÓ󛽨ã¥Ùè¿ø –Ú%sÁÅôä|6-HŸeˆ‹:$ÝèB­wAè!€Wð:·;“=ëÆ²Å)Çà¹yOwÚjdßWPçÿ•Aîkà+vIÓ -žØûFˆ__„wDŒØžü ¸£ÔˆîÍÐã.ùdlaé[pã?Ç ŠSÁPx%yéO嬘uâ Rðσ=>0ƒv…ØäÙ€Ú9{Ø5êÆÍLq„WÑçPá¦5=ZÇ–ìC³¶&0«Ó¤ßy±Áû3ñÔ—=Ä6[–§iôäk{ðzZõlÅ›g_ÁŽ6¬k5ò¯rS<WnªI¨¾´¹1—îµ›‡Ë–ž‹Ý{ñÈÀI²àn5 ú‰ztÚ·×v¢ÛÏP\9u1>9®Â^fæÑ[¥ÓXö¼w¤á«2 QùÏsÍ Wa÷„ÅiCòU̺²‡›_¶Ö€Éø\HµdÆ×“qwÊ6Íš»¹óõ‰Í?ZsI›åMñAPA۔͔*Q–w|óP£S¦1’#2ÞØÐ‡ÓZaO}\+A¹%ϱç\= ;ûЃBô8ü49OèXƒD”„ó‹ø2›<©ñ”Tçj,3ð\ïõãP+¥€ü}$ļ5%Ù¶FE˜\ú–Ôÿ\H³›Ÿ£èÐqXb$ONå ]Ð}~Ëœ™Ï»Õ4[õlÜ7Šå:ÖlYéèõ®’§#âÅð×Ó¥+±Ir¬ÂwzÏÎ`2…yämÒK˜¤0“d²(åÅð^:›éâGý5£aƦóTpü$?Â^WÏÇÅ—©A{%Ιp¾u›MÓÃ⸴?ËhÒ #Ã…„x8pQÍF´Kh<]™r ³xÒßdè¬G÷qŒá+nõ÷¼U…1„hÊñ"O¯Cß'Õ¨…ŠlÞØfî@a ©>7žMœ¥ ·S¹}B+A¾q Ùµh+A™WøfDœÌ-(Ã0¸5žÇ–7J² «"®9ß›-wf[…³`Mï²ì¯4³åê]Âp¹)Ÿìz¤OýžgâÍ1Õ\ÿ“fXÕs‰¤ûËÓåêÁ¬F5Ÿ4Mj†,ÛÝšéõ‚øï£5Â/oŠÿFÇ/3iÓòŸ à”ã&³C»¬¨C@7Ô0ˆõq‚ìñ‘•x«:¥Gá§²yìÕÓsØn{Úº¿¢Ç†F¨ó^É=iVÃWw§bòáTX`‰;´`5žžoïâÍ×£~½8ló¼ŽîWµ!à· ßÀ•:W±l{éÿ³RìÁ FrË1Õv1™O¯åÖro¤\x*£öÒƒ=½n†‘uµœw,°©IÜ3¹ôù9óMÙ¦±hº”Þ¦¦ô¹ßMèoâðĪ>^§ÿ©ÖŽÏÿy°9ã}ܸëHÙ¡\Ûá§`ë¼T¼tïþÚ/OמûŒ ÆR£î±ìv´z\lA»­au¥=郫»šy?`Y`"Úº Óêà^<ºÄŠþÙwsg0›·÷ výA~OÈxn”€•™æ\hæNÞÚƒoù i¨ö–æW ŽàИRp1ÄccÛ¹-¡/0G*nç¿ ËƒõgõÀ~ž1[ö>ÇÈL‡Ð]«)®GtzÒe|îhM*LH’¡/–aç–÷ðBᾘuæ¨xòhÐ "¬ ÝaÑC~Ú"-T_-ÎþýžÍëåJ|ÐG©3 ðj#ªY.Û~W`ë’}`dÞóã`N§ÌQ¤ïKãáä»#P”ÐŒ=³>CCâTõ÷ÛI³ì…܇‡Uüû™K¸Ô_bh…œ¿d *²§MÌ”û0|UomÃá >®íf,^qdi®‡ñã‘ItÉÒRÔ°}ˆfŸ¥ØÅÜ ¬k냉ùÿí×Z~IþÌİi¿ñ×/mxzj=}k¼ç­Jç–Ò½Ïï’I;-¨’º«?­CëÚ|ñ˜˜7Õ‘ªÄ´›gpÜwKêí“GÂPµc"žU›öjXÀYx’»“¥ôØ7ïR©¼ŒÿÎwòÅÏGójjj°óÕGxú¥î ~ÄŽ7z~Ï^ïq˜ÙÒ×뱜çE7¾—f1Vcè‘Ø<õ+'(ÌfI&C[âìêT¤nÙ³áÄõçܸä8íŠZ†^ô‡Å 7z¾+™A¨ÈZ*NÕ4]ôr"›b)Âîöq"®Ê\š]072\VZ®ø1y„?Ç<‚õ¿™Í—QTöJ¥È³›²)´iÕd¶WA¿ rcÃà éO./º4nÒã‡PÞÿE‘¤…rÛð^Œ9§qÚ®}zˆç•.BàûMxïž}0Ê™JŸ$—ºà§è2˜÷â>§$Œ(™[ýs&ÒÕŠ5ð«í Wœ.JS’ËáŽ÷oÎáÜã¸â @÷Õ_˜3ñ¹²× f-Ç?9Æ èÁy5‘0å~2©8`Açö™ÀûW/ðùã™tƒ)ÊÒ§IíQxlÅM\M^µÃ)Òk;½™ê½ÛÄbÖ8š;Å ?ÜI`uG]Y©¥9¼>íÏ6K)‘Ó6üD”Í   ûPŸ1‡ýÜÂúfx9šÓÃ^ôùµ4vJ|X'‚¹˜ˆs†ˆÐg~½ ×h·@¹q*íNŽEùþwœzáavÃ6å] úzÇi®[Ûøå7IU nnÝKï¥üç›ææyýC…iU\¡°-ݯ>ñƒSÓLØ‹²>~óÔt£«~QˆË#½i㥕ðü©{ßñ‘+´œH2ÍtØÉeêÓó Š7nbû<…™@vy8å(ÓÖ.bO£ó¸¨#%ü¾"4Õf.mpÆŠ;‹˜dT6Mù‡:'s“±éb8QyOÙ|7l}îmhõj3°áçÂ4cÊ`ÿ¹t޳X¾¿-.jv‚{Î3Ù¿y*ôÞÇsd™©7uqQ#ãl4aÂê=äXD¾p¬ƒß÷ûáNº »ÔµçœQqaÖ:ò/bðÙ¯T®iQMkÄ~SøÜ„,ðCÚÎe¢Ä‚Gxßs1å?óbzɤž(Œ6/áýß‚H>¬~:E£'ÂB-T×ÍÇe_ùׯ¬a±zxáx5\Ï"³¤PdÅ]'æ©/d½ÁÕÜâ’(>í*ƬŠÁiud«Ê¼©òo»ë5ßdX×+¨Óµéâðäµ|„HœúðwEŠŒj·ÇDÞæ+ÑÍX÷·rÓ&ã»è§DÕh2K²ÒÀê´gÝKòÛ?žÓ-Lé2uöÜ|<¼f§ñ醫¸Ú²å/)BfÐ †tÉ«Ýè?·b÷&ÌS¥¶ØhIJB<`z }ú&Â;÷ÁxkyûTód0?‰ëêèßxMZ•}F÷¤6Ovbö׋ÓËìø*û±’{øP‹”]}‚A{P©K¸¢$ò+¢`‘ô0l Ûˆsö½€c•Ñ(¾&î’ûëÃ0äq!ÔΡ{ŽJ’ž› C7`Õ‚[ÜÖŒEŒ·EHE1x¥} w£~ri¾n¾MÓg`Åêbðÿýãò˜ê(îç¿Ø‹­é|îÐa_¾Ô(Ö§ÜÀQ÷ðŸ~$Ü¿‡×ÿÆr[WNaÜœDx'±……Œ¼CKG'ºbÅyf6_›ÎÚDÇIÐÒGç˜u§Õ\½òt€Ô6-` Úé8Ã%¼Ã×É*6ơӘ…ò5h=®„¶N´Ç]S[¡)G•y$­bª‹GùàÚkP´õ#>[aûƒhÍQÎr%=í À…˜µ 3Ô¢£WRí°£xõÊÈ®vÁÙýñ(ð>™>˜P .GwÒù±ó`ÊÅ?dnÂ!Œ4ú@š+Oâü†ŒéŠÂÂs1üàô˜xS Üeè k-æk'*ö±äåIxLø â=úð|.w~öréÿ@fâL*²Ë «>aÒË%Ì%ýÝ‚MßÊA®q3û½};¹ }™Q­m`¿/_£3tÖпnáÌRvg®5m¸+M_N¿ƒJFîpíã"vk- ëFݳãYµ¬5ö¤žòÙ©BZwú ¯.-ÖfÕÁ¹Q¿Á_€/úäi{³ÓGéÃÉôÄ[ ö9¦+{A°H†möTçf>8À¹›ž †qB {M€ž9ñÆD¸`ˆ[`Vr;õ±mña|–º†,yD>5§9<,’Å_Ûõð€h*ýÅËRÇü , þÅŠ4óQuP ôYC«§6‹Ü¤ÇæÊ7Qú$|²,’-~÷Nhrª{æáž§;afHL×Hûâ'ÁËeÅ®ŸP_1ˆás;Ðé¾6]¥YÆ-³[†ò"*øÚ¢GŽ÷BÈ¿ÍÜ„õ1hxv2ºG´‚ÝK°<G¤÷dÑ o#¨¸/•7[d+žJå{ø0&,éËGŒß ËûM±år0ìïâܦî†0Y98—x‹óáãÛÝãPàß’?ŸÇæv+ÂF·@*CÝP,'ò¼BÃ(%öèêz–¢#H_§;BO³»(ô¢…fq×›Vcéš>ôÔ]ÈzB΃V¡"W°“èyø ¯× rkl}hvø;ׇZ¿?aab¬¤Ïy$f0ÓF¨¨|¯'mbŸßÿÄVM+zNã$õ©Ôf}‡¼XßÃÓØ4C„™¨ïcÇÓäñ³ôj,TÍÀõ7|XËÇvØs8•÷Í$mö„U[Eé¾Ñzò}e"Õ¾¶‘Žd Jþ)Ú® ò7Q÷çãØï!p±Ü’^ñ{A¥2 fÈš¢ô¾U8yÍYlª™H/j6A޶ ‹.=‰ôC8§yÍ‘>Ä_ÇPû¹}dëÍEøEê;W°Æ\–†¿(ÃBAqFÿ“MyÇYóîf°¹^‚Š6ܧys˜VKµøÙ…ƒUvNaŽÎÚÄÇ+›“Y<VxÒÙ»Uàn¢#­®R|ÝŸ½Áòbz±ÿç?ƈµoÕGbÑvC"† '¿mãv±n²Å¶wb›îW1•HLsùѱŸ^]ð–‘ùb‹háIx9-çó,Ã¥aÓ"U^œØ·TáÔêÝÆ—7¼¡ƒwGîpŠ?”˜â8:ißF–Ú‘ G7@Ã[Þ²åà:ׄ~™tÒ¯ò-I$È›'‚YÐk<<7ŒýéØïk ó`".øXà •ãþóF?¨´á/u“ÖU²­¶öÜÏ’CtsÑYÎ>é;Ù!Ž‹å'7Ä+°ø íŸê`oüf,í­„7OÀã/~zõÁa²Æ…9âxÆùÙoàí=§È/È‘!°ö§㱊XsØ Pµþ0hé8ã|½KdNåuƒÔUœ´{#ßp\/¿`µ0h6EM—‡Uáÿ Çdæ`&_„ÉÓ+HdJ—Òp_ê@À§-PUø†ÛÖt п܇‹ñýñHÞ[ã^—Ü!nô•ÿîó49ÈYÊm姤ú´,†ªk2rÚ‡lª™^µé˜,>Ÿ·*`6xšŒáOîÃ-­TÀ—;ñnfLåq’ßÁ‘+°¢} w¹kÚœlåÞŽ<ÝÍa y®›„IÂÔëQÍq:ܱf1Û®8Ly»+z þgÒ¡ëvwyeïe×[ ²^Ä?"!ÊzĦ·žÄf{É#H˨*kØSÆsß¿òÎàÏäy'šîŒ\…ñR„¿o†¾g0ƒú¡ñ¤™4êr*xÞ9çðÙn9|¯™ aŸdAÊe Õlךç\¤ï-îñíûdæCr`‘kS`_¼ÒIét3&óã7k†é¡°«F™þšÅ“sàGx,ɲ—~ÊÁ[=Ÿ·˜¢Þ²(:ýah»° )7ÑØÙ¢†CH½iÙ“0ôaTDÃKYXag û6ëàäI~ ß7ÊI•m[ Àe:åbÇOÇbˆ‹àY¢Þ0ƒ=¤ÌL©³v6Z¿Á,{Ô ¸CÑdã ë(Õzÿ‡û±ï#¾?…8Ë϶£g‘ÿ+ 2àeϸy¯ÍîhÃÇ?n°'ÿôwFð×ÈãÙ€ìQdùp n7Ð×b·a8ºUàz qLzˆÝ—æ`æÖr,°7§ÿ*¡N§Œ$¿ÍB‡6kØs›Ë2`ö•K µºΣýӭî·y±ŒÃ“@Óö^Θˆ–qgÁ“î\fcÀøËòàÃ~_v`ä>86Êû’~@¡°&›á°k“æ±’uûðÚ«ËpäK$¯!S—î›ñ•L/ÛðlN<ÊŸ‡y¦h£ž!ü+Z‚énp`ÞMP¼èͪg§áäC¦´×L›Ù=GU™,Ôœ„¸®ô;–.Ådh °7Ÿ½¶ óöK©²ž°\’ûh-†ls‡¾¹ChgéO–O£ÏnâvðdR6s/k­-‚ôrvÄ3™ÈÏ·0gß=ì0+FMg1Ì(£gzw±Š·Šô§}1´Uc•&%Üß›nÌîÒ4ýÚ%rž£Žº.FÌálÙ~w)ÛäÎË#[Ì¥éü.˜¼}7þ5 @ ݉T¼b÷hÛ?$ï–ÑHóü™ËéR'KªÞ§ 3+ḱ3Ó)'²~•ø[‰ƒ¬W¶uVú9dqäí8väóQT˜r´6†’›¿‹ §õ ¨wÆsõÆÂìÔÎb®B® ǃ’†'´8,Å»üßàò:š{¾ÁD9Ô,Ö r­³áESO«ã¼é#Ò%C7 Ó@±¹ìÀî0Vüe*àô™A«![ÝæÃÖUÌd±ír¬ZÜ™ÂåñÌ1í.–ÌaN [¸‰÷qÈ›ãòTÊPíüNAB‡ÛïR .3fþ·xüöƒ_ í ߆"!@ îg„aÑíCØÔßÍÉé(q1ï¸Ï¹Šh7y'[ì¹ \òfLn8 êbÍp­§ÖŠÝÄ¡ÚðŽ#Ù÷Õ˜Ó“7(?ÝúŸÎ†FSŸ‰…B­ pñ=É?†Š²¹ÄĈnºjrÏúr!Q±pÚV:4žq/,o€èɯ¸ñÍ<éÜ3Ÿeîi27òñ®ZéŒâ…]$òêÀpÍP?SÈ•)ÖBc0‡Ù:âLtÃ*\||~ï AWƒXzïoO‚íîò¬±â=–‰Fµ‡›±uO ¬(Ðzà€qUÆd‘»P¿Ï=[ÞŽª]µ¸+Œƒ—>a¸Uþ‰è«Å®-pÃà'-Ÿ·OÂLµµ,uÎøa®ˆK÷¿ÁÏeM°ƒçÈÒÏ<“RoÐËІ‚Cáuv\JÓj£íCu³¦¡Úp~ê G~}ÀÚ9fÿˆÂÆguXX$ ¢œ)ȵ‡¸! òKÝÁ}S×nêŠc"“H)äÝÈ„g£ÜXÉ=Þ½äòCBðë+¨½ ʸ¯är·«³-CøðC6x•ˆaò]vâ³úï2a1&Ñ25dgg-æ?YŠßΜÑyÝœ’!» ^Ñk‡A­æy—P &œ5‹kH£·?Ï@£RÚ{ô*?B‡ŽÍOM`Y;úÙ¦»ÓØ9—jÔ¹P áÚL.KÆø ð<Ï|:e0ï¨(µx*ݪ³Ë §Ï€¶ôǸ—äÓqk*œxÑ«ŠñjãXB¶;Õ·ÉÁ:úìè‡@jo³™õÌó‡wnpÇ{ªäp÷ÿh/ýwïGxß)G˜*È÷¼6üBÈõ+aŸR®òRœeÉ×SÉŠÕ ¼%"`[Øa._á4þ8ÇþºJÑ·ÖW<·”ðdê-©_Õ%rGOœ¼úUo½!¢9#XŒÖÇ>À£ÅÉÌýI(urþcWûá¿g’×_Îê•Ì&Z#LS ÆS§!yòîš4Ñˇh®ª2Æ0ZŸF`Ã2ü$pÞ+s {=Ö|kFgcÚí…wTÌ™RÇåVïw `œ…%[ Q6j.ùqõ Õ5)MмŸŽéÔÆæãúü7þ^œ!ï>ßëªÜ=³s5'bß^³¥VüíäJ˜À3]–ß'Çxq^¸A ÉÇ èÈëbŒTc ÷À»•ƒÃÑi˜zi{²òÞýVƒñ뫸½­“7gÁQÓ㘣{’×»z«áä™’^ m¿VO&gBÄ…#ð¤†àÕÏsáŠM7ví;¼é;RÚëaùdÞËwï0xC1-3Í …ÜúBlЬŽSî«ãÏÕ­á¿ˤ<í`l¿Û`w_ÇdÛ°½×fp¿F&ñŸÕÀä=[ñTŸQíP€ÃÊzЧÕÅA`⋆™áüî­àpœÐJË$i"Vß!­§Ïƒ­Éc¢± »_íÆ¯‹4¨øI%N7­ úëúÑG£žÔ^ªÄëêq¸(± LÞ&—Nf -9ü‘èX^y‡!m³¸WOGA’<Œ ÌB^¥\ÀA·7ÎVÏ ¥ã츳×Nâãírtÿ^#¶¢9†ZÏ òë%©¥ÂO´,´Â[gðæHŸ‡&»*¬þ´œžÎd—U¢i™æG¾Ý¨žòyôøç¢äªírnú]Á½[µ °q"©9XˆÒN‚”2 SEø³Éˆ±w[árT9ù™²ƒ’¦ëÌ ¸â¥xjÝ-r3~”笱dŸòå¹â/`nAjfXÒ;?s‘“Ö@µÌfâ§“§_ÿÕ^6üÌÀ}°ýÊ?¹IFY|¼+‘è@³7^ôUÇxaj¸/šs¶¬ÀgY޼:ûyÀ{ ç×[€oÚš!Ü™}ÄêÚ#Ü¥Œ‡k¦7Úœx×'ò¾š<9PÀÛv¢ÄaêÇÒôf»=®O¶.AÁuYØfNh£z-çAóaߤñ¤Qs VþÊ…h¥}q†0¨ƒ¦·©ƒ|1Û®Êè’C¤íR¯]LÞ~ùÌÍŒŒàG}¾ŠN¾&^wÓ`ò(ÙÚŽf§þr•z»Q;¨ ëÏs$lЛÝÈÃÚCѸ»ªŒÿÞÊåxJSÒHÐí¯‚ͶØÁçŽc ¶§²§‰… o4‚±ºÿ ¡O‚:äõàÒ¿ °ÊºEó*àcãõyò•LË¤Õ ÿ#æëÑsŠÈµiÞì’ÈmîÊ­eè:înŠåÆLëw]$ ÷\ gæ‰ÓœW×0Ρk°žý“ØwÃ(Þñ)ø6Y ¯d˜Òƒs¦ˆÅÃô×±ÿ>-Īóê@6(—:-žAwÍÉ-I¨â­HnćMNlèE,î­Œ^ÐQx麓'¿ÁÕ=¨ªu⦅‰ïãuHm3 _o=>a"¨þõ G?¬Ö4…œÚ&|ÿíÖ¿7€_¤MdÜæLiÛw&FÝá¼RU BpëuP‡v›Ë\ɸ­n ŠGÑíÎ|%V bZÁä…&»¼Ï7´û øY)úÁ«¿¤Ã×ý™Ä¨@ŸÕž d»X5¸¿Ý ßú~’XÕ1lŸÉô8[ÿÍi}òígðäðEÈZ#É2ÎkàÙÚƒ4÷Ù0vfd³ñ’êT\â7nŒÛÛ§Ù‹÷ °XÏŒ®0æž®£:æ¦ôÍEª+\Âæ<™Í¼Ÿªà„ÅX\è†E›âö°2¶ËJÎz:ÓÞ%vÁt2s¼2½IŠÀˆ”él¦àF^ýäj¦ûq&3؃–:X=¦ž>íˆaç–3!!;ºnµ<›j-N×^*ù]kÙ)8óÁ‚­Ø ÷Lv.{]›’‹šÝžðÌE7Êì…ö5 øLižìÄ‚/ 3 EVwi+µv¦í‹ÚÈEËYthAÙ4®\?ï±ay4•Š'B‚ëXDÉ5œ¢TÌNýþ Ý÷mÙ‡ø4ø*òÝ>=(¿€&.Öe?J–‘ÇŸë±Vü%y’(MW•.¡©wq {¶³A†öfŽeBÒ4×;¸ùçuÚE³Žüóºø¢Ž«S-Y©2{óå1ØãJõ½§ÓÕAcÙÚ» kGœ Nrн5ˆA R¸! ÞH>Š3ÏÂ@‘0Üòã.˰K?ò鶈ZŒk® •}TY÷-üÕ<Ñ,I À¯"ä¶TH²€íæ4lw2wÆx >`A´×¼÷<¶aCŧ¨ð/úl ‘Ü/QBËë°ùq ø øQËb+ve³s«lßy0=úœ«®ÎÁ‡J™«ÌtôÌß8kvݼ>we Jt÷ÀIX1ΉFîT¤ÆÍ¾T}Ét蕼AfÑšn/*¶4œÝ]«ËmyÕ Yߊ¹2Û°øÜXø&çŽgòèŽwºt¨ÔÂ>d“¢Ð»fhK÷¦U6TGK’>×Úʶþ͇·X º½æ,m¢ 3ŽJ„ÀØx˜xå2üöL!aÌLLƒe©ëq¦Ž2]Ї‹‹[[Ôáõw‹HôÚ-¹”¶úˆQÙË'áècS8áð§k–²[®/> u1è·ð.8†@'ʼnþÛ©Yq*gUü‰<"Rpú¤-äÿ‰b»¶ýÄ¡¯ØëÒ•_¢±r®6Qò¬Ã걿ÁlQ;ùf¼—‚š3¶½@”¬ŒpeÿJØe¤ÂN?Õà {ýjYÄ¡ÎÃA¸›õ 6__ÏÂxqþP9·ËßœÚü½Ë¥.4e7¾Íbr5™0¹î!J–ëï-ÈkºÃÉE:3ÅÓß0àk=|¾µžèX­ÊZ¨­u€Ží…žgòô•3)*åÁ&É9$z¤”·0‘Á]õ_8ûœIô¸Æ} ôã’&±³øåeBtÀ¸¢ªþBH`3l)-çŠ/!)z•ƒ—g7ÍvkX$ytGq{y?*w;Ñ{¯áËÜrüRi‡óNÇYwáìߦW'ž.±‡u–¨ÙcÁ»Ù…F0z(OùVh.v õæ1;SVîÞIÆo Ûâspµ ˜Œæ3It.ÏŠNˆÃÅV`®Är,Ý}’LÉ–$kUÎ’¯w¦ÂÖ÷0Ë{í’gZñDgþÉÄ¥\°‹:ìÏì¿ r§Åéuo¦õÝ»®¨“ÒQ¸ê$@oØC¿'‰_ {B{Áäé+á|:н SõŸCëZwôö@‹g1tl^ LýÕˆße–R¡ëÚôоLÑ`ާ±å»ÇƒB¦7{ñÄß”œÅ¾&U]xœÚn99m€çvï€ÚÑ3"±3•ê(ç’!mÙ;ŒÁZÅPUüCgm¦‰«cø3?ÁOŒº qÄ.{Þ+ ‡7ú«èph* ±N%·»§Ñ©ú!UNƒº­YK£ê.á®3–´Ï¥'s‡ôž‘LJñi›ÑY–ý*NT²üh|KE¦Ð%öàÜrcvA#»š‰ô¢ýXòb‹CˆúGTþ¤ÆÚ¤Ã¶”ݘ“lM¼ylÁ†œ¸KÜ:ù ÿZêþëañÓ^Œ}ƒ§¬dép€“ß#ë6õÀü6qŽÛVº?NÂÆùàж˜PZÈL·ß†Š¾F²þ…=-¿ù”ˆù .¶Üd#Vo±3³’T}Í‚³ã"˜mxØ•€Û.9Sµžpè>‡³uIêC6m¢[Ÿ›­’ÃGÅé…9²ðK_–Öãº6[PÓë}xÑ=‡þrÊŸ”˜_Tzš>àŸ~ŠôöVSš-I771α?²-Œþ»EÊåê‰HÅ.6¼° ºB[ ˆí©Ûʽ?yS=(kÖm½ª|˜ÌfºbÍWnÂWâí6•9o¼  ‰û¼UÌîÊV*¹Æ–g@>S…ŽL7ª…®·âÍÈ5´éý:¦X•…o¾‹⢠ÿy)‹tÚ‰-³ØO‹Üã 3òeû ù—éÀúÛjpç ~w …;y$£Üš®ÿp…xìXÅ´W_$ÿ¾È‚f| Èù@Å“{ ^¢B‹é§Ô&ZŸÙ-Ôkñ˜¡Èr´ÁÛ3†)ä °·Déu¼}G­ØÅéÛXõ›§dl×OÍ é]²<ûŠ™tÑ@#45nbø@õeKË9ð=n k`2}¥VÈRD· ë3K&:dÀäîGÓÁéü†¿90ȉ±£·aW±>[;yˆõÅS;‰N’9q-©ŒcWÚ¶Ó×—;p‚·½]p²xÿ¸%üþ–̪Î.KÌa'Ä~`V­ «êiÁ`ÉF$öp%ÏB€U!š0³íÀŽ-NbÃ0z‰üzÞW#>Hp‡Gš‡@¾‡s¸¼š5üÛ Ò¶R¹ál<¢~ÚÎeÁª*ô‚îtÚtÆ‘…:ÁîK}¢£s°Û§”eo—¥:mñðh”•‚ÙÍ›$4Ý…½ŸxF*Ò0¸Å•ó]’:–@RQ;¼[0Z;ܰ­Z„““·aô\&|Û—x:ùÃÈTb¾‰á÷ÖhuÅ £òAX£ê·½€çëbáú!ep­1ßÂ1Xÿg2”_Íà¢dÒ±Sòö9ð¿~ã~ùà´SÕ0ïm.„„>Áï½³ñ™Æþ|ÀG#x7þÄÿ½Ë§JJÉ­(Wl_°ž¥ŸÄŸ8ÞUë|Ò[lÏÚ]}PjÀϸ 1}IØÌâaÄ8‚VìŒ š¯ûqFå*’X°‡Éù^BÅV]vÂó«:Q ·vƒUÅs´žd ¶ö/!¡O û5ª‰aº?vªô¢ü‚(´ôä`žæ6œi´Š5ÜOG±{˜£ÖŒG7†ñË-Áögsð÷³,üñãNç³[v­°§1Ë[Q+Cúô˜/clÝ’ Þç/SiìšjªÖ²„šO{ÉiŽ[‚Yïå¹#Ù‚Ôüª=+ê—ÀXøy­\Ÿ íõÂØ‡¢hIüÁÔj¾ø¾öÏÍ&™\”-Äô­S1(oÏï/Þ_«G¹ây¾M>2 =ûáÿ( gÂÑßyj«Cá¡ý ×½h<ËxÐ.Õsq$Ï’^>¬ÂþóH/÷tò¼ïÝ} ³g8²3_ö¢V>€À3Œõq…ø=>üšÁ.^áÚSxAL–Ly[æÁ¿z( s’xÝšI[–È0q‡Ñ°è<ëÓbŠ{PÑ:žZÃÔ*1nJ.-?· ª¼z`ò깜4RÓǓجûopïnŒ:.IýæE®þ¾®Ó¢þ;ÏÖ‘8.G8e W ;I.9H™›/&™»žÛ¯UØ€ÛÊQ¿z »ükt}>®®~„_–&bÒ'cä„ñ‹¹VX;¾“¤þÂéoFÌïÂ1”{ž6â,ÕZ”œÉ]±ÀéOÝáÈ’H‡üè=åeøo^~ëªÜ ,é: ×6z°\Ë´b‡jcà·oKÍgÉÑ5v%=Ô3ŸíÞ°Š ªéÐÀ¶¯à.7Ïaq¤ íh`.®ÈŠÔÔIáÃßìVòq(¹1M²U€«æbìd­Ô -Âÿ¼Çš¯÷±KgîBÉy7ú^BŽ­J0…J÷GÜiÕv|ݦqÔ‹><¸N¼¦ˆ ÒTé T|§ C‹dÆPO qò.KŠžÕèã=5f>î‚lÒ¤ x¼Ñÿ𛝢ë•!¼lð€”ñD˜ÖÏHêö0ŽÅWµÀ&µkDó¶!õ QÄe4¬ %½ Ÿah¥¬¶øÍý.`Ç–ÒUûÏAèñd:ýµ KôJÅÝb|ô¿t œ“ÖŸ¾=Ë}A §hxþö672c%;ÖáÛÄ”/&2ƒwÐ [ÄqÖ yGò¦•H쪠˃ËÁð|Q6¨¤¥R2 ÇÎÙ†PÉJ=V,)ÏœfGCÓ2§s¦2™;ìÖã< |EâqÍÝ~´Qc ýeOWížÏ¼4iµ—v\ºA] »˜n’9]áô xªH»õ}©ÌlEfôË—Àð œzÀœÖ]ó„ÐÒñtíBÕMãÐX†Ç*QÉ´ñeáÆÀËø·dºrÃãâ²çòÌ÷©8íq fÓì¶QìÔ1eW%Ñû.’Lò–,n dÅ}‘,L°šÔŠ ²ã¶[Œù/Wˆ8hžkäí%vÐt7ªÏ¼‡úó±Ñø6®{&ÍÌÛQ‡³ŠpAT‘mw&ø)èØbÎæ9 €ÁŸÕ(u|<ÏIÄÞy¹ ¾o>»¼\ƒ{]ÄýaâÆìÞ² ÌuâX6#} Ûüi [áç‡g–pÁû"Ùy%'Îc©,ôEG·,Ñ%)m pfØžŽ›u~ì¤ôÁëƒhö¡ ð k0ãûÍäÉÿN掮[ÂrlgA¶‚^èÍ;;x o^<‡g~¬d/Ä*1½sÒ§îÁÅ‘,Çù>^‹ƒW°õ‹³=¡ /ã¶ëcð¦˜$L ‰Gßäʱ8˜o?Äó¼ØÁûNlgx)¦þ„ÔñKhÍùBÖº»/ª €f·`u[dAçýœÄ:Niàûµ¹ìõ›hVóé ¦¥%‘È|sº˜”ãËKôyé'^­ÎFöÂ4s>±ïòåé/+ Øá±Œý:Äê4ü!/Ì”]øÅcv³h¦³[û³†¾ˆÚÊxùDìF0Ž›¶ù?Ï2离%„á§ð2 ÖmƵe/¹Ï2¹ ëõýú娸§ÜþgMpU‹G T°kGÂÙí`ýÕÂÑ÷ÈmŒ2Í ²‹/Bص|)©Ng;ˉ;˜ˢû$©Ì 3ÐÊ>ݱ[ÿR£Èl*.nJMå̘ÚY 8õ8ˆ˜(s²T³üXÏ`-mÝÿ€ý I¤Û×:°…e›Y¶X/îÝaÃj–Ñ–SûѸ¾¿M¡óRœØp]9½§—Å»À†;³ØªiPr©›N7(D—-ZlÁ˜‘óvõyð/|(ư·ÐÎöT^{ñÀ›ÿ&(k;ŠšÄCÙ£xœ_F•kXV·!–J±¾YS¤#…¯8|h Û¸+Ži™8rüË×F9c[ê/̤#Ïã´3Úw}ƒÌ©‰B$5‘`B6¹hÑ`ŠîÅ<68µ˜Ãß#ùzÿ\àÖc“{}ÕÇê˹U‘œÏ 2Áò¤[§eR°e’^±w„äú[X¢Z®Î|Cñ3ÍܽŸÿó,7~À—(è ë&û²SðM² K /FÇðf´Öu6¦½ŠÄ 9 éì=ahº1N•4ÀaþÄ;ÐÞ»Waþ(~Ïã>suÏúàíî±TíªªÍ]žùnƒ ›Lp©¤(½šc‹™l2Fп|WJçDßÁÅ]ËÙ“ê`}á¹±W‡e²3¼¶”9pmL;·©µlLgËnd’9ëDÙ:QKæv†U¹Ér eðõx ŒŒK‚¯ß)}<ƒ-TP SþNn½èzn÷î"O¾è±«jÂtÐcòÜÃñåŸ[Å:šðá¼R‚}WÓØ“¼±4Ýe >Êñ× ³ËÊ]T´ð1~±ÆäöðVî{6ÆÜtƒn?7ºòa2~Ù(­‹·ÒgëÍ©[UX=Û»“Ø « ÌÄ&™;0í+Éã7`An'j\íÃèGѰ~ºw¨E”ê}L€\ƒ$0]™Mj´:`¬ÿ±^2žmöLæOîÚ†_NHPŸ~¸¼ãk~c¥e7uO ž^¯JÛ2¬@èÒ XfëÌ>B$ÌnI±re¾ã»_º;b¸èM… eÑ‹z?Z‹· ༆í"Šìùêó¸)Ó$>Qúûî|²L–êÇãîðrÈ}Þ»ã]`ƒü6z£y®] ™´3›Œ;ÈݯEHFñns—ýÕbonkP5ãíp7ùMëþݦä¬5YmåCKÛê!ÜD„z-ü½ö©äD¦4’ÍG¯À” B4ú½.lOwÃßÕ°¼å7ÞðYÜ|'"ç*©Í¦­:Žæ#Xå¶sïµrÓ_2ï? ’/ ª(O¿°£³|ÛQ:j:•~ü ÖØ‡Â/I&ܤºJ»É.4ûäeœ$w7ÂN{TÔ¡UM•øÝ勞ØÞ?ž€ÐÐi^øzQ|±- ާýá\eòH̲ÿ·&b€,¹oI?J§ð¼vÆ£ èõt&»*kmn@ïÜ5¸u}7°t£ó–fS±¯ñë§"ºÔù/gä0"èËaUú{§â–†-ƒk4™žÅ}fþlñ¼8HKHÅ)ÝÑu¾ÎçïãZ+¹8ˆ=²ÒL@\˜Êé@ùê~ÎÈb.0Ž.+}†bHJÇ\š•p˜ýçU.+ݦ²7#ÓxwÕ.¢v–*. d+7ÓÇTõ¯OkÑ› öïJ*lÍB˜óe[î=Š\bø7´&ÐäKõИÂÞ]ÝͶL `SBvàS/oèR+ÂÝíÜe‘qlv¨•tÂ]§1»a'ú}ÝX%E.`é4’°Z +œ™®[ØÇ?‚¡O"ìÛŒfàK):ú¶ÔƒÆÕ0åÉûå*FŸ„Dá;ó$&ù¥–Ú¸ó`òayö&E›j|Jc ÊèþÊxöH²?rmàü®<õã½QD5¦ÂNoScÚé€~{þÀ¸)cÙûÕY´0ï1×Ñ’9àñS™,öƒLÃC´<µo¼-ú–°qU/9 †°Í!Ÿ¦®z…C_ÞÑÚåÔ¼@žÎœôµ¤fS»ÈN^…Èbø7M†¶¶E±794\ï×#ư ¾ÖN3@wëdhž6M ïkŽ*ø¦½Š¹ä’=.±Ø‰æðdÓ)ø.À±cgß²CfÉÔë¾sš™DãüE¿™ˆ~TØÍü@fQ_PMVÁ W†IìÝ5Ô6v/n?`Ì|šÃZÞÛ`ÒËœ5/ˆ«ÔÙ ¼¹ª ¼Jˆ)T¯ã×ýºÌ)»]Çi¶ó”/‚³òfj ®JjUÝØŒÐ£è”3Ýý(Xµ òG.qºÜg’¼¾Ž/J Ó>« VN—/†uùÓim}Û+û_/gè säò$[àµ%Çß¹`€iø6 °çð%2›ñ?²ÈGwîCãqØ¥…rgÒm–þL}Ó%þØ_…°ÜÔO¼ ÀÑñœc\>v4ÈÓþÐZ_l¯‚ï¶x<~q3\Ü7Êkö‰°\MkªÚË9Ƕ"ø,ƒ1%ò´{…-þíd–‡˜äY–]¸Ô?MgA“ׂò·X˜?nwzÃ…Kuà;ßW‰¡ÊþKqñícô¿Ê«miÊæ!Tú®KEüãºÃßCˆÉ/îäõ|^`‘5›’5Z½ r-¸ƒ'¯kps^˜ÓWÑ}ÓmÈî9›Ý=¥»£qnÒ ‡íè·%ûØëa%îÜ¿¸O&¿=œ`ÍXرΘ´Ó;xáîdva÷|xå7º)œB…ýÚdaóBœ7Ob|§ýÀ/ïCɱÇÜ+nv+k±¨#Ǩ×ù9|%½ñèvrÌÒyIf澇wÌØ#Õ¶ûã0LɪÂa|I–ø£iô ÔÛŽ¾!Si…àNØf=€¨cs Mjo½š37\­ßÏÓ¡Áµ…†?¡QÀ)ЫքÊõ #°Ž¸ßi %W¥™‰œ.mZ¾–ºŒ€Qµ:M|œIVØÊ° [ÇÐ^^é.ÀO»@Hp,ndž…бҚ=˰çÐ=„Ýï< ,ð¼ãÖY6Äù—\'ÉZw#zúý}’(ÆþëßÜò½¿fûƒÿÆ=(x›[–sö?ª‚r‹]мѾº¢{¡)»~[€5Lx…SS©ŠÒrúÃý °€ÔìcäЬÎúw)wèï%hð„Üõ¦¬_Z•ë䱯±M×".ñè^œôç,¾1†Ú'5s¶N3vϵ`/=wÂШî^ô{-¼"ÍÙO&.ŸQ¯|7[r;6.Šç6?¹€ÞèS‹)¹Ól æõ¬,L€n¸&Btó=z`© XwðŠv|F‰¸¨å<ö' âåº=ò'9|Лé–_ƒ·¿´Ø¢–Åx:'%LÍY`ÃZê0 ­»Ì€)TáŽ[×dihrØšx@ª[)Uåλ«%Ù³]¯ð‚÷9l,Óa3s4¹3ï‹Ëåià=[ê,|ßaï—„³š„÷0*^â‚"¢ÈÝÙZô§ÖyNs–*M<éF%ÙñtMzê%‡Ý· 1¹E‹e Áå—nÉ]Isi¶Ÿ ºÏ·ƒûÄÏ8éT–®ÿÎ…ÚG`E¹<ëh¯„¶»÷ÉÄisÙ»è ¦ÑÏ%È©G›8±»ypZÔvË3d°E™|_õö‡» Bì§Î&øÅz!kÝ5c}¥S$™­!¢Á½lr@á F¦G£øé_úãôÔ•ÃÛËQ¸cf,læË´tœyuJ¡6W ýÅè‹éõcë ÿsvT0Û—p‹Cœ•3 »Óut=7¨;•®øcÍajzã:JîO³Ö óîŸ×¢_nâ‰ZQœü¾éhQ8“w0|¶§Ãþ×Ç9ßÀFNôy&ÈOáÖ½Õe“uoa“RÊ/þ¡Û¤ÙöºtgÀOØ¢Âí.iÅÇ_>’ñpóÆ OSÚæKðܱµ<ûs G- Û*¥éË R¬TˆÓvÊ€OäRS#Æ®%oÀ6_Žèyô£ør3²&ñ ÿ¤¹ïZx:¿Â/¸ÀGì–æm†“?ÌÙ7×ohµ¤šû£Ç^YüS]‰—¼W—΃—Jt–U¢Ïåi}F žœL½Ú aÚ^Ø}¸Ã6‹·î;-¥ÓS¡|â^ˆ¹ÚÆÍշƸJ5î㤥˜äu4œå¹sï<&ãXÒ‹‡älž}5sÞÃ᫇¦WŸàQÛ]=B*Ú ÁÂxˆ«ˆ8F/xS9›Ü"Wqòûð1òï %íÚ÷‰+dˈ`¦› HížGT{ÓvÑXéœ ¿ *pé­Küͽ£œTîç?.K:²þK¯ÈÆ’ Üñü^­}E¶ÜëÂï‡càëOîØT9^I¢K¿c‡9:í¥(tî¾ Ú½Jl(Þœ¾JÓ f"¹äK'§Œ8Ë)×ð¤òaœü µCIK¥ª–#±=æJÍU^óÔדÐRKš|'—ÆB‹@>¶¦Mb;v='‘ZRôZ›mŒ"çÞÆÃy±W¸xö0ÜŸ*}ÝÉ$låN<ö×hÜÄ€S5RÁ;!å™/l•€˜²™È{øœ§áî y6sh\QXYæáuãŬмWºðpGÕ¸Ìóñ­Eù=bt a=;ÿë8OâæO,DÝìMNuæ8.cý~—Ïå ,äjÔ^pù(.Ýq‰L¿xnI p)c¨–ÔœøúœùfB=Kf¢âþÜêæÜ+¡:4죛v®âÖ/-À˜¢ôõ›Ù ߪ‚Ó=›Œ¹áj;?œ†±ýÀ;šÇZ2 ûxîfÑ'œ9­ŠŸð®O,° ¥c6ñ|dž¢t¿=-^ø‹;¢™.?5Ø={9ºÕz*V8þ$–‘'9× ¶øl|¨÷Ïg—™Â¡C­÷ša–¹Ë(cg—ãRcEÚpĈ­˜ùÿ´Þ†õáÙ°Ío,®±w[õšR0GÄN„!,®u§‘à_äÉšÖýÁ•£Ïáú\ì¦=2×ÑЬL’´¹KßsÏÆå£ hª_>Ÿ$gêÔé‘#‡Aññ]þâ¸÷Ü©rîÁÏaÓ`´©zÎYÒáý“8¦NðÚJqx0p#.rÜþ%¿qxj:¼Ô}¸£O\‚ÃײHÝÁaîò1!¼«Dr áÙïîxtQ7ocîÞÅ„xçR<8 Òo¡Ÿ€è}ĀܵÜUW7nî­i4epM¾ìÅ/žžäóšnì¸j‚;o”Áègø¯ôbnß“4/É'·ÚŒYÌ+"쉕+ïA—d>l~èpFˆ¤Ò¹TG’qE1r›‡«¹–{µdÁÒh>åW@xüd”Yý„?0û:­Z޽åEpÄCåäõ ,àøªo$£°‡[ú:£ãÄáÉÈoeðTÛŽ­nà’Öµb÷­Ø¿bn ñ‘ÍÙ*S[±o_>åî}˜¹ëž¶‹…g;² eu)¶:ƒ»¥T˜¬‹>íuÌŠѱP·ù<máóR—ânß™¬È[;ûýpqu8]¶ïth5ð#ìÕ©s— ÞÚüB¸£çtõº¢K9¼³Ëã&¯²`Ÿ—|Ãòrøè«ãXcg,þð O."¨sU¦9ÜÆåB où‡·KW‰uÕ¼Cï¼X58•½ýð ¹OwpŒÏ¨Þý]³²3Áê¶ ]ôù +†cþ­q°†;s¡°j,ûø/+ûLö;hŸ UãÆCåLœåq ã´Ù§¼Ù$õi·kùnìèý’«…RÁBnÊñ“T§¹‚8ϳdêåcé„=*¬S}>p ®ô*ªŸÞÀ^âLæ!5ÄY®}…6Fzظu1l‰TçÖÞ(Ã7w•ØÔ½ëEi¶ûÖ?³ƒ$–A”¸¸ïNâÌÑoù-‡”±`Ð1;JÀÎ3:°­¥™ÿÌ¡/ïqc¸Ù­/¾Ñb0߈Þè8IVßHIí28Ë3#›C°Xd%ÎMå‡Uð]ãl<õÉŒ&ͱÃ]qðÉœVÈo°‡.ËQm×M’»ÈÒ;‚½v#Ñ/În OGç§)ع5yMØn%C—_q£æÓñkÙqnŸ©õ× ÖkÕÉé¡sÁ^°¯Q€ìÇë Шÿ¿>Ì\¥þ˜ÝšN *‡‰úé’oæKv=²å%½ÏEͺ Ì=íöºŸ'J®·ŠÓŸ¦90ðÍ?ßOýçq–¬TâÊþ à™1"t]g×¾÷Ɖ%Àä ñí³—ÿ«IˆøŸ…ìÊp4£­nÜIçÐP1ŽXÍZ‹9ZáyÿCRœí ãO‚øék|Y+GÔŽ…€?8¹Iì¦'pK½ê¹û2®bÙ¾¯ ¨v¯kÞ3 k'ŸÄØói+ØAç\_óSP[ IÿêIω…fG“ö@lúO7“ÅÍ­¸Ëæì|¥É:ÌÆÒ›bø¯wtú¶)ôÓmŒ¾WÆå>þw ´æ!NÌsÅ‘ïÜöùÙ8_øƲ¾•­!È/‡ ãðb'=Sòðež"öìäÖ˜ïy§¬¹îŸúºè§,ñŠû+¨’šÔ}cž5Š¿ˆ2;T OAš4¨öŒsððá%“«ÑþÜë„&õ½W J ¥U Ÿ„Ø?fúâcG$A! :e”×ä`·b¨;ÕàþxäÏŒ•c+¦|&bš",¼R ìÞ^7;ÖO”1Óã3ñp=Jvæ͆ú 'I '‚Ñîz2;ΟŒ],‚OE ¨»†(Ú­‹·SÅxgÅw®x}'—ºŒG¿©«Ñ—¸‚NU¸hÉò;÷1Ss_÷§;ñ6ï¬àþ£ÎKºWLŽÖÞHÇØñ áÂêØq yGQõï Œ›!‡aÙÞhµç:Œ»ØjSKpWr./@­Šk˜ægva‰º8k9'yß)ü\2ª#qZ Ò´ho¦¼œ Ù{¢˜RžíµÃAN€¦u“/¶-ÕÜz_ÁÖÉ– 7Gl¨2«ßÌAýM/|øÕú¿ñǸLb V.X”< {#âúà=¸aâNîU½Õ#Aô”xE_/¡„y(íkD«Ô’áצ’5ZË7œ‹`)–ßñs»0»í¢HŸ+­ƒhÏuÁÙŸ‘p5(‰e¸ b1éÃÍgácüiT9S@¿F‘3‹˜Bž:}¡Âc¿Ùàºù11¿Šë5fnûáÕOz­ßŒk¬sA£=cé15qvbÇ*ºÄ²Ô‰r:‡Ù¶ h¶]šq5ž¸ý‚ ¬ ‘ÃZÙ…ÔjË®IߥÏbg^â;§ â5ƒ‰Ô"ë5WœdM}]uðùïŸxG-öYˆ³'±×Á&Í;ÓbX×)ä'Ýæ"o=±ªìé€è›dâã•ydˆK‚/-¿1~©#i{ßA~hd0­ÿüÓøqɺÕûè–ý…êý"Ü¡ã6°îaîW‹ù¿YHð%:ý±`¹ïÊx‹ !ÇbRëX)ö³ñjôÛ1™oiô£Y ý|ïIx@ȹÜؼîm«Ša‘™(‚I{?ऴ ôë[m–õ¨ª%SG™(T‚|ôÎÕoµo§ËmØï)‹¸ÿ|ßS”³@iâJˆ=3GÔè /ˆý^¦ A–~Ù“Ž¹B¹ÍÑÍ$'|9HßÏ€}»Ra’ö22^¾„X0™%“èö²˜o­ÊD¥Ù6îÅ3kœ;Ëœz=úÇæÆí¦™_”™ÄÒR´ßêD…¦¥â­Ü8ç"ÁøS—Šä%lrJ>wòŠs#™4üõwXÛ ã&¸óFgÁ͈PÊ¥R]O)ìh‹a³ÛÇó·«±Ë@Ò÷Ï ‚ÃÈ Ðwî2vpA ]ç'͆R>ÂúçÓÙ`É$ü¢uŠKìd&A°ñJªù ôõ­©f® Ï9w?É}íHÏý»C<ØHò"öCA˜&ô‚iõEü}_ !åôaø%™è°mþ\áú —§ˆ1ò` »Ü̵%‚ÎÈ0K·¤%mêÐn$ pL„ú¬§77’··^à3…]~â–T`Ç"+|àñ‚d27î™ÉÚ§˜³Ø”Y8³;]åçL÷&.¢Ê{sº+Ø“—¬óPÛ%Íèwâ2ÜR Ü×Ål«Á|–öDMwêÆ-²Ûp÷Û(ø‹8]æ?ùõr²l°ü®l¸÷ý;žv6ü;Û~ôã´y¼ó;“ñä‘; 0ƒÊE8©p?ìÞónTÿ¿éé†5Dª#§ÃÖ>ÞÚQ>ÒÌïÀÉ¥° °‹Tëà>Û ZqKQÉç7x^¹ Æ>ôÑÙOx@=µW%í‹äX„—Þå«ã?-Ä3*¹øÐUèâ•€tpSrNu°îK€ÿúE[,£©¯9<#Õ‹’!oykR}0¢ñ§ÙÓBž»§áÛ[9;S0/ÅžmBþ¼\¼¦:¬+A)²ð¤ûu’™çI=ä¿‚N¹ó §='¬5>j4¾½£×à‹²Ü]8¨MQ‡à†æГÄ'vãIß6Þýe´µ±·mÜ‹î§màåòÖwy"«ü™Hþ™År8s<34ÑdAþª°&¾SS¯òg§Æáž:uöPï Ï8e½žöjBùºwWC¿š1¸›IÁòݹ¸Ka%±0p³‹³Ù–œ•œ.§NÓ&¦B¤(‡W»ÑÞbÀ¥ÁjpEý$Dön„ç7؆¼¦þá7Á1šºG„–E$q Íý°î€+®ñ„ãÕ`ï¸dü«ù–¸6‡Q®0ƒ_¿-ƬärȨsº~ ë·¶²wæA£1&nª):Ñ1c™ûå]T%G}‡±e•g±Êß.ÀÛ±à5ÒE~\ÏBcÁN˜‚k™N³ óõÇüÿŠã²¶äÜÒ10rÁk¦Â®÷³q5ÊÒ÷þÃĪbÞm:ìL©pT¦suùx(Ô¥í=XŒù^Ü4(Å.Ø·Ò˜bÍ®E ç̿¸áU3êVý%ÿL’á÷²Ûèzl=¤·†¶½XÊ„"ÿñø¿;y/„e-J€ms¤·ç;æ° F\Qô;XxÚ9EVãä×¢¨»| 3а…‹ß áúE0}y¼¥MÙc÷{°œ£X\—ÈO·{ÕÂ)k-Zûë¦ÞÁ=¯óÉèwÓÿ¹ç¸Š%gÊÑŠ“±Ø&oC*Ðð7|¾ðanbÆ °é±`‰Ýka‹ƒ4Sþ¤D¾ŒžqßC ¹\KG“vÁÉ·ïÉœ@+z7ð+‹±ñ^ÊLðþêR°›;vÜ“ùf ò¦fŠRíì/àq< s«lHõcaNóz®àæ³çñhQ’ƒÑ{x_þ›BFë쾉T®Òˆ~æ™PÉ•F`P”Ž÷Y êµNhš˜ÈRÎ¥ºZsÙÉÙ\_n1\ Ìå}йŽwì_Áú1þôëZk<:CŸ†Y«±ÊEZlï?uö&Q†­ØOL¯.Ä{+Ÿà¤{qhïQ¨ˆ´¥a rxåýe8zOþØ-Šù›Œàgk]¹’»QUÈMIޤ¯²Ð—÷z^ÂŽ•‰`þõ ÙëÄætm„8ç[P«×FîâÜ ¬Å¼ß±ã¶•[%Ç>B6æOQgƒaFpáY><.àÆ/Õ€·'Îã¿z;Z0S–9OÏb7^˜1þ1nàøZü®~üòï ·JÛ9Mˆ^õ÷„χáì™c¨|o7‰ì¸öŠòü{ÙP*6@*}a’/ƒ¾W· zdwü+ƒâ‹æÜ­jR2•²;ö3qŸ‹x™˜i$Æi|’é®É¬w £¦Ê‡TüA„.Nf°±”ÓO/ã¬pÝF¿>“‚홡©&×» ’ó4862{y‡åðÑr#«KíË1fl<.¼ñž[æZ‚éÿàAü:úcZ’,Y¶àÛNæ·p7Ê÷N¤[‚²Ayç=¨ËZ˽rý‚Pú[ ÛÑÿˆSàɦ:÷«U©Rbj*mcS½x¸Lø ì0cAÁq¸}f Ün‡ ߙɗºýŽ> ™ç0\" sö)²9þ[_Ëœ€«ë]¡¿Iïü•DËHãu»QûW|öPû0=Õ`Â.<Ò¤[”½|†§Ã¶óÈ‘raÊ3ÍÀ¾€-8!?—ûú|õÿðÚc”é–‹-d…¼7×>PS¥ý°ÐéÆ'àš<$¿ ™cpSÍŸwÁœ/10k—Ãûí p_G!/àÚ7çûž7ÿS.Ž•¸éðËÃÄЬ(僄ÓNŽ'/äzZ ©åËn˜o.M^;œú oFâ¸KûWÕøA²4b=íÜiŠ+´îqr›2Ÿž‚=‡gAè¡Ãpe®.H wbÛ·™ðèyYâ*‡;lD8^Yš”ÖòÇ>\Élïr¢¹±p‚/㺈Uš<æ{Ç-ðÀÅ£8t-,w½]CSç‰á퟈UÙs~Ý9D-± 02<@þqâ¤õßx6åXÔ.5aGâJ a6³à—ƒoâiˆ¼öv´\%ïÞ—ÿqLáÚ5˜3í¶Z.€¿ßÖ²ƒ¦§!mC3Y²1—¨8Ò’gWÔJ·«w“òi<&²»ä:åÙ¿E.ÌH¢L•~gU¨PÁÐX^Í9Š­`§éÓUÂì5 åòOw£âüîùË-ïó¶‚ÄÀ*úŠÖ" ‚û²@¬e2nÎ…kjtñ,;úwâOtX¥Ãô\3!´H——“çú¥¤1gxÄÈ·ÎV¹÷¢`à œÊë…ʃ™xB²ÏºêdÞ {²:á,4ÜŠÇ ïëx3©aP+QÎmèØ„oõ”¸7?ZÛBáÃ&AžgôiL¬³„)ãþ¡ã‘ìkG¶õ¼Èˆæ{NÌ+¸’ þÁ–TÀÃ’=Hã^é9¢r  ÿO¯KâÓi¨öó(ñòûˆW¯ÃÛ†WÜûR Ú{ºª›¢©RÍ/<É­#cïÿ%æUh”÷5…ªQýg î\*Lªv4 Øõûÿß·žœ£ÿ%ˆÛ[Ì™þû€¢öס†+æë­¸³–ÂþqÓéÅ'±÷Õ,J¦á"æõ,‘±V}š¬+Ä ÄÙ÷¿áì²iñ{iÍ.ËÒû…¨iâú~"wbî$f¬’ÎôÌëèíǽ0ûxÓB“©÷f>•z{B˜ÅýCØcKUï%Òæ†ïàÝ ƒ2ÀßJ‡n·Ç­åe‰ØŽÛ»ézÝ…+ŒRè³üh°åE+Ý9¬^y•n Hͯ½ÄìŸ.û0pšf_x1Az!“rÍñžò2:oåBzº4‚šý+a¯wÇò†Ùa …£\ú$I¢O90§uüÚÁt$;FЫô2öÍsİg¾Tpi+Æ.JcF=ç`£ÏWŒ¶Ä'Ƴ¿—¨‘xŽÿ¼€ÕOï‡O«1àP+9#ųSóeϸ*PÕºÀά0¡ýÇš˜ìæ³p ÷’Üî2V>{#t®¦ëK€}Ž¢ïMÞãgiZùl;±XvJŒ'";íØþoº,xÍ 8ë0DåLÙREkÖ×»—…™x2‘ Räǧ<ð˜uˆ©¥¬¥ Z¨‹~1;Ædeãƒ×j4HW„Vhf€@x"»ód½²õ ùYø§û鱕m™›ž!ûªA.ã¡Uj8Çå<ûgû9õ¢ X–hËâßÙ1µuØô¸ œÈêXuLm|Å›åìÅ þœ fG´ÙVßߨz¿˜É‰N§›âƒÙ×ÅÓðÇ@Ož‚#˪‰Ås8`Ì•å®Ç–Û{AySÄÇÇÃÓQ­v6™>OyÄ«sŽDeKröZ4Æ+Š#²flÌàn¶ÕÒy pÖæz®°2ŒëðXÂMÜaÛ´AüQ°cÝ¡VôƒH&$_%m¼qLµªfVò«ŽÀÊ’l‚¦‹­>ÜÚÉâ}ùÖg° ÉLUMƒÎÞã&Eþ€œ®6k¹9&S÷ÁÆluΣÝ—žÉµEͨIW2¹¿ßñD±9¸÷‰ÎHáe+t€•¼ˆïêÆ'²Ù¸õuÄл˜ÿ†âÖÒc‚‚ ²5›Y®þ‡'º¯’qok0véK]—²ô2Lö›ÀÆê¾‡Œ?O¡1«ìPKùÖlåäõ3w{z‹p­ 'mðo€xE%ækqǹÚ‡øG‰$ žM›Sðõ$Zèê@cg§ È G¸ýÕ™Ûƒ®#•œV»û©§_“ìhº€/~NUuÃÕÔg÷^Úå?ÈVF­cuÅ“©«Äú´âÔp¾…&ŸãËË:´’O% ±žl3j]¾¸¿‹â›¸Ç1­7ÉåIÝ(mø:ȲòþxØè>4Ìæl|®ÁX=[´xtÌ€Ÿ+3UâYäÜÍ”¥7‚nq ®¿ Äg|=ôí–Áo#±X™GKŸAÕ¦^x”œr¯QÒbkŒôdÕÈò5=üsd;{ÝS®¿ï’ܶ£ìyÖ±sY P¨¯ÿ,§–BNô­\„ ¬ÄŽq±L´Ü–Áæ )Ö­œNèýYìÌ^ÆW²umÑÔmGkü›„öØCÓ} öoõ&vp¹>½jZB~¤ßƒóoÉ àÊ|X»dFõás¹t7»†-¿˜Kü×&ÃË•Áô¸´98‰ªQ¯J öòíJæû™‘=OÐìÎ T}}‘LnÆ›#)CG³ Ÿÿ1a_¶Î…úpeºjŠ]ŸÝ{oÎêë]X}î*¾ä‹=_ǽ—2ñý­N¡QO:-½d‹?°i¥úŒì¶{e]Œ]«¹#ð镬ӿI›TÍÖh’»Áu/¸ˆOª£ÅÆóœkÿt¦yŽ ©MÀ¸I¹øfë::KG‰^ óð•C/ß½Pˆºÿ›BöZŒÖ­@:M;x¼u"Ë‹ƒ$× î®J¨}4þxFÑ®…PÁ{ˆýËû°öÝègí8Þk>*/îåâ}Ñ©M”ŠEcKK$XÛH¥»ôèô]å¸tœ[óÓŸ}RQa·Â¦þíÏ$úñÐÃ^´ÌcƒBä`;ž€e—Ø §#œÐ¶0kMÅîìH9rû5T8½&IVN–³²}8…ØÂ«£×AíT öˆnŽGaðfé|öé† «·ìÂöze:î: |‘Š÷-%éMàpj<†e­‡áÜ8ß ì‹Ác|Þx„NÙ÷]]â+)[ªsp<ãÑ»œÅªXV¸N· g«w¬à,V,g[ 㘖åW.¦ÿ<Š;ǹâu˜V3šãÊo9áùËxa"‰à¶›@sŠ¥ã>ýX7ô ƒºý~Îð½/ÇçiiЀÎSt‘Õ46rª;ŸXË%'à7&¼HéüãtÙì¤ùÆPè8‘¾ ³¡æWRáüw#øÓ•û¢þ”Ô‚tìFv»åßù‡}™ïÒÕvìBêþø8}¨•y‚ZíLú¾Ì=2B«e sÒnØð)=œý˜»¿M«¸õðÈ(L.뱒纘5nô©Œa*¦ÁI¿|ñÅxo¶t½$än;ˆÇUüxRLäÙÃÁ/zTêÑlº¦}ø±î{ýA{{)*¡¸½Ì4Idþk]°Bkô‘¯¯wZ¸Ã32`ü˜Ã89é[0™:/dæá¢àIÞ{^2'zRÙÌ2`®¿Þáôk1ûÅgnÜÔþ²¶T¶-€ú %¶|¬9Y9ãþù ªÇ¬‡ŠYþ\åÆxÏ“æœìCaUÍ\q1–¼ûr””.Hrxòu˜œ_‰ÃÂ¼Ž©•xÔ˜Àµ 4¡x)ª›áŽHŽ+Ù£YZ.ïÆ›ÊquŽ=½ð"ì¾êí¤âø3^ÃÍ­’lu¬wa0žFÑýS§3×»rTvW5^*V¢••¹ÌúLš&P‹cößDÇ€L¸².ÃGNrVöÃÖ…ÕtÂìHÙî‰Rogãó9¾0ßÇ€BKk‡LûùTÎ~5©i6ÀÝQ&l^Þq¼3ªÁ¤Ìüˆ¹xs˽Lš¸8PgDÿtDR‹éÇÉŸ¤8jû´ü ǶزÇçÒÅoÁæñ Ø#Á´mqæÄg `©À& ùçÑÜÞ«D8ìË P‰º„Å¢‰{¬Š¬«O§Ó„ à™5Ëä…ršÈ„»¬}é :è+TCXÙ™T:a¦=-Þ)ÀzÆÑS¦±½÷nò|t?ó_?ÃJ§&8ñ3kï‚×g²©æ+Ù“£ù4ûðïý0ö[&fû&âë>=ÖûOžêª´×ãß$±Ô&aÔ&šÑGXâÔUq`ËO,Ƌͧùs’¦pK/ÒôÙÄsìzã¨6ŒÌ&–ýGø¿^a÷#ðzw5e>íÙ0ë§(>%¿îÆÑ˜PÖïón€¼è±T›JÎÏ£ËϰÇÜMOK¿ý‚¶"36/ø#Îÿl‘޼)‚>´ü¯Vcê¹'ß“9C™ì[M<&W¨’¹³bhù\WZe¢GC â[Oû„½‚*ðÚÅ’öTÃ]KN€ó}YvÐ[ÓùîTNk*Úl¿KÎÅœC’£ïyõ)/É¿íg¸ß)´ù„}ð:—N4¶¤¶{“Ù9Á;x«„â—)ôõ–I°ÿÑoõ®xù#ŸÍ»¹—=UbS®’Åj{à˜¹$ôZøâæ[aνžwáŽÆq%ÞqPsâ÷0tײ0‚=ÎcLå*\½ã0|Ùæžh‡ïWà“=û #f+\ˆ^‡?æªàødÄ 0fº4úµÑ 1%9/ ¬ÚVo4ãêëÑáed9×Öƒö±b\âhF= ´¼Pš„Ÿ†/mF8²(´û…Ù+K9®\Àuh߯Æ#u\•)ŸÓµÐa§ÜèÒÏ©h{u?(kÆá¾ JTz‚)¿änž»kÄ^¸%¡ï}œ‹íU¸…gaÍýÌÀöœu087žßí^r ™$Då$¨¯Vg½uÆì„ƒ39™IoQaêÙ1P^£ sÎ(Ã’JÅÛVÐ-%¡»î~Ëþ…Û«3Qv(šÏ¸á›¹Ö¼úŒ©£†Ér‘ëþå Öçס•ÏV¾D{ôñ|_ÌoY>­^kl„š7 ñzØ<Øox—ûu¨ÐÄZ~uÜQ’æ²›÷vGw¬,×ô²‹k xìèãß]`VßÌ%Üwa~Ó2Ù÷$m¨Ä–Å–ÀµKgˆq(RoÿÄg3ÔØÅÎëx|ÌmˆßšD”yÝðOû#ùþœGôƱ5½6Ü×õRÁ‹–G£cóx2ׄ66ËÁÌ¥—9ÿq™°à”}ã³b¢±ñ×”ðJÅ)'ÒãF­kÿb(Í$ÙãYAñxø*HV,§šéŠL¿KŸ.yÏ ‘#øß<ƒü48.çÄ5æ ±ï£`FS"Y0(Î=}³ o=ÿå—‹‘YÏ€ÁÁ ÎnZÎ(ý>:ؼ£ÆoØŽ»¿ôáðÑ‹(¢ÎŽ?Çý2gÛÕ?ã‚Tobqô ü1ßw?§àÚÓó éÌ.ÉlÀ0£x·t3DΕAÕ*žRÆŸº ®—Ѻ®\¨Mâ^Žÿ‹·_ŒÅk¯Raa Ûü öÙÜ„’•ê4éH÷|€ú³  ô4¦ÔñâW\„@a6`'ÂTÆ<ƒê«˜uA$Ó^. ²;ßðl¯w½§:ÈL6aœ“>Û4¬ ³ý¸ɖ½« @ºD”çMÿOo•Ó÷ý7Ï¥Ò¤Y£Fš5=gB"%S†™’yÖ¨$ "E“ Q¨ž³Q†DR†!dʘáßû÷_ßϳֳî½Ï:çܳï>g¿^¯µö³/Ô|˜B;rë˜åñÛ_ºÈãí01Y¬ àum<Ïú8 $‹öâí~|ëÂàØKL-ëÕ¡8Nd·øb Å®§Ñ)å+4ìJ¥†JôìÅkx©Ë–F*ÎÃMŸf€ÒKX;ðëG ÁFáåÄèúÀ¹X|û-sò³Ç€ï`Ó© ž—Â3‰Ç8'Éf0 úæêR Z4™×øÔ’Ÿ‘!¨÷«Ò³÷îr¤ç4Ù›“*}ñj·„€†l×tYX+o ßJPõ£)ß „N¢&àœ`@â6傽ÒuÜ]2šÏÝú înW o¶ºÓ£S}¹è¸E¬ò×Þq7ž~-Âïó> fÄ”BÏ[Ä0éø³úÌŽG©Ý§a g ¯66â·ïZ±…ÃhX– ooþ3GIÒ#­[ài˜(—Î-`V–@ã;w0òÊðî^q¶.æY9šÂFaa°M«<«¾ ìºbÈñæ|¦øf3¤hO€ aNäXÍÖXï‚&ëcQÜÀ3¢´6:oÊ`¢­ ´ÍN†˜Êzø}M—8ÕW å$ŽÂ¿xá™3ºµó½M…¨© âÙ?^š:¨<ü²F^pKz#þP{Œ—7@XBVŒ9 j£Ÿ¢Nø˜SìSÏ–ãÅ> £ c;Š1}D Ñs¬ÆwÞ}è.B%Áï©j|®õ^œø®vñò;›„KýÆ@ì„jØvç y!GÃKž¸ß|é€ÃàXA0¶®Çƒ?ØüåoØã”ñpçs~Ôʤõ†@Zä(fóZ—öäÇO¥›³Õ ÄÌÅÄÇÞWñøù³Æ©QûV/v½7¢í¯sX¡ì\<:ª‚mRP¥U›¬@!êÛäx—e|ˆ`êb Pz^zŸHsïm[¨‘o$]ÖÜmZ~]2ŽÏ™#÷O9ò‚Åê¾(ÓºA.rÁt9^:õTJ—Ò·m #Ìù©œh»ðŽP’¦b9[`Þcihr_Á ©6þ„_*X¹/{þa';”À¹­ïUáñúJÌmâ%0.¨ð ¼… CšáÜSRÚ® kêà_¥r2@fð>„Þvj!gJ`JÄU2rÂt²x‡%.ú*ˆnšG%eUͬ&çÆ<ù'X ‘äøÒr&81ùÉ>×Zhlº’ÅP0ì1Ö¼xÇ p£v<†J.æzÍGàIÑ?œy>낹ë;a™õELwSуaüWTtL?‰Ó¬®Ã»Ð:ò6ìÓv˜ç'„ Cg£cô_áÅеd¯OîÔI ïÐðïôIÓ‘=—0Ÿ"RpÖ’xC9\Ú±ÌôÙÌ`¸ÝnÅ«®Çó;·d“Š3âÜÛˈïÜK~¨e¢Ê¼;»Œ,ÚÏdûGÃÑ3­L綬óuVlPæ¿|øùî~è”r¸7ÉÑN©³u¥=ñ¼‘6nñ<‹Ÿ^lçÌi@Û-x3z-<›¾£B‡R)IWŒ*µÁe¹ËIÏ¥]dd=Ê—¤Øô‘u]6àçuäÈÄ ãɆðo(ý>.Hèò¿¿Î “,Àa—8Ù0ëqÖP„ˆ&)z|Þ"áϽÁÜíÔN¢)ÜB7œÓÁÄ}údŒ2|½u‚ÕÂ%¹’…7ךó»GðgC¹©0‚UêXðѪIšs<Ú< Þ+ãåæh\ž×ÈæÞÿwinñ_h“e¯’× ÞÎ_;kÊØ&ÑQ4M]$F‰l ¬^A\ô¿±Ö’õìyÖRt³¼á¹åÚ,Œ;,8µfìGÃù¼¦2×b5,¡dÁ>¼e$ž\$³g—Àgtlî5¡}W³˜ì0y>ãÙ-ÈXu‚ô¯¦ã ø{-}RaƒCGÍ‚<&H1‹`ùÝ•L@÷¢g‘.~‰Kq9~rþQ·ù÷d(W}Ê&:Kê—ˆÔ%ñ€TEšÔúÂI [­y®³$öËJ޼D÷Çs®£˜åŽYÔfzÙÇß“OòÉßtøÀ=Þÿù{2d}ò椰$%í̼asO0ø3ø3ÙŒ¦ú§°)º5l¹•šÿHØqH ·ß ,7@Èt;žÛÆ…N)oÁÁa4®éŒ‚C;ñÉõíÂÊHS*{j¹0õZ )[_G³=ß Â;ÏBÓ¤s¨sÏ ‡'¹À¯ñÐã7›ô»f²â‹¯`ZÔxþê¡Ôç«Cm顃™ kSÑ›öQï[¥¨W* DXÏ@$/QKÇ+3ˆB–4–Ø(Ó1ÚGùús*:ì6˜PÔ^| m¨\S©ð˜™ f⢪FH™µÛó¦ÿ!ò0s$~Ú!˜òY™¯r“ª‹Õ¡\øö[pʳš“$ÌŒå°S[ŸÖ„´åz}Hûñ'}ÝΦþƒq]d9]`‹ éºÒ {¸Ýóvl¼$8h3' åe& dù°“{ЈãV°öÔpûK½éT²òâ#´Ö4† ' ®,]‰ß|¢A81¬üåøZüôþ#˜vk&þø†C&1‹õI„0‹\‘K†¥a›`Bx™*˜p³…5Ž(WWAD4âü¥¡4BtÖ Œ0àÙavýÅ+v-i1?»DÏ\µ„ý*{™H~—àõ™rt±Ûˆ²wrqºÄVô{\Â*?Äa9|Gáœz0´£ÍÁ'ñ­Ø<·ºnìÚ6/Ú„‹7| ýYfq .zQÌçžl€IœÚø¾*{,‚—n¦ÛÜgÁ²pKÇ&­Ó'aE*½ìBé ¥ìã㉂ç{¤x(ÿ o¥Í9YnÏ›þÜD– Cµ[úqè.vv‰ýÒ=ˆÝŽxûÅ=xïtò¦é’ãŽî ò^¾¯#ÑEš|„mžE†Óg ãî§õi̧¬÷þx2™÷éÓ7Z{äSÙ¸½l¦r?ž“þFüü;Ù–w£Ðjw5º[”ÁîV5<3ÒÞÎ_ö7%0|§*Í—>C>Š:,t ­ü®@­ªsA+à1OÙMƒ¶˜aùžl3z‰M_»X²ÂIá©k†ðp Q0ÇÿsÞ(Ï'%ÃB éü8z±j6"á§Æjó@'%¶ÆJ“–ŠâdöMÊeù³Eôq4Õy‘9È^Ôõdñ™ð£p5Mñtåsöìgš\ø·ý‰–E;ŒIHEÏQv¼öÐáú©Ôaú.ªº°&d¦remJ­¢C1Òþ÷-9Ä´î~"SŠz±â讑’V·™M/Ê7ˆÏžÄiw~n˜ kKêžÎC±Ç á˜i {±Bô±nF²@Ùy*Æ•_$æÑ·Àu¨%·þ:ë$ 4½1†ïìAѵs8k%rÁ„‹*²êÅØóÆ0#s8ÓxÓ‡ç³ÂIup†6\`›æxãÈs „Ç+¦ SL¯ã.}.–ùôìªqáܧÍN˜ÐôV!ÉÖ…£°š…]Á5l†§®åKõçàñŠ»d¬¬‹«F¸(®o‹ðËÇ+‚œÒYœ0ä‹&9âš³æüßË‘ÄாZ.™“±ÑD-ï¾ nWc¦õb¬VêÅ©ó³±ðD0ÿ&¬dó·ñÕëø85UÌ}¿øÞXç¯Ê3·EÃ$² vì’åQÙZ´åÀ*ÖÍ­ÑùêqXjâÀ¿…‰xI¼Ç楾$¾Ùd̘Ûu2•?/§²ªhžó¹€.KÜŦ(Kò3 ³x…‡-ÿ17Ÿ.H¾å^Ô3ãqŒ%eU˜Û·‹0²Å‰ RÝѺÇ oOƒ®·3¶ð®á+ÑWãŸÝb!)ízä§É7“È÷B½ ¸mg ;z¥A >_–Æß ä }ܰíl1½ùp6 2›ÉŽ4Ì›V~raGÿ¶â#Bgf8ÂÎïÚ´e@–_ÔðçÖÄøÛW˜ß;Z{Ð b»{É<‰Sè»SϨÊ"%½è²}–ÜæVì&VTúq²Ð£é/hïN†VÉ¥èÐ3œŽ9­Ç—îþ ž«Ae¥'—ãñÓø¹Vwö0¸W¸5Ë”~¬ÎVÿ&V©ÒÂéµ£v”ˆe®D5•|»o;Yä¾G@SèvÌ‹!6Ò×#—“ k­Ô¼†í?¦Ò®ÏÛðMÛLšïŠßbB‘ ØJ¼¦%žÙX,.`éw—sñ.mZ–̯ÅÿÚå‚ïnÕ8Gû´]W¼<ùM>K£i?¿aqÓ3xùc}të'ËࣸÝ|s.üÑK^*å‘Êðøê5¡ô‹¶?ï¤n ”êxÃî<$µwãlÝ»0؆Þ.¢³ŸmähÃAk·9Ü5[L³çÛÐéQ#¹Ï–'µÓîø@‹¨1Mõìíí^|Ç–E”›=Ç5qñôùÊ ´5јœu˜qU“ù†ñ÷aµ÷ZÞí8×ä|›ª ´¢a»£p =G§ó3¤‡ÿ¼ÇôÕ(pí&ÑÞý¤®æK‚M·©…ÍuÒãyŸ¯±·gaNÎR˜§Ž5i%Xv /;„¶ûñ¨í·\7?Â#‹|h‚áq²Ö` ”ñ›ò8`á,L˜(ÏÓ/©éM¿É‚wús® V^Ôå­ÏÕ¨ÒZ:óÁ«º—ZgñV­*¿c’{êŽÃ–[Ö<¦Õ#œ`Ÿrö°~mSTÏ¥M!³Á'aè8’Žr5~zo.KØó¥¾û ÷Ñ;ØR_$èiüˆÖkçÃÌ•Öp~žx;ròþG½^QøªëÈ;¨àœÊ œÐ­‰»Ïˆðù¥å¸c… {÷Ñ–Œü‚WÀƒ‚Î1Ï{Tïâu"1íùÒ›…Û%¨ÛomºpÉQØ–¸U§bëÊ@?­:Æ’…åøg‘ÊÔûBəٸùáV¼è&8}©‡¹‘%V—œÁiB6¨lûÃÎ?vƒ´M;ØØø›pªêÌKÂïúõ`zYûæô°Áþ°Ö8„ÿ q…¬ùèqÐbÊ‹Ás—4ï±J†;¹98ÇR‡7mÝJϾA¯ŠlÞcÆÝÌ+=LéÝgª {r Éÿ0W°¼6*¶: ïbíÊo‚ÎvcüõV‰ëbg´ß 0vs¢ j].fø~d$>nØÊ‚Æ)rq?ѺsE·Ù d™f,~êKD¿²ÔÌΖÊ?œQU“¨þ¢Èîý-"vôÜà¢Mû݃IX'Üј˾I壦»\zjø AÔÖÝÄÚ=ÝŽJÑà Y foQ¯* &ù3©îZ†á'Bø¯‹½—‹CB’ ³ã¦#gÂõòó˜;ózæ¿&ÙG%XBR!fÌ<‹Ê:êì›;ÐÛS,#·£’SvÄ ~êð ~±w2Ýüà ,Ûðï×ÄÔ¹/¬_Îñ„ór®zé븧s÷ÚÑÜS1Ö~ÞóYÎÐ"¢sé˦t:o® mš“‹Ï|ΰ‰O’ΔäYm+ìmKgÁª'ÐÖ¸s}Ž ²\›>; _ep£ÐsOÊàðizlÙú$^öê)(~=‹[^Ta³o3nûY Æò?ñH$#Yü0öç j ¯ñLaj1nÝ é÷~¼ ”< ËÖ^€»íR”ìWõ]…}^Øòu1,hÖ‚µákéM1Q wº~|ƒôGRp÷ŽÏ7Ï€Ã÷UÉŒ¤Pî±û9¸©´`çÑþ£àêÝLg<)„Eªò|Œò V‡30žn%fLFæ¯.ÙÎ`Y%BKS™öôëÄÆ«V¨NäÆþ ÙäTKÞÞ÷‘mþŽ÷l]ñˆÏ;aê¸dˆÓïÀ s·ÈË#‰4¼’¶çðþ/ïÁ;¢¶¹Æ):€ž<Œ|c<¶äŒJtØ-=ºªk Èèb„þ_ìySúÒ+p_QÌ­³EíOÁ×r+\mÑæ§nn'ë®}ÙV±ìMÝ.<œa“ÕCÆî"Í)Ž|á|[î ŠÊ›ß€Š èþwþøûfðû„·ÏYCI“ûÓÚÊúÕè´’Ïi7å#îGÑð¦ç¤ü2_\t†ÆjòMpáº}l¬Ä÷nø‚烣ð“Âjüm£Lâ'gàº!ãùÖdݺy<¸Á±¸æ:’¾ùvk³ÓÙÃôµl©ù_ð®´åc¼N@û±&F·{@_Í2¸¦BãÝoàéu yQ‰ÓÂiY@ì׺Bñäëè½ì (Ä8óŸ÷*Øþ¦°ôz«„3Ç|<«iÈ«¯ŠÁð‚lÈÖNøí,†iŸ ÑîH³³ «ÜºÚ[‚¡9ý ó ÃÓûhÂ2Sì~=”ó;tC`ä%MzÖœ¢‰ŠpSðÁË#pnüF(EY,·ŠDñ_çY¤ÄáÉ|¹¦ îä}!W\qIÌô˜¼G°+ª<›^±#n₾ˆÚaO­œÉÿȃKbÒ4rͼš=Œ†I~ÿÍÅìbïFøûÊÏ>¾ ¿ nâŒ^=?|,½i­øLj&§Ä·ÃÓøã˜eWˆ D˜MuTÀÃhf~Tߟ®Ç-è=¸þüí;¦ƒ1‚m\ÙÊm÷ZÆ>ß–†£íeîIAÐ$ áù±TÇ,ÕÑ/糸ϥwÞÁ]ÍÝØUoJÇNS£FyJtåu8õ°v-ÿÞ8Ó×7ƒ¶¤qÃbqZ3ª·_©e.䃂¾õíõM1«è„à™Þ!V?μ{‡ÿ>NÆéëh¸Æ,PÜnˆR’B5<)g…ßf/àžÇÚˆ‡ÿ6u§¾ ¦}2 Ô¬O&\#¾‘ÍʃÁkAÖm¶ Axíµ"5<¸l²Ñ  fSvŸhŸs¦X¼z7KRó §;¤iÓÜEP”– EÛ 4]÷ e©öïÍH†`܇V¼öz/ó²‚ú8$m‰e·â ï˜(*ƈ­6¼öV¤÷MÅK"ɨý,«y:›ƒŠCl¨öþ?ìBôYNÞECÕÇ EÆDM³vÌ3¢¹‹ iQa)$}ÌFQoôŸyšTb_ï0>ð;ïå5²ù›C±bn;›lHìr#6 oP4Mn™‡³j«2^q Þ8ŠNž’ÚÅ$ÈIe òT8i´ƒ|Ž¿¸D‹êË,a£þ˜ó¯ ñt»ÚqÎ&Ys­IyÄPéüÚÄÅÂîñ‚"Gna.Gº®ã×jm÷?vo=1þ³ }}qp½þ<èî ~)—8j€¬ëù ³ÄFºXЗׅôÅéBѵãùÄq0ÆŽ »?›C˜Ë,P >@n˜ƒÄö¥Ÿã>â0_-Ø"ÞÂ{¹+Êú,DùÍjü̸Aý¹(Ÿ—äÂy|êö™t¸Êd"`þУA@×Úk6ü&ö­—¡ëëw<ô`*YU1‰Öïw§S—r+¿:”X³ Þûp¯Û3yvd7.1Nå]^ ]¡Ô6÷;y·àI<؆)[8͈ù« D!;r!o¶‰ oLÝù†qþ÷| ^’SáE ]y¨¥Ûîž#×2>.nJE—šàø8y¾×A‰Ú7!Ÿµr85.”£KwlE‹o#xâÐ1<ýäWRå)’ëìi¡U9ì-ÉÁ- ‰üèR=ÖZLk|~¸òcÏL¤2¿Öò’Ÿ²ü y+²Iò¤?|J ´‡Ð9åÉXís‹}¸²êžïeN‚u’Édýñt¸¾dÁï¦èÑÐ!è‹çׅЙfô}¤=0ô |ëï"Âð|¦$ º¥‰ØYÃF!XÅ•ï~Öþ* 4Ò!ßÉIáõ®Ãuྨ®˜!I3·EB„é"Œ{ pöH,R\$Õ—–âÙ ¼>(+¾¯åsµ©G–€Ÿ[µ]޳ÂMxæÕw4LÔ]KÕýjède‹€J«}µ®Ž}X=”Wïé„€ã¢t¯×wá’ÐV ‡-©ap1]š[¼#†›zФÈCð<Þ–ùÆô¯Â!•ÉØ>j ¼¹–ZãÀÏß}d÷зDCc3ñÛbÈ„e°H÷(:¨#¯¢O)öº YÈõ 6â7–ÌPA›¼(nÚ©—}9öÊAàþÓ`øh*×¼*…Û_Ç«{åYubü>8þTsÀ ´Yü·Â+¸wï QQò%æÅRìþRzB——œˆ€…ÓCªp&ÿ]#NkÏÜgi\Eª{P'É@ÇGúZ8ŒoY”‹‘½pX?4äy™à´Þ‰†yªÜÿýÈŽè!k­HÝ´lºèš/9îÁo`IL‹oLéýš5ÀçŸÂÜøÇlñÛc8U¯Uê³¾º <¦½Ç ’égWMþԜŸ[b4Äò0}1Ï“ÝaÄ{e›I³ö,{’JïoÊÇO9Åt\옻֜ü\ëC„ciEñVttÚÂw&JÒñ_”aÎýs²fxù ÖÇNà©a9¤YÒŠ»ŽwÁ÷³óiŠÌWÖÜÊqåë$\¼Ý™+Þóç鑳éÃÅ^|Ya¡{‹¹t•Cý? R3Ô•hjñééºØebËó–Êóp¿x>Q f-(¬3ß‚K$cyÈ5;1¶‰‰Ì‚²Ô§,èA%”Ž ¢âkøfûüR†$6ë*oÍÑi;ÐyÜl(|i*’ÕBݼðÙ#8z5³ÜÂw4øà5YtÆ”«¿Ì†F¿.p¨gN•›Ðxe-pó„ k‘ÕiTðöç|×H01½ÑÚq·'yâWIví%1ç¡Ûç:'é“óÕ°qÏ7h|¬ÍcÖ‹b¥Ý[Á·}ÄXñhm Â…ÏFòú_°±£ ê’`ááÛ 6âì`,뇱Â(¶ÐiUŒt¦b»Þ±ÔUŸØËcn¼Ø¡JèZžÝszc¦CÞç(vöÂesn|rL'‡‹™r“8*ßÉ„XB¾Æ¬œ/~ŒÙËûP;¡ÄÓ©%žô~‘£zæ½LrîKAùò‘tÔRHªô¢CDñ_‚•N;N-’B×ÖÓ©MGÿañWpÓ”éèr_™.DÒGcFQÛ¹Ù±KMÌ @… Â{ðÝ?1Z©"GïÿVæÂkèž÷//Ào~šÁ«ëÄxÊÛ·Ðç“È|è0‰ "ÉiÊ49K 4ÒWЗ˜ðõVþ9ÕŒ~xwYÃ,¨Ø¢F•å”ùÝÀA(‘í\?y¯££Ú'³®Ï{áçJŧw ‚æáòtBÓøIH¥‰%2Pþ­ØŽ¢âò°çØAÒRþü½ìɉ7vXU0ƒ®( Îma¾þûð|ÈròmåF¦bèóÃâßÅü¡¯-:{î7çg¾\ŽÅ(‚^…(cbO&^B…sÆS« ³d¾Y8z.§9öŸ¹ÌÄ;à7R‘ëö©c±èmôíÙŽ™!2üç Ë}¿dí4™´×ç}Å·)2¼Þ[EŒ³iѺ׬úèCŒ9å­tE×´Ì\Aw_RVƒ¹¼­QVà jÃiª,ùoUVžå Ìy÷ú¦hÂÛ»OáQ’#^j¶•޹då÷Ù4m`ilÎEÓÆÐé»miêÕl±eÏr;Eµ mxá=î kÄLj £Ãö|‚ÆÙâÔf| ù·H‡~@…îItÎäl4™Ÿ½ÒÆ0‡ƒ0e{Š!^xPsžÞêD>¶mƒ–D/ò1ÿ¬“µ ›.Gý-ßñ±ýx¾ÙÄ [æú¡€:x4ÙN[©‚…Õœ<݈»Ög°VjÓeˆyG‰ J3›ðtôóÚJ2ô0@}p_mZ ‹á^Ñ! _<—„¬¾Gl†Óz§¨}2‡¥ì>Dz3Eˆ…2ðUò‡„7æc«ælÁ|{9s*¶yŒÃèi ˜wb¢Pèã±™îðÎǘ*Ò䛃’xäÈLËsÅNÉç¸tŠ“àJf(ŒÓÞJÄ!˜¾*Æ÷=M!.³‡Bð¾8_&ÂΣß=>~ã;)Ý(\ÿÊ„® 0¦¯βÿ/àû{ÇQ Ò8§óÛ¹}1ºvG¯vÃìáÈõ‡ŽÜÅó2&T§€ÆÓZöVD›Žû-JÏMJ~\=Á>¿ÿJ¦˜)rz‡ÂÙ·ØFU¿=äû™ÅJ=%ÿå+èœ>O~?$'“âéѬuˆ]/]DæÞ`‡ÍWÁÚ÷³8Û©O=ž^E ?U®Öó€õHÛÒ%gž`¯õ>"¡úêÎŽ€ú¾-(.]žjª|Ïöƒ8^YšzyÉò°RG° {Òz;è¦ñZ´Ùß9£¥ù€à¥Ær»GX‹ÇIéñ:#ÇE‘ÝüGÔ¤n¢æve®9â!›oW€¦W{ΕÚÉÚ1røH‘W™Ã7…˜9CÉ+šú~Ýé(ÏtÔx~¤¸uæ¦a5Û‡ËÔ’„?¿sû1S°4s]2ϧ¬o†Ã!1Ôõ˜$ß:ÈÍ¥Ý?’‘&^<¦Ê_µÃMTÁ#p ŠFäbpßüa •ÁspŸúL~ªB•ÿx8¦˜ICδÁ'ræÙßL¤)½ú ¢=eh»úaܳZ¾Ÿ\@m­¯ÁUGä•C·–åþÁ01)’|ã8›êWnëp‘¯ËiñÄbvÆœmK6¬¹ ΫCŪM×z¨]2Ü*nÃÏ ôi|4Ýx›Ì1ª“6º‚yšop»º}’e yí;açUOþ»G{/GÐ[i¯ÉKeÏ%I¯aâ†ÙÔôënV°ÅÞr@}ÆÊZæ >ûc ð¨§Ì9¸Ü0þœ*À…ß\Á&å ü¾ƒLæÜ<Üþi>ò$yÉÁÌéä~’4íV'ë»FØïp¦î/Ö~bàâÉv ù5È£~°7‘÷™÷ ]^Î:ðÙÞÇla¯2ýÕœ­cÐ7é(–/þf‡‡Ã£µ¼Szœ?ˆ²NÜÚߌ¼ï‡éqZ˜x'vvV ö(¿A¥3#8K/aë²õùjÿƒ@ôR0m¬4ˆ®T§ó’û°õÁ8Òw'‰Åãì¯'@¥!Ž3Î!Gïwÿ÷Næ½/“Æ¡›„Ø4@Ý5x,RáT39®ykÆLår«`Iç^AO¨<*LUÔÅØ‘o@Ç'§Ãòs8ü $ªMÐÒé ÔŸ¬Å¿Zǘ£»*.©<‰lgÈôÕ¡’»;qyñ“YL®œzü³ 6Áææ©x8Фü§’œxº·tžÕ£¼c3óË“„Ü ~dÆo+n3ô󞘈wM;±2j þÒ÷Üÿ~>Í 7bçMÔ~Ø$œà©‚¹>Ò(-ò…-ŽÔL7ÚFn¨ÌÇ^ûqÏØBp‘z$|è¥[ZO²‰Ÿ xû¼JøñË,d%Ð{í=‰yU–wÉk×µP|ÜžÚMš å§ãQ2ºŠ\‹9£š•0§l.˜ü‚ëYøtÿ$º5»û/OÄñƒëØÐ,`Ö¼<.ÇS¬w7[«1çOªÇÍÑà’Œ"ÜP$Ãíñ—ôtÞÒõ#דˆÅñXu5ÊsÍ?øwšÖjáEU¸SCô†xžhÐëÈÛ£ÄeÛw–7*Ó´yؘVÒñ5IxŹ öë½"{>õ³gð`”(´Œò ëè5væ„6·ÔíŒ"õ_»F¿ì$:Uzî²yí@ÂL –¦PN (y¸ Ó>hB£â Ô?éóŠ“°êÑqX#=\`ž_‚U^a®ÀÒ¿æ|Û ü*S)\Cji?˜‚\¼øºÅœÆü¸téÌm~vp]ýbñÕSIÐr1^0ö ‰NÆ®9ˆÚ7E Õ´—< Åo«ýa·Èqš?Àä'WcÂ>°Q~èùW¸Ÿ<>¦KÔëuh¸â(ë…£–;Ò½f;@ïž~]«F;Gý—áS‘+cHZÝöñŽ7Ö“¥š6¾ôþÞ•èøB‚š\sFXÁË#ü¼q:.0¹â©*<°i†Jß%-î±@ïå!žÁÐæ¨D“DZàÝô*¼Œ–Îu÷x6}áÜLjA•Þ{zní€7ÖwÉ=»Jx¼Wœ*-զǎ£ÀRüðY¤Ü^Ç¥=ïyUCk“½P¾GŒã?{ž?* ÂÉšŽ,Ü™ªÃ{¹pÝy{¡?ê<»b¯Ç¥ŸÆCòþ ø/g˜M>Ë6jnÆXžØ<•FÁÈÝãqÍyq¾­VgÆ`ÄêBK“‹ìú†VˆoQåaÒžôØçã‚%ûq’‚ ¬{+Ïìb?? Á¦p5¾üôÜ™ÿ‹ ©³£2wNù‡¡*}Á]¸dÄj8G‹‡Ö¯„Ãx»8xÎUâ¤&škUƒœØh?êÎÆ…8Á—’z°l°„µßÜ!¿W.S ¾ú\)ôæšÂÊWaYŸ(ŠüÓwû±,3 ¤Ÿ(á½°P¯°ü•Biàf¢ÔNùM7º×nÆImB—჋ëq {y{,·Þa7î#_„ƒ å¢U;t7²%d±öòUê;)üÚÆ.&yÀË޸˱7å±!Y3È”Ý_ j© FÝëg7‹] MàÏs{¼ùG§(xÕX‰™ãKÐõY(47ƒì :é£ß6T•ï*ÉÂ꥘ÙN©j†÷¾¦oçø°O‘É㉒x•ÐèJQª)µ0O×l‡û6ö|çOà*°¢ù÷Ì©Tâ-L®¿€¹jqŒ‡ õZûVüñØÝûÿ@WŽ4–/m:ÚDTº“›»×f/ ö|àËŽ æ7U†&T­Ã™¡8¼¸F`}d/zº¯!¥¡Óš%`[Ö%TÂé®v àwËq×I®IFë$/ZŽË´P®ÐK²g‘¶Ñ¦üNwJ=–¥ÂFeP̨„Êiõø}” }ç5,c£ fKÛ)DcAê^<è{NŽb}r1põöp¶¢‰õ¹€¥ó›ÁãÁœi»ˆî1<‰ê~­¤hx\Ñàþ†‚*\ÔË™h,€únljÙhVØö w67™ %¼p-”âѳ/³Ù¸z»,ºe×Á—KßY–JÉñ€‰;Ôh¸¤θe@Í6_òÑäà„×—Ž;êÒ³§↲sur ê)ÿæ ½û{/çâ©}oYÓ÷XØÞy‚L=‰/¸$wmÁy‰/ =¸éž(¦kÍ­ _€¸ƒ˜iõ2±ƒ×0Æë;q‘9 §zÎᎠWI࿨êQáÏtNÀí8vînÆQú²·ùL:æ{Ò'‡RŸK2 ¿ò!–UP¡}87òØŠ?ƒjqíÇi½_ŸÌC?»Ët½=÷Õƒ>#Ôáû´ úg¨'Ÿ5k&Ì”Qé“öàúN§{/@54rÃ×Â-ä\ž&ØØ¶ú¹Ï¶(ð=RÍ‚ÔÍ@l»Œ<½úSÐàh:Vh±åkté#Í`´‡ Ͷ]…{W?âëú‡ªð»÷‚Éub°$?´.œ½íØÊhW–k×}ô6ît;¸T‚ߘ£‚·Z¿±Ë~Îø1À ­|Ä8ÛµȽWàr8LÃèáEÆò Ðz±W>` RÇ·`ù„©°vhh‰®fÛ^\…Ô€õäÖ>UVÜ)2þ¾h}Üšª½·ƒíÓÐ}j€Ø®ê`wæÁ´‚M¤l†>ÿ½ö [k²Þ¿Ê cîÔxœ7ì+ÌwzŽË†¹ zäKS;½tn&{îDÁîQ„4Þgh';*uš½q—]xv–ù?ORö~d™ž“›ò¾ðÇ}Ü’¼‹^ÙÇ`áû)¸ðÄ9\ÿ"Œêå|KxÈ|µ ¨Kí‘:Ëþ˧>/·Nã–Ò\`™lÇCøÓÕ§Ž¿ƒÞ±2uç¼3–\;‰·]õX˜˜w©ñÆWÞÄ*#‰,xÿ Îß †°üÏ:Œ‹]B'M †W×P›é¢Ô¼OÓæwäXV oKì­·*ÊJ·à Û@ —ÇÒµïP/r gl×§ÎŒ@õ•nÎ~í6ûà{ènL^(Oë>N"öwãAÒ;¶%꣫µ5`Jœ/›ۦÓʇ7 I^„ŽÑ Çð5Š|Ä87@ŒêÎÊ¢;mÔ©º¡#&yFÀ.ü‚ú®gð~©(}öîyXfÆ…Š¥˜¿û´5F‚×í]¬³³˜<9Ū3ÿOs·¦ÿ§àÇÒ“Ña|z>C8ƒ[v-ï¡¡û4Ù°½j·ŸÃ¿5Ðãù.²¢–Ç$|~&~L+f‘y1Ì+u.jþŽÁwò/ m½8ÝgÈwoëž Ïû]5È…Ä—°¢~ Ú$”˜v'N—c•‡ÂûøúÇšzÛM7»…ÿÁ´{§ ~ÿW,LýA>ù^acwÚô(˜x‡Ì¸†\„6Îoµy6@¢ä (ý˜Ž6FÙØ«·p¦¹?<½6†O‹¯ÃMÏMé, àgOÆA¿Ü(_¨ýÛñdö¶~‡-?°Ö\>×mÙ«Ek¤yìì\XßïÂÏ—Šòœc¯¡ÑA”ÎúL»ÞŽÍoäG<Üü}—'´Õòh~ïî:Uñ,š£„º¯ß0ñð4œ%~[å«àJÉT¾:Sà Gó×ä« ¬_.œ_*Í—׳¯‘8"í¬Šº/sAÞ’³ y2<Àt&Ë(fårÙÂyo2Ù…zŒ>ÖæR剂Y1Z¸ÏÕÔ±¿dzÐoíäÒñ=põ®Ñ—MÆ¿I Id¨ú±Up~©+—ß.kr¯ývh®#Å?50ø¢¶”þI4¢4A¦õ µŸ¿’ùP‰&‡ê`ÞA7ÜØý“õN€¹=zü%7«h/š–eÀ>.&¼"3Yà‘dñÿòª¿(€¯gCCx8¾Z'ʯïqæŸm>³üŸÂÿÆÔ³”Á² dùÕD8ꥄeß?`ÉÂt¼g](•qœž@FßO@é ædŸBúù¼‰µÏrÞÉhôåÈ×ÈSÅ¿9h”ëÁOÝ"¿ó,Ý<›¡wg9râ4>`È^ûFA÷’3Я—@kw@êú§¤õBü‘MçáÉ^|Ο(@GÚzü+ÎYy:—¤ƒÃWFf­D2£¯•eZ¿ŒükJÅöeÀBI#rfŒ<Ý6YÀ;ûû!ô† _6c-Õ2ïRSâg( Íí$ϰPyúô[”ˆÍÒA>[mc!Xó؆Î<¨‚)ãOµg©x¢ð¯ÐÍ¡CŒÉ»Oâ¯{ã#Ñd…âIPþ4 { ƒ7#Öó¼npÕ}ü`øS0„‹lÂXhB#î0ðŒœÌI‹î 7j*N{“Å6Ï*Â# cp•¡;º)ûÓš)ŠhZ«Cµ:ú!ñŽ2Ýôò7Ûv|ßä|ˆàyLome¦*Ç09UœzíX„÷íGó_’ç‹-tè‹®aXv#÷ap±,ÐbòBl>ÿï‰UtþÍX°Ø…ìØ•GžZk/ŽE©/Ô£k)…” ôƒŸ9^(ñ#½BƦˆÂýï&QÕÄPL«h‡‚XÜ;VZ>.byvât`íwˆÉÊKGM.~c#=›Ü€î«bù”™gºJ辡²ödì…{ßh¬m,(iIìÁ”Ú7:6S’3pAÇy®Ò£Mr×1Þÿªðgè(¨;¤Iƒ>Æð#­y¯¥6óÀÅñ/6?ǪÄeÐ<оÓhæOešæ˜ÏJ]¯’_ b@üûyxì*GwŽ8 *ƒZ¡¥4&[eÒ?·®AÖI>ÑPFÕ¿|‰§ODÓÔC±0¾%€gzÛñû>¨;ŽÞ›ŽÃý}hûã ÿ¦z5ÕjѲ&˜:2šæ¤ªêVä¶çJ:IUðü Ô/¥“¼@d£ÈýIµÌrúþV.š¹hÒ/é%¬æÓ~|R›FÎÔ¦» rqZ¬¦þÒs¡*ä%Iv{O–œSâ2íÒÔ¸S–ÎËP¡wç6½iÅ<9Ò“?·VZÄ.Áàç±S<‚>W]ŠÓ5³Q8<æÇ®€o-&¨½%K0¥²Ïl˜.ªË7,šÉßÈ~›ejϾ`ØŽv:ü¬ßöj÷ßkôB¸ÆÏþ¡w=¯^ Ûƒvƒ­²Í{R ±e~,:ˆ“?,Eßò4P-ŒÃ ÄðÓÌÈO“IT§‚ÄÏUô؇2òk¥×-ˆæ§¦Œçpª—ÝòšÕ»HÓ„˜=\·üž¾ÏG}hÞ|‚u¿Æâý5Î,8¡ ×ÏçË&,FÙN?œ«:‘H‡MÀ²÷·…ÛŸ´í]Ã1›Þ%‚‹¾˜ ½‘OòßJöåƒÌ€8½[lÂ[¦±>¢vEƒoˆ]}¬wµ%%ðüÑaV1ù8Œ²*×c(‹9.¸bRÂ.%á·,!˜HæxŽÁÞ*Ø÷*÷éL€3#†Ð=cGòwù£xÔêIè“ëL=¦9“WW,©ÞE5¾ôª"ͯ‚(;.å8¡²3º‰ÛñZ¤sˈ0î=;V%É2Í‚0[+ú_޵÷½Òu3 Çÿ,c/<¦ŠA½2±Ð^9^Æ–ÇZóØß¿¡xŸ·2¬Æö+;àuf3®üaI+ÖË£Bl{QFzd¿èQQDwœþ¼{hvµcΤ)ÔùÁm|`ÿ‰m:° ÎÇéš3±È,”蹃å Joâ°j?}b=3œÆ?w~æYÈÿ0·.E'ÆkÀEçýxÿ»ñ*þ‹ý‡gÐùGÀ*«CžêÖ£À6å1[uæ>+€ÎáÑX;¦›Dο‚"§èˆRÔø…8ˆ_çÁ@û]hòùbÝt#sz¤ƒâì§žTDΟ»Ú…±Èª]ø$½,ZÄHѵõxo’!mP6¦fÙ txˆÿ´—E ½Ï¾7Á€ÃŽ;øÛûò<ð¶%û¬7\8‡§ŸnÃۧ㻌§uO×ÍA5Ó3—ó 0L“ã+1%>ç•;7)RÂ?Vút÷‰*á‚9s0sÓxú"k€lÝ glA´Þl·äNþ|غ©À¶:Q‹¹ëyä›g,¦ø'j}Š£9]|³öiLsßsW`nök4Ú²Ò¾Äð2 ¾G°Š%ûþÀ Ÿ®2§ÃƼø¼7ìµ£ ¨óæÐ"dŽ×pÙ+¸÷O“gê&ÕÊ…±>ù“Œ¸­ñGßmJî( Å©rаæg2îmŠçºîYz”¦þ+kX¯ë FGåÕ^†ó.¾Þ(,c·áäáÁ2{¨<¡Ç§}^€_᜵5È)ž ÁŠY8Åi8]e{ì1 çîݧðÌßð©2U7eçŸàÂåmB©°—äKIüš)úJé<ÚrƉä Çù¥[ã¸ïˆƒðóÖRô ßI‡#uëHT7C«ý°¿g zýhw-_’mcgúƒà–ÈKÛ|×YÍ€cCñõ©jœîø.†ËÒ¦£ ’¤á÷Fžõãºm ÁÉÆðVX Ýêõ¨ùpüལ„vÒ"´cÂØb;’«Ï”%ÝÑ·Iü¡#Ðs¸Î™Ëlï~¯Ì½/ MÕî uIÀßIPݹZ¼[rþ}ûšŒ•ÛC”Ò1Cõ n|9‘:hr'·˜§SŽÎ'Fó ó;€ŒY†;†ò\-„õÜâXXz‰UÖuA}}=bÈ¿žZÍ1tÍÚ0›ì°'ùᥠzú4g&bL¡oZfr0îU¨‡£›Y4ý/–Ïý¡!5ÜC7üýJ毂ù©FÔôš#4¹1èÓWÅ ‘!Ô&Kv™Òöt9ª÷¨O¸cu,n«†1¢|·iñÔ€ê~ðú—Löί‘éÿ”ÆS»ÔØ÷‡‡Ø—ŠQh´¦]fHÕ×àáÊJï¬À×퉠çÝ ÓÊ Qáõö_{Ëþ…¬ÍÑ‹IŒÛ 'ñnÓÂà4E6æÃ|ÚI¦V† 9êüá¬ëPgÙ&„,I-F+çxóÃ,<çoìÄ;…m°,O ª: b½,¶úC~̃cte¶%[ê‚S?êÒú #ùõÆ´Æ^E›@tãQò¦fÐï†Ób¨Äì±_ÃÔWJÐW×ß¡È]ü¦rHr¢»äÓÙæg©ÂCÇZ ,/†5¬Ac¥":¶bl½pXU~½}vÞŪc$ix|ñ‹ÁDeGš™;›*®×æ³×«óŒs£`j¨§Ðîj&=¹!’Z”ZÓŠ'澟9MZÄte°¶|wÝ”gí®`üýÔ'£ò‚Â`ëþ˜^‹~§s£ô`wR…xJõ‘Â[ޏ|£¥œ¯§Åb£Qù\ߨ·ºîI«½¿ñ·6Χy¾$ŸÞ†QÉ`‘y—ŽÞI]‡Jóš…yWdPq¶:6ß¼ ¿uSIqåºÅê ~p¦Æo†ñPË¡ˆ¦£y‡J¨çTÃ/÷ÔÝ~fF´\FbM·Mé[G«Ù:: 7n~Jz·ä£Bu2ȹGâ‡ÒÞψO@Œ}ÌŠ¥áš4-fRŽ›Ç&Ó¼êu m 3x&‰‰ù$X°§ï>UÂzÏ=P4Êôo„·©W±¡i48j·LuÐ;X¨ªMùG~céœ2–úlÚ šC!fÊL¢¢DWÿz÷Brº"]¸^œ¥_ŽÈþ~‚Kj‡ó³Ö0bìÐÛ4ŽùŽÅC¦âÁ´6‘/„˜y#ø-j«>øí?<8ç`f—>æŠ'Áéhl¼+E½¥O±ÂP€7_is•wÇúLp°)b¥RYðGUŸ7¨Æ ZN@qz"ž¿¼7/áq鵂ñÚÑðÞI™_«¬"¹“²qý¤ÍLÿÙV¶ík–?ä—îà¢üNˆ£_mGSˆ"°×«õÒ¬µ°ÿÒ —±¡+¿YñÑö2ÂI÷é¯Ìø1ç1ôK$Q‡Cé…¿pâòzœžþW,øÃ6UÄгúTdqD´Í&;g|"êÝoá¶óq¾òÃ&ñ4¢Ocú}Ìé;v'†¡Š÷o¶ãünú\l>3“|$€”ålŸõ^òL$/ù!¡]pÉG•wkÒ¾ÙÉké&”>¸ µ“uéëøpÈúÇ"íKñÞUN®ùóKuª|cÉ^™Ñqf¸g8ô‹ÎÅÈ ýäÖ’fáÁNá…TOQ·\8}NÍíÅΪÄpm; r“ÃåÍR\Ãw™pÒ¨GpL © .Âä—VT&> íGè@žÞy»œÞ•rç2kÊH‹üdºÕ`8l‘Âo~_BWN4æÙ-™äà]5êS8škI¥­FÉ7?X¥ÔOïßÞÆ;†ÁóÝÐ(òGX²Ï…>þTÆÂÃcArG >?¿˜×ùÙpqYôeõlœ™PBßãØûÉܵb|ÛºœÞî]E™Æl•¯£†’ è·ô‰¸»m•ðûC—ÈáÌù aôù0¾=yihÆý: b‰¥p*Æœû‡­ w•m¸DF -^½’Wl?Š™V‚±7rǶ5Aÿž9h8Z¢ÝñõÞ^˜ÿ[„Žéü‡mò3O±k‚ØSem~E|*©©¬ƒÀÏëè²{ùpUM•î·‹¦¦j’Ô“QôyG¯gsx‰Í\5ñ‚3¸hÐØØe ¡•ñ .å{TEiøªJz²Q+\æQ“ÜNLÚpô! aJáp¬W‚›òÂqÊÏjL£±óæÓ§žâš­åƒÒâ\²ã%¡S>¥ci÷~óÓ >Ég4ûá~³Ã=hâ¦o¨TôkÛ⨰ä9wZ‰>Úê uÚp¹Øq~èB$×´Ó©³_©Äƒ–/¡û§O¥û†¾À;OYêCulÝ‘MîªiÀ-ÇvØy•®ÌÀ} Æ TM¢’)¤xeƒå›×®¦~™v<îI,^ѹFŠ‚ ©òصÂgKáÈK1Â]•wÁÒ¥›Òsñ]ïG´cž¢†¼%² ¦J#Ø«î $4:ÌüŸzðC¾”·DÂÛµÑ``³dpl1ª'ªÌ%¬†ÂåA{½Uä8˲æÕá¯j8$xE¤3DéÏ/òü]dm½…G6¶ÁÆ«Ñp|¤ ß*;„›þù̦?U¤ö ¾€Û½£Xi²ëÆ‚üÚóä,õƒÜª‘¬Lî aføšO^]Jù6až¾«Ô'ð¬íöôšªæ ~òb+4tx|q"Ik«¿?Ê\‹—°ÉaûP¤9Ã7×Â0@ô *CÙ 8ÎV€£Ü„2ó}ÐÙ÷5î*΢F;Ôéó[nq4\UR]ÒoðgQ<Ñ›}‚FJ€ïŠ \Fhw.˜ýwì=¾\#êa½ó ˜8Wž“ü yø>«ÌJÀìÓpïTuni“…g RpeÿN˜ípˆ¬4IÄ“Ûö‚ªa…§Á #n$+Æ»‡ÇP¿™x$÷©ÐŒa­û ì> pÞP./¦Cñ™>j7Ô’8=3pk7¤çd€ÏíW¬]]³¶¾¤±è{c6z«æclB"÷ïð§û&&r=Y`yPkãUl9±J»Šèê¶Óð÷¯ ´¸ÜëÝlº„Êí³ ;ã­ì(ªZ²m»çMM;J°å”µ{$Ž6Ñ“ˆHa¿àAñl»}º}°'7=&±»Yâ8Vt*ûPºMïaîú|ƒo•ÖHBf3³£óWiC´ÕwT¶?-° w…)ëáÛ>9j{?>+ÐâdÆi¸Y¹m] ý~SVÚj ûxOÛZfª¥E›nÊÁÊŽK8rjü;ÔG›-[ñ}ÜqV{ÔOãkïGø¯_„[›xQk9 ožÄ–¬ËÇçЈ6ªs¯†ÛÍ€w Ncƒ´Òåµxˆ\—„r—M¨ý)ðcïo)Å;ÌgcÃzzÇjz©uÑ;cºÅ?„?z›Lzmb‡J‚ÑþÁ xtŒo{[Çß{+ŽùàV„ ·´K ÆŠâÞ¶[°Ï(†z´{ñÒKM¤t¢;7ðz6Jr³šD>!b4vÈy`ÒÁnJ*Ï£LÎöøÝ7ú]ðz“ÿ§æ€¼3d„¬ v:Œå’» –?ðl?) J¦ÜRrì[‘ΧEwC0³'}±ÂŽæ|Åç@j{"lbœx$g«>òIYK&íl!Uè·`kGOƒ©hº[”êì» ªIÌø›åñ˜%ulø€.Å-8µÍš[øZPëÉ2ô|êfܳʉVŒíbQWX¤x¨.ÂSJ}¨ÓsŽhͼˆ?~þ7^å[Œ¿ž ÁrtÅÖ[äëˆzx¶ê5Y¶oÌZ‡ÖþŠÀWü—Û¼o“wþEg‚¯ü±õFÜÖåïÚŸÿÓòTßgQí&†½Îô1W[ÐżÛòT/£,ÊAeó6®)8ç~såöOg’Í#}É™8:UQæ/‰Fßs/I,˜3ÿ*Q_gÜ©¶tÐ3_ÆVÎÂ!ÚÔÖE†  'æ×à«Wªä³^.[oLßÄۆÜ×l4uð·@Òéü_ h¢dx Û;ñ¾Qùpdš÷sÊå6’<õSƒ@}EIJUäK¿z–|/FóÙ[霉Çê"³o ?8(Ðz“n¼cê+$ø™–"–ë:›†XÄír<øƒ•ËOƒã 3@â½'°•f*Gâ!æK,ZÙFŠ”§ÁçÇ#ÑbC~—ɇØåR¨+¨wïŒ ‚Gíc¹‘•ƒçä£í ^rN;öÃ)’øçý§júü°H±_8EÖª>õãß)ÃÝTAëÈ&¢š<™¸¸­Äý&…¬ ÖŠ>–£õ‰'aáhePYÒ!œ¢êÄ,(úyMeaÔöólòu%þO²B0õ” ä4“DÿüǤX.‡8ÈèÒü:,+—FÝmX-á#‰^ž"_#Ù&XЃbªk Áú2ûõjÝB>ͦ>¹ ¯Î^›g Ø™/+Ù†÷ÑÁW ÚŸIbÞÚél8ÏeWÁË¡¾¨¿f=)N9H”Æ¡¡CÊ C¤Ï\é¬:»L:ê¹y ðh•=†}ú\Øÿ—-ã¿Qªä(¯¹ß/Pj´£#©aÜõ´Cøe²ê>&“Â\!i\>ôὋôáD;n™îOÿ­ÄÏõÿ˜¹ÆF¾Í–Wâ÷ûsA¤îŒoKÃKVâ¯l/˜žp>(eëRäÀky&ØÕK1í–ôl©e‹G\P#s^ëp‹|BNZ_)kêðKdŸ~5‚®-H–àÝÁ ó]è+†ÿÕ”Ö ûD¦7ç# eø± W!lÈìk[úÿê+›O³Í”[8Õä(Õ÷¢ê›´è‚âtÁ™ñS°ñ›.°e ýšåÁ«•…¤àVMmäCÕN1­Ù¦¨öv:Ä ë‰Šì2ž7S’F‹*SÃ¥5$ó~ÚZñî׿ÄeÓ0\ê3·pcŸ#‚®‰å0§:ÿ¤wZVð”â{> bVhì’:Î¥}°ãÖXN±¦Ô‚ê5ßpƒÿ~zqÕH–sn7ZÏØwL[psÇ4Úqy9µ…ûŸï"ç×(òôåILyÏU¾fâ"áøÅ{!N3ŠÂµpWà–;ˆ‰¥ŠµÏæsçG`Nù°ÿò¡ÁoÓ6p—Óæ&Mª<é\#[z¾RÐ(¦Î•=éð¥žüÒ¢\úD¹àBí|xgµ 7WŸÜ>¡Àlº P+BÍ´¿A¸L^{=œ7tÃH'/šrˆ£¼y1ι'ovæl¦Ãß/‚+.eè½Òœ/Îu$u?T¸àL6ñ¥J—¾wàÕÿ«+-¼¯ùÞ~›Ì×É–‘ÈÊǰ@˾Üðeº&àd}CžiW…=RÒ˜÷VNÜéÉWZÏ£·§çá€è5Ü8ë"zè;Уºâéƒî]Ô?…¼U§i3¦ðE¾ÒtEInr:Â|ëâNä­F@»ÆY–wMê†áÔÀß…د$Å/?@K1B~üBMUÈò,3Ú=J’:”éÑÂ9úóáîÞ<òw)´48xVåñx^o'οýc|Kü^PóŒÙ{~b-þÖô´äLž\X-»¤è0“L’“͵#ày@6ë‘6£Ú!îôƒžù’ü&í|aS21Fi(æ;ÃçÑh_?<,vFØ^ZJ®ÌÄÑ­°W},®öIàç³õ"äÅn|]À.œt¢]Ÿ ÎÌUÀÜ¡É|ǘì2-$ž+‘´Ö_„ K¹¸õ;²uÚa(Ö¦ßëɹç8ªx <ü©€é!?Ù‰ÅÏá—E?T¶êCžâD¼´è.;ÆÆÍ ÁüáÒ1üÛD¶qR vþ2·Úc݉;›ŠPäÕ\>¡Í|P‡«¡Â:M¬†¼õ@¥§$¼È®˜F³‘'2Øòyʰˆ=`6ß%ùaïdìøÕžû}t½©˜y’ aBXôêÁ+B„^÷=èw6bŠÝüá|ûe Úbƶœ[|fe Nu;®+­ÇŒz÷ÁÄ…Ëàò³ƒ°àd†L:ÚDñä…vôûÃç ÇH6 «‹uéË.öàU¸oøó¾ ¶E¹¥GAýÒs\¸ß“ö­÷`¿àÌYžøï·&?ãõ6ßÓâ)£¬hè E°¼‹ùNlBˆ ³ç¦ûàîy:±§Žmœ™nçDàb¼+®VB w¾õüfì¯P㳸ön¶+D‘I©lÉí¶ÊFƒÞ¯[;¨_U 8þLuÙB5 åø“õhì9Í3¿²y[šÐ×õÄãþ-Eëã‰uíc= :Î „ŠãYßÐ öùÔfh™B ÇŸôŒ‹GBì(tßÃÉKñí+?¢u±Ûã²Ì!<>%lî‹ð¡ïÈêÁý=<· ¶ÔÈÑÚj¸è†n[ªK‡]îÄaCZÑ!ä"¹±Rž¶ŒàCå½Ù¥?Ó¨ÕTîò«“:·‘dfÌ×FgcYÌO¿¹mK^ãå¤,\k'F³ä\Є_ »î ê‘nlýœœ_ŽfŠaÂòNôl3⃘©)¥Kß¶àbóü ªñ2vÿxÉî<1‚Sø©>–ß2¨ß…µLF¡W¨üK™â°ÆĦ"þºTÃRÿxòyÎ2¸o| WÝ—b?—4"Ž%×m LÀžv?" ~Ú ºõ²ˆ,¸þ¿:γΦFààqú›Q¯N’\C³ugÃ<Ÿtû¦D>—é·‡ÎBK±š"•±¿ÍºGÒãb°é¢%š jsçõÿ öÏn¦W_Bïz’ï ‚é `4ÇkÝî’¼&l§µ1UÊÂ’u›AzI-1Ò1á]6èõ4œ^_Å%³Ž"‹³Áôé›èØ•ÛÙ•‚½Äüt4Û%°ÀÒ› +¼ƒ ‹Sð¡ÙF¸*ɽ¿J`¦A4tKÏCGÝ^¢w:û: Qž[ ÇŠIññîtÔ0–‰ƒG¾o„+@£üû¼2bðSÖëñî­ 81DŒz™‡dÒ“UiøLª5ç­À쎟(œN{,ޏ] 1Í|ËälŽÅ7Öw1áÎ#|±T¾Çoóq^¦7îß9™V¿'ˆ؉žv ^Ë#ÏÇ~„M:ŽDºÂžÏ?9 òÊ6ÓO領3´húÓH¼=î¾U•'»ùlAC –]™Š_¤ÒkY[Ègy jYû—·$¬j”w¸Ž;$²(“%¸"h/Äï/à¦úž¸1Û—œ*õ¥{ü¼ææ[wÖ@ë³|¼ôVŒjG¶Â†wê¼¹&v½­a•§”xåá3áÌu¯¡§u¿Y\€%Ç“òì,¬ m…þV4¯)ÆÓÀ]’º¤~#½¹¦¤ïR?LöîfÓJð;Ïz¾Ûòóœxð‚¢Ò]H_V{‘ßbáYC¢Gçæa[¯Â¨7ü® ÔÌho?Zëôóyüî1ùe(GÇW¡»úCTŠ?Ǽ¹ãÉO‚Ï…1åQ0׌Òç«”îöªÇ!Ym¨¢€?σ›VcˆuænlNÜ[g5~]¬ý@P9pè]þ®Í‚7çÕÝD_%Ϧ·b[ÛFÒ¸I/§T"¹l\÷.⤕íZ)NmÏ8àP›Pø½¯½.P9þÅÝkþîã¸u"n®™.¸uf²ð¬æbLq ãüÃàô¡5¸Ð$&=uÆ¢¹'ÈʲÙPpCŽœp—üWóyÖ7Ø\&ÄãªÑúþ'AÞ²)X¿FÉô ï o ¿ŒUByw,öNÀàßՃʞ‘’÷@e’ýÙðê«;øKŠ0,HœÞIÓà™1ùGHÿÖƒŒ•ñ›µ4ò¤ù½« &Ͷ‘@òrŒÞÛ"RS¦`©@‰ôl>ýá¬Ýj‡+ôÝ 7IŠÖD-Ž \VOñ¤H|´—æwB‚ÿ<¤¾|ʱù“4¹ŸV;P:—[àæ"–ʰl7 \4ÏWÎ5‚¡á•ÐÐêÿºáÓeLr¤3Êá 7`¡H5ìmÅgŽ;* ßɺӾø`y,_ ©/a±c4Ä .ã;UE~âî<ˆ™UÃ2ÇAå<̌†/¡ÐwWD”ìEµÒ|Ï×RpÜer×nFg)UÓ¢Ö[TÀkò3rv;À£sA«ò<ž–ˆ&“@0l+hrEáó(~f½S‚yqÚÀqâïJÌ*E›œ F_º£JÒ¡ëJ”z¼%«7´Mê·¡Áìëü>Z>q"Ò®‡1©ò ¼L­`+êSÙºjd~³5ê88áä„Q Ö;ØÿIª¼áß7oC›¿<÷äxrãÈSß ’8îÂ{2[Lš{”ϯ›àª «ÏœÂêéî_¾»ÉÞ(à‡¨d¼ÐäÈ‹ ÀĦZ¦x¹æN§7Nå³åæQøç“°RžÁCÏzðsšÃÛƒÅø†Ýt|bæ YIz¶žÄ55xhÉ]Ah‘-Ûv»"Xe‰½³*ŽÅQ¸Üÿæ^6†ÇÖ»°Fðo±‘@ùE_ Ey,ðý^4 £Ó&p­Biº¹º©›Õ‰ö†Ôç•,¨q!>_'A/=: ŽZSÙõºkl›MrS„‡t=gSká² }u¬¿F”ý—/­ñq sù"Ƀwì'WW(QíëôÌ80Ù†>Ÿ-Ýé· 9Lq+m «êÁÔàWL}@o‰ÅYŠpxR–‘æähÚúñ hR×ÍÚGkCÜf¥N—¯,€”½«A‹Ý#Û"`1 ÖG3¨RV&‰Ü‘ÆÖ6MgßšÒhyò¹° ܬŽBHµ-L{›‹ù/zÈ/ÍáTÖ(øêט6ÓŠ,¢E¾½w§Îw’¡ÏUÁux[º;DÔyü¿Z¨¨1ƒÜƒwHéÄâºÞN![#:fÎ[ ×Þ©a÷Š…0[i+¤í‚…á‡`ÌŒíìÒ´ õ‰%•xäöCÇ]b )Ë@D³‹6” #ް»¿=¼6 $õb½á{¨Im øÝ›ÀOö$âu wøµâ«‡ò·t¸5[fÖô@ØŽç¸ó+`w=U±?,öII‹)…é"[pÕîñìÊÄÏäɈ9\Gª˜ìKo#+K?·e‰ñfne_ BÙOZЌτcðAš{ ô,^ÎÑ&€ÙËäÁ‰¸;LÔÕ’hzŒ£wöá>V!øø\/} ·’ ¡«a —u\‹;O|ÇøZõUŒ~ù¾$9â¨1UpWÏ›?ˆlg¾r×V”‚˜óRnØ| ç]ÂíeôùqmYžÕ¦Â½\·¡Óñ 8és.Ò„Z•ó씃&̼ø‰˜tzÀÅJV.8{©c߉òÑçVrÕŸ¯Øo“naÄv!t5®æ*2j T4§ÐcLÃfðU)?È¢={ñï .]Y¸Èð¸ðÄ üè@ököÞÛ7 O2súü´ L®_æÇ‘1qx”ÿ_~2xþa>AU0ºè,ÆL ¿v‚±Ö{·ü|Ò”ç·+”xÆÃ©ÿï»PrMn醗ÏÀ-§8P‘ ¬ºðÛ•«t¹ß2èíV¢–Tp ~´ªñO'Btá$(*2Å6ý`îèlÅW>íÂNÉÇ$Ók†à¥ÍWL8 ]Q•ðôµ8}¾Vke°U:^§µwRÝÀ°7ìð‹Ó˜›Q·hNŠÐsÛs”¾eÄ{'㦠1Œ*¤‘`ÉpPñÊÇö®¹°gëp/¯ ’ƒ+–õ1«)°Ò¾Äî±KÛ@Ù7 ϧeÃyðbÎ|Iª=×üeÈOëáú+²°3М&nS£…—@Üù2Yv3ÄŽ´³¹E‚—y’0³N—§\x'ˆ^v«n™Ê$X_‘C$ŠíyBg€01u8tû@Úfz¡„¢e¾>ÞMÆÕDƒE~NõnÇqM×qãM,p^ÌñF‡³¿~wôaÞÉtºYô²‡ZC$Æ•€%oÎ:yjP:¾§Â“ïX•r‚y—O¦šÔ½ú6L2}ŽŽnäáÓ¿¤¸*Ì#¶DWøçÏZNN¦÷ë—á3S_¦ÖÃR¥ýs®‰(ÙIÓÆÌÛìÙÉRýùxdûââPzàåPúÑ1ŽÞ©‚‰•ã ÿ`1±÷ß‹[Ã!oÍ`\9ž~˜XÊSñéü» 1‚®2;€ {Û98‹þ×ÇiÆN¤O‡Ð½TßK|Á„‹Bâ,£‹Ïd| ]ÄŸW¾‡}®Å°ÈðjI·B¡Q!9rà*¢ˆ¡‡Óµ'r°|•4»cO¿=Å'×ݹrÍÉK7raWœ߿ю®ïÂþÜNÇ ó}ô™„¤½IINà¹÷ÔóH3»=d$Õrˆ¨.ÛéÔ(Ò¨À à@þú¡#]äåc@–ö%àÇí¤yH6Î3¯î¡ÓŠƒè–¼a|§¤ý­‰ËÁœÆ^ò=;œ¦?Ø ñ6Àg.â)×që¯dlÑ1<ús%›Þ‹áÜá¡, imD}·}Tü×L•„ñåãh¸×KÜzµßϾ ƒsäo¾— hö¾Eé‘ sŽ-§›G॑»™ß}Ytêh‚é^¶Þu³WÂ!&¹dS•4Ÿòî³ç-¾îÃA0–¡£O;ÐõÝ—qç÷áhvâî® aæ¹ÍƒzÑþ6Ü@խèj2,ñ‹Äûðçnf%bíTAÂy nªõ˜Z­ ƨW¥°Oí0Í’ƒíy"ìþÓ¯¸,~<‚È–z¨ïˆ—w §ö°'§ôxy²:uúíÂ.¾{†5mÁK‹¹S Š/fçìgÏ¿àØ%»ÉÖsáà7à­jŒ©¿ÝmXÚh»”;ÙéÝ )µ2îŽuqV söŸÅ?DhÿÀ+È2ôß|+ºsc´ö¤3©©Yl®Zàú¦ý˜ÓéÌŸž™À Ç¯þ°Ÿ¿ðpXõt….¿5¨íþ øÑùäêIÉ—uö1»À~Ò‡î\¶×…·fçÐî.Ä )…0QV—ÿ°Žäßí¢ÃKâÀ¹Ó–Í[û'nÀ¼'nµn/x ÌùØÞ9‚K®®ôäÑåTyånnUŽ¹ê›Ø"¥\º|n2IîŸÊ+Ãs™ýÂsç#tGò«àׯ¹´GcÕLÔ$vÆCË”ùË!ÅÐ0ÎnP›¨Bï²£PrHž?ª´Ô»ÒPóýJ?Nf»ÿ®„€×󹯷S0ª?“z8Ö D×,>û–—L9‹{æÝ·ž£êáxýË%*æïHn>– Wü;¤G°p{7;óê:¬‘™NÇWœæ‡ÏΆ`¯ë ª+ɶ=i„iZS`Üê¡üU’#ëŸfV[ÇÀÂÜ^ý`lÞ5œ¦ŒWÄGÏAžÏÞ¾ø¯‰½ÛºàîE *§lÀ‡ üç’ô©¤ti{U1Óƒ?6ÖÜSïŸ4àƒ¡Yztßïuuc ½Á$¼fÞ~W¦¤óHc{~þ,ag›ßâ4g¡Èÿÿ‰*2Ãf›Br¢óÙCS¨Ð÷¢p­b4{fͳ\ã X>‘àF·,!Õ­>ySwÀìïzž`T‚Á’èïmƒÓnêÃôñ3¨Š¥<¦L…¹-iö .Øî ßÓ1{¬µ§Fk:.vÿãGÑâ„@Ìü0‚÷ߪÆò3icò#ˆ;¸†¯ ã÷L ÃÊhuQTÅ£ú9T¢“ÐùÅMvÆ>šåZ…2·ƒ±Æ‡ðwCé ýLp¾†T‰rW_Þ~M•*Ñ#|áÀƒ“Âi~Á *}S7ºàÿÙ¯±Û´ž(ÁûroÈ dóæ÷À”ßv r½µÒqÖÌ2|èHcú^€ ó_›FwŒgŸfÉbúfUü.U &6ÐÓÑûÁcÿyÜiÉx— UÞÙ`P§>œ0[E_~zJŽo8m©‚ÑÃéþ·…DM±T ?ýªƒðtÓ€àÄÔ[èøû-ó¬Lå.)~17ø¿ºÑL¼8nÙ¬N‰únVy¢ ô{$xËðé} ¬p/×;ŠT¥ÊoIüd)‡6@J™3žôß%ˆ 70jÇsX´Q–wIÆxo-iIHÀû6O›Î{sm!ÚºG¦EÁ¯P]~|_¤0ܯ•ɧ üŸý³3ÑnÓºÒ›gbÙ))®ü¨ƒ›LÇZ.§'ûï=‡m{c±hIžìhsã9¸~ù 'Þî I½-h²" ¶T¼$â~Ñdô¼1T¨Xµ«[¡NØ«d±²Ù‚›ÜçK“K¡[ ñJÿro!¯Ëq¥ºÕhÌ9c\?hÏŒ£Ó`TØ(¡¥z)˜tO ëîESÞÏá‰ó?£ge$ ±§.Æ‚m¥ºy åó†Ö`Ñ©“¯}ªÉPýÉÖ_õ„½~ôÂroñ²ºgÁ¢7Íqà¶žÀqììõ×dßOàãO‰@®˜ðÕ'×á^ƒm(s¯|–Ý.Ì‹3¦.Vv’·ÿ¿úÎ$)éšNå_Ñ´ûæfÛðµûÿàïšH~áĸ·=“ì,õä ¾ßBÉ óxTI Z®¼éÙ:n,¹Ñqý?ÚñŠš5 DmûºÐöMþqžÍ'~¥r'ê!G{Ó¿í,•ÜŒJü;oÆ/e쇷¦ù iFV<³Äýç#þç)§ï »£½îaq±Ú 7†Ðз1˜ÓZ„7+‡À¨ûùd|f=QéFæ›Ï¦>_Á›u¦t„”0Ýo6|x]‰f£uø²ò3°ôv=ýD†>zf †Ñq£©Ü©|¦ØúH¬z_‹3å,Jérê9NsVð´]n “}Ëï}‚ç¥$õ›,?ë‘‹>ê:äa¼ƒÑñpx7eëû8šû5ïD¿NŒ !òðfËu>:‘Ò˜•+éÿÙÿêEƒÀÈ¥›ÜvšÌ'í™B4g&ÀÇEwth´L]]êµtþ6åí¡'6ÇËb/ðÅè?øR_&=•Ga@?„¹"%ç±£ÇñÏYcê–T·¾dÛmÛK†)ïÅ! šØËÃèØ‚x¸²Bƒ*Nšö»Mù(×Éäfh!<Û§Oµ¼=褓³Ð-A-lóñƒÀV]‚œ\ykµnÕJê.Mz55WÌgÓ?¨ÑÕ »yðÏ ¬}Ç>,úÍØà<ñåTîõü2ªùKP“©ò|š|.ŽàÔ2°±×ËŠ'¡lZÿ?ûƒ6å@áó¯8§» '.  Ž7’a_ÑHPû 2çDhû±&°‹—!éxøúTþôi¥ ÅÓ£+jÙØî¾ßzO/ƒ[k Þn,3‰V/”æ Åù`|% 3w£UwŸ1‰µü€ž†m-…)¨IƒEJàØð³hû9Œ×V7à¦T˜ÿ¾/$kMÒó!Ëüwñ}ï·ãŸÈkAß±î ¼÷šDôiÌë¾cÅG|¢‹“íÝ€÷ÖÐ?ç7…ѼÃô ¼_™Oó Ðá×0þëÈG΃5]²t…ý'¬i…ÿ³ßûìˆÍô˘û8æ©­ Á]|ûÕ©íbo¼_±Æ:³ÄïÁ°âs3?¢BNž€•Ñ#ào¹4‹U:H9g0×Mk9ÀSgî¢\ûC|z! 5€tüC0znÊ[&Œ€ÉŸ`òQelœã„¹ûÀ Ý/W .Ý' ÍA­óN!ËÍ…yK¥èÕ"sá “(áÀc{za‰ µ¨èÅ—'êóqñ±~Ú ú‚l0'Ž_}RÉý >L¢ÖCco£Òˆ[¸xÔø´kÎ3IÅà!À¦uÉUËÀÂ÷PažÝÿðïJl8N€”àE§?TÈÞQW·ÁzO_xg)E7¿€YÔEŠMùðÀBÝlQ*t" q/ÇñÐëR fMÉÁßa¸tŠ€wеwö2­Cšÿæ–ÜÞ˿ڞ'þ²i÷p¿è-Éã%®BBÅn<¼û2ÌJÔ`‹Ü:™‚®õ°7ç§¥îÃsS1k¦E]5¶²sro༨Zˆñè@uš2û%ø]E«Oò{dËõ¨gÆ{¦ñÁ'¨¹šMaÏeR½0#Ç §ÿd:1Þ±.Ý­Ô{['#&mlÙˆÛØ¹gßõ{(}ù¹ d7†æài¨gzZP®ÿ•íþ–KûMAda¨è†ÖqXš\‚/=ø§~Iž:*ƒÊ¶HRÍyÞ8òÂlx&ˆ¦ígçAÉÏÝxJÉ âÌTéðIa%E×[ZCØ®ܳMŸþ5ÝÉ?Gâo¼²ÿ‡ú(Hó™%iõZ˜ï…IF7ˆí–fªt¸hÆMâåQ®|ÓÆ0ª[­CE;SùO³ÿóÿ˜â4ø|±,nFÂÁþ9lþ¤dÈ^¼‘Þ[Ùç®Å8I5>Lœ=º+ ®°ÉSù„À×lÍ´NdõÄoµ\ÜýU¿ãBëj¸äi6xÞŠßÎP@Ÿ‰ÜX¸ñߨ[eÎ%â'¢évëÁX³Ç¼mMèÎM.\eu9Ù<„ÿa·s}á§á}Áá,¿ÌÒ QOÑ0¡:³¢AÿèÁ¼ª•˜µ¢lêLpú«o0º¬œÚÊøÃ¶9ßX…ù ¶¤`×ÑñP? dc%©ïÏ2\µè'<žÔÌĬã^û÷ÿ/þ·h]ƒ+­ÈÏ=î·ó‚4¼i‡Cplrе)ÂêªxˆÜ»™Þ>kM‡Ý•a’SÇÐ ÿ½KdÑ~´AŽÍþÜaþ¶ðþ!´0Îm'HƒÎ”röeál Ü©­- N$\ƒtáFjizÉœ&W÷Ê“°eDE©…ïR¡Ú¶Û1Ù©ô%ç1Ô¶ýÃÚÇ|`>W¯ ³·^ÅK×üï2l1t„;UtÜý[- ÑAžç•ƒ#!^z- S㪶Š|ÿýN(v»Ì®J•¡»#qǦLØxW›ÞTúô¿ø?j›êd 0S&2fŠÐþŶìЧl}wTK¶AÇA\rs7¹à«—ž/¢bµâ˜~Ÿîyò¿ÆWJ³x¶ƒý-rj'Vñ­L8ÙäÃ÷íŸÆO:iR>õ¿é MÍ— Á•UÃQ¯ “\]„¹ªVt«ßRþtŒÿ7àÉC&}b‹u|‰Ü*%šË†ð5>c ?¦Y úCiÖì¡|ÑŽ‡pf‡®/Ï#¯ŠñqëãiÔ?w¾öã&šQãÍßž¹ŠgÙñ†¤ÅãÒ‹†‡wcÀ€)ì˜ù‚•o+Ï?±|•[ n\›þ?ÿ«’¦Šü{Uy £(BÈjSTÌÑâåwˆbÔ l4Ñ¥ý©ïðéïh:o˜<ÙMgîÒᑱüó”éøûg³à‡˜;¯Ñ±bfšÑOã·‹W„}z’dÔ¨½\49‰ëíÄé,mÚ{µ-˜_Aã¥× oIŒ§o’tÛÜ­\äW2=jÁ<Ô‹ ð'­)?6È:µÊ!â÷'ƒ«ËKâ2i®;uÕÝs­{¸·©ˆl!¥æAX\ë_gÀ¾ksã£üJw%iœ£G7¤jÒ‹?:øJ‰¼_¸‹·®‘¡ÏŒÏÿÏÿ"f9LüÏZ°›9„×LÄì9˜— Ϋ|MhˆmÍ‚eûv±00¤3k§Ùš—­Jgò¦ñÕxåí8zé²4ˆÍ¥ ³6¼Qàºé¨¿†ü¿šÒ– |–ØT~¢q(÷4ÍÅ>šgšœÆ…ÏN {Sѳè5‰ý±ß]IÇŸyÀì…ü¯‹"\–@yV ó»’ÑúÊ\×hC½ZaU‹&;8Á’”n|¼~5œßf…Ç£¾@yc%~· ™#´_ù´°g‡&¤Äþ—×=sóyaOz›¿»Ÿ»hÓ“MÿÇÿõÄĈøô9à8ê¾bÑpû¬7dmÂÕª`Úñ|p}&CWº]ß•{øaù‚õwíéƒÉôí·¸ó…w¨Îéj°øµ•ÿ¡_åbIG¶ ±¿Ú¢7Ñ—û'À™…'q¤ç2¼yÜ.Ì» Yg(ÿçDžUâ΋ Y¶¾Hp0¡eVý†På1X³a- Κ¢Õ~m|ÖäÍsçøò%­N0ü¶~| •}ÚÂbn=Š(,¡¿mG zRÞƒËÀ¡™S‹7Ë@ï9FŒàsÍ7ãQ/ 8ç-‚K'œã• äÿìO§B;µO¤lŸ1b§L•5Ç’íf´vn*&ošŒ’7‡ÂÎ7ù¸µ² m…ÚŠ ¬^hÍ·Ý{·\z'ŒÒÉóq1 7‚àø,¼8{×ûøvý BÅ ‰˾u¼Y)1»qrÒÒ\ˆy1ûÑXážÏœ…~ÖÒ´XN—Žñù-ôýyHp0 ‹mùáÇNK¾Æ²_·Ø›Óa~½Ø·4AÞ#¸¯5›¹øÏµäžL—Îkë±ú®üÌÜ*rR8S”ÑÌ\ðÝv¯‰fòˆQª9{`ÖÔRtþt>̯û?û]»•¿÷&£þz;ú÷Û.¢ÑæGR%ésMÞ:fË[«äÙ{O–þX>”Å Š­³áàhÛ.‘ÑJ®ÿ5g®ªÝÏåK²¦©CùÅGÉæÛs‚#(ö\†¶JzÑûw¶âšqµX°í¾`lI/fç=±2ωå¼ý\×Z6Ûø%Öp‡}eÚÎJ4Qw€Ä+nð¸ÿ©O•ØÑ-ÆŽ0gÓ¾±=ݸžŠzÏ´éðÃaªxúü2è<‰Úf:p¿í7‘êÒ5î¶Ø‘<Ïñï—ëlÒfî%ŸO2,ý/þùÝÛù—H÷„ÛK‡ñ©êݬ8Å?ÜÄR"ö ßŒ[ðKü.ÂÐd6Gù¾3pªý7Ї¬«êx6>›'gYѦPGzüÉyÜüÅÏW0äSBcéíYß…:6+yÃÿGÔwÇsõ¾ÿÛ+³È ™¡ee¿îë.R"£A ‰vJZB$3+- ! НsÝ’Qi A»$¥BChýzŸÏþ8œó:÷Óu]Ïëù<®s3n`J÷ÕÙ¾ø`;, Ï׊ar”?ˆ£DÞià¢îßi¯S,øG í·žÊroIò(G¬í]ÄôΞ…3F²ìBÐ2&R"6ÆöŒïwD§7Ë‚HÐÞ|ž-/Uc£{M©ÍÓ·ø"î%¼0y†·bé“[“˜1¯“˜·`óýÃK!zZbý¬CFêN° Š|yÑ+ÙÖiöXç«KäW¢Yü\û4žö9LaÚn¹dUØzNGœy´of D­Øgù$j))Ï䦎bǪ¼î»æà\h‡ë,DXWà%´}Çq ©ðó‚ç³õ¢çdšÓ^OŽä.`Û®½ [*b]KîïÕ¦dxÃÿÍ7m0£}3m™^r<4öŸ!/²&Ñ©P*h•L÷a“R;ÿ¡ìRüQ”ºËXÓ7Q´¬3–inÛÂý\TÉ=ÞJV £ø®Jö$–N~z€Y=cuŠÀ—šÀ}Ä˦@¬†àˆþ³ºý‡à»Ü»ÞºLê¹~ 5ÊäÍ s$?t×C¶ÑdxÛ[ËÅ\Ö¢¼ËfÌkø"/jÒlâ¿î4,¾­Šߨ’%Ås˜Wb?ZÖ…Ò3SCðÈÌô®´k«Sdù³üàOG úÈÿíÕ¬³ß½fjØ4èhêAëy¡8»ë W$¨Œ*o3ðœm×ð;昉—Ê0è=~í¿Ú5PúF“Jž^¿rGÑWFôøçÈ´ˆÁµÜÕÞS¦8—Ï1Ç4÷AÞ`ƒ Ü9¥„ÆJ9°uµZÿ°ÿ꛺ë'š‰Š`p÷ËHÿ;“P¢âéh¨ ÄZ-V‚ô¥WäNåoÌi‘¢&_9±Áv¸rh•ŠXÎ}§ÇÆ7(²ˆøg¸º"W-Âo•á±;â,´5”W̓ì¥PZ¿÷›\Ešy6õðX Ó?®b–½kPùáFP}ØÂ\_–º³˜m¯ þiØK+ÕhüÆÛöW6Ý‚ú…rTh›;ž¾€îEu)Q† {ñ{Çj  ¶³Î<1Ÿ]–6;Ïø‰—Cåé÷Ž,xm׈çëñÏŸ9 ã狲+OØß¬õüý›Yˆ@yk£RGêÿø5òÐHŸ_§ÂN. á6SEîW‰tí´cß'Žb/…‡¹•Ó b¦;¯|á#îd|À³ï+^§XÇw)ú(îÿdòVXzÈ…v×ä±ÍŸŸ;x¯¯åå»é‹¨òÆaŒ¿zÄ7Â%7ƒè¨WiT&`s·¢_/Ìd».óA¨ç>ìš =DƒîóÝ™3Üü¹ÓØôOêümÉS¨˜ÿ¿c[€e† på1¶uF°?cÀ»[œFNÍþh®†…‘3!=b Ý:WÜö§{5—AœmD.ÑbSÓ„ØŒgYd¯«}û;n¤ÿ}á="•ùGàJÀjüë1òvM„ ;†øË Áó!liºqtÞƒ=ß,p¶S)¨-Íç4žué‰ |¸¼ý3ÚÐD«–ôˆ”€˜bN[þ>üï}}J:ÊaÇ:ev*a4Ý.›ˆ“gýá"ØY’É­‹Z€³–àóñ[€³MÀegÊPRÙ ¬#&ÓqWg2Á0;öò§6ˆ~"·¸¡ÁMO¨•h¾Ú»r+<F£¼>ÃÏ€G°éüI‚‰Wq8QŽVm^Æ;¤Î—^Ê^VNe!ÏB`Í!:åétzÄæ.T$7bf½êHý5ªçN„ß„G8l•~Ë—[Ìà‡™8MÍîäôcä†/¼‹ù0ÞS ¯š ×Lþ¬st0æ‚&óÔ ÆÂNÇúúÈ·5+MêA¥ï¶œ¸òE؛ĽJ½ùÏãïÆ³^øë¬È‰ÌÀÁgw‰c¹46>”g'”Àóå\Ü •ƒNæPÊæÝ½)‰+#2À¬¾³Í&r×~ Û ¶`øùÿß:ÑtÒw¢p(‹÷“wœL8Ô‡GTòþÝû2„>,«‰p.çnIïÇK Óf6¯/Aíõ~݆|¡’4àÛ…ÿÛ'zºSÒHýOèv( êã^%æAÉç/Ødq…ÝêM„°žs8Ç}'žTMänª!³ÆåäÒ±Á¨‘L ù1 CC|™´ëU"ë–?ʾ¾¦{tE['î_&¡‹êoý…­¥AdM±èøÃö~tñ»ƒ¸øÅQ8)ä Ï—Øñôùƒ<ÏŽ-¸Üƒ§Í* ¸,_‰kQÛÉù©ËélßDÓÔÝ`Î1 jéÆ.NmžÝÂ'ŸLXçzæ»Í‚»_Á‘¼·#øAù ë)àÖ§õhÒ~l.ÄËcðmN0JDɱ1Ôîž–çV˜< û¥iTZ-ZC'Úv•ã!»$pç´£ F¥üåºw}‡Z…jîï·蟿Ï^Ÿº]Ìòº)wƒ/„Ê`ôg®ÇÓ '^%:F5äŽP-ÏgÞe(»ú“?¬3—¿Ë"o\ݹÎUxóø$²ëÅ þô »ùkkêQsìAÜô†®§#¼6…¬›!,å ·yÌÌ(V©ðn²H>‰(½º²™·}ÕA4ØÐÆX¬‹gÕ¡tMêWÎÁ7Lw¿sãW’¼V ?ÄÀ‡!=Üd7 Š8&‘iSü¤á€f‰œ&›·â½S5DÕö9¸n¥?ÿÒúd8jõª7Â,ñå°>xžñDf¢IýMïp{?Ì b3ô¨ß©¦*hIç˜ðÄÕXìÁhlÙ©‰¾©oðÞ.2Ñn‹ª ¢žl‰M“Ë‚"í`ˆÈ›Çï*(‚;×ñeq®Y8çÝ+¸œúŽg‰1³µ—aÖíñt`± i€©m¨2=ß²íã‘kt˜¯Æa=spOyõ›1?t ÕȾɭn®‡óðnÚ$Q=ûá({ ÓèzM8,dIs¶1C.7mzÇ…®üTã¹d½ a—.à̺rÞš3½øûÎ*PÎ7a+ Áíp•èÆÕçUpvQZö!í> ÏÃñć8*¨ƒäˆþ;í* §L¹ýÿ´™×m¶Ú¢—»[~›ÿõÛs|X=Ž.e0»^ˆf¯`»¤b¦ÿôYvÃþjÒ¶ÚÇ$7€ÏwÞ=ÄyTcnd4 S»‰+T9«À`` Wð©ùDà}þ *åÁß_ŒÅ/|lJ7ÿ‹½8}óï÷îªi{P&­‰dVîg£wŸ¼Ÿx~ÅaPØ©C=ƒÿæž¿yê—[šÔõùh>d@]ëDYžÿ8)þ g»ipÃ}õ¸ig >œTŽnÉ‘Ì0;“;VŠg¿[s/C·£©©1w'%­ÎðêFø¿zú9¿`¹´°Ç žÅ?Û$^Ü»±uvÕ°ÔJ‡;P–…>O8¿´«èŽ1´‘s*õ†gýà$òÛa\H&~Ûó3m„Àô½ÝxazŽÝÃ;äWއ3òÔžLäl¨.ûОV8áŸæQLœݧƒèLýÃ8¥ÌŽÎ×àа¦Þ¨Þu¹²žèg'Ŷ–ó­Ö{â &øŒ[}CZQˆ¶DY‘¡ bÌÜQ€Ýw¾…+DÚá\ï|Ò¶ ®f§Ãô®^n…È"¢_¯Ë”k´àÏÉ 8zU¼èèÉ+ídâíi–ÒÖl¢tÔEвsuhyÝ’Å©ÕÁ»4Äî±Q°$æ&¨H\§+Ÿ¸S±YxÈ2•Xõ»€o¸?¿Ù;t[ªH³lž¶ûÉI¤ÝÁi¿9?a‘€Rn¼Wº–ÎwN]n"çm-¿ðÓ´»<–Irƒ„i‡Ahêc,ý Xe늗¯!œˆhRw§“Øí º¤šÑÅ÷×.M…óµðß½1¼ú/YÔÆÿÝ—Œq¡jJW%‚xž;\’yËK5§kÇ·?±Üí²…L½zé~Ó'É>ªš».Bâj=*ëøž“òÌñòÇXòí ‚ÜTЗaµÎ70²"›n!ÛgË ‰øTzc¯:KÞûšóùfIW8¯Ñ$`Wƒ-ᔯýoçÁ)‡P5Ö‹lÎð…%˦cä¾™ ’ôú¥“t06WÞ:EæÞ Ó7¬›9UÄuzÕÆ’©‘`tZœK[Œ;ÃÃ`y‰({¶l,œ¹–³ŒŽÁ¨m|¸éYƒ†¾¨ÓðÏÒLÑ= åïŒel‰ ~ºfD~äNáVÏàÑE©Ò¬þÝuЭ‰3lÙï1DzûÐÿ{Û{ã4ïÛäõ˜EèÃVð·_X…Ó¾éA½î%î¹V3ûãyðu9&ÿa0ï­.zÚ ¿šH‡¬€ NîÔÐh:¿E†…¯˜{fHÓÂ3™ýB/ˆ}ýÿÛó9wAöêÁR ]ìâ§é ß&Xàš•1¤î]6 ÕeâÀ=1XÚG~…‡c¼vš™rà?ïyÓ¢ŠÑôÝ£C¶?ÌëßqöøU ù $ßB®}*#’:BôÖø—àô×ÒRûÈ.£0sàØ·$eocA÷¡(.4ƒ:åïøUÝex?· GøOù–78˜úáf¥Fœâlz=êÿ|æ>\xr€ëá%bä8¸}§ cÍ\ÐiO7Ú]‚\:ôšËÇœõ¯ÑXÐßžÀ„Ï2Pyà$÷ôŒ.=um€Ì@MÈÝ´E<¦âÀ_/<. @Åyx+w”³ã¦G“ïٲà žP£ýþ¬ o¹3Ù¥ÛlÆ©µlòÜõìÚ` jwëÈ$Ö+®£|ð":ç l7¢6Œc»~ì$…ƒVtÁŒ„š‹G_Áâå­Øü+‹sžD©fm\½½†½uÚŸs†1ow59¦0‚WO俈I7ÿû*EH½¢KýlŒiíò‹¸ôÅ_N3x[ѼÍø„«Vp+;@Ée ¦ÚÆ¢’æÌ¿S¹¶ÑPò]vlšÊ„ vr2¢Cp*0 ';l‡é»o£÷ë\\7,žó¨úKrÍ0Z&Ìfй5eCYÎ9+è³¾ Þ›Áî8îÔL9¶¼­™úëIkG‘yJ´ªH”9ŸÀ†%ºVo}ßÕ†¤ÃlÒ«¹¯š¤BV‹aóÙÉ0ªºÕ¶0°s ‚g2Çá©öaž¹Ú*¡ƒ__ýûÙÕ!{¯FŽìêщ÷”>îfg [.e—v`']–ƒßÎð†ÚÌ`¬Åi²QM™…»Ü@¿†œw¼TVâ¸Ðìï1Ä¥å•Pxvxß3‚U ó! ò‘(I²13Câa%81A“^ù Yñê¨}Q?Ä¡©˜+³.ÿIª­ qÍ–9-a!o·qKësÇvÊã¤þ½ Ôv®ƒú&m&Àw€¼‚sXrk4³ÙV†ƒÒÛ`³ø ²Aغ‰Äs¯VÃ@öw'ž'o2rpzª zXÉ2éR>oÁ‘ ø]w‘{Žëqnµ}0‹j|7‚ßNb§uÛþg×bÜw6ÊÉÓQçy´û| øéæá?:ìP„GéãÔ_Ǿ$ÈMËp±gÙxQ|SyaEÇrÁM}œB+$œC.Ë’ÓG&;ë"©žj ª¾”†¼äÂjòÞ’î™èM·ÏƒÒëÄ-“ðÈÌàç ²ÚÖ$x¸N‰{—”¸£s˜ÔdÄ`r~4w)óæî²¢R†é™Å;¥žKëÊÉÎþµ¨øÅ‡ÎŽŸ(-eЈñf8¡â1¹- ÒkgÂ2·%Ütílžvyìÿ9dƒ°™ã¡l؇Ý>†ž(Ó!ªÌ °*ðJî,ƒ§YÀº `ó qßJSî#§Hûun¢à%UXè©A®a{ÏÂÒŠî×Ñìô Ašþ^–¼»BýQà¥{¦PR/kÞM•c>ý`/–ý«XáXNÁé ü·oô¦ËqE‹?—6hy]*K(±–Ðÿæ—}72ÓàH´WT¡3"§ò’R<ùtÜJ†ëNw8CçAþáã:l¬Õ<6åL80»‚·Mö‰¶€Yª$c}\ ºº3ÎÚÿîØ 2Åh">|~Ȱ>Þ£"aî…”3ž™ wv‰Ògó`[^:ÆÉõà—稡&Á¶nK¼SÍ ¬ˆæ7ÛÄŸÇʈ—l„ºDB<¸wÙ­Ü•%è6Ü祂9Ù×mv4W/É"ÏùàRž'í“Îm©‰ûÜpm_F ˆþa+l<»î˜h‚èþÓP#…ÅW¸Yù•èã@W,Û ƒêb4v¸â÷âËÁ­xûø bµêÕ† .V/:‚ v‹ù;¯O¦%ûÖÑ#4D<Îìãëšæ¢õÉ ògÍá‘ø—Û'`èõd“µ6³áƒW‰fÛlòžHCL„Ùåó¯Ñ)3ÚG«2…Nju§Þ™wc¯­1¨/Žd¢ûˆ~ã&f~+Tsíò-¯â2¾B»Î+¸±¡Cbyýªêìçñ“X홎v_¡xȈ=ݵœ ÉôŽÄ_åþkîKò¾ñwj}P†ÆpJèÖ*Ät¿ø?%¦«cÂöí¦Té\*UNX„£—ÝÇ‹!}åkî®ámÞ:{zàÍ,&fÐs{ ÐeÛ ®l0”¶OÏdbã`~}ô7¼ô£¼š­X~å©Úüßg“ƒ7×3«Ã0¿¦’øËÃ΢û8aj>¤ˆ$ ·o,Õ{³^éRíç6h§Gï_ð¥ï1¦x 4åý»W ï~K7±ˆÅSÈûüL‹˜ëJ=hÛõw(¦Bö1#ýß~O>^1’ ë®ÌäÇÿÁõmP‡¬‰9Ðr,Œ†*Òw­\áxoâàTG ΂_C †/lÀW¾¢t\Å^nî1Y¦š¾›ïÔ)ÊB$ÿX% û$@é¨}øî­wõ&Fƒ×iÂæÍ“Áñ½h OÀŒ/oñÝž“¨Mþy©0=v¼¾Mí<ˆõÅKÄ~œü§ë‚ÔÙ¨I¤4L…9gq…1ÐîO >rÈB•ÜŒ×HÁðÙ8#AŒ;1Ec1Ù™¿¯æ´ [e$É;öO'§)`Kþhº£TxrOa¨á6¸‡þŸûUGú_rF(¤ð'‘žëOaÙ^QNì÷.îÛøíðIb+¿-¯/N£ÉÓk ÁTˆ=Å&Yø9à7lÊÚQ_ÅiÁïÇäeÖqžÅ‘‰ù‡øiÆâŽñã™vôA.J¤%?¿æ£¯c–ç9tm±†ìçó¨ã“AØÙ¯‡uGè@ãAF ôl7gÀŽ%ën†ìõ/°`Jž°V¥ƒÛ…ià\#OZ_æ_zõÚ¢{xª×zHLBÈ=ýÇCAjtÎÓϘ4ø‚ÐIßÿçÿ^(@ŠuX}S¥^œqþ´¹føê²p»*íøp”Û™$Âì.¥cÉî…´dv64¾tb ¿}å„J´ðþ+avo?‚˜Æ|ü°ý$óY@Àr~%èyïåûºÖÂjƒTq7~¸¯bxÇáÌiôÇçô‰Ï8V˜Á,\„ØE!'j`/Äö]O ÏœçÐÁÕ³ÐU, Ÿ]IÚ¸õ”³¼'N¦®˜JïMþUæ“ëR (¥µ”;«“ ™‹M±¿«‚Í® f/;ÏÁº4iy ~=ßÓl¦b¯ÅnºðF<†Ø‰ñû:Ÿ"F'Ì?Ï-8KJ®vó¶d S+ÉqÔ·å%W¥¥Ê>Ï].crH€k7jP›…GŒÂÝ«7¡ô$yúiŸ”¿=F®··'—Uö‡É…FM¶hÚÎqAÄ(h Ü­c賬—ÿ28.¿.&Ùggc»ëW7e4]¶Öš½»4ÈWòN$Qb …^‡Ó$]Cb!-ņðþZ@_%Gû*¬ÔYJ»ÃùC¸¢öOá±î(Ø ûï-f=6« Jw:ÝÐbLýWÁޗ縦…IäxÅençÂQÜJÃÃÿ¼ñR®ä|-—¶ÔœÆs`¯7,b ´GøïpîN." XåI=W¡1Y§±x¬ÕüYˆËÍæ±Û§Ê0|s óz5œ2P$Vï§QÙªbP¾ùˆL"¹0îÙgˆMÇRZΩæÏ[MT.‘AÛ_ÿÎ䱎º6¬ÓÛNÖ ü9?™ã$óíuéÅ$Ÿ Ø,jÆ>lX‚][ïÃ_gu¸¡|EeäØ ~)¾KŸÍú'ÏåoÛ±çúƒÐ4#ž^Ͷº¨Â‡¯\e`-HLÔ§Åq_áê–Xº0}Þ–š u¨«‚fÁñx13€î­ ¬5 ËÏžøfÏ%åÒ#ø¶¤cFa!tجÞÜa2óÏt¬zwLמÄñ)i9­|n¬ÑKbMu™°©;RmÃÚ·Nfó Ñ#­+ÇÒ¤®F¼¦R kž V¶‚f£œ“u4T莦Ÿ{7B w …³Ÿ…¿ßÉÅ"K¨Æ'w;z}J·”ð£Ò :y:kzш“yç±âÅ'ÒûK _qð¤/Ü/]†ï›9÷ÏgY|øX¡díE²ù®5ßU©ó`oà$Ãj£ü'¬1}ÿ%ôÝ<—¹9‹‰-%«ÊÛðĸ8v3x-Z¹ä±±ë?ù?|êï¾á–,SñFÅ«2,á¯ZV9ລkˆ`¶;~™W £ï?À×¹ñÎ’=­‹ ªûºá“¿7LÓ=„öd>§­dÑòf|ªå InvìšÂ"ȱgUƒpl[7ñÉÅé+û&C¼]½ vå°ºÖ Ôãµ*„êñ¹½-äé‚BÈúöWÑ2#-èìx 5¡ÁÌê¥0dù §*†²eULt8×AnÇ=²ûÛüS”^˨Æõ*OFôŸT— ü m#ל¥`ËÖ TO‡å–âl`À–™ŠHѵy7íOIeƒÛÒgÜ­¥ZøÀ;Û¡»möž’â4zvÐFõ+ XtúýãàîDSšfv &(¾ÄÙyi¸d´‘þ›D?AóPy‰q§Ý®sRî«ñL³ ú1<ô¸¥öŠ¢e\,ÛNúD,ñ×ÅèüØ™ŽO~Sªæs/Ÿ‹òå¬ò¡QLl^#æ%:ädÔZÌ5‡ûå hܶ þä.t*ÀÍe¥Õh‡¾ã–pI”ñùL² ᥰ>°©¿²ÓmGð»¿nG÷=á{û?Ÿ¦oFÛ‡VÒk˜]Ê¢¢ŒqÂV[t¹§ 57°ñûÒQñ»<µx—*—–2§uéàôlu™ˆ˜þtKì9Çû=T$^€Ývó6¢£°:fÜÝ› ¯à ®~^bÍy~)&šòF,æœ ‹;û–´¹¼áN¶çaî‹ãDXe7¶ŠŠ2å.S*q'—«ÞmB‡d“ÙZµ>_‘‰ÄúîYäàŒÑ´tÍ ¸ÙªOoÝÛÍ—/¢µg<ˆmøð_;‰1Çù“ ¹i¢²¸¸w9$š‹>KqþP‹Êú8‚ÿsm4Ñ>²ÅåÈ“ÀœTÐM¢û On,ËäCT{ k ÚŸ¸`é-àºÖžù¼ÚË6ðo â[+P{€ÚœtO–¥ÁVæTPׂnðÈÃo›kÀfïXj ´œú v¢Ì 5öĦƒ»ÛÈÝÞhøV=‰Õ^‰QfPdG½VIƒ 4‘Ͼ"øäâݯHðày2¬bÂ/Þ߀߬!Øb¸¶E8œ¿Í8µÆí¨¦XGZN¤Pí»Ÿ1òV'(*ÌÅý‘p¼“0rê-_fm6åÞó"qz×}ªéiÃyK-Z)7~¤ÿogH\ /c‡¼ +Z»ŒM~í³Xô_YÕ³‘W‹,|êd<½£ ‹Dq;u•jÖXrp}æqöàñ7¢Î;É–ìâ«3œYZƒ5kÄXY’µl~„ž©UºøÛ-XJ[J ¥#ЕܢÑOVCRóB\°v>c´œ©-Œ§{ûKðþ›p6E4ŸÚUú#y5ŠYHÆQ^—4æ%¬„±î…´~~ ;³Ýž¥~Udïúc¨Iÿ-ÄÉÿôÔ‰:ùÍn“¥ø¯¶åì>ȳ¢ÔJ ñeûk­xûoÑœúR8uL›­!Øž‡ÿ«ÿŒ±Ü‹ÈÙœ3ë;Ÿ.iÔ%Ç“aîÐ4ùÎc{®K³Í›«x´HŸÒ¦p*ybr÷£¨«z:Û¾ûx¥‚æß~ÃuïÌÇ_×èËáÝ|ûœÞÞyS6‚{Çc ´”€ñsºþÕßBä÷=…£1t–áJBléÛÝci~þ4î(ý„ zOáë²C°B1ÊB,™µ´œÙ¬„µ‹Õ©‹f2<ÑIeÑsRÙ_|¦Ÿ¾¶ž‹Ê, }1ûoŸiÇŸ1_Cá]x›D¤¹¯©xyÂ,ý´—¾§ƒ?êFüß„L7œôä- ¹8ƒŽ˜Ó;^MôìËD?ž ­PìÄeɼ;·&ÂÊMg`½—àÊ' é§L—Ö¾å,I«q“ßýŒÈùnc¥ËžaÌ¢ÝXjØg6JÐvízð¯4œޓÐèçä‘ù÷ôË\ßSY”OÙdå¿<½w܃[SéÀºâú¤Œõz_‡E9ðúDg·¹sMaÏ”x.õÔWxXõ…_.Øn®oÐùVn©ñ„ºnö-’f{×ã.áOO= ÁÃŒŒ5fúë»qî9V0µuŸýÅëîøè}<'hîƒF†`Ì¡&løs@éíϾ É£“½::_V]?K}.¹²¾%Œ¢YOí¹ìÀfÀ3;Ùœ 麄•d` ™ÌоÛšZ¹b—HHïÀ]_¯¢\"e%AætA¦ÞHÿ/Q.'Gz‹QÛ"œ¶ eÎÉì÷ÃìâŠ*zîËAð˜û€ÔÛ$b×_/ºZNž®)›ÅžDÂÙñôü L#²¸^'ˆw—+}´e1?v €C [à{ÍÝú¿ç¿©«@»÷¹Î݆óO^á¿Ï8Wáz*ãËàÑRe]YÀPs—[8« ÒÌ%ÁmF õ»WIß×`E¢÷kç!ò(GƒŒÒüÃ.^tÈ»§'ì„G©ªXû¼eúŠaTÍ™ ŪQÛ4ƒ )þ?³®pnõôQc6*åºÒqža¸åÜÔIa.gw³>Ùµ,ʰ1ºŽ:E«@ÛÃuÜ ‹D8s&]aþ—“ºžJ§Où¡ ¬\ÖæµaòµMP·å"ÿ]lŸçE‡µîñ§‡‡aþ¤±LËÃGw“˜ëšÅ#ø_UÝçÊ'Ibe¶;ðÛ”éCÃ'aiß^hþ«o±þD_°ª{û„ÜØÍ¥b,.n˜T®ƒ¯'W³^Ó  T>gÒZáÙeuVp3ScñÌÊ@ØØc̶MÞƒUN³Áz G¿¨ÇÖVð`v?^5Ù ;’)eeäÒïw$v•nË¿wì¹57«1ôÆK¬?ô€dîgtPK ms`‰ÊÃ8M¶rÄÍÉ ;˜lT0ËìP³L»xŸ“XÊéIG ÕèÕlQ— =ªŸN|ÚîÀ‡£áÞGx:FVík‚mQ dó° PÍâN‘I4sÝ1¦¼é:y2.ŠáÃ4K(OO“>ˆÒàÃà~§ûmΈÿ Ðì#ávk+.q'¢¤è‘é¿qýq3¦nq ¶”ûJöõ—&;!Ë&)—÷óXÁÁÞ½žlÔ›gÅPßǘãs¯Å((OwÐtð…¢·ãøQQ•|ù7b$G{3ôeÃ¥n<ÉÓãï|ÇÝÝÉ %é9¿ nS+ïë #r¡éf4½ƒ˜Qz{C%ôÆÅw$xÌ3"ÿfOöÊOBõOÛàjøªÇ4çÖÁÝêNœóø ªßE²¹Ý’vÎÚ†ãñäh2ì—ƒEÍ“¨ëö•˜ã; ¯…OOf•úº‘øO¾ò ÌWºòÏÊð˜õ')*¹—‘ o!ú­"7aE#17kfÑݼÁ;‚nuRf¾Æ Í?9nî$zÕ^Ÿ««“Ö»sÉ—ƒ ½’áÍ““c;~QžhpîGÜÃÌX•itœU£—eìç¨(ɃYh>ÏžºŒ»G~ˆ°ýç×ÑEwÊ!£[ ?ºý朣Èrp¡~ÖÙà§®†y•#þçÁ\lœ¼6¯œ§ÞÂf“BÐ=É)ö+Ñ;›ÕéìøÉ¬s¢«zð–ŸâgõÏ;Ãö‚ð³0Ù>îÄ/ÞRÉÃ\á~ (‰€.×vØ*t n®C Ýèâw-¼ö3у'ÃÅl…FçXcx€“píÂMzðèÜOXu<›kµ¸¯]‡a¬ì}.{ßg.{I1îœk–Öà[gSñò7sçcÄ*>›@¶M*h×߯`‘¤?v$½!9bêìÉÀ]VzßNv‘eÍàØ²†Ÿt:ˆÓ·Ÿk}Æ¢wH_]ÐHü¿÷¾Po3 &Ï×aÇŽ€ýË—DêT.>¬Í›DqœÍjpžÃnÓÚÃá.4¹)£1ûaŒó,"‡k¹d ããÛ¹s±fÿ<(/ÌåÍ-˜¶>œT>‹1K1*2 ßkåø±¯¸Áp?F•^^VýOcÁØéÄ+H”ý÷ÕüòYšäÐôµ¿ ±…[Ô1‹õ© â¹ÆhnµÇy²l{ ¢R”¬^I6“„ŒÉ¼ÐÐ\ß39kÌÜ~Äb×~ø|!_‚êåçÉ‘¥R\{¼‹ýs*ææŒàwlYvw ®ÆÙO0ÅΈ›©9‹Í=å‹‹ž”qaTô¥.3É#_<³!G·’„œ¹‘9’0ë ¦š íÈ5}s (&¨adØV%ur½GfÙAÖt °Ýv ÉUóþ°H>É`]â,Å®bþÆ‘3Ž`;ç¶ÃA5 ˜t-×¼ÿN¶åËá"ë¯daz¸†Iï¦,mjׇܫSèN£"tØ¢Écx*ûo¢€°|M=ñqÅ];yäeÍÎúÊžšiC ¼\Å¡±tôiSÃwûFò?¹b·ï~*>/y~ãN‚íÓçð÷þXHéž UYô¿<›Ü)Í|>ÌG§Åº¸3Œx_V<Ãc¨ÔÚ âúr!1Ô^šg“Ò´Eàhßåۘؾl2uš$$|ÝAºz]°!äÖ«•Cr+ö¢± ‡aØ}«¥ ãq­Ìóš¾ë[Á2k<Õ8dŒO¯ðßÖèmiI”=šñíÏ+(%ƒÛÝöâ~ÍZÎDo6oZe+qî†;çφ‚¢]ä¨AاÀ/ãïþÝHZ= AãÁ(níib«±–þ¨û' uæO»ýê­R;Ÿ-±þC… áé¢úmmñ<îM´Þ¯‚m—»a‹l®Pü€i¼8ôûy¿/Þ„/Ú;‰Q [×aÀ4sࣀ,ž[ƒ·¶»£ªòv,âÙpk.y7Â<< ò´/,[Ÿ‰šîuü)¯¶“wqyXhs3|j¹ÅÛoÁÏø"üù} \?󀈿^È-êñ \ŸrZ²ª¬ÿu->;Ë#'góù'›-hQ­7N8R›Á ÇWO¤;oN§þâÐ4l“#µi×Þ7æ¬ýŒ)ËÒ^¢IÝŠNB×/}zF`W±ð Ó,ÚHn˜–rFïâüïL€ÁüqÌwö(̈/dzAlåÅplÎ68ê°€éfH²Û_ÁãâœcÈ6-Q£Ï ;1¯RŠtE*Ð¨è¿ Uÿkcƒ˜-þö#k(Ü›dÞªòt|·¶-èæ¶ذöäüøs YŽfýõj7<ž ÂÌ÷åpuΉ¨’Ù8ÂFÍkPÕöLMݺؚ·ÿ‚süq.ÿê~L)'\¹N!Ö‹ 0ýÎÈ›TFÚ‚én÷SP ¦¨5!‚xZlà¢É%¸¶ÙŒžIœ ߎ­ÂÁÌË|õŒ§j¬KùO²Q8Å~~ë]Äè¶6N2ô[M‚ƒ NÚËÃMɼžLLq¯¡û‹ø©=kH¦)Ÿs?¬Eƒ¾n…«u{¹ýyÏjØ¥7DóÎPúŽi¨´‹Zœ îVc‰>Oúzzà‡~|9_[" ¸µ³™K-6;]À¸†"~ýî˜"N–ÖŠÄß>ägd²v8³MÂm0µ¨JÒ3`RK7è»}å=ŸŽ­Æ©¨vå dïró¾¼àäõ8m¹#ü})ÁýI•'™HÕ_àÊç ©¦Ö(jý ˜uøsœ«„·ý:ðØQTóû€eZT(‰3Ó¯c0/ÁDû @¤g™ë›‰–ß”YøïAîîCiúëóQ,• _[·áŽÀe`Y ìã‡4˜r„¸è¿?“œa)Ç2ŸYŨëx¾æ~Øui'óSwƒ©J(‹“íÅïÓ`ƒ´:%ÓèD œ›9a¿DM7™ŸÜ懶‚—u4ËûWg Ð_QãÉäú`ZÿFi;í§nâqh¢¿ úƒ£e—9ôô,éTcÇ¥>ãû©ŸàshÛÛâ|U#¼{•'꼺÷UËÑè`Z;FÓXi: pÓs¯Á‡}sÓ›•àwm1•,Åü®=ã«é}ÂñWÿ¢“ƒ Í߸ŒN9âÏÆîà§|ÙB.¯t¥ ùENÖ,e‡$PƒÅ¶àßz©¸wÃ79üN³aü{^•®Sƒ•¿ƒÓöÕPÙpÔ^¶àïyepZ¡Å-˜}ûÐHÿ»}ù,oœÐ(Ýw®p¯¹…ƒ²ÌašÛrh ç|;wE.%ív•|=•¯Ü‘Hë3!G*#´§éê,¸â+L¿ÞõÄ'ÍY¸yžÿ²Nq n&?²æ¢½t3y1*ÿ]Ãõ¥Ö€Ùš3¨$}¹Úfpw¿掻8«R Ü9½ ϧþÁíj}|)M7x‘ÒM¿ÒÀaŽ]rø1­3$B7\Á2|æŸø†êsD87ôZ;›'|5 š_÷aôyMœræÁµ g]´RbEG>£ß"C2×?›ÛÝ Zã$èáAü|z=ø2u„ÿ›Å΋×Qh+W¡ââ3°oÔM¸—T†wª²˜ÖZîœÏaØ)–Et+ó1ᘃœjw„ðóRxðSi5Nÿ<ŠîšßJþÌÄŽ¡œÞë»°Dâ > ÒÅÕ³¤IM.ÐÜ’D.öo?ìpô£ŽÇN5i0ÿ0eV:G–¸ßÃé¾Êhb6[­©×tc¶;3 ß'®†¯ÚáîÀµR¬BÛj« È×wÊxæù®;O• tÝáõOòÃùSè’¨°óÇ&uˆÇ~NCE^ “TIQ°þµ =>ÕCêætä2ÚÉĘ)#Ï~§tñÂæsM‹]éßiwðI¯Û·;ÅS\ؤöÙP=Ož w ¦B2ôᆠTkçMnûÜÑø~M2=~DÍ~Ê/¡åc+N £Å?jŠûR¹ß{âé²e_Anâˆ8Ó„ŸÚ È£¦Û}$šîµú„¥“ ÙýÓy¸àSgõÝ<-xl\2‡µ3g‘{/°?“„Ø¡%ðíx95ôŸHÚqµÀ^¾•ÃH ÞLc¶Vìœ]µ[aRóvÞuŒ;çÄúMRXf§{ôã:‘Õ•¤W½ƒÙZ×\ºxòVÎzJ8˜¯ÉãYþïý‡ÿ«˜w¦‡÷4ŸƒÛƒôõ«¹î©@K;*ðêеkiã=Yro€aü†h¬ÖÞ7§·èéÉ»O¥D˜³ =]Rɾû W€9ïöòÓž,Ç“ç)û2:šiÌs9FOŽ¢AÙ'nuW ,£tî9¢¥Pf&Ý›EÙžc`´ýMTMÍc|jqŽx ôÿ™OV2Î8¿æëš;ðòk%¾Ò:Éõ]›MjZfÐË.aÿÖ›Óæ<¿†9åÌÜo˜˜Ÿj%é^á¢7ϧ­‰ds jˆJëFˆp=3ÒÿDÛ[0üû ¸5wÈŽýq¸œÕm\ý–8jMåõ À·û5S+M³²øÇ–5ñuTôiáO5¾oju^€+õSšf;˜ÿ&¶\G€íY`Å6T¡Od™ôë×y+wG^×"[¤„93æÊÓÑkrpò»÷ÄåèVþ«'ÞšÏMZCßj0îÚÄ!’©Û%áeÿho(NïÆÅá÷æ÷¸¹j+öU]yê&2ƒÈ`8«ø…ð´Ì9£ëjvæˆÑëßP%ë$¼kY+ß| â“&béÜ>Ü>}ÒþœSsñÛ^_Ø< ÇÕ‹Ì¢c¥å™X‘½êÌä_zÒ¿/¼1ýˆ‰ K¨J¾ûú'Çæá!Gjæveÿú v5âçDé› üÙ×ÈܶTsÏAιt¾~¹TÏŽÁÌ´4^?vó°¤A–šzM† û3Üá!èÚ ²‡¶P›Ü3tÚ¸ùhñÑŸw„rgDEp ù ÿ ù.:˜öWŒ‚#ƒú,þÜE.ËE™¸\¦×ÇûùÙíÉl^v4û68ÙŠKðw©ûrl9*¬Ë\þTãdÛh¾4 ÷½ké´þ»ˆ%òCXà? ³(ò\mhìKp{.JE=β` m&·áÌù)Å2Rci~¨'6BéUZ숯1X8,fÍ?S1-£Ð\q×zz"j,8®ÜÎÊD:ùÃoO±Ç_ýèv+I:úê/Ô<œIr^L¦ŽcE é]ó©Gÿô[q{oð§‹fƒµòhvÉD‹^ü³®üöÛ¨r†Š¢YÛw=6i¹6vþ§¯DÅYöÕ]°üÁ ôŒö¢Þ±ÏQæãMÖßfK·¿¬ã F Áìj}zpøªG`—~cDpÌû4‘N»¥@·ÇùŽàŸª>Ã-5P.)š¯Þ‚ù?¸õoŽèŠŸäõLOèš ‡ÏÜ‚®:pƲN}?O|#M«¥s?JÐkÛAëŽ x}Žt´åƒb˜½uΟZÚ«c·c‘±5ØïÍ's²pšŸ<½ Ó‚ó_÷ÊT"S1™¼ü9Ž9ÑHÿ°}„ÿ‡ÔÄ¡ä÷~2ÚA ­ €á5O8UW|ïÙ‡R’º ¸SƒCh{;•÷·`<]FySŽ£ÌÇ€ú^{ë jíÈÎV™1¹]ÂêƒpÅÍ oæ· Ù‘!SžÇý¸ÅM|'K-Ù òòÊ"òE=µâ+Ñå_)Vø’óã1nóKrcÿ{Ü*mËŽ[ZÁ·ëBœ€åùZ]œˆ>naµ÷]ò,!››Öãoi.„÷´¡êÀUü¢}ŠéMxHæög¡¦Þ(¶y®^;âÄY:Þ‚úØ à/œÛÌáã¹½œà p ¿zîB#øe£XþÓøÇDŽ}T‚;ŠÁÑOÚAª>c {Éš]í¸1 „Œÿóoý5±q¼[›ê¥f°Ç¶— Á²˜ycÛCnâþ)Ô£DlVßA…¦4ÅØ€ŸYM§í4§Þ.1˜§IϾ½',Ñ.  åÂDéÉìuÓXuzž`êV3*,²€•þ8I£Öm‡ Ë5(çiÌ®ÜåðÜFs:Ñexÿ½„¾úƒd«·Þ–§ dܸk¿c_íT’Ü;ür#0|§5õ”£–Óó¡uÃX*¾x í³™‚¼×–i1‚¿êˆ&Üùª‹zÑíþ ”ËQÁËë}é$ñø¶Q˜k3IGÏ…… j¡[ŽÚ³)*ÔùúQz¹¦;=«I÷Ý$Ú¨}^Dš²ôeG±}x »ç0½šã´ÊÒ§=Nìî 6®ñFþHBý_´ctÊ'ô’ØäQà¿Ï’½Põ€kïCcŸ­Í[ ãZúqÁ†µ»°˜”HÖ€Ph)×3GîwÌ¥‘i…8ac8Sžxxº«ÁèÝS,ûü‰ 9¨Å ¡õ†yŸÿN9&{þ /`¿.3ðùÁ©ŸÙ—^ ³ÓMGÁp³úHÿ?c܃o·’Â8A¾6 ±¤¹9‘÷eÀÖUÀÎÓ‘xþñ²‘¾‚7Im‚K¤£é£â`œvÑ–jyš17/]ê»CŽè{ Õ5$û•d¡ù÷DúTþª?ÇtRîc‹@:¼H£ï¾´)íà˜‰{‘G-þЙoƒsƒ.pnütðIæ Åü0&ÿC‡5¨Æ'uþR}’»§&Œ¯ÁkE÷IïÔ< ÓÀ÷p•™ëi •eGŒãà–¢ûíÛ3ÂÿÉ•k‰p©/ÒÎ¥ôžów¼oÀ~)9à‘×i´éÕF¸ªdH‡ü·Q›c[ˆòº‹8'Êf^„È}{Yê8iVh¿…™ðÂ:|é£Bô8]Èd²c1j… $–€¸§ÕV}̳²¾Í5„ßÀ³Bg¸C Ç_@ ÂÚ {­;«¬¸Ï÷Möb$—?Ûå-¦r •žD~¦k™¾‚o#¢Cµf…ƒ›»9Ñ©÷XáÀ¬¨ïÂbƒƒôéåpüTù<%Œ^2‘ƒ>kª^x‰þÒeøíc&›þE‹9Í·§U}}#ùÿ1- dxÃdõÞxhßdú/jêêh;#Î.8…“ßÎûìVRʾŒ!ÊY$‰SfaÜYuötÌaN!lŒý¸]¦iËĵY½úd6=¨ >— BóÁ|¸xT˜êïLÂ?ÇCÅÆm2Ň¼ ˜Z#.Ռ㪼ØnµArÌq%˜ï çÙ¾0ÂÜ>ð¡î ú¬‰ÏÍKéŸo«¡fm7lúàe§Þá”Fm\ð-ÔÍ訞¸y0œ6€ZE44äÖ‚Ï!œT÷rF± ­?ñðó…$ô® =ÕG”ß ²^ÿ*"[³f$þ™ý¹wnðÕ^‘eÊQÍÔhF·¥ÂÒ úìÓôHÒcv ~+^…Ö“:pÈâ™ü£ žw‡üÊ"ˆ¸§ »’ÿéVÓ+pê„&ó™b;à®ÉCð‘ÉáW…ÎeL¥‰™n;—÷7 ·Z³ß¡À$Ù,Ôõ1À¾ü§°ß ½®Yas†2Íš~“_¯Ãm|°|tÌ šÌNùCAáy|/2.¥òØ€¿·0ûê~¢>%@ù¶d§ˆÚÕÙ²WR,Ü|]KàêKrøc{x/âãÉerôÅD)&ý›Ryßÿí¼X*(xî#}¯ÂžÐ-*ËZÎ<…î^Qö~ª9Õ=¦¿·¶r_›d™©wY.ÞÂ%} êsû¸ ZnœÅ¯·c1\ÌLjŒf ™‘(ºä4ì|3…y¶í£ QœV¿عÎfùùø×ÄæŽ§ç£Šÿ]SÉÆ:4 8E#…ØÚ‰/qZ’+»¼l·À3Šš’ î÷'m<•g Q»¹7ªÇùï7{@­Iç ¢I“/>¡U,e·>hg‚ˆJ†=£7#Ëqa6ÙÍ ™\ϯl^:ŽöÝÿŸçÒˆ‹³ñ}›Ýˆþ?œã [–·ÁX» ª'¿„­Å,¶Ê)ûæ•áЮ™Ì¬X‹ÉzJÓ¹AÐñ£ eÄÉnIA˜²R†ÈQä\®*Ó—Kj0cÆ|hÐÊ›6O‘é½½BCŠã¡Òï ¯ñ ÷ö/fºiṓ ­ÿNü4ñDtõjZKG{ÞÅ1A´•ï*¯:YŸíd–|rÝãÁ¬o0oÎ= |r‘­êö¦—K@q¡½R(NÕÖ{Ó;g¦rç)-[·Š÷È}Ź0ùÓTgk4,åi£³ÉIhŠ«ÿcWpz[4yÿÝãÿãw”O#¡gõ‘g<JóÙ/ AƳL&£¯òQXÃ+÷ü$æmÎ ª¬ÇüeR!bî[xÉýÖu›ÎrÚ?~ƒÆª4è°fò®TÏ|[·s5=í¡N÷®ÇÓ=+©}j$æO¶ ñ7¡sAÆ?ÝóšFŸcQ·ó ý«[8õ1™ú9tëhb[&kiËúú¾âµ7˜¬ð–Ú¢~Üzw@ð˜A0þƒ}MüÐÓëØÅ£3èð9_Ψ®•|pNb"´iÙ piŸ º)ß1Ië8;V±äú8V^ô IÄ`Í¢‘ø÷h«s÷ÎÅÁ`Mº§è l${ ºÃ’‹nQƒž$Â+Ÿ:PªŠ;ÁOm)]ø6¼)ÃéêÐìéfÊò\qvçl»# o×Ù²iùɸXî>ɳ’ø×Dz¼ïàðªð«& |nªÒÊtÝV¤CZxÀn'Mn’¦¦7và#_zKŒæ¢—Æâô)WiJ îB"~±÷½ôƦ5°î€N³XÊÔ6H²Š°]¸¼@–)n¯¢¥HŸù$2ÛéÇqÂó©TåLZgÿD©ÛCv,¾J –ÞØ†ê FâIÀ²?‹IÇ’ËxÇNŒu‹Óˆè›àÚ7™v-¨‚÷Úu¼å½d†×{Ø:f7 *¹Ë]Ô2†„åÜ¡¹jTû™58­…t×y®+ä*œhw†¿N14Kª“4òÄèz«™Óø‹F5šç`É‚rX~O€½›—wX¢~æ)6-8#LJùéÇH~ü"›{ÝUFaàÁÛÜ»_5˜·”Þyl 3Ô‘Uü(¬Zc/ŸbèÊtþÙ¥ ŽUèœ2 >íy@v &±éLGy#ñøô™xùæ Pãm¸0W·Óh@ïÀ‘ø×{éãÙÄÕ˜—›Fç鯂ý÷IÔœM·%V˜qè•‚LWQ„ãÉŽanzêÌɪïÐ?Ùb†O¡€í.ºÐÓ „ûÏãÑw1ìí_5˜öÜ‚UJ˜p¿'KÂþÐ7°ìì ¼k _çÎ`zvÒÌA= w©ÝáÆjŽ¢õEXD Ë»+ÈîG¨²„†1üäE×`¢’LŠÍGš»wÝ;EVõ^ÆLãeLÂ<šÉÏÕdá׃à¥ê £ Iä'çåsœsz-/îfùJ°ö¦+ U÷À½JQØês Ò4޲ Öt(»×¥œDW½›¸˜ÿßœÌU/ë¯B|yû‘—Â_c%ñFâAX–ü5«’¡È! $kFôÏ“t(‹'ëÿ娱Ò)¬q+ã +7ƒÝÖ7üÔNÄf ]úG­ >9ÓÀœ› —[Hbk­éìiG9æ£Ë `¥f&âa«p<#¾S°h»>UV¥±IÒ%Æ”r¶2mìñ2!¦ð6­ü‹JÊ´Uó=¾“Ýþ-)ˆ!%Øe‡“ Lè7"Îzæ9±À€LüTyˆü´¹€I×Bé×'6l¿‡*§°ô$6îœÁ&¹&ƒiÌc< äA.t²R°„Ëß°€í §n=óéÔ.Mjl:Åé~‡ÓÅɽLÀI¾XU»íï?\wCÏìùš5°±y%Ó˜$ MÁGy›—Á„¹¶Àa<ðgö¡ Ý}-i¸æu ßÈ}oð@ =îÕJ\ƒcé©\´U³Ø¯X~ 9¿² Ü{¹Ðkå \z¬z¿ þfü£âé¢Ôû±ÝS’ÎIÉ.`ÿ¯—f Ž·ˆÎ{Pîd‚Ï[‰«ùNÜ:‰|Ã;—oÎçˆS¿<.|ÕKÞ}G'ø*8‡&UãË56¡½vŸ€w™rljºÎsÝ_«9ìÏ‹!‹cÀ*Nƒ&õ§Ãíáø _ê¿õÌw÷¦â#5{øÝ }O ±>ƒŠ(ÒeEšì|ÊØU±”]¨YLoœpâÏWkÖÔAÀ!gt¿¦CîSÀ8Ï<\#¡O¯ÊèÒÙ±iÝ_|>[ŠMôý·Î‚‹_W_ЖøwŒäçwüÒ‰;u“”Êq®t n/Ñ´»¦äÎCJµÙY\Mò‚ú8‹ÃÀ;²5ŸÖcÖzxýþ›§^¥#̸i°2~û61 Ûš= Ð) Üûޣġ*=ì@·iß$¯‰bœÂEœVéEë¿+²më™ý6•‘úÏ3¦ÚÁz^<žØcOsµéºYªn»—5 ÍaËåGÑ—ŠéôR¯ÍZð>;O$W{¶·z,K5±a‹ÿiÛ'“1*Öž™NœJ}z€¿|Òèc{3›™>×…œ×æð¸F™ÌÒÒecãMp¾¸7îÏ?ï;É…Ý??÷~kÀ;»YMo =ý‘¶Úcœw³„®>3<ÆÍlm®mÖzHÜ ,©âÑålõÝG¼™Såw¹ bxÎZ¶ÿŸ/œ%9‹ÈyÐÌ¥%pðÔæ¸î*¼*ƒØEë *j p®åšŽÿoþUöàAÉZuùFÌiž$ DÍ‚»$ô… ¹¾Ž¾ð²¦!Óž@ùœ)0T·ëš/Cäx6¼&Œ—‰ç4®Á…ígPz‹7 œFé«'»©œG4NxèÀ*D@y£*³Ú¹Ÿiëž$ÓE>@ÿ<;–:½B‹ÁÍT¥•‚¡š¤ÚW„+!'ýhŠ+±bÇl+0e”H®ËaöÛ•¨AÒ6fºaºÁ{îºôdïwÓ+3ƒè¶Jo¶r“ž7]C/Vñoðeéé¹lÇ™xhT Ú]tF–?Ìè[ø¯F÷±÷?÷à·œ:ï NÇ#£Ä¸4¬'Ë^v£õXèéøMÊKÒâÒпCŒ6›½…‡ÃaÉ…"® mVÞ¨E«œš`ò›8"“v²”=èT!-PKP¤3nn#-Ëœ¸Ñ+aæ‹,îSïX¾“þBP}žœžçOoX ²¥=qãøí”l½MzµAI¼S=°ß<ÍÀ-^~¤É| ÛµÚå+d˜ã’ð"Þ¾'O¦Á\øšÝ8;1 iñ…·¡šѨoé&20þZ:»ŒÁW¡S"Iîô¿°·P' û³x¹àòݘí]É?i½˜Y®íuø*¶ìüÇÕÏH²XnìA-¿öÿ‹Ê¶Kò*ž.YL¯¬QC#y xÁ'!~†ÜƒEä{×7¨›} –ÖoBóm÷¨Ö5e|5u n>°]q<…u}<,ø ¯K?À³QÔ¼ÿ¤YìƒK®òìÃØ?d×õ{X8ó0Ä—0áñrœÑY'f°a/æF 9ŸZŽkcXÀµ“´ïÃðß:>Α!ÍÇ;¹Üõ|x»v ì?Ø—›ò¸ ÿÛÿòŒyß2ò ‘¶>$'PÍÝd|Ÿ í0H&´%˜ÝÚ…`XCêÚëpÊ›ô ý§‰ƒŸañÅ -?9Õ#«°ûÇ3Nè =ÎÝfÍ<ï ë#¤nÓ îfûh¶XüìJðÀcS•iCœ9øm‰€¸”’ÑÃß8›_¯0x¼0—'@Åœókæü-&º!åðl”9fiÏ V#âC._ ïm%©§v,ÑQØñ¨Eò øž}ã™ÓºB\vN–¹d”à±Yâ¸u¯*+Ýø’KM#4Ûö lñ!ëê…ik¦,øO' {ÞŒÌõXÞ!߿ǒ_³&€z£ ;IbÓ¢¥(ñN™pòu80ú3?%|*(=íªþ÷} ¼Ê?,½€“ˆÅ·Í*øx~*ºxIÐxy¦§°leN`ÈUðÛ˾ªÂVZ6ÛWVaófï†eí!ÿÍ8CçÁ æ±þ;œø“E6–ÚÐgÈÖXËœ<Å躯F¬âOè+±mѳm=Yò£ åËÜiD“#Wss—l¡ò)/q"ÿ2è}Ï­–LÇü> úõ•|²^Cyžs(Q¿MŠo.ÆÓ"-PÓgLO&™p»[sOB¢‘-Éÿ•`nÌ5-Òÿæ,®¿‡»Õ(_§™Ì^ñTlú¹ÕÉè²ÌŽì{GÎÊÓ|ñ 8pï¹ÿî$Ô¼C«Ÿÿ‚^C%èÊ… ãŸÀw‰tl±ýÄKþ8æhÔCÝ“Rnû†zÜ¥êÌl¥®€$$ò\™2£|ЄÂ1¬¢Éˆ÷~Ó •$ĤŸñ`É+ j²ü z8Ncå*xÄÀ—⾪ªÑ§·Té>óx,¯W£ßTåèÌ»Píò búø-q GQ€Imäí#¯A½ÐšºÅoŸ ëØÑÐ}©”*]‹¯RgŽàu`?F&"¶Æƒxy‘.óÞ.LEÏÂ%ô™|—ØyA6V¼&ó¢V¸n#6O;œóŸßN<÷‚œøÝÌϪðb9¬WæL+BÈÒ#)ÜŽ¯nØb³˜s²f €ÆÅ¢tãÒgœúŸ\Ÿs¬’ŽÂõÝÍÜëéœ]Z2v‹XÁ¡±è|-®×½ÂS?n¡Z¾!àŽ@ä‰_àVþáA$táè¨5VwÌvLGÕ«=(~rΜk‡–~þ'V—ø[¡ÈæoRB…m¹Ü¨ê¥QÈ83u>Œä,¶ÊÑý¶sGú°S —(“ׂ]aóäGè7øŽT'¨0ë±·qç,>~1Pû§yT±ýt^¾w¿&û½ûêr¼vXã„×;a.?Ÿ(Ýû`zrlÅÛq4ˆÆÍ«±iÞEÌŸÝÏtÀEEaõÅͼMáÁ(7ÍŸrQOPpÔP¸qÍ–¼æ–Ü8§›FÑY›Y‡áVÒ²[„;ÙãŸ^àLNáÊ¥×C‰éœ­\z—4[²î8¡Tº‹,Éû1YÜy¿¯ä$U‡ÅÖTaç{Nk² »®WSÆÚ'm¿ÈÙ7ã³ x‡Öjàw|nÅÙŽ á~=‘à>¿ ·ùÓÑý‚OõÒC¸ê×ógÆb•¹-7.ÑÿîÓ£ëåTÙa&Ë>†óáÇ·yt¦ö+ ¸-Ï‚7H¡§˜Ì5Ë#£¾!d–ëÀÄ1SðÊ·¹ðFꥃ¦’78ù¶"7ý­ _/ÀÇYP'Om‡Äh+l6¦G[ùËäT¨_},粿H¢ƒßtMÚ|§Ë’cqRõ ºêç°ºµ‚Zy©ãÌQ+ÉÛ¸XzS–¼†¦j¬Hé5øw ržÙÃ2qÞU…¿xÒLZ_q`¦/\Fò¿0§UÖÉ‘¬bK6_|,t½›©ÿÚúdÛ©[»-ßH¾k7vp?õš¥O’ù˜zE‚ú…}†¦nä'v‘µÕ¥àug:Û*†­²d_¢4«›u™kÌ¿ÄBUé·éJÔ$P”WVâC¿Ylâè‹ßë¿ÊZŸJ‡.­ïÐÛÓc¯§ƒ¶Vyòì쀛Ma³M¼5)æ’Cˆ=ÝÀYjñ’b~ šÌ¡=F‚©=pü×C¦3‡»uøxÈ“9œ ¦ŸýY8o/ã†ÄXÒ›ìØc÷IR¸?}ÿ_K9|*èöFÇùšqóÈ,>°çNÓ1Nux$;Òs›y'è@éÓ|®¼2Ÿ <€®Ö¬±ê/¹ùm4^²2‡cOÂ}‘·<ßõ¸dè<Œ:•ub0¡³Vèòáp§ḣs÷Tà˜N>ËM—ãÆÑý Ià­G·zɱ Ú =V|ç*ÐÉåk°<ù®‰è¢ö -?”Áú7{Ñàþeþºä}x…‹æMí†Ëy¹p«õ‘[^Ìßœ c%ñÊ€ÂÇ9KÕ4¼ò] j¾ŠÓt=OÎø›ßJ@­e˸ˆ·“þ7ÿhªN–¸«çïØ‡·ŽÇ Gñõ¦W\’ø5ud"òš 1TB‹”ÞçäÇÐus<”pòKN’nßsœ™:óDVó·'Ì„äÍædÏÂU?n#3•ÞH~HJ}’ðÍ’j´ú,ÀîÛØÁž7?ÀiE(á[[QñÚë°¡ÃK­šÆI„¦¢÷°:é|Å[#Ìr~~é>¤üóØ—YŒëÆ-ÃövDl>áfúæAŠˆ:‰’Â’ø2Þ ©I4tÚ Á¹®ƒ8êþO2ïLJ…Ëß¡£ÎJ Ü ‚×¾ ±þœÿýÿƒÝŒ›}ç/ÿÆû~ø”¶ /ʾ&&š«ˆ„¾'ìØ0ß*Ç¡ÜïÜ-6ðAš*où º}ö´v —m¸÷¼[Ž«Ý|€Tµ½‚1Êa§õ_|«õ“Ü¿Yç¹++Fã훢dÙúÑpvƒ&æ™ë“4kÌ `’K×p³U4©®_É(€í“ÝÙ’š}ÈiåÃïO¨ž8¶ÞŒ…ÍnshòŠ0<>•½=¡^f÷48 †ý$HäG5&ÉE‚iH_M…"òÿ­…»Þ¼X¢¾g‰_·iúEw9Y¦ß™N.ãY("×FžŒ>•͹eË«bs6ow¼OüHÚVÕã$ êè©„|™ë°G»Œ§çÕÀçZ3ˆ…ÇtÒâêМ§ CË÷qR†iÜñîð–“eÓK¡r´'XÕ:c´AN?‘*?Å•\ îë·aïÛ˜ÛÛ‚ìgoãyÀ÷ûx^ý,jOÿÌ­)] ¢[`žÎ34Ñ=Íy(Äbcé"êP}ž[ýH9>kAÇÕñ’¹&oG÷löäp¨\!†¼PçÙ{ŒP~„g>’ }yjÖÀfäÛÐiqr‰'ƒ&æÃ\ŽfG€áí7#üߘLšk3Qî—×Ñy‡¤ª•ʃ\ÕοœCG1W aa—!IÚ¯Þ‚mëLȾk^óòŸ$Š—q+òNžÆ³™KPÞ= ¶¹‰—wœF»SPÜÖÌÜ ÷Ô 5³Œ£ÒK¡”ñQ¦^<êBfͱ‡7J!¬ñ>öÈ=Í6.Â:Ìc+øæ8é)J/wCšG\–û@xèT¦-8Šžy÷ ßå1à/S%G¿·rFóÕ!À›C߬J\þê%l®¿ƒ:W‚Ç6¸“5þ›^yμÚ1+Mf„ÿþئ;hõUÄ }ögžÕ§¹¢•O@æT*ô uãóÎÏØG¯±´sícâ·Xòÿ¾%KƒBÑy˜<—¿~×RPÌ}~5ÑPýJ÷ÀƸTÒÑÂMp²¦¹Ùòغ¡•7U<žˆ?,á]ç­fÂé']'%ÝŒÅÑg8½ÖÿûûgåZþ g>X‰A]™öÞç0Í·´ÂpÚ®%X³@}9þ‡[xÆÓ=Å1«åHd¨*‹q¸Žç†P»_ƒËä =Ÿº8Q©²8ì~åFnüƒw€Ì^íÈý]á a?áð¤1ä¥Ó<º•ØÇUcèTu]¨P< ó_B^²h ˆ?Á…Op¥·2A×s?ü™q“¯s+¦Ò1«÷¡îŠ" G¿uÑi8`i‚êKM ,¾‚sl;L¤«ÇpoOõðt\Ÿsím,ÜA°Ý TK­èÔ»ÁxsZ=öˆJr'ŸŽÔÅÛ5°'eº4?„Ô¡TŠ¿;áÀ žFæV®¶j<^2¤’çyÔÁRž`púz¨Ð¿‰4àõdm¿8­½›»ôÙœXoƒeG¼Q#s¨&ÙÉv?•À-Kžð.Í“¡>yèmlC9¿Hö­fÜus¦½:Âý˜…¯òwZ›¡ÙŠ?>ÙKâJ£×æFsÎY^0cænô‘käm‰cÍŽò†¤•¡Æû¯OÌŽe=X…ü‹¸(rˆ„ÉJ­×œöƒð^R³B_bZé3œW'JóbµaÏbuzz¬*½³eöÿ£mlµè‡«Šû6Öàû·R4g5} ¿«µr»{L¹êyòXi2‘ýøÂòÂöÒ™FºÌÁj[åµöe%Ñ$Ý |;o& q>>>øN ߉eͺ¹µÚRt® Ûõð ^‹Ê­fñÇð¯÷*¶2ŠMoY‰<м”Ýø¦Æ€½Ê;n· Òz:8XÃï=ñLð•%)²‘§_l¾{tNµ³£1Mê Ô¬/·—k™°æ]H¸¼‡\s³%KG¹³e'/ñ=®YÓÔápiâ(öäI;ÜŠÆ%¯ `‹þ]ò}·÷Hþ(›qÍÛWAã”\8y¯VäqŠ/õ™Dž0L›ó “žƒÕÇ’ï>žÚ`ÖÒƒôpï)ÔvdulؤZQ&­P޲ÓaÍzK:óËy‡¨yßpþíRŽ-S y¥Nt‰¨½ÛêLª+BД@x›Õ Fýûÿ›c&¥vÛ-1œ]–4.÷£&6ÀÅQG!úRËÖG-!ºÄ˜Žµ™À°”—³Q>e/ºn Óf'Öô× ÐÍ ò¡â´ W÷åüREç—Ê=ÐdÑ]xK N¨¾'…¡†ø~Ê+N¿ç:'°UcDÿ=ÓwåGfÍGϲ㞠;hÇa› ¿1a$}]€f¿ˆ®¸/å+þÀ£ž¡ÂÊCÜæ“&l -Á)é’M+àù´ß¸òͰMšÓ( -ûÒÇ-˜në>Xÿkûë]&VçÁ-å JÅ.Àê(/“ˆrB0Æ/ÿTj<‘úŠ*;“¹uEøÒ)¿öÝoÒbÁc—ÚÑî axd2L+Qd‹Ô!wð‘ƒÎBaºa—;¹¨ NK·Esý‡%™«°.;dº·þõ¤Üß“LËm0Ó…àâ®T G¬Ý«`\a.:úwŒÔ¿ÄÎØ ¯(0ÑÍR¬±ð>·èˆ¦ý«·+Ø¢˜—ÜOÅI°±'Öœcsîƒ1SòIj ŸÜl}þBӘݕíÌ4S”jÌÒeþmXG›7×0ðßþ®‘ø0çYJÛ·1ïÚàz˜5ëŽRa¿Ã=Ð~þ/ìÐÐå[o¡y"ÙÜo½ý úØ€Ù”ßíu¸BwdJNãö~´f_Ë×°g'ºÑ!ÔÉ(ÑŸâw¸ý¾ì|¡øq ^Oey‘[è Ý2ĸ7H¿Å_ñjìúÙDè[¢•‘ìõ'_øý´ ZKÒàÍ3Ñ‘üﺖIÎ qÒ–— )Þš~Ñ•¢³]wð¶ÇuðÕ~~‚ë{[0>èXÊ@­ÏâìcnFˆØÐDÏ*ÔÝû3¼BTævpi#fF1óÕðtÖ#íaJý«iÏ ¶;ô6K  ÈÙîNO´£³ÙädË>»Aß óâlÕYûó[ñë:gìãÛ¬ŸÄ•s8(¹KRØÅí„xú[¼^ò§o¿ˆë”ðÍw ø’aÉ‚Ö&²{L7}.Á³XÃue:Æø ¥ÁÁ,WЃÌ8ÄñŽ6ù1½\MâÐ%äðÿñ§Æ%ÀÆ‚ÑÐn0‹‰u¥™~o¹ÿf…WÿH‡ OMè•Gœ‚4„ÔÅ`àò¯ð~[>D^DÇð}¨Ô3¶•‡³F¿ñp7HœÏÀÝK[‰¶Pçé( žw#‘§=yIáthá5[È?ï ãåj=@*¢®Á¾`5üw-ž<—‰ß“àÁ„&|” Fá»·æ)¬•ÿER’ÒñDK9\­u‡ÔG‡xÍÂ… W0†„®;¦dâ“•%üÝ IwÍlᤒpÓ¥_cëŒÝ8êÔSœµå­XrçÅô¡ã$ìª+ÑòtÑß—ÀNÿxrwÛoØÓeë ÂŸizþQÀ7ü랎μfœMfÔÓô<ÏÑm{¼ÃRw¾èõg ñ~/>ú=¿ÅÃÀØ6’UvÆÀÐM¶í˜ŠB—^eŒÅ¿ŠñÙZÂvÞ'©Éåœr•s—Þ€ƒ§ð†ÝüZ;–®Þ4Þm[‡wwjx6>ÿµ &ök;È÷šPïDm”ß›.Yr—ukJ’æ²èê°4î|Mâš šÐÏÛÔ±whU5tf¨ÐM ìøû#ðA¦«;¾£HÜ$ŒŸs7&2ËÍÿ{þ}E7ƒ™«qjÀÎ|N8³ßt rÎé±q÷²H†Ð0 :sŠ«_4‹®T¾Éµè$ÁÑ>½mŽÕX¤„õÞ-¸;å7ãÎQÔœ~õŽ)ÒÔÉ£©â®µ¸bO޲ö„÷Ûødž5Ú¾!÷>ýÄ µN }8‰_Åë=·«àØ÷ gL™ sêÏÌ—ôKŠ!¿ÿü$^¸k'©JýÂq}‡qFE<ë÷€M‘µxnn çH.ƒÊ [0€·Þy6_mG÷˜WÀþ ºCÎõË-QæÅ!¼ºG‰F7T¡~ýd6Ðøö÷Èðß~_8ÙKôzø[ªù·& ñ -îâ±)ÉØz{2¨ÔT£rñY¸µœ‡KÇBñ•8¸mº†–¹Ý׿»°ðÌqþ½\c,U¶f®Ó.ão ØÃyTn†ñ¦è3œŸ^æ°sj7g¡Ÿ‰©uG±Ëi,–éSÍ! ´Ú ª‹A@~1ðfê‚ •㺌Øö‹Çp÷ýjðÔzYò‡8búõogãNǤGg ži"g|e5ä-ä’§ÐYûîÁ¦åÐQ.<Íéº`¶üÇI¨¾½–uš²'þxÉ {îÏ£™e3Fø/}ÁlhWì¿ܤð88xwœkþ€6J¿È¥Ïö¸‰ªs½'íØóÈ1hvÅ‚²]j|4‰±Q$[´áÝ2æ–wŒ³xžŠòs×Ñ¥£—q¡zx5×ä²}¦¢éYM¼ç4_Ïi$;OòÖÀmgœQM¾‡ÄII±£ó/Á—Õ0Ñ¥ŸX]Êàù,vb³`Á>¢mDc âaÔÎtœ\st{]#ãO °Ã}äæNô0ª),ÈÈÇHY™ ÏóÐaÅÀxÆûhƒ‰ÿx&Ñëâg ¼B'Uø/òÅi¤÷Þ‘ø÷äs÷£E¡Ïý ˆÈ0•gøŸÄ›à˜ñub_®ƒZ"ÄIÕ”1³½8íV#Z«ØÃñ#å¨j<KÏ·pêMôòÄ2žˆø6´e%U²hBã—9ìËÍk¨øXLWÐ…¼Û(Ï×¢aLÁw1µ> RâØµc'{´:µV",U8 ‡ž¥Ãq…½hœ)„wú͇/bäª/Áˆ3 Wä çºÜ95™.Ù²7ž‰æßnÓf~­UX,튽‘²\“¸,]Ð=±q°héb/^ˆ?}i{T‹KlÅyÿ³îæ÷ý»~Ækâ´Eü&п[q]Mù̫Ԟ°£§¿{ÀË›B,ã’=üùÞŒb–ÓFSõ›sQÖ1€Ûqí)}ŽºXy1¹Û™Ì óqêžÅ‚ç•€<¹ +L2þ;†°ìs4ëç"ú28ü,‚©âFÐ3«Ãò{ר³`€¤±ìµî´2¢–.n„áKcèͧÔàÏ!ô—…ñÐ!W‘tYÆíj C™’l&žƒàö­P%…Æ'¼ù}yø°[‰}v4¢ó?„±ü‚-Ó»jNRFÃôؾ_ÉSëj*~èðY…‘ø¯ ‡çq¹Ã(”6ÂP›f¨ß= Ž×ìÅE:'ð…s/C> ³'BÎÏaâ?ê éˆGÓÛ@­B€žïµ¦ ·eèO½Ó\çÖk8½HˆÖ‹ýáÏO2ÀyûQS} »f¿ VkŒÇí•?°Pº_g×sÞGàŸ¢·(;¯ï»Eµø‡Ø›fȧ øé†Ï¯ŽÀT¿ËPOh h‚Ýjƒ|7cöï⛥¯ùnéïáaÅžé¯K@øàÒ BâL=áô‚=´ÝYz>Àh‰N¬§ÍM¹É`Êí¥#ý¯c({1+ÅÚ€'²#\n'ð–$´¥±&ã`ÖƒU+2ù7’¾Åûæšèß7ùzŠ÷0T8ÿÐUú_`ÉwI\j[ ÁÂá°töq(ö½=Š_ᙂíËø‰~uFÌlX‚–GPú(´ó_¤0þ%šÝGÊö߇…K`9ß–Ëû;! _Êøñ3§ÓøŸrì÷Ž›À/–£z‘VÔï­!üwyÁ$¾žßR¢h?ý¹1¬ÞÔ êîÜä./Ä„ÿtæ¢ ’Mw/<ä`*+@ï6 ê³Çüz†´pòv¢$<Òÿ¼ÇÍÃæ?Ð|ZHÆ÷ä‚tZßâ±b«·µcªM.x ’(ývä—xáìG\^Ä4à妎=Þ¨}†[6‚KQyáu¬‡h{Ô]þ¬¯:ìHÔXL^{–éenNQR” ›Í…'§Ç@Ç£ˆ¸Ø‰±áìÌ Ö†ý0ðHg•ƒÄ͆ôéäçà~fyxï2oïƒ4Þ´–gð8ô¯æËkH<FÅüV¾ÑÀhÉ8¨w•d_~RòUþ<|— ï?K²o^Ídpãö¨„Ye’¬qFÄ,M€ŽŒ+#ù/ÖyÀdz c™“^2}=óxðDZK‹Í¨Âª³¸gÝ *TF $¼—qYeéÜe”×ëËÚšÇÓ•f›©‡éYH¸iÁÎN†î´W¬Œ¾zÍc£¾RiÙ>(9‘_ÀDךEZäwö'Ú̬§k‡JñÕðb[==”¾Wí"ÎUtÑjé¤Ëêcg±U¹Ôh¹8s{“"; àÒK2yÎVûûÐ#ûX2™…/Lb!nL×Uö{Ñ–ûM]ZyÐ2f/“Ý’Ë~«³»v®,äï<Ö´&þ»×HþßòÉÄ+f™Ø6ƉÕfŸbù÷‚°«ß ÆU/`KMgÒÏ "è³'ÒÌolÕÜ –uU¸bën˜òM™õ–kÓ‚ÇéÖ7þx7g?îØ3š]úz ··{1‘«[éÞ=EdÆ¢TBr,[õÕÊ¢å)롲Û_c¡^ ¹µÀŠþû,|<àÎ֤̃Éó÷³ê%Qx¶Á‘ E¬îÏ$jðJ™m0£†¶±£ûÌèç2ìê o¬í;†4dȲû}œÿ™tú5›ŠÖš‚ž'¾Ðª7ä°Æ 0ª›>„“¿ã¿óµhü´×pÏR™ºTà;®‡ÇuFƒù«¹ö—É>Ó„ýܧI—™øë§¹5ïÿ tÔsHÛ^ ·À’´8üq® —þçÙ·D;jQ÷.U‰‚Þq6SNf;ýÆ66œøn/¶ôŒ¾ž¾ž9»Ð»:8?ë |<öˆÌ*\ zž âªÍÄØq4o$DzC³¡ÖÃÉ5F³ŽŠ`³ÏŒ÷í‘ m"wãìJ¬`þÎåÚ9è.í㤎Ïf¦¾ p݉1Là´t4G±ütðnïóøÆÌÁ²ò­Œwj'Ì4ûÊOÙ³‡ŒVz =A#õŸhñ/~Ím“aàW¹,^HcÆmÁyC¨´þT¯t¥ÿÍ+GíN†ó„ÑÄ~.{¼x)&eb{¶·tY;l§Ø[¿™^¯ÃlοÐ9s6‹ÙÔ«ü…Ùþn h(R攪´é›SŽL~‰!¤´;±óʸ&'@h ë×…UÿüœdCm_÷{ÞÞÅæ/Ë™]„§ƒˆ=s@ÍÛ“ÊäF•8êÏLs¤/ß¹óü³uÁmIWÕ=’ÿ¡·cÔòXÎ}¼954¡J.èºIŠÍèoÂsÇá£MBìñ£QŒ´lBÇ$UúÊ&ºgQ¹Œ× ­&‚»Þu0ÙTBŽOe÷”ï@ÛoWV)v†ºŠM‹éÝÔ"&¼—‹ôK –¦Š¬ì~ͬ>CùŸ¤±úG+\ Nó:~,z’>sªùÅý1Ȧ¶{ž‚Õx_nžÇ ö(ø<'BËEoRs…Ó¼í™ôzŽ,¤8ÙQéÿtµâmnŠÅ":¶@—¥΢:+!«>–¤NNcÎÜw<ÃÒèÓn4ëì6+š}ÿ}f$þò?â½!nÆÅ>®3e2\²hÁ¦g*l¨é:ˆæíâ&k¬ãê 0‹Èß©áÖtZ`Ë]Läó'í…Kgv3•Éõ w@ŠËäñldiÑN{jÍŒ½KÚ/NûåDi‘]ðSþ›SNJ^ ùjB r#œ–‚ß¿¼¸óz"UìO ÌŸ¯;Ì•FOËçPÿW„:,²Ä.Þ°Mò*?·ØžãªK¡ö‚5]ÞK5N`qÓŠðý¦ß„7M–öÌÇÇâ­…¢ä¿yë¿¥œÙ-?ž”š>ŽïÆ{be°ç̘ü‡øH¶(Ãb)zG,ÅùŸañó4?O™%¯9NW¿zH®)Ý…_+8·ÚôpÇD:ëU `d×¼%ñm·=åêš²[Ÿ&2G¶Dc?Ý_;êê7QyµMts‡/“»’Ä)^2àv,ý…¿^ßâ]N-MEˆÞáŠã„¼X1L)gä‰í%ñ+išõJ ÒËEÓÈ¥yçÑ`ÍsðwT‚I+ é¦ FôÜ&#êrpõp;Ï9ˆÇ §«©Þšo«I|Ͼš¶.aæ ›÷ß^Ö,úåL:Ñ-¾ßêÂ}õåðëxÊþä„Z¸x5Ë[[È}qWô6º Â{ÕØ›&)&¿_VÝwäÿžâïÚÀavZ)XçËÑlš†—IÓ˼|ÛI°úÒ –d®ƒ„G,¹vè¹™QmÍnL2ƒž tèÛ…ù|"Ú‘£¹³+cÈŒ¯£¸YaƒdWá8foO s»—£|K3uYò±Aî‡0‚Ÿ¡ Õ”`š+u#Ieôÿcê½Ã¹|ßÿq#›d“-‘ˆRf<ïó’¤T¤¨(‘v©”–½g² e–•Ïû¼Hh(-••JC“vß^Ÿãøyÿþp\ǽÏs=÷u>O™Lï ’Tøæ…t~1¢qà¸õ™-i0op:Û ®a;Ï¢eì0ðn `ç‡ “—*:ô¤{S˜ƒíû˜×•¶¨øÃ„Ýø^v"þòúaÇé>öšÑ–:­åŠ$°§Ö±’G‚ØÆ×¹ÌÃpYò1-t3FAÞì3¶yˆ4uõƒ-w˜¹¯hF·k¤Ð·ñÎôÔäFèþA’š4ˆ×21¨;}ŒcU4€æpŸ±¥uñÂõJ^j}áIceTËŒôdÂ}w Ò-Éâ'à§‘¯”èû²s §BÄ~Úã´´$ü±‹´´Õ¨ÂöyàòR $¨MÞ(£óV‹LW¢R¶KÉŽƒòvåÎüG®×êã5 6ö=¸÷eÕ¬îr¶5/‡Uü”+÷Ÿ³’˜>nG®òžÀÏîˆÂú'³` _>?åAN¼×¡'6Ò·íçIYôÖY+íôåiLÏd<tÝÛÕ™ËóY0”ãÃûÖ“‰ö‚KêJƒ'[Ð+‹î0“žO"BëU%ºº¤ Þ¯¢'ƒLñ©%\jU%—kp¥ìdÚyL UW‚ohlðÞGÅ–áìÏs¡ì®5OÄ]Ê«!õFq2SÁ«D J›”-¿àÅs2g¿ ´þ*d8KÓ¼PúE@“o#{:°it¼q6´Ýr‡Y7ÂCVœ_Š—f’m·»Y•çMrÖùëÇ'ô¯ÄaÄ8©ï¨ñJ㮲ñ7bxŠ.=§ßĈÅm!ÏÛ iÆ1"î÷ ýÊCÝ­I¬ÎÔ¦3íhü‡vð{Œ½*y‚VÝ…ß”±% ùÙ«A½®«ÍÒ(FÁ`2 ÝåKTgÁ—Á«®[°ŽÈ×ͧw¯âú%5$±6”nùË|ʆ_“³ÐvúYH\'FëüO vy!ñMa;OãåToúÝä{²å|o?ŒŸß‹Ó–¸6\ynYx? »CE˜Vîdê;6†¯–Ðç s©Ÿ‹þt;‡?/\bÔ|WÒª Puߟ þ_žÉË×*ƒ²¾Q¾¨ƒ|‚X-@HÁÈ*öXÿ=´K= ..î¥!FåH-´8›‘ç.Â*W H¾m§³çRûæPøä{|œ9 ~P#f°`éuÜ÷»CèN6Ãæ1zVmþÇ㟠ֻ…tmÀS,Ô{øwŠÓ$çŽ«ê ˆZAý+r,¥±>baZ‚ÿõÈèÑÕfZxCSš¶„ËΜ*ògï½Ï‚õ‹ü üÍ|o倥`‚«"£éæÉyijzfä¾Â÷$u”éʃ`˽/èÅãÌlá³`T«Ázƒ# n"AŒ£.£ „À¿z(ïµÕPððæôEQºXF•ü7'úeÖ.|ó•¹Q«G'/DZ°\\r‚Œ-r_Í{Ø>»„û]Áø±sìcäÎ|€‘Wbÿ›9 ',\ñ&D÷þƪrsh||ܰÿÏíŽ ÍFàâ4uX±Ë•åÊ^j|IÜ:ôñù gò¡Wd6sæúEÐ]Š÷„âOó…»e¶¶.¢üŒ{kš9¹¨žIwýŒj¼Š®‚¯ÓG}9kƒŠOÿts%“çè‡E<lÐ*ÚÿÝ ;fß…Oóß³k … ‰“°ÜÛ“&Ø ‹C¨¯t%3\‹óC0¾ô9¼9¹Š-d @¢»{ø-aKöuÌ03!œ)údý’¼ívº¹}˜¨ 'Ï”¢‡pŽ·+ÐÁ¡³¤˜£ÍönÎGóYŸpQúmäK:ÅÞ~k;aÿ×UŸØ'‡§€µµ±Y1ܘ)HöYî‚Ç¿O¢ŽÞÎQÂb¡M(ZhÁîM08~ ¢_›€@è'üÍG—²Âtdp&üœ=B1ÞÙ‰ZͲfŸ=¶¢(æ›V“„°¯ðØóìØ*~׌èãtO¬C)b©ÄOS–:#M2aïgâØ£ï°WbhÊœþWαú©hÜVÌR‘w«½Ù-N6¯Œ!²ú[ñ!]N7mÓúo¾3û{HV }¿}:µoÿƒj¿,é1¡ÅDýÄú-«ƒ” ?~n¯-ÄÆ™nO+°Çœm(äô‘™ý¸†NjÓ”Q5"#ÌÎ~I¶ó€ Æ^Uú#Ü‹0WŒè¸ëiôè?Ïhž0&‹Rp¯r.³{½í©ý‚‘ƒYlÕMlJ´»Û0ä"H»B6àñë)à俚¹/…ïùNb˃\šØŠƒ;åY"X¢“÷òÒaèï,sNåÜØÍÈŽsH]G9£¯aM?€÷§€Œ@üRXÆlbqj.=²£-ìÕƒSøü_ÜDb¬÷Ÿˆ’ÀÍEèUrŽq[ òö¬¸f×ô;fw6o ¨¯³g“×ÕLô¿_[ûš½s]‡ý&àÈþ739×·v}Öáè…Å0®+ëØØËÐc²‰îö°¦9O¥iOÄçDz2Óä°êÊîõü×€º"ÐÖî û²ßæëGÑ öܘÅCf¾¸ÍlÞoGvÞ‚‹ ‹Ápø òkñ16? HÓÑdüøz· è÷VJO{ Â{›"ÀýØJæŽúo«|Ø^¶IP„nó*Äc!Ö Ð!M嫊ʾ‡—ì®™Sé y†ßÎæ?OþÀG܈" |VWó*Ù)êeøyäwc” |7Ê!넉ú÷Ëý3/ð×p(æ­:Ï^!¼‚ÝÌÊ9j?váîvÖÇÆ ›ìYíÓã\ݽwÑw«?ž‰-…UQé¬ðW7¬øíŠW›}ÐKYÛ··3A¿ïC9w) î^ y›@ä[³A¸)Ñ‘Ñ×PôÀæ;óbQÐ5üP-I V΢ÏyÄ1Ò——e’™Ëm§Ñ0œ¥`DúŸg ðÀ)n…ÊvúÈa;f¿ŒÖQÈ„;ÿgÖÿ€6濃òƬ¾ó‡}üé^SšÃHIrñ³E''øýIŒU—'›½eiUG%+ú×sÂÿHÏÀ¾u¶Ì^qRvqÌún´ ¾q›LöªêþÓñ “UlºÛWèK~Âùm˜Ù7›ºŠw—P,\;yí!„_¾ËEZä™Î½”Ý‹•/ãçË7Á[Îo(† ‚¢d¬v¦¥ÏK2Å{7(D—0šÙï6sþ­[‰d€pP'®: -ùÙÅ5g裥îTís%£ÿÙ¾{ZþXÿ˜á·OF­Ø;,}FÙËg‚Nª!–EåÁÛu­V¹2ætPÚ‹|`ž`fÊdšL{Gbv¼Ñù¤ƒ«N¡Ï_gT.åþüÀ;‘ÿ¢ZmðÐÕi$p]¼J™ž0¿óÎ[€™‡‘+ã IüÒÞ²ób×ÐUá…êy ÿ™‰â¦¹°õS¬ëЧ¼§:9Ï]Äÿi÷6˜--»)9¾œŠ._ôßLfVxõ&†§|„)Ëæ€wZô£|Î'xQÊrW~‚ƒw=pÒFe8@¨RBd¸Õ1yYêøßµÎ¯1 eÓ©ÏKòs0ŬÍIõõ5ôÃÚðÒï/ÜXSNø•Spí Nbl'3ÛÓŠŒMÒ&&UãÚÍFsK öò Øb°fÐ!}…sȼ´ ü·žLå )^ÇCé.Ž-¹ûŒö¢â¥xÕè+Søå:ü‘fM>n`B?Œâ’xH] yQòÎ=îƒ)ðãCšM¯·còZ}:{§:É“Eö”°oŸ¹Rãßìø˜ðq³pǤ‹è$ñ“ïÝÆˆù5 õâÌñwÇ"âÔò‡Û¿³ªÿ´ ?‰{+õ/7 ù=ëŽöydÈþ SPßǞܦWŸ™Òš„« ð$€Q\щY[BàǃröÛ†«œK*ð=ˆS‘¬»z ]–W²ÎÁJti€*ö³êLÏ£iúÏt¹΀‹Ü9øŒ»d‘ý^ÀK{srÊÍyð×ü$¦UJÐ:àòiè¾_Å&žÜë?Qö®¸"JÄÅWçaà§g¨Ù—Á5M×µžÓ¤Œì!´î»¦õ.ÜþkLÞŽ¦Cñ+ÔU'ÏFÖs0†&äžbùdõ9:/2}½«9Z›§Bʸ¶®ŽU-ƒÓëð€iž~r„?«£¦¶¨•ˆ«„áoûÁÏŸƒ6"f¨`s‰µ>êcXKŒ®wãèg¸Y妰̼ՉϧA ëž|Ì#L|< e6:A’ÛE” 3Å¥s_²NŒBK:‹±Ç/3â‡Ôiúíæ¦s&H'îe¼•%eOè?’„w´D™¹IØžg ç_Ä|}{ø€ÍÜë?Žü‹³éño=<™rªâ.[çƒjåäÖâl°ð Ú/×rI ÷¢ú'f•'Õ~Ѝ>÷Ü,¢¸[®‘UÔ5CEùý”döÌ&nÅIôwÔ˜ZÍf鈑âQ¨p"<ŸåÁøv 2øwq˜-µéXÃ<ÃDŽߩüWO”Ð|•.)Ëeïw§1˦‘ŠiÅÌê:;,‘@;Ó/@_Ï@]‰Rf[ .I"OçŬ„}æŠd¢Ý-gEŽï†È¦/V®šŸ9ý™0=Óž$¨R[cC(­V!mÌpÓ¹¶ãÄ57$Þ¾˜À?©½ëf2üÏâ=ï`rM&m{5}>B¯&©ühIÉï½X·ÒÞz©`Nö(&}7cV-Çšõñâmk•ü·ñ³·Ú~Ã;ÿWÜúï# ðã!»œüNvôïD®r—Õ<—“¿p}óä1jMzbþÂ¥À}à ¹þ|M¯Œ'ü¿P×Ï ÷1`ÝB”Í—cV¸]V¬E¶^^LvVâ€Ù«ÜЈBzMxç„ã a¶C.Öh×3‹§+½1æØ³0“g£¿˜ËY85~û»aï»!lL^‡%sG™ùŸ²°$¹vØ×€‹ŸS»>ËâìÓÙ0´I 4ùR¡Ýiøîy쮄f$ÿ M+>–»¤Ù%G³@Ïw*nìÐÀþ1’h¹‡´HᢳHËž>Ü\…­ý< Pàð¼U­=®}€Ëú1¾Û•h,  ±÷ì`ºÖ'¯k†þ•Qöx4gÖpý%¸Ä½û[úwÞ|[ËáÛ.þI˜L‘§þWÑ^"„œRU ×3ÃŒ=ÀsÒ7”õ9Eß´ E¾ñØø¶•—3·f$çýúÁJr9}­/@‹,5~;¯@ ú`Ò³WJˆîõPB>éòµm)ýüZ‘ˆùÒÕ1¤`íÄ ÁÕ#Uð£ë_~|%G­Øé„ÿþìÑŸKŽ~Lgħê²ë«ÃilB?<ÌjgŽÞΆ¥N™h¡\qK䉱Ÿ ™¢ÔOg×ÀÚczYÇ€ðòÑ›e–´ôU(ËUƒË±Ö þלušûÿq³¶ã¬–ízØ´ÈŽt§o£#µàjЦfÒDßjl8¯ ·‚Üèž7Òd— ?‰z§&[Déiÿîö©GÉW]W¼Ã”¼Ü_Ľ]=Lu…åˆÛËe$Á2´qÐI<Óˆ´ÓJâvtèg:c€f­&Iysq¿ô=”Kla<Â_ƒÔWm’,Xyåp‚š!OÓb™µ‡=Ïvi;³õ7_°2÷"Ð}“*ÙRçÛÐ]V«sÔ¸ø@††bûûw ºG£R$Qoÿ ÒµYý¿uØ{<ªbÅA?RõÿËÿÖ†U +³ø"šÆ=‚é ~sˆWÅ¥‘=~ÒÀïDÝêNÛiâäM{4è‹;iøêq\ºssFÛ¶¶ùÁ²0Mr±A›FV(oálšùÕ–;*’ÆzQò°E’~w+$6¢–d,ÖŽØÎXH÷za÷^ÿ®eä¿ÞågÅHØÙ4Vó$qß5iMù1jº"€Mµ¯B¾K³èz‹]8¿4bEâáÈØâ¹ÍûKÁ¹3ãv6CLe!nèúÀX /!¬ôº\öÌcQ`·0YÐvL"»Ö)P#ñ8XzæÓÄûÉuvl£¼ÖñvÉ‚tÃÎÇv¦Êõü^} GÎHS™9Ùw‹iX;ÀíwX—Zo´~|´Õv³”ÇãŸ~ÞŸû¸Øû<êå™{3ó±÷†)IT¾¦ƒHF%îyŸƒçÓpóYU²`Å8ÚñIþ~š=ÖcGßÞ¢rçBÁuÇfÊã7 `üкqd““ðŒ‰R|‰Ž«XvÎ!h ncƒóì{™<Ôó?Hý¯=ãè¿/ãÚ=ôëK!ÒªÒkýÊ_™~r2¡°w:ж.âZŸîFÑɬùe5¦²¨ K&“K›÷NÄ¿ñ¥TfÑ—V8)ÝúhõcKIí×1si=È%|dwÓ'ïŸÈÂïýÛè>‡8 ùs Sšg¯…Bìzñ<=NŽ ¨Aèe7X– Ì/ã\Ö‰î¤0s1æÕ¥5G-qOe2XΖ&ÍÝXàBÜË…ê9uì®ñpR›—Yvø9¶j ”ùm¬&—´ÕðãÞÓØQ¥G%ZêØuÈKyij¡!p{- ›ÔÏbáX$H³fL…ŽÙñDêÁ?Á€lxÀ{|£"ÛËØ™ÀÀNy:¼“—ŠÉ…À¼Ï'ðÛýbÏ#ã ›ˆŽö.öÝ·ËÜŒ¶¸ÒéGö'&ý—[‚k‘‡Èÿ‰Ä¾¬Ýl «L».Í¥Ún¼Ôîðøroê›Á[SU’n&InY™Ñ­;öàMÑ ˆ 5#šfÑc{»ð{üÞ ‡—öuø×r>;Mïeo@›ÎV,¼$éfIȬ/E‰»è’% |éD}ñÔ¾äGîÑe¸Jf2\”iƒ-s,ϩӨ³n°­ÿ9^kv›À_Ÿãë#\ k,\^ õ;’…Ͼ/#¹+¦¾éÿüÀׂØ[WbÍ‘ ¨>² ïœØÉ˜ÝÙŽí=Ÿ˜qÿ6 ::ާ:.ãŽù)ÌÂáIhøz%ý³ì"ݹfµ¾c<‡0NvhíR£Väcýb#Z¾(b¹!3ÈÁŠ?`7í×ÁÆë ¥Úºé<^&wæ–ÂÒà•MâÙ¢«\ÆÛ2Šñ¿OqZh.F8ÅDž06©»÷-¢3&ÇÐMâ •ñ»:·‹Ø6§ÉxˆjpTågu¼°CIœ.›Ã¸‡ˆâêGÆø÷Šê1g¿_D×Ô‹P>`•!é¼ÙÔÜ#‰º¿t&M/±‡7ôßçoIF“"èX/}6Ëz„²56ä›z:Ê“—Ê×ð®@6¯Y åÇŘ¯‰§YÛ¤BøÊ«DýöŸçÞ~yç¶ëÖœspìÐÚ>gQæ™N÷…²bãmÈ]ûL4WЦ’ À€XbU‰üÂWÿ«G!ò縧y®Üc¨o¨N"‚>y f°îVIÓ~?°Í+f¶_6 k­S‰f·Tïçÿcå0Ç.̰6ÀŒÔâò8‰ÈKŠMà_¤“Žôò:üà#o9—G‡àPÞoV¦^ VtO‡ÓIëÐ[è2$÷-ø—[?£Øhsˆe­Ú½pEéŒvh€Ã|Ää "^½üž}CÄa£õ(Êš¦cFÕ!ØgÒÂn÷‹bg^/ç ýÄ.ItÃ# Lí}=¸&x¯©Þ†¾õX[‹žÇ Á1mÑ96’ü{.¨c¿sVw·b×Kwêþ@…ÌÉ> j[V°ÚÆEX*ªA6o=Þ3£:1ÀWvîf^bdFw£Ä[ìÞ !‡óÁ2Y€ê¿¸ŠáN›i½y6N*™È“îú@fz(ÎŒü¯šÑÎÖ"i­®TÊý9û&Cåùâè½ãФoå;4òÁ{"¢´Þb'”%5`²qL6˜‡1vÑÀÑøV\“vgáŠsúzppo-BQž¸û—C·(¾Aá¥ï8ÆågÑØvè0Jäh†,5¹ö¸§áñö\(Ðü‚¿óÚ!Ú1Úu•Ø ù±xGá9ŠÙ¾×LÏ&UM&ûŽQä›iÜtöŒ ©™Dë´!Ù=‡=žÊtôEÒŸ{þbøÑÃT¤À‘!jÁôçð<6ºíèd¶c÷qkËuáøjÔ³Õ‡ØS¾ûAi’{õª+j‡>† ,9ÖEwà¹Åf˜·D’J‚*™›®ÂDT ³ê Do'Ö*¬ƒæÍøÑCÜr´‡»ç‘CŠ«ÿúP'™$X“¯FZÍO³¹³¯,@K¸Ï» Nl…U90"ÿö|\Æ(TZQ»e’®‚%7‚Ëœ0ÜŸügÏÏÏS`Þ¿ ”Ó¸Ò¼¹­‚Ù°5¥¸³>ŠÉyÊK & ›_/p²XÃ3ÐâéKvfg·Y_“‰±=†Mæ¿0÷±^í©†ð"d~ê:|£ß6¿x›k˜u–}g!F´U5ò²;€^²—§»zÒÈ*Õ4Øn¡LïÆÞfúÓO‚qJ0½ÙÁ1|D4ÖªAû²Vœ¹8.Å;î“6çä}z^,¦!qieÜLÖÜlü>)FÐá0 ;‚MF è•1™;ãDwÿ¬BÖ±d/»ÐØ/QÜ—N¶¤T³˜•¼ªB<'=fFâéæ¹®Ôoqå“ÁP ôúÁŸ}= œ-ˆÇ½’ÓÛ{0‚ËÏJ¨Sÿ¤ØìJ{Ÿ>b.#§ß0óñ€‡ Ù±î~º»ãúçm°7ÅC±é¸q– ˆ¾ÝE¤dŸá‡S^¬lq¶vBA4?®XF ÏD“³F{€'-ƒØÏßû ~¡sãc(Bß*ëbéƒùêÝhiÿÚ'’#ÛÉ$-!zY=.êýÎðO¯…=ghü+šý½çuå2Ïô4èO‘3dáÒ’œ²v+nAõM)Ìl(Ç5›‚àÊ–õıgÁoºôë¼J4b‹4•[™ p(Ý….½¦ín¹shc4–Å\¯\ tÔÙåm¸(n>vÕ%®Y“‘'ƃ\»Ïr¼§'øýâw×åƒH„!Gj|ý(FxDDI˜µÆÍ4„ê-3Ém~2¬¥OSL_aÇsy²ØG5̽)Ï•Ÿ°xÚ~Zöb5dí– gÏ©@<á/]*XÍ<••#\„é«Öë˜ú¹nü¸.6›pΕý¤w‰4u‰Õ'ê#ˆý´Bõ …x¾^?Ë.ô<Ù¤:Ù›·L£;¾cýs=H …–Þð¨LžØzûË6]"ÿ¦ÿ.¥%†eøåbû7׋³Sÿ7Dh™ Ÿr(:jR—šòÍý+±‰ŸZó?ƒa­³hÓ1aÿ8[¦­P&=*Â슿¬üs÷Šˆé}ÿìoÖ§ïÙý77™»ôÁ|éŠJ¿!'HœèW_ÄsªÇ˜w™î¨Å&,–¦Ê‹´éýÔÓ*öúƒaf5ß\4Ý͇í;ÁÃø Ü_†– {ÁyÞ&¼¬C¹=ÛàͲ›\Ë®ã¬Í—08UΰŸ…)Ç+ïɼÎRûºÒš¶Qó™â Xä@ÿ®OÆ=Q´%+ÖÅ–0/!¹¹›íÝ=£DáCß{nü>’ ®§(±yÏG¿ê‚ü†&+Ùþ˜«BYÜ™Øÿ8îüã¢Ëˤl&ï‚ù8§ÝŽãºožD^%‹ô(,ñrêur: Æã§„áZôtÂ¥£;6ÄÖSÚ_ÇE¡]ð->šj<³„˜'aD‹ª3+ÂOBàîNØiè‹SMÍè“,'òtÙ–×¼…!ÊÔÍNƒÞyy y§0Fñ¢ÀùŠíÅàBÜ.êÅ}Šf¯&Óù3¤HIvjS@}*6 „ãÁέña#ô¹Y ›üâYùæLíÉOåý]¨ì™mt¹Ž:–¸Ð†{|$Ô"—|©FÍ >˜1Ä)øèCÖhÑ´6´ðzò„ÿ?yãC† >"[—^ÞÌñmåäŒU/xº¨À¼´yèÿŽ‹kj+à¥eel(rd yÎx i'÷Ñ%3¶à…Ð|vëåÝdS{L-+¦ÇwZ0æ&•Uκ¥®b­ò2ñ¿YÌþ||¸rÞ>füð6´pŒ&[öÐFi3Tݮɽ¨)I¿¤>edü} dÌž\ÝD—ŠÂVÑ6Ó™—¤\„V¾æi“v‘I16D6ÿ ;xüDm)'ù-&tûõÈ6¦ËNUcôürúÚM•ã½ã<‰µ<ŽÎ"D´k.yRy‚Z}ï™xÿar0Žª§‚ÑÂó¸¨P‹<šù \G“À ;ùKcpž£çĆänÅ!\ã‘ 7¬€ðeÆPÛÑ x¿Z Úu0lC'›\7+ P(O•öxý‚P]ØÆ{™í±šJ'‰œc^õ’7^qŽûN¼©”Žd7Ñ1µG·{ ¼Ã’30ö v~–¢5z\ƨZƒ Ժ›#9¸òæu|TVÉ<Ê­ÄiÞsÑ|e!~§­·Ìðç%Ež¡X»ž 7b^²[¦0}v¤2ƒ@„µ8üFî`;s-Ùb>3½­È—ÆÌ¡­Qü>aMž¹÷búœ§cÀ·ù„-ðÀíJÐó; ^LùÈòÿ,‡˜I'àF;sîìfî ¿JV¦ê&dÌÈLm2ª¨Eo½d=ßÏ  “©Ka7îÓ¥ÆbÔ»³Œ=* n²¯ìݘ}üp4Þ‰äW¨‘ =îXUç>™, fâëñÍŒ_Œwm<Š.N…§‡#ɱ© Ô_U‰¹¨4·ñÁ 3ΫM¢”÷¹Úžùî¹Ìá3k¨Åúxä©ç£³Ž»Ð×Á•ðé›3ýÓ_ŒSïÇÑþ-'Ø#—Ù$³¸Ï_ O¸HsÓ·E3r:"ÿë¿°^Üe»®u ö›}즩p¨¢7.°EËuüÔÀµ˜=s6ƒå92+çïâÞ–‚ÍS©]Ù!Ê?ó%–¾ÅfŸAnåŒü#Ì”BÌ'ÜÞ«Á*å=+ry7ìAõÍ–téš ¬@¾§ÿòVûù®×j 2¸8 ÏÞV¡µ-UÌ»ÞD <ÕÈ<þ;•.yÜ€¦ÑA´+ æ¹ âßi¯Ñï‹4ä.‰…=^üÔÊ_–ü½VеWr9«ÃÀÈ$À÷èpæ­ip5gÝëøÈ®µ“ˆÍà_´µ5†{F‰`ùëÚ„ý-Þ-`cª`Ãî§ Vã ›>σ Õ.¤ØC—Ĩ‹Q/¹$çù;ð\ƒ;ŽÎ¤º~VT@¡óBâÿ¯ŸØêŸÖŸ{fZ¼f¾x»b¾Ö9ð¨´ÂÞKÉXBÝ|‹ä®¼Þ• §µ/BÒ£*°œÖ‡Ç%5`Pü! ê߃`”Û‰*ʈN¡pcù6xa‚*Wnöî@šït?¯Î'ÂJ/W®­0cK¼€¾íŸWšñÌ:^êÀ{»§6r_¨¯`™õ³œQìà¡ËKgÑù\ ò/3•&ô\­É©ž¨:ç„ÈîC dî–â`½Œ®ÜéWüŽP¬…ÈÝ›©ªÐ Ê+5Ž©½{™Ž{+±²V€6šÓÁòw¸EД˜Øê‘ ÓìH›`,?³–—øîêRŽE«ž>h­# ·#°³ä E¸ªv€Ž) 27®°¶§›ÑxŽ6x8ƒ¦G¦“¯vô ÌTöáR"ÓGlÁØàúËÿ4>án¡‰³gÑ9û‰ÿL²žgˆõ0v¦¥·:¸6õôiýVŒ ]ÊíêG«é¶ô²æ|gû‰É^wx§ŠÓªmÀ3b@ŒSÈé%8aÿ7¯C÷â2¸ï·bòRI~å-ú:¹œ¸½cHá8?Ù²IïËÀÍë—1ßÍŽ0[®ÄsÎ1wðjsÖ®V&­¦ÒôþÔ÷LD¸()¨H¢gFxHß•$º”þxm^ È –8é­'‹VÕñŒtS=‹çâ]èÏõçÙ×6.$üð³sþ‰Çt/c?©—­Q†~Óˆï‚oì•Ûú4!\›Û(Fã„÷ãæ3WqAÃUn¢}.´®Q#ãoãIav Ù|ï2wXõ¨_œCw˘Ó2‡Ë´Nf þ¬Ó'ëÑ‘èýT¤šõÁÌ9[‹ û7éî€/üùHΧ]Â?:ÒÄoíæEc%¼ôç…EB)h³\;æ‘V¿àA›9 Ü<öoÒ Ó2®e4„îd.}ïc\‚­ðdûdD'Sõe>L§s3úDUÀ›ën8þç÷š ïÍ8™ô‹[«kHÜY…›­! «éòÜ,<žxö@.T–>ýïwPÓîÃ+Í)°ÿü<¥Î°J·4Ñl_”¼PÁq“!¸¶ö :hkáu '—žâšˆš34$ þdºCçE!¬^Îú<.‚I¡l*¡Y…6ØÙÓqâý÷ü=¢ áNƒœ§é ¢âM«“™Ìœ-ty „Ùì@«;yèw@ f|R£ê¼!è°êVäÈP'|–c ²­c›mƒwÍ@“_ ¾UN£\cMrm@ŽÆ¾XÈù9ù6.XÛÇ•:~§ÏžM›—l$/š“Iî1þ Ï=s¯k^Á£¡¥÷­iZÑK´]á;»%ñÌ®pä×LB5ÿ,âH`Ä}arñÜ"|u+ùÝ ½Â‰¨6/ëfÉÿ}Þd~Ý\«žÍ,?q}\k«0Ò#‡|o@ìßÉ 9e¡ÀË—†÷^OøTÍqfSQ%ǵÂà™Ò^ÇÜÖ±±0¨¨9ÆjF&2)®2Tä}³â›ý  ö£Ø›Rn)Ó2G~ªæ¸ gYÿÓÀÀhÑ)xPVûÜÄÈ"÷Ùxä˜/“ù:‰ [±ˆÎ~ÉKåÊ[ØX<1Eœíñ6¯$Ôh0žÅ÷Oóñ‚E(?­Ã/"PBj93RGÿído’‡XtG›Ên-Â…÷…à~~Ìnõ¢b:¸Óx Þ{uP‘ÿšj7èƒ8-Òó/ž&;Þ„Ýw´ŒãP˜ù„)7ÙÍ~Ù¢F:”'êßm1/P’äÃ]ê ¶>š<³Ç ÜVýÙêo÷3ŽÝ‹(ºy˜H~¢96ƒªï¼…WD#áí’#Ö .dã'•p˜g:“ºX»›¡ä󢨱¡ÍëÒ¹sWÍ¡âÞ¦â6îZ·—«\Àö²afÉÂÅÇÚØßs3AÓ5ä_=õg2æ>Ä… Ç™qcy”éz †¹D÷Ð#,¸·„—‡±óƒMIоaî¼àClÙ—#àU9Êf>ÚCÙ ü—Ûþ{&ÔÂQÊ“IÕJb0Ÿ$‰ÛèI;V°œ~ûh²%™i0ÿ }Ç87ùS؈>QÒÙz¶$XWPXͼqº_fåáO‹ìá­Ò0ãÜMÎêžtNóx0ÛSû…ý~¤œÝ¸â$ø[Ú ûÌK2+¡¦ð4” D!# Z°PšõlE0”è[ž œùožã»¹GšölP!³Ò¼at·ÇƱ5›eèéÓ“é‡teë››äþéEGAÖ-xµ/’†¿)bÄç@ä}‹¤âb®Q8¬ä_ í"=ŒñÊ¿°ít 4ø»£¦éØS ¼åyÀŸ©F¤µ…aÎÑi4(÷»©ó [Údã#>Áå=t0½Ãø‹Î`¯¾4)4£Â½1džâb(ºs?~ í%é5 SÌ™ádt€Gú+„ÔA°l.{#ºû—  ¦£8ã[7ß»îþåvœÚ‚å•ó`ðn)«í¡Lfÿâ\Ê$µãŸ˜ñ/Ñpvûæ‡úÌ&>ß ÖRæ~Ïã%/¯Mþ”@ίCÜ7bÓaç7~:„r;‰œK=ÌìŒGÏ?Dë_N“có8íÓZp’Î=¦ä^&Ø^ã?#ÀSY‰'ާ4]X¥ùœñ™'KrÔt‰×43 i{‰µë¢@¶Øz"ÿµ’ýlÅÇ=lÁ-YZrüÏ"¯ÏGîAwˆÛ#Hk†²Àú‚x(ƒe-Ìû(%(taü4!%oJP?ð3¶i.ÄÝJÚäÙÝ^ sÏÇñC1ܪ3òèß|e_ä dÓ^6G1@‹K åpåñBvº„©M-ƒ±ÕÖÐÚš…}?2yJü`kË¢<.¦<Ç­×D\nŠºÁ~4£ýŒ• f¿-¨íj¨àòC!paÆiæ‘]>¾Ý=›^HÓ¥…±÷q§Q/¼ºv“Ùë°B<¯sº¾>_KÑ<&ƒ,.Lä?ã îÌ/Ñ Œ~E<Èå;[ʬÉGÈ:œ†'ž/Çû‘â°|­6û®NˆºðוD/~ÇjG—ÂÝ3ÏÀt÷Ly}ßæD1ßïG—gæ¬f~ÃVQ¼.ºŽ] °Åä)£ÍûÒŸê‘h"AB–¢A*×Ur_ÇzA^o0V“a…ûY|1ŒF‹ˆþzG\5z zBËpRŒ¶åL¢*/¦‚A¦§´ßÌ‘#·K“#ÏpõI>â v‰­98…ð¿Ò¦½m…`6hDÌg”Ã]¯·à9ŠÑ*²XŸi§@Ò>eBÿ»ÄKC°y{E2”cméÊÝQ¢L²|ƒ°/,¬= Á¿›|²— ƒoÌÿÓ6X°>±)ÌÉýJ`uLãwနf>ßÁd>H²ŸëÃÜG0ºŸ¬ÚF>¤)À—”+äß±TYr ™–öÚùõ©øTc|8ç5zA vmËfýYa|ÅÅ,Ù;‰lÉ Å-|‡àø™@ÎÏw°Vø{*z èN‹$CÚËàsFrÞí„6Ç‹Ø+ÒŠ2ŒüŽŽÕo|+ö 7t‡e ëõ<ßÚY“7|Vðúµ(NßõZf[‡,‚p£|¬,6Cvp#¹Â f¶ A¤¿.Sþ„‡¾a†Ø3?ÙÈ’¥ ”?býçw6vàÏøZbr¿a¥Ð\Ô—c/~š;45‡µ¼fʆNúËa21Èþ§'C0çÏsè×ÇMúôs¿ºÝ8³ïù–kÚŒz9›às:zäX‚NÍ:²uv?$KŽpsž¢«ÈZøæÖ‡#{›ªâ®—7¨^ŠCÓ)»@nAãÙµÄÇgÕAîà}x—â­;Âá.+¿À/+BïaYʾºâA§.èùo5»®MšsÁy;ú¦ªâäþ[˧6ÁÚs„ptÍsÆâh2éÌîŠÀnÂļ0/ž…½g…)àQ}ÆþÝ€ ±_Š„ˆøOÆÑ¤•g%Ç,Ù+í1|l±.YN….DCtÚIÎ8·:"ô¨Å¨8éìBw›1œ¢vŽùµ< ÜåéóÖbl|³ñP½ÍÑÛ' sÅB‘}#OÈ‘Æò ¤®nƒ,/â«üë¸.é>è¨-Ãùš&øÀæ§Ïô&ù"'Õ8ÏAIý%dµŸ4ž N74xbIt$2ë]±ò!ô\*<Þd„᪪wO#Æ7Š&êŸnQ;{À- í­Â´i0v×’¬!A …5çÖc®É7Ø9ÔÂô+­…;çîÀËL°³—¤ò[yHº¨)óÀÕÆ6¾ÍÌŸw¯î åZvßëÖ°;»áÞ\®qoÃ]W÷ï9rÀf,dývÃeö^ø¸„Yu5ìâ¼°9­‹Y²§ –Ÿ¼‰~·Ûp¹¼ƒõj×SèîñœIvR¦ÞnaÎiœ‘U :ß&gé¶ üýçÞî{0Ïá*ÞôXƒ_7œ„Þ\ º¡IXÛ §usÑêþUÖ!ü>&œŸN]ùiŧRP¸*G®ž™DéVt‡v>U Çï}fÕ†~ck3Ö_Û^÷õèSéE”ãë1q®(7°:ÿ| a¤}.}ï9Š#q½Œ¯8>¨Y‡CbÇaŸÒwTÿPÇm8×Å,H6eë-ÒQL6’5_°}¢þïž Ö. ™ˆû³éü·Y$ÄlU>%ß¼È͇þaìL¦o?ƒ±¿3Ø”#ûø'Ó[/D,Ã.+øo&ôÝ£_DÁX”|¿*Bk2”q‡› Õ]4„Ÿ®1?:‡J¬ËuWDÈݧ†äû“óágýz¼ï^¦nM[`áËnlû¬ËfËAÓøB˜œ¼œ¢S4éåî·¦v@ë³:æ zrQìŽÝ²%§†Ø“íûè,~kbsá9Ìøü“µŽÐ í‡.ÂÑW&ìq;=±C´«¼Ä[»šñÛ•zìס?n8ýOÿpƒGdq¹™9rƒtÉ‚ö9¸³ág,jj>/ÄæX7P½¯Fvw‡€~á]ìÙRͬ¼ÓÛc&Qyn&H{éà^?gømÖÏ|;¬Ë¸UŽ#Çd¡ì 6ɪÒAëù”GH— IÙeF¼+‘ªKf§ºÙ°ÝwØMÊO­tÜ·b 3Ú¤BËàÎþ4œ¹}KïLÂ]™©Ì¡ñç8fÅmž}žLšŽ¿ƒÕTܹSVAU¦Yä)E¥&Í£³>EËÇO0§}3Ž¥“…SYÕÜ|ŒßnG®d¶Ã‘½ûÈ›¡³Ð¨ÿ¿í8Ålã‰F…cìÏÉÿú“1Zd æÉfÃ|_}²ÄÏGéú€L”Øk5ŸóëÅkô|Ó¤ºÖîl±"®ïìaÚýp4ëÉÄ®Õ ‰ä©Q´˜î@Ì*5Éõ©´ÅHsýÿ}ΗkPª1™9ëq’q+K‚¬âïpùÍWîÊ~ú6¨¥›À<ÍR¶tÜÙ{g<Êfï{Úaõ_0iÎÜúúe"þÕ_Å2z¾ÙP#éK_•ÀÃ- è÷ )êð û9¿M†ä¤D#®^U÷øHì§9´Q`K}(ÿ¸Õ\³ŸógÕ{ÎÀ£5ø«@wƒ=ªb§äÖÇR&Ôþogÿ¸9Ý,XˆR}ˆ·žÁÍº{óeÁi:$– _æêÓí‚ò˜‡„ìý€<·§“Šóè~¦‹9u0t ¥ÀhÇCxfz„„弯C;PÙ&‰ýñÇo†1‚•Çi oÔÿ»v9’½­¹õÉ`ÐÓo˜ €ÂÎw‚S1àEU\ùÀ²?†Î|N¾ìI¤…jûØÝŒ´•ZÑêì2,_“CòóðÕÑLTq e7ßÌ$Ù> d‰ûW¦Ûc eŒ4è›3 `Ë h³/#J½¸Ð<”[­}˜ô0Í >ÿ4D%¤ZIm4J {³+d†‘¿D!`©&œ/šG¿Ü)F­WW9+sCD£ Þ?ÔâQ6Ü«WÜ­ÙÉäÞ"¼ŽñÑm!*ﲦC+J`˜§}×6ÚߢÙ5 éΪú¦( Òü݉îÛW/e«h×÷«¤v-Ñ}tUœ¥Iç [gRDÝÞ×r.Ü>}ËBÐÚŸ‹óÏ_¢z­‡˜kt±üITæ#vcF4t,‰5–$Ke¾1ƒwkéc¥^f‘±ªÎ| ¼êü×SMîV9rÔÚ£0º›®^Ź,|Ý«àÓ®|{ý2§´2…ÛÛšøP¿ L!Y{DhL¥&½ÛužÌ[\O{ëÐÜIZ÷Ö&MÂïSÙ€ã—Ðí½ñ„ýËF‚AàÅM˜q„Åc}>p¬ä"ÎỊWÐK#Ö½^Âq^àLwÝÐ!ý²˜¾£ZX·~LÍ`«Ï&³».õu ­iÚÔ<¦kŠ… B0”­/hmúq÷Ÿ¯† ž¿×€ŸóŠ» ݈d?ŠÅ2m#ÖuÇQ´iå<¹ÒˆÞß~!¯,]!¤Nµ4é“üäÿæP3:=ÌhC\né ÷S›ÄTìÂí£þÕ[~rÞ6k…¾¶¯¬À"Qf †ð~û>ê3FÕm1x§#ˆdËoc%4€í#)jí_¯k”'òߦ_ ˜ÓîjM£ÜDÞì±YùЦw–µèÚ—xëáé=#tˆíã$tñ€ÐÁlÿ«õ¥÷ Öl½_´üÓhz°3j΄ÿ­_ˆô3ÚïÆÞ»TÀž · ¼$‰ó'ÈçŸ:‹¶f5°ô³îÜ3††Ó=YEi¶’kKWåý`~ Ÿæòç9PÙÈßÿ$2ë¡kÚ Ðé‚SRQ¤Z6¬>M–ß K…z˜tƒ$öÎQ9jõØŽ„„îc:Ëîp¤nV3]pG`&¾~2™ÈÉš’*1išÈðq¨ú"W¸Øq‹k©f@o´ k?Åb¤ò3Ÿ«Ðg³IÓê8êüý9û<ôšg£ÜXs8NDog§^ÉÃ]¯3>ât ‡Î•H†Ó«B'êߦg™Œûòl¶M*”†ÿŽF·õ\ÚS(ɤ­“&YKò™Â}»PqTœÖÉÀÆ”º[_ Ê8cx£]ËTáÒmúM)dŸÂìÇá½szLmÃO^~xöÛzúvê+<²î$F.ߊÝGmé×oó‰Lq?,è<…mŒÉzÇT´ Îå¼ùëå™ÀëŠäÓå¯pâêUÎ~82¦jG/jÄÕg#8ž¥2ôìºQ°¾t‹WÆÎ¨‹´pK+œùÇUBiÒÎèBVEã¶äº_ ä,ºƒGÂn09¢W «cÄõ} üî_Ë|Ú™3aÿüG÷Yi‹¨KÐî)þª°…™ó©›…:›g/cº®`í³îÀÌÛ Â^fŒíc—M•¢²‚Št¯p'ÛkQÁ8è¯&ïøŠ°â[s®H€&Wq™Ã™\v,ÃÖ³ñh=ÙƒôÇíÀÉÚ)ýYvfÀ¦ P“ê|ËÁàJQö×cTÊ cÖdÃn.¡p«©”i5Û+n}ƒ\oFþ0^=“wWàòüÉKsx€¾(g#½á©`yÕ± Û{ö4=ø©G»VM¦™rK˜M«ó°õÅo¼æu;ÜÞ5=}š7ÿ§p85Œé{ãE$ß•7žn>ÞΰGR„œT¿zx}ÉžÆwáç¸ò*•î¡+æJÐîk̦ËBˆÊ4£¼èÇê?¦^âê+,!¹³¸ºSOÁ4§Ç£–B2²£ÈÑ´Tàh-bS2|:Z/OžÇch›7ÔÚŒ‰Í÷Wx§"ŸÕÎø…˺þ૆Ìõ ÅŒ÷Hó€ï,œ.È¡“s/Kv=³„¬²&—í˜Ç&°è­!Ôú‘c{¥À-xõlTb” l8fŒÝ!&FZŠq¾^œÀ™±KE Pí˜U 1€õn]¸`á=8: Ì1ìA©IÙ>§ ožK$vÊ“w^²Þ¿@ð~:ä$ȱ‡‡4Á®§Ïh Ð5Ñš¤ÙtœÆ€I-.üæÁ:,ÜI:”ò@T³ ]ßó¡~q¬Ãð>!zÁê>ì¾äCå–N¡ó+[ÙbLÓ¤J¬Ù1¬Þbžôöõ‚b–WVè~დÿ(Ž{¿ZŽ|ûâÇjŠ˜Y¾ý\Á]xÔ/‡¯zø¨LT7¼‘J¯¹j¯í .Eˆí»6ÀpO“ø®g¬‰Œ:µIž5ÿxrPég6S ‹Éæù °Öæ2\ÖØ@¿i Ó§¯ÊÔ…_€ÃE‡¹Ô*©TòµÉµ©Ò¤6h_^¨À´ÚÅ´|U l¹O®?eù-™“ÓœáÊÅ@søÍ¼‘!'ê½A0s5§âÊ&³‰À¡ôh¼{“}³‘—²Ï}ñ²j ÷ô' ²W—zßã<ôå¥õu°[Ö n;­„)}bt—¶6%V³ÐVà<ç…çµ(1bF"‰ 9½ú*ÜèŒÔdÔìf£R¦³–ªÿw­§¬ý3ýé °©lbÖvÁ±æßxXzÝ>ñÓãáÝðƒ‰Å™!œµ…çHü3 ølý†éèÄ»Z§ØÑ O¬O…Ü‘šMžoçì–P WònCÊËZ<ýpFlUúoõúÃ@.2Pw–™ýJ<{=p¥Þ4¬”=<»~¬ÙLŸ&?n &ŒNE£|çÿÿ&ë÷Eøýå1J­„¼ìlÖ÷ÈÚ¨¹ˆøî ‡)ê“éë›/X†U¡/ÿ¶P§E¢´4Â4Ë9ôv´‡ú¬#»d«HHN¹.8ƒ²¿âZð"½Ÿ}DÐUYTŸ«HM¶É4Ïû£Ô¼Õãf¤Í£‚`wgÂÿì½ÃùïÅž·ÉŸñ Œž›D÷LÙÀØ-ÀWóóÉs“=®,¤WÖq,\ÏrùŒ3Àƒ[ÀÈÄâyI"ëü}îvÀ¡gFDJé_m\FSæß‡R8ÚZDèœ ÏãÒ…U¦´®¼Þt)°z·ÈÙÈa¼ùW™4ÊÚs1¬]ÓBe/QÈ%€‘hÚkfH¿$L#Ó^nG…‘¹D`sY[âNµ–i£´ü´]ÛÜZš¾y‘ñéí“Pú K¶.Ü Õ›¶Óª½+)¨·¢xü 4ÊŸÎíɃ¼ãÜãŒ0 ³Ÿ°ÎäܛН9“^­§ÃçUˆN*\Å©ïaEK"²Oç4ˆO†'½`gF ´o`Y¹£ tþë»žÆø±mO-éû]!(O’Ô ¨Ò{/}ðgD„}‚ ŽÇÈ[…`ª4Ô‡+ì§£Qœ:Ìrþ쵂—…/¡¡s½2e)¹<þ ˆ§ÛÆ„LÒÉã.ûƼØi‰;ý-ieè šýpî=C½Dkºrò æXq4[Ýî‡ÓM»°fŠ!è?ÁH|I¦ÙÏÙ}Þp¿¯s¥X›´œ§/ γ¦ã§áõæ-äÕo#ZLà?ú%‡ÊŒ8–hZ¿Ü·3AA\ñS™Œ?îD‡üd?ÿ Ø7îÀ´Èpg¸êb‘[šñàáüŒîÍb&TÝ—æHLg®ZÝäŸý­)y¡;/Ò~îÛQ­¾>âyoM€g=ãlá â©–EÑ?«éü¬€¿ïË‹^Ôþu¤l\ -+b¸s~óS¿uïAQÎ…:6` Eªf؇&ÇŒˆÏ*²IË™¥exqß î ¨e"Η[çiÞ7‹š»0sƒ/× ¾© ªò#­õIj‚í_£…ë‚Ú&êe,3à|q0툙MS-÷B[¼)Ð:¯÷¢ïÍPU9â°'‚jÕ‘­'C iò?‡þwÞ‡¿¬«p=5²”š }ð%þ–\d>¹¡Èû"À[r´fO(ñ/4¡r7bˆÛ³Ëà««D¯í?‰ã¥IXá¾ç‘»Ðì¨/ûÝZ¦”öµ6Ëuƒ¹Ëm„­òáú¬£ÔÀ)ž¦Þ"·aã5qÌY.Nô‚Él-‘0#I½çȯä5äî4´YV;¹œÅSgS•B¸{/fènbN:M&'ÿ¡»û^OÄï³¶GíS˜œˆÆ?4mNÿ\E\$ÈRñ•X§­A$&Ók+æÅd}*âWGûÅs‰ÖêFÑQ~mÍÍ#²éÕÌ£¥Yî‚é¤kn)û¥¥›ùrÚŸ¶­²dün†ÑA!9R,Ï‚¼œ#y<ç­úñ­‡ÊQ€Ê67 /£?O¬$GW¹Qù—]Äç´4_o%¦åµÄUî ]kÚÁUÜ»¬qÆŠ³—aÿÂ*"Ù¾…nNBñ™át¹Y-!|K¨J†ý5»Ýë¤ówPïð.ìÔ^Ëjhæ2sέ iKSµæ2j‰'GnþoÿkIH3Ç…AÏZ1úÛë蛨âëÆôÍþ-0c Mª¿†ï¦ÒŸ/Éö öˆ«4õœj Ý êl­–³P6›I¸Ù‹ËÖàÙY·ð_Þ V¥¿wu EüócLUjƬ,4_†z ÉðÝn¼²ÄеèkWMêýi#æHöÀñí™àð7ÒãÀ¢Ó¥bë›C¶bæB´ÚWWá¼-äÂ8Ø~æÑ߯‘å•¥lÕ*zÆ+k·ìfúÄ ÈÁö0bH¤¤âp×­¹XÄ^eV­'— ¯aYÿþc‚`ÑÕÂÊ)ÝÃ׿Úh×Ë™tJbôd óÛ¬‰Aš">Ø,JÌCXx¸x¶àÜK®4Ç;„J.ð¥N㪴$“Ÿ¾¹÷£òdÉ[ž(W8™\ðË`?6•ÃJõq†ŸQjÙÎe´bÐä ØÔ™SÇåÔá¹6ôÈÐO¢ÑçO"{þj݃Žs‚´l9áOa¶>º©’ëàø¶éôNî6b_X éÏ6“¸v1²T÷ D·UBþ‘^¦ÔCy ¿-Ó›‹ƒYbdQ®8y²¡ÑóO4ò>lƧªwñVF +ÿ!6“Ô ýcx9 ”¢àÞ\ÖÇü£µšà:ú£r&ÍÏÁÜSzŒ/¯+dâÀ濜¹sbèóØÞ5²¦…³gåŸà×¥YÈ?`ŒSg‡ÌÆ]bO™¥<P;Vóú dÔƒ+ü<–é뢅öYhÙ:³C(7óóçújX?é ÌÙ{ûï1×:ê Tä«‘vjþi¦îcɸbR[ü]ø8ȼ`Ô5îÂá«÷ñ‰CÚm¥Ÿ6ôÃ,ÕõðÅ_€,Ýi@ìe9pO6ÚUß3S° ¯Vá§HÚd¹öŸí^±þbÖlÑ’%û·:Ñåµ8 à}¿VÏ‚l /¸ls“Ma 3ægâݹ÷éôøeIÅ- 1k‰×ÍpvN™•T‹‡Ÿ¹1Ôï&<ø §^2MÅqÇþ^Ž aﹸ7: %ÓjX¹×Ž\‘ŒWzj ø+£Pí4²Mƒ9wbPVÃýciÜVòøÕIlUÓ#qÏ  â½é9RŒK“ÀGȘåË8Bô}Ù«@élXýÀ ¡Ž@Û<³ â›;¡ü˜]ö†ó½™Ÿn¹aM¼ÓT¥Ï–ôÆ6#äÌ_@†¤`á6Þ‰ýa’déÙRnãLb®4ÕJT#c÷~áà:+̯™N¿%š“‚ÛÐp¤—&."óU‚zª3µ•MÂæ†`:ÍÓåØ%wÂÖ+¯!výf½ ‡Óô-ê–AëØrXq 6ý«‚{vO¡c·¢ðGÒÆ&Ãx^M# ?ÓÉ‘W|¤6òΓn Š&Š4±ÿ#ë½=”XzD€`ä=hšoþӨڇܻò5}ÆÇÒJ¹dsê"Œ¬qAùÀðFá,¾ý¾šÎñ] |¢/áDv+œY¼ «çPÏ0}¶âìaz­®f—ö³^aÞø÷.±ƒÆø\P½öNN7G=_ úcím–ÿk8:wC«S1WñüÇ3O ØÇ‡lÙéùdùâ'l l $èFcú¦]œÎÏ7ÐE$‡:µ”à‰& ý>ôå;x¨<†ÚÏ҈ߎëx×ÉO÷Æ ¯Úmü¡@ÆK³¡çé0B'‡>Z! LVÂË:„èÃá‡ép(2¶l+äþ~gFS Ç3²ÄZiZ@‘½<ÎJàÎ¥ªT®Ä…&9lBaqcêIcušiÝ×aȦb_r¹÷l/V%‚«O-™Ý^Ùð@àóÏF[¦Áá-ÓqH)4^ƒºM93ulÂ7›¡²Ó5PǹjÑôÒ>^8õ©o»ÓÇïβwÞHáºüFwåhèhUÂeßfÒê»ÙˆÙ¦°Yù³Y0fë%fÁ•DxÜZ –áUlåÇêñ$KùMÅaƒv-\èNÚ+èhuìrS¢/¥ä©ì:5t줆“u‰Òïj\Ox°|~0®[{ øÎ¯ä íšDÎ<›k-¨ßÁ6Z¤šû››ÛWU6âRÙ˜cï‹k·²³ÙYrÀ×­e[ï°»>ËP;|nôñø'?eäôÂÈ+*Ú¿²éžŠšÞ{×~ï=xBØûÏ0 ¿†b?1¿¾Ä°Ïæïf ^|‚uÑy8ëpj)K>gª6;‡YúÚÛx¡~ÙΕ_“ÉÁcàuc%Œ¿íÚ½ª©ñàÊ=–Œ°6}œBª”íÑäh9(­êiG´¬ŽdˉP¯ýT-K<ãÕˆv —±v8È:ù,e:ÿþÒã‡?|s™¥eÌ9ï/ p'˜Áx7ý™öƒ±3s!kL±]+×|ܯnF1‹2”èÏßàֱ⦲»ŸqŸ v£_^šsŽ„oaþruàó±gŒgÝ ü¶0?ð°n0¿L;®f•ƒÆƒ>¨X 8sî5š‹f¸OYN4›»™<V!Æù™bLšä…^Æa ý×ë.’ÿÎj2$|Õê4õX®w—û÷Œ‡ 3ê e`CÃa8Ó§AKvÍg’š´‘|³ÇåÇFñräuxtv;\P"s!»zcsËc m¼„8-Gô}×ÀºvIb-c¹ÏC9b\-IÅ ‘kzÝ{ ?q âûÆþÞ¯þl`g}ææÌ—#.ǰKr*8yÅÑ\œ,AÕߤ3¶QÕØôg-%Lø¿÷†*ü´V¿K#ïÝy`ùüÇðŠ%->4—¯|‚QÿäìSæ—Ò\âºPƒq®*EEÉ6|ãþnœgÐÄ¢‰Ùló˜#ú]€<ŒÎátXh’ûjÝVâçþ°‡¥"ÙUâ/1$[Š}£&‰½'cP°t3þ)Ѓ¤Çžx—ñÂnƒ«Ü…¿0ÚF7@^;– ͵^>rú°iκ&òê0…§„!Kü™g›¯áî§Üy§Œ©øpŸ•¼›t•³ËÍêðë&aÔ/¨fòÊû!—,!úy—A0ü:Œ†Dbbör²i ›¹êžÆ”u¼àFXnœÀŸy61Oèa§x¼Nü[¸w+ΖÁ®§*pÄxFå¯&‹§äÀ%žé(s*…ÉâÇñ-Áú½sñ…ôlz1„×êS˜güä³ÖIÐtƒé™‡8of_c«ÅèšKé‡õ¢Äpž}ˆ3EÉýŸßAci;kÌ·š«ú`=œ©ÆÍ›…èŠA ‰çˆmûÈ=~³™É>aA×êóSm¯ °F½j<H“lh^[Îs–±3‚@cz/ùRËÍfrž´>cÉ_·fŒ«±{ñ~’]^Å<ìÊ 0•3È}r÷¼qœüÿôŸg#³Ï²ŠíûRimkªÈÞ­še[Ùè°`¨ó—¡›øárÿLª´ ïMá÷ìbw¿ÕSã`¹G¼ôø`Ù)a’{|6Öu„@Ùöëlú¾³ðg…7nØ¢n“hȬ&ܤõŒéêÔÀùÈë˜Ì}|–m5Ñ¡åxȧö80VŽCº å~u•Â9©!Û ›«üqêÈ’®-+Yï’äX£üUÌ&!“)Šdº:lñ®§¬ÒùÙôkà ûµÅCb±u‰2,Ïu ÊOòP·Æ ñ$w¼ù!x— 1Ý2+'â?ý×f&"P—6‹Ò¡…xËÅ­£ö²ÑçºqàÝkÌš#='ò®ˆ¯½É½}[ô†){/Þ Lo’!yŒ Ö-f¬¶Oåüa3_&áùs2øùæ_|ÿ16¦FcÒ C6~Ä­™wM™ôiørîO©Hڽ蓞½Ý˲áÙë1,HÍ#A ð‡_l5žxa²Zã{Ns: –…c+és#XîÈùòô8*îvauå’±±gX/"3ÃÛá¯q Dۃâ¬ê‹Z|7–ú³mi²á,º?}6,hNBžëÿßüç_¾¶ìaoÈ*W Se·¨I9ýZ~ý‹ñž8¼“¨Bæ¾`ªš­hHW9c%!&1æxà¯hÍSƒ¹õÌãÍzô×µ&Øz"”ªÝ®þhfwÐg} KöL"Š»hâ~i|;Ms;åhß¼!°8/AeG ÙêöTø¼Á“݇xðè%®‚ˆ,ýË6¢•ŨJl漸s ÏŸŒ¦1‡³þË eáú’¨±¨ajCÂnÀuÅBÆ+j[å$EYQLß8—lâ°§õè†G¸¶Ð‘Ñ<‘ïn¤!¾Èa݇ 'üÿ—n)·×µžñã/dø“s˜%œßð¢ö!\[‚GŸ`¥5HÜ z¦XåŠù°`|8]}Å @kð(Ôä%~IK(Ïôµ¬„”:,tCù;iÔæ1ÖÔGšœ …ìãM.Òq¤ ïžD‘áa)ǹhª·7u)£ÿêép·ZÑñ¾uÖõâW± ©¤BŸŸp笽ÐtÅËo]ƪ¥_amñ© üû‚e€LïaøÛ/på\€zci/$è\DoÉ+xxù*Æð‰$®XéWA÷çì¢_ö¸#Z›ºÏMb… CùŽÏ`ÔDZè/fr­\Ù%/à ùw ©üéOñǤÀbèWodß Ìïö¯ÃKK°ïû'ðrûǹOAy’8Ùqßš¼Z7ØŸCûˆ{Xå/T¨®Ä Ëÿ…Ž61¢xKYÏôDÁÙ,~<±©Â:å½Å–)ÿî©FB œõãků|^X-gFF”pcÇΠOd5æ}5‚C‚K0SHŒ[!¶}bþõùú@æqJäó Ãè¸,ªÈýÙ]±©ÚsYïì†3ŠP',G߇µ±O¼Ã´lO:ÍD·àó(s™z'OÄ’GÈéQz›Ù@å ËàPÙ\˜|æ7žSÝCŠÔàþ*õ¹…1$‘³ˆ6}ý§Y2dÀ>2fV°M£¼¸hí„É霨\0 ×f ÞéÀ’zIˆ1¿Ý±‡@Üå! +cÕC`%<ù“ê$W²ÿK¿2¤ï‚*°ycðÎ$ôÈeJÐß™Õήfz{ͨª úƤLìH˜F±ö%ÝÂá³K§|k¼´< ßô´ÁBþ87uŠì“.‚Ú›'™{j¸¶þ.ÈTósU,)<–?S÷~f¿ps¶…à½/:(h×ÁL¯ÙŒùŸn²³„HèR´ky”(½‚ËKx°àà3U?‹ ½. õ2±¬ÃE1¹i/CÂÏ~ˆlKÃ<ݹ¸sP¯ýp€Ÿ4Xè,Eº¼1R“.´àµýaö©¿ðö¤ m*Ô'22W±âívèS«xW3zS¨øÈÐ# ?83ÊÚpÙ"'6(Gær¤'ð×ìc_cöÜÉþ £wýÅ©˜ïórà i3Oû™Y ï¹æC§Qà¢yZŸÆÎßq &wsŸÏd¢7®dÖ:Þb'‰ˆ†xQHgÛ{_”¶Ü=ϘiÐWžº:οâ [çQ‘U¬æ”6=øŸ^1™ÂEí†5¿ô°õ¢æpx~¸B‰c>¬mæ$Zð}™µ0š"Cû\)Ó* ûô^‚ÍyKô°f†›H-_îãÝH~*DSA‡É$pj ñ’-Fòœo1U`~ØŸÜ¿ãÇÞ˜ÄK]_0Å ’™À²ý4[9ÕÓ¢+YÞsÔøÈðÎag‰l%)Å$¹ÿ:Ü-òà`9>´“àœµšBÝ»ŠUå°Š·/<=DÃÈŠM:W¶Ï/¢¸.²÷¾çLiÖ!Ë/ðÿ*0߬•}û‹Åã3±cI%ä;š0ÂŽÃMò}ÁžŠ@·b&ÆÄ¥Á‡èÍŒhõ\"6:•¶“H&.+gá¹Ozôœ»6s)¨Î Ãá¯ÚжrÑSËÝevä®MJߦ$w¿)j®cÏ_“¦7”yȺH†ÌíÓ`Þï3£;ßázz2fêNà²JÅ¡åéìÔÍltNpåžÀÍå䥰/£W¼×Jȃ´O,¬½)JÓâ´@±' ^ŸÁ¨Ò)°-V˜n·}+½· ’¼-gÑ%.M®LáMx:ÇÓóÖr»ÚX7§ðÎo5mj“$»_̧«âµñZV#Æ †CÔ‡uÈgmNBvaèœ{°,TjUæÒL¸y<@hú"ÐÏäöª‘Þo{azõ]Æ(ò)¼ªÈ¤Åbͤ±TíÇçN'£ÔyÚ.rˆ°‰8>õ^Ä__ü¬3¼JŠƒÇæÃ‰³[qáO Ñi6øg.ƒQ3™kE¸Ro5ýjü fN c÷€åèTÖ<Üèüõ%Vìâí0±ç| ;ÈÙ“B¢$°{-v`yöآ΀%Ú?($2~3`¡x#óÜ ’î[+NMÅ‹ o9×D³Éî k ¿DhuðQ²d4#M³Ø‚3—G1/(÷E‚º¸ËðÀÒ]·![ý ® ”¥jÓ$Ióy@½_§ ü…3¼KœxU•0 +$I¹W:^ÙeIRLð¯p+nSÑ_68k´ f…_æX§ÁÞØ˜}®¦þÀ­W$'ö,÷.­ûÿÜB>‰@ͣ˨y§ŽYpþ3ûÓyfe’71ØuÔ ~M¯'ŸLV\zÈTº&±áH•§=fM¬=áTr>²àô®d/ ï %QÁ A!7\…vÿMBƒZx~ŠÜDüÇâèÞ(¡0†'£Hßð{Ì/ï sÈÜqs±{Œ³Lá1¼pœ‚ïÊŒiÚ©½¹$f&†‘úRqúœ'8{“k;É©­Ô¯éûxÆv¨M|ˆîû1õM1$¨÷ÇøBJÎ6vCð-Œ½°‰z/}Ç?V"!­™ ~²v°íäOòyà[‹¯‡Ò7ñˆŒgŽ+X!{A…˜Jë]<Ü#Kðråä¹È’WÌóÒaЬ9ž?¢5+j0=;‡³µ@j¡",pHà,Ó‹b«“§cîØ2\ã?Œ–)¼Ä2¡ÿŒ:jÙ#KðËžaÎÆy—QëÑ7f¹ÉgVë´:9°¼¸)æäkì6EÁ0;F5Sš¿Qp›ÑÕÔ7­ŒÓªC¯Œ¯‚­ÛâK÷WÈ—¶ó² !7’ŸL­Yµ—$`¿½"•T¥rÑèòYNdÌÇeúþtÑÔAXÌ_Ü3 Ð´økÇpœÄ[ààÞÕøpî–Ÿ‘ãúÚ/åÜá‚óš&£¿å,Ï `«ž>Ñ(.ˆlˆÄË\^h[A5ù©¯¦$}?;+.i¹WýŒãKQT>r[ß0ô×øµÿ>~ô~Ûï~;wÿS±Œ­ò æ‘2ÿ;ê|TË¢øØ9(Œ¢ŒþLòÎÙãÜ ||'ø+0 §µÒ/iÓøû#pà/:µŠÝ»'™®ë/dtÅ\pKÏJZ§ð _»ŸÂÝ"âT›ç9ÎÌŠAcpõÔuü®çL×~ |œü»T–¬ýà‹Ÿ˜O8‡¼gІ¼é®UV`ü˜uºlF]KdÉ}Y:YH„rÞí"ƒ|šô¤’"I[q´O9¡‚Êl¶a#½e!H2N°kÕ8ôøH'óÏ”]Ïœg8oÄÙiYzôEÜæÒ¬fØgZ‚Úó¸ÿþK bèÛ+ƒgþÓ`IÕ¸\U•èT‹ceM0~»U‹iûs;&MÞÆã‰—@Z€ÁW[èbïDØ¡/EUndpKC€¯÷ ^|Ê )•Àrãkœû P Æ§;ÀuëzëÉ'(~ÓÏœ\B®‰ÌÇN…ò1õ[¿X™g>‘ÉgÐþs1÷yÇ¿œ°ûTm5'’ ŸTa圫„äî5¬„Ü1²`J4Óîã u ˆõNkÂ\`˜-“Kñõ±`R>$‹wǃhÛ#¦âx³îd%kÚÜh¥ñ=n ô[ÅýÞ|¾>WœˆÿY·pW–r|&O§S.(’KCKIyaĉEƒ²h¾Ý§Dö-¹C"^4îÐQ|Ñ>›3«Ó,ÚÚÎU’±Æ:3c:¸¼Ž‹ Ý‡íðÚøìw¶˜œqG`ƒçFvÅ‚'ŒKw©˜¼„<f 2F‚8Ó}&ð–Í!³ØÑNîdžùd쿳 ÷>"áùJذv©ù5„«G]éöíAä&¢aÛ"X{F˜îö‘Éϋɟµ0‡µ!©þ,w¥.y|)“TÈL£&†Xëe¨—B'k•eˆ%§žÑg} aÑl)Úc,Hm"Ì&òÕ³t0Þ߆§“žàÏðÝäLßþ«Gº:r”ÿI<¤*´C‘G7sÐâ*ºNÚfÓšNðÓ¶½^˜§&E:gÅâȰ^)}âæ‚ŸÏu<×90ÿ·­:N_y UóãsÍíØ÷å{ûJL>±d¯uBÕu¶e±(õüÐÀL•å$,íñû¾¸±<Énöe÷$éq.hÍ…ªn!’æ+…¯F³¹& f<㯊€[$ý±'^Üc/‡l †î6u”Ù¼Œ½³JFUnÂ_&l¯—‡ô¿‰xmp¼½ô€Ûûá+üœm4ÁùÆ"5iØ»m ¯&ÉÝZ^~çÀ°·8«Å(œüÊ^Y¹%×BÀ/Zß­N;¯£“w¥âÊ3:¤ŒÓˆuÖ)lCÚqÈÐÌfº¦ÕBäÒd°{5§–%ÏçŸØº.šž3xo¥¾ÚмxB "‚QðëaôŃN> h;z…yv@¢õ °õ)ˆw¾Å=[¸šËk!×D‚9`¡–mþýmL<£[pÙöSðÁ‰‡N;wO- 9§`·} ë¦¢Ž†Œ_|+6_˜´Ô8C÷Ç«LÞþoL—¨>º4 6äN¡Çk]PÓyñXM,»:ñsÉGFiêkVŒ™ ‡:°ÞÇ¢6Ûs¿P†˜ 10¿;œ H’ŠýæÄ°L“¾)Ò"Þ>"Ô¸ù$Nþk•àGÙú*Põ•¢+ÒLàÚ 3mØ}SÎâŸFoê7¿zÄCØ­&Rt[–;Y?I”4zÆñ)dz•ÉßPˆ'þÜGËÛØ'ưcÚlâ"lÆXšº‚äÚ§Ìé¶0¨~"Iâþ«M“ðéŽÎÁ—C+ȶ’Ý0v ù“ßAÚn ×sïZ?VN£Ïã-hÂż|ò3{âaù ^™/#\\Û OvJÑ[+UˆÐµsè¾J’îˆO k_jÐYfšXûãý%MZJ¡a}Ù˜ûkžzë»n¤¢O†XEq–^¦=iŸ Z·ÖÉ“˜ÏÆ0åbm™KÝ.øQÇÎu^W ØÝÔ‹Ý©ýÍLðì¾Í\Ö%–Ú‡0^ï™}íæIÛAÖʺYgJ7>ÄñKo'ü|w#ÔKf27ÍUˆ\V%;ò„¡µÃúdÿÌVÊ{U–Ȳ[6b…‘9îÛÓ}ÖsÇ8¸º>xº’ ç<à˜‡]ìúëØÿé9÷²ï0È‚–»™pO^ÚtºŒÑî)¯d°ôt$š–×qYÏõ‰ú'Üx™9Ý;Ÿî<'C|‡­ ©›@=6ðH’Eo&£­ï¶| v^2ÊŠ}øÂè*·¦ö*Ñ» ޝ$©÷‰ l§<ÇÃgñ;¢â˜N´·€o¢;÷ï¥ 4±«n§Â çJŒÛeFrŽ;ùµ“HýKügBÓ~'k6$ó&ÉAòøl ›»:ƒö«ÓKae8ù)qs'£K·™)4d¿<³*„Ò¿ÙŒyâôß31kÃ[ÀøBݳUdIg¢üDBu®ã®*YˆüîLS½!çxò¶Lð¿Džaø²ø–­¡"ìhŽEIá¡3˜~ª½~vßÂÉâD>o!96Úþ¥+éõç!žìD%ºde,ÁÛEr¬$Úë+¹¥û¶¢¦Ícb2Ì9VÉêðèÊ38\ŽFV´Ð\ˆù·wNB‡ÖÈãTÁX,—°åرšÕ5rG°€†Ÿ·ÅŠî^ˆz±sF¢8ß/É“»ªE³ï6êO·Âòô.8Y?Žcc™Š_*üuµç[u‡°“:àÛúk(Ó õ·¡MÐJÚ³þ"nMÓ§åï–3qK^äÏ@NýÁ}|»ô×ÓâÂ}J<ÈE6ìÆíCÞ¬q쬟!Žag²¹ÃsqÏ„ \%Ïî פϒ¿ð•NT¯ºÈ5êRi݉ô_'s(öÀ§Ý’Ôª3ˆléq¡q¡g8)?iX2GŠ] ‡ÎXö¤2™§úôzù5àGÕ‹´p½ö!´¾0¤GÅÿq:¹Áó0eÉZZº:šîm Éÿ™dФäú-‡\1QH¹¡Goï9Ææ°:e[°m*Ü:QSg¢ Ñë4ÚNÜåøVðÒ0¦Î˜‰ÙÒ÷ptLðczp¦Ëv*á÷œ7ÿd7jÆ¿ÛÒýh,ìr„s;"26‘ € |›çvb·R3©Œï„[©/ÑSoÌ}~Þ>©Àpv¦ÿ¥RCP;2¤æ7ÂSg@~Ѽ {:V¿²t¸yû¾ì‹DÅÌZ.'^Ž5ÞœÂ:Ý0³Mc”ñ‘Å5𝠧ìÙ«æBΠ.ÆÉÈþoÿ?`f«å¦JY‚Î]W¶:ù¬Û[@øÕj¬Åy« ƒ±.Ÿp, ßdÈv;FtþÍç^˜'Á£L™µF׫³ÈNG6Ã0ß Å¸yîhöv’†ªÜÆy¼G WÜŠ¹ù²8_÷¾jæTm¯p}nÇé穵p;$-³`amǹ³× 1Hï.ØÝ¯…kzpß“Ñää†z^ÎOÔç¿Àˆù*Ô[â-D̦õG¯rKÝüXù“»àò¶í3þr²Û3áþ!¨ŽÂ}  ´Ï¦´²½uXF½ŸÀIŸ¤‚±£ðçŽCÿÛÿSO‡1C Ïð6þ<‰6WÒÏÉúôà»Iر® ·\$]ÊÀ.¹‘!G¶¡¶ýÏBÛæ»äpæmÖ¡¿®ue­eòtÛϸ\ÅZ?¼àþÍ@¯« {õl>Cñ“ÒX±§“îRò qÚúÜ'ÏoðMñ;v­”‰´—PÓ×-œŒÞbZó\Ž ©²Y¡¬0éÕž¶ý,EÙékBtrÏ "$ή 'Ò=SÞA‚î^¨…ï <=;ò&ÂÛGE€'›h䨓0%Îà‰;JéNdSO„ò‚×[Òã†5LuZ:ÿñ¸´ÿCO¤ä-Åß¶lÎŒy1FO^ ç´8§1M0›;k†¯Ñíð”ƒrÞhÖ‰‰t…ã\d.qœ0_xõ”Êþ‰×Zñû¨qTã±#pÕZô}ŸM¸v‘çN¾\¯æÌ4dŠF¾ÌÎý Ù¿'‡>˜˜Gé߯;a+pû9ê;g5¿_‚Ê÷ ¢‰|5Þ×G?§š‘Ë_Ó¦‰ý°ÓRƒsWI\,ͤÛ|$`ö–©6mñ÷`ÚÙk¼ÔÇ 4îØÖÈÓCÝ0£\’)¼_̦_^˙߈€[û]é½àÿŸ[ eçíµ—+RO¶£Røœÿï—¨>5š›öp!=·VMwÏÁðÁa>»*vÚÛÍÑÆW à}ô4úäú%TÞ&Æ+kqÏŠN<Ü%ÒÛáçÐ1ð8V‡> þðâà_'Œ+-ŸBxØgî݃ãh¹E†>-þ³×øÁCùhصÅ7¯úJ®v-E›P3üxáÙž¤Ê¢TFÑó?{/ü÷\ﶃ‹Ié 'Œõ³`+¢é[¿ýph¸W'àêG¡¾_šæÎ(l¿ì"9¼wE¥O´BöÉp–x©šëGü/Àa®Ùkn¬Ñ1¼&LÆû§ãkÝ^¢“‹ïGƒ¦¤ãñØŽb!îÑ0ê1el-¨[Ÿ­½}<›ááM'±M¼Š¨/E³´:´ƒ@ÌñbðEXšº„„¡Š¶ W‡Pí²?_œ¾XíDlþa?ëeWLâÌÓó y†: ÷¹„Æ+xv¥,5>R@ûv¤v^GËzxÓ†ò»ÑܾŽ~Þ®ãÅCðŸë™ËžjŠÑò]ê¬5¾§µâ½/qAÇR渫Î~›÷2KäõŸÒ]t ²iùýGFìd’¿- õ‚$ÁtÉm¬®ùÒópMð1b¦µ®w/¥‡$×Ãk¡Oœ¿9‡ÑsÏpäÛNˆ V=ÅÅ7¹û1Í@¢3÷ðÌ/*ÁI3Qì»ÚJf•‰²øÒ'¸Œ†ƒ]¦+ÊÿzÇí=2ÿÆ_n‰itަ3}/­‹ùë°Ã¿‘ómŒ‡ÂÆ|¨ûM@+0‰ §‘ŒÅðm‹€1¾¦´wæ7 ÎHÁõ1ep¢^äíßâþÐóíÎ)›‚a Šø~Ê|¸t§÷Œ¢ÿý¿óäM<ã_ƒŸEMi}‘¼ÉõÆä7ÿ»ÿõ'Ñ=¨†xÞt`·Äèòì ÌãªÝ›å Oç!8íNb‹þ©¢ü¶5ð±y[× }xÊgt8Mqƒ  äÌz sÔX±á"ø«£hi÷rößœ´wÃ<'p "ÕMØwóI`Ư×*x˯2ÿ‹ÿ*÷ô…¸ôIkužGîn ííNxrºsÎMÅg[6Ò€®³$øU”똡üüLBtöÕ¯qûgoTÓR'Ch`;€ñï{EWè@ñöËü;ü8pŸ¼<¾„ÉŠkb÷0nî]©C+knC·ÃB§Œà?aaþ4/˜1†š}ˆñ~idiK9‰¾wì›°ï¢N?zû?¡‹þ2Pó"g>#UÆ…\FsþÎQBÁ¶ ÜÙñ«º!íÉkC’5–Fá l?9†Â6G“Š_…Ñ눭ï\%iFSƪ²‡Ê3ÙŸx49$qϧÀ`ÙK˜úÔ èRJ‡ÞŸÃŹÊìûOM¶Aq-,zêÅ©iáêʈšw/ô9‚tâhjS¤ iÚŒ7é¡!ÚÉý7;‡våbF Â5WzÇ5+Ú¤D{Ÿåâ•#Ѹë¥ö)zÄþšçAüòc€_¥K—v]¦Çáü¤½øÖö7Î|#^ø1c;!øyÿCêiPüÕENù'AX³ê#» ´b,³ñÿ+rÔ¡¸"‡®Ý—ì°Ðï9ç5^™MÈiçMÙªÏn4ñg©lcb=^hWŸÇÏž +³NÁ#¿|ÞF%:)Ÿa’à6hܺmåÈ«›Øº$ kVÖ“ŒBwÜ/Ä“¼+üšué(¼{lŽË… ¦œ³€IW!Þ³ˆ†ß¿ÎðL$“po•À>Tû†cÉã0òïT%¾épã%9EÀOÛ@Üð4g¤þûEñÉe\ZžÈ­ÿá >]|¢å<ƒ<-Ë!×φp¯xöXÜ}û“CIÛÓýؽ-†¿dgI˯ⴾGÃN—rbŸ‡ò?¯¢Å˜ÝÐyy׊BëÝ@¨Jµ¦ÞÑDêÊ|,{‚w¤o€\¦%ÙþIÿ4¼%KÞ@ÿÐ]LÈe¥f¸á¿šL»™Àÿ'Ìßà(ŒJxÉ-FøPÀ¶þDˆLI¼ËíöK'EÍÚ0±=‹¿¯sÛе„¹mj$i£Ø˜‚ÐæxcÂ@hÕ;½›í¶‚ëëNqsçÍBËOq_`\;o¤þ/¼£DÔ"vr)gl©ÞL¼Y’LÚµä¹rm-.âχ{*Ê!¡ L®ˆ³%±ðhåW2àîƒ-®è³ý:ïC²VìòågðvÞR•`O¥õwËq&xÌ“¡çf“´ÅÕðü_48ýN»‹8•cõõ½Öê/ÆE¿‹ÐqÔhyÁo‘0àE¯ÄÃ?G³v 4ú.Vbå|)Ê­u 1Lp½,uOä× ߯M,`U '±m&õp:ÆŽbЫÛ<Ž=F§|Z¨òÎ}Z€£šãq]z7|¾ñµ¥2yG×`ùd}z¼^òLj1Wá (_ÀÂÓ(JMÄoóuFìïÍêåjÛŽrÇqèº ;±²·Ÿ;Ï¿·þ÷Ò™_ÿ5ϤÎ{AvǦ‘ï”°íN >ûU"’Ô¡ê1é Ì] G›;BhãTœWÛ•`õ¤­Ôé®>ѨäèTˆQ¹ÌÛ¿çѹ3£½Ï¢à`!®.&Û/Œb'¶Äqªim0åÊ\ ÑA—£Í܉iû‰xÆ |¨» ’õæ¡×q5ƒÝÖ£ÏCŠîLóÆñÎ`ûº·¦¥5뮂¨Ð<´rK‘åÿÛÿzóG ß¶‰áâYÀ[4¯ÿíêàê•Tq­S9®¹='­)Ä_Ѫ(žL'7£ØbÊŽÙíàYá¿CfÔºN‘ÖøPHšzTJ=ؾY >ž¼ÈýÛ…®Mšì™í ÿ­X¸Æ‹SGùp½‹ÿßL2¶&]G’+ƒ×žö‘'Iƒ¼n5:±ß þăO‹?T”WÔ—­²Æ½Q”KRSèÃöÛŠ1u1ukÖòMYiAݳ?àˆ\5±<Àê‹®úƒÌ~_Ü8= –楷ÉÍö'‹ø¸ .Ï©¥ø\`ƈÿ% Ï®¢•¼âBŽ;Rx$ R¡k0{/ˆPÓ¹OqƒM?L3zB>¬ÇÛ¾¶ü;ÇÓþS©PCï•aïÒž ¼?Õ„§¸G‹ÖéX‘ø÷;AU/–miàÍQr¥’àS7É`{^#®L2¡_ÄÃ'seÐÑÏ)òƒñŠƒ³üBJt[¡ÃfçÛÍÇ3‰jP/›€ë¦,F­ÐT7™û“{#ƧïJÌÜC?¬ù}דÀ«ö ?èÐ#ØzÙÔKþ‘×»î`“ã]îèVÔOY ›¥$Øâ7¡ÀE‰²1Y hù£ ¿­bÄÿùºÙÜ•{ÞÄlfŽ*ÈéJ¯ã´ûBðþ’‹<ˆâ}ñжë<\w‹äœ¦4öζæü©9®ß ‘1âxèyÅM±¹†-ÿ¶ƒí’`°¼6,]ÛÄy×2n¢Þئû w­Åîq.L«÷6Ñœÿ7½3fí“èý©£¸à¿ñKÛt0﹋¶õ ³"V<‰û7ª`ã¶-x(Ç€>œá‡’8ª—n†íçã±s5r÷ñ¸‹†9Ÿó®4áwx´!ýª­Ït ¤qßY;œ)!·íT!Ýå,úMnå¿7©á)íã:#ŒÀ×OVe¶ÀÎ"òÈñö}þS‚œñïù“Ä¡~ ìzý9s-H«¶f³d `Ó»²üg6c6üÚ ™sdhÎÓ Ü¿Äí¿#¾œƒôùv°îîSš5™ÕŒFÞ möìS-g³º›¶­ã–é”Á a\]ªF=7ߟ÷Žsµc–Ð)¦K ñ¤Ëš:»Ò|àåmú«÷Žý¬Ú620ê/(–<ärìx‡j1òs7V;Üåö^¿lïëÓ O:B±Éä ¼Þj@G•—c}Ч}'8ó¦1P™¦&›^ÉèøzØN~´‡>Ý𖨯Ðgž‘©Ö´@êO§·»²Ðr!Z`Mûš£ÒçØà‹ãäÚ±4Ìi »ŒÄ¿«d4¤ÉÌåŸÜ‡–F²ˆÛW!Ó84ùçaì<°x W'z¼ ué}ù|8Ö‚^xòÙ_ô £ˆ±Â`&^W™ÉDbÄéÞ7¸wç­À÷Ò]n|éº4u~¢3TåA×F”U?NáÏŸÀT^’Ó«ªàÔ¢|îÁ!+Z£MBRó¡0F {ó¦€DÏjXýÂpžºlÕ”BÔ»'3|“É—î‹dgø[¸xJ>ç>‡@¥0õ1“ËÝ(K5Ç~ÅПFš•‡ÂkÀl¢ºÌÁþ«”Î…ÆÙã¤,Ö·àO­\“çk@Øû‰¨Ø=u•µŒî0™‡Þ¿®Á7Óe`²â(:,MÃX¯èýõžë-óÀÎ2,·!|ïr«W©³˜{Æ8ŸøÀ—»Dàh*^²içf ã•Ñ‚TBm½5V„Ùh&`Þ›œ ý‹ó?Pz7o Н|Á­è‰ÅijØ’-ªyNƒ¾7ŒDÓX5PI<¿ -Ð.3 nÇ% Cß2šú×g”ßç$Åv‘´ð¥( ûêÊÊqm@0y™­iÞ t”v('y5Ÿ Wwc$þíëå¾Ì†‰‚Ðu"þ©RCá~ZrÛp˜ž·-FÁƧ(´á ÎëYÅÞÊsƒR¾Ô0t ÑòÁã~[¢ÆÞævBå€2™S}ŒáÁ˨(h2J‚4]†)o†ßdÊp|¯c·Q ù©Ü¨âVÎñú ÆÛ=…ž5fŸ}ÀŸr]äJËÉaìm‚kŒ¤ñá(ú¼äÊT&áú³+¨“o<÷p…?蘚â«cáH£›4õvm …{y‘¸âœ~3›²m;QV#œªW™±Å6@YR‰ÎºµfTiŒô?“Äå8sç8rGf9>¿[ˆ©Œð‡OñT¡]]ªL¬`2X˜?…PѯX¿»¢TÂÑëÐ:Vƒ=Lâ±rm#ª%ýꂸ׫`NÐ#œÜ/NMm9Ç?9ÅkÙ“uúèuô0ÞÚÛÎolwf×I`÷úrôX¶vØWq¸F×’©ý„´Ô l–Á©1y°Ž¯h?Óë ·õC3§›aÍ &ÿ{m¥„{}MihŠ.Ë[´vß<˜¨° =¦Ö¯©zN‹*0äÖÞh š-E‡zœqóÛ\H¸§ÅÆž ¡E^pÔyшÿÿ\Ææ8wøqAœH¾ÂÿæŠíV8² `§ ó`”} n˜~‰„mÿÂ+iŠ \\7þ˜{›ƒØCQ3&$ÒÒƒØi»Êí4T,Ò`S–ûóÜ神Ôzo ”^x†mÂlgÝé®UwXê£TJº e…|¿èßüëõtM¯-ÍÓiã´ÿmƒìí˨–²M*´gªËLÙ_ÿoD®¡òWiìò ŒZtؤõ˜§³†ÏÍýý° *z0bíoÜV‘f±c $-²A¤0²0 ƒäËi0Ÿ½âÔØð›ï<âÿ@Q„DW;êiá6ßdÿÍ÷[*€ç•8J¤.ÏI·ãL+qåÜÛ^cÿÍ;Ëµí§—Ã¢0ðV• ;Œ¡Q–Ï9÷G†ÔnK"­I›ÎHrÝ©•ƒj»É¥ '–¹<œÎoT¤ÿø¡4þÒ^vlÔCT¶[ Qm¤OÕ’îœ<ÕÖ+ÓÁ“Í å÷ t¿ì±ÿj&MÒƒµ«@+û8yq(‡×¾Yê.UÂÒÀ2õmÁ‡ÏœTåsÿÇ ÎCoáSÄ×ü$‘1JÍÆsDCfÎ6:7n¼‚r‹½¸õûGx=ĽÞ~Š|°¼‹ç;Wr*“ Aö-—¶¤¿ú-¡ÝUÕÜŽÂz¾Ï^úêÈhZôܯ«Ú33¹,œ\ Îß18sž>^ +^ŸÁÑÌ£H6Ï×`*ô/¡?þá m´¾‹g^¼é,’óŒó‚•zÀ+©[6šr>¥0ê :´ÖCH»6¯,ºŸ¤§AÌ(ÇûÍÅ+ø÷¾’Í-)xiË ¼ó ̶ÃÑeÓhÞ£/˜±,››lÓ…2CñÜd~gL‡n}…íü•8X“Œ«ÝÌ -Ü“^¼EéA3)JÁ_f%d›€Þø©Ð¢ÑÎ5›‰Â¤;tñ“ƒö£™pób-¨O— -B8I,æAÌôàv©°ät3,“»ÏñvבÑ3¥¨çœZ>¿ðŠH/Äã1þ ?Ey¢OÒÖ=J!Ý7bj¦1›ýè ¼ûò™\=†>PiÅÄürœ"1 Û¯Dß8®ÿtþé‘a‰®b#øgM`³ml´6¹PT‚ª9*pwj?ñP¥…-kû¸¨GÎ`|~{Jx4ÿn! .—€äÁ(îŽM‘š¸³ÿ³p~mÇ_ÒoøÈ =h¨G·57Hö’$x¨&žþü J•98h’‹á’ÔIL éüØ -(Æ÷þ#!ôY+€rrcÐcâSèüýë Ϣܵ"Nú€(º«\&6ÒÓGNîœÇLºYé1tû%Àþ»ŽqµqêÖ0LZèI7)†b쓳ðgm$ê‡ ³ñ†P=q<~œËŠ=ƒ¹/oŽÜÿþæy&túÁ~Gy"³?w;êÒ¹*ÜÞå—øÏ ¬˜á f÷Æž Ì)ÆÝ3“¹ ídRO„- aVÏ.à ‡—(:—Õ^¦¿ªsVš´W†&Ä]æ7<…ѧýˆnE5zª·['O}×€Ô‚&X¥WÀ^(ÏdbùRò"ÂoÂeè¨múlÆ=ú{M5[þƒ÷B%ÙðÚ|,õ6güAwcþ‰’IÄñS.ÕX‚ôYT½¼ž­×¢‹$iØÞ*l›¼GmÊ‚Òa^Ý¢ÚW[ Çh˜%ËÑ »áµÇÊ‘ýÿïÒiD(YÒ\Ö¢åÁp¾߈*ÝÍůÃ6æó´ä½çŽ 1Ç«ÇèáÆ±t˜3ïò¸ä•ÍdFX=Ú¼˜„ûŸâõ¨(8lZF^&~Gëñ‚4òÕ*zYø7¤Ä*aCõynFr×f}ÜÈ=8ÁÅ#Çq° ¶Ÿñ$Ñæ{pÿÄ{œo.Ÿ¬y¹ŠI&GãÔ ê<œKú›üqFÃ8NðÄ(œ¯AA£Oï6á¾Q3ô˜;{×ÎÚ¿f#ùÿ¸ÿ-?R1<§yá½°…ÔÄí±¯¤«+‘;+ã×eÀqO9Ðû#Hj6,b:!GÁeÑkÎwsûýׂծ1Ä]Ïeww4ÞQpÂÇÛæAþwSúÑ` †?Ñ «aËGc\£hÎ/)ÃÙÓØO œ×mͤ>aU_K±ýç”äÞ!úov³’b(oÇ 6ëуOð¸Ç4\oɉiˆ²m}ø=î"ÈŸu-)AòM¾¤É\:竽´nsîq$É>ƒÜ‚)ê£ZMÇÉW¢àå"5t8äƒþÍÈØïYp*õ؈ýf®õg."«ŸGbÎY+Ð)Õgóuo Œf§nn‚ М)Ì>Tχ UE0Ët: Ê‚m“€S %b·–`ƒÊOP¨ÄÅ…ÐÄÛÚÌ$د2 úlÀk{ûÁJAŠžúøBLJdç«ÓÈ£Ê,ëçC?ó5ÖXÑÖA}®ëK0èÝ#Z•A°ãõ¶æ¢ÈOšOnû©) ¸m?5æAe/ò9­îà¹ýF-ìƒÜ’<œy©gŽÌzc.¬t¯ä%Vƒßà ¬ók8ÎЧ÷¾»Óå»ä…Ýy¬·é„`Ϩû3¾›€·m Y\kKŽl²"ã^,!]oq){Ôèèì–¦z- ãM´ÏäFÀ zäÉõ¾õa§žá»¿]Ìžä|Fý䔿 £Ûé6nïÐ1¬Ÿ^3Ng±iM(½Ï‡Só…°;»ËÔ—@ò·Ÿœ¿ÏcòË.wo^ — Ó`ÍÓcD/¼Öšóëç’Z*Èö¿ÌäÇ“e*†sÁáU\RüQÚAUqˆ›ê¼ïM¹Ž»ç@ßÛ¶çûŸ ¹f¤ý¥,ùñ×’|] °4„Ûô5¤ftqW7½"¶ZmÐTKŽÔó[¬ŠÉÐêxîø T,‡AO]ùy QQt.?#°_¬Ê*Ÿþ„NŸàÓT€ó³'±Þ7Ælâ¾;X£]AJ!~«@T¡-{û¶«ªlX×Äe`¹¹17¢àÈœy{w¿½BE&r9‡ÛÈO—SœX·Z­ÛJf¯ýˆ£J6Ãê¶”jLåê='Q×ïȻ]Õ$zð š›ïçâØôe1|{Œ™§™ß'î£F¾ó[]1v~"XÔ^á?Áú‚œº¾Äï®Tÿ‘Ýv¾¬^AŽ-©&b]Þ̯ì êÿ}Aå#æÔd;㞢·%kË(Ñ+ÂèªGxq,¾6† µÌá”ÜGS9Ï3tÃóÌöª„£¹›YÕ1ÚãV‘B®ðßüñgK`ɺìɪ—`ÕðôëÓg“ŸÀvõD”QP¥_ö®"k¿r×JèÝh šó÷ Fz]a~*©\f½!U½&\Cq¢7ðµ¢=4ê-Ù&zƒÙ$¥p× 0®3‘íkºß“ÙÕ1Ò°¡[”>³TáÇK0Å+‡mþ=‚ÿîöCOß›0y‹õ( …„wF`qj ­™¶¤’tàÜ«rØmcÏÊ’„ùme%l|§'í¶³…Èug`éÁ>¼š½Ç »rÛ–rË¢ ü]é‚ç_yû¯ÊÓ„4º÷^ͪ†ÔåÁ¸1"d÷¼§‘r‹ ßÁ€ú(-d*ó™ÊIv£â1vùx³ò½§9µšhˆk—°Ok_BãÏÒûz+±>~z4=Ǽ»YÕ­jfâáMY±Ö7ÔÅòw´¹Ußyj·âí1tƒ-ó+„æÄ΂×KìØìî»üe«³A½E™m×\5ÿEû0rèþÕêÀÁ³îœÏµmr ‚èmou溇RônĘ۫OsÛ˰{ü]PìëÂË3raîÜ3pºÄ«¾^íþ:/j¯å%ÀZ­fœ—9{£0ržÇòÖ¯9ÒG—²³ÍéuSG/ãÇXÆç‰K1éM>Tö€ÝΣð™ö |`2™ša1ÛÏÃí˜í\ã¥Ü|i7I˜Ëœ ¹kë±àq¼Öà1ñRY2Ð!—<æ‘©šy¨®3™>>úlF_L‘4žÞñjP9Z…%>Hþ„-c…{ûQea0·á†[?[aÿèý®ƒÍ¼ü‹Öw‰›ïOL~oL7}{U.@ÜôA”vo…Œò*Eœ~º‚ë·Ì¤q¡20ö…Ét‚“K0Æ ×DLj2”XdMŸt£ç-k 0º |. ç›(e’Qž¡XÿU¼8‹3ëæºq¹pËu*ª ,ä~ Wà›×¸öb‚Õ ‡—÷¿ãê¿æPÅ l½~ 2â ©Ùä½d¶ò86«y‰’¸´L8‘ÆšÇF¡§ðoÙJ6GAŒÉ®çH¶Ž f„~'¿ç(â9™¼¤!ÅÖõ¹âÛତ;b¿ZëKp¯%–¼qì¡q­¼wÇÀц!¼¿Q Ú ƒúT¬|ÍàªÇ (³LÅs#–9Óôyܘ&>6½c{´àh¿.paP|.ŒëŸÂ'Á˘mL½jÄ¡0!ú\E†Îúû_í ¬ÝÈÄþ`ÆmVz/ LVï¦?Jø™ï6Á½sé('Êt6á œòÙ‹2%9•æwÚ€ýñsP±VoÎÕÇ®²¤°¥œsñã Oªbsì ☇ž<[¦oIGo±à´%©cù»FÃî¡äí™^ûŽ÷5¤âE<áY€»{ù,–¯Õ3‹O`I¡-½çŠ~ ¸ÈˆÇšÎš°«?Ëpæ0ÿ€™Ìç±6My/ˆ<«tÆ7oîEÖ ì_{mž 7bF òTïÛlлTˆ!ò1¼êN/´dÀD½éÇg©ØÕ¹Ÿu)ÓŒ>`åz%#޳fëìÿ£îOîR †š²¯ÅþR˜z2Á`ýåÛåW£^Ö‰¸=Æ…oÝQxÂ;,˜²ô8 öv ã¿bò c¦“ÉdË]ÉæûÖxa«Û½6¶†oµé-dG6ÎSa§ÅÁ™Ý/aåFYæÕw“3R?ÊS9l‚kC°Ëãì낌œqøË_…>w L¦ªbRÞ$H®8‰÷ƒ§à†eñ„›¦õÿOc5‘¹LZ—H²ýÆ¿¨”.‡–*'¹½¢´Mæ3nthå>¤ˆÁBâ6•î“:Äú )M­ c>‰Y$7’ÿó¯ð¸¡¼©P³à&>î‹7^°;+ÛQˆëƒß!^mˆ»)Â" ‡Y¹)· V[çᨱÿpÊ~QîZ§$F~?‰.¨¶v"xuWþ÷—Þ?¿ TiÇþ©lŠÉ}úå…´ÿ™°u[ç+ {ñ3Ür¥‚ûXI̺7‡'q·­%+ì‡Ð¥Î˜õ¦Œ'Ë¿,€'!Öô åahˈ;n܃…_Ó9oý(îÔüt|ß.F Ì¿cAã#3aÄXb+á(äôj1¿q> .·žú_Ëë:eLËŸcHZíHÿŸ“RJì'à½?ì¿ùæH½ Œë˜À¾º¡ÖÎŒz­2{ã˜ÿé3S5•9ì¥f4ÌsÉÀÓá%×"Ïf|aG—¿Å¥ÂÂlO%¡žÇªÙ3‹(öýÞAê[+Ϩ,«âb•‚ap¼ØÍä¹ôçpZ!6ÛZãHžžBÇbþÃpÚš5N{e¦³T«qtÑÜ\°ù;—îÚu“lå†ü¡/“þbྣ!5)bÇ•ŽÒÕSéCI-GG5,ÁÏc¼Á×n¾O;I- ŠxGUÕaŽp%MQ¿ˆd¶²œV¥‘þg²X¢gz“ûUuxúßLT„ÇÒÕ­AìYÂk :õh‘> ‡b»¯ïwah}ý4/Ö€À‰WP+Ô›¯¡m@·‹ `Al<vªÂížþ,|˜5íp¤î;g`̦9Üð:l(7˜än LO=†³ds›Ús¡}´'wíÐü´n{3 ÏGó0'ó¼‹_ö%!t» ÷åÚvê2ÑÊ…†Ñ#oƒiZ7VÅ\¾™Ñ®×\áDQ¦ï§ËÄ ×”¨ÒG¢lŠu¨™Mù¯Á¼áÇ1ÜÅ-8ÆåDvŽøßÿè¥z¡gËpï²k0ÅBÿc~9÷2K›­ ¶b­xD)f$OT¯_:IŠ…M`Òêq‚—½z~œ8t µÏ΄¯‹V£Æý±ôw g',xuo/Ãó>¹¸ßsö&xÃÅgå°ÏÑ6:Î…— ôùø Îÿs˜Çî„êÎF²¾çØMðÍ zñÈJtá†{Ø<16c‹2CY›YqGI*7€ógˆÒÎ\$Ê µ¸¾½‹ÊAÒ³8¸%MDýñú!6oŠyCè¥üj²¿m1 v?Šz5DâOv5…ø?Öi5¯#mâUüŠÍké½C°Ûq{S>T×q¯2KÆó±¡é2V9Ã/•b*»{î<^ð>÷E(/jÄ3'[¸¬oáêO,–#÷™á…]; 9z4M›m€wºq­‰&U9$Iÿ›•¾rŸ¯fI&<¦b»¥piú iÅßù,÷ãHü€¿ViÐ7عІi>]‡-?r‰Uík2åá$0‡Vÿ¡­b• `w Ï– ³še $rÅINbý_0YûL»ºr‹mèæþZÒûb=,¡ÍܵáÊRmÒÿ`¤ÿÉÎE²»‚È·ÐjÔÚ­Êã–½?Ép¨Ç§õ‡.ÄX—|Ÿ÷°^»eük^ oö[`a˜<1Ï ÆÂ±™Ë¹* ¼~ËSP»â.÷¸®Œã¼ÄÑÂãÔßvÇÔ¶4îé¨Üü_ høÝݘ9íÌ<\’¶ƒ¬3ˆë—„â—#Pì~‰|èÿÈq‹4ÙFGØ9©_|v£MÑcÓÎOº«Eç­så»L[GÕV¦;y\×¢ûá%¡ZÌyw œR‘æ_ìLƳ(~WžÈâ~Ÿ…-Ó>páŽÓ!Ÿ ‘¡Š,÷Ó´á5%GüÿÁþ)W=©‹XÙ†e_$éñƒÞìoÂ92wÊ<Þ*Ìä¶â›_‰{B‹7¹ïK™ÛŽ;œOíN‚3žã5;+ø=ƒû~ÔœÞ9¯J}æAþ‚”c˜zn*®ùhLkö•=ír|Òb²Ç9ò>ã6Ôµ‹17Q]ÞŸc˜Ð¹ WM£_jÒ EOA®+€úÍe¢¯Á­gqÅ=ö=àP­:4ÈS£–gÓ!yË4Œ¹ AÚR\þ\]jœ'B÷vH°‚îϸ˜nB­¨Ô{éNð—•5~ ª¾APcv¥îåæéìÛ®Qÿûýç!)r)ó1«È¹:æx`¯(=¶¨S…<ؓך°KdúÛn¨W¶>Ç©>_N›b¢ú1¸p“aq³´¦3œÖWŠÃ}òÆ ]”¡ÁS»œ¯_YÚd'x²Œ;õp!óÕ۪͂éö_æµ/>ƒ'>¯LoXáNé®·а›®f˜î #o±=¥‘’ kœJQäÇj دމ¥dD3Yh9…ì«AoOKû+ûµéÚÐMà~+n$Äò«T`̼1Tyt) FÌ¢v97ÿO¯ZgÃlÛºŠ¿Ï©t–%î6ÅÉô2ÙDŠ®×}…þÖÐ+ÓÔîè‚MpçºÚ•šàª‚ÑêxÔèNmâ±m 7y:žÃÁËâ8¶8Ž l÷Øz š— ÐRÍ•ØÞÂîÇ”)è;ë2ÊÀïYáüL1f%•˳éÉŲ]…h8C ¼Ï©±Üt¸ì¦Ç²J(¸œQâÖ‰ÄâK—/¸/æ è 4¡~‡!&¯à9ÎÅÖºTÀÌ;µ¸?t?ö¯ý‰¥ÓÏBȺ{l1¦~ÍÐ þ;Ý\0½d€ÎäJ¥%-éi€ *u¯ùJ"*°î­ÍˆýžŽãñˆ,J‰lá?­úÙÇ#1ðD#VÉdcÔ‘±Týõ2 ub?xqºiÕ`Y|Êrnã†:qÚüÈŵfã¤%?øÌIÛkyfJ}ágÀ^òá»o#È×â×aŽrní%¨¾¡E6·pÉ_ö úò.H¸Â¨.Àwë@Ëù*G†{¼„§^}¨k7òW³8{˜pf/b¬!Kš%ƒò5…ä¬èi¬}üDžæ„&—€ÿUG”³»ÎÓ´Æ¢«¾Ü{ó øj†þÓ N­já¶‹Ë+aË”|d/ŸŒÔ¿¾ÔSÜ›é¤iè~u&©­„ö¿áèd]h»ò›_Ò‘34ü}þ~ñ¯ ß‚©Ž”•±¶o-çcm ®S¦"ß%Ñ5Z‘ÖMZMo~Ù‹ç”C˜çIQ¶yåV²3™ý›÷¼} FS+Ù0¼ù=¼f:3Í™‚ü7B¬Ê5\¸Q”q¯_q»ûd˜Ïöë|‘£I¨íó¥Ê s…;_áÐI¼cÍ@÷{ ê?å}ËÄ¿š¢´½F•È­"})øï¨ŽZ\\Vìy5–ùZöµ½ ºf»z9 j­¹õ±˜¤Ý=âÿdE20…#Æ©õÝé‰xx ·Þ ¡?$3$c`Íúnw¶1õ{eÄÂäö±XÜóÞ²NO¢sÓÉvi]ÐOõ§Ýé£é,G1:˜"ÎE<9µã…Ô×ÿ5oÅ™mLØv »Ú¡N÷¶âi(bÕÁDš3…hº1 ãehYÚIF‘§çìñFeÜ$;jŸcðŽ³°Éõ7ή°%OeGaáíbîåßîŤ|TúXÁ•]•çûñ¯Š-N~x6a¿äI\#L£’ÿ¬Nö_·oOÿÄùÞ=™eòÐix~Ò ûÿ÷ûg,\Ím»Ü_™aìNlû{˜ºëý…‰rê4Ñö$?ý°3ƒíÙšËjY|·œkz ·pSÙ'Áä£^`€J¹[uÐ7ÜŠy×1ÐÀ»?œÛMÍ-(ò±]iya5 ëí@É)¸*y \)Àb[wjªˆ¦‡ÿ‘rßÉ©œÜׇuÓ fàLßl­‘=“QWÅeC»PÉb`‹ U,ƒÊ×DÀ'æÞ9&*ôñ’ 7í¨šs"r-©ñÅ84¾(ÃÖ²êÍS8âTŒ¶'ù+n*ÒÛ ÜÉ<ÿ3#ö{‰)ãàï\½P¤L›Pv›<Ù! LŸDšÚå¼yËÿ›¿Š’ —›O¯"cö¢"’|[é„Ö²5ÿé?ónNn Û¾&Ô¿m:†êt ¹{¸ ùÚ y¿®% ¢K—ÓT  w`̦zܦ®¶ï6²ÍNŸ)®a+&&ÃáÇÞ,H\&¯ƒd•îx§ *eŒ¦â÷1Es<.î½ÁùŽN·WœQE:“?Ýø û(Siõ+pý…ÍN;Ð$ô6ŒaWªÙu§uxg(ÈW‡ÁáÏ;ñoê}T½ÿ×þÁ|õx‹½ ˆÜíÛÐ캅íôêݽÞâéåb|£ÙkàK,H7¡ê‡·’Ê[Ñ8OùlœòšåYX^7NÔ½¶3B¹…Ñè£ÃöW¦@x”•’Ó…]š`gœN:•ª!Óe#MªxJÊf_€¾9Kèåí‡àEf1¿k[²n{¦QÓ•YcK,[гÿžY~;>d_ßäŽûŠzÒì™ÛsóüãŸ~•[ãç·«úÑ(j6,__?¡ºˆD,”äÙÇи*åf!æ¾L$Må‘p¾g"}{F^ïºC$n¡]_¾Ãùâqdº!âÌÐcÀ¦$à+Ã…Ü? #Øøë tùÏƒŽº)þP_œ¯…·2N@ŽTÁ>ý¹0±—ÍkNûJ&^k³¥BÁpÙGº-w']0c-Þ« „K/×rþÍÉüM]üs4qƒÓo¨ò Æefìîã6±ü f¹½Sw²h©Xê¥ÌÄ" /³$’ñžT»*Aç’5xúÓ.ºtô%ˆÍÝCz—ŒØ2¼™ûû,k»cŒ¿#ƒHN‘ °Í¡ÿ77ìÐ.-j²`&ÉduV°ƒɘ€£H%EùÎ…º,`¾ÛÛq‘»>FˆVWFbËIüõÖ€=Ùø{n‰áнúüI¯†ðòdSˆéÌ&…Õ§¸C»ã1ß&Ó}‡Äù&P«Pòuý>¸l`‚êÍÖP…ø¡è&ß¿×ÆJíÆ·Z§8Nô×=O>>^€vW9“#Ïð°H*èE¢‚îù9ì“§¸rìàæû‹Ðq{ÊðäÓ…˜véÿs£4µ\æƒc?oC©OÀRt—õp¤ÿxD#ï›…]ÃsF”Ö™ÉB·Ÿ&F,‡¤úÍÇ?³à“ãðt&Ì’ÕÇ'_*éá›M¸Ýùö?‘bVÏBÉèÇwáì×4ÜT؉^~ ‚Èn©‚ î¯&®Á °[Ökú †ÏSyYý¬ö{]&5¹ì{ÐY¯¸Ÿv`:7›ájÛ:¦¬8‹Lk«å&çóXVä<96‡u¶$¢ƒ³dQ¡Öô,pœhx ¦V±3¥7pÿƒ0<3¦>Ût€ÒK ,Ÿx¥oùÖ»}Ý >>¦ÐÎïæ賻Ƃ˜×ñ?ý¿½·f’¦Ÿ’Ãéš¹ôýÃ|ýÏf}[½gAûn!JŠX¿ô]Ê7}ŽËò„1Äù îU·¤‹ì¹À™KUÐ5ÿ .{õn½;…ï·éQ=‹Hf»Õ‹þ~6ƒZeב¦¶òü(Úxé7·Üíf¤ÁÉ£'Ó8”KíÀW\•þ´",Ùù¡Kܯ3è+WEê9=&ÿ†M½jTk"ŸóUG‹´èø ¸_ÏWáI”] hA­eS€<1eJ»<™vtøHýÓ‰àY8®ÆŸô ìËÚ/ç>Á“×së^-‡˜œwðxQ!¸Œ ‡þéØt` }Ðå‰[¥O 1¯ zlçÕXöÀ´T5ðOª‡žßÉÊÖÈÀ…ͺJ¶º¼â%—ðù“_Pæù—»2õ/ß?1ˆçUÀô„œ¨ö ê‹ÃÓšxöígT,Jå}þ–GæÄr<¶®d.W¤î)£IHÓG¼h:dÆÂÕ1ŸaEØ¿·™Ø4a‡£1e˜o«„TÀä  ¸æ¶–›øã|=<”>ÚÀ¸Ú{kèZË­Pqwdÿß"©Ò|Éí×±èîfÓË“p‡[&æ«€—pÚŒ^rµãx,úųÜkͶ®%‘Ñ^¤V(N½®€oƒªìå`9LŸÞ³@­½TÇõÃB¿<}/‹ (_ËG»@Ýí¾s ¤=xçµ;±E…eÏý ŠQgØÕÀ LjÊ2¦lÂé­ÏÀqGpJ…Kà›ð^ŠfÇ#Äékcv|âúRA…ÉÏ*dîrï‰kŽ OÁ~¤G;±òµlTP.¨ÏÊ`¯Æ:sç)ã·’£8~ïM8}·NzM{É—:Å=|}†Þfs=镸lîHý?5cÞ·ýú³Ì·«é*µ·œ‡Âmxì¢g‡†±e-äPÆ%O‡Ço#ñ¬Ñ¸×0™žÜø ó[VÐ÷e±t[t=+o–cÊ–S9s–y¸ 7ô¾@!3 vÖu¦‡ÏÞ^û°sI|£¸ºï¤,òêì̉œ>ÿßl5ÓRêÁãä)й{ñÜ2ܨ£†3TLYΉyôuôcX "\Ítºe[5»ü@ 7FÕ"ç»ÁQˆvÐR9í=ØZÃßu{Q´7‘EyrÇŠéãù¦¬8ú;FF^b_ÌM9¾O4|µÙ5‚USŠqJM8 é ›KÆÖ;õ•P2þtdVÌÅé¯]h—¢4®°ñ }óÀÕ/–\ô¾“Äš ÄQGWÁâ_Áسd'¸J_ÂÄÍtZK)\º&—› úú1®°ŽÂvçóhYˆW?‘u¾­ÞÊòZeªAØG›,Š!Q's­ÿNð"7«qìŽU‹Jæ7}Err'0·?ê0®P•æÜÞ ²0½EŸ½°qB§¿̘›Ìb·)Âöir°Äx>¬V…ôD=6îš>ž/sx<ÄšâsëÅ ÛåàÊ#Yªk0b¿ÎÊÿø;dŒ €l`ÎCã9ªq¿T™ÍÎb²Vù4¿µðZˆ†˜¿ñð¾|,ë×§êO Øãç*dåÄú³ðø3 pZ• ùW7ñ5Ï!E ¡¹÷×0š°óýÔà!¾³\yïE(Ó£;éwie®tK±º 5Ú3n.MùeÎZW#‹ý™ÃU¸‡±ás’MÛW£º`þé9K7­çѤX;æ_|,Âær·nŒ¦ãzÖûy.§§ÎÞ'MìÐÜÒ²+èÑf”HQ`;KßCýûP.Åm!›ºkëÏÉÿìˆËÐòU t†Ð“©:Ìîàx±#·¾ii¨dïǙھ÷þx׆òc±t×}ŽÛ²€M œGÿ¼8É3Í߉Gº9«FlÕ±Ð?LÎfÖ§‰Šæ°1z68¶Kpæ^|hy)ļŒ†ì¡Oèt¤ ãM`QÏ·Àù,<»§Ûþü€†bY¨‹/ýÑ é¿®Ç$¯?â’ËÌÙ£gº¬Ì0Œ^ß²”î[zíö™C[f7¹ÕnÇ,Ü©ôšwXÞ¢‰c¶áøNÅ€?:4k»›ªš'®‘Äi(³%ºÖ|ãì¹2’ÿ²ä(Üm¦98¶nV"³Æchx8XÑG;žˉÚìÀílÑ­qP®¬C“>òÁ¬aíL…ÆËÑÜ—„!¬Þn³ü©‰;#u>ñúæ† 7ÿ ¬•òÆÜêvx¾î&ÌJ§Ÿ´©Ìá©Töâ\:ex’QŒïô¨T*þU¡Ý$¨§ÕCè7é"qÖk˜­æ,2#à 2ï!¢²—í2¬YW .ù&À}±"ÐŒ?ÆÆžYI·/ÿÄÑ«ƒ'-¨wI,¼t=ËûæË¢>Âiù£,2I“õN¡m«éãÏ@ó­…Fü¯(±šÄ$Ç“ÝçlÙÉ\Q–º¤Jß^"õÇ]ûR`³öqT93øŸ¦2>+æE¢Ù¶·ûeo‡cøœ`c;dýÞN\Ž…šoÃ5zo[pÿ¸ž­ÀAŸ°Â¯Ÿ³ª†=I8Çæ÷4I m ÁÔîVNMô.wæ0êufWW_9Åhë‘WÖb¶`4t²ØT°TO¨ÑÑ·š æþþJ…Ñ\eË»ÿ1gÁM">‰MŒ&o%Œ@¦¾ ªÓ^»Ñ æwC¯ç;ø!ðܼ:&Ì5¢§ÿtoB‡`E@Úˆÿ×/ãô7ã-Mî?Íå³ÅÐ¥– JJ!õ_2¶êÒA-´]²”ñEôèí¬šŒâº´³ò4N#Öüç÷ã)µ(ú¦UYžZÂkÏ}Åm’çèªTŒyâÇðdgîßÁé|ÿ†ûÆðz$`E LªÉâá~St.õM%Í‘Èÿm„ïÞŒ‡SÏ”YX™ óD NŸ<ŒÓZëàÑvgÒùP€8­Äþ Q³„' ‘ÕJ^/¢ÿ¸DXw_¯ç0(ejtE˜L= íQ×ÑÔk57qìe_vžøÚ‚ÆÑúw_c; †æäþÓ©öÝVz`³OŽ,ùá åÚÈߘoí?ˆv!* š‰lÔ©z¡5IÞ9çmƒ×o9ã‡+}àçdÄfçÕÁ¥ÔXº¹ÚŸ§y¯— ¼¸.îG·$•Ëyïõ> 3ÏÃÍv§àÕ½v.!0Õ×—CLx%§}¥ÂCá—!¸š.gÒb ÑË5…ØG„âKQ¶!÷"_ü³û””ˆ\‚ ´ø¼Œ4„)ëÎã7s޻ɗ1bž2ý6õ Lmu¾ö}ÐûÓš>WÛÇë®=F|mHVÊë‘ø·Í*D…WΕÅïÚ…­ãPœôs÷?òQúh6¬ûÔÏy\ºL_¼JGË9_@ét-íÿ]†ö¥ñK„àV#6!e ¯úX?Ø-ÕdW¿ç@äÄfxîP޳Ý?ÂCE¨)©¿– ÌqÙB:QEž¹t}Ãæ$Jì$=z¬ä’\ÿ2Ù©ë0«7êP—±žê×V³ëóVsÓoîÃ/Êð˜\ŽmQÀŒ‡‹Xñ 'œ³±†z»'ÑýO‹€ÚÂuê,ªlÛp~ 6Sy5mÌ«>CA«QÐbW¨ÛF¯–~Áùg‹Gü&VÀiïÒg[§OÇEO ÉS 3zܹxN!Î¥av§ 竊>k+_ÂÌtܰVÕ‚Zñ©ÍhìÞ<Éþ™iªßÁµÏ5 ÿ ·¼Q¿øl dO€eöÓª§©é†‰t]éf:©?¦þ‚õ¸*y™sl(ç‹ÓÒœ.ÂV˜~Æà”ÍðáÈ<êuû(jôŽeÞ %$RîçYPÊÛâFoD(0æÄ,œÒ©Gež$MÆk܄۞C‘÷A,—\Æç¤Ó½¢ÉÝ zTI "êA€ÅcÞĽ?±,I˜ñùh}íý_{×O,j[Í |ͯˆ*Xò§9é•ìx¸hžKÄV{?v²ù,ÜöÎÀ¤‰›ˆå›V~ÒºûDtËn\ýQ–êhÒ'ö³ÙÓŠy,[aûu^ÇÜ|+ªÅ™ÈÊÞÊÓpdŸ5ðÖ!-ÆÊt‚žìÿu•S¼{ê ‚Í'ÊÚô•M/^y3n-«‚~Ó6@Eôù §N¡kÍfp4, ›‚aÕ—høÝŸ‹íBÁåP&wîZ0*¤O¦9}‹i›úBžcW(•›$ËRç S©ô[x;f;ñ¾Vƒ«¿ò0$×›^Þ6̯øSGøÏ¼Y+A/{T|®Ás*e¼˜¶^у™Ù•€±ò4˜Y÷ÝBáÒƒwðû(¬i‘£§¦&â\Í(œjqš<¿V wòçÀ¬Ï|Px ½R|Üôë¹úЖR½ø¨–9?¹¸ÇãXß¶³hÑ\ †ùäëa=ö9A€~Ü{Ÿeš±Cö<.æÙ4ئ{ v´C´Vb^upÊxëx?•m4)Æ…–A`´O»U°0\€­­ðÀ¾Èjd¼œHJì fP‚só9m7A¶&h*Jž”„“S„饧Wɤ7@ì¾<ìÕP™]T¿ ¼¶N†„¾ûxç\|=:™ 7±Ü¹3ÿÓj¦‰F¦ô?=æÃyÑx?s*­³“í[érgÅI’²?øtœýnØÏ–ì>‹³¬h÷òh¨nÜH›Þ‹Ñ;3áâ´ é‡ÊPA\X2öºç@Š Á5Ý4'¶×ï¼ ›4QË)ALÌ0ž©STÕž@T93m³Q4nù‡n|ƶšÁºdåXö&¸mïj°ÑïÓ˜w¶6¿Þžh 0öÊ€Ó•3£¹cb³Ë ðí“‚¾YíØÓd…Qê-~«ŸL:pšzLÜHëFêß7ý|ÈXT†Iræ¨sבE‹ä°!¥8úÐ|Û}ˆU89°#ë›ñÒŸNâ0y2<[S@§@/6Œ¾§Öfð4lóÐám2¸OeãV¨ƒ,KȘŠíó¤î¿‚i³Ò+ìÚ}=塃dÒÊ<lÙmŽ?W,¥£`½¸$ˆ¹e…Ø……^åO'CíC;¹ ŸÕéŠé7qÊ.2°®úçTÁþxúú ºÌ¶ñë!®*šæ]$$qÆaöåèh¶)7»† ¹/VºÌGý'ÌlȦcÏž]ý(¶­£œUÕ¿ kÛ°ˆ_JEvБü¿èZò³ LhMoßJïùÜ®ê,oËe\W+{P§l8Ç"Éø+È9®£[_ñ힎åÚdãXs{”}iɹÍóÆ·Ç”è/×@¸êþ‘TÕý‚]k«0ÙY“Nqi㩬ŒÆ« 3âÍØr×°ø¶ È~I¯Ÿß‹³ ¾XÙÐñËÖß Ûëª0ýò1h *Â7·iÉßTÎa…5Îr…Ÿ:ãéÝÑøÊÑ”sK|Ãë£Ø¢Dð»WS+MÉ\!~oaeâOäž#RvVܹ %ºÅp*æ}w„«7$AöeÍHüï¿´æÝž%k©cTÆÕΧ×K8eª&7Šþ»‹KGc²²={<ð¤V2¹âÊú‚÷2xÎaljÌ^Ä 3‘º$Zr¦Š§ª}‹ ™.Ðteî6ÌcŸº{ŹÞÚÅšéSî“›½ÇVÒO‹S@í Àó6÷%ü,É÷„ÄŸbøÊ) ¾jÃá÷‰zC19twùSkŸDöÑI"¸ê¨]0Ž”áº ÂXú—OGû«0%±Í´r˜¾‘6¥±«ÔA´î|ª1`‰ÛéݯqXüBÍž;ÐÒVl¼_ÞHü'Pâõulêñ掯—gk§ñ¨Ë¯ØP7&üËEÒN³ÿ@ð5øYð”Ë?A¼U#¸¼Š78æYéJ tÛiøÓ3‹íoIÁÈwän£9»–¬ ‹Î¦‘9oDZªW’˜9Ãçò:¡Ö3»%ÇèòÍ•Ççs¯x߇×Ôa¦£}°Ð¯ŸØ/Éä.¾™Eˆ…}ý Çx0ÿ”};;¸erdì—Tâ.˜ƒö's3ZsÉÏ'¸kÞ1ó¢&kŸºžM}õŽô}M†÷&<º¸õ¸§3Ýg·ñ›ƒ¾l~Õ"ºâWúÓx¤þg%å²×Åâ¬Í‚lýÒ Ä]ï,+ UÂÁÀz7U—M:ÔUs‚Èà|ÙìB—¹ ¾Î†éÛè‰ÝXzüÚŒ÷Ã7¼9ô0q†MÙÚð\\ý|È£Ÿâ,lX6랉ӸEi±ç&ý^ ÷ß>€e«uñÖ»lšøC¶¶ÂÄS¿`ßœÓØ šJÆDË1…jw¦pþÝ8€ãõäè¿xuYi'<ãxãäè…&B¿ü‹ vE`õªÙ »e6|Ús{6ñ öæ(Z9k:¹Ö) ñëc°Øµ–¹ àYŸ{õzÜÿžé㇅² ž­›!…!Dͪ Ìn¢Î²áV­Hãµù ZÍþDŸÃ¾åîÐÿd?¤yùcû„F¨«ÍÒ—~A±«'л2‹®†Ë˜9 FÇiã^t(²î3é#à P}õ8/\LÔžöÒ“`þÊI—²î¦ ye6;pʘsZ0‘•Ï EMê˜×’„¦£ÎÐomm8­«¤~øº¸-Ó'Òð/¥ô{¥;³ô® Šæì™¡k8/«Çú1±ðE\³Ë$¶m÷X¶rR,:í”—azÛ'CËi}vâÞ3¬0$lEñ!Zlæû?ý‡À1ä£^"n¸]I_~q5ËëY¬ÿ+û¤wyäÔËÐöºú­£èÝÂ4ú%L—^‹ü@ºd¹ÿ¾ó¬xôtGƒK#^H×§sjTÙÙ‹„õ—™€Ø§1,´½‚<8¯ˆDr/Žj&cRÊQî~ YEóJ.ÃZ“YtóIGnåriú£D[ü€[RrÜÎË1ôÛ‡|,wƒŠ£šxÚo V †*ã*²¶uˆ›dÐÄûR§k|‚™Ì¹%ôû ØjRÀž&Zpyîö692lƒÃÞè1 ¡¯Ý™ÊPFú¨0ãz,û5°ð¼7’ÿr¿Sñ -ØN–gw-öc¬/µ¿«C_ìÂvΚŠz ,: ‡K¯ÀÖ–ñ$+yºZúŸ®2è²lïÜ>}=®Qq…H:†Žz6:M}Çwú+Z£ ÉW†±sj´Î7j. ××÷pÃÞ‰žû–Ìÿþë 7_ˆS…é3A˜¾õ.FÌ„7RF\ë}v°/#VÇÖmœå¦¨‹«âNÑଛ»»`Qv#¶H+ø_Þ €KvÀl¯\εοguJ¯Ÿœä†>ÐÉ3ƒšèTœi'öåAãxyúR|Ý ïȃ=8w°cëŸëNãF/ekEòþTÍ„ÿ´¤kÌ0êíSX)kÊíÕRaÝ µøxm]û²×Ï‹àåôŒaËÃ!:¬,zÁ»ÿ`—ªÝƒ—ÏÁÌ/ÞöžL‰–Ó=xžŒ£gON„×D™æ6+¦»x<›žs w‹±ÝWìžIð¡5Žzž™€m$!ŠO•6>…£S3Ыi#dÏ­‚×›õУ²—KJ¹NÊgŽå‚ìGü_¶Ø2®›&cöŸžóÎsÏÐþŸ ˜5µà±EĹ¿ln\€5ßèÀ±MdÚ—@X”)ÅVH%à&£:ònÉ.h3ƒYÇ3`âM뺉7/Ùg\ûK,oK‘1|ENýøØwp,>æP¡µ¦ï–Â_±nDäó2¤\žÚ弃.]ØÇƒøŽlOq¼Q¡÷Î$bK§7¬~Àwó\u´ÙÂE:¬Ù˜÷}Wdq }ቒs¸Pƒßb²ðÀiSHT?FfO}Áë5XÄívÔc+{ÔØ‡U_ñ‡ÇcòP`&3Ÿû"—)Ûal÷ÿí?ׯÿÍ/׬´'íM¥˜]õÁVþè^VTwãjf²#ùÕÿÍ>“ºÈÑŸ™Dyå"ºôôt˜¹xŒYPÌ]º¼Å8в]ãPè)Ø,W¤~e‘çßA0ø.AkΧrDc`´WÒY5°l§íØHc6M$ïµÏÁ«c¡uî>˜ÚÄG_H¾Î¢ÜÑÄfîaK*ä¼ …~ÏyQV0 ±1 ¥–‹ ·±&Ñ\¼“Ý f;ÒfÏèúyWæÔgÀüç~!eYù#œF{¯À ³ÿå±üªÑN†ocÃ9Ùµiˆÿ7®TÂoZw8íƒdnz˜Ý·â]Ì8/@_žD£ôÌáñqr'Z”]*{]ÿIçXýji ØÓ¾–Id Ò…—HϘîK·Á”Á»$ãI:……`ÂbcækwJ_•£ríEð˜h‹wtaØ›v²£`-iø`Œó›2ÈÝ¡ì@ÇL­rÁ¯6¹ \íu|xiVó3%Ò0() ;ö»qb~‚LÌë=y™“„ûfÁŠJ¦;>†ž|é ©g°†§ƒ‹âdè¤5“Ðxüc<{X–žÛµ“Ï‹´ƒ£‰PYðŒ{N›eÿ‡ÿÄîÍÅoáÛ¹'µQ\UŒ[òÚžŽ?ªHg6‚üúH|˜hLÝŸiÓºba4ÓØ‡³$逼آî+ÜÀeì¨íW _§^á-Ø™…·¶žå.…ºq M aԃݜO¡÷Èõ'&˜Þ`M7¾™ÀnÎ:ÎwÝ}„;báIgK¦?«'0Ž»CDNc®ÓðáCAtºœIËÞ$Y†lUÑtÕæ@dçq·ŽÍád,WÁ7K–éÔÁ%ŸM³Ž¿ ®o>à;áh·í€E|ð=‹ ¬€ãcèR¾-ùo.š·ôtow©ÇªcjÏ^n³†&š(­nV*ßǬ ;x9d¿«ѽgŒë¦ÁK—› ¨?NÏC[9Ü´ì çØªA-îÜáõûé…ðDÊÔÎÉáŽà-Oñ…άGÄåÐ$Zµò oôâ“Ø8)z“äàiù}î{u2šLk&V›ÓMMªÜeY\7Ý‹dª:€TL3q{$ƒ€î¼œV–Ã<üž+Ig_ck ë—ÒšÑÐéƒøŸ~tpgýÅ–MÞEbr¼Ù¸ÉðÙJ“zÏQæ[KK#ÌN†wp®~ô´©+~ºvtdþ¡5×îìw¿ 85¯Î,ªGE”`‚àˆu±_©IR´Ê+_‡ŸA™k —$©xÈ[Øh%Aƹ–ó,lâØÆô2ì.åf%â¹¾%p›¬ÁÝLž§]]¡t£0mݹ•¼nò©yG«~š‘0kª×dyê6x;) m4迹g¸*¡ñÌòóhjøwGß#pc‚ ;¬u¾õˆ\þù¢–˪uƒ+»àrhÑ|p޼35û½5ÐÓ2‰ß ;™kÿp¦_›Â¬?^À´ßçAÖ>ž/°W× ç¡¦éS¼³kùˆÿ5o•rÏ ^rDñ õ=Èöìêld¹/O`Ãf/›‡Ýǵ„ô+)àrt>™cR„ñ¯ÑñÇiîSVö·Â­ÝÊìÖu:«E%F¹ÿÕ ŠèÛ`”Œ¹òG–\)š¥H‚ñŽS#¾¯Ï‚›hûºïšRotl&ÌÙˆº5Q ƒ3`ʤF{ñwV$iÊ’¾›`[Hô"§ò£vŒ»Y¿P‚9½©Ç‹‡ðî·.è¾/‡V/•Q6éÓþ+8ÁÍïH½ÿ€öݥؼPÇOƒeŸ.s² ûF”ªüÀõ_Rgfs¡Mõ"ŠM› .±P$æ&Y| -®ó—hàyµûý>,K$ŽM|pXtŒ˜ÿJÀ½ïã9‰½8ùJ`’ô„h©@{e7n~-n¾£ŽBùðÇ~,×ê<õŸã£$|;£ˆ'¥wÚNù€WË,Pþ) ÞxîÊqèë’ íÜЧa33Ý÷ŽÓ~fO窯'“O ÕRg\Ñ/Ã׌~ŸGsÿz†ÀÇ[›ZäÞ…Ç¢u8é¤8•ÛÏ¢¬;y´ôÐ3lŒÈç:#¾Á©ëRÜJ˜—«M§çXѰ™ç`µf%úKE󎉯âf-Õ¤ýÏ‚í;Ä:¬;àøS…y¸Û²«)YUI.…ÕsV Eኃy®FºŠ:y½ŽÌêßrxªxšá…NÓ,1M+Ïpù{ ¦‘Úñ?xR“¦¡÷!‘‘ú_tf ¬Õqá–m’‡µÝ /÷-‡·Ã¤µƒ¸ãÔh"pe,¼ù^„¾ã`Ð]™ª¸ b_æ1Ô¯ÛË)©ÆñŠçÙá‚«¹è ×6`ÌÏ<<±] £XD³zZÍŸ-3ˆ¾’k±"ç3÷6|"ˆiÜåéžíå©.0CëŽ8ÍòÓt¸–ˆЛºi/È‚ŒÝ<1/py»Š;ãVŽò[nã÷Fñt §Æú’åѳ­Ô¯’£G†ûó¼GÞyWÃ_JËbpUî÷QiÕh°<° æ}ˆ¨> 3•‡ï#ñ¿é]gàú†ü‰;Œê— ùþ)8ãŠ×* é‰óåxó„3Õš8­\Z!Ó2 öoT¡[NÃûte&t°½¾xEé€Oå.ü}r%×ð'›ãŽÀ–o›q±™/èŒn‡¥Vgy­åˆÅ›j²çú–}ÿ §íù µöu€¶8ˆú¼#mWư‡ZR±ô=¨môàføßÂYŠÝÐu:‚}£PþßE.µ²z¸žt“C£?£‚ñ!ˆq˜Æ¹~¼Ë­ˆ¡³qTâ.pÞ5½}ó˜àÕVÐ)0¦ up›,?Ñ6'ê}e×ü&'fÿOÿ¾nûm\kL¬—8áÖ•¡P2û ùýz Œ±ºJÄÓ×ᢰ¦U‡N‘êñŠ P{(^ú”€¼æ_.w¼(åž'ÅÿÜpy¼ó±ã3%©¯Ð!Ðü¬ÂM<㛂RÑÚkÈ׃‡&Øíz§]´°ÿöÛ6w*°ßëã¹ìåó¨Òùßä¬kÚ …N ¸3J5™WX8.{±þ†½FEýP’ç8‘U˳ï^h§Ô€MÛÈ;n [÷i6ÈìÅ ÷ã/u)œç^šYÓØ–O±\¤³z\zKþIžå>O7bcÜ‹FüßúQ TÂÝÄP®Ü¹ž´.–Å®Æ÷Ð})‡§x&¤nãï‰Üì—ÒlŽe/mzÏÝ»cIcŽ `èýuäZ'h„Ú£ÿ7IúBÕ‚Œj“g]~ ô™XN³ê 2Ÿ å3 ØéG^;„€É@gŸp >[¿âάØFï:‘Õ;}ù»/ÅÝçÏ *lÇÍÛÔ¸q9Q¸þ]÷ˆþq~ zÆÞÃF©,ØS øDêÞÞôŒÌ)Ðf/†Ú`Ó³A8Ø,Ã’åCÝÞð›”ÆWY¶†4]€Š ï±mWã_CÛ‹¡œr°/¦‘N^ ±$-7.¼.¯üQݺ¦µ‰A[fY²KƒIäžCÑ%]ÜÁK1üê~ ¦wä6:ɳó½g¹‹ÑÏù–ŽfdùÛœãÎ ¼¯'Æpû¶cöñb臧xÈY€™á¸o»Íí¾œ ÝÂ!8¯ó¾ B¥lUîSŸ ‹Ùó-OY±¬˜=xvÏ®¬D–ΩɅ¥Y6#ûß^¾„—Qü >ìÀ†}¿w1œ»viùlÇ™š¢ü 7³!ÖÏ”ód©ìÅ4T;lø¸ 9ïÃáûoj¸M6ÀºU°Ÿ7Kÿ$«¹¯É•ÛQòÂŒÔËî¾}ì©ïchtê±›âÜ µ×øø{ HW_ÄóÓŸ¢á c2Ó£† ¯™Ï Ta<ÌhCרíÈÿp…¾š£×é3$6E‚JÎ6£!ÏOÃÍÎ@è·£Re± se­¹+ÏkDØ£óÒìåÅÅèµ+žEq¹P¹d÷P¿·Än:«’{‡¡4–[.H¸þnX –ë£ÿÌIôˆi1þìÒ&5ÉÎßÓѵ¶ôï˜Ñ¹IŽp$Ž„ó³=U†±W6ú‹ë‚Ñg8µÎ •ó/CÓ6¶Z¸Šs·ö샾ïÆè8þ+oñýj86Úíw`ÌÆ/D~h÷'m+wðñ+®æýOrfœýT J*Ái×àWQ–·Nšw ³ž¯wï¼w½±œ)¾_¿” Ëİåq°r‡—½ÿ#E}9ÿ;ÆýÆÛ§7¡ìR2¹XX%Ú‡ø+,tè/‰qd[K(ùo&(Lù Ö=9NZzŒàßùrßp¨£ˆû÷Y/S1—fWh~“4S¾ìÇRfËt8 iýŸV31láõ•õ’ÆCãØ( ;zDç;þ§ï,[ˬU™¦y×ÐRÊÌÝ…J1Xšñ‰LÎ[ÍV|ƒç¾Ý0~w$Vno€g,²¶{Á‘‰›Ù<Ã\lÔ1¡1 _1uº,íŶ:i«Œé™ }¦,ýþÅéЛqp‚a›Ö>•Ž]¯JOòåp˪DnqO›áÕfl'>Ò ¦¬ÃνÏ _< b gRî˜ ¶¼ÿ•à“‚äK‡8Œm[Â"ä^}-?Öݽ‹E'»‚þ9!JžÃ²^¥Ë›«éqÛ ðÝ\Ä v¼WY.(s 5ÉÓŸq6`ô –|øäÄD3ý©_¹$m¹á™£"ðf¾ÓRÈž5¨ƒ|ÈÜLM™Õš™lÏÃd²ï†!§ ”~I³½­/@â¯4í#Ç7­ã–Ì›ËÔïó½l­ÀêïA¶ºÛ §,”¢ÿv¡Pn0 ÅääÃ!þÀx*=„0ÎV”µ/mçNûf^«+mZLï%Ú±ú| *‘,É*ßò¹S¶2X¸W™Ý0‰ÿ¬òØ¿w.jþšË, ;péX36Íá lv†sµIç™q²9 P¬áÍ5q§W|“ÐÂAdja÷Cz(u2&ž™B6­e¢@v‹>tÌ;£@´Û2úÿf¡ÃJf‘:ïհƧÿ„T°ÜjtU™ÒÚÍ̼|2e; =ÞWR«©°\9›}wIés}A&J”­³R£ëÆÈÓóÚÿ`uáz–kI)—ÀFójÍÙ@}Lw°T_i¦Q¬ÅÕØj°u^zL]n:)9è¿ÓÕ‰„~(®2e^Šï£».4bÿþ…†¡Y͉ë;áŒmÒpW¯œ%¹‰²?¡öþL:÷üVº~Ã;“#ašÒT:þòdü!œ åçÖÀ]O»&S« ˜k,?_Kà™ïã˜õÆ»*c‰Ç}àæwC|“• >Ýà(v¢N›pEï ^“?y÷fö`Š&ÝfÞiŒ>IËHÑa:Þý ¸™(ÓÕMfØz½D$™K¹8¬?äDµÆÂ±àN8Õ5–™eʲÞÿÈAº ·ûµð}”´˜ÎÎ X®â3ij«Î²tfI!Ú© aÐxó çÏ`ÑÜÂÿíÿþýŸß„ÀÜ#÷È`‹8'ÕÁé5k³ñŒãIáÍið5¼Þ­ôj¬\fÂî‰Éó¢Ëüái¥XÚ%ÿ§ öw½Ñþµ;Fúëô¿ss2q]_Þƒ=娺ó 96)Œ¹s«ÙÚUðè^ w`ºíÜ)ŒVÙiäFx àôt\¬@-÷Äá3< wï¼í‹ux'õK}(N=òLéõHyú}ÁLÜ^¹{µqñùàÿœ/â”ÀÉõW Ú'aªQ+…ïºBQ{Ö5N}¥#ãÛ rö…éÙÆRø4eYŸcˆ5Sç×sèÓõ×ÁDú k>J‰ Ô~æ ’ý¢/77‚Òé·p#A–î³µæ>¬Z|y1I q€Ý[øqô’[$´z‰qLL¸vˆ2Å“#ö—ðí¹ œZ”ßí:Öÿ\Ù¸ú¿™h¼|5ˆ.7›…{çç2öÙd2܉‡éSîÍ„døºþ>Šd ba#a>ª4/%‚û°º ÔÔ¡öå%8=š”ã‚/á\Ïñ–QÊÈ™ÿHõàâ¨ù†³º›ÏOñ£G#Np_<êÀœÓæÒã´èw&’µ8>`ó΂ã»4nž`%¼·ÌÁƒ^FôO‡ªdÛðMja(atŠÊ°U¤Œßjú–’f«$à‹×5ø¾öÆåÜ{{ÉïXVm€Å§õ鯅¡]z»2HGð¯÷¹tØãØÏYVõÀñgÜïEëéW§u¼-vH„ä=˜îú= ´è¸•9,qgýJ¨’¸˜V3ˆ¤ÍP™îÁ®ÚÍ!ÍÓ!¹ø ùõR÷n0'çóÇ`¸Êà¯x=_e¸cf’,rïz$(•™ˆ›Ócçɦ}m¥»þ¸ü«:}zYM†•8 ±Þ³çˆ ç jªdkn›>+[ó£±_ån«å“ûîw¸Ÿ÷p½³_ÏÚ~½_‹j}"̶+3Æà{ïBÜ7΃$-¤ÖÏã} v{¯;–ü á\ºŠû£%9ÒÿÀ0ßœ#ù^Î_ô³ão¾Û«RݯJ#”™O"Æ5¡o"g8{;é~x¦„&aþÒ”³“Ýño›ÌÎk$‘Xv¸ãlóìç;éhÓ³A ¾ð<_BωÔÍXÃøEJàízl¾Þ¿y¢ôj;Ò=ê^̃žO`V³#lÊ» ¢¤»ê8VÙ¬‡€Û8¦²“ h”@úFæ{Wc—k;nWM]Æ®&i¥™g!âmöÙâ&,”•£~›á­X.†Ú€öâ›ôöÁHZ6>žôùK°ÆÚxzËfÿ,±‹¡îFâ#Y‡Û{•˜²Ú[Ð*  zù-*íªy‘[>~.kß÷ y)Ó©Œ½ ™è2ï‹Ñ>þîé§Üñð×ßalf Þ9VKÖmgºD›™¨E³û’ñ\ÏìÏx@ÝÆ‰Ó-E{ø–ëÎã®w_qZýÿô/ÆwÝ€[ºä·Ë :~ÇD˜,C¹5¦ûü jéM@½Jö— 1«ÉëBs–ÐÅS#y8ga<åóVÓ£{ÇAcJ)tö¾Æ%¼Y•Øgø>:Ý3±¦/ÕÄè¾C6¨SÈ&GxÐEeYF×/µà‡º.ÀƒÐË¥=ùÎÔIq;»¾ô"›ÑŒji‹icsäÖÏ¡®•2TbNL"Ypxç%XléÈ ¯Ðü÷”:vœþ¿ãˆ»ºíF»'YÏök‰+,>Bû<ÏcÔ¦N²~å.:#R~ŸÇ–(—0ÿå{•‘¥á–¼¶íú,̲Ư´ýïºIìÔ+*\+ÃĦ€€{˜g‰BÁ–ÅpKsuú‡zÍõ¼·_-iøršoÆs6›ÈT÷Ngs%ï`àžs¸º£jˆ.jNTdg5âY@²&Õ4dž¾(“y;Âa­üh›(Íš8qºËÄœ-‰¢ßäqDÏýþïÞçœ~Rœx oÿ?¢¾;®§÷ý¿½·¥¡&)­×}Ý %["2B•-¥%%iQ*BJRR¯sÝEYѰGd„„Dé×ûûxü|þ«ÓëŒë<ïë9Îãê¼¼É2’Ž’ž+é첤¢.ŠͺÎÍŸó6آݹß1ìõaZµ¨ j:«ÑRdóžY‡÷Ûvâ§Â_¨~ÕíœMÍÇÈñÿֿﱕ8Æî'*ìÍà‹ IïDÖÜ&ÈB]–ò’ÕfRëqìòV7Hõd”®C]s7æ^>–-6eþSrdº'ȯ¦ü4f–'B_ÿö¤?VÄ_öÔ‹^¢´Û5…¹ÓóäZÑòRá(îÝAÓà|¹[ý8jD`˜¸°>íÓDÓí0K2£ŽÝœz“rIè;C¶±ø>yõ…cÓDpQ¾2vY§â½üØ“°ðV BD”*.‘zC¼Ž|vÞœ‚¢ëDz-"„FVª3ƒ¥µ¤|S,œ*MŒæeᔆÛű+»öa›’(ýª¾¿UHÐzÈV3Ž_„Kû?É€Ž~lг¬ÕÙÔ›é3£ü÷BßÿöEbQ´¬z•ÌÌ}rã pƇ۰›ÿU‹ê¸}¦« ¦]’ý~C·¿ÄÀÓס&t4½6fÓIMc2£/¡z·Û¦<…ZJàÔóÆ´"IŸ•ºüí¢)ßü|»¿^ºÔâ\ßP±j 2ÛÇŒ'ãŸ\„Y¼3Ð~Ë*d‡B¸·]ý¨únN =—VR;e#ôМë“t ˆÏƒˆ[XåÙI´ï–·Éc*ÎXH¦«²3éŽÌÕàD«„’Ï5Ôïv0œ1 Åg0áÈ Ü@óÏ‘´É ïŸþK^H$×A›å= ðÆ”Í,êæuØçlÇOe‚o«°¬3þ~™DvŒºÆÒɨÞÚEdC­H·‰9Zܰù{ͨ6MVÛôÞ\GÄ>£äMc,-e‡ÄèÁu€«óç—F“¿ÝеdS±‰¬Ÿhzü–¯b ¬c¹˜C|g¤ÂNlÎ׈ÎJQÆ(ÅòIî¹ûð™a‚ÇØeiÁö¬ ÀˆÎÝ`¸T„žØ<MžÃH:Ò0Ÿað'ˆ[«ìiìDQ6þl(ßœÅ~ÐGŸ Aü\we`:šüîü¿A¢Ž l¨Ñ—üÆõuÆZ,/q»Ixâ¤.]6Çâ5ö¸ÎåþT¸²#ï$`ó‰jXW€s,VâÝpÆQcævãˆÔfâ«& .íÀw~wå>~¿ ’©ÏQÓºNØ3%_Z)P·çí ¸ªÓ½7„&9Ð_ÒýñA.w*oíã8n±”0?^U–>_ž—ÆåCXÒXxuª¶ûéQûÅ0K[ŽNûù ~Ðg÷=P1¸ ôÖsæ? ¡2{ ×çÝ@Ÿ$¶¢„s}]JâËãÁÑÌ™¾šÿçˆäaÀ:«ë¿w[нäJÎæáWl™r w/R‰9[áqU¬¸aL|€dûAt¼ÖÆIÎAŸÇ€¯'LDш4çïîvlÜ’ï0Ÿ²òSàÄx~Òl3v<Ô>JâÑn:ƒnT§"),¢¸4úðùb*6‘ÎÚº ¿Ñ'‘!êR,n:œ?œ'kß °nz‹o1q/ù]o7=ÂÙìÚSp¯1 wV⪊ _uÇ$ ŽhÌA¾ŸC­¸É­Î5g}ã_ƒ‘áò|>ãO’aã·´)÷Àôq úI¹ÆWø¼DþþY2,˜ü—”¿×Ì{Pø%/dàðɬ7ñcBG!aë%Ò֞˔K…©«Òt»Íµ=Î&µ;q¿‘Ó^"ÏRq/ì§BÑ©äx¢Ý?±…f0Hv}ßÙ zÎ>:‚ó“Û¢`ÿ$W¼¼M*Ì iJâ_’o/Än·ßíŸÀ0¢ïd`÷P9oÒïÄ}¶Ži9£äÖ Hr=VËà\›õifJ´=;Ë]þ‹íoE©ùà.|]3#'~Ç<“X°üpŠh.ÙDæAø/CvÿK7×÷8Úu¬þÕß[0L)â÷Vx«q,ëT8Åí¹Ñàl&cËö‡ã´·g`ɤÕ<±À=¸KýW¢ÛLäº*àU„?[ åÚ»6á–¼OÜcAcØÿÕ >Ûò¿(ˆ@ªÊT—¾‡ôÆà½Î$\ê'YvÄ„;.eàÕ?AÍbôÍ£Z²åG²´@,™Œ± Ä¾ß>ÆÁeb¦pœæÅÃãÒ ÜgCD.ÚPGCêˆÃþËàÙH›¡î~'väõ!\µX€…ÄÝÃ[Ü{|©Ÿ ×’·òpžª-Ÿ½Šu†Ï¸÷‡nr¥ÑÿüÿÎK:¼ÏªÓ@kQ4­¼3~(°—ÕþDÎù9.ñu‚©-— ©eçݬ-ëª0Èv -~hIÓ½Ã!´õ(ï1‡ðSÅT›W:±yý÷p.Ys¶5‘;R? -c4ì‘òc+”nóÎ`YB‚pöžïn&wŸ_Çk닱3ðêz·£lµ2µ‘ôÆXï`{¢è° ntêf3®Âá¬\"ÅÊk‹àycĈŲ›?`Av,×°L˜6É‚½3¨çýS ¾o/ÛRš‰­_¹Ó¹ŸaNM>úZ-ƒŠLͤªÿá¿z}ØÚï'3§T¢³äVr¿«wz·ð ÿæÙkdÕqLP«+‡9ÎUöýœÕscˆëø‰9W•HlÌ\îq¾5›“òð©3(Z~攂41xy.œó•·mi~ž MaJ8é&å7$rZÓ;ñ‰ë^ö‹£?\•Œ–‚&› ÑáêÅéóð3s+|z4}7©Óž<®#nÚtO«¸^{A¶¨¥©~åµ$VA úÝO¢÷þÞƒ‡“8ÞTD¾l®Ÿ¾öu€$±&^y¼(Ü}ÞíƒÁç§ðÄÝøt_Å¿çßfš¢¨ÐýŒ¯­ŽË7†òwÔEBé.ú÷ƒNå#pifÊ=YÎ{;Ε>\P ×FðXYjL^qsýrR‘ÕðÃô5T´¨P‰Ø£(ë1†nGgü¸É ]¯ï¤yìÍŠ!b'½ÍÔâº;,Aûyd\*…×ùÛéÛ Ûù}Rjüû¡…s.÷v¼+sšNï ²êc©¼t½ã(±n'¬Zy·/ÿ€I V,²þ3Ì\[­ûbÀæË%xöM“}ãÁúŠ£ÀJw,ž˜³–'ßoj3è%¹ä÷¸cà{²ŽëoÖ€oÐpjË¿úî>-ÜhŠœG3ìD›¹W`Rù+H WŸ?ƒLçì¨ÌhÆéˆSË/‰D¥v"½eC£I°?åììØ‰Ùð OÜ¿€n†éšWó¸ºák¨˜#DM’+ðüßfÕ~ ·³M™`÷-l¾êÚúó¸ ç !ñº"û]_ÁõWeSçoæ}¾ /?ÇpƒïáJ©Ô¸Aë_ÂòÈpN¢nù׈FÎ{‰·{Ð{Ëᵂ$|§ÇêŸ,f×t/BØóš«3‰ä!Òã:0÷X1ÿcøg^ÚcŠÊrgÖ;€bå¶ú·RT"æ‡ã3¼ × Òœœ›ˆß€4M?‘‚fŠætÙûXÎþl– R6ù®nºëv“e» øÂ'r;:TÙŒMŒè_¦¶û³´ ëaúà$Ú´ó9”Æ;Aw—9´u&¤é<^!l‚“=l>;“­1š¨2U=ñ;ô¿ÿu¾ò “±m'r;ðâûv\xÜ­þÁХƮV†Ž¥ÃèSA GÙ`ø3eª‘k˶¼CÂ×ÇÁW[À» î²€n‹¿ OÍÇÜ£Û±âøtZgÌÄoÅÀÅâ‡øðk æ>=û¯ÿuÕaoé52ær&d¼(‚Y">ز”sÜqï5)•›¾_\·aí/la^p)X¤ÏÀÄUS°Iñ$ªFíçZÞOÄÀeâX” t8l£9äùѳNª!h±„…ºYÀÛJIʆ `ÔX¦S+Âvw‚‰[«ño`ÈÍÒÆ'pà” í¶‰à>p/p¨¢Ï=Ѷ€hö¼c¬äX‹r½úèd ªwYáÚ™•AÕÏТ¬LŒ}“ÀÞcéçÉÑüÏ06¶ž›ÞÇo96áïØxFƒ%)‚æ§CÿÖ¿¨Né,'ÓVÄrÜ-X½µ;ger›ì†øžº\“Ó~X«”‰Én«0ì$™ó|»§ñçHëbk“w>ÿ9/|‚DÌH×<3*/;ƒfÄlÆ–t}æµ¹ Œ{_Âé9ƒÜÅ+uÜN†Ú«™À’Õ3ø*Ãý¤wÜjríÅžu¦pà·í°;Šrã΂?¿¿ âæáô7¼½f'¡wÜ[Ôð4Ço׿aé,/Øø3*rÆÂ&Õnì-®@¹f9 '‡¹Ÿ?ãQ[æ#Ï Lׯ’C(OÇ"“2ˆSˆyv­ÜÜv®ãÛ?üoÿ Òðûƒ 73YŒJûîÅk…éì«sYœ¬}uÀ†j½Þ…›×lU“¦3}ïUð m;¸¨ÆúY3¡ð|–öw:±^…­KTbëöwá§¹/ Ðú_¼ìþдd;ÿØÂ‘ñ†Ð)ÎÔ\­é-)öèêu,9\§½º‘#þí„Õß×Á>A5XúÞ©sðÎÄ3sÑfÊr*[Ñ'¥ÐöùVô_.¢Ø”‰[)²ªççPh["F>þÂóØ%ÌÌÒöÂáeYà÷Á‰¬­ág…µ’-oqŸÑyÜ”}b\'bµS(¹{Ø•º„ZBøKkj~W-­;Éûµ·!tK¼n²e>&)p\KßNæFO †+TÀiþB2j·(»ò¨”Ø.Îݘ\À ‰ ¿ã•Q$# >Õf«DŸáqÇJ~€M Ô9ŒÅ˜õPö±)ì€?ì͵r¦.ÿÃÿeètpý*…8g›‘|\}ŒŠÐÂïY ü•%H~¬$Ý|à • s9. 0¦£™í{h oA~ãtÖ{ejl)»ðxêºÅ=ðW§G²ÑtšNõ©ÂÍ›^Ô;fŒa7l¢qü}ðÙ5•e(܇ÇIgHttž®VÆùŸ£È¤="TûE-ìr8g\İæ 3½õç&”žË罰䨳3œ$”BeŒX,¾§%fãÕ”j§¸f5î„˰iÄ2OWpï^‰1‰2Øÿ}x„teáΑ"¸¬5KVXS·¢ÿ𻵦F å¤¹ô[”èˆ ƒ 䨣3ŸÉž*möŠn 9-TÕ2€ÌÆ,rÔ« «Š_þ>=ø}°ö í§µŽtÒÊbüþ@ž³Û¶Ì|úNï¸Q¬oû+ˆ˜©DE]LáE{':κÂoµô§µÕI×3. ¢¿l¼)6BíÞ÷ E“A£s!UÓŸÏHåOrdÒ¢/QÚð ˜ìC§Ô\, ï×SØÊÅ£¨…h7©Ws‚ ?ØÝý4{ \î§ /AlÑ4Ök+MÕ¼¡¥ÉÕ¿‹“æ9þÃ?`nwfÒ"ðü< ž‰qi@pÖrÛùµ>K“RÝXØ  NßÕ܆g*zŒ|Ü‹µ¸ÃÍ–~S±±IÓ!J¯—|å]Æ>}úúu|Ÿ÷{5rWÿ8óçeׂþAyî‚Þ}Wƒ·—ë€OÞOkùJÇm‚7=eüý5·ˆM\ éê»M69´‘¡Ó·/+”)ã¾ÖtÒ1Ç ÆòØ çpß¡ˆHl߈ ³eˆŒ‹NßìG^Õï…{ZAü«.ý#zZÐ#Áœ˜Póà ™Ð­R¼o݆λRi±|XSB¡å® œ6¸÷¯þ)/j¹é^×Éê³ßñÕž?ÜBõIóéUïhP×ÍwËøßËÚñd˜UøüãfI!V/Ä’q—ÑÅ]˜E2’é%Á¾ôÞi­Ýüߣ³ÉÛÛРȞ=¯éÂ:ÍãP7ö ¯ÚZ–~§a,üÙFrÐAŒ…Ý‚•öR4íl9§ÎßýѵpeN )VpcŸñµ:=Á}§Õ;z×î|È ÿêç”^‰BUŠJø…­›÷c¶ñzðÏ|8×,¹ÙÆ´xd]ñ fÑ“öç)¯× ]–Óù? mÞLsù4ÙÖ¡³@Ÿ¤‚½w¼x%ÅB'k³[VrÔ=ê>Û ÁŠù}8 M5CÒ˜ÇÂEôñ<*~ì*ÍÚ5ÃÏaÒ–¥ìùÞjzeÂaþKÖ…[VåS£JI‚Zk6“ªEùŸ—BÇÛhX.èÂ÷0aåÇкû7I†+Í›æÄͽnÀ¢šfÑÿ14iýCò ' ÔŒ"nµ}X¸5ßxjÃ㕲Ô;È D÷tã…¼nnsÇdœ3ç ˆ†Í¢ÛàQù 8ëk"° Ÿàt¡6»üª²v5ÂjÅèݘ c·/!G>x/>D5ÿ&ÿD=šnB¶iæ¦-®‚ÿá¿âìz²×cÌkèåfŒ‡Ú*Qj$ëÇm=•D#ÒIºçXzqÃf\ÐJ³ÄÙ(‹›äíP®2pcË-ƒñ«a“¯ááþ¬qôçG –ö½’[¿óÜÝó3>9ã1‰94^é'øw'3Ó¡86ÕSÈ~y ׯÂ"ûóðiVì:³€­Ùsõ=!Mã8&E–póÍÙ…Þè ÛDÌ{øͯC2gÌ®þYÉv¿1£eó1¨V‘ùŠ´¡â÷ÇÌ÷·ÖpVóD™••:=ò|-n c‰Ðt`éý“9yÏð@5«Ü¯ûÿuS ¹gÆúxy\ Xì¬å~¬íyüsyZøÑ™½,¾ cŸ¤Ö[ªÔ“ç=hà}s¶~««ýVËt]³¡Xd7´?€j£°óp"N»T£Ó›@ýö)\0V‚;17_N’fUçûP•Ò'š!Lìó |î|Œ7²^ÜôÏÜpö%à¸v楆®Uá^0°r Må×Þ[ —=T©Wí"”«ÏZÝÔ¨¯<¡ªo‹ ôÇ*zñÜI²-Q· ´òÇú$0ðØ ²\&9äþÓ%œÎ)u¼·ã,:+EüŠ`¶hó?ý;;Pgm%·¬f±ì^ êpM&Kì§3wànù-ڿÇ.mÞk=¾â«T7þwz®y5­úHžjé°Ìޱ̾°¯ãa¸G¾®™wsž Ó\t‰ÚY«RÃ^?öQ|<Ú#«K6³…4š6ì.ƒ ¿Ì¨Ö¤Í,-ïm®RÃ'¶yŒ$‹ù{ÇyÐHŸÆ¶þÀÅ+J™øwêû÷-½ñß;ˆ ?m8LSª;i§B3ÚJDÁk~6®«ºÎK}6S£1õMÓ ªb’G¡<ã¡7ÝPÅÐñÔ¸jÜ»šÂìjÿ7ÿ­ p•ÛÞ †GêlCŠAA|=†—‰Q¹Õ²ð KŠ=(¬Àë2Ù¸·Â%Ó3Ÿi¹˜bv |n÷€º”&=VÆTOä‡7þ…æ»Î¤N ’lèñ†ýpIÜ51Þ·–%pn­ntïV]Ƈ¿nòÐ]P‡¿F2óŠe=pËÄ—ûðw2mqÆ<ˆÉ7/pìF²„]=Žy€·®¹ãÖˆ"Òyögç‡rQÝ n~Ÿ˜ÄmÇiÇ‘îS¯‰öDav4n{õí(·ó†:N6›EoE¹añé œÛI\÷è0&>¿D.äôÁtE¿ýÏï{Íßs›û!;‘[ؼ‘+ûd9Å…¨fLBòEɽì¹ü1}ÄR¬ˆ¼ÊìÄa¿ìû¡Ô3»H¿³c–ê‚h´¤—û Žë¦_æ*BUè–s*L}ç~ø.“ß¿¸a°¥äýFjN'ð_Ï@?Õ Tÿ&CKã^‘o2áÉð3xÚäÇ1P#Äj´Þèf\îÑÍÊ@DO^Rþ æý éwÔ~rÔ³¸Nêî PÑò¡·‚þàºÇ삟Îõð¤ôŸðþø×\FWn(E§¢%¤ŸÌà Ý1¸g,K*JýWÿ…,$ð.‘Ü}úŒ¯`BMÌ|i…Õ}°6†má°wª0[ì= 4ƒÞÖôc©6–%]†¡| 0²ï&s¦ÌÀîó§‰ß^J­ô[øCÎèÈN¸ÄŒìïK#ùù0S<’Íz–JODL¯u<鎔ySÅŸ­cŽ»Ïìâ.fÖãÀöE¦=‡3óäðØÉ%8tò7L‹ÊÆ“3p÷´Óõ ¦«1™§exùþ>ˆoʃ©TÍj+ºQqG5¶çW;\XãEÕíÅÙÇ'×ààÙ0î® ëvàä³™#}ßEÙ¸Bª„îTþÇ¿?%ᓎ/Ðí£R{áÅO•Óf¶zûH”âAØ´ŽÇÒ à†:p|ø:QO\Ï.Þ¢8~ ®S£U¥¶ø|í:ßå¤ílY‡ÎIIÄMªw§×ß-¤›†7Á ÅþÉ!6é«Áÿ¬.“T¶Ç­{4°í«=UæÑÝë×ò²N¬ƒ/‘G¨KF_lÖBj7WMÝBŒX#FÛýA_‰—ü•îZœ'ÆŽõÒO>`ÄÏÑxkè ÊÊ`Bëz!àÕSrmŠÈ"Nd@b/ÍaíÑ„ö<œÎ[¨rÍåR¿ßøÇv{eèÜI|".O±R¹n—$Bäì"2¿E¯ü¤™»ö7 •žâ2SnÏá?—>Œ…:a{ZõDpJ.FÇÈtÚÛŒ3ZyÏív±9–úlcÄAÚV†ÑQ1#¦ÊÔ$1D 9í%†8PÓ¬k üÖ Ãø¬Nœù\vgË<ÙÎh zÆvó˜ʤ»¹ãCÝœä÷eÜË© ^xK›åa#Ÿá6ž43¢FÌrÑöè!O›B’Å*XÖÂ-XVˆÉe2†Þ„œ¶vÎvR,½°Æšu-žJ'˜…²ØñAÿž<`\ho0¬v"lö£(èšCÐwüˆ†y]"KöÀaÅBööó7â#| ~~›JuIÜa šbnÀeÊÜ™âO܆JY¦ôèpJ còwó|$óô?‚Å÷Oà«WÀr¥:´}øz_áæï®'æž’ÌUù^Z–‚“Öï"gÂÒ™þŸÏøðh/ÜOcžçߣ¾ÇÑz1,†ãRrl¸Ê„ÎqyÂ×X§C½®ö8‰I´Àžqç¸ÁÛžø¬,îö‘-3X‚ÆvvS%÷®:…îùR´¦½öµœÁóoˆ?×ÁLä‘KK๕.ÿøÿCæoø6䓃b¡o ÖJ]À§[˜Ý¦~˜ ç‹b¨¸C»)ó.œÊá.ÏÝÍ‚UUØþ¤%d¸n'ŒÖÎbwb”™é¬•,]Ëy~0êÎ^üy¦³Î+‘çÛ¶Í[Fv´î`ÍöUbÖ³Óà‰Ø-|i¨çœb„FßöâËÅ/X¥”\y4“ ®3ãêœÛPí—4ïðžt—¥ ø •ÀÛw{šŒÂ/ÄèηÌ!~*¸nÊ",5hï*=úé´!õö|‹*4Ë0„ŠÏ‹`\H"Qx©Æ ·ˆ°Ù¶= óÕåE\úÇDtÑh·ÂM½ºì˜õH{ql½D lÓ(&ûäÊ®wÞ.ÅüõTÌÆ3+ñ×CmhoOÀ»9Pý»& Ú…ÖY5°âe|2×£–&=àÓôÇ-Ñ䞬ù†Ã‚ÌåÊü<æ0[1ËKÊ*â垌¡/485YÛ}×·ü&òYØxüAšpõ×þ2iUúVcü=¿æ´üŸòïN „¯B–Ûô©¢–<_IlÅEèÝFלHeh ,} +¶çñEôL9‘iâ´Ä˜m?DØ„³û¹´|q̸)LÈÜü×ÿOR¾C|~ _-Ú’›~Ø‹ÞÝÖz_ÓàNpmò­‚Ó`š¬*‹ Š$ 6*±y~S9èýaLïwf9"ÆÔǧVŠš³í< ýÌÕ ´Œ é„Ï<äóçq5ŸtéÁlRz)Ë<†¡¨jp˜ÂÉQ¨{»™[”6mÛR9©ƒ»>w6ØãÆX]}ú6×~+Íœ­ŒâŠ´€W_wý€1yt!:î!™ÑÒ„Öäq¹«á¼ØtZÛWà#ÇóϦ°µÅSIf2LÑ’Q'§AØõS¸“ìÁ€cmÿðߟt‘8Þ÷Aë%ïФ³ΟހüßÖ{q3_Óz }±H¬p3ÛÞÛ„b'S±`µf„O†'½ç ®Ï_s艳”ÃdÐRL„N\PŽé áQd´óüùì,—%ÝÎi7ü原5Ç-““I”‹ ®]S„îì¹y‡Ñé~ŽÛ!M/ìó„ ² Enf4ü ñ}>Ö’&>S_À‹ê—XáýF;Ë‚â“ôüÉÕŒ²†¢¿%¸à¬Sõ<:Ürê•nÄjÕ㇖>ÈøåÆ¯§EÄÈ#]ü:q˼Gà5·–ûìÿåþ÷¨ôtÖ°Ó*º{ žé†xqHbNî0×3‚Ýœˆë]O"§YÍß¡d‰Ÿ_½ÆKbiÎ=¸œjÍ.¢æÖ°Ü"šæºïC7×4úu[þó*­œIù[—Ó½7è ¯8cµšÖæí…bŽÌz~öœóûv¬Ën-Sè§¿ZÝaŠl=®ùÚ‰¢ûÂÈÕ›ñ¬¿z?L6³¦?j˜Þ‡nlµ¡ ܦ3;ÑX&§¸ŸÞ6M ²Á•0oÛMW&oŠT8÷ûôò)3»w3éâ?éªHùñÓ-ÁÓK•Ìvëjê{QíŸþ_S¸ÉíVâK_6¢±rA8šÏöLt`óÍ; ¾áÌÝÖ ü˜ÝYŽŽKcÀÀvèŸÊÑ?0sÇt–ÿ®9%Òn÷8Íç–´4ø ¨ªfÓ—»ÏFù0½÷H‰-ÿRÊɹGSÀ ܽ¼€ù¹ºÑ„œ]Ì{B6/`1ï°†)ø[;+‘¤b3qjë n¦X2Óˆ½L›Lãpæ6Iš7%ƒÎ²be‡¿ÃŒîhît½­ÞË^ÿx}Y’ÆÉÒ½.¯¹ºþFì:Ø f»R‰¡+]mä@ ½Mø7”#Q^Ô™i¢+ýãÓ€[üò’(Ψ.…Ûýxû•0âû9pú;~$«ÀE!ذh]¤gÅ’o¨-]m<®‹=›Á¤Qi¸ÒM®d±á)@·$'áÀ©þæÀ‰ [cÜ…ÜhE¾ãõ¥®ôÈÊ\>» “Ì'3˾@À{VŽ{TO£þ‘jx-1„jöÙµ?‰ÀûF2ýÄΠk%§ç[œëG8%ó¿kcyÖtôÎRoý[ŽÔá³I/È_˜d5 ·(½ÇFáù0[F†®¦OþÕ¿ÓCBƒeè¦Ãª,;:›¸ÏžÿM[‰ñï„ÐXD=ÇF0þ°²#޶[Â9„*œ¹Oå%°8ø=>˜QÍ“}VïøÄ*ؤÞ7œñ˜™4[h'[3¨BŸpãØ4ÉDúÙÅž¦§#Þ¢˜Ü º0:Æœ¦f “ë?'Ó„c[à™2õÎSeð¤„Îæãp†=.ÞÞƒWçãëÊk¨}8òåàyåµÜ4emVð0ˆ-ÖY@ÇWÇÏ«x0r^,Ž”d¶;<þé_e×j¼¶ŽórºÊmÞ[±õ¶x'Î u–´AIäwüÕ'‡õfÁhôLW‚ÅkOrÁYkaÙƒzT¹žŽü ²½‹k¦|à[Œ51›“lºUÞ©ÁÐ"?®ÆhoÚãÇ\¥ åú_À³Ä =£äþî‡$•èùíϪà*ðŒ¡_5_…¤úûR´mÅÛKpÇ %bÓÉ‹ÏTa/´û?8ßû{R-{ÈÔí!Dæ÷>,»’Ä{Õp×÷®†«™ öoîv½òBœg7ÂìƒZ°_&š,øž »Å¾"1ûñoþyÁqA4|?Ý¿¾&Äu:º·À÷œÃòM\èŹdLù |×I.ëɰº°ñ„"’CÕÄ÷ØaüX‡‹¦t’ú%–ÿÍ3Së@mV9¹†ugC¸èg‹Y°…ö|ÞOÊ#Ñ 0rÅC,q¼EòSB÷Ï…—ãï:Ï™ºý#rÛß| ‹ðá䋼#)Rõ]SÃÁs‘}aï†4,ïVf†¯uÙ}×,¸ìz ‹M€±i3áT¤ L­7aæ= p–Y¸ßç±a‹4øáeÇõä_#vª²ðIPngË»þ÷þ³5y—9·ßƒ°¯Â¥~lë5l|CYIøn‚äqªpG“:öã×ñLÈ$˜”„›ÁX–G>î,¢þ åpðè{nâGkü,#Ûø—ð·æ|¦w?b¿L§†ž¢°N -Ry¯ÔÂÐ2Ü‘–4EÀñ¸#{—öµþs(ä0Œ$çàì‡BôïË4&û¼…|j™ëì}À†IêÄAá,]¦¸ÖZ\Rp©‚–mSÃôï]äÊ‹V˜]ò ¾J|5' ú"˜Ðe­œ¿Äºd¯µkÜÊÍZôNœ$qô?JZ¶uPl¼Ý/LmD¬þῤ×|öàØC ãùMi z ³–­ÓhÙ6¿µšõxÕ‘%h¹-$¼}hŸ"·­-ÑaÉ ÒºÜ×Ü€ýSòq~É îôHé{~°I„£naúæ7ù;¤\’eûR+ÉÖGIJ´½îma &ž.Çݾ×Y±•UGÌ¢-",ËÃ’,þµú/ðØ8)j… µX8œËÝ_÷^‚&YïÅ!’Lg¡5zÁM;ÞÇ\Ç%VT'©Vͯ„¼‡§¹ÐÁ³pDû£ÂödÍD\r~'»i±@•»ÿü”z oMî~äÆX|&!ÄÖ(!Ê[¼ u‡8Ž|C©¼Èä3œú3 úà|,Ìjý ûÖF“9«cpòÛ_äˆtØðúIkq‰nÑÀ†‡Î,é›çì‡ó'Lb4͘ìLI0=œK#U!”À,Y”5IÆ‚«É@J§!b>—§€-m„øŽÁòþ>Rùõß|….g½µ‘û[ì >l¦{²P‹ö'a¥0ô} ÄXO¤OžÿæŸ#ôÀ@ü:|Ý6n‡qªÑ=ôŒÔHò´ã@Wó ÓœÅËwøÇŠ3ú8ê(òÀ]¯  s\f ƒ¦_¹ÙUf¸þ‹Û<â9îÔ>…EV¯0qiŽ_Gƒ%.à*¦•8p_…*×ÅuíÓá:ê²#J3Ù@N*TÖœE¥Újœ5'žHvc2·š™í”b—,ÿ¾yݼ©›h¬õ‡³éûŒ†vCÄ?^ýƒì¡Áq2¹-¯A¥îi°?jœl]·DÛ”êÄúƒg¥4÷]€¯ß~A‰}<8{Œøº±Ÿ‹–¾çDœ÷Á„•ÑõÎ7´Ùî«ÊàŽØ=mo¤À´ÀÓÄ4ÙvįFÃ…×lu’è?ýß"Ÿ FŠœ˜Û3ŽZEüœ3qšô|Þªê=|y¡h:eQ»oqÜ]+ÔºçË;é:»·&чDMÇ‹Ófgö$rŽ÷’crÍ¢°ü¤.ÙŸu„ïõžÂË¥ßx__® ^Âü½xgæf˜¢íK³®…éÝÛq¡ê 'ªƒ{ó^ÁÁ¿yr5[Éâ’)Ô%4æ: -žEp/Æ V=¤4ø Œ¿1žFÏ+†¦OS™lßiœ*}Õ+ÆÂëÃ!s$ËOùjÎ{?Ž6ÎwEòj7zXŸ‡Å®Ç!ä-Q‹'=öG€çñüßó¿b'qÕô~bÙ× Ïût±]F“ý7ýMq< y¼Ä4ûa5½H\Û@¸v ±‹üNcÀ./Œ9c†s)½tâ)\è ‘–þ8gM ¶v¹“㉑Drî¦^Ù åÖ”í:©Î6x4*frº,¹m'xö{Iºå{ˆÒ•¢>zCΠ"·_+¤"P~ötZ3ù § ,ÈŽ¸ŒÂM×Å¡pÌ$òð•ü —¢Ï¡ëã ìoÁyÜ꣄»û6‘¢¢ä@h6‘”=Wç¿ä‹îÏ·?dˆÕ•8ô¯W§¯…èæáÿ½ÿnB;ÓwØóŦtLÅcó¹!² ÷™•¡dK þíý¾ZÑ¥ÄlâÒ1Ã4 Êó-ØÃÁ”zýOq|ñ1†P«åާö PQykh)%c˜y¾N­Æ®…Ëe |þ$V¯V¤¿¶`Tœ¬%Á$´&Àì*]ØÔ;@äq» 5oô¨#påØd8J>¡©r¶É÷°c²*l¯Ïrèàæ/ìê,!jµå"X¬íY›%x/½WÌ®åßä;©±!pAt7*fr5jOɃ©Mð×õ ¸ßzÎÕG–Ct’*“‰š9_•ÿ­ÿ¼UÎÂ'C¸}w&õ9²T>\œm>úæëq¦•5fDK¤€+[ÛZÒg@Ȳ™çxónZÐ §“9£©Î\Ú¯½(wlµÓÏã]r]flÇ —+H‘z¯“¤/:´áæš÷$ôçV|·|1šGíÅý épxvú²Jv‹S;U)l×|€ý}HÆÄÚ³Ÿ‹Þ@ó³ç°RÖ”ˆo´¤ñír¸«A#n£ÀíðÈ| ·RƒîòÁ$îüQ2æn%¸ŽUç£ì´ú`¦9ŸÏñK­ ùþ9×ëbàµë Çô½±*tL¸¯ûÿ |ó„ºrõOD¨cÃrî‚Åk´T±‡ò}!˜¾~<èàÈ Ø·„*ÄêàêŸaçõ3ܺc!Ý@†å<M×$Kã¨gáMêÈýr·#ÊÒ±!F€}ân`§ÂO°8»¿þ ìJ¸|ÿ-nyžŒqgñöA6fûCb‘ø–ës8Š!§¼¸ô²ë@Úeñ\w \ž\V½eä|™6Æ÷Çmn79Z"Mó]—Bôþ©ÔÜ÷&濳ÁœQÕÜ ©Ü«; ÜüíÑðØ-‹¾ãmùM”g耇› “»Y’_qŒÂÀ~?d¼.¿Zÿ¯ÿ£“­¸š%*0ox ;bufìÒ€ü¹E\WÎqî×ׯ´ñ‹³çÆÌ^e :S:0rBÓ> ÏÞ´+±ƒ›5Ù×HÑ›ŠoŒ$醇·Aýϼ@öšž…àú,üøQ‡®î$ã2pŽ`fï’ ¹ž¦¼Qäþ²c—ãá¾ç¼K§ðg'éX*wÕÊv‘ªË½¸éïntYÜÆÙ¶œÊÛ°Ùf .Èe<×õç›lN“:2ñýìí¸Ç³öo¢×ü/¢ÄºßÄkô~XÙ'ˆ’@¡_Sé¿™jN~z†SǽQÄâ´Ü¿þ?WWÊɆªð'_Y…;¯Ü媼té »DØü`æ€Ìn?îílg´h\ÉÏÃ$Eø$ë4 Š‘.ÖDNlu`kïãl%S¿â7$×)°%±+ˆdÇ®æ†poöÂÒT[úš×ÎI¹ TSÞ:ø•ä/Ãå-NÌX¤«7œ€åísØPùh(3Ä[¿4°h­4µh|vkΑvmà©ñ¹ž®õ§dŒ Üo™‚««Þ¡Í‡B0^憛(P~¿ÑZº-¾¬a÷ö›ÐlUk˜œMŒ³³!Vw4.ùÜ‹'d…i>3AIÁÖþ¯÷€®5[®žPÛ¤K8ðøÂÙw¹í/ÜÀÀö:ç¶m" ”0e¾ÍŸ¸bÕS,ò窖9À˜#vlg{Veg@_ì)è0èÎÄ­·¦ÖX¹™©ŠáÍ׸{PÜ8{Ì®ÝßÚ1g»$$m»×'ÞÀ»<üóÇNøVpxäWéûÎÈSïƒ0<ë<¨‘~\›]N„Ì'л&ã·ß£±%NžmwaæïðV½ŒäôW5s“•´˜ˆä®¸­ Æ»—b‹Ÿ Û6âOŽÅ‹ýÒ=Ò§³È·Â]8<{μ6ãÿVkq>ýÉäý^XǕߓÿÍåÎa7q†€·FlÍ^Ü÷¿.ƒ#ºÑ ©·V”1ØRró¤÷àB%K–ÍÀÿx©–|Í\‚Ïð´^"ö.·Á~ä*°ªC¦¼êb/’M~Ýo/ Gúí7CÓ UÚqS—ÈÑ®]­ØºFœÓëøµ“4àà)ô^*þ¯þ5oÅaúÞ4ò¹Xý)2ч`odÊýúB²…­Ù#ã9ÜþyA$U«…7Ÿº8‚KžX„硸ßk²çS ¦ý<ºcÕqÙžøc.Ät­‰Pž ÝÑ÷ˆg®îŒ~ ²xåîU¼¤ØGåh‰shÞÜÍÒ£Y¾|¾æïûœ¶ý;¼ Âs;Ê ˜pl4½ZË…`0öÙ\Ä R(øë—ËáÒúÖCŠ„ ÿڈ߱& ÌTžqQÎ0zî$8–+Q-ŸÃ¨SòÔx{ ø<;Å-[î;ÔîpÏécf×ÿžFë{À$«`B&¼DÓ@3b]0 ]/ÛxSrñp ÚÖ=ÆÙ‹ŽÃ²Òû(ùh6J­jÀô˜•œÃ´)d,{M".^À„*3îUd56«Óq_Æ‘ueüÒ»h1®NÚYÖ'1rN"žÌD˜¼ê†ÏM!Î~eX{¼¼‚è¨@Gx8>6™Þà÷£íkbxv;×öëÞ+J…/ÙÒð²tÄ3TåŹàÉqçawÒ;^y¸,=¸ù µÉÑ›†‡9•k/ 8>ÜY;n?ȱ ?C‹³˜Ì%UãìÜ´ý4š^veÅÿùŸõ1 qfÎCâr|4 /RFné Ü¢v£S‹È‚«›I…g*÷ P‘Z®%ׄù ´?vd.rþ‚“üµ°fY:Ãé`ô¼-4Üa_7ƒ¾ÏÇñ|n%)6Hâ²D¯ÁŽT2­µû¿Ùl|4s*LŸaÈž­ ç9v ¢ÞgQ<7> ¬¦`¿2ß9?ã$OJ,Gêá —tã¢WgÑåêmÎeê:ܦœ‚½óËñ¡é™ú7pÏΛ‡–ðø9PÛà±Ã¢Ð#NÛn]‚ª·ØÆ׸7_q™A>\ßt7l•ÚÃrÄ"°ýj(ªe¨Ð5eýÌÁn‡Ìj—¤7L¹CôâRo¶u» ûµ’´…år1“i±¢›úz6«ñ¦=’U\…V.Ùµö– ýó¿S †øË;ƒØª:0ʳoa¬uñIûˆgúÞ’¸‹— )ñ.Ñø(‚«ë­`W#—z~b_ÝX>õ#ÌÓ š÷„hàå¼/KÑ~éÔ+6XüZ×áL‹ãx¯Œ0YÊ–-Üœ@¯[çpÍeïÐïº êErOÓ–l/&ðCß§(Ãæ†2îDá~^Ü‘1ô×V¯ê¼Ÿ¯ÉòyFœFÈ'ÈóÝBÓv·úATz¬J]9‹‡ÂrK˜>´Í¿Æû¼‘0ͦ0aE£Ó•»œaƒ0+;±ŽõG£®˜à6/£õßÚÍß®YÍjØ gbçƒøÆ üãÎVW[ †ÕaÒCéóèRî\™!å‡è-ñÆYØûsnwÅ7UÓF«bˆHk„ÉÙ |¦ 'o¦ž‡BËjtØ–8|Q£Ï¶~"|™YÌ=ç .¶O›Òwâ`T:<‹ÚN·ÜÙAg6ïÁ˜ŽPH2 çf©Âã% þ¸ÐççéŒO¶,þ9}ë¤#Ç£ëÆ½äåm}‡eéJ¸t¯#†ú®ÿï¼¼S--ärC Oùé!T¿(Ç/ž3œBor]"þìÓ!l™*ÅR«™Ákéú'ù¾;U Õ+©n¦9«ÜgC!}ý`ª;ê!>z-zwo¦¼FKÌíéÍ¥L3v§Lq¥é“kQv|í7(ÂåÒð…Ú(ØùÞ‘zç:ar½"žø²”óOHÆsa14mî_~”×1¶g±ÎÿÍF¬]€åÓ’ð ÒösÎ <4ë"¿ù|x„-áÃ$fø²® KÐõ»ú8gR‹›{ÆÒie-8Q~)¬¾£Im‚Ó7Î3ñíõs(öCšþò«&—¿ s|¸œmÌ}«`¿rEÙúYÙuŒã¯-G…ªIìÀJaz~·Ò?ÿ³Yo,·ý±y=nL[7r>€”â9hôhàÛêPx5‡†•¢Í èÅ¥‡N2Ù“Y´Íw9 ù•Àª›§Âލ14Çc‹ø;LfåÁ’¿oÈÃKþÔüU#.¸ƒ?ƒPgL5npºÀŠ#2XAo&øüíÅ)Šr,}]>ßtj<ÍY—M*ë’iÝß ¹¦²Èôæ²<ûݬo‹ ¸¦C_¨HÑÑ'•¨ÿ—HÚ^u‹ø{^ñ4饸f|³[#ÿ¶sÝ ˜Ÿ8šÚ=•pM¥ Š`Ó$u˜å I'(faü_/¸ž÷¿ùgInD£Ã³± c;²Å߹ǣ&rAßë¦%qÇHŸ>ÅëÃA—«»Ï9sWñׂsÜ™ãйé#ñ=î‰OCY²š?Ú&N¦¥u|»sýXЄGUê¹ùoVÁî‚{Ø¡PûjôéÉ¢ñPiø• ÚÔgìWLÔ¿Œþ6|î”òˆç¼rFMøˆÛ§q_c"@oîP¨ +l%xÙÑJ]ÖˆÍ-*\ÞÇ è]3NÚ¦à:³w¨0£?WkÑ·ŠJœÏùƒ°pü-nH4Ouý§£Ötä 2T›o¡ k I' \µgvé£@&0‚o´ê"i]–H.X ƒÒ–KpÇKö–¼Gû>\•-Dovþà~êTrçF%€õ˜ëU߀^kHí‡#ŽßÐ=/W?î$³÷e¨–µà?yR»$!<Æç?æ( ³G.Ę.C!–ª‚ò #:%é)æ-N‚]‡cñdÄY©†÷þæ9ÝyÙ"X1þî)-%ÂHìÄTιŠÛ”Jaâ„vŒ!Íæ_-á.ÆHq“ËQáÊBôÈ>5ú{ºÓ¿þ_?¢ã¯Eárçh(–b i–PûàÁXð™ã‹š‰×amÛvzûõ{rIòì>3Ä?½Ù?\w ú*Ó8ÕG…}©Ø¾Lå‚—ß!ÅÉ®(|½ˆhè.†cÎѨ˜ÊiTñá;|뢼“>IÜ9Bm½‚pÙâÂ^Æ™ Êž¡E"4…(“úÏŸ»K>¾¾CÞý] ¿¾ÜÁó[ôáfl.©{ÖO¾©PµŠ½#9ê.^ üŠBa×aéhM¸¿¾Ý´ÖÓ Ð›ÖÍB•ÖÀïG`gðuÌÙ匕cÐú‹³[úóŸþßÛž‡Ÿ?{q¦[7ÐÍ{îã–¡ý8ËiX”òAÃû"Äe&áíD6Tô—w¸EOmOâ]JNÀîTü>EŒ.ö³Â >³¸›Æòx<Ï”*Ï»‡/oŒ£c¢`ž¼4ó¾š: >Ÿöæì—pqÆ . ì<¯óÚ;çšü³<å¦aþV‚ÛW-B¿„K ¦p‡¢ë$9ºpùü?ÿðŸ»ò¤£Ø7!¸òEVÍâ·mJ¤¡Z‡‹×‚zue:w¿,ÎÚ´“ÞmÁªíºô×ßñ,.Ý•ob'A%ô5éD¿4<¨1ŸIúK³+e;X_ÍQºÙ¹ Ïíâ¨Ë©—£–ÒY|íbmzöc;¿v™ÿ¥É:BŽàÍÑùøÄÀ’:¿ÔçïZÅ =¦°Ô™›qï^y¨‹ǾƒÆ,ª{ éë0 ?E¶ÔSž9ú'aýò·I‚-ôgšH[¯ïýI”»Þ€wgT¢Ð…wU—rpè$¹ÔçÚÀ£Â6xè¤26żpøÿõ‹‡9qz‡ŽÀÜe3ÀÖ3Ç:²™;¸muÀ‘Ú=sñQ]·ñôä6¸0ù¼}ÑwÇimþ78LŸ}Íþ€Šiš,!pûvùœmDúý>Þ=}Œë;±'â$6e®Ë2‹¢s2ðžÃhnà×û¬—÷\©:P/]Ž]??‰yÏ}…æÕìðµXTU|À_y!’Ó¿pÂäÇÓé­±.÷*e+Ð~ßxˆþ CYú¸wu~جBEö¨Ã¼}“¥v ¤¼¤ŽüÀ߯ðã÷>¼r|7»o¥¿ŠýÿùŸù%WAeC57Øó÷QÄ—™X·ÇÒ8³©°ô$™oïƒ_V®ÀäŸÀ¨bÆÎ^È%óoÔrY^ÖT÷n#œ[0™Ž×N„Ž¢ôõë™ìÅokXô8‹­ˆßö™8¶B˜¥Û;Ò˜rq¼XW&îÖ\6® šy•Þ ¤!’z´~çžõ†³ÐÆî<7_Âô¢dÙ›YʤnYþá™Wô6/TüÜ?þoõä„ÿûö]DÃÕçˆzÈNh˜È¼õëq_‰hú ¶EHy)&ì—j7¾Ü‡ëNôáìF¤ãÈ10zøŽ›šÏ-Œ‚Û»„¡ùÍ{NÀç2Qšþìz/IËlEœ*Iݤía­ÜÜ÷Bžå|[É%/½ŒQ½u+æbw] ©šÛÄIÏß„¿·”ℳók-Y3µ+ƒF3aëF˜SÑ‹3d¨ˆ^)ßÓŠ‡…6‹¹YbZx—‡˜Ù‰|­Æ=LõC(gâ Z æÜÎÉçPFÀ ÏNìÇ)£‡ :c—²A>•¸Ê¯T þ‡ÿ¨äËÎÍ!àa#CcdÚ ¯Zš-×KD²3K¥©X¹%uQWcXñ n….‚W5©Qõ]h["Ⱥö_Å µ£Xõ'í}?aâ‚ß°Ó'Ž}0£''Hâ™ÆòL¸Ú?zÁ³b=ÖñqžYvL;v¬QCdÁzìKŒri@ =PÚþ V[$rž®éXw¢‚×Ö:LkãÁèÄ êì; :Џ.''ª•“¿êô«e:Ú­¹’;–ö09´ý]?_ö îÛ÷ M/oúŸÿ_ÇålúDœýuàéP9¾q9˹H¨°ü‚«°íH+\›ÈZûpÈÆœì©a~0Íÿ,¢¥æ3Yÿ†mÐ[kB›æDÓ»_ŽÒ¾7›ñ½ÉNv.Ý›¬J—ÆñºßÑáâeÛ@%=/ABåFÝ7ÐÁ󙽪0eUxèJwjOŒûVæ.fa‹7XI>F¬Éñ_°u kŒÝÎ>”6FM{ÆÓ'ÏT™JHq³»µ²× Ìj ·æMNN;ä¼ûz+Ôkijˆ‡+0bËb¼YЯOmŒùhò ünLéÛcØè+2ô×´¾ºëyè]f@SWHR‹]jð¨iÖ?þûU5ãíR¡ûÀnïåôü¤} ]ÖÝP¦‘ƒqäží%\xï-?w"÷bNöe}çÂ%tâÑlÍ“e{&Þæ>NE[·,oq£Ç¦ß"ž–\_‰>ÑŸ²sm%©YÃy¾zùKŒ.› ¦¯È„WLøÆç—͘KÈöí™)èdQ }‚Uû5™…yçú:ŸËÔɆ5OVæÁç5®l³Ë š®r‚ó<û.ÑþFîÉÀTX°13ª,A|~*æ}Rd^¢1ø¬: ôeìÁôwÔîñfï~ouVÚƒ ™ºÈ¨õzölîî—…Ú¬òø2 Çué×Zs~eŽƒÕÌ.öQ;f‹ÑcV³y3£`쪷ܟ{GÉ]Ãë°¥)ù'%Ñûd8UίÜæó7NÖf÷-%àü´ÓìÃËzÞî¼mâfùœ€3CøJË›ÿçã^Nð°0Œ?Ku<2áÇ}G<JèL—\<íE[ŽÉÑð—y0A`7K½q¯âغª×íËÛè>=ºø §Uk±¿4Ë.‰ªœ‹Š2y5SásGWS™€*¢PYÌ ªàªhé¡ïh¦æ¦øOÿCüÒè6]IúF(aÅ<®¨Pœ}öp¦ß> B«  mXuöªØs‘[ÎÃß×Ðn“„bÛp™"@ŠH€{ñJ:Nf1i±Ê5ÇÙÈïl9æÂr–¥oEi¯±4˜?J%Mïñ×úåœîÍ¥øøI,7¿Êž}\±ž}~늡Sgr;ÛèÌÏ dü‚#(çÔ‡f‡Ö›‹ÿ÷ÙY géÒáX8ðɈuf†a=÷Ùùâ¯Õl…àQ.saõòÉÕE´šOÇ)a–+¢Yþ†T¬­`½†gëãr#ɞòÔÕÈÂ?ö@ìïlhv¿ì\™ðO œ±á´{6¢ùÁ Üœ‚øZÏ|¡¢˜ššHÓ³ ØñïÓifÙF6ûN(ñÌå1ÑŽl¯ôG Ì_B§j¦é‹Ýéõ?] µè5ZŒš%sUhø§§òæ„j;¡³cíåWVÙQ'o¨ÖTæÖOlE…õôÌÓ5´zé&Ö¹è6ý6=¯~jãu\÷þõü© Ù÷åiqø1¨–È­($Ç•‹áϸ><›vï¨ç£Èwó;šJ- »‚ è+gæ3ïÖnñ­‹ºñÈ>H_|•E,«%;£h_±+“pÁ”©œƒÔa®ô÷<ߖޝ­®ò®¦e¢Yu çpg6ï¹tž?¾f{ôqÃ/à!‡2Üyõ†ÞµæàÒÂ?œOÀf¨ßZß¾¨Â-:D¬ÎÀŸ†TYòƒü½y»º°3l; ­½—[cùÎI\õ˜HÜ(HT÷:ËZ‡‘m g—•ÏŸ¥ª wòØæ+Aó—ÌÂk§]±DêwòI÷Þ‹°î¶ÓÃ.‰ù£¾áئs(èTAä¾ô¼ˆœ¿ÿA h¨&{7žìq§Ð¶…àÓZ)üY|„Ü6<|.~ÝXþwMÚró\Ûœí#žçaå+Œ»e%zãÙÆÊfœvÙo:Ž"3WŽÇ°p'Ø,¾-Éqd 3¨„ž \tµÁ‹ûnñ~¤ƒñäÔwu4SqÊb7ßÏ‚|û[v ø¬¥ ÎsÉý¶5lÅDÚúåkðr¯)™<‘­”f’Ë`íâ,¨Ðó¤9çRéï/E˜sEÞ¯[ÆÔ'Áåæ/0O<&œÙEí¦_‚wⵕ›9ué‘5ySž¹»U°ãi.4ƒ S¯ñŠyc 1; ê'žá—/q˜à$‘‘oA&ò,QxæË÷¿üƒ¸¯| ïbPv`"wo—*\:…‰â¼IòP¾n?èô¶q/Çy¡ÀR[¬ŽEmË8»=>lʤJúºê~s©[3à“@'üw샰ºŸ*C_q†Ò'òIK Þ%í£ýµÙÇè.8െMœ{ŽYÛ™a°ý]²ëä žÙü¾\u`V_ä¡ Ö~]k$O#ÙÞB/:ò3ˆO}Ç·«³ï˜ åEY˜›ÓˆZÒSñ̦1ô’Û{õ›@¿ê°¹/GüÆS[HK’eêSæbé›=¼ Ûéj/9¶vj)¶…«ÁÂΣôÀ g\=q ‚Žr5®)N]¿1ŠšÐµÄ>¿zòÆš±¹ oœïÓé/¤é 5äzå¬À)GŠYViãõÑ$Á1C•Þq›ËxŒ´'wÀŽkAêüòRåÖ’‡¸«XXÙÕôÿ@“±bìbè3Œ—]ŠÓCÀÅî/xÚOìK(`›ÅwÐY*"¬3õ áˆÓyE«Pàw\ëÔE“¯µøH?>„+Òµ¡N:#<†ñäÍtòð"þ- Rü0†_HW·ÕÂ6Ãy´±¶lk9 ץƨ›%œÒ:ƒ¢v³íc8xñ œLÆÞÅyœ²÷^ÖÉtx»ÑŽ]wœÜ)ÚÂVìçÎ*;Ù “½K¹ÃeàV¾;»©Â)©Í‹Ëòª%¾“ÄYåïKÐädË^ Ù`O=!_öBBž"î8=FÕ§Ãý(qÖÔt7Ȳä—ÞìsÊqÜ%‹s¤ñ^:ÓZwÔ±û?ðhË}X³!“¹^z hUp%4õf,ë«(d“­G±y×à6áÁ´2Œ¤Wö(±[ÞŸ65‹¼=—ÈÚ­wÂɰï÷'N;ê*Wⲡûõê{¸¶µñ óÜ”-,õ‡YÿâxµèꪎV%H“;|s~r/ºÀ]SÄßUßI¡`V>§·ì$nƺØåšL|Ü0Ñú„ÅÊX1;›éÙ^'–eöåÊaÉ W6©(3§Ë}ñ€þcÈùpDK}(õ—熉«Ìp£'ÄóÂ×àãZ[ Š„§Ió˜òùלÿ°$uý&ÆÔíÎáýÔIt_Ü]|+•F³äŒTÔåtý‹G.Ü1—™+B¡ 4¢-«­Jâÿ–Ïxnîù ¶ŒWC9—Z ûKð­ÕxvtbŒ ¸¬ùÚÿ* £Ù¯gŽX2W‘>k×§Fyr¬tmzœƒ§×wãuñL¨÷^C/(¢ö€ ËÛXËæ¢_Cé¹ãÏíüiÆ<È(О¼.v.e?kNôúVLzTS宀YÄNÎÇû,n¬ã¹Þx…™Í'#*2AÔuLXÕ†‹¢7[£àÛYx6:ŽÛµµÇ̤KeðkÑz ÃÖýÄ—)s0²´;@>Ëœ‚µw¶÷C‚é vA˜pj¼L„®OÚÇϼÆýòf¶«Œ˜‘—^¬Õ¦†¯÷ãìߺ<ÁòJø”¶ŽNüV§ì­Ø½ÀY8ãîV纞­8Qÿ4È*5ÂËð®H”=ù#Êï=y·G—~rûŒ¡É|hŒ_DLÐÙž+Eå7vèîZ‚³O¿ä~YÉË<2™Ùgï§×ñ-)¿ý )°UD—Î6e ö ¯éÃìßÇÑ=W’D?ºÿ]Ó½ó£qäóð üYC‰ôeSÞ´À¯°$ö.JøC°åxZ7nw1YŒÊò³Ð?k,™ýö6¸Ï«{¼2#t6û”^«¦¾â¦”º°O‹DÀ}À>ÍÁ'÷gK‡ßätð‹?Äàáa\´Ú]“¶±­¦'iud̽ŒûV©‡ÃéÇpãM:IH’5Êne„E™âŒËhf Àö)E9·¸à¹œ6.`¯és")J8½ž™/`f(Ú»ÐïWØÑÖ$NGá?á{6®ó8·¬Ò9qö,»Þ¶jò›Æ×„G´éŒÅ+†7q“øZúò¯þHG(\ÿ/mÛOûã§äÊè‘óMÏálë‚è’•.LÇr]ºE›Y‰¥>ñ®TÅü!rsuYôo3Öý|$wîƒù!Þ¬Ytsxj WÝñ²zŒX{“õuÚW¯¤WE%éüü+xGô4.k½Eƒñ0¢™g[“1±¿œça¡Gf±½ýÀÉ Y÷Ú˜žY\!?ý°þUiU³dë£óxKà$ªØ¨²d­:¾¯>½ ÂÞ!,ib2†¹Ò {”訊+ðÑú:7Ê?4<$˜àÕ‰´(ó*É6¯¥ß›é°Ñ%LµŠRtØ‘œù /qlŽëVvì¤혽 ãgmâ6žm… éNçÊ£‹£iÏæìÇR]ªÛáÅæÕÁ“K‘ü³M‘´V¨=ÇÒä?À¥v&+ø~ŸV‹óEü·@FKûl¨Dãt—ðžk.f‹>1»_¸k)Üï{™½¿'7íÌ ªm·‡T{ÒC è¡óéLå£4[=U”®ôÕ ¡¾ <]–â¼¢ôù[<ö²Ês— !ÜŸ+§3‚›Ôñ®$§ãH5í`é™0N4D•Õ>qd§iÛOMÖ{1Ÿ=ÒÏK¯élÝKzÝa,›þëóÑf¾>·è½i§™c®»<ψ¾ß`ÂX6¢£Ëžû oÏñeY–*,Íâe=ùxøÚ9zÄ4žä:+Å R‹cxâj5gñ¸b¤3ÑÝtýÖrº63n‡I×U-×Õr#yÝLÿ ™ôæ/ÞIéÐΦM¢æÔÖe=•² ¢6îe«ÜeéË#¨‘ãGãŽÍ¤¡J™êò¥ìa7pÞ페öº¬Øuä²ñCn#ÇpëL7Ú˜Æî~[É6zÓÓúˆEð_îµßdvfùO88•%­ñ†² ƒuXOÒq0Ü„†>ÀÅZlæuf’<’ãmÀ{Á1þI!)–<­¤–Mq;ŒIÃß…á¬ÛZJɲQ'Ȼ߯ ½ƒù°º­KÂw9Þ óa¹¯|t¤JšÒlçºÇ$™çI•è;,ól„µu@-Mžã (Ñ3ÓA{ž*}÷}'~Tˆ·F¹S±4î÷iøó¨ìûPïuý+V”ªÓ¡ù ÈC‚Vh>®Ö͸Çì.¹?k‹ø=«Ë Ù³îÜ¡A~¤ŒÃñ‡‡HÑJü¨*Þm<}*^‡žÇÞ/¤¤”A3Ò tá;¤_ñ 8²ü’DÚü€»æ9ÓÎ÷¡ánñTaóÇ4,ö$‹Ý0zT ž­ºº^·¡ßlußÞÉm¡ÙQ|¸‰òt½˜³9=Úæ4 ·G85 ;†¹eHþúYœú‡5DNÖ*+b@ÌBøüËo¿O¥õ4™›rx–dá¯ÞòܳŽRñ‡‘x±£ B‰ûÞÉwÙÑc0e›¾¾Q§žõCŠfŸ;Ö„9gsá–¹=)+N_Š\ãl,÷áA|+wƒWÿ˜6ƒ³PæÃÓµ¤qJzYžæ÷•€„Æ!2¼Ã”6ÍSD—‚÷èðZ¦}—¤ûsá÷4 8ê¦GËÜèOÇa´FQœ›´n=Üõ‰xLf3¬Ôe5;þâÆQ ¥ïÛe ½æx23ƒ­J–ÈS—åaQQ<«[.îehü§ ÅÇ‹ÑnåxîçEÈ”d‘‘¯meIÙÌ÷^%67Ôþœzˆ5ÏÀ_w>î&†àp”Sý&cØ‘¶¨é$®)*eÏ£ù÷J0ùe!JîR¢qðų\8-ÿÕù?aªnR| '§‰0¹-•hv?…8¶¤ÃôÕ»Ù ãD®œi8É£cÿ jì ¤—TÂØ#Q8-¢ÖG7↱µÜÝkS©‰±9ò%çvODm…1 OÆÐ'¼BîÂûë\½P7Û"&ÅþÈe‚Ü»›˜i€s¶¬§Ïšï`»t ¦üŠ¢ï¢VÓœGÓYAµ û4î%°ª‚›Ý™É1Ôáô›Ÿ–±¯½=(;‚ùøçJ8Nm¥9½Ûy¡ASàXh8ÞXAN³ xYæÄªdh”A«9öìm¸çòTb‡W5XÝ-3˜²[žÎÜ`MÊ7õp[oƳ¯w‚—ê<¶óíºÔÿŽó’¢kŽqÊÛ9àYØ€Vøn§Á\êÒ†Ñ#þ8püún@‹©ŸË$dò¨P$4ÄjQ«n_æ­•ÌBÂb›Q ;7S;kå[©V8&þãà&ü6E•ŠÓSà¥Ç¾EÒ+šûAâ}-8œg~ÜI"äyœÏ«G²èý@®+§¦ÿñ›ã¤hBé,ºóQîx3ÉÀ“Ó.òÊ×KsAÆ'YÇÁXµô»S±ôCèX!¿nÂãPX“–Ï2ÞÚƒë§3ÜÏÓÀý.A¥â#WšA¯–é1/¡Xg-áƒÜí b¼æ ß×å,»ŠÏBGQþ÷6ÎRc­xÒBŽ=Vg¯êmØ,%+º14h]ÿÄ+ùèÆ~gN‚UC÷Ú¬8{UDŸókæóa°@¢þéøJœ>Uµ^½ Û§3¿'ÜùGŠ)µ<éÀ0þº_q¶âúÁtµù:€s‡¥XG¸4sÜe@íM†¥ŠôéÙf,œ`Bö+³§ä*öÃÏwjМûŽ—~ÀÜÛç°ïŽ1.ô(Æðà3Ø“¯ˆbÇÙþáĺõ…ž0>ENèm¢í®Z”¦iÒ¢W >L„ýXÈ#«E0èA0sUz|,dkñÖ }ØíZ‡‚­9™­pÉ ½hzˆ×f!»c²„/õBì›Ëð½ø8ªŒÎ_­aoôÖ¼E·oñàÙ2PÍìbÛ~.æÄ•.Ó”:‘ÿ«gÉ™ùP³å9~¸¾€½äBXÀ þ,³R8è°š&ÕÛP•…Pæ4bרÒС5ÌõJ…–9ñ×5š™MÐ(Ír¿ñᇦ04×|ÿâô|Ó(+üE¿kq[ƒuõ‰Dˆ‡Wº”9x¡F#-‚ç¹™¬µõ æí•Áº˜‘œ8×;tô¸Ë‡Þãf³D'ïO7ÌŽ“þ9w ÀqÇg²¸x?k˜¾f˽ ͪoáSÊ* ß\ Ïiž7–&ǦðBVwbãž}ðJ$Re£Rû6Ök#I¯Ü®"Úæ2s‘=ÔòošMŸ‡–º•8–Ÿ†ƒGòÞ%ºhÒrPúy†Œe âU+N1seS#dh¬ü/œÔÌÚ'V@ºR&·¶ÐÊrìÉI£¥P§'ÊÌõßCÀËŸ¸·| ]ÿp>1~ƒ¡²K8ÑeQøªe¶‚Ýò+Éñ3²t­¬›³£ZŸŸ‚Þ—ªìÒß8zÎŽ6Ÿf¨þKŽ™;Ld²~sK¼RÛz,9ŸÉËÿ©Æ7¾mÊ’$wàî3Óà^Är,º];K¶2ió¸$ë.ÎívÃWÄ×w€±]3±êW—HæÑW³Ïßò2ÞÜ“eXОÃýÑ›Dßî2Dáàרö9NÎ4fNS§¢÷ëF\’žA&”,¡âçƒÙ"8 ÛaÍ´c¦¿6Œ¦Þáãþ{\¶úX4tåHÁ¾o°Up Zúeb¸ª)«+ˆ„Ñ©âôÃsHÝ›ËYÍöf›ذózéðÇw‰!/¿D„Þ²›7×[ï“NgùÂ"Ÿe4x¹´é‚/ÕéWþ-nÁü{D\[ÌTí°o|YÆBýƒ½Lø®ŠšÎ¼7½£÷ÙänøMÎÞZ7Ëð.Ç&óáýL,V½‡z)tÝ#*Ý©´SOËã$ª]Í-ž¯ðIFvLNÅCMkqóF`‚±]P˜æÆœ¢ÎášA-öK+¦®möZòá¯2ÓõlI&ÛQËǰá³Ü”­‡1Î^¹ÊNÂôòø:o«‰ñb­°ê«&VYøÀ²jO|ÂÝ…Æy©üŒÅ¾0vío¾è#_¶÷óh¬ŸË©~’BáoËçz:š»Î`ì}q”~í†OwâpÂX4ðˆÇmëÍiÍÆªÐÙOl$kîÅò¡gX?ð„ÿ3RoyØcèM[˜$¶tú¡ÉÄyØvDM’žaìï\85&j ‚!^­„7yÜ.íW÷S&„JÔ,… D3ÂP Ô¡õà!Œ[œGº'õ“.Ñè2K–†šG¿¹‹Üú¼öi¾ü׋øÒwо ¯#΀½l"¦œbÈ/Í›9]†´_ÿÝÖ±± œFG¶ÜçM1=NsÕÛ¶pƒ­ºl×%XT¹ž<^qöÄ>CçÉ%ÁüÂ<Îôò ,¸ö\q2›Òy¾Ì5䢮~ý sè†3»]±Fc*ÖÂÖÌn²z© ¦V`Ó2Ö±ä›JìmŽ=;o1XÛ¢qv*DeèáÃME`å^Š à¾OO@ÿ›.+}6‰³<ÎAøõ7¸kàÿÓ«µœNCĈÄÓì@’5> ®ú¢õÎÅà¯&Æí<ô˜Ó9Èj?¥!—v²â÷ѺßKð”X.÷cÅ!ô8R@Nìy€ïý4˜v':&Ðo«ÛñÚŒ@œ{ô9=."þúÒKÎCÏö~ ~7pËöF¾æì÷øzk~üž÷ußnð|¯–ŽZô’Ð[xv.Nζ¦3oùSV+ kø'o’\ä¯LÅë<ñó³ÝôêÜ<ŒKX©YY°hð.±¶8ƒ7;0e»<ÊÊãŒc¢A§¶:CtQÙ<—‹“q!rMàaX‚ù ±p*%Œ›\u º5Ù¼Cm°)lîÿÐÃ5}þãžÃ,¥d¢òYÄœšÏ“s»2…¦èóqQ}´=¬Ü"Ctàƒ­»æ -ZÑÜ™PÌu»£¦.ÁÐ sS¤i»`×W”JòÂßñö^jÄJù ®¶ï•_╯цCkÞp¶ a»Ë;®Lå#8?¼r1¶ôÉÒHì•Ù©ÙÒTN²å7ȱ›ÉXœŽ©×k‰Ÿþ0æ9Àp=Œ©YG¶8ÓZá ÔPI‘j8±žûW`»æ(å¼—šÃ–ÃF,þf X[›âb¥œmÏ  °>TÆ]¶7ÈÇå³èµ!1:èÀÕ¤N«§ý$ã¦5Lž ÁÞ×d8(EãªÐtÜ¢YÉyziââþñ¼F+pn£9SÊ„òÚ¬§©«WÆ‘Eë é Å=˜t.ŸS9§Q§Ø U'r’(JmN¯…qOÖ·÷^20'‹yŸIѹL4þÛ “aYCM£0ËAeäsáGTy±/ûÉá¯=*¨‘3‰•-»MÞÖrÜ´ä[ܘ`n®ã V |Nžå¾×¹r²q͜䕴žqã6ëÁVï±4)qoÌ: jŸuiˆƒ5Ø~eϸuÝ hô¼‰ öɤ~À²srx® õ%a˜]*™·Ù—svì'®­:TÓ)ã?܃ó[}hol0˜Ô qo\eYíHïGe ñCïé2ϵ„ŽI\¶>lìÏ2rrÉE`¦¼u2J¨·ç< Ú…)´0é¢5­ý†¢µ(?ö/7z’ YÀ•q“_ÙS…¢Cðà#*¾Á/÷8Ôß) é´=ž(¼ Ì‚‹Û°ùá¨ZfªX€ý Xo¾—{)Ë¢õ@Píïêsœ?gÞ*ãëõqðûÐAÎzK˜èÌB£,a6±UÞHIPcÓ#üŒ Œè¤<í;ãr¶—Ì%ùì '¯æKʪ_sÇF¡WÃqÔXhA‡}Ta÷èm˜\© *‰àyÂ…LŒ“‡…[œ8§³KaŸÉUxy¦žä;R7ÞÆàû¸EB e‚¶C .ö.J’šdC( 4cŠ~‡Pûi&.êÌÉÂìü¥ÓäæWo\=Þ—Õ×àÖ}ÖP>†æNߎOfJ±“Ü[þß§f0Ó…ãŠ÷ã%‰Ùhû‡AX´;®y÷ ÌË hóœV²oò^”I·ã‚Z%é—׸–ʼnàwÅ ¬dÜP)º s¾ðSOêÒ‹ |ÃwÞ<~žÝÑŠëVõq×í¾’y³{0¿`:W2¹{$ƒCøXÒ%“1ûŽd”R [kBE=!sÏxÕ§óÀ¹ÇóXWÙN”Ò8‹mž…(×¢ÅÍõ,ÆYþœK¹*®ßÆ‚é vÒ²]°6õ§b\‰X[ö}¿µk[ðšÚ-¼š;‹vms„Ðçá˜rЉ*“Õ^WìKà ¾¾÷+¢Â…a¼§òGðm‡­p‚„­àVÈí³ß m_ÆÌ?‰M÷\ñÑÆ1¸y_-1K>€õûá@ ¹½t?Z±Ãx|È–™ÍРÇð*÷qÌ3ß~m#£m$¡Ä^•.„‘îŒ;| ›ÜÅo#\TŸA"§¤×_Ýz‹Ìÿ:Ÿ´‘í1ûàd½Í¿6 –EºQ›%ËpL 8)@%ÝC¶*í²7à䯋°ý2Îh›ACD×AÛò-ðnz9ôŸ;9· #ˆٚ!‰_u”­íŠóá`” öÔƒôÊ[xþÅ.Þ^ŸÞÖ_ F÷•XEvçÇå F;r‚îM Ý˜Ã™¾3%îïEÙ»—ñÌmë3ÀÑvà©ô>x&Ð/ÃíÜÜ˭Зz‚ëRššV†t}óŒO™ÃJì^¡ÒúBÆ9¿ÃK‹ûœËV>å,Þ=–l»¹‹?ÊÐ—ŠŒ~ Í%Ð~êʆVãÚT/Xñ©m^š±îû·øýŸ„Øäó›qCä|^Îv:îñGÂûP‰»íÅP=6 §k„ÀÙÆ øõã8lš¿ ǖ»ÄQpá],(-„÷w¼éò7"T·þÊ»«fãw“eõl¾(‰¼æŒ¡§1ô¬ëV†ï‘r¯nH÷œ‹[¼`Ù>’ÝÚ~Md/tnBÑÊc¸æôWˆ>eÆæ_„¯'[°c_G“¤‘ìúà9ßd‹'·þ‹#w`§4›[õ/7fpß©P¯æöãúuR$? Y±~”èEþ:ÔnÃ6”´—ÃôÊÌ1ðþs+\Ÿ9ÒåáxÅê{]Ün€§ÚÃé¢Q—ðå“—$3*ŸŒõ]C7i]@g='zRn/&šXr+¶ÞíV°jK6Ìv¯#3™=Q±žZL8‹Ïª5é|£8Ü"É¡º­TÊጠ7s}Ñ;qîä\~ô.wÖ~8&@§¼‡›]¤49ž;69 Ì4Ô¨Ï ; ŒÛ{ÞbïÜÁÜÏ~°|ŒÛ™×NLH˜Ú>jXvOÌ+Ké4*;w…Ÿ-ö6Üš«5Tkþ1øÂ{Hæ~Ê7óôIJ'ö42œmÓ º÷rQ›^áÏ‚ê}œ¶/f½[;ùeËèy4¼7C¢ K™í_OCôÙZ"¸ô*ÿ½Æ}X«Oç´VÂ’ßË©`£óó§µ³›ðÓ¨yÜÿãèºã©þÞ¸½GDö($3#MîyÑ@RIFK[JZÚ™‘lQVT2JÊH2îç9Fƒ¦R!¥ŒöFªŸïï¯ûúŒó<çÜsÎûy¿_¯÷=÷ÚÁrZá0‹3È?Fõ<6Ñï£TÀ!“õnEÑ©² n¢ÈîËO&Y._x ‡¼iJíT5ò¤¿¢£h㜣œnÕkVã'Á2ýÍ©åQöRD›Mråïèi€üžTÕ$ŠHÛCÇ~^AÏÿ8tl5.‚;+Æs!³°Ëˉ¾y¬+$@Žõ_žÜØrÔ[ö™{á] ÆY*\õ…Aò¬\”ª$¨ÒЊ<|rw)»º ŽÓ;¶ÂBOC‘%[,°ŸÏ)­ A\+å ÏÚø>Âõ$#PV—¿ÀèS²4æÙß‘€Öš70ªªˆÓÿö†w(ñ¨H-†Ù‡ü èd&êvä§lü¬-Éq—Qk•:¤ùdsk@Û­,xšnÎ~Äð¹KvQ0ßïiÍcÿx/¹µbŠÿņêIdüíùdû”jS¿ 6/ˆ r¯!# C±ÚðêU"”iýàœ_O‡½T~1:ß:ÕÖ|.›õm%õX|îÕ©±œJ,rÓbž…iuÂbîmç8·Ÿ[‹ÏñÕÕ(rDŸNVZC÷ØõñvV¡„« \J ?¹gF_‘ †º½—Ðû¤–¬T¡| A”ž„ßî*²¹§À¢\Eö¤k õjáÀ¼¥J@£6fÆUñ ìzˆd.K y‚bU ,[sžÕðBißS>Nz{úÏÅ¡®ŽKÙ:ƒùZ.ÁËí ÐZ%œЧâagÈøø¢¼9Œe?•ÕxÍïàèª ´¼hmªÐàÉLèÎØ†i 3é²ôn¦¨,vì2©iV¬­3Xi²"cA*•#Dï–Ïa«šôØì¤Xv¢Ý‡>“dsoUpä~òþ–+§–˜„®Ê1ÑcÎÄÉf"+j9€§që,ëÆ†nlE—:»œw+ý­O_í«b|}9w»sKûáñ±|vàƒN/\Á¯˜±²YŒü)ˆ8n2qМÆüŠŸiQÝZ 6%.l0`ŽN'a³ž&öÆ‚Áðnh¿Ã‘—³°è¬ËÎìç_ç0Éí9„m‰ÃYÒLËè(-cÐhЬíðiøTÔ5<Ès}!Æ6¶zcEqš|8I•ONe/§Œ§–û'Îó±ÚV—ÏpçÆöGП9?ÜÀP/=KtPU¯’Hfú4Ÿ Ç®b7BèãÒqìðEšrn2Mð™Ê„ìÛ¡R¿‰x/À”WS\n€½_®`y© ²tù¢´J½Ôκ±9áã ¡ãvmʦý|½û0bê5Üf¯A5“”èæ3ŽtY¸ ³hL¦+ìÕÙ/«ö ØGåkt€Ôp¯SÓ¸Ý!–¨XÜŒ.o9Ó4u6iê z¾b—n>ÂÍ–Ä÷Ó8vN° ” ‚³™Û> öçÔ«>Á9ÿô%Öœ»Ÿyë=HVÖFR ]†ªE#°àÔz¾—ùM°hï'wä3±o· k³ë››H»ê}¼#¯Áw³%eð9ß ¢Â 5W÷ÜþI.úÑû;RPá¸8­P‘dW[ÿa}ù~$WJ y}„˜r3¦²óú+¡xZ:.½ê¹Æ°¹ÝW{—BåüÈ`&Ìö›0ìù>‡˜»Í7à pH#ҫưoú:ì M®pœÍßì?¹1¼¢ ÷!€Y²…GA¿[1ü:ú—§:>Ý0*+GÇÅݽóo8f‚ȼOx÷‡;xÝ™ÕIç ÷WW*1‘fFTïE ©¿¶DÃ¥«É¢ÛôV†-‰ Ç‘[rä¶‚U()Äà W/††¸Ì6±ÛHÑÁýá\Î2!ø<î>ù+Å»aÆWüˆÚÛ³ÈäÔ{vü™ö¶Âu_8õ¥y$o¿¾Ð^üW G%^¯…IËWÚ™û‹1“½O°¸`3·`ЊV¯†Õû$ÉÆ¼>,J>ŠË/’WflYK0XîYßLq£¶9ç¢ýŸAˆz¶÷©i*K¤Ëó· ñìX° À¼…ËUÖèMú0~z+†äÛéÏÜËÆ¡Þöxàï`oM×®–½°ìð=\½Y”]’™ÜYF_ø>:…ê™ÝPtÖ r^™äª`êú:ÞÂy(¶²Í(®ØàÉ ZÞµ:î˜?ñSVJÑï.â.jɨ³' ·Z2Š/Žøo  cwáÂ¥ËXà”n‹^(2c¹ƒ2l«¿¬¾ŽÌm/œåþá¯yÀgÅ'¼ò0EÖÌà }žˆ¶‚Ì v,[ëO2ê¡¥;„¯àRb XÃÈS®îå :´Ìï.Ä¥OàtäB–’‹7÷‡pkUäPzÏ.ײ‚[áíðo(µü¦’1·A¥ºíZöò”¦_`¯~¨2©Ÿ LôëÎuš Û¹f#dͨÀþmÒ´(pzE%ù­úƒ \šDuVyæU¸ñÄÞh_¯L·—O`§^BɯopÎk)ߤˎIu|†iv7PgØœV즤_[˜=¾nÍòåaÝWô®Wdšo€Af46¤˜âõU°åQ<9éìfh€Qûjèá´Ù Bù(HÕKßáCG諯›©©“4§i>‡NêaóçÝÕ@j ljÉÆîÞÏmiNgGËÒAäà^ ¹,„=k¥™‰?Ká ΉœDO_ÜLb(ZÓµÿþו+jPÅ^Išj,AŸÎY×AÌØ'îçAlls}c®^‘ìð]9º£5˜IöüÃE"¦E×/M§ýe·ña•'˜Že¹ùÚ4Þè4¾ÛaF'7´CžK3'4ÿÜy”n}ƒ‚}8K÷mðöÆèhL`­±YxØ<5 RÑŸÔ£]„v;ÎcÊEù‚Ê—9õ…rðG¦xü;ðøí˜Qù•ÀÍ}}W¥ÇcSÉóåû`Öœ|u &XpA£kÿæIY¶2M–®˜Íü6‘uImåRfŸÅzµdŒ˜¾éÞÀ´ m¸~Í“Ëòqë“`è’úÕÍW qŽj´ãøT]V8t35~‘åµ~¼åÝ*ÔÉ0¯öOG³'ñÜ„kjÌ¡o´põßà½ü3 ‹w¹ ”ÝÞŽo\Ti•T‘_ý_i~#ßUšðWYê¥k°úûp­E#.9¸î´ça$7ÇÞa—Ú9·Èf44 {õgäø‰ÉèÛ½w™°ÌL?ö3j«²ÔA×?Jð:X”Zž€É|y6Ù]WŸi–•dƒFl ˆ¼¢|nÓ{!ª§ãD¥×WcæÑz°îçà•éIê/¨BÐ&]sˆ¾+Ê{óqcé+èló LùQ…\l ÉÆ¡»v`¢ºµÖ®§’/åèðãsà¹~"û0¿oÓT*z{¸.7g»y9`Wøˆ$òêÕs{£8ËÇŸQèɸ4„Ï©à¥Él\ÓXV”݃úÉîhýt,†õí!/[íi¾›3ëu ÖOSàgnxúY±+X› õ§¯¢éÃRÌîÙ—E®’a©$üñàé4)ÆeóÛ¹ˆåÑ þÐcbZñDÕ®­d1“\µ?Å™Ë#aZeÆÒöȢඅœlòQ¦›^›Ýƒøë‹üßuâ)²ùF fÿ‰€zâ4„Ãà6%<Û ËÕÃÊÒ^g A|GþÙ~A.­W€mŽ6e.úSáÄ„3°ù†9ÄlO…W¹7 ;õ7žŸ÷;“ç°7 [I¥Z#8 Ò=¥}£ùÌꊆ\ðìÔ ²Ro]ÂäYÑ9=˜¸ó讑À†$Ìþ&@£30xËI¬ôyÆÝ“¾‚ׇ±ïælV1ïÄ\ûoæÂuB™™¤>[½o ®˜¿  ¡W(5m;D…`ë¥Ö8ÃΤúËøðŸ÷Ø$l¦Üš"35™Ý˜"ò²^ ɦ*%À6<À£“†¡Ê¢ B._dvA²»,KzcÂÔ›cYø¼Jx´U‹>ë´ ÙU¸iR,áÑf]V`¤Ã¬šU©’3,~½›?9! ZwÑy1¡Ll×TpH……i–´t~2-ÑV£«^^ÅûÍRèq?‰ò®tÁ¤¥E¼Ÿ?sI¿Ñ'Þ­µapKª.˧Vç²-Ý™¬4ùçLwá{9ú#<CMƒ.ßCŒ?g>\Ögâ®P§s6kãµóŽj<Á_~Cy^(í¸·çß³†g!JTäf Ú81Õ¢—h¤c‚gÌ?®‰D"ÓÀïŠNÄS¬Ùéºdô2¸ˆ;u·£¼ÜE«Øcwó0bO¢ÉØM¤¨ç,>¸ƒAåê0š‹È§L‚I²79¯˜AîùkjÅ÷À’ÏMpTÇ•‹Ý9:’ô ¤÷çÕÕo:‡ÂO"û  «ùxêô$º,ú#ŽŽ“MHF‹YŽDõôO2˜)I¯¤-¡õSÏB„¹ ½¸î´”¬¢¢ïop>s}è"ÕS8£Wž>6/Áqûp1þà&§É¬ cÁõÔCÐ=}Š“±KÁ°Ó@Äg·aTï¢V.¥cýËÁÖÄœ5Ù.‡°™ç9$©RÓ¥ýÜ÷³ï—¡Lý%4|ö ^=« šõU˜¬Iál6Ñs²EeדX• È‚N(‚‰K4X³BÞ‰íÇ`B¡0±É”íú­DñîiœçxOoœÇ¶<3¥‹ÙL˜uhNZA îb8­wÃÓŸi9<Ä¥>;95Å«¸|®ùC}ù@¹á4Ü1 Á¾<øŠS¶&áß+²Ì>2‘í1 ,ê»í-¯Óß×énßSx¢æí´7gÏÝã˜mþ\ÆŸõk®ØKß¼ìfbŸfî3-0Åw}öÚîJ¦3ºÄf—ÖvÔƒ£ÒuÎ<è1m_{“j‡—ÓlxõIzÙ¶€åd)²ŸGÓh̆6œ½7vΡ=K±ÇÕ“ëìýÍ_É‚u7cQÍJêUù=Ûº`zµ» `_/y^ëœmg˜â«9´.Í ö}á2f]Eÿ…s™‰£1{§y™YøÐqooq¢nmX3ëSd³©úíéøJÉ…«;jÂ^›j0»uW`í` ¼Ìh#A®A†ßÔ\Æ€« xËJ÷+Áµ. V;0_}}ðjyŽM¦l•tÇx|šAoýÞG{:Õi^øZ˜ÿ„7û­ |ì‚/0Ü»,Ÿ[+rRC ñùŠ6ù‘'sÖàÓÏKè>əܛî/þ9Šq\a–‹3wà Ó?Áõ>‚äèÈ|ÊìDƒ§ä1¿…Ã/ ×ÅöœRds:åFq)žªƒa‘5geu“l>£ÀT›½¨Ä”T[#MÛÎ/!zþÖ¬E\Ílƒå¶úðmi477ðŽjøù6çHÕÑy4.A—6†˜³±{°aî}¾âÁ›?}#­é<ĕ͕¥ÕQ¸{£UücIŸ6õ„%fì ± ›úùni’e¾rè÷îý7¬O¾/£òF¿q›Š®7c–€„â[¢þV#õÓ-ÕØô¨ ´šÕèh{0ÁHÜÉΟf.®VHXâ‰ÊEðfÆnË¢k°ð×Ôß²¢9;XʨrObKr#ˆÑ³ß<4ó†= W|©֮|ø²<›·ÊS+Si¬ålкǚ>–— 9§ÆÀpï*ÒFƒOYséSéçêfèZßCʼº¹öÓ¯ùV8hÿĬBɵö¸ðK?g+|Uåzðzîð‰ Æ?ޱs8 \cµØ/ÓeÔ©m!}ðû2¿~ ‚GõÜœ1›Ã‰ÀwmRfÚ»À>¹gŸá|ÞÇ´>÷“:àÇ–Aüc§÷ ¢ØÍi–V/ÁŽ)´“×â“Ýñà5÷†Ïƒõù4ÍŽ’Í}_y›|û°]ïÉÛtLCÝ=ŠDFë,Ù?ÔÍ{™MàTCŸ+°có*nÇæn¤_bñÏÈIhïð„2K´½»º¢‡8oˆ²Ï<¸¶O7¸WSYÏ æšPBJ–^ƒà Úá{Ïî´`AëÆ“Äe¾¸fQ"üÑüËÓóœD°åßø‚oß-¡sÒN“U>¸<Ü•z¼Õ WD¯àæ€@`´‡Üb`¹š:ëMV`^Á³ƒãÙªÈEÔÇ~ ]¼# }õê!Ge #¥’Ìâ`(v$Q•5±8Ar3š{5Ѓ "òìS~ mj±øÐ~"º}‘Â'Ê”ç3§H•òÅ{ær¼Ä-Ö4h—™*ÈÒ¶G‚¤‡„ ÓYwÕYÓç|¯íF­ïŠSOðÕÊ0Örð/.½¸šøË2…q’ô¹['˜4e2‰ßù,úŒ"U´ …{MKñù'zmÇ4 ÙÆNlªE¹á­4£8—=O9aê7‚§òû6ÖÒ£Ý×ÃÙÅÛ’Ú¤K_ª³h9&tü1D9¤j%Ǹe¦âÔdÎN¬¾‰öT^àbwºÓÚ¬Xj6—Є-­˜5]‹­-Ö‹„Û¨ež âÕì’›«q8ųnB+ý3T\x¨î§M4H» sRsÉß µôàšFxz4¹f†³FšÄ=|ª “¨œßôïÇ;îWhJîQºdÖ?L=½„Y6>¯l6î§)µÐìS·¿À&Ç4ÎÁl%¶íò'ïÆÒòÏádØò1g¿x}{K…*I1yö¤y<=[Ê"&²ÒWm8ôæ,ýS…mgÞqCžŸyƒnÔr]3 =C³öÒ²-%vV¤#ܦ²T_6¾@œýµF¡¶]¬qÍx¶ñçtˆ¿ Cè‹S#ÕÉÔ^ä·mŒ/ ÷`§Ï 1] MŒG‡õw°íw3<.®Eí‰_ vµWdçGÄèˆa*µ¤¢Ÿ„èxÃ߸Èt@—ƒOnä‚“ô©Kô|Øu-”¦îg^Á3¯šd¸ß$½^qÏŽ|%ËÅâïÐáy™wYQI¹?(²" .Åå`êðw²_”ÃëSzÀBñ:9aJ [£Ñ0Ä‘œšŽ—¥³È¤üOPpT–¬YÖÊfU`Ôt µ}èÒ »G ’3ÙÒŠö§oy·%8‡Uã¤mQ÷eäK÷K(Ö©ÏmÁ±!½Ã™¨½å'ä¾ó¤ûfyË"^ŸÆ0¯"°t6‰ð/|‰‰{QW+‹ïny~5ÚÎ_ß…ž~/á¥Ø(.—£²+áW—‘Ïò䜲XÉr!v#Icnvrãû'²ª[Ù rFˆ¦Å9Röô"^qÃD O£y•ÎófeãöRÅÞûDëØMÎߨšè•¼B¹Îm\²Ô6H–úO¶ðžX•á ¯-?¬ÄÎì7‡éñdñQ;fÒû’Ä訓µúÂ,S`Õžˆ õ)Øs7‹}ï] +Þ ÈÖ;£5ï|nèàNåÆá§¹]¸/ëjIÅ‘UwpfÝ=™G·ËUÀñ¡TˆÛÂÛÐ,Oñ{^:@ õ¹òìj°*èù“%{Ø u ¬tœ‹ÿtFRWãžäk³Hk6Òý ÓÎÁ·¿.’¡$g\É~`²—-m¼‰ â„iZhê·•ÿ_©œJd=^þRçø×#{ k™;¶ ùÎGæ±ck3qÖdqˆ¯êáLV穆{}K`˜'H½“̈¡~)52Gé_ápçèDVRÞ¢aÃp²B õ†‹—Üp¬t/ û‰ã×écèìdô_•Ë}¿‘ŽÛÊ¢ÁAì&î’Lƒ©6yòíÒ¤ÏîˆÐЄ™Ì(î–Oµc)' `û óÉÓËÇßÀ½ÚµtszT¾¼@Z ÞóC6I3_á pNR2¢ ™; —/j3–p…ßKÉŽiîD`g3nÿ¨BEõŒ¸ïáÓPûôXr6U÷ 0ÒØ…ÛWÅòCÖô‘O?ˆÉÀº'räS&mû©E ú{:MÝ®L·D.†¸pБ‘§öSØðpö®¨;òðEA ¼±£Õ±œW 7Ž„Àñp f•ü—¤#Ï`’øŒnt‡Ê¦ô¤àmRÕqdl;ˆü‡ÃìžG!¹ùis9B­[G1F‹©-eÀq#rtÆFqúî®:ÌüRÄ…¯à޼釤†©ø~ÑMtõ0$µ¢5P62†~5³†_o+Áqø/â ]úuÑnªQ9|@ã<ÂQ¤£•\âa™«=^ ¥*ã„è÷~+J—(PmkMvZê3ŽÍ#[Mm™’‹»2F’zˆõp·…¨»¦:k»ûbÏÍÂu2§ dÜ[^i¤ÔÎÇÊ<Èyõ½Ç¥Öã¨EÂó­Ÿfá‹•2lÅ~ ÉZˆû»êð‹qùh}[±¿“›¨7ˆëC]é,q|ðå §`x®»2îàÖñtw÷^»¾ž;€±…m¸¼þ>·pÓtº§ç])yÔ{F¹’ž‡ÃçPjh,X¬P€¡¾×Ç`bšmã¾ÁÃç"8böûþÌe±ën¢·B ¯u)O/AqÏd˜ó}ˆ3\ïͺ·4âƒÎ Pw¦µ55©ÂÓ$’Û§ áw°¤ì+×t÷>ôO“¦Ùìƒæzî„n ü Ê$öŒY¼F)ï­`%†Y¹’/vSÀÆ-ÜSÇ3 ñ¬˜-ö>„³E³ ²Qˆw/6Öœ1e%5‰xdþzîçâW^篷éå…¸T‘Úˆ¶“Ä1Ô\µf4w9VéÊ&϶ä,? ëôX?>ŠH½æòîï‰i—ÁAY–dN¿†"&¾\‚Ù[à¶üÄ2Øðñ9¤Ã²¿Mž¦vG€„®;…¹°»ÓpæU#X#¸ >¿N!«Ç ãpŸ$',¦A½¦ç´³/`‘ddb‰C;hRP(öæãÄ}ÒìºZ) Î6¿ÙF4A݆Wj]ÂOªQİCiøð_7ò${ÉórGmþ ÓŸf“1»U1tÿKÎrï$ð§7Ƚm:ìùs‡×œÿq®à驯Íëáçªl¸'¡Í&Ì›JW8¸Ã«Â™¸=z7÷-^ƒf=Í!+Ž€ð¯Nr#Y•-½‡†*qxLTˆ¹/Óf ¥r/LÄ8çU|;Þ4äѪñI`ÊcÙÚÔñ„2µž2”GµaǤJWv’øû$»dx Êz‹‡¢‚ܸڧ ’àS£“1– '>’ð†üû“ð³U Ÿ-ÊD%¥RÆ ²=äqT(_Ør2=“à n>Ÿ±}å4úUÙ òϽǖ‰Í<·Ž&œØ·{ŸÀþÆO°Áé8Ž$_Á»wsQÃmðåð ÇÛñï„wÓÀäBï\J º‰Üâª×íäyÕØØ~+þMfç#Íp±} wÃr ”¨J—îþ…Ÿ,¹¿{®àw§pXñ2Žó>4Ý2žìñûÊÕÊÀú Qúëg¶?° ¡ëÇ€zržùø†ìœ[Á[/rkÿþá·¯qgµ—ïÀû«•¼ASÊ®>z ‘WòŒï†-ëÄàîà/®!ú8Î{r›äh Ã\ó}ÌþÍÐLÖP3š|ê÷^a1qîTð  ³_` jVxÎä4B‚Awú¸ý¤M)A*ÚK÷’0¹v:ÎÆx«›0y9¢‘«(VofåAÎø¥Žr–µ"á_‰ö_/ãg]ƒcéO¬{ÅõhÁ›vtó¢`Èù ìÙ«³xØÛ }—Ã]¹4TÑ×≆͸Y¢ƒÖ9ц?ˆN#~<ѧBÌIþ7)‰î„×÷7aü^#X}БyÌh$9ÝGñ@·. [| „Z‚@‹ƒ4¦D·Mê‚Ò×5îp“|¨‘äoœ(=*¥ Iih úXl à ,íö¬ÁsÕqàÃAòiÏzèõ åä—‹¢˜Êøµ[›Iœ„Ú÷(½;ëÁðF§›FËÃfãÀ#!È»ŽFѦœ“ÔI°ü' o_Ò}ŽÄ [n(½ýÈŠµÄbj>žÙLp0¹Šü=…ç– ùº¬o§ï”'$Ü{NÜ"á˜Y¿àè5È雇Ô^@Ù wôa ù·U «'\Â}Èп1œ‡ŸóvLÇd籸éO+*?ºEîë÷ƒÏV'uP‡¶OòÝ‘eÙ Á÷%èÞÐȹ&År[ƒßãÍyجÉLçrØyµÛkÁi¿«¹È5¥0èÑÅÛ]r¥E[ñëê .ãÌD´ÖÆwÓWq³>ánZȵûqJ¾ú\ù¶IL¿`¸®ê°)Ž­—¦Õ¯; ¸÷ .°—¥E5/ø»vz³G‹—âþw¾É¥ ,S·Q÷ ظ†QÄàÊjkšcNSžƒ¥<+=S„Õξ©×`õ¸ ÌÙÙšö hÒèÞL̲`(‘WVàü°ç²öqã0Uï3•ч·8(+ÙÁ4“ñ<Æïß‘»göÒ‘Ë‘–ˆsË´Á·QÑ£Ö Møå]&¼ý›KÈ«X[ `IOCé £ LhÚ{Ù5>°ü ó‡;µHŸM®{[÷[l)µ£bÌbA'K] ßÖü¿Ï\ ù– Jy«”^âµÂnñiö\¬.çO% ²`÷î.˜y½˜»(È£·º/ÛI&ìZá,Œ[œW®¥Áy‹oÜ/³ÙLêÒ?~ïÂÇð-ë6ÜÈß4‚nÁ:曲ÊŽnÇwb±LöŸ,d¹†‹Û¸V¹£P"[€5ÇÂX¹H†D´æÜg¸L\ÔÜ'òæQPg2›%¨‚½…0ÚH’dª)BXq2ëlÅ’øµÜç‡Ø·£¦š “• 5¨äð C2¿Ù9VXÀL¨‚KºõÜ… ½ÍܹÞHŒ ¦X0Sƒyå]ã&œÌäö«áKÞÛ„QZº\Ï3Ò$°˜*å ÒêC¢ÖU ÇŒe&Kq𼩘ÓI$¾‹AÉ™ã$a®*“ø~„Kø›I¹Ä ›Qfj7‘éä"3{põœ„~„í’a;À‰)ðý†k=º ³bñz× q¼Œg]Ö²Ø3Ѿlß"M[åLi–®5EÄá×BZ6”Ám.âÎnp C2*p³5ŒÿM>4“÷òXI'鞇—÷] ‰÷ÂØ´Œ¿Ä½ò2goô†4Îw…ƒë$Fi?Öx^ê ²ºæÕŠÑ®ø`ìL_¯ê"a_w3¨ƒ¹¡°×ä¼]Èœ¸°–ªN®àŒU•eê`âYÐùÞ‹’™<©ˆ $¹ëoúï ®ï†M]ê¸ó®$»2c<µzG­é€ç¨Y°YÏš i·3í.FCÖ™ƒpyu&m[Q‹rù3ØyãZÞö¸¦Å0ü "OF ½¹'w¡q3 ØÏÜb–Bð uj·¬’ó vD@űÏX¼%‚ÖÙ[Љ§{1@@«xª0w½-Ùèúdýóéû3›p͹+¸ìuõu’£Š‹Ì˜«h¬ö¬…³–%¼÷[à³aæh*ÅÏ[õ©f•,z„§â Ñî²pರû5‰°œzŸ¬Ä;™Jž"UΦOÀj¯P˜u0ßä‡P‡¸œÌöüÞ86}¨™sì3ã^v7拉Ԁÿ¨†rªbÅ[Ù. E·ˆ‘6ÅcÌÛü¤ç¹¢÷å!ι5¯vA‰ySyw’^ÁîpoøñÙƒIÌ«äÞÃ;QŽLò¸¹Oaºª)1[w~æÏ±“B}q,StƒŠ+"ÜVw\UOŠÂ<1p¾ýÜ.G›>‚ei;XÎE V‘kÍÔßCtFDd^àKÛGb§R&·Ö·¿¿=ɹÎ}¢U˜úî‰ ^Ïg=¥nè÷`¸ºÇ˜rÿòå™èeöÛBœ½ u†iÒ÷àðØ,Õ‹?RDé½T!:"ǃ¯íW¹]•Fø¬yÞ­Ò€û²ŽäÖJGvîçzZ³L’ʽ^ Šâpý¹vn“áX|G„òî? Ý\÷y:œîkÁŽZdBð‰i6}å#IdÌ1â`ÿGôxt/ða)ïæ£§ô<´¹®¡ÿpaw-L-=„ Å P¶ú8aõc˜ 0õû}ת£¤a×wÙŠJlÿcycŠÀX˜õì^¡B?ùcÚm‹iBø. ÊEÜõ (0û%žøób‹±õp0eöQX»Ï“);íá¦ûÌ£±¿/ᆨ4”HÅow4yTz1ŒDªS)I>š Aßz[ªÒ¦Å6ú‹3âs˜-v¼ž tÁaEØ$MEŸ ³ªÅ'hP¸«”±c«Áoï"š“ _šSxNѵßÒx‡M–2“£ZPfÜO.ƒ ©òlñòxºBñÄ çñýRñ¶‡9܇ýñvÌìK(^OÞÇgº€Ù>#8ìÕƒ‰D™ÃÇ‹¼»„Y¶ÕiXÓ2„!O.qg35اðwçwr'Y7¾®±‹N‡ÑY¥8Þ5šÚÎ]Kæ¨Ä,ŒÇ»YÀ}媼߅æ8é²@;XöMœ ßÊf*I¯¸Iä<ÕÙb>AYÙ>:M'ŠßêíÁlnû1…ççðDq;5O³ð|ÈXY¤Æ6eâý»Úx䃭7?rO߯Çi¦gàBä\êñ5NÇ&µõÁbÉb䬨©S*L}jÊ"¢èŽq¦¡%no竇j‚t³Þ"8"´}(ÍØÙÍMùrŒ>œnLC,èë—Št<1‡÷3ðÑÇKøîäg´U¢».豫ZŒ“9|Z?v{½/Ø~} Sµw¦ñuóÙ¶´4þ¤LæÁÌÛ² y}$ͧ’´j qUO…èÏÓQäÙqô…ÌXj¬z“VäASçU°~yÃ.4À J¼~£å§2Ü<õm½:ý/!Ã]ž^v–Lèð§•K8¹ÓUxïî~ÿIÞííÁUNèlHØÁF=èËhã‘o¡tçfaz^*Ó»~ŒÆÌùE+qíÊ¥ÔbÅ]4Ç+ø^Vš®qÜËbõÐÖH DãàÙ{ ¸+ˆ³ïì“Ê lýM¶;®ÝvúÆW(µ“ËTê‡í—)[¶X øÇtX¸•2û;s*ݘÛ;U¬™óS+XýXŽ]·ÛåÀxñ+^â]]šóê&ÿZÁx1/÷:Þá^6¹³°ƒe%˜íÔÑu˜ CÉ÷ e(ŠuÐÓ°¤V5 É)†Ö Òlw’˜ÐdhÉVø¾6šˆõ<"Õ '²:yðÔBy~äVJ}”yÃ'¢Q$ɼgÅÅû ˜ô-9jmSÌ%µ¥×?Fû“ýžÎ¿Ñ9'CÎ`Ž9º‰ ‰Óu_ÙßR#66Å™eåÀ¡UÙTZö:é” çÜÚ®bnÁ ž€µŠ”/ÄæC+aÓâ™$n¯3˜øÂ{Áƒ(—œª;×Ð\ÏN¤ï;žlKÅÑœ¶ãG‰¯V,‚°¾³ýÎÆ¯~gºvãº|#.*ÿÁÛÀ›6LÈÔ£`{ÊárŽGŸïùq{[¸§‘ÆTÖ?ÿ*£úõ1ìÏï¸ÜÊŒ½½ÿ«©ÔÝ¥¬2× 5gÁ¹ Nlõ7£¿ûЉÓW3,¦y²Møf¨‹7ã¡~ìRa¦,6c^ï’ˆõ5a¦r9Ö_!ôÆî‰¤8Jˆ¶ó‡:«õ4aœ­L•fÛèS³ü6Å#K´iÓÃðQMá gðUº ئ2{{±Ô[ Þí¯3a1ðººç4Ça”ök’8R »ínóƒkzùª3é39Înµ.3žÂDZëËÀnåG =}´™ùÍ~ødàJ/Dà–è8>…[òéÞþ‚·'·c¦ÇúX=”ÜìüÆS’®ãÛ¶ž‰×‚ISwóð=rÇÞÇ.¿½)逫;aZJ,& \äVW+°/ Ñ÷~]ˆ¤9 ¿ÁÛÏkO™üëP¼of*¾`?<›.ÏՅ㳨»ùíê€îqžÌ¢8Þü3¡´l7ÎÒb+ô yÏß)Ã.Þ+¿Óé?§q¸!¬õzz¡û¡"žåÒaÕìZâ½ø$z‡aü“cÐÞPŒ|ç§üM—?£f£> ó¹N¶jÞC"2 7HÁÌn%¶[Zœ.t­Ãþ£”­˜\·ý…)JÄaØÀl*;þ|œ¬G?tÍfêóähï¯›à—²óS Àã¾Þ¸¬E;Wfùß»àýÛxøxä ú¯Sb¾óÞóË4N¢Í]äWø±C•~Nû·~•R±ÁmTÄgQ¿2—u/F-r\æ„ÑaÏÍÌáj5ª˜xÓ)5¬`Ü^LÊZöÉÙ„e\?ª»²ÜE—8Ù¡jR´)‹imÄ •þœ>'N‹:kprŸ/¹ýü1ø?]—çúȸÀ ½&’ul#.FúÎ ‰Þid`üЄٓ¡Þ¬Ÿl[†u"wqÇ']¸(@åΈ±›÷ˆ”FÏï„ËŸ§@ì‰o`u²œ{z;þz€‚á¼HèÝ[ÃÖéâ’Ø9ôœ­ýû#PÄŽíÄ™/Œ0V1 æËN¢GZ^À‰¡Õ¨(æÀ–r÷Çå=u±ÝÖtzO|µbKwtÉ(=Ú¸^öüËÃýÌìu%¾J°À.Š?g¢\]–¡.®QâVaëßR’í™aƾÉÏ­½½Þ‚i´®¦פág|çñXJ‚8ìíFãf#np™v/AGã›x¹Æ ˜JIØÒ}šoq,¹*‰'Qüxõ¢£ý{Ì] È!rz,Uú7n¿Ó€ç³~t÷Âëfš,aß¶ì 0UÅ»°)6—mh“¡>7ÖÁc=U'óÖ–Á§7>Lúî=ЪH¦+n«cIh<Œêæ.½T…ñAåH´$i÷`(:|î¶Û¨þÓŠ±º0}0ýùxyÛÿhÓ,vÁé.ç{Úny,‹Ê(G×§$Ö­=r牱€ã ˜í”+óÅØþƒbÐ;÷5ZW–á{½G0‘¤cújY8u‹¢v¬¯ƒ+L‹fÜrí4»ÿI^÷£Yε䯥<[³¥‹ë Àç/@ËРߎU€üÛ¿Qvç\*PÆåµöÀÒ ¾'>ú/gó]·Ja«äBðzð5îåb÷E",•»r6ÔÕƒ0¿äfö˜GÖI﫸rˆ.ºÖÄíÕúzŸ åR î´„Í©z܉+'àæN[ªjsò£;0«` î{l&›'B«Û&—Õ ÷”uñþ`—¤þi˜1³f1ELR†Éq10wF-šPåût;sò<¶å˜3« ÞkhШä=SuÇlúƒ ñãyªòóvœ—¦§ÒÏ⊰úÔ2¶2þìœCí3ü’WÉIŽjéú“+YtŒ#{p`íù«‹Ýc—“Ía˨ˆJ÷Êé/v­è๧n¡«ɳœ µ¨£ÀšOGCÌ(þæÛ¡ô„…2çÞR‚ïÜ€“ûÛ˜—ÑuöãÓ}P‘¼Â„·ZÐö·N¬p‹#Ûxr'}çºße2 Û ö¾hÓªHžv=ü,Z½¾‹ó¯cÊñ$Î0P™Š>¶¢çý¦ÑåïÛ¹©ÙTUÂuŒ´ »ÄŽ•^‚éïv²) -q«s˹¢ ÏöQÃè‚æc>í×ùë yRð—ônÝs/áA^ X_¹Éï 4Üò‡8³b)x{¯‡—;æ‹ Ïí³|#MW;HÐÖ=sØ‹f=f?ZÿžjÓ‚¥§ÑW‚Õ6D²yv“pÊŽyìæú›,X( .*³áeëyF!9e<*ý 1Êon  µ{¼êiÔÕ MÐ#+Â{S8uë ^õ=6eR?†ÿD&C·ŠØIÙŠ_!æAÌðŽæêÖŒÖJÇ 8E¯‚{¾ó)ì[F{=Àùð%xí(}¯ÿÀS7'¶xQUPZ[¾Š2«û˜1_“í¸ÁSؼˆ½T–a’Iç%RS?y’Éya\ÅGmºôE9®<{ ¦.k‚'ÛÆÃ–áŒÞZ†ÛÉ5~#ö h(Þ…¡qRtÀ¸ L<þÚ5ø®ú>šl¦?^·?j7{”ÑF>Güä¶oŸeg*z‡+z—Ž]™ÁÿË]²&ìQûQŠ/ÒñžŸ4CíD¸Üº íÅÙçg2tÒ'A¶þœY¦áO"7 hÏ8bslý’›°eö^–px*ì¢1#” žT/ ê;às¢zZÿ wj.,U+Öã¥ðd’6øÔùîÄÃIÌØÇæn¢Á¦á–j#oïœ3¨$äÂ~S9V¶Â•½¨Æ®w<å:_Nµ!̵–Ð]7TÉvÿËÚP×_'‘Ké œ/Íl›÷’ežÃ'3ŒûÇ›PŸ^õåøW·g“I>¥8X×ÕÕWÀÔê#ÌqÚQø!¹ˆFE9ШÄn\X"AÆœë7Çèir ÆÖ%»ÃŒðopŽl¹ ÙïÐlŒ>ì„—Öòw>›„dã$ÜR}ƒ~˜/ʸÆ'Bâ:3˜Îè¥3òEÑFi>›i&G1„"ï¢ ý:»ù:dê¶ ðx})ï|a•¥ŽlÅÖïÉ~Ã^<_@ë¿üÄSÎïð€]{š,Ö>GQf¦ôöz_ÔyÖ ¹ssÀ¾76Îj´Û´¿ÐNn½.-Ï“g;¾‰³'ÙɘóÑò?¯3^>Ô‡:öXÚÄAðÛ$š½òlò*ÂÙ×Ï¢}ü<á1]úúÓ]¡‹¥Zî¸Áì)Z'ÄããçSÈ~4¢›’OàãnÆ-õ¶¦³•Ë0âîdº¡ÕŸ¦ÉÌ¢¿‡¦BõìÅ´è‰ }|~T7ÇÎ`¹Ws˜m…0u¼N´÷mÀ7w¡ñÚ9¨ð`#ž¨ýÚ⪧¢‚DGþØô ÌiÞRöûÑ0o|]zH¢ŸSŽpýÃÖ0«·ÜçÀfk lŸV‹5–sÏ>Ã#§Zxü5¾›‹ÑÍ:ÙS¾» COê}n4.×ì@qñô90ôĘýz9 ÞÚD!»hjÞ[ åç>@BÏ)NCä:Ž\ØéëAv~üÚ!îÄëm8·A§É›)$¿ø,„Þ ¹M?ÑAì&ôä.¤óVÏg†+þA“ˆ½õû6úÞÈVHÇò±¦¡# ^,ëV86)rXsê.ª¬]M3é`´%²586k ¨‡ÿßë, £sûË Wœ uQRïgRwß%’k9å{µåéEéL ™ }…ÕÜ¸B™THµÂÈ´TÌ–>‰;wÞÇù’;ñ™ulàµ{p[%Àîš—bhO5vNS c,¾Øj?!‰w*yg^L‡™ wá|æDU¥K…oa2qa»ìD¨ü¿Llà`¬õ[°„FGÒ°°á¦j9`hÿ¸¼$=ÄÖó»óê´Í„‰Çátò909šÅ¿Æ2ûïAGNž]8‰ËwY1¡K×aªÒ*Mï)¾Æü“aÓ—: zòÔ$Ù¯3/x"Y]{„¤ÁI'^°‡š‚<ÒEB´éjôǾ^¥ 'SçÈ Ú^‡Y›½©QËgôسwŸÃ_GáûÜd¹N3Îq=…! i¤¾·&?ÁIóæ ÁÇ0ÒN¯b{þ_ðûDTõC቞þ\LéÞ~|ž{¯šJvL…{éᰳà dêš w«ÿÁEšgȇÝRè]~VmpáËøÞÃÐݯ¸%b—ð¶ ‡ ûºqÎ+1Ç;N[W«‡Ç_âŸÕ²À öKÐê€=Ô®½ÌuÏSÀéj£ýØî=sVâÀjzÿ÷í§ç„؆ŸÏðp’ ìþ^A.®¹3ç¡Òò§]A¦‹…ðØ;%Ħ¼«÷!8 žÿ–aÙ.3ÉÉŸê7ªrß“ÿÿÀÔšòœóÞ*Ný³„Èt¯,juV„ž˜9›h7?: ?ÉjÓ¨?iðNê4,Ù¹Ò PdçîóX‹äOþ¼Ç¯É-Áðßø sðþ Ü%åÎ:ÍèáoÂ41ñ·í®É?ðíØ`@ãtͲ3Ü?±Éœf 9.Î~ w7¡×5*ñk17|ñ/¡?­ð~’=‚Ð%Üø¿D+¬\Â~PàÓ‚Û—ŸÅËÛ3ÑvßKÈÙq ¢N/…^¿qlÐt2¼íZ u@èãTVy¾ -Áú=c°kš2L»oÄÒ…zl•EÛõIÐòn¬?²“¦J V¶€Š]Hçÿ®¸FÎtA¨j&¶¼¶¥Ù¨œµÓ‡0÷ê*S·ôæ™Ã÷éWqåd˜Õ†ÖëOâú´Õhvg€Z”Bówuv'|„œ|+Èr”‚ˆðkq8åæDœåÈÅ))èk(M?[9“ZénʼnWüéßÛɱ_oÀáÂU÷QÅÖõ¨Ø Æ_á^a,òò¤X÷µK8`9ÉâAîù‹!pÜRѤ¹_½š¬æ9®Gÿ8®³µ¡ûCuÙœz,½ñ §±ñ..:`JWúœ8áŽs’¦ª’.t`àÙd»·K«©l}'wÕR¼¿A­+‰E×5©(­¹ttlªœ¿j/Ï-¤dÜñØ’7P—ß·¿˜5$eD‡îÙðÞ+ÕãŸÝ2ìêÁ)#ËúnÞŽ9*Tq…(›såßqÿ]ïÕ¦TfÅMX÷ëdÇQØ8î,Ñ’iæÊ]cP«á+Yük%M7SçŽ&€Ë‰­Ü‰‚s YãO'¡Õ囼™OâÄèÂ|'7¹Äm]üÛjÌÖ'–ÇOg³Å{„ø¹R›°dª ëx<—¬h<Ë­O4'zy¢dìU¸¸ž<‘íà/ÐmåÞ_­äþj«qîN·ñÒË|¼¶Ü‘,ý쌻^¼ç¿IªÅ—¾þðêš(õσ˱èƒÜtTj§³Ú‰»Ðaß1né‰Bˆ's©ÛFwžŠk4½¼W«5Êo×JÑlÃ(Xý5ŠønÌÄÛ¢lâÏv|¹9þ1®KØ`ÞÖv’07ª¥RïyÇ€‹¤ü÷¹+Uiª8í’[È:ðÎÑai·…éÂie@<‹a»ÇÔS>oNŒ‡‰Â ¯Þ”êÚo—_-Ü¡=9úº /a÷þDdÓS¸óÃûYàÓ‹ðäË.áIxXjÀž²VðжýσmÃßÃFÞsG¤~r}Î.£µö%wØoG¿bÐæð˜mMglë$ý*¯ñ´ž»õôÜs6ƒ¢ÑÚr£Ô *Ëâ»LK²aQÿžfîƒé-¨ŸfBoÖs«*âñeSvôÞå~ú¿Ã0q'Òá¹âyA<Å÷ ™­ÍÝ[|žwúÓrr_ÐÅ¢sÖìVÓ„\’e’eqøÎW™=4=CTýç€àl]fÿ+Ï‘î}‹_4¶?LMÅYêw9™ Ë¸ú°,;%ºüMƒrËí87f PV…÷›Ç1‘Þéä“Ån8+>LÚÇ¦à•º%¬óükpíp@ß‚\Ð\Í›Ôõ‹ˆº+Ÿ%oÉ÷õuDbÜÚ<‡’»c.ã±u{!²˜Ë†BáÜ^XÔzjá™qìéïܹf*÷Óê"©»Öß¼åiÈ/e¨ýc6úGÉ–‹9àüâ§~m ´º†DŸIÃ]”ÚÇ·Ó­äšXÒ7攨ˤn*±®+Ün]èœ8tz\!y·s˸=¯.ðÜř۶«üqïàªZ ÷¶‹Çn]`>WÂßåTï‘8|ÔãȽÅ:8MØ ª6sVßò_ ¸àÈÜ|®:(T™^†4~+òƒ*°ìnÅ}+!ú›=hàHævx ‰8v†ÑM96lÒà8úÄÃô7p(þñV¦q§;σ… ¥%âè¡YÍÕ†…ÚºÌSï &ëgÁ÷{ÉÐý$ R3ÌÁsð;¤•ãÆî%¸Ä/—Jä}_ጀ»D3('V‡ÂU% ÞºE˜”bLy%>hxФ}"lÈØ’†mí‚¢SRŠQïðöwú¸Ô"Íò·1ˆÓ²çŒ˜:»[§ÞêfÜÊÀ¤ò„Yù·Ab™iONý€Œ#‹áBp&TbWc>ñt^Û¾“«„üø ß˜–\k%_Õ1mÉ6ºý´&Û'ÄŽ^S¤!oŽÁ³v%Vqô>eÒÊ·uÜŒú©£C3;ÉžBl_~(?1q &‰}„¿qCöáó7ÒXÿ#€%m†ÕQdõ¥P\r/´ÿÂÁ·àðüã<™½éx¾PˆMý{UΫ@Üâ1t{Û6ìôŸLr»âqyû_®¹v&ý²÷í+ Ú‰¤]5LzÎý>¡¡Ãͨ•¤O.n^ÂùXƒµSÆÐÖÃÈsû‹8ùPñ4FÍ7: o0›»´Îž]·÷çÚ¶î†E[oAùgqæ=[ŒžÌ´(-]¢ò¦’£=8UÞ6TžžÇÅ´Yc¿é*ŒëµGÛ¯0s‡5v=›´%™Ä¦•èÙ+ •KËxǯ&aî"[ú>ú8QÀTzóÄ8V<‘=Ù“é‡Ë`=}ÍY·vá§Á>èæÃ#váçÏß v΃S¾Ks›aŒ° ™Ý£'&<â Ò˜ Ü{ÁkúU…±õ°F#‘DŽj¥Èó‚˜'Žõ0s~6gµí ~{'ÌfM¨àäÑÖøïØ°ÅFVp®UÀKg7×& ŸÃ"SoÞ*Ï+$n.LÄná[°_ü=Ö?‘§´ÎŸYJ±â7O€ž9H_öýÁÆùwˆÜÑ\ÜTœAHCœp¤Šî ­ÛØq:@ªo(³N50»:‚UëØÑ÷à¾ÆØ%#D¯¶|Ó œ\ù=°¹iJ³Fãe›PÍq"4+»²ß¼ºÅÖœ³ÁÌg¥(l•%e=¸¾%D|çÐàÐ:°Íþzóu°÷“MܺƒÝ]²Bn!ó\ưã÷ž’¯SíàiU½©óêM³ I4”Ê{ßæ¼ŽÎ¤Ç–;2Áút¸æ8ÍdqK#©ø.I¶ldlÙ+ e÷ÁçpIð,õa'Ê?‘Ãgp™é3;m°Ü¬@¾já2wBʶ(¿N“³)(Iæé°i7\©aŒÓ:Æyk)ú¸¨¯a8í,ßÇM¬}†9íy½¸îC††žã»j°¶úy#'Öqñk¡üåoN+Ñ õmˆÆ‚yôÎI)*öµ,<7ÆéYBÃó/˜#ÃΘ¸ƒuþC¾×ËÛµì x›Le2Åé…Wÿoñ¦Ì ÀƒßO²]Ý™à0"–ÞÁÜ®ÜNêpÊx¢Šíð „à*W}zn­=މ_gºƒÉù4ô]w­Ú¿aÝc6}úœÿ{µå\>s­ÊðsI=·oÛöÑ“Žy5ž:ĹÃ_r—Ü6áü:%Ø‹ä~üÏ»ælGo · ‰4íëM#Ÿ႟0$?‰†¦}›á8NðѦÜ; Oß­&ëàmŒ ÞþgoÓ¿dþî+ ¦1 ºJq‹æˆüá‘‚³ðæü_P=‡m=œìŠx+¯ûíÖÐAG‰•v#h=Ä1‹ÜÑå„;é=ƒÙʲFi1üÏ7½Ä|2.X‚‰Bw0{“ÅùðÀKb )J—yP–{[”Òà\ïFºÜ@]„Ï‚“uª­”£?Ïk±‘Ç{énÈ*4¡yÇkÉeý‡xâúqÌÛãM-ºËàˆÇUx%–OƒÀfwVéóSðãaB\ΧႻw`Ó¥VÃ;”ŸN×…ÄóÉÄ,~Jì`…ßiDÿDf3ùÎn€Ù+2Ápål*q@–W1Zß `eëu~ÑO×¼ƒîV –ЂüÂ,™Ãƒ’]`vk-ø”ý⊦í¡°¥TóEùQù²W™“IÛè+Î_—&³¯Úx²È“zOÏb~nh\¿ši§ŽÀ’¯ù$‰½ã÷ˆ«Qwß0|$Èc—ÿV‚‹Ÿ3”r±9ë´™ðÁÍX¬J]Ü©LX¾¿z¹7£gbu8,Éj®a'¿œB¡Äl&ã,b_¦’NK>/f¥?ñ¿•ɃêI12ÝjV›²»g|‡‡ŽM$Îñ ¶Åÿ‡;fÒ‹æ2_…t´k*D‹‹îtü@uI¢ã¦ßÀõÚÊvcä* ÜÈ`3.÷Àdiö¹…°+ŠÀ9G—.[ÔNO]Í@…÷€9¬Î'¯¡—³Ö‚§ ª Ær¹y«QºD&ȱ̯y`Vì õ“¯b²íYe.F]Š>€Y€cT'¦¦Û%cÌGOfi<UÞàÒµtá iðšAbúüàcúOôø*ÈÞ¸D@|í ®HPžÉGÝ›ßÔ g¿æàçà)\šäX2ù‘9%ê͸º³rÿ‰³û¼1ôýOGD¿Tàâõô'Ø5ç5Ñ:ªË^Ü …1‹¦?v2Ìa±sïíïáÞ‘)”cM-:8`5 M—îÀ>çFpÝ{‹/v€ø\ŸÛ·£ÖùwðþýQ¨-EíŒsÄkHoÇNƘKG¸ÓbØO-aº+?ÔéÃÙ¦nò\áªÝææk)p2#"Ì6¹•;Ý­Îæh>Àõ>¹ ›²áÏvMª5Ë”½’Q ^á ØÁ¶êÊ³Ž»*xn­ãV­"+ ‡Ñž/Ãîª-¢7vzãà«xý>®Rb3ü>/ ïsª\‰Üë#øEnpÓeéÄÚ8IJæôôÿӃœÝ2GªåmL‹_=ħ œØQEk,õåȦE¾œßÉG µÊ‚,ÙÂas¨m“‰*ÓÝÓ•‹#°´Q‚°=rõq ¹†÷Û:Q!ÊmŠãçí-p舕Á´Šk¸>ÞŒåü(Ì¿è¬#‡}•›ÙoçUàl¤Ë>w‚Og²1xÇ ¬ñ†žÈdœ_WŽêÕ]ÜUDJ{ŒY›T,náóCmApŸ Xê{Q‘{z¬RÍž=(R¦w“% ,$ÏK呟‡±kËX¨zY/_ŸWš³ä/_õÉ´Ïí –dœ‘Ê~ÞñÁ(ñ.‹Å]¿I^¯u%~©$·ú1>Ý“†Û÷ª³M“pcÃ:ùÓçªï‰¬Ð\Þ¿ñ{±ñ0CyªÉ{…m}NlÛßKh¤sË_ˆááÊX²”f=3µ™ã'ÚzJŽ+ däŸ4Ýmw›”¥ëÂÃ7Ž(¤Õc|¶‘®ÙmäØíOäO‹8ºËC²âVîÅlyÈäÃ0¾=,.ËÌëjÐѳœš/£W˜ýnÚmÜ<X…°(ÆÅ%èÔp1vìg’†aK»Ðò¸&›y½¹»ÊId`w)ªJÊÓ©¡—Á{3ª—r‹<ð?ö ± ø¤E >þ ®/¿áÅË#$Á󬻽€inŸÊ,LSØÁ‡Ö,Ò}Þ8{Í©ÈzSL¡+ß„C—úarKä·GL†ý)nçDŸ Ò’O!ÄÒ´_»‹²µ[¾aôzþ•M½ðvcœù(M}k‡næÃŒm¾x§Žƒyß-hDCÜ|ÉÓPc/CÕê ;¸êUá8¾R‘Õï¤MGžâÒí:äöàfÂÅp»Ð"66eÞ:P [EîÃê¹õ˜!ADp½KþAwà ÔDXúD{ü1s÷+°küßf.¢:9ØÙ¸8‰¸sÿžl{Ê Û¯cê]/!ð^45˜ÎîØeѽiR¬D²çÔäbëØ®2¢Š‹D }ýfÖQ§kNO`Ÿf‘ïË}éøû[i›X²k#³ Á.½IÃSÊPõW2Ûøå§²ç~…m²Ž´ÅMŠ|¾¡t²V;§<ƒòéÔYÖg͹oùèÜÉ¡Àc öBåÞ=(‹6+Yj÷JÐS;¯ 9‡ .üþ]¡¸y‹.ÎÄszrì€ð?n©t±î `þ™|‹±Q˜¾²¤Ž½Åþ—]ÜoŒ!–3¨H® ›Þ%%u§·g`·_+(æÞââÆõ¥Zôéñq »ÄŸvcÝ[efÑSC’ÆÒt–:®:(“÷„þûÝ”è2Tt?M¶mÆÞ®±¸ pîØ8x6MœùÐgñòãkK|ÜcNî¡1NãÁïH1B›$emÕdÆRR ø»â'€[^ÈÉ}ÃÿüÆá¢¶\GÞ-Ðs’„¢•ÄwîN÷±8{2v>(þ»ýgåÙï›ÛŸÇ×®šXç³熭‡áÔ'$w–,‹í0`úÙÏ9 ¾<ÓT6‚¼ñcAõ|×ÿoš»žÆôÝýܦeªì¿6ÿÅ6 5b·4v´åqÌêû^\¹k&ËkÉ„jîmÓg¼ñxvzɱ ÞÝ`C„ë¤ihÇv´8»²y{x7ŠzTÜÀ.…¤2túxm <„ØÕðüfÖ×쾇ÞqõÛ^NõÕ'¼Öº›¸xëS‡Dz!žÕܧâ!ðj5tžIE>/Õzm®Ê¥wyÝE6Žp«ª[5¾:¶,—o(ûVyA§áùRcÒ/»5MT)¼8OnÏÅ'‰ÇóLÊ%þYé]ÒÖzT®ËáÉeõ\ÝÎ Œ²«‹áJÀWxfÚ;ÄÊù‹cŽ 1ñg²iILÄæyw6ïÆ~!Ñ«¬[Õ°hlN=Ë×÷HÆKR¾xeà$ù{NžFF~Òa&ê´××Ýä9öNÂÓþëIfçQ|þ,±¥ß"G ×~)œo6‰mgJÚ_a¢r5gˆšé`ßÅV_Ár:MY_ÏK®dˆôV5]Ùý ÇÉ+Ëx\ãÅ[¤gÏàO,i7ËýpÔøSïëaS«5u5XDW{/„Ê8o²­D“íR¡6Æ^ô?¯r’éJ¶·[™»ùõ6ÎÑ×Ü”@â™|?#ŽÖ6þxý*‹õßð‹R›÷~ õ{•OyæÎdbêiÜXzˆþl£V“èªGç€3ÙMwøÂm"C3þÆ‚öˆ¤¹¿Àľt©[.mhá”—ãÿqÔðŸâb>äèß„áÝ’lvzðÞB îm¤úÓÚñÁך0‘}¾–ƒvúŠÔôþ¤”/g/<ݰâíglº]ÂÿŒï(o»¸Ûw )Ût·'´°Îï·QaÍxq•³#‚·½‡ëå à€ÃF@— ´¶¥Ÿ×úcy½)}ÄŒoûÉNë0äK 0ñMã™ò>ì–±Õ05üUéEç|Ö`•b¦ðÄ0i|qfªaé`Öºž~“´¤Ó÷œå.4ì¡ç*–óÙñThºSÀ®oe/uÃà}v­à83ZÇ:†£ lçˆð8O£…3ùFÖ6Ú]i4@á*¶•ó×ζ#—ª2ðà¦fX¢ó®€@£]2o:þçkŠšÇ.†o¦£P]­˜iï} ¥QËYÔˆ¬ZŠ>ô Ûß{¾ä27ýØñ§ÆîbÁ_â·Ü¼u[£0J¾ãÈ7)ˆ5<Î5ÿ< ÓónB Ý5´r‘¥ ×EøŠ7¤ ²*6*ÐׇÄá߬٬ʿ•ÄŽscº!:peæ"<§“†¦g eÒ~¸ñ?7eÂå»îX¿ø$~y Á+O£mÍ ¡9N¯5Â3ªAeÉG8!š G¢à”J/fÌRÆÈìø|Mƒþœ¦É5Ïe³¬l¨Ëy¢èT/‡íK¡Ý‹âgÉR¥{…«ÁôI,y“E3v£&gn‡ÍûÙ×ÖÇÄ;m}tI ŠEÂQ›tÀѱĶp<©}y¥ßdó Óí0z¦r*ª8-ù&hÏ\Ž÷‚?ÂOÝ8ò. Þ®Mýî•u×¶@ëî ÕV7Wàê½Ñ þf ½•nl})çQcHŠ(ÁÆuú¬M;Ÿ0í3òÆ/'Œƒß|ç±€—K!±q ÌOÏaÕÏ·ì m>9ƒ]œFõî$ÐÁ„¼œ0Ä}®0„yjSI@\® d6£k£ë;/sþ,?oÏ.šû³oïc1lŸ7_ºM„T9råo<êë…ertbC6ê\øP÷fÎR~»úSð½¢ÓþX°ãÏý‰µß_”1…?zĦj /Å(Çm]ÛajÆ%rì¾=sâ(!rJt¥Ò0÷àß5Nà $šîúX÷ýíqðüÀƒðÚ›¨›86Ä4r+îðdRdௗr{ˆ‡'™àt«éü‰ò—à¶d‹0í’pÐ|øœÌ_.ÎÊ.…¢mõLˆZÏÓñeÿbL\|3.='·^ŒÁù«Ÿ¡í’](Wƒ%× ™¦QFN£Ÿÿµá÷âL°J9Š9ÛDÙ2qÖ°K™ÆÿÓ…ðùo@Óë·nÿy|!p§xˆáš[î(9ëªöGƒ—nÞlù ?Uƒq޽6~MüïÞŲš,òUOž½'koÃt—a^fŒ5¾Vó0"i.0ßêFC•z8½'7µ4C&ׂî¯ÇÁÍ p®ôêX¢Âã,ÀËrKž²”UõwÙ…ÒrtžŸmŒo«¿_Û¶ã¬7¥ªU?ùÛAžÍ1Ï1nÜV;ÓŒ[xð•Ϥכ>?õ(Çæÿç¯Fgžë}%M/zô§ê­’Ê=©¾ŒÂi]¤Òq'¯&öçàŸÝ®QÞ¬EÏ¡(P-Átÿ*2—­ÓèmÍœõ™þÏŸ¸¾ç •ŸL{Nœã…q]†si T¸9YäZ¼é0™FÈ”p¼‹'PQô ~ZmÍdëná¶ÆKØ Ï’÷D²]'f Àïî󩧸ýM&u tåäÆÌÇR—8Ý9‹Ciø[8µo*ÝwÊò^ŠñŽ^˜ j’+¸,™ ¬áßaj,=êciûGÜ5dA;’û\ØÄ+<œA«Ç²™›ÞÔ•W¡ KÇÃóÑ}<úž]ðªN´-x ×fü{ŠÝ’®§Ç?å3?‡¦÷A¿ƒ·Q)(ôaáúµû+ùý½‰¥±åøÔÁƒ|\Í%uŽ'!X IV87ªµŸ½FKaºWà }r\žÖÀ 6{A.¹¼5äd˜\‚2³¾BGÌÁ1`v ¼™îiø=Å tÇŸ$³¯ëáðä[¨àÞÂdß#MU¢ÇêßÁ»4¼ìí…±îËÑþ×Av­Û-Y´•žO;JgÙÒS[Þª4Ú´L5 ÉÒÒöV²´×–ÉìjƱ BØùòléµjØÄM$OxÒqcÙ™qΕ˜3~ý/öp‰=”U_Bÿ\aêsµšÆç¥ÙÎìv`û×Ð)?:ëMÛlO÷KŸBÉ'éeÁ!N墧qÆgŽ¥¹¿³Qw—<1ï‰GÑh2áF*hDCæ63ön‡Cû¶“éÏ ¼¦˜ÌžŽ&x(ª¨ÎÙ˜y¡¾Ç.v¤X–T¬ÌÆ‚…:‘;=w"ŒWVÍ-Ÿ)  sHé‘&²Ýp<離Î9åƒYaü“ùÀ$‹ðäýA²t†cÇå±¾§¬¼ 0@oܶþMó8ÐÕÊâ½°-ºk u›UBþ»w;éwìõVtÕ<?v™â†KFô‘ïfó9œ|÷Ä©/·a¦h7Ž×mG'ÏÑ~›Ò*ysŒÞý ðÏväï†o/ͨ£ª‰8ÿ‰çXž€±jéøuî~m¾Þ¤ žôòýÏïbü6eZ0ÌkJHÀ×­±;v(Ýúñ·cöûYø5k3kq¤ƒ¤¨[ ƒúªkÀw“ÆÌ³õ„§o âÞ PϤYÿ”hœªRŒ–ŠtÖv)ÞáGçp_L\Zƒ~IÝdm¨ý¦ I=vè±FùE,v».国ä]ãGß +q ±X{§žº"3›þZR9\¾¬˜¼=“E·«ßB™8cúýÆ>ü÷J¦V,£ '’ùü1Áàûc!å®YÃHÐ0‹…Yi·p^‹æoa×ÊÃa‚Æ,.ø…·£!’{À±ki½8¿g6Í÷øƒãÓ·s™²{°ô}<¤Í©ånæ´¿ †Ëî~Y‘“_ã»z¹î/¶ Sg®f³ší=PøxËïw…ɪŠä]¯Ý*¸‰šï¡þ&‚ê+²íêE8\ÛÆ]©üBꤵé ŠñòRЛ“‹i¡ëØÓ¡,xÜ9̳‘‚þã„é6uqÿíY|œ$„™ãît7aú{ûÁEá9ØgÄ΂O°ÏÂÏWªÂ)¹—0. nœžË@3Þ%ªR Ùnâwç!ˆ¿”¢BC¦òÀ’6ƒº¡ ×09çF°èõ2Ø“„¢ë¢ðЂ>ð~~„«j-AË…Ù*Ð&óªŽ3ãèËfüöO’õëÔâí©KXïG‡R*“iKÎŽÁë–+'…áYáûÀ‚GNKq«“XœMcâ‚kqU¢3|'_7G£táSü=vr6“Øt©Ýìsä#îÙü`nZÊ'ò"ì.±+œA%²ÝÐU-„H•cÛñþçsžæ%ɤfÔ°Œs§aî Vûô]¹ƒ¿—WÙÍ‘[D{uéÅÉ?ðx¡s­ØFý5BáæYvxÞèžZMÇâãopfèAˆý¼fkÃ]¡3î'? S’3ñì’7·Â {ºÞ—U_1ÒXžn>9ΙeC—_“¥‚CÐíä˼¸³ °ÙWöòv1Ýû¾ÜK|\rÍ Ûš4yá.q ?:Ö!nq% {x «ø:³ÎÖ¦ö9ÄâˆÊB.fÅ3^ÕúG˜5m-?…v ½åóÜã8!§M`{û7étÖ¡þQ(üë21úÝg¶œÄwi7þ´s2Ýø+ˆ¿cü}ú>ž;'»+ºLÙàŠNT³¶ÃO$ù;5²°*Ú–ª…ÿäG)}áñTªLò2[aÆŽÓ8Ç&–3lÎÁtM´ÿd‹R¼ˆÖ>ø:.ÓÕ%è§ o1 Œà¯Àzúo×Þᢩ¸mÏ}X#Ø©"„nA|¸À·ê¸ŠLz(»_Å}Ç£QrîWP£Çêî¥ —ÓXï—‰žlÉɹŒÉšîœtÔ(³šC?àX%úÙ©Kí;ØÙäxëûg¾±Ž"4׫Óiï•S:Q×¥ü€»rt¾®,ƒk1ǹ™UrÌaÂdvÇ£­Fõç`¥³ Éë,]¶ÚU‚…öÎeCš>¬q· m¾Ô§BÎÐIq¾˜R*JWV„àˆ]'VËÔa—ÄK6õ!|ÙÛ¡ ÒÑW½ ä=àóðxÈÊÈšHüᣎk\âÍ¿uïw w¶ÂtÑ~<:¯¬wÇ´jY<÷z>kŒ\Î6ÕÆº$m CA½æH†8ØÝ'IO³zô)ÐV¨{­ž#ã¡÷t¤î5†‹Ëïs«ÏÚ%Vʳ‘½<¯Ú4ò©MŒmÒ^œKûðɸ hñþÚÄM!‚áf\þ¢ÐÿΨæ:w¨Ò—/TPzæ8æü± «vÄrC¦ý¤?{2HÂ3»WXâP ~Lâ“(.È÷ÆŽÀZônxMd±4È—ðí7ËYf‘#Ø™°Øëbà³z€÷«Ì˜K}uäèW8öséöM©…døÝjŽ>‡š«¿áO‹Ïÿ¥ iÉQô„oªÝZý›x+¾•Ç®¥)ãvý‚sÅiÂûxQSdv˜2{ÏTø±¼”hyÁ_¥é›gàkyŽý)M³»W»ºúÊsåÆÏòÀO!‹¢¾Åug»ŒÉÖÞ›˜®už;þ¢•k‰MGÁº%43i¾JTñ~—î-l…®ñÇA"ç·1-ù3'Ra+ÎñEœ ¥Kîâ¾›—ß/:•ïyÏy³±KÄæ/´Ç#Iâl­Ž k8«ÈB¸H¨X¤ÀjäÃpåó6Tþ!͉µã‡ì>[ÃŽìµug%бd­ ís¦«rúàhÐèÒ>5ÂÖCÒ4âï\ÊV&\‚Wü ä«i0ã×lœ7ãFþȱþôúºñtŸ°oÖuc0º'B®úžçižXL.¼€ÈÏkñLÞU(hZÉ:åshð-†BÐÿ®¥b²ISž'§H_^¥°ÿÈ.ê¥{–Xõaü­™œâÓ^4p-Ä¶Ê 0-_¢šêqW^,[9–z·L ë6HsÕSná²]zT&å*H¢ Ú¬Rf3;õèŠA ý0èž ·q.ßn¥ø©L,üSÝ8 %fìÕÊóÁaFÉõ°`Þá…üca‰¸ézš¾:ÁÆ=ä4Ÿá~t\ãît¨s¯N]²-ÐÅ #1xêЂ!·Ú€žÏˤZ r°““ú#‡‘¿8Åí¼ÛÞ3ØœþIløG®Hú7ÜéÈ€~ ¼ïg€k¦†2‘éA`h×Ãå†:ïÐÕÔZÓ•¾LÄøa”[v©c¨Rc<·~'eIˆ§ÁvÌ›^ÉIþJƒ0#Y¹4‹Lñòf9vGÿ;ךHrg¹Ò¼·K‚óõmÜ÷í+ín|”Ã&10Oß§ ÛO’smLöÇëp»¾_àT/Û² +þ°Äy*x%ëéà òÀÔ–J{^ÀÄZS0È ƒ¡!Þ˵ψDÔ'˜†ÆT'P–í*„ÚXOH˜Ž…‡Oãü½Äç³Ç(|ÂAÝA¹²–mÌ…' þáå%Z¸òãúMwpéÿLIð½)8{2Ï`é~Öyó¥f£àÌ]°ê©}˜í„e3Z1^ßµƒI®6,ì“§(¯G]›ÊvðÓÏ·cêûOèggycnÙõ_N!í¶§ñØ­‡$kö[œ:î.nò•Á‰7a‚üzüìz Ä ±ìA/>Õ’‹Ÿ>õS;€LªGÛ¡}äûÊÅb2r* ¿H…ïÓ†ÑYذ>RÄ¥nøH0”Þú[‰?±1€¯“z‘;ô'“øü\ _®®á †&Â:×Ѳ2¿NŸgÂù_ÐpõtX3VwL^Æ.IxŸÖ`8M‚B.<}D–çf°±’‚ªp¥·àÙm½‰'‚ ±umfìG®ú‚t w°’d×û»HÓ1 */•Sx«É͵è?c nq¦Küf‚þ:W®hÛ{àE;³•öÔ¥S wD5ó^>“¡éKØ£áyÔêŠ/Æ"Tõ“aÌì—Ü⢫ ó"×$õëZtÓ±{|ß ¾Ýî%dÚù¢µ…X9@ãiR&ç¿O¦¶A½êö忾Gí­çaþ ˆ+û {>ƒŠ€~ôJÝ #s’@óë¬ÙÓÄUÙq½Å¬ï Mû"Æf¯5úœXîX¸<ýÓPÁ” ²!dÜÉÿqtåáT}]Øãô 3`C^>x(I6Œê÷ÌÄ¥à Õ†ÎÕë¸È 5¨18ÌýÚþŽÇ»˜Í=þK— SóE ô[¥ÙÝÏL~o tëï¢ç§…á»5ßÐAj&nõ-BáìÜnAøs'݉KúÑmL.<ü /ÖlÀ¦‚$S! óâyK ÷p¿݇×ÎpißoîØí^êõÄ·qÃhУÒÓÃQ|¯5<6eü_;™®h=ýïþö 1TÇ”xçF0÷î¯Ü‡âJLŠ“guÝøÚ[1tþJ–ü&åÓŽÁßáõN–r°k®@ÏÙÓLws%ÉàdàÍó)L`ô5ïG=x!ƒš9!™“Ø·{”Gˇ´Cý±•äÅÚn˜»‚ϵí¼9Þ@”íjˆmá Ç{ŽÒ™6Ô?À]Ÿ4‡›kKÊÏ}AnSñ¨î; …¼Q>5|Ÿö¼p Žúø©o/<˜Û†²déÓ—àºÍ´÷Í"—ª9öÞè(j®¥ÞžÓúÝâD!>•z=Ä'mŠäçãt¹²…I{ ±¢×JðcY?Â]¹åAŽ)ÇévRâ6Ówshǯ¸}—#¬ 2‚³kNÒ'ë깨•öxÁµ‘M± }7Ù‘´6_¶^I Ý ÌðÖ¼6¸°÷Úý澺?ç Ž_âz§Â„RwVѰ˜úëïç-”³bK”e ½ÓÒ&CÙî3P|Þ’7¬¬À²HÇ­.ªd‰r_ÊC•åÛ´Ñ5Zð»†8i¿š|ÂŸÖ °ÇM`ä¹”D©¤§É)¬ã“ ¨~üìø®· ·=’`:1QèÓuxâ‚.¢_àÏ&ùWyk­—’Å%Ù úÃa¾•.^¹“rƒý pºäþüÆíihç?äGù«ßÖÉ`îw½ë¶p³Ëð„Ó{”Šy†ÁŸ?ã[ë?¸»ç0ô¶ß†MßôÉ’½Õhüj9¬˜°%­â&ñcH®wá^³íÐôŠû½±CÚ›|\á¿ÞÍÏíéÕMÛ±¶q³^ð‚Z¨§qµp¿Ç½—vGý[I¬‡¶Ù0»<‡[õ¾ùã ë¤õŸUÌ}¡S!üNó9órw_Ë\rÃQ´üVŽQŠxF‹qßí¨¥zf*ËÁ¬>Ê»ƒQ¡"È_°ö=™Î)?B•ÉÉHÂC¹ô/žä¡·-ÐÆ5ìçó³ h<úyiàœ„ŽËÀ¸où¾õŒW|ÍŸ93 ?m?§óØ“¥ðsKBþ xÝ5‡Å  ƒ<5âjCšE&K²Î´Ó¸éi2¬tçíÁõº­xÞ±bH1Þb œ[6“üþ­ÃÜÖýƒÓ+@èvwȑˡç$SAp×tÌmÐ]·c4ú Æ-ÝŽ˜a¨÷˜æ'G¢z pªG®täòny)1¯ÍÂôŒ£-ùÂíÂ1Ub¤ò›–O,â\êÁ ÁN]MŠ•yE µ4/óÇA9ÎbÊOpù°Õu¢øI"tÿX„À%:Ö"þ}#kðÒ¬9¼ó‘IðSO‡}¿œBú¢¼³}ŠŽ cù5ÅDì:xU{²aW`ËÇ1äWj2¬ïr…"›ø9ò {ûÎÀ\ƒü·Š3ˆó>=:þ|+•cãžÞ¦¿gê²I¦F¤}rš‰ù±õÿx«ÅØâ5ù¨ÇK&ß6^€»Ó È4á‹0ød,{<‰èNynó1ƒ¸‰`,:µŽ}ÙEØOµ8ø)™LœÌncç‡ð±E–iîׂ4í–rlÓ™iHŽknß”s·öဩ!g0(Ÿ¢ïnéáÓZ8e¦Â6§ùœ%;ýÇ£pªBÓ0¹Æµ×ßr3+MEÙ»ÛóÉ”$fº»Nž@ã¿9¨ºè 4ÏžÃÂ…Ï¢=G¹£çDÈÎUJÌ1;•ÞÒž#š¥VsI µ8ëSçÈi 1bQ÷ÝïV`NÈjò5ð8=g­×;£Ù´ óûKW~˜Î<,ãÿßkÚ9±—ã/½‚UÅ1æ[7'¾îŸ ËmR°åO+,Jý‡FoƒÙÚñ—àõü|j?6•ÕÏ©e)b“Ád·ŒÿDfúàð£Z{«}u£ îd Ž?0… º’a»?¸þî!¶iE?Wý@•Øt…3» vk̆îK¼l©~x§bGŠL¿Ã§O¬åÃmhÿèÍ —€}˜9Üv2ãmÙ¬ÍÉÌì“[¯\%¥—à^þ[ўƔ nÃþ‡_˜^ày¼Žo®Áèlif~2-·î`¬³X…[9,ð—x-gUÔØ¡Qþ¸-’»üYÁðÜ£†6ÚÑ´^"É µØ¼({"]Ë8ºÈëþ†ü÷Rl]»/.’ª†Õ“Xi£K‘C⯘‚ñ§m0ér:ýXŒ±/áÀ¸÷Ý&pIÀò8œ `7&mä¾,Cóãùlý XR=ÿÆTã„g-¸àX/l©&s §“…\.—IW·ŸÅçÂL5k=4k1XËEÓÅæÞðtð-®~6ƒ;Ïb¨í©wó•K逿Á? ,9™[c-H¼•¹'uÁ>YЉ¯OÅm-¶aPJ«Ïáët¢0‰Tü“%ÛâÎ]c‰ÆÁIü“?·‘þ[Èéš6”;[ä0qè }ø!W‰}4e—Ÿª¢®óKè…wðK4:œ{ ¥×Ú›äœ#Qç³ãJóÑØQ‚›"Ûö‡¾á½’§ðÈh IÏ«æ´7ï#•Dz¯Ó[9c ¶Ò«†£8µÖXý¬•Þ™•äÅ+x¯“W¢J¶õh£çít¨såHMå¿æ˜3 îG*nòÚLö$¢žO7Þ´ÐcåÄ@ýå·Q, òv^ :ÙÍ>l€n{qÞwœÕ) [q~úvܼ,&쀯ީŽýñÂÌUft¬-©yLݲ5ߎeyÕñŽŒÓd–Òyo¿áò™Æìñý¥Íûy‡°`òæBتïxáü9Ú£¾uOz0Ÿâ8c(ª_¥)s•¸ÿ|Öõ—á{ÌqØtp) »`Ë :]eþÌ;K½K°âyÝh"€¡s~sIgaþžPàw8ºtÔºr›Ç‹‚=§€M};î^!Qäâʳ°åuߜ›5Ç’ß]Äž_êÀ a$l©II¡œÏ–64H:‡g’ ·9 c¢ÊàŸ¯Ûh}|„fvBDÎLŸ”[ò!uOÎ[¹·àß¹vEo@ìÊx¡¸ÎŒ+æ¯5ꦻB5HhǤ'EˆÓÞxÍ–VÃÁqñ]æzU¦_d» “}UBDÿÑXvÀ?ó.û“#ò¹3×Rç{ÇfMÃ%Öªl¦Ù-ì¼ÑÄ}Y6ÕïñqÛñ|øsø <ÙVŒÁAqÕÀ9:{Ì=Pcu ØÂЇÔHßœ-1hƒ¼¿ËIä5-2Ó½û÷n Êý’Á¡ãÖLÁ¿çŠMEÅÌ!\°w+9ýú¨Eífæöl•“3gf;B¶B$øüzt©©À™¹_1#;‹zo”ŰðÔ±L?>Œƒî˜)ÿFóSUmTŸ•\çɇK!?«·þÍ„C{x;¾-å4&‰‘÷ZAÔ»Š—,Õɼ=ñÇ‚Xrì ÿ¿ÞÕ>ºò`y`?þ‹yŒ.QÛ°<4&¶ë3ãY|ðªyˆ–ë·@eª/J/LÃ7?Ãâp+`…rläu%çÕ±‘ {1“…Øþ¡ó}ÈÁõ§ÙI!ÌÑ×qpfÂJr9¡Ž»x½ö·ç».ðö˜$울[ÇL!Þé÷ÕœÎ^γ,jTmA¾n)Ó˯çª]Ʋ’ýˆ¶à'®ÌÄþù·_ìð}Ào<úQ•$ï_@„yÒ›«X•ê?¿!I3ÆŒê÷q—Aÿq+þ®2$>Y:,öº#¬]O6þò…—ºbìöè³r<;Þ8Çq£<Þí¥ÑEôƒ8 È*È …xe‡"ók°Ä÷!ìÕîu<é˜Ft, 1CU"þ¯ô/fÓ¿:CèÙ—ˆ;GµÌþ$I^ÑÄÁ‚X^Ýá,ݞͿ ûߨÍǯc¦r#Ÿbàó8Sµ#øïB`(áFT¿Gõ¹™x­2„ä¥ãïÇ#ø8,ÞZ˜`ú WÖ&àö U–Wâ©V>/r$š\“GnÜ;MÞ4Ýj,²Ë€[ÚúL>E˜ ]D¨zOñëë&!äòÄe˜Ò¨Ê„Õ{pwÿ\{· >Õo™ïÒÐ3Ü·Õ óÃq÷gY25X7¸:ÀµÁ9äƒþej%ÜÎ uØÊ¨ 3œLD¦§€­œ8[gq>â bE›$I7û´ýÿ’ï}Ì(3 '‚ÚAm`-ó´ÙãSœs:ÙþÉNF± ÎÏáz^.D¸½åÊ÷Z°#­ép[$;ö ýw‘{¡DÊ*ÚÜì¹}Æ3'{H\r_:«1ƒÚoP»btWU@ó˜“EÑë¯Éh0aã‡EÈ~ï.|j}¸Õœ‘Ðì\Pn¢O9R¡Z +çnä–tøÐb‰L™KfuTAød?"¼ÅžLíê$]xŸzËQü#ΦMÝìt3geExí}7¸;„“̯ó ÿW sr[Fôý“sã§Âk%ÔBðÝ;x‘Xã;¿ïP;9½ÄΜ¼gVÄí\üOGà…áàN~9j€]Z)Üÿ ´h[*¨ËÆûgÕèš‘Ý\ÓuM6A FZïBó–WÐØàDFÇqÙÒPÞQCœ$t—ÿ­ƒ=¡ \·@#nU†O:¯Àë}Ÿy&2DDb»ql/ì¿¶×¹L&DDäþAЉþçt…¸"¼-N¤ry `Ûš„­‡/Þ¨Ä3âô½|%4¯‹„eKèÊëÈde5¶p»¤U<ÃsOñBì3.zp/¬Yê†Nòør&dny°›YÚœíìÀ»jBþÄ—¡šÞr&ê×ùç`‚š«PI‚¶`3vïÕ ‘„Y·r7®ÔûÃrv3?.<< ‹ßdÁÌÝ ølÀ7¯ÇÊï¢S¢Ú¦NÊb.sF² ö@nÝtåi"ѱ Ÿ7|È5§Ïÿæ’䋜ëÇ(0Xúμ¹È ÎW⇠zØCŒUMß9Gø˜ž^ »úVà±­»Ð«Õ.ÞKÚÝáüè\ðx:¶ùæyU ü狞ê}{NÄÃÁ¹Ÿ0ämýÛN‚¦ì%íœ-óOò}÷ÕlOP#¸©HºYÈMw:FW»C^ýcL<4 —vÙ“ÈÖù/wÜ9HbDz [Ðêý£¸ìU¥ Ýr¾îW¤ ëƒÁ§^Æ·.`*‡¿sr=`¨9&£ïÏk¼¸ú«´\¥À2îªDµŸ«EJ ñÙ+\©»“ïjÆî|ar1 µ© Z[Ýéö﹊/?xÏV›jS…&K¯â'…SÜÄßrL5çd¯¬Frø_W‡¯€£ÚxX4ôÖmu…òÙ³àñ¬@æùm«Ë¬…¤èåª4ҎªºdÜâ=x€Gs>¡uÐGäZñWØHž'Þ[4á«DO,{"Yh6bWˆÿ÷ÿ°ær 9â_‡ÂO\ ¹î Å©ýDíT&7I‘úmkb“¯4Ê›§°TSyZ´d"ýò̉½ä娰kï¯ð[Ö‡H=É©âðýˆ[óYˆ~ ‹“G5›(‹¼-Äþ8]„Ÿ§v¡JÿÂyßdYWØÆRÃÑóœ½'oö4ãW|óäç6“Ü+õðØA–Hƒß;¾óÇ[½!M«¿ÃÂ}W¨ßy2Ï’Jƒ ®ë{éQfïÆh±¬8lóãïÇ€äË(j¥¥À$/Òm~I¸kž/Eó¦^Ï%Æ;¤¹Í¯NAá¤Rh¿^L†«óÙó%˜l:—oвÏs°ªÖŠ+ZŽ»<òØCkQæhö›}ÍÈ£ èܘË.ªn…rH3Ž-9ÖÀ›6)&f.ƒíl'Žú°¼aqRüÏ”Þ:óx6²äsªs’üå19XfSJÒû’3Ÿ ØÆSšìØœ Ö'+ZO³ï­³aÖ¢ f³z¹–•‰{M^»£'°î`ÂÒUÛàÅ„f²²&²Þ)°[ °Æâ®}éÂ<¶0ž§É¤¬nã­»¸f2ëîø¯§5m ñÅ é-`2g s_Š,¿8‡Í¹¥Hwg$ÄöÛq< œ_3s¸ƒŸc8ãÔ*œ¢AŽ–¬'ƒû^A­)»ÉU^³dk>j2éaØØzµ†áÒ¸ñœUE(uÔs_³…Éz:–ÜQ"Ö»ã”ïw蓵Ìî¡Wð;›ùµŸˆ¸BZö¨r×Å2YU òbë ȪçÒ=žŽ«öÎæ­>µF…Fc›N~ÛœçNŒÔbPf"²J5&Ôã¹ýØF?ÛÑۚp+†Ïf&±C™rØ´ÅÅ1'<Ðc5>}} ÕJV±¥.ÿø©6eF•ñ·åhܵ—V¯“&þt¢ûü›àáj ú†»ÀÜy5L³ÀF]mòüì¢F³¨#Ë`Ũ–ÚØtë±"û>oÛ¶ÎŒ\l¶c*_§Âõ62xÉ'kï W«KðßráÐ •ëñóÍ]ðûï%¶5`:_‡×_Îâß’2ƒ$?1øq%†ä6œaòUlçm%ðZáEN8>ÇèããÉÏý{Ñè4‡ š ¹{ó,˜{§6¹•È^î²!‰¿Ò³w§°úi‚Ø Ä'*1+´=¾.Çœ©­à[²Š7´³¾Ârí„xM–$noÓˆÚ¹ü&y"ès.lí'Õr?©è,=r7æ>w®Ñ˜M“š‚3¸må8«û·‘êlœžf:,F«â¥Óˆæ­$6få_r!åÖ½(a™»Óűmxæ–)žF¾l™m+'°5›¢Hý®\6kü1¸*–¬°óaÏUf²—0(x€9Š9‘‚¼x<4=˜ˆM%;8e·ŸüÏCúxÒMÐiÁ‹°SC ÿ¸gàßËÈEßÈáñ©ìÓ¤ Þ6ÓÏÜÉaeŒ²€–…KðB œ(åL{PmwIÙ¡ÄúLÖØëX\¾|øqƒ—¤aœÆ«²G=1an` „›žáõµGPÑñ.]ŸBnÏŠ¢Öüa|•%ÎÕd£èMIreµ(![“FùÈ'Nf^žÕi*R³ úäø:kyµÇ 4¶JC…™0òTº@kÊ=îÜÝ,hÁ™Ð®fB6¦´5¢q\ýñ$ƒŒ]¸õWñð&žBóò|è3yÅû¤~ ¿ìs&ÝQ¢<«Y²Œ=)éWZp_·ŸËi0dbÌéÜSº¤¦_š-?TON5d³o*“ŠTûS²yIÛëhnÓ|üÛº]«ŒI`j:»§A–Û§X¸‹ç91•M©ß€ž²àÂŒáÏti6”{ÇyÚ°½¦u¸Ô4:}¼àå­v<ýn2) åw"d½–:zõ¾ƒO “H°‹9§²ìá"”žwÿþMDy£}ð2\r\¼˜žžñVÅ‹§±¸fAæó¦2~€ß¥cÉî¯Ç`Á©­Ìdƒ:ÈÌq§åcö±¤$ˆMÀènA’ùæ[àz çO†[¥[°{ázvù|?Ƥ½àTÅÀ»ìÃpíÜq¸4ŠK¦žfÜÖÒw´Ñ‡Ûk.Fιʲ±",UCŠ ìÀ  0>!]ï%—Õ‚ ÛxØo&©m;Á)Ÿêl c++õ; ¦Yx91¤éKßFüñ×ëþŽeíûVãgIEre¨_‹€Sjà_{Šÿ¥G*º—¢¬íW—âOÃ@ôÂ~8¹„ó¯Ãxá¶{ oú£ð¡å~è~—?ÎFÁ°i^ŽÝJ=üJøu/6ÂÔõ54\Ww›.a‡.½ iûbxñN6x"Å…E™fÂ’±Y˜ð9eNÃýµï oS'ÍŸ^/  äÑÎmV7×}æëz M&5jç¿®PkþF$ßÑn¥É^_ÁL‹Ú!8ºNùqæ;DˆK†;:IÖK³7bõÐ÷Çu(L„UÃ¥ÍÖÖzôaM3`]ú!˜Ëý§š$Þ¾ 'Ð#T½¶cÂ__¡Ü«b|ᴃ̞eÀм_× õ/³Áᜓҳá…ÏœY³ô?»L»yß=2qÛ½^üîƒBÖtÆy>l’m wÖüA—çM¸ýE7VÀ€¸ê‚qÓìıDéÍZ÷Kü—ÃÊ7˜óV„ö,¦7G1ýô€'Ýž…àµ~{l'óŸç²KÂàèån(J¾ ÝÚË kc( <»‹›0+ ‰¹²8žµT±¦cב¬ß÷>î`žÏªYÇ[Q¶ÂÈ}ç§ñü|wÂl¯?pPÀåìðd®g2`COOõ£9i(Æ ólXËžKÀ]¯‡ÙEÇØÁœûLeÑI¬ëU OçÃ]…â±S›E÷_Ãðžf´ß÷ˆU'Êábm)\°“M|ìF**ÈÅ0×sÛ§'ékÇ“KZz,dÍ5ö¡Éšì±lØYŒTŽü¡…içàUì46íçLÔ‘:wÏÿ!ë§çZg6aÍÄXΪր¬ÝðZæ.#ë¾µqûdË ²D…D G°x‡}8³0›KT磜jÌL¡I{2PKe7I °â'3Emzû”3sI!ës5¸Jâë×*“×"édIL«6² ·!‡»À;Aöé0ç'Þ £–Én­ã‘[‡&›*ð[Oª½kéºã%W4÷˯BCìÎca¨âÜ@¿'„T:ÁÚ+Á´èe6KêŽÇXål²,¯Š‡ˆ™MXuqaœÛ”KúCßP«×¦ìWS^*XKJ*É£EëYVG]°ó:øLaG[Fðôà3ºŒ9ƒ…a:ˆ y3ñ/ŽdãÏÙ¼í¡†L÷] øŒr»g{Ø+-u^˜C c[ÑL^’d¤ÍàÚVè7édÞDRù½€ÕÇË~Q°ø…ѨT` ·üÙå¼*8Y?NÚ‚Ûêr:Ë—ç6B'ý> u·?¡ ”5S/Ó’oœüÌwæ¼í"ñýîÕ¤r¼ •Ÿ6‹»·ytïkxú‹óŠ{„šKX÷ï.¸fi‰eœ¹6U/W•áõWšÄyZ:$,WŪ¢Ä0«WÖÀ‹š=\ÍÓ|º´¦Vîõ‹Q ØÖYhøk r¥ú J)ꑎGA»ôøÛi‘÷žÕ|g ¶È¸þ;„üˆÍT|êâÁ^ßË€æM{9…—¡h¼×Êt²ðúÂ*Œ¾ Û,Øó—=øv~"§ZûgÌnÇGõÚè'0Ov¾…Ù‡KÑÍ|-(HL›`IVö+;$Ø )A"{À›ÔK3Ó¾hô¼§Bt5í¡üà®'OÛqðâòÐý*ÞÜçÆû¹¸íê¿c ëR_^CgKN#kŽ"Õ?°…Dý•Çÿ|ÑŠy/±ù±û½&‘ÖÎvãŠZ xÿTÍñ?q­ª/nÌ£\ŸaüˆÃó1ðB² ÿó×Ї×W>rí‘ÏhÀ™Å5Ø|¥z.åËb”º9@óMÏqhhŸAǹ[.Ýt5f +eãàÊú«¸Íü¶Iê9v>ŒmJxs±!ó8Î#Ï“3ñá¹÷ê|óZÏAh¾•C­â¢ˆdÇò}y/ˆØâ/huûÛaìk#"ÔZ n×Tɽ6 ¶s‚™‘›ˆ¿„æÝÜÂÑêl|9K–îÙ bAÒ\}Ã4véX?¼¶¸íåg b›nŽU"=s|$g’¡©.ìý³J舽ÕÑϱzÐ\˜'CîHµá÷#]¨oTËíý£J¶ùaFƒ9ù£’‡gdš kb*^³˜A¾FÊ“'Ë+¸‚aeÿ»Vß΃w­é˜Z—‚ ‘|îļçz ]K þž«Ò©‚«÷Ãu5ò]éÌ“›Lý4a“Ïj±ê±ñÜȶV|JŸraÛ˹ygÖ²Ó>Òä@èV–mòž¯Ú×ÒÙþë5{æGCf@n¾<€?£ï€ù{=^ú75vÁ?#Ï®gÅŠâœmcèè:æâ‚ÁŸœ„ž ÓW"*fG` Ôo§7ÓŸ²Q]^ˆûöª›3ãXƒw\É>/ð¡ór8ñKÖÁ+?ܳЛ—™Do8Þ>]Ï}¯ƒçYA(ó[•Mþž‰šÃËÐÊôo÷ö—h¸*+ìßДûšÌ»!ž­9\‹.®Ý§Nž®kuüõþK`¢)èér¤H°Ü¾40 HÄ.lk&EóQ´7×äñsicuœi>B7yF¢Ù­BšÁ$nŽ#•{¢9ÝlPtåìä·0Y+‡»aÓn^вj>;¸À '…&ÁÛ±ZLåX%æ5‰3Ùëc¿p™ÛK^HÖ*lÖ{ zGѽ¨Ø:§$ ËnŠÐò;î0õ³*:_Èæ î‘$½zšìví üªÙ µ÷Îb÷‰H~Ö×zîˆÙ<šë'Å{»Ä“é8Žƒå»àÕri²]VœÈ–Ñ 2ÿp„ƒÊ-3â¢Y‡B½™Ì/W†ÓUP#{’6€mîcªè#ÅìâذŒ">.†ÔèA ß$ÏN¾•áäÏ_‚Âíu°.V }«¯A±e ß×b¿ßºó{ﯥñ¤ßâè;Ýå:áü»š Þ…ȵÊY¼Æ1õcx}(Fß0SŒ Y6Ý`xá,î5f¹/k038Uå>B÷I˜ X½—ÿFû%N/ÉùÕD²© çBÕ¯ñëš|þûÓ¨ß/D´íoÒ}1]¼j~!,3‡¯·ÝqØí~«{—ayM+]87¥¤ÔÙZWqX›;Q-³Xê¬hþNÓX4Zˆvó¯ryÞ^¤{Ž{9Û“eDÈ‚ ’0ÿäyàlÒ¹Õ=31¶:•NÝ@d'±Ò,ÒÁû•'À&"ŠNÚƒƒ'þÀ»‡‰8íH6$‡ãè¡ÆMÜ¢¬«ptçç2Å=¢(¡³ˆ+~„_g[°"=ôO¹+܇G#¡Ækëøw ^ͱÁ‡ÇÑÍî&fm᣻ÏÿίHœé>‘EK¶ò_½ž‡j¦Á†yËÀ{ž;ª+9±èebl—p-N‘ùEg†C]uQ²¾b;2kÃömo í€N΀Ó…X¯äeþ,éWô”Íqüa8 ™¡é^40Kº¦^éùþè¤u’»zå#à™N¼Û›7bj±xrLNø‰ÿzUØ£—Èi§Ilƒád\$&Ä3Ø.Ê^mx‚çÊ„ØøÅGPa(M?!ççr? &‘¶ãgqg—4S_5VYqZ ~Ð^ê¡VÁããækjÌ)–ºq×á<綠ÒcÚOVbk¨ Nâjp±€#nÝ:‹Ž´Óþ X[h ÷N Œ†˜´Áq¥@.áí ®#þ%êàÛ|Ã}ÿö’“rbì„í^®=L÷¯ï¢Ìè>WYb¶Ñˆ±Hro kð=l{?Üô?‚‚ƒ‚Œ¦ÍäÝñ•fg?´Ñß*Q{r_ý^1t*#÷~S1ÔÉËÁq³{0®°Óç¼³°Xâš=uá›Ëš’æhþ Aöµ:Ç)¾‡"7¦s¤—Ÿ€m×á®Þ¯¼fµ2ºî½'î>ÓËùG‡ó¥OŒâLÑ'°;'¥3ÔH’|9ß2:–9Ðü©€õ¥~§* .(Á –ãæ…ÐÎÜÍ[ßѳ¦>ð=AŒ&ÂÓßåÐvîâ‡O°oæXöÈg#Û\ g?Ä`p“NyàèSã+cèæ·èÔ‘wøû‡.ŸÝŽì 7i]øTçK?Ë3£ì?rŸò h¾¾œ»“˜ˆãºõ™©Œ³Ÿ ‚‚"ú(ÜÅsÔkì§ 4/N“Þ)–敯ðæ,$¦°g2¯9—‡/ÐßäÜ#Ů߀Âèÿò†î–%õ~Øÿ3êEé£CX”¹+n¥Ã‘V:dþdsàåA}P“P! /äÒ§f²2ã†|R$ÏÓÔHór/¸Û€ïtgaé¿?œ°‰$ ¹ãCWתÃצ·øZ ½.OV$tܵÜÄÚŽæ x–DÐ8;]¶IWÆÑcZ-MÐÒe›Û˜ý[/&U5‘h)gÓÚ¥W0Zø&¿TN‚8¬ À5.çiõÃ0ÞãKhæÑ(TMOBTCãQ_úie*:æ·kË,ìÜiöÿÆxÓÏ[åðã(Ö/4:AóWÊARfÕ¸‰X™RI¿wü~ÓŠdúûudv(±~£k”‹Ã­3U±³ñ,þ¶WÇÖ óÉÒWRä×—s 1ë2Œ›¸‰}NÕg?®¶C]k|?üžs©ƒÓnÿðÑOÚ{n&:s¡èzSƒûÐ4ãï5x¸l5z»ÿ`—”Æ5¬:u÷§‘߃ƒ°ðà9Ôí¬÷ƒ÷ûF÷ð´Gôaîiìpk„ÄññࢦËÎžÏæ¬¶aêÁ­%gâê%í<};cæ3Y…š>ïãÞKUšyëÉ«ì X½|&«UÇ»õé =Ú¶„©eÊQ°¦•¯a&½ãüªG¿k€š/óDǬPæšq€´ È`Â-æcõ?¿¤ÂiFØ( IÛCñåAYvf£Ô­KÀ•wUArK<ªÞúÇmh9 F÷ @$p3ÏžQR_¸UÁc—núGñÞlCà‡‰Â^»:ìÌÍq-XZeB«5þà97•踿@³”4#à7÷Û?hºŠº†^Æ>Ýexi¹bK›âÙA-¿`1z匃þ“Ïq^N8úÎ:€ºzó`ߥ žåêùø}R,ÌH8‚_ƹ`ØùKÜü7¢\ÐRxü¶Îd ®SPsÉ(þòžfñþëÿ ÏÎ#|7‚²q»0Ex ÙûîÊø¤ßZ³ó_`ãÁ§ðN0ƒÎûp‰[}S‡>Üoè|ù‘›xå:„8}ã“ßw`™‡“MúÐÞØ„æöÃ^']âŸÄ%j«qëÆ8q~ó@YH‹Z§àŪExaá¶pEÚý“ÛŽ‚›¹¡ùÀï™3ûœp÷½¾ò›ÑB<×ßÀ±e°YV23ßÂÕ-¹˜ø£dB¡H*ˆüM@~p[¾ƒ¢ñ$_(Þ7ÒvÅÀÚ8àùîÕ'(HRÖå¾ìxÅÿUx–_»Á†õ–\C³’Ř¥xût= j‹å¸± Sð]ÍÖobÀ6'†ð–GsY“Rhª¸ ]8©÷†^¤¦çÇ“59˨ÌDü¶½VÎ=‹Ÿmë¹gÓ Yn®&=÷ù>¬DÚ›' èsäxAÕTÎô1½¾J‡œ×ÄÝÑ?„¦³oã0o'üyîÿ²f9Þ90s]†\|. º²P—T¨É’Ck¸‘r ´Í~ 3îôÆGŒaSÈeüg¾˜å÷GqŸè˜7 —Í!æÐÂΪras’žÂR…P'ð:îY¿~ÚK‡ª*ý¿\Ùî‚ã;ùÕZŽcÿÁ|î]§ü>X€:a‘°UÀ[¾Ld7·H³'Sâ—f ¢3ÃöœK†Ö’DèðˆžÊg»Û‘5CW‰ßjtRŠ$¿VòA°÷òö˳†IZ ðõ"ŒÑjb(>85ˆ›äB½Ñpî¬ÂB™Þ:ר^üŽH Ó'%6¸ø>ÓWB—Ùë@äáx¼Ô…÷ÏžA5;ø{)èßî— {¹¯*““ÁGé)Ï~ R×Ìä®'䃢c–ctÒ:4Wy !»%¸_Erì´x¶ÔáÅÅòõ¢Ññ©¹;¿ ]ÃAâN9¦•p³@¢k0saŸpìQo*°ò'ƒå…k`fÙfpT³†î+]XµÚ‚U*G ÕßUØ#UÏíž ¼"Ül­Lh£&|ÓƒÀÔÛhµÙbºoÁži¾Ã]0˜à?…³ÓT‰ÏSi61ò-puc¦íGÁìÚ0Ý»i±7½Üß0|´×ž6Þë GÒyæn\j›t鲘a89†‹¿ªÄœ¯#uÝ_Žãb¼PhK3‰¹¾—~ÂùÊ6‚Íj]xs[ ×Þ<ÌÕûGâ{'ˆŸiÌœÒÙ"Ñ‹h`(ÆÞœUd¥gZÑ!‰Ã ›Oòœû¯úñ'\uL(äž7€Ç è›%níª×\KÞ 8Y˜+•6ò*÷–ñ3_]ÃÁ#0©0Üæ„³íbÉ@s¿Á1e–6]—óxÑÍm‹¬£é{©òïÇPpm=ý;YèN#‹™};†©7ÃèË›‡™vöW¨õ_ˆë¾ë1‡9ÓXAW û]¥È8ü ÿ|Å£gÀÂM–¸%£øXímÈE/b¡`B¸½ý=® 'Ë›™¢(›&Œ@¬i7L<0mùŠø¡ó*GNØ¢õW_&£ Cæ}ë¹’åûŠÁåÜ—A_4Ö¾Ž<Ï™X*”Î%úˆ’ë…Ÿ¹ž_ú`è)lÈei[þâB3/XPÒ Fѽ°Á:uÍñv2ùmeÈ~L`O»âñ‘‹<]¨ ÒÝ¡ÜO l¬w¤¦‰Iχs¨cÌJ®dáï’¹$é 1ÑQ£Þ$ÀVù5ÂŽu˜à Ö×mJg˜“´©Æ 3–üÌVc‹²¢ñ;-’¯¦ÁVÝŸ¯vMeþâli,EÞ`ðZ iAð^Ø·ð)v½;s—6pѦ£¹ñ”{z· ÝEÅžD\T®Gâ½¢Éêâ³xCKŸ«ÊOÆ»uœï‹²¢`g7ã&Dº±7;ý°ü#:¨Yƒ-JDñ¦ýLnEaqWùû_ºµt=¦\' ÷£ZÐúpèQdcœ‚ýs¡Ãîÿù éͱ45ê ô%,#ô‚)Ó†ó”Ƀ3}·”°3=©ï×? 4ÿÜ;=.(Å‹¦²ÿõ€æïý=•‡Bæ´§°øôbªê¤“õþܼ& ÊÜ;=ID©ô5ý8V4z±{òLvx ÓM(å”»ÌáñÀCüÚgÆ5‡“åçÞÑã…™îÙ;¨\o[‰PÎQ²x^=÷Óp?;™} mË$ØÛ¾í/Ê·ä äÂiŒ_9~6E®ñ°|WcíùãÛÙ´Á„.ôäºw(=i&Þçb¢çáæÕ~p;<>u©f‡·¶“@%p ç¿;ÄëB'2|…bý• ¶hÒ:2.¶ôYÔ2‰ûéøÁiwr9s™¾ƒ5ý„o^¡®ÞO]¬HžZk¿à¬bóf‘v% ¹ðfüž‚–ºäîì^œ§–Bˆ¹Šoéä{ Ù>s¦£$ÁìZ ÁãÀt¶;0†Tïh…u í\üS 6¡íëBz8íR¸©wGYí¡ 6K$Žì¸®ÊÎI4àÙâädW \Zû«nMdÿŒ~à–£!è>G…Ì8)H¤#Óh{ÀlVXªÍîúìbèj‡‰Ò>ØsçÞ7cE:4ï;3úi×üªîížôóO.ƒógPÙš ¸}ñ7,^ ln·JY¨Ìf<åÐÇ îžD< ¢Ðl[6^߬ÊÏÖî Ú*ºðMÀ \I8 f‡ü×Cź®dVùói7d ǹñ?Á@l6 ðmh1Ý¢#O3ÃxË^Œp·û¢ÉîÓáê¸(¼äán'çs<î7qž¥¡“v34혣àdؾ~-t ÄV†·^†Ý&k‰ÛŸD¼*"ËìÿBñçn»casTþ&8ïñö,–€{]V(䳜^"VG­†[‹´á?¯tÙ¦ú±ú%øwè‘ ‹RΧçø®PÀ–Ù_1=° Þýšˆy‚`5œ‡´;á_H+Ü^~fN;å¢àÃqêäÇm[3„B®ò¤¯i;˜ÇŸ£Gúºøsý#øû§[.îRT<¿Ëò:tà ¾ÉÕ.V#ÜLÈ„cy¸ÄÖD£D©K7‚•én,üCQýå7z°f<¡0½ú98Üû‡ÁwŽaÙ©d0È_kë]xQ’… žñ #?+²)Y趤Œ§Y,ÌŽüëÁßG‹áhˆ3ùºè÷__g1ÏX™?“µlZJ3Úþߣ9²M…··ÄD!.Áh,¸Znmç’¤K·éœàx+,á|q+È~üÊôÕ€ÇYí°<{-ŽEI´ûñÎL\[@ÊU1bùÀÌ9‹Vû¥Y’À\ø'èº/dX8&Ô…Œ…À›¤›Ï… æáæ&yV"+­Üؘóõ0Çv¤FßdÇ‚Q¿ ÁqúÒ£¬Hü„©»®àüvMbûÁ¥7µ×f·$!Ãí$¡knÃH-ZXˆaBÎÔ3¤ËÆZ»‘…Ô¿ôˆÝ›Â"tëhl½"Sÿ™Ž3ßàj•jÎÈÈšM)½†?§Ô`¿ìYpÜs>?z& µÈA$O9 ÌgýÃWoM~8бCTŸìƒûc˜â†0Žÿá L>¹’Þ®t%ö[^r/:Öî¡×±ë4Ø™ßç úX€Û¯…Í·OXpW&³Ñ÷|“õ:¤b©'U-J⺧ˢJÎ4²¤X’}x Ï6¬‡]ýu¼£ç$Á{lF•ÿz;c÷¥«ð³_4åó-ôèµ0¶†î­ÜK¨mw |²ŒAø’»ôÃñßçbxZvI§!{û}7Ó ÿy¨'¬åËióáè¦/ ?/Ž9q+ØÇyUXµOŽ¥8Ü0‹ëÍ.ÿŸSûþ/­g5Þýz¦Ï€°…±Ÿ·Þµš£Æ=XyÙ™ÝÛTpÿXc«»<ÛÏ#VuÈÆ÷1X…'á¾D§,(NÒÇ}€;ÿ?4Лƒ¯IÍ(6Ê/Ö$E{´ÙÜGÆ´(´÷d-bóÅ0¤bzàß0xfƒ±œ\Ю¦yô^§Ã›˜Ý˜‰)sè/}Rï}‘ vé±ïÛˆ¡s*Óî™ s.RW7Þÿ{6O>XÅdÝ Ù §Aªš"B"•^ÍcZÜAnÈw,#ϰ)Nž|È!ðµÔ2†™ä'6´õˆïXÇVh?µ•ȵ”lªÚ>É5×Ö_à´f%3_ Møøok_Eñߘuì î^ôØìÁ¬åÈ­GtCD zê’ËBWù~CÐê:…,|8Âã÷‘¦ ü° i.kW yµ3–z6•⸫Bt£­;<‹‡³§_roZôT…G? á‹©Ø¶i—Þ¥_ƒ*¹O÷/‚ÑÕKŽ ‡%`—ðcz:ù=îY;›‹8²‘[³ì„.ÜÉɉ•qI>q °+ô¼xõ1±â ŠH9ÂñROvŸÃ3ñ0YóªÖÃÕõdO¥3?h¾ôþÕ/Ð~Áë¿|V||MÇ⸌Y½Ž§>w8PÝL¯?“#“þ ƒÅ/¼ÿ×—4 É`Xæ¼&Hÿ:Ê›p)ùû7Aqª;}1VÚ2¨y;uõâÁ$+;n5R¥ÉÓàh‡3~®VcúiÕc>&‹ó©zÓ=8ðå.UTñ§ÆžbÀôWø%Èd÷O´ñ¦-·|x 5¨…¿ïõÈ£àXfØoÆîìjÍŒ·Íî-†°oKvi~ƹÂ'¹XÛå„{wŠὯcYvO ·£N’s#ØöÆÓØQ8…´o…]ûÔHY€×k!fÙÐê¥Ëس@u†8~’a•Ó¬Pì¶:¤/]‹BÆV|§­&ùáºe8ùâDÀKŠEe­ã®ÕǪ÷bøßõŒÌ¹h<>‰-V³ÄIîc0µ@kâ˨ױR\#‡s3ıGe1›b ß‚ùoEŒÑæY§iM´v¬€’ebdb[>ziݹ:Ù¬:%WÖ}ÇoÿNùóoЬ1‚Íß36}YK-·“¿j˜p“Ù`ð’¾ëêeÆÌVûžUµc›ÛJ¡úË9vx”o ÿXÍFNtãý¯·ù×óhÓ@wDy„7 poŠØ¢ƒfämÏ:”9,E®ØKÂÝûj%3º,Ƽ'ä²øû1t“s]N¬äžümîl¼1­É!Ï:WóË=ΠÍúOà°Õ–)i÷â—WwYþlÜT²Ž=ñyˆ³V¶ÀÀËFÇdcòùiƵôbÆmc2uümÞÞ'–R`ÎÄä˜Èe[ȸ[ŽÆ;¤ÉŒ3ž,íÅtèT=ϲ±x&½\˜è ´|jÊž¨Ôb¯”ÿª0Ýo“™ùürè¢q|ÊÀ²?&>‚‰Ö¯Ž0–G´ñæ¦FÀR%8óZ×ÑZ×hÐYaF"Þ.&ùýÍ`BšoLX‚Î5þ$¬Æ—=ÓÂfEuÔö™ŠGJ>‘ûŸP£‹ë˜°æwTËÈaWÞÃÒYyݦ†‚õÁz|4Xþœ+Œaó¯o&w ë8½gGØi¿Höàö0º"Èš·Ÿ÷þ9´>‘Í$eè ÝClÈW}’ëõ§\¦´bÚŒÏ4`™2SI‡ ëF.c6Ùj-Ê„ßcŠH#Lß® )j.䜤$“ü–ļ&½»ÃÁÂ_ü^ó‰sÝÍÞ~ÝŠç“p“L$Á‹R,ó£'.aè¾çÿÜ’ó°{Y/t9ýç…&C¸ßk‚àóSb®A~Ö_F݉¸rñmôÍI¤ûÜBáߘçÉÍ ,ÆòÔŸÜ«?nΩ@ÿ KXœÊ8è™ÈÖoCû¤ç†GH´pü*ÙÃ÷°<Œ¿¼†à^ôzPyÃ4gn|•|2üÀÿm9åÿþæ…]ëP"7–9âûÉ+ñXÝ-°¶ €Û@þ¢Yx¦w4â·†ÙдÅ®µFƒŒ8éZœÁÂjt©çt—ä³S™ÕU ‘ñ|ɘâ9츥 Íü,…í¿s>m‰UT¿p<ƦÚBMJ1tŸ"÷ª#1)ÍÏr„EËM˜°/–]€Û~Tj´& ›ù¯äL¿½„e‰É1/™8ÈÜ(‰Õ›&àéøZלýq¡¾…ÉäUÒ¬Mk#>ù^ÑZ.„äݦzõo¸µê¦dv´0Ö6$[?èÀúsºd‘ÜzváD8±_dM”—íÇ-j£Û¬¯D ÂÉgÙĦèDGx°¼{ÊL°.Œè‰bpÂôWŠæ·DÆÄ'AÎC&¸Š±€Þs\óÛ‰Ä`ÜnlÝø‚¯–—„ÖÓ%/½™—‚ïÒvÑÁN=¥€th‘Ž„çì²½ ›« _¦«Àh%–jÇ‚Wƒ ¡l[¦-¹õl ÑÙU?Š_HÙg‰K7ÄIÅ–{ØRÑä¸BêURÌLæ<Ç MpqT_[ŠCM*K®O s¿)ßÔYoO&o$‹5ó@Õ]ŸUU•Ä…$zßZ¢ßíÿ’ž€~»„¨ÓÆ| Rú`>Ñi+#¯l%ÁbY |I¾]*1‡~þ’êØYœm!îØ;-ƒ¹Ä_eVZö,ð^"ÆéjƒÈ‘LR[‰íp? ©Å…x1›²]}Vr6‚:Žêè¸l²LòV~»Éöd_âÏ‘ *ÓIûU!b®ëÄloLdË_¼9q¾?ÝË]NKf_ºòH­ÍbuäN}µþµ¹€§Æ ³û«æâUe”-šA^üм”zm Š®9…߀ˆ®&Ös„ƒM˹dóç¸g£sk}ÇÏ™ ƒkÁŸ¥I>â¾Ä ÙU{hLN‡~‰q ÁéÁœ¥$“y‘¿¶ ÄË=ŽÄéïëuBˆ±ÄlCŒ«·ççPï×GÞßä‰ì¹‡Ï¾ˆ±­ÇÛªóȽcö°l>»$CÖš&qÎÏ Y{J‹¸…îdÕs¾€âØ6Ð-Y€ÅºW¹Fɇûïä亓ïW²gi뉡Ahº²hÛ9<àX’RÔfuXqà :º&†Ûð “<þ‘œN Z7âû¶ct[™ ýæ7g'|n-ª$Ñ/aR¬7â1Fnx[Úü¨{ˆF>ZÉ‚>¦Áé§1`ý„“Y¹*nÎÈÙ© øþ>kÊ&Î’ä7_ý‰Õ“ÒáNQÜ_&‚† àÖaVô#¾Ø‘»§’·ËR`Ì©_Ðv`7Ñ9ÝŒ[·Oá}쪉Í?ø¢ `¥ sÄÈù$A4Bßâ¯1ŽäÕãðÙ*ŒÂ"‹õ¬ÇX³Vh_{ôäSà5O€$¸NïU¥aAO÷¼g! òÁ÷_S•­ö$–ÛÁ‰mYJª®nçúÒtˆÜ©ïÜúêŒVL‚;>nÌa»ÌBË÷Q«Ä]••p>â .6Öcë2¡"êên€ž§äˆ¬£$,ºJ9ɇüG—³_&ÉçEuP*ó‡# ؇ã F0&oøÉ%ýˆõkIÈ¥0Bÿ¬—fQ¿Ua½×.n³ý pyPÚì{*”_¶A“$ÿ÷KØ=UpžÇQ'ÝXÖ7“©Šþ„§ëVcJC]ÓG§ø> …”âÿއ ÿðçõÍ<}·blaÎ;û4Ô!èT})cãù6yk¹³=Ëéøc„Û.Y†ò­ÀY)ÁAQyt’'­1‡!ßœ!ß0 Rå>âƒãXÂQîÝn"™ ¯H‘áó®piÿ¨sŠ7tuûmp:Ý›háô(™-y~¼wnàþOŠlMÔbHÚîO7ÎBÓý’dµ®+LiHÄ#ÀLk?åû8V-‰Îª3ù—UØ*©‡ð./wgrV§rô©J)Íg_ëóguµCm±{Ùrw\Üœ=+> Ó®Àb§e|³žlF/¾çêýh]u ÛzÚéNQÜ/—b}ð<ÈdQ+AÚG‚ëdÙµIL¯5wº.Ægñ7PÈrt†Î!*E `îRMÜ—iŒñ²0S¤ õÐCáܼ¤RÜ·N‚ÍRQ?·TÖÿ/v^Ä?“&Ðk¢ædšô/ˆyóƒ÷£é5W·§“ ì焬'—Ob÷DZÕÍSYìI²kÛ|v¼Ã•¬xôDš9vè¼*Š Ô@æ„T8:4Žp5ܲívdÛª/°ìåi¸$OAkÞ%rbÊ£õ…å½t ûêt7«@î”ʶÀ§»­8=. ž´ÔBâW Õ Aló’pì,|‚½Y¼¯ÄñA4÷Ó7Ö'y2 :yÆ€¾(!s/âëÙg¹¹ t†Úð磖M±tð0¥’;#¹‰ox8¥%ó’yµ‚ÙmÓZ…@×(†<›ÈÇÇ-X i?µâ`ª÷¢Ï5‹{¢ùíÓ44ü)wæ¥ ÛóXYF””×ÐûþTBK'V ȯÀŠš>,ËÇñ¹ÀÖÖî‚i/K¸lú R÷ò+² ùˆ j½Ü†Öñ½´hg'xã<—.©KB÷f‹gÐÓ™C5RzÁuÁsÀk±Õ¸™×¿m#ªñ£#qsj(Ù£Žlý _?YøµVv%9sV”×λ›r íÍã¼…§!,êï_{8&U}£“)UUç|÷ÆÐ“ªûÙãÝ>¸€i±©¢í͉žqîÞO°÷×gXx7”ßW¯C/çqøîÞg쳌™EY*â@¯®-ÂZ‰ïÔ|I+OÓžŽTI9ýÿòR­f«ÿþ›ðG0C‚¹\\Ê“sÛ×'q÷N'Ó¿ž¡ÔÒÑé€TÊ]BÊÍÚ ç¯cUærÜè6ª§×]¤ºc—püÐ8<ÅŒÔ>ƒ’ï¼à½kÄ,: ÆîÏ ¶ÿ. £XÉÄõÂÙ “í$E%š§Ü¤A抷ÃÃP9Ö éÉ=Ú Ã¢úÌÏÚ –.}wŸ¯eºÛ"p\‡÷+N’¼¿­Å<5.Bÿ[Iâ<¤ëß>ˆÿ:;|`ƸbQ‰Éþ2 Ù±r`âæC>àëÔ ïUúŠÖvR‡È{tÃÊXºB) £/ƒöÁôÑØÊ÷“–ƒ}EÀ½PŒÛr³¿òŠ»_îËçÁGZã>ñÎÖ4¥³GpõÐ4ÞýÃ’°>g=ÙûÒÓÖÁÀOT÷óâíijÀÙ‘jpV/?d’ ¾3 òbÞ¼e¤°3ò–½¤©›× X†§lLÙäcþlöúÔ«(æê…žsÛÊ÷´…?‘E`ënŠåÑbpóAMîÙAâ…Œ1J¶–³û¸ºïíâÍh»%‚¾Ï>Å[eo‰¾ƒ|V'µœ–dsß_â_Wk¢=¹[%Ï¿e¡ÊI(8WI[‹½a³ \>vŽæá–ðSËŸÁˆÁ <õâ-®{wSßgÀ’“J\§³8ã>Žýt‰€)£ër.¢ï]e³ìN‚Û»5ðq@’¤AõÒ XÒ|¶™žâW`r“ Û´ú-JŒ=D{/ö`“v=·z±$ª‹Šß?¦¨ùù·CÝï¼B‹$TÞ-È$}~s–º`'“‡WÖmÅÇ‹UXe·8É2ü‹B©qAåYñ®ÀpÇcðâîRNÎú ^’4c»yÓÈ.i)’›¯OžšL!?¼ÒpaÜs”ìs‚ÅÄœöàçÄ P±"V«Àæ‘c|ÝÛ  r:ƒû×о²M;ÏŒ¯9‡c7ºZÀ;b T-tC¸vz®¯= ¯‹Hú53–õr+ÖÜéÄü•I(êÜáØ¤hAøÌVß°Ä»WWÐÞ® и¹ êU3rå²>d7~‡ñyKÙëë¢h±û6|àg|ßà d_ðd.r˧$ó.X‘÷»EñŽ5Ü3P¡%š”3Þ‰æ”=ã¥ÏêœRfZÃí® ¡ÕÏ€¶õwìYŽ _×àéÞt|ž~m¼ˆ/$DP ǘˆØµÁÈþÓp~À¢Ìaì±Q]Õ ;º.GøR玓Ïây×R¾Ì6¶.~¤åÃI–]¼ë–Û™ì—:éÈ-òòõCìZ:–IûÌ ýK焋ЧWIí_O£æ2˜y¿ŒL—1ÅœRÎ ˆÐÄïŽüö pÚ±F ;ý ît?—Dƒªx$ª›豚D¶©«gÞhè"G{Þ⦭ú–'vâèü¦ûÅYo™™Ö¢Îd’M‰Ü`yšÎÕ|:ZÀÿUDç}u« ˜³f éÍvAõ§_ñ¹õ_Ê×Pd«´ñ{ƒ;!¦Ho~ú~ur1¦•øÑ×]vYP{eBF¹˜µ).ò0Äë—jF“®I·Ñð`.úÇeÒä ù(|öô,+GÛâZîhçü´HGÄÝA¹j/úøDCøã<*ÆÓ>è÷ …þŽ¢æ…Ú,Rì+ný® …‘‘—þ‚Îíhö’Ÿ‚7/ܧé#oðÁîC˜½tåN|÷RÆ8m[ýÛ‘żNhxƒëÕJ`ÕsUP±(€c)È›tó6m=Âï{¬¦ ^Ø9à}9EÒò¤¼ßŽrÕI@J²ç Z\‘´Ìx†Ë”ˆÏø$ºðd%fïÏ"0on’(ÄŽôE´vÅfºÁc L+‡¡)’è¶J¸¿`0g‚"S»»•gÚ!Beñ£þ]*ôE›íš¨‚9ò}YÏVµ]KGP¾û®–#Ñ5èÖ5ãp]à"nY¦-.­±Ïdì8íE~Ì/ÝŽ8mÎáôvga‡÷ 8}çJk#W“—Dóv82¡ñÄÛX´ð Îká+œ1ÕŸù ÎâÌÙVR•d€ Ò„`ö˜6TÚñ®BȽœ[Ï"b:RÄ;u˜ãe ’ƒÅ抩¹¶^äœ^´É©ƒ¿F`t Õ¿F•!æ{ŒwXô29=AÖÅö¦Ã¹ |n8õŽî»LÎ9i=?×y «¼²ôß¾¤á§Ž2¿Ìzüö-šO/Ʋsï€VFá«ô«3-›}däCù†Hê´Wš…MÂî¶2˜å‘‰žŸáQ &9ÐqæfË1›Xmö7ú¿þä1¸u’žˆýƒGÊ¡Ä?Žíû«ÂrpoÊm|¯A|,ÞнԞÌ-=Äm!0ýÌ›“‚kï–Áô„R\_×Ɇì8ilN~ˆ™¿·bщ_43}v 9Âô«Oi²<àÆôÄÓ&¢æ†¢¸½9q¸f†›R}‰p»=˜¼gÒQ£xª"Ü,>Wå°½$™ÑŒãp}öH˜s…:Ÿ¹@_Õ§à^þ0=v« ósÇÒ2“A”óÃ3¡ó ×ÝuVú)v[Å ¸£nLEP,Ž#Ê’÷)¯ôç,鯗uD g¦P€ºÒ|nYº ZßöAñÚwðµD(f{ß–tÞiqÖ°Ö…Y" Î÷þ¡ÉÈgÌ¥‘Ä%¯Š®kß E;Š .w+~’Vcƒ[º@÷×^˜ßˬ‹ãR3…±}yvÅNdå™_q§ák.q’1Í\Τã[èÆœ,.¥vÎÞ’„ûs=K­ˆô È^Ë=1ó§¶*Øä#Ä^l¬!ÊÁãÈeGRñ(„ý>îI¸²âSú¤Z^Љ‡å//‡­BXŸD¬.ÎêSár¶4ÈÿvdãÝ>A™&¹dHTƒ“‰‚ÌgŠ½ÃÆ3«i0ÎDY«Ão½ÏÔõ…+Y¢¯†¶kC¹ Ú°Yé,‘êh®õ‰ÃÎ ¿ ç¾wòû¶aÖÜV‘ÉÎ‘Üæª»ðîé.ªß–ˆöW0mö1Ñ”šþÏ º\çFæN •ã‘×_J㭕ؤàúoÁ °úC­Ýx1h?üóœƒ&籿×dž)Ð3ôÓÒì¼é^¾ýÄB<8ñ6¦ˆ°WÃãGygý63ÿm*D!Á…Imñ„d›¼|?Ä߀ÏOˆçl¯¿Á;G`ÍQk¶ia7_saœMç£ÞëK°#-¸po²ðÝ<ò§jÌÍBß3byS˜ äjadØ/TN" ņDöÖ "q´ Zf!·ÓŽB½¬&hí†â[½¼î;P#æ?CW›¨hž‹ÔÉì^2ûõ°» בâ§WáU¡éâ­BéÀñœã·iðâ­#ùivTÚÆ‘¹’lh£-Q‰½_çP¸1]šXœoÁ×%0틉xçŽÁŸôÙçòÜð¼‹mÜJ²Æ×Ô`vöÛ?X!ô wáÉÉl{r*©X‡kÛ[ÉÅ n:k{>²ÐÄä/-@‰4Âî”aìd²ôWظh3w!GœõH…v*„µžÈóڃѿ_³ ‹Ý¤IvkV5Œ÷b_þØ/köS°Ö,µ¢¦§Y\Ìy¨ÇÌuÆ2о×»lyš‡•dÈc¬7,Ó_k!Ä$„ö˜Ì æª1»…8íK¾“³D畚ø¶ÙgDY²”Mßaxœ 'Ð MŽ™ÝBY2o OEUÒrl€wèÝS.v² ǯ_B†œ_pBKæ3¤ièx> †=¤™äjxÛçŠrÔfù>¸UΜfïKVC’ýY,ŠKÀÉ˶àÎápØ~:ƒ5•™À½×CÜÈxe|]ª §zc`ËŸly€hŽêɶ³õà½Ù†ÌÒ˜Œ×°Ù:.…O¡sŸÚ³,B.å.€T¹!8§åxÃN–¨8MûËy[V1.,ú ñÕ#"Ê¢D`Ä4Éü£¸É|?]™¾ åëO¢ëg\ò\Ó}ææžóëSR¤Jù*æuÝÆ1É˰òüOð(¬ÿäEŸ”ˆ5T&O…ñH àá,-r˸i´&øSç³ñüó™Øð-Ô*½Äcư¶y96›ƒ;/'SIéù¤ÿÖNîÑmAÎéÍV8.[H·‡¨!ël@ƒ¿FdÈëx™n‡ÛÆãiw?| ì*Á¥N˜Å¥ýCs«×´îöWÞÏXS6Ö7ÆŸ!jÍs™âúwÜ›ó00‘_"ÎIÒ±'¶qÇw·p91éð`þwUùËÍ”óæ.ÿ«^ýG w" ÚîEâ†ù³¹ÂÆ9$±À š¦s× ×üi>䔹‚—V]nÜ —ãÑúUNé¯ÇsO½0jk1 ëã‚÷~i–NsÃdžÕ(‘²ï«é‘¯ûG ¿ý1˜U+ èLyµ9Å/àcÓAÕÛ¾ÂéBEذê%Œ-ºAû20hË ÎuWëT D‰­ã!#k:›>¼”õꯂ¦—£x^ƒ‡VàǺxKo U¢URÏ}]Ås´¼˜ÁMH çÜ _àfÏ%”CŠ[5o|ØíŸ‚è*nŽû‹Ê÷F†ùD)Ãöó1¿~ì:fÄz¶[±Êh»-"Hråvp{ |½Æ;æxŽÉÄZAÍ at E¢Ø²$·O~”fÙÿØT'iî²YÚ¯™ÉwŸÀ‘Pv;é2Ä,:jóĉÁê$~Sìÿå‚Bò©’³¹—Ã)ÏÝ@NûMg“×;“9yþ¨þ =In‰.ùª=KšAßmyù6ü˸ÊCÜõ“M¤uÙAŒŽ,á‰CÞ^ù…9\çñu,z.M |?ÂöüߘGüÈÖ‹|n›`.º¥Tb˜{"Tý9[Q‚­hL¦ù©¢Äq<››îbz#›|­#ßÃYQ´3óLàH•ÇÜÆ¯-Ø´ÔgpkÜF4”šŽ¿o„ƒW”àPßtؼjÎÓÊĈ¸ø ®Z²KêLƒŒcê Ô˜[îLn¤> ³Cyç°O?èãîN*ÇÑøòÜz8ðºî)§Nu?¸Ö›×h½y ^Ó=ˆb™mðÊ|:Dª0ï×)œÊ¼¸¢\L2)G%Ÿ 0£8†›º\˜ß”µž¨Þ]'ñRŒ¾_ñ®>}凸µœ¼ñL¶à|&>Ž@ÿhÀëIM@™ÛÕuìØlkÌ~$‚½Áe ‚÷7¢çÎ),}s&–†“8̾E#æÙÿ¢»¯è‘›Å9dJíSnƒ½!«·‚öŒ+ã›|- ýBM\@Ô;úñ‰iºÝŒžüä~žL".w–Ã5ҰÿüZö|‘»wvñ†öÍ!ÄøQ4È:ÚYAfä' Ÿ4µ'{Lnë²û¯à­Ð4PÕýæsš¾ìÎ4æiqjö+£Ây„ëcÈ‚ãËXÒ×;×-ÉÜâÈûÒ“PW1‘:ó–Î=s>èu¥Rxàÿ€.ôºHçÜ8M®§ÿ$JóžÙ0÷¼4nüst4Åzõcpu=£)×O€}‚ Z‰61¯ø1¿‘ûukùuÝáìrïlðÞ›Åû¶ú9­š§Ã[aÿ‰›*Vã{Á÷ºÄ_oü5óq×dÈ…1²tùîÆT`¿ÞÒt”;S¯žÀ]ýJÄsëWþUá°¸VB$[¦aGl”o,ÀÂ&“•µ†è­i@Öwµâ“µ4^e×°m+ÔÏÀ¡rù½2¨ôM{ëE*fªƒ¹ð9º§x‹ëÖÁ-ZÊtï°œËùš- #ÆC´ÂüN3ާºîŠPsؘ®î= X^ßy~(N ªíÐa¿ŸWÞw-=¶êß©þVˆºjÅæH‡"ÿaØ{1 [F×bL ØEÎ4Pùóps† Ì?ò.ÿ3"ãþʱb?±÷ÃYfZ0ºiØÈö™D´4¯òâaªîjhÝ~Œ:daíp=². +M¥BµP%9¦á ph1‘¸©Lf†RHä´1ìQlùgI&öˆ’Eok0rr,„}±Dû×+Ñøµ¾µS&¹˜^Ç£S]ƒVÝJpéí‚Ý5ÓØGÝNCï½½c?~V˜D¾Ú<„Æ"¼GMWð¼ôÕF˜´þú#!EW.ÿ¹µžÀÏ›ÉÜ-©Nxqá‘ÿÝßŲ°Y4µ½ Çý X¾”ÎYx—Ö=´ÂSú‰|§h ]KÇáì[ÜÓ³¶dÅælVØxrt’ý®ï!ôÉúWø¼äzXlàï_=Zo˜$¿—bEeU|ý0 òL;ì>–áž÷CéÜLŸ™r³|¾^#@ª$¢\†ÍÍ“‡7Ü-ÈÐaYQ1D¼|KLH&?ò$ðÂÊn¼×dºÌy­!)”ª·/bôÓôr–0\ÌoR¥dÿïå¸'ÍŽ H{Ð6k Þ±ÌÍß‚UnB†^H»î‹ÙòÜÃäju0÷¾ï#»)Âc3†S`¾A.Ûå1‡ÕÐIÿø±Rf©/am’$imïÝSIJu8Ö­aûVà¶Ž¤ÂÚiêv|3ñTgïÄXtòòòn<[Âö*óý‚øó{?ÎÞÂ\>÷b!›Á-ŸF6½Ž‡ÍŽÄá¨ùú¤‚­8žDn®#µ §ùkÇ|Å[ 2°´–kÆCÜÜ6<5 <|â-Và¹Ü:óÓ6“mÂ߉@á!Hœî?Ò=Øõtšo`IæÛ…ýéàýt§¬ ã㵘?K%ÖOçã¤+ÊÄÛ&}¾„RzÓŸäk‚Ñ0Gøæ¹ÂNïYdÍ•äÍG[62Ô›& ã‚»›Ù¢ôS,¸û5Tß3#gÊ]Èe™Aˆé `““?á²Q Õ·¾|ÄÜÃcØÔÓ¢Nêr³HËUâ­=Š’F i•7?ïG8ÕeÂÄó’ÄW-Ž3«Ô`<óàE×#¨¿ö š¯¼o»/À›¦ùôŒ¦ øn.`•Ýjö^IP¨‘ ³f¨§ vîü-iúT‡®Ÿ° óxAbjNq>‚U“ø˜%&IõCw†> ͱ#N'ê¹æ‡ÑÛ¬žë Ò¿Ú#œÅs:+FÄš]P8®ƒ¯?ª¼Pÿ„>©ŠÄŽ{«PÕ®SR™q³ËœE¤“ù\×ÈE®·ïR·sØú¤¦Çž@seRõp<ù!­ zb–àÕTŽ‹¾œ„?6ÀÝŠg‰øó%jmñ(UÖù~KIøisæP» PS,‹‡%‘{¸ŸYÙñ’ÇlûžR2$k 8zí®&ât‰€UzþÛL7IaïE'viÿ"²gÃslkÍÁ(“‹8ór+ÍÉÅíùL@ú×ãBŠU²§š¤AÓÅÍ(ï×B*Ÿ?gyA@VÄ%€—ò4€êûKIÌc1ò7ò8¹yNÚ¬Àè¾v½o%ȹ˜‘z}sbn÷_/¿KWi}{½)X”Nb’§%u X‹ïâó&™•ûå,Õ}áhó)’µ]’13²vQ0¾ÎC­½aÝùLÕ3”,˜¼•?#’° n¼šmêDþûb¯®ÂÚ¬c6'wà ãâ¿ ¨–()Ê»È ,f»'n@¼• ìÜ÷šWºÔ´ÁY,™&ÀºV]‡ëk @< –í''ÀûSޤ3Ô‘ìjÓ&íéÀýx>–oQEwa2ëG8­+Ð^ë€zé­ÐèÒÓGuÿé8湈O…Ÿ°‰ñ,§QuTßNÄmŠå4gb;¿`Õ7¨ÔèË2йo5~v$c£:ú~è2Ÿ T{-a‰M¿psþXGƒ:eàñ]uâ°þ¼ºÃçnç@ãêm\ÙB 2(–jO)…ÁêðÓW“ˆ\'rÏl8Ýßzl»Nãü|ðaƒ.ù¬| TO{¢™Âyž½Õx66•Ù¯Q#ŸS=ÍZj9ŠÿãòfÂJ mZnºfZU·k°òo ÌZ-C«™²õvS!uN ]¸:¿‹0æcmü8 w‹°E¦Ï±¼à'˜ Þáz£Ôø^ʹ¡ÏÚìÂS%Ôߥϴõ|ÁD3—VäH’E¦0èÍŽ? ‡ô«ÀûÛ4å/qüÏÇ,Z¶Š â M´e³EËzÑbn©q—fâÝphdQÔ‚Àù±øËˉ5­öaŸÿck†m:‚ë¶Æ²×£˜ìtbn9²'Ðïñ ˜›þÖòÿ@™E&¬|~ —7F †&ä<éÂ/“œ¹%Óñ½Ù<,on ÃÔ¾tüØ|—î¯Ãsó.#6à«éùœyÌ H9µ‚Ù–;"ìœ7'H³m…xç˜  >6–êdy~‹~Ãu…8°[ÉÓHY¼Þú „Þ‹Û8!ùÝàœ JüWá¼ÓÅ°å— ‘Î=J¯(1É3Pøo„r–ÍÔ¶N‚<°ÏÅÚÜ×ÜT›7(§02I»Ö &¿° –MNÄä“#9!YI}ä:¡Â7—ï‡ Ÿ°Lò ¼­<"mc™ìžsø–Jö¤píïõÑâÞÞßÃ`Rfw·nvèàþæß¡Åßß6÷ý•…u¥­8˜Ð†¯ÌØ ¡0ì¯yõÝPe Œ=zLݶo›{evä‚aõ*G¿y@¦–àáæ˜iá„&«*±DU›o‡ãŠé[q¦š¼^äUƒÜÞuìÐü¯¸\ñÊÉFÂäùÔd»ãJ|@rþvôZ<¾=Æaÿ 3RNæÕŽÓƒs“F Õt&¨}M{Þ¨²&yð™ñ‹?ÜöV½ýBe¦I’#™|ßgÛÑY¦yï"ˆÂ›–›„‰iiŽ+þYCѤ#¹rÆÆàžCÓØÚuæ2Y„™=®âÆù8ã×Ë%ðbÇ#¬ðŽƒ’' %Ô ×|¨C/gt¶aSÜDˆD~ûܘƒãõq5™—ÀÊ+Ž{Q‘giäM'l|NU+§ã4wÀo¼T’]}³š”‘¦j(P¹‰c3ׂ–ýI“?Œ¯¯góß+±ý9B¤-ÙcÉú¿2øÏÔ’—Û*Ä’OíÃõé­xpÙ1Ø×7e-"PQ¥n‘ .áÜ/Ð_’›ö_ÚÓh}Ÿ:OH¥Ä®/`C=0=|~îP†•úÄr ‹uI‚µóßS½ïìýÍXÖ©Dj?ø2k…´h[tŸãô$ðþ¶w‘ ó)’„¦c# ¼b*îLsc‘ÉéPmó™>}ÑÕ¼nßbºê¯?®—ýÓ¿5£ùjžë¨&°6YBŸÏ¹ÆX‡ŠÍš,ÎŽæ”q“LtØò¸è•Y¿Ÿ[æ5 ²Fìþ›x¢YÜÞÌ oÿæÔÏ|€oô°zº‘=ú¿ªÍÎõèæYØãhʤ–í‡Õ³ŒÙW¶j†|ÑràVV §Eèý’±ôÝÎåì¹i<üÊl€JƒØûÉ–­»ƒÂ7sÍ}ú8ƒ !úsÁVtîU%ÍkdØóÛ³™rb޳0a¢m˜×›ÀN´[b½ëîà„5Ð*BfY7Ïpl×ü,E¼Nïĺ߸g®Û°ýQ ”î%ÞÓ¥¸ÄÅéÜ©§K˜Ã!”š•ÂQ>ܱ­p7ò>l;üwGÃÓKÙèÑ;‚'øÂäÑÔ ï‹ ¶Á×¹¸ûã™ÇðT"j:å#ÅA"4/}1ª­îò_– “rá>З^3ƒ¢x»#š¦+ ±ZA h]|oŽ÷º] T¹.Û˜XÄÛl2ª8e€Ü‘Û˜\Çñ/blòã1_ì\¸]GL+8™Ýÿ7å·ÃÑk`»Kœõ?Óº#Cp"|o‚ÛR&q™»jô‘ú EÒ¤U<¬|Ö}­„,Ád¤×]ÙéèPÜm ¬¬d%+ÅФúÁ6ÎÌ3gеôè¡ti$·?éÃ鉆ØíÇÏßb à¶¿ébwä;îþåŒRá<È>ÌÁ±øÔí)çu/™{ò]‘{Tç„R¶5h½2žŽœÉu©ž«–¯\_żð4„gÖ±…3Vƒí ÝÂVÜ#órØ®l©+tÉÛ§ÍÍÂy™\ÿˆ-ì_|‘³¾Mrño°Žõ?G¯dÅUO?qÌÇàƒš~˜3Q“dnSeNF!Ø?C÷›±µÆãÈÌoõ ÊîÐ@Õ t.Ó!ûOA‡Ùôs°+q C+Ó6ú/¼ ºÆôp›×%#_3Œ*tð Ù‹ïü4`a®HõrIî¦äûX)ÞV %ôªb± /‚¤h/ßöž~r®í÷½Ã ½.àÜo pw_ïBÎX½4…Š^ôÆO;_âtñ7pöf)ÊÓ‘x®ÌÓ…éåés‚·²Àà†¤ç9‚ú¯ ô™>—`ïX5nt>?òø_tºSg“ã£x?d:Ñc!ÌÒª ?ß?Ç·Ñãâìö›"¦nÔlŠ(är®åx—)³³;/bÜ%]­FÉÙ#Í…ñØôq@vŸÀI“ø[%,äw|-kãZ·òiÏZOü}#__­¹ˆP(0«Àa °0!¹ÿãàÊÃjúºp¥¹4ϳ„’&Ò ºgm)¤’D”2dŠÈ˜æA“FiÒ T¢4ˆ¤{ÖŽHBÉ¡P‰(„(‰¯ß÷Çýãž}×:kŸ»ÏZïû<ïóŽ{03bsQª>ޱò¬Âñ3ŒºÞ ò‰õÁ'ÆKüVÏœþ³4åR`LPˆˆ/¼ñ>È“ œàCh-³ó}3ªžÂ¥?=É«þ{Øù²ËÂÜË-°„¾Ã”Â}À»a;ó[)®í©àl”|…{ÒBÉY!¨ôÓ%ew`5Ï|*(ùŒ-JU_Š-hØú9}»ˆwÔOæ„oŽf[ݰ>à)ühÉÆÐmv>×vïîòˆÃ¬Oä!¾Þ·àèV{êcÝ;I>$ÆhQÿW6Ü•3©Šá/0.'_VêÂҵP:Õ®÷? ÙÊä@Îسc#äÅj"=/ŒËgÑ]¹qÙžÍÌÃmð~Ô2໳}+$iæ°&Ù~ç70=–&ê¸ ßol·î ˜¨É¬]rp •…êÕïpÚ[o÷º€#·£Y¾ÙEpq~ ÌŠ”‡³Å ËÊåÚyq}ê´ ÙOb+÷³ª&°,‡ ÙÏËq™=ZôŸ§ÛÓ¸y­Ì”£%ÌI;^¼ø 6~NDÝTGüü˜á:<«—ì’¶,øvPŒþ3ïËl²"¿q^Øås—«5à¶)ùlY– ýÊÂC5Sâ29Ï\@Kõ`,Ó»€Iš.ШÌÎt¯…á0Ø´>%[ÄIÒ†x6Y~[©ËÛäe©[×V¡0g§ÔsOúf‚u¥*\È.ƒ9ÍçPu º•ÄI¢ÞUz*ÄäíT$wW^ff*ޱ×K…p¹ÿ;¶*oÑÝæKzRÀ¤êþ§Ÿvnad +»/ðä¥ Sõ~ ~ó»M~`ÁbE˜\#³¯ÅƒrÃtø©ùø¿k¨ÿr \LZÍõ¹ ëö)Ïžèic*ÿÂm…ì ܃:N"ì˜ä² ž#«>bXQ;s½ ˆUÙ.X‘z~n#ûLH7ÑF»_7™™8(_p­Ì¨ÇößGðñ”ËP<0#‹êÙä' è*…;ö&ãþ¿f¸Vp&¥§¢á‡}àâYwteYÏÀ‡Œ¶µ:<YÅyf}JÉÒýý@ð°u¿®Ž¥7ó±Y¨ˆý·g,ÿ¾–tíºÊýÂhлwF•2%1jäKÝn‘XBfïšß0,ûˆ­ÉS'ϯÀ„{ ·Ð“Ùß³ V9ÍaüìÌ™ Ž1lûRÃaÖ"ò_îWy‹è³[ì¢×ù(ÙÚ Çg£ø%?vñq…†WõàÏ×€ñ©¨´ÙÂ\‹gÎòÓM2›™gãg@ýÇfHÎyƒ{þ´2“ïÛ(_ÉžÎlK/¬’ f2ü^C÷»_ÍzdBйǛ¿ è®Ù0Åz<à¼Å©ÂÍŒ|ñ Ìm:„KBXÁƒ¡P\8,?\d ¿ a ³§ÐMVWÐÌx ý}2<7m'Tò7 Õ~Ã57ëAty3Ã\‘Þ®½þ}…’7)LÈz7¸´ß¶<1Âíù¨ùòó¡ ‡Y7ê$^ŠŒ=ŠéßøÉcùbØÊõŇpò:òO|gŸKPïUBDõÇ›{qáP‰–Jòä(_K¸Œf©qÉ1 ÖF§‹Ùxœ™4©î»8‰Ù—Ñm{¤è‰+[È“ËV 0è€!_Ï‚½æE¼úi!¨ûœÂ›š¤óÙND˜n°MeÚóŽÓ6<æHo0‚»£çQþi'ÑêÙv†kh©C¢´=so*Ö¢¥V&&×·AìX>G n))HøŠë….0í%:L[Zú\é¿F§Ú5¬ j;'&æ .H @ÅŒìeÎ…2¯ßÕÀz¿ÙXVý fÏ~)¢d±ïª–«Nk6q<;_;¸}â¼ X|­çÎ']•q(ÖÉP¥x>*@‡¼ìÙ#¿ØúcSèºÛšd²NîºX ¬%#ƒ°_IÖ c#Šnc#ïzpXE`Aþ(ûãØwæ”=CxƒàÌæc ³¶OIңϴé†pa²3ïúè¿aõ'â¹vp,ãN‘ûÃö:Dã¿]áœëjZ4váöñ·£ÜHªOÂvŠsìeoÁÄïA8ÿ- <ŸÜ·I;¸³ÃëaÕéìñ ~Få®™wŠ{à˜!óš(ã²—"°èØ'¿á[Ÿeì‡ò¸>£Zh©{ kzžã]q(cg¢‹·-ü–¶&§'ÂÑ{þqTþUÌìÝNfê‰Òwà²J'£¸!…#ë —½# ±8”¯à'׊®3ޤ¥pœódƒ$<6bŸ÷ÃݛƘ®/ƒð þ&<ɼr_×îy!ÇZœ­Ë‡}3Îr?}mcõé=îeÈØSmÈm2!NV›±6A‰¶h®Ó³) ’[s.°îmÀÓtš¨üÄŒy‘Ðñ2 ËÀ–ãø/ö7{Þ¶ŽùùaÃËøàÇ©XD£¼,ÍfœWŸÎ c¢ÍS‚é’ýxµS†,buéÇ[7Q¹Ó•=‡ K1SS½×ÞEÅèå ½Ó™>ÞqT ;îso5ÚðTw>Dy=q^@ß¼¶ÆXÇWŠÑÛ i¬ê.Üè †uO›l>õ›ƒäœø¢M–ü™ÛßL=ÈØÙõ`k¾†v­bgæ˜Ó9’Y ó†ÜÂCçR®¥ÅsfïºWÈŸ€·¬7¢ÃŽ5(“[‡kùu8'üN rÝtšY}›‘òZÏ$îWµeàsÊ”šlÖÄîtYjxÉ’ä¯ßLV’4uùÜÞÅ$;C’ÈH(`þ5ŒöqÄ«GC˜ÇT¨¯ÖTt¿¢ˆòۤó.á³d–¹â1„1 á0±±‰‰WÚEjžÅÓ[Ök“˜éÍ©DØr(œ…ö1.šyLó9UÖÙMjhÎ~ÑÉ~"„šåðl¬–­™žB;õàÇdß8)xÚfþ€Ë.Fda­&8ŽV×øWí$gÃéëø”õJåGA-Qê˜ÊŒKSß~fsî0öÙÆP6‚I—#{ºt9UÛ8Äkÿ jßþŒST°Th ¦/B_iYj0Î`åšoðìÙv9lˆž1ŸÙýÚýŒE 1~aÍØê^Äèˆ(Ì®•`C³0LÄðWKú%‚äñÂsì±µj̯çµ(u‡—¸JêãòS¡tþp!ì^¯Š?ã—2JËô>àØD.{C6„ý¸d&©¾Sü“Ù[‡Å9ü*…L¡Ôgÿ†Ÿ?±;>/Âñ"G²E_žžëgï¸ýàò>jGíï H–d³÷jS¨íÆLwklõMDÍuÒ°ÞµjyÒhÐÝxtªÞH/7m?Zw̆:2gas[3¼R¥›“‚ñs¾É•. Ó²TIÕúĹ+4¸Ix+k*ísÖ¡†Ù—Ð-V€ìÖ»‡ßVµbý!\lT =a¯ Š–²®õ tXÅšÎß’iÝØa1…~ Ôç,Ùr™)?5ŠÙN'èÊøitÏü_ "L´í@‚D{ÃO/j®~ŠáÙøc,à)•@jÄ#Nm^zÐÕó+9üÆ—‘7VÛîk§ Uøþ Lk¥–Þ@ýK9ýÓ¯czñ\bqhÏw.Ϲ,¼es¡¢ øÚzË^樊|CE‹`òÄ+ÅJå@]¼×FZö³iwò¼ô…çjÄß*’½=zœî˜áÅ㨃SŒfS½*ðL¤Q‰öxõ%.þpÅÇinà‘1cäd@éVMh~9wz”5½ÿ»Tüa×ÒòÔ„.Hò#¯Á„Λ‚X±\™µs[ }-5x"WœxO™º›gCr݈ù*LRª¾Á ÍX¤òÙJæÔÁ ÆÙ\l¢ ÜŠØ?â¸îEý8?M‚gæÂN~’ñü4ÊU"<6*D±I°®f~?þ†š_Ìéó9SéÇ¥Åð$~½¯ý†º¯0îïîà6åx*êae÷¦à«`gÚf)>›ƒÿ^C¥Àj²ê‰ óËÿþ§qöQNdš•áò³!6[·ù&±Ü^ÜzHˆ~|B_ÄÐ!{ºKY˜›Ûƒk_Ù\™3¾ë¶ÅgOBmü`C0ßRøÙ>ŸzŽüÆàwY“=ø û¶ø\x…'4GAÑ£‚>Î\H.2˜ÈgAv¯âö-"&ù=lûÕa¸«©Kp$UBÄQÁ8<øö±»ñ’5•{¡¸2 w{¥1éï¹9µì¥ÔóÛ2ÔÓ%ë>JßÞJ|i5Á¤ð½†;Ïʼnó³ÇènÃO6^¯ÆCŸ¬Iœ~<@eüª{*}È Qœ9¦MÕ:åé‹úzT´‡=7tœ3z¡q­Ò½Œb©xÐî:Ç¾È ü¼Å9¤ò¸"¤Ý´Fþßì1¾bX°(ó7µ3s¦áéï¡SÒÍþƜ̳ô¯LqtÛŽq'Nç?²–'Ñ~š2j7ÀÛÐ(HØ=„¹¶sh¦o˜q5uÌèƒøétÈ4ŠzíEýí\\ú[šÎç\‡Ån§XÛà»B•ØëÃLÛ(ªzwë_ŸGÍX°»|‡‰ÕTƒ /ïà’Åδä¾>8Ôp¨_h îIßÁöËæ8,[> Œs&ñËæqhNÕ€±,ò{ñM¦«Ø˜dþ<éJ0±¢š}ªM&s`İ©=šÃ˜Ð!û~®ÃŸ <IJK‘N0&¯[ž1_Ò p‰~&-ÚcÁÌs¿EÒÇÖŸÌFôóU'iŸl-ÙŽ+ªˆ/¥èé ùR<ñŠ–“GgÜðÊI«64½ ƒÖ8ï}èÇNŽs% z¨Æú ¯Ñ^KÏ* ŽÝÅP¸s ;°žm…–ë]˜§Z êäéjmÚ^´ŸzžR¢o @T™²M0{±ã"™ÈôV`@ú*[à!· „f.É ;î¡MŒºFþ‹c jŒè«Bôáï·°¨'ë9Ub(Çß —¯ºÐ×¹Uì ósÀñ<‡uÉkl8#8ëf Œ/ôŸM?ÿË‘7éà)ô‹Yw2OX×§×à¢^ J–Ýcšc_ çT–ÓaRþ3¯âî}R$Fw=Š%mdÑ0t~Y¯—m;™Í‹Åq…Ø-ÜæÍcøû¢ éú ëîì'ÖŠŠÔBÈ‚9{׈ÞW!ã×ÅAàí+ø)¸ w=•Àý¤š^¦lØ2C{@"¶Ÿ¢›”É__ri` m\hDüІ·ÞX›:ƑխÿàÞ¯lfÎ{$s”É×AloiaŽ\ IâÔ̬ÛZ‚y½K1Ø(§÷<ÅâÁZµí_°¯B‚ˆ»}B¸|—å^´cæ]‰ {Üîr›…[@$¥¯Þ”"·pk{š¸»¼^c»R3Ìh{ƒ¶/%¨Õøj9º‘=·ä•¢%ËCc¶OÅ‚&wt˜ºŒ&Ä5àŸ—q£º1oÓÁ›‡¡×Õ¬^]€ý« æúI‹¸†—îÖ³WéÞã5ÌC_Q­Ù•½òTóŒèíñzØïqŠjª±ilÙα„~l5€/@•Û?˜Oó²¡èþKö¿+ÞVÜ}“XKÄO Ê?}a[ýRè‚—ÑN㬖Õ'»wìw©ÔFôÙùu‹¡òÓ;\7}h¦&²W´Û@tƒ2^‘ {¯s˜ÅF6tÓµLœ€eŸ9g¾·ChÐLÜs¬ï×2¸ùÜšÔ¦ˆÏOzàÛšTåÊ^|6¸67î… s¿1O Ð}MÆ›Dƒè\ò¨â p¶ß†¨]<ê_€Þ ¢(Õ!‰ûNcÀ SÒ±økJ0ÅDŸþ½.gn¸c½´šÍÜ€pÜqwrv’·ˆõƒÛ¨µd!sâ™;yÈyTÿ_^¦¥6ûO%»'ùA‰Ð,¹å·NbhÙüOÏ}ø(árpÛîIo&òcYŸo˜ÁÒoZÔ­,ÃÆÐòfózˆ!EU!€<È óL¥ÉÑ¡x->¤77â.½OóCk™­gÁò¹°÷Åhé\°µ€Ü»×^#ÓÉÎÓÉøvç‘ûùÉäfâjMaý¤…È[I1<µ{ûÄ­_ð ©@GrÝÁ¾m­aó_þádªÓ!ÛŠÿ´ÌXÚ2„©šSÈŽ{½Ó º¢œª‰g`ö7 žÎåÁ=Ý:8oÿT¢Ç>æŠ2aȬœƒ¬3òü†÷.P‹ñ:Șã½q}Vu %nÁ»¯šTà{37©ŽË :ë“ãBN(¾ü7áZ8;í€Ó¶Jƒ ÉÎ#[#îa÷ÚqöÄê‘# LJ>’ CeXþÉI-ô–ÀPï¶IœùLŒó€²l–K °³ÖÚìàMá–%·±ùVIL´Dë0¨B´QËܑ䆜&á4•^ßtÞfóiiîQö cj<ŒEçïÀ¡ðíôZßC˜Øãϼyº%¬¤aöÔ¤I€mK+cOT¬#U¼‘lª×>Œ”yƸ.u…ÔîÀ÷gáÃT.ênXD{VOÎþ3ðÑèŽÿw?qÈü3QdÆî,XK{:E i¾$a’åh åtê~í«šÌ,ÿν«±mÏ!9~iwù‰OV†1swbh^¬O_Mn»ç¾fß^¸aÇÖàÆ ÕÔvh6¿X ùÜìC†ÌûQfzˆ?N=&2uÓðë‡fæîÍRv4Œ`EÕL<A ÿ Ÿ„Ù p4ƒ‘I[D«>^ÄñSϘ­rbD7Ó#¢y~Ë=PñlŒQ™©ì{8¨äâM·`ía²ýˆ­½i G,oa½ð\ö¹•‘v”k §¡±Í…ËNís^àc¯‹xæåx°LŸO-‘n«ÂÖvozs‡0‰Ë$;/HРcTy%íÄ'/{ ×Qž’cß0ª³ žÔgÝ~ÁrÃ|<íT„ï/(Ð˼1ðYÚ kùXk•\øS”€Ç3 ÝL>Ò»JQLì *¥¶š.ðdZWðþ’Y-DÃj¢µ¯^ß]Hxg݇s‡ðîjc28±„tnÉ81Ê Á A j«@÷žÖ€ŸO6ÑMqÔùíEr)~>=ð dtˆ¦ôk¨ÖùG4*Bû*1òrxu<¥Nù'ê!cj?¤IKoÝÁg'ÉÀÏC4ãâ|2ÍdžþA-ÝAFYLÇnþPzmÍQÆ.Žõ­"¡_ÔRag ññ^.‚ m(åQ!]g²qªïz4Q)ÄÄŸµø8a.{IVˆXåôaNH-”¨ £ü Ë€s_ò0ß¾|Û%¨…5Ögõ14²¾£bd `óg,¾nŒaæÜÁísÑDQ“#/¢ënwôæ‰ÄøT<²~9èH0K7‘pžFrî` ¨‹æZ‘ Õj†{GÒÇ/€å=/ú¥ï+FÝ\Oö{Ⴤ|ÜäÓ„lp*sžÃýy¹#±ìÔ;êŒÇ§²s.Há¶l£$ñ•l5^Æúú£ ˜zÊ£ÓÈ¿9–Ù’!@,†ã÷˜s‚—š1¦nvxèˆ;ÖÌ¡YRˆÇ]*™ú®MÄ‘+JQ-ɧêÓá˜Öfñ}øO+ý°à»Q}!¹'˜RöDÙœŸ.˜šN-Çél›8HŸ*‰ª3•¨ôì¸:"›ãížBõ”mi·]¤dš%zôvƒ[ÅÂTØÒwÑo™LË­l¿ ø­šC¯Ài/&Û|šÔÁ Z÷þäésÁä§Ú LsQ¥|G‰—þKäôm"rßüZiEúó…èÔVE: º–N«>FŸÜ·ŽñYJÇNwÂTÿ \“µ Vw(ЭÎËhV²2kalŽl©#Uh¥ÿ¾Ûšðîí_&ùé<:à¹cš Gù >ΪÁß?³Éô¯ñÃYat(†ËeÕ°øÍîŽiÁ>?üÃ[à…d*«Õ í³bhUÛ7Xq¯NuÖÀ›î¬–€0YTéH$SÈvƒZ½‹!â µäˆÑ9YþŒ_Ä!šú7ì³ ÿŒ%dF®ûyý$OxÜA»†<ÈqDÕsåÅ4x°ÅVÌëM˧x´}ÙêRDÔ¾¦*Â<4}Ï_jÜ¢ƒêéÔ|F•Z£a6ípï þáW •íRøOb;®åPL~5{ÇñÌ.>²+÷8þJ­¢–_ éxôUä™qžÜøšF¯jŒcúgZ" JëÚ“ÊCñtá5¤kÕxèÚËJäaI癢°'Ãiý²,t±¨ü6Ie¹?OŽü: –7R8k'ú@úA£¿=T<ƒÙÁæ“еИt¼W®#ˆ%JÃËÑJr…¤Â‘„,Vílû;5 ­Wí‚%Q|lVñÙ²sUé†Rþˆ‡ÊEHA¢ŸúG‚îñPø¬v›¼ I3—F•󕯣±xÑTLæ‡ÉX@îþj5ƒ3±?XKž8öMøŸB4æ‘|½›à>aD|÷v0?|[aBäœh­ƒ¬â‹¬—ÇhÎßÚL²ÙÖ´Êw˜U¼v€³d^)Óõ¶Ù7ƒûý©d“¨Ü9L?VøÜéŒB‰ß±y­ÏÚdPV^ ÷Æ.cm¼5Ï‚‰jtõ”‹ðHo–ù+@ÙYQbb*Bn×?å¨{x»ß+ÂãŽV.º×݃•ßÑ%OG`•S»ëm+QxÚ~kg’ÿò~—&̯Tx¼ŠÜ”¡aDHù|îä¹Ô"~h+ðÉe¦q‰¥—q½·‚&ùr •B¹wìÿâ“21:[<ˆx¤—¨“§1[©k´ Y[yŽH™x‘"ª!\Æx„º‚õ×*˜wRþówÅC¢ç@­H·ß1‚“GgñÇJl²šË Ã3AXUÒa“Yv^|”…y¢šÄúî LÑÛ ³ á =ÉìþÀÞUÂp&3=çcôÑièœÎÏ´ƒ»spzÁi$¥U˜]v“*Ý Y­ù³£å ²ñ1g6±™zòÏÞB™³ïØ9lsè\Zۊג½í§ñ‚[s+7ù†Ù²“°øæ*¿¯_ŸêÂÍ--Üí‘OðµUÛÏ €ÀôDÜ#¯ÆŽœ»g¯éâzëS»Ø5AcÜ„Î*î¶­aÌÛUÓiaÓÒ¸gëâãÓÐýq:ül±„P>5¬ÚoE¢NÖÐl_Nv8Ì¡"ÿ´ik‘%dÔ*áÔ 7üè¨Kª&Ê¡¹×ÝãßA‡«4I³¾ÊªO\€GÊBdWl«U#DR¦\Bñ1®]ðFº-ßOeîÄ#Ï»¡àɘók5H 5±Ü>E8µÊ’Îz… /œpÈ®ß[뀋éJ&3±«OÄ’µº§ÁtØ€Édß'ÿÄ„†.<úT“”ùnDsG ŒÞaKÓ…¾ãÉ@ ¼xÝ”¤˜MÏçð²/VYÁ/Tª!;z4I¤¥6½ ¥ô~Ð~0É(`Ö¿•§½Jeä.vc’í{¶2Þ,ÔO¡ëX$ý¨@ëR¦4® %{ÇŽ²§¼ÿáÛ!öY‘"võ˜Ò+‹þ€Ý™ïh•û•ÙÄÉaâ¤È?!YÌÙoD–%V­šX±”\¾]ˆå|5Ôoº5”œŠ‹k\©Mçg0k• š/Ì©m­ù©ÒBN=RÇÏç¨X˜Vö$÷Wg±KOo VcÅÐý²ÚÖ-£CÅï0*AçhÑ·^æ}¦8K‡Ñ‹ÛEåݰ;’—vwþbüKhÜVA:ø% þûÌúb…Âçlø-b`ÃR ŠúÇœ(f$n`üÒÏàQŸ–5_™ ¨_˜ï çaxþ\%S±E–]TîLª}ò@Å4 ¶W0æg·±ÑU–¨Cötˆž×E2tXßâRÐ>ŽS~ó!Éz„Z*¹2¢QV–@‚Æ}vt|* KsÄw·- ZËÓ–`ÄÏÚSÞ¨\ößÁ«“m_TW+A¯ÂBÚï(O|ÎgCµM& màº=}ƒV‡>Oò;7rÌq‚‰ ÛÌæ¿›Çéá mpyçjârº¢áO×F˃$ü´§ÚÏl¡ê¹(‘ ßµÈì(Á–=×aÛÕvüX| ã‚È.ú¿˜KTã`u4nPæþ»c ŸûBú!AôzäżߗÂý4±„FT ƒëO:s¡ÿyH›çðÂÐô°ñõg½5_BÕ«j8êþš*œ”#Ÿ&šÙ’ÏÀMš™Ä Çq«ÙÒˆð4”@ªÁšWmZy¦¤ìs=~üjMç®Ä¦ •[6 Vèðà2û^˜êêG.>Ä0/*ÀùáR²ÎX bß¿€ÎÁ/ o±7ý&yK—ÇF`¢ãéx­]z€C’u#ØVÓU¤þy=X0wpû‡*´žæ‰ ?23%'¸ÞÓUháF~ºìÑZ,Ø›ÕazTà‡5øf@«—o…Êóªdú³¿8}÷JrÝnº ÂØÝ*t›õ<æsè-ŒõžFGUÙ-¯>²Õg»°Às½0.N9m ¡[ØŽ1wFÛÛƒdøD0­ë§Ñã—Spß}̵ÃÔò2¼yª˜“_ÚÉ팻Í4û}e >üâÞþf‚ñ®r´cÛ&ðÚ8áæïÐLí ¤ù/Æ —ãlÓÀQ¸·5®XÆ@vó)¼òQ†ìT ™Õ¯áVÐCÜnA:¿hÉèóœ¶7vxûI8žºú^ß×0#9EÂ6šÇ@ÊBøW$B€ÏW„ggA¢·®jÃÑ}n0Uä:¸ì[ΦN\oxþÐvòÚcœ0'>éñ\U«H̲JkØâÝ {­ IP"óhãeÒ´>ˆvü›E¶¥4ÂöÝÆÔq¬OK’÷â•[ΜWuèP&4r}>¦¨jÒ?IŸÙW¼×ÑÌÍ÷HÍ&Ò¥p èk k(¿“HÃ7z1=¥ðÀå :LD‚éZCÌÝ1É36ܽ±.LŠuÊšcdW…©‰šq÷!8hdEoÝ÷d ’€^.CxÆm¹îšt†ãS6CÉ n^œ‹ËÅSÑvð×2@‹€ïŒfp»ò¥'ê7xCµî)ô®tD¹Lzt#ÔSËt4üÀ‡¶æ(Ž­€’ðç«.<“™|?þ>A¬¡Á>ì’¯uºBV'-b>M0°aïM ƒtáv”Í i:ó´2ñÿ)NZ‰6«4æ(ž¡ÿyJk.é@ãµ'Éõïÿà̯{ìåëá¶¾)1žŸ‚Ó_ 6_ärÓÏÂÁØ÷¬LÇ-|uFÈÐxÅG¹z&´ù"§¡x… ‘¬Œ·[òTÐW‚¾]‹Cæ¸ìbrCAõ/tk ©â# ¼ílµûÅ4~UÆ×N?@zÍŽµè6öÜÔyÄ|æ8˜u߆WBå(³g/YÄæ‚ÞðˆÁ“¤iÈD ÑóK,É…œDh÷~Éõx·8JlføYf–ˆ LC3áê†ì¨Xˆü^ Þ)Ã[qiZ÷Ï€nõjÕ;ƒW7Ø@ع+¸ëðlÔ,߈ó¾³í°Cö¦¼S£‚}žøÉ!~òCØ–Xuãòd^"–k*pÝV^x¼Ü•ì\…«n’¾Î6Pø´n}ä8]¨Ã_W¾3ßv{u~;þÎ̦¢!ïÓ9™å1ŒÌ¥ð±ü/û¬ÿ.~¯ƒÏ…uœJ¾ûì¾bØkçSTæâ,ÏéÌ•Ò6|’q€*×fí|Î2VùòÌî÷-¬Áo_|xlÖ ÞƒØø1œ½A• 9 ¡M™ žÿ¶°zÛáhM1SXeDî|ðÅ<ñ xø tœK†Ëõíèù¯Mqoäpû8T©ý.»pœ «AÃNb³^†‘5˜ ÚÀ‹3(ý¾ˆ†â‰Áæó”Ù`¦ÞÇ(ìy Ý#ÒpùÀSvå‡\xt»nØBÈÞulïËÇè-Ͼùap£H’ƒÖ÷y ößS™ÈÒ¹änã56Ùà ÆŸ–¡q×$±Ëüã.óŸMl] àíC5Ô<ÉG.g^åOL³Ž!Éû.FNÇ( µ0rã¨3ˆ‹CÙòAnë!lYr‹Ý~¤>:0߸䷌ñú[£•øŸ&Ùli “û †Ñ¸ØÆÖ…»@‘à.Ø[ ’b** w£éO—9ô͈0þç¤0ó»¢D®6ž.ªC§% Œ@ í aó­¶?IHR;úNLˆ®ù>‰A aÚÊeЬ¡!Ú¤7#+ÑÏëðNq)½Õw‰»?s¨¡1j>±þÓ‹ó‚B˜±x^b^\Ó"¬p¿€†SûÑwÌŠÊJÉãÍŽðUú5súÔ\ÒÅèO1’Éñ¥Ó“;áÕOyÒ¡w›Y•ÿ‘·ûŒo¾È1yùíŠ ¨ÆÕ&)Á!pm,ŽŒ Ç`ª;ì4Ó§/öþþuô’ó|j—skñ4‚^é­é†ØS¾¶$<ÇÍKEÉ‚/!ÿe†¿yÁ äPŸïéTãÎqºm”2ÁÛ­hɱnÖ+ì+»§UÆgš£Åxæ‹óø«É1ú =C«p–ã-ì_~æ«F“‡?G°M¨å§E`ì“$VÃ6þŠð#ûX®M£ÌÖÒ~tßÞ‰Á ÄãÕ)´iÄ>e´>mŒâ¹b4ú±6®sšo;çÒÐ9Ãp&±µÙ×Íð˜?d*8SáKY~ÆV>¶†Û…¥èp£Í€‹&óéÕÈ\R äm2÷! ËtFC1ãv c0 ;îí!i=ÂTO² 'ba‹‚:M|”[¾‹2ê­Ëð·–(Öµ0Çh?˜ÍÉhHœä¾:~Kéå“tŸ&žrÊ…™¦Î¸_Ö‚QÜdNËF‚¸Ïƒú;«[øtúÌ,™…9'e^Á-m¡ð"ú3Ìò5§ÇT_àQדäUE=þ:·˜‘ª¦†ÿôÔì3áÖ¹Ù=°âo>þ­`+z ºœ9ü‚úîÁ;ŸÁņ̃Ä÷c_ç0ù«ä!}é/ÎÅ‹›ÉóÃbd‘q–!$äÇîƒÐfÃŽJ¦[åì–ĤW÷Á R „?â¢e2On!$«eú×¼=1ôͬ@Ëí9 }A›ݶG•äkLù‚xªQ¸’¬¹¤Kx&qÚ–xøLÄ ±Û…–ÐiÞ+É¡Øó+X÷iÍ•øëÀ<²ïZ¬ìS—ÒÅÑ– HO§;gÚ0 ,Ü º·§Ñ=!,9µ„6Êñ`ЃAô7zÃ(©`O—?ñÏ~5S¤‰ôµ‡x9J½êúa0%o8 B𬥪 ”hŽ6SÈéZÕùüYœÉR}.²W€–—$¿¿6ã}5z#!êÇçÐð« TÚžŸÜ߯……üt´¿‘ÕúbJJlà&#Ì^V¹éo7 ïŠ,_·ŽûüÍfXô„Y€s©‹•‘ÿpc²ÆàâýÙñâ9Üi]®[Ÿà•Í'é ‹Xù.>õ_…mŽ¿˜á5ªDÜí5$LùÃ&ŠòáÊ„—0oöCüû<5·æ£µÁÙÿ{)÷%š‘kmóà§¥,´{ÈzÕÓã?AmïZòj¨’]àí%kFôÍN˜5Ž–Äà¹ÙL¨J:û¬¹¤kâÀ[ã8ûêÕUâiø lÃ4ÉÙîÜ֔ɚˆêRŸ?!êî+ØjO:×ËÒ¤—kðÈø;xwí¨ÆZ‘®ƒ¡X÷ê×yÇ1j  OºÕ²{Ø]Ò°ü¶5¬d¹7ß§¸©)ÿx噤—Pý·‚y±{:;e§DÆãÊ–°+܇gEáGÍ?¸\4]¾Ý€3÷L0}WŒµ$YYÎ*•>g—ík`Ïtö°Ã'àìÇm¬[ð‡´ˆMÞvµ:jÊòω€ÐÍIX¾=®À»7#Œz%Ñ’W ‘Ó÷a·®9,z ¼,áÅ­*¸÷M‰Nñoa‰O!69X;þ‚³=|'þ¼þô×Á«Ð,0GñâQtvœ†_Lè’„Œ—¡%“å)IÇ,§:N]é8¦O9H¿R&¦KÏãnl‚:¿`”ë¨Ç99 r. <¢»5Æ‹ºÓÃoðŠÈ*Üsf+ä¼n‡Œ§ŸqM÷®'¿ß~ÄÅ9=Ì©üx¸ç>*{ s<…Ø‚šÒ§}E÷ Q‡­è¾Ež¾œëL›„4è³ÍTêô}vç²T8XµžJ}d²¬np›?ï$8_ÎCtÌLt"YÔf^üH„ü~Ñç¯Í¡9ï­ Èôs~º8Egú×q R§·Ìæ´ì Üø¶†g¾c÷^½Îþ- A^sñý‰8ÐÉIuÇ*æ|ÓN\¼×Ö³_?²"~­Œ«½?üOc¢×Vƒ÷‹ fÀõZ·}Åžg‚¬é¹bÖä¡àd-®Dò—0µæÇ¼q}ì’Å3P ï%*áœã6òr}'T5kóGœ~J<ˆfâ­ø¡F‰Q2C·¤HúÇ:œÂIÊîéd%ð³Ç\¤È¾»é‡Õ¹ØüAŠÎnÕmy¬Îô“Àã.HÄ hÛ‡hѶâúˆ«¹ ü”§i>r<¨Ú¿ax R;“Àgc7·*ø7î.?rõƒ0üöFUÚж—ÉdG”õã.£»w/#{ºR¨¡Y]ðâ$1P¥‡ê§Ð¿‰Þ0šã…ù6 ô¾4a}Í&–—VQÁ/É$¿ö)¾‚›D/@½H1&íº¾âÉôʦË,ŒžhðJ‰žSXI%ORysuI»ÄíÐ5 F‡)=cq Ö™NÁ-o\鯹ôBz84þ‹}rP)~\)Ó&¯gøÓe§Ù³~³mkññÃLÖþD,“«õ ú³Ô@?¤›¶÷ð °°¡UœH™ZçE4ušÍFöسõ±ÃБâ†Kuõ ÏÁ·w¸åMÚÍq\±¾ô8C‰h&»ö0¿ÕÀŽÎ}Ì2-ê}?~rƒÑæ‡È;ƒß-oBñî°OI¥?úÓÙ‘«c,¯Nko* /3S¸cAz$Cô&ÿˆd#m^£ýÅ~f²>FU|3X÷›pþl:×’qÏz‚›¢!v‹'­eµ¹’åCééšÚTù'6¸áÞ_El†  Xˆ{ÂæG–tw:Cn_CMGbe2“¾x IdŒçmSé|„¶U†X[ëKŽä#UÄø§ŠãˆM¤´ïuBôi:Å&”¯HçŒ ‘óŽÉŒ_—Ž’cŒGÒ^<å3€?ï§@ÿ=˜/°îM?Ëß@ȘáˆVÉ_Qœ/ˆŽ«>CÓ®»¨¨Ý gLÁ›î3é1 ì01¤öCnìúéׇÎÆP5’¹ú"γÜÇòÍ~bu86…«J.àÓÓæ4ySäŸX†ÛäA.¹ ®Ô ^ûp‹ñ%úF?®tÃO+òe9+§¡éMV2ÖŒ9ü÷¯!,3ÚŠ²g¢‰Íï00æ3/ëÒV×iU`Øn}T¾w‹Ÿ½N+?Ù¤[ ‡OìfîNöÁ =6l×´9Ðñ/ ýZ€múÛa„Ènoó~ãOL™N/< íü3è‰Ô@à¼Ò¤ÃRD$øóg=?YójB‘Ó.Š“\¢²×^ˆÒȈeœÑåZLë§Xˆ*þ‡ÔZØ«•6wÇÊØ0÷(,üƒÕ7NVb8ê¿íÇá[˜Â¬Xlå÷£îÝàcܸ~Xøvyï%S3R9Ï_a¬ÛeØ/àLºOcátô›B»L@µGŽº'o?ýÃfOgÚ¼Ò‹êêËÃÞë8¬/GgíØ ÇµBQëÞJ:ÃG7ëúSR5|9¤GvÔ]ĵÓ^ÃÕ¾èX Möä·åe8ñ³ æ.1 21‹Ï&ú\|WëœËûzvL{ Ñä\ÃÜÃëȨô1²êÏ|:ÛÎ>žR£§7_Örg’3ÖÂa—™ŽrC²,Ô†| ¬'C…½°xÕ9Fp¶ÛìªÿŸþ"“Oâì±à|<Ÿv _oðÿuÃ…{„é;¥TóJ.$—/$+œ×’ñIžÚcOÒçu±M¼éõ‘*Éôé\ÎIòì.«ØlN÷jÉS“‰ëì¼²Ýð1¦3ž1õ§˜õ'`‘¡CzEgÓ¥ÇÎÀÅi…¸æ×bXà'?#fK¸,ð}K@Ϩn4xhÉdyw£L0½Âùpz[޼>Ã^>› Ëë¼9E5¢$uº<è[X2&YEx¤Å†\Ò[Âè±™åüMȽG|NŠÓ+-¬»v$="( »”ÜȺâFtmna•em°k Þ<:Î…Ó‘DSöÎîí$B#™ßËHÕsß²!s™Ì«9ÆXåÎdº7p¡pÆAü)¶µváÌþ›˜µÝ~ˆÓ†¡ðÒ×È×õ9§îÃðùV¸e+½8õ 3•KÈî=ÓÑÍ®^io¢1©LŸc qy2‹:>ÿ #©ê˜¾ê#»¸v:Õ´¢ö‡‹09¾vò½>NO,ÏòóÐU¬NnQ>\ôûö¤KÒÇQ9ó«ÕÉÙ[šÌ*ÎC4¸s‚ÉiIÇ wýœ­‚@¬„Ÿ¸\3“•ôl¸$q]¿–Ñx´³ÊkÈó frîìFÉtGvéâJücÆ s5ÞƒBþ nC^GÇ¹Ö Iiñ0“ÏHl,±Ñe%>¨ÃÐŒ^&³qëÿ×B:câk -©Èsî8¬}•!Ö0÷v*s7D˜V¤u2±ó4™Rù™ðQ¤˜õÜúD¾O!Î07–{_ä¾ú)A7—𢣲%6[:³¤æóD‹Ÿ†~Ľs¢´‘=8.afg\Bsm_hÈ‚ ¸D&wi-ÜæY|B! ¶Põçt²›µÒ÷•+p¬¨çžñÁEÇ’Ù# >¡VþiZ&‘Ù¥µìöÍ5lï³dìZ$A4Ìç“Û½áýÍ›°Î(|Þ|ššnoÁY¡ÃpõÀ ôûRŽÆ ßaúÜ¿Œá¸±k0! º HO=ó@îr˜ÃÈÓó¿ p_ª,- à%B‚y¬_8²çZ`ËÄY|–«HË>8Ñ߯ðÆ>C8{Dežƒsì=6|Û_¶©qx|“ÁÍË ›DÈ÷ÙbDušý8²[ r¡kt+š;¤“‘Ý%KvTA©< ÷x•é?AÄ}@ÎÌa[ìäáéo ¢µ\»!¸nªÎ)’£n—ÑËCµá}gHC‹úGtþ)ˆË}­ÖAöÉ/Q"Ruöls%mAÈ-%g<âàOOsøƒ]°Ùdnªƒ0®6“&¸éÓíÙËÈÃxu"´X†ž(ßA÷l—¦*3öq®,A¹ËZäýø}4•¦Ïd´‰ÌðO±Æv¦ßuÖùÐ2¼Í?‡-VìÀïÊ@Q.žyøŸìtû j+ñX†[ÞoZ?a¥Â&é!{Púlü ùI5œËøÉ™ÓžäJªÛ²K€éÕ³CKÉÁÕ[ÈCëο­ÝšöÛ¨øÁË 0ä%N´|~Sìg ¦×Nûšý¤â½©É» q/ti~m5½ŸE}âÝàpúvê&žFó¾ÜÇ[ƒÆÄC4þ©p eå{|lûT¨šƒE¬1}ŸL{õÈ6»³ðŸ¦¹Ë`Îk$ïŠѸÇñùÄ¡ºíñp%I•>ù}†Ö…i×FصЌ¼Ž ¥†ÃÈû<]X—Ð ÂiÝ–ùÔß—%3Ó ò‹)ItÑù°¦L úÍ ˆÚ×'œ/k‰€†%–ú`ЖW¤ÿi£­Š¯Ðñ[U”)'ý1‚°VÇžÙ¿ô¶¦CÉ¥aý;no"ö½ÿÓð|9EOå妘޾éóÂ)$æ_&½ã‰BR¢t½†5އm³¿2ª“éjS<ñ8Œ,Ô!–?”©Â˜<ˆwj’–3z~¬l3CánK"k5ØÒB8jò¼!vÉ(|9ŠIʈ™ÆiìmÿÕ°^f,<›…õÙèõK馳ËH¡]srîkt̾GTÚF3?˜¤Çd2öé²dµ 7ö}Ñ"-K›Pʧj2¾˜]›·žªð ÁôÅf0¹b%jA>(‘]_§31g²s¦]D‘¼9ô@Ó9¼]WŒkþX`ãÍ@"eÜÈLêÂM23ɪÀ)äò}%›¼éEÜÄ Zµ_‘T,¸ŽG‚‹Ñp¸…MÒ¿ %x…™–Q š>ãã[#™«A—lŠ!5B4:¤Œã}Õ˜Æy>Î0Û÷è;™Ÿ}òj Uý~Šþ:÷×Záz™¹ðð_1ܨ»G·b9ÝàÄÚ“ÁI{&òµöcbBó‡šB6ç=î½LÔî>a|ý×±Ó¼àaêQÒ=ÇyCÊ’á¿[Ù¤)T8Å„~8oO‡oRƒ5ê4®KlS\HÞ'+ ™ ó¼´`är&s|Ïlx·þ6Ùû8§Ä,f+tObA/Õ°o£€uh·Ì8ÂXß­Â#A'Áyo6)-Ç-¶Nþ¼z-K ¡µf8÷Sì7¦áG>ü­ÆÒ¨7ìYGOô<¿ÂïÀ†îøìÀ65‚lûiœ¹\”þ˜~˜ŒæÖ³±'tÉ…:¸{ñv6Ñx Y¶G’šjR4ŒÄäׯp)} ¦·Åð`µ 1a’·Á fµ~d}vN ‘²uM¬…Öó2Èú袛çºÇ1ñV1çÚPè?MjŸtã‹ðtXÈwªDOC…®sH" GN£ôl&X1jî bmùzñÄÊßœ÷ƒ¡ÀHˆ–F,¢#·éÃØÕÔÿa £=ï"óRò,,º/Aç›ã=ÁVÌΉâjåðP[ rä¦#°Ô\>~„2ít²ôg%ûŸt%à ‰Ûl~Ÿâ*itã缾qõ¨!‡X™›™$âóF"6•zÊËÓîm¸%³LwÓë0‹ªý ‡*Iñ/á®öK¬)â!Zalß½k qÙÇüiûö’gõŒ>Ȱ¥“µÀ×/‘”£3Ù3yóà´ÞY¬ÐÒÇ;ód W¹útžB€]99åWLxæ÷&«H–˜µÓòê[Ê8œøÂXÍÊ£úÙI(ñì4ñX<ÄiÖL†µ©Ø:’Æå¹-ºP׫‡å`•MH‚”–x‹ÑEÈuùX²íù¼Æ$²/Ý2-àpL5£7Í>©”#׺%!kÿoÐËá2ù[¦Ð€ñi |o¡êdY•!Ù¾¯Ä\¯àù¶8bl–Bžä«S'¯Ç¨¶L‰*ÿ(G£Õð½M†^ù´–H˜db¢ÐqrQv1t ½‘t%¢í·‚ ©R&+@¯á" ÕéðBSŒ,:ØBž/¶¦óŽo£lX›ØnìI¸{Ë—èË¿di®gu,¢±ßØGjQxêÓ'HË)Â%¶¢øyÀÖ>7„»ÓÈãÎQÙ ëœW n£)³1[•Š}è†Õ[oƒþÛ›l•¶‰ùZQßåA3öNÌVÝŽ*PÝ©@îªüåæ4c–«ÝÒåɶ.ýÌ|éŠ&­‹M©ï=ÎØòlĉù’$@‡”ÐN÷‹õêú8Cÿ´ ïåå4„)`¢*r€'A÷`©˜(GFrM½ QÅöc£XÃñ«|1~Þ°‹¿ ©K©º™"š¼hÅ%| r*¬WVq˜&xßxyS‘lŸMžª4õ¤'ÑßBfí Þã’tWÉE,Ѐ-®\ 1A:²ú4xþYŽ´2s ˆ«ËîG±/áù& ;'O¤É^{‚TÝá{}qR­ÑKñtØ0ŸjdΧC'ü¨³ï Îpˆñp8Fþì,¦:ŽS‰£“ yhäJ-~%‹šÑJzt‰ÓZ’¨áµ­ø>›·“ñk'ÉÀ‰Ýd­ª5íkÈÃÚšm¤ÅÎAêP EeÓq¥Å.ò·¨¹ûÓkaáDÑ7 î/H Ó½ÏÐ÷zo`Õ_FýÀqF±*Ä·É^n³г È—¾©L¼ÙE<ç2ÊT•°ûj±œ£Ë:Ù¤‰ð+jfÛ376pî8óMŒŸæä¿fï|f¾)³iŠX<ÀKK@Á@x Á”&Ib±Fˆ~žöËfs!”&i¯ O_k®¨Gô’OB¼Ì>æöóNô¹T ŽmKÈ Èžn¾„+†þ±:Ï ÈÔ-’t꾓èø^„9va#7Ä18qûãRXº}Ø*°ÿºþƒ\‘˨³>‰½*I•Ùxâ-áHž¯šB ’3Àçm ÜWY‰± JñÒb~Òl}Ïç„@úŽ Ø`w—©Ù¯D÷þÚ ¯Z ‘N„³™‰í¬Ü%^²öEóVv™.ž«9‘†²©¤ýY »Án)²—Œq³’þÖ S*ü§‹æ¼5¸ç²–Y»V‘úÏjD¸t4›ðÁž#•XðR þþùË(¥0íÂíx~oý½b#S¿ Ÿ«™À)a2c­$–—BÙï« ÿDÉëû„KÈ ñ4jýU²Ók/»µ:½²ØÂÏ/¡Ò®–ݧÁLߌÁèðÖlôlý‘1¼òòšOÃæ4Söa99¾Ø5m"É!AY2ãËg&¦Vlëcí½ê¹J/ÖõB<’fáú„wCC–”!¹,~Ͼ½/-kfþÔsÐÿ™ËÍŽO…ÃFç€|ꆺ¡è2:›Ì“ÅžÜ:Ø'ñÞÜu¡›Ô °rÎ\t7‚ž¿J_»r(­_‡ç¶QËKè_bI<‚ñDÆgü®ÿF›àÌì8öÝ©õ0+¢”kbšì&mºÅú©_¯B=FÆ`F€7 ׌_”¡õ• ½è)È^ÞäŘ8°kéRöRo5Èì±óH ¬ø·œˆªsHYß "gr •?Ob@æd«»‘ Édj%ó#ï. [ ð¬ÝQâ01ç_©áð/{™-è'Å·6ÃkWã‚@S¸|:¤wžb/Ÿ#+£%éy¾%øå?nMùƒÜ.[ƒpÓ5\îmŠÖ\? _E™òŸáJúmFzq.˜_T5a—{Ó®ž<Ô«‰ ²Ñ/™®ìß ô ‚ù¶±˜hºüçnøÌÛŽù S¸Ïv ßhšXWS¡ mܾÁ’Ú9ì«°×çî¨eO~æÐ¦íºdi®ÙøJ‚ªnøÀ4Û´pOÂaÖ!e„ŽÂ>çµh:XÍÜžºŽ®åC×Ypçê}˜x¢IRˆŸpB·¶á›c4fi{Üî^CN|2³`Û"Mº,Zœèë %A³m`7¯ÞC›»Be²ýeyö}=.q[¦bpC£»MŸ | ƒäQh\Ç‹‘–0rÇ{ÿF’9rPgÇ:èúµ ;ñ}ÅNzçNlßûœÙ}±ù[+hMVܵçenÓMtLjµ]Kħ @õ‰JÜbÀ½±g Ø5iÐ7F>T­.oÕÅsÒp‡öp÷!ôÍYÊü8ÞÁ¤îR$U/Q¬4Œö ¥sS'p¢>Ï„qÞ9û §GkÐ×ãU 9’Œ1ÆÈ=>Š_ü³1bÁ245]OáÖý—x¨[„šÿ†Ë¥s ¯J”xùÌ!é6$v®0}+²’Éã‰!tt5JïÝD®²e ¢$½€ÂÞmi°G2 vÿö€\kcÓÌ„ù+Ç sK6¾Oއ°hæçœå¬õöiDü®!Öû9Áº9pVö<ûôzÌH4hɉô·œ6‚ c'@‹-àÈù…'®ÒpÓCXî7p†‘ÞßƒŽ»w£†Ô1IŠÆEžO™Æ9j!ÍG6¯ÙIó9víîdó‘DZU–~Ò ¤îV^ÜuC߸«+¿âöÅ)ø ºŽ‚À1ÔÆêe„˜M§¥Ó¶ÂÇœpÆa6½ýZ ÿÈ.fšœ¦KòUi¢J:Æ‹˜Òš6á8 Ú­?BNäM» ©” ÖÖ%3;˜g§æÑþsа2Y. 6{tèÚª$¼wq+Ta„SµÞbîÆ8¼.{ blËÁ.|5ô‡Ü„½Ì—_U˜òae†MÞüVQöî³?0|QfZ†Ð5Õ‰Pü»XÉ=à÷ã¹ø §ïEfºüAã/.tÃJ^Ê|ÞǦ©± íz…Í^¹+²óá¶óM«Òè^«DË×z’eó/âÏ%åì'±Ù(¬©¢éËçàoS¿P‹lçdÑ59š ™‚›òت“ÓÈÅØ(Ö ®ˆÝôÈu›1 Eå ¸d~6ÖJ*£¾‘ þPòGÛ‰çÌ¡éæ¤m¬œq^N÷TpÕ}œó$3®q»Í+mÔ}jpBç%Nćqÿó\öh[¿ÂåIàz1ôù á×2{&y†eãN”S O gÓ UaàJßÅ·K pv ¾úûã8o[< F#õrÎpµ!œ-C‡´m3…ø¼S!õ»Nk‚ƒZ ÐX"‹žÛ§Bð¦0Ø&MQhU·¸€ÅUwQ§’–U±pÔ¢Ÿ>ä†óô˜OölȃV¼]×Ƕ¸¡xó,8Òà KœAç&¬"E‚0툳e°rW2;ó©8-ÓP‡„ö06ïá˜þD‘øøû÷ Pë¢n‘ïAíY^7v¤ó§Úà†gùÆ ¨íÄN½ÿv=‡?±úpaÿ4 ŠŒ‚H(³Ðë![›¿„j°öx^SŸÓR§N”£ö3ÇD3²û_ÃÓVÌÅטƒ=âÄé‚vä7¬º^ˆ#¹º¸>iE¤rA"Kœî wdbº˜°}Ï 6ÁÞ|䢩«7y¶´*WÿÅÊ’\¶ùC¸Ÿz‰C³^âÊ0khñ¼Žß…äéá“Bð½cùé~…ì¥Ná-øøŠ3¾ò¬gšÞ¯(µô»OnŠØ‚fÏ­¸ío@Ž™ì5å³pÑ«nìL-ÁºÑ×hy#B$¨TÖ(š,1ÝsÄaU¯)øÿöe¾«ÂëÞÎdÿ='Ô°èψ FDäÎÿ„O<º1–ý{ë/‹syÉý 6Ø¡¢uK6Óù&×AñÍ\¬±¸¦ºvðÔâÛuñ¬œ2ÍúÀVRw."ze©œõÓ\êIÍ]Þâ³?ÙLéSyÂÞýk_CÛíîDj¯轎Ç>I3ú5‚?<[C%ËöLrÃù$}G ØÞÛƒ?Nþ…åøˆŽÜ\ìêå¥]ŸÞ²WëÅHZp1¾ü9…æÚ}ÇÛSo1ö©0Á÷Û‡~£Ö¯Vf“¢=Æ~ bwW£z¡™Ìf¬|ú˜m_‘vâ€è|HílÀq…sÌØÛ댮Ÿ»ùy<¶vs색{e\ú$?á‹ì¸ÂtèÞZÏîʽó×b‘^»[jˆÝr¦‡¹ÿF†¬Ÿ(ÅQÁ©Ì}—­¬bt=,ðnf>:Oà¦UÑjXI“V§âF­¼I,6 ÿŒù))²â—±×+×ýŒƒò'ññ+.ëû¹yã2Ä´¯ÑbMlj1þš}ý¯ ŒÓM±îÀÎBž,xœ”‰q‚‚¤w—ÍN¹€þvøãE-ÎKŽÀ· aÖ!”ØÌCß?Á¼9èTFÄN\pŠóç| ûúš U£:Ï¡µ¥#ã=z ·D=aT7¹ÀªÔ|8}^½EvúyîLIaòÖ- §\ŒÅªg}°hIÏ<•ò îÎmâègï±Ù,ô n:‘_]á@X ^õG‘z„?{þzùJœåòõe®¢g¬)YJdÓ¥mpÿŽ Y%öî|†¤“FTo› Z®ÝÛJëmsPSµ’Y„¡ðLCÚ$ÒØk¯Á/qÜ0/{íhþÖ!VôNÏG4›_õ1£X‚64£cF?{êãSœ©Šs;•aɨ:­ø½Äç57ˆ,S¥9ž•Œþ.&ÎË„ÞïxëV8%SëÉ™0Tm>†Tm_%­Íq¡ 8¡XŒÇÏ%ÒÃvöºëÀa ‚U§a»B"UÉt¥R7@uµ,Ý –ðÖsÎPYZqΆVýœ&K¯á½’¶ñG{éZIν-3 |†;;#{‘+#mrGY§«¥ÌŠ4µ‹`>ø/§*QÞÔUÑ™D¬'5+šÙµRuö]XËoÇmú‡Ø­+£ä°0؆r¿Æ‡°°åÑL×"[tÞÂ[ƒÝ&‘tÇë¦ÚÑÍ–¼tÚ¬N¸QŠ«–n O¢dÊ3ϪE·œi°È·‰Ø1dƶEdꀃ[ž2ÕóºÙ†–—x$ø lwãДiÛáÒæØöEžÍä¡ÒΠzz5„Ý­g“Z"ñSW6ìJĦ;Ýp6Þ—æœ|Ä hö£Ëò-èI‡rP9<Èœ0Ïc¸JßQh»'äo¬ÇÁŸiÓ‚"v©Ç:­ÿÓ`³Ô¶“+fº„è‰Cš]*¨é6sµ=üpAùQ,/ó1·ýÐ×Î1;Õ…¼ü1K ­XÞârFö¦4-Ùçæ•àGŸ <5ç:ú7WìaÚÚT é}.ÎÏ0ÚxÍ@ßg¥ã_÷A4[ì^EÚTÜiŒ]c þ~¹ìu±Y¬ùž7(ôü)ó½L‰®Éä!ë|§Ñ«÷ÄQ\æ0ˆÎϧm™% k¹ÿX²fŸÃ›Tæä=~x«#ôfÒuOb!ø3:‰}’?Mh--KμÀèA¼÷µ š…JðÐ7~ö„•Iƒ¨h)AËωàþ†*\38É«MC‚€+ˆå"ì*œNm]fQSå7¨04 ®Ö`ºó.ì^—Ëlõ}€—U:Ù¹Í5°ärÎÌò‡j‡“̪å‡HãÅÏøa½Šf6°Ÿà—òôט=ìC®«ñýçýÌD,GÓÈiä·ù\2CGˆ.\‰‘S7ã¢UKÐUÓ˜´|R¦ºGçCôm^¼²\œl8c¶P|ÙŠ{÷ ¸IebHÖ[ÛvŠäó1ÞŸŸ=éο˜ŽÁÒ¾û(ý/EJVíÅÖô¨.5…Ü¿ñ†GÛ07ñ'¬P¸Ë䬾™ßfÓÖÏÊ˜Üøoì¾ û‡õÉÄÓ4®²¦ËfêÀË÷¹Œ‘v)É–¼ cÏõ©ˆt44\µyåNÔÙ«I^Ü"_¿q!±ö%ÖÔìayCi¾Ò4¿v×1‹Ž|‡¶ƒÏñÍÇX;ã2¯U¡l“S6ЋgzRoÕïØk°™Ó3aü|@údÈrþä¯|Á¦‹¸!w|Wƒ§‰]sWŒ±9=…tÃb”ÐЧúÖAv¤šÿ d¿6üÙþxWÅdMâ·¿`£ì~âù: æ/Ç—ôë¶š2i.Ìì ÃpH{y•ÍôFx^^…Ð[£ÀÕ«á—êDÁÄ–z=‚ùÃ:ôó–[°d¿)äßJdG+«@âð?Ð:`L¢­Úàd¯aø9FÎïŸÎ4ñðÑÕ \9}/&Cæ9‹½7$1Ã_ˆV½y)ò0.çOŸ*„c§ùteµiå¥&×PDY- ?$¬‰Fc(S´O˜È6©a£"i‹½À¬ ÆT®ï:³;Ð’=ξÂýWÙ´§Þ0ÿÞqûÞM,t6ãV«9dÏ,#úÚL”Méé oµe+@êô}¸«\nº ¸µÓš\ S³-å<Ôû{.m|Þ2Éi gMˆM›:‰NëÅÅ'Q59ßkò€ ܶЄ;ô³“yèk-Z2Éžz†JÒ3YݰÑx'#í•ÅÌѧñf—¡ô‘*Ù!¯Ï^׬g±G¹ZTE7î·Ãàž8OE”$ô¼ͳÖpih*´‹ã)šØz*«»~€M‰ƒ BaÈ8#Æ\ƒö½z˜°2óòAÄž%ÌQÃ{@-ãmÆU_ÃHòŒÚ¸îZ˳[7•ØÔÌÙL¥BÞàöh£æ}qòxƼ¸ÅÌœmQ>=Š]³g%<ØfMnåÅuç"h¿ /l šMª#„XƒæX2«yê§Á­¦T¼õ<80ŽOžLiúBýþ{B;;˜¶îì^µ›(.<‡O4¾±¡º´£×„‹µ½3¶Êò"2+e¶òù`Û§»LìJ„kÿp›•Ð’Â"¨Xý’ÙÁí€ñÕ± m'í¶Ä:õ ¼ ¡-#áìAËApHu¥Éš›pDÞŽn²¸Ñi´·ì‚‘âT ŠyÁ•Mx†;èz¨1±½ ”ÔÄÊ(og:E£¨âÌ5¸ÂÅÛâÛ`®‹õš€¿Ÿ=àÝ$6i!(s^o}ÃyÓ½,uâÇ\¡P2My Î~wˆ› ›_³öO 8uò0稈½–çn˜)ά)7„ñýØf"NÍÌÁ—lóH$ôŽ|{ÿ娻Ê|ÿ®†µлZ»NÀzi˜yÙŒ„ÌbæM»ŽMk!<^š.¹åI^.½|¦ÁPKXð:xS›)F5tãØ­»ŠÐ5þ*;è$L ö63gG3qûÜ¿P+(ñŸ/)ˆ/Ü_ …‰ù‚¥¬Uá?¨=0ÿŠ`Öi´«¿désö²°ò¼]J«íŒ)û>Z²˜‘*5HæèÓ­@ø®©“>UæÆ‹ßìi“-DWøUßs b‰.=qádqUH‡¸&Õpºƒf=Š ˜8Ž!F4jÍCXûÆžèÍœ‚sž GèЦ•¸Ìù^º?€‹:bþÁh†÷èI&:G +Sãè…±Kdàž9=z|9Ö=—‚ŸÎ²´4J•®sÅMS¬IÜ®ô`nî¹¾ö¿ØDs]áRx8–÷'|!éPW×ÅÔ5^#Y³šñÅZçšLã{÷üÃ^(~]”póÂçÝôÏÁd›àµèj6ŠßŒ”¶mœÆùâ¶‘g§½yÿ¸e[æREaªÌ«B¸Ó•ˆ‰f2ÝÖ7›¬á”`D¸ ûÚ\:ôF £@ဩ.ÂÆ©lhVî_#O?ls%5ãŸpø Áƒ¾p£Ô šr ªÎ€õk'6U çGÏœ¥ !AÓQ.ã ¶J’¼‡§A6ˆ|«iäÀRò±gô˜iã…ãÓðnÈGÜ(õ˜Qú³oÁh×_ì„S8ÄÜä!'ÎÃ_~à^,„óò.tÃÚYDRß›ˆåÖ@s*,Ä£e°ãÅð”‘Bµ>yˆ+kÄm=zôWÂÎ*ó7œÍyX7§©~‡› ÍN5dvv)·“SI@ê8ºè1”ó9‡-ZØ}c ]YŒÓ–&²Aÿ iûaAD1M¶ÒöŠÖîcWX2U5é²ôVûŠåy~›u½_Š<ßÙsóøØþ'u8õóâÞ ³ôÖ|Žj 2±sÙ«üÎ0sB)c6ãê÷Y¬’eg¥0}Õ ×.Y%óðÍf>nQ¯áç(ro¼Ïº„:Šqâ/šZZAÂê´p~܉S¢ ‘hÙ’ h)FuÛÂØž‘8È{ÖÅmï~³êV%xÌû/dôF3/a=¼î“cõ‚x)ó^—*ÑdÎ\AñÇ»½øLg)ò8Ê£¦ =÷u!>ïÎf¦U:°q~§°EÜ ·ˆ*Ò}gá`¸-f¾þÇœ¬S†_8´oè»ö‚$a·q5&ùÖÇG1ð=w˜Ùg4…„8ï¢äcRDÊdÞ‡[mLï‘XæÀ›øAËÎMl&+-ü˜õ¬4Ì:ùŒ##â¥? ‚÷\ªÙÔò/”é;‹ëÌa§,ë-Æ„o]2^ë(¶±µ¡…wV‚a•ýÏ“ùê“K`sg]ÌÝî]Ì&K[¿¸9¯ 1Â; _ŠK‘aN&j•ÍÅÕŠs©ÏŠÍìÅŽåÐ8Ò€*­ÇáLâ4ÎFµ ö?ÿj™ÊA|>õ úY)²gb™¸-!ÀŸ²>’gH—RŒ.ײùÆ(á6í©$dgó}ëj8ydá=z ÚÚe¡ßZ$¨ÞÁðe—`¯M·¦áIqq¥ÿéˆSo_e‡Ör ‡¬àÔå•`õPö_I¦]½¸Ëu©Ð3BÃe83¤ýIzà.xþä<øµäAAeæêw3ö)ûèâUÓ™Êí™ÌÎ.µ ´òˆ!Üפ‚XßkøÍËOö¿~A.tA‡†á§]§Öa¾íXi»æÞ2…ÐUšM‹ ³ò$,ÜâT!ON—þ ߀û,äIà™{0[=”ŽJ¦cã÷H:M®?ºÌ”¦k~70·`¸t],AW8®}ac²¢Ež>žÛ‹ŸSeG¢tQ ûwÂçi!Ø×`ƒ¿2ØÔŸ÷™?ÿšoÞ@PÜ’vWÐÍâföÐyX¡ :Jqì #ÕˆÒ½Oã`†¦ÈùâÌÆ‹¨åi—»CÐë’ìudeŨ‚t2ľä%Z‰ðQs)û|Þ<´W¾èÍàÆ žÇ©ðáž2õ‘\KÆÒ¾0#c88~#²[áû¢Hˆ˜¥HBl¾±¤|,僄îE8 ÿiž%_>„±öEÌÔ3ìեɬ Ž6u–í`Mdé¶´…DQ¾ª&ÜaÑQÊxІ÷ªXnò w :ö°‹~ú°'ʾ³“ÊQU6J´%i€Ëi꥟È$ØPé?ì¬åÐ;e[™ …Rìu‚à`Fý6Í£Çmâ \õ®Ø Y ÀTùíyÐŬ×|†KÚÙ[õ ͹A6¥¢“×]ny|"x˾ƒöÇ ôÕna*_$J^û𢃠nØ`g¹€œ<%FvÏ Ög V&’f.·1ûr|½ú¼k¾üŸ/5ùÞ6hTƒAG8±’ûÎD)ˆá]ûÚuÞ˜l´@gÏëØ{ʆ^tö 'EÛà EÁ´|f¿oz°Ý«ü&qo>v-é¡B|rq-¼)cô_´Ó° w]¢ &Џ‡çÓ!À…!н{ aÿ)=n|ÒqFç®Ô ÷1•6±Ç&.áw£}xó„ÞÉ ´}—õto£¨ann­(‘ÛØ»Rxèž­âôóðvþòõì‰sMœñœ0¼­žÜ`tŽn{tÉ‹éÄ'£gÍL¡çÐÕ ªŒ–»2­‘“§·ynqª›îášsc¸R ¿ìR&x1¾ü›€¹3øámÝVœ˜µM 1Jz¿]! –=€FÑ™D`Ë ÈÝÅ .ìþO_ ™Öé {«+?áºè›¨£±Œ˜©®íf¤)‹©QØ9b¬+L6  ]²jÌZû޽’µýt¡ï^fHSšÜãw·qsR#šµöôØ‘Vf¶^2o§P‡êèPÖŽoøB8Ù¥f¨å®eém»)´Ió28ÖtP¡/<ô?ŸçåÆt½Õìǽ¡•\%ÈÞÔo>v`Ý,êfÜœËKÈ&¢JtvWà`û8Û÷¹.¨¬Áÿ ÚãÌL¥Æï<è#ÅIÀˆ¸l7,Ô|Æ0¢iåfƒYãÌgþ™ 1îÉÍ{È ¥·¾@ÖG nWj¶xšÓ€zØ`ÿQ5œEãÉ ¬û—Ï(o÷c8ª =–å[\Pùíbjl|€óp¦÷Q¦½¾&iÑ+#?A2wòyªÒÃõ’ôßãHX£„j_á;:pßCGº>F?׬BYTOÙÏJ'‰|Q ~ý®ÍV£OXºpŒiBZ²«ØK½J0sï8ã“ TJ–´~¸‚DZ¿”'‚ÊŒáW±L•àß! ˆÚeÊèè˜cQkÚÄÌe~ÏP¡‹ïÖ°¾ÎrôÈ>-ê«çÀfíôƒ«¯üÑpì6„Þ!weIñÑJÚ-"MbdméêØÞœ~UæôÚÏ# ÿC_*q¬…²M*øgn¹ûd·û¾Âj¶$ö8BÊÜaXXŽpcS‰[‡ ýìs—ìjÑ ë¶l¥Õ8qü³èü‰-4_§‹+§Uˆ1Ϙ,;I:“ ^x3vd®ØzRî°™š__Gª*:ÐÊRœáœ›M§™èSzŒì¼îD;DvaòP¼¸€ÚÖÖÄ®²ìý_Mαerh'¹Õ³ãòÿŽ8¾"™QoàD\2¢k¼ÞàÌ{³éaå`j_É•ÉÙÁ>‘“¤«(y|þ">ucöhLìÍg`ùÅô(ï;|¹ ‘Õ—8ÛÓ¶™XCŠd¨ÇÒsÄdµ<}íü¬J½‰ê»N€€:ÊÚQo):ØÂO †Òì’@zÝz!ñÈõÃã‹M•é0² Í:2•ö}W"q…ú´v°˜ÙÊÏÆ(ì‡=jAî3}9‰ù”»ª Ö0ä0Ì_Å¡©®ºT¸ÒÉ£ÇL¯žÏ¥1¼O‚V¿O@ùHAR¨Ë×zMžÅ†.¦¬ça6â}—'!Í·T3j?Zp””ã•,ø-‰-)`  '¯¶¦IDe«’i‰YпüÃï—Ë|¿ <¯cq0ÊÜŸ3ð_,[Ýovþdͳ"ëGבÂÂäš-/ÄÉ AA»Ñ|<á=ÐW 3ƒÑ÷^dÏç*¬g̨"çljž`_GV"Ìu¡ïïÛ ÖU\øÕ„Dô¤M¶¾“\c5òûiâï¼f¨Rœ*ýz¬aw”ëA¬â zö¬Õ{½ã VRÆU”U”ÀO5Úô÷;’úË€Ý` ž&æÄ³ïFf;ãÙéGèçsÛØ»ZbZ•CŒNpÈùÄèHZRȘ>>cʼt/w?Öö’Si‘ôÅðQUòo/¥[F2ª>Ö=›‡®­ÙI3ìÑÖŠV¢â^C’L£ØcâÝýéÚ5*ôïºæèáâ=S”6xŠÓî23R¨Ô Bƒ_p/ð‘¶wÈ~åClGÚTäýåÇD›Ýå<ï7°3AäDZ«xcü wŠöCTöÀòW/™/ŸÔÀyj=ŽçZ½R#ÖÒÕX5w‘?؉Eú7Q)ì5¶‡Çcèì¸Nª¾çsC¼±»?Œ<^-N-³¿°ë·ï¦ºWéý×z´F}5”I&ùŽöôjægè}ù•»Ó§Ý¿ažÈ z¿ì†»/-¨Äà.¢`GÈ¡¨\ò¯ê!Æ.  ÂZÉÖ Õ(¥šLo½¦Gã‡AÔ<žJ0 ܧ5¤¶ô ý{P’dm %9ÙrÄÈSeç]#·wÊÑ÷oÐéå#¯|i¯!!÷ß(Ñ¥<ô¤ª ûuryÖ„±CõTÄY’dô¥öGáëç]Ìóó‹¨Ñª/LQ1{&Ý”„(*uál¼º «c¢K7©ÂÑ„7R¹‚¨µXÑóó”ðPËq8›fH75úíß66tÆGôlü =×°ÙfópîØtKãØv=Å ¹ÇÁãZ29»œîSËþy¬EGNI’ v·nw"j >øÐò  Œ17ÒÕáZM()ב#ò‡1Q½‰Tý §8õX›IÙ/N3ïU¯³ÑÚ´V4„D»~ÀïwB‰º!Ÿ ã°ØÅzú!A3H½ÅD£;œkC½˜f+C"?PÛ$kˆ´ý ï.Е?XnÙiL÷ФÍA¿ñ¤¡0ñ•!5[íá¤X팕Çå#× ]ž‹Cç­ˆUžžšaF•”ã}™å)°&3kþÁˆ«'>ë9n‰‡™»/¾Àþw}XV)Áž07AþÉÿò²ÅÒ×4ÊŽcª;.!«’}I’'“ü®\›½@`ߪ2òëøóA'Ï}¸·Ù+søéØcàÖ§FÜ¿ª’âàŸ°ØO‚q¢Ž bôé4¨öánI/£ûàUàf›Q4OòÞ¹ª„çe\z<‰¡+¢T¯•·¢ëößç—Ü ­ ¸iÀ¯¯;ƾß^.—²Ø€Å÷1ñX%ŠLýÊš±'Rnãù%èÚ‡2ckqiÅ:>c>³h<3"Ïr“®£OdF±øT—9Í~¿A}FûñÀ\’ëM!âþ}1þ„AF‚˜Xš‡ÝÍR`§.Å´Í WfJ“ébS¨mK~=ÔÎy“j!lÓnl‹Þož27âågyúp%f…3ó.\GÿéÉÜ‘5*pó• m}Ãé´¨evêEPK)`†þ¸ã¢9|ì0eÊJOOžƒqðH«'_÷>À9„¾œÕpL;’íîæHž±Ã%¥&ô—¶3}iI ¤’p_&ž0‹¦Û^L!4)‡ÈRË©ÆÞ*jRrKÌ@ÌîÂ{|d×p'š„‡Óx©fôM-hQσ›àÏM K•íð˜‰1•»«Cœ¼,`˲³(°¡«,¡?Æ2åóŽÑ­üÄêÂGt_«F>¥\ ûAEM>>ÐÊTyL£avÊô®gSU(Îtß7Â鯺XÞ‚µ4Çi”ßdlöþaBÄ÷ƒvYe]ŒIëòâ²ö(U¯ZGè¿ÐßëHsÏ.Sº¹’Ͷäa£=½²ȉµú¤NpSôG8Ò‡œ!À1Mšíèý2˜Õ±~ÉSFç]mÀý ‚e*í˜c‡–Þcw7ÿÙGß‚Hç0üïïu&­ÊГÇØÌI¦.Uؘü{ìb»<‹®Û@gd:©óˆBMëÃ0£™¦³˜ ö±½š³wÒ±‰}Ì+íEÌ‘³oÑd:ù¶b!‡“ M½ä°¼¦¦ïÛ^rôcùšVr;µ¤‹ÃÇ;ÚÐ*ˆ’?v3A!Ü5{qø*á™â}Ö2w!±qP'ë~Vœ"‘U%QG%ÈžJ5h‘¾áà}šéà{‡"w‚^ƒ2øÈ„¹7£¡Î¶õ´ß@jøÆIL˜†'x—Âí–iø+ÀD|61¾+JùyKàíï8£õ,;')’~úbK­µ>p›Ön·sŒ£Á¶ÔA@Ýtb¿uï ,=áxì[±š+õÐn²g‹>'(»=¥,_驳ˆGtÉ+¸…†ƒ±uY^ XÁN}“μ›ˆù½'ð}ýfŸl…òg3Øù‰¢Ì"ãyô²ˆ 1 @ æ$~ê-ÿ/¨1³G»3¹oƒUjV¨™‘ç V´Ÿû©sô!_Íxé™Ö­ž/—ñ¢1ß™ïÕ tá,]jºn ¯ßß²a–7Ìö†­›˜€?ž{î™8hÚ±'„-}æï°w¶%˜AÏt#üùi$ýÐ%ƒ÷ý…Âo…»púxuT©lÁ(‡?lì¾ltpü™z´ÒÉ —çàpïÞÿ|¤èbo4r’ݬ6å²­°,Ê —‹¡ w¡¤Ù(:¯oè><@oÚ)RÑ™·¡kÌ,˜ós áO=‘Ä&ßPÃESÃà±v*ŒâÈc’ƒÀƧF`²t(N|Þ"Op ׂ’¡Ýª03lN[Ôþ•3È´µË©å„D¸ð¸Š‡7½¥^EBhó_ø˜sƒ”Ý›Žç{{Øòš\â±Ý7ÜÙoôüýøµ@h'Î|æÚ Ô.Âö‰ßŠ“7!¶l8Ä Á31o¸}Q7Oý‚Mܲ>ûw ¥5ëq¾ÔH*3ÎNŽ%:ÓG°_E„·?.Ö«ŸÀþÒFX7ù0®Ê€‚eÎô×Rgœ±_KÊ¿2©Ž£Ø9“ZìÁ/Ü_Ù+øjO*[ü ¬g_ÀU­Z¤MÞ ..Cƒ.I qLbË¡õ³òs ›Ïà©0O¼âçǦ?G¬ â¯ßÜ€WÇ®aÏh=¾ká~aèå ¢>)‹„^É%¿[ÁïìS°yú^<>NŒ¾pøÄfžjÃJAéñÕ ©Á ùŒ9ÍìbCìzšôÎï€Ñ256†Œä{˜™ß{¸/÷%–¨Âýú ¸õºíú†,á*¹$€öWL:iHSWì…Ú*YÚ4ê2üršÊ¯½‰ïÈV,8šÊå0|ÆzØør(eв¸‰ ý¨µë¹‰"uxwОßLK‡ocO¢Z…'^ZæD%‡Â­7ÃùÁ©.ôrȦ#ŸÕGp.H¤uþ—I‘¼?Y4á3~Y¯Ê?¹Í¾ß׃ëÖ•Âë÷šqå¹h|¸:ŠÝι"ÞûàfÁ¼š6 §Ë‚!/‚1%Õ•ëEÉñ­“ê ØÉ†ˆYz0ûªœ–!¡Âõ»ÂèÒ~7(,:@RZBéÃ=ª³ªm*Æœ+­8éJ´P)7¾Oä+žâæû Iˆ:ÅÂø8¥ù²µ8_ÿ-<%K%ÿ¤²µ.RtÙØ‘PôEVþQ¥ –Íø)²ö ’†H˜¢ªH[¬§m>‘d¤ÑhÿìÉ-ÔôñÊ#?’þY‰?4MÞÏÕ4ÆÒNv¦ºMD,ÔqŽÑ:ˆßv4 ÿúf—â-™Öùè¼`mÑ‹–Ý›@2pÖ¸ Å ©zï|$t°…üÄ`$l©z¥¯HL»Í,×rõJMžš˜ÄEüî Ì> Ši̧£IØV‰¶Æw0ºD ?t‘½ö§gçÐ$M+¼¸k5)H³±]õ-A8eâb5êgú eh€a %†5¤ly"^^„e, "­—]ÑiXtø^'R¾ÇX…Åz°ÿ´ºªpªÄwèMZ€Ç¾_„…?;ÙD—}ÐîÅÌÎì é"zxéƒ74†½G¥Ÿ+¡nÒ6X"º•Ɯ؅£#ÒPmâÐ:Ä{$ »»YV£è†Ðà)Fg=UC×+ HíøÓìàT'žËáç Yê=¾‡Õ¸Á§—O@Y¢ÈòvK¾ÈÒs”σR‡ ™ÿ|8íR&ËÃwʘå,sn6œN1g×#°)S n$Èpù¿ìUs%<ö­Œ4¿–Ñc|¬ëF•fISèïìmë-œA3Ðl‰25[˜†[Óãˆù×Hv[w:Th\µ èùÞœ|Ín}îfã­ èm©¸,©A9*Ñ9½¢ NŒP§QŸçwx‚«‡; ¹ŒÔôøë®[h7á(ºíÄÔàöÇö/tj­æ'Þå±õ&H©ƒצïÚ>ÿ#ó[{ª]É]­AÜ51ÞåÏë~á¡‘pÖýw ÷3ãˆ7–ÙEŠgA<ù"òFž*å@Ñ6#ªÛÖ‹Oû$ù¹ú)Pù«óœ $úΆéÇr„7ÒI@y*êÞ.Á ø³0e0º*K+™RïðX~Þ8n€ û|!Œ|{ö• Ëáæò0l‘ÞJvhÁhÛa\íe>,˜‡­‚þÕ•|LfìØ×„Þöa ¹Þ×½­ßÎÍì¡„ Ìy?ˆÂÙv¼{Š/L›‡Gî«q§ ð¯™å÷‡´.¿‡²‚—ä­åJ8}w¨ÄàÊOª˜rS‹1ÿ@¾ŠTÃBѹ\|‰NY°);ÆaBÛ+p·ÉFù‚Ã\3øzJ•^ú-ƒ»l¹ðÙ%pŽx„žó,ñÅ![Ä1mÑkrm˜µ{š%+ÝqË5¡^è7¼½—˜°a{ݱl7äî-AÃÈýØm®ƒn#Å©…ÿc<±ô ” öäMÌþ D/ëjÓ”q¥X:¤–/»$èuŒÂ ’¯È—ð$œÒ5 Þ=å¸Qh?GÚñˆq\œ&ÈŸ#ìã¥ìnýoؽù[½g1:}2ÙÛÁgäò.1…­®ÁË¿>'Èf%o °ü´œ¾ÛÎþ „Š¥s±¤T‡d•L`Æ]ä‚Í¢«>ÜÊ‘—Ã…æÉùl·Ô8¸IŠÛþÝ„:ùÌÒâ[åòþ\ŠÝ}g :G®‰'€eW:sX7 m ŽÕam8ÜY;c‰ÛÏRXJS1 õ¹ ±¨Ž67ÁFßj0T1d¥ †ãÕ?QöŸ¶LŒ0Àk4'ëYЉÉÂã`pN“&u‘ßM1¢æ—p”k2[\e ßÜ „ö– äQ²;»¢_x)LœÞ ^ÌTZ´épier«gƒ `ô¼>†ßšV-þàð>Ü}-¨ùÒj|UsçêÓƒoC¨Ÿ,.t¸ˆ ÞHЯ…âÐ.q×1kúÀ·€÷?Ÿ k3Vá°åN|æºXñ'ï.Wä>Õ3þS`<Ûê­² ñ„:ý½†Á¶¡Ãéƒ4C”_Ž$ª¶¢mêàv¥$OšçïZŵdöÿ;k1°ãZ:1ñ;ƒ¡zâ´$é!w¼õò!”¹GCò–ŸäK³55µ%e4h“7÷6áô’´ÕQŒŒþ­Ä‡|Ër/ÅajÍq2¢Þ›¯\'eõò4Rc 51œk}ö¢ÍÕ°ùC78ŒãvëaØ¡(j~œ`ì­y°xÜ4ø;Á‘g.Ãüò“lëö´9:‡œ¶ä ‡ Õ½”øaw?’~f&×¢Ê\VáÕ5p"½‰¸ŽäAc>“¼{[–\Ì·–Ô/š4Ã2Å3»7;ØŒƒ g>âõË)8²l¥SFAƒ×aˆtŽƒî‡ã™_æÊ–üˆ‹¬~ð&D*Läg Cè~ÿ:œTâN¾¦ìg¤ÿ,,"VpZ©˜Œ•BÊ%؉öhVuù>›˜dŒ}­1`÷uÙ·°d”ôÐôõ+â³ÝZèïz .=§j¦Ã¨ê0<}Xúvä“;âðbÝxÜ¥ôÏGËuT›‘Ãþ$™uqôÏŠW^îÎû>Œ¢ƒj4)ŒüjÆû‚J1lîwm»-(^vSâÒ›ýóõ ¯:+Ò‰êÓ°»±7àϰiÄ4p&~б³~bÿIøeûw›ó=Ä µ¿ ~dæ0ù·íÄ´OŽ{E†ãö½|‹5;=¬XðêÖf¸>㨮YK]ÜÊPãduMçßJ¼àÁ¡8" âôῚó›£é$Kú¬a'š ¿À(à~~Nx1dÈ-”'^füع¿àãç ûÍæð{“^a¥¥-;³ŸG9Ïoþá~ZcýñXV*Ð÷p©Ö:òB•·y ­;qÜæFâ²§>Ç_/u]–Ç9Ðwß}蟥É0Fb:H¿œ:Ÿ€†ù‚ëýÂîïUðb•ŒØpKîlÆKc ¨þx¹p²# ?µDã3ž¯Â¨ú6#ô7? åä-ÚùÖa÷è߬µú ¨™DÃ3[sžñ¹Ç]™|ÌÀ㜠ÿýüûÐkJ5hrÿƒäù[5:ÑMœG|€#?®þÀ€HÌ÷§wØQ·è_0=)“®{ýûôZ!ÿ‰é-ÿ‚3×r‘g öê .WÊ ‘üùÖµ|cH \wS‚C’[™UÀ%ؽ>:Î(SÓ[+¸Æì‹¸4›³¯ŸRpDŒ%f¬š ýF 4XY–Ço¡åož“;ÞðرYpÕ=ƒ]lÌxGúÂë2ÝHx9m*hÉ߯}¾ñA×p§·T‡òŽ¿¤ÄÝy¾³ÛlÔxÿäõ ݬKOHµÁ`ñ]¶eG‰Ë™Ž0q7Çé?.â©¶žR4’lËeÄÍèÁÝ×\®­XÁ3w¿Eñ×GqµQ‰õ™¦ç¦“‰WwAûBqlÛ‡7Tþét}¼t$’¦ñajN;s'[_"¾g¼çअ9_ÁÊCyAT98¥—ùWÎØ}ÓäxÓ;+È÷éµP´O–ÏÝØ"”1´ Tq/<ÝV w¦qøõrè.» ?^…$XcÇžîûMndèòüEá÷ˆG¤)çŽõ.D‡©¶œí ¢Û;NÁ­s.,ÓR–“ù{„‹ïœEõ߃ðçüy|®ïou¢0UØ[F¯‚«ãÍ(¹§Â}× ªÅaèpÏf%Žå<)Þ‡–µdæ¢l¸áuž¥Oã ž©$ñ˜×ÌŒ£z1ëé2ê¤xç­]EErU±Ø`]UdA­Ô$àÃnfòH7öm£­É§aú|qîÑrŠòxõ‘8;W›ïx«Ï…ë8Ìó¦ëòWÐc©»¸$A#®¥ñ×-"àf57S$7|ÇBé†[rd²Å î¿ý>x—’›ã ¸„] ?¦Bù%)zÌ«K ?ä3«˜¯M>ê|´´%]_ìAÆ[ãÖë~à6GšMÁÏoTñò}0:¿Du¿¢×I<4ê4 ¶ ¤Rd–-‚«F³”€Yt¹ö-þzì./8‰þƒZÜkE=ì:‡k6Çòôy¼n“ ÕžûE0ÇÌ”Ö/O£OÄIiÚî‹~ ¸ü¯G$Ù@ G*îåÖ;×ðî½È ‰ =%ÙëÊiòå£ö JýDø×ÏæÄ|¯8 ‘³…?3‡B®›?+s’¥w ÊÁØ'?AÌÁ˜Vm> Ih;›ÃìæxÓ…ÍR\ÛГkÞ0çÒzå§Î}X&ŒoU¥ËÔõé÷.+¾²Ë;?_ÁÃbÉ«]2´¾©n5&Ѷ¤]è“ý·…·2 Æ /¦Ó;›á+»10³bÏzpéj+ê'nÊ7½©Â¢7ê =›:ç™ð9ö¾¼¦û‹Ê‹Ä$ÛTZ’E ëÆRõÕ}äCÜ~êÍ>\]w¦ºcy×S\îG "…÷rïÁÍ'_ÐÈgXµ—W4€D|÷-‹®>)ð´Ê•£½FÑ{ØP玲i–Ð6—í™]ÊÞàhÐ"”Ÿœ'œlïïfø¿g賺¸õès¿Àrx–{$'pûŠè²à(¤ˆïã×’Á5Ï€Ï2°׺Å0Ë'”þŠ3ÝkÅP¸zw¶_ûÞèSù4 (QF¥NQ~LŠ¡Ê?Ý–Ù;L å‚fúàuºD®† /âQ½~¬6×£ù2KÐûÉh|<û¢ðÇÜ“¤Og¾»ý•Ý)Þ ®­gðOjÔFdãâEédä€Ü×?Œ7}`ƒòlëa\:艋«µø‡¥÷¡«ynNøÊ~Ά†5–¶JJ‘?®•èð…Mþtû‰X¸¡^@rïkP%q¼y`9ý«û³{Éñ©¥0ÉAý7f“¦PmÁ]ç‘äŒT¶ÐHþ{â³-äMÿ’ë šöù0ÊJ¾T|,ëÿç—¼ô<ÍÎ Hàïí¦àïPEJ‘çêäyÌRYº¯¨“,”]„k:Qäý*Ó z‚±aD Àñ[`¹{ O_ôŽ'°²ÈqXÀºØºv;~ûÄl~¯!š*¯³û…Ýù¶0«»J/  }„)—¶ÚF¶xV‘³ZÝ´tΈÉóE˜¨Èí¯vÑ • ÁóÞr¾ã&Éo•çöÞ¢\c¨èT@–a)$“(÷#kS>a¤KKªÚ{GBô^?|Û{ý³IïgùðÑ¿³é3±õÄì™&d &Ro)×›² 6ü†ÖtK¢|Û·Þã¢Ò~Xu€}¿lÅzÒòØò¡ÊÔå©u>ªa§ÀÀl)ýk7‚–ÚOáÑEYÅq÷æ¶ñÏú¸Üœ^wÂÆO§5?ŠáMÄLn8n”dͧ o·@èòÕÏÕà¸5Ÿ×>Ê$³ÊþÂõíÜ€¨ÐóOíÈÖS§HœUœ,/Ç7Ü0+oüRã þ\Äà±ÊÔ-J–¹7âéMÏÀ/j)Äm“„AUìÜ]K³VԜ餞‘81ø5ˆwD äGpÛçµ$6û=og&ΦtãŽ>¡Íü2Ü~ÅïkÖµH½Onm§vÓÇÇ€dòÞy5S:ð.i,«þ…LDÖÞ.‡ +ßÂÓ{¡Ôæðt¼\)Ë#Gî†gÓ&GeÞÂö±i~Ø i.¿Ö ·/¦‘3á"TQå™ë;“Ú*šRµ’ÛüÈží<1è¨í³è…Ó¯ÙXŸHoL€ÊU=‚›±Ù¸Û(JÌßà^ ›E$AëÛiì‹¡3»ÅaeÁ!|§³ƒjîó`âþÞ¨×× >"øGD±¯^Ìw·-M™=v>ŸŠ*«µÁµë8\’Ù@—‹PÃÝ”ƒkŒ¹ZV¦Ð¿Ú‹iס»ðÍ«†WÉñð­ünP6?÷"‹úZæÓký&tóœó ?ï*¶ ¦=Á'1-ajUÆðûÁ? tõž°Ý‚K~;…^â—Iµ]oXõSÇø‘ºÀetYó>§u.ý6é4Kç3‡…aÚg~N÷t4Ì=¥BZOùÑ¢«ÚüªavhÙcóE*D§´,ÁUö 0æÍ+´'ûYqÒ6Eüdå­ 3ºGñêñ h^‡ ·Ù†ž?¯‚ù·@.kà+8`qm’¦ñ!™—àÎb[râ¾)|Ø<BR¿’TÅ.ìÜSʦ± s•&ë?BëåíT²•_õ±Aýk± V‹-"[§1¼Þ~UžBG9ÅòYïÑÐ;oKCËŸ+ùó«2\&{6î}³MûíyæºH÷éÀæBñ¥6£ò§yéÎN²ãéu|YË¥›£ÿ·‰Ñ&_üÖŸë—¥BÜsˆÚ ÁCêJH¹y)˜:à=§ñ0ì¶<q‘æÚo‹HøóIÌ]Z†Þ¯øˆÿÊ<üoO«˜¹U5ÓÞòÇ©Gàeø–·'ŽÿòÇMý€¦k»cÓ…¥ ¶îˆpT–_w÷¾ò‘¦ºÎüµ›/?%åʼn)¾ vÝiÅ'ç ±ñlÖNàÞGº ¾áˆ¾-ƒkEètî–y:âëR”/º!L¸zkªšqÎB&Ø0}nW†Ž§ZT*kŸïc‹¾ºm"6Þh¯ìHç·/åK642ûÌrœÔ*r·?²·“«Q'ÿ&\OÊEƒuútßø)ÔZ%ˆ‹¿<ˆ[žßtwîݶ†m~eðfß.0_ñß{)`=„µfYд¥æ~ŽÂ†a;áÕf ¹œCÞ/"ü¼£(Ýo‚J3*ÿ!œ–j~†»Îãþ®±á¸é™¶h£(=Ò{ýëä`†Ñgx±sF¸(’‡-p(õ¡PÞVžOûèN+;AsÉ%2e×9rSz]®HSO: þ6jqqg9sùË\3ÁÅ›rTQ.o ÔxိŸoÈ×ÿ«ÑJKA¬Dßz´µåËéîBêÂÈÁe¼È¤¯éPK) jÞÇ›P!õ,VmxG¦_5á¢Û£A*$¿J9Â…ì8hÈpaå{‹HS î®æ°ýìa¼‘@±næÏ+o¬ç³­”Wo€ÓÏM@lø),y™ —½ºÕüç”^°–.ÕŸoÿõ¾1|MA/yoMÕhó]tu“‘àŸÖûóÌ¿çÈÇ]ôá·2ryöÊ3 Gðv›{htˆ÷ÑR4Þ³ˆ,P¹æ™tç™ôœá“6~cÊÅ iÜËbzp•½~5€Ç·Š´z;‰O7< [wvbÑ«t´™«ÿD;ÍTøÇÎfÞÅŽ<˘è9pcMw\%/Â]okñB…ç÷Ç»@VÒ™?X±”ünû€ $ޑаl Ù(Ã7v]B«Ìf(:<‡‡Dœà?wòŸ»…³z /Úº4 \i~Ù)zUíß>FK¿Í1Yzyø\& ‡ñkc¿Ñ§Æó9 C}Éò“Õ`ÎãPž8ý9L’>M–D'Л{‚Ù‰ è§ð\¡9‘?]ÿÜjBhò‘ôü¾õ\cñm¼2U7œÆó›²±¿”p»Ö:{_íÖŠÀe×`éî`šOe.oÅ»ºŸÙ¶],!.ü¾§F³Ó®e0Î4„6uã“–˜R«»3hþ“³L. FØ ôÅ õéï/Ž´§i;Ù&…34’jÇ?æ>Ä9m°|õiòîñnôô½‹o¶BÞ¹ÀV˜×„´vÂáAGR· 3 iíáSÏ¡™é³ywÖUÌ:ŸÅÕÜ88µÅÓ_ïpƒÃÙü_9E÷þJlmP妵m°g7R©G—ùÿö¡uÙûöòÐ]iÜdZµJ[¤hÙ©I(x–Cj¯XP[­0è,ë 8¸àÁgØ å.´ ÙCvf|‡skŽàÕüZØ6©ß\['¨?êÁïoZ?]ï1×—žàç_&¼µÁ„oVkÆ5jò¦ «=fñÖÛã©yp¨†§vá‡Ò©TqåT~¦H·= 8¸§Œ4 ù׿c!hh<Ü“¸,:§5§îÆO~OüMêרÒt{m N;ÀÍ6†&ו’̉#°'Oœ:ëM¢¯¯¥ká&£t¥àÊÚ<)ÿü5£Ê+þ7 ?ƒ ¡£$Š7»©C›Ânò£ë<ýëM+&ìÄ'é¦Pò|1ïû GWN¼Ï®Yê¡©…¦‹Ž í¡VLUIã]mi›m—ðÙõZôU×A;ÝÕh-`B©û3±ý'N4.#—¤ôiò7†£¾`Ißqüý©Ýú-ðƒ{©KmcËLRÙDãћȕ‚@iÝI0õüBÜþà“qCpíoiø“*à¿5iå—÷Â[éÙ¸ÊÂC ¯¡D &[‡¡£Ó>¨O&»ÅÔÁùê;È·Z–?c-1J|Qaëx Á¿]y¯kðŸ\rð–PgÂ|ábçß d Á¦ôºŸDwuÃ#.±ØYiM—ϧð¾:ˆƒŸBËãØ£¬xYkKÿIŒ‹ò¨Dû~ظ÷ ŠwÅv cHû˜Ëtå’¾'ºðøJtì{HÞ¯ä> •¸bÿ:i´-Ñ©eÏlƒ¾—ïððÌÑÜáì€Ë»¾ë°1¯PPr÷Ûõ¼…ZgçüãÆÙX'ƒ#ûi’ïf4ÝÍ*Rÿ&?6v¢müõÖ Là[±2ãà¹L? ÷¼RÀ­n+}æ«M–-¸€3Uaéû*ºîÒ1ø3ób9HN_…51'q³bvÉYó·¾g_ÈÆ‰çàÈCgáÛÃpœ¼Ë€™ÑƒÕ:üИX0°ŒÃ5([Áš^]–ÅŒ$’òƒ¸¿2‡/ÐD/ö£ å醸£hn› Ÿ§œcÏ>ºÒ£›!Øñ,dg¸£å†¡ô~dxû9Ò‡vþü›D$uU_ÏפùÑ5~siÀÆbÛ‡ÍÎÚ{nÀ¨Ì«xþ½,ó§G%ÚQ›Hž9µ 3ÚLñÓ%~]<3.<•NC®¬û =Z–À>®ùëæ)^ÁÍ3Î ß\u¡?ÞoãÍÛðNV)!U+¢Ä©m¯57O#¥n Û4IúiKlö’Û$õ©ÿ=•>)Žók'aÓÕeÔ¨´Áï6äZžA›‘DéǺÚý<Ôx%”{ø¼ÜYU²)ÿ™qH˜xJ…âÙhœÔ²šF ÊUºðçcðÔÈ|Ág_4÷’;€ {†pÝ{×!`C±mêÐûJe&˜Óek•éµwqàpa6nÚš/zj9êtÄ)j¿“ßæÀÿhòo÷?àJ%~_FŒÏú¦ÁéîcÄm¬YmFÿÓj9Ù°+¯ŠŒ™%‚j¿ïÿ㾄w&$ų(ÃR#êAÚÿ5 £‹³V+C°s):géÓŒe(®/Ç,¿´ãuß88¬›½7]H‡)ÊóíÜ”w5§Çu´¹ü§(>ïd°àWú[HÚ:Ž.¨™ÆýbŽáÈ/ª‚E5·`nM;´ß9Ž;·ªÐŸí¸Áµ.?]…uÎ œ9>Zð–†ßŸ!À{_ð¿ùø×I\¥©žÍÎ?ŽW64à«/O 0÷(|õ²ˆÝÐÀ çìéê–Ñxó–Vg/a^Þ#¨Iäº{ˆRÕS?¡áÔ2~-̘Ž*­E‹ ½¸hÚuOïâç}[Øâ«0øÇóëq|z»ž®ÈG©Ã²¦=‚-õ3pÌ#4ìz6»—²&òæÈH㉈¡·ðŠÃT¼f~—N‘âg}Ï2 ÛÐÔ~=,<ìÌÌ¢¢Q¤üÜ_êwäâgz¼Ýï>!M?Éw€ñŸÈ¨¤|È’N = Jð,ø-ˆ-*…áÚƒäIƪ™)o-'ðèF)̸q ?Oç%Rt¨|7±³ÖÂîëK©Wã^v‚vÂûï'ˆwîNÚ±c?Û;¯Gš5–ß/„mÏÁ07›Üo<ažHcëk’,u%äKáÅãë¥ÔPùøßÍÙDœé£4ûIÐ)ýŸ‰bï¼riY æì]Á³VAÜ^,œ} ×átØÁÓ<ˆÏ}Ž ?~àùÊø·Æ6$>†sÇŽ¢Ïñ^6fö2~Òq8] ¯²Í¨ó7x¨š%+‡q7ý¿ìú½fáÝïJP=} Z´áu5ýì»M:|‹œL6¦¬!ÇLOïԋìì'/xnåÏÏÊ¢”p*œø§mÿ|N$pí%'ªÐ7šÒ8*TœÍæJ°0: —<°àÆùW±AO_51ãnJ«X±ô1”‰žâvKXõa¨º?ngŽÆÇ»÷ýã¹ß—_`Ú8þUÇŸÙͦéÃÛp¤V6X-Tá#“ºð°ø}p:] ÿ|ÁÎØ…p|ìif}!|Œ?ã¸À¡4Ýï(Ô•dá–‹:‡8½X`nVÒ¢€¯Uz1£ŠÒÇÃ´éØ¼W ݽgÖÇF_èÕSîu㱟Éíœ ‚²ÑN‡„O&Ô@³x\2S`®-GÉÀýáToO`ò ”¹1„މñd¥oàu½SCþ½ƒŒKÄ$ØŠ‡oÉ sÃðwG=Þž¸-r×`Õ]#zÓalß¹lkº±‰ä}@™7+”É6HÑwŒÁÈ3=Š@ÿþ@~ß6 ‡¬“á#€P¤VQî8\ž­»ÎDâ+aà¢"Ô[ïeìVÃÿì'N„oãëKNTm®»µ[œœ¤×‰a’$¹_‚ZAê…7ˆ¯qg2—;Q¿²¸ '4«ÓI½£xÙ—Eèþ{\¶; Ûåè­ãD}ÿøöçÓå&tq†*Us ËçÀÁxSRÛx~ËÖ&øObƒŸ˜ §n-`P+x€×vêcÿ&;,Üø'Éj¡ß…òv;p]Ípþ³^ž®|Ëc^É;ªH®”CÆè%°Ó¨ë÷v¢ñÏa¬èÇKöãr\ãwpHòjP1¤Ò²I(">ÙMQwWíÁIéIh²ªgž[ÅÑ'H?w˜Ä>.)¿ qì˜ó'¦›.Oø›Ö(<]E¼J AÎñj;¡(Ú‰¶ gë“ðuÓàÞyKø±ôÆÂ/ÏÄÓw*ÁÕ[<ìÉÞ‘~&µÇ 4ˆÞ=:,÷<†S Á3‰°ÃYZpÎÖÈó6Ý9Ê'Þ ám“±æO¼}º‹ {Ù®+Ñ´ ‘‰Ü>CÆnïB>4v\;ŽukáO=Ǽ…³áq!Z¼ˆ×îrb–®á3c`Äw&µd ~* á1ݱ(%åÁÓF4@ÝUš{ç Ï_bfðlÛzù· ˜1æ5› ì“€ }ãèü7`}9 hŠ'÷YƒT<<“F9± ðõG(^,Jw)Ø\€RaJàkÅ¢·¦³8‰“8÷!€Öù´ÈB–‹©Å‚ô¡zA†\!vÜš†cµÖóM½KñÐ!QˆñN^gWâ(çßR¨šÈ±Í¸èU&_aþR°!¿»¿jCØýE|kF.<¹4‹‡òý8:Wç&Ý'm~XÑ5Ž™rÅÏ GÁwf7éîŽ{šc„ãõÞ¢H×%¨Þ°Ÿìi¸‚&EöÔ–+Ÿ/¿Éá_£uÔëÃî?ø’|›À|ª·cz×5²òÞG–”,KO?\‡y_L™í̱|õŒíÌÉYŒŸ~hË×·îgÓìSɪ©˜sûœ`I­Ÿ¶—Å•d±‡r’˜!w› ¨*8gÄ“3S½æ—á—¶"ö»\À]ùþ@[âpvç5×Ç×%`é"i:ÓIRxùP ©œV»í_r[t„/¾W£ÙS#0õ ²Øý7õ`ù÷/ïŒk„iœÊRô¹Œÿ Œì8H¯ÒfÎâûqš“ ›tŠíqxg|œ`ŠØnÌ)XÆÝ•S04¯ RDäáºY%>»4…Ú9+±¬T/¤©¨òl5T¬8Š›¦‡u­óȉÒ$ÿríSô ^R_E3"¤°·z$õsœL«R­Ðcßz—Šú¯0ÐL™îS»Œß=_ Øµ~à±÷QEðüZ Ÿ’p»­VKqÈH´(i$̳˜‹Ê‚±ù3˜426&…ß®àÔãëèÞ{H­r…ð«òIÁ–GkÉšÕ«\þÓãf°ÙKžê7ŠºÌ2Ju\pìTÚYÓ½kþÈí€Ë¢=lÞ…`ýU”KÒD…AGܱm5l œ€Y*Øañz„±ñ!Pn„›?ḜSx¦+ƒ,î~€›¶fË~Á«FBm¹%‰3wÀ sšÑ³­ùc;6›1—ÝÞà“ ÛÇæá`猕¯am=‘œ{L4Á±YÑ”¢rÏ÷ÃÁˆQ¤÷F%¤r$¡N `h×g,«?†“µ+qsf>Ô½q€Õ“Dib|Lz5=JáD_K¨ŒÅ´¨o‚S†³ô˜y ðz$lX¨ΗëÉQ®K|ìG¯¿²£y¦¸‡Ï¡7>Çk×/Á¤•íÐwQ ?¨'BÁW?~Ú—«ùæÎ×aªØ ðî¹HW¾‚²d6„¬»@ÖÎD"z>LÝÔrÚa<}LF‘Ã-ø©åiìÁ§ó8ë“»CòëÜ™ó®ihÓðkä,Ù‰>%º~¼qò­£Å©ï»q‚ÓÕ3Ø®ìlìÏ¡ððîpÌ+_Œ¨ŸÄ1Ü1Ì ¤þq§þ/káÒÙ‰¬¡ð8«Ø¬Ê5ôé³S°eÕkx\„¶ýšA^ÝWäúƒ˜ßøóD1¤Òcg¶ÜÇâÅøú´ïx áž½ÐùR™+|: g-†’äN0‰H‚ܽ~PÑ ÂCä~ÂóÃh‘t7è^›N¦ê¯4ý˜O–u«Ñ Éæ¼kþf÷k+´ÏÀüvaÓ®Û,¡ð"qœ ·ßÝ‹a‡oâÙê$¢÷'–; xO²9 hIÃã73¹ÎìË8í(Îé›? ®”dc‰Ø—ÀÙ:¸¡óóh? ‡Î¨´ûœÜÑÐr7ÑŠA Î^å‡ ~w¼5ÕVf[þ•xž—§{íàÃÅ(¦-!thÍÒùtMÌ]RyT‚7ìH!‡·áó¯žT|P§ì6£/Þ€Þ°èy„‘ªFÜá!ªÝA_¹=f/‡öpÁE^ãýd3z;xïîšF#rñõíÿoVYUJRè]kLW|=ZàÞPØš ÛÒýñé7úbêHºööE”ЧëDª0]jÍTÁÍ"ç£ïóR¸´\‡ÿþbÉÿNËEìzcÿ快ִËWšw„¡KuøÓ—°ÇÿÖ“4—/…aÜÝ. gôç#ú­ƒÎQNн(zìbÈKázNá,Zý‘çcÎTÀÞYdŽx>ü®ß«.UBÆš½˜›V Ç<>3@ü¶Ã—6¹g²ÈÉ\á¨ýÃö…Žá7çþdŸ7Í¢?_¼g³;Ä©÷¾%?¯ïl€tœgò•8q½ÿÂ+_kŸ~Î ¢B5Ùgq‡‚/OPY=eÃmÉ´O1¬.DŠ›í–‡ŠÓÃaÚ¡è²î¬ÏÛñȹa‰£^QåCˆ˜Ð-Úž¾Àëö³¸ëv-ëŽVÄö+" ›}’hÊ ¬qX¹ÉnÖibnüRAíêáÁÉðqâ ô¿ÏÅŽ$À+=úÎ0·ìÿú^wY¨·#Û¹k4ˆ1¤éµ1½!jó`%>î­Çö«8ßYg)‹ð}Rò`¹Jk\å"¶ðác0ÓßœŸ“d¸ç_MôË¡3OlÅõ=½¾Ý„_‚zø^ »›˜úôg˜“ä‹w*=ÁˆUò¯U¢tâ§Ùø·Æ'·ã§›‚ ógܹ+Ëó·åâ#EÐ?t'd/çï…^ßÿÙNÞ÷Ïï ¡^Ÿ&•?þ[ÖÂ,å\¼µœ]‰‰;ÌÄxý9è’žÍ}Ÿ'w+—Q‰ù[lÎýkŒix8ŒÄ‰MºÔƒÃótÓŸ”²2Aµ›.Ùmo„7ÎûÓy;aˆ‘¨šéñ_cç¢ÁjôºÊàsïp.SdÇ#¦î#»â> ä¾?Ó‡ IÑ%¾y`þUgYAÏt„ÑÙòô…ã1þ P”VÝO†]µ»ñþº¡˜5a%ýºq#.mó&>có¡ÕS¯5)`ŠY5׌JôºÀ;h´è«óJ|é£Õ‡á„—]»ˆGÍ_ÌMM¢K&tà„M½Âšó¯×oë„›þ°{›Äv=eã>UÁYe!N)^Bwör+«RbgËpS®>¯¿q tŸ.ƒ=nÊÐjKxמPç˜:ß Žiå“ÐjCv¨n2ȆW‘§o½‰kÞ\™?„Jé ¥«N"{ÿAà}À†ì”Ç›%ÇsŸŒ€«O´¡‡øŸÎdpïm:¥æ'§Kà¿u4nð a_¯å_5ãDÂú¶LQiNçŠ`ã°rÈ.H`'¿'&í¢|I·2½tS NÖ 0ƒ™‰¨9ã(´<˜Ãöøæá0ƒ¸ûs´`xäc"ëw¼WÐ̽ˡäá9Hx¿ "Ç)ð)C„/¼‘/÷W¡ýæ-¨(NÃsõ„oœÄiuáuÌp­¹y£,·Vô£Z'r؉M:ìÒ= Ð_ÊŸË索¦3¸&Ó¦²åpÆ9ºòÅBÐ_¹WUѧYÀS(ûo­FË>*³|”>‘ŠÍ„'O3üø‹ÏØ •i¼ÛV§ñ—ß Å+qù¶tšÝuÎb ÛÐ×ëq–!%ñÍùâ4ciý®×s D;\P¿ù ®½óºÊж͖[O1ç¶ ôû)ž³]TiÉLJ§…« ð.v7,U¥_,Óœ¡a4ëþ{".£ ƒ?ÕyOìiâuk=d ƒ£1P§lí”è̾صeUË+Açi~p4á8MwÆÛ¿]qO[9[bØO^ß7áò‰U cëÆõÔ{pj .³CPÖ½Õ27}õ åÜø¾~¨·=ž›Ìç¨jC²÷14Ôà›vÖ‘Üã[ø›Ð¥`§ N+w>€‘b'¡Am>OÚ›‚%?ØUãBþlÑ80ûQχœC/©cŒÛZ °Öwf’ÿÍF·:‰R¹€`]Š£šB¶GàÑ×½ð4ÕÕEŒÈó²˜2z÷DY')¼x^iþ ÌÄ©Ôäƒd”ùb¾øìhnýç7Ù[QŠ[5iy v¤vAÁüpL™3Ž¿WàÎ& ºÊ@.ü)'çëÔIÌ~úmz“ôöÓ}7öqç¯O1l ÿk¢ÇwĽ‚¾®I´Zy ÷¨»/P œMM6èÒçf±¹b/jD{ÓÖ9#iÈŸ‡ 耟EAr¨ ¿dŸÅ=G„…qs¨f^#F/§"L4©pß Ê?-ÎyÜ"ï»}pâ•©|ÙŸ¥ìe{ Öiñüù»Ñhÿ’tòÜù÷eh³`.—.3ü5Å,çöÖºJÊûq}C-štÀá_Á°ûÓR~w¿üM•öÉJâZã7=Ó¤:e©º­<ÌN;IVé<·R°$¾Š„—åžàUÿ:Žü! ”uaªîAËMK|[º ¿­²ÃSï¤iñû³Ð°ØžGΰâåaÉӿ~´Íº ­–$AËXIœÊU?%å·MhGÎ <<Æ‘–T»HÛyÀº¸öËYŒ=³ý·¿c§‰§Nº=³ £ß–_Êô†ÉÇuºl¼—ž|Î1ÄTMNç㸾ÛhÞvšy¶¼s‰‘tç`Ãñ÷)c؃¹ŠŒ;qÒ£` MÌöž‡Gë›Àbõ1¸UسMe¨þŸ×ì¾ÖúÿÍJÄô˜àR‹½¦ Ô§ ኾ8M¿÷™ä®=UÃO ¤z*l])ÃvŹC®™YGìfy‚VôoÖº"¢{œÙ!YYÒéd…iCq0óÎßX ý~v|Z©4¾÷>œ²pØþ–Õødà Ù’!ii}.XqM“V}J<ï §ïExÞ¾‘¨¨6fg £'/À½“çáÐ÷!´hXég ØPœÔ¨HgÙlàCÜrÑgý—{uãÀ®û+øüH†‹ÆM¸ÁΕ;t ÷L(Ü«;-ô]ü>}ºCÔ«EI\v{}sUxV'l=0/+'rŸëzôéNQžáª EM«˜uÅuLT¹’ þ¨u¨mÔ[SŠ¢ãs¡ÄëDÏ«a³"~áSóÅôwa<Zô—Ÿ4 ›*ä®ËÙÆÌ“¤hדåÈj¦ñì칸ÞL›>y0 Œ,ØÃ²c¸$Ø„Vf¡¼„ [Mw­8ÇTêPÇq-ÈZ €ZæîQ²ŽÏßîÂEÇ{ó-0†u>Aµ‡{æa±ðr`54´ õ-FpX~¯0!ã_Ô‘ÒÚNNÐg¡Ø÷4ÌÝÿ­¼öüë)ÎJ½˜³äÙÊŒó÷ãß/GÁ8ü$Æ1ãqG5hÔ¬5èùp(]榡CaMhµé8q¨Ã€'{éÃ¹ÂæèÙSx·ð–ɇccª#ÿ¾0ƒ?.?/°eÊ|Þ]¨VÄëg5 %èÔ®lGòxê¬h³Ó؛Ѡmv‚Õoá©Ñ±Rg¸$'3»jdù+'~ävÀ¬ÓP2JŒ›I˜Cû{Qºðþo?àÓÕÊ3òª¡Ôî™4²o–Á®Bxuá K3`óĤxÿ~Rö¡ ãOÈò)Æ)ØÒ‡L ßj¯@ýF‹à¢¸h¬¶Àåæð·_¾£ÒƒƒdáäOìø“iØì²wgáºß}ðUo?üm׋~…Vw…_ÿéÞ¤ò÷ƒ4¿ýå$lÿÕ®“ËXPêcô™pDÖÿÆ1}ã±pÎJ¾Ù?Ô÷„ò;rɉŽðÒ¾ †&ýßucYgôQôRˆ [](½yEœ¬hÞq%œö¹”—=À%×{X<ÝĬ÷‰òiÏãÁáx")øÏÛ:ã©á=Y¼yÒ &´è¦Ü[Lïù <‘XÍrêô•­4ëö–¢ãoV° “$°3Ó튲ÇÖ•ì>}C¸o›_w?L0çÛ¾çß@êþ29ÚŠ»§Ñ¬w1|âHÊ š@«J™êõM„J·…üÌamŒ¯[‹$.A³ç#Ç«R>ôõêÒ úç…ŸæËrÿãw0|¿>Í*zÆêÖ>k¹2÷hŽ!ù!™ÂÒàÃÂÆŠdXõT Ï=n–N¡lx»u Þ_úL_Å™5Oȱ×c8ž²ÁªØD hÙƒk"hnó(˜î@7Z_‡_<Þ—Æ´t+ðû¬B06=ÂtTrÙyá:\þB¶=®­6¯º}ŸW/ÏûR¢`¿ÚVØ~༕)…èú!ó8®k|Lbué¨ÁB8õðŒKÆÙ¥Ã]p¦RŽ><@qžòújkÞzmËïÖá&Ój1Sí!îsCG,lBùëRøõI9øŽ€ªÞ1´§´³¥æ?ýµÝŒÚ¬ñåƒ^\Fó3æ=Ÿùc;å)<-ƒ©éd}ã|ô³^»À;™0ôù~ºFå ³xìÍjÛÎCÄ6{Úó‰>˜þ‚÷¹M¥ëZÜùÊc]ø[»’E~§lò„ösçTÔý¹ãÏþq& ¾ÄõéG„{K:ðÞç½Øîp oY€ÓÈ`xí9‡O^ðY ¡Tþ™xJ¯ĹR_„ÓNËÑWuA×úzi[Ó7ֳ飼£0î‘1õb ËïnE«ƒ+pg²<½T>NyÃLBÝøy-ªnÊJ¶4Åõ ºøMNÙØ@ÑÙ"ð‹˜Ck_ŠÐS [à»C8ßž7!@e&£6¿†ÿláÍ»Ð-`¿i6 îiíGõÍÒàÎ¥ùÆø w¹OðjÌ?;¡l®˜að› .)ëÛA|Átxýx<Ö>‡Jüü«l\Pt–ËÈðašg!lªÝ-_@O§âÙCBwB¡Ä ¨ü4ŽN-Ö‹hø­[Xr;B(2éë+“ìaMì\y ¹4Ì”8#‚w?ÚÓéø¿Ëd\l ¼Uû‹¿4¬rœèåX*™K¦=¤õþ²tJk,/KL†Täú>QP<5›{ú oG% FäŽ'ëˆÒÉ~ßqʯƒB#‡Z<ù–´< GE† dvc±œ.ÉïÏ*¥>‡zƒÍ<òV6Þ©.eƒ !ÔÖbWžÅAÕT¼CÔF†MAµå»`‹R­Ëú„d‚sÄé•чá­G;$ôu¢T{*~Ÿ[ Ö8¦,‹±Âë3(½‹Â3éX^‹gcáv¸–pB€$Ìî>3Ö‚nP&Ì7”ƒwˆÁ¼óÑ8¾Ý”&-º Î< %‘’<$ä|ó×ÃmÏ»Y±‰ÏbŽZÕwñ¾øÐûl|ׇÆý7 ïuØÎ½>QK ©–Ð@Ö`‹n6¯Ù‰ÇÀ¾Z”Êúe æÊó“[RáPj%–}zŽ#ZN¡]¤ZªàäX ê˜ß…³çËâ ÜÀr¼>c4ÔZLø{#¨\þo_O[F'aÐþatç÷_øõ±­ó|b5l¼´¨oÁf~Ñ–†–›Ð vt]öHºhæQtÏ<)œ({I±´aZTqÚ|ôieüËI˜'êËÇ—ÂI?0gÿút×f*:p½òÑ/:ügx Ó¿& ^v0aÛ±ù xë¼T¶§·‚PûŒ ˜¦ÆÆ–o¥%õOàÙŒtb°Ð€ûŽÃóU´H7”îŸ6›cÜCB{;È—e>:+¤®$â¹µ©8f¾\&Vœ ¹NôT³hEè=,±ZϪ<•IvM$:] Œ‹ÆãèÃÁ´^ëéPð„ȉ}ñJC“.€™¡ý°Yºò_½žÆoÈxÐ`kþyh!¾Ž%Çæ Ê+~ÃHøºSƒŸk«eó &@  %ý>5TÃ'5}+]–ŒZLS çÓbŠWgÂ^)Rÿ*˜×´ßGAB=ç-Š'߯Ĩw!}Üa Ü]p‘o€ì[#iF¤š¿þùO&¡™Íjø»ÂŸ|ßW´¢¸æY.E%øïÁp¾ŸÁ+ܽz O«_$˜d<‚;<‘¢…RÓaô…ôת¡öÒ¬x|_ãC²7zße¨Ìí·îÆ!ÎCoÃ*29¸†$}¯A©w׈i<¦9KÐí“KðÔ 6`~ ê8¸øÜÀì¿AôøiVÆ]<Ùÿ~šÇ–, €Rß«.nC-øg%9ê»é L*!wí¨\çy’.Q‚[‡Ò1˜é>ŸþØvƒVÓL´áÄmZ•ËïþL­ ŸªwáÍG ¬oÄR~ YŒŽ;|ûîï§Þð§w —câ$"™ƒu‡ìpö³ÜïI(’7]^ÚjBA—& <ÜT³žžß÷™mí€?¦¬wYg+ì½TÃfâÂdY:tìè|;CN¬‚Y!Êøäx+æY‡Mú*tÒíµq1™h>ŽcI ö@³YlþŽòu¸wÌbXâs‹ŠSûɧÐ yë1Oªþ~/¯WP¢kqB@*(› &|®eOn«ñŠÇ!h·P–ÛORhñ"Áåw/a½ù© ô)] C¿N„E÷’èËßÁm‚WM~‡{ÇaO6ýÕ:2ÔxxÈ;Ö§9›<ºr Òn)Àó¿Ï±ôÜlˆ{åÍòðeT'è|4á:’üC G-7åNÂ6É@¨zt€šLØFN|ÁZ/áÄå÷aèv~Åy<uUG?3ØU}¤Öüž»1—2[ÎÄ“ŠÉ ›éáL}ø$¼k43_[± ç«qÁpú 3–ážI«qžjwž5†®7;‡[Í%Qþª—Ü?‰íZ€‡~íá·2f€êMšÖF¸ýU7¦wAƒj…Hs·ý¤ÀzTÕÄ®Ö`ö>¾| >òÈù–ÑôÞ“Ã(XnÎ/”æÁyjî·ãƒé2ü½ßôlÒ›+ê àÞ*Ÿñü e|¶¾å&["½¨“cþâêï@7XFð59ÿú›-ß׳‡j=Òd¿V %OûâÈ ùrPPÂfÈO£²×àõbðuŠEO~¢¡®9ÏÀ½?oýyмS¸‰N÷£ÿ‡ÿg!ë:æË$ây¬Þ Ábsg|5obbÕ¡AãJe$amj«Š•†yý£]²–¦À_‡ªÎ‹‚§Áÿf u.àö?XHú)¸cYЉá$þé²bm05ëÄm•/™©úð? ª31Mô'HfÖ³Ã6G¡;M ´Ü"Ë÷ëòŸ1m¸¤TuׄàÕ W`!÷™³KÆ~cg[5qZR :V5@ß)]6Þ,ËuÒ@îòxÔ]á€~®à¬k-ÎßQ c[ÀÚýn3চW˜Zd8wEB¾&ºVý—ÿ- A}ØVvê÷ ¶õƒÌdN<Öq¼é+%}êgð³H Jýø†§®¸§Ü G÷þ`™vöÚ¥z_ÆPc©6vpá=عIŸ7J)ÿPÈ‹B©Ž·p…º _×BE»KðMK„Ä&Òs[VÑ2¹ÓÌ‚QöõB~¶öŽþ¹„­X.E§šl¥u­ùð[ú Ü;§TbñÇ+™å;լȣ±‘üú¦»;쮡á }Pî{Ä|F‡æ›°íb|JÐä;A íÊ–´×‘Õ Ï3~®@›–rpÝ•ÿð_® RùKqýá¯Ã5䛩>-»Ú…?Œ¬yØÄ— yaÁÍö8óèv°}„èôÁ6ÇDaB»qþW·’õJæDS¡µÚ0{ºþT Yùi1g—Aùî/‚Ù3h¿œ!MNsàÅÓ¸•]c¥ã±¬l´Àq—¯±æÙ Ýd­GeŒç¿ Ë¡,{ʺ[òUP¹ÇðÌe©¨—6œe9ʹ‡ 7XÆ}^Tõ±û]³8ÊËhOàpZšTÍw?Un–á/9RQˆb±+¹ ð ª|ió¡£ÈÿáŠ*€vQë‘NƒÑÜHO ¿u%° öZCÞ\þMvWæëm¢øŸ"‚Žûh»])*h‡á• \%ûüËÅ…³ÆnâC¬ªhgÛ2PTûw`äY÷”!ôԜŴ-L%Áìk¾ú¼85p¢XfÃöý’ãCËí9µ4ÃÑòhR¤Ìc{s;«láÍÈ©|º…5Uò;€CVlà}Ù]èº=TåáUœîÿ{¿Ç‹±\ç•ÜÐŽ|Óo”Q?±"¿ÙÁiïpU‰ m+=Ž?IÂZÕ¸‡÷l滂b ê:ÃÿÃOä ¤ÍÌagö‰Ð,—¡¼(t€,3Ð]CP:8 Ä%Žá&¸ãÕ,Ú^aÊWýy‹Ï¶&Ôñ$´CtW>„„[ÓB§ &—ßÞ‹ì¿üŸS )mìüÁ \ÓǼ_ "jH§ßA2ÈÇ8åÁÏ‚lÝs“¼ô»…˯ôY‚¦MI[JÓȈRCüºÎØöÁV“rèlCO6eÂU¿üÓ‹l®;s)Ô‡qhöæ»pÌ’!ôÓŽ³8w¼.Ÿ¾­œ€«¯_†k<©®a#h‘1x­Ô…¿,ýˆuµPy8'Öì‡õÞ¼xB$¯¶öçáq/ñwÌ>¡¢aŽmuà*}»0JáÞK/%Ï?½ÆEEÃyÊy3Œ[;ˆ+6î‡ïžžÂT5m^ô€ìßz‰Å÷ÿÅ_•K¬˜I†Êw³||uâK¼4é’T­páæ'BùîWRüápQl¨žÎ-VÁéºÌ+ÍǬ|°Ÿ6‚Nœ£Ãg[•á Õùõ8Ìÿ|¿+R»¤-`÷a®ÀæÌAp‘]Hí/8I¥þùG­‹íàSoˆ#dFò G%ºwK*¿ú„aØÍzní*LÌN y÷‡áˆWÙ(P-G»6s4›¶#dö¢ñÂ8¶¸×˜õ; :[„p?å3ÙÕ†n`…½Ëͽñcqß þîm‘ IÁãⱨ³Kœôœ’„ð\îÿðOzw—5;àð–4AÌ·e0pà<oDÓWÌó¦-ýßòvÄGE’µÓÛ‰í”$ù©D ‚?ÀÞ‰cé™Ö l¯ÅüS"íC_QãÎ;!ÝS$ô}}ºU¸â”Ý-Ÿ‡·€ë‚-¸óäo¨û5 º·mÁƒ™ðð*:ÈØÓÚù1<ºq;´Ïö%Ÿ—„ÀÞ™n×÷†¾Ìt\ôµ/¹þEõbÔý.S]¸ÝW¸1‘Ïr¢ó-&âÇÍ ¼AY‚oÔ\†V £yCÜfºèj]QFútœÎ-LÜ4¶ë¶@îÞJrc¥ßø+úΡì ¶M&lšl@GõÃìà‘øEGŒ[hX ÉI ¿8 ®Ž:z¶> S¤EKwI%ž,kˆf~ëÀ@@ùÎàÙôïGÒwWxß63X#”f;Pq¯–t@üÁ‘ø2eÿ°¸ ²ë©ÂnYþhí#²OÑ G™á•öbâì«@_,8Ïê…XAŸ É¼ß"~ O—9`§ ·Ô&Á¯1ØõªÍdÜ¥‰ÿÅÿùßçóÓ÷¬ìo,ãº&xv*““øãs÷×á¿g- :˜†Êr2IŒY¥àÉרÔé^Kæ Zbå‹àÃZS¼’!)¬Ž)öÀëµ×ÀøgÛìùB«+ˆIË\I=úÓÔí’ O]¶Ñ›NqPu“§6¡CxgÊRê|%’]Šc,LèÔ]Ès‡³ÄØS\ƒ§bÛˆ2ø.MT•.þ3šò‘Д%Ï ϵç×ó,=zâü|°<0Z{ÚS±D ; ž† —åXîÏ£øÿøÿOØ5LbŒáWT<Õ>ÌÞ •ãu}°À!WEb¯Á¯åPº5l„jq…ð…Ì©ü)v¼}ã]'ñÿBt*—FƒÎÓð˜o¿¹œœK›¢”PynJíq…ùÐ\¬Â|t‡Ó3gs“ƒLøi=·–X†s%¦8¯)ñ{Àí»¡á[Þxqh¯®‘#è ýQxû´§".éJ}­>áXówXc>t÷ JŠ;Ÿ>cõ3oZ[#ù0=”;„|Á ï‘t•M$Œ–h&wÆ$àÈØMÿñ¿på.&¾g4–†«òÇÍÏÀ^$žSF§8CƨX<'§H¿½îÁΖàxȈí‹<ós³„»=–òÖŠ Ö¦Ì7Ž:sŒ^·ho=Í$–f`Së ²ec.?!N7©„¶ì*ËmG¾Âo6JÔpàÀ4Ü•Ã-éÕuì÷Õ¡ÉÅQy4•ÌNÜ£ÎNÂqó&£†–?´\ÚNnJËq©ù¶ŠQqÇ3ñÆP›~‚Æú¶Üp†´D€ù‚U8ÊÅ›ýû€´˜æ\G¹<ø/þÃí’@öPŒ öRÚZ‡»K¼Á ÏTÿnDm/eÚ<ó,¾«Át[ÎÀ‚£Waa¿'¯sühˆÃí½„~›ìCDÞ!%Ú\ð¿»¡¥¿ƒxÿEXÑš+ )·„ÁY77Ÿ¢Ê/F§³"q'v@«Âóå¸LFêªæà Íï¾²#ÇÜÁá‘xÒ^&>Gg«\è\!ŽÇÍÀÀv`“2ް¾ýçc·n=yŸé‰‹ÂSѾi=U¾3‹ž¼§+4æó_üþTßÁ]88ç›pòºáÔd°ýnø0R Ô#`uûB¸¥¸ž¸ðVÃÉüdG>úów\ñŽ]]îK­`r^nÞ}œ®ŒF3»}¸ã…'or„…™ÆaÒ§N9û<ïž³–‚±®7-Ÿk…aJÒHØfWÌ*›œÉȰrxõ6áo.HoÎåçüÜèÑ— <ùx"Æ[ ï3Žâ`°!×Âñ°«Á]V°%½f¼á›±<Šß6šÄŸJŽ…çVC7boÔW8ˆòB‘ýt·¡šð…Ô:Aùó¡ÜæV;Õºé¿ú—ÏIhÓ^@êöaÁ¿ž~EÇöA᪪Fq>g}ijjIbn4ÊØñ)ŒÍ¨X‰ÆÞ†Ü@ÿw´ä—ylÎ6qº-eÜK²éMB°ÎYÁlÞd´L¿óнz¬ÈL¥b!õË–]÷VG% þ÷Œ/Uû,ì˜Md#-iëäK$ÆX‹ç7Â7Ã}h\,Aÿwõ–{ÚT;v àŠ¿ä‘î~8ÆãR3Z$ƒçÞ¯bãVVXëR—S~xè"°™{ OW±Ñ`¶±(Kx­²GøxÛf0½þœÉØNú§èü‡ÿ®!³Æ<ç®fhy´ _JDí ½èXý\h¾À‘ýï.ç½c\èN³³Pbl ¤̾§3#«¯lÿì7‰À¥£ž²¿ï#¦5@TŠ27Þ=„'eÜa ø‹Fi÷7,ét9Ìôâq_,¹¥-Z˜âÿf°ŸþrÆ—H¤¶³œóÁýr3J¾vA±T%r@í“ÿ[(¨©WÂâ!°{Ê<:ìzÏ= åߟÁ¸R#ªÈ ¨¸T>š.7g—G(âÇmäWÿxÀV‰­¨{í*^+SâÚ¼9_7òþ§ÌÄ“+ÕVpãþQ™¹€wÿ£ï?0£Š½‚ùSmyXßJ.9Ñ”ƒkÄõ­æÆâÑôã€*¼twâgnÕá«Íl‹Ó?»ëFò˜ð‡LÍVÓݹj™¸ÑkéÎÜÊÅ^ŠÃáe6°$ëY&NkóLùb½œñ]šfgAþ–å즾,~ÐdkUZõhüâÃqRópúì•2¸ÙsiÿAòûµ7¿Û^³Ò±‡ðevtKGYž;¯Æï-?„‘ï&ÂÖF5Þñ♩=ŒÜaˆ~õ)?>€våCêÊ¥Ö]ö~·‘‘8ìä, «À…¹¯Ðþ•³0á®/ U¨rÒ ˜¼ÀœG8Æ¿4 NJTÖ‡cßÕPöwÝXp›… ¶•ø~åæá˜+÷9þïLó‰Ú˜„‘;ý¥½¡åCê ¼Øuü_že #›óØ7#)6ãlk›Œ%_b±ª«‡^èþ.8½q÷Èã ²h1w5ìLhFÑÉVŒ±+6ÒïÁÊÉR°Æý)^³-Âò¹:ô­Ým%ÿ»w˜§¿T63¯¥"ô{R'ö_H†*Sx#1.ÿw ¯2Ç‘ä“ôøÕÉpÿb%<Žp„ïû£›ŸÞÚNìl¦BF¨ ¿¸ê#ŒÙúºe‰o®¢Ä‡ŽãË´_²õ›ûp[±/þM1À[³§òžIÒlÙø‹(üþæ¯>JMê|Û²5Û v¥ñ‰Í<ÐЊoüXŠŸ·Lé%êܤ’óG%ùM )¡ÏµaÌgl>«ZÌ ñ|ƒ³Ð!a--gFÛÿÚ[3š~MžÅÏ©tÿ×ÿeoÁB½¶þêô‹‚HéݦGG,nƒ6g…DO îXOáÖž4cð+³l:–ä û0uÚûDã“øG@†‹Òˆ¡0KSx&¦0<º¹Ûçœv»êÑÉžˆyÿIÁ¢K*£îº8X5ã†p) ­.!G–_b}êñÆëÙ ¬óä-?®€åm}¸(“›pJÑVc ÌU|ïGØ ðýb(4˜s#·ã=££pÈÈ#žCp&Õzþ¤3å`Ɖe`5cµp¯ÕhQ°º_l¦Oßz âÃŽ3ÔŠý¯þZ©L­¾‡¢õ‰pªõÀÝÃ÷qýìé°qÈp>=c.ºœÎñ=Ì.êÈÒ ¡ªÔ¶­£v|ǵa^|ðê[âõ&^·ˆ`­íZ¶ýp6i+îÄ5JøNº'é?€Ñ­¢tÈÙlÜf Š§‡NŸôÜp,<éBjg±#NTje_;},žPõ•_Yó€în]t½­`·º!ý8ñL~UÄ¾Ùøñ“ÃØ Ý¿lƒÞܱH“®J¸ ×JĘó/0ú¨Ê-M ³†¤ý‡?¼U›äí|]þ2§ 4e„/ú[&ؼv5˜‡ä‡°-8AR¸-w¶Pé´}(⸟©Þ\‚›ýFÓÕ#÷â É©•K‰TäÎb¨œá ÚMÇàêüKP±)ªfF+î0÷úP”)ÅÇ=, Moc½ô}a¿L—*Oø³É‘ggð‡×ôiWc*ô«ËóY{›±Û" ά{!÷øŸVxÏgèL­3GÓìg‘𵫖 Uuè×ÔA2ãÓh\·ø×Ñ¥û£¸}ê3t߸Þ‘…ãûq×Ö>hèÅÝÍt·ß(þ)Oç¿ó¿Í ¯vuþïþf¤=%ÿxš/©H}Â\9ÀyE,N³k– åçÈÑN3hÿgyÌÆãZ\uè1˜÷\àÞº ðB5–n›Æ×ÂÌ7gqæŸUÿ›ƒ¦£+ð¸óO0¼<šOûçÛÌ|>-5ˆb•ªÆür÷3|ôç ïq'—ëTQ'l÷ŒLâ3ÒdP÷‚5³Öæ³Tçr%s:y„ÿ¹Ášî%p›É’¿ú”¯F‹ Äi¥ôUüž½a <«îYaPL-ÿüasårù¯ú\Ñ2†›§iññ¿®Ñ•ý×ÿ¾Û5ã”ädœÿük´,€i3áUëå°a{/<¬ŒGÅ‚+lÒ'y̘ ÓØN,Ë^AòÂÕ‰Æ|ùÒNè•#­»‹p˜ÚB¾âìy877G ­P‰b1ÀÕåžÂR{¶ba¼r‚ Kƒ6Ë•Þë<“׃©øQ eòÏ@Ãè?uÝh±âýi`«šÂÜë`Ÿ¬$íÌ€j×ÁõÌøßlôüÁži¡ã-eh‡ñB^cü™<¹tŽåXõ“‹1jtôä Œ\Ôˆ7,cé%—ÂpïË`1¬dÖ é¶2¨¿&ó_þÉ_s)6a$óËNÜT·½“¥wÊס]P©ðóÛ–ÉQ¶A‚WL9ß%ÔèïÍí,,èÎN³Â¥ÛIÞ×VˆµˆƒÊú0´…»—Ÿ³kó&’Ãߺ tA:_ ÀcÉJìÞX@2AF`GÿÂñ¬?øÃ½†¨IåðL9¡“½0;jÍeè¯Î8öóÜÚ»•$×Ä úeã ®¿©Ô…í·yÍâãu-¼Å„©=ÇÙî­?a›öôúr„ý=d Ϋs0¥®“éøÂǘ8ÜR†ÿè°àÛÛNÀܽ|ãØëÿÅ¿&î+Xo…Ïd«ÞÜCE>x&›^’çwûa¦ËUÅÓ”@æuüñ•vø6·\ù“8yþ¶w2Ë]s ÞÖÀùËŸÀ÷ÏZ\¤À7.8 ‡T¯â>ýz“µƒþZ‚rå¸W׎Š?åm?sáÅSzíQ l !ËGf³oš_…u+‰oC(Ÿã:ŒJUÿ@=z·ÊDò£Ø“,ÖŠ¤Ó¬xk\)Êñ\X7‹@lî ¼^ÁGõáR͘m/Iw­p'ISçò3ÿ¿ÿ?®LßN&JyÜ´¨zá ¶‹¤ºvM0g–þ^Z‰ÁùSéóÔTH3ñÄ3Ã'òiq7Ñáîo.+Æ“#tèñD#L›ç!LzAz«Íˆ³åÜ{àøº®À›A±‚êå aâË%ð¿çÑ“Lø±W·NB"éÂW’¥X÷J‰¿®b7ƇÀÇCøP}¶6†Ôö®#§îL¡jsF &8q{.éòiéHþÔÂë¯Ä’·^U°À8™x=–b·q¡›$y™i‹-òa bõdø ù{ áµŒ——¡=ÕËÁè²4V¶˜ÈýWÿj†Û¢Uƒ'ûd(ʆþÓ`ÛW¾Äï2—lQì¥:}4ž»×/£›4aÈ üܬÈqž6²&šÖ&»Ñ×wxì}.ªî…ŸvÕ€iÕHØl¡‰wº.Áƒ'RÔøA0dÖ䲞æ$êèêÁG¿zCfîŠgwó+Ð#Y …·ìpq§c¢©Ç©*|b@àÐŒq\"ñXùò¦˜3XõóÕ‹~Å–OMÛ…élÅ9/¾tÝL>tZŽ}ÝÂY;0±É…ŸxYïýo°{-N8êt ½¬#Ç»ÜjàŸO<óÈJóhèWýÿ¸ŒLÍÚÉž„ÅãàJX¿x17ºS1öжS;+8€ØU_PšuWx·/Üù—œ9Ot¡´óù^ðÿ²†N~‰¬ç';dµŠÌs²v_À?âÃiMßF4,ä{OBá½±/™|‰&‹TÏ£{ž3§ÿ´ÿº«?Èγ–t ÖŠ›7Ò/£¿¡Gžónñ4{'Ÿ¬"*¿EéwÇR8l™ /Ã×]O6á›1Štuý$>úâivpd9düëEÊ™ÏÙªæ¡8óרfsÉìÌMw¸ÅTZ»™¿CŒâº|¨U";bøÇO d7û/ &ô£•„4ä ©Å×ëLþ7Íæ_ý†[½#àP¸5­Ï¥›ÿ×ÿ\>KâÍ=oqÞQ/ºzÛb£ –±©aà¯+ N·E˜¿»‘,uw!“ı ›jP†9CfD)[ñÙÝXvj?ÕsnÂ][8\«Ùu»Ýéß¿ìHŽ}f4€œ&à‘µXl…×ßþѹ? #z4ŸØ{ —žSÇ‹i¢ðð÷-l¡j»@”; ¬¦æéöìykÎ86Ù *[êþiþÁ^Ó4 0¤ü½ÐåH¯#.üùØÅÚ?Oä]ü|7 %¦Ïã!!B6áo9›3ì± qYsWʹÛÚ¼8X3Š7cý¸‚ÿï÷ß3a5:\â[Òx~ä,6²Ö›Š3Ån÷‡x9-^Ž•æ•›úXØßÃ0Ûs'œ(²ü™¥DU¾Í\êÌuć³8ʼn)sdù,b@ù†jþL3ŠüÊåÝý|r|Fxœ††ËÏàóžxš¡fÇ/OS¥ï·O…þ7zôìO'>ùÕ1ê¸@„ïYÄ5?ˆó­kž²ï®ÚN¯O݃»;FÁ~)˜eTHÕÉ¡“¬1r{!²-ý˜¨æÂ‡Û§Ã¹Š ¼¸ðžóèXëU¨²{² îK+6¥dÂõ2§”¹âŸx]:\öÆøuâö³É¿ádPƒ¹×a•ÆB*4Íuê h,uáÆñM0k©\y¥Á_d®á¾|ÇC=ž|Î…ŸwW¢ñÎüälîöv›`Âê“ÔÏáŽmÞÔeêÔw› ]ôU–ñ˜j:y±8=“ÛIÊ7¾a=Ñ®‚xAÐÑÍ~ìháökähsµÜŒsg™Ž·Õuø‡QW wíz¾h-L×”£ýËHSY9[qq•þU°î­Ìÿf’áfO-kËUåùJFô¢Ÿ*ž±ˆ™¢…ú·ß¢™y( ¨ÙA÷Ø ‹v‚šÛ F¼öÅÕkSÑ}Ì}bì©ÏCWÊrãŠ6òó±3M¹|C/H’ÌÀA¬vÝ„çÉoô º ‘¡AX{í úÚûÁpG¼ÔW,¼òÈ–kŸšÄ—\!Ê?Bpao³Òü Ó=¸ÍN-®’:*.®Â]Û2x‡þÙÿâß,[ˆÓÛpýÉK‚_ZÉXëý-U• û#—ÅÞÆ’ hJ›í×u`·ÚpÜÛ2•ê»8PÅ3ùÉ* ~7·¶YÏÈοö”÷Åã°k¥ kw‚Ì¥GšÿàƒFsùtE01\H›xšp¾ÛOçñÚdäËu<ï‘%œ™Ù¹;¼ ÔÓM0[,–nÿ! ™‹[°dÓG£÷9 ªãÍñ´2><«MoæÝ"Qî˹’´5ÝEï,+`w=ñ\ XM[Hcý‰0 ¬I`1ÒîÆãšÒ‚¹:ŠœWýeZÕ ´z,:¯öƒ€¶¿8ø°§‹¤Û&éÐyY¾A7ó¶ -GV ÷kðNˇÈçX÷.O܈åÿì‘<+®.dzÇü«ÛµOÑ´@tæeƒ×2hPç#J«œs÷éÓóÜÈ‹gcpœ–}´»Ú–FÀ3ÁTS£äÓ#0~’ ]À[ˆÝØ”÷jðÄe^|IÄî¹dïñ¶è¦Œ4çÖßYèû0~ìg1̬ÜçÝbáo9îáxr»sW•Îm¦ñò±s²Ù‹ÿ5…â æ´gJ>-IFœÄÅÓm!PdrŸ-›{ ¤­Žá…™b\jßœzI–{uÃåÍãù£}î\rˆ ·–X€? ø›ô\¡ÿHS¢s«)#îìâ¶P>m6›vWÇ B`ÅM ;}\¼èX¹¸]™Dê7yÀ ‚žåÑ_˜¤ô2ð}{oé!iœ qßY¡KǵTÌ}É2;$èñ =¹/V-$¯ ´‹Ýv4Œ/WÌd/:äø"±£¨‡Ï™Æ(W~HYNŒ¾ŠdÇc¡ÚPnùðPó½'^¼¥ü ˜Y¬€éÞ=÷l R ·+ø‡¨$L¿z\Hu9{n*û»ºí‹!‡Kg‹âõ¬ûö˜–nE¿x(üÿ¿Ÿ£ÑÀ·‡&¤ðsyÞÔ:è>n·£ü\ÒA4}ÿÿˆúò¨¿ïÿJ£JiPÒL¥”©IÒ=ûDæ©4•dÈ,’ˆ4hÖ „FR4I¤‘îÙ§Bh0…’¹L‘ÈLü¼¿kýúüѺ뮞{ž³ïk¯½Ÿ}öí¼ ÓÜTqYX„@Ój0(…e¡$oâ0˜wtsÍ…qáØwÿŽÓ³Ü-bÙayìˆÊ~r’!UÎ[CöÉáüÈAhàR‹Ü%ùÃÖÌÿæDÃì>%ŒŠÞA‚4™¶Í¸ ÉéÙO¨ØhEç~õ¦ÕgÔiÊš;m%J_‰-瓳$¨ÔÉH”yd ªmÎ0RÃLgÇc¶O0Yðx¿÷Û"¯±Cf%øßÜi™Û õÄ<¤UoÉ×úÃpÍ]“¿w?&X0\ ldMð·¡ ­T¨Ãæô®Ï®ãsop§µ‘aDËüÅÈrÇnÐ*E‰C¾+‰"R9e¸Š µŸ×ó­UÔbÄXˆ½ÛŠÇš q®Œ ý²é«45¼øšÜ¯¢þ “ºþË-ÂDÍ2v]Uƒ×3Ãë1jøÔsv.åòQ¾Ê€¦äDzÍf°aÓV•Ô€nS¹rñ%T3–âRÁñBe·tÚ]†kÝ£+¶6¡ÝIŒN\Êg$ÌÃDIœZ)ήVéÁØòATØmCšÌxq:·¾›KÚºÀOÌ‹¯´ àzênX’‚›öÄÒWeþ¯g¸¦a"w—n/[DdžÑk÷[I§Ç{sw2óë_ òö¤È«'£ýx›&Â΃ùì}Ÿ)o›k éKfÒ‘«ø0q™àyÞz# ¶äëf 0dçHûu%2Æ“ý"Ô†Ÿ=åŒ= ÞIœÆ[–r'‘xøÔ݇N¹_ÙËY4è’ç=FÐÐÕÄÞ›ÒiD:ͦÓFLJðršÎ9î¿=áU§£ø7ä+&)‹AŒßkn q):=Á½sÌéá‡z\Õ¾­bÖb™§ç@ý£˜'Ùù9j7¢õѰùÛ`œ “êÑ›1ùÛn,ziC·*œ‡Ñ&À°þ:þ¨œI†O²³Þã ¯¤$ñƒt6 WwF•íó°ÓÑ•t*ÊP… jtk¶”NXÎæ*êáÞÛ» …2ÏŠÑ…•kk°pü4"²w8Ü_°ì®üèT¥û¼ÔùôÑùlåH-DCï™.lœ^ ¦Ÿžãª¿ð§©4—ÉS§Òr°¿¹b—ï‰JI~Ûø+»ô¿ì]*\/­)^•äû„†æ˜Õóo†&ñî¾3xóÕs<¶È‚4»ÿ…™ëŽ è¿ÿûEDÁ=T87åŠÀìûIöx¼4µ$ÏP¨±†:GE³ÖŠôáe(±Ñâ%•\9ÙšåŸηÈ'Û²A£|-Ô]øÀBg ºæañµ¨”{×¹éñüÝÐýø1|,ÓCõ pfÙ!¬º4õó5¨iá È}IÌe>Bì–,{=“g†¤ò/W⇤ÇdÛº7øùÐ\º~:͈ÀºÅ:t´²jßYÌŒt»Q·Ú ¦ìù—Ói¦Â‘âj¼™åK/b(õÚ’@dú7pMñ»íÆ+žQß €Ëƒ‚±©0’æ*?ßm@þ˜ap1ø[Ó¢O †aQV.¾ºà@åeFÑÿf?«µå6Q ðe±]“:š¿;‡§tÍà Š è0|À§¸çMæRC†põG ,ǃâÜEÔ[¤T8iÖ4¸|rY8£ËûN“c÷eÀ¯¼„5}+»4Ð.&E‡ï~O>¦=@Á¼Q<¼\”‘<œ•S«–`Ö¡å sv)~ mA©štðgîú/ï Šç~U–tŠä=˜F¶°sj*ô}Ñ=¶aì!8-½K +”£:þ¯Â1m˹y0ˆº,…Ñ·"øÏß›äð |2Ô”‹ð7N‘=ÖÙ\‚ºØõw 9x¹ÞÅñ–®à´¥+ ƒ,÷\Ê_lM w‹WÂ+(ã*qš›ëÆj/³¦!á WU‰³ââñîŠ"A´åT:×ó&žÙªD/U«Ãe×5ÿ_þòªQäã ~<̌΄AËžÁŒ‡ÑXháŒYn‘ðeëUlM§nvƒ³Ó'’þ#±Q}U¯€Qöcf÷Fç©“„J/ Â#¹/]©AMÎܳ9Éö)ÀÓ£rà†ò¡ýÆ[P’îˆ:&çà†J»ðjq,3˜!œŒ¯”‘\ëß÷X&—½Þ*üõ«ýX©!O[§üD=¡jÁf:¸å¼ûv‰ØÌ"¼ÿ3NJñòŠ$Ý‹ªDù½Ûz‹Qbåtá—öê1$[ÖCØ”‡rRIß3ìí´Ã¸ühþÀóïÞåYž÷6íüvÕ+2þÓËkÛYÞ÷“8w“37ŠÆžÛV-‰½—ôhmÆ7¼*>9½dºá¸©7™ö¬[Š1¢Ø/­ÅŒî{†æ±ò¼Ó&OìÓ¤gÆ¢i‡Þü& N% åc_²+ª³`Ĺ×ÿò«;d¼Š>½G„»òOáªCkà’|>¼K#™ÇËD1µßúøñ¨ÆÍË„+"cødl­Ïz—+c»ò>œ”hN“Ê:Xïàt\?Á§¥²–Zø³Ô’¦íÓÀ‡”(ûx‚lî€Ç[oÃþq;y/Ðÿ:•©X5K ¼¾D°guèLû²ÚŤfÛaâ¤Õ°¹ò†@~ÙZÜU’Úý6èÓ¼ÅEb@9¿V·j9fZ·â*Ûëdõí ø-6Ì›ÊÑ©-…m^˜f·f‘'üxbŒSSIЍ,¦ÞUü×°Ø¡ÖtÌ2g÷Âö[ûYÿ«:ØK£1\d Yœ¯#ßÇM‡f÷JÜza)Þÿ;~åiñ6ÚÜgB%†ÛÇ¡ã>3ë ð†?ùš&xáÂiqß}_“ Á&£truôòúPލ À-¾ãø“¸Ød¡ÒUaÂþ»ªšl(ƸzüÐ{iž:uÚ‰ÈPí®i˜Ñ•‚úB`÷Šz8¢ón«+Àžc;Ù÷+!TxQm.©aÇ$K®¸ÍŒÎ=XÉt´ùó¡¹ò;|ùPôƒ_áXófÇ¿D¼ôþ"!'ðyà>Ø5¾ÊÖhPû‚«xÓç4kÑeuCe©Eár2åývjM ¦/Œ­.Ä3Y,Xç“b‰)Î}lÖÌ<`Œýn5ˆ„á<œòg¼„}pâîP š¨ª­—vÃÕ°Ep·à÷Ê›pøi.W¾Â³ ñYÚžü/ým`·D3àMÇrl´æJ‡dyâÓ£˜pt-½¸ý»zÙ 7=P‰ÅpîÑ0þç–97|‹GìvâŸê ü«ûh`xVžØ¦² t¢¹2ÏGõ( ¸T]—¦Gkö*P‡-Ø_"ÆuoKó-îßÉñßPjÚH*½÷³u÷ øÏ)“R¶Ì?+½û@­ªŸô¿H„µç;XªÃ{œm¾ƒ|‘|Ä"´®àòBY~úh+s›/ƒß…WqÕó4?8«#ÑJµ™¤¸~d_Ž7MÚ ý—>±Xú¬}Cš'™Qqoe¼ÜÎÇÓ™[‰SÔaÇ‹…ì³™)Õ›ðRxñ{)T¸}›ipr÷òp¢Pá ²fúó›qÙÞ64òöCµŸXʶqæ7ðWÚ†&:¢|fÌ4y<GÛ©Ñ’6S\uUƒŸ.¾+>Ãö_ÙDåq?²œ2Aõ”mx\uŸÖè΢fnˆÿËjVEc8³ù–`îˆGd“ã2Èš5…ǽüÍöx FWTÌ»]ˆj$.ü‹Cß/ç:ÖISE§±0ÚEÔ½ @q)Jûk®ÍG…†X“}G6à± LsQpGÎw8ݼŽm÷f‡{,qS΄šÓsÝ1@QE ›ýüüŠMÎyŦ̸sfæd¬¡£ø޶~ï-‡ŸùûÈœu?`ëè!¸kÚÁ‡¨e|ÌKÆÎ¶ÎÂ#ÊÙñ!_³lªÁÙç3µ.Y~¬f;–ξ@¶<_Íf-‡{3†¢a·,qî=7¬{ô?.4—‡Š‘®?x¬¨Ÿô©.Å„>)º2ÖÓgqþ¤ 6*-‘PnÏa[1±°V§®á7–…157E¦aÏ÷jÁù¼‘të…zà ŽM³‹Þv'â’Â%lzbõORç˧0¶ý™(½½£’Z Öø ž.Ö l㨓*RIlÍê}ä^ï]ìˈÂÁýpA;’vÈóÝÃ<ñF¬Ñd¿Ás4U|u‘m¿™no­ýƒ ÃðH.êÀ?WÉ×+œ÷A| Ê™¾'‹§‡Ñü}Ý0úu¬Ka‡Çûƒ]õ ~3<lôÄñòÜ×8W½ñ_¹„^26¶D’39!Ìqèhò^Á‰~c.Úý ê-f‚[³^ÜE*¤öKá$ÆKÛášÌZÜšÍg×»Ò³¥vÐU>ƒ©oׯÇ×BøßÍQ\b¹y½t,]ð`v¾ˆÆ¨>7º·l&âÿ ¥£± |ðv=Ì‘ËÝO‘ü[3ÂãFq˜}ÝòßgCp‡W-xÜëeª’t«A•SÛ…¾û3Q—ÇhZè;ÿ:Nªá$ÓglYÙ(:ÂmÄ¿øs®ù"Ñ÷€™ùÀgG±Í&?ñ¼âð4:ùnüŠÌ3Î8 ¿ƒ@©ý R ßÇœDEæ1ÙA5X›¯BW~J_ÊAé‚iìêR þ­6óB0­“œ8ç^3"gqëIBQÇÔm: Qª«ÿÃ&dÔ¿xc5§ˆ}ºteÝg‡ƒ"¿z‰F¼µs/œ½sf4áãÄ…dlê{A¡â?ÎæÚ-x?••[†õœÄäo˜žü‰FqOQû£W-Ðxî΂Õûºàîä2½ôjkŒ£SSÄ¸Šƒ ½œ+Ä®‰2ôÖ§x8¨F§)„uzsñàö'ì·£,=ñ|¬bël1ûAΔֲ¯âëÈë¤I|Á|–mÑ v‹ùµKeøsóyXÓºdÌ3ÐnYŠÝzƒ—t@@ýyxÿJ†ó²dv\hÃ]õ¨[­$¿º¥t$ Óp¾Ý| ·QSÀÿ¿9Êíœzª §õ& &ª¥#íváRÃJœå WÊo‚ƒ—>ÕR¢b™ëxçå*0m¯Àa¥yÄ݉Ÿ¯Â×ðÆ‘R+ø @wÓQš°nMŸ"`ÖkcMŒ'dDG0å-c¡aÿ(Þ .ÎÒK¨ßà4gjÉ?ið,0i(?EÄy°ÈUríè_4.iS©ï¬OÄÓg'ï¸JéÖ» D\Ôoá>7Aly.üîZ{O™@úüõp­t9d%|kU'”zö4;â§Ï2´ëª×o ƒ¨>€ÿ›pjõlü=n2æMAÇû‡±é–2ZH @ÌV€VÁûðkð$ø~&c¾€ï!ÃÒŽŽÛq®,LÁ%ª•°¡{$=8#äÎGq…Ó‹¨Ë°M¨VÞˆcf”á”w&8|ÐdÝŽCMyÆ`ºòŸoÐ;›„>$2:…#¦ãâ‹–´þš”-‰×ßSé }å˜6HÊzríø»(æFƒøM]rs¦:Ùà¯ý#ùog*'°F½¤^vç¸YÙyæoz áɸçµKJCq­Q-'Ý·….yð ·¿ž˜|]: L$V±ˆ% ¡Ý›ŽøƒßŸŽÂ„¨ȯVã“Æ­Á‚XG&#=Á¥:‘Ö;µŽ] 2ó ª>8Šú׸Éül;Š’‡\‰xè$”ÏœÏô§cЛ‹ðIš|[‚™ŠÕÌ)͈ÎéâX¡5”?ü¬O£§¢Xî¾j³+Ýh,AŸ=ýF†´ÚÓ¿¯‘Ó\ñÉmðgïqŒ>¦©¥2|BõVî·VoVCüf»ÔIêK .¦wŒç̺§×»Ð±5ÒDr† Z¸EÑW–QÔkR£Õ ¸Ã¥ݽ‘óùH•nöm­[.q‘kèÌ£åsûHWìB¢j‚ãM¤z_‡ãpËP/ÿ§´§Òã7ãUÍÓ¼÷f%ˆŽ6¨¿°Œï² ìgÂ'(×Òá¶ôü±>_Ì·þŒámmðVT5^ÿcŒë­?qïÇ>\yfŠ ¹Ì‚Gý™¶>—Àü€:”n‚.ïþ}GEr|ãŸeP7£¶Æ·¨·š ®Þ^-¼’½õ³AüÀy4l7ÇfUñ§z\ÂèØ²õê#͇k\Ä{?¼xë–¹´ÆòŸOµÜ‰%¦ŠüiYŒ0YØ€ã^ØóŸ¡tâ\zö"Xö¥ÕR3¸ö®I¼èésÚœ{ûýq‡žN4v†7ÍLZ÷ï^¥ä3œõù Fæ0_u*_Öo÷ÿå7Ù"Ô¿6¯ØF‚ÆB1lK‘Ç[³Íé±Skà‘|lºã 5oȨ1¿±~ç{Á¶0_ î©À#òßpå¦"2yåvh::Òï®âS†Çj§ä®ë[x{É’žÊ™N2ê<1Ÿ×À¦õ†°x©&_¼ã á©Ñø~ÉMlZt† ÎXƒô_Ãåg¼D{5–ÍÒ²†xôX¯½10uŒHlFv\zÍÇkW—õ£3éñÛ‚¶è}([hLo¯ÛÌ~¤— ¢ÔFò°£T;´è:7‰¶mÂät1ì—ˆÅð-4>BŠ|øOЖH&e‘¶Žóùé‹Pòã9tYöb,=åðE¸ù{9(~Ë6u4`ãÏU,mçøpÍ-ô¡Õ 餳¸çjï‡[Þ%Ä:C”*ÔYÀÑðhA>8>C£Ø!ºíBI‹[ÂýÎG° 6O°óàNx7½žL®Ï% öð~åj‘qRxgû]–3Õ ßfÞBËÀÜ×ý`塇a/+ •âÚE(“îÁÑþ·àÂÖÁÿR†K<Áð[Íh²ß}Êž•÷a·×|’e`ÈmŽ¿gÏS³ ¹ jáuÒ‡µw˜UXì89ÞÃ)¡¹ðdÃsì^sÞm'Ñé—Ì´àÙ1Hû#M{¦‹Ðo%ŽÔi§={›è9œ° 4Åd©ÃWtu½_³ê¬ý“—›>ê;UcÃkJ_†Áì½±œ¹†OØŸÁö‹:ÃäR°ä•9?;4ƒ ¸QËT˜_žž'¾¢Ñ9ØtØ 7Žpùï¡¥8Gÿe¹öòþïGY¹ðô¶“â‡éÐcOÌÈ1mG~Äz G‹óâætPHd »*XºÕ/Íl¦›¥.â¯Ö‡°÷Ï)¼ûØ€V,¦r¶­dÖº—x5©‹,{L; õÖ0çÑaÒ2û†_ gwMÓ]Á*¼;Q™|ŠÒ³7naÔfT&šÿŽ2W¿‚@m+Ÿ“ºª †¿“°k£6oè±”¤ü÷©GívÒæÛð@°'~ye 3*ÉžÒ-ßÎæ,í@«‚»åÄhÕ!ºpÒ!\ߊšAÕ¬õMnóŽ8kÉœ=4€^Ã~lÞ­Á†$òÂyoÐCS”_éÔ |e*Z- k2¥)ÁN´‹ZÃéÁ`¹qæ$¾%²+÷Ãqùù¬é¤Ìú­E/f+Sßë”nQ³áÉ¥Ãé2ê ö…óiûœbÿ.E°õM!øT֋݆‰ ~éyô÷oă_~‘_Ïãã³Py[ŽúøIòîð_ÿtýyvÔl4U2 »â®¡ß© YľÛïãu¹®ÂGŸîš”9úaö·t<¤E5… MŸ&Òý6|aŠ ÿ¸÷%„Nã}.ˆõ/=pŽ_:vœÿ6÷: ]`ÇE ïçBÎ.Õ"¿ÿhòõßÄù’ÓyÏévfx-’nãë#%ÄaX N‹m¿åÞXç‘Ç^<]‡G.S~»'—.¥Ëÿ¹ìoÏ¿` ÎI8ñl“…uŸ½¹Tµ„nÏ3çÒï-*ôý{%¢ÚRе#sÍÈúšç†#ÁLÈÁí¶=]7ˆ†º.áò‡&Ðð“y¿žÝy%†·¯9 ïCLüW\}ì#,Zâæ÷4qAï<úr¢}hcyS]¿5TŒ³Sω±•.µŽ8 êp;Þ˜6ÝÆmÑùÿ§Ue(ùr'tœ‘¡CïhÀ̼P– ùê­0WAò‡…îj‡&7%©¤@Z” xáR|ô#šŽ‚™s"0,ª?ùPÿеÇAòÓƒ°­ý/¹zå öή“‡KàºT0,þ¥K oM‡¿~£hïŒ~Œ>ìÇ—ÝÄ^ùmbô Öµæ#¢.±‘·Fпý9¸€µ slƒÉ6ðÊ·²ŽüÃhŠ®ŒYÙ¿l£vMæZ#ºá&åž¡êTÛ³CX /ÆM™†z\€…wL ÛQo‘Õ WæäBÉrIÚ½e"H¶(ØémS¸sŒã§†>ÅÆvˆ â{FßËßáŠõeT¸£È+\òPøÔ‡{Œ·ÿÅ#þûp‰´®9!Šª¥’ |z™jäšP/ö’‰_1£f×Ï£Î&²zxi³9óç½ÂwCZe¥Œ±s6‚„D4*Wîç›7®¢}Æàáéá0'áî{TÅŠH1[¹ÆŒ¦S¤•Amß8®\R‘¶ëXìërA¶ÀŽYæÍ: ¢ßD?ß'è÷å%¼|”Æ÷oã~_VÃ3xÜ=êú·° þvìOíPKØ §_÷Mè£!üÿ‚˜+\èŒ.e`yþx0ûÃgzaÛ§5¸C!ŒM¬É_¥ï¤Æžùý´W°YQŠ+L»Ù³'ãí›a´Ðð§ÐoM®Ó³÷L's46À¢­8þ¤1µ6ØË÷6o#Þ/~±®U/ ÎøÙ¤Ø Yæm˜êÐMn _"ýãÚÁÐð™à¯sÚ¼¾n&Í91†½tã¯~ßdvI… m2)˜éˆOj2°á³!¢5„oøqž~+ÇÀY/YÈúW‚‹³UéÆ"8!£ººî¡Óß^œú ß½Ýo¾Îægjñ“ÓšðâÕŠþ3aï(,Ôá·Ý\ûþ-pÈV!-Zfüôµ5‚‡óã¨*qm•åýf\\m6÷‘ØÈ•ëçñÿæ/œSOÜõwöñÛ2‰ôõŠ#¸)E‰‡OyÃOl¾L7f.…8UKò¦{8·29A=u¨ÿŸáœ8ÝÀ…ßãÉb{B¿!ѵ×X¢ÉS®®¥JæbüÜ­XÈ=ëô¼hú:-ž•}§GÎæKU^À -î$óL;HuvgïG6èöâޏ‚ÇOÎõ{Ïò#–OéꨱtÙý”Ý!MS¬ ©LÎ*ª\æóŠô¿*$šÛ¼K‚ˆäÜÐ_ÑLÞ~ðšœ"˜ÒeŠ“?ãÝnè*6†wËÿ”… Š7 Òê¼Ò]+/qáÉkgÐ)M¹Ôúy$æ¶<‡Ç"uÂUCâá¼¥9÷“”áÆ{.ÇÃ1âôú øñc}Ù$ÇŸœÁ%ßÌ`±RJpÏåU¥ŽgŸ¥Bu‹2õÞ°N¨É¦qR¤@3Š:{ÑåJ‰¸lœ._J·î/á+õ²­MoXlTÑ|±›i[ƒ›^ôb{n/›v€¯Ð³áÒžÁt¦U[Óz‚Kž„гÉú¯¾MèäDAÃÎIb_†¡™6¤cU;Ìn±Áð¶(fIWŸÚŽó'$BÂS šûË*ŽÊÄÿËja˜ÇRÏqxø1œ?»v7Ý;È´+ßT ¢ºrSái­ûjarÒT¡´Ôp´%š "é_u;Ø^îÅׯŸá–ñ¸¸ÊžÖÉyÁý#èa¯F^"¼}Ñ9ö|Ø ½ò3jÁRêFî ÕámÜû†(‡Šaþ†¼`@ž ç»M]©Ms'zœ³¤šc9)œN.p †’^>fG ôhŠ€«-³SÖtBúqÔl©Á|!˜{•³ñRÇAa‰)lo·§n?ö©ø.ø½|s Ã]”¨÷u7î[÷?ü-ŽÃλÑ%i ~GÁ…u3ù³yÒ°a²6=8ì6öeiá2‘w±L•[ôæ;žWa¢“Fm]Á'¨˜qÑ©™´— ƒ?Oðçño`ÑØ)tóè÷Tah;ÝÝq>¨Gáó¶*¼å™FÇúºà‹³¹ôoÞYv“ÇQ÷z+ô {mRƒiæ`!ú.šD·F>†G2Ÿù„†e´ÇYœwØsc÷§¼ð÷Yª¯XέG^#ab4u²˜ŸkpÖÞ4ùÝâ9šW˜-ƒs0ãËÐ:—A’úz >©@`¸a(šf§uÏãù¤;èØòp@~¥':‚Æ­ÂþQÉÔçª1]>û ›}Ð „åØðp¬uj`·2+Ø×YZtç¨x²&W–ߌiÒ÷׉×ÃgUbÉN9|†§“:/ŸtŒÜ€2îÙ<o”úÔÍ¤O&Yóë³k`QÚ'p:ÔK¦Î› Ǭöóì¯Qcr:ÈM7áÞçO“ÁHö±`–Í=òp³'7+„ Ó_¢{åh:3ÆXÐ:–ey-¤YÏ–æï+"‹¶J¡¨± 7›Ç]-ŠðĦW8âUõ?ž.˜¶têä?Á¹¦bÜÉÕ–¦é} ñnxÐÒßú¿Rý>ú,d)+¥¨Àof·|5ê?Í÷Ü¿~‰v{Ó“&¼ù/,Iûr¸iT«0Ùå)¸žÙÉoŸ9 M’Ap'D–ªo’¢W‚ÄyëOª>y·UB‹ôê±.L€Ûwü€7N¦ôæÔ©•šDâ¦ü³¿kƒù+M­øI#x‡µÍ;þés›ðÏÞ“‚ãkTèÕœëÐÓJ¦zqã gY¤ÛXj{l2zX+Ó+²0yå;ÜPµ8¿€Šógà€e3úÕ<Á»Î3×%=X”^H[º$ñx¤1ý±ä æNO›±ŽB©ÇâùOö™eýp'j³DÉÃY8h‘!/ Λw¢âr1ž°¸ÅCëðó¼XX`ã©Ñïá]óRbU7”ýÜÂÊw †sºÞT¹ov»-!“ºn€ñxQòœÇåwÝÑã¤-7ú:,Äî wEœ´‡Ÿןê#Laϲýüºnövžw=}„Spc“<™FúÁKqµÚ„nþl£§£ îáköª¦_¬¤}%%ày&³DÿåÞÇ…î¤È8$«ÁMÍñé„*Ù›ô_\ûŽ%}Gj^~OGß=á(œ&§Ãɳ®gó_ô5uYYú%T7ÐÂ÷¾Kùñðô]ï{ì¨k9akðf‹$¢b»+“0Çû 1¡j"ç9`„¦8=ºð(ènˆÅ*A߸×áç¸4û2Ž;nH…gL`W¢^„’ŸÙ¬V-ý³wóÀ­ò\cj;ÑkŸ2w›+”ÕINL ±yz˜Ì¬CBÎ0›„0Ð\½‹Y6Xãªmu`–Méø1¢p(¶‰4o8ó6z±S_Tù:¿rPÌ’áTRNø«"÷ì4³a–Ütù"Ô_¹Ìþq’®°¸²›}5²‚»´pXÛëûw28„î-ýøfp”?ŸLFL¼¾w,4iÜ 0’&o‹çCcu:Õï¯ÃÅK³ô‘2ö·6ŸeÎB4½3‘ÎZ=Ž›ª®DÙAÁÝŒ±þáädÝbt]¦Å5ƒÕíÝ\ý`èi½.G(=æ ˜¾Z@{]Ðlj ¸ß~‹1QÒ|oï#·k3ú4‹œ‘›Ë—¿¢x¦Ä‘7¯…à=cPjó³JwkÔÅ¢Ñ]Øïo,ä®ÊÜöÚ||«,Â<æwba˜mÕ4áª!T*ö,åu*»òCÏ– ô»T¨÷Êg‡œbwÿ: è)Y…ÍáûQÍ, /v Ûà«ÃnõÎKåÝÛÜQD°NœmÇw±#øD™¥tâŠg¤ÓCŠ~®Ž?ÇFt³t7GQ=ë8ÜÜ; .Ùo¥Û"ZaÌ«%TéÎ\žÓÛÂ}ü]x÷Ä´}Ѿ¯"ƒJØ’]eš¼ÇëÑÙTƒ¯{³„éE?ÁGå^ðqĉeY`xhŒ–Îw¦ë^ÿ ÆÃLiÐÖ)ÜGWƒ_Œ– gÇð•.˜r¢l&2}¡Ým­¾çør‡×ûäVÕXʽ e‡mcø;kú)óÄçüïüè”LÒÁ6oïæ7—â1=7üù@žO ZMŸ¼‡ßoøxõjX´ ¿fÜkP3ÊúçMÆ×o5k\ÖΤ‰5 šÎîDæ^&9R²&a»›9ßå$7¾ÿe¸· ÇàGU²`o:\<{·Ýlµy—ì¦z»”d<ݼ Î_ÎÇ⩲8Ü ¿Ø{.©µáëo¤*Û”¯c7àÛ‘#BµÐÝ#'i¶ç@'p7´Áµ¿ê þ­UÓúû>Ó8D;ŒŽŒ8 »ï® £®L$‹¤áT|£0fÍf Æó/¨ðßÐ× vr>?HzUú¹ýßqîM¦=Ò”ç>_Ç7¿¼ïW¾‡¼ÃÜV… [Lé. Â]ߌ¢….à äÅNrtâ>/D›y܇£÷”ywTVÖÚ Ç­Z|5þ”0«·öV‡¼¦FT]ü厃i“ŽCPo:]\£F¢5ó¢)-³½•íˆ{DNÌâ7ÿŽû*ÃóŽDÕ¼™}÷­ƒ÷ó¯a‹á|,+Ð7?aÿëû˜Ô§‰×‚aC¢)¯|ý"~®ÇüóðÊ]W~î8”ÎH‡Ûs)tTÊ0µ€H óö&ãºaŽ[/DL£ú2Æ`™¥O/}Ä6èÁ}é¿ðôÈZQw×n;&ÔØŠíœáÔ')¨Xõ®ViãWEüZrmžõÀ÷©xÇé6ŽþO®jÁ+/‰—W ^ÚàÁ>)|¹|ý*^‹¦g!Å`"V›}&’ßÎBñ»,#ãĶ“CðÍ.†.m‡U×3°ïÐÿÎÿêÏ8"\t/ ÞŽä'¯"¹êý §h]3ï1CØ“MÌfΜ–ãC?OÞ 5ްó/"Y…¶_”ž‰‹&Ä“9’ñä‘ï(ZóL‡OSås-¡Aå¨h-E¥/Ý……õ³h²¼1©ús›Ž<¼0‰WŸ•ç¿æ]Ã]gÒQ^U•y-OŒÆÒMŽÇu²Ÿìä?0œ³ÀÂ'¿e¾¥O¿ñÎÎ@Ô”˜WB\UœrŽ‚{«üÙ*õMò4ÆÁ¼úQh‘;—«²féÉ›pÌo-h0„µÝçÐÿ!<¨Í~ò/õ­+ª.¾ÄÂ%ºñZqìXŽ]Çrk ¢éÉçš‚,ß:M4¨Žõ²±KôÛB`ן¿°,Ã|s/BZG±ðÕï«äÌÑMB{1mÌÝûEªPt«»·*ˆmN¹Œ[Žª£Ç7-ºÌH—=·V0cÍd¾Wü89>N„Ë'sAŒì˜ÓǺ`50Zµ¡Y(œš ÙM•pð $¿ÝÊôFm&4æ5™¢ðºÆcE^øºˆV¸ƒ?'Ô@ÿIªô©º3S„ÓV§  eWO³È/Æ5÷pþçO‚÷YÊ øjýüdðþDYѱ¸Àò´V*Ñ>·ýü .äáƒéûŠr\òÄS0ïö`€§ÉL;-[ðRl2t¾Â²²Ï}:™*è{ñG]•¸èåsôÈùçïNÍ3ŸioQˆ go Ã%\ÃÛH‡Fý¤·ñÕ[zÔõ-nÛkÄ]Ýø©´1|€C¢”ÜvÁ²ÆÜéÖibÕö—iþÜã_MìœvM3˜Œ}›Èî9ñO—µ°Å¬“,õ¾„¶ºQlrS Òy†f÷áfùÜLî¬KÄ-mïpýõ0<5Z§‹Úâí)HìÕÿÕÿ<þˆ‚ã¹xø¦œŽk¦Ãb™³B»w~ÇDž¤²K:«h±E ëÝöªÍ¾á© )v°s¾Ý ~;„âÍ‚kh~RƒÜ 7ÇÍõó!Xä 3ûu’T® ÁqRÒXfê/ñq¬€Ï–øÉ\¦•Á¤ §ÌY~!ˆT­©y_6f•0£—’ü—6{Z ÝÎ}ª™^T|2ÜØÞ]£¸8Œ©ÏL"¾ù¾H†Ï£DxØr!éWãèLˆuƒÄ½“phª¾ý¬G"[Ò§o º¾‰:¬‰D[Š¢ª€… Ç:Óâß ë{äÆÊj ¾Ǽ™ÌjÖáªw›xJš_Ù…ÖuDZÕ:O¾*e%VƒÉ=[à ç* <µi}tÌÉ%ÔJ_ÿêƒVKÎ×,Âïµ/ÙCWì>óÎþþÇŸ‡„ùŸoà>‰9Ø·+Ä]¾²s"ÏØŠg“íjvŒçó¼V/ÔÂYú´÷úGVTˆ·½ƒ¸_& 7*\oeI^[Ðs–7<Žy ŽF;©òþõp­'5Èã]5yºhæ<êwk8ë æ¼5G®mç¦ïR±bÛ t÷µ…*©›¸búK2úi†ïƒøGÖA‰½˜O3ZŸë€“ Áþ‡’p»ß!>aðæyèm2Œ^јÊ?.IÏŽ£ Ø›oeø¼j³ ‹}×M'^RãmêIÙ*dÌNðûö]7+òº_×Pjc2è­R¦Ù—ÃpŶ`8&“MÌ™~q Ž\wŒwʃe«ñƒår.âiÁÛf|ƒ'ß]y^ÀSxù` }¹ªŠ¿·’ÒÄ0üºMn@þÉÏæ vYÙÞ,åð»63oŽ´!Û«Jìú}¶Ñ†Š½4ÅÔ bGÒ¿ÞJttK*û›%|¡·Þu†Ù‚{LÚò[˜ýÒüäøŽmî0^Ò'™=††o¥äëqž]! ËfßÂ%i¤oå"ºþ£$¡'…²¯OÕôt‹Q¥1|V´8•CÁ¥Ÿ¥SaÕÛ :Öæ3ªï–‡‰¬Žüp¸ùþ#¸A­ØaB³,dñ¼­ Ø^=Ñ6°$í9ÜP ³-–áÍ´, »Y ×göàä|Mê'@«—@i¥2ÿ¢'Æï`ùÕñÿîX°|¦‚êGʱêc Ô®ääØk ¿ZðÜ“ äò!ֿܺ*Ä·tFÁPwC<í%Ê«£ÁÚï¬ý¦Î|QŒÙC=`A›&Nzí\ŸG$k¶@“¦2>úcJ}”ûX›îÚôq'¿²ð6¬Åk`¬óU®¡úBvwÉHTl^…cö‰ó¨Top_í2EfáXî=j+zc;Sƒ… —ÏYwëi\D|÷§rÒ,”ü¨C÷ׄâõ䡲™?úzÁÓ1Åàr†¹¯ne÷L%áfn3Ìol—N°ÿòªT»ŒnƒÆÙf4xèœÚ{•â‹ícÊ0;¦`x‹å,…hm%ÒHoÁqÛò\¸O0*¹[<¬¼- ¦¦2(à b»îÂS§$6dp™`Så`¬±=Šfžm(û‰ÅuÖ‚Ì 60ü ×[;ðyë}ö¸¿G ¥)€\»‡ÌaN?üZg^ûФû£¡üüA¼Å!M!މ¯6œÎ7©þ) õ‡H@ÂJ3X¦¿‘¤˜”â ¼“g÷oo0Ùß µ¿Ã%í7ªÞoÆ¢ëñ‚{‡Æ9×p¯}&ˆH#¯úçƒÓµÿÍ?{i·ó؉×apdyPØ¡¢ßá'3G_Sð{ªLUÏÞ€a^›áFC•©Vü;éø¿9È]ÔãÓ4÷óÓ:/Èþh®mŽçç^‡èíÙä°JÛ<¹2ůÃNO{Ü¿ð%I8ù¬§o@Õ¹Z„8‹’жCP´*‹[ÈIŒû?®®AOÞËüLb0ýÞR®2µœi^Ü3-w°¬Ä¿lÕf žìõo9•  |ÿ05üŽxIcÃìBµvàbó AåáHœìM7îÓFó¹Ñðݸïgoçu“6ÑGašôÊ öÿKÌnWæbXð;<]w½ââ™ê=}pi~i6Ž¿ a²X6K•fLÓ§c+ӹȹlÔI*ô»õ°” ¼êï>|~t/Œ#¯Sw“¶y§Áâ²øxO®dèP“W†xBf%?¨?˜ßR !S~Hჶ•øÃBM¼^“«p^¯N…åcÍi»ì:“Çíè¬ÁIÌlå`z1HJ˜_ib®0G,˜oµ³@Ind6‚Ï`bæG8ܺ‚¾ôÇEÊõèÚ0ΞÜï÷Þ…caùP‚ÅÛö€ŠÓüðÛ‘ZÍ‘Àßü@#+¼1,txGî„Â×tîI[¾ïéï¾Ú&ÁN XS*Š; &òÖ͇€iÃEt¬½'{ÌÄ[”qÌ#YÐxI4Op2Õáú¯=‹2¢T8¨ © Áœ:Ì Ncs¬§Á†±Sa³ …ì„ÇŽ;±K1^Õ¹Sæe™_PJ¿V>² ;„»Š“•»ÂpÞ( vÜ@­Ö‘àö,ÈË!ÿÖgã¥cå6O–¶Ãˆ¾‚•ð~… JÉ™á/µkä¸ì=0¶«þ·Ž à¥íÉèíé4À§Ì¼"p”éÖÆxrÕ{Äa0ÜO‚DÚkšÃ—ÝFSÂ&Ó~HûT™øãì¹áøÎ@„~¸×&Hêu§ø° ?$fÁd¹‰p´ó´ÐFkwÑ=:d#èkhÒ|Åßæ#^³Å=†œÞXÈú¾× d:³¡­ …umšJ§'òìE0ü‰&üÀ²­`5Ódm—¥šNÏaa|+©rÍ— 6üÒÍi¸êÂû¥t†}dº…â†3axex+Ì®£úZÛ„…C÷AòÌLHŒÕ€!ÊÃéÓ†Dté ¢«ÆäÀ¾~Creæ²ÿw¿(–*o†o³r!Úþ1³óHÆ]—}P¡KV¿ÈÃñ磊«˜×î"Ó ¨ÑcÞòi:ÿ¼ƒÁOÑ\–g Mã#øí!¶Ïœ¬žp§\‰+ O2®#Nû÷°)—O’[A øÈ²®÷Èzm!¶]ñ…AGjÙì •ôzÅÝ:Šo"ÊçyèÖ¯Í$þá[Ô.Ì€±O>‹ÃwœÕ7^Äù€g·$·NL£Ggê‚áç©\ÿa +Î0JE»„`2û˜¹-¤ž÷Ì0% NÿZ›¡þ‘ç0ª±q@þ¸þHhû³+".bïg{0O<#6µ“W:†XXfŠfÒá_n@ú[M*'xÍ»2Ø¡æÛ$ø_Þ`|?_ù–÷xÊ~)\sogòÝeøØ¤—•îpå·Ì$¸IâYæ×>‡VHÒ'#øÌ–ŸlþÊÛx5p0I¬Ç†QäÓܽX1ή©ÊñÉGã¡ùŸ?Ûg² ê›FÁdOYêa@ç6Ù}¹_ oŸj0£q·°:±…ÇyÂñ¡çQtÂâëþ¼nå;‚ãú`PÖoÈþÙ‡ a£É;roÉ(˜0y}gG¦d.dÏïlÿ‡u›âªÂm t¯O lþ+,;å€UC¤ÐèC8Ä…1ÛGvP+v à‡—+xÍð!8{Ç>ú«{}›<›¯#O¿E^†U/ªÛëÚpµ]!î6èb£w€Àƒ+i¥ì¼\oËï§³ŸÀÌEc¨fM{'OÏfµ? ¸w¢9Øz+‡Óô߇Y5[HÃÌU(íz€_>‹Ë‡v2=ƒÁ<ÁHTÕ²£ðê y.a7þœ™HOï°c¸—úxš­Í®ÇR‘µ´ã 8︱˜[-ñá_¹¬ž Ó6Œ§G'Û🅳«pOÐv÷|òïÃËÑR4×T3ªÎ¡¯Y ¹§GÓŽmæåãâ)_üEñÕˆÿþ} Æ„-%ïæaú¢ ÜWåé¥Z5ÐtXn?ý‰üà¾UüðáC¼ýƒ–ôò¦ Zt‹p$}y9”-Ý3?´l'×~ÚóûÔ¹˜¯!ç%‘pûØ6š;Gž¥&·²þ¤}Ðs2 w{`+ç|@û8xtæ7¾O"áǪa½¼•¾pjœ:È‹‡7!§ÓÖúS?šÑ+žáÞ\,X8íí„lE>¥~½{2‚ ‹ð7_Í`¬2-œæDû5E¸pB=^±½CL[÷£Ç¶Zø;{8ú¡;Ë\øêïCé•‘¿ù$«%ÂõvÀgÍ'|YÀúm‰ˆÜˆÿ&Áµ¾Xã¶ÛÕ˜¶wlâÛbø<#eîsd4鿳6Äã⽃kÆ\ñÓŒ¦Z©Óݽ—ØW6Œë/MÏ–/&ï^8·2ážcnýç*žÙýïií‡QÂ_Ðß¿‰Q»Š ò§Qá¢? ªëGl⎀A\½~õU¹| w«,Å3–ÇxÎ04x$ÇË@úƣΆ©…Cÿ÷üß97V  ~’ò8ˬ÷:Ã{{PlÎbGw¨Så¬=øíÄUÜm!ƒáfídYj4oZÿæÈ'"«ƒpü| Rø á@ 8ê)´ ΠJR1fŽ< {á|ñL\7t®þ‡–Íâ}9h7D /œjÇÕ }ІÜùÁ@uÉet»¶ V¦MƒòÖVôiù…¿‰3D¾Ž‚]®Œ‰|øE¾Ÿ<¤@{êU˜%v;Oä/v>EúEÊÄA¡‘½vS…ŠtÐo€I›ª°5¾ ?¤ÁEË£pÃçô8+C®¸8Uô🅤=~88ÔîÀ±YËù =§H¼ÁÍoÇÐ@±78|Y·5Je‹“¸bÖ\f¨Fç;!nl 3·×±ZËpü¸M›h¯<Їf.½ÐqH4–šä3ÞìiÆaÛšPÏì*ÞY8ˆ[è„£´ãiŒz{‘hÏ@“ ʨéÝ&¬¡{°] À»†ýEï=«Ñ-ë÷ÅFÏLÑÛÏOïDO^“ý>‡Åá–’$—_štñèÔ †ß$è…¥¨<.È«¤BvEí.;¤úŽÕ,…Xs\| Lj• ôKÖ€ÕSíú§ç÷3dWSAWñ‹cÏ\0ŠW.þè4–t¤ê ?k,àA÷ è0: a¿¢­;—‡O^ÉÀ¯æÓôs:ÜÜ7Ž6•Ï¢òÔÉÙ !` qÛƒ¢£Ó+È å/ºäX†N,Ti…Î×qóÖ£äA¸–¤ßfK¾ÉÒq¡î•)\Ì鎜³s„æ,xÉm8?yòQùôœ.¸5äÞHDóé]X²Ù™¦J¤âÍáÏjÒM²„-F÷ ²y Çf)þÕh$7ù>´Ç(Ð踳ðVs&”Ö_€)ÆÙĪÿÿuq‰Ý>J*ª€»÷y¨™qª_`΃áàÞãÎÞ$¢kãDê<Ó†é›L$ZC«¶² Ø…5î=KkGðû/¥øªD5ªžcðC7¨ìòÅá=¿áÀkUúö¾/î¶ËÇÓÎANwxJï;þë$3Øï³Ó¨‹?¢›ÕAä^ÿq–õ´kµMtº.̯ñÃñ¼<JóëpÅ–×pxáO–ë…©ò0xΜ(kLJf‹â\ñääó÷ ¦îÞ® Pfô}r¶À'% ÃrÛ¿²—‘åQ`unß!²…Ö?ÇûYu› ò9:–Ä ¶7Ùð’gµì@îH”?¶÷=y;gÂài’|Ý0=\‹uÊÓµ)Ý¡ Ÿº Ïø¬»µ RþàØ„ûøæãœ+5‚½´µÇK綃XìdèÄú7ð¼g°'c†èDº·ÌÌ™xzˆ3…$$a¶,ÚT@û&Ò´obüèõyàç÷šÍ•£Åëï°ö¥|J±2U^6œ?KvBy¥0(“1ÄðC¾¼à{:_«82 p¹ÆOør$ ¬OÝ`“kaáÅïPµ{L+²¦ó‹èþŒ‡ð 3b€ÿªfLĩ˛XAÚ6ú;ô-Úå–Ó%¦ùÃ3å0]˜T³)ð(ôåL@ßÍpÄ!xœ’ÀB7Í¢;ÜÝaùƒ¬%% <£Lac‰ &ž×D­Ï^xëÉQÌ 8 êÀxQ9Òæð ½Od£7ÖÓÈôBFW^RçûcÇzs—û ô}ç²Ö€z¸õ¦€¼‰âoÔ°~Ï1Ô$l/l¹rž¨ú/ƶøÚ"‡EòK‰²ž :]…Ýc¥^KкémX½ö8žÛ8’þÍÇJ ó)'Oá… )Z<·jÖç‚ÇÙÏlõøÈüƒÖÆ ¼´a¢y&ù="ç4ºÐ3îgA<_ƒCÁÈš¦}átì»88´$ŒªÏŠ R°ój8t˜;NÏ ­–ñ.I?ê°õ8d*ir}Ññª #ÚíÀ×ôY9ª–LEë²Þ@Í+ÜRy†¹[|Ãy‚AdÂÇCp§ê.Žy=g<=†söÛðÂWNÔN¢ ò}A÷H0¼éÈÆÏó²æs ÔYÈžm¾—CgR÷ Bzq¨-ÞŠ•ñçñîÎI|NÆæš¯ç–ÀÂU8íæ#èÆ'hyDŸ»miÀÃùá©ÔãÇ‹û·’<ê]ºÂ_ #a_b/<˜u¢ZMYR· j¿q!}ÞóÈâ%±øqš! ò3Çß’MX½ëÙô¥hÏŒ#Çå©1­”èx.–¡/ ßÀÐ-áØc;‹Îª¦,¿u%4\ãe«’Ù{±ÁÐØ“€ ˆ&G&Ò9_Õù”Áó©åÓ\Oñ9¾ö8€¡/rkëAÔ(e$_d.É-Ï5lÄexœŒ)ÚƒÞ+#`цËð|^y¹M¸2`Øš hÿè´4ÍǽW•¨¬u=»º' 7Ūñ®7b\¹…azw>½Ý‚ƒÆóÛ›þwþýÕeºÑI¥ù+™0’;}y)¤è©iú°Tv#fØ„`JÇ8ê=‰*§{ã1ü™ØX>ûG®X›€ó*Ä`αdò‘FÁÖʯ¥†`Ž%L_Fœëבôi6ÔØã º-ÇÂTþñÑ¥YäÁ›Ð,’‚ ÙîÐ(F¡ó7ö®Xˆ’«pêÞ>æñ7IЧ)Í-¦_@ãPi¼ø]7iKÒ©…‰,«? Lÿ,‡kûÃàkõ+XákǾ6isט¥¼bu&v¥ÊóÙtîeü·W3’:éFò8µ}äVn6œŒ= &î¹ò¿.y@BÍ¡²qŸ`U•ÍÐWäO*ᬧ±®¯î©›óÔ“"Ø g#Þ’¤¢_åHq*}îÛkÛ  6ðÖÿ3‹.±#ÇJD©áÅBs{O&“k¹Ó±ý¬Ä,Ëœõ˜¾ÛK˜ÿË _ cð”ó3è¾”!óíä[oƒíOÚ¹À»d¿á„Í<„,ýÞ8ëK6ñþ[ø´yx&Œ"IBÍÿw¨l!NœÀIé«P¬8ßË¿ÇïA=A@&¦+ì";zuá\ºÿàu›{LtÄw¹Ïàü$Úê¼ÖCïÛ{нN`•ùbvÔ¿“é÷¿) ügÿ{ý‚×E{¨±•)ÕRÒ‚EäáL=zsº§Ð|ôj44º§†Îçw_Îçr-xþÇZ˜hŒbVÑ{s&Qó 3ѩģÖDó©'h’K/ØÞ¥{žÓ÷oÛ0[g?Å@~TlZt/›ØãùvÜZë1y64ú_7À‰¸}üHë!á´ßˆÊ"|ö`çû2~¿³g! <Îÿ椒±… ‚¸G®ôCâx¾6Ë’+?Bå Ú¾¹SÃı³„. äÉvô9X„n+п¥y¨nœ/Ð;I7±Ë´ïd?Þ7ÿ ·t@±?ã; ’žÌ¦‰ÃÈ4ôVªÇÐÆ!¼ýÛZªqh< ¾^ƒ!Óþ²ƒÜ‘ÿù³‰‰áºlƒ¿¼[Ÿk“Ôôã$Z"nÈã[“‰¹Õ8Òò}—p…™*où9šß=®A/殄¨o±ÿr-á®=ƒè†¸[ s*†nïµ€(9~Ëè+ˆ§8Ñу‡Qß°\Ójÿ¬ýÃp¶ÌD/oÂgô‰¡ŸÁQ||:‡îs3!*cæòðOà/KCMË!øÊQøõè§Ýž£âØ1æ¼¹_7Žc î]ï=µø4Û¾?›­§×ak¹Ýiõmxï¾ ãõj»ØÓsÇ„úëØ¥øåô×£]ÿw¯ªÕlÌÌZºÑ‹Î± Å?ù†Œ=Cÿ]K/¨Xð=­[á^j+ÎŽ½‹•bÀJú.þP›Ì3g`Új6ëð(z8l ™u}<=—‘7î¶³ÕÚì¨Î(.c’k2µyÖÝ»Ì`–?™¼Ê‚ã—$8ðLw þ·`… ¾l…*ñatÐ@•Ô¨}>þéº áǾ“sÛ[·)Q™•àÌ> ÊÉð‘W‚£ŽÚ¹‹€£Ü³íW_Ñé†îPžÙg“Áz)ÃA‚¸ÛýøÄÍã¶ Æs£l þìÁ òÞ •ÝóN7 Ét“0í×#ðsM8-' kޏÑIã×3áÙxfùTŸ§;Nó7w½¨qÛ}hø½f?˜D7Í3…%¥á “—ƒçù#Ø5·é<ÔªcÜßI4qµ(ÝR^ÏüÓpü#]ºØ_…ÏZ°Hè(çm·@ï4>þ8d@~eÿÅhzüVþ#…¤pºÝäûV\Ê|85Wâäi)\0\Vãï^~ƒ÷§íxæMWÒ7,×>z¦o¡¥'£ÉB­Z>O÷*ô–둟uÿûý‡-‚‡ ·úAÖv"~T‘»+eÀ­˜÷`2õÈ] Õ×cö?]>#\Ce›Áó.G¡ð&^Ì/Â'&ËYïÊý(¿T Í[ÿByä>D­äõ ¦ñE oôá8KÊC`b̼«Âaâ÷¡,HkÞQQ¡^»~Ó}Þ57“J»nÉCÍAtª8Ž)³Ÿ3ãÄ¡ô±fy •†}ßLTó9>7â¹T H¶†_²…š¦[aö¼|W’')]€ìð@'Õ?)ö|‰§ŒL¸M.ÍìbE“Öóµß§Óï_gàræOÖ¸g'Ý'7>ŽÀüó¡¸uª•ê?—W2©ÆÝ<çÜZø±ÄŒþÊóÀõ•›hÞsZúb½+÷æ}™ÊÍ-…1§¢fÐY,'ü™–.e¯lp˜i'Œñ˜Êùw-âéX·ÕRè^ÞV3’'^y¸þE)>q»[[1eÖzç+SÖ𦕠•¹¡+­×à/oy³»I*(±ø ¦•cƒVÝ¿¿©L6ú0H„‚^3nüi ÖŽ CåšdÚ¡/¸iê[XþUŠþ÷Úڪ̿ÕÿB¶OŸ>šŸ ÿÝKòwú@ýoÜsEôz²ÇeÌÇâ'¶ôÙ+þåõE;Õë"°X#BP*«Æç~»ÏÞ¬Tä[µŽ¢ÿ¯ØÙRG¦~Öïúx@jŠ~Ôe~¶¿ÑòÛtlM& •ÎÔ¯ êì·ÁëZ9q±GbT…wÝf‚ÉéA [±ŸÕ_á_£Ñ{ã°ðS"ß,§Aæ%iÚ×-ŽËÆÐ×çmàZ©"»òà)ä}ÏÞ1.$7ˆ"«mÀ_Ž«A0ÔW4õàwÕJäR´fÊfðP¼É"· ±m¯¾Ó¿É.ع³w û-mÁoKarÅ'¢xj€ÿ= œÉREŠá\B¾tBlNÄ\Qp=ü-Æ-ܼ  µý$|𿎂=¬+S€9"èm6¤ä¥ ªÀ?´' £‰ï‡áÈï=xk»+ý1~=NèÍÄ%û» ¼À‹;[ï†fÓeì®…ª@p·–ü}˜;¯tæQ7Vò †­ü½nÓ«¡/F¦×šçP×à&x­UI³ËHEa‚ØFœw­’}5*Ó¥½D56³_>†Ï]3°ì͈ iÚXCúÂXkÕ4C D©¨‚ KUÁ'ƒCæ8f«¦â?¡Ä÷ú€üÊ}±½f­fò™yçéܯÜâ®}â{‚vO …Qºaü|® iÅc 2ZéTèjD/&ÐèCaóš 4ûp½P³=÷îØKwuÎ㱇wòð˜‹PyÃSxèz ´<—ÀƒÑ™ôtìuaÀ÷ù!Öÿõ†dß&L›»—ožâÀgŒ°†Ø}³hЮ$Z8y"½?TŒÎ3;n‡xáü$Ùy<3ˆoŸË |þ†Uû±x†Ý†çŸü©k:¤-¡_Ž~ÇìoëOfzµä¸¹=)›´Ôîá¾çäÃxÀÉ:›yˆÞ”Zâ0À|nžù–÷¶X]üs]ƒZÒV°ò*?ýEþ·IËãÒÄõ¨åûáù$žªU§= üÈïÅdó,œ› ÆUS×Ó »]4\ôÏÿ1Î=y…qyð!ûÍ’]ðÍÛd<¦rOP>9‹|Þ²+‚jqv¿Ö–Ú¾cÆB:Š–kÀAê,c³%›«6˜Î¸dcká¦ëà÷èy^Û©Çñê‚ëÂÆ[å°.·7ßÅžãgžUåÑóˆŠÜœæ\$Üï>gÎÀ³û&á{¯hXôJ’¦ëIáúó L³,mMŸ±þN4×@Z‹uyñg‚þЗØzâ0³»¼ î˜Ceö ¼ô/—žê|—?Ë€ª{¿HBÚ`Z[Zƒ«í¦ð‹×Žã£Ü$’hz²d þµ¬öf¾‹ *~G3yykç´>ï¦ÂÙ6è±Ã DLEyôë(ܸݬ ,ƒ½–£èó»o`éüV¶¯‰ Éhº,”T.£ß<€Kët`Åü(\tWžçÍDáiÅY(5(eV9Âm£!ÜÜ„’É×ñª¬u”iÀÀºN0° É;xìÊN:䀴šªQ>Š× ¾àƒWötAÃI»±Aç@âñ6–ÝiqÞ#I¨E%L“^צù³Zÿ ôëŒ(²Ñ^Oúú`Ò˜õ/~…Ô~$añÖ¨8r2/w|Gwæyäà€ý77xàý›£ðå&1Üsþ.V[*ñ¿1!ðï=\;žEIذæ&éü} _ôž…’ÃyF²7ø¼PæÛ²«‰ÅÐáìŸýw <®†5»›‰¼â¨™hwæå¶êÊ|6¤»%ë}áî”Á\¼r:tì=Ê •Y—¬>O÷XL.&9óáçtáà’§(·§•\¹{|V~@¢5惽~I½þ˜ì¾µ”½•Ï´Ó™ ²Ëäè“#_àÈhôœƒï žm[U|};F]½#Ÿ‘­‹cŸÂÍÉ X¼9w©E³m#|„ªÙL.ìößn2êYÄÁaÞ:;C n¡"¥'؞ɺ¸jðgxâ •Gÿâ™yp9¨Šìˆ9EŸ&ŸBoóE°T¦ˆÅ—,òI(LŒ.#«öêCØž1øvÛX*õf9]o-ø!vÚ~«Ð+}*7º¤þå€Ã~Ü€ð‰í`¤j Lv7|´_I’ûÖc©Ã]Äq8N³ˆêyn¢;$—1Õ¶8a¾ÿ®:±·ÆšÂÓ®(âõ` WߤÃb³ÙŒ„¢SÁlÙÒ6¸Q!O•‡Þg3Ûûqžš$\¸‡G†œ†È[±  ÐÖëbTعm þo :£—dCOì=|Qàƒo¶ÝÂUžtÕìythÒ9y!Y5V–ýK”aêeúcý2&þ/ñ­l„Ñ«“YÝóüsÞŸÿ]z‡dŽ=ˆ’÷â¯ÉG¿8ÞqQ‚­2õ©scñzkTí|Œ¾ŸGÓd…ÏPøÒöùñ‡§ôèË¢GО›CìšEø8·tÜÕ©H½S¦ÒM/žzæï"êß¼qÂ’a‚uOë!Z ø4Æ:÷¡úó=Ø¥ÐZàMù%>ï”XQækEѯA˜:&/ç?‡µÝÚ´6OÈŽœ^ÁWô^Àƒeð_¨kŪœ‡´WCéNÏlH\<§Û-ÆÓqc¡Êî<üc\d üÀXwh{æB·™ ôÆç¸Éï5´ùe·Ù1º3äA«0‹É¤€ñ¦pöJ.œD.]âb»qµÎ`:É*÷ŸŸGWÜ®‚ Kü0d†}5aþíëåཷ—ÖWǰ¶@oœ†ý/[ˆzQÄ]DZªo¢µÔPžÓ®Â=ÅÎ Â}¢UñL->½nЉSµø‚< è6s€mÛ¢‰¡ûH¡ é+EáÃaÜmäfûÏv¿t.£Õ3†à‡òA|k±ò€ü¸8Ö7ù ­í`¹{=ö½Õƒœ¿ƒèMi´& Æí:ïÓæqÇ÷ˆ/îbXmšF(ñ ‡³Œ|ot±:2¤Ô‰Ô…(@ä¼”2ƒÏ}ûñꌑÐ-q—MI9†*¡ðÁ‹Boº:³Päÿ×G=|Ðp®¢3ŒWܰ§RñḫÈŠ%¢Cæ ÆóEžâ´%%W{Fà2l:üfVªgý{°gÎ0î2ÍRhЮ ááL"è q Ò°+ÈLĨºa<ë»&ެgJ‚R2U[ù¼¢w@Á–Ãpz¸4,nà¿/º€¨~~o¹F•dü:xÌ ¬)‰è׊O–ŸDû8SºLþê.ÖÇ…R½PxNøì³#þ§{ŽäIué™­B¥Ÿ· ÅäôŸŒ‡mE²,?æ+ܺ˜6åÊŒ"û> Ìln×+é|$>7àhgJ‹—Žä)©ŸHd^Ÿ öƹšJ¥ã({(íû§ Žö ÞÀEÇ…±i‹¡àIiÚæ ׬úa¶}PR¡‚—†NÆù!†ÜêS2öéÃÛ!]¬Ä*–T[Ý7¹ tËG¡ï_e$K¡êçºø§¥æË®Ä—°œ xýJ4\ôÏ„=Ê´·6 …–Ã1åž]`gq‰9Ì {¾‹Ðm×pÚq%ž"1½£#©š´ÏËýÌVvO&õCðwØ!HèX€KÆoDZ2‹®9"B×OFw¨_Æ1cãÆù¨›^¨R­Á*¿ †ÚgLÌ'š§Š@Q}Ð=ØDʸý:r‘Dÿž ´¿®ÁëügßX‘¾=ýÛåŠÞ£±bØÇ7h¹ç%«°ÎÃÞËÓÃêü‹Épœ2ÜŒ…œƒ°ÄBŒþ}Ü~ÿH ^²eÁ€ü†» µà ƒ}È™éÀýÀ ÁŒ;äÝ𜚹¿ÄÁ#hsÛˆ_^Îænú£øa‡L4vF_Ö…µb^ìÜÚÁ²Åû1|½&⬱ùbPóíËb¼ö –?½Ct‹ç`UÒD|7: 罋Ƕî\ê÷ ^{]†n™˜‹‡]nÆ‚Ñ5¬3Ë.ð­8=s¡ðóôUÐ8œËëöbÞ >Æÿ”ø/ǸäQ|ý¿$¸ò&"‹çV­…M›kÈ«‰XôøY=ï<ì§¡Øï&Ê L6¢NC ¹üÍO¸€xÎ;ø-þb⿈¦ 3xXÊÚ62Ý-g1ùX+›™þµ-W„Ê‚{ÌûG˜°ùÏtòTÛŒ9fƒŽÑà{¹S§ocê^]02W÷uÍFñ÷5B‡5cx¥K.Ô˯qmÙ)ßN Y‹4]Ÿ#~é ø³»~˜À)Kµ[oBšÆP4] ‚ÁG°Ÿì‘M.ŒQ yGð|èBž¸UþÛKäŠ\²fE,QZM.E?a$Ÿ QMDëR»ðßu8êQ£­ð¹=½µ3ò¢ï CvŠã±«*4ø·1zä_~MëcŠ#‹ðÇcèyòa þùÆØƒoB¹e·›ü×{|Áµ›Lé”ì’Äâ7ˆ“ö öêûð‹|o7êr‘Ÿo@¼¯í sQ{é\ŒìÄæ}lcŠ$ß5% »/¤“-ó±Rw:ŽÈ·‡àIÓáÎØb£E §Á÷¤.qMT&§ƒ­@‰ÍSë&z7FгÓä`–UMÍÁ“ÐxGt.ü'üjQ7;ÉÎHc7µ/=¥2Ú¸0*%§&±òSu}…¼Ÿñ…$Dݤ™DcÜÕ(QCBB°­ièáÙÓÔwЧ£XÈ©@AÕúVÆR3`âÆ‡ú_šaׯ(AJKÁ”ä¥[h× ,’;f.›®‰#ØÕVô™¬À7‰$b­ößX­Â÷7ÿñüŠ(>Wƒ®6™Æ"-ÄÀ>Å–©wªïí%¸µÿ#ú…Yð‰Ãt‡Œ‘øD?†¶,§eQœ#‡‚š+¤Mÿ m¾†ä^…q®³è°ÚQØY3 Ÿ•?kíb«¿¼f9ǻ٤¡Ò´þõjø°l.û¼øÜ}KžO‚© Lú‰ /÷ k[H½¹ýr–×”;Ÿ‚ãÏØ£uθ~’ 4ÄÖˆZ‹ëV‚ùþEüÉ|ÙýÏNßÅÎ4©aNT Ü›ñâÿz—“ÏtÚ©”Ûâì©[íRçÇb’ȼ½Y„Ç–àšÛŽàÕ¼2aý ²OI 27á«oÑÅp¹ 0ÊžÿZ k·Ä¡ŒÙ¬W¿…‡ç-Åk…N˜]ÕƒQ“FÁ5•Ÿà=ë:[£8”OøRŒ—#Ãü_Ð5‰¬Í·D%û]`úF’{‰Ð÷³¶áõ'j KêPjF&Î=¹úß¹àš;™øV­Ì²²j6$»@ÇG˜˜xœT³˜£™'~SðËgK|Äýýò÷à¢ó8ÑSK¦ØÌTŸÖѬÉêøÌÒ¶œ:µæ‘cþ7ÿÌq”[;XäŸX`éB#>ꀿޱò÷gÁé½¼óøbNíäUÆÒ4#å6Íæ›vjÒ‘4r¨m yU@˜R:yöô0DÊ ¢ç€}‚ oÖ‚¿½ßAB°,Èœwæ‡ì{19x%|•[Æs¯œh¶vÛ4Ò•¶„ÎÊå‚_}88óªÿã\6Φ” ÇšrmÞh}ýŒMÑZì 1’_K½½ÁY"ÉLñº*L]=”Ï XÝA×¹\¹ÂZ¶R9å£qþm;ž•ùóR´`ñð*­mJƒÏoGñ!®è'¨­‹ÇÚÍâ`¿} xtÀ½LØqéÞSˈ·{ tbo‚ÑÏ!¼çq4fžÆo3­°ØäFÆž&÷5.°_f ÙICg…@Ô÷Jm-) Â!;À…V_ïƒÄAáh´ºK¾¸ªVžÅŒŸŸ`¿Æv˜h劳š°k¢, ç¾FgÛ3•¾×ÀbÇM<̓¢Ä,2-g9_•ßµŒ¬O¿9žãíØWp(û/qU#.)'á÷Sþn\4:Oÿ%”x— Ñpˆl¯ÏE±ï²ëK*d릢|Kl‹æ,XJ³ nØ¿ørô¹fEܦ%ÃÙpœ;ï9Ì~~EXºLoZ¸ÓW+ôÐÙÝ“í(±ÆQs%¹ˆn,Õ=Žg6hóGú ë?¥£à5ÃÜÉ—‰áEðr¿$TH<‡3Xå )¨¼l2þþLá×’‘tC÷æ\¯Cvµ’»,jŒ÷±¥S@Èw³¾ “-ˆ§üpyž#”ËÝC¢7è±ÖGñxðýKvb\ ¬qê ¦_ [ĻǙnȬ†B屿ÄýhŠ„Ûóè ï›Çß"Ï[5¹ß´ tHçߺügwß4 .¬×§Ó´âÓ+Îþÿ¿µL¼a›¢“qÂà:æ Ãlcø‹†øýÁA¾Á7vøÉó¨Õž4ºP…×Ô.ìšJûìP-术A~±B+';àŠÛBd…sy¯†'íºÜNìC鋬lê<´Tø.¦öþZÏÕ¦ŠÓ#ç»èé5'q{l3øø_Åa÷É­01zFN<_/£.¨Ñ¾íh’ìÁ…¢að§×ðäÐì5õ¤qrž¼pÖ|H]µ˜N¹|uºTøŒI!4@ßFùñy‹¢™œYDMeûnvÔ]—ÎôœÄz¥ƒÛ6=ê:]k ÿ1²+ƒkªûkŒ—áºmÛIÎ’cXÝuŒg¬#Õ-ƒéû’"Ë»? Ðû%ÖCÙ^yw°=m‚âea¨dá ¥R'IµX¬ôÁ‹¦ûù®B™šiÃ÷Qé„@zdP#Üv£ž¢Y}ÐÐ…‘Cž|OË 88f¿˜oýlˆ‹fº½SQÉ  ŒÜ­ØÅ¦lT¸6=7އ«Ïnà–‡øˆ2)ØšÙAÙöÙš+~¹ö'Øßï)àõìøÏŒáoÄ¸ËÆb6æU7Jº`êy1únM<ÔüÜ úJ-°bír {þ²%çêøÏ¹ _˜Õ;UïD; ë~7æi’LkQúHr5¿ôx(*/í…K²id½úæ{À}VHèiq³‡>ZD“þI¿jcGÂb³#ôË€¿k@ºd;Ÿ˜ÃÙ³ƒ I]éÅ~ÛMÖ¼p;|sÝ 'Ž=CÉ#˜ùz6ú.)N*ù ¾—ó!á‘L$TЧc!?\nÁõ Òëà ½5r4Èóá¶(¬ÊoXÕÇä»ÏàùAõÂÿöm6ê4Îàù) ø½´ˆ…í¾:àÿ·W¹K°pü Áâ_ÃÑ·*ÕŽ`ª« ¿å*OƒJhµ ÒÏ–ÐÍÁ{àuB=þ¤Æ×À…ƒ3èÃß÷ùßA:ôLéfÌŸ¨‡ƒ3ø¢µš¼lÛ ˆ”ýŠsNœeÚ!b<¥ú.:«D pÇ.¨©Ç+×7ó:ÍìýEúÖðdøËÓ«³2…Vw{HÓ­•øk±WM >yÁ 3# B‚¬hß®–½Âæ©Ç€µ ¾B/nø‡ÁX;¿;úzÁ%ËÃ$þYÔ$u:mÅè¢Ô€ë´ ‰z3šK†Ã6ãÿÕ¿ôí¦ðq|Tê#›SË6ÏåÐ53‘JMs£îܳ!ðù9ô­GvD‡w6B ûQXpq[£9µ–ðÿú¥GšêÖ,^Þ5™+¼í¤AçÐ6|ߣvŽªw êƒ[Qãw¼ >vh]x‡ãŸejÊxÜûà°_‚Å¥t̤dú7ƒá¼ÐÇð@:Žrï¾{jüÖØg¡VC¶Ñ}Ú"tÈп¼á2gyq®[­Î –~ÅÀ¾ëøéÝgŠOªÑëÈ&µIc¿‚½U¹´`9¾:¶,‹aëL[Zù÷ïÊ{(Œ-Æ'?ü5:ºôM¢ÐQ8â®%-÷šÂ½B‘ùö_¿ô>>H­wvÏÒ`³:“`5_̼’Fg`=Š-N†µo$ðÒŸbhÞ,.1i*p2Ù¦Yr´I3Ì”TyÛŸâôBðá´"¯v¡W‡ñyŸàç(CÈ1:Ž…KÐ{ed¹IÑ„%B’þô%FÎv†‡£ýÂ:ÿË¿v5 ðâQ ¶ÁûcßÔ¼qÿèò®ž‚­#@oá|œk£ÍßW2vük$òÀÛP¤+àûcj°~W”é«ó¿Ãç3ùAƒIªËº$z<-uýÇKê)š5Î…z5èjdJ½ýߢAÕLéáv; ç0¶àè|°o.†®â²?œrßÞrEX+Ìma4Ìš¿Úz®¦y qB6mÎDm‡ßìÔ Op(úÁä¨Ô²ëùqü ¾~ï‘í÷_°ïN…®f%š­hfó/ °Êod›d`NÆp:$ñ|ý®÷"ÄùÎþCð=óyø·$F6£±Ûo! Í.ß4À¦Æ»+/àQdNïèÓ·oÁ0t(ï™!ë]äèÒLj8(‘¨sa¡F“˜¢ÁBPY´6Uv G¯‰/~Æ‹;k[µP4í´Öf»½e0oÛ|jÞ”A"¬âÑñFŒüø]c¼+„`ùLëF Nšq}$í:Bƒãè} ~p[óx vð9(¤¸íR6®Ý¹ Vå™Â¸ P—†#_Öl]§Ãý§#>…¢hL?7œnþjIÝr¥Áôù~y½—0n²íF Û&|5_—zÝã÷T£µÏ¸þ3ýౚ=]dÈ•Ãpñï,¸nÄAc¤’áødçܰl²0}ë0>»Ÿ !>êÙl±Rôë§Ãÿù ÖãTŠweAô ü «MC!¹@^»;Ó™A÷]¨7>@rráüÅshöf.3Ý” :)¿‘üÍ&Mâ_Ùƒ}·™ü…'P½|=ÔÇNÿ‹#à”5× ÏÃ÷rÑѺ›Ø+øãœ"%¾Ðéü9+ÉG'ÂĨÿ~K¨VÚ¦I )lu¹:ÿQ›ŠªiìËºßøØ¼VO¥$ðœ.]8…göîß]A}Ìžgw€Ž/&l‰©¢P0!=`’ˆø´Ôº'‘µ*ïp‡kjÝÛ¬‰r?`ÐÉ"T)ÏBß Þî×¢¦SS«·ïZÉO†àl?E~9~«¿CRz!úš96˜ÈÓ…‹3`åG9¾Ç¦‚4îMå›_šò¤Íëpß}šüêÓþ &ÉÒ “þyƸé *M‚°—ˆ’ý5aËŠuüÿõ5Oòé çG ÷ã¥~ :ã¦ý·?ÁzƒJܽÛSÁ¾ˆ}dçþñ§l p^·Ç‹°ÿº…Ýl®¹?,Ø£ŒÓ×<„Èð \6‹üš'‹‡zŽ`¼š*uâß ²<5!•VxúñØæ¥É‰G·×õö.z=š_w—ã5ý²ôÓϰIAšžHZJE–‘‹^ 8|Q6~-i‡ºcpöc>yµEв¸åÐ= Ð,»ç@ÊÂ{8"p5 ·&V¥oˆƒ„¹4M?jx ¦Z5$óŒWhT¤Ák÷¡êM ø µ²¼%¸uœLv)CIö -‰É]}çÑè:¬Ú&Ô`æ¥2|Ÿ¦MÛêNRÁÌ,öqéñþ»®xˆHCÍbÁÖþ0~¡"þMÏí"üØô¹«„˜§ûXÀó&lX]‡3ï/b›/«ÓŠ /QûÓXºÕöí5JDãúY°Ú·N¼Âï_)ìëÖw¸1\o)GÃå—!Åa0=îЄ#—uC˜Š>ö^ÄwYÇð‰ÎI•ÂbÜp\”Ç,â Åuà®&ß{çy!>ŽŠ“´k.‡6€ô–.ˆûˆz÷óÑÐû3&¼y€—Tžd÷IaÎ;u>LÍŸšHÞÁW ë `¯Â1Œt.‹1¼|,•j9šà ¥ùÜ¥ñô€þ›V+‰eÇX´cC$°·{{Ü»rSyêÏ¥¯ Ê{¤à4¡ sõqâĽڃh ³I¨› ‰Œ›Eš•÷á¾aæ|kß$|&¾Ó§Wþ÷­ßŠø¶0õÓ:&§ÈÐs‚8—ÓCÆl1ÃöQÓØ³­ËPeT +z #/³5‘kYJ€”ØÜG´Ÿ…û{™ÞëÈ‹¶}ìÖñ7d´Ÿ/½ü=òþBi«\ü² ž(}bÅÛisÿB:î‘=Ö@ïÇñg ÀêcÌßp•nÕ’JG=’'Í›þôAþûaÐò¢iH•`£1AsAç¿}¿AóÙ›(m²!;ÞÂiÉ&ôú ºED’›Ûж›§S Õe´åG « ÖWýä7='m7‡¥fRÀÍOÀ¤€ týjЇ¦;;÷wÅ¿Ä|ÇJô9Ô"GP™òPzóiJ=Ÿ‚ï6„Ï+Ÿ!ÃÙÊÃù„®$,No>U䧎梼Կø51–›ö²ž c6**ñëëq¥Ü žÕiN¼•§w Ãç3sûkðñóuüñ¡ãº)þÙª7À¤ÕÕøR)‘©J¾ÄŽn½zÞ*“iS Ø–h i(Ž…­píþfÚ%Nm:EyFQ¬sIÁçåŒ}9ÕîÁ¶µ ¥ùËí2l«cŒ«ÿ—‡ 5 Çë·wÓ ûYÔwjñΟßR’ñ®«Xv,”?ßQNE‡sªé6`‡ù•›…ü‘ÕzZþhÍW¤‚hY.ø~ÀþûÝñS9Q¶*–š ŸšL¹ê÷Eôún_œ>|8?š’÷ÞÇó;{ ;ÿùuԂǰ#8äcü><½Ôü÷“¥8·0vØ5’F™Cð_ϱkûþm‘7½þæ˜}‡~…Þ4lýAx•œŒeÏ\ùÓÝP?KÂ^vË$jm§ÉŸsã Y÷°ïÐÈgã¨C­}¾ÅÊæ«Rã‘Uøjj/øžÎ‡uÎe§"uͪiâî?ŸµŠxošN§7Ž"²#ë±7o¼Ó2¯9ùg_YÞ‰³Ç©pßç*ôlð4úl\l‹÷_êògÖ¢r%ÖÎôQå]‚Eôó":ûf|!Î8»YšQÇÝ…ðòÏWÁ[Áx¾~ámò쉘Š' VCšlðJc¾bÊ —EÂú–qh\{ƒvÎÆ¥§*ÖýAÿøŸ:?‰<øÚo÷1f&I—\ VmÅÝr¬uö+ŠŽdC ¸×¤p>ùˆiÁÚ' b5eèy‡Í–䂔˨6$ æX_ÔîW­xª£"_õó .eA ¾ÄƒÂŒ"ö¸û7x× ë{Íò‚Iß3ñw³.6UçzÅioûÐüoòtMÌ‘=‡3ìÆà´êL¶·N¶h«"ßB/÷§øöö;¡úß èj\-SãÀ ö1Ž:‰!7pÌÅãèíª…¦·Šq„Ñœî׋'­žcPDÏ’Ã÷»ÓlmojltìÐm€ÕŸ»˜VwBÌtÊ)Å¥«? î¨ˆÚ¦N›îí‡3ƒQõéVûd xµÀس=8hÓHÚ%s“4nÍF¾b V/•„>ß³D3y'?úfNʵä \<1Œß\ ÃÖßÀ®RxÿeÙHèêeå¨ñö[ÿ3˜5[õàå)+Aýîÿ~ÿ¦_Þ_÷Õ±ì‚Ë(¿%ŸÈ @±…£¸¶x>.?‰McÄùÿ÷ÎYG SÇЋ"Òð~ÒG¦öã$žp¿SÇpÿ•k ôÀmöɽ w_œÄGH@ÑòUtü¬?èÒ}·Ù¥BGô.eøß g±ä9éÑZÎÍ󖱊yØ>$ å|§õS¹KÏ>^j½CVÍ %ë¸{æ9­-Fâš?¡ÉÖ}¾žwö"é_÷ ¯ÇÓõrVøçF—:x•Ü¿oÍïvŠ‘áÛ”‰°'˜/ÛŽ_ÇÈ‚Í *?ûžÛy„ÚM À¿µ¨ f¾þo"uÓâÏËàýŒXb܆KV”Q6_;É·úÁmzSè¤I ¶Óé¿ÙÍ`ÿ¨ ìotÂ5ýâ=ŸbB… yÖ~ÝùZüq7½-ìLnͳ£c$¸Å\+ž©iDïºÇlzn[Ïoc:s.ÝÇ-ê8^_CÃÒð£ÛƒYÍc¤cþëÆqŸz©Þ¯D^·8~p)ä×r—ПýÓéåÐh: izµøqÑ9úiísàÖ¼??&$l§¯k4h–d2ùÑ]6œÿZ6ÿýÒ‰Sj%éøÑu˜¿¯ˆ¥¤l¢ñŠørËgökW!˜æXb|ÈoÈ+üНe’„¯ËAl|žPg39riè!<[ü¢<Ê¡þÊP²T¼ŠG©RKÉÛ0ô|;y‰¿§ÄçŠy¡ÝEàãÇÆSshѼ2ŒÉÿ)cpûý›xL¹v¿?LÖŒëÀ{úxÓ ‚þ U€÷“3™UC:N+v¡>+ˆŽÇ4R¬pI¨{y5ÿ«ŒCŸ„ÂøvÅi¤]v|sgœÎ&\ —U“·°—È#ä3t{(tØ¡< ¿SóV°>|koŒÀ÷Ÿdq»F5!*fø~ÓS265t¿e‚qÌ:h_â3]áõkB»¯¥ôŒ5N<àÄv¸FÀx‡?Ø® £å‡qÛOÇ©XhXN~ ;ËàŽ1@c¶`ÜÕ÷ðkN&tïþ•ÿ¨/ÇêûÞ–yŠŒ™©H‘ŒÏ^[ŠJƒ&”JŠJó@šdŒÈÉTŠB”Œñ쵓” ¥Í4ižçz}Þëúùþ…s³Ï¹­µ×ºïýìs?^Øp_#6l¦³<=¸‰•?2i)¿r~øïŸƒyZ¤å©$« Á$'®÷MŠ«‘}~5,[e¿ CP×–<¯[xJ_=l‡•æƒøÛ#ôÌX¿“®i!ÅŸŽÁªgª4êX$¶~I€à‘Áp²W„îÏ¢ñËÿçÿz÷‰o¿Êv(d³$eI®)$.åq<Ÿ©çìé£Qnµ"¿;í=ü}Þæ# qE²#¾PóÆØ7ê´pûZFk^’›‹2‰Øñ¸þ\Kú®Deæ=Ä‚ñ³Áßà8æd_ƒ¬UÀ»¿†Ô¢k0",å\·Â†yaŒpÚ¨Èqþ´po§ªð)ç# ×x1(Ø~w(ÂVìýkIï_%3ïÀ캨~|NJËE¤ùKËk8×­Ÿ?Nëb+’/â`ïÙXüö ËZ…XT؃º~å.Dÿq*™]0˜Õ¯{CêÖKÌÿᇠgœÔÚzö¾Ü.¨}‚~ïUøMÍ?øÔ< =öC_j3;òM’öMÏ"ž–´ô§wüû¾ô†©ÿ´áçÂhJŽvù—‚Á†26Æ‰á ªÁ7ýuF™"´Nõ2QØÕDne˜óÀÓùì~ClœÈ¯-\Çs>.µë fv‡`aÙdê2¬òduùô£·È±÷†`çj@ÍtÏO… –Üdœ -ñË…y÷qRK=Žx–fº[ùŸ”PÙ33Œ¤±#h,×ì«ÁoC•Ð4mTºÞ‚)nxÓÓ‘X(ÜÃËKîÕþþÀa÷þóxøv.Â?ò¾Ìgr©±:ÈãOÐøÝñükù=òCψ‚ð6¼_i†—v›ñòL¾Jå8 ¶å¢÷3/*á“° ë¾“–Ä­vô¼Ô"L7Ž K÷>%‹ï:Ñ!ÛòÀÈ(†wd$‹fàc/†á¤ûŠÄùÍ1<ýñ$áËt@ÆÌ3\ÿAÈS)~pÄ80JWàIî™3|$ªQÉûÅ¿¶!€\öyŠròGè‘kûáÂÒ'˜ßßã”ß”’£Í†4ïúpümx1w& ŠÃ?Ô­ãÂ=+ÎóuŸåèÚW— æÛõ™ ïvÝÆÄøù‹\Ü¥&‰¹*áÜtðRÖú-œ*wüÆ;UèëÔËdýˆ|vév ǪÐi§åøëT×Í’ûžãâÄðþžköf2ñô+Ç–&}˜±(1:ôÓo>’Œ›ƒÌ81=&P¹S"bcÙ›ûPÇ0YžœA³ß[p¦"H“Vç]GLé-Ï!Ü"+ïÆÊñ–ÊáੇÊ/‚¹L…¢à_´!7n\ÎwÆæéÍSyIò\ï˜Ò¦]ù¿¢è [¾œµÆ´ ÛÈÛþMšÖ³=Nü«¸#Î%Ê{”~^i §k†Ëpã26zqÑ4ïb°?×S‚%WžãàƒjÕt@xŽÆò©©úTÙ~µüÀ¶jÇ 3â0h÷&.áx$ÃÖ4ž‚ öNÜâöf:²À E´£EY~«QâXòÖ×ñ8=Á ¿’ÛûliöžéÍÅeϤþó†U2QøÌ» u_‡Zç¢íÔÏ1Û8ü¸†`SÕlyÓû—ðsg ® ¦¥‰ÑÙÃñGùÏØiÝÿùš¯eÏÃMðíÃ:tq>ŠÓâAÙ5úb`ß47ÚPnE&|¯¨ÿòì__JšJÂÀä¾6¸ÿ‘Á]9!ìòO,UÎÅÇHÆÊ@ºAv/„Ì;ºgTAtéLáµÿùÿÈï»KÒúÚŸWAKUŠ.SeÿÒŒ¸‡î{œ°ÎêˆO Î§â8Ý$8T£±U˜Ô4„í_3„X­í"Z6cÖXÓ ¦…%Ó¥x”‘¸ZÍGÓ³Ùøêõ+ôÔÌ6FæÔ˜£B¯žt¬’)EÓŠ8¨MQâ×{È_9eN~m€xJ"ƒ!6x<ܾ-k^€{—Œ½¹Ƈ@avîÛ‰zâ*9¿ç$+j«Â̵äæà‹ôÙ cL;²†î÷¯½›(tÙU³—5 W‰¬ÚšGqKŽ ¶HQ"¸5ÎÀ¿Á#âÿ3M–HTzÃÕƒ¸(ø±0Xä¡`¦Ù6ÐßYª­˜£è‹zä'JRÍ$nƒ$·_mÌ$#Ò 3- ¢0“ÅØËgƒic¿NŽŸM+m`S÷¼Õ—»µÎ°?;@]¢QøÞ$„¤?û5Ÿ |̆mâëÁìX~-œ ¿¥‰„¦3%²\î„+ÓâÛo«ÿùRK.ÔÅY_ª¡»?ç•sÒQwI1TÑù蕱ýµ/C÷³GÕáðÙ0„o)<Aqظ§·Ìhô^߉Ï6| ë6ï ˆ‡~ 8<©'îùYðoA)ûo_ñüÜ¥TbÈ(¹ÌDŠÇòÖÞ¥0¶¾ÖžW'¼1 ÕsOàÔŸB¸‚@ëy ;j¢LÿŒå—/ëì»ë„ËÜðgæLœr£ wÍ&;ôÔù+³·˜ñNH¼D¯‹CÁMF7}J䓈ã¬^)î,+E}›Ì¼¦LBŸ;zý:ó Yûœ½ïXÈÞR@ùÓXü&†š?Á­+ÀSφ¯ÝJ¤o^¶?†˜ë/áZ¢ptá^¦øTwçྐྵŽ4`¶%N‘P沦àuà-Yc™¼<‘ð?ÿ÷’ È-L©›ðÊ™nØ®Kk~Gc{ÓT› É']döš¾p§´ –ÚŽ‡Ï™¥Ø´ZƒÎõÝ™ÿöƒ‹ô>¾¶b;ß¼*tþ,ÅW¿[þÿže*;œì(Is0ø×XºÆ„.÷¼‘gԙׇklÌ{2íÓf\¨G',u£¶­ÿà\ÞcÈ9”þ,z‚ ÇÄ@›Æ îL}« Y þ?-1òþhèSŠÂFËcpqCtj$Àå¥!Ü1C•Fÿ^Ì·;×Aí­ÓtÊKO^ä&JW¼Ú1Ç¥ÀîB,w~{=Yüÿ¬—áqOÄ_GæûöW䛬IÄÈq8ªu!šYØñÉï|qóÁ#¨¢X‚n¾üѧS˜:b£@cw¹ñí1ðS{qm¹ š•fÆ .”ŠesŸ…až(ÐhëØäÐMÂæ& 4¶':´¯/‚µ¢”Ý…•Wf‚ÕÒØ›ý¼ÜÙÃN†|ÆOU£ÊZ¾>9“,›e³º’pâ‡6ÑF÷#²´$*ÄY\FåáOpñjBï=š ûzï<ê§0ƒuììœjRº'‹ÙÝXäÐÄ4 he Ô}ˆé"´<¨'‹uÁ,MÞ¾P&þ zLz5€ÿÕÑä{Œ v4¦â¦þ9Ä"N’)ÊO™rSYõ/ 3äui×Åœ·ÇßU ç“ÓHÝ+SV$–ŽI¾á–öz´'ÎZ‚¹Æjœ<ÑçŸ}[‰åŠœ¥"ÝWdø'½FbðvŠd¦@c³¯ZÿÝsg¦6<ô3¢n™š¸¦\dA’swdÂ_˜>¨"B-è7WAdÂX*Ú_ï²z ©”¹NzmA4®Êó*1*¾ÿ$–ÏpØ xa½_¡ 2aD¯ò&¸è\„JÛ«xàÅ&¸µIòŽ÷ÌNÉCX¼òþOj¾¾ 7o<;:’«y æ²f6ÔKò¦P>µ><ÈÀ?{WÁèü;ÐùQüYPG¤Â%ø¾GpÂÂh²MmUy’ÉêÃæ¢ąöIdFß·cÍ!Š+;¤èý‡É Ó°”½¯ME/ƒõp¯³øŽà¤$£eñò ]ÒùÀŸ×È­½—ÁÀ$ Ýõº wÎÞ¾A“;Ö¿v~*‡©{m  á•Êmw…™Vø ‡BbƒMùý Ôäéçx ¶øžºü'‘ÅóqÒÆ·,ÿL|<~ÄjÇéô!©®+?²D´JáÝ¡õƤB8âÓ13,'¨Ó€ºFXm _Æ”àñ¢tæÖ_,vž§`¼³Ÿø~ìR¥OÖ‘u;Ösÿy9¨\ûƒÙ­‰ã“nGaëƒ*Ôͽ‰3¿À¼¼`ª .OÂç>‡pöú‰|ÛÝ6˜•ŸÎš¿ŽâÍûù߸[É×Ò0!ÂŒêüšŽƒøpv*¼(úpO˜Q(Óy- (}z©À¢c4ñóçwI‡¨Øñ2«aæSW¢ŠÄå0Y¯½ ÿìbog±†³€^QÀÓ6wl6Ï¡&­@:êiÿžÉ^®Íu¸(gâ@üRHDq)¶üc˜»ˆP×êE0W4VìpçW&é@[ãY\pû)íÉ`ņJÈÏâãªßÁ‡¼I±‘7Ѐ;­é‘ïÓúþ-p³§ cZâ`ÏXHy&ܶ½3¦|4RŠùÞ*c+Œv±ÿü™ór÷ò?×Íà¤Èd2¤XØ<¹O„ñ*£vÅG–n^öâ®\ÅRžLt:Eh0êâMÑN”µI¦ZÛÚYÒÚð-PŠG¨FÐ7³õø‡V1¬ü¥Í&œË»_r îrâ£ì¸ùûB2ÎÒVhT÷•ýp›À4öÄBèÛÿñŸ›ºYÓcw´vÿé‚逳Éií«x?c ½çõO|‰ùÿ~Ícæ¼ÂL‰‹è¦†ì¬Ïz.æUúž“`©H†vOå±s.à¥}•˜¹¹Žè…gûUxúei>¯D¥¥Bø´±’l¼¿fÌYŒŸ®¸ð‘{d`tú4þòéþ"ï§àÕŒh1Ÿ¾þ^‚–3u™pLÞ>BWÝ^5Cù§ ,¬êÆ^ùMŠï´ÃØ~®è˜ §×œ„޽™8?ô=Ö¾œ5`¾Ý„[jÔBJ~9À(š5q$äVJñc礹R‘6îÔ~<€¿œ&±(}¶}r4ˆËÀ„ýýðA"|¸õúã¶½M‚¬G_á’›1¬C{<{TœMË!ÕÍóùà]‘Gôët€è¸sìÔ†Ãðú÷3ô-΃ªìCçx樖08zk^o^)Ìó „ĹÂóéÁPqTš¦ê¿„â3¶lÔõQø{ÕGÁ£?ç±»m î]þ'Ïã—• äÎ2 jº€Ñˆw+w‘Uú5’.ËÓôqºSË—8¬‹Ác·ÃÚ6WTÑÿ‰ !pâZ5ªìxÊ ¬èÕ}Áäω¤(¦qàûßôÞÀGNìHðtš•PÁäiЕRõØe‹¥ÑæôˆÇfh”ö b36˜° NF-$ôº?•sPØÓ<ÿø#Okl~àz»o¼G>¾A.Ì ;DÓ=¥'azðcÜp¢ Z÷²5™_à¬Â't6˜8íÊ +ÏùÐ+o‹°ý`7¦mOaÏ(z óÀÔÐ6X…€˜ÎK6åØ<0›`Šú“]y}b kûnIó„caÌ7UúÙ- ›c*PUEš:YÙS‰©¹h§1ã>ãI~FðøY:fÿ¾ o¤:a¢JçzùÁáÿðXîámb(QëƒsˆõÕ$¶og&jA<_ÖõW_±8!‹›«.AT\ÿÊG­×´†Tâ VÍ<ÅÀøãÄôñ)júÅ„Îþìîȧ‚ ¿Št~ß8o®G3ÓÇvßÃOéñ#µ@]qèuý •‹Ú…{κ² F?Àå 37”ÄxköÖè´§ÈŸ„õÔùÊŠVŒ?¤Ž{½&Pÿ#!´oÙZ|ð4—lºÿÜVbÃnuð}¶û¤*•⃢» íÖ ) &ÏÌŽfFfi¨ÅŽÀ–<5J×cnϺl~Ä]u†Ö¼dßG¦ðß$¯à?wË%YO «Ð.˜·Û‚~-‡ŽÈ;xv¤(©ó•ã›/e_ lWztér‡I[oÏwäéó–5:æçm”vÃTO+¾S>šVió ?ŒhšI¾ÿVJ>ðÀ'’"üv‡4Vˆ…À×Ô<|t-Y{à VݪÆË,Ðàãfî£N&¯£üìXxþ6hÕýѦ4ð[Õ‡‹»{˜Øß$^´`4©=­GÏüçO ;"‹qЀ/É¿1àòü™ý7]æžïÎà½_cx¹˜ß´‘Âé7åDó¦!N‰ÅB+ÿ0 ¬ÿ˜uBùB#¡Ù:y< ¯Ä§žK‡´øÒŠ‘«È®£ä¸*áÊs·¦ÂU ZŠ!ëFЉwfòv£,ÖR…ZÉ,·c9íÚ ¥|†r«Ex7½Ý=¥©µû/tðPç¡ïãy×OÌSóū͆tØœYt…ë,´Ð1§O)Óz[Sî½õ-ö[óUßRÐÐyžÓÄäâ~`5ç§éyرÍ¿ü™†ã^‚·ÑMœ¨z’fî–¤C¾®à‚…wQzæo”~b yi§±¹ŸŸ;ßå +“‚ãLGàšê|y¡64ÿŸÿùk›;佯žWü ï×mCµ®Xú/“píÓ#ùÚàØ:ŸŽA4n,íÕ«iÕ¥lž8 "=–ñ˜…“yÄ݃(%ÉUn?äP5—ÜT‹YµJ¼=Y³{"¡Cä!>›\ W­–sÉ_¨’s˜§-øM†D½„ÊR{L{ZÇcnØqçUy¬CïNVàOk΂J)çz#0$À‰Ï“©ÅEÓ‚xù‰pÜïû‹¢ $ê‡Rƒê^ó7{^òúŽ„‚„Ü|xu\oÝÆ½§ÿa®½uJŸå•N£-‹œ©Ï É^ÔÏYþ¿¨Sƒ`ªË2°ƒ?¯œ|Aá£E8OA„ÛªAÓûqV‚ô ú…sÎ`ÂÍãë=OžôÃ˶WÐÞ)É›1ûpf{ÚƒW\…v ‡½[1›vAˆg&o,e_ïš3ñj&I7á¸ÉÚ—þdƸ§Z™n0HÆé5ÇPÆ0OH{‹…µwaûÒb0l~–º…v×®²|¹§Ðù:…™M‘‚e[^¡GåJøx ‹QÉOëØÛ!’ô׊váF·¤Yªýñä¸ç$É1”€¿=”Hž<2ŠÏ—v€½Ú!B1Ë!ù?êÚ2 ~¿ ¼žmÇ{%“hªk,wë!Í1%Ä™šÞ^†’š/ñF«7$ôŠBјxöpûv{«,zÙ~?±’®'Ÿ˜}Y^™áϦåNÅècÝìIÉq¯—ÿ¸Ç†néeÎ/OÚŸz¨}Ö[. õK:vÛe¤Ÿ¢i[†/¼É7§7>ô+À'УžÅ?œœÆ‡üTäÚÑðNÓ™*/&Õ—ÓY‰0r‚ŽP¤Êó{•™Úƒó˜cÉÏO°']Å8}NîW]*FjÄ)~ù¾Ï %/JSž„ ÷˜â³üÏ¸Ñ T…óÌñ„vœ}QßBxS´„k2]&""ÎZ'ƒ¸ßo8¿É’‹è|dk¯Äwõ…™g±â’\ò&ë$K²–†×âÁ¶Q°zu&èo±§aJ(”mÀH7j}è:”äûCÓi7Þø¦ŽX¦HqðOÕ=#\ÿs­¶¢†*ôYÀJ²¥Çwøä§Z×PóU§ÐÚc(uÒ×ËAežðõ)!f 7Ñæ¶މ‚ÑK·’k+Åù£kËiØ»ØtpÚmÌ×5l®6b;ŸHÑ„iÇ1âË2Z×Ñ$ȶ¨Àï7™³à’V¢P§Ë­E¾2)½"Aʶ,ø:ñ Ô¥¤ Ã˜ûØ~ø; úö'Þig.3–ãPŸZÜ]‚‹´|ð3‘áY;)s5/ £\÷Aç..Aihšƒ?ï•Áã°$ŒW‹DËu1³¡ Ü}Žàá­o™_‹ßLØ=ņõ1ØøÓ?›N¢Ïö/`ŸîŒ‚£î7êìüÅ×÷aÅL=~ûÔa´Ö&ð½ ¶]ÈÀe™lyf6ö†ž&wR °Ù:ëƒvÃÕòtÃÑ|‚êy ­œÌÿTe³í}ààÞ éþÇñ|ý="ºŒào²¬e6gàô.Ǻk{FÑk1CèUåh´í8‡÷‡…Ðéƒüuákñà2ìû+ Qqpqn(IÑú$ŒW¹†÷ÅÊðÁ“í° àÉnIGSÑ)ž©Klt.°  %º_Áç%©°Ë–ë -^Ãñ{‘<èR!¯nâIuhØüswåàäÀ`ˆT‰‡qM‹aÖ³e´]´môÕ£'f㯓°©â=»Ð:”ÖÂ3‚»ƒÙžÓ0Í®>º¤ã­[>Ý.Z½¨ÿa"¯8}K ϱÇ[*ÑÇ_ w\¨Îêu0%ûº@n#»ßÄË« ˆÙ`;È +4ÀtÓ#Û!AëåFÑGæq´ê …¼‡á@šOCÞK54*5 ïÏ6ÂÁ s誦‰‚P?‘~¸ŸZ^a.sHÕ’ý°hýDa$ÊÕ#"Á'`05ܶ‚Œ‰\GùPîWøApðƒ¶°}Ý9tË÷f%·|ÙíÇÃXù(’íÈ¡ãÍyVî …+a¡ÓqöãæAZºä'~ÔÌÄßÏkðM›"ÂÄp»nÿ7w?°5“yÿ8D> Ã.=>N«Ú&ÔBð-Õ~Íufàó?u©õx–¦BϹ0ùï‹ùö¿èD‰¼óØ)&/á@5®Ö±l']ät–WÖÓà b§l6üµ— ¡óªa£û^ÿ·—©¸yp‘úÇð@VšÚåZò ¡ô†dVË?:Ñf!>85œ¯mÄî¼Ã`}_‚Û_'ë¡Çíw:øž8™…Å'âf­ÔÞ ±*V<)Á›®¦¡’`"UŒµ§ey÷…é]s©×v8áBzÛæ(ÕÖõ盂” rÆ)îÞú¶º:Sí–P—P„ ÉShRÉgPÑS¥—â¯yàT Iî ÈqG X“³™5 tË/I>M¶|+. n9ýÆýçî«Û'‘ =ì`¥´ Ÿ§ÂÊ€J¸ÑÏí$Ïcî 9òÂD¯ì­€¿!ð]'‡H×Ãô¡Ï¡×Ưl|M[)]>™*¬2‚¾EX%á…¥ÚpDa– êϯN‡Œ Ó ü< ÞÖÎÇGUJÐóü#LÈQ§s*ôqÜ5G”ž}ÓºQã©k¿÷´6)aéi†}{ßÂ;Mìýøµ°õ‚46n9‡UÂD(SC[dÏuç áD¢ –¨ÿØÿþïò"”¾â'”nµµC:l›ÿ NcoJ¦Ðƒµñrß¼ç÷Œø'Èð¢Ì°ö$ KeàSŽAÚGȽ~i¨.ì“äoÊŠQî«‘ æÕIüX†)®·Ée¿íÄù±ÒÓ(P~"¬Y^šÌ½þW§oŸeé;^ 4•‡²‹.©¼«Ø”­µ¸m·¾ÌˆÈVW3vŸÞG/·iÓ¥UӨȱµ<_ê”e@ÆRŽ’RKùrŸåÜsîyÌ«@]Lªãã ³¢ÀVަá$Iù7rsÿçðÜchLŽbE‹‡ÐQ3óHÑ’h¶çÏn˜þ*\ö¶âЫ%åïEå?]ÂJÓØÞ¡TÓ…A¬ûP>}ÅBîüd¾é痙Χ㇠gähy'ú©¶¾/’¯nÔG|÷ñÛŒ×%txBô~š?“þyàÖ¯Ÿ tVÀhûsRÛtgÜ$MQ·¡CgôŒÒg6ãè1çiøÇ)w[âñw wÆ Zñ¼7ËHs´ùÜÙ3¾ë÷ÂÃuÏÀmôxzc©'é–êd²“Á•ú*VЮb «©¯Ü<Räζn7¢™f ìÿY½é«¬‰F?ï|9F§¨…ƒ÷âi4[m6|Y9r2ÁåüoR*dTb^])$ïXFUÓÙûGÇZp¶¦ <¥Ÿ&ŸàFº"üøå^‡«\ÐÀ$šäç†ó„ƒñ|ÆøvØùjoj\ æhÿ¾—dd`Ñ+Qî÷s6– jñaèaXO¼³ÇÐEiꘇð½ä-Œ÷å°)R‹w‹3npÊ’žº½^-7ª»Ú4÷úÓ­~˜ 5~bJ3º4.¦Î?ÓP ›JLõB§ñßy€^¥†¥u¯?nÿŸÿÕßÁ¡kÏÈÜíAüíØ•ôÀüÓx?!”¯<‰Æ ¢Ž½ò ô¾¦j>†¯[ÐÒ)Þ³{(u¯ᓞ)óñx°ð:N­;ÌÊ—'m¥x½ zµâ¼¥A8µ¿ænŸ7ˆ?ú»ò-'ÐÕ£1þ¥=½¿I‡:/P:*Çï·¤ÑJ‘Èöóò‚5hù©Ô¶éÑ€zY²]r0/z9ž[Ú]£¿Ÿ@Ùp-¹~°il>F,ÏÅ­Üþî¼À—ú<‚½ñza.ßú8œ_ÜyQ·}E¶9ðmbOùìÙyX”Ê'lY5€ßÒQ…|ÏÀœÀ@\[¼€ ïä.ƒaPm E«½^6Ž•Þ—à:ûyÚ|s:&Û áOÑ9fA ÛôÉÖãG`Š· [õº¿ÀãŒäYÌKrò‡4Ÿ-íÐÄÎùôòæûŒ}†Ô¿y ÷ì+ô!´3÷4³†%ÜVJ–¶½Ç´ÉõxJeï<þN »ÇÚÓ†pñ«ï€ÄIóZ"A_ U埇‘ÍÞ”è6|³_¢L_ˆ‡úÕõlçú!t•öΌ؋¬ytõÔ”ªÑDå Ç,qÅ𕘑¨„-ªªõOFþ5´ëˆð¹j_ØÑûRœÙ‡¯]}™Ly¤øç£µùZræ´Ý9]•¾<Ö‡Ëîr[‰Õç0qÓ2e± zãvaìu&s‡†ðUãwÓEË’±9ð4ÞšvÞ®Z­6žÎ´¸@Ÿ¨/åÑ™¿·Á‡î²O+L¤²%Иp½Þ|пvÝŒ°eÑ|þÂK—3KEç~Šv «Ñþ‡ ]ÕçÈ7Á‚Ê{P¿¾M»€ž/ú Ç¡DøJ\˜¢NÒWPeBL³i'/ýÅ©ll”0þøtjë:šͯDéó t‚þùÖn˜ÓÁƒezïgB1Á·ø-¸ùÇ2<Ñûç¹lª4‡r÷-{Yó®b˜·¶Îûæ‰òÂwÏgpê–ZRn¹ˆk}…¶ j|»õGü’Œï¨€ÖX,ä)DÂæì¥¤|ÁÖnÒH2fˆ ~ÈYðÏp]¸ Ù~Äþÿð?ÇF­®?kÉ×úÑÇg<诶c¸?z*ÄŠÚÓGcÆQK¹+8±ùì±õáÇF€R_â³W†>Ð å; ø «y<ÐÖ•Xz g¬»yr3©v•dÜ-á+î7@Z²”¨ž#+s4p³£4ÿÕf\ú*´ZÔ¶9…ýçÉ µ—Gó~‡ nˆ¿Ý,Ààwa{Ù%ÒÇ5úئo¥xC-•W=žvé5äî9=(y–ˆ—þÇïñhj2„©“ó}âàgÿlË¥0ÇÛeô0Ù}¡NwøUÕ³@uu8{ýT„‰_ºç¶'ÓÉ;ÝxéùxzÄÅ7½N°°GãQýwÌuÔ…ƒ‹«Hbá~"?ù¨¾X‰¿uîCßMC°}W ÇíÄaý82*ø4¾ 9ëê³áÃtiP»‡´wçeœl` ;Šó|Ó"¶:Šå/Ÿí%#17¹Û|àþ;uÔòe²ëþÁà˜«ì׺3Dd§.'üÕw•ž>é¥Gð퇗ìû­LÖ+ÌïœÄÊK+ôoû¯nR–R _þ ¥ceî“'_Œhâbïº, ¶Î8/O“—Wc~Odèð¾vòøš4ï™’Ó*êà_á\ØeD·O–‡Ë#Ïã‚A ¢Ä縜̘/‹loÁ«µj´´@”Äe1¢Zþ;¿yì=Ò½ ¿mˆbO6ß§h07o½™Ì³mÂQUÚ gW«à¼÷ JÕ ®+¯ ¶Ãôiøé‡øÂæ:,ŒVAƒ D?n&?®¯‘7ŽGÉŠD˜“= ½óåhÖÔ·×pˆøø lL§Â†waÞîLÞ~f žz8÷Ïÿö¹gƒñ.³ø>ƒ0zaË?&áÆ÷YLƒße&¸ðçü‘ïÌ[†hñó}y»!½xHºæ=†YÎôáÞd~Ðá$ìl5çí÷ñ ?UèV_9ÁQÓüˆÔcì3F{ZUéÏuðñ­?W~?ŒZ+(¡÷s ®áýZhçòÅl0šj áì’ëTO¤ÿà¡ÅÜèÒD,(ßLŸ·¾‡³³epgÛ0ºÒÚ‘Ï®iÂÀ–xwø=XìDo¿*ÀÈ®ÁüɱXµæäÞ¡â6Saòd5š´(†í”çqY(óT¥»'¯à™ëðu€(»¡;n þM¤wì§«E÷sÚ,Öq®šÉðÉö%úHƒŸ8ÅpGÖb®ñ3 †½›€ûý,x¥cƒ ßðÌÇ£²DPÃÕžÞЗãâO¢‰Eˆrlä·x3¬ª5EçÛÊ\Mù4á¶',= 'wBɼËäÝ•%ç®.¦]ÃŽÒð™ÅÐ>Ø+©ì¤Ó}¼±àÖs^幸MÀÑՙа¯‘uýL„®÷?!ÆH’J¤MªÙï±.o+Ù6k_0oaðdÿþ1~¢·àë-Nâ/’ýÀõî;H7  «GösUà AJl2ü àÿËÎlÆU#Â~‡¸Ð8AøÙÏè®ÂA×íÿr N¦¡³j1lø$C;§ÛÓšc™0o”d‹‡Ó³W‘ÛÉÉèó{;ô\JXŸ€‰|ìtwE~à \È<Ç\Þþ+ìØáÌoEÙ¡×p“\×¥Y5Â[6b<ó°+þ¸¦ÍvLÍ4ÑÄNµ¿¿›Îˆ#k?­À“žðOÆnœ¹×£?~å È2 ÑÿrpÏÛ©Ô0[Šj¼bk¯[CIDD[¸Û[„8b~¾- ª8‚}c¬x¥DÄäòc!½P«ÅßÖ,À€Öÿ½ÿ‘„[>–³³i±ÌcôU¸/«Cm¹{Ï<‚ƒWâi•}Ø‚×ašë5\»ì-ŒÓëÂcFóHý£@¸0]Œo] b­P¤Ý(Œ®Âüd,Ø5É8X$µ†~ç `òZ™„¾P ÅkÓñÍÜC ¯·ŽžC—é«ájÂ_³jè­”¥SÅг:Eà÷.–úŽÉ‡…ùª´vî%ø½k®L(ÄnYð¯i§¯Š¨òo=¬‹¯"¡ß쾂—M•è ã¿k̰Ä;ïlú„ª·¯3éÇæp¦c)f¿Îj\,B•ªMz*0>èïø¼sÐé8„¯óa§h·”Ù…Vëw£ä'o|ôsÝ4ÏËÍNbç²ÙÔT<ˆrápÑR‹[Ò¹ƒ–ð—­_qò\cjÞŸÓ3^>Àº™íXuLƒ*½×ã&Ëuð»ÎŒ•8€Þ(CøÆ!¸`Yjw‡·á·˜Úò‘ÔVÐcÓMéí4\û½‡Ø4E²ñÓÔqÅ• ì`é6T5uÅm­¸Àù;L[t&-£êÿ±e#y^‚¼øÿ}þÕ{Ûƒ$ý‰fÞÊ5˜1E‹Þüp”æzäŽ1úåàûl •îÒ¤EºÂ´?sa¹öqL§Íc áÐf1º+H„~ÊŽÂ÷Zªô™ñ2^3G•륇“J—qç¨fT4ØÇž?8‚{ôV@à3m=âÀ&GÝb.“U©@{2?úÍNqþßµÂXWf‹Ý‹-xÒ¦Ñp¦r;V,³"ùÇÆÒg¯`«ïhx%O]­Þ`{‚Ýýö .©<Î^öÚYØ–P„ [»`±þ=È?œ,,Køï•Å©ºÌ:.Ÿ@e¬2ã¡¡öŽÿÛÿòO!‰l¿«ˆ§oÃÆg@×à Î&äTÃ&Kgè±tÇdëgìy‰$ÙØë‰”¾>Ž@ó'òå3–ÆkJÒ—Ñå8{êþº€zq.>£óähõü$æç¯/JL©Qw.>º"Æ5Ÿñ¥&Z‚I¿b±ûŸ½%P€Ðc.üVç3h>{œõÙ\B«cðøðb²JBÄ6CNeGÝõ’SBousö7\Ÿ Þ9™æjîÁ‰®üzÆEô©ˆ Á¨ß¯…Wì:.¡¹!»/®à¥GV“Ù²¦Üÿ„æëi¡¡š8Í«iÆ£b³ú_Dd IM¥O¿/Ÿ[ñU£o¼yw¬HÄ{QC¸Ô<Z–toïm"jrñìá¨Hha°ôÓAw•UúhÑÄõkqÚ&c\ù¡•Œ¶½)<|^‹nüÔ@ÎŽŽƒ.‘)¸Š{ÂÍì£tÞÒ•ð'ë¤íÌ&žvUp_>>öŽã‡“BàòÎW¸çêÁ–²>0“oǤŸX|i![z,ŽYߟJ4= ̧`¯k!Óýr›Õ®oÄeÓ’ð}SšÝC„ó·‰sI -w=:“õ–-ƒöø­dV#c¢£ ALâ(ڄ΅뉼'“zc£cøï«§úx°úz?wŸH³µØú¡:÷‹r…†h°ÊÄ×gÌh¨jEx³­Ûn­ŠÇàÄMUzÔD[ŸLÃ$;Ž·×ÂïΉشi>SÖÆÆ·Üa³â+k‡Ý›^ã÷Ôxö¥™é¤Ý¨:¦Yö*’’7µàÿÿ¦¡¸ÍZ–ðà)ü.1¦‹$Fò¢§«`°t&{–%ÁWê‰Ñw›Ôyã³Ñ0ke#ܩʥ‡qBÛ^Ýùj5“­«àÙ•C,ú‚<*”ìz-JçÎ.ÈvÛˆcÝ.ãG(Í#"\IÂ%e¯å‡æ¡_Vèç „’¹P™îM÷30>Þ‰gc ØÅL‡¶Uü÷—LŽëĬÖ|ÓîùÜ›DòµªØ|¸ˆºúÏÏÌ¡sÖ J³W@ëv{ús¸#lÖ¦öÛ«AdÜ6ÜšqÆ»F4CJàòR¾Ý¹½†À‡bºä^Oº¾“Û Ë#íÞáããì[­6QÕÐäׯ¹òùþ9¸ÛÞìrËÄã¡бùuÙ¡Cç:ÿ&çÙQü=IžïÒI`¯Ú×`¸Ÿ;¡Yô|ïÿ÷‡ÎœÓŠ…ž¹ÐÞ¯©7OÝ ›ÊÉŠH3ܶ ?Ù?EÃs.ÐØü–=¨“ÀÛʃðIÛdºSч— ƤÏ!»¬ûyòØÍxûÚ4¤AsLÌè‚e/`ƒÙX·xhçªðóAOAÃñ=Ο[ŠÃdÓ±oŒÏqà×C±ÓÛ=p–ʈÝ÷q þ§ôUAÕîB†d9Œ5 F›·ÉŒ°W Üœ„›§áîJ(Jã‹ ú˜õ)e:÷Ý<Ü^,FM=$Ÿã5¸RÒŽFBùs¼,oÍ妌€Õñrd{ù)V¿Q™vÙG¡ŸsÉÝìȵÙÅþíp¼B„{·«p‹]¬öƒÇ36<ñ.ܱšÎþlçðÆd!un9ȼ_;c™„ý×>íoAüÖ-Èû4ˆV´‚PtÏI8ða<9Ý —k­"Œ‚Ó¸b÷_´ßu¾©Ÿ"³FîÀ /ŽBØ9b<`é¿ü獵Àè ¸:Ï ¼Ø:Û¦Lm—…a®’ÝòCƒX? —›p ù8ÔNº„?Å>£ÇµWð4qjÆÞƒ¥wP­Ë¨qË(l‰H/?ÛAç¬.‚ êÙpöí4’QÊÍ#Páu'Ô\JFýì•p´êå>Ï4óVÙÇiºÛêu"Ÿ!ÍjÜö¯Vغ3÷m ¬äùrŒ˜s ¿&—Ítïã䉄<í4P¡òcœ¡ÏÛˆ'·®BçgÉ|ã–íÌpÓyb,.Žë]–BW¢?ÖüÌ:7嵸yõS˜í…z×µÖë7ld‘úm„·K“ò–Û ×•WGNâƒGkCè+WÝ^ðC)¨û>V/Åö[â´¸8¤·.]Ïb²íËtˆx–H}sR‰·L39`¶Œ5Á‰g»ÉÓ¦Fx£¿ôÚµy0_Ew]»…I2ã¨â‘dð9.xnËIµI®956Æm³Ç“­ÃÏc¾S([9{)èwŽÍdN“¦³¢þ˜ æÏD))úF¤„âžADb=ƒ ÕÍØ³!ÅÞ}ß"¤ªqÚÁ\L?‡ãðÅíã }ÈÍQä|žÌ»GÏD ̹ncØ;c>;¥8šVôkë]ŽvÔ(«NÞdß¹;öOa£­#T¹ƒD'Ù¶á*¦'hc[zm¨ŸDVH0§ü¦p2Âüdhèòdx5þ›xŸÝË=Ž[M Ø“9 |SÌ$®_yI`É# ÿJž¶Î¼h<û¹ô,d'îÚÁx]-®zÛ†½úZ/U\GÇŽ¬IçùÝ·…:ûòجk£`œeýô .D÷{p|¢?4Ù–C{’,·+,Ú±ö°#dèÍR‡\ ¾£°“ŽPÑÝ!$HéËï“ÁÝGùßhÕH,'ç ò·§ÂWTÍ´ñ“¯MÝ`ÉÜE/cè yF&Ø•cfÆLtLÅ® qîr…)™x1ºÚhQ8\žíÈ·Äm†ß~kaÝËp®îhÑ‹»W½™'á–Z<ä¿°¢OކƒþO0oZ˪¨ ëÞe»»Æá±µÍÅÑ"ƒø…u+ðù£2f‰Ýƒ2É »$êuò៪5¾ò˜{VÅñš×)üò²ÌkóR\¥SJŒ_4­åÜ`ôÔ]ñu þŸå_@Í"ºo±)Í@Éàj\åxVox[7ë|¦”\ÆâWmpép:îù3œ—ù]‚]í8ûÑUá<É`á’ËÒ³á¥é>ô!O„J3•x[ñmφBÁ¯qؘ Ä>¸žƒ]ï`õàh¼W¸ÆŸÑïý`V9[ ÷FEëXÐÆx·Pöß8ýDZäø¥¶?ìŸÑ5ô^¡‚K?”ÖÕnJ£3«au qõ΄[æ&ôºÆ+¸Ð÷˜Ô;VàÐ3ñ°ùsë3%r'‹ØXý•\ЗÀF™•ÀºÉ+qVü¹þ÷¦Š)‡·¡“á3ò"qÜÒu-™LµÑ‚ÀGÓùDseVþU•;ÈÕ±)­âtêãC¬eW:X:ì` ÏØÛ¿3y‚¦(·å3 󺊞bBPîV~iH#>zò,mÆt¦4b!s½›Vµf”Ü~…óÖ[Ó¬Þ<¶âÊîæ¬¿wêé T W«¹Äþ~+A÷߇úËPÌ¿àÐMLE 5Ù~þzŒå¬ÜÛ2Fòu+zˆ—Ï[Ö3³Ïj‰HV¾µ¸R‰±D7:c–ÉCÉøÙpiŒ.,S³E½–A¼òãÛü±þ(ΕǗµ³é•HW²¹Cxʺ› <‰zãcQàuf­V!Ë•3ˆ½ËHÞ Fpþîsœ÷éÈ?0 Ä' G}\yS6ñ¯gÂ(¥|1Ö¾•ùÁR9ÑA½šTä¤â,ÍÆ‹¦ žãp>øÝw²{ù~$“g`î =\²!¹^OÂõŠƒø×Ÿ~› Rü÷ìî'§¡»g¨pú¥Ðù»¤Å‘߇' [âòP¬ƒ´¢ªó6¡Ãg^añg&á¶ÐÍ<ï~0¤,µ‚½ã>ÁT½}ð å xºIsÈ1d'ª Ä¿¡Ó‹VîÂ,Íl¿ï;dJ`ñSg`;U©JT\ó…ÀšÙ\{ùQй(Aƒgãê{ô©þÉ£ðŸçrÇnc\rÀ – â]oãýªdf©âÊMü²ÁGC@›eñÊpurf}™ùÈ Ì%ð¯}9Þï¾G‹š¿aÔð÷ðÝÿYž9\0þçÚ0Ÿ¨\èÃãvEÌvWýª ü‘F3Ñ··çßKWRƒÊ.¡ænž»LšÚiu@³u‚0³d1*çÞD»-¾Pu~‰4á9Ý 4üÊU<ð1±vÚ†ÍëŸ÷‡… §Šä]›ÑWϦ£ôe~¸Í™üÑ¥™›óPeÐ'¶uc(tØrs6¥øCZ[IÕá%·åÔ.é4Iþ.„Õ ¶c^©-´ô|ÅY{di¯K©<ˆUu íR5Ï¡'º¥ysv<•h“â*!Ï`ü­Í´à_~Ûv†M³s§ƒVO¤ãœ­Àßå~ íE“•‹!äåAºâdŠ(” ¹Á4ªöËŽ¡™õDžÐ”H/þø­@=Û2È­Âhxm¸šý¼¶‹w\‰Ç2m iõ^œ·?RÊgóÑÃxîÅý›à½‰ä˜ ƒÑ… „”¥!é´"iû8ˆžüYÕ5BRÞÿ^2P™²ýÏD…NÇ…ßî$ Oë òíúìÅ9¼X‡}N€•H¢Çwb ú þ†Ó¹ƒr£gÓáðT'L½éÁ»mUéŒv!Y¶G ɘ Þ½¬-õQ\9Ô=ØÞ—LÇòùø ¯í îm-Œ¿ 8G¹“¼eJð·q÷xJ‘* þ|J½€Œ“òt¢f dˆ‚J&Již`§FkQßíI‚3º¿ÀÁ¬ÿ*ðc3ùSË+pÃ0‡©»ªòÄüG­×‘¿¦íت҈-WCy±ÝnQ>úöåÒ5EgÈÖ"¾pß°ÒœŠtEð‡c·aMI1H'~ˆQÛ¸\ÄTV+|Bˆ×7 ¨Þw‚Žyùˆ¹'.æÅéŽOéÎ+ÛÁßÁ‘:IÎŒ —–óX±qhµc™ F×ÜXKÒ¾‡±•«å?æâß˱êúp§S¡Óg0~ÙØÊ¦{JCI{$x¦~nÀc§n’òÔPjz’Iዹ’ƒ$Þ ç§Êçp¾v'õWü"ìµÅ%f4£›ozEM¡Å‹JØ­I%ýÏàŸÞ-)f¯Ö ¦k»‰w–+gàÞìõ†©pì›:Ž_›/ y¿}ÈÇ¡}dæ‘0Ô'GïÌFLÓçv·‹áÜLü«wt4Ï çËÂÊØƒµÇõ™µ^.¶®jƒ$ï10â6Þ ·}Çzå§°5GŠ¿î5¢/‡Wàeše(Æ6Ý^ÃÌ“£S­üðØù$ôNM§?ôh±×f~øùa˜µn9Ðu‹”¸'CŸžó.ûÉáY–(s"^He;ïhñßiI¸a˜Ïí>;“öñíÕ2àð ” Êc»–¡AS´¨ûòXˆQЦyöðŸ3Vý͇O]WPßw(ÌòE—K÷`“ð~yR€ºž¸"ÞŽV?)¦÷R£Œ[,t.: ZZò·¡ÂGI¶Ús¬!@œwªÅÀÄݸÉ_F¹Šõv¡,¢!ŠEÏdäx)1ßéÀ¥³‚Ènʯ׼‚M%GÐ¥^Usø^Sz3ÿÔ;ªä“”%Þ }ŠgÍ~Zò'Ï­ÄC;§Ñ]ú²|ãØF:äF ¥|›*Áý[ Šû·×“!e1üáŽ8;»ºÔb\ÅW­V¥U^Ft”Ù|ÅNžcv§g¸Ò¯C7Â2=G>+=º|ôùªŒÑä£Ýy”žæŒ—làͪô}Í#ݹ¯ØÚÑv-|‡3]eè‡;Rü¸»8̹8”‡ª¥¡jpôj(¡ê¾ƒœ®ÿ ƒÃrÉßöj,ÛäÇÏü‡bvÕBÕ–n^Y nËŒ.*vÅðwAŠe²fÑþóúõ¹î@ÿÛ-=ˆ.y0V˜=§ýoþ‚¿Uú4ÅÌ%?~ƒ¦)&ô¹U"ín˜Êï/AÅ6cZsÎ.´¢»WÒïQ<»/›k;Ö 3•Ï ’þa.d(÷ŠÙ€eÂ[0·,N|-×%Ø4L“÷…çó…› q¨NüS§3">¶ÔÚû6ìÉä§²}™`«8ðŸGƒ‡NàWí…îFÜ ñÊçS¡ØÅ;£êˆgº'½áëSUé“×9(Ûí‚Å; àúÕà¢iÏ#Τð)k%¹×º[ 0ž—*\ ³æ]¦þO& Ôÿ7S"ÙÙ¤N²ECŒOo؃¢›‡S×Y­¸y}; î Ø¯û?Ñï•"ÜóP¢jé³=ó1P%ÒúÅfàukÓÙMh¯,NCë"i:õ¶ ÿÄo‚³h¿¾7Š ~%Æ¿8ó ׃0úôXxæ>­T¡a±ÉÙÜAԼќJ«­àgf©Ó®îø é!|}=VwˆÒTwÇ®K‡ƒu¡09?ÿlräëSÞCáÚ(¾hm<Ì3•cÓõj¡¡³Õžé½»¸KÞ_VÛqò%êÙgÏ¿Äáø"ÈŸaÿùX«‹Ç ð߸‚*ŠÅà0N’Ël«á†@TPWÀÉ׺ñu‡mÉ¿åpÒAýlz¢?o­v¤ƒ÷ÙV I Â×:’PÝZŒuwmhcß%á„ËÏáÞriªæ3‰ÿûáÍ{ÖÍÓ5ƒðMÔj—)M& ý;¹q^>Ú("êÃ6bÒ,µ]‹ò³ªÉº¶2òhR6Ó œ¿~®Fx„S?L<{ 7ípŽúpGÜ _7ŸÂî”ð)lø–òÜ’Ø÷ú]¼·> GŸ }¬Œ†÷l`ÎçIðbT ôê0Úi_!f!ú— ô¿n(Ð ÃÔ~mçvOKD¹÷Ò¼¥Þ›š¹Áíã'1µ$ à³”Ÿ)Å‹ƒW1«/x8)ªgbÓ›l’)“‚-¯ø+L‹ÎxàD]Þ$^Ò‰X0'‹¶Ë]£ƒ+úIŠÑWaýû¼¥Ý„ËÞ ©T~ï<½‘qˆ_cVO“á^ª=ßsL¤Jrù¨y|LåQ5ÝJcvêS~> î‘äy/÷p¹NEÊc¼h‘Á?üZž Ç`ÂÅÅÜãQL·~,@}qîÝñÆh?Æ'¾™Ø½«„g¹Ò°#—³p`þYMz$R„~õQὟ6P½w’ŽWf^beã…Ønƒß*öòebj´ÑJ@Ïu¿êÏ/E>_^Š9ØÉºs¸gé9¶öb15¶É‡´mº˜ù=ŽÇÎͧAVçØ¨ŽP.6_~¢–.ÿ€½þžŽWD³™¹>Šz¾¥OÖó[sEøÎ }Xï²–F&¬¥Åw+Q%Nîâ’Þ­¼Lï ®\V ÚwÜèü„\š¹òm”¬H“:9'–M¥Ó¿šÑY— )<›L›úVMåßLÔY¾µ®dØ9j8wŸ©ÌÏ{þ‚áŧ!êI1Ö.[R"Òø÷d6|´¤«.—‘Æú¼ýÅBž˜{†x‚È”éTsÂw,æß D¹Öz‡´\ª¸N•Sx–çdòs¢4ÎêfþG-¹ß²9È¿­JØœ£Æ½šüãX:Ì&Û^&ð`îOÇ©¢3íçsqÐäágÓàõéÎ×ôs´Ü¡šÆ–}6"÷ÿõ?Ç t>3:Æ\ÄÍÖìßñ«à›•ΘçQO½xáêÌ×)ƒàWÁüÔØ/‘ξ º$‹w£ëýÕ°¨Í‚;=ØM¾:Ñ_âïà%Ú“èG#¸š¨8{ÉT×&öë i]Ãûk¤ù£ü#MÁîM3éÈK®´gÁ!’ù¹Ä<ÆZ?o¾çãú`;”{ð1VÑøÅÜ4³Å€­ ¹'; ‰b“°¡nÚŸ2 u ù¥{È>šÍ$úyô^_*rÈ”¯ÐRÂÇö%¸u­![ÞŒ/÷úö¹¶`§ósjX÷O8xé¸VĶ&;¼ÕR£~fjüÊÏ`±ï•`Þgܼd?<4§3Ô(q/‡Iôp_AØ—w“’eºtyÜЗʥHð|°†üÅÊd÷ÃÓkàÅü¸þð'8>^†ä\¿'Õ£ü†¹´wÞbÒ±C ?1¶XCTFk÷ÑÜɧ—°1ßHFþáúò:@hâ³gL‘áôšàÑ•á¼òÃ^š·b0´XN¦g†X‘QÊÔ¥ð 9Ó2†²oš´[  ¿×¨QïÖ:t9´>\VeëÌbYïloL}í*)ŒŽÔÞ„a±™˜—ÇÉ!¶äE1íÞ–™.Ö<Öï޳B‹ß÷²3`Šƒß>: ‹UÂà­Ãhqíd\kœ‹igyتÔLFƒ.ûq >ç§ÄCÑ„»T~‚¦P >ê3'Ó6/ gâõaÝüèOþ! <ôÁO_–^Ò¢ÌÆÃˆç:ûRœ]½ÿ[ÿQsN¨ª{ŸÅåÀ¯KƒxF’€¦d‘Å¿8Û’Œa)J´ôî/9 ÊÍüÆŒNÌã½mÑL,k?žºŠ ŽúÈê`©|KÃVAö(íþš“Î_£_ï„CW‹+ßé‘F·ûlÀáÇø8·nöÂqU’Oà½cGÓƒ÷±ày½Sƒ·o'CäL–ªF±ÆaxQ¤’ΈùTg=ê { - _¡üðì½Ó€A•)Ü>î¾]ær[PÙÉoáê¯Õ°ùÝ%œ'N×4òÝ¿ÜéñšÄ¹A™ŠÄNà‚¾8/šÛ.Ù0PÿÛnî!bó“S¶B6ÕN’$¦¿G…9è%— ‡üâ©K\±}ô.7j¤‡Áù™<Óc%U àc\ÍéÅòTôøh:kÉ%Ü@êéòÛ 9è7#n7èñ÷¾W ³ŸSƼf=´îòòç„Å)Îuj&sÙ<ÓÅØ7þSK£Çœ©xïxú©¬žO\ÉϹŠð:¶¼¦jÍ)ÊOx,ççBö ÏlpÄ®]º:‚AåyYz,}é1§Y„-qºtcúoÒ1cÏ‚]Û'ñ½U4©ÊŠ¿ ãŠÒ¹qu(ªê½ˆÿ–°í¸¬T+õ˘è !Ny“%Ð;ž÷×òÛoì1iÚRlx9•ïßí¯ÙÇpy*6ΑIÈüÂôó‰(#o ·þ-½´¯xõu¾©nÀµ›æÁ…¯WP0¬üµþÀèSOq{Í“º'/o²©Ùyì›E»{oÎÌ…Çç³Ñ e:¯k„>•ýpÃÛžoóSàIÕçàÄdúh’+Ìj9;æú *˯ã/+f³^‡öF6‚¦~ŽÙ8„FÏ #qKÂàO·¸‹YP+OQ¸<G«]'a[¡¹B™ß?¨Hg/l!“”–£¥ÛÉü °‘¦¡ï°-¶ô¥—© öÏg‘Ø(tà¦Tz™eÿóLåCäF“aÓ]ˆç#/z{©?k< ‚êÁÛ[@û:jù§"%.ÿH”KØJÐ%×—Ó5qÛxä—õõÿ“Z(¬ôK˜wNœ[Fà·ÒÉþb'UÀ’³ä^¿ö à4ŠÜ‹6­k—G¾:ÒÄÃ:´ \–£è‰_¹móþ‹é¯xAÍ–dÎI,&­'“êñtØ#˜–!8ÌÃxÁÌpêÕ\ØdÁËè&1«™œ[èÀDfº€·ÙQ ²Ò†»¶ïÐ{â:¸zX•Øz\†àjUnÓ"ʃ%‚Û§³ì§·x]ÛCjù<ê˜&qô@©“udw ý4z$¯#gÃõîRÜó»_s÷7¦‹?'Ÿ³œèÔ¾t¼ÙpÖÜÜ4€_Óû{tGý›S™‚4æ)[®’ãÁ7;ê$/ºÑõ†Ò|(8‹3Cë4v.C 5Ö¤Á]¡¼ðçiUîõî9øÖÇcC®‚‚ùzùÃH¶‘ rC^À?ç˜uk“ðDÝRd:†NLï€äó¦tð‚‘üÄ;CªxIˆ’›Gòš#¿°cÂ$웳5»Ù;}[NËå ×5ºV¤š4™•á´j¼-ü‚`x”)y:f øåÆcì'Ñôiüdd l­p¦-· î±4‡Ó9ŠtCý 4üd*«ïáç+á(“äz³£ðK7ÆaÇraßQ8„?ÃEû&ñ·[KF;Ãñæp)´ ¯ÚÏC#û=`íy'”mbçü/³{?ÔuŽd¦ ×ëŸSâ ÍEx¹›=?µmtœ Æ£B”­z€)çSÛ5Á‚AwÀk6âj³0Z* ï?ÚŠi6 ø<½õo±©©lÕî׬°Ý€þlŠMŸ¥¸ò„Ú–|ØööLèLB­i˜{?âþè1£kˆr¡(³|5ØÌ/á¸|<j §ñHÙ[ÿ…½˜ö.cåÚp²ÁS Óã·`¶ÿ ¼²áÙþñÿq›ÏXxü¾¿ü÷ë¬ÛÃê§ß%«Ê.²¾€¯Ž½ƒ†[Sö á:GàyÔT;­¯?7ÁïÒsäkL œM¼I³­a;ËÃEPcØš}ÑaÇWöö ¤¢³`p‚(Ï·O…Q;ö³¬.st:¥Hu¿.%ƒ |qÊI 5ôáYj]Sã¾=Ì™|NÏ‚§#ꉥi\½ª÷É∵ì‹[ (%ˆs‹éʘè,+r`ýëï§D°dŠÝ9¸µ±ˆ9;ZCPËœ+;Õè–8onýáÊßîÀÏ›LhùQ'V ê Y‚iÊNØÖ™€[žôsæUɂµǡgŸÿÓ÷Š™a(ô(⟡Òì‡ódĺe,àjÉ=˜=­C0üTü¾ ´7+ñÉ»–á+<25—§Â½µPä¼öÑ5¤2Q„–Ǽ†‹“À´òw<'O_(±µÁJXŸLrÄö™ræûF 7¹^9øÔ¸(c‘“Þ|Kø …‰8vò(C;žä^ÁḬ̈ÿç~×ݽd{X:Ä.b­o'3è9íhUÒWKÄ9'^GA\5z ÒÐ8æ¦Ã1ßå|λ}ðnô f6ø÷5?Üy€~jr:u¬ø¬+‹—¤­ö0Ȫm'b¹N´¸Þ.TNAç#3þöÈ:²µN‡ÎûrbfþŸѰ÷ì7ÒÔW±wDxGT<,±¥Ó‹jɽuXi¬Æ_´gÃÜ?‰”;BK…](e",ýû•ÅŸ7¡3W~gO³ý`ý·Ûpéò X¸y#š­$ægâÛ,c:ɧ2-‘qz”Ê“Fþ²v ÿ׿MQ-%:£-Öæk‘Æp~¦%è\ýÒí 4µG(‡Á‰©íäÔ‹ó,ì’51è< †š>\å–wM†Ð×G‰Õ|/Áú{³°§"ÜŒ¯‘æ¯ $ñ(øe&o¥ÇÀ ‘LjÁ|=,¼¢ îÙ)°f²¢ü…]^ÛM÷Eâ'mUÞÑ¡‰Ÿ•©ü§'à[0‚¾ž@¨ÆÛhx´É\jGqwNõÞ°žå6|úœ*afN4þ›ð Ä\”øíŒ‰ôRÁU¨ïT Å¿2 ‡ëŸ'jv0«D\i¾ššÞP‚åZ•ñ7IîªP÷p81:…zE¦íß ¶*ãpé›HfòfV5—£Ì¥0xtX™—­…§«z1iœ%}QÕL2¹@‚÷l8kã¦År»kpyfLô».þ–¡ÙÃŽ_íek%èÚiR4oǸ·ÙŸ\ÃV½¸‡ Ó¼Ð|Ã2VÚSNÕn@Û_„5–:,¹6¿6g7…¾ê«0û_2±;¯ IÚ— ö‘6>(³àE%ÏÙÆ¾ AZ˜ñÊdP5y Ç‚€+å|Å];Œav‡%þžx ¯´ÁýýFyÄG¼>G&h^€Uúë OBë6l¿Ì^öà¿)Zð¼M‡¯‰}‰Öß*pï£8{³#*¨F£gô*ly3ÒEÚ™â6r{?cƒ^icMábúÐÏ|\b0hã á¶1[@Rëœ_ÅróEé”"³ºmU÷pwš!^èý*,úæH-²¡;‰r“§Sù½ÙàrZ>Úë-ø°ÔŠU6g’¯k<©ìödM’ªOÛ]ì9ëx!е³5¦`?¼Øáü¥^6¶¡ täqr"½>i1J†ì!•«Ï“5GtqÍ?6ïvŽ{¶ Þ ­Çù6pbèoœ]èB_?ÍÀé=úÜýÖŒ“~Àm¶áõ4î㟦ÏC`Ìüð/ ƒ™ÊåÈGJ‚Ѱ.ì–Ñ£ÚÜŸÆ ƒÁç1ðŠ ”Œð¹ÃÌ;ê’0*¬‘œ˜×ËÞ¿Í}Æ×âëŽkøø=qRzd<¿2E+v@§J%yýæ5\]6TPåñAX•- ¯çcÀ¿]dÄ陸²'S8 {زǚÂÙ·0Ç×gÍÂõ²ðxU4Õ\ÀãËÈÎY),tïUö÷ÓäþÈC¡Ü î*™‚ŸF±Éþºk$dW€ø²°¦JŽd×™,Ü(1‡ü¡uÌ]m*½¨YƒË3DøÃê·K ~Ì6äßSëX„c3vÖ(Ó}ØZï ð϶W`8ø¡`‚[‰þóÜöbã‘Ïìà(ŽšmgŽn‡ÒQØÖ×ï‘ 6`»tú@ýó¡ÕŸN¡ýªkd­ò|°Ì_‚¦i¹è/ ­ûWBÌÛýø¶/%Eò øü?|D•ŠÌ(ÀŠ Zœ?4o—%¿—ãç±aÐÔ7Bxí¦*Iõ{ÏÞ·9Ò•bh³h ÷ ’Êõ[ÁlÄ(Uº@pfݲ¢è|vó‰ž}»Å©öOö"z;N7|„ >·ÁÃM—v^:Œ¦ZûÁ„N¤ÍÃiùñGh¬° ú¹WSésòlÏ=¦#EÍËÝ„ÑY/aʇk8Ú÷!Yìö=V”áÊPO=;7`—Ÿ ª^T2ýIÓh‹H×ÀûOF³SÙ½öíÓ ´{@ù 3]^cØK¬2øhyŽÙD©ö3u:kE.>ßz‚'˜Î5ʵéžÑ±üî5u¦'9“ê.åsSÕqÂåÓP°íxk|×dñÒY-É@Íj<œã ¶þ¹ƒ‡T™7q5×8ÓÉÔŸHRËû±löqá‚˦¢|ÁûÃD^)¿ˆ¸!­pǯßþ€þÊ2ìÜtêžÆ³%ÃaTºJ&|e |>ž{ÜW†®†CÁ“¸]j2¶¼Öä×BYˆ]J ZPÇf+J×0d-¯ðÖÕ«=f`ýãí×Yô$OpÖé ÄŠÇÖ­”hl-Ý[wvûYøÚ² |ß°îí£\Ó¾Ç÷ESA£Ý,î” ›+„Çf ý"05`dôB¾Ù={|ÄO{÷П_Vª3†óéñ[áâê5¼{º7=s‘ÝÈõgE£À%{n]—Ï~iûÃÏñ°åBTøMƒ]_QpT£(f8’î·Y¸îò¨þL¸ªIí¥¶Óedˆ[ ¹ð©ÿFï}ðÛØ¹hZ¹¯F;ÃùŸâT°¸ ´m‚ðV½Û@þ;ôÃÂ^0üô|àjÊØ‚VSâf ÉýÁ ùú|U̹°àGÏ-’toÂn1ó*¼Ç»`¾f1l’µÆôgÿñ%Ü~±‘8q ~æ‡Q¹¿=š¯Ï>ÀÒ¼tq‰c%~vM¨ë1\è•&MÃÙ™qàjÕßÃÎÂc e(žÁ ¨÷êÒÑ0 ‡¬ÙBΗEƒá•BÌo_J߇¯Û¥„±ë?°WcRqRaä]ÒàšÞ û~[Й’ ð]åD›¿ Ði$ÿî‚KØÄ^ôÈÂÆñþĤcÊ@ý›:i=êŒG§@ löO¢Á±ÒqÁ ßjíÔÉT_îv?ÝAOo+îçâ!Ð%pC‰Løw,Q]8‡mæ¿6‡€ÃûH89‘ëæ(¡ß#8•°8ŠÛPr> ”JbÙ§ sBAz¼à×ß³ ´ñ¦xoøwiŸ¡ÙSÞ ¤ßj¶ÉŠj6æ“>àü=ÏØE"B&ëOp¤–ã' –ZÆ¿°¥ËO]Âívõ°\c;ýâц¶ §Xv6=V™Ï¯hÙáo™(&”àe«ï³—uº•A™ðµ©ߌ>EäϺÃÎ^±ü_;m4‹Ør»Ý§‘y¹É kmÆŸ{!¢X‡¦‡Ø «è¿å$B¶Ix55„ÞYžM&°/à7ô0~SŒ#c ¤¹rA'ŽÛr^F½Þ8”½ñ›ÌÂñÏ‹^ûêN'”œsŽp?„ù_£Yæg{šðJ*\«†³k´QìÍA:÷õLœÛ² Æö|'äõiæ³p{¨ |‚< ̓‹sðtO9›¢¢DàÐVGÐÑЧO¦ØÒ…öîté¶¥Ð6{ 16u®ŸÄüªJ²òþ,Ÿ›çÒ –aÉ tXr‘Iç>è¯⟠4./Î ŸIá#ø›±[ùLËðB¦šæÑŸý}¶ð¶ ›t–]=†×ïCÞÓàð) ö Ç’‡‚h×}â¹MîD·?ë³/ƒG¶·ƒÏxTTÉ€ Žp{v3þü{ÍÃc7c˜yÊ ¬?0ümUùß4UjíRÀ–:¨¢V…}$ëý~^.}#‡w®£óËaòw{6ÆAž§ÍÙEõ®ê¤W°fhà›B˜;.GzFùç³ñ€÷;¼’áÂ’-ëÁTÔŒ—·ãžÁúxK¢£t¨OÝBèÙ;a þW r“$¯+·‚ʤ8u„2½ù!œ¾Û2’ÎݧÂõOEàÞßÞÿÃRTað#7#Éãùwùp¶o0o‰ÿÿ²‡¡tè8zxf1[6ÓËéðh…³RŠ^¾²õµZËXÄȹ\–ìD[Ã6ª¹´¾ìÒçªaKa³3FÂÍŠ‡$ö /Š[ ¦AIà±ÔˆÛïÐS5>‚ù)ϱÐé(åϤÍE£¨úGWþä~5ßSÄGÛÓ­¦£°3¤ ¯¶šÓ‹Sb¥×K4yU§.(àåSÔpÂü$rúÐh§&5ô+Âå«”P+ù ?”4ˆ6†¸RvÙÕìŠ.¼‘…OÛÿ°€Wt_Q¨Œðá§6æðûÝwðíâöVk² Æw"ôÆ}Æ \uœ3/ûyˆÓA¡xát£ös¯·{¨ÒUúÔx/r•¸ÄÓBâ¶ïÛð…áõ‡qTÃX,*|Úþ7ñÃôâG“IÑš¿lQð(¿¿úÇâb­`ò_‹ÿž.k`èzªÒkL·ëâÂ0.xØs1Ç:Ö/‹ó:rP®­o鎧m÷UôOÒ—<áÙ¶ Øs Ü£2™Ó>wpêZÀR¶ÙqrîNÎR“Ólï߸ܬ眅EcÒø1ë0tz&ͽ$tiü}út~Pe¶.|ÞæK®™ áçœÁ“!·~]§èH2e‚'DµRãõ[™Í߯‚€;-h¸.–GÛP©‹°`è?’î= .ð¥V'BqUæ <3z/égÄv´+7ê–¤ Ó„ùçño§BñÑç{o›vb2l'z:s§®;Lûk(d$¡íŸL½ZÞŽ4¢Oþ fÓyÿõäyÇÊZûøŸèQ!û«— OY­£²º%èð´‘\Ú¢ÆjöˆñE×Q{ôtêÃ͸üª xtD¹*‚ç;Öá~å§øûõzø¹_™¯Xä‡Å{Ìè­µ2°öÛ8=o>Ïyü‚>ßÁKóRáûhžø ,ržËóãS©Iª,¬+,†­»òXƒú#ð]Èßw'¡yê^4xLm¦cè.ï$Í{n`RcƒÐh\Ìt˜|¿“ŒG:É® —Q.†YYF {¼À‘+rû‹Ã¨¨¦5|8šWïuBQs<-ÆÐÀý±Ô¢$tïÖÙw4CÌÿõG¡¼ÒxsSoœ‡y‘÷ÈCyIÚ'ðí¶DlûQ/Œ¸ÖE´Û†ÐÒM;xܸZÈò›ègÈSÿìB–¯Æ7÷Xá\Ûóp>eO».H´noiú{-Aóî÷ó1Ÿ®~!„¯Á˧ Íîâþ¡øui>ôG,QáËŽ}Æ+ yh<ä^Z_o®#®KSA¦F6×#+â›Ú~LoÃbjò$Wv”ˆY#阎 ‚Ÿ¯JÑ!ÏûØ#e#¾y¡ºÃñj_œh:”+‚ ˆ½Ð9SNà¯I °KÚ|`ý#\ô³Î© ¢7ð‚ã%xô¹¨¿šÒÊA3aõõ²¡…§â£õ÷Hí[1 ~x5…ÀD«1ÜÏã,®o}Çsï!à‰îp8±R—DÚd‚•ðåû³P7^à[• 9ãdècý‘øcBטŒbê§Hí¤rö`¬ 5å3±¹ŸûÜÈDô¼Îd·ôSù|)7?×…/\dÌ%¼ÊÞªç7Ï¡¯ó_l "¡8µ…“êè@²yz"ÊôáÆG‚%¦µ 5»ö}[û,øž²±ÔóK.̶còÿûµD:‚È¥ÿeY# ùÛ.t•¼3n,/C±÷Z¼~R6ŒúG/ØÝjcà¯Î(ï˺>Ûð‰¯-±ï¦U«+ƒ2aÿ¥pÒúr _¦²}O¹‚èXQ¶¶Q‘o¸¨DgÿW#e7ñ$µKh£x ¥î—R–ýpÁ³æ—áîKqç!ÚÔÕë î}Ú?yû0úøP|<áÅON $-ÙïÙÍÁ¿PåãÓ;í äð}Á~A$~ ʽܹ¹e%ºÇí$Dè{Ñ<š¯ñˆ¦¬V¤FG§ÃÃ×aãïôúú—ü„ÍAj}^Žšüí&o¥ŽÌ¹ét—ÑkX12&½W€í§×ÂË‚I\BÓrVÕK¡“J±ðj€±ÓÁÛÑñPëp&Ñ}xËâËÂÝ$xD³>VFjC‡8ÿcñBÌÔÙ•Ë?=³¦ÁÎB]ð¬þ%ˆ ½v_5ùÌ›g±ó¸4¾ºŽ'SýxF³Ý3)¯w+ñu·ŽcÑéá´Óú(L™´„†©¨Ñ]ªÅ£æaÀöã`™>‹ž’X9›Wà™Gg×ßÁQ³*QAÖ–v®ÌÌþåA>!”´=fîÖÕ°&<nWƒí‹ @ƒâÞ}9¬Æè$îÿMš3.0Ÿ'´'û8äG`îset‰ Ÿµ:ù_]1uÕ3H€q N?ì‰sÜ&Ñ]Ÿã“úgl„»(î:2‚ŠÌÒ燷â®Y²‚÷õ?YtÁAl®h‰§àÆq:[ý±ÐúOJÍkÇнhø¸ ¼ž)2g‡‹äik/¾° €–oãIcåIg¨À½¼‰Yò;¶spÛtb¤µ-ã×Ä ÛDF™ëPóK |ûÌ<”~rˆ}jýNļB¸Ó•@BÖR­Û·W{3™ £°ØØ>ï(À?©´.Wÿ‰®ºTmj<¿«;‹ï&`y’½þLïµÄ]¡X–y?ëÎÀ¿Ã>šˆü§O7™Á7½¸õâ¸aRŠhŒË6NårëlQ²*†›˜«CAM/ÖN” ã|'ÒW÷üèÄ€V¡Ÿ]5{®¤ÆÿºKQãò\}Çl¢áÔHžì= ÉÇ4ikú0ÄÝÇjÎâë:¤©½æoð¹y<¿ïÄ/ß)ˆl–Æ¿UgQbXN<_¯§Oä'ûÿ‡©j¼úzÞýw}‡~"jðþQ¸µЩéN N2²I¿árÚ ¨Þ €|\-ÞW¯,Üw“åL‡Þ6E|éÒÎF|yÏN®A_ v‘ èS´îv àב—…ïµÐÕ^sL¸ñ¨\z„?¤g\fAûûAøª}u‘Ãó.ÌĪéØwN‚v­¹Œ·ÏÒuw}¨ÚCEfp/•ý³ÀÎ9ÒDï¨ ½{¯tRà=bÈŸ ØGËáäÔÃô«ÃxºK~_yrÜS܆V©‘\ìàcœ¦I¿~“á«[=©G¸+ßÛ8[`qφÙe -å{yŽ£ ŒÜ”ÉË\ø(UCÞÚ#ƒÿšÎbSË øjõ†xX=Fc-dº7¹D.7GS£S±X¶}Ÿª‘Ä YWH,Ìør—ÆÅÅ‹êúǽ†½SU'ÄW£%蕳øÆe^\ce?aÐ3%OŽAsÁ^j§·…Û„F¢üø4Œ\€—OÂ'_Ù‰ôìM1:uß0?AøÓ ¢Ô}øWûxÜŸgÀ{êbéÑÒ/ÄÌñ; È?Þí(/± /”¾ÅI‰ørÿ2wËyri¬mpý¦Q™!Loçhz¥µ )Jqvo#ßä:•ZŒ¤•_ì å{&ä5xR—á¾BÃ~BNßL†k]0Þý4ÿå¬NÓç<Åãv‹1«[0$[ŽKiƒnñ`¾»Ÿ“ü´)ˆÿ’A?Xpm2öí¨b#OEÂÔ“yžËTTz™ÂúŒÚˆL[2÷™ ú{ZàP¤)]t1Ùé‘„uÊ!ˆíNçêgñ­t,ݽ9`þë®WÁÝ5‰ÄR0g~ <²ž…Âgù·uqüzs"}XhC\éÃ'¿pعp^žu˜nÝú•í:.K-:ZÉðG ô’•)„îò£«ª ¨\õzú®Â•ÿ·ŸÙO«ŽË@-ÔeL‚KoÓÉN…uh¾% >|1ÁÑ%SÙäÙ…_*¤&ÓexP¨;=3þ(ì×k§G¬¿ ÷»³8kª Ž–«Ã¹W{¹a½ÜψáÙ­Î|ØPÉöiüVæ *:m—úëJ%Õ.¢ì}¸ù:‡ª¤BC0²¬‚M°”ìï÷_`M–6§'ÊVNI¡'.äÿ˜Á›@tËÿ#êË£zú¾÷›çyžÓ¨Q!ª×Ý'•!C„"*!*Sæ!4©4’æI¤H(‰¢z}󥈄 IæP~½k}ûüq×jõ:wxî>{ïç9wß}Ãù_/¼ÃK‘ ‰À”­\UóEnskןžG6çK0f›CËÆNf+žÀú%' OÒœ¹{ ½WÒ@Æ÷5'ü}?ïÌÎ*ÚúK. ê¹wT3+Ÿ l5ƒSÏr(ÿ˜ã]Ï/¥&Ü_¢t!šÎ»’o“gðåüËùï&é@ޤ8z+,!‰›lIx§lËŸÍï;gƒ}!“Ø”›1¨+>ž”]ÙA¬¾ZsSÔÚŒ¾ˆÁ »jŒjÀm¯¥±å¢6ö»ì†GñÝ´ïŽ<Ù[A77tÒª×jìgëÎÌþ;ç#t!Æ“|›S5²þ}B÷»/oF`ãDC6ú™5YTˆ)ÝPÆt+¯A§–[ÿÏÝû7‰lÔß‚±ûÆbü¬P^³:V Ë®²7P ¯Hþûöó6·ÌYs?´ÂG×ó°dt ÞO΃ÍãR¨zƒ ;hgFGßçdÄ ŸOˆiå ±Wj³ðÛl²¢Cw¶Š ž >Oß±‚úvê°¼adr‡ÈŒ'2ù¬[µã5˜zod&ösÈõXc&¬dŽÝgÕYº@+¸n¦ =ì— !ã©ØI–~ÆŠ[Q•Bèý6œrÞ –ŒÅ´¸ N·±¿ó—œ›é1ÎÎÈ›©ûÅTÙ"‹²¦§Ü؇Ë9ŸóÈÓO°&ñ•÷pbC/ؽ’ºUåã÷©)pŽnyZ î¯áôL;Öðã,z¬L€]÷§÷šJìzò±XI»!:ºÇLqÀeZ ÉQ9ixw¢=ˆ­õÀ¨þ  ©¤‡~ü7´E}cEhßIkö¦s…zàøf>äK’äx#œaÈŽ¬ÈF7« îùÛ\>¿F˜¾?•`ÎÕ(2Øä8|<ˆÜ8S¿½àîÚh‚ nÁ÷È Z¨EïM ý`›¶}sU¸o}ò9©Û|=É©’ilá²f.¯ÞŽ VÅr¿?½EjgÌü¢ 0Hi%‹¿ÜG¯ìR£3CZ@ÙF­ pìÞÚvbxÇÞ]é‹´oåJÖ®RÄ«ã'ªËŸ€…Ägôœ–:›HƒcÒá‹–81gñ˜ù]ƒð;°;e½´º÷1ýÂÂÐo‰|ÖyН7žÇ§ßùpmÇg<£ÿ|ƒîàŽÈL^üÞ]Ð-ûç¡ñoÓý&dñ.{k£ì32ãã?¨ŽYûA7ëý?O|ºIíB±wa4žÛ¤IŽ|tÇ  ߌÇq.A÷ñÃ,y²l<“W‘#}˱ÀæpÎitÖØï`ñ’Dz¯ÔÔ¯WðzekÍLúÿß¼HV9DR¦&!1›M¦oùÕn±lu™9.ïË”˜îÞrŒUŒÉÛh²`Ñ|¸dó‡ ©H³ÿ®¡}a#ÿëlK²–Ïðõã4Ìßá‚úvÑ,,>›\:}€5y³1Ï©ÎÓ*ÆÍ²"ÙÍIlN×I,r*d³Z iËä wˆ‰O(€÷yéÐî¹lâ’~Í·<·Ör¼ÕÅ×û&8²þñÁÙ´étüp ß°€¿+¶•¾Ø|—«Ï7o·Ýxmb ßÜBUQ:v?QÂúüc¼À´N˜5Û„|òšÎøãЪӇ茉"ÖwӔǭX/vkvà²ÒÜç±…p½Ø‡„qO]¼ ’c¡Ö pb,ä éâ¤~]ÿ4q‚¯f²k?a¦ýr·Ó„5M¸i•j(±;•½¤Ü¾„ï4-ª ªo¯…ÛµŸëÕHõ÷Ãdû¤ó4[Z]8)E66­$fàÖnI\Ü´Ž c^in…|%!o<ìƒå ;Fì!|è”Çò€×¯»|N¡ÇÅÉØ»æÚ:Ÿ¤¯'߯ËÁÀ½ý0“ü%;×õP³M.ðs¡8§˜S B°ôez[L@Õë£ØÖÙ äy/o^æsÚÏ| {Îr^—D <ÿpœqs¡6ìL”Á¦L¶zÜ1øûÆ‚Tœ†Ïì§Ðq"/x‹ UÉ“J]îˆÄJ˜1ù ž-æ$“_@é¼ä½=v]]ÈU߸ŒQª¸p( ŽÑ׿ìñŸ~ºäè)×.E¯8OãE鈒Ýïûÿ]}Ša=âÌÓ>›—O­]´ÅáïÚ*9@ð¯~zñQê$fcmq þúÌ#}–×@Öì>Óµ1!àØ$œ5‰°±®°(Ô™ý8u "Þ ‚¦Ï4¶KzéÛ*ËÅ]b¿”MAÏñýš[Í}\« q¦TOå!jü Lߥ†éÞf½’‡ðÓîŸxôÂtâ6æWØ“Ž «‰†÷t6û¼:´³(-vO?\Ã-•éÑÉ’ô‘¥¹¨ >_Åm/´™:ç‚ß“ ‰D?—>9fÜáx{µ¬HÍ\A&ã‹‘Öò¬»µ‹ú| r¯ÿA8W„o—±wwäÁµÅx„ÿÄÕÑ„ö*üxMJïq3oΆ;ôÀç‹Y£HBk/¿ïîXr„>Ù•Ì©)Ùa÷þœÙ6ŠnÑeAÛ;€×¸Rp”ö=œ!lãšqWû,Øèχ½÷¹†“ùDóë{¸{0$†àuš+Ÿ¦oã.sƒa{Y¯Ç[¸ºíHYÆ…Æ‹³O†Op­ú ô-ÒÁ/“ɘ&yžÈË!ÚÔ†3ïॎv®ÈG-¼ÝŠ•ñJ,oÃ"Ðü˜ãÄÀdá8fè>ÿ%\g™ çŠihÛÑ ¥/3`©Ó8g¹çå\­û?ü‰þKqeËc “ÏÃEÁZØl>›Ñ£–éNòp[u7ÖÞ´”â¾0i˜›rÖ°]·Ô¸ýÏžcΘ&ªþæHúU O6 5¶õÔ46ݵ•´È$§\—W‚n®50mðŸ{,'¥Áb4³R$ Oòs ù¡³áŒ_sYôéùø×À‚{:G–Ý«“Gñ Í#þÿ¨Å§Ä¦bµç~îÆ«NX¶FuŽ,ãÄwsdÅJ¼eE6g(±wƯ0áÚEÎl¡õnÌô®·CUý!›rMŽLE×(ix ªº²ìøWJsü®r{—>Góĵì²ê5l{¯†[¶JrþIOÞ¡Çöi’“Êè´åŽô¿ñ¡vÑ0õŸ ¸˜yâ§ÅÜÅC5èw¥“{©gÁ¥[ïý†oUwéëÂóðîª8…ˆ’G‘û{m&Kô8Š[>dSÝSÙוӇ¹®Œ.} Ásf“7j½ð`]HzmÂÕJcIÚé,9&ð¿ïynÀü2óµ~}÷®IçŽJ‡Gç-™Sã'ü8`ÅFCYeT]rVx‡¦àíÝË .p ¼z}˜ÿÖ– ¼K‚Á‚¬áÁNîLòpþœð›úˆk°›•¸Üìž]wâMÃp¢­4®ûdÎIñn@øÁªÔÁù5Ü‚[>àÞ— Cvœ‚–ýãȧâI¢0SR½‡N%M¶þ™0“>hJç¸PtövB{µj =U5Oë¶‚XÄ<¢ï&Ȧš/ úEhË—&ˆÜ{ ÊnFóºªdáÌ %r߸ŽOB}Ù—Ô&Öÿ3épA3^4lúD“sU¡àÆqôY{•žÿŠÀÿfŠ…jñô×d!2Õ9ýœoÂÑo Ìh"æd¬qä äÂÕ²Ü4è†Ú30m—19´”}–2áY)_åøƒJhVÈ¿÷ƒsZ+KD~”Áãø8þûGÈ<>í:ã\ÃP†n­+¸ú³iÏêâGä”ѰòN<³ø í HçMÌ/áNôÍ„µo!~ýlÒh„meIýšTê²´#2WÓàgwÀÀé ®w9!¿ÏCßùƒø8;]¹O<ƒÆØÜ£4ÌýDi´ìþZßç´$p8‹Ž†’zìÞÃÉX5#xn-´úB&†´GÂã¾T–»}9´BŸÃiüÐX?¤âj'Q·ÑœýÖŠv(ͪ‡ ÚìØ]¦cqW±rÈ70Ä´¼BŒ»"IºC+ñõÑŒL+¦ÅE£x›JÑ[Õ&ìǼ$pÈìÆ ¶ î2óÜ|=¿Wq›÷Øa“çQÖa{^µgd{&Û—T“3öÁ„LeØ»CoeÓÉ*$!d7Ù¶ÀÇÎÒƒÖ¶Nöã*Έ.¡ùsÍ…ŒÉNû¸·ýÕÉÞGÀX÷¬‘õÏÈ¥œÙÌ|jm9ÎHÇ?«qÞÍæaû˜ƒZ‹“èq¢_¿wÒàƒeuËÿÖBˆÿfì[ð<%<9b€NÁÀ–òOr5caÐuN¹Ð”éÁQhÙr&nnà—ë¼àø÷ô õ°œ¦ûÔ„„¼Ý€*j0ÊLˆ¸º—àï1¨˜ÇÚµÜã‘ïvœkhü ×Ü&{‡uos+Ö(´pA“¸¿Ýḕ,ÍS²ÁÓùøÝKF/šŽÿ\×s¿q&Líjätçø¡Èܳ0S\ –m§ëöåcäU PZòæüøÁÿ¯ûÿð¯Ê¹R·xj½¥â£+`fý]ÎÚWØ›Ãøns>(—¯ÆÍ6 Э*Ë6uÝã×UQ:ùÔ‰aµË‚*hø9/ˆl¤Tó…5î”a‹·'Ò…ƒ!ÄV¶&ìgS!~ÉžïÖµMg ny>“-À2o>^ܬ¹3¾ÒkZho¢ö„q2ž- ™8ŽÛ‘¦I{¦€NŒ,ZôÄñÕóƒéöñbuQFí8ä’ :‡EÁô³ ªß¸IMFÏäössðÊÁµ¸é ïg-®vOâ,ûQÇûé­‰§¹Ù|8•Å¥(Þ ùü‡¹ Ë^]°ònÒë>ú"È]ˆÒª›#ºŒ/|(N'¿:ú&ý~,ŽS};ð_Ý3·+é§qæþ“†i6¤em ÚìÛHbÎCz¿S<–À¾{~pYSVlØ¿ª°~Ç„*¯Ë¡†/È ¸ÔÀj“Ø`ñ®ÛËË2îlm œO8ïî?ç¿ Ðf Ù·iÞ´ÓTx±'äxºŸ %£p}$ÛÔø‹æ§gSÃåbôô{%V ÒŒ/R¢¹Ë[ôá• Kv8Sò2FüßÜî;ýv³ŸN‹•ǪhV>`ŽrÒf`°Žcïw@C­w3SÖÕ¬ ¨>‰(TH³ÒÞ/h9Æ„~—â¦ÎØÄHŒß8 ŠêeˆË–eØí÷nr_k“e>KÈþ@¸?Ñ Œ” ñü¦ï\Ðæs\—Ù,ÐyFÚ¢DHÖ.#v§g*—/Aê«>òD†¾ó–§j§}öªõpg¦œ!XÊM`q'ñ‹½A8Æ‚”x~å/\2§'äóœG»ñ¾Ý ý!+.ýæÜ2ۖ蟛‡Qî°ý| œxgÆÆeŒÂi±É´§¶|Äþ’ßíøÓÞ¤CzéüQu.˜¨%Ï™Íȇë«‚mp‰c×—ü˜9ëpì¡Y¸6O™¶ðà§FÝçv¿·…¾œ 䛤âà·´JËÂ#?xkåËQ²-„Ê­à´”$HéXøf§ ÞM”{:® ήãfc3çôCÖN€Òl•v>ºß†¶jp—%KhQ&7+æâ£¿–èôL‡µ¨ã,Óßp¬æ¬"¶â»4WõÅãÖÇÁì‚ͨE»¬ÀÚõópÌ # 3 ‡¢rî¿qß'Ìÿå£x–-¼à ±LËîJû½£›&‚Í7KæšÃÄÅr3'íeï´m¨œ ’ïW±1i7z÷I’V·çTùÌY¨ZçK…vVÁí ®h\>µ5<‹{•¶»Ù ‡IÌ84Ÿ7ýÆÜ¼C þâ8xñåà1{ ¢W}@Áö.4Ð"‡5Ø(5g³o.ù˜L ¢ŽáµiŽ8Ó tîA®˜H0A2ùÊ5~€D8ÔrÖ’;ÓÅð†ñj¦¢øš[öñs—Uf¹{ ¹ë!|Ð8ׯ<Ï!ŽáýpÑ#™lQs#Ûˆ^Ú¶‘ü—£žÀÿQCuû6²&øÌÏ—+Jâ±t*Ñû v)V¡ÉÉù°.u<œUþ´¤(^ ÊãV­9ÈS,äÎS$Vª¥ôS¬ ëy/Hˆµ!{cJbý$H§ál6³ª’ßœ,ÄòŸCÌïwtõ5Ì[-é~GQ¹r5·áG o!ÍÆµcÔÈEÉ>˜sä›ÑênUÂϽES<Âȇ¦tùó˸=ìØKW⥀&z]}>÷OÏWÀÐVkÂüä¡uÑðø#„ñŸ2IñÆ›h™žŠ¢mpÒ»Ùü)‹˜n¸!«ûßóŸñsw™•Xb ú—ñؼbªgØÆiÉ¥£üËÛ(oÇÜ/×Ú£ØÊI˜´®Ç…9óvûXãþæäÑ^Kìž*/>G`Í„—øzŸd¨Ésµ­úäeìXæùâoIV5dÙ&“¦QI8tu7ü˜éF‹sÆà´ 輨 uâôc&ä±´I÷2¦réi˜° Yáh2ù™ù8å¯Õi?¹ó¤?Æ% —Mž£fÄT–W¹b¾'É¢!îdz$(HíÅû5®(aÃ6}•fmÓ¤XFÂxÖÝÐ[®á¶Å|î_çÒÿ_¿J¿»[GÉ]¥/5°Œ…’Sg×“ÇÆC(þX‡HšÃ7O_Áq×HnÛ¨f| ‰0}j Z™Â¡m' 7ÇžÈî¬I—Rø")@D¾q¬„w€]ŸÊ# zþÐXb†ERàú¹$°‘b°ôöæÔ`¸u"~}çŒY…sɧþÜd–‚¹mqw1?ï*ï¸Ô òáÞYÉ“×ñJèGàí±!¯­/ó›~Ä]º£Ø³mÇ`]î\¶åŽ3ö#=‘3ȦK¬7,ø&›c65!3KTYǹ:žÀ@^TrÆ©ÕõìM‘øˆþ›»<Û&[ÄÃdep|uJ²ô1|ÿ7µ,‹Ü«ÆÖળÈb1M+Ú I:…X|s.¦^< NÉaÙ¸CðqJ>ôÎ_CFU;:˜^£ÓÿžÇS•}¨[èÁ}^ƒÏl&K׉eí•?`BÀ!\ú@”¼b¦Ž~ƒ’¾aÏõƒø~T*g:çw»ý2NŸEMÔ Qæn8Xç%cø“´ù‹Oþ®€ÁžUTH¢‹zT`ì¦d,Û†¢àZZ4iݾŸŽš…Jƒ·q`ö 6dx?ñRüÒëxœÓ÷½wð˜ÔÍiÖ´Œàÿyäßð׌?îMÚ&’¿¼G¸ýeÿßDkð‰ÛŒA•I!ç´c;„Ï3ÆÆk·¸7%±go>Ž:¤Â¼úz`÷Š®}ÝjnéÇ3Hæ ò[PÝ?^Ʊ˪ kÿð“?ùÝgA(«‚lÎæ@ÎW«O/ÇzŸ 5úQ¸Þ!Cn.Å›|;„GIªS^Ñ ÷Wç€Ö#òõI6Œ‹ªMzxY1 üOcJhÚÿ¯uÖ?öD¶d „™$D?8ÄU¯zŒŸ~³@gXR5»»¢ðž^ÜdH6ÿÀoíGžF7'9®>Né+ºÇT®ˈ[¨Rù‹¤˜~nv)tÀ•vš~ë .3oŸE;Ùù4®ê—)ÇURý^sö}È uwêÑ& ;|kÊÁÁQÖ¬¿ëh®&sLBɧo<…Œdæ~Ã7|øE'¯"¼ñ<* ²¤•šLkãQª9ÝßiŒ"ÒKÑtë^d©9+oª$+ÊÙ©B8sb <àÃ׸ViœÞŒQr¶»4\ï!oê%.,á:ZK.f{þ„áWa·h ¾ê‹äÅ7âäÈ=è{q¨nô°VÎìsÑÿª ?¸ƃ Ð;Ñ8qÙZ4—Ñ ¿/e{m´^Ì$Åâ¤õ©zÁ¾ì©Œ#-;*Dv:lçÿþ¥fûœÉ‘ô×È›Ãg0¥¦d&=`¿&Äâ ÿ7¸ÊnÌÕöáØëÐV c?h’i)6íº-Q.r›ÄyÜÖ>cÒVµŸpã¤YR·-ûž<5Z¤ÙÎíü¥_¨ôø[´áræ%®g=“áô¤8x{ì%Öm«§ÃôäáüQïyçn¢¾“|ò|Æï¼4†µœl¤ý¿qÒvÑè4+ é2ÿ‚¹°oÓÅ‘ø—ïlÎïX~žÚÓVü÷4Ìò¨ÖñlÕ}!øê¶–¯Õa¬‹Ë`9G›Á ¦y’_>… ü©Ô=â4'äÂâ‘{°6{c,r]B³æYrÝ}(Þ6œcäˆù2ÄCjÏ`…n0·-OÆÀ™ñÒ¬ÓX‚ó:?‰‰Ü»‡I³¦3UÿTp»|gk‚îÚ wp?ìΦo´#~'gÃú}aOˆ$ZuÒ5R‡ÉØö£\ýÏnäõ #_uhÍ•aÓÿÝDqÛFzéÒ*’ôz¶=~ÖxsíªÙ§€LQÿÈû_{3óöf.æ=;šÉ¹?‹™Î«2!‡Jm0(Ð{íÅr‡phfuOCÖãªÍ÷àžò;£Ìd2¡æû%\f„OÞŠ“°Ü&ðƒ–Í#šáNN"¨ùhòýÛ;fç¾å‚äœqÆOkl="‰/…”ÈóÛÛ t÷Nt ýÍ}M0ÆtiEÇeßep╱h“P‚ˬ¸—’Âä‰k ~8LNìŸ 97daž· l¨ ÊרɾéHþWãL÷/¦œÙ¬~ú²¿(+RÓ p¢×fj1 Þ}ÝFïýÀv|GÑÚéŽÿ‡ÿÙtW8¨û‹¹DˆX¤¶£ÏM9rxéSȼߌ8E•h?ŽgÞ~«ÈJyæ÷å=Ĩ/‚Û>‹ Î@מ:%^Ž¿èº³\›Œ¿mù<«Ÿ4cÉl\:ûô©ÛïDá>ï.¤¯IÂEÊûIQ û'ÂÎwóÀ1Ñ#+ÏðE†„É ž.vìÞ¼&«³ù—]ðà"Jåã oe*Œ³5fƒÑäØ#=Ò8¾‡Ûrò3\™ü+÷% 5™pQY$íÔ§÷2¼m}Bñ=ÅñKÝRˆ5fVìë9 òÊn;8é5»µ Ù‘óú#ñÿF@-§RÕÀ=~Fhê²tìHJ@£wÎìð†9ä{× €NdµèÕ³ ÁE_á}hžRB;ó:h§òl”mͦ£ÉþâlÉ5\Ÿ½”ã z°`‡ÉÌ÷}8 ½GžXƒQ{ »S’ºl™ƒüašû]<½ÉÆþL…·AðPüüþ‚ØÌÍà—äúj­àóKˆkìrK¾ÈÂW8I-ä¿:kpôüÑO’}Â\Òðœö-SX: í÷½¡TîX–îFuRd°Ω0«“G`‹—»'h2lÿù#üOåâuÔü‹‡W¬‡¥ï þí°{óMòù×ÂÎô§ÔÚXöEXó¤ßL&cSØÎ3èº\ŸøK¼„ÒOaùx'ÖÔµ›Ý£qÙãåuç½é‘¥ø~–ƒMõ(‘¶¿^©Äc[ʹ&XÄÎß̧ç’DA:þ>.<åOÎG£„[üúr ¼Î@ë@bÎ%îümC¤Ûîa…ç6#=‰õE˜ðÖž“%f‡wq:7÷±¼ç—ðÄ 8õ~êN͇“ÛÂÈ!]W …dк-ÃÞÿ¢Ûe$¸õç0)Ì _²ûÂŽÄ¿º¦ÛÜÔä8Εe¡ÅGC¶uú7p)’g«$Ðoá9¸í)5Pœ‚Z³ˆ^?|Zj†SÙ”ëIðm­#ä!èg/€‡s΢a¸ç5Éž-·íÆŽŸYðEVöé)¨]šEŽîÝ­Óa–q7T‹ãÛ±œí, Ö“ÙHw䕯&¸m‘{`³tvBÑèDôù:•ɤï`Ïå󧀺÷¢±¬¼çVÞÆ‡¬X¹KÊ˰ªé:¬öżÃ2p%3Ì~³@%J¬=Ïʼnëƒt¸-ÞnÕeO7ýhÚëÑCß,~3bÿÿjUdžÁ“÷é𤈵`"3-’¬è{΢·Èã7õ4TzéÎôUJÙq 6¹t Y®e¥7 +8÷NfM¿@ÊU’Á®;6½½A‰ñe„¬L´’» v• j¼=×¹ŽóîrduÉ-PŒU$Fë²aùÕ¨´»Á9(3‘µ¥ «iŽ%/âˆgQ4¶w¬£yodØ*{'²PW–æ]ïçlÇš^P.üÏÂÄ8crмµgÉŸúÌaÜEPž5™øœ k®Õ _°)þ}ý:Ÿ’ÃÛZéòX£™þ 99âÿ¼ùåÜßÒpL1; Kœ¹6å(È8 Q·ä`õ­xØæÙŸ‚¹?)âQÿØñYÚ7‡â¬t1¼<<›Ÿï“Á¤ ¨üõwO¡{j ñ°S-^sC\k`õƒÙÜǧ&­jÍ”V½„î²Dýô]îÆUuð”e»óéÀ ÜehB òÖRÑò:svÝ`P@gH‘âŽbh&qWt.ë1FCû!86¦få6ðuÆ]B‡%w9É£r¬çrðxsÁzXSq{<á}À#N½n„ºä2ë§ÊLj§7W/'‰ÙPÙ9¢³Öšã¸YèS)µb,3v4_X\ß2X¸@—=ûpÓ½KQÂÞ”YŽóE^Ípúû”þõéÄ(êR ~c¯VÙq2ªï¬›†¿X!ðkŠW Bºè Ë\3:œ—f/ÄÄ€^õ ó³¢aK‘;ç•YF·ÎàfVÈòK½›04äOû\#z×zpìÛùZØQzÌö• ïoñî(óëòLˆ¯ãkh©Îûc¹›Ë«¡ËÒ¨Y'Êõ.„—.ÅôÈì`Ø0:6y~¦(¢Î9-ZûgõòV:¾™ÿÝì 7éç^ØÕø_2§%Ïq=)"¬ää4ºä'šÁ@tÍsÃb—Áu‹Š˜þÀ°ß™”ß ab?Àúz:¿°‚ÅÁŠIŽèùN€yjuá‘o…ؾG޵&¡7ÝÔY¬~%†…Þ@[ÍÕ(´µ¨nQk;,ói@5ÉÔäEËÇÁY¢òM½.^Bö3=Ogð¨k¬9@ðjüIXœµ™fG¯ãî~_Íé*êáæw0ûÞ7*¬í‰-ƒûPôœ99ñU™+ ç™þœGµê/qßæÈ³›=o±¡Ð™†ÄTáðÿGðãÖPk úv‡‰kûI2Yß‹éo„S‰œ…*SÝÓ‰ëZý™c yyɉxÊbDzNîo{4ÖãËÉú_«ÈžÙót ÷1H’=òxEø¸‘çf¢Fƒrt¢×~AWe›ô#.¥5s‡ž|+±vö¨{.‘ÀôâC˜ãTÁ«±×”Øi¹^Z.š|÷rf·f,ˆ¬&$?ÿ,EB ]˜þU;ö0æù>¶¹u ÙÑ LpÒ,òâ•%ÜxâB‚š'1™¡Npº¦KVMäëúotÐ+«ûygXo¦sv·3¡$j.jœ¬†GU3ÉÁ`Ô¶~ÅÝìO‚íÞdÕc²ÿ´.ËÙy–à>Xœâáâ©zxjVw¿£‰xšœ$ץš²d=žSYC¶ ¹²è1rèäëŒCï¯ãs¯Dbïu#û±Td ‘ŠðÁõgÁ»ƒçèÇN]¶vY>YžmÊ(ºã1q)UÂi_ªÀ½ßît€O2 ±ÿýΜè­ZH·pe†åûpŸÝOÞ§Eb³»iضĸח{<©’}4‰„5épTá-oî (É(ø‡‹þĺ!øx“*Ê?¼B_¯‘'·|Ws«BüpˆÎéMɇ*½+¸¨¸kîî!&D± ;} ÎêãË?ƒ”df~¨ÎÄ,o‚ôOÈ~«D_t4cÇÐ]¼™ûõ·“^²f§ÖCËåè³;r~©pîÑì¿:mÙX¢Ã£‰8fg‰Á럸g‹\h)Oýhèmˆ{ÇÜáDô éÓMzsu$þ7Í¥uJ¿ã°Eê&,^*HçÂŽ¹ZÔþB,8Þãg§ñ{g8xy̤Bß¡÷ tŸ@fóáà8,\©˶WÐURfè¢H$N& Ÿµ;xFæ,DxŠâä GáM9h^vžm„ÄkÕè–ļu´u KÏÀ a>o²Þ¥aþ=™eHÝ…GO»xFA=üá<_Od;ôá‰ûE`¯"Fv·Ú‚ÉûÐy¶-¦¿Eë©8êO!NÖ<­né“j>ü8¢Ï«:PWC›%nKãÂ~Ú¢òãóÜIéQñÒûkš'rŠ gQÿÄfþÊßû§Õc+Ì9É➬0¼…ÿtƒÁê§Ž›¢#ÐOô-wéî0_ð%+’`qÛ$bRÓ2‘FÄ6ë6T¤þÛ‚Ÿ¹öÞSdƲRÔîèæ>ºf“ö• Èh©åDþ½2»Þ›KnþœNCóÙÖUa°ðM ¬*`8é_†h\†1tÏƒä¼ æ\d‘Å3(¹²•…þ=¯ Ò™ï1C2wZéýRó;-I°kÛú«(ÝJ` QâôáÙÙpcaAL¶û°àÇ&VÈ! ´Yÿ˜|>?ˆç“#¼Tã{(U'Ìú÷ç »îCëUX•hF^D³+¿Ç±v»3§ZÍÏ-¹ÓʈsYÛÀx:öoêÆJ<>U޽]MÔË÷@Á{_øzÌQ;Ë>dÙþKê“rþa°eÓ*0j²$wþæÂûÝ›Èê/¯¹€ Kd\ ¼ÞÿjܧmFxê“Ý~Æûž„éÏ’f"’«À|Ï^“gú¬£­ÉÙŠ’~dtš.þ’YÀ:K§puÆÀ»ÿ²]+ë>„L% ûíÑWì$jÏÝ<ÿJ$`òUîX¿-™UÿœÆD¦Ò St”ļñÏþAà<‘á{¹½vpLü|ׇ?¦*s÷Ûj1ŠDÂæ.äLwÞã’ޟël/ï¯]9Š»µa±öxì™-=ÌÉ›¸Ç2àâÚû¸øÃ]:iæ=îÅ}3l0¨Áº³qB»Öûq•æÒTÁÅŒ E´>‹ñ?ÔIOU6'FÒí±ËÁõwèpž•þ¯—4>H3%çt‡µÃ4lŒ³Cµ«ç¹Ç[æR/£At?‹ñ3£hwÍ X­µ'«Åh¹Ù(Rœ wÞ~¢ïØÔm‚:vÿ«ÿøh÷ Œîð©hH FÈè2K¿\ØZ)F~oµa<Ç8b°Ù‡…뺺ÏLr»‹.Ÿä‹Êƹš¡tž®0nV2 k¯ª“~· Paù’f_í¤ RÐôà -–ÑÙ/F육õk'\[j ‘óÔiµûxìÜ÷-àîpŒ}Žof¨‘Ñ_jQxâbsLæúËñ÷4;,Ÿc÷ÅâÃÜ_ËBÈÚ ·†ÉØTåü{ŒPÁ§ÁØîpSX$HN~–aëšwqw³ºaÇ£AtÝL©Â`9˜´-FÝ諜£¾%ÖÏŒÆuYæ#øµ×Dq.]½¼amü]Y¡¨–ñ ŒLTè ™"é?ŠõöAxKÒ7nꇿ—ns¶ûíhû$CTÞÌE>·ïi´Þy}nèD¦­Uäéün¦GVhg´Ôªb‘à>˜w_®W x¦p7KŠhzWÀ_g#¦ä_‘ŸâyQŽŽæ?#Ñ'”büg¨z&§ bJ5Xé=2JÄwDÿÅïëÆðû5xÏO‰]y-‚~2¶L|ÎEîbÆýœ™¸ƒ™áÚíË4ñóGf(êÀ"ãànvDü=Ê%˜K§Äذí/­Ñ,¹¯Ý¸«öWQÆeÉ…QåKiܽã1ØîŸ Šã[ ?]‡\­Ø)jSI¸ãiì;Ñ õwåØõ§ôï¨K0M}6 ÿ=“¼ŒQe‰ÒÉ輄219üh¡Flþº¿´ Lp)ƒ™Á¹Yñ£ánpÇó©ƒ?»@´dºùreñP–‡ Ÿ?ßùoiôpwgS-ÙřݼÊJ#øwiú@Rø>8%øJ/åBwÔ´8 À*žæ€¬t8H€úäÞ’\av¾Ã l.DCˆÓ-j^®Åà˜2wúÕJ"1å'þW }8N‚e &“¢~/ؽùÞÿ>–œ<5ƒJò†ÆŽäXûYü³Ý™Œ.6"GÍr†¹Rx,>†£<³¹mƒ8cwTœOšöz’ô‡Hõ sèú¦M,“ªÀnÿ}ÞÔ'HÆà(¤«Û×È)ýæªï^ Ï}“áÚk2e¨‚EN_¯«ÂÏļ[Kc¨_o\+Ä,«DHúïtÒÍOx¿Ùh$þߎÊù¯Fú¢’!ñŽÙøÝ’m1î­îj3µV öÂ_9/"µýãù!n›ÙD®”3™4—‰•"ÑLψ2giÔ“Ÿ ?ÃõOâ³:8ñr 2eR\ÞÀC[åW¸d`ú÷f&•|˜1àÅŽ'ÊÂÙé’T,ÉEiyxÜHí{uH7o ;›ÅÝJ!øs+L[¤ÎÖH+¡¥Ùx¢Mäû†UtÐ×fÎJªæVl9ˆ¥…£I^zhŸNÖëúSÁÒâ$—áÒ†vðQ$5ÂÔJÝ™ì\tOžLE°|‰Á¿MÐhA+*Í9OC¿Ü†öàø³G”T# §N£Ý1 ¢ÚÖ^âÇàÉüFÜ5W‚úçCìþy¬ìý_/ÂnöBP†;¼þ²‹N"—uP6~ ]~ê m¿LÏŽÿÿëµÏW¦ÌIYj!a vŽƒ±ß^Ñ£±M6_PÄÏ€Ž›VŒñ//㌟žø_¯ç©"Dwœ,濊§cëµX²š$Ù°\”lþêx|ôfÚ{BŠH'I‡%gpoŠ<³ž Æs_ÆÀš]0*á,þ!‰±'ðç6)N k7À› 8µz"ñ/€¶£dì.m*$‚ŸháA'âªþ<ž¢¤ËnN§ëä8€º©;–†›´l˜èÏ`f¹¶×Ùò¿»‡áó'âä´]/^wr¥úâÛáuz<înÂK¯¾C—R,[?ëõoÔ²©Ç»0ÚýPÊÁ)mÚ+¥€„r"·§sŒ>³&œëåEŠÉv[6ëÈ70\§HU"-ïÓ©^è¸RøåBáû«Ü«—†,°·–&_ÂwC­4#?ÈNšo³óê’—FƒzÈOúöè3ôóêÆ9Â\¹Aª—«3Ïí’Ä@6Kìç\¶ó@AX }Tmˆ‘°9ë+}LNcñ_Æ_rÂŽ…6ßÇåI,o’:iØxwÕ ØUs‚ÓeˆÎi26®¢o=ãÆÝ÷_Í6ŽšE‚²áCµœ_ÿõŸþ?üíÙ÷8^c÷`ê{8)‘N¿ÌMà6šÜ7+¤È›!4(ÞÆ›·®ß2&². ûÂ'hxõÐti™Ü¥ÜALpHÁ7g’wmH°t¸6}†ÁèVúËG©FIv´O‘$ÔóN\ÞOÆÏàZή »ïÀm­'k—HÜû\!’sÉŠÖNL„ñgz j˜ÓËñÏ‹)~–; _†ãeééëˆm )ú›kðß3ËbÈd‰~ì2B8´q¾/×pýâ§y[1ؼ~;i”sbË¢s !R•¬yT“äû9笠 8}8½+Äþ}£ú8÷Ö[¼…¶¦ðvE®Ý,ÇXr1Omz ÜÛ*†”ÈV§x\ë$Glº´™ýàDÚ©a©!çÂ"¾mƒSÇ8TÙZ­KycËãàÖ“ ГœË›cà ðÁdIá‡'œÖ^>î;K1u8¾çsΚr8V3M%ÔÝpIa¯RÃqÒ À­A‹áZÇ#xìu»vžÄ%ýìh[]xíjØ$¢Ï„÷:£Ø°®<ᘃË/¬†]•:Ä- ÊÁùQû¹ªo äDÒFÓÊÃëƒJÜW #X9¦u©ã×ipj0‹÷ø/;My‚ì~C÷MäT<4ãÛ¹TãŸÊ,vóJT-ûãN\§‹6˜Ðüšzü›8œ“Î)ãÎ9šô¶TÖ5ž ™G—€Š÷Wx»è+Ú?'Ÿr^áÕÒÕøgÂwà'\ÃÖ$¬ ’Åk®œGeUÝ9crݤ®Ãùéµdÿ—Äq¾×àrœyÖ‡n9Åçr ó‡yËE¨¸ôû—® ÅÎ)´à*:.þkÄš¹ƒè¨¼†-3±òx&®›–§¦­à|å5‘F(á<5/X~&‹Š¿…_WN'(W kC¢¡hb0¬Ý±ÿë ½{òÿÖ—tþå ZœÅžýnÐüì"fM1ä2 x£D$éåø_páE:,%0õûØ|;vÞ‡Ž›zxm.ó;ŠMƒô_w1NÕ,rʺ÷=¬È¹ºþü}a0yÿ_?t‰ËÚ€+íí?ލs¬”OÏUpóÄX/ÜŽ’ÉèãÅ7{)çÅI¶ß}®®ÿ*Q°vºÇ0·‹G¯ÿ‘Àî¥IðÎd ”wÅÖ½Û"F 6禢RìhwÃßçpvô¨ñŽ##úßHÒœWYg§+P({˜lÍÅ{»_Àç¾u¬ú"¼ßØÛ†rœ»)í¥SÑSV%Ô>p‰¥AµáFí¨Æy£4IQ_¸ugsÏ\Æ/FÒ¬øN.]çö4Þ›’SÃz÷¦ tÞwg“¼O{+–=G™Ü:Fë„/òucÆ¢â^ÄÇqÜdÿýØÿæ÷C;·t¤ÁªÞ;PüˆÏÍú=ìBù5òt{Ò-j´÷..œ|Œ»õ.ŸÏߊ9¥Ôb×lŒÈ)Ÿa;žÝÛí‡ß3Wq zêÿÕÕžªdW{áˆý‹N»C¡N"Ž»¶šy»PÅÇIüÌ’JpÙdG”ø†ì‚€,ΨzË5Æ2es[8¹ûþ{˜€þ[£Aÿ÷2¬]½„k:÷ˆÎ>¦H¯Þ…­_ŽrZ×=`™_7<ù°¤.º¹©ìQHþ!ËéÅ(°KJY8½C~Z¼äd†Zh³fzìKÃåúnìæ¨3Øœ¾–§¨xK rdž½V‡ÏžÞÁ†sq±¸9aœ £¦‰³Ó¡r¼ªzI¸zÜ “tƳÀ“úd² ÔìuÛN'žß_ }y—þ‘ZD‚ƒoÐDvÞÔŠñŸŒg¯¯‰z7.pÂKÓY-¶œø5#bè“Å™[äÀYùC¼‰?§Ñ}…¨o 2òù´c8¿©˜øÐ ¯¢ìc•=yV]Œÿõ‹È·ÀlK%²>PÂ’ñÛË^þbåÔ}ÐËâ+à‹òMȾ ®JduI v]¾%ýixg’ ›°I‚i8oÂëõ:Ü%Í“_ðEd8¡5©0™wl| M×`¹Ò[Ú¹ö9x&>äõ´#/ú èèû‘ð{+pš“3˵vÅæ¸^Øõ%›|è bÛ‰&7@ºä–žñ•{I0.÷>y’=‚ÿƒo Té=§ÝÍ3ÐW­‰[®°ž˜eüWk|åÕì&Ü“!ÖnGxáïÍ ²‘ÃÌ„zl¯X~YÎlZ 7¥ózB÷¡çäl®§s—^Zpc…[¸‹2ø6Ĉô‡$ÑÎÊ.è#L/-fòç¡jR(,7¾Jczt™7~¿ ¤Ÿ»³±úÏapOàz¾é¡ÉÚ4˜.•W»~Á×Ðpìèuœ#‘Q?†Ð]¥‡.<¯Fnh_ƒÜe¯htd÷u‚#ÚÍ” ;b0È*šºÔ€+^l„U[–¢ðÓ ¤Êí Üö~‹q\5mÑÉÿ­·”a—ÖOXÿ5O–e`CýÖkd@^M*áNÝ^Q/´˜35"¶y×åI%s?Šÿðþ¹b¶ˆ9vcјqÚêLXhþV¼Ý<†}ÙR²I·°îøÎñP!xNžˆžO¡"β$þêoPðWbÎ~½—ZfÄA¸îè½»[§=ZÃa7z‚Õ–Àÿ §Ú,Ø{Zh)@çNwtá!qå6HØ#HŒn¥âš¾äÛr ö~¥KoBáñ\œq-}'â+‡]8ç`ûâ—ôMx<Þ wfmúQ ÂÎúºÿŸ—ZË·Ž2‚sGSø’÷[ánš+‘Æ8rþÊXìçCâÕ]äJm!5ëšÀ–„¦€³ G&¯R#YÓ'cNÿ9LçÆRnob^õqÌ<½ó’ÔØ»K±èXÐå{ÎÃü·Á,ÍÚï^1do{—‘zsUl–‹f;§ÇàÓ?w¡Ìþ ![f©MÚ¢¢þëñŒ÷_¿ä<ãàiT"$‹L½*Hâ'ýà ¦¯$ÅƒíØ¸E‚dÎñE‰ŠÜµ“²ðú¢,hÍIƒa<œ¯{˜Ò‹êw 0rÍ8-Œ:º1ýH[ 1«×8?’ÿ'= ¢G°¦~<ÒÜ[LÉÙ'…\üb{.×Ĥž#ÌŽ©Å§/~â‰Ìï´4—'Ó÷×bëÏ>”ÊWá6H˜`õV»Ú.ØÇåõY³AUÙTøþåßÈM¡ŸŸnÅîÝ|¯Gͨþx×ð=™F%ã®`§ÖZX•hʤóêA¢ìÚ9Å¢Q·ü¹õœ«LšKrÖ¯ƒš0ñf8‘{y1`k­ 3ĈËãpnà2¼üø““[‘¢EGéšoUôcœ7oxp¿–g³ƒÖoPWÿÌ8>Ò|£¶¡‡°×¹ÓÒáš‘2ÛØ¦Äº J¹ƒ3.B³â4^Ûøk 3-ˆz©ûcæòÝÌ |:š.ÍÁ™ëºà3˪ôLðk‚fpµ'ÎBª`ÿ'??|7»ìx×iÞÚ}ãH´ØuÁ¿2ë:fZ`B˜ÝÄÑÒ8Ùìwùñ7¬xé‚oÙ}*³1úø˜ŸRšÂÒL|‘ú; “²šW4[X\ŠÙá~_¡ãÍö¡J++Ÿrºãù°÷wmmÉ Wñë4ã£ÉÔ)¸ðk&ÿð$ÿPÙ.šÕ¢Â”ÖÝs…ûhtR’ùçÅc‰ÛR.:þ*Í~…K®ýÁ K#òäléWpèx†ÂV¦Üž˜ ;6A‡B0Ëãq]÷©Úƒdhμg׊³ÿúO‡h)S-ƒ4x }Ñž,Ôò"^'[€ì+‡ñ¡ÂÌæ¦ÄHü_þ4”ëŽýνþ4œU\ñçöðã–1»UÕ=N Aài½|&íÐ`‰ÏT-:/ ÙúƬ;WŒ­Kmĕ׫áðè…(d²Š…,i¡åãˆÐ  t{«³ßŸU`žõî_@ |Ô:ˆë‚ázt,ìE ö-%/^§ÔHœr6™{ûËö A¬_,ôË™~Ø+%Þ„‰/ÖbÏÚBXÕ²˜œóõ‚žù”öЦ÷Šs‘®:†éßO`ÌçhJÃpÌèå(Z6O^dÜÁÒøhâÌGã®äV+hõvœ»áýÿsä7BþÇK555°¬t›U0ø­n}íB"æOåöGŠadi4¤ÒO¨£tˆ_Jæ.ù'»’D[ý2nŠ:DIÐú0f/œß×EYV$fÊq0ò?ýÜ0}¥ ùÒíƒk•b`«Þ|´]”ܺð€}}ǸD_½Áq1ïñáq?°’÷£quå‹qÎÊÛ0ùÏgªÿâ»D¥ ŒÙ¹'}X3Û™hÖl¤I«sÑEy»øDŽI Cל˜#ÖÂùK¨±0'ñÄ"]ø°`&Ô¸=|ìwPèQÀÝxE&K™ÿõž |HŠ#‘¸Øg5§íö«g¦‚pþ0/&É·ã‰Û¼Z\¡õdø÷×Ô{Ô+zœû§ü]BÞ“Dv¥w3c'°C¯À¿KƒYÇÜÄŽ Ø¡îߨq°€vÍÛ‡þ×ëpß¼<-–Žæbr¬Æo7k½=SÕšD²ƒ¬!<& •»^!M%{NDÏûð6jbtßGÐ ®§7ÎH±åe±pÁj5iÉIÀ¯äñè×#`¶K€Ì­çí†{–=°ûú:~d”Øž ìÐÜ&´ïæ…2rÞ.F.Ƽ/ƒ¨ñÛzdþç¥AÜÁ¬ºÎÜ­`Ø*Cvÿ¦'*®ã­²L4ð(“io ð€ H/xÆÝ–rŒ ¦pR#Žþ÷›ïT/rhâYÔ‹&£6ü£÷4çAôN”9¸Œ—««ÓÆ»qµ1ìßÀD8;†&`Ó&K±6%Ea‹X2 :(“J΄MÐJgb¸'.\Idç_òz=œ jÃÓÑÐ=ÔOæN%'- ÈI—¹ µ§œ†Õçó0®$I³)¼T–ÓnM|‡g§¾CÆQAöæ»*ÌÚ&‡W<œ™ÉÚa®2„›/?ä^,Óä~|™:‚ÿÛúÓè¾sÛÅž@~m Úgy1¹-Rð¶?\Ó½ CæMý ÷ÌÓÀrjÕþ±‡·ºUN­S¯Æ ©º·l‡{¡ ,ce½0Yž¬žö<$6ã&m’væ.nª‚+ÓÔÙi[s^í\`åªWpüÙ5ìå͵àЩ†ïŒë˜·ë$ŸNïÍ×äŒ.ÝÆ¤å¸Î’Y$rÏ4ÖQäð ð—²Ë5?W†–Khïú ¶Å%“Û|vn<©UºÂæ?¸ýR§É³À’ÿß³Ún´5K×=€ƒ’ñÁ¯Élº‰"~TI„¦(‘ç…‡o@ÿŠ%¸mª YøCð¿¾Îœ¿èš=k•¶ÄñżÈé´"ÚQó•¾|Å5ûÆ€Wêôš¹“ÉÎØÅ&&Ÿ†¹q¬By,AÑ(¸ë^;5‹äƒ•–!ëø¸Œ½ õçÞueq¯6c›]"È •Ål&FAµøH5¹o?ÛæâN—=pçÙaì˜÷–Ë['O6G¤Ó»EÈÉ«cþëÛ—å1 òÕË“íQ‹‚±ÉãñôÍRä.c¡o¸ þæÎÎCGçÏè6ûÈoz¶+ŸóÙê.ܳ¯„~7q§ ™;¿qÄþþ¦„,YuüOY1¥¼F€ïÚ¬áÇ_ÞªÇV†ä0*îÌ"ê×€Äü~¼Òˆ§÷`M÷Ç Ç®RLí¢ñª¹¼àÏpmU9Ÿù㪱1è×HhK³>Qu¦,>ÁuÜñ¦4îJ5Ñk=:í ?òkž„iÁ1$4ÿ½ôk=þ^Šo¼V­p=ˆ]ﮂZžrwÜHØ«tß-Âd‚ÇÓK¿éÅa^š¸9$ b¹Å ”ÖÀÛWT@½([^ôDlÊéë/ÂøË^׎¾ŽO{õyî•õ¸¹6–LÁ[æI#öï´iç.ÝŽƒŒrÇ¥î-cÊV ¨+!]èVLÄë IÃ/3ºÞ\Ð.¿Nn´ñÒ] ©ªˆÁá¿zè¢ßèž$M½rÞWœÐ2دZÏïp>¬ã®¶Ãλ;˜äØBêÛWI-ÿÂ9½…ºƒð{Ho^(i ’µd2î¾ Èny]âõ×a»nwïh oÛMRÅðæ3c"XìMÍŒçûpPû.žP’ Ñ;ŽÑcªsø–xãÅKÃzèìpÉ9‡["Ы³€Û×µƒ·âp›£‰Sب{3Ô§Œš ÕsÓLÙ÷ãU4¨ã¯¡:„•þäd.{2¿õ™è,W¦? ›1Ý…$VœÅEÛUI«œ+¦ù8•߸ø­uðéŽñJ½Ëe·êÛd'^n˜ ý‹i}ò¤2™{àI'Gô‹äƒÎg{R}ØŽô©üú|ÖÄú¼‡uŒ ÑLøÂíX¯ÄÂì4Éã{‹pÔ“tÜr—ìÛ ª}­0>J‚9§ŠBQèM,‰Ò_u…£EYÕ†$z`¡NZÇ6hdañùw`“¯À\o|£*›é>GÉÿÆpNM[FøÍáóè8Í7þK ^œƒ-ÓÿBÖ^{8äl€±ƒبIçèU-wbùá ½zò3w0qÎ’jÆÓ[áÓÝ687&íÂû`C8ôæøã›?yøžY±ª¾Iœø1|û0Ä ÆÃ®JÜqK‚Êëõ΂Oï’¹ÛZ꜃c)Í*MUæÊÌ`>; )fN2äòÒ‰dâFyt¼< +¡`Ôp9‘‹-ŸðwЇ0)ÁürÆ‘Ùwúq^æ–ËéŸc¸Ž"G,hYƒÍþàÄhäOZhÃö‹õÑÎ×ßðíòŸü¶?TŸ½y‹p3×í‹ü³FŸ@@T€lÙ…‘Q¬Ôì(j3X˜lÎ.dŽ")Iâï5$0­ Ɖó´sqòûªàuá[HÜ sßïcÿõ~^ëЃ;5MO½_ƒ ™,R †Îã”E‚(×ÐCÇÙÀ±1‘ô ÍΧR‰´ÌÚ‹/&¢njpÿ,Ù˜ð6Úà•Îm95ÄY¯?O×äwàÀ'{ÔMá뼘sÞKð¦{ç°ëÈ=ð]Ä=pkyŸâß/ž†è…õWv²³OpæQà!åIÞÿÂxˆ=CEþÿúZ^Þ†ÛÓþÐ_æ2Y£û Ú”ƒÁ§Zøî×+ p³‡ T#ø! ¿‚Ò=±¨3ïä[Ã5Ë…Ã÷êµl0ÆÍCœý‹L®IÀ™ š™’7³¡¾õ80˜X—>V’q‹ç‘¹¹Tèp Œß%ÊftI‡L3bãßåØ`³<ÄTáÅu\ñR½¾NÀ+™a˜Ý)ÅŽõL û%»áöT;¶%t'þ~×V·³1J<ÝÑ;ú8ˆ¥fà”ÔµÔ2{8wäÞ¬;÷º25{é=³x3oæ|»M S¯CÌÑU¬IëwçKš¿öá¿{›€‘ç]NA+Öçš³±“}ÐpëjÈôÔÀ—‘»ðÞ! fpø=úìÖ’Ý^sÀ÷Ìr¹C”Lÿx6E$a]ÖX”þYÁ¶~ãcêü^nìŒ=8OÌ~©®‚ž-’Œ¯<Ú,@\7‹áÊéöè «ÁŠ…Ž²Ô­LJ¾oÉÒQ{®ðïÎ`+ÚÊqÚ¼z;\ _R)¹ßJ>h¿A;M8-äÂ4•÷Á‰«ÓÁ§Ú•¼ù™Ã¶t˜²ì# —‹ó˜ÿ¶Otþ)qvøö1àÏÅë‹ÿÀõ‹gȸ«ûÈ]Üa¼‘zΩ‰ÿ×ÎÐÔÕ6<Å»·¡uµùø"o?5$[Æ­Å`“]³ç °S?Á¡œ‹4m{Õ0ÞX®Uð÷WPÜb.ºy꣘òAêT%ÉŒúL`qÇ&Þ¤%áó¸A‰ç¼FÌ% °ñ#yü&®o¢3µÔÀ¹·†ZNadM•Ê×Ç/> ¸èñSX:Q„µ/‹ÀѯOÁѳ8j›2¨iïEe½YLIL §äDĄ̂#¸Bâ.ã{«‘Q!wÜÂ-÷ãñÏîߘg´˜·~0:ÍI i2WÀ8¯ƒ:,™Çýr ‡ñ0ÄÛó©gdý«rQ¦)ß9Šñk*±sWøEy°£·ŸÁ¤FîÁ‰ñdOæKL^›Í½WN‚» ÄX—^ ¯çðL˜wÝž;JŸ¦²¼’Cœì9[Œ×ø…ËU!¤Í–”÷4Áâ »èln©p09«Ë¤ÄðØ\m?=ˆP^þîÎ^n dëRìÉNÉL²i§d­‡‡O‚Õg˜‚AíâÌàK 7¯© +"ÊþÿþN‡]Èâ¬MLãÛ}¹&v„ÿŽ}x„K{rÕUêaû:T°¯jµCNF=Æ)N5$Ç?(½Âs¨¿è8<]®ÈÊÍÓÉÉÁ)ìÄe 2±V˜ÞþµÎä¹,1þyþ4epÞ9ZØ2ÏŽHæx³™ù4Âæ;¸yvÓæG;Àê‰$û7•޾"ŽÏ˜ÆßÕLÿƒ"轩FíY3áéTov`õ#P­“ÅŒ%&ÌäÒ,&rÐ:`NäÏË2ñéwáOHZ À¬ÑyäË÷dC‰÷H¼…Ó_w?+˜Òí‘I •VѺ#øŸ,5ÂQ ¨ö…<(içJ ÌÈ£uw¡òâtޙƕ(ûAXú&nã. vZkê >òL—ÿ ÕöX§ÀAïSœøÛšè\KÃû]¶0Ûb.»ÞB5E-PÍÐV¼jgSq&™¬ éò¡üg¥X½uŽßÞÉ­ëh‡J_ô|}žû€êWöaÖÆ[0ïÀahnÈãVêD%–g|§B^ ÿyÒDy§¶ïÇÇV=ô·Ê Nù¾û(TrÒY83F·‡ÎÁ £ikã$|l"G&…]Oç£áãâW°úÍ‘üï¿v4k½iDj«Ý0÷ù0ð ö¸44.= ÁuÉ×p\Âæô»4[³nHÖÎd8¬òÿ`B÷3ؾ1 ų°ÅóÙ<ƒ`2æÈðZ?;2h88€kã®4¾’yøbÞÏ2î~cy&¼FŸ‰xähSN¢±ïÆ%Çà–£:ëÞdNz÷¯åîÄ»’oLŠX¬×e—%ÉáÅKaÿ$Rùe4,_Œtý|”TOzä܉ùyäï®åä'6†RU¨Î/~Š3?¨ÒÅ­d³5çy]”—s*:yþ!õÒ]¬Spò‡E°­â!;È»€Ï &£tD))¯É‡3n…°ƒ[;¬CŸ£Lh Ó6®e?—0•—?i®Þ0>¶šœï›K;'¼aV_hÚÖD–è<€^s‡ÇÚ.E#ßíàî(Ì~:Šž“Ãɸk7Áqà†h‘â/:,N ›Íšv>=O!¯g2 5‚p¿ 8¿@×0‚Â+DpÒÒ9ü ŽÐ’c´ê´?8ÈfÁùµ²xyó.|®š¨×ˆ±îõT)öõsq,ª]粊›2;£/eÅó°4ÂŒ|‰ $ªMÍÜÆàø\Q¦‘ã‚>¡0˜ÑD ‡ãž .xfè|ªÀäF[ºsT4ª–ƒ«åm³0!óÒ*xËU×3«È\\›ÝB‡ÏÒΆæõã±ogÈÿ \à!»¨Àõt”~ÿ‚®k Ê’àþ‰PRòF _‹æâXÚ»\h]b*˜s‡¤%9èxb.[,, ïcŸÂPŒ0ÙÝr‡~ÛN¾|ÞIzþÀ¹/‘\ðò`¢ñ –›Ð¦ êyXÅYc2úC Z_÷!Ý™‰0ºûͬWEÃÎàhªÄ_çÀœ3ëYƒN-õýfJһɜ=›áç'>øÛ¦³û¬q²à5XZˆú'ïf® MuÚóÕˆAîfšXsKeÀ.ó5D)ÜÐÐ$-Çf7ú°9ÿ¨Åê4RµÖÞK壢|ùˆÿŸ½ô—º‰s: ‰¬QH€UMÄ·ã³Q \š©TÞ›ŽLÚ\Åpœ{ÚµÃÃÂ|lvƒÖVêÞæ„·¨À?VøÏ¤ëéï/Š$·¤¾þåDf͇+—õÍOÆé㬠ۣØ rÀµ—”ó4Œ¢¤uy¸3&x{“Üe­°ãÛ¸½Y”eú OUA_‡øëó¸ÒÈ=¬Å‰û–•ZŠôáÛTîϦ$®òë[îܲgÈ=–œ™ëXЊuáÜ"‰ïÜÕôpzýX;}ÕdõÊúÌc.T§˜NÏŒð¿7GJQ!"„“¸1ÌÑz§ ÿÝO·z‹ [æCÐïþ]£ ÜGebÃì™8¿ ÖI溺÷aVõæ#úZMÉ}4ñÈq”HO…¿áŠDܼ†ßo\Õý=ÔfÍ,hŸ¥ƒÁ6G!,Ô2ùhæp S–O#ÜeMæ·Ùˆ=Õ»a¿¶À™€=¸îj.´odJZVxoZœK-à®ýî㶌ÿò'xƒ† 0W>QÎ:€Õ¤‰ÚpA¥µÇ17Ö“éöžÄãÞvÌúØ{tÖ7`ÖopáÚÌü”1ÏñÄŒXu¥Fì¿õï *|ýþô>?(s:ÛÀ›â¹\Úº™†UNJ„÷¯_Ã_;e¶¼K›XÉÔ‚ó‚N¼XXB¯ýé¥ÿ‚²pg˜,K|X†*Ѝð®ˆSûÙŽ{ê´1Àå-.Lº‚å;±h¦?Œ‰~Ìß½š=É4##ÎË©†öòñlZë5l(ÀÊncP@›¢nCA Ô+⾫ýsj‘ç݇ö¿spÿ$!¨V‰ÿ¾Dœ-·SƯ‚<Òîz~»T€àÝ]DÏ 3Žf@öÚ ¬±ÏŒg¬fN‚”΀þ®V(º˜‡q£%°×ú?S< gâ‹æ&‘êkóÉ­¡T4x²çƱ»Áûì+ަ½âUnžÎ^\Éd:ªâì¿>ÓG._•™¾LMù<(<¨Æe¼áú‹ZxÄtJZÐÞB‡Íîa™z¤iM$·Ýð Nx?K‚–s2þ\!7V¤ä5b°¦ð,—䛀B-ý²D›Î<†ÛÝOÁ¢I˜8ü<Ž»%äˆSçúãéz6!6jƒþ÷ýW»…9´Üê9`¶”/'FñÇ …ë§ ÷Aòl¦(Lfˆc'¢œùXí÷8|²ÊŠ7}‹={?5“†f­dEm‰ÅÎÏ´ñø ôàêPL›p ¿WÄ“‹ÈŸwǸ”uMôÐä¸ìRö3áÆ†N$~šµø±y J([±|­ìÙê‰À9UØ#,œ¼‚ìëpç“Û£9me>Öˆ;“µ&»øg-/ÀËÆvT” ‚y!:äâì{˜Qôï— “½KŸaa¿ž9®È”wSÍ«jäJ nLÅî—Ä’Ó‡~Á¾–ÿÞ}ýßóÿ®}÷ø/DÚ¸šü%hüÕ'+¾À&myò6˜²™b¨û$OeÂ×p:¡üH«WÀ -ÙQ dù]{ñu <‚;+Ba}Á/ÜRh{õ¶r«•›a©ëÆ5>Rd—ÛQÜn4¾-z)^ÕtÖ"RîFï¨;ƒO /†¤®aïû õV´ù#F"¥_ýC²ä #ʬÇ& •Víã-~~”êò–ÁœkcH|¬(y³™ÃÇRìAÂi¼¯:üùcÿ×ÿTƒ¶ÙFa‚Ww©è%ÌždÏ–þÝÈ_ìu‹~Àßï/àƇËuªP=˜ÆÝM#[ló±gž{`ÁÇ w‚QJìZm É^©ðÒ-ìšz<9fëjÔHjßy¥±^L€Ót¼b`OJö?Ì]ŽƒV¼|Û#ÜØä[u±Å¥è»$†þ3M„9üVŸÊ[ÑïW”ˆÛϱ¨ BïïMæ6ߊO‹ÓaûÕ8/wïÂþ7g1<éßF(F$W/¸ÁŠ—÷±;î2¬ ò¢µ©ÒÌι$å¶‘øï°«ì„ à*ò·*6Á•Q©ôñå ¨®:ˆåBá,ûžùVåOpsÅF¸»x»[¢%Ý\EÆtX§Ã._°ÁÑ¥+ {×ðùµàwÉdðïÉÞÖMc÷[½QÜ*æi•sÿrŽÒß»¡p’Õ`ô]{)ÌÓ:Òó([× K¾«³yz"XÛªÆV+e±¼q°ñ`.6m:‚‰b)4à¸(6v#ˆÂ\É(nwÅœòD†­ÿ&iEÌþš3ÔE.gjÓìáa©DË–Ñüã½xxI x®ßеß} -^‚ܼ®=bÿæk7Ð~üIþðf¿¸Ö†YTMEÛ'P1ýÏu]òå³áÆm1¶qª(ÿ{“®¯ À¹jÐâîLûÿÐŒ{AÌ"n–ŽŽG3KfþH –›ìƒÙ^müsyBTµñ1ØŽ½„CVPÓ¯ßi•ßKî…øóÿꥩcÓjدu„\áÏÎ]ÎÞW¹¶òJä…F@Ú›~¾spÜð(A…ª1dŠP!ïöîÇ(9A‚KIvŸJûƃO©£ï1°Aq’g{Òs¼‹,qìñqXµþª+ÌâGnůՇ¸Â2]2jóxöyÝÿæÿ©”͸{^$nÓ­‚eÇŽã±ovl€mVÉø­6•*^±cž+€ûM¦V_EóïàNÂþšP·Hc¾&¥9Â('w|`+Ér òù‘‡¡áÔ_èôœGN‡¸Àóïa¸ê½#Ì=s™~y€þc¤XÕǯÈ ƒÉÁÁt×¼æZfUŒŽ½oPÂ2‘~œÂ=‰Pf•×pr½Žìbú;ÞI‡cèvï'71Ùæ?‡ GM2·FoD.`»nÑþúpb[ãçD½u†ÌæÉA’/¤ÂJƒ AúÉZ¨X%Ïæe¸C£òg8°Z“³Ýb<²þŸ¼‡´Ž!ÎŒ¥žÆ~̤òjMô£1ä#>úû h…éŸD9Ÿ}oêbÓPÊìí½™LWþ–c ŸþÒ§\3©‰£ÕÁ—¡éTž¨%xuá\bóLˆÔ>2!S¯D3/™oê/ËÞ®Vα PôÑ–õVx³´!™y8»x¯„8CSƒ4¶=²#¢(ºñ 5‹Ï…ß‚wáÔ CÛ 9ˆ?¯.%ngc¸…“ñî)¨èuÇ´ÃcñÙ€Ùüë ¦7äàhåYpK®ýÝO—Ÿuç„ÉßbIöE\…ü‡KºñíˆýgÍ¿ú–ª i7­'uá 8r™ºg¹ÑÖèãUo]C‹Ô!Ì0_ µo[ñŒ‘üó7Ãišäý \kKrôT¬†µ[Ó±(x{èx€žØpeÄêàÓµ2Pâ]ÙB "ê\‰GtÄ á±mŒÀI5½4¾‹À‹—àÌ¿½à, CìI¯Þmniû^²ÊÊ¿,a+G‹¢*?èUýˆç6‘½Ë@kŒ)°YW*ŒÙö‡ìaþ ¿ó§Çpjå= %Äâ µžž@Ó…H–b*jÃü¤ºÉ@NÆÄ¿ÒͶ ý¦„ã;Û0ú^‰)œ‹ÕýmH_§ÿWíØz%ÜÓ ïëvH2ºá˜Y0?¶ŽáŠ<æb7aðÔ#tþ3KˆLØÈ+˜4€Ÿ«xÌ¢GŠ ™œÆÍNì^}KÔÀàÚUœµÛÊ«_óûñµâV š¹G%­øn\û;Ä×2¿`ÕR~~9þþ:–ܼ¦‚õ)ËJy¸Z`.ïMþ8|W‡Œî߈v39S~%šæè°ð’áîðôÖ’æß@Á~%]±.Œ_1˜ËW?€á'ü`ùÖ:³ƒq¡^°äóבõ†¥‘ؽ¨¯²(Œ%³´äY­èöaÓ¡ºc‹Û¡å^ ÉbH:ŽâÊ+HKÕ×¢J}<œˆkO‚þÑ$.d­$L­ÙÇY„^†ÄϾük“ÂÞ©|juÞ•¤ç¸øÜ}Ì»— ÉÀq×vn¼âQj}iÔ}›¹4ÙYÌ¥DŒÁÏB[# ?Š÷ÐK¾ÉØzK™‹xª‰³ö§aëG;x°°43ŽpéË.€_q9¶»†ø%Xúw?ƒk^d ÈUIqÏ9y…pt|c)ñ]2S¦e¿Å™‘¹¸þÃì±ÜÇ;uvÄþj×^C™Ÿ•×…˜'$€f„1û·xd]zÄ}ŽgóÓÐv^µ}VOSEH_Èðµ-¢óóvÖÑ$5KÁªÖÌû\»–¿K¡,Y¬WR& kÁÊ\RrH˜øviàêš484;ÛWŠBHt–OÇøïüs©'èÂ4˜“,M²̘·¾( ~Õ˜ãèr8f•'Ô ŒMÔ;”H…Ús¨¬íƒÛ‡žƒ±°þ¾®_öœDo•*úÕ=’Ó>ÕÌEðg²Ëž¡Ó›ñ ¿} \xñÎ¥ÓÎÆh™X†…ɾ¸vž=5ûþŠwÙéŒ58à õ`†h"_Nþ†Î SÎÃú#Ià§@¿Ë ¿]L€Î])˜+%ýF!¹L°}™5bÿñ›!öÕz|ò¦˜‹Ïù_ÖuØíÉxÏj?›!”G¶zà×nº#ž }ãæ·Á‡FàbΪ¹ÃÛdú_¿f«¤^h68ù MØ«8ž`]8N‘UäB×=…U-O¸ôA²½J¦ëÔð/‰Ÿ¢mz[`Tu›²ƒÎzöšeñ¹ñÜm÷˜‚'ï¾;jÀu&≠ð¦ÆèÔÿ:îÇÁ˸"ìþ²˜Û?—^ÅGç9‹Eñ`]BeNpBA'È“åS°ÏYž¬¼6Ò[1 §Ž~ŸE„X#Zf/Á¯ñT’ºd?âægp²ò‹qj…8ÉêAñÓz›ñÞ5GÁ«vB·4 ¢Å×kœnG#ì½– %_ôÀwY4nù­C–|è†XµE(ºl·:“ÓXjCòï܆ý>_¹5ÒÆl¬´.&›ÆqeçRÀâo ç÷C‰÷ïü*ô”î¢ê'_I‚n´9¹¼í9§[ßk6¿äÜS³AaÆvªkÈ¡¥»¯ŠÔÃ×Ã÷àiAg2j×:sO °j9òiçX||o9±x¿Ý;×QÞ îTÈOï½osÚ”H{k"̳¾ ‹—öÌû§yk¬•Aº­Ë3ÚÈLîÞ§ï“aO,¶ÁgS üt˜úoÍÏ Qì$·éQ|Úÿ¶;¶¢ÔÀkxúd/ç×`E‹?‚è¸ç8¡V"Òn¢¤4ÉŸbN~©ÿà ¹(ìµ÷?ëoܑɳàæ'ÂVªu¡Ôü‰à½]€5nø‚+{$QF‘XÝxˆ?Ì)¶~zƒƒ‹¦`,Ì@=µ8ÜQ CJŽáuØ"£R¸-{¶p{þbBsG³mCŸ†s_6¬Zä†o]Î`¦~N8–Ï?S¤KŒ['0ž´¹ezg/ ±ÿ®)m4 ¢Ð4µ!ä¨hbc½¹pü H®0ebcNÉ:lôö7X/hHVÿ¶bºãÙ;Ë-쥶1»He t›4¼™ýdî)°…r ¬Àüw-E™œýû MaIòÇ”cé”ðªTbSfáôÁ͸JÊô™GÒ‹cWô¡(µ"‚(Ÿ!ÖÓIlÖC ~Z*¤I̅ݧüö´0”–Kt#íˆÚÉkx¤k™c= dÒêœ^4öKr”kpòo>KËâv%>âxNöÌÌ»†ÏÍÝ)ða‚üâ„ÓÈúïÊFúâà3P•þéùÈ˵_íÓe·Ddñfž1¼Ñ%ß—Y° wO¨ä-Âõo€‚}³Iä–Ž=áÉ£ÀVlZnIFêã±ôâªrXq1o.=Asw×ñÎm§Xrf쥥˜½x*tÄAv4n}¶e'ä¿à§My¸ÊÝ›½\i…*¯¸ù¸¯r>øZYëÓô]ág¾89’ØÇm}‰§R$aî£îVV,¹xyOvM]ÎY‡¹‡£ÿ ÕäYÜ…~xèê5Ž·£ž×%*Oôɯhë)ÆÂ{S¸Ô‹GGæ.}ºŠÓ¸­až,yð=tä!zÇÑï–<ìÿÉÖ e F¨ð€ºÞv\•ü—^?…êëªÁèÑ=.Nè&ESnã×w˜}.Ô×ã=ja¹nü‡;A¯í ~4yM·=“ÂÐI§`bÿR [‡ÒB?õ©Bî°ÏJÕán3<·Gž½ ÔAýóÃò“/)Ëùó¸ÞiQ=w>Çæ²áñà·w"+ 6c.z·pB¹"ÑÚϵoLæV©“÷²3¸Ë³>ñ»¯ºr={õ ^ZŸ[œãÂ.þ-Çï˧‘/{GôËià]þƒýç-È(ÿ‰ÔsÎ3X²x«¶W$cÎip]*ŒÿÚƒ=KŸ„)6¯±ôv."åpP2–ºÇжô)ì¸ú0_ªVcrýþ× †£•Øw¿|øµÚ›}ÅY;mÅ¿Apˆ/Jìg_ÁS ØÂÝݸշ „žŠ’Ìk<áÐJC‹•ðØ{M’š¸žu’Á²ÒXˆ[#ÈføXƒn§,«¬ž‡ÒäÅ‚ÃܨˆEäÙª4ü5Ší iÀðjÒçáÈ""âÐÏ׋ÙMÅÜ*qm6ex`S4-TCÏU µm )5âÿUK"`‰_þÞz~ð}¤LXt/ƒBŽGlÔõYHû!lšÇ‡½(X¦B«dæ°ÝÚ‘¸6æqrÒ'oܱPL´”A÷ì½\sz ù&9Ž8íð";~z°'T‰,j® ³ªšq¢‘%’øÉ¶õ¹¡ó-ˆ®Œ"³eÊàµv#xªO"²3ÔØÉ‚¢Õ¶o¾Ö$ ̾`Ãäo¨Ï{€‡I³oÌØÉÞGi‡¶âº%y?ð¡ê,ø¯þ¹ãU2ÎÚþƒºóв}<¬‘œÎÞ½f4üTV1Ä#idÇ Yt–µÅ¿Sžà/]ÌAl:îÒÖ„—[ŒAdö_îpßSØqè(ÜŸìW9ÓEÚnwžÓð÷¡ª¯µˆó%ä¹Â˜7ð”JSƒFRŸ¹°«)øåjõúð“þv=‚ÿRZ Vlç*^æaëÝÆÿú+3¥û¨”vˆÅ]N¥£û½ü ìÊp„ï£Ö€jKìHh€Lµ¼¿ÊC’¾beè|v9G„Í?þ›Þ7cÑeBÌ2²ÓñŽà-D1 ¶ ¡Ug1iðüŸïسëzQ ä n5|Æ­VÈ ;Ü%0%< KOJÑ÷T.>΀h ̆ýÕúãÕO_Åj²éÎ'ëq¯?|à á¸ivEj%¤<‚žQ¼ªƒ®××ë “˜CRǽžA>wèÂE™Kp÷y¼yLAèwî¼û“3åÒGÖÿô+í¾§ô„îq<çw…³þÿI-N=¹a\Ê”cy`.ÌãOäÝí4ÁÈ x·ê8¸m”ç<ú Nò¤ÙÐëTæñséèÀ­[§‘PÍ~Tßã5“Ðý”<7¤RMwwaøUiŒð¾Tƒ°$(†/³ÆHÄ bwÁŒ-W(ç2££ùJç1ÇB½ü‚g‹@]Û *ÕþAé]¦ìBˆ, öžIl‚¾£NI |ü6™Å÷GÞ[ôƒÉ-²8é \š½……7–Q÷šÎÊñ¯Ôy´W½ eûÀ¶„¥øGañHþ“Ô¼ë8Õ÷6·wÉ/ì¸ü-6·‚Ѳèe“AÇ>{ÌY>2å~óåÙƒÀ#ØÿË—ç8F‹~öºÎ$…òQËS7I¢‘z.Ms´—*‚°ÌMÈx¹ˆ>Ì©âl».x)…Í) ˆå£bn‹ÿc*êÚÅ»ßY×ÝZæüÝã¿à÷¼ïØäí†Ù²ë`ÊW3Vxù5îØíÆþFTáv×GÜâ cYyF´_šËÅ;:’Ra1öþ  ¿WjAT¢ª.ƒiˆ$9tМU¥'â±P>ž¸*Å„§ç½ü³Ÿ¹ ñ̇s–P«ŽŸû}$ÿéµ–Ðå=J$\r?ø.6evjKIÄý0œ•QóÎãY'4Õ÷aOònÿךۣjÀ"jëQßæìÒ±bëÆßƒ½œ,•í,Aá-ʤp!*Õ܃íùAtû€;{¤%Éo?­ŠëYRX-ꦋAÕôEx\‰Çfï]ý»Ó¸dƒ;¸¿#ÒÜåúW/eãŢ̶¨Ø‡•=Ç]hPãM¯Šä{£;é#†¢ y\ó‹U``„7%Rˆ‰´(D`ìU/Ò<µ ¦äžãÌfÎ+ú Te‹ÈkiºnÜlØ{û-7Ñý ¾{ö{dþOTÆÚ9’øgÎGDérœ>á2/¹F€½Þ2Š´Ì*Æ¿)-ØÚlÁ›öp6¶}·y°å=â×¥Õ´4ã"#–ÊåÀ×¹‰+w-GY£ù\rV'|µ þÉ3ATœÛ=Û8½E8-í4zn*¦3œM¸4ñ lý·¼ Ìç‘9:O[G[¹ d~ð$¶»¤%o•B¬=.qo^QØõd9K²‘ƒæØKh=. –®ÛHÌüݱ÷œ3™±r®‹7&›ŠÅYÎéhð¾“ñ;x,W«¸Ö÷X(þ©"‚þìÎ¥Pø°Åœ÷YXy$þ=Zð•¯_Á˜SŽdÆRHÕKçVJ“žúÛ¶R‡& YàÒŸŸéøé–¤¥fNÓíÆÆ®ãt•´Ý°VÕÆÖèG(¾÷ ©Êö„ØæÌê½#©þéLl×cá·a®S-j¨Ünmvϯ€»7/’K3XIÌ]$ÈlbæòœÓqx¾5^;ýâ¦vª"`Æ*_ü…™‡5É-͹L»ÝŒ$L‘$Mõ2¸i±ô/¿‚GŸ½€Æ¹beœðcæñB«ÈstXW³çîrÓOÚ’}yuçËg³þoñÁmáfkßr_~…Ìÿ OC\ñý#Wìz]÷œÃ¸¶Ç\ÕeÖZ— ·5ã詜—øÇê0œ¼ªB?X²›æ€Ê9<ÇÙ®äUóez~å´/oãíˆ=‰c/¦A'\¦bÁ5††AÛïBøüî&ÊnWeg ŠyGêáê‡_x¹ö5[¸ï¡`åñ‚Ú=Ú…:O²©±Ï6˜«É2Ãâ±Pý•¿oIBeä0Lè0òLÈŽYeü%a¸p÷V^4pžò޽pŒ-=úß(“ÿ¹øyÑÜÄ5±xhÙ\xxè.nè ÁáMü÷ªJìó®DHœc²ÏŒà÷YuÆDD`’Év_nFNju¡û‰:ôÊR`ò»úp¶´ D­•†û;*áÂσ¼â{RÜ›]*@-#Ž}]‡z¡Å8P KQQax¾yÀÛ’]°Ù´ C†5Œñ{I6=Ó4{ÐÍhó\"Ïtf8Bâ”eì›Þ#zJà3?¥Æ„]Çy ÁÐC rð»4 ‰û…]Ô1X+ºsÿ ¹ÿ5?º‘©œ …ác’ãïÒáMðTÒ J^¸Y3Õ…7ñÒœ}l“@¯ép.LqÇîº;c¨³Ò“þ8aVžcÕd68ýºµæ‚ZúÿÞ:²ï!MÝ« S¬°xi>”ŸË‡H‘…ÄÆà-¼ü¹æ²$½µ"lËa è:eÉ6}-Ʊþ™¨ú1‚•­~DOÏ™Œ'V>‡£«‹aBÄd޳€7Gëaò±Íl±š8}ÐÌÇ« êæ9㦧ûmIÓ¼ÏÁ耜"GónrŸ<ˆ¼(èŸÑ'b j‰$*&ã”_zØþö œÍî _µÌá·A~;8‘jlU`-SñÛv¨/8‰ý[/¢ô÷R<²djÉs¼NáiàZD®aý*A² ²¾+á^[nk„½š‘ú¿½ZÏiÓANôþkÞØ”lŒÎYvó )›ók³½¡ÊÆ5¸zÀà—ÓS^cÁWÖ7íÌ!·–Õ¡¹m1Sm½IB:tÙ¦úhÖcÞ…9nÄÝPÐiªx"ùøÃ“ìí®ú¯ç2We8¶iݦ‡!…éT?…v YÂʦâøŸiÙøb‘>—Ï“¿?àT$qa0æýBr‰-‰ÏÑ€Š11 ãÖ‹ _4鿇¹lÀý7íi%É¿w†œÜ(Œoû³Ì[GÙš†\Þ·˜Ë!E?¹ÇÍ.çDþ„IbZìFQ/”ˆ–ÃÎÃ#ùŸ¼:E•«¡ÑßÄ?wc_«œˆÚŸTþ^ „åþ™Ì\ÿ#†[Žf…07dùkFÊšö“¡ì$Yà Üœ~ Zä°¡Fô·º:ŒHžÔ:&þ›ÖHg“S#Ð¥7Š6ˆ‡1åD"ð¹þ¶OäÆÅ°õJV0‘°{U8«}ñ¬Ëü4èlãË„()YCæ’¥$£&‹›3j6³9¹Šš´Q%Ež¼8Õ†os4ØÎu?±,zµþ»¬\e‰1.ôϱ¼Üûm÷ý7RðâÉDbp „>ãË gðýv ’.ìôøÛlâáüҊ΂µ³ãȵÚ;xéº Ôà¯æçBÅöcà›ž ·L1t÷S®Ú;‡æh]…BÕ (ù>™ñîD"[Šl‰Ø\­ÂöœR"]æ’¥‰ÄÆ+·~ú:kù¥ÛŒ˜B¦$¡iýxME¦Ýd£3¡"ö+®>¨BVN“æìÏñÃø½óz¯=ì…ñ© 0™³9CŠºñó¸HbþÈšÀ·XvþÊnÔMo’.Æ/(ЉW=4Àð‹,T›.Ä<Ÿn¼Ð6Š=/ $ ä—RžOÚ†“’a¯Ê:K$Ô—½B­ÍIdÍŒ½¤>Õ%ÎÀðç¦päY<¬~3—ÝǰíÁ>Nê’[‰ÃqJ˜ÉÍZ8­¹k¾7<¬€÷¸=¡>¿õ…ÛÔ í^H‘⣒äA®,)u±äwé×óƒv¸A‰Ç'.Ö©=èò><ZE0_ÕÍÌôòæþ–#’n¸Ã¦ÑWîæ_k¨khÄéϦ¢îÝ\¥µUK ×Fü0"…· ‘£º”¸ÖüÉ´ð~׺¾¥ÕCå yž÷|; Št0µÝ ªV~xP|dE[ÒÛEI˜;—Äm‰£ûß^äÅÅ ðAüßu0‹Ï˜à¡D Xyæ27G‹îÒÉ:s¦Q—T,6º€NîLjÖÁ-tÏèH*+‘÷Òé­˜µôÜ6 øO;ºá²&t£ u®‹†éS%à¡ò4HèM÷|I>~¾ÄGJ'qf.ÊÇü‡ÿÞPw0ûX©ñÊ(Ü0EèRÛ?3úˆÁ3ãü2L[´ûYäåg°3h¬÷Åã!šÔca÷KÏ5؈¬C`Ïhd¾»Rp¢ãôYE©â.ˆ™‘ñ—½nBè§NÔVd0ak Ú„ÈQ!yxB¤=²V^M®Ãñúv`eN+ºÔø,ËÏ‚ã§Âɪ³—Øþle0ȵ—ì:‡&Ž×°qÛ0úè€Yß ˆ°3ÉI¦4ðÎ;"’aˆ?÷úr+ñ•Ô)_–¼úÖývÄÏÜXÒ9'º1LK'øñ +ï¡îÇÉÿâÃÂk8÷F k»± ÿx ü‘n!“îOƒò_ jýiTÿ &Ù~×UÞòÐ’^ŽE=ĹgÙ.îï5‰í²G±dR4˜c®ì3 òéÑXh~„쉘xOy4‹1ãñ ¸a3<ûZÏrÜD…ÇwÎA§•YJÑ=ˆÏØÉ>ÜÊ€sþ 8ë̾yÊiˆ•ðx»;Øó¬wI¯áÍ_á²èÙ|⹋dò· îQcÂçT"Ÿ¶‘®Òç†+pñ륰3».]˜ ..¦ÿſԒ"ÈÜ“×(ðqi `ÛËrQ¯ &ü çç«;Ðn„6>˜g¥*Y’LóŒyÅ.ÅŽå-' ñšf&™c#9õ×05Ô†nî5„Y™‚{ ]xÄá)>Þ¥ßl ÝZ[VBÈŽ½Xò³– îè5 ĺà✌-fŽ(&õŒgEaÞ­–úS…™ ‡ÉU‘ÑüSE3 †S‹„W(û¦ÙMAåÊ$ü0aJ-ùMn5\AçåçqVÍc,n2ä,|[™cæ!e‚¥£©·VÚ·€Éá46iL&Þ4BÊ÷ÜÿðßsßÇŠ†« n]Jƒ+ ¡tÙW\·ü¼—]ÌÖ¡“oš~ ƒ÷VKaãÔVàÍ¿±À˾P™z•4Êá@µ.xïê„ûa#hUn/y´á<**Âïªoa^l58Ü×£û³Cªnå%`ŽøI˜z¹ƒ(7«c—¨fÏ}¶©C~è>˜Ѹ[Ò€6¯º<|=»w:‘Ú­3š#ñ¬a5¤I®;1â|}H%•åZsµèÓÖYtRƒ+J5à­ÑÔw­=£oއg¤‚Œ˜Í]šA¾&æ€^a;IŸáÃeÄ zsÜø/©ã¼ sÜ>a_ï/vþu¤ú<ŠÕÕFò1Â@V{Þ'Ú~3€š HòQꦼ6_‰Žc‹p®çfîjolÆ oáù²y:<±Qƒ(}­Âæa¶¼½‰ò úb«®ÐGý¬jw@óú‹,P}ðøÕ/‚)q`TT0¾Ù}_×,!ÎOmyg~:nW½¶ˆMŒwçàôoíþxŒæ+Ø:žý¦ÂSgËðÊ­RÜed¶À~A½¸` N¨MFU¥ÄúmþJº^zÔÙ < Ò’É 'ú¨1 §=ØÈsÒ²‰o³ u Ìãw"èøóV±à°•ÄaÞ=rBg£`¨ÌIt”ra&eOHTQ †ÈËC÷¿¡ð¢m >ÕF$CiåÒ'p€L€öñål1ÈÑÕyÜ„+)m˜¡OК/š³Ä')Q3O²\8ŽãÕbÔ¿Å:kbÃÎMƒ¹L8\¯~cÛÞ¦°pË<ÌHË©„”Áëz° Ç“•Ÿ¹±u:*]Ñ£×” TÊ‚!¤ù!Ú›Ãy˜¬/Á/nù ذþóÚª“Ô ·¡›6_Å[¥¾0Ûå"¼_às?}`«›Öcv¨dìi½Wà;mÄã?7gsô{ˆ7]Ìñlïw’·®ÿhý$·nŽã~ÙÔcª,mÙ×óòSaZ|š§+Ȧ¦aGV ü–££æèѰ§*dVÌ\R:ž´à5“k¸6G•ð߉¬ùïpú»Z—ß5‹ÿOÿ]o¹Ë½ =Ìþö¡U©0®8‘ ïzBdh#)’Óa®hâö·´/àššËi]¼=dD„ùPGx¯.M‚Öˆ\p V˜Ö/ý•Ø+gÈë°Ä" 3õ”øû}l]‡+¦bMë3Hi)«vv¸à£Ô|²hÁ4Z;<)Á1Ñ~ödÒ °Ýd‚Wޤç/*Ò1¾×`óS"<è×á¤ØGÆè\cçø¤ÎC¨š’¦«©è“ñï<îeƒö³QW$¹Æö°}m"Üq“ä\§±Õ» Öœ~‰.í3xöÿü¿øÀFø£§ÇBgÔ¢Û·«±_¼¤ô^ºÀî¤3Îs ÇouEx_§7a.í²ŠIä×`ΔÙ4¼^•}jîB¥¾1x+ö‡ %á6ž<7ƒn²õÁÄvé„-‡QÎx=SñZƼ"Õ ƒ¹ãW°w)õ‹›ŒycàÕœ[BË*cüeQ«±Í§ N„â׋ ¹ÌÇh1ã+l{ßÅö~*"KO©°à9—™hq ¼ÚŒã„-N®eaF•¾‹ÙƒóW¨eö\F«²{dÃQ:UóQ}ÿ.ÔcšT”^;þþŒ$üå-G¶ÉCcÔ=X¹§þÁ­™|}k„ÀþUÉh© …/X\c5L˜‘ÃöÝpf Šž8Êó-t~;Cv¼š~ö”£íNP:æI¦÷1a‚*;¢–ijñž{GuP©a(©^!ýÿ­¨2%ïÜ”Ißç,Ì_r çþv%w…°Ì¾šÒµ©}à#L¨nabã×aÇœT¦a0‚¶î¬€šù…$¡=¡úúú?ðié# žw3ÿ¾µ"5þd2Ð33{ ¯à9ßè¶^ˆ_EÂ@eg HèérOÍXØ0ã#èl ÆGã ÊÔ8ܺõ¯}‹qGWÓÅ2+ ùìB¸äb Ã2qÿ“Uè\¨LSZ–@ÿ›“$¾m½Ýp²êö£Ã6õä`÷Þ ¨›‰Kƒâª VŒÀˆ?Õ(<L^9hÐÙáj oájá2rÓuL:ýú¿øwSSP\Ë–¿aÁå6â·{Žxý¼†K‰rŸãm,ªÙ‘20i žºZ›r<[t—=ž²¹úóÍ+¨è:„Û/Хʕæè¿, >´…Áwd£îþ!¿f´aå_!{?ËÒDé¶jœ±`þóYAfô0•hÒ§5§¦àÚ W6ÕwðÈ8Y®-…³æ«Rç[à.EoS[AüÁ˳*šêî€ú'[pË­…´E…pæX%œW0Çüÿ{ÿ뫾ì@t%úªJ±Sç9L×ã>þ0üƲØ$ý´e}d.¾øfK×1äã7¸ÐNÑ£lú K´§@M5ÃØP{w>þÞWþnŠ}ëÖ¢Æeú[ª™}¿ˆ÷Œ†B[š.¦†Ñƨ¤ìl¦³²Wxn]ìþYŠA§%ùÝÑg™LîO ºÚÆúϾA篆ÐÒ|O/Ù«KÏI/I[Ìì-ÌPsƒ5?ã§—nPLÇrÝî14u8æJ(â¦6y~A­ÍÃSÅyáp/¼u¯•:Hø$™ Xòk"Þþ†µ§ÛþÇ?j$;ã™åÝ|¬ïêL:ÇÑ2/˜u©¯®ïÚÆÒÔ£pXÖf.¿(Î<AÖžôëdúÜö\ý*ÁcÆúãhµBÓ¡`Óò¸jЋ&¬mµ'Œ¬]Å¿ Å©Õß„k³¾c`(Ã¥yØþël̎‚_ÇÙÁX9º#x I×’çÇ)¤øçã”d=¾²íÜþ¸–òÊ|aË0pÚìKµÎ >ïïsˆéóõ`Þ"Í]“z±Èõ&9u MMŸàÌ»ë˜iû9ö¥K“Ll6Älë¸?L:¿§¡½²?z¼Wã…ù’´ŽHü7ÿ¹Âã,~ÓÌQI0«<¼öãî“—½ÔÕ¢›RGó…Ûo3q´h"<çî3¼›¸‹ˆ4¿Æõ-ã1h¥(o ~ b»ð¥Š .ø8ÿ ÷ԡEô-îʘLOÚU²Ä>Q®¢ ¯wÐ’ˆNÏkGÐ>ò“Ù§,aÑZíL´PœZ‡ó}eèÞ܉ME£ø¨I¨šïF>»>ºóJâ,‘"èŸ{SËèq[Ê•Ì å]àb¬HÓLeùÏm8úæ^v8¸·`Sge‚Ò­óx~îÛÁšt«ÎØÓ­mGÀkËÿ­ןØäº(Ôw…Óé xU>ˆù&Ì!Ïlàjm(šµ1û·Rè‹&SƒàØÐÑûPøýQÑ뇹ÎUhp'Þ:’…ü«UÎþ»Q¨º˜Žœ‡¹ËvÀÈàßBõþëdú’· {2ÖÖ™õº#hï¯LpÖý#Ì_ É%¿ðeÜ^`K ÈÙœ\z(JÇäšÓŸN‡ñ¡~2Iz>šïßzFåÚâ%37boÀ,gÎ&tæ9Øb*JE︢à Eìi<·¤:Hÿì¹ ë—DdNâiÎqij9Ü»a#š\. éûŸÿµˆîzÒgî—´ß7"/¸ +„ǵsø††óB«÷çÁ£÷'ùIÌh³»Žuç n³Ù¥3áÐè¹”Û|€“«ÚÙžp—üZ~ʽSîPù:ûöžƒ4ç. ÌX…%ÃFâÙ³/ U`ÇW…r{ÿ5($Gù •«(•/H|‘‰ÿ|ñü Ç8VlÂsW¨#¡ÓDãé?Ÿ`h8)µ1ÐñR’—´G£KW  çÕ3‚±/¬F†Àæ7î¬dO.¾­È a ½×(¿Ò!ÞOƒù®”¥;O¨ñ®º^Áb_ùÿü/ã= Ç6/cà 1zW¾pX9õœ¾ˆ©®}Ž~×+p,÷AÑ!;VH~Öì„‘¢/ÙZ£l­RƤWb 8Þ…Dè©ÐÍiö0o’oýE6K}eÃÍ.âÖãµLÁºÎ§ ¿¾8¹]ÂiéÊ´ øŠÓSÎ0Dv4è)ñÖä;x{Hdmއ;ÿ.±ú|òŽ(˜ü¡žžÂeMäðSU^ã¡DWíá §Ó„j¿®@•õXˆð}M4N,hÁ••)lð^°-B&ˆ_ùdÊkmHß“ˆ>¿Và÷ªëÐîy|ªùþ«oÆCóäríÓ[\î6†žÌ5vi¹`¹’/ªe•©8}î1ä‰Wª÷ÝÏÁG ›Ø<¿Ó$Þº‘ìÐæêª@åÄIÔtWcÖº«…›íÁý”8mVH—[Ê41y2ìX:ÈͺCØ…ÛÏñŠ_"&7¼ÀĘ,àÂÖ1ÿ|ßEÿwŸÏ‰¸Ç¢\9ƒ6SæàïˇÁÄ/Ÿ4úhaÜ­Qô‰–7Äœ·†^U]5Ÿoëµ%;†Ž=/÷ ô¸I?XFÝe¦+Æy)²-Ch ÖÝIsøö¯ñ²ÿúfmÏû\þ²¤OxåäœóÑž.0¾ÈÞ?] ‘~gø}¡ðÒ@Gß…¿óˬœß‚?²Ø¿Î'ÂÍÑ;øÀ%m®àß>íÍ5Šü´òIL(Ľ ¶s<öï?ØEGÞDÝtgRù ʸkǺc©Iÿy>—™‚Ž@®þ³¤%‚lptRfÝæ-(„Õs(½SgÃ5¥A×>·Ÿ9CwëJ¸ùch÷ølP¶>@¯õ¿gUä!N1².[8†(ÎCí Vý¶ujòí~ìŽý?þŒ¢me’  &t¾¬¾;–:Íòú/þÕ“ÿ×µùdû¤|A~T>–)âáž:ø6¢H½1÷u2M÷Ÿ(í‡%ÜË-%õ¬Ò­> ÅŒ’r̪¼ Ã{_°6eФ ­Ÿc‹2†Á¬\¸6Ÿ†wÌRHIÒpg™ìµ‚4}cœMæ;Nd¿#‰\¢Ë³J#HHÝØ“u'*8PÝ©wɨ–nxû;e–\£´$Ò`r‡í¹–ƒkË“÷nÛÑÕé!l½ýöûg€”Í\«°׫Îä2OÛáäÑ\¶óUxçcì©ûÃÂÛ é¿a¦´üD6´x,ü?ý#³ /ØAÐßœó°s.FÃöz)¼iûžinp„ÓF£é9e}ÛÏ~í–ä'¥“®ó ^!ÌÜ&á‘ õä—↬ÇYz½‡pÑ]۸ߎ¶Öe!þ݇±þ×áCP Âø+hâ— /ÓºIÖá‘pa¨*}{:†w~†ùJì£ìxÔr!îŪ4ݬA ùÙšw[‘û#üžµ‚Í-æ4+t7UNœC.ï"Ô=4ÌW·±s¦Ç ú¾*¾ÉNÄ!³xÙLQ~¢Í ÖÎ`!—·öï†ú4÷`-+Þ÷ïøa–-˜v̉­Õ„vñ4\B¬è¼™v¸eÙ òòÒ/VSÜë5ibƒµÙôÕ"æà…‡E‚?&žüØçF¨\¦Â§€*¥Áß^…ëxÁ£=°k²8ÝmáMg<^ÀÊcŽjòÒ€rvÊÖ†–JDP±?yx¨ÅÂî›ÒĬ2ÂJœ{<ÙˆÃcJðBD1Ù5c=]‘–¾ûƒÒÊ1¸*z'‰µé£KŸ™åaLö8ÚU ëUk 7_®Sa$HáL.Õ ñEci˶nì/ý!BAÁN\ù&C¶Þ“ºMmÈ=oúÿ± Øõ•S ר» ¯h|ÄîéÃÔû<õ2šL.êöƒã1qj1ºƒ•¯? 媧hÎçÛ0pR«åÆ ËÄ?àÿ©#E8VŸJÖŸJ‡¸<`¡GÖ89ÿ#ûS2¨âïAι ÓWÙ‹#ù»M²¸G;Äê’Qÿu;v¾]^Í"è¥q wœ_Hÿ…â–Ïxt})o~Wƒ2ëB·ÒZøä(hL¡ÒaŠä’·¼=ÌóÖ¨Ò÷L˜71E¶ˆr ×AÝrè Ÿ,£9r˸ÒÁ1´7ãX}N²¡ftEðcoãþËÿ>Oà¼Ø”8éãØT} %ÌFÓÓ[ù"­8¦Ø{º¿­¤Q¯¿a¶ÚaöðfF”Kñ‹Ö{H÷`|z5†ƒú3-vJ°ÅyÒÛ;‹Øâ„Ñpgþ}pp¯CÏp}îú6R°¡ùt<[Në‚/¢ŒÙc8yW†#éÄÉÖL?LZ¥'Ñ㕸bÀ.ïU}Ü@õ™8:D¦?ÊUöpAÀÜPî»ï!QêˆÇûcxÔ»¹(xm 1ûøðóZlfn Ë]Ÿ›D¹½®Y>š(E,¿‡ø M$`“ ugÆvü ùÿ®[a£ÿ]áÿ­\ñâ!²Q ºÜû‡«—>"-ÛU±(Т«u‚’ÒL¶nÏ+ÈËœëæ&o©Åèø1 ›W)Óº¼›Ô…¿fµ…Þó³ [ú±Û÷*´UKÑ›lv«¨r2öÒó ìò!ZÑ® ÍÒ­à㽕oèµâ»ËçãqݧäÇÄåxeÍDþ8Ìš¾¾[°ÍN–úéŽåG ¸VM¾¶Và–4¨½D¿ß¤Êó2o²ÓÉ‹š'l· «¢UF’Ôó…i¹5Jë‹Ó5çqZ…û™>ŒFÿ½DŽÝˆÂgþÓ?9ÃÆÃÀÒgäÍúHþ`ªw Ù&r3i‚øsó3Ö[HÍv~" K±ÜË”RQ¨°mÔ¹±¾­’®àog‹;‡/ ¿c èÄd>Öí NµËÀg«HaBq;¶:Þé°;•ª¼p³$<ºàA? Aú¡T|=FÍÂŽñk?4ù¥ç7qÜS9˜#'Þ8§Ì—ÒØ¼Phüºi=L|nÎü¾+óq%`2œ.Uˆ¥¦žùôëAâT¸.;É\†hñÓiÄ;3ª×%‘cöaiˆW|~˜§£ùwá'jdÿßï;£BÜàŠnŠÃo’™`·a­¾?ï.°åå…ãP:®`ÿlvÉJViÏgSŸ`˜0ŸEËu@í€9>©‹ö+5iƘÃt·ØE8¾dT,’‚nÉxþÓnÍZ°ˆ,ÐѦú/Ìy…äIÒð­÷þì‡ÕSë`ç6q–´p;4t ™2¹qô¤çbŨU¨Óö¹úÚÜÌÁýØý^×7€Z¥Šž7ãms­éѸÑ4$!ë8¨2‰þáÜ{ÙMÈ=·ßm˜ ó+Ó°ß%t ¥=?ÀþXolÌ© ëu‹ˆBÄâ³µû?ü#VªÁcÕ!ð¼ê›9, H½òà”ÓɯUèÒ–„׌÷â㟱Öô4HÙO  îê6õøÜè‡Ô•͘.öVØpÆÎŒž†—,laÇôIôÜ)Ornº|–XOóäêcy¤±+ÌÃ_ÃÄø4å3xgøwöI.‡°EKÅBñ™dyÄXÈÛÔÌÖÒ­paÐ'{V†€Üf[*³iv'å±Ùã…q¯ÓXâÃQx>V{{+±¬ê oÉ€w}nÔ¯Ô€Úš[1÷òÒ…p<ýɳ1¢Mû;ð_ºç͸êüïÿ·þ·'ôàj/k‹áxÙ³‹Ym‘¡ Â÷¸úB3Ùº{èÞ{ÊšÇ.‚¹·Çò±CôàоkØd¢@/‹ýƒ¢lÂ*ĸ ÷lIÝÀ]~ŠÓ†)xì«%oÒŸ,¸~üY<|™œõq¡Wo×ц=—¡.t0®…Bø®&Å÷çYâ"ϧDbi(”Ÿ"cU‡aL¾4™hD½]=±ÿÐx|¶z¹Ûµ‚Ér†Ž9ñ汋÷]îJ¾?»$˜ZÿLÀ›íš¨¤‰b“ü¸í >ôêzîÏ0ž.+‚µ†\½ÖÆáÿýÿÍÛŠ\ØìœÆÖÊõÁËV5Í6wÏ!¹ªmàw „é3¨¸Ûº~”>ó»lL6~b~ª¼5W‘Šà]˜]º˜+¿:ÇžKjÓã§ÂE—4 츃ÛÇÛâØmò(¶Z™ßjZ‰^ýõP  þ÷y묛0²bþ)Ó'eÏÁ?è Ät·’ž§ø<õ#›ð<Ù©»¯ªÝBQÂó2ñ“ŸE¯Z´ã¶‹ö×€ù±IøOZ”®:¶ž»ëиÛÛyjk!úç„QXöÅì­Êá·h ìßW‹×â@Q\¯*Æ«6ã©øž³ÿå¿¢Œå°Cû*)Ÿr»”åèÞäç Ðz–|òä’¥/Xy©š,ÆÂ7Yô¤軦Œ¯¯¸Ñ-›lù—vîö¥þmÝGåÁW+ jeñ FŠS/E rõ3©Î'+zÜ[Vœë<ßÁÏÈ. ©Kõ˜L$&²ê¼±=€-Ð¥×µþÁù‡8ñàœéh ûã@béAþiØyº/ÛŒŠ¿jÅ¥Ö{œÁjüKÖ¯t¾z^䞨¬Á{Šá ÷d4>Z£{“½·½ˆê3ÓñâW~c¨3Ÿ£.G'_—äãé]Ïÿê_P˜4–UW“•³àJ¼‰ví€ìäµPìúFðýS(JÎ<^… *ÿP?ð# ¿ZЯA ãÉ×ß›¿îèòšZ'”†õßÙ´³Aé…¿^”ãðèAˆw£ó{7—ºoCçÅ.çÁb|AÓ!6ë€,NwW„/-& öL–—OhÀã#]!RSºð_:C…-Œyä¢^v(4­:Žcš¿'âT1‹Î4ŒÁ÷…K ÓNãJN`è<ñ.Áoò˜ªÆ=xÚ`‰¢ «·löÃ3gNá÷Kó¹ÓÆ”ÿü?yŽ!^ÞF£>ò¹×Âà›­¨ÎÄ áûNZ’AÌŽÊñ£Š¡UN‘uíù ½çŸ .Äß$,O üt*‰|W6ÕÙ4–f¢€¾3çq;µyÐ Ø0¹ï6+óšDyngìÄÃ0¨_‘:?ú.‰‰ÔÌù ^>ù "KzðiÄ::ü¯=%ªË—¥…ÐíÖ„Ú_¦¹77â&ypçÄ0n0M’H¨ÑéU8¡N‰  õ>ªðýüï\®ò5XÌnfcãK1õš÷u;½·5˜Ï‹š‡Ÿæ­ƒðfÛéÁ×Ïr¡Gú3þïÿ¿5ÉÄQ5øD4†|üÓ{=ŒS´ƒB¥`ì Ýÿt°(~ùZ˜o˜ÂÎ +Çþ’4ü0Aî5;ñÌ–§…Ë ïÅ|Üå:…«P¡ufZ ئq+Ë`Çbiðç »¾­ ÁG³Ñ($Ô:aÛ•@áøHÇ4~êcNØAX ¬ß#Î÷nßoçN¬6n߀»LßCMùD¸/&8gË»Wæ³–TÒV ‹^Œ‚yU‚‰hCŒãýèåþ‰táÒ6T6ŸJÊŽ¬N5ÚhnÆyÿVì¹3‘¼¬Ÿÿ6Éý‡?kÔ³êö†½,õ® Í™¶Š³kÚàˆOÏÉ[»¬‡q¿ÅËñlA®°¢`!ŒÝ°–ÿüN†<ª¿?7Õoã½£ËùÕηD¦7š+ËîÁ×BÜ·wmœÕ ³Ë°2À'Ü×r:±¹úýT;¨¼~ë…ƒäj!i}щE*ö(Õ 3WÀ“z#8¾ï\ÖŒÿÞ4Ð/çžœõ`¶mØìr¥µÿÍÿ˜ì;Ì®¯5€I·Ñdc,Ž•GxzX›n÷ §öFæ,U\ŸêL·¥lLø‹„2rõE:~þF4X‚í~ó öU0?áN;jÊ;nB-³|Ï磬©VÁùÍW¾6þõLö¦•i!`÷(ϟ݉gŒ"xïÄ.g|7%ž‡k¶Ë è˜X<GÇÇ<ƒ1ŸPd÷Æi͆ýO‘züD/Ì··_9=vü%pÚ>’†üŲñ¸qóB&™ãˆIS¢ ³¡€‹7¿Ço¿ èr…¬5<ÄÝ&¨O·`öGéôX>‹ž¥‹wlþÏÿ_îT¡Îö¥ä·¯807Çÿïn.Lé-Žô÷#Š'/ãETA ·W¸uÆËêj CÌÝŠ­#^Ðßá÷ˆù«ÏØø.D7[ò:`þ[JRvâOdÓ¾Ô](/íD­Ë3¨çªO`˜u’‹Ìñ¤žø]пó­+‡&·ºê7#ÍéƒÔÌløC/ìPqnsBÜp1Åþ½äQüÐÚ˜ÿH†^84úH³±e8ó]ÿìÿ^ ÞÐŽnajøyèoXãõv‹Òl %:=ùæûZ„k ÛºW8Ï~(ûúÁ‡)˽b—ŽŒ¡Ó2šÑü/HÏ燾^GyM›4èù×exÐøýâ,/Õ¨ÍíÓ‰ŠéeÐnç=5±ge˜/£iò[ñn Š-¾I¢Ãõ§Ø;£âc-^EøÓK# Ëi.fN6àK†+ò¹•m8pb{›ö÷}’Ä ï@\õ.^‚².VÌûw—ð°G$ßUŸ ?ZÓ Å»±×ð*ýQQZVÜyÄ',½› ]gq½NVÿ;C:T̨O˜½uî.©×Z ÛææT¿é7àS§Ïø¿ùÿ!ÚX¾ú¡pàµw‰?K:ÚÄ©a¢?¸+×瞂s‹# ÿ’ê?kÑnÂhüxò”ÚÀ1«{Pt»$Ï™>Õîe?û&òM×´øÑ†µØº°Jm…G­Ñîâað‰Uè(/ ÇýÕQéÐâ|K ÎíEë?s fô(4øg$Ì.¬C"Ø“kvôêYM´üÕáä"¿„À›uüTÕ+üswöòap[Äåf¸Ð(ò»?µƒR×y–šåË&.øˆ½m¥PzL §h„¢{ñnz°{½ÚØ‚âq8ˆ“¸õcÈ¢Ÿ8`øæ?ÿ‡æ jÓí‚  èè—syÉY9”L%?¿›QÙ[ 4ÈkMÊ5å:Uc0Cê=1Þ6„ÿó)‡§fFüǨìõGz¹çà W1d+Ò­èüá&ÜpZd[h÷ÖÚö<DèÆŸb´=q?5ÄæÿÁ”obä„9(q¢šz­•á;+³˜Ý§0™*ŠMWY̾<.7ùÌ`.Å÷HÁ¼üýð"ew÷a¼ïb;rÃî(ñî¿_Füòm\ä]g¯5—MðÖD¦õ²—^F4Óä‹pÇÒ›Lÿª1ÿªÚwþÓß–øTÇyæÃîå«éfMçÃþxöêÊ<îæ~¦¦WÊMµáÎKÌ8½Œ×F?wðÀ …©ƒ>@±ƒF´ri o6^ÇV~xþ:—`cM6ʧoö¯n”Íæn­ïáÄ:qž>ú.™&ýuϤn{P–Uƒ˜ø‹Ü.;›þäÒ4Äj–ÊÚ2t“Ûþê€_²_‡7’Б)lÛH®­}‚'§­€MÆÈt"áÛ’æjsÓ]4âÇ­ÿw__ƒªr[“¿.@ƒ?ë¡jE=›ØŸÅÛLã×*Th³ñ;Çÿÿ–àfçGôøy,q’çj+„øú†=¸t8U wÄ¡/¾ç=G¡zQ„œfž}¶wƒ‡’8Ôƒ>grûÊöó˜ï ³ÔŒ—ÖÔáÔêTçiY¥óÜ×ÂÐt´2)l ÂÌVh*3ï›í°÷{ ¨J¤¡~êX|ñ`.îK±¢þSü„·µËùèÔYL°å4ŽM6áI_#ðî˜ß‰zàñÇ邬“²xñ—ݺ¡=I‚ÑÎGBnP‹wì#qB› ŸågÃoë«ñ)‹­!oÑEüöG’Nš5GN›úÿõÿÞ–ãö<‰Ž¡a†Ò6:ŽI½‰¢§ àÚ´J(Éðu²¸}ÛHª£vj*ñb½«¸)«Ãßе3$dîªy¹‡ývJŽË—ÈÛÉ=8ºd?5,ÚÃ(Œ—àÃF€fƒ=Ý14Q¤n§â÷Wo£cú%1J;n·%ÏÂbØédNC´æP³½N\az;îÓüŽ ô=ü=WНùâÝ‚!0uq!ªUÄS›ˆo ~¶ÕtÒáÅeZœ¬G7.Tu×w$[ÁS`žu,oøH^ùOƒz&±É#üWÿOm ~ýRt©ºêç“5)'ÁlTëôœ)èqÜÆ:f¿!§*ЪõõËДruª¢×‚m-yç×{¤Dw;4½œ Ú«TY튛áùÑÎÔôÝlxvÐMP½P §wÃÅ1ÔÖWÜéö78ó,‡¼ŸÄ/ÔêÒógOÁó‹ó¸Ì®&Ô}5’ïŸô ÿ‘ƒM‹txŒŠ ?duBךP÷èñðØ1ÿ ? Þì&a§g=;zGœÖ:&@ÛÝ>ÐØ<nYž##çkP/½ïdØnJçn>^¡1`§s~¼B ¸¡4C^²ûøe#¬œ‘Ço,¤acÕÁïÞørOŠ_!™°ÎÆŽ|M4Ân](1}-_ˆËÈ—øübÙŸ-ë'Žä—[…—OBï+oN”E{ñ½T«¤æƒéeÐyÿžen‡Ì¸Öç»ØI— Û±ü ب»ˆyƤÂÚ…ƒº÷Ñ3våo y•‡×ý¤ø¢›ÇèÜ;™äßk@ß”%ŒTÜÅ ~/±íøPž´æ ¸4äcϪ|2n¾Ä”ß |H d‚ÒÛñ|vœ3¿## c»¥Ùaû™üº¼,®ÿ¿õOg‡ƒÆ‘Kð1ö8äË¿ 1¶¡økï+=J &ûˆ¡¥ÝWðð³ÅÈ7N)ñÇð™´.>3›ÂÄŽŸ‚²‘P>¹ƒ‡h8³ ‚¾ ¥{\¿BàùxˆY~ºÆÜI܃pÞ€·7üW÷ƒ©S¥,¿ì F_bà‹]°”ã­Ëkaå:{ºÓe5 êîâð¯Æxtãx~w•Ô[/ð¶}…ó«*È ¤!q½e¶›~Vä2+SaÕ5Xä| ßÔ Í|°¸sl欂ё¶pãc_õ°]°îL|_©ÇŸÌ™Ë¿MûoÿW¬ë\ÔÓ}Æ  :&ƒ[cª˜ú{ |¬¸„‰;/ÀN÷,,–ÑÅèWŠÜ´ß—ÎV½@.íð -¢Üòú/ø¾z1ü¼-Ãc¼OaJ§$39Œ¿¹F·|? “ThÙÔ™(nÁR¢&±@iª•îO®ÎÖÁ¯‚Pßg#¨mBo¹¬Ä«)§øXÁC<|LŽžë¶Gµƒ‡ÁZ0ŸË-Šffž ~aƒpJ])´N¤Úcù©5UüŸ‘, [3h÷QÁò²q˜kp›ôÆf¨WæEÝÁó5ŒˆØ¢X‡ÝØñ´úòý V@ÏåE‰ÿøïa求þËÉ®}ih1¢ˆ­·Kg÷>NÄG?hGóIx3²³"ÙŸú…Øý/5µîâ½å¤àŠ5èÈ`¯¿1øµ<Å¥x"Ø¥ÌiâtìÈÌÆ×zÇqFÑF¸Þ>lÞ_ÃÝ!PÜAJ®¢ÐänÓÔµ«£æm@£/ìsí,â,³ž[¿š,ˆ=°K²ãg)†–“VC—ê8ةꅷ÷ŸÖÚ^Åò9"üþÔN¬šUö%ñ9GñÂnÜûb ’/X(e„kÐKEº™Å\1jý/ƒϰÒJ=zñõÉÿâÿ¥j[¼v4¼™ £dÅà’Ÿ+ ~æ”T†yâ§G¸¡%ˆŒh“ª,È…¯Y¾˜´p)±É§éXq{‚3 ¨-.©êõÖšˆvZcùÐÇciÞ‡dSéGôoʦÆéoȼŠcdœÓ_¢¥ñNN;ͽ‚¬ßñû®ËpøË%p»ÚÄVoï`<ï`Ö8]ýxÌ~†ú­-‚œºÌHIœFÁ¨‰ú„dÙƒÊ~9>®W“;UôÀ‡¡Ùûv=~ЯC°ä@­tv\V†I+¡àý9öåË"Ú_z<‰Æ&1êü‚³ô¹Xd*T߯õŽW`u@!~ïÓ¥©!GÈ5Ö‚Ü%M(þW‚þïxƒ© îütè?ÿ.׆ ›LX¯ãhm8‚Ç$Ð,åß8^æ0Ú¤»¢ø›S¨zÃ]Ô´ðGØhžÔ­ÇçIƒ˜[.¿¶C8Þ· ^ê ùò}DK»JÿÈñ!r:”•GÆ 0Jwä>C™Ì¯‡5†¡p¦£‰ Q˜ÉE¯+Ó?Òõ[=‘²ØÆ›#»^>g3ò:`ÏØÝ\!!­Ìsäc—¹Ò™‡Pã?QÐ|àÊÁ±é±Ü”š=µb·r/ÁóoÉ8ö *Z˪¬Õ™F±Œ¡qÍ¥ÅüCûiT[öÞ)EÞ‚ß»»Ê¥!ÓdÍü÷]ø¬Uð³DziTLõþ„/—8ÓÖ{Ds¬3^K¯Dƒ,olÜnÀk|/xÓî fäñç\³™˜ ØÉÞ…½Ä×ÛF£ËjiR¿Z†·ê‚q…·Yä]uz?ø9v¥¹`­¼ºîœ¢U{àmô¤?Ý,f¢GÏ¡ÒmuZ¦)M£7ƒým´˜¨NÅîÿ€ƒ¹®ìEF&¹žvGIãNq=ˆÖƒaÍ7’<ü4ÊÄï\Á‰Pyþû^Iž2XtH¾Ó-ô˜'q3V|7=!ó…§áÁÓ´Œšß{SÉ"ãPsÍÃv}qzÄí Qrc™¡L±ò4ÝýÄú^„Oy­ùròþ·¨0‡³vÙäqÕ|4Mî"ç2bñGÍPøp\—;œÔCÁçÖâ’Bÿ¼JÚþýcó–ôâ·Ç ¼Ô?ûŠó{gsq××Zòm¥+½5Z {Ôµsðv.y³ |±¼‡â/–i¾cŠ×Ñ[qMxÁì+ñA·ÙBûV8¶ã!{҉ô%ùÔ/ [wBa›:=}ÿº®ôˆÞ¿v/tÂå+ï 잩⿓ötìîç˜>[ÕRßÁN&üv+Bbt-¤mâ :l4+‘|&’£hÔï)x$_|¾†û¢`iñÍÿôIx4¶?T® Ƥ»WðšÝ¸7)’©|ƒá× ðä“ý¨}'—xÍsú .ÎU3>:EþðÄ#Abt–èHv¶­î»kAwEü** îaÓ'Ø«v áÜ~p9L,}—ʹոãµMgø¼Yl,ô‡³I£¨öîe4TBOÅO$+Bþ"«*Ç—“²@b“,Zh\䆿„ãgN‚õzÂÓ¥<½0ˆïªèÄ{f ˆ/|NÐs>êtÄ(¦fÁšË'ˆÞ¥|ÌÌ Ù‘ä£ì0ZÝÿJÉÝÑ£`ÉQš¾ùÿôŸã­Ã°¦¤çǾ҆ܥ=MÖà”sGg…ãדÎ|ÿù›Ì÷ŸÌ[ò×O•äì„PêF9þ‰’ÇÓàÜÉ>¨joÄ‹–£¥¸ Y{+O%ß U}zæþï·ÎHëT£¦Ïß ƒà¹í?!¯ÇC)¸Ñó(Ÿ ÙÕCiëý!Õ~|ï¾ç‚eÓœqëÛIh¹Ê¢c_â·Ùíƒ2T GÞ×\­Ÿ¼Ú{·y?ÅÞ¡yÁYX8¨‘k[ߢä´N\äReÿ& N,ƒ+G!óñ>Ïzì/ ‚ò•óÿÿú^6ð^ý– ixì{:ÆYÊÓ!âÏpK˜)uåë„åVã‡÷4ÀÄ­t¨Yº†o?õøJ:t×;bûÈæø'gcÍÙÍÕ§{º£¡Ùð_.ƒ¹CZˆUðyá*½X_ö˜¤•› ƒuxZù¹ê Öªÿ¯ŸO] 2É›åÇ£‹Ð‘¼M„Cª&Pxñ>®IúŒG寡Qï~{J&´ïÁýþÛÐæm «¬®T‹·ÄÛîúzËÎt §™›³$µØ]Œ“vŒ$cµ‡Ò“Å^íñÞI'³‡¢ÇHÃ\Õþ W‘yXó ;_àá÷rèó}̡֛˂ÀÀ'm:Ío MžÙ$87µF°BW‹m½UQÙV0gë{¶.I’ãÄx´kŒ(S·Iï„qjÝlÁê#dî.UZ,ž£™&­òN%i»Åøç´í€¿v>n˜«ÍÂgYÃÎÁ0¿†'`Ö]_sx3^¨ØÆU¿Þ…È­—«cã§Q«m[HÄõ-Xo9žß_ãÃ<¾M"Ž®1-²ZqÃxÁ«]‡`ÆÉ83¡ÿ÷=3{ûÆõç^Pä×MV£ðô™ð«Ê_Ùˆ² Taß<êñõ'Ô¹ÕÙlH’ ÇMû!a¶"]rŒ“¾÷í ÞðŽ\Ù,B—+n@± WîQñZîkÌ>¶›×ür¥klÁ|Gš½Ñ’‹mÀö>)î<ø3 }¾nêtþ#]•^Î-ÅèÚ+wta* žùœmþ² £Ãq™îo(“±£Ã¥hù$qžpA\öNà×óåùQ¬Bx1oÌü†Û^ŒGkãH|¿GêMS¡(ö:Ú‰–B¬³<5s» ™ œ \ oöb¡¢>Ýwß þî¿Ëîú •ÏÐI³-Anód,Ø1M¸8ó0“úµç*¼bO 7Ãëq8½¨ lM÷­¸—ØÛzÌX<,?ÇCVc«ÅzNÁã¥C!âÑ2¸âÖR4.Î5€ME²\_õ*ÈD%ðåmØÒpšžóˆÃ¥4`Ø>e:×FŠŸ¯¬‡¤\k~;3ØÞX3K†ÍÚÊb¥®ÂÒÎ…(˜l«Î嬷“Ðb\ªÇ{FdBD}êV|ÀÖ¾qp32Uu²€—Á˜~u#•ØgÆKÄâè…n›qwÙƒÔviˆR?Sy8ÜŒ.ó¶R¹6Iº}Ë:ÄpcµÿàXú ãôéÙâ¿°p‡?v½ã?ò—ð‡'èqpÕ}z‚^zBy÷•ô®˜Üû-E-ºÅ Y4ºLÂýUcøsEà9=÷éƒfºéÞnt?>û'ó¹ûð‘±[¹±yošv½ð_AßÕ‡üñÅ4`õ+l4 ǘg’¼=Ljï‰A”dW3ÞáE.›G{g§C ¬ˆÈ«Íh¡ù”‹Ùëà¬Êrx3ôLwxe''ïU ºé.pŽ-¥?oÞÅ­štçì[$5fïÕÏJüÝÎÕ<Ãù2X ¢Øb™Küõ•`¾`k+ØÇv“O"Ý‚ (MOl™¥Óeá…w/®{pŠÜ®Á·¹GP~Þc;b" $¾D~ñCbÚøÝÚ¿‡­„2|OÆ 0îC®ó£®ísÐÖ'” n= yãkAié<ž¨È—ÄGñ ß-`çiq‰]ûìÌ•Ü$*Ouyõºƒø¹O… ?dq{_®¸<N'^àS,LJUÕÀ÷L#ö`ö,þÆÜ´DŨۼÍð2Ó ÓLÑſ†é"xÑo,»5g})Çn±÷çØÝ×pé“*&,™„Ž3m*AžpOy÷gG ïi¯öõ ·Y‰/(øˆðy}¬^ƽS!j°ÙmȹJîY!í±…±BجC­fôÂ<÷xÔX|•¤l€E›ùîoQÄ1Û’É­kÕ@ž«.-õªäð´!tꇑ4hüWöçz1h¼Î2|—ñ·¢ú8ÝH–Þ=•Å÷½P€2ï`Ü`O=F•×ñÒWGž”MleøJ” ®sìèHÈÄ©óÑußü>IfËnÁ#µ™{ìt|©îåXú«<mIòèÀ[»`¤×&ѼƒVt÷¢JpÌ.!¯ïσۆùLRÕ”¿;å à˜óy@~œ—Fÿmæx9tØüY× ŽáJ£s þUž*5(±+IXÂÈ0˜¾_“öiùP}¯ñ¼a^>sŽQ ¤‹Xµû|ÿ2}‚sq¨«=L¿åÊc6äñ-Í\Ýû´m÷eòÛr`ˆÛ!៙†¼!÷'.0;mÛæ`L#~ðz§i Ö(M‡1k' M;K‰êþa(wý+¦Ëë`Ú,"˜±ˆõWÊ çrœ­M_µÛ“¨’vŠW°k·­È¯Ÿ§ñê)¾åAµÄî$»:ã÷-qè|2 ÈÁ.K4ÊÐôõð$ÃËÍbdÊ 8ªLßu_…CÖûáÄßÛ°|«<‹Î¢¯ÿÚb°Ë(^:FsÎ}c?æ£æÅRxë¼æ¶¡öòž’U!4K®Žôá\Íׂšïbt]½-žR€¯¼ õ7FbçŽÛÌÆÖ’Ú}žÌ¿›¤ãÙ­c Óg>÷’\HÅ‹ïãF8¡ÙÅ*§„på7(?@« 66{n´ž¹ ’+ ècwêw–¸’ª‡® ¹ø (ðW¦Ÿñn½²c™0wc0NÔ¢¸ôg\òV™Vò…eJ<Ëo3F¯yÚÉÑÔ÷xh¤x»-ù£—ÀÛ©5ðeÕi´KÚ†ý†ötÝ–w‚!OîbQ‚ðÂZiiô¼Ö…¾˜â§Ã¯å>,Ø\¨}ìÀ×=%dÙãDX7¨áî4Bâ¬læâÔÇfþ;A¢“­a¦É+Œ;ù™$W<’/Ùc‰Ó÷˜bÞ²&Âk ¸¶ ˜­ã“‚†ƒÄçi´3øÓ3‰ Ù!Ê–|?I+FaM§.J×Õ-ŵ¬ûÀåN&®©ƒž‚3,Ø|Ct?ÕI R4‚ûw«à”=è°ï7&vä¯M,¾ÅŒ×ã#uxr”·û5È÷¶&‘Óö‘_ñbÕv{ò}H³<ÆÊ7ªÑ1ý?Ð-º€•¿~Ä>‰«âä¢ÐV@–„"yá}NÃÄà[°ÐQmµºÐs×~1]T‡uKOaË?øŸíç– IH]'®  ký$¸ŸKŠpØ0[n©Fi‘í¶t¹Ý÷r̘¦TçVÇNÁ¾‚Eƒ¶˜à³’82zX¥ž…ì û´x _ò×”‹:©Ð!&ÐòÌõÄO'‚–íJWüEŸ¼åܲý:LiÇåê™ lýK½¸ ?ò;{DM=€Þ‹†…SðîŃømi0z]Ê옸ƒÊù*(^†…ÖE°§Íž^Ÿê¿ï߃\g¶÷$nç7­?Wà¨;ó¨LÎC&Z;œ.¾ûº¸Ð=Ê´'¿Å†KÙ¤&R6 ©ƒÐ9^Ô2~-Ó]ª-0þøÕ鱚!}|ІKÍ cڠߤåðïI& ^ÿ•]¨yµÚ¢à#FGýVƒ]{ÌqRÍ1|‚ÊwA¤lV|¿lI¼Fdzýågp¢ýyœÜgÌ/ÃkèÜdÊŸŒ€‰#U¹ÊÅ ükvÑ`Ûoÿ³CÿÁ›Úg°påm”JÝN×0#îk‹“ùÕoÚ4çÄøù5Ž=}dHm»+ANz3ßû°YÛ‡«>ï†þi&Ü¢o(­öÍc*e€­'D¸öÖH8Ýï‰ï]ŽàE.sÀ¯ë¬CŽpä´=t¼Õ§oê‚„)ì¤ø ð횃Ê}Èe4žõ€ìJÊ*Ï”­çW\3ÉÃÕ¸´'”iÿtìwŠ0tÆBÁ°Yå¾—ŸpœÜ” €œNüà¸L |Þ™«Ûêððþ6¦x<øm…61÷·5}z€‚¬â¼øcnü~ ÷å$axi*æ=΄—Ò}èJž“ðMY¨ÃB…ó‡Ëã@ádâ6,/iÍ¥7&;c#Ø£\ƒ3k-”…͉ƒzøN‹¯àdøŽ-“a[æJRÖ„Ï÷Â-EO¦§7ŒüQ!¸pg-¶_ßa9 »Â,Z´-ùßzÁ´!Œº/fõñûñ²ÆõÁçn 'e’û÷§°O6…Ð,ëM‹·Uç©M"“=_À×ëKáÁs[lQð§ÂX¯ÃpÄë²b\çU:ŠÇãÏÆ§xlí2¸k:¡g# oùNôÿ’!”쓤U¿¶“¼É—àDJAáƒ+Ô‡½Gs°iž ]¶p!$æ°Ñêü۸ðpûiµ_‰äœª@£…ÒìÄÊÖ7C‹¹6™Ô¬˜Ôˆ"SÒ‡¢Ìg~eÕïK™A¢%õ»T¹)çÐ7v7®ûüÿŒŽwÜ$ùòÔ-¤ÅÖŽ,],¬¿}˜Œòªdòsî±å·ÍaܼõÈD,³‰÷{+PO€+禰ú¯btépTµ§…[zaã 3lýuƒíÕ–¥Gy†0zÛ_¸.V‡-I§°sË º`™­?€|u/šÿŠÆMKdxÏÌ]<—‡ÃZ-/74÷Ü7à`L}KDø¥“±Ü©î:ŒÜ‘ŽÝúCðÒ¡yìÛ¾ ØÞ;‡ÝX÷ë¬D½óZœDùͲù4»]@—Ò´r}Þœh¸ã‰ lJ*)«Ç¾ óбŸaç!;üy2 #¤YRí˜Ù¿™ï²ŽáóÊï‘7·`.%Ô{|3š|ÓA%åü`ÅÇÏÞÍ7E²ëßà›ß¦ÔÒ™t¶™YPÞÔ¤€Ü»Ϫ“±`Ž• óÃ7c1ùí,úìà¾xºXH®‚}_ìïU ™ˆõB2¥Í‘¯˜+ÏßßûýË¡eÛLüêp›øñœ$-®«Ø¶ÛzôœÌ(ºñó0ºýgΓΣýÃèª)½Ð¼Xš_ŒJRc£H»Ö"ôµƒŒã±²šà²Ú¼tžbQj:l¸uŽn …üÇŠœ_¾Ž—ÝÿäCüWÇhc[úxð/° ±dXãj"ƒ7/†­«6v†.Õ0#è/ë-‡nŠòEq»ˆ¡®!ÿ¼ú–0>ñ¡0ðç)´þs¥Üs@{ÁØu±ßë(“MOñ,›‚î 3áëÁAìÍS1ŽÉ&A9N4¸?+¿Ë£a[)h-b¿ÎÃý¯¨³« ŽŸ‘¥ôoؾ϶ô·›jh+_~»ôîÂÌt;¸x´™uÇ€ÕY-*bp&-Sà™‚ÉLÉTxxG••ÖÊZgXªÝàõO·²—1q˜n;Ýi’H¼ËÎÇÊû‰“Õ òt†ƒpߌÕÄÜ`N¸y¦$r;–À²”¯ðfËkÒÔy™0TPƒÿ²fƒÓþæàZ³n¿„Söè“2‹|œ#FgmGßFy®þË ,VŸ…ég4iMÈvšð M:Åùí޳ܫڂ˜©Î.êÞ­ËŠŨÊÍ/@·6áèÝ懲ZmpŸ~–5ù°©&_!¤ÛŽœî–§AñíàµÎ‚†úXhþ! {YIÚ%³Ä×6˜“ö|H&‘1* ͽpÂf-ï8 þ=…½Æpºt*›[Ó sªò]Ÿöc“ª¾1a¶£aƒ‹;¹Yv¾Ymÿ)z\ù|mõ¢]9 Ÿ­‘AÌïÝ=rZBœ[l’§¿j ‰Jš<ÌQâ{oÀ¦Ð×è÷kíËË„¼_Òp6f$I˰Èò}ûR õYå”ßÈ6ßd?NÕ“?¡0¹$þŒ«„i3¥hn¿*]|ΙgˆNëÛp•Òml‘Y‘¯˜Ë¾b¸WÊzºn“!Rê`é#‰õgt ]¯ Uî¶@š•$œsÔÎlÂí1ÄÌÙ†jVÊ2÷§ zc$·Ì­o¹¡\jÒgÖ³r-\k‰†éç ÀðE”ØYQã‘'„Ûå’qF¥"Ù=Í÷žáßLtñU°* ¤@°{1wkw2ªð£¯êŸH7Î^Ì ²M óý¶Ú®€ Ì¥ÆÐÑOâñ‰0‘]¼›Æ¤g`ä®^Ѐª%Óé /¼·R”³(Þãw¼æ™–×JdYe§/<÷ŒÚþ« [{F¡û wþÑ]êÚ¾ˆ•ø’¬à> ·ðõ“\ŒhõÁ—³ÈÌ¡d^å2Öcp=j¼¦…¬â\UªŽÎÒ…CÂànj¸ž¤»FÏeûì¡;D›Ÿ°pF×¹Ét×qyÜ×ãÀU?ãø¡‰ôÇœz˜÷Д&wª¢¾êbü}M‘o:'OßZJÆ èÜØ–KöçœÁ°Ø ã%¶ù‰Ì/à_0¨iþy27X Öý²tƒþúx|‹}œ›¿+p‹‚Î^ôLùpõ3ã×$†ð¬‘Ù»+»@ÓÚ„Ûß>[Ú\0èy/LwÍK„îeATo¡-à© bOoêÓ¨è/İ­Œž<Ï==þmb¬^š—•àë>t·þ<*µéwúKo­'6{ d}˜Œ¼C=yÜdšè²lós‰˜R¾lýU6Õf •[ÿN¸X'ßÚ„@‰ïWÈŠîf+`ãÁ\ˆ[½ˆ*ˆ?âÍþ۹Ϝ"îäÏL¬øÐ£'aÇ­õ`²ÀŠüFx¡ÍY0\ÌÛ-]éŽ2ø×‹ÞW£79i¼ÊF³wÑÖ[6\d=ÓXÙºQèû·KðÛå4þÖ¢û…qê¹lZ컃ž}„—\êлޙÏxï‚—¿BGùMXoxE‡¾ìÕ{ …¤|¸]}gõO¿£ŒŒ ª³æà3-gºdeeÕ~H N#ZѤæ >¡ÌÏ-ÄåãrÁ×.ÅŽç&ÑÓÝ©øàÃa´ÝåN—ÿ{„#‰ç¸r² z!ì žÅDg¥1cXÅ_º7À(Y~tæ ¸›6Ÿ‹L*¥W ìÜ:âQ2¬ZË~GÍÁîÙÏà–|v\)º?ºáµšÓ8qB(ÛØ´Œ“N»åýØc“IÄBíT+VAãøL8ñ{ˆpYfA$|ò1çÊ[SºÜö(héÑàOAlq÷I2kû¸³=7Á†ÅWÁÏ¢’$¿³äë­Añ‚ß' «x?6èë/ŸM‚ã¡Pq⼚} <&VÝ£•àÈë­dÄ“{X汓¥ìåÎxÑÁKè`‡[·ZÒ²OšT|ûYŒùpžÍ»9ƒgéÊÒBKž6¢/µ–"÷< [ÅtèÀ›v xõ‰ÔÈDcì¿0VòéÙ¿C‰ÚÀöæ¿‚jsn5±ôÏ„ÚG‡Á+Bœi”ç9Š4…Y’ úHÆIcè‰×vp¿èð€Œ·QãkNÂþ·EàáèŒOþÊÑ›IM:\ZjcjÅ1dàNx“)iaÁî K\±­¹ðûÄœüê n9¼’/N}tM0ûŸø o’ðË‘.ÛzÚ¤cñeÝ5¬w;BG ;L'O8H·O)ÆW'y' ‰§cíûoÌ@’àÇ—@r4áÓ¶ýºLt…öÓyäϹX|4|4U¹v’(ØDÒ¹RÿÀpõ ÑÁp_ ø¶;‡Î^Š ²}îÈÃ-ƒö¹úÒÖoÁø6E?¸„£*»`;ÆnØÔAüDÛ¦~’vç †îO¦Ö?rHóë¨7õÌwuCñ¨1¼ê¡ ×l9R£AÞwU.ybPÛ)½•qdá:á‡aüÆr6zËx^7QŒ˜í˜ ûWŸ„Z4Kô yçÊ^GßÉÚ)"Ê_éªÓƒê‘ZwìîåO¿qòÀð(OóýÂRÔ¡læø'>Œ¯ê’é‹ÖcÇùܸ̉-jA{||¸R oOt‡!«`o.‚ØZGô™…4ôK½éH—Ý7 O@îS)n}iT22qª«e:‹è¼9xgýR WÈ®Vë{ iÓ2!Ës'3ßÇնޤ3¯ŸªŽÓ ?ÂÓp‰e=vñÈêíæ±ØXâóÂ×Áô[äÀR8û¯§ÅõoΓ¾q¡ êoÃ+ï^BAT3(¥Dñ«†5ìHþã\?!7£s-xBx Ÿs_Š?30£?eî)>‡8±~ønpž4¾w¥Ÿö‡ÐÇ~ÇÐij!ßGÁŽ›°u®$— Žî•B9 UðzT g~jÑ9y‡Àm­}rP‹ß{`O§W—Ñâ@/Víæ+­³ùÓ ¸bÇiòpˆ2œÐ/ #–¡æAevEÔˆEôÆÂpý`Ð÷o.zëÍKN=„ÜØ‡¤{X™Pež%u±op Ç£ ÔnÛ/È* gyÇñ§½1FW¥Cü§Æ*Ÿ5¸ ëpx­? æ\iÎc²4S÷êåâúx],s— ß×êãÁ§µw “M™D=¨‡EROŒæ'¾`³}24¾J‡)‡ÆR¡I,Ķ)R™rCî=”•`ͼ»(%3‹OþdMÿìj'—Ê1z«<=>q@ðsY {•…5oñ‚¯ô†?bõ‹G“¸£õ½€þ(aŽso£ …;GIðµsúQ®É„¿qZBl6 @ÁFG.¯ÙÎ_þ‚§ÑZ"7ü,sRç÷Ûµ1ÅDJXÓ ëg„£$ß 2¢Iáe *h-äÛ¤_°Ñ_­hê§;B¯kÇñθWøòù[–°¨Ö„.„k²ˆÆ­ÈóËÓ×a«ö>Ü’Ñã­Äh¿¨7Ï댃¨ÃßÐÓó´ŠÐ›u’tQg<×(˜ÇÅC`M}p?Oë¹á§qnô­ÀÉdÚ¼ÕŸþIÁÝo/à/Rî¢Y«5ØCSJÏÃÔ¡ ¼l]¯P]JKüÒ¨On^yH¸¤o1zħÑê÷¢ü±˜ m6‡š§Öƒxé8n“t ·¼§*%¨\¾…®—û‡ý¡ n1ƒÚÜ86â?ãåùâ‘lš]Ü/Àæ€ 8^ô/®4†ùø¤z6Tß*a^™—ÝOÖoĨò°ôrnØ ãD– Þúoðç~4¯|LÆ_¨ÖL݈JæyPHŠî›]áe-p­!íž{‚“M+ <¤G¦N?‰‘º¢Ü¶<‹“çã SiùËrx»&bÖ«óM¢´s“6Ýg+Ê£.%‚[€…š¼q×wˆþ†—ʯbÚ×WìÚÒ2PÜ7‹óÄVhMuD¥þ¨ÿ=¦ÞZ©KÔ¹Ûûw¤ò¥?:}œO·yËÓ;ö[ XÞ•¾ë9Cõ‚å¹Û”Hážpx¾Eîò„ã· HfànÞcºžKÉqM+jf¤Â«\fÒ+'Åøù}céFõý°3½¹Ý¶¡‰¦×!+g¯/ô¥KMU)^[Eó >ÁwÍÍ(>§Œt,ß˯÷† Ë äøí,¡ç¡6öSçêZ‹ÿœ E#’q¿Ú5ÐÔNÂÕ1çæ¬"Gvõà•®~òué#ÆÆÁî¤\̉s:dÓëïµ µÕUy­³(SÔÊE_º];ÁFû¾°zÚPøN¤úÒIG"`šy=L[ŠÉúN£+Î9åjd3›Þtuî’ £Í?êKãWóɼ!»¯šÅ‚䌑xm¦9/~ø‹-J g3çEð(Õ~¶÷M$¬Í ¿òȇ>Àò"ø<ÿlŒ^Lï%8ØM!͵hmÎì²#à…úl¾FrÎ,/D“WÿؘÇyx¬Nšvnù û@~£þvÃã Shd¼Ÿfþ¼:.¡|“õ.Kë,¶â²iÕ pχäAÜéXLŒÛÞ|:*SoÜ€ó«tyåZ7f´9•:· öWÏb7DR€'z£~‹'úß§wnòeÛ’¡h’Õ)ÁwXOà‚TóB¹ÐïäIZåÍ“û²î©ó˳§ó#"I´ñß4虋þ¿ãøÌñtÔ"Ôq+Æ7ÖÓQªØŽZTnà¦\‡*.M£Ž†ëñõI%jr¬ŠK*‰ò®)N4Q\Ÿ~=¢Ì+›bèzš„õfѬÜÓ<†ÛÑnû"^¢.B›kó…#¼>‚£7j—„¾ñÉÙ%üíö4¬^½ƒÿϾ£úU´SpGíÜC³{p¦u<S‰>#ÅýÑbY }!æKæh‹pµ¡–ô›§ ¦ÑäáŸshWõžaI¯Z @ŽÒ9¸¤iÆmëî`x­ž^õæÉn‰¤+T¶_±·Ï ›ÕŒø„V4z¨,½ÿ1™Ñq×à[x•¼éÉ#®.ŽÝ<t:€îº!|xÓyÁÅL/!Ú\€ˆr5®}ÐŽ÷—ôAR¹ Ÿú,œF#‹Ñ ¡Æ,LÐÉæŸOd„`—µ'µQæ~‡÷;Ê_ñ]ã*1Ô#0¼N¿û ¯ÞrŽ[MâÓÝsèá2ôÖç.ØbU¬Yû¨?eæO àÇeÔØ±•«Ù³U-aGN6ÝFÝ¥ÃQWÚ>£·‘1mú\K“>¶Š)P9C¨¿@Þ”.ƒ¸ÇvÔׯ“õ¸áÙxM¾xÏfvPÜ•[tôp÷«Ú}U„b´Kò&FˆÄ²õô7o:¼‘ŒvÓÃÔc^xûP4Æ]õÇëâo0Û>ÎiK‡טŸﬢÝkϱËÕTØy†æu=-sÎp“áƒz·eŒôч)ÍôÛóaôZ”¨ 2ìƒS{ MT™Á¥Z³‘O"hI\!Œ?ž Åé¯1 æ¼@"ÌŠfšƒÎñKTmˆ×£|ù‹“à÷e=µ:\!æ Ö€ã&tÿð¥4¯t2òQ&ÜsãFèÞŸÄc,ÖÒä7µDfˆÿŠ7ö˜óµ›ùzn™ÞHž—¤ŸË—:ÞñÊ!4ma?jºŠ– rG¹E¾^æo_цüB¥1ܬEôQÙÅ—,V¡.¦~8fž©ÿÀQÎÑsïyR×s>líN}¹,+EG¤ÍˆôÆ¢™ÌÊw)K¤ž„§–Áî ZTòà šb +WŽ¥æwNá‘ñ}Â¥ËÈÐõ0ÎÁÿ.œ'×ϦöÞʃÚ7i¤ÃçðáÑ£ ÿÀ'ÜYm wDºØÖY°qºÕ™øòÞ‘ÃçÌ¡êƒvó<ë{l‡ÞWåèÕ)´0æ8ÿ¢ªBý“ÆÑ­·Àër!úlP¡³åØ|·[8 ‚ fêBG¸$¯¤V0âÞ^Ȫ,dzA‡Q¸ð<³ J ìþ¨aÆ|jƨI²âGŸŽ¤zÎå¸wØ-˜5)O¸ãØp8¿Îšú=]Ío¾? ÿ=‡yž`\Flºó+ö…SžÅ—Ù‚Cé:tÎÏä¢]yøÑÞ|G›f¯Ô¢o÷Æa¤d Š×4ìšÂ>yKs‰¡ãùSù¸ºö*,º¬Ëþñ¢ó-¤ø¢Ý/PüêUú¢ÃŒÏÿ|Œz#×1¢*ò\nt‚O,M Ϧ½c'ö<‚ ëðîâòGg/T¶ùCTŠ5_~Ý~úñ’·.äíÁGÂã§Õ‰òÁ%ÜN0‘+®ú…ÖÂüûZÜžV£áҘ̱÷×pÂ%šána~¹8¤øj•m—4·KÆwíC® n¨ð-MŸðàŽ¤äçEÁ«¤,Ù¾¯O±ó2Wzô%å¬Aµòl0ZbÎ7ÕÊà>g9¾«¶Ï/‚Û(ô%Ää ŠÔCWžÕ*3÷'6üœÄZxšZOwNÀÛV¼!ê0xIÜa©ÎÆÜcòì’ ¢9AÕ°Îs çŸ2QLE”øáÂMi>ĸýƒ›˰`Ù\Ø"¥ANô=õiWÙþà¡Xr|=é}m‚>2ÃCðu™"h›0È]^@*nÿÇÊsqBu·`¡‚*X~½!v¦³áñÜ ,—º"è{DÅ¢ºYM<¸ÐÝD.i$dÖ’qÄŒ·ŒDÞÛKµ§æ¢”ïX~>k)¸W)¬P§ôèCáÒôf\~ã8ìUx%ï¢ñÑÝÚò †î2Åaëu¹à}!Ô^Q¤ÛW­k;´-’ºOÈ?˜¸ª+\ê©z<*l8ªŒZŸ‘Jm ÀßW߉›¢$^+Cçy_›Rè+2œNyˆSæÿDwo?zQKŽ5Vµ³¯÷Ïpø§ ¹ñüÂωtàŠ]e• By5ênaƒž+Žà÷w¡\üõ3~dÏ+عy|м>\¸%ëÝÕ°T_‹ßù8yÔi~¼/^x̦æõqüÙå Â+l’\“¢“©ä‘—ä~Î#œ¿s [ö׃‹õVPWËÉp­:‡ž>w™õ™ÞüßZmz%ŠÞUS úYS¹Ao..Öå65'¸Ä0_j.õQÏFU-Q*ï‡\ãé¹ÒÛð¢8µÌö’7*˜eÃHÞã= NÐå1Ê×Ðed1[Ö» /I›=Ï÷dÜ”DêÙž#-Q1¥Rßž‡diÓOw?šN‘†;ƒ¢©†‹­yÀ}`œàïâ[0£W‘nõýöz£èÁ?ýB«GòFëèowâGÒƒøNíè|òŽ”Î€”S'é¯.QÐy2…&Ü †-–t|šío Fóc›Üø³êôÉhd'ÄÁÎ,Škö'€ÒÑPz?—†ßYD¯ÍS¡·ú¦²5é‚­GVó$ß—ê¯D–ò–NKzjt(Ù5p´Ãmzr¸m¼ùS×ÏÇïÓÜ#k(ߪX­² ?žôÇÄerüÆ¢V‘Ž©bŒéô>Fÿá?awðZ¼üÌ‹“ƒÙ8I#ÓŒéOqGª)SÝ£¡}G'³.›àt$e9Iàš‘ßèôjšŒp[÷IÞRxÂE¤¡euÌx”üðö³_ãlç5dýïõx|Þ.ᱨéðÙ`^¸ÓYª:Tͻʷfá1ùs‹Ø¥|îXLiÝŠeÏß’ãÅ*‚¼•'!`‰<=P5*Û-7e˜òÙ=˜#'Ã_‚Fé…TD¼ÎdÊP»£ÙÄ ¡¼Â84Q2£ód‘ÇÞ|ö¬RÜ«˜¢+ïÃÖ·È’Šș¦»góÜìüÄlølÁ㤸¥…:tŽòãɇèòÀApXjKßmûÈžM1{ÇC¾ú*HîÛ.f«0òÄSAÌ·Xì3”V|RgëG ñ¯@ZsÔžgûm…¢Ì4TÏÁ½OôyÁ•tÓŠ´Á?„owÊ£´¸®¬kÒÂ=}%xpÒwaèó4lTʤbwÂðÁð×Ö€š•Xµ¨Ü ¶sày¡8ÄËŽ§~†Kä"À¨P„Þvû~2}9ú°ÎëILå¶*’üP]líÝT=}b›òuó*ÙáÝ×È÷û¦ 4f3ïã÷ Ábµ‚Λzïº äO§cöT°@I ^v [ìÄxîŽTþW#Ñì"“/©¢[L£ˆë-&cÝsÏ¡’ñ(7öcÝe䡯= q.nO!ݬÎ/3í$iÑoÈü}†DE€Sæ',7œº¶eB¥¨{мU…>ò}†s/„ÆŠÅd™ÈE˜ï¹×]ÕÁ^9$á/á8å În†¾ë¹eú7Ùó P¬“CÉ.Ý1Žß„&KsºlîyÜ0öNür’e7£K߉ î‘°ùÉT{Û¿µ ¸:–~Ñn,zÇ&ç¢Ï$0¥ÊŠ6þÚEOžM·-ÛÍ⟄òÚ§ï«Ç.‚Ã>ÚÒ‰’#¹·Ž,Ø>HƒÓš#Áj³©ÀíÛÌžÒÛîđԡO„§ÃpWç3&•Ã&}< r{¦ƒò2ºF®Y›Ž¦yb´{éy8xû>¬†"ðËBÝ­Ñižß]¬¬Cˆ3N:cáÉ!¼~S›>>–¶L]ƒs6Ãç+¶$…ê‚§”Óß?Å)Ô© ï¬Í‚ãâkqj}3É^ ‡[¯Ÿµ#éþ5ðæùi6ÝJŒŸl|\¹o¹.ÿóV—_ò%4w:=ùÀ†¯”ì±"Ýÿõ7ö ½Û{ìlxLYØ*•޹õf´ºy7=úU™Q'©“÷’Éd­Ž³Ð¸OÆ,©Ç!‹Ð?ó-˜IÝ#™Þù8Ø7þİ""Cî&+7w½~“;Gðmá0ÝÕ ;«íHÏ­’áéBƒJâXÝ0zi’/“ ÃÛ8ÎWµâ]}f|¯êIÈÚQK¼ÑyF9: /SG¼\ø–ËóYÚ2ô‚N7;é¦[½qùSr7ý±[‚‡§ ò0 [yQå©¥à¼oµÉà×~D¡ê%€ÆhIzÛ ¨‘›Ï„6r60«î–jäã³ :y±*M»¾/‰Œ¦ZªuB±•ùWß’Þø|ïka†Ùnœ½¥œšHì_@ÿÑÏäþ¸+XÜ ¯}”¨l“5½úL“ôi ÊÓ>Ž,ªÇÉWÜy×ÕqP_Ÿ4žØ”èã‹?z\;î²Eƒ´¾ÏO(~eJ’|o€42rUµÃ9ê¡Oöólñ[ø«î;[²GŠÏŠÐã×cn£/(£¤pÊ•‰Ø¾.»ú¥à‚¼}ñ焽¡üWÝ0&+úŸR¥cŠ;I¶@~¿Û;h§€[¹™s|\$ÜÜeʧ¶v³Ô„µ4ÁížÃìfupï:ã£Á5äî!JåüÌe³²Ø¯´"øâFg6¬¸g‰†¯ÀmçgÈ^´ îè\„#»Ö êt²3ón‚«þeŠ-³l‘kCïUÿâÚÝ©°×}5R¡8ÿ{*¾ÉiòùB슭ƒÎâ‰|\¥JŽ~‚G/I¡BÀ ÞÜZÆCyGŒöoÿr1ð±ÿ2[=Vwþ —,® ·Ÿ!÷r…keç§IçñtǼ[•é°P"±,*fÓÕ›b ÑL,>l‹ö‹?áOq··2Þoâ%*ÉÆóˆ„~2”nKG½ )ü"AÕÒFóWÊæ8Vs ü&Y¬fdi>šNz¼ž“«¾àEõ‚+ÈfN>ÉÙ‚Ó%¸þé ð+A‹5•LͶ³µ$ÈÑIŸØæ¸:40ÓâòzQu† w—J¦¸ÈáÀó àûü¤PvJ6v˜âÿö¯;ÿU#'ÅÖ¢\Ò*Ÿ#þv@µJ+«Y¤F¾<„F¯}Lz«<ÚÍ쮍ݠUWø¯ýßzk¸v¦ÊPháÇM„›ÿ`ꦫ 9ZV?ŒbC½1ãB>\ý[FoˆñéóA©r Ѷ¹IÞDÈñ»¾lÔGî%ð†#þy¨Ò‰ŽŽ¿àý-7ܼ @Óp$âóSpIè‹° ⟙gŽYs|tìø?…+¡«05K¶OŒ©þ9aïQÍ«Ê0º¿DŸknñ±ÉìTF;j_¾ˆUe!äÛPS¶p®í²£¨‚¶9%ÇQaO`Ü6¯'->»™»d1;‹ /^%ÎÂB6öSë»9šÉŽOãµ÷ÇÒ˜Åï@uð9|LG£DF$Ó?|™då—BÐϘ8k­Ð:ÜŒuÉqíq¹Õ*WºÁxx',øgÆêl‡ð§é¡8áŽ3«èÑ&3%üùþ' Ü™]Ç“…á8sÛ ~Ãû+ڨ΢5â‰,Ì¢¤dàõY|–ØN^ ±÷¥xZÍHÚþ÷7‰½È4Z‡á㇉bßVÞwãù9Åæ›Ó /¶À#‹døÞ/Ie^„ÿ Z=PÄ"Ïì„wÚËðëo*x®v•„Ì?ïzc¡²X ûÚªÙ…âw˜¼ ": ³N•Žð½LÚ³ë°ÜÿüU¢šÏòq«0ž-UG×Ï‘åÔñ‹àH_8,o>Äû›E¡"R¼gÅͫǡõ|EºÎ1J êpéÎE÷½XÐóþØ•âäÄFi¦d©ÖYœ™Ô…ò¿P1Ù˜+Òä{'ÿ&¿5¢Pí[5ÙQ¨ÄÏM»wÜ_ ÎnnfŸ'\ ·µ4yÞ 3X\žÌÜz  t~2™õû/qùžŠ úç=ÓpÒ¦f!ûQ>G‡ÒÝÉÄZ¬ÙÉÀrv=Þ€/´Æ°#º¸ùÝ’j»­›ÃQìßL ’Îeºz]À×/öŠ‹‚¿–—tÙÁ—ÏáßåÏ 9Þ ê²Jq¦Rþ‹aél¿Õlf-i³ÌàK×Yö&C_EÚѹ{Z@t»9ù¶?(b¬\ˆO;—¯¿òj@ã³̹xFBï×Ó1ŠÞÉõ—ÃÝ‹=pÛæ;+±çÍ>OÖV^Å?õoá»h>«+Dæ@×—i¨×è‘£„`çZ ­YqøÉW‘ÜxBpÉO¬“ %?üñk‘õÅ¢AP¶Õæ7"[uòÝë ô†ŸÏIú)n(¼Ùp—”}{‰¦Ób@³6– $Ø¥ Cc¯.¾=t ùFa—H4\ÉŪ÷Fã<&r};Ä_ÉÂÖg=¬êôiœáþéœ%P¶û'ÍKñYç2v”gØW¾4âüŽƒc©2?ת+¤˜´J9c‘ÃÂÕç£Xt }1ÓbYhƒ2ùyÈŒÞjޝLà÷)X]›‡-g5àËÈÑpsÃ:9s,®™HåqTÌs¦;2ÚôUùÛ{£ø¥Íȟ⻤¸GŽ?~ýU¦°5Î%¦JR£i߉Gôt~úI™ûÒ€ªÍS&“v‘•Üpx ó|p£7÷€²EË.NÅ©O{ןÿÆ_ÚûÉ»¦9Hl$hò,I~éj^{êÎ’~*à™»2öväý€äŸ/ðìôTö×J–Æ)Ô cÈLK­oKö`Ù/`ï,‡"Wá¹ ùðÀWø½m6OÈJ/©—ÓÅRaÜá#yø¸ŒÉJ EÆ&Ô_憺V2å¤t8S›Šä0oÚ+t0¿ˆvM‰˜rp8(Éà  ÈAŸˆÐ§ðof,<3·8½ˆ„÷ /_yé¶µ‚i¿ËØä3èòFE*¿Q¶ gkmdø“'¦¤(Ñ•§æ Ãî.}úïÌèT^PAoå,#ý3,áIÓC\f‡ÏZµÈk¹.ÌÛ&Í/9ËñµŠ|M¢"Ÿ0e4Sà-,ß:•„”¡¦Ï«¾°ä;ñQ&X0˜ó®Q â2,kH$ºSã:4üÿãà:À±üÞpV¶ì‘¬ŒJ2’ßyNR QÚCCÒÚZˆìd(²R!IÃøÎsRˆRiÒ"¥M[Òøûý¿ë:×w]ïûœñžsÞû¹ïﻯc­Æ‹rvƒM^$)V A‰ºThBqÝxBW“N™‹Ÿ¦<ÁË÷ï ÜBèÿnÀ³ú_‚‘U\Wê×ñ႟¤S¨ÏìõxþÊ-xÜhà y‹FÜ®)º†n„3ÏB· ƒ³ÚÔñH( ?4¢Ö59L·e‚¡ô .9)Ê_¹Õ³âô¹ÐïùV¬•€á©átµ™ 0H„¿dÊŒÞ+bÇs3ô DU†‰-lS¤>«RÄy“EÀjØ*a¯·ZÁ§6ãÕÓQkÊh7|…½ém=UÚ>Mž6-?D·O)¡ŸŽzãÊKãqò#V~GhpÛˆ¤Ÿîƒ(!ÛžÀÿìÖå+}Ná‚Oª•;”ÆG\†ºØsp'l:_Ô?†6Þtçq¤ðJM1}üu-)ÃûŸð…½;ι[Vñ¬o3øåÖ™x®4–/8½ƒ—ß[€GëV£©ûShÏ‘ •—¾¡Õí¸üÁ^þÝ.®°ÄD—p|u£`Õ²¢3O‡)¾‹ª4#5pïG¯ç«IŸÐ=Í÷¤X2~;˜ÆšçÒìv?—A6Ü¢y!e4ìd$O¿Ên‘áüÜÛÕ|ǧ\ÈmNàÑYO©èÃÂãÍéÓ³¨¬ìÈ% Œxü­“¸ãˆ(­¯ ǯӧ¢ƒí¤á—fî)ÃÄ]°Ig-w8p~ô$EƒÚ†Ãäɬ“ä˜Ãxá…Aý~ÕOü;qìK0)‘p<˜S/nÀ+_’Ü»r´O¯›ŒmÔÆ¢òa¸1D>´µÁݹHTC¨|·ÓLqm Ôû‘?#…e¿t©µ`'ÿÎã¡'ÔÀýéQbÕc*FpÛªIdé¶\ص?¹x˜óý}®˜wî9Wàˇ½Jë TdßÀã 5P5,˜;ý*dî¦Á•s†´Ý6—¿õG4/^?aÇõSC©ó®bÆæ2¸“Šþõ¢tüYðÏMA¦‰õbuàÀONþ—"¢ £n,{ÏÃÍ“‡ÒŒ%ëqË”‹X®ÞnoGóõ¶üß÷8ìÆ÷A,L7*ÆÐô¨A­¾’6ï@Ÿ÷÷ ²f»jvçÍ*$W>çC|\Z»ŽûâúM$úÛjXן¦ï½ùÈ´öã¡5¿mÄU|.¡>^€±2ʼ{›€}÷ˆB‡«±êì|үꉻ’±énXÈgÓ©Gâ°uùPØwÍŸU~ ý¹B:>Õ’5|¨Ö_9Úò~9ÖÝ©‚Û0ƒìèò§Ò4øm½×LgE ªz¯£™Qp6DݳáÅÑN~C OjËqžÿ*ÃÙeÃyÓíùÐÐùŒ´ùtÓGØ}£óv]…‘›l`˜ÎUz}EnÝ"Êçn¸ƒgrÎCàø¬Ü®7GñV“¨|Çdœ±.p·¤ó௓8—¸ºÙaô²*vK:羄+ÉV¨«_FŽÕÙ£îö$’a‘ìüôwí¥k`ä‘ôì„`ÉCÓþóm³{[ ê|><»ÒÔÆs¡ð,äKœæ“„Û¨óÁAE÷ý”š%À£#Ó™U¹ýùì_¾¿ ÍÖÏŸXîx'C‹þÕñt¡"4È× û¢àä»3 7ú࣓¬#£‰4”æêf[ð²s>5BÅwu±Ÿ‹qH.Z6?Ý\á¾R•¢x™*ª4ý„ ÷†¢Þ™k$!e5ý™`̧Ìc+.ãn&ÏÃæ×âƒÎ™°ãe,WÀ¿¶žìÞï¿x¨ª†û¶²–ë¦ÜDt ¦†ºAØfòb|5t: ,{ylùü§Dÿ)'é+màÛRÎõû ªô{,u¦F턌oºôtÞ¡ëÌèê†^çHbÇ|ŒÝõ,µ jgèφ:[i¾(7úFÓÆXm~Mû\‹à`óý0œH8‰Û÷ªÐiO5hUe­ÅuEþʱ¶Öáö¹8jö_2úã}üñૺ =A4;M˜þ¦^Mf{¦ºâ¤50ÿ…ƒ°r-bÙï||jÐ/<|é,6öÀ‰:£¹Î qžmú $%j.iÔ@Ú“­LL µøÌÙî0½³DÑe»SaéãJØ´æ³ð>“cÿy®½ý’?‘¹Î¿wëqõš”üwKèV‚h%1å.Ó RxwH ]䊉Âw¾ï«&g¡F¸^: †~¤¨gòå^U FŽœÇrc†Q¹Y…P›KàEÉ)Z0¿-ÚEKã&ò½ó.‘3‡DxqÒd›£A*‹ÒÓVQ¥@'ú»÷Ëʼϼž¿ÄWêjT^oLÓBÒ§ªÀúN >}×>ù2I8æ™q‰¤ªBAPWªÊ݆[ÉÆxjÛRj?{p?M¨DJôh®Ÿ!ÒÏ*³Î‰9¼N_7¾­0§/¥] Σ».Àî•®eÛïÄrN4…ó°âÔ$úRã$ÅcB¶àÌ…ág!<¤1E¯bûƤ/ÍãðóÔx^%r šªÀT…ìçû‰´(µ”ä&2»„šgÉÔ+0Îp©@¯5s7cw’§óh½íüZoäkÇÜìDaÌ‘•ð{³>Ùéâ;{„?Ülé‰Rôú j­(w{IÖ8EãVIrxö(4&¬®ã:d^IüšqÓ+–â¹Ê0øns o\A¾\q€7Ì©lêHÝ_ÃŽ¹©RI½Ò*&ƒy³î²Zu»ÁÜ8I’iËŸ“÷óKÉωö¨9z(l³ ÄÞ³'XÂ9k>ó×qÈÌuàöZZè—!E2W¤â±K ð> ŽÔ›O+QéË`£—Dmë‘Tãæz™]ÉÒÚTxÐÒá<4Ë·žãwEe‰Ç"š¡µŠîv£/³î°õÝcŸÞËkg݉œ0|U4—Ì…ðQó9Njà FñM £qèŠ#d„ ½˜” ÏLRPâ›;¯™™óÏçâœÝXÛ¾Ô/jÑ!¿cÙ¸O†4uê;BU>‚ý|'½üz˜áI -yj¦ÌÝ4Õ¯°}VêÜx¬íý·ŸÐ³+‰DX_ñ[‘^=Š|¬ÀZþT̪ÂàóÒô­u>=UBž´Ç•²¨¬s¾ù ß½ŸøÄ…C¹ƒ8?šM; /oÇ„ÍÇ!¨© tâ±7bÀg[ˆ0„¼—û—w»ÐCá¢^,~`Dg­«AÛÇWáƒÈHªÿ¶Ÿ°âœÂÑ…äõòdœ'o‹ÆEÐõ&"Ô•—“—z=B'Kzuß:z»ãšÐúM¬l‹ÂŠ—0ûÛ',ìx^0_dr½g˜ØMQ¡ y šY;!¯ê"ÛÔˬí±VÚÒ»¾2ñET¸õæyFâIÈ9v´ÅÃîI\[& æKmÃØ¼zàA…óüpº¾÷.y6ûþթǃ"BHÒU䧬ya»%ý!q™+ØYÓûÙ‘ä§Ä{8V… 3NÅÅþ™9tö« ¸EêÈÍgãÀý¦8Ìæ‹õ³±ùHó·_ ‹Kéãâ üñÊ_­gB‘•°Õó 1ÅpiGt¼¥EÛ{Ãy\toy¡LíHVeÚÒW*qàüCˆ†Nò_CºÑ9ü݃11Èì&)øåù‘F&Lâͪ—ñ‘Ó»Ò¦RkX4ÐYk2u+ŒŸrÞFøÊx |0Ý óñÎà·5hOÐáì¯@t¾%uøWÇLNýÃê†cà:þ2n·GŒ.N÷è· 4Ä>3ƒ¥£„9GÆ ÆÜd™«Ntø!4:ëH¥OcíñW„õèV( Åë«¡{’2¤üQãã_໳Ÿ°#M•&¬Ë3b’ Õ1ÉëlÉöll*}E&-ò­Ýt[ Ï~NEç¯á £¾ ²G¨â x‹C¯M¢CtÔÑe‘© ›eDà±™-Q>ºNÕâ·ëq|íí»´­ÿ µ<Ï|úÖL¥þ#†Ò7+Ò˜·GÛ¿‚=¼ºÚü¬xáŸ^þŸ§ÙMב=ùø;•éÐÇû×­¨ü:kt=^Ï'Ig Ÿ ?ˆÛþj¡Íö¥Üiä hìXAÏgî‡ þïØ^™³ôûÌHþI/»?Ï¢÷¿œà§¯ÿÿsøw¶aùıè*W ’'ž°Kf6Tç>GSg$Î,ÅS÷ÑÝ~ u‘´Å{ÓaB© Í ¤‰¶Ï:ßâá ÊY¬8Þ*ü{¶C¢"hþÔó¸²ÿ ìãAÞIq½¡jT&c Õž;N¸¥Èo¾uÞÑáÆk¾Ô¡¨èK27#BÄÑÕgè“ïþ<¢ ÄWÙpuumº°B‹¯ á3ÿœ¥{WçSÏ`ù¶œî>íË׋ß'dÞ raq:hf€Uà9*¶/•‚ôE6vìdî³<îXÞÅB‹<ì_ñ –ÖaK.ÕÈšDSþ ™¤ý©$Úú-™4ök[ FãÖç=üÍnV;¢@—¯·T§®™q´dÖXt¥È3¶RÉÊÉX»l—·›Ãg !\µp'&¨›ñ†™WáJ× \tó¼ºÜ‚ãÎ'ñþêd˜ç1Û‰#—~.Äû2øÙöJ5Q~¯¯‹TÉ»Am–’PdÊ[vï¦>ˆF¿l)a^àò¨"º¤u/¬ÇÐ4Ë€Ûߺíböÿù­QBɆWMuÐ2e¥RñjàR:öª -^“ÖÉó}¢¾ÎÚk¦ÑD E:ôÚD.#B3ËpùôPYû…í]±™Ã×;øZ÷î»fNçô_FŸ‹a䀨{5† ES_e@êkS~1p)FŠñ]Qò<àœ9wð¥WŸ«rÙS„Ï>®† ’¼®S¿¨Äò9‚/°ãÜ7ÁŒÚ8Üj"JOt—“„+6ð9PsÜo yRh5¤ýÆà°I˰ù@{d‡é'‹Q¯b7hω+Ó•8Æ­i‚—{«ÆçmJCÍé‰(^¤ƒžûÏ@ž¡6õ·.žhC—È£(껇uÿBx¸åš;&0ü9–ÿç§vRÃ).;± æýÂòyîtð:–„lCkƒ¼Ðê´ôžÐõ€÷ÓŒæþðÏIœ¢§Ä÷'MǺ*¦¼šX]ÕàurÏ¡c·;ÒlÈÕoô`j±??j'ÚŠNÜ$» ÷ßFŸ{]bǽX‘Z8õz_ G€Ü?Yzíë<:yÑWðR2äÖêG¸¤¡Î½O:æuPñ:äÝ–©¤µÍ„Kͺ§R6ð Š#8ØšÇe§%m¨FùòIBŒþqÁy™­&u r£ïö<~C»-8ªà *O©ãæuÕ!0=Cwåë3s¿2ò°ïÜniXeåðž˜$Ô{ÁIͨûpvú#ü!¡Ë­d'àè»gèl 1ÞqM“¶èÒäÝõ˜ÛM”EÊÁÁýݸUþ2C_…bÈÔ Çß²aÜü{vèíG™ÙÕg±E9‰ëÎÚŽW»&Àú:e.R»Šÿ1| ÃîÙðqîÜàJ•‰Ê'&¦ÓŠä'da–=-{!CCãˆÐ° #Wó§:çùê×yÉÆ0zxÞZZ5*“z¤ž¦ŸŠ¶ ¢]Òøtã¸èO)0ƒ#üÜOXÕUbÜ´hÕÛ1•ÚOŸËkù«°¡“®…xñ”­#hÀ¾äÓê‰ôEÛeöUk±Q¾JK"qÝÎSd ;Ðo³}+ /Câžmtç¦Ôu§:ï·Á"ѹð%°¦Ýffù6Ð÷æ>ŠÌú†Šê6<êÚDŒ2Qæ7F‹ðÐ]Æh¯ú ¢—‚†8wí&l÷EÐ9êA]ƒÐê]K~¼®Kñ cî×>ñ„I–œ‡Qüh×ehÌ>Fž¾ŠFY{ Z}Q™oa»†À¸O=ðgº& •”¦EúTüV'Ù³×GgqëÑÿ lÑv¸ÿ`8ïñëÓ &PŠò7XŸ bÚK‘~°ä»6G³œ`ƒîKâóÇuƒPó<ÎÞ-Í>Í-$³•¹·µß(¨…ÊfºEí0LÏ­ÅD­Ù`·W—ÎÄý{"ɵޱ܄ÍËe 6-85)‰=šVNʺãoêH´E¾[ ‡ †óéîª|àç0úr²$¼zˆŸ6Ľª‘\[*ž?,ØÊîif@uÄA¡TJC÷´Ý,.l r_D¨V$…´³A^T–Ö;úÒäU«øó ]$ºñÛÊkðOžžäq8¬Çú¦.ÄîÙpÛA™ƒÏ=ü0£…<”•¤Ó¹¢QñÑš®ùÉ…ƒÃ°r¯#õU1ÄÆ‚ŒS™„»ƒ6cÓš48ÿN‰èÓm“ñ…”˜FÄ௠EÃ*Е—¤õmøÀ»Ø¢™·Ê¹Û& ¶> ö~U$Æb š ÿƒqÃfRåáz|Åö‡d¼Q4Ö©>aSjmÂÿ˜7c”Ì·¦Sš“>“0Uuš-ºÏþùÎE·ÛØqð ¾ybM}ÉñÇòìÖæ2öÅÈ êÆÜEYW8µÉƒ'¡ðè>_èÒSƒÖæFØwÐŒê^ׄê´[ qK?[¥ÃÑÕãÏàV[{øù[”½g²q‰ðýž2ý¨ô—í­ÃÎé™;‹Øc»ï`r5ïÝ·%ZŠÙøÍ^ž˜® èóå9ºVËþâ¶ï¥°¼d8ge‚‘Í/ÆìºÉ»ð`ÖæÁCO^€s§.áœóü_¶}°±Óüs˜ÈòQXX UÉàõú;‘w ‡œÕâôè-%l=¾ŠG™¢î2mHõƒÙC°VÒ‡[î*Ç7ÛJaäµb8î †67‡ãÎ^¸ør3V4—1©]ßfž‚Çå"tÖŸSø t,nï›G¯@ÜñÖ¦Íÿ¶ãÄýl¨T>ÌNšƒWƒDø£p·e;I( ‚Booz"ÙÂWN¤Ùg`ܵ$˜qj-ÖªNA• XWèkLýY+™Gsç vÌOf·^„ƒÚAU‹O¼t ʧYÀÅ7ªô¬‘ª¥ÔyìLúû±+ŸVÆ>æ½5Û ˆWÕtš»è7Š“2x~éYKëLC&ûYƒåü†}<ýÓYK¦º|&ÆIPø¥ÜêE[{f.äŽd·Hñ—Õd‚“F6?j¿ùŽxºð€4U“·¤×Ü6:ïó8……s£Â˜3Bõæ:{äþVw.ߘΰ_ÇÏ¿ˆ¥‰«oâèãêü¦Œ2m1ŸÏ9¦™/’èœÕÌyôKß³“˜–P·iîÿ¯7^~¹±³^-œÉw|™:zÀwÓžyÊù¦¦"½<´zŸ 5¬ýø¬Ô‡85ò"ɳÜGm¼Žâü?·…äÛ\º_Ä"OŸüáz¼GÙjÿ&Óo{ÎqÁacÞã]ûþ ·Òÿnî‡&óçãø©H®ÞØ p~ }4. »7ÿÂeq!ðeŽ9~ÌRâ»?g@ñ» ¼’É@”Òªzb ’‹¡ÄŸ4§¶ wÈ/«»GЋðÕ{‡€|ÄUL ’š´(:WМmC›ìħK/ä+Ç¡g×Fà÷Go`ƒù4j•5ó™¹Ǻ‡r?Ú°<†v¢oàÐ:c Ï»-ðù„@˜QñIàî~'G>Âæìshû}ïÏn\„qg³‰j³4ê<Ñc1Öx)e.^ö™†4^k0S²„¤¬“ƒg-\Ãå-iÑx‘Ø&̰zÎÅ­Ú]N>à2©Í°ûrºmqÙkÁÅ/Õ üÓ`áIZÞ•É”µeØ©÷“©÷ýD²ñüç¿xv¬4?¦=€…S¨Ö^ÛY²–­kEµ3?Hš¯ ý'ÅUñÓ7jù¨K•e®ÀG.x{<ðUþ²aÅ •žüm° üXlš»å¿$ÕogCõÛ{Dk˜Þœ–OÄn~Äš¾ƒÎYKæ óJâ ÂÙ“ßÛóåyÒü‡øæx%vîXÊn?À»{XêìS‚›ï+X`s.«/.úEæøø`0¼9¨ïŸ›Ó«W ñã™ËäxÒKœæ‹oG0›ÅRàÿK‹’ó3jÑlE²£0Gã;|TlË3¾àõgý7w7•ègoòÞ Fi„Ô¬ÎçZ‰Ô[ã4^W2¦)qò®/+D°&I~Yw65-¦Š"‰ ŸÜBžu˜ðqéÿþ¬@+ˆm[‰e÷þ+¤0v'¼ìQ#6Ìô¼OËøIúdÓpWÐaÞÂWx!£B÷Æ¢‰ypùÛuLÿ3 ê4–òyŠ‚A~ò¾ß°§Ÿ”ÅaÔæW0<Þž‰Œâ 3ñ‹µ³JƒÛoÖaUuPùáá›§Ýì¥Ý|pbŒ¬›‚Ù_†qÑmx£¬˜Po¨:¢Æå*GÂ-•ë¬ö¦šÐuÓT>õ¢„}?; Æ4ì² Ø¶j=g Cž:q ý¡´P¦7v-…Ok!²è2¬Û)GWˆga}ífœýÔ€êæÃï¸Õm/Üxà† ÞéÑ»ç6â¢ò$\££.vþØVrOÓ˜Ô³ÅQ׈Û/_úºl(k!ÉSuuyã™s¬ëL·á–ë›ðꇧ,¦ä9Ëåà¾ÀYôAíIð¯ÑF­}ꣃȚ1Ïà€ýCŒ+æÚʰon ;wB»Û¦Ó™Mè²XÖ¶¸õ÷ÑP¼=ÃoâwGlð`öëN"-­…î ¥ýÊ«¹ø=vöa|MxO*j T]W: ±eËDú·!‘]àÛ±ÒÁ‹sô¡Í½6¤âÜ"ÜaÉñÝÒ2Ü=8‡y±JD)~&Z,É#6TŽ&7-££õGSûEOqºmjÔ†ßaSP*! #ú,àç”t¶Tç#ÞÚú’‰þ¶„_éÑ‚„ÛUøgÁðØm AšÔdD#;ZØÇFo‚ Ö|áþÎÚŸ=r` ù%ö”µúá<þ]|3›ÊÂ.ÊÒ­ 7±õŽÊì÷ûå8¯Î ež…£^Ó]2å¡©}”NÌ"eiÚ“õ,X³ˆô}êD«¿ïˆAÅxwÆŠ¯Þ/Ĥӧá¿ó—ÍoŸ Ë´5ÁQZ…7( Ù&³"²!b'85Éãñ!¯!.\Œ®ñÝGTŠÃ /xÙze*´Æœ!‰âE¬„§‘éí»@a§^õŒ…ZTqNfÔQø¨aJ•~ËCZáXÔš îŽÑ8Lñ3s~Q#¦CÿòWd ©.–Áˆj¡…ù$L›ã_ƒ‚Ùô³Ã 1B€Cr¦ò¢—öð9·ƒ7|Æg©î¸ýÜtô*Ifb›Tiàçqè°rH¾‰_È{ÛÈDú\¾¡k¸qMÎP<OãHÈh/zPÌêv¦àè£?ɱ…§pz»õZý'J>%—SûúT•~:Ñ„å‡Pn† ×Z ·¹G“ë<-9 ƒÎIR¿yßáLÕ0¬ S£•2Í ½(òׯÆ'úJôáö$âär fn´BE6hVæß2£Ð;\›®ØŸco$€øã©XõqrŽO¨‚¥ŠG…Û*kë}ïAYøHX8ÈiÚwØQX&tiœ'?N˜ÓêmDGc5ªÝ ¦ËÆòý]_ÙõÃÀz×BjæxRËát[*ú¦ÀÊá”{œ×dß»³xä¦qð¶o&ܵ#ˆYúö+k‚ý0CÚ˜Ø ¥×ûq´¥&ÕÝžS«ïþ kOzÀÒÙXlDÿ6ß$§¥üÐoÈafóø®ž†–j2 Àc¸ršÖûCKàY|ÒÖrâI1LjڕKÁ©/ØsøìZ>…äN‘÷Yèqþ6ފ͆ ÞXçñP²¶ýGmg3¥æÛз{;?¶`#ðŠÝ˜×Qs‡Ó•;æ5–¦SÒs„y·ëÑ|BŸ0ÌØ­–&¢ÂA¼¼WHžG;bº¸+>šØD âsñÚµ<ôŸkÕ̹p8ˆÁ%¶KpâÍöš{¢ô`yî8~\ÝN`Ÿ°¥° ¯ñðùÑrZ=û+}aÃcÖŸöHêqðØõœ“âÖCÕŠ Y%ÅöÝ–€„óéÌÎ긠Âj4gãÉ»wØ).y¡õõazÌ X¹Y²2d©çÏ@Ú# M9 —0"ü£ˆÒjrðCJ mµfRIa Àwôe˜´¤~¶†bæ”H,=7~ô™Ì ;äqÊ£_0W£_h;S·)aèãÚUëuR‡Zx€gó0ÚR{àÖN6÷—©Ð'•o"pkQ‰à‹Ê0\vEŠŽü™OØü›OÈðÅÛ¹Aï ä%{aNáqü"Hröjìï±´ÂIø8§}êf¯ÒÉÇÈ€µÂ9fd$‹>ƒUÍc>¾‘Ò—ôÕß×»,Üfb‰éIx1dêOùoÚ-ñŸÙWÜòo4éÄÏ'NaèŽd,[‚:5íl å¤`£òköµÍˆXXž%±¿~™÷@ÿ„ÊçbÀ>Z”ÛïáÝצq¯½ó!´[&z>÷¡_&y ;S5éH#Ø òyQlÌ`.V ü‡ú—ýXK4­©…X¹2H:¨Ì·¬–ÄUÎÉ8ÝgÝø¶g©‰ò>#;€ßO“ÉŸqGP‘lƹÁ¬Äë=FÎÜÃ×ÜYüž£èý9–4!êÍ£ù¸À3p*(ñÜ$~ðo{è>–²æâRjïLÇ£?$éÍu  œdbk+±Qâ2ûº^œxÊIÂkR0b+{Rz–¡Î·J=¡×°•ìJá ÿ#ŠšF¿ªÜ´Iyë©^AqžY‡u1]äÙÑ4ˆÔ¶DýŒ1TÉN„wÅH _ÿ™?‰(µ—‘§#|2HÅ1+î»ï˜ó¿Ê>öF)£òç Js+Î%à×38z¾šõT¶ ¿ú$x¾d*­œß$Èyµú‡ÿ@ñ­Í(¬NåJ.aø9µƒø”ù ÈÖ rã˜ÐòŒkÒ„Â:`âÚHüpÏ Åª*{'ÐWÊCøFÖ‡.6fïÇað‘Ôx‰¼’>Çä®D€›ß l¯=~üƒ›3G@ÙÔœ´ý4¤fÖÿÍ„Ý_â±/ ƒ’“ÑýÎ4 ÷Yź>ÞÂŒ›wAÿu%„_£v›Aäa!,nfÐn±µÒðøš%PT®ìPbþ>˜þ«GÿÆe~ïËC¾Å—Ѩt­T2ÈÓ¯vh}A GµýeëoHQ?O]L™1ŸþsÏC^®#‹¥= ÁQÞß:¶·–³\ ¯Ž’áuì¶·4J´:ò+.ÇÀõÊš³ÿ2ùY»—Œ™Îó­40²ß ³bØãjP²ù3¼yýšLwåÖ:@†ÌÆô£0Pd 'Êõ òîSÒ´» Š%gà­ãÊô‰ÓZVZ·5åžÑ,¶Xú>j©œækíóéÁ#ùƒÓ/YÔ±\úìr˜P7õ*웜€σñ“ò.^’¤Ä“~ùóú°Ø!JøÛ*âKnÃÒQãéг‰|âí‡üˆ×,mMaq_GÐËíüä;{2˪ü&“åŽÞTöÙ"±C›Þlµá»€ß~cÆ_I¨Æ=õRètÍPhà§C»z }e^Š7å7^æúCøÐì°~¾ ]¨Þ3¾ k>ÄÍËè` =ï%E{g˜Ò¯öÕ |“•UKqÇñ“ø«sY­á)žœÓÉëåŒ9- Hì;!ıEȇçìÙ î˜:è=ƒKX1Ûõy(íÙà ï#,hZ’&ßóÑ–ÖU] íõølƒ>˜ïž–MëXø,}¾}c;Ÿ`Ë3ìäh}å8H8,¯«„d…uiSå—6L†S…2Tújytp!õœ¹N+p\@{Ï‚¨þñ±†™.†r½áxà \0OŸÊÊèñÓvG`dÓ|(9–”^Tä×Ô¯2¥”úëÆÅ»BÁÙðƼÏ冢ö­Í¾uĘ HZⲳ뤘[¥j#¯šd¨¾ÉvòÎU”%Ëâ ·¶3÷w¯ðG,í».¼xW›z·Yñ3£ÇÓ>çÄA-=ZCxžÈ\¾î÷ý]ŠïÊKðr„<¿›™–öÃÃÙynÌ vSÓ€­v›¯¿„}¡zðó„'Ø:Ÿ¬>6d;Âp˜÷AöÙthÒôÇÐuo˜§õN&²g+ 8ŠÓYmðl{ÉÎ…«™ò(~Òž'u-…YpÔ’ÚuÇ×PÿûØóÈw'Ï`û5JYŠü>üÙaCóö¥0‹úp¾ùøAºL}﹩Bô–âþeËaŒÿø²?¿(ö¢æÇÍÔ_´ {m´P)æñžî "ëÀ›¼Õùû£XüVp®k8,g—¦ Ê»B„}C‡à)¯7ÂjÏ`êi|”ØV®´œI¢.­£1Ħ’ý;ðÖ¶ûLît![•à-ømJ@o¦,ÚÉÖ⨻átƒðNŸø¥ôV²†€Épv¿6µ™‹!¡ëˆý>ˆ©sÇR ÑlŒSšú«R Ò|« °^Kx ìqTÿ¡Çó È«¯Z´òÅ ‡ä¸Û’XX2ì5L;Á„ß=0s”2ŸÚ7…º´p‰ì Lwê²×W‰ïÒ£ô{$”ßSÇÆ 0{C5hA h®@uf*ºeŸ$î:ôK\.4‹òÕ!s¸Ñ1úXaĨ ¡ÒøK˜d9„ojM®ÊU€äz{Á8—‰ôc%fü6 [ÿy㢫Ťÿ֢韩½‹yS5ÒeCàý°D*¢[ØQ%V»wéRT¬É$»q¹§§\3à:øë?ùsëCÄzê8t÷ÙSô Ç}‘†Ñê`tçXé”`Ç0¾äh tB„Š…˜"©¯Öà‚/¥lóнüåžQt÷ºý¥º¾y.†£±a¬‘/,¾ÝHò°1á'Ü›ô†Ï´ˆÛL¬¿jÂ#«cQTT‹ÿš»˜],@ÝWECtr 8JígÍ6Ò\ï” ^ì²æ¯Ö‰Á²í>ÂÓç×±ù·Š°ºÄ“ì3y C ¹»(îJ‡g~Æ)g’!Ë o ç° e¥ž±}§Vá°º&÷Ú”ß1àÂ!A(ùý§PgÙ'˜Q# ›\ŸJ€É ^ ë½^(ðŠVÏËá¨d"6º¿c’ßw1™·`¾è*zàÂX´;²‰~úêÆJ#ãt÷ë6Ò—6”†—?^`Wý5l:üý3íèψ§ÌrÁyT—‚ê/Ȳ\ëÃ3xsz"ÿçSOž¬Ã}c*ñW›.¾R ÞÎç ú<顲Ù|ñn3zcÁfz°ð2Ëåà㉵r0í÷Hº% R7EÒ§öãheiÆ?› röz°u³6̬­Äkëž;·Š•³÷‰¸EW“ûþÇ¿ÊÛbñ¼0"n ßÒ:Kàå1ÿ(σmñÝXcUHï…dz=_Ÿ’ƒçÍ¡Òå‚°Þ8Ñùáõ¥XôY¨¿IŽ‘äâžG…š÷üPz—(Ýæ6™…â1Aß7l®?ŽykqÝÛ$ôûÚrj#~1?È;»4hà¡Ï(6ñD¬B¶ü{ ‘*ÉÅkÕt‹èbAþ®Ï¬øVÏñœEú'©£Ó©k€2I˜â¦_–Ьk…›Lhyñ(N .‰S’´„‹Žíž_×BƸ?@¯šÕ´QO]x*_ÚÆ÷f×ÈêÖì¡ãh¼à’ÀöET WþLîNé€6˜/|?/~lVàU^¯ð»ö ´Ï$¹{/b!ÙMDñÚ§r<ðM“—Ô·89ý‹±½/ðû4ºPº´V÷À76Ã^çðùk^£òÞ5|µ¢$á?£<›áF›+ÕN3ä>‡Ÿ½ P»* åÖL¦‘“·ÑÔ)WqÑ Nì,Ø £ØåÓ†<ƒ‡ò¾é»`ËÌ_X S2ë`Ú®¢éÒ6P>ÙÆîÃŽŸ7pTð/X1w¬ /ÅÜ‚8¸9¦5* Lº§ƒñÕ)T7ìsܸ~¼ål08æHðõM Ëw¹ò]aŠtÒìTžtv<.­é£:™ÜäÈBºíïf˜Y ïnNç~¡ T®R JÚÑaqàíHZ­ü TôU{c†R‰(9ÊæDýåÔ®ÂÈ_wánÆž¾«šŒðþ€_Çó× 7ò¶ºUðhYüH ‚Ð{á䯨 Iø© ñ+õ™ÏNW(°õ¿×̳eëaÝÎåÜØñu8OŒsBj½î³7c¤°·µ”õ*€þJC®¨]AÒøÀQWdÍúOL±"vîÍ€ÚÉm(6çÜÿdÍ6Ëhó“Ë0³ºÓ¿öAñ¦·¨ ºAP³EÙ)Oí­ý0Eýn´%Ï*V‰NÑÆ÷AóÉtP ±¡É.²´cÉàÞ/Ũê±pû‘ ŸÁ&.ìeJÿ†ÓxËi`a}ŸœÒs«}%2"y½85ÇÍ·3 {W>çF(¿Šæ!K!&¨ <9Ëى纪I|–L*›Fë¯ÏC»û_QŸŠ×^O¥ŽÇ³Àèn;F®bøã©"­éÞ<ÇFö²ÝðXr ú.ÎCnä ¿U6Dÿa9¯·Â¦Ó¦Tm}èÄYrŒ.zQ‰Í¸Oq|€—¥qõâ2”_¦Í“Æ2±1§@νŠI“w€mÖüªÎ0ЫÅßñˆk}ÅiÉ…‹tá¯>ìv[à &ðúÏ µ!Í©Ôa+"Ý Ëiìtî¶#…V»+¢lÔ"ºÇ”ñ"I)îQ¥„Ò’ñÆíBXêØÊ9ò;ÃÕ¹Yfø%ÁÆ®¿¢y_öVáŸÄ,̈`²ìèJüTzŽ4ãû9Uà8KÜŽõæÑBo­\¬Û'ÅۯПËԟλ 4œ÷®½ 𭍉E Jú†O_}dÍåHÁð‘º<£R– V„G[NQOåðbŠ ®~ªÇ|;Côß îÚHÓ¯3ªq÷.œoÚÿûo³€×™YÐ9÷÷‘õvÕK ©e–€ûôÕâúãÍè8% %ߪC¨VƒÍ-ëenâ|Ï)M>tº ÕÚmÉÖ1M:Pû¥©ž –¢Â–·ÁG÷ ú”4ÀÂÈ2ˆÜÀã&¼ÅÃOµIAª4~UÆQ†ñsê@kýø¯ µ‡!ðuÈs¢tư.¦PZ %wf¡¨†5‘+Žo»J¡…h?d…ư€?ýXÿÁŽz®òÀBÅçðj†&›§ÇÝõ‘çæ1,9á &ˆ|ÀY]#¹ãަŸ°…Z"•G,ø_ÅLéÉ¢µïUùÊÄÓøtòâAl–§ð«Œ¶=ãZüù½—¢dBY>ö«¬¤µÙ–bï…ò—¨¢º]cXÁ‚ãt³÷ApwZÃE»2ÑHÔŒ¾¥É;?tW¦Š´¨8N„Í'•ç•ùø ˜]yÂ>Yâ„Ö%üû¹²@t -+¯#bòYp)Tóÿ6€ÇEQ>ì€Ýá­Ë{‡`Ù”Éxµ×…šÍ¿KÆ2!þ¨B{/ž1f+˜†Áê~«ÚÆ[yµ¨2sv ÿ6™þº; óEÃÆ*8úg2ÊF˜ðÖdUj qÕš“ŸW[HêŽ6\;ƈÏËŽ¡Ã ]x»õzUi¢†ÛÞ2*á¯ï–¡çÔ*Ú`áÌWÜ<Âç_+ä­‡xΜã44C;óöó}Ç‚yÕJ}YšÉ[4·ûâèOÓ¨’^ʘ(ò¾œßló|ú¶ÉçUÆPg]ÎTLâÛ¶£¹s&®¡ôü÷VLÚ#ÜKgÕ^öKƒü-.KéÜëwP"ÑZîP¦wÞ §×aéÀ¼9JVÍufGÄ ùzG]øë ÊîZHóÏÅœ?hô2Ýü¶“I  WS Í"ê²xøÖ™à•dÂï$Áó]ª¸9þ¾˜Ò>½l¦õtZ~mG겊ũ´¤Iœê¾þ!»"à}m ÊåÄáä)û`xÏŸ}—̆ ¹·Ì CÈ¥æ¶ÚÝ߉|Ø>èÈ)Á—\绀tw§ª'e¨ÒX¾é ƒKU´:&ÂÓZ†ÒŸå'àé•…0ÿp3t~X%ô2óäû5Cÿ©'pˆzÐÔ¥f°¥Ú”[ÿ´ +äγ۟Ó!åœ<_/EÍM…0z/èêðec©ªƒ!Û‡ri…h=ú^ùj3FÁßO.|ö ºzòdج Ç ƒß!îpÒpŒÀób˜OBSºHX @³óí[C§¼£ &Wðï+c°ßwˆ†ïÑ¥÷~?}91º£X…&mÿâ,î6\S¾à?¯DðÏßB·ýÅ>̘kŽç¥?‚µˆŸÚìO§+  S!äÉxƒã!üÇ/<{†Ðæ%±÷üâï˜ú©ãöZ‘—g[1ø´!\ÏR¡ëc¦s‰Þ^\e¶Ö'Œ¤õ'aþJk*°ŽK~^ÉZgq…äavsq8^uû™ÊŸQý–QM’W:÷—–£ÃÔ¸‡b0Žº,CÓÇKÑ%e t‘ø^¸”gÀV ? ¿=´‰KÌX>õþZ>ë7_ëHÅŘ;Ç•Äw(³_tQzã(4;Ö ®C Áõk–Ñý†÷ÊiÕsa¢I™Àøð>èY|Í+œI§ß›FîúãÚ€bE™à¸"Û V{ ð`¦ ?ºº¿KЃ–®µm@îŸqdÌÐøš¦[‚¾‚ábgzyEÇØ$´³4¡>=cãSÈ„-‰B³Êd°½Ó‹ªUSÁå'#?ÂÕ ö¢µ®NGºê;y¼KÕ)Êuh9¦a²øœ5ñ¡ÔÇöD]%O²¿f© ÑDûqæíòÁ=-‡ðv*÷>éE%–Ûðæ4kø(š¸ÝŽþ0ÓÂlé.èž©•§¦ðÐÕŸáqT8_þ±:WIñ¡Edï«Õ,€k™…=Åé¥ç{èæ‘¿ÙÞ+a|ò’XܰòkÍËÆØ"I~ö¦/ ûDä›R¡ìõl|¼wKñ%þéªßéX¶m4=ð`úùUÄþÚ·Ø‹[kÉR1šZ”žâ‘)ÍDØQ §­@Gâ¸ûmæã$ ¸ß”Þ#¡Û„S€Î4åQˆ5õŠÜèç®7ï@?ÆâgªÑ÷ÉÛ!³—^”›ï¸ÂžZ<"ù_CùDŸ]tI~i9(Çí׳ͅËa6S€g½¦à#½;±E3í5Ò€miÍxéÛLºÂ_™>Ë–„K<¸¢ìp~gØØä­@{ÎêÑCCÁš=¯°l¾·tt¥Õ-Aü?ò6×ÑÈÝ$°Îÿ0÷Z+KÆ3 n9Öõ¿Ð†ÞÇñ²[áÌH ˆ1_…ÕOÆh^Åaá´Â÷8r³U¾«ýi¡BÏlk…e..*A¬¦~‚ßâ¼¾w,þçµÙ¯ÁµëH¶a¶½þGv áÀÅå8c¾®¿q„¼{SÃCÎÙB|£äÆjjBw"ºjlgBUÙhAµÆØjMoù<ÆÈâËÐ’G–Ò1s,¸è;'zMĆït‡Ï¾¶à•H<þynÚ¾‘h†4!ÕoÇä]ÓÀÏp€-ÐÅîø¢agÃŽ°»Õ>¸-KŒ»Ét‘JýlP·v¤X¸ Ó:f2µGѬ/°ÿËú§"ŽÞ6›XøRèk&F»ŸÞ©{ÀðŒ%¼és…÷ëoÁ¦Qé‚÷öyÄçõ¤Ïð匡´üÚL€å˜¿À ÂÓÂ`í9+~Ç!N`ùL'Fã”ȳB™·[p ^ƒz9ÙR‰óBh~%N_nÚ Þ*ðÜe«ÀBj¥ßã–ô#|ƒ{Í O99^´\’Ǿr©ý‘õ}õè^âÑšo ¦.û|¶9U½Š#ŸÌûîÞ9‘õE¥‘ü2)¼e4uîÈÓkm(½ï &> ‚®ß³xõø“ÂÍ«Di–8¦YÛáVÙ-\õžœRÊÆ·Â+³?`¶OšÅÏêÆè$½ª·]‚±¿ñçÝ7äÁ£¸f±2zð½k-9|'.½WàïŠ)Uj|s—ØQ“ì3dï§xpÛ'vÓÖŽ_ð+…ïê¼9b<Þ5\ˆÞÓ? )€…3QÒR ƒ&•c¡N9ú½ãÝãpl—&?âìI­B‘ÎßÏšï¼ÀÈ\c\¨F›~Å»¢ ¬l]:ôfÃi:Tåf4íIÁ—wN¢Üìæ¯kŽú- \``ÍÉvÜ Ð Ë6¡’–‘ì~ÜzpHÂp4¿žµn¸¨@¡A0®º] ý%`áP€]›Ö ééNv«Ä¶ž¨"wÛÆ€Ãúè%ؽì®ðøl J¥0¤ÚJ½ÁmÞë…ï»â0÷5úZƒ{mI@eùEDÊ?A¸þüU¶k¡:Mk~z3÷ñˆŽ8Ͷ-¯¨PÓ3É$Ld ™4N ýQ¡Ú'ðæíTŒUyÞzéG©I|׿|Ô[4œ»ï.e^–ñ0¤ œ>·}‚»ú’aÇÝÚeqÎsq(>\ ¾ca].¾kÅŽhcùŒt¸ÛcÌ-Ãè+·Á1K)°š¡#ðûÄYô˜ì$zó¶:ì:⥯±àg Víz‰_üFûÏÚx¤uütÇ“ÅÐzR‘Î|vNM]Æ—4¸€û¼Nèôz3‡>Ä©:„)¦‡Q_¥N¼ãß;í5û¶mž†14EBHÖž“‡—ñº´¹N‘|q  Å6Æ4ªºVKÇAÄBÖ¾:‹jÉ™»7ÁÉÈ^.œŸæs¨=²ã q•ÄkTý‘JŸ‹sº³ÜÄ»á[ÓMÜÔ-*'Hÿ4…/'ù ÙôáS‰Ü€íÜàÇÒê ¡ä’?@sV¯å¯Uð¤ìî>îœZ´:ïœÇmon Õtc!EUƒ–™HQK×HhÞD¢k3!ÿ^—b}óp’ºÕÚ°µÄÔø6I5ê3u6½a(ëôQ¸Iþ$Ž®¸ *—`àv2î™ñŽÔš I†¾&=<&,Q d`#ŒÞŒ1 —ØKy’jw—Õé+òç¶A‡J&©|Ü q­,5hg?,7ÖÃ]Äš®håI//ÃÇ;vôbïPº±j$U©@ölqˆ“U»dÑQX´N?4T`dÝ> £Çëâü£ñðb\DüÎÆÿüÈsª£ðÓ’ 8íŽ =‹ùÌ¢˜¬ËÙ¾%"úʘh§Ý}®‡œóͽ6Âb:ðã™ ~2ÇbÍ÷d­¸ $+,ƒxúaÀæìÛÊåcÑèò\šôÜníÑàj" ¹ä˜$6;B’’OÃàsì8èÏG<ž»›½2{öÛ7BOô˜þç¬ÚÏÔ¾©Â»Ú"˜ÝÓoÙcgƒ¬œ.Î5+mqŠÞH®éé ÝÓp“ŽdÁÄï ä…ãLñ{•åΧ;ΓíP^ú¾°A·¾’àjÆRT¡bùÙç¼h¡ñ~ƒRDåA•=2`¡Æ¡8p-„àpC´“jÄ™ÁùÁVìÿ'„Æ=xñKç8Â"qiêql¨éîµÄRÆæ r.‹j¶Vx‡¼kÓû'à±ê!öyX9Þ~ì–¯§@àuì›O¶ÛŠCQ€}SþˆHîÞ›¥_ÀÜ‚(¶]ãyö~<Ý;û0~ðXÇú’¨îEzFÇ„˜üôb12xcÕ-6ëóm¶0,!u&ÏÓ¬•¼²è™¬‹Ã®§üZì h©Ââjp"9J:K2Yb \7q&ŸKNzŽI¹é`š(€rÇóäXòo2nÅ}O·à›­Ð-YØVY€™ïUñf˜ x)ƒà»Î¾.u$LvöâÏ[+ dS.÷ÕFñ¬PHªýÁÜì€ÿÆéy­ºÜgåöz ¬^7%»0¥à κ¸/4ÔbHþ¼‘U­‹aÌô2ÁüK[ÉöœÜ32»_,ìÒ˜À—ˆ¡ûO;0µ‰„´‰ì@‹*ý´òyp”òà áRŸ ß3²®Ýñ‚~žÃ¯YüÄw Ï =ÎVî"àpáþ» ÓÆqŸ/&Ô°Q„}´ÜO=[ j 3úaÜlð8–/¸‡i‡ÁÛOðivmðõ`69äà?—p_Á{¦¡á}°õtÆ6ÍQäÎÁ¸÷×+ÜÒ)ÂC–Ë┉·Al–¾¾é'˜gøJ°O6ŒtQ¦rp93Ëö"6VVޝÞáGG|Å™iºÜ0h'ŠÍ¼ÿÜ 1pLÞ’M”äÓ…%r<®ä*ìÝâƒ:)ûã/w+t®aÒ3_™Þˆ>6-3å|êØ;‘žÁ~TQ[¥ Ön•¢g”NJ!kþv¯*_±«“ÕÞú…ÏÚòÀo²$]²"¸Ý‚÷uWÐ 5f|¨a§pqb$U?úÆùÒS1ž5y!¼Y½ÔÅ™Áôy'ȸù[Ø,µP0>µ?‰Þ ÝÇÇCHËK´4âûÂÃìGNn@FMh‰¡ñìû¹v8÷:/ӱ范ã‘DÉh9*+GœÙâe¯Ë$Òû¸úéÀo¯ûP”}*dŸb­Bóiºõƒ8w]¬K EØÝG]‹Ÿ[q”¢æ`IÇé¿ îOT!‡Ãq¬¸ŽÀ<¶„,¡ªØ±b1¼u§Ò?ÞÂcYy2™ÈNí§mrøÜ]š‡J†uSÌiQn1(ι ñ¹æ¼p‹=Äc÷²P³O’Öȱ«“¡qU J·Ž‚-;ÔÐÔ<›}­£GçÛ37Mj¡)ÇCf¬çŸÄĸš÷õÚ»MÉøkÔ}¼v®âT?Àƒ¡¯ åÝ5§ý3Äxñîeð¸z9[ÄT‹;Œ}ê†3òg¡þF6|; —6¸ÒðËwpÄb3*½m J=w`7¬š0iØ}ÜWz tZ$ék[yXëÄ ÍÞŠú 9Í–$ŽsÉDòާ–YºNn?M«Yé›4}fC²>^ejO-Á×= ¯î7äº5Kè–;qh}&ÚÒíÎ"TI¼GSq8˜svO ÇÞÚP]%Ê«ë^’út‡ÄâI÷Ìüê/a÷¶b”»y n|ÉÁ¹ŽE g+³nááEÙÎi“Kq¿î#“0:3¡nƾ#ð29©~l4áŸ\¼aEÊŸšW,”«­ ,/¸á)‡G‚¨?æÌšV×§ÃHµv|¶$€…<žD¾yô>H÷AvI½`ƾffzM÷ÛŒ“Ôø ’ˆŽ« !ÇË€ßk©ÇTi¹Å3È?…¤vvßÑB ÛóЮ1z®Mä#û ±àb|ÓMºtðë¡ gÛJªîÊCëÈxæ³Ï ¯XÎã¯Ï£²Ö-òrÓTܦÊü‡Da¶ÍP¼ý{¾P«º?zÉ´Öõ²¼oWàþ‡;¨éq ?ê6ƒGa¤³~éðÉÁ[+z~+AÿVkö1¤g~ì„mwv˜_ç Ùƒ¨¼ø ¶2ç§žmÀ¦°Ç,Ô¾jƒ·Îh/ôÂ/7Gð§Z’˜´Å'ÕYÐ×Î’ðÌà}™ ýšÕµm‚¼Ý»pëñpï’‹êáçEútf¿„ÜÐ寵ƒ‰ï!;ü[—'UO¢ª.¶D)"vÞ•ã-F¥à{û[• î]Do8ΠfÆwäÂ…¦´ÿbƒÅÇó¸, Z¢Å/=ûêÌ—¬‚ÂV%Ô·nE¬B¼µÙˆ/iÿ3ÈùJ4|¨>4‰RÉ=‡@wlÍÿ‰ž‘Å,YA.ißÇz^ë¹³œ¥øÒUO,0A-‡]ÆáŸÕ|QK„äúó"aŠðì$I<ÑàÈw»ÍG›âÜüÇ^|}釳ùØ¥´OÄ66ZÐÔM2tܰ]`¡áG݃ô²C5ª^ÛÄn?ñXuøvõš<ñÂ?&Ø&;Eåi\‘·øjÍK·îƒ½+Îà²@e¸XØ;’ùKò Ë>ÐË?In/ÅõRN“†ÇÏp¼ÏSüõXšÊ¥ÄA±úXø´ÉÈieááëiYÐpܤÄÛÆÂ;qX ïŽÞr©. ×ä*C”Èï5#ùÛÀ2I‘dØqqµHƒE•³šôê±k¬çߔܴ M–îæ‡Gßd:³ !:|3ØÝù„š4Ž‘ù$h|k ï•ñ«Ê2a¿é°›>YðÍÀžËo¾ ›æPAåÛBÜ&BS3„÷m€G=q %ê×z¢UY±ZzÃúÁç_§ zoGñ²rWXºe;£v­ÏŠÑS'ÐÅ57ûiqËÑûqþ¿/Bk¯½dÿ7i¼¨–ƒÚìZ] Æ^ðÒ¥ßP5è*s˜ónM[²ÐŸÀÅÕ:tÎc®’¼ ’ÃÎáv]ê'NNþÕCÍŽ#àÏ_cÓ÷°*Ä Jðéˆ7 þ`!ªÓ¦»O Æ,–Gœ>Ýy¼Ú"Ø3Ç/ ÿ1‡,R€ñ¹£èÂ/ªÔu¢ÍQÆyç^Û¡³ù±òþD²`ßw‚crîôŽ›#V¼5 ­í¦ôô.Äß2™\cƒ ݸ˼•éõ0¶dºÐo+€ô¨o#&{ÝC³\{ºÕ’/ÿæ ë¢r!¨>/ì{ ~õTÅk!Åø!C zò«²€J&/æ1[»Ú…øäg'>ÊÅÊ­5èѧ.Ø&½ U‚Püê âikÉ÷†ZRÍÜ­4õŸ+È»"Œ¾r?[t¡ÈɽpÃäÉ«\b/5¹Û¥§Lʼ]mÏ’ôÖlÌ— à?»µñBU„8›ñ…ísÉУ?¿â¾%çpMËÊ1kÕ+”¿¼oY Í»±þñ ¹û¦îÏã[P.vPên³ÀÞX‰ò0w!jM·áÛ§Ir„4ÐÝEó?ÿ®ý‰™â+¦8ª” ke³? ôˆò)Ôœ E‹7y` vöܱ¦V·&À›“ ú` 7BÊÑj×d‹qg×qx JÍ/5á®Æ4nÞ'’î_Bû¦3&­`gÝdhþ‡å|‚ø5‰¾ïÍxú¡,¬þ¾šúú¡[õ;ñÔ€/÷-€»â âíNÒ^öýÅ8rSŒ{o“æóó‡óñEÑèk°“·PòÔ9Ì ¦×{ŒhyöVÒ16Ý]„Ž÷\©[ùU\õS‰É©pü9ëÜl‹À{¡\É¿ 6yPþôå:pßQÀ{ô73’÷QhPþŒÍ­Û"ï\èÅ a51Ç·ð{]ê\ïß~~³¦•Œ^¢‚ÆT¼{“á›kï˜wú(>Í4÷\ÖùÏ[Íž©ÅÂE‡«ø¼ß›Ìɧ›ÔòCn°Ùã˜vö²ä%ìU¶Ý1”WG” vlØ‹îò9ð.1 ~7¥vI CVàÌϼèçL1F\m¸6<Û.Û+áVVîV'`}¶ MqTá.m7á͹ÌYyw?ýJ2—ëóî6Øoð |‹ï1­­æt~ŠíÎ-½ü›]A:‚WóÅbí‚ßd6È…šÒfï*´|/?‡'ýËüºIû=úí`þўŽÓ]I¬wiµŠ‡OýEøîö ütà€ß\–ºHœ«…¿wo ¤ç×FˆzCÓ¡Zøed¥õ ã†8äÿÕ¦-£þ‚ý¶v'¹WM»ÿM‚¥º†¶‘¤GîäÛãé ¹_…fdƒäSÊ•”žÁ”¯10Cõš0ÝU–:WÊPÕ›èTs¯O æûGìbÖá ˆÈµ¡`~%@ðp¾&Oy³…ß8‡¤ÇŠR¹ìªÇåµ)ßÚ…©‡ã¡×ÙþÕÁpHÁ üÞ‚Äòià`ô˜È-[ɬ¥»HÈ$!þ ] ¿þÞÔŸÂ3òn;R8f®Í—ÛÁ<õ%®6€Æ/¿ÐHMÜåñYìx:¼^„ŒèTF_WQýP×ý±“±o­ }ÞOôëàá›B»MoTÆáõZ2÷w‘€ñLvò1¡æÕëøÏˆ%XFvã6©Sèg§Ïd‰£U ¦Í=PSCœÀÿ¤ä ­ùš7oŽ?T|à gÛÐÿú Ôü躡ïÈè‹§ØÑÇU(øÖ¤û³`¤ÛQ"÷ì^¼¹:у˜·,„äºù8ŒHm€êGjíèÛ­Æ\ÛC\\=gܧ/þjÑê+Fuâ†ÈFt}®…ŸK áûHîv<[N[ñÄ'jœ†UAú`~³²˜ƒŸ »ïø`õ"lš´ E/Î+ˆÄ«./È…'ÙÌk²‚óu‹a‡ö}AíÇ<¼°`4´ëPŸsà_Œ%lËi%ö‡Õ1eÅYXé)NŸ¯¶BžX<øÍf·ÃÞ -Â')9ï2o´4AMY78öܬº½:¬5ˆÏúìºpUh b\C%ŠÄ> e&©àwç\nNqÏ£lÙmr21Ÿe7“îMa‚©ƒ|>ÊÝß{Ï‚Žcž¾™\‡›e7Á¸Í07í<8g³Œã‚Œ¡­`¬„rÅŽZMZÉë4v 3¬À®¾Q´+† _—¢hô[ÔxÉù,; ’V€k¯§ã±æSt®Þ0L½u\°úÇ[4pñçƯ…C*¡ýË*¸>ü¼h™ËÆÍ0 É;ÇÓÚõñÉëë༡v¨²¾¾»0ïï]È››‡^dß–€tfDÖŽã³+v’Ä‚¡Ô7¶…sÈùM£™Œö)ð™y&¾{ >ZnäcÌvºæß |[¿–- ÇQF¤Ç÷øB¥+æè…¹¯æüþñLÎÁتb2õq =UeGîÛÊðÒ™]è°õ=¬¹S‡¾£Í¹êÛÜ|˜Òo§s©zì ¾êôÆGÈÁ\ã¥`!{ W½YGgˆd‚A /ÌUŸÌï}§àiÔÉÆ‹lqþÙi1¤}Awιƒ…V£ò©Fœw`6u—.É_ËЭiŠ|¾ GÍ…„Dj¨aZsè¹? PôL圄º€‚ ÃÐgf<·ÏKÿ(†;6“0b_nzøkï]Å›Çbù‹[xæîC¾šiòˆÒ­ôÞîKL´`ÑÜ@P7O£ï|ÝèüÖçdöéEpÐ6›Jd¨p½oGᆳ›ú G‹ÙÑÔWÞÔñß4ß°•®ñ¬À¥{0¢f½Ùb“J¬é†/JÔ±#—}ylKÓÛ éÖ§“Aü$2ø±{2•)/³„+#Ô¸NàköõÌm¼ÙëKK‚œ©iÀDü~`]ùÍŒ«ÏÏÝKvðåpø¤<,«ÍÅ~WQ¾¸O‚yGå¾ÛpÇeíl¥X8¦uo…›C‡ÐWkðÈ‘Cÿ`BÆC¸_cÊÝ®ù Mê“Ô„`µ¶»ÆÐ¢Ú¯Ø°ø¨hÈñgËOÁé_m°yÿ ¼Ð†Y:Yàª6 ¤3ASóYXÖzö¬ª‰37j÷mï„ Aïaé¹a¨×•†œËq`­<Î Šž¬wD¢ðï?tù²–~öÿF`Ù8ž2}>¾ü Ûn/‡+küûE”¯ý$K›» X”“ÅmÕ¨÷è^"AÅžŒ¯)úá¤w<¹Zb9[†lÚ„`–qU‡ÆÕëqëo¯`RÞL¾Fo¿Ø>šm.Ä©ÆwHxµ8?|b¡À±ð(4$ïÀÖýŒÌ¹# r‰ëâŸxSt1õxÌÊ óåüü¹õ(<àÑB4Ç,ÄZ¶xþV<Þ=4 È®Å02B8ëDÙ­7!°ÈNŸ$† ®þ½íõ‰ðnèèIí og\o"21ƒðº ‹Fª³vñ°ŽqÜ®íÈ^ÈEñÙ^ iÊÖI…£”Ä+\=c[Ö^s§e½_—I—ísä&…ñÓsÈOq:ÃÀ™¿/ˆ¤½Š¬iÖmî³BŠ^í>x+›Ï>ÖŠ?ãŒøæVKªq–WÔj TÖþê¤ôì£ÜàÀ,0þœÎgrÛ¯ƒ¼U{7 ϯk‘FtÛü\Ül†5ëti@,_T3‰ËèØscËxìHÜÍ<«yÐÓQüû!:¼¡¶e$·*åØÖ“ì| ÜŸÄgüH ûµ¸Á-¾´·"ÇÂ%×k0?ñ¦`ٟ̈¡W_À†MrØé+ Áy~‚­­8"@†$+´3Ýk;ñýÇnr*B‡Ÿ“¥G4°±£·½Aå¯W`Ó%Q.ö3œökTw‡í ëÅIó PëÞÖ6úÌ5Ûºß6c¶á-ìêQ¨pÄð²b†šŸñøÑÙ|`Åeö ~ |ìÁ¸N*¹7’<Ù-ÍSÈ]ôh¥«§ ¥E·ŽÃ¯ hÓ‘˜2~³­Òuð¾xˆN¸u ½ÌsÙFêþé2×áwê5j÷Ê}Ľ!…£gIÑ]Û]éîñ*쥨Ηe¯†8ÁÞ[Ið•ýFÛÎ)tÓ\y°ôÀ"jü£ž=Ôí!ãyÅËHÑ‘£Ó~†sÅö=ÏŽÔwZBžè/ll{ Ç’+„½WÑùÃYܬ¢@­OWò1%k_, È8G4õ?c¯Á7\8w{âCò/WÂ:‰øÛSMè…‘`”fFÃËÆóÃÇÓI…fÚçûaÎÜ:¬<¯L?[)ðfÝcØg¡ÇïíãB•0eÞGÒqe)Oœ¼ß®z/¸k²œòê#xóÙ?íÊ„‹Ø*3ã2[ZxU‰ÒŽ<–Û sŽÿ€ƒ²ø­Ey¸Ý$* áFÀdšnÐŒ…"é¸oÚ´©!@-ÃM%ÃagÐqb’Ÿˆ.¥yä :ÎÁœßîÝ͉ú|ð‹(_ çwÓGs±a&|Æ,®ý1”ß³ªEKÓ¼³W•êÏYið eŽä—Vž W~žàå_ÅñÕµ@˜üä³ÐløqºÔùR~îÕâQësøøˆX!3ñC§Î¾Þ¿ 'x\Àw"`é&=š®èFWö=áŽÖ—øT”¼’UM†ü0áwŸÁÞ¶tªG6ÆÍ©ÆÓaüO±.½6W•úÞ£ØiÇ%+’é"ß(¾¯G¿Ù‚ª/L¹Qz5,_PH+tî wË>)oª­‰ÂIs^Òw®p²¯ž®¯S¦µ× ‹ñ/»ó{áSèù'Á|Æ÷¢'”³‹æ—EkéÒC‹ø[ÕÜòÚq¬è8 fÁ”LÆ‹ãžÀ°5©L¸ð‘óVX<Âmnì¸pÄ¿¢4«Q.G…Á¶v(Ÿ‡K+XFh:¾ÛbDÕ¿-§Êã#¦ æË0kø›üçAî=ð.¶€Ú¢ÏH¼ip†è+ÃÙ;“ø¦_ñâY´ŒM_ŸŒ…ˆÀ!(>¦ óúG`‚ò,¼|à ñÚµZpÜ}²¢àòùåä•ÑDþçñ 4œ4šEô`™Blž ÅÖÂBïYø±'FÉú²Q!q8'BžÉ¼’":J²ôð?útá2›ðî5ô®cè`Œ…ýóq‰Ö"xùü ›½nêŠÊÜ"T©ƒ&Ó¦¯‰ÝDîd,ßÁµÔué§ã?Ayr1k¡Ó@ùýç^ÉuøD.ž)‰B¯3 ÿHšJüÛE²eíx6±äæ3¹›Í,žvÅc‚.Bô~Ãa'5{¯ÇIõˆUv¥sd'bwØiØgYÎC¤;ð¿óŸYø³ ¥‘D¿ä*,×úböó a´oG#© MDKÛ@ôy´M‰)½tÁqX¶Q Uj  $Ó‰Jw-$ÃJƒèŒˆ¡ÙÉØdG%Läèžx}¿‰*ÄÄ2Uǯ¸Ë½¾ýƒÊ³(>ef[†'6Çá Ó|½á6>8¹¿Ì8ÅÞUD¹÷6Q„ÀTÁb¶du©Öê$2Y“…›Ü*ØÁíxA›W5*êÛᘆx8*…øVDø—…ÀƒüÛ̵î ÿ AV%hÑà† †Cè™î"R9ÿ8éˆ*‚pŸ»à¹ÖŽlëîf‹/]>IKuuäÑ5:š¥¾s€ýštQÆMႈ ŒXþ?(ó]"œ“]3Yüõp×X ‚¿lÄf•X'F?TÅò§,©µŠ¸O-Ɖ3 ”H$ âûÇs *œªúc§ƒßØ;R“ŶH)ðø/áWj4³.Ѐ9½æÐlX—œJàð5ܲ0­ïö ëZóNKêi¬ÌòüÊàËL?…bäÉVÜé0g_ƒ8lß:VþT õo„[IBRñQ|¿êLÞäK§?œÀGt¤ÃS> ×8Æäø³dû´uäãNqÜd†ž"ЊBõ×­ã +¦3ó`U¥‡I?ÜñÒ«  8³J Uú =丽@†?Ýå&Ô¾(ÆGÚOsÓÂÑÄ|/ Õ¦|´ÕUPs\{¡ñD ­ì¹¶ÅoÁ3؃˜ÊAFû]òçó,tÛ´ž,ú2‘’:á\|5Œ’å7Žÿ`_¿]Åɯ‡óµ¶Ãø[¨z_Ï+nïç7Dܹƒ“1»–zVeÑër_kF!¬y¯ÎN}Úy«òo£+˜½õ3Ö¤9Š'_Ï,NàÓ #xû!8ég P?‚'ãìÐlš•;«Ç'DÝÆqW2þóHㄇ’4F,”Oó§Ÿ×FâýÚIT°ã:žÕ$"gF¶_øjË"œ6K½k/ÂÄüMÛqþa¬õÐmúÝ- F*u˜6øé®GåóÒÆ&³|ÿ¢®Ónd gµâòeúÜc“5›òDEç”èýR)” ÜOÃÌiðÆËhÜ¶Š¤Îˆ¡Ë=X ‹êj1tâE8ø«ï%iЦä*®õ× +>ƶo@de4½•èE%z+ÈDo¾E‹êÉÕoZ‘” …dXœ{ɉ ÈŸ* ó&M£AbŠð/¢ OËn$§WWPWç -òˆISA9ÚˆŠ®™Â¥{àýµ?µ{Ô¾Ñ/ôL¿WÄä‰ç[ÜøÌœüÜp’Û·ÞÇM΄1]Æ4âSûÏ×ý¾É ?$à/»Pzg¹?„)Y“V>Wo°Â•SYºÁ?ÆÜiÌÉm8zN7¸–mÂó‡ðî ƒ˜èØ ¼†¢xøŠ#ÛiïÁFT³›ÏìhàþLCÛ¢(ùÏv'}†ø'5B·~ØvA•¯|Q@™ìÇDEª#æŽ 6*Õþ9‚†gZÀ¬v N(º//ìÅ›zké`û|›ø*Lx‰:G°…l”d)êê\ÆŠç|J™3½Üõ›ÑþL*™Êã+•œˆ!¬cyè3z9m»rReyd`—'Æ_÷ͦ¤“¡éb«Ó«Ûï˜dÕ|çú^×rµ/9°sl‰PqßXÁm.µÞ„¦+:îÎWRŒê´F&šs'÷ 0ïú^z}›)ÝQÒ‰‚ï§àÆËtTQÈ"Ÿ¦œ`‹–ÏæW?*… åŽ9^´íA#ÞËMaãÃù8á8ê([)<¸×™RUÝCWAÚ&÷¬ö„…›Eù–ß-Ð?\›Üù…®^Ãøò–¿¸â® ¬Ê oÙÓPÜòìkYo4ž³ÀÜüåñ2â?‡Íû˧–O¤³4djd*Ñ+aáÜç¨)|-ZŽg—ÐÈœp#An&î<;§7úÄÒ¶O¥ƒ}c¾ÿi:)ûYKfaöŽ3xQ'æ¥ý"K… eÙŽ£ Éíè[•4 ^Ùœ‚´K§áí#U|gg þ5âüw+˜Ò‹rP øma8+jü¯E÷GDrMŸ³°ïŒ '¯Ë‚¦AïÊf8þco|'ZM¤¨bÉh^å(J¯ Ï|^»BÓ¯iÀŠÀvÃ^è‰Oe®+óQOobðš¡ð—¬=y‚Œ *“¿ÚáР~>p•WãΕè–Ôt–2C U¼UX©Ý>÷^¾à™¥Í\Lmˆ{úÙôçae•Õ¹$GÅä±¾åÞ™b‰5Åâtö¶R(os¤/´ÒpÜžP\ÈÃa£E ±FÀ\9G†Ç_Ž\:•W[èƒhÇ,.¼wŠ\* hÖi°Û"˜¼W…¾QUä³÷˜‚ËCqú_Iø]†¸J…Ó¯ÌnxYýÀ’ÿÝٌ˔«˜Çæµ0gÒ!tu}Æ>­ñš>}| Ìg6ÂëO¶íš({Uý ¦}<ˆcœáQU*ÐìåYEz{Épz87C(–‘HÏîçµ›ûÉÀœ66ó` 캘¨ïá .ópe‚A,ëàqædhÎÞ”Âóï«»¼á­ädÚ´}>WVåeûïbLå*¾z»°.YZÞ`ýf Œì3lîGÖµi „¦`ò»lÑÃwO?öU°/ägâ½J zY¢}ºÇBžöNX¾ë>Ó¿Æ­Jô—ñ…gSYöÞƒ˜oUŽÙ•KÈ~Ígq ¾mn(L…÷»Yx‡+3R ƒù‹É°‹#ðW°)îñkaÛÃ÷Òq‰ä¥Ýú«…òñR„õ6ÅìBʆ"¡í¢‹¸¯R–GÿÌ!¡7©õ¬*Õå‰)Ø×ü™”FXB]ªîØJQ²^XµíÝu…­»®ì%»‡˜¡Mw09«øyL&^ü™©>ý >ïo£è¶nòz„ß–†.FÀ‚©I°Eù)I´¿€±«‰ö˜I8q›;ŽöμÀmN•2äÜ} Smuåq»NáížVVû¬Íþ$Ää·EðßÙÑ«_)°Ùq?Ù“3¸{à1LÎÝÍŽEF)ù°ŸöµÒ¯ºˆÂžitïÎtüôJeú2ŸŽëܾc×­µ¤¦L™ì+õåÜ/~_ø´:Ž›–Bpû~ü!vƒÍ0¦RÿœàäèÕÌó÷Y nÿ çïLçþÝSíîBåð|)µT“äð_«-v®Zÿ†( *ðN§9¹zå<(­™€Ž“©î;U³–dE Ù'ÿŠêÑc Ðúž<<¹î‚Ÿ¥<>wœEnHÆè.cô Ï„ ã“0ÏX¿­OýjÀ«1ÖÊ‚ëo›IæL¶Á3oõx<J*ËÙ¼ºr¼DgC’ZYÖðí6ýfÿ\o±úlæsI¬šç/l)YšÕu.IäâIãp4QÇæ3ÞpÀÙö­Dµ”"Qf| @s´Ž™‚F_&ïÓÇáç ÜKî”Éǵ\دßh©éHúÞ GU¿“Â-ðHµ5:AJ ýaÆ£ÂÇ_GÂ×™5xõ£N˜V :{L@i×^züŠ _6¢:ÿ–Ç}~RðYßLЄ/R›€äºsg×… xùˆÍˆ×çRëµàîÓËH”•i”•u±:ÈozÂVý©ôÇ%§˜çî°«É*ˆôxÞ¬ôI'?÷XÐÛqóiÜe®$ºb4íJûN+:óÍ£ÞKFòèþÔýë Q|L`%0[æ7„ƒåPšÔvMÎzòÕ¢_áöK_잦 ÷þL‚ì¦Ç…8áH |“νòeaw[+žÇwŠaaŽ÷Ol¶M5„ÌŽP6+¹÷ŸŸÉeÝRyêƒðüº/º:®Ù°š]UÓæß#—pƒ­6ŸŽÜE»Aí^Ì}‘g["`ÎO”?=VNëb5µk§ËÀ¼±‡ø¿fÖbÕ¥6ÌÕM€u¯:qxæúR(IÝI¸ôÜ ’)K¼…ª|À!‰È<Ã5å¸¤Ä F¹¾e9(NE'|!¡ñ`GŒ/œ4žJEÜecå¸ÂµN{Ò€ÇË6BIÙf˜²cðÝ•£[ ‡@ÒÖt”R}DÞUŒÇM¨„"Ïcñ\Ë#TÓç‘'ûy âvÛd88Á„|kæ×í…ŸÌá~ì,-Z{{Ø5áÍ…éxº¯…°ç#áÉsI¶¿N; ¡oÿüÃó*(Us S¦kbå/Ù¡ ~^«<‘”äÉï 0L©š(. …iJ4Ó«%ïü›µ?˜_Ôa´×K”—ªs•G8XÒo¶;…ì¨ÆæÝä’Ýõ@‘¢gQP«Â‚c/ÓÚÀf£dN¿Ž-´0)îô^÷»ëë¸~I<ÆNuÄ¥9›nŽºÃ"aªŽkÜÕÍÆy«A‚ì^HÞªg^ƒ‹U$NÚ‚Ù.‘Ô°|R¢sŒŒUŠ[¦|&Êm¬w}hi¬ç£VeãY›h,~Rä/CCUÎÍÂñzm/{Öœ ‹Î>q.èÎ[/Ôû¢L}}œé¿kAa'ݸ݉¨”á¶ ¶¹=”Ç59@þ%èÞýy|8ÏJRˆžC%Å÷ápÉ«¸Ä¾wøá+o]6ûä%òèîOüóý'žå å kÌéV™ƒðz×9ؾ^„Þ¡Y£Õd9Ó7Öø‚ÜhÔÑ;ûŸ¯›¨ÍÑâû~ `UÊçÄéùpPkþÙ²@xÇ!øw4—ïÇí½÷ÈÉL‘ÈøÜ×¢BCH¥4¤¡ÒÞÃ^Ù«$d$”*…ÏsÝQ )BC´ij—úùþþxÎçsžqóÜ÷u½_ç¼ÏõÀ•üTçÑó¼Zí¤®i+¨®“gS$@eÊgÞú¿¿19ë;gCíbäpÿ¥zžñ§Ípnóv2xˆ¦Çoœbßʧ’-!r0Åqíþ»Ø¹S Ÿ m«|ppW9ÜÉÍ]îÄ´¯L§«ß?ä íΫӫÐKé;üWÏÙíF<\Ip c¿½€¤í{ nÝ3±Ôßí€ô$ ÔÛ;Ó„gp“Æ«²¿yr4¾ÄŒŸ5BÏÒ# ·e&{+…șñN¯y–óÜ©bž*x<à"ÆMçZž$âVçXú¤3þ½WBý•/pÜ3yiˆg—DqžZU®¾Üýc5x,iÎ’Y «S&0é÷×9öô únøŽ³T/á€Ä1h2Vg’Û]Ù‘n˜“ׄfóç B³#7kà\Cë3%è–¿âÜø„‘jñpü"Ç‚ÿZÑ£^ôîõ™4g—ã2ÁÜÎ8)M†ÖÖ,D×øz³®än*D†ÝâÿäÏa̶d¼Åu|eÖœ¿†ãÉüÅwÍÏE«» ÀÊ|¶Ð¨”1.Œ{i5bÌFð·‚*-ýkÀäÎÕ¾‰L±Á”ùØ42¡¿z¸ÀÝ£Ôè¥W'aÌ”q¨ (ý–tü½¼éí£ÿƒ¹dž)>rx k‹°äuJ½÷·g¸nƒ y¦óˆŒ¤…Àœå ¹'¶ Á#+sNï‹&àB‹/ÜÆÌxê6ƒÆ²Y˜3w"K»#”ÿ<$Â²× ~T;ZyHÓúì“øû‡›ìsoÄ†Ò 2}f Ç{¸êým`†»"我M»ƒÖÌrÄY&0‡ŒûxVÓr&úu}$n”oÂkЭV¦UBÁ°=àeб]·Ë¦ÝO„ëSáô¿†h:K,Åë=\@Ã1ò½ò3Ù´d¯³o[ucÓ¹ÄC³Ù¸ïtý¯ 0nL,SøëAãb\*ÃV‰lçõ•K1å‡âléæ“ÿú$Ú¹bç÷}³>t·´à‰÷¦ÔFÃþW?úN°ùúX˜•à‡¡xlܪHÍgS8t厳ê¿ñœª™gsðÙwçäLó£×®Eó¬®5à\ód¨:“½&N“ƒsxÏ5ßrÞ6Q¸l2Òj×?ä¢?UËVâ$§cýcWûÀУ ¯öʲ/ãIÓªGÿÕ‚ÆENyøvð+xô®@eWj9i,~?ù“ëZ1†=þ4 ~,ÀŽž”ÍI¢3#éÍm0dÊ)®jÞU%pó˜Êf?SçÊò¹{ož‚Ñ¿™8¾æȯžÇÝ}ëìÞ®MWxkÀè9Þ‚þì~ùþ —âVíÕÔäÄ.Xüzl¸ ï4³xö+†±Ëy>~ÿ|›o©‰&ÃÄý®,ßš‡y ÊáÂ/Aj·9“ýÄ\ÍwJëP1ñ&ð9¿Ž.¼Æ‰èIÒsr¡4"ß úK›ùã&ŸÆMâ ¬U±ç@ 4}mmñ¦gfH¯Ï¬§Y¸´M^O²GÉm_àæù/ Úñ›la×Þv8 µþÝA2ã¸)ú×ÄÂ÷ÙkØàs@µWpÿÊVgKƒ7¸¤v=ßåZ÷§lˆL½ +âc¸»sÃqÍXevʼ³zÄm¨“Kp-‡ìCƒÄ7(>“L8'ø|- ää3.ñݳrÐîœ8÷57 Ü¥“ˆÔá,Î_"ú·ýåv¼LäÂúqÍ}ar`{$&^r„Û›±;л͡×}füÀú¨–™I^Ð+£Ìt•XÑ…ÃÝ$fA1 Ÿ|Û3Wƒ‚ñ$|ª³†ö}ºŒÎG¾-¹NÆNô9·äE>lz°]eÁæ¸&”]=åï I`ç ìkf÷k€V˜8²7•Ö`ÕЂ» aä´<}úÁ >é¥o¦|î<Ñ”={vJÑp¡o°õòNîÇ få›môÙE•© ›€.-F´ØXÝ7òeq[{'VJcí  }7ÈùÇ@âi ª²%‹Ôjª°©ÖÒ8ä9~u`N ÃûBxEá&ä½Pû¯ž4¼ûüNÑß56_»h³a'cÝp}ueµ0ÎBsK¤¨ïµŸP{Z„ý~¼Çã\–#¦È/Z½‘p/Èšeñ ³yhTû 3…Š:NFÞV”¦ƒé»ß`²>Õ¾'àE¹Yhh1š?¦E&B»å¸‰c\è×E¯¨Ÿ†0¶ n‚{_Oa﫟pO0ŒÕ»™Ñ¿§ŒXçÎ0ðã4ÀðÀ¸¸ØúT9ª•8chŒZ4¨êÂZæ;¥ƒ/ŦÅöÂëxO¶cÊüžÌx©÷`é‘2 Øøí4·ÒÝ¥æ4ºÉ‡^9‚¿ãš‰Rñ :Ú>9•ó†ë9ý‚Èå0HËᥢe4Ù߃ýl¶„¥ÙKÙðD ×;ïáÚJîïÈ6Fg”k©Á£"ÔñðÎ[Ñ_¿ÕÁ¯k5à²ÊvplÕ¡ó7óV$hÑgÃÑÜ©%Bì?ïs|Ôy~BÌ1ÚýW®c¶jÅÈ$àè[oÝ®@sO8rcè½ ip"]Ëé?rRÅ?ðÿ ²çÕã;ñs v ßÇi°†¦Xyô^¼ƒëBOàëÓöþžBoZ˲ŸW®òJÚ}A>K›–K<ÄO‘É`#N8d)øŸOºÒÁšœÆ1Ü—Âè¶äßïê,êÓ© >ÔÂ"K¶~—+šœ2æ,$øpYÛê‹p—“M½øÒ£‡`ËiqF¾ 3çEjºü'oÕuøe<‚ëšÂaõ¿@ê»xûaQHçÅïcñwrð§¶5Œ“Ê¡r[ÇsIvää³yt™«" ßïFË+—QoUlBKÍŸ¤¢@ ̸ Ýќв*i}§æ†ãÊÄØ­fpÉ#Vé³Êfhù‹Ó7XICŽ/a"Êš BsA¦m ³0£Ï¾N¢>–KÀäg,=SéDUfÑŸ"˜JÖL^^IJº³OÀàó§'ÊPÕü7)V*†!o ÆEŸá43ÿH)zbÃ/ì\²ŸÿÔF ×?"žAtÅ|S609”ÍjL‡cèµôÔÝŒ`Óü?áþtúmU2ä9ëÀé±wЫ½å¦ÃHÛhpœÊNqb¬/Ô…ŠMR¡öÝæ¬€ –7ñì#IÌѽB+C$Ø'¦GsuËè.…E¸¬Ø•m>ÓO ö: ú÷ÙzÞƾ2ñ yºGaÓò…¬ôír¦ û¥½¿àÆ·ñ»X(®~@2Ú"ÑR̪Éx²åÞëèÆ[~ToðßÃR…ü8šß}D Ï߯)LcMó¢Ù’÷ôÅ­Ãô£­#KÕûÇY„ígyºTó•4ž¾Pk‰ÁsÁͬÂþ Ñ={ Å›”ØÔC±aŒ;• Z„¢o¡k‘]9Bƒ·½÷E»IÕÔýìé㟸CæE‘ XëiGûðÿâñ›ðlÿv0äM@õû䨗‡d˼8f!íÀ,~سYsH½ÃO˜*,Ä".à/Sx_“IúâO`á#+|ºC˜½ÛDg9ƒ¾oñO bˆƒ ´‹t™A§±Ôªþêƒ]8Ñ,ˆ'ä:Œ2½XµS'áÊõÙ™† ld~OÉx& þ³eg(²í’ÙZºwÇ~`K’¸„àR:YĂޫzÈžûçÁ—7¸))’pí±!UTòa bŒM²¦‹ûtàÈ2í ¨g‘™¹&N²ËLàzº{?¢Kû >“÷’aôœt(¾­°fU;¢ÃhÌòrÚã æñ>/BþÁ%Üʋ٠±û0ܹ¹ŒSÈL¢9Óéf-cðÛ+€tQ(Eç@°F”+e‹&ž#N¥–T=`Ž˜\Ñ_ ;Ó[àe{ÞýuÓ,ÞðxW¿¢x¨(¨Ì aŠ·EYuï.ô=' :*ô“§4ûû3r;Ž@A1Å–HžÜƒékèÕl3æø»5Gž’¹r´[]Ÿj¶Ž®É\_Vå®F?A×~´>ee0´iÙ‘¨ŒbœëWù°õW'׸DÒœ³”ëÝÀ¼^/¦wãÞ‘õ㯒O{hÇMF´Ÿ“ŸµÍ¸¯¨_ö©ð…… ^hLÝ¿6Lx24 z &Àg‹ñÌe±;Ûù\œ>çñ5Jö¼zžÈ¢‹ä‹M?ÝFž­ùÈ‹Õ7¦‰«=Ò¢@ƒ&r÷=óH§0P»ºÇ4£Ð­lžÂ­™ÄÞÿõDµÿ°néC2×® uH²I;lˆÚûÅäswÇU›™:7œÞ®·µñ½x îq¤oÃ4ŽWz¼ ‹ÿ\C‰TGúy4vkŽWáUÚWq7óó±¢r,û“eùye¬”SfoŽ ÇŸ@,ºvsfÓ °¾ËœþЂº,M¦àó†x±óÊ)ªãuÕ· Ã²nè·¼‡&KÏ¢ÆÊkDoï.&8±Ó§žçËM4¦ß~½âqÿ6±ŒÆ\0,ƒÏ³£p1÷ÚN¢Ù U³8Lc¯¿àf¾™…ó"qOÐk([­Ž†%Ðû‘øä·;ϱßKÝ¿”aòù rá.sdIšÑ~ôã)†ý{ŽSß?ãÁtîTÜr¬ÛJžUÈ_‚OòV‘MÒí'¼˜«O1¨DßÁKïs=WÿA튻䯋ûy³]ãŠS`pr´x¥˜f— Ar2pÓ½Ñ\GÙǨ{J„ƾMUø¿göžàm+‡Þ*,MZ ²W“9÷&ÁŽOˆkñB<:OŠMX gîyaxÇ# Û"NÑs)ÑÝ*F³ºcPãH,¶5gb7r9âxMݾúcA¸\$7üâØÃ[ÈŸQ…:ïe˜m]wÛ¸€ì5'ž¿eáÕ$sÖ.4Â)Ùù€rÝ ì}Ù ¡7ÏCÉ’ ÅÜ®ÖOdËŽj§2GvWs;w©S©•ãù‹Ã¡8Ã4Š‘_7aæ1+\0œþ¢‡¸6'­-†ïòaü«Nòd“&_I±íÑATb07–äÒñ_%èÎLMø+ªIO”5¹DØã–3^ÄaqÆÙ´‰~X¸òfFabÃNº&ë(º­\Šú/ÍhMk,×#à á¯îsåÏRÉæŒÈ…‘û™ÜÊáaœ÷U‰(”fê³SñÖ++´œç@½gN`[î ¼õ.¦Él1;æ$Íþ«Y‘p%”(Òò}Wù»‡ç³ñÁ®09õôß2gôW#>Î<Ì ÇãÉy}¼â)îS]@\·l¤Wý]i~°:1Xæ€1&1ØúòÎñCÃë¨Ùèšïôùã–sdN®ýmt;¿L‚zrCuÚk”»´ŒüÞ€‰/¾aUÿ~¦¨AûïÆbCð¸º$çt&WÕ f§»ëM}RhyÌ\¬Z¿Rá{€>lB‡#µÜå_¾ÑöU¼>-UϢϞ¥KäkÅ0ð¾3O¯Â&“"²§Á˜í¼x_ÜS‚­š=|›íƒ¸3­–o I¹ƒXq b³—ã³%ÙœßÅxlË£þïÈ–³\K`„Ver_'>Gi£1ô˜¿8»Çåðþþ[Ór-X[[ ú|o˜ùý)Q¿ g’ Óû.jÜt¦R\¯k ”èµ°U.Xë4ŸE|·Â[•Ó¸š5} µôsøŠWì˜Îür(QØÊöˆMŸßþâpâ'ÔM·aP®Î®­¸Â­™µˆøþ5¥…q.Ìçm4ÕS]„K5?ãâ7Wœæ‹±žŠñt²ÚNõd7Zm gK²r±r™$Ψ~ }±w!`ßÑ¥n˜x„¿ l3ô`!'¥˜WÌ6v_?’}ÔÅŒ—_Ãk>ÃùÐH0)J—¹kpÊƒÓøÄd·ïlß¾²›r—ÑwÏ€ŒÛP•ÌÍŠ…ö\óDVª} šèS-Ua¸ÿÆ= ÀNei|ãÔ‚ÁsxÚâxP<©NdÓ^’2WOºI÷%**ùÄìaag3v•Ìã'1ã^„ïÕWAq¿ v»_ý¯F3,ñHÄ•[âp¾—3|ÒN¬lºyë~âºx|Ž)ÜÃKu?x>Në–îY®Ih!3 ßi Sɽ+˜ñslS7‡¢ƒ;a¦\ôœnFmt…ñézq$«Ú`‹ý;r@ä'øÊ*`¾áSžâÝ ¾è?Q,‰YºÃܲš§(´y.³ÕèäîJeç~‚â;'¼A•æ™ Ñ§bYœNE"_ÆpC]“™Ä)4ãïp ÷‡Šñ±ùB¶®,•z#‡}~kaÑŠ4‰Ôÿïœq‰ôFúTÒWY}Y߉l‘ýA̹õ­Ï@v‰#ÍÿmN ¢Y®Ó<]1‡LAϲHqáãÝKx3² ¿ÈÅ3Ý}ä‹òÆ|…œ%f®êECT…©…A ~YÏí+ÙÌO>nA·…î`âf‰{¶tîîøhe {+P„>õ3é%—LÍ÷*Õ`ö/ÓøæÓRñÀštžzF ×¾eËUVg›¹%ÐT½ldr÷"R1pó,Ö¬<¿*'¿*7@í‘Ë >þ&$¬B©c1µ*ŠfªLBË¢Zh¹üö’{V24ºt)ÊXÊ6ºwóN~…å¡Ã°ìîD¸wý·-Áïï`_1aKR=!ÆNNÜÛë¶ÅâÇÓí¸tm'gäü¢~z÷E°]¼ž26|(ó¥†âó©ÿŽVv_ZË\äýçɞ⹎M;cÄd¦Žj5ÛnÎr‹*•ý^¯ïÙÑ·†GðüKŽœQ÷ êõiK¿ ûÔ¹^YŒ+n† ³ä>ܺ-þüŠÇK¿à·‰gÏ9y‰lÔ@ˆþ¸–Ž\¿G.=½Äþp>Ûû œ^­©FÎoµ_œš×z6mê^¼+"ܦó.µ}$" é‹:Öq3åuÿB€ P~„Ï~gªãõ—Çi$/¥rSþ»ìpYÕQÊg“œéèØÍ‘¤íÒTz~®›ÍÃ'YLO ôƒœè…Ý44/Ÿ³TÄš¸Ýì„̼¤pÏÇã²64Æ©ž ÅÒñ%¹ ELÙ_]úës"[1±˧]¢¶ ãëÎ0n_ã)pUbgIjÅoÄŽ¯eà ±`ê©A~m›Cl*„X¸ù?\¸O‹-¹å‰aƒf°J3‰­ýT~õïqÜ“t^ÌMI4ÛF^Ïg¯½þBpç»ÆQ§etñ¯]ìµ0}=b@×J¹Ò“£Ún_ÿXV亖žK¤JBìhI(­]ÓKï ªQ‰C†<Ý9elûwJ2Ú¾27³O þôããÈZX- Ç>ÞÐaõ vÏEjŒ—pŠÕÛ.áÆ¶…PK)´ºPDgO5Eÿó0¢bDï×%a¿@ü[hL`9Ê›'÷ú iÍ´NÐO5çžÖ±k]ᬋ+Dï}Ï3àìpô>‰0.L˜UŽx€Ámœ˜fĤ7 Ò/÷ÃÙßgñÜI3gúnÊRv¥àœM“l¥Èî­_ù?5Ñœ¿‚ÌN/ŠîÑ1;¿iôEE´÷mg°'…G©nJ›ïÝ‚SŽ¡ÕCNdæ*{¶~í%nœðohš£‰/ºUi÷ÉQHJ¯ì7°ÙB-È…æÝàÐ=àLŸ+ã—VW4 ‰ §¯çá5û=0sM Mfqá~¨ºh91÷ž(:¢øŸ'û̵F0Ú™‡GWë0w»ë¤^ï#yx›v;làÆœJ¨ú»ˆ¾›0µ’yLÕ§DNç³ñQd¼×K4¹ÓÒ–F´âP¡[£Lþ!€nž!L¿»žÖ ƒ)O¯ÃŽìnþïqÎxÿm'ºdK²+sÀû²ân‚u0õ[—ˆM6wÈé-í³í(vášgä`ÑmS磉½ >ÿwŸr€óÓð£æaÌ\ZÁ¿‘¬H¶}ÛGEROc€ÿêBûÆÙ±íå¶ðð¡*Í]hÇôÂî^Xg™1ÖSHç@>Î6V¥AsØ™½+ÁÏÄß_8Ÿ[±Ïó&Ðø ÓàZq Wy¾‘ Y»œE­˜ÃŽñ·Qóy§Ál«ˆã6‡Wü”ÐvªçI™wÓè:¸ÍîÿùÛœµà´$¡õ›÷Û–Ó@Ý‹ q×2h~z” >ºÎ¬6Ißo´ko,;õ^‰·òv&s­*#ÒåÞ¬.3–ßÒv’Þ”5bþ§Õ©úÝ‹°Pg/mà1Ë1$yß:y½ª¥þM£ƒýóØÁ›E,!¥W>ÁMçO³YyuôI~ N£ÌT‹w±ÿÆ|Y]·9fin¹\ÓÉaØùTŠÕ H€‰n8ÈûîæŸ´¦Ÿm ©à£N ³4ééSøÓI”U¸Š±¾“þxÈÄž®yâÇ| §Ap¬}yΧU¥ÁL¹xêb±"·ä³•_"hÆ®™ ìSĚŎÂüâœý]°eæt¾2=wz9fm‰¦þÆÞ´»>½ xJg4ʰ²‘<ú9y6{¿þ«iQg¶‹Ò‹_V‘¸§ªTfå5\vl1½á÷Œgæ[þœ˜N÷‹¿Å+!QjØ;›û ƒ‘÷‡™}å/øP\KHŸ*­›cÊ”ž%±«‡©Š„26¤[e²Pn€£·v=˜7†]±c³bšÔ/7°†)ôŸ´‹ÜœŽéELyIØúKRቦ ·ÖÁÈÞ×X;Ê^ÞzBôkêñÛUJ¥˜ÿ­±ôjÑIÈ_>fìRÆ“rE X½ˆU,™Jâ%é ¿óÜ» =è„TýZìŸÔ„ÖgDÙr§f8Èο2eê”XÐJéÊtJO8^²‰Âgžƒ8ï вþ |nÆE'®7£;$¬ÉÝÒeh¤:®Úx‰›ú¶ßDíÅm8ô@‹°»_ÈÖ ì\£LK'%£û–4Rj´°7D‚e=a͘°ª|¢ØÓQ­º&n)Ýå:‹)xÂßùZìhÕòDBŒÞ︩ÖÀuóÎQÄ/@z( ˆ²H¾!5|·–EVmC÷Ë?ੱ7—=c#K¹!JóÆj8¾[Ž/}ãv¦WA­Þ7te“éì%õ°é•,S6dÎ_ŽÁßK"œù—øõD ô˜êÁ¢°‘—.®Óðç*wXz£ø¯ªÚpÕÐ|0ì÷'…&oø‚¡pò†}(8ŸE?¶†+Ùo!gßrœ±³ÿdù8&)w‚'˜? zƒÁöé!ñ<Êžî£_’ã¹£ÕvtÙ¡÷\€Äf8²È›oòÆ„ªDz¢ÜÍ‹PÖÎýšó ²DgQý$+ÌÝÔŽCí½<ñ÷RôyÍï†ÕÒ×HÂõ·xp›1Ê[ à¾bräÜT¶ãF8üx} ò5 ó šÚ7 {Ö‚Zu/Øáêçè°mJ’´é±fõгE£zö‰ÕÔ¡õØp@޾s>JŽ$HÑß*GÈÅå«ñˆ§2½´ÏrEÃuq±r6>pàƒÈWH†#vÓоð<¼V…¸å.°zž8ú÷'£ÌÃc¤vš Ü™W„¯q÷ãåéYoox /Pä+‚ýœŒÈãO)Ë…›¢}¸Á"f[<…±†ô¨ìL|çHO6G€¼6[øâ —{RÒ%lž§*ºDEq;^ÆÁB٠ܡ93áܯB~C³*éU}AnR”‹²evµJ\ÆÓFžëÍãxgw z®ýzôwòÆE¶% ’­ýì„æZcé<ÌǕɳh“C=|ñ—8lWÇ®ÃÒHïȰýú± }ò!÷¹ÒæÄ@ƒÃ Ì%+ñˆͳjà$ø _Ÿ4HvݲeoÀeç£ÿÆŠUZ1æª3·ù£3S+))nKBû3“hl›0½0l§ÿXñó§ í\gÚ¬pn”¥À·nš½%_‘ÈþFºÙýçrË‹ ë£Cqù0©= çQ¢›;ÅyøGæ¦pï&¦±s‚£šé;c*„Î:rLt½ëYSÈR÷Œà‰eT?‡á¥š&û£œt‹^bO£Ž“uES1jö8š(ͤ;ÿ½œ" ¦Äe‡^ã¬(Â`اMþ¹Z²ºe+yÚ/ÿAP{$\¤0é’·)/œ-mò¡Ib4ð›;ô ô±%Þ”&æÚ ´7@Þï˜ÈpÁZ<û´“'á.ÄJN 0¥í^t넉ì‚ÇP¿$ϪN.$Â$ŽY2GèúËX€JmÄý³ȸS0®î=÷µ«’ŒeWtâàÔ>‰úé;pÉ`gð~OªÆ"µ}³>¦3§æ6¼Çpùë¤àQ¸<Ö§e[¿Âåq$¡©ÖªIÒ¥ÉUX9ÿi]üv¾jâ\4"qíiüñL O­ˆA¥æox<ôØœ{òNa`—– '—¤±ƒ‡ƒ¨ýÈgXvdÓGÔA;çwþ­*Û™ÞMÛ ýÖSH›E¹Y“?¢ö>ΰ«Á¸“Òó§˜ãõ Óu¦‚Zˆ?«7IFNö#nÙ=&¼Y Í'ìX£I,þóÛ'fÐ[£±vö·(zü9êO ‘ûHšÝzá¼nžsgË—øÑósðѯDüàøø/tÙœ¥óñØÎvì}¿^9½8ñ ñ’—¡ß«†ñÝ™©üU%`Á[¶QéOZo5<Ì«åΑ»!¬ý5·}ÞÎV§µw ±Ë¥¦€­& ÂÆðv‹ø§÷Cð¡:à…Xãçò V±«lñ·¶Ž…ÉÛá³´(Ûºñ<¦×éQ6?ˆ¯hææ™Á®¦e¬â•Ó\šeÞ¸šmAT+©ÅÉjYT ˜3ÍE”h‰ù.0îÏz®®6ø½¾Ì_±aï]¢Þ’ÊÀœ¯k`Êý4¬»¡Æ^ÿòbB ÿÀ@ÍJ|4Å·¥×à¥Ö¸[þ^Ûw™Ó 0À&êó'Ö ›¥\ÊŒí`ïÛ0ñ·Þ¸Rñ@ë³ÀzéÏN ¯ ¡îo7´ÜZr³0x›7»ZH=ÇcŒa-–Nû‡~½cé—ƒéÄÄí)>ŠH`‡eÄP´Þ ¼üéƒJZûþæ~\qzÙÀ®tâxg784‹…¯Se7*$˜å‚ð¸ì(‘µ¢¿åéµÓIر[Þ»‰{T”ù»g(Ñ I¢o¸K/¼ ÍP»z‰9c»€"ÝÿA…ö¥ÛÁA³«hñXE›Žâó¥LÃõ6J §sÿ.è0±IÚ¬Œ?„é2Ó Ëx8¼’£cÉg2kX³riDÉwöiÿCñó ÌN“6ìûÇû.a„[£ÎBÉ$?°´ô†ÅX³rŒ„ã²Q>\¹·åÌ{°ï[–ñ¬X˜– þ]ÜCFŸåZ—.AEw3šºu›vG•ºØkò ÅV•M´Þ7”iZ=Ç]‚ðøìRgÎ÷, fêÍýÜ¡Á}ð b_CŸ¦2KÒP)Õ£6fÁÒö³0ÏL žøÇÀØÃ_aíÛ*Y3u–ábAÜ»m)öó†¹iˆœ«9måüåE ©j«á˜ø ž»:‘I¥¾§!o°üª Ý‹•¨Ðö 2]ó.¸hšÓ]Uºt–Çg,}‡_üåXU¥e„²xºöã»ÿ¼ÍÄõôØÖw ß:èÑ9b‰ÿ4¶L*†“¿aÐÛJ<×éÃL*2¸Hu3z¯!SÖâÉ"yš~Ö´ÚÍ8×>ÊšFPµv=þy®EŸ†ÕzÂØ·:U|îáÚÑ>=‚ñÉËÉíùl“&{½K#vPqî”Ñ ìÞ@3ý ¹1µÐçe3¦+G¢Lçþ˜M© &ÚÌŸü„èðCà·¢*ÎëŽÚ›ƒ`G—¬™ªOÇl‘"Ûtë9­f¼òÉ#rUÁt¤œg./ˆRîg° z.ʘª¡~’>䯷÷„wñ¾yÖ öüÁÕEƬoÒqˆrK‡ Ö’iR¯>Mû6Îå¶ÿÝ é±[ȪG—¹ÏE({RŒ ÔÄ¡Ñ6mé àz‡ P0£.Õo„ ó…ÙÉ„-\l wr¼+4¼ùŠ¢©UøzM§Öûd_ÇϽ‚~¯—OãÞÎö‰_±`3CŸVÃÌ;9dnEyÜYMžÅÑ6ñ´v*®\D—Tç¢â:aö·æ8Oú2 ŽsfÍ"ùÀN›à¾M yÿÝÿŸ7û‘~*gNE…×–äõƒB¨_Ýw´bÉÁ³Àf*ARáüÃË& E‚#ÉG_íÈËR)˜ç‘Î废C{í×Pwc¿éñáŽÏ8æ´×žYkÅoÊÍ}G×22¤¶ˆ_ΙR7î0Æ™Û3­›xó€*«q?ÀBRßqó„jx…’|rµ*…̱˜ ÕɾPþ> ´À+C†ö5C‹uûÙ„ˆ^¬¹+Þº8—†¬,€ß½‹¡¡a4öcŒ-AgÞaX±ÛÍÕ.CòI: DµB~·9צ@¼àè;tœŠ¿¥ì —C\wj°ð1ól9öÙ©¶û–»)õÖl¤Vmj,HÏý|M„¨aö8x$‘‡[ Yãï—Ü8E=uV wÜÞÇÉIê±=:°ÖÈŠM½8‘¹({ìLEàI˜2±• !]R…>ê–e!˯“9îý¢› vÏ.ló¢JÆàÊÉéÈ-Æ:׆ÁñxvíZ¬œª@Óî`ñ!…dUœõãfÿ’!ÅHQV'kIC3Wãªü½ ûv¯Ý½ a>GرštÊÝ—p*Dw}û†ñäzÉÐæÈÙôÃüwhäÐcåFðîþ&—¦CÃó˜ï×SäǪkøøðNÌØFˆà¯¯ ¹¨ËLÏS«6X·'‡_ÙÐdÁ@úä±-35MGÇ}ÄùäVüW(Ä 7gÂ߆`ºùÛ$úþ5ƒ1 è¹¹DïRw¡á=O½óüµˆå*?¾Eãc2lÁ¥<ò$P–-| „Qœ¿?eäpϽì@8 ÍM鎽û0ÒA‘­eýï6^ͺ1]‚ïúÅn²§›cEØçOP·ÿúëCk‹ux¯Yt 5a¤à&,>ƒ&Õè÷LfvàúßùÏ–.´vÇe+Úpí‡%°¥Ö‚œ©_¢Ô~ͽ6rUØÞ6ÄàQ‹Ò««D£ÌO£½uŒöA"æbxë ®ÑÎn6ˆÀ;™BØ—d‹·®KÑ©½“YcW;¸Y`~nlפ$¨d×Kòå¨2½FUôô¸INfpc;9(Çâ–Îgw´å™”|.n8FûíÀnÓm¸j4Äm‰4s˜i,Øðÿ‹±>kÙµUà’nË==<£ÝFÔh{*ˆ€/E–`#lgK$ØH¼*=ú³‘çðµ{ž¤ÐCgÎÃâ-[ñó”¿ÜÁg7±·* nÊ'J }:‹Ýü»„sÚ=&dC¼¤9Û§™“.4 sì[°VËç^Ÿ¸Ùy´4cCÎÂïuÖTgc º nä?¿`ä½7^Jà,ÿý„RÍJîÂ³ÙøëÉUlÏVFCñ ,7[„M¯­ÄÕ-iäœv(Ï$ÐË—uñ†|^@÷tUN# ‡s¹ëeb3ñu(ÿ½b²Ò?’`‹¬²sn>¯ˆ‰Ñ¢¦6˜­ótè ŒÊ:r#wÊ—ysVVÂõí$È÷xÝ3ÀÇo•¨“à ˜­åÂq²ì²Äf¸f2$ü6À´"Eö[*¶4 2—Ä7(bHoŠü†­ë´±^è¨ } Ußlè| :P²ëmb¹m¿ãZE;*léó$÷Ý¥p©ô+oµêNò1ô*ù\Ôâ<þLŠfÁƒþ±¨¦6r+\É3Üდ1ûé1(XIŒ/k°Úi÷QÇ׺Ûë¡ç×'ž}e(D~Õa^½ø7ó'¬ûÜO¬fºp;ŒÅp…x^ÿ‚6öàySV½Ñ æ/yÜà•Ë\‡•í{²^צ%¯êjE_qÇÁP—\ðU¾gôE6_ÛETê¨ã‡¸âÑÊz:Ãò®ÌWdÒߓþsñ`+jÁV=Û€ƒºš¬É³ˆ—ÿ¸fö«á¥ü-ÐqoE¯gÇáüñÈ®å”]sq ¨Öß_¯·>Aß±ÜïSrdœ‚-yjyVöºsÌ®Z•MFyß>wüL6OþBnv‰Ñ[kpÜM¶a°ß@¯}a‹s/O3q'ÍV'£þøíÃJGÔ~.½så…ƒZp*íØ ¡ÏîÁØLg29TÍÔÈòëðaîfªün Ç)'+¿7@±\4ž|+CË«"¡Öà.dí‘`Ÿ÷\€S•G¹¤‰gaã5xÒt=¼éëó<ÓVlŠOÈlå^ø6Aßø{i2{ý½õw‡~ãXh¿ÔŸT6£qç|s7äøM€sœ ¯¯%áaèê‚דl^ ñ’žÉ1IK½øð£Ë\‡òÊ+D©zùSнZˆ’éÅô?¿õM#A¦~x‹±à­èðC·7ð°\ þÑÁ¬Bê2IQºÃB‹ÚºMÅäLrˆÆÚµ)ÜØÖ²}f3„NêÇë6- ™Ø§Áë”Å9-Áçñ°š O¯¥Xt|¶ QçUv~4¬½K´WXJ;ir] AD{¹~mnDÛŒ8î,Fón¬Ï€ã&¯0„WØÄÜ0ÞA–„ªÒFë±àû]ºúã˜ÒÀ\–$䟣îChˆ1ã¡4›õóÞ–1±8 »¡€¬ÙG¬wnc¼7.Ê‚wï¿gb¢=(MÃÉÛqÆ=ÇM A(È{Žùw³QÀ"†ØŸËÁ°º:|ÒÏ_ÿ æ±íCïÙÎõ’_òÞn:C`%È)kÒä˱0d9Oq3›WʸuC½sÈæ½ÒLnË)V®ã¦è2­ùøR`2ÓwB*ÒÉ­|XÍùgà»Å»É t%8ûXÐj‰<(;´†sÑàÈJºc8’Ê?ÚÃn5ïä>(¡ µì¨ëÜD6ww ¿-ý<~ÒW »"ik⿆ì„IôkÕvDx=ž·Ç{Ö èQµ¸‚(¬õ]G?9`ÚóÍôñDÂý(XéÐKsj–©É¶”;óßÌšJ}â3ÙÇûõxmýhû£Á¾;ÜÀ·®N˜zÿ"tïZIïÃî.+ƽæÚ´Éù.ØÍ†ØWTz#Çj„ áW†©.Ó§–ÀÐê¤Ñ„-Æ.9VÁ¿6:gŠû½õ>‘ûöÊÂ_¡¶úžUbrÆŽ4ÏØž=Ç‘ŸV¤•ß9º_% ™K÷>Öw¹+¡ýÛJp+®d °Ü7Cx3ÖJZ£JW~Ø…I¢LÌ?=Z?aȽ7`÷°óã§MªÇ{b@.û„„ BŠR9ÌÐ`çj“ì eè ua}›;ÀÑd%•Ý8†ïÛåLÇÕÇ“»?téü÷±T*0ú§]†ß1jÑVšöt;¯}†ƒPé~õA–…r¿‰SL"TìãUêôçõKÜÐÄ08Z­ÑðŸßzI!®Yp¿åB+¯Äª«Oãýˆp»¥ö¤¯¦¦N¿Àåã®fN3½jH­Níwih7;ÊœFùä9 ­-¡ÁaRš$'ÙÂûcFhÚÔhÜÐ3‘6Ú¤£Ú=-ºg‡¥mK0µ©‚3ªÚ©s¾ÁÁ¼+£Ñê©[Üyˆk1ž‚WCÂÀ¡ûïhôJƒÖsÜÑTŒ±—¤ã#üÑõ² ;8ð Ë'>æ]Ìu¤]ÏŒhœž[;)“ÊViŒ-c@ [•˜ŒF7Ö¼Lñã×ß$öªs²q4Ÿ¥V0•­°øìS®â/H`»fTSµXCLšÂ†Ö*–‘ èõu&XÓÎ~–>d…ûùñ3JðsÉ:šð“Çóî¼*†™]›©¾O%êr~“ìL.vî¤o‚óù¶¢y¸Ìu=ûkFï `¥´:cuÈó©lÙP=ÀÇ0˜Ÿ Çø×–JòÖê^¨‹©„ß 5hŠâD¥nœ1`H·ž¹‹7®lÇI…êTöîØ1-\vØíÎudÂ]Z?ç¼åßâ’mðô—S¼Å!ËñÜ¡[$-(‚·«ë:è…ïb<Ïi°£5›§4p‹üçe~¦I¨ÿwmzzáb ¾+Ëî+åÆïÊ&LÁåµ\õ®Çpèó?|Ñ÷­Û—cwA4øy[s™ÇÇ9Q‘•'!Î¥+OI†×m©È Ç¥öN~Š?üHMy·Ù,ü]#¶%rSÍ|¿çßÛ׌÷A$ Ù(_O œ—j†QÆé ÀÒh²hÇ­B«ñóÖ èþ@„Þ?͹¾ßË{¸> †.—âçË0éÖeÞ|‹ƒdì«ÛÕ‘0- ůÅC¤È ª(ñ I÷_ÍåEºU\΋x(žä]nâ,ðé)8ç¾,Ê®ap}'® þ üåš<ÙìÃ`´Qƒ5µh3» ˆ“÷À–/JT6[‡, qqOÙm·:Ó½0>ú/·l¼5ÝÚ³zžçÚô½±Íô®.té­þØ®‰§2òùÍæúlýfQ*¥ä‚Y6ŠzÑ#=hÜ AâwÝ/{N&kÓ¹å(3º<o^Xoå©ÙôǨÕXQYÀÎûÁÉZÆ ‘ÿ|Ø$ˆ³›dè½÷1ë[J'ç2~[·Âsžó]›9ðïçMœ¨O&Ü\Žƒ‘SYËÃÃ4 ª˜» ƽZ´gËî¡=¾ ŸÂ4û¡9šóÕÀáÔ?HrYGÿë{V /!Ö?Á/N—xöŽ!óWM¥ÒWÀ=®¼—àånðÙ¸ÞmY#‡µ¼•î£hV&nqb¦¯›ñL«4ÌÌá«®ÃcÅw ›ù ßØ rIŽƒÁLi­ÕQœ; úC4Z.’ˉLícŒðÙ >‹vÁ”6üX*7Т56ôêÐ('NIÃ#N`ž¹•ðˆc¶Ý=¼MÿÂâ.I2íážü§ °qÀŒZþÖ¥ )îøXAœ‰®™L7Œ×§³C³™©»/5Š@Ÿ¶9à™½;–E|”õ¨dÎ,¿Þ/Ôrqg¦SÏI Ú”ˆ¹-F¤9n³ªïã^ÅuóîoË€Kï÷¡n^1\èïÊ?D=X‹±£Só½!lúì5Çë9µh¤‘OŸÄ=®ÛáïÚzâÛè{ýÌ Íj2€‡~ŽmpN>ÍeÄ+ÐOI\ßÁ1tGÿòaÀ¾•LáWõÿ!¡‡*ˆýçµ8+Öˆ¦OÔ)ír\`9Ó„‚æ8¬×¿}–g_ß\FGW-Xé6†½ú‰²·ø‡ ‡0‹ÖaÊ5à/ Ãß’ø6ß $ϰÈûw8Gszo޽Pj‡²SRðóÊÃtkQ4S¬¸DùjíÞ=™M9Ƨ þ¾(bº;v,ƒªgÚ,ÃÚ¹IР£žTj’8My;ƒ}Kug¦M³¸¬¯÷èóªpBÊ@„ïiTtŽæv¼9Mwgí ³¥T¨ŸÐÖbê‚Sâíi–4¯p }è_É‚çÿ%ez2q~9n[ö›¶³±ç1üëUîJr>²Ý:p6ÞÉ'A M³Éâ/‹©¸Ü¼Ý9Í]jtòÅ0Êê—pÞu+ig´!Óž «¶ŠÓÍ»S1ív*éu3ÀÐ:èX‘‰¾•ëØÉ>sz2Å”û0'fÜQáŽM‹GvN‰’¹\ÁÑïüÝŽâËkljgcÉ//â¾|ËßhëÆ‚L¸¯C ¿ã'² ‰‘Ï«àØNeH¸×‚Ç,&‚øËHr¾UŒ™Úí€~þm,Ö)GÙ/%ðÆÍüž‚Sq'ôö\&å :Ð" ò슡Ø8M–íÅñß^ógŽá(ðrZƒö*g“sS2ŸÊg·@øái°A~6-ÒNçÖŠø‚ÈÊu¤®¤¶5.ÅÕÃEäsžªÙ˜ÃâͿгr ­©>Nºü7ⵋ¸I–PíîÕD¢'RŽŒ¥Ïkoã³Álf¸·î–}`ß8}:¨$ ßs¼aåVAzh‰{Ø‹‰m]ðíz>h8)B˜J/©ð!Ìn†@½‰Ù2Ž-xë‰^*£{ÕoêMÁÜñÇÐzG •NKmû¹IGRaØ)$¤H‡e§R­?3ÉÍ0mºû× rvÑCòµhñ¼ƒü•K{¸YÂã¹iE`»i”™³a“I1zøýV݇#pæÔ \¸o)·´k»jÞræ/1éÈ0ÑÝ×È—Ž1ý¯–4¨Í‚ŸÁLùQ ªâ'5&“[¿Ò¸q«¦À±Yh¿ÃݦtÁîòøÏÓ|îñBvòÏ[PtŽ…]Û<©h_$FlTbôƒ=©]­Æ"†¯à㤙 e¼K+ÉÖy2Г&EW PáüTÚ¡¾2ê‘>ºíìûk Ë}3&‡ïz²(n#p3b¹+I&Œ»ëŽ–‡–Ñ·‚0+t!§;K º]ƒÈÆîw$i¾+mô"5ÔYi¨6ÍõÙÇçfÿ† ¿+87‘óÐgPˆ[‹°¿ÃŸþÜ+Iï¥{ñî>$Œ D²çŽñpm‡ß÷ûvH:…ó7ΠZwch»—öZžn°~·Œ¶æaÂ5Ü1×û4®ÓË_å1Ìj ÅÛß8!Õ"v°å,ÎZ˜ÊDÏ»áÈÂ;¸Cèl¹Ù F “Ù£ ¿ØXoK*áµñÚJ§X1¯%TyTߨìø®‹xŒ;.Nó;†ð~¯+“„ P¯»ænLaÅ×ÆÒ¥O3Ù˜%)tFÇi0dšƒëØ|½⺯’—9ù ´7o§ÓñÙš3ØÐW IQóéÏ}™Øð?uÇðï“wÊÆt…½*3:ül“é·ø«L l&à4‹nVKÇÊõ3˜ÜcM¿Ò‘ÞÛ±–þãÑ.  +ÇPKí£t韙üqƒ&4i ïë±§šî4Ân—?¨‡;¥È5ù@’p…á·;™yó"h|¦F»•x“{â™ÞšÆfÜaìÆÒ=.M ¡ôOP*ILf7/°=ÙŸ`àý%Î/y:ûú Ÿ{3ƒÏ?9a4ýˆûH)É£N›KN eg·¾ä1Ó‹¿ð¢Û&hÿ7Ô߯ Ö«±ñ–æ(µc½ÿ ôÌ`¦¢¹£5‰©h¯£÷f¤ÑO'cQÝâÓ|•‹ÿvLc§u"Øí‡Ë¸°å§¨Õ\–8|–_ç$„Ç6%RÅh_4¸=VùÝà°{7¦Ð×&çÒTΗ¨áms]&dïHV,b÷B͉ZkiÃ{Á(TIâýó=ʽ^dˆV{°;Ëñœù (ñ•ø[¡,å½-5ɺ Ö“¯Ó¹3S`8×W~OMhâD]i`‹míÌäŽNÃq‰¥à˜hNýlDéâóâ\fh!<– ¤é7}ã<œoý’ìnsÕCõüáR!ò!Þ‰‰Ð%ãTé{Ó™láË^çIkP±È¤a!ùü[~þ4]ûž<.JeôèŽîhÚ»ºNÅ3}6¿{mgN¤É1 h˜r£~þÁ¬–Ó¸a~ýäuËú›Ž~_®Ó%c`טg$÷¨&ý·ªuD;ˆs&Ї[gPñ݇àÝ"!î­ü:.ßö; oVcíóVÂ…o¹o¶UX/÷™ÿ):4 OlÝ.6gÀÏØž•\?Œ{8G&sF€Äúð±hÊ3èúí†ñž[QåoŠÿj„/{._ÈqV÷v_Çrgȯ1e¼óK‡lG™¸Ío$ f‚ƒE)Ž<:OV_€±µB ë€Ólõ˜´½9[“m 7¸tp_-O‹‘M6öh“fÀ]0Ó¤áþûI÷àkÞæåa|C±ói,±Zî0uÆI* >î${gwô—_oøq2”³RB‹iC‡ 7à˜Ät}óîª8¡ådÎny'ü8ÅÞ.cþSç0µL<áLSw¦ý±wÙkÑxgÆNÆ2³…¬€Ó }RÇÀü:úÈè1Þ…,æ¬M7}³¡º½x80b.ÔPídM ˜Ác»3úP¨û:šêÑwwØ:Çã˜9o5ÏDæ$‡U=8K*ÖÉ–WÇxú4í4“ý…I Íüå+Üaëý§˜ê8‰^Ù¸–ݘVŠ´ãÌëŽà*"¾“Æ•Ï@H°ZÆÒ Â]$ó]|9t7M E¿¿¸Tÿ,Ê:Á&݃³žcÕ@9a#˜Y™ÁÞ—°N£(ÂúìX¢¬URȤÙXâ|•ÿ*_™šô<`æ× =…ËÓ!î›äô<$þ|!ê2\ƒ¹·Œ`­y>'xÐàjkW ììsb/3³ize³´Gñ`NØ]ÂÝy– sL“iþÂShþ¥†Œ3Ùxßù,0 FÇIp£'5ÄŽñGÀáÊ"â&î@;¦S¶õ“}~Ò˜ºM£q–Ï0üýJöìpdYW¢·õnxó4+/Aé–dIü]ìJñżí %`Ûþ-½Óo£J˜3  6ߡ훽º\› m¥åæLIi–Èß±J¤+N›é6j²ØóKéM=öå¤*»?îçê~¤KŒhSÙkhx3ŽÝÌ=~¡D߀¶¿~O„×6O7òóŒé‚üÅtFû~vq–ÛÉ×Ãc_ù»™¶ù0ò ^áÞŸêt´-Ö³@õŸ s“¼ËLüÈý\ÈôŸÃÁ®ßœSg`ñU2lô<iQ"Ñ;1ªé[1×_•’¤õîyŸ¡M+ˆs¸7™è óŸÌFûÔƒìŒcäñ¡öÓt*ßóäÄà3£svïäòl€ç%HÞ…gÄdü4”‰²Maä°ßl’<Л°Š{ô(ãÖ²—2ÂÜ«=ŠPàÃ+³øÊyßßÍ\†Ïà ÛBîûë{øÀ™‘ƒ7:ø³µpÄË‚­ç輚“`|°ô{¡L¡Î+í#RÝap0¯¿Í»@ªK¸5)Øùµ©á*m‡óþâÔžÓ„+$K{‰S÷MîªîXœ8È`Hõ"æ|B·ûATänzjΊ,PU‹‰-êâFûÆF1 x%ÌxŠK¹¶;_¹ýKçC€¦tI/C·dHX±/ìb2U7à|tN.o‚“bØ^Ûƒ¿Fóf­ ]õ#“_ûXŸŠ„ËÑæÒBTñù%a¦:DŽø5¸hKñýAx[ïãýPˆ?q&{bÓû•¼@·Wüé¶¼“?6pÿ«Æ´­ú Yºy医ñæ2Azº0•“1ógmîAD)аu-äÂä v”v°…aŸ<ðP»A2¤ YNÒ¬ÉËåjR÷bpÆ¢¶=4æ¶§KЕd2«K¬‘ ãÿ Û¯TܽŒ:¦C˜ñ9PJË!áÖê,x¯ ]}–ñþáúçÝp'G§A§Áн¬›Š£šðáý HædqMÆ„š,r¥½¡éäˆg ~¬¢à¬² •¢sɘOwðU÷ }¼%¢ÂIb¯|Lj5 <3w–í‚O“hÅô…ð ©—ˆÍ_½ÿïYPä¯uë‚',O®F MZ1¯›9e<Õñ¨@Þú:ø3R//Ì%½Å‰\á½[¼Þs‡ITq M°´aŸž aïÝácÀ¼³!tÅèê,[zfÎ!É:æÐsèõÕB%!ð©]‡²… 8I”‡»w³·'ŠP|â½6™=ø~“«P3agR-˜ƒå1H¶¸B~M€ÖJwÜ÷µ0ãx*×¥™Òú½xÅÚƒþzïÈBžDÎ>ò_¿>ã3ÂÅs4âC8¿­ö3gf-ɦŒ¹Œ{óQź&®ÀJ”.»(M ®§Ð® ì·ÛQž §¼·`ì•lûÅ·ú2Lw\Ï^'FñwÊ¡ª^(T*Pi‡o\ÁÆgqdøñQX—|&I\½_ðE,¿séÇ2ð›åXzuȜܭՃ¾/QârsŸFã½= tÚT}’€ºÒ–lJV[½é¾ê¹ÂÉï/mÖ÷R5Ù‚‘mÜ“û£qæwî¹ÿ Tdº5¤{H³©Ç&ÓäÚK°{b8Õ7‘í”ZG­é—ú*˜Ü̦¼á±{.)¼ÁIÓqª‡9ýîPˆ+–?%­äûÖf.Õm&uß5“ê¬Ô¤B©«™QQþUÎAËžýT`¾0Úäuãüú ð¼éæÕb­ó²˜MÐDXžý‘ ƒïáPþDÜ5­Õ¹`#²/c<={w Ü·å<ïÏ€„€w¤'Ø••ÖƒòñøfÖfž:ذÕ/äB0¿ò28³qÜêSbÔäöt=ü¢_ÎÃú‚4°ëg®3ªQ˜ñ]©[Ž — #ÓþÎ?×®L<_N';mnà¤É8‡ ôÈžmÙ>ûÖ?sâ`‰B5/Q`Þ ÿùVô XAoÈ<ÂÔ#~øògÜ=-nYé öȈvÏS"·½þÀšï@+iœþšŒÓî¬ÁúG-0ù÷mTõêÆ=^ëÉÎßKq:jî¹Ë© ûa˨tÞM«ÁØÚœ-ïÔç ŽäcªÉ*ºoÏW.éÎ0ÃïwX'QÊóƒÖ¹+˜3܃‡ŒïóþvÀ$µ“øpC#dN>†y3S1ŒŒ«'ÃS±L£>eÑ[áÎWÏN¤2;hMßî•H”,Z¡]ëRØäŠ‹x/{:]\«Æ1ûãAAÚŒì,7f¡+¯aõ'1zhîG¢ ]1Ÿ‚ñ‘‰¬J$³!°Ã†øÇ4#)ÿÀIÌíoñ ¼d—@{–.„Ýo¸]›[¹žÝÉÑå <ò…~6îíö¤ÿÍáé’à ¾AHêrcÁM0óGkà`ê‹õP;ïlÒÆÅÉ¿ î3…Àš+hóüüžòŒ¤m˜Åo¿¢9¿l±ëårhµ¶X5¤˱Vª¾ôm€RñÜIãp8U¹†½ù.A®Íì!#[Ç¢Ðüaè¹uŽß_lT&Ѷwhü> :ö1¹v$©Bôŵ½yð,PžÅ_À½@+þ¥°ôo²Ä»ý)$Xì «f 1×: Ü•ÀG :wU ÜÑìäïJ&ƒ1÷¹ù‘tÅ–˜¾úqø¯ÞÕ–>´zuö…O¢uèu²f.*Æ+ç.a÷4cxU°ˆY \ü ‚kȪ‰Ñ‰4)¯—ù™Qï2mŒÉ̸DÂ6*±íÆR´öÌžº+\Ï7¡*+£¨cªwؾ ˜>£Å½0Ψ•«ö“3‘w\‰û1’êEáuëȃݛéÔ¾#8/3¯±|ü2ˆý¶¯Å«Âü n­ޝ•MXµo^¯«# †ôØx½+ø1y,¸¤àÛ{QJ·ŠT,ë Á¦šì®l!eM„µgÐ,’ˆ/~MÁŒàòO,ûK2Ñ©üxVðŒ×¯‡I µñ6¦ÞïäÝÙ.†G/ާ½lÅì^0¯9‡ ¶Ãäú=¸íÚV ßôí‚ÒŒ©v¤KqßÛ4ò ÊöŽjðÅÁ9˜%-Ãÿt¡÷BìÆH*ŸÔ˜sëy$ꙨLL…Uo5è<ã+hzß×Ùxý zþ<Œ )D#c4oÕÈsëžÎEóàS‹GT'±ÞµE¸_ê 6 Ûà®O SØÌ‰­)źևøQd»¹Ðì>UBäá6<%5†½ú-OM<¾ƒé4Wr9È.ïêäÂCæ‚ÏúÛè>3*Z9û=ñ˜õÔ?x“×&lÀ–Æ-‡X3LÉ>‹ÆÆ`èÚ.Ž:Ý¡†žô€+!2LâÐm8%1 Áðœk¨·ífÅT9—ŸcðE&ŽÛcð·‡=†–®àÒXå#sê¯+Å»‹ SÇ…ÌW¤cŽTM+"R²tü¶¬‘Ñ•ÜÌx†³|ÛRª]¨‡y6'p¬œ?® p ôßT¾´.´êv5h޺̅\Ø€¡eV¬uÝd–¶^Z6`¼h=\<¹š=#Fø,ÝÎî|J³è–wUx¹* Çìÿ='áíKU¶OJ•zõž›]JÊ,z_%ºí<ƒënŽpÞº*¸¯Ã'Z«Â{u!²üÙFœÛ?‚=9Å0gÀž½ïùÂMùå&Žp\¸Ýôb mÊ$PÞ©€C–q 5S‚ݱk#'³Õ8åŠ!ØÛ€¸±7_æX€„€*ls=þCôS˜áÔa” žË»&êóCB¿ã’Ì!î{D —Íá :$Õ©Üu\†æ{¢`ãx$ÒƒáÉ{x:8—0îç›(ì _Lhë°‰·&ы驜Vƒ fD‰³ŸoDYçãó˜èQe ó ½+Sb,àó*„sÒŽ•9ÊÂáöDÙ7~úŸ!«¤`|³*z-3ÅRãp…õlÜßD´ÏÚ¢7$’õö3YÀ¢N*Q…5_p¶6£šØÎQ p»<Õû?¢KØb[p?èÉg]3á IBß-tVT'z3žrÃ7ü¸7úÍpSS„tµ„¡…,UT¥{¬ä©dãqè ýË1ÝåøCf/hÕöò™óÜßN e/8õªf”¸:o8ßÀKÑØ5¯Ÿ¬ëC»‹Yò :7ùð;°÷} 7Oaöåœí­²WºŠÚ­í-íq!ª° ¼RnSù ¶Ór^< }’ß±\ÜIÄ)ÛÛð`ãZúïl©™Ê2”Ÿà˜Š*ll€SÙ,%aÚÜæˆô:PD* ×&˜À¬*;þ”Êõ¼´E0`ëH-§’1»åéý»ï`ej®û~Ck¡n ûPQÊÛTÜB4][ÁPv»süšuóÎÛRõ¾~¬ýhƦe8ãY¿)\^ÐJ¦´Ãf‰§aÊ&äÂ…é‰.#ÌþzuÝ„’©™Ðwü-¾ürÌ)«KŒÛ÷µÒÙ±82öå )gÞJP׳‹ÐÙQû¸s3Õó¡OвP!æ톧u#Ø’ñ œlì1ÍÄŠÊî¬~hÏ•eßÕÁÙ ¥ïFÏÑšpI-s1öÊv7T ã…pÎgN imÀûƒ8_êoŒþ#Þp¢«æOMD;·Ô­¢Ž‡¿æàEù ð<Õ`¹¹³  Üeíºa]]·sW5þvÁqú0±‹¡ãû›àW”ƒŸxŠxbñJ(+IÁµsKpæ¯ZX0þ ø¾áo}HðKéeh¿ˆåÍ9Ë•dµô0·À¬ ¯Y¹òݛѺ9 í«€wÄÆ²¤s|7ÄÈîj2@”²ÜH Åw®Ým”Ó¾•“d§±Ü³-áXÄžÁ/ßLx?|–Ìî®à-ãup2;Lèì”枉ǽ¹p'+îD6¤ÏýA*ß>éäQ¾ˆžÄ¢—ÿë3ÊhíoId1úøkðr`‘Ÿ?@’Д¹ÞÂÌg±<ÓžÁn? eg:ЈäãÜ6®gº,]f– kߥÁ#Ÿ×°:»ÖœbÓg¥ãý¼b¸&=–>“òeãšÂ©ëæƒ Öø¶‡ÑþœþÛƒ ?äÝšd3ºïšà’Fy`ì Tò8Þ´,æÊFù>¬ àQ~>š U ÍÌ:z¸J‹=ÛŽþ>'ûvÃ?¾cðµ{"üž;™¥3}®fŠ,œÜaß5Vã¸éºì„Ô{Œ=|e.'pŦ¯@Ð~3.סi…k Ï¿ û:âþ¦ópÖ¼œ›xp,¼œŠÛ~à9þÖc™WÏ¢Á½÷¼«‡{0tê!l¹ó‚ÿ~ ‡±Ê@ðG#®â²°³ë;Ĥ_äl 0ñãa8>ÔÄÉ­È&z&M f‘rÇÆ3%ÿ>.t‹,’¡˜Ø?‰nBi,…-t3̽ÀáóT6!ŒƒxÓu4éâxò^•^—]ÎeYôs"q~ “憲­-‹‰{0ýà¹ÞËEà#ëJ´º~…¬úabƒÇp{s=ôËXpÏnO$µ8Í¡´ÛÁñfÇ4ÅŽ9#Üîˆ÷ðm\,»Ð7$~šbúö@¬ôf‘Så`D´ÛYñ’gä;›»L¢‘yG (o9ûsùÝ!‰iZÅ bÍùzÞ&+¼2ø^ØJûcÀêA'\hùòCX½D†KMƨñ…ðl¹sȵG7£$¬ €§NáÄ%ï,=Ý^{Õ’@á£Ð>ˆÕkˆÙû(vf7>åòªºÿ:G÷îá /aZ>À[¸„]]óÜÌó+;$ÉM8]? Ÿ¾v`I›]¸)Is`~ôk"Sn ¢«#¹”òRØŠ ¼j ëüœˆ©½âLcŽ}úÜ’ÍP:6‡-H¬O:|¶™Mw/ºÆþIL‡WfÁë«IÚº•t^ÍÔRcºIFì¼ÜyîâA–E™P¼ˈŒa×nfÛu‚Ëý9LÿÍUÞE t¢Š{3sH°”fc6ù1Ÿ;å÷š3º)D;¬ã0u°Ü|î’Ó±ï!¤Ï‰ö,[_=æòöX„—0 ȶ'|ò™ì% MÚIpËWƒµWÚ²æ?1.ùnY©Éù‰Eã†Â7ð{–5Šo“‰Â1°6ßÑ–àõžü´èœû"A¾ŸP`s*Spڊɸjl:ÛÑÒŠ&¶ÜÙU™0EA” <ýF®m7G/}qˆ°÷æoZkj¯@êø0ÈC³@Ëîšó¹.dFÆtãUslò#j†áøÂu „ÝÌ„›Ñe¨îÀ´60{ÁqL0'WØ›°mÕœÈy5ߨ_»áÀ„ç`Vpg¦æcíA'2x"!œuQÍ)6ƒ{îù›¾q›fßá«5%вz1öGJvÅí…œ ’ìÅëÕ¸öÊ% ¶â”ÛV8Ï[™‡~59¬NXæzBuáwØÓ“È÷³Éå%œ¬Á››Cè9ðÚÈ#®¦âÌwC2Dqb4pÿÎtéFxáÚ‹ïŽú¡¹N7æKªãt‘LÐÐcöŸ¶a–¦!câñà WFîX‰°OÿàqùßèûQM8õ6½€CôD­#ûü.v}$a?Šéóâ-øm‡•Ï §oC¯rUž8ebî³sdïÞoÅ*X2~×t®Ÿî‹ÐãÑqìy À^YºDwª­…L•y?“‹Hå_–«h‡âÇ‚ðÒçp×d% ‹6 ¢1V*ˆë–… k.â¾SX^è=f¥­G­'gp™‡íXîðØ)9*šh¢÷Ä} '×÷7äå^åŸ|뎧S©x-E tt¾õÛw>ç ¾è°‹­GQã… ¹Úý•Gó™Õ¿S¸ù«3%“C¸I‡sØq­`¬ý ÀîŒ8Ó”’*zÍå ¼÷2·ƒƒ|‹›îÌEXž¶l½ïFÔ¯_Œ v<ã¹'.d{¤2Í UK”éÃ^îF€<}1õ2JWUrÃcÎã|Õ±pÐã*„XMgiÊë—HFáÌ¢lã±(yžÏ@ ƃ5o5æ¶Wt)ËNˆ.Ðæû9¡(þ}ªÄ1ÜQóþ–Œ£–Ê+YÕ‚Î¥À8e,“$zãP¤Ô’^k‹‚îû?y½E.téjyö’¯æì½äp^iøéŽ'mÆO×½é©'QxÇ4“tTíÁ«„™½"Ó}í0ñÃ)˜q-)ªb!ÏRqéyQêYxÖù=!­ŽCj§0‹||‹›>¡Vu áé 8û¼}ìx(°ìì3°Öa)›¾ÜžzëåqÕ•ðA‚¢þ1U6·é‚³a¥!âÔéÆz„­©I0‡ 1 ˜—¶0z† rò—Sáâ„)hêÖ§ÅØæô_$.´5/>ä—Ì÷Ds÷- ›~¿ôÚ‚{€³çB©:TÅæ=÷UØ<~ˆ2Ë {¬˜÷¶ðKÁQXp%}`Å G¶õãl ûž#ï39…>!”åŒMo°v8íwÞ„ú`I8úù>J:©Ñl¹dHì`gFùñÇ[·€óø2‹»[ÅÍšfÊRtìQêwîmýÁ±>\o•-“ù ýڨ喗ÐáçÉjSN¢‹h2좜÷BDzpÙ;Xã8 :ƒã¸ÔµÇqåÃ.Ùí0 Xºš …|â_ a~mÇyzÝãX’™Ù½¥‰sh'9o½pXšáù†dèÛ.@ÿèK²Ô‹òÔt–:ž8ð€ÌûNFŠ´˜¿›ž!J7ÎýÃ{1¾W”lïø#þ¨ûH Ÿ|Ën½È$* ¶Bà~YzÂ_ßù¿ëŸÎ¨ãõw?'†ÜpüÝPñr¼"â0ë<_}è«_øÀ¾æ•ÊcÇË™Dí¨-TüPa·Ÿ¶£YB¯,:,Ìþð¤âFÒßÏ‘[ѯc&8eÒÝÑ¡ÌxKH?—F}äãͺ8«>qÅ}[¦³\›gxòÚ1Üüá8½¨þWýûÄψÿ—«û!0m<’ð„Å\„xר¿».¤*3;ϸÎð8¾¿ý·7Õ)eNv¨VÚŒ²§ªQ^¾¿gJ ¿:pÕ·`^†Âì»#=wÕ×ÃF #d†-KI¸ "ÞÏñæûP°r ²CŽÜo•ŠOÜÁj¦Þ¯ÓãÚŸscf  À¢t¸™2F`ΫÇ&6¤ìW"»&bxqý¸_Ó.{2JyË´Ø—›ðÛ¨ºXÕ>Ÿ¸) ¿î”¦ü€½ok`JÉ4|s@…=nkÁû?l0®(eb[¡a²¾Ï ¤ã$Ê î+ØOoǾxâø9u4¦ÈR|ô6dMûp×[ØíøíþcñcBOLo¨ÔîÇ„è™lóõVüöã¶s¿EìªÔ¡o“9ôZë‚ý·ïÀ²Etû¿G(B”YòØvÌN|Á)mÍwÞòTFÁˆºÎ|ǦUBÉð{ìâëãÈ={ ¾Œ«¶ž”ö ›UîÁñH0Ø4ÄÓ+ùŽ÷µ§âÇ 7Ñu³4^*¯&‡vYpÇ.ž†¦XÈž7ðúÁ'¬jÈÅê+œfKkÝ#EÊ{ę͎v`!åÀ÷XDëïjSIeÖZ¯LÇKE²%?gBª¡5µ>ÌPuu=TÈáå^¡«FRÑîþXväG$~ýãÍE\=ƒfË#ÿÄ{(• Þëåq g=¯¯†wfÌ{µ ›g>™·1ºÜ›Êð'»Š+žÆûCТ9¯>ÍÇùØê&‹áÂYõ§Ahv°BËÃö¬ïRóh¿£9os ßëöjêÑ“.Ñ-¸x®“΋†%7ȉ%JlÈZ‚Bæ Š—†Ó5UºL-Ù®ºCnÊ1XÐvšò…2àmÑT6ýçK¶wJ:LVÝÌn‘)¬ªg2|2€e ’èA÷HªlYÀnZOÏô&À»J?ª¨wΆçB£zðEA´ò/áÁ±£L)@“ô€*žÈ¢×®Óê‚læ2÷Ñ^Ð j›ÞÃÖ}¸<5 'hÝ€igòé»;ÇAå(Ú¹ŸEºZ0Ïô,tÜçCÓ7&âì‘QÝ8¸‘>WOkÎm¥Y¡§X‡÷)Š;®ró'Íg--縧ó ¦>®Ç®‰ìõƒ0^W YÒ­}²~«ùÒ®%V±%•4ØzPN°¶9JrÖuê´t© ³â·Áí63j.‰-ü%¸að¾Ô;òŠ_»¨—k×ûWCÐ’ Ê)YkÃÛ›\féÔ[?üV.§ª÷«ÈÛxè—X‚-µZLZ6ü†f2yµ.<üÉ€ñûŽ%‡ÕÝ2LvR7ßÿ¶ Øé‡7Ø62‹…ǹI[Dqf_‰ã áÕ·»Ñ-}%nüåj«0äF ¦Z=Äs>ûådÞåbŠ7`¶öØzž“0ÔeÊ9¦ Á=%»ïýÅËÙÅ'|ÛžpfT[¾ø¡[ádß+(³°\&ϞÄŒ`æöÖØh²"“KX|Nœf eƒns?¦/ £]·qjÒVª¢&ýæVÔ6ã'Wº Œ"t˜â¦”ᆆN¡5O”ºd݆7ÃRÔ6nèT —þDS•µàí¡³g0­ä©}—­&B#¥w`èx#¬ánûŸÁòq`ÍxA|ò½…›?kD7¼†ów41Tc`ô¾b¢ r âù8©x”7Sx´C± æ$UÐ"[ÈsùõØÅ &áN LàeÂôõaõã[8qb(;Þƒz—ÙÉ·ŠÜŒ˜§¼i­V`­vÝ$:@¹A‡H¾ƒwâ@ÍÛKgkQC­(rÜfþS 6Ï qí+,8€JÛT!É{›ò<Ø(J²£™UüØÍ¡`ÿß·zí­°O}¨M€#[†¸ûÞætÁ–¼]ŽÊZÍä^ëTö×áqÃÓ¹ÎÓK¥`Û¥‡p¬ªL×Ö¡Ðö“Üïàîaq:÷çßqœšqN‚Êü:+ Çáޢ؉0,.ƒGŸÛæžëj‰yÃ3Üýä8޹DrcÝù¦¾~äÔEIîSÖ3ôúªÎfït¡Ék@4òÔû,„¹jí ¯'Lwø³ƒÆŠôÄËã௥Å.˜,…Ž©Ç=6·V8£›e8Û~c+³¨$Ô$Á쎢˾m¤¢áaX-¼uî@Ý4[\Ü’g÷–0÷¡˜ûLˆyÏÐaƒf£÷î8ÏÖ¶ISc-7š¿b=>(™HK_ZÒ[V]pC.‘µ÷døò ­Û·v³‚pjµ4^½íÕ›QÛ­¯HpF ÜÔCÒ7g²ÚØUlès•-†À…ª,‰ºÒºçèYª²Õ€?¸ù Ü,IB—çù i«Çò겜ÐwàxÌ–è· ûW*É—7y Õø,¡ ×ISgmެÓ:µ(û–qS··£d¯ ›u®ùK°I•S_è3ÿ¯)ݩΔîl6‡ç2?Âûmδȱ«Rœ¹±Í¨VØ>2ÔwÎLt›—º®¢Òu]vÀ ¨vg".Þðk—f‚íåø¹q5SgÛf½%_»È®íg”äáïeê·`l??î"ún_¡š?¦Ò”\„´Ìïè9#ޝºH†&@ 5(ý~סÁ¥Y¼jãýÓ{xÉaÑh´Â€õi\˹pó„áó‡À®—\œê:|r¾2Â5釣ÓAôÇl²~~ž’²¡g]Œéñ>Ú¦D¦ÅМv/8¢ë96tÁŒj– ØÃ8!º`• L^ê ÍïÆ É9ß "ág~„JМ¤3¤qjq΄ÿøÐûD¨S¶½cæ¡ÀhîÌt<:?.óOß}/ŽÌ'9jÙDcm ¶~ŸˆGJ”ÙƒÕàѳN’àζ©Êp5nv¸õ5‡R®sÚÅÖP>t-¢ÑQ3®jˆ²‰ƒòÔEèïø· :´ëW=;€ÖÜèÅ%Uš 4þ>Úìw”×àÌŸoàÏ÷ñdYÝ!þTëç<à~s‹ùEGÎòN‰°káѸKì(åIÑÒgp…EXØÌßðän¸ ab3Ùƒ/ ÀGô4½„»C r¦0û}½ zïjÑÙ·ÆÀø×÷ñNk&—H£¹ 1 ¼* .T8K–À]E\Ó“ðçjšN—æ>%WáGßÝ E‡'báÜ+¸XÊ•~WØ×†dŠo×M¼&¥Å.ÝGÞ¹°Š' dÁœà§SH?[ Ã'&{W–Uè÷qa—ð}¯¨<=̽GeºÌ÷7¼ð EãpU'âÙ1W¹Ãá)ôÏ[6Ïý!.>iFŸFàz=2cÍt^D÷¡ðH(•å U6 än×1";2ˆ_÷¥qOïaôÒ )¡H…Ò…ë8ð|½è¹QË÷W¹Ÿ‡0d¹“=¾ ûu¥¹ 3kDzo‘—a»§2)q …ú¯ Ñú4'¬Sv ˆ-ç–ZôPŒ3H»¹£Õ'æåÓ¶jùœgÆzvçº+KΙÀ²¥¦°Â×Oá C•3wVK”¦ ¸@rÿ~Ì*ñÄ)­›`x4ŽúG{£’’)VEŠÃ•êõäÛýü*¸m3¢íÕKÙÖk.,¡d˜Ó©¡TìØuHÚw+§èચRˆ8•ƒ}ý™VÍU^ëótv¨9}øU”Ë_$Á†Œ‰Í_C4Ó%±7`©¾/]"Ä>o2C¸§EÖÜK€¯¹oáJ¤{ámÁæÄ% ·5ˆÝEëNÅ@Û¾Ë0x«‰†!q!ZŸH N‘êÕ@n[ ÅêgäÉÃ%,v_dWo¦ÇüV£ßÊHör„—EedÈh}°zˆËwñÅæ‡ÁºÕ»á¾÷9|P› q½ùÜmK+Tø9„F—…˜iæNš>âOÇK›±kBɸ‚ȲÆnôæËeP)/O¯05ŸÞ…5ß÷£^ÛgÎmúi¢ã“ fÁ…P”BÜÛcœC7šé§ðé¸] †KÐ7„Þí) ²èçXúüóXqü4vÍdž—’qÕ!'¶Z!“V;3»Ë%¸²µžø%ì£-\™†gËVrš¯¬àñ‘Aç%·21®—AÛªcl¯Êf¼"äÈ9Ž¿ˆ{j; ¸ïÆ€êÉ`æó¶Í>ëýX©ª$ž*øÄmzÀÃIgßq³9Aù Ûž& ¹Û®­i,;ÿ,—¬•Œ Åêq\sˆ-¾Ï·Kz¸’›2ÎZw‰00ž/ɬ‡fÓHÞŒÛ #OÅéû¹pÍrY"|#Ò€fà—ï(wË÷¶‹bg˜ÊȹÀ­YÊ´ËR™ZüëUë“°¸SŽ&VÆ¥¿B©Aé ¸ »½åX{bì[ÿ®Í„=mjÜî»YX·7žŸ&ÂgýáÀäsð8@‚÷dè‰5ZøôkvP`sš¥ˆ·Çy áËh(‘ Á—ÕWÉß{+¸-íàÄŸ>ì½·žTˆ{Í•qïöãøÍRŒÍ²º†7ÇÚ‘G-uÅÙÒ s‚ñÖ“]°úh3¼Ò‰‡»3ÍéHÔm(ž„IÇ"ÙÛ9}xfF ¥?“Ý·¢ËŦ€Ù+Ü‚+ž…« iŒ>ŽòÆtN´³ Ï…äáû¸®L„eoú '´péúGpaE=ž‰6À<ãÜ”‘'Ô÷Bõ?øQõ[U˜LøÕÜäbǵ?#AmxÌJ‰Àé8¸)¦;ü¹èÂhòTDˆÁŠÅ8æR½˜¨F÷üYCG¦“»ÿxÇŽAwubŸ²˜mä‡s…\<ïyÝÞïàá¶Ñ}ÝûŒƒB‰k-`ŽÛ ø~{ÇîG’}¦\ÖË`PÙ]ÑËÒ wÊ0çÒ|S8oV|ÈiœÓ† ' þy6¤¾+…çdLú#¯g‡ÊÂ@ï\(ûtKn§Ã›«âìq‚y%+J7¾•Àî]²øØÁŽ+\9Ì ÏFÂ- i¯³ÁÁë®Ü?…íIx0A“ƒ¦1¿ŠCupX¯ç-“榥æ R/Ò/ ïUÝ#.7u Øµ–»^DÑg×áL{[æ^LvÇ?Á]>›áíc8¼ã>)/3ƒuhÈ>wÁýö²!ð§ó*êú…SÖmÃÊ'±o’ÿÐx -¿áä„J4OB‹§Ym˜ü8)q`Ôñü=¯?ˆ]ÏÔ6çÅâºÇiPu]Ÿî^ÊÙoá?±ÁL¶zÎ43ˆx¾­ûÔÆ"ß¶ƒíœ÷d[»Š`ÏX{hü1ªäAÜDzž/U÷@—-ε—²Ð2`&4øÈAÐ C|¼k-žý%í|É2ÉÃè» ƒ´vž!óç}€!³Ó?UÆÂ†½äÖYpÑ Mä9¾ÍY \á _íAs YÈ¡‡°>ô y÷ï(½…|8Þ„ O€/QÇà_îDzà€÷ ì÷Ÿùüª»í8`ãÊÃÙZõ3P?×APå§Ç_ÝÿL6Ž_Ç+j¿²êå9xH{,nˆ‚WŽ‚Ê¸X9áÆ\KF›?×I¿µ(Ä=[1V‚îõÞ¿YEûΊñ SV³Ÿ¥KèÁlþ¦Î~pYh[»*W–O%¦Ûîè¼süüm@íg.ÙÐ}è_uô+çRÁsN.ìwÔ¥Êa†ôĶVöç¢2Þ_'°ÏuÆ‹Êk1²\‡¾=¹ƒN7ÁŽQ/QkÚ]2Ç Žü=ðn¸ °øD3óˆè8ÞžS1}Qt;w½ý>ª$ýäs¯a–w©¼ú2¥EðZÏ-“¥‚ö¹×pŒâtîg¼¡zfŒîD‘7@k•|û´tdãϬ#ktÓ 5æð%™â£Æs\kU$ üVDgçïÆ®³óAæ‚ü§aN¼ªÈÿ6þ¾ *–ã»dYÔ)¨ÁÒ]bTì4ñHÁvÏÎç»ãŒÍÕÂÆ >p°*ýJñmP t7*ÒêcáÚ3ƒuM¹”= Öã§Ìé.ýF˜o0 ÔF¬dROXä´h˜º õ†æé·7bÔ¶MhW(G7Úˆñ¶Óûpj‹<^2|¿]Åö5wУ ZÊ’ÉÑüL¾¾vÖOÆ<,€¡×EÐož<ÄÿKƒK °õÅI\Ÿ3ŠYUçaú{4 ½‡Î’‘ø7>ïŸ}ˆ‰WŒ ìw4ÔìµÆŽç0aŽ(LþŠBw}T}ƒ'\n±y1Ø{èÄÅ,¿ªhR7‘F÷A\húƉrÇB3ºãqpÅÜ(æQŽFô„×T”Æ%‚¤¢É#¨€“§ž²õNzÔùÞ-¸zRŠ«N›E¢³Ÿâ”WWÐlñohzòµÝkñ@[?krÐQ»‘¨±ÚÜ _“G^aÞp+ÜœY$´^çhr»Úë; °: †º|!Ùë‚ÍÂÏäDÅüîrt=.‚ÍÔ$0ñ,¬s Dš\ˆúAAØ -Ã'MV$Ö.ÚØõ²] 60ÿ_í0Zê5¬ý{oÛ!VûfâÈòfv©[-ŽŸÄ. ‘†*ï lɦw8uëY\•Ä™•äâÚ–ÔÍ Üê¼pÙ’ÏxâòBeÇ*З,Ü}ö)Üß íÍ-¬üŽ NÙ-B\È;V˜ð™eGÝG¥½Šè=õË<׈¯` ]·BЭ𠽃'bžþ1V…ìÆëýl™ÃVT(ƒö =LÁ‘0âΜâ:tåcUµ·Uô¨k¾Æ;ÆÅ¨  ÏPTòšöîN·n¸ñß{Un{¨ÔƒKpâßL®1æ/‘¾[–à܆HÐy®N^Í@a}ˆ$¬tÙú¾µgß}†˜†\ØXù&›o FÆ´dI+¶?MWkÛÑh=ÐG&šEÈ©"ø")MÚ…¸gE0v—ËÒ«ëtyøŒý -k…wO…“!ê’¸LÏv|`{Ø-(Ì@§ë lY(ÙŠÍ!F<‰žsëjps,Þ§ÁؾÇXÝ• N–[ÑmÉ>Œk×Aʘ¤y…ýzh¿ªp½Àû¤Sf­œDÛ áüEUÜ\Ü 6zJÔ¯h'¾˜hE4Ö¹ñàï`úÆòM}רç’\võ#ñ[ â02ô"˯5¥Ö6•ìA¤45 žN;%cXº“ `Ô茦g|#ÀsFtIEàо[ðÇή,ÿËzÊj™R$-¿µ’.zFÚhÓ/ÎÁw‹ñ,óÆU¨îRA›e 1!¹YÛ?„> †{ÏÅ¢Ái2Üü¾7ÑÄ_'3á±Ãz\›í …õ-ä盫ÐôNçÈÅŸ­`¶È§iÐö¦ÔXú[§^oÓ…Q–©¬RM•Ž¿/Í«ṽû§¸Yü"ùtÓžÆ>ñâdenoÏ#5ôRK¾ý[À; HØ7;ÜôŽ@_…8k-ºH.N¹¯TÀ:éùßîØ{G‹÷ \Æ —z¼-.ËŠrɃ§éµa{ Dþ0üQÝIëçþ‚lªÅ 'ScŽÃ’ÍMäYš>íȱÄ3…§›Áüq8¨•©â¢ ¡íH3`rQ8üûOâøB¥#*ñ¸W¦Åçá,·26ÏÓ Ÿ(]‚CúiøRB G©€ù~]$yÉCÔ^zFV¼báá¤ígüžw—•wô±½3aãʯ¸¢ÑŠŽ]5ä'š›àä‘\¡iš) \²í½ƒ×É›…ø¥‡ç샕vTìøvÒ¼ ö)¤b¿ÜøgŸŠ'ê§pÓÓÏqØåT³ þ’$\V<FeDñÏ· µÊòû·'³/ŽÓ)¾Ç ËŒÁÌ2¨¬ṳ̀ ~¢É15²^G‡hžq¦›ºÄç2 ö9›âèNKvPõ(ûn¦ÆU"ã!ýR#Ó”é`ÁS Àü`8>’€À_ tCÁK¼þMŽ9ߪÂ÷q˜*sã¿~°"À–ªñüÃKÈü“9,¸q¿ãÇâó¨wGÏÎÌ SwC÷Nw<ÿf"~"ç©êÐÙáp(3~dì'-Ÿê°}3‚ÍU^µL•/iC_)ù³ÏîÏÐ¥¡rÛŠǧ߂w൳‡aQÞyð = åûwâ¶¢ ¼YjÉy‹ƒjì®Ý(A.ôxro!oý7›¾¹+ ×]ˆÔAmº ÇYØ.öB[?³C¬QF|5ͼ& ÒóF¢úˆtÙöP8³±ùºo0Shñå‰ä›Ê8eÎGæ?«]ÿ& ÓN”à@wȽ“äÆC{ÑóéO”9Ž?mÞBæžÇ)i‘ð5t]¿#ÃFf ë9Kx@Ô¨Aàò5æ é¾îÉ:õdj©.ý8y&w’:=5Xoû"‚<˜äÛ7ág™Áˆ•P·í$™ûL–&daö­6¼R>„ˆìÍ O·ØÀÙü`ö1)„ûw%“"“ ¼«x¦ ¸¯ÌÏ)‘IÚîäó˜ýdƒ8Åe1Ãá~ˆ$¦ ÏXAôþî´3[siijеwÒøârôG’Ÿ’ºO¬÷¥ð7oÁÀÕå,ùÂo2Dk úL–ÚnCoIžpã0µ@@GhÓŽÏvü×°V,W†(壘§¶ –F†1¹µŽ¥ybäIдæ1 ¬yÛÁh ÊSb53°úöWЩ¯¢\“Å&&…aOÁax¹{”ÐvÕ¤®u [·s?Ž&ßøäq?åíáùxŠKÄr±G_“¤kÒ‘ÊEp|äbŒx<”Û®¿M›è |tv5õ)ï…Zߨ>´ tß’Áká})dýÊñtê\W¨ðÒc¶ÅÇðkú+žHæÑldtÀžùÛ±Õ9#£Frâ—†³¦ýã¡-0iE.O”¬ÁIòîôþEKž›§O#­LyÆL?æ¼@ŠòwV ×FÆ^¨Á Àõˆ{:Þ“/óKeË™ÍÙtÿ,^_íᢨþ–܃·„«¡Ðw&èm.b°p2oP>‹æk±bƒ²¢ü4VÏ5¡Ê2ÆWaÌÎ0íZ²£â:g<¿P…ºŸ’‡??^¡i$¶½YÅ/[ªãoÏ£°áI+K·ø ÉKÂÀÑXœ~þ¾ÌxÕß¿ÂÐV[¶cg(nx2†=GC–@ߊpxÞš…ž'²ö G˜Ÿ]d­fA)ñJ“%Ómع޳ة¸„óï­Ì÷ÁºæFÌß×Ç/¤Á•ÒøÜd gVÀá}ÿ~+ØÔ¶HR;5glõH!/”yñ—q<æ@(ŽÉÏÍmÇKsñ˜l=6xµãÏ…’pûµ¾]#Ü#%@#‡^ÜTÒRžËaéܳD!k>ýgÏUf†axÿWá©m7…§?‹€ÌôôÍ£m×ëZ¡1$X¸ d.ðýAÅyP~çœ ðû`Ý0q¡ãÝ»@kÞzv¼^‡ß(nÀü›u¨>pŠÞ‹–ŸÌ¡¤j>L™ü…Ìó¦®e=äu½?–_¶AϨ4Á]¿Pr8„¿ˆ0$Í­š|ÎtöFSÄ\×rÏ»ßØ¸ÙðÑA™æüYýûãÖ`º·Þ½nçÀ¢ò.Øþh°ÊÀéY"¤iÔäš\ÿ!¢]C:É{2Žd'­:ìÜÇÎ;#M<¹·ÕN>PüY‹‰ÓæØB-±Ô÷ËШƒKðä)~GhÊmÎÜŸ۸âúnröîo°ôUäS[úÐvó_¸¨Jݾ Ÿ¼aðs×i.§åÈž^û†'UÆò޵¶üµÅ×7±§¿€[Tv³^ݸ·þ ¾ò¼1¦ï&+kZ*|íbŒ.ßL¨º`¢phj“õ.Å™ \Íž†õ5C#äaH±¿ywºüùOsÚcî UiJãpúXΑ5¾ÒÄ¡ñÃø#GwnQ³”Qñ`:vJÀòÅåôâ}_nóÓƒ6 +‚ aä½–¤G.í L¶$§rßß/pÚ)úÎüÑ-ÃäU_Á Ñ Ë÷TÄÐqâ´ÙŸwž&véñƒÕZäÒ/GALi7ST§*£—a£pAýÀy(¿ŽUYÜçµm¾6…i¥òòeŠtrZô‘â£Åã‰y8rR TMœÏKJð )1T: ëöz˜Úá;ês³zÓ ÊAKk.O2•æÃo™îß…xnK^ÑPÂ~GÞvÁ ŽºP°–ž&¨·ýAÝ¿ ÖeJÐzW—¿ 7à!§tñÞþö‚œÖò«†:Ô¾­̤â9„Úg èñÆ:’b!jþõ¸~ü0ömôw81D] n5”>èEL=ÃícÔ߸Ù05߈p¹½ôIâàýÑ æÛ´J—O®Kžžº…t£%\þ>ƒ V|ÿ‚ã»¶Á¢ïï±\© ç镳Ñcpû5ià“'Hy„}º^„…Ætʾ]‹-xÀç:¸4„€e¿*N‘͆Q®8:æêö§ 7WF˜8ÆŸÒh!‘?îÉ÷¿6ÅÉËñ‹ü~”œ£C¯Ôïd§Ä>b–­•<ù z6/þ‰¨åP„ËÞXÒ.¹lîî¶8q2ìíò¤E+±ò•>PÆOþ+!Ì×=ÜžbÜ‘¨ñËSͶBÅ$§2È@u:ÚëC÷á±Xô<‹×%èaÎQ&tXý 0Ën,ƒ+YGˆöïWÌsƒ ,R¢¯´,©ï;þ`+à‡f8­3„>my æ‘L}É!Aå6^/U`_ù@0ÿ®&—°ÂKQ8BÖŽ®¯hA¿·Wñ™o8ù½l²Ãç÷ÖSÙ9·@ra(¨m×d6ƒváq|x„MÑóàK~.¤¾Cu ðßUÇ/ñ›„‚õ±xxl!Úo †Ææ°©I-°ä¾ñÞ£,¬ÿvÉ™aÛ{_®5=Œ…ºjÓ­v)”­ôÇó³È…ò4³v%˜n_•'ž@ƒ×RêÚ/‹tôù³º7(ùá.çnIϾkÇ1e*\¿|/È<$ßÞªóùòrô5¥ûǾhýOðþYÅ.ö‰Ðs‡JØË}ïàÍÎùôã އÿáâ¤\°ý [d$©ñ(O¢÷Âw †Ó«7ó“ÍÊÜA·.NÑᇠ#óA{ÄqüòÝ)]Zå ×Ò‰wG¼fZ}SÀÏ´‡L_‹?kÝÑÃLÞ–¦Hn dš~Ž=x–©ZN Ç”œ0áP:9Œ®jÓ事–2ɹ%àtFžK9«óc*s°íÄ~pm¶åŽÏwCõ­4Á¿49ºË잣”Š5óÑ|Ž"¸ýùvÐrlD YÖóâ/ùצзW2¬;ªJ'=¨Á m)ê{{µe¨i¯«pìŽ?æòJõ³àö-.­yÇqÃÉìkÄ&ÊgAô%7´zÍ•6‡à¾ôá`s(…¬ò’¦K•¿câ >k£'ªB ¦Þ c’Y˜²f-®Ü¾…N2¤bêÇYs¤,1–Ÿ’@ï<?ÛhÈÔ#|TØßUÂ"«鎫€‹ô¾T Œn¥ÿêÁñwÑ[+EöÓ«_¨WÞ~~Xu2·÷‰AßD3pýe¾Ìáí*ðµó/TjͤÅi2<òí8¾ãº7õ‹Ò¢Ov|ÁÿŽoÎE¾A@;b±Ä7Š»™ |‚Lòdæ9Ð×&C‡þîÄ#7ÄèÕ3ËAï^ÓÏÁcÅ÷Á6ë¹|£y=¬>“ Ds5¼Îë„ÖädŒ”ɦžB«µzØ¢¼Œž¹ÓN¥ƒü¦ „2àÜÍä›èrXwA†*)D|ÑLb!íÀtÞ²× †wAvÕŒ˜êÇg•8¸^ÛÒAïÞ¥ðk!…ßV¤Á,•Ú8Ë-$è¶Q†4ý­.kîÁνàˆaä¥TºmhÁµ7Ôù 2ǧÆÒ”1øÐSÏáÕ"6³c(µi m™¡5ô7+0j!ÞýõX³“À«C|}•Õª€›iþÖ“¼ë®l ÎBkYG6Ä/&»}ÅöGXâ¶ T|@ES—À”éØTPB5)zpÅdÔ¯žÄ»w¼²RüÄ(Þ"…·ÊÝ…ÞQ¾x§g=r³B…rTÏmû¹Ö?ŽÄþôÒúr¯pf0°'mÊ.¡gU3™<4Œ[y˜rEßhòT Ò“Fòs§2G§0Þ6M§;ˆ‘²SŽZaM¢ -“†=Lð®âlúwÛ .¸ Ë×…óÙ»¾ ‚+qd:±Ns'·w¦°ªɸþÍ7rKF O#YJ™¯o9(=ŒEËiÑø)¼Xø©°“9Y&€d§¤@9ê¥àÞº5È6|A›UäM‰&³‹iÇi‡`tÖà‰ÆÏ^ 6/3้]°ðš€„ qG5ëÛxÿòJì<®Ç^íÇšÑ ˜Pê‘£G};ßyCp]s,ßgÒÓãVÂŽ‘'pºíMœmãÄ}ž¡Xq1´ý¸Þ_Üó!£6"‰Ðå5n9˜÷ò4wÀ´ÐVôÞØ„Óf\ÂY·zðdo2lG´:'ÒYÉ8þ]5n§0¥u( Ëmmí«¢æJÞßTâ)avвÿ+ö( øäDm>ÂÿÓýu´v³S)<¨7A3Âå›!.=üd©Nî6Öw¼IX™ü„ÌSÞ¡Y[þa¯½;¦È7’¬Žýø»eøn9²#±9%††™DCî[p“~ ~ @þFÀÆé[@B*«ä÷À”ùgØÓ ×ñÇ®R¦§‹RÏmùg—Kàõýz:‘Dû|F¥ ½ØÒìNUâ”@vŠÍ{Ü:³ÿÓU«4(ÓÀÊ}ô Ýy–6i—ë£7²ï36bϽ!n2 *Þ`ã¦Áwûz´ñÕç1¡cÝM(™„Ïį±§ Vtã´8>$Ü”7fûàðÏ…Oœ:QÝFŒË«G9M¨MmH=váæŽ‡³ºX²¢6íF;#–B”O[„ÿvÊД‡‚³22ÌrÀ 畦£ny±Ë~7Ð~†ï>àÀ2ĸ÷4®ÒäHWDïþux÷§3y_üÕšF™Øå …¡‰7Ùc©!ÜÛ] æêãéÑ·aÂ%Úû9×:jàO۩ฬµ ÷àè-Ñ$r½8w\+CUƒly÷Ó:v}v–¾>ÆôúÔlØ'~­ô8˜ð8̰*Å‚º¬æòXæÓ‡«f,$odð–¸9šIx… ›æÉeRúa®ÑJþ7,Q W%1~íàx(Œ×\®À•M5d õ7_JÇø5’u¯ÜhÔ§ÐÓ|•´’UômRÌŠ„Ë)WšÑe¸Ç&=ºÿ™,·8 ½*üÞÚ*ŒœÂÎgï‡Ð•ÛhÃyE¼Ú+Jž4xভ¡¸ÆNIæž3j‘è$q'vÒÓ¡ÛèŽÛ´‰šñ ô4:äO%Þ¸ß@_'Q¥Èh>J:5}¼áMü?ùÑ»Fx§æ*ÂÔ3 hT£vCw±–ã!þÆ~äV _¼”òã{ÉäcElÞ7>|åe43øI×ÄŸìM+ }ÿ8²ëžgñ—vqs+1zL43\G`wj<¦Ì_Ÿ—cGØa¶Ú">]Má™»Œ…×¢@+ý6Çç“Ó’ÕüÛœ“D¤m!›>;‹Ç›Ð«¦ïaH½3×½¸‡,çúXäÞ +ðù…5$°úZ¼ÇL0oÓö¾È¤ƒ±°cëKÒëƒa®O¡üÓ$Üë¨L—ؽő ®›CÄŠ¢ÉU£°ác>™]HìfMÁóI/þÚºTÎà9óÉØÀ7É´à¦å¹P\ÓERëöÒ¶Ëi«ÙŒUã÷kÙ„Àgøôî°kÂ,Cz6K‚Nwñ€ ‰!üoùjîõ‡ÿý?–Äá‡áCxe™)îZ‹:Ù¨A&ªËŸõÍÀ0º"MoŠV¢×/xàÞG´×éÔ ŽU¤guèiþiÛx ™fÎ}¤¼yX¼÷]pA`R*ÈØØ ‹¾aôÔZ‘©³°~¯3g¿ ¢{×ÁFC.{ö<̩ȧ5çâÕ =¬¶.sZøèÉ™Œx6Š:™pÏ*}~¬Pœ ÞKò¦žèq›Ëï:&³Í™ÌìÁóîÃ`­Ë#¼|q& ýèÄ'½·Á=Q‰ÜàðoRäSjãôø_•-üÖU&׎Ëdص³M˜=û&,>º4àæÆg¨Ý.Mý—æÊÅ’é o˜Ýþ=0m#Î9ŠâR»ÀçÔ¬u«Å¯Ç+!‰^ƒdÏ9<²G„þùžÊKÁfó9ôß”N¯º÷<‚f’(Ø”áΫݿÂÊ?ß…›"¨E»ß×CØGbš*m\SÆüAÓE7˜Ô2Qô~)np±­Ø80âÿbn Ó¶ó¥Ÿ'Q]ÕsPnàŽgß%練?¦np£q–JÛaÊ=Äêî¹l»ÃFXgZŠv¹Û©¼H)\)˜ÌLj¿$^‡ÏCC‰%T95¢ím¦?EYs¿ÔªÁû©ëèíÍC˜×Ê-°|Þ^,×çîÂ¥FtêÓ8»{¬ý²„¿ûw—ÐÕ%:dâ©CÜ|„?îÍ0¡M*àùŽ´µMâ‚U·`ò0#2éédºÏP*\vd×߇ÛÕö‚Kà?ô€ˆ8·ý ñ«}à~Œ&Ÿ›ºšn~­Î½ÿ†aó×{(º¨Oü¢ÇyN±à-|Ã+G'ýqaÜãô>Aë„blü6+´éË©h¢øÔç¢Öohv§=³“xòûh~¾Rƒ‡(d }ˆ]©É•ê^ º~C‘*TÓˆC—ÕvdíÚýlÂÔ$ÌјŽ;”»ùzv„„Ó°øÑa [ñ KÜÃñæi*YQ‚ÿiŒ Ncì‡-üÓÓ*k 'ýö¡lÍ^ð¶Ý‰o¾¢ãüç¤_÷ÑÒð5Tß›/TŒö£âîcÅ®YÂÛfYÜ­ûÞþàŽºEsùí~í+C£ð¨+Ù­cÓéõ·´|¸· Žý%çq“l¾°Öàs&Þá "hk‡ ­Gk:6ñ$ï €-/ïwñÒS^¦ÇÒè»nôÇ£xíC :U¡J{/c_d*~í)Æ,jÂJ-7>]ÒO ´‹g+èÛéÑôÛ·Lœ]]ì±*9º=5¹IÀòİê$Û¹~.¹2¬€õÎåÁöKȹPÿŽáy;q_ÃVÚ;I…|XO6éá‹;Cˆ¼ã8QîÍÇÂaܦP°`_ž„WjØü߆TiÏX~ã» xA&ï?…rå±,ê|ûàø Ü¢È¾Ó÷ÂY½šõžh‚×.uá–GäÁ2kª‘??ë…TC7úï³5YäŠÅÓÕ§O£AG&¨”$«„$AˆÅ<Ö/Æ‘À!_Ø4¢Æ×<ˆÃÕ1$-KŠùûˆ°‰Ç® ûûBáõóX·-¬Ýeö÷ò¡Ø ‚ÃaI»(œxäï^¾s4(2£Ë£Ç åŒ~8>³ŽÎë™Äò5N`wëG¾_FKWIÜö°n\rak#oƒ UîRÍ‚u·¬¹v¯V7ÊÁómŠô@§"k Ø ó7 ‡éKÚ!qsymI[Wqˆìõ»òrÆ)ÔÛïEßÍÍæ/§Õ¡®sŸ” B'ünÇks®¸ë&Ûó¥òIdì“kð«Ø Í‡mt± mSE×@ †_¹ŒK7Óàp#t|šK=ÿŽãg–cãÒt‹Õâk.—Crzð'aõoª¡lÌåÉP^êzT—oÔÇr\¶Ûÿ¨XaYè#¸_#Ešœí¡ã³íùÚC¬¾ qgo<ÒÉÅ749yÔ•ìaüçë\qD‚z,ã½óùMwahh»8«ÅìÏL¾uì($6û`Å-0”*™‚¡üêÄQp­ š¯áwe¸á6’_?{œEh¥Ül¶4ÜƼ+Ç[·»™AF§pÃ÷û f”ļÎ(á‡jiZaE¢—eáKëád×°X”K2¤%ÎV°2g€eÏ™†²&©Ä`[®˜lÉ-J…D9¤ 2üpΜ±ì‡V5Ó­R@ñ8ð›õF¸6"l—„S©®ÈÖñ¦‰»šÁ®6ÅAyU ³žGïZRãü;И& w3áv¾G‰ÊÉ,Z.&Í×Ùµ¶óP7T‚vÝý#†C†Öb²Á .ú9Cûñèo³Àæ…ÃÙ6ó`ìÒ} #ÇKò2 lÿ8„j›e‚Bо×öÁÖ*Iêh¿Ûô{1¨ö"¬ÍïpWGLœxÆþ¯Ô€hw½Û·Ö€ÊÙpؤçÅ7È„ ³¾0É‘¥¸á±Þy3 ”_6Áã­' ÷¸ 5PäÛ?,d]ýÀqñjüí·N¨œtšgEOâ‰;ç#ÑL w²¬énƒbþþ»]›sÏ[ÐWƒé³+%ÌEý·0{My¦µ‹–ôå‹öœf^.¢wõìé ±ôBaÿüªÚzÔÑÑœž3Š‹^úÂ+˜ÛŽ}@¼½¨Žœ1 ¢ÿÄð©BÐKk<¯ÎæJ+¯E¬|Í\§…óT(åû|÷ò¯šoˆõŒÛ°ÿÑ2sí<Ÿ}·˜ÆH]†,‰·:?ù­ÃÆÅ×ð?¿çÖeh»X_p>³§‡¦w¥‘¯½2˜Õ~ –Q»ê|¿u<¬ÕР~l˜!ΩÆQˆµtå#;»ÀÂМo«Ôç⻋AÁ»k®8Á½ïݳÍáÛø¡T#%‚mWç·¶ÁñÉì³ÞQÜyî ßm«Dµ%锃·áÜl±<~qû0îÇ_¸4þ“~{›MÕq¿¢Mò¿æ½Ãéà1›ó«šœÒÀ ¦_°_±ˆIêÙQYè|ØÝï1íñwɬš ´W‚ wƒÏ‘ëèÃÎ£×žéØ¶²™å®u÷™ t“XÈv{Ï¢«Î¼„ËÃïý§©îþ¼ôÑÛo¥¨èŠsÔéh|ÎF5BvNFù•Àv´,£bQþ»F„k‰ðíËçý§ &ÇgoÃi³Ì„f¡Á?¶ ËžUAÒþ©t‘RÙuÇÅ=ËpÉÕxtg´z‡¡â² uùrÞÓù“Üý ]&ò›øÔ¬P\7‚©¶Ç;¹ ¸Ý͇¨ÿlG»YF¸]# D%aƒx7. “âLî1ëÓ“¥ž¯®±^?En©ìGþ„D£Äx_¾!åtíe¥iìuj?-‚±ÛÆÑÕ_UÈÆº5´éc#DyVáh\¾ó4nwhÃè`sÞq8ÿ¿ø´}š(¾Y—¹aé°Ë6ú•Õ©¥G8ûr0´Ü«°OÂÝ~ü öêCª·@¸ýJì`ý ÿ"­°Vá$yÞôä¹×3ExÛî¹<ëù8Z3"úS¡Q‘¼ÃN5ú]g;“Ňÿ *‹»1»Ö_xT¡K~FàþËKVâý%ÎØ6o þîÏÓ?ÜA…šÕ4{e—Ñ^ÆþÓ[ËZˆÒ>Ÿ]‚”cLs›'Ê»ŠÓ`Â%K'Ðc:ºØ#s¿J&âíï0KR“?joJ_á±¹¬ì•žp­‡‹=Î.3 »!´0¨’¹¯£oä?E IL¾—Bçžç—Ìç@iA;¶Ï‰" ã=ˆš_ [r N%)R¿ÎðdJ-;˜kþ(pø)MWýŒ†¿+¤ÿÓ1ã¾çµ–Z 2ûí©x“:> ££uC`ìË|\]È1êI+H³àQüÅᅥ9ï¾ §Ç)9æ ®ýYøGëû K6àObjÇ?UKÑÿ¼¡¯šgé€Êµ¦¸ÔJ[`3/Éê|l+…ëULøÜˆGš)Róï±Ùʆ×ùRW¡¨›"‡~!,°ôšLçäB¸(õ'ýç S1¨($áËÉt˪b;Cz‡­¨O ·ÊÎ…]ná‡Ùþ`Ù*Ÿ:ŠêHÑd‹B8:} §ñ {Ìf‚\–)î-3†-j~*Í/u~‚GB9X3B‚Þ¾×Ì>*ú‚Ï[oª{[7_ .§†Ó;ÅRTY|5³ôä›U¾^ ‚b)n¼Ùp>}pú9–H©£Ðí'Im³æî†c ‹!E»qç‹ì’Ñ, qÖÅŸ÷C‰÷Œ Ü[§ž ôøäñÛ™÷²‰0sÓDºì(ų›¦ÐœGŠÔ¹­óÿÕ ‡ bâ£ùèœö‘ù¾îC§îS“Ý]‘ìÆõN¾º¡zÆ~Clû~‡Ô•}‚M¾¥8ÝÌÂN*CÉ$7¨òW.çšÞUp¥^ê×qòº2 lÞ¿ajúàíÚßh¹;êèE˜ŸV ×; ø£Ú$¼9á3îmW¦ÁÛ´qïƒ@î´Ê‘?õ)e‹üçà‰Q 'm’äR•a„è §(¸Î6¬{Ćh<‡.»!ÜvSÜà:Ö—3°ªrØî¹Âh?ÌyLjþ¦áã–H¨ªÍ@Ŷ¸{] [\×áûû`sÿ;ÜÖ`,*l™±ö:oSFyAºWšÀxýx/õ:^œª ¯ˆßÎ=¸ëؤïQ‰MHF‰GD½×2ë/âÄ@º0R/àý[v Æ_? º÷#ØÚé%P´4V À`Ü\º¹*gU¡¹ÚŇs¡l¸ðбð<[ˆç÷ÅcËÅŸ¸t²å^Ãi·Á=tºªf¦Ã¡?I°„'ÌøåGÜÑ*)Hþ |t@æí`½ê ¢;W’³>JO1ÅrGÖsFú?hèZú€U¨eG¿’“ž‰‹¶+Rëhe.-œË')Ífί‚©·_(QÝY@¢wÒáû†ƒ_/Àµ.%úÂÕ ú½ƒ¤€Ý>^†Ïs׆êUÇH¿ñ5r8 v$xÓØŒC  ÷”|·ŽO;¶ExxÇæ×øÉì é2ÌæÝÁÒ¯4ŠW^ÄóAoñ§ùk¯Kï¹âúŠ ò|ØÜ& ¦­xüßW8pN„ž|:’J‡ô³u®5ðTâ¦$tY–‰"óÏ£¶â}”‘yË&›,àßL 1îô¸²ó”]ú ËîO&âPE¡i4¶~î@¹È&ÜŸìŠ)™°×y)j£é¸ÛSøimzñß/¡à²M ÕÕkŸ˜ò˜xösŸ æó¨]{Џ§aiQì.j…÷pŸX½àûIüQ“&¼·`,<|³E¸íb®ÀyË)ü Ô ÍèÝ5œM=ž6ÈÏ€z7‚&_-½"4G$)ŠÍg°éI ¸ÌèG‰Eál܈<¢ü=n­‚ôgQó©) ? ¿…kC“ÐðE>“–ôÅ5gÕ¹tøS°³ö^T>¶:„-H›H_uSåBP—m!çU‘õ[Ta·Ò "ûr%,UR¡Áš!Ì¡/ïã}Qð]V™Ç÷_#ñ¿ÆRÏÐÑ|×£aüer-ö[‹ò¸k^®¬Ž+»%áý¿C(»%Ž~ô°Ç!…ÉœEó¤ÅU‹.oËÆ À'˜|w)F焱¯jÙu¯j<õÀ ÆMoE«:!glê`/ÑçêGdAzêa¢ÿ~ÌÎG©¯¿ 1û(þUAÅò¢përôfï/àŒ+8RW†Ü^ÝN¶îÃõk•a½ñi<ÿý5–mªÀ°6kz\Óž?WРë>\%3ã0ò{Ü;’æªY èõ×8éç Ø¶)Iøáj(ënLÆcÅùŽh_Ün¾ØœC’¤W‡ÆCÚ0iî~°èù ÓŸwBþ×ÖÛ«SÂ!á¶îødÅüÚãÈ9Ò\ªL“›‰y'=Úº¥šH¿)&ÖxÉà:Ñy\,,j}‰o&+CóAKò=êNÚÒŠ¡×f+Ó=uœÈo8OòšÁ^9{Ø8‘—@üË6¼¸.CЭ­ƒYõ¹… p?ªgÖ¦eJãØÓÑè;v$ÿ»ò J«;ÑÌÃKA5ÿ9»•OVƒÍf=~DN‘Þ‰m —õ¸äÍ9|÷ò6ÍïCaåK?}9¨e¬¡sÒÁx‰ 5ÈÓ¿³UøÑà—£¿lNºÑUñö0ul+Kç“^Ü {}«„;Ol@ï8QZþÓ×›ërÓ¶Pu ç^|Ä¿qëz':;T>IDákõXøÔb޶f3ïÅFƒ9¼‘ÌØ§ 3&∅<çxèwÎÑbïamŸ;?!_Fóqlù1Ç×÷Á0ÍßXû=™ÄüîUŒÛÎM¢æïºÙÒä&¶æf"ÌúN v@°‰8ÈîÁÏÖÓx]Ée"7ÿЫGvŸŒüª%¹ù>VÝ€‚ðr›)•»ÍÚ´r¡öÜUp ­fƒM §Aóå <øT EÞ.aJ …l†ÆZX tî9Kþ8â°Œë0J&ƒ ÕЧ‹\áCÐR ˜_NÓ~Âã #È|ïs˜s!W°­ªŸ¸Š‘Q2êÂùÕ°ÚáNÙ&˜¡!Šf¼fñžÚôx’Œ?&NÔ %œyòv2ý^4L¹>†¾)¾å4hf¦2¼u×MÓ|¶˜m·; #û¯À4§J&K£Äêr¢n ¸$xf!¿¾1Õº´¶-ø¯±Æ|ïa8«Æ;+âûalWèÁ]ÙÑÄØ  Zµ¹Y€oä‘0+6²FÈ`·•ö]¼‚¹SÁy¡lX^›§@ã³ØõÒ ·ÕÎg[]Ø£(JÀÌ€†˜ÞFµXQþs­&M“£áýå,a|/‘^(ÏÏŽ"ŠÃ?çÁåý Å&súìa—Ÿ^Äì=†ñqëhB:=ŠŸÚÎ'ïÓµåF¼³÷(±¾óYX{µºNÌB…÷º´@ð•ÜSF«ò°…ü‚:µ\ع’?‘ÀÔìkDÏaA¬)®9€«òoË–¢b•.>^ Ï.‘‰ÝјîdB>{Šqc“ ,ÍdÇö Ÿ¦\¥$kª¥¯ŒaËs`Döü Ëã:–`YY"7L7£õmv(»úùhzû@W+‡u+R˜¹Bªz·LIÇÙj°? DÛþÀW«_‚Š`3‹¶À¹Í\~BÔ>˨5ÆÖKÏÌïÁº°FÇcrÌÙg>ä[éƒëgiHÛ>þIŽäÚóÅøc%Þ—YËv8¶A#ØÂ ëÜ6± TšÌØ»õÌ·z%Ö'IB\d¼79;èÑo•_Áðü%Aà×S@‚³ÞkOÁxä:ù…1Ôjø€JùKbbXMŽT%·Ù“I­“Àôœ'Ô$\%ÜÒÐã½'ÿ‡ãTÿ8ì͈‚Ö ‚CâA¯u5›©Ö F4ò„,Ïþ‚5VzøŸ5ÅÜðö2k>ß<Û:·ÐãÝèȽXø†Æ_…±¦×‰‚ª/ˆºúášb²dŠŽb%/¥ÁúÅ"8²l £;w¾(þZúD=²Î_^…ITÄ>XóYW-púCH²Û 7Å—ò¿.ùñ& ÀrÙòý9Xì-‡‰w¸óéhü:‚Ô(ÏÅÑL ´ƒ¬h¿ýÁ‡Ù ìpX׳Ès(ç =/Ïàwõ|˜ÂOØótM Ë›¶ ïÞB8ö( ÜòÖñQ-$*L‡/Øeê³ÃMÓ1 ½¨¾¤ˆÐI¬0òs¾T— ó¶ŽÃé’|ÏŠã3ú-¹j‚ÿù:OþT[:J€NÛˆuâY˜õHÇq¶Â#¸øh·ÿ6‹»jÞÄ£S#Ù"*×Âwn3è½½ øÓlöw€èôô´–7._FܤÈdãU0üŠŸ”£klôé­X± n>YÂÜ*©û“`¬u¯Ðc_ NÛ¯®%m˜z2?AqeG:>Ò‰i®wÁVÿÜ4Úq²ƒ,™-Ã}B´Ø˜c&t“±¿Ô uó&rçS„_ùzÞæâvÑ@LòMâßôŸcú€)==QžN=îËËçˆó¨U:Ç3D¤™êªÑt‹¶Ã9þ\vŸ•œû’µyÒ7—lÉd-MÞ¦qŠÜ>”vH.å×Íj`å¢Mðe[M^úQØ‘X»`›-&ÏßiäcxÞô_8Äߎ¢Ýªötú¾Cܶ ŒH´çtBÅÁKd^õZ¨]3Œ…V½f×e´yÄ\n©ï›Bž4¿›-_> –½oà‹ÆèBZm.¾>¾äÄçó¼„4åÊQƒsbìpQ·ø-t–¹CFJ/>v{(°s§‡ÃUàÔ )ºj¨¬ØêŒx»…7Ì9 ßßcQÙ—p"Ñ£’×3yG»Í§ËÍ¡âã}Áv ¿]ô¤—ŽÞÁ¿÷3@£án/k»œƒÿ×¹¢Ôz†|,†ÿÞEnÇÒ‹¶ÒÆÕRèõ2ŸX$ƒÃ1˜`<–d›ñB™hpȲ†…¸º8ˆ~ÊjqÔÊ×ã Ú1tžÊ9Ð)yåz¢ Ýa@—Äž‡¨ç àV‘Þ²‘tkQ!}•>—÷_?Ë®×åÓ±º"Ó3qú.´ÿôšÄ–÷àTº1e2?µ6‡RÌò’ ¾¾Ÿq _Ž41•õéÜöŒ ʬH@•3ÓI÷¬}ðêêXÚ"^¦ç¡Ï??þ0η|¹ këÃ`EÌg6¥Æ€Ž^=ëÏtC½ò^á£oà:B”>¹~€šñtË „ç/ÀEã ñÐÙ¬Šá«~eÚªD®Ñ”…‡×©`fÄuXðj¬Œ“Å´`oú|x>´ÙÁ‡ç5Rfðg6?3<»ŽkR•íÒ´xFüYÌaï<â)³ï»Q¿Þ‘ôÉxk4²3b3•n‚ù|#êœö Vï£_ â±¾½hEGÕå¿_½€œ;g{ÝHÜ^svþ§4}:;¸U'¢ f€%«CïÓÃ)[¾vT§£7ýË·ì×b¶s¹fæ<‹1>_™BÚ(ô7?‚-;Ñõ¡ûØõä‡èözÊÞ™†éÅ'Iw‹ >j­í ÒTkÛP¬¥·>'@ƾ?ÌðÅLºp~=ìõºfŸ€ãÂc¨Z6Œ¦XJp;…S(ÖâGÜïÀºeSPÂßÇ1=C£Ûº0ùS/×xçÇsH¸udf‚Ê»q`ÆÕ¸ÒšW©KLé*q&ª¾gâ¬]i°R¡‹fn+SeìQ^1Ù§)››Ðã{ÍÕ¨Û˜<µMCÍǯ0¬0Ís,éh½Ãø¹O“(mÃe¹~wŸø±¿,hòÕå|…ž­hó¡aYa4Sd*QK`(o?kËôÈ2*ŒN74­µ°SðJO‘k7ÅÂÃJøÞL§ñ›*ÕÔm´/¯ }Ê¯Š†ÀÁóɸ¿ã¢ðëh^Nsψáý“—0àzàü`-]U§ÄbcÔq‹k:Ô¶ hß6yþãž$§âb³î³”‰6lU•<6oþÞ#Žþß zÉC Úæ,ÃïP7V€Ù-YvS–ˆ•+£¤|2æ|jcù5k#¯f]`ª…F,²c={˜&F·ˆ%¡Dš"&¬W%“3Z`ëìõôýœv]”Y„&aºXšÿÛ‰>ŸgîgõÈMÝ­ôáÆo2ÓŸ•u…‚yr.8˜ÆcÖŽ4Ì;d–Àv°Tþ¤s¢³Í Ž¡æÓ!p\v .ŠùŒ2ºŸÙ‘ð½`ÃsÛSõ5t·ÂL6oÔ~‰©ÀaŸ84뺃ö<4ÜB'‰Áã‰sñîÞHrûž+{;Ë€Þ=÷ïkËs5㥴`k9v³'qżn(‚¼qÛ ”ÑæA|ôÂ$9äŒ&Öô岩”³‡[íö‚c^M7¡Ž¯Öö8Šq^õ™E-àÿyAÛE,ã>ÀsV+ów“^A\ñp\Úp•ΰ,íç“y{Êšhþ‚ýÒ¶á‡sÒѲƒÐ¶s¡ôïºãŒ¯µ5‡¨üÝü¯Ï4¾çÖj¸¼÷8½è|4…¾´QGŠXµƒÝ–ÒÈÜÞmÑ€pë ›ïS(\¸[Œ/ ‰ÆN Åú8S‚š}–luÉ;Ûïƒwéœ?í:j¼ÂZ…s2|jr6ìø™ ·RhÁD€!_)}“±†z•–Âî ï‚UgT`÷MeZ³ò>Úó†™h=ÃäóšäúTo”¹-ˆšbçÎíèóF”µèú=y8àƒ–›Òª™1ðãA:—<þ¾èn‘ý|NS—xb0/(rïÍ;ž\¯ó2iöø»×Í€,³ð×5Ãű¢t0.±è²ã·¸¡±Í}ì†Ýx¦õøO_Š6k¾ èxs—Ès~h÷éHÿ‘Ôõ«%ÛÜÃ}#¦’kvÆyô@œdV€T…,¨÷á³ëÈ$ëR6ßw?©—„ÚW7Ú¡æž:ŸúÊŠ’NþÈÔ`c‡³\“?lŽC|ÉŒÍö$‡®]Ãs^2ƒœ’”)‡âFÏbø:qÖïPç:ª¬äŽWÖ3¡*·>Ã(7òoÓ_ø¸©ãK” `ÎkòÀ…ßÍ’àâWu Äç5*¦Žå¾4A­©-XW&ÂŒ)Ï…äÇÙ°%UVˆý}™¬)G"f%Á‹Ô¯¸ìªÉÙ?EÐ?oåi±`¶EoîË[§hrÛi¸Jü~v8®|UÆ®U)ç=ùñû±¹}ìí¶âûCt©ß!-êùù v¯{‰icÒÆÅƒýGV­Î@‘¾ìt«:¸Ð‚ìl¢Òpì3^o8ÂÒÒ&ñùsÄhþòXŽ2£éz膧ê´Ù´ v±&pNР—œN£Å¨×è±Ù†¨ÉÒúF9^÷<“{¬¸€·+OPÇñû›!|ô|Q¶è—*o6›DŸGMEñäXz{Ýxž"ÃÇoúª@Íu,WÈ Ϻ°÷IžÚüâBù܉rtw‹7\º›Ø¬Hø²"–wë þÙe‹yíøÉŠßè¾{5l)FznϨ$3úexºò10òþ®g%iÒ3ÚÖ‘’!½ÐºE™öz®¥›c{º‰âÜÎ<Á*cúµA”ú;†ÃäÌÛðcë,ª¢5Þýé,ܬtÔwyÁØ)/áFO>íKH¦·f™Ó‘û…vgšÑÜw%Õ&€{Ú7ÙÅÉ}ðrìÙA¼1’;ÆŸeßQœNÿdª =»÷ãRËqøNŒÐg:E²Ô«xyk:ß ßàöŸúé„™®8ö ¢&ðLÿøPP|Ñe¬ýsŽ©J¢—S8ÔY;±}“6ƒP´§4=& ÓÞ±“ ¹Åì9Pl_ƒi¯ ÞÚËҦ²å9|Øín¬<‰ŠrYtsÜP*–êOÿʤpA M©Bñghk¶£,å©P%n/B|)Âäï?pÁÙ}ôù5]X^)B—khÐ$í ÔVfYí5‹0È»µšš\ÅÃÛg‘Çÿ`…Ü´i³ä_"wâ¯ì\꜇7QÈî\ÕäóVÚó™Å™`,§Ÿ_/Ç·.xn, /",ù3±hn*{Û^‡±7iêë>V’qSuƒø±#ëQs‘!}³ßŸŸëhÆ/-ìýÌæüÙGâÏE3Ç-Tçß?Xãâö­AÃÀdú¨Í×Ôl•³%¼Õl6KcŸÓSáÂtM²èˆüN¤ fÓ).+ø?VÓ&UcüÜf¤Y pÓ'&–’/JÇØ³© ×àça5ÚU@ЙùŠ™èj},îJSµ.z¡h¡†J|þ6G\(r{`nW/>¼”ÏoÎq€yµ_ ð¤]|t}©`b—/ Ž>Q€ –!|÷±‰¨aRó&-æÏ¶\bgú—ð˜„xH[|›(ìŠfÇ%¢YÎ,*µÆ‚‹Úxò„“ãxbˆ eÃÇ$ãÄQ)üüö@ê¡p–¹iÅ'> ÅÆ—`].Ʀ¼ŠF¹“òÜ΢–í8_ Ê7²}V¦ºw¹E¬- ¹ÖÝGà'«A¿9¬Äça°ñ¼2¦^ͬtèî}&”®“|Ú R‹Wpï"ejßcŽAvT¤^‰Å„ùáê©p¢© År#ïc¹ódj>Ò¿\¹‚ýkMé±á;ÀÞªŒeü‚eŽ °=Ø‘¾mŒ‚“ã*À~Ž;_i·…ô‡ÞÞ>²|U˰$@’¿þ(t܆ÕÝ6è¢u_ª,¥Ÿt£ÑðA.è-NDL0‰WJ¸ÛÅŠ_jèg è_?;zæ{,mùϸI9Wž©G»¼µy Í7ìœÎ?ZÆÑ¼J9zMj*5)"m{ƒä›ÏËãÇìò–ôoÖ„¨òMXô\‡Þ¿øœK°ÝV•9X÷žWÑþj±ØcîÜJ΄–xêÓí±Ýøâ“"êôÜ>¤3ŸM¦•²’ïyFO¼j†ï+QÝI¾úÇòÊéŠmáÓÓê Idý`^O ÇÎáDªÿL&*5宯tšŒÍ*ûuÇi~_.ˆœM•úóù™ãøZ“$ø;)®× {vÚr÷à뤦`9S@½ê¤§d´^„štQê< b;$éÛåï™X[Ó £†§†ñÙ¶«¹Ó‹—Læé(ÝIß[»ÓvûSô¨i­à˜â4º¾8’w<ÀŒfjó=ÓsibF]é7BqÚÒ\_C×»¸ÒÍRâông$Û(Ô.Þ{žz{Ë?cëëÓ7#ß¡scÏšZH_È÷ žÏ|vÖrýyÖ´ÌÝŽêÈTñÇs`å¤]ôÖ±›¤Â=šÐ‘»ùÀÐj ºzÇ›î?КíGìN‡ß4h^Úàê–`¸¥&­-ý‡«!žä£ëßip§×Ç/¼Å¶ÓÝüʰx6¿ ³þÀ‡Ëfá8åBPðprÍKÅÃÈaÚhÇgl÷ÃÒs½ö|ÇßÛàŽþh®¿1}üÔòì†×~gPfá,>FÅ„o¸Ö ò/¨K&Qçeé£EÉ££øõè-öšÐ¥ë±È'Ah{èŒ)ãåKLa„ÿ8.x`MvdV€ªÓhz¼f!9UfB´¾×æâQôýçÅdË“±.t³86øÜ ñS¸á³ZpOt§}k-_€aÍ«„;viòOŸg£µéRn9v6—Úr‰üÍÛH³wy‘I, xÝF°È¶æ™ã¬éïÌg¶—³i^ƒÎñÏÐòä _’ Ò7Àî”×DœkyÛ-cž3¼ì{¿ŽìoþBZß^‡;HÛžN7ê›ÂΛã¡Èø;¼ÜÜÓ£±Éý¹´ì f½B&…DЮ+£øÓ Ç÷šå¿±'kÈhþîw"µû« ñ™©¼¾Àþí'tr´àÚV-(yøG°÷óPL3šL=D(5ýÛBf:ËÑ;gûag·;¸˜©Ðv›‰¼?d :›Š£% Ö¾ø‰bâÙï Ø0̘¿T·dy Ðeâ¿ÄцiØi¥ º›50¤T4RçAKð"öõýQÜpÌÔP¯´Ú Kê`y˜([QÏ…í>: 켆¿Þ‰Pï–‡ŒlÒ¥§M0a^$»é.‚âWVqu;ØôÖG`vΊ§É»Ã¢zørkY°å´\_FPb:¿òì:Ú^¯‚í'SŽ©€ÈUºlÜo9.¾ü²ã¢s‡°(à‚ ®!vKV³ ëWCÔš5‚W"Qp@lʧîÇŽ·pè×nréokÏR„aRç ?ì$©ÅëwÌøõFœÛ2Ÿ-³á·¶Òí÷Ÿ²í÷—°éÇ¢„o9ÎMmpzï /;¯­J°µW™ÿ™0loóE×~¸0qj¸+°»“Œv‡²0kÊ(¬ ‡Å‚ñòOЦއ’—áŽ=‡°pMÔNÝN“¶—°²qGàµÝ1¼:a—X»½¥9^.%_£òð–×_æò¶K}@÷¾0Üx€×29ôk]0—¤sŸUbûýÃøs™ ÿsd)=n¥ÉŸ¯à®ßðóô~Ø`'Ãw<Œ'‰÷´ý^’ØÂßÉŒ>î‰úVšÑ: +h[§JkL¾±µ–s@Zt6_¦&c¸]çbô›+¯|¹‰Ž=µ¤så ?ðž•\e>¿ p] ú7¬¡‹l³…ç}Ú  ®3ªè©‹–üŠ·ý:j0RYCrMTLß Ó'`W”Šà甥ÄÁ57¨Xñ€™@pÑ›*õÜbÑÓ<¨N%åKvP*ZhF-ï=¡·&ÐI ùôÃe8åÌ•17Gòžq~<ãÖ‚VTâs?|Kq£z7%©ñ¿0jõäËÔÆ7&÷àtÃx°|ý£ojãMoQÌz a&^ôõ®Pzø¸­s­€ ÅHòì—ÇÌeìœGS á¥IêÈ©S™©jüüÅ(úl)ð¡sãaˆÝJºðz;î5C·^ø^^¿sÓÐuÂ)Á–4CðÊvË–1/·™ôó³ÆnoøgÆ' óm@yû0Zo¿›^•kÇVgMµ±§¿kÿz.ú†ÊÛʈξûðÔXnH9M¼îë󷦇@TR,ŒÞ£CX§¨LþÞƒáÍSব9•‘ß³í©ÎÈJz!D’miÃj1.*MçÔè¬Y:øíž%,Þ@B&䝿¼7pŸÜò¿oÍ'Š‘½ˆNàâù-Åk±Ó¿ƒÚ>£þíaôþ4~Y8dµMéùÇÉä«Ôhœ“* R±pÇï,ÿ¤ïF×àÙ§ïQù•<—ññ¢}IØ‘{Nœð"¾‡³iнŒ&F=gÇö¶,X#Ã3Å wM{¹‡9nÚ]Ã|.ì¡Ò*ãèp¯\Øã¶{RÉ5X°ïœ×^ÍGöebUå+¢}ޖ̾HrÛßâñŠ/lZ‘)Í—öF›xKî¬øÏ¿fh‚ÈhŒXû';ÓG# Œü³þXƒ->Ö\ 8{?¯‡—ÇbÓùÏ(2& N@À3 ^¦ã' ±¤Rî´ný è4fN›„·¼`ã†@ð¿àÇKjŸ€ÄS5Øv¼Røóˆ.Ý;‘ªM?0ØÓÜÁlç&áÙ£yÐÃåÞ6¸?Ó>áG]lÕzU¾Àm…#8?è$4t¸òµl°÷ù}Vpu9ž sÆSft#Ÿ·n…iE’üØ6lÓWx)ûÂ{2á¶v…à˜ÌBÐñÎ"ۗ퇨§‡¡;ʼnú_w¢;ò|hòÌݸ0a°¿kDóÚ5útÄc½ZÙnÌÞjÛKÂùˆîðvcÙ`þzÑŠ?ØÑo›Gó­1JÜd‘yõÙ`Úbu´œ“€­?®Â°¡Âo{®BËùfòà¯-kº½´5Òðà†¡”°Rœ/þ\8Ú7ß´þÅ×Η!öÍÓ¡®Úz7ÛâœÂ8z¯žpuŸnaÝÞ ¸4X†/÷î×aS¤ò&|X—È/¥`‡Ò{¼6? îï?zÎÇñĆ»hØ“ÊdÃBa¼q6ÄWybƒÅµÁyœ$c¬½Ø®JmRT©Ì^ÎS¥ ¦¤àŸ© Ø9˜³3“¯’u àÇ#}ø’¹Ö>¿S~˜ÑÆÎÑü]ë0~bÁ°è@¬Ï*ùUP½ø!öô_'÷²ÌÁ»ÖyY‚nÿ8g÷ ßÿúò0Î\‹™%Ÿ Þ²ÙʳÑüÐTcæbw‚tì¾ Éc°ÇQ‚/|qÎÜ€7g´¹èQ“LÄc9ù§·žEU´“FI v)€jÞEëë"p½4VÆ£Z4iß}G:äÎ|¼¦q»v{à¤!°õhŠÏTƒ§ïvƒÏ _È黈/ZÂáÎÒÝäȃÖÃëC"°CÆV§)R}Ïj¼t<¢Vã±îà~æS-âçÓ ¦ÁwÁýµ4u™Æ-Ÿc,hRý¦ÐÑ'Ç«=g€²Ú72çãºwµ¯0>Ë<¤–ÁœûïáбöÏÇ ;:+aÂWkÇ? `‰­>K}¸ܪd°î^ÚŸÔ!µ¹z4ÅJ‹gR=L"JåÆÇ`AùKác…,V]>ý¹¦e’;ƒóT®K"ÅCΓŸk7íÎ ®X‚“§DÐCêë!u¿§V¾‡¯!ÿÀ>K‚zO•£ç}ÊáÒäÍÞçˆ_ŽtÌÆÑ}-pyT5.w½øŸÇ3ê£&#’Ñqûiæì »î§!üýµ»¤sÖU4µ=—¾\‡cCº`SìK¬Ú€áÊ{à·¯$="Áð¾ZuT !ŸÆÀñÕV(›_ #þÔ’Ës3! NŠ–y£Ñý›ÈÞÖ‹Xh¨Â{%ßÃÎ'ÒÜP'^låvAú<2­úmÀú§?z[Q6sl>,ìp¸°ñ¾4ÕÁé:é°oçZ:˜¯‡÷ÁUåÌkÞH Wf÷ºÀ£kÊ6‚|ü(Z.œåÿàok?|Ií"ó"·Á±ý‹¸Œ¢Õ˜°KøW4&ú•ÀêÒ¯dÊI9|Õv›/AÑÒNeð‡*Ùðýôнy†É[Û,èp4m í ÿaÖ– ÒJQ0ŹŽ“š‰ˆ"öºäæòpPþ2^8i|*N ¨"ßj« võ?T¾7ëºÅ°¼Ežºˆ!•ÃÅá×â^ÇûÚÇa‹n6 rÅûÎq¬9Ç Îh}#ógÉðævUêNÓ`òÎDÜx÷ô;&_Éì#z˜¿?c—*óû: p­š[æ±UÜÑ·ªûÿ^Ñ6ÅhÖæ£`þVV‰_# [´p‚í¸hô­?,E=_§ÃœàlâøÈ‚¦éeñ•ë©$Û€!ߢؼÑdeJ›‰ætƒŸ5¸RÈšlMÀ† çÏ\ËÞ„±õa°sòös¯1(‡Úðq w1íç¨=ƒ3kÅàcqÞ(F­÷b˜öxg X°×‡‡¥ã0*K¾ýÙIÛg*Ó‰çåéÊ·†4þØbøôE‚ÆÌ“Æ››/£êì)¤Rw¥*YôÆ=Æ83Jƒ¦•ˆð#ã]­¼ý[ªpuï ÿà“7óÞ4z©yß­X†’ycè¯]}qª·oüÉlc.ÀîÖ¸èÏP¾LºI°ïÐ}xax»Ä¢ù÷™pöÂ! ~²´åpÄig4_êë£äª' ;:\ÞÞF?õQ¸ÕÿïŠÀqbT¯Ôý'²‰â)ЬÌϦ]©.ôü^G©0êY‹Íûá΃òdÛ¶’§âÕ©nHËþþ³^Á’¡µ´šÂ¥{+ɦ¾k‚ÉKLÙ†ãÈrÕXÌÈ v¼3¹—¬ÑÙ€øï¦œhAíä+xÅz9¬ý÷OöT³=ù£ºwÂÃ1·Ð¼v=ÄÕcÍø\”çþ3³È]µï gÜ‘W{ e‰ã%µŒÊåx)Ɖ?ìP'eyxÖ=÷ÝJ%õixy‚#Þº ÃßäêÃßÈ Ø³`,ä¥D®>€Ìå:¤}«d)糵ÑhÔÓæ£OxÂÓ&3ÈËd~®åž¿È‘‘|Ô¬üÒ×- ü _–“£±?!å_1EÀÍn¹Ð›4Ž,ñ‡ÐʇÐÊ÷‰o1˜žçÆú¿ £•jªà~µÌ#áίÁÏ«ÞE»3ŸÃ·‡Ê\,7ÝæœÀ®ÏæìÝv%¾‚ˆSvlô;¥øÏxelß]‹°B\+» á² š¸‡ñQ¯1KnL 5M£IdµÚ?ö6èFg`îÙatÑWDîÛ ÆVŒÃ»·SðÎM'>*ðjމ„S’îôA¼gþe³°|ú©Åy“×<œY¶_P¯Áß4ÿ‡Uøåîgx±kÍÝuœÈ{Õþ÷^P¢¬2ü5å-úP]«y9s0Áü#[3>Ÿ==BžQ¢Ëþ¹ÒCžþäUq$۟ͼGiAk_8qä·p‡J<æˆãMú—°"«±Å ?|šFoÂĨB”Üž- d¯$ݹf:Ÿ]"Ï«MNῃg™UØcp•yÆ~VYÑUo]ùËÊ%4·< ß‚ mak5ÁSðƦùCIV¾>E°HDæ·‹Òwרç©sxúÆRúýÒ}V8âôšg\yu·ã¢5"tW4æ§àË.g”gJô¬ØÏ “@B"5ºL„Þ½±ØZ· •¾ÀcGm߀!…»A.®£¶1Çr‚zÐFá')/ŽvÚ½CCõ£DA­MybUx„ÀXrþ=¾:œxƒÏ ¿Á³E'ù_»xÞwO{~Ùs3Ÿ•ôôç0v÷I îû¹%›cÏ_ÔcÌ¥Uhf^ ôÅiÒó)ÈFv·á²¼ûx6ó:y»'ý>õÆžJÐ3Î…ÍQ<gñ3ÖG™ö¡óh'ëÄä´qÏ"ðR]TÚeóý§'£”a§cÉ¢¡|¼¥?}hbŠéx—Œ&¿¶Ñ¿›ðŸÒ|è[Nšè¦Î‘üæyErýÏt‘Ãòàï`™üü:ö(±µ6ç{»ZXÆ<¾]ó LÏœ@U3bß6½&Áİ.2™ÏæË.–²¾»Î8:b6+Ò3Û#èÁ‹âh{ƒtÙ_…È`<Úxb–DàµÏGÈ9ÕWXS}u] Nç‘°{à.^ç[ÿìC1G%¢:æŽ]_Ê—gIñèÏX+ºr$|h|¨.Jtl¡#î™Ò!Y÷ñüí\a[NyXÊœ²ô¸h/’?êtcÌxH±µ¤äöq,³Õ`çÏ·-âqôÀN²ì¢ Þ ÊßްFebcAoiŒãf_‡ò»¢Õ÷-ððË5ÐÙ™Kd'²`xZø1ž[}ÿ†Š&üâæáô­÷–g¶‡ß•Oñíüú€/ÿx5¹®Í'U‡ÐzQULwë›ø³šÓ£Ï;-|i„Ó. f»éÌÒü^Â}ü{݇>t4ÀS»îÒ¹õðÔ]îö±ƒ.(¸Æâðí,êç¾ûã3áS[#³Ô»º×rÈÓÍ2|}¡'?dwk¿ÏÃÉ·8ЬJÇÀë…x|½6·Ôfšà¦YZøydýȇj±¤§"+1¡y©±?IöLç;œËÙŠ?éãÆ:¶Ëû?#ÿ ÞÖÆ’-+%arZ6Ç”5ÝvúúHºãøvGiëJDnK-|áÖ˜%dÓH1zmê6ëÌ>zäÙ¨*< AÛ'À÷Ê]zŠ+pKïCÁ«±zœœ€F¢üÊnµm(µ¿°O5=´;ð„º…Øôª^®Á…Q%8~Mn{ùÒïÂqƒXä»ým¿'ù°ïÑcaf˜)-rˆšÉ àJÑ*A±í (PXŒçŸ™¿dy û#ìÍ ìòqÊ[”µ×$u)N¤úä'B>u_ø—¡ìŒ*|õ==ÉÔŽ¨ñØqñPóä'¦´=ƒ5+­àªû~òŸ‡õ˜"Ž5;!Èè,~¹ÝqÎÎy¼\Þ7~XB‡Y’èuYÛWïü±ŒÑææl°þÿ:ã¶4 ¾ÏÇ™G'|…Õ ø¼à¼ ÿ…Îb¶¸îM?1ºcL?÷†Ñáï߂ϻÑ|ómIzh æ¹{ðx¢ /;é@d—ð´ÉBzÅÎÏZ?[IÀ­7„mN]ØÜÑ‹#Ç%áE¢BåZŸ¢]° Õ(7KŽ®ÍÚÇ •yêr ²c÷Cxzì3ésæ–~—9=“äŸÍ¯ÃcÝ~Áä³ø#…z¸ºö*üšj˯슙ÙÌ`Z6.w›yÃÉtG×_³0Ôc|ØKŽN‚H{->zx6›ïãÆ¾*JPÇ“äqßT~°VB¤Ê…BÑvLI³×$¥GÊøÆÌ›X^·Þ:°îÅ+ÒKã"aÖO˜øÚ5wW±,›tTÐxÎÎ,Wá_. ã®Aãu7*ð—IŠŒÐ>+ÿ¬…ÍÌŒÿõJÝî_ac*æ8>ŠCŸÝ¿ó™RIÝ»Eг™R¼º¹ˆÜ페-2¦ß™Þ _ÇŽU*®o‡ê«n|Û,–ý4€JŒÔ£oŒ¡·¯Ëóä /ɆÒv˜×Q¯2©]X>N“K€#ý Ù}4€†#—‰æ˜?LuýAP;[C¢L¨†¿é%RâC™Á¼.agàRᆅ_ëp¹}ÓàãM|ôÆBlë9ÍrÒ£Á¦&þóv¾¢Ÿ€ß–·‚Æm¾êÏTعgwl‰à«Ïª`BN ó]dCõéX*-:ðV9n_@Q'MÜÀBZz Ë·ÏdÐÿ~ÿú“øEg“ÝSTP9Șnz,N«§¸Caå,P €q-I|ÿª!üKÝtáøÏß™ÇÇD˜±ã9Þ ‡ôÙ^ÀîT¾Ùù°Çp¥b=ú_«Æ)× !¿å u< ŽÚÆ«1Ež ­e±rrÎâ—ÿ`O¶;-ÍØÏÞacùJØ%…çNœ€`ë L^ýðE3(ýx‰»6ußêãø ´ ´Ñ©r2UX¬ž#çAO‹ãÅy#é•Ç`t?e½P¦s®ÊÒ÷{/h1ÿ¡ü~\ Wñ¦ïßáñßA|è2-®ßœ Ú&¼À6‚¯I¾Çcfò ’Œ~yÅ(S-èÜÙ#¼òH–“íKA|uü}(įŸ{Qsq «1>ËFÝÏÀý·—ã$¿.p‘¤æ³’èýû +Ë)ºÏŠÄË þØX&´ÈõwùdïÑB™ôJü¦Uƒgô¤èÖÚjð©fÉ‚1:$û°µ}¿>`GW>fÞ7Wã’âÙlQ‰f =aÇ×LvuÖ|º¡š‘–Ž|°ñáç4iŪ]0+z–€,¥°úàBÐu®ï]úmaõýÑ“t¢ip¼-sÌ…¶ ¾ÜXý¶£âéoØ—¼%õ¬y™¦`Þ4xAOžJ>öi‚„AÞ3/ VŒ&Kdž€Ø29|û~ÿöË;¸®ã—èÁZìQ¿ÎóoÉoóùŽíøß=jsŠ1ˆ]ÇàJ5´ŸŸÅ¯ûHp…—}¡A‡Ö?ödÐý|ž½ß µf†Ûõqt<W †¯*c%·³ËQMBh Q•x‚…-ü,Ïao6EÖ¯Ú\|ù ¸ OöÃqñnV4 ŠÇû`Á'8ª¸µ+ùÒ§Uøž‰tóñtkQ5úîò%ýxôÝ9ˆ×¿‹ßŸŠòó ›˜èÝÛhôóyW;u÷·¾ ŸÔÿ‹Ì¬ÊC¿Ð×(!£†9Oq¨ÓNQºÃ樖^2’®ÀC“ÀÜ3 ‚ïÎC‹ãbÜúq̺5Þ~|oÖ‡à㽊 ¦RJ£ 0û_Ηä‚I•dÜ–UdáQ>ü^)v¨ÛSÓ_È‘]éÌgÆTçQùñí0¦/â/F—磸n‰!m-Ì•)y´bÜ5Ø£®Äß« d¸áË÷P:Y‡ïX8‹ í?£žÉ2^eâËŸf/çÑûóqå$Kº÷ºÎsºuÛ×ÁÚvtfÛrî?ü .»¿ ³j6Ñ™áitœÁŸJ®pS{¢ðφ0NbVó‰~Ä}â¦:°‰w^=‹ -(øO¡:7ÚáŠÅq¸SãDõSð‘þ><¼‡ëÙ¿ÕÒøïR9HTðœFG~Jj:×ÞãŸ%E⮂Cކ[œáîëurIØñì3nω`O ö޼9ts·6•È>ˆ粸ÊÙzðª1U-ù 2‡ÎÃ(`šDòÑÓ©çÓ”.¾@µÌç—~ Õª*©½ÖyãÕ%r:„JD>âϵZЫ͈/ÿYr9îR¢B“DÚ59ìŸ/¤^F¯k®•!Ìc­*8ÍËD é ï=°H4=7àî¹Ùî;:tràœ¢mÄ×¾X QséBì;cG7?‡…4jmŠ êw÷Íœƒ:{†Ò–/SaîwGZ:Nh1g0}KwÒݵèLχ¬ïZ#¾[2Žïg^˜_+–«C@| þ{F.ËYÐÀKüÎF¹jìw4¥º­ûˆÚjqžXè «n”LxƒÚEû™½Ì2/!u£.2fü…V/»àÒ+)’hZÀoœ9Å2KÕ¹Ò6Øåw–žžgÏÇ…]c׿¨òÆÈw0g–N¨0çç·ÐëGFà|úÂT—® Ï`o+%éð®ïX6·ˆ7n,Ç»–&tþmÆùöŒIGØ3:8ºî‘·N–®]·­š)—P篒cèÏŠKDow:\Ûˆ’À.ãá΃ëûC‚^<»ˆkͼˆxm ]2M“«£8È¥Ö”äÎë¶ó…Ab°óÆ>¬|æÂŒ‡~‡ÙY}doY›|“X ÝM¼<57,ËaNV£Áû¬ÍM× Yöpž&WrÔä§´¦cÔRk./0¡—¿ÑÆßÓéÇçÊüyÃ2>jèræ}é0ós¯/Ï,¬kròFÓ–v}¨ÌS¼ „"Ðûq~4€Gd™IßaVy<ˆ¾1’=Su¥¦a›é›uä“08*iÎôU‚È¢òk<}' ±íU,4ÔtL ·ä%ƒáko^s<Uðº¯N¼4ßʱî¦4ÞRžJg¶Æì[ ô°ƒu½`-¡ íb™$øÄû~7Ø—¦¾ýcjÛou•?:|Á(}Aœÿ¸qÝ 1å4à•õ°±7”þ줎RoѾ!÷-y„ë˜ú¬-tE– µpý`1€jÇŒaˆj]u}Nð|™F¬,Ø^†B k·“oòêÔ;Áñ§ÃøòžiÔ8N•ÆEç¸"E~¿é'J˜È’“q¦ü»áÌže’§Fа›KI°[ÍQ)ƒÿ,ÙŸÓXèêíÜkŠŸ'BõG? öÁ} ÀGœø×½D%} í{e³ÅGú…hÑ1ÉX壄sšVC€Ò,ø:ŽßßÖ{‹«™ùÜõdÏ(]\>›±‘ÿœ°i˜ ýUœŽkbÿáªò|öhô} ÁÌØ/= q× xòí‚ЪuOOø••qþ¬jbºRè¿y M~ˆEj´ù¦;ÕNLà­az¸’àX°+ wŠåï:†Â‡«¸ô´9pê×s쑹ÁîL_‡ßÃr„)k.±ëÁö?ª\ɵæO²Å ¡OÁ¹a.ÙÀ^²›žm ðÓ§(ÓÓ¯?àÔ»?…Ç`³Û,º»î^<–Ìœ»µø˜àaijCØy~<]¾ì ?9c%¾—¥Í±ôνµ,ß~&•¨K¥õ?ŸãhõdX6u1ÎÊrBÁUœñOçôrÏm¿ñjO )ÚOƒœGÀ™nƒPÊÔ–ÉQ¯? dT5íRß·/س" new„|¼íC/~;Ê/VæCÝñÃ8ðê2›u ‰î^&ùC”7¬+‡›ó{MŠ%ˆöâuê‰tIùJžþÆ"W›òUM]Py.•¤WkÒ¡‡¾€±†6측N6Øò¡ŸÃ ©ÑŠÙÙWá¬lúUv+½>\À#bp¤cšÿH ¸A„–ñ±óø6›7UÚ¢ˆ‚v+šå…`߃¥8âÞ<šùÀŒz ÁÿBµñ‰ÄÖØ¥ÀžÜRç*Ø1§q\Dý(]é’†'…ŠbÅà]eÌKÜ^À·¶ßð¯¾ò˜Àÿ¨)½åäŽy1 ôÎ&1žjؾ+héÇy\«è?—ç>vÀÄTsZÔŸÏoˇ‹ÐµÁªT¬à©jú‡ÿ~Ã%ké)„§ ²:rfmn„˜÷ãøÚà40ú¨@K{Rø¬ •xr¥áÿˆxóxª¾ï™çÈ”yž‡)î^[¤9¥Ò„’J³J“³IHR„J†DÂ=k+SÒ<¨ ¢A‰ êçõý=Þ}î_÷ñ¸ç®{ÖYÏý\Ïç9ën€w¡¢yÊíéç¦ÉkÑŽR¬Ùªõ‡GÑÈYü§'n¡m9·â/7À{œ6q$V!F„šÑ{röÜSh=n ŸÇͧI"ˆDåQôY‚íÞkÈžé­ `ÔÍéŒWAÌsÌpHÕ ž'åq"e}( :Ž<WEÎLlåx)ðú–¾w-­XØ ¹­ yÂ-ï 1]i=h2Ä/RyhYµ’»Ûr€Û/! ã §ü ê§¸ÌÀìµixSè6™`­ÆI·ãÓqŽP+w”íVA‹zˆ«,¡m;>¡fGÿã›T—TÇÓcoÕ)››ÁÇçbœÉ¯™ìNµ1ù>Ym°ÓBÜè—F Rx|Ï_tx]V£LçunÂòšplð¼€÷q÷ô×ðlNNÖZ±hOçòfʳÏÊhFr38~Mc#1°“ßïÎöÊ`ï›8¾ËR#–ÑôMýÚî©ã¯´a|»9 ^º¦‘Éê¢lúëSøe_5 ßãmY­ÃûYŽ ç!èz$ï*£'¶ÑS~ Ë$Ðí´;Þyç‡í…ƒ%ää£BòâÛeT–E-ÆÜ‡Á30èÝŠç_eb¯® K'q#1xéÁºLãêœúp7Çd4†<¡ë²G¡¾‹'×7÷.ºù‹Oö˜MÙ¼›?âPòOäXÐ1¾ºôdæLøáëCZÖÈco+IÛù1äªá\ZºË7ü:‹úŸ£ÄÊ`?w#Ø=›Ý:à¸E¤mKƘYÎxùø$½óYžîylýù–Ûñì)Ù©ˆ+ Ž@§àQ²ës¨ó„?;Ïsæox%ΉXÙ¹Œ>ýq‚ë­<Þæ6ìØg˜¾( 4Ö´âäôœ,†úP9â=hÈNίA›‚¹ÐŸà a›’AMl÷hà;Okr!_¥}Š)ú’_yW1àº4s5â©Þ9œ 4ûÓ79?Nâ÷'aÑÝ!Nd« kääYtÂ:,#÷. œd¶kw«æÿ„I·¸î#ç0ƒ€ÿ*ž™\Ä>®°ú>ÊácÂgCþ®FŒŒ‚;§qZCo±>í|ó¾Ô¸&®ÍRL™Ébæß…SŽ/Gåeý0]øÎ(.ã?V‡±“ñhÅINI_pDwqH´ÎÀÉ›|~ñ<[Fg?G¡eúè—`ÎÖm‚¢Õßøã… àSéiØ %Ëúü-èƒU°ääfpÿx†vâ >wZ+jkí``ýl0ÊÁš¨ïü-}adqÙY(õ—¡Ö³58ߌ<¸ô ^…¥¢å¨ ]‹G0þ#ß‚ ž§EüF¡êO{äf€Î~‡àÜåh꿉2‘î·÷2~ôš‡O%µ©îžŸÑ)LV ‹¡IÒy®âKîŸ/¢ºº¸²"'.É€·Ž† ?&# ž‚غÞÛâql˜nkÿS'""OS*í¡îa" ]SfšÓWfþáý•ˆr¹“2¼ ?9«Ç# mÁáY¹‡‘°ÙEV:ïȸdkð " ›™=¢ñ€¬.I†OCyÄåñ°½sÆu= 9G¡†ïT¯æó'ÌqA1}St •F)Ý6ø  Jÿ®|H<”ÌßH?O»‰ÛÖ^‡úò,ëmç\ãœÙ¬ªQðÞytÈšÐ#†QÐî;ž=vH"âíŸqòOpîk‚±ÇƳؽjä”×PYÙ.0´ÿrg/ùC&í5ÏéLlš*ý pFŽã~º~'“2÷Àoƒ4?òŠÓüÚop.¯®pœßês0§£ß—_¯viJjJQ·ú(þ–£·7wâsÙ5tPE]ŽzI~åT ÿïx´wóù¬>á-íNçnw9€«Þz|ôê ú]lÄ‚”‡è³ü'‰\õ€ga™ƒúðÜð ,LˆÞ7Êÿ›o¯ïÃGaSÀ¶³pU–}›ÜíI$^†9Ãg°Cëî9*E‡øýœ‰eoÍã—¶cåHNíŸ_qBFªèy©: à…Âh–á°DÝ[øÜsç7>ä³R© îõ´±,öƒ#Y¾>—̼+NäšÉŸ¯ƒ\÷-Y6ýñ'!œ‰ã ”ÑÅþ9÷ ¸'<Ïh•—¼EÃ)DµU™ÎZß­³þ“¦î‹d õþ4PUú 7Ì”Ùü¶MœfR0fY‹cªÔbt.ÄL*±'A®Î¨ñ—+â‰/LÇ€ú[g4ë‰ËDiÄj-Š÷®À¸©j´ä‘9º—áÖ¨²ö±¹ðmùj¶¾*†ÌàÀ5-œéÖ]â=Ý–ˆŽïóÙŽ»‚±ÝYHìL„±£®cvº!djdàŒcԷׄùeGÀl««èÙmÛÅjaÖ÷äÄõäA¬«ˆ%ênaóûAÎ…tÂ!¿_$s™9¦6lÁ¢|rAI¸wÎQe±Ð™üˆÇéÜó ·±^t=DnË>¹¨ÑV£œ¸í"NC#¯öÕÆùP2¾›&Ò ?êÏÚ$ë¤fþ9ƒû”K¸Ž¶ƒäʧznÑ>fh±”ä=ºZñ÷/KV AĦ’µŠBté ý•c‹Ï^dâwï•7Øü1mP³™º×(*á¤Jï?Æ5õmhÿ†ƒ[KSÑ36 ~÷Ã~w1§9W-*½C+w6®î¿và ´\†þÐ’‡Ä>gU)…¾½]œW»=ÌΗ`“Öåpòàà+S ö5fhÏͽ- ¹°M#ÇV@Ì‘`.>ÿ±0­.GI5Z³Ã ZÿÚù/RÉúëTª ïy;´òÀëÔ_„›½Ýဇ «=…/b4ÙÞµ}tƒkjç®â%Í*^ëQÚ½hÝ•MÉÅïÓ虥‹ayºDTަŸï€ƒ¾Sï4¥ï¿€Ô×NË›H·æ±Õ*Ñð*ØV抱+wk¸ÑqʰpÝ/®pv8ªðÔ1òrrÝÐg65«øÌX·nªÓ ßX€gÐ+̯ۀ;LÆÒ ËÐi´%Ýû>‹]¡ÍeoÉíÍqÉÝ&¨ÿHþF‘ÄÆ´¡x&=ýü2‘82ïk†y•é¥Ä°!6Œò6ýæ&ž= R?ž‘ʼnù'W)ÆÑx|]8o^Y;RId‚‰üÀja:wÛvalj?US’ 0Ý_úÒæ .’†ìf÷¬P‡%Σ#>uÞÉyÔA ”•;V¾ |lÝØÊuÃà³âw-õìÕžIÍTߢ•ШÞìƒÅ=0œÇŽc»–âZA>4›ÑÅ¡7è YºuAxÝç6Z¬Æ½ßF±þ£U »‰O\ڤȀ½8,›dOù{—݆!ÈÓ.¦å/ê+ôr…¥1rŸ!Ëð+Ãîá8f~AŸ.>à6ð  •Á¥^S¦™ÛDëwœ!Qç]±¡|> ±÷ãN9M£–(³Ð pp—ÿF™7›pn:&\tÂtñ<.üv¿4B]Õ^RZ<ž'}`1^î1§“›>£´n,ç︺N?„§½‡à}ü., ëÂmß<Ñh±;|Ú2“µ/Rf":Öpj5 ·‹Ò7ýÇáÄ6!˜ír uÅXù*A¶}Öyлñ5#7‚ëQCîSt HÌŒãuE±5ÕuüŽ;g`í„rb-¦$mdëT[ÐÝ[j„ëKët6ÊSÍKlÊÿË“ÀÿrjÏŸIFó†hgY6ö—;•³7b§ç‰°Ží¨ðù.wÖû÷1wçï-A‹Mù«n&‚ÓÔq¬gB"nO„m;辉VÔñ%‰- x1ö­3¦6¬ë ¹†íÇ€.«â|j³S`³žSÌ"µ£ fŸ ‹Œ)ÃÎúx·]’Âo[H.1'ñæïÈþÐ-ôóÜ„Á£»šÓ‰áú¿)ÐQgÒ­¸.­…œiÊRÎUí*ÿ(Æ0¿ƒ$Ó/ >­gºÔðóx |«s–K“ŽÂcB?áè¹Û¤]Õ’ ¼v`wÃgaóCH·*¬³™ò t$žâÂ}û@kh*•s?O&+ öW å%…ªý©¼[ýt•i|ªÿÄ{s<·f¬=†ëCo4>‡³¾"ô—e,‹Ò¢®_Å­ ‘ôÅŠXÐL:C<ŸqÕò&4Uaõ«÷GÓ]ë¸_Îa0œ]K&É p¿¬ðax9ÆÝ ÷C ±@JÅ­v`¿ÊßÁ<Ý \{P€%T±@Ûb’½i)U‹4aŸNg“ì#¸ûìhÓ¹H’Ñc:U“,Þþ$nþedT¬íÒî#kŽWÓ¤c5ÈMšÊLŸG°C„Ùƒ>ÓÚ„çæPq@5@Óßà[[¢™Mù‘v‘&­ÚÌŠcŽõ25œ|ö7cÝbjYÆœ<‡ ªvLô+bš254(ÁûŽ—ÁÍ_ A³œ(gèÓÌEÜxsÛðµîKázåÖÿx’75œŒRÊÁNÍ!П¬ÍÈZChïÅt“ÃàïµØíoÀª jÉÎtk-1¢ž­gá´!7¼c,\v“¥!ŠãYd¾OǼ’øZK“çk×cô'_’¤CáÏÑ©øJ®)å®Åqp2ô „¿2†D;VwÓ >õqÒR³éÚÑIœ¯ëuî‚È/ìK~B&H߆(.}×ËÃÀmu†ŠŠ4°J¯ñ‚Ýû̘Ë*zÊt.ãwTs’ŸQó`1Øú¸qTtêx–¥×çZÂG×h˜¨ŽŸ›8Î:¸¡v‹ž&ýV7‡Úu 3MK!|È®qŽOÏŠ,¿ª›wCÈiiµÖ€ô^Lk¦_Æ9Úo »ÑŸ©•¬d¥‹¶QéŸ~`}¦-UbË"Â@¨Ã Ÿ¬Å‚Ýô Õx8QÃ… ›°Þ`–1]¼‚Ió!Ió°ÕžX{J’u„Žc”çýÛØ/.—1úÑ?›7Ç¥p&;æQ¾òŸfÁ®K+ÉŽ¬Ålâ« ¸ç\ÇN|·d7ð³àÙ¶OœüVMØ:Ê ™É*x¡C/FaFïwlL¿I þž‡ë'piŽ;RëAó÷ëáÔólá;y|Z±漜Œ}ÙrL6J‚hB@™½Ë ãÞüʲì´çjzáÜ øNvckM¥_‡1Ü#²IS„þÁâ¤k©ÕŸ8y‚-W܈F@FéSnͽôðÅ1ôqð~6kV½°D¨¾æS"’Ê…c²’PnãîEÐ ‡(]¾è«4XåbñÏÁ÷Ǹ¤Ü=ëÞO$Ö®ò8U£¯h¸ƒyâ-°xÆü¢‡<™óŒ S9Þ¼~šk%¹àÊ«Ì<_Ÿ¹XèBo¦> ¯› Õƒ‰ò/SsÔcšÚNLbÝS\þþ fŽ.$^ÚÛFøM†ÊÞO¦Þ¿OBm¶>ã»Ð¿³Î`Òž>\¸t*͇ìEo Ú5•=鈿ŒÉ zÜ‹nv»†Ëê?ãê+ žFô‚»ïkîéØï *øZr7aVÏTÖÐnMí?òöÜXYzI‰\h÷!¬îéÇ—ü¹Ì°õ5ïâõÖç”Ë^¼ ”6¹Ó{k!~ÞB6æõ9L*œH´ä×äO¢îßà:Ááæ*"¶vLÇÄw‚íÛxqªÙØCzƒFÁj(+ŒÁ}“_ñ{/¸¹Ö€l7!Qi'©ûò0›O ¬X¦O#.[ý†$MØAív˜Ñ$ÇQ°ú“_úÁ<ÀŸGæÂ†²QôÏFi¶+ú7Øžòg哨WU:@dA¼ê1^ù /-ïfç„oòbÑ;‰ûXŽ&4èõèd¤¢úQôz·› už4œA£§çƒ™¥èP5…*íûŒ:-\"*²ÙÎ…D¦tVé@®v!ÉüP†Þ\"aÃ7ù^ëOã!ƒ‰¬W<›ì4=ìb×QJ~m½Œ!œ.«ˆåã)¹Ü“‡˜Üœv"k­Jÿ\s¡ŒâØjÿ9°íf俉„‹†¹LafÉ@-¶Ä€­òHǼéjT6iÄ\œA 4ºðüª-$~Íö..ÊûïáŸu—alß¶½µ¡ŠV6ì[{'ñ*…ÎÍñf‹Þpò7âÑ_k »pþ9gP<±Þa°Çúçè±²ÂçB¥¼]ìsÇ׉AÈ-†½5ËÑçœ4÷ÜF‹l{/q“Y;©ìÆÜ2åXïËDæ’‚_šQ*·8 1ýä³Ó$våå+ü’Ä·ïö€ï§vq1ɳé@ˆ1Û¥“K¯kbâêR®l#½ç‘Ü®!vôáoˆî¹‚¹·ó0E%ÒùvàŒÊì~ðMèVÓ zÊ-à8ã®ý ‹µ'nCCä|xuÖÅ÷daöGYÚ\Š.#Úÿ8Öb™ ˆv«3•ä½xå×BÖÐòsMëAD"w&-Ä›®õÜÕQ$ãúTT:ûn• vT¼ƒ1)ñØNwz|ºj®kÂÍ)Ðöz?=ê²%GÖa·ß¨®.F£ä¢±G—G;%ÕèG­ãà»»‹vfŠŽà¦àGtå”;H VkÕBðy­Íë<ªG73¢ßYÔ–L¦wŸ©0—&>䟧jJ .e'3£kzÌÙÍÀaœ9_À[šIú—{°çF™Æâ6®ô¾—PìŒÛ>—Àe9¸÷Â{.õÝP®Ò†rÉ õý äúŸ†ð?\¬¾‹ŠéK2±žk˜ì¦Èn4NäJ³½ØÓ¶Ÿ¼)¹0ÐÕÆó쳡EQȵ‘ž±L3ûŠOxÀý0bMN‹ÐÍo[ˆ:Lr8çòù®:,ºzòšÈë.#8µ5˜~äÁ“™ZäÅïaðñ}…\ =OqÍ*nxž3¬¸ÚË/û±‹¶UXÒ òSáøÞ\ò«ï.÷¾Åë¢"ÈÕ œ¬•0þ·LÝëDïËǰ V×ïÈ–Å'^N>Ä=Án› ìâÔ\Ù2¼´nÇÈÙ̧—op¼¯Ûàtý=WÚK^çûœ²§Û¿‰ÒÅÙð †B£o°lmz{Âõ˜*qœ4ï ²%)0ís®žÊJA“úD¸}«3õ~‘¿ü6þµ›qõ­{°-Ù¶6|â}÷fiŸºù…þ”+RÙýÇêÈk¡˜îŒ/S ñùópzïo-7jƒ2ÙøÍO£<ßgŠª1 ÙãÇ#Ýo"Ô¯Çù¥¯ðçµ9ÔVycá[PúÂX¶à³"×xè$69œ„]'&0›±úª lB“3}4‘ŒJ,†¯çÇQã ÏDã½ñxæÍ™µ-gS”Gø{ëm:îúlú´­ˆ­,øˆ‡òæ25›FúM2‚ÝCÅž<…w¯®qã•´¨Â¨8*)³™o3Ð-YÔT5‚ý¹È'5Nƒp®owœ ¼ó•Fúuñ îøãß©r{mŶ,>1UyäIüX ´š]¦ÑôïYöwÂl¶[` v<˹ ¿Åc¡ÏášB.îzTˆÌSØÍ%´|R°’« r« :ê6€Ù\?.ÑaÛ¸ÆøšwÁ÷ë2̶=“«U'%ûy7Fƒü yÆ‹ýpý cÿœÇªR56íã>¶Ñô)y΢v2jÄÓa•Àû"qx– õ‡š@O0˜„CíØyÜ$—8¾»Î,0…¯Æ“?ÓÂ@¡Ûµéf;‚ãÏ èŠÉŸ`‡åúý˶GÊ :r‘»®ˆ­ÈÞAø’ðÙÖ ðý×pÏ~…‡·Þó‘ˆfMðW<ŠnV)D…êslÅÓ¹T¢qèĉ¿©k¾ÂqÅùðý!Ïü×(ºlZ3,ñg™&òð~9Ÿ<1êáÙï­…µ2yèæÐο3JŸ~ uÃÚîÄdƒ}âäŽ*®„4Oá:ÍàgÊ4Ãre'nõŒØ`&E']"vN¥™‰W±[j×…»E¿sªj‰äЇ®ý–ÕDs[oîG:áN[3z¤Yë{*¿a™p/„v,Ñe¾ÃX¾¬§íÉ$“jµP2Û…­W×cE±(·pàô^–~AáÐSàîä×-€Ð5¶ ¨‡gpË}ž Þ<'ê½ã*y:œy_|ááKQ:r®<'ùxжM€ò¡4<œ“G¬â{!ç&B—øe,,yoÜþ¸ˆÛrqå6#–Ë…°ëU™ Ò8Z4á2l1_ ì³õI®sGñÊav«C†¬ø‹7…Ô©wf êÉݬ뚃+áÎØ?Š¢ãSÈ=r{ÕÄ麰1Ì5|![¬X¡¢ó°êS¯Ë±ûˆê¿áÜ×”óK'·l…èÜuºl‹DÑ?ïÂIÓ(:sê7°r>ODæ°¯§°¨*9ܤ,Á†ö(Ð[_–CßÖàrž‘q­xÜ"ž—,¢ŸŒ¤Ñ»àÌú±’ýÒ ¤GæjÐ¥ º4øl.äo…]ŽŸ±t¬>>P7ƒ7¦s¨ªþYÌ¥éxÀ›È³ ‘º¿Èn¢wwýg§±ôâªÕض=”î+LÃfa4ðx1÷iÐiž ¸+V ¾„ß"Û쀉c!tËûЉf»°ª~xù´žŽY¹>Þ°UªJüöªÃœIîuî¡–"Í;mIRê>Ã}SÞv=7-ŸÞþÓÁWþ4¥ª÷¡ã~ –÷ñ+¸þÍA5!¦'9‘½ë >÷çк›ïpCK,»_3‹§LFcó(T¯É¬äBÅzSð:ÐktaÓp$ ý¢Æ.Df¦>6Ç+Ðí_ϳÙïÀÚà"Øxç ïNá¬WZI÷}œÃ¢ÔBèÃ!(¡–~p€þL>B6¡ 3-qýà—äK×7À»?5°?"ö[Êãls3Ôxà‹Í·”Y^Xî_ý ¢w'³æ‰-°ŽïC×¾•þÿ¡‰…Ù¦³aÏÓ»­>}ãdnÌ¥©Åg@À±Žs8‚™Š{¡¢l&|oï‡Wk?`Õø¸.±ÚZÕ°šÙ±½©†Täªb­™óc[î%zYáð ˜õTâ=¢¹xEš:ØÉE=Ù„Ò1·¡s7lq‹ãE],…_×È4‰Hv!dÓ!LÈÅ?äí¤ Xb/ ·Ô€¿fEoÚPñĸ™æ s„¤él êØ›Žät,¼õÿŠs§F†~¤º‰•˜Õú™l_O8©Û› ¹Íb´¾Ì–[µ´Ž²Çü[ “^_k2»5tkQ,ö áû.y.c‹Zì'L+4›7wb'jÐ l U‡ `‘ù^<ù×Ì•¤XÕ·;hft[ºŸAÿÂרa!Nvåüu,©v«‹)-ÅãåwP64 u‚Uð»¤ÝÇ»o˱0¢³÷ç/{ bùŽ8y» {H0ÿgëáîáG¼TуdzÝoNÄæ5—Óq§ 3uI—“1²Td÷cæâ‘ñzt†í(²rí8¨=¿æiÓ}=‚8àëµ°áîc"J>¢B\-jÌ%Üaý|nš¾,5Öô‚±›ã¸°Ý«a¼É*¶7«õ æ‰ô¼„ã 5ÿͳÖÖT¸<ú0‹:׌[‚dB/ü®¥gº[à÷cüër«'Ûà.?/ô'’Š^Š!…&è[زD‡ºîÕ‚•ß—ƒØ]qß¾ËÏÃ#>«ÿ߬sŽäÖZ!¯b¾r7Þœ#ƒ©©\·¦/ µ¿U2ðŽg²&À2»rX:P Ú‚P2Ñ–ÜØïb{= „7‰Q½3O!Ë9† Ogïä ´ï+–ió¦/íÆW#ººúF4ôíÛ†¿:¹MQnÔŸ× ·:ƒKÃk¹§aÆÂö™gK^J0å4=Ú›k+¬vB¬R4}[¤~Îí¨fÕN é¶hì¨Õa‚Á“éÛ‰Rô]G5\h9Kãú3çâ5,"z}Ó÷.TׂV‹(Në–eÃW4x¹=ëð¿yæÝsÇP‘â ,¾ú"|V\ò÷ʨú$1z/p9'ÓoH'޶Á©F¢ìòÔxr=mUTM$?‘KÓZ`{¢u’‰cíVº4¦|êXÛ1¯ÍãØ5¥2ò'¡·Ý™‰Û£SØœ¤.ücl±ŠÂYòð£õ5 §^·§.ï˜üêgüÉ-©ZÏ[Hœ'Çü{Gzüå(6œ?Ò÷¢iΡPá©LÍr~¡–X+÷v`‰6Ûò]ˆõ¾[Qk´ÐòT[b¯‹lF®IÈ}¼`¡¯Ôyìðü“P9í¸ Vàšr,a=Àc k 4Ðy–«¿Á•[™€”Ö+p¿Vc‰=³·d GÓ£â²ìN b—f+Zp üùe½<:Êu >4IfWÐÐü:Jª:°Œ-ۈ‡Hš½*»Ž¦ñɯ¶æþm-sˆí„Ù0=S€ŸGÖÕë 3:ªö:Q9bCwEâ‚#^¾ÿ:Ô ï€÷®EdAh/7Æ*­D¼(og2$XÊ‚°P ¾Ø½5õîƒY›&- -Äsëâྰ[ñ ÛïMãŽ\µ©“Ó5 kÓ"yŽçÁÒ÷)Lû$š©ñ¥e4ú™ l3‘†bU¦·½¯ÝÞÀ$;¢Nc¯öBÞ¯”af‹wÀO=voV>Ñ·x+/£N÷ˆ~á.¼ª»K2 g¼s=?åa3Ï(¢¹NH$ƒçå§À”Kš°`•+sä+ºè“JÔ™Ã.MÒey¯;@Ð7hR"ç']iϹ%‚¢Ìä¼2ñ•ÅóõmàšóŠt×pZB[É6‹Ab*¶ ¥DÙí­ŽŒ7u! žÀ›Á6Д¤ÆòªóAÛ@nw½‚šžËœÆçRòá\% »çLUîI²7²ßùnñ§y–3œ8‡bC4æ²07x*ËOU'÷ûé&[Ól§ÃaÏx˜8JŽm¼ø„S[9.Ë93Ÿä‹\º@5Vø´ Òdˆ®Y »Â“]Æ-º‡óÞÀú<zU0’S’ž«M+Á4£è[1äé[Ñçéø¾ˆ°=<àJà;Ü9q"µ¯_D;’¸Ã¶4{µ NuVÆk#=þNáO¬x¡Çž›Q‡mgHvU×u¨ î‰Òï6‘ðõš*u¼° ‰'rž3;ñ¦•\ÒÇïPÑ)άƒ[±¥÷÷êõA´{8Ö*Ùƒûv¸¸¹HÃËÈ3®æ^zl„+A-Üü9ÂT%u„κ€îZGámô>T¹©›,œé©$Ð<ªO×ï‰ÄΣöäÍ@pÍ_rˆh· 4öŠ+¾\Žr „é[CxÞ÷Š M°BÞ¥w8o½-ýxûÕz©³ÄrϾç°J‡§ÃgAU¬œ œ;'nºä3,îXci‡y¬É”©©ä;O¢ô‡ãC¢øVŠí“_kÄö¡vêL6©ÕŸ=ÈÊa“Öù°Ú»þxïi#^ð_LÙ‚²Á#Þ.ÊEà\¿2ܬñûáûŒg6„j¿à=Ï–mKQ$küB±iòØû' £±sÙM¢Z5ê\ÎÅÀC£8×%GpYÄaîËþy#[š×¬œìFõ›rÁÇæ¾“ÌÀ'añìZ2,6•¨î‰å¥î+Ä7³´ØÖ +–6÷é²·b2Eáí«ÃÀO½Æv¥`òj½òî-–ù0>ú6¼ ­î[€îõ(ʾ Ôø èU·ù|"xd {­ä`¡’>Sš(NǤu’×zÊ4ÔÛÏn‡ØO/!ÐĉÍ+?Þq=òVLË眾ô sg4qŽ9 xÂ5¬îàœI¼¦Ú1°&Ð~ÉJx JðÑ5ƒSr*G6c.€†"N.oA±©0¤8 ¯’bA ´2ð'±Ѧ1C áÚu:sÝC˜jü¸ÎcÍp©¬G‡¹ŸÀÇ$Ì’ÕIÓ·ô|Ž8õ¸Å/ÃXX}•ÇZíu ÞÌŒ¨ÀÝ˾ñÕ={‰›ðE´Zˆ!êChæ,;ܔbõaΑ·.½/ SpçÎùn^ø%Ël“2IÑmÄÝ?3ñR¿4¦Èdâ«= ì‚ñOôø¥;öÐó£ uÜ…켸ÖA–Qqs—ÖÁàûý3Æ \\Ýé.»PVûNÖîÛÊw9)XWêÙõ©PÛ¹†ìKS¤Ç ½a0AŽ9[H²†p\X˜¥UÃ1ÏḧTzÀSÑ$šØõ ÂzIê†moa‚ µ·År¾¸^ó7ÓZÌ:’ I¥pWý"¨*:Ó˧ÎÁÉ‚fî¾IlÎ.„%JóàæŒd˜³Ç›í÷`ºÞ®=²œ¯óí ¾<­E8-Ü]v¾ï§Bæá˜œ,‡BOû8Oî˜i'Ãd·‘ ïÏu✠ë|¶™{?# ̪Âê‡é¡’F{¼ã"iH`ìg9Úø'„k°±EÙÒ2|¦v×ËÊRá~×h&‰ã¡ôá4–‘\ͯþ2 â§{¢¼ÌFRVÈ/0}Æ)›?ÇQ2æ EHåÜœ²×î„•)ÃÄi å?‰-ž¬Ëž‡ÕA†öîB—54$®€£ß[±5ñ/ÆàT¼øÎ[.ñ±T<EÖ½!+Þ¤¡RéTˆÝÃKÝÚL,MNÂ¥H]–m§s†ç`âL?nΛd&Á½ÀXÍÙìQÔ p þ„ï.'ÀÂz°N†‰r…8Ù4—÷ZOŠ-j|É]Ì”Ä#_v€­÷c.Â.}fŸÀ…só@"0—DªS£Ç1:Ä›zÔ<çoÚy—¶˜€IÆT¶¨†æ=_ÁJË…]¿î•*pö@ y$‹³¡n¯Ã—}(væ,>zn‡›jHGjàÔ’OܾL0¹.BEãˆSê$Úwcç߯ÿr0³ø=†>ƒ¦É5ü—õðáN9šÄå‚ ]ÑPçœÐ2ææX ÓG7Ã:}0™NáÊUï,’5Ý &l3}x! g ôF]8{u 8g@ÇŸc¸z•žºRDn¬YW»¸77”¹=›4½yÅ5êÄqÒÑßÀˆ¯À<¿£ß{sÖk<†ZÛ!êV4ågâ!ÙþÉ´*œã÷¡Ÿãž]„}?$ÙÂÇp÷Û‡p_èLÉž‹©¥½üÄûæôÖå°»ØL¦þè y|iZäÿz㉟t ·çÝ)j«Þ–+1¿0yììÎý*ë@J§8κ.1ŸìÉð—bܱÐKÒ%ÙÛÔË`¤UDöîT£g,ÁxÇþ²×ÑxÚöi jÏLàÞæ¢Ñ´ŒäŠÄ¦°ÚŒ0:]ÁéŸN½žäð¥ÇÙ’ƒù|îzø=ž°ÄÞYãɤfÅ%<+˜ íÝ Dî Œ3î‹4ß¿àbn+Í,6gðû«§qV^ ´i£.}~Û—\{ƒóÚ.Áý¥4ISª¦Yãêoc¹‰b¡{ó[²þ |êÄÿºäTM܉5_¢ˆPá7¢ê\Evf׋ÉG³Í0k“!ºGëƒqqlY–ÇmŠÅ9à- ÀgX6ÆinÃ$Ó™!¬ÃÓæ‘œ½Ó62{J:ìMy‹3Ösm-З>È1?EöÒÏ2žá´;Áªô0wx,w|â*óG3Üx‡¼Ô‰†åï¯Àâ” üÒ¶ x?üÑzw V~mƒ¼Ö_dË’N~GtvÁ˜ï –™æÕ•¨£,CÃêÇØ ¶æw¹¬»—9¿3 äq~<o: &Þ^›ŒW¿Oâ·|ü‡_ï²0dwdPž©ì[BÍŒDØð °“ß_¿GpzÝÜým&ìÕ×k*°,=ˆ€¿4Øá4-7öÐ"óǪãô:{æ_Uêâ_xó`¿ÛtX’"J T`y"z8…©q³i±ý uϽöðÇÔ„ô×îÇ3 TÔP’Mñ[‰†Ö@pÓé ·Ã( N”[Ó é¤Îc9ѧª´Ñ¹¼8”7¢€:Gâj·¶ºeÆ¿yg‹Kñ†\ ,}7‰ü|t×_K¬-‹ÕcÒOáóZ œ€^îòt€;²³*þå|i-)Á¥Øx»¥å´~ÃÞÓ™-Ë¡Sè6ÎÍ„Ñ÷€FMÇßÛÁ½¹¦Gv!ø{ªš›ç9І~%ÓG±119°·ªÉåÏiE0]+B.­‘ KEqâ@9W¿ð)<ÚÀ»T´/žËåBvwsÒw<¨›A%œx1(Do9Û°o‡Ú€«ŸO.mƒo·®s“ßÜ«ú +æãŽÊÐÙߦÃå¨FÞÏ©§Ncçrjd7÷3V],¢±Ûðêµ xq0 –Õ›rãΩ9_9-65t»*‰ÿfQ*;ÿÿ—¿‘)LŽHÀ?-7¹H½8¸=ý zgàëWÓ0<Ç*\-rsƺÂË<€Ö5·pËΫ0Y\êžú‚¢«µiBÍ[|3›õê†tÑ«oÄ,?Ìþ‹ÜzPð+‹&žÂÛ·ä̲8ÎR· .¿‚*b¢Äo&U~¶ÜE›´ÏƒävC¨ž ¿/¾€S1¡ìÇó—0ç 0›*~ ¯‹¿´{:6z pG¢>’"rlø 5vÜ9ƒ"'æ·$²gë~,_|ŒsMço­Â5Õ.¸pÍìX)Ìn\ÚLßë¶ÿ[ÿ§z!êý¬*ø ËùKÀþÞ˜«2l[ìáLÂkÔs“#mrè\BtöÁ$y6}k\4XÏçïë……Ó`ü1\= ”èðb‡Phy¾¼³ªÓq›C¡n×’3!™ÊFaã-V*$k<¿ÂÛ?®p¤{,\žÉ‡«¿ È¢'Ç0öCgò»›Ì¸´¿ÎÞ…'ÛôeÑ'jI¼ù:üuü~„=,›.s—Â÷ø›„wr^²öðú°kÏYÌ=ÏúCVw¼Á¬¯’L¬Ñš¬+–‡S·QËé9Þ© Ë ’ÿá_sŠ1H,‹üÌŸäô z숓«Ž ÔÙÒÏ#ýÓœðì®9âô_ó8[érp\Ïà)QÓH€¿{¦Á±, (©£fóû¸ÑÀ;¿gi„¡†(åõX0©#ø+~õ/ÿ‹6Ùæ}Vƒ>øªÈ ¶Q~¥,&†‹iÐ+iQ¹ïÑÁ¼5—è¥a7XøÌ–jUr/t¶ÂTñcÀçï$¯ÿüÁY9%鸇ÓO»Fn)À#§ËlÂp.:Ç|¦³‰ Q~‚<œ`Ǿ¤Æ“¾MÓ kœ„%Í:$ ¿ Òÿåÿ΄ñõ'³‚™³¿ƒåˆ¶4’çÞžÇn”e¯Åúq­N2Fì¶eqCS°<í¶+‹ùáht×ŸŽ•R¤™Á£Ùª=n4-#“»Q€–/„IjÛ™Pé}Ü“\HôRoÕfY<ÅKãä±eáãsúLn„lC +Ñô¯!äí¦ PVø´l=§¡”ÏXJÖæóªöÀ‰á_è§IÜa4Ó,$þYzÌ_“!kíÀ„‚BÞ½}®ÿ®P¸:Ú‘¯w4õfóÏ®2“Qgôg w¹ðÿ]Cüèu=øzP¢V^…Î,?.% 3‰Ài9š˜#ÉnÔ ±‹o/B劷ĒN `îMŽê8Åú’p¹ó/Y?W§<|§? @\ÇU<ìy7ìTf̓»1ðñÚà:ŸX"„¸k²nQ!Z%?™ Ù"J§½“å̯F²€jôFÔ7´4¹„·n‚H4§wV æbcæ"iŠSÓÐnå8&';‹}¯K… ^]œxc+Æô©³©Û4HMZ4¾ÏŸB^Hxi/ºÖQ˜ºétñÊZšÁy霾"œ­ÌwÆfQçβú§óÂ5²iOyrh<q]Ä,4Sp…¥!ø¾¶aËg “Ù+îÁû¾6Þéƒí„EÆóÙ\¿Ä]´Ã̵ pá°-\|ú¬‹dé/Ñ *2Fš]lÒš=›½qŒ€Èf1öËò4ê ¨ù\ÈÏzä@Ÿ¦9 ©¶3oÁŸ&þÖGKYÜ#žËÏ<|ÝèÂ’Æ^ÄæI<ÈMÞ…káüØWþÀVŒ³Áœuè÷} ÕL§×Œjà£Ð’19[u­=Í óÄ1÷^UB iËì.^ªÍõu$É+æô[åLݨMËÚ_ñ=ï¦ÿ«ÿo³¼«N°¯V^öÀ¥V†¼£e_ðuËIày~™ýÇÁÁ7îÔfŸâ 6ÚÓgzÖ¬qñºCª¬³¼È¡'G˜‘ÒÜ7å wOxÄ?™e?Îb寕Tг„Ûû| Ù—Ãz—¿¤ó¾,†/ózÐykîé¡‘V·`W¬ÒQý¼»æŽÝ'sF£ûföjç6‘‹`~9ÌUëfã$ž_i8ªŽšÂ¿fuZŽBÁ^Ö¹k-Ö¬¸‡­\ÊUX1nU?hç,¥»O¤ã­×Shþ†™ôÂ6õC÷ñQzÝþ›±ÿúßïS€?4ÀeN`›6×pK¡ž_‚™â˜¥þ/•!êaNpñ³: ž÷?ÏâÑi§–Á~Ç}à6º]æ|€ÐYßÐHê[6G‰ýuüF–µÿÂÒg°mæœ'ñf‡/úÇYWmÐ!å*üP ‘wø§ß%À^X3&”ÉߟaP!H§~„p›e(,¸ÔËcñëSèt[•åÖôpg®b3|$Ù‹;àÆÅçü±OgÄ¥¸O{K°ªË‡mË/Ŭ“Ù[‘< 1e™Ipbû"˜´(&‰Å‚Õ|nÕ3= š×~š·Àúïs˜ÿÜøUÁLñ¨t\‰R;pl>¯ Cį§žð$]ñR}6y˜£…®?fàÇü­Ü‰su+%#xkg-µÜQ®Ïþ¿Ùi^ÿÒJ¼yË+D×­KõX3Ûùþ5/f"7àŽ%«DèΞT¦•§C»Ž…ãÀc4Ÿˆúñå˜ùõ&¹TÝ€*fW@Dw ÝÒÈl¢Õc‡ù«™“aŽT›‰Ø×1Cáåté++úô— ìž2•j ‰Â“ ó°La [0¤E›£Îâ*ËóX½i%ÝòF濇‰•³A1T•H§G²¹7GòH7dBo¿`LG9ôÌ&^³ç ø{5j+Ñ­Õ‚†yÎQÅ‚iìýŽ÷Ó&²Ì}®œ˜‰Ž°‰t.RäŸ~ë¾ FOçEP/ÑP2Iž×:Šƒ’8“ºóäJý«¥º Lƒm2’tg möÇËsán·ßÜ¥†• 8þ2 øu¦Y5rço¡çß(L SaÇù~dZõ2Ú>×^yÒÕ_§ÓË"5¸{‡ y[‰V'dhÿïdfºq"w¬ý6ÖEuBßp øn„ó|( <ÈûÓÃñ´ÔïC<ždã’öÑ ¥Íð¦K‚ ­ÿ ±/ÚAÓ%‡kŽ+‚ îj‚cã/hå§ïI+_\p!þ )E2*foêÅ pê´2Um}‹5×ñD,>ì8‰úA¹Äø™5=4ý6¹¾ÚšiýÙÂý/ÿU38éÇ“pè™-,’ânseÚæ´¦=оÜw›Áï†ctó;602hm²B•µcÙó÷ضuR#\Ú»& ò/ºl¿R·mÝA~Ïšc88:¿ÉƒObE\}Æø’x‡‹üÐ&«ßUØ¢Þ.)w·òù,m³3ü®ýË‹~N¾ûê z >×%åMÇHþÐ7€ç<ÊiŒôñl-6‚çqf-F°íbû{”svü0»¯ð'_!³öR»î§hÙV•­j¨sМE:ûñ‚ßf“޳ñÜ£lãkÿ«ÿõ&{(^2¿Þ´gw&¢ü³xÈ(}&Z橸ÿö7ê¥Ýƒ¦Ò¬à޲\Ó]ºPÔâ¼[hÌ6æ)ñSáèÅÜ­«°÷E:?ùÅçö|ײی qFÏØyL«â%×ò¹ø¥îd÷¤>n`‹.Ý$îÌFÍpáu*Sí™{ñZ (»sD˜G!/M›Nõvfé§—à®Ä*| ÀšnÀõn)t”÷C»Ÿ·Â>h ÒÆæäh¹ u{IΞ4eЦlXa +n-„U¯i¢],?Sõ7º©_¿¼Uçt.ìÿ_«œ\W݈ësk±ãér¸ôj>0ñR‘8…ù(ÙRwѺM %¯mÇÏ×aïy 6(¼Ÿ^/½·Î®äy6GQÏEïÀCg;YÞp°þg¿%¿û„Ѻ3‘tn/z”#åjí0èFœ‹šV)¦_;ALŸ€Ç’² ¼˜î¥r8W²wº’χmØ€ÙiTßÌ»ºÔ½r€mœ Yä@hwûä=Ë§óŒÆ‚\¼!uj0¦’cAhÑab4Ç’æo^AíóxëS þÕÿá×LØ<¹—›û`µ›oAU_„´Gíܱíc(ÿl7¯ÅG¸R½{aÆŒžoçGXÕx>œ8‡ïlÖá›Áhœav¯Ìíá6õ¿ÆAÎ ÓUB/æBhe¼žkvSã ú£çÃs|¹è—äî’¼ºì/Ü¡˜óPÞÅÃ5Å"øüà>Tè:Fx-ZTýíO¨÷žLæAœí\{™GÞ¶VñžÞÀ6–~@©\u€7‰ÿê6?ËE;x)\îÕÜo‡Á/à ðÚ1éž¹¸óÂs|ÿdX®ö„`5ÄYv"iA‹FC`(ƒ¹Fêt°j,æZ~ç.ékRçê”®áNæ Âzã|tZz/ö¦Âñgúíò0Q’:º}Së~¯0£§¬Éœö¿ÐÿB‚þ\‚k–G0]ƒ1ôµ¾žá¼ØR“ù ÔuŸ˜üa9%œý5>|%u¼ ÊhÖ¶Óhþßkv`|ÁnðSTÆžŒtx\¦LÞ}ĉW^ÂÀÑ…8¥©¦Ì¿€ýŠà·§&KùÒÛk,ÿå_ºEî͈˜uÞíøhRb¿½ÆÑùgôའ®Í’¯+ôgÜÙ°ã¤xòe;݃R‹`f€y×ú›7a£6ê&qËÍL QPŸŽ›‘íý%ÃÖtÎÁמ‘$r^8þm‘BYfŽSú¥hоƒP§ÖįØ9—×ßTŒ¿^æ¶&Ѐ qüvæZnªÕWV¡!áê°ç° {A"ñÝĸ~h>Üû²"+²Èª¢(Ôœ?·ýË5øtlÍBŒŸàÀª§XCÚ§nnX¦ N‚Æ¢Ùpug /:X™¹ÞÑj‚سT˜wMáþÛ}`îò×dO¡7ûeý×%»i‘‘aãäKðœ]UÞ'¥Ù]óDÔì €ÖEðRMœÅÔUæX&Ý3y â›4Ù’‘¦¿‰ååÍØp¡òs0–.2¦£ggé¥×ÁèÂ)få+ÏÔšbXº«û £‚•ØœçùLöQ Ô E±«ëtÉx÷>ªýq}¿Y„EÞ Æãm`ºžºƒ2®Y=Œ-»­™ÇúïøÅXJ[O§ò´M€áø$¢Qž)û>ñ(Ú6‹-š=‡îÕûÚŽæ?KS>µªTisÓ¿ú¿NVà Í3Àñ¤:Ó˜ŸŽC#œßU1îìxºí‡]X‚ÓO0¬ÖÇ#Ýél±Ê&L’D ZP8]†Û»ÐÑÓY^ES±MªËL8Î)HÒt©:ˆÈSÅ•'3óy 8FÊ«|²a f!½×(€Ùl¹þÃû‰ïRCð9³+ß8@ŽØnÌò‰Gï3ñà»óÞ,—Å´¸tìº"*8_n¢}_|‡èîÐÆ‘ØôÃõRè›ý“ð ·P—ÔÃøáÎ$ª”|‘¿íŒ&m›Tºú§™õˆö*ÅwX…/µFÑÂ&+Sfÿ«ÿ—2®Õ¤ÍtžcÃôI”Ú\ãÌBžWÑ<\ªKKåJId´õº!ìòHf;WèßÏ]êCÿ,ŽÅ?Ÿ*\&mTdÅï[ û‘k;–…wƒÂ8žð2fæqøî ¯Ã¹ aJ0tv+4º|ÆÉS,èýCø¹P’>yW=iM¼/õ—øD¾É–ƒù s?Îg¼®(H¸¬ W[ù]ÒÔO=T=¹ÐL7Ð}KÃ.…s}po›$ÒÉ;= ým‰e¦2T+ÑÜÜ)•{*@%/ôª4X{î Ä—„1¦ëP]nHÿ«ÿ½=(yŒãB4¿¢šÐ.¼yü O«*{”v"}-°u·4;œˆc컹îÑkÐ|íq8úÁ~ß,ç=ºý‘Ô€8ÄŽ/®Ýkš ¢¿—ÀµQ$,r=t†XГ®`.ø„×-¾EµºÐ¸|#Øç”ãïøBœiÝŽ•?ã°+ã èµ^ÅMúø(rµé…w‹àî˜Ëhÿª–ï«áË­{Ä"+RÀsg1»w¼½Ë;ÚQAtÚÍi‡\9¿Å§™Ä„”= äÄMhÚÞ Å$6Ë¢6=€Âau˜µž%jxÃl±”÷¿Ì ÎCÖŠYdLe Û®Éö*á÷Qt y]ã¢NçìjÀG/Hâ'¹‘þ?…îŠUbÓ½e©”™6ímìãJ¯ÊS­©×ð¾(°+ý Wµëëò$VÓúôLý=R‚M[±“ÆsÈ…­Å>-?/£×0ùö`œvï.aw {xÅRKXšø·fI.Éÿƒ‹KÝá½Í44-º’i8¿¤•­ntbê /9‹Çj¸OãÏ_X‰ŽkO%AbŸ¹çœ+mè—ôiÁ=Êëÿ ?žäqYf’,ßêêMc ¦¬ƒñ«VÂ^¥Uÿò¯ýÔ‹Ú)³ñêä±ì«ß—u»SŸCŽt÷ÖfÎùšërf¥Ãê¾47¨s%l¥©]3š})N¦ î¡ÿÓÁ:û=cèž§ó°qt*œÞ¯ÈšÃVи$œÿK¾ù2n×>&ºï:Ú«“ÏBÌ©à.g3vº˜EMƒ ØíÈñLû–@}裈ÿf­¡%¯ %åAÍc"{}ù,ÒËgëÖ€x¿Ý}ÿu0«"ŽéßÙÙ¸{XwZ»ÃÊkg°ïXÛû„¦ï²`ÒÓ6üæKá×xêçq*sËÙýc lwô_þVSV‘›?ï ׅØí#Çþ<ÌwÇæñã÷`ûå䘣èä#×dØü1œ[Òî…‘•|îIå ´Y˜ƒ]MçÁ¼Å†ÛêÒˆÁl^H˜O‡æŠÂÇrm¶-;/œÝ‡ÆKÞc—f-t¼¶Â·þ7 Mf9¨ÒoÝ "|O6N…\qW7äA„èrbP Zo–‡ÝdŽSîø”Å¡ò LïøM‚~¿‚W· a”º7—d•Œ×× PžU*¶\ä_K©ÁòÍó8ï³Axéòhš´ ï¼>DnŸ¶Àã¿8ûû6Ø%„éF­ÿÖÿ`ÄUÒü¤ ’cšÐ»í/H¨Q^±W9ZF8R¥ÀÉ8ú×s,¹,Çâ7Ý ¸· gz Qö¢6„ãá©-äá{AhzÒ a¤Ç[ý­ˆÇïÉå®ì;€ò‡ÝÀfÓ7²m³5}œÓ=ŠÑXPŠ8O;)¯0äÔv>Å]â/àQe8”¾” îßÈײìÆ'®{«mw($“fÂ>·éÀm³çV®ýCŒ› 1Ydë¿ú‡¯î‡³N²ñ§5}÷¦UzD(uŒÃø„$Ö(Üsîvàæ%>lÂgY:w>¸_‰Ë2áÕæ¼ÇŽEî.‚™ãÌáåÝ6Rûw7§S|g(+Âk3 &ÌM•¸iz’‡»pCZ^šeºÔºs “¸0™øw-†è©oáêŸ`:†Û€ž*[qÈû/wP}Ôâ¶Ë¤S êà`È!Ø2E£;oìõy­\Þ1…ºß1¶L”áˆo}—b…FúÛ¾TmúÇnÓ|¡¢WQaêˆzЀRQtâ˜iØ*Kþõÿ‰N-†uMèëh6ôCpšœ€“ ßø»õÕá[ädÖߦ†Ó£&qí¯@ëÌxvêï-â[¸‰ÄÅdƒˆP w¶ä2Œ …]ˆà\E”ç¶±™B”›ŸÅà²y®÷ï-3c#þtQÆhþ5š-•;M•ëWïí²Uáþ·sî '»b±øò’ë>û&4Vícæ²Ã1È·˜ÊŒmérº„¾´oF+û,îÁóˆ[Æv9²óû­é’к¿C‹V/‚38÷‹ŽLiûî¶’ [Të„i;6Ð<“õŠê#­™¦èiþ º—ãàç4§$~ìÝÆÄ·œ†1g«ÑV7–u¼žÀÛ=æ!– öÂkr°Bú4.‚°K„¶ÿÝmŸ%˜Ñ¬ËDñ[@ÌBÜ6g Qê^Dj¨-.»†ž^¿ð·‘2›µ­7¸ç‘N-Ú9š­žÆÃÙ3t'x ™/£wê(ÒMåoÓLg“v‡]ÔsâuäÏNDKRòÃu\×aëË“4°þ +¿ôÇ苲Öi;ˆv£ý`5•…Ûž…‡âàåµ…KŽfCÑVôþk{V¶Bйÿÿæ>]žƒFº ‡JÂA¶T‹õGðƒ¯àƒ÷³ï@{íº÷ÿóµJ‰Ô¯arÛñ.O(ƒIñ&`þ¬sŸÚ‚âò;X¸w±q¬sYÞä&—ÆPßc¹Ü†™—@u †ÌÖ_NŠK³ßбPUŠýE`tÕ *F‰ÂÜõÑ\™M6žè 2²iÿ}Æ[±>ý<ÈŸ¶(õÓ’ôœØHoµÄ@·×]bn[{­’¸ž¯CÐ Mó‡ÅA{+«Kµ99ïx²vÙ˜#r ÜŸÂß³Håî°¥-"ñ0FØóF Òà·)Ü“î*b–¬Î³íoÄŒ=P I yŒç¶¦‚ÊÉ»¨“ÂΦÿú_ïË,¾–vi?[6õŠl(ÁÆà)Ð0=<6»BפÎ~²Ù®øÚÿ XLŸz ¤«ªžo?:œ§æ¹‘Ù”_Å“£ è› ¹ d²5RgR­ "â²HâLe¾xëî;ÛUÓ4XC†¨í!ǾIÐá§)à ‘‡Tè*lÿooi½ØAzXÓ0ßv¬Ý*È3¤Ïž["¿Å×TEak‘(K’‘cìM½5¼åÂLoùóºÏ?ÁŸ`mæPtŠœrÔ¢âv/ñðÏ­x¾à WØ™ ‹f"uYñ¥•Ô€üŸþ_p´…Ó($Å݆lÕσ 1j y¶£õ>Œè,Ch¸'[ ¹íÙgqgMЦØÄJ„ä½çðéìϸÄðæÈZÞc®Ùá’ˆ}píÒiÎiÌ.æÅ“¤å‰H‚î8ù…xFÌÉ”) ¿Ç•}Ôñ¶Ñ w,†á‡VeÖNBè í‘>¨*3ÕÓ¡Û1•ŸÙ<—ã†ó]b‘ë7µ¨n´7§Bj–~!1 ðòŸK¸ùç$Îë(¢3¡=µÊðýuLÏ’£±ó«a­¬]Â/âeÆLGƒ›ý<§bÕÜAðKÿ—ÿMßJâXú„³<GKf -ÍD‰`EeÎ6Û)³õé|‘÷@5ìæj×—Áõ•8…¹3©•K- „¡a9ö‘dø„ ÷KY*úê-´OScöÅ1TðÞY¤^霟À“Ù€/ñQîÝ$ô;:–UDê£ÐM/Œ’^¯š^rƒ.R,“,«±Ç>ët^³H!æ¬N`DÙˆz^”¢¦emÄéÔ.vâøbÒñõ9 «QÓÅùÄï› ìŽ)â ”«³×Ř@ê#¸šY2"šìÁ›üwØò¥Ë¯Zmˆ«œÁ4NF`ÜÝ/dœƒ*÷øN",%ñ@?WðWŸ}[V¼!5åµX]§CÇ éÿ·ç3}·y,Sâ¬áÒß*Þ\­:ðï1cë\U™Ê\x¤—í±¡—Ó ^>°ï¸Û£‰ÖâÓaŸ§+¼ŠarªáuQž•¢ ¡ÇpÍÞãÔvm!1³Î·ë±ãôN6ûÎ"újÆh(œþç ZÒ4_M¶Æ}>ªkle¿u™éw˜ßGGµ‘êÖ]üïÎç8ݯJ°LW‘U­ZÄéH$“0}i::û<{]…}â¼9SÊþõ—­¯øwºæqa‰ãéêS‡0b•ê°;ÍixÌIÿ GzªL÷Xú0ùl{2–Y}þRW%‹ñ}n @‡Ù´ ‘ã&òÔês0ÚïnF·»$c‰·$8­[R¼ñâó¯$Ê£ö-ú‘µèÓT)Vßx’ ÛÆIÅÆaÃC x>ãöއþiy8!ϪÛtÙ‡âMX°ÿwB«¾þ8G‚ÇaJG%L={Ú_Ý®Ëý¶r·Ì‚§ïAòÏ‹Ðâ6šíÏM'‘«ùWX¸\‰,Ý¿“–ü>÷ì¦ÁHž¸ÿ\ù¿úŸ;Fò›rk–A¢Ô*”{Åݯ¢·ÆFOóø帲æã˜l­ƒö3qËþi¸Ã+ Þɲ_j“ L«Íʹ;ˆí;*ô¨ïöÈpÆ‘ƒdj– Š)E®6Þ²þofÚt ¸{W§ó ›mÐꢷAx:·ðBjXNÆ^ß¡|‡•+ÛÅiÑ’Cu1[K‰ö7sØ&oŒ¿R]<$ Pä“ ÆÎÂͧä©Âó- óü”ÎNÅMm•äNØ}|›ú‘¤nƒ®Ã#¾=àz\˜ ÒB æ]É} -=ºÿøoàHiY`L«Å _æ+àÆ½q°âv9‰jƒûuèþ'‚pÿ— õñŸ 1W/cwÞ[¸y䈆Frb¥,> 74¬€ ëß g~.~’cK§}‡oÓ¨…ƒ!õœßLØöJŽ¿èûÈgT“ÂtpÕ­fò;Ä€ZØ‹J§ÚUNì§íTv{¿1ÿZ€6¶H’ükñØ ξ7V¸d••CoÎ\~Bü-ñÆ•&O¸Ša+èzãx3œ€Ó4DÙU¿C(ýù)<Þµ‰Ø|7:¸œÓR,€kÚ ü)7¿aÌ!öæ}}qàÅ?ý§óp¿¿—˜´è³«Ò¡ât S¼;“瀴\:–N~ÉÍ}„u»‹9Ùo`äøº•ÜÖ»?¸,~n°h#†mø(pªì¿D®${r³&¶ãv*8°µzËàø-+îWÀ*­ÕøW`ÿ ‡[Æ?q¿ˆ‹^…c9¿]ÛeàN¶Ø•e^%Ü…naÐ9 ù äÒ{{F˜îfÌÜê³'ÇàÇ#{ÙꓚX¨Žc¿ÈCÛ‚z8šì š3æP·iÝxõ‹[[¦J>Œ¬…’#vÚ³àö¿üC3öƒ÷¬säþ»óèã±nHÕá°’-Œ< ¶œª qé‡ÿ’â ×±3}Û>6—îŠ¾Ž©OƒÂýðÓô¯)³3¶r°QQãWÎVŒ'AÛÇ€¥Þòp©TZZ3]¶DCïF zóüyj°6Š>œBÎwÁ‹(Êí°U¢Ž8”uŸ@w¬YÌú\á¬÷,ɾ•4tu58¨Ä ñ¥ði¢sYÆÌ4$iïsy–u= Ô.rÜ„]NðÑL’Ž>že»è‹(-ô½¤LòÃTé•ìµë>¾iê!¨>;‡¸ ÙýÃÿ„Av'à 9\úÝÕxùKGAxò4­¾[£¯¹,¶Òg®VÔaÍq\ísM««ª£ÍÎ8{J"TòÅXø# †%ýŠ8eË;\*¹Rjìrp&$ÕVóM3_á˜3;ù_õÙΣ;Øcå§äVí&gIÁÏO‡l3Ø‹ý•I÷ƒB´M‚öú0³Ú•™R&jö6¿5ÆåÙ<ýð,,jŽA³&ÉOq¡¢1µ[6“jÔÔA¥Q(E7p™«Dé²+Šì€çvª~ü—è·‹– ÃΩ8W€F-ÍÀ4{‰ù·½Y†«P´œ {Æ·à+ý›œê°ˆ±¥ê'ážxOnùë[ü㇄Xõý¥ðÄÉ&F‚O@;x¨àj¼õÀgªKvžÁÞ‰$†AÅpÝP•ÍçuØz˜D–BøÂ<¼ºà¼ËÌÀkÜpókîpÌ=^ïaˆ³«çT4‹y…1ID¥Ù¿ç,Æí+ܱùo/tßèYz.žóä¿Ô €Ãâ¥\¢C(jb½â|TÝÜ]÷4U ¯D&‚È©ƒè¤´¦„£É”£$," u3¯a~ñ#>×û Ô>póüÑ:Lš.x—û¬ÛôïþgÈÓiÄøc¬‰þI:¾—ðù»É¥™áàj¢E'*ž';_¯¤šö8N\*5ÃØ;™o![ìªÂô.fAIv8hÏýÃ]1¦Ã©ê øÌÆÿÌ… ÖCÜÂW¶´l(m=W@h¾ù#ÂÄ45FômžñÕNSÇ¡µé$ìN½ ±EBÐ$oƒíåßÿÛ šç²Ù>+ŽG±Æ|>fìŸÝëY»3¨·ú´)˜1g»+ðнgî¬oò ¯ UbÙߨþöÞ8¶vx0$¥RȘ׽X÷¬%ÿåï.Û§4P.]‹Í¸›‚OÆjÑé!@¦Ýi•Çø ‰·mÓZ .¬¬×‚ý7?\{Ü”¿¯;j´#že¿7fIÕ·AËÞˆ‹›™êD&[èQ;O9²éª?n~kË&ìÉ•’ðjÜ2Üýe4úGÃa—©`&»†µM¢²»vàc–ù±\ƒëX8v} <ÉžÎ†Ž¥@ Ë3îºûzŒ•9@êé-žqm·1EÝÖQôûâuMWnA(•ea‰O {ÆjÔU@ÓŒ¼Ø×r¬7[:ÈìšÂ¹'@}DçïWÊÿ‡ÿ—¶çà’Íf9[•¶L™NµçCèó@´}ò<ŸsçKù`l³[;G Žyy²ÙŨOÊa ‰züŠe×!wmŠ.‰çí|?nÚãNÖD©Sƒé­Ì5<«mÃÙQ—ËÜæCk°·³•œô×Â|v–É%i`vå*;í«t ¢·ÚË!#Þ‘–ßpÅ™mf[hn”·KϨÑìSP¬oÏÿ eÀ×ú6×! #™òƒÍðyŒ/T¼\‚…’9,cÆûd jñ¶ÑÎSú¬ðålö¢¹åOØQ»5AøßÌö»ƒt£®KöÎÌL‘t%~²\%§÷JÏD.Å ºÑpó×¹šuÜVžãß; |HŠ5pgpÔÄ' œ¬ƒ ]Þ\zn ̽“çœ)YY±sðt‚ ŠV?Æ[ÆZØßûŽÄå|qåL>Ñ cÞœÄátü»Ä—þ˜Óž+¡‰ï9ß”D¦(Lb³gÍëqfà3v ûû&8ã÷¯ÿ™<:DòZIœÛ^|uD æØuÃòÀ<ó¯7áBÚlj9?kÚäqû­‡ø«dŽüŒ¶㎇¨PНºQÏpˆýšÃUciªlA¢"$>1¥);¼Ø¬²…¸æÍ-ŒZ|*n/Á:ôÎf:þöI&nP»O?¹'»`Ü@×Ðq~ÑH|¡¬Ç~â8 æý»ó;1ߺãäÏœ¿+ŒŸÚ÷ G¹ÐE a³A"ÓMªæ>ïS€g;–Á͘Pœ«=ldCù¥×±ç„:[·Ýü¸mu&«¿Ãý<+Þ×¹ÊÿîÜ*1¼FnkóúG@Žßú"ZcéçÞT¨Ë”-NlযÏ[½U¾« ƒÜ¶ûã¾û_™Ê¼Fõ'‹éõK'༥<ŽûFsvá·(kì=ÇÔ†gpwkðöë·˜q> ~°¡; Y§Â´‰¸«‡qûÛg\Ç+,°¥â¾>t©ôgÈø9“èeX@ôT_ö©Û‹šŸˆƒ5j˜ mM.O…‡rèf]‡©·fÃÙ¼`Öþo ÖZ GÝKÐwÕ!ˆ4 uÇ䘓V1. jÁÌCºtjW"8[XÃ̤½ÿð¯\-ž£`üÇõtsq~¡;[§FCFzñêß:Ð2ë*ïÞ¾Ãd‚[?wtA/4±9P¡ýÿöV×§{¸xQ9b5hO—M(C¯†;dmìh=ߘݓԱñ~8¿ˆn>‘¼ëÇI1 ãè¹ó7¸ùAg±‹k†N/ˆÖŸÏÜu¿Çøæz"¾=Ï-n#kJ£pKªTŒ }Ú2ìzk××f±O1£Õ þ›¯¾Z+Ž#¿AÔ—ÂØ—ƒ\jî8(_=ŸT „Ç·æÂ¼$9TJ¶ƒ'~ܸø^rÎY˜Õ/UùW…ðQP<Š^•¦©y§p­½)[µi"“ÙrR‹×·†cýv1ÔuUÀŸù¾ ¢cMtçP­yn´üób\}…‚¸Ö|švùY)•B:¿8°cî’W+ÓÉ…¾vXr’“Э o’÷.*Äñ\G- “ÿ˜r'…$°×Ö–©ÏK§Õ@yµ>K ŽÆÝÁÓé²c6ô©ôI(·ŽßKÅ8S/¡–`b¸¬—KÑĉRì—.DnøMn÷èPÝËOÐ&Ëšu…óhv·l?) ݃qqáKÞ© ×é­¤p¡®†SÙK¶XÿãÿiŸÇ&!¿­„µ£!fÊ ,OâŠOMf?>4ro ÆPã¶ß˜|eø—-¢k~_‡]á8&x2ìÞ™Å{;v7ï}æ©;QÕÆfܽS÷Ý Ÿ¾9`CÒARîØc'-·£gÀÂ{×@ðaqÅ3K69¹s{øU7¦Ð´ÅÉ ¼µœ¾&s”´î3d^ÝÝ`€÷°øÝV¦Ü•Ç›½t ¶òÒ¥~¿þÃ-䨄À(æ½ÀýâîpãuÄÃZ™ ìScÔ~…§€qòS(™ößòeØi›ÑÔ§ÿ¿`þïùgëwÀo.Oq²qoÝ8Ü‘ù+OïÀ¤ã-ôu.{UôÒ„itœ÷Fœr©‰wyO%Ü K.ƒ®§3_³‚ò48®<;Ïs« ™îM4ºúr–æBÀçà#a¬ú9,k=X—7I^lN…7“¤þåà¦ïJj:6§‡-ßüðq_3ü°GÌg߯cý¾ðûË#¨Û5‘(ˆxAjü'üxÝÔÍÏ–þcPP< çÅnóB*Mñ—8G.Õ&RO >Fž3§Ú®?@¢p4‹+8]×(¬KSäéeëÙ3nÄ_[ÉÐÑN•ü>ƒJò°gðF¿D;5³Tf$ó—n2Æ}ãyôíØg¼ä_zÜŒÇn¿Ä§LÉMÁMxîþTÐðY7,ÑyPºÂ¡Uó¡Gå5·àRvö$Áã_ipKÖnÞæV‹Ïg/†^¹¾ ºCÍPEñÿþÿx>P€ˆœÕ€Ež„¿^ŽÆLzÊ*û 3V5s‹^§ñ¦t<(Ðóì/¿g´3K7çQfÙASéÀc{šiÃÞvÙqÿÍKW¥'“IfÇØ}Í,ì\Äc÷G‹Ówâ£Éß=yp!¼­`‰"opÁ䩬ôÊ ~¨K¦çôGш[µð°­óøQ²}Gà­×sµWa³:Éš-IÐüt7óS=Ø¥VüÝ<«\:atæí‡BGñ—ûr^¹z Jg/!š)ŽôUnß5‡küqšü”ÙFEî÷F²xàWcái? ÿñÿÌÔLâ¾° ô “ȱ8NÍcv]ûö! ~ÇÕT#І7¹CßÓáÓKª±Êõ»ãÐëR&Nû,Us¦P‘WëPšd üò4<–`K+=¦Rᅥt¢‰ ]7ow6'Z7^ÅG¹MüGöŸ°öû8˜q}?·õîuÌ8ÜÏï´_:Ã$Ãw º}ƒ›e}qUqÄ ïåªèùbGÖf€ÏnCR*ERa[ædº¦ó9~ªŸx[>{\™®Ë_¸ßq•£Êáu¹&L‘ £gŠ…©|hÏZ ò¢¹¢O£¡9\•.pü¿ùo;ìPŒÇé™b´aV?ÏУ1ÃÂT…§ÂRÓOò-.ãâ· p¾Wœ6KžäGÓº$(…ÞRöG–æôáâsp"«¥L;׾¾ O·„ÔÝÑÒ¤ó4D˜ù½Rš<±Œ+:šAý«¬a~éJOVcúW"Ù­§‰UÚ†ýç`óݺiEº×M UáJT\Ђe‰ì£ÆŸ(mìÜLKÕÁ‚­“aT]"ûonºYEÚ–Z± æÇ`z©-‘íÁ¼ƒKàk{ٳРû¨…ÿÅåû¥ÊÌŽQW›µu×S“ØÝÆãX{÷ô?ü ÙöË~K#8^5Œã§Ã]û!Êö) ÷¦º)3ðæX 6J!ÛXO•ØÏñj̈§Ã´OD±É ¡\Ë„.`]¯¯±%‡î£‚V6LôLħ);é-ǃhZdΜ[¬h†R%*.ÜNÕž¸²}n—ø†â¨ÏÞ\|S¯Æì§íç´ãî‚"\št|–lÅÃ7pÑ™»È¢Û3éÄ£{ÐåÃ*j_KœF½…7f±=žòLV{&ûn®Ëš:áÆ‘½¸L·‡ËOߌ0l-x Ž?õ˜kA$^ªaðÙqqŠÇ Ýÿê3¶ m~Îë·› u·”©ï;bÞÑåAVÔ`ÖcHÚZÎsøpšë˜MÇüáï^Ä>%©â´ú¤S#`èÇ>ÎF-«ŽÚázm/˜Žw¦_ãBöðI¯ýGÞÇ™ôsÚy|¶J‰!S ‘!©pnŽžl÷£ÚP£*ËZä\ñBùÚ×<‰¾»È£#1!*A”|ñ³TYÛ'yÖ%¼œž5e¶-&ôY×PY0‘„ÔwÂß—ïøZßEè…õ"ìùñ—XèF{í7û_°ÆË“[¾7º¾ÒáL¶È³ž ÂLUÓ•e¹]Æ›öÓ¸$ò¥l†‰ÑìägÑ |W‡‚`ÀTµ9¶Kãf©¹“ëI;ˆç¸™œÞjb®ï‡="3“ÞÎCŸ ­.Ö©§ïx³ñÝîu€Æå$Öýá¿ùŸ¬³¸ØÉ&¬Ð·Îü*Äo…ý5…ð¢ï´ØÎè™,[I˜Þ»“CÒã˜òIK2ŽpW½’hþ®?(b2—¢Ã <øò©ÏœD­Î0c]/¦³õ5úÖEüÃÿ+í|°$×P×5'aY‚7ÇSv²Ôa´ú®È•«]3!+ÚçcŒQMr$,ö)Ì|¡ er¥@Ü÷ÃñUÌs³!ŒiøŽù´]y5ÍlšMo ˜÷ÙÍš1|Œ>½¶NŒÝ_q•3 Ø“‚ ç³ät6_…sÃŽl¸á&æ‰âõZQô·H$fö-'÷´¯cÉèçpø-˜~+º :©‹¨¸½ ¶2wð=~2–Âi…s—¡qÉ+ì’ta¹{C [mÄß™cç-A&TSAjíN’”Ĭü·HˆÓ¶Á€‡ºT1p'~ižÊ4»]™gLÒsBtŽä|ØV– ª­pyd­Ÿ¾y9gNe9•qðä—h5ŠY„Ä¡óªü0YIMgÖ‹È–Û/@ù‚?(¾Ä£|8­¾…µáù§æícôÉw(zÓF<ïƒÇc5 ”w 6ªÑyžªTòr4{ñÛ‚ý•Š¡ÁÕ®`zX–qú‹¹·ù“/åD®¦òÂNgpÞÁ1@Œé&K±ç_fÂB½Kdúh<(!F'¦ZÁòXgš¾|.û6þÆKŒaoúÆÐǯ·ân£Äú÷ó©£pµQu¾Ôáù­W"ƒ[¼ãy;O˾㜲¶¿¾Ѫy½9éx@Y“>JÕ'š#:Mi£#ý´1Î%eb9¾s<ŠÑ¾“`²j §z³ ;®öý¯ü=ÿQß—ãÿýŸöÞSÚÑPfvݯóJY!£HȈÌD2#Ñ–¥4M%íq¿Î‹„Bd«¬2 %#eü¼¿Ç¯Ï×ã~Ü÷}õº®gçœçyžû:×¹²!ÓS‘j0Nú])0p¼…ñ;J`ù©qøo=ÐñReºž|ìçqðñÁ#̬ç›ûàçksÜKÿÖ±ë¤e|Ô‡3žcpÓ¢º!ßF•àídy­zÖáúëWIt¬8kkU“²v€Žb0=œÎ]‚÷¶ÛÁ;8-ƒœ±,êÀý¥­ª„WÁì-úø]q ‘ÚgŠnKšY@~Ž záךß]ñà® :—XÈSž¹Õ–^NÍWÇPödá6¾Oô3tM¹ ÙuÄoÍ9HŽ‘ÃCjp46®(%?þbA í¨â.Eë(ªÉ©;ú,LÊœN¥EÏ; (· Gœ'ŽF/…%ÜðmôSœ¶§UƺÑL©W˜³si¼ø®º<;#5è¾ÝRБ hÚbÁd .R»_èç°¥¢þiý TÞêDÿÔ›ÓÆÈ-ô‡ê Üó3ne€ƒûTzóUòÿÛL©kÁóã_|3EÜ[4ÐÔ¡bt¾ƒFÜ.›îíGpáqغe3Þ‰û‚>YÿðëbŸƒ(¡‘Ì;䃉ÑK¾$z(uH ec^ÿË&2̳é64¾zÄ6_cv~Êô¸ñ¬?,O5.Íà5I<¶fMú҃φóWßùŸoÂë³î÷ã9<n4­opç~A[Ún6µªæàÏeàëEK*eéòý¬tš ÏÞñ¨ü¹Ãɤr’®~З߾ÜOÖ…ÕñWÇð鯠§×Dë0Á].2Ôé\4užŸ4àÿŠî±ÄGÄ8å [‘çB§œpCG+Z¤¾“ÿê΄aºlÕüï˜ã„ Ú©r¯;5ò1†“63é³Ù±(óÒ†zeÃÛÅP¿â0ÇF Á”K‡Á°ËœîÍÈCQ©)Œ¯~Ó̓”Í€)^;hëÖHü•Ñ«êíqw« x.J`îS#±wÓ/|z0nŽr¤ð² º^ «ù· ê-áºR3i’÷ô{‰–MÄeŸ×Ï+¤ÕÇ ìýf\¬<ŒÛ8¦×ÍÁ‚~{ñè"œÖMþwÎåø˜^fb§¾ zwNÃ\ÏxAŒUèþu6~8sÜ´.Çú—.Yïëìþ PïËOz|%µçË…f‡-è÷ieD(^ ›ÅÖAJåIÈŽ¼ÏÆ.Ev{:nªùŽÉºqù‰Q0f»€8lK"KŽÜ„v(œ>´>= ú2´A9+ÿÞ‡¹Þžð¹¼ùÆ’F±/låßÇäÓêl6§®S½°&0—H0ÏåuøÎ.ôþÚ¡÷°” ¢Ch·$Ã5Bª|ÿ›M¸ï*î;ˆ½­@F=¢¼wÊS¢tè- µÑã9?Ø·D´ûwl\¬HÍË Ô.?^ 6f©ì™Biqé‘wÿoö²'X3«ÈÕyA`o¹7ÑŸw}ö"Ïn—cÌÌ#|ïÇ©Øp/ Æn¨íø6#ëœyýxTrºGuÎp7ß8<øÝÒ 7ð&Ñk²í:n;É5öª°Lƒp:jq?Ž?\¯ /tŸuØÎ"t'´E“½ ‚ð™Ê|˘gvRijÂü®2ÁïSÐA>âšÕÊ|Å&tqô×íåD÷Œ"\w˜O‡>XK_Çô©‘©øí½?¶9 žñäÝOE®æx¢N&¢ñUÉû—j\ónBÔš}Sý"`ô›¹DÅÑžh<…1M'±)u?~Ÿ6‡þùª‚–¶ ‚%…ó_‘uõølé%Òz UD#P¼Æ€'† KÔ‡qå`¨ÇÓ®}Bëù{ØÒGY¡ÅLš…Zkñýd'ì @õW±d&Mƒ© À^ ÊK>Â¥·’xÿKùéY»ÒíšDD¨ªÃ-¦2øN­dKR‹=Ÿ /ÐLo(¼>«ôN°ç‹‹Ñr‰WÈAµ|lí^-,¼ ÷æÌÆ’ ;¸¸MœôñÅÁ£Òøʈó$Öø& Þ-õºøêaK`ës+ºÌðT¯ ÓIÖÑg¸wo ¿ÌÚN'E«ðü×ÙŒžõÆŸç:È£±ŽÔhß3ÅæµÈ) |œçN‹R$èß#2Üʳ¿¦oœ)ö¦T ñõɸó¬ÈÓ¦[ƶö˜%øß ëÿ_âFa¢-Lêñ#< ÷Ñ(Ä™J Ê„gD›;žy ˜¼õ687’¿‹BjömЋ¥ 0(s+5?¡ÄÛÅþ›µLD„ $r0þMX¾ˆ•\$¥­ãm/þ°¥cWȱc+ΰÇÀÓß’³ÎŽ,AD„OPM##Æ€…Ônì]U‡AÉùàé8ÓÊå![¿uËûµáÐk¦IÍjŠ„QžÆ,©[ \YÁËòà‘j#T¼³ ¶O_×,S±ä“ åáÖþ˜£hÁbܤé‰ÛÀ‹ã9.i…•ßž‹ÍDFR¾W©³úߦ8ª¿ÆÙp¨P…í{€þì§Æç¡dlª…¥AÑñ0úºö.Ê©æ{C&‚“ÊZº(T:ÁXLþœç³+K€H‰P‰÷ç |ÅbÞÚk)óõÀ Ã/›D6AEžþµ9mæŸØlIŽÙ"mHcæc½Û2·s.]'t§Ÿ´´è¾{#áù))Ò[4ÍÈ)ì“qafÓvÑeÆQ²SŒ#„⨘˜ÿú¯ÙQ›XœÕˆ c*Á[p·åuØ{nÄà›gsa¡ã{l[Âi¥ ›q~5Ù㸤eÀ"ué7rbÀþ¿÷Ã#³–‘•]Á$Ý^™K­öjq:ä»:Ùi•†$¶@Ñå¼”D6 ÙéÕ0ã¸[P¬Œ»û6ÉSü˜ï \r]x¢rÇÞ„)|*Èúõ`½ê6f“9Cê'Q¹BcZ(=ˆë“ÍäÄ…£‚°'£p|ÈÜ1^’ºyü|†çSø cˆø˜†ë"Ñ¡'bˆ£J0É]ŠCg¨ ‡Ç`äŸ w[ó1ÀY™'ïkÆ—~V*mŒo­ó!Ën$,žªúû¡U¢ÌEPxîæe” xˆ3ŽGÙ®¼n[s{n Žš"øÿøÓUËYäá×Bý\1~Iw".—ŸGk|ñŽÂáÍ‚yp5Ü‹l½4”­}öXð5¸¿¥¬;¦âWÈDç‹@í&Ðt¹cäý `¸{ ½ Wàþ<5Û:–T¡M§„pJµ<îZ嘩µ„/¬L»Ï§‰•ì5,0]Å §Ô@oB Ô-jdêj‡À½â_.•8Kôá.©åt‰ˆ,L?5D-Ÿ‘á 'sI‡@°Ÿ‘%Pëüiú|ô¯…¨Þj,ˆ}Wˆ¦K£ë™6˜p܈³Æ"?xø¶¾ò;äÇÖRÁ¢#Á?åý/+×´ þN.„îQç&Ç#°os4Äm\o´ßÛ}£(‹ðz2fO¨e£WÛcš¶Ùi ƒkÎŽ¢R&vxCn¦›…ú6¼¯q NlIÃÚ?ÖTêá 8èf î.¦°úåoâ¶W–s0º_ œÂÈK0zí—·`lš€+¸ð£a¢Ç·}[é«Þ9´šèÁ²ªböí{Vë+bŒ€»m›‡»î㧦D¸·ý0‹;ÞF"OØàµ[ µm.Ýö }Z²¿Š)äÊ—LYµ¼ÜEqåìhÞ£Å%6ïÇø1î¼¥çØþÔ]—Ù½dNA¡4ÏB…oQBü²i$oœÑT}EÁÍÙA¼ý9z¦ÈóßsNÁýƒ¶°'#J5ÓɘÃéÕ}P aA^­ˆ„Ývh^¸få«aÒá4y³Š•¹¹¤<ÆÎ‹'Á_ÃçM{ðŽÿ+6þý¾è*¤ÁÁ SœvÆ!Z£ /B 9Î…ìëEèTÍP'r+~Urà+g0û½R©•Ï2^†]!>´2_ɸ`\_Œ×ÏL‡øëlΖlsbαûØ5¼„mú¶I𣞰àý/á–‚^šú`€ÿ®mV [*lÀ({8¨ÌBÉåc¨òÄtèŒÂƒk5ùW¿Cpª“‘oá. tׄV^1ÆÐ±îp@úI=#Øà ÏR`.·£ÐRÇzÆy@üÔI¼aTü’LÜŸÁ`÷$ìRÒå×[£áúÛ2ȺíF‚Ï$òê!4»Ãჹ.- ®ÃîÂà}ZŒj<;O/§¹áU8û})¹7¶ ŸG|ÃüYxûü"iNÓ¦Þš¡?}¤>èÁ“¸ ¦ŒRbÙ$ÁPt!½w¡J ü$‚½×&Ð+máÆ½ XÚ¬=®°EdÒ€þIì‹ã«@]ã7+¶7¯[×!èÅC6íô;ü󭇜K;D^¯‡ßƒ^¢×È_$õ~‰à”÷'6Æ(v× ƒ`_ŸDµö%‚ºŸ±eæpÌMS¤¥«\!ãï]X|Ú­=mJ>,[ŠÇçÎaFÏîâå=ª|NŒ<ã=©Ð¬/ÃF}HïMøIá“p·×kŒiÊýÏ<‚­¾+™†Û˜þäTô\ ºÖWáèiN̽1çz1ì*¹I&iXûW.ü˜Üö9½»ü†¢ëá/ä¿¹Ñ ÃI±·ÏÍÚ&Vkð”;ä¢\"D( øÿœ)C!é~5x­9—ŽÖ׸´‹`´ª€Í7˜ÆNVá­ùƘ²Ì÷øÀèõ§q[I¤úH(P·ÇgFƒà.bö ¥±ÉV0ú›9¿­>ÂmGÐoúvôû¸@Û¥úqdxl-YáVÐÔýýÖ"¸›=Âøuø)ç"Fl³"Þ¶Àû¯Î¢ÿÞÃ靨æü KÖíÛ¿HTWÞ½|; .€ZÙö[…*L0âs?’£óÓàÊ E^„Žseþqé\<*’Ø¿ØJ¤>­„OÒ)P2'Æ\\ùÅ_¾™›ô©TÑ¥üWZj!TQ@ådúÏH¸2v^žôˆž H[—|Cqù]xÚ>VœÖãýž!µÌ :0‹Æœ÷ޝØ'~ ¼DÃp®°»¦éá5¼$ßò§¶@±K+žþ»’Éûÿ?MãJ¨Õ ž¢¦TDÁ‚v{Ý*޾FŒ5¥xæB#¡ýõˈYCÁ(Þ™MLÅÐ{Ä?xî3†ƒ;ÜØ3f¼£Á›-Þ 8kÛLxðóÆÇ ½ |àÇÙä9×íp_¹=å*œs3¡ƒ¥ ñ´¢zn­€–ô2rwG:öŸ†¯ýßýrµ'p®–4 *¨%ÃßLEGÍ3dŽØQæX¾Û2áÖ Édt¦Œ“S›Ï'Ù伺$U‰ÿ,y‰¶-Rvjaì“3Øj>›vö f,’¡FIVüT„8¯ "õY*4cC¼ èvNŸ¦Bä;C`±Ü&êôÈ çš"²/7à85ØÿC…Æ>á³ÛŒXwú’á­ŠEÖÊÔýÎuÈ¿d FõÇ× Þ:¦ Þ‘®¿Jª üàDª. ®©„4Ý*ÿaøïøp±Æ bìeI‚ùƒ𤼦t’¾ ]ŠwwHÓ`•©xîyâ€ýnÁ›ë%Ù©tiÿqœ÷çJcNM)S<9Ž7ÌÔ€T—Ko¼}‚ס@ÖLtÄ¢›÷È (5ÛŽ/§ÜÂðœ#0åEì3{…ðáº0ÌY‘[‹Ï ­w¡FíAô÷Qä[¯é’ÿf>«—ý†Õý¶0Á˞ʉž€Äm*(]êÄöïè"»¬éú"W¼§xõ—1~ÎÕƒ?<æÎu—Y°±™úÜrq6v¤]#ƒõ¯áÑÞuðäÇ´9…F Šá´ª+_ƒêø˜¥âÛ~#;0»Ël±ì‚"­…•þ#ñÚæ 1ÊgsšßÁ¬Mj‚7™Ž,êº-ì›+m¿CIü;Ø.Ù±epe~Ù8g.^óK—„2ب¬ƒ—FÁ͸dÒzÔ’XŠârVpëŒ9q׆ÎZçŽé»Åù ùpzQÌW?ý«WÉ‚þ%è¶­ƒK _ðÕ0=`¿½ÈßLøÞP‹Ëç10¹Å‚0qK X9‰ÚÏ&‡fÐÿfZ§ì˜Fà ‡ ðÿ±i¨b€_ëf3öì1äþÐ¥¾ñrüÇÇ/$+î 8=U£:×ôyŒÒgœ£ªÄ¾4Ä1ó…M¬ûH8q|ŠX6I ¤]é95S{´!jÞ5*~+?¨>ˆîsaPh5Dý:†/ô «ËŠ 7Û—¸ŽãÚ¾BØ/ºËI`ŒÃW”—‰ynò$“Ÿcëß|ÀK®º¨å]MÆç•Á.÷äêݶiúº OîD‘ï†ÒÅ^²ôàÉx–·É’ºÊøŸ*©á™”tZï#rõr+¨Tà™çqÊ#ìßLûzN‘“½"4ÈðýOçlUÑ­ 8ÿ”ÂÝü,í§–ýË—ç‡Ð÷—ƒþi“,¨Þ$óúäÁi9Ÿ–ö¯þ{ l=ÿ«Ž{ïâã˜WeÍ¥TùЉ©êxL}gSÏ=AܸU½/OƒcøDÐ9º^nØ¡€ñÖË5§-wзÅAÉn V”/ao~fÁ¯½1ãùoß§Ú³°Ã°aŸ;O´ãSóÏÂ}÷œçcÊ÷2£[fÁ’úhèª? «OD¢¶‚?\8™¶Vçð!2XîcGÞ,š߯Âëà¶­ V¾{+|xÏÛûï½ô@pàÝ`¾¼`R¼®ÿÍÄB±%Ú†~0ç¤÷/áƒÖ:“/!šüKÛl­‚±JƒÉÁïk!Ù/7¯Tá5%JÜÓ5Ç_š€kZâ ËÞœßØ^3GÄÃÅ®pQÝ–ª–ßc¢{¦PFÅYÙªWpYj2a—ÀMa1 ©ÆáËüñÉrlÙ¡Çï.½G¦Lë…Ýû~áÂÔÁ;Ì…­±&X”õ/¨ïcgN¬áyÚÅX¼?B͆ÑwÝë 6‚ÀÄ›ÑäFF%¬“Û ë&kÐu·£’°³ýŽCú>9ª}à$THÒeÛìkÚxHïa}pa¹ ¾J›JÇ_Åôüýü^Vº…jJ4Ë·‹Œ0“Yßmàâ2ùp`»:µ•]ËÄ]i£.nÚx«ÆápµÉÓãq«he¸:ËK㺙4¬Ïþ™eÇV( ±3·’æŸüþåY—fùŠò'äù²Bl¬¡Šõ°ùÁ~¸'n‚Ó5Þ“ò?f¸,Á˜ÞÜ4ûìçÂ<'5ý˜S¥¸a—8-mxˆ»†«Q¿ƒjtžÈ4”ë©Âû¯E©H©7ì¼ö§ªaÍ1f`nÎ'þ‹Ï*ù.aÆrà®Ó|/T°¿ÑOÄäiÉúó)~õô;ðT$Í„4 ñ.¿YË?º‡aÿ¸ X_ädû0/Ú®\#•_ìì®UÁšûO@÷r0ðNƒ•Ë‘­Å…AÏ‹a™ŠÕ;¿ˆúßXË…RœMé:ScµymV ëÍá²TQö.Œè#‡X^þy:(u¯£ª¶ÒԙʼBr^È”¦·¥¢ÅÕPîc5„ûóF7qæmÞÈ¥ÇR“_ãé $¤ï†‹ccñÇc­VDz¯ g4†ów©Sù• ´ü¹óö¨åYb«û»Ý o•Fð‰ùô½—¤ðvíI6ÿR…ðàÊ£´šÐ½«ÓÚ¾PܼFm¥õͲüÿ[d¦w üG8ÑYÇO‰¸ó¡µB(ž1„—É5 ÈW :íìh¾à¶=·Tš>{ßáò%\§ÉkÙl⇷œæÒ tÊ!O”EÍdRt¶™«Õ=û8=I›÷ÊßÕ¦»G?àgõÿÀ¬‡b ½p»¢Äg¨–Á2íl.Kov™ò2M3Od6ã‘§+`jh*}»ï4Ë¿®ÂJs©TËL:¸-#µöNûŒ?† ä¿=Õ¯Ø{Í ÜnKç_°¥ !{ ò©ÂÐgèÿ8 ²†ï©äàݬÁL&ÞwDÁ¤ÚS(|>‘ï|Ó…¨PTãƒ^í ÿÍ‘ž¦@œ_ÞG(ÿò_³ "R…»ÿÓ ƒIëc™§¿ß¤„+’ü@Ìÿ¼{ÿ“][Óˆuº‰|xO&ù$«ŒN³uE%0,?úÙnÃ3.;…Å~ôFÏX>ø­Ý¡»7!çÁNܹ͛U;¢vÛy¦©¦… YŠ×tŸÂÃǘöüzþp½nÊO;Ww7¡L@Ô9+qÍÁÁèRÏT7Ì#×…/áéTvË7gªóÙºI$õñT"¥t ËVáF¬c_÷>—µµ0Ç¿GZŸ‚3NÌÝ*Ù)©ÐÑ3 š¿²s7€+Ð~ÑŒ#Ò­øÃ]Î8Äš o4ä“¶oÑ5¿ }ƒîÃùgÇqŸš]ÑT@l®íÃkcºÜ­c@ÿÞ=4úd¡âòL¸­ÒÊ9¨š[óßBàºYdÿ‰RÁY#®-s’¥òÚ?¡öI8eïn@o_ >÷ZÆGI˜òZ+ž­Îý+k®Gç;¯“ÿÉyqnt>‹üÛºÛd°¸© æu¯ܵ”¢å»Ùj L°ª uÛ°èåƒi±MŠ™¾&ñëÀ­+Ë&Æ#—0³Y‡÷ë.AcÃù|ÒúDÒo¸˜Î7—†u ê»D?G  áùj%¯e-úë¸Ã¹¸âÛLu”{lÐQ™Ìs6ÃÛˬ½ZKú›ð¡æ^²°†.&ã>uü×—-r>L¶qio¯H€1™.<`™'t¨]Á½’òñ¿\ý+Â@Iq>Ïh.oŸ•«mÿùÝ+ܨŸBëëŒqÖÌ ð+ÁŠo6}ùoÊ)hîàc«’A9>wâ„[¾Ppÿ,ïéœìC$µ{qO¸¬…0³5Ð[µ™«õM‚»ôô\ŽåE«0¬XFøÊý½=ˆ7œNÏY±³Šòhèqž.6‡ˆ{nFÙ`iž²2M8­Iƒ|¸Ôº³o5$•îöm*¦Ÿ€’sžÁ3e#¾>|& Ê5bå¨J-`Í\úl)*KýÆýö&©Ï èÚ}#iís–4õ_]¸S} ÿcɹ½4mŠ7 ó€ëåùIÅ?þçl*€/»¯Íα³¯Å!5jùo†s’d4Ë_³G½ÎĺÅn¼ý•sÍ’¹tÃöÉô¨p VCÿh:ZKçý…î\½Õd;×)gÁâúL¾tÚ[%Ùc¬¸j´Îùu)ßt¥S ºq·úz”·³dmÄ âÞ½/YR¯W—˜o‹7T½‡{Q*¸LgºÅ‚¬ “&Ch×à¼<<¼™KõV¢úëG(ü:˜«sß’ X2$–jцO“øOêS2,Ý‹ÎmVtüw¬nH/ýãU£·å;;gAÝ'ô’éL*Óš^m¡}<[ÐjåA3oA×r\µÄŒºŽ½Ì™ê$¾nnÄå½'Úcî/M{‚a~~˜Ý?œ^üuÌ|ÞUð0ŸÍT|í ˜Ó]óÅ¿áæ/Òô¾©Å¡ñ²|M©ƒ€Ø|C÷·›ù/OCl]Éþ–žG¯Þ3P¥nÁ5ªD¹ý§`ðµoÄ=ê°¹íÎx§Ž–_FrÑ 3¨¿¤?·ñ'ü¹‰¾þcÍ ÆŸá{G8óuxkî\^lb4`ÿõÏG‘8Ù¸Lã0tülW¾§ø?ÄÕF"è`u+BêáOþ2êôõ_ÅÙíMP>6V;#nˆò‡Xfµ$ÕÇ—K.៰… ±p|°h4â½¼Ñdmp{ÕŽ‹VîÁG_…Ð;J_MÁZÊ6^- ßì‡W"š°Ðæ8þ:¢Þß…V§îá‘ Gi½¥6¯:Œ[=rÐõZ±P²¡†jtF~wúW·Ü¨„Ps%<û’<½‚½Óûˆÿ²QðÕóš÷~ù ñI‰üµƒÓ7QyÅl¨ñŠˆÿëo“m±ï ñë¨l¾ 3×gÂ9×O¤öOrä¡°ñäFÌ^]ˆîïî 2ÅgÃßâ£ÅïàªØ ¸ÿînXx Ôc. uÂÇÓW‹ }r:¥ªñ’ßøò[;ÙdeÀ[õJ*ÉÈtæ¼8“¸æÎÕE? 3ëÅèØµa$9¡ç/ZÇcûR sùsºä-ÉÕ„íÊ=ÃGƒ²– o?^IŠ'A×c:_¸Šeh[¡÷sW»fA€½´Ù‰úŒÎ…ã7ó‰oºXßÑ"èÏhGË„I°G: ‰ý5ó_B§;ðŠY„ŽB Xf©`æ%3\d>ñÈKöç±™±¸]°îWZkFçœ2XX2XP=ÌXè|[4Þ ŠX…PgÁøí}]0}´ßn2Äö•cfm,=²†&Y ä?ñÃ-`íPH挩CÍ¿éDÍ|»Ô,º¨ZŒ¸6 ¼îGÓ¨;wÉ»Ðd¤Æs°¾º‚ö•ˆÐOÈ.²Ä›—£éw­U|ºÈ î³ß‰)rã­›ux£f+Ùz1ý ² ¡Ôà~5½Œ‰ºL¬,çÑèFh{;…ÿ>ûƽ×ÃѹÔüWQCO-ù»›%px± .¼“†Ð³õ3xÝ Fަü¥F<üùêÿ© +‹ùµ4½L•^[mȽ¼±Ñ¾ÂáKøhÐ6 ’ Gª¦^Ò¡¾ƒ"ÙîúòþSœp4g½L«µ¤Ùþ“Õ<,kTÕ{bMy!¬Þ²†Z ¦öv¼d½#¿¾Vδë£ij­`äË0ì·’ˆ(Ùk9,mªß5÷½õÒ‹©ò_Q·ý N9çLEŸ²(‡M<½z2êd~!'ª.`þÎ?°L4†çýQ¢ç`ù4ÙíNOçKá§ûgÐlx%\”Ñà‹.„ò£/kâl¶sÿÓKÈdh:sе·È‚Ú§çfJñh;éŠ À¹[cÁyy>ÖWΠݫ;mgþÓÕÛTÝ bb>³û—/þÀÊí6õŸðöYÁàƒ²8²:>wk {+wÐé§VpSש|Ïèc —U©r¾0Æà΋}ªíúðfÄSb—ûâÖ¾«~ñ8g°‹Ví.š…(¼=œÙ$3¥{ÇÁ7æÚU^Ç«®¤Ýt>õ6çîŽ 8*<óüŸ®ULå·2B„;Î ^z<'‘JÉ˃ó¿—A})Ók?Iá`ðé þ¾yÛnkPáRú;tŸ«ˆwm¨lÌü5Í:ìù$Ê]ÆÃë^g _× &A„oÛ½Jh³4ÕŽ‰'žK>ÁÁ¸c¸ÙûìPˆ$®ëNÁ倣0|#['5m\Á<æ1¢SòW¨©ö[<×›¶jAmnUü×»øhâÅøOóã9¾"X|Ï ×z^ÁÕA²g6˜Ç*£çSI̲cËþ‡«/˯„³_1“=tâø4qñÄ­Ûèï%ËðEÿ ˜ùz9©³Ee½åÃ%†r›^;ª¼6­vyS­K¯Øg·b"yÏ»S§½ã¹™‡ÔíqKyn.Hƒ_]çØì…ÏA½§F÷•“=Þ4óÝGÁ©à‡ÕóàiÎ2îÜ·‘ˉŒ$/-®“®Áþ<Ïa…àôü½Øt?ä'A^ûS\,šˆ6ëËÐü™6hË® Šgv#×sÁÝóé÷øªëß}YÐg¼¦OžŽÛA-¬ 2Æ4 ÌYOÌ ÞD›N'S“yFtZ…)~·Î£¾ÛT©ÏSš"2‚N¡BãÖtï%TgXñ˜Ô;l·ë^µ×œ®¬ˆâ¶+¦£¥ð .]7 KC] <þ2…7­JÇÜ÷n\êm 5ZIŸ(ß#r/*éÝëŸpóÖ¥ðVù8:K–ï´¤×&-§œÞ‡{Ë¡îÜ*€%®óáÅü‹´ZÕGPXHw^öÀÂÊIúgÄ™¹¸Ð*„ˆšvÁÆšPf٢ͥ47Xä&~õ¥ÞŽÚNƒ×׳̹;˜­»8˜‰ÝDÃ×ÏI©Ž7É7²¤âA SÌZd`T>ûfEve5 ®/»Í{ ž-$ß:{áøA%`1UL®"m¶ÑåÇ«Á@&W7£†œI)²á¯Ú‡×|”8Èõ>)PÿÓž\ž9¸f÷bë‰D£™—t‰Óð òW½UÞ¬ô×°ãÂCþ—ÁPÂ}Z×áþSŒˆ)‡s×7ðï=Ý”º‰»E'À¨'ÚüKw;6J™ŠøhÓšYÙÄâÎ:‘.~ZšwÏAçÔ£–ûÄù˜ËCá|m¤TDóÜ‚hAÝ>úlôk<•Q‰µBجk(H¹)ueÐâÄm‡ÚóÓÃÇÑL›A4È&\¬Ÿ²_sýà$9@7Î~ó¯÷üIVšÖ ¦nà¿‹UçaÚþ}$ß/.|¡‹»áG“?þ¶¹Î"æUb¶d®ÕÂ’æ`ˆ$× ­sÜŠþ„Ë*Ž0KþèÛ"Øíü—íjDýS°kÍ;ø}góî…”Ù.ÿò%uVX~T‘jJ'{]ÃlÁ2LÈ)@ˆ¸‹&@i±¤ûpHíz8¬}^Æß¯ˆÓL†'sL`¥üyÒY¤Í«ºÖkWñëYŽI nÔõ®=¶×³ÂÂjù×°}–ádÿrg¼£jµÏ^³å¢‹iÙ®«ìü¹µøãì°\ü™ìsFëýoþO]9ê„Ï¡¹¶=(Šy©.Öºþö¬*!¿fœ'síyVR «¼À¶+Ôãݸ·÷/: “·«4z —:|XÙ…QÔfdªéi *Ä&Ë’V¸sÅ­_ü!5/Tx–ù2ÈPô…Ÿv°âªGqY ~ðò€»çõGã­æ \ ÿúþû[{þ ßÝÊAûÂnÏGRÿ³ó8Ñ$hÍèDÉ ¬ÞÀ’ÅÊq3µZx³ö>­ø[©­à"Èî6¢úÊW%çá˜è¹üDÊ z§Á§ë¬¥Q›z: ñÖáÿ¤ãÐíQÖi=—¾·šÔ´þT’SjAGú„ðØk'h’G0dæ\7€©OÙÿýÒ ÆÃR98ëÊgæ‘3‚_-~Èà¸uC ëôyÊ$C2~Ë\ˆÆûdÖ3+¢¿\’+¤ŸAûóÀyX) ž´ŠÓ§‘zÙ¤sE˜¶+F ò„ÛÍóxð‡y4÷~(~›€ ŽN£ó®(ÐÊ[­°ó˜*¿HBá¢éL¢\+‰ãùµ&9>²ý,JÝìÃçuù¼âYu[,ºŸZ)bä>x|gÌòp‚5jÁì@î:²M.„¶ÎÁ´€½k‘·Y TòâKÁ×gªPi¤LÕ¯É`ǽ)lîòÂÇ\§Ä‚«-dÒÆ¹|´ÓU¦9±¾ï#ZÐôµà­-xVñô.nÁ–Siü¤X6~¬|ô‚<ÛrÕ.yÁñ¦d®» WuºaÛ¥8vˆI:›õ˜Cü ³Å®ÂàSãÑ#1ç<ùÊÔ[‡QÕþãð~^ /GŠ+ðIÒ zyg·lÿÙ.Æ4“,àÕ™§pHE(¸íüzë=°¯8‘MUCÌ­ÆsŸ÷N|¨ó/vñßÙ1 88ŽÛz}„çËþq¬µ†@«b< µXŸñýšý›cg~ú3û6ðÿ’@š|rˆÙòQpß<†˜àz‚& XÌH <â¤hÿµ}LÆ8WnÇ­)tE@S™û…¬}ÿÑnзÓ|íucº>íyõP*š-²-„É˾³kR_@ÆøOµ|Rœ^­Fß»+Шç$øz5† æ>¹鬧Y0Ôî%«Æ3piÈ*t2‰¥3FÜ„gFçAÞ§Hèñe0“=ÕÍØ˜0€òP,Çw{6Ð÷›ðÿž*®jÍ÷¥¨ÔŒåz2tŽQ>\pXL·œ= GÏ_eÕÂÀ`Ò]ö&ù&‘:š›]wÀû²Æ!¬nÌ…ctèÍ·Œœý{U‹Ê˜ÈÉpÒg]„¦>|¼_ÜߪFOH?Ÿ»ðâ?‡ }[ƒawªO¬ÏG…ó¼ÝšôçƒXþÛ›Þÿ8˜Ö6o‡Çíg…Qçïà ¿MpÔã ´|P¡ãßì‚Ø!«ô eëjp³¹ƒHÓÐ8T™bÄûºòÕ¿_Áã-KxQAíǸå¬,>nÎWVâ¥ëÐqQ9Xì—ðÿ9ç§±˜ÜX¶¤k¥pŠß bª¬FÃÄNãàKçYrvÐû$Ö·‡Ã¸“71â²4ßöXžÿiÞ€!ùMBéÝI°[? GvàÃ?l†h ’Â[à·È­"=áßš¼6€Òwfa¢OñsËÇ7G/`ë"3žg›ÊVöüyÑ…|Ý=†ç1k¼7±Ó«Á5Û­ð^¼*æo܃Ñãë@^ã+.>øS|¥é÷©nŠ*à·ð[Ý@S©Oh ’&ôï/„}¯n‘“k[³¦BÑ$!LƒÞžžI6ﲇÇñs¨ú‹Qèí';Àÿi wÑbadÚ»­zXˆ÷N6¨Ô—4¯Š\5‰ó ¾aÖå®W‚玉‘IJúøwX.ýSK®Š2;Ïf6¢ÄR*Ei³o4yvÌžij<#vÃJqAñnP•Ä’ kÚWÁ݇oñ®g0ªiŽâŸ–8}L$KÛ[NÌã(²5heŽõ™ÔaþhšÓ>œ¦Ü8Ÿ7(ò½ÊÌj–ßq6¾Dí¢Žü¹0ï@ô™Òï‘7 s,ï˜8–vÖ¿…ï»T`»”3ÆæNãm åà´±*9†™7À¾ª~Àÿ§¥-Ƈµp{ÃûƒÆñ<™ŸPÛž›:†¡•B»jx£¿F©¯Ó1Ùþ>^µ8*œ•Ŧ|Ÿ½1®Dò‰$íR­ƒ­›Ì=ÙmuÊ3fe° +-4.[C\Æë‘â;1¸§¨ú;vœ± ÷Œù ×$7ÿ8Ö¯±²Û}Ʀơ§ö…c׼׸Lô”pãäNaýëØ6½–E…hÓñéºñõéH̾õ‡½Îc W褀ØÄÓ°h³œû•Fö~_€Ñ£ø¯4[¶´JJ /Àã·¦tç‘Bˆÿ˜…+޳hÉ.xòa6Ï¢jƒ`\àœ_& =/O’W!6txÚI´Š Æï\àPåoX;E•ëì¥J¥o°¬ô ¡UÿŠ£vñÉü¶ñ®Ðwu~Š Ä e’£€ñÕãqÕ•c˜ë]!ŒJ³ƒíu иr4ÞœY vnÄpöxÚ—¶ÅÔî¡Gw-ºŸ¹Pã?{1®×‘ÅŠ‘ Ajºk~?((Ñ•¾—`¦_(h›ú” 7îì{§”³½+ ?Uš[<ɇ•ƒÛqxÃl~uœ™Rj.øøBù+޲q‡áü“§0úŒÝWLâ&*ÑœæK°1è6´eÝÄ[­ùN«Cäô¿X1?2ˆŠNÿï¹ûhšãWöi¬hŒLÄ#ÞhæV æYÒP⤄Ö%Fþÿ`ÿÌßÑ$l”{‰Í=ׄÆŸâÜïwᎪ!NÛWãÚÙáÂb|*®KO?Ÿ@oïg¤º`ÿ?ÍÔ9$¯¦Uß ÌtLàϼ üÙØa‚Õ“µhÉ¢>²Øo1]ù¥Ž[ä·4¾åØ-P³{õÐî!‡¿âÅÅ*0t¤ÚTOžÚ¦ÃïU²7¿,\ NM!†Íº6†ÕZ1ö÷lýä†_úFD^B³Ï3ñÇKQl˜ 7n_`akÍøÊóË`{¹’wØßÎ üªtf´Ááx¾U€àæþýl2«B~6âβ±˜e2¸G „M+ÜxpFhdÎgbÄ}¹…Ú7}t¥Fò¸Ü¿ì.‰F®,O ׿ õ9ÜÔC˜»(MDVcÈ)Gtêãiãòð÷b<׿w[£È㟠hq‡Áá×qëž´11j þKÍ ‚æÑ˜S±È”BÏ‚&< Er?‰rósÌŸ.ìLÞ7'†w»ñæßVmRƒ»Óö¨œ] ÀÓÝeU“¶rÙw+¨Î&7z¾¶Ê· ‹þ^‡òÎzæ¨ ÄCáÒp·m(½ÑîÀÅijáïÍ1ÌÐB™ûMueáðÁP6ã+¥×t¾ ´3œ`a‚¹ôî~q®ÕjŠ÷ƒôPK·å7ÙÚçÝxpœ.ŸyËþé¶„Ìw.øð‰ 7ˆ¸ rnƒêZ,¦ ´æP‰‡k!*¾ lßÏÄ-ªgáœn4z<äÿÅ¡çQGé0-¦9,“G$ˆ`ùŒ^cçFés •k¤Ü LŸ`BÖyp­ kÕ«ÙÝÉé0Åô|LDÝ'›1èMZN» “Ç$‚ºï{LþlʳÔ>1ÙzC.>g¬ä$ß."â¼gÜhœTܳö´@Lg¹³W‹ßYÃCƒYŒú úY/¦ýR¤“í¯.“¨´“°/ö,öeÞL¤Kô#Mž'Å•Ú(fwø,)Ê»%㺂îŸ “ì±Üy&?¸x˜`‡@œÿ|4 Y§ÏsÝE6ÀÿöÅ¢®l+;>^g…f0Ϭ^8悟ç!vÓw2Ÿ}²Lä€/ úH¹'èáªþ¦Zº¾t—cÞýAÿ´jS|Èœ•‡ÓïÊQçŠ.S†±U_ÁÄB÷vjѯ£eéÊîS¨;zgDŒ‡©NâúζØ}¤·Ë£æ3ÃgÑàŠ5\)ÝB:SC!bÃFG[‰û$\Ò´ü/C±{ýùhxàõ„yhÁ­âÓÁΫ 2aqK \rv&ƒCv1¥/‡HâÑy@§ÂR½‡`õG^š›r1™/¨yt–\=¯í ³üÂãóaKÏH*ÞÑ‚^–´PPŽÍ¿kñ”ÌxþëÖh˜wQ·ßI$·åç‚õ©9lÁì?J€ÂZT~VÁd: 7ó%¹GÐçª)*6T£âS•ÿÖÇf’pÆxéùŠ:Ó\±¾:Šj¡âŽ;FCÔÝ*8øä<›sŒ _PBüÖÈ øó¹{x¹˜HŠ…¢Ü%J5í·CX4ceˆÒe“‘§­¢>–ÏКu2î%‰’kó=æÉг½m»hÃc}z­SŽ~•y õ]òT¿Å»ö«R5•×°Ú5ˆ¶ÝÓÅ çèâ¥ÀùuN6lÂeKo z‚Ö l¤{?n¦r -X·®—Š„óàžÓ°yB?9®|ÿ¿ýÐ_éšFYѲxkîqT„6Ü1¥ÃH›í.K«f{Ò%ê4tÛ0ºª!œô^0‡­ÎÝääŽk¶¢á»#ý Z ¹YÉqö£'“ù&yó u7ááèôûÛÝñs·¯,½¯vgÓž9‹1ÈPëæ‡P±K3htÊÕúOÀ&$Ç¢¯CšPfÔ1þ¨ë<˜müAþ&Ç©?_cåÚª²1øbe¼X©Îk#˜Ê(!Y‘6”Oœ7 '«sm™µÐµËV†AÐR/œ°º¦µ„©[Û±hïrT¨½­w>»WiJÀëâ|$eEp¦b<\¨^Yn àµ6/þ« f«pæ¿úÒ¿2„Î:xç®Ôá_¤õ1ur:F=Õãüb8]¡Ê7שЫ Ù 'ˆµé«Ð¾6 J®ÍÀ_Böú'ý‹Å¼pèzI÷ý}Ò°ÛAŠKºh1‘„× “óݼ,`Ñ©hôV˜…Ê®CùÒ®gÂÂ2YLôR' S{Qð`ý~v–`õ_yê²o5Ô~%ØÁËX‘ú?Í>½GzÇÖ¡ ÿ.?ªºŒê‹“ñÏßQ°9¯Œ}c©Ô ½trÃR]rEΰGûÕ$ìähîco+¸§Î­o†µy \%H ÜÃ/$ËÑ« Eu9NÏ}9Ÿ?Þ|šÞœ™Ä|~ICëÎÙÏVø¶1œk?°ÂÕï>¢û$¸ÊºÙ“r‹» \ü§oI+§_î,\#€¿sm@æ>Ðÿ°2g?7µ ½ÕxЕ±ô÷¹?ø8V‹Âÿdþy]ê—¿›ï:ÄÉÕÃ4Ù¤49šx„Åûáño9ô‚H'Þ:{=:L鉭åÿpoW ã–X‡ò©S`Y£·[ú í‹sÏk+x·™ ¿9Ûs†.¤95ha»—'lžH=õ©r¦)ü”uCsÕ_Ìÿ£-lÌ`pÌ ¼‘n²wßðWÉó¸ó¦-ÔðÛ¢KÃxX%ãz=ûÀ;¤â§·À“•Óù¬•ÇØiý¤ü$å¼…¿žÞÃþa~97‡«%òÏg}é®7÷ôOö¨.Áµ5#aÌ¡VÜß[žÊ6Äj~;+¢M-ÏÝÂËv©ð§m¯Ü/C/N¿CÛ àÎbkº¨R‹wëˆØ±‘•¬eÝ#ðßYͤÂ_‚ÔI:}ž5ªfçCk²=ÿ:ä Ü–S¡› W κºð6›–ž¥DÇ<„ã2µÕ=Š{-jg6M㩽]gák¸ ^ªAÑçóð×ßm”ƒ»† Å›?Â͆Ã`aö´^õ¢æ“dÜV­Áp=¡3:þ ’·'T‹Â³žâî%ÊÜsR9 ȸ ;T“ z`|¾ï¯ÖðÿKsá¾R6ÙãO^¾Î úú(òë+ʯT¾º‡o$ø§ò~ ÔFÍ]ƒ '‹ÑW£+GÞdç—ŠáxÛ@Xm&‹×* w¾ƒ±åö0î~2îšÓO·‘fÇwPuã9.9 Ò^/Ù‡÷ýøiÁg\e[#¹ÿ¯0åæS´ß`CyœÆ¶±¹xrç(ž>ä)F Ê'÷þâ(åôP÷(¾y¯Jî'¯¢1{qøÅÎþKˆ-ÐvÍÅ>¿H|ÃñRvˆ¶aþ |X”å…ë\JáÌ—x6RV‹l{Ë Ü“'Â;“±ý'¹“w­^‚ïæ½ÇÄ–óm!Ë%H)–¡ÛF[Pï§Éèì­‚{ž’[kÆ¡Ö4U\=/ÿ9È—• ã²¥1¸‰-ƒ‡ h;÷ r×£É.ª5ãn3^ƒÄ†Hœ©Ô ¥a'A`û•¼Yvç̤µ£`×ù ˜Û'lúcM&¤u¢ÄBzñH¬éÁwnLàoÕõ¨ç¤.vvœ˜p» 7û äC/LHÛ<€¿©x':+äÂN¥kè=ì,NUÒ¥ÿT!lœˆÃÂÃAÁâ =z| ¿~Ä^Éò†.qv†ð‹ƒEù}£Á¸üA6jd@νØâ¼ ý÷5×ìZ»™mš¹즹 #µR¸é !ùËP?Ób×akórüöÃÍëbL½Û _ã_•‚çÉ3à|îÈ?ÎL„üÖBòY½Zpüé)¦£üš¤F¿ƒ»lÚ¦ÊÐÃd$OJñŒÒ` €²:wÍAðÙ¤oW|b]Žj¼¹P•¶F”ˆÑO™çù!ÄnÂ+<ÐO˜‹RÍ/QøkDâð1äÝ_c^ÔG†¾èbC=%oëÁ鯪\æâr°êZ€×á¡=˜~ÏÞ6Bµý¬³Må–´aUvžÚšŒ Ö'Ú=y"­ éà ר†mõø£½Ž U|û¨þN²¡ãtà|Mü.²‚åÎò(¥3‘ºŒNÁ¹qÁìX–8Õº—Ç?SøD²-á þÐánÕŸaâÖAüâW˜ã»m3!ÜÞ[~ŒÄ„/5pWy ê÷¾}›¡Öv/~îJCòÓQòÑÔÛõÎÊÍÇwvþ^’ËlΡ¿dì€þr$c“`noÿbÆ—‡–€ùŽùüêK[úclËêveÃkŠ!ùRi”²å+‡ƒrO4Ó³Vá=ßRØ£É^ ½Ýò­©n–1wÙ@^ùÍ„gZ>ä½"L·Ü ¿GgƒµÚ"ÔØ¸þŒý@º#ƒâðÑ^ú=@wïdžÀíðtP.JËÇóì¹qBÓR;^êkNwLo%wëàIóÑ\nK'<¸;ús³cálþ¬(öàüøbTÈüå™®˜8-œýEÅe¹œüXž³´¹¦÷Û’š“ª¹Ðþì6k›QB$3è©ã©ü7òi&˜I®€’»à+§ºà4»lFaÏ­¤f©.ïR™á„£âÂà ‰x_âž.ÅOéšñe¯‡€oÅu¸0s,Ä©ÙR÷Ï_ñjR zñEM¬¬÷4Θý ¦tvA创0éb  CÕJ3Áò`Ì3.Ä; iã$%þñ”uÖ°³0i[.k}5²ñ‹ëÇ@áÇ:v#å4lÜ’ ;ïÕã#\¤Qަ¡Ï ·Á‹d¦†—¿&ÀJë²þå jùНeã×§ì–Ï= Þsî£_kyx¦]TQç·nðýVÓèõ²Ý :ù6Þ}s §hL¤oú,ßçÏÿôt`öPu\¡°;2gó7-9‚«À~ß`^4±JóM¯‡‰JĶ'ö8Ã)¶åpÎ:|Þ«EÓÂÄùŸ Ÿ91„¶wá€bnû @ןæM eX+z ÊözAŧ$»p&ŸjºßnNGÿ?_pUZ+Æi‚p”>5z>%çx`âÜÁ|gä/tñSæöt°†£5¦Óéð„©ßPÁ<=_Zºl ßÕ…fç瓉o¿ ã™S͉êNÔÙÿOwË8Ð_yÚê8|×3Ç&IZ73îèúù? ³ êê¿ÉÁ|0à‡À'ÅEÂïS¥ù›m)øNð€¼óVÄ9YÓé#uzZ³F¦½€›Æjÿã7Ø2…ŸÐÿ§“}•R„ÕI{ÀY>¹=†/ s«ø+%öúl%žMkÄô£ øKyEèøÈù<}ÁßXŽz‘\´†¸¨É˜6•åÃ:„†íðÙò6ÖÅYÒ‘ëF“¬ )Þ$yvÕÀ–Ýïðöº~¡và^,QˆGiM¦½à,ÑÜeA×­„aõ?’¤á Á´ï¡¯\P…ÙÉ_þü·>Œè‰a£ÚÆ à—š.‡§“‘§q÷ŠO0¹“Ðý›Vѽ‚¿Ð6I—/_ïÈ Äýùß]uh¨ÖÍ Ý\0l”7 ?Ig(ò¥²Q‚®çƒÐÄy&ž_‹¦AØç/Øùb2VjyHÌ4±â‚8‹kɤŠü:®{M}ó…B·ŠknciÅÑ0­O‹¯iG‹p…³#ÐîôdºÎâN”?ÛðÇ aÏÛøÜÏ’?èÏf!OBøŸçìÔ»¬¨˜C399Ã’Z„pR.“ç´×Iv mÙ$N' /‡9³2éÒà[çu«pú3kîÓû¿û«^€fy¨†hò.ÿÖæ6/þ ÚßWœó¾h–>‰ýª]ór©øF~ÕÀ…o·Ž×ô— „qNp,Tž®Q|…’Â@+h+ùØnEãÿÅàí»wáÆÏa¸£ª ÊÂèGëS ªcF?¤ºð† »Á=G@k)ÑÓn®¸îJ‡ êØ.i¨Æ‡T„¸³øV¾>²op1é,I ¾~0œ?¡'Gòü7`¬´V%!yb;Ù'ò¿ÌÝ Ï²"©Ê·tü£Kƒ£Õió'wp<ŠåÆð”zMn-³ïªÌä# à/™¿Vü¡`àÉâþPá€÷Â&Ó%÷š¡ÑÙÝO™QÙð<8?q<÷œ2“NC|ÊþáéÓêÓ÷¢öMJS[ôié­~X²¾¸æºÞqÔ:·¿å%p·­1¸äFû›²Žž_!è;  2.N8ƒïä½®1«« çÏr}É#’4榩 O;¾B_»Éf—hfÜ=÷,”£íã©ÓXÌÔ{Š ¦÷±D‰©|³f‘ê·$E[(Í»‚{Çl„ó1KÈw‡¬øžÙºâ3‹­•‚ôMÞüÐsHo~ˆ’§“† Žþÿ¼j8޽Ûf+þè/»~$ãLµ¸Î×etížD’8õu>k{÷À­-ËPjþ˜q¿œöÇÅú·ñ ßA³ßoAQ™ãäÒ3+ïD÷›@?Üþ޽N ø­'r:JˆÛ'ÌꂞºV”ß“ ‘åaadüݾ—ÆüCÝË­D5I‘ËháâÂÛÊ„=[—€EB L5…õ=WÀU~2úŽ– ò[Â¥5xíE4$ž)ƒä=§Ám9Ÿoôƒx–ámì*h®ðÇ­e÷À1t(Ðã1.تt:õì È7å—ŒÓì´ý#÷7ŸåÖÎá—/ŸâRªCx¬Å8èÂÇx®ÅÊò2y4›ÛÊÆñTùMô¥Å \9 U§$Ò1†qôî6´y\ÈMD'Ñé¯+¹{ѾÞÍ;Η½5­iM|ÎöNœ0“ÇDÞÇ’—’4 (•gŸŸÓÅâ×û1à¿«²Ò?A˜ïÿï÷¼M9ÿê1%Ú7/ãÍj ðöÞ0ï;Ž™_DZ}³ -1íDͩչ“ øÞ‡—íûNvH¢¦æñ4ã¡•ÙGÃ$ÏbžÃ ÈŸÆ×­×à…ûàî¶tö›¤Ñ32Zö·¸2{wÀM“§˜°ì:»ñ_m»”5ÖwÝeÜjî26{ÈFZ|¬ëU¾pÈzqJ >Ÿu–.ý ŽÚGRë;¹×ä­4§-G(qËšJ™ÆÐ¬Ç—PM°šv=IçßË.ï[i“ÃÿŽ÷^g qð*¤ó”h{KlL5ã?ÓD÷v>dö|¾¼¿{v‹Òý†ìÏ}SþiöxºéóDêhU…S\\è ¥0íLû¬%ʳ§ãÆ q<Ð<ˆªú} 91éC©p~%Õ˜&>öæ}ÃVCÛÐ|rÃb>úoûêJÿë¯þÿøçîC†ªÝ8S<OfÎÑ—Ñnü=’—ÐëˆòÖTáè]2fÛyÜ\ŸKLÖªÑÒ$yjî1™”Èû¼| V†aÆh3jþÐ|Þ~«)Ýõ/òƳrKÙvI:ÜÍXàpÙ‹Êݘ»³£3·.¢uGä©ÏìaÐqQH^ø>%eÑõþ(&½ÜÓ&ÀWóøöäiìÙ‚@¦$¼Œ©ÇÚÁðÞª2ÎŒÑLXöòï’ºîH¯ }}¿*s ÊuñosÙû•ªì´ÁâV>øöáœã#Ñôf4¶]…/}nôÿx˜„‹ë¶ ®O›Aå›iùn9°¬C›ß…§6m€õOMèGãq¯o™‚•¸¸ñºn-ÃäU¯·—™¾ŸÂonOÀƒ]Ó©bGÿsrg‚ ê7>ùá•ëªpÉú)6‚Ç=›e18{M.„\ú‚ž‡'ÒqŸbÈ¥AÍd²ÇV(;{œÍ;©'¨r4ãëˆÄà0øÄ&¡öS0~:—=LÄUÝIÚð;e¶Ô™ðe]ïíö¬·â.iôèøKhUáÇ.dŸ„ýÕ¥ ÑKÈÞ¿¨ÁGP›²Ýtñe1Ú±ü‹œ6eÀþÏü`å™Ã1×ù7,éLÃÚ¶óhsÿLÐÀ¸óî$Ç:—œø(Í…i`yBŸþ¬Œ€m%Ï`‹¥5´”&Ãh‡¶;.î„™úîG ¼ü:F9L§Ž, ÿüO_9 ö²jš g•‡âÌ.>Îýœ®U«©oˆP€útÊOL¤y=hý"¤$ÇC¢®:UÚÆnË«ð–%šÔ2+Gxc} :Eo†Uä¾zBØ£(ÄÛ ÞžL…ÏÂóYSqˆé4Ë¡ÃW“¥»²qJ×ýÇ9§ÿ@ø¨Jò_¯óÈm…°2.×Küa-_W’áÉ¹Çø©pî.‹Y”HgßÕÁ”Òf¼æJ}TêaÖ¤WÌïÊ &ßÇߦÜAÛµ³ùÆ1{ÉÄm.tÂÙãÜ\/ ¦ ûÄŒÍ øBgyþ8ø1dn4£¹«¨ÉZO~¦ön½hHÍoî¿‹q¾ß>#‹¬J6Ç¥ÓØ¤¿e‚¿»îÆÈ:¬Ô™O, „›çž þ£1õ­­8¾íÕEß-£xŽ—,]¨fŠ­?[Ñ)ªœ{e¸b_-L1‡ÛE§Ð¡~3ØcŸÃ\ìÆPî]æÿsiœÍz0j†(n<9„×ý«ç çf’)hÔ£€rm+øÆs'H·Æ\8^ù¾íHE™zx"=…‡ÖÍÄ«û^a«z 2õ|tý³æYn´]â誣Η…ANó=ÔÜÕ0ÛS•ž(™Å5*ÚàO´Ž ÂSš&Ö 9ˆá‰NxÿdôBâq^¹æ#›ÇÜø‰a•¨ýä¾í‹ áq4.Y¥u˜^E!P=n_m6žŽI=ÉÊÎðè%îxàëX~ÿñ˜ù%.[\BS=6øPh–‘â¿m¿°Èj|éÔä³Å¦ù¨ ÕAäUC<6s"nËuÆÃ5ÂW³· ^Y †Á†ìnÇrbþcô—nÃÎæ&øvå4ÜÀø”¡D= ¿ ¢“ l¾Ì-'ˆó©I°ÈãVx!\¾$”¾›ó’³0ÚoÎ@ü;ú„ƒ‡m‚øfcbBáìW›¡}: Z—t1+ÓxvÔ`Ü}[&ày0[¢È;þ6bŒ‰¬½Œ›­Ä—‹®3­Ü¹üèãJÝîŽÆë/`QA7¬ŸlEeû½pƒs0)¾F‰Œf#ÉŸ¤Js—ËòþÓf0óÇW8‰5ï±jûK˜ù{3Ï;˜^ïí…ÃÌÁ}ßN>É™s9eF‚õ¶ã·ætxzÞœ¯(Å\Óz˜Y&ThóBÅ^5^8 ¢›>àâ‚Zœc”$Vƒ{«óǹH´È™±§™Éôi¬Ã*IXSpp ÿÌ„:i%´º¡É•â[³föe‡¿¿×øÕ‘Øðö6Ñ?‹¿¥÷¨…Â={&‚–B0‘üôË6/b)Wß³øgÞ ¦¯9¿žÚ‰Jb§Í“ð‡½LF ZšËίáA¦†p'u}•lA:7Ò+gƒ@1ý±pvy5JÛÄàØ9±ðü¡î¾4í‡Iàz£;ø.S¼£ÆÓ‚b7xÛ*MŒãÕù»µÅpç¢õ?F+…ƒÖâEp½Vî?°Ç¼m4²¯ƒ÷±ü=ã˜á0 ª?M‘??¡xËgâï ÷έ††¶®û‡VØÈæ ˜¶=„—mÈÀì Ó¡Ñ`8ìê+Ç9[?¡}^›zñXî†ã»íÉÏ ýpíí,ZîY€³UT¨‡¾U5?Ljƒ0´Xì~¬ìÁ¯7 ᕲ%½¾OŽÝÉ#Ç[Ð+ÃMØÄ¶Ÿì×›7ø"(B0íý ö§Ð€^‹¸ÈÞ“œšÜChž¶v÷Û³/9 ô°[¦‹GйṰ_ì(«·“ªÇðÇ?FëLñxµ,}€ìÃ= p …I5ð7ï/Û,9šH¶¥>w­áÛ’ù5'¤¡ÒÛJˆ÷Ú+PùÅZë¾ðR¼¸^÷(ÞóåïV—£J%†í]†ï=´©®7Þ÷/`7£­È =pÉü.8ïL庾¡êvIrqX|>NF4™ñ‘UêÜt²§>“K½ÙÞº]ÂŽý9pÁ»ŸÖóïGà:Ë{ìüª» µìÌ“ãß2ÿþBöüïõÁÖrMöýw|ðî`wŠöc¦ó<\:4¹¦Ì݇Ü>3˜¦„5k§o‚­ü`š×9œ¿ž™ þý£„.uµ8îÌ"qâüéÜs.ò4Ê\&m{b‘øUÃÑTmHŠíù¯·šŠþ5€ß㨀ٕvp*È(ãCå ¼³b‹tyM\Þ@SG*˜‰w‘¿Î+áêDh¹ B+žÐɾ‡ÇcÐÉT܃Ӿ¾cÖ‡1`Ž:Ë]²¹ë |÷mÔ˜IÎ}ÚÒµ;xgPV ãýgìî¦6‘òhÛDj^"úy¿Ä/Þ³pp·ïÙ¨!´X“DòA‹»?ߎ"ftÉ:Q.}v$˜/0{nCŽW²J‘·$ׯ—lŽEÇ£áðÔ= §œÔã;ΊÁÁÙö¸ˆ…™éÁØsáYõz¬ðdÙbLÿ¤:ÀÿM"âÃÅ`ÙÈîš7‰ú8Ù* E¯äžcCYæ¯Y$~Ç.Ü/yŸ«£¾ê1¨Ï§&/õù¿ï!Ã×—½¼Û›LÐèÆ"lï_ˆEeGÈÏû\˜lÐ7 £Ù!™ƒäþ8’f µÎð,ý&TX%buüdÞee‡N·v`WÌ,¨mC‹‚R eÌ+Ñ”z/ˆ!Ÿk_Ô”?(ùAoYº£~܈¦7ãÓ]º­’ßöÇžè§ýîD܆àvsˆ6ž(¶± #òð£§d®ÕÅ9­I•Îhò r%Þ’¾,~ÿvqrÕHßxG‘™Ì®¸ ›e/³™ct±q\> Úû1ç`NUöq7Pª3’Þ'†«þ|½F~VÆïów! Ø·ƒ$‰‘#µèÛ•HÞð6ÞcD= ñb©æwIZïHçÝ¿‚Ÿ2¿áðÓì©© ¾Z„¯Ê¡vò=»L-y¾ëôŠû?°¯¬--gt£ŒŽ¿Õù7û’­îŸØ‘Žl4r"”ùæ aó$©qœ4m®O#«O‘ÁÚ=QÄ[r÷7ýJÊv–Cìø:Èq†ÝÜaLgµÞa­N³aúóSpôŸŽ+ûØ J— ÚÞéÓ_c,ðâ/5ªw.„ŽÜ•´S™¬ '[’‰Ûõ±¼°ýDdõâÕ!QÂ~9i¼õI‘Ú¸ÔãösPš«ÀFwØBÞ\Kª®"tÂ,îVâD®Ž‚À/)·/ÿôø] zyÝ zÀU ç{‘Ûï‹í6<úo.^úÀõÿIƒÇ2ŒexòÊÔ¿£Â‹ž¯ÇÔB¨RÒÇp¡ÿ ²qaÂ—ãø¥4ïé%ƒötèˆX ’Î1Ñ~ ô¼œˆú§+`e‰Öõ÷ÍYà´†OŸcýGÒ±"—ðÀ¯o‚†±;ùÃk‹0ñKÏYðaËü`³ˆ8?ªD6N5¢.„1îÕdÞ:-|½%çŠD³éSˆŠ°Š<žø_-–¢ÃŸaÿèÂÂ5wIñJ[^rÚØnù•±ø®V"×ád}n}áºå¸òçzÕAdaSYûò´™lÿ—“Ïàò<­ë? Ù°i¨ÜÄz™5k§rüðugA’ÉD’{¾žø Ç Ÿ_àçWöà±Äž•oÓÔÁìSþ+õÑ5ï m´`5"Av¨Ïô±Õ7gȺÎ!T°3;Æz × ){¼–œ/Ú‰sD´ð~Ò]ø»_v?º A¡!|¨Á)B“ªÁ­¹–-x¨ ©{“0åU)±Ya9‰˜˜¥É¶ÚÛã†oÃ…W…¡´ßª=J›ÎÆ ¢{ûÜ^g9¢¹Ø{ùÁ¿ZG•66Ç È·Tôø.Íç ÊØTp¢Ix,ø£ðBß »”m¾ù£]ùl5Н_Åñùøxâ¨nžQŠÛpÍ‹,ÝŠ'Üè~¯Éô¬êX0Ÿ?íß…ÂW9ìÿ—‹NîL4r„û¥ ú"Žôùv°­gJIæ¦Á~ ºcþ\{¡ïyÏ$ž:á_¤ýן“… vZî¿Ñê2ô¥ëtt ªÃÀ ‡d쇫Ì4)¨¦ }(ÿ÷ õëà«§5ÌWU¥½“•ñêû4ø*9‹1Œcî½!Nò_NYø ²;gá˜îbÒxÌ”«%aõ–߸ëzó¿´šïÚá‡Q•n|©c ¹ê~—¥yMˆÿwÏSàæ]ü ðâÔ.8·¾ΗÂg%{~ê­÷­(giOcQºfÝG2™ŠïîCDÇaЙ2Ì9·:…ÑüCEˆ @%ëÊžŸå®h¥È"6Äãå݃ùz]Ž©ßŽcuÔslÔYÍ•Äî€cèôû×HO$t³a¸ÏsHùåÆC²¼q´ŽMÒBhóÒ'§ôPçíLLû)Fì]¶Òѹ(´¤ÿÖ§³ÎÈSÓa°¶ë4z®—¢%a¶àÙóƒ¹?›ˆg¸ífñkxæüC¬lí©#vÏêŸc¾D=9âÔ< Ÿžƒ2,d²ýÓv×Þ³Aœ„Ã_Ÿ#}£Ë!4ãäü>‡3Š\™•­´M ‚ðÜ7¸ÊwµpPè4"¼ŒFšÖ¸ï_ÍôÅ:]’8¥‚ìšp°GË")jþ&Tøäô7,ù4 æý˜IzO³áÏ.‚D{7ØLÔb1[aSu< L ø0ë‡Ë„_WN†Þá«pÚñ÷BÇè@èzÞÍlJÑ@ƒÖy~†zOƒ– ™x@;ãÖoÅ×ÃjØöaç…7ÿF²+ËJ1èˆ W^sý&Áï"ü_巌Ǒ |am~¥ÝÛæàùEŠŠà9ÎvìŽ޸sпœGÆ“UGoBÝo`ŽË­PøI § ÛŽEâ+^i_Fjï$Š 09ýñ¶Éåy‚€”b/qÏÎi5·dzpì#J{Wð8ÿ¯¨>ò4õOÝ 5'OÁú•÷!M6‰h¾¦©âÉ4o–ÎûHø dbÀÂ"kØÛj š3Xw•-ZÏ™L5ÂÊ‘í3ç«ú¡êŽ%<}gSZé/C[ÚÔXX¼é ö¬ÒÂKk7‚ÙVoü¶N^å×±1nÁ— X8úÝ µÛŒi@ËH~dõ‘ø—·RûÞbž¶(žéÅîìL¼YÒ ºíxâÒœ fÅvÎçkÖŒÁ«ãýÙåok’¾m庅`¾›'u^!ä6_¿¯d0ÑKjtëµ@n—Ÿ‹Ù÷¾±ü r†ð]bdãº1tšÛ1¼é? ·YÈØ2I~pE6èKåƒÁø£Æœ›M¸„º+È+(K'p<ø"®úÓŽ#Öò%¿•x€–ª¨ä¡[òdúKM fKE£á®)èfðN¦˜ì 6 âò5«~¥c^o\ ¿Ë$ÚÝÙ¸·¼ÇöThèߊ‘Þs›i27ˆòÍ“Vb‹MN=ïï’¶ëO‰¹Èw Ïë1 Íï(;»AÏŸ’ÅŹc¡Ùu ï3ž“W–øâ=é3øÈN>Ú±›Õ +aF‹._íW?Ö0Q;èøÔGvO7¢.WÍiДxèÈëEÓ÷BÛu÷šI†#ˆû¿xL\pƱü©êDäÒ"ÑðA<ÌÇ^qÉ9Ö‰Ñ9c´ñn$ê!8fña²tõ<4¢ ¾Ž¸ÅN?ÀTÞ‘PKÆ$³þ´ÑäÂ>uôî6!çî ýk$¨…Bêþõþ1ÌÿÁ]r$¨ÕfM {wµã•n5ºm[[°Ïš^ û¤b`ëäé[+;~öõ-ØüƉo{XÉ‹†Õl|ò–åYHs½ ]7¯¤xÁðÕÙ«W»þ~{¯™‰¾ßÍxÁ»«àÛ0‚n(ù£¬çQø¹Å„?ŽÑݹ8«¶v¼ë?~?uÄÀ¢Ñ#èÞÑëy¢¿Ôn§Î'N‘#E!,Dv ûªjFgLĤ|,; /lO5ùñüµ‰¼Ùs _J ã–"r|„T)üɘŽ/­ÄÎA–ÂÁÓA´Íûßü3ËÅAä¿^ç”g¹üÞö+‚cP ŒR=HÑÇfizÞ¯J¬Y~—ªä ò~mæ[;Ÿ²Ûƾ‡2·EüÚð—_,A÷W{ñˆIóh­6¯ðþFLMN¢wø6Ü>3‹Ñ^`•‚ÇïÿBÿí©Iš!Þ‘§ ¸JJ31Ñù!{‚_/kðÁî àcW7 å®-šT—ÎÎ<™Ô®ŽBÇׯüöê4øÓ¤ƒNw’ÎÉžð¶C"ç~F™WàÀÁ£(91 e|û\y •ŸeH¤}ó OÖ¥¿šÂ„µ«ôÏŽOóÑ([Y7dŒ+R§í_yu“-ÑžnEÞÌ>ïŽ]R 炬Ño˜6µ·²å!oL¡û•;öYýŒ8øšý5Ô¦qi¢ V(ǧþ âE?C\’*ç˜O`.|pÿ ÇǪòÀäÁHIÜ9WN£on̼}NfÉrƒilj“©:îþjê&£®Š‰ °Iœ®kê…QÛ9hOÓ¥êkò½Ý‰Ô×øw8÷ú5 JŒÄWÇ îÊ!0Ž×§¡ôƒL1›QgɇøÈ¦ÌQçコ¡x™ Žjâï© üÒƒ¯öo=\‰!ïOÁ¹¨D24>¯oCq2†_ù§¥*•è½îh\g„nÇc `r?ÖžIªoïpùÚß * ú+cá×á£8+è´&_%«‡ñá ú¤p*º§<ÄÇ…ø tnµÄÄ †‚µ¶»& ÖåºøúO)¾¹r—(¿V¥ƒÔaš…'_;ó.¾<'KOVŽ#ó‹Ù­ÕwÐ5ÈTeÁ&·üúXM¶Ú'åÔö‚ýó'¬Úuï'‹>ŽÜ| }é  ŸMCôÑã ×)S§ÇqµV ½hµúöëÓÏä°ùªû@ü«ã5ò¬-–Yú …ö5j4Vª%6çh›óûàñ½y´tö^~ãô2nUŒÿ¾á’ ‡Sê^D¼E?¯ùõÁ£. *3ÏÂÂ1ãÝûe†æâ|Ià z¼E™Ü#“BdøJ©iô±èR©,Êû—ĮӸ–$%je/ÂÃGh±âkÃùç X©7ßd\Æ¥Õòÿm°cøÞ¬•ˆ÷u6S‰=>¤wSšWáײÖ'3?žlâóçøà×Tü\ßf£KGâ$œµOƒNUšÂÕFý†M[¥é¡wštSDÈþÅ…¸E³?œ¾ÃôxódáµZ8ý9–Ößé#ÇÞë²¢IÏp‹a„¶ÇãÉt[G4˜¤ˆñs›ÙDï4ÒªËoøÌ§ñoÝПÅÂQŒóa® pRÁ³ßKpû°±¼ûc:·\ŒÃZAÿ‚Õªª„Y+ÕXÌ«"X¡aBíG%Ðaop†îo+<Ä…ŸzÉŸ¨CBþµÐÖ=ŽK|\­“”øÛ”·Œ(6³þÏs06ãz¸Ê û\C\˜.Àeb4ÆìMŽ–Ï9Ä ŽÇs‰ã˜ÄÁêKI‘èÿæßdê 'ÑZQ°äÔ+p ;G6yþÓUËØ]%yîËn`Êôü¶l2ÿøvîZŠ6ƒÖ‚ô?mnݥ̪;¯¦ÞùÁlõìAÔe¿#¹?„Žèô†øá‡ ].!ËMVñŠŒþ½Àvòdð4€'Žü@š3ðk s&…¢S›(î1œô}Ï“v豉ç;ðîÐÈŒÄQ_#!dP ›x-œã¾CáUg°ÉPe÷&Þ†37pÕzsxq7žŠÿ )E÷Q»4j‹ªÑ1™°62€×¼9w&ãÔð“¼VÑŠ]>ë4 ÿµåÍa˃pñ‡O:0Dh ݉¤RËÄä³q«d©–[ ðf;~_Ô”æ¸Váýï 4¸ú3\«%¡‡Û‰Æb3VYäCîe_ÂiÖtNaŸîÅΓKÈ=¥C`Qø gϼ…–ZOpg<ºèÉ_mMèÿæGÖúÍ€coƒ÷àÏXYºFÞÞˆìb=òR0» …²/>Œu›Ó±èÛ_l’ÛC4>¯!–Úÿb7—ˆÎGË®Me;í¹ÚÇìúè{† ǨÖé´eu8#_~&n¹‹Ç4Cñîö*âeö¿ù÷¡V0.9nUMà§uà¯Õø¥ûL–½€‡i—„)½ÔôŸ_ÝÁ÷©™P‘Éð,‚ŌMá…Иk;ŠaˆŸy“Bš–‚áîw‚£2®P¾[¥æH“Ÿ/±Žö«?@«KbàW}˜‡ñkÄQaꥩ 0ê øÕöãŸ{q¬.cüxÍJ·Kò w‚IÙ– øtÚžßL„—k>bêÆßäÏŒRY!¼y1?¤w68ÀüÊ(ÖÖ\Ì7dBߥZ8;öY~JÿÜ‚ =ÖüÜ¿<~`¶ ÿ nÀ³5køßÂæ{ žC¢éQ¹· \an¶Pjd%+¿s=H²íh Ë°D<‰móz;fªóÝÎqø´}!‘Ý~ˆôeLá93¤ùÈ®,ôý„©OrѾMšfÿ‰aFS¥¹çý÷pàß1Ùå%Da£<8,º%r ê²{‰ZzÓÿõ+·Ù'Ew)j³Ü:Ês,içÊ7ø|J–¸ A¬åî]sŸÏ Ä1—`…­¿Ùq:ÒÁ ¿«ïåÜXÔth—¼׫«ÀÔuúêÆ6¸:¿ ¿7oÇnEy.=_Ÿ–ÿ6d,«ÆÝ­ÀŸ­ó ¼Œʼš"P–ßð_¯0{åoÁ¿Í@OÊusQo×"üÚò€}}Š]6žZï}*b¶Øý×´îBÑ$lniÄýŸóÂ÷AÅne¾rÐ~LÜY Åíã¡dw žÒ@cÌ]ÁÁ‰ç¹Åíº©ö;Zd.ÂÇ3åø— ÏqW¼DÇ(ÅÍn5ì€È5\½ÁÛš:°“Áã/sX|¼q·‚ò‘«øhÍKØ~\†ÿ4±aJ‡šPï´&6Üì`šˆu]‡k_fòÒûQòþ9ÐŽâ—îD`dêQ$ÛD& øÿÈ® غ ß 5À†BÖ¯§¨vÝŠ¾ó} ÎO>✷`j©I·­£+‚•ðÏ‚{xkû Øæ±”¿ÌXNß½„¥fãøˆ·g„£e ÌåÈ=GuÑiÓá"}…{ˆÐÑýµèÖ3Žž¾6ƒ_Ž¢P]› -m>Ôºë û¥Ä¯GÄsãH=þ64öø8@kGžx´…Ž7’Æ¿Åw©ÎxwôL:¬£Íþ^ÂY}BL<´%'U¡Áçå°ë+†ÔÈQíÑB“}ÔlÜø’ùâ|ŽX úÏŽ‡‰‡ðM×&NˆÙ'þ7ÿòúW].³öåAšõäYn Ø”ˆÑÐêrrs–(#wr°ßÀ˜®R2…Î †t½Óîºg×?¦Ïe þë1#_rñ²üXïuÐÛ`çìã舉›¨jüV:HŠîtKç.aqËŠ‰H¼¹ÁM²cÄÁ÷—ûèÎO5Ä·û4ÔuÛò%Éè´Õ ¥ÇíÁI×¶ÁûË&49°?¥ ‹ïÔÛ9a3[´-™ž½‹†/ûO¬YàJ¶<ݰ,&gîYãjCO'¦“k‚Ýþ\°l&ÉÙºæ.òGWK°‘ç8”ÏÇ",aÖ!¡ö)M¸pCvôªÒü‚,5f"HÛ+ÐÍMï° 0 W”kþ~a«ýò6.‰À>çùüæä ©¼˜J2%!úÕ «_/j…ÉO¿à)ºÖ>^î Àµ›ŽAX»„p)•Á˜ºÂØÇ=xbÔUvÔ‡_·•è¹×Ç`Ö’°ðùf¨ÂU0dÊ 0Ø–º° ®| #9¹ Xo]2~!вu0m} ΩötìX ².§–Ö lÁp<—m‰ó'ƒ©O1XËçàIÙ{ ù{euæÎkÌýq=œ©û2€?onžž^Ä~1çMÒgaoúdØ·9Žœ4µ ”ùñwžÖÁn‹-2û5lÛ¬ÉÍÍ&¢÷¼RcEvv;œ±ÞÉŸ.tò¥ãÞ‰p—ÝFüHÇsè+4¦ÓGiÐw¡Æ#xã1’6Šƒ }‡Ñó…>_kʪ&ó´—àôô1B§ªn¢µKÏòv zßN‡a‘G`´ÆtºG»ï«ÓYºã¸ìÆ3'v@Óž-ÞRO6|‡±#?@Š]~ ×ñä™™üïá( YªŽ7a¢z6îú¦ûÖC`ÑGTZ ;€?Ýεí7á¼5™È·ö°¸Ôã¬ËW›O-H†àlEâ0ò„«\#±RÕtZX‚Fj.´igÉ!ìNÄ©âW°ž—¡å`¸¥º¾òPâ;ÉÄÕž<“kÒ½çÿ²VÃGøƒ"mg£È¦¯XeßOjçcÍI5Ô$¢\U& {“åÈt­FÈQ·¤Š§°í¼3_¡fFß´`쬯øcöÙ‡¯|%À:V™/.·‡óÝ“èý d_KcØ™éÔâÂ#ÕKbÆôáÊYÌFñv”¼ÆËÛáÊšU¤Nu4~=ô±Fsoº;Ô°È% þ[.Ü62…ø¼OcGÏ­:è6Š=û˜S e)ëÞÀ:öV Èšy0Ĩ^O:‡ûà7Qß,¼á* ›êŒ¨~ÌZär§PvÔ¤ƒ±Kù¬µ>\‚°CâÙLêpÜŒ[xø¸ ÷Ü|KÖˆ=t“LZcJ“®€ÅI# }£Gk[ÿ¡Òb:fò<¸Ø‡Óô¹êˆe “NzODas“«[RŽw¯ÜÀo.§þ›# ~£b¹ŒÈX¿" ÿ>Æïšëé‰Éò,ßw×NÊÅÕE4)«ìŸšª뽃HJ÷œ²4•ËÌwŽŒçŽ&𻇸æ•ÅüTÜ6Дµ%Sħ¢DÓAr›=Û3}"mØ,%&.ßÏÅ÷/ nµ}¸®aù'C[ÆsÃõt‹üPî{,•îÉ‚“6€ÞÝPFôèAÞ±#”¾zAë6õâßZÐÌ(%ÿÍnPÕ«ÉŸ=ˆ'›œ¥›&FÓžï‘€öyÜðÓl¢4•¾™M­§¡–³¹ÔqTrøzµGŒ9~eg*ÑÚ6皀˜<5â†à­º$ï°‰Ä@C P˜;–Éó™ümôihœaHDnF¿j€ÿìmÀ÷ð êÄ.z€S]\‘§'3u)»W‡Um†°éOˆPÆFœIêü°köÿà ހ_ƒ6,ÙujCñYÚîöÒÏóñÿöL ±Ù ãfuA^ùœ&› óšn¡n‹_y¿Šði‹"9jÈkÏìø?¿,ü=þ!(˜ZÂu)êÞ0÷ŸBµÊ£Å`è=ý‚÷?þWGK€hf Nß=•äN•äÿQ_ŽÕ÷½m*2D†dÌ”)J*ž½v“i.*š•J¡Y“y£D¨ %„g¯ )šÐ¤II£HƒJoŸßu½¾=×s®söÙ÷^Ó½öYgëÖ·Ô z5ÑÐó;hxË0áà ƒ¸²| Fˆšâ»[pt×-F™$¼\®ŒŽ3‡AàŽ=¸Þ¿9€ÿä‹;¹FãyÏÖ\n$:Ì4á# n@ÊØh×Ì÷jóðsà²` ¤ÏÆ+lù­¦iÄx¯Zg9þóã‡pÂ5\‘ þÃ(Ô6BêªLrêà ‹-¨ÓÚ\tÿ fA`luCæÑV¹Õ¿"³ :î¨8¦~qå+õíáþÜ]ð%”`C…2iñ—ƒÓ¹9Ø9Ç„zܲĹñ X§^¿DÑd_0=væÏªÂw8cÒ(ºý$ûÌ>¢ÿ¯ahiþ .Œ{ƒ—ö½Å¯¢·ö­ÜòXyÖxøWžçVcžèOô0Ÿ ³E=ÑÛ©lûSù†Ÿ±ðüŽ1—ß„×ïšR™W£áà½6âxJG|ƒ#OÂ8·ç8u5Þž$ÐxF\¥¿’-“+XêÏMøL.¦B#qûS+˜Ó·¥²q ú4Zõé^¾‰ËAÁ„*´Ú–ÆN¥L¦­×déðše”ìÎÃM[ðÜüKøkq=®ø›²„¶s>2箜"/~P‚XGM:èþhZ·;žt¾ˆf,ƒ:OUzîR!.È‘Á˜úåX:ãÆíP€Ìì¥òWÒL©ï<—èÜQ¦-–TqçjR­Ì .ˆÁþØP…!¼rö(¾LÒ‰}ß… Òdð« ÐÉodϧ¸ÒÖÑùè }jsÁ²¥JümëR&><Ú:Òþ«‡†ãѹp(î nûgÏæcýþÏï<3“ÃÝ?mù0·%ö¿TR±IýK^7´S'òÓÚ¡|ʘñlâ÷çÌ5ã9¨-2·: ñƒ6Ã[- *´’§¥·‘àÛ¸}mV‡Í¤·¿ÒêÒ|LÔ7f2ü¶ŒÊ‰[˜ÚÙÖõç(î|)L¿{ “þ†ÓŒ²þ·C©íú·öccPê{ {úp/5zx”ÁöÏâ×Ä”ù™ykè’ X<'™x–C–fT&JÏ7‘ LC§D¸ßúŽç(heUÛfÒG¡1,ÎÆ“ßÿ7÷EÉ],·Cæ«þÅ;éÇ¡¸ñÿ6†ÿ»ŽΘLmЇ¼¶Ñ¬=^ë¯I=6{ó9AôR¡7 ØÅ-jƒvPš©[ò±îJXn ‘ÔAº|çÀ·‘5̼g8î í„óròÜD¼÷Ëbú²!´cC0q R OzóXþj+aÏ+*2d­°lÃ^zÍf8\Y<›+\y„ì ª™(p?]DKî*ãQÓ âú&®§ô•ÿÜ©gÂÃjpûuôݤñüíˆX8¿ç+Ùµp©¥ •K Vø þ©î, h€ô]Áè¼½”¹~ÞCï¦áÓøZ»Nƒ;LÀ#Ž“èڇϾrš“°ÓëNYlô_OhXVoG:²-Ñ‹±ôíf¸#Ž£‰Óy¿É¶Å;A\};ŒÑ7¢§Åg‚¡f;œÙšÊÔÞà Wˆ–íLÚøŽ,•çÒ|Âmꬊpï^ô?Þ†ÓêÀîO1tx¥ã…ÀAôoÁl>"Äì{ïnf3ö8 ÚctðPûPúñüeØpv¾À˜þ=q¾Œá†åÒÔÍýÍ=ªÁ0D®¿^ÿÖj|ÄuØg0„6'~ ercAÍ–@±˜ÿïÿÇß9+m ƒ ãLéÑ*]Z™~Œ¾3ÍÃ33Ũþš¯s ¿ÕXprÀ ߘã4_Yªž"$c:°z§4ŸpÊ$–å‚äÒ8¸"J ÌÉÆÑdÓèllÛ…°<¡”õï`8±.‚N{ŒÏã¤è÷qÄüñkò§(LPØÉn1ÆaJ#(ß§Æ"Œ­ÎoŸ@n¥v¸ÐÔÒTvÆìn1æasÓ ¹x>Ì~ûG}ÃÖý]¨kåM*êàã”+°·Vœî z%¸áÂuLû1-O—+'à·ÇˆBË¡ÝLV”ÐcÌêêºþ“0Í€yZUã>g\½/ /-¬bZ?à‹RÔÉa¢×–ñØYéXI`t[L×ò! C¢‘êrç_Óy¯®-¼žÈØ¡Ëð̽o.›Óû@k|*L+±êXàjÇ}GVð‰W’ƒU—Àò’4ñ]Já\Ü9 ¡H»û0fs8-š™1Ö””æÂ›aµ‚Æ×ÁІ'_óSFð_*³`Dã˜xk †ÍòÁ±Ù]W1¾ÕÍfm¼ñ{« $–ƒ©é1v)V]7õøïÍ— oè%\t¯‚Œü0•ïJ;1àÿ:-ïào©8.dÛ~ç+ˆÓg ¾^#È´_˜X »5íÀ/i2º&œ\¬ÙIœjŒqCé!<ùÏÏy(«óKÅÖlîº8vâj°±Ÿùd–0ø×ãFÝáåcdþ‘O0]/ n;d±g«§pÃ)ÃqáÍ\œ8( /Ö|ƒ7êñoøu¨“Æ] ·§ßƒã»p´Lú%nÂ;ýuxÆ[ ¶,‚oǺ!Q×rcâÚ¿ÆühÇÜélES#Åiph®«4ÂÝK_ãcz;h.|&F#¿S²Zð¿çk﹓êA‹pªü#0õ9œÁåd$f/W¢k{×’˜FSN³ž£˜Ô-8¥÷¶¹¬†YºÃ¸Ø“3h1vßѾŽk„á‚4+>m¢SWhÁ/Ƶڃ,Ñ<<îƒÛU[R¶ö¬× FµaôÇ'Pz‰Grá¿k*¯ºˆûÕÙš]oQgÄ&¾Qú¹]Yó–”«çsùOIpJívÝËv¨Ñ‚ÞWøæ«?Nùt­—áíª(UŠ%Š™êöºz2tÓEBuÝ[Áì°:~ÑØF–”GBrg6:ȇ?OÿßûÏ6 5‚‰?ŠÈ¹æE°Ë\Œ îxc‘¹¦Éúb~].Û,›…KŠÄÁr® ß´jޝ€È™ãqãSxñRš5­a+vÜaçVàe@g«2áBK:-ã¦`dØ ìªÚ̺Æ>þÌáø+¡d¶>g[¬ñáÂ*¢ò©…ís>…*•ØúP$ÃNºGÂí¿½húÛ–÷9ðëLøÚsZóþn;†ÏÝÃðXDÌr,Àöi œJ†®ñ—ðÖÝHÐW–¥Ò†Ö0rÁRÈŸ>š.y²º’eIÆS¬ª7çÝw¹…îþÈ3ìÅûJä ~dü8PëOÖ_eh~HžÎÒì@×NQ>ï~0öd¨Ñ·‡dÓÆ¯lt~+y¾ý(–\ƒ'¶šÐa­páó ¸|öjèHãìùÙý'¶$ã÷"иv+[ü_æ°NðøÁGX·Ù”,:×;fCÈñ P¹ËKi(Üö¼$ü°ëfbÝO¼u Š÷McXœW†…댅û?!Û{ž$mv§våh»uÔeÝ­÷äÑlw> [˜Nt—@릘6æ”ð¶0ˆdû,…é¯aÚ<5¼‘WÂöåýoÿkÖÞdgæ,²*o*˜øòŒ=™ì³ª1‘xžU¹+Rñ'w* %xÞ]Hw^°‚É?°¤Íž3ßËý;ja÷rô äð}’âäØÖWep³r>Ýg—áNzhÙ*£µEGÇýßtÅÒðÑ/HzÙ"7a0­“¸€Áõø`ÑkÈ%¯o ¹Ó-ì”a9¾SÅÅ›öÁ¦Ó„‹—Cpº2Ø ÓãÏw¡ûÅcÐ<²¬\ð>øWb”á!è:PÈú­÷2³ N·ã»•V0m²Ö‡ÅÊîßwÇÏÐÃÎ5™û_ý#áÚÙ©5„Kʬ"——¢z½^7"-¿1÷’®Š®¹¯í ŠÝ@rè0>²ä6xŒ«Æ‰þëèbŸ…dyÔ}¢¼XvË>`¯á9»Ç=_åÛë:…V¯¶â׿ٯE“ñþÞ£¸·)÷Ï>M<…b|„äÒ11Ö~áìVŸn ]Û/1ÿ™…0QÝš¬ÈˆÇ[&MôÀ[OTñÒ‡M?’hšZ®Ê£RGËñæ“z|ù|.©n$~cÆ¡†ÓtøÇ¨ìDMZä%ËÕ¦¤Çv9ó—³té¢583ã=y[ˆÇ/A·ùäøW/?x‹÷cæÁ¦4rq¦™8l±v‚×Í…,uÊLõP —R¥5zaæéX8>¾Ä^‹Áòä6ܲðæÎÏ€³Vá:Ï$&•hBÇ·®€q®vðT1YŒüþ“ÄïØ…ËºpÝ7fm’(˜¹ªÌTx€7Ï“‡CÕ…bF¤¡Kî¿güð—Ðkº>Ï3d×tè®ó£©áµËè=ÄVô'(á5”2­áÔðÖæÿæÂß¿Íû˜BLËtôþFÚÌaMØLpŒß '{ƒõàtl‹Á&TµÁ×Ï¿øßÕµ;ˆÕR„á3áðS9˜u8<šAÏÌßV×êqÛ˜vô{Î|êÃqaf0æ¶}`ìÓ9x8ãüãH©¶2àÎàK¿2ÏhU~4·“üLNA‘6 ž8¹VÏ a¯:íƒÒ[ÙE´ÜVC¼žEƒ›Ä r4&)¯¡kóÁl2(K6ã•tg¦7%NPzä ÛF:·ÌjaHþ«•Ž.ø‰òfWá©“?øŽsÆîi—`¾G9ñÏL$Û2åp¼Ü<â·ñ3;S!$/Ûᮉ¯ÐúÊráÓ†(èrÁÄÉ3ð‡m[LD><ô¾ôâ~A›ô¿Üêƒ,ÚÍö‡é/aFä$\bD=¢îeMúÆw_õôÓ¾øŽ(hPóû6üHŠ ¶ü¡s=áÚ)M¾×í¯6(ƒÙ¾©ü¨M[°.õ0+!´ÍpTÛ\¼?:Ÿ´Œ¦–ÙP¸¿S_åÁ ¯£°ªQ•÷’·`†ò4ö'?ÒèîRc”bâ<øP,¼œqÒî>ÀÛ»“ùѯýèíú–ïÇÙâW>ýB7g˜øžCPô@ ]¶áÐ y4ÔŸÖ·v°w'ÂcÍÙ¼oì:&~bÀÿÙE7·b…¢o²‘˦ ßÇíi¸s=|<ú:‹D0nÛt4]ÐÀÖx„3CÂðlß0péA“7sy!Ï÷ànS´tÁ_Ÿ2ˆK×m¨;ó±²ò]%kÚ‰3Û£¡7º<=1êó>\(®ÉuÎ…Û;v‹ÁíÜÙ8øú1XÅÙà3d¬äUhÑZG¦—XÐ>ßᾂ|ד—°BC–6Éä Gü}Žå® 4ø Ìj~ËÆœÆ®ÖX3-‹ŒxCÒÜb0el# žti3Ì<ûÛêàmð[0$aLÁ^(Ç ÔO{éÀžVV‰møëµ9Ψr&ƒ”QŸ/”;Â&\h!òQ_È·<~®\k´ä*ÂÉ/·dœzù*Ý"ì- Æå¥q|a=¸w/äE×[Àõ³½Ô«À4{óKSÀ½ºONÿ [œ›QüÖ[¨2 ¥ áà4Œ”<ÍFN4¡™¥«hLÙ[Áãéðù•*-ý¬„1ï(§ý× ˆGð=§„xÀtÄØqÃFm\eajǬ`uòM˜2ÏÇægãÅWÎÔSk ¿ÔÊåçÖ@zqa*½ ÌÄ·­>¤¤Sׯ ø¿ÚàRлÑÄö8Î@ÁÞBpí•çß·8ò¯½WH V!äÎËÇÃñÚtðÙÈbèñGá¸S÷ù’£o<Ï`å°.6g«6ZM_…Ã]p,»þÞ° o6´‚ØÙáðôЮt̓¾N¹‹þݰ®u"Õ?mÀÛ–ÿlj—ãàê6'çŽâ~\ÿò|~eæâ|gK¬Uã/G¿ª¯Ø<û»5Ûl‰ñuöëpå¢*ÔY¹ݵeh}øT~\Æ·ù»Ó¡Q‹áR lWÅEÌVÁâÖƒÀ.ïå>óÇãaÍ[}] 5Î 'Èÿn[?À¨»pæêùèùÍW&lÀ ãèηÅ,¼ù{›rOÏɇòB ný¬Ô'~fÕ~Ù°Âæ´‡½À¿Õ{ØÜÉÑ v§“¿ç9S+ûçCÃX+ ÃËã¸sàk± gþa©ÕÌ+û'\>' «.^²wP_J+%@ø+½Åö-¡ïôÂÐäõÒ½âLÎkaIŠå°Th$̉~-XÐ$‡…Kgà¸8yL;ð’•¿2ÇÑy€Õ[ñ|ñ bŸÝMò3wÀÁGÅxhÁXpøñ›8–Öù¶Ùˆs[÷*ûŽGý#ðrÖÿÞnºbÊ !êˆïxƒ˜" ‡.šÍFS/¦^û΃øÍm¤ ÍÐ^û Øö7±gûýýû©`¡Q"tœ˜ÆEK†s‡³ºÌ¶1ŃJ4¼- Úz0ÅæªàŽ“ôm­ÆEë_¶‰ýù¨ Ó—¡÷ºâ Rõ<ßòn)Ñõ“/“Àhc¨LzA ô·A²[’…Æ4vF;޼èjÿ´u˜9%€Ùè›è±#£R‡ ’N³{#8»Ü»4ÞC%taÿÕLñ ‡Q6tЯ»ìQüQ˜(wƒ2vt‰àwú2šÏj?„¦´Ã´»-D–ÙÑý;°6ï*1¸mŽNËÆÑÖOßÑÓl4.|‹=ü‘§b*É´þ«•c—E1fÊú4äxqr#lžàzgÃ3W~Ëÿ"ÒÔ=8uª }TêÌã~/†=¿/ ¯¿d]¥á´ýOŒ³Ç'ïo%‹c çò/8üv#Hÿ•á‘‹n®ÌûV¯€ÓwŽà™„D|o1¼»:”w»ßnö*b>¼éÏäxR¿7Œ»`&Î×LÀò­§øKç ´ÞãB&Ô4àÉ»¶89¿œéˆIüë)Y8Ø†Ç Ùì}5þ¿púz2ò–büXâž=+”áÖU vP”/¿ðXø2} ÷ÜQÎnìpÃN6t¥øtGul&\û*‹È:¡Ïð>øåLêö§°ï‹ànºÊÑ|ù4½^¼±îî ‡¯òünÜ7¶õûhÅî|‡ºJåÆpp~øò‘LýDycñÛAUñZð¹§Å-º_²èë lÖèÑÜgº9_éåbW·dãk€ë)ñÿzWïq‰³”¥Ü÷àN:£W—ôyâu¦'ð-žÌ+×¢×vb€&¡:²„œ¿ykÀÿ=¥ ryWï*ScaŽÖŸÆN46àž÷gàá [¾õT?ŒÿZG nIag^4èÔŸɇ¿ÀÿP6ñÍM‚ÓrÇ1Àå8Þ•çɉƒMدå€×²ãà¾ÊO°{ŽàèžÊ|<&ÓÓ%¨¾E8ä½7æ+¯ÃVíæ­ÂÏÏÌ9U— ÿÕ5ß|zš•2lbAIÇà¿ÞÓ¹sbØ rˆí_4 öšÃKOÞ`=epÐ- Ÿ>ºDÜÿqÚQ'øÍôm¸ð«¸^…þ|@×|Éó—ÄÐÛó‡ÀÂßÕÐp,”oªmŸŽ_úO³ÒI]äkÔûœ‡oGÈcüÔ y‘T]Ã9Y*4B´Œ~´`Õô0ý ‡¸Ü“Àmª@‚=¯u˜_JCxÿЃnvz …_ ib ©R¢‚d81,’½ö— ~wLù¨KC±åÔ"¾RVB\¥©œáÚâê…/ö:м†ÉäÙâat¼Á/®O°ì`1HZ _cÛ:ž3·Ô÷˜š+7¶÷áˆä Ÿ\ra’Ý(®øFG¶=†2Æü¿ÏÏ~IÒØ ,ÑUŒú…SúìÂH8¢“º7Œ¤ñÇéÕsZ|Ãßäügä˜VV$òe½øñ“çÑçÁ`OÇZaÓ—Ù4§3gÆ”ÐËuãù2…,p»`E7MþÕ;§ƒzšéÙ퀲n#é§é¹¸©Ñƒ—½e°iœ.OÑiÃù¢|“ôvÑêÁðjðEê=U…;ÎñÍ8÷w‡ÀÊê,l©¦«õGà©<)žx9ö.ŸÊã%Nƒ!†—ñÑ{v)›RšÊãf{ÒŸVéôÿåC¬©f È7 宕§q‘†ܘ!N—Þm#MèÞѪ`-]Ÿÿ¬’$x†+lvœ5u”áe½›éÎMø¿ûöâQñÖJå·ké÷¶ Øú+…¶K±è£ûXeݦ²w<-p*%{¢|™úô‡x9GŠ*8–cóê²ò¼#>ý™ËZ¯k·ã ; ´˜pHèf:‹?¨: F³Ly·@>G$ÀuÓz/º_þùŒ…2­¤§¢ Þ‰Óa_ÁÈòÖy§€b‘]»,Vh»è*È&Ò¥¦†´K: ÓJ͹²Ô>–áÒÞ3àÙ¬S~чš¨ð÷#§´„£ŽëpV­Sapàãi¸þþ¸¨Î¥/ŽôþWgͯÇÃþ=c¨×·^<.¦8`ÿ¾§ï°¸#é¼ëåZr~,Ѧ4õ¼óèÇ—ëex«ÇmSÁô§AÞ¡ûöIy Bzø¤ÙÏ—ºT\¥2•ŸXÊÊÇä0'_yÞÑ ÝVÏþ¯Ç²Í/y®½7ˆS¾ bKï¡v³ »Ø=¯¨„[ã_‘¶KÏAâ{=#¿Œø·MÐ2ò9nRhÒ©£ø’¢í<7é-\m3åvï¶ñQnBè&Ä?ux›ì+Üøì(·(J!>Í×+ÑeÉ«¸þkc8¤‹xiw M4=î_|áSÿÚhN»7a¼OO”¸ ó¦ øÿ#…µ¬uŒ2 ùÕ)PY‡¥ÆoÀx~™ ( ý}€ñ F¸êŠ´«‘¯íÌÌbdü‚—§q·‚A#ëÈ¢˜¡v¶.U»YŒ•†¬;•‹èKrï+#¹‡r&,[È—†Ž§»ËÐc÷ Â{,A¾}8]lšˆ¼×ŒMFÓÁ_>ÁÜŸ’ËúkGò>.}§L“^«à\…Ôx~Δ±Õ1ÐÉEžxuŠÿÆ[l†pÓôNxU-EÕ×ÂûOQÏÈb5UéèºJôO¥pøE ›üé<ÔÜ'_8\ï È?êE:|7]…?ßÁÅc1,ÉEžOu„‰*Ô÷R |ö/`ƒŒSxÄW,Ó‰íS¸üîxgŶ:p6`>L¬½ŽÓ§ùàiIkj`—ˆ•6sAt~«˜< ……Å;áåí.6¯<Ѿäb.Vþ_ý¯×:FônÀ–÷áð¶%øÅAœ^/ƒ©§ºvµBE{¾ûÆtZ´O £ìè|ás80Ó”Ú‡ið™j?àŒ¢Ò‹]¸ú®9xnòçúfx¦Ð‹V( §*ù‰Úh:²{1±j*ÆúgaðV þU¤ f*{ÒƒÑ#hÝÕ¥|Êî¨}Q†©Ùƒ!àU!^Úº‚W½d¿áx98yåC§£ ÿé‡6ŠoI¬¹¿¥T¶;„.óláN¡8,’ø§÷¾cÉ–\·=¹ß:Oý0 ú(÷fžðXœXúB`àÖ€·æjQE©‡þ_(ô€KS6NS•r­‡'–#¨ófm U;/Lè-µ/É0ÅБx,2à¯J&ÀìÓO0ík Øoéf'T~²ç{^Ù¼ÊE©½O?ãÏ-ü ì¶+ÞPJB­¸åÌcÄ>§KçnPâç/Øð<¿XŠÎ˜û EN(Ó_6 á—ÊpÕ$ˆÓÁñ} ,$0zséPiž¸†Öfl"‡N¶Â·±Mð-ÿž÷>xÈÐ_N™Û°i\µY†¾q ìÜË* =ôþ;Ž>Ò\%@”&ªªr¡ëw!™±9ÕMçY߯È?Ñ¢Òb›È½KÀêNøÕ³Ù*šô”üJṵ́·'ÿÀ}ZnÔDD—Ú¸}· ¦üÈûO&Üá®H-žO]NÛo-ëýJ½ ¥¹g¶ÑQ[Ô¡cßVؾŠ+e}Ç/.|uNð ›FÄ-àò§ôÐÖ$:×ï8Þ)dsKUˆyA<Ÿ#TK–Ž˜›D=d†ñy’Sx~âVØÿNž$ÆLiõüxz-?ñí‰`‡Æbô´ã݉ÛÐþñjZ;ÑfyrßUÝá]Øû=ÆEðQ¦Ú¸mðªr'Œ®æ¶‘£ìnØF×Â$öQjÿãw3Vò•®ðÓÕò´}IW\ïv;Ó™ž»áÔ¸Á4°T“ç§ûsØ,àä’ ßSvÒcÚr~Xõ,“]µ£†ó)3„<Ãm#o_2’o2‰ ÖV¢FUwIú¡3xI¼?¿ £Vc€æ©JßÉÍug`õR_n1o ÷P0à‘š¹P2ç)®’/ÂII£á_F/Èt»Ž9ÕãøâîpÈ•YD­®Õ‚ÞwpÀ]•ZHËâúsoìKï…àiye^j3W,x; ÿ×ò†âm÷8ÌìÏÄ ÁÛñý ºÄÑ “» ×7tí ;NÕûۖÜCv ÎÒOÚ>Ø—º%!¼R÷­ØŠŸBšðÊ{3úÄ­ºž¦±^™ÇpÍÒŠV–jпxÿDŸÂø­¹vê5¾YæE#ßÓîI°ú¦¼ŒzR霡É7€€˜àL~açþ²5»—ˆÁè’¨êgÍÞV‡ºà@>Øk,.,‡®¿Jô°×e­Y–szPÛ÷*˜~LÀ æ]øp©¿ñsï~aÈì>·ÊåÁAò3Óá¬gz6N¯Æ­Ý¹ø—»‡£¡*¿|f†¾I‡ Õ!¯Sš_±Á•ÓiñèqøÞK‡jÍ_ç7¨X2’,Ù>_8ÿ½¡â´F.Óë áb*Ðà\"]V6‰¾œŒ'œ –Ňé¿óð‚Ü]¸ªTÆ[ç¦Ì¦žqàý_|²àøÞÄ„gåí„ôIó6™ÑgÆ9äò3®ã ¿ã\oá+hœÆ?HÒP7˜ï“Tòéè N|™CgÕ£¬åYîîW曥Z°èòÌñø²îªüÞš·ºB‚î6OÁRëb”þõDÀ¶_4Íx ÿÝ´ã8xGÁd¿ëg‰ñ×ß®ÿW‹ ߟ‡Ó _ ©bÀ ®[¾‚Þ·B(Ôab¥1U„ðüŸÐ’JÒÊ¬ëøøãjsŽáaÙAt¼–=‹þ³w:|»Ëqr›>JìøÁTR–ãL5þH«„.ä½{zàé¸é¬9DA°¾NÆ¥šÑ’y×XÃi}zpdNÍNäÇclÐâÈ!:ë{ w\¨MN4¤¶‡Ï2¦1‹ôø®;zS'™¬ÛGè—/xb3ð#£Lxó¥¥ü³K£à”Ý;´ô½MŽqn ÀG¯V§ãu›á³ÃŽÿ·:ü²@Gã0¤]†mk¤!Íd” ¥ó.)ôoÉß¿pÕ’×àH]+?ÿm#{  Â_{ÒÜ/ª,ÓÝ…&D–€ûà‰äuj8“h<ímàׄ5ŠYh{ǵ§ºñ¾y¿Á5oºCÆÈ¾À×Íén;c^Wß‹3g‚¹ð (v‚­zd+œ€À2=ÈËx>nÄ9&Á–CPÞyiS¥_¿eÒSž ­Í!¬3˜û¾U‚Ðü:öì¶45^nÛcؘ ûÑB:ë6ûãðGÁ¨+ƹÌÿ•…Ó^ƒIé T_; ÿ´¾‡•Þva»~Ç þ·v¶Iußžv—Å;±+_³Ø ÅPé~I5¥¬²²gž-ù‘xBc ùc%¬I§Vg¶ Bv÷“¯_õxÉÇ1 'k@Æö&4´éÿ¯>™ÿRBå–ÝÆ¢¯ :Ý=•¸ûu¼·FÒÌ“ ©³’ž'JØB?V:Ø]»K Læ:8ÿ‰:œRƲÇ+ðø½•|QG ð{H\…Op¨ÆEP›5›— þhãä6D}º¿=>/=®Æ¶èñâéKR£Î ­ÄÏîaø]Z€û{÷‘ˆ]ù»·1<È“¬”| fbúx5 änyáŒ=Âà—רªçýxOñ!™r_°w]\ ïóÙܯéõ Ä-+¾Á¸£XßÍ)¼ô\ˆf<&¯Ï=#ÝÈ}ލn.FmvL@€8¯µ Â;Úˆ±o,Œþ»œÏx/ŽÓóàp[6޶ Žûì8Ë%—éïPdÂ3¦kp9ïr|µü1vhj•P žt®!SÁœgªPTÑã>£Lyo‹!j½†3ëD¹ççÕ(¦ZCÒ+°»%ùè3*c@ÿáÛOf7Ù B‚¥ÈÏ’5üÕ‚—xêü#,±Ò “ï‚ÔÏCÙÍU7àÛ¢½¸f@÷= ¸b´€*Š,ä:Or£p X¾Yæ‹l  cgP‰~Ùÿê…gCGÐ¥K°ò—׿îV9ül:sëÔ‚í¿f8–9ÛÍgÂ:…‰˜äpœxÖnæê d©O¼?,Ÿ…̽nËîÁL‡ÏÂtsyúñH<éó#ÚSå!eÖwfûl#öÄšö~¾ K+§ÓzÁ,w³¬0ÊÉî»è­ˆd®Ô"&"éhöL…Ÿ—z ³O5¡¤A0ýoÁ€üÇEóŽÂ©‚â]7Á:8‰.—d¡ 2tÒé=èv-žW¨}+G‘«ÇfQŸƒé–ú±ü±ë}4µ”±¯v,„„¾,hV˜L¥U¦R¹ù’‚cçðÕvÀh –á:û†µ¹ô¿zépKžñ§½_Èñøf{á\ç%|ÑÎíæ~a£®ØÐ–+iþ°ñôúf–o>–˜gSãÍ.—ƒ.]àá1Yì×Á{©7aý—5¢fÊ5`V9™¯ß4™²{ŠÉ¶QÑq{N ^«D½îÃq¯pÓ_i;ñ§pxg ZÎBU³Éüç[C »]ì JG÷€œ—Ÿ¼×œ.;;•Ž"ÿâÄ(̦[g&Øï_ü§ˆ>¬Üæ6•ÎHm‘´)B“êþú> ÿ]£aã wg̦Ñ\ÜÝL~¯¯î÷LùU"ÿwZ½¸H" ÆðŒ• û‡ÛMøŠNĶN™SËý$jèT0:€Á‹ÏÂ&Ùjìi7¤oåóÓ+q¶,¯‡Ó†Ò(*ÎÅ÷HRÍCBHø­ÎètœêÂÏ)'’ñuã¡]PË&„õ‚S_*}…«Uc“lkè¯ÅS†€¤Ê³ü¹‚ŸA'a¡£.wrýjqXHÏ¡CV0•A3a»Âyᓵ+qÊï•0/¯ ×Ðü<¹Bx÷v=jÍÂËåzÐY6–Gº6`ê¬ûÿõ&I=„ÄgÍ¢{ž\ÅãWî±B?—{‡ñÝ#ðxv|®ÏÓN€PÞáìJÓ†ßB­íxnñGžZé°SÇåð`ªlAvÄëá[÷C|wër4ïmcçGíÅ.Í P?*‚u< 'c{®³ê·„_!Ä÷Ÿ³!ea>dßÖãæ[¬)[Ëþ¼ÚÂÝöŒâv]“¡Ô±†UÎѰÿá{¡lì´4|*|…}p¯Ý†~ªZÂ~9ió½#ÕùµrŽí/F2Ó‡ÒSï‘ÿú7o/ë$[Qé8Mú!+ËÊ[P*#~põä»ÅÓÙõ7ƒé¡Ûrü\œ«Àl»¿7¥ }"W+Im”"ÃÏOÊF×W¨Fœ¹ÅQÂï²"ôK[ÀaV3:V¥¡ÀF8 û(Ü8-…¬”ž.52N~ÛbÛÛ\ÆoIW±×.'þÓl…sÕpu½./\9ŸÙ=ñž L«aµ°oÖbcr$Ö$ÁØ«Þt.& º:û„äh:x=—Ö'Àj¿ &3F‹.Þq“hnή¸;ë>Ñ ÚgÁïdY²©GŸÍñä1ï~’éŽÒ\}îã“ôÏ.…ob‡aÌônX_c<6âó•°ñx)lÐø„jÙÇðój%(€Wí•aÛ\Cø¾»û—‡ÜÃ/{Xéõï8Ü~0äôÊ@Ûž ²1ªûFõA©~)ùût £p–G àz½ ;€£k.€éM]®rô.³6l#f½éýûwà¬ö0pØ£Ž'în…-½p³<Ø[¯&Þ¨neDGGÿËYÃÄ!õÅI»)ñ]àìµ ]=™76Û°¦é2#6 Fý^~bdžûi,Ügúò–- òsqãËÈþ8ZmüV–þFÑæ£ »<KâÇË'òíçñ“ACqy‚.-îŽIV›°5'Œ,< µ 0îl"Ä»6Á±Eòß?Óõ[:؉û£ø§Pcnåum‡1¦‘üÑÇ*85ÚŸªL¯u ÒO)0‰h!¸¤Èó ò@\ï-Å¿´…¢åew®´ù8T¤¼îÄ7,&>E¾D²¦äõ0Ä•cãˆDÀD>Ø/ÇÏÈ‚6ßdôö¸ÍQÀç^‚žS¦Ð™éGð<1‹Çú­ûˆãÙ3øyI4~­s†ãÂáßxå£ü-ñ“Î7Ò½¨œ4MøŽK»¿‘Øp Úë OV-f$!úžÃA#ù¹pQó«—à;ö·°ªà#ÜYw´dÏãØ~ýü 7ÇCµ¢3¢G3q ´f¬øï£:<6õž{0ƒ¾Ö€gÐtùæÎ>ÊŒ|6"´-[UVmÅÓZ8á³-¥ªÎšyµ†”ìÌÿq¹3ÞºdÒdúNr²p‚­]W.õk *¯2®ðqyW{ ·ßý gÖmÕ;…ì”Þ<<3k ïåã‚,Š— ‘žîŒô^‡ÒCððhX‘EqçÙ”Ífb—pã o±EO’š-1ↇE©Á¹­ØtÀ”ºÄ{ñu5/ÉíU±Óî$ZöXÁ–¹'QrïnT®xþÕÖi_î‚¿l(ýÁ ™»îP¾÷²lÿb€n›ŽC¨l§ä㬸ÕϘ¬xt ?ý_¿æìoK±HI?Ÿ‚'·æÈmÇÃ5aÌÜIœ ·»ÎŒœy{ yöqr®$“W%ÀÔÝRüšžx·}†¿“? vOŸÉ †pµÝ˜£D;¥áÆšò_¿g¿AR×ß2î·Âæµ%˜Õ¿NØ>&ž,W¢³/ƒ-Y ƒ;HÀá•ðm¦w´|;\‹%Ï6Ô“¥«ñIëè“}‹?F𢎠Ù- ^ûÜâÿ(ß4ˆeÊtþÂòz=*þW …¹˜CãAÅ,¤ìA]ðOmÈB‹·phH:]ÓgHHõ~H¹ûŠìƒêÃÆ°êV9NrùÁJÖÏá sšpRz&Òûðj`,lË·…?,ù»Þ"’;…0'ˆ‹ó ™ÿ »j—áù¥Ÿ ñ¯ <3)7J:¸O¯ÜôèÀÍ·LWz/PšÕÌŽè”–I“©8¬6Îx,C«èð‘Äyb­lÍ+P‰9‰È/.ÁŽ9 .vÖÿXÆJ—€ä'ØÒ<äý/¾ºÃ:£\aÿQxpGÚµaR¾V†¦“0ñ¨UÍŽ/l`Á‰ñÝI®78•x^Ø Çån`Oîarâ{Þ)¬¦ãø±áÞ<¼}6Ê Ã÷?CØ6ÖðÔ( …=hî¬RÜ?ÙˆÎJŸ Ùgµqú½¡mÈ%24Sò_»á§¥j8­6õ*Á^÷Ç2¿á8!…ònm’+)J¿Þdö+'³ fSYp‡E«;‰­ÜgôÏ­bµ³§Bbû èè´ ??ÂkËU¹ÆCh–+ÈlYfÑ¿ÁºØúÅ{ˆžBÝ€ÿW«ºÆ6WIÀå«WqU/¾òÙÁÚ‹uñïèáK‡!R">l~›†'að>ÌNx€FoBÙâÆk0zö÷÷G ©\•N…go¢wtŽ‘Çë—pùÝßñº Œ–_‡†õ–¤| ÂëÅ©›µ6=rï#œ·ž‡w›¤ùæ~=ªœ FºŸáTÍx½? Çùï…s×eøÁÞoHFJAóí(= FC]YÀj_¨išG}ãÒaÊ\èXwödá§Ž<®6Œ ‚ùêcá¥×^á÷E,x:)ëÏCK žß–­Ïð{¤Š‡—ïEã¨"R®°‡7ï] ê6ñ\;~?| ~-ÙÄo±_%‹8¶ƒ˜¬ñ(¶¶ö¿k˜úáF~ëf-ûÖÙK‚Dè¡%ÃáY¶uiÝËšFSEH ǪŸÃ±Ry~Á:”ì ­"ɘqR‘>JS&/èi”Ý8—”òeÍRÄ·€±(‘G¨UlBDSBð~KH,<Œ3cµÙxçjñQ.ìU¤–I« zädVÛ‰c§¡¹Ú~¸÷³‡xßÖäÓ´»AóM »|ö$ãºâüÍ÷MAéq¨K·åŸ¦BÿÕOùïÖk†`æ¤a]Ù­ÍͰd§xµ¯Dë÷q¼Ìè7™è52Ù¸I{#-ì¼³& š¡$軘ïUôïƒôF©9¬y<+¿ÂY¯XñU”¯S’ãíu}¨íK&Œ\BÚñY²Ø”TÖQϳê0ó‹3jo­ÀOžoÉMLïm®]X*¸>-ÝÎõóGÐq—‡m~Ï âwåä¼í\,»Šåô|©4Êõð¿øhÊx7# ;6UZ¨Dõ9a`mß¿ñkRŸ¾†]ݸaª!ÿ¾¨´jŸÍÚ9¼óèTŒ´:`ÿ™ ¾poÅ#ðõ–ÆÓ³?À™â'XYvŸÍÞ3ö ß7+œj{ X¹&WŸ¸I%“ѧÒaMh6-‘ÇTÿÅ˶¢\wØf¼¾lX—¥:^(³ê»"ÅO‡s†õ€Œû+"ƒñòÆP²3ê"æEÙ£eÐ3R`W…¯–À±ž_¬¢OýÞcEñ3HÖJŠõ¤Vmq«‘©¬“Ds‘j\üîÑÝ)TK4äc:ã&u˜âãI['0ó_ãÿ­o48ü*Óë4LÈF§»mpUj4òÊxbc] Ú{;YØÙú¿!q‹jMF—Ýa´0Æ‹jA%ªÇÀ›á ø¢ê 9lDöY“ÎÄ6÷À(î4I šZÞB{Ñ ÷‰†æ@E‚{/¾&[W>lP—çÞK_²R'{þyå¶8ôS‰Œƒo/"Ð]«cV”¢¢”8ÍhÐmO“Qy¯•{Fæñp ŸUC|õ­1Wa·YE¹Ä«ø"û4˜ÔKðÖ¢62g Ó—Ò†™%é0RÑòÞô‹¥ì¥m*œï²kq Ù“þµ"bO·`¬ƒ/þ²Š.÷«ýwÏqHªßBâC˜ñ¼×ürÐ'sCòöŠ;<לDwwnâŒd× Jàü´‡ä^›?•ór†õ÷†pã›èW°Ž£à‘ÁbvÒÎèßl—+£é{ZóñoL6¿ÜÇVÁÐú#0fûH:ÔdîìäEõÎmÀÿê¡¿$„áÓb¼Z1‚ú?¹ î­£ÙA«.rÊtïjãö‰»xÞ®C<î],{–²ßÝ¢ÜOö(lÝ-¸4Ìù¬Jã'B}Ó)hnL3÷á¹IiÍ ®¶Aš[$ø÷È•rÔ4a<7\Ñdÿ üå~×î˜=Q_éG!µ/‚%¿Àkö8ž7gþ1š ›®]¨ü:º¦©åÁ6IIîéoÇBŠÚÀèàLºûþ~r£®ZÚ P‹¯†{3ÓÐÒa?µz9Êù:φ®ŸHÿ«“¶•‹ëlGS‡õ™¸žCaßt;˜MeûáØNc¬±< í¿sPâà á9C\äFLu7áôˆá\®v;¯žcÅW´!íAsx•™­óðô¤>¦c®HT¶&ÀÖC†lKÊ¢8j2.^¨Hÿýòe»•è•tG¶T{Ì?VX(KWjñÔïaø|=`Œ¸ ž¾}†äïþF¼÷ŸÄM)oÊûvøÀñ¬Où ³‘¤2%—A›&rZ/‰ïÌ|`±Ï¨È)¦­ÜÿÄUØ!ÚW¬MèDƒÑü•ô%o§Gw}ËǾEÚ0Ä{=R…_ÝMxöÄböï_‘ã Y+Ò§gš@AÌ™ÓÈ¿l[®“Z?iР¤û°þ+l–ßö´þ,žöÎÂo~”ÎpÜ §~CÉ—þÜôÛX¤>È+ßÕ‚xÒn^àRÓ™7-&Žý¨È37kRïÏÔqœ%=_„_¨ð]ÿ°ç}¢ôB…>}$XÙŽ…˜¾, |_/ÄSbsppZª ÁkŒ/`Í3Fõ¿Úxð§wðO‡ÝVðPƒ†¼› S™Ðµ—ùº]kx²x0½¿d4¿g4ˆÚÎ=ŒoZ×¢ ÆÁÜ¡äJ8WØT‡‚aCyTv_ïeCW×ׂ‹ÅIæ0²MѶåá_Sé÷·k±Þ³¾DÈâ†â06N.·Ðål…†rö÷ò¡©ÅZúÅÕž:Œˆ"ïg ”i†À¸F•®_MλԠþ÷H’â_€9‘ÉxõåG6éùT úßþO†Öq¶Yæ.“­ºWR6Ú;þ°…Ÿ_ö¡§ÙJôÀã,Åã†P˜Þ‰ÁiîøfÞx\ó´–Œ8¼ç®F÷Ú|Å3ÃV“¬S¸ßË€›57³¬eîQ^UÃL±ð¬% ²Â×¢_–._9ìÄê½ÀíY+y¨ú,”ñLýöf¹fE¬\*Wðh¯6dÎë46 c·‡ƒŒ—#Ó\Paö°Óû©”Ñ…É›Ëá÷`E{4Í ‚ËwÎ}Z@·SØÏ^BùÌ ÊÓÈžrc®Kw±'Kc`UF«ƒøÅë/ ¡’ñÿèÚspÑc2ú¼ªƒiî¦ÌB\‹UÿC¦?fÙBÑ¥ø0O8”¹hÿÕ,oÜÙª¾—Ay›P0+©AøæöLö{úP¾%iÖÈ–\ߘVsßBébnûøè¦ÂÓÇútv@:Ë\¶›.¡ø ¼9i5¾róÄGs)-¾µ–Þó[LïLòyÙSÈç´‘/ÍÀiª€ïj2å»ÇÚâaû!­V> ; F)•Ädífˆq(€n„ß’*€ÃÊbܵG”-)ʼnCLøaŸ°ü]á7`a¾¼\nB]Æ}Å}>o R¸ n®VàY­ì‘œ'-Ç_ORÂäxc:}ÒzPóù A épó¾&Ýô•…d]CqC!yv€ã†h{Ô4¼-yG°N%®é‚þßO‰¯UîåË‹nÔØéN¡ìÍÔ>ÈHz£Å©çæ¥È®=$7`èÆ^X:«³Òb€`f$_51*Ý¥hÛ Q:ZÎç'vƒ¸ï¤{ÌÜ%vå{Â}‹Á>ð$é{ý‰9-k‚~ïw0\â=»ýHo¿¬Ë4u±õ7{ áÞb2Ü=]8rô¢øç8ë4“Þ<iÈ ù|~ú€¶å^.Ü!ƆN„“ÛRávzõÛ …ëP…Ž‹œ #·bXÑÌ{ë_âÝkbDwîdþmF3Ó8Ì|L9N½rì7Á)kî¾É˜®úåNÄl×Ðñk,xÀ®ó”Z™<‡–W¢´6ôŠúý'Pxåƒ! 9ñ®IAhq¤ ªÛ æW×ú£Í¿ s­[áºÇ ~dÜU¬Ž§Í/Iÿ ŽÓü”˜Ö¯9ìúen¼Ì/ÉÉÜkðÙZ·†ÕÃã‹Ö°cð+|Ý:h@þvùòý͈KÏ tß'0Ï_Gé¿$òàO w*Lƒî”çpv‹­ ÞY“Ož1 ¹º¿¯Ž»û3©Á"Aÿi 0Ïu¤ó—,!¯ž­âÿÕ>óO3Á;ÓkÆ@™Uã?ùy@|%œLú…o:»a‘ÌOÿÓêêA>_Ÿ¾:±»üЧҎÊÕÀåðÔ™ô9¹ŒËs§¡Ïz)¸ƒ¾Ç iƒC2z¿Euxæÿ0£¾n¸Â}ìœ&õZ·“£kæÇÒô7ÞLTä’K³Ù¸”£¸åPY®¡9àÿJ¥tѽn8§— ¾o·¢¹I), á†Rᦔ1õ¹ÕÁŠÊüUX;fg”£ùü t+Ù…›NÆÛë¯Ü2'qñ/‡ð¼ÉÂójˆÁš¦§4ƒ›…ÀÑi£é ÃE8½Ý÷™4ÀÁ}Ž1Tä­=¢=ŸJïÏ$y³gð K=š!ývÊr¹õ6ôs¼͘fȵ[ly®¥—ýL'µ žÊájí)T3q7wU±çô—2ŽÞ víÆô{ÐEöÃÄ—>²Ï Óï¯@î¥BÙaChøì xQq†ŠoÃä°ìoLd¾Áâß¡³%Ìåú»Iâ èqû¨pœ¾nom–¼æð-fž;”kÂ#$s›baêÊNô*ñ<•½œp/í¿LN ½‰dü2(=Ž¿1„åét¨I ³ë˜WY©åˆ:°n¼ŽÇ±}Ú3üýº öÔý…)ÙxöÌlš6üz-¹åŽêÔ§ÏNõÅ·Nƒ¨ì¨újÃL/AÕÈ4|¬Ü ª$±¨»Š´ÕÄ‚œö/Æ;ÚÁ1Ö J籿˖0“ bý"ŒtJVÁ‹n–$1nrÔãÓ§MÁu7E©ÄÌ5öÊá‚Àk ù~$?þ!ùòÜÛvôÚO©Å|¥¦àÊŸq°Ë*Å—cÍãMðàê[Hº±ìçL˜¹šÐgIº8Øñ;¾' Ò[Pgd.J©×µú©¸{ØêãoÉ£3ŒÊ—jŠÑ? ) áöe!|Rv:Lp|5Wä–¿²ð¢Þ\-<›¯hàØÃBÁª¥µ$Zÿ¦\‚AØ®˜Û,²ðK´*-˜x—ôNÔ€%_×`¡Ã5Œé»ò·uÐk÷ üE÷\ÂNÕ]Å„ÑÛùSç&p”ËD±W7êz&ýoÿc_9Þ Ä¢°Eí ,ðcè¿_ƒÓ£!\uO0níʯéŲþ«X^ÇÍ 9å£ÿõZÆŠÏfTØ#BÅüGðé¯ `×Å}d¸;¨Y+Ã"uîã:ë¯ç²”KxÝÛB†[²Ú8e¨Ð[E†Üñzåe¤<¼ÁÖ[Ѧ##ð¹h6CW·ùÂFz¥ü<<é–¤sÜ—òµÖÛ©Ô ª-ƒq–mp_Ì÷üºÇ}Íx®X7ü]6=/_!Â1Fp!`ìøpM—R[Ã-Ž1K§(¨ÛÕKf?÷Ç#Z =¦ ØÿŠ*æÖ·Çñi4kå\êhKàJÆsò¦¸Yh7gm"Ê=æN£i{.ó¥U†tï!Cz‰ìå«§6qå!ç…¹ç$áÏ­Pø¬jÂwÇhð²I­¸nÒt*3¥‚Ÿ ·E¢­Ô".bðßwÞiûÔ½ü±ÉväüX2q=³â<&{¯¦^. Û¥ºT©$™ˆ'Œã¨2_Ã]8I·‰q·÷éõo!<ÙS@ÍR±Ê÷%¹³Y—gmzŠªʱ ɇk>± _ØÃÉÁ¢ æ%À²ÁÙüJ‡)^yU%ø^àÅyTêh™àOÎOC£Ž÷Ø$I—æºCô:¾ d•Ô"tw²"ºv¥àí;…˜9~)©ž¶Ž~΢êŸýpïÓ£<ïÚü¶?ÎÛFu‡½&×~÷ÁáU1Øë›"Ÿ˜íÕ!Ôi];,QîÆ]W³`ÿcK¾õÕrÈüp‡Ø;ž¦ŽžÑ|´œ×® Å‹Ï|¸»Âr~´ÁŽþí/‡©qùdÉ—qp|y*µÛ*F#i;Þ7„á+{`äòÙ8Wm'¿·*Š:¯  ºzäh®ÊÜâ*ù!ðÕÙ-ræs ´ù[1Þ"EMB¸§#QUþ#¼}Çl@ÿݦOÁä61AÑfO˜ZªÊ÷´ì’t)x972“Ø#Œ™“óoƒ¿±•V™x½õ(6í„‹™ð¸ÌšÎöIÄû ‰<íÍ,zuÓZœú}nÒËlL…ÝúÕX=<&-U‡‹‡Ãžœžä½u?”àÈe„ïþ_íp|â~jÛ«Ãç¬z‚å[@¶ø5¦„ŽÀ!NÛÈ’k]¤õ:j¨úÐß[†ƒ 9Œ\Èâs“»$«Ï“µ}ÕÁD]^ýa½„>¸€ãRqÎÁ+ÿâ:%Oéð~Í`ìq½‰§WŸ‚œÍ·üsÃ0qSgË:ʉsn0T²oµ^X‘(Â7†õ4n}g ú-L[oŠ(¿ÒŠïÍFò=én<9d¬©–À+¹©¤¨G«Ñqdª¸2*ðL¼É2Ý `œñ/F©\Gå®×Ð×fA…i~PÒ&Me¶-¥×öà¨9ªø)QðpoÈŽ÷e·&õÀ“dhNÐ…·ºy•²0ã÷Rد6†Ž[E¶ï„CfÆüÓ¬l¶Z^oo%íWÄàÄN½Ê™ÐÙ5ˆOÕ€çgßßò0l˜dÆT^ ‘µ±0sáVá^ÃÿÕ~!Á _ânÛÙà`)Í¥íÈl2;Š:°q´5µçöWÀšakéôu>ôÓ˜0Lž>µÁêì5f»Ë„^î²ão–/áï¾ä0/ÛUHÝæóÀ+Wé…‘ |óåx}ÿaÝø¥«”‡DÑ´•¿Ájnv啳0 £éC WÊæNé×ñïqÊ‹aì!Bo'û²íFk¡ØùßÚg‹Ñ-Ë‹©M±µ7 '‘ £éí3à›¬=[ðÉ‹¦MU£ÍѶXs7 õ~[Ñæ5–´_u¿µäYáí¥Êâ$"Îy»w€ÿé(F¢AZ*ŒÝ¡Ës6ÈÓð+«¸ºÛ [úÝ•®geá¶Ó9ÔhjÅ$#Oº1ö-äd^ì†{×YêòãÔµ^_0Ém-S¼-I×”\ÿãÌy“Ïœ9áq-¬¦RŒž .á'qVâ4¢°c7— ØÁr·üÁøªWP1õ#¶ÆBg‚<½íLëíµˆû‰ôz·ÌNĦtk‚ütc:³E7Ž   l-†ÇÃ×øwh’[N\ëÁÓF“Q¼ÈÖ %ÐM1 6VŽæ[×KÑsAÍ©jœ\ïKw­ÎEÓüD˜">–—åBèá!t´tM”Šv:¡ßì*ðjPF_³Wø"}­ØßȪª xÇM5j3CïL–Áµ·€§_<œ4Ï D¾­c^ón n_;— ß ¹"4 ØÿªxWð´)\Ú·?Ÿ¼‰¹êK1õ÷eô+S¥­«E豉 Dx„üv5‡#­vô‘Á0ˆh‘äŸÅòà¹wàY%œ¹ê¶d .lßÂA5Gª0¦q?~x€› ôè(—PòOXƒwÛÑúsæ©ëºïz:†HÐàsÈ×Ð8œ§dÿß\+÷†“ò[.ès5e n@¹yý¸ì,Õÿƒý8þC”ý»ŸðúÃû ¿«–Ù*”bË“+ ÆOÇä=3è ¢µ}ÒìÒí`l¾²׺„±i†æ|›v ñ¾¶}@þe/~aùŒ_º³ ¸x‹÷Kè„|¹8fÜ"ÎìUþa]q‘yêÓ­Ë¡"h2OízÎZï{ Ä×gÁ¯ë/¡{ñ ª54-N$ÞKNáÅöõ¤-,x²¿‹3>kÑCÛb Èt(&ùö€ÁË0gÕ{vËÁäÈš9ÂzOy~¹¹Uý¯ÿÓãý[ÁÂq‰oKÃvOmúÚR‰Nª@ÏZ¼dõQ.òhÛ½Ïï§7ŠŸà¦ryn?*’DÛð=ǾbQ’:ïLÜî@ÿO¥ìÀµ:V>wM'%P'Œ·ä=ùŽâAuÚ8&)Ïó ÿÇ…·ÃúK÷˽­ QÔK}+Ÿ4ú4½«‹c ý`C¦ ¾MÞ®ÕHì-ªÉ´g„›¬ù…Ë蟣æÔrõ"xc*Žû6Íà:á¢4="ö„n‰+ºësþ÷üãáL´Oúgßn༣â5+óbÈά]˜9ÔJ¸lèLðŽ…ïkpLö¿½šKP÷Rv-ËÅŸYÌ/Gœ›’[Â%×ܨ§O ±šz _ޱ¦áë¶ÁÎûÄfH¨Ú­ÄÔ„›$§í 6;Âòž x­: jõW/nº€ß"ÿ‚VdSÿ·,û oâkA¿ð9{s¢"¯ƒ¹òŒ–æóTŸ³ þØL㟂¬ Œ…ª({¶mÏÄ|Å&2•}¦Œ»‚k A|ì6{J$ZÄÏ£v•x|Ș3ÿß¿zq´5*‘ÍòOpCø µ:ú§£Ÿ‘=[óz š;É“be3ü–)Âáà1Bkr;Ç|ÈèƒÉ‚»ì´Å¾Äù9ùX“Qu†´¥ÝšLYE¹y}³|–ïNßf%sÑ—°îÎIœºf(½Ù—ä>£Í½©paM>”ìíƒz©#öq"Ý)cÁV\jqʈÎ#+|¸Æ|9ºHVžê¬½‚×ÿâqê zîÎEˆ~¹\j ±?<Ýö¦ODga¶¼<(¢£?\„+cã‰û:NŒÓPÁ³ üsq2ü°Íá/›Ÿ àŸ”#Œ;3ˆ6´GŸ,X¨ö-E5½@ÿ÷OÂ2}êêÖÄê† cô¶h(Ð †ƒ/^±ŽÞ!Ü®Y&ub‚á >Ág>u{½ wý¬'š}qP¥eFï~ÔãsfH£pÓ!ᬩ‰\ò|;žè¥Êsy ¯Ä´¿ŽMæÖ"÷ØAŸärð\áÜñú0_ŸÁÄáÇP©4´ðì8?¸µÚ,ëÇð"Û<è› ÇïÌèG¥ç¬0ô„`šù^øñ’±q}¶øµBR„4mu]…Gƒ°_%õXµ »¾TèÍËÕÅ?°¬‰ðÐÀª¨žF9i¾eW™R¨„Ê¿Ñ)‡=…®o¢¤YÆ’ëî›+¸&¡Ëûr^’Êo#ÐäÂD|ýð1$*¿d†³yŽ[þ¾²Žï•xë7àóoM°Ýñ(k(Ôæ—ëÛ=‹Ñªú(Üw<@Þ .V$Bo0äÿp4«ôø{_P¡Û†ã¥öá§Ä4&;?¿„¡ó…n,P^‡ì?áÍL[a ‡{#†S§'è‡xtÏ ·å<À‡§"„ëýF ÄѦ—ÌÃ|«U0Á›}‡ Ó~[3h0Wú¹‡h°?x~|1ÕÁçhóð,­ñl.¬„þû!èc¬³PÆ—á«„ÛÐvõÇá*!¸á¦AmS^à» £¨ðÝ{bxY›çz=g¥4h—Äy0Kx¦Ã;Ü駉ߡ¯zÅîkùžêuh®ºüÓƒðí†vÒtŠ6c.‘æ¨áÿw_§ïRà››ÆjÛÓÈ™/8wÖxXØ™‡g‹5à»NŸp¸úQlHåg…ýè·â vçç=µA\ì,j$~ÆKSΡ²é8ˆ¾ž÷¿ýŸŽ3°t÷g{Ï«‘\¹5öØÖâ¼ÅO‚)¸2 g¨ Q÷ð¼0’ó½oÍ }0»Ý(ë²Ç¹—ØšóÊüK’–,ÎaÑ«î`[¥6•“\“Šy¼asÿ ܱ Öž’¦—ZÎÃQÛs¨ýÌšûuÏCƒn`MG‰t¸/¶m8Œß–=EÝ|3Uö#þ{?.wÿû#ö)¯€c›¤é®Ê¸t$WÞ9?˜Dæ¹LÇ}Éšu£¾o‚H‰“Ø#ü'»}F8ã¿ç˜i&¬%Ë’F—½c†“ˆ†Ù9ܰ" Î>®p*ùÉ…ã¸ù‡#ðxß øýô<üQ¨ƒµ™ 8¼^ çl8HNìN`÷O*¢´S0 ;ç ×b;p©ƒ²†c QwÌóh¼ÔÓù;ÈÇ€þ„6øñz<Ò‡&§Cé½¢dH³ ƒ’mò¸dÃ9󟌘ˆéÛèæÒV¬¬j/±J„ïô–æÓ£—³mG¼@_AŸ¿S°ƒu+6ãÁb[xBÂoÃj¹,Æ[ÂÉðÃ0:3Ü„ÛÀo(]5 ´-y‡"~å ¼Àü­'ÆŠ£¦¡ _0êÐ ÷®‹bWñÑœ»t=y±îâÌçïa±A2Ée‰.àWÑ€3‡BI¤ª·x¥M?¶ÿ`úêóàÉÁø®a:}âɃeÛ1VE”6ÞŽ! ïRqöšÓö“¤õÀàk(íÇox}AÝ‚¹X—dEó çÓ’9R´õBœpGŠ9|ò©a[¬%é/ƒ:ÔnN‡ð¯í‚9ÂÐQ…s¶¹â~±ß¸»ü6ÎXO_Ýÿ&›µîütåWíÐ ã;Xb)8Û:˜Ó GÆ\$G—\$ý*G™Ç*¨ÖÑéõÚ=öl<‡¯×ŽáBÃ`ªû9⦈VVµ­¡ÅÏÃpÄõˆÐ^ÇF+^A‹Å °®»×!¼”ÙÌê,mØ&ïb¬~v^מ†ÜüvwÄPšóU~¯{7ÿŒºô¡òC€6«ÜýÔ‰Óä7”ÝU£"Íbè'²:xࣹC0tÇyÜ—3>Ì0Cq+H<äÈY=H|K€ ášpB5ž0LWßs¹¿–82õÛXùtÕSáxCÖ©ÛãÄñÚô½i4´=iŸQ8Kæ6‰ÚŸBrÒÀ—êÑiû¥qåš\¼z)Mð¤ÏŠn˜Â¤Ÿ öÕåk¸æš»ès³Úµ-aç0eh6®'W †óB¼€Æùî,çÃ|&îáJ6.8%¸:µ?’}Dà„•G0zæ9Š ™bÝép(ûNù üÿøqx0NfÖâ±E¼“ãKO£›w¦á´AºÌcelÔB6œÿEN4ÛùÅa´Ãç Þ” à>Ó~‚üú È Wƒö–µàùâ2tö|Ãw§¯À»…£iÔp”—¤.£áÏ‹:bÐ0œîJˆ‰E2(æ Γ%dA1A‹¾2ˆ8ªuk-x‰ Ârg_”?ƒ›'\…±O Påe› ßLŠ2îCR¡>nü™D«Â~ãgßÇadÎ zzÒkµåÔkSº¯^®¡‰‹n`Wø)º^e+lõŒÇŒ‹PäÑÃúz§ì~Ó$—‘ñ\hó ,e×#b˜³ãKÜ÷(¿Iµâ“ÓÀ¦y?X¼³áαélÈê2°ùIà9á îÇ®y‘xÈJ”ôC¯f+j«ð>iÿ ù%©Â·émLç,G©ßÃÈ…æ$ÿ˜­èSà7‚ß FýŽÆ{¾â¥ÞP³á4[ض>_ë kEÙ­"”¹½€‹t-ƒý7¹Bõ{x¡>]hyÅõNÈâÍ^yÚRü„¬Ÿò Ö:5 |Ú{vJý6N_w“)ü~+ùª/\{½_ÓÁxLA„΋Ãã/£Ú‚¯ÙÉýÏ*^ œß³».<¬ð -Ž)òžMwíL?úbä¶*è>!Ç;G …+t¿ ;=çâð cÔpTç¿öiÑœðù¸îw¤@þŽ7¦ªžC™7óø×ó½dÛÚ`pq&´Êlå…êä‹âŒß1–η„º…xÛh;TŒûµãQ÷ö &‘jÆ÷:üËé£òŒXAƒë"&1”öFÒ.{vè+|b+1E÷|±£a¯  á18òÕ/5Àu1l¦ÖRKaF·ãÿÏû(Î\Æf—d†g?ÝeÂ?'ajÕûYÑ:,døáîñóIØ{™ó=ƒ œ_À÷¹a¡k|”ù@Œ2¥gÖ¨$’À¬ÎÏœx¶†þ9µ$Xõ¢-xgÑ-Ý"º±óV$Q,w‚ÙOÜ`›}4ÛzVwÚM¤ÏjªX­Ü]œƒ²¬ä/#½úC¹Ov1‹%C¹ñÌ.›È©J_åZf4¹2㎠ÝO ΨÀ¶CõÂUbÊ=²<ú« ÚŒm„ø §2O’ŠŸCáò}ráÆNÔ´>ƒsÄ„Wbš!¬þD=ù5€ÿÑæ ÁÆÍ®{'ð²áBì»ìæ…Éd{¯?WÈ$_±«š:t×Ú&a=9ƒÝ÷]í§DÊ.ŸŽ—åÃb%ùE=’þëÙ¤ö”œù§sùE¨í~¯ñgq%›è&ËÙŠÑ óÎàü4qö§ÑYÄÃÿÙØóµD…ÔÂvì+1°×ã¼» 0(˜ýYúÏÆß¬'&ÚIö+ÌÏ`ïɵ .²Œ΢\|ylW6¤g3,—eEú°+Aõ¯±[OÕ¨ËònغôØV†œUbüV'±}6ð#éÿÁ}•øÃÔÃp å#©ÔËÃ\ç±}ø½L0?ËŽï/ZAÇ)R>}ùeøRùd·\oÍËÄwðêR ¾¤3N_뺬>2ê•1O ©eSáo| =µÀãU9X­^“'+VV©/ÿ·ÉêÔ¯»ƒüó“ÊžŒw–ˆËŒ]‰öØ6飰âÖ|¶¨”òA«ÓüºIt›p2'ÆyáC‹åaÒEWºQYššˆró£Ñ®Æœ•·ÓGÓþóó^|ú6!ÄhT±D}Cœ7c=ù,ó¿÷ßÜÜŠIWzJLŠN {‡oE†aßÛytÖ\c¦c5‡÷£‡$²érÙ<¸øO[±Ž±ÖÔðI Kq¡çNÀü^äCKñir3|ÜèD­ w8#É;¼¯Â/ñpTž) -u¸óŽT@¬/¿ŠCÓW ÝmyÒøð4&®ÊÀ|%Sª·û½Pvüxxð~>8N›ÉýŸa™ÿè¸?ŠÚi]Ø5$|—å’CV)ùNóõÐߨÂ΋Ó!2‰|ò:5îó7¤×d‘èõ2ü‚K0±T &‡cäøÙUè‘™ b씽aý¦ÿ¯þI‘;×^b'­wЧ¶Þ,jA06ñlHH¶ƒgMÓø®ñÜJ«cŽgû¶Ú±ñþôÅáT<ÅßZ£iû:²zPô}ѧXèÈ †¯,Pq¯=Ÿ!_‘ŽsF—ÕYÂè[pòá;aOÄïò²tã¤ÑÔ­íŸ] b±Qºní“àçžÞC|AµÞY±ý×üaáUu~peftˆÐ¼BMÚpÇššvTJ˜l¤ sfÑ¿YQ$é¶ÕÕœ‡ ­©ÍÅó8äA‰·úŠæ—ÓqÉÛ9äa%¶êÝêUåÅg‰˜q‹r~Ù½éß»ýƒ¼˜| öŸÀ'C® Úïãé>ne´¼Ê|ù‘\nSsÇëQd5ðzjK)¼ÜüÌöŒ¥] !þŽ(MüZÍMf|ÃËÐíöÏM]þ¿ší—ÆhàÆ4¨ŽLE¡†'Ÿ&ŒÂ(K%®·/Kÿaû:ó‡‡-hFå=´;Y‹¶½¼È³ÅÝ^œÂfˆz[#ŸÁó<#tË}/üõ/ÿX®~ƒØ³…IEäÞe*èbr&ŽíX³Š–Š|bAŒ–‚œƒ ±h„'5ÑP6µ‡©ë¤¡e2åŠnèÿ±ÌS»ã´½¸€fÕÖÜ"d ßp÷)3›…ƒcqO±#¾ß1‚ÎŽx.vxÍF‰[ áwüâ’ÓBÁ,ߘžŸ&‚§åmiìÒdöË¥‹$ô_…u+øªµK1hìI¾>JžŸ9æ6 Â]a”>¿>L×hóYVl¢šã10ö8Ùª8—›ÈêòF© AµÍjz"ãd_;¼*†¾±÷£}GºØ—AÜ:r*½qI íÎoåöùT¸4•‹‹Uð ¸|`ýôÚrŠ÷£pÑZ=Ò„®™ð“ øÁTðü¤½v?| þÇ/=7GYâðí!àeñ–»Mä2Ý—Ñ]¹Ö¶\•…@7ÎæF³B¸uûpºDCò†ž‚ŽêT°Ïà£Áàñ=#®§±âôò öþ²8W–‰ó{Ó&Ÿidë·‹˜hªDŽÔcÃV#|¸Y‚Q8Ùtˆ½Ø; ûZ³ÎâXvÚ;7LY32ly~ÄxtÒÚ‰÷XðÝç¼H«¤)Ÿ³|!.î€sgnà¶³Vp#Ï Z®¿ÁgNÐ ÆÆÑfåyðÚPV±±äÕÎMØ~§˜¼n9Í–©…¡+‡1Ž5öi¡&ö¿èƒ9}j+Ü +BUyúE`Tî_rŽ[yƒ­](a#í‰wšð k Jh%ïôÔèÆÇ„Úaÿ×CèðÇýðj"wÏÌ ®Cw”-àmÙôÒ‡ø(âÎóŸM⟃’ɃÜ+x©WUãEé³=u°m¦óšÍé[OI.ýé(¿Såwô©ßÅŸ¢ÁUkŸàÈ~5(ßÿ’4GŠÒN‚®âvŽìî«c¸X ÀºÍe aÃáæ¶PÜ·è8¬³iÃCqìŸÞŸÔNâ|ØI>ÛOG‰aß[ÿ`q¡KåËô=ñ¯ÿÚöÈ«V?‹¥T1ÒÞ}T‡|ÅUŠ.v8Ë?ÉS9 mVžÜÍäõ*`Ô&BO;IR÷­O`øúåâA2ñ° –<¸ÓµÖbKÀª r-^ü Gïv¡ÛÐN|ø&¥ßÍCn›à쾼ģÿßOæS·sX¹]‹¨†âú °M\Š.Ux óã¡~XƆ™Ð¦ø!ôšý|¾lÐ.ºC¡ ú³’a7N¤;ƒ¥iRé~Þ{L†~yé+øc[˜ëw®˜EÕšôxÇû e£ ±¯ å.=…ëwä轞”üoDÆÁÍÒ•Ì)|Ÿ-ˆ~ïðƒÂRTØ7 î%‚ù&|Aê5Ìœ’§=J±éµ8ûÁ†XÄí§Ëhßh&˜c,€™—$aøA[*{9_ßV ‡.o…KJjtÏ Y¸Q1†8<І) ÷³»ªé,nÇ9(x+‡¥¿Ö $ÿŒÅÀ™Ê¸$ÁüçUÂgk(<ˆ±æËÅ^áæMÃáU°g·ð@º';*cˆ?[±mG®0Øé'ëOJ ¾ÇQnôaâCªñFe5äe<ÂßMrüûØOö{X?ìižLŽhbý³åiÓíl|wGu‡ã)##4l³b/hÙu 4"ÙÙò{lØŸl žðVÌðӣ͋×À½×PºpÝl¹†¥Ï ¸¬ú òŽh´upþÉÅôIuP²$]„1¿S@Ën)6ú$ ‹œÅµ7_!mFc§ä'X.Dr_JÓ~ñpöÜcÜ£-Áã gøû¸[* ÿ¼|bÿR%>o©kסüÕó%|Îçïà½Û^•|ÞÇÓpÍ`Ƣα¿.ÂÎ"fó÷VTIÐ]r"ÔêÛr¬ÈI¡%+CAþ‹] û w÷œÄïÍãòßÚ‹ÉÙ³ÇÙ«ðg óYŠ.Ø·€ž{™ý…ÑÜ?7-·–™E¹clé6/\øòo‹¬lèµâOäþÿ2,›ßŒb?…Íxé«1úÄí#ª™\œ.¶ÇD¡J/Æþãé‰ô’øJò÷²:¢ýÿøþY—(l,9 »vØð‘›þ21úã´¯`[Üdþ-“®wÓ葽Ôîù³æ­à5è‚ßÓCð(çŸ  F´ðž>ÞHg…_ÀAy,$fNãGcCQuK"wkÐ «n £{Náë™0ÑYj þÝ€œÓ1а±õë6ÑPƒ«(5#g+¦áóU¾·ÈŽÇ™W²™ WÑðïLª¹N‚¶CŸt×64äg¼ïc–›4Õìð¢ß×:P@Sš<—¾|srû@ü| Î(XŽÕË*a‘ß6,Ù÷ 8.áAEÀ\·§Ëú1kÙ0î“•[ù¸O™ŽnûÖ üÌV7¨“ZÀ/¿"$nl£ðô-e\;ÁW]Iç'Õù‹{ó¨x‘ æ´.9V˜ãJ&ªæÑ™5Gàq!™oäKƒÚ}è´±à¢?&ú …ƒ¦©üKŽÍ€ü‡Çß„k—'àw)E(©ç˜"h€—ìéz6‡W п4‰õÅŸèycmI,žÅþ;>öCOÔ >Ôw<¤ü <˜õ®ûÎþÍßÇÕ$¢¨¯×ñÁ W­è i;,/ÍÆÃ‘rü@Í-zAϹ&zÎA÷;E ¿k:| N†é›±ñ•*¤ÛJðÒï¶d¨‡(ûUÿ»7Í e~o zk TͰ¥6Vâ׫l†qìñz‚SäÂ!5ÙŽ^qŠ|*ÂÍb3ÑÉb$yõfYhý™Lâv¨Xüö<Áüé¯þͳIðÿñÿ5ýÄlíegÃѶNNýŠ.'ÒÿakW9ôí`­m•‚˜²0üþ¥¯ÙEW£ë?^íÞYB\,ãHuø]ª€áSãÐ^E$³ÍèÃÓ¢ðé©x{ùqC¯­W¦eV€C²9=ï኷/û£ÉCN÷G‚ûå[xW£GXºZ ®¶*™ÆØŒ·‹1¹Rÿ_ÞóßZ ÁV}&Âm›R6~â};ñÜQÞƒ{¡¡ ÚU$yÞW5ž.jŒ×fJ †8ËM;z^?\x¸ÛžÌ;{ Å@ú,Ë„pç‡ ›šVƒüñìhEsÃÃd›â%L–~¹5GS#5)ûž'šÛ‡;pÁŽ:´Ì ã'4Ãpç,ø«ö‘Ø«´±¸ÎÌfËiï|_0‡ãøÔ0Û:Ðuϯ_äªq7=YœÖž6 ÿ Ή»KàA´#zFÞ®”վ޹£gÓ½Â8½þŒ):D¼®ÀØ+¿áb­˜ÔÙÐòäêߘϫêLéåÿGÔ—‡Sý¼ïÛ—‘¢ì»¤¬)•3Ï •JE–´ï´j!Zí²ï%$k(!¥Rœy†(Z´¯JÚ7mˆöŸ÷÷º~>¸®sóz½fî3ϦJ'YÉQŸÏÏa-gŒ a­³ .2é¨n,YMOY]¤K"ÔøEmKºHn/;úðcˆI„Û ’“äÝs'› IØöšàù³ƒù|×åt[ín:¨´ >f]ƒ=ŽdÇ19T¿sνìëƧ`ùhž¾‹ÍÆÖªxûâŠýµ]Å®“ËpRZ«Üâ Ç /¢úÏ› iv8ìõ‘þ¡ôå`uõ¬ƒQéêt«î]¦ébË–64CMâ7"TÒ¢;öbööÃ¥Ÿ…p%è0s3ØLO å¾a0E¾ 3‡ó*©3°jùœ´ãŠ€^¶ã™ Ã!bÕ¼©‘L:ïæá‹ò›ÕøM ¾jO ^]º:^kÓS¡ðh‘./”—¡®ïFÑ}õѸk§LJ³„²ÛõØ"ˆ£úwÆ‚jœ¿¼i#JÖN‚PÉpX ­Ê]˜]û4†ŽŠ§:ZjpÜàÿÿòAÑLb«ð_¡•ÖL†±O7 â7r}ÉhÜqâ3ü\ãK·ö¯ƒç’íŠòtõË/P³¢ˆL]|˜þÚ’ sð‹Æ=û*æ:†º1Ûk%øV(BF²ƒ´¤k-7ß5XpF6TGe¡ê^;˜öbO €ñŸÇBÞ>3n»r.‰TxoÓîÁÆ WEÒ°oÎ=âxsh¹â…s£ÑùìL Z%… ¼g“ý0nónx¤â]Lñ`4²;¦EC<<ÀÜtàÛ±yPp±u†óÈËùñôÕ SzóñvF+Q3Íf.òƒ¹ˆªt¥+Оi=¨Pö‡ywD3OâLºw6¢AäYŒ9~ˆ}{ï<Ò iC¹ZÍ]œÔ¼t6;Ò_ÖA¼ÿÁãíðJë84ŸéWAÎQì?/3§Î|Us úo€µ‘¾(ž£ÏÏ© EO»o`ÓZN\O+àìíûg¥¡X†4U»0vmžŽš2ÎX1ÒÿóQ¯uÖ£ó•CZÕ…T‡ÛE^K%?ÿ¡ã`ßW :%T*ÍO»íÁE7äòߺ½OA$¿ly‚>šWÉ3&ñâÅ|vƒ*P:†ç=—µGǹ\Ú,”ÅìÒÆ‹½gÉkÅó`é®Î=¯àžN¤S+aùÜ*x£U!ªšFŸöëœá7áóöoì,î‡ç~Êö‚ïzpY4Fæèñ7óåpÕ¢‘tÈ“áÔHÕnŠøÊš‚–ðD?]šgõ™=+vÁOózÙ¾1ñÒÓ‹LOä(MVÑÂU•à2m6$ªÑÖºC0¼J¬¾h°÷5åP:MÆM¨R<‚M¾Q¥Z†zéFB¥±ýêW ± ÓF‚¼Ñ2ÈòE²x/'+ìÐÀ‹òÆi·Xݹ8¨X{ 1)b(³Ùó¼è…¼á  <<°ÓÌìyÙHEzÂd(€øpµ¬bÛ>ïa¦ÃSÎ [^aFíÌ.2§•ÁÐ3r ]»®;X[B-TìsBçß–°J#JÛ¬'‚Å‚mçÁëïQX=á Ó·¦ ã†ð¢1š<Ñ%gÝ#-üõê  OaoÊÕý¯Ÿlyö ¸:¾fþ|cîÇÏ%»éÓ(H¿eHuÛ }pÜœþKÍ〠ŒŒ)Ç+i>p{‘õ¶‚î—‡ð“IÙD‡»ÑÉ£Á~ä,Œ¹¢š¹ÄÞ¥¶*ÒÇuk놘@mÅs?ªa¡ÑÌ›es}—Aªzœ”ÒÀ+téÓ®8 Ç•©rä$aÀÊ üíßãо'T¬—°)òé¨:å“ÈrƒîŠ l¾p5=z0oãrp~;/ÿ<í®Â×é²TÔ»Äϰ½¹ÐõÀ›-Ž‘à›^gÁÏ x@úû{a—‹‹ðM î?ðŒýДÀ<ÓHÐQ–çu%ñl¡(Ÿ_,]·ÉKŽÿT¬Æö .мà:îíÑ]þNç à¿þ2ŒØ?%°[ñ ZN…8Ö›îÍ«ÁJ»Âã³g@‡4?òºˆ\kŽ¥WµÖâ—kß xI(2åK,xì#psÉS£Ou2ýíÕtwOñ{Ðí§ÜØWÊÝ Šœå†o­PÌÆ}˃!×gñsRC)Mñ!•Í'qâxXí¢M×d~ǾxWP°­gS²Œpßã…Ðí¾ ×)Á«¡­ðÁÜš‚QF‡yÊÝr¡¿‡sè)•¤5ûqE§ùôä'Ëqo³šW´±´=¢Ô/É f¾`ñÜZtÿ´ÜßHcZMòÿ‘ÍAh}{ {ê»M|Zb¾Y£ÆM}øá—|][F*&§íFÇaÜß­—…Ø|~w(-½ç‹U°–îøáLÕR u2ÞðsI teL¡–ûÉÉ?5æ,Ï c&ði Ls€Äñš‚)÷俽տk§üË‚_AÙlD¿¦JµÌ­'c²ôOè¢Þô¡lêhª ¡ãaÐÓ<9f]ŒC³}:ÿç“~U}ƒŒÙ,@ƒ¨ á/a ?}Ÿ µóÀªÕéøÍo0që¾ 'F  ¶l߈3*{‘JY²Ý[Téy™7ÜŒéþyüËwQ¶]NÛ]p0ÂÜç¬ &ƒ©øäƒ¾²î‹Ñ «Â×6i¹´°)fø¹ åÑÅztæ‘ÍÌʼª¾EæÐÌ Îß·\gVEFôĭǸX< 5)ó  Gé‘Òû©feŸ'¤Ñò¾ O=ªA¯WaÁ»Ó°cf¦ ç½4ýb~¯I¿>ŒÞ‹MC²d5?ëNʦïCÏ!/XÝò'Ì*{¼;x nD‘T5rxÂIÔ3/-Œ'ÊËdy†i83/jeÊÆ0òy+\ÿj Éùwq”q&Ú¤„"Óæ³—¯§BÔ Yþ2ó/ »Kç¢O?ß–ËÖžŸV×vë3n7GΤOg‘ ø¢{5Ûæ3z ¯ŸM Dm÷oèݦèãëo“áÁF1ÔP(ù<ŠÎþF¬ö§‚ª{,(‹‹PëÒ¸Òp>J¦Ô±´¾/좽Ÿµ}ýl(I*Òdùˆ”ƒp+pœð¨_H…ߺ(fiÈ?½²àÇ¿À“a 0Gf&&ì4Áß‹·cu¥üþ¯_®±ØÊÅìtxŒ;"PrÍÃ?;r0ßæ»¯æžª{ñëfmÚ¥¼•l­Ñ¡Kç>ÀÉÍh|Æsöד‚9¹àf CS }pHS4|ÿ5¼w‡Îåà?K^\;ÍÌþƒÌ+-zNÆ—¶åEÅøBö©×¾;£u™“ðÃã™tð˜!ô×^Y0…ƒ`~2•¹˜@1uÆÿúwÏç=оT°ogg–ÂÍÝ88´›]'ï®rh4”]ÌB½×]BËÉC`Ö¾<Ìý±‹û÷§ß ƒm5¸ýI#³ÓùÆãOàþyôÿñ¿yAÉv©:œ°%†ÄN¿Ò¢çûÛÕe2ª@.>™Éï½À>vŒ`²ßtààK[n¶‚/[¥Æ] õ©ÄÑ1!t¼õ$¬ø¾G·÷ÁipáÓ·@ÃaOüûß9þÏØRÝݨÂW:õÇDàyl©*…Ïí›!Xá¨Y<Å!…sq£ýH>5Y—†÷kOcZÐ9w‚½ÔµÃñÞ­(æçM'í­AYÛ‹°kÊL_=* éDê‹þçSpûìv&É–Ã=ãóp›æ¢¾b1xmc¦â铨yõu 7ºFùôÇ/pý;Ýùÿò“"É:ÈVÔ|DzÜ™n†µ–1äbÅhlE·q”þú9ƒ’dé‰ÍÚ<5ù4S° ÁÂíïYëçx|UàˆMV_ñµW/‰ 0äWÓŽBø²×¯.7=‚ÖòDÅ‹•Âmwž ~O™Ù6üE}r]ëõxüx.2ÔÄA´á7È­‰Õ0udÉ”¶a' l¡r÷mp‚ËCù /ÒŸÀfƒ4T%ê,AG›tä¸OƸž?„xDÃA:vE˜^¡ ëw–áì41â¶~.4V¬…0Oð½.ûŸ™ôj%ÀØM«Éž…ÖP ßkçös?†DÙ£½wžþ„’ß ±'k#[þÛ Û²)^›Æ,KDÙ¸C©Q “ëÇóۥħã H=ú%”qÍ«S„‡¸'­™œ~s…⟠Ó_W&@šŒµ0ž&~óñÏÑSWöØDdÿ<ƒ'É£q¹r+›w¥„<<`jË@íE0H[ˆ!ñi ËÑÚên]¨Ê¥öŠü›¸£ò0|Úk:€ßÕr&>»TLú.€üŽ$¾kÝA<ë J®¼ÆŒ¦ ŒÓg²õµ7ve÷±Â÷^èãPJc5 þ&®#n%'q齸LIšßžÏ'Î)de#CÈŸIvc^_A×£g±ÜÁ ¿ +¾gŇMQ_]YHt¥ñú»1vÖëd¨÷ç`òOàŒ¿OªÓUðò Q(Xt׎[Äú¯µ<1Ø*}_ÆáÍoñ‡XnZU¹^iÉBg_špQ…®”ŸM oZÓhɉp¹ð<Œ# ùôƒ¡üw>îS}õ4»á— à†ó@üO3"kcZIð®pýse¬ñB0.ÿ„«†LæÏÞ†¤¯kp½t\£ÏÈ›ù? ÿ:d8üÒ^‰­>¡8u¼9Ýÿîl½*JíKñ5VoÈ¿]øì˜”ŸaÅ C¹±K&º¥&ÁÑõ’|áò3X&/O¯Iû1‰ãÑtº (EíðÍìkP+îùiÚriÄæZ'Û{sFàÓZêÃ,©~×Qa¨E$K°nò:eƒ Ÿ0W̺ç¢'M‰x¼’¥?©ÂýE{áû0}6jZ>Kµ½ÈêŸNâÁ-…B‘>òzÉc¦¸½aÀÿ½"7œÝýÜÁþmÜf£Â¹Åk„^ß–Á¡´ið®r=^%$c3‰ÞëãXs½§"«š{ÓukIˆÑšµGpõ‘°«~ ³ê è7 5.â>%SK óôÍ¡+7ZA´ßeÔy$¬°±FÆÏ±%™&xòÔ6ò9Z²Ç„—­Ï‘ ½ —ö[ÀÖñËqyÏR³Z°,¿õ¾gzkYÐõ±°"a·ôÎ$¦BRî™Å†wç¡íÌè”­{ñ €8J\Ǭ;‹ñî?=ªûÎŒ›)&bpësœzL âZ¥hàR…þëîÕHRo,gŸk‚ ×K ­þ¡ŸQ'D¶4ÃAÛÛp*G‹ £ÀÞs |–‘¦×ÍÇrÁ§CcÖ+Ø9¤Eøa‘$Ÿóø!®‰œ™íjÌîµKËRb/ßÅ÷Ñï±ë'xž¨%ã☾Q<éÿ s^½ÿ-+´ÐËo5nkèÓƒ È`ÆÙpk™8rmñrð¸÷ f<‹„yþJøÌÀn?ý†©÷¤ù1­ý÷#v|l`±bJ$MçXOmd™ê|¯H.î^wœ\*~Ž µßÙ®;kùzHþ;zê,ùíê8NXÿWsö¤ç¯çÖ ¸ª9Ý/ç'ý‚É7u*ih…ãÝçP4#|a½^3ƒ¦¾Ûra†• µõpƒ7ë ¿ÿ{hêÑ'þŒäÚÈ œ¿lz¯¹¤Ã¢œ±ä È¾þ¼*G 2õȽ‡ð†t,d]T§ófªÑ§v\ò„‡hoH›õ ½^.GÍ9¯ðÊÚÅl—tyþÄ ¥…=PvÉOœ9Ía$]¿Çt#j#pÚ ìÍ::£å©`›U^z—$½*‡7ÛcHoy(›[ú_„uÊ8‚êàÿY¥ƒ×z„¹»&Á›SÊÂà ª¼&é™PûðÃ)O¦[b®×èóØÂÇßbÞ¶ŸáêÆ8Ø–¬Dswgs„Âèé_„VÙkØtó¯ÂgÝYXm®‹/³Íé³Á28|ñT|´jž¦ óff«ñaX(5i:€ìhÌR†?À¦ fcñ’åF'".§ˆº¤+ÍqœB¥­ èÄBœ½kö¦(ð7ï3±Ûaì?–çÛ‡CwóaH×?WÅC•ILÔ’¡Ó+YõHö`¼!e@.õhòÒˆaô­¸ Äß¾F,mÂÑ ìö@þ_ÙƒÂ'±¨U°zß%¶Då9 Ùà2†ºhX'4n; òÙ{»‡ÐdvÇ–¼À“ºûàüïk¨T0•n:× –ÞïHÊZMúÍáý³H9QV÷Ðn½<ïz߯$=0C¬M°v¶ ÿU΋’ ïm=¿)À›—… Ö¯wýYË|s™¾ýzö¦:÷è3ƒ* –£ø…<=ðïºøÊq:Nþˆr[mè™é…I÷ðßL7A£ª!qS°ƒúÒ›L§pˆM›G?o’äË›%àå“qùßîßg“N¼†Uã>ŸèiSÈL}:`oì'¤L‘GTä‚wÌÏ~.ÒÌÍëFÐWãpÂÈ?ÿy”iü GzS&‡Z©ÙðÄ»« £ò;Ìyº|ÚÔñÞ?oP/ÔãAw\ø imj}a1i(•¥;wÅòÈä¹ óʉG=¼Bº".â© ä~Œ%Ôb#08°”8 aKßS6n®Å¿Éà 'úüòÿy°ç{~ St;€E¤Ñ¦O¡D)~ jw£_íàgq]‘!wó¿@~ £QæfP»ë¤WN£CåÊ™Hi2¸ÞkcãŒN ðߦw%dŇlì–¤Ü3GkÙò=°Ag8<ÈÇswŸâ™èžíˆÂÆ´}® ¯Þàˆ×]ƒ==c™·RÕDäûVà—Upäp-Ñëƒ ‘#pÍM  èû~-þ–ަž›M؉Úe0m"`Ú0Ýþ DC!ò¶ú»ŒâCh{Œ–°p×IáÆå qSÕ~7®U€s-á`º8‘-:¡ÇKRG±­\œüa‹Zã'U5Lš3‘K?Ã@8 °™v%wHó$‡@z?ó"ž,•äߌ¬)ê^Çó:m(»éÏ@þ7εúýèÂøO:×%Ñëºé”BBW¨büã›ÄùþlpüuDL#äœ&ïrÌ%u³æÑ¤9ú|í²‘ð&áÉI3@ýÿó0ÿIßJ,•ôøÉÇ+©ënlõ¯ÁK[n“Èqô©œ&Ý÷~±Ù9NVægãb@„܆ug€ãEà{h;l(”ú“ º ¹®S?¿h⊑Ü52XG=0MÓeùÑñh,Õ‹ÖnËøÎŽ,x²Æ˜YÚ¡ÁÞR’t~"¿¶wŒ¼ösÞcÈî׌˜?Œ5–äç+àŸ {›h=Õ#%a‹è¡µciÙ”3(1ƈ®úLÙÎï‚Ê3€¾ýúóNšMÅ&_Æ¢-:qøU¦oÝ•æŽñ[«ý±ák¶ ö~8ôœ?³6çs¯çYÂç üq"üò™Íÿ)T“1aÛ-,K¹K>ž6ÃÜÜAܰú7íÝA¦ú+£óù8ã†u{–µƒëÈô½€kîßCùáièúÑ”kC-~ …‘ºÁ<$p9}ø5ƒ¿znͯn‘oKKÔšIB_låyYÀv×’H½@¾ñ…ýzÚ'æ*áAÝ%ó_ùýDÌY4ÐvÌZÝÁ Û©ÆÕLªRš(,Á_øØn /ÒU£$Y“/£98yÙe²ioŸÝ£æ!\ÿØ_ yÃø¢íÉ|Œ³õþ §–úStU¦áñ´Û¢>‡iÒð*Ÿøö+Ø,¤¯ÍÀò…!½ók"QÛ2„|zUB¼dxBä*¡Õs3ýD6ò ‰z·òF}÷ÞKL‚)âp´m„8ò Qù°%>B\Oâ”ÎyüsŠ8ð1«‹;B§”Gù¥ Åiƒ+'`¸þlþ\I–Îû(K+xÚÀøWNé!b«‡‚é‚háWÆÔzêÉî®+¤Z}×UÑç[Oñ‹†¸JV—ªILàž&§Øµ‡y s®npÄùO† «qÊ 9HþûFÀê;2‰÷ëgÍ¡Ë@Ý2w=ÕÃÒÔ0H›2åÛÇб=(±O‹Êµ„‘‘÷®ÂËg/Ÿ²÷Ñ@üÌ4Ú{3è,œ¾ ·²›¤ BM1&ù"š‚ k58…?”\ÎN9 |ã`–ýTÌŽ;‚+ï)ò?‚ƒpþèP¸4²ÿyЏv¬˜«®²:ìyI'xì À?y†<˾Õ&¼]†%tòŒöqœýâ~9€—Üɵ1‡ðµº6ú{¶'‚á¦,Á‚Tq~hjbýø‡Âfé웹þ•È"ßýY†Ûqôëk‚[§<`;.‡ý¾ûÁô>²iO2ë™]Ž‚Ök07N‹ ç\ؼ(r'!Ô»âom7X´¹™Yº^„¢¹?Y›ž…Ÿ",öÏ ÐsÀ á_`¼ÖgÜùh þçVà}Âò &ÐÒG&n¸û¿<†žÃE‚ÐÏ_ɹʳÂ&…ÁtÈX ÞålA[#3Áet2¹Wó¿úOqÇ’Á“ÃYBB{RZO±}ß-öÍ*šº`ßå>bÕ%Å_}a3—8PX.Ž£Ó[gÈ ¸‹÷ز¿î|ÚÈ­äØ'}áÜ}?’–jPχûáRàm¦n|›. QdÎ9P^)â;†©åÓPâ`(4?ú›æ%‹â›$³1µ@–/þ ÂšŸzÓv•³P´IÓœ¾°¦Ép ºŽÎxªÌ¦9ƒ—`ÕU_úqô\þ©Óœ›\– ;§ºÒ”å?„ú±ß¡uÆ:\|6|œÅ=®jã ¹!tö‡J¼iq4ßœivçXC£‰5žï[¯G³Hް¬ÌŠªWÛP?}Q~ ÕË)ãýr âLünN YÎV“É«Òp>_y i¸³–Z×¼"R¬¨^;{lЬ&£Íí«4ég‡3ÿõ…ÝNuè]_À µjÍ/ÇrCõñ¿°u¶5Ö1¿UÁoæW"²1»:òëWÓ*ûLáùeNDÐ8¬_×¹ÑÛ{FÙ™§q|p[¦?˜ª»˜ò´oçð?/òæ³Ñ ¢Y†k–‰Ó°H<øn ´¨œÀ•"àU­Ï„zàЄ0~éA|t–+½­ ÚžMèaÍ¥FþöÜD<Å›fOØ×–8|=Ɔ´h&KÇb©H$|XeŽv—C¨ÈZI|2|émÀÄ+–\‘Ï€G[¶€CO*‰ß¾Z/IGg?À?o½pŽ«!Ý-i¦»aŵ7 í~§[Äaaðþ`—"11—:ÑÆ/1ùM^]Á!3˜Y N†­á§$¹Q"0*=NæweÒð]ÈXÏKQqX­ðïÂytê±õÜXˆ™T£® O'tâðì*p]ry²ë’{q«‰889òù·j>~‰6AüûÓ1Ôg֮ܵrG¹aÓÁ}pT÷Œ;Û¶³µ©Öãpà_Î÷ʴï·(m—â¢á1dè ðÑ >2Èz]szewñ”ö´`,6Eæ=ö=V)„ôk¿°{v­¥•ˆvþ_òc­+tc<é¬dÞ7a`ý3ÿ¯ÞÅgÐ&Áß]\!6™o)<iMS…ŒóA9x:f õ:#Dm‡§‚G"ç‰Ãª/ØÕåÄb2gAïڇȇÝÈNá£?c¶ÚIzÑû,l¿+Â7SîпŽöÆó6Ã)¼sâuH´î€žèšx›ÎGIiI>[Ý< òQíF;jWf‚\î0zäÎ]&þyÎZ¿ó/.‚57aIá:P\¨ÃmäEhé»F”ÙTÖ+œqÑHiú[Èp¢Í\øã/:ÏK£s¶@ÊÝ!xnì;üݼ&Ï ¾Q­þ#¨“X7~w¶í Ù°ñÒ´ˆV£w'žf9iBåãPìÀ@°ã<3í\‚Mˆò£«0Î{2Þ6‚ëÅWHƒ‚°ËÀêç¹lvµðô„xr ±‚ý#š úUçоCêTèoËÑtÌ“ãðÜXº~RKÎ~°šj‚4?EfÖ‡ó¿&ÍluÇ >Î<•ªûÍ¡˱Í,ªŸ¥—Êèˇ¹f°-Ûz‚íÅ–Éa)[Ï£µÎ÷Ñãß>>¿Æ–Ÿi.Ãyz183?˜NqÓ©üc±)qz³‰ÿsïÄùâ„¥s¸žb- ÎX ÃF&ÀžôKXq8qJaÀ~|ëfœÿ”­®]óÏòÇ›Ïalþ9:ÿP54}ágFNà§4²ÙúyÔÉ'8¨ó6ÚüKÔ/³û‡òÆ¿`§%fGf죪·³¹òûñàO»ÿ{Sy®Èd!e”ÖkÝ‚¡…– ±4ƒŽ`W0²~ˆ k€M+¨â7Y¾mä t²õ£Î2Ù‚ï!<ÿ[5\y9‹n™™ôŽåzÆ¿ ú• št€MsXBõ680_šoèvbA!ðgdimÏç.ߌåÆo§£ÞâáÀǘoîGw«ÚƒJùrzm‚:Oh+Çþ>ÂýUÃ`û“|žÅ[¬/Î…»1&øÉj7¾‰ZÀG4 XÃ%áGµ_"èG)*!âI÷Á{+g኎îü-Þ‘Ë=‰W6€…8¾÷rÑã1ØZ8_ð;æ þYÅ/åÊÐÅ~¼Ôo_±‡déÃÏÆtIÑhAØ­ $ÿˆ--P~}‰ÊtË÷&˜~“Õz„Abø^˜b=¾Y3·ànæ²f$½wå<È/ï†E»• áöBØÿ)Š9=ßLÏ…©ÓWR…€ hí(Yê¿UŒ«¬’ÄÕ‚ðzM÷”¤¯ï:9»Bp y9ø^RÂüðýø×þØv2˜%XŒþçÔÐIe¦Ç¯Å‹ƒzú~€9k·£hÇ

        ê2[éà çb•`]…8=Ùr–L^ìA}Ô&Òg7BQÏ1êžAÉM8ZCO&ù’×ùfÐV½^ܼf—¬Ñ©KŽãÆûMä˜ø¼-mH##MQÎq)ä˜gq«åðÛèº`ñaR¡Ú ñ yE}†ßé Kšq…ÞìÚÀu´²È°%Z¼`Ð|2-¦–Î%o Û!|ês|9: tEÀ!# ªÞä ŒÿCywÔ;膇L'pÜísó©%P6o*˜{=ÅA¶“iõJ (ˆÃy"ÓÉÑ+Y8:"ZíÁ›Ëámº ÿ˜bŒ?ÇKÓ=Y'ñ¯Þp˜uü4ó›´›˜þÜÁ䛞ŸùHXewˆŒqËe½%ä±Ëchïrž¿ý/âªù VçMUOjs?å¡*³_äãÿ>ˆñÝŠÛ¸EÙX:f‚Ê>mPŠ0ã‹gœÿ¯þ4Û<œ&¼ë€ùj[ñë¾7b*ü]§ÂVjÙ‚¬8}]øî²§0ñÃ|ž·¤‘­Y»^þÉûÇŒ¿Í¥ på˜~¸z•Ö9 -ÎÕëÝ ..â\#÷0%·À®0$·k¡L¬ÿ{”³åZñ³ëNââã¡Âٕͨp¢§¦ ÿdë±|ÅuÂ×<Á abpo ¥}bè>mßKÓz3øt_†WCIåÀgv1δ½KYÊiNè7h­¢?KGáíFÖ6Œ`™¿&œhl„°ú٨暌g[Ͱï@ÿ|b²c6Â÷;jDsN${ú±‹ÝÝ}z–‚#óÀêðãI*¼ÞPÄþ«U=ëë"ø11¶œ—¡óþÑñÏOhfÇ’¤¦S§îŸhzU®±¤æÉ(;y™­K_‹gþÔ Òuؤ˜ŠfsÞ [×þ•MÉàY•Æú—„ )ƒˆÖ!+\?]Ÿz³§y½Ìݲlþ¬LË‚\005™ž½?c¶©ÒĦ›¸)å7xÏÍ%ê¿ôТÕ{Á¸©Aôæ±sø_éÞÉidÎj5L§»µw3±Vq9=¶‘+Oú 9UjPó’0cû÷ÌCÔõ»zPe“"ZåV3;ßµøÌ? é0ujøò¹˜Âþi}0òÚn$1 >,—XÿDëçÚmró&ß#Ïpû?gpoq3.(ØŽ{·n€gòbó‚©™´­Ys îKÐ!%ç€*ÉÓQåG±ÐU‹?„RÒ‚§°Á‡eùÖu~ü”V()nà5b’´E¡ Ä çj¿Â¦Ûé𾜛 ám‹RáH¢5VßyÀ/˜‹Ð»‡/BFåRp)Mƒß¸ƒß©0çú%¼üx/’Öɨf¹Ì;oLùÓT}sð¡Ç1zÝ+ð¢wøÐI+¸‰Å 1z.3Œƒ[o¯Cî˃0þgJELFÑ$[fwû>x^Ë'ö©¿Ö¿ŠEH} N#|*é«l~uú,d¿ÿbÖÝпl Ïæ(pÓKîô~´%÷,ò€¾éC©E¸*0R„fm(±çO_1Ø"LA‹% ²O/Õ{Ž©„ð¸9“Âöã÷.eô¶Ÿy ŽæBÒªÐ9$)g…ãðÌõš^À·ÆãèÄgËè“;»ðŠa*?,TÄ«,©# ÄÄ(UªÛ‡î$=s×Ó‚=°¯<Œ[ýË~ZæGïžš+(V¶‡ŠÎg𷸔¯èa‡{ ®6wÂKË~N’ EßÔzÐ]©yµŠfSâ¿ga")dAÚÞüwCëJ®þæ û°A¤ë6ÃFl6ÈÑ/×àlÃä´ë3a©²3Áâæ-›ßpÔ1Ü'ѶØZ~ &:äL9ñc5¼˜3^û•À­1»ð~àVܦØ-oÂ%Å8hoÑÄé7 0å@%~“tFÌbP3ŸÊÿÜ §Ÿ±;Ý̾¿}J¾0ÂÍZ Ðà¢Â‹¨åò@Á‘dþÝ 9½ÚT}þköj“0DÔŸå™ ·œ[j{æ£à´ —Þ¬ŠãôÑnÝv°ßU¿ÇíÇ ~C0d­ÌÀøI*ÂíªÕÐ5)Áî¿Ðmr´¹ãPûÛçãiø‚Fœ.u GIÂ{ëÏâÙ“T·ƒî()fj×b¡ÆéJhf2-ƒ"jöq°õŸ‡ ú‡&Ýѯ–¬¦”XibÔ× ˜¿s6ìÄT«(¡Š¦o27žyK±(ä[t¥v.þêþƒ }K"="ûG(èElA§îü±|l»1¹?ßôb·Mÿ Çd2\͹ê¹-xé7æ…æ@û5%þÃk6×PçûŸáþÄj¨U &²Ž‹éÙät<¥ÿSðßßCã1óÉ;}T7:Š–O¥ùÛO˜‘oLÿy§ãaw¾üj,ôÀ¤ùm8·³Qø&Ι5Šãò®(rMu8ÿ¾Ø…òƒMR< Q%çEÂÝ»®l’§ؼ‚Šavÿù™×ëòšr4Ò²»že¢±ex$Ïn *¤sõ©pß¡°³RR5èñ|ÊÝTç ü^ÔyO€“žc¹ÕÍNA•‰;êŸZÂ,^Da|¼Oö¸–`°\<ö~L„éN£„ä7Œxí vE0Wt/|ôy]ï•Ù‹—¢´¹k2Ê[i“­²µ¨ÆÅhx]!\sÊD ÏÀFÿ*~¢ùت[ˆô«I|û?#¾ ¥sÖÖ’£ðçÏý|‰W×@ü¿#9°7 c*P‰hË&2/-ê/šŒŸ¦¿âÍ%°*òñÿÕuŽÿ›¥ ÝÉÛ^àºgEðlÚ-Òhì†Ã¦R6Ge—лï0Ìw˜ŠÓúfÁ”‰—°ðçSÜ?|ÚnαwHÂñÑüÀÕÉX“Ú˾+Kš›ÝòàËrËa–¾‹0öO.\îNRÃÕíùpIö ®yõf:Ïb¡ú§ÙoW9ú`îg(8÷ý¿…ñÒ®ôÚòy¤!'>õîU¹`ùóÁÆüìI:Ê2Ç€Ò+)zò”I¯T ªÐ÷E9¶,œÁí¤ á~¿ÎšÞÓ 8±~ê_ÛIW¨EÁðmo‰ç“°J³ÞNüÏŸiÒlïëÈgX€[ï3¦õÈ}LoŸn»KÉi¨ös0¿•º`a9Yôýi.nD¹¨ë¸CW…–߸3ÿëTP®¸–=»wFPzªÅ׈!ÔNÆ:ÿºÝÏEO™sp{x$Täíäd1…šóaüúg°nT;“zÏìÿ!{[]¾‹¦‘ÐFw<³}><Õ  ë %å ù>v<-Îl!j¡x3Ò‡•¾UÆ6zVxHm7ü{ùjÆÍáõü=X=i®¶Ù8†àü¯òu Íј·w*:FÝÂÔ$!Ö—-u?Ö‘º-ØþÞV±$/N™oÞç*|ô6òVݯ+?†ßxçH¤ša6½Š÷žÇ’¡‰$ûßJ¢9ü< ‘ñ˜ÿ;ç.c¾ŠÃ°“ÓQdg-©û %ù–(*±˜HašË~Lç¹pÙ®7lÔ»¥8k¾}-¾ž]ÕòSëÖ‡ŸÏÃ&‰{ÌϬ—¥ó©[Õ½{ôƒL›0®um¢W§U‘’•ÃXWêEÞ6 ÕŽãKÕ‘åL 9y¡Ä&ì.›nF—ÇAãêHùœOÚkÂ{·‚}Ó=¼3¹=÷ÞfÝ÷Sqòˆ,|´Üǿ͢½Ã£xžþuX'ÖÃfí_Û¯¯c€ûXœ¹ï©@Ê­ g¨Œãv."»50È÷ „ìû„±f•óÁÎBòÍj´ûÉÓ„¼ãðnÅ'Ô¸ §×ˆàLù×ìvé(¾c ‘µµÂ7s8לÅËʆІÝûÉ——ãñî‡Hºv»Þ¸AwàpU'9š¢ãê˜Ç¯]üsÃ{òëf4ÙÜt e¬ÿ¢Q—øjÊóšN7¼.:”ŽP¡¿bJ™ë}?J¿W‘›sÏaÏœ`æiŸ)м§FuWì°‹«lÀJ• øbÛX»'¹VPjü%®O€ž'ްñôaš´oíçz#® Î{GA¶øœ¸¿Vt¦£n¹€þ1+e-'^À£G>Й$Ék½ˆÿ¦Ùƒ*¶-I‡ß̥˙À{ÝaHÝ … jBœ8eØfºdÌ1AÁôu°£6/~±;YrµžÚpK©;ð c;ˆžØ CNÌ¢ GNá›Êì–ù#\uk1:ÆÝg®‰òüæy€üîp¬IÛ$ÜÝgÆWûÁòNpÛŠâšgqc}*~8‹rO÷bTÝKîýÛÛ—ÁëM; XIŠ—J ãV¥yÄÄÅ•®{µâRDåºT©3Ûrx—‡ wF/¼ï—ÝE—0í¶µ ô¾ GñXÈÑ ‡œkg`Z”Qô™ÍbŠ Ä¿ï£pxwA ÂÕ>CÕ÷üélK^-©‡ÿ|ÿÅî8(²àC˜ý¢zhò}€%oGÒ`ƒxô•~‰_\,ÉÃò™‚/ë ¹F^Íl„ïó¸W¡£f¥ò–ibôR »OEavõ$Xù&TœYˆÏd£ !´Q(X\"LØ«JW¯*ÀM’‰•ÌEœ ïA‚;Î3Ó¯X”X$HJ{ „ññ°bHPÑ£V«Cô`MþæxbÔ&ØnªÉ•MXPã3e‡°¦Ã1]ƒÃ“î«I¸àØpþÈs 7îlÅ¡Oƒhú&#~Ï+OßšG»ž¶ápØYЋ£'BOúòü÷ÁIçë塟t5\Âr³¾@ŒT±yï3|ÿO›/ Y'rwé"l=†?6ØM•æùÃóa´ÖÔßgˆ¥Á3 ô\ÈþLgIgŸ£¥ér"Ú´§ä¸¢Ë±v»q·íf®ƒúo7ñE‹;Zá¿yã1 ÓÐw¯ð휡|Šú'âS"Å )Çý÷¯á{£6ìÛ'Coݰd‰~£ðyQ,¹ï†=kÞ’ãû6Âà=ª\ha…NÒȦ #©Â qnóTg¼LÃÛúß ì¶2»R‡.Ï€r^#‘ú ¥É›øÓ¯ð/ kŸ‡1ìM ú<¾\»Âû,¬È:ÍtÎß'¡íOÀ|…^¹)Ôó˜ÏL.ãää‰8WŸc€ÉA”½nÕoî‘™ý0Ús6kT“¥¯™ÒI3ùˆá# –Ì:ûÈRÑÐxˆ—wƒJ3j·ì+¼Ì÷ âö1üuQ:ïøØ•?=©SX¹ˆëùu_$Äí ¾Çˆj}vÄð?zTQAcÁ<ÿh1*¾Ð§ŽÆž|ú*¢,Ðç9M)ÀÒæÑkÆJ¨7¬G'=ù97ñ‘¯¹õ!›ç¯Ä†Gmäk牱š{>øGÎn`þk,ê†3­ÜWÏ®:q‘ˆùÜÁ'¯@©öØp»VGÏÇÝj™ä³Ý.è1OCÖmÈñš…Ç;¶ƒ~F/ìþùŸÄå§bEü:“O=ÄGU½Ìb^ÿ\ȳÕþ¡½3êßLÜvmeŸ“¥x…ž,ß:q$ä÷êÁ“LIêã[×h#f÷vÌ|œ¿G“úýâzca\ô|ÿõLûEZ´ øáuÁÇ=C8ãôdé{ùó/Gíâm§ƒÆf{:k‡8³Št¡Q—Á¡'/ñï+u>ÔÝÎZ¤@’>ƒ€{™8?4j¼¨!äëgÂw‹H;f5&î†nGnX·WÚ™âûø:q,4nÌÆÓsvù{ƒ88§wj–ñ\©9ôÔÎi0âúc¾Ý‰êvY˜èsEˆÉÃhà·TlÅVØí+ ‚öüQó0èן¿m¤jæÚdçœËdsZKšÿ޼‚@ì)oÛÏÄ6)ÝÇ`“©þž—DD TšÅ¨já Li²ç s2à…ÿYáè¼ðŸûá˜*vj‚¬ÀGÖ˜ö¿/¸>n˜Þþ 3CÍáϺœcÞ,è%ÖÜ…7®9ÃÖ°ÿñßf“9 ¿ù 3 ïf3¤3H‹M̸ÜÈÔ¼q¬ÆËà¾ÁìDÐ"Þ´ä#t¬xÊc,A|l2¾Ë g—$xc@‘µ»žkg_‹Ó:P)“v\_.E7Ä:‰½ýóå$¼ïØJä® ùîp¬EòîÎnš¡õ÷/bÆ-U>¡©ü:ÄØÔË`òÚ˜·ïfè³rœhÙ »TeB:ïÚ)˜~±ÂÒÅy­ûp”ª-Òçæ­x%â óù²~Û¡²Mxì¹±dûÚP¼’¨Êëñ(ý £ß¾ÿOÿ ™‚‡ŽœÜ®¶‡eW¿âðà-àø¸½¨Ba²_6¡ žÖq±žÍ?,4¡Ê‰ ˆßêVgƒµÞéÔ2D”J¥Ðár šþ«GEó—ÑSøÊæÏp?ñ&ØÕæ’¯b|ÚÖ…4hÏziA»·Øaý̓häýGž|!¼3? rÞÂùÂ~¾w¨âÖ*Ó ;Gƒ³.fõãÊý.N‡¼žMÓ£8Ûs†|Hha¥tñg>µhˆ†Ò¹ø‚utÓ¾jô—ø‹&ž‹XÕÉY€¦M4¶NšÕ*±DÉË)‚Ãù¾ÜQm¿/|òä)Ï¢Ì*á”óBþŸ¯8þŸ;}ôéüçeî”­Åö)7ÑcË(Vx ÎŽ¸ â‰:pPtíMPƒ}ªÌõÛPšÚ Àð¦¶D _¼K£›$3Ù“rszby5ÿy¬•¯ ‹—`¼ v½7}Äþg¡êó£ôET•®ýHnm  óLjÑÁÁñ°?µ vŒŠÁm5'kŸˆùð bf|ÄLÉ:å½¹0̲·îÜm‘ÍujæÓQ.GÈVt%óZ%f=BŠIÏ£u}AcÐüýÿ7û! ?.Ä2+;¾ðh-¤ÛŽ¢ec¶‘‡dn«]úJ‰¾?„5£ðoô9Áôò½4míQr²à^õP§³aÕÎý\Ó( çÂ.7\H¦º¶ÃÞbj£¾}1¢F³ja«t=§¥Ãí=ÒÐ’0—¯Yq½˜ÔÍàÛŠSGÏRú7{+™n &|ñ€ ï?3è–¡3ÅÐyM½dº™ï)¹ˆ=ô9:_"?ÆQõãN¼®ë6¦y¬ä޾Yä«xz|:]dZWgëaøZETÙ»ÁŽÎ:k‚A¸ß@üW­=Ü~aŠ“ øè[x`ŒÜ¨:Úƒîαáºô_¾&Ù}"”J®åתìHŽùpj¾l2‘\‹=âE$×, Óiv`)ý%¾‹ËXêóLKV£gÅfÂQ)ºyE/jLËn@ë9eˆªÈý/mc†·N€Â'ez²qk©X߮ͿÆ^Ç3I3Yd¸/ˆn§Î¶ 2z€ÖÆù­ÉxH1·½ØŽ7vNfûHüØt¸\29¥ÚÇÚÅáñ-kªt&…ÍÞ;OÌÁ–FÞ;a&'NÕ¿Ïã~wâ_ÕQ •²Žã)¹dÌšoŒëjóÙžªêþ&·¼„<Å3°Be–]I^ŒÍÛºÀ¼ù…Ð-öZ` γskàŽ“•¹?Šª»oâÝÚ×!1@ roᇳaì\®7µ2×Pþ0m+ —:KµÎaŠüq‘?+rƒ áøAÔ·yˆtf!k$Ím÷Ãí‹_â êQÔvµ+xzQ¥þ#¯ VYd«æü!C¬`3µÖ‰â¿z-8±[neîÇÅYêžXÜü}<òðƒôG–µ£ú߈U÷±z÷ÔçÅÿοo%ìûš -â<Þø ¾Ø\ ÉýkÕÓ%EæEâ¡© ‚pºN®z ¯¯Œ¦Ë–¨ñ4åç‚» ž4ö®.Ožë€"Ú„ÖR`vCÆâ% ;X·ä÷äŒmK™úÞ¥èEåù<›C, S“VÆFC›™|Å·ï$ðÀ<»ÄžíÖÀÉ‹½Pã{ž=_ƒ¾wa½H¬l0¢ñ®8fNng+x[œ?*%dàñ–¡èf! zƒäqôKΠ³qiQ$ÌÐ)Hz2ŠoU¡ J‚ÔP þªAzÕ4á‚1~0©O…¹÷v2£ç×;Ôÿ”þn‰—òMØ{ î~ ®¾ e¡%ÅÈÛ¥è)hƒ†„(:nø”Òç?‹ípñ`^ð\”×Ká÷8¼šnÉ‘…béç°HJ¿:¯.ÞêÎÊÆaÑ­¥¼÷L&óR¾CJ»Í…áÁ£xw@ü÷Œ_rË`Õϱtb­:ÿžX ÎÿA‰%혬Cnò< ‹”£3zìyç°Ë ¬üˆ[,ÐûK!]µbN“Hf´6ãÕócà¯xëÖ} Êaó¸Ì€„«SQ¶,KDaìˆO ±å¤éÕaöî—d×…²ÿÅ‘,F‹Ð¡·ÎÀÏyÈ:¡Æó”×bz×Yœy‹ƒlu1*,…yÁçá§¡~Š‘äUšó Ý-ŠË„°5w‡ÑÁª´tç]|Þ¯_ll~ÕY‰m¡›jӻɑ8B]á³~ÙÆ‚Ú)ãÑ-³žd×D²Å»¯°¥g±°ä>ZeFÃ~ƒuÍ+vîÏ-1±£òû°ºCÿ«½Ýj8¹h¿š^û÷µv­Ç Ôx¾ëvp^åH¥'eá"ƒáÚ`­q¸!ù YËfÃuQrüñ º/1’•„'àݾ8• œU÷ÿëžÆëŽBõe¹xüh´5ÚbÇ£+`ôE„Fî0Œ>rnªl„·òaâ9쟽ˆÚYФêüKfì¬{Å~™GƒwÙy2gdä¬+egfZSÑoßPlÄB:4Ù—¾û¶‹kÍ¿LBfr×ÌHöðz*½¦= FÙëó<ׇP|nyÉ™ö‘/'^ƒÁ“­gk€¶VYyyËœ'BË+FØm»tÔ…÷hNßsØ·õCèÊô·XM] žá¨ÍÀ‡<Íõõiæü01‡¦)q§t¢´5?Ž·z͹óÛàýŸW"#‰u¬ Yv@(zÏÎ EEÿé° ½ âR”ñy€êÏ”ñý°½fçÇb³O»gšã”麸 ”–À€;¤¹ø;s7‚®]ÄwõqpÙKÆc€YÏŒ»ÏÛð¿zÌ2;pY£ ø»‡#¨VÖ ÁÍ)Æüúa\J‹VÀÍ,Úh2ˆ®îòän퓦²çÁÞ 7±Çáäì?i̽ G¶¯§¯/F×Õº‚™ãUé™À|Vx#Z\Þ’âñ¿ÑãQªðÞ33’¡xÕ­ÅðGAËgð?KâßÕ~$IÀo© tÊt)n#Ú.[óˆ}^—áP¯k(´ÝU[AÙtkZqC dü·"õ@8ºèû%v³7_AË!LtZ‰Ý£ñÈ^cå©SéøŠ·Â¼ÔPê¾Ç ö¤^%Óׇ€õâ­ÄÒÄ ú²„üñÜ)ú2k`NE*Ÿp|0mê¤,`}P2ñt8&‰+TGÑyQ=dÿ³nÖ¾½ S Þíâ0tóû…*…ªxº×Œªx:ái[X˜kÌaëS’\¯~ƒûµpË8¬o‚®G:Tyj#ˆjž`fíÂ÷›šò_®«L¿6l`.‹A´;¨¶VÀšŽéçW¥/á‚¶yh¼ý/Y }C`ߥƗjL¤io†Ñ+áÙ°{ì.˜`~ j¥u!5v{·-^a§j3"±å¥ìgÕÒÚ;[÷1ß…(Qɺp› 6ZɯGµÄÌJRk’p…'¡O²q{>å#|Üáû†J¨Œ¸K&µËP¹£Ãióg~’ùDVT¥“©2lý‹P²Òç-¹£# &³ =ðëÅW@<¡ÚÍ[XbYÆiˆÂ„%¡ød±x|tÆFÓ§Ø–—oþwþ¥õé6AZB´ï<Žç/~¬wµâßµx±¢4u=ZÃ'´ ‰¨G [y´Ÿ«”¾¦¿¿ í«Ý@Éö¶ß…æ«©¸…8íó=‹Ÿ×…ðy›nCä†@òm¿ýwL‡Žs‡HyšÕ*Ç_þÒÂÅý±œîÊûŸ)°–•¦¢!_˜FR2߆{á1ü[§ï¥A÷’Ÿø_}éö©søžáøÚ߃©Öú1ôØ¥I°$iz®Š­{V4º»Ê0ä¸>·Yêð½÷?ß3»õ 2¶ã½.ñÔ>¬ø;„ýW;ùK« —ŒúB¬[ƒùd'?ÞX™Èc’å¹XøNaB}4wÙú8;˜~ÏÎ¥¿MŒÀÖK~¿š1¯ÖX¾Þ@œþÍ‹…¦*Ð:í¿šÌ8eÞpz`)V°à³¼møÄµìî·£´.É™Ïæ„ñ@Ù% üðÏö/ùÞò0¹ëʹŸ ÷„`ÃÉ¡‚ØóY¯"‚§ÝrðÏIà™ª `ã­H§+ËÓÔ™«x‚öþ"Bbûî¢çß'/e›®Œ‰Çzà ;JÂ#=Ø(‰NRà"˜—nÈ=1CÑÜ$ذS›ÞÏ âÝ=ÔÊ`3Úüsƒ»T%V—R·÷{QÍOÀoªJòËbé¦q‹ðŠBܵµ¢ÞåáX&¹œ§$Jq“f´ÜgÏÓk’ÈÜ*4r-DÉ­*,iËŒ:æJ·›ÖaÊõ-0<ì<¦X+ñ[š6ûF–Ú¿Œâ*Ýjüxå~þú¤ U‰ØÍ/ÞÅ?ÎMÇÒi’üû¶TÁã{ø{ÞnöGú‘(Û [¼môïÆÜ4xòmïÎÄÅ!o6?bý õ?õÁœ'®ø2ëÌ!ËE èÈãOPÜÁæï;@¼¥/ t§_j‚¥?V‚wE'15±§»Ï}%#|áýÂÃD4ñ8þ² Ÿ¯%šÃÜÀ¸Ä浈cŸ_,ØW ¢[Žûáoã¡Ôµr•0ÙÖ\øÔ`õ…{Äû긩¡ÈÎ{Ó1ÐUÕÇL‚jÀ+Á} ¥üø 0/DÃÏþ${£ @OF•†1ç[´†ð§¾HãªRHYЫ•ä@þ1.¨núBÏ´66s÷Ãü'9ì aw3Èœ]äà霺W[Ær¥×*Tþ©<¯Û,ï*˜þÜAMK•Àï{4­˜0 eUÒáè´Ÿ¨Q4“˜ÄZÂú󛡈>ÁïVƒ‘Õ;ÁsÇÅ´àÏP8öd+‘j!ôEi«|-Qô8@ëÍ$©«ÅS\[’A-Ũù—qµÈNùqšmé“âº?ŸÈå¼Í9?]Bsܵ٘";’R¥N :ŒÛ&Æcñï“°Cü9k^i‹Kv¥ú=`å[e©Žœ$ÞèkEU¥h¬<èzsç±’”~ËÏtňÂÝø¿6aï|XfdµðÄr=šà‹¸8z [4“ì{»os²ýh%±ýÇîcÓ]MÁ=»á¥¾â£‡'ðÉ Q>Jú Æ:û?òĆ|ÆÔ邹ÆÜ72>¶%£NôAHóÁ;íì AüWZé†,jù¢E¨;¹ 'Æ61OÝÙà™1uO­D¯ Ûàížt¡¥t;ì4‰^†P¯§ÑºÝ¦v… Á¡ƒ bn¼X†Êë›bÇ{T7ûMví¼O®†±¿{G $ ZYÊåZœý@„æ9–Ê+àÎ]œ¼òþ'awXmÏprÎGœ ŸùÐûÏÉ íò8^ë \T™Þ´b”.}ú}Y´û.ú‡«³£SpóR~+ð„ 9IÎ}7¦ü*ø±ýÛ`ýÙóÐÜz˜´V¢÷.Ÿ†’=¬U>=,µÄ»ÚÐÑ` Ø0‚n7Þ¿™NO9K³‡uó©ï&Š€Æ•ƒè|ç99HM”Ïàï>äûHŽÊOpS€ÕP;BÔÚ”ió²(2å¼7l_¶U_©Ó[ ¦Òƒ÷ÂÙm`J=é’º ý}°æj÷É´¦¦þ¹£ÃžFªòžYÉøOg›®©ðÿüÁßÓv"ßq„¯½:ŽOœÚ‚á™v4ºb*MXº–ÿWOù¥øTðѼJþèT¡û«þDhÅwæžAùåÎtˤµ8jxM[­Î• ö-Ë ØMÓÞÿó@¿œÖ‰KÄNÂǘ/øbR%ýepð!«Y‰ÏvAcáž *BLYóÖߦx¥À޶y|L0ÁGÏÐmßáÛ’|Üýd&šTƒíæž¼Ky¬Œ¯ÈÓômu·ðµÓAT½5:¯ŒdcüèêáÔßxçù :§Ö†NMRå:"=lÓç1ø‡u<Æä")õ†ý3§+#Ói߆dÚÒ14Dâ~¯•¦|ø¡Ú\Gð5:§ wˆ{W8å7óx‡hùtHj:Å“c9{*Ý`ÆjÙ©·røG÷TníkÊŸêhÃwŠGC¹Ÿ2õ/è`ú…OXä{~Jš‘º”nÐ|ŸfßÖ¦Ç];àTÙºç¾t–k¢6˜s‰yê$Ý-,{`ýœ­°ÿÛÜ*uƽ™IïUFóˆÊ)<Ôv/¯q;ÀwO‹€V0ÅÍ?ÞÃPÏ\ xw Tb¨Æôø|†%Ÿû©+êöÿçGÕ5VõM±úiÔ¯ƒû`Ü"múÊÓ‡| |Ü· ºO~™Hƒ¦D |ü®ÈáÙCr-´N÷k˜Ó¯Ì!2a »#íB›ÚF¿ƒ •> šO I½mä’¢»X` õ+Nã^‹°îSµÏ%“ë“HhñtΖAé½$A%W£ejRðÑå üç­þòSŠÇ¹F;= u£‡p /<…·Ocáïè øWV Ì÷7àl—xᔇzÔ}gv­?þŸ›¬72>Tq‚ÎÝÃ8뾺EK Ûg1¨ìÔâ¿îgþãœHÅîÌýG¢ 5Ã`]qîLÓÀÿj2ï”íÂÙ¦ÕpÌ,Ÿ«nRçñOw0÷”ì)x‚Ÿê‹™“Ð ×4f±ª$Zv؉ßüì‚ÛŒëð å,ÙÖŽúù“éê mº(wŸ¸¹Auž6™CÑÝœ{ aîù£Ð"?’>›ôÒN,î(Ø*œ…¹«ŽâÊñ£©Ñ³ 4Q/`y¥ê|dìpºwê`>Nã¨7DâUn{¶мWÓîÁr´3ÀcþJUè¿á4$…qÙ‹hj˜µi¥xüW Ìèk ºî-E»7ºÂšVÙþsѧføûáÄÕ98|äN¸^ M•dÒˆ ÐƒmÌØÎº×òÊ$qŒ½˜ƒwži°ÀÏóèécQ} ¡E+ãpUElÚVJ‡OÇWUËA³B8i¼8Ž¿Ú™‚“|hNÝWˆlÞM[,Z±ûìO–˜g™ió‚¶P(¿AÃ-Ç:¦*Á̲wØù¥Ánû{œíjG®:wáòö\þVIý„ø³§áŸZ¼® ª¯½a„ðuÉ89‘ÚE¨«®}÷¬Ü2„Ï3ïb ôjÈ Ùìù“ÕìèŽÁÈ žc[ãÀøŸ|.d§ÌÅÈ¿~>>qØE<2LÂb-¨²ZÓ:`À¿nÂ3ÄèGiïïŠ/½˜1¶63Á‰l³žLÇ%•² e;6á@4?KÜ@½â-Œî±f¦‹¨V÷>Îæ^_ 'Þ Å´£¹,ašo?+ ž‰vô]£ß6¹§ùBV§"mš·ãciÏ„&òúx×ý%‡‡|œ¾ v$ÄcèÜ-ðGå$|[àÝI« ^þ*”ät⢲—“Œ&ÏÈí}Ð!Q#¨¿áNï½ÕçG¨ßjÎŽàA— ü”\‹)Âá \YS ›´K l×O”Þ›†â£å¨ò¡spô§$y‰s£L iWÁã- –¥¶û³ê’Z ûҷ¹£ /›|¨ßÖCîmdávC¨´O'‡™Ñ>íXV‹S¾èÂrÁTÛ £SqÌ™™p¼å*޳6¦e/ä©›ÓræÖ»Œ˜)“›jOÀò²´p•çKH~Ö6€_CÐhÜNŒ_Åãnêãå©ãênêa¯ÿLþÌõµ ÷­˜uŽås˜P›B&ï†Ã“d¹Tg{Ô3Œ‰?·À€b[¢¿­ƒäïà ë#°ïí6öß5É5³aø! X¸ë,fÌTA7É8œ¶Êšµ5öÁcù7¬W.œnéEçOr¦N’]ó‰Üß¾$ËÆ ÍÑz6þ´ Dn›@îg½a±ŽgY´8Ç0Ó4Ö½W”ß:%@)&ø¯ÿ1Òc/DÀŒqå±k8×[ÆÐ¤®_#ÛŽâf·Çc>¨¹61ùµ`Þxdÿkñõè«› ›ƒÑZ)›íô”ç-z vÑߘ•ã0Ø‘ÕÏ×8HhÝ„É ›6Ù“×N“©;@ô›Êõ…AáºÉpv£7Û;Nøv yŠAqaðiæŽáäѺ™ðÁ(rAvV8H&˜A€b¾]prCîãÕŸÙP»å,|- ·þªÑœ„¿[ @oŸ”¢7ZâFœBÁ¤PÖùŒyú.hWµ’Ï*ÅVüL_}}€¹†tÓò|(¾UÖ²©e Q1Yx)º_¯ !x÷é)Ð1.®×P‡·Zl­¥ò€þ;Ò\‚uÚiLjð~ôZrZÖK3“Ô¸þ×Z´³ÏÅý>E&ýºB¨‹Wg?Æ»™gaÝB>¥¸t<û!Ãg?܉O–bË;ïÃÎV¾ð‰sCÔ;e73¢^>ÎD¥…âÜág5=[=ƒù$•±æçr8þj ;”*M5t xkð(nçrç¤;cȼ­pó&|ÎÏoýOÂg@SC€ua°R©‡¬ëçüžþa{\ܘñ¼ïBi_#vÃú(½»:)Þtnã#v/½Bÿvuß„ʳähví"ªÛºwÑB|ÿáí@þ/ÙK^Ýû\}P¼íY|òx=¾…»ñ¤Ò°:v žê<ãžÕà‡©‚7§Sر”dǯq¤9(’¯-L· .–IJ—ñ™Ds 8lûYµo…ïdúXæãñP7ôv¥£tøLìU”¥ñïƒ ޝÑIõ+˜ß¦ÁS^°û õì›|9¬šqîŠ)¯:ÁAãEháWEzìÔ,~6†ÿÐþEþ\~÷7t %ñ²ÿ é©$Ó+Ðâ·4ÿð|*üÚ}W,XŒ/<Ñ]F¯S\Š/$£­‡¢Iök„;ÿj þ{cùÜ‚¿¾¾X|Jo½ϰè'/Î7heg7¿‡¨kqäÓTx¿×¼;0bzï@Ó(3m³ˆK>5ǵö(ò\·ÇŸ’¶‡§Æç@Ѧ£l»Ž¼Í ® E{«Àá2‹ X 7E·Òêî˜=(Xùò¦Qx­¦iØ¢Ën9¾É¨ ޼˜Éµõæ¢àd,N~¾…&è­Ä9¿”¹úò£¨w“M»x&H¦°É‘Ðp?Ž^UÀÑÁá0^Í÷iWÂç½$Èg2øÞÝ—Oô± £]XJÆP4‚¼½=üûŸ7g⦅1,¸p|©¹Ç2þ)Ó cž×™N¶Ö¡§®(bÔ;u¨1N€wÙáhUÝm„³—ã³êŠ!%¸Gû­„{©Ò±z¶¬½q,ü“¼‰åMâ´*I,H¡¯óÝ8,¿Û-ûÊF˜ˆÑßeÓ™Ó²ã¸|ñ60»›ˆ†ÂI¾Ô9/Rï>aR÷mq;Nl¢rAjÏ'Ô⊰—@¥m›~~/Ø›‰…]â`Úša¼ÞÜ_¬ÈS$ök@¥ª¾ÎIA•ÓçÀlç4Zˆ3ÎÖ §nîu93ÿ–Cªk{^ ‡\­˜2§Ÿ#—¿eä¢ì¼q8Dè2$ƒbQús(šoª‹ÓáÔFcê/ÊÛåyvÀnè7îÕyð†Rqþ'ÿ-ÈÔ|€Úú‹'Fksâ ™쥽"žh9"²__¾q™ …ïß‘LËMèR^b§{¸ –u‡b*|ÝßNà‹ˆòó³D‘2^I´ìîeF× ÆÍû}¿‰#¤A™S°L| ýë~—™ èg½Eh’ñ‹4ï- –'-éNÅXîü»V¾%]¾„/œó "çÖa÷]rùÛ"Za%KWYxã¸!j Ó NÒôÀXônlÃ!ß*áË~-n<-ˆÅË»¡óè#¤Óè'D?÷Å´6DÏ÷=,jA/º²‚çûUIÇI¼QoÊ´ËA±#¬p!£Gh/¹Çj ˆˆŸ'×ÌÕ§C3Làþ̸a?• ŠVÌÿÃçPæ¢58ØÞÁWC-iÜü)ðå½ ]ùÁ®K§Ô7m`‘AËü)~ù#Á'ëp)EQ-eÚo®Eåh >ê²#lqW»å©Æžb<9ñ£À SO0þþ¸tñÆ›±üE{8J¨ôáÑ–y {‚¬øøÛSÉãÉ#¸ÑªÉlŽ\4z{$ôié8ý2ï*øX¯¦©Cxp¥èçÏ€8»Ç˜"åkbƒIh|ëÚ€æAÉta¥ܨƒ¢ÞÅñ±{w3ô NKJa œ}«ñfÏ Æ]æ­8ãè=”~[80ÿ;ßú¿šÌŸ$Ú]ñÐ&Gr¿3ˆöKàÑà èo“ÕmÇŒ¯"ðIì:ì'‚åÛ åÑ]ôþ^‰Å.ÃÙ‘¶™¤ïÛ]¼ò8ƒìÂ%D>í1™úwÆŽÏÃ÷}°fIÝsà6kާß×Q_µÝðì½ S«Îe³Îø¿t.³~[€`iNoÜÂ÷=Ÿˆ:þÏØeo3vl‹¬¼ÃÚH~Æß#¾^%í_†cr*°½CɺQùĺ*­ÎÌ/[j'¶¦¾×¤yÎð·Ôj|:‡çM(÷h»Ä¤³rñ…ëX~?j?ÊMУ<Õ "•¸v†6sä>½¸•O+‚„Ñ®pæÃU¨9kÆÔ&Xðæ#³©TN5YÁ2_—Ãï½—1Ï÷7{*…{^͆ÕÅhÀljth×W †q z¬¾8UåÔ¿ð·á!Ôx±2?8R_8£¾ NFàf½0ΚÌ߃ÄâÔ‘ ù½{âüØT¸e}Ö“…øL;U^œÏýO0ìÐWòÑ_¦ÝþMž=PD !¦ $xçìÐ㘄‚+~ÔÞ–ß› J“ÃðnÓ4žFÒŽÝãèSxÛ8– )ðp«“`;tíœ}€´{јäxžiMO½_Н+ФPo¶ƒHi2N´ ÕKëAÓGžFóMÜ™k³ŠÊx¡  Ëæ@Öu©þþÙt¶P½Aé<‹¼bíiÁÖÉüÏ‚V8³»‹Ñãƒx‚B&½~eÇþý'’e¯ál]U³ºÉ2˜>]q2Æ·ÂÔVXž ƒ[½„5Ë2amæ|xÿn3Ž!g^÷€çæcÂå« vCY1Ýñƒ!ýqv“X4øuÒ%ÃŽ³ýõ7A~tšc3›¥žŽrªØÚÉsß±–­syè¯ï8e·»äªÍ×WÙýçÞ>Ôs¶HRߪL6­ü…$$d*Þø­G]Èx˜¦?‚ú¼Ç—Ü>…}rÀvU§`è7¿ýoœz2Z®…L'Ù#^‰›—˜Ò9S¢ázè34Þ]3d‚0÷U?71z:¥±Qï…¬§ÇVf¿¬Ž_οüÔGx¾—Ѓ’àõqbËaø¥ F}RÂþkØ¿‚Ø)Mgq¾²\ªŠÂ³}p”–â`]¹ä(tÝ1‡m5vNF‡øb¹Õ°íF‚pñˆ,”š(†ÁjWxÕ«‘¼"ý ¼|Á1óz%ìsÑ£AWlèÜ–Ñð±c> ²ÎÇ·ùÒ|†Ã# ãÃJÓçñi›Fsm}u~FRÈlÃÑ™×Øj$ý7’BÂð'þ‹GÇ[aòÃßlP°&4\ÉõÖ·âéŒ6¨’œH_ëKð‘_ÐT¶v¾”â' ¯£ÅN.z:´¬`{ækLó߃.b—ðøåãüUøö2:X»8n÷¨Æž1üe +ÊÝé"§¹ð?3Ãk͆nÇßܱ¤{ !VB4‡›,VðžX¾Z ;ú×;óp78ðÉ\&ö8wœ:¯/ÈüZzbc–sÑê™,@|¿åíÊg'~ †ó¤ Á8v‰ëÐdù°Ãþ˜0ì) 5{ ·³óHŒ}¦LIe¶Ê.Wî I½±f þ;õÄèÏ XŸ| OçJR óüh6ó›’֭ĘM GÌäSnMõrÜ=ù4 çnÑûyÁXsþÚ¤wîªÆµÿ|èÁw[yç ›h%o•ºÆ°|ƒoÞ—@kÞawTÿ|µ¶ÏçC¯L墚SѲÅOOšM掤¦LpéÌ)ôëÈ9à6¨šOÌhíFE"ýœ½iùÍžNäC¥éêA{QgÇÒÐÚõ¬o]"^÷ Ç·’+¿óíîÝD|½:ﱆ«ê³ a¼:}x þ«[?ÒŠÐqWDÙœ†•ÿ;ÿçÙ˺u:8Ѳ Î ágn*ð{ëòaì ºÙ2¿^Ÿ·¼‚Çébüõë‡ètî?Á¸uš¨@Š[¤ªƒ²½8¬ÝFÌn7@βp°ÛË¥§òÓE$nžÔh~Bÿ:EÒÿ¹z|oÈ^eBüï™ð#°kAá’»pÃMƒ.öîC]ñf œ¹†¹.ù‚ש£x«,ù}ø4þWïù¿vV,.$¶«]áç^ºÔ†~¿€»:ï±w¢;ï÷Íä"¹sÉùÛëÀhx ˆlºŽ?Ÿ× ók_2ß5ôà¿(:þÒ¤õÿI4jÚÿ%ãvǃÂÄb,ºa ÉÅÓ¡³5 Òƒ<©†±¡ê4üÑ%¼øÒ“,ýKþvCÏÅ7ÿÕ꟒!­C>A̯Õ`Ó!ÓUä©Éì%Ðð­ òFU‘P­jœ¤½üÂ§Õ ¦‚îá…í8j]3lnÄ5ÉóÐE¤ õ·g®í©‚Q ³Ð1¸Qð)rîKlz¢Kk¿ËSÄ9 þÔI‡Mdµs”p™¼>®xþ_½s:lBªàVË_˜töñIsá}ïcX•ÍÈ?•ûòEáîŽaµž?fUƒªßàД°ü‚w›Iù\˜>͈/}'έL¶£c–m›‘F÷…-æ¦'óÜ7ã虦÷¸Ø£ŒL«Åg·EXµýQÐÙûÛß¿è»õ²vGiÔÒ‡WAqv*«Ò?ENŒ×çzöaìým[ì¼t—ôN} ópE†Ml€ ÿ<àŠ{4?|#EØ,=>ÆÅ*²ƒžÙóõ?Ÿ²Àj§#·Œ‚e{&¢ÝGZ´²«îÂ9¹õ ­ŸÄ^½•¡3›=ñ²:=²4ŽlI(€Æ–³ØåøjM 'çjÒe¿Ú°Å+{¶ £KæžÇs[úúUâÀï?×üÁi¹ªgƒË]ºaþxºª 3ÞÇ×¢³AfÇ(w0´òcû¿aíŠ x. ²nÕ€õi°†l»C« ཡ$ °ŸÉëkôù®Ð&˜ÿ •i%+ÀF‹õÐ'wmnN÷ÏrxKE‚\~Ôu&hV§ ê÷g1OÙátnÿøHýVÅ gkÖ“†Ñ±±e®:ÍÈ‘æÓGžÂ“R»ˆ¬ÊyÐþ'GD¢GÒT\¸rä^Åwœì "öÕS·Žë‚·+íø¥ shÊò 7×ĺ˜Izþ{ úéãºS6—¤ ÷Xf@¹¹Ot’ŠyiÑ™}7q”o OÜ@Ý,6`~•,Wê5Àí]¢øýg • æÝ¹—Ù*¥³ýýª'ÎÖ£hæÂatÈÑ~Œû«@"@ ¾KIbêY¸¼¢±AŸlšºšz9ý…[–ÁáIpyUQض§loÆ÷ãæCåŸiô£õ2îòw#È…ös×±¡°ß²ù΋çç_'ák íÔ§ãë‘õÖgÍN©9ò M]ðf$O©\ê¾™¸ð’1_¿Ò‚þx5 2 iÍùÒÞvÖ?ƒ¼Ö­E5Ûóü÷rõF -‹ˆ;¢ô˜(^†;®ºÂð«°æ–5‰ÙSž]ÞGɪº°¬U‘;ö~‹ÉW°çÅ[Ìl|…¯å‚pó‚ƒlb’ *’ãóM”¹zýz>xC1„6˜qeÈÃÆ¦0ú4mö‰Á·3·YiÊ`,\øt2Âë¦i­äŸF*ò ¶Ö¤e£9nú$Ï—Ö òíà3ÿ Çs?²³«˜FÝÁ…Þ§›Ûç°×…âÈÛ!°d¿&gÎv²TyêSÒ±¬~¬uÅ'÷+AþÃ)ä…'Vb³ÏmˆëSáó lÑž1ÿ‡eéÒGbNìÞÊT¼j-ÃÛ/Ã…&7ñËaôî¶mìYR ž“›ÏSvRPÛSj«%xòz/Z]µßhóc“½ÜéƒSfpù…ÌçÑD‹VáßSß,}îèmÍÍß åêùGPRd<ŒÉøŸç§r½ œ}ãÉô+›`b»>½j“‡ˆ$}ü[÷;e€£ûzeÊ9¸tû ŒÙxE 1xÿðG@×n;À"åÕhX¦ —oG¿Ìü~íjÈÎχ‚ø‡d»œ/õªÌ$ÑÒ|ZadÊo»ôb÷ÛptßæOœ-ñïHu› 1À*B§‘«)…pk\2|ýø‹,X}_ì>‰yU)ðäÁh:u­ ½|J ~âÛGÁWAD»O³‰Ë”úó­Ý5KúÕøüW«™ݰg"Å™iš3y&𠵿éVû¸ùˆF&r>JgQ¡©Å)ül°ví– -å·ðu·6%©à õKðÎ?÷n0Ûkƒ±ôó”;ꃵOCyß œ6vFÝ£ À^O’£ I÷ ¡ÜŽ: Ä­×÷â¾%ðJäÐÿy®ÿ±æ$å"”Ž×£v V¬u×0®')Å{BÊàùœóP;nßÀü—°‹Å‡æ§™×ê²ÿj3ÃÚŸ'àÚuè1ô¢Ã²Üâ|ú´cp/ª€DŒ¾ oæø ߩӋÁjT¸Zô3çAÏ4 ¾ÿƒ¯ž–OÚÈ3=Ð[6îŸËç­ì4»MhÓîùxX¹Ÿãí…‰»o€í(~À?€¥~E1˜=­wÆ›qê$~ÿm¼2’ÞÔÜß®]„*!ì¿¶®]>º£jafË:úKu;/ìØ©v ´ÛTŽ­Sâãéз—äW›ç±d%5ZfùªBp…E<ÎÔÍÃÕÅP0u¯Û– ÕþWð?ºÌÝ3æ“é ¡xûÞ^¸¿ã™Pzq4œž€}­dô´ eýš}`JÀbˆ *Tïr>Oj#ŸjòÃݼüh/>›ÖeG ™´O%,ºP ƒøžÀìXÕŠ×7uÿŸ?zT¤Ì© å÷¹mòÞÕ4šÚh…ÊÅwy<ŒVØ‚ÕÞKäÜ!\éó–Ž …CæÚÔù¦$]ú>]Š`Ãþ2|§=‰·Ýq…Xö1é=¦\<˶nª…×oÂà哸áë"ðo”ž¬‰‚žŸãHÄçµôý¶:¸05:MyeëÝüê„äÊY+PÙÀ§ØQ=  ¶1®twºñÅð¹_d=º†Ÿ÷8ðÉc/Ò œhªÀO8[CkNKÁl¯»(¶ü ^yˆ*û`¤·9àZÓã–o £õ3ŠwÂÞëôØ·}ôqAí‚d>üe]:c/¢Ûžpb~-yÃúÞµˆSvÓž`|ü|à+ËCº`á+üj@Õ ŠQ-B‰Ï¿øþù…Eã‘_·áš‡rÙ÷©¼µ/¿ìÉ'9E4iå”;ÈäTÏ@ש5|V|6óLüÊfÊEÅbx_Øc;{ÈÜYõ8ÛY”Î>!ÉËgWáê¨TŒiÎ¥&΢¶Ôhu6¦·r$uy½D ÆŸ§©‘f|å¶S°««_wäƒM¯5t$ql„¤+Ž0K›Á ûaÔR³ˆv©ÆÛ‡âò†8Þ|e;U‹jæ»*§Òo:SùöµÅ`«lL7¶¶ºŽ&Ðñy)QX®Áó¢lX¼ùß)-!Ÿ!Y÷"V«´àéïj¼îÞ-LÎ*OýÚ{ÍÕT6Ê÷(®tdp ›9GHQ w <¦50þmáyìÆ XhŸy/ZÀÇWD`Z`*ùÐ{6ÿˆ7ÇêûÞÇÍó¬È’!‹Ä³×V Th64k.Í£y !"sÈR2ÏY R‰"BTh5?ïïuý^Ÿ?ž?ÎÙûÚk­söZ÷}_Ï:» å%J©–‡>µïdÓMBXÐ-Ý|s íñ§=xs©sÃ$ê›1•í^Ë4o5‚dñ_ø›eļ{ ò£8P­kǼBÇSg—(ºÆrÄ_±†yÆú´ÁF‹ àìTì·;*±5´]sû–¼’Sÿ¾Ì‹'±nÒúžoP"í$s¼NŽÌµ¤ Êá´ðïYh{Œÿû¶ Û71…Y§ŽeõŽª¨zÒ‹^¼HÌÜ;¹Ž*ÔeQû²ÿ./  šKé›À6ýF­ª†ÿø—èÔk¸/w…±(CŽ5Ȧ»§r±.øe²xÕ,T…-ynV+As,·¡ÎG–ì´ušÇ±'_‹áî¸ëøhv#^Ü’Ê·&‹xE' è†z{Ë<îØ„4öf¯}~"•…'éü½]xÊN´¶9À¿ctÈ|Ûc™ƒ*¢¹lÓ$oæ¾®mY¹ŸãÇA¬» ŽÝ‡ÓÃñ¦ÎÄ_'µyfŸêáLRÛRzžê|(ç¼Jàä­$½z¨®>_L¿,¹A=%Ž1µ°"6&h»¢ÔŠeëÜ™«J0»\ñ¿úW}Z ŽC‡€øD°6^$>ÆÇɯžwpinúìípk±22ÌçÞÖ‹×Ç¥«Ø­0¥s/Ì6?ŽTm•֡㔽qßW3äÒ¼pã”~^«o 4;d‚ß[/Ps1ÇL¿zä…¯fÂqq¨RfJ_”#9;Úˆ¸Œ›?ÛžJßÒƒfc‡Ôµáè¬qgѦ¥v (Þ(F›¼iCóuT?LDb´ab¿ T{ãlÃ1Ðóø"™9\†JÙcéšKæÌ-X…oæUk*ХȂ}NÆE+–rf2Í䦳0Ý ñë?þÿÌm#L÷±„Îo3éц>²Ùs~Ú>ÌYÜ1¿ê#¸Pæî=¾†Ûiy’¼œl…7j>pÿ»Þ<¦Î…«æ·9ß.izî¸-Û˜Ä6*<„ëÒÝÜ„R)æÿk.ñx÷;®8Œ4dŽƒ¹¸}&w¿{>®–Bør¾(ÿ€™5•È-Ë.˜ÄC ýC<.u“CccÙ³çøxëtZ¼6–ùaQÒXÖûK’¶tŒ¥Ç/N¢+¥ê Z,ÂJõhÈßfˆü<„w¯Ã!Z¤A¹ÍekzØáylàz ” -Á•bÅxí÷ªÿòÿtÿÞÙ©“¨ƒ’+[1p‡ˆþÑ•aö@¯—Œaº¿Ûðù]A,^Ëm½!Ä->úö^Kã̧_å_ô:êo‡áyN-{?hÏÔdÁz¯¹Þáœ;ÓÁÀï#y(Ò@FJÍéçkcØ2õ`LižË6yg%áN¯úµ7òšŽ)³„2q*ªp‹û»qÌ‹fÜ\o þß•ð_žÞÚ¢K·g`âæÛÄÊ%û1’ˆ+‡£uÕ þäÂ#Ä¿Z®Œ×A½òð£:Lu3ŠSg‰eãQâµ&EVÇà†¥É°ýÒtv~·2~_VðßûwØÁ¯[”ˆ¦}°»V…òßßFûUAŒÇ{öå˜Û¯ONôÃ…wWpÓ‚rpû&CKŠ2ó°™GD—\ÆCùÉãslÏÜåR,QU“†Ý”£†"Ÿ¸Is7c¢jVH{Ž^ïâG67§ Š]>ôm%w§DV,ÜÞ(Äè·Íüe›^ÀÍ£¯@"þ<ÆG5 íŸ±ôËjÒû”3*U¥­i’ gêÕÇg‚oÄø-ÿÏHµãÌo+1ïð ô\ ŽÍäjÕ"ü7g;ü¸¡AozaÓ+¶~¥)ZeÀTì¿ÿWÿj<…©ÓRm¦ìèÄ»IÒ#{ߣmÞ6:ñèx·Ÿ:g Ò¨ZÌÁawà~ýf™‰ï—_t8mj@þô±½>Ö0âw'\¦¦(Âõ«‹Ñz§ÔYV2Ó?Êôï‡bîõ–óTf=añ:{¹yÙ#ä¬Ü Ð[´˜Iÿ‹„’’w¨êÿÖ­AKwÊŸ{Í”>l_Gߌ[id6SSu¦›¿0—ê³È”W)óEæÂž k°pµ'ÝÁÆ$ZãÇ%Ì¿ÞÇcTø#ö%«ˆØ¤„ÐÖ‰py§=ëñògQè´ü÷_ýó|ôç\O„&•\¶BHz­\ÐI±œ³\ðúT¨ðä៤ ðcÎtÍÍ¡:Þ`Çé •{œYyÙòƯV¦S-Ðç§ûþ«„oÊÅp’û–‚¸¾ï¨FR éŽŠ°l aýSðñÐF:™°üÀoðyj)&„ƳMÏúIuž8Ž[>Ù²ÈÚyäÅP.RÙÄŽ˜ £÷•j¥,B/òû›m„%¦(°—ÛciëBEÇãÌ)ìñ>»é?? óC/“ª2¨ëú¯Á½G¤È¥Ü62áÓ©‘(á‡Ë°û‘sþã?yum ñE\&1–¦aRù(æ[\¯~³£ËO“ /‰–¢ZÍ_³OõåW¼D×&Ün¸•ÜšÜÆ°žKW56a Þ]>ïßqÎAQ€i_”¢½·üÐpÅTh©9 KÝæ»}[ï6î"µ—”Ù}{fòÜ =o¯Ç_Eþtè™Ù¹í'üØp„ç5׊Y…w)’Ôv;swµÇ)n—P¥•áŒ09j²?üÛÃà™b}ÝÁˆ4Þ1{“am[5/èn8ŒúÆuþ)#æ:áXt£•§Ï³ _ÖÞ„™Ïܱ‹kãBçßåÔoZãÿà®ê[=LÒ„µœ3ÚKëÆs(˜× ¥fŠ,;q";ð›–ãÊ/FÑ¥O®CvâYìšJ÷&ªÒöBLLkqåÝ0®’öoÅ-«3°t{ùœ ›G÷y§Iug¬h…Õ[þrÔÂÙùãy†‰NìmØ4ÆÅ`zñ3ø$9ƒ½­Êžùø ·}çÿl´!×Üã—9Ï bZ¯H;EWýn‡úÈ9´äÄQû²`ñ3í$erðœÛOï, ¡é¾tÕ+'®(¹Ë¼ÕÀ?Ö Ä&/3¥ûb9زãöÿýÿëžÂá€Ø.Ç»Ê 0ïO,JçFð­­è‹ kiÐLBB“ŠËãxG$äØ$Ÿbª·G×¶Ý0'ÅMu}aeX3HˆF/·ã4uæO8µ ˆ9 ¿";%EiGˆ/¼ì Z{ÿ c×z€û$[*Ñ÷Œ7xw'̳øMDž7c\+°år? )qéC¿žkâïò ¹,|²\ÜâVÀ¿™°Ùè$_÷y7¤f/Çoï@ÛÞ?äêÆJ®þþ2NàÛ(_CEKÂs‹™ìøöé¬4c+h>µb+3i©a‚ÿíÿ(ÙuD{÷3û€¨‰4`D‹N»¨…uñ°IR–ÚEÛP§›oqµnœ™½† _GÄ8…š ØuVÛ„ní–ÄPõ‡äXÄ4jûNí1M&jVθý‘muGq“0ºðÇ]ø_Нÿ]G ƒh6Mô0hJ«Ò›…éÈäL’‡áÈh¹!‡7ˆÝ¢´lô­q S9hÆŒ NÜIê×.TÃ\û% d§Gí›Ë½ò.ïK;EÐbÆe.Õí;ÞS­‡/¿’ú*ðï»-8ô&V×q[ÎCÞª žìÝÿ;ÿeç—Pû/÷ñ§Æ¯B£E}Ü„Qm|R˃Y|ÉÄ•Ùßqov-ê„ts ³pÏǽ©:Æô8SÔ]/M7}{Ç,xø«; ,’xGÞOàÒÂþp‰óèºd%zuÌzÙÜH WÏÅy›[!çK2ün'ã_¨Q#ezës<Œ‘˜í<Òõm,[¯—·Ó_ý=±°õ%­²ãs‚ê˜ ¯KO7ÆB9ØT“« —@ÿ½ML;)kŸ8àá{,܉ÆÏGË·²xhÌ?Œ,!\ü)è’ë")÷k¸ü¸yc¶ÑÍ/ƒxtí¿ÿðoUId‡9c—‡÷Tï u¢98íà–0Úæåô^7@Çøç¸¦­ªv}â< Â+Î"@4wâã]†¬E(§>ìH:¾‡°;†4,¦œÜ!¶‚"h»Gt7…p²)–Pž‰7eƒø½Ì’›Qo[5Y ;î ­ÑNž[ÛÒ虢àR؆¾ñÓàýŸãÐ鬀 <“¸I¾/ȃ…âlOm¬¹XÆí[aŦ\šŒ%Û§ã’o㨠wŒh%gcºî›+©û SÉÄ~CpíÄûs&0)8Ü^ŒoBœ`÷zzîžâû_í‹$±¡ ô‰ ×uBcïz4~yyK4à\áNã]0gfG¶ì)­ +qò¸(2Çò.šnq$Â+/¢Fš 5š³N옉+4ŽˆÛOÀ¶QûîgÐsQ߸NÆŸl+JNHÄ>ãÔ{®`óø"ˆìçäzc¡N#>iCþ؇žpk€† aº #1N^01 JB½x~–ÆØþ+€¼ö¸ O&0þ¸}(ñ± öDŒ§.Þ\׆‹à¹.Œëv?€¡Þ›—é}("Dpuû¢‹É hµŸ‹Ge™ÊsÒ[vó¿úÿed&š?œsÝ‹Hz|®ˆO5ÿr)ÓÇ3ÇÜÙIW°\F’dÖy]jÜØß?!nÆjðxjÏ`Ï lV rçãøèÏõã¼WP§£çàÍ)d8l)™Â‡Æ‚Ì35ò;…³ ·AÊâû«ElÙþU…äÉ"=ö3ý\™4 hT%('܆Qª>/i¹‡!ï׆¬O*TÆQŸÕô–qR££ÍãÝ¥//Fí}ùxyy/_êâ Lª¯æÚP O|ˆôDQ:˜gÌû'¹ŽEi±‡ :y«ïpÄ¡âÇñ›wqp÷†"ÜW¡û7¥Ñ‡ÃaÏúp– DMfU£ÄdSî—o7 ßrÌïÜÍÞçÔØü㎦‡ÅÏqxêVv±/ŸÛ×9„’5:Œ6X@¾s„#ÔîgŠu|‚ŽÈž„\†³Ýtr'+˜Bäâq¶´C×Öɰ:u.­‘5¢7D§áÖò'xâg4¹rì&Lü‚„™CÿžºÎ1°yG,¶?“b‡v/§÷jܱC´ ÛFñ)jú-²òÆtÈü—5dXóîPTÉ d3 Oq›æ’ù‹½¹s¨ê=¾Àö¿ú®-HŸkýãLÔOqSwâ†ÐÕP°] Ž‹)b[ï!b[¤± ä ¸îT¹¨Êvμà ÿPͺ@nIÝ æ5÷¼­ªåÞ|’†e‘wq—Ê(W[£DœÕKqßôöwî©ò .Ò¥ÄwØÁ쯡¸¡#•»z÷0-®÷Ä“Déû¿wa÷c)v«ÏT:°/?ûP2®rµEy˾Ä᳊Ô®Ïã>ÍßõÓ;y¿!Ÿ p¿>;Æ$àÁòt0k‰€—2ò.hïmÐb‡‹‰™ù{4´sBu¦´å¾.;“¶OTá›D ŒðòªSP't ÏÄFÑ‹ª´fò†×ú’éêS˜åÛ`î>†­;`w:LT5´«}k[KjàYcÐ!'jl™ñ©—dk¦q·´foöÂë´)Ta«+­Í'Üq#åè‹Í3ñôNa6]x"ŒŒœ†EÕµøíé‘;þ„gY¹yO¶ü ¹Æ5¥°†ÎyJŒ¶$rS“Üh¬t §º]…¹yÅ]<¼tŸü·ÿƒæêâæ9ucAf~.–¤ÁÿÏËb,+¦×C SÊQÍQ†|ÿýŽ?zÃ|SP]L“¾ûs„xî>Žuc@¶Ë „ âÖÙçárn2,œ–„+f´¡´ðH\õì(¶ÁógpžÐÎrù.¹~ˆ¯tÂÖósàÏõ<ʎá¢Q¼ŽüGêòÏCöÞ1øu~x ¿ÅZDlìL0YpŠS‹®Ç`ƒ=8÷átÙ¿­à™bœ„³Ô+ý`­öFàɽÀ¨¬/¨¿ñ= Ô¦q‹‡Å¸Vú„ó—]ÄkÙ~çX¯ýoÿk¦@~ŠhçãçÒ°±E J/Á݃3ñš8`š†&×W˜ ºÊsØáÁqt×µ)ô»W&ÓvŸÕó¹ÛzÏ äÉ”/(‡òòÌä’-Y~¦Ž<ñÑ‚»"!½Ó‡®¹ý·%4ÀÎ/Ãøk×1®÷Œ2“Û‚ÊkfãÕÙôç†À½Ë…uzÞœiÕÔO…Á›O¸q”\3Ñ¡ ÒT@S‡¶OQ_öÖ¢õ°ûƒõñ¹ÁN‘d.Œ‘õÍ5\a+7ÙÒ’¾µ6Þ½$Þçe÷¿dRÖ`…w$XÈ$=Xšz¾z4þ·ÿ7ÝyÀe7䩾èÅ'^‚µ„y6õÀŒCK/ÑÈ×vôYÛmþ¾}q“¿!{VCáï²!ÞðÁ­x2[ŠÍø^Á©·†po_¾‡Ó]€1«³¸ðб_‰mœMD[±ä¨ݚЃ"NbÛðM[óÞfŒ`Î-i²l¶œ¾§vIBèã$êÂIBüq:îU5¨_ÛFÇ<ìÆ†/)œ›q'Xkïa7§Ü…D>ª¹úr§A”¶(<ÓjÆÝi^0ì·Ô,ñŸQ¸¨;bÿAm´ &T8Òñ­0_¸‚w5_Šî;S€3¾½á¢ãä0£ì®®b!mÝDÌM$.w%ïC¸´~A4œŒ)'_b@ö5Þ›SÀÁO•p¸@ƒw§Öå㘞†õ]¸”ßòM“Õ$.ÁsAs¸Ô5ÂÌ!f>öjF`ûŒµü-Gq±ÝVê±aO_/Ô]² xóΟzÁýßmÐìý$Ÿl"v w1T9F'’¬mI8oy֚®Jÿ/ÿW3”Ð\„;ÚvÀLgK˜R½Su øÜÁ°Ñ`/”÷ì ' ²©Äq5(xPŠWw‹“éé8˜q¾nƒ1峨„#Ö^¬ÆÖ]ΦVÄ¡Z›Œwðâx^’tßÁ8ÒÜVLl¾4ò-"æPþë)ôʬìŒÃ5îI¼3NPßFÓg«°6¹ËY#ÒÙÆÜЫ2nËxgZ蹊.Ÿ“ âaÊ,âõ,˜xÒ‘®éЦ†á¯F5¯,¼`zj³Øóµæäíí‰4rz>é»G„d8X¿F•®¹UJº‡æÁ­²±(ð¡ÍW€[ÓfhWû/ÿ5î…®nˆ• ©Nkd–ɹÆôUúcòjò Öá߃™,òb>.=íͦœ,àé?ìå\ÒS1Ák9EÊà߃C,p87ì^HÅ3Áð~;¶rÉ ¼õ!ij¶ç¶#]¯Ã9éQL0ËÒ÷<@§±nôàÉïü‘ô@˜¢Hÿ~`ïíàkÌ/RP$†Æ—6±Å•3X{ànx‰1ußr Oÿ."—Kг±þÕø•\÷Ý$gò¦ûïd¢“Œ©þbMˆPŸË†§×‚lÙ-è7½[–AÁp ÜáÄhízèYüŸþ]i>´î&)Ð_Ã4T ‰ÄLeªm=Ý¥C$1âO2¼U–, &ur‘Ô6º—´ÍÞ;&ã|±Ô|)nkÃÛK?‚¸s}WnÈÆFİÂ'ŠðÛq?ÌOÇ"~ŽðV¬Ð—‡«ÀÜçhKv³ Ó¶Ô 'ÙÓ7ÓéWÃd[ÔSü·ÓÂe¼XÃkvtÜqÚ°ØœýÚŸ…wÞyÒ=ý$q«®e-YØí¥L Ïle«6†°9)² />ìàwÕ…õ{®¡ìÓ("^•_}ô¨Üµz8Ì?Æ)p‹¸ þü—ÿW¯mÁ¾êÐð´‘ÉÄÀÜàýØWâ‹_'Q¶4†U"&òá0+U’Ù¦à1gqú¨.~®šÌÌg[±üÞ¤¥=™áút®ÌÅç’a¸L9¯Gßȃz[6]i!¾‰há>7”Ah{{¶5‰½»­Ì~Óóã‡Ê’v¸ôp,ÙÍ>4e²1çàb²&üŠÂkz xhÖC”rÚÇŸu}…Ÿ±öÉœOÖNï6í¯Wcò‚gØÇ¨ûØT*œ¡Êš,ؽeáìàΕ\à@ú¿˜âb³˜¿Ý]Üž`M#ó£Ù܉ÿ×ÿ~˜hpÇ­`q‡1±o¡|Õ„é»2Ùªx þ®Ì|æçÁ q&$äK?@ӷп\^5y⋽¥Pökg×ÿâÊÉÕšðx3¹þ/IMÁî¥CH+R MLš”îÿ€W:‚EÇìëÉÀkÈcòÆ CïÁß‘œ ‹åƒ'% ŸL‚ôðèK)œ’9¯6Tâõ•‹ir/Û³òApœV´åú·9Ã5:âH”½Ìàž[þH€¼²ÕØR A#­¨³lªÝ|I,´ÅA–nCãø\8ÕÆ ïø¿óÿò‹ä‰Ü˜»M¨[£—ÆCŸÌòuÈ—øïûÃÍwŸX?äÃzá<°èê‚UaßñD#mC—ÙÌ=V(ÂǯÌq…… ïܧ°ô’™8Ý MžÄÄÇÈÓS¸„]9C„”¤éX¶–¾o˜ÛÝ1ñgp÷e=ølt7:µò®Gp{&ò¼ý1×ûÈC˜o4ÞópÆ÷=}ücU%üØ»/ñì{müẅíJˆ…^5 ¹ËÂ1tædzV¢š›``GÖDw³þ…wEgààõz¼æÎ¡ž§%}6?˜ô‹òSezfá÷ÿâׯ¸« bÉ&´ÔÉå…ˆPŸøP>3lú£L›šbàÄÝdt·ëåצlÕøã`^:žªn³N¸dwà˶Ô]XýÕ›$´q,¨ÄŒÀ¿å°©ô ì F瓼VÛXóK‹F¼ÝUfj\Î"Yº*UÞæýþ‹l¯\º7¯•aºý.~UÇ?Èz÷“ÄfsÇ6M¡Ú7ÆÑ˜Àî@“ +ÿÕ‘ãé3‹ñ8ß¡úœ†A;j"[_V‰ÒN#¼btÏÍnÀ—·ƒ¡ér1:$§à²Ëá¾FóÖ€ö>btïUúØÝm÷OÇk?"vfÀýb=Λ4;¸H¿tx+¤‚®¡©ÂE¸fØ ûl•X½ó±¼j‹Â}@óu·ïš4 ßõ»Ï–BÊéC§ñƒ›¦z/_Š#9ÆÒLޝ›D> ¾ÂN·n¨þ¶‘¾ùt Ï=!  l|=~Ô4§‹¿t’¦1ÝõV2ï9¹™µÐv¥ x.æ\í¥Û(¸+”u¨ÏÉõÂýK…Xôugœã%ÉÿXh wWbÙ©tøm÷«¦Á‹UŠhxHÍŸöNŸÊB¥Gó ¸¯—Ìš§JÙâ–·Ы´œv5á·;Q|¾:ukR¢)–ÄÇg—Yì ?HÓµÏÓQP@–»|{1ÚŠœÄ²ä ôdU^KÈÿãg¦ÒgÅ`$õ»LhÑ,œ¨ý4Vñ_{ÊѬw8ãákö}p¾!Hõ.â˜aYèÛyd<µ “Ž'@úälnɲlüýõTÅqå™ï߇xÉÍ 2By.}B,øb?|£1Ý•¡Ív v‘“6úx%¨}ÏIJ•y¶`ºv{»b²®˜´VŠYÖ]!Blèi2Œé‡ß¡V.N?©/õXàªMäÅþY°qÞZ³c€üùRË­»ÃM½&HôCgã˃ÜÝþS˜°ìo½ïìsH^vÓ%c¦%fk®ÔÀ }4fšîÄ´ýŸá¼¿1C7úþáfÔF@°ä|1 M~õß÷CÂìû¾B®jãì.FèS WOŹñ{±Êä&ø¶’±·¢‰UŠ#Z§NFs#| /ÃBb”I˜Ÿï¥^=L άԴÁÌ >foâM›u‚Ìt¹ ¡¾E$ÎGµ®9quOÀZ…sÜNÙZüw! ßü˜ÈŽï¢u”™Û‹«d6Kæü”ÄÝ·ÖÁ`‚(ÛW{K»ÇÐqΤBM‚…¾Þ W$Çãþxsw#tX5¿Âv[’.² ¸‡ÁÕ0Õ u«êƒ‚ñK¡ðèMRùã%9±~Ý>µ‹mYƒV?v£B&æ\=æœ9×3©,«NArÝD&rëä|âÏ·p`Ö‡o“#³y´ÚK”»=ÓÚ¿òâ– Q—½¯ðªœ6ëø×EgË@¿pUi2$›¬ëyÒG›¡ú0å„ç}©JŽœþ¯Cè(fƒK<ÑJ‚‡Neèà ·p@–M ZADõ-/ïäT:n¢èªâ–¶¸¡éuº3ü Ýlõl·R'ÛR…Ù¿CÍðj0™ù|ÛŒ…èaÇ:tÐKn†bnå ˆ{/Ânw\€Éû˜Q­Ù<智Õ Ûáá˜*ngíE0öxËÍ:ðÇŸ(À:g t¸‚ð8G™9$óñäêlxäªHMùjP/ö‰Óq6 ›éó<_æçA„WAÐÁ3P—²Îé–‰P›ÔË9_û†_w¦€o] PŽD‰7'¡«ˆÏ©O<ˆaóÞÁ“‘p–f¦Ä×:3cCqØ9wʨš8&2P¼î%¦¦GAÞîìyG!ðÝ÷’éÒO¹åõ°ƒ­!Qæqðþ{ú/¼i©5àìˆäbn Ì ]E7žß?Œ—R›¼›ÜÙ‹p:x-½ªõ›ƒkê¶v©Q™VLs–£ƒæU¤=™-Ø¥)¿çPŸy9xOÛ =háëØã`34‘¾ºTC®þNA-%|<×û×|8Ýàp1fµ>ŶÐÓøiJ?ì·¿ ù1Ç Ð·Üí8+CÝÀr|$3ø†Ðòªì]Q­«?ƒÊËtI¦Ëvb¶£ºZ†±ïþxÄôC‘¶z÷:#NkÁ3îâXczÑÁ‘ˆ?I¦DzøbŒõ :-!p·lÿ’3• °ÊÏ”š.ŸÁ|d3,EØï¡uÄS#£¤ø/òaö™³ÐÓiK»ßY³­øsrÅKħZ{ápóê0ª9:‚§ó…Ÿ­ÂÙa6¨¾t%Y·û¬YþßÄDpsbV`Ú«^x—s £WqBËb¡^Y“…=ðÉtÖYú<çë°N¥ÍØ«#Æ.ý<Îrö6¢Áƒ©X^mÊ®Ïìƒü£–,Þ%4›-èÊhÔ½ˆJ3XrñLNZ&-ã”Y¹] ½«SÉWüˆ+‡càóç‹|Ñ–'àœ)ʬ^~ÁÞÔÉü•ŠgÈõ9Ÿ‰TÚN\ÕÌÿ2±“ËñVÆ™%÷ð‹˜2žü`ËöÌUa‰9.Sõbê¥Yá¯<7æòwŠ0é½q`Ÿ´—çôb =‡Jÿðâ­'ÜL) vr‰“ÍßÈŽ\Šc-ó[0cã}.ÿK:{œ„?4Tp…Ô²vÆ$¶äc¸vn&‘¬e¡.Ù—‹í!Ÿ ¬Þ§o¾L‚l®ÐË¿”™àÖ=¤úÅlð_b‚ÜL™b1G2KbX^ˆ#ç½mS¾¼?¸ rÇè~­ÈÝmB &b§¥6­y I ðWˆ%cÛv;N‚­y‚ÌhQרɛJ+¯\Ä#%`T.O¿UÓis„h¬cìš3Bk"¾_p‹:¶»BÈy90¯êÅ<{#Vr¾†¿óS8Ú¬Z@Eµì¨Î5 ÷Þë‚Ë“TœÊ¦¨)3S×NdúšãüÉ W úÙBõü©_ç@¥£)¹W€zEèÅç6˜kqǾj` Ë•O…D1QžO¿lážÍ+Ã1‰Ö<ƒØ^¬\ªÊ¢o­%¦ZƒøÀ÷“Rmæï,)æÞ?WB2QŒ†+¿lšî‘‚çÜå1aöúþúm°{tÏšœÇöƒ%°fN=N1¡!ý¦ì†Ë1Ò,× ­âßÀg_.˜Ejâ¦æ Üi»Ó0ôMuu©õ¡C\Ér‚ê!ÇÈ^U;\&Ï^½zÍ9å쇬J+ð»u2¯=ƒï£øï¼A¥à4Ê'êÀ½Å °E' Ÿ‡×álCkÔõú‹÷Z rKâ`û)/œð;mÁAâ§“Ëýð¸Ÿ2Æá¶ñ0¤à6dG—c³ëGÈ7ù†S)R …DxݬƦ~Ë­µªÂ¿G®#å_Å :¬®' >!;ûR!DΈÈ/‡‹â™óû@¥o›£|>µw…hŒ¦&ºž¹Ïí9}‹,1.§ûް±K‰ž™î L~K±YΚ´é…†oÄúùdfõ2æ²ö# îöâdn}áÂ{4á¥ì6ðõÍGa/=âÚ:–]4ÕÐöì–Ÿ?â†-¸é¾y8µ›p°¼¿FɽF„”×q3®¨3Ûwùq/]˜}K$¹ð#v•©@×à-<:ŠcD䘱’­×és¡ôçÝ1˜8f1÷]'×ÏÃf<·Ð}dÞ EÁ ¸¿øLþ­3U…鵸D>Åû¦S?ñ…tm’ ê*tÕ]Þ½¡zÐŽ>€_¿Ë°ãm|2GnÓÊ{O.n¦mý¯aÑhnJƒ{;h™æVLOX¶± c)ªÔãä ”Y‡Þqxr+Aì0ÔŽ=‚OðÔ×Ùt±k0vtþ$UfõF Ñú9tþ%ö·­g ‡p³›1se%^×ä?_qö¤}ŸíÙÄÚð(~!p½J…|Õ $ÿß5á&WÔ2 E5˜EøôÏ|¢SrÁ»‹5ÙÙ`Äš·ñÄ䫎ø Q‘›çɆ˜ P±˜¡~—ðóž§ãý=òDÁ妵…yºóðÑó)thžÄöÇ·õªÑ÷ š¶?âüÿ²“å1°@v2DïHbÑ×µX‹r,e/¥³FÖCzF'Ì8 ‰“"ûÈ®B[²ñ%E€Ž‘\q^CÝÚÖ£²û#q —ÇÎCÉ92,Õ ‰u‡8,DøµXx _ÿÍ@ {òèá;øm±Ç~ÀH5ø×x%à‰Èá–ý•<ç»ñäÒHŒ¨@Ï‹Ëðë‰[8Þ­4ZsC‡¦ÀˆìDê kc×£Ãð’r>79`Ç©Hü`/G¯Î–Å^ya¸ô¦ EžÄ}ݧàðGÚ¿¸‰Ü¾~Å^Ør#ˆó³a›9¢ô‚¬XàHWhƒS%؇iy$ÓÂ7ï>»î™âêï±®)åâ³ðý ôOW`{.åàߌ¢˜7†­ÍQÀ½‹KÍš Uß®“üXüeŽGÞØP9fÂ$þ‘e†ø·+„ï›H–ÍŽçߦ»G5FM Î<‚-ž,ö°/úÆ8âø*¸Qú­6«bú¦HP]£Ckg)¡¥¬ ~=~‡;´a¼²ûD®lœ«RÕèÙ»ciã3°¹ Ïå¿/à Z‡7 ,Ù¤×sqý¥~ò-â$§ eÅþ“ÏŸð?Ÿ›Ÿ~Šíñí†7—ǰýÇ`èÝ)´Õ|jÑӨݪ Ô4耆™mð3i;ìØŸÎN+z³¸•J°´7⼃Yî†lUé#áMIh–»DãÉÓÇ@é¥!¸žøŠ‹¬ñļ…tü˜J¤áRð0[‚f0Õ×סfÅ\œ@–T¶ãÉ¿ôi}! 3 £“üt¡láW S„(óûõEéloµ$žÖIZïfŽâ¬=µ*ˆƒðûkØ­ÜÇx[qstè†Ï-”=˜õ ̬·³{Ç.0«˜)T·¸œêjÄ›lîÂ$[öJs*½àPÃkskäDÙ¾’ÙŒwfxzWÁ‘^U<}u —ª.ÆwŸªÇbpƒÙrjf-BO‰²í›ôÙÝËr÷é OÖM‘JW þýsj>\„×rké\Ô†8gj–|Šî0gÃC÷ðÍje¬<þ¹†b8¿<‰™Êo€ÿÍ-·†'³¥¨Žñ¶û×XvÆŽýÌœF/¯[B«ÝÄᨅS)ÄøÜ“h-¬@¹å1LÝ>¼ØM®ÖÝAѹª¨yë¬yhMÿl§JÇÌØ1…û‡z°@Òì[ˤn5eØ$®|w¨\-Â#c>`Ó¡'è±I‘:\¹‰Ÿž3nå2M4nkçŽ|= Z³Ùû·2ô¾{9œdŽÚ_ðÃçãÄê^!:Êð©h·[X•ƒÁNlËVþyW#–ÝÑkŽN£G}„(Ï,¾VÛãåñ¥è¡•‘Ïš`í3#± b®›gËâ©(üo=ø™«Ð´µõ¸wÞZˆ›÷ÿÆ{Bˆ‰d“ó ÄÞ€ÎÐ ÄæX|;tFèêt)¢i†ñ¸­Á ÎøJÐ*£NœÑ$AŸ6.BÿëÁh¶¨s:þ º&,Æ“gbPöZ*dY×Àà‚`òm»9Õ=3‹ž1ÞÏÏwàN:˲Õ›ëÇ-W¤ > V6ÛXÑ„êнV‹!Óåë¯/Eë­¡°¬5Ÿ;¥ Â7hr!ßÏÌ¥ÆG³ÓšƒÉÕCú8œ‚wŸß…ÐU¬8ÀCWàï0ØÃòù]p•›cø“Aç_>º‹Ñı}xõ¢(H‡?ãB/ûÀ¸ÒY¿„p¯žL¦C饜‰ä£4ëÐÊA“ x8û&î8+WWËbÁM„̦.ˆF̼†Ö_R9óWUÜý/X×Céå£ûHdº=›·¼oÄÖÃú`NŠÅés¾-Ï’:§Á£›È¥¢pAÀþÜ_й¬M® s[-eÏš‰¾Ív8¿'‡]=©ÖFeú!oô ŒcA…ò@=ÕYÛS=æ,W 2cÕ˜àÄÕ¼ 3ŠérWѼ:–¸4­ї㨻Á+¾¹ÓP]¦ÓÂ_šZ²A°ùùîï¼@(=:š÷§À¦kåpöÊ}H–øŽÁ^›aÏDa¸±E•½›;QsÅT_Z-£:Z§ÐM¶£¼­wýîÓÈ­– ë_ì umÖh?‡n•‹bÖm¿`@·ƒ¿½°©«ˆcÌ‹ëЗUç÷íÂò°©ôȶ(öÖ&ïÜÒƒYüjÚ/NQÙo sUc ¢é³—®ôæû@ö/šûû°äWJl“¾/½v¤Ò=HêÖO`“äÉž>òg/’Æ£ŽI ‹Ù’BW5CVy'$èíbÏ_ó °à}÷6•¢`;Êuš°y«Ÿàûð`:ãêÊÃÖ™–e]Œƒ€‡Ð4—všÉOgZ#XL—Êr«?‡Q™ùÕýL{?7r1¹WpwPnÞèÎÄl.³¹™¢´{ïÆ[S i³iê[uêl¿ñF€ö§ܧõŒöLÌ‚¿Œ@”u {·. ¾›.  $ÑP _ë ³ š-øCÜ–eª Ó¸<(öxŒ›ÓB`oäUT~< ©Š•›'0€÷î;à…¦+ÂÞÏÉÞ–¹,þÐÅÑ’0ü÷8ì\3 ú{°Ì*=ã¾`ü …®sÈWzç*ûÀÂF1£<öü0`üçzôÁR7¦`=‘‰WƒNÃ"RÓ´œ­K^ƒK=üàp¥<µ<ö Öï‰ÁMK¥è×õå¨u$öÿ˜ó\R0<í>îjdãt=Áãzé¿ÙX7Ù¾/^ŒuHòÈ‚/¿EÙŸß™ ¦C3Å5è›Ï ÌÆþQ¾2¹÷£þŒžQ?‡¾o¢#ÁÅÐð²“ö÷ýÂE·#Hçv%êD®bä„Ó,ù×0I»vfÏmØËÔ,ü¸K¬¾¥ÎÍâÎÝ`xÓÛ«›bI§ý †‰ùytÏ(^|5vP¾Î~Jp%<Ç©4pq Ϥc̹9Ù©±ƒêÈ6Ñi'ߢ[Æx ”V†ÇËàáI=ü±NÚ’ñXÀN÷ú2Ôâ.á¬Ñü¹ðØžÐ¤Ý v0”:Ã%™Âî0aátúRâ6¬¼B³?šeÜ%ÍI seä¬<†Ü½Ã亳&–’‡;œ`Û¥£ì®ãè#Ƥã8 ~ÜËm, NpPœˆj¾~¬ §‚7©÷.N,ÈÂÐ ˜ð£+ÿ…­®›°&0 _.§ÊÕ·ÀÔâ5Y¤NÖK`0à —ÞËŸksÉ6É—Wâ'6œ.£/µ£F´ 6gü‰FTì¶,¬œP‡®}èïyï`[B*¶eâ©›œþÏTØâÐûî Rÿî ®à®#l3H¿Y"ô¤ã]Rpð&¹½O ·=:†64ÿC&Iø1‰hYí!"ÁüBƒÜ€E¿}ŽͲõasÇÙÑÈ©ÓÀróvxcæ©ëŒ,Ø"OÃ6˜ ç¹]µ@;0V® €e+†ñeqdo %/ËÔëÅUM˜ƒp+|^¬ËRoÂw™­<û‘Ønò‚üUèa/ šå÷‚Õ¦ãï ±íT&J˜)Ð=’r£uãúi8@ܰð× ¸îDÎ×B¾YÿÙJ öðìO BÏ)n€€* þ÷»|/Àbd ˆÿö[dê‰ã¸cû]ÔêQ†Gŧ1Îþ×èó’cÓ.ÞjFÍ>â_ ?l¤Ùz Ú~¸îßw ¿gò¦ƒ?Kü¡Çd³7ƒ³ž.ïÆ‹[ü¸77!µ$„-´g¨0€u£É 0_=µ VŸïq!?48?9”4[ʨ‘Âq|ƒê ˜é2Sè±»;Xmb§Ñë·‚¹/¦&P[ ÙõÑ_áx¬ÝÍ5ê=§§¤2aãUojÒùEä÷Á[µJ.W,œÊZøcîL+èPxƒò=Çù®9ŒúË­¼‘‡¿—!lK—.«!©ŸP!q<;‰ÓS¹[z¢Ô½ÅÊ ‚©×%ГøÒ¿äÎÎÝŸÊy䪮u8–|®I, ·}„™¿IÖÌCìüÁFîàªB²Ô/F¶€ü’S`»è5n¹ó¬ÿeá\_q0@ úíb=Úå0CƒÕ¨ÿ Ý|ÃÙ”à•oh¼ì+·Ca.TfHQ) ×¶±¥ÅDLzÅ4˔؎/Û1;_ŠEW8áì½_ð|+X÷Jpwggl%˜A°1M\ô¼à*+Dì,¢âN xB^ VÅ—CïćSèú]¢l—ä,(A{ÝU™Ï!X-…uymaµ¢Ø²_¬\Ä–E°w°Sjìw5crtpý¨,­J ±Ð«>Ï—¦çT‘ø¶pü2¡”r›±]è7'õu/‘»Cæ@µ® ݰS‡¿E=Eð¸àÊþ&ŸÆÃI±r,›×S‚J²tåMYmÏ"{RaÜ…@¢óöí˜Î¼*N2ŸYtØ1ip{u ^<Ÿ@u75“Çã< w—:J«£™ûØ`ó'žr¹'þ æ4xµœŽWÆã›û¸ðñï¶?áÁmÙ%ôOÞµœͬ3øáo7qøP+f‘O…ç¸/íƒXÑ#I÷X¬c½ôůdîkC96vƒR̲¦5…Òîà-é ©l U_€ßù…”¬ßƶ¨ÄœNøYðž[`N[Öà †JM!òrád†@9\¼WŽÿbÁbxèBìH”%‹ö ¢ù»Ûðíúâ%1ƒÜw¶¢G&ÌbBn騆VTBos £˜⊧>D°Ï\ذ´izP·9&œiË5ûH ýÅ߃žpIuÙfýžÔßàlŸCèÆ7xjì¢òˆØÎƧG&³KeñBŸDžÚÏꬓ¸ÚÜíwÎ=Hƒ¼Buš9ήdôp•[hL¬ØYìgœÙGxU©ÅmÁ»½d×þ×xõÁ$ÚmÌúŸ×pµÝ6Óšîá‹9qè*Óú‘'á#ÿwï¨-XzÅC‚³äü5ƒ§ùVÌ_(‰M ×W,ezoO`ÕÑÜý}Ï0 «ÛAÙï Ù¸ÁŸŠç¾±™f´b¶.ìÈOÆ;Wºa@‚å;·Ù ™cä£Ì¸ÓLü£½ÞŒßw_&É é™U;Èì}¦hTŽm2(yW™´_Äk646ˆ#8•¨÷dmR©8 „nãÀbÁöD¹ £Jqå¦taÝ×uÊ_:È(&üï¢ÀíËóñÕ…Ë`Ìf¦©à½&¶ú1ƒ°ùÌUmþ íöcÂæÓ\ùÁÕ8§M}t“¹Ÿêî$,@š>u=Nf­àÑ»º5<évpì„ µ”Í…Ðeçasþ)r—œDß§Ã fUÉ©•ÕƒTK J=»…7“ ³—ÄåÍ"4·qà-RÜ7½¯Â3G¶¿Í’ýó ãUYf8lxïZ#¬¯cÎN<[:ÆÓ¹Ç›ó‰ÿ%ÐM¸€¬Â±k$ºõ×›ÙŒŽ{d™âg\t·8õÑ?žd<;!ØÒ¼–êNXE%®¨Ñ¢MI;Y‡/ár>^‡w”²óÖr‹Â-•lYœ.ŸôŽ«ƒ ųà /W¼·$½'sHÔ·F}SÌ_lE+«~ðOsA(O¯‹ÎdÍÏßsÎçÂy˄Dz«||÷Õ3íwÀ²H ú%ñ=WÜ&O3ZêãÆé¡ÝaQl_GÍ‘wmkž® Æè8¼ÕøEåêà×Êj *žKÆDž%¿"zEÙ¦ý¨ûê ykaκý$qNîZRÕóÞMlÂtÕËÜð»YÜ’ëa =vˆk-9Œû>q[ ¥XçJ°ûèyŠìỵdê¾W_Cé{ÁpôÍ\<ò¦•hÖçw¤æÀ†n3~Ö¥mð¼U’•…åàQç,´:ÑDâvåpt¬;;­ú+@‰³eóÚ#ñØ ¼kœÈ.8æ_#ÐðD/9œ¹ $sNb£±1ü¹ÕF–u·‚|ú²¥ró¶>EÌvœä•:ž5®ˆÌÛ<LžÇή~ÅÝÌšF_» @žCÕÒ·Áµ7þ¢ýˆû°ìûÎãå\û\ÈØY ÍØIòeŸÁJlœ9زµ'1ãK1~$è6“(&s¦r¸Ø1ŒcE™ðñÓ \vy6ýÚЊMÙþð^&블ÉçÁTö.7À†Ùàß:áÇjzlÛê!ÿí2ʸŸ ¶ÀÄõ e÷}ŒÛqR@J¬ÙÉê>õcN¶­ƒLÖfêmc ·’k±õ¬U\ŸC÷[¼ç _à*šOÁOÑô®x ıtø°Í’V |Aš É¼—IÃðƒí¨aÃÉdB‹÷$®¦é·EÄ êB?“WŸsð•&Ç)+r?=Ÿ›%#ΫGx3nGâµe‘pçš$IeVÀý-r8üpÍ#l9Rź4ËGMzºe8ÎDˆsP¢¢$©ŒM%4'êàRíí¨øIÙ‚GÄ8ûM‚†i¦qáþýо= ‹%BA;Ç„ñ6G×$9ølÙ/§ÕñGV½ƒCãÃ1ã§¼x„¨”ød¿¨1õAöÇLš¾ž(cà“wZXÛ°Ã[ ó¹#Ô‹Ç <ê”49~.·ù«,g7äëgÝáiÝ_Àd®sòq»ðïúÜ„üä?ǩȈ.¨”è³RwÒš‡0rlQ§J5²œžp÷E<(À÷r.Å&›áeEÌ/ã¬G4Q×*›ÝóÃugí?Õ3 ‘½¸5ßžuÁjDûá`Ô-˜HcÝV“[¦±ìrÇr 5¦×¾DÁЂo¸2À†õ麱óÏÆò÷/d½®ÂÓî?Ý69Бiðñæ~ü»úJßSÆK¿ éN7±dû3ñ¶} ]¸öc ÙIšú’aÏÜçðý°6ÔÇiÚ1î¼ò"§˜ÐݺWáÛI¢±²˜ßëØ?%k8#AA£Âæ® ýqçHÎÙÐpZ@?/tâN´ãù$u”LV#YªùPUÝ%ÙР&C=Þ àµ]̪§ÿÞ§†p°¶ðïBÒdK¹k–rÜ‹\úúh' ^ A9© ´{š¬Þü¢Ë3šS¶}ct=T„•|ß½GñÆŽ#ÐêÏø½·pfø wu~§êÒÊÈ8f8–{§>‚–×p /ŽT†Ã^²ž,f…éÆ7^ˆ†Èí"jY¤ÿoœ}w ˆl<“>ŒŸ/¢•lÈIM •rÉXd;z~lBÃÂpÔÜ¥g‚þp‘>¦¸íW(Dka­W ÷¿~h¿Œt\w±Ͼs‚Àž)té-è¥þ¿å%ÂÜÿ;s¦Ú‹OÁ|ñˆKŠ.šp¼®8XR«H;ŒâÈög—ÉŸ}*øÌF‚Î?—hlrâtÓBqä'"ÿ” ˆÌS'Úù+´ñD;©U s_ˆWø“>ècþŠlÒ¼ªþçk¥±7a1§àWK9Y]»#Ò÷£æs°©º‹xŽ $™¦;AÇ©žó2ƯésÉðÜR>Ú–õ× 2B~™±km0fo'¬»KLׄáôìL¼`#ß_EŸ8uúv¿^É{Ž6?ãp®*g_ªGÕMÀnÿGÎö9ç¸ù›g†£åšc°ÙM…Nt_It:S½¯?ÀÅJ† ÿ CMs齫)8õø.`1_já’˜ùÃ.k9ÐÃý)¨uÙi,”'Göˆ²ðeZë+Évú)98[öCˆÄ(g‘”´·a A'悱Ø&#Þ^œÆ¶˜ ||(9ÈInú‹ǃ¸°m `·  $]‹kýïbï ;6㺔,…º°bÞ¿Òdä÷(°ˆ€ƒè¤£I¥žÛRõíGqÎ9¼G­·0=è盽 J:>ƒ–óy41§&QpÀÜ› o†jø'‹‘Êî(×GÍÇ3)šñσv¾·ânLq¤N_k¹òØZ,Q‘fŸ]ó¦N½‰³x oÂ3=ü—‹_jÂ4'‰Ò”¥/1ç¡ë¯Elw‹,LÙp¸Á£œ}íU˜{Š´x†‰CwášO(|7ÎåÆ.W¢ÝÜW¨èjà+$âÊP-öô³4í˜fD½3}¡ëšÙóÞè;aàóL¬pÄó«&pgF&ÂõʘfiM—ÿÈÁJƒÛ8íúܸ@í]QBfØ|ãÜféÐV»`<£X„Q:²H÷ÙâÎÕ— /2¹]³°œ¹Þ¼`«74÷²uø[¨~·&6ñRœ@äo®Ït×¾Yˆ©]ÕecS÷p»Ê>ó}4Ùùó«A¾d »9cXìÖ§öµè!ê[‚%Å'ÑVB™;%”‹&mÖ}þ>WVñ†~hÒÑõð“BÞð÷ãl5‡‡¹Šô0[pÛBÕȱ[îTÎÊ€å98І¸Wð¿X¤hªågx}TŠùÉ—ò3ë¬Ð-pÖ4v>TƯcÒ¡¼Ú'§†€ºÓ^6§+ùà7É‹ØñViWC|œ%[ò­›]£PvÂFê&ÁV¯·€{m iá3IðnXjQÙÜ+¡‹¸­·L¶tâ\—?$ùH ÝÞ)C>fâÚlm:¿b'½û¾v‡Ù÷€ýcft¹±^bÅÚCˆí2J?ŒÆÎ&ü8³@¾Â³MTýH#I‚4gX‘&Ñ‚½7þΰÈfoå „Î᎒al«åÕ?Jø—Šû@ÇO7)ª*M&ÞK´›ÒYŠ…ƒë\NÓKv^#Û+ijîºY9Ž-zcÇFŽÈP“­AìšzçßmΔšöÁO” UÀÅH¼þHœÎo Ï]ºàk°o]-›BZ0ÁÖK>¯`fÑýÌY wÝ&C웽ôå«%ð­öšÿ2¥]¢Yóì[d‚û N/´šéI²þzp üýéçœGXì»#2ù4VÝ]ÀwRð=ͺäÙòe.ÔEÛË幯gÿ¼âèþŒÐÚ “Ù›ïÝ"¨p ÷ž£'´øÐWýž¯ý¬Ý>m'¿ò"ð£Âbl™lÀ¾Öާ̨v>O`ÿëe3[ ¦žºv†ÐÂqÌi\Š®S¥ªc©{©:[X>þ{棘œÑŽû'‹Ð‡u,a‹ uFgÌèŽAÔ®tb:I/÷tca®7ßþ•¤/›Fó6ôCÉß<8æîÂJÚÝP*m=vÙŒ3üç€.s\èTÁ%ø`‡û(³œ–•ˆà´ì³lLÊb¶Ú¹‘Ö‰<âéN€ …–ýy£sA©Èëǰ¨¤E°n캄x€ì¶HZ¾Ø˜­^TC'< ¤Âr,}8Dä½ U«¦ûš/¼Ûq28.!«Â“pÙ=òÝÿ.Ú<6›Âìcsw¡Ù´ ¨úÛÅ™K ÂÇó71gÖ3,¾ÄÕï5`¾úadþÕC¿EøÐuzb0þÒ3èÿËÛ¸W~ýØNßáˆÿÜ“xX$¼3[°ve8ϰg yuàñ7_Ü#Ôë§ú·Ù²³¥+ÈœCGÔ  ý  Ú(|FÍýXîÑMü¥ÔÙÒìÛpLCÌ_ŒcfYÉø¡Ö† ò8žU¯ÏÈ6ÿÄîåŠÿ©1QûúDc\š|Ž XÒî_qÐy_MMžà!cÄÑ!·ZCäžp>s  §œw,A¥Ž¤^gUÝ[ÎLzS#˜ªÑIÇ 0q’kþü0:•5`Ä[3šð•[ê"EÖÃuî |»ù #%ŠájBú,<Å Ù®"þzÑin©lˆ4ýѺ™öÎPeRÇEÐw‹<½Ó¹f}‰ ³k“‰·þvèd—ð¥ƒ)u>›É]Y‘Á»cL£_U`©Èvö¿9oi9ß.5!Wø;ešÆìjA¸Í ¿–êÒM×ÂIãÇùlÓ51ö½âY,²ŽöM}“{¾SûqòXõ3>OúÈy½ûˆw,Ƴ¶éypÞyp¦žs¿Šû~:ï¸J²ëËÄyä)§¹² cü+ñdôAŽ÷¨ŸíŽÂ·ÙU8UãL»àk{~NŒÂѹ¸Æ°JŸ¥ò¾~Z F[¯ðÍ{΂Ÿs,¤—g³”‘,xóþ%L-æ:EÖrO½$3©i¼]Ú’‹,ezˆÌã¶}Ñ…ÅÏXl¹¦#øxÙ'.¿?åÚADÉž‰ý~‚î‘ “fÄIA™¾7•¡¼«ÊÔ§Iw©Ÿ‡‡÷ L`!îâ ¿!øV Ñ!O}¢2Žjù(Љ5ðo÷oÂß–  {!ñÊ%²îKŠ’bÑVDä3ìé£ús\|é~nÞúõ ¼DŸæßìæVZ™’ŠÙ&x±FZÇí¿{ŠÆ~†õ; A7cƈ 0ç…9Pñ¤Í©Õ¼7$ctàv¹ò£…˜J˜ÍØù”ÔlO`§^|r(þ7 Ó©ô§jØ¢²–†‹Ú³ƒ§vç†àÆ”©Ý«÷|iãmtM]¬ª€ªc‰-é@·}òÅÚHæiÇöØá÷‡^po•8-ŸY¿ù¸ÛÌ…‰ÏâJÖ©3½”è9¸Ð¹Ïïs'fÚÐé,0µ¡äy¸åˆhÙåà›OW¹›ßÎã1å×0§Áù‡‡àéNbêc‚ÇMMiÅ€3ff=…Ë$$o:‚š¥1Jf¯bãî†À²’pÊt‡:ÓHïo„ˆ3˰9Ef žBéNÔægjN; ®‡ýèù‹ìˆß^ü_‹h€é–eä ‹0­OøÅéHQy{u¬àƳËD€E9¼ÑÏLéÞ Õ7¯šˆ€üG!4BiømŽb߯Ql½Ï“rExåUH**äÑùàlÒ™v…ëo+!í»ß’šdÈ]+Â&¬ËázXÆòHGÄ%ráNx`›>-=ÿ×èÓˆsÓA­1–Çô“˪A°(hé~±‡NÀ¹°pŒz®Âä&FBÁSazÖÝwex°NÓÛGC ïýq&1n3LUO¦-¦áðtÇbÎÀÍ”™UîbÚræÀ;É1.tÇ—…tyÆ?z)‰Gîžf¯C#¡Ë;šŸÄÓï¡sж2ë[؆òyp3Û„­ä&ó>¼u¢›ìµÙ,I8ì§É” ßòÖVÍ"J _ÀÁ™ßðÇJ [dNebdz3‹ÔXÏž”ÖvèÄHR ¥n©w†Í¦6oÁ=4›Ü»É›{ñ!oÁšó²ì|x0¦ñôñ½Ï×¼ÓÈò7GI©Çt´9æ‡/¢×­ý °Ho,>Ý À-ù¤Í2’‰š³ oÍܦ-…§ÿ¸á¬dMVðóä¶Þ„W¼j’±=V{ˆó­5Ž“óâî\ÿsyœ”–éi\_Zš¹År“ÅŸÂÇg \ÛÅI´áð*øs/Þʳ`W1DnN†' ßqSľ{óIŠrØýë6 ÞÌilý–2òÆÏiSïN"Ùÿ?ª¸Â³SZl¢[ ÿ×™ø"Õô—s¼Ô9x;S• ¿k€ß·œÞÂîϓۜ؞ÜÈî:<:x]×dRŸsˆõx3úikoR®®&ùgÁºÍó°móLÖùG[™¡WúN2ò|=˜¥¬c‚&[ÐÝ^z2§à½mY(œßMŒCÏCó=¢“׆½¶ÓX¥ª;ye" YÉ­ “5»áêï±õfÁxv‚ñ‘“Èÿêóÿzž¡§d2÷~9ºMç=™ªÄδ,cŽÄìí{ ÃT$.aÀ)j`n ›!ç¬ ¼=aŽ_ÊÖrKÌÀ7dt:rqIeïlÇ#Ø2Z,âü¿ÊÓc ¾˜¶æ ±þÀoPˆiïÝy]+aÄ.qWÄ,A^_ú]ÚÝÕúôÓËîy’)4ö¸€ßUKÜ_ó?ùŠ•› éO¿ðž*[ÓD´P܉B۬Ȭ§à]Hq’Ôf±.Ó0Ú&ŠüÉÀN'öèI5zÞzÀEª¯Àóª‘ÔÛü_ý·ÇðB/rÊÃÛû¡ÌJèóè¾³§¶’ë—ŽÞrA‰{¶¨Va«ÏFr¯»¹\<uV×zccQ6„J® Ê^+p·‡¥º qÕ‡:çH¡ÑÖÇDúìV¾‘Ÿ‹¹&FôÇ«G2ئ~OõYWð#´™‹%~QT4h›ã 9¼ÉT-È?xâÖàCìý(¿›pt o‡å *šF‡ÒgCƒÑGÒ- bø œüºhfŒ,]4u[ËaÉ·Etw‰?•½µŒ,|Šª§‹Ñ' Û}”¿ÅM£ûNc›õxZáò¯Uëó1bcG‚èôXz÷g&ûvÒ— güÆcz´öe(«|ŸÌ>}ÏeE!Ý\Àšs ÖÃ;ñaóü©GœÏ€}ÏAعà'©ÀaÔø—Ìf•äÉ Ñ?×¼ÙòÆ6zü_.¸½ £ ^ø&Að1­jƒõqÛÏ9ôø±.ÎUmŸæÞFî¼';[¸‹N™|»8dLoäN§ú_[°áv.‡=ñº)ðí®øf:ÍèœoçÙ>O¬ÿzœ^›×ЦžtLÏN\í>ûEÀý•Ù—ÊÞD2õìBOo¾ÀT*|à+ñQÜNçñhÌõù¯¥ αÛ#0M#»^Àf"ˆÅÐëûšpÆwµåF×\•@𹈯m´Ø­+s0Þy<ì<É–]£áÅ”Þ;ò‹ ÒïÂjÔêóuæ³ä}VÓIãõibí{T8ŽÖþÑëòÞ>Õi´ËäDt̆òfGŒÝ’2íqôÇ]._)•Yxú›Í[¯ ÝŽêôå™”Í]ý’H´ß§3•dü~î)º¹€n·ƒ /¸"»(ìw§«;Lèœt’×7òMÍÊrã²–÷ál éš}‚˜†ü‚® ÷±äÖV"ýË¿×ßã-kYM~úýâÞ¯ý¢ûlYiáe–€¯ëoLò”†z¥F }¨IWºÌL*ÄFfÍ¥¶ýÝœýüÍܰz4™´Š.™T~«éPrîzÒ ‡nF³Û{°CTÆw"™è¾–^‰ US pnŽïcC2¬+Àm¾!`ð"¨¾¹| ºŸŽÁgBtú\ÀØ)VthßWîÔt/o{‡4=眩¿Ë'%ªÀFet¡¦/íÙãÞ[plM9,~~öù±÷Á¢ÌýÏ&ð,1Áéæ_IU« /Ñf5f.È!oš¤ò¯ Àr½.^г-ûþ­ŒëÕîÁæ3íÜÜ£!ŸMÂÑù\ŒV4¯Ñt=Ã)Ò7Ë•Xd’ m./­/ú£ÍÚpçso¸*S{MW‘¹íÓÀüúQ”š‘ÎK—Àå Êè^\Í¿ÿc+}° ~ŸT£ב–±Ó¨Á‡•,ºA’î㟡åYW±¿Å‘ΓõÖlâ p(Cÿ³kÉ;ú[ß‚‰höBü‹}\ìDMÆóÍ¡B#ž¸¤OïfÑ…ÁeÑ5ºÍ?}Qpܸú :ÎÖëÌ«ßBçAlÖkLaUºøû¦\öåöt_Ï$NôT8®UœG¯IвÒÚ¿`'ßBŸLe­ÇÐåïKraa]ÉìUG*K4a_÷?LZ¨É‚÷„Ð:iEšVÑóCÙ¥?šLtß;˜tÅFmœB=&®ÅÑø¨Ã][XèqŠóqù§6H< ?vò0©±õ=rZuÆ„OÁ ÒÚL}ËÎR¢› ¤äãWÉ›¬ê–ܙɂ7=$ÑùO±1Ê’D {Ce?Kx¼•üo¾Ñ¹ò¸Ú5p ëPCÅ[Tõ(/Õ7OÓðX¸ÜàÊ2KBé±”R.ü00áÓ©ì¬é46=W—e»FÏ/¹ÍZóËÙòyÛ±øz…§;YÒ¡zÃd#·%‘¿såiùé×ÐÿÒ’*Í¡tB­À\žÕÃ!î‹8†­ÚDW©yÐn öÇAžn?8êódèÅbIzü“0Ûõý'n¿v=Ÿßq6ã/ã÷œ‡po?=¿&Åî6â¹—D3Aª¬hçÌKTañb;:ptt¤ÍaC+8ÛX1¦"ö"lCH¼ÈìŸÁgßr.C‡À#d.?¹ý%ìÍgz«mYèhÕôO£÷Ýw‘«É;`Íß™{§K±ÓUyÃvÞxRz×_'Äß©r…|èw̃%[#Ð[ÁìæŽ…éóÎc”)X†©pr¿ÃL„3?º˜Í¯«“ó(½Ô¯…‘©ÁíÌ ¹*||öƒÈ¡˜z®žlT‰¥üçyܞǮ¤áW3<»gGsbÒd Z$ºjM¡?V"Ç&­FïìI´2@Þ¢Tv b¢” ¹ž‡;†RT%Bt;Ç;Ì[ÌÕÉÏ'æ7%¨Sþmîç7qZ0F• ‘Y8ÁDfÙ‚…Š×ÞäE÷°?d¤ø»õ™ÀTfú|5‹ý„e‰pØ3¼/Õ /4;Áôæ0+Va—ÏHÑG¿ä‘ÜrãÂh3zžè¶ÇЭ®É '§™=Ž8×Ä…î™ gSWàäçïÉ"¥Bž€—Ë(×µÅánÅïcL/çl¼¡Ç¤tV3ÑõæìG—.KˆÛÈUš&cãËf^—ïv˜¦Vívað]Y–ì‚u\…YÆ·D@•`HlÂd-sXþRŒ úë³q ®€‘ÃuþÊçѼå#wõÈP[n€ï7ÉÑgçÒ™X¿ÎÊ*…e68b—Œÿ¢¯Cø+ž„º4Ùºv3,+«qѸ߱Œ7íkÎÑ&²V*|/yzÓ^s„,)fDbPE,à©~øü¬÷¬Ý‹Y“Îâ³U¶`Þ’Oä'Že›C”¢,³\³óî(Z>.$ÕÊvœ¹µ ŽdÞ‡Jª…«óÀ;¾ßiøÑ_6ZØeŸ…·Ï@ªO1ÌõœÂü²ñìç)ÜæjÆ}uë#—<)Ó_  Gž«A¼‹"&íèåxÂôOFÜ|T‰»n‡OûÀpõF<-oÌÂßÈS^O1|ú) ž+c!í„4-øÕä¤|É•¹‹@{÷œ÷Á™o?[ ÿ~;c¬÷föÏÀ 4×V’)ƒø%{6î5ëÁÝOõ@ÿ«8]—.ÚlFÿÝV¢Š¿£Y˜!ØÕ9@íLsþ·#Д«A^&˜¿$Ÿ\S¨A‘[]µ ÛïÃÙŒ<¾XѺY·Ì«Qsn•·x‡NÙ]xèF}w„Ï.«©}åOLêéãÄÔ'P‹s•\ܦ|B§9ŒŠî Ônò8u­š ªÖÐäºuìJˆ*=v¼–Mš  êV 9v%éí¿‰‡[£çæ]´ t&äP æ&öÎí‡ê™‘ôîE?œÐ×"²tb‡*Õ°zNmÛö±ó+"ˆÛŽœÕŸB˜lÒÌD×È1ù ÎÌgÞux–Mëí¥C#gÙ£º,zªD\+²E¡lÑáSt­eÈGwáº|;–\nOüY_ʰÛ'‘lÿs3/å§ Ë鯅?I¥Ó<ø^˜Î\”.À‰å-p„â{]œ´°jÕRé”둜ÓAP G–v<3]*ƶ«ùžã8MN”%n(àKuá9 ļTúÄ5Üó§Ð«u²ìþ‰gu­Sl¹–‚lõamv"xŸ}{õþÓÐ Y`u8 mRœ™{ }ËñcMGײFÃ0¼çÿYõ'æ¼ï4—dÁí [z¯$Ó$)&ÊÚuh]ÛDúç3ŽGáŸW¡Ôâ ÌJR G }‘%OÏ5ÎgÁs¸Ú9¢4éÝ5(Нõ翱u¼,+›’ ƒÏ |²8´¾Û 5Ö<´rM7@ŸV6æemÁ'ÕlVõ,˜®IƒîY±Æš±¼;­3¸¥}DJ$ÝW†‚ÏZðlý)(÷·„4—Ó(ºt "Ú&1û¿Ç¹oíSéÓüF¢ºþΈ çßèØÒ®cX•¨"}÷8‚û_/uÙ²8kÆSÎÅ÷­ÊnßXN-ã:0ëÀIŒœ‰£Þ‰hË °Ù§[Q» ÒxÚþž³¯¨ä׸q8‰+\N™ÎëxúÅòËî‰aûvL‡y[cp‹Â/Œ¿pö'Ýâº/%bME3zîYDë‡v³¥†…ørÊy<ôú¬è½y*¿¥<ÇwwÔØ“1O‰ÝŒÜ€ ÊÝÏzömeàE¯l`?çCî¹üÕûâ¸Ío¬Yüº]`p_ˆÅv\ফ–`ÞÎ4p9ÁD/_'ª×@E’1¬Ò‡Ú0bV)ÉÆfåbFQ&;bµCðÖßQíCyÇl踽£õ¹íŽõÚûWާE ¸6Ò –çÁ&ÁXa°ôÏ Á„z#z ÊÃeØÎytO™ëÿúy¿ÇìÆ—è‚&­'LÚÆ"®ˆí>†eÿ*Á.8DÙ6âÜ©ž üN–f]äÈù§JìîóxL]g ÿuâös;ñ’ò0)yw‚i>€¥oñ’üsâׯë¦S•É ðä÷±™û<Ö s†÷‚d‚1éR%ìâéAxã³,áåûTlõw"ã$çqMÕø+ë#zulÁ¶©Tù],h¬Éí;ˆ§Ûtú«ï ÛØeÈ\wçpÇ}š¡I4‚JÊ*ÒéËœYÒÍb|«ÕÎõ\%:3ѯµäÈ\iø—-€AJ8tM‘…ÜœË9Ûes&ÜQsWP½åQs&˜!ÉlÂÁhDÎ&8±¥SØÒËÙ\׳Ü7A7àÌDè¢YvÀÓ„•EʱèZ 6þü¨sq`OžÏ­ghœ7•Z,u„1÷§Q½C¤?5Ê,IåUº9 ‹©}ȧàÓìÓ¸±7¯"õäY†ñ’ÍXwâ8zÈh m½L& J`d½Kþ.apcüx?ÆU“YàÖ÷D?j2(œ˜ƒé’£ü«¹?™NŸjÄñ¶Y¶`óYx&:Ti@©‰[¸p÷HüÆñ\‰Û5Òù};ÍQ£©³îà®OáXÐg%ý—Àkl¶¾g•%ùØ`r8ܧËÖ‘É¡%0®z* ¨á9rev3®â@TžH¬ôŸËaΤeP;|dOw‘÷š®¸b{ñ›k'νF¿ðé¸ÒÌ“;q^-æí&ç<Àröa(ÃHü•ok&ò¦ß#×+2IñwÜÕcp·öGlœ…MI³áîþ·¼¹[„Ø¥`*B_íN¦s¤þBMêz:뉲!×xúëeعdÒe]O1gÿ=9ÙAܺdˆ¨dìäRºÁýY]\‘ö6,ªi}nqÎ)Yþ âœç;+ ¿G’Š mÄšW¼+%"0þ„xŸâ ËáÊçVb’— §‹ÒW¢ï Pw)‘”m&í÷y Ôó–˳²ƒ$ÁJ^íÆ¿’!§ãkIw4ùxÏæì·ý1Jp'å'ñ~¢ÀÞe=†'zi˜'x„ëÙ[N*lÒ!GìèãpåtÖ…ïõTþR5Ï£Bü†ÇÂqï+¼|éïüqTjÓQÐØ¢AÏušÐÂ[ò†æ¡àÑš:É©Šxs*æŒú©>?'ˆ ¢«Ì2¶nÄÉ6ß&!„qã¬[pëó/þ2X¿Êˆ…âöŒf”~{škºò‡LþÓ%BPã{&™•b‡/¬ÒùÿÂ7õïäèëÁ¼ùWžØK³úy±¼­šY¤ØÕ¯ü†ô¢#8w[ 8x©ÂÞ¾7¨»R†iÎUÆ“y¨9Å{À_ÉåV–BÃZG´«ú~ç~“]{´¹™É^Ô©| ›±r9»Þ;›Ey«Óënç8åë×Á`7ƒݲTèÅ86ÞÙë ÌYùãrPx»>‰èp]._à]Ä"nÍ Z¬Ž”Óø^&qj~˜;ì€æEÉØ&²=ŠÜ›y‡e×âY<·Wn⇇ŽâXt:V7ëc¾„›^‘šlŒ…UÊ}¬. 9þzäKí2Õô§ã i7NqÙÖÒጠá{¸.™>ä¿,aŸÎÊÿÏOaé6”™—ˆ§. °¯upmj=h_Y÷tE@;®xý1 0 ÄšFŠØè\i\F[š±böúåëX"~.ŒÌ8 5¾€]• ¸ÇîWòÔ×½#ZÖÜó´Gx}ÞxÞ÷ç¢Ì7¿ •²Þ“1)óqÙÔ\ÎSǶ:WNå šUÁän-örTKÙÌ»ÇÉy«Ñ?ßH߈„¾Š†*vÍIì…­£†3ÕoBXÐTÚc@·Xþ·‰4Hê!XmI†ûáG¡îÈcxè·?˜LeóéÒ†`\ÿU‹…ˆ ð!Ì’˜…j 1Dß„y)Œ‡ú¶E.²žMyô‹·®ß ó1åá¶…‘ðGL¦ýãOú@n¼äGDþ!»¬ÁPp!Ø‘ 1úc˜x“;›]Sî`!]r³[Pb×Sð,*Å¡ÍrÌÓÕu8úA³Ì!L\¤ÎŽX«Ñ¿%J’Ķ«n‘¶`Ž 0&{¾³µ8fk*Y`¯M=Wµ£È˜{xóã¢Qýœ†Ï÷/ÃÞɃœü¦mÐß ‹õ‘eª³èÞËnôJ¾ÏÓÅãñên¼2–N×®ä®ÔŠÏø¡/E·¬\N›W³L-mÌ:¡æÍû¹ôþ–÷ ŒdA¹ÛK¬Y©„‰›òüö…cÑ{Ø~±?SdXè„Ýþ»ù•U}ùM‰Õ®œã  ß> š}ºL·ŒÚã¸]þS¨ û0>îÄHƒ¢¥àkû—_ ´oÜz®úŸ-)½ÅunŽàÙ¤Çnݿڵ頜| hÈ r²RžM]jÁÅ[\þÛ†ÎK%!fÁ3l|nJ.8bžãuLü·,9Õ ! !–IÐ1£AgGîQȃrãÛ°3º^ü,A‹Â,2cl1Ü \lw 7²A1m\$)Ã|¿ï‡'ÆÿïÜæ/­1îß>ˆ; ;N‘YI˜ÈX¶5š‡-žuðµú$ÌöÙÆÔ^-ÀÜñ§iÎ¥W R¾ë§éP—î`¾ô*RÃQlÐ:ÍI~íA©ò~\7”CÚZm©äuxê)ÉÖY‹áù­^°Ç'×—ÆÍ:þz³ßÜö£óAg¬vœ'¼!±ÅìA´6³zSn–†èU´Ž3wÓTúI©ï|PµYK<¿t׿´€Âî› Ïɳ8%‰ùñŒ;Ø ‚’]¼ýkeáëÚ`¼ã§MÙ’&tØATf]ǤFØæíËÛìT:Gò EfJ4΄gš‡ØvOuxR“¬Îñü„Õ‰ãȨqÛ†gDÅÑÒÕœ?Aʘ[Ýî¢äáë7‹„ᡞ?õŠ,J(ÖèâŒWqX{Îk¯áAWT1ðÀ)|eú4õ#ñé¾ÕòÛè¼õ ä’ëY¼®nÊzfIs>od)5B‰›HIÜ?²ìMŠí¶À}]¦øBÔ ›¥´]Ñc–*»õ´Ó¾éÂÛÔ= ÏíÍÇÔß3Ä´"Hùò¹ûz=ÌW„;WB݃í$fç䟙ËÍ Ÿò{íÙª?"ô‚çdÓŸ9ã Ôÿk²§–ù Nž$ñ&ciÉïB2¼ç$þjÀ¬–`øV µ»ÌY[…*Äï3‚¯?D™«TÃsËÿL}Ä÷)Q_åîzTb ·ÀðUq¼T&…Wœ¶¢áw_PøÙƒïÙªŸ±8£Y‘î ®'µw”p÷¶[&ÌÄE¢Ñhl ·µ3„eeà”|çü¿<Ë¡ÕNyÄîܺ`élxTbBb;Ði±¿CêÝÔåL$Mè •·hw^›¬³oÃw+ÜÙšVC <û&[F“ AœÉà,6V«o‹Œ¯_F~ýa¿ùò~Æ(¸[˜´%CÕêbJ¾’†ÛÀ7bçeS…;c%бó&¿&m‰ƒµæsØ1¬ÁVþLþ¾_ÃJ†A~Љh¾Å5}g ­ÍöñNp2¯ÂA`½ ?3Æ©Sãþ¹SàÁߢÖuBúør(^¹ë€†iþð©Z´T娍 ·I”Úà¦ÍsƒÉ™ã©©ÈM²Mj9zÜ)¥¨+äË¥e¸P†Î–ÁåPu|÷.XœŠe2 ùp@ê, h, âßsëEŽgÞí\ÓoJÕ²7ÀËœÕTFñ5Ø—¦Q›${䙂›|ã%8=ùÿãèÊ£©üºpÆ2f.2V†¨P¸gŸ"*T„Ò€RiB¡Ñ”y,*D*ILáž}šE)MŠ”ÍÑÜç÷­»î?ï:gŸ}î9ï~žg­gíË' ~N;\ƒ­o¢jUFæ,æªÅÕì¿>У¿nAñ÷ßQ*¾/ÜWÏ[‚ÀAxÅòÅˆÜÆ]dç|ˆow¤oGMÖ]0AHòÆCûb`ªõ>¨™äË›:³q¬ËSÂ=«„–÷ÇÓÎ ûðHTYk$NCŽtàEÕ.¡ø¬!|W®5/¼Aª¯£é%F+ñÒG¦Ìâen<„ŸvŽ¢ÿÂ#ùšCq\šþ‘“¥ò—¿³ØPŽë³?ý1òpìÄá&ë{xtù0>´EÝx‹‡‹d¸¯í:žð[&“MErájÛÝÆ–ímDOJ T]ÌÉ¡Ç)仟<îÛq ÿŽô£WI0£>ŒäíP£æ1~ú&lÜa…³á’Î"¾ËeºGüÁ> |»O”/ýÌ’²‚üÏ‘üUƒXyéóôÐQph~$ŠÏµ¢&Š™¤™{Òå©Óªwd}‰2~ ù‡?]KAíš!þ=°œ*Š ‘ƒZÞÕØöÆ;À×[{qIvè±[aMäç©;d¡ô»œÿ¯?3ìÚílKc•Ö<ô2̹#üvÅ•/]²Fø£ñ#,p©‡fÍÏ`õN‹ÝrF÷óÄI«+?_I‡ÿ™°«» ·™}&÷]*±V(Æót›ðÛ$/®òq ± ¸· [Ps·ÛGöûY=z»i#ôíC‹1õд L¿†O´å¸æ»›¤}Ú;ÌšŒ5ÉI¸‰Rñ9¶ïå<Õ» $B”÷&”ï$u/áðš¼Þ–Ѩ)²b*·ÑGöμù²8ÆO@¹0ûpPøH†5¡­Á†|×-^WëÅæQ¢ª2p\N› sAɬäOƒp±Ý!ÐÍÑä+L‡3Èqç¯ÏX@ÚÇXPZy‚õnÓ;Y÷aøêѼúï Ò;kÜzAe“è ñt¹¤<~ªÏOÌ~âxëÝ™F¡l~iô›¨»PÂJœW¤R» >x3\±O¿¶ññP&ä,¿SOµ]‡RžfÇW^,E›ˆ j€Á+z„ã^÷‘Ñ*‘|Ç{IxÉî6´7Mß[í‡øÉ[€G>vnÖ¡u¾Ô~­/û6oEõ}¬UÀðÖîxxk÷ÔNç^»’KYb¨‘_6ê¢ÄRø }¤0Y±½ô¤ƒõÈÛtê-Y/SIx¨(P¨‚[mÁíXø»ÅT}Ø|è-Nߘ½‡+›÷¢Šîî݈¶-i(û}8Q´œÉçiiQz,\x®ã ¾pïc³î÷‰ºèØ2 ¤jžbuÁ]ºÀŠ.½<JV»£Ø©•()>¯Ju÷f›ëSùØ?©½L7~çEê±Ãv×sФª°µ®Ê ÄÓ-ŸVòß{Hü Îœ(û’eË÷â§iöPëE]wŽáÏSoÃÏU±ü@Þ"<‚ËsqË;Uºìé}8Öþ 6«ióÿüÚ#| éʺd*Ú^[²q¢lÆTï¤ÓÓ©ñøçõdò°Óx"ù |³’C9Åcû]†Ï="àzŠx%ôUõqçChÒë™gÁgk6ë¾´¯ôÁ?fƒñ´lí_€Çú‡Ó#7øã J~¬SMý àÏþbòz¿/ÖÚšr͇—¥—Y)ÌØ²†åŒ_‹æ¶à8uöîlܶ݇>˜7¼qÏ9_tX3¨/ÿï;ÞÅlÑò~&« aŽ;Ï‘¼˜pܲhOii‡×‡eÉ©½ëù݆JözÂ×_â„ÝcȘp‘†1ýõÁòì†Aø†·¡y–±Ö˼×'ÕsCÿÚAå“¡ø°ßôÑ„z“¢9?ÁyO ö’aT¬­1 …÷ï–ìUãø¹903îlê[J’>lÁò£Gቑ¾_¡Âoî¬Â뎃÷@æ¬ð|¯4ŽdœïÐÇ–¼w'únWa®Y0¼Lúã­gã¼ÂeÔg¼2?éþƒ¼¼ã£Bnä«yp·ÀŽYð°wŒÑÍ!yl}%NQ&Ù7Fæ‚ñœT”Cziµ•ðµÿWÜlÕš’4üp \}yÊ³Þ =ÄÐG²Ÿlõ*Àž3ͬr³-Nú…·†\r8¼Û¯Îëa=ëPg¡î»‡ǒЦçrgCÜž»ìþ÷ŸÌ»Þƒ¾ë‚:ó¸Ã³`¤¦ØÓr5؎Ų…C˜x€8”·g1F/¯¹gÒ“8œ.àm<Œ]Ì:ˆ/°Ï]ø^ÃH$ƒýÙ®‘­ŸkÉg×§¾éƒç[‚7Ÿ­ä-oŸà‰g¡¨Ôs’w&ÂÏñ¢4{ÈEÌœŒ: 8Yg;Q›cA¯âÇïS'A>)f: ŸßßY2 <&ÃG¥<´›ŸCö’¸·ÛPý³+ŸÿKÞDçàÖ'¹Â›rÉp}í9˜¹´ŽˆL}H\:Ò¡=k*m†óN™Ò?ZAõT(ÇŽ`33áÊ((ë¢?Ü.ƒÏ²$vU 1.Fs f‚åM1pÅ´£¿ó™ÈYShu1¦ó`"?ýaÈÿýÌqJw‰Zà4®ž Öf=xpŸ6?¶ÀŽ Ñ·MbV¡ž½ 5AS:kx7sOO×0“ÿÇÝ‘'CÇÈÒ!é‘,ôÀW\xBŽ.­Ïœó ­âæûáöuÒ•¢Þ¯asB^@^ª";±DTzãg‡‰YðeêDrtŸ'N‡&D GÊÀmžpUmkG•÷?£é†égIôå¡4.ë 9þ5”ee¥£_C:oîeOªÙ"‰ßðæw-Œoƒ›‚½²Yp!ç=NŒlg†¥R=£c˜0ÖŽÖÒ£:£&ƒ©i+¸é' wÓ9 Òàuì|oMá"·ŒxTè¼ÖY —CLùW;g¨—PÁ’fgªsz-j]— k›Øö׸^Ř¾]«K_^G?Ï8íRC§žš)NJ¼Þb! ¸Šûã³Öš‡ügƒöaó9bCÝïHÓίG ÷r!¬ ‹e±•R(z'KhutÏÎù#œ²°|-B9çDòcÌyKli’…­{ñÑfOÑ%4ùøùI¬ýêÉÓ÷®'"÷Íø‡³SèÍ"ÄðÏ7Üz¹6$Mæw‡Þ%:?ãA7 ~0õÙåmÌ?j&ò¯ÃÎy0ÇÏŒ«¸ I˜œ& »²Ò=n€Í±ïà¸O”'_5¿¥ø\N•Ö)ü8旅%è ¯]¬廽0ÇÉ,Ç…àÞJ9TsƒÊ­’|÷³)tç©Kìýþ‹¨ì¨ ØT‡[~ÞÂ3;EhdÊè¯U§ª«õð&;ìÐý ]~Ëq D(ªò¹bѨæö\3á§öYF7¹Ã´Zf¿uŒ35u¬ÂŠ©u ëÙó× å¾¹xõI$ÎkN!¤õYvR›>¼dB×ïÖ¦Ñ"Æ|¹ÕO´okE³m¢ôJïìÀc¹³±ÿbä)ÓâãX†û س1‘?¹ÙN„÷:àWæFþýMó¼VL›ú*Ñ¿ÚQx¹HŸG.-'o—t€Ú¾ª¢©€·%#èˆIט@£ý·nà¾|ù¸Î=›F¬Häî ƒA|Ÿ²Tv«EJx`{ýÙ»üÊi¿¿Õ®Öäë¿Í€Ù™´tír|¶r]%6•k†V¡qa9¾¢„ü»¬Ië¿Ï€Úp/ð¼íÌGœ©Ä®P:kÉi˜ÑÍv|{s~S¼jÏN‡âJI}Á™Süª˜©Q »C–2‹9rh›Ò&|fÛI!@ä[nÁŽ!’ôéž»ÿù©ñó61ðf¿mׂ¾-ÏñNï4ñGNŒ{€+ƒNcÏ¥§xúLŽø~ÍO¸J®Ugyb|‰ó$AÑöËl±d·p@ulx{]4B;B½®éb«C,ˆØ5c–ÿCòyÇDö²@;ã±ð~ÚxZ縅 õnÁ#‰Öp.<œTƒS¢~™.“îÐ8¦Øò7ó»`lÈ!Ò\ð˜½*3`­êYüzÆ8r¶àà=’4~=ÅúßÙ‚iò¯Ùùó‹Ä’ê<{ž[æ„´K)X°Ð™Œ“7ٚݷ—ÏÀ“+ëÉèœÇØ×y~žþ C‹{of¼ÅjÏTH¶{ÍîÕá%ÏsX}F‘oÛµ™ULYGç½]Èý͹k¹?7ä1›½PœÇüµÂ¶AÝPýf ckP¾ñR µøæQw¬>°.Þu'6kä¹ó1¼?¬L*ϳçµ3Àº²[ðøÉ\¾5`<¦sÿ´ì‚Ñ/¸Q¬K7‡ÏAï!a˜õö(6x¹‚µr±| T2T¨;1ÞèñUŽÐ³êxOžo‡º0±›•0Ñ×]âÅ.?¾çd;*¨Ü„ƒ¿ö`Þ³>á×ãìúðç¸h‰$·œº“êØ'~±2eGØv¤’é9Æàå%$o$%¦$‘-wáQåXLOÿƒËß328ƒ$qtâØç6¶ëÞsYÁZ-IâV÷Ò—*óв:DÖîÔYóQ%üúÍ$Ok•¹·õfzÌB ÎKŽE¤9¢ýK»2ÉÅþÜ>f&5?Ʊâ3¹^PúO2V$‰ãïØO;ô¦¨Ù“ã—ÙOÝñ¸]m5Iš5¥þT¿#—E)Ð?«FQÅ%žy *~ÙзMþ,h®=1û*ï»`I­þ¦ËH)~Öu:í8ÛÄš„è|~mP§ýÿD _d98¿½ «y*[:!Šþ(Om’‚øÛá—qê0MzOýJ’$â}X³D‡ÆØ%ñ²F4³å)ÏÓâÃlD©Îýø*wðPÃyœïãƒþ¾DÏR¶Ùï ®TMåÍVЩ}hÓ«¡ B†òºÍ¦DrúUÐÙùj‹(J ú„>=WŽK¶Ær£Oxgk:Ì݈*ì²`DR ÝÔcÏ/n …©ÓŠÈÇ ´\;B^ôàéÚ)•²…e$hë£@º5¨uF\#)ê´:9» 7 Ðø [,¼€Ý-B‹¯à÷È©(}ú= D4PÇhk+9O†M}ˆ«z÷³3ñèj;¼¼r|•ùä–œF:#td/¹i–¾s v÷ÃÛºHõ¤Rtw ‹µ~²¯ R¸éS‹0iŽÔ¿EaÞߟÚ‘X½LÃÙC(QF'Ï%|æ¹qôÌú›`(Î “é¿'õ|ýÁ ¾áƒ>­”‡Ub+¨ïÕ˜q§ âÕ»à–Ã[¾î› ÜN×\³çÂÚ¼eû(Zß~ Óu¤óÄÍfðâ`»@–W‹daá”Í|ÛesxÏóA|i(,~€j.÷ ·(Y^É¢ÉÇáJ´!݉–Ø)ò²§á\Ù~,¸í <Óm |g×&ÚžªA{çHÔXKnðj&K-cZñhž-±ŸèÄGë¸áž§UtÔ“PZê0‡?"ïPÝVCÄWñ“>ÜÂ)„»Ø×à'j0ú MF›Ô}(î:Œ?m=OÂæW «äé|SzûÖ5PQ8{k_ FÓ£~}`Ö’@5zqÞLîyazÚý…Póè?þ6[fK릟áÎVØžÐÜÞï•`O/†Ð·ìéè!Ó¹Í$}¼åìJ¿<[vFÒž™iôê¤Ñta¥=œÔzÀ<¶ðä:/jÿL‘zu&.þ;JÇ·¤•·`髽`ÜkÁ^^à'”d©|ö2Z󵑊(Fsi«çW{û‚Ê]ý%\h]CŸïåÖ–Õì¹Ùh:î…±|.û}ÑéÏ• òC"x *Qic^œcÍ¥¶iAΑ¾û¢-ÚæÇçÃøLs]tæ§°Òôý½ ?÷‚7Óõy†ãœùVŸ<ÚGÅ:äxæVIg‡+1lã¨r‰3ì©“Í%2$Ý–Êßïd˯æ¦à/×Û¯]x]v".*NÁ²ýÌ´ü,\þNé ¥AA'ñ†³½:/ZVú‘w§ÄÂm Ízeï†/r=9;­²>Šçqƒk) ?<‡OðÚËZ[Œ¹Ïo}bkqHö+ð'o§¡ÀÍÿ[?ST¢Ö?#ß¾¥°;Íã`z³:ö/áV×®Á÷íÞ¨¿wþ6Á¬låé… ¾Ió…1×_ }~…ÆÖ;±üÖ,8~SþE "MéÓèÙðUyݱn ¿w k’B©¨ÿZæzŠ˜ôõÀ„UÁüèµ¼&Ì„ËÌ,Kw„¸Ð®ÂSÇÑa9’ÌûC^ém‡©r·:æª<¯v§eŽ–çíá–Í2û“8a/ä™Wå\jpj,½xa=Ô_zKúl˜JÏIf'#Çvóœ¬2¶Ï pwO‹{¶`ÃŽñXrW޾ŽëC×–ˆ¾° ¶~…«+ã‘V~&S‹Ëà|Á.özÆn¶ò[:‹ìE‰#apOâ™WtÉ”-$àÚ1œ¥´Exmý/Íhdb:e0lëu¦ê1Ùaî†.Ö»U•ï2 º©R¶Ôò©ùlúLg]B…%×0ÖsºäÛgòvÌ_± L¶¦g á¼e銹øÁ4F¸\1ÃÎN´FOÃ'…Û`¹í^Xo!F£èHx>F n| ÀWkUùí[›è¤ ?a¤ävgÎMFìÎbSn7îŠØ‹Å“Ý1df¡Ý‘㡤¨¢ ¥náW™…,aîH¾ã Øjçn·cë­«¬|ÃFÁæù $}¥Ѻ“6:|"NoØTÃ!¼ê`/.Ù znQYG½oRá«¿]Á›ëíñÆßg8ÿW#†Ù®ÉQØtécÏÄy‡r±ƒ¾¥.U5kØœY$$Í›y¾2åá5`KOMÜ›s\æ§Âoîp§™õ65e“Ëñ‹Å`7¨B©)ž¯»‚khŸ&·²gP+9Zåe3ÄÁ¥ñ‡Ðì® Ývç%Hœ6ƒÍä!êºa}î¨AÎâÀÅߎÅUý« ·Z^ù Î nü‡eôî个Êýf±ºÖ4ÕØ.L¿ŸJÙÖ´$Ä™~êì0vçTþ¥g-.ãÞÚÕðÅí&WÞf@Ã;T²s»Û™ mYMðÊr>¬ð«âŠRC¹ÛÌz^¹R–‚ß8EoþH§eµ˜¸l “=SÉö}¹ µþ†t¼´€.ü°•µNáïWE`ÙA¼ÝŒ+*¹¯tî8™7Gâ¥f£Wù‚ØÓxfƒ)ï¬iVÀhX?r>ß:GíØÅm£ÊùP§?ð¨K=µù{ûq¦|&¼¿½…Õø–“#¾…O¦¢/$èÜ’n8Y)Kë0€IÍ€[zå tÎ O‰›Ó‚¥Œ•žþÀÄ×nûéÑwCŸ#ÓÀF(ÌÄ÷q#¸W¯= ˆL"òkNÂrñ©ÄjìV.í­_žÎ#ëj¶ÑþÏÙ\OÙïìãv®¢|Ò JGÏK Sy°Ö?èšëHÜ1XÉ'óêÓqü³J&N¿†§ŽFsþmäÖË#ysò¾ N+4‹!V}[ÿϘ_}°¢r§ ~ÉpžxJä‹Óîž|»Ô0ZU¨Ho9w“ÀÒ1à›üµ ’Ÿvüîðdùd”8‡ËwÍDÅGÈ‹FöEUð¬VS¼ø@—š7­çEã'€÷r~zÙ0Ô}„µ•`²ó54µP£òsØ?|Ž =bÝ2«©¸_ü9ŽÚ+³à_›$­}gÄC¥° :oz\'oéIÜoŒNGîíWrL³ãÞmÓAÃÓžúŸ‰¦_þÈÒ{V°·ÑÏØºåµ`³Iý£þá·©ônv.Û0á=™´¾ $çgÂÜCÑ8ÖFœòZ1þLCß'ò–­ZlÆŸ+à¨n)ø¶#“¬Xò…%ˆã´VxçéŒ{÷AXÅÚãºÂoëR黽ðI‘à—®t¼ò´ïÅ•6ú¦x³}=¸{\„é%¸÷µ5J¬J‡}÷…+:Àôßež¦F+o=Ä’×…[ТÙ×IXbt åô <ÚúšeöæJ—=Páù¦QC>,¦¹5š´üÙj¶÷_4qÿ£‚Ãß\óë6Ñ…WÏAth>)$­¸£l~zÁø(Ú/¨ÏªÃÚ,/x«âB·¯¡±ZÜóNR§~C[<~³¼(wz$(—ͽՉÏìžâEÿ1t[\3ÙZ6NPŸÐw/\ƒ÷ݶ±&¡†z‘#è·Š¥´ñ”‰óCÞèS¡O7¹ý¦’˜÷;p÷KlKqädcø1X°/’Ýþ¢GOH0vjM‹™Ñ•G/.Þ*Ç#wws«Áý£ç;yZ bç톧þÖ|õ˜¸ ð ®’ë àZ*$&ÁRsú:­žìÎ’§=Ã]Øm‰Ýèð*6™'ßíAã7uX1‰ÅªÂt·.|¥&NSŠFchÅI<¦=9‰7÷ïÆ¦WÑÅs>êðÃp¥v˜mÔÀœ ^àŸß:ü¼ºΔd:cóQå¯ 2)²‹¿>ÈIЋ5¿q’ùj2¿f=+X€ò²á¸1 ±àÞËzÑKþ©`Ã×øeu !Ø4nS{þ„Í©¸Nžþí†[N³™ýR+¨qH†]£hö¯)ü\P~RHÄ O£q®Y«9¯ÌûR%øªqZtÕ Úñ)Ú ß@ÓÇÓ§l¿J-+RˆÊK·Ñ~Bć?‡ª=.É Ö»mÒüXU!Wôš˜Ã‡÷áHDÁ! D+÷5N¶Ö¦%סë¯88¿u‡àú¬™X>m¯pÊøqðóˆ8óznJO\ê$=Ÿ!ʲ^înÌb©ì™½ì šÌ ÞbÑž•OÕéY“Ûøíx$‰R0ãw»œÀÈIåâ*p×_¾!B‚O 4%O^í%ÍR‡àÄ´q\ò˜<Ôxo€Uáhá[ ï_‡9]Ãá“$xöñ™pêP79›ÿ[>×usaÛƒ\°Ý'‹ŸÄ›%¨Ë?gˆÛËÐkaܰyMrŠgÓÒæ ù¡—»lÀ­*YäŸIÄ.íEI‰°úw9kþBcèªë0{ßYܤlMwV¥c÷Gò Gš=ëŽ1\àá]츟ˆ™#’ɈRæãˆ¿^[âA9/p{wý+`yåM${k*\:¥B×ëõ@j3Fµ,%£=üžXª >.ŠßêÒ¥·Â•:·@)D’­Ü1@Üä—³XõWlYøyˆéêG÷ [–ýa™pw«6ÅAaîfâ«fÍÕ³¸éx¶:0=½€mAçAû¬]8ó éiE&õp,3?sË:˜jªÔz\#%Õ•8%Í•¸.ý7gÛ­É{Mèè2ˆ|ã!F…h¶WŒR½…=-àÉ8Ó—/‡„Ѭ«qÒÓ<ì[xTЄ:¿]Ùy±ìsÿDZ£Ô€õfq‚9Ú^¹t$ßÀ<Óe¸a ~lhÇã´Aðè“ð†G~Ü5 ý+ŽàªkŸÀkfŒ4Õƒ]ÖB"·>eË¡õ‚'y¸íó@ÿxMÂv‚·¥Y*uzVØÃGçc¨7ŒoÜ7”¬×1ƒÒyªu¢`ñȈ²Y†<Æ¡-ý§9ÁÕ-büHÂ0áæ)ÐpX_‡kÔi÷e~_‘± u`·)Ï̶'wÊî Õf1…“õ¬zý\¸#S?v¿†ÜQþÜ£w»l'Žæ²V‚ñïIå¶ýÓ6]ÀÉZ‡Ý:¡8^­aÅ‚BW8ù >Á‘}À|äúÈ£Feløý¾ARßÇb»„>õÙ)F%ìäpúsÆŠT…££çÑ'ñ|%>znãÄ[JøLAÀ²îØâI}Ú§  òù˜cp\™‚‹·÷’ü ¬^"wKЧÊfà†ÐÅà9\…&n¾Áê·«Âj])j šƒÏùh•K®¬g{þ<†‘oï ÚgOÁÒ¨R¡Û.mø5k9ýbð³FˆÂ%Kbï‘ÍðV|8Ïî8D–]-5Ê|óQ'Pȼ*ñN<¡%å3 ôïüv³ÝÜZ¡j½)4r ¦d8eì§]ûö¡¬éA2Ü&<þIÂ嵑ÄÌ:Âg©`²y:>Ð0ã³ÿ²­–[ 7 ùÏÇCmÈ><öIŠO®ÙËžë¼"O GD?Çeà ùR¼\§È7w>™tdnçâb:”.‘8 Í«Tø¦·aòªHTÜëÁ(¶ 9W¢3v„áX‹rÑA:vW½•XcÌõÖ?‚ÎåØäŸÌ‚v[ÒÑ_‘Ù$ Á+0šõ“,¬Ñ¦£&ãq¡ú°¡ÿ0Z—}„!™§qçG%ºò‰"/m_JUwL ¿Åûáôͧ`Gƒ‡ÖAAgn£~s&&7èÁŠ·±é\.ÞïÄ‹âU¸hÇ¡`G›÷¦ƒ¬l;_ñ Id/®[ÂÑÂt(ß”^û“üïv\ýÚw÷ûŒâšXëï8X¾‚%äN¥o”aÉg?¬ÓHâ=ãC@fô>~wåóO^Ã×õnT¿Â¦úÍÜ"°}P‹ê±IªôÎÊD\utíHM„¼Çâ´þ+{ì÷›Ü" rމQÓ]Ç1)üyµ“ø¼|Œ¿,×8üPŠÁ ­Y¨ò1Cýö¯‰ò8Kĉšù§âèc'ÀFß[mäÄ'g” P§ùÚ©xåçqtö™€²‰‡Þý€¥«%ÐáŒ÷üú49ƒaõŽÆ#‡ðp\ºpÞòÜz+HÊG½ ìŸ]¯`Û)¡X1WêÇ£3éšP7ÈöŸŒupÆèä–­#òópiùrÚ<ä8Û!ÑGoªqËaÑ({ÍG*¶’íëV1ÓÔX˜½º†kZ¿÷.CHÌ)a#=2AÙZ¼—Fk] ó²miç„5…*0{—-ûÙÕÈ&­ÛŸ–¿€Á8p]aäêmëOÿâ»48ñü1~gfðñå[liåíˆu°v’4 †U³áX~% Ü"CÊ–«pq»™äºeû¾j ¨Á嵤Èâ© ä è,Vôa·Gªâ[ãDV¯ Ä|9…‘XµEëöâ׃»ñùPRä—ª– iŠ Ø7Œ¬þ5¨ ç§‹ðc5é2š~=(¢ÇÓ0Óu)dmSäÒbTü£ }¤«O/§L¹Eõ`ag€­‹+Á6" ªNÆaçŒ à·Ûú[p Á?¿1£ÕRé$1© [§ˆÐów8[sØ€ŠÙºT¤)‡|û |SFvÍmŒ ÜŽ³óHÑ‘AÝ”MzSpºW8‘íƒM*…ªFÃéÓ}.h,ÃoŽDî ÔÂ…©7±ÿ‹.»¨˜ˆšvƒü½'‚Qô++=•J¦¬Ó#³s@wV,OXƒ^_‚K­_ñe‚þd:+€à›ª(·ßšŸî~ƒdäp}xôjŒé“?çˆSïlÞy™MY~HX^« GóŨ­òv¤ bôE _kÆWkàü mNcúQ#ø,œê\ƒºóŒ¹Gx'†[• Û¿‘øuõ²®yÛ›†«ì]ií7 Qf Ãû“F¸V+¥Ï‰@ÉÓƒØ}k<\uÏgN³Às·9žþìÈ~ ÓI[ ±_î½LÜû=mÎBô»t4¤;0¾BŸ«Ï‚‹²Ílt¨<Ìw•ãËîuý ì¹+Í(ð ‹â 3Ftï!Æd˜£×³Ñ¯N†´ý–§42æ°ä阘bÉ‚³sjp„Ò:ÊžÇÐ5«çñ®×Sظ s‚%{FÀ’=û è¦(Nû¾êµøËâ<+é#Î{ KºŽ¡Y7Ñrøü;Ô»ÛFÓ¢ù¬²ö0ü¡]e tæ ßîûbN޳ͰàÔ,ü´u/öÕˆaÀÐ7díµX"k’ÃÞÝBaç"¸3ch—TÿÎ`2u¹Žºj w÷L`#‡–19•rtáC²{}JcƒS*t'úAT丵÷ÐÃ0ä¨ÚÝsû#±=Gn)ñ!*ÕøéñA<[8›Ë¿{ƒÿ¶?€-Ò§„÷­äqÑ*8ar¥Æ›é7ñDp¶Ý/|i¸‹^¿DO_ÀÂÈó—lø¥ï#¨{e÷Kí Ó8?ðá3¾2>Cb^M@¿“žlÀaÍŽƒ8smp:]MJÞ}•YdŽÍQ|Sû÷ÍOÄÏs1åþe Ѓ‚=™pµ¸’Ƚ#§__&“seÐs‰8ŒT‚ÝÁCXí­‘\ú|5ú.>4Uhűa¼9º•懗é¦tØÀLؿܚm΃إÛO§ã(bo‚O6kñ˯²Ù|©ê¢dA›w˲Öç`ñ¨d4ô]'ÙBnåi\—eÌ^{}ÇÏVgHVe%fÇ•cÛ¬¯`0á¼}u_–}‹L„Ð3_ ~Ôƒóz(nŒúÚ—ÙEåµÝwÁåÒb:ì¨1ZXNËå18e û±Èo;„kšùºÞrÄwQëHW6‹AøÖ0ؾ!–gzæ ìæöÆ;RcØD›"h~³ ¬ÇÃÏ–‹Ø$ßÅN9ï¨UØz`:Y¦~…ÙûÉó³çéHú‰=.¼ƒøª|(/Œ’À{Ó|øi…“‚¯9Õ0±@G…á%'ÌaúÂJ\xN†Ž¿¾ÿ÷‚;eqÔ›õ<`Ť‰åXN¤xm!ñB7ºÿô\”¸²Êi´]³K.¼aØ.B£Öa–Š,¯ðÔ.r´m`KäÌ÷—fAôu"£7ƒ®®ûÅ>MÏÈgKßñ²)‡ˆÈ‘ÐwëÁxkº6áÁ¸¶Nf4y˜Â˜oWáCy;Z4 YîáXüp2;:‰,óm‡ÝW ·ÍÊ÷ÐÅÖuöóÃNà£ÙX¥¶BÇËò?o‡Ò¬Ÿ2ü‘' –­¢³Nß¶yŒåÅKº!_Iÿ,áÌAl[¸Í”ê‡ËñâŸE(ó’-‰·€æàT~¸­–ª¤ „¡«VqgXêùå‰ðÍ€,8mSÁ;!Óðƒc¬«/&·Ê§áUîÎ=B×|¸ 7ã­?°äíºAœt‚ãp‹ÙM‰£Ûl÷𾹃ºI·U¾'í¿¬½²¼õráƒk,µ˜´…ìùôœî”{L†8•r»B5ž ÒˆYã²ùó“©“¢$¼=°Æ:§ƒÃ×å xUs.|eÎN®´f‘ÞpŽÏâ›HãS~&t¤°f¶»¢ 6¢Œ¡;º,•ãÑQ|àrUñ †7+7Aè) LJQ…­ QüwM#¬2ˆMýRâp8]e3Hò%ê,ÇëˆðÕÚ( ·kB?M®œh¯ªípK’(?˜ìU’èoîriå¨sõ!ô:š€ ;>Õňm{aÓ¼ñL¥{LÒHRf! ¯-€1ÆÀG§·€¼f<û| žüL(ÅaÇiÏ©Âè›GéT­ô½þDˆ;9‹®l“ϱñÇÑ|ÿRZmkO;ðÜý¾M§‹ƒ]¹Oô úÚ˽kîbœ¬=3dªð"¬V^†$5PÏ÷;&ÉÇÀ‡nR¿þ2F¯Q¥Žìƒ%‡ƒÐµ<–WÛÒ¿¶÷ÁµGÊ­`tÌ~³³Á18·Êˆ RÃ09¬ÊÏm¥7†дK¯¾Cïú»Ã?ý‘üÀ¶rì=^GšÄÎ1ÃþG`½‘@îü ®Z\9CÅéÁš¡ü¿œ"w¨p½³Åäj•4DÛÊÒ²oOëgl<Ž¦á ±`”6~]ØDD2„x4l =¡1SeùšpO2ø>©ê¿ÿŽ%U~ÐRñ©9°0ÔNÔí×N2½j/ªQ‰“oË¢öx}LÜ7¯>zÁØ3a44ßt„߈ÑôÉ|èX T c°´ôü²+‡‹ÒqzÂ6ª `DÇþÉV j1D Ì§i8ƒuä{æþ×¥Ë ­yÐ}ó S•Pó€ÓÌÑç7ë‚éÖ! 8T’'8¿$‡ö[QÑê8<â<3á!_Î €Hÿpxð`´Á“öݸoÖaaìjx[ÈDS]ð`˜¾j=‹½ó½áí›_P_é‚ÇÆ ¯çAôz· ß¿»ípÛ»Xàw4Ïúž„Òû*|ï¯zTk?‚ÿº"`U†.]½Å~tÌ¡ (D-Ö'˜´ Q`xõ!3¸Z‹æÝ£Ju¸fÛU’f¼ø®DäÍcvÒ!°uövÜ`>ƒîÿÄ%‡È²Ü‰:\ºs8ÿa¼„¯^:Ä-ËAGþµLÄC#SA'ív^Sc”áÓ=Ê3Ÿøaóhs©à€²o<@lGœ}|Ÿdé½cÕÏj¤G´Lœ/C—Þû ºJ{§?9û¬D¸äé§‚®í'AÖ_Âã8Öµv(]ß[ †wó¢¾×x%Ä cîÍçâ¨ñ2NÏÇ—ÜËŠD”`’÷Nüœƒ—*ñO#/2ó­'àÇñjüÛ?:/À¾Z;V:#?/?MÆ¿l"uã‹Éu¹äù®¤EÍÏÑ£¦—aÔ™Ñô]~Ñÿf »ŽÈáÒ*sá¼Q)ÁYCæÂ)G] ´øBÎ䕱XËÇ0P{þÜï#|G%9¶»§fŒà'Rv㑤6<š³Á¾¥0“ħМ…è:b(7ŸÞ·ïD÷àBœ-±-¬tø šÊ=±ºìªàþRr5¤wyýÁ× SÝç`‘éš—ò†Ôj %Ò‰ñ#Íë'yð ¾Y¹æ!kÎSãõíYºÙE<ŸŒ§š/‘1Ó¦“äCA|ؘ¤-Îíœtøâ§q4òÝzz¾ú É1lCÿÂcà2VŠÎüV‹^×Cõ>i>fâ0?Ò¯oC›–ç:¸(¶]œL¸ÄªœzÜŽ[Ò²û¡VV…¬ŠxYkœ:Ž·&í; žn_òKÕÀÂ(@(cSíà~dûë¡@/M3¢~‘ÓÐ}u¬L–àgò¢0>×·`aj¹Ã£{ËÈï¸;pyBô­E·dü`¥:¢\EÞve âIÕY¶×í*&]4ç•Ïü ¸[ ë¶HÒÜ»ÂôNxAZÀ£j/ Zä&çqïÀªÙ† +¨y³/½y©íò.ƒ‹#¯@°ƒ{ÞÇÀp †}{ž*±%‚Áñ`´!¦ìÞÅç—íÅ¿köÀU‡¯øÔ¹ž¸ŒÆ»iª¨‘?N DÁ4k(¹'‰%f\Ì>žîçaã$ˆï‰ù¼/Ó”ùø\c: ˶ã¶Õ¾¼ïL¤»ãÑí‹ VØŽ±c Å%ƒK™Àió"{¥‰±§Ü?J>™wࢤg¨´C‡\N@ÓÅ4æß:Dû<Ì:Åq¬KÉ.rà3üK@¢·@ø Ç™g%‹pa+ ï¤Å’Q\œïyô†]oþ„ÛeÕø¢s·ØÜâŸðA4‘j‘fÔ¥¸“±35½}€eûµ3?ï'¸¼â y³˜~ ³k?Bû"{ª"üÄýaTД'°ŽmÃqÚû@ãô Ì6À_o¿‚“„$—îVbo´—Ÿáwጄ=XÆB/Ά¶–‘ÜçŽ*ΤGÑC)®XÆ –m„ËÒ[|b$¨T¯•[š‹.[]‰ìÙ¥ðÂùþ>TÄíVÑŽ§%0êXœ/À˹8/s2'‹äÉŠýÉo¼šÔÙâŒÊ üÏÏVÅ !8yQ Z/ÆÕ×@4µ¥7ŸÇ-[ ©Ø²pzQno>uê¥ÕZô†ª¹½…¢Bh/XwsÐ’Ì7é<%þN/Øê•î;ËóÛÇn—)„/î­bÍgšq¦ÓbaäxM:pç²à¨‘jß.ËaˆK+L¥•L,w¨Ñ6½81¿“©ž`Â&s.æ1žÿÑ} çw¿…7ãå¨ßÈÂ}gžëÀÖ:àkEÙï U®ò©žØF·Ÿh'lÆPÉáxy¬(Uíb5­‹°ï^´C×Dφ§Q-$øÙònÇS4i€ï!´-·Çúã=Žóx¾íwrpø.¼2ÿ!†ÍÀ¯O¼{ ïâó­ÐôÈ'µ­°mD‹žãÌ4¾&8¼òó%NÀß>܆ ߧÄ( )ÚÆ²|žãOu3Ü>eÞ?#N§‡‰»¯CBêeòxªÛ3‡ÎMDº}9t Ñ ¾çŸåúØ(ö%‚òÑægUEÏÙHÒg-X  Áékrn¦%ÆhŸC93òvÅÚMšT·â+æËäYW×±C‡BéIî7æ2 C|ö¸ò»²i?0Xêž±­Ä#âtù²·`Ø ²• LÃÅú³¦Âñ;zÜ@y+˜©dã¾ <òªJoþaÛJ­¸¸è/ýL“Ö¬?‹1•9lξÁ»|öa3)”ÎüŽ“™Ó ñ{>¦î“dÍ)–¢ÊÃ…pà¹V r_§¾>è~g‹qîÊ,=õá{lœà_Oœ$GÒ¦RsŒO½­'Ý@KàKG›,¥CF´Àû#1dähF>&G,ÖÑ‹/ÉÆ)ö(þª"â3È‹¾ˆÖx‹Ç.=³›æ\.'›$Î1„$íßøØM^3™ÄÃ*$é±ÓñTë„»¸à_$<;PεââM^.Ïããܦd¸¸Ñƒºsà¢òM²mß aÇp#žô§7AQÝÁY»p"g)Sž„Ÿþ½Ä4„½.ÃESÐ4î…`}ÞmHöŸÎÅÔÑÔõ2ŽÐÖ€šcñ0<õvÌ9‰Fñ†°«JJ(3Y-.AaÐå!àÆÉ‡É†+~Ô&˜[”>Ës?õïu û>yP#j»ó9ûAÜ‘!Ÿ°”š®»…³^Íç%»Ÿ¢íÕSЧ½O”Òs ÞÖåZg­¹­S=³.°æ—›âa2çë ²‹Äh6’ÝCC©šÕ6Ø)^@F>U箫]hýb3lÐß͓ڱT…áÜ®#þØý>¼NA])š^€ûƒŽÑSÏŽÒöÀh’߆zÏ4ùÍSbcaQ7”ÙSÊüç†ý`ì1ß›½ÁU‡’è†y¦Ômà–ñ¼{G ®˜xzMrî¼›Æ^ ë_Œ§šK^Áîš|ôŽv2.?J8e–]e9†V‰Ÿá®žÒŽ3§ÊÑ¿.ûˆÐʼnžØžSêñShÌwP¤n2sÐF\f}¯§ŸÀÈÓ‡¹.€À­:°çD2ý3 d¦Þ¤Ÿ5¨Ìªý‚•ÛÄ8Ñï…M㜹Ãzm˜q÷+ ¸æâ¾xi56;ŠÂ† |•0ÄxéŽÉüWF\£‡žh”áû{ˆb0âªß‰lÄé¿`óé 3KÍźR³Ciží"záè$²Òœ.Xô·Åêó/‡NáæƒDUˆá¦g‡ÑËùÄj)ÚYÌÑKã ·Îüýî’üçÜ4¸²©ŽËºÑ£Z²¾:N2A ÛoDÃŒI÷àWÿJàòÃðU%ÿLnÅçe [ ?šoà÷» ¡ë…ŒI3å³[ÕhÛNGœ.æÑ-D✟ñ›AÕ]dß´`د¤a‘ÜØ¹„—e¨òÈ–åüí Ž7™{ 4 ç“r°àu,¿Ðô›\?(ÁsãÅÙÝ´ƒ¥‡Òë÷RÅ`C¢zƒÞt0c3o·â•íCèÞé¥xïõWÐ’rààñ˃*NœÆýk¤øÔÝ;ñW½!ŒÙiEGÐÚa5טiñP«N|_;Kå’)UQÀÖ+cÐÚñ)÷Rt {¶WpƒŒk³wsûUzÙ㛉­p:Ò‘–ÍѤ-ËùñÃÓØ¡ƒg4¯…;±…;7¡ïÊÎ㺾58ú¢ ÿ¤áÌﻈñWR Äw¸ã_T~^ü9j4á.•Lb– ›ñѺ›­ñW 7û,:hûãøY¤æØd¸êªGîn…–GQ®¯¶«p“5δþj'nw[óW{ÃóJ7èõЦ¯Fíº×ÅYŸvÁà %^Àò…¾Â‹z¯`q֫ưb‡=,é="×Çä³J¡¶@„ÒŒ$ôM寽°F‘¡ÿ<7¾ÛqSÞèó„+ip±|)ÊÊ]ä ¥_¥ÀáËUá?•­¸=^˜Ö{™my1šÜ×'+Ïëð‘Öuxí·O5ÞZ+?€áÌçè–§ÀŸå¬Áø0„hãf–*¹Œ¾=õ÷')P¯)Sxhó‡7Ë”€^A‘Åh8y*ŠîĶ—vÜSæ0ØŸzˆíCîC‘¿¸m".<1Nýž=‡*&cm¹ñž,|xÕ”ï#N[Ûð•Cº®™ÉÙ÷FÁ¬'shCæV0* \ýéæ nôyE->»¡‚;ߦqA¶ Ñ²…l¨’&ËΤ?î¬$?®âûŠhPé1ƒßÿna†›%Ý+±ä=š?îÃoßôù6¤¢fáˆH54øÄù×nnri§ñþA-o‹€¤}úíÊ"¸®eÄ/—ÙY¿YI²(ÆÛòüõ˜tm5蹸²Ø5_`}\ÌýÛϹcè4S z_`SZ.L¾ö€ä[ä’e²'ñsƒ:¤mÃÞ“AÂÕº'…ÓÆ®áǽ.ãªkË„9*Ãàl–3öU–å׸7¹Ý<—±ëÑ£e:ë›'ÎÉ;Ú gpJ Š\/É¥ùpâB!ËãÞà"HÀ½µä¡‘U_–.“Aàt ÑÊM­'ñÂB©¹äSƒ ×ÐwŒmQ¡´*u=a¤N²gïÂ&ÃŽÆ—¢¦ôg´ ,IØŠËönhfèìÀ Ï¾Cíxù°lU’à×݉p¶:=¶ ›@ç!ÐjRáé‚ÕpaëT¯Ç׺ï¡Úï3¬²Ào%¡‹Ýê†xDßÃ5Ñ-ú{ðÓNcR%{ MèF.eð îu¤ [sadi¬²\íõáÓšKè0ÓæüU£r7ƒqÚ˜,vì6ˆo½æÉÐ-›¡líß —ÊŸáy½—Lc¿,Ÿò3…4•4ž)ÙÁõ&÷3‘±ƒºÍ7÷4¸‘¸½#QÄw:>1“çôn2û”Úˆ²a3(›ù–“g¬:lõúéë’Biž\-æ·iÐ{gé?*¨Bo|t…‘t fe>ô2œ:‹ß*²zMCîm^0 }®@´·aem87c hG¢ê,kØ»ŽRÇWøhØÊÆS·Ð‰$#ø>Èÿtä…¡«ye¨(ãG[>éðÜ.q¨-8ž2DÝÂÅðzÁ7¼)½ڥÙªI:DDFR3 ÈÚ_Kr´ÖR qiÞƒúϲÐwS3¸©#ïQÇIJpÑ´`³)‘e-ÞuG÷˜•°ù»9Œ #ù‹HÃ;S*hÄ…Yqë„¿8úÒ2î­ ±.%½…ÃiîþÕlÒ—ÇpðñòÌT#ŸŠ`íµQ´æ¯(vlÀ–vëñuKÖäzÖ§Ëà´{ ¿¹ºÉ“¾ÝÇ ¬È7ª%V(á"z}çhz#ßL“hü{+!äƒ8®p¥¿–öcN™q,…Í#buØ4ÔóƒCñΰìÏòª×¹€î9†íÜnåUeø 7IÒþB$LžÀ°÷½pÉaºÉž„tÍdèãw+¡üã´=ÖÈî}Œ7ƒTœ+­Jo‰èÑa³¸NØÒÔ·¯Z¢E÷Bˆ³­G¥“ýð7騰{½ –ø¥ñ宋Á£êj˜Íýýµñ?óšQç°Gó5 þ¥L9?o§t iýµì‚ÖñÅ;áò:>Ýo7lz3Á!Ù4÷–|Áëâ^$ýÇvç½:þ7Æ:~?ê'J_ÍÏ@)Ëú­?žE.žÅûÏ×À¹zÌ~4õ¼£Â_YŒ\ÜÙ8•Jâû­Ç·XÁ2oÌ\=fNzç×@I{‚ h…2Jþk¿æËm(\ÛŒ­úé]w Ÿ_•§«ýâáá£8åÄEòFÎû§ÌÂJy}v©ÂQ•ñ×ýB«;®ôhÊT:K¬œò=Ìâ§h‚`ÒDz|£1÷p¯$ªi¬ü» ªOnÜ|Ÿ·‡ÞݪÁ}Z(5„Ô$76‡ÁÔ5 “*Pp7á®»edÁ"c}`ÆÄÁ̃³°lÜn\ñ$vÙ ƒŸÊÒ˜ªá§•Dèl#{Lø8–7;$ÔXq‡e×9âwu?n<–.Wç] xß~üú%ÞÈéAüËÛ¸Li wŠni+r±I]„Zë+p›èÝYA`­ˆÕ†óÁðJ,q.‡àUgð~•ñØØ•òäñ$úÂüˆ«ÐºßúªpÃ++éÂc7!Cê½Ãl"ê²æ*ÏEøxÂKlÙz›éËYAç¼t,ßã‰3Ò€-p_,…SGÞÀ¿5wA0ÿ–àØUo.¹2žÓFà̰…Ìâ×kmVyZp®Fj¼›A±c$,œÀB6îÃiŽŸØ½Û‡hŽQ$±«ÞÇ767àñ'{ñ:?Cš1æì,{8òŠ›q³æxV-M£ÖÄã¯ôéòÖpø+Z ÿùŠ? õ,zðQ’CãÆŽK¨<ÉO:±Å­|âf££íJH™›‰ÆãDˆêå?`éM>+ »Ó±ÝË.Š'6lºd‰óY NÚ^™z¨¦D_…ûÁÀ nÿ]5KŽBnAMõá‡uZQû‡4oº7 þͧÖq·ÉóHóú&Èüu7™[Òðºþ¤ÿ– êù,hüüH଱ß¼'øÑ9bg)Ã,ieêõi™0]J½Þ€m¿*Yøö_‚9» ÉÈŽ‘|ÁuÂlh«˜.2uÕ7xlV)(¸l(|¬»Î–A´)±H32'A²\1”Òº³u^«`jóOV©9m'ƒg¯2»1ç0¬#–¼—C™£¯ Ê©Lͤ ø ƒ’gî¨)_$X¼Cñám̳>‰œØÄ†¬[D;¯·!¦+¢èßJðÑA/^Úâ°is.dvñífÿù—)Œ~6iðN]’¿)mü,ó˜iìRÜ:[•e®. ‚§pyp1n¾oj'˳O}:\ᵜúQ—×¹peòYòÑq9{@ >:^$ÚkaÛUl=ÝY‰¶°RðEÐzÚšŒu‰Æ¯ÏßÂ3»`+_Ö#CŠÆÏãziÅлEñÐxðïµ¹w„³6 §Û<ìxÍ/þhh=ZÞÛ‚¿ï¡×ºý ‰èÊ“Ôü5UŠoãáì^³*s<ð ®¼«;g\ý@ :5ÙϦ<„]?Q»þ‹y¼‚HÏÚr^+¡Ý>Nø;ºÈ¢‡(/}J>éñÍÚ|“a).¯‡_Ý5èÞ5ÉxPBŽ6øÔCé£ýø(< ݲÀNÂ2;ÌH{Äi¶v~2Ì:çKœ¬ ä¥(ê¦8ݤ'Ðã1È®‡ðënøêžƒË‡1{ Á¼ÆŽÀŸ-èý¢—œ´¦¾¡ÉÄ7ïïM I£òÉž`åOð±JÛTžb¿Óè W…IÙ*ôú¤h–ÚÚÅøv ˜;m:÷¾=Ã&ŠÓ8Oüª|ö: §)n“±ù`2FH:C–äh4—‡¨Ìðz›<õö³e½nïq1Mê¢"¦¨hES¸dÝK¡ÖÅL(MÁé·ˆô„PŸH–»{áê¬>‡¨ ‰°qä9ù6M˜É ñÑø¡$¥~.|²¦i¡·Ç¢nT7öüQç¯î©Ñþ3ñägü4®sÀ O‡/¥þ¿®açÄ6R=$ŒÌù4žÜ9Ïtæá~ù´H9ŽW÷iÀ:Ó¹ìÞ´Í86 bzãÎ>s,{œê û9]álßµd”(•Pµ ¾^SáÝïRŒÚû4ŠÅaÚe0Ý*ÅâñMê(<70‚Gl0 ùOîï#ay`-QÜGAäk˜2Êž4¨.ëh×ô†Sñ©h¤ Ú¹SÀv¢>Ÿ-ÜŽ®8æŒíÄa‘w êc0$§œÅ<« ¦“¤KG| QOOÁô¾÷°^ß ÓÂαŽå˱qÓiH;Å‚ÇjÁ£2¼¹òtcžb7ñ\¬•´¾Ê›ÔŽ¥x< |¦hœ°z¾ñ¿ MºaR@Uq'Û8õLµãªÞŠÂ©óϰ_º§ ÉX']Û®ëûpÏÞý°±â:Ö Gáõ‹u°SC‰‘À'Žâ»),Öd®ð¯˜* ë§«_ ×ýŠæÆÚãÀW›0oI%ÈûøËsX³<–;m¿¹6>ƒÚ~ }îšÀŒˆ\¸ü&Ÿ…,ü­ÆãçÏ¢3¦â›»‹Ñt~hÜÍ€4îÂÇXËÃEÓ‘4töxÔ)Žš+÷Ἁ”x8 ý+Æ•^²—k†A„ãaô¹ñ*j£¸Õ89Œ?æC«Spæç&²áÈ#Ô»nÝÕ50uúTÓZN ‡VÁ¥±u`zb.˜O”¤Â(ej«Å7©ìd-ƒMM´MÂ:IÔ¤(H7·§×f$Sâÿ=|ëÛ Ã<ÙùÖ 01†câ§ÀýÊQüf“LbLJ3õ¾80A›:»[£óúZ60ÏGѯ¥f@¶êSc5µáÎ"Ñà¨Å?˜‚åšù£yG‰Ýu¶òÊ!ØafÌ7?VÄÅjƒšpÃâÙªMÊ6·¡´_ßïpee&Ï6ÇékX¥— ž¿ÞÃjám’¸ß„¶Zý‚v3rgØl<ôø›®7‡]Ù6‘fü“c,¬2kÆØ»º´äv4XyDÁâš0?-šíò«*ÌD4„‰ñÿb•ÙG¢Ôë!àXR'øèªjòÛHðOQ*Øn) ¬ì Ó"ÒaÈ&^µ)Øu˜¨âî噤õ¯%Æl0ü«žvýß»lÜž!8õ]–:mýðŠ&‹@pÙ~ÖRLÒ_j™vYþáá-l˜ MK& «$¾2™­'`³Â3˜^vŸcÌ×ïdLý~}Y‘ÝŒC;ýØÎk ys'ÜVŒÄSÿ”¨øù‰8þˆ92{ ž™1÷ŽÏböºqøÈ~ßtá ³ô²Fëøåõú„àü[lɱ°ªµß^kïÇý ±©‚%øÔwiÄ[ñd>:ÊLmØÅ!I¨»¨?­Ã”=èÊ£—YÓ GØ1¸Ÿã+{ß”'ã3üÑÈ4â`N”.ý0V†?(i!>ãQ$]Ÿ¦Û´ yšîùú#Æ{‚ÖÇAm(Íów€/_Àò×:4êáJlÁÃ|Û­ Ðý­ÃŒ½éñü28aIɪ M0Q¸}ÍdÕ6]œ\™ÎnÂ]¡”zvÁ:¥‘þw¥FÎÔBV BêûˆÉî<0=(Ý…VL;ç1¾¼§¿WàÕé0Ab5l<ËSKòÁ£ýn*I¡Oýn+éæžyô‘>7ø9Ž[Èæ!z,⮣†±“í&ƒµlVI-‡–]óf˼Äa*ؽ™ÍäsR!$ëÖc@çßÆì ®`mnGêÃ0·T|u°xóFâ~m_\8:­Äº¬³äò‘'讳jGÛ"]¾æñ ~ñôS¬QÀ÷¥Eøc‘; ÚaÅ-.-â‡ý&¾°~K—…ݼÍô1F»3K¹Æ?Ú÷©§†B°"{(2æ+OàgO xؼRÌ8µœw~3äž:94]8*îBõSíø,iÕ\”ú§^ùÅ‚ÊÌ_õFa¦n þ¼¼œ?Ñå¼ánþ$:õüIf¿6Ï ,[OZÖ¨Ê7?kE÷a¬kˆ užÙ€>µ‹‰Æ¤ã »ý 0¨¬‚°¸Ûp™éÛçvãžèù.›ñÁ¥Õ´hI:±"ÞÜbb"˜äãžçGÒ™›>¡gÌ}Ñ¡´&‚|î] žÒ T¡»û†aìkÚgцqª¿ñÕ2nñ~9=ñe›9~ýÖ†»§à¿qÒtÓªt¸æxÅó²±±,ê3¾EÌTñ­:ް"\”ÕϳÞc@½I‹ÿù½Åï#ÙÊ·à†Õ)0û.‹ðó9&¶£FOse†wn#¾ÿr E§XÐô6^^M‹jñ²Ç4~nyž`“ç|x¨ YŸ×â”YWÉ>eù '.è ÏôüébŒÿÑøÂ¼Ô;½Äß;±'Fñ«cø@Æ/سý¸¯š…—»®ž»Ÿ×_ %yLþÊ;ÌõÍ¡ÿüw 7G-Cù´aôjÚ^ÞöSŠJåˆcçˆLlÙ°˜¾/®¿îB†’>_“4Œ„>¿·õî€M4ðcNÙxÁî1†,9†Á®J¼kÊ x‡'ÅWÕµFÆL§‹ÇT¢æKšÕ̲´Üá’ÔZe÷DºÞ@ Guå×ìŒáÈ~¼—[=¥a„)Ô=}x-y-ýcó¿•¡OÓX³ßË'T²É+ž‘èÁZ,u'Žn×GŽ¢áï°ñí5ØPvR&Êqãý›é–©‡ ëy˜T@¶_ :\XÍÛº\›ÆŸÊÄC礣à3­ý<ú3âyùæK$qnZÞ×çæãýù‚qûèã¶íu.‹Ï¯‚­†ê¬³ØŒç¼X†*vñðb„Ú†SÎöÁª¯\µ£‡%RÄÑ$°;òNßQâ›¶|äÖ²f`5d4ÂU ;º¯d@ò}c¨u7àw?Ç>qú.`>¤¿ºËfnøA´‚¨o¼wBnÍãéwñÙü/_üùï9–žK$ßâñ‹CÁO¼˜3‘8œûåü¦ŠÔ²ôûЭOÔÎÑÕe–Hçÿ5gGïKÒòQ˜ç=šïȘÈlôøÉ›§áÚ$–-¯Á£§ÂpÑq>¡5è{g8¸ä>«Ý¢NO]Lo¿·¤Ñr©äiߟµ7!po5 –XТù)phÿضd/8º5šòã.“ñ²º2L“¥ß ÆÒW>úï‡~ã(ZÜ;|_{ñå“`Þ}výC>ÙúªŸÇäávïA>Z戾OêˆjNYŸÏªú›pIJ_˜e¬8FRíPɸù®ü?Ï6_Õ"‹{hZô–U9È#°æXˆkæ%˘̸$lhƒ®q€±ûß’úý–plôkP½ïFfŸÉ‡%"Õ²»ÄhÆwOØ•ˆæÆìÌ’©¨Í›ÑÇÙ†Çx8ÀÁ×VÔߡ•%ÛòÏžqlõ¸QèòÌ©%C~®„ŸKTè¯ÌÌäÝž(?šÞ;vž^AN¯ íÂ!Òí0,LžGLšJV8PC— Äe™ ¼?Bš–Íá SFòrÖ­Ã& ¯@‚¾‹K‚ çc_ðdH¿Os‰ UK>"~YxÓ¾C^xëÉ”vYê>» ò­GÀÙ· hæÏ“h5÷1ó”0 úWšáßÞ‰|( íéWH¸rÝFn%:jsñë·]0sZ5ÆÊŸcÅeû±ägŽ”¡ã°èCâ²PÇl(N¯uÇœm†Ülí>Hõa13¡ºÌ™[GÒÈy{ðæ‡Í­) 2fâY=1~¥(–*u“xq+~*E‘¶®jÛhIS–9óŒ ì|{ZàæÖB4×_'‹ï\cNWŸÀ¬Š»p ÇŽd/[Jöß„cþÞ…¢ÈKÐíý ªÊ÷í77Wèª^†Ãäö€‡á96ãìöû¸_l5á²Å©_à°¸ªO3GÂé7ãùëoK¹ù74/êhœrûëz⃥j0|Q8*7ÚÒ‘†ô¶åw )Ú¸FôÚˆ„KÊèï²|yÚa7‰ V—>°–aVnòtñçëðbŒ³Ü›ÀÚÆf°%# ñ¤F_]D³Ï7@T]9™qRRkjñXŸ}!Ý€ß&re·qØÜÚÎöù†6­|ö÷Ã[Ö®=×Ã¥ï4yžx4™’x@ z ­ñ Ä)”ìûgÈ¿šŸÁ]«ŸcbOÑëì<Å_¢ïÔz¸dõ•¨^Ñ£EVkqáF²ºúÚ8òé¤\ŠQ㙂â1Ì\VÍ|ûWl߆¦ÝðQâ"H¹³.ï2äîgÑ~Ž8û¾~ ©ÒþŒ6Þ¢4÷Ãcx&W Eê… f)91xæX;œ*|…¥º™xùpÝ]6ŠÀþQ|tÄ6sÊtÞfð-‡±®^Q¬ÜÒ€Á£6REÉY¤ÂQ—ê}T"U°4Ö…ö/» Ó"Ncî7j2£ëŽ&A»®L:©Å»•o¢YÔAüÝäÅ6þû/Û>û.Č܋–éìðÊ"Üè|HðÈc5ö…]1=ð¡=Z”F gËjï@àUæ‰yRô°ý9Á’¸*–ÊÄ‚ on*žO²Ôoªnš€ïµÿ’¢ÀWxöMŠTâÁ±ž±ïü1©Ç€öšÛã‡Ù·œ³ŠÓxT4Šç1¤ÂVhÔ¼¿&ÌÂÅe¹Ë©"ÔÌ»(l=öˆÜÍh‚ˆârö *îûBc ÐèNä_ý³~6ˆ‘çöÀÉwztÓJ)8lì@·Ö$Ж5«é†ª c¹äO¨Æý¹Ñðx˜*íÙ®B·œ9‹ YNS)$°Qø<Õ’‹™¤ðK#º°«^«¤x˜‘*†*/…S¯&ÐÛ#ž±ÓFGa7 бè,¡É íƒÎ™Céd½(®“y\(VRgŽ’Eûèî7þyÔ(ôZ´¾´˜p³ÕF|ìgY\õT‰KKqΕ1ìîuª˜F¨Y¼)½ûp(´‚ïTþ[zèhQP}ùžÒ¤nÇ=Pú¸{$fšD2[õ® œ.yá¹øÓØ™L}ÞÜS›Á¼@–yé4`MO1ºLúI6.l‡·^’({Ç<(Àãyb*f³ÊW˜ob$=J†Ùgå¹ñËyÿÿéOÒº´ÂªwÈêÀÉÙOا1¹ô×ÛjÎEÿ&º¡¥ƒ;H”Ï§ŠŠ÷`¤‰×ØÐe»¬YÝåSph÷œÿ$ –ÆâÀÜmð¾ÔË!òÝQX™%‹i£ù–²Ñðì&Z8ÿÂÒW‰¸°<Ùé·ä`z-ò¿KQéù)¨™£/÷ÆŒö¯ë4Fü%7ô…LiÚH® ÄÆî$æe½€­'áÉCºäž9=¨H›3oøŠ¶ØÉÍZ”ÇÉð­‡¦à…¶ý,fƒ* šb ´lšÌ%˜Ó÷Ë ÈJ”ûkˆàéÑ#±8-Nµª“ˆ Iv®¿±ï‚S-Äh|̤®ã±®N´ü}.†ÉúÐÒîDÁ/}áÌVcºUCe+ Û “dè“”!x2¶ ¬•qàãj\+ýÖ¿_Îv¯ù!xÞúô¿`øS'¸ã3²'[ÁÇ›ÓÁÚy2+&àLñI£@vÝ¿úÛR /S`á2Ô4|Çî>4Æþ¥Ÿ!sÅ+Yô÷Í_HSœaÝM/¡Å*K"#.F7}·çm†SiÉ¿aóÓJØ^(N"¾ƒ÷ƒ²}²þ”? áCaãL¡ðÚ´õýJ®Ü„Q^flû];¤›ŽÁ}Og PéÂ)3³xZ$|)œÉãGá­E ôÔògæ l¶ìżïúè=ñ#N¸sM*_ Â+ wl‹ƒ£{asª-{.ÄÍ—½@jíjô>•­Ù'ÇŸùoûVvR4?Æß‡™M+HàêÕè¬Ö*P¶kÁ1^3í]6†>8oC§Ôû’Á5Ùîæ?Dòp 3r‹ƒ™ñÏa£²ŸÐS>w›\ãŸØ»sÎsN’¸ã~ûãßñý®\àÂ|¶?n4\ݵ&GÙų‹Ip™v__Á×?Bx|% ^éÐ+c }`)omMª&iA¡B*_¨8‚Ï…·°åœ¯]}˜ 6Zs¥›`@Wÿ³zŽ/õ4¹ÙæÑdÑ•g°ºÉ…_œtŽ”?À–qOyñ¹ãw_‡:ô|d¦¥á‚¯2S“‚íOä(höCðêKßLD eîâ·„ß¿lËË ÆÒe+6ƒwÿ.áxë±üŸùVÚzi[¬!ÆÇô©Pç‘Xõ~6ö…}Ô…‹êpÆç\ä{ªMyK¤ïîă½ùø-x}¬d*4—ÞS²šà°B&çk/°‚6|ÒìØ©TÅÏHüÂ+?/#ÛAåæ:aŽ>}!ÎîßH£¥–ßÉÔsç17ê XÎÍcù»¶b±(ý¸y>Ûõ¨²–ÕÁêüP>úÝ,wз$NcFÛ QæûWü‘uŒ=?œ€YvŸÑÓ±nÁ×±ªpç«<ü®‰Â…ÇkéÅß|Hæ8\¤CŸË· ™´¶O¾ÍÊz&£—ª$±'ÃEFo  v[Â`¦6BšÿÜûRµlÐrs®“§Ûj6Ñ·2Aûñ1ÑpÛ‡1oo¿‘sÛ®^Àϱ`Ácš<¨3Ÿ}ŠS;uièâhð«4çgÀü¾0NB™vûž–´™*?Q€Üö\Ób ô„—qâY-;,òRâOdJ@Rf÷ ÝB§¶ÁÏÅÇá×'l-µ‚ÕVµ<Íý0žlVï˜YÔ²‹±…3Ž [Èpî ¨…“I±pøD^¶]‡}Ý#`ɧ¿â„áíP~¦¢çí†0Äv0àcf͢ߗN¥ÿyžÿƒÂÔ ªë­Ë[6¯§–Ú3Üô¢…}æQ—>Â¥9ËуþU›ÈAÕ•x7=¿œ3¢G¢<è¾Q3y’ª4YÐçûõÀ8j-jÕç2CG+º±f$­ÒêÁyzd´|QÃÈÍÈOP7ÿ\îá÷ñæéž½àšR¶õlÑT9>N®:~9ã—„Û¥<€3+å`©N8ô_[C$Cê!/KgPKÁÀÚ7ìõÊ0Ö[>+ó%›6€Uð=úÎêkð…Óª¼»±ûU_þ—e¦8¼™ØÀ^J\db—ß±Û»!{@ËUB¸v`?ÎeÀ˯³ÛÕ+¹HËhsë «¾eÆZËðÏ0BUÊeö±ƒ<²Z°ÁßȲL’¦‘¡¯Sx†— ÈçŠëçƒû^6)`J‘2kIWžVÁ§/v;WÜ8úÂH^m¿¬R¶Ò¿ÎÇAy¦X÷!(r¯äùèq‹NÿÅ c`³Ö{9ý —>˜ÃTÍâû ¦£°u)•,v€Î ´eû(ž ®À‡ÜÏ‚'J±åb št [8žŸý)Fwe f‹…Ðä‰U0ÑH”¶j¯ö|­…95.·ØE໳‰][4î‰þˆÃ²áRu¹òÿþè›Ú»è .ï]@·ï¶á §nCB«/\«[Šná)¨XÌo”{@f„*M{&Ë/=o…]¾ÃøÕ‘Ü¿Š_ÙWÉßh¼€ÝsÒx®Dºß ãå{Ïií$ñE/ÌXËOh(aâzã$}ø¦Ü]x0ó³?ý‘?°eõnó͘»}1¡6ß³èI-OÈ µáî*_ñfêKª°»•4¶Ð1õHÍ ·ÓúŒïÚ`ÊÃ;OãÈ÷Cq÷­ñœ:= …{jxl]?Ñž+ Ø~ë†5[-Fÿ¶\Ç¥Oº©nÏè^– _û‡¡}t õY5“=îPÓŠ^ß#ø¤¦á|EB x¦úâ¥ã;„ ·b»ïxìéEœ}Ï‹'ü…í›ÞÀ{…¸öïnÁÙ5WÀh^¦w¤L”#ðƒð SŸ¨H½ê‹¡žæaìðÓ˜ñZpáé*˜î¼‰üuY„oò_aç'µ~ˆ„„GW°ò`jÕzÂÏÓÙËpm¹:ÝÞ­ÆÃ#vRáŒc,`á r[¶qñ~ÀÛÍZ+Øðõž¸½|Ï{pXÔ¸¿!NFÈÓ_=ÉxA¢’ >(ñÒ& Öa?žÎ—ç¹Ùßp˜étðq,žÃ“ÂøÛkÁ±øV\m„‹Ü…?¯1j‘ [ö :Ý:Øúy§°+â1›…ý‹œÁ =#”þ‘ÕOŸc¶ù{Œ9q«…(n*M‡U^&8æƒÄjM®ºçƒÐ0e XmÊá—¿,Àá!C!åé.^ÇѤyJQ€³\}¸‡¨Å\µ:FÇÊð=_ÄÑcÅ\› Æ_¯2åeÒÜM÷6ÎM‘ËÒ¼ÌH‰÷Þƒ«¶õðåèàïv׺î–ñ³]Ê44|)ý`äÊ·þÂÜ®±Üôò#pôƒ_Ÿe/wóiFz´n'íÈÛCs:Óù?¹ûdÆ›~fa ÎÕÚ_‚‚jFcÎEg¬yäû‡ãÃs®hg¼*®T¡ýÊ ^ux6¯H£( (DIå83IˆI>*xwéœã6‘4*ˆŸÙÔ(]‚œT+Ç}f P¸4Lp7[“<3ˆƒHåsÌ•Œ&g4 1ãÉD¾oŠ1ÒcEWo:«+š8/ØÃF¬™L#D^`þ­•ÌV¿ób=ݾ ǵ͆‰G0Ée‘ݧðïdn¤$¹«q Öo<Œ²óª±\j,\ [ù´¸mj$u¤’e×Ħe  -Òm] z*wé;â‰ï üDb„I§AgÀþʬK°ëä&øØb‡uq˜ñMM›ïÄÑü0äß¡ÒÍðtr3~OXÄrêÙB»e°í® ÝÕ0ir Z7µ¹Fb2^¿7@ºGhÑÄ¿³¹ÝǹôfJ9´ Zýf OŸ¡?ÔiŸ×m&õ¢M‘GI, O½NΫƒ¢ÃðXÊ&ÔïÎÇÃÛÁšˆ ¼ñc(iyEˆœÂblåÉwE-៭˱fÖtl?¾^Q ½ûM1çÑì¸[žz ÜžŽ­Ò@ɵS.oCÓ÷Áxpo» ¸Îâ²ÓIêÛL¿(³;âï6In¹9=uà‘±mÌXÔnÉÅ“¹cyV¦.¹õù $T¡µñû]Kšíã¸wD)¾öÓ…ys÷ÀŒ·âüܬJ©r˜úE¦;qÇ%/ˆÞmy.Þ.â§?äÜÔÉDøè*n= ×…£±äù54…ó½c’·ÊM–¡ZvÉŒ-o„ºôcìAp<‰±k'tú>X”J'¯2ÝXoVyèؼZf,„—'Ʊ 8ðt©š¾Ÿ\ð}dž$Ý‹ç»QmÍ:P–0ÃWϰÔäBho.Fí}S„S¦Dïj†Ö%¬b© jÊ&?C’ˆ–›.U™.;}Õ0ñÜ387ö,y–L™jLV%Œ€sãÍ+ÕDf.Ð}¸*GcvÂ×,_*Õ½Î.ùK,ÈœÂõþ•ãòçÞ<Ìv,’Ñ £Jß1³M*¸¤à*~Ixƒ±cÞâN2u~·À›ò@á››ëÉù~î5’ßZÚÆM;‚>®ëøþÒQü؉ dmó-TôI‰œHeü´á^ ¥‚GŸiT(Æë§£¹þÒü²U2ª.ÏÆ£ûÌð_C"L˜ðøÅ|&Cn%ÎÆ„ðß$PàÛ= ÿ_oe8¿=š‚FÐ7MÅ=w­z³GŸúÿÓu‹"ÁàÖ+Ø"+‰>ã„ÅÔ†L^Cø\ >tI"Lë,…ÒBÊ®M ;/AÅ T³QWí>ÃrÏåcìÐVpì,°ÖÁâx5ûþÀ›ád†­9VžÂ°û§Xå™MPÒCÒz·‚·åCû¶3Ék5X::—Uì ƒë†SQpd8¾ü+ WeñÚ³D^R†öÌ †ñòqöí˜F×G[¹tt&¼Øëƒ6³¥+$ÆŽ…M§oa|À.jë„Eë ã‡[aâ˜-”„³Ûj±á*l=yò\ê± \ŸòÛ€žö?0id4~¼µÒqýòs‡9þ~ð“Īÿ|–ºÏ`¶ÖE4›ÿ¥ÚÑ-,œð ?ý FÉ=@ÿzÒ>U„žõõÅï‡NÂNÇ&L‹±ÆBç¨%nAûº.£ï}{øg°}0Æ~3vœ™ªN÷+øM8ëÃ>œûRòëÞî=À¯Â«ÁwyùÎ!¸s§´°Ëe,ì8Ñ cµ/‘ë»ÁBÄY¸÷¨5ݪ^€Ç#ç¢ã÷ÄÏg2/’ §·¬qñ<×yïO>'þ±J4/ûVéks÷ ŽXž¹ŠÜ8uðÝttYŽçäÆaS×rH/žŽß/yãÏ ´uÙp8š‡?õ”èk»Ñô£c1М oKðؘRZ®¦ÈõÞëÂö¥˜¦tkµtÉÍ^hžÅÝÅ4 Ú³•WbÅC<+Â÷ýé°ï ÚÁ‡ÇäñíétQ±ZR‹G­äÙ‡€³ dDõob×âõ(=¶L>Űï/· |®S°.ücïøPÇÃ$»ü3Y¸’U»äÀû=ªÔáx;v¤u4úO©†ñq°;ð T~‘æZÆEª M§çÒ!:@lôÑÕ'ÉÝmoØîýMháÂ/ÂÛ,¶ã˽– ß݉‡'ö@R¸:3ÉO\²ù„z9Е›_m/€É”™tÖ†ÅÜGÚSG{ƒ¥Þ0þ?’®<œªï{ËL™’¡ ™Ë‘H¸{mR*ID¡¢(MJ4ÈRr§F›-Ï-aH&ÎÀ¾' ¨7—k•Ž£•ô …~"3Ũ²pwà@0O3@WÁcXÑðRF‹ÖÏuƒ1ñJÿñ©¯-ÈŸ;Š¢‚)\‚™žÂtí»£8}^lÛöŒwÇbýï£ôà'!Ú.“ÏùZâæ[›²±¿uÈÈþ|½iaĨÙÏ5‰àU·1°£í,ºÞW†eáà鵎órW¦ÛŸ}%3öJÀW«"¸oØNÎ8š±Ÿ06ˆo¡/&ÛÀ"%’‘©Ï,BÏ“ XüÕ 2Ý\˜nRæŸõÎ`ò²NÛã)/έ08ŽJJ©xï—ü§‹v½œG®MÈæ®m‚]#‹è©î<úþHfåÞž,ôÁË×CŸoÃØ0]±‹!n¢½Ùöœã (ôΕà|cõØÝCÇp:o%fE?!νò<9‡r9§µ'OrÏ\áB^?°å;È ïßÄZÐ…{{Óoõ7£ïÙa2‰çŽ&w³9½omÖŽ‰ïRì‹tlÚL6üÄknkp_ä<·PšíZ/2©IüŽ?³ðŠÅ5nŽý$·,x2 '™Ü¡INöô ìÝ}˜pg­Y|¹Ī‹Á½Õ“ØÁ¯dŸ£4äLøHœÇßÅÊÄñ¸~J"KŸø‡«Ðó üÒ$®ýà¯|=/„JúwGv¿ºk/¡ãÝ*¸ûW—½Y§(vû¡ÍÒ‰|˜ îG¡­3ZörÇІ6Â"e>ÂñÇ8• È¿Î6ûŒƒ­®¥h“%Ææ} rÁÖì×xYæH_ çÕð‚­ƒýÖåÿé·‰ì†98R^ÉcÅî8Óa98žÂáe{0­?}Êßòä=Ù5á"¼&¬m;ä ¾Z剜fwDƒ_}"ÏkÚÚuû•¡|º+îTZÉ%ô§s ]äÔÏbïÕGPyÛa 8EKPs…?³à)hØ%ÃÍ·G¹Õ7ߣЂ©Lç³6Õ çê“-è æàÆÏøIXŽÈ'˜/a_ÍeÉ2N˜]èçWÛØÝIþpe_bóqºÇû’Ì'?ev¢û_-(Nuç~:–Ý úÄKG”ùg·C7¾róï)1óÃ'àÜâF<·ã=Yk£BŸ™þƒ¶¬ø×oâsS SÎ*²ë¾2xá™=®·» Ï9Ðó¥EÜ¡¢ ^Óâb^Øç¢ ¡%ËéÄÓkÉ÷€L\¨8ˆaóéÓk Ú¬áÏœÂâŽÑ*LrV;\ûøV£þ5O)œñû²ÁiòARÒû Ä]eQlÇzØòm˜äE3X¿Ô^4„ÏC$(ã\:¥ÿ·ªzÖ(?ÞƒÝaôt¯5PìÆ5w‹mÇÙ?ïüŸ|›Þ8ÅJ“í(¼É9Nb“4Èw÷ɨMޝqÃNÇÀ‡g¿aZÒm˜ó¦b´–©ò/Øíƒ gàÄðäÕ#.)Ê Á)gpÄ#Ѝ”F¢b´7eÿFÛÞ\¸éODôñ.=ÈÛð$TÙ\ù¶y ÷hÞáüÑåÙ3\è–ó¾ã“D5*ÔÊmênþd|©bMyþ ¦G—âç )p!RªÿÅñëÌ÷ò't “cakˆÿr¿‚ù@N½P AÅ{Éâ¦$ô‰¿Ev=QÀG#¬Îü—nÈ®Î} 3ÌûàäQ ê(Üx.yþËÆ“€9Ѹ¹s NñÌ;üœmÊ>óÇÇŽ?pí…bØy$´Æª±µŠ$¯ÅöYœÅŽ6lSX0Š 6áÅ«Æ,±í辎åöGÇôÊ0I­„[aâ ž]Ä-£ø¿ÿ%.Ú/Eãs{¹Y+æãwÅ,2e@›-økÁrm7Òýºèã†C•嘷±w®~wOeѹ6lin#×(y³Jÿ¢ñã_ø+âwÄx,û´LŸ¯áWNÌ •¾RÌa….[ºY•*„†*h¶ö<Öw¨kW ³*Ûû7i÷èø~Üv£ßd[÷g”ä×þOün­0Ü’Àª]©¸DDgäðîÿÝ‚‚çôYúe!ÎÐ/þ×'Zÿ£ <ÅÙ'©ºKlc‹[­QÇ}»m˜å€ ݽj";sñê}¤9[s¸ëy döaé–"4t»º¯nÆß–ü*Xˆùán­–aVÙ'Ù`}¥4‘ÌiÁí§àû½§m¢!×9̧1œ–ÕQbž8…÷¯G‚]/zSäÜ9ƒ­Ü.¯,|nº‚Í®œG×(ü×ï™N|È2ý,©ÕÉEPZ-GN`UãW4¨“¥â*ÌlÇeºÓ¾rp ­‘ÂjµNTÚé‚ÎÙëP¶C† ·Æ?‡žƒ¦0{7» öOåÞN¦š¡÷ûAZQ JO\ú¥IldáøôÅu°.§Õü‡ ³ªY–çÉ.„í`~ùÙlÜûbh·Y1r:4Pp#ô|d;£¯“êÞØ#¶ÝÃèæ¦sxŒKã¦ä a`b71|¬DMÉröºÇîJ„}‡˜èž³¨‘$Ãî©ÉՓľ±yÜ)¯Ùèãù±ùûð®ÇæmÁf¶ÁŒdN@Ip5k\¾’¹x/g;Þ–°±Oâ¡ÉÓqWƒ*Ò _öáy‘­^å¬X×#yºLr6ç_©Ú'v”Ü—1§w÷²ûŸ~㊻%œ‹ï^ö*i;-ªÚK_tý³áYt]Å{ø”‰˜ÈÞk›ãš¡.ô|›ŒG<­`Z·±BŸ­?+B㞃ÿ´ÞÖE>`uZˆ~7¬Á(ŸLòwV- =paY ÷1³÷°¯oS æý \›vë„«à…­>!WG ¦þ‰ ÖtãxÎ:ߨӗIÖT¢î>¤/[Âx ýà4ÎúiÄc}¡ x¥eÉ ß[•9Š'qƒ2©ññp0ÉÎ…á˜ÛÜÞЩл$/î\‚?š‡É¯9ÒìEf/,>y¸÷ûðq†2ý< I£öAP]·êï3ü;9¶q§‰=ôü$îz(þ¿ûf®¥bV‹9<¨Ï öäOËqó±xW1^ã–ѱ¾/ðÈÙÎðh2¯Yºªqí.I²¤ÕùºnÈÕ(á¹]žhVûžˆ*‘º/ÁJl/Н…ÿ¼°×× ¾¬†Rh¨1*¹@KʉÐý1È?oÄ2DaúŸN2v`üµûÀ_rÞ×¥ÂÉÄϯ²'Ö³{v|œîüŠ«2 ‹ÏºsK-NðÌßõâ³,W´Ü:è÷:üXíFo ár5æË“Û“ðdŸ)[Óº’NÙ¯ÅFfë±G÷”©ÌdS¸8íN#šJuÒݱû¤4ï½È]8nþ’gž÷…†|Фó_Ð+–É83î"û !2[+€ìvI‚•’cXLw=ZÕÃjýðrb6Íßẅq Êû»ÁR$þL ÂŽùáܳՑ8ùR;”ÎXNç6­`›üܘU¾ [¨ý›Sq7aŸ\•™TÁ3î˜ñLª.n ^¥ -AÍZ Ù§Þ»Ü!Š_náù/=…o¥Ø±w7!_Tךo#Gkb¹aG0ß{ mð‡f·@| 5åv¨_±Ì4Îó|îöñ£y®¨6-¶+:Ð"ôþötö6sW*&Ξ +VfÓÅ…÷¡Ü:„ÝŽ;Œõ{4`å?a¨v|‚^xj”MYÄîÕúS²õe;_Šcì’¤tÆ8zþ‚xÿ _•…D®e|½“`ïœÌ[sdì(ÖZÌä§0©´¯°?ï4~Ø3þýõÕEWÐ1s`Þ måЊ%†ÜÅ&Î}VóhÜÍúcÂé`a,{8Y™^(YÁÖà:#ćþ¹1‡õ™™/@¾€¾¬%ØÒ'ËÙ<æ{§ð–žmáÆ{M¤+-?ðŽ×…Ç/.àíÕR ¸ô©w›À>h^…ûóçÒ™Ýɸôì.L³`wK¢©à¢³ÜE½›gÏÖ'28vÒ‡nŠPFÃp)6pFË̓T»'g´d.ªhÌ`8iöíá)h]5v±Êyúðh#Dkâø½ƒ 9Å\”Nq¶õû9‡ûÝ8R}”72Mˆ-_xw\»Ÿ®.Ç_­¹O‡DXÝ‚í ánÑÏ«q©øk.~_ ¸¡c ®qîˆÐèçàŽÛV6vãlÖXÛÏ+ñ…&¼\Þ¹;£á‡t Û%BT`WÎ÷Ñ3âôäÒ¸WÛMì&±ÉÙÏÉÇuü(\ ³<pþƒ(*W¡uiüJUv|®†¼‡©Ã[™qÀQT}‘¿×ëш¯ƒ¼ÎÝfìÝhÖù¯²À€>d—N¥2=£ít÷mgnc`mï?o`-ÒÌI3š•[‹Ñ@ù+ddòR5“Áà±sd“[óÝ—?Ô?Œ;ꥼŽDU›Qß}ÊpWS‡Z—ÅP­Ë~,§6š=Ím†Sýaœö™3¬m¬$zdžÄ’Á6Þ½¢«Öa2kœñe<|ŸL×—í†y‹ÂxíY5ä¯S>ÿBö+W71¿ëpãÉ^4¡õ ±nõ“I1m qM–Æ·úÂW=ú³ÿ3¹²×˜µ›:q¶·’G¹;mýìÍYÂâîºÁ4ì®ã`mBöÕ¢ «²1çØ,V>AŠÖ†@÷Þ3XÒcA¶²°¨µj"+øòþ8>©<ú'cÓßÕ‹ëdÖâñïâ̰'”vÜ¢øOõ?[­ǽò ?ŸæÂÒ˜ t`à*Ê9îÅ—»ŒhíEj»˜ó„ZО‚S&ÌfŽ%"l_§|6ðf+zÄÙÏõXDh=–fŸ–¸‚t[4{½&= #¸Úرtû6E®su&3v@Sΰ¶çb´®R…·:8‡„,ü éW¾hèMöôâ íu¤Á…ïù¼³gÀ¼ö"Î[îB{&“èÉßI«Õ=6š~G*è>×qôÄ|:c:m:ÁòFk¸ ¡(óŽìâ…É0j{ñŠÿQ¡67â†~&ø@šÝk[ÄJóç±™"º´äÍ6NôïEè±"tRi ¬ö¬ªu¿=›Mb7ÑoÁUìA” ›òæ%˜?á] ²tÐeRh´üÕÝœÈ¹ß ©¶šîÐ_†’¿µqvÐ8°N W×ÿÃÿ|ñÉáãñUÄ£"Š­Ï«Åj÷Ùß]Üpª?ìšêæägRû}&Ü“ÅkìÍ¥Wæ±5o73¯*œé<‘.JðĽÍX`H*S°`Ï÷÷Âkêt'›}äT¨³J4Õ5ÛÁ,…öcBöœ˜Éì\A z[²ø·V:GŽõãÓ }ö%3Ò+÷Њ„Cèà"L§Ôh²v <š7€ ô ÚuKÒÓËS¸OãÍØÍ,z£H€ý)C='yöhŽ9½"t þôF€È·wØ9¥“à ï`–8³ÎÞo¦EÊj\Ò9 œ=µð´@«+ LŒ]Pù~&»è°À?¿ÁÂbVæËÀfÏbfæu›+þ#O/w£¨{:ëk yMøéÛ=Ô5:Ì Ì¸€örÆÔÂ=ËOà ýMØÿB„ˆíä³8ç¬<1«Æ½†Ñh}v6©ó3 ¹žhW|‹³¹mJD?q^÷¦pÉ/ƒ±æò2Ü4•è_…÷_–Ãë¥1à´x„àó ¶UЕÞôº„EÅɃ“äçͳ°»¶„¼<.Æ"žÃæ‰{0°ÿ>®‹êÇ52ÿÀôÏÌ” %—gdr‡µ¾‚†.à7ùnòæ/ªÂó•öðÁq%‘eÉ>ÄN£Ã#!Öí X\À»Ò¿`ü?<èÒ8ªe¢Òô’ÁXTÑÔƒ|ÿ“¤~òlxÑq ·Žµb[tn¢Ü¿HfÝ ·.ˆkLÂõÀC»y±vÞŽ5ì䱋¹Ðòìjß‘Aß¼«pUe ~õ€ö×'jD.•ÛÑZñ1LÒªžj.È¡^/36V“üzw‘ Œ„å‡ñ¼› T9 ÿ[»›€A‹'/+"„Ö½â6ˆ×ÀÒo9(òÊî¦v@ïMPLÞZ[”aò dñ‘dµXò 7î]Û&C±¢;HtJ°3?oñƒcîÁÔÏ ñO“ú\Í¥ŠSDz3%Z$,Q"h"Lm±€œQüºDÔ€&,Í'OæË0Ï…¸WA‹ÙT™úHø,±¡‰¹˜¯ÿÀ®E—þ›§Ï©Ô-Ãf»Ì»~6¬ì6bz†ôXØ}”?p2Cd›Ö[³º/ЪVc.aºùå¸à>ŽtøáƒÉèüÛ˜{õ3‡þ}˜Å4–ѹŸaçÅw¨š#Ï”o\£',fá×+éï)µ`žó>í§³ç?ǨkYÎDÛMÒ ›²ƒí—¼ÂUŽc&5j,²: <Õ„ÙÅ©ç,@Õ© ‡éÇ›¬ŸèÐdCŽ~OÎ Wý¿Ë3ìõgS÷MdIRZ¨Ø—GUZjü`í¶Ó¨Ô8†³iÏG·ò(<5l ÆÏC›ÕÙâ–tº¿]œ­“‹f'+²A3*þ‚PÃÚ\xT¢Cÿ\JÆ× “Ø 'yü)èo @¢Aï^m‡#!‹€žÿ goüD˲d†Âœnü€›47[0á,šG±]W ™I©|-Œ"†ŽŸÛ^`mõ”›øw ~Þq™\³Ùȯ•ÕßÜ[­`°>ëŠÝmðâÕIk¹ Äg¹;|kþÔVÈ„yŸ«@Ó'Ž«õ¡bÜâÎÝXq÷8tµ®CD)”÷ï 1Ëï˜ó‘Ó¿©ÈbOÁÛ/æã½¥*¤,]²´3¹Ñ›ö5¶ÞàƒOŸ•ÿòý— asÅ0t·Á¸ŽLº¤4åV²K³1Q'Ü_O¦Š/0or"ÏçùS¢Ò-NÏñ‡¹N‡Óøî£å“_Ü6?-Xö›°¶c@LÓXR£(½ ,̶´|ð[Ù÷ëê쓊0*JšÐÕ‰ñÁIÉ%˜­Åûm+Á,’<ÐaË8ºóµ2jï‘aãL pñÖ&„‰ P›‚g—Õâ³I¯¹mêyha$A/‘ÍxÓ#Ù#ž™{z,3°!dø?2Þ~x†¸<rþ<Ǿ ³çÑb.k{Œn-¸¥ûî¶—kTþù˜wOé~V©â¢oUñTÕwWµõ³Íü}9(e! ›p®Î ‰É&Ø›hN Ç$ ˜Ï%xÚ%ôÌ Ö¥Eà‰|žwr)Ÿ§1íɺB`I)yŸ[òŸŽ¼†q"w‹?ù3ïçÉßÜ¥u‰ÌQMzñýBºøŽý2¯€UüQ „–€”† Ôõ%á\öí+u ¡^˜Ý/< Õ%ÙÆ¬Óo¦Ãn4ŸäçºüíO‡P¥djû§aÇÝ}PqË’þëZÍ?s„ÐãXLœ>lwÌKð’;uÞ“>¸W I­gT¸P ÂWꃛ}wèý8öäd0}¨à^¦3èõ˜-t¬Fœ9}„»÷׋$ìªÂ '£áÁ|‹#&¯–ͳ^;ôòò1J¢[]MÙ›AqöBó\8Ó2Ûãy –Â=í æ8~ÎÛã=2¹§Óq›R_ ‹µ>;{ØŒAŸ0[;:yi n™„MÂt~äONÿÜk¨›aÁ”_ÔcRëÈŒ¢KNϤwêNó>ó…˜oa Š72‰í–0æ„6ËIp§gÒä˜ÑàSà3Îh-­Ü¿¼cÃéÇÌó0/A€M=QëÓ—±;…AHñí¼ÆGÛSÅk_Èš¥r´ÃqÕùuûrßKqá3í*ÏyA¾Ru}¿‡ÉœàÑÚáü×p¾D„®·‰ä²ão©;5bÏ:*ÐÙÞ»Y²“=]s6–ív­cqÛXá£+ø÷Øm(¶‹iËÐýJÑè<CÝ7æÃ©;Ôp¡NÃê¨âs»­¡Ý×q£æ¼³h í ¿jYÁ,i-«8ªGI´"´UM»l6ïûvHq`1›ßN¨ßö%S´ðÅÙ’gφ[Ð ÏØÑ-¹´,ÃWMº…¾ç@³°,žš}ÕšÑmJ >Û«&9Q£9)Cbôî[lÊ5§·RÕJö>³ª˜ë%ÆÈ¹Nìlè{h¬rf•Çóþ×Kúí¿±Ô%' ¿]K£ž·›àö5–¿* { JÈÅ“_y_ûÿ¡¢#ºÖäAb‡)F e¾!,,Õ•¶ªÜÆ÷ôœÛwô§ÆBfã1´û®×˜q!ï\w¢…ê3‘+ Â=Û~ay>„jᢦ8Û{‚@7Çp )ÈðЍ9·â&¤N’¢‡ÊÂÁoÏnÇ.Øqó<+yˆòRÙdím—1(ã3Öøe.‡§bü8̳øÆÃ9)cÀÄînâb—\ÄODšzêÐ1ºõü S5 £k€`ñ»ÐýœIÌBšðX9‹ßÉœHÇóø>²ìXÃvæ<7 Ä„Õo@e— ¾72¢"åXF`Ò弫Ͼò¼rà§O¾7Œícè²Ø>–ÆÖX_ZÛß³õ¸Ð žåÁ™º=`ïW·ÿ†È½0ëv2lmߜӈÿ Ó!5ºa±0f¾JckLwpN23é{µ©ôÑþNÎ*£›g ³¿§”h¾Cðhn¶æFrÄØ;#eTáDqMÔ86þòrêã|f"R iÒ0;ø®ÚÞ Ÿ’Þ´YT1í#YòS‡–:˜! ;«í_‘™Ï3±\â|œ &“¤ÁXI‘zÂ×/W‹0\ךA²It(¬…ó:z_¬˜#[9°­o…áO£ HÚ‹ÁzÙø017_‹}‰åü·É>‘j¶ þ7 ½JD½”O8?f‰ ›@ŸŽ4ÃѦrèᥠ¥_"W»Ôþ?ý0x­ÈdÏh±Ÿ{åé”íK¿µŠÜ¤iù–:òíP0Zçòhü‚m|×c^`j—Â8èõçüŒÃ”]}¡<@N…äòl{`k -ËI¥ìß'1\øDöz³×µ±t•ÍÞçâ±t¬Z1¨r«p±]5–„à‹£øu‰9TŒÙö3Yÿã¼7|à´hl«)†ÎáëdÿÇû¦‡¯€€Æ).àU:ÎYÆ*eØž¥2ì±™&vjSáW9¸+c }&u•Žn‡7þ"´ôŽ [U> Õ7y€û®ù8Û]’m&Æ[¦RŸô`æþò"þ“ÎÝë×GûâY쀧?[•5D?Gý£-Äh”§FyŸ&†U)œæ“JžDòjWþ‡\j­ªlPõg§óŒÙ?…-üÕ»àÝ–j^ñ×=``s ÍÄ ºõÿ5ÕÝIÑxâEî(·§­O¼É±§5o0›_3×û¦³{ZéßÎi;ƒwœkÜ,Îþ‘‡¹>¹Ü.ky¦${Ûg}e,¿^ä^6é³ËÌ–é)Їò3èK‹6|W¡ÈîÆçC‡m—d§ìÁ9¨ åù»vDð[ kL4(Žr;Uþ3´ÐþÂ)Ôeá×à™f1)?“‡BiP]- bɽè7õ: Ê7 %Õq^g²ž1{PT†…»ˆÐ»l¯Œ‚EÒÌl•:pÜF×\°ö63ÎÅŒ”N,ù½®òá"ÃõYƒRþ]ƒ·BF¹Ìˆ ›Üýˆdß!Ö`»–.±Ò"M“á›Ñu²|^ iö€³Íï`þ¼oÄþA)’ù¦@3†ÌèÇFU"}Z›ÙýƒüÄÐÓ¬ 2²ûÁdIW0#ËšéSAÆëÚ‚…%›Áýw(»C‡­Y—ÆEXN¤Š´Øì×¾üv¾UrqcŸæÄÀ=¿zë²~kÀƒn2ÔØï;>_`J÷ø˜SgaÊ'âEsq†äU88…Î?ÌÒópô>xxðnpg&?wBï(ß-MRÊGßÕ&ÐSö„èC–úæºï艫:œ\m'ï¢äXõõ$;cLýBêP`‘WŽðŒW§R¶d×)¶´¤Ž{öë8W qê‹ÈøŠÑõýþ)îPZÎÞ%-¥«¦Iòäe'£Ëi{–lÚÁE÷Ç2GNŒr”e§í+±È³}þ^&ŸMRÉ5«¤ò¾4<4øÄi¾× á«üia|†˜.`T¤ÜÒÕÈ2ÙW¸Cö¦œú‹&²Ž;• ºëÚ>z åΕðël0¸y­ÅY¦o¡x•ÞØ÷š_(ÑŽ¤l±æÈ<8´Ñ–»ó”{®ª»í$3„au˜u=›ˆ›c0Rî!‰ÿã4èä V°Ãpæ¿ÈV¥]â°wÎDê–~œ‹c&ókúÉ•%2=´<#Ó—“5‰÷±WÕ‘ý\¤‡ÞVÅðyà X_¯$„ưPÍÛܬe ü²ù;Ü…tŸs:6*ìœ a»òoÚüq«Eå¿ÈBs8» •8é÷ x@sÑÂì>ÚsSé'ß© qm-†8ÙánßNéø’·Ò]žf,‚_Ó&ãý™Ñåž1oÅi]Ô’XEwMð#{»žAYG=¡ã ~ïeØ¿® ]ήąo—Óy‰+!úôf ˜Bß<̓¯g|¡³«Œ»z| ŽS9 >¨rEŽÜµ®Ä#­á`5ÿ1Þs0ÅWrã@äÕ:ò±Ü&"'ÖâòF ¹cضfR½O ˤ§°O•yî»_í;÷òÎÜ~7tpUx¥žÛ¨‰„7›z¶\Çêæ,xd~›û±x>¼Ëlì¿q BÄ pùP*f¨.ƺÒW`”Ãù{<‡îÊO¼¯AALk†½h>:`þûœú$ŸÓo%—îOA‹µ’lX4Qû¥K/¯¼éÇ«¡²H‰–‰,Æìçæìqñküc<u;*øÉ’Ð{Y‡®ž÷Žó(Ú‹¿`ƒE=OuÑ;øï·û­Ñºf^ÎŽcpmÀœ¯u€3Yó¿îÓ£ï¾å WåJœ—v ’ja‰ÚF¸lz7M¸Ì ŒIÂEäáv±{áÿúCgú×¥w!úo {ó«•úD˜fjÿÆ€.”†i@ÿº(Þgá@’lrßG\Å%‚=ØV<‡Ìꋃç€p7Å„ˆ5QIX~3—¤Ý½ •î3ØøhcN}ñGôÔìâö ²ü…bpBa}ãOuWV’€‘/舧}¬ä‰}5b(ßjÏúLü𣭩œ QÙ%h(GõÔãpØmÏ6aAµ\vzYq» Vv~AÕÕB,TbôŒ½Ëk݇Gxò!'áe’kվϛ« ŒÎáÁÑÛaMÛtâN¹2Ù_J4½{'»±Î¹½%‚íqôBµ(;| 9ÝjtTÒ¦²>ÚôÉ,C.„/ÿ|çÀ‰tÎC7üçb š¢ ÁB”&,gð¾Í–¸ÑŠ8]”™|5ÕMx÷º´0­U‰ÉÆÎånÎ0Æ_Æã©éŠÛ › …á`þj¬v«€¤…½˜´L‡öZü%ñ)lšhzô‘‘ZC&ûÀ ïl@AM):[°ˆ¾ÝÌÈØI7¹;‰‚üo#¢õaö|á eà«Gè³½,ýZ2U©On^Í[%ÕŒ_N¹cwóåQÎnH/¬À ›8ïÏzt•Kªä“^‡áÓnqü¦"Áôç0œ·§fbÛEÆkBëAMfà‰ŸòëIÛü·àòì0l¸xŸÏy(€AÐx<¢Ò€žÂpÍ{µÄìcP²l¸)@#·[Ðá|Æ|x ýg®s÷Œgš†SèîV ®‘î@¥fáœËÁ0äWÏ»m! e—.’'Ÿø\nP!Q4³á«‡œáÞܽƒ»\RÈÛÓm—µ«ƒsïc6Û=è[qÃé‹ý},íï὘8—FºõÀËë\ÆÇÛõ¹1q׸óÍÝà¾ðþ§…Þ¿¢‹¿o“&¹xN…Ù.0ÄdvãæÚP¸RRJ’?˜£V <ŒÎ‰th¯FƒNkМóx5úýåüót:Uøà ¡õѳ<Í‹Iê燄b69z šU­ñ¾¿6ç^7­þA"—™Ð JÁ¼Õ IZl¹jM/ÿ­Gd ,´åB§²fQqnøÃÞýý»R=B!½Ë—<¾ó“Œ·MƒK™ÅøuÒX*ûV°ï:JV^€Òð°2º‹ÈÛFû4Ïÿé—¿dû_øßΊ bÕBtyœyœ³¿ÈbîE6n‘ÿü1±Kò&ýGQÂÌ…»¥`Eâ»q×éxò¹+^ .ãÛKiS¿1›Àðg›­îè>=¥s’Úç¢ç2£HÈý#LÇÿ®ä´;³¬~Q¾ÙŽÑ9 ¥â éKónû¹4®)†ãïËgàMû!º2H•¼p ÓGyµØ=n¯é|¦§+Žá¿·€žE ðÛT€ï(äתRz Í+¹IGàµK/¿ÔõÿŠ±Ñ¢q¿.Ûwìºæ‡·v¸ÃïïËPmŠé•| X^Õ9œjJhl‘ÆcÖ'1X’½Ÿ% ÈSªª˜”¦«º¬J;Ó›pìIWLzÒ̽Sâºö]…™¹-¶}gƒjÎ7¾Ä_GìýmÁvF“÷D¾ÛÇûCYÓQü6k®”Q¢[O`‡µÍñÓgßϾÁ%eçð÷øVþGI V ”N·Ÿ ÒeRø@šD¤äâÚiX䲧ϰ39!]h„¹æpÛy:ŒS^›¦ÐÆ@'2 º2ÇÐjqmæfYÆÉ„×upmä~ ¼¡–Cä[Ü%·>s^Dð¶¬é!ê™WȬ‘\<<H6fž {Ë1ˆ˜¯€Û<Á/®˜»0ë)Ü*òÅË)ä¿9ܶê²lßîáœ/œ‡é{ìÁ§ÍólÅi5¢…”ÉÀi<ýÓÊ'“Ïfìºû<;øU?M¡ú) (¿–¦ŒQ§‹ Ö{r3¼='‡›‹Àé20ô>#üí°ðßTZ~]œÍéöÀÓiàs_åH…µª‘(P:æ­_û½b¡÷É!nÕŽòèéi¸¼ö÷`¶.˜Ý‰£†¹þÌé`.ݱ}!Î ^‡ªÈ–­ç`¹Ùb6w¤½£÷=fú;‹ËkÓ* M7ZÔàß¼XTùcŽé÷–Ò‰ƒÙ`ú[ò¹²—¾‘ñl—Ÿ•={áöæñØåýx™—¹ ïWÑŸ<ÊuEU¡Öö¼ŒÃÒPaº¿(Å‚éátÔœnMßÍŸFC%V°„kjôÉö;`´ÂºõKhçÛ¸jHš‰vkͬ•ô£¹",£ìSkâ+Ѧ›³äàÍÄh«xf,z7î¢rP4Û`æ7¨±Ò.EÏŸwg6³/. #Í{È£U5xSw”+ªÑ«o¹_s&Q“&{vLÆŒ »#ŒÓ¢js¦²­SaºŠ9;%¿ôˆ**¬cjrq‹^𿾅y›µÙì:ö³ó4j/£ÏÆ©rVßE™ÚÛdô7pfÏÓí™`Ý#²Û1жõÙCå»gà塉¾Adù·×¼Jœë id± ˜±0 ‹GO?ôù½„žÎ¢›¤éAçv²"÷7Êæì‡gæ]ÜÉÁtc+ݽ°,ž ksÔüÑ£ÚéRÐèsËgc™D$ðËš|T~…‹nòó´Tà·øDfܘˆ×ÎŸà–¶ÛS¿Ùóà^Y&´ë'r§îà–$}Ž\ï‚‘3¨ÜyIÚ‘ +•ºI¾ä’Â;€»ÂSÉ¿·_¹ª©ãYåÎ-¸Ì.E†|Ù‡~É41*ж…÷ái×Sµ+ÖóÞ|ߎ–r2ع_ž~×vN~ KäáÞ›OÄ¡F‚«9Å–†®ÁªÁ4|q2LûÕÂuÝú¶Ïbá`ù|œSÓ̵·8îÚª“\n¦3µ¬×gÅ{VÒÁ÷ZTöžã¿=¢FKÙc¯¥œÂ­¯àtÀ×>øMvfÀ¦ õt±Z<ùc;Îg Û–âåáÎè¸Å—ÅK'@›ÙÆÆeqpÅaUÐ}åÊJWeƒÈÝ­¨Ùðg„ØsNëÍh|ÞYø¢?‹z¤òpêsqB~¼‚ð‡$ßÓTÊÚÆ/…ÂW/Q÷•}j"LÖt”ÒæœÃ°Rä§8”ÇÛ í8ú}öÒ£‡›¿w+ŠB4Úà·~Ö\=[Ì m©nu¤fÛ£ÿ¿åÁ`f+w7©}…Ño˜±6}ew€ÉÕŽ‡í»JàFðGÜ·r#Ë\hºFƒmºÖÀî{ ë×oD‹ý›`Ë·\Rc³†·÷ ý 9æïÅ"ºXáã²ÐæækMÀò•IÐlÕÇ s ¾q3ñy|¿µq?äÉç» ‡ uw‹„móñ[<ÓA„:)⦬[ xhúHO¡Ëƒi,€ÞK4LX•ìQáödrô0gÔ§Ù`úñ7ÑÀ“k~©ŽU¼ôQNâi$¥°ÌÇ!øiÛ*ÂBNÑŒÈ šðw'žrWÆwû¢F9Óy\:8†ýˆ‚ERÆfwÖF±UïtXKƒ!õísE¹ëÔ±Z¥¥Y°‹ÓÏÂ{7aš,y–M°Ïy u‹¢ py;LÝzž•ìD‡‚wPv"ƒÿÏ÷øXvЖ%&u=˜–>I%‹¥Â’íñðsï!ÒqPÀ&à×\äEŸç®ôÆS}öçeÚAîôÙܻȸÏóÓ4f{Eã;ñú³kkñ=QÀNRÑ^°ïƒ“éPù8<–û ŠÞ%¢2‹ G?—{°LœN¨Ðe‹jÒ Š_@}¬+xò§S‰fé]°Î²;‡[º-™èã§ü$½‰ìúÍXðº My[l‰Ä2+íL~ v~•Ï€êð| Wì!øMÛ½ž¹…Þ9ptú2ÜY¦íå[@Él¹z!ˆf¿θIð§³X…=i–¢WÓóI†Ì°}²–Ým)‡?ïsˆÝ>-º  ž×üÂ4U”mi„­úÿȉÙh|, ö–€áy>Ì3"±Æ…s`èþCþ/î(|èåžn•¡ƒÕ;¤é—G`k<†Å­Ô!ùŠ=v‹ÃóU²˜æó²±p¶8Ôù¾Gÿo”„ãOÞ,ͨ§XŽëeÏÁ ‹p´oZ ÷.DÝ ®¤ƒdpKT`Ç¢X<¼Ç›» @¾à+)ÈOÀ(±­˜ök/\u ³ù Ùàý*¼r<6ß¡4ýï%lÈôþ‚pÜòl-9xè>—žAñoj6.¿¦Äô†ÃYƒ ()”ÅgJU¸ZZ­¯‚Œè$ºáÊ î‘ï=²±bœöŸâ^™‡eBƒ(zË€­Ðª†“žÓ`öKKúºßïÊèïáT–ýr7ù7/ÿ; ÝmÈïÕ°@¡”Û»dÓ×þvu*–5—=÷–³ ¿‰Õ»ðÛ vx®fE § sº?8t‚k°_Ù˘ÔÝGª{Q*Þ|pÀ3_–˃§ã÷AÈa?aÉ‹F°Sˆ>H w¼(\ jš«X€i²mû£Üï–~À7<壘˜ñ§ÝŽÇEMAØk–ÏE9ÉÓãAõm6>,»BÒÊÙ`Œî>¾”ûýXWå‡pžbGU/Ìc?æL„@·è~·€ÌðžMþN[ÏÂÎN‡‰óyk¾*²¾#ðün¹/~yúdœt/qz¶Ž´ƒ©•\úQ÷pâ/Iö\ˆ¬ÖSÝeÂ>c×ûflÿÞ‹9”Ü.ÈŸèdŠ~WH‹Pä‹~AÏ Rp _’Û×'@&È?!j÷Œè¦=zõÜfðº˜DÞϱíob£GçØ­Æz>9B‘l +’…[s ذŸ0„- ƒ/††N‹µÙãŠô£ÐÚ¼¢¶+7‚Ö’2#vórˆ€ÃZ\:›~mÇEÐ2YÈ6ŽúX —bAkj!Ú}ÑÇßK¢1}Y1V¥ek’!1L›Ý5û‰…åKÁÂJ5º´™ØÎp’Ø »öɰŶQLÍl!]ïñŒæÉÒñš†ô¦ãv÷t@yqå†e<ÑMnZÑ2摤KW¥ÀÆ}`h=ĉMaûû^s…‘uø[Ûz·[âMOn‰ž Ûf•;ª øBßc®ê&å«£¹dÎÚ.K]'Þ„kÆØjñÍ1-;ˆº}pbmG]FãáþNËÇkÑ,ô2é5Tƒ¿Å,Š¿ž_i‡G’Y—TTêãÂtî‚ÖfsöG,RêsÑX_Ž…5Â|2¯ô ¾î‹Çàc¸4 ¹œÙlœÝrvXÚÊbõ/m56 ²í»aá‡t¢X¾˜ôD7Â÷ztµ"stG÷ÿÂKæÊ‰¼À/~Àû*ÌBy´‹W¥½Ì˜¹E˜1™·§a_l ß+øtÿš ÍEoaBKL “cÏ¿YáÌ—çùã²jx^<Ÿ0`Ѽ+=¥7?Ń¡<Ö¤Š1µƒdþÖJ|—»õ¯…žÀóxǤŠ/¯ƒÖÔ:ûg°) kjæR—Í&,Nã'„Šì‡N“ìü I÷7E¡ò¸2æ²î*d°gŨRî¯Qžõ•¸'‹ÐU:?8÷¸ýŸŒu4a©÷“ ÈÜ‹>tü÷ǫ̂ñͻؿ¤~[ŒKm6ë—s¼¹‚jûy°SªÅ°Ý±ƒË.qÃÃ…o¡ û¶Ý¨…½·„˜¹öQx¯MóÞfÀýøg¨væ8¼ªâMIÛÇÕÅ‚Aê48¢='ÃimËz+ÊýB‚žÖ1:(OsgHÐÞkwi˜çgüâlÏjÿZÑëM™èé©5O„*œK‡Ò ¥P9n)ïz…Ý®AìµH+Zß\Žkì†áTñA^ÇçXþ" ~}óÁ+;&£§š öµ)Q-­Ü‚O8Å»lã©Cðîîb(Ùʦ½¬‚ïFP³g&+±_Šê>F°eò*Ì{´W/IE»ÛÀ÷™×lžpZ9Ë(þ[Î'“*Ér­zèZ÷†[Ö9\•ã”åÍp‚#pú§9ÉYòlíI1Öù.„c9:´-%÷7«µÍÀPoŒ ófç=Êô®j+úĬA™ÑüµtRd|9‚'cø gƒÀAñN ÉþÓ8+Ÿp`qïa]ãKXj:.×åÿ˜â†w$Õ‰æË9PYâH+—ËÁSˆ>3‘¿BÝœºˆ§‡êZqÓLܱÌ÷4IòG¯Á«›ºìòRev·+–S áÁæÙ:hëá ÆÃ?ñþÁ>|ü[ƒ6ŠÑRGœp#•ëü@m-!)NîìÐð3è»gL]3XçÕ xçC¨^ÊqèqУÞì@…>ó¦Âô¡fž#4ðDÙe=Â~(¶r=.&¸³!N펂õ}ϱRmÞo–¼JK<G’–» ·´ÒçNI!JØÞR vÜØ‚IžY]ÛS!nñSN>^”Y]…Ì•/IrF.ÞÍIm|įþg€Ow®Âý6lßÒLò©&†$+зú¡PÝ”Ïbª‰3&k˜a3_¢¯©²Èw“ Ow5µ^‰c`ÁÐAèóH#Ý/@›äWü{½g¯ò€A¦8aÛ“K3%ÞÀýéq÷#wüuô ¬wVÅMdçUhïþPÔ¾Á`£°?«ü$Í^gÓÁÈ%TEÜ„P(­“ÏCvL>Ùw5Š4¯ÿÁ9®<ÛÆŸaW´5˜Õ²Íô6µC× ~ôÞ_]úõ¶Ì7à|£A?•vkÂ&áj=’_„üQHè9,?‹®£ïF{ìÖ0æ :êÝÂÞnÅB1<ÿ•™-ëâÎ>fq.ˆÁ^½\Mî**0„Än‚wŒ WÞ¼Vµ·é‘xê®<›ê[ȰUfËqðw NUM¢3äR©Ï©˜5(l·*è½¼†ó“eu+×ÑýVñÇçx=…›&ò{Jú°ßú8¿ŸÄqyJx)wk°«¤Þ¢:l\}{×ËÊ¡õõrÌá&+¿ªÉì6Éb–°L“Ä=Iò_ÿåí 82X>òðfì.V¢ÌNæZ³ìϦÜ3óß(4×Ô>Ùfµ™ƒíU²ªØ€]J¿ÄÝÐ¥ÂCwp­ÙyNíc •;¹>°JÀùvWÈ)nã9z“äÛÓ¹£òk™@ê1&óCÉöÜŽ<°z"ÇÛpS§sI?)³ÌÊD¯Ö“DyP… GcTÏfy8‹®ÿN„ Ói¨Åžž·m“i裵G‚E[ xr=uv1O«ÉX–·˜¦wD@ðzuC:‹»¯Å4®Çá”ü…£önâäë•ø(b2Çïô¤Ûc$épõds©X䊷ç?v@Ä®TCi ]궘®»nD¨Ñ=nÀœñ‹A\± û4{IæÂ¿ä΃y4Á[cÖ=ƒõ¿›Á(T}ò<4A–öWè²}D¬ÿ€_CˆxòæŒûà7\ÂÍouư¥ÈÝÿá͹2kXµ& ‚œãØyóH }ñöQa N¿5šwAeÁšn‹› ƒ_´?i¾ËL[ š+±A\¬®P³Vi5f/·m‚ò³Ø»ù2^ó€?<å —½H‡~]zªþÄŧðãø ñãPïç0,Z2×É嵐 „m»ük·IÍÕÕtŠ`ô¨x@¥{ïÿz2{Z˲ò¢DÏð—¡7^rWáÄy»ÓƒNWK®«Ã¡G&Üá£NÐäŠïÿæ¢[Û̼ù˜4 8NX¼ÜtK»ëþ ï÷ɶ+±ùc;&—ÏÅK¯Â?MFŸDrI’ Àö1‡ 1H=o©¡áRjvZÄÊtÈÂܵóxåöÖ™Q«mpc»=6O€=‘€TsŠJªça›ü›¡ 續¶žáÈ[ŸÕô—¾uÈýÌþl­eAÖô׿r.^Nˆ¾ù8‘–÷EÑ‹ÒáÉôýÝÜ|è8=µT‹“KxÏÔâü}éáì…ŒžÝ¸oÉsÜ,Ï}œM¯?yÂõÔ9± …KáŸÐKT»“.<`ÊìbÏ@Õ’ þä¸xt}%›cÚa÷y?NPí7|üÙˆïIcãkì7(@#ùõÔ²kmØ+Š6ÓHMq ªG6ºôGc%¹1.Äbk¡ñú0fëéÑË?r׿>€ý_bpâ•´¿%F}„$kÊí3ÈÃèÎHz¡;VͱÇ/§”yª --“¬{poH`›¬)Lu`Ï3 Pú7®Ö…‹gjÀr×r®²í èWaá[ÔW×*i}ª:··ºxÃl/’²Š“ÞÌæ8Äq.ÏE©oÐuòúÇu I¼É¥¸Ì`O ¥QPØ‚É4c'˜ 9(°ô* »› °L9V(Du8‘ÀoÕ0òLJ?ìw‹›oåŽÃ¾Ü ›·Di¦5“ºdÏ/˜äÍù§ž'“‰px1†m¼ŒUSá,·7(´·k0#<˜ÅÏ8‰ZgnBÑ»ËD´·ËöjÄhžp,2ÜÕK:4 ¥i ÍiÛã_AîX ]þçzý×bñõÊrn¯@`ýsnLC2ÌYí½ëòð¹*æÕbµïÛ‡÷›Hah ÷d8=šùÂg~†ì+‡ â¢(ë­Ö`K>ÈÑe3“àÞhÌHíc¨¸#ÃôëÑSÕwñôˆ»Ø›‹v)ãÑÎé>4=wŧU '6Ç’ôkŽ- ¾Ù 1š°.ìï¦a1·± žuÏ£mh¿L¬ÎæcÁbwËOØœU°¯Ùåù©³†—‡(AÖª*ÎôE.\sGŒ÷ƒZT,Ç®Ì@¢ãkÊøÔ鎟OAEuÒ½¨fË*X¬´NË€ƒ™›¹ÃA`g* ×9qöÁT½Ç°á#;I˜ïAŒ~LÊ\åiÏK)fŸžÉ¹Î*ÇCÉÈzk¸£bòØï5ƽyÉ EîŸo"ôéú÷EÈÌÞâ“-i ö¸szõ_,G>¼À3Ûù$øW1,/ƒÓ³{Èp© ëÏÚ3éçË»É1x²ÝÆšOÂ#néLŽF•Àíg3ØÏ{sÙ¤[ó1}Œ6ɵ˜Ofl„ÖU˜z> ŽJgã*8é³ÌlåÎÿ i‡;‰—ðb_®Ø³¸J,h®.ÞuEö07ÖŸ*õv‡†q´óGVtŽc#º¬yÅ?îâš}hcx~ôjÁ¤/˜Á_Îé*'úÞƒØÑÞsÜ“)|: M÷³ý0•›WfIëŽæÄH”p+ƒã³u¨Æ}Ü:gDXjÐçÒ˜–ÚZ¶âÝ;08ñ ý­u©]éô*›N¿l9ÈŸÝ!Fo6|Ɔ*[»ÇÇv.Æé¯¢¸kré°ha[ãTñA뺳X-LZÆÈ=LæìzMÂM ù¢º“ïŸ3¼& ‹£ùü2e¨UØ ùÍrô˜œÍS“Ãe#V8®á^)9 –Vl¾»ˆ[×Ñê_7è˜&ÎñbÏ”hìå(ö{Òdp‰»Í¼ÖŒg7—ÐÈÔ\¨V=Ž_¿ËÓoã—²öª'—câ¿á—o½X hÂn¸¬Õ˜ÝÔ þ õ®9MšÖ8²?³uYûn¸ì"J¹Î<\Qp˜ZìÛë³%è—ÃÕà}v|*Léju|ôe9Zg¥bÿß^:wBmR× CG-™‘äHÏg^I¼ÀwUXe—³©³B-ß©( J,f_l¢ßq3õ¸ÀäêƒaÜMά×øÊâ­4]øó·’nP ŸÇßž¶T¡4Þè­fÞþ+¨Íú(úÙí碖òôî°>ߥø$ç™À_œ×ðÅz—Aiæh>Ð1£ë»q*àŠúq›bãé.YtÑb=”.‘ ®M ÐÆTŽR¤ ¥øm×o)ƒs}ˆ‡Œþâ®ü,ºä¯ûQöørnôˆ)Í•<Ë,CØñ£s¹Ðâ^~ÕŽ§xDD‡ö%< QO2-ßÕôi\.þJ+†ã¯ò7p½˜”ü}ß:auÃTÜ>õ8É\7^ ¾B”OgS_ ñ㬶 ±p„îÝÁèñî:7AüîYóNXº³=é½°Óx.ùœt„»”¸—Gh1þÐ*–wÓ”­ì–'þRèó±ç´|▾⬲OCKz¦œWÁÏ×=¸ˆAE6{Õ ê5úãÌ•°Ìý$Ïói5ê*]WŽ¢¨’VªKבOƒsÈÐÆT^3–˜jÁ^o5fæãÎ[;þ|Ýõ‡ÜûdÂV áÙ{Uä•e-JªÙ°ÏÞe\Q­8G­£v°i¹2Ûœ½¯ÞÍÆùñœ—PÇ[aØJN^Àô]Ú‚5"]°õÄ!Xn+E¨Ð¥eñ6**ßxƒ;·‘:q9¥Ôâ7|pCµ i÷yDJDâ7÷çüg6}˜tE‡¥\üÕÙB9[váw™¬ãŠË?x¡üÄsøÓö÷©A›÷Ub´ù­‰ ‰iL$H¿4í ñì»Ãk0µ,Î…nsV¨5SHƒ©›'·çró]…93©î·z ˜õ™?eÖYP ˆf¯>ã—mX]x+nHï=ñ¥Ð}µN¨j³ Ògˆ‚Î~¸X³y4†Û¨»Äþpð$¿§µÁÊ@òéÞ~Û·±äì#*óHn¥zN׊Â`Rq?)ÛÙ³ÖÂòÈMkÕÈ¥Æ5štX;—SŽc5åi®|9^‚KÄÕù:HE½†mcyñ¯æ ÈSuÛ/ן´Ðè­G|™ã†Ísñ´t>*JÍ…OüPÈíò£²~ãYÀaz⣠v[½À?­ÀÙE‘ÈOºtWíq"¾R‡©+2v,­Â'+F81£÷èzòt÷l†“èÎŒTl¼ÐßGk$©`ŽWùoÎÌ´qbl0®q37Ë‚ÛO_ÎI§?ÍÔWüøÍéDZa£›5û¼Ò}“OC“ž5ßе¿Ò¹… nñCohÃùÜìêdöΛh§1®Ê ²€†4xÛ§ŽÝíe(™q—“›X†ž»zQØR3Þö yZ`½åXj$[wyÛÕ¿¸dq ÷x×d²ô´¹¼8‰T,ðäLþQãWFñ$à&^“çü.) Sðö9+Ððp#ÿ²”¾j2Æõpêô|ž’6~/nåëë=Åo•Ø‘²~°˜{íÎ㪣c Þò:”ËÊà­IŸàUÈ ð›wølÛÌÝ×ɳÕIÁ/×c¸¦j°q’ñîNý"}úÞ¥‚Tÿ²Ä¢¯‰Ù]¬ðoð;ŽAYðl»-)á3 YÖ<-¸<^Œ\m•†ß~†tîºB¶#¾ ŸÔ³Ñy­Å¿­êØayY<Äï„ßà»ðŸÚ©|œµz<¦¸CƒÖ,ÛÕêOpoÆfا:–ÉœîÇXÛ+øx”—,R§j7BpÞúõ÷Dæ.u”ó´•}ÙþV£ÇdpIï"Ù"ÛhHÂUlžÊìÒÆÐ˜­1³ 4üʉև<Ý»ç#¼×èÇ·SnC§ˆè9h²½QÇ1ÎA€>?—%I1 øÑÎYÖ’è‰ù¼U3¡ufÊ%ƒòÕKüÓ3Ý#‡Pâµ1->5ÛÇñ!Cý©Xۿņ²wüåJêô6}‘Ù·9Ë4O¼7ùÝ}d&(¤ñ'öÄ óŸ«üevGIÊ[–8f¨.¦_f9ÀöOêTùâ_Ø91‹ž=ö _nŸFÝÔ§Ó±i½Ü!á,\Ýóî̲µé6¡kלƒu–t™ßV¸Áº.gXµÛš [Oð­ê(é…/EgѵËýY‹çxhú8ò—}€IG°eïj ‘7ÀIíµ “Gë~úAdaÎý-C-$|È퀟ØÚ싃‡¡iötf®ü©&U]Cƒ£nrBÕï±êúy˜o¿œÜ¯ñ/m±¤/9Ø<ç”À mŽØÔÚ0›¶'o¼ƒ]ÏÄÙñµÄÀÓΩ ³}gñ)ðPõ;·âðwÐÏÉ'Á¡Ï8Å®c(9¼§Ôt‘ ¿ß`³Y"M¡É{,ò:ŠˆEí]î϶,Ø+¿ü}NÂ8w'Ôl!E"L.¬èZg'޹Ðiÿn£N®:Ûi´æŠ&¡kÝhrúÌ»µ ÿªÌÇÌHAü°n.ñS#ž(©«B‰Ò,Üb?­1 Ç#O*ÑÂtKnc‹;°ÀŒeŒoGOí .Qʉy­­à~ír¤wŸÂøs¯HRq/[ý7ê?¸GŽÖ>±;ÙhΤ`lè<×eŠÊzôPNïåê ÔüÆ]Òw&NClªq*Û®âÙ'IÓÒ­ øal>¸TJ Úª»¼4·Rn°>ž¬ªz€kEˆ»êX,—nÇ?ïéäi(}ë 6¼ô‚(ÉžËÍ«pbXŒ¦%]â_=$Â;¶½›(C´,›Ê_k”Kšº¸3­œLu ŽqÏ…ª{2\d›ñ@n¿¶Æ‡vÉÐZ3‡vÕñIAœÊ✠å-¶¤n1ðpþgÞÏ6¬^VŒy5ØÓ+æ\>Ü&§WR(^}›¿n¢í`äC\½r;ni¹Ž 7†o3LɾJà]+™ƒ¿ømV0G›k¹R7á9œÓ¥Ö5¸_n>ëý5Z3¿¿'¥tè‚wùÎ3Å!|[ÜrÙï´WsZOõà Ýmå¸ãˆ9´Z,§å_¨æz"x7øì]S®]ÆrûÕÀ«Ò ÂÒõ\™>rËviÓ[Íkñs½¹mg޼~yœ#y&hΆ…dé¹?/0·¹/Í©à{î9ʧÚ(¼!–ñüE¨Ý.k–·cÓøm@ ž‡°7«åÐ¥9 Îmé‘©Q¹ÿeÝ:núa X±è Þj!ÕÅœï’a¼¶ zvô^^ú½£óaºS;z7Epùו©¾©(Ýáú<>^Bë+*T9ô¤É+s-9¯¹ÐÇ&p×( £V-Æ^éõø×îÄ%Ôp„·à˜ âtK•éÐ3{W`y€›$Ô Ý‹ÁÃA‡É|p‚Ù»ðãüßäÓ„&ÔЦ‰¸”‹»ÈÄ;Œ4[­£¯T,Qÿ@"š¾  ÊWsašæ^Ë,B'ƒ3`Òðniw¥˜›«úÃÄÛàáݟ䈉4ím=ˆÇÄÞ’X³[äeŽ)‹tW!"b=°Ow˜x_sÁOáHNûR«Ùëqó¹Ô² D“Èßu û6 ÞŸà§ö~"ù6©Ø´5 jŠÑ¯n˜[oóµ±w¢,7»¼¢wÀ‚Õ×áß:mzaZ*Ä_þè7'+±;ÇwBéÂ}”Ìø^‹£è˼ý3“Á$%—|*G-68á³NNƒlq0:SÏ<‰9iܨ­lTLWAG×' m=ƒì±ê–7RüoÓE¨öí8)qž¯qê—uœÌAXUÓ6™a ø¼ô—-â&O_…{»ÕY`i'ŒÙù’[{ê(ß­Ï4¬ãXÖ²£ “ý“”oE²þµ[óû7âd¤cŒM.¹ÕÎq×õiËÏãd픩L¨É‹,¿q‹ѱ{”Ø2…Õ,oÓ$H­kµ•bt¥{¹l óMèASè¢æ¬.U†|{¾žíåobïiʞؿEÏÛdiŠ"|?„þm'ðƒö öóTÒIÇ=Q‘ o€¨rJŒa<ç$‹g']^óOƒ®·j‡Þk*,Æ¡„¿o‰ H\M¶a=6Mbí34±gr~ÑŸÏýY©·/ÁIÓ²Pj_9ù‚¶ýl¤O‡}uܼÐ\RøÏ®Ö:KÓÏ&¢°þhþk‡¯^/Cý.ÅPŸžƒ¨Gô))æ„7Í`mªâôEº$¨©òy·¦Bå '¸ñg+Êó=ˆè@3RãÙ¶¶؇ÔWpí“à(2£ÇE»aÇù¯Ð^ñ’‹®6Á½3„Ø´¸6Û~&´ÕãïÝrÔ¬ÒˆÅ=I…s_ÅXö¥(;ÿ+Üs‘Û;kn˜— ÉZÑxæS''ŠSW¸òÛ¾Ksfg®7·}l›}Ÿ°Ø_2Lë·iíÎ-„Ü?Ÿø‰·?€Æ¥+øµP+pŒP/}õ1h“¥·¤µÐº»'>JáÎDþáßÊÑÆÒËED/fkêÙŒ;ÖWâ¦K¥à©ÌD<•ðîª#Ø=$J'xµaiò®šD¿<ÄÆÒÈÍßm]^·âÈyÈíOè‡ãç­¸ðóÇáÈÄb[ÁC+hÉÞa\½‹\_1Ü×Cpb] þ½Óí ñíJà™Ô‚÷t%ú.yk=›7ÚD‡Þñž‹‚©-M÷b)\ºÈÕX²ºƒXòÑ = § ´{Õ Y{·‚ûãqüë•jôïc{0»ÉÕ„  é¤50¬ø?-u¥ßö‰2u3áä= œìY bú£y>÷¼cX>»ìˆq\Ê¥ÇþpOÀ‰ ‚ =]’ ”="Y’wˆUíè_ÿ¨è âum?î¡ÐSÌ–üÁµçŒƒ„ýј^w›‹¾ŽõàRè„Ïk±U‹ìH±X4£)v–¤ATòT0YòŸ8ÐgdžÈÉôg¼¶€o¹Ò´Mçüxû…óÚ 4©aÛ£$ÜÌ/€OûÎAs\*¹)ÀÆÎàY}æs5GwÓÈïÛ!ô»$XÌ›††5P¿`[XÐ:¦`Â)Q’e'À:û¦âΜÃÄôqøì[A÷¿e­“e˜¸WפªIK)²ãkS¨qï ¾¹ùEL˜”Wc茑©˜~ÑLO{ᘀø #…·®àaÃrõéb.WL‹h/ã>åZ²qyHØp‰fƒäá2i.zŒî øIÉ`ôj-¹ú£Êöû9 Y½=2ÕáNÿ1þõçv°kÍx`?Àÿ|.Í˹~™$|³¦Vç•°'žchÕÂNô+=E$ºà<¡L¾gEëPîн0HW¬%Ê£µ²y«/Ì<»ˆ<Ú–‹—Bµ¾¼8!ÀÖ­Q£|ž*ë­RNáôy`½nÀPиh ¯Öù2ï¿FsP-WN€oŸÇáde}ú4AEÎjSþA;º±õ:''ã´W¦l÷˜³Ÿ•‚#ÿá¥;bPŽ_~Ä‚@ƒ,ÚÚ7êhô†Oü¦;©n&†ì:+…ñç£ðÀ™X³òª¨v®Òê7ÃÆøö‚þç"Ðôñ cägÒŸçá­ByªZCØbÿ§ðåôf¬5ßKag2h7Ó¬´zn[‰>øNΆfÇ¹ŠƒzŒSbr©¡6·jb1Äp®e:Œu÷À§‘Kàa}Ë®a÷?¡øÖX9&ÔT;å+6ÈÍ]: ¿5¬Ù_b˜ÚžFz§³žœóh'-¯ÖCÕ•( –}ïÆqä‘ ÞóZDéhÚp…wDˆÇD¢ZÉ YyœÄÅ ðÀΓ¥:¿àß.iàÒ_Ôã†á?(]0—…^O‚²×lu¶<Ý;ôœÙxÐþÞ7†6Žžã–=‘­¦t(‡mc< s¾,içnǽÇjOYÚ¬’Ó+§ÍvF£å<ç÷>'ÁgËlþ~aË. ÷»¼·™³<6I <†Ô,H~y“»š®CÙîxî£uÂ!z5ÝÍ̬yÉqð¹'“·Q¬´„æ³µ•öüåQã áhîö•¼ÙÆ©9Otj¦1kƒ4žŸøb2hH,\°‘Q*âþôìî| Zojá-ŠI$aÏZmÕ€•ƒÒ¨ Å=Èœzº¯¾_s_.vo¾ªØG/èàãxU&!–Êú–Á’ó #+ýQâ¾{¸ü$xÆxµN•d~¤³ÜèAfÄÄâu¯a Ú;´¹šm[àƒO®WÍB›r6=à# ~$=º3Ñë¸4)|ÿžä\hÀ>L)ó¤~×MaÀs 7"“ÎõŸxÏ-þ;‚Í-Œ°ÛÝ«3•Ùmòàœ!hþ„¡‰²¼ú¾:²àçS¼8ÁŒ~¼‡bjL·ð<7¼L zÞ;áÉ[m{‚5ÅÅ ``Àúy”÷Ôæ”n–qIçpe])vhÏ köQ¥äpmGÖ~T¥-rL3¿ Ô àô5k¼r­œozå9apÛ µŒœÕÅ K’ˆXƒ7ß„™Xì>¢¼qLWÞÆÆ_š \GŠåÖˆä´RÁˆÜDØôu%Úùtà÷”\øö ÇÌÀ =—¹­ ·s*ÓRàj6mƒ)$ÌMZEOÆ{fûàkÔ Xk‘?Ãÿ¡Þ•UCçDW˜×æÆ ÅEùµæ;2N"MåÕØÚž‡/ûÓ«@èÓhy•wÌØÏ’ï6ᢈ œæÔšŠ ó¾=H›ã„4*ëq™|Î^ˉF“m*:4ÑÚeÊÏcá¬môý2':"mI'‡k3§ËW±~‹8 âì ««´òQj–3íhÚÎ{p÷Ôb£>Ž)žÔ‡a'/x‹\3–T_˜M‹bõùc²ß²û†xsH’ÝÑp@Ç¡_ðJQƒ>¨ËæWÁÉõÓ±¸~¥ɱžÓJh®4H÷ž¬q‡<¬+²+¿`iý!ú‹Èù¹9\Rß4÷ˆe6Î9P|!€=±¦Vš«˜ØâVn—ý3nq• {a;ë±2½2vtÜT¢þÓÃy™ á°­ÝíÙ…æ~Ur¦±¢ÈÓì&îQÆ*¶t€ŠF’µFg@ôoçÆ&¹ a aúþD¨ö2·÷ÉRnö»aÝ¿X.dLÙ‘Ýô¹rÊ+h¥ÍXf£\ ;½ÁJùÛh_· Ú£y×~Ï„gê"´q}$|=\Û}Œi·dȯP†—¼aë‹0Îê®|¬‡¶‹Ÿ)¨ò½üNÇОÍœâ’gàîv V¼Ôb Äq^o‘ßb|`¬ÇŠƒÏ®k`¸AFV¾L/ Æ¡í kôsë³á øã–Ub^8ýÁ\±K†dé>'võöl<±Wv +cg¥~£s£-ÌM?M’Fjù‹Lƃk“kžðB:Ñpö4ˆûQ?«ª‰M³~ãÑ祜X†.]û÷)~°6æ>mÙ†‡ŠЦŒ?î<ìª^ÏÖY²#i¤ûBonòNšmÛ@ï‡q+Û ùMî¼{`œÍ¹[—@£Ò˜Mèã00׉zŽáÆn??¢ÏëàL#Hr}<>xùœ$röœJÔ…›\qÄ| ÕV¤7ö«°§(:¯T£ÞÃéo[Y(õàÞfà ösYî´µy­ÊS{-zÚp/Õu•biï”ñt§ ¼uX¾É\ÖË©hGËÕ¯0ëu>”ýµ |Z R'¦á•Ÿ“`¨: ÒîIñ~i'TM~Zj öM§ùë=0h‰ ݸt/.Y¨H¿…£xõA^½÷D¹0‰aa$*$Ž®“ú¬z¶.tý2€õ´iÛ 2µ¹R~C_ÎY^€Äx¨¯ ßCºa¸7‡„ZUá„À)œ‘½,úK¶î‹:Wåà„Mõá°R“}M¾ KRúùù‡c`}ÍN,0ÃÀ©gañ§Þ§ÃxwáÉ©Á`,’ºí8/Bhƒ»oL9ÂóyÑ6<<3‡óN⳺T–ãF~|OÀÓ…°ö¦NÌ}ȉýkÛšr…(>B±îtÚ6–Æ” 0ÃdžTª¼ŒæMß Ï\¶R¡÷^¸Ìd-ó\G]s~â‹Xz¦¼n­Y²ü¹¬äþ¶ýwSíì…úGéÿéî^¯Ì¾ ™ÐÓRy¬"~Îy.CÜ<Ûqõ>A:õÜFNxN1=¯rŒ ÊQòô~Yø‡™%<€¡zîú­³âŒY»Ã :Ëd*ëZ®A_L?ÙsÔáwÍS”Z®F¾½¬dµ7ªðgÅ3¿}›­™kÍêÆû9~Tê4Ý¡.ÈN|–`jg,xªîìEk(-¾ÆøÕã˜Ù“hbuÓ‹É*â­¼*K~©g• †,èE üÙÓ )¢XøåkXÓ­õ‚8ÿ ŸZŒæþ™ v°é?²A'µ 9­ž“õD*xê2sõ-ÄÇÞÑ[óº OD‹9¦^´}þv¶oaÕq;èÐkŽn©XË„‹ñþŽØìÚÑ8!ð,Ôœùk¶Àä'2tçé%L-à$ý¾Ê Æ¿Jŧ¼ÀEl˜Wí—Á>Ï[Ê9æl~Ä"Vl`H/÷©°c§Rý×—áíÁN½¦Oߎ£‡]W1Ý'Î,Ìݺ›&Ó‘›Êèý׋}–(åùT¹æÌ;Ž>SÓáR® U»ñ‡ì}ϵ‡û³¢çIûíõjQ ÿ*dXd œø4ˆâú¹¸ò›(8¼ÀµG o%ž¨û-–7éò[Zz[†Ý›¥@ƒS„áúÀ]›%1^ð0D³5<³%¦g…Õ\XÏ øzòÞ¼„2f"¨º_n¥µÀÊžðwàm‰uX&³'*”#/“Ð>Ž^iÜHÕvqë|fòOt'Aÿn *ÇÛK7Ÿ¸EŒä|á@@äðØ Iƒ·Xƒ}Æ¢ô5ß Ê ^Ãdôõ=¶¡˜¼j,!ζü€=çó`¾~¯„ÎSXÓâÐ8~·Õé°Öi‡À4Fÿ=ÄS¸e ]íèá‡È®÷s¡hZgpý57l3™og¢ô1†Õ^$%‡:y÷>­…Ùg`úû·XÐeÈzÓÀõN=ìD36GŸ§6™¤½: TŸi3‰¢4GPžÌ˜ÆÆìà.ng@)‡Á~-ir²&ü×sï3ïpˆ&Õo0KÅH‹Ý w‚®¼ynNa/µ&ñ7©Øp=§9ñò,èN%¤°,î¬J6L³?ÍÅÎ:Jœ‰+Ô]¦ëÙD©,ŽŽoœ@u‚lÙ‚Ë1½ó%§Q¼u%Sv»„ùA~èm<ž,Ч’=uðÌà$XK¡mCô=§gÒÍhÙJ/¦¥ _oþÛõQ¸e['…C0i*þ­ý€‹Æ9°Zé1ý¸J‹Éš'ã†ÁD6ú,óS‘ †.ÒxyL UߺÒVîèöÓ§ |_và&n¼Æ'¼¬ÌÒ}–P›vU4­ØÌ®¦ËÑK6ãL¥nÛ.3vd¤<×7ÁÓ:†š#¨þßËÔtót–•l oùí°X܈=Ÿï7gü%Ïù¢UÔiÈÕUärfгÍÉÃÜT‹å$+k#+‹§,ÁggÞ @×fx—3îŒpò¾>àêt†ÍULÇ5þA6cÅ)–žiI—iìá’æI³uS†@e«:­K' Ýb‹ï©áç…ìe…~ð¼k žôŒ¦Qú@²0Úl2;ÑwÃ\¨Wþ|0õêøeÈÿúõTo¾.áú¦ÐË›ZPm–.kG£š³L~ŽxS±·gZÊfɹR¿Õvt£ÿyò6t5ëÝ»…wÊ0´¶É«dwðŒñü+ã}xðö¥ïè,òÞÑ’½?Q/÷8î›o_D6†{‹3é¤,<»|23ØSOd\¦¥SW“T%®%¡˜›sô"~ð×Bo;Mzßx ßòŠ Ü KÄÿ4Ç’·Ö0ÓÉ+QÉÅ‘ÖN0¦K¯"ÿ—>]1.-¯tÂú  Ø<Õ%p{Éï-¬<uAÞĶ» vÊY ×Š÷žø{ÅŸâá‘Õ——[ž‚_b@÷íµG½é½Ø›i ë~ÍåS˜ÅúKܸ°ip­7M6½ ‚™24a™4D¼tý÷ƒ@Ë{¢í‡(M:9ÑŠôŠÀó¦Þn-ƒæ{üôó8…¼a39›^‘¬BpO5xtá1 Lm#WÂZ¸ÀË×ð¸aÊi1ƒ9g°èF3îä2ñÃßN¸dæF³’ßá¹â:¼SŠš%|ãk)¾ÁB«0®ÆkŽIp+p3R‘&ágøœ…£=]'³G|NЙ*EßHÌöx÷ȉèñ|XõÜ*;$D6?×¢7Â9^H¨ý¢Y‚¥¶Jô¸²¤zË1á ˜&p ¢Ó=@« Ž¦Û° /WS»”ñüÅ+kqÂÓcøíðFxz!“VÉóoµ_ÀQ¿acÍmüæ¢ÌâLÀ°Xýz:òy†Éýw@è6t& 3IÍݰkE",çî‘çIœÙkÆß2+CtØ»[·È®j5¦.¨Ë5ËŸ„ŒªTçØ9®?õ0gWqÇ~•dÆ?Ÿà†ÓÆh?'.¥•:g$-ÜŸÁʺ‰/´:È g0ãÄ6¨¯É+Ü›œõÒŒ©]|[öhщn‘ Bª±è’7™¶SÉçìáÙúSïÊ·pcûmè>¯Ä„´Èd!ø%= Ì|Ò7¿v£êá(Ù›@®%a[ÕWîXü3ôšÜ„v-”gƒdgQç·Í‰¹Ç:÷qÏW´+“å¾ûoögÚÐ-4I­–ép•α \} <„„éNM p\ˆ›î­£ôZÞ¢aúöLÎ(¸õ¯ÎL™5z丛¶Ô÷írÎzÏ/¸R6ñàî c@6]œ OáWæ0 h*’¥2kæŠÒÉÂø6±jtiŒu]N§êÌ£+VÎÅ–M¡ ~Â+òÆQù 6íBy8yîz.zaæK¯¤g÷L¡ÊVàéÉ9lò  ×w‡ôY±$~Ê~R™¸µÒiå]úìØ„qìÔ7 z.R•ÊÜË>½{^Ï%耧P>’igqwç-”¯ ƒð'ɜߓXjßAØçye£Ä©÷ëk$ÏÆ‡íÝ’j†¿‘UÔòÜ»ø°LPíLœYóá+\vªÓ¯6c-~¢ü³$xùZ˜þ’xEÂÿAÿÒ$|\Hýî©S•v#ü}ÆŸí›rù?ã¸ízŽŒ—rÛ,¿£úUºK#ŠeE¨P[8[5tT}èCgu®ö®Ä(¦~ņþõÿ·ãT3`Õ»¦ R¡PP–: •@M‘#¦;äбõxçNl¼/†ëy®ì›®ú…McÊ7°~qÜ^Üvw¶+ Ö핃M'*É&‘[²¥æHÒe[qÆý‡ÓXðY‘ñµd²÷¿á‘çjì퉷°çÈföîþ'¾Þ8]Z·Ž^;‹„#ÙÅ–d¾vo£ûfÒŸ+ó¹¤óÑX±Á–Jž~Oböf|ů¦žä–ÍüDoNÂébñôf… Ü]ÞÈ¿xA’žÆî/­ÆÖ’&¼D•æ\œÍœ~܃à“éøi¶T³*^Œ×eÞ!SYbê>T9NÓ¼«½ÚñôX„è\xý¢‚=ÒТ{œ…ãâÏ!'ð97wõyplµ¥£v¹Â})\ó,oß5 .<.€»…Yü¸~i ×`úP¨%tÃÁî»ÜÛ'ØU{ƒè§VØÚ½_[§±ã· )øÍݳ!Gœ<0ô†[Zßx¶ØK $õN]Lwýäõ× àžíÖ8§e+ý®àŒ šR6´¸<:Œ9co"w÷ ÜM”&íÝ8–¢ ~RÔæÍWø}Yçu%}õ8ÓkOðiˆ!>*™Ïd;–²fÛ4å0ûd,‹¼ôöcòQVðê¦(KTÈ€¾öx<:aóâþt|ãǺoFµÖ#xFâ-¶þÒä&‰qJCùªàÂâÞÛ¢U’­Ù=†d›×¡Ij*lùwK`ý ™X×Kþܦ{\eÅ-®Gâ,zÜŵ8ÈÖ¹Šêz@ÐB]#N†w1çñIG:×q!’™»±…où8ÛÓ”z\L¡qE¨nÇ›5Šœ¤T7´ÉøoÕkPeA¶K<7µípíïªmf›7 ³[“û9Ó'Bt¯ßwµO‹Ô•؉ Uæs¸ËÐa%Óüq©ÂV€=YàQ¶†EÜYÆ×Ù‹¶™Å(rM™MÙÂÙû„1ôqå6¨‹•´kè¾á÷Xº\‚žeÓèì•3XIm2v,ëáÖeïä=ù çæqÒ’}Üüš28à¼ÅÖwÄš_÷n3›øS™öíû;ÊÇŠ0ÌlUŸMd±eg9ÿK¥ãÃu,½æ cðv¹+í™2Š)fsÛÇÒpírÞÜÊfü±Ò™»°¥÷nѧ_¤Î‡µønAéÔ,Ålàð² Üb¿q´ùº:O–byÏŸá¡6j}ô+_¶kæk—@ù/5jW(¶0ÐåÍ@kÜ {üfdE/pFð³6†ÄýãiL€Ý8x•C3ÙùÞãÌ8Œåmƒâ„dÊ],èHÃ=ÃyÌbàwÓt1ÿ”Ò\Ò0ŠÁ$ùðøìC4×”d5«ýðéö?ü¨³ TÃ$Äç=gмAˆ yÑà ;x33:¾dÏ:oææPÅÍwå“sÃC˜QZÀ­ü®Î<´U9+“ ¸áKÓåbžÒÖA:½_4îÚ]©%¡a[ˆYA>‘-å]µžNç}\²—Rqÿ®óØw¡“›%?‡hk›áò¸Ïü÷2È’âá\õ¤`Nÿ»9¼oJ‡€C+hà#Ö35‚^)–¡å{_€Ï;I¶¾ý"ˆMcå¢q|?]o\=¾·çd¯€kÕ9 Ï÷D•n]*Ñ[J¶˜ç©§±pÉÿqtåÑT½]Ø<Ëûœ»ßg?{Ýg=Gé½{ÔªÊ5 Ø:•~îƒdðåHÝMÔÄNÔÁŒßö g(ž¥Ê÷/ÂȼOpXt/G¼OBÉÓ9èmì …ÄYgcïfB}Ç=ð/h¥Jü4¼uËú™âú4î!?7½ºA¾ƒ¼~ÓN´]ðÁÖ Ðj•|äe†à¶^)õ&C¼¦í¢õØvy[ã2×5ò¶÷Â;"¿ÆÍf;×±Õ{ð”–ûTªGr •‰XóUÚ,²‘æ Njbl×¥¿øn“'ûÕ}ÞY qæÌ-8ößÿždѧ÷òž ‡¢ª»"“”ÉÂ5.pÀi)ùO½S) U”Èò„/ÿiŒ¹t¡kcyvºžÝõ¨X÷ŒÛ4†z»èÌ_þ¸ Q™ý›c†cö·r³¸ñdÆ® êìàÀ³©ä¹C§íÈ‚ $ëËìºü.à‡¥‡á,3\oa Ĺ+§‘˜ãN02k>ë¿>ò†Guσ/Žky‚Ìv£>nЙIV“%wÂïp³skpÒïs½B¸d"Û¥EzäyÄf½6Oÿ@4<¹Šl|:.¦ü[˾â‚3Á°Ep/Ÿt.]`Ä6ê'ÂßÓ30)óú?!ú_4qï"˜:ì…–ù¯]¥ˆ„Ò\.vÁNïA‹kñrß.ûµ<¿oÞçø€g¹l“[kqw •R˜göàܰaˆº·«~~ä9°Ëãcä”Nh›Èfàº}Îôj©?F⌯ƒ8{E(^ü¨A–\ƒí{¢i”|ù ur¾Û’TwçCÓµ/¨.VŠ3,Cð§—pã‚ *ÍéÃþªIDú³ œýöï4^á¥xŸ1ÂûŸHw¯< ‡b‚ˆÝGü*Qk9sÓNñØ÷ ¢ìãú}DJý ýZú Mfr wjÑ*å(ú3Â2§ Ƽà:N&“ôÊ‘ßä Bÿqk„,™Iu.žÃ2OöÀ*Ë^;íÇmKÉÅBe¨v ºÖE`Ì„ $80 ›¯`Ÿ«8ÙU¬Ãfp2DÂÄxÌ0½:ŠW½â~ŠõâDK6Oü½à> Õ ô8r[0½îÏd4!JXu+©ÿU!ŒÇ[l0%»[¿‘'ÓIõ¶tÊõ3ðy“¹ûn#HŸÄiR$NÖ_ñ¶ÓŸ W©H ®s×ëó/ñYÒ}zº) ¿iö+æym-”–taTÞyµ˜u.œÖöÔb‡ ɲ“Ã9§ÑL+ FÖÊ g´ÿì~N”í¬¢)ØB#–ÇWwÇ›HsÒ 1áÚeÜÛu¥ãÜgÌ"­„½Uú†üµÏŒÀÂÏe?Ï –%”;{Rè^ûî' ÈÙEjxàÚLhÎd³Ž«éùîLbDž½» š'Z1á°*¬VIEß-é¼#:Ÿ‘]¨Be§7pAö#l»4:¿æUqÅÖÇÀ·t êëuãÔ®  ïïýæuº.¸íS€´•ôdK‘y_¼ "è<Øú?y ÂÏ‘~ί\Œx%˜@â‰p”/€ò ApãÔRs¬N}~  5º>7t£šRÿõÂcp? gú&›K~@M®-<}o´ÈJUh½c½öÒ>>TâU¿ÜÆŸåRj‘÷è—ÊÀ• `äá øYÇ‹t«„“¥ã§3½½îä×Kg¸wij7´À™°ØqÅ‘äßðµK__á›2pñ—øäßHc݃qå}{b’‰ÃUÏq¶e;]¹vizΧ>†¥ þ[þê«áutÑÀDzv®‹{Y kRc™Œ°÷ªàFýÇ•œò½KhZjF–Ëã<\Æ[cÊ,],P>ggÞ:÷S¡È%ÃÎsÍ-½h¤l°¡(ú³Ø$fÅI^ƒÂÄ9\JÞÜÝü”8 ’åÝ aε…°G­¾êeq¥oÝ¡#PÆòøÝ©A۽͜û6Ê'KC¶['·&>mþ‚ÈÓw´·éV¥'Á#·ùÜè>ŒÚ1ž›pox7H¢êò~‹òzøf‰“ çñh!–ܼÖKsq_Ëw˜àïÅðp ÔàδمnV™Ü™H-œ³¡¿þ¥z <5ÅSLË1¹3?"Ïæ¸HäЉQ0VGÔ.·“Cëè\nÍG„=A²ôpAúöNGßQ>¸{¸ø(Ê;Œ-{Í}y<ȽjIäÑ6Ü»ˆ¹µ;síƒÍàn‚â—×å–h0¾]A}Ã>SBknq×wÛ2:×m*ƒB9]–áÀ"À€¼ÐÁüK9p¾Q˜Mlå@'õñ÷¹{8kÇEjoíëO%`ahYw¹ŸœÎK%5rJâN,Wd/ÎÍ¥ñšqwÿbl3žÇ½'Ë™ÄOC¢÷­ „¦ÂͲXï“ÁëTIçwÇi°MêŠlwÿ#ˆ9h@¿ÍÅüež(Í6wéÆHsüú{1„Ï šîm´äM Þ2¼‡Ì|iu—/•kaçxaÌÈkIù†`ŽŠÓÙÜݦ˜±A–M<¸éºkp€å¢@+ÿ?m2oÂQ8sy6›‚ø¤_—4enÂ^g&ul9òý éŽ+˜ûWÔ›’¤M yA4ºzýQØ|ƒÂ¯­lÅýnX*oˆGçrºþh*z¾¿é€¤qâddÚOj¡$CW;DÂΗ@Ü×Ìi•xŒ×~G'ñÎBWÔ<ד ß·åºK°ãZÊ¬Ãæ=jÇšDu†»Ê%Ù+…ÈB3ÈÈ,†uõ§`¯ædðž,1G@Öö6ÿ¡‡NÒçùŸY‰æIbd®É4îÚ‰c¸ïCuéIÍ“"Ѫ< c~çsLTJ.9Œ·~«£‘»8›kr$›Åa½›¼:#NÆLNƒ‹Zaô{äWîõÇf˜¬ù~>‹ØÙfKR,ÛG¥“^ÁøsqÕŠTZܳgÞCbŸØ»Ý)°q©é|…NÏ‹°¶·’†ØÏC«¢Ñzßõ 2ïÞ@§‰ŽtêŠ,°·oå4bù¥j¢Ìé¬ ¼LÃGu’¬âÑ8bÔ„o—BE•2³(¥šï °{ ¼êò¦>k ÉÃc9ÅùœãåëèZÛÂq¶qs‹8Ú>â⬻°‚ŽŒrý9sx¢'ñ6˜¿Âgï&³‡Ë8íŠ 3» ë0øN%ÌÛ´ Š'¬‡=©ü½¹È6Ñ«¯ë ËFOM §þžÀ–·a üÕZã>‚°z(qm-¨kâN‹¡õêp@P"øW`©W9ÎÒ`£×¦;Þ…ÂÅv=’dÒ}"¡ |ì:Ÿ"]CMü·àœÃ±äà}y|Y†û‹ÿòò4my>'¡`ë'pþ Í¥GÇœ‚ºu¦dqŸ–o$%Ï¿Ñç‚sѬ÷ç™kÌÆ §a.íOÄwÑhÒ+!»ñ-¸g;±G•KݧžÀbΧRGÜ-Ø­~«´{“Fðî®,£“¹j7e&¿Ù¥ìÙ‹ÆRÞ¼ßÚ[qÅíÙè•ìʤò5Ø÷Ò¥`qñ(èìÌÀ/S`ÎàWnå±tá #´y"A5Ó±õÌQì|6û†+O÷R¢ÞT%Ž#9ÅpÂOƒo’šN–ì’&• gùŠº>ÜÚp'ÂmߎçOð!+Mš=Q»3NCðއXÝÁ¸Õ…ó1ž—UÜˢ“È47â?•-´cúxÚTd¡'aÅ­l”ÜâÞòé8.æO9°w´Þ§rÿi¥×(M!ªv%PZ;-ÆÜ‡Yk|QäI( ¹wSù:ð<ßÇ»6Yÿ\žWc ~¶13 WEÙ÷¿ékR ÒÛø‡ŒÕ!vT# é®,ÀRIc>ðÑÿôz)C‘(M³'•®˜¬3›¢ÉGg)¶/o1ê4hóü½ôËiªr<qÒåÜZ»yQÜ|Ö—ô˜ÖÊ RÝÂ~,YVˆžG¾Á‡ñt²ª<? BœÝ¿ÂcÇ"8—׉Z è'·©$nN¾ü_œ$ ó¹ ¼i&õ4J?ñ%«ë¢—1í‘(QÉÕÄ Ïnø¦(®ÙíŒg´wÀÔüoXüî ì_/IÏ:á¢v²uˆw¸À„¢Æø¿vGwÍCgÝhœ±¹ —ë<¨û¾ãœ ˆBßähyl3é×ÈÙœf®ÒdàØ}zôïWh r#3˾ÂI»ït®¸:Þú “Žâðö*]™ËNðjáöK?èzV‡äD,Ìc¹¼âÈ5Ü'ËWPÑ œe²q‡\)Ä,$/ůϱã¤,y#­‹vgÁÜ ÇÙÚåëΫÿÓ&×ßßë!0ˆËVBψK%ìÝ€ª^‘#ÕÁ0>xIÛNayñ•±æ£Þ_T>ÁpÕ¥ lš²˜ÏÏ#æpɦ8˜pn ó̈~ÙWœø±4î x$ÑÌš®Œ',`H|öä_ÜöÉÜîyQ`UWnMSïgPyRK·¬Œ¡ËÖ(¡|³ .¼zŒÞ±uÅøgvìõbY²D&…oðæl7×ÄuKøñ‰2ð'¯b³ò໕yyp>WÔdA†BEðn™ Õ$Ò&²Sð j¦º£€n81yF5÷Á‹USpÑê|œ”E_[äÁ‚qµ õA–HÜùcÍÙQ¿ÉšySYóç\zèn,÷Yæ/‘ö9z­¸ßïˆA½ ïùÞBO¸ÔàÓÁxR¸…ëoÃͳßÖçT½  “`»ñ ð8Obæ_x%Vƒì÷}³þØâå‰i¥Ë"¢PÒÏÉ6BXð-øså;è]´¤&‡æ²+Ç1¥W Ù^o)â±Gˆ¼ÞPKèi?\ëñÌ6y’…e Sç‘×õÍè7=Š­zùÇVëqÎñãÛU~øÅ“ÅɵÑT€kÅ&uFcÅÇ2ÅÑ LJ±þ`÷xÆbäfôdPm§??€R啨9.˜²Ò)$ÔÅ•/žÇ]cY~ç;~4 Fí`Uò©>žXU,!1éÆðKx·©õ'}GÃÃ6УÄÞý)„ó_äP)Ç‘]t®¦ë¿¿åj¶ÝÇ3y ììøãx[gþ:Ú°ª®'øARÙèÏÁ†¿§Á1õ<ü™©ÈFòÇC· +’#k|ùuc-Ëš} p·'[ù»öL}L…»˜˜ÐAVw}‰ðC’yOŽT‘H(=ŒñA±°*ä,7ÇûGÉÌý°ùÄ<'ÛÊ;;ÊG7Û£3qðe梜ŽÉPJ ¿…‡¹CxmÅ (>ÿ~ÅÂÉ‘Nv÷@2;þå<;”×´@jQ8›$éH„¢ÿAßÜ 5šÞå37çzª}fô<Ÿ âg2¹ŒæAŸï‰¦YìDÜ8U8·¯ê^vá+ÿóœL¯*Ìø\M" ìò™;Û’’I¦äC¢ éÎF ónªS*H¦~pCn‡#3zù¾tj‘¡•Éx\õïíûÕìÜûC8yA m»çB¬ö&ÁÅ¿àVVä- CšqÓ§ôŸ_‹/=5‰­¼>÷Î ¿Æc‡¡2i¨í€ÒouPh]^1SÐY)û⸟‡¸YÉàà9†Œò>?±ýÔX8´ã:nMR±Ó¾Ç}ÑEÊÎÅp ëNqkì£p*ƒà›_T6á"z™,"Ž hÞüš GîH¥AŠÈjܹGŠZjO:I‹DËP·g5n?: fÏP‡×¯—Cÿ± 4’~‚i=7øöaš°~µ¾R_W,Ùºû]¼12M°-î<Úî´`S>z°!S¼¿^S6H±Ìe‚ìjôfxtÔnñ½L,(€kŽ¢ á}&ð$æ=„Wo¢¿©8«´R…[ÂôëÁzô^³ùºìŠ},m‰Ì¢¼´zºœoÏ€?Ê'‘…{¯´‚âP ì_Í‰Çæpåª=»vA†(îýýéKÐë^#¯4@ þšvߘϼ9·Îbƒl<XoÆI=ZX,ö‡¿à°£u¦`éXÈòˆE›ý=T„fB{%­uU!wôäØ0[B#´IÄEQÒÐ< 2e¤~®¦*YúDdm4ßñp›|µóßu{¹"‚xëîèsZŸŽ¾':QL!‹6D sb#gl ÷kaÊý`TKKÂ!»TÜ·`|ðn ÜÁE#`m>žý““c‡øg`šj ·ÖÇ÷’8¶é¹.eu“òñKo]™»ædâí몠9~/°YÛìŽçª[/·ÜÄî®D‰eOa^Ùyˆ·iÅy•i¤´(‘úÏ• Uö…|ÒO5j—ƒJÿn2m¬+­Gr×0ÿ½ F¿ÃsƒÖ¸L}*Þ\ý¾\¯C‘Ü ›ZŠp3;ÈÒGœ`‡þn³J1Õ¼ƒó½ë˜À-Xpý/i±|~>q1bßç铃[~ ¢ömšÓε†˜³Æ™&Ä\k6ÙjnÌM?#}¨J2\ÜøÝfÎd®æFX¬\Âuÿæs¿þ‰Çuv¨æá-%¶ËNþ†œ‚õpí]6i§E¡ÀÒ 2ØCŸ¢©ÊŽ=ôä­24g»=³Pä¶:9­—€c6î!)ûµ {ØÎ•>EÃM`‹)ê¸e+ÌÉÅŸ•³î7Çah ‚±½™¹!eÖäÈí\ȱ4;ýL O ÓnbŒÖxòÔ½$mĈù˜Ø—BKyÕ°Ž4`ýëx¬; >gB0ð¢9i­)‘L9’±ÓEŠîÁ»ºèÚS Ï|ád„Þƒ½;®ó“º'Ы^ŽPØ+MB~n#¥½µ W{Ç×Ê={ðŸþ%1ׂÙêt®ëx'™¨Fâ²§²Ÿ‚äÁ¤Õè-0‡™~0òa¶çâÙµû`îMò¨(žzD+²÷®€¯–H‘o½cȇÔ$,*$ðÀè6hë± ¸c¹f)ê1ÏÓJ8k|&š6ĽH­MB˜ã•QŒÚ3Å©€Õ§Ì`â¥÷¨°gs­B®h*wjÁå;c±á~~8{Ýs€±Ô³tÓÃ(nëA-Æ÷ïáÒ~‚“ëŸAüšüöhƒ¯`µì/P×”ƒGß1óo4šÌ2Ý•ÈüûcÙ@›|~Ak.HÀÿÉ«ŠÛòFÞûÁ½4Ú[À‘öÛ$Ÿ ™:’šµ CDzMv;ñÌoÔ<{ î+«ÀèwÚñâ˜ÜLÍ“ò¬75˜Î9gÄ‚ç:s[j!Óû·«í¸×Ÿf‘¿Ç’¯¿>Ó»z‚L3Ù„Uz¬ƒ¬œÄn&™ÛÀé ¹Á‰Î±¬ËtK>Ò‰Li!ü!H×ȳ·w6òEÿ‡~&¤ñl3ç}üÏ=à Ÿn¥ÃÇ~¢cœ8ëëÎŒhÔx#Ò›Iƺ)§ÏúÃXG5Ãá¸NovÉtw>Ïf긮ñ:Wó²š×_%Ýs”áÆ2Sd*ݸXí(2õ(•ÝŸƒ¯Â ÑÇððG˜ì=L%Ó³nCÌ–1ì¿k¬Ù÷c3‡ã»Ð©ºˆýp–'›;âþix -v¸2ùççx‹§íàø}]t¾{6ð6 ±£§}ÙèúYnöçÍlnˆ-|Ûì C!p^S¤:4±ýÉ4˜WlEÞX¸cQöz²Òø6,+ö⤅T؃Rgò;P˜¾ìßCއM£ó?WØU¦È³W Rì‰ÐvìÛJši6þ§v4–&ãJÔ6ŽÎí[³ðÚà&pMŒ„ƒµœÌ¾¯`“¹ž¼ù½ $ù0õö(þ—»Á4csvwµ"Ñ~ùu¯›UPu8ã@wº·$°]oM·Ü'œºÐeG¶^°®ÀU;Ãñx’pŸáÎ&}V58æwàóÔ6˜>Çæš¥ozŒHúòhãö¢[zËôÚKŽÔ9a§²*6<õ[ÚÎÏŒã$»+àwX7§s£ßx棛÷nqÂg©_§™[œÿ¯Ì̯?i”H'rà,º¿¬Å[bj,A\Œ”ßdõž§y½¾Üñ£vP(²‰kÝíû½ìö~@áô‰Ì0~"YOóLÄÙ½ ÛÜÛ9Wÿ¿ÿpÚAv ï6×âíF¬g ‘‡r0åy2þX÷§”h`ñ&Íú÷ft©ÁÜuÄ숮‚ƒ¯ÜÔZàfJLWסڤ2žçKMO—†q‘Ò$¿"æÈôðÉ·V#ð©¦}š¯açõ•$, aÑ­§L¬i*ÁY­Ý‰¯(\wC?žBÒèN2Þ¶rW·× sÚ$’ìÙÆoê'ÚF³àúæ¸#¢Ky°)’˜²T’h»‰aÄÉPLÙñ“ëúªAß_~„§ÅTÁ£cžØìϬm(ÆH[mœJ WÛȹý§i&‰dmÿ':rçÈTrûl½Qqû_1ÄmݯÍöŸN…ðÎPãøƒ?©9޼y¯“ãßÀ1[æöº ݶ«Žò:cÞƒ«®ògÞ7ü– ÇBâ³ÁYÆÛ¥qôÃftÃä÷àçÇ™¸³/‘ŽÙ<†#ìȃÀLb¡{¿ÄýI¢Ù°‰1M96˪VOä>œ|ÍÜÉ9ÚÊ€ÄÇ|è/ˆ§kxäùÇñ¤^ä ×âó„*-w!w—gââß—PçÆXÂï•d‹Ÿ¼ÇåÊU¸xZ4&wÍ%-âòD2{ØÎu¾2YüW€ß9.œ\fi%À­z!g{¢[77‡¼/àô‹ßaÖmº¡ø]3ÑŽ½6M… íüö5^H«F¸õO'€’‹(j²ªÐ|x×ü ªc”¹¹¹ Á®\äáýK"¥·Œ½µy I={áǯ?ð±ÇŒd%Ñý‚P§ÇFóÇçTµì6öªÀìÕã ãÄXV\B癢ط 0>½5¼¿ñ-炃I8}°ò^3üMýT#Á§¥H«=< ’d§÷^à&·ºàÕUb 8>”:Äg \A.Œ3ål’>bMÛ³'[¾á½~ ÎúÿÊñ_,W'¯*«áÎ ' ¨Ü j‹è^ÇÄúàt˜ì¶™Û1Ø|z}_ž%³¼Ý„C—”Y†í3H*qD½âÉ(SÞWìßóúÿ&r3óÅÉÊ»™\ù{«ÿr‰¾®Áÿ5’ìª?=y×SªÀ6i…o㈢S ÿLt£üãHfÐ)L$WRûZf¿Äèÿzî-±þ[}B•5Ø5²ÿµ 6Y cøûáÆ°.{¬±€|εdËÊ~p)ð¨S‘ì寠C­ËÝæIÿóƒö²à–Šeu–SØFÛaØÔö{#c!î§þ2'»7ÆÂf3HCEè¸ìjwãÞ~ ¿­°éøôhbnÜÇÇàÕ)†Äc·K—XÌ N¼ ÷¿u£Kò3~xç-š´bÜšf£¦›:˜Oc¸ªÛšu6áﯠíè l¨‹¯N™±d§A:z/äÚ?îÑÒ%ð}×ø° ©+±³µaÌ I¦ReÔ€ãî;~¢F˖ͳ#ˆÙË‹ìÔÇÝ_D V„ÐØ[Ø„ÓcÁç».]…BÖr—t¯"kTHø, _¹.ê‡f“½ìkà\/äsÇ—é²&æ)½ph,£ç 4ÐýÞ3˜Q½šðŠýÉn –ýžOÖ äbŸÅ ^ÌDAÂŒý]™yc9¹#7 |ï ‘ñ³ß, ßúþ¯¶ön­Mwöaî2²<Þk~£V]'ˆˆZU«qâ{qw¦€‰A NíE! ¥¨2I˜Ë f™‡°¦FìïáTøSº“Ì¿ÐQüRóEغÙû17D{<7š;pôo‹Èt|,gƒºÎây…ATÑE_9Ù«øy‹’ñ›Œ R-€­²Òä§­$ûr^re๻øë5.õ-™ÇB©^C.Æ]ZÄ=×5géXlj¬3$›~<¢»ÞlÇ£ÆLÝc'ö âšIÁ‰õÑOÊJ¤oÖ;œw÷ø]…§ÞÊÐë«Ë°wmÄ\Hä¤ýÜ„-ƨ3ù*8ÿ‘"¹ãXFÜU8Ö/AΜ¬˜²vôó’a‹Z~£ÂíEDlxMWÅÙ•ô$¢‚‰¦d%‘$'§~G RÈ¿GÏ„ºó^>Ao²–ù·ï„£k”pîóúzoԷ牂¿ÇìS:0µßŠ»{FªŽ…úI°eÛ}œ?µÞ(jÑ]³Zé§n[š »S}H‡¿™¶p=8è®ÃkóHñ×iŠ@¯‚ð`Ý,À}½ˆfW£èÉ`]dFiü´Ž:øU)ÅÒöh€èBY¼ù~.ÏüEmŸ¢ÝN)–4‰x`…“}Kpÿf|3Ù’7oË;š£_€w–ì'O•8Ì‹æ +: Fï ŸU£ÃÏó¦†*Ò¯«¸ñƒÀ(p«[¿ôO[ž¬©‡C'úùw:ÿÖld§ƒåÉX»Lº|þM¨i}‹5°ï¨“¾÷ƒËMø ŸÍìÈö$9¦!5 W¢ÏKØyŸÜé“'¹#—ðð5%Ü4a˜Ü€ø¹g¡5£…n18lÏpvÀïp†F_9bÐú‹zV&áÆå7¹©ƒFPmÙ@Ž“ßv2!–pÞLš½öŽòý Jl”gïÀM;­Õ_¸o™‹¹©¹ƒ¼#wk…Åßè+‘H˜´(Ž?Æ[aÏú¹d‹È_þ[í×Ü»›¨îÄΜ®ƒƒóDzžñÌÑjv²&)÷ÀÝ­Qa¬Kp×b–Gþ¯‘îè‘ ~*«ˆŽ2ÞSjÅØÑþpFc„ŠÌ)ÿÏÃ+¤ÀÍ»ÓÈB¹£L îƒÅÉíd6IbQk«iý¼_øÉS˜yÉ>ï¾sXìàZVþç«XQéeÆléøUÌ¿ª„¹¦È‘Ž?KAŠ¿‘ô½r%‹¿g°uoí¸½‹ |¶+ù´ðý:—#f…îDf øçwѯ'Ʊ W†èbñÖ—¬ ™e»=ç;)ŠY?ófAglÙÐrS~À´-¸_Ú ÿ ^A«V+æ᥌3ûöRÅéGž5s˜Ìão« &÷ìw€é@•Ÿ¢H®_ÁŠ÷ÚÄ2ц]ÕýO· k- ù¾1éL)ý(L9®F6;®ÀsHߊ Èÿ˜ §»¹<õóúÁó–>¤¼b©5Ù‚5ÚÔ-sešŸØ­¿žeÍI‚ iI˜P66·í‚ß’Ø&Æf¹ðåcáv³8„*M'ãw,Ç¡]ÿ×V N;Ï›­+BBÏ<¦ùã8B݉æŽU…°¤Ô@òcg!Yj4‡”zŸ¹ âd¯³Q“ÙË[q‘:üÅý9Æ`7‹)V‡gc¼pnìLâãþ¥ý0¡W§ý*¢†ÎÓ`鑱ÜÝ;SÙ‹ï(<}ùqò>ŠËÎÀė܇°uD¼¤×a‹|1ܲ¶Z Cú§}l0X‚ØyÂMƒÕÜÖœNþÅ1ºXwZª–®Ãþf[øÏ¯yÙmý$ü¾o;¹|T 9L¼+pš^è¾D†ùðL þ<2&O5ˆQ :;f(€b~j°á×*ãöŽ&4Bk£ˆd/‚É™J,.¶·ŽÇñNApÚÔÖ­ºçgCÞÞTP¨Ü ³‚A¾Ë› ÔQÄèkZòµ?/Ík‹gq_™¹¹GÄ'n öjIè»Z‘Ÿd·™%}MÆ0ßfLܵ6ÝÒ†ºòÅäÓóü4=€wäÅZz`w ;ømºÇAqó7X#òŽÚ´ƒ|¢`IÕ[ “½’óDÁBm7lSHª¥>â÷C¼§qZäyU$v]ýÉ»³¿'Ñ—`ô°Ì)eŒ€OM*ÜòÑdÝQ¤Ü° ëÊZ™Ýúßô ÊO ¼›K{dqÙñNܶ ¿UI³–h´)¿'²Î¡–¿íݯˆfM£½»“`óFV2äú8ƒ&u¡ZÝWê9¾Çn8À¤c^¨¾U–Ýl{ë¾Fá!æ‹ë¼ïЪ®Ìá1íßÃøÖr9^ª›B‡ÊÑœ8ÏcSpåÎÍðqj4÷´Á˜ìÖú 뛞 *ÖSI1Qr¶ÍRßi±ÙoXvD"žÊâãν`sÀˆÍüZMÛ›ÀËë!.oüšK^½¹ÉïW¢½:|ñê¿Þý ïVª6¼¡]Sáa@=¯`2÷{0!²ußš€žè*ì:%Ö}þi¸«µg›¹ðþYe£DD-E/{r7yµ“ ©w-úIrö9mŠdN7Ûìô:Ùž{íüµ5ÙÞNpÿÊk\r·•×ã+Å‚…Ü0Íð5š4¸bê‹—˜+Aù~¼æ¤ˆ½É™0ûÝˈŌӰÚG—&´-bá&-p87¬^ðs<<Üí-<‰Ä¥2\‘£M„’Çñ*4Ü/ÌÖhup‰6ü¼Ž´o»D£î ,þ#ƒ¿gY³jEBfD¢ïQ R“v â¹ÂnŸ8îIÀýÚ9™cˆòêW´©ù8>h ‡éË0âT˜§,dv>†¼+«wƒhw"Ì|óøÇ΢xQŽC»_Ž`´øg˜÷p.÷¬È.eœr~À–çòò’z¤÷LØÈÆqàj0ûç7`¡«2Ñž€Ráóàä>S¸¥ëŒùŽí6[¹Ïn¡tâ[DªwÛZèŠ'@£Ö’ÝúÞ€ª1õçnÎþÓ_g¿Ò'Úü?°Á)­îÕÃõ Ö0w§(I‘“†¿ À-k")ËY=J¨ñÊû4¶áŽ<$Ë—†ÂŸÀÁ0xG·]¸ÄÃrð¬ß<¼4ñ —Ghž‰<¬ŽÊ¤ýÙŒóí§ŸÖÀ½cÉtݾ›|¾ºµ MÄs­BÐ*þT&ŠT ¶W›³Oàêl0óÈÃÔYø+oÊMÂGþ]îúÇ @Ë h̆4¸ý¨užx¢ÁÖ|8’*H*vÕƒÆõó\ÔÍ(8Vék/>â}ÿ©‰Ò»Ÿq¯çCÅC­õ­Íf¼Ú7ЪÐÓDZq$£jð¡rT€ÀJ™ Ü劲 \F—%m¤‚a  òTƒX€ík Ïä4ËLâ^H:±ëºrä|Lg£7òê ÊG·Hpô—:3¿ ZCÎÈî¨O-ä÷bëì?øß÷—9û`¶y“/7[ù0Û;mì˜ñ zV“CV‹Ysj,N ÙƒÏ~P«jöÔ9š«+b·i›7gW&ßÚ hÚ/Œ¨Sa{_߃ž1ÊìòW4Gïì“ÈÀÇV¤+W­Ù)ÌþJ„A>¿çâ?ϯØ=)jݤèÛÃ*äÏ ûNÁÙ¼K(®?€†;·QGq"jô„Þ-‹ª{d*ap+Y‹‹«ra:k—±{³]F{¢úÎåðL£ƒ×ºY™mYIêî»ÿÂK•&¤yb#—tYœœÝ³ Ž·ÇÂg~ÈÎ e¹(­·†¬’M^Tù™ë/qß²ÏÑ?ýb$]ª…jÑh¼‹à§¯q0Q:¥MÞ€_ÂËðdT4ÕÆ Å!nÜúõâ¾E¼Ž€-ìá¿Ì1¾k¶oãÉ|>Ä;´y&.Ú(%¬xÇÎ&ñ¼xEL‹êà‡çªädà:ÒLU±Ûb˜ª o€|ÁÅÌô¾rR­ø,Ê Ì³÷R=‘nòë4²©!³yóŒE]a‹v \˜žÖÎ`qÛ6âŠ™Æ ¡‰¯mz@vO<ÙÿÛ‚®Ÿ¿šDëg"}7ž<]/ÏK®Ù_BžatÏKîÀÄIdg¶9ÆÀþ Ça”[ѱ>v+¦k ÿæAôp]1ncÙ»rÈò ã¾K8³‰?"àÕdE8¥õŽ»»$c3YOßeVŸ¹÷Ë•Pji0X߂޴éääV_ÎÍ¡“+Uý@SDÞðÏž= ÉÛš¡³U¥›x6Êðy]*¼ß¡„½cöäSnϺótɟ賘Ažût”Eål@û h¤mFnÿôG93|ñþ^·ÝÓÕHõÉ¥ ûT'8k6ÔœäèZ°ŒÃ1QËÀ|û+ü#7wéÚh°«ç¯tØ3ÝÁªÅ°&Úòï΀  Ýð*î)ޒ«‚Áx$ìˆÈ†¡Ò`ŒÝf ³Þü –R„müMƒ6¬™ó™Õ¡ÜÆ›¦]Ù ½W þÄàµEÛ¸wÒ0Ï6žÎ˜*ÝCþï"#¸’|S¹bmxæb5Þº@%77 \ñ~xõ—»°Ã({Î&nëݱ?f“ÒŠõ~x>+Žãû‹ƒqƒ2m~½8­äbä\@•¿¾ý²†My÷ø~Ë@PV—-¸úÍ ŠTæIâ¼Íi¨ù~>;j‰§>ź-ôlîÜÿ4ÅtØo3“-kÑy|q¶tì:ºÇÛë¨ »ÄÍù¢uðì¨8J¥÷ááù/0ÞµªÏ_äÚÛKñÎyMb²€\ï9ÆÊúB Xò=šp–¨½¡c„%˜øF'||â¿s¨úzM^"þ%ohÓM!v åF-ŸÊ¤,¦GŽ ³{«ÕAd’47AO¶k HÖ]ÔU.ÅO9|Ø[ù¢ÀñQ:®3¿©“P×ï<—PKn' 7VÇUn»ðrÐ2(ixÍëY:fœ@ÓÎVÐã÷BÍáãx`eŽø £aТ)lI6•^†Êà}8pq?ô “Û©õõnlµ@ÖÊŒ¨ûÎÆK‡Ýÿâ–2Ÿ¤ÄÂ꬜öŒtþú, ¶r]5$'‡³M3:ÀV'Kgcø«9øŒ'FßVÅ‘À>ÜÝ•ƒÂbD1k±|ºŠ|µÆP å‰õ)u¶kYþ\À/‡é¹kÁ}Í\ô^ûŽ»\XÉ—~K@¦þ;NÌî€Û¾"Œ»$Ï›ït3ØË¢¥t÷‹}ôó šwb.¬AýBò»"Ÿ[W´~ÝLÁY´»áEõ«œñýÛtе4S4ˆäFæßäŒÀ²i‹¸’½*¸ìÙwt?˜MÓ—À&ÉlX°^ˆhˆ<¢—KTØAwEê½j “–| ]Ú°BTž½î2˜ax–[”’ ©³ ¨œÍ'êº]+ðÝs;Ööð:·x—%%ãnBá‰Ù¨'ùv}ÛŽja.ÜÅ — ø‘ ô&5Áº_w8÷ ÇiÕ|?E•¬×Éã(»Ì§­ã@½m"Ù·é——Ù€BÃßàì·£¼ƒxVþT¥Î.x;“Â7Ÿy‹¥ßš÷y¦…³ˆOû)T2:Èv‡ÿß›f£IÆo®ÉØŒ+öWŸmdú!JŒ„Sê&Òω(ï`üÕàm8}õó¨ÿÏ—¹O ¶:H‚ø×y¥eÀ·çq·.~áKºÖ@ËŒ›èvý̗ɇ ñL0Q“Ý?¸¬þ{¿¶,臤`bq}ŠÆ,4ë= |¸êE*D+A~ªG;Ýo¼$nÞÄÌ–&Z;þ;'wÔ²„Yïúóðôp? ‰Dãh,R¤£ ßZ¸3nfìæöXsí(;x[‚éNN¦ß¬,Y¥ÄaTÚ­€ñªGqç’o0G–.[»8Ó77qŽŒÖÎÑbû+M8WóôAé*T³Sà‡ÏHôëêdSacᅢ¬Õâd­ÞF¼°S€˜v§QÂ"ìxŸ.™aŠÿê®Cc¦ñ52ã\¹¡tü=¬á<Šá’å(`lÏ$n\™^Ü`¯E¶%«N˜sçL¹ñwà3[§SdáÝùÜ¥(9öÛU„¾•÷‚™KInänטwpašš¬Â>,¹ÀŽÛü‚/Ò!$ûV„„MÃÞ+6,qŒoµ” ª?ª‹rÈå½s  [Âô±yþqˆIÞÃMÙ*׆gàŒ¯ßÁì˜ÂʯlÞûw¤ÚÈ :oäŒ^MG¥²³è¾ÁŠFÙ•’²÷70ö— {/HïCÞ¢>˺bJTùç¹>­é,ÃILÌ$òÝÆ ^JîgeNNجhBM#§í ¹ÜÏŽd×1~ý"iV.¦D׉&¡ÂWq\t~>”±ƒ/†W‰m¡?»"Ëck…}0iÏlìdÄÔS«ÈÎ7¹ [²–eÓ\ÝáLxÝü;Ìäß\)Åî7•¢RÌ&¢ÓïL,¦øAeôö1i3j‹eëãêy|jÐp¹ kœ{î+s3A-¯‡>-ÊÅM…Ê,7Á‘,3^„Ž!âä¡Ä9(í½ ×}üØÊÓ¨ðû¼çñf(¹g5ZÞÝE¾7ÀJòbÕ¸Ù‘L£^• J-';þ üçíüè!»c.Äî/êàv”•ò"tXdÅ{LÓ#ÕЏ˜Tm<ê*D„×_À-à±]<èüyGEÒ|øz•ut€h’Ú¥ZüôSâÑÆ>N?N³V-"¿OBÍo#¶½Ë ãÂÄpB§&IÌóÅ!Ý$Ôþ„'mœiØu6Mñ6iÕ /î÷bÐï P‹·dÈŠß„ýYo… ±ØþÖ›¯ç´v%™á_ ß"—0ÓsLKÌŸÜþð“÷JÚ¬zä˜[­kò_ƒ¥·0Ö4—|kÁ »Ç°ØÏ¶˜Ÿ^EËf´ñc‹§Ë”¼¾w»ì²”œ×‰Ælý$¹ûM2Õ#íµà{[èÏáÙ¤œÄùÅ*,û{:¦ó4ÿåàw¿Dfay‰™žª€éKIɵXf4i)›æ*C|§¯Ï…˜¾Q†•Ï€Ó©ÄBìwñ,²~½4Œ¥Ix#Wƒ™ÛˆÃŒP0é(Þ°ü¼DßÝÝL>&ÀzQe´­Äåé `}d»ÆôàŽ5f,ëNÌ#Æ’_ÓIí“J+cÌNÞS%F~Kiov0ì7-£ÇÓ…1þ*y$\‡>µ·piô÷˜~‰mû6–¸,´†'  {«št‡ÂÙÄ8öÉñ ½º©˜ÔHŰ¿ãÚh┹,9d€;,;—å<æ‘Wá.Ä4Ì™tÖ ÂºÛàm›=ûSœÍñ>…*ØÝZI–TÀä1iøîÏÖšbÄBÚ‚\ÏìŸ(Bî#gs3pÈ-™µIMÃÄÙ¶¼ÀÞnÚÌÊ?í!ƒæqøïM> ;¡ŠsK󸇺'˜Ëv'9’XK¶âª5 '?çîÿ‰îkXû srÔƒGÎõ åña$ªÜÆ JBòÒ"4Hf<—ãìew\°ü¿¾ëó=£XîÚ•dñ¦™lz‰«[£Çûå!Û@?f<`•®“‰ß<ø‘7ÏŠ˜1ÞŸF.0[×Ù 2ssö·É‘¼¨ôc¹ÃäuhNö ÁñÄóC‹#áïd]äfVb߈¬ýys;b8Þ•ˆVœgÚKŽòhÞÇñëÈù¢êó\.†±§BìªÂ\š‹Û™ùH$9˜×²+ꉦ¿,Ë!gÂÎ㸌É$ 2›õ)ÆàšÕºÌ'[®±áM†ðë¦-ð+?;{÷‚ÊÇ pûýIx§ ;½&Wøm˜4õ9*c³«›÷; ¦t£‹ÑòEÇDQ®º+ñx/ Ÿ»i˜uÙŒÈd„ ÙYhQ‹S3aø©àSit\4Ú &Çÿ_-þl¼í¿ª<¦™vð7\NKå’ǹÁ0°<±í¿{ ÑH!Æžé¡§ŒÃ{)6Šyj5jìzØ:LÜÍMåÝÁÊšLº$nïá¶ýJàts‹¹ Ö›QÍð,¦¯Ëž‹Ç£Å¬Ipo“1ù÷-Ÿòm²ñbé ®H:”NøôŒ.ü^‹Ìb Ø·¼óˆÂwÙ@’_OMŒb|hs5‡âÁâÃeº2ÒŸÓ íâM¨ G‰B¬Å±Þî_üVˆÙ•Š›Xp%×C‰™X}`µóüqeÞQßžj.·«VŠ™¡ÎOI ;Ùß­Å~õùržgcj®ìÌ'°í‘*¶¬ÞÈ÷œøgq/óÍ L{+¸Ë“ÿpñ6‹¸®á5äÐR)v¨âÚ[öêâî‹"úk»pÚ¾×xÌã°•€­n w6&‡†Æˆ’–ÂdäŒ1óü¸—ÕeMg‹F=÷j˜¼/ô…è«Vp]WÏxDÁç&TàÄx²MB…Ô? ÆÃæ÷î¸ÎΩq¡WÑÌì¢=WûVÄ^Ç´Rî‚XËbv]ùf9I£ÊÝyìÊäûxþ*»» †Ùh°¦›9çâ÷˜ðó8jLØÑ“ƒÉ˜Ò\Jr ÛU´ˆ‘]?¸Œ¨b.tû](‘€ÖojärÒvÔúsàŒ({özVFÿâýöq £ÞG JÃÙ-éÊj8 ŠÂâåvÖdzKjhfCu[vnþ~@L®€ÝM%öýÉ"d¢ N]ó‘Zmh‡ó¶a¼)ݪFØwƒw0d.Å9ÚFÒHá¥`“~2£ Þ¾=¼Ê¦e*aº Ñp´'™nÿìöΪŸZ )ø>ײ6N·Cð¤ ̹ZœÎvÔâšúúàsß,6X¯Ã9sïøáësA9þä+oÙþüâãGçøçTpÁLˆ=/Œ?dBxõ·ˆÓÐEÈY`ʺ•æC¾ÞÐ#¢YaØxó§`¦Qž úÍ“½\Žñ«XB·*Ørµ_ƒÑCÖÙG…âæÁôí—ÃPÜ\„M#$Î`yåA®óE:õ¾ÿ’Á‰ÞAÌî9՜؈¶¬uûEü`º‰é1cÎFÇÀæx <ÿ; dñBî[Þ;ŠSø¿§EÑÁc•­EŸBØè@Ì´žËUcIлÍlÚ;t‘j„cõHÜ2ó˜ k¸~—d-;Žìå2I»»\À_ß&3ÉR5¢çÙ˜ÿ—ËªÛøåo0„7‡p½NÜê¿õÜËèr ¨MÅ‘Äsè6n9¹aö5>A¨n'÷îÃlºË2Ä‹ኮ]våùçæäæ7îú?AR²X€Íµÿ‹÷Ÿ¯—Öð‚Î!¬ºÈŠî®A¿-ñäµøVâû/§°zþœçT¥Û–ïZ~ÍZ†'¥Ÿ‚Ùks6,ö’Š®ÂSðý›H ]-BÎOŠÄ)SÙžAT­¨†‚÷…‡Šë‰Ï©ùÌÔ;‘ +“$¡üðj‚_ØNS"™ž|Þ>†täó†äâ16§ƒwD=†¦Ÿæ?Œ;¦Å|vŠ?âú$;pSUYNfüqq"óv-|y¹š+ÜÌ^/f¸¬ÆçÇ6Ð*kàæËUÄaåUZ¬eFnÄý†Dåcl}³µU'©7rKìÀ¢Òz$‰¥§4[~~ »C–¢æ¹ÞŒ¯¦dœsyäP‹Þ’ˆ?¢«ä:0{äˆýxûÓC#f-àm¸…ÿd^Ñ}§âp‘Ì7üé øhd5±¾8H?Ä'‚ò¢ëò„þP„K?.Ó›¨ø,">U*°âÚxºªŸ#—³Î4S²Qù#§~ÆK³¬y>Qyö)þžá àGº|(¾7#Ê“Ÿƒ‰Ÿ ©H â´b_BëP>ˆse¬ò_EÔ+AÊzKxß@®6€'µQõ±'d°¾ßô®q…äDv3Ûüw÷ 8WÂå­çàÍ|9Í,ï<箉¤Ó¿<…—m'¡dr ” ®Á 2áp8FÞî¿Àމ('ñÄChÏûÑy9ü(Jÿüôõšì“#±<–’Ÿà!µóÁÕ/é¶¹LúrXk¹0«Uп٘í/çÐÿÍ}(š¸š=ï5ºfp¶Žz+>¡êº± +gÝo]åã*ëË0¥c=xÈ‚Ä}ÛN¶ž’ÁД¼ Úƒ=SXÏma:M:_œƒçò‡ˆ†õsÜ¡•N®8 –‡s3±'6ñˆi\-¶_sÒžÑ+¾s¢›"éF8ˆ‘²5øÞ«Ôu¥HïIt}”0½•Òá¦L÷ÛÆ¹èKp½n§Ad[(ÿn}#:JCã¡t³ [¾PLé±gýï’xÃï¦c_çBÒÓ2ÈÝ{v  糟‰di²>w®+—¼ô'Ï—¡Ï±ú)g;6N¼3þÀ%kYá6RÒ”I?óeábñä·e<,5hÀå ÄH’÷{<“<Ê‚'°'©:ä{Oä%Q †LýÁ|óÉ.ž31^}„ý J…Y8„S{ @ú£Üö¼ =»Ž“ƒá÷û›°+ßhÓ”‰Ìòôa¶¯¿ÍVe?ÆÎ'ÚŸµÉ3ÃnÁÅ5ìVù72ãÜ~öuö2"‹1DùJÑwo…2Û´v:WÛñ.ñxÑŸ¢èãEŽX~ë³]û´¤äÁø£¤Ã2ªV“rÈõ“€·çOá¾Ø(¸i,GŒ_‡Bu@ßY¶øŸ–;Å,I“eF_qvº%©ª1"%F¯¸õiñäI1ÂÃ*1ÒSpfN¡xÄ!…íXBx¡ÓˆIæ~2¥¸¨®Ÿ­eãfWÁmtݲpUçe“‚gÜiéSYòù¦ª– âÓsƒpN–L¨Å.Î?Ä ó“Éß]*£œF”Ærlø©*‰‹a%gžRçãìh¬YaOºóÒÉþÙwÈO³^.àmÛbyE|™?ö\ó”ÞzaÑ3¨¥s o­œÅ¢sbYŸU¦…’5ÿ.±œáR”‹q&÷¥Õ@lO'îylÆ‚õ<£bOô„¶1þšð0òîX1®š£D6M•ã"ÐöÒ:vçP°×ΠØÎ¹Üÿ™†‹îq²!C¼«)Øî–VŒêUÀ‡ÃÑó÷ªÝñ»#‘ ¹'ÓÀí¤7¼¸Ž&ÏœLþÛ°æðã)¥·t$Xª´ ¸ûë°éáRÄãÝTþ=ÅçÜi£EXqT’Æ›ÝÇ7QÃ|¯si¶¶A³ÉŽÓ¿SU”#Å3 Ù鉶ÜþØÛÔô°/´¦¢àm&>í$$ÛzÁ'jMl[2@Uiêho1€c© x×»–„8‚×Λv•œ89¹çú%î£fÿD¨ ·ŒÔmKŠ¥Ç‚P>þª[ 5L±zc¹\¼sÖ£f |.{ˆ™ßX¨„7õÒ6Ø!ùî9˜²!óµ˜sîÄ Y‘-þàôçõœhý7ª[4Šf_¢S'Õá»öV¸ê?:'f;sö2ÉÜ=‚,ãí/j±2ƒfƒbš=ÆÁ4R(0Œ»¿ã¶E:lû÷IäövtúÑã+ë¥.Å`ßfUîšÌ]Ú¾LnÖ< A¼ëôɧ˜|1Œ[q[ƒ:4JÎxA¡¤ ÿ]ÎZÚŒYY‘5LœæË›½=€;zÏ•<ìY•>å¯rA`I4¸,Cߤùóƒ®Ãy"e˜M³9ƒ‹ÀW^ªWÞ„S•À¯!ˆNtqͧ ØvèÖ¤ã]žû—íGñBÈ;¿ù¾>œ”Ú.îï‘LÞ¾+­ ÙyŽv)*ã·¿fdù£ßX>¿OBÅ (ùWçEŸe?Ŷ…ò\¢°0ì֮庞,Ãèû•øå}>mþùˆ›iÔû®,¢blý°Ñâ¥Qïzª¥î½-ÁNNãs“Vryº³1¯YÚnb_ $Ø­‡JìÃí ømýì [ÅtàCý§/}»ìèœÁ½qeÄÖ¡šï^¿iKò6ÏaCµEÜQÓHŒ Èã9d\¡ÛwävúC°@Ýú£&è‘Ö/ãÙÅ (;<èuž…»ZW9§ÞäR‘%ëÒÕa·ÇãÛ2oBžŠáã±Å˜?Ì٠Ê_JMaIb7!ñÓ¹4æ—%ðJªÐé¬÷`úô,ð*+„kL‰­ô=z·Q…¬–ãÌZF¸«¢EpûÈ9¾q[:Š“Whêÿ‚Îs:‰i›Ža’è*H8z€IH‘ó]é£Ü%ƒsÞGéOéh¼ï¾5ïGtk#±Ì¸ ¹”“Ç*˜%Cº*ïò/Ž3î)ÖrP”§GËv0f ³Šl\‡!kEù{&ó´½¥-bÒ(ŽÍðìbOªŽñÏ?¶à†öì!2@⢉NnæjÊ'‘w’¡x))ˆéç4¢p ŸØ§Œ éâ‹ÜÞ«A°klT%[+×RX_‰ãÏÙ’pÇc´ò)î­¯X–`QÝ„U~ðæòU÷Pì§Ä“1fÛ¸yÒ«™Ã÷Gt,Z³°-— åœ;1»¥…q=MDª\z¾Äe%cÈúí?Iöâ;l«Ò|°>ïÊ­…ÇLáÚ®;dÕÉt/ê…ã±?._÷¥ "ak¡ îyjKòÓ™÷ÐElT%ºj°zâê 3ƒ¨ý‰Ä³Hn–#¿@ê4¹¹ØïWéô¬o(Û8á,‘™G}ÏaýJ3´Ý2{ë70o31Øm¤¡g–À£ÎñlÇ…šðòêKñëÜÆ;ǹùþ§QÚL‚ë×dê…Ø•$Sokaõé8f4Éw”ñpa1tØ¥á _;Éþm;~×гúSØî@š·}%÷öŒVÿ[N\¡|tbðX%²Ç:•U­g‹<^à˜îÄAÛšh>Sd‰îðËJ•¬ˆXÀÊV:rE«tØ\Ñtj*¶36Cs«%ËÛ›JcRR¸ûs2±³j#ìÌá®vÌEñAuriÂæÿÖýÿ1,žèbc\,V-H¡ú¶tÜžO¼}›™¬‡>Ù7Õ G,¢G9xFI&ãé‰3Q]@Ÿ¼ú4í¦Vcsÿ:› ÝD!…øW6tø7“½ͧÈNEŠù¯ÈϺªrû0ôI§p+ò·sŠåFЖuŒ¯Òuc§[BõàEØûEl´'æQί˜¾Ú‹{×VÓƒŽwiÒYX!¼€›­K6÷\àTÕãâ¢(þ¼o×qâSsòMô%÷QÏŒEͶ!¯ž»2ãqXÒì-Œò!¾VgŒ­CQ up{tŸ4á¢þ$X"L¯æ€oÀüùØëÖA{ÍTŒ ±`¢³Õȱ5}Ü ß+ø½@ˆ¼yÉÏnfëß•É Õ DêB[ñÉ"z®ÄT¹äÂ}T]d@Ãùï;F×ÃbŽÝŠÚ‰~û$Yõq5vƒN!üÛ®lï†Éäü &q°~ñû𰲦O©¡îý‡±è¸o(Äò’Xæ=ݘp#_°­É†-=ëÍD_m#¿qCñ*á!‰ÛçÀž<1¢/Ї€·‰ ±9ï³NSb¶ýïi©ÿ®c¾<ùë¡9ÿè“Iø:ЉUbðø±̯vEõ ïPcÅR¢&x õ}By¯Ÿ[à“Ó³‚œÒÑ"8—lõ¸ {´Ù´+Žäü #ð{ÍåG næ’áõ’v.øÉ¹”úò$ñeuØ@šÅ5<øþ œ»=MÒMÂg+ԉɮM¸*nÜj2$á†Í(™ƒfƒõðÐf·!m#»’»‡…¨¯ÆŸmÒD)I‘H.2EÕ•|ìÿ¤Ãú~ÛìÇö¤±r–ì2Ömê±´ ¨<9›DDý£ë ØÚ‹©}Y ޽1މ¯Óf—„MÀÙÛ‘üªS#OlÃÐoéYêòSÏ2¥v‰7q›W/w, {+”ˆdƒ0Ìü¸–TØ®f‹ ^a?fpš®Êèp ˜%ÎçmýV§#°ùs¤È¦ÓAô»‘yä5 <ëTØR™7X"?‹=M4"v!ÏP3Õ„÷p{,›ÿ; £ñä•Í\]@Õ¶£õ½}Ô'Uú±°†^PÜgǶüÁ0í2µz†à Þ³ùFÌ«\˜uÜkáJgöÁ¤3¢Dqv¬;¸†;ox–îm¢[ç蓨o¸âíÅTÈ->¥‡ã#SW˜iЈrŸ'CñM*dÇx®7q¯ƒ)ÎååªêRäí«,Yÿå¶À§ÇPé½Ô¨ò¾$¥Âƒþž×ê7\IÏYúÍó)ü%î? £zjøi·=9ž·3;¢åJ~(2™²zî%¬µÌ^¸þæ:öûl€ ªzX6A€½6žÉNôˆ‘ÒÑaíü<ÔZ,KlËgàçU>8}â'nÜ©6ØøÂÎUgŸQŸŒ±Q†{[BqÙµã„ß Á‡Çá‡óæÄ0! 6®¼€•»=‰ÄE[¶ô~6Ý!™ãÏ”B“Íg:@MÈdƒ•°vO¬vþ[ÿx_2Îÿ´˜>Õ>DjZ>b‹i'¶ñ-6¯[ÖoÒøË.íÄP¯(˜ä%@.=}‰B3ÈAÜ/¦ÊÃyú,ýéq&jÓ·ÊsŽoÁèßlât—q«Ïýá›9à:‹Hü+„ÅÆÂäÖGnŒQ+W§6ÛO ª‚Í ääÂŽ‡äIݰ3%ÍòÇÏ1Ë$hç¦]˜Ñ;$uQíi#0²à0>¿­É¶chi/Ž$àoc}Xt|»ïýó!A)&¬ÚIOL†Ç¿7¡§À6ü{º¦Ý‘M~>>êÃ2Fj -:ˆò"lÑ÷²6\5®ÃÅåàj\ (VË’õJhí¯ZþÍÚØR·Ý—ªÀu÷1Ìôºì®x‹VŸ²QÃ˃>NÒãÎ$†óôK4c¼6-òFÑIÏyn7áßÉø`ÍvXqÃyýjà¿E˜/—!;¾rµK±õH3$ïßÈ­Ó”¥ËT&±·ßG ¼F†È=ìëÖ 9Êûà~–Û1¢…žV ,UŠRm³¸3ÛáïÄí\†K:^ÿ8…(øˆaÖÒ¸½{WT—Ä.PÇ…®˜ °šÍQÝ@£±àò1¼Ð/ȺÈ•} x-BŸ›uö N>÷Šf²—ôŸàc®#ew§?–»:î2$&]Æ"r¾jæAÌ„—<#)vÊÜ“í;ußCrA%O‚iã'.¯¡ÚíåFLÁÑy,Y¯£Ï6ä­&Yj0+!]໕ï`aŠ2#±(¡_‰#SqîJxch‰.ÜA—©·`ÛVLâ™Õýw4nR*óï±iΣoïZ¿ÿ–-Ç®€Õœ®Hôn<‚ú{ÀSÏ–5n·‡³ÛLIA2ÈQ¸úøðÀÇš*üç Íï™7…]À;¡Ó‰ùD ,|øžïÕ–üÓ`¤<Ž]•I¾·^B×%"$ÚOŠýV/EŸö$u~WåùÆ4dSïÍ8ÖO޳XàÂËTû‹ÏëÁx¡I¸+ÆÔfÈ2×&AòŠg›V›ƒç먛dLxšŽ‚Ù?`’n?¦÷~µÆDº´×æfŠãÖÞi,(Îù‰Þ‚ç* °ðø”î´«Íôµ¤'þðô¥ÿ6+²ÞÐ7E |ÎÁǤ l›V#*(¿â4oYÀ;U!þ¹ Ãøö¢  [VÐ1·…HãàkNÄ<‹L½jE\(€mŸ¦>ÏÂù¿ ˜áqkN/}=.—=K…Dÿð½Î°Ž+ßðÆé¼vó.Ü¥kÁ•YoEY•Üm‘]œ˜ý:Ö, ¤±yç´ÑçSÉ5xðñ×€3— *È=ã%c@“¤ÁÍl:…c^º‚í­%ˆcqnÑ î|Õªo²é‡W¥ØªMØAå4 Òô³ÍW°ûª<\V#vUIÑ`Þ}Ç©÷Ü´? ·wrøÉ¹ ´ª!§ÛtFZøb¼Ïé±FA ’÷F3ž¬³'w*9|ºgˆ;:eJWþE1·Q^Ô9• J7Á/ËYDüùÐ/2!í‡åXæý*Ø^²[ôº1ú–'ÑzÈ~1ý›š…'ÌH“í|œ§dIÞJÂgÜ‚•“²)ÿ½$[pÀ“;õx4«æø0SžÂzV¦ò<ã6½tø¥Í‡š¥}èw&ª>¡“a¸uY ¬–É„J»pîT4³S‹Í £)§xdÊø$–‹âê¤ýãK\ôi7lyXJ?mòçÿ繜¼C–dtHb³,_Ë™õK±Â3 PIß_ê†vs!螘~¿ƒ]sà”¼"ûš‚Þö»@B£ÍõQ¥é&à»æ®ýÃw}Ý ÁÞàc1ïO}LÅíF±Ã3ø‘Íb°p›™ŸXÎ WÝÃI‹Éãeæ¬5\Ãù~ÀVe¢Ýµm0¶•¡pª$qöR!ÊZCëAoNšL†Iaº tö×ÂíŽDh4& ÿ:¡Îì0,NÐ%©Kg!Œ†ªiø`0dÞÄÂïåtûõ%tØù*çÏ5.jÈK9O]"@ÆÜ#¬lðlùBÎj‹+ypOŠØqÂçãtï¼Ëüïx{>½F‰é;é­¸ÜTüÖÖ"¸ pu°|Ω„ñç~”D[S?hšþ ~ãÆCèUZÄôÖ‚Ka"Þ“Sær6¼à~~šÀ-³(‚ºÁ )ÁI©m|‘ˆ`Ðì d&ýE'Oþö¹aT·x1þÙ¾ˆ.ÞeÉÙãvØÝ•  ¹è®4ºîÀf:8PŸWÚý)¼§;‚PÓüíäBGqÿU6©š,‹ß*öB‡ªoŽ,~µÈ„CÒDæ¬:Î3ì‚nx%¹_÷Þæv¬zçì£wI4Ž®Ç?ýk˜~ª™Î†.÷RùŒsôÀϨÛ3 S?7cÎäПõ´Ž…Àô–³";Ô ïâ–¯‘Pw •ÕˆBw¾…¹ævúÝ~ò—}ÃâÓ›ánëØgHPÆÙ¸Xu€¼KÎ<>†ð>eò’î€ò]KðàÍ ü Óf‘¤ŸñÔª}/œ½dËËT¾ŠÚKLàVM5:9Þƒš Xæ%ÃR/rš×»w¶6˜_‚‰¿üèµ-|nDh˜NH.¢•ÊœöÆÏuµÆ¬u×s˜±y/7ßä*d7«C`¦<Z1€Ž“Øô¥gHu~×>Í´Íå÷lF”Ý%1¢Lž‰Bz”·Ö‘ãÜ .[k?Y´ë;Õà×΋ s.ɲõ9`›y€¼S°B»!\:÷;o¢ºØ"9üÛt Ó“n`ŸÈ7˜ü¢D÷¨{õ|Fz1° ÷Ì™ˆ'së8í©Ñh)WŸ6­‚®^HJt§B£?h3–ro~ÅÁÀáJ4K/¤77?'Hê“–‚¾Ì:Wcǜ֘³eŽŸù±y\ÌŸõÌëî)6^ÿ™ë fwÓ ÅlìÜãXœM'•üÀå«*0y¾«¶u`6s5™SØÀEe¦;™s´DnÖ~|ùKU-‹íV¼ÚK”/ZïÓi.<Ü ÷îäy¤h•'ÂXR¾Çṫ!,o¢#2étÜj'O$eli”¹,yÞ$Ó¼‰ë–¹lÏ'ŽÅ÷ƒw}çW«æ7øî—'2I~dÑŸ&øû$¼£"Øï·y8eö=Ÿú îÕ³QL,—ÔŽŒm4¥—L§ÂÅœ±Ä[a6i.­á3«4Øo’›“ñÊŒUPÿu»xÈŸwòC‰ÙèCý`Ë” 8óD•=žÔˆùÒ9h¨uœh‹l¢”MXRùa¶gÎwô“啪{Pߣ‚äÔÊk¨9ׂhÌ##¡1hLÕà\mÌz5„ÛæÍä”ňÝÙcÌÁW’FñgS :?\ÿy>ta&þGY{8‹ÚŽ˜ÿÆš{<d¦ó#>Êb[£(3¹ê†Ó/¬'+u ÷Ä´‰ž×—'pérvd¢† [ØÎÈÎë|×5kYmWÌ$Š­wpŠùd;zF^¹³p½a¸rn µÍÍ$OuAî¶2<±Ô˜ÍÂkJÔY4ëõœè°âx~ÆQ&MRçi’ê…òÄhè þ¸· ÍÞñtîW¢\‚7»[×ŶþAÏÅìdg4–¢k³*™f£Å¾¯Ú ^o/PÒpƒN“bê2lÃørúwŒìüXÄ%Ï__Æ`»e+?”Š5yTXú=gåò.Þtã8#G´QòÇ SsÙÆ§ç¹ÇwñÒWWJ/é`ýy'Þª2dVT9) zH#ÍY½Z˜Y³ÿ<¢¿‡„=íX^_Ý^–?‰þ§n–rÆ“Åo}¯s¯¢ºSy­ˆ' ™e°!%Z°ÅÝJœØ»B ;6ˆ{÷€ë–ƒT<ò×O/â‚õRä–º-2=Ç·üÕ:ŸuðT0Õwvì¶…—Ï6pï#ñQ ÿÔárÎêõd޾«€7î­ôæÀwúy¬ bÒì“ÙE_8¼…·ŽÜ¯m<ï¤K°dã…ÍШ)FÆÍwe\^óT>N&I¦›ðj “ºÚ`¹šÀÌ¥'±Xúh}Á?ÎÞ d,È\ûǰ˲tü³Ÿ)‰ 3ÏáhÞtrØr¸ã>2šà½P´Ôâ'¤è“ç;¿s ÕÛ™â9ÞóOÒt J¿„/Qɰ¦µ µzvrÕÔA|«®À¤Í€5e<…|JÚþ>§'&¢ê£_èðÙ;t»ŸZú³ÎÂÓØsBrk-ù±7$ñþÚXܨŒÖRs`Ì›ÉDT"Œç<Œé·_rSï†î%cY»KìH<[/,")šŸQõe ¼–‰Ÿ°é¬÷à\¬ŒŒ‡;o8Ê·ùŠÕ*ø<î9<~Å=x Ȉ÷ ž‡Äb,(foƒËxzvŸè]÷nh¿¾’|)šC}¼C¸µ2"Ä!û$¼v7àiìxƒ7.ˆ“¹sÐðt)ÌWe½Aq‚Œ3ŠÙŸ]‰Ì;Á’5y^#[•Øß@-¶ò0÷[R‰AcÝ¢ÅήC‡&êC¡ÀqX¶ 66ùQ?fì) E…{Ø!¾!¹-ØÅ};n‹RkPËi7ñé_¨Ùÿ‚W¼‡¾“‰£ˆ;ÕŸ¥µú!¬˜1ùÏû9éú¾dèEøó%ת€Ê˜m¸aúAœ·n§XY…ü«³÷"öz|Ç ÷Âð|~.Z?YUe6T¹Mrº!-ݰ¿F¸oE%Vµ'×ÿš° ½o^ÀÕóÎây·¨“‚ .‘0þüzZ}i7„_W%E^Í”›ãÄÞ»þ§‡¦m­Ý0î5uîÙ‰òWyäXGž)&ÄÃ…?Fh.ɽíá‘¥‰ft]k–P$Ê 0ç‘{>©…£ëÈÚáÔñF?5ô?™®¯¹©7ƒÛ£…D8¡çм ¶4¨ªLø û*œ9©%MØw윅rFk°k%Ë8FãDDZ{Ò«!°0 —ªíãÊ…'sùÎÐõ¼Oï«Bzy;°Œ nÀ™þŤ"0ƒ^Ús Ë…ÒÁÒ >$¤¢—f2ˆÏ]ÎSao1ö3«Ù›Ñ¼äCÖQVòIŸýé[Å{þ¬‰þìåsQ… O¾˜Ëþr€Iæé²…ûNð«Z빈µ6\£Zw½² nfO~DàžÓëÙŽ?¢¶cÞk2cîà¬Ë¼cH|¡ñ7ÜÎͼ¼‹Æ!;eT˜Ý«ÇÜÄ3©üŸ*+QÓã7bq”Sê…º(ü¸é:/ö±ÑÏçÿç3]º­ç}¾‡W×rþžÅ7Þ¹pÕá ”÷e£ŽÑöd‘Š…~Á[»¶1cGbú|i|yLŸ¦`ObHŽøÓ_÷pÖ—Dˆr£=ìU:_‡Úý™¸UÕýઢ8iÊSÃÚÊ\5nwOé¢×a!›v Ê®x‚¨*:=ý2µR f±©F$jÉ.6á® jå…ÐâU$\òìL™5ÓùÑ Âe‘üCeÕè÷—°ƒ—žá’mwñ‚”ye Ì VH²ˆ—D!à6'TÿztîÆo2[ ŠnggÝÈ5qLq_!æÄ “¿Z ¸>ƒ·5s¡—]ð•¡$;DØÜ÷Š ÒiÆZ j‰„“&ë/ÂèÙbxéÄ+˜Yú†õæÓϧ€ï;.»hF"y¸Uæ Wy]_y “¡[AムéÛƒT¾Ëe÷ªóéÙë¸'79îØoYÔ.2D½kÅ6&»åmpëæK˜võ%Öô:sú‚ìØt;*ø<§ê’%¿‚ ¤` [àÙNŸªZq/ƒðÜÜëXaŸÃiïR"‡Ò[aú²ëx#[’Ü5"ìc1|¬‘`=ë(7­ç_ C¿Éa¼òˆ°{·”]vàzÂ/?#·õ$ÞpnÇÁN'"<0Ú[ëžbÌk&§¬û̪0¸Ò˜Ï‡çbºƒ3Æ/?CW,ˆà—×`\÷?ðÆC8¥`ÖÐîˆÉ*ôªÎæò³ÅØ®relòªFïú¤þOž}9‹XØwAÆç7œã÷Wj‘>L|ûP›ÞùàÚOít Z—³žš#/úðpL8ôî5õ³¹‡B; ¸èg{ÀHL¸›šÌ>V—(®¢8à‰Ò/Ý·ÍÃñtLìÒ>ËMŸÑt*D<­µ&¬qªÊ?=ËÙX„0ßÍI«êÆf´O!ž+`ó‘XªúvÊ[»„»W¡©L‚í^«FDšì Ì, …¯eÒ%;泟ádÓqb,üÞ›cìyróð)ˆJøKï+”ÀÂW<ì+Àt½2På÷0'Ad0d{Â(Nè[tgͿϟÌúáÀòA¼0¦sÃÑjû|îÞȘg'JbîæÃw…p/mùjv¢Ž}ä~fËÓ¯Ý!Øö6gÜÃw"vle‹aG¸›õX²õ3DìEX¶;/¤Òp(³­+Œ˜Ó˜anš .›9^œð¾õaåš ¸§ÌÏ9ì°ë;|æ7a+£þpGŽDÁÉò%¤|û9Úº¶ VíƒU{&“⹄u©Û“ˆ£\vÓ7ó­Ãœ°ïèµ*Ëo-Bc§~u ç]-ùkÃÁyŠ Ür-†ŒI#ȹ‹°qî£ë_`%·‰I›kðÌÅXʽǠ5G b¯­&žàÉæ(rÑû4»×½˜”Œ{E–løÄùÅRf‡c‹Û5üñWм™I¶„rÛoÚ‘ËßʹšÅÝàdÝ7ƒÿ{"ºÈ·h'æ&áBj©(ùô°œþçí*kÎôx¯Ø?“(êYD>ÚôÑüÈÛ§mpçá­uøÙ<ƒn$DXP‡8®Ï,Sb·ÌNÃm3a¨R¼Š$Ö»d2y]xŒÛ)!Â.|õÄ|—%¬pa«p¾AŽò×]u°Íî<÷Ÿ–[áK wµR‡Œµ’²ÿíp— žKekãÒÁÏA4N™ÌÔnÕb4;Ž»ä¼P#z¤^‘嬾¤‚sÅ#ÎScCX¶× Òß‹…«¾b×W òurY£Âª¬ÈœÁ9à(ŸN<Í7%¿5ÙõyøÎl6ñ !æeì]p ™²F…XlA¯Ñ Ä›\0]Ã9êºùû’IÛç‘«»'±÷{¶1åûÓ‰ÛvU–c³Ogb%W˜³RçÿuÚcSjÙY«UÀϺʶ©¯bkR@rïLxyÞÜ’W íAœ<±@ó½h"w›zh[1žx†$"¬–&+“ÜÉäË»é‘CCtOŒ ÙÖí… ìa•\ì—€ÂѺÜå·ÐNÜ£ŸóÁÆ,’û½¾„m_ks62Á%WᎭ»¼2¿ï—#M{õ¸_ÆeÔ«b6n³æ“ÞY‚Ð7YÓYwP-»MOçȲÍJXž%ÈêÏÄñ¸g–äLe3jâIšÎmûÇ9U\âz­|ÁiV$¬¿]Žïl­˜¯£dÖ7à·S!nê+Œ{Š=ßW²“¿oA$=©âV_U†ŠÓªYÁo>щ7õá:—†“g²â5L5û .לM[¬™ßõDØ)Jœ€5:ì/_„á×:,½Gƾ­îümÊ'Ñ2m§rì5ÌÝî@‰ZQ¿y¦PQ?“»Woã=Oàºeý˜% ?K’x?7-¤?Çœý•I@X OXL\f\æMn½ÃýŠä'ÝÂC7Qëq-–Äî¿Ç¹p·í¸`ÙàyÂÑ8YöPf”hfCìçh`e× ñ’*{;}#çUJXÏBnáÕP¢û\‰E¬Ðf K9rlÞEJ¦úâäÃÙ¥¤XäµÚ}Öæ7ò¦,jm=1¢–²”†'¿ã¾- e¹-¢ö/†;ù«ù1¬ÁlzòYÀ4Ôþróñ~ïX2J‚HL±zÎqA‹5™lñÙ`òµWˆ¹ñ"ÈÄ'È6§•hû!‰:pdL"óE!ÌID’™irÉ<û®*ÏÌÇíÃv¶»PŠØÎ/'~#®,ú£ Ô>\Bveï#ÿìÉœ6#*c.BëIä_-"pňô 'à¡·ñ~Vþ:ÐGCO±þœèÑȦdqÝ ò¡Nžœß!À”o_ë(ú\›D¾x¨Ñ/LÙw»¬Ñ"¯¯h²Mvè†9É7é+|}1ƒ^x>š{èL¢¡~æio#g÷Ç1Yý æðùhõoçGcˆâIR,1Lð$¬çÛ³Ë?_S1OÔû–Kþ'9N¢Î××dî¯ ìŽ^$—·ÿžx+Gºó qza8Ú;¾‡™ÃXç\SVzÄ”„ðK˜½NºMÝK²v¯b«“çÁÎÌ `W /¬ QÕ´mº[p¤j=Ñpv,nCa1¾àKЙýSoÁm‹ÀáÃþ@ô|¼ÞÞ€QÛŸ Š“ ]zu”‹ÖѼ¥¤e$ƒd¬MáæÙÀKí¹ìï8&—ö›_{þz^?:¦am¶2Ù+/BÎìýGUÍ+éEÏÑ™÷É|ZýW+$ÃÒ½o@q¼ñOZÆÖšÄqÇÍuÑä 6}$-v˜¢f ûGkúÔt Vùwgç3DËSôì‰Â—3¦‚yâ$6P›)r•à»ø:x¬ì®Ô‘Tê?„KÀxdô™N(ƒŠ¡WÀž— מ c×ÇäP¶ap¥Ns°G(åëÖÒÃrã6¯¬ ’!ðÌ!¡Þ,óV[å2óº;°cÛVŸ&¬Ç/~­ø¤;‡Ýp_sU]žE$¢IðúÕLß3|Ôq ý ß»â¬Mqg ^úù€ *Ó%–ÛŒ±tó-^½®>:W¾„¬šÑùó¶l³ä¢Ȟ¶3Ti¤˜yw‚q‚$×á3\NÓmHP¬5Ù`8že}ÏÃæKItÿ„¶ßƒõÞÕ{ü³€çC3`kpLÞàÏ4;7‚ÞáDVpU†uj?ã‡ý½…ÆÍ"¬a…ô”Z¢[AIoƒo7ÃÂi×ð†ü ûe…r‹@„ØþKNt›¤ÛŸs†¬ôàJ››³4ž,‘Ç/£\&lb1Íã½¢«^Y’Y‡p "ÃêÊ𞹤dG)º¼‚‹¿8Ý)'!Ôm5n?–[È_ß&Fo½°{Þ=l:ŽB‘‘üë"™XÆL»GeÆI¡ g*Æ¿­®Þ€iÛIs¼„©@³æc­î5[´™÷ªý íøº nˆAúcT®OéöXXýN“$ÿB.x‡¿yÛPõ÷æ,¾ØÐ•?.€úÆèWo‰7¾àÔóaÜ“×ËxAÃd¯I8W±ìîóá Å¡½¡×üo˜µð+*Ôªg¤ÊT—Qˆ8ÓB-Ëï¢÷Óë`[lÆœîªÂzñAXtáºVf/ýï£MJ7z¤Ös[*ßrj·Û¸+HÖÉ%dÏ{+¦¶ÌG–úŽöÑ@¸P•&y¥øxç%(­ªFÁtWf ×^_@pÆÝCÐþ¹wÀ9:Í%›>³e+ >P—·lb]8¦³íâ$ŠåR,;Á|ùY4ÏË€šé笒ÍîMˆÁê“Ө̱‚æA’‚¥Ú'…Ã|ؤíZô¼S ñ½Œ6ãøHámÁÝg‡øÍÍ—¨õþNÜð Tøx,xfy—íT¾o|!Ú.´‡/+䨩Ü0ú(S–f.…i×÷ƒøùàgÙŠ§®fbñÁȸ³Fàýpÿ£¼Þ¸Ž§OP½>“9àÆÒv9’ÃU4øiA¹x6·NãÆ“9™ûR¨¡¤ˆ7Êíà³eûï»zЧîê¢nÏô4ëDE¹v\gvíDDIþþi´Þo,îœù÷µVÎ7Þ#}m/ˆ]•+Ü]:Z•#÷‹–$xÓÉr=ƒoÍë!wÕÕ®Ý(Me‡]`gO…{eÞ¸s¿6U/ZŒõEé×áéè?ÿ>ì-U§ÖASù{JÇ‹Ð%3°ÈÊóŠ;ñFÒs …jì¦Ì8|}l.œ4‹÷µž`^cf£¹Ÿ¸µæÊP'b6õþ¬U§¦g†²Éî q™ž_°ÔÜòBÉègdÿV,XUw÷cxö¢=è<±c_^–¡TÅ-ḳ‚¿ck[ºPûuÖΖ_1uêu(©| ¹‡æÁìuK¸ñ¶0æÖˆë=|Pz£ ª›ÕPõ9ü÷‰<–7¥ÓÖ A7×&†Dãz§UüwùU¦£ñƒœtqÕÈsÌR<Î)Rc- ­šŠý6¿ðU¬†FY=e^ût† ú Q;9,È«Ãô yÐwÿîÑYÁ_î_*∓CÒX¼¨~—ØñgÚÑü˜i&“Óç éNß¶³ó•]°²Ž g»'®É˯üù-ÞøÇŸíោâe‡qÜÎñ/²,ÿÎ76F“Ûeõ¤$%n-Æ>’w;†,°2!vÊ"<àx1U+("æxCÅÍl4?W¸ö°”Ai+\·­k˜FÇ 8='5:Üp\Ú¨š7bèbÞ‰ãŠ{˜ÃUš“9ŠV¹OÛ%éƒÛðç§*¬>³‡f×;P‡cSpžÍMà%-,¹î08nDÞûIŒ{ÙÝkµPµÇŸöoS8ÔôÁMŸ“äo4aóg­c_‘†½BØ–6$Ddzݫ’¡ù|~Ú…—rÑKÒpº¹Q5N*SÉÍUShsòB®_mG¹ÊRf-JoݸÀªý&Ààg¯p½k êÌS¤òyb°ÙŸlùòšL¸ºÐ«ËÉU§xüíMH|ô’èT‚¼Ûg˜T‹{J»ÈÇÍÌMùТ 8ùŽJí®d=(Ù«L_)ÜemsJÁ{*º\6Î3×õ­«g+8"f ßlÃ9qÂø£Óxì¹ñøìÕE†ËãN8_wcî‰ùXçw4Ev±qsq£¤:7 ÑäïFïºÍÉN[tÑûhPtußn¢Ð,‹~{V“û¹BÌûTF¿è:àŒyX׿”‘|º×}|(1oöeàf¾ëÿÎ3uÇY2þÛe˜Z6Åç•àvSaÂäsÂë¡I€8Äo.ÀSDà`¯*•Q’ä¡éŸHžT»?b4õt’dö®’ðÈq*·˜àŒ2¯d1äÈAvsüU”s_ê÷†ðXå'äym*Ùòø5løÃp«‰“¿¾‘ÕØ¶ÁvÝ8‰ “aôÃdT³l æ„áÙϳ±#nß÷]7ˆ ‡^ªÀ s"ôØíå÷*†Ð§“Ñõ(áε2<äG[îœ å}$ðš ìl°ÅA Téù»uèùú (ü9ÍÞž‚¿ó€Ï᣸ñüRå´’o¸ãŠeÂÉÌM÷1¯e)¤¨•`ô©%lXæhhö„šP5ðQ`?z$&T¹áÜó^(l— ¯\5<ú$<΀K_þ é_Þ—ðdžj¦ó¯ÁŸú0Vì·OÇsüJšQOZXès„ªÓlƒR/žÝàÃ3î΢³ë5ñ¬Ã ¶ìbÔ4×Uâüw×lúiÛ98c7Œ¿%›~'øST Û¯*Ò-ÓŽ ??ÆòKø»²~’ž]"8±œ—þ·šõ _N)@XöX0[¸‹§_BÿÁ޶ ÐQ~†‚±q{²`unQÔÀ'sEÉc©(˜{î*¶í?™Pî;Wžgåžkm žàHìu•¸rS$¬»‰ëÆ‹@uç=hwFO;ÈòŽçJ*ÂÁ`{ê(ÃÈÖ $§.Š>zäK5¼¿À¸³“ØÇ û˜Ûì 8áæhúÆÏLΑm®eŽß4o²œ¨ï8gW½ð}(,?óÞñµÆVÔ'ÿJÄî8ðU3Nr0B ÉmX¿]…ÿX·Œ'šÁÒ‡ÈÞ©±¸ÄI–îhrž–NÚO‹;ÊM#'Ž­oÚ áSðÚå&烿°Û Œôê¼c³-yrîùô—l}îW‰[`ŽK)lU⣞¡oÚ^Þ_{ Ã&n¢"!”Õ› -¯´ð¡½2®}x.& aäšìr5dbÁ #9íØ.Å{; pÑÎ&ÔNP¡Ê'a^Z~S[HÅPVti>7æÇOµ1[A?ñ6(C¹ '´êy‚BÏ`b¸’Žh<«¾ßq¿„é¿oCn혲/˜dô„6ÏñîêŸP¢ž<*­Ä?êØûxèš<EB»àg±÷H“¥†-¦|ƒà91‹pa޲υ;O€ÆÓ%è/ƒj:·ñu†*ý´ê8|ªßÍoä½Å”° úô½yf÷ލ¥ eFŸkCPÏß+ƒò…VÊpý‘Ÿp]|ºûdà·Õ)¸ÝcÄ;g ì×ÙBÆ™éÓÙi[à†±w¬¾kÇAãøëDc×ZV¾`¬¶yí,ù1 ë㟛R0Èæäu¨™¬6– DÏÃþc§¡K®‰L úŒ­?žÀ±s†Ôç½—°<‰AÁ#¸ACyÞ«ONIJ?i\õP¤Å³Gæ]°Ç5G`¸~0yõò!x(S‹WbÒz@å†òÒwÓàI1Ù¸ÿm䎚IJv çWˆód~ðvµ %·ëmgɲÖT¨ÞË*î°×qì3– µ×ºÁã• |ýÙ¢Tí©$^±.‚uþX@Ôpl—Ÿàõ¾ß$ùq0I¤ HÍÚ2y4ì¿6"«› sÞ*ƒ¯õ ”7f‘p¶üÍ-Ƚ›ÅêÔŠ’Þbsñ]¯9kÂq‘ ¥*ͨ»¼!˜Ñ…Ì—ˆîN}ÖvËö»;³˜l ,Ž¢LŒ)>°þƒ‘{ò øG) “^Ä<³…ìÝÅûÄív#ì^ÊÈ9½ÉøøÑ+Üýl=ìÞwååíÀ —»ˆûÜë0³í&äÚ(;%Ú¯SPú†¯¿ÅGùgÈù¶-p«6‰ý±&Ǿ®é“NÆ:K¢V“[ Ê<¶oǘ#½ÎCª403[u-à‰Œö+û=ô­Ïú,ÒàWI4>ú”3î?ƒ{Ë0cÌ7&i«M:¥»®Ïƒg›Î1¥µÆÜÄå.Ú˜ÈR”Và4‡}à);—ú^„GC®’˜#¾N 6ö4½}#êZE`§ìv{`.÷°ÂíotPnZ«.R¢o_W‘U%ª\L-ˆõLO@1…dóÜi¬ýV ›`BVܶ¥xï-¹°$b‡Ž¥ ªâ¼ÀÒ€ª}ÊधÏFÂ[Éw0þñ#”¼8~º¾d½…¸½)•õX½ÆÅWd¸ÒõAÔœš°RK ,ûïùëVÜ8 §,_ÄošuᩜŸ¬Òg5Ùö`ŒùIxUÙ‚©ÎÅð|ÛïÚ½×`õí=Xò°š®¬&í{œ¨é ]*¿Ë‘NBæKrØ#¿ýip"6‡}P½á,>s”!K6Ó[úa¨52jÀßi÷á«›:ͪJºýñòà›©6 êÈL{«#<à‚ƒ;4ðV§/ÿqÑÈ—O‡?F“²”J@/1¾0±›m:;Ìk²±ZEŒô>‘¼‰ó‚Èíû1Þj1jêƒÂO7°7øC^ŸÃv·ˆ”zŽ«wãÆ?ÞäÆÖaÔt~›`N½ËÂNÓxø±æ6ªØ>!R}ãpOd'ùéô€Ifͯô&¸Ã2ÙO°èhÎXŠ[Ÿ‡Ã‰ÝÒTe9¢ã£Q|Á§}8·=NxËC6Ê6bÕýc ûòŸU¤¹ÂW‰ï"ôvÊ]gQ-i|û´A(º3o¶E¿— 6´’|tž Ô†Ñ]• Ès Üß,Ê ¢ÓÀá!’Uõðb‹LümÆæ^ÃãF÷P|Ìo|s7‡.‹‡u7ÓÁ‡·aøøL¯ù%и²Í‹ŽãŸ¦Fœª·‘ žhMjÒ ;N¡ñ§ˆ.Ü¢ržÖÖMWÄêÔDlô7X»;—.&úAºÔeéüäj„ù¾ôiŸ<Œölâw*ݨ\»3 U…-f-8rçcò™¼È–¡·$!ß¶o«d/´ƒsLÔošN±þ:g‚¿}…ã—xÃË!Ôø’=íþš€†zq«,ÅÍcøîþ³d^§vÙ)ó·›¹ú-Kú¬µži5 ÅÍ‹(£·ýŽóô¶Ò%·/Ê÷t_¬'nØóˇDh³ë a†ünÚKž5¤m§wÃ$Ö Íüð™hœé…ׯºTfH,l¿ÀëLeàää‰püÝ{<–;¦`<Ç7²cèRCÊ^Õ'‘µÁ]¨ký˜•ôN„Ÿz•X˜y.þtƾ›2·Ãà%娵)wgÍÃóê?ðY÷%Üpò¬M†éK‹P{æ[|ðnž[ÛA‰Ía‚/ÚAì®:Í= Ï£ÚÀoÐn2gÎ|Òߣán~*Œ~iëÆoAgÓ¸“ÊÉó? Üók£àÒ:G:_a1,ûO¼7*ñ{n§ðëÃè7Ì‚¤ÆÃ@P»“Š¡?õòÛžP ê¿àYa8ŒJÓ`&-ðþµ§Z doѹ/•ÍT vÁ¡¬0è7‹9'ùŒ¿œÃš_QT¡—ö€8 œb6>·ö°£÷É‹­É z_ƒ÷µuƒÄõ[lîT–nÃÀg×:áìª.>wu´ÂãæLw^ôe¼ÂÜ÷ ÐN c}ïÁ¼;ún<ÚŒa;ªábL,f†<§}„Ûi£Oh;ÚÇA)3¦*üÉ-¦D3V–aTÛVô9–nM`O¾”Âéžx>ý*zÑžy Ú} ‰W8ä}=†«LÅè©§K° 7ùâOýF`êSNäŸþøó(xþJÌàñ Yî±;ÖY̧Ÿ4ÌMƒò}ü²Ùm ‡_¢¹Æ2üŸÙ(È×U¬.©9ŠÖéŸÙ^gKZêeÇKÝaЮ£Túåb´ Ù8œ…ò‡] ­Çþ,ƒüy±TèuÇK΢tì˜U©ýùhÎÁôS%Ô-ÔbÎ,Ç–~YȜ݂ÉqtíÆÛ¸mm ¯Ýu ¾ÉÙÓ¯£ìéÜÕaÌ”Aàøq<gò Îo9€.Fâ”]8‡nlç×&ëÀ­•~°¤ä»S& 4éϼXÆ"õiMçBþàg(·ÐÞÅ“fTÃߘqTý'……ûŽûè7(¹`2=¿žËE”2Óp~g¹Üí‘"O+“xÛ„p²~|¿0Ø¢­>¯bÞÇàí~SrëÌ4n8³gô@µ‚:ïh ҀɎltÑ ¼pR”~n%ß29ÉÑ E„"aÖ%ºV?‰×½È¶&¦Âºy¯Y!›#lpþÇ‚úâX(ÈfÁèá Ÿ„¢):áÃ58±ä¤vB*ñ­ ïYp¶4†’Ôòg8ß^‘˯ÒAîÆ7¸,<Ÿ¬éõá‹Cè-¯˜>ý[ÚPDTš)‹RïWudf[2ºÛ)£Ot nÔúD¤ú ÅÛ¶²œõGð–m»¡6ŽJ«¶bB˜3÷rÉAÝÛHÚL3>ïAÓéÅ|Jæm4ÏBqo;Ž]:‚›»™–WcDÁVžì­Ìÿ¶Õj.ŸÌqL‚óó89>yã:r¢É†;È ÀÛÈðÌÙŽ—W¯çU> *zoõ¿Dý}_°Ã¼ävDÃ¥l|å`üNI[ª-µœû–-‡pØÎÀîmˆ¿×ƒa[Pru¼lQçgsU¡Ë©ˆP•j²qý ~=†Ê}!ØÛö®¼ÝÝÃfaÁ=hÆöð•ð¶Iœ;¿€wÆuø ï äåÐMþ[ÉÈôh.p ¤Êõ¹¢Ã"îõ«ŸG7¾‚¥óìÙˆ[²\?ßMf/£«ü­è¹Yå-Žçw:g„1ž¾ÃQý[)¦çgãÊ@ÚQ¯Â]›nA©AXï>ßG÷1/÷‹Ø°ãyûËÙ¥ç{ô.Ü´V¥Kâoß:ÁM¨O¯3Ÿzy£ð™D>ÆûÂÂ]ÔcBäÝ.¬÷>IöÔ/¥rÕ„~™X/(MV§Ó[öÂÏÕNèu(š<.³ó‡FÒ¡o+á¼Ï6|÷ÙÆžÑãÓ3NüÔvtÌ*¦‰ÍÁ¨ÝLMiáã5q«ª¢`pí5ôÀÀ˜jnžSAû­> ÖoRÆü'Cà©‹,uþ :°˜\‡£:’øZ6(HêðüF/ë”Fç%;?Âü1å¨:aUÝ…w 2 yº_uæ0—>aMë· £þ®[ØIúJf½±.º¥ìiêÚ\8z*ËÜmÀ$„Ð^] óy ¯ÁDösE9±XgJeAþÖhûÓ„côÑc_©iI¥Å/¬é«‘éx`áQvh— ¯o65àþ`¸–ûR.Ìäšâ0TO±vmJ42~#x þÈ0z ‹hÆlE\n ycbð¬sšüIë߉6êxíö(ºŸ¤Ö®Ò>…®ï•ø÷†Ïÿ‡¿½C$VOcÞ\PgÇYf6†îz†ƒÔ©è÷뺾«yÆ›|ñRè]!ÇsGm"?Þ˜Ò4‡÷d_ÀPZºs:”œ±7#™õryþãq!~MÓ¢:–ßðKM¶Pïý Ø8'Ž‹ÇËuËÑZd§ £7‰-ùýéNôq»1iU§íEмòɨì߸SA îm;",ùôEhBmaa"Kœs$VØ¢².5–6ã©1ÇPÊq½{}íööytÁbqjÿÜ Ö¾â4®š`Aó]Å/# Wâ=ùü\ußü†3¶7ð“\æn‡MµÁè… Ïÿ@š®ñî,Œ~ÒŒä}.Vüå×›[7¡ÈØE0ó„,¹ôŸ¬DÝø;`¿’‘9íñ„Ž¿­" 9»EhÊi²Cö2SÞUIì:…`^)IÚ·ìÇgKEpýÒ«ðÎê x ò'w„õ@áºÞ\°>e†-mà8 9£#DY_bQ¥D°gIà4g”q¸ÇZµœù!üªÍe¬]Hu62ï7Rü÷fJ†ÚZs¥Ø»„Ëô‚xð9>ÇñºÐãŽÍÉo†ÁpÝ:MÛ ï$µ0ðï|cÜ€hÏ L›+ÎË¿¯}²n0-ÔðSSŸá›ikáï1Á|,Ú%O«+ÓQþWâÝ÷pb¤25ÍVnÍGŸÅ„(ø¾m!ÞÝröŽÝBLgØV1º|Ê$ØØïÂÞ{ŸÁ­Áº´Uü$üP¦Sïá N}U@ÿdªÆJbj’]U± ~¸dA±U>9ÅÌ0Å!~[°yëñd šÎÇqV,tÉ#(Ö‡{ãBú¬·†”–+Ð¥õÂ¥†MLâ}Ž_FcsþÆìð±’ãÙÎi|¼ÒCf{Ë’ÿQüÁþŠ"·B9Î7mƒ#Æà"-–ŠÜ.U…º—øÓõ›`ïrwÂo»ž…+¿WQuâ Ú»'q‹ã}gñ+®«ñï½—ÑÍÈå'„€„I5öz¨R+3'º0å5ÄÚà–¿\â³Ýg÷\cþ&LÇÚKÐhm9,¶Z'ܤÈÜÈT\êBiôÔYàw°„¤LÚâl6u6Æüò„4“Ô>`Ê+=p6L-€ Ë–Ôï¥>8Œ^Å‚Äù1µ!'­¸+JáÇ«ØÁ;RxvÂG¢ðxëÒk»…ÏIæÊb97‹ô±L+®sq5~° Y˦ÐÕ·w㯣©ìÕ\œ®3˜;Î%ÆvAá°þûÐDž°?ˆ+8ŒÄ–šÁ|¹`]ù[—ÇÖ‰pßmë=¯[Éôc¾òãT,ŽÃoüúy5¹ÿ5Wúf™.Lû3¾‹þbçúËØMiÈÉ_ÿax}# ¤v;ðywÔHšvÞö·äR´häÈKèb#ÏEW˜a¨äqR0l ùÿ_þËKÓìÝÁ$^G™ú¾@ÛNNq>ÌŽë’àq®‘”;žNÈ[Á[-ãþ+CQÙ$]hû-ˆ ΙÃ+¢Jm… j÷Uxi÷~í8Ë÷mÅE†“ø¥ËéFßûh,ú7ÝÌš­¸Ê"Tºâ¬sŸQêöLø1‘oAj{†\2¦—GñV»oÎîGŽÒ¿ã…7ö›óëùú#Sh¸ÙÈžÞ.¬¾>Ÿz>Ò¢O#¶ ·¬]µ·›÷dBÈ“À«$áuðÅÓcLS7WÇã½d5zÜ5˜z+µ@_bð)à‰òüø5üø_<(¹ŸÎÜãhä>U2=ã²ðf·,(¸æc¨˜õ¼z¿´øBÃs8Aª‰|:…å…g`ºë[ˆ$8¬q.½>‹y*Ýa~+óqÂiI>lQ%Ì,Üaø Ÿ„7àtóXÒ_ôƒÕ€¼’ߎ}Ž©8ÁjJåsë ×qu¶Yy(ôl%Ið½Eü@¦¯©ÀÕêöÐW½Ú‚mϱ<±ƒ‹}©aìjñå'™+’ý}X²Ø6ü*Ñ5ï»Àó¶muòsÓïÖ¾/‰˜ÒOßt¾Ë/À*éh £B§ýÍ?¦jîpôóaöÊšÏ3e¼Îf,¯ÅkïÚ„kÆæ3F÷¡æ² ¼òõ.,‚;6Å “ºp–ÁàÜl>²Ò—L-ç5I†´yÌiB2NcÓÔÅôø/ªlÅø Šü‰?cíD‡=ö§³ËiÖFSš$¯ÁÆgn"÷D3hÌ[.âÕÀƒÜßà@~xu’àëP)NfPáÍ[è;ñ¦pˆïêzG—þ.–çF[(„T¿ýëÖôvUŸ¤‹³í‡òµéÜh÷h.”ŒÀpà¬B—w”“#ó\ihñJHÜÒÌ˳V㽤Z2r"Ý=¤œÌ¾ŸHT=„T9ÆÇ猢¡{eˆóNìaÑî@þbÏþ1}_52u—Žãå=ºt…ìz˜.ð´y_¾F-³Hh8÷7žžjãípöŸÿ_-=ÅD'Âã`d¶R=°xïyT¼ Õé²tkš¯»,Åÿ G++°²žÎ3ÝÏÓúK¢]8–?«6ç½{[qÒbEºûÐiȽU^÷2ØÆ¯Ù‚s¯aê¯0Áõ !£¶s‹Ë;±©ä2Y×Ûïg³®«¡PUÄ*¥¦S¥¬½`±I’î}-Ek¾.?qS=aiÂSÜ~O›ì%õ—¸™õhž›}éõ½Ïë(~§ª¯åËn]Äì{Ãhßí¯HS­Ðñ7 sç?M[‰ÔÙshÂqÎæù‚‡ƒñø9þõ·ü¿ü÷8Ëî(\©xyüù±|Äæã6£7hg¿.à]ÅA ];„—þ%{»AÑß–o• ¥™~„ABŸ5Wx©Ìì¬iYWìmÔ£Û“<¡!á)°ÒÙT)m$ê5\ùt–€o K„ ®Áo×;ðpaþZ|7ªqölZMVܰuÞn0Ä–J㟊æjK{®›Ñ G‚ A£÷(HÑ¡âîôú3öhz+|·‡ïuOÄ΃õlœI0Ý•9Ÿ~¥¢tþE<0‹ò½Õ¶´0è=´Æ»ß“öɼjVŸñî.~еÿüÿó€4xûš5Äö°cûŽÀû¯¹–Z [œ†î…¹n/„š]¿Ñ}…H¥„c_ÙJÅó¹õ"ä×›¾$ÁL¸ÄU^I&(ÕÅ/‰rè|ŒkÜ ˆÃ±p¡IPLnN¤ã¿çƱT¼â,½KõL{VEÛ˜Íé úÚv.ù2mͤÒÀ%Î|ÍÛBxÙy[ÉRçý1>PÊ»–V³ÔŸYð‹ÊÐ%bš\Ú¼Ú‹ç³E‘o!ìS5ô8ÊÓ6ÑûÄꊚ’ Ž|Á ¼ht%4â ßa§ûD˜Um‹5‰e‚ÿá×OÊ"Úâ°åÕ6:äå2俘& ç^x‡ß®z@êúlhøss§¢_s;x]~Œ­Nõ×äiðß!ôcÎUöM_n˜b•L9·ùø¹ÖÈk(€Kµ£ C澫˜}=R°Î§Ã.hAýéMØócNuJÅ]õl9œòßËM¾ªs¶ÃŸz%+QS~ì¾Ö#½5ÿå¿Õcøz¯)IÝÇUgœ¥Ý.1 ó·Ò…3&¡iŒ dÿNA%§ PrOäo·æÖÛ²«/ IbA8¼»r‹Œ<»L0¥õ¹ßÅœ¯g×/>±£‚%†¼þ`ÄŸ¼ÆÓ À¢¢ã5$©å+m%4à‹Ü@8·¼ ß²Ì5lä*‡I÷˜æ 7à?mô†¥›ÐÛË„‡ ŽRïA¼À18ïi(s⒗̈öûx Tü?J ÛD>÷ /[}=çäÐÔ"¯ñﲞØßÚß›¸aÔM|˜'Íëý`Ë­ðÆ> —.Pc+—»°S'`ôâ9¼Þ{ëcÝPþ¬ÓƒYÌ{ÁîåúâòÊÁPuþŽáækÑXT¯ƒõ)ó±¢Ü›xÛ˜ÀKÀçm†#È7݃¤«'.¯ù걸̦w£{˜f;¯sVã&Ûð\w5œ¼Êf©ðâ>Gœè»G8XÌ÷¬Wƒ{ÚNðëÈ.ô{Ј ÙMá·4TQä?&§ÁÉCðsÌ@q*.öJç^CÓX72xÔRr[¨Ì‡Ÿ™ ßTþñŸœå[‰.6A˜n²`’} w_ èXYÄb<¢a×…i¼±yÅÁdzèÜöp⠲ȢĆ©ÃCâ´Ás89_ȶGëñI£Wñäù%ôAà+(p¾€vqÝ` AÈ(þ12ŸÞjÐâJ“µ¸Å¡NçÚÄ£ðHª>w´âÞI˹ºèA®¼yYã4㊫à׿X¼Û½FþR‚ÏWð|ö Nýì%#6ØÑÓZ«ù¯_bµÆG%éÇá¦ôœì Ú±Òœæ¼BK­Dâ« ÅYL?×[²ªð:x åD™¿¬ô¢ûü‹ÿi0Õ—b¼âlh³ÅL^`H÷«ŽÅ‰¯» ˆ…bmå,Ô=íK?]6äî)÷ðÌ„¥tYª]eA[K<ér- |âξx†=»›‚vÝY”Ϣ¸Ãäùï{À´CpÊ×jü¸©í&±ŽS ŠŽ«é,ÔgÜß žf¬w¿)/yçë­úÁ]þž¼cÈOm6ô­ýÈ ÖTÁÃ?7a¶^fœV /’a–Þ=âoxM(¯‰‰;\pãLó|Ñg¶ã¯§ƒÿùÿ:å¥ïÿi™WLÄuk(oÊ|Ȧº‡àÐØÜioÁê °eÁ9¶íº¯ó0¢ËÍ}pÔg„à1öpœP>ÎW‹Ð0é+.Ý€ÍYþ·l ?éŠ/¹yÛŒþ°L0"HŒv"Y\¡˜»r›Ør2¦`> åð$W^Õä‘¿ ÃæfÑ÷¶ùÔóþv"4ÛAw¼‰Ái6· ÍúHÛÍÇ?Ãoƒ½!Å’öo°pÛrêejÀ[™ãðyd–w0JÚÃME-Ȉ™3ÐA+†»¿ôâÃRbjÏT·¡O’ü¿ü§×¨ÉjÒ’pr¼øÆùÃéüÞÐL803—]l~Ùƒµaó7Kb4Ù²j›àK™)ÜÖ—ÂB¼·Ã”¾ªó4£‹PqÓw7eaW³sUðlÈy²——éò=q«Ç¶¢åù#qÄnmÆÕ0Å#«ˆþÕ^vZ7‡L¾¥‹ç'ÑHæ"CñE*18©|$}»q(w_ì¤wϯvÜV)þߘÁC0Bb&Œ\"Šq’™xe“?>Û }\ÝùèÍáôö̸Ðñw•©r±Ž¡˜²ÓÇã¹× V›9ä–\´z8ùŸýk*;?ÞBlÄ…Úµ_Oðà)™˜0Ä^:††­Ç˜¦%Ç?oÓ°³µoÑ£ÝÝl÷ÓÍdR>¼XÀÉ¢V¸þe*{›ÍÈÑ£†â¾ŒKäúÖx0òСkÖ>‡í-0ó›%Ÿ(Ùb[S±Ð×Ê7YA؉œícÏ3ÆØ`Zþ)¼+÷ µgbþ |Ñ0gþ¡± _ížËï¨íúKÞÀ»à@ qP$Ç_…±ÑX×ÄdÐyz'اÀ‰{SyÊš·8/òHu`æ¤Pº® „K'ó’1=Äíg$V¯¿Ì¶Mw@ý‰7þñ¿ìmë™—{>ù­_…f÷raìêû H”8¤ ÍûUxZ2>k?‰ÛVFíÇv0Cµ ß/4±Ë‡Ð~cy>*êªo±ãk÷7¼¢0³ÈUEw²ÑœÈÕMÅ7ßXÁıÂRÑ?Âw;µpÕW¾çñmrK¾f§lf›=xî‹rÜ?h$&*RëýÓˆÌÀ`~æÊwÜï7wŸøˆÁI$àT8技]µ†ðtÄ øÑVã×leQª @Á6ûçÌÀë‘S„v޼'^ŠF˜‡ Æ pJ†Êy'‘š5„Ïý<Šù¾|ö¿ü`WAP÷ <¼ÙŒLÙÀ_~Î?~R¥î.B Ä©›Á±^‰×˨ò“†âøvÕR¼:|”O^ŠûnäÁAŸ±!®ŠÏ/`‡JœQís6Ðú8Œ/€FQxqVßù^ ì,>„ƒ]0-Ä5,Vƒâ”|˜–ü úaG6v|¼ÉÚ53qÔfÑP 7 ´Ô´w]Nº>ïÂîË2ðã~Mm™!žÿü ®“ øŸ»Ë…â“<Ñݯ ^=®ÇI•ßqõ Q½á6Ë—IòÏ')öÐÄ- 2ktáçÃÄùOB¿‘T¸ªÃcgI0ó¾ ¥»âññX±f—j‹À wìi¨÷6´ý9Òp“H}{‰Å.óñ¼\.L(jw^Å¡SgÀŸ¯„s*¶áÈðR[W·Ÿ?¬5™jOÿËM$ei/œn%ƒ¶8;ØE€¿C0\b#¸ò“ \xÂÊ­³‹_4è8±DÜ’6.”½!Ó¤‹a5ñ€“Ë™K"ÁÔ=û¹IÎöáò\(Ï €‡_ƒ‰†ÞB<r9 _É©yÈ=RXmË;°« ì翊k§E釦Qý0KE“›wïÿÙ佩-ÆõAîðPPn….§/A¥5s­Iª7‘F]ØùþÓFGm:B–å©á¥DhÒÞ ×ÎHðÂ’‰tò’…|Û2zmƒ&­üÌãéS(mÑÅ£‚ ÒÙvš|?2‰§ÊÑ§Óæ³ü—9ð[ä›ÿÈ4ðš =ŠéYøÁžD(þmMïÀ¯¡5İŠJ*d„µXç”P¡°‚¼^&€ÿ4Úª«gœ3fòÍÿ~a˜Ÿ‹‹nã={¨Ù–Æ Ê¾¡^÷Yðp: ±bj—-"Y+Êœ­‚?üã¿õ¦H+MØá–PLh À5> øD:Œ5HЦF[Þ®áEË ¶ÑB­Œ‹8ÞïoèàU³ÉÃÌy^dom$ÄyÏ£ÕùºYHìã\¬ øÝé$ã]5 <“ßê4âϾ›“»+ qÏ¥yİ5„ÕÙÄAáÕUMÚ:ÿ*Ü}®G÷:ÌæNoM $m 8ƒ›‡œ‡ MÏ„v6!Lsd›¾j‹0ù8?úA”­˜mD·ï?Š»Û6Á³ßδ>U<XZ¨9Åà´.O_Bî §öCyÁM)ÚªŒ;_tÕüÿ1E1×V#ˆjÂÇ ¥Î×ôÙÎywˆd»a 4σÏÖ á¡} õÒ˜L‹›cÖà #TéEY1º¯ÐûÍHô}”ŽKû=pz”"îiŸˆÁ«`ê¹!‚ž¡ö ]ÿ[¥0%_i™9ðü^5Ž ÉV…•Aù«M1pYJ.ö>üÌ#»ÎÄãΑGpkŽÐ^jɬÌ`%§LÿÖÊ©¸õýHš€U·õáµ~]8Œ–SdiçŽr}]—}7á*=æ¹?ZG¢z©#Y[™‚;ôvb†WÊtTÁ¶ÄKÿüßbœѽ<õ?½1ô:Ëã`³;$¾b Ž|`‡Þ])É"¥£$´>ËÈé—?¡tÈpèï9Ʀû­Æ¡‡²çƒLñâO]>éQ%{2$twfÀÚðû ê"Ž]°å¿Ðùƒ`ySN •'~q{‰@DFÐpn¹ïî€'Í‹˜LÏ-²öz8JFÂ/Êüs†"›¡o~×:ÿ7—™Uðκ I@l‡"·8™¸]V$]§Ü¸eÍ*Þ»†›ªº ˆŠaaM>µY„›zá¹k:Aá&I³i ¾Ã§•u8Uçñ?ü°: ¿DauãYòt¦¼ ðäÁ쇋qËu ~£? S¨AåžÉp‘öãN™8yœXù«káeÌk.&¿–©ÒÀ‘èT,KF•ú×vˆÿî,|òM·M·D¿K“ÑÄ#¯g÷“ƒpȬ>2òÍ4Ö4"»>·Ö&Ô~þqÌú¡†ã¦(ÃlY}TÎñ‡c¡ÌÞË”ÿY(»îAÙ®÷äÜ‹L˜$Z}¢ópÕ€< 5p¾í=<Í,U7`¨î¶}œ„?ÌŸfËÒkÙG`Xž1=î·ˆvg¯‹S¸rØlÚt®éßï?‹5*ˆÏªBA¼£j³‰Â£Óð¨'¦æ¥ÙUX󾓸h¨òêFþüwã߸2†Wú<=ónt¾]µV`WïYî¢ÍU"e‰IÅ ð\*ê-—òî@»>¦A«‹(Žèt†_ûWs3Ýïà4rUR— 2 1iºuH¾)¾04b‘5ñÀ;ÉD5+–f Â>‰áˆ¾FyR Rµ„÷mÑå³&%Àdð¡£›®² •ëØm1f-^AÎ ŽƒçT£„ï¼ò³§X­VTû`vÎj¿*ðÞ[Ü×~íþ^ë[dSçBd³Å Ü(9œ†Ð$ÛÆ9á´ZdÒû“¸©Ýä7A[v-ÆË$ Ä6ybfe>\Ø¥S[3¤ j<$&¶500ê-;£›õktI_T‹óƒ¦:Ö2ê“9 £.n¯® | ä¨ÒkQ7ñÓ¥.ÁÑÖÜž>jEy_{<ÉqTÛüC4`VĶªH&jã݈8,°GŸßC0ýD™¶²Pÿøfôë•Á ¾'Ý•ÿÊуÏóLaî»Ý(ÑnÄs¤é*gQnö NÊò¬H\öŸÿ·™‚…eo‰§Ä,½|Â\>BG|+«ö¤ç¶Í€ä&އX$K ¸ ûdãpKÆvHë¼ ñU5¬7ôð¥ù=Ü0Jº2U`v×.øøë >Z%œmÉ.ÜœƒyiÇ0óÝ~øäšË$3ðîœ×0Þ:Ùßw„E¤‚úÎW8n_8¦/™Áç[Ç¡…h4}uèô>üö/‚ˆ~® ÇøÀbäw™•t(9у‰+NÀ,›?X܇¢g£æ.¬Ñ‰Ãú1öÌüræ%9åŠ;N¤™Wc¦¤ Ë“j·g\@_=ÿõïÉ©zäïgg¸µö; C"˜dêa&›@,Es!òûrþCüä:dútØhnžÚÞu¨yB•Š+€¿ZÉz½ÃÇæa¼Ð›R¹Ã”m…hr}n Ú…üBm;Zgl ·'ès¹¬<–SÄ›Aº¨ ôPíÇUÄ¡Á²ù|pìñ`mVEÇí_P¡Òˆ¾ Ð=¼ †Oª€ž™ZxYv.¬¬D«Ä=«—­W•˜¡…Ó*Çr—Ñ&(>Í gÚ6ÀWå84žÝjÀ?o;„ g#j¬y¹ç2ÁÌ}ahzy!¶œ)…r¹TÁíK¿È²OªpòÂ6© tÍû°þÿúÛ/¶:m† |éAx&Að°ÖÐ:ý•K7‚õ[AÒ˜™÷‚±YúÓó6¬ÙºŸÕøCI¸>Ý;5´ö|À=ë°s+qrÔ},<¯ÆýއaAêpJN51,p7ÄïF³0Õ§–ލëiòÙ«0Ã:íÚ|qþ«ï0Ñðôú|A¥Ý‹áÅ2\åm‹óÍèÖzkP+‡ǹð½ç|È^¯%0fZ$¼Ópf~ó,¨ò déÙ_ÌsÐ(’ó"׺•bÇÃl(uìÂu‰xy»cú™•%¢PY ¶µ(T¨úÇÿ¥bíPv¯"5ó!¼)>{ ?`ãÏÕBË>øtx Y¼¬ "wkp¥äócÛóÇ @ÕŠ^¦0È–F?Á4ŸéüV3¡#mWî‰g»’ˆÉħ°ñ·5ßôâ,îZ)Ëë. Ÿ?‹Råß/aÈy*¶Õ§ØŠqͳyÔPn1¶ž Qc“¡Uë¬æ­|‘‡2ß« Úºzèu÷Äÿr\Ûb‰)Ó@â’=ñÆžYï—¹–þ\‡¯f}k†"t]%Ó~€”Ãz<»à0Ÿ¯°ç&Ž€ µí¨‚{DeÑøÌWÂ;×àÇ‹ªÿüßå3vßæ–G¢û#1Þºû„`ÒJ]8sÈ”e.±ƒF2<*5aõ´úüŒtŒêÇ9‚uàò*œïŽ…½K‘ ÕŠÿi™©ïb>©Òœÿhè– ³`RK þ°’'Þ¿J–Üƺý±dE¢ Ya&lKväƒ3 ÉúƒA˜¾q*w¥u­P½#õîÔâ"Ôàã=ò ÿ<¼ùHp™àü”‹üçÿa”\oâ^2»UZÔþj"˜žÆì‹j<æZëøà†|iò<ÍÐ26]øn¿]ù¬U%ãÇg‰øÑì6©\ ¿g4µÇšk«þ Ÿ»Dï§ÚÆ•>1Û™VkX¡ÎS>0ÉÕÕŠÜKît]TàRK~ƒE­4ÿYà ™ÓÖàŒ7¶jØ9øå-Î=^-ãA‹÷aVs/tœK÷‡=ÁQã”ø*rìsÙ‘Y‹øÃO?QçH0çHö%;‡oQËÅ“ÊÇyRkÄ~lEç 4È’Žþr…ü6zä3çf ÿåÿ -ö­ºG^²¤×öÜ€½ß“0È$N®ÆÍqÝìüïRX£¯=Å%F_™Ñ˜:®wQ†ßrdkŠœÏÊEצ[߬Tb¸Ð¶U_„»]1 –™eÊÖTkÉVër•W¤‘*›áØ{%úÎóõÞ†«Ôù~©èo§JÏÓ0Ég.6íÔa«ÓàEt•¿ ƒò­xÁù'è.°b¯b0ÏÈŽ®Xvƒ©ÅÑKDàpÿ'¬] ýÇF¯¹ ëv’1Éù̙ñh÷Ó…÷o×ç7‚®ý‡Ã9f±>‹Y/Ê{^TýóÿoòÁ°êi*º3 Ž& Á°3Äž·Åf@ß~7ºwC(˜}NÖI18ÔN¦Wï½*,u¤™§¡Ù<-²ÂpuŒx‡Ãì.ìEè–¹~±Ú—ºN™D¼&&á]ã(ûg)¿q£€*æÝa vðßïòáï3À§)_ÔÿÆvï_DàùJþ5ü*,ý[§ Kz6”¡ } ‘®Â—é1dêqºÍèt8¿†A ¼ƒ¿‘ä÷ÏIÂå×Jü\'Ó¶ÕÆÖ¹SÈôå}äô>i*7f$Y+:†nÈ+Ä™Aü2 øwÿoëÎsl¸êr\.Ý }½]0ïó`^“ËÅUèÀøC0Lð¤Ê3ƒß1èª:lóNÅ 'EIð¶‹ñt+º-‚£ÉKl)WðÎúCäÒÈ!Âæ1©¨e„_H¢ÔËhá)ëx´ø(÷ß4ŠJ ôÀkÒ7ØQR‡óﲎ… 7<ªue©e†¹`£ôw¶H½Þ£ɺï™fî\ÑÚ-?†/1Œ˜<Í*—êPËLÔ‰Ö&W‚Ëñ¥ÍÁæ ×Áµs,½\×MR*ðç’:–¬ò…ʧ³æÑX9ôºE%’1.1ÿÖÿ½q•0öôjÜV]#ŸÔÁ^I¹±è'Íì@õ°œ´ š*|åöÉF3 ê!(@¹oÉ :½’â£pšÕN(ôð&£»ôalÜlâ3K‘ç†Þü‰ðÑ G4 R"ß ãâáÓáõˆ3äÊ™(+ì„?ruLk®9»Ù„*Û=ÅæbHÍ æÕ-ÙÓdùÅòhÕ|‡¬» y%kùýñØ~3aO÷& ? žj™3ã§×ðpÊ88Îï’ãÓ`DóqO­$>  "(«ßŸÎÃÃn–ïAîçÔ e_ÏN{s> ù‡ÿüЧŽbÔˆJpÇå“ð§g,¤·§fŠp«W”FŸ,C-ͬ¼¥·®Û‹íþA4¼Ê‚Ú-¡GàšV&+U‚_ÛÄØŸ_ƒé±g!dÝB“]Áõuø,a>-Gÿ#›ÑÛFÊÜÅGöŒÁ”®nHT’ÁIJ¯Ä ݾ£/r2£T›úKÖ“íF |XLb\$ù ÿAé9”VRlËñ§E“¼ðcT/¬;< —Ü‘¡qï‚és1Øã ÕrðÂOhôù ?æÀëÎâ4éžyáÆÒÌàZÙ—…‹fUüËÿûoîÃ˾a°·N–~É1áëúHò«bÔþ HI¦,ʥܽ¦^X>Ä«Q²tßOSÜkÒ7_FÃNáþÆhJƒ©óäXÜ}º¢•°+kz}-€kÒY¸Q&”(hî—1D~ø`.õu¥@4ê¹Á–AØyò2Mš¿·‘¢Ã¿èÓ]NÕÌèê|â$IøžìŸ¸ÎçÜP§t¥ðLGÇñçÀqR º>¼'ÜhÑ‚ICˆç ®ìÙ4Óg˜ÊÛ„K°ì® Ý÷TšwßÝ•ŸXçœyà31”>ZýßuðÉI̸X ·ÄΡó“ŸØŠ˰nqÈ×ï„3¾êµ>±;Ãþæž*¼®gO¹\Û µ—§‹}¯báêèBpܲCZD»ö ~ÌzÂæ–´Â#sgžp³ ~YôâN#Øæõ_)ç@ŽˆFD¤Ò}m-ø'©†¸ÇòÒ^{&5±?xj¶Þê(C‡^} 3gçzêmü F, §¯¨ hÿ§÷Æ£M9SÒÂû_~²¤;ã:\wþ‚W<…Ó£F±51N|tb‰QˆbÛ/NùgÿâZQ<.¶˜…ËHñÄÿPñ|'ùJØ–§ñ³Ýt\¦“$j¾Âìã”tžs†³ÐÃMç{¸óÐEÃA3|ß5ˆþt˜M·?µ„Ù 3Q~Ìq̇ì í:PHºž‘ÜuìëÆ^?×ï-…0Yï6_‡ç¶÷Ou ¸åä åbçMgwïíÅ‚QÒt½Ü1¬:>–ÇÜãMâ+ ¢b'ÆipÇW]äÓð8P€t`ÌZ:æÚ G¡k×9 ˆþð@d "NizÝ?K“^ây4Š6Ûâ¢Ë Xþí6ºË@Ö­|x ‡Í¾Š[BÎ ôÞìcÇ;åhàGo}cÆçKÅ?¥øSfãy‡]¸Ï|$ì–‰Ö|ÑÅöÃÁÌøXy+BlX$ÖÿºŽã“úA=ý:S³Q†Å¯¯–^ü`zþ²þÂÊŠ–â8nÔsòD¢•9qË·ÆüfKûª3 'ާè»8 ,w.½Ðþwèž?ˆR¥xr÷:ß\”Úÿ|C~ï‰BŸ—a°d»2tv@Õ³# 3o&óhZ§vë€Äsi~;#Bb¾ÃMß%øz7 Ëœ ýN8þð__ª ÁÌs&<Ál>Æ—H³ò[DÜíá¶Ðá‘/…êÚ£ÿùÿ›•ç@)ä>Ì¿T…ÝKÂ4û4ºüÊ}x¸Ñ”ÏS £=-5а¿Wð {PöN_SzêýºxëwEÞëÓÍÇÍ¥·ì+ùž*þK<”×LšA‡ù[«t$º‹ÑaV:ƒ/.ÈmÄÍÒ;è¡m'ÀX©Š>ŸÑC]ߌĥ&öt|ž.™R«ÉÕlen{¯Ó—š”=ŒDfð!KEA?1;ŽU£÷{XþšY\¨RÄÛoÙQóIü,1Çæ™oÓ~èñq;˜{“ŸcöFç¡¥¶ýÿi©‹Ç‰Ó[?3—Z»Ãÿáÿ–»Ïæ•€Û‘‡PxÅ›?x~+âÀœsf¼÷ù.zði ñh@k±cÍ~#OX¸±D©Xý§ ”–ìäj½Áè‚Ó¸¡‡Ýrë9 w:5Þ¦Åu—1ŒÍš­‘uü@·)]¹ˆÛ2û±ªÂ|(t{(HuWÇ GÃþ­ÆØfö  ÝV²8^gÓ¾´ :eë!¯PŸˆÏ° ÎL£{GÒµý…äát-~c €4^[968AC‚'ïOá?çâ·×¸¿ç,ýÉágÿGmétš´X–8ëM‡ˆíkˆV‰Ô?üc=BIÒ÷"!5»wì_crýxêþy€”›ÎÀQ¿a—ñ7øO_l7e1ߤ? _faÁí/¬¦?66V²ú5søÑCìëÊÓŒ"Qãž~úØÌ*Wx¹+O@½ÉŠŸH˜A÷+Jða9!¨ýâî(ån效½#IÎëb˜ÔªÊß‡ÍækëºX 7dבd_yºQ>ŸšðO³Æ+ À¼q:WŒ¯Åž–ˆLðAí7º<­z&¿Z¾š'ãƒ#þdx©^8‘¦™ß¯wYLM þ·|³Æ­ñLç“'÷EÇ+ï†ð•–|ö™Ã˜óàˆ= ±ú°hµbÏjƒÿ×lË×ãоgb|ò3à Á?lª¨ÿÂ_ÚÍpå»I{<€“#3àÅq!ém‚cóŒ¹Q¾:Vâƒ0ô¤L¾• v9ò´vF ¾‹J÷=^MkìðÞǸ5”ö|xIœ‚ÖÐÆÇÃÿá_ÓÏ–§boˆ Oßß‚ ¦‚'ÜØsý˜¼x1™v<øˆW^EÇ#0ìol3(¤¶c¸]Ä\j0DœN÷ê­É/ rnD…£«{;’wé²´- ¸¢ug³náÖä5<|W$PÓ4î"»†Ü¸bO3ö¤ÂÚÜ@\¶iUv݃]btýE®±=œ/hL'ÒÙÝDAù'VU¯âQãMx°^?;¸•Ì;˜rc¯±ßó´y_sQN> 5‡ó˜ìÅL¨8˜ÂFÐ\Ñd¾ôQòSžë5©ò²iidõã×^û‡ÿàü÷l²ÔFtëâa}+~®Ý’þ4ràUy7àƒ¢Ý©ºœœºÈJ§ÔÒ‰ר•b(úù‰Óœ/È•û…lu°Ý7f(]øk< äþg'BíìW êv™×&8ÓƒO¿Þ•¤èëJ:VQ -›8ŒL‘…š´èózàà'°0ÝÏ…g‡ÒþêÑüóˆ ¸BìßzÔ’maÖúh^3‹êìûŽ5’ñ¨iKm Ò±äî Šï5xwÂA>4-ŽùÐÃãñ¢9 U§CU ®áÊõvÜfð)¦öôÔÎüËч¤âžß;øéå¯ÿ­ÿI&ª˜³«ÓÆ/þËÑ£ñ¼ °³’í0fÑEAéˆÛ°eÛk¶GÑŒ¨ü¡°¬½ ®NíùY(W[ÉTjÅøâã]ÂN?_°Ÿ¸<ô(1ø›ã6-ä­2_þO'íZ³&kÙó¥þÖ3‰¶yìÌÀt¢ú¸— _·ÎNWeƒ·°€‰ÍèæSÅŸž‡càÜØÒÿ³S“…?o:•ç]³´n>Óuç¯s¿`HIº<e¯'¦I‡HQóSüñ¥Ž(÷Ô “¦ê¢ˆÜghø>Ö96W–NQŽ÷ždál~\ñçî?ü~oP±OåÖ¤®/²÷šÓxîƒç¤ëg8lH’Ä›ô3¾²ƒA£©Tu.Ê)=ÁÖ÷—aõ¢PØq¯‚]´„ƒÚýœÖ‰3¼²WŠw˜w‚ïÂ<润izK-ù ðˆQ«U<ˆì½ùs¤ø¦ù½¬;H†+i¦¢mh?„²wk3XOÏiq§’5°5¡ ò+‰¸ü/^žk܉ãlwžiFFkŸÄý^|ÎKy¾(±€œ/é# 2ΰ»C«PqôŒ±ß &½êd’C7;û1‚i E·­j‚糧±odžñÿë®](â{ "–x³Së+YR9l}…EvðuD6þùs_\%M‰±¬½€n­»É¦þî€]úu`yaTGÑ:zölÌÓ³ÌS!êL®Ão³W¤Ôx•_ ÓÁ8’¦®­ÁJc6mƒ<·Z8”WûL&ÐÅÙ‹uè³à>Hó}Š/Z.±C? Ä׊ÑM ‡Ðê)·±}ì/¼>³ Õ±Ák?|ß0“zØV!•œ²+p’ûVìÿìÊS®¢BÕ&Ò>˜gÙ\f^òG1 Øu`0MJ>)ô¿§—-ù §EÎÿÃ<<”¹Û,ÕëÀýö ¨”±6äü€÷•ŸÁвö™¢eÓçPÓá¨9)ŽÍàÜÅìUZ±,ùŒFÈ8Ku#Qá÷áQ(tŒDõ;ñú¸kÌgI#xØ,Àù=Z4ed#+ªŽ‹[ ©ß+4‘øè”òè69S ¯*tÐý¾)ßüKøl’F=ÓâóÕEäzÁ:Œ¿§ÃG©ÌàÓ禳‰,p<ËΧ ª@STn•/ >oüÀýžª‰Uã¨POÔ-<‚â³ûù#0n¥’h>xÙxè;Hžy“¤øŸÄêƒ#×Ú—‘r´xø:*~®Žý?þ®+ó:sRÎ3‹‚“7ç`¿w„ Õ#™Ê­f h)" )}<ä~+IáZ²wO^ å #ѳú %Ãè©0žÔ•sö—ã*C#Þªi‰ª‚Qµµ¯GòÙ?ÓÙïë—ðÂaIž¦yïüùwÿwNÀñ(Fýá>¶gñêº Ø•gC­>†¡ŽÈm)\ˆ%ƒ3ZUÀî]ËeÖ›=9©œá!†ÜeT:êŽIŲ¬óÎÇu”éЉ…ØÖkMµo†íjƒ¥5"|QZ6 š7œ&ÝÝË”¿0·£UU×”S?µt˜j?š{9ÌãCs¯ðx‰Ú!í@mûÿà†S„OEÙz9­žÿÚO~KËЇ©²phŠÿê=éî—p$5Ü[Š¢íyßG °‹ãÿáñNá!ÑŠüÆšodA~ uÑ /Ã{qÞÎkðw.ÿžòèô#o{1íspÔ*ZÎ> õrS”±ÍNaæ÷~¡B‚6Ä) ƒwÙ]q’"ª?ƒq†BØÏª5;Â"¤ÃÉ%Ê\–ä@£ØMØù}”pÚÓ-Ð7'È­)T㫎p!-½#)]æÈsB•©êßx¾ÑW± 8wn/ü¸;Ðÿ~ûræÂ ÌN–D—)×`¤x${U›j×)X1Ù)Ááél{CîŒÝ€™û Áêšü œ•¨…òçàïgçåýê¸Ù0n² V+¥hÇû(81Ú‘š=úüÏþ…#¾¿×€¢*y8ë4 ·Vf¡0®ÿ{ÞG:´Þ8ŠG.ÁO þËÅœ®º©Œ‡v æ²iY8ÿp¾Î4 ZûŒp‰}+|¿2Æ¿œÅ“§ÈbØ– <颇eㆠ¤ &óY×›¡öÈrØà{‚»`qé/Ô _sÃüé#4åǽð®Ö”ÿì󅛥]p3]•»?nDÑuäí®2¤ûÏÀ;óMÔôe(DLS„”‡®P=w8Ý;A‹k¨tþ­±¶ãÈŽExDñ<8$Ï ⨵¥+q< ‡< Dz³U Ü»kÜ‚g|ÿéŸUêN}Eg {°6*mŸ^kPS ;OIƒw‰+ž8šg_ª×¨Î˜G6)’½Yªô¬`%š¾°¤m—nàã[¡ð=¤Ï‹I#CôópX~ú„Ð~ª ÀiÁåÔƒ(4Säw¿K¡Â‘ÃÜÐø$InÃWò ¼w¢¦`[©wãhßÕä‚ÇÇa¬/fŽ­Ê±¼c”*ÆMVbIMwfŸÁ•“:Ѷgù!°¤u"(õr ™zá¿ÿóôÃøöçdδ‰üÈ +ä¦n8Öe0 aIŸ•XáÈ嘥ã_«àŸqïqHÚxsªùŸþA³ð8¬,?FÔÕf Ùܳб, V͵$·‹Þ’9ÍÕ 7¦CŸ`ÙQXü= }vºÓäf)–^í¹JäÎ)`ã,êÍäy·ß–t¯ç[2duŽLU¾—y›Æ«OÃ…’™’‚aÏpÛþ°ôÊ\P²’â}e P¢p—ÛÜÆß)a0æ†"UHu€uÃÿàzï ’ KWZ£»õ2²äÞSX¨•‡× CA¸k'÷`#é˜J3¨8oƒ‡N½bšÉŽôæ¿ñÎò-<霂jNéŠgkÈ€CŽ Þ€†Ÿ5h¥û=t°KæÆÿüÿ€öS&ïæ‰ /uáÀªeLeøH,»Tßà¬?øDÌœò³}#PË)*¿FâD káÉ;0åv*|ù€/ç|Ń٧و!‡¡Xý-g¯uÖ°MYиMï­Ù˲4§B~Y¨ggÀ£ßrìëI«£Z$Ù­;m8xó{áÍÜB\³u1¾t^£©l™¶)ÏËÍi ]툣.†¡ÃØ™ôÈ&Hò‹ù…ýà¼Xƒ=ólÕ‰¼øá|#÷-P%•Ÿ«0c>_+àÖ4léž]ßÿÝÿÞéš!øºgìÿÂÞe! ~[ ³Æ8ëô¦1÷%:C5.Ú?·Ïq@Ïkp™£qéÎß™…eNbÿ[Yôš77ôUáî;8sHÛ7UŽŸ›ÑY»5:œIoUÇp›q°+0½à8ž%¹{tH³¢å x5¡¼/Ç}A B«6Ötø¤Ðïé$€ˆ¯äûÆ‹·®”HT€è:7¬-­ƒË"Öô–(Ö£ul°…éŸò™‚÷;Æ÷ΠŸ/ºÐk‰êtË»™1ß‹ïÀݤQ°®g¬HºM.ݺb+ë™,·ûWÿÌø6Gó±hm=ËHTàÅŠLîn˜0RÞ£ôDù͵ÑäAd® “§!`šàŒVåÏÖèâ¶WäÔ;Âã®.’p=âûY¼Çb¾¢ãÏ£ûµiä€o'HÚ#ÖÌK#xà‚sNÞ"¸>Ç+¥Â-—tœrü¾à”UIðlãu\¦%IÕv* ï[ñͪ|…¿ ÷a߯( UÕT<ñAÜ⺎±˜ÖÀØçÎN4Ý— Ëc_Zs ŽZS6Zßí=v/fZ`Õñlîµ[‚–ç¢7”Ÿñ[ÿýþi!¯Ï·žc/ ̘›ÅñÚgUC10à”6<‰ÝŽ·ï,G·MÁ˜¸æ"ʉTEçV¡é$üòºŸhv¾‚][kÈ´‘Ù‡¤}p{ÙM0hÀì1ÚÔùnƒðËæ9ä·‰)=¤6œÛŠo%É[8Θ?òõ§BØ- *É%¡Ñ%8Ôÿ&úöà˜9OùŠó?=üò)¯öÕeE*vÀ„w^…‚±È‹Ëó +c<½¶Zû£—j Yð#êsr5Ôâ²§hr[¿SO¡h¾ ·µ ÷Xs︪¾#¾þ’Üw ùžÞü/ù ’ÕÛ£°Õ­w|=ÁvÝR%öò·aFg Œ±WÓ¿ Ö°dtßöVÓ‘c¾Â‰CsñîŠ0ù6·$IÞäŠV|‹BâàªøŸ­ÈÓlÝÛh¾&Ö0À!"bÔÁ®‹Íßc ï‘"õ#°2]Ò»eéæãqÅórÊûÞé³(èìw.„W®{ò—’?Q8Å ¼÷v¢Ö îðÙ~~ûÂ&„™ò§­ulŸæ(þ´b$W[ú f9¯‡ê¯¢ôëËÎÇšGC¾ÄaZº}†>L‚§ãº×]Ë!o#x¦pÞ|z ¬ í…;9ú[¯[BQAh&$P•†ð­ñ”pö'ö§@Œ?«]õ]ú|îºHúÉEüÿÅÿɸèAY(¦ÄÊ’âÀn—=Û~à«ð°²”Ç ûÎB^à*ìÞ– g¿îbŽ+Š`µäj^f:g¾™À…/ÐÉ»²\P°»¨:A YùE§AOú0EéàLÔ¶©ýlíèÆDö8¡ŒÉÖ}eǶ‚ZÒXr*o(~‘€Ÿ»Áæ}ëÜ B§³w7NáŽTEg:-ý»ÉÂ*W´Ô·¢¹7'baÍa žíÖýðàÕ# /–°³­1DQÆ€6„^…ÒkáwS5Ž;Ö‚W/ËðKgƒnóv?Ø?6ä@ݰTæ´´ñŸýçG aÁûð¬#Ž­a°öJ,hýÉ|oã-½æt~?ûi§Fƒ¦KðÓ¤øâdxg?ŒÕ\0é8hò%M}£îdO Cë ÎôëÌÁàsAAðþý ³Øù<¥·“ûùWpÂqô^²(÷®'z¾Á–ŒO‚÷þòáÅ“iÙu{.ŸUÆtn_®–ù¼²"Cf=(œzØ6iWʽ›Àó„yppÖL ¨ßíqúÔ>í(|>ª ~š©™÷C YÓ¹ƒŸZÓ‡Ë×)Ñ>]G>u›<žQÐÀ? .X^×ÅôjL>j«gQ—IÓ¹ä5Tn¼J{`ÝóedÓ—å ‰ 7oÏc__—Û‹ZY¸ÌRáw]$Žã)ãDc³*_V4‡yM—åiçðG¾û×éðGÛØPÃÐb‰p¡(”EÀð Úô¨4Ü7«¯q\¡ ÒxOp,jŸÐeò¨þ¥uSš`j~#Ü©U ƒ–½$1ÚçpÄeYÔ²s­ã |Ô Á‡ŠS¨?áLû>†.{¨@3*v²¶ë„9ù½$iÂ9”©éγcè€ñX§QÞVäyTÛ¸o”Pʨbu 1Õà;¯^XœÃM²:tÃûv¸«‰à[Ö€CÖãûEÿÿvw{º‰²-«¹ä™Xæ0°nÛóƒI=(fÝÍý0É:öŠIaªÒ!¾ó@Éý3aœñXöÛ•ß½½vHŽæï!;Eú>¾8¸§í5ªý¼`´À¼Á¼oDB„ó:Èß¹€Â™g`î+N?ÍjH™E?fNgý‡áôáôBüu|#û„ˆT’ÿö€æ7‰4‡åÀóí{£^àZ¿³› Ѱ_•?'óðþ€:ü=%Ï wçÝ`ò5Áö¨eà¦#NvŠÓa¹Òi°MZ€J¯„B¶K‚ßà sÖàÏ×õÿ̦qð¦N>ì˜N/\8‚Ó¾,AבïkFí£ï?dŸ¨=j#I?ú⹫ [é:>ÀëðJK‚gLˆg[—OÀ÷Š ¸gö®ÿtÊ褜—?ÁCqM>2 Z? ‹|íĶMá%dèAKx}h^y–pn¢Ï7ôÈñmу±3Ê/ùèñüѱùXWZS~…ù‰ zu¨‚™9†ódñM7˜ÔÒ$>´ªÑAzÇnbßD€£’ ëØ éyßÑ^w[®NÉݧγ ©Cp:+¼ˆ"‡ãÙˆLášySùH1!6},,7JðØÜ(J÷%@žìv^¿(‘«’Y´µ»ò—#æ7zq»JMúh­?|èµÕ•KÁR[‹>xkÈk3ñøxŽ-_ßÿóÿô‹+ðbà¡ÜAo}^ü@8ûz0D)ˆS›·m õa߃¥(k׊?wäã¸ÛI$ño¹¹û NިĶ%×_ŒÆwúôóóqdÛò<áY'K\ÿJÍ?¬‡»›;™‹ŽÇÝùIÅ7ò0ï>,]–K."J2\Ïj6´e1•mwðï@ábG85|tm}Nò·Üc®ŽÀæØ×Ðþ@kbž`räJ¦¬ðÖ9îâB<ž?]©5ç:aÃÉïÀG ì•Åš;“é–Q8—†¢žÌA¸˜'æîœÎ-C}yCþËí¾÷cád&?¢) )¡CpÛc[À3œòõ4øƒoGaŒŸ §¾¬ÿM3ÎÜúŸ_¤N‚DÿsáÕíw‰xx!Š.Êçuù –¿¥zcüOÿ¼a†Z®-ÿ‡ÿ×…íðýâ8»Õ›]AØâwpÑ44Å2°>Q‰ŽÜ,… )s@&`(܉§!KïJ÷Ucù[ÉÍG4à§ÚrEÜV‰¤ø‘ý~¦Ê~ý²$sŠð¤ G0OÛ†[ŽˆFÉT-5Ûãqªi©O<Šûôo3Û™Ô9w]TxkÓÿ®ï.7Óஹ,¸RÉtª*¡G!„¦5T×Ò*$åÌ«®2`MrüHÏ~cÖNúj÷Âz“`KÀ¦hHtÏàõ÷Gñ+~?Ñ|îlPØ÷yúÜ%çÜþÅ¿­ ÈÛ >È™°Çmž³è¸ovxó¥,?Ì0nÏmrËâ0œN3¤’7ZØEA>d;ᄪR(È[-ÎJ<«ê3ÄÞl‰.¨ýÑŽ÷Ÿ“ç~KËð˜ 3(5‡àÁ©¬oò^¹ßNY”LðÑÍ~ÖÖñBÄ¥±ùIÚ¿‹SJH€¬=^Í EgçJBÿláÃXšx „¦0·~,I펥û“ÇC±Ûmf¡&\8½®øR;º:NØ,béï|@|ê\©³Ô"ËAmùüùP†4ŸÎ÷¶K‚ÝEÿò¿ÌÏ$¶Mv1Ê•À×!{XíNYºüÕhùz=ºþŠâãö„¢ý´x8;an7•ÎÛߌ¢ çØò§ô0ušD¾aï>RÔÝ@¾ÚÊ7ËcÇmŒøäµjÜ#õØXÅâBß2æÞ÷Œwà¦á‡Èc ÎbÒñJŸ¯`y›5N¦þæ’GšlóÃlî¢Â]ò%©Ñ±dÍŒN„Ï÷¿a‡Ž©Ð%ýº4X¤üÚDnKt°¥áL»I›ŸïI¦;[zqGØm{øŸ×’êø†½E©¯¯Kåa žÆÿ⌤6nìÛÉfJ‚kç¤øQƒè¶}Õd+¸£’öp{ .Tÿ9ŽèêÓ ´ °µ­<fÂ¥—æÄóçA§ ±«gäà sê×çK…Z|à-©º‡Å7×ÃÞ±Ò0³Q‚Ï: Uª¼¿$×ì'kõÇ¢Ã-5Ú´° §N2ç}Qçm"¾yuwéË ªI]1‰¯'–ô¸M ß]PIÌjÆC_å7Ô;_²áïà¼O$—;fˆÒI¾âM2D—–À…Ë`2¯ïl;…Š——aÉzy>"U‰yÕ[ãáÙOñÖ¯µÿîÿ? œ2Ç`òyºT}6OÜýJŽÙÁa‘aâ7;úHã N L##ûVà Ç90×O›ž®Qác¶'yµÞ¸ö¼8—X.ÁWGDùÚhýxVs„Îs¦ ïͤÖ{BxÃ\Q쬴 cã×à©‚Ã4ð]ñÉ\ FWmx¯€á^å4çs,ù5†µjc¦Qô_{JQuyÎ/CŽÒ“¡IxĵÔÙø{ Yš5‹ÜO·á]u‘°¸h ¾xz :­ÇRç…©¼;O‰ÞL™ÅÛlM©îö½è½O—kzúÏþ`çÎNof»Tèî¿\:½b ¿sy'ÏžÍ}æwŽí,žMûVbâÏð‰;‰‹ÏVcÊ›,?ý9Žm·i¹0qÁT>«íÙWe9v L;ȉ"MÈ­…[³3É—ùÑB»fQÊãn1Á[”ÛX r¯ÂáWæ çùld¾ÆëÍ<ñ–D'¦Î,·äÇÖ&ÃÑë¶4:t.š]Ľo‚¬¿ëê[§6„’ŽÀëLœ|fÁùëqô‚¹´~‹2´Î‘+0Þä3ô©°g;Íè5—AÜèÇoØ¡(¬”Ä]¬âÿ_þ+úH¾|îÓÂLF‡‚Ç“:ðK:³6<à¬=¹T4ׂ}øl+lbÁ*Ftí«¡|ÜÛP÷)?&ž…Å_¡Çà­ðjü ~ÝÖ„/ OCõsq˜eúæã7¦Žø[!›óêÜ78èÆªõIˆ¬¶T˜7ã"ñ1âyêýäŠ[^Ü5ž~1œA:|<è0Ï`)wœ½àNÙôŸìkð;aùcC"QYOÔ¤ùñÖFêZ@z"—ÐG›˜ù‚÷‚OÞ!lw¶_ü¶RtÓæ0|Å(þÊM”ìÜŽFÕ‘áÿoÿs¥øK8¡/ýÂ0hóL´v¨Ëȱ,xv nÛûª:!$ÌÞ5ÝÄQ#lAcZ€pô—òe¡2®—¿Í ß;3í7—ɫșèwç¸ðQ‰(Ÿœ¡¼©Å®q<}$’¾ÿÖØm€ a,„eÀHX ›²MÎçp¢“æÛÀ£ªY|”…$\Xþ™œ×±££·ã¡Ä',¦$“¼¼»œ._˜'æT0ÞŽÉë ißoì¡oamp–$1ÿtÁüÌטý*U˜¥“€úÛ1$qãÂ!phµ(ìÙ" 1fQûyÌ¿ý”Œe9[›ˆ?ŒgðÅ$˜9ñµÜ6y0¸îž n÷±´Ÿ&Üïµ:ÏèžÂÁ|&M<Ìkw—`ðY'2Ía8ªè*r¾”Fƒïù8xrÍ?øRÙù,èa$ެtá[=Ôyg“Áï-l@'ŽZvžÃÕ} 92ˆéކõs­GA&?´íÎñ dº &Ó¡Uª˜°{œ`ÂÛ§¤Úã.®™Š‹%žò#‹£ð×v¼s>&¼È·kå‚ÒÑ,”[E§S±ã)ÐV¨À n…6t%;î˜P¯†xË­J•ôø‹ÒTü„å?ûŸ_´ åáî®ZO¤÷o4 ”ÖI¶9ÅǾ[Šêäù ìÓ°ÜOê´ßEõv¶:É”ŽÚ8†.Âç+£Á8÷ ¨ÞÇÇô~F§ÈÜþh œÅË¿Déo%!æ½Øòþ»¹VOÑR: _m)Û!G{ö–ƒ6Ù‡_ÔéÓ)wad¨™Ô1=UhïgË*ß$'FŒ¦*aƒùÞ~[:od"”}á—5øô£êh2C@žzŠ;§dó§J1òl,ï_ËŸ ‚#à7è)ì«ØŸó£ÐüÆaÚ¼Àj‚©ÿïù÷°®<œ7¼u6jÑŽí¢ÜÇÈ õmsÀöÓYT*(³¾§ã‰/à úlŠ€xŸ¥ÊQ|ð_^¢í~“ a\K™³¹=]D¨úƈôhƒGm/ˆÅ•NÞ¿ãTí ûÛ>_(~ ö§xÁ÷±P9]•öz'Òá‹Å¸ÅãWŒâ’ï]XÞ ÇÀŽÎGðÑÑv?Æ++Âßñà–ˆ JY€ñ|éÁÖžžÊ—_Ï¢#šÐåÛŸÀ7W0?ùI˜]|GžKí0 ¯ÎkCb3Ÿ½˜ƒŽ­¤·> áy_æþã?“'ž¹›ðEuYVì' sŽÄˆC~4àŒC“™ü)pâiTÜ·¥ëݲ±E=ä–¥g5÷OkÁ_ó<›ÜfL'KISÝk)˜“XÇŸ™Ó é‹¸í¸½ìëïxTÜ`Êï,˜bwµé%ý?(½÷±wå ¡“ÛÃ1êùO"R<‘;Zo€Ägq¨¹û*$«MÁÛñ»ÒHô[±=#‘>$ÁŠ'Ë ½Ñ÷\)ÇË*“QÓ,O?Øæ&hy«ͬ´ÑóéIÈ4Ýj‹ç)kÛÐê·µ]-O#Ž$€å„«ðU¼çŸý‹f†”yþìÀQÈ]±Ï{Ï §C—áãÞþzøS8úcwŠÓçqÖñà9E‹¯kÐç9-y8}Ô<|•ßÁrÙ'˜;a?O¸–Œ¼àÉ]G›Rë#Ò°Ïr=¼Ëœkì¹u_8ü·w4Ühq^¡Í¯Ï5£Ù#MèáI:üÀýð1Ô†M ¦².ç`ÆxA^†u©†Zá|´8©Eof/Äptb¯HïdVcǾb<…9Ók×ÖðóÏPïöÄE©Ò_˾„Ô)zÞvÍÞºZo|*Lî.áÝqWa¹£:7:pæ_þ ^ø÷7Y¢ƒäx^™*¸ø{¿ø,’úõ®tÚ™÷úšM¢s‡%q»°å¡ –ÃŽqƒù5ž<ºl zØþ7GÍ21 OwƒwÎG°±6–~ÁmÒšÀ8Ô‘Îlü#„ãñ¥s Ûù™\œ…»’'qEó ^õªšÈÜÆ‹kÀ~÷pJ/Vâã¡lÇ’l0X̓ö„@dµØÔ‰ææo°÷ª-f4¯¡[FÕñ¡ L¬¸Ê»áTîÛ.žj à3˜ ©Ù¬E/Ý-¦“ÜòñÅ ÚÑ’9y¨ ­BÕ€jHÿðñ~å6pÍê.;¼½GŒË|¯“÷Eà‚™¼^=FéÃÛCuLoaŒÏÊ%iæ¯ÙC¶–ßÙÑG»9æ6àß6t‹>–ëÙGñúüöexå6—¶d:ñ«[ýá×㯸øô,xm2?_/‹ïOï'ÃϦaÚ¦<´"ê‚—<¡Ø"–¸Í½ †å#¨®û%<È€ßNt…Uù³MÁ东/„äËbí3œ¹Ð §Gá©ü™,3¨žµ£”À;oÿ{ì:³ :–"zCÕ¸à\%ô•ÅÃ};~Ö3véamüµëÿ^ºÍ›¬­ið!1ÚûL™Ž5u¢+ ¼Q«aéçühûSTË€øE"¸ö\™óˆ®´[T„÷ü¸‚o¬Ö@Í9 zÛã;öO<-È*½«…3ùUéiüh±/½lz-F§«/A8f)—¯|ð£[¨*u liÃ5ZU)Xôc)‹>NVä]?e¸‹¿"lè¯.l©P’ÿ÷³ê¶b¹óÆÑëõ-¢eÍ"tŸÁCX½û7Ìý©“«4è7±YÐí#œ°îÿ#êÊã¡ü¾?Ù÷-û’%KÖT˜{n"Ú7TZiS)-ڴزgÏ!!•öT˜{nŠJ¢]*-Z´«´*ý|~øþ1¯y=óÌܹï9÷œó~ß9Ïy˜üPÄtï{x8ã,´ÝÞN ›¤¦ü?ýôÖ¦ÿ›ø?mfj±ƒø•¹ 8Éð|Û«FKSÃ¥iFÔaˆ“òÃϘB“{¨‡kud9:ÖÀÎLÄà^|HUþP5…“I÷ñþì6·+¡a¼g®Q}¦È_J EµÖ” \·¢üX˜¹(µ]Ã>ú–ã…¶xD¨Zl$ý”fH“~Ërš[‰újá OIÚŒLv‘8`^éE|{ä!⤫lve³êz¹tyrž4‰ý¹ >öÝÆ‰ãî‘k[ìß6|_}ŸõŽæSܦÓ!Ÿ$ HC“k„œ¯ÆþÌøßõ{ȸë•ppû]8«}d̤n{:#h/óØ}–½ÿœ9ñÊÍ`˜–.£h½Î-H þÌÎoòç×B¶Ññ·ã5óDv¸(€»_? Î›Ž T­mÁü7½-‡Ó3™Û' ú0 L°‰ÿF¼ŸÇk×>€ꓸÝWVµæ$~Uä8s#|$Lo5Ãù^üþö¬ò zïÎ!êמ’¤È`þaì6ƒÊ½êBé)Tä‚ß*íÅb ù^¿hþýEè~[ å*ú4lU,H¤²â'¨^”øe6úÃù&'ºmêåûß2~F*5·‘¢§YôñÙ#8׿‚lðüÂSסr6^·’#Ï ©ßyK¾¡®:ÃU©ÿïQtÐQ)œæ‘‹Ié¾{Ýø 7°}[6ãðâ ¿âIúÊ8…mûx>Io䦕À¼†X½§GƒëGÑ'™º´ PFܦ\G2–˜’.Д:E3Ín€WÁ%n3‡¾NhóˆWX¯Žêú\çò>hçM7ÜÏ¡Ÿ$“m/ëé&Ûµ"MkYcC8xúÿf† OW¢W{ÓøúÚÜïßvúHÚgÜœD_hóàu;¨‡ƒñÀÿßñjâhWÛÂNÆFQ*–qŽÃ/‡£¥ßD(ÏâÏ«pÅ“Rz;¼W/Õ†k_Ri¥œ)I||MùÓÍïÏ…Ý*s©õçSpqÌ`· ¦ èáë‘<âò¬5G-€•mV48.ŒÛL¯àóDfÒ•OÈ…F!ŽLÁu4ž[N2-Ž@è¶Õ´cÉv|œóh¾ób¾‚–ÛyЕe¼~¿3×Ò—ãÁO$¨]_*6½ÄU7nÇŒGÕ‚Ón_yS} ¾LNMä ]|C·lÝbd·H]Iý» û¾Þûü@ü‹g‹n "m>ØÇ@sÐqî!Æ1ÃùØ*ðp}5˜(rûðæÿ±¶ökð“ªôø†é|™êd”W &¼}•a ò0‹&Ó…³îËc%`ÒÈ@t)~ZJ^N–æâ|ül_2¡¯ ÅŸ‚†É¾`¹%[³_‘ûœø–kQ8HÖ/µ`VÂP}%ŽÿÕ3Ÿoüí¶ñºäª+ÐÅ;w#J’²Ö•?‡'ãòŒ’dÙüh#óÙuƒWs­aÓ»¹Tj[1¦mŠùú@{%•ÛG$Ÿ‰øÚÂ_“ÖØÿû ÿ›F…c(Z^U$_— æ“Uýy°®5ÓYOkð‹Ù98廄ïÏcÿ¸<3ð FkÌ;3“^®ËÕþ”ãÛì]WNù…®ÏÂ+!(Ṽ Ä× û1ÿ¼«Âq"ÒÕƒ‡ïT¨OÑpT;NÝÞwaZˆ2Û¨.J܇Qsü¶ú(•à 0æ —ñ}~\ĵ\WgØ?òà'œšìOd<ÌiP‹?7²ð†/áWà ¡6•wßœº³:Ž ŒHü='<]ÙKÅWàã2 ½KÄX~J)ÊY$£bR€5½À©ÙšT¼O/mÖÀg¯%¸ç˜@œ˜p>þu¢å[¨íÁÁ\[ÆÁí+*âÏ…ÔCHQówŠñüý.ž¼qׯôþô±õÿ÷‹e$ÇÓö°/š`Ù˃°Ÿ€xb~0^I¦çdÁ„åºe*¸DæmL÷ßNÃoÑkyåH ^ü'Uîö¢Â“gØg·“ÞîmÇ==©íÏsøïð±ËÍ¿Áýè1¸u_“¾Q×ã§2²Àqü¾Jï'ªå܆âÍ›ˆLÙxêFŒbgÐžŠ¡x.¦w5¯ƒOÖ=¨6C‹>«•…æîø>l?K¹¹ìN;æo?Á í6²£‡yêËIL\Ì # eéË·ÕäÝ{¸*»ƒ¥ÕŒ'¥Ù¶ÒkøØõnÝrUþ/™ ú¬Çp܇ƒV¶Á¤#oP p–ÅÞèÂ#GçCÑA„ƒ¢! dšWõ{¡Ào˜Â WúÁ¿dBõVT£µ‚*ì æü‰¤©:±åª 7Ñ$†ªnø{ †ÙVÙáM àRš #ú¼l^$î}ƒ}¨®k¿ÙÝ0æ©wq©†/³&m×6€Ê­;(Š5Þ>è4â0˵ò‡{es]ù ÃuÌå ćmç‡÷@å$[Ü~À Ô·«SLJäI­#<ÛÎÊÇÍ…x?0.ÝÆÉ„ ˜isMÏ^zé›$µLµƒwŽñ‡S2ɬL€ò£qpàï#2Ri*xvdªüWÌúw›WPÂl”ä¨d‘Íy½_ã™ô[°Ùö%\Zs=«^€®g%ö,}†S~šñmÚIÐÿ]—1‘o;¾ÚThêC{ˆ9ÊîêgÁ Ž·T‡s]ïzAå¤ãø¿.qªBíi¿Qb¶“öæ—öSô®}ðÈé-ÔøÛ¹ÉÔ›àS„Ü/Ûùžð×pañK̼_ ž÷Eè²ÀJRB8¦9EvLàïX<û6ìNއ³ 0^þ®ŒC÷HÒùóŠAÅû3(^ûŒ&‚ÐÚo(ó´ÂÃÜøÙ8A‚a8¾ÌßÁÚ3²áéTK᪠æ~ÿ’ ùëXx÷7’¿?­º®½W+sQî@ÌÁvq!ÀÍ;h;̓ï[ÑŠ÷%ùhÑn²|ý fu%®_iÆñ·Qwt“ÐìÅ~¦ùnæìY4€ß·o/Z…)¢Û‚£ ñÈ×ǽ…ò—-T|èH®/Olž ÞS¥ÁÇÞâ2޶­V…ìldÑ•ÿ¯‡î4ÖÃühà[¦çâí\!¾ü¡ Û-¾bµî8ž8”ãͪ#(å}xåí Ó˜Íëo 9²Xp7!Ž„®vä.=Ý M_âãèK¬Çá ã0pŠ,—¼†JÔœŸ¸Ñ)žpÉ-Do IûðEäNñïñÃ1µ_DÇKù‚Ýþ£Hxø Lýï]ÃL}sáûfôGæ\zìùHš¶þ>þk1§Û+oÔ¿ÉG 'MKgÝìµk3îôg›âjHQÉ4Dèñ2½ÉÔzÙUÐ  ›ÆB½ÞR>z‹/Lð‹ÀšQ?À}Ãò¨ò2ÊV|Sýúìæ¶R4ô¿Ç.Õáý—ý1.töUª=}¸ÍðëP#ÄmâPåŠTá/ ÞqàÎL~-(ÌK«Žzì®ãJ:a\Â(80”ÞKE‡¾ì½G•°|‰?Q÷ËÍ­N.¶Á£¤`©£û;&Ó¥Án,ีÂhòó|-V¤a׺‰Øþí:+xYK,'qYçåИì ÓÈÿi‰Ö'FžqÌÖc;|i+{äòí2ÃÎê$ðÃám'ÑzCù5š>:œ‡zï ¸âÇx¼j!Eg^Úƒ‰‰æð¢û;ëUk‚)ciÐŽz¤•@C]8>—7ç÷þóŸ—JPìé ¦hÀ¥B.àØõ’ü­Ì\âòÓž†Kºáê”[¨s÷³›]µéÁqZ˜©¼s/°äЂ ¯×À£ßÛ°.©sVJÐ; ‚g§Q{ãÄ´»êsmÏŸè±f2Ô²¶î‡—±Ç›ÌÁïÖ]’PÓJ”OOF7­8[á:°þ…S1:Ö€Ägéðú£÷!Æ| ¹9õsTòâá’¼ÿ˜-s¼å梊ƒ4¢„Fn¡p¾s‰›‹¥Dÿõ¾DõŸ°oî6ùu0\Ê­g/þѹ+ÏJ wÛ:xÏã>IÒÝé•xÚâ-öAš*ãì+ñ £|f³™¤î>ЫÿïãÜþ³ž,í˜]¶ãà0m$§w®Â›ÓÂÝY8{]•µ`E? XS¥¥pÊ‚ÒþœóTx×â(lu šV‡bÅãðD›‘߃²Ùɯ‡`VÔ97¿ëê¼+†è_Ë©—É<ëQÄñà ÔPN?' ú`²1Ϩڇc¦[ÑÏv3ðÂw]>ÜèÌR´¦òF†Ô{¥ú:4«ÊY­ÁJù#·:-àÅTzÞq(ý GçäíÅ)—ª™£bvÂ^<¥¨ÊŠZ|¯^.„z½B•¿9Ð*"å»R™µ}‚íùãÝ>'dzSWž×ic|Ô Q!“[wó«Yyh©ìH+Ÿ?BeQܸô˜ˆ‰R%ó(›(\Fo0ÅÄÅdÕ­å˜õá+›†W‹¶c—ºÅóƒøçÿz”}5Ä¢m½{Äaqá£ÿ_Óë‚ï2˘ȳB"œ½Qá,AwÊ9ºÕÿJAÓží¨?g!Málp¾ ·NSî?DžálÅŽ{ žðB¸»å6t½ÿçÙÐìÔÒµ¿¦ÐFO©þcIèólq“çRºa±¨([·+¡6¬x{ú¿¾ÑäE`V­ØÀìÖNâ.‹Þ³{S~cÔé­Âc·Û™šCg]w“ ½û¢Yæ±l6½¤-…kâ ]ïƒ`–u|͉bæY9u]ËÑpê0ÖÔC¬ªc î­5þ<‘ G „î®Ä³™{Ž#3e°]/xÿ¡o DÛä>û£Vµ‹Tè¯2pè ¦FŽCI³þbqútpœÂ’%ö4´ÅšoŒ&ž$ߨ‚宯!ûîxBxH­Í?Üå„<¹¯E•OÜÆ¤Im˜ð*jò{Ø¾è…øûXþø;Ôm¢É/ÁE=H½mÇW'\e·v$Àµ‹— ÚFˆ‰¿Þ¢N¨%}]JiLçmæ´$jAògIšv¼ä›§p¯š3$îÓ;Tž$Ƈ©ÉPÕŠH,-ý‘› àjbb“ͼWMA‹RCb¯ºƒ;/âAPEÉ6UVh뉇§ËÄ¿¢?Åìè½nüy¡œáà¯öÎĕͳÈÇ%,:zÍ7çE‡XÓÁ6hŒŠ±;Nm°à¾ÎRt¬Íã©dKV49ýÇ %&Ù‚d“.ß9k7í+{Æœ˜³¶¯òLVNÓÚƒpbu6ä,ÜY ýñ‹U 8–¸±ÑÇjñq– ~šÚ¯„`÷°“ðÖb?[1£? ©EÙNômöFn ¨ –%WÆ?€³ąìmªÄƒ³ÆråAƒàŠz=:¬· _3 `–ÞZšxB¨ºŠQW #º”½štš:/»„všû;Ný!ÁaGk$›“q”š.§Wã1vŽ)¯ÑR òÔÎa=6¨ë^WÁÇ7è¼=‚ÕÖI> Îi1¨(F«×)syý ürS.tœ¡p8m]«‹ó÷c—w ²¨¡i»½Ü¯Ó¬&sg“¸_¶ìFùÕ-†Ç=÷ ýô¡8ð¦²,Õi•¤/€tzþ3£œ‹`¸ŠfÍLƃ>Ƽ£»âΣQy4¼X«@¿Ÿó¦<Ç2³d]’/‹èxÀ¼¶™PÏá%,½l„àãï•p^ߎW½Ìƒú‡Mà6aÌ@üó~PJ$ªTñéIQH‰ÉÏkÍ€ËK‘'‰âüëíT0ç{ÉÐhCØ”wž9$Îç+ÙË•‡!>¶Äj?ÐË*DYO“6O¦'Ux̶hS6݉ü$! /°†­¹ðAZ¼Ó³ñúí`¯íNó\üˆLì_|’ŸÄóÝÄéyçB¹§„úŒ{š˜ïç¥ðDê-‰²ïçÌ'×C–à©aƒá~ÜW|¸êËí¹Ša·Ðly éX"Ew–Èò§Š~pÙÿ ¢D“!‘°Ÿœõ`F»+9¶?ôá°ðËrýôþjëb(·yˆ7.#Ìù¡Lͪ¾³åû.áfŸÛð§(˜ÍË={ÿFÕ™uXÃûš¤ÄMS/ºp£ç›ADü<øØ["õذ¸|ÛÌ*ü¤*ÆokÄÃs3-LòøŠÇšVÁ§–ó ªÜ‹{ľ’œ~=q¨;í¿ÏáíG0æf Z— Ñ{›)TÄ–2ßÒ¡¼5OwK±?­&øÉ< uÖÛbðBèZ3Ìm©ÿœdõ‡$2}øUsÊ‚NÊ[Ã/Ôâ; ^ÏíMP>1,æ%‹l‡÷ïÔ!¡4ÊöªÒÏòf|¸L)}õ=z¿LT&vôÆ ¾†ý!rïOArš"õטë6Ëì8D©ˆRÓz9îÿ€\qÖ„¨ˆiØ|Ù”UEÏå‡ÕÐdKžþv².v‘aÚo\Ég¿ŽÅCŸµÝÆÞÛÃlg`#%n`WßÅ:ù”"È;-bÉa“\#>\ÞŠ¿&Ç t'FãNmî­« ™Ö‡úõe&ž»­A_¬“¦ø%žë׃³Îס«þ+à¦ÇpY^*jÏ8_ šÖ|Ü(½gáÕ騰+ C|6Ðw[³pÓÔMu#®æá;Ý*œ©ð_Ä'>ðÂ]†±Ô]mÏ~X[g3#X0O¿–…•†©°(ú=9,½^ýÜÅ”^ÌåÍ®8·‚ÖžU£Gv=§W5pë!Ê ÀÞaFqÈåÖ;x·Úrüt§Ù%É=hoL2Ë]A› TøW>šºx]И ‹Æv²ëÆó\Ýzòï)75¥Rîâß5¾/Ö££ö9’£kLéÕú-tϸjXå¨ÈƒKŠ&.Mb´¹ †¼ëƒ7ܸœ³et‘x´^"&ÒüÆaÐPåßí³Ö¨ÐÏÝhÛqs´”šo„Ü€¯kÃÏJßðíó¿„4 ¬½` ÿ0‰Ââ¿)±[ñ…ð$9e¿‚?S‹U ãø‚#cøéÜ1tôò·xpÓY8æx÷ïëçR/⬧zL†|@ÏAx2Õ’¿ÓÊ7„ ÁK¹fy@d,T±z픟Ev-å.ß±ž3hÉÆòð+c1µÆ•Ä#ÇůÑù‡œÈ¦e¬~&ÅçYIÓÿzR/o^Ú 0öáò¼ý$ÏÏR„qMZÌ_fò¶ «¼¡ÁTƒ?ß­¥Ñ#ùÄŸñyêõÔdÙú)dò4}~:Ë [ãÒÌÙ°%._è†ÓŒW>´z²í~,ã‡-¿AÅÄ9å±>þ’ýpv¡"Ãob»G=öܸ‰â«ÁrG8™ë"[‘кï;v㞀—ð÷K>œÛÞŸ×kqš{7 ß|N>R¥«'.C£øA˜bkC­:®Â§_C¨©à0{sï‘¥Ï÷¢½Iɰ<«Ž¼ ”ÇÇÕîÜÝØ‹´^Ī—QýÖ0ÁA&,‘¬XåÄ;÷¦FsF¢kHèž×ó®îib(:ŒÁÄM*ðAò,Ø–*ReÝɘyç4,œ~™…š• FFV£¢]4V¨“Ñw0|T »Ùþ²|Ö2g陸ðu:—‰óO‡Fþg:½d…zBê°Px³ú.‰3¿Eæ¦g¡–Vˆp ߂׾ß÷a_°Ô%Yø6K?;«¬¨‹y DͺÄîH1ÜÛ” SÌÿ1…ùÌÙÈL×6L‘Ù,-=%XÛ¢Ç:£Yøô2’i5÷O¤¬Äœº+ƒõyô0ü_ÿÏ{-9ìlónRcZŒ“Ÿe³&Ç(zv÷t^œÖƒ‹­àouš(¯D”߇mP-,™&\0€ü ¬|{•<о"\«bFo½)À8[Eø9N@ô“2°Ýw^ßšÎø˜Xö&­¿§B‡o¦0ðMÌã€oÚ-é²ðxîWÙç”’i°Ô@‘¿-MžGXÑ”‚W¤ûT:_qÎ -OhÐøøM\%ì|r°‡¸ñy0K^’N‹BÃZ#¸Æ„"ôPÏ„=ñôit$—~|K'™rª%ɧxžvFhâ§”;8kh"H»þ¯ÿ«öæ±ø`§ Œ=øu[Õ‘_•Ã;ïœé¸¿ ìÕdEœ¥jÇOþijÕEQyªZ¯‡aMÇ hñ9¬6‹¥k øÔ¢ÈÆk(}Óš ßAê3B‚g1°ºJØ0U÷™²“ü(6ª´³‚“¡Ìp-/²Sï×2–lŽí+·“ƒ wa˹[Ì_t%ÊöÅ‘½e'g>o&ÑCã•â°aù4°ÚTɺÞQ…ÉQ0J) %TD'Ux|ý) +Îuûî3¡¬>‘IíÀñ')òË~ ¿³vå±°ûÌF˜à®„¬L†÷ßÿöÿF%áæ½7H¬Â+fâÍS±µË£Žá H§ÔùN Ýt¯R­3¨¶Ã&>ÂaØ^ž¹:ˆ–¤L¢áµgñOÁGÔ}Ïž¼†ûIƒù•J{0¶HG›µp4òøÌèÅ¿[§áÇù~H9‹i}Eó¢±ÏQaímtJ#{Qäs:6Ù ¥Ï†e‚§YÓ~óõ{ÀÖø{ÙŸŒº%Çá¤SxÈaNŸ̃–Ü c¦m¤“–{rß‘=Ìj³½û%êÝKF›ù«h—þr~^c‘àû' ¬;Œ“Ó„Û%b¦C.VÏŽYþ§.pîáÿï!m83ˆD÷0}ïjPSü$xpI…¦º=Ë6沚l~ˆ ½ïoÀî?=‡]Äu{¬¹Ã(vd–"uYŠ/°«ûde•|þ“LbÓ£a^£¿%À¿lO€Õø’ü<íFóZAâ¬u¨³rtf0hZúu¯Ö°€×ƒ ñ‚*e2a8¹uôþ½ïž³3µWˆu¥Ï{_@7înnxÕ.\]|Fø±tŒÿÌ3˜}rtF(£Èî#³t8}¿ì5‘¯;ARýOb³ãfqŒ1ˆ‡L¢q{þ;&ׂ;¹ªßaüdQÍ _ÀúZQ.3_…&—$Òî"¨SËœDÓ¹êµ8új5ŸÖZ›dÀhÙ\^±Ì¬¶ÝKõVùÝñÛQ÷Ænüü³–¶Íµ¤ë]ŽA¸U ¹°†žÚ9; iÃÊlѺYôé1G¼óñ\]ã>cú|… ¶<>ƒ·ŽO†ôÄTþEÞ°3äÎÏrL¾õýÏ9Â.¯õÌG Á»MÃ…“7¤ã4A°â:Hë_G1oªY ÖCi̺²džè•,Æ5) ÚEbtóás aþ×¾âCK0noÄ›0¯¥Ø`ÌÏ ìkÓ$y;§àÎëz<ÀPu˩ڊ†áäÏôn²©H®}az‹²ˆ¼Öc\b?+1Ï5J¸vª#þVòâ'n½pu³È#³&^ÂU{ ›¿$\zäÈïNëć»-h°Öæ?‘ï÷ςЊܺ2œf½’€ñ½‚¶…c@w£"K ËÁ—cðz™Ö.õdÚ'þ×ÿ,þþHÜ9õÆË†-NjLÃÿ F®Oaéæ/pr¤:õ=åíV“'Âݪßž©†<ãë9t6:æÛ¡ÍõÙ\)K•n|\’5ŸAøú)ü|YJ&ìƒêA…(²ø ~¹W‚¢]ðüšï‚ÐD‡ái~dLœ ç¹/|¨Ì¥çMCÍu³1v¯VXoÂô‚ ðÁ¾n/S¤YŸâ»íÇ…“¯‹óû¥ÈÖËÓR€Æslù‚­¡àx+”6âÜ&A•]}mØì¿dÅÝ<¶ j:ø_÷â]sxêº[X(¹‰¼¸ÞÂ>‹L#™ñÚùû† ° ¼~¦ g^úºÚlUe ŒIw zŒcL"@¤X†Ž}~ ´>m€Ó‚ýÄø½­ Å`tÊÉÐògŸñÓ±"\RcÄ% m©Á²Atñv2Ùë1¦´Ö˜›ÁÏù‰P÷¢:.‚¦îIVSJ¿øÜÂßÑqþò ¦+»Ð·“êÆïסÖ#agâ+rt–& :«Œ¶fãø{y|öl"žÉVÇmgÕiõü ðvw:¨ô6âÝi·™}çTùg‹ÎCàú„ýqì6T·!¯—Lãæ}Á­oÑ}[÷ †A³bŒö(ðÿ¥2½‚A3ÀªG—>t(+;Ï}×fñ#)#éeµþîqivÍÏf{˜x"“ß¼óX]‡ª±èÉ7ÄßõyFVe !åS âÄoÈûóЏXDŸu¼!ëgШž±tYäW>ïà_|b'77H¥ÔÚ.n? `ÙÌFÎ=-³Wñ×&q|¿÷bV¦ І€;ß]ÉÆûx³Çv^\¿€zåÁW5ˆïš4œRŠQ£®Bäœñ¼w'gö~Íx¿ÔŒYŽ¿¿²«ó¼`BÈ=¸u4^(¥ÀË ³¡"׎ÿ2nÀþöËeÁÚ\ž®NŠž=FϬ°ìîÁýâ„' —a»´<=¾çTdÞ^ Ë]Š×°8Ç u2ý¼vÄÖèÿr ½lÃÏ®M„H;U~õ†#ïjLæœúðs[*Z<ÌwåÜ‚shLÙþïðvdßWgÈÛ÷HCuN08 ›È¿lY¬>aïhº\±îœC-ÓqHÙ|šÔ‹ôÕ™ÿïAÝ}d" w2¦š¼Wì*gâÍZܾu(O÷%n»¾Âí´ïDÙÈ–I Cêj|}¢|e©\Îo¶Øø&úl'öFÒœ—þÿüø+z8†ä»{á…ÚñØžì p˜v’~¾žË]¯Ø);¡Xû<ÚënB§&Ÿ Åf1ô®ÆM¬0Î…L£X8ŸK¹ZÁq+?¦ûÞØÄù—§pÿ‹<îHÛÜŸ‡·…û®ÇX»Íœ²722·ñ×Çÿ—Œ$ôž[…{5‚È‚n{ž±l=?–d ž¿ÿ@Ùõi¸;a(¼©tæ©}í`ÃænV„ûÊñÜn¸uß>…šÔ:r$kõ4f—tåië…r\sߤ~ÍàKm#Xœ|:ù Ýl©ˆ&)Ø;.‰í‘o&û§æ“ñwdØ¥ÕOˆó™y<©Yþ̉ÙbämÁ7eƒÃVSæ¦î .3M`Pi ÐGÒ°E?#W[£èì>ö´q= YV•‰oaHÚPþþîØtð8¸Ë_V4’‡¾y‰M§ñ‡Ö|®·@ŽÔ… {D¿EŽ?U #WŽ`)âu¦ZÌrÇbpZÜ­awÙOë6È|ÖQÃ茩W¡zÿ^Öª˜ê·‚›1h:~ NÄl¨[gòžÂß-®4ªö.™´M·~EQ‡:œßP6ÕÃOÒüó«ýß bñ·' ðÿ†àdœo~'dñ†,Ìį鶠h#ŠN§“þL©¡wàqØ48¨kDÔ*xí/M¾ë”6”xÒq;;Ȱ”ip«k+jxŸg ,éóŸ1‚ìU|gé$ºÀ5–~º¹´ÎÐCkõùí|ºôL ¸fϤӄ·ØÁ‹ù¤pŠ Š ãø³Qï`V•>ÆèÚNàù.÷IÑ]÷ƒ‘O#¶¾îçÞ äøŒeÔØ£]Ë%x¿\™ _í«nƒàS 6‹Î¥ô‡£û¹¨ mnEÃl‹\W2>z;Ž·_Ã';®ãPI¼ã¹ÐÔqßkë Ù7ŽÀÚ T4Q‹‹]9(˜vБ Ña· l„j#¿a•ÙÓºó‹‹ íÙ/tÿ*KOõÿN?!òïFâ†þ-ûà5p¢N/ÙNº‚Œè.ïë±ð$ _3/:uˆˆ¯‘çê=l§Ê~<_smº‘]‘ãß÷ö°×áðŸáôv¶äÿk¯ý‚ í»q¦ÙsΈ¤ãnp=áL*•SÆ=×Ï ÜßáYÉs¸wr”G=ÅKbÊÜ`5ÃØ¿Å¸ns#†$–²§ÿŽ2³Ë?𞸠tëŸd34J™j¡W­]ÃÓ6RÊ’©Ä->líEHU äJ/*@éÎêï ¼ÖØ:~ѦóÛ¦ñQß/`z¯&wx•‡Ék¿ÓɯÌð²Ûl>þš4xH¬¥ wm§CYãòƒV´¾¬æÙÞdiÞŒ£'Ó.Áã¬óp9à+ñÉßMíψ`æµ,þdTy8úŸá‡È\1¯$ýOÿK8A™ÆҼ͗ÝdIfOøêV!uüg ák:ƒ±9"6¾$Ô·ó}ÝÍÈHúzÌ`š1ôHi~!™_–PYIO,‰7çr®E~` ØÚVbóöUp*7–ŸR›J®ž’çU“$pßÃ*,ÏdTÜÈ(álþB©œUö¨cµÔHj;[›gMІW"ʰ,,: ˜M"IÑo{°¸ º:¿Äjó9µËøñŽëôñ·Lp(ýѯyÇøq?ŸEЦ¨'x°%…ŸNÎ#—V «¥tùñµ ëüvºxmmpõË›ÎEß°–þ¿4É•h¸LÅ…ïûãö¿\<“)„µšcqká6vk&™^¶ýÀ"ÚYZÁÙ3 ˆ–å¢rtõêøjÅX>Õ·fº›Ò÷Æ/Ñ_¬Š¬’5€OgRP.Á’3‰Sè“ÿ t4¼Ðó%ä9Çጡ¶4ʬ‹Ô=»ùZï!ñÙx¨}øUŽÃŽ-1XõüFp¼ùØ Ì·7àƒŒ1¼Ó±·)iâû8LûíÜx/Mž¼ ŽªÉSÙ—qlÞ5UE¢–D ÄÿÜ€%“ð~ükØÑíL»Ã[!iŠ&d¤Fï}œ‹ °™µ8þÒØ¯;ÐO?¨Ô}g8bµŒéÕ‹Ñìß–<;„Ããi»aÍ…×ì±PŸ÷yõ©v._ß,ô´Òd ŽíT¥+ÂW±ÞUÅðäøð‚t½ØI¡Ñ½ Swf]m†±.’8U˜Çv¦ /%í ³•Jà‡üg6ê~,ÑŠÉï\ó¢örøÍ¢*‡M¡uêáÝ•ýx_QžiÄcžeÂöYLiÎ÷å¹cþïõ|ß©ý°æÁ1T…Ëë†ôï”·xëÉ úßÜ‹–hð/âGYy‚O|?äÄãú]|çŒMذLl?¿dÉ\ßõ‘Oe¹ËÃ\"¼»–6‰HӵŦ˜kÌžþž'hþv‘iÏ>Ïn\½Ž]!«‰Ž`aÝjæáGMYŠJf,¾ÀºÇ­…U%ú¾=Š®þ,È•à÷àѳÅD½²uW+pŸÛ ·ýÇœ™k ĤÛ‡pß©nÍ£±qúß„¿×+bË7¼¼¾;¿KÀ×h;>Écä>Ç”~¬3Ý®J-Œ`I¹ 4Þør¼¬LÍ@-{8Îk{&¢…Âî’ç‚Ûžcy´f1•¿ãÚXÝåNäV.¥Â ò¿²ßRUøåŽÁ±eGPkÜ+–$© Ÿ²Ÿ ¿ß5§;º±‘ÜSgû{(K5£‡=ž£¸Ù èkâÎþ¹Î>©Ì?©(ðø’{ÿ˜a¸Yš*ÀЋ~̪è{KÊÀFÁœŠˆëP©)EØóúØ—Ká÷3é¼mú-à3B‰Èå6<1­.¯}ÙæÖl¡ËhlMWäŽ1½Â£ÌØ@è¿#øÀ#ŒtxʲX|·©úÏù¹Žô¨Ãüþ°3fö¨Lbè( †ò×áÄ#£­ÕGÓýË& Õ¾°jðøyõŒð€§ä@þ»ÿÇZ†¯‚º-Ì8v°T»)¦Ø"ÛŽÔøÑÛ ¨b»Oÿ}#^ƒƒøÁOåÄì«$ª¶íé?÷„ýýs–Á´¯6“ÀåN¦5¦ ã±Õ~2| •Քσ#¿Âæ4ˆ×Ö§çnØ ¿UKÃÕÙ’8nJ!9¬­Ä¿,“ 9ÇWáÎŽdR76éR[2¹ÓÍbVâ7³pŠc_‹5¡:?]Ôùï(Ä€Ñ~áŽ-¾¢“¸÷.Åž%­äÉÍ¿ #7?OYi…ð}µ<÷’rF».aúÕ“ØôÑeÀþóÏCxR…py•"þÁqóD?úÓ{=Û´Ã'N¡–š«PñÂ5æÕÇ÷½+…5_ a¬ÒNÕ¯aUÏDÜÿý.ËФ£o©×FËÌ„jªdk‹5Ñ'§ÈÓÃE.t÷ÖزðŒ)F¥—H6;nÆ;ïY¬ÆS(´_ªŠóèQ¢LëJ|41c Ì5xˆ:í–ÐVóœÒ¯››§’š}>¸®Fâƒ/@ã)ÿEcâøÓ‡¡à™2”^ ©‡L‰lâÐ*µúÂæ¶á¦/¹÷ÇqC)Ö~KD§é§ .nÎþ¿ƒš]êfyà …Üo}Øm<†^/ïïs%†[`Da?zŠ•*yáöq7ðë/ª×6ˆÖoË€ˆ ÁùAצè`‚¢NM-„s;f™I`?½ W+ú`ð:oö㟹M(¯Ÿ•)ÏnNe8uìáÁ[~ ï>;¶ ƒK&Ps¯YO¨¹D¥!±ÌÉÀ Bn&Á†Žõì“M=É q ÍË’Ù‰ôr\ÞQ‡‡€Ž™ú›ûÖ±»qA¨.…»ÎOf Å;Sá¸{s‰”g)íèÂ#ÇaEì+ÔLû%h”k¨ÿÚöómùX÷ Æ}íÛàhaL·†Åïv ¼ó\àÇ“¨6ZÃ?]Ψ}ø[ £“‡5³øÌ\y*<=ŽMm!e= }v>]tЊÛV|Fºk2®¸7‘æÓ¹Î:®Ý´¼¢gÑwÿö‚þŒDØ—+Ž“oÏæ—jG¬}…$ReΊb}ÙIídØr¯Tïü@¹Üpˆ¶Ýókéë=¸{Ñ}¬¹Vò&·‰ä‡“hû“¡o‹,ʋͧfÒÇ+ŒÆ(|6â›´ ™å¤NR`€Žu ì?2Lÿ ꎩ€ëO 5ÑÎ+œ¬s„eù2Ì—«Ñ1;ËðÕPIØ”^5fíÂ¥ù_pí–v g[ce`üÖÍlˆî\~°r%,‰?ÅšåÁŸm³ù’ýIè¢]ÅÚŸa­+àV{´–iðÕ¹×ÉÈîhpQÓ¦›eæÂæþ¤ÐRÜï Ù}Ü:4ÿ·9!3c¿àÐwE¬²^D'¨8‚ùàx8býþÈ&N•èíó±ûh hº1&}ë+7:#\£=›WÓsÐXÙƒ¶GyçäWx7*«¿z }»ÖáÁ…³‰Có)¡Îó”úJPjÃ`(œ c8,]s/Jl…úeËÉÙÖ0*=žÛƒÍFùìC´)ðOú Uµ‡Q«2)¶!'Þ1gúÊ`‚¤(?Wb ­Øwk%,%C?ªX(³׌&Æð·V‹=¾Á4Ç ¯) ÊurèûáÖöI ìÿ’’ðáG31>š^PÄ¿Ýqpoùzð|9ÊÈáñªÝ85’D‚ž(Áº‹»Ø/Ú ž!g`T~·_‡ëî(ðâ~ž1tØG(-ÉÃ+9¸êèW(쳪üÄÉ*XyhìÏoÀ/á#™ù‰«ýïÑ‚¼gWHeT!Ö†ìÁ/©‡Éóm¿ pƒ,uQXJò6Ì—ÐPxov”½?VŽgßf‹äÕh ¯NAv8&䣛µ%­î;ã×üD½•°4GŽf®ðÌ}óᵤ»r©·{wÂ$ÁM\·ò…ðªEí@üŸ¾—©h»UÛ—aá¡/lÎÐýd¯ñw `®Ëí'‰ß³™Ä±fÁ>óQlò}w,ZljsÌøÂû&®>oñï: ˜<ó T™kÓaÜóÒV`H±\ˆþy/ÛŒÑwS![¼Ф ú{à™ê$,»(F+Jÿ`Ÿ,ÌZnŒÍcÕiÿëÐ3Ér-yº i#Ü59&¼ÚvŸ&)“\ñŸží/ØoÁÃ|ŠX÷>áµýq|·w!FZþ ¥Ã´Èê{[Y¯¼<þ‰ˆÆ¬öl×ê1|WÃj&9ü5¹«¡‰»Ÿ×á3ýÎü®yUì–Ï4xÕ1G5)Al9ÕÁݤa–j™AÉ+I¨jÙfÓŸ£êÆ8ÐÜeÇtwG`áµÐ®¹ÀKö$Ez°–šÓ&á±NK|óá*žV¸%Xá$‚M›~ Äae|zS–[^&ðqRæ]>¹Š]¸cÒ§£pÙ1LJÈÆé[ô¨šÇi"’lÈ 2Í@ZZšŸUGž4A{O1Fzµ£ë^|¿í3¼óÂ6]ml© 9~.êžÞb bƒÎ g·ûú Z—€C¦œB“)Yðä#ªvÌà»Æ0ká€ÿßÊ·ÂÝG1Ù«ŠàfS…ºñº˜¨NÉÓ§øaô-¼pð 3¼:‘ë…l‰p½ëK¶Cóhì¤×&ëΑ§õ¿HÕ¸U|ÅÝIÐ$Ò)W…)ö[Asx>œ¨M6Ð^{þyj)¾³xMJõÅèÌ—µÌɪ„Ì3§Æb·á¥´7.Öç×r) ™°&åEB•ÈS6¾ð„d$ÂnÉøílÈí寃ÂA]ÞÖmcç(’ŽÇ ÿ¿û»Ö.Àe@lIÿT½„:;þÑ:†¢Õspéµp8_ÙÆœ+7áÄÓ¡/ÿ¿ûß ›ôÆÁ%ÌZ­Ëg~FùIÓB+õ:ïûûøƒnú-~<½?¨„LÆ÷&ãÕ]#X‰¨5œñJ|QLF’O¾ö}ôW€!L$/‡†ª4lLÞmšZ"½äÖiUXuŠa÷qtÇqpÐT„T'¼݆/$åW:·-ŒÁÅçÎAs$™5z1•ІXYðu;¯€°™ÐpL-´áy׸<Öã˜1Џø]"ºÁ 4Wßt ^…ü„Õ2È툶6Eš¬ 4⪣_âl{ºò°oMÉ^ :>€?LÞ…ßÓ×qd”w8 ‰0 ³¿…¦"î7잷ŵ=¾ð§1žåø=b5«u±s”ûu~ hͺCÆÝqbðKC~^þ!Õ‚ÐŒ4ô¸¿þ¦$£Ó<â !ÏŸ+ZÑCÇ„' æÂ¯Mr&[@æ1¤Å×E/‚×B/Py{‹È­L„íÅ#AöÅL¼ õ6¸ƒÁMê´%ð+” ñ㈹‚²Or|3¿Ÿ7ïC«UNÈŒô`ŽJ. -ÝðßÜ`â-1T“LfNû—ãès+QóÒ5òö}/K™,Á½'ÁöÃâjÐH²,Ä^4•Û f}øR÷9YgQÈ ¦þÀ“eV쫜4ò¦t( ú)á°»“˜Îú˜jrŽè©pU««h-ÆI÷N!y¿Ûžžç Ì96ŸÆ½‹ÆË3áû‰¸|ýɺúà§ yì…þD>µë.”zùã±Þøc„2/éåñ÷fpýC‚ꇀV€]s6ãSn B£ÁàÖÜí‹sÅí¸ØÉõÐtZ–>=ÉñEôœ«®ÂÕ!Àíü©ûÌnn:¤ßg˜Ÿ'¦¹Ý¬ºá9´âÝ˧A8øý_Ö ?)c6 ³óXPsP  êŽã½Ë:4Ð9¿=P‡Û™´Uv¤‰¦³j­áüs`2üŒë›¶MX¿í>øž\À;ßfü6Ê\>=¸;î?Çž}ìF×HÈð~ÀF/ĶY´ÂŸ³©°Ù¢Ž âPvv¶Ûð.WÚ3ž\¾¶½¦-䛼eŸfÄRçù/À=ò'äŠÿÜMÑO² ¥ÈóF¾)nƒÍ‡ÿÀÜýÒ<\Q“˜¬åÅhîæãJçëÃ4¯xA£A34ù¹pù)…ÿæP•£øÞF÷ŽÇ>óE¼óoÅþO‡L™âøùxêŸ =¨Aå;Z‰ñÌX¤¥Í¤~Ãs˜>gnÔ¹ªÝV㹇diÖn=¥¬6=ÒƒjÿHƒÖ¥¾#µ®·^Âü£™Îy nF[pF½f~8Œg採[žN¦+kt‰yÁ~:±ž§ÝbQùû¡ÌÔ™Åú˜ßçÐ!͸êóc\bÔe’:Ûž&-t„_Ÿ¼@aK?^! 9X…x"ºäNxOôDåS¶äKÃñY¬5ñ,Šœ%º¿Ì—íJ‘xäZQCîlÍš4õ9 ެk€ÿ~«šˆµß]áœUŠt¸áfùttX†Ç..ŠÙŒ:²áqJšÏŠ}DΘeÊÜc1»úœÄñ¼H„‰«ð“CwìÌß|<Ägò‹·â Ÿ™8%øÑ󛃇ýiXu¨`7õ¢GÆSËUÔVé ú²[¬Á>»i»TZ‹ÇaOêQßñôÏ\\ª³×\Bsi:Q¯”g^ë_ —¶/:o†²Í”¾uÆ; è™wûp«A"¾ Rd¹Þ4É6Ÿt‰Øà f¸P„V4ì!Ã6FQÁýÅö¿TeJ žN%ë'µmê¸gþ\#ÂßêV³ƒÜù‰RC:m>c#Ç“ûêݬª/Œˆ©ßÇ(ÿ Œ|L2+å¨ì³{¸Çà5¬¿Eçûvá‚€4̉w›F”Ú6×ÛÄ¡þ¸ÅxëÓ`ˆžsÚRéê%³øò…ýñlÁo¶cTŽM\Ëܳµ¨Í`7j.`§NlE͈0ÊnÌ&£÷V‘KR–4àH ¶ê»ÂøKt+_â$SzlÍHÖs>®:ÎÎ;‡GE>‘ÏÃÖoGغ¥Þܹc&Ö^›¼tôªûÊÆýLå5äâßÙ\ úöö»:ÙGSpïjJM„çñ®Ç8~RI÷Ï âƒêòª O2Þm;Ÿè•BÛ”,¨£A8&-Ö"Q…›ÓÎn‚÷5|o§+ß›³›LyÔ ˜!³H°Æì"¦’s·^e~Ëyëºé´ÁÄ•©Ôÿ[&ù×)Œ˜3_žÆŠ(jê´œ&שò†ùÅhzà&{°ø->ù6‰îŸ]Á”mà§zðñá`áŸIßÎë†ý3Ûà~7ŽÆÖXñI:ã¸mÆSªŸSÁTÇhàqKö-z/Á“F.¸1®Ùùñ³–ÿ»þsäiÔ¿!Œ½œ háˆýržvÞó£×Ï}F oeºIæ(Xî|«V÷ªv·’Sz°Lÿ<Ú/õƖ醸%ˆTÒ!ìœ]›Ý%!%è˜[h~õÒóà¦ûN⭃יøïëà»´ÂíèI'2¦Ÿ#V)݆ÆÜw0u¶8Ý£‡Ì'N° kÓ‡’Åz#höín¢—gÀć?€û?螇ø>÷ó]jÎB+FÑMJˆæý *1_ ¡i{u0æé!p)?¾?…Ï0 Ãw¬‹6¢Ö"Mê6Ó†_ëys/Ø’M+ÞÀÞ'óê?:*¯ >ý?,›8CLÉC6kOᘹ®dÉÞkr(×ý–Äå-ziO»gw "û5oðÜø ÚÑäè5V¸þ)<‹´òm=‡š:£Ù}ûËP?Èÿ ˜LwÝsÃⓃacG&n/pç»2ñkz,N[bEïO˜É·-;V3AjD„ûÒ ƒiuú£Ê&±¤»üìÈò‰QTÚÇ„…Åü žAdv Þ+P!–WÔQý½?zQè¨4Ø?NÖ—’Íæ¦Ø4êŒp«ÈþhØQìðñ¦ý8À,$ |—§A2°¼÷ŠQ¥7þWÿ¸.¹nñ¢ ìÞ¼tÔžV)\ŸæO¥}ka󩼫³“¥ììOõŒòÍGÿBgœö!tÒmXåÉ‚ö#‘ÍÀ½C>ãx½2ÁËù(Ž¡°} ml®‡Uçàƒí.ns=B5†°Ú³3@}UÚ¥tÁM’‡/Õ8¨>j`7ê4¨dM#ÑaÎÜێÊtm8ðSƒºå† öE;p¯™&|Ôû'c%N³¾eÀŽë’ äCé®;ÙpC#V8xcþžCs•© ¥ÌÿÕkàƒU[ñr à×a/AêïD”nµAÑñëÿW†©6W„ÝbËÜâô¸<(*ˆ"‹?ã~^G™œ„¼ü1›Ž³Œ#­>‹èëzyŒ¾%Êëmãðò%Q¨‘ò¹ï øqÛI(+‹GŸí[qáÏ-/¯CÙÎte‘T¥4¸°S‚[_ûˆÃêqXüýRŽ—ÚxóhÝ›ÌÃĆzëòK³Âó0Ìî;(Ïcgý…­æ`¹f_æµ~Îi"c4®@XÎâ\žCmïUàÕqŠB¹ÍÝlxÉ®²¶æ‹På-•è¥zÄqy[ ÿ”?cÅäHx¬C« SH½[þ@ü¯ýiÀf*€¤0W˜˜B^ÿÁkOzò¨àpÏ)Rèð½Ó¶§ãº3Í$}—ÄV±£ç¼Aô°ßøs0¼*BQUwú9‰Ã*Ù¡|lª8%ÇáhGïleûdžV()p™·‡a±â}ò²`:o ï§übv·Åi·ÿq,·Y÷¿½¥£ÎZó pà¾î’xû¾=‰—›H7nâ#½>ü0Á”ŽÔk²‚¸ûÆi8ñÔ\>Ó¤Wzn¤RšÏÀâu8ª®Mæûul¨‚Ï!ì?zKáÐÜßxU댒qäWGÆP§¾•`ù7~ÀÿNÕ‡¡¶aëê9ô]V9¼’\^ç~oÙ½|%÷Paýjr]‹õß̸׳ãøéÇ ÝLìŸ,Ý<ûŸÐúÃc<ª¡Gvàú탩Tͺ¾Õ ש ¦“äê¶]†bKI®¹Ö‡+µŠ`áå-xâáF˜±xfr†KçÓ˜Ã?@|êj ±áK,ôèý¢: z“ƶíÆ©òÚ7Hbä]ð•Š$3‹7‚fŒÏpB-_Ð 9ÉÐç'^“™†üaöÿ7a[®9‘¬z¹w\Þ }aÄUò[]’Oï¦ÿ#¾‹ú¿]…‡dÍÕøñÞÕŒLžO”VnÀ_J\Yjï=è U¥Ç Ÿ@Ö‘‹à¼?¶þ:Od:éaùó˜ó²uIÀ• ¸q™]ÜyJÜAÒ.Á{[!Ú¨¯×T²MÌazº×›7 ;fв£ñ€â’Q¸Òx~;í ö5»RÏmŒ|ì—vêõFxü5†—æ×á É¡tÑ´Ð[¾wE×½’ˆÃ"×!(ó¬¬ôÿàÄÞë(B}¯|>m¢|ùZ ª½?ZƒNSé7Éò[Q|FPßœ4°ÿõBÜ7Ü?ƒ-ΓiÐätBÿïõ ô6$ì—¦BPáçÜdøPç6¶¸å¬rÁ{C ;[ò³—–þ÷||k¹¯Êã[ºªï᪯&ôîCŠ5áÔãŠ8ã]HidûÎei6b;‹m‰ñ¡Hpó”\Ýʺ–~„•¢|ñ8AÍ|€L«odBî"¾óÀFˆ»ÛgÔ뙕F¿.¢Eç|^ >ý¤Jµ+—›ÒSKäh÷];~Kö>y³ìu;u ~>’öCø¾é-p|YŸ ì_(ì¸"Æ/¬pbg¦½Ä™:ÿ»þÇ{¸(×ñç"§5AiÁtºIW™+ûŽå‡òÍh\«!žäæKþPD(\ÿÁœ~ß;²Nd¦m \Fw.Qæ[2W¢ìÖ,^š/rqðEclørÚ\zÁA1ÌÍjÞ˜™xKxø½$·ê ㆨÒ‡—q·â•Ïjè²ûH½ú~½7ŽÌ¥¢ÅÞ˜/Ü^)ÏýÜAÆW’oñÏÆ«ÓQƼïö*~jË;·Ô”îé,äó~ø‚±ˆ¤©ñ=‘ÅÐØ˜—“·òw¹ÊÖpfÅdÚ©vFvAÊ99´š5•·zr3 åÿŸSM›'Ñ“Ç4ðŽ€ã™Å-àkòÞ–áK¿ç‡û´ëɈó5+sðj¨<ø¹”Þ™ËÄ®-¥ 猸ZŠ¿ö_¿2—‚g!Ûù„[øÌAÞüªh ÜJ çü°ÿ(.ø3ˆÇ Vò¡7DxDtè(Ep‹ƒwðȉ\ò½ùy½ÅǦÀ iêþGûbÀàÒ;Zœjˆo¥;2|Þ=CV?Ÿ³ŸÊ$pQLµ>ƒÞS“àù·cônÂ6v=ö,¿´é ôôâé?{á»óhnôy¥ïáÞ„ù¨P#‹K·nÀ¥^`Ý“´üïZülAªïÈÇ®Q›ETÄ)’/hþ€Oúaû)~æòˆlˆ¦ñבG%^B;ƒs¸ÝV‹F•í¦9•à¬Ï.ÀÃj<9®•v€÷²0Ö†eøáÃó®0J]Nañ­J’á~ƒ> }‡gòVò ¦i¸.WTÚkÙ—eçÁU/Ÿ½  ÙÇ PÂø1VÍÅ…µƒh¦Ï~ê;žŽ’G3V14˜Œ-Žè0gLŒ’ÄîÁV¨ºu ß9"ŽßXÀslq®x¥°û»6=ó£‹é[òe‡#³’4Á)/ßÜÿý€²9¿²#—N—lªd‚~Û¥èN‡”½£Î—ï¦i1,bšZ_- b·[³E·;àî„E<2ì>9ë«ÊGHÓ»ñ“hµž¨|nÇÐøp8—hE[‰° :ÒÄa„¹œoŠ–¹ÒtÜÝfX¤g=ty¼f!(ú¾h¿ÉbzÞ+é/u1ð:mLådléÙ…×È/í\¯è5Nr‡†Î0Ò½ˆuèOÛ7Â7'Þóëi¹êÁ³«{`Ê 5Ìas(\Íè„™WÐJzè X[›9¥©lÄò-<{´ gƒ9œ’vÀOg‡pÕ»²´h‰´ÿˆEÃNQU›BZ8Ep†(DmópëeaâËnõå-0žemno~Ü‘ŒcÒ«¯—¥ ÛˆÂP= —×´³Upü«ù””»¾þ ¯òmèÁïðbY4}¾üÌ2Mãÿ`$é{Þ° ›ï7°jqš7¼ Ùj‹ÞŸÇ`•ó’VÂÓ{Ð#zðpØ?AG§Þ~%Ç(b]¹sFÁö$;v)¬žËg$ îVÅwÝïë~©»Âú†7|}\?oèˆí‰ë)KÞý×Î^i «Ç8à\±-u{Zo ï<4¥ã| ¬þ†³HAÊÿ×B/ŽF/ø½auö†\ßn:®m-„™Űa­HAj,íý:jëIòoÐø‰qÓd ÌRT§o0’>wO„ÉûÉ’J]®mðjû*t\£E˺ë!!ÞŸw7ß ƒ&;ÒU›¤iŒù6¬]>œ÷ÌQæCwˆk?Ä»õ.ñIÓd¹ó…,v…-õø›MŸ6& ÄÑÃo!vÝtfr}ÄçŽÄ‰ÊŽXz ª6ŸÇ®/åà:Cƒòa€¥4¾”?¿rÎíùN_ü”©Ô¹Ë`Ù Ê¿óžíËù„ž6ÜÙ0ˆª{jñáß›°ý¹Nß¹jøæƒÂâ[dT°Þ;« ë®CÛR,÷ÚI÷ì´â+¤ªÁgiÜxªCoù† "¢àì÷”[¹wØ+óïJFBœÝ †“øÆƒÉ°)`?œò Ç 7ÄnUMÁï¯Ñùé\:Üa¤'ÆâÅgpf×`t3,‚áWŽ’yÁe ß¦¿*õñUИøÿmðs6½Xâ—~aœM6h ™Q9ØöÍ„ÖlØk†ÒÑÑê¼Wc9¯ §UMþNþŽ–OÑÑlÿtB‘Äß0Ó»V¸¹g¾myB´Ú'½MÚ^Ƨن£‘üØígJEpŸ5Ç”maáFÔ¿Ö{’["Ôäf^z‹ªÂ‘ÜDÇ,-Ænß<(ÜÌѧr0n£š4Þá6³Ì~Ð\¨(ÇÃDê_ÆáZù^”ÿ¦Í/M_EOèѸÆc/'r¹«ÿÜÚµãùÛïC@tT6nÛ¿Þ¼Åà<`ÿ§÷$ó·Â_ÉY¨4î+¾+£æñA[²[²3ðË: öá´¸†‹ñ‰úò¶ 3y<äœß¹c|ú?»R8¸i Q6Oo9"x1Öt™ñ;{•ø²˜¾9”.ÀžDhÚ;†³b[Þå"„õ¬ˆµÆ™Ð*¹ x¢nL¦I*ÑSåQðáðÖ÷«’>~^ðç‚5š­7!{z–3ÏQ,Ôô.˜è=à òä÷8O¾ûm-y8ŒT\È„u{¡”^&¢w:ê,"¯a•—Síìsüü[TÉi *W øgŠ{ÀÔ)"{Oø¿†u +¶x'PÚ±¶_G XëÆpÿ|2©6.–¶`ñ“…ôÕ•JWÛµ«©Â„$¾xIYð¤ŸëÆD<Á«óÐ绊Wî ¹G¥00²™6_¬‹®3…‚ÓÇÙB·uüñ[ }U^=+Ã…£×éT\=Ãÿs€'›êêbŠ·b€‚µ0©‚”qª8ññ?°»bŠ~§|©e²{Cž_Ú…é:BáKrÉ`+Õ*U‚IƒúÐ)þèÎl€Îw?~½ŠÑšš¬eñâ'”8òÏ^Î!¥·ÃÂÛãùùä£úQ”xY Ø¿cG&JGÁ»}Œ7öÚÑõIùµÎ¥¸u± #õ öT¬á'Ëè­ºô‹Óq¨qـٻÏÁ[•>w“¦ åG>ÀÑIºT1j7]6˜ör1P­¯"½¯ ¸ºkµ±£žÏ ºSû}óÅ8Æ|û<ªÄ@´Ã˜–ê¼ÇŽüŸÐ7t<~xœ§ÎÏÃÇVfüçëZ¾úÚV¶ß7Ššâ(8wz(èÝ 7ûã¨ìÑÕ¨ö)žê» ƒS3÷À‚mn\îãFn´Êˆ+¶¥*>™ü³xj]8)(ÛY /•¦Pjė챡ú ö/V/ÂÝͰyQ•ÙPÆ ¿ á·½¹áà!f†ÓÿáO‘;0éÊ|øöׇ¿Z· ØåSh­ìç‘håMGUä Æ?žB¿ Ž£ÁvÕ V²fOØÀLÞ…øÎ<ŽJÿ•¦,m xòRúrû{œðôºÐ&Úž6o»çö^2 w zºÃ×*†àÌsÿÐøán:¬K õa5múÆãsEîà„MU‚š/ ‚ä]\îÝLª{.•l—åóå¿Bxè"d"5äsÛ"xw(ÍÍÍf§}†HýOÿ¹ú>!sOù ÌJ„`5îvüÍNe¯‚5òRè°T–ä]Wã§78`Ò†ùÐ÷(…G\†ýû…0ÊOÈÞ­Þ‹ác=Á°4ž<]_ #OÉÐÎñòøáÂ`úõ•õæ'pûK;¾fù;ö~Ôbè. Àm¶vè¤Pˆ £;1¸Bžý¹'“O-E‘ÝC±Òk(œ6•§ûýb Ä1øz׌{5@[™ó©W°¯îFlýÅ/Qiš íÞ ²GÓÒ’t9ŽÄÚ1Wq„È9¶ö;D6ÇBð„\b7o#‹pz P“Ó¦e#Ì1å² ¿·Ós¿½E)kçOë–‡@¼áˆ}cÎÇXÍFg‰ øVöÆÆÁöË• WhI4œ%ÞÊoÐå‘>·VNE Y´I½ x0Þ‡Óî ¸1ŽˆQµÕíÄÁ(×l·€’‘®§•²ZB½’WpJ¢†ÕÔ b>“ž2_Ñ»Â]ë´¨ÒÔ.k9“ÒeûzÑrñÞk™ ñO,©CyNýž‚Ãò<ùšßêþ«Ž’‘§Ëvéдü#h:AžøãÍÄYÐl‘Îp‹Õ t„ÞÁW˜Ñ¦Ñd»lhÊ/¤Êwã±-U4 ÿ4õ†@ÐÃq¸jJ2재ÏJçÏ¿ˆùžZdÞÛ-‚’Ú1tMöaä5?â¬ð^`4NR2§Rím™8{F,]úZ›.¾wG$ãåµ9dl… Ü Z ­ú[Akÿöw\ j-TCE³§ µy,õ^Y‹Úñɺkäð¢°kñArýóXáµ’]§Éßë‹ñûp²o´)]_T%¼•:–ܾ=’œéÊbÏv=À€7÷ÑØY*ìc&LxýÕBÒym 5ÍÆ¾ËáôÒpòX,K*Âô5j(þ ]Ç:ï ¢]ßUðz„1˜R5°ÿ5&¶ÖëûÊ!Þì LÑâYîÑðÂ}/FuÌ{ÏHâ¼õ Fæ«°ïSrP˧‘…­¹'p}ìI'¬‚k+ ü}à{>Ø a¸) ¤ZðKÇOÒ:ðþ´ ÿàú‡õMt†}#:0´Á ” Zðtd ‰–‚×ÂþÕRpù:/¸ty6›¸â.„|ZŒõár÷PR„~³[…•vÜoL ¸XÉ(®nO>=FÍá7Ñ“5°¤0'zy«9_ –;vÝu’\(R€05ê,¹·½B¢=Ü.EaªYòÀúÇù) 5£†=P0ÀŽLyÜ÷ø”…( Rv,AÇ<6Õv:]vÂÝíóéÚ’#~ý#‡VkjσÅÖ¥ä“M=\oˆ%ÔÖ;õkñìk ‘(‹¦sÕ§Çå1…8c¼º:Þé,ƸYÅ> käë°ãX&Ú«ÑÊÐ^vf]&Þ𘠛Gx`ÿ8Dûk®uìUKOàkN½´öò“ Vø9E­|dø²õ»YÚª"œ®!dB±q8<ó+Ôn*Ç·ƒxˆå$Ÿ2Èb‹*ü+Z†ÛZ†ðu:ý:Pš¿Ñ"\h÷¿þ¯kìRÈ¡žv²¼×ô,±a§>êW-„«Ý –x>&sž)¡Äú:8Xð ì èÌÖ å+/cüÇ~Ûf|š\éé{nX8¡ ç3b©ù¶à>âž;ù+zƒª¡'؆ £Š%î-õk¯Ã¸hè$ÜwÞ‚ÿð™ÿÚÁÄ|Šð¬ž8©¼¡.tôôÅ¥ŒŒP„M§µyǼ¼b"îK:J–ÀZû54izØm9&“’/Aà»`´…+ØHübšž2©)ºäiÝbðÏ5ããÔIÛR îÑà~ðÎÚ¯ùoßÏhøG3°9ù.ÞÓ}<Àçpn?\B¬ s õO *Ô,á¿n›x‚hLŽæUOe„ÃSDù»V):Iê êø4ÁÊq¤lï!Üuµ?íC§¯AY¥Ý<ž$ÄGcX\A%÷ûð„Yý>÷Äu'ÑŽ°¢5½vé¼èySýOà¶­!îF8Ä‚+ê–¡ý2‡¾ÔĽâŸq,v‚¾œ=;ˆî½Ó‡nI2Lêï><™&NÏ™äÁo{OôØdÄÞÎ? Aw 4¥ó'É3qúÌ.ûµ¿ Í&øâÛ4æç¿mÀþ¿öžq›KdÉÑ(ÌÌ¢*0¦* vx=Â5| XW™ Ñp9n3•æOðzÁâub&ȧ<µ><[' M¯¬éçâø°z)Èî£IJé¸+ï4Ý ÂÈUÝàÕŸ{‹NIÓ³²ñ"-9yIÂa™‚» ôùU•¿ ìíˆÂQ#¸²¿íˆ‹©(OÏ)߯X2á;˜/@ }hËéò‘*ø¸î®àçÛ Öëú>”)â›åôbòFìSø\¶…M»yLug3±rizúxø î…WÇ5NmRôýìsÜv@ÿZßL󉥨xX&Ñ€?ÅÅø†oáþ°8¼Ìv—vîƒç©ßÅkyM 3,À°/8¡É“~2µÆMšÑXd›hÝMÖç á3 ÁwAš–…‰u;ºî±8E"ÜŒ.Åe?$Øîj-T³Lž7ÆG§uè¡óD$¹ˆLŒ/Mî¶n<¨ÞN‚[>­Xþd\­Q¦çÁ˜ß#­MgÞ°ÀÿìC[¤9 ’/MgCè`cÁì KIzV;Yûy7z¹\¦¶8vTg*<}ܳ"÷£Çý"ܘ©Â?• Æ0·b<‰à¦ÝrTuv:8ªbgÉâµ/çÑü÷8¨Ã'§½W#Ãç—ˇ0ßÉ»noð ܺI‚z§\ÀÙq’øØã <·êƒî5ê4ºq”GHPÙjü'n½ö8?·žéÙòy“)‹Ýùøµ%ìlèIhéAut²žç‘HLV i^'ÛG`1 +„÷fñ]È(ûxfèªÊs‡¨/)ÄsÇzøoÁ-aõíUÌzž3ž ‡aļx˜«ñ“=½‰©Y8´ÿʼnp§ï)4¬„É©#©Î‹3‰Ù«°D3n¡sýð’²5ÿ9•]—¯¬:á ~?ãAoW‚#šÜ ûB/ã2·©´q\5‡ÝÆ`ÛN,sJãç–†û?4¸ù…+‚K»³@»y3ÔÏÆºâ62ÜÜš‡v%r9sQ>|Ù¾;æ{½oi´9G,fÍá#<@T٘fbM°)Ré‰s§•Àäpe>Ne%ý»¬ŠH–ÀôOˆŸ¯Í"Ñ®9þŸ’ì ›²fbëN-hÛ!Åž©)SÿÝ‘ü³Éô;Ÿ 4_‡?æN#£›QU>‡ß×¢kž³Opšf2«³VÆkJX+æä¦¾¶®=>Ѳ˩˜d4¸%'@‰áv·RqWºK/UpÐ,–±ä¹`§{«ÎmÉȦAGŸŽÜbp.å8uãۧy<üëÛ‚z¹Ú\Þ>oWmfÑΦyòÑÛ5aiÀ>˜3ëi;mGã³agKÀuÃÈ6ChÑGåŸ_HÃ÷cäí:}"罚þò0$;[ÿAéÂHr~ÚSØ.1z¿äÞÓìý·âø’½ù¹ë¥þÁ±ò¥8)» Nê¨Á­ç_pÊ_)Þ¼x?1ýðÙn”€S¶ÂÔãñÁ1o>óò#PÍ(†Mštç!üqÇw”×L$€jëçß#I£Š˜—g/Ö@'»xqdŸÉ«­ÊX‘¡MmõJ‰ýP1zØð™xîX}€…÷k©àIÙ‚§¦C®[=äQysŽºgÇDfõsŒQ†øòìPñŽ}\ø}Ж ë{ñØ?O¼¹´Ä TXžp‚r ñ¤»0*ü<>ºr›eû?ߦn%FJ¯ç ¥Sýí¸Ú3äý g5Ïwé<ÐÂÞ±2ܺ?ÊG'–BL™;–7Ân©Et'Dœüóìí‡ÓÒ=䡃(sR‚¾Jà¾=ãù ÙµøwÐ]¸P;’5_L4VÁé ²CŒ©VS+~xöŽLÚf »Ý‡PGß}عäÔ=ÎO™ôç‚9ûÑÑ9„ÿõ] ™¿²Ùó‘xŠò;ê}Eúuж©_ðúaG:¹§Žkî’ga#Ò©T¾+Mí÷õš@Á•cO î› ¬lß"¶'NŸhv$õЬš5€ßê¥6üŠ,T´ÃÞWà–r"Ðø/¸|òØó Îjé¾õ¸è±KÕº£ðÈNþ<ðQÏUéÝ£íxý‰:]é\û*·µÏÃ˦‚.ñ8^<í-G{¢Sè-Lí›O¸fÕE!“Þ Çg⃳ð>ä"~Ln‚!¹oØÙm€cú°)(®¬å­é4ˆ‡’ M:ÇÉélö âopâÛ8á´Ò4Y§ ÷ÂÚØ®bGž7ü0¥ÏãÂ!®ð#êSs¥¾n@zK é?OÿÌ‹gÈ"½;~;Ú5¬¨ÿin´À eäþý3ÄøèEÈï…[þ™À‰­¯Ñ¾± ·îý2rØÑÿ»ÔýL† ¢]Dõøz4šž'l~Q‡.‰Åu¬·†)(á ýpªò,Üz¼‘ï…sÞ^$f_C¨Œtíªvuz¦?®1£>Oçó_@’`(rטÚDÄeÅdñà«Â Wpkc‹ ýÑV0˜ŽnoòI»Ö>h7Ç]? jÙY(ÌÎŒ¹ìwãëú‘[Q¸æSΉÐh1Úg ìÛ¯ÊWäiÑ‘ àÒÓð_M7[ý»BÞBѽ üÿïõQ¸¢£ŒY:hÃÊÁiuò6€‘©1ÝV,Æs‹Ð§g\¸sÏ <Õ]Š+—çÝÿê¡õÊ&”1…ZãO7ÝGƒ¯ŠR“ vØ7DŽÌ蓦7Wäq›wÉèÒmàõ­‚¤»Púä ™“Ehÿ&~†%Ó-yGP5\~âD •¥ÈûéÅBÝÀZØØQ€Û_¦rª¯2zÉêÝat_û>XÜìMË^«QÃ+1mÔn:Éâ yßtkóñÒùSn»ÖÌe]ùhì"·í8¬ ˆ£'J ¹{¶ÆoNÇîÉ5BK‡\²xãex°$j ÿ?RvÂ4·•lÑ¥Áôª"]{À”~måßÄÓñ*Pî®C…Ó1­<ƒ[|¿ þåÖt®Ïná±/˜WŸ>ôDG "Í:ù¼­½Ä2x½¯ÉkKcé_É 2é¨(WWçí¹R± Šî5Œ·×M¤[mæ‘… jpˆ”q¯oI\ϵ{/7’èu.\¬s+lÿ9‡»Y"ÇnÇõcž —šÒ·´#’†¢×Æu|ÞÍHþPYbÞ‚Ë ¾!l7-âcy{Iý„%.©ë¥³ì͹ý°`%ß›6˜‹‘±ü§æ0žÙ9”w¦+Ä“-ªD¿í‡var¬%y©†vK>¸ÙÌÁŽQ‹aðó7‚.1ÌßgɆ5à÷¶“$¦:/+…w’–Dé¶)lëÛÇÞïõ:Iôlù'ð¯ü€³»ÉýNÜw©y­˜H½ [ì/Úï3ã—‡SÃO¦àÒº ÕŒGÃù!r¨-ÑÈ2ÞyáoG˜±C„>Q£‘½ÉÖºuh1ùÐÚ ¹ä .–åOƒ Abq53š–N{œ»„?;.š³](X*$ŽÍ)8Øã/NL‡n—~c™Í.¨‡˜~>©¾ÝkUÂ¡æ²Æ€ýw•&ÎÏG»Ñ2|ʰ -y‹j{îã±'!Ù:E~À©‘uq™å14OûÉÓEüåLvàþÍ3!k™"åž0\qj=˜DN|-ƒ×%R µ&î](\é´~¾GáÍà28î)ÒßÇÙ‘z$WdÌ”¤ëUŒpóÚZR?ý, Ü%Íõ®-‡aí ás¶JüÅ ó"¸t0ˆÌõnÃh¥'XY“àº- ³ûDù¸0)~Mo6iúCbÍ0ºµd9øEÙ¸S˜}ú ›ày%×§ÆøÕ·Ëð­%¯™NÀ‚û/r”^êÝ/ŸãE‰y‚· GH×én˜¾Q”¶î«¢×eÐø'Ê•þÃV_Ö€ñ‡”¨B?ï¾y­›-Ÿ(ÆmGÀÕãÜàÉÏ•°æëKö½U…7Ù«Ž–À¼Àë8®-œ¾ûŸ4ƒQk—ÓÖ"’P«Ë¯)Á?£‰0 ®à莦=©–¤/(„µÑU0Äû’๖:?™Vˆy“#©°ØG¨»½LÜfLgg-¢™Ãèã OQYó9Ðe€+µíxÅ üJ·, Q勯žÔI©ÒÝ+~A¼1˜uæã˜Þ!4Å>x¿ïø$6d¯‘2ò"¬,÷ëS_õçp✌›xT%ü ¸S„êý¾‰WXÒrÕ[lé£p÷“ù<û-šØœÆáÆjÊSØlŸ‘»¿Uá8õ"[3U…ÏóÿèOu‡“P8ÏRJì™Ø:{&m¿ [W;£fÙ`úñ×D:(m+MoºÌšÝK¡åO;ÖG¾Cçñw £óÓ÷‘×cpÉúgøu‘«ôÐ?ò'ê„ óÒˆ¬î è:'OÚ[® “§ˆò§ë2PMâ–íЦ2šrð+CKðþÅA(¯‡é2&üÃî“øâ…< ‘ʇ†Å¸_’ÎÑŠå/¤{æÌ‡ƒ+Aì‡:x`ƒÌ?ê=Ëü2’WšfŸ© a7  ðf 8V¨³ÃÕPÿÆ ï/ÚÊì>?¡*;;ÙQ¨Ô#*²*tx¨Œð“¶½8ç~"Ãé²púã‹.´)¦v³B‘-5‚÷÷Ó1Øó%jì¾+V[ÑÕPñËŸ›½Wâv¡Æ°Ä.ŸÇor†KfüïÁ'¼Z:õ>zëQÏ ¯Ìƒ>]½éñ…«?IªÄ!ª%ÐãÙûFñW'Ñ9焵 ‹ábõð?&u ‡»»¬éå YfQlNO•ÐÂù·Íé†`e^ÿ¯CBŒxªö%AâÆv§!KgÉäaä8Ôò9Ñ™áíytÙ›VfÒîŽÉ1ÎëŠ0ÇY²ÚPñz-Xõ=Ömö‹ 8TNx¦’'Þ]¥ê6Ÿí½à‡ýçȃ~Øì—½™FZ¦ºÞ/gDvw?o¯‹ÜLÏ_Ob› pœpÆ£‡ð-åó€ÿg¥•BlâR”™ê³¹ßþC°§h xC”\" ûæ\ûC Äz‰ÖúÀ«±ÖÀ¾VIòñr¶Ô»}ê“d¸h4;xü8û¯VúÏÊ_lN8øï~J¡à7¾á¯>yYËÓ1Ä&Ãú^``ÑG|dªBRŽž £q•Ž,•mZÍþIRM­cns¶Ì€QË­©ÞË!ügüóÛRUŽ0îÀP:A÷ƒ°!z+|ógœËA<ŠèÚÏ!O’ `اR8÷'ä&ÛòG¦i0ïT5ŽŒáã‹FÀ¬3SÈÊqñþo(löæ‡$¿¯€¤—'Pé"uØÉ.OÞ‹Ù^¢tu¿Ïè—ì'^¹^(ß«@ó¤h×31ìtnÿÿæ`ßa´±¡ |Õ‚]÷Þ±7ø•§Í§ÉòBT>Ï@yy8‡§:Ä2SÞUŒä³ ,ñöޏÒÚ™Vn’†+u¯qÐNC¾wË/tÿžM;°ÎÍW$Ò•…0âÝ!´éþ:ñs¸ñÛ=¼íf æ)ãíE ¡ ¸­åk 4ÈbsÍìøŽ'*©³e)ºp©_ß%dÅ·¦bóÜyô™n1® {_ç´’Iiì¯(ºœÅ•bºª«Qj¤.lÞ?:Rw’:_@aÃ×·ëBÞÃó(S= ¿Œvå3ŸUá¹ó/Œé×B~5èB§“øÕ“鱑ӡæ|:i~µ–_(Ψ3=¬‰KÞ܉Éå(×RŠät¹éQ\r.š]XáaVuÿ}6>÷ ^3|áqP$Úå¯÷Ö6ÔZ Îì U ©FwÍÌ‚JG`¢ò$ü”© ë}â‰|ÍNæQå2ÓÀÎ ãq}ç$8hO ÿ»Ž®pÅúð¿9‘rüì{KºÒ~sÿ;23\¡"/u¿ËøËXžL䦈ѿõgýuùá¥>‚”Þ^´4Ò¦…¯®S•¿O@îÈ#23ñ–€Î8MiŽ’´Gé%ÓL¿“xôª"ý¬‰É£ƒÀiýž½p)¾ïúCL½—ðñ5 ÉWœK´ØÒÜúB¶t™uëÅä÷ÙÓÿÆðœû•Ug™ÓãWMéîËüâªùüæßË0{ƒ)+ú ù§DxjA ±7º ·žÏÃñòÓàJ„HJèòuÑÑlC’%¿nN©aÊ&ïG¤­ùŸ¢ƒÜÑZúÔTƒÎ¬òEÍ ëäïÊñø~at]ÚÃrjÔÜ<°þë¡MìN¶£¢â¡T÷ø¾þÑ)áÊÝytÝ6>©|1ÔŸZ@¿úOã"³'à YÌZß̦Bþ½c0c™4Z¿8v!š|òÜÅØ’´ƒìV'žxÓŽ‹øóñøÕ\ƒžw÷¤§CriÁ¼(¼u¹•Ž-Ö ÛäU״芟ŸÙ•‹ò³}<š½ÓOÅÁáÏ#ùÄ·qŒ³<Å©®K˜JQÍ3uæ9›%0[E’Ç|ÎjLÅi¥ÉgPqS¦É·Ÿâ+‰¯ä¦êM˜µÞ;¦¯¥ êIX¡u—½¼)?àë~)špKhrþ«#îèÔãÎxGýÒâ‹.í&á¾~°L½H V¢cöE’ë—úcΞfàÆ»†r׊p¸dª¿LaGv|Aÿ7âôÒæUX—ú4ÝïayêhfÕÂN< º¢«©‡‰#¬Ó¼‹¦ò¬êS+Ö/ü?¢Þ<œÊï{7gÊX¸ú lðžËÆ)AôÃ(˜<Ê]…ȱ ¸<Õ¦Ú)i°ÔÙ–>¶E4ÒkîÇl :Q7JŸ-Ã-•âtQ!ãöýáåyv«ÖÒW’÷…Õ„:„ü#]Ùs¨’•†^¼JWuêÁôø«»…°˜¸Ñ ò;Ñã_>Np>Â_¬’°ÿ¾Ô’[·“…±ë§U kGvâ!¼zW‡§¦ó;Ùö,ÛŒí—ObÝ &·úžPnµ;XŽyÏpù•$®{™#^! ûJ’zͰómÔ¶–û­dùþ» Ã^Bó!ä‹ ÜKDñÈgä‡i5©{ƒk¯±ˆÀ$üö‹ æªó _Ø7ßÅFpéû6\ZÄÍOIQ¹,áW"„S¿œdÃu d½‚Õ'Ð’¯ «!™Ë"vÀÛQB‘[Ìyí>˜Px€XÊÜÇÉDC5Rð§a4 FEä º ÞÜÎ×?é%§ýÛP}ýlï˜Îk7þDíá«íŸWƒóð¬ÔÇ‚_ó|„N`–ñA*<ƪ ùáN·íAÕÞ˜&ôsÌ£$g»äÀù×äkbÈÖ~EÇ,ò?ŽÙóöÁÉ„—Pþf2¶ÿÝ,‡ù Þ¥MÝ‹-ñÐÊÈœ0…ÿüªƒN£ƒ‰‰Å>Zâ¶–/±âü«=Qû=®[À¯Iõ¤Ópk¦ËUؑǣ±P~ ²IYtHª jŒé‰GÉÂmzü²Æ,¶:¡çNà†±L²ú$„WDCˆîQ¡EWÇ«&Ø"›€¸¸°z| gyíÀ_}¡Qõ7èâïë¨`¸4--ÈìñøÁNË%£îß²âž0INŠÞ~\ËžhÛÐcö{yÜPì %øÌ_aÀ~WïbØÿÂ’z¢Ê›ÇèðîgQâŠ..r]̥΅CÞય¹4Ía/_öä'üøí"-èQ÷0~mš4],§Íw  ‚/š’ñ˜U Ýsfèìr 3·}ÂûK߃ŽP¯°7 øKŒË–¦k¯¶ãÚ.ü|×9¼h}Œ†ÄŒ¡’¥mh·í8™þê¶-óÌ÷S–¬xÚgVþ#†f;¡³ÒH6ÅSKöãßÜ&KZw\D6KPɇ¡ÛbÖ?•æê YxòV5|ÜB™Ê dÅîH:¶)~yÅà¼×OXîT?2}ß û×DÉ“Ábw7z,?žL2*#ù_B|Œ:¦îPƒ˜ÇpoâG|í–F®}„×%”¸ùÇØt䢰vÁI2¼bVäï‚´7³IÌ‹§øIä7©Ýÿ\A›[(]«{çºÒ¯·Î ç…dôáqX&5¾X™òGêH]ß Ri(l>®è–ƒ‰z·!.ì" gfAwY?§ØÕ çEøüêÅUŽ%Ŭ,³šD?éÄÂ]›yõrÎú¯Ç÷}—ãßÃ…d2«` ¾ìÉ´¡Ù-ø¼zÌŸLÃ-¯;Ò½¦ÌHÀµ¹Uõï›R<-Ÿ™=Ýîµd•¤¡uùj6mÓ Œ3?\õ7e?JÜU ‡ŸP¾}üj¢œñ2nèV˜„ï|v‘Nã\hüø;43á†yiÇiíæÔ#ëY¸Ë“'Æ®äÿÍzþöúŠp»Ü6I¬D­ˆQ`ÖÏ›èÞñ2¾n¶Ÿ§ÓÅêžÕ-ëøïûJ|ÒéUø´í¬)ä襙EãkrŒé¼¾X>ï#‡-|eª.<|x ,¼ÁŽÙÿotú¶NܽI?Z—óo}jüs])ùwEöO¦Ë|oÁ}¹ÿ‹ŒÓ‡Ž5O0vo»}kKXª úªÜvkl':Gê„ÕÖAÛèV4«X¤ö•¥ÎùÂFékÓQ#ãê9Ø0éÞE»y¾•Ú‰— †ó ‚pÐ{ˆý×àãC,h[µ.N5V䆚¢£ðü“bV;W]°þ+ùü~!¢TcÛ85ö7ëGÕª.Á«³˜Tº&ýðì6qy VŽÿ‰ÇV•ƒg!mQ(DÛQ9(uäù¨.Õ_‰Ì¹7¾þD$­'Ö)%8çC,²6§ÛÆ(aAv%ÌˆÙÆeÌCÉßGðÑ£‚õS‚aCåf˜µe.Y)ÇœÜrø¾o5ÌxlK¥]VÒ.%i:¬ðîT: gdÁÁL‡ÜžÀ-Ô§ ce舓±¤ÿæA Ú΂‹õÞÜN«™œ¥»«{HNæ"á]G˜ß©ÆåߩҘ”XX¬ů ZÌ{55ô4t%åcæ'^½QŽÏ»bKëØ»—“00= J¤âõõà¢ç;ThäUÁ*|ÃŰޥŸÞ™†'ýÖÃD»£lTºÏ x†ÏËOAðÐF6å¾ ,8«Ä¦µäµ:¾xU°S—iòгñ?ÆùÌVÙH¶z¹àpí)Ôòa¨ MÂÁ'}çÑ7mðyj§Ã¾û³/0ÝŸá0ÎÅ5ž ZQRô»‚=”LÊBÃA.püãeØC ŠËÏ <ƒjHcÍk8£ð´ñ«±ó1à~ÿþ­ALÆXŒØâÀ·•)Òæ3…‚ìlHV_¾‘e[¿&݉gÛ8 ÇÝ×ç«/øÁ³‚$ûm±ÛQ1*ÛRÙñ†U|Gf#h<ê»ñãÇ!üÓºódAîøY)ÍwN}ƒ’Ú¡´w¼R>Âæª;àM«dHÕ—æá§ ĤõzÐ]•…ÝìâÅûð`ÏaõÜ 5ÿ&@õP1z ä Ù™ü"?yX–DÿÞFD[¿€î—¿°ðéY´¹zš”,ÿHìJ·Ëñ ‰58OS ix†Ìï`^?‰àóŸX øÁõ‹`cb<{#ìA‰•õ ¹!ˆ>Ò¶E›kip¤N”VÙæ‚áÄs opºs'Ã$ÍjØo#8ÂÝL±:…¯Gñlû§¬¾d"Mì±EŸyÞ°GÇRø#×£¶³¸à§ý{¦é£ùÍn=þ¸Ï3\FñÑ©wà½Ñ²¹% ëæÔøðúC¸´MÇÜ’‚Fë­Â£ÏÛðµ‹)™¢õnÏÍÃØ›Ç°|¾í¶¯Aå‚ã,ðŸK“fv¢ƒxøÂãøJusÎ8IÚf–0µXØWîJ¬eµùš‰Ö˜•Š÷[½@Â0º~ POëƒpF¡ š¼ç=œŠ«Ö–‘–Ñ Ü¯ Õ…äÊš­àÑ;sÍ&QÉã©Ð'æf·kh–0%-BÇí%ŠƒPퟵ“>M—2©‘á¿ÿ—IJûð’ÇIሹñìà«hòÎEû/¡˜DÑbSÔïHdN¶KðuJhØXøß³Î›,_âˆ7Faºx.P›JE׎‡3,ètÕ Áˆð™­âÜ`¿¦XJÒ‚ùÝ0Ló'úû†áÙ782øx­bx»@ ³î…e% }ù'àR—‹‹O‰ððX}*-J[Åȳß.|³E hÇÖ Ej ‰éV<ñþ" [à@ý&C‡ÜXœçˆI¥lÛ—¸wÊ \ZßU<©J /`HíCøàlïul&/jÂ`¢Écvî¦1çþë_ÕþŠsìèي)Üó™\õ•!ì+&&áñP`"«’&ÃakVû&•$Ûiâ_³X|KŠ·¿.%µÝ\Ä=Ôlpd®³#©½W;è¥õ_3›óSƒ·« Â0YuêîÐ@fÞAƒ>á{f¼²9\j”åÇ”j`Ȳq4¸ð[_7T÷ÌæÎç¶àP1#>õÍq0¾¥Î:–‚ ʕʤ|Àe·«ðs†-7Hº=Cäèc?~é‰)µûT-i#èÚ‹"ôhÀrú¯çÖìÏ¡ê«ÐdØ øÏλUãü?C?Ð^üî;˜1_{?%yÓ‘²×ep`Ümм+sã;ËìÂâFÜãZçßâŸï¹–ð:i¹y+Äaëš«¨÷=%WKS·~ƒ9þ¢lÖÔ(ÂçJÈ€j¡9¹QÐÌŒÅÙ™CÄ–ep»H‡:¥}$Q+Âີ±ð8Ëæáº¡S©…f+NLþ'3ÍÏ‚0ù7»8L…^Ü{’mn "ïN…¢HÌ+˜zO=•Ê…ò°þåþò´®/_/˜ôB†×މ`2Ï%hÿëˆñw]žâAúh>9±ÌÑú—øê-SÖÛDtª/+‚™#Á(×íðç« HįùŸ* }d6Z†t´5ç=²×pØb%Ø's^–zÀñjMø”q4îÊás¡Ÿkñ}ópáhiâkýZûuº[r hE‚Mc¶hró%›€¥W‚¸D:,Û¨…˹.6é|­]/ÌšxšÍÎ!PA¨•ÿ}Ü6%-Öª‚ÁÈKxão¬ðz†1­øRæ)Îô6‡ÍÉÓA÷‰1Hv&aÔôp|ÿr9n7ÿ-Œ÷¿‰¯Ÿ‰ÃyéA{ïM¨¾ç„Ñšª4dG¾Ùø Å5d¡!ò8¾_S¹ Æe3Ç æÈôàñÍ›p×µEÕâYá !Z…6$è£kÕa%ì™ud_ßxg¤e¿ú}v@œk$èõóýEÂÙõµ¬ÖKç”5£øiÒgŽÝ:ÓùgUE\–CžHkR‰¹¹öŸ1?Êf•9“ÜæÔÀχΊ ¡¯«Œ¹ok̹ê>ÞÜ…|a¦}ÂÛùBØùç3˜øbøÜÙÞ€+ÕÃÔ}IèïIÀxX/I¢ÁC6ôà`›:œë%+TŠ,eÞ‘sP)Ò yåmÜ:ùìœy®í«$G­%QáînXѱ…«\ẎNoVZ„7ýw±c¶ó=Ï”¹…²ßýI®RÝÄ#îh³gãPñ”}ŠÇN¤}qμÝ9Œ]ì÷wµUÅ\Üd¹Òš½`£Ö„2³$iȆ¸_=îþ)eå^ |žðÐÀýß ‹†c¥¾<ž_ý”Œk©"fpìÊovºõ~(h%ojµèÅ d\<ôøíÐæÎã6À±Ž*¶tC%œú³„¶œÞ'˜Y5¥mÁ)•¢|šõ<½·dáÕ˜d«u©18`29AxHãÄtèú=‘Õ;­£ôòѽddD ´ì˧V.ÈzF©Òø8­ªšÈ˜Iò½yð7%6;X.šk3íaÁ»Btó; 9&Ü_ùüÉ– ½Ÿ)ZH.‡ÌEÈåzÂNmGt&þ@ïékÐp’8\tE þhÜÿµ×ÎA“ÒÝXx±e¹`Ð!Úc´;£GÒÈ'I¯ª]©ÁË’ô÷‡õ<ËV‘×H9ðꉇ ®#–U7-—GÓaOÔb·}1^véÃymÃp̆ìuÂ0šA uózèþ‚ޝó‰Ù££ð·s1Õþ¾óŸjñ›G‡‚wù4>&|“Ãã9^t]P I¼÷ Å¿°Gÿâ`Ãó?8u»pQüwÜ}?\7âS|5è¡+ |í*_ÿBXEFLÒã‘R¨ö-yÿtK;ÈMLÐ~°pçK5eù÷ŠGìýgCú~ûDÞQ<‰¶µ3ø["FŸœ£ö•ûHù‘D~yÐK¶£´k+Áµ‚+kï☯EXk6–ˆÊ¸ò¬OšøH2½®ß'“ѹvÌói"²÷Ã™Ø rtdi|Óó­5ù5;ÔT×äB‘Ó(Ú1ŽNùrJÉ{M7¤!ãöÀ§øuäß'süpÑÊ¡ .ŸD¿J↵‹Àb‚5åw`nØ9t¾—ãϰìpsj?ÓNþ‚/>üãžk°T3Æ,…3C;ü¿ Âbž$°ú7ipȦü¿^b¡K Ç#Qe¹¦ï Ds‹Žà0 ‚´ÍGHíŒ$|óÊžwÞÇFï—¢§¦ŒåglÁ ²n§|ݯßo,ÙòÕØ5£ßXS9%Z ÅCÆ—.e3G „M§ò±à§$Ýz÷.I˜{§úì¡Ã*ÐäH%xZWÃíl;Ô9uMÚã‚ijpùøÜáä[+@ÐëCç¡Îó¹†w¨ñ¾‘òh˜‡}¯°C\•:YEeÒᢛø ¸B Œ]d»÷<9Ûž/»Ê‚L_ |þ1uÑLX)–%ìšë¯ÆgcºÕhóÃY]°OÒ†Ÿô^¯W}Á39I0é.l¬+'›mãöT~™ñq'$ø{‘Á°rãMôýr^˜üft-XÇÑ8:U™ëÄÿdûm¯ò¼&ìñ ªîÞ€¿nGã«0Ö·}œ83ÇÎ?>÷͆üÇ/Øï3p xì‚u>ãØŒ9œÃ»^ƒè]ÃïÌ¥Y:B$1Ð~ÍÒŠ;ÕçOc™ý­6æ¾cf´\Ç]õIøu„%®©^N×<{@>¼:‡.3‡àß5*øgåb«ÞütÖoçVgcÙýDíË Œó΀Už–TÅË™§üÔA±‰Þ´òÓllÖƒJO~âÞK‘¸ìWVüJ‡íÑ…ìˆÈü}è(K×L‚]‰W±ä«(³<ÒÅö› ‘p²æaÊ’›ðjäu8>\V¦ŒÆè‹¦üAË8/iÅŸ]Fßæ.£±?ŠÐ±âÑ_{ί¤}¯BÁmÖ>¨®¨"ãF©qŒ0ʶËóf‡9y£B÷öAˆáTøëç`þÙ ˜-âKuG9ÀK+>ZüF³LhòÑ£á¾Ptóˆ­–¢·îôÁíú1 ‘1<=ŸÂ¦ëÀò¼m0zÖ^ݹT§#,£ºÑYÜü77_éeSè#ËPHi†=ßÎý×Ç ¯þ€Ìñ§X¶î÷¿-&Ü®`÷wqüa8Óÿ4cwdâ©_³qø®ÕˆÍ‡p\`èÙ¿°·ì<mª1ÿeôŠâ»(9jÛºZW|ÂåºQxñªßúlvõá!Ö¤bÜ!,Ò<€ʬù+-ò9´ÃìóIç¬ùŽ£Ú;©Ö­}‚ãç®ä¿dz"èÌ1èæmý¸é¼”&l± gzŹÛ·pÑ+€ÊYf³Ñ{Chß‹d>}«+^ýkó ©‡®ÿ€þ;²ã óö2]Kú±5B°8è N¤û1Ï@l >ùKv¨Øñ¡ìùÌ4ȧy¬ve ‹»8¦8»ð«M!ÔÙî•à´òÁçÝÓøóÝÇ u0§ÂÕf·î)ô.>ZcEg$ŬEÞ¤bë°L6TïYöÙ{¸7é=€•ëôyÔÏ<äú$«c}-¼WF’rfy¡÷#_ØÕfë°c;)õt¦g‚/°q2Ï¡Ê|V˜9º5MwÀ64§Cû AýÜáœÞvL¾MbWuaÝÝSh6v8­Î5àÿ»û¸]û1Î˲£k¶‚fOXŠ(rKßýP¹ú9™›±7\®Æ— Üq}ß:t/¹ª ¸¾‹6(Réó {ô˜¸Z×ᢥ&tÖŠ0=mã!|çþH´½¼†¹eHѽÙúìbEO«F+‰‘`˜Ö}ÎÚºýÚO›:ÂP׺['Þ‚|óƒØÖnÌ®Á·¦'ÁC-|’öaEË6‘j;\sõÓ«Ö¯Vƒöòƒ,e Yàü4Xùà•`„;›ªQúZÉÐ9v*{ª&ÇW˜Ñ¬;“ªÃ (ž˜#Êå>ϸÿ)xšG”õǑʿ‚óû¿ãg±Y4Nâ¬wÓ¥›ÌfáB¡c?é`†‰DC¼ÎKrûž;ÞLoƒãyX™óÏÞïÖPά³@Ú3Î/K£ÿ]GIñ*Ô†QÅ¥¡x¹±’ìq;_%qÝßÙ^Í>mV#¸»×à¶cV¢ ã|yá’Óè“üýnå€iÛ\þblºÐ¶`";}rnn|‰ƒÜ7ð½_n±Wƒïèz6¿Cˆ‹ƒ7CâÅÃ(Z]ŽAs·€ŸO»àÑQG|¸ ñ“· ,¼ÑKDJ¦ð-«(ß땊MeÐâûÿ扞T wHÚdtºä‚ßM°<óîEA.[;'à‹UFX7$/¦Â²û‘x:çø.¹HÎMöÁ£%ü€V {Evr¹Lj"+ÆÿýV ›Þ³¹FÑÎR’ûé›çïÃ÷Ñ7¤Ñý9œ¾&Æ#¾¥0Ñ¡öü¾XÚE.ʼnc @C…Úyó_ÆÀ1  õj Û ´b¬<Ž«_à æ`]Èiò^Šcû!OÚ7-ÕSÓéåÀ½0¦}»¼U–ë Å¨²øý$šw¾ð±5$bÓ(ÞUôDh´-ŒúßùgpK»pé9:¬n`ù¹ßÙÝ£ ä`4gé*ò°â=ÿöìîoÆõû<ˆX“™ÑsHﺑ4{YM«Å»z|œ 96MDçõ~˜æHõE"iߘØä/NÎ,=…w*ÑÒ%°Ý ”7m¥÷Ò‰Fù²þ7\mÈ×>Æ×){ÃòbEŠîW„/ߎžÛ°3Ú…Ÿ_â›Îâ7A9]g~ãž°ŒE+Q˜“ ×}[ÈÖÞxƒdb‡ò$lúÁaxß6öy5®,ê燡‡qÉÈX:¹]_ÖmÅ{4˜aCߢÿ;Mk€ñ¥¯ÙÃßÃøe_<5Áí.Ùdž³Ãä>tmj†k÷Báù©ãdGwlÔiE›±FäFí l{ŠU/ÈéaoÐNdª°ÈV›·|ÅÍ)®TäP\º¹Š¤.Ї¢Ægx¥U”¾VÌdkªÁgRÓ¬NÄ„‰/ØÖ×Á'N«Êß3+ùQ|nдÖ(Á%¿d¨Æ¡]<÷ÇûMdû:”q Á þbdlH€pßøíð9ó:^›²Š,œCôfåè£ kBÈwï`Ó×—;ªVÝ+´mx¢A%žrþIWÿ×—ýÿÛ¿¶ð 1­Í€óÕ`”FL ±ÑJœÏi=ÏžÂQ?4Ÿ°‚Ó‹q†ÔÁ‘À„íWÐçã^#ÇŸùñ„ôvýx˜õÜǪßYp&ì"~.êÒŸq¼Ìy7ýo¾ó˜‡áìÁBiXrp»l Uùëøâ™+ð"<‡qw¼èű£ñïg}vßb:n˜x 7&Çñ]+¯TÉ©<·»„¯x*´¼»{¾À´#J¤j¶ ·9¡Ln­ä'ªR¹&Ô'¥M>)ó¸d!<^³†E£ÈPØuö|°âp~Î9UNÛ}è€ÿ#m#ú*5pJtÌ™JŒ¦OÄ I@¶lõ¯’•qæ·­ù„N4T5—\Óÿ¯Çþªs“x?¬Š.…2¥tiÞê‘Çð–E9ˆ)¬ÅscƒXã“z>RN[¿‡Ë‘ûa‚öž=˜0=é&æ–_AÞºT€{¼"n(±·Ÿ§S»Z õ´³,´ÛD 0F †?,Ǩ¾0~löʬÄåá×I úuä"b¡;}”Ÿ½Ã àñq/îòß0¦;^l©ïzh²³aý¿³Â]¿ñvÑr”ÿuMâ_ w ŠßŸ%Sãùn¤þí£üoµ/ü=ŠÛ>ÛÒñ¬‰~xÐÉAž+,þm‚RÛ¡üÒt—v¥ç·óä¶i°æ©>£ÀŸ¤ ¡9G|1õ Ó ÖÞÏÀZ®‹Þûkñ݅Ø?ÅŸž œLŸª‹ÒÓŽ[¡cŽ]¸«§èqû™J/ÉßRö¦÷#‰µràuÒ`éh!XmŽÅO¥Óé5Ÿ\So\‰‹æÑú²<ãåûþwzùGrûêŸê”M—±½¿FmŸE²;døp­^(žŠ¦/‚kõ/–vW„ëe­eG¢Ôîm ~ëúõ¯Z’ MºE ÆŒãÁC‘EwgàŠ`üÓ¾“üÜÇöUžäâÙØË¾I4ǹˆôå½ë® êM©‰U?&4iбS3Èà@QÞaQu®yìµõEY;üœÊ»Ä¨—¾:zñ+‚gÕh¥eôyÇD­à<–ÁvƒHÁ'ˆÂ¶Û‘л6›çhÁÊÕiXSøž¼œ¾ »²Ñ“Cõ¸OÜ žµú4ÖïúßùÏÞSVXeïCBFg_æÖC~¯e=y€’ŠÃÛåf|óLk®äŸŽåÓ0ù—9lËÎ$M›¨Œº?§¯EpÎs¾¾£åÁVÇX¹97´+ Åg ޝîD~ ®ÇH>*,³¿´ >ûJØÇ,~gÓ¿Âì}#ùäæÜ»ç 0aÃCÚø()ì ûÒXèâ8¦ÚN¤®¡ ÎüA…à]epàz7±þ5gwúð'^Òô·±"NtŸJo-Úý{Âl÷ïTUë5F$%àå ÷lÇâ’9q¸ñÁ Ù–ÅÓD¹qu‰ðÿ·_ùU±Ò—¡Ÿe¯CZ¢¯É¬ã£óµÐ÷¬ íVÒåinô丰~Ž&ÇWÏö75ZÜ÷€¤ÕásWPf÷Ù•tRZ_ÐK¦å‡ò Ñfh*BþŽY-0lYO勆Ò3å^4Mæ¬öH×wg2×ñCÊ;ñ´L /Ö<Ê:ÌSàýƒ PÏ®ÀÂc‹Ð.¼•1 ]iepUiJxì…K3¯Ð©aÎäÈ‘»`ÙWSD(—u ¥çü”¶)‚_@ž_õǪ¯Ç{ª©<ï½> ž{ wýs…Ál–v~µâù³Œø_êÞ<¼EžÿĘWŽüäÚR _—÷m£wvÃmoÛpÒþ§ 'ÛJrƒü‰Òœ¶òÐIz¹Ð~ø°O¿ÆvbžÁXî¨âÈGɼ†NuÞlµƒ•]¾dW7¤GüŇ “áŒÜPtë &2 iŽžüçõ¥ º?øÊý˜X0‘ox…‰xhZ¨ƒßdïàcËØÓ¢5ÿ&Ô9+x~­z”Ò»|rùm¹4˜o>ª*› íÐuf0¬jp¤7o„Б;[ñgá.Fìü‰´`0øÆø¯ë`(7fmÁ ¢xÙ; îdˆ3äîàEA6³ÐePÖV€ þÚ“âí¢Õ×d³²ÀóPš`Å.Ïð¡F>ʰÜò)|Ö(øDw,}³væ§8󯉓úXýôaìé×âciµƒ]4/uªÔI·Ò08òï0j~îe¶3WaÓ.õu=|ûA÷™9ð†= ÖºquæÌ…‹‰ +‡óæ„pûGw×p÷EÐâ¸U,—ò =+кàßàþ…,6†¿ˆƒ)«#ávüBî•…¯[f³åÛoÀäëTüåFü7ËJ­•å#+&€µÅ[«BüIÓ 5š0ÛŠ–õ‘;¯sðòX&¯¸šÛŽíÇŸ5Ø•iÞðçóE®¿9Žè[@MjqÃ_bÜæà ¢¿Ï”« †óñöñ¯Lòÿú‹‹«JYÑÚDØjL-ðD— jrÿZΨ-„A¶0ý÷Шð‚™ËNQñ¿²´M’²Ú¨²{]:ïõš|*~ø m˶D§ðߦeÐnìÈ>=ÌCÓ78UG†ÿ¥ª$ìî[¨¸‘É$g5'RéÒDîê{“œ›f½Ñlþ{?þ¦k d㎅ â*ÃίKÇʺÁš:úru¿ 1üÖÛhZeWk2wñ^…T>4ã9›ö)™6Ê'ó1O ¯i8ð»iÝè°ê4õUo!ª³Ž¢Küy…ÑâcøÏÌ"+H¯=€){&P0¹‹)¸Òl0­µìö¨ô4¢ñÞ¦=t‚Ú_vcÜ.Vbް`—)0ï;`+3=ž-€aùÛ±ÖZ2W»¸•`VP VQSiÂq”8Û‰–ïTxÓÎÁ÷È÷dyJXÌY¡çýl»$¥7ìEÅî4%NN£_Ϻð›“ݘSŽ Mí}]‘1‚žZáÃ\½“Ÿ;XI½8[¿{zt©Ÿø1Xü3ž_ÒÕ Ók:]íòLï×Jz«ËÖKïǨ…›J ÉʆþþƼÅ)….8îŒñß¹á1Y{³@¸ÜR”eÓ¤yqð¸I„ï_D“~›BLÕkÌw(ÜÈ]Mt¯°ó±PÊ26‰½‚/“~’AÕ‚;Gê:qí°Žþۼ܋žbæ–{°nÛrn´ãØ®V¥ÎöSyFj&Itþ‰òš{ᜨ3dw ¦ai÷`¥èºNéÞ‹ùLLÔÏAûD >âÆWðu[Ž‹¶†¡dóiܵi')U,OÿK0r«%j®û•òô‘ŒÊ»„ãn¡Z‡7¯ŸVÙGÍh~éNÔ. ärã6㞃I œÛ9Gš¶xÙßv} þŸÞª‡¹¥ËqÕ;EúÂT‹^ò~†¦ÿòqÄ7XºDŒË­¾'X–5ˆÞ–ˆ":{qÓÊ<ÒmtÚjÃ Ñ š½Ö&/¦'Ö/É¢'ìwêgRœjÞ¯Ÿò™ì-|ˆ|¿ûý(†œôdÕ/Ø[»Spëº ¬CÙ¿—Ãq–w&[™ýk²Û»ÆðœÆ8¹º Z£êɈ¬v›&o0cövó1¢d|°PãÒ^)ŸcTžA’` ­MB£=—pôm®2ú$ì.ÆÈµønd*X‹’‰b18C„`QaØÌ<`¿QwfÿCÌitVºäQ¾øEÛª0ŠæKe‘Um`ëÌÕ4[.\ª²A•àéÍ“içÄ›ä2 ýÊš`~fø£K¶ æRrh>ÿ¹¾*ÿ¿ṵ̀Ùê(^:Ý‹÷΀Òçßò²ŒXv…!¶GƒêÍ‘dæ’TôÔN€ÙŽȯ›VhpM‘<3{ ³?ãõ¯˜š¯mLÚµ6ÁX6â;Ùih¦ïsðѸ8«ÞAžLºË²'BUø=Pê8 …#1O¡òDx¬¡cJ²‚Oië._Ø–-ú­¸àÃÛþ˜ñ†È Ò‰ÓøI°0½’%gÿ`ÛÕ‰Xu=Vý $ÅÑàñsè“Í Q0a+®’:ÌíÆN“£WýÓ¸`ÂO!¼K—ksÉ+ï!5tgÎs1*Ö$ÖYC—I'œß.ËŠ®Öñ…‘²ô–†($gBéZ¶å•-3¯J V‰ðÔ@Ɇ{àü¼»ü#—”ÂúÇAýE¹óúd(ß´š†WÈ`Ý5³†m{hÀ%îhðt§7à$ãC×·ƒ¿Ý'Æ?'©²¡Ü¨éœ=~iy÷NÓæc·s‡UßY˜®+O¿ž7€ÿ…Áuäʸ&ø³pW^•E×wHã³–Þ겯ºôOW¾ˆ¦áJ¼îÍ1ø–\ûn;Ó]Ïó™v”%­Ž=Áŧµ‚mßš¸ÓïÄJÑ"¿g8zª6½þÜŒn=¡,0°1Ážœ\êß³™›ê6aæŸfrÌטjKoAsž¥“÷ÛŠÙŽÕ8¶)–L9ì‹E”á™ì|®z"sæ¡û©–ç4¼>= /þ×']mº8—ù¨P7ä:ºÍÅ_Ó6ÀÂÝyõˆ üµa=#âAR'q ScÚXƒŽ­E™»óèþ%?4kÿ5Nf¡òy"­FfŒÁ_þ²¸@c/$mV¡Ýp-æÁëw–ÄQKó² aÙîpwè=ð?úŠø(ñçÕûpô‘‘Ô±ˆàÃnxôî¯`ör<1ÜÞò W—°[Ó®a¬êf"»£””ôº¢H€%Ÿø-‚(Ç3¿_@ƒê^!· ¶÷½GëÀ|\óCœïù¸:]m7ß÷ŽQ6îÔ8ÁèQsàHš •ü¥¤ò`c¤4Èg¯og ñ.p²*Dñºaùæ@ H /¯Äi.ëÐ=öß¶áCG¯§Yî‡iqQ"]“¸xjñÝ9{Y—‰}=o"ï™UÇSMi ÚAøYªÇê26©ö"©9‡|fÕõ]U±ÿ× }e’¤îƒ»zsÓÏï±úÉr¸»a3îÓðæ•­ù§SUÂ+÷‘PsCjÕ4‹5¸Ìí:rpýûTº‘ï¡Ê3Óµ‹Åhsƒ8ýcÓô·pÙgOÙ=«L*Pˆ·+PÌÅüwupbD< =pƒh.žŽˆÕ”ÍäéA*îKJv«…Rf°®xL^j0àÿ—£•‰]ÛnÖvŒÂ¡‚9üMÕt8ð[\‡Ëq¹= |W”˜ž%üßç-Ì:M‹/_ÆüëôhÇšù(]© öQòüí,·LñJ&üÞ¾]˜Ë^oì³R ÖRàõ BÁPwc~cÁ:ªò©P:óî& C7ꃙ BÙ8ãÐè”ÌÏ(iÁlHæÂG„–kSA"ünɯ˜I¿u|1e.f<œÊg¼Â8V7åéuÛ4Añvm>ôƒ‘½ÓÎÕpbþ>ZcæGÖOŸ³5XñàÎX™ü=9Î8¶wÀþ3_ÖÃù37ÛO+ñÊ·²\öO Þ²W§µGfð 7œñO}Äàs«Ÿ.¬m‡‚=Ý ö{~RÀ¿Oü ÊÖvl´¦»“ùߤÉDT-“ĘLƒñ×¥¹òͱvº…Oàì%Oül¾’.«ÊAË*xU<š÷Ýǯi´¡A‹ŸÂ¡òõÈ[3¡nðXZW“)<Ø<ÎÍ‘â¡÷: Ì* »Ø1"¯¿>}ªÁEÂdüæºÎë%·ŠYgW5Û®Ÿˆ³_„¢÷ÂÃ`)ÝFæmþŠ¥%dñ–(â¬Çû&*Ò‰êÔ\¶™Ì{ _¥Ü%¢}v|ÝüLX_'A§Þýï3o!èQnßÙʵ×A¬ü+ÿ#»æL ÛÇmÅQNúwa Õ<ÖA×ó¿èÝá·m—bpP'˜~(ß¾…å{Øá1wpM‘¥jpûB}îòy$l‚ÏóX?« u¬,ø¼¯}Ð8§”¼ ×ÀFÍ÷ða• _å§F׿éÑÛžÓpDq9h,ÿ'XÒ9æÛ—‹CéD$xÈ\`‹®×ÂV=k(›\3ô®’µ–‰Xš~j¯ÀöÇCèµ[öhu1 ÛtühÞÝdØáš‡åû­H¾é°#NÚä‚;ÕéææÂs›‘T­ñØ¿&]µ‚x?K8n´ˆ}pU¡ï3VóÀ+Õ(r0a€ÿ?ê¶vÌÙÛ ß¶ý`…)o±µf(wn‰BijÜ‘G ®‹Ñ1nqæ¬ póô˜;¯¬jƒ#êCpϱv·]Ïw6ÏÁ©)àåw ø8?¾ŽøÃ–‚Õ3 YÒ¬rA*ë׌†Ÿ)G`ö0%rÒÜŒ}Ý W#& ‹MÄú\„Èuí(>á#9“lɬ—á!‹~ ÷üŒÀÆ=ªr"‹âæñèÏ)PRçFÍ­ÒHð®m°Ñ,èIò°ëC `kŽð_» ,^¯Oà©»ê07ÇåÏÙÃX½"¡Å¸.ûs5»´báÿK:êŽ<¾0CÍøoF³ü©jT8~ÿÕzpý§7±â€]ù5ï¹ËZÎræ>'Õœnq¬` Z[Àªi$ X3…i÷Ú€JX-xeã•Æ9èô½Ù“7£iòWø›“ŒG¦‹ÐÙ¿åaÔS~nÞjŒM²a‹;Ü`sî,ø±ÄŒ¶¦Ü‡u:wvÔÙcPY(Mhm&K‡ÿ$y÷…£p÷ÇàçżÀd/^Óô¦[îì–JØþ*+FŽ£ïƒÝ1½k¨½S°&][yA`4êÔPð" c­gC±iñ@þç–àŠc ì®h4]µm"”^wÄÀU£`r˜&×9!ƿ޸Ÿ¯®$‹qŽe#ØãKÖÚVH¾¯ÿoKð*†Ù‘OÙ¸½·HÅÿú¤{‡$`Àn-z¡fn½çE7N°æªƒSÞ-Üi~îí*¬’LÁú1M¸æsV÷ØÃá,':ãîpûa÷Ÿà™–ÌNÓ°v`»LŸ¢ò #ŒWm›F0Æf 9q™L‹= i­'Iãmi¾ÐQª¦ó¿›b™NñøbŠä×¶Úqݸ(ŒÝ?®îKâñ¥â&µòßÿ|z 6Më&kT=x“ô ”ZPJ£ ðä;„­C¤y¸÷V"9â0v åÖó/Ëï3wÅJæ,Àv9 -¾ }ž âo’¿tÅ(zèOŽÕï4ø ˜ Ù J³ûuSO‹"̺yýº:ªßë„Ñõ24BoøÄ‘\Õå$aÏVH/|Žkè:üjZ,¼Ñ§nŸ×âe»Xò²Ô{eeI¢¿"­r&ÇlJˆâ_oÈÜæÎ|›²öl'h,Ooôe‚¢ÔÜ=c+¸n­g }Ç…†þHµ¼ñ¡µ.I_`ñþÓüß3¥[(r«ê³¾MHߥ㙈ø§ê‹¾R|×Þfx·R+;MqJ§­žÏˆÙÒ(öZÎ ä½qkm&ºLècûöÀ%:Ô¼luû» ¯ˆKòùÎÉ$øë›ÁêG"ë)E7]Ø'6þÅ+òÁÂH\kþëæûÀQÛ\ 9.Ë¿~L§øÆ-šØ§ÐÜw¼Y­{Ä42]²¾ø%y%”ž‘ä¿Wå€Ï”þøÜ™Ílj»±(Í’_ Òä9ï|1*õ0ö !:;¹ ,Ʀ`ëw¾k±+ËW•€çÆÿûþ·À·. q«™h¯Å]õù³hYSWÉô’ èWKiÔV›„RO€þד¼rg›æ1›önÃáG÷’â{÷Ɇ:ä®/Úñ‡°å«,M\Ù ï'Nƒ€>1Þœ2Û¥ÿÁÆÝöìØëÍ|ëäÉhlLßü(½³$ea!å¸ôT:¦ZÌ侟kñ¦¯Ðk¡:?2ÔÖ˜:ñUò+`§X0ìªKe¥•m >Nî¶·åϯ`]Å?פSSÏ“‡¦’øëƒ#\û~.ˆÄJý0,HSbåKtøÆ´vvK7ŽhePoX<Ûo þwn}&”’ÒÂïË*[šÅè ç°ÏUå¡ob1ožÖ(DÃÉù!øÌN š.hB[ÉYÜðä¾É?މ^6VöÝ>½‚ ŠØÁÆðÏɉÞz퉪ïJàý{z®×€v®[O|‡¿"†…—ذñRhµ8ÍJß¡¸“%Üñ«!º‰.l¤š#ý°3M^š wÔbPA|!!c­ÐÌ9£Úæª5èOÊAÍs3±sH)ÆMO„=ë_³î~ œå6K{/“PMkúfýOè~r.=‚­Ý‚ú/_ÈÚ_ Ø"©K³Ö¨ÀóªåGÔÃ4Åþ·Òˆg#fºÂƒnS¢üIŽ÷xXÁjm :úÊDîøÚ˜M“‡#'n¡WêOŒŒÙ?B‰Üµ 4@©‰ø6‹¦™4cnÌx¯ú]ß*òñ# Ð;‰ŒuÔÙâÕ×ïà÷É)¸(~ºIÀþ5ØïíPú\†O¸v BÇEØ?Bàˆ¿›„£[̘Ç Bû÷Ã}ÌsQ­Ÿç·µ®†©Fß±Òl5O-Dñ›_Éù(MztôPœ™“‚EYà3¿€ÿ§D°8wàüo,%ÓËW ÝêË8êÈ=xÚ¯ï¦Ý~æ£Äøô´PÞ·ÈzG:<ø„l7drArxaj:;ö¹‚þ˜sM¯*X¿`-L­+€ÇË®Á¥Ô¨Fò7èêÕV“‚‡dôbYîmŠ2!p¹\ß:ŠûjaI¿YÒŸʦëy€Ã®|NV”ã‚UãÁ#͉ó<@ß$¸vUJø,b5òaóñ©Û&T7²Æùcþ »íàKÓe(9„ŠÚVŸß@UÅ#¦WsJ¼ éçù”—¶«ð×’ÜÍH—¹†cÊÐÛ†þ/h>‡®ùð‰j)š×Œá{0®ø_b×”·àÇ'Ùøû›=1•ôˆIÐ×ÚTûc ñ]§®üþmuzéÇ$ú5J„êG>F÷LkãôQô¶/žpcFzð_6OÐRaSž[×*3Ùûüݸï|»'O/YfÂì° m¦†DÛ—ŸÞcÚȵèðûŠì2ß>×€úêh­zmT¬Ô1;øÎ]lWÆXØæ„î²ô„«Mé˜Iw£^ïo„{†è{~&Þ¨’Á/“#éœ¿ÒøûJ¤`ç.Ih²¾ôóߩԧt?4ÈÆ¾hz uË1üû¾˜ØŸÃs·ï…i«áqÛÚ§º®µ®Â)G—S ÿ},>>5WåÁýÀPžØ¼œ{6¾„úŸÎñÿäõH”¨ÿ&x}†®ÿ)NMu*ð¾„6M4ÊÂož£ØÚŠ*>4á úžœD[“ª¶Ã׿óÊšãèËŠø“5Ó@lêdº¹%bJ„ò·¤øxo¤ ±Z› â&Rþàç³»ìq‰ùKÙí e§Ž¿—Køh8X±+ÏIÐ[W¢£s" m¦Ç. VJ'–ÞõÀƒ³èíMJ`*­B Ä¡íN)êeÚ Â†Ãž>DÁYg˜_â bµÅ¸t¨"íËÉw¿ó‡×d£²25;8,¶OæWe³±ÈüK/Ø7`¿yZ2¶eþ¶E /~}Ñhx"|Ž}çêð¿h¾D…«•¼Á}+ ÿYÿŠ ZœI®T— ˆJë¥ãŠ)bÔ&ªŽ {r—Þs J–*Ð=sCQ÷¡.š/¹á{„óMž’çù­¨kSêÚµpïK,Xœ®Ç›æ@P˜ÿ–éÎç~5çî.­`¡QÉl´akÀK—{ ¢–T-¾%/‚J±äaù, ß«Ì Ç(6Çã"$ÙeËá„ç´oTÊÅ7X”’}lI?+fë–5 “Ûj‰á´iì@e4ªp'ö³Ù| þ}Ú® ÿÍn>"EóšV`颱üU½W L¬Ûp‡|>‰²ÈÁ{É—˜g[N±Œ…Ç^¾(÷ÓŸÉ>;€©‹jYÃÅrÜtXнƒA¼4 ËÆñE¯]±€Œ$Ë–|FMW¡~ø\]g‚“­½ðÙww˜5ÍÖêôó–&*x=2›ìÖžUí>-’þV0¤©ïbæòëð€Ø) ]*Ag`x§é7·ö’—oÖÀƒèÛlÑœ«ÄžNLµÇaI K*@Âõ8\¤› ÿ­+o»({¹ºúŽuÚ9ˆ˜¤ó\Ȳt¶øßç?.(‡’žŠ øó.8Ô#/è¯ý1¨'üê@¡¤ù¦LPï=æT\¼ ïg\Æ’Z|- |ĉá¼lï þy÷X>âÌPì¹ì.æ?±#1¿¨Éãd^ð#”?Ço³JБ$‚I žŠíÁí¦ݸ‚Ch?ÆzĦüˆÄ›é+˜yÒ5Tõ„ýIW!,³&nÙη|kg½< &¾ùŒ÷M长pÂá#;¾1¶ç¹È£ÛÚ2œýÇ„zGºòÍ–; (ÿ ª4­fæo0 l?t{˜pE÷YØ»h3pýgˆÑ¹§ ªO±ïÂϳùý6¨%ç±SÿÇÿ>»Û° •eøî²mš‰Å–tî‰HxÕ½O1¡!]Ôú„"¿Þ†^•'auaeû`å!¾4õ$xÈÍç‚Ä¡ükD6ÎPÉïå× Je•ÑPaô9AB¨yUáDÇ‘kŒ<ŸŒÁMÇñà°?P,36öÄ1¥Ÿp’‘V¢Còf1ß '@)Rc¶ió?ç/ÖÕå_Ô¯‹ÜJçœ[Gvwã³lMzgx®[?ä•ig‹.Ï»Äï¨ßÆ\¿ðAÙœWÅàû>EŒ¬Ôƒ¨iž”Ù/¶GPƒõFôßUûí-ÂàÆEQ¢ôâ1$Œ‹æÿ=&1£©ËôÁ1Ó…þója¿F솾³Y°Iž“9:ûáP˜ô¼Gxdî®à2Žö×fŸ=˜tõ#û(ž aÑk𮪠7²¿6¸VϵÔg¦¥$²Ñ®zT&Uû=œ Õs·0/3 xìÿŽY5¨£”bJ?AÏ´‹‘­“Ú`…ù^´Oâ›ÇÀ°¯pXp V4{ð¯#VÐiewлà8þI\΃N ѡnjoïÉ{)Þ5d7,šw’J/4¤ôwsÝã_A4z -KU¡ÙËlpÖ¢%ñßW;™o>|ƒ\³_C³ãC ÷ñ!ÜSM=.¥Ó»Ê"üΞx¼<º„ ú‡ ÉÈ÷´tØSϰ\é |^°åÀÚkÓQMñ#òÿb%äo7ºóªÚp:3æ>v8hp¢iÿÚ2Çヱuú ú/|[sŒ¬ýù |Í6ñÇ_°ÙPŠðÙ" —m*€Ð|Y®=üŽâªI×aÙµ‘Ô Z…Oè^Ë/× ¿E´Éâw†Éð4m#ò³¡Ÿ×¬ÁVž‚;-ö±Ñ'&bQÅZ~ý·5 ·Å‰w¦Ðõ)o„]Y]h·m¼Êû,… fÇžûß÷ß½ 2$sÇžAó˜‰ÂŒur\ò\ÿ{3î>žxûöô×4Y\óë ¼¨Ñ¢Y®£hÁš$4Q;F—Ö—`Ãä.á÷ç5lQÖ¸»ËÛÍÃÛI§1ίP`Õœ IoÂIT¨:ÿtŽ­ã"¼ÛªåVjCÑŽ‡ðán7¦å=ÃOoU úØIRÙù»fŸ$µ~ÂÔ…% Z½oó ˜yæ*G^/sYZØ%Ál7\ê×{…lÊiU¼–áÍFiÈ/ìÂdÔ«XéˆKpÖ)„Ï¿4ŠÏ̸}CãÏF²í™‘`xáQ}O,Œˆ} ünèe¼é#\6§€´5Þ “¥`ÖÆ)Ôûe&ÌýAG…qÁ­OlÜ|B¥çÌÃ\ þê,.;:‰š~XÉöêò9ÖËé°¨)«õey®å0aŽY­ÎáÕ×ÒK¾úráóuÔÅ:œk?ŠHtî8Gc1[î¸òQ‹°¼ó(_‘å%éOpEî+|q}/$NÅ……tqÃlöàÓvvlHÄÿÍ„~±Å‚ëÚœçb®io©;ˆ¯ÝŽ:"qÝ&)<øPž^½ýŽùß"ÿ͇^²í»Ü Ž+Q©žEô퇊ûÿÜ.h¥«N_èç’Ò8y¯$Ó¬¹¶SÉ^G·.: à æÒññ/ðýÚ@&*ñ“îXÀ(Íàm´ “ ºÞ.ã ãVÐæÕ£ùBñ;tí“èVsŽ~¬ßD~6ëñæËU˜zŠà+U¼ëÓDz®âDT-‡Aî|HZ:Ý”Ε_™ iÎ`:_0 òÝôI¼K 6,ˆ"óR¥ Eëÿ0q">ëÖ¦N™´:è¤í3dÎ…-¸¾ë^š±ˆ¿Ø ŽDe4œ~ñ¢2tÿ›-x¹_ŒNЕ¢«}'"<:ÂÖ'`z´%­ZÑ4`¿æŽýPóÁo¾¶&”]†®jÉ)ûôK²ÊŽHÅî6Kn¶à'ƒªØûî*Yéz¬Ý NM‡³*úyX!šŽ­€²eMuúÓu,÷™發f§´Uq·§ÅÏ+ÉÜ ðóߊ’µÿÈÑèÇË Qá,,œ-JÛôÔA³Íš¯ôò]Ý —– 6ùݤ¡é) ØÇ­D_³êºö§ õ|½—oëboM(Ù3•—­(á5מ ¾á<Ì(IàþÃKù#õhªÁa» æÊ ÖãúÔVmä#<âÀë»+÷upGƒã0¨|8ÈMÝÇ&øl#ú©èÀgБÔÅ6/»?ç{ÓòŸ¼ ΋gnÐ#ë6bMà5P™{¾úIñÚQ~ îW·^<68„׎Ôâcë-ÑrŠ ,ªšL+"ø¯8ZµÇCÈŠ]ú]bÈbÕol–³32]:gc+\}6–Ú—íÀ®ŸS[‚}w6w>Ö¦³«¥Ab¯>ÍÚFÀü†=•㉕' Ñ<sžJñ±>7ȼNmÆF¥§>Žàש¡Ín(¸˜‡¢«µ)Í; ïoK²þµªÒ¶hP¶¾‰}‹ó†ÂÕ)¸‘æ‚„Þy¼î¼zÚÛÁ+¨ DŽhÑ9ZƒøÅuõØ<Ö/;NþÛ§`IÝ(:«#Ú껄§“Ƴqµ)dDäCb&?ò•´hQcNŸRî™—AæëKæ±s4TBËT\üÁj þ·¹Éóg;{ÁMd59¯{ÿ¶­ôëVijoÎ^š¿Âã3çRÞÐp:"Ͼ…†ö¢Ó ôïÔ$B Pÿ' ­E Iõ<ëÊ%†ûìPÿ4GÛžTxñÜ ÜóRPa¡-m“ £=ßß’=÷ÇÒ›¾|xÝ-ÜŸ„U^#H½B ji!x°-”ÃÎ.|ðæ:ˆÍɃö)RY+¼\{š)fî¥û’MiªÑ|ª Ýf@Õ®C|ûÈã0#2.Œ×…»¸°Qq%ݶ¦ ÝİZi_ðmš½ ¿:òmÜ’O‹½gP‹ -ŸtB$2ànÞ~¼¤‡ 4-V‡=¸ÌÏÍ»½´G3·êÃI÷þk; CM]Ñ[? %ìôx”’']‡aFœ6u¿˜‹ëÏ}ÄêŽÜü¶ãýýèþàºKK›œYæÍ9 üCàK¡µÂeôÜ©ÊûZ/€ï¦ùô‘K%]©‡·>¯âyFÃáS@:O´¦³3éë%Õlh‹Ó9šºOÏfþ]«8•OMq;¥¬îÚ=d“ èâ®Ø¥uTxÍøá«þüaïbúü½}SÆrkÑæ}ÑeÿR©øYOêžK‚Þ~½õó÷%ÔKºÔUŒw,sã¾#Ú… M¯ññ:S*ÙUþ$xIøÁû¿¤( *™ðÝžÞô¤åP&UpÕ†ìÅi üÔ9@ðɸÖíšMšH&a5 ÛuÕ—&âñÅ,®ö‰ 0|ð@¤>žâ»`§ÎŒ¸´Bj{Ç»” ±ËP|üIœú-Ñç×/wáÍ—é˜onÆþž§2ÏHTõ–¬¼gæPï£ԥΧ†TáÑYgXîT~d™:m»ŠO âøª4Iž—BFé¯fU¢\Mæ&쫽Â-j˜Ö6]¸\o ÛÆÄË¡aœ;ø£Ÿ­.SÛéCËÄù=deçº Vƒ×‡Ïöï¶LÆÀðKö‡†¿ƒGýzê¬çQ®· ßÎÚHÛ7ÕŠQ4îçþíaÄ;uBèý P=÷„”Öc÷Ÿ…ó-µyêUfŠãøISÃêõye4ºN~_wÙÿÈ\}®ÊX:s5„Ù¦ã³q¹â}2x›l¥ µ«ŸÂ´Þص,oO¥±–ïÐæKŒ.†Tõuôˆ Þʔ䃞WŸt6ã½utŠ ›EÞ£Ã5GÆÃ úÁ±­òԱκ½}…Cïäàíá¡äS®<æ¹ôR§(&Wý„ߟ†r¿®ùss7Û¡rÓ×zÃzԃʻž‚€S§™Wo*ÔÜ”RjL%>n‚Üú¶õªÞÓU½ØÏˆïk–f¾32QH>|ÜÉgÞ ª ùaÄfm®GCe»ˆQ¯ªOÕEú_‹žktaò3mjæø‰ùãŠ9ûàž‚ör¸E”øg§6¶0.Œlµ‚ôMþlÛBX¸d ô ¦"Ë"`h¾|ïÌRN·1‘„J𳓥» 'RQÙ2 ˜Ä7KŠÐ} aÿ[.Øg…£oùÁ/Óvh©¹‰‹È=Hø] ù£Là •Œ›Ì¯M80`¿õÏAPäy‰,ë\ÅÃC°pa¾š¤Dvï—æ Ož¯D6÷ Ù¾D™99£ø¼y°µu!y_~ç.’£¹<߸kËPéƒ?¿ûP0Å“< LÁˆxM¬ªªiÊ•—v² +/cBD%x¨-C+sGÔ^<ºG˜swÑO¨²9.hAÉK±Ðf?HKÕY£ÔˆKäíþÕL%Â’ÿü¡Hó¾_A¡ûÀþù n´u6>·3ÁÊ8uÊF¾†Õ«qË€.˜·á<öÔ>r'^$£M+UDP˜ý‚©^×É#ÊäTÓÿæÿ„œ?F|j««]îˆsï>€Í&“aܽ}`éØ &è Z¢÷ȟ踖¢*_’ÁW\:‡÷|†áÈžÙ`‘Fîo`0çIØ¿9ÔwÍ>¶Ê>ó_ýÛ?å`ýr=•¿û×Á£•žÂE‰vçŦV ½,!âG³Á“jÉ☼¢Ø†¦_¬J”>µ5扻ò„Ýá¼zÎ(+wÄ5‡CÈLIy~¹à†0$o6œLýN¤nÎà’ÖÛ ¯Ô˜ý¹\ä:YbÍ›>röùeÒõÑ ÷–¶¢É=m\xN~üIã7eaääx#*ÆÃÆлþ-xU6ƒ «0°pëÚ] 8þ`"µ_÷™œÿdÁKî‹ðk¼©òµý`fWÂNÞùHN.}á×°ÏMž‚°csPìé\>ÿ„'½Ÿ¦ÊƉÓ}–Â./O^(®#®ãmQ®õ-žÕmÅÑ'*ÖëPÑ”Klñ¨B{îB»Ì4ñé¸ C ·á „ûJñ=Ó­±cs0swŸ„®j‘¨ñ.ƒµjN`»DÎàŸÚZ?X|N› G°¿‡'Àë4QZ}WÏSsáÔÚ.6kÚfá¼£“ñþ/Q:ÓW>fkߎ¦'YcÙEw\f´³êèHsû/Š#ð[­¼3óD• ÜßÉ0™ ŠÚH’¤èÍߨˆÛƒÈê-ep¥÷ÊÝ9ÉBW¶ l›`Èž~;í;ø¿É†œS¾ø<í<±Õ(ÃÙì6^àþlÖðÑà/¸ÉZße“4ð~Ïö ðu=åûÞ“MêplöU˜ÞV‚®fvrÐ2áòïñ‰õa,ÿ ÞúžfgOŽ'ãšþkΈÿU²}ÞŸÏJ´¶c&y{Çš[¹,³{‹½)UKYGü šÉÀ oÚ7h7øüËHí狟a–ò)âõ@O˜y¨ž©7Ü\ ZOðÇÆ·h=Ü’_LüÅÎ?Œ!é¡Ð.™ eªN6Á—7zØï†tZ†æúÞù½S¿àÿ“ØpÒS]ðÄí4[°óXÖå6ƒßcM†6Ÿw£†I†}@»N],<¡J»î•T¿–±–l¿º!Öï<†jGIá¾mE‰ÎÂEï`KÊ ìáŠ4gh ÉȆÝÊ»ìÕ¶ÏÆžÓËP®€ÇŠ˜­©J“Ëk…kš0 ¥PvTª-Õ¥n—£ðZe9µ_ƒ´—¢dè—½à`t’-{D‹¶,€öMîtƒš'îo|Êò'š@Oa®ör­ö^\„UÝxtIkÌ|ð \= o+ÛÀçË7\5µ2cXdóD_?ðÊ‚;Âî‚Â7£0ÄX‚V¿‰$\·€.SQÇ›74èqú‚Õºìà ý±µçG–`bC6íV§¢*÷ˆî’gù/âç iB™»HHOÂ{.í`èû~¿3 ‡æÃ4ÅôÉÆgøÆe+vuqA¼¯ž¨ÚÁNØ»°CŸVÁDÉ`\Z\M†ŽÿRÏ3ÿìa ¥¢KgµÏ¥;/ W'¯ß®fª kZ£ Lx¾Fž$¶oÏ2Ó¤±çQ<‡² ¿¡ªe/(*ƒ}r…!fÓ¹ëxEœv³ë$o³!Ÿ/âµI®0~b,l¯|f·ºÚžª·˜5þ%÷ÚuñÔ¬odòfžeœ²“18y—àânè´ò‡ âC|fCò®ù°£Å} þ%?}ƒcïü·'w1âSŽô‰Ê?ÍògÕáödk–_Z n‡ ú1*×?† Ÿúdü^ŠO+åŸW¶÷~Q³OW͇a³-ùØ8uNÿŒÀÇçfÁ9GÚw.ÛžÉðÇ-íØþ\’¾¶¹ûî€ßpv£ ˜HN‚y]µèðÞ‰Ø3: »à|½= t…Ùõz4áN¹ª!´Ð%뎕Á«´µäŽWYýì:.½1™¬žPMvŸ1£ÄùñCÃì+¯t’y;.’N©U8¦a&ˆ… MëBGcÆ@þxQ&\`7…úßóyr8âŬM9Œ“áüÈr|}ÔÀ.KS&eïe¿(Œ‘þ¿ñ3¦nŽCHË–2 ¨_DždOc±í~vÿ&<®)w°d¤HõêCägôMŠ˜`:'½Ç˜¯¡äc§_Åa^Œ,Ða†VƒhÞŒ¨— O†Ðƒ¶Sa즽¨ö«¿õ3dåîH£%c˜vc*8I†ýÈ¿eäykq´ûrÇþSà¿}DhHÓAñ—1îº *Û• 4CÅø‰[}°8/g+Ó©ÿ×ÿë3S`ß>a¬px9ëæÂK½µìõþ\â&¿g>·Â?¦Å?W51½ˆ‹¨lkCW½QÁÎ?ª°gÔHHÆ+³aKõ2¸,Ò jë*¡ôñq~!ò|΀²fY2Ê_»qwÞ:cÆ4BÖ;Õ3V”¢÷‰|Ô²0ø bÌDâ›ðfs%nRõpÜ8< >öÆžG=ìùyÁ´ël‡Z‘àäÕXVK‡åׯ‰¢GW#ÎŸÅÆü<…í³¢@ûÎj~uA \üYÎ :Ã…öƒÚÛ_´Ä<» à¶«q¿5j¹¼èË•k‘÷P¨óäÔráþ¢q\ÕplRý#,¾½·ê4BâuqþaŸ¤°€ŸÞz§ÔÕƒLÕè ÊhÚöŒÕÿc×ÓuçwzM¦ÓÐÆ¿ ž?‡z¼8&Ä_Œ9 i¢¦¼^Ú‹Ž>¦Èç¼Ò‡MïMèøÑ†<®- ÞcÇá ü5L,×À?n’x0+Dœ3 ôšìàØÊt\-ž‰gî £2Ên˜uö(¹3/™yA3Ç„¾ÜÒÊ +•ù¾ÈÁLõ@,íLõå¹¾’ËñÕª†•€eµØò` ºyè_‹³cÆ œÿT,K^Ïî°-#¦Ñ|"ůÔ¿3› ɆŽè¢W«J¤¸­j%æ8Ô §ïXzÃç5v¦Îæ£D«ñËsá™Ü)øäK Z«Ñ‘ój@YÖ•OÙ}OC¯Žƒ»õ ýÓ_D¶N´íš \ÕŸ…¢¢y`>¸¯ºmaš‹§QcQe »‘ Å19«fµÜ"»û¼ðfÉ_HX:ˆ¨m!ɦ|g¬1¬± ágüu¹k_¹:øi=Š/?e„ ucéêMŽÕ“rm¨Ë#u®lZL~ŠkÑðqñ¸%Á ý8Í[ -«ä¨¤Qº`ê o¸ýØÿæ¿ÍHKÅ1Yá¸0ºŽÉÒsÞ"ôv¡;V™Ãn:sý‘¨ÙV‚5è©ÈßÄûÛcøZaD~ÞyŠG¥fâÞâÙøìðc6H®EÛÒøî±ü¦™%µºñ5|~‘âTÛ¶òUWAɶ,y.q˜šÆÙr©äU\Þã9“Éž )Í‘8z¡(=]>Ÿ4yÊ‘ ïOâdã•dåÕãÖ/B—Z¢'˜Lª£‚mÔX¼Çu/›AÙv7,{§ÇþI¥Š+'°@™<¸¸G½v4]qÏK‹¡‡ûQtÒÍYõü·¸.-$N,ã»çrù¿É–2+2ÊBŽÜ¢Ì/h!ï:ó Ïoýïç™òª!œfŽÌ„wsͨuÀQÜfñ Ï%,¤…ƒ…Ø(ÅE'ÍÃW´ø"mì56æßC”9y˽·£!ÍÛÑóØTj›vÇG§AØîY‚LîL•_îG=ý=8;Ì nÊóOˆ“}µ@ó<œ›AtTß~Ò¨áKeÓng^<Óú3”/=”R W`bB“t)\¯N$Fçc?EÙ^#÷›²šVâ¸>”Jžˆ¥&Z³¸Úʹ`©%;À`®, nС"ÞïqˆÏdöxJ ¹mÄÿíÃKB€¿˜íF¥][AnËa0Èô@}¥> <7˜+‰ÆQ÷'×ɦºñ<üÝÔ­îEÍ´"ÁüÙZô¶Æ ¾Ø¹«B‡ƒ„Ü “÷Bøþýè·¸›ÓÚ|ÜöRTý _WÌe§#9Þ,kÁ.++b8¼+ì]¹ê®L¬X¾<§BüÅ\öV, B«A€á#®V§:+.Â-íÓè¿{7Í ¦šßÖÑ1Ëjð¯„1ÛiÔŒ%*A¤'C–iP‹øClßw®i "­^ÿía þõÏMKí¾L%C 5íè×±ÔÃ7æDñÐJìuoû;w0«‘èaÒFµðóì(*½=€Öm:çóüXÁ9|úr¿pI›:_Ôp’{¡'E—~A]žò¼”.º¨L'½öÁ¥ÃÂiò¯ãpi£!ͬ†[gGsÍóqÅÁÓœM³â–óŸbBˆ÷’q`Šûâùü g™ây):8Þ“Ÿ“8î¾ÀÔw9¼Q%”çû+ófW ºcÜ*Þh$Ä%`Ç3R£ oWä}ø„oƒ]m÷ÆÐø‹W­¦ekÖò£wÒðñüzhÊôðÿW³EØažÉÖÙ>„zëJ{îÆ²™A™Õ5÷„× xw¦ ~1¾ÇZb‡Òá¯öcûšcpé“ Í™{öž>ƒ¸倵P¾´ìåϪ¿:36Ìjì˜^¶ ÝY0DÏTàÍC ô°£=¸Qš LMíkׯÂtOi:2¿ùI8¯-‚Óiêhíh…·¼oÁ÷+ ²§pã®Y°8ó«Ð}÷9Ø«dÂZ®7BŸêVh9q_Ë8Ãèõ£è±*•þ÷ì´ìeKYZÔ¯ÖyÑÇ“HÄÌúGØh~ä?o û›¨»þ!s`st¼à_à)(.Ã7ž{ˆÏÞ™ˆOÈH½­ðÝî ;Þ= #g í3gR½–¡<óLJÆ8±GÞ' îHfZGÃ#o3xí”Ùuipçg r…Wø¢Š ó¡bÿâR±ËdL»”„r Ædì¬ùA¶Þôƒüݲ$b×PÝï Ÿ~ _o\ÏJ-SÀwR˜ ôð"|Õ•lßð¢û_C_ÁÜ›¬Å;Y8ªj}¤X ÂÝø_ï”.Ö2‘H?¯ƒ/™  z½“ô±àßwãçøx%''L}Û\o‘jc:í¨ „~¥ƒlÚñh×2н>΋XNøp»ðYtZàAœß¾Šz7„ÖeµCj³8.Ž7§sã[Yaï=\8JȪOEQõu Ÿ<™¾ð+¯ÃóS5éÝ‹»h´G|œN¨ÊSúoÈNâ6\”×l?MË3áÓapñAe;Ç+«Ýé¨ _6C}Ö0çÔÁ4Q#ŒtíÞÄÎìyƒzkƒœú~8.¯Þ’³ÏPaÑ uvÓÈÿÍäôäHØyW)ÁÓðCø~úµð/äÈ :äbÌ8%%õ» À&Qì—˰‰2 ¦¥mýuÇ>™:“ã$°Uy–Â’¬9˜%s"7 Æ&ƒðÙ®Ê<öÐO›ñέexæØrŽF™áuÒer73>NE•Ò\®+dLº‹Ù?ËA'…2”¶áîË dÎ a}èÊ_p[k3AYá8tT>¸ºÿ dpjv‘|pŸߛĵžv¯ {«M.ï¥ÕáÇQ\ÔNŽCöê•cõìÓ[HIÀ™û'|Žb·½’¤/NkñÅ¿’M2×ØÄ nÔcîxno®Æõ~&©ƒ¤Òjª¿·£ª&l0öÕcÖR3!`fûúg]#KqûÊ.¶ëOäŒ{„-Q‰¨©ÿv®(ZI‰Sµ¹\®tÿ)çN—œñÄ’h+áãžX.|­’§cÖ83õÖº^ƒÖy퇫‚cðñ’ &?“û¾;Jò&¯æþåkaÑe 4 Säùƒùš®6á®[x­Öìñ æ}÷äw¯ú«ÂÈ¡8c(•8È+ü±6ø5Ö” èÅ0y¸jÿN $–› ±15Y«AÅFÌÅø2I¾pÈIü}„”]0ÃWKŒxͬx4MñãO‹pd¾8¥û`º}¿Ä/[ŠYߥèp£Z¶.ÇS8|IRp½Pòñ ÔnL§{br™úØTX±IF¼#Ô8-ÀX #¾ÜøSúÛ#ôT¿ˆÖMøæñ HŸDW¿ ÒaɺœGÌ@6 º:am†¤pcš)\>Ž×Rû§±W¯eþá¥(SÕŸÐA÷=äxŒ¤§Á-½ÁøC-”¬˜ÿ[d¶Å·.o'  æœkæâ¼y+üŸ­-¨¾ë&„£¿²Y7™A£lé} Ÿ¾_8ðªè ÈÔ)W¾=š/«ƒiŸbÑÍz+Ôéñ¿“Rè2/=ê(³§$8óÏ›æA¢Žß-C?ꢃ§¿Ž‰iæ2ôuÛˆ*½rG5øt»Á0Îv>š÷Ž´z¬¡s,ÒùЕ7q»oµC$±5›ŠÁ|A>ÄÂÆÙ×Aüz,|jd·ÄˆÍœz“]9ÞK¦êòO–òȼjÉÜQ˜ÞôkípºF†°Þ‘±×(Hhƒ'ëVð Ãñáà1lê±à4Knÿk"ËXÄäå¸ïãVáÝœûXø¹Éð/{tù•'¾¢u-hZ·²£aú>¨Ì8q6žô××$Yû çi¼Ð¬ù(™1o^Ò†´/)äF|8“¯ï! 7bIJ*Ìžb“É¢,EºT{8»{åk¨ŠÏ_‘é=ÌìÄ®P”Œ™§+¡v©¿ÂŠ¿ùúœW¸â Š›lTÜc¢p$€…‹B¹Å?…j  W…Ï.´ä/³ñþ ú8ÔW\–dgìÏã÷GV0V¤? "`ßÏQE&YQø¦ÃÛód鯹¿Yy̬ÿ'®ª_ë8VMíW»ÁæâÔ¸«ÈÛw–`ŒŽ=Mz± Eâº@eo;¼Œ Á¥hÕIBWJ$ÃWqŠÞÛ§Ó‡§2ñî¤1Ô+ç6®{n6›w¯„ðVÅ^²øgI[†cʨEßæ³¢ÜËÀž®?fžWÎD sĶ€Ùö´† ñ —©Ü ×?¼Dïr.HB›Ÿ¤@„óAaã\L™vNg¿$²Õ0;ë=$ý(@û-ÐYìGì·ÿÂÅçáüÊòc‚°mê0—*ú4C—ýh^3Áž—_9`¿êõ&Òš·õ:Làó¨Í3*H ÞÀêsΠ]º½@[Ã_ơо ÍzÚIªâ&¡ÃǬdØe¡ú9:¥wÌ]°‘Þ÷ßD¿Ñý|¬ÄC2¦ö0i­D‚$ð{· O¸u>ê§¢]å4RÎ<ºMyÝŒö‹·?ñ[^àõ·_M_ svìǘ¿n$hè{ü7s–Ã+JØó"Çåm¢QŸ ¯zÃènq*sí»ðÏt( ¼c¥B@úŒtkdKQ>ª Îï:““(J}´aã»ÃFž uS³à@¦+D®®ˆÿÜ!ÿíî1cz Ÿ øšM CX˜ñ§.âq<ªÑ;}6—ÿçH{ÏÀŸÛ1\í÷Eš¾ó-h-Þ é’'‰ñ—cüÈ;[¾÷ëK–3NG<Êã{Kãø·-.sBŒÈJ8ÓÒí”çÆyPWÿ£L¯o8ØÕnã¹³Úȧ“xâC6T–¥ƒðž87Žß_†WgK™æ“P–?ç î«+ ò/À· ¿Q®Á2FIÑý³œqÿ™Á´Z!ã#¦ÃΔ xÆ ¿9gÒÏ—Ò1^<®w–v\‡\9!Û&݃«– £¯£6òÑŠßðóšy¬x‡½Ös&¨§³‰÷œhذ#ôeÒ5ð i›³!ôœ¬"ÛÞS·”C騍‚þ7Z~09 á¦ë3å< mÔaW~”‚ `-õ:T˜Ÿp3'ž©§¡Èã Y¼söQ‚¨ÑdCšò˜0` ˆNNÍK_A•X+Ý/6 ú_'UЫ׶ á'ëqÃÏѲ+ý\3ñéÛõ8*ÛlÀÿnE~=t:¶þÖÇì\öhö^\ º[ë àÏê÷d“ßHä•<ÀIV+òV-87ܼÆÕ}c ne\{Å´è• J7Ú7 {_Ú 6IÈs/iÄ»E†àxSŽÞËØË"]ûX«ßi¶i¼!ºéÎ…‘§ Fì®ìU¤E›U`Éü¬TÙËrv%âÝÃzýë+¤L13R„ÖöŒâ#§RµÞ|Ö²uâE ?6 ¶ÅÜC‡2 –wÄ™. Ï(`1Œ Õ4µ0YgÄwÞ ¬7ÁUcœyo4_èMΔ¡›™ãÀù‡L¥‰;oNçÐñTaÄ!LŠU¤=ÏŸ£ÏÊËøE¥œýÙÆaU1}2Ø–å>ÈçŠ[q[rov£ÕÛv˜½Â4½%h|¯ ‡N#˜Úú†sØþø8Þ_:³ZÃÕ/\†–ûjt‘r4»ð×a“(ö5CÍü(·øqòæ ¿òu n˜D=Ä,H–Sn²C5”ÖÓÀ¹füaéÒ´/‰i'ÝwFSÃy=°@t.ÄÊ*QÝû¥8&äÌ~x{n\¤åGgÁ—ï#…V‹oAašv›ðK¨Å þ?¢¾ËïÿßÉÌÈV"‘Q„ç¼OJ¥(%QiiÐÞKCöŠ’™Œddµ¥<ç}PT´‡ŠR!í¡ù÷ù]×ß÷rݮ纞óÜ÷y=ïq^¯s¿ï÷ó “§:­JŒ¾[9€¿Ma¥Ê3vµ2 ,ú>pktæegѱ&¥|Ź~M¡ Ê„(³ö|c‘o” Mz™‚Þ%¸Sù'Š˜ñyZÑãwŒ©²…‰ê·`Oc,7WXÀ•ƒ/ÑÀðpæCãwí#<áÛÒ¤ïJîÒ>Û×$¯i6­uÖá_~Ä¡˜P=þXBô¤½ü]âbÚtdúmØæ:œÿ;xÄ;¯?®v¢­f5á¯/XôÑÍl7©‡g§Ó£§Xµ}>q©–¦ïÞÅñ÷åÚtÝ =:,K•ßÒÏUk aÃ#þï%ã?z à—þy‚hù­†¸c¡0AýlZ/‚É+qËó¥øvõJncÉÖ˜r¿Ë?±!OŽ^1¡õæoYGš³ðBR=ѽ¼ Úè»3p¶BŽÿÝ«Š?ß½c#’ý<›‡Ánüc0VPj5šžp´Û…»áô¥]Õßr_bœ•ó PŸq0’ª«2^9â$¶Ýœ$XÞÝætbá;aåÖ*^w¦fÞ8õ§«áqÁ á ¥&p<6¬=£&Õüã‚›%"—WWÿbÉ>Ñ·KÔÐ}r§àœ‘ çò‡Ùò3ã1ûºËÀú·Iæ9L ÕÀe­C±02™}äZ0{¹,Tn_u6 bÝ<8‰W3¦ÆñIÁ“Dêø·òïë÷¶æP켜Ÿ_³æ¦àxzß±,BŸw]/a#-ùú2YÐ ˈOHDeä¹gÝ!<¶KƒÏ*©#Ñ?^8+…:g½Ùöë¢ÄªûéÛ¬KìDp÷0GÜ·œá£­ò´ò‘*÷PÑÇñC'ÒŸ›`ò§ôÿæ\µ²­ º‹eé¡õl;ËßP7T;t»Œ&ÛµÂm[ßxß ß̵hìÑ”üëÄ~‘ô¾|¶i—Ù$¹—oi¹ŒoG ‹ûɉ‰ÃÕ%(¨®R9Ïîõ¸Sg ¬©MÄ?† XZ¢ Ln(¸à+ÜÆñQ'Ûð¢q†­M£ZTñÅV«{K³s ìËv¬î,‡ò‘ÆäÉ«Œ„ö¬¶/ÆzšÜâ™3>hª€~«È‘§.ã¢Ï› ‹Qªhè„Ä@{³žš(CõRÚÙ¯7¡ãð$Þ=+ ÷v(ÑÎcr´å~Ž.?I¦{ÀVºÄ’fy.®Tƒ‡|‚CÒ_0)•1PǼéôï÷éX·/ÍÕÁŠê$ªð=NU qðLQt~ñg Ð ¿Xª1éö=’Â"Û¤^Êo1äçÖO§)Þ‹@w³*&Ë6cιG(/Ûˆ2¿Ò¯ÏqçU;éW7ü»ÓŒÙÛËf^(IÕN?eßWá Ù]À~þ¸ÿ×3Fu惴Ÿ„é‘Xzr:ü}"IýB{¡}VtÛï!™ÎCÃWðy«2 ãÜSYúζÅrqZñð5ùüé3ÉxŸÄÝßÚã9ÛOèù{-Ö&†ÒGÁW°{ÅiŒãWÿÁ©½¿Ø=éqèÓž‡û¯ljU#ázG» vÕ|›1ÝÄ[™ÚAFm:dÊ[èþy6d`ì„J°œlæ|+*Ú.`¥'ÙºV-áêF]^qUÒ¢±ô¡{s½™„Ç¥¿šï OÓ¡ïëе”bì'Uœ£w¯´BÙA]ò> øÿ…Óéd’ïaÜ“6xrWHá{Î;A¬÷Pº#ªÒùôg Þw>/ÉÓÇ-æ“n£ùÍa4hÐØjÕèoö’yÎô!7@3™&{V‚—¬yŽçðcš€_~rtøoÜ|Åœ«Sá[ÈAj1¿†s¸7d "JôûaÐØB¶Îù-þ¾¸OÜ€G¯Ûå???0¯ÔÝ8ñtÈ*‹Eu¯X4¼ûEHÁ]¥kB…Ü,9¯¥š¨¨ú2r† WO¸€E["°ú}-Lô6ƒÿj­—Þœ g.†€È :ÎIn•Í"H7-$~Û;˜§ÉKP|#Öï#CùÁGãXDái˜]ÛS ˜ïºh^¼"/š(<ì®NX  {[¬ABÄãÎÝ&Îźôa&/œMO¾œr3=±ò‚ !°ày>ð¤¦FO,`+|«Yº±J# Z¼1=÷¡cd/Uƶ¹É·ŸU8dŒ:´É®áçß'áë³uà3u>3ŒyŸ_ ƒrɢ߶|ÙÑA´ôäk8>¿ƒ¨ý^ž×6“#ú[XB_¼Ô0ÅÓSf­7Å‹dðG®û8|`ý‹ ¨µ&èTy§pz,G8àÞæ ØRÔFNŠ$CMìø~G&½«€­ÇnÀ±ßF0a||õôÁÝVà¢ïŸXϾõ…ÒàvÌZ¤OgÏðüsz˜3\Ÿ´’+–“ÌWäÇAÂoFOGWw#þfÉØjÅëäÏáÈà Gåƒêªé¾ _<Ú djqÌAÞ3µ˜L× GîäÀÌÛçñ‚ì¸1¦šÄµÏ[YÐë<-3> ÷‡Ì],·I…“ÌöxH)ÄÌ®€Æ•³Uo¸:²|X #“zpHPéÀýÿ1· …¡öwˆÕè/öÌL¨‘Ÿ,¼9€±°š¥»ë<¹X†ZJô¡«^8šwhà AèpKXy¸ GOÏÅ>}fc¾wBY‰X?÷ï!‹»÷À3ÿj\5ž?8†×'ßfŠñÁ¤Wâ“yêÛöÁñ£éü{þ‚´c³ˆ e%;':žêû¿‚÷µ×àŽÇËn‹}õg›³01>LD}ˆX`?ßÐÇd| Ëõ²®æ&uä»þÈÊ_Ïþ«Ã¶U5ä2Ëc fÿÚ´£ ~Ž4ƒÃ’ 6ꮟé -'…Ðó[°qÓø¿¾;ƒé<þKG㋆¥P|_•Ùí\ªiZ\¦ù*¬L0¡e¿°~ü%f·[K†/c¥V´x šž; U—ð²·—Ð¥}“÷Ò¢o 8±_û Ý„3í ¦•5cyÀ`¾Sv±»ú–oýÊžý½NŒ-kpò{YØR原žÌä®b*(˜0ˆ›'oAáO8&Wƒ}©™ ª¥EoF q³$oüêÿê¦ìÑëëõÐí= Ê ®’ö~Î{0¥Ÿg«Ò{RN\Eº‰ÌÎLc2Âô+á@¼ h”¢¼â3œÎo`ÿ£ f/1’) ú«Gù›‹_Ó±¼î4Ñ“¡A ¡kRÈ͉ÅÍ'tɘÜù¨a0þ„Û@©Æxªõ~¾ÕÃ,Yiþ¨ë j©ÿ&çwÁ«e/Ðày.‹Ñ¼D’c=!az ndž@ýtéð˜'h\‘“J !oÄ t•ÒgËóƒkeÙK>Ф-þ…í: #Åx0Ýwn¤“³åi«áZðŸgE{4ÌhîUMªÚº“–¥ÖÚ¥ãåWÌhg/ÓÖ4ÿÓµìò—AiË]4Sœ‹Á§ãÀë’*=`úœ)>–«)W‰þ÷‹ÿ{þ÷ÏW¼I§ c¥ö“…“gBÞ³1ܽ7Lx=í ¸–•°«/M©úE{šXà ‡·àCgÔ¿°\P ¤EÅ_´À5Ï ýÐVx_§V‡Ë1(Ïê&-LäEiÐ’£ Ü+Æ÷ç¸c ¼»¥4è¿Ve€›jtrà'ëÈ ¦ê€¡t9[ ¶‰*/f0"/BÔ$Ùö¼ÿÜ8ÏQ7ùNã) ‰Dqù[òÒF«Fç RN3›Ø½6Œ™E^^³Ÿø §«•UéÖEøY.«ÎZÂüÅͰåM/L¨ú2ÿ*of fÞŠ¥ë•Ð[8<*Üh÷Ú2ôœÜ…µÛ·ƒýZQšå¹föì‡ Y­B~b„OYIÊ¿\Aê¯]ʯišb‘è-`’²,L« î?ï‚ïš¿a~.HÒ¥ÁOÉ"Åìû´CÌRé&\fmÐô¼ž»‡y£ˆK^ï©n‡…åøÅVŽïÚðŽñïƒú=·; 6ƒ÷5'R9£ž‡•݃iY_ZaŠ™~Õ°üžM©BüŠz`ÏÀ}¼q¨öChWYÏÍM7ó YÑlÓò2¸ bˆ/Gì$S/üïùŸ¡ÆO 2:˜„¹†=Þ`‡þpJ2FÜ™ƒnÇoƒÝÉ×ý¾d|_¸..¹ _¶³Æq-0Ã8¢TóñTä.¸ªÌC.Ö±´í£hù99LÔ8ªŠe¸Dskz·ì,6¦oã.NA°kÜ pY G«¤àÄÆ·øÀ·…·2â´}4dÍßBÝVÚ`ëòlü #é¥ð&.ú•Ù/šˆÕðWPgÑ‚ÖK¢òê§¹£X×Ì(TÿÇØKñü'3@KþH÷Ô ’’DÜ “+¼1ÿ@¸`ŽÂ[´ßð”|ÎÁ‘9~yKÎ~k8rÊ b¡sZÏ©aÀE_Ÿ¢Ïxü…{ç†p¶­¾Y‚þªó÷/úÝÏ>cÓЭL}mZ¯+ñ\Óãgö{ôŒ?.¼9†mdn—À(Ïw%ãã'¤âÖ£gáÃÄà¿@ÈnÎÛŽÚ€on±{ Þ£ÜyvMÑ®Qæiéf䱋í:ÉõesáÂ/%ê±buï±–u ²“z‰´Ò<Ùê Òª£¸bDzüþ¡'²ÙE¨òÒœ¾-ÒédÚê^aÖºÕ`øG–Þ°[EÖ+©ƒ~ÛcŒxX‰öwiÇN¬Iø NßÇ@Ód)t%‚°L\æRŠÇ{èôË ½eäïØ‚†Iä&} c;¤àÀÃ.(m§‘_Z iG› ª0WJ'c÷•«ìSz Æ/x öË:Ñ‘\ÅÃ%zBAÃfônü_|“!ãE/ž+×àÃVÂÌó¡I7IÌ"=º§ì£ Ïg;üÓy)¸ðÏZŸd¿¶à¸ìyü¬WŒ]%98tÁ aâ½^V‹Àß“ºù×§‚“»øCßË»¹gá¼&ß9Äk…1 2ˆb p‡ÊPybo†4Cê§Sh±ë&¼ ØKóN¿—I¸ÕS ¯A­¦˜Sa†ª¸ÊZ2ñçXáó1|ó%p\ú›•$V³°I3hõð*XP Õbú4%l<:GËRÕµâü´H=Dü¨ƳÐÔú#‹PyµNÔjzÇæöÅø‹Cxð‡" š¹0é"I.Ò§_Noã–wD!ßr=û8Š/¹\MÎ5á“Ë’|ÏÌôŽM‡¼OÝ0äÄ<¢Ü9O âtÞK–Jž_ÓDùU…0±¾€­~fÇ]~FvlY í¿ÔyßW>±a>î/“ænÊIT©~Oü2J0òCh#bÁ; +D@¡G‘Ç$îƒ vCÓÜ)Ød®Dã{¯Cöóé<å§"«Š-ßam¥.¿=¿W°Ec%9idƹË'ö¦9î¯ÅEÙÏÞÆ7®´e©³N0­„“;» äÓ~ô”)(¾“å“ͨ‡Ï8:<ņ­oƒs͵ìç´©ÂÙq$Ãç/~w½‚¦ï;°¥î¬P!UŸÏqPãÎNÖ´}W½°TßÕYø·øÖ°IAbxh•.týM×ndâê¦$$äê?؈/æ¾ÄRëáSR3ü¨@g\G_¼¾Ú“úØAÉð¸mQ*¹äV ݸÒ;Øcv€½{þ—¸ç¤Ã£qáct<ˆKÆoÁAÊûGt´Ÿ°€Š‘üâ }¾Æé=ë?ØÝ`Ó"'€ƒ_-TWé‘<³AüÊ¢Bðe©8Û&#Eçæ^Æ=:øÀoÜå½+z‰õ„º– oLa#'BKL=,Œ­ÆáWWcafŠg¹Â½[¸ûŠO‡7äýuhL-À?Øu!dø¨%¹³¸e¥$ͱH¼ìZ;ÎТÆÖáÜÆÅ’jÖÊÁ 'è#Å=0_ʉÊlû€x¹O‹½gãæÞgÛ>ó—Û*`}ûc¬^–fN¸#f5Jž¨–â&Y¡IÞPªØ†W6;ò ËSØq— ä‚ yOzÛ¢@oVh¾.àBÿP0=;ÇxÐ+BQ%n. ³B'Š?‚µÎÖü㸑ÜFNo®ƒ”ª¯d\E8ß9Üè±¥ý6\˪æé8z®‰Ë"¤é‡ejÔÕD›¦MÁ·!«9Í|> ìµd‰âëh¨ ;CŽf`ìÈËÎß/kó6&Ï‹+^alb ÖTe£¨€òï”™½ÅVUl€‹Ü£Ír¾Õð ªL3á™Råp$P[¸Ãt6ãYng”&ü¯NùAN ®-Ã-žÀ_çàÔ,89ù—óu¯xh÷£Ëž]/-¸Î¶‰ÙLË'Þ… î%(¾K*¬¿JäI$}Xø…l0ŽŽ~“ñ·¥Á‡õéFãtÐÿŸ½O".#ŽãÄå¸âÜpd3øèG"´âÛ"lÝ«AÛKþÁ1‘T úZCïÁŸ…÷ìïf¿B¦”`öÃ<24õ¼z«ÎÜ/ô׿€ÄØ×8×·E׆¡S¾5O°k†ÿêŸ3ÎÜ·5–¸axjÿÕç¾kE¨¿È{Ø–‡·îÞ†ÍO󊿚4}u%øÕM…[R!Ló¶4Ø*àc9âØÅ_!3q/}° ò“çÓ¢M%pQÚ™®ùÝI7ž9©OïBý=/}8 …ÝoA­eƒ€™pØüÔ¹$%´™"\¶õ\9ú‘è¿+ØÓê .=þü'›_Zsƒ=ÞµoQ±²Äúóø¡iÈÞògx\~vJâoK6 Øÿ“²‘ÓžH‰ <üm7*ù€w¦P‰@ >cÆlˆHíç:oLQ#»jG+Òº-]lʺyôVlYŸ.Šþž¼Ó|­oýÜä3ê?œ ÃæòÜø}ýÚÝ Q¹“,”hƒH0ZSìfÓ¶ áEï üµç¸ï£;Ÿ×Àë²8!ÁŸ[²)4¿l¿,{”|Z ÷¦QÁq)~WèÁ÷«ÅøÅK¨qƒ8»þ?ækðA­p¯a3üJ¬¬vx–ŽÏ~ËÑ[“³ñÕwE>*]œ2ATw&¹à0Š6ð§ñÂ{ëó€ý…I x0Iââ.0¤Éü0»bk…·{/pçžVúS0rcÕü>›ÏÚ07*ŽãóÐs 1má+ˆ|-¦V «.ˆ5Íü-àkÓ5ÚRÏ;Q×bŒ~QÞOþAú¾p(ŽŒ&¶ÞãæXE^ÿï(. ¦ï<;aõà°/à ÕÑrm:¼å Üæn2HÏ®;Û˜KÚ¯§Ý}°Ûµ™°] 1/ðmÛ¶r»)’\&ȉÙGwÿ˜@× ¢Êõ«øÌÓšTBJ‚÷*ž2â¦äÓŠP)Ôjò¿ý¿üÒ°m¹ìdÆÕ7]DÙQ›àH’J°âÆŸõØmíI°õõx;ì+*ü(~œG²^ÛÀû51¨uh—ÏbCcá`^(ùZ»6û?eÍKÞ3‡-ìeì3(:úwþ…[KÒÑØ$ƒ-Ýt“Õ¸DÛ¶IøÊÔŒ÷åmf³æ,„ÉoÁ³ûT‹Å.7-ÈÙ,Mí='ã â… ÐñÛ¦Wu]àÞ>ªôÊÈžqd†/cþˆv]ºË®xy Öu)×üoL´ñ!jß‘à1—"!ç­xüá 3î”lø «¦& øÿ˜µçYEšðbn ‚Šú+ÙýáOQp¾ ¾¿oÁŒÔLá³ÕòÜÏîqyoÙÑŒ1štÂÕá<ÿpº5½&9m£ÁVºTØi¾?Öšm×P†‘qŸÓìLYž M¥ê8e2ÔxCÖhUÀŽŒôýs>öTt Æä¡|ß2>¶è6Q»ç—=g^K¥™Ô·$493•{fYóa[•aêS(ÛŠ· ÇàÎè5`¾" ÖR ¹S¦TççiÒ¹9ÅäÞ LMÞ)8±ø=†Y Qjb81ªJß^Ah{üm3¾ÃŒ>6³¹ÙÛùè¼B¦\ãƒJ=¡ïÅ|øž eµÝí•(;ÿ4vžT‰;wˆó§5\­k{»2r>aÃÍ·˜³G*Q싺É>zR/^i#Žûõ`ô =pÿKo-šÇ‡ Ó¤7^˜ƒû¿ê¯¿Ÿ Äÿ…B ¨îdóRªàKÖ0xª1ŠÙ8›~ü׿“ .â.üÈÀÐâ±ÉCá?Q:}ù<(¶ºsOáÁñ0ms Ö‚2foÎ"W%Áèž$—v¢‘ÉøàásüýÒ ?ÍPॆ`Ò¶pv×6eÅ&v=+Z¢ÄèÖÍG`£Ý0ô1c“È7¡úå‹„~y§¶ìF“æ0WôÎ^% 9pFTJòÍcÆbá|Ð:炆yнf8´<=µÏÝØaÿxÊd¶c‰ÍÞQ©.`ÿ8Ö¯E|w¬Ç)¥wpéßûï\tZŸgÅã„þvòuË®@º¡~¶½q8 å&Þ@­·Ëicì-,™=Ž[¿DýN"‘æÃ!Ãñ*œÓ…:ÙZœ§Aóq"±^›‡þüv­ x,ê*”ž×>MKÂ÷ƒO³® [Фê-¾(ãÃuƒ×S˜³ú líÏ·‡»ïÃ|‘¸£EYøËç4Ž{ñ {Màh_ Ì<-Á=GüeÙvôƒp-<<…ÿæì’.—}bLñŸ?Þ-Jƒ‘Ò"üLb!8|_ÄëË‘.ñÔ¶ì”ÕFÅ¡mè¥5'~‚ó÷,þWÿb4ˆùœ[ÂÎ.‡‘Ë cælLY0‰gÌ@WXàƒAü…ûÜ•ø×/ð€Vº0Á5™Ü!š4cÍ`¡bqîQz ÓtBabÞ:£O¿ZÓ+×ÐvNÛ3ä»8{ ˜¶×ÂÒåÕ:ñÑèG#ahƒµÊQ凒°çL)åÁõ{?1øúEâqQœ]7Ž,ö-‚qJâØ7kÚØ)£_v3D‡uW»ÇHRÛ)÷AÚÉ•/lÌçÁ*díóÇìáÒ|hˆF¯­é ‹B¿S ™ìtí_Oi1­¡þŸçi²ïªðJ·4. ¢Ëø\>.Ê?Þ2§Óâ¨AJ"5½ýsó]yðó;`ƒ_§È°»cáÌ Û›Ý Õz=Úö¼¢¯Á¥J ¾ÿk>“x¥.èºýÇ©†´!}0# ÍMûØÖ}Œ]8)€Ýò6ÜÉb;Ì5ÜÈçoj‚2Á7ôÜ•@'® ¦zgÍ Ì]J»þžFǧƒ!¡ï‚pïšQ\ZÐo9á_æÛò–'hîî«Âç/óO¿ªÀuÍ2 ÚVâ4U{Û•El3¿›ÁSxÓ›!üÖÓPÿm$óF”Ÿ9¶wõ‘FÖ—$dU–]hö4_¨^ƒ4ƒD¼è-¼¡iz>IHûpÒ¾ã0T†ÙüÑøPvv'/5c`Ç•(&þÐ’ˆ0œŽ…«pÐ8£aGû«:p†ÚVTÙûÃé‡c(¬iO–ÞžOÙЮ±,À6»o³Àæ±0Â<_ïŒÄW[eà_Æ.æ?ä!µJ”„—U«ŸrÆ#Ÿâl“«è~b‡¥€õ¾EÐÝ>”[‡PpšBçF=ÀÒ÷ØÇ§ÙšÒFÜòzZ¿›Mæ6mÃcÙÁÀäý[-Æð×$Œ/Çòc÷جÄý4gÔ?˜ž,ÉMôš˜ÞÓѰƒtVßV¦+ßžF²NH©~*üuH?ÿ•æM‘ª°±G—®Ë Ç~þ{U–hÜİWõð·5ÄÎ˳ÿê ·E¦çw,Í~Žª¯/‹Ô Éûx\vÞ^¤ð âWXÏÝ(ö¨ë¯ðRÔr:Å­ :Yl*äÕõâqÜV“pªêR¼¡…£/ ˜¡Ô9ì,êc:qwp„w,ó>ð•5ôèE2wYÿXýÌÕâÃwçTGàÞíºpiƒ+˜•d`õ£šü ²uä^±,¶ýŒ€S›®ãriC—{ŽQ5ÉAú_Í0ž¬Ò¦uϴ触r˜:ø˜`ãi ڞꊥKÎÀìI²8rv3,=•‚?K”`â¡–Ñ[Ó¥Ìøý ·¤Šûãƒ×ùLÌAÇ–•ÀIEpvl"*Ëõ¢ãÕ%h:Æœê9‰a™<*EÓ”ã±sx•mV†ûÿå‚˧uDÄrõßqˆÊÝDM™ÉöÛ‰B}¸Õž} SÞÕ‚‚d0‰ÜŒ•öúd#®ŠS%[béöùÆÎ6ÆyGÇŸ Aâe:¨¯Ù:àÿ¾×êp©Þ!¬koåNÑ…“¾Ài·{øêk¾4B5œ†Ò0¿"^•›Å3\jãÛîñ×R:ñ"NÛón-$ ¾éTsLÿQæÉWÿR"®yÑxFó5)ÕàŽï ¬32À°£AsÖІFaPÿŸdð4Ö—„Bï,Lº7•[‡‰Mèͱ†¬ÄÙdºk][d¿KÔ{òcrG¡Ñ| ê™Ö²kéH‘}4nzVv~"/¦÷BÇp9¾^feó]Gs0yù *(LÆ©—VpÅT8ûc”ô%<ÿâ®áÇÔ®<#ÎØ}æ)§~:MP¿ çæ‡óžUÞ8ýÎxêªýŒ·ìÆœêÁ¤w¥ø(å »¿qoCFZÍÃ¥ç9ÖÔØrFV]Rãz}Ð{Ä¿4jðÝóa–Pš³ìúl›Ä5MôÀÉ}"mX-³Ö€ÙwkºYîÈ*œgÓÖ†ÒÕƒöR)»é{ê’ Io7|'qs‘):Tþ¥å 6ñg­@¼ÙŠ›ieÑšV7Þxé îÐ2 ÿ]«N åŵðDz åÃ]©æõÝq0*ŸßÄŸ’ˆÜµÁüݲ¸ûÿkÌ®>7"n& >yíuÞEÌ®|'9Õ¨Ò {j±?Ž3KO6å΢ӟO®En`Ç.ÜǧÕèT:Ý¿f<=žâ¯¤¹Z¥,Ÿdýc`ãîLvyA$d,Ü ]ïlÐ¥ó³íÈŒûJ¼®‚;Ì‚ŸÛB sɨõ݇K>-¬ù›Vƒé]XùœÛìÐg[Üt»–åUïÇPÍt°~3MÂ`þ>qªx{ŒÊ.t¾é+E7Ïp¢eêdr'tIQí9opÆ©X$ËÚH†»)Û±*.'³1ç|ñÃüÿíÿ­_÷™ìž.—à4"ý ƒ×ýÅÆ ðç|ûµ·×ûíĤúL\üG‡G<üÇέէNË.Czg h\VåšíaBN9{_·+¯ÅUMEVƒö^äæ®8æ»ÆJ®f Ï“lL‡ ”ú„£í´é‡IO1;¬‰©È]Å›}Ù¬¢ÙB ña&ï< R%ƒi\Ý´SÛ„Åw@æ¨*ü¯Ûõ¿‘¬ÃëH~iz­Êñ“=AHTb“YÕ¢´6K + ÓØFWm{èKí~¢J©;Ì»«‡þ»wÃé3/¬vrÿâKrÔ0Höè²bm©òdl èò“ƒh`ßxþhÚ<ª™ þg”xÛ=-.Né8qÎx,¦Iñç¤ÃlÊv9ºuÿC!}z6»ŸÅaèÂkOpÊ«0Ö×·… »Yš^$==& 6ðq\Qïß#Í8ÈÕß/‡vã¹XxüZ à ÝGÐcñ` ¡ÙÞ¢H¢§¡÷Na.1ÙØ¹´$r¥sýS#R•dÁÃïaK/¼p’“_ǧD2 Xšp#ÁÓCxpöJºþ­ ŸüZÝJ”Oä ÄÿÅ2+tÊ`¤é.rNŽrñW… šFÁè¯'†èðjº“¾>¼é²$üÅù #èãUµ©ñ”E¬`RXìÿõvVۭɇ8EaK!©õ:ì–}˜}{÷œ¸É高+oBÃΞ„uõ|‡ÏžÇÎÄž‡Wº·…*‹¸[ Ë¯'{¢¢^Ÿì¢ƒÉùö4ºÀ†wÂø×iø÷E ïí=_Ù­«šÏïŒà¦Áz¶¥qŽŽMÆìçsñè©Á<Ëä©ðHš*ÊO-es–…ãˆ`5.¾#“,šp‹ôcã↞ü÷•Xér§eå†TìÐ(jçlbj >Y«ë7¿eZÖ£ 3° ÃW'Bü& 9>¶ ¶y›LÍ%o8ó§\ì¥uÐ?–çÛzà~õV¨aëÁ­iŽxERä:aé²åÐ1æ®~kÈîÕîÇM“cX»qfŒØ ·,¬iü‡õêÓ—ƒÎTì¸u;„P'l ª¬Ÿ#8t8œUÅ(ðg«ëPi¼,>Á€žý—­‡¶3œ­;£Á™ã¨YÛxÐÒ˜÷<¤.3´8îŽZ rÔÆí(®ü%‰ç.æòò†üPp~ …iqW ¤­‹(…Ó žÍðù—9­ø|Að¹ä^¿!V£ÇÒs…ðpð62îB~º”ËÎë§Ï0f›ÆõTÀ‰+¶ü°ÿ'8VUM^¿Ó†#ësPÓy?Üní«naÓ¸@°ÏnÁAÅ"tìo?š= ßh+ã”­Yd‘Ÿ':ýŠ"?ÔŸ³5Õê$R~ x|Á­osix¶0ì&“%éÆxXo/6öÀ܈yä´P‘îß,Ë춇©£iu˜D‡‡´ÿfGŠmð³ô2®PUÇ?_€êa—H‹Ýlü»úh*½€Š(2$å[».½sŸ+Ðx5 RE°;:“=øbï|¨QþãçÇÉÿõÿ(¨Á¹¶‚oQ¸àÒ ÓçdCoÈCÜìpܱ¢×£W¢þ¨"˜®% ù',Yc\»ðÛö~^Ù×BÒ¯8ðT~‡œ:vþëÛü9¡ Îù¨Ð'º¦lE{ ºý@z*e¨m™=ŸòjÁkj3ޏ¶nŒÎý>áó ¼š¹ŽÎÊØJ’¿‰s5+}ð…q•»IÜ¢ ¸÷ý> –Æ5¿Ã§ÉÅ0yÍ6œ™ Nz÷O‚ø?5ìšvœ~ė½/#ñ™Ð…ö¥éróÔ`éwš…Lç‡^ ©9˜$Å_ù¨Áw'+Þ}Ï€&U™âñëÞ‚& æ­$/ö¬`HŒØÖÿÃSÒ´ÄD&†îá­oæó_aB¶{-¿ôr?9£³ã-qø~üãÀþ‡Í¾ó¨ý8œ|YªCëÊGÒ‚e°âã?ÐÆ…c°³¼Z玂Â6C4_ì³%õ”*‚YofÑmŠ”mWÂbåÛÌF©f$YÓâãî4ªÞ™Z<ÀQµ8ñƘWf&ácA0Ušž\”Ë¿ô¤G¬CØãKù0Xi9Oé°¡-sÎaÑìaÜâ¢.o[jÇÇÚ«“%ÒV©*P»k‡ÙÑk~JËŠ,N¹Ê•º9K¸Q±~m9·á}+VŠÒ!4´}&­«úÕýë§íÇÅh·7 –5t£Ômrêuùt|ÜÏ'Þï=5àÿóªªS¼1}Ó1Üÿôózóäç+ó-é¤É6}o*x`ê[$ÏÀò¸¡4Jþ"™[píôtÁ½~½)}g~Ï'¯s)Ó‘ ]ÃÄèÄ¿ øõòO6'ÖÜLnƒ-øÃýÚ¨ùSŒ6çÚƒæú˜½ñ6»Ù½3©Ò[ò?a´ú:÷Î,šºMŠÛ SOã¿(z©u1ö%É+CGcXõ`ËI¼AD‰“›¡ì­.[¸ÃE_ù`3Ú›}Á׌gËÅÑ«}‰  ?¬?ÝÆÜëýëÞOî|mH ¤Åkðw¯0(´úÞEcß;YèêícFʶÜ÷Öiã $ ¬¿ k~ ùìž­ÞmÏÏÍ‹cÛsíYТHínoöä¥oðнfØûè4üí¶åþw0t†5?¨0Ÿ6Ú;c]z^•=ÉGD*’ ³}ù‚£ò<üÔeŒÃöæGxgy *äf±5öÿ»ÿ»*ò {h}æl=Œ£<÷Ãâ?„£ß#çfÇ7õMÂY³ sü TžUg¢éþÊîè-ç“| Àŧ¾7‡Ï[ 3ü6žÕwàÏïH°z6|È>mÁœ›¦ÏáÛŠh[YO¥<"»~Ÿ‚¥ëjp¶¤2W»õLøŒw7XöReH“òBsö¬×àgü4¸Ûqw.´‹C•eJ<]:Þ~SAóëýLKƒ6ÙŒ™:!Ü¡-WC&èß‚š#|¹ "6›ÝŸ¼’ïYó„„«ëV_yVAŽêÌ=Ãh¾œ*¨­úßïŸX¾6ÅásÞcòa”­e.¦pÛZž 7ö¿…î+òبé°}c8™&ؾQΟÀEö4ãò¼§àp–u1þÑU”î¸n «cíq¿äq¨‘ˆ€=/–ò†µÛÀÇêl(8ÎU ¥¯¢yô®x<÷U¦wâ|7~cädµg…€:ðiɵXÕ²;T7 Þ´ÅÔI‰8%² 4ìÐ{èZ¼ô.Äù×âK8û¯—ÉØ…‚IЮp)Tÿ–&σúù„ãGÊKJÅédÿ¡üKÎ~ô¦öܼŒ‘Oåøè½4=‹ ðŸpTì ;§ïͲ‡ 6G‘g#“Ø‚LYî¹5’3³‘52Ôb÷BØñøŽ Í“1{¹N˜YŽî'ášÁç±â¼¥¦W-†ƒw¯uÖ³7K.àÐ\IúMI‚ǸïÅÏ[Ÿ`Wf.–y¿†QV.¨µFL¸Jׇل›º†õ`¾…Ë|¢ØZ ê*Û׎”³çUDZ"n Ö˜‚¨XèaöoIÇ?Rp÷q4hñí{®Aøo]ü¯îùõ%3\Uü‹Ì?·¤—ƒ~yIRUIY˜ŽM#¤òßB½T_+ʶû …2™0Ã׆ú®‡ºikaùvI*ý\œ¶nýI¢[óqÖ99’}kŒÆÜÂ8 Y¾®+S½„àÛS€½?…|8G'¿zÔžs…åj€‰Ì\èñ¤&DßdJð{©ED(ë‡té¿#ÑX°s3\Õž@œ×¬aUvâ»î3¸-Á”¶ ]¸„Ì/ˆ0¸BÆI Q+^ÕƒŽà2£j§e5ç°Lä l H…ÑeNüVÑ .±º ú’FAïJ%â&3‚¯–ý kE󛙿|Ä.]˜Ù1–8£úEÙüÇž‡²R‰Ó‚ …ÉžZŽÜê,¹&qÿŶ‚ˆq7( Žf§¢ÏáÎì8§{ƒ–CϘ… ®ÙD~µ«Ñ­t&5ßDªð€†:«òÝN²\§É®;ÐÞw~‰F+£„~¾· w–!­ "îJ´n“2›—¶x¹áöŠ÷Lݧ\ð, R¡ÓzGR‰ïip`&WÝ—Œn'¤IcéX\ª2òO¥bÅ›¶?qtå`ï¹ m¦Îćšá!sà{¿eSgëÂùÃθ颀Zêb“õ?vmï 4â÷PìãQXë˜RýÿñÛ6ÖÇÞ#Âk>aÆ,+AÓ¤©]´ÅÛœW©p) Ø‘nS¹ß6/&lU¯)ô„î®ã³ W?¥{¹8É;o&™éðéç1"žjCoÁÉm£èÇlS~ÇG|Ò 0 ù+ŒïT§7¶ß€qÞäáXm:ëì(~Í£A¸qÌ_¦—Å"N ÃÚ¯<µìfDãw·ÅBƒ] .bÏ/¾Êc£~H“¦ù¼ù¬ _5±j¿Ïç¢i¡¬¯s3[T¥E•feðž‚{ä’ÉS|uÁ5™Cg«t´áÔ?ÏPv`ÊOìÕ98`£ eÄØ;XðtܸöNq®dc:f²W]gÐlõH8TÃhòû )xjï=öâº×N”iÑ÷"à¢^+(,ȇÀ¿‘B‡w–P~y¿t$‡ø¡‡‹{äC1½õ»¹öÓ“`ÕŒ­üña+Þg³›¯Õú‡/¹ u_”…Öd“b¯&\ò/¶ÖÍœTWnÿ)Š†Ž¿Š×™&Ÿ¤³•Íü×&Á»˜,»„¹÷† &¹ð÷™S¡kÏY2Û¦?ä2¦§Qæž f|xMÌ•”9Џp/…Â%ž²šÈz,íý_ÿ‡õÈsÊȯl-†|}¡'È =ît£ÛÜi¼Âs0M²S¥ëg÷ë¾¢:Ö‘?’BÀ,>|Yüm‰NþâÔÕí:8ZQ_vHêt]; =_Þ(ó‘UÃù»yhöª‚¯8Cda1 \€,Kâ”pzÎGÈnkI#±VŒ.§Bž¬,dâñçÑËá:àÅ×ÛðVÜ–¸Y”† ÂÎÎ’_ÿ•}o¬ÂUKÍPZù)Jßçeá[èÝ~PŸçMW{䀎h “ªÉÃõ+øÝãA}ÒkH: 㻈èð…üׯç‡PÁHžÈHE Ú²øÜ5¯Z¶©mžTg—¡- ]Qûèö*ÏRy*TJ=Ämë Á÷w6,© Ú*CøÒy.T-Á­4G`‹áHÌXVÀú ñ”»¿²(×o8Šß3ò‰Æƒ»l‡$¥ç„µ¸ÉAgFêý?U9…•«O»<¦‘5ù¿!iÃ{ÒèN’î¾À)·5ù—òzV©8Ûüà6&ŸÏe#NhÂGàÝIø±Ç€¤ÏNÆÜØ¢=c4öèt N¼Ëd)~ëÈ’ÜÄT=&¶g ¶ßûÎÂìß}z/ºM$‡Ó ðÛ²[Ðå*Ço±çìiK`Ž—º]ã›$ùòÊ #ù´¾ß$ƒSàø½ðkýt©Y’Gƒ¨ /èÿÁ™×#Yñ,Y.»!TØp9†t‡Øûól›zCXÒÞÆúÇ nˆ'FåÁŸÂÍxn~¸Ë ÃÑ„è4ÏŽ_>ÑÛX˜x1,Uv Qª¡WßÖ.Å×Õ¥˜¿¤ê&,D¾ä,<Ø+O£¾Ul#¤vw‘ÉoëÈË}‡É=§'º±qé¢Ôäq¶®òÎÊÖäÑ–™ÄrìM5Nx`v Ô9<“…#(þ^iŽ•!¸ºóùWu <¿ÞAd"nüú v(MÃxøÁ’Ùû {KL¨‚ewè×òœœ_À}”œÁëQ¨PI|<8]Wã?¾ÞC±uýœ…ûöÅ@Ç«z¶;s.¦áqˆh÷õçÜê‘Í@'uj9l0±F0Ù )!}`}ó÷,ãp³¶ +,“ý”™Õ: ®eÑ…H÷ &þ·ÿ]qoWÉS)g_§k· w Òe²*]ìð”dV§Å¡ñò´÷.È ƒ› žk [‡¤ÃP×ýøEG££y¦f aè %/ÓȔŒ^ Â+FƒaíŠÙxÿz)I— .ýºlüušü6ý|s˜Ä-ˆÓýÇVz<Æ”µð®"|3 YÇÂ|ëMpÐZ-ºë}!ZVm`ÛüJI¨ÿ2½ ˆy†¾Az"—D¿%‘_Xá²-lÕ!eXXgJ×ɧ’Œ_öWësüibÑ!ZcŠEÞR¸ÌÌ# «‰hF%ŒÌ6ˆÿÄì ¿*î-~ÀvêW¡ãÅbþ¹¼wec Ó°të,Ù”;>ÿû[Œ¹%†óš‰ïØÞâÔì¨îû'à[‡Æ¢×81¬v:ÆÄžLãy'«c«§ÐêΰË`=Hø½û/)ßóÛ®óŸÂ'7=‡åÅ`e™ ,rÓÀÀ°h®–Ž t‡ÞNÌ:«Id±^{ ^·1OÜüÏÍá«Îd3»y—!ئüŒ'R±†Ôýá Xš/„¥}ª‚ Ÿ¿‚o“^cLjdöÇÓüÿÆ«yXQ9+Xð>J#¹àOMÿg9€ÿÌÙMdü‹2v_=M¸½M”L‘¥è¼s+hA¿‰º\»K»Êç1;íàõ¢þþÝ¿5MXÏÅSNË~9æÈuDTÁu±4û¯6Z(Êœa{YCP-¹f®ICÊÛH_ÆqØx#Ô9Î>_Xùé"TÍ:­w:ÉѱÁhYø™\˜>¿ÿü ð$på´ÃùÇ —…‘Êb®4ã‡sõ‹uð|x4¤È:Â(ÍóøâÌ8œ¢…éoc…e×ñŒY49®ã„^{ÁÕ‚ aÀûŸ$Mb< æløñ[שּׂB~ê*bð¾+°dñíþól¾‰jsÇ~QôÚ¥XK®"'.̤WÜCIgÙ6˜¼r*þóyÃÖMÒ§xÇcŽÛÓˆ÷S«Ÿlj¡¥ì´ÚjrhÕJ®|;g¹×Â3á5ô™ ˽ü¡ÆgʸNçê¡3h«’2EÁ^!¾t>‰‹ÂDyêÄXØú¨Ž-»Z1ëNb”»½ÒV@ò× ÑyzŒ'ëžÅ0qàÖ :ØT0£ËÊq뤳ðùõ$^”eÉ _O  «#„ý󆾋•`pȘ)¹ÁÔi}Æûþ±ÖôÜÂ=(Ô•E Erûó)¼iØÀþÿäQÞ,>wqŽχº¬¡ú&ê(½#‚å6xC­¡.þ‘H`—LÛàsc‹sˆBél_ºà¼-<øžßráJ ßjtعöJôÖ‘ü=Þ+z‰ÿWtŸç³.‚ÏýnXâ5/ÙUÀ˜‚›$u¹ùÞW`®ž ÊûQKXw–„á´¬.qpö9£ƒO·à¿]‰Äsû) M7† KøÜΘº9†öÑâ!Mýq&˜GÇ „®cŒéªU>Oã0¾O– !½hùâÅ|åÍÙôíÞÖpyw߬N}½äø ‰eÂ[‹üÿËàëð|È6R¨éñ_dÈ—1§úÇþ²ººß¬¶:2ËõèÅÊ6Ò“îÊņM%ë× âc½ÖâÕ!¼Z_~¼ì gÕ;^á*Gˆ]: 7 #u[Ôò™†¦•p%% l/¢*1âê÷®â¯óãq^æBºõH%îh߆w¯‚ˆ›ªÜãïM–£5•o÷•ç{’‚‘+¯gR½’\±Q™oôäÕßFÂë§w`[??Dg Þv ] raqp(,ÚÛÇ$žúðy ƒ¹å‚S‚»ç3„;}Áîů`P‘4—WçUg–Ð:›é¸býÍÿ1rŸpÙ†¸0x*¦‹Œàxu3LSXe×2yjgö- 4Wv£ÑQ%|zî#{ý“#VTý¯ ýó2>ðWØÜ`O=Á#ð°ÙH:½l1ÉÒ$tÙ–Tlèi…è„èóNÛO€ÛŠ8È[„ÞCÍyÉ“¡ÔóY–/ðá bŽÙ`û«d>)µ›ü( B|/ê ?_v}íÎlÌüÞ-l’5¡Î „n~šÀ¶ü¾œ‡Ï›cÀÙMھif0¹ÏŽN5Τ%“µÇž¡õÇbžmtˆ¶ Sä·þ¢[öÖß]¯d”\Ñ„ç&)ŸÏ§®6ûIº£ß4>ŽdéÒŽ]z0ר^ØTZG½Ý+`É@x >BûõVîk†Ù1Z0cÑ2µ®ªúàÌ <•ýŽ==>öüþœ¡±é*Ìö–¢öãé´wÓÀªXŸ:OÅÄèC0~ÔÜè,—GÚÁ‰7b|Žü0Þl¦EußÊSá°pnÒÞRK£gÙsyýJø*MöÕfÀöòüöôýÕ‰¢Ùè¯UB:>â21ìê­QüËØ; P˳”×qÛ“ÆÜŸŽç“{Fð-wÐ20²7ðÿYBGr.f/šÙJð÷ŒPÎh=¹™+¤ÍǧQXU Ú"é(UhL%ªo°ÿê–÷hmD{Å~ùi$N´IÀðkÓ)ûD’žÝ {÷^eY—8·ïd ”¢ð(­QFUÂì8ì;N3$e©‰º<íYø®oûˆ‹Õ?àÔ‡¸Ö YÁ²Õ–$ÞRþp®'­Ö1ä!)‚,­Ë øL—o·A6.¼¦™ýa—o¨®»=m³3¹nšØ÷]„ã²rPX°e,Lifl;uߌÿ×£ºIq¿UþÜÜí©üÄÁ”ïÏbûV¬†-ÝIö'©Q½Ù° ý\*ð XB_ÞgÁì˜åìÃÇ“h¯×ˆAn‘Ô]¿ÉæRGª×3 Ü”³ïb³8¹r åY´l¬š&é`n%œX6_5Ë£XYøäŠHXÿņHÁ-.âUó‹hgÉÝlÆÔ“lÏ:q¡¢r²Úó8½0Žåú}ÂFuLø(”›äðîªj<¶ñÞóî€ÑkKYÒÆf¢1d©výŒ:í°GQgž²7ŸX·¶9YYbÄ’sGCß`<ïš Ï{à¿k$ÍGû±gp§«'®üWøu>OØJôòàלÐ0Ñ M–—+ ȶÔ¸gº%*OFšƒÍ#q.å°‹}1ìÕü,6ïN&ƒSÒTÊ`½w%YX¿K‘FvŒ¥O&¢š·};_¸+PÛ]Ž€ÕýÑTâXKG(ÒMÓ`ü(ïúƒ-‡Tɦ•¥Ð÷Uï-çõ±©¼ ßüôý Ñd‰°¢³A¨nXÃæ½Ã¨¼ÂÊ)j øn½þ°íÖAv&Y oî’`Ǿ¿CY3¬¸RM^L ÃÂi¡(úЋ÷6דˆ½Ö,(CÔ…†þÿ߿특,´Î@î=ŒìŽ @£õ˜þ$€üø^YžÆ¬X—nŒ¤ŽKqCÖ\l|£)ж˹þPÚYþ‹-©I§^YI5W¥ã,ç:VÃÎYoãwè8í¦¹–Îí÷ÄyYžô¹B(›{†Œâ…ãqQBÍ,ÁÁa9ÐÏdû×i«c„¾ìÙJò7\fûB´hêɰôÝg¬ží‹µkÊé÷V3j§-ƒÏlÃØ¨ û¹Ê;=þîÚZøyŠ^ú¸F®¿ÎdÈýùÏE€[¹Îÿõ™.ú=C°´z Mù¸Àÿ§½~D´Ãï0õ¥·™ÊÔƒàÔ쌆t²s·­ˆSe$x½#”hãÖ­0nü0L‘”á–˜p SPûüº äˆÞ?¤D&î‡q¯‡@‹íJž¢Bïöv…½¿ˆwÞ_xþ¥ =Ÿv2¿ÓƒÈ¥ãÑäXÍhž|UŠŸ—J/݇ÃÏäpvQ4|™x†­Â±ƒ‡Câý뚇DtøâlŒ#d-öºÜ»«ßNÞJçrÐãRÔB"~œÔ(豉£,é<êÎê‰Æù>•Ê…úFŸØÁ ;~â2ý8i:“×ñ€kaáüÇïþ7vê;;höº“$øÊ ¦ßW †*ƒè˜=»!æÅ7¸¥e‹FÕb´6hÕv—ž•¦ õÙ·¯¶Tü¥Ž«Š%k–¤° ïJ\}ñ/–Ƈ¯Ç£L×Uˆ=¹‹Ú® ¯ü%©ˆQ¬Ö¡^Ò&ôâç8>e°µ`æµX[5êTåáËöàœŠ¥²Ä‘z І |¾¿#' óHÃîIüû!^‡¼|Yš—Æ{“ô±í©Jc6ÌVS§ÿjeé¶öwlç±.œë+‡ԚÒÓOªÁÕJ‘ëê tM ¹Ì¥“ˆºþ¿]) '¸ª1‹g€…ù~ÜñìJ(¦n°Å÷3”¤ºpƒ¸Mhôæªà¹éáUxuÇ—…bÁ ßjÑî|iñ|¡[m—0Ò¸-ç°x.GU_•mÅA­ï»ÐÁÓ…în,jé8,lCÝ[¯°aqŽ{´©{/î_Ÿ]yÅb•ÑÄÒôø¯šßX±·UÌçY-ü­«:×9`‚‹LCÉn 0m³$ê¶¥0Äq.æL› ŽåAp h(qã5Õ5SêEº! =b'Œ½>˜¶ì>‚¬Ð]ñ…ö3µ¹KX8ûa¥;ÀòŸß*×Ý}­×ìoÜ ÔøèNfè²@½2´øã øóR *¶l? ¿ /tMšLÏ¥þÃã.#ùDUW|ÔüòÖÛóü±Ú4Íð®ÀÉþ%ô˜|¬6¹Azõ9~“>HkÚ8ÆÙ¢À´*êðy9 ':Œ–;;óÛï2¹µÄB8¾„±^uhØáÃÏ‚Ó þõÈ{Öýµ–î8#ÉOŽÁÚ{aÚ3ú^² ï?Ê%Ó}V@êWáØé‹¨w÷A~g˾î-ZÚÞÄ'‰SÑFZŠjUlæS÷„“§1ÒTEý>ž_#üÿøKïÞC¡È:¿ '+ e¹<è­øA¼í4øü‰ÃéARX½$¯œÕfW¦âJ'~Êgº†/?œ!·ÿÚSM© â%"Í}_%ƒ¸¼.{Õ¥<ÄiͯXšïyj ›ì 9 R°ÿYÓnF†lÛEFÀ@U}6N\EùV¹Õø ÎâJ–}WS §ù:\xÛ‡voÆía÷þÂÈ?$‰y¼›WbQš5}`m‰cýÚñÌœ‹¸dïvØ5BѺl´&W}ýV°9㤷•Á{íxzçš1µ’!mîcðêòý¼£ÃØ/£l# GÛ4žsï}ú‰¹~ƒpsòd‰sØèC6|ü¶Ýx8£wå㩺1ôvýV”*G% ƒ‚o;y¤¨1÷8{T0~œÈÿÌÙf5¡^¥÷@ü[²t&Üä‹c¥) Î+PÕeÍBÙ„Oì¹(â`ÕW‚iÁ­Ø±Ú…ád;ºÝv(d¥°¾êPœºý%?N–§ôæ‚k¦$ÕÔˆÃe¡‡±ÉD¤õ’qOVèŽ1À¤)˜W2®(ÈFÛ' ´*>—ÙLJ&°oSnÀdÝïXý Ͱ>‹Y)ÇXÞØPœ1Ô§)+òÒ)"ìÆkmTÙväÎ]î±ÖoŠfíé4sÑ2roÈjpT¿OsìqáËÍØí³Ôúx0QñVfó}í%ËeDa±›8ÿZçÄ(zœüßó_«|}pyî·é‘åéêyB÷tQ$_²céÕ„¿äÒ¾𷑦“Àá«çÒEJ™¤ó~±³¥ØWöhËš8OU€×{a•ñ:þ{—:¾]ÅÍ8ÒÕgO£ûˆ1|ÔÕ|á÷ÝÖx½¢wŒç¥zÁ0¸i6õë9É—Qº(«·~~ÏvÎõ |s'‡¹ç¨ãú3Ĺ¥È•:-P&¨5òÊo« $]•>¨šEk]ú¹æ‡ =Ùòûû5[Žã¤ç­üŠë~q.!5g¾ lZ†ðˆíGñ•¤WZù4ZÒ^åPâ\6€_÷RÎYŠaŽ'!h­*·šìN‹²æp÷ß®tº[ìÐ2GiCýý>Y+ôƒ¹þa¼òS U?u„çÕå®I— #çÎ u åï æ&Ëøùc#¨ÞGG„‘±øüÂOìÚ$Ikw/¤R&~š7 ç…¥à]µ šjç fúp¾ÿi “Ðï÷Ÿ]ʔsí˜KvDÌ?"uØ€žjŸ}ÖŸð~©-¨ï9g xuö7Cùõ ¢ÕhB½¬”¢sgÛÂ.·PòeáÌ›–"uTÏ6ŠÚõ† –L/_‡®lÏ×(Lå.³‹èiÉwëðlI5êŸù&ŒLA±%wñæGA€^^LÐý¯f¦;ãî¿3èÔ{©HE=œM“¦È' lg!NUO¯~ºjmRTlõ|ž$ÝSþÙ㔀ùÀj?`“¼9¿å^Ô¿M‡$.MÇÆç±=og#kyˆú˜ÀÎ?Ãe=³pE_0^—n0¶ïV>Ö‡W4?/0‚#þñx=ç†øœ©Gïå™Â£¦5üÇ®b'ò ^`¶®^hÜܶ>bÆÕwÁìÓr¡]ã^8?±WÙÚòÅb|“¿œØ¦KC‡ÑâÌZ4;âY­â>6õÆÆçð'/^ ”ïø‚Q!ÃÐÖV‘ÿW· 俇WŠ¡!ù";¼`^µþÁûÝá`Û ä½½öî*Þ¸7›VŽMÝSïbŧI0ô]`î”›UˆŸUŸ²·s{ÐúÀÂÖ×xuf ÎvÜC\‹°´?³«wuÂØ* üèæO67¥ÅŸ³©ÉEh2a&¾R†Màwå+ŒYa ÇëÏ¡¿¹*Ý#Û¾·Ý¹ åO(½öry.‡ÒÚ)à×T*ܤ§Š×ò¿°U£2ñ`ÕÁ‚ pâá-4JÒáŽÊâÔ-^ŸO:Æ 4ÝàvóQf1Q“;ü;j…pÞJƒwjÞ…â!úïÌü~Ÿ«´EñE»ñìãÇðs´ ,\¼„~lމϊù%‘`¸fyØÞ¯%hzäX*m¿—.íñÏãhÌT{Ð Þ ÆÒL]9~¯~#,t?†t0W´†«{E¨Yt/¸%ˆÓ]3z±ÿzló¡>¯uqQ}=4éBñ¬»8VŘš÷¸òç—–ñh…ÐZV G޽ WÙq©™àŸ;“ÛZ6@n£€¶5‹òFe jz) ‡´™û•½øVЦ¯²Yæ5ú|Öv´t8Oˆz ¢{*²ÆßncÑ7¯ÁÈéRì‡{ò€ý¯yÀg³±WpkÛ üdNÊŸ¾»b}~.Ÿl(xcK㜯חàû£ n°kíNãOA`Wnïluצ̋D­Á§q‚ÅÍËIûŽžAͤp‡4}ÍaßÎDa(~¾8 c6 Ì²pB·Î¬‡¹ë¤¸à­&¿Õ9œ° ,s×Ã…¦x\Žï˰¤¥í ÆÑ¢ˆ6j¸dц{WñA)†üˆ»Ý0cOYP_–žaº\Eç& ÎJƒã¶«xÆ—g¸ÛL–‹{lER8wU›Áã[šûå ¶ª¸ÿ·=(\ü JÝ~ô‘Í=¸Ý$Éið¤rþ×6‘/ ×à“Ÿ%15¤¶{5ªd.í×ÐüÒ»%òa)ïÀ×ácõÑ·ièÛñU–e1É=@<$†“Â2GºèÃdþlD07|z ^(r¬\dI/M;!4 xÍv¼'smúˆô¨ÁôT„&.}zë÷âÏ)é(ß6†ÿwŽx§3dÛBÁŽÊGä̺¥ô‰H4™Ùí:‡÷ƒ•š.ÒM¸^ 5 ¥  ^Œ”eÅeÏҤݣB•áxë¼ù: ¥/yS?cW‘ð¿ßÿ»Ë `ß(œ-6 µÕø‹±¨löq~û¼&žp’£§fq›kLå`9_µ¢ŒóPP>|þΧaçݹT¾Ÿ[A‡Ñ{güW3Mï}¿?eÓÙÒ?›‰•ÛÚXËD¹ã!8¸FÇ»Ê}¦@z…5ß>&˜ПKEZbpÆÍB’÷é3~M]ÆËšòÐï¬ËK*¥ÉÐ){lè—xyÚRÆ„óï#*íæ¯õyÉx%úeÞ%¬•ÞC‡ÛŠ0îØ?”÷Ñ‹ ¢<“nÙò^¾ö‡ÓÿÕvW>‰~Ïæ ðŸÍ~°gâC²£ä‡pHTçYÑiwcQã”l’y —¼Ë˜×œø\ý9ìk Dª?ŽU¥o¾`ôP.µbrµ£vÚiÉ{ê¿ã*u¼uS†ÝŽJ|ƒ˜µÞMiÓ¸\8ýð¶½,ø§¸tùcSº±‰*ODÇ”t½Ñ%tû~ Û‹3P¹ð:Þ.k%創ìÜAuþÁn(©ßчžâÁü€æt|˜„fO= g¹]&-Oþ2ÁÖŸ!.ºƒ„]ùIVGžcÆÁÉP­É“Gç­¯‚Ñû† ˜Üëeï[@Çž'ëß¶ïS`ð‹(æ¥JYR%yü†HíÁ”Oºtj¿vºèv^ÚÑU¿ŒèÏç ð.b;8œåžÆÿˆƒõlb}æ+KY¨gïî¡u£ÛÁÆã¸àQþdª²-^*ëñ¿Ÿö^EeÁ¼@èŽçpøVu+{-¦ÃÂKiš$BnLbùf¬¡û®vÌg†î"bWG‰Á´)ø(š¤£rï52!z3jkëó›sïÿ×;š›ß@ oÆÞm51>;=ƒ]Z^FnIJ~‡„ó…0M Î#窰·LÉ¢e!Ðvò¯p~ÔÈü·â¯ »zûðÂòLHiW¢‹î)Ò‘‹CØ¯Ê ønøŠíh£ªÄ}é-˜hvºË™¤rܺö(-rùƱ‹6þuÞÞ3®Í‚]ׯ 7ùL…ŽS3ÀþB%^Y¿*öz“­ÆštvëE¸ÕYJÎzU±Ak7¡Õ»CXl¡KK©(“ʰǀ¿ò»ø¼°»­}æ\S1Xñx ­yv5”ã#ó‘2M—V¿¯eknÿ–ºÌ¤m€Ið ¨ÜÏð¹W(DœûÅ¢ãà¼U×@D%ã|Ea]Ð>\P&EóÊŸ‘†ñ‰ù¯ÊmIÚâ-XœPŒ±L§èhó™9Qtõ¹‹—¥¡ÿÀ N€`ivõ{O%²{d´ôkªk'rp5;#¿=„‹Ã2…³ÇÔaðvœ™#²×+°Qì,¼jˆ…A+¸P{šõ“ñ`ï^|~z-1.*‹kÞúˆ>‹¶Rkw#*¿C”ëæ'CßÔpô3koW§˯ÁùÙåpÐÕ® aî¯2½ ¨lØA# ÷cS•5?%~Š=ü9NýÙEƒTb‘Eí¥¯ìÁïO)êÂÜEÓQÏÑ…þ¸@™rÎ^|õÅ™zÜøqùÿãorBp+ÀkÚ²¨<Žºü}ŸÈ¹-?ðÒ;1§ñ½æ—/\µ® 6õI@“t0ó9lO·ýƒÊ­!Ø¢¢ ¶9µ0yýrûØdfñ#ˆÌH<9~ÅÆ¸U±M%õ½»þý'&@Jê?$p /Òc Ýø—Œ«Ì]ã|¾™K–îAû+âô _ŽsßxYnîŽÕ™-ñ™›?$¯ôåÎOm¸ÕÅH˜Ñ8’¿ŒÕåïXñƒãXiÊ}|®€£ö¬Ñhk¾·ºÙx§Ð·¶Cia  &'Œ¦ÝÛ$éç1ùl±LùÛ:g`ý-9Ȇ¿¬f•î•ÂÕÁûà”T¾:r¯æ*aí£ ˜ò '?; ÎÁØÔÓDäÃR˜bLe¦µÝý9vžN ÉðXÄ^wç@¥Ç2œðlÇ}/Éê“ØÌR@ýÆB0 v¿u†Ea>¼Ü­‰t/4Å[n³pò‘Õè¶Âž¥½Èƒ¬CHoå2ø¢€¯HÐVJpúÙÔ™ w3ÏW”áã™ÃÉ©žZœUöB·?‰ùif@BOo Žžµ”èzßDµ}µdÑ TœzÐl2ù3õD.W¡WDÑj³êµIñ“Õ»òÿò®Ä\ý \¨#oj.AŠm5™‘Ü '¯5âç#Áð½n.þ¸qºâ¥I¶ÄÇú1:ê™QññT×Ç7«  ÏŽŠÒ±5=à%\½r’\õÇcì4U–äl˜d,Æ…6&4úäSPÕÞŒí>B¦»û¶;¿}˜]%–°Xé¦0Z;F‡®gê_9éØaAP1:<€ÓïÔ“—ݦ9|(GL¦McùÄD7a¨½'”ÜÚÍô¡úIñwYÃèFíP–\ Ë•xŒô1Œ;6•ì?RÜ[UîßñÛm˜1c ÿýHŽÂûß¿°IÊ4¹a.l¶™‡*>Á_wq¯çU¼—cO×:ª“ÔztqýE&W-‡ ó4HU|F¿º;ókYÌYoV'û•li-ÅFñÁðøÓ,ÝÉõË×rõ³ª?ñÿÑbjµ=Ä­#€ ’;o[Èå³úüÍ•UðÀG‚V}îÏýí°o{›ð-¾_o7NZ÷?$óÓ^?Ê÷¬ˆD5çòKy(ñ3L€CÎ3á±R4Œo‘åqŽ$ߺü1k4±Ÿ­ñ‹S¿]ÅÀÝY˜Ä×Ã3¥ÍläüѤ¼bkõÓS9àÑÿ=éž–§2³R¡>B^u nݹ '×€C[«àmƒ8×ô¿E^¤`³Š‘oq{Á|¥(qy.5õY½ù*k~¾î ÷ˆÏáʱ-`P•†¡¶@ãG¬/•á›^*ø¿éÛ»˜±u6> »‹úkÎ1Óf6'LF̃>wNáôÅB–æö¦¹áê½Rñ'M0îG3~„R–s5ÓçBêÉ2ážãtåàMvýc™sa×ó…´Ã&š[­Ø(Ø6¿O þÁÖï¿Ææ€sSˆÒCö*†ÃuÒK/:¯ ªC“£Ý`þ¯“•èÊÐôÄ£ìV·½Ü0†Ñ•2¼a®>‘MÐ“Ò 8¾?€êNõÔÏ¢æp@ÒšNÑwgJ0ñÇPn—½æÊŸ¬Àû'7¥EvôœËc R›Žºë¿B¥Ã~‡R´9 F5þîKW± «É˜|üÖÕÎWÅiôåA¼{½@õ·‹mžköóöáQ(°´àóU¢`ÇK ^«¸ ¿.Ö¦Ù>Ù¾êB[™™(Þ5 å†ÒQåÁP*±~¿¹FKè(mÃ>Š@õ®¡ô_z46Ê©qO;Ôƒ0Väúº?Ø¿[ßOü’E¹ÝŠøË£ u®GbŒ¹é§ÛËBઆ!}»K‚—ÍÿêV£øc#d¿ÏcDK&ÆÃóuè$xIÑ©«çÑû7v2¿„:¸&s­ U"ÙëÿÜÀ¯lgv²pŽÕ<¾ÚRظ~¹»µ–ê8•¯IG"}ÂN'[Æ_Àø ïVÇ€Ùçfí5+U/ã¨Sµ°¡O—Žè×Ï»O‘†ÅEx:%JP¼+>¹E¡ƒ8~užÁ¯\–ä†ï ËÙ3×_‚ó›'ÜÞHRÙ8!%¨ÿ2FëNÞ³›Kçµü"Ôqú§T„»j¢!ÏÔ çÜÌf³Î¦}Ye(8<„¿î\I~æŽÂöÕþ¤Ó²Îü˜@tVßgä£ý£êjÚ °ÛÅ%eªq´ÇìmoÙý£O—žÀÁÚ‚¥œ%x~ï¨oßstfÓ³¢ÃøÆNÔïùna¬Ã ÄgƒYO¾ í©.€–ušSQ Sv¿ÁM ¶žxìñò÷ÀíË·K°Ç¥˜<‘``šØÀÆ)‡ÜOK@ÙIš<öÆošb\Ⱦjl„yAšsoC¹ åÛ?ˆòZï¿l¢Ïy¼?,MQ¹ê4ÆP‹¿:µˆmjÇëâ¦Ô®r=ôÏë@†.:µ˜<{QÎæŽ›V|óîô¼›Žñ¯,ùq9WpQ\Dì¢Ôx‡?Äõk‹.­‚ÇCñCÞC°¯{8€Ÿ§-·Ý»ðÎÙ÷À'|ÇK¿ðHéFì4ÿ ›„iX¤õHsè5Ü9›FÞ_Ô§Iî\°½Þnå6Éáüm˜Û¦åHM’ªÙ¾‘˜ßÑ$”4–ÙZÈÒ3{ŽââiÓqÄ»;$ lMÌðCY/Ða±.ÞeS ‡“7µÓÑïäoÐø’…–f‡a¢È:qž5»Üt ekSŸã!Pé©“ fKµ ÞR”ßt¬&»ýéÙÌfg#Ï&4Ÿº™}NÏä-5¾Ô§e#|ôÞFdf ãÍE+P°Àƒkí=@Š„ƒy:æÁÆÀ·ðcÃuì~lƒ÷‹Sçc`¥¶”~AmÏcð·k>Øc  ’<|°)µ<O-¯ãÜkôýE=ê;=‹Óžá¤”*¦¯äÄ#Ë®ûfuº1‚>s >7M¤Ss}±'é8)Ï:IÍVÐçÃøÏîhv»EU Ì¬¨aÛ~ ².«Ð)±Bç™',hE‚6œqI¦ÊŠÓûS Ù÷wéš)M¬ì*ªk²M¹l¶‡`éÖ.vQ£†ÚO ’Àt\¸À£,WB¨o‡ß,@ûÕa¼ð\Ÿ_>Ü‚[/bê#²áSÉaÁ¸JGvç¶2|JÉ„Eµ áÜm¸Ý¤Q§Ü¨¸c¹@þŸßë’Á¶íCpèТ‹j[P‹ú/cÞ#GôžR±ëÔ8üâðtÊ:ÂZƒß\Dñáø¿?ZO¬àåœm0z²#Ͻ»u‰Ÿ½Cœ«L‰®^Ð| jcü›Ù8øáQb渕u`?Së#³íé¸kñcp7ˆmWÃÍøj;¡zëô¨Á‰¸¼ë»Q\Ã/g©Ð¯ß0â© WÖ¼–K&q±5½`0’}?áŒ"¾à”9PH›ˆqãfâ‚]ÓÐÌñ+Xi­¡.GpÈly¸qãè,JƒØû«xî–¯`|z[˜õ,ïæ]Ô©DÇÐlø‡ZËèóTäÓB.¢j΃£ëÙõÛ·Éß8u.åÀ—‹q¿¤ÿõ±Ht°; ´Íþ ¦Î7"é[Lжmý~ã$ú7Íä[ãocûÇR®)v$/Y‘_ñ{Øûˆº!Mø[á3¼ ÏoˆÔ±Ü7]¸?Ë—ÿQtË¿hQ ¡ú£=hHŸ‹+‡ÌÇÇðÌ Úó°†V™ÛÓw~,”žV»2€¿åZ¹ Kb¸áZ¸ÿè8õ& †uZ°5+aÕ»1Ì1, V­DS&}…áeÓø² ã1øô"üÖä56-èåå€Ù§7pá#E~÷èødr½BÄÑç†"×àcøçzìõvƒRUÐg •³?‘ôµ³è‘-¢Ô¯^å£ñà{Uø<»¥øœyžpSÙŸ‹Ÿ§¢v o’Ù7!*2_P“RV½¨úåßg»Šu¨Ëª·˜üt&¡Ã¥9dzð?òôT jð3pÒÖfâ"ÙåxfÒ|^˜0ˆíŠ<‹¿ *`¿Ê™ýßÊk‘$öš û¶R‡\¯ÁW Í;öc˜¦=â(àJÃÕAÊá§ó°Í!ðEÞNx} >o8[{­[hî0g³V)Ó8;r)Üxü|=®â‡£àrŸH¢><2W\+ÕFã éwvw+‚Õ­õÀæ¿ ÿõxö6ÆÝßÜFÍG$õ´9Θ¥‡o‹eñÜn8j¾œ­Oõ%ÿõ€nëXj™!\²{é“»ÔÏÃÏ ËÅt¦WXµÜãl&wl!Þ˜0…úž»Ž£×ëá±mÛ ¹.œrNà¤óÍá?‘Ýo-°©Ýü¯§33…·¿™´Gh1;‚)VºQ¯¨éÜÒ÷ 1J §/~*À2ûÝ4ã‚4Nút û§Ï•mÕÂ9“â ~Ãb:ÂS‘[ép¶ëV)váñÂý ¸+cå(x58 ¤®‰sã ?qûn!¶ÍÅÝDh«8´t“䣾L‡+›ØÒÀdÁžçðv¦޶óáƒîM.jU¥F3‘]ƒ€[Dø{Òv¶÷ʡμFø|÷ ‰T¦îË·bMÂdøœaÄçMèÏI¿‡p9zó‡,mÒÖ%›C™W˸ ð‡»šÖüo¢¡#j‹ld ³•NÓ—‡¡ðîÅ€þ\;…vÞŸ…óEÊ!àå'x¤ÃßíK…›qº´½ì!izœMD‡oa纠\ÿ1£“×`÷z>aè.ûsJ7Ú·€î›a´èg0|©l%­íÐÓT”ïÿò ssó½} >!ÖÞ8FÖ–Îç–4¡Ö3xüÎo¹nŒCŠÍxT}©š1ª ½¤E„ ¡é·™²ëYŒ]¯Ä]?èÃ5±rR+–Ï…Í.pûrèþÕ™éBÕû‘0þP=»¸ô*Ø2`Lí{²~á7¦§}—D_‚è«ñt‚»=å«ÁžXÚW«œÔ§§wUµ\/XYc Úk5ñ‚–=7íü'¼>­ˆV¼BáIܼ| ŸÉìÇÑêÞÜÿ2?^ãçÃ•ÝøL6/ÝâÄ n¼\åΗ w"î<**‹^0U§gÆ’c«~–%" Y=Œ±ˆeâaÁзP†uf ¯´ÉñS~¯Iõð´‘î­I…DÍf(þþ,Éó7qÇwÚ¸+uuúfM•¾úslP'Ϻtø¯ñI7æµá qÛÍG>›Mû_3YÝ?,ÃkŸÐÄi;N.WAug+4|?¯kÿoÿ@ú4¦—O â|¥æ`X6uR‡¹ÓÂpбfh·¨ˆgk WÙ æ²ÏØèTƒÎ £v!h$™ŠÇ»óÕ‘¢ 9î.oí †KUé“·j@ŒѯSd`ŠÒr˜¥-Ê“ ‚…ÃKÀ×Ñ‹¼.PÆñ>¶ü†ÙQºÄ)v¡¦½²ÔÖñN ]$¦QÀEÔ Ñs×6a?Ýn…Ã5µyÜìÆœ åÎ 'ÇþyÂÎ'át8» iÄ¿êÓ`vHËŸ‚r‘4lª{ ÿ"êñö–N,´wÃ;r6˜?c¿óaMUçîöBœŸõŽö+Œþ÷ ùÈ#pÞ;¿íC7©ÿc’ÛEø¶%•XÙ\ {ŸtÂÇ•—1õ¶?«íáM‡ ‹ØÊË:ïØõ–Ôµº`èۭ«âƒÀúË2˜h¨ICVÜ„º‡Ö|ó²©Xö,ºmgÊÙk°´°„œ]Z¦³ î–œ -ûÎ= ò F‚Fa³£€ÛÛ1w· åR8tå`’öðnØs„¡–œ­ôÄðÃù0#Ñ}@ÿ…:<ƒô{aä…\"ÔZ)Á’NQúë8ì_ŸËÿ˜‹ÂìÎìêîbøÇ~°½Ê|CÞ’÷'ëî…ó˶°òŠD¶Þ`‹aÝ>¥wõ9óÏݨ:©fŽªaY%œš-A/¯‡ ë/ÝÑïßi !¶ñ13°blV°Ò0(ó³ÃlëpXùb Æ”% Ê¨G$Ú.bŸM›‰×Ñ:íVÊÃ1ŽàŽ?ްçÆ9ü’× Ã6•óôÛpx\ð[ oÕ‚QQ!«Û *OÅìÞî Þ15$鯾NÁÔÂÓlæŠYpʤº&®»[Š¡…:xáÝ Ðsðíc~1;Mîwj—y¼ïZs³Osèé¯}ø¥Ì—×µ´àƒ×&Xí BÃÕÞ –y’Ú{¯äFŠ3èÆ¿Oyüú˜ü¶¡ð9b,þŒXƒ[<þ°´ö`ø%s“(z)³£Šéd~¹ìp®ƒAí §þ?,E…V#º>÷þ4ÙGÝ àËälÁd†y¯“pyù¶hJ¾];®èKãègžl_à8³¯9J‹ñ-{ØÍÆ16V4žbŸžpxè‹îª«™D©/ï¿u£Æ«´ôpZà ì¹’@*O)ð-¯žOk·ãÚ3 J*–ðLMœDU3;|í} íìÍ0tÿ8:í~ÿçãä5âà=á;iýsÔ¼6†î?‡R)ññoÞu¢úÃŽ©ðû¤)NøgÎV~õäY7VVOy£ÉUáÊœ80¶}[Î Ÿ(«ãk¯=­Çãq]³8!- ÖósÞÑ«àߊCN¸×ât¹kè¸ï(¾=© «©ä¸#0áµ¹æQЇßH ²°$ Ç…À”Ss1Nµgdçâ±È2N9N5¢nãݽ)äÓÖ•xÍi¦ŒH¯WËAÜàÜ}ñÿéÿÿÅÿâñ¿ø_ü/þÿ?Š·¹~è-œƒ—@Þ~U¼EOÀ=%ÎfKÞy¤>•®›ã‰Ó'–û© Ìî•aøäÝØ¯dH_W„•þ 0:ê(«ñ]™ ™·ãXTÏÙIŠŽ^Æ&5 á.~?…#²áQ…Ür²AueišLow‚ý¼zV Â;íÓÍh£ª^€ž]BxÐ6?ä®à;7ìç„ç•à¡Áfäu]¦E•²d·ÖÌù¹”Y~[…Áƒ|?ŸäÖÛ~'^/îÙ¾ü$ôÐãâ~tWayõÆœJغ¹ÜŽ”gKøð1ÙAË„¦â°¡úLÓ&ûWú“õ‘îXÖFóaêš•@gía×”UÉo…8)&@<®Í"CoÞ`s~ ðMa¬u +1°àý8þ¿·ž"‡7æ‘Ã}æLCú"jdð¸‚ÓÝÜág¯ÀñU?]ô4œ=yÆö8ñº!xt+““™zßg *æÇѧ؀ê ‚žË|,lVf·Ç¾ÅŸŽptà èðy¨MÂÆ Á¢URìÅ$ ²al2f'ž%º]1zAá9áìE©X¿²d+oŒ!bº&$ùÒ„÷ŽäNk;‰3ÌdòÇôP«ÓOM;ЧÝoÒÖ*+\"ºŽ%îR†°¨$ˆ“ͯ·“elĆ)O¸7<„3'aØð-Péj…Kxdú¿*NÏâ-N)Pe+©³¦jè25‡})5`¸ù=d|øN¿>¤;k`×ï±X[ôžç-9‹\¶ž º=Á,Ïv‹Álkl33 »]b¨mÈ7ÎäU. ûÅÝ jãd·Œ%ê7š@t‡+qØäŠ?L”Ù™ù4÷‰3'Y-NÎ5{íôe,§­ǾÛOj¹¹ýç1ÖDœš.Àª£+@ÍålöÛLÿn–eî“ÏBŸÍnˆ®Ý ?Vp©k'²µÑppþ?´^rˆÚF ³¾)ÔpÍÞœ¶S µ 27')"³ä!ˆüIÀ-®_ñHå=îRÇôÌIÍnذñWò¦>xÚ®LÕÔ†5– )Í×ü_ᄀ¯Ô,ÞS‡3`_k+|Ïr’31…¬b%§¹ãЇaor?7Œ'¹÷ácðþ›·¤è…:úàÉÀ ¼Ó~ CG¯«T\!ò/¹‘ÊðZ¬3Þ€§ŸÁ!/ RðÐò*ÎâÍÐzt8xƒS;¶ä~È’FÇ»ês #Étû"<ÿ,r!>YU>l`lÕcÜù!?ïÈÂȺñ¬ZR†=.ƒn€>0·¦ésUùÞj²ù} ìgî\ôÕ…6³Û\ZÂ0„[Å€ˆ¯0 ˆ?À¦lm&7Êcàsl‘?.ÇÍŒß6‰ØšAúCõ˜ÇÐ8"Y· WùrjMLðëCØU)D£)¾ˆg_~Ø’B+áúË Þ­ðíämkù÷oŒ‰)ž8“ ¤!eÓ5È_‡ä”s:K–Ž'7CÈÈÊb¤¤Ààl$[m¥„ËúäHÂ4–'—k$دø­Yð#o7œ*XÁîÇŸe­vªÜv¯14¨Å„„ ±…®þpYpì iÉ9B꽄Ðý„¹ô¦L-2ñÐËäóõñÌÔC‡ÉÂãlE2¤-µ.úxýS7žÈÌ…£½/aÕW7&>VÇ~‡Y"x9ƒV;[ðºU(‰¯ë&»vT“ †L[w=[³à%¬•a¼ƒ=°ªV„^E¾ÒâVU¿'n£·¿àëŒLà‡È‚ÈÔË oC¬öb§@OýWÌ3=N:ìï1½_%¸èÃ>"Ü–+=BñVÎ2bê‘Ïb]¾ÂQ¯mD.á ª>—†äþ@òúp2öOÄc«Ù«‰â(³^žÈI2 k9²i²6+òfåÚ1ŒßSO´¯!ÜúÀ÷÷m¤Ì´;wsØ:€‘ÙçhÄÅ›°í¯ì K{cÇÄ•¡nh)ë;ó¦Ù,#eO¬ÙIÇ÷Û²nûpZwæɆ¾ ‚dƒý _¢ zEñ ÿ3~ߢ[ Ç–±bÍ 88e‹“×G¹Š5RlW—(»–µ.ŽÂŽÙ¨²f?.Ø„%ùSˆs¤HÌìƒå>ŽägÀ7èÜQ'ÿuãÒ‹ÑlÌÖfðÿ¹se†¸Ò#­¸¸z[4ÃÊCèôUþ¸$®–§Þâ¤/+³Áñõ¦~ÂÓ5{È®êȳT‡ÿrä™Ù-óÙ,Õ‹öU< RÙŽ­—žÒ?ç7s¡uÄ`ëN\¸‚&mû˳¿ÝL}7 éªù·4ØüOOÀSDŒ¼žåÚºi\Ìdg|pAu^ÞâPCқɨjÍb¼¨Åχsúޱ˜¥ó-Â3±Éøg¥=Ž”xü噄dž*û¾¹»ÞÏx()š P¿#f®W&­;ÝÉCqY®+.žËªªÃä„‹ ën ë·¦ ì)!Œ•¬‡r)‘_ƒG dIÇÚ.DñMþœ„§âz¤q^ ¾æŽá9¥nº+1$´rV‡°•SfâÅ%71B¾kžfÓ›*åx :…>˜>™%I,&÷Ö8#¯fÛ²•®'“kžÂÃÓŽÌ<²[ÓmX®ù-n‚ãv«q%KÍ@öÎb«‡ý{Ò¹k®²ÚÚ!¾Œ(…¼}9ÌcKœ8 É¦Ì† ‘I¬{ÁohT„£ÛydÈ)Á~0@ ç*™£«B#Ϻí(5¯]Ì~…µ ¢Ï8b£&„óöv@Øù8èÐÚÄ‚Ä&E?¶aÜ¥Y¸ØÒ©k¬a·ÙXÄÒI¤&h%§<7€Ô[²æFäò2\Q|Æ_^4zÛ¢ÈÂãÈÑ=«p×ôFT‰öK%Öc>„4K!¦¦“Þ°?B‹~´aßF=æ)¬H‚DZ ß͘UðÊ8ÍÎO­ÅÝiú­kxÊf6Éo &ßDó«²°Æ+– ]Yˆñ6)Lø¯)ÙâJ,­J@Dw;é·˜Loj¡ÅÑëÑFĘÍ&н@ù@ÓØrÀÝÒiAïàÅäù^ø`™çÇ0žÐ[Ú¶í%Vø@™ê n•X9vÖXSÕ1¼¬!ðr}¿yWPkqê^Éìäÿ‚׺Tz¡ššñß ÒÎhá‘|qprØD§üÙ‡+‡ƒhi˜?™[·f¾Æ…ƒXZKEð†J7:¬žÀ²§ÒcNã=‹ÅÌt[ ôì¥çÿ¼BͼKøGÍ̾ 3Ǹ>¼~£Ž·—ƒø'Øã-ÃöØ ’㇕`B¸ýÎKjMÈìâåä½ }Џlq+WÒ Ã…G¾€½*ßê_¨§à8ûNÔ7~¤„ñ¶X Á´SWø3ÉŠÏùÜۜߜX†5,>) ¿MÅ×ó$È¥I%0šª{ȶ=ªÄ÷‹ ÷Åg:ÿ¦D3ΰbc«@ÊÊŸ¼ÎŒql“#¯~³sÏG­X/:µï¾˜O7LfÇÔVcÁ¶Xú;>¯¯gx4MÓ÷8‹O!Áá'i¾’$œ”d"y%`tV<ç«á “5¨Z#μº‹.ó4ØýÆdÔ~$J¶ý¢¥¥ƒ|·sJ‹’’Ê-Ù¿•óÜèM²eÀ¶þ¬7š_¢rpóôO\¼¹;Ä4fãó¯3ØÁÂ4PÊ™ErNodæß’OOqÿœgâûô+hç«GÎ,š@F¿s¿§ÕÀÇtMHthã6fÜÇþg\}Á n˧§à²ìE°+~\­OÄ¿ÌdÞÁçàB¡©½ßÄGxC¸¸ÁLÚz½˜%주ä p×\ÃÌ^µá¶„Ùl-Ñaq&Ì#!ìíŽ"oB4,êBÎK´`òu ²Øb)¬¾^E¼G1ägÒ?ÎÅ~AZ¹LÞëÆN%êl=¹Ø‚ÜMäúþ´óg%â¶?3­™*Y{gÏ‚á›a4·P”©Ý :ZSÛÃ=¢ít–vþ ô=¯wðNí¾ûšE|/üF³ šè» ƒN ¿À=óœ•áÜ}£U0ON›ýY÷ 3ëÍÁ&r/¿!k'çö¨Ÿææ˜À…ÙRäYc:DÊFb©á{ØëhvKaÖ!>Ë>gC&îPa]¯ÃõPOÈjŒç—ÙâÊ:¸øgs|‡ñ±Z¤ùŒ'ù8/™¶­ñ eoòYÍ,7Üš.Y *¸)ù!½*ÜÄí\Īô¢óòi¬ÛóŽËЦïÇtQã&]RuÙ¦,šO¶¬â’u“ 6MßÔ¹@é5Q´8 ‚=Íüò:Dl™!,`I1yXÛÿÆ}§a׸ÐmtøîÛŸ›²ÍçxáŠbl¡ÍbÔþÓÅeOe¶KÏÀë›e\ÖÑ8®Yô.ªy.7;!êàQêçØ–uÐP÷“–çJCÇ5cTªýŠÿ~ûø<§¦Leæás¡Î˘2ÛEŸËUÑ…9ÂDìïT¦%u“'Ò,ŠGLV±3쬷NÚÇcéKmp—õn빉d•ï2fäÀ$V;“Øš\’²ÅlW7bû{¬JŠÄŸk›ðÓæ ¶{éÆ V€ÓŸôÜãBnWeÒÖàXiA6õrjz­˜U¯LÆþm¥¦5:l0Ò‹ÅERéeÏP¾z ¥Ñ‹Þ—à×þüª&i&À…ÂÏq˜»mˆ«n dïè}œaÀEÊH±¹v/Ñ:q6Ì\íw¼â\ïÌãM½´o½`"Î`?# ãå#!íê7ºÁ¼O²íHrÑþü̊ý%Çø‡‹üQõ_šz·Ò÷OÁP©¯Ü©ßµº çD ô®éÇîÊ'(Sö.ô¼¨älƲ03Iü°Ë¤WN&Ò/¡zÂëo„$q\†ÊTÎݸ÷Ǫ‚Á‡œì©`l–¹B³.Í„…Q[þ”âÌɸÍy³ø­E{_ãËäîb{z=سÙy°kî(÷Ü…´sêmlŽÍàÄÊP5°‡T‡ð®B . U §ê>âc?XêlŽ.²¤¿A¤¾Æ‰œ_‡Íö­œòm²uÝ÷ª;§Á 2}È7ôe}…ñùïJÈ»kOŽù7£Jðì~r/?¥Zvô ¿ßq°4ÊŽ[¤³”Y‚‹¤>O曓©’ã\³/¡É"qè-{€Ú×ÏÒµqû ²Ð“_'9¾²$lÚux;dÙve®(‰žšËœøö låZ+º¸îçxŠshah)§Üw®¦â”Bˆs+çä{ÕÙ¶E™p`[)Nø}š;}å<¼`Á­Ÿ}ÌÞŒ;nìÈyªJÃúPäe­¸Â|Ey‡çb\¸:|<–;.Oç~½Oöí€WÊéœøcü&¼ ~Iùøó´щ\O.kíÃk/qY 긭×+Uyò-Ihñ5Ì <‚M›ýØ…Z왇{þGŸ'ƒuž>è…ù9©fÄ-ØI@¦|,.1#Ò‘Šìñ¸ù(ÿ¨ˆêφ¦²£œ‚—ÃÅ:Ÿ©³³<ýk×4us+õàò²×ðXXÅnÁiáéU¨•ËDÑ0w.´Ïùû©˜ú>ÅûBR»:·êQ>¤î˜N¼Ïê’½íÇñg³=‘+.ŸäV¨tÁa“-öª¾–œh¶F½ó’'øå²4ë \Ž[5Êžû òæàŸ%*Co^E‹_ Ñ¢ºTnÆA‚b%zÌqùm(Û|Ÿ{éÖ‰mº¨rSW$uÂéCoéÆà_À·.‚ÎA`fŒ;Ü&cüÐOøºSŽìX;–LrŽƒ}òÏð™Œ;éË>À]fÁ|•|ûXjøo~-§âRœÇì\|yÞ’¤»VãÖS©¼Gc èÓ$-úkÅK¿·ÂÎm!ÿ[ºNb€\>ôŠ1§u]¸6Õ£¤ÉÊ9à}j<=?AŒ?çpÓý„FÕû8Ÿaa"ñm!$·N]×+ô¥ó]ˆ&NG6¦ ðÄ$~‹Qw”œö„ãCPz!7ç*áæot¨%Ïþ ʳo³±8XˆÜ׿ÈõÙŠ²Ý\3tiœ@ÅRiðŒd¨× WFß§–#h²é*·(ø½6ùn2<€[rlqëÁ7k=‚ÓÜLh¤E,8¹ôà…¤Å´¼0Oæ»ÂS_5Xÿ%ƪ Õ:X$rWêú\S/Œöb9ãxº ðO´ÿŽý±Ük¥CƬ{6;ø^šm°·»6B6fY­£®j•Üe•¼üpI¶RA×jc¶ä7Áñc¯u`ÚzaÖà;H+ÔâÃä§øvç®kÉQû/÷LaÛ}ö‡€nÛ¯Ì÷Ï$çZaÈ®D”õ„§“§ÂVm>®ð9óµÁqù4¼÷²çáâŸýþ F¼Ç«mé 2òÐâúSNÅDŒ­\Œ‡f€IØg4Ÿá}C¸)¹«ñìÚó¼FµÙ˜~ù=³5&dèhîÆ›ßs#v»¹¹›>QýÓ¦õ•…da:yìó ¯îãPdC-Üê{ möeHl$ÙL¹ÏÐòá.§ûÊGlêqbk VÝžÆs‘ÖdWýÏ ÐðA$תhÌ7^ôŒgP¿6ןÃ)ÑN¼²á¨äù‚¬füÓC’ŒÔý„÷mïi¨_ 6ªžc&°e?C!G<kLÒáAìcÈŸ Ö®‡È»¥Ã¨2û<õL$"o7×-šþ.-W Æ›Dá\g;U¬ÙÀ^‹N…„o‹1ßëüWwùr¶•:3™‚]¸ì÷Ú‘o€×|>@Áwˆ,²†ÉÚ¸`(.ä'ÀìC&œñ”nGßX ³®à*ËiÐr+uŸË²¸OÛÈæÃÍT¼õ;幉±»#qt¢þMÐü&Ê®sdçŠÜ¬ø˜(“Ë¢`qËeN;=ȾSX½n‘Kñ‚l k²„[LGuG›wës.ü6ºI¯lR&õ®¸qÏT®§Kœü-¾ŽR#‰×;´MY€Ì{>Fk‡ŠrÌx¶¹g¦ÂþÄžÀF_5†kK@{ò8â{y,yîó_ý(¢™»§“ã»ê˧ QµóWpÏ™ÍHVipg¾½Ãèßç0Wï.νäLuŸŽÁ¢«çQÎÁœñÆDCé„OX­nÃY±£\&‹+áêtu”ÿðëw0Ì_>cöcTÿ~ Û‡]g ö56Žuü%Ø[§WBú¡B,Ôç)Ãjn€ ãɬ`›WþæN ÏÄ9 qõÒ™¤¨¸¯nfo6•p_&Åí转Ž^Éo÷å%‰ûŠ NkÍ}\Ð6öÝ‚›æ™sæ×Žqy=“éùㆹҋ3ð‡tNè ûòpYÐ)®-mm‘¬G›œþÆ,ø|ö&Ðò^*Õ#ÊV|µGß¡×ðz¾,Ä®FÖ¶¾ éÂkA}åJíJ›~ÕÁvÍ ¦þŸ­ÀÑuTù†õ:| ¦¹?€Ë×ø÷ª œìt%Cï¢Ñ$D•†ºÍǃ³è®MÒžõ§xÇqUGe˜éàDHVÑÿç¹8UoòþF/ï¸e*—”üô2¹kmûñ^´-8ž+ çÍeत3ÓË ‡på2öãÕ ÎíV¼:­³š…™î²wøiäRÑå´}V>øôè°ÕÈØõÆÜ ‰7ŒY@‘2Û"_Á{ÑhKB7Ò‹÷¿ApéS<½$kÚç±U>ê¬óêwP‰þë5£@ïÊN’ta«—p¤Î“4x$â;¼eq‚äˆB)n/#j—’aÜ6wˆ˜˜‡î+dhñÄ*X\·…¾i3•uùhçl…"¶á'?èûHkÎîec—ßãäô§ŠÇ;±öê(:>“ù<’cA>šìzGO>guýy-Ù\íÃ#˜’û–Æxúò š¶bòÑ#󦓀ªG`P0–õ¨4àoor«¡ Þ»“â÷ypFù+dæe[«JÂØ)=4>'„.ya„ËÀºéä%è±%„ÓM¹íQëÁvf)ÌŸ¥BV¹•ÙsòtÖF_ªk^}M’¸b06îùÌi[K“%è¾Æ”ŒÈ7C¯Ž øën#³ÎN'þZ HgÖ ûÚÙÔëN›¼x-½ú2—ꥤ‚gélxø± ÄÀð{üìƒÇ•\p΀&37bú.G@|ãVíÕýàf3iV[Š›ÊO¾óÉ8_c*«PÕ!EË!÷ò1xõE‘ùi‰©#v–iÚ¿àžá*÷CäÇâFX8’ÅYŠå`™ÉO´èýŸ¾à4欳ëÝX{,+°þµ-S¸Ë]ØLz8[¦@þ®ØÄx¶w`Ë‚kT_ÑÒþDáã~6]—&1c˜Ë>E¶å}*ŒÖ "¢JìÇû D¡œ^çcº!ƒÙ÷»é´?ƒt­Èt¶ö£ 93q×ü|n>É]öûc7i“¡¢­`§t&–N!Y3Q¸4L¼…‰ý &ÛýW†M›N€!«€IuÓé«[0iÙÒð…{ÊZøöçMQ½­®ÂÛ’¬sY îÅ·{PcÑ Nz £n* œt,w”pç”ܰcn(gÔá€Ï[:¡=»%kÐ*1ÞÙ×@ÉbýR‚°`qŒ +Ç${+Þ‘F,îÜÏ˘{ Ô'ð*>ì…/¿®bÛË:L8¶˜wnfÕZú\Jx>¸ÏŸ…ÞÉ:¬ŠÃšÁ€»¦œ£;+jÊÇZá÷X<Þʽçb¢Ÿ3çÛTȽøJÁç` œ3jÇ•3Dé• Ä•Œj‚í¸D9ªÇcRÙMØú¡”ʈףhÂk\'E6_ƒû—J Kû¾×ƒ~YY¸s§¬Ë¿àÇè ÜÉxióë&sMjv,Ψ÷_¨Ç5v ç}?¿ëÑ™,N·žÝÌ-³T$nKdÈù¶¨•|“…ê1ïôWìT¦ÿ­b¦E Pßu™à*3Ì•P${*MÙéÂ-ÿ°D ]`¨¸ »ݱzê#‚=Ø^£ŒRìÝ…ƒtÑc|{.ÎØêAë·Öúä%÷³itäL€ì̹˜“ÖŹÉë1EÃw(óì!÷£ßŠ­å5aöµÕPóáYt‰†<þ}Ç%ÿÉß&¶ûO³Š{[áŠb#?<»—­~°œ(„çsWußáûXRjü\•YaÌvÜÓ½cù»Ì¯ÑŠù–¬F«†KQAÞYU6¦·Ë$à¿ÿú%ʪA§• :&LZöàZ,€icq‡÷x0˜àEví¾cMzÁ[o3¹;¢IÒ,>£óë åè€q«üØ)‘t‡x°Ò—ášUÃéÚ:>Ýz¨ olRÁï¾/EÝÁ¨Ý9‰l3¸O× œF£¾vz7:JŒg=©,Ç@›ÝuI¦)ù ­¥¥÷²Ðe3AaÜx—h qwZyïå`¶‰5³ ”9?îÀWuú'¨‚Shnà$óoÜy¢rÿ)˜D+@ŒÁ ÿFh„ÜB2yâ ð^ÕK¥·ão¿«¼u1iôO> ‘Y;Æ’'ORù]—ր̩Nû OÑ>gus¥XäörvÿPîÛ.Éêõ›ì[†MÙTû>Ü~-¾nå‡?QéÞn¡ÊžÔ4JfFwðÍ gvg­ØŽätúúùDréQvPH¦í*ä~¼ÏRöÀ¼·¨vÛŽ*¤jÇL݆qáH't/±eÍ‹áºzð'd"Q•–ç5çò†ü©“™àÊxx†Âò5‚Ìß«’K3?ÃUúô`›K«Ä®CH+‡xYÏ÷ƒ\ÿn±r!Û¾ú3,¼Ö†U6Ýhµë ¤ßó"Ò¿eØ×SˆÒwb°F…Ø …€ºæ#<1ªÁ.œ„½f[È\Ï êtKÞ[êM6z\€Sué$£f-¹R on!>óá~ýLò.—Ùëm‹a­‰bL½¯™["¤Ö‹â9M'2wQ2yw֙ݻ5ö+™3MÑW˜=ª­—\•eYã­x‘-ñ¸^÷ n¸!{{~Œôwsõ¿ãÐcĆ]:$w“uS´È¤'†°íEDºÔƒÕ¶xX^–É®àeb‡ÊF¥py¯nô1"rmqÑz9Öªnñ_GÐCñ+¨%ᾈ üº3§àæçZœàQ?ßEŒ5­ìG¹=ªd6I­ëvÂ$¼D—ú´£kÑ7\.,ŒÄ…;_ÙÞJkæ^âúßÇqó£ÀiÐUc½àQõ,ÞÜò(¾ô¨NïËà­YÓãÌMHíN>¸?±Çc[€ãêdÞTÙ ,ýX:=­B!—3€EË„Éç=VÄóeºÖzBêéÝðâ« ޝàŽèªÖOÇù_>€d|ö¼ŠñîJÄb™0͆{#†Î x`ÇÏçÞ½½ŒÕ¥Öxçéo;ç—Šö+tç‚lå+p~™H'¾M„°å1t1‰àâ:û°ê«>, j]Gu¼ÕA7 àsϘ<½ž>²KO âJuS(ý— +êf^Ù>Ò¿±ƒoEãŠÚXY4/6j§iŸÑH@‰ø EáÈ™Kx*€ã¹¿Þ 7çÀT‘[Üä¿q ]Ó€ž3\©ô£ñLÍ«›½;Ê-X‚>ûKài3Ì—Dí _í{Ã.[òZõ5® Ý Bv¸h¨ wßwG¹(?úÐ_–Tü …é‡>€V„(»ÓÈ/Ÿô¿¿w)¡é©ÔÔâ÷sÚ$®pžI}uœëvßG³ eÃ9.ãy%NVXçV. ÿxûØÜ£#p ¡ <6m‡£ë¯b:O”ž;¶Oïy‚^±øMC‹®‰CÞYI2;| 5K§_líPîÐü„g²WoÐe>oŸÒº‰ƒà²}/DïêáÆ¶¯ÇÁƒB¬òïäû>á{üáôˆ4{ÛŠ[ º 7]ª …¨FI4Wæz•“÷jƒ—Ò¿i¤Ðò7‚3C68Š“õÁ{ø•2È™¿Ã¸©#SËéI2ý¬*{åd}ÝŠ±»NqëÖ:¡lª%ÌÕxI×|¸‹Ö¾ ø®!ðWþ %³ÓŠXÓ"V?ÿ"z‡Làì>{Ïz#XW®J”¦d°ÈÛ}lžÞ œm“$ÎïyM«„x9iðÐX zÎÜ‹>ùI9¸Ìá ?‰D]Ù·¸çCv½êº ÔæƒFcV€ÃÕG˜ï7sµÈ5yvѵú%Ypè°’OwË“yySÉìÆ$øj=Úþ´cÇÊå1w¬%+þs þå÷ÂØ¥³aRürâ³>]ú}¶–þåë6E{Ó«pßdˆ ›¸nè/Årte½ù[•8f[_OÿñÈ!§WtaÇc¨ÑÆF7wb¸t"üÔþÌ-àµLSd ži‚[‘iÊ›Gl¥NòV_ñºu<ÏMçø…·>à“W+Ñ™i°(W74Ÿü §‚ˆÅpCGƒ¹_ÛŽU È7N££¿¡sFõµÂð-qgÅ]ØqÜ“,þüg'_¦sß“žÊidGa(ÞÍ=Ì»YLú"¥Ø‰ i²{Ëz®gòdÁ_PáÕorYOL zzî÷"Še°>8ß^£b;7õ¾Ž©½·¼.ÎKöÏCÙ€‰lhÌOþuLi¾‚® ;0½(qó¸ÓîÙ0Ó-œñ¹YFP%”Hý-'ÒƒêÁìý¥c¼_ï°-O”9»zÂÀ]€5ëøBËSgöÅÄ‚Íÿ<Äu_:ÍÍ© ç¦V>ÇØ—9]¨ÍU‘åpÇ×;¡põ/êùI–Ž‘búM¸FZ…K%™[—0:¨ìƒ°$YL̰eäHNH^ÅÎjL@É9Pxûxê§CWD¸·od»Í~ãîÍ7!8úXsç>O9Á}«fe嘤L)îÇ‚ÛFðÊ¢àæSˆæÍ«tFÍVvuo'±. yh\»ÝüŸ)|-JtZ5ɤ³q(˜ø çL &WH‰GSôÜ™ÈÓÌ·n:)zr»%!ÁG„­?á ©àª·§‚ã+q’{a+S{'‰<¸€­ð·á.âØÓsãYÓϱìèÇhîyêDx’ÒGCfʲ]uG î£+Þø¶ù)F·)‘¾¶¨ ñ›C7¬©{ãOìçlÇgÂ; ç+¹vhñ#Û7É™(ègc‡‰yüù\½|*ûà2Êé­k°wÒCa޼-67ÅeAãAoç4¶(K‰MúÔŒ¥‚ øx´ïLÙSz¶ÖàÝ­Jï \Ää¿ù#й uX‰}¾P†SŒcÈ÷cö[Gð_K/?Šh³ëýÓȦ2]¶·{%yþ”Óð=Iïôm‡Oïµp~ú ŠÿŠï5¯ÃD‘ì‘ù‰NM¸ÎùkaÛ°*Ô콌=Cr$ä 4®©„yL NTƒÏ:®¤eÖChóÍ!‚«Iââ'8>FäŒ ëlý/Uø(4`DˆƒÈÏõìZMwºò$ôf–ÀDÝ«œ_Éb\#˜„÷ãdXèço“ó/Æ7ðp{ÜË8U¯:ë1T4¥A¥Á ÎåŒ>\̰P+•¼~meú2šl毭´(ʆwc]ÑäÏ`_òë~õÂêé¬bh?l|q§ÈŸ:ã+Ïo¢vL&ˬŸC÷æ)¼>›rxf"ÇúÄq¥qÌ2L#>õ’¤0TÓ›Öcï=]ø¤ &‰ê¢uÃLø¼×N]æ¤w{ÚúdÀñÀôúœ/b³·Ÿ_¹CƒŠwbÊÌõðB´–&•ü€lÓXºð~ ÌMɳ¿~]Ã4àŽ7ýà¥- {¤È±ÌOâ³”í¬n³µÏðh`#| †$¦Þ‰}ÐR#WkSÇd}&.9Ž$;gÖ†Ôççz˜ßÚ_™a´kýç‡WZ®Ó'*¾œjá!ò3n%zĆ£}º»+Å-ყ_©1Œ7%‡<³xoæ<¢©òÚDI‡æf☲§¬uÚàD‹"ÙÆ (ï6è~K^jHÅ7²¬£BŽôËÖ‘“¡çÉ_6ÅÏÞ*Md/×ÃKž¶B6¯cçc7"ŸÏîÞbËR§±Ž.dÎT7ö A“Í—JÁµïÛè‘^žpOJ¨göI¿áÔ÷ñä«× íi‡k{A:#n»Fw­\Ô‘ö!IVçÁOü ?I„¾K§yí?S¯“Õ*Ma69Ý"éÀ_MÖŸ…§âY¬Ÿ NÛÖŽÛ£qͽÕ÷÷œ¡ØüÄ‚/4a¹,½ˆ6yàEøó{.lÙ‘ÆBµrØN¯>Þ ~}‰¸+(oV·ó“íµØƒ›²$4M´5v‚ïÈjY-I?»f³Ê"ì]&áþn\ÍD' ™?†÷þ«i¦ÃmªéºvœUf¹‰˜¶Ãá=œwüVòhÈÛ±t»÷ :¤‹ÞDÊ|UÙÍG·@sjMwçÖD£ü¯´àõiŒÒ$'âá€y9B‡ µ©ûÁmø¾ËšŽqË7ë±K/·ò¬^ÕÁÄStŽk÷Ŧ óg’ÍÇÁœ±J\ýBGLéTä¯7 «P ìñ¥_µã!p]ÄÇþ„d‹&<6Ѷýã´jFqÝù>.ägÃY+abIü1âíy¤3ÄHíW ø;/’_ž¨NBm ãñV p®ë;QÉÙ†oòìÊÞc´ØL–ž`Gú¾¤Aø»eÿçÁ^’8ȸ!Í~µÖAºþG »Q3QšLFìËQƳބŒ]±æ~cËÚAÈØ¢zØñÖ×ÄrBjpèi+T{?†ýwÕèÞƒ6¸!ÅÞy>¦Ì3ÎÈáâ¤Óð©Æ|Ô+S§ ÝêÌH¡”®6þ§ ›}¢k:!dý)|µ’>o`ßÏ_FK{:³¶ µò`¹óclaçÍf\”,ÁÞ,eÇ뜤}žÑêØß¸õ¬;#ñ =¥î„š9+ßðS^.D•p64©Ĕד\‰˶yˆb›Kp@ç:oºˆ9›]€YUu°Éò)·\fqÕUƒ¼î>Øe× ›þ*‘H[6ë“ NÎõÃ2͈>[‹=|ì¿«F2í@Qù=„8¯žÈ9u²ŒYÈßß ¥² };¡p» )̹rñ'°kv7÷%]ðãyö;û ¦J’½ß@rG!×þŽ“ž¦Ë2”ï`Ù1±QM ×Oþá–©Í%öèâ’yÂÝÇölqá¨+éµ “j²Úù1tJ®+üû†ÿ¤#üôyŒ,ÓbuV¬ž¿>Ë¡åŠ"X±ÛÈþW­/›¬° ’ÛE˜âÒ¹´öÉX8›ÌB^¯Fúô_¨Ž>ì¡n§h¨2“ù€ísT‰M¼ñ5.á<ëíIÞÉÓPj”ÀûQ6z-.q´Sé„#ê[ÊÒÉû/KQ^ø$ª ›’ñOOAfì>íÊ¥>Ù†wÔ!+I™“¼ò ÝÎï&360—¬¿ðtì]¨Ü.ÁÐk—q¢ Äû8R¯xœýˆc-b“‰Ü48dzþ6†ãµãȦˆãø{£ÿùšWÜ )x¶ú*~*“%¿»¾“<ʼnlîû8æ¸Þ‘éÜ“O­ôŸåK7î6žüL~¾F‚æŒcU bٕľʼnPõÓ¨9f TÆq»^*á<…-pÛ?„̽lÎ|Dqbq4[¯‡N êÁq¡šäÇhvzHŽ,Øû~çv\K„¨–˜öõõLÁù– ¸'ýPsX‚w£ûU2>NŸô©Ö©@Àü¦þ¹N¬y;s¯áõê}¸Vü|Ü´×z㛉í0}Š%yPd‚rM³P,©”Û¹Ë#’ I‹ÞLæÿ4Ó†_b Ô%ü>”C•&§bì»$ÚnûŽö$Ïb‹Ý_½uƒó„Èfݽ´eáôè9 œ …wå¥ÙÇÅ¿±®C™ÆsPÕÿK[Ïßðu žî܈»õ8¿Úß8%ÏT&ðU2½‰îZ ´¾öVwpëܤïÍŽ1÷Û¸,` ‰îO;¿žÃvû—Эt”öɰ‚EòK…»¿ÏÞ­Q-ßèC–JÎ$èàoèÏ•Z[vàÌøæý’^piFç¹lFÌV*ª}†FJqIí?ºëaî|K¾¨âµŸ†l‘™‹.[ÌfDg“†¢ÐX¿VmÕ&g—ާ%/ ˜¾]wBídL.Àd_#ì¿y—¾Xy†o«´nÜs„Âj6z]~õ¼ƒÌ/.ø^ô1^ë}NýÍâf¶b}o9l=CÞ6‚èÉ…°äI¨¿5B«é˜ë-HDn‚”ƯxÒçã³D<¡»µb_ó´b½xö0†ñ¼Éå‡-˜Ü ]w®RŸø2žöÄRp2l¹ë‰ r!«øý¥hŠü‚íDJ)¸‘Np}ëþÆCøÜ¾k”oÀhNz=X‰9ÊçÒçÊ˘©‚€¨1,rB×:ý]g5 öØåV&!ÙˆâÆïPÂùÚòÈÍÙ²¨ß{œ_*%KUöÄà=+ Ðõy‚mcÅÙ¹ŽçÈÿxNFÑ‚ùñì„Ôz<¬+ƒÌ ¦p4'}/‡Šuý¸p³!.VFèº?3Íg°ô_3¸?FVÄ^‚Zè¸ñÒkc¬¹vÍýè5#î7ÊáÉò$ç—¶í|‚W"˜Ê‰™˜_’"wãEÓ\ ©0±µfsùélt?q<ç Ÿ7ŽeeÇpúirjЕ½uÎä*¢3д¾Žû¤u zª–2íï·aÑæ,4z|ËXt˜]ý§¤¹[ëS€ëSf.B¹Ål¥™`¿/’ì<„Éjj?(È«tÏg;ŸÐ^Ãd\¸¤•Ûà] rãž7½§¯›r«¾:„wqœE6µ®!³úÉF^Z/¹‹9ƒ“'‘ŸG¾cf™|R#9ñ¢à]õm“þÙöPÖÒ¨N†^éCÑ›ëønN«p‡°)¯K*ýBÓ`ÚÓ¨è|Á­è]@DWs:#àøIUµ´ ¤lw{ì‹> 1SH÷FhøAwèeëîÙ÷ÒhÑY†VWz¹õæXQ|™êÏ?ŽŸR“†m ¼Öu°¹¨^©mf9ÐÌõCëáÍ[Uv7m&XÉ$—­"g¾‘ pÆül\“d§jylœäê.O±P5­~­$NÝàÓ´‰l±P`ƒh¤8tWµö5cRg{ Y{*Š{Î`ÊQ5pØ&-OÅÐ í6šõp*KÛrtBo€òËã¸H«#M“Ë`YQ \3@åÛÝ8áûfà?Ð$ïd“m3§~ƒÐJ_H5Te*Ï”q…Ê2pÈDß0gòi¿Ì)h‚]‹Ø)ÏF4»¹¸„‡gccŒ%ÜzvîÝQ`ª¿:±‰–À¢ïãØªß¡lí£ë°µõ9,xH4Æ!¶¥5ÖîïëÕÔïïVT¢ç“AäÉRPYÊåí[î'Û W儉äŤl 7&gk®PKÏ5òí˜î;€Æº dŸ“Kõ"SÓ{Ápuˆ4‡óNÎϵße=Êo®hÃòôæ%|<û:øÍ3…Ÿ}óÈìò\@p<îWÜŽccMYUsù´è$¶4eAÀÀ=°¼ÅëBœ`j&…zlL1$I®Ý¸Ì䯶V‚7ímg¸ð+æzô+°câcìÔŽä4ÿ –6ôí;aµì3¨\ªåBÓ7àÀ¶T”Œ¦hÖn þãDÀÕK¼ëf÷îÖà)»·4r® )Yòjõw²o8æhíEnï ÆÍäq¶¬29Ÿç‡®kâñòˆ[¯ óUDHä£RèIïååOŠÅ¥·áƒ„&1ÔçKÈæ÷`ÃÕr PúkË©Øz#I Õ¿H“‘ãGpf\>]˜žŽ›¼mȉËù˜ì¢ÀEl»Ê.­ƒ×•ÿy‰ù“—?G%Þ!ø­¤Ì‹$Ø1Ç¿Ø÷5 ¿]b»Ç}Ä&½GpÝç Ç“˜NÜî‹àõLmçD°P:Jk¥nclz ,îûÎÑ¢¸fw-[¶ LN¨Ð BX¾®89 oµ /ËgÀ~Ì8¹#eŒwc¸yÌm^Bžï—ÀAéÝ(ïNÈ}Ueöqè8©XbF„4ŽÐ‘újôé?‚çÂÏ€ø A²¢´ ¨c&7š²äÝY‹¡„›AÈY{"5m#ùQ%Æ]1×!kæìÁɧv“ Á ÌùM:ÉŽcbK±¤NÖEèƒVý/üéÛŽ-7wÑ©77ã¿MÏéÇßMð<ì2Õ­ÍÒüßãëW?ÁG[ †§er#Z«ALGnõ®¢j73ñýþãXÈÎÙ÷Wj¢ÕÌø¼æí»u´Ž6€ê‹<¬f c§•S…ìbœòD&ŽM‡—1ãÉ&õK|ÖqÐjÐ~ŸÀÜ]ŽkÃ#!o¢N¿À@Ì$ |f¥áÁÈ(®PÕžÓËý›­H[¾)0ËtQ¶n2³Ž¯F£`;zó·i|Ø2í|½sö~fÊÞæxî©(™¸¦‰¦Är¾Ýœxž6{!W3_K3ý)hõ¡†l?‹7T™‰ü#Npƒ2 9÷¬j€Ü­ °)^šÛ/éÓgr¯ª±O'…ž|<€Ù+ïò÷,N€‚Jœ~·‚V‡µÒù§ðÇ’+x)f.T}„'µX î|€&›lù‚Ÿ9fPü¹¾YjÞ Á1ËVÒ}—y…ièq_И'Ï2ê·‚¾b Wø| ]â Sžb\ßqªJoA|°˜s=÷Ïû%“’³Ä71¯Šº³ IB,\í ¹j(ýä7¿©þâšå ;ÁͲՉTBí_UŒë¥q©•KYΆH¸ÇÄyþ¯6'‘üP›ÕÄM±½ŒÉ}—hó÷hÜß[^Û2»¼{võÈ sä[Ö"jwh´¨.¶Âs±’¨%w‹s‰^>u!TDu!¤X¯Ç{+Öc¡éDâá‘Á-›ÐO¯¬· ´ugçãÞ‚®Íkü“]Âm¯¬…PmIÒb;@ÿ«YÅv^ý^Ç·4Ã8S[†hË@?´ô[ØíÌ\O¼Ž”Òˆæ6Œ-ƒ‰8îOhµ:ñßu`Š‚^=³8ŒeåÞ¤âÚØe_ǹ˜"1ò®Ýˆ}ÜhËz"KAç}çè»Î¬*ÁÑç¸Cz‘x³VñjÜJ;²xÒN N¤¿¸¤×û¸=Ó娓µŠ<™*ÚÕôÿ 'nf3Øckrr¦>Þr—37.GKV¬À>eÖ`vR0O×eYiG€ÿ¸ ]ÂËø·Ÿ® 7-DZû‹Œâý¨>lyÅÙm á…F$¿ß‰õ_?EMûêSîžå¬Ö¯ÂbŸ¹HSc0Ÿ$A¶TŸ‚c.ătÈãñãHz”!è´ ì·@nãAòþi/Mx2o_ØÇ—¦Œ»íÔÏ…y_…{^K1µ'ž;ÞÂã–…œ§›RÀêÓINØü·èò¨ÆHyo²…YmŽ*Ž7>’2ï¸æÔFÎfò+—‡Ò$3YNn0 ž\$ã½ø;\ï£ö˜ƒ$ÔáTæá¢‘æQŽŠƒ0 #aî803 µÃAÎ#­g’]Všøiø*G—5r¾Od+ÿü‚÷f?©ËÒ\üõî_©PË{·p‹J}È~­µœª™7Y´‰‡'3PjŸ {Zu–-Iß6Q,ù3 ‚Ö®§«Ç¢›sÌ«2ð–£dØï,Ì,øŒ*sap•ìh¯yv&"ìËý#hü§‘úÿ ƒ°ökP~íš?Bhß‹9l®˜ äÞ\QÏ„ÉTƒξ§¼=bè±-h§ŽÉ”ïÏšû(½|cÕfòøâ[6&Õ$q…NÇèµ iô[¤(––~ã ÒƒeûJ®Éù,Äfþ{3'öÂ\Ì‹xÍÝJ ²—ºq·E”âüÞm`{dµâAùãž¼™ç+ÐW-K—ýüùÓŠÜv#RždÄòç,4ສ ÁÑr:¾ón,u`œ^šì#Õeµ\ÑHªxa~ç&i¢Íu÷СÎýVxÃýÍcç,åmüŠŽFµð_äß3GËĽ°¬i/uºCýǽ<w LäöÄâÊ v!Ú€íÿÜ¿ú’Ú ‡ê¢6an/Õ×ÌŠ¡Â œ‘_0OõäU<Ô·—Uºž]ÏI¬aÞIªy(âèø ßðû¿i´¦z:™“q¢_/&óÕC!üP.òLÅE½KhôŽ\¡^‹aÛ&±„RkUVDoÜz oýÅIFÍVºL%¤œئuVÔŠ/˜²cû–¢L™1Gª+ðÜr¶&V ×HÛ°OŠ«1Øðr½Ôù‰¸G|´‰L!¤œOî¦aÇóC°n(• ÔBûåòðÂæÝ=gÎuà <÷qQqõwÔ¸%¹p'Ø9ÖùG˜»¢þóÿúRƒO/Kúz,£™pum.þÐÁå1*ûw.›ÝBuü•Ø×¥m˜wÀÿõÒ8Ÿ¹ìô¼-ÜÏŽzªg/z;8–”F+Å%X»•>Üv­§Ûgy±—ïài‰™Y´“•¬mƳÑœªPhDÉ‘¼;¹ñ2Š8é^'&}¸šê×0Åd¼K†õ ñØ!ô3|Joë-‡¥B ð{ÌT8ɵӪªÌõÄ\¾›º2d­×†+·¸{ŽÃÄ-Þè¢èÂ}~œý[ÜCec²œÎÎJ¢6ƒ¥Aºh=8w½ÿPYm&Þ==ÒÜPxŸ8Žùs vÍ}B¯z¯Œg¶gÃú×…à{«—›°ºÌ·h2-O9¦}ó6jøßƒ¡kÒü‡Ó§7¥Oxßù õ–=ZkÇq¾ãìYkÒDVØj76,ÇÔ?ÓI›þ),Ê<2®RÄáÜòOµ‚Šwwƒá»<,6â½›aiÞZþ†Ǹ«ëРJ–HøÍ/u§ È÷ÃëàšÞF”såã>÷Óô™Ì·ÓB•H–<ƒª)ùü„ã!4È?¶WrtNÒ’ Ù¼ì²stŽ™:_›KÍ´t™²Ù4Pö.÷¬Ô© £,ܰäX$|ó¨æùÁs”;ü†wãNËqÂ:ó_ÀªÂþŸ¡ä~"Ç?4 é‹DW­Æ.슧O),W"ë'lÁ/¸=Œ·]74mæ>b¸~úfó‘ȇï+j¡åëî«ÎQø=×”ô¯ó¥ü"XÆÆPÜW íɯñêåëÜmOø;ë/ãÆNÈÙècgJò–§àÁ[û¨r¹6Šož€› u8yr`J}3S”-™JìåÊÁÒ4%âItñ{Rn4Q?G{< ežõÈÆr³‡á>“JÌ3è‚C»$ÙŸVK¦2ìŸ{”É~vg 1[{5è8’ Çl Íe ù“¦Ä|nñßOp‚CÆä¤/Ün=ßj‚¿þê1ªU52 ÄkQœˆýg®ÂíDAr1$$EpNÎMÔ«%{$TXð\¬u¾MkÏá}‹¿4fõMC×ûí¯í‘%‰æÌ{¢éȧÐú%Gùw– ß×´˜ˆ²&æzŒï6$ÐáÃý°~Ø5“‚y™*@æy ö;ÏOb÷4M¦wo=Xjóæ|Å)þïpFíQ®êÕjnu{–ôÈÏÉŠåœJb¥epÿˆ:»»u¯bã¥åAd㉾¶ÿ{ö2ÿ}.°n”uü¼mÿòò&8ôbmžûò:m¢¡vùrÚÛ~Æ(±s¶WÀ©óî¶L¦¯æü¢¢îeèÌË&yâþâøÏãjîZAÞw™Ðø-¢öR|îÃùlØ ÏÆ¥w×M*kÇÀà3øýž Éø»…3ÛÒ×6ƒ y“©ˆw gÎÍùÏ›™³ÃÁébYʼظì]ºÁeê2ÿð ýóçøì\(Ž½Û zV¸×è9`êln2 Á§›LÀêe*Ê ˆ'BÒáÍê¼(% ³†ÖsBiÑpH=™Ë´ÀÔcθFîØMMƒ¾ÚîÚÉ&8›¿ʤpy#hõáhÜù~.| ‰+ß ÓôÕƒxQ̉ê:fðôFnpê¾'ižu4\¼¹ˆ)¬ôÄŒœX{P˜uɼäÄ3þq/šC8Å™F ¶n21ÑD³6ŠÕu a±€›ß]g álO9Tÿë¦}zͨö*ÚžÇÒre¶çÆQ´üÍL&!}¹Ñ󶟶¹Êþü²`ôp1[˜€õݶ¬©‚â?"FÖ]݃BŠõŸ]>ò-Æ_ÂA.¸Éö©¿q‘e†¬$&g`¨o÷.5’uÄ ‘»3„!»± t¢.Òœt¨ŸŽò^KIçî0¨}éBg1ü~\øu ¥}F±lQöuM;·eä7"ëÁL"Û¸§ÔµU„UîI cØMb(oº.æO$^$Zí|òÃs9·alÈ%}¯XUUŽïúù¿ÕÝg ¿v€íÙ/Q$¬ŸdÿE×–çèæ—“=:qÃÕ•yøHY ¯ÐŒ÷ñ¥òbî’y½ú:7‰âç×Ê$ÇS·Œ×æ9é´Qý±Sؼ ‡ð¬àÊú ¢wçÀ'—P83m ¨U g¼ㆇVDuv.×;U¥¦Î…—Ÿciý̵8¡å6míâ—ýs§»(ÒÂIÜóA³~Åö*Ü’µŒ¹,܃ºUP Å#‹Æ|E»àiö}<ž ÔcK7~çÏ>? ÛŠã ’žD¡4}N;LŒ¾/“"/{k@ ¾]¨›½t¢·5ú ÃÐVY¬p0âvžÇZ\Ï‹~ïãö¥}ÇÚgÝðyÜ:4W Ç<Ú‚©%U¼u?^ ÅÂSxéŸÓñ_Àö)2Mƒ­dt;Xz&Ö ^¦lh7§vµŸ›1.&Lo›ŒX°ö•d“”ãê˜/\âbGròÀ ¿òíQXóN‡”=2fû.o…M_9ÍçãÈ•<é`†inÔµ£¼irÊ9ûÂQ­]‰ “¿ƒwÂɘÃìZ‡HþR£)cîÃ`¶8¾Œ™ £øe3ñ#·BíèX¡ì6¦>¹™Æ+²gñíÛ‹PTsˆõf½Ä*ÁÏxû6ëóûÈI~j€ô­f¸ÿP]tûOÈ “sÉûü›²f9±ê]Ô[XHu:a´qÊìAõ6²÷áb´^^…ÿÍ‘ždu#ê÷aÞ9OìšÌú›;o×19 JÒæ:Îã‹å›¸•ÖQ´{ÕlvRÈ¿:¶~ÇGÉ-QtŽ"aå©|×ÔµpÂe-à<ÞrºBß.È+÷l» ™Ò¤ãÒ]ä{¤©Y`%'Ì­^i×M72ëšEĹa˜êŸ´#ýËD™BO8–¿!ùkzë?ÝI%VÎ Ñ-@jÇp y´"iBíZCþ?'92™Œ#µò7ùÇ„„ˆ¨;ÆÜðD7}3Ü5b ¦ýªd°| Éu! <_è¤zê „ ƒŸ)+½ïÑì™[àü -¸¸Ý5bõñɺC¼ªâD¬øÎçêHϪeðõÙsp åkó‚XðÌd^Ò!"©• e2ò¸ôh g¸Zl|6‰Œÿ øÁ4ñ“ 9ò# U¶§À‰À¼*¥[¨tgÌUÿZ~?ðCJlÙ ÉòW<×Õrgg4 nÏ?¼xÚ-]â@ÍÇ“-ㆎç"0Mp>¸½¾ŒU ôÚœ6ŽÍm}•_ ŒYõý'°ç°qŠ!sNí‡rG[ØØë Ï7Æ“!‘ûøÞVŒÍÎ=Åxù/*Œ%Ií™édWÕ6ý‘[ºû!ü¶Oåó®ù‚ãÕí¤LÍïº>…‘U¿Àb†+>Ub±&¥à|®X+SÕ’yÐ|ç’ýÁåMÔl– ùùKÆ2g‰õ0ÉFY&m…5fwILm$•C "gdÈKª–c_t[ÐgÏYîÉôwøß¼é›^ô¦×Bt>ö›ç‘Ô†üÙIŽ~\ð’þh²MaªXHCXLÚ TÈ8ƒŠ‘ëXÙÌ4ºv¥þ,ŽájVZ³vŠÌti2·Úã7¡c~!ʤÿÉ ô*;Š‘™ "&ÃNÿÒ!r~ø÷`7ç41z?Žêü6ÞÂÂM–TÞEÒþ}„-[ÜÿšÛ$«oGR uf Ñ ˜—õ4:íÄ_¬.îRwá.Ãg6O9²mìÜ‘68thuRdÏ?gߟp9á(tGQ%c¶å¼%Qíc/×Cð#ök  œJΠÅ^û9.L½w‘=å-ÜçßOÆÿf›«•È.í˜8œ æjžì‚€(ˆ\ŸÃöpVdÖyHS:ÏœN€>*̾ÜÜJýêåZ)b9õ>—A•H–j²î£ëýóØo»F¼(¢Ál¢|PâïÜûi3i¿uùG0%§<¦™‰;<#seÉ¡‚ ܼ{-Û1+$Æ ±‚º£Ô¡t ?‚M<âÈn×e_„±3Ûg‚ÄÓVàeÊ!÷h˜øã47œ$æ/k#U³ô"mBÞw<5“`ÆaVs¸ä’·è¶^dy±hYhÏ6ºnc)uödÁIyXÜG]f•ÀÒpqø«IÂ̾N?ÜA¯/} çKw\ g~ÇðöÇZ3—e´M¾¼;+~“çÖÁUöäÄ—% úºðsE¨ý݃ŠzRÄŽý‡ÉÃðtS1Zw]ç†ÿ„aÜügxÅBŽHFfaú1MúÑì.ä¶ ‘GQ¶nåyê=U‚ ýáÈ´žÍaøïƒ ûéV Ÿž¨3Ïi'0ÛÜ‘œéÒbÙ—¬˜s§ Q}˜Åoë`ýæQ\ÒåÚìþÂ7X“/ccΣ´Rþ~½Ÿì»'ÁÓYÄ÷ãt°n)­É ž{¶b¯%Âû¡0ïÂLöQ]^ÝР몃§’Dñ• 1NnÀÞ#Ðþë+X/ ºÉà÷F•ïÝ*A6ýâi˜IÿŸGúùûÏÏ<Ø9„Å"›˜õ¡|œ±-6[¾‚sF*äßñn»t9޹®_ U7»1dÖB˜q[m·ü³?~žÊÿo›ø — »šL§8N¼óß:Bßû²Ë_À¾?õè¹K‚-Þ|Û#؟٢HÆúâ3!SÔí±¦S” »9±ÃLoz& _b±'aG¼ìku‡•qü8q="VÍvÖnænTÞ¤Š¯Éô*äaÃ_îh¬0éëÛOìŽÑå.-hêeÄ.æ\ã6æ<ÄcÑþurôÍ…Wt¾çqîÀÃoÓÎàή…©Û†px³³y-ΤŠqÊõw(¿m,OŸDþüz…ÇÅÃÇeIìÛðZ˜¼™cÑ>G®úOú…þñSYáþ²›ÿ%v_‚þ«:d…ëT´ Šzø/Ú×CEìmÞÙ/ñE~xOfSL4©ÊÆË(,<†¬˜¼®E¿âžý¾¡uð|ÿz&òÃ’48>Áº‰\Εg´mjð „n­ÁŒ²¬œ‡Fr³XÁ}X~.¼Ä×sRBw¡ï|&+ŽÄî‚J»½îVD¾|<;öìzNôa×®›â¶7v¨¾Àõ¿ó¿M_«r¾c¡!hžs"£â”•îÀ¯ÿ(þÝDËLN’¹“Ä0ûjÔ;¨±›ï§±?og“ù$…l/²%Kv®b—¾]FÛ–T7p-;PPKäÙ{Í&|±[žðë#-ÏÞã¡xØXN<šB,¶ã§7/8«±‹ØÄg] sÏ»­{•º\ÓgŸ–/B_Ù6ò_¾ºÒ×8¶å[Cï’,²÷sq¼œÅ“·¶ç• e3ØÝú½ÿk~S=ókñèÌh Ž BÚìçTAÖ¼ù8sÒ'ŽßîóÃL“ò?S¡÷i7ß5ÖµFðÝq bOEÕ4É·¿ ,ñ`<4á-†L@®LT?£{¾/ü½{…>º”ûö$³Ynä¸Þq¯ù¬~‹Û;¼£™k:í@^:ú’ %Ù©ÐPº¢øØ—r‡ƒvÃú¬xöWEu”÷Ä"–aLn#z<:þÅU¬ÃÙZ}âö©¤¤¯¥;æ1OivûÏ6X7KSâJ©ÍΗà íƦäV3§ð}öˈ„ûÏ"áó“È·ì½lÅÚ@NP8n¯Ã…è@ð5xÌ8‘ãÚ”_}Îs"Ñá˜6Ž.¡áÐ8|‘—иœ]Ý×&c‹|hagšöÂ|ÿd¼;û7áNzYC'Uöåt]ô"_.ñy×ÇE`­×Mȉ8ßÀy«ÙL4ò4V/Ñag8´˜´™/lAë jÑÝj­»ŠÊoŸAÍác¼›#ëð’„.û˜oÆœeOÁ¢l…úá«Ùp„ÑÜRç¾o¶uµ8î`1Ê•`~áÈiÃ8(û8uGÁ:ïeÌ:êç2ìÊ%YìåUœ·ÛÒÔðçMo‚’ë*£<ðŠm«„KOãH²ât<›Ç}+ßÁ"]î€ßÎQmßaE®ø~ÁKαâá0T÷Zæ]‚j'e´ŽFHz[€"Ãö¬×z.y¶ÒZ*û©åGÈW#úE.,?×E7I’ûËiÔKÒù«ö<™I:Lj¡ƒ%•»…Þî› Iù‘Ÿ´æg;ݼò .ñ¸ÅuTœÆ´ga$ù`s?Ý„Wº¯ÑWÖଷ¢dý6hr0f©.“ÉîÒ0øöú!î×T#zâžìö–3Vzöl`íä¢ònœ¸@ ÆÕ–²…ËW’Ä8)G€4l×# íTR¾“«›o»u,ñSÕ~h0~å—¿¼@!îXä UÏ Èâ8÷`ñ <ðËÛz£Qžôùή‘ô¥˜LîCÚ[=xù,†Í܃wû ~Ð s?qüyëç"œnǬ¢ Ø3nÿƒoÇ?åm)qÃh‰¹¼Û~J´«°€>ÈS"¥þspbh:å_ ç u.Ý^Ì™:Ç÷§ŸÚÉøÐ¬ÀK”S¼ ŸVo€‹Ï€^¾ '<î.8Ÿ­}ñBÁÇ¢Ñqò¼^7‹\½ÕMv?BYÊ{é"†qʬ'á‰ý©ÌK¸Ù/ÓñY î3þP³¾ç<ÒB§³q£JOå­>}V;Ä+84HÙa-V¥?\ßQHåŽæûð &ê Ͱϯ‚÷¯k›9Tˆóço¥êË/p5 ¹36´ÕʼnâÛFê¶yˆ|ûôÓaâc¶åÎtxQ»ÆìÑf‚G¿b«»6h·h‡vëxzÌøJ´€ÍaI’3¹ŽšÝæN°À£‚SY{©¹Lù1Šx kÜÿaÌvG†CýÅî²ðDx¤§©’ºcQiOw"JKé§ËßÐ&üœ‘:JŠÜQæmû\Žý]Ëž{Ø“ÊëɪÓ%˜¿Æb'ß‚Kc¡{ÏLµT"‡§Ç‹ ¶»Ž§ ýá†?ñ†VäpÐtr°RŒ+K{E妴ÂwÅ™ìíÅ/,¦6èžæÑ×⑯ۉýãµlã )â­-Kæi± ýô¤2E™Û1bŸ¶Ûæ™±HÝ鄿Ɇxù–2ýwç¡þP%'¿þ=Eª±ÑºCCçóqÖ<” m#?ðøþ½Ð²@‹ü] Φo@íœÉYþE2PÅò1*û€g Y^×îíÎ&œUû'^Î8×>MÆ?³6Â’Þ\¢jÂ~M!Ý·FqýŠÚ>™±‘lœú8ŒlÿId~4ãäO‡ñœí&Ž'ás}–ŠCP÷@}c@ÌT ¨P/„š–øÅe {WeŠab¥º¹ýšÉüqδ£&’%÷¾‡zŸSÌâS>·AóuÈŸ‹ö`½ƒû{C‹wÇsÛ§±)B_èT°qس¬ ܇Zi²ðÙc\°wQëkÄÊôñÌ0D‚|ù“ ¥'§qµÚd´6WÎÒv”±Gg–{½> ®ãϨ»X b´áö]2‡í>» R´¦7­J5 Ù––ß°L”‰?O_.$Ž–äM†,ùuÿQžƒ%†Æ3ÒiÄžMµŸçÇP¡§‹½zArç æ ÄøÊQæl¸ý ÷­O†xf†2°ôÌ>Î¾Ø R,Ècž .¼W@Ÿè›²…÷téŽËÓP-R›ÌújB¶Î µ[¸y*&ùÇóŽˆtckà6;Œ÷}¤’à(ü¼ „ZK&›Ös(žiC„ãîqU.oèôcïé$muÒñ£SCÙqSy¨TÀá•ðÑs2s¯»²ý+gÙ2fâ%NZ¬ç“Äé·±Îî,^vÎê£@†û`äQ0<ÝÃIû<,´%WãÂ@C{PÔ|÷ É0Z<ޤM [MIÒ«\ÿ׌Üê€&ÛµHŽX=ØwÏ„„Û.hñTªîréæW8#úÖØ kÞl#Ç®…ÂA8Lš?E‚•pà>f/ÄeöñÍž(z~‡)¢ò 6"†ß›}ñ‘³KzUE_-}ý i€Ib]h8Ã’LV"ËÚ.ÃŒê¯h©ü-í…MÉìÓoQ6×n–ÉVˆ^»ŒSÆ3æâ †;>Ay|ªÐ…®GQp;U²4ˆ÷w#4rŠƒ^\Â:!â¶‹°úekHÃw!Òá»øëöS—D7Ro8‚ç¹Iû5ášÑVzäÜX˜®ÆÂúõIèøÍ˜¤²Ÿà¬³.ÌièÝŸÊˆÔ¢ÎØzjA]õîÌE6w­|NÔ&k¤ôñp¼2ì©I¥ïË>ãŠú?ðgÖ|ò÷!JtõОIB¤»i"ÝÕ‹}¯]„.‡êÏõ¥ÓûmT¤°v^AÒ>œÅ{wÍAÝà;ÿ_¸#nHæõÈZãçk—0\¨ ½I†ÊTP0®Ä¸»8U+w¨H‰éœ)Å9nGIFÜIê(1mžæÂŒöœY9/ì§I3¡´M„Ûƒ‡,øÇævSÈNÂBËS\Õ×úÉŠÔžÛ—”Õíž*J¤KàNÈd\u~5Ñ=‚ô]ÙÍUá\–Œ3”;Ë1“#ÔåQ27ØæHÚn|†ðy60ÀL—OƒJï½xnÕ;<Þ`ÆÞF,á/lB« 6v‰½ KB@fú9?ùÜ›w7uz°]÷½¹R‰nú}ÜeP\ŽSÛÁh$•_ŒGÙûU½4i§9¨3‘nê¡lÚu.Ïæjf¶À]…@è’vàçŒÑ!ßó¸þp#\^1‚>ª;ÑèãFøºê0å@¼‚ùN¡á<ÿËÁôÁþ‹ø|-±‡^*ÚÄ:æ0èõ=ž™•ðÔL$/dÃB¥z¦{/ª—÷áágEdwoJ§åGT ™4ô$Í^Ê+2¹çÍãÐø B¿Xg8[†­@_Œ™Ë–[èÀ)•Óœ”ùLš”×ë?‚ñªàu7–¨ )bú¿ƒx(Ç,S—œÀ`)÷ŸïzøØQú9H˜­}è„>%³É›§¼3ÅÖãL…4®ýI&d›3éŸ8‰ôpìï¨^÷á-{ÕÄGïªÿó ]à‘Âý¸¯³¶ŒaI—í±päºôè²{­®XlüÃÈJ›M`§Òέ ,„ÁGÂŒ‰]á°á&?iù&P%¿ç \€>ûiI*̶ô}*!ËÌdÙ›CATJ)ÿ¥H²ÄpªÃœ¼ ‘]ýdœq„ ïõa~ 'ñ¶œ2àcA"ª/=¡4p‘ Ê …¼E·àŒæUr·–ðÖ]ËÂ꣣ÅZ"“ËA ÁX,Ҁ˺²ðšÀ4<$â‚ݯ¨„öy,x#O »é:=i"À]Åæê ˜ùý«¬4Ç‘‰pxú3#mMLËÈsŸ%¤ªøóO]ãNëk’3œÎëC6Ejºéj^ÿRû挱=q8¦ay¶;F1Ñ¡—»P>‰“ªZ€§þ!5Ó«‘¶_ÁFõnîë,H¹'[iþÚ3XµÒÎ#{à©ÇRb¦æ_„Xá[b;Ü F‚‚ìæ7J\ÿE5&Ï‚«{'‘)Q†puÿ^úèË0¬þŠ;´—à­7‡a·€î1[Ë„¾n†Yãg3g¥(8æpËÛ{*»Šy'vìƒÛOxôí÷…$(}2¸Ü#t!Çø+ƒ`a}XFᆙ\ßúQ÷ûϯ½g×K<ýÛÕŠ§³¢µq´_aÎ4O†ò«ó8ñ‘zh}LOYaZi=·ü‚*ê>å?7#ë¼~€Ë;¶f¾>÷q÷Ôz¢ ë<Ô˜@:zx`Œ×Xòtv&·hÒ-êö±—{”CÓ,«i«ç& \ª²:ã ìÉ{Ž«ÏÐØ¸ø±²…+ù ‹R—ÂÂ+Â(pFJž!ò0 ÎÜD唓põÐeî¹ÍU.˜‹Á˜bR¢5û€>|'Ç^~ î¶tu†+.F(vñ Y\Ÿ‡Æ8Óq7Ø~ÏìJæ`¦âJ,®ži–¦TzÁKÖThºMÿŒýy®ð²:ýöû!Ù¯ÁÎß‚õúèXñ†ô5r3BÔÐsr3L>žè¨ïÛ1°_k- ’¶‚Ê£ê| ÷U¸÷Éìîï‚Éû0ä°¬’£«Þ«AÓ£0ûO÷u\>KŽàÚ sÏ—S×ò¦œ×Wøpz~šôÖø ®jm)µ4Pg]ó”`LGܵRÃE‰ØáŽ Óøe7Ðî•éP.¡k¯Óô3$¿è!º˜Í&ÜN ²þ0wî²OÕa/óÃ1Ìjš¼ða}ŸýH[X.}ç©NŒ‚ùÐøTŸ&?¸)FæìÓ‚:ÎÛ|1‰»B}+`ŠÏ|Ĉ4̓s²4ððAS⡉É.¶©d~½îΙH¶Gáä£M0¸õ:O=ާ—­E«Þ]vâWî‚¢\ؽZþi‘ýoú9¯5Ú²á;·øR*¤ôßÁSG¦â>›pγúíPí7ÿ¨¡+îü:“´_›Úàþt4¬ü½(†ÇÖ,ÜßO|n& slˆÌ»=89ýœ7¡|³kØaËÞ™Ú¡Þ<1 -o$ÈÍ×2ì¢ÿ\bùô=̱>íbŠÿÍŽ&Ç*® J¤~ËÙÊ~ß­BÝ»+áâý=8sAÿQáF„ñ{ᵄœûlj>‘!wêžsGç-…ËÞºà³[/¿ûFoJ/‚rÓ¨\µ S«®Þ¾K?=˜†]y‹hs‚Ó~.E_ ®àÆmsǯ±ûhæ·¥øq”Ïì_Fö7Dÿ—p0kó†ÿù¯G±´ŽÎ뤫 aný^É‚õÄóßIÌ­?²ôú]o¶–ß4óíeH0L‡ýýŠlcz!XË$â7Ôÿ«¡AEÜNýÅðhÚ4:ýÅÈ0˜Æ¦»Í÷Хŕ;Y̓Hº#LD¥ÀþåpA´á‰…Q´ê¦ü|°ÔÇ“›û'¼O/lY ÇGŸÕoYF”]ÖEÕúŸøIm ÜFî±g6»yó7^—Þ%ÎãÙ¯Ú²ú‰=òœ”m ~J®ækÞsD?ý$za\$8=–ÅûÍm} ÈR®Ø±m¡ŠðÃã8+Bîƒó¨TßÏ5o¾e?O5 êésêÛMÑÃ<Šg ›% ·Ç_§+]ÏA’ô]®ÎJ‘üZ­J<¢Žs‹­_ƒèñÕü–hà£ÇÇŠz¼Øµ"×pQ2—ÀË:ØJ5Ú7c뤻ö†‹Îã’1\ö(wXé$Ùú•–æBRí,¼‹cΠ}»%‚µ¬èì«tå_Zk½& TãpAy}%‡[¸ê<™‹]jåô¬Cw-Ön>_È~½¾€»B¿[öÖWv€/uÕØ¡Ë¸"ñ.œ´Cø&–àaáNf¦¢ÌÀlÎs_˜o¿Í«5PEÃT¼jÅCݬa4¿¿C®|Šœ! <|V† Ÿ_(shÓ 9]à.NÈÅç¹øž`ÅY{â6UŠ„óìùKÛ&¡ú=q–)õªÖéá eaΫ§Á~qœH».ûÙwFÞ+{G ´€òUã±Û¸wä/ ŽßÊVï´ç~©ëãÎÂ5¸ìÅwýó¶¶Fà®7’¬Xñï_î1~ºåQT¼ÓNÓöeÁp‡ Væ^„Ec»è¿<\­;RTà´Ê tZó™ Þ=ßî¤u²7܇ˆÉ„Ê–r6_?d½EÀô{¾O¹ÂExü­÷p͸N.ÿ»—PŽaŸ1CÆßνâŽc™õºPÊÿøF:‡üh\ôëš0—Ý¢] pÒ ëïÄ>tdC’™tÏžîh€7·ûÄ,˜k# Ñ.G¸G¬è1Hx¶•®ýÂó:z†Î€Ô™›¨/ˆ7³¥þó6ÿßLëB\‘y޽Êc“fUAÎÕ4\Ñq®ãÏ=?¡²#¡Þ](Èì2ø24™Ô„=qP@»°«lÖäOõŠg‘½a„™ 2uíZúÌ’³Ø,Æ:¯Ž…]ÒÓἿ3¹zù:(ä^´×ñÍ_â© E¦rX›T–(@ÂÊt´ÓWc‹—%¡zëxìÙ·€4ýJaÉ‹Z¹¯†áå;4ŸCóÝJTqwœ<‡íÉ^L·&ÙTaJ:q'‚j±Ì:ŠíɭĘvp|í²ÖÀ™Ù¿Iã®Ê-ƒCûÈVÝÄȈbd=ߨs*LÏû†­'r ¡ìØð A#ì'ä~¨¥_ô˜ŠÁèÝSÇÆ´÷ÙÇ·¼‰D&ÿâ·hª0ÝØ“ĽícãŠù/gÁß[²$ÇXU¦ÂçÎjܱü^jUÀs§óÐâýq¼Ñã…Ž@ùéTlm,©wÇ ×ÃFÚ¤Iÿ$ ÎJ¬<òc›ªàúõÙ4ªâmž |ûwÔkð& .Q#…ÂÃpúD=7zLpuöF¶h‹,U@ý¥Õ[–Á?6tXŒë£«/ kòÚ`äÞšmxfÛwº{u²Äùä?õßIRàs GsðÔ›+P®wŒ›ô…k´ÿÕ Âìŵxz+£ ÌGÒhox&ì¸h‚ ­|Qka0SrYÅMݰTê}ñ4;DÛ]—²Õ©5hf,Û¶|Í»* ²Ó O YÒ¶ÓYï‚~þ¡´_ã%çèà;ÍU‰RÔoÀQ¼[|©Òœ_cÜ­&²vGu2FN4rÃix˜©4’Å_Î3Øõ—wá¶2výâÕ.Á‹vGé;ñõä{Š?®^<“mZ•Ì>íEv^þsŒîX¡K4Þô ïõIØâ)Á^æ~ÃÆô+ô—ÑZ\òŠŒpn›´"Ìm £A6°uÆdê^Wµ¦Ð›6¨Ñ}“ W!<2Ï .Ú ,9DŠº§¹+6ÊL¨$•öÄÈ“éËŸ¡¦½ñëÙ\Á³¸)è"ÈíÂ:sHúŠ—øsžY"µ<Ä>âÅó!¦Ú‰}[žÏÔYâòópò©(†HV†hþc6eFÁų2ìÀKK‰ý‘R]øøÂut !Ò »É¸]Þä_¢5ÍŒåË4•s 3Þ¡ëÝ78qI$I´ôÅEª—¨ÝY+ÒeN~n2Añÿ£ë«ù{ãí¥=UÚÚIJºß÷˜I EBÙ+%Ò^hjjJK¤EuÏ{J!!‰‘¬”²eû÷ûßçù>÷Ü{Ï÷=ë=ïûù<ßÏs.’«Kï`äø“ª©6±Ðñ n>„Ï.â›AnnóZ˜éaHrV‚L_®”d¿¢e!ÊØ”|šp XÞsg¶Áò÷`LÖ,ÞÆÖÅ^wé3Ì%z»~&†¸r‚øþK)ÏQïNa_@|™¥žOðrŸîhìêG'÷R³n7¨Ý¹ã 5‰åP/– e°ÅÖ¥¬EÌ Zæh²õ_ >ù²åíÄ~‹ûM΀]I䮈„Iž°ð¹é”sÁs{à‹Ú2>{Wö"…m0äHè\’ìùՋΡЖ‡°tS ÙÛ+lÈxž¥%L±£€f<;FâïL‰lù’E$&gqsRi ÏnÄßÔ@å’HŒ“h2=XÄD=„³ÒˆÎ›"°?P3=àéQ{"¹ßL©ÃÄïx©ÿîŒäÛäqïïÍ…û¶ú ŒåxYq {;ì‡×ð;¾>‰2dÿ~N„…7ÎÑËô!XÆUà®Õœ? o6-e#zôÉ¡5ª½žL„ÐEcY§b)/¼·¦%zGo¹·´HYp0g§¿œVn2CÕS¤6Q˜=¢þnÃÜãبšä‹j$ûSûèÂ÷2äñ¦?Ö ö¿Ãyíþ vÞRdî¿uù7X’°(߃ëúpÍý´Æ€ÙšûT:k×[ÿ‡*EÅ`­®?‰wVÏ}M¸(j7|Ûn 3õ) ©|dÎÙ+Ã\ŸwâR1+Ö¾:må;apŒ÷v`gÄÙ‘g<&¾9“Xµë€¥†*Ðô² „†+A>ËØ º-ã¼Ñ𻢿An Yvѽbé¶ `Ð2é `Ú.Q¼$H<ÇNæ~i ³’›kØ­žƒ|ñÛZXäVÜ ¿h¾ž& ‚焈ˆÒ!Þ˜yÎ ÜIc¸Ú½ëðCÎ"¸" ÷:° l¬åÔ^3N—»ÃÝ?û/%Jã\¹X°äÅÐ'àÅŸvÚÄçÚl¨ªÊ#xmÿ ³¦è!¶–xËêEkØÕò“P64–ŨþÀ' GÉ‚ízx!Ç‚õfö@ÓâX®Àç;œzÈ›üY’u^ØŽQêŽx<£ Æõ˜÷=kZxZ'¥º‚‚„¹TåǪfÍçµ3„ö.9p±V²ìF©®øÃ}óWcôª(™:4È;$:„g £Y¶Þ[PµÁн|¾ÃÄgP 'ËjþÆâîe¢øB5 _{™.v`MÓ^ñg{¶âr¶îìaÜ€6~°N·¯¡X༕wõ=–jN"ßÿ³áa}ÒÓžÏXŸ/ê’cä2…¬ƒÝ¹'ðëÁX²Âw?Û³{!DÚyÁõPSº¼u&{XpÅâ«áLŒ3þÎQÁðÒùà?]‘­:öeÇ’–>ÈÙsUW«ÓÎÆzW²eÒs .`i%Õô€øMlÈõ|lÝTŒYy½ü«:̽ê ÌŠpÀuʼnŒ~w|v ¦H“ÿtÒsK}a]÷4ôñFÿó†µÁƒ¡å—_‡¯ˆ4ÓòðÇó'¡ËÞ¿èþi%j¶dð„"8^gœí©\lý.ÇNˆ¿Á‰ñ’Ü9Þ–/úLÃF¹Úút#¦µ0€?(; (Їz×¹,e®1ÙõîXg¼«À^xP„ü;=n©ÜåK¼_Rœæ?„WcÌz=<{º× _Å‚Þp”Þ(Fb¿ds>ÏLðe¼Õ¬T§¢ jlL™8>󬄭×’ÿ4ÎE:¤!¬ƒ3f§øø<ò0JïOÃ)¯ÖÀáíäªðTò{«>™Pl ó‹H‰v2Pç#| ‘bŒ}ú™ó\"‡.ÄÎè{0ç·Àø‡/r¯åØ»‹“™ÌÑ:H˜³€½[/Of(~•÷ÏÃÈÜtÞúëIŠ•\Ç¥5 Ë~âÁ-ÚPG­»uÉô¹KÞ‹‘ñ^pgš;ÞÒ]o†5þ;/–ÝD?áDpº8Zjž¢¾¦ >û§ÂS¶þûŸÓµ›à‡üjn^f4îÏ˜Ç W&`Æëé$ò{v©&"÷w£×7iÃÒ“KIÞw”u4!WŽkž3éׂ04ÂOåÇa6‰ÂïíÑPö"ý¾£˜w¥ßœL kMÝVÜ{ù4ï~E¼/@ƒmP¼A‘iÌCÅmS¹§ZîGßMpÛxÓþÜݰ=üæÐjÀ˼äIñDI2²`tOÚãr})λJˆÌÔ¿Îu¯Ô†ÊÜ$g¯Só#h?Nž=::_YO„©‘:ø¥Q…|{%ÈħC’ß.&¯µ„m8¬FêûSÊíDzf:õëœH¸CÙ–,¼¼æ¤®eš.ÖD.R‰LQå»™ÆPî{ôî‚j–ÿ¹›:?ËÌGæ{ñ–˜Qä‡ñ’wØÌkžðØ¡GÒ‰Òž9ì~)ÇZØä.q&·‡Ìs¾‡¹Ó£ÿ‰hT×Ô`Ãr%üñÓ¢oç¢`Â~…E’ „ ¿ÓÌ/âÏ[™,Ðw:‰ö–cÁŸ*IŒß lT %!¥÷iÒ@2SúY6Ï>Ái=ôËËä\o.CµQ_ó Ç| +º®ð8¤_ ÄwMÛpjÅÒà°Œç–=üæ_‚Ksåéß[ÙÈJoÐù“\¦rØÜb¿W)~.¨ÂfÕ>ØÍo£^#|~õµMP¶õÜš"L^Ÿ pÏp‰Áˆv¨ýPHiý…Ë=+‰©Q -Ò½‰;ŠR ý]?þ§±>×JõCÄÐÞò2T¾ÒË‚ œ¼ý–Ä àˆ–”Ë,¦’¨¾‹´äJ2÷<¼}§ç½Úƒ—Cñ§T WÓ^%D]Ôe·8 ÛK§õ]„e~iÐâŸÅUŸÛ‰»J^@QÈ)TØæ†ísõHkÆaô~9—©§’äί°M¶ TÌ`Iö ,+4d[·Ã™A2,d½£¥¡8k} Ÿ…¯™5xö±;1u Ç•[ŸpÛ="ø'‚W£¦Ÿy¹u¬î¡×³¤qmÕ8һׄ={z•óu¸€!ë[ñ]ŠÙXƒ ›¿áò¿v¤¯ ›;8sF¹Ø£*U¢=(MEŠcç@8~Û.ŠbþÁÍ7°âœ™¡·”¸è?…m¹dq‹;ž™ ßÇùÛIãâù,'1¦çOA[keÒx@bp'iúgŒU&óp¡m ýÇË⢹—ÁáÏŠ4Vþ¡‘w3ô:Ši“•‰§!'I…ý³‹Æè¯Híα$£.·T}ƒè·¢,6«‡òíæÀôЇ4E½/p¤ãÓôUñçÁ?¨”Í#Êb§Q¿ï(\Z³§.È@+=I2v…,nf³È3M5Ü}8ŸŽ}¦Ì¦ž‚®  Øùtˆú,~ S<ЏUûÛ1=XŽþ§‘îw•ÀÝÛ£§6ŒiªÒXáÙÉ]½g‡‘.$ÃF–I^–$íêI(3þÿg@OøA½ N†óÉÆÜh”n>ƒÂ*3At™8kþ}›'l°?zÁÙž—¸`¬éÚZ¦%K*‰2õc4,le*ÍA¬Bs9Û˜t+r¥gZgßvcœ!•W뉰ÃA2òÑm{—Ç–ý¬nôÝþç\Ï#R7FýÄÚ†÷7'\˜îx+v%ð5ª\Jñæ£ÔÌжëJ“ýͯ‰µÊûIˆ¾ªü³‹ih6ÀzÏ%ÌøxªoÞsVV¹‰¥Ç¤Õcu| _àÉ+ýmÎì¹Ã$ðfùPÌL?߆{§~pÓ®Þ¦Õ9ôêֵ̠«>ž3~ÇbëõH2²XœØŽ³‡ú¦q,ÈÐàd& ¼ N¿.À«Q,XÛ¹™Ùˆ`g¥û¡Dg:ûá©EBóìYT­*­¤/b“HÞdi¢ú6’¶ÿØÁ_Ì[H¸pÖ–3Þt‚›"só{7õÄYV Åðº54lu"9WÕ‰¥òqòKùôfWÂKÞŒâá„×UÔ­ãˆR÷|²Es‹ÍíI™øÞ¾°ÌÏ¥{Ñ} £_–°—‚Gñba=1[~”é>çö^ŠòÆöçv’QÔöW%î¹GÉyTßü4tü3e7>þE±™"¼^ë±L²¿‰_?Á™,dÉ6æ*Òµï“ñ‘ß9œ>?4N:Óx×¹°ÎnÓ—÷RóÇÐ_M…D,Ú‹VþѰ!7ÿ>Ãé #$˜¹™°åÏNòŸ>Ze0Êžäè®#¡7P”Ö¾]Bž-ßÈêÇZ±!fûWµÊæþuS²¹ ÂÏvÂþ„3Ø`íÀ{#â‚ãV°íº_1=ö ‹º+ƒo«¸·—¯ÀžF/‡ïbó!KÔŽ…èïb.Æ-÷BÁòÊ90H8NJ_Àî¸T8(Ê|M4ñÍme¶^g¿¾ûÈÝ»B¿¼õ¥fGÌHðò8ç [ÉÍ)¢lJ|&®™NBDØÈbúÑÇ[a<–Õž ‡¡úS5Þñs%Ã¥ `ªß³ìw±'LiñKväc/kQÀrÿ¦Ó®Ùâ3ç–„’³qäSÂq¶P݉E¬|oóÛ¡År„{øX‹yæE`íïp¶R›ÏÏãÖMž9~‹5Kö?ÉJʲ˜¸ýx˜ ãw^ƒ!ž%Ñ™>?=I„€#ªlë§XÛx›Óû¥ÈiöV±¿·bÁi'ÿe¹{¨nôç½ÆøY/Îêfæ±·MÜ©Èý$íÈulFi̬ž¯FÿGC^Ѳua¬™÷Wï­Bï1ûÓZ²ÃEê¯Ä£xµ8o;Uùyè"¾ ¤f°ØËN”—º,ßÜà–ù¦ ò¤_0ÿÁºtÞdR*q—ýp`¥7±Ù»æØDÑ3ýÁ]Ê}Ƴ´ìßÇdíÅW¾ÃðÖÖ=š@K´0tJäN®IæÊ>ÿ£Þùè¨»Ž ±’êdêðæV0B‰lùlÊÐs 9ù„ݳd·WzÀÞß«0Óe+N€ëM cµ¶Ë«C_I=³lZ-ZÈ-¸’D×’aל20yŠû´Ñ†4ÆÒï1Åœêë\ÃK3ˆ» ;¸Áå¾Â 2éÄC|÷Ë\·Øâ¿Õƒ0ËA•uhæÒªÊÒÆº“çãéÝùÛÕ…Ø¡¸Zò¥t ™sº›ïœ…¹e8ý­YËWñ¥DÐq ›î$Ä_EWo)¶má&fûÎ=]À"<;[æã’8ô$ÓÎîf­åöÄ›Ìa—cÊÉÜzfØ› Q{ãÙ¥|½¸ß¼ªâ”ÒNßKˆ’¡] øËPˆ4Ùç¢=8³‡žsíwÂÃðñ °Lˆl‰âPöI9?;K ¸3ëÙº—#\˜”<“½VFxUÒuåHÚp‚f|yk~®dzL'ð‚’¿"ö¨S>—³CˆÔ)Û’YwŽI…Zªúû8¼›5ÊáÛAiÎq¶4ŠÒï>‹CîÄá1ùÌ}ÇKÜxÖ“æg„³Í)„å¾")ÛWÁF%ö'î=Üú±çogÑ€tf~ú÷µ@ž=ɘÆÌ˄Éáä´ê'Â`s%>k܇g„YÞ×|N¡z«iÃRgÀ†ÂqäÒžbxÜ(L:J+áÏÓÝ q: Òó¡Þ¥ Öëöª,x´Î“3’²qc¨fŒ( ^_‚ÏÒ£0Si<÷wÿ?˜äV ët¥è2ÕYäaóvœ­Æ’½MA=äw`ù+¬À›a¾ vw]F¥Î¤­¬ª²5þ»$ ¶†/1³B¦ŽÎÇ{ŒÙv‘¾mw'xGφÀPÁ¨[|4vÊ·Kcàˇ8á©[.¯I¦?÷e!'ŠðÉ×c(„®\|Õ7^©r%_l8½ÀÑþÂw×»ÈwMÃÚžìܘ³M•|«„5!™ÜÁˆp^­êöt‰*øûSµÝ Ü#Ã<îy¿Y~o+%ÿz'ôÁÚµüÝÂ5Ü*'a|  ‡Nb†b§8Õ0©FÞ˜± Y^ …çèóAfá“HGËÜíøûpmáRØ0î8ûôÙ…é}’¦šUJ$uU1Õ:sß~ü,ÐǵùtÁ†´b”—Þ‹ßïsw/-$R=¹³÷âS»é´Y8L-¸†×çì‚Ä‹j,`b ¼ÚìF ãͬ³˜µ»$ŒAòM5@·ö`8,[M¤:6°Å'V¡Æ¾Õ,Sʈºj“ó`ÛJ-"t|zümâ^ÄÌe¾ƒ]X¤Nô4t™Ò‚¥äž¶9›úC:dÉE)aúv0n„¢E»"›´ê Šo8†5R‡™nQ_ºîÆj‘–:oºJ—õÜ?[‹ƒñíç÷øÐÈ"t¨¦¶ëpNeƒb/ñÅþu™«™ûjF“øf˜Ö€³4«qwv ·âc ô|¹Ÿ.œÁÚ¹0å~?O^( ‚bñ¯ç\  ;αzïpj¾ögóáÑ!QXr²‹nÿÞ\ªo)Ïôì]¹Àòñ «ºÔùi¿Ä(ŸJ¾IWl3#û_œÛ½äA‡9¡/;±|Œ,¹íåÄæ—çbgk#tŠörO ìðŸœ/¾¿øèuxü~i’a኿9ÙÐN'›'ÁYÝ%£`šàÁ«#X¨Ð„ëÖ•qº-ÁôûQ¬<Æ[nróg±¥:íèd¤B~6:s:"þ‹“ÃïªDö³<)´ÅßT_c\q>"ŸÀq±”y¹U–s±½Øºû3$9 ‘'@õà¼p«†Ë–š› ÈðL“ÙÊ¿<í6ñÂb|Øûï `âƒ0j$Wä†é~!%Â)âõÑ´¥>Ù†,:ót xú®:­Ÿ,±äÌüsðÿÃ/-TL’D™H[˜§lÊüެæ_|¾¿Ž˜áЧÛ7ô“eê>øyãoî¤j6-zOg$ ¿è‡á&PÃõ/HAz¤à';–Sý×­¥ {pžÄZ|Ýh@î)lðœÏ-½¾ ¤‚ƒ&Ë?£”¯×ö©Z ¡ÕÚqhðD7ì±`–¾,={ÇWs‡Ý¥•8^©†‹ŠÑÅSéu\Q¸^\kvÏaÆÚÅôRs<_ˉøPì8ãL„k- Ô.øB ¨®…B…K‰Jd:ؼ0!»¾œG‘Aàó23Ÿwrã+“))Î…Ùáfø6k~ÜpŒ7ûPý¥üø”îØ@ü¯1$¦Vë¦ÜÇy’pï&¸xn _êq:J—9‘Dö•Ê”7ÒŒ„þŒ£ø*y-ÔN Æc_ñ²§'D¯IÀ.# ÒqÍÚ·õQq¿^¼æ1 í3¾SÕ­'q²Ìîá§ah¸N+d U®31¨Y–{Úw•Øwž» ðp÷ ú\ÅxWä¹Ê„ÿ¨B†í:YÍ_ñ};Wp®¤¯~Ë Ì(¥?%@Ûg»…Ø«ŠWpµãÖQ^ˆiþ\g‹B‹2iúþ0Ž¢÷EtÙëi®0ý~\]ÈM´u¤»†›AîÏ+>+ìƒ`h»+ù°ˆýHnåíæ‹³5ó ù»g¡÷)1XTh Ý'——Ä«(þ:†÷Ì»>ú ’,e}–=¢ë ¼þ*g@NƒYá ø˜pž¬á$¶›‘¯¸cbÏÊ| ÙË0ê‚ $HÐ^ÁSXs´§¬àCãDÒ»ÇUÁkf‘hù= Ó†zè¶õ•Üc¤/ݧ@Qß8T¬¹ÄSµ^× ôh¤‘Ý"a0ùŽ-ŒßkÔX>F<3´`&YâäpÖlè²4À•'àŠF6§û3‰I:¼€×ÓR@=8¦~SGÝÄX[^Œõ»Ùæ.oÜbú"Ã|.ʃv¼À[³ÝisköóáøhÌ»ë¿ÆÁùš|ÏÂÊìÕp¶ßƒù¿HàfivÀ4‘dXÔ|‘óœu„*7_oº²ÆX±ËưüE7콪K–Ïm¢žÙìÛFŸ Ÿ£pÝÒñ(»¯ƒ®J‹…w_+0Zn Ú¥bôÙ£Tg[(Nþ*Aú—_£ùq'¹Å?¹mwLpõæ³Ü¾+è ÌQ ñ-‡fÃÉlÏÆ')|n‘š6x)”âd?Uªê zýE4ꆼü›yà$y6ìåûõ‰Á£ãP¹4úÞ:âÛêDú}f:¿ŽçT6­Ç—g»@vk1&‘NΞ ÁH>ã‚.«þ:dÝ,çÞÛÚ›áë0æŸ7¾)» /B8½] ¼èé©\ß÷—x £”{®}R8û-¯ñW¥>iº[ aê3¸å‡È)E€ÏÎÓ<å\ÀŽ_ÔôÁmäŸyƒ÷~àý€Ô')­1ºSž{<ƒ)/ 7%«·Ob›×ߤñÇr9*6މ†7cÀޏâq 6ü;‰mZhFTµ¨¢Ý/a¸Ÿý“SXè‰×V%`~v+|²•dž;‚Ð7ÿ%%{°OgFüñþò?¿±w’ÿ“‰&×óál¾H¹k¹cqÕ·0<"R…'çS¸Fsù~ÙpLR_l»æ¯CMóÁÎëd¬6`Üþ(p”™ë+ Åû|ïý±1bS"J®7%ºSÆ^µ†•$jqÈ剮ï]þEÔÅ>iƒ¡¸æñ#¸±F“i­t€ðuÛÈ’_GàöèÞjtˤ¿áŽÔvžgx,­â0ëÂBJï-¡#_?`r_0½µY‚¹{Ÿ¦—Çðh99«`ðHâ4˜éq LVm¢þúDwÅoÐÜ? œþà(æ™ |xy©·àC×[jäjÄ¥è¤Ó3žù›£Á;1Fž&ò¾~,æýº9›½4ýH'her[œŽÑõŽš¹£í¢d•38Œ­•Ôµwr6¦ŸÓ…SS2¸¿Ž„É=ÚMŸ”ÓÍê‹pß B$#é°–|®À¤ŸÕ°i(ÕBª«—7CcK-ªn8A=S{¨•äìQ.¶”',«ˆ-Æoh­ï~ŒRÕ‚"û`·§ fk>ƒš¹IÐu¬—}GžfÍiá7íZ¬Ñ޾'@*î=ÇIå”Ö+Ôà'©\ ’aÓiô•øØÕ ^#”~”—âÆ_¹É.« ¦”Q©9Ã4ðñC:tS(;O›:V¡Öà-úB~-¾êáª}…ßsPËõ8gºt;”´¹±´Ÿd²^& úû„¶pa¼"Ž¤Ý‚qÓ£ ߺY þî‚óâGàäŒ@æ9ådw/ĶfMR]fNõå ÿ"»s» ŸyƒÈþ)ܛ踳«vîϱZM `þ©AŒÞ¯Æj¡)/߸1™¶V  ßhhy.†_jE!,¥ -ÌÒ1æ·l±;Ë-|«êê¬/6¼Õö‚Y†VÐøe y8( ýϾpqö Ðð›nßžÑïòá̦q¸:õoãi+Ì(]@ïÂkªPí~üõ½è›:XL3&n‚Á¦(Òp)™hOð#wÝØÔqôTt,S«;¯g¤0Ý>àâsÒã$HÔ¯×Üœ_ Xôä4liÚÈdÎ9£fÕgz¶6?žsgÏgõÿÞò§Eb}x&^l”G”XDÆÄQžEÔ¸Û™H™Tâω¸eN:¦Ê<פm$®cù¹×–ÞÝ"Ï}U¾Š.Ü>n[¨,2A'¬úS‰‰¹0>5"&]&ÿÝW+­@BwŒ äo+æ0&™M¯÷ÆæÞdv©g ¼3Ù€k÷!>’gyB[anÚK49­KTìtIÉÅü¢ ægÞÁäûâìÞyjËv˜p$«á tDóQ×Iœ|ÿ¾ß¡yª49-Y m¯ºéÁ‰oþ$ùÂvÙTH&”¯ !œ¯@ïAyÚ |¾aü¤8P”Ñ éxè™ÙÅ-ô:E/ýÁƒÇˆh7s±ßKúæ5‚FínîÔÌsÛó…N¹Ý¿ïÎ$oþ…0þ¤~þîm‘ ¯õ…ó2Ëáv½iW5Y.kAEñ¦è 7Yé ìkã?½âÀÉ—‰7cÐG¦WîœFÆDr¹k” aýœu[,L,GmÏ}1eÙûâq QPp>ÅIÐÇtŒ[°^öí~Ãe?,ü{ÈòÀôÖûf2÷¸QœÝ\«ƒó6Ù#Jƒe­Ò\Ô€×i*@R?Up¥s.âæ¸3°îæyh]ÄÆ¸‘U?)?Ämtݪ^Þ`ØŸ†!]èv]·ßˆ¼›· ~Mz@ÍžÅd„ÑÛå¸J3”Ð|ø–©ÊO¨¨ÄùF€ÎÎ6Œ[ˆXô'ËÕ³Ù¹ôä}™Y÷¸1àC&nrb“2æ“S#¦ìèÖ§0°q£ÃÜG§Ð¶ç¨/'Ò‰ìAy Wd:‹Õ5 Gzðš’_ñr”Òp´LO.Ÿ„º·óa5•cÿ¼§1R=›Dh¼æ_ªšÎÞÚ.@ý¥âDxª\Ϻ„ù€¯3ÌîEû›¸h·aíÌpó„¤ÃþB¢ÉýGž§RxPV…i~ö‚qÔÆSò­É kÞÆði¼ÞI¿R—©~˜ˆWg™¹;ÑæS7M¼, ?¢·°ÓjG@ñ™<{ÛÏ«lzêð°]Š›(ëÜxÉyY)µ‹ûn~†~yt&w7 3â$kŽZNñMû©“>K’ò¯~d²f*ÙÛÍþÂ%ùi0üd)œGy2âûG’}ø·”>ÍT…&¿éTu•+\è°c—>vp"G^Ð^½ZÛýœÛ {7Ì> £é·K8-mVš ²íЬ]Û÷ö”âÌc·™w™¬JOõö^£=Yº0Å]–ü׆¢{î%Î!¹dÛ¼3(·ÿZu]Ãá½àÈË„3†GñÆÄ|˜ó:ô¤áòÝEx¶ó¼m!_ÔóV¯ÖbGdÜa£²#žvk€ÐÙ|8)6D…Ú½¹Ê=3qЧõM%†ý °c"®‘Ò™!µ<‚ž¬¡ý öøÄÿü(nKw¾›Ãu}•&&r ß8ããçáŽbßZiΜ¬˜%±{Å̹—ï/³-Hý¤Á=MV$ªOnó ÚåÙÃE^ôê¾Røþc9ü€è+¶ë¾¬gò²»ÈXõ},øÌ hW#×7"ªÿ” ›ºÁp‹è¬Ǿ[©ÜB.šWï‰oœõШ±’»üÒz‹ïðÅ4BÈd-\û0<÷. "¨/2 ÂÃ'‘è¢Ñü0ot=ª$aÖ]ܺþ.TÊ»¸m”?½<ª)Ø»ú&Š…ÆÂ½Æùhû²_pu‹›èüˆº¯Û‘|aðÃÉ›-«~?Ç™`÷™0:æá ðyF_G}áX^û qâzÌ*J„m‰T­i2(ý»…wFŒ™_—<™’“MŸ гÎâ'8=–âò¤g°ri38Õ½É_QJu7ÖM©æVéǤêsôœÜy¼+§AïÿKv©ŽÝqÜ1uöUA+³óhü^Ÿ ›ÕbfÌ^â/º”Ⱥ)á¿£jüÏRZÄÔú\] DNún`S>àÉNü~„qÏbp¹äk E¥øêy ´•˜1åGóH}Á¿†ÍFA¼éõèäVÍJm8E.¹áÊöIÔz_EãøÊj|\äR“<>hP%¢ÅËÈF~,6KšAè¿uä‹–&9âž1‘÷81C ´º±ƒ½g7ÁÕë<œ8.F^E !ïô~¨Æçqµø\±uê±Æ#–p/í+W;ã]¶j< ¼1e×㣃ËP_@VýÜ€­KíñÛ¬ ð®x‚.מ¡{¢!™‘/¢/DC*&ºÉ²ø·op±&ÞÿÍóûé2•ul}ÈؽʑuUÜÂÆíö¯ áˆÉmNDC?N8ÈÅU¼§Þ)?8¹£Ò¸­y+ÊŸ¡1KÞáÍíø“®näž/z .¢9 ~ï Uª-‰^ȳiï5vLÀe3^K›–TóügÏFß+‘ìwõ"úd¡\œ| Ú¬¦AÔ­Dи{’Ú'·Iç#pÊaÐx|nxÌ!M•ÊÄw¶¶g†¥-pa®Ãë@õú œqÃÀçå<¹px}ÙœlöOÂÀQܶ4ôU¬ÙvÅL0c*' ¤î¾}©M»r.ÄÈ`®;ææn†"í"®n–ù3hÂ’lDZ5$p÷eR;)‚ŒÍþþ Sõ'¡›û;¾£À„=[Á#½®Y 1qí[œCÏ?þA™ªX4Ê—Fëο|žŽ¼³`ïí]Ésûx¨l±"›ìLÙïÜÌ:LGG|^΄„݉$¸=O©=B3¯a“ÊF¬{òâj–ã>^ôÕp"Í==ø!üü–?€c~‡±ú”HêÝó çô2|+ªÇùH¹²=¾|Pª<'Ûâµ¶_Üá 'ܯD‹’ñæ„!èºë1šK7¡Ïœ(ÇJ±qï×ÁYŸ~j"¸ƒË5lFÁ)<.¢y–Êd³€…ŠpüÇ)|1+g}œ>cçÖ9ø;ê%oa×~0Ü=ƒì0èDå%/¸”-Ùö r™gaüÊdm_ ³X›˜&¾ÕÉc¿fì"§o\Óm˜‚”ËZ ÅŒÕvàÞãñóKÎ¯ŸµÀ6Ç@<+÷çJIàâÁ(Â4Ÿ£¿o ·ƒ›È”e’j“™htëW p&þy)[\IÍ®)8iO"ŽÙü?Ùå&Jˆ±êŽ2îG!´gMgÝa𠏽o<‡l˜u9µ ë;‡úlC6$«®Ä»°^Ñš-}ËííXˆ.]»o<—ü¢–^•Ÿ÷âÅÙ9êŽß4è瘙œ ™ÀÎT#/Sk8 %%° ö%Iíá¾B>ræŽ$ÄÜš•zï'å:ÀËÚ‹¦ýÀO>¨ˆ‘âE¤Û2™,ñ‹çîtÃDSR’>7Ì'Ë®s©Ÿ[™ó“CP*V+7ga½mý¬«G‰›rñT¾¼ÁMÁV°àª?ßìùa¦Âű%p.ï([&’žkyìýSÑÆö#ºûK"< Ȇþš~î&åãëIr¬n|&ÞyáW–VÀŽyëáo,bÃ’3¤9Õ“„õ[jbô8YõÓ ­Yè«pæp§6¶kï‡Ac® š*Á¾ýÎË+âLxþT2vY,Ê&ÿÅk¡$<0ïFmæÌÖeЯÓpÍÇÙðâÕptWÕÖƒ\`5Ì›ñå’Ø4|s0 ¦þÜŠû8¤ŽO;SÀá€=ª–Góïmdâ'!qÙØrá&-®²‚¯§SàíÉX¨*ê¥UR`ÿü|~äk@xˆ³×fráypk·("P6“ce'jiuB9o‚ØÒ‹ÆxzÃ?Áë+Xž²6Ùxe #jÝP–¾ôÎZ‘%ÂÞÿ0„ÙÃQP©p»í¢ˆí¡\¾Ó6¨é ¼â¾Tˆg†èì”…²Qûˆ¼„4[Ö´žÿrÿGø·I–Ltð«n´ •"ŽIWÁÝzž“ÄNÙÅs‹Nâ¿K7á냬æ€=[å)§,_ÀÀÚ¿èro;ìéb·{})½>gÃL=êÎ)äÍ@'Ÿ™Ð²Ê™.û1±‡ÿ­ÑËvá·3𶤫²!rSÄÆ)©"ûu>Æ jÃ1|¹HŠ´³i8\P3™³‹7rõHôõú{8êŸÂ¯ù›n1—¿ÌâÀl™Iû`E¬dL9§k[¨ŸŸ |¾’E6+MdçX‘w wp…G•˜Ä§yXz×|À× ¼Ÿ&JªºÂ5øÒ&CœZ¢@ÃH‘3_ý’ 4+×{­ˆO³ñ‘à5´®¤ß®vÀñ5–.ž€ƒ¿kQà~Ï}%Ì¿ìBœÛ¸Þû¯¹´÷ àš4ZÞ€×G×jù»ÏlcòLšœkÉ^MÝIò>Ñ™0š”Ïÿ'ʺŠvp‡|“Èý|yÖrš\ÛçLª‡²ñnÄM:\0†-¡Ðè£Íö´' €¯Ü$ɱEj¬eŸ »×÷,ј~û†6sÙ¬Ó–øb¯Q\ýÞõ®e-ŸŒX÷½P¶3n+)õrA(qj ¿Âó›Ö$2%Ž9û‘77}°Bî$;W÷ײ%r‘Q4`W/;$àCRe²À¯{˜ô2m¬lÝ#ÃãÙ‰øƒ8+4‘^¼¾Ûžü WÎ BOˆ ï>Û“¨{{ÈçVø²9”ݳb—óß‚Çl6¯G¬ÀØOVäÙ #RõBÛüã·=‡ÆË;ÈîGX(¯¢q»×yÜ3ÓÈ 1>z·Pc6Y}à·½Àƒô˜éy'!aÂlHãÕ€Sy"¹Sü›/xâ [½H9oQ$‰.£9÷ž"™øa1—X`Du…„`’äbú¸²§žF­h)¿L]ˆ^-‡Ò¿ŒÁÛ]2Ü̘ôóçÜpC0ã>†lž|]x%’“YïÆdâ‹aÔÞô ½ü7QR²!§ñ|O4ÙQÕ â¿_ ×³ó$Äd ¬ŸŠµîµ@”îá[=>7ЊêÆÃê›Cxâ³.|à +Vô@RÙWT5·Åæ/vD¤IKÎã6!e 7ÆåÅlÕ»qÜj¥]xrŠ%IØæƒó–ByÉkîØ[5VmiÈ­œ\€I ì7ˆâñǤêÝz/Íß÷åti(™ {£`YÀ9˜N#{÷)S­;Á|O;b>å fÏJSMù;J”ßÉG¦j':ÍèÕ?xb¬%.\v WŠ`ÛÊ<°¨c°Çf ä µp)c"8]lýaÁ6gÓ‚x¦æîä_æºnIs_ûáœÊlž®¼.Üó¥ÈQ#1f}wkhHc[GŽÀÐÇ{¼z‘x4 娲ÐypñA7&%]çVÝÄÞõýÆ_'ŽS7÷˜Ÿ£KÞoƒ‹0›{ Dn+2ÅáçQ¤©¾Ÿ³R4¤âïA¬ô¿Q…5¼›WÓÙäÁPõ£NÔà"ù.˜öà:_S`êR½À—µc±vaÜÞ GxÔtkÜx’Ì2›‹÷'µa~i.Ò[H^¹Üã‚\ÅsÄÈÛtCöØøÛ!'&;„ØÆ˜f|£~—}¸m.›³IŒñÃ1ý¸ *;xˆ%ÞN"í7ßÁx(æJÿ8ŒÙXD!ø·&Þ|\KWˆ‹½ÍØedx] $/}]EO¹!OîXðKÒ¼¢ˆ~Ê‘gŸªUYìC+j=¹ˆnx¦ÈV?Ì"߃­’4Yµ2‚„K“Å! ]‚U]YD¾I 0K:K{BÆìĺ<ƒÅÞaúÃXNF2{ù×”%¯¸K£”>Ñ)o³¹¼\<¢zÌ÷€k:”³ö„ñ76œg1ç4tÍ\ŒãÏìƒ!DmÍ"Ö‡Þò‹H¼Ýd2mž:.$AËXË”™DCv'Y ¦ƒ–ûÃXõÐ8Â9Ù3ŸÍëYçÍlTnÏb‹^rà_2s/ ’'ŸµA#,ˆË’G¢ïe‚5'8ÏÁ™¬ß+…ˆîLç¹OñÚ«±äГ $2ë!æ ’U+"áö ¢“p‡ìJ½ŽÍ‘o.ÂõÝ ¸Uבû¥=ïó³´s{HîŽ-ìnöNxªLöÖ—ãÃÆ\ÔË,dÒwõȹƒ,Ê÷6ªžóÀ­w:ARF¬tíéÔ§ãÈÉ^ˆylˆ_å=!q»1ñê²F´Çº X9‘MbÐ3Š;z³âôò|½Æ™QèÚtgLöÁûqæÄo±xÍ‘aî"ñ»ë]Îói-÷µP–-~×G úÊÐzƒ1]©I¾{ê‹í®ìÛ±&\˜í Oäˆ}rN‘¶‡9ßÒÜ!:ZŸ°å©0xµûŸFc9H~$Àú6q¿¤Íq7y§CNˆ*‹~^ ½1bx&~vn‚/]x5/ø•pÉb‡Èµªé£ÇÄ’—Á Kž.!7~P­W·P}Œ ºó*ÍñëÖ{˜R~Ò|sr§Ïà×5‰°uR(v\±ÃmþeØ"‘‚˲ʼX~er§"÷©k˜+ê|²§Ìáô´CXH~ÂÀìT84Ô§ÄœXú¶Ãâ—ò¤ÐäØžéA å¦†€tÑ j;Šitî½§>áùÙMÿ8‰¸ÓØ>' ̽ÃÞ& ˜¦¡ ·ýëù›{Žs<,XÅïpt{ßfv†‹~Âü(7(þ¨® Úp#ô+W?)ÎäÓʹyTZQ„” 겿k¦#ÄXª‘sFÀŠÌëàcž†-¿»Å3þС^Mâ¨÷ï̱ŒŸÜVíM°|þzëíD¸¾#Í¡Wh””Û@“¯¸•‹²šƒ¿ñg÷sèR0ã©NʥÆ8ÊÙj.£[¦Ê€öF×¢„¦î¬s©3y§¸Vº(³DWv_ß—†\Äê¨Ñý¯A߈¯4bå»ÝØ<M²PH…´8<¿@kbê ö‘mýó4s–er’³ñù ÙeN6afÞäŒ×¢O“šýŒÝùS 8)˜ ±f oÁF×yˆZ$lAØê«Â­\Wîטé,XS´]—°¿Û¶ã½©•ü%ÙßT)âÛ4 ÍT\±3?‚Éœ?H&Ÿ€‰sœÉº4iÖ´>…ç´b,þã¿ä[L¸ÅI¬M†Ÿ(úó0öå;â=ƒÝprÚR{¯Ž3"ûî—áþêzðþý+%Ãßy#°‹üÊ=/“ªaêT#ÒSþæ™ ³/›§BÏïa@Z%ÍX’£ûõò6üg w±Ár5|s­ÇÌžEò˜_úÔKáÓ¥G1MQ€liÞÆ¼ò.`ïÞ¼^nƒ›Ì/où˜xÀ“ö|Éà^ɤP©SlMx>VO$’+É̃…°éÉGÔ(G4‡‘yüz¼;Û,ÛŒésê˜°æ¾ 4<û8¸´^Ã$®Bê­áÖ#¦ñô<åÕËæK mØõãðiL>©[0kÓAvè]4 >ýSÖËÂÜíyè±Âˆ”U×8´–¥1…Œ£¸h»;ØÆirw¾öZ¹ <?‰à´ÕX~/oILÁû÷é˜Þ¡×v6á5à½\˜œMü­ú‰èþà0þV¸Œ_ö¼@§/k1º!ï?˜FÏé±eGª9#Ëc ‹aÜ¢ÙÑÈšaòΊ†U2‚äH»¢:‡æY(â5]VpS˜¤T<ÆJ¢p&I^Ô¾{ÉŸØûø1t»Ë,É…¯>ÅÜ×Ä-8½YŸX®—_§ &]OË.qjÉ‘$8À¬ák"y –œk¼2Kp¿¯ó˜ Gd§“ËÏOcç³p,'º,86f9Jq'™ãæ%ØÕµ›´ìpIæO:k]÷Μ­ “„w´ Ço32ÙÄehòc I[ ,†ÞçL­õp·qê^¯ƒ‹®¦,ÿi4|1‹gí¹œc¿ +Ò‡]‚'ÀÍO›„×+’ßTɇç3é¿Ì,º3B–¾÷#ú|™-¸–ó¸³NóÙïCɬöà5¸4ºN?·F“ËVÉý¤|ö(ß5ʦ.y:,¾)é|ÌÁ·'Üíl 5#Øû(¼u œÙŠ«fìåy_zɦ‰·D)ŽXMž‚óR}hÕsæ&³‡„íÿmgv“¤Aúã‘7îçê¢K¹ü¨—Ü·ruL{–€§•ßssÖð˜X†-úÅ$GŠR¶¸ýªÀrÒxî¤D4ëEHW~4Ì5Cíð þÞböv2ðlšK:ú½Mè”ëüå–󩊳9›.Àc±aøÏ©¿ñ?sº{1.ùaI czÁM´xá$ò<[ìxÐ ƒÙ<-Ç8ö"SœVÆîèHøuêµL =§N¿· ©nÎ ˜ùú.š–KÕK¹€ºíü´àóp1}/ÿÛr{Tß_Ž¢žqüû>½Ü¼`IÈï<‰ÓÄ‹’s—*©Õ²Ä î ƒz8l $ïIJGÑ#\ ãø¥9°ÂfnzoBÿÓ6·lO–OÍÂ|cŒ8<™Ü ±çÐôQË…† ˜¥³™ÎÿØ&a!Î{9Ç»€úÕ…ü±ÁBL&Çv¾V$#‹Ì`‚üaì:{7úkaÏÅE¨8œƒËÉëL1öÔË‚øÞzŠÍ Þþ$? L'O‡ý…Çø'…G¹bÎh,¬„íSÁ0«ƒºÊ‡Ùv›Æ©k±wK:aº4‹—¹†C÷úè”óÞøLƒ®§Ÿ!¢ÓŽü~¸»æàu×"°ïÓ$á»™¸ÿQšt·â[fÁÑu|¾›Ã¶rÞˆMþzÍÖÄ@VŠíýäÊÊ]VÃÄ­8°u*4=¿‰¶¡Sð} 83hJÿº||¾w øÈ¢ß«VÚȦ:ÕÒbæ·»Oá'ÁÜ™“»°«GÅ  ¦“av]-Úôx²Cgíˆä;¶î£㫨‘EËé:Ó XG Yv=Ü{øVrïaQ”=si=È¿Z¬ÞG¸ãçáp¯DÈŒÁœ†o¼ûzà_`Jkõ  Êr!Á“Dêé1ñ‰Å8!ÊU9+އáM½pßL¤}Wãáê=(°ç+×þþ:ú4&`Üö2zõH\ÿnIlŒy¯³Âðõ†BQe8¸ŠñoØe`ފ︡-›{à 3²âq{k;ÞþÕGZ„˜³X-X.vžømüÖS…ÖŠ¥ä5ïý(V9ré°ü…(ëˆÖf3žÚ7~¯øòË/Ñà+3yùrŠäÉë©Ì[æ/nÿœ}ÖV÷BƒaŒ¨Ã)g.M„Ìø}x3¸©Åci±(Yºß ~KžàÍ3@Eož9¶B¼ë^–y‰uêé0aí8¯—ŒQÊÊ\Õ†.—ºÏ<”„{J'ñï¶§PPxj؆mðÿ «Ï‡öðú*ðö,þñ=r\Aƒ1µ5‹R÷bAa$¼µU¹ÅûƨL¢SÑŸ€ï“aضúú|*¶­§Px·l ¥úí®Ô`ÉN¬x{ú-¥WxmàºÃ‡ËïH>,¯@%±^ˆ±8;¼œº†¤¢ïM]ØÐüE²æ‚ߘ8[ OÌ"?464'quY½”ÿñ-Vxë*ÝÙœH&fÆË»ñ£°â°án8ôYÃ¥„nœ&gH²&bPÇýž›Í匳“œ¸Ò-.ü”Ùa 6*Їó鉸yÞ"òïï8Þë}CœÆ7ÔŠNÂ@û˜b€ÿÖ:‚Û‚VþÈâJ4Ýÿ¹oÃôÍ‚<Ì\ަF™xãÚnüT{§èùq£öxWýûÈË0ml.¸ àþEe¢ÅÈYxºâ nØœÀÕ„’["üØ ƒ—-…½ÙÃèÓôƒ»àØÞh«]ÃkÜÒ ¯á5Ñ¥ —– çæAøO˜ö`5L™6“íçÎ7î0eÚz™PòÁ“•ˆÆ} ð˜ÒϾåð¦0u»h5$òŸ€‚ÝNîØ¡Ⱦ:DƒôpbÇ¿óÒ‡õ ‡Rµg!4Ó?sªg~¹ Í{/Çœ%Á¥ÎÞ}!|i݉¼‹ÕP[d×È+¼»}”=ÆÛ±o¸¨ì§g@ÌÈ#ÔÙ%ÅÙ%ÍäZt¹õéŠÄõºÚVÎMÞÚµMUá~i¦v¤ \'´Á™‹´îž5>í‚ ¯­ñŠa\”ÜGíGã/ï°(_„M +„—Á»¡óèk{:CpfÍo1&è:ÚÚY MÂÌÊJˆø âjúæ‚äæœtÏ=éáVeõ刼œ‰¢¸ÜÛ÷¡­%´>Åb]¸8Ùå¹}zÎÂÓaXÂÕrª’ŸL ÞÅ1v£ã~¯ÏåÃpý~h{¶Ç)êÀÝœ0|µ(K4—1S«AÌMÅuÿÖ³]NAü€ÄW_à ª†£fW îh+œálÓ»œ~òŽM˜Ä,ÏB.ó0~\' v¦Fx?;{Å‹pÈþO_ãÆþÝD« Ü6BFÏFñ*ä˜îD*m°ë,UÀÊûˆ(e7]u>Ï%mßÞâ×v ­WÀU4š¶9<‘ àòeõX±ü•êiýËI f7„Ü–<\73/wCÐýx¸8[Šˆ0ëaœ×ôŸñpÍÚ@º#`¦ÿC¬Ÿ\OÌ \ë¶-â-ØmÇÿ¸H„Ìònãái)T¼„«OÒC¸¿dzaUï|";+ Ñaª3ÕO5…›®Xû”7xÁe¾¯ZŒ/d–[Ë ^ŽóGtHf»Yh%Í.ÅNÀέÁPÜ<„óÓ{Á§þ'ø1ˆ8ý:S`-––,á R–Ë_×Ññ  ¸¦Ïìz¯ÀäŠäJp:•~º†ä1ð€¬­I+ùo¸\K˜“BšNãkhx‚¾õwçã jÍïçÍMø¤VŸT‡–Óõ­ŒEÕp¥Ï6bWú$(—µÌ iäªdÚ'ÛÜ9÷±ÂLÊæ2Ü;‰ýKìQ;ö&^V•b½cGÑE§Nq¡ò}‹¸2¯0X¢„$@dDµcÔµü+y:$•û ¦á#cxV«kƒ Ù±‡ÆL% ‹{}U˜4¬xÅkÏ^K_.yîá4Àä2Ô¶H’úÞ!x5o)Ÿú™ÛdpD?­ÁÉÃ%¨³ïx¸ÐöŠ2¼Ë«Â'r… ? ·¡¸¹(wõ£KFÊ_!™Û-¸g½ZÏÄT¾]ÝK{Í Á¶b7óqÞˆ-š¶ÚPá&=Ööä4}ª:Ÿu\âã—Ú(8)süçΆmPæm?è[•6ÄVXšßSÇ8é àum* ¶‹°hM-<;Q“͸w×¾â6Y¾†O¾ äH¿¹êÝÃÏ<.ZA?yž¹ m7cÁš-$þóQ|Ü4‰Èµ-Cþñ¥Xo&Êêÿù5¸˜Ž8ü§aN™÷\E·scjœj Ù¹¥O «·=2Pãõ‹;<Ñ‚Íü<¶?—i†£¸?zÕe/ŸÜƒˆûÛØv—BVtA„>VÆIúarô׌­é—©ÍÓîÖ³¾_št—ºáÏÕvœï‚ÓxâÖ3Œ7‘'"i›°öÆ7z&ô5Üí•ggŠ”Ø¼´::³†éü\\°)ѶWâŸa¸z»*q¸·™m’Ê£d¥É–Ž4úÔU˜8-…¤Ø‰dÛuy¦³ÏˆÍÙÙˆGLÜq—ï}œ*–MDNªò¶æ¦àc­9pº×åÍö’Á1'¦óäHõ‹ÔÜMbU¢`ó‚6~ͳ…ÜÄkp³­ÇY§×ãç“h $ʦدg3ìø(5š;}ÞÆ±#åX©@V|}‚KeZ©›U2×÷¸¥KžB¸Âd¶cÃx¶²›3ÈuHËyÇJ9µ™¿äîÀÁ3¸í˜4øús?Àü(/ü훋ójAµIór6À†Ž¨$kCž)óŽŸuc'g]åÜR̬)„1PA2Lûs‰4àÖÇoA.Á%ñLÈÄïß÷Súj2.‰ë­Õeÿ†Ò©”ñóã•A*d>y|B‚±†o³M/§~¢ªi~øÅùžTœÎ¿–¶•Ìj1æ9öÀi?ùäÉŠ<-r_e7î·HÇ™¶$So%DvêqŠŠ0pƒ$ér×%Ž"äUü3(#±eËy®d‚q[3„§]‰Šv8S?îÕ5¼¢ ˜´¤4´×âºê*nÁ(—ð–XÇ- À§$Ž”ÇeBþ?B;ƒëg1&;óhLx€ÓDØ’›¨ÑÏã®fe{Š{ž2ÀùŸÔ&'/ñÚ.tpûOŸÀÏÂÛo!ä]Øhýü›Þ¼;Ž™fNGùGP´y%íû£, ãätN~ÜÀ«:øˆ‹…ÐÇ´|µ +šÑ…ë¢ÙÊÐ9l‰â":²n·¦ý Ø+oáo „7_ÏÂÜ¡é$çÐ89È1×V=¦k·šÞW7'·O‹±ÓË6‘\ ýÑ ]\…y• <²éÇê¸ìã¥ðXddþ¸£‰QÛŒ™«c?î`<X)†&Qcɬý‰¬àô{ÚÓ³©¹‹°¾pbŠ4ikKà>ÄÍcÅïRxOBÁâЇ'5Æÿ/ó°ü¤3ÙÿµÚ²=¯jÑ|³ <Ùº›79t SO#ïÔäˆÜ3FW—ÈÒÐù—h‘û'Xå®Ê´CÃ0Øt9™žñSNÕ ß4—¡¨m È*sƒ:¹@‰Ô­Êb—_mÆKN«p_Ž3‰%éPÀZÕSe“.ûóÏ8°1‘d­ª@)Ï·àX=v$úC®š9H¶À˜YM°ÛA’8ôÃNï›Tbge‡õaÇ.)bU›CÀ*Ê ­z¹Îz¬û'ñQ>É}|®ÅIÊÓÑ6y[?Úò¦šøÓ=éËé¥;ÇIÌÐ ·#iÜ•ßM¦ÍU#rcÌÙœ9wð\ ëz¶ ¯|Ý£gé*@®7Ó*±TÞªnÜçCÑ8ù¨ 3[ã@$zXËä3¨¹Á„m9³{ù¼X‰~ ÿŒz¯`ñ-}}Ãw»móø¡€ ‹àÛÉ¡HüT™´à/Lû»®üÁ·ÂëØ¹ÓaµC ï¸b(ì¤ÅfóèºFifÿ¥gî¯À/ÏÿŸ¶0w–õ‰°´#dǦ{ü‰»ðÔרSØŠ‡‹,™¡¹4©¼3¶öxAVî¨Ïîá_ޱ¹#W£+¼ß2qï™{袤Ƣׅò2¸^Ü®;ŠðÐ@:zß;f9ÓØÈ‰[8ië\|ºß ÷ßÙÏÊ&@ï\-R¶Ã†¤žÏ$òKÄ`Yÿ<Ö¹Ô3Kб()θ۠ù—!<gêÉë)õø·Ò• øµš´ÙM$íVüGwÎâîŸ~Ä¢FOvv`_ïXæã#Fþ~ÛOn¿;ô-¯BT´(É|+ÇbÆ‹ô8~ËžÁ÷àvðûËÅ)  6H#æÈ2§y©ß…د'И9ÌÙŽÑ$õ6Ó…B±é²ª®»Šêj t6¹{ü6ýgÙ+˰÷÷KÈ}p ~lå¢ê¾Î…%i9OjçÓcŸqùî•Pxf!»Ù˜„Þ8‚‘ ¹üRÜ| vIÔ ÎÞt®sٶʽÇ{J%÷>ü*s ’[‡a(×57§‹‘Ħ4<”»isXl ž‡"=òlƒ™5ÔÈF-½ Ì2§dX÷oLȳC øô&kÏd³]‡§å¹6ç1N½Õ2äáæäÌ©¬ü€1ùW²?r °„À ­"XZïG”V !¬ÿUÄ\¶À>-D{…=ÓS~@Œ¿Ipß*ùt¥=ð4ƒ[eÓ˜Å8AÒu0:»eI½…‰tßÏVµ×à!»lßÐ/Ì]»4ºÀãC ¾³œþÁÜQèîù§Ïr€ƒ:MœÕiÙÆÕ’³À¡8žÈ8BÜdÃHt¤zéncÕ‰´,½œ~»±iÕÐÔÓûù¢·2èÀ™Xî‘g4ãOÑåvÚÁÝH¯Ê–Ctk:qõm?ï¥__ó¬ÎƨeÞùiDnt ’*…#8¦ÛíÙe o¹˜Å뀚<€çß@OýI¬9†×®ÕÌŠ<ÿ<ä X)h¡­ì_þ¯£ýnÄÙ]"lf§3‰z+FºdŽÑ-š\§íÖÆþ"O²6Z™I,+åqÍm²¬7¢’Ãt¢Ü÷– ÈÞÎ]–yLæƒsõ?YaÔçî´®§RkÉ× öHñ÷s\ý:¦m¡¬­Ù÷­ÐG­ø[pVi9s{!!E©|BÎc¾ÐXÌ »‚oDs!×'Näq™¯¥ñ§ˆ5«1Z‚½éï¡TàF5ûзÙ^˜óÙ&llæòÎmÅÄydpbæWÓçÓÃà‹Qøré¸4S ª£™+qa6}òV‚¦e Šn†­¡ç¦—¡~•0,MQáb"¢!J ˜ºÇ–â꽎ì¹8dà&{GþG[b$3…}v—–¹xÝc ýì ×êÕK$§¿ÎÃë©0éÜh޾±’®ôþKÍçéãLû™°"sþ¬v`O¯J“²ãXâ)D‡ŽÙ¤#ç2vHrë' ïåb9v~•QùzˆD¹ÙÁÛ¦ÕÜù/@Ãõ0—VèðÅØǚɒIs“QRhfú)Ò±ûl¶>~£*V듼À¯¸°nÊoÅò‹ØCGÖIÏLn7‘››C޽<»6lmÚÇåEHR„î]ÄäƒäȤÙfÜ]/]²ìN\)4w°;0ŒR1¥t‚7ï­òmÞ-I¶¾}nãOk!gú4V•éÊÖ¿Ò$ÒŠ¹$åY HÉN‚ž¯§ø¹¥ä‡mí¼›D¦˜ üé„oüѺYMŠ—ë¶X]Æ#³Ä/a¥ÎMY‚ÏÆ†ãïêÛôWà]ø·°¿D»±gMÅp³¦4§%àÃe™Ð[jÈdœ»øo† ð‘À ’Ðp vûkaÑy0ä÷wö¿‡–ÕÊ$e”£¦¦ËR±pyì9›Q‡Ã¸ÁVìõ“`¶jØz·t]T}òçþDßœ†tI1¦n¯NÖXàOÍÌ;5ÛæÕ SãsÊ/»Ñéµ Ù°Ê (ß„¿üƒqJ#¨‘b‰³u€²ÏA+×ñðêh ˆÏLÂô, –=B×ÎföžÓˆ¤Y»f: ŠEÃ|A\׊]ÓÝZŽOÞ — ³›ûÄIxÆKP­ŸŽg¦vÃNGAXuw=î,oç\Œnò¯úÁóNÊ€ž2Œò_à”ÌspcÎ) ÅmJdKxg!W¶%ï>â½òI0ú™$Õ­Æ{2Im½<„œ=LVî|FsBLXWH:d`…“¥˜E…6¹æ!G’vJ‘æ«Q§²: ·ÿ7žÊZ)*ŸÃBj»Ñýœ5™¬ZÉÝ÷D.WQÓÅ 9»A®k'4J<ëÀ¶ïÀó}:ñk}€5THSè$2Kà9:ǬÁÝ#w¨ÿqv•ÝÄÒ‹÷à¡Îq´>_¶ûpqÑ]<ôx‰]fCW‰àû«7 ÅEÝ·ÝÏqHó#Afú¢š÷ng}ÊÖ`¡½ŽzŒaŸ g²I†$ÐPŸ½™Á^=”& µ¶Áé= ³ n¤ªT³þ¯o.Œ:í°|4M»ƒ§—ó]»,èÓ“Ñóò$Ö·ÔƒÄ)Ú6(ýxâÂsYÔ¸:|ºë®™£‘yÞØN_­.‹ s ¤?†dw¿†ÿéøêäúîå´­ï.”ÉÅÓ”wÔ„ùHEƒŒñ8ms*|¼AÅâc±Ö漚uœËü'ÆÞ&¶YX1µÆž–c‡±ašùqÌFÛ¡&¶êTÂþ ܹ9Û}láØ ì0ÁŸ§îÓcJà:Ê‹d|˜DW!v¹Ç¡ßÚƒ˜^µ”-³ŸNCÊÚÃ¥d Urkñ[ÑMn‹°>a 3á·Ðü´.r­£¶^ˆÆ7pP|6þ]Iú:ÔÓæ xÌ~‹Þòž8¢NdOïÄç|Û°“˜,'Áœâ'Õ0¸J”ìÿ {=«q>~ÍA{æÖ „f+;!Ê_šÔ¸öqÉ—Z1êÇCÔÿ¦Cšü–°.Ûjœþã÷£PŒÏ”aÄ®Ëp.PcÌ– —Úgh‘»ÀÍØnÄýX[nüñ|’¬=]O¨ÂÁpÚÿ$ *þ@…&[­ÐMœ†¶5ß讓¢ðÊê5T­™»Íy¯½ç¼Éàž¦Æ¾ù©8œ²Ë­Y9 Rܳð,þÆÔî ¦RŠ­ƒä †2tšÈ6D‘”ræ/¶„ãÇÔ€_r ü™<ˆº:¥\Â…"ܸς’fÇ%dX¿YõÝ5ü·[âÈi ^åÅ{hæÅnÖª²3ÙM”ýxJ·ɳ¥¢'ðK|xå*°h‘+è¤Ú>»Ðg£šO9Š‹ÎçAV÷a~ÿƒpÔZƒs0£ôFÚ=p¥îÅ¢·ö¸I®òÎÍàbÐbqZهǙùÞ· ¥n?€4SA¢ÓáI­‚!QÜ/“·”rjê±ü»iç°0³N†åÁÝUË8¥ˆ—`þu> Íd÷6CF^8d¯š íÍpEÞBx'£Åœ¼×°>÷ Ýzø­9ÜÄ£Ú,Eù¦B–¸ÉµS ¼ÏœAÓظú*¿s78ŒkwC×Os‰`Ê ®kñZl«ÛÅtÿöÝ'A#úŒ nï¿mX­†R{–à¾s) jüežt@îw)vMü |ÕbßÌai.†0ú=ïŒÂJ±"îiƒô£6(z/İ=™vö¶í?ÂÕúá‹×,e~á0ß0ص‡DØÑ P²$]ø½·5YÚàWº¬=~~ëæökšÌâýäüô¿èÄ¿‚¯ÝTˆ…˜½v„è [°ÛÖ™tJÕr¬â~ýŒwSí@§PµqõÂ-1j{¿4qè{ò–\ŽV% G-˜ Ö 6#x"©6³B¯'2ZŸ'ÐPC™¾@‰¢Sðuörö±xù®0…´­Vbô ™gt1Ù1ž*Å“C¾IÌϤÓâÇ}a[м©Å¼º‡3ʲ Á£èw‘½ÿ«È&î!2³\É·´::ï¹ÿÆ™™¤ˆ/În†M$÷Þ`޲» 7Âk3ãe–s²¹ûȲÌhø“i e!šôÚùS ñ4Œ¤ˆzãÝ~sÆ)ZÂê¢hÖðP˜,8[‡©¶E\Kzø¬&K3@1·÷ÝRƘåè,[›ûD˜½5øs’¹·+¤Ù­šhöL|&›äÎ|/:CÍ6ñXñÜk°†¼Á„!#¶E9ä érCfêû[/ôºàÙº5äJ’,]_ãöÂܺ«_nMÆ m; #~73 Å”ï°b«þÖQ¹‚]‰gÏcì±[ HÌÑj&;·ä.ÍÂgâß8•«‡‰š6Àb8Ú•|>\Ú ï]Ž¡‡ƒgžb†¾>ž„LÚHƦ"ÿܾ»À[+ͼp1FºÕàµðdgw«ë¯õêc^NÉ ”ù  ãS ¸Cüùð»å-Òc–š‹ÖAãÑ5ù2¬(s`û»Ì©–DÓ>Óf&Ix:à),·¹B‹Ý1¸e kÉÚ€^›É ðØvãIö366øÊŸûð®ç1ðt;Š[Ñ?v><¯™ûÉ×cZ`|bâ¦9°kÍ2˜Ñp ·ÔÎBí›gÑ·g[íG=3`?ÝXÀ§¼šþàåüÄ>¨a„i0È+‡;»uáý¯TånÂ,‹øêä,tK­eÈDݹè`,Aö¦\ ±W8žq_¤/Ž·dW\Šæv?›JcÎ,BÁ¿ оŸI9 ;߯â´ú“¦±5‚2`cY¾ ”¦7#Þó>Èn§—×èÒÏk§³¥ÿò7M™‰¶‘;gƒa䉸?8¸ú~‹7'Ó§BßÉRˆÌ’„é/RQÿÏd.G;ž¿gËB"d¹¾¯¨„m[Ù ß(œÙ¿œ={üÂá¨$Ù®D4_âQ?†¿§)9Çëµ—õ×Ó}Ú¼;sœˆ…ƒ>ш·bvIݼ ³by­õÎXªÌKª*ä­ÿ:€ºÝ…\ªŽYu„¨—=çÆKsäü–&¹}ò ;R)A~-8D‡Â0þ}×$r"£lˆ«S,Y1­¢¦ÀÛ0vgÎTx»+ýWWoYRܤIÂõÝÙ¹ysˆ\U x‡F³Ÿ~&LÉcÔWŸ„K«”Èbã]$ùŸ v¯¯Âpþh¹ ÃÖ xåN.X”ÏZV_Åšr¤#™£\$?—}Š‘"%ëÁÓÁŸpRÀ}ÜHñU fzV™éé­ÅøÓ‹IÈÝ9Dˆ¿|< C¾ûâ™çœwÕb>Æbãiøú’³ag çp»¯åãóÜ(rV{.Šï?­dØ×ïÁ7Äžü%Jœñ¦ù¬g©$3Ÿ|¤GêÀâÂyêå<ž­½íKfX³¹áoàÌ©$üzwù½Å‹¿ó@×m2|®ÚOH ZY‡³Â-.,d\ãÿIÃf×µ4lëBvÈÕ–ð»Ø·«?yã’qíí—à´“ï@&Ž#«·ñð’?Ù ƒ¹OÖ0›­}¼O™Wà¸Áöùö~vÇȞƮÜêy‰ähÝköÛ}#Ø~|B'x¤ðr.»¼«‹s~ÇLj‚"Ì—Áïbž¸]8$ºtaFük–d¿6Z­¡'Tzs.ÊoAAÖ¢Y ù“{è¥n=, ©¤»ƒMiøòÚÿë’WèDGÿièÓðÚ„€F_-Ë™ÅNõ}å,½}ØÖh›Ûåøì%[8Ç"|áãÇb¦™Bî^t/œqÆføƒY0}¥c°ìì\.Ù¨Zk†³—eá»,K\½cž<¥KÁ´* ®b{3k!¨ëóÄWôÛÀ>H+’¡´7¸ÆÛ½jÀ'3s˜âTŽÕ8社ova8ÛuBu°ç`Oú7~ÿýx,J€œOïÍjÄÔ|,lkzÁåÇäìò⺺e™ÞÔïà®"V}u8Cã zÔ5ôÛRdùïÕ$Ù¢~Ïb7ÞVAGO ý±X”¥tÈÈõ­°i‡ÔÙFAN¹+˜ûç>–`**W¨óöÛÜ¡%ºÌåüœ¾°—ó›T gû YIÉÔn "Ò³Èö ×àÝÆÕL¯d˨ævžÀLg9±ŠÎ,Õ[ Ï7”áŪl"­cM%ïTµIÃ.Ž›3[ŠÝ¸ÄZð1'µ{%©÷ÿ/²ˆœ›©»0‰^˜ÿ$ö^‘ÇÍÎÜ>ñSÐ[”‚ÝnmèkŸa›€1œÿ±–ï2I =WÃkj•'jéìçh3Ù`á6êÚ‘C?xï„%;¸?ï¡môONaÌ\øÏF¿;Å\)kxº^…TLzÏiܽ€¿ú¥ Æ/¡¡×pí†à¸ÆŠm]ÝÇ%D<@i››\`Ñ"4›¼åZ`ç¢UX#Ññ’|ôž.Ë"W¶ãÁSÆŒ4â0ëçéwT•Ä0msX˜‚Îi¢káÍî¤õAø&9–¼â¹Ã›…E0kR5ÒPã¹¥¤Œ˜—ñŸ^÷L.àR~ƒÛÖŸœJ‘6y›w–ó¿t~®¸ŽÆwpl¬#¼4scby§¨–m,.ø˜‹' gáÎÃ3Éó=°aL÷ÿõÄw«±æ·ƒÐÙ Dê.œB¿)SpúòTö’rEg!=K‡}p˜G o+€ÕÁ.hí~ðw4Ò{»ÊŸMf'„ãÑX=yN^ÍŽcB# ¯Ïìêͷ̉{Þ°o>Œ+Nâ½´ixq­.yµã^~•‰‡ÚjAØp I5¬ƒÒqOa“°#¨JïÇ_V9Tðt$Å…?Ê;ðívA²K}œVo„ BFdÝâPÒ# ‘·áÍ!¶Û.‰íkØÉ&…¯‡cùŸh*ñ†·NáðoQâóå=ΉžbÁ;Q7Ò˜…¬#î¢cYkZ5~lÈö¹§³ig"éK·ήL…µ8¬€ ÚÃ=ÍþŽ_eöÁ† vÜöß'?gO†Öí»ÉlymT±0Àý»rÈö+9¶³ iw8,»wÿŒ¬q_7Ö7-¼oÔBÝ·ùäטKüw«cÉq%ÇÆ>µ3pÀe1^½@<‡ñ­zÙ¨=Y.ÊÒv’kêaÁDYâSù¶m}Mÿ½øG#¦ÙòÕâÆ’=7£úÕ~rü€ÑþÒ‡¿ËQCad”w9â›ÝTóŽ5”Å'rÒ_ÕQqW-¼oÏdŠÊðŽ®In0$¡[…‰‹¤!!ɼk¿KáaäE˜¼!{*KàÉØ™dÓKÖ‹rø³ìoÏö¬õý›§Å:#2Wýu0{l÷àGí"w° ©0¶pª1Ò`yV¢ìAf—ÊѤÈѼ’s‚µ¶@ŸîRR’ñN¸oÈ$Ά³ÁAmÒóIˆýwFôÍ?_áWÜa&:ég¶Ë†éf«’²¦õìÉ#%¨_÷'†ç‚Ë…8’]9‡e+Øá¢³!füTæûH–F}Õf­®VðBe}}=/\Rç1ï…G6 ¼Ò 7ý~Œžÿ(T¤ÆpWÖ§¡a-G®Fnåæê,]Ü“$Ôõàv§Ã\_¯8ÉhÁ/­zX+þ‚jf²ÏËW²ñþâà]¼ŸÏ›2æ_©àFD28ÝY}(£E k¡K“Ï]åe€¦Ó[„SƒïÀ—OAä–Óþj‰E;±·>{¹ìÅêð!Ôûª?Ðãí•ðkßYÈÞØ€½Oït£5¥òäHÐp›§LÌ—.FÞü×ðëŸ#{*c‰2!\ŒØmêU‰›ö@è¾äòwYæ?w)!åÑÕ>zQûwéñxR¦=¦8¾`÷õP¼vÛ^¿ Ó–6;üܲãÇC}µ‹ÂË(ó}9uÜZª‡”ÑÍÅ™y- Ÿnÿåý^ÇÁŸm5xòy3·!­‚ʹŸ[ÅHûpJñ)¼Z5–§‚ÙþfÖŸÂ|7:²(!ö}‚ùϦO`)º}Pä*Ö¦Ò}¿ ô/Nïî|™(/f≠X"LÎî’ÝÿìÍ®wÐê>žU.rbuì?4èÊl6xì#DvGÓ^³w¼»Ï•ðûM ò~ vj±.zžµÛ ֨òv¥)˜ëyžO?&Ì´eK–rãóhäÅs¨Ù`×w½¡o†ú!¡-z¤±›¾á̲õ é¾ Èûzš» ¼¥–žÞÝGõn9r÷çià€á´Ëia“óS.#U[+³d¢jè‹MÛÑtö \˜ú—”™àб“@£wSçõr}žätÇ=NnòÌš/O"&à/Þ#Ä6l($ýüëaó’E$á«<™¹³ß«„Á·_:¨T”ómqáOºh¼ ,m¤«&Á AùÜo¸(%ÌØLÎK †?Ö¨¬ö-¤Qd#®hˆ OÅ@©ÄZœ• 3î…ÀÃt°‹wØï³ÌÅàÉëØëd‚…q¡.Ï&Â1wߘ¥»ÖÆ·ÍgÑöÀen¢¸/ [ ÿÁ„{ºlû>vëM {4†Ýµ½¼yOi¡Ìz””r×o¾¶ylæ¦2ôØø†6òò>¤ƒ?\†Cß…`òÖ”? J¤ÍÖó?´Mg¥&;É{7qªW› E˜¨¦#º¥ýƒ}?W³ý'%Ù†K¸«`*™³d&HábýQ÷Y™ôé3À÷,ŸÝ€wnŒa#s/¨¡„à)ˆXx³â˜Ò¦ødó;zêp1¬4Îîî7x{]–ýةǻ5ñêÑ&\ó™ h„ÿ´É [™Ôö«Ð¶@ÇXBµ[>ìÕÂ#uH pƒP6ïµëX¸~à_èúv®òb}àíÌåɰ™òÓ ÷é5nZa‰Ûºþqaï¿#·H€ÅnØv˜«·Ü.—)¸\‚7µZÐ=î¤m ëóÓÐjPd^rškÉɈ—(é(ÉööBdÅXpT&<Õøã¶ ÑÀå’ìÐ!_H]%ÃTÔoÒ Ã4ìýÀ_ðD®ùYVÆ^ø³Be¸Û É‘÷{a›œןòWꂈ]n©÷Õ7¢ô„KÞ¼Ío’Â,IY¢r^—%Ñ%MtË4: mv «œS!=ÖœY>ùBµŽlçZ|Çü·ìkù¹ ô§3µ<#XóÔ‘u6ß‚~`jÀkÚâS? ÚQrùÜwZ•ͼñ戵a…¡™zð-Åh·ßÇÀ„lNBY +}ýè# o<¸Yýº¾4_Ç7Ÿ«.“Õ˜þ~-&0úzÙQ½Ò¡xAXÓ;rœ&3FpŽ„;Ö¤‡y7¼y!â[)Ý>]/t½çøEŽ´1ù<÷Øå4üº¼…Ö+dà#d…ç£?gšî+Ðjm·¼­„†7lCÞ‰§´?×g®w€óz è·ªRê0ývå?z‰±n'ø£u¸®öÜûÅíø6È­³ËxøkÎÊr;L©Æ…óyòc­ñ‰ÅN”º'|Õ³°wÝlLŽ}߬ÄÄ9cH"7¤ɉ´]ƒ¡4yNXVÛÏpöyâÔCèy‡¹xôº+|«Gû¿L§¯Ü§½d½Ó„“Œ~rË_÷Àþ!¶6÷9n ½£c¤!s¯ðçÍYŒ;ƒøËpŒÈmîøUn£ÚÔö^ˆÊå+Ù¤¿«¹%Oy.Ý“¹¶ûì@‡÷àÊç=Xâß‚¢«Ù#zìAÛT„Ô+hVpKG³¿ªyïÍ~qêºaùÎvÞ›½¶¤µF|UfÜB„ÜóK0)»Š¿m¹oÔ.žúâ‰çÈqk¢²°Í PETžEHNÂ9÷ô8åú…ðêî?XY1‹;z#Îõ²ɢlKõ+š0·>lãÒG:¹xó0\ ¿š³û"Ïîó,±ìÕ:îï(þÑ`cç5âþáZ~—çœ%Ñ 'ÌŸÊcDYʳ2¶ÛòZVÇ!¦»ÝX,ŒA˜ðՆœ[Ö±­ãø¼¶ïœÑS1ÒxR—MíagÒÝpÖ9[&‘WO/ã›Ó’¸ì”8+z #µÄHïS¶¨Hâ–‹“¿rá»ö.N—fU—HÒþ^‹˜ulÀ‡ùµ˜qd=Ó_¾ŠID ç³@ÎÝi1×ßù&`Ïò}JXêÍ0sz¾uÿ…›&œ…œ[IKÜNN5/»ËEYÈ,Avmó ŽŸýŸ¼™2£yÐP°ž¶Ù¯¹3dßÑxîÛ99Ú˜•¼šþIx‰™úqü_Âz bv Ã.ƒpÿªºÚ<†ìçàMÉðìfg…ÆCåtxhý›–ß<„-+‰ãÅóy·zT‹¸4ëzX¾áÑW|ÚæÚlÔÓ ðâ6ŽäQ½XHÊàÚ§ÆÉ#éŒ>WýÌ.|Vã[ßäËŠžm4ê:ÄâìÛÞ‰«×Ç“ÕpÆáª¹ˆ;¯,EM¾g_8ÓŒKPg~šÆ2&ÞDÕQ®áÑxƒ×«nŒ»”¯ó>˜®Â¤˜ÇXy$žÔ¤‚¼¯èlât#Å©ª±[’ I–ÍBé¶<ܰl·Th<Ú$Ês{bÏÑÈ QphíX6dô§Êp_z”H¸ž ùøbñšk‡)³Òõ%¸Ôz%e°ëˆ(«³´ÇuâÍôCø °³ˆ%¡¸†*Ü)n·¹÷øº÷ŒÙÐÍgxG®I·1󴔘¾êÇ.´>¨Äõŧðx⇞ÍÜ›h/x±mû|Ùw)hÀ¿©ë¸ ÒèŸð×)“=b¬óŽÔæoAƒ—/àóæ•è)oFä‚’¨À\öîóxW‰²a®äBÞ·àH)>JM§['é1ƒˆh'z ŽÚ ]^73²v˜â÷w¥pLw"ŒYºj²zya'êøšÎ2To­:ÑWnƹñøj¬8ÔÔú‚~¢= ¼øÇëøû™.˜P‚ß½¸kÙÜßBØÑ]„~»²Ð¥ÃokœçÙ`)4 ¤ðîÉ-‡A«h4þ’ ®áëqVøT‘™n~È¥­ÛÑe‡q:Ôc¯{œ»hDŒÛÔ0ÍI„[låÂ×öAÞ²c\Ww1ªÕ?९˜Á|OÄÐçR|“}Åt¢K4†¼çj‹MȾ#G°­D™ úzžÖàøO¥˜ã$ŶérLW1ŒCåÉåÐ:þ¡‰ÜÞ».;¾C[Ç*È.IÁ-àuãD6kñdFýÿÂY Abøx,YQzÿ¾N`;ÍØƒ :ø'¹¾CãÝ”= D@D>íB?`p²3)‹mÇ®‡QLvwßôv‹ýhN䔈]–g'¾oƒ)“ü™5ñ*ëhè‹ÉóWCÄcÞ™WÞì¢r(N§cóÔz$à¾#c0DÔ“­‚ÓÏx88vJÿçþ9Gâ\­ÜÿÎz†-NÇð³©fN!5=ÕðGt ôßj§ÿn³ ÷nÙB]æy¿“«4‰û»Õš…SEºû ªÃW'qNOÒ0Ñ8äk‹°›/¡;x|•~#‰þ¤$Zœ\µ‡i9ŒžèåLḠ¦/_ONYvSG=ò2t”ÏW˜à¶;» úÝr0•óáïlŒfrωÉÏ.ˆ”W¢%J4.Ušu©SŸ.jQ¨KÊN‡²¤óf¬d×q̪ÿíþÝ)Çt—…1ë,œ?dÃÖÏCÖôß‚ÄKGø,Ü‘}\N4T‘äAT>B ‚Æ‚ë6Ó#N&ò˜?%„à¢ÄRøù.¬{‹&Êáл¾$—p¹yÛÙàƒìò3>ìÇÑû8CƒA\ëq~ÆÉöÜü]= [ý‚š8ë¢Ïå0ξ7_‡_½®sB³ÉЬ%ç¿€•t-;íEVßëG­È,ŒÑí¥ß¶6Ó¯{®ÑƒUøâX ì(Žám.Êà&¶ÕÀ«'¥§[ƒ rO㦥ºh¼Î™Š‡È°ioUY²4¡%~#xo™FVÅÎs)\±'’±\ýÛêP/ ™ã²è_Xìµ&rÖ,2~ X¸8à®65²£m¼ÐŽAóâ7`;+/[xH<³j®à¼Õ^Ãdƒ8Z5Äü™©Ô^ë0·F;¾L`EèªQu¦ÿ?®;Ëï #{gïdK(Â{HHÑ"‘RÑÐÒ0öŒÈJ‰PŠ¢xŸûh+!Mikk¡ÒŸïï¯÷¼ÏçyûÜ×õù\Ïu= .ì¼ètep ¿rš< pÀ7ûŽðÛ÷+ОQ࢓n9nlÂ_òÉL=ß”åË`ÅÓÆo/kÈœ¹·pëŠ §^¡˜¿^”5IÎÄqÆŽÄÇÿ2‰œ À¥X𺞽ä,¿_†}å6øÑ^–Dù’+-{q©ò7¼ ?Våô bñ—‚ì&ϱ:à0÷ªêùO=T#J\kW’wêrl8×–n´å´ sp«ý Ù+LHÁʧ֨´R…nðfâçÏÁr‹-œPÉbld†;ÞzáèÃh1 ÆFÛp¢%æ|ýBfÊ ITwúûè<†­—w³{¶ŒÜF±ôh~ª–œ¹¬÷cX`› {.=Z;}9·ëGð ÍøSƃʎ< ,iDÃ}Fhg¡È·fqB«ÄXóÓlÛu¶ë²+ªF˜ñk<¸7DJ¡©uuÕ݃ã¿$âzg_WðܲÖ¿æcì‡h´bdÆ-gswAz˜8;3_csfÂÉ-«¸c%†\R²UÖR§¦æp÷•9ùnÌn"“2 d» üéb‡=‹É<ˆÅ?ÃàÆþ\Þh?<õuà¡wÓ³`wÒXÙoˆ)ϽN´«®ctF6‰¯ÌÄúY§Èå+Ø×pO^€šï¤OŠNqƒü7ÿy$r1=Pì§ “܈d;ÈÞnXAdwkãÁLò\äœ N„ɉ,$`ûy/Î?–çJÂÓ`Êt)ÚµC˜Î›}NÞg+?®ÏoOjhþïI `@ÿÒkÃf)úqÇŸ0ñžR|ÐÜ¢¼j¤N‹ûÖCvÕ0|)5 ½Ë¢øS‰,+æ4_î…CfÖö lvr Y.†ýÅ1 ÿ×L/¾ µì÷9Z­y .9bèK¬¹­Å9“e¨óo îÿ9…Yo²e÷”Ž3*YE†ªò?®Ç™ Wã>õ$Pq*ïÄå–¹æ2¹þª·œMÁñ¶„<°.ÄÞá2±`é_ŒC1ç™oÞyÂéš‚öJ{V ×væDgÜgÝ„h°ª&$üæ-w)¡:«cÈ ù•lÊàdºÝ¨¦ƒ" ÿûŸ7d>gñG%a|˜VÆ+˜ù‰w«/êêÒ˜vo,L¨‘Ã[WŒ™`Ðk£½ü6|Ä›Lh½ûtVóN’}IoÀÌŽtîÙâÇd,S¢úëo÷gó™Ýßø9. \5u@E÷>~öÕ`/DMÀ7RƒÝú6á„ãsUŸm=ò[”ñØG+8Ü9å<Í¡Éõ O?5çû8Åp¾ÎiäsoB ™íªULÛQ¤¼ÙÈ5<‘”…ûpƽ…DÊÑœ|ÛH ?›0”B¢óò¦ì<ŒÓ’ìhßûÕ´Ý3;7‹aÂæåôxˆ"^N|Ç[y˜L£ ˆN1fGEªIH¨0«Ê¸†Ó–•¢m­%«Û˜ÆÖ )°›Çè´µkX·ºsÞQ ¦I¼Y—Â÷™ÇáŸ0'M¦²ðLu¶7~noÝÍÃø«C½ŠMٞзdÜÓe”9ÄÖ*Pn3bJ¿–p ‹ÎÉ_¸ÏÞatוüˆc…Ý…þ<]^Tà-w”›õ„$ÄÐçc¬™Ú›ü:ÓØÙÌzF`Óò%èáÅúL»s:º!SùŽ7×ò1øJ)öÎÚF£Œ'³#ž/qÑÁ"Ì×·ÇÖaº²ñÿÌÙ>tX7‹ifHÓw÷ÃîŒÛ´ca'”t| I--Ïòé³4VÜ8͹QˆãádMó‚õÑlÁAÉ»öp?síT`¯·§ÒEvk`Õø½8«ÙVÈ\!WFù”@·6[¡âÃŽ´N£C²Jô¢†3 ~ƒ ìXwž“÷—Cn&•?mˆ·Â²I+Yÿé(ZD÷ƒõ‚)°uiúq„ù­‡Ò·GñÑÚ?2œ€GèTöçÊsø}Å‚v.ÿˆ—sSX̯Qþ÷ ÕLÕiÙ×›¸ûn6lèØÅV¦™±V¡`úxÖ{XxJÏìÞ¤“61ûßvôÏ„ñLÇ:¤é¤É*p„ÁæùI¤±ï$ûñ^f¶¤RÕ?šì‘Ù~P÷ÍáüV÷£AŸ6}rm*«Èa7uofëqâ¦*µí=úŽäqó…vóœÓÆÂ󎳨ñèÎOAµîݼthß_Ž›ôK±ÈÀÏ]cc°–‚e4áµ4O™ë÷Gã¿W物ûn’1f<­~Éà„@ly'†êmx+.?‚3âùø]ø~;µ×É'‘¶Â wlÑ\(ÆÄ–æ.-8 —ÈTÅ1ìÖ§DûÚ)ŽnàöËâE(Qmªžû¼Vê0w¬uZá»ήIƒ-‚tZµ£K,€ùÌä¾fŽe·¶Le¯•wðÚZAbÛWÎêH (â óNîbÞr¯õ:FÜÕ«yWŠûÑ[X;Á¡ºpÜ6Zs…Ý|è„…ºàæ™eÛa©æx*ù„Ûßû-úãñÀ–ì]®G-dÕ1ðzVÕjÁ!;VÞŠ¹ï=a鈓9­ŒÕ¿IžO§SŽÉaônµÅЋ!އ‰¦ž §{M—}þ]ÅoȽwˆÈn@gÝj,ÖÚ+Õ”"˜šIžµ¡9ùJøâ oU>Ymðjÿ:Z4XÁüòT»$I½-1Ñ/IŸ)»Nd¨KKU:-ÇÝ#MƒBÐMKœÙ@ƒÙôÿÆéçÌ Dg1ºßHש‘9lთXg—Ú¹SØõÞ}•3J^\wHñöG›Ò@òmÃzºþ=u0ª‚û‡ÇÂáyiø¹yùº(Ó¶NÝcE»/‘¥ËÿáÚêkkéË©ßqI£5 ·Rf2ޱðÞ2“(yÔñâEnÀðÊ:ì1_… ìäè£_»œöÙùá¼]5ìÇôC4àÊ:²åkÈ­ËÌ>BÔÕ:ÈóÏ}ܵÅÔgå¾”ÖcþÕ;×ðÇCW|›P‚åz%xlë0ôäçaHÐhÚdÏæÙÜá:Nï«oë@lvc‹òñW¼Ë‘ÇerP<¯)`r‹=l|ŽëÁçÌJÞ*V‡ªåü/Û·‚ð5ÎL×#ß–pK¿~Dƒ]`ÒÁHjˆž•¼Ë3¡Ã¿™hãÇs}ž #ŽïPá»6ÄÐ9k0aÌ-î[Ù3ÞÚ gx3¶Ë‰žŸý{ªaé•aX?‰uãyè>§_-˜I )Y§7ÁÇØW“{ø‚LK+€ÊýÇÄ)¦=a5‘5)ÉÎ-dsG14]ÖâÎa–Þ—¡mzœ7¸À}Û÷ãùoäZN¤¢ÿ:úRbSªó¡“ŒÌ@îOÌ8± N sF¾ÃDâÒC¢m~•Ú칎:sy&5âËýoñˆ¨-=”!Î$VÈ@”ÞyÜ8[Ó°¤»7ç€Ð'S6çÎQü1/®­ÞÊ—0=‚FÒ T¡Çš=ïß›²êYÖLxX„t¿QƒO˜à1tÕiâË+aD@V¸:± ¡Ï’*˼/ülŠ7A¯\t“bõz° ðq»¶•¬oW¤Í¿²ÙÐ Öã2]©Å­!£5h‡.{:>o†=„ñ»w’¾ðø s”MÜI£”¢˜O­K¹ÿþWCc´ Ù>/†öË[ý8Žh[2ç÷7q•ÍZ¬4ó¥ýsöÑWkÓèîU-8Öu~½dœ^ â_êÑ*4~yÿÄCã–ƒ»-®Ÿæ‰IÊìD’]<¸S¿FB®µ=<+ÿ€}»'£Y0ö#U¸·ÿ9¹í» ^)²)ç—à¿ú·¤+Ý?é\%+#ëÈ‘u3hÊñÐý¾ ™Úu Uˆ®Ä}¢ÿÓOóÙa>†<3 eæ³õÑÅΗwyÙ7îˆ[.ΗW 1NšôlI67!P•D푇c‰exaÅnT3{wû’/ÿÎBzÖhjy'¬gˆ¿oÏ„6D¡¥Û0¹fmÃ:ÏYÀógåäÙ‰,,¿kÎ&›}„ÿ´Ø-7Ó`âeen–ÿC2¦1,'€XCvûæÂŠƒÐ@v)„U©¡]jW‘êËܧáÜe$tíúÑã//åá øÄsó×rCiíäÚ¦@jÜ#k¼•YVÿW®eJ;?r gšHÐïFcمݲô²¢;¿*ÓŽ—€ÿ–Pp–Ÿ„Ú'uik¾'þS”UÞ±aòrÖìäÙìÕ÷”?ÿÕŠñ¬ß&zAÿ._~ø44KÖ©/ŲöànÞDL -BËÁx´~±€¼\ Áörô÷ÐW"2W椣”e4zµƒ¢Ñ~ºc—ô5e}ãû mÜmâæ‹-Ë@âí¨©ôÂyØ»a#¨(*±&Á –À€Þ$H)tƒÂ”o\×2 =hÏjÃÔä}A:Q†U]p¡…‰RôÚË™0?ݪ㟠{ÙlxðÀ‰­²Ð`öyü“;jáxßëîà'Z5¥–$(©€{µÒ˜9)ñŸ™¯À­¢6.Ê r(›ž×»BR?6²ïIáOd6?Q4™e6è393Uº}Å$´ùÁÀê¦$¬öO`7”ÛqJu$]UšÞýXÓÌ_8iu'oògYºëÓWLÚ`I?„±«ßÂÉs'˜þgG;†Ž¡'S£¾#î,lOœû2Š »2©çÈ´Õb?ת¢ÿå:0–Õ¤_gœ±=êlká'¬›È®µ× õcÀÖ饌¥HÂd^C¼ÔZ¶éÃæû2›XYxt6è5qÖ”Z›ÁáþX:‘4»‰dÝL„¬'`Ë©xôw=F¯¾¯D·L3,™%”Tðê<åÚO§Îûç°©Ôí¾:†=U3¦rÆjLËç~ð:Oòf/]Š d¹;ª±ˆ*ÉiSÙßø#r%uÑr­ƒ,çØöi1­;ŠÑ±GÀÚQ…·°&¼‚ýÒ…­Ç L—¬b\çjfzÆþÆ:ÑÏÖôÈy5pnHV!˦)®¸é@ûÆfjÛ{°S¡“ÆvâwÕ`ê6Š­Ó6o`Nã¨^þ ì–bÊÁV4ÎZ™.VExX„±ê,rÇ:}š']ݨ‰Iâ¦d»o0VßX‡;نĺXüŽ;KF²Û‰†SŸk+¨eF>j/‰ÆÇpÀk7»¢©Îò·–áäa?¶¾E™¦¸àéYVÄÜ<©1m5ûÏcy—þ&juYÎE¸äʯd=½ø¥ú6qù¥Ïɼá@äⱘ7Ÿ¬ k$¿TžÂ»”0ìhê'†¢Úð¦¡ìîRg]ÕVx~V#ž2|ˆ×8Ñ}41õBm{˜-+Dqü×À¾EòTj©¤U(ÐåÛ¶°Û4žè;Vêp–½(6`á‹•(11¤¢.¼×߯±ˆ>;ôð5éôÉ饤_ñ;8 ˜C÷í8ró/rÏóÎCÊíÏ8NC><:ÎùÌê⦽ ÂÚ l :QAÏN°Ý› á5¿‰Ó‘Û¨°"$¾¾!S§F»?bìë¤Ê“åÅïiu’M±€E_VÒˆýõèªë`ÇâdúëÔE´ÒR¦ê•¨Œ¯0„Ì? ¦¡³Dóháü0X©‚J?6Òì€ äH°fé$ö³3ùÒ¤½l#òKHä»ý8 9Ÿ¯Á±tÏ¿\|_*M/x]ƒ–i¨Ý‡4¶ÌØØQúéÌ–9IŸ>ê)¿&ÇäAyÛIðPÑ£o{óáÁ©õô×’8ïl ¶×ld¹á²l¼¦ÓÑ5#iAÌøg!×÷q·U›P¡·èƒº¥0²ó1ɹi‰²AýÔbc! ,.üOg 3ƒòPSt¾å—+ ×çfð.À ç©ß|ÓŒÞZçé›ãÝ(÷›÷³h7lZ…‡gœ‚¢ÔVU Í«õ™R¡&³qw§ÓNËcíÃ1ìåP—»·œ¯â&^ìðà ZºôòÍBZà­À.î9C jøÌjö_0KÏ…©.²Ô´F çJ)УÇàç„ö*y2Ëø@§Œ €úÓXö¼z…¶8vµþ/›³H‚i4¢+uÀ>‰X<${^ÈÒEÙ2LóB0}´—®TyLx—åi•ƒ&Ó¨ZB÷<[G­@]S®Î?—Î<³Œ¾0Õeî=¥›Á¼i´Whoƒíú…»D›Åxû—ÐÀ£eô[ÉgÜ·Ç€^ýÝ…×cÇgïÀü\ ø4—i¬äÇÉJÁ}߀óL‡>©¸íFvïêå[VôÀµéJ0$ÀÔç•óê2tø¥3؃iV\Œ½5 Фî¿+p€b˜»ÖAÏyÕ4²3r* ™Ñr¶0ΈÌçÿ{W°ÓÉ„çÆ6÷fkà›ÃOøkÞ<&É ˜y.f¯„^`Ä\evÍpÃ¥°Üè+l”râÇ:T<$ ÿ(LbÚýظõ®´Õ^ õ¶aÐ'5-ˆÁvLþ´&ŽJ$šK?û|"â B Cà)çàQ³Ž ¹[{ð½FJÁïý®³³’Ô„î2<Œ}€fWHÔ/Eêvc:):ÄÏ× eO]£ï–ž÷«™gùüº„^/dGú­¡mígrud?âņ¼BYü¹HøtïúµôóŠÜOÀÒSxÏ·fûÎ/Å‘/¸w¯j`ªm²¾ñ‡NÝáŒdHŽlwnì®´o;þ³÷>®€ðÒLB&ÒTÆ¿ Š'3Í<ÜÔ¡H?ógBF“˜ƒ¤9J¹Ç+²d£q¸ö›¹¦–\ÞSvá–5·Œç\]ŒiЕ«˜Ó Ÿ)ë¡øtot}쌧E‡pÚ½´±¿F>ËH3ËiªÜ´}„Þ,ðƒ¦ÏT}pü.¼…Ja4Ý_‚¾œÕ‹fA:à—(DMêwjQ͉¶¼Ã Ü꣸Yšp2xè=j?ë Å<¤Í–¯eaÌr_øÀ½©kÈ$ŸxW®Go¥‚wýñîÏupa叿}›är¿›ß}¯˜†Á|—ÊL%“øOËÛPðîS²ãäZø¼Î†Þ¹žMKЉph7wÝp]Xž »·˜Ãëa:ÿé0¿t÷}ú ûzD9»`¸WDnêqó_Ràý aé’*4S#‡W1« »ä™¹}öÕÔ€ðXj=9.Û¾%W¢â|6|a^ÖǧÒ0æº!™óH–¸.Iפ{@Ý”Z²ôἸ܋š­ÂÉ£|äÑÏä\ ~Ç-®$ý †ÈŸ¡'úx$¼Œ/W“L|>Xá,?C4¹£‡Å÷¡¶Ã‡÷hM¨›†ðW—º““n…0¿Ì>õæ¾ûб}“@å/yøÛ~­' BövÆÏx’À<þá«zIý‰ß³m.›õ~"U®Ž"åõw1a±{vºº"MPü˜î]-ĬɃ“‹íØã ûé=C-çቫ¨ÛSUúÒx4Ÿí5é»÷q2ß‚eó'u{({{n<ã²ísNãÜϨ‚»6Ä»,bû¦îaŸêœÈcít{Ö¸õœ}ÿ¢'‡ºpdÁÞ­Ën]]BsûÑì°ixàl;·2b –£±&!SÒB;ǯ|b¿eþê‹™ÄföÍ´ÚïU2w³#عÛä;nâú÷¶Ä;ô^Óugû[ù¯Ë .r5¾xŽÏj†­’R¤š!:tã,sZP_ǙѓL0së_rJ§]'&^1ìá7uf½x/Wõ“ƒ¸-JäÇœ/ðE»­ù~—˜„äF,Ú6ëþòÅ5¤èA‹(:õw ûP Ío}™Ï«¾fÙ…~lgÐg®{ˆ¡Öôr¾²¬¾45C¯ƒ ™’¶$ÿè=¶Ý8ö „³>ócL[]Ÿ-Ê)ÇÊÓܲ$YzÓ~ ËšÀ=C­+³/ìÍN_y -6&0åbôpá~ú½8omR§ãŽ}€ Õz̹„lSòÅdf»ü°}Ë!ö¬yTìhå¥îc¾s¡m:~ÚìEÕךSy‡G€"—ðfæRÚ"C]Oâch+ÿ;tOâsÏÌCÉÝÆlFm)¦£™c)ŠuS‰xy՚ɬ¾ÔÄöÂf½/¼n­L8²Äæ,^?-Þ’ý¹x=©Ëi™× ø›y»ŸýÀÿbÏéӰrï"º©i<ÂñËNó7Nކ¸_­¸æ­*8vå ^µ+žäRˆl—cΰ©6mÌt@¸s ¹ù=psjŽr’ k±Sª#[fSc‰åìÚò+xËF¦•æÀE¡Vü+ŽªÃä&W‹ÊÝšð,Ûšø\?f²´i0 ²Ï›ÐÇoð²A3d3½ª—ÎMrŠF©·‰Ü¼„y¨^ï÷‡ás9Ž/úè5Òè5‡ÌªÕçzÞÅqŸDa,ž‡»YJ8¹æ¾£ßàNÇJ<^¡N뮜åm9Î' åƒ_óVät½ÉÞÒWU&³—ÝÉÂÝvì…ÁT¬z…+oåâ·yËá[Ñ(ÎLænú[ããŸ8ç!âØÙ€ƒ½Éü6¥øjç"QàÊ [vÓþŽÖz¼fs îýD‡#~ëŸj"%«ÄŸk°?S«°Ü`FéÔrñ›õé¯ã¥T®Û E_NƇG>ó”·JãÎVlS×bZ1çvU&C¼_>à`û®ßè–ÉYÇó*ÿï£,¸IÚ©«ƒÌl/œ+KÝjÖ‡R+ª?k—)a9Ê£gJ×v©Ö™¦œcn9Ù(_Ô ë¥¹/ÁrlxÞôWT “Õ'á%é~ÎâH¼øòüŽÓdGç¶7ŽÒ¬¼'•$œ£Ñ÷â¡Ö2§µm`ºj1ÔÌžéôŽbS³·äýqY:ç–7ê•÷¢Üf~æ¡“¤åÅkÞY!v׺ü{°×mÉ…£«6Ãéôgܸ³ŠT¿)„Ön—Ä‹ F±ZC&,ÑY‚ø}Ýü4œÓ0ÊàÌf€ ·&¹Øp?k&Ó¤yƒ°îw>ݾŠ÷ßâ'8Ùh'¶Èí…c »9ûM×pGU ¹÷û%LÐ\ú¶#¿Ï¤uç öYÌLj‚gS²¸?Úôw 5囸©/ï“`;´Gy±‘Ëÿ} 7íH#¢æÏ@*W Âê-áÜH ÄßÞÃéþ¨Fwϱ\nù¾Bƒ==øgƯ|I‹¸~`-H= /Ø#™"”óÓa›;pëæÄæ©×݇·U{¸ÏžÛðL)Ç/^ÚÙ}?á³ÜXz¹Ï„þ(Bé s–çMæM> ŸÅ eà.˜³œÃ½@spß„ë8¢IKi%Hx;BÑ!¶~ÞV|¯³—;ì­„§N)ÒögË ¦Ç>V1 «&½æ{Z‚>Pè·K2у_¢9ýùÇàDõu|°±î¸7KžÀì'" ¾G–Öoè‚þ‰óÉ=Æß§îóB:qÌ+`[¼QÍ]4ÛóÑTm< ÊTE 7(ÐŽ%¶oâEãn^Óà ¸`« W#–{ÄqpÖrx|N°ræM¶h‡§ŸÚ1i¾„ݸŠ=“®¬‡ øä9L³¼Î»ã>›6èsmvf'\Ž»¶„âuûh0^ Ÿ¿éÂ_ ¼[ŸH‡ëe®è™½øâg2_ŽÖ5âšÖ)Lkzê©è³BÉ›Øú5 ÓøølAß+gßÛ”w]yHT·lþ°lÄÞûciFfxó¾D)PŒm‹ó×Y³e?œ1×ý9 M ZÝ]\è•2Îó÷k°z¢)?5‡ÈìWB ]/Øùl?讥ҡ˹ûE±WØœ·~ÇwU×YÍXlÿæˆiknp¿6wñ²6œÅÕA"Xöï='uþ(ñá¹B´ìNÑC‚´7@Å7´4[ ©âðܧ$2}„ݸ¯é+Š„†Àø²hרç¦9Íà¼GÖâö'›pC«žƒÉ}:ÆW‰-¶ØE¬ Ò‰Cc?JÎÔM’Œ EÛIVQ><>lì ösz*LsoĦÇà6)½÷0‘ç3på‰e`¬“D4ïŠqSœÑW½/a3/Hän;²„“šs‰Lø¨H. à{婒Ȥ0Ük¾Ï<ñ±ß¯€JÇqØ´v%þç íý°'Øë²5§N㯮Gx仕ºíJÉ™4²ÛäwZB‰ŸKÅXì„é´hUjªÅ’üïox£caãzüftÞMºCV6ѹ u¨{H(Žñì%ìü\Ð},îèE@G{&‘Í)â¥:£í*\iv»ƒJzšI‰(>O¹Ï£ôàª×vj= dzU»ábÌTꮾ—º?˜Ž×|óˆb{É’Ïõ‘ƒ˜ yflóÕ*ÐH¸ ë /ጻΤÙ~>ó4¿‰S÷ÕBü³Ýhä ËÖ¥áÔ~qz£óÊhÆ‚þ€ªø¬âvè Ð"Mø®CI?+v°~"M÷b—§;·k¡.•¤B¬ðð$’uy;¸¦­#àĘ2Zý ¥Àí‘:Ö çaõ![±o|7±-¶£oŸEíçâlÊWôÚ÷~œcè_œ ÇŠpÉÁ¥p"È4:noÅFø^Œázq²¯2½q1ß²`cëä`ÝÒ«ÜËJEV!:)Ú/¡j¹ -œ…`–»&+wuAÞû¥ÔÙ6úìÓˆ}ïü]™¥˜e‘‰AÒ ™W——™ÀÛzGØ"ø‚·óï<ºg¯<“¾ÃÙù¹J¥ŸÜúêd|n|¤¬Ç™æÂˤ!\3òŽ4=k¤Š|2°å7ùJªïO‡©ÝZ¨lƒòÓ!¦_–†µ4bŠ”˜t\ã6gQžë8p‰‡¥ŽÕøöÌ÷¦ìú)ð*KŒº½QaOyx®¾çýØLGúw°§SX§ Ë¿¥ÿùDo7ÅŠ™Ÿñîñ©ôÎðAòg‹$Ó<½™èÜãƒü%Kªoñ ?\Áõ-ÉÜT- ºã–#F«þ!¡A«ÀðúnºÛ øðG˜-,_Æ Æ– ÎÕÌÜ鈥•>Ltl&ªˆÆaG&wíXß8­•Ÿåg‡¿¬þÒ":l Á>¥ÉÃ"ƒ;`byƒC×[äî!YÒÀl•/¡˜w:¿tœ/üŠÿôÝebüoæÜ~ ¥y¦Ø+Bº²SïU(FÉ›á.žÍ áõ§p–—¦Ã21/rxM;¿úo>$okÇc/¾’-Ã2˜ô@ÖÌ_BæéïEýµù˜3ä„:‰ ü€ß(,¬ÍÒÝcQ¿ ¶éÎqi¶Ýç ü½&AŒM Ýx/8•óÿÓ0_¹s/¨£ŠÅ5hHxŒw­óðšÞy\ôÝ7(16äþÆ?'o£^…!{s¸pÍß\Lß0<Zeó)›pCõ9dŸ”=|³ŒD/žÂÔµñöoZEþ™²¾i ðŸŽ:³Æƒ+6ÃÈžó ù žŸa\ñ£h¶È"šj4èº)ÌôÎ'_O'ºáóN<~1+óˆ¡[• á§ðA8wÇ[Ù³s-^¤Š¼µSÅëDè‚ ¾Ò…r=ŽœÃSöÓ`àÄl{ÊPåã Ð#MäEhóŽÃXY§DV éàÝW%Ü %a\óY“֌ԗå0Ç»Ýö'n/çbAÊu)Ú6}2 Z8 ŽKðGÇÉÿÏçÙè"í=5|‹äü†)‰9äç¢cÄ5‡–n˺ä`ý¬$)œÝÉzÈmŸ{´®€wê|5Ô%ÛJã€ze÷:£÷…E¬&/›º9C ®1μÒÄj‹>@Ÿ…(-ÿ“¼Ò¿ ¯ÄÁ&' ï—• ås3Ö¼ÌÎ/œÎš„ÿ‘Ö=¸ãìº#¶ ו)áwóã˜å%K'‹êÒ‰?;àö»©l­fGÏcÿjÌ™ðŽ¾¿m(G_ÀºÚ6§¨ö¶Rku¹ó†•WQ:Õº¦º²u’—袟ÙX4y®±¦‰YÂLQdÓÄï;¸ÏQêT‘÷ž×=+Š•OY«nÑ1þX”Ζ ¯šLÙµVG&Lo¼KžÌò„ ¡ÒtË„½pTGE–O`GVWbký^[w˜,Ì.ħF"P;Í·yöÀ|?C|3y lÊÇÆ¿•xñßÇQnÿ¾ôüŘCÂlsÐ;Ì<¬ k›È¡GgÈ÷y3ñ±®<]ûYB}£Ðh³Nt 鉚´Öþ7!A•Né÷ŇgÛ9Ìb4«ùT–›b¿ÒÎ7×fF ˜\ê™Ìç0õd<ñÍÇià p»p(ÆÔ¦A;C@Õ7Õe"âåí5¤Ü…VôÉæ,Æáñþ%¸z+×ðü%¯Àÿ3ž}XÅ­_6nk"U™JBÎí$¢w‘³=ú“38ðö~mÅ[¹!ú£?òšíXš’ìkÂÈÀdhµŸCgJ1qn.œp‚AN£ü+º''ãÖ¼ÒÍ<ÇÕ/Ù—Yï¡âÍEø#ÄÏ}/ÍvD¬@îZ«¿5•Kœ‡·Rp*—MæòÆ2:a]ÒVƒ³ÃWâ‹äÉÙE˜¸oø ;þ/oøiWc9†üê+üÑk¨51ÿó†Ö?¥DK{‚ oÆ-”~UDÊ—ª°Î˜ Ú»Oœµù ã¢å»Ùo‹«p–€B ¬µ3…TDªp‘Ê¡ûÃüp½#¶çqfâj`Ùý í®Ÿ†Ê6EšÑÜŒ;«äÙ³õ™¤¢x:ºøáÊÆ?¼ÏµØô?üaátt‡Íí\½€ ;üA˜ÅÉçaÆøv˜þZŽŽ+§—¦UÙ†$lû5‘UúcÒ£Ó ß½ özDcÊ !{rWô”®aá6)<»¶ƒˆ<_sþïÿþ‹ G¿¯¥“õ¬é¦»o‰q5>WoÃûñÚÜ„˜-ôx1Nyy#ºÖ°7ïáÓ†­úʸkZ1¨—›rlšR=ùlI›/ÅYJÁÑõ÷<ò)—9®§ÖI€n¹&3(Þz~–Y,XØ®‚ˆ‰®œoûg}&ÿkóÞþ ô8²·]bÖ$ƒŒRÞ}E6P×>s–¼VžæU{ƒ„¡;]è DåD/Š \ç4›Æclª ýt*},cÙ¯ì=·Ïoù½ü4ŠoÒ>A]å7ðJôf>v¶ 0½¢S$$5ht¶Tø ˆÈ!RÔ39ÜçÈôµ ÀGIÆç°¡E¹W”¢­<§5^ 6Ž­ä\½Äà’ä8š® c¦Ör 6céèužé xÆÚN¹û¦/:C¤ªYû´ULüu5-9ØÀîìšÅ‚µÈ|¡¿¼ZóH®2‰sSæa}„ö[ßA µƒ'3+Ȥˆx`¿÷a‚[(¦Fä`ˆì|Òrz5 Ø”HÞª3‰pafø°Œ¯^'‚6kØþüxûĽ¢ ÈíCÀ;r¤À }ú(k Û¶Bˆ¾Éw`2³ìM&ŽÈú¥Wàj¸'q«€Ñ"xe]T}<³ÇŠ‘EMí¸­ ´VùR³]k ¶SÃÏ^°¡I„ù.§Ã ­øè’T´Ê€¹…}¶ü/×îU 'Ú€ìÑd/òkñÈx„Ê ­`%t\± _vq#ÇÞ‘“ç²ðëÌop‡?Dé;’Ö½ƒB%Á€/Iyº§¹Ð(cÚS7•\¬Êƒ;sÏ’ÀY²t©æQ|·|›hõ{œáÇ·ó|zºðE'þ°ðâö˜ mb¬rO=îsŒå´úUhð´þMå†áܳVäÿð'þÁ«œš˜D ½—YèÔeñ–‹‘¼ÙͯúæƒÝ¢EØæÒ‚/äÒpìãX<«ŸÆ•ÕêÁh².ùÂâÚ^î©»!æÇêd¢Èßè4:êc7Õº)¡ÏÀ~x'yñð/.Rÿ AÁðXe Uòû=þž¨´ãy™—€fL‡Ii-uê(NäÞ[9Ð¥·s >Âmûƒõ¶j~«És ѾµhØ|4?„Nzú¿Û‚³±ê«âMm{Æ-˜!GŨ£êž¶~6&Àc¿d^À8Zê —Á«ÖÙ¸ë1òˆÕÝ0ˆ9YîTþØØ ‹bÄÙ .‡y¦L;ó9¾nÎñÅI+æcÞ‹§ÜÚÆ"öWÆîºýL…œ·sà;×][îðöyŸÆ|E ¶ü·>Lý² Ä›”覙âôqX*o4wز x’¦œéžñìÃ]ºC?=òÉÅ5ËØOë- Ú_CÞÍ.úÈUþ mKÚº~@3‘(¸¶¨¢ù×G98øø ¶-œ†rä7ÿüKä)ʃÌú¥0ïK¶Àè£SèK¸È’m517a༕bí¬±)$ƒ:I<kI¼ÅVÑ9 7°Qn©X©C÷bi¯ÈkSgÇlpÒ¦ã`qМ‰Œµb#ÏdX«ñÚgzé‘<ãþiÑ·&'ÀõkÍ=äÏÔÏ|çnMCwÏäC„·?³êÿ y6Ypª}Öèü… e‘6Vž¹ÍŸ¹øŠ@RŒ%»bÎ5»Ô’´–_dë¼Tó]{nýÆ=[T™tßGò©H“GªðGq&Ü»iM¼›ñFN®+fd^ŒKbØ„=7É5ï98/¤“¿¶<»|ƒK¥ÝÄgÄž.3øE&k'rŽU °5˜ùØÑ‘…<Ørç$ïü‡Õè¾û9ü]‰¡O2q“ÜkT¸p ¸)4{o.<£‹1—Á6ù":ºœ€U`°>‡,óŸUïíØ·yVPkP©oµ9/:;›åF×GœÊvbWçUœ:ü%¯9ÁHÑd` × [!ÚÝéuÍßX#· ß¿IíËÐçH Kú xY)Q›wØðä.Vñmоç7'·¯RÐÛzŸ°KIjô«ÀyÞcÎ5ﯫM—Õ‰œÏâjR`{˜ÜJìèFɳ¥ìènO* ´‹«Üt ,Îã¤yƒÀ{SðÍòS6ýñ&®òf Ì“tbê÷çÑÕÉIpæYúÂÃ9FïÆ }o`r®kþٻІe¸íá–] `¯Z[`®ÁvÃ׃ŽØBâ6U‚óOCæÒ•C«6k³oMi¿Á xçnâeƒ/³û®FŽÞPf‘²NôåkWH²ù‚ó6ùƒy†¼ê7ø <²ÐÑv ;úêÆPGÞãÜ7ôæÝ_£œ­ õ"Ê1úÒkøeÈÑ­?CXgûÆû$ή}¯âE”™‹‡+ìÞ¼‘ÎîžGË~*³µ^Âtr@ tèÄ㤹F4Tñ ¾8é€Û{/@iÈZŒmª@/sšŒx&í"œÓ0C•™ŽÔ¿»…ÌõØÌÎüºïÖ 7ûæ må6¾ñßscé¾1æló‘w8”f‚Sܬºêä}‡ÿ7qð\<ðþ(&¯ßÔ‰[Méú²ÑóØ#®(ì³g&vàÀÀ²¨DŠ3ìPM¹‹ß·ç#9ž :PD·î–¡=)o ÝQ}œ‘H·цw|¸Á› =–“ÝqœÎªãŠ  5Õ™]j—dk ÕY9©Ë>‘yËðAùy–öXv—‡@ýzÏÙZ»P~ÙýE4ïÇ"ÑÒÿ|wÂQd\I K|–;W‡VÀ®©§èÍ+ØÅ™aT팭ƒ£)Æÿy?CMƒYóL–.6Ra7¾í­XXf&Aï/ãAÔ3}ì°MC“¥oAܹ v–ƒ£öâü“ÏW÷úGÕ,àVº:=¥W€‹•u©€_fï9Á-{½ÃÜîâ˜Í^R›Ë½öW9£qËÊÚPC¯Ÿ·2Ù–gD S¾Sx˜ 7aeWáw)¹Êe®5b^õeìcþ X}b/œ)Ã÷Šš”÷g:÷ŸŸóÖæM8óÓS®Z£¿ycÂX¿ÊoÉ­aI—¡±…œxwx>u· ³AâËâw›ó"¥ !Mf-~Ë}Æø/í`úÈl²^=ú´5ØŸÂɧ*€?s¹_‹x4—=ƒ)ï¢Ì£“_k‡ÓÖ@‹LÛ{.)úøÇzTÖè:\hL^ç§W˜wä/¾ßKmûj°ë}8矡„´é‘S'aâí~›h-ÊjËÃ, ´_Ò‰ê¶ ÒÔÚAü¸×Žö ÞË Qr˜*%ˆ²;^dïØ¼ýU}š+NÕGvÓbÔH /…̇Ò~WöE¥„– þ¡Ìl®ŸÃô×4¡¤ÆC0IɆ‚…žÜ-‘6vâ²3>ñÚs[ØåªJй¥ÐIkûn3}ïÊëØ©µ¿1åàí»öìÕßPqµûÓY€àD§ŠÖ;ÆRã 7ØÍ“Çðõ†&ðm_ ڿmìÌàr=z„ÈÝ-†u'2áCÈxV[ÅJj#ð÷yºPɇ¬”9[éôúnat˜¡ ËucèªÅ/ÈçeöJl!F,›ÂÆDfªow±Ç;é*ßtþõGBÔÖ8Ìz{«çÒóÐÙŸãáèzºçR9¬4¢›ö‚çzGÜ Ý+]˜²[<¤LšÀ7t2¦¦'cpÌ,i–´Óœ•µiÓ¹àªÎvÚòç9J&‚¿ù’ªl„cª™õ§Pz11ŠwÐ|wÐ$˜#“BtTÄ!ãö:f¹•Rç3aà(ØÉÃeÁ¹Vл~™ËŽß‹UÙQ,ì—|ß3 “nðcý‹±éæ"ÌÞІiãºY{ç©Íéô¾sÚ•o‚äôeÞÚï èh îŸÝoxyÜZvIÓÖÞXRøf{hÂ'—¡ÞôyÆß= G°7ä:î}ÐÍ1N“ﳑÇΨƦ} 75ðy¬²3w 6³b?|pó†™ìßÀDN“VÜÆr×YP°.÷ô[€Éêÿyx&ÞàÕ|ŠK÷ 2C+c¼³Ý‹œˆMÃuœèÕœÔ->Ö¶½<Õ!,š}}F 9¡ "©«:<Âÿ×ú’$2a…@"¯Ìašiô¡qT#éºò¶ÉŒ@Qa¬õyŒþ·éR•Len0O­D³SÛ±Þ΢çí ojÇЯ@‘ýÅê/©à×”Évxÿ…K+À1(Ž_¾‰¾»,èm—+‚ZÎbnáÓ-˜ôIeL©Ä\ ø~Ï„V¬R£c‚'Q·O0Ì+ &iýà^¿ùÀ÷¡6g\Á»Õ›grŠ‘u8fâþ˜s`dòŒ{³VíJ]èÒM‰ôÜ©p6¿}qŒêz<ö© ÔBãðÓ*L±ú‹­ì0v³,uÉŠ.C™~=³‘.(KàY,߈Ïõê¸Ðoãè½AUV2øŽ Þ²Àð?]à5‘)—Ì¥ö¼1¬ívÙ–ÚŠ¿>zp~æCZ´¹–\ m¼\E˜ûyÆEV&sÿ}¯>:OR&bN^Œ'ÁåED~ç'˜ÐâtaâÂf›Ãpµa1ó^)E¿%'g^Σ±ÒÈÇå¯Hj¯& ¦ãq­Bb²Ýg™eàŸ‡s¡¸`ÈxŠ>½ÜÀ¦›œ{†=æXµ€Ô¥ãà°ÚuÛ»á&õ´“V·öûqý÷ËÜ$eUªQu ¾ÆO€¥r@sÛè~Ø$á+îä>7ÀõMý¸¾ØŒSÈïuº]c]2½°æàEhþ]ƒ³VæÓ®Ùÿ¸ï5Í &RåíäHT1Wéœ ï¶¶`\ÿ6+Z §2|<î¨$ØÐ¨òHTÖËî:üñþšdþøO«Ê–¯‡Ÿqçé͝\؉Ç\cE'óeVô‚T>g½þ*XomÅ;í5 Ô5þü‘§æjlUùlšQ¡OùÓa×QøåAlÒŸa°úA–³k?n7ÐcŸCía(Æ»  Ã¥:®†¡uÜ»¡IôBÚ:úÎ:—¨m¢ñO±JYËÎ~òèzKï¶À_Ô·œ¸Ì„ŽóhÌ3iÐ9Çã³B¬!ã ™"x’4¦ß•²p¶bûÃjâ|ØžŽ¬Í ¯·’ç­ð¦Ò•½–³cÛËxÛØ)zÉy>l-YKÓ<¸ÞŒzªM¯·÷0ÃÐbÎB}L*^ÃÓ¯L Ï¥Ðîû $Ýš©¤¥¶zì>ºw:\Zh—bA¢ò¤éøïÏ^Te†±ç¸´º¯Ž_|qÌ@#šUP¥6Yf‚Ù—ÞcHÿ,èÔ  öÒ¤ÏÔý¯&=~A’él·§ÇóÙ6)5ø®K­£ì©¦1ú3cóBóù÷ØCòŒ¥¯ƒ£Cj4#t!{#EæóÉD=a˜xðx‰V࡟Üìo t·Z_PÉž¦ÜÛÀÉÌÆÐütv̸{ŠéÚŠSÜÍíYì]Þ.X×®Áî& àï³a^ÖTd':ÞÈVeZï¥ØºQ.b3\‰î×®àµÕYÝï8z¸ ŽlYÎþ:=äj<‰šÊsì³= ãÓàò Ô1•¡ëè%l( #«ó±g.ªè‡S÷}ð¼]–î\k˾^DïWãèË©7¹öP(UÚ”òFô§°€v}ÈÒ6zæÌúÝVµÓFÏ”&Ýoð,¹¦ÙÜSËùðÚ事°¬ˆiة٠/„Î`Jl\N¡ŒŠ~šÌþ½²¢' Á“œ= ªÿ ¼ö_àÒÜ•é%Á$x(À¸ÓÒ á} òB÷¡õK3 oÚËA{,8ʡϺ«àÄæì»*ÌòžóC´]éÈÄ: ÊÒ'æ÷gÒ1«@ùú:tÉWà&a—`*þ< þ—x:F$œG+÷Br챵ͤ×#NÀˆÖkx·¡¾Ôþ$›ËÓR¼Œ§Uðõ¾§D#5¦ •@‡ñD2h?›Äïc☠‰i=<ÿ½.^²‚wcsNùÅy³ÌÙ„ÖDh8­ÎtƒÔÙƒoFLsþ9nû¢3ô_p26|¶„6 «ñfàyN]W§‚™O,Íü>—uúˆB¤› yº‹w×ë:*í¬$.Γˆ¾ârÜŸ:fKÛÀJ1ìNiÅkvãèYÍ"´šÿ\ø–$by~7ØG„…SWÙ¢åßAik‰x}#ûõ *Íœü‚–äv¿ûpcj¸¶p—Ö׃þ¢¸ªA•ž•ëu¸ôd Æä˳÷ÏV0¥ÛÉ`µh›rþ—µ&¤_œBœ]>–¾²\©^Îõ#~.Ô9>äÄø]¸õ’¨S';F"LäÉã !úTç:¨Ì±àÍ ŸO{÷¿&GÆÕp•öZp?F™T©=áž9ÐÀk¤ùµm ‘Žd»B !ù^°fãßmáðt t wsÁã;ñØÒ^²¸Ø’¹Ÿh‚Üß%\3»âôBÕ„Xd<ƒ‚Û¢ðe¦|^¶ž½+9ŠÖ{Æ‚ßÖ™àsuÃhÍáƒpùɸY3Çøøà÷(uê$ìÃKí¯€Xž ½Þ Bgìió«jðê^ݼù” •a}¨Xw 3‚ÚI.–s‚CŸP¼9<lÅ—b«˜ºž´=”¢O4š!ÓÒI!fìÉ QŠw⢠.{*p#äXBû]}t¼¤Ë¥µÚ3ç¸íA5u›nƒ5çÂb[Àþ qè5ï)o~­ jLàÉgw]AöwéQzë‰ÖØ=¶ Œ“f³.¡y˜ñ˜òWdœ*Çš%ñh7ÝŽ (ÓËU.Ðldˆß»aÓô²hi95F=n>Ûìð[nK Á2*7Š){GñσÒ"ø!#Æ_Â}ž‡8³¥¡`2;…ÞNœË],›…Ç·Gã*·+õ (¹&@xý¦º,o©ÓIQÜÆÐ±ð{ánÄü4/±]JH™ãà¤íÍÁ,9–²þ+·õµ=[FìFëÞIá;de(9å¶O= §Uó²Ð}LXE_ÇqoNã»Kÿ@°6çÙW¡ÑwYºtèÌH9ÌÿÒ¯HOn<‰óë’¹Ê]¨¾R–®v-†¨—.NËÛ¬Á¡z€«\ûå÷¥ÛÅHpxÔ~=…IÜy˜ß4,¯#NÖ“L$»q× ­Ò™/„gcá®a®>ð+·j`,.ßåÉ™‰DqÓ§Gã÷ –<Ëyê Ù°ŸwI­Ã"˜]ÇiXuamjRfb)(åìŽ Û›Q9]Ò}Ù`r";üOŽî˜öØÉ{N"O®¤OžUe>F·àâd¶Nâ;îR@ÝàB©0%W–ÍáÞ,éÄ€Ÿ5ÜþÐo ÐJ”¸¨pQ…Êèfˆ]üJÌU•§_ÆCÇ!~óçìpºYM© Q*â7ýMÄà§ Ümy7NC¼Õ˜‡h;¬‹þ×¼x¿3š~v mI{¹L4ÔÎ:9 …xpD°<—6Bó¸¹+bQ¨´ˆW§“C*1¼eÂK謑ä>wò4½§âáÂ!®}²Þý9È›ÂB­jizBÚ“ºA6W³çÄüÉÅm7xø£ š¤êqu`n?÷…ÝëaEÈI-‚ ¡©­8À5>Ô¤/ÿ‚ñuªl¦b5lê7@é›öxÔù m4„S³äñÄgÈy¿òcoäRv×}>j< q9zàÝÿ>M1¦® ·‘ßÙWq®álLJ¹ÉýßüáðGHÑÇ‚)T0`:=kWFîZb§ÍŠ\¾£ýüxÂo¹Ã™­éାÝ!qXbBLJf¢¤ª P: ¯œ#QéÉWY²îMñ†‚¡|ÜÚþ¨y‚ìî 6ÃrIê,{’”¶ ‚±·4môõ‚½òO¡u4ǧð¾à²1—¹ï®æõ>jdxݸ¤´%Üã±1ü ?ÿç9òtzZv…ÁI‹•ð57/ž­!_n °Ã"?aQT`V|ß}Ÿ»Õñ×éNí'³ìDØò4¼g®ƒ¸ø|Þ”®Ïyé,@µö%«7¹Ÿçâ¶C–,ÆÕ…U ñÿìʆÝhè«Sxÿçkøp ‡Ô3y];B¸%‡`ÑÃu°èsFne÷;a]›Þ-éo03ÆŸºÜ郣Êb4±ô ˾–‚§—œ‡œtr£W¦Œ tÒ$¦¿¬7ßâò®¯g_WÙR+©@¹W˜9R„ûX‘-ï>sÇOb¯ï{ƒ–”Šüô|Ä Ü7Æ6vlÎ O¼ã1„VÇlxK·“¡;¸í…2sÔÄLACæŸo\½ÉÏ»ÛyÒ¯¦²©7}aËiMÎ=1œ÷ýÒR~Gf Œ±tBñùª\­… ýsy?ÆÅZ œÂ{2/½‡[³ ØÅÆ´yyV»pKߍ9¨k\ ¶çàËlYÜØ¯O_¾óÇÝd”' L&{-úø»¾\j–K¢“¬â;cü-ÀžU¦V'ûÉö1”/ÜÄE''q×֠gö,ræ˜(Û)z æèM…±ªÑ€BÆØ·k¬”ÞÊ-ÈÛõ5f” ÁÚ•Óé[]Iz"Nž=üu6”=‡–Ðé3U¢ûÉcß0öwÒ*¸VR€'ߘ€Š˜Q׈Ûw¤Ð+T·-mæºÈ³ÙÑ÷`ñÃ2rn>[„ªãøVun¾ãöý,‚Eº¶àa B_f§óÀÂÅc1–Ô¢Áظ¬s7Ÿ€¾yN,­ï1ÑŒ;…Z \1ö¨´û)Á·z] íEéuÓP§H+,ž_k®À×QLpdW,4†ÔØy0nþ7rƸ†]+ "x+ºý:Í=C‹î}$»)²Äö{äÀ]{0oLãj¬ë±Õñ.¾?!®|çŒßG˜v:òj&á°ŠTz~å\×)áæ•‡°xÿ^®Maí zsðõ­l´9:–vŸXAs¦ãóC3éÙ1<¸t|wãötòåïuÝ.y}aðD^€öþlÄizn;r¦ˆ¤î·Šô£I§sd •Ö9…ÊÓ³¡OU™Û tïÕ‘÷ßÂwÃøÈü"XN^Åe‡ãöƒ™»i]¹i"}±+Kæ®Ä­ëÍas>~!uØs}S¿¥…¹{Q)0tû® ¢ C 0B޵{Qc÷1T}‚”k€9%Æì÷ë1즹|0Ž;xŽû¶í>´o<Óˆ ÉêáÑ0Avap.ì­çTéÓ‚óUب0•æk^‡”ŠbT¤D%ëå ©:|ÇDÃÃ;øKVµÀÅïÍ(ñOÒ$\É~eS–åb®ÖïÐv£4ý¤gÕëaŽf r>Ì_•O£ pã«8<¶‰â¿ -‰ P}`ÿ·X#dØïDç]ÿ äw%6X™s.¨4¼¯MI†ÙÖþàØé½‡\¸´³}xóÉV2Ûøþðb¿ŽkÒ")+¼³ ñ×+u˜?¥‡›” ÈuüøDj2É9g¼m.Êä®Ü†ÉÞ†Ä 3ü ×±½ûUÉÍù’4àh.fND1™rn®âv­hU9Œ§wóPïµMŸ¡J%CnÃ6=¸¤e½4+ˆxNÞ‡öçžÂ˜ÊZò}˜käV˜"‡wmÍØ óÎ˺øN>Tß±”?îY´ÌÈÁØgC˜šó{ËEYˆ ÛÖo'N¥ƒpÑÁÅì|Ï{X9¼÷u ЈëÍX(¢€rW&ÓÏlö²½’2¯pÅçD`|ã.Z5zÞÚI~ÁBáC¸˜(ñæO‹Š·Òx¥!\妇¸Ê'›/Ùô”gù+›«ÐxÀŸÂ»&ý3ТÜùV·¸êé¢ì‘ô,îælæ™`Ávoˆ»cràìßû@޹§ÂÏâ¬Í‰8OÉ€ núŒ- ïðÊ ºƒXÃE•`|¾6ˆ{û^êÞCíã‹°Ë…oÂ÷Å2Ô¿Á„ú¾qã¹¹MÄï êPg+ϲÿÕráÞa½O‹žp8 9ãö‰«9x,ê>¦ìÓdóÓöƒ²Øû¥7߀5qcÐöµ ×}Í€=„ÉS$Yš%]”…´w®¿+àm´9„Ÿ»]Ágéa¼Ü,A˜áÂNø:å ®îÅÏt!æÔžŸ¬Ë¸òQ“ö¯ÆÉÝKP6ÎŒo¶Ø€¶,v!gäÇ‚Dy5ïÇFCþ¹ýVpú2L»—‰³SF>rÝ.‡à¡g+¾ß/@þ-òx†ÈïÇÜ€(—÷®}hÖõBÏ/ýU€z%ÚIÇNÇ 5GÁ!ÀÖÎÝÃD'ê¡›€ý3»#òÄXÑhÝõl€é“Xòi6=pç *Üì©wèšrìfÞ@‹¿¥è¸rf- 3X?žæÿ‘ä2Ž|ƒ; ûP¥ò%öÜ: ùŸHãeqn Cøg'Âi—•\W@»·žçÊý‘b}ö(ñ6gkõ9u[z@x"Ý\8ŠWr„˜ùÔ ÌÇ2F¸e‘iàxÕŠ:ò«ðJŒÝùy/îJ)ÅàgG˜ˆÐêå1– ìø‹Ë_ rÑÕ‡¡ÛT”Û²„³ÏŸÅãé?y¡™M 8qüfÓ¹'â²HI˜?Á ­LÑD ÍK'6—µ QyÇim¸³QžO¨‡‡7þ‘Ÿ|uº½Àƒ…¬O±àî}GfxÉö´<õWÇÈ´ÍÑØý7c<œYñ›=´oûtšÄ?‡a~­ä·J?<Ü–„‹W·ƒ›ƒ3ãjP`‚Sÿ÷‰{^Påm¨ª\ÈéFkS«+`;V™…ŒéDz#ËØÆR#v—‹à"UšˆæEc\;ÏW.š@¶¬AþÉ™¬‘šâ"ÉäÅ󖔞†7U8cþ&X¹Aô(æ¾€S–Çcþ’ã˜ýî”=CûnX£ZòBl4D­J¹zíŸ{é8ö±·’'—¡âÅ sdÞ8‰…«èÔU ]B,²k1Áv+0©|K*aàç_¬/æ‘A^>Ô­Ý>úv¨>¦¥¶t ·=5NÊÊÁ9[=Qt\? ÁU'vÒ#=¯8.^œúˆgî)¶œÆ¶«˜ø1 ¦ˆÒ’fyˆû]¾=‡•ila7~£þ^q¸;6‰ißa›cj¸OfkPMî)݆S›xðçˆ6éÏêÀá²zÒšNƒÓŒFy°+]ÖÞIÁøu›7«Z,ÁöËÐUOŠÐ'L„ͱÝBã °=;­{~Õç¼ÄCªÚÔæ….\0Z‹¥ËÙ|­ãdÕ‡ÇÓ^ÙF›Wßn…”5ình)Ô~ŽA±oó¡±^Çqÿ¸X2Q–ŸÞA®n $‰Óbæý͸¸E…[¥ÙÔ#‘Ìö‹4½¬'ÈbÅ`¼¶*7úœSp½)SÃEúÂÃx»×È€Û3»ë´9wyÝøQÓ¬9f{9ñÏUçÚµ>}»5àéaOØpÆ¥ÙRg!õè¬B®çæßTbBŸßq—ÇÑófÑýxjj„„cWfœ×b§äý1Ùõ°e )sˆ» Ìæå­ƒCÕPŸú—líˆÅëáßa@w ¢iŒÇ~xzEzý±7qŽöÉÙÛz…ónrª,±d':®Q}zj|„Ä+üæž™%ÃÜ˜Õøg!wÝô2I ‡†íÿHˆ§*=°£cÏ˜ŽÆ¹FúMr¡súYŒlîG…‰l*ÖŸø@n­€àáPÞòúz”ZdŽ·/‘sôj†ys&’ÙhnHv¬.á <8 Ÿ/‹a×ë»èñðn÷¥ßª?ÃâÔ*>µ)dž¯ýpÁ,˜›qg Ûø'€® ±£‚ŒøoßâRÒÀ‚ÑZtÐ÷>ç¬Vc‚ê¡cv¾fYÀ§²4äõgrvh;[·L”fÍ“f~lâ]]zÿ'Î9eG¤yRÞß5Ukš]Œ|øžC è§)ðc²+÷‚/;®“üà *y²Ô3îûebMÖg¢ÿ*ÈT·ñþ=Ó£"ÖŽÔ_5…¿ϺüĈ X_Ü‚þòß#B¯²,©Ö +Ža¸Ÿ÷óÜÇp¿›MFêmœö%!¬ìU¥ûû°ÓGºFÆàÍ åà+nŠCï­Pwvžçï ï„Ìq¶¿¿ŒmóZ@ì‡×cÃn6¼J›'ÙýçzO‹pã€8ûMŽò=W‚Õ?¤¹9¼§àÓ5‘-6ìÅ1õÊä§ýc¦öZpåçœÐh«--Rž“úçÐÀظѕ'ß*mfÌÒê< kùޱ±8ËA“¿bXžùuž¡Û#ãÇ-+\MSq:‚ZT´¾€Ø°7侺-Ü(šÆ]U¦çâ©×¤JÌ6­âRzMhfÔkh–h¹ù‘;i•Áw7Øßw. [ço¦û~oB“K˜¿N€MK+SKÔh~âtz$V•f=ÔA•Æ:NÞé"úÝýÁiÝ ãÔ®ÈáÜ¢,XÄýL{ˆKÇÌ Z6pÐ?‡Þà⥟ãIõL|'Ì{íÏ@ª 5ÙÁL½†ÍmÜ­Ò6Ò¹Øç4˜1£½RÌËq#I»P&/ x‚¶êgO¹ÓWÒ?í³Øõ½³á¾s^Ñ…GŸ¥Øìusè¡m?@àb3¬+áojœŠ‡OÑûÞga·¾,KﻢC¿¡öZî[nK¥„>p^Ž_¹n <}â(|pB 9mfõkBÓ}ˆŽ9œßÙþ&|/µe>¶a¹ëo^ÑRÈ™ú”ôlì%;rµoƲ‹Ï®AåŠû¤èƒ*çd»œMðÏà’GÎsk3‘3âlc>ŸLìx‰†³¦C¾¥5¤8™àåSKXbæ 8·•‡^qÓÝvà¬A)Œ­¬Ák9ùe¹$ë™ýܫĆgþâîˆîaA+Eé¿ RôõÎ1,”¿&©_ïš²hRùuwÆä1v´X)™È~Ê¢åHºz¸[ÅÔ{ÆØ›lN®ê=å:nŽýkª¾Ó¯K~ŵ«.aÙ±4•³¤³^Ìb›íˆzH5“¨Mã¢FË£÷ˆÃŽG0Ǩ–üËfënÿÃ|“¯(+&AgÊ)â%n9‹õô¦;ÛžÂËõ,ϼ ¼døº°no¶dcEnaÝî!Ø:"ÅÞ¬mC§IÔ=ÈKJlÁfõ¢»[× n€žÉ¼ƒ2t¶2„éÅyüŠY$ä³tÿz i5Ü¢Óë8žÝ îì8-Cü¸$c2 t–âA#Y¶3°6ÎòáÄ&àî}¦,gc'©»þÚ¢ÝÈY1S¦Ã dªK›ñòÙéôÎw'0u› ÙcŽà«ÁÓК¡ÎzíÝ¡ïe°Óÿ¿0óº!ÞKP}¥r8b:–þGbÅ͸·ÇÄX ç1L\Y£÷!ÛYt£]wón*ª­–ã6h«Ñ/ÓaÞ¹^h°úKd­'…(¼®†Ë.Øgú¤YÝ–¥Ì´j-¬“d“N'¡žø=Rú/“KkX€Aã×ÑeM¶´ÚmèŸ9 /“ª±ÚíÿE¨‘N‰/¡dWöL9†¾ãÞ=?ÏÍ\…çT¡A¬\–ØŠ{öÈÒÈ&)èŒuöð×äÛî ž9/ï¥9|ÝC'-ø;7‡γg¦ç2Ø‹•ºÔ`å'¨Û0¡Y ¼…ê±àbËìÞñlšGyæ¾½xw»î–YŽ[?©ÁÏKIÜL±½°p`;(-]B›g °ùEoù Sñaj8õXiO¯ÿ™ Ooë:/ªÀ‚i{0ñ  }u"žhÄÒÿÚŸï:±3’ê$k§-8t'@™o'{¿<û3»d·®Æ7´Y­·×—üîœC¥ÚÍÀÜ °¸«ïHIcú©é@ ¢÷ò<ì ìÄy; Øb+GÂÛ0 þ}MSµð͉åh&kãì–³W(f_âKï [txmJg˕钓4hòjÜc#¶ù lò7\ Ç}¹_‰Õ¯Ûù|îÐbººÚVü‡Ù̓Ð!X ¹Ö”{šÍiùïã>ÖÃÞsýð·ö $NL'.ÓfbÏ’20!¸ufÔ?^ýgf€µ÷Kœ±½gE“9ÃXêQFK¿ò«J@ÔâŽ1vƕ޽(Ö´†éµ4âͧhèû‚7¿ç:<Ÿ–Dd¦°1e;èö§[Ùï&ªôÕ‰ª_B„FîáÈö>®¸M²?LÂ#e§@FϤt®e^ÑZtƒÖ&2Ù>½¡µNÊwbÙÁ5á6M¤Ñ#ñÒËbÌÞsîË„³?w‰Øë6,ÓN¦Jcαð©Š4-°6EŽÆ_ëG¸^Ä6ü«Çã¦,¡'—½I›ÎMú˜Á¢„ƒ^DIoa=‡ÕPñ¿oV%sõG1¶)”»chÒûtnE|f*²á“¤·w.çí'ðÌÔK´8|çȲ7`m&ÍËYAW÷Øm†½lXô»Åñí+¦ÉÊ×p{g ±Ã?ʈ³èUîr8Ÿô4~GE VmámLÝ„¯XD9ý&Ò2¼ åd¤E–½0bknaÚdBkNDÅ·äµëtºÖ@‰sk¤–ÀõÖrn ï)l¤¿¦rZÝñ$£g4'â÷ó»oo›„t™„ê^•g?5¾AŠ‹!Ò®Pˆòv_ Dª¦óf©H@º¢­S~€o÷ê‘sûåé,ïËœ¨¦"ëüþg§qÏx§aÊy+úÁQSý,½¼™“‹2„¹€p_(|9¦„*«æ>îmæ÷Tc²_2¶Î~Aâ7í…ø 5¼æäÑ<Üdz†w'ú6;‹³Þ?í$e4Ë•ø…Žìzù1H¼Ë?2ÈáYøÓr—´–ÏaÛ¶A½Ðr°9·Ÿ9üĪ1Cе½`üÉˈiü?ÎÚ>Žj£{zm’6`ï—縡í#Je°L»58nÒIœ<>-·ng#‘öôA—½5šã½îñŠ¥uhð£/±vzéÓ}ü]ôÞÖÍô°±8›ìX†¯‡¥Ëfx{ýaÎj×^Œ ÿËÆæþvÝæ/9ŸÎé]s¦o¢²yf"\›8Å ìo³XäÔ©†]ñkÀòš ›î1† ' qÌy´åKÆí?Jx+¬™íËFhK”e/–8±®ñ Æ#×ÏÃA™ã4Ý9Šž2á|ÚNaRß{Ð+ÔbQ³¹y®.´uµ{ü}!5uéG]’Ì©@œ¥x¯§ù†š4±i Yb1>½Ø4»”÷”S¢«aøˆÙyå^¶­oÝèð(Ç—‘HÅÁv¥Y®¯5`jl­×Xº²¾jí)Oªõ蟳à~z¾Ä¯Y7 ½­Ž>ô˜˜.Içwæ0é ºK;R#VÒ‡qX`š{Ú)•[2‡-­YH›ÕÏÂŽÀݸŒí¦û["AŸÇß Ïw?'¶~màgèF_Œæ*®½€—wÐ#Ob™P¸žÒ)Z#¸65à?­2±xêÍ$“<˜}ç,ü]â˾Ýzo³Â^á=ò8@í·~CgË|*ÛlH7¾ÓgÉòŸ¸#–¸.˜ÉòçA|læn_Ì Qg('÷lâ¸gê‹ÙA§Nd*Á£áNoôdKNΦbÂ!~ßq$~ e#LŸø ²Þ@A6nùtnî‹,ø=TêŸ'§® †š `Oæ­å žµeÒ® ¹‡Á·á/ÿ ®¿5 ZIT•,SM÷#Æw*ñøgIü2i„|èÛÙ]uXZu:Æ,ãÔÖEb°¶f(Å9XˆÑ¿NqzûéŽ!#¼PcÆ¿~w“;„Xxøz Õ‹yc''t®Ì©!r{ð[n8 ß&ªÐé¡•¸mî_ð›òbqäà ˆÛ÷>@Õó ú'[gû8Û ªö9 òæÂC2ì_½ %ûF1Ù’ë^›Vfp³^}¹Ì™h6iµü ÜEœ=V¹Š³õ³ŽÁÃS‡ñéô:ܲΖ~,ŒÃÏn@ÿü\|þ"ŒŽû½„Y|„µüé!c-´¿ôï%C¹ëUî€Ï9®Iå*ЋߙÆRlqD!>,|¢™&¬dx/Ó¸ÐO‚¶áÏ]Ù‹+­\x'µõ ÷y,^ëvE‘w°\à 9Øñ ÷tf§Õž“òþ(ñ4™ÿÄÁƒkg9•‚a—Ál´®<×Là[þ0çùªá£Ÿù˜òçO㦆´6âÖ_ë?M°C³EèZUªPiß$ôÉË&³ôM)é¬ê5™€f7”¶Ts)•GPVÀŒ-Ú3벦ŠÓh’ÝŒª‹ï£¡{?ØY%ƒ[›õRþ nãƒ1fÏ þ²qêTóC›c L›Ku mV!*æ™8îÚgHj?Ÿ¬Åè²$S¶*tK<ƒÞªp¸åwkÁî«ä,\*¬Ê^Åo‚ü*#´Ë߀ñs Q» ÒPžÉé§ ÏÚ1¬ØÌ¾Ì•gE»¯C\ãC4JÓ¡š³è¸ }ô.¬À º•Ë0H‚kÖ ’y)þúVNü;÷£ÀIYvê±ØßSÇ*¸ÿÓ}<¾©žË×¹áL±6ŒÇœž†ÒKóá]Ö ¼øð¼=™ ã¡ËÞ†æ Rª‰`›,O-¤ÔhJ x”¦GöÓL»M ½#›Ê4qÝIyÜÔŸ{p÷FZòé/òK:ÈW„íõðÓÔƒ?¦Êz?¢ƒ÷ øpãFð“Ø›BgÊ £È¬%l¾²$£ªgaŸ¥ ê½êå+Ï·§ivjl×,“Š$ÓwÒ‹Ã8“¯f´#O‚Ó¿·Œ‹PÉGþý(Û›Dqõ ´-]ËÿôͰuœ:”Í•å}:é‡Yý³±¿øXy ××™ÒÛªó‘Þ„3%½˜•,FGŸC¡wªTïÕJÒ ”‡‚i‘°Îq,ü}~ˆÄ¿Î‚è{:tæzôÞQ+¢Êº{íñª½¸t,«ó¢i3×ß%Jl²W g{Kì? 3‹5Mâ\^2¿3¬l‹FÖØ¢¦1Ÿ&3™þQ%úhcçµ+ÜžüãrÖ°·õ¥Üä« péøî‚Õ4Ö#YÆ›ñq5Rc»NhÐÏVt£#Í"lCm:éùüKÔ™šë8q}ýkÑAVGéÃiU±Qþж§^b°·*©(šGò‰µZ$Þ7Žy·XqÜ`–³s>Ë ¦¾¶Ú,ë€=óÇ„µÝp ÜI\LŸád‰|ø7áÜT¢ÃÅz¬Àhžˆnáͽ^@žúq S\q¿ñY ý¸O<>~û¡hŠ?™û7m#Íñ‹¾!_ôÚ~8Ð*N­VtqÚW[€ÚªÑ ¼Jø6½ÇÍÄ m^µ4÷‹sýŸÌÀi¬êêL¡Ý&pÒÜ~Ò}v •}¬¿7wš¬±,Ô&.Ö{p‡½áëÁ^´¾­ÅØß) º ݼ×&¯€/vŒ”ûµÿ_ˬ½·&ÎÜ}¿¹«OÖ:<‘µÁ™gëQ¤»•ÕRˆÙ~UŒn0f÷ÆBËÔj¾yån²à¾ —ДCvûJ€ŸKd|ùÛÙžR3Ø ´§¨Ö€»ø!ø#¤ÆU°‡Ðl:‹NŸK’úäé\‘.bÙ,䉊²¡ ^L¼àÎÅôlÀ¨J%Vc¿„$RI¦`oEņ¼¬G•<ÃÍïA¶,:¢eÉGöz‹×õ‡/ðß·ÚÏ5'¢PS.&û@ ß Ð¨é8"Sr÷¤é§júì–&5>£@iÒFö=A£§cØEå ðüf³U8ÎÏn;îa ˜*u»ï Ù-.(í-ËÖ'3Ûq>Üm¿|P6eþµù(pUMî÷4ËŸˆõ»ñËÊ2xôºÇtâxi^øâôL95ÆtuÓ¡)Õ…ÿ8Þ’æu·â?H_mAZ ö^LÝWÕ¬iÝ ÈÜlå«çá”G¸õR.IÝËy¨GÁ]#sœè%È,Ël¨–ÍAòsö{Œí²Ç|×cxd\ ªîkNÕ:25ù-.{ó‚gÚ~¿“ÜK–aÕ?ݰ}™VT¦Gï(R~Oÿì~_yŠÇÔÄRpiÍkòQ|)½tk=Ä_é!V· = štF/ç|i6iPGß&¥·Ê;ÌpǼkW‰Éfy:òþtNùŠfn ªñêãâyG{öqœ#;Ô(Î]=­DEËp?Eèý¿dÿ¦¼±+•t¹ó©£Ü?# ,GÀÿVìšÚÌ!p‡¸ÛïåÊyè°W¶¸ÿDíVxQ%‰¿'Zôÿšéùµ{ ž^‚Ü÷ b¹Ú‹^­Æ{(—~}â(¦R«»îðó½1[;2—}|f@WȈÓ9®à¸üpÎÿ9]1í¹Â‰s¬J}r~‘Kë2±Áv#>JYĤ–öqŽFÒ¬vêK¾÷»xTe†{o‚à_î@ÙR¼êîÎHÀÌs7ÉÒ£†ðT¨žô&ýuÒw^ú»é‹;‰üGx8G&`tâHšÊpÃü*ämR½6ih4ÍŒN½}—38mmfÖzÏ¥ÚÒˆêâ}°ün"é_@??žDÂrœ¦ ” ¨‡Ý¤ð·{†BÏå{zL¿œŠú>ÜÆÍZtË”X²+ÃÛã¦LËRଥ)ˆŒìÂ¥ÓÁPé6.žý¢S'p*{BðÓÌæó~ áÞÐrîÑwH&G?[àÕ+Òô”Â!¤ &ÕqÜ„UûáÌGxæœE¾Áhpƒßê±,+Ž×½#6BóàÚ4ÌxÆçŽ&˜z³*Ü•˜F+¢ÍXÞë5¨ÒÊLUÁÀÔMÿ¡ôîi\D ¨! ÄÑ¢Òæ<…/Û%ßÓo©Àä7xûB5ï´q#µ> w,˜†“fEaÉkO û B2òÞðÊK]ÿ^&¹ÆÁøÅù- 8:ÇÏ·†¥ájút<+»?ž> td}q °­0%Õç°—Ùv¢îÔ^pÜdaWwƒØèþ{>}kPùé](2^BÃþ 1þ†'ø*LJ~×/“TQØ|B‡3kº‚wÝçqOZðJüĬ‹#ÜÒ©:T?BîúòŒÖr J8¾G Ü9»îg8PÒ°šV©ãNß.LYNl2[q)~DMNPÓ Í"»P¢ {õªø/ß:âÞŸ¶p|ë j¯ŽúŒñ9ž$ò>ŠlÊúT.ª¶÷L¢y]3éŠLÎÞÿ‡ï¢WDfbûûq,mž=·DWˆŠ\=-Bž¬òõv0dzyh³ü)Gñɧ±4pn6ݹµÝGsS#V Wڳ˅­ñæè¿Ra Û¤LxŽ—¢Gž!'×í'ÊZÎëQ£xR^€êšöLü¤/¶èjsžh³œ^ Nÿ£ Óì‰Çõ›/1ó]NRºrñ•+ð³/üv'’g7 pÖJSåÜ'æsz+’°uÅQ,Y@ýlE༇ ŽU í®¦psçNô:ö‰XŠ *”ôoĽp ª+åXë§¿Ä|ªLX‘ šôñÞ•mü¡yqhHÏòͧpþäèøbÜÛ·ŒJ7zã8µ/„«iC—U¨ÙŠqn=NÎÇgáý!;&~Hi½ƒjAP‰· ™I/ìž r2G¹mw5ÙãÖ(™%À¦KU`Pˆ(ÎW#ôPÖ7ˆ=R‚»ŽãHÂ&öâØTTø [™-Ïêë6˜ßUJ6¾óÁÀ“ñpä¾—ÉDÏ€ê¾-È^F\ƒ©Þ'±axBï%8=½HJµ_ÂÞÆËĺ¾ÇiÁE!š?Óüçã–RÛGßÕ*iÚûx%wí[ 'Tz‹;úîì}\UËÚþ¦)EBÅD@éö¼C( %``Ó ”€&(a*H(v¡¨°×;Š-Š-vw`·þ7ʹrÏ=ç»÷Üû}çÞïÿñü~kϳffͼӱg½k7yüªMf¢Æª(Á°y]˜Ï›¯\/?qzaÞ31¼†ëÔfâ¥\‚Áó½8)Ï*tº½œÛNÜɪ#èr‚Á³,YaŽù’bLù`1ΚÐį«áo¹^‰ñ.1Ø}ß"¢š(Áœnw#š›Åø;®5aÞ„t¶q–×õAøåœƒ9Ã}±y{É]‡š@ƒè!HºîÄÆ« öV½ÄÛ·§‚•ámtMw ùTa€í4e1L¾lË¿±ò w€˜p>¡ÙP´H‡$êÀQ¶ö~ÝI¦ïK@ÉúƒÀQÃ%ø‚fЕý ÛÂmhõ§þdχM°ô+ žw{Pv ÞúâÒ#«IYùx¢j*¤‡¯ƒ°M{aÏD'RmºS°Í t×c¬NŸ×¬ÁÁaYØhé†c%ºÒ{瓎Ëñª_OбLÌ¥H»²d÷I¤çîç ZÎk¤T‚j9ŸéQ÷VUß ¸‹z‡.“yšl\d7xK%É”> (1·³æJÓ.ü3¡ÿ‹Lìš™AMDÚç(<ÀYÝ>‹Ñû2 íÉ3™?’>ŽÔU l0@¤z¨åa‰j/vJ¸NušÜ Óº?Eýt5@7­5ÿ+€uÁrx®ÂcÑ#=@¥ç7îÛÖë |σót…HÊÎì‡ýGî€Jölšj'Èm˜ÃnÛ'žËBÜÍ÷$óÄ Ü z|÷cw«Hbvf8ý úkxDZ“¡ìÝRLB;Î8NÀ“²,^¢«¤ ÚìQ‡›A!0Î^’h+Rb©Ã·]¡ÀN­ìÏšà 6œ&Tvƒs=4¿V ¨ÛÜ…Ø6&›_]'K.©CXa3d<Ÿ‚³I÷ÈWüÝo*áÊv1(JWbã8›Å"ôV–(ë)Tp·Í@‹M¸;…‡ðÒÖ›`2õYÿºå½˜ªm 4Ì\Ú¶…qø jÜ7<Ù¹FÈKtůÒ8϶/ ò?ïoÇMöJ"S«ÉÈÂ*Ô:×o˜Ì }]ð™j–U å1ÄJ”^\BÝe^p.â¡•)\eêj6eÍHŠ‹l€ÎB J°‹oaiªOê5ï#«òFP‰ª‡˜¹b {V3Žæ/§¤V£ñwÊŠÆCþ2Gfºá0†ûÄ]sßâ˜ÑÙ,]FŸm2™Þîs‚{Ñ µ—È7ß$Ô@…É*{çªpµr.w& ‘Ä©Íæv] FÍ‹AÔl¨Lm¸†ï—­ÆŽ'Ï£µ¹(- c]ÏCÔMGª÷<›– o–ŠÑ‚7ëðÃò)H÷ï$½êIgÎØ!÷qq\¾m Û޸ŅS·c}í„2_ÆWûЛï§S“õ ‚ŽS•Qã|½sJ‘Æ*™±W‹£¡qûœq*—^ÕÞ ìSàËg;H\kIÌ-¥©ÒéBœùÁ ì„óÆN*ðîÊJìuÌ]=Çø˜Üõ6±‹~B–¬ÓÁ[–ÂPy ø\ž:NÞ‚ß²º`üùádÑ®|b ‡•¯UðL`9\}ÍYžLã–ßUÄzW7lоÁ©ÙžæüÏûCÖùù\·ÕL´ /šÅSagñ™ÏfÎ.YŽŽ8 Ó§t$_^éÀ9 *¬€ “¤YÄ‘)˜hxÞõ}K¶ÆÚÁÃȽœæEeZ–)xi/ 3CŸq+|qÄùâU…yNšè"ÃpúÅm䮚EÜ»r}ʵ sÁWú'1Ü2 R—ö`Éú›üÑýqðÌ@ŒmØ…WŒ“Ék BÞ,–¡™Z¬â²$3qzN> SÀîëͩٹL¼šCsw`ê®R˜ã¨@,÷è ôiá|q6 ù„C¼ý( +!¾ iì©\ø¼†‘‹Zä±}µ“pÄ­$¼?#›¨Íïç?nâ\GÖA“Ã[l6±g=TŠÖeÝÔz˜Œ×F-·mtÅ$²‰y&xaŸÿZ³4/«ÄõU‰Üõ[Ñpa²!‹¾äHžMUn`&îâ«JËu`·9mPÌŦËqÌ`s_X¾ô?-¸==ÑK¦‚5êJV?ëB»îi„¡%7ÀÓs»ÀÀPu³éM <qxb#»§PIŠËåññ“»X‹ª˜0.‹«¿âÇnFÓ¥we©ŽL*}v(ÒzðÒ~ á3Õ©`Ê!œzÓ” µÛºâJ‡ÞA­„Iè`_e#¸·O_b9Žfe|ßd“u§–3×0tp/nEÃj®cWÀ ‹ê¸GVÃúWüÑ' vÚ¾D8:”i< ]á8Ú'H‚ú¯›ÉÃNaÐË‹0ø5È’Rz3õ5—Xâ…Fºi¨ž^–ÈPÿÌ0<Ò0'^2b÷ÞºpËÒÇ£¶öc0ÝŸÁî|Hc /Ê8Ù¤sü©7àæÊá8mõÎêø8 ªÁ¦¾‹cŽO ¶?NdÆ?°·˜j›ÅÝØ²Ÿè'Ò7º•ܽ”¸t~DÞº†óÇmä–YÉÅÚœ÷¨©y,l‚ ÓûҦʤBÒ€ßÕNꥫn)³õ}‰½qDí3 Xן~8ÃáU 0$Ë68×Ý.€£E*8$’ë»ûÐwÓ_ÃÉÎâôÅÊnTâä0nâ³Üçàs¤óôñ¨.¹”mEé鯜œÃ}(²«%oåíðsç¹ ²:÷ª›Ò3@{¼¿ ÙCC@i¹À¢÷}Œ¹Ÿ„ÖO’RÌâ<ûŸÄàK|ëÙŸÑwP¹¤M›,Ën½™… Í—8µÈwX,W _wéC~z=|­¹C:Øö ‰‹¸kÑU8{¦7êê}ƒN÷?ó»$÷a³,—ÒY/KÐÅb&•{é(Ìç¬Ý~Â\¹dè¹ê)^JÚòw=ñø@m€ "PØ‘Ö)ǸSu¸JD û¢™Çƒgè¾y-~²å¿rc§:?…•æt~jfxý9Ÿ– ”^!5¬c²šìáõØÀÏeýÇÑ;×dÉ|EBO ëCËÞ߯¬Ü\NK¾Î%¿¹„Ž(U%Ë>Ì}EìïgwEܲ 2…m½‹ÒÈ_U£õ䉡+”NÙÞƒŒé‚Ÿðí¼Sè÷Ô˜•e¢Ÿƒ$ØB5gæï~‘œê•æœ^žŠï_‘Ï—óë4¤vb'«ƒÂðf ý…iìcS. <‚K¿¥Â{‹|¬vèÏbcT¨±‘v2CG§õOaë/.kb0tâjh`¼;›êK÷Äõ`iÞD×½.îò“Xw&Qß÷$¹êÞû¹Åµƒ8é©–T ú±iŸ¸‚9§ bG=$˜†‚=|Ø…U+²c…yu”œéYžû^ànGU:½ä+·§ÌÞiÔãÐà»dNý™“;šL¥¤ à5_“ULšá›pŸ’'33X€¢ÓwàœK?±†3+ÆåùFô¸Bzëú^Ž?»zñWƒŠ–˜`óm a2ðMÅf¸‘ÃòõºÑÉK©³ ŸÅÝ+îâ·µ´Äé9ìôøûá¶’ór¯GßD+v5¿¶Ù_ÅÐÓ1lù AhÄ"¨è*KiÃöÄP¶ù¤9Óóˆ¢{ž^‡Å†‹©ŠÖþþ'â42§ çPìº=¶v],\#jÒtï]Ø©i€àÈ =âšHYÇ=àßÿÊ|R`/†ËcðÛ´J±dã䙯,]ÿ¡M^[±³™¶–Ü—)îTÙ /r°úº&úÔÞào0­ùÔ½I ×ôéŽä•4%ш-²´§J;òî)D\OÇqk%èà›9›æ+¤èÓsì|¹¸kÌÀa /‚A°4S1…áŽà¶ ©¸æà(ò"ð#×ápg yÓ—.s“ÀkZÖTe‚-4ùgÛ?´3d…19p>Û‡ºN¢Ÿ¢VbþDävð»¯÷ç´ØRX´v§)(ã6Ê€ãD+:ƒ×‹® Øýt*d-'Ðmšv߈»äâ{ÅŽÔ87ˆÖNí‚ÁðOœ³Ñ¾I™½@ÇÔ{p®@ "½UpzÁ4nΑ ¯ÆÀšù°ØÅApË ¸ جՕ=ÜŸoö>âÆwš§T!<Λ•KÅÈ*h:xM†¸\^„&N€ùÆupª¿8ž×I {®?Žýv™×j²À÷àÄ Q¦_·'ZÑeFÛ¸üÏ{aÚ¡—¤BïÊlš¹ÒÄfغ±‰}Žë§m‚Ë›Eqð©¨]ö î_²ä¬Iîe–µ¿Nò¡Iå+eZ¸8™ ¸ÔÅO:î_{áDî%wìžU/Fm§.4O£}VÜæ,š¹êàëÜg¯×Ø nÇò†]¬3‡0§ó.XkÖUù²¨:{4Uê!½jjçÑ1G4é‚ô àÁŸråË¡º´œ[“(ÍÖÖ¨2}ñbˆ½M"{‘¨Í¯¹†ÛW°º$ŠeÝÉéÏo¹׸Y>ãºø§8ag_æ•Coß~ By[ÆY’»ö‚ÁÑžX⸜\\b@î®.•1ªC³êÑ…]O#¢xLrê ø602:¿æò–ò˜Ý®L¾Eèfx”| Þó©Çycf<ЇJ®ºÆù%´ü…[“¤Ä3N€—Í`h\[AÎ΄F1G–ýå-”º¯$×; V3QŸº«œ¬ëêÞ‰Æù%»äYã‡:‘=Ç mï<Ì{nJûºï&\ ,ÕõÌ”L1Ö˜¿GÉQã[AÄrÓ'"’r•—-e ŠÚôÃà â°J_ºÜê‚>›ˆ0 w£x_÷‡6U¸VîâÆ¾M»gF[â[®_Ãå_ŸÂÜ€žl›!,=e ãljӗôjµ¼,h°…!{È/À[ç×áèìðTCîô”åæ-çÒáƒã Ü6æ¾ j²9îÉéˆwãÔØ¢s½Ñµ2˜Vª[ƒ’FO–iº£ž¬B³Þò,ýàQØ6Š0‹ÂkÜ ‡>œÜ×¥uîäç3í5¬M«Ã¨™sè½ôax°îWù1íèþd—’Ní—Á¸ƒçÒÆÏc WFÂs¥^p½ª‰{d”Å:­ÀñöáÈB¼åbO,ƒjc#Ј¹Š¥÷•ˆÛ]àz)¥òMåi¯‚ý =Ù•Ö^#³oò–qäzó24ÏïI÷í¸‡$)í/MHªMÚ)í f&è`é_Üt¬/vEtVùvü´=Q“‡ÆÃá2j2ñ·¯« ÎýŒó˜r}Ÿ’fa`%å7ÁúÒjXjÝ’n â’žî€s#ï’û]ƒGP#6†v§™™îtí#k¬/GOõgܼžqÌ^%Tï*Ãx—Oxuå9ðÖÜ ÃSëa¾ÙL{=ÂF¨Ÿäš?ÁÇk%xþÁ\²7½n‚‡yqθÎ5-ËCô\ªb(Ø3Η«È_U§³ë)^•O$gÀÖåqc¤)콓æõÀªÐÝ|ÃÃ^ö·h¹À©ãrÒo’ y<ç\ƒJnc$ˆ£ic§Áz-'ÊÌ Y˜„+{,ÏcïN%Õ_ÒñIÙLÛ›Ì<^Ïm^*#˜émåf×ÐQ8Dg‡ÝÃó}ƒákg_Á épn~ q  ÷ Y­à6.ÝE%‰§ß>œùÜŒæÚôc7%B îë:.8°“u§Žåé\nr7wK6V3¸=y&WmÑ•½tíKO>n©ßHÀø5‚!÷„käôû¹ãa¹Öhäól”î½âƒ 2>…µø½©þœÔÆ5rzìÕ×LìãRÆV×Kröá P­YËíK½-ЙكéF÷“Ýx¨Û2ˆt1¡»å³9¥Kó 4IŒ ë1%•Xѯ÷áµ:€Š©=£Tóжv ÝúǹÜâÓ°Öõ’õ©, u66à[‰j<& }–…Á¢” 4‚¦¡_Ž;²ú‰`к ¼rsë/yÃkᇑΨyÇ ýyL®! ?¹å ²/Á…;Rk]^ïœnŽšUÇH§h3¸+ˆG>ä@A@dhÔàÀSk¸ë—¢C×—Üû®’lÇÀC´l ô;x'?T£Éqªh7; M:§Ñq³øŽsc1й÷Ž ë帮ÅÀÝx!‹óœ>&øÖÉû-Å1NUÄàu<¿ ƒêò>üS³ÑùÚ•Ô‘ýÞãµÀ}pÎüô»UNÒæùFæ®Ý‡÷?.ÀˆÔKözë9Š}ÆÂ’®ÁFûƒÄcA!™Ñ}0ÞI{ />X2ßmRÜ´¤8{Ù-u£;Tåõ7ÑðÙLfø,™ï_ ;è§°2éœ}e˶j>ƨ[E¬QĞɛb¿¡èSrÍyO‰s®*ígÀÒctȺ«ØÈS€æs£jãÓ9p|éôв¢ÔÎî)¨²;>=dbÅ=ܸ¾MîËnHuãÄtØŽ+ªà5W‚ÅÛŸÅÐ7Úl‡‚pLÞ™OcÊ·ã&©,0 <Àil9…¥°¶p9ÿhD ¦ÚÁi‹U(7t%|Û™EW>Y˲nD±{W!Z2Â4’`‡F\×Ïg_†ì)‰£œã–yÄXQ”îí8dg惦×|˜73jKÞ`&›MÏt>ˆ.×Íhy]9œ‘ö&¯6dÀ En`â†eC¡jù.vM=Tϳ¤çòP­¹Xp÷ŸÆØŽ‘T!ŽŸƒ)béÔyØ"°î­ŒÊÊFôbý[ ­U£Ïµò º¯ÙYb¯Ié“dvëËzš#Ë6¤s»P?±PØ9"ƒ.PgäMß©´x²31ÉFÏ3yîùÃÍiL~éø˜½OPˆI¿ˆp†Ï–•™£ùº<øÕ—µRf6ßRØÇlµ•ïÃØ>ÇlºùÙ*N+V Ž]€=¦“¦ØQ´—t:×ãín·ÖƒÝR«ã2|iš‹iÁ:td©9FeŸçÔ"œ`Ùrp6³ecsãXè²×7ÀÝ͸îá0Öó§ ª û éº ½çÀAíqtÁÒ+\·K=³öÏ!ÕF)äöéG\CäVz¦ú3ÉÙ€ Ä‚ú¢+¶«’“þp%•çs¹WŽ 3[ÄÉHÒ­V6o­¿€Ìé—v¢;r3Ö½ìÁ ¯âŸY“ÎöÔ`?|¹N˜sßC`¥$Éé•Üá¼%$!>詈+HñÃeÙñ”Xˆíl }Gc¬n·+Eƒ0µI £¤„ú¬‘;îI>- Åæƒƒ€’@2¢.ÍÕ{ÓÛ§ß VÏ=†Áš§qS£ p:Ôf“1H¦ž©ÆðZ`LýW×Ú_=Öe}~'Š×«49´:¨ —"çÐ7[®€]©M5;ƒ¡*ÙñôOv'k $O+Ò©u¨ãõ\òÙå8wGy(V§¾är_¤ežRlF¡,ݥ݉;‘O;ˆZ§Cèîu|fïKU¾Ì°7˜j‰£n†2ƒG}9:x¼ÞÖæìïÆ—ÞËàεPÀ!éGÕÍXíg‡sÜ#hó‡yŸëÏlº;0bBs®‡³'¾0CÜ‹É\tE£ÍŽÐx<LSŸ\ú®2‘ J Åî†3©¬ñ4úíš!dïy -zžÆ¿E×0D„jßö`ßFÁ‚ž6dj`wZ½Ä\~±UT¶Ñ°¥›À½»½y3>°eFㆰ&Ëi0Åç.& …Œhz2b0UáO¦"_Œ ±£ ëÓ “Ž4“гpAa¼×åê–J°ñ{åhX ˆ½qÃQî§NO?’„mM;àˆC_:½!†=”XÉ.µ|Ó+,™JRÄûwÆRýÜnËmÓÀgá0ºÛkÔ{*⛀2´EïçéÚgîÁJ“ÙÔ,õ z¾÷b=B™kR§­(ñd :œu?­Êâx.lMž룲†fò0…/…Å}è¡ú&{ÿ'Ùrc'ºUÙŒ­± ¦N S˜óÉ•p(þ)Õ5ä1ÁâU¬¼ÈßrNô†þ1,U°ÂœÇ½h×µ1ì™›ÖÙœàF©ù£B3õûr|àëÒÜ]v¬çsuvq×m¨XÒ&ç³N³WBËù櫞V•,çñj=ÅŠÉÞТ™i^sÁ„ú;Xc‰»4 yF¤&Άinv\OÙn ~ÆÞ¢M{:6ô[CžHÑÒ´! Ñq Šo Yµø¥»%3œ`DL<|3U&ºs ^¾–ókàø.1 i„K8­äïFMEi˜£Ò“Jep/'2¶©€–ô ©£Jñ]~ †w_(ØceDÜz^ÄÉC4Ù‚ƒý1(è#éø~'èT>€‚Õ^ì¼ÂsHؘ _’0ú‘s%ƒÊ7ÆÒ°]r®¡ìÞòÙ˜ûšÿÂ}±õ<õUHËÈP—=¨s®N˲3{Ï…:)îÂ-«t`ô­ÅÐeP$}·»c¦|¿5ÒÄÙ)Ã½Ä  `=Lx>l5u˜.Ɔ—„Óƒ“öÊâ×¹+Ñ´Ò 6Zd Š£Œ™»‚2ëw7X>‚­êôñ}pa8îKžçãMÚHžìN!«vVãˆ1ƒàx^ µ¹Žç—톻özjÒÄÆ#Ü ÷Ѷ=gmc…{Î=Ç—Sps$·cÆlqZ¾`çЧø-rÅç>=Maµßr§s§š†`xä6c˜š-èÁ2& ûéSËa¶#(J‹§à¦#AøyBP)æ|ŠOÁÚM›à]U5±8&B§@ɽR²úú±^kUçS׉Ñö‚µ6p;ÚuYŽe=;Ü‘€Á7ø•wúæ­JæßÓþSFäRÞÆ3»ŸáÈ…P÷.„úE (Ö‡›ÍCuug¶cÞb(«”eÇ,DYˆæ:mN.ØoÍDor·=·q{Lb²^¢ìò”:ÑìF§_YI¶¹÷D9KnêkW»å`´Ò“z?Va&õçPµl={»øWmgÇl—-' [8¼UîŽsFÇÐåz‰4Ü®:ë 结1vBæÊ±ëÌ1û.cÕhQÎbä¹5¢¢È8ÐŒ¾‡¦³[Stè„.îll 4õ-ÉÅɲÙP~ÚÀ!yä!Á%¯zö±÷?¦€ ¹dÎÖ=| ‹—u†Åª·àÓ§\Ò8Öœ%¼XAƒŽ'G¾YÑÛ'›¸"_ gktiМ©lBè¼ç8¬qWľ¥tj]â:r9‹——¦sãÓXì–`¶z©-¥àJÏí™Æ¾]÷gR¤„ãO+NÑ(¸ŠÛ|í#N æÑ£'´h‡#½¨»{, îrz\úîÅ8ݰ #f»˜ééyÔóÛ~AÈž*v@Œ-ÎUb#¦ÍÇÏ·²çVâcæl^ ×g¨Ó—kóÞ^ §ÜâTTÖÓ—ðå‹\á-Išä¯ÅTŽÙÓéc¥èðÙ›1lfO2Üö }ªÇ}¨®é/æçñ¯x8ß –o³„nûhšïIœº+úÛ,æÜ à–>qaâ·aÎ^k’0EŸ–W]‘ÊœW̺+¬Â«G†Ó¡òyù~+¨qce¾r}Ô¤hD€+Ž™y”u¤þîäœV$ÜX€ÃŽí'›W–’ìŽYìÁàý|r"­]U…—Žqµ4ÎÈO]»¿áûÕÁ9HÉí'È\éIº¯}„ýû‹ðo©`ŠPýa$”€Tè+(íö…óêc€ÎæÄ Wskì¥aªø ˜òå,ˆN Â÷‘¹ûªp÷áoüš±™7òäùg«$X÷.×8ÅAò¢© ùû`uÿ™ØÇ6¶Eªã×—_ 8[‡I=ãÈ‚Æ 2Ï ÓüçP«5ŽfÚ2çÂñüQ݆…+gÙÓ[°pÿRç›ÓœGGq­ÎU˜œ1û2Y¯~ÆáIs`R·¡8bÍ¢íüDz [ú_ØŸ€ñ»Xÿ±#õëc,2Z{„Ÿ=ƒ–4/8*%_¯C¨Lå{ÎǵÊ 8€a!c`¤Ê}pÍÌ=U½€ ·)âËRª7s*Æ:îù€nÐy•Óì9"’ÃÔ™{ v¹4>\…5¥ð`Ï h”M %y§ÁšůiKð¼¦ë9:7眵„C•çðŽTÄ+Ðî×4i—Ž"ÿX3—¶GT±¬hѹü-rᘃYçoÊ0?‹´œ½Ž}8ï=á:Û²Ç]üh·wP(vC}k0Zù=¬{hÁZtOoyØ•^[ M‡OÊc›;æ’…äéÅ 0z‹$}ÛÉ„Îú*z=WrA°k‹;3+•Ëx‚Ü}mK>UÍeÒ9u?é?pNßú™ÜJ.·ž»Ákcl¼± èˆ|,{ñŸõ*Å©«LéŠ5.y³?α’dÑïç¢ß®±èg*‰Ïõ›“ãÀàI9§²¹ëí‰Xþ`¶„“÷ô±w Ö]¯ä¾ Ög—×Ðýk»“ëWzcXlgVúE7ÞèÊ4ï¹s“Ldp©¡838÷Œ¬ŸÆçúЏÁR‘LÛvë¬^ „Ë/6⥉¹Øop/Ö±i(+ßpãÖ…tkÁ0øhÓ™j^6 A¯ÍH×â“0¹Û0¾^žR\ÿ|¯·In°½Çù`ï5l¿\oê(fÀì•È©ùRÔÍ`%t¾iŽsá%ì_†Å¾I»×YlÄ yIQ»‘yzeç¯Ã›CV &?êÈŽ*‰Òá™G`Wöbh’à¸Ðæ.ôdÅæh¿x™¸¾w<6«œ†ûœ'NâÂ!ÛÑ·X”*ÒˆË Æ‡žÇ%.tâ‚b7*ˆ-Ï@SÌê¡Y>„ÝP’á¯ß1»ž=ŠV²X+±„tõ4d'ŸXâ ƒãpG£?=üÞƒ®é>¹‚Õ¸`îqŒ9šºA .åük¨oãÁݼ²®†ç`Cá*Ô_ºuÁ­›ðõó<`oµ(;²Ž('AJ©3í¸> îúqkxC߬"R}ór?Bf‡n¹daÓ*|s`Rg ì—0~:ŽüžlÜŽSð~½-.í ³W ë·¼æ'.èŽFØ¥®?§Gܳêùü^”˜]ÅŒõk‰ù>ØLŠ—ÍŽ_¼8¯º+Ø8‘‡÷£ý ocFÒ,S@î©êó|¡.eÌ”êÌ>OzNÒÙz·:Œ6¾.žÂ9Cã\r>¥Œä*9±³àJ½¹ …6á(u'†Œwd«ÅK ·áA7z°Æñ8£Ò‹i¯VµaÒLý`"ÌüR†±¯ã©é½Y>€/_õÐѯ?«›¦Hùñžl}tgtš³ :äááe¢4cç>0r‡Õ½¿p·¯u¢sª¶ñCÌ5°á‘2Œ艥ÁÔÍ„¶ÈÕ¼# ü¢¥hW‰Éäkß5DOí`Yÿ¹ðaQ?Ü}±+¸ºýŒ#°Ô`½ÀÕêZ|ÊFÜ Å3¹Ǽ°¿fGöa¸7ÙJŒMŒ(€¥ÄèE±§ÐG8wëà-€^ŦLÇe:½-6ÄgÀªYº=øçTŒ»ôF“¸›ó¹,£f¸¿@‚7?5(’)ÂQ®6‡˜¹zCÒ¤Ñ *Ûƒù ¿—"àgчŠï¸„or ©¢?KÙª‰"ïGã—«ÜÇì:‘˜©P­]ÎÉX”€~¨81YOv{‹ÏÞªœuh}ô-âñ2ÎtL*ÍvÁ' ߉)žXçÏtå–ã21]\³Æ´zAìÈBîÌÒUÐõ‰:{t:«§eBûkaZ¥µ;· œ‡Ð]Åháivk³Ë>ÉÉ6`³»Á)¶yn2¨80Î]î!é[°ž®Ûy“³…íGže.#'\RaÕAÜÜ9f±z»õ‚§ZýÀÓe!V?9IŽÖ‹cºÎ=,®?¬[ATt¿âÕcbTÉ·^E/}…x¨J|ÇÉKŸ&ûWöƒ¹/JpʲCX2Ñ“n8ÃBï™´Éà ø”ÉLý~ÛzÈ*X±GœìJ=fÝÚ¯`Ø‘³{c)é{oæ?éFf9Qñø‡$ú¶œ48È­2˃IÏpßz°så™-ú[rÇ1¹ÎF4¼s5W«Y Û6¡r†D™ ›‡¾Ä‰_˜Ú³©¨®¬À®^žÃDØ#xøvö¨è$8½Aÿ-q[Ò|Ü¢5„˜Zβ^5£ý9'ã ¤{‡qä<>'™ Çq·È2þÆY%v)÷à£Q º°‚J59ƒ^G¸šìnÔ‚Í׾ɡfd—Î͘÷Ë{vÇeOû°½žÊxn}W毷º¾?UwêÒL‹˜Énv¸ÓD^aYý5ØÓ}~³“«òDï¯ÇÜæé;¸P·];|2»¡×•]Ö€ñ%`,£GÏ —¹'h¡“ˆwàTÒÔsˆ›øóì ëÊ<45†‰K•ÐÄ-¾vMžœpþ³z4³ßêAÒ8Þ‘H>¸±ðÇ'¸Þ&JøtÂnЇ›†À½káí›=$ffË7s*òÑÔª#±ºvwû¬å.ÝKí‰g¡ÉJŸÅ®‹$_Kvàÿ¹ "´ÉÙ£Â9€Ô!øÂ†“^Àuøð–¸ñB¹Ó|óãöe§Þƒ±7qž§ŠŸò/×iŸéIžfL2Ô”Lçn_ƒ/G–CÍq]Vg\~oŸÕž7§xöãPΧƒ$¬NÅ¿Eãq~ D}MÆ-òxxÚrÐw–Ã.¢¸-yŸ¸Ç|ñƒZ¡¼úe ÅÞ'ä®/Aªä~¢·õ—ë¤þðb‡í߸‰Ûìjä·ƒhÎôê’ñG-ÙUz"G ‡¥Ø‚’£À»ýK.⃛;ðîS=Öcè¸:é%^¯Õ`ElIèÈ(œå“U‹DÙËmpàÞŸ™ØrŽ™Ýþ`€ÖåV(ÙT ·ÍàboÙbBQäûñèÈÇðj­u›çLó›¬è¥m[¸º˜zl‚Há\æÔ ^C:54áêÈœZ§KÄÏ”¯z nTž›g Ëó%©Bÿ¯x0¶†¬íôXÐÍ"˜e?ñã‡E-¤+kqÝgVôu[>v-¦> UýŠÙ$•Ûømƒ,õ•§¾ò &éâ¥ßé Œnfè%œ÷Ê£íÿv5gúm"›­ ‚6ãd«¸ûúÈë2œûú”ðUçâ DìW>ÏnÙP”V”`ØÕCWaŽq/vØs/YüAXá&r?µn¼É}N&º…†äÉÜ8uE‡U½hÍÖÉðb‘:Üß/‚áaÊDÃ5 µws~ áµRœËÅJÎ^¼ ÞËÆ“³Å90s€\×un“Œ1Më´@gDª¹Â®­ú ’WÄ-ž8Ÿpöç°fn&ó¹ˆx»ßM¥ rbágОñzÏÇ[ûÈU* x ï›áÝÔ›düæ‹ä…¶%Ò»ÇY~9¡ÆFàÓ)Š=¶t$”wÂõþr¾a9vê$Âêfžå¨E™0 Ã$Ç9ØåEmÀ_* îÛ{ ¦B¾SFH8€ìšÓ\Z%<õš;}½š<26'3¾ ƒk²Iµþúzk Úô–¤“ô’ì3{Èá‹÷¡ìb¾ø<^Ì{9OUpÞÚ#¸ è±@¾ÀŠJªˆ°&eg’òx,¼•̃tÓ$MJƒöL+"º‹BË™e¯­ZÔhm†êqÚlbÞ@†žQæÙí òÌàÚ î¾c W8% o‡ôÃmþFs-v0b Ö.”‚YùF옥%àÙf)ê?¨Ì*B¨û ÐÙè!ùbLƒ%˜vZMöiäH’6QiL'ÖÖ0Õ}+êõÊ#›g Žd&ÑKµ``¬§nÂÝG€´(}ŒNÔÖeàd€æ£ ©LWCÜ㦚»c Ha¼|Ûº-~ š &EDàƒÜùÔÇv—ÀmF¼`Õœ ëq.ÎTœŠý­ú•<ú 5?Byÿ(´œÄ òÞ¢‚¾Z¯~éWüV¼‘ ÒȇÕÏÊ`¯Îh2?CRžqsÇ»g8±Dms² ]¶© CÊ,jSw »MXÄõ´ÃçkrIg%%Z2v {·}*]X;êbñù—ø~„5ÕÜñ_ ØLZäÙ~¡ˆÚŽ™Šk–á¸ë@×ÞO¸æÑ«]r}”À¦¬WÝR…˜p¼Æ =ÏÞBâ£P—y Ç¥}„'MÜüÊÙßÏ,ö¿‰sÍÙ„­¡ªÚ‹6Y­ ƒ%[Î)sV€LU=\qÏíÁøÅh”ÿº‰óG_ß7ö÷V6ñÑötò¨s`È?€ñÉ—0ñJd_>Bnõ9Å-{mDät›!e`½"¥×d³°ñPWÚ¢ºfë3¸zj#7p•pNÍN;<´bOWÚB|Ž7®Z´ßL[€Õ·Ãà÷ Ç{W”ü– ¾G•FVã¶yÞìÄÑÍ0"ñ÷Yé Yǽ›¥;!ß8^”.#V)ØK5ŒZ÷bÎ.r,j¢ ;mA_ž›þ×P-Ô÷‹*2‹…ö4W–` ž}ÛY„&MïÝä·à‘ÑÃ1¢¯õ [®SDz2÷ï‘Å!ÓSñDÞò:±´ódwÕ`çÀùàÐûÎLhÏí(èÇôÖ›1™Á8ÝNš}QÎB‰Ç2tsÃ*ηï~¸îÈÉ}Ã;›Åd²˜^K©á¨Õ¬¯Ík(ê÷/ê½#Qk€IN„õ¢LàÕ…=Q‚ûîö¤øi,¬ò”¢ ½²`÷#ºò Ù¯Sý¶k×oøq0SÜ‚lµBLð Ý>—édœÖ#ps’¥}¶1ÙF8y³Œ ZPO=ëLíº [žEзò"ì­8aoc•`3»æð€ôÚ~ä,®ƆR;Qf´G‚-X`H›‚&±[’è„)yÜšú´ÜÒž]?K]@"e8ŠsÏ`½ a=7M¥©·2ɪNÊŒTDã˜>QtH€-]• Æ¿s5ÄNµäôñ$Sç06Ûm;érGŸ­w—yRË;¶TVÜ€ìĪä0É-¨Ud!޵XqÍôò1é@ ¤ù[±¾ +@Ñ¿?mjv¦ÕSâ¨þ¸±ôŽÁN¸?¹#)ycÖNÞç#—Ó¥ˆ¶ î«UhŠa'|¯Ô™ x´ —¼‡…Ï?ã«—Ýàö¾9\Ò,[ZPså?ÇæÆö– élž“ oܘQê–Eõ Å+`¿M¾[6 í©Á§§z3«j{røõMLœ82£ª@ôº¬`ÖÂx³Do¾c‹dÏs;pV[yPøº°$uàþ‰ÏøÉº„œõ…}á·á¾{0(¾ë-›»”¨™Ãn5Ý…cʹÁÏÀIQÜ+¦‰}ªã‰äU¸n¡,ý–•_¾ô¡{÷Z¢Âû~‚Ú+wÀïõ+ò2K•[…Ž\Îhq¬S~~Óí¯ysÐíÉp_'>»ñ`E:ÎUú NÚ°Á°œÜ(ÉØÕ}¸_­œÿÞÌĸ°~¸õF5jf\N»¾ÂÛK+ÁçÔ:¾ÜéO°Uñޱ¨!±ÇýfïãÆ»Ã•ŒIäô­n“ð/a8:MØŸIq&QÕðä}“Ю¯ •ç+â¢Ýеƒ`ì’/ßy@©×þô# Ûü0¬\‚æÝÚg?hèü{Ña8.q[G*²›=¾à›òÏö»ŽH±×êÀ©Â¶ X³Z>>G¿F~²ßº?uo8s9qÔ'Æã—7’xMÑšfr]دK2nÔxATž•ÊQäv9ï…ZöK+xAtžÄÁ·¨‰¨u9”E+Žc-ú•]MP-o/4]vçð›ñã.CüPú¾Ñl®*õúéäRx Dß Ÿ“OÄ iØ %zëÛvúöcojä–Ž‡Â:qŸ¦JÁƒ™¥8'ë4‚)m=76wÁ]"Ù…¾ Ñú;¶þ”=Ù‚_ü'áätŽ[ÿJ‚>t#Ôë›?Ê´w8eÊÞ®aSÁËî0vî{™³OZ›Aþ›ÇðÀx3vz¦Å’2Àõ ×qòÌWHJ¦á >ÃpÉnÅ SØûüÇp–aSo]R÷‘[0í‡GêÈÛeš´÷8T®±¹7}A¥~§N5¹n „Ž¿z—xæÐ-;ôY¯‰ãÙ\,#YMW¹)Çã¹ ¢+àVß·œÝ.)òúëYH2ÊÁÞlêì<~rogíÛ‡më{Êò^Àé•V4fd ,ê’½m±ž'O+‡æpç‚õiá:¾ÔNˆØÉ™ÖçâSÓKèŠ{E¦~ÙÎ5ñ¢n%1}iwAsŸs>­‚3­á~š>W6§ÜWg~+ h´ÖMŒOcxbÒGnÍÉEP¹ø¼Àœ•ÛôF’{¢D7_{ˆRCzâ›îìØ™» r÷854lûÒEÙ½˜Užyw ªI…NY¢Bù»aËÔITñó70Ö°Áío»Ñ…á)ÜÇÓûpðÑ£|U¦BÓÍÄY§"¶ïË}NÆ]ºn²A—¾ÆœËðÃÆÏ#{fr ”0yU‹7¼¹s)X°!f5J³ zRØiD9z9v¤$ÜÒ–|à¿;›z®Ö£ªÊ>˜ì:[8Öo·û¬T ÷Ë›ì»/F-kÚ¡*÷¯,Á;e1É|æíV «V¿‚㮩 7½&½ÉåÛÉ/‚k'nà.¥l.Éj}ª!N¬T9¸dTƒ‚È} {R‹U}Fþ%°{ñb—nä Jy¤iƦN›H;.òA)8P4¤ã*¡z£+Ë‹¹OàêQ%Ú}i-Þés[m¶€wæS‹ áZÑú 4Ý“f‡ëáý0 vâHZ½}׃ jw¾UÁÅ~ç —k܃ÞL,„ CŽaÔG&³m!X)«³3kðY×p®çÑ,tÊHæ[ãMÒæE Îõ\ÕEÐSB£m5QãÕ Áé(1öîü 8pè6÷5:æ=$Tî&wLô7êì#ÎÒý:DžêCë5°uIw¹g‚U¸âƒäöv¡sÍÖsêÇF³4]_fj±¿J¡¬ØRÿêòý— üÒÙå!CPvZ ÷µÃDV@÷àó.T2˜K¾Ì׃ìÑtÇiºd[?,_7Jž:ÌŸxÓ‘Ý·Y'æ‰ÑµÇ{0¡ÉvÊòé‚‘âtéXupªû ˜ä¡B¦¸î¿X%P>ËÈËjb1û)æ¦1Ù«e0A¸ešÂF×{ ×Eö`ävBõ¶Á¹/jxIŽÅæÈ‚슋8Æç#™“S‹÷î˲[/áÔ}ÙÜö®µø’ËÁ®'Ê„s3ibSeŽÛr Ó)ì½õQpIïY¾i2(¬='Ê¡1´Íoo-ªÈ&/H£ã*ñÎþnÔ-ï÷õs4®> ™{ÁœËg8Wã9å:§½-v$mƒ 5×ý JvE7N= -Ïj[€‹i#76›r'ž|áç|‘e¹6›1zû1t~–¨¹jCçe2TbB$º.­$îÄ!gÕ î‹Æb8¦²”&gSžµ4gç•‚w×΂ƒ²ZxøB7Ü+œëïö§® òˆ}ñcÜßaUr1bVo†ÀŒiÃÀáÆkLÞ¦ _?+r†V’°Öu(f¥îÆM·¸«iXq¹U6ëLoqÇ/†§tá|çä!lœášTÔpKè&<¿Ù‡vñÉ’k<éí8Eºó`o˜ej‚˦ނó×:£ô‡Üú}öp.0 ³¾‘2Î NÝÔ¦-:¡OJn‡uïE¸ôä|\?D‰Äå¡g¹1•OpaG.<Å>OŽÁrèñL}±Õ‡íŠê4òöCæØdC3Ts±üm6ç¹W…^|"IÙ.]ÐUñ§•·gÀý0/ú©f0Uuz 9×…Kî5z}Ò"æ™þÅë±âDwêú¶ÖŸ™ÃÞçÊ`ŸZ…–´ƒîçMDAwª~²f{ÞòëYG«º{-èbó|ßm™8Þöt,¶„NÑføe³µÇÔjÐnâIèÎìuó··®1e{nºŒëç:Z%I=0òÐKhÈÙÉw6~Dæâi¬ÜÒ™íº/Åd‹žq£‡¡š‰:l¹Ë‘…jR°„;”nÁ¢¾åp;Ÿâçû ¸‹S¢›âœñòùNp÷«:µYué»ãþü/Ç0yÀ08U¾ím`á#,9Ôz¼¶ÄÛ§“àÆ;ä“‹7÷>ª#_÷“;[z×”3¯s›•÷ãÈÓqìà—Ü•еK1Ù/êK6ä8Òrx¼†T:^§#úO¥Õä8²'˜½{];Š“è4¨[kÒŸx<ØÈ–6ÕQr,–Ü^t•õ7µ¡ÏðÜžÓxþ°î½òáxhÞ“°BV|× ÷û‘%.j ”5›µè˜^‘w^G—!öó¹»–s¸Ò+»ÖΤ£kk§9Ô“ÉËÖc\Ï›t&EéšÃ®o=Ê5M ·H¾µ Ð5–ϱ%®”É}(›2[}jë÷|1x¨Û õ£hæ6¦›é±‡i³]÷ÒY‰¥¬ì©Ä™NÅ£sf@s³7[Y=‰UëœÄãnI˜ !‡fŽ‚ñ~2,kû|²BŽO”r‡QÞÜ@”í¿‚xÚFÓ±åÀn•ˆêéÒc¹ž¬“°œ/T·T6‚5è¹Òëølõ‡òǧ°ÿø{0IU” wQ£wæ‡A>{úJÝz)MÎrduù’L¯~8Ö…±Vtýµ}r 4sŽ=çÒ»ux‡æïˆü†ç‚wv´Þà}“Á°t¤{¾ÑñEznt›öÙ„i> ‡”-5,ZrÍ Î¥Ã<ÇÒY’úì³K‹žj¬âž2ÉpР÷ö§c…µ8[œÒ‹ÝêĬ:ωO‰`ӔߣYÎ<°¯yŠ «d)¿ó$6éÞœÝqR©‰ôÕ+fK:``‡ÓÃ^·sH U`cÆ©Á¢c¹\ÎØ\ì?n+,W¬"dtzô:ŸYÖ,*áÕèU¸ìh"›)¢Ç&ÞYŽeVLG6ÅØ-L¿‹÷¹1Z~nðäøÃ¤” ÄZ‰=pØ"¾rVÉ}éËS0åDzGÆa×=Ù•µ´¬¡ìö£ê"º46׈1ǯÈO¾&P/JÃ’±Ÿ±trJ èkîœ'£ríOß]IöN|Ý-äLðÈ<þ|¯)‹‘¸KÆí6§R±‡voj^ÓJ³é=cu¼p]„]½†r<öfÄG¼Sû¤e !įt ȶ¬G‡J ¶g>¾XÑ·ŒXLíS¡¸<ª_€;ãN‘CŠMXlÔf&g;Ë«ªÓ¬!ÿ•+ÛŸÆîtºÁe.q…‚Ξè¯p 2,Þ“in!X½û1f¿¼ qAâÀdGÑ.ÇEØf1dZçpÏr¦Mú°æx>ÎIVc{Fi°Þ/5è¤5ªP-?™;^ó¹0ÌÜáÏŒ¶4ã©Ad9Ï]”3«Q/áÄVq©êÇ$Ý6ÒãÙ¬åü²šÞ^Éc ªW¡ÙØ4g£ ÿ1×”z˜^ið‡ÞN{«ÈŠ‹çAEÅì5ê4T §›Ghá¦{¦´ß™óøtµ ]ÙàÚ¶RcãPu¤r®âë¢épfûîµÃbjÞ—j-•ÄwcÞ)úàŽ&»¡/Í<ÄÝèæ Kx½¦Ó8ZʬöØÓ.£7A—AxfáF¨Xgˆm¤çDTéÎ×Á»ÖˆÞ éÎ&r²üÊñ©¬n’u[? ^Y„a²{øB÷Æ–Ó”p{:ÉÛš}~Þ•j2Yör–õÐNcsÌ¢[|†A¯wqÔpG £åb¤ñÝ@ú"Yü—[â s94ØSÆÊ&ŽEÉC»Y@u7|ž™E/Œ( ®Õ¬¼X8Æ»v¤å…¢ì?t y‚9_m™Óþ\48Ìän#§mÖ³Æ~tü•Lß÷:±öårÅ\ÙÉ\~·^¬Oí¬YR :‰÷«4ÖuÍ@x0¸ ¸õ–d ºò¸U6•‹1“b›®f“> tD Úœ~n?S¿’¥^F/eÌCZø¯^HŸxý§O¦]-‡¥2TwÊ983M†¨ ׿ÓÉ œQöŒ«‘aqﺠîuf>¼ºö?Ž=Ù^Èß°˜“¸QÁ•;àâútܾۜΦBÔ5€]Ænöû…pn¦. ¨š[.€ÍhI®ën=Ô{´dªˆ×<Ä=_ÕYZ-öIÓ‡Ôg¡ðø„!.›B`’§*½°q-VÒ8v×[“}8¯ÇdS×B}ÞÔ‘ôªûÄí|é /¦‹âô“¹ð&Ü›6?ÚK&Ÿ S/BÔt¶½Oo"·{87eµ -ü8y_]èÞu+¡6©'_¬Ql~ƒêSÑ}}h[/F‡÷R,ÜvУë¹ÙVGÁlo/v† ë–ºý¸A™½±çŒuTÙU˜þi:ÌIuòÅÕõ<3šëÛa(Ïç {*âô.{ÉÎ;`KF9T›w‚ÝA PwOÎ4[³³¶òéä[¸z8êÖu`ñKGÃãQȨÞËéµÅÜÇ·IÇ'¸ˆ‘°ÜI ý>\…·Åøæ0Ã×îbÌòË"°pôfQ ‡sÕ0Á´5è=ŸGǬ;XñBŠúÓm6K!#x}žØ½ìÂ6O~ÚrðJ\.MÖ» …«•©‰Š sµëÍ–6÷¤N㪨o¨?—°Ø“y!Ä ŠÒG>¡öHM”ü´˜M ‡‹=ôÙ§¤©àW³®Ø}#Å–@kËR°6•ŸS‡`â0sœ½t¨™7æãV^,7çJ£àµÒXú¥á)šÞV¤é+ëóûkÑdïãØiÃa"‰žÍ*b91ÐKük“úp0pʰý ß±3•ì[ia¨M¹Öö #ú}ƒq|" HeÍÇ7@Ó¸-È¿H¤O«R…VØ¥d#IÈWfIP¦£*Õ8Xo.öˆ·3¸(«.P»ó"¹óN#ŽÌã&•H2«à õä6, ö_žbJ‚ 3ÞóœŒc¦?à˜†ÄÞÇA×ΆZ,z×åÅ€ƒÃ[Ñ#N“³3G¥“K`’…vi BûÕêÜ‚>ಢ>Í1c:!1µkwZ?N‡½yP‰ËŸ+±¡+Wpëtõ:µøáÌ\B¾¾†ÆÑ¬¦øˆ`ÀÅŽ sa!w­q#|hžƒ ³‘wWœn..G÷ jöó2Yú MèvfÔrj°Mm2˜¡õ½…ã·Z.6Àe3ž ‹»=Y°L8Ù…\,'ÆWäÁmmŒñ ¥B4°j„*«Ú ô â8&6ö) š«Ê úÐeؾÄoà¦>I! ‰·úޏöM_*ñTlÕ’R­èn˜Åºˆ=f›ï~%ŽzÅÌ[¼§I±ÉŽlYÿÔixUÁÞÔSÁWeP3©t¼]y„ˆÏÄä€Á¤bÀ*¸Û0–•ª½‘QÙô¾ž]Óƒ%æ‡qɾû!aÀ*ûPŸ^%”*ÏOáÎ÷¡¢â}™ôà èöh!ë°¯œ¸Ý×cÍeXí#š~KoM)Ç•O'@©Â l±ë­?ˆöÿhÏÞ¯Bÿ95´KrG:æŠû<Ð ††íJlzi2èŠçÐgKNCú{ôØ§É„éƒøê´×Ôõ¸kRVÿÅÒõdᵃ'3èéφQ²$ç’Q 2¢°Â©>YªLÅh)]Î: ³µ{ÈÕ5Á¬Æh ~2œî<‡ìb ·v…çÃU¹ƒ÷ãÚ¯IM²?éñX—|šÔ‘>?~Õ+Îàdù#(2q>ý|£ ¹«D%v£²µ \[{ƒ~p3¤ÏFÔcþµcäÒ:¢a/¾™ /×t¢¯€ÚkP2h»Kæác :Ç5Í.?E>å_íOƒK iÿ¬4‚—ñÕ¨$Þ¢{Ýb~ÿúx2IóDͰÆáY…àaу’Aï<ÔFŸ„¾‚Gg#˜{Õkx’ÝZ<¿Â÷‰Èäòoß|´Oꀓn‹a'¾¼zf(XsØm'NÀÅ ¤9½ž=àj×nì½ ¸¿»H¾66á™Jlʇ¸gŠ#äÙ€;•’P·L ÏIh3Ÿdhü*ÂÒ;ÞC¾M)™w%ׯç£Õ±“ $súæÅ ú>QR<ý‚À%«½¦C¿Øìæ&®Œ.ºøj¢çóòc-o¶*H²bwç΃~šwøo»L#cܸ~ç.ÈX«ÏØu.)/¥Ûž÷@ý =ù…Ã$QU½.Ù²æ§öG¥ñ¹ðþt?xnn ©‡Si§Ä8¸<¨yC ôæ@â&N°=çÚ?L¥Lšà‚u W„º\RÇ•xÔLÇkˆ ÇÇZ2®—,ûr#'÷J >®Áó÷&¢y¹#Øf+°: »º„i6uÝFíN;×ÃÓ7;¸)¯!_k.äˆ{À,wˆ¯«¨*îfá p²Gà3M‰¬ÐÁø^ pü¥·qwž4ÉבGΡ`·8M7|R › E7´õƒ"xx$‰XXOí±›põÐd÷òã8£“”±o"¬K»³Q³bаè5y°ÚÏŒtuŠtæ­ä2Lbï­X”bZŽfà5tßñ]wÁDœn8€]žeõ*Iª!u{޼ !ß6Àsõ!Ì4e2ç9³§§¡ÞÕöøíø˜ÿxÑ=Îq9ŸñeÚXœ±Ò‡&¾È Û¤#ÈÄýK!Så3xòÇ`ŒÊ|›"5¤—m)ˆÚ^…L E–|t vÉX g¯ùÃp8qš@Í­brs£ÃàöÆð~‚ X­µ€Zùfî»Íåû‚¬^q™ã½T§ùÒFìu"uCÚÔÖ‚«H< ú‰hªl"/7ۃ̄jâ=~1'yw5fáŠè¦“Ó1¥¢”ž=¸ Ñ"œÜš,r)þ>Ysì<¤ejã>íCx³Â’~ùü5gíÕ¹8ýE.qÌk©¥[¹#£œ`—é~B¶p®uå 7HÄÔñ»ùU³;²5ËñÛyF¶­­„æÀÕ«jQKˆOi¼ÉR¤ç·Çá͇§!ûÔrr­­š¨@Õ–¬³”c)FL"ò·Ãõ%¸áÒƒö€•i ÂçAò*îðÂ^°çƒ71zû6(^œ cî$Õi×À„^ø¼‚µÙ"2<á ÆªùÃ9=lÊòâ¦K¯#ÑåðŤ'Ú¯œ ~IGQP:›?SÜ…iÑðjbWú\¥/ó›aE{O\Ktf½Bã·Cq}j1ש£90 ˆž&#œ§yÑ©õç¹Ñö©7Vs«—¨p ³¾€ÑØÃ°ý@gV;Û‡=Ù´¸gCàÎ)sŒé«¤Ù|ou&7BÉ/fdÈ&yÁÁÂX7˜ÒYñ ¨/æºí}á“G :áŒÞ`ˆågáIÅÐ#?G†³o9%xð¸jÝÙå×z… çM.Ÿû ² ZŽ··œ‡æI‡Ã|½äÉ%eþð^'ì»ôÈ…Õg›°àÍD î¦æX×´¶?þd¹[cC<÷.ÇÆXtŸùÎç„sÖºâ=ÕJÒë¹>Ø9«Ð8s}ìqh'ë·ŽL˜q 㢎}(8ryW±z1š¤IÓÝÇ€XÁŽ+67›áêÚ|ÖûâtΧó׿'„ì4'Ú;µ .dNð‰ʘ“n‡J'$¡ÊšÂÒñ›ˆ«¬,U{e¾Û(wZ÷*W~M¸öYb‡sÖ¤“‡ß°Ó ú`l0Ìxßz?6¾ó8úB’-;÷˜Œ_ža}÷Á€A=ÐgR%Ö­|Ž7+Aâ×0nÌrCèØ_žš x´ë½Apãâ B+»³õ’°ûøž¸*Þ›õw˜B†Dª“À”Bά`6Ùs´UoÛq£]=ƒV€ºbܾÿn=ý[cà˨ó0t .M?zų6òã©suC×ÀÝö>¸¶ä<Ç''µ8ߊHN3µ€ˆéˆ`Ìëû¸5­_ ãœÝúÃÄ™’¤zðô/‰£tEQã½åh@èYâ'Ý@Bö\Æ;¤ŸŒD<4±Ê*»1Ÿ{9¸£à9~yЋ,è-CyýÁRo5a·m:=|]X¯Ó=õäïf°nÒg㧜ڠ.`vBŒ³•^Ì-,€òbc¶Â–]Ö©b¯šô¥{ ‹t¦aG§ƒÆ(Wx‹»¹m²ÇɆ¤à` óh8·ß] ®|FÁ®CĪkØöeJÉŠa{H Eç`~Ú}ÞX†wÂkÈË5(·Æ»Qü˜¿Ý[zR¦$¶šõÖ@l© ¸¼€¼:*¸yé"‚¥,'°˜»OÈîâO³±nƒªTì€n]¤¹FÑeP`ô,:0Åw Ç—g²²Î ¿®‡kŒ8³‚Ììª뇗À çPñ}xm"OUšÖ€wš5 +¹³æ­À…^¹ érõy‘Ìhõ9•rÞjZ¬ÈZ’==]Rçsú’ ¨Ô%O"XÅérBn¬÷¡‡ðöËà‘PË?<®‰ÌÔpƒÝ‡ThúDœj‰¦( ^/ÄA뇲§_@»Œ²ù>Y°ëÃØ>l&]¦Šçwq+|4pÍ'9Ö°Àü¢ Ø×' ½¼«ê1^³žt³Ào{ÖòÇ—Ìvu-Ñ8œÂ PÜ€)Ù:0(Lj˜_œåNÓ¹©ë p{—.¬üÅŠþãP+CãÂ]à¾~…°n‡§ÚðîéWäÍYÎ}ž"ΕžÐ£N}bÆ U°m¼Æ,Á¯ÙÞv3uòeûÇ0¯BQ$3¤ØÓ¬*Üõ`<¹ ƒ/r<WîÖ’IX¯0åe ð^Öþ‘égˆ®FyëÔ—*$úaÝàÞ°´t4Û³nÎêö‹ê9·¹¸ü´µ—] k—m|²” 5ÆMW pq’[Ó³å,¥š¼Ù_œó±°ly’Í£ &\„‰GöqÑ–oáä<'º¾,<û]Ïô”IoáÖ ä¾Au] ÎpÕ=¢®{L©E“+Þ4ÎìäƒVÚR.¾Óîùî‘,ðãBÒ={ßÀ}Q¼Ï™MK ‰Ø-»žª ¡Œ#Ô»w`ê[Üp‰<©nëÉr¯¼!ïGƒØé"ˆØjÌ›r‰«uç¾Úoožçö4wä®ë@*}äèq‹\’8IŠ­\}‰Œˆ°äËŸ2€ý‹}øåKvZÉp›‡b·ÎaÂHö|)~P‚_o<ġދ ¢Cp“€åè°¬Ïþ\§5;õ2šžèÌT¯(R}nÌsç&̲^CÆ:Epix؈žaªà=w«àøÆwPrbV5 vê¹Ü0uEôÔá¸Ëö¥µ)$Ê¼ç° ¸¤o5¡úé\éôx×ÉŠ:z…ãØ%ÛÆ¾›‰†ºUÐMFvz²õ2¡ËåèPO˜÷U1š;àì6¦Çž!Ón-%†e/ã¹Ð±º7ö\²%éæ³óauònؼâg{ô$çyr-7{–SæÔ™‘Ï~βûjv Î^£!ƒ›"q„áàSÇ× ñò-ªDÌÆ<ñÍà1!¼ r#öœ¹†'u_Â2ñF{\ñ^†õ}€ev¹>%&üÙîLqN1 Q§usË`fðB.fƒ XyË£´[oÒ —ÃVó÷šñp®y|><œï²õ™¸aëFt¦RñÖdë¤dè¬ÁLÁ{ó’1¥—–U²ÛïÞ:“žÏ£éR=èÅ ¡œÒÄk¤dðl{Z6ŽÎ?hG¯†ª±4âŽÏËæcæb+öÕ&‹Š<€'œYäÔàr';ý«ŠØc¶3ëãEGùÁjþHþHÛÛpûÉnè˜#¥hJloúÙ5›ëF X×å’³ŸRëÇ¢ìùš\Nï¢=nТ3TÌ!ÅÑË^ß„GŠj˜5¤N>†Ýì×úá´¾f7FßéÅú*lÇR†,ÂDÏ>¼Ó µî@Œ˜NvM•—a²ö+˜›¸•£U0B)•Ûÿø@5g&|&Ën«@{ýã Ç™â wÅX¦ß¹rišß}×l3ö¿"ÒŠ¬o·l~ÑŽ,†;`[Œƒ§ÒÜÏ}Xàš 8¡—ªNS”֌¾—6öâªYNüJ§Ó¤—lw`Zèãôh㜱ˆ‡k‡‰Ò=©¯9Éõc¸z™°Pz螪Cëz´×އ»€ýGÔÀíÒWûš­Ï°Ã­õL™­.3$Õ]šàå•N>W€{â¾ñ7Ÿð„Þ<áz®ÇOnÊ—(´I‡w݆b}®Ì[t€Ÿ3òœªp¼¿Ù•bpf[•˜qÎZèo3„6Ü?ÅmÊÞÌ%W ‚D;بF>¾•¥ë‡Mc™< ¶Èø8¬Nš¯„u~ÉÍ)àSãÊ)oÇ=†I9 \(A×½[CNMïJa¶(ÕÍ×ÂÙª€×s¥h’² ë4x;ôyÙ ä§òaVÁ)XL§áª²ûh*{¬ ´o(¢Ë"O Bš—°Ã˜Ý}؉•l~@ð䘪»buciGå+8dÐü²eì=z\ߪm*ø¶0ˆ:¾kkÀž1—Ñ}AÛª;ž~+ùŠ[¬îUGìWŸÉân^'~[§²¹¬b‘ ÄûgáµE”< ¤;¯dÀ¸º{øe¯ q|ГšßŸF}ÍeÙƒãîl­„v/ñ³Äl š¾îÞù™‡éƒ¯f çÍcs-D•o[Ȱ½]‘Ç×$sr ¨á• Xﻃ’ Ù»raF•“§‚a~Æqîþ…znÜ9*—Б<îÕ‹­Q`Mæ„ö9»kËß’ªÆJ"³àê%‚9ç°´ã©,µî2˜EÉá†o›±å¹Ñ3 X¨ÙŒ¾XŸû)Ó·«F¡×…¤¦K'œ;¿’ºçJRÕ«ÑOd>³Þ@k2\àuùv=ªîE\ffÑrÙ“}–ðYZ'éÅ^¾ó©z‰6]´G”)© {øpô9¢ÅÖ¯f¸Å?“&nŸÉzÞ5¤Og¥BÇRfp«}È:ÐÙ&#€8Åû90å‹­²Áô{ŸÃÇŒA7Ù><ø„s·RfuÆ[ÙVÁ#ؘ ‹tÓð؛۸Úw{µz>¼iç¼e’“V,Û¶•’C|Úü^†ù*¦SE«EPT;‡N#‡`N¯« â;‚{ù1W¿Â„½5YÎ:÷`çwp®gÕ@ùÄvR¡£ƒ­hÔn[P0¹‹×˯—jåø¨£*·R8ßÂ>™Xä[L©Ö ḵ„̾Ã8³¡ÔÙv(6ãŽîIƒ8ÝBàö[€Ä·\·p¾à@Ô0\¼àì32À2fÏ|– Ð%ËŸ©hvaŠƒzSõsóqV±2Õ=z˜;~÷nl°À-«Ç².Ê´£}Üû° Î⤷HApuIìH<)› ©bÇ`Ýó fЦ¢ÙZÆ·_Y/pk—átgšlm‹]ý¢póñ\œyQv ×açõFÃèžL4ÖœªÙ®å^…Œ1h¹çg“N¨Rç²z ÕØã »ÇUÁäíIG›Pxûr§p\ŽÁv´Ù¡›#I³ M¸â­»>z+ˆ>æ a³N‚«ôPÍE½ÎâÔÙˆG‹Â=%U˜\]ˆ+§b­è>YhZýŒ;®GÐQªæy¦ƒßì}øqT.Þî…ë·ná²><ÍØþtÅ”)䙿-^›k÷?!ÃÏHÁç5;`ÑÝB\›áL÷öïa%9:Ƨv®ã,3õ˜ç ðpFu:µóùq¶]‚oÉ`xq~ Æ™xã^HÄKOÄèÖM\FP=_ÜX/eoI§t³WšÚñ$¨¯ Ý) OËpÕÅ¡æûñ³†º?½,ð˜1ìWÆuƒÖ Øõ{ìHi9kLûM‡uº¢£)a;äÞ‚¿hGü< ðäj÷1×ßiúg®’ìN±·ÍRL5³8÷îô[x:Žx1˜;ÝùäÆŸÃ¸1Çø›ŽŸUջܞ«¯aßñhNvø“Ü+sgÕr1ÃV’ëV Ã<ºž‚ é·EA0âÔrÔp;>\ãE)øºé ^)Ê®‹|Äùç§Ò»J/Ðån.\Ûl¯iÄ}«•ã®ï{§?Aós &9/ƒŸT‚Í"g¸[³¯ l¼?â{#:`i-|~ I]ù×Ptþ2îðŒÇÄnæYÒñ±°ŸËN‡n;sàÍ”y0uvç°i ä¦ï!Ög2ñ€:Öjn%ŠqvØÑª‰S#º-Á ¹mXù­?ÞzÜÐîJ{v4dÇ?ÅnC˜E‘ŽÓxÅ¿¦DJYÃÊßÑdW_ºýåWp6u£WÇ]#‰çÍážêYH± )Oñ6/v>ðznÆÛêö¤iÞ92â9aÍ#'£cA PtaUÒ9 š­ÊzÞ%Cà3_Ê‚ÁØcûØáPöƱoÎd]Åf`Ÿ@v[ÍŸÎùrÞ…Ì%Æ'‡·¸qóJô«Õ‡í½kìw¦Ÿu¯(Z®É1ÏpR}Ü­ë[89“ż¨V® ;½ŸónõÂcÒ¹^rý¨ñ#UÑ“ RâÊ´²`ÔÆôÌÔõ™È™”:p&Ó´¨IlUÈyåpÌÙ)øùf*FsgœuàÑ&œËéP/ÝJô÷U‹WñˆVr—z_åÆôd¡•:hîeÊìzÊ0{¥;a)I6-åþÀûËD¼Þ7[°&x‡`kÿsÐQUŽÞëF/Vç^lºÛ"Ê tÕvÎÚâö¿îƒoOÁ•o`Ó-Ð{ª4ÝíÀ:¯Ú"OßAŃÝ`²K§ëKGìXÃ-=:ˆ®˜z9: ¶/ÝCß?€`Gyº£r<»dTS×§` v쵫+rq#uC’UkÅ©–Y2[»r×ow}¯ˆIƒdÞ\ø´#h7Mf%Þc?m†ÀsðÞCX1-Vȃñ#ëprA·bâhÚóh L~wûxÔ•m^GRöÂzçˆX4’Œ±îhÝ<¶¡˜©¿ÌǾò:T°E‚MÝÕ“¾~+E¾jßýa/æö>’J¿”Äê€ù$¬9‡í W…wfݨç(EÖX­B½øEšÙ°u±P,ú K7ãxzŒžúfÅn>>Ó¯çÑÞv‰XgȺ ¥L|lOˆ^A mÝhAj9›ôx”ý>§q¬\MŽ“pÔÆ±âðyŸ%uº—ÌÊÁºåY°°C69¢PD“§…–ð£¯/§+t ÿã*øª¿Ì·Û°ç¶Èîgãâ£fèSbκÝ~ ¦Õé,àÅ Ø ëŽG5蛣Izº°’Ý0êådðñŽF³ü}`Ø„Ë/_ÃÌáýÂ/qq'öõí†ÇŸ‰PE‹ã0ày¬6O!iŸ/b÷¡Nô±ª2µß u"Û9¹‹°¨oO¼™@^S`Û3 pÖ6îz›bŒXÒBK¦þR†fUW}uZ~ßB|Çcа8*åÜŸq …cؤ¹°LµfîRfÏ×7‘kÊꎼÁx÷Åòz™5ø¤,†œè|öb”jÍ—9#Ùã7¥lW• t|3œ­<,NƒóÆ0Óèsà<âÙý  6W)b‚÷(}Rö)O°2#ÔŠçFp¶v¿îÙç€3v>%žz–¨Þ¤„' +¸~å,´i<ßäÀ{¸}¿ †E¿ϱ¨á°ÁÁ„íïy ýì ?Ø™©-½À]ø¯l0Dz•xÒPWiœÇ³k¹aOE‰ì§­hÖeS„sçû`ä&³ìãfo#Ž«ù¬!+ †/Çûg’áî‚§;n@Êá~§ù"4¯†ÌΡæLâcæn5Á‰wç@«î&=P7Ï+#|áÐîs3hYºŸï ×fv°.ºŠ©ŽÑ¤}^6‚DÐ#xœý†LU2¥¶oD@>òqÝEÃ.àæ6 es™D§hßÛƒñ: ó^Ïnä5ÒÅ/öäíŒÌ«Åœëásó]¦5<ƒšŠˆS¥¢úúº"»ið&Öæ²SÞÁ¶«…t·¦?øI‰[’ÌÃ¥ÃçÂìÓ0Ll7tHÐ>ÇDs‚¡» Ë SLÏÌcg @Þt;Õ½?ë0¤dSçQzlL÷¥°-~?”Ž¥®3A£ÕDn‰$;q›âÇt %#‹=z²ãŠ¢ðfD.¹°a<Û®šEýŠ¿rgŒóØ©Iyì¼B®ÿ‚{“‚Ù•½¾0Ùq;W[äMKބӮªÉ„Q7HæsÊ^Ë&ŒwÃñ½^ ^«,båç1WÑ ¨6—:ˆa—Þ°çZy˜Òg \WíBת(ÃĹ Øœ•Á‚°¬™ôÌé#Ô±)7dV)œð[bNöÔ IŽjOe²ôÁ•¤ÓéM,iŸ}ìµÓ#Æ‘ÄæÂrçx/\qÆúN¨zæ½ö^?IÎ]8€'FwÃ8KUzcyG6éöDôXû_=€ñhиX„›Íû°•·Â p¥gýI:­Æ X£¦Ç·Â1³\éþçǸ²µŠ0WêdÆl…-ùçÐä¦>«®—AÛšÓpôêÜ»"‘‰+ Weù¤¢¿2U+¸@’" hí¾‰ä\[x]ù:‰ß;tJ ȽðpÑ <­/eÁëí®è¾E›NØGéÎzÔxì8:l딼}¢åÜ2ÑØ¼ƒt1?¾ŽKu zÃØpr2–’ËÞ¢pé+ê-Ä@j@nÛ9]6»g¶I­Nî<y:P9´‘Ûâ;,÷ÈÁ«8ïUn,¸ÛDe ö¹~ Þñ Æû¾r‡ïèRÕ˜#¸×ñ&@ÆtiÖ¡Ÿº½æg¹5ã¼å’¨pò4󣁾 ÌŸšw¦ ʳeÁÓpîr)'1#¢©Èûû°|–68 ›ÄÊÑe{9ÀÙ©ðÎx%v­hF±Æ…ôĬBÒ SFbÃ9R h Á· »¦;óˆ^Qi8wë:»Ï›OrCgÆÒX)¢g–{ Tf>‚ØU¶œCzO®`N5*†ߥ/&«ÃþD-ji^ª×÷r‹Á½5‹8·cÃèÝÝHT6¾&ýjHœ]:áµ£íhG;ÚÑŽÿsˆŠ‰—Ÿ0.>* 2r\ÔäÈ„ˆÐˆÈàqñ±‰†QñF^¾Î>蟫—§Ñ?‡±VBÓÌÂÂÒä»ifbòݾ<3s+Scssc3cž±©‰•¥OÇâ¿9­¿‰Éñ q::¼øÀð€„耰ßó%ôúgˆôgâ–B@`dˆadLÐÄ?GK[š›ÿnù[[üªüͬLÍy:Æÿc©nƒÿãåߎï°!4m„—x|rtÐ^ÂK¦Å®õ^£ÅΡµþ‹´^âÿ™ÿ[ðOµÿˆèИ?ÇßkÿÆÂÆþ7í߬½ýÿðKŽ Ñ±×‘õø •ýwËÔŽ?ÿTûýc-ó¿nÿ¦fÆ&Óþ…vííÿÏ@ë¸ç,¼|¢ƒâ‚}<¢…¢<ž|ËØ&öƒóxß„àýó„”lµŸùÓ®ÿËð­­õ`ªð’öõõpIâýœâèð~̃ú /%߈¨ØÈ¯¸à¸`€XÞ_¿ïwæI¢¿çÐ^yÚÑŽv´ãß„?£÷ÿ=‡öÞ¿íhG;þM ÍA%Gª^[·˜_÷š¥]®IÍ•µUÇÜZîÈŸm¹/ôpþï¹ÿ»ÓÓŽ? ÑVÓ´•óÛð¹møéŸ\D»•·ã¿-ùÜ2«’oå{~rñ6ܳ•·£ÿ? ¥®·ô'¿ÔûémøÁŸ\D© Ékoÿ- ÍyK<¿ýyeòßÇô¼ÒûßÇú¿¾ÿwËÙŽÿ?áàêA[©øåšÿßC$”Ç“Íió7Ï/WßW_?/И8–ü׉ Œ‰›¦–`bldbbamnýÇŸæýÜù?R;ÚÑŽv´£íhG;ÚÑŽvüIø§Î$ü‘8þÎùo ‹¿yÿÇ̼ýý??N~ÈÙ ©„_Káò~l¸J´ºËzGDD·ºÈø md¾ß8…Äñ~þoô Z^Ù’ûîÁ'$(&.˜÷cá«ÙÒ›_=(òÏ>$Ójv¦qqÉŽ1‘“£¢[¼ÙÇL†$,Ñî²~®Îã|¨ç –3®Ú¾ÂÒNÐ ˆÖ ^1¡­ëô€„ˆ˜–#¯söo,¤[3§%ì.-r¸zÇÄG´>Þö¤c íóÛ)›ñ›)k¹ä‡LˆN˜54:"!¾%u¿Kë&M ¤=œ©¯«ç@/Þ6¿Ÿq¿dž~KʾgœoB\DtXËÿ}b¼¿Þ×i ø·S`ó»)OHŽm©Dr­Ñ‰ù„„¶¹ýþRMHlLPx‹ÛP?ÇV»ß.ËÂý¥,ÅܽµHîõ«½•¿_jm£×ø­üüÿT]ü‡ íëèâì4Ô½¥*ªüLE|PxHðäïmï?&)¾A‘qmÒâ©£ó3-Ýé q>^Ã[¸OL¢NhdÀ?V¿Æ· ¿Îô˜³«zD…ülö!q}ãõþP®ÿë2ÿ£bKyûx¹9;ú i︘ !A :Á!Ñ-£sÐ÷þJ'þûÿ)òÿªã•÷qvöKÎ㜨_K}ïí¢#œ=„è$†‡D·°@alQ!ñ:±Â§#‚þ¡ÄHÿuZÚ»Ù¼›ý#5Pá—ÞjœŸ¿wKªÿm—¥Ó*ÇD ü•üŠ~Âèëèåí<Γz´$@ÇOXãƒbbCt¾w}C à u†ûúøõÓæî@ÿì¾@X¹¾ò~Î'D~+6^ëƒ=‘ùK÷÷_ÎAZ®ÖAó/¥ßb×vúk‡6ºH[û6}h[û6T[ë_·ý¶nS«Ú:þm‘ý⺤õŒr~K’|}=þÚ¯²«¥ÉüÕc™Ÿ¡‹¥üÈk qO2M˜*¡)#¼dÓþ‹ÿÿÆ?°þû§Þùo‹¿ûþ¿¹É¯Ö¦&fíïÿµãσkï2”÷WïÿÛΚx?;Ó–®þ¯: iÞ¯Fó߸þ“ñ´ÿÖ¿¶ÿ9Ý/-ø'ô¿˜µ´ÿvý/ÿóøåÿOÿ„þS+Óöþ¿~Oÿ‹¯]ÿË_·ÿ?¨û¥ÿ„þS«öùߟ‚vý/ÿ·ñ‡ÛÿÔýÒ‚¿£ÿÅêo禖æ–ííÿÏÀßÑÿò}«Kœ÷›ú_$ÒZí3xÿùëœvü6~¥ÿe6ïo5´±ïihÙ•ü«yRK Ý„—ñï9~ÿïOHp;ÚÑŽvüáÏê{Mϱ½ïmG;ÚÑŽv´ãÿ~9•"ÓÊMÚðÀ6¼¨ ?Ò†¿ýÉE:µáÆm¸g>¡ ÏlÃWµáÕmx›¸D^ýä¢]Ûp—6|b>³ ¯hÃoýäb2m¸mÞ&±Ò6üôO..Ö†·I¯¸wÞ&½âsÛðÕmòð—ù] ·mÃ[dèßÊW´±o‘¡W+oÙ’QoÍŸ–ižr+où/£c+o9Ï£ÚÊ[Λ©µòÜ6ö›ÛäsË|T®•·ÄÕ½•š=ZóP÷§½èÞóN-¼åÝÙVÞR¾­|¯ÐÔmå…f×ÖüTù™.1Ç6<¡ _ó3Ä.ÿ´—ÿ™â6?óAÜÿg>ˆÇÿÌñ¼ŸéßÒšÿV|û7crôÄè˜Äïç̤ÚùŸÏ=½|<¨û÷ª ÙÎÿ|ÞV÷ÝÿEó{.ðdw l[úˆ¸‘‘!q:­p‰›’¬ã¯ã—ÙòÍ÷“Íó€K@‚Žc\HÈDŸ€àˆ¯Ölbâ’¿øþËÞ>^ƒ|¨‡­Ž‡«§Ÿ³Ï@g/g!Ñé;%$.¾å|»…¡ŽÕ„ÉÑ6VzyÊÑËÃz:Ùê8'…MN Ö‰‰¶Õ±±¦Þ>&–¶Æf¶f¦¶æ¦†Æû€cLTT‹ØÂd„èDDÇNNˆ× ‰ŒŒI´ý[Ï::ñ1“ã‚BìÍ‚L­Ì~Ë= :!Þ^ÿ·\""¢BìML -~Ë5:(< Ú¾¿Éo¹M‰ˆÿýãbbí L~ú˙ܟ⻻z:·xÕ Ši‚—·Ÿ½ŽŽ±N_Ǹ˜øø ˜¸¸ÈoÑ6k}<¼œœ…^Íuúšë$FDÇ$ÆëüÄ_{õñ#Ðè¨Ø$¿ÂOŸ.ÔÓ^ÇS§¯K@tô÷ƒãQ11 áBöW9¸úùÚ똶µòõvv´×1ùtúþ"Vb\DBBHt›`¼©¯¯#u·÷×éëØRØßôãm‰Äˆ„ ð6þ©·{«wǀȈÀ¸€ßö&ÅÅu`KêX·þöM>oçá2•¯÷¿¿åX˜ÙšZ··œö–óïj9ø[ÛÐÈÉñá.ñ-äg81цÐRÃCãb¢t¢"â"‚[T3E [XDtBŒuõö50Ðñð5m–ÐüË- ‰m ÂÔÆÔâG¥67Wê¤÷ýŽxCY÷˜€à_||—©Õ¥%*ÙÖòüî(¬‘ÂêßïøXaÇD¶V˜6.±1‘qS¿çkëïç2úµ¼¾úÓ. ¥/–VthDØä%aok+´ fY´ðê«gbfbl`fj&tÑ ˆ ‰n‘[˜Fá1Q!Fq1áq¦F¢‚¢& û*#êîAý|ýL|BÂâBâ[z#OskSK£è–ßq&–±q6ÖÂÀx}ÒÙ,*^˜‰!ÂÊ×&¾Ð¸I~1‘öº&Æ:ÂâÓÕ Žˆûq¯¯û« x#{tXPK¬–ìmãûWeoÏ÷?%ßÿÏA”Ç“Žãýø¿OÈe ÛðG?¹ìÚVÞŽÿ^´äË Ž­üñO.³î'— jåÿÉhÙ c¼Ÿû’WÛðç?¹D›½N‰þmøˆ6|v¾¦ ¯ißüä’Úm¸cžÐ†×þäR-òÊ|ïBZeÿGøWFý¡%Ÿò~îKÞãýÌÿ÷¼¿ìÃJ´ìu¶î·JXòþ²+ÐÆ~ï/û°[yÙ‡•h)ßÖ}X‰7¼¿ìÃJöæýeV²eG¤u_UrF~@höûÁ¥Zäªk儿ˆ\ºà'—ñúÉeåZy;~ÿ{¹ÿLüÿ { ¿Çy?ê_K?Òº¯/ùBhvøÁ¥Zt‚·îñKÏþÉeømøÇVþ‹ÿ„=Ðÿiþß–YÿøÓö[_]-t\òÝ,Û3–¾ˆJñtÝóÝÌy±±Å”;ûf‹)/;ÖòÏJÿ>ÿÿuµàïéÿ26ÿý_&&íïÿü)øqöP¶eöWú¿ÌZÝÿJÿ—t‹ž‡ÿtý_¿R"K½½ÇySêÑ¢¼¬üE}Nl@\@THBH\Ë\ñ?FñÒßKœ£»ë¸ÖÍMá­šðV'¨uƒ6>dÒäè ÿpER¿N°„Ü]¿+Þ*µ-¢haý¡Äüë2ÿ£bKy8ûúÒïÊXäÜcÂt¢BâãÂþc¤uNÐi£­KÆË¡EOË8×ïuÆ+.","Z˜ÅÂôx¶¨–únÿ÷—àý5þçWl£~ò‡ô^?UöéDëôh90­­ÓÆ«Î÷ÑêÓçó'$êWuGÒËÇukKm7íëûýßaÑÓ‰ù^0?¶­Ã#‚©V­. !Áÿ)5ì×jÖ¼}\…Éòk9z£äÑ*ul\„Pð„äÿ™¥£L¼E§’ÐTó‹æsB@Tl‹n¸?Ò’ÛU‘ýKªÈ¾ýP€eÖFˆßW€õW#ù_‚hqúÕ˜ø×n=¼ˆ´qjÓ…·µþ«¾²­ÃßöEm]6ê¶¶m›E[û_ªÞ/vÿª~+‘”å)žòcÅ%™òc-òcèÿ¨~«vü×ø‡õ…šóóõ0ùgâø;ë?SË¿}ÿṲ̂]ÿóÿl^þµ.îpö›XÍËÔ³xÃùÞß:Ü8*õè›Èèð…¤t[ÿM*ö®Ø›o¿áù_þEöžÿÕøÿPd¿áù_ÿEÖ9?õh ÏÑÔûƒÂxI}NDþÈ¿?ܵJé®±þ³¤q/±Å×yÁå/µ0WÞoæoEöžÿÕøÿPdZÈãù9ˆu楸… Ko…ñÿjüÂ™Õ yô6Oá­O“öY‹…p¹ŠæoEö7žÿUü¡È~Ëó¿Š?ÙoxþWñOäy[Ïÿjô¿Ù¿䟊|üÿç?õüwÆS3«_ëÿ³01kÿÿ üXuˆ–i¿ˆÈ`ßð€Ø_º,ò€×¢{ø»ÓO½0ß¾kÇüe%!óÝ‹S@B@kÈb߯6ËËí¹å¹ÿÅKË}KAKþ5þwüî–åo;´Y=ÿá(EZeíý[!·YD«åýžDÛ¦âFº¿ÿ{žt~äwüCžÅ[=þÕBRhÙA¸Šïñ‘ÇÓÍV‹¹¿ã©µìš¿’Ê'a^æ ÝßñÚñ?‹ ÿo9NìçCÝÇ wõtòþÇõÀþqý¯¦æVæíú_ÿ üåÿ‡õÀþqý¯ææfíã;þ4üžþ×–Ô–óC-ã`ˈõ›ú_ÿ7«Îù§ÛÿÐûÇõ¿ Ûûþߟ‚vý¯ÿ·ñO·ÿ? ö¿nÿ¦nöëùŸ¹±I»þ×?Oÿk‹ŽWI^ëê¯u ýËâµõSˆ¢-æÿfèÿ—ñËÙñÖz0•÷·:[×ÿøÕõ§€îº&ñæ&ºÖsÿ@-¼öÝþwÌÿOáÝjý0¤ñ‡©ôà‡©Ñ:Gînýg ÕŽv´£íhG;ÚÑŽvüŠ–} yÞÏ}^m¸C>¾ ŸÙ†—¶òv´£íhG;ÚÑŽv´£ÿ~¥Kü—7µÿ7¿âÖŽv´£íhG;ÚÑŽvüoÀzOöxË\Ü×ÕÉÙz:õ§ÃñÚ¼©ò{öCÝý\û;ºPOOg÷þNÔþ9®ÿ³ÙÑŽv´£íhG;ÚÑŽv´ã_Å?­ÿé|ð¿ÖÿdbjjnúkýO¦íúŸþüÐW ¶Œ÷«ïÿýòÑοúþŸrË+¡ÿ!ßÿSn5ÿîÄZ¾•4n óq>Î…Zã~|&/YÇ#$ ~r\ˆN\HhHÜ?üá¼ÿöï…ýÍ'ÿ~õ™*™–÷÷$o,C¢BâtB[S¯§®Ùò1´„ða# Љ Hˆ‹Hú‡R&ÝF¨ÿê“€"~;ÉÿöÏ\µ|ãúï~æJÔ¥E=GÒo'ün~ý+™aq>!¡Âbãýõ×®d~)šäߪ~"Éà¿÷u2áߟêìéØòý(u¿ð6uU篢ùÃ_*k/Â­Ûøþ{íWö{ûîêäç"¼ëêØÚH#‚Âÿ¦íþ¡Âü{ •·ø·³Èë7³HäŸ-å?Xni/ãäN>\‡9ÿ?öÎ.ÆíÿãC{¢”KFÙµ+1É93-DMi¦Ä½WwÔTC5i&û%Ù…B–$×iCIdKJY#ÊVáâ^{ö²û}Ÿša¢~ÿûûs>/ã}:Ï<ó|ÎóœçœóÌœçùz³¨PA½üüø>bÁ>=X(ñéc`·ÔîF¡Ù‹õU?wŽ«³‡$ž ÕˆðëÝ?ruü´Ef …2c UGgæ`owWª.ªº 'ÒýkFŒ_ßCòú§cêÊ zÔkZ«Áî®Ô­šG<þ¡Â°9nú¨¨šM{³™µ9åœWåšNŽÞv®lOohíkÂ@êSgš“#ÝG<*¢ÂÕÂIC³à° 1üÐïô((Jw='šˆPÕOûB]ÿŽw½Ûë-ý9¬æo¾˜.øò©ví{ÙÏržUÙ.TU¡jÊOìšAub"é^—´¿ ‹MÞöå:M®+—éìMípéÀƪïb¡Ì×íOÄ‘¤5¨4rCÕï¸×Yõ“^X¾Ž29W;oŽÛˆÚб˜"‘ÐG@EtþxÐ%§š •VR‚o‘^R26“ëáNµflž˜ºª†ÚÌ“”‘jû& ¨a\–†Ê—·A…mÑ4…ýz×jïêææìà^{ í…!!P$'_#zˆP,Á)@—¼§6 yÃå?R ILee™¼zc*×{iSYök‡{¸f5ù+SÙÕê^íÔYO~<_gaÝajE²ã®:›ª3Ì‘]ò™±„ìâOújÙ…ŠŸYA¾‡‘]&Û’×ñðiYç#儺{J»§êTAé¢ó’gž¥}9‚4u¼ê<:´µäÂi5Q¤›ÃK!¼6’´õš •I‘FSªÕ€ê@uùÁÔûÁÿšÑÈÌó&Ðÿ!þƒ ¡Ûøò÷ÿfVŸÄÿ²4ïgF¾ÿoZuÁ’‡Õt,Fщ!ã\ì¤KŸÕÀ¿e>çܤžÓ+Ö}xŸäùR ü[JÌbä-Ïzßߘ²$zF0ϯKa`™íÎVÓOuí=®³ùê÷Kãù~7“Œ¸F­€î¢¢PàØaöžF¤6gc_Ù:­¦-m<–ä}ÑøgøãÎÇ/½"üMXŒ#­Ít¶-öRIx^ÄWûÙŽzeÎ> YâuÒ8ŸWg.p½íÔQw{t56²§ïÇßÉÞÊ75o |whÒ£ hxΠéÐ*,à`1{ pô+¥•êÀ Å++‚K’zv= ÜžØxÐkèÎ5Àó¦e¯Þ+U‚XcXŒB¥+ï;eE÷Z-éÒ~ Ð…ŸáýÈ·µKôþÞ¦¤r/0æ¾_?}`j^õïÀü¸ùùËE4†Ÿ Málc1Žj ŒÓ¾pÑ©Â?ï6ìåÜ?ínH&°„;uO{à}#÷ÓXŒ“Š ë¯*3]bì›q¨t+pØB÷N@Þ ßàÔâ”àòÖJOû“o¯b¬>Ô3‚¼¼*û˜/ð‰p˜ÖQ㔺c¹—Ð@?èhà€çïþ~~*ÚÄ (ØÜ%l?pæ´Œt`,×^q60½Oé°ÛÀc ~Ë×.W_Þ|‘±À  Œ7µvLöä¥ì(â_Àñ,òÒ*B @ámïyªÀy9ЬŠÐ=Ü-ÔùÕxÚ1!a5´ „µ"õ€©„”äÆ EC£MUgÇørý 8 _å\Ööú•4`R¥¸{k`nRPðRüÊÌKÀÇa=ÞØ²§Õœ³í6»t©ìÿºì¬èv.Hï0 ùϵupõ(ú×ÀæÖ¿ªÙMÏ^½VRÐ X½Ç¯åãŒfT5÷°‡ÿüµÃéÝØÑ6¥0¸r`èïÀ¹GNeÿ \ïÝ̘%~4$ Xä<3º%ðï®ÚÆß¾NÐ/e1ŠuÏYù '¤Å"F\Ó‰FÚÂZ‘z@H‰ÔBJrã„s[u,üs€9wó¶/æVÍ>ŒýÛöô'*ã'ÎqvvR=¹hM_Ó¦ý¢ÏÏS~§÷o¼ œžèr׸rúUóDà6ÏÉ-€&Í W”–«œ>¯èêjÍb”¶Ìܵ2Øm‘cÅ{ ­ÏÅn>@A`!0HûUfà컑¯£€kwb=fÆn[8xr<*Þ¼9¤X¯3ð }Ìo³XŒó:ÕOoûœžýpÐ.±m¿íÀŸ§'N׊<û™Œ4>®Qܤô3÷–ßÛ<›ùû àÝEš½Ç±šû¬ØÁÆ<»/ÐB;¶ èt—;ø5—ûW”7prì„óyÀ¥ãUõ{“†Äò#¹ôÞ©«÷=ãv±ÙÍb\TÝreÖO@útáñÀþžÍZߺ/9 d¸>8£<ó¶&pU¦£©¸=ò„ ÀBŸ€ƒ6À«ŒWJ«µ#]XŒK­îvŒñvÏM+;‹ Mž!gÆ.á¥Wçv~òògikEê!%R)ÉÎ9üתó˜l®›Í4ý¯·AÍò±¶²ªgþOäæÿ˜[XYÒèVÿ`9ëÕ>ÿ§Ç¿vÞ—dz_côåù_ffVfVrÇ¿¯…¹™ÿEÔd²•Ì/EÑä`jbuÍdoZíÝÒ ¨‰çu¦¸J§©þ[Õèó_ì'lä6¾vþ›YX~zþ[’ó¿)ħÛÒÕ9ac¤Iõo퉨éÔèó߯ñgåWæ[ZšËŸÿVÖÖäüo Iú=ê“On~­¥"­öŒš>Nr÷•äÂGy®$:íäbè”4Þƒ¤P7%«r8.NÁ¾|êéÒÃJ§ÕŽƒzÂK‹# 仆úòCù¾.¼ZÝÃOÝF K“'ÑêË$""""ú%é}¨Ûêå{êE§5®÷ùìUºZ} HODDDDDDDôÃ)׎ésH`ª–ëvqvÅN¦ëfCÎ9½©/Îwðš ùWƇ›t|3½àñÏKUñA~iÇù^Öߣ²ÍneÈèülÕòÿZ­r¥šz¿)òª‚|e z˜Cþãy¦Õjò×m|ù/Ÿu†üVÊ«¨|“˜±Ù?êH΀‡_9b}ð£ší^zéù~Ó·xB~EÝ€zDZ¨í€üIŠç_>€ür‘ù¹”šÏoÓ9‰òórâõ=Ss‘òug¬ô©úÖû›è»“ô'< IZC&Ý^&ÝU&m&“(“"“æÊ¤“IJÒD ÜþŸå¿QLg®1sTMR¹)ÓMQ¶ƒ˜l®¹$©i ™t_™´¥LÚJ&ÝO&m-“î/“ “6—ü¶%ÿÔ·Vìal×ÔÔh*M™nÚR~÷šÝÒ4ê÷ êÉyl{c“ã`ß”éo½ˆˆˆˆˆˆˆˆˆˆˆˆþWÕèù¿ˆû(ÕWæÿ[Yšõ“ŸÿofiAæÿ6…jgÞ´ ¾“®ÿQz@øjÔWdßIüGUZ=Á)äâ”(»::r¨ïᘓø"ºÐÏOÄS±)‚„aÁb*Xƒ£ƒƒ=ÝÝÁÑÁÝmçPÅÖÐ8%J’í|>¼íÃì›Y~¾”ß<Þå«Þ' ÒtPÿ>_–õ–E>ðž‚;ßV7äžjÈÇR):qÝëÄJüÚQVuså8I"Gtg‹ùÁÁ<º—ÑH£QôžH6j¢ÌfÈþ~Žð×ãRqGºQ·)t–^jƒôž|:ÇiçPûb¯×&Q%ÌÓÞ‰3ÄÛÞ‰éâÀup§Öp ˜,ÀZt_/ˆ/® ÿä+4¸fÊ*æó%øˆ‘ÔûKÕéÃ+HfÕ¿“¾_¯µc Ïÿcø¨Ð† ’×? H®V*¹¸z°©Þ¥§KmwBUÊš:É ó¦ÑùãÃxba¨€i±IƒJÑõ³ž°oúÒÓ‹ ÷fT[Og¦……ÝŽiÖ÷;u¯Âá2%ÍpSoª'OR’žo¯o½NˆI]&¯Þ@L‡)êÞû›š¸C2^Ã×’¶£Íd¶þIã$»°¾àKª¾læçb'ÉiöɌ/Ç*¢v~ Û’f* õÛµJ8dQ¯Ùµ1‰ZÌþÂJDŸª×ÿõ}ßR}íþï¾òÏÿ°0· ÷6‰$7}»ðy¢°P~º0_,s;¸‹“»ÓÞ”åäÂTWçR=1üãÉ®ï§×´ôa /Õ*}\(¢û… ƒ`.?šIa_æCdÞG}ò)Â1"~脚¶\ò)TH{ʽfô¡îÂóå×Ft„: ù¡äÞõFªÁ翟µ7—ãbþõOüT_<ÿÍ­?óü‹¾æäüo•Ç£¶ž·‘ºÎ”_˜Œ*µV¡¡‰èÄÚãHgkê ¼ 9Ù,B¿N_€wŽFÏF!c×ÅÈNa1R¹¼Ù¯@±vËòƒeH`‰Ð/Ñ(zW42Ô[‰BÒv )ÇbPȽT¼5UrÖ tÍTtj=¢?9Ž<ÆÍCé­¢‘ׄE(FobØoFw´—"«Š(]9/MA“–¢Qí|d›9iè-FÄ3ñL<ÏÄ3ñL<ÏÄ3ñL<ÏÄ3ñL<ÏÄ3ñL<ÏÄ3ñL<ÏÄ3ñL<ÏÄ3ñL<ÏÄ3ñL<Ï?²çcQ…(t\Z÷<ÙÝ„†XE¡1añ(½j·}zÜnÒ|‰ö/š‹rK‘}p$šx2uÔ˜‹æ6[ŠNžŽFâþ‘ˆ‰ºF¢Â™ÑhlÎBÔ·C4ò LD%ó– Ô¬µ¨àÄ^ôV%=;¼i¸Ä#V³Ã¨àÎt>j!:Ñ&ÞƒÞ 7¡X­(tÍî0*fÄ  ¯6"¿ÑHaÆ>4ëôa”¸5-¶$ž‰gâ™x&ž‰çÚ³Q¹2óÞ<ÐGß ŒwíÑ™[QÄÊ!èm{&*žÕ íª‹–.ÓDi3Z¡?Ïh¡0c54+ r™ÖÝ< ‹„ݵÐü–èß4Ðëî­QùpMtѼ *ég†\£;¢£ÑtÔfZw£ z¨Ü­ÿ¥ÚçîŒîÞÓF6³tݽvèM«Hë}'´Ø¶ z=Ëmrê„.²» cÊ-Q×™ÃЖj»L •ÄêÏÄ3ñL<ÏÄ3ñL<ÏÄ3ñL<ÏÄ3ñL<ÏßÀó Š÷x._…ùD-_*ÍÃÝÓðeánœàù#£‡8·:÷Xš…û6OÁ×˶ãÊ„Tœ˜€OemÀ¥Ã3°uþ¼®uÞ£’ˆ Ç&`“ªíøÀî-xF`ÖµÍÇÞ§b…ö»qvv%Žï– žæá7ñ¹øì´÷xøŽdœs0‹S³ðÊþ;paø^ìû8 XV…K·gಎðø³IØ6ô Néó?©HÃyÎ;pÐÆ*ÌoÕœ9üoØFÚ~Üñ@:Þ$Ú‡Ûn»ô¹…·oKÄmÕRð“ûI8êø¼ÿE2ž´k3ÞYž„X§b¥5IØ.|+NÖKÁkŸoÅÊq‰øºxVÒLÃ%o qò€]¸šžƒ2ïá{Gvâ[«àA}sð­3w°+ ¥à²+)˜}b^'Þ‹ßÝ…màÒÈ,?ç¾i–ŒŸ¿‡Õÿ…(%áè9Ûðà½Ïð¨\5æÀ ØNï¾Ý†1í4­[¯¿ƒ ç&ã×·wã½2ñ®ŒL|ƒ½ë+¦ã˜G°z·L¼÷R¾Ýy'öœ¼ûƦà.§à2íìÎ8€ ~)Â1“põ‹ƒXaÊu\µqV°=‡yJpLÄ+ÌÚ² '%¤á “\<'(‡:ƒ‡gïÇ1§ªp·ò=øhÇ3X—¶[V½Æ)QÕ8ëöA¬Æ9ˆÃõ[0{³t™7KÿÃι·Qßy|yQ2´MCÚ‚`¸63ŽdIö´CéÏ€¡.Ø1‘yÍbmɉ`%{´k’\KI¯”ë»Ðèdx8„'„@Ð.w<2iáx^›pG¹Þ@y\î(å äVŽÎf‰l §+}>Ìæÿ÷ê¯ý~þÒw•A3Î#ò@âYYû¥Í²hó#rþSÏì4sWo”oÞý°äs䦛åŒË·Èÿ½FÖ/_#?ÿ݃rʱ¥gÑJ9áÍurÉÓë䬫%üÀzÙ}ý:yé¼çåG'?(©ÿx\žÌŽjüÙó›$Ñöˆœ¸ý_¤é8£ñWçl”g¯‘—>±A’?x@æÇ—s¾³]Æ8¦ñá¥[eÍ÷vÊ1'¬—m×Ñ8탚Æëî—e6 }¦Ïô™>ÓgúLŸé3}¦Ïô™>ÓgúLŸé3}¦Ïô™>ÓgúLŸé3}¦Ïô™>ÓgúLŸé3}>¼}~õõ?KëuÈ¿>²V®w³_²\Ì/®’#F½*O~ú%i?ïùÞeË$qÉ­r…u«óÒ.ÙööyÎzù»-wË{v‰1©OÞùé™zBŸ<ýëÕrÍ¥+eêi÷ʾ ±ßß/ófmcn–oüFîšú”\ôÚ/äöç—È7ÞyKîúiMãsÙ•r\ÓF ý÷Ji}j£<×ù[ùää]2ùõÅrý ËäÂKï”÷ï“Ç£7Ê®¦%ò­å7Ê7Ü!s³+ä‹o®è'—É'þöfy2±N6Oì“÷zï•Ù ;d×–e²°a½Ì¾ôÙùÜf9÷ùGåÂ_ÿ“üé„7äŸÏ^"?yl©¼÷oä”Ì=yt«œ>f£´®ÿ/ùËM÷É-gn—ù³WÉ1ß}UN9ÿeùÂÊÕòÕï–‰&4¾lNj|ëè‡eIv»|cÍ6ysÜv9nâ{²î»‘žö»eÊçÖÉd÷µ?*²\ê›WJô7wÊ–7ï’Oå×È_]!]¿BÞšºFN>~‰\üìmrâŒåòÇ'–ÉÛ?|Vž8z‹,»àAYõŸ»åöŸo’Oo‘ž5ËüÝoÉÚ\!wî¸]fÌY. ÷Ü/W¿¶I¾páC2oî;²êÚ_JósÉŽ½C¦?ö®lr?úîY&­¿_EŸé3}¦Ïô™>ÓgúLŸé3}¦Ïô™>ÓgúLŸé3}¦Ïô™>ÓgúLŸé3}¦Ïô™>ÓgúLŸé3}¦Ïeësÿ2œqÆgœqÆgœqÆgœqÆgœùwãù½c~ï˜ß£§Ïô™>ÓgúLŸé3}¦Ïô™>ÓgúLŸé3}¦Ïô™>ÓgúLŸé3}¦Ïô™>ÓgúLŸé3}nŸù=M~O³’~O“>ÓgúLŸé3}¦Ïô™>ÓgúLŸé3}¦Ïô™>ÓgúLŸé3}¦Ïô™>ÓgúLŸé3}¦Ïô™>ÓgúLŸËÕg*ެm§­¤“¶¤5-+™íµœLW&m¥’vϼڬ=Ó1;¬tmW,Ùžh©JFØ¥>sÇh<^飑Hÿùp¬¾ð§‰Æê±X86ÂuuÑHƒ —{³~ôÚŽ™… »c®éäÌ9Úº\Ö]ØÕ5J#Íž"弞ßèû­/Õgœ–3ÎZÎ8kÕè\­ûÖ¼ž8ãì7zç~ëq.œõ œqÖ2Êé •O¿³ÀYÏÀg-gœµ œ+˹×â¾qÆÙoôÎýÖã\:8ë8ã¬eŒ”3T>AüÎgœýFïÜo=Î¥ƒ³ž3ÎZFœ«u߃][êõüFïÜo=Î¥ƒ³ž3ÎZÎ8@uÄï,pÆù`8ã¬eàŒ³–QÎÕºoÍÁë‰3Î~£wî·çÒÁYÏÀg-£œÎPùñ; œõ œqÖ2pÆYËÀ¹òËq î硳ž3ÎZÎ8k‡Ó*Ÿ ~góÐÀYÏÀg-gœµŒ ;W뾇 ÎzÎ8k8ã¬eàAüÎgœ–3ÎZÎ8kÕè\­ûÖ¼ž8ãì7zç~ëq.œõ œqÖ2Êé •O¿³ÀYÏÀg-gœµ œ+˹×â¾qÆÙoôÎýÖã\:8ë8ã¬eŒ”3T>AüÎgœýFïÜo=Î¥ƒ³ž3ÎZFœ«u߃][êõüFïÜo=Î¥ƒ³ž3ÎZÎ8@uÄï,pÆù`8ã¬eàŒ³–QÎÕºoÍÁë‰3Î~£wî·çÒÁYÏÀg-£œÎPùñ; œõ œqÖ2pÆYËÀ¹òËq î硳ž3ÎZÎ8k‡Ó*Ÿ ~góÐÀYÏÀg-gœµŒ ;W뾇 ÎzÎ8k8ã¬eàAüÎgœ–3ÎZÎ8kÕè\­ûÖ¼ž8ãì7zç~ëq.œõ œqÖ2Êé •O¿³ÀYÏÀg-gœµ œ+˹×â¾qÆÙoôÎýÖã\:8ë8ã¬eŒ”3T>AüÎgœýFïÜo=Î¥ƒ³ž3ÎZFœ«u߃][êõüFïÜo=Î¥ƒ³ž3ÎZÎ8{ÉÚvÚJ:iÛIÚYÓ²’Ù^ËÉteÒV*i÷Ì«ÍÚ3³ÃJ×vŇ¼‡°K},æŽÑx¼>Ò?F#‘þóbu1#5Ô…c±p4l„ë"ñHÔ…‡œ8zmÇ̇B†Ý1×træm].ë.ìê ¥‘d«‹aÔLq§SÚ3V:uz·Õ›Í%œ3çž«qãÝcBs[·q2ÝûNŽrц1¦p§Ýcbÿ“÷=­ð¨1Ö0¾üýâòñ…£Éyç_PŒ.,ª©).p—÷Åyá(¼÷ãf§;»ó©â™é…”½gÎHÛûRö]dÔ!LûU¹•ª£ôû?šlO´D†’qˆû? ‡=÷]4ÜÀý?"¬ýfßÇm[tÔ3{FoÚýíÿ»Ú¸àÔ¶=G½´ãˆW÷Ô\ü®¼ÿÕ…'·bÏ=¯Œ~{OÐOûÙ·wt§×µ½{äe_wÒC5“—WÌŠ÷Æ…O½èEãÚTßîÏn»nrû·>žÅ†Ñ~ÚèiÆÂ¯Ïu?rÚ&y™aœ´¤f²![Xc¬¸fœÛùEO㌾£§WëâÃqT-ƒüü¯JÆA?ÿÝú°Ïç¿û0Ÿÿ%íÿi»¶ÔëùÞ¹ßzœKg=gœµ œqÖ2†ë\­ûÖ¼ž8ãì7zç~ëq.œõ œqÖ2pÆYËÀg-gœµ œ+˹×â¾qÆÙoôÎýÖã\:8ë8ã¬eàŒ³–3ÎZÎ8k8㬭Î5»¶ÔëùÞ¹ßzœKg=gœµ œqÖ2pÆYËÀg-gœµ œqÖ2†ë\­ûÖ¼ž8ãì7zç~ëq.œõ œqÖ2pÆYËÀg-gœµ œ+ß¹×â¾q8ë8ã¬eàŒ³–3ÎZÎ8k8ã¬eàŒ³–áwÝjÛ÷pÁYÏÀg-gœµ œqÖ2pÆYËÀg-gœµŒjw.AÜ7ÎzÎ8k8ã¬eàŒ³–3ÎZÎ8k8ã¬eà<|çjÝw)8ã¬eàŒ³–3ÎZÎ8k8ã¬eàŒ³–3ÎZÆp«ußšƒ×gœýFïÜo=Î¥ƒ³ž3ÎZÎ8k8ã¬eàŒ³–se9—ãAÜ7Î8ûÞ¹ßzœKg=gœµ œqÖ2pÆYËÀg-gœµõùÆ`×–z=¿Ñ;÷[séà¬gàŒ³–3ÎZÎ8k8ã¬eàŒ³–3ÎZÆp«ußšƒ×gœýFïÜo=Î¥ƒ³ž3ÎZÎ8k8ã¬eàŒ³–så;—ãAÜ7ÎCg=gœµ œqÖ2pÆYËÀg-gœµ œqÖ2ü®[mû.8ë8ã¬eàŒ³–3ÎZÎ8k8ã¬eàŒ³–óð¯ä}ã|àspÆYËÀg-gœµ œqÖ2pÆYËÀg-çò9Wë¾–3ÎZÎ8k8ã¬eàŒ³–3ÎZÎ8k8ã¬e ×¹Z÷­9x=qÆÙoôÎýÖã\:8ë8ã¬eàŒ³–3ÎZÎ8k8W–s9®Ä}㌳ßèû­Ç¹tpÖ3pÆYËÀg-gœµ œqÖ2pÆY[?œk vm©×ó½s¿õ8—ÎzÎ8k8ã¬eÉ9kÛi+é¤m'igMËJf{-'Ó•I[©¤Ý3¯6kÏl›ÕÜÚÞÜzÖÌ¡î!ìÒ»c4¯ôÑH¤ÿ|#5Ô…c±p4l„ë"±HƒŠ5p0ôÚŽ™… »c®éäÌ9Úº\Ö]ØÕ5J#É`ÞÇì°Òµ]‘ÁfÞàúXÌÿý¸ÿðþ×5ÔÕ¡ðDZa/Uþþouq‡&÷82ᘹ”™O%œ3çže kjöÎ?üp©)>w|ñç+œƒ`±ïï‰bòî1>‘hiÎ¥ÒóßÖQœÏp)‰L¶ÇJÏʧÒùtªÅì1öû§¸Ç§ÝcìiVwçô= ðW@Ö¶ÓVÒIÛNÒΚ–•ÌöZN¦+“¶RI»g^mÖžÙ6«¹µ½¹õ¬™ŽÙa¥k­îÎ+—v©ÅÜ1×GúÇh$Ò¾@ÜG¢±†ºp,ކp]Ô]h„ÂÏ–÷§×vÌ|(dØsM'gÎÑÖå²î®®‘P‚§q«‹;~Å=ÆØ rîXS|lBq^8>ïcO+Þ…£<Dÿgr]݃Ë8Ôý®‹xÿ7pÿí zÒ¡¯„&&z;öM'n'9ÿw ú¶<èý‰Ä"õõžû¿®!VÏý?ÿÞ‹¹Ç”æ\g>MçÓJ8-fÎ=7Æ0n<Ñøè¯¸Â_wÆ—Ãg e&îsnÌ¡žTã3?`>eÀüSæÓÌCæ3ÌGä¾pÙêâ­î1¾9ÑÒœK¥ç»óšâñy÷{šÕÝyEñĨÏ=Æû`ÍȺÃÁÉÚvÚJ:iÛIÚYÓ²’Ù^ËÉteÒV*i÷Ì«ÍÚ3Ûf5·¶7·ž5Ó1;¬tmÊt™v©ÅÜ1×GúÇh$Ò¾ðP<6"ÑXC]8 GÃF¸.ZŽ¡ðDzc½¶cæC!Ãî˜k:9s޶.—uvu„ÒH²÷Þ>òbw:¶½ðþûß¿Û,3“+>2é÷Ì„þÎHÛž¥âî1©Áìtgw>eì½á§®´÷Œç‰5C}Ò„â8­1Ÿ7œÞmõfs…e§¤º{Ý+¹ïhqÝͳ›NoožUøø 7æœt.g†zº39'“›JeòéN'Ó ™¶{ÖZëÎfL+”É…œL¶ðj™pÌ\Ȩ̂N‹™ó;1¾¨UÐ+|ÞMhnë¶3…‹û}ÞÕ(½Êw£…cò¹½fÎéÍž—Ë8va³ÆÞÛjö¸cÆ·45&š[ÏœUÔÑ_Ç}¯e´°«þ×ñ”„“w_–SÝ£>Í Çè¼™úh¸Ü+_R·2ÆYÐSx9'sGÏNw ø±ÿ­úðÍpûõ:÷³aß Xøã˜D§i™ùousÎ (.˜ØØÚÞÔÚÚ˜l>£ÿ§â{Ýœ*é]kìÏZY3`~ ¯§šãÝÏÕ¦Ùç7žSÐiwÛæ¶ÎIç¯4­AÕn7ø Ïò®j» ?Ÿt°¾|xØÆ~íW^“½øð5ÓÚØÒäŽSÛöÝ¡=E—PÎ,ñ~œdìÏÇðNzš7¾õ¼–dÛ¬s.*¼ª‰t>“¶CîeÓù’|ËÞ¼C}&Žkoœ}VS»;ûvÎ<ªêÌãgHò%jTðF…$$3IÀi É€³’¯™ ­›½ÌÜI®ÎG23§Õ•,ênKWv¥…nÙn¶Òml]7»Ú.Ûµš²n—Z?°Mk´HÁªZÖFq•*Øÿ¹É™À@1€¾ÿçùå=sæÞ¹ïùxÏ9÷Üûd.&¿%vö†A£§Ð08†Ã`Rëf{œz% )RPõi³^æm)ÔÎÜó´É2‰|AKå(mao+ÿnµ§š± F»¤¦ÊãMÕ\ïr®tòåI¾ÖbáˆÚ‚¸ó‡#’بÑÎU£-‡â`ïqUUß„"¤¯ñDdïm|šòä©XòD:s~ #iÛHºí’u†sšå„‘aVÉTgÈQ‚J(&Ä«¥¸`Žr +æáÚáß$.°,Â9âRÆ"œaNçbž8YŠù#³RÂEÍáL<4)n~]h3ÿjcc6ou§»–éÝ-Is™ã¨þµ[;J»–;á¨Q74.ÌŽÓ¿ÿ÷—ª§ySÜÿ—–ÚÊ“ïÿm¥6ºÿ#™ýtê9õ‚tN”Aü›aÆ×8iüÛ#iOŠ[…­Œâ,Äç<¾ÖàÁŸ¼PÐîKß{;u6V¤ [9“ÄO…ËÒ2’iöëNýñßyWeZ¹áÅ9ñõ§E»ksËODûÀº’gD»Ëún‚ý¦í‰gEû#߽ωöÅéuÏ‹öâ¾™ûD;©o‚->úà ¢½=øSÑÞ/ÿ™hÕÂìçû÷ô‹öqë?í+Kîý…hß›^÷¢hsûfˆvâáý öºY»^íÄpðeÑ®ùÇò_Šö®ï'Øpÿžý¢Ý´ù¾WD»Õæ> ÚŸ¸üW¢Ý¿åP‚}múCEûÿO¶mÁŠà«¢]/ÿµhc'ÞO°÷~gÏk¢ýòºû^í·¬î7DÛwàòÿí [%ØW—<4(Ú£ƒí‡E[®üh¯Ÿeù­h?L°kãmkáÿ'Úxõ›¢}ôÀ§'Ú7|ã‡DûÞôglnß¶·Dké»åmÑNŸT´‡¦¿•`­}GÞ­7þè{¢ÞqL´÷ôWÿ^´_Þœ÷¾h;úš`ÿýÀW?í ß]\´/-™B´áW}(Zs~ÈÙ^9ûkølÚKVX®máÑÿI°Ëž¾_m´áæÑÞ}⪫Eë#Áî´=rh¿gÝx­h±~fŽh|ãçŠöðôgìûOn+íKÓÿtžhgÍšS$ÚœíGìž-‡æ‹öé]'ÚãOˆ¶¡¡¼X´·~`¿Ð¿g¡híOß_"ÚÏÞ\*ÚŽWÙD{çæ7¬>z×-ÙÑÓ?ó¾oZ~s<~{ß2ž6m6ò-y½†§MË·žÈrj>v<ò¾¹nF-O›vò^·²‘§MË7«Ž¥jµV†å¾òù›´kVÛ:¬ÜÂÓ¦å¾Ä ï–yÚ´Ü—q3~®h¿cXîË[KþUåiÓr_²¶÷µß6,÷å‘ußk×úŠa¹/Ó·WÝÎӦ径 テ§MË}Ù¼9¼‰§MË}9¼äKwk}˰ܗì¾¥cXî˯–ü÷yÚ´ÜÇÑçïçiÓr_Üüæß—zxÚ´æü?²ö+\–Eq~AÆy6Å9Åù(q.ÞÛeöÃ)Œ½5c[ õ¼A\ Nï˹˜Äw*t0O?ÿßÙ2±+¦Ã_°yc‹/fì7àë3s_ÂØ„Kû>_ÆØ5ùŒýÜw9cŸ¹‚±·Á?]ÉØgg16e6c{ÀÆ«»NbìøÛÆV^=âgÕ<ÆÞ=EŒÝ2Ÿ±i×1öˆ/`¬¸˜±ƒ`ÛBÜè– O‚ÞRÆÊegìÐYÆXE9Ê v⮸qƺŌíÁë+@ûÜg¼†P'ÓPÉs@%¨2è]`'èOpXÐ3AX â` è½`?9¨÷0€º?&OÄgPœàÐî;ÀàôƒApLC›Ì• È t <À‘¼‘v½l”>Á·»ßAùçÀ¯ý¹#±Í¶7+àý?pãØ×„þ3Gè?EBÍľ-¤×¿‚ƒà"¸´‚mà‡àMp%È 6€o€çÁq0A´Ü zÁ+ >]ð7à ð[?W€(øð ø=(DÛy@'øgð2°¢m*Àzð×à?Á ¸å¬m`'ø1x\‹²7‚?ß/òzCÛØA3ø"Ø ^3PËòôúü*ø8 tuà à[ Œ›ÌX øè߯‚©¡[ÁWÀ!0ís¸ì/Lûxw c«Á_€GÀ/A-^p?x—¢£Ô€vð÷àið˜3—1¸ | €ñèseàÏ ÇvL°ñ14´ƒN°tƒ^ÐÇÇ^£ñþŽ>; àq#A;è[A7è}`8†€}{(6À'F ƒvÐ ¶‚nÐ úÀ>p  â`(6À'›F ƒvÐ ¶‚n#núÀ>p  âe(6àM@qÐv€°ì`¹9z¼JàM@qÐv€°ìÀ‚Ø› ŠÀRÐ[@7x ìûÁÈA\æƒbÀ'ÁÕ@w‚­`Ø žÁ; 1k|²n2h`+è½ Ï‹à Äõ4Pl Êˆ{´ƒN°tƒ^ÐÁq~b¨µ@  ì½à)0Ž Æ„™ ,@q°tƒÇÀ^° åÑØJcë…5¶òu¥ð7øÃç8è;@Ø ö‚¾NÇ@.úl>(•À š€ â  ì=`7Ø À 8rÑ·óA¨4ú~PAt ì{ÁÇ@.â JàM@qÐv€°ì`¹<^@¨@í lÝ ô}à _“ _—‚`üæ È t‚­ ô‚>°¹ˆ½YÀ  ØÜzÀã`x “—F¼:Á- Üv€‡Aè7bø8˜†˜­NÐT]`è»Á^¾îƒàÈE\çƒ"P œ  ¨ ºÀÐvƒ½`È òA1¨« î[Á.c|xï€\Œ ³€ 8@“1ft‚m <ö×À±ä7OI$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘H$‰D"‘>9úTkNw­3äSn· Í™rÀò@Ø{ÏgœÔõ)Æò“¿´œïIgKÁhT 4Ç”h¬9”æ`G ¦úU%àk޶m\Œ–ÔÔ74¬r¸JÎø¥ÐâŠ Ø²ŠŠE6Í–ÙlZ¾!f++_l/-//-+e¥v›}q“*Îb9GUG4&G$‰E׷ʱÜ2Úq¡ ôûÇÂ¥±Ôi´L^Pò°?Íkð^T^>jûW”V$µ™½l“J?’'éÞþ$MKù|[ ²£ñ—LlÙFš£Í~˰\¨:íøWCþði^ãTñ_j/Oÿ Šÿ±'Þ¦HK¥‰îŽõfrâ¹ö‰4v:íø÷Ÿ~Tž"þíö”õŸ}Q)Åÿ˜È˜÷`²;&‡|rÄçŽÕÊ!¦ÝöÛÄôùM»üb#óÕø¼]Øsà'Y2úAXÝÆ~iV‰éë y`ª[ ¶”úˆO‰(¾Z¹%6ÿT0“%­“Øh™$éœé´çŸ;Ýkœjþ_œzÿ_ºÈNóÿXH÷súÌñðf#Û½\²2¾ÉÞ‰œ\íCõ&ÊÅ·ìò´\Š7ñ1} ¿œÿ’ž“t¢åLO2ïÊóÝ^9 GªÃŽ`ˆwƒ3“¸Œ&ý·ÙYƒO3jÂm˜¼"R,"{oSC-’ʯ—²öIÉÈ9%µéS:žë®_íªvè~_ÚVC1îwXÒó¥˜Ñ\ç£ï“=®ª:·Ó㬯Óý_(øP£1)ìçMŠª15ŠJjè¼,–/ÜŸ.Öø5ŽU5ŽHÍY£`!£ÆâRâWCZ)$8"™ý+ÚªúcÚX Ë‚ôŸ3m18“;äP¬#¸nDñù2¦?ÃÉu6„õú5Ëj,ع¬µŽ*·³nE=ÓwµF¯%³v¸_“«"9~ƒ;Aè|YB5r²‚%Ü…Öôe¨µ Ù±xoõ<ã‚Y.Å/|ä'OðéUË[ÞUUã¬Z¯cO$µjJE3ãZ‰#‚Eø&!äÄ/Rú³øåH¯0sCÆrúV^,§Í_IöhKZg?°É(Ʀ“t6”ÁüÆó¾©“Ïÿv{EyYòóŸÅ‹iþ éuN3Kœÿ?ÕÅŒÙSœÿ d'ŸÿÇ=ÇÒŽå?Xº¸ç7ŠkÝÍk.7 Ÿ&€+kÝÒ%åj¨#¸^‰,Ô…ÊÂ’}!ïªê<Žºº*$' —žZSå©j®q¸«]Îþsâ÷Ù+Žš¤<ëŠUU+›«k“ósV8ÑI™ntº=õ®¦¤ì¼úån‡kMUšKZêugÝʤüI õ«ª\ÎuéÎÉmpÕW;ÜîzWÒSÜ ŽjŒB«š×:ëjê×&»ìöTyI™ãõá,¹ Æð—rp“»ºjUR®e™QÌ…% KFjþbþ1M[y¾YÓZz¤†sµÏFÍj¿7R£SøÇ¤šäYB NÕ?'ÔÜd-O¨±<#µ¦´K›54Qû`ÖŒæÈHè_5‘Ëî4Ýy3K×ǃ™7b‹xµD³G (¾9&³L¦ØÔSW´áèÔ§ÎJ{jµSZ‘xF?qqÊO¬^³ö Ï\ÛËèÌKRÎt«-ÁÌj‹7Ê´­Ãþ%í‰|ÒÌùÔ ÑR\Ù>½q2Z0åxÃú52:zœšñ¡Ú² ³ ÖWÙ<蘾(õ`S™ôɬE>,ÕZ¬ÿøÔ"÷nQ&µ8YÜ1ª®ÔGñ³WŽOur3»uÒGܳW‰7~¼*±$“J¿Öá\y£ç¬ÖãÊW=.̤sÜΕµgu¢a—Ÿ/Ó}“ňƒÂ5Š7ŽHÕX«Ô±a£¤Õ‹ª&*ÍSCRP‰á6¡‡NÕú/懲¾Ã’cüzúj2ÜåÉ’ô­}η_ÊOVÇYf:˜ðÇ›¾,Ô6LÚ‚éô¸†·<Ò6ëòp8 l šsî"O«"ñ€üX¤FH2?ï®Ç Q½½£rP‘¢­r›"ÉQÉŸ)ú‘çš­œ+.”M3ê4¥LùFa&M™Kx‰ya%¯±$_ Õ­®å‡èu m*²·•—´#˜¾¨ÓÒ-ìMeªÈßH_ä›Ò™c ñð²ôd}Ôìcâ]·µÞå\é¬Ón;G¯` ,ÇF*Xã×*jKkl¤þÚÂ9¢~^Ö¶c£mA"£Õ§U¸W™ ”a,{NRÁ†ÝјD3û¤H0*…ÂjTÑ ém•C!%` Š!¾ÿ¼^ùÚähTŠ(Ѷp(ª¤/îÄ„¬1*ð)ŸX-oLg üj$“0¢*¡ÌŸ hÏ üJ$¬ ø,Í6ª±Å:ö9L÷íHÏÕÝâ‡C¾ Æ—«ªÉx¤ûoŒÆ)Ú±^Kã«©Î7¢•PL˜>Opºk½±÷{ÒðÖ“î»4<§øptDmÓâ_Û¸Fõ믲œÃZOZàX77Ô»W»øMÎlî¹â÷c¤R7(Z_i‰èÃFåÔ•Ÿôôˆ=Þýú´îkuy&Ëþy>Ë`i‰ µ“qûæð-D>&\¡M ÆA­µ1P2Î»Ž©9n?¹ãöóÑq«¶1«GÓè íèŒ>ÍíX«ÅÄŠ®‰6>/|OZpé —¶Ó»ÀÞ¨¯µŠu#úÈ€‘X+I¨~ÉéÈ º’u¶§Ãä‘Á‰.âZ£-‡fò„up[@{KO"äÀ©]þX ÏôNZ†éj¸w†×Gyíp߇æÎ~«àÜwÞä©løñ…^.»Sºõ²÷6 ‰¶HØ«D£áˆP°áSÎßb幫«êšq—³\{¬2ß­`DÁ"¤¨W6ŸìI~¬Š¤p¨}=)Áó±4VíÙ‘1:¦v< Ñ–ëç…ïIcJ¶ÇYËW3jÃ>þ`ß'ýIG@EÔd²¶K~9enzÏùîHæc/} þ¨=‘¥-ìåï¼d­öT ”q#MæÔ\í¨ó¸êõ÷Ô¨µÎYk¥Ü¦W‡ù ï·›e>ƒ›2|ס?Ãd'Ù¢2(׸ÌGµE¥¿¥½½a5H×e¸Rî‡,éý—>;+}6ßþN·›“þðñé³'×5¶‚µF³ŒÚ o´Ö76Í<ýµ«ä 9‹á¨þØ~x7Éb8¤eïÅX ô½+áæÃ¼¶˜oOɹÁ6›W‹³¤Ø„s„ÛCñœá›¢”L{R¦¸˜OÌY('ä ËN1?uÕ•P†¤•‹ø]ÒôŸp1a.óÍyJÌKN0ÂЬ76ÛxëŽI–S»¹ç}@?gq3Kx/ÏH°¤íŒGæÛx£pá*ã÷ÿüÍwí½’w²÷ÿleö4ÿÿÁ^f£÷ÿÇDeå¡G_¾ìÉyÏÝóëâ^ýäŠïÿ‚; ‡² ãn'û£}f,õ¹ík¤²SÖH–D)"•*BB)”"¢š#J¡(QiSÊšìá=ï·ù2ÏuyæœßýÿŸy9wk0¼HN}.mXº:u»ÍQú¾²j™ˆu0ÒYïjßOƒ¡™rUÇZR¶Þ`û …ŠªŒîXQ¨H$À|qCû!›Æ¼媃þÊy«­åË¢1†q¤»‚ è®=ôçj²CÇ9„»lÆ-Ÿ®] ¯åò@œ=šwÏö²Òƒìª+t- ÈÑîãsK¡£xÕ‹ëxÑ=-Ÿ±}Pè’$Fr@»‡&ìn"ÁÛÜôƒs„³a®@ê+$zŒ«›^L.·½ôñdpÚ㨀QÄ®*¸$ž·*)‚Üx ¶D9Â~) ß'?Å@V5§0Y°›äL›\Š“ÒYó¶164É›“7|s‚ˆ~ué¤>JÌ3û¢iï«'n^d0§ ûém[LÒ.Ÿ8-Þ1kÊ®‰}ŒÐ˜HefYpi÷øöøJ7#mæ¬ïž]"(³4MØ,™ŸE=^R$eQVéU¾À)ºÃštêÆce8¸S/M{£¼¶å¸þh‹62Mˆ¡i[ƒáœÏÌÛ@C8uŠÜö<^ iGþ~l÷\Níö|¬L_GÜ»ða†R.VÉ¿H´lxEà,ýpT\#ß¡¡¥š.Eü–etD=Ñù Çw¶ˆP^¹è.ë2 Ç>È#ÃgÇø±\ª¼õ‹cã_’@æ–XmÙLAÏóÖÃÒìb°<7´@D í92»“­ ±'n ®È<^²DžRÍ—6˜¡¤†_m‰úË„}IãóùV(NÕÀ9Ž*mÚ¼†?*,€íoiÏð64›,ú}ïE2°‡üŽSVƒ[ÚÍCèLØØî‡ú("é«ÑzÊã>@LÉ wªÇ$%D ñ=¯‹„w;ã”§ _ x ;Çý94 W²Q7f ĪfqÚ+"Ñ´òŽG,àÒŽjš\ê*ãaÕ.îé»¶°K£¹|BìwÙ>ü¬…Ê¥/OþL0ÖŒ:]…diqzÌSÁ‹.¼Oí­œ¸°:ù0›Q¦Ž›2ýz˜?œŠVFìÕ£ (éÕ_¥÷ÍÄ…Á½±!‘jhU~ »*È|œ¯*²ðúNÙå5o‚Žß|¾o^ÑÁô{üë}m¦i§m“VJC _šWo ¦>ÕÞAê†z®mASªè¹xË­Çû©ˆ}OYÖB12öXNP©CÓ±p’„Þ•+îòF-7|ý2ËHùµUL-Ù TŸÄl¼Wg™vùû²e8àÜæþ¹íÆ`t.IÄ ŠÖm¿´‡Š×7o)4ЄÛl «RîÃŒJ®Æ-m0;Ý‘²E]­:ýƒ Ö^¿)Xe<°}'Ž\­%­±OJ\Ö× CZØÖvCìFÓ§ÚH#ìFÞÙ”;Öæ´>Op/ Š85c7,¼.Ìâû½x{±ßX‹ŠJ>\>öt„8¡eÏvp‡ò·ŠÒô¸/‹2޾sÀn„§|`‡ÂJt×F§-Ñncƒóz!LÀ¹+|<Ú‰ª–<ÐçуÚW×"w`7bŸ*q«ž¢£½Ù=*|èjÖ{îoØ ÎFûÈg‚èÛ…hÙœ;†(Ôè¢Â½È9ÂΕþK³Ãýòü$¼¬«‡|·óœd4H6Í (öÆåUg¨;ò#è *¼àæ›,Bïøýšúº²|ÝŒ©•¿—¯”„Æ•n4¨¿#̲l‚`𿓏@ÒGë{Ú†ûtL ád—ÀÁEW^Þןî?2’ë•+CÇÄ‘>†ZüÓ¸ü2ðË®¯5ëf(m\¾ŒÝ}±%»ñá‡囨,J¬?–x»Ñº×}U¼»A¶Ê,•׃ѻ°k^©.Xç܎ÔCHV¾º«7NE8)Ì7™who…Ø3}ÄÿºÖ;CÓ‡˜Ž]Ìy.U8“‚:Ï×™a7.÷6øf’ ãå^õÉÚp"©ÌO»añsT)z‘€±ö§[²É0þ-éþcìF4‹ÙÓI”Ó½!jv£´½ÊévãSTÂò”ÏqÑ~9ûv%3:µnû>ìÆ5…ל Ø ï÷P)i3Ôú)MÛ_g™0®:{¢Î YšMÎ0 B±„û©ì†gþùÝN6(meãÈï d8’e!¬¢YW {'%P Yá€ö0£çjâVì†ÙŠ1÷ëAôòö™¥2"ô3ÛcŵÑYzûpp ¶EŽš_`Ҁџm~êØ åÍ‹¢F;‘«g§V²Dº÷õK\e|â,œ®²…ªî /72)‚›ÓؽkxÏlß´5눸½ZWá–J@ó­_)W°]bV)OÕà‹K}b,nn-Çnä<¸%±xLù5yÛO) ž‘-Ï‚Áb¡jètù)þà`s`èîØ¹»ÑÊ|¶¡4l*tUm§Ãþ}l»S±´×öÿloÒ=`Lßý jï.%Œ`7Xº÷¿äù«Š¦ mnm¦¢É9 Ø„£¿”RÐJlõô•)rI»r»tOé̕糌D3F v£×Ú#ú'vƒ¥1®Uæ;x> ÜñÌ<ºgÒ±Ë!²ÍaxßÚ6:‚&~û´ötç0ÃàÊ_ƒŸ ÚðcDí{Œ¡.rêóvžþÉ(}ZᯃªNo,|#Ž®–oöÇn€ÁY¿O±ïloß–²@]§IŒì†¡/°‹—”žæÔ&vÓàÝû{I½Øæ¨úñ¶oZ¨1ÒKv Š~©njþŽFˆ[KQ¦QŽ:hUâ, £[1Õ¶9™c7üoV=j°C‘ß *O-Z¢3+'j䃘àïHv„=Úa¤g Ä®ÇX$mñçM=ÙÖ²èèJµL/ã ºñYcãgìFÇ9¦97ì†âŽÏ: †H!0ž¥?lŽ8>õ&òC› ê:ÊÛü„Y¶NÔ\rždù¬¥­IÀ¿‘èx'oây¶ƒ—Á×_§"ƒÁ°Ú-mV F¾ñßÍÝÍp‘TNß¶_¢ÅïœÒA×íÛ]-š ,u„(UÂúÈ“qéf·¾ {¯~Ü¿Èà]¿x|ÐÙ^ŽÕféIÍòÈùØ>ÆWå}šÀÑÓÅh©z‚ݸ™3/ÿ»ñn±ø×YY´…¼4w»‘íC~•Þ¦ û2 qª¤¿ßœ³»Q~Òóg“ž5Œº|WÒ?lüÉŒ¾Gôkçþlk0VsŽšž—úè•”yjð›‚òXý†O ?Ž[U…Ó)èöÌtLv£«TB¤EŠ:’u'B_kƒÇ왡]Â$tçò™-³x0Ýzc–E†ŒÉ€SCØ8UÇWIt@âï¸'vC1àöŸ×ØËáÏ÷Õõÿ!v]~3r†­ûþc'v#eçL}ìÉ"µ¿TÎ Ù^ÀŸÏØóË×û³h`Ä)³SbÕ’Ñ­5Ìjèo˜«æçCTdŶà`)ˆ’T&"¹>S^‘yMÁ4 •¯´¶b7ª7¾Êtº;ËþB>z»1f¿stßÞµMd‡Áà³£ÍÆÐ4F:åŽÝ8²,ø} {D츕ÙBÜ£‡§önfæŽ IÊj<ç«`]¤³ÔÌ1Ì„b齃ìÒA©{÷ˆO¿G“a• ØÙíýJ%O„CeSæfìÆS&–‹ÿ»‘ýüÑ_nà°ð¸ÔA¦‘À£Ø ‡ÃÞ+i¡k7?R7¢¢³UJrGˆÒe]ÕH;t÷àšë¦qYÜÕV©‡Ýȵ;TSo‡Lõ*k–häªÒY¡&hæ½o¡böVì*]£ÃÖsbéØ þ)¿î\:jb¥jd ò£c3?`7ærÎÀyã…9‡R³!âž0Ìd ™#&Ïl¼“Ôj‚¶èMoäÒ…/…Ù÷'´€÷‚úÐeɈ¿`8T'¸XÏ {É _Gðßñöµ¿bðÌÙh“=g7#é'ûßI¨ú ðDޤƒ:žúÔ,_š Jš/¾ʧVˆ~N˜@œifvϾEƱ̦î' xƒzùnHÁi%Ú+îè>F|] þn(°‘žKu1ì=zŠÝØw¯ áv#p–ë·ñ:Yô­3aW vCœ5Çp±C(ÛàìIC-¨É7 2ÅnÜï"ǶXÃ…f±˜±ã†p¶±sùW¬8ò–§¤1áyòl~¾êt¯>rmXûÚýj†ðÝ5ÁkzPžjïSÁT ÊxÇEIÁëòÎrô‚ v¿þF~¯ G*…HèÈoFjËoÚ¿W¾lÉ$ƒdÁ6~š<Û~ý‘D}^‚{ý±¶½Ù‹Ø ù÷Š9;ÿ²Î.Æž^,ˆ´O5Ï »qhSÅû?-‘Ò“yIe3Tõ 5DFu™(ù2œ”_i…FbfR ÈNuaŸÆnôµn²Ûnƒn©$=›=M#Ž‚¾Ø ޝM£ÕL’ÈUnàwg³>:˜æ¥ƒ°sï7ŒµË ÿ€ØŸuª"`ø¹ïу­íŒÉOµµGI¾»vû¯9 ˆê—¾ŠÀnü·K¿á«ˆj_•~(:kÎ9ÇJÞE¯2L⎺_³…Y†X.·¼"öpÀnä]\8láFÆÁ™4~h–ûC…ë~ZÑÊ7Ô`³ªª%Kv†© ~ÄnxþûMéÉUFb›ÆIé+ èK†hQÃÁÉ<Ö諆üö2äû±¡á¤þ›Ø ë¡›«×7äUMÁ÷ˆçwŽoÂnh~{^û¦Ž†š8Híž§i ¶¿`#»ñó¢œóNn54}w×¾x*ÊßK×þÝȾ"½4ô–‚.žÛ6ui†„¦+Fű.?*©ë fÙºÖ±Ãé¦Ö¶ØJ…«·X7²CÒSóÌ£Œ¡û¤ËÑ8ìÆ˜põfá#Ttíúö»6š°ü`íŸý0ƒ±…œ·]Zx:ƒ’Ü,u‘`vç¯~ÄÎa¤"⮃zæ?¿†â(póEìÆú±sº{Ÿ Cfø×ùMH¥ÍÎå)vCÀ`øÊ½ n¨Þ¥±PýŒÏfÄ߬`7<¬o…žÓB L”!9S*Ú6!´µ´~„HøxÞ2ÔJ÷¥°Ë¡/‰y vã×sûWûîÛ¡}±×m:حЛ_µË>Lpš>Ø#ÙOåªk‹tàO¾†Ýøúı±¤€ŽF®tó\HæG‹¢×¹{±êÒ󱨝“2U‘/ ÑÒ÷¹=sÄc¹…<Åf$ø)=š_2’·9¿°™dhœ?7<¡mÉ7JÌó­i'ØYPïãT¡§# ±¹x˜3‘àïbñ9]¶nFÚ«Ö+’pù‚±}“¢Ö¦Ém¿0A„ŠGˆ~äÒG§é}›ÍM@³Í:ZÐ"cR:qÛÇ­@ KOûyG 2e£m'"úÒ~«Ú#3d X„®…¼b”ËŒ†c7|=yE4OàHÅ{‡1Y4réÅ}ìÆœv.kP·2ókSþYiøáa ØåšOn&:ÖàB"S¯d‚¯Ëâ!q¤rÇírU9ôоõµù¬\Ä+ïëvÌAi`¶ù É[¬Gx“)ˆ}xûdvÃ'뇩¦ ]þÚ ¸®Fý¯ î)´Ã£-ö»˜¤“!©³DJ»‘Ê/3wER½¹]ý{/vCèÖ–Т.4z5Oìº}¸Ø¶–å ÝÚèˆÝÐäk®Án¼°½±Zà 1º;éËòËÄŸ¿LÕ²7¬•¡)ë–/üP£.¬9Ý( û:ü»ñM$µ(*ƒ Öóšu騤úÄ[;ÖI¢:çË|—[ôÑÑš×ÿb78ŸM|,ƒîg=8cLQ·%™ Ö팶þ¥dŽû$hç?ȵ~’ïîîa?‡ÝèØ¶.òK°"úzätH_Ž|‘øõ/b•±Õ9ñèB[h8Vj•ºE2n<òpÇnDo¸b—kßX׌ž%àåÐï£ÁØ}“™Ñ—Ô`øk+J¥Cïù€yìÆÌ5Ú‚I‰2Š[Ó,RdSDo÷›°¥× [Ú™o­óRCcßRйsh¸ÓRÙ‡ÝØ¡xi^úb6ÉPñÉh×0êÌ»&Q6nè_ Á‚šŸÌ$Üuv„ú÷~ç A—ú¹VænÆ Ú!SI¸À´q¿¬J¶ã–¹qf‚˜~ì76ɪÆßßvn°1èé’Y~‹ ëwrñ½6`·ýhrã)˜grˆèÚ×Çxùü ¿ó$õ6˜¾«{É Yùµ»‘æ“+ŒÝˆs?;›óŒ:6)w`7Îêƒ{Þ*CÈ•¶C Ô¬ߘÔÕFYïÄDX´¬¡Îï Ÿ}®!äÕϳ`7}–0ð¨6³M‰Ü,?õ‘n¤²IAÛ !*¶…é  ÞóžJœ‰´>¶¢üvCŸ%‹`Cò«œæÏÃÚ¸®NY`7D\Ÿÿ^÷“€ì¹ÔÜÇ)dȼ»øÉ»‘ûêÃX^9JœˆÀnô•Š Y`7üܵ#™­ç‰m]¹‹ ¼¬ÈèÒóöØ >SÕýû°æ'ùF¨:fè}¦Ôå^™eâýÚø[׬¬2§ûß#ü üxÄþ/v£øúW êôyö(e6• ¥–cj°§~úpÌ I¢ÛŸ»>ÕG …´TihfÅ¥f©FI­¼å~ª+”]×ÄÂÍÚ×¼©s§k°û‘C¦£XÕ¸‹Ý\Çr\h¿"R Nç.°€ÂÅ/³½«ŒLAýÏ—láÎõÒ_ÿlAIàa`7¼äww©2ƒ?Ë bÙ~ý¥]‰Ýh­Œ¸Õœ¡nfaª Ét¨ÝúñŽ€?À`Ôâ÷Ê*e”1¤sª…[Yl«9pw€\“ªrQC3_6Xï3‡Xæ•C,ËQG·aÝ–÷îÿ:RO™Áçg¦°j‹ö»5ÓÞÜñïn©4È;’^‘†Ýô¼$¡†ö˜¦ð§§"'íC‚Ö‚Èð§©ÐåV J6¬=7GBúñµ»°QTiæàˆYFÜåE©~ìÆVfÚmìÆ›3÷ËP±\Õ¹Zk '| yˆÝcÿ9å›AE™´m\";4á5ö³aÆt_>{#Iª’ô¶lÓEÍ´Zcãüˆ³½*厃ú”lö‘!Ž.6¼Ny‰Ý`ò{BŸïáÓ"$-Pø@:›Ìks$ºþ±ÕŠ NHõ=¤ãxÛìÆðG¿uV³Z(¨Cô=µo:Qé]=B ÖäSw:("L«ÞU_ D»oÀnp¬ÿí"Rk‡Âm׉X¡ªŠ™À&`;Æ=.åa½œìüE‡ŸSñ°{¢³]ÍoÐѳŸÏœoì@\<£Ø KŽÕsüè`[ês¿!Š6¿6´kŽ)7|Ñh‚¢Ì"Ãå%tá¡JmÊo£IÏ Î“%*Í~¤6;ÎF+ n¯2£ q ÖìèÍ,[l“ùH sÂÁSð_Ã;¡"훇$<·›QöVÖAëæVON6k·2­é!aóŒ=5&°¸žyÌ{‘Á襲vZX@Ío'Óì'R€”Ï}¬ êc4J§W='Ã]ªààÓK†Ë£ùŸ°~âß ±Útÿ›†ÈhÂ%ïìWìÆ±¡i“Êà©ê\6¸K ´OMó?ÃnÜ“ÖEšÖ |-­BÔ™]ƒ¼1âHf4¨šõ^—¼‰ÿýÒGéíJ«-3D×é.×O€.<8ê¸z„‚~>Æn\uÔÏ*v%ÁU5ÏImx_Æ~^€„|My8”_è»I?N.­ÊìF~mºî º$OâŽÆnؽŠÝx¿ûtòæ9Âë½²=€eÅ=Z±Án:OÝ’Ånäñ°eÕ뛡1.fç[˄ؖ„F‘º²ð‰rT‚êF¨»W°nâ¥ÛK°–£1ÙÉdóž´ú€ÝÈ/¹Îwy£$:de§Ù¦Vu´JB±6 ®ñe2Hc ìÛVCx¡…ÌŒÛWÞßvª"ùºŽ÷ ¾d ìéÁnleѹfuH¶QíMJ,À_àRs”ÿ*C…ö%äõ[ص;©"ÁKÎX&»*b7Zßõíí>h§¿–´˜åW·Wb »‘·wÛ57­›HíÐu*Ø ©¿S›(£pQA+?!EÔ|äNŽûíbC™_‘“Ú rûÙ vCôîÝìF¶föRØgIpoçœj¿‹¯£— Åóܶþ6,’l£!)é·Çi œ+w»‘K¼ÄXN uŒœI¥¢+zš×±y®3¡)hë G›Só$¤c#á»1–mt‰Û}–AÊ“Öø‹Ý0‘oþ†Ý¿r5yZ˜ Âny>Q¿c ¾%!Ÿ±¢ó§ÎQQËñÁüVWM¸—>>f<̨µÞ¤0&¢ }þ[ÂuÑjÄQ• ~4³¹¦+ÎF…AÜ+•&qqâËÊ0vãHðóÚ Æ7óç“„’àÔÜŠÝxxº’VšÃu?„ëÕÓ @QIV»n­],^ÐBÞe´d'*"HKe+FÅÚYwt›©ùkùN9´å‹G"?vCtllðç];¤Í.â|uƒ✭øWëÊֿ핟ì´G]Ñ5!õCt8ö¼ˆ<÷O±Øp“¯šŽ~& |·ø$€v{sì9vC¶þP¨$vƒ7pV¾¢ˆ~—òSs„öú}y¢õ&ÈÒ¡Â f³.(Çks³Ñ'»†…üŽÉÀMwêÇ$`Élèµ\fFƒœÑÖ½6ŽÀvk¢mãz Ϭ?ç»aNGøS%áiöë` ÜSòOë:&Oi_6¸‰.é¡¡ C¡×v˜À°|£¥µû"ïPßý™‰l°ÐŠÛÝ!î8*dù÷1š³c«TGȰùoÉ_¶­ ö‡‘ÝÑØT¶KRo±Ôƒ}òc“d´ñtqï,vƒw®øÙõÏʭµÞ.D Ð{VvC×I$ýÅ6J3m*¯0„✤ƒâè©¶±Äëæºê»>§„6Ü4@3ÄߚÒݵ}™¾p˜‚üÝ.ópÕÐЭܧG_’ gk”ñ«?ÚaVÿ…Ÿ„*¤ÑnÛØN Ý?Jš!—Yv#gðFx¼®$âzJ¾r»ÁIdifc7§BåIƒ9âT알OM¬èµ7‹vCöiæbÿ¢%:WШfh˳ß)bË„ë‹ßFõ—­—«ðáF>øµzx7óM Ð:¿!Ih§ ¢ït´žñ™?Y‹"æ°wòÎŽ:ªˆ8¦9M#nX€oq…÷*cö»¨wûY[ð¼û'd(\fÿîtäÇnX«Õ]‰Ž6ƒ/'/8ì»D€É¨CÖªú¯¸„©ÁAGþ½³GèÀÅ'olŽÝxNiV9ÚªŒèÃKHŠÈ1Q+Ьr€¸HØÀ¤:ºa×ášPs8ä¼&ì݈ùm-_/ Ÿ)}6UÓa-ëÜIyìÆ#¶¨’÷4ô¤Pºjû1´}Õ~‡Ý0ì½K~§¦†ÇåO¤S‘äá»F6‚ˆgHŸn]KA/Ÿ4NÿCB £n7žb7J´N3ÚLg§|Õ{ÄNXAöŸáõõæð‡ç)ãt+d}­”||Ëäª ŒV±ÉÔ8…Ê‹T”2X•ᥠz?Â^dè3X{|Ù$¨ z ,=táîR8ò›–ïó°5×A×_6lй/ޏ}t’XZÈhD=¦åàWaør½dÙ»µ ÷¿_MZüࣇ^›­Ý¥§\¡–vã´­õÝåe-Ô[Ê®6ïLEIò›#Ie#ÿå„èm[tÐRj­{„z¼ÿ<+vãóPå~¹-~Ü@Æ U_}‘¿“ ŠÎ\<ëãhFntà>2Þïmœ}ò²îÑ‘=ÜѺ (ˆ£G´VìÆåÊ_C¥|h¹Qõ}Ò˜!Êϵî¿å2Gä5»;7Ü3AI«M/u!øb¹îzÚ$ãI–ôEWxüéÊ ùtš~¹Ô¿ÀŒÓ÷nþaæ‚æ–buI@Ûó™T;ÙÅ`±—x,¿MR9Ô—êè ƒõªÛ‹&ÏÁÓòózh­ìË™<7ð33÷.Ù¹ÈWÝÄ÷ÈÈÞ¾^ŠÒx+ÇÇÛ[Ãw÷1Ê_çÈùA†v}÷ã—.v0L¸îÄ`7êò¨s¥`ƒÓ†Úó£d$÷Ôg3×4 ÙUM<ø® ÂÒGjÁö¯°cj¢†jÖÂ¥X¶íž!ŒØ].ß-ŽÔõ–=ÜKÍ!#ï1ÈþÓG‰'…*Ÿ=œ!ªcÝÉ;H&ʘˆ¡ Wnê%ìÆ~ÄøE¡JÏÍWµAóÊÖFìFúº[ÿ‚úPÿ%à O…CÆÞb7¾_éÚF"$‘ò™ñ-qØÈ¢½°Ê‹;eíëf ãØOu"lè盡ZsìÆAÚû×YØ*çKÙuf(>¼ü›§Ð2¡Ó?C3ȵB‚²9‰ºî|0¢¢íÌŽÝ .ZfÃn ^IïÙw„ }ìõ§”°]v]ª’Èo«pêãv}´Øô.ð+v£'•Oå\¦ ªÙ£l¸ÝJ :DÅfµÚýig$KIP϶ç{ù; LÒbREm'«Þi%)ŠèÄq©EÕð[q¯y¿ë*ãLoKëã [˜mS÷$E ÏÈÎÒBI±#bQfÀw]R4»€Ÿ¬[Ça7ô¢"‹v©A—zåa:\µL=¶ »!>y<åG—2º¦8ÒyVJÍw(ïS© 6™XwÆR 15í?fåé-ßS°}âÞyç$áÀ¢þÅ}t0=4¶B`7> }'彡aŸ;_ÅÓ਱~Ó*vƒqŸÛ!JG Ù¹rïV8MEç˜4÷`7Zœ¾‘¬Ä9’Ïýûá4°ø‹ÝÐ526Q›eÔ½U4ÓÂnÜø&˜l‰ÝøØûòú¬?+Œ¼´½n %…=ÒÒë-€ƒµè^W ;zPÒW>ßÖTÔf4?º¨äÀ« wÖÿ^Ú¥‹‚Ë÷lžâG9W?eŠâAÜÈE5âè\õ±5IìF1ª¬ûSúNð~¼·Ñ…s6]Çn˜j&½ì¸È(ý×wé®öìkxóÊ')è½Ì¬aïÙÇ`êQ¡®áÑÖ÷j?Ú—ëS“c±úœ‘ãúØzÁLýA2ª<ºc$vcஂQÌ2œÿüA.I þµÁn¤uçV.([ƒãÖ3…ùª9¨F‰£Zó¦8füzå†`«ŠW^¯ô`†øa•É•èú¢ÇÖoQôkÇ ìOÑjŽÒqçÐ(ì:¶3!†„F¤¿}J~M@ÒmŸôŠCdȼ:˜ñïÿsÑ]ŸÎÔXI¢®÷ñاµŠñØ ïâõ³DKKòÛÓûØõÛª vƒKú§¿vƒ§²¤NÛÎ È÷Ôä]&xZu³B3¯¸Ì,ðBß±Fë°k–då_Ø ŸŒAïáX2Ýw‚² »±qãsÉmIº±…ùD§>êÐâû*®FC®–¯‰%Ê ‹…oíE`@rY¦ÒΠ^tûXXD‚Ä/·‚Û^S€“%O»1Zé&Û‰{JìË®Zœ7œšˆuÛWBjRlÁ)ž­§=[JgžýøˆÝx&a/Óiî…È=("àœQǶ&ìub{Ø£¤˜9ÄС×ÜÿQ,v#ÛËßÝæ£2Ò2T½¯ ¯ˆ´Ù~œ, §ÊlUMÔ¿aõ^±ps¸ñ€£¶»aƒ>?òÛ' LV‡Jé0ü[/Ü»!îpç{š’,‰²Ž¥áyU률aÛ ßôò«¡ª'ÂF·³¨H(3lOˆ­ òê^ß6y‚¶ŸK‹X$¡<^ÛXµÝÂhr%fPVl–âäê€Ý°/šÅnÈ0*ºó²Â…}¬žoKŒ!NÓl…ÀnlYUÝ2SŽ{–G Ï5!´ƒìú„:Ìð”RöJàÒ†ÏÆÝ?üuÑÎ=#ÓüHÿŒÏå¯z:(.¾îG•8jâÌKÞ‚ÝPèo~³0& ºw]\7a7ØNÐ…z±g¥Ø¯&ñBx[ºt fŸ}·ÁnDXÄÜaÓF›cj>ì¦"ó-Ìrmù#„‚øØ¢Î÷Õ[Êu^\“C›*¯§M`7ˆ¬‘›N·íн¬ÑŽpu+\ìp0Ð ò§çšÛ#{…-+>oéðýäsusìÆb“Šf8ƒŽgsKO]Æù,ütpv#¹Êîð: ‹A¯!ÖeCD)+pv˜#ÒÂTOÿ­4AO:•ë"´u!tS,—ºÂ$ã”N{³˜°ºµ¼|gÓî¡¿W§˜‘ýÉ,Û¥-Ž e[smD‘ö¯ì±ìbXgêÙí²„?Šc{²LtPx³¢³Á "¹÷ØwÇ=år)ç1? »í/Ûf·È,:ïR¦cFí+/JÁ!Ù×>†OuDÀg2ð$6‹Gk´3~\(³‹Ãn˜H< Ånü½{bó—Od46×R©‰ÝðþAðS†çé—{/djc+Y»Q¸§©ûŽ¢5Xø¨®–>3Ç›µˆ£Éý?6^¿b q}g3x #;h!µn†°“¼®oÐ:7ï¨7û)¨ñÁRv#ÒÐòµg þ”6¥òé€ðiÛûͼ$´óÓôJ~'+7;ÉDÿÿ¼I–œ–ªª¯ÞºMµ±$=‚Ý{²]“s3À!Õ•¾ü3°±~<Ù†þnÑ'°‡ùVÎ,.X"^-MÞºmfÈýÈCfÎe¢÷Om˜Ñi+T;t÷D./ +n’àÃn0¬Ó]mзgú$Ã"•µÿvƒuëÞ×F’¨0þÄú•>Ò¿µ»1ewa07J¥ê¿âwÛ.ù?žËSngœ=˜È)•O‚¢K£ï;(Ð/ù²Ú»‘“-º}-[y-­ãgj²€°É.жý*#½Œr5Ѧ +MËaž–ö»Ñ?ØUëºß @á×Ó/%¬i%š¯a7¤î–7Qƒù 1Ñ¢èà©|„%»ÑÔêêT3„Ýx'"ÄPUD´×îs%„rÈ–ÎV5´¡Ý=± »‘0„.~Æn„"bů+ûkì¢,Åt°ìÛñ#»¡T×°^ä …›»ù£hT+}âpÀn'ÂMœ[67_¿D¿@EËaòAÌv‚¨Š<Õø(Ÿ‚¤]™ü%!™ûwc7$[Ÿl[›aœ<¬éŒÝ»Q~øvãLçòÎ˵,Ð+«àYh 6㾃>Øa7¯+bÕTdg”y0DÜë»Ã=Ô‡±o–‡+Ø´¡ÅÑóÊ«`]JVwÞ;ËPÜv—kë “·]·ÝG«ëㇰÊÆœGïN Ãí™Ý6©,PG؃-,oÌQBË„0o7/Ùw1’i¦5ÿI0»ùõW®•âL=:A¤°ä;lÒCqÞîvaA& ÁÃ(ª°\d”£k"ùT ›8™–ý[ –˜v°íècìüQïöòš˜·FÅ¿`ø}c?‚Ý3° /Àn|·öOIë##yíòwæØŠãƒF“Ê`o"È»˜§딘Ìëh#f“˜?!òÖÐõMqļç Žž*£Hqô´ÙœÇñ²9Äë®Õ1@í“#53Äù¶ƒãm›5}FAþ§Ê¯g`7ºY.ˆ+$ÁÎr Žâõ:À¢[̇Ýð9§RßF«‰FÃ¥ýd?èYi‡Ýú{Êý«›$z,é¬ò¿§nô?RÇn”Gš¼©!Ï—Ö‰T(I°£r¶¤0CìóßCOnb7ܶÕR\ÌQrÇ\/ó2q†Û ´B‡œ/[Öá…Ÿ/v b7ž Ð…›±„ IåS$âYÜ‹°å'„.YH¢/ în}ô¡‹ëõ)ìÆÉŽr Zj~õí­«\U¾ðaŸ\;㪅|zq. X¯š–³=£ÀCžÂXìF°ÿ“6ù|E”'/ã¤Ül‘\«þ–« Ó*Šß…x[(åñÚYÞ ¼'5+°¿¬¬‹ÂÍÀÛB8v­Œ€”ëÎM©àžs‰3 ç/k©ê¢î:lKUV®Ân4>åZÏ;£Œöé.îu¤)"³Èúžþ¢â÷ì6?]5´6wæYL„9ø­gÞ¾Ž€§¤nÅ^ZX§éŽùt´Ó?‹ÝP×4ol¤¡Í²Óú‘4è0èO<ˆÝ°h>¡c訆¼æRýåQ‘–sèxv#n²3â|6]PmtY"!O³A‹lì†Û§¯¯4ÌØÝióI>–.åß ÓEAĆÑ9~¤ðBÃ#ç/[m8wMݰßt(»Á¾ÇëFÌa<;öï—¸:’tG»1”°Ó‰]š88{@¿ˆä\Î…Ø ÇM&|¾|ÚèyûùM{©ˆªy/{ÿùBøº:Ÿ”¢êPý væ½j—˜3y‡Ý On~2Ti‡úµF õ­P–eñ¬Š5°i‹›>Õ³G+;MÓ¤žÓÁnK÷ÃØ.]·úÕv:z =OœâBsáM»ïb7ãÕóêxÐxášÄc„8æ÷\Ùd5GÄû$è–› Ä»ò Â&º Þyã½ÑÆIÆ‘®‚½¥<Pwýîc›r8[õÙ\jŒé=wÜÏ¥æä{)Iâ:$ð&ïñxß×Åø¸s<÷¬¾$<*Ý}« îiª~µg‚¨-ñTsþ¦‡úOt7ù‡šÀö÷n,š,2Ä:vQNª[@ýâõ‚ )°U—ÿàÐÇH6tÈ¡ô‘õVÂ?ÙgϾïåë°¼ši;°±ÃÌŠrÝdä8°&ãŽÝ¸fÓéR6« ”ŠÇf×´ È±o©»ñã€ö9iYkȹj)0ÿÁ®å“²Ø/Ž,i7'sÍ!çPÖÄFÄT}ÓÏ´z†˜7àIÿwçæÖ ;!ĽÞnÿ-ìF˜q•XS%î)×$•$$uàÕö•LWºþhÝó'tL äñ„‘!ìmèž(송¿ý˜¯$¹ûáÿó _RãùØ ë‚ç«]ùÓÄ£GëãöF±£ˆž–³zÿ»áóJÒ»ñD{gd§êLÚrµèßñ£´µ@ï„Z­ |ÐÐ×NU­Çn<ùõaZÙÅe+”zl 'Ãh—¬pvcúÛ¾4=I”üX$õåk}ôÊÒ¯à)v#‰ílK®‡ ú\h÷ÊÃ[~Gô‰èH·3®_}¹á< ’ŸG°9SÜz »±,ûƒäV¢ˆŠnlêðj³€vÇ»f©°Ê×û×~$ÆX\”¨t(Âøãƒ¹g±-ƒãÙaf`P^®'WAÀïKú´4솋sØ‘k²jð*JÛ71”k ÕGÚ°Y¼Žq+ÊhµÉ¯tŒ®ˆ._ßÐX0@4\ûé0§©†Z¼Žh)ì7‡i(÷ÖÁn4>äû+7Ümˆó¹t€#ãMØÎ?NÒОþŪ0¨m·6¼‚ÝÈ-y¼û“«’zvªÑ*ŸŠ<Ü.Õ°DÒ÷óv»œ¢ ¸â½Wm–IÈáúö¹gØ £ì¬ÁžÆcÖZ—‹Ø É añ‚ æòTî¼:; c•PæEe0-Ù\ÙQ­\[/.c7NôLïe¬!ûJôÏÓƒ† Ò7â!Ž>n|ü(7Û¤U»ŠÎ“ —XA൛3„›í™wÑ€ÔU ˜®QÐ)“u¿:°‡%ïõ6’Àí›û Y(ªÚÄ}ç\O¶ï(ùSvàÄ^2°kÔ3.c7¬zùÖs‡H"ÒØ¹ó±ÿ?g®á'Ý`ž{¹i“Ø4Á©Ã?û½ÕK4Uêb7™pc7‚_>iVð5Câç7ŠZX"øó&;›ŽY!þ-êoð‰j¼K»Ñ¡õåHv£«°èxGÖíUÿÝ(ó>»Ôê"‰ž³½õ?Õ£^(׌®b7¶‡1oVpAMùéV=~"`ªÕxŠkc;ãRÆqþi¼ezÛNo¤€éî —±6M±Šè+S­JÒK 8þþBA­þ*Ã7¸S;"ÒÕü‰Ÿ±,þäìFƒÁmsZ¨Ôﻯ•ø×]zߎÝ(¾f׫AB@˜‰î^:pœ9÷ »qÖð£ËK.ð5´í ¡ˆdYìŠ/ ‚Ë–²TÕÐÙMÉËo°³Ñ}œ¾ØËœ©ä hZ~Xøåüã—þµ`7úÎOÒPC{É’b 8;ç¶b7]rH òQC±ë’T^RÑFÅgØ?®\ªªÉD R‘ÐûGBR7îk.a7t†/'¿~8ÃÒÍÚZÝØžáþÀ»ám3xt¼ŠsÒ~þÊ2†=Û„ÌZ°,õŠŸ#[¨¨¸ïb]D¬&̘#Ë 3(2l[5þjÁžýþÿ颵ßSOÇøQñ+£Àr:HüÁ»M—ÅQÉ^fÕ«ØÚ£‘UÃÌ"À¯Àý€dê“‚˜2°¹¾6ññ6ÚÆ=‡o*uŸa7td)rˆi£“ZëœÏEQQ§Ï7ÉôBür/•MZ1Ò¢|eùäÑq™>„ÝH4ú^wè¦êù™¯øØÊ iheqΘ0ÁR¶¦?{”òÄŒ1ÐD}Î…ÈjìÆÕ áÄ>: r¹5®”(„ÆN ÈT`7\ònÕr; é‹”¥1 #´X§ñÙ™˜#Jßm(‰.2A"ìgCʶê‚bê³'þI†C‹ø² ³\|µ‹ö¼ÖRެó$™‘^yÑG`^áÈ ›’ `í]ÌÞö.†Ô®Þâ I(¸Z6{Û]ý‰¿3à9AXJ^ù·ïº£êvÆ0ÆÆë¸y¯ÑÁÉÚbaòÀÛ/»ñ—4L}õÎ>mÙÇ`QP?»ôŠ æ» þx·1ƶçL&b7~—3r©$Ø1áš¶ß÷ÛéŸÝ’°WØíNøüS†ƒWß§5jÊû1Ø ÞKj-™RÖ Ü?Ï«1aAðmK¸82íô½,søä£(f¬b€~i]_+Ÿ!†õö¨ŽôfŸggfÝþ0™ò »±© ­<'Á™ÁPŽ9ØëwëôÄ::ή®7ß@€~äPåb žû{§ôž`7ö¸ˆD?X ÇƒØ GÅëuW±³?íKÎN?CRêÈèIøßF-ìÓœ˜ðƒ?–è|ˆkiñ3äûÛú&Ì,šžz$z¼²ãœöJâÕØ%±}²O9ÿ`7ÔšÎñ˜‘a¬Hz\TM nÊVå¸î–D4•º‹6½ú¸ç>>HW§¡lŠÏ×ë&2HÅ’ä²-X‚s.lé]ßÎè-å•'¥“ >Oca[-ÚNžúˆÝ 8½ZtG¹íØxûfð'8}Ô^e´Oö/{„ÚÂOçÙÝ““Šp–Ï?Ñ »qÊÑWúUˆ<\Ö¯ö¾MÀQgÇþ¿TXMŠÔâPƒÄì?û†éÐó–ñh»á?rÿ¨Êzt~„?ÃR ù¨rÏ 6§@¶‚‚ºñ‹ÙãX¤9<81‰ÝØöEÚA JbÙxÞȦÃg/¶_°ª¦Þ}æhˆ&fèue FÙN ÿÂn,·2÷ý VC¹Ó&¦[K¨¨[aƒ¿÷VAôÀ÷ÔØ|ÑæG•VHhâü—kê>¨å‡Kc\ù £Ót} vCvBCõ vCŸáeâè ϒž~ Ï4CD®د=õ*gÛ©¨Ç‚Ïš”  &©‚;ë%†Ïö »ÏktK›«ï]dÃöà@Ø_~$Òò1Âûº/X÷ùîqdBû–Ñ„×/×ÇǯrŠ€õ%îMØÍ{ Œê±' ºösóC\ Xs@ ¬Å.÷c7*”5´Ü7j£Á,~¿„X*ZV_;1B|¹|hv#vCùéóÊòȯÝ6¥»Á•YofTa‡ Ÿ¶$;:X!¾}åoŒ™`3óD`†²=º³&{ïa>øÙÏ?Çn„Ÿ>gò‰ŽnŒíx‘ý@M‹±—b7ö¶  b7Ü—bË6Ë!Õä>—pƒ9B{Aú¦` 2Ü÷òÐÔ]øù³ŽÃ…k’¡.W>Bú§´küëÚ€@“xUçWfd».ªPr£#0¸ò×µ[“@ãO††ZkƒÿÃ7IXg_–U·[õ\Œ®¿éÍj-2æÅ›zn¶ó;îIóÇý¿üMûÜ©xwaQ4þPRÅ3FYÐñ¸ãØ §kÝŽØÉÞÝBȈÅc8:»ºcHæ1“ ø ¼¨¾øT tžìô°ÇndºÓ@Ò(l%¯þB]pW˜8*Ü'[ÚŸizü¼"#T¤yëÖ7k3ÄÅ'<N„rôPŸ‰ñ¥ Qó“’|wièÍÅÙJÙw$øì|ÿ´MKû_oùß •‘»µä& ïó#5ñtå(v#•µ&R-^QâÏ Eb7²6üðxŽÝX­Ú²tÝ­}èHjª‰|¤‰Ý¸¥Ãõ7»óÛýð†P34øÀ‰wb‰xzÓHâ^ŒÚre^Dï7é¨Kc7x#·(y¸ÚàžÙy¡ÞŸ ReI>FØÎµÉŒ‘ Iô1þ±Ç;}tÞ'ývÃúíÖWJ:2Èoï²Kg˜”í)hg8˜h¶ž;A‚ýÊ#±AÕ¸³¯æà?ìFÈ„6ËÓzETâ©ÇÖóÁ< ÑÝL«Œßw[vXÙ‚ì“1-w%x±ZÒnŠÝ¸«ss8x¯h¹$„¾K@ÏÍšÖíØ©¹À;‘ ªpS1Á,Ï;N ‰àžÞ9-‘*­‚‚®82rìQÄ~É¿ìânq[i£Œò‰,P9`5Ƈ°"c{—R% yν'éðCô_Ù"v#¯>õJÕ 9D(>ò§Áói©"š†P‘´íhF¤H[ è.£¢¡$–GsØ NÆ ÚÿÍETl•„f86øb7ÞÜœ–ÍšaÄÔÅæôa7LNQ¯\Çn¤Zî®2Á@s†Îdš1|'ù‚MÔƼ\Co½¦"ÞŠ=ó’4ÁM©gÉ–4̨ˆhIšÖ‚½¾ãÌ’tÑkgרÉ%~änP4—Œ÷õãƒoröœG×]¬ù„ݼ÷³‡_"OLª¸ŠY Ø üòSعó&‹æaü{Là¨b ”yÄ7Îb7Ô ™žKk£¥qZÕQ*:š:êyòèñîe`Þ[QZ«qÙZÉ-ýÕ¾ŽÝ0.:~ûºÚé-ò#ËÅ Y;mx:¨Ïâúonבíшä Yæj:œ×?j9†ÝÐa ×M¤#ñ6©/™…ÑießBì†õ—µôu¨êUGN4Å9}5òJÖ#ÔwHtä™ ]µÌØžºàóæØÓN¦IÆÖðË2çõáÀŠ*§âS0ùqôkôGf´##´úœ°#xÇçò8á}<_d³íaãÞóú#)²’°¦kc¤ƒèçé?£&ˆŒó¦LzÈ¿ïÉ#Ï$úÈGYd˜Ùß1”´€z?j¤4ôº¥–Ëõ1ŠÏv=#Ã}þ+ñÏž2Ë{œøÿ|Cüï×äÿÝpWSá«#£óœÇ꫱Ïó‡Vô8TàþÕ:¦ß=ZöÓtB »!µtqÐ}ƒ5d]¸P4Ìb?u¨ò¡¸ÉÇH äŸÄëb=M#ÃÀõ[u/·Îž¦ 'Cm+ž?ïMAåmãbºØOá¢Ç~`pv4è€Ó˾í\$”ù»ª¨ýùÈhÖ› ’âã&Új"ýVb*E=ÐÑq ÁnÞX\Än¸éœ·?Õü¿ÁÂsu„˜¨UÃnx _-\ž·D+Y£FKÌ'ï9™£C8oh¾í`Ž´BÆ[vw¿Òᆒ"brØÝ6n}Ø æ²™×&»É´Éïo8vcóƒ-‹•8ç̈G¶i}ÐG»šŠ¢±Ck?4#äeÐ ÓSñšhHÕp/Z×Îø”ôfîëQ |ý(9_Nóa ½ò±[ 4`>!E$AeñÍúfv4/çbÅUFhbxûg[n NÚ r7\ÈØ-S—×ʂ̀â^ÂçsŸ€5®N7°oÁAõëU8ëýÂ?d´ö½z¡‹ÝXË.oz©®‚žI˜^z»S±¤'ÙDŸ vÔGìÚ †Þ÷ngŠ2‡–áIìFÚó€]B®ÐþÝ^=ú8ºè5mxa ´ÆãOÌZÈÃâ}Úå]4èº\¾É»±ý{†Ã5$®p^Iÿ&™<ºápÏAÝè2 ¦ ŸÖb„sÝûwW±…O–pDÏ0²ä¸&°6Á"ÃØ–ýþÓqYL°¶d´Îü¸1\Öþ£ŽÝàâ‡wùýT$ãðæ|nš& ¾%ÝkfÈ Õ¹õK vîi ß“¦‹2†Ç÷þãGvJã":è‘™uä q´pÿÙüìÆÆ±ÃÕiëE@ͦr¡AÔEÖF›mê1G"ÿŒ®6ñCþC¶ÇZ)40dÓŸà8ðÔ¿]LN½je½LEÆ~Œ—/¤¼hÒAEô‹/>ûÉ£;·wäc7Xwæí:Pn‡>9ŒzJx[¡õƒs;[·0AjͰÙdÒ_ºô¾N‡¿5·n²éX€ý—àé-£t”¤>²YÄ@•°´ž¼ôÿsæçöìdÅnŒ¾ï.)Ñ5B\¢Ü-ù´9b3ù®gÐôÆÐs|“Ÿ.C3Wº—~3RïÌ{LéÃgn1+ŸNÈ”‘ayÏŒü—T³.ð:ÂÇcˆ–ëJ‚² çAáû] u»»Î¥$¡ï×Õ –a:èÅ×»Y¬'þú›:mz(ò"™§š€×³hé]Ê‹ŒÔ}Áå*âàp§‰¾_^Î^x2¶Lïc°†zl µàù-¸Õá€ðSF–ãÏç)Ø ¥¦šê[Ø\ 3íN5¹9?­‚ݘjñÉ>Ç­E&Ýé¶_µ âÏ|Ô/mm´iGâ!Yà cíÇ ¼FðæíõjûÄQªûÉ׆)æð’wûf53¤·¹UN:†Hí’Ù(9Ü×.Þƒ‚Lo®žsÁnÔ ¼æZ Áýíìw6›é@ú­å¥NZ—àŸ;pƒç¥¶à=db?eŠÝxÐ){èŒ$ªƒäÑÿ{ƒ5Uš âó.¡¡n‚H­ÍNÔÔu¤G»ÁúÞ—a7Þ¼½3,Ö ±nÖ»móm‰H£7 µBÙ¯«7¾]q ¿³±–^éºÙ «¶\¶õžøÂ{¼-r±ºœÁSÚ8çlðb¡üê×G®£³Øk‡…˜þˆË î@wÅ88£¨ÍøÄÚΘ±;âG‚£¡•Çy®R ŸÿüKìÆÅýŽ_ÚQÏ…Uâî°Ì—I[=ݼÊðßݻ-l¸eºðIU \~ÙžàÂnH6hé{Ì@ò­'[ÌþL¿×ø…ݼ’j,Û§ ç?ÕÚnð¤CMµKÿ6ìÆÜÕ¦)=tÇæš ·§"º°6ºÞ9s€X!] Ž[¯†îÿ»ýöv#óY…¾?Ze™BT Ø«Õ8ß@.®³ÓZØãÙOsj¡8rð~O\¨ÕÅn,Žh÷PC¿wþm¨¢"õ³¶Ž‚hm÷†‡T ò¡=UfG×Ú©½ØõÀRÐè=à°ø¾ò¿‰u{6<0‡×ç\Šè1AâGPK‚1Dx=ÏsÀn$Úýó¬ùFEý’·vÑ35!v]½¹=ß0£iSZOψÌô-t™fê¢ '‚¶»®ò£&«„Ûü:h‡•°ÐÏqä®qÐV¨•Œz¥òE`}ü¶M°<“WSl±?s„Ê ¸ÀûìÀ¨~" Ò ]:6b7nD¼ÓÜ£¤æßÆó=IE5o®W88BŸ «æÓAϹÇßê$È£·•l³°ÅO÷WÑÊìÐǹ੠+´õ1¸ªÍ›¶¾í#Ù#­Íò”Ú«tØ=6»I»á]?7;ÿ›Ž _µ\LFSKO .`7â÷®‰ãr@Íë]Þö!3aÓþÛ”9¢“=g-Ë•tج Ìïâ.¿šûÍxó=‘<®÷ú/ öØÀ·ÇŒìSo™‘®MLo‡#;S5.·‹ ßÅ/½ºÝÅ0Nbת—o£Ÿ¥6Q¸o‰]4 ö*\{§ûDi>,NÓ8eÙ”Àª ¹EÆÜ£À¦õ¸/fy·<Ò¤HÃU‘Ü—Ú}Œ]c/K³d(î²|ëÝÊØVÕ –†ÝØÈÎîð»!µy÷’ý 2Šš-oîÃnôWó«í„‚ëí1-È]oÇhÆn0ë= h\o ÓŸ«n3‚oTY­qQýôˇ$sX)îþúÆÖÝö°ŠÉ›!¼Ìhç5íÑÙ¿Û•‚ª›£åa7½o²Ug‡Ó…®ÿNÛê€òÚ°Í UÉí¿F@ü YE¯  ³¦íÅnäï|¨Á–'‰”i”x/ì†Î56-K¼½7ùu¬«øE0ï—ð"'zrò_µÂÿÏ}ÙpÀn(§ßy?˜`†jþø„Ýè ¹5d…®K­ÙìŒZ—ß^bVÅn þîÌÅn䝸~Pr#ƒ~6ïÍGØßO’%щÀ[饟ôÑË,çÔ)4ìu‚É+ƒžPŽ­»zL`Ý©dõ£ó9ƒ£û ,7ûJäSÀæÄa7¾5sU0u)"房ï&pŸ¿»Ô<(±Ê(|eZYéb îû-•`´mæäïZhßãtƒ¾3Сòtf>$ I²Ç^s‰ ¯àÇ÷6UX™éku¥CYÊ£!Ø)µç‡ÌUÐÍï2×F}‘P=œ–>@”ZàPCÌâ» ¶E›ƒÚ½_YVØÛ’j0Í-~ÇóRcéPûÖ‹î€Ý ÅDù˜ði¡SSÛM޹Òà¡Ð±ŽlìF«W¥¾ÿi5ô,j]^ 5¾Q ùŽÝ¸Ÿê÷n®­ë~œaG†C“õ\¾ÂèìóO«ƸO¢1_а®¥Úa7ö.||õüÉñæ„à^£ÃÆp䵇cvƒßUëzDZM‰|ÎÒ¿Bí¹~®aFŠØfíÅZpØ»°ùœ.úVúænÿ?:£ã;¶NÝÐýì¼9I-ù•\PÅn¼¬. ÏI‹ÀìõƱë-õ‹æ£CØEzÁ•7x´Á4ž¿~ðS°›-jÕ´QØ•ü{NSQÌ—a¦ûGˆ­Ïô†.âëŠqLË“GF¬†N`7rQuÓ×ìÐ{ŽÎa+TöÉ`ÇQ*|=7±æ/lbï~ ÏȧÃÍí=ìFèÖ ÷š9:’YØh3Q%Œ–óJÎb7ö;·éMq: b¿|3»•hg-oQ™#«»3óN› ±£–ÔÊp]hªxÖÒñû7CroöØO}P]Ðùû¾ßbÎζ¬ÍŒ¬ýÙ®g29Â]»Κˆƒ½é]ŒÃÞö×·ˆHB‚Tž£íaÛð g‹Ñ••]ºå‘ÊÛ|ŠyC– ÌPÑÀœô"#X¿1iUÀD¬ôžMèJÃÖúü©BÍ>Ëe‰±ßMdH—­Wô¨haðˆ(Ÿ8‰ÝV‘ÏYÀn¹z”ì*mnËFcØC­±é¦"*,=ô@ðôÿ¼q>ºð|Á8ò2¡Ædнêð8&‹Ýýðki~ÎÍfÓpK6C‡:¶½í["|ní±Æsb¸Èp‰Ixd$P°“ápl»AVÚ&wy'ö³s­ýÀn̯(Ðì3%‘êïQÏ/úHýd¦fvÃ2k>åËš4ºzÜñ”xŠˆ«Çóý}Á€S÷é‘$8ØbÜ¥’Cí^›“Ø ß>_™>Edû*–g-`#u0‹­2ÚŽ$üÊr²…„üÛY7·)Áàê±·Ø ×¿×£CýÍàí«úUoÎô‹Æn´ò¹|=×  M[Ü<ÃwÒ¡ŠYW,»qýЫNGÄ’Ãz)*XÝá8%:@|ñ~¹˜³N ˜v‘_ÀnX´ÿÛ…Ý8·)%óÝ@3?Û¸%š¶ž·£ƒ±wYJ£g…µÐÉ?¤ÀÉ4 ¥ðI7a7Öªþ¾û•£†jyžû¿º÷NR‘ò-çÉc9šðçÃV‡½là ätÁVò›xýÊù¹š£‹Êâ}H»˜¿””£î)‰{]܈£‹£Øw{Ê oÉ‹€›ÿ»^ t~UW  »QË´.óðu#Xé1¸§¾oŠÝàävûÍJÕFBŒ£2ÛÎSÑyÖìûöåf‰¥ÇØuPzmõ«áy´Í†[â0vC™^]b‡|+µî·BâO®¸Q˜`ik¢Í/^{TPtå´gJl…þìÀn<ògî _¢£#³–1èncRNvƒý |÷Çnܽä!­çh„‚Y–&zæ58¤–a‚BȬõsѺp5Ö £eä7Cþäyýª}àÿBûn^_O}¸ü’éíO_Ÿ¼ä¯—~ûHðÙÝ/š¸ÖÅx”|Q¿™OL-}·ÓA‡ur#·L¶Ç-h5衞sö™l9&P²5õ“Ä"£Ð0»ð0¶þŒ¯&¤aXïÈ©Pµ>Ç]¡ß6 d½¢61÷„‘YcÝŽÝè‰ê»*‘F–é6ÍãùdôyóïÖUì†=ks>I¨WÿpÄ2kë“—¬¡Ø¥_Õ[D­!NÛ”@ÖÖa,ŽJ–âÆâÍA·»èð&¤L\ “5Clêæ—}ã hŒÍB–ìDAÊÞì– ØÇ??Þ,œgŠÜu -\€5–„ɉ¯K—8m,yÇ‘ 3cõW°­ç‹|7ÞD°•ÿÿQŠ:ºÝø©Åù4æìq]wÊÔÔš }Ü6k"ƒÝ¨hžÉÇnT°ìï~uçƒå9¯—ˆÃß–_~Øe…zÛ¿ï݇ LÈ!©Øäȇ{œÜmÐ»ÓÆ—èWOðª«ÁñÍcu.H¢óõßéBú¥ë-c7Ö8Oy>$O yœÏ£·Wç^0V„lÜ@×!jLû,öDi+V`7ÔZ­nš~VD®Úw%6/Yq‡žµQp•QÏÊ~i¿½-$å/u‰û+Äë"§FìFl¢ÊC&?3øµ1¨«î â–7°«¼!QŒ Uð}aBu¢Ã ˜‚avƒòÆŸPôTAú!‰LŠH",îÃèñ"Ü¢ö–›×L^(8h§"ʰq‹£še7‚¯eGÕ:ì׿zx»ÑöìÔÄuq-¤5¤éæµ¢ÏÈÉß±äþVž¸B5d/îĤ×@Eb. ; "ß­¢±‘Vô0Ó,`E=.,{Ý(#¸üs7Î0X}·‘•°Èkdøv㔥°Íÿ*qNùSªê~c°)¸»Ñiê™¶úÏû‹­ä.k‚ÊwrÕìÚC7è׸ág-(‹ïÍý|YMŸ`yüEQÇ%·­j£³vÞßs‰£<ëÒ >Ør;Á%U¸•t[X »êc¨ô»ÑyÈ5®s^® DÅl¤%ZÏ»aP&¾UGõuíÐÕÊ£¢è©-#ìA#„Ú›r«fôâZâc…Nyä#À8 »AJUßwÕ©1³qزB%B•Q:ªL éúªõ0§=JÒ♤¥>áØ ùæé>Y&=ÄúïÚåz”ºùбìFÕ«¨Ì>”D«íõq5BëT g‡ÉsÄÆßKÅ­©&(¨÷¤ƒv¼.8*”ó4ÿÍ( ¿¢ð£>Ì%K·µÁ9;ÍP®í+³1>6çooXîŠç»’ïrAC?‰eÕ‘K¾\ò•·NÑA—HäÞ;Ô â°ØŸÎ==äùû»øè%àžzº3Ml‘ajÜTüŠÓNûÙ6ûYKÃì‘J²±RãÐ)žàµdX*$µúÁ†Þÿ)ìF8}”fŒÝøpø4es•H~©ž¡¡†Qö¿$T ç`˜q7¯6ìø*¤l‚ÝHþ×Îà³ V5Áš¬Èžá±GJ¢nA•‡ÌAÄ<*é¯zÙ½±óPæ àÙZêÈìÕ!V ÚžøôÞ{ì†ööuÛÓ5Ä¡$äÃî6P÷òWnf#¡œ€›—Ùrð–H¸&mO†ë•^_±k—É›òïH"ý÷þ.€Ý°tèÙ܈Ý`=*7\–lY"°ï]Ãé_¿_0ú#û;TƒI ¿Û½º4«7/5G@ȤK² :à:s3>I¦voßRûé7ƒ(o¢Ý§ßN©§MÙÀ`À«›×Ÿ1£3ÿ";À;¯ÒŽX\]b|º•ÛÅÓQìe‘„‡¥az¶§täõT!Õ Â°_ç´zµj øÙØShäK§êÚ…›9 jX-`›îÛ¤aM¼ù$—\ƒ´ÕèS )m‡œN53:o4ÍÄn¬Zy”îÆnˆjÝ[ª;CF¹p¦T»¡Jæ4“QgâÉw·ˆkƒVœú¢(vcÿGZM5ÌÉNð14ã\·s6{ÄQ@úü )ÎÁ³Mžk»ö ïÛ=³ŸŸœ!>Þ#¢"]%ùžMýjKAÅè­ý_ìÆ‘©Š;Fâà#v4W+XîœèäZ‡Ý0µaÏ"ÀKìmíIk2˜WT+rê¨ApÖØ^…’è[pðmìÆ&Sû¡ìƹ†1#ÄÕôo¾?¸ÐÌõ ×7`7>ŒÄÿ˜µDñI©ö÷²ÍÐ¥ÆÊ âmKÄCb˃GÎVÈdädžÛœp@í¢ »QhÃáaƒþÝ»-ýÓ† *µNSØhö[LÃe’èé¢סA}$3bçz J}x%µ¦V}þa§ž#áV«/oŒ¾`t®Ý¸ä{ʶ«Æ¶É ÊWa7†fkWDb«3~––ªµ•m•QW¤>¬ij bJ±_Ò” ë w_ vCå¤ÿIÅÝfðoÊîFßsrªzXL–© ¼K)^>SÒ;üléàñò¨_vc±çÇÛŽHÄuÆOKàˆ" í*á-:2@×øB¢þUE ?… ~`7zÍŽ-`7¾Ü/›÷?°‚ï5 ¤Ã§ü£2õØ ¾Ý浜rZHõ§É˜® ê)L¼TìÆD»m]êm5”ûë ‹ƒŠÖ¹ø²˜mD‡ÌÇu)È.7æZ›8ús{~°»QY¯Áúnbš‘oÿjлáòüß;ÑFsà3½nh˾Büs»ø}}1|rZù7ÝÈö2Wa§!`û¹çš&Xû’aÝÂãÞ»ÃG{µàåÏo[Ï—ê¢ü¶ï‡ÆÙF¿›×œ6j2N”e Ç9§1íìì†Ó>H¬+Š4Gê¨úõO0Oí­9òß*0f#›ký;wÑ`ŸkFsv#ŠyVò»‘6J(ö8½RLE7ìÞäf{SÖk&ºØ¥þ©ïž1) Ö“œKÎØ Õ :“/…v(eO­ŸÇ +tìFZ؈Œfì<ùÏuµ˜õ¾=N‡–¡?K°Ö’fŒsFë|®˜wÎŽøøš¾CŒ­ûÔöžÆnL5íË=†ÝhJ ™ß~’ŒNÊvÆëc7š--jÏÉáž2êöè´¬6è§>û5¦¥Èô­ÅBë¬áaˆÝ=.K#ÿ#ªî(Ž&½×)|Þoi÷,î7@‚©·ó7¥Ì¾_Ž ™îÔ¿rƒù1γ¯îm¸GC ßålžØ‰ƒÂ—l"t`øjÈ'VZ†6ÄsŠ€ ¬?ÇçÍɰWˆÅŒ‚Ý8ÿZê͵fIä¨òµR »aȯÅ) p`òäÕ!ÃDý.é჎ë±Ô[1ìFc…À¿T솾ŒÌ%3dÌä7Òß¼Dz™ÃOu²B ©o»ÏgrÂ$ïÎpì†\ý–—»°iß¼®í´$CØ9þãØ­±&<Õ’èã —âO}¤rNôëVì†A|iÔ>i(:qYNÿèÚ¼ïÇ Fž‡ïnü`ÚTç’@Ê`^GžÃ[ kÚÏÍ("íÇ‹U ¼–Ðý/ß>xu…‘±áÛ%a#[Ð~ ·‰vY 4jòOïÃnÔÄ]÷hô6ƒ—Ò»·~ï Àwú5W vcoüÐ?ŸU¸3y|û:+:ð—\ðžÀnìQ׺)’ ‚>¾ˆáÏ:®ˆ¼ç.Å ¼ÚÍ5ͪ"ë穎©8ºóqÉ®H9zê·—ýFXeNÚ¦àK‡šKúkìÆVõj”µÐ¶™b«2 °^x¡ä†Ý˜”»ÎÖ †ü6 W>¡¢÷›·ÿéÇn ß{ÞPI¡ ”ù4Õ,vqäŽÚpû £‡eSßM3¤»ÿôXb78Õ'U­±oìÕr»ü#v¯ùyfø㬘$a1 H«Ù{H—†¾gسÞЄ¤‰÷¾93CŒ1¯¾Ö( u ªÐEAiúœhÃÌÒ “Úhr8¯É`¯8º¹êZWÝX 8ܸÏPÖíùbŠÝ¸Ü¦WçŽÝ¨ùrè{þ~AÐaàpó£A²~dÔ9솰êßj%Smôçß@Û—2*ª.õìw!Ê2ÿ–JÎk£’òö+œ ÈòK¨ª-v£•ë’Rî;ôX¥Çás†²ƨFg[}*äR<ùdcGã­ä3ÿ;½ ±Q îEIddše¹Í»Á½·»í«’ ¼ôçøIцg‘pZ°å‡5:ð7å8AÀû“f;Ä‘ô¥ƒaæ0z0Në\¬zŸÿeÒ ¡í ä¨u¥Aû²9OëKéc7 =.ºõº‹Ã¡‘Ws1:ði“Üì%jçvsH%àÛ÷ñ½¾&dð0«ßåŒÝ˜8帹]‰&ùI`7¦Û9)ØH/:ºkˆðu ]_‡æ-}¾ c7^ü+ÓUÂn|Zp~ŸYh†âÎjä?\"²ÕË©%öV(Õÿ}*'YÞ?cŒÝØ«<_U‡ÝXÞþÄ” §^lQ¯ÀnPûÍÔïK¢;‹§óz‡ôѦ[ª‡Ó°‰>l÷ŽK#ýòyïbø¤¦4¡òõ#5þ›P‚ &9hö>‡( ñÃjZ»q±hWÔõ¿ŠèZåÛÃ…-4¤}:qq…Áæý½gz‹-ðý6 ¬T¹ 95'ìFrÒ¦×v^f@øêÿõŠ€'N²/°¬á"<ÃUÁûäY§j3:öíóà ÞnêuR]™õ[LWDG_Œ\Ý3@œýN1ÿ­Š¾è|åRŒ5‡Ö¢?Ÿ°o™Gí6ÂÈÿG/: >qÚ6ŽÝà´>Ï祡…~-)ý2¥ATê‚Å1ìÆÙÏÃFÇ‘΋ßåžQÑcyPŒÚ!ˆê‹G”C(è™ÎåœHq4aœÐd„Ý0Ýðê£Fó4ã#m{¢v#¼È>#»áUþèÄõeÂãøAXñ6†æfN-ìFôž^ò¤ ¢JM¨{Kó–þ=ĸùò¶góK-èÚŸ#oX­‹*ŸWÍr £Û¡îoGµ‘dÊc¾¼þìM7=Çn¸Ç¥l•0M®+Lù‚¨9 }"»Á)ð÷Ñ@ž ¼ù‘®áæMƒÕUiµ2ìå\ÿƒý–ÚHV‰YàÉM**¿·°içÁZùÈ{mJµ« ïvÕQ@Çö[c7.'»<²+°CUBÜ ~ç¬PÀ½!Ú%)&`²Èž¶CRÿÎDÅÐ!ËñFà;ìFë¤Áé1=û6ðÁ±-v-&îÇnü¤q*žes@ôeᯬ±FhŽW0DVdŽ`5Øë#rØÉHOÓ¹  ÌÅëçµÿfè_ë8üLÔøŽHrØB\¸mö–‡Ì¨8mÎbÛ'hÿûå œv|žŒÝèz´/|F|»dÛu®è ·¹'Ùg7NA«×䥋ôèDë„*ZhŠ-`]dÌÎ ¨û.˜ƒëí‡;èû¤AÚ®11œÔÇ€I/]r2ˆV¬¼èþô˜a^òøüY솠ضÊ&ìFdݨKr<oPñÃn¬y&°i¨«À1™B-S}mhºÚès»ÁzþäæÝlÖpfÅš_ÑÝ’ÊLÃÜüÅÑ3£Ù^î¼.ªŸTO4@®§ß£„‚üÀÆçóV@ÎQ×{˜PõwÊ%ìFÚˆá•É qxðÚý¯ÛQèbÚ’ò™„ÆYù“jt®{aD†º©žŒ#Ø_Woz•¼‘Dñ©Ò|ÿŸ‹®7­Ý‰Ý8åáä­ÓO"vg×Û_«ëPwDˆv£ÝŠ1Ù6c‰žÄŸÿµ|Í ­õ>u©_"tzï˜X[!TWe9ý‚ü<®™`7D"Üy׺[Z[F&•E7ý7c7®ê÷ÝÌVA¾çͯ^8«ˆœ¾’ûU£›ý)m#ªh)éWvãè{ƒWqØ ªÌ”¹ÓFØRƒœçJ«ømQìí¦cŠ%\:ZHy˜±+Þ˜{³¢Ê±;I§¯°¾PCò_ _PQ—]WÏNA´b¿ã§ŒE¸f¸S=¸.â‰Ý¸øóþJûÍiÆ•íÇvb7•[9«°~y7?Ë..¼n5ûÜÁxŠvevc8Þ~Æs# Q༬q&TvÔx–1¼ä5¿¼Ð‚vɦ»Â÷tQê'}«ÃÜè~6Ë@ÆmÔ˜|™«d·8Êxº‡w»aK¡]|n%Sþdf,Їê*‰ZìÆ­ª¡’ AP¨ ìvs£Á†{A°ñ1*÷mµQG‚¼ÏµÛTT ©p»Áϲeà†¤º*¥ÉQD¬Gjƒ÷a7W¦dþ²: GNßJ”ß}óL 9ÂæHïÒƒ&H~uÇ–¨Ëº·+îÇÉÖߌ§9¾ö[šõaúúƉ­|¶P6k˜‘š‡+ô9@Œ Rñ;O‚oùýû%t1|—®Ìü’€ÍM®”k:ÈU±Þ¤eýñq哦Øe=ô»òy®Ï]ÈXç÷eu‘»l³h:k<bF£¤ádziÕ‘>FØ|¾ïž2¤úÑ»¢14ÎËÉÂnìÙDyò»á7.ºkÃA2ò±_Z<ˆÝ¶}:OU>¯¾Ù| m@y§ƒ±¯_qT°²XƒëäžZ?#¸û0+ÖË÷ZB¢¢h9-œÝÏÒ ÒðŸ3‚q3„Þê÷ݶ€®,Œ²5¦ ¥ÂƒÝPmL>¸»q°àñ›à 3ôÏjû²ÐÝ%‚Pá[7³BE‡~}½Ìâ&'o™c7æ×ÒOîÆnŒï±òÉ3 C©ÖÍøìF•…´â`›$:¥) 9¦6~Û>»„ÝØú,dßÙmÒh/s£qK\Þi=.~‹óÆ¿,6'<¿q! xòŽp™‡a7þ=¸ñµ—S •|r~Ü-e #OÄEÊ­0ºlŠ—«ÛÂ׌Kjf¯•à2;—ˆ$v#æu‘`ˆ»ôÎîZ{K@èá¾'ìFÈ·•ú™mª`QóetÊ ïÿø`7žMw™+TAãc7EsÑ­æûïYöbwnÄÚª¢²&‡Í¡èEo}9vC!á“5Kÿ¸=,)ür;r¼>%ݨ»5yW_ ÷Ô?gèÓ ðxĹ—Ø ó/FÙI¯Õ怷é%müþíh)v#»Ð¿î(ñÞ¼DåGŽ‹J±¿£ÞýÜ{aš!Yº“×»Á?÷»jPñdÈz‰ mgý°Ó¾¦ *Fc7²:8…n¦!Î?×*ûë4áx½—êÏ!†z^죥§ZpÊ'“c¸^=N˜^å@¡ü}ž&_µÑ½Š¶;=âˆ-;E}»Ñ2›Û½÷ÆÝo%¬±<7å­¿a7ì…\«]†ÁÔdä¬ëÄíÞžÒ‰Ýh.ÊZvÐFû²Ï4%×R‘Hfªl‰í±;gA´eD-]n?<²W ‰ðx(`7®¶è»{ û Di *´B=£¡o-Ä™`úÙÕK¹Cvè¶êÆ#¼!t0h½×Í£kwÝO7»‘õIæKÓ•ØõèFlhY vC¢"4»ñøÌËN»“FèÊi±<}ž9Âô_¿ÎtîJÅöÛźp°ýZìãߌÆýÃî³Mú ”Ût6a½-P~$DÔ1£¢ÚKM”nxÿàøúI`ÿ)¾JîPãÛ•ãiÑÃùw4[¾BÝ[]užçŸ ¦L]ßóäè¡ËR Á¦õ& ¸î™šôÒcÅ1·HvÒ^ì<3xñˆ4èyÝYãïc(ìMXH‡¾pÞNRŠ™ç°/ Þ¥Í`7Žo{PNFª'ÿ¢tì†ñíÆm:*pdOÈå¹mÚÀ6b„ÝRÚö¥|Í F¤›™LB@æÉ/ª¯8êŸ,Þ­åo,7¸/í:k€—ªawÌ ¡=/‘m(¢Š3BÆ€‚dÊÇn\A¯æ7œ‡ºŒÏmOëÀÙÕ!ÝýL$DVÙÇuˆ€¾Uuɰ”øµ©»Q×|x>P¹„4•M`7l,Zÿ]Ån<Y«™¢®OødöWs£hÏ›\ÿŸ‹Fì®eÅnÄ[>¼þ¶Ú ù?}ò°£r‰P U<4ll…Î%™láä]ÄSn…ÝÀåÌþ.vƒ‰ÆÝÄD'CV˜K¶E N¬ßÚº»[U5MOô룎ŸokkÒÐòŸÌþ„-Ò(O)Ô«úŽ”– 0{t½`„‡1šíIÐÏžH[¦ðÓØ Y¶æcüJèL‡MÔ˜œ%ˆ7þøwh…ñÖ]úÒ1%[È-ák)T‚͉û˜±Ö/{›\Í ç)o Ç{Ži\ÎÁnhÓJ›ªBÃ$WN–® –ú8c7²Æø>ÞTAã/>†æ("æÅÅ÷¡DçGõ‘ŽÏª(xÒ:’5ÎJuZJÞ`7†×ä¿ÞÛïòÝav CÀ¥H»qì@ž´h¡}o>¶Øo¡ÅÂåësØMû?­|PC¦,ùigº©hËÖûVF΂ÈÝ‹u$“‚hkÍÖ‰£wDÓž÷Ø _¶ÊUΣӌâ¾ÃÞØ€Ër¿…šÌ!cÿÕ”ŠK ù~ý$‹mưۚ£òvCïneQ¼ ILžÜÓ¨ ûŽî‹½=0Ä0}ÁA{¢A¼÷¥ê"‘bÏÉ|(-}ºþÏm´ä$›‹8*0?bCzJF*7ÞHïÀy#Ín¦€ßÙ|׈âï5Gß;Îò Û®ÍÎ4Zü¢<€Ý G»;´QØ¡EÿTõ:Ýì»ÅQÛ.o| ç™ìŸ=Ú'PÓˆ¶ÃFìÆB·ŽxÀE;dpÛx¤Ô 9lÒ·Q” àȦ bßìPôÅ–>:œYgüO»QY8©É©¤‡ê¿Kù{Ü]¾ýÍ–ñÁní9.üœÅ‘öx…e!îÇæœsÄP.w\k¸ J—Ê×+×…½û¶ÕïiøÍXIœž­º¯½õLÏoIدŒÖlS 3òwe–hÇy£ ø~@ ’®¨vïïblÓ)wœÿ&~!iu·uÐz‡‰nùuÁâ.É”¥‡d ™,dš€v-5k×üãœ÷š,ë¸9¬^ŽÏ·L‘†ÀÓ›ÒŸq÷16ºÆÞÊ'Ãu¿çîmWƒNu1ç±æ‚v+<'Ik·sŸi05[‹¿‚Ýh/ß$¤¯{T*Ø{iCò“v!ì†øÑ”fë+`œäÍé8hño.ùûˆ£ÉÅ{Ÿî6‡Äs›çr Pÿ¶±‰Ê3D»Õæcs@÷.|6·…‚äøßš°flÔ_jçÅ©Dý÷á :PºüF³~M m{vK‚õ÷‹?­ÒÈp~äõóiìÆ6åLc±_’è{H\ä'ìÆæ×ÝÅϱ¦oÆôÕ¾;|Ü 48xPQ¹™ »Akr­,š¶DÃSÄ>¢Ö ==kÀ}âú¿‡¨rÒ·B™ûì_Æm“²m±1ÛJËY½l=²T|¯66ns¼fÝø“{¼®ÿ½$R¾RârbBé.ýÇnÓŒ‡!RÒ(¼ ÃVò¾ômìjÁú@y§bM‚ý?G$öí¦€g{`L5v#«xÖlET ŘŸ›cQµÆ')óg+ŒBy.WY[pHo\››W­{Wî õk!§àëÎ'\ÌÀÜÜb/ÏGö<õ´ïÇnh.¼°ÕR…˜”ÜIŠ.6xÞsÀn\yù¡;¹N?¹¦ˆzJèÕ{ˆæ¸Ø·vïUQä㎥"ìÆƒûÌgW±AZ¡_OoánóÚ6tؾ;¨jv#¿¿€Êf©…æsJ–îiÑÀ¥@ð¾Íd}8ãb¿«¡nbûÛ[*ª%™Sú°Ö|å ÀNAL»zƱAnó<þˆv!Ô¦,hšqôcA¼vã[›¯9vcQNJ3tl‘8Ï Oß¶3V«Ãn¸ q|NÇ9n߇Cž“öVÒ> 1nÌrk>Ö‚ñ¡¹¥Sͺhìd.Á# €ÆÎÝ|«ôóiÜZNâÈxƒ»!ønKó 70¾D)â³@5&/ °<–ׯ·i ÁµOæ·ÛÐà …÷Å,vÖ\ù(ÉUñ<áË2yLEbö¢›MFˆ KAóãÚÈ:§ö~t¡únj­ÍÝØ[)úF$Ï)ïËÙtË ‰Ä®ÿû[ˆ N=¶½ðÑEîwl(ò¦Ãçh©Í€Ýà_R¯U×C.guýï¯GZ陨­× ¹Õ±Vë79¾h„ 2Žme#,ó‰ÙíûLP«CÓÙ*]8vå°ôŽ»¿[©Ÿ6ÕèC*54ç=Ùª0³ÕÌè×½ªDŽVØÛµ&ð£Œ2L5ã‡CºÚ›#.D’·¬¶ Á:ÄûlìîNÖ "ýžþÊL†3ò._ôRÓÆWVÅÐý]ŸØÿ…â>O~<é¯A†-Áë^‰ëªÉÕgæ$QŸÅˆi;vÃwù¾ù$vCõä‰NA™oÄ …â oTþá3vc$$Ê[»A«“¬x`†Î=—ø¡_²Dp'”7êX¡&":ÖœþHEmÅn´^ÎÙݘ–q>Ü­Isƒó·B±š‹*K;¾I"ÝeyµI}$ó¡TëvC7½IÄ…]Ðî+à—PbsOéyë Ç- Ïl3œ_ÝëN“›ì]Øâ šÏü›”ÐÙˆf“TKxF¥öô¯0BððªKÙ‚Gõ®ov\Êp3gè[vã;å‹€³ž›=çù¿púÀŽkR+Tº|°>ZNŽ>zò·ƒ†çZ‚Œc:vcñž õRA¾âç*ÿ)ŸËÇ÷ Gþµ¿QEZ¼Œ ƒxs YŸ9¢Â Àgˆ¢s‚6ÀCk±…=æt h¾†Ýu“¿e§…&®¦koÔ¤Á£×/šb7è¿×‡L©!4jé÷ŽŠD/$Ò"\Q£î瘙êˆÿ±¾[Å”u¦Ø¥×WÛLœ¦ù»Ù_øb7öœ)~ƒÝ˜ÍÞüŠb°HØ8>·2†Ï-´ë½ØfÇ/\yº44æÑÔ{³EyŸ.­}7Ä`Ú‹æmµ .fî²×3]tföà÷s‚H:U_xW—6w{àk/ŽL´Î‡Úa7.ð×Eîòº³v#Ô ²)»áP¯ûÇSø§hÛÌi0¯øvg Àv•~í'žÚhNCuT ™»Heï6!¾^göý¤Zíz/ßkP@åOùY°­Wmžä`„[Ø~ÔX¡RÔ?ø„Ÿ Xnº#Ôg‡vdZ^ v¥ÃÓÚE=ìÆBس3»´ôPt‰ÓvyQthxtt'vãLÈ\L³šžó`ºYl„]V×f‰l­sß‚LÐû³š«÷tA¦SM’¨üÍ(u>üCºRžXcQ±É]RÏo2£À//7O?r€0fÙNŸJ¤þÙn ÐÅÈêÏ=4Û' ‡·°6é i”ä•_Dâ»ß'~¦è¡²þš ßžšÀbmV`ÿøã'ÿ‰BÆwìºûËÍù¹ÒЦ]àÄÒǰ“ª}M†ÛA¶¬ÝõŒO™žŠ°\ÿÍèc7ÔzÂ:‚=ÉH>æÒx+v#z=û±"Sðà)½<@R?ÆÛ5b7îÄnH_´‚“7ÏüûpÒÉ?Ë÷îGó¯¦N¸™C箩‰r”q)yÕbß !lB}mhpÇ1ê%*Ñ»Éeÿ°nQ©vÕâÀ½SB[ëšœú¸ù´9vÔ¿Úd.ˆ/ãÞ–çªdø$ŸÒB`7¶òñ°­H¢$ŸT=v£ :m@T ÕéeÚ‘B6¾Qñbê˜ÎÜ·²0šøãSÏv#(½Xa†>Þð™.X"4ﳚÉR­WÄ‚­ê 䭱܆ÝxXÒ¿®»1Ù~àòu2œº·©4»±;qWï‹QI”¾á{ø›)}t ™iòv㢊‡Å/)tî˜èÛY$ÞéÉe¼`ð³&Ÿ^6&Áa éCÁ;(°gÖõä$v#®-­p“œ’'ñJhl±„ÅòßÞ®0΋ÕÖ²l°…ú½¾¥W6*C÷:öìÆ§;ô’Ë;Ì üð°¿À7Zfü°=¹6Å¢ª°Wào`°ÖÞ^4¸†Ýð©âîTAԯ敼{²tNñ¨îüXÌ#Žçù¢°’«AÂßõ§bgšºþw#‡ù¤Qv£=Žâ)š¹@TÛ;¾M45†.ßGb°Ûëî\5¢¡„.ÅÓçšpŒßm«nv£(›Ãÿ¾ýÿ˜U;tÑ¡[iâÂè»T£¢P»6’W°áŒ•8ú9Ìu8»q‘“_|C€ì2´||×mرë{vcôbóiÕd!ð×ýbGà¿KÄdv#vãàþ*m$¸ xj©ŠºK_äo!d2¡Åè½6Ê‹4¸þçŠg½ó~^Îx9ýæÃ/Ø!éÙÏ¥> V¨®ÌØ&‡‡ /Ü*Ïì¶C•IÓ':TnMvŠÆnxÍÜ)çÒÓCëî?ÒÞ½K4šÑuÄn0=÷Ÿar@“ÙŸÝ0B,?Wy-Ïoxž†˜ »­Ò”ótAOº¢Tþ›±kEF¡¯\¦>¸Ç¨QmaÛ?úuftü¥Äû÷õK;yáó]ÜY•øÂ»‹!Ç\s?âµÔ­üèžG:h;òþZ½ð‹àóL*îMÔCc+ýBŒvÐóøõYtxñ'ü[SþsyÚt߬Hüý.®_íePRTzx²È k8´º´ç>ãׯ¢‰ÿݰïçŽÝvòÿç7¦zþî$#¾üußc7±Å·–*ðæVþ[›£ÚP7ìw»q¼ÐkEáÌpå´ì>oêûHB½Å‘äMÞ·‡œÍÁƒ›S·ªÊù˜<*Ê š!n½k»¨ˆ½X»0˜BAk¹Ø7ÕÒב<Åá0i¨÷ÌMX(=õ"cE  x<êG€IãÌgU%2\%ÌìÁnh];f{˜}jX.ÁnÈlKœšÈ¬‚!›’ª: .;åha7>hªxj£ØÀð§â¯¨ÈÊêÜ¡KÔ"ìzà{6Zúâ¶nNM(/û a7öÖ%z6Ÿ·Ca¢×Îf +DJ¤gs2AíTAaî ;Ô0öôª3î}KçoÈç`7ßË+ƒª ±¸\.ŠwnŸµÂnhìt0TÂnø?}çþ﮺ªB¯sÿ3K¬H†Tˆø˜ ZãýuͺÐý8Q9®ð7£®íç•b}àe)MÓÔ·6‹%ÌèÓg å; w—³‰Ùm±mÿQtßñT½qÀ‘Ù{ïÍŽ.×>®Y•T*«PZ¤d$Q!²•ˆ¨J¥Îƒ¬2C™Y…ìQÙüžß¿÷åusÎûûù<î9cNWç»?ø³ø·ÿ×îÀF è[šôãà 6ñùàù«¨7©êJ µ‚Dãü Α<£b…óD/¼i²U(…f|V»q o®ÍÓw$Á¯èÃ÷5•ãÏÔ¤¿%!7 ¬¾•{!7¬²výÜ/ © jN‹È ¡›¶Œ÷+ƒÝÞq±d0W¾è„܈ÐÿZ ½lzϘIÎÒvºÆÊ‡ùaà8ý°¸=`ãáKêotaÁâ[§±ãK{ÿ×mMÚÁEXAyÛ`‚ÜPǺ^2·òŸ…ƒíeàÛø¿€G›¼°‘¦&î0´fJIu›ê‡ÈØJoSQvøÒ9Z;¹,u^Õ¹A“'r÷±w?vŸ¿æk03<‚™üƒÜè ><1o µzŸyš|ùÿyæ\J+÷×19Þ”ì*Y3È9G?$¼‡à¿%¯8!7r®XèÛ±€aÁ5IðÝöWÁôÿ=?ŸôkSfýËoýÑLÙçcN"7n¤:¾¼W& Nîª-\@öLJ†›eM¸DÍT"™HS¢=[LT@ɦø_-äF‰ë‚5+Yv×™¦šQ­}§¶p Ùa[Kðh£žÀ@Umߤ·Î#7þoyfig úXs;'0`#õ3b ¹aÏ¢ÔÌñG¨Ž´·rZ cž§u¹AÚ‘’¼òKº§d—¾ø(ó”ÈÆ\g¨´B%hC½MT¹FžEÏß"7ÜFé ¦ùÊ] ß ²øÉüÓ»¹ÒŸPÉzT™å¸ì,M»ë*ª‘?tçgöf{½r”áRþñCìAáØú·4ÿþ>:š…úz›gL 7Îür9jºwîôÿºèÎ=:}¡**`Iê^¢ù‡ÑEœ¼Ë§eÔýŽbÈ iý1Ö$è“×јóU LÇæ¸îmǃ’?sľPé4Óôß4aqR"޹A.¹Äþ‘ »ž—sõøáôuQÒ#ä†ÃЫ. åH b@nˆ¿>ְܰzðä÷s€Ö˜êeuHn¿Ëa†Üp+®±Mö"Ã\ÿK _‰Ð ãì‹nåIlà‹ÞóÑV2ÄÅÕf•ƒÑ~¯¸û<í¦§|â­`+O®%Mƒ$^LÊô¤§N?Âp|²‚ÍÚ+-ÂÆZ ÞŸW´¹1ç5¤ÙDÑŸ*jâá,:FÈ¡’œ¨k¸ö®ý2û{}hœuþä±¥eìÌeOƒG‡ á ‡šeRƒ&°Ñ¶Õ=‘2‡W^j–IÓÞ§nŒ8Z‚ LZ‹lZhRnÕÁ^h´/¸$_«æ9Ÿ×BýlÛpš˜•OB@ÓiÿÑV ˜ñôô½S3˜ß»ås'‚´!]¬úü›nCðð»·ÍÀ ®t#O릂¥œ©ÒÆWbà»ÒNà?Ýø'ß—ïnI‚£ºÍ÷j•á_^=¿‘ŒÜ°°¡„ 7 Yê³üM%!F³2N¿L‚‹‹þGÃís¹ŸBÏC2Ô1˜UDnü,§å­^0æ‚ý{òõÁá´¦T7~H”´¨+¶¦ós3¥>êÂæÄ‚û¶þSû™:€G–¼TåTà3æµ}§y#$¶~°cBK•}¯2óSÆ7x! ëûk71ðå‚ð]qIÀ—ÿ0î#r#5éSd ¿ÌÂiæ2¼½ï‘6â·Dùôb‚"Çœv2CµsÍ\‹ÈŸäþ¡ä†õ)r~»1ô“ð-º³ŽÉó³JIšÁ{zŒ,òv%¶ƒâ.È—<”‘ól6?/JH‚~CëËœªÊ ºWþ¦Ê9&—ú|äÿéÀÃwîhg!7â¬8þ„§‰ÂáˆÑ×¾rý 7)š/›ðÀbƒñ5U>àrJ~«ScÃŽ.È™ùð4]y¸ÓøËš¥)ø{ÎíW@õÎ)0l)¹×hð¯Y9)€óúÓ‡¯’ï}µ1!ü‰©-S¸%Üb¸M7¶äf ~)‚§>âr¤µÀ—ol¹+È,²¯dA*ËÕøIÔÉÁd…ëõCÃXÑå+‰ï• û`ä'äFIâãˆ_ÈKr‡”êøA‚_NÝOÔw(}46È#"eg_œ@ni¼}/NW.,ÝœDnÄ¿ù>ÇJ€ÃzÍÒòãDh<´ýy¹‘¸0¥w«—eóž¿9Âʃ~–s ä„TüŽÕìâÞ•ÇÅì‚Üx®®oÜÈðO²´ÿ‹õ%Ç©‡¨€ú‹KÜ‘g<ÖSï$ÁÖòÔŸßÕ@^ýýþ[ŸÆqÒ™ýÆOž©þ-}šÐða$‰› ÊIïæ¯©$ø¤BÓ ?ÔŒ>"ÿ¹Ái|ù~ðE.Àù2Àò “ œ РˣÂì¦÷v:Nl½§iF…³Ÿ½ïŠÜ~×ÒáG†J^ī߉0WÜþ1«ü$ÖÒû7µ õ ã-ÉLçååà(Ŧ¬¹q‹¤u”ë¾|ËjtòR‹¼¾iÐÒ€ŠÇOÑ­àúR©à°žð ŒlBnc{Jý­´!ewx+¿/œOõÔ×CnL阵n[CórÙZ}øiþðɹe¬ýë%ECÈ"· ÒТ \Ëx¤Ìæð¹«M‰I: ÐïñÇ‹æ–@ëWƒæ§tZhöjÂíÏý‚Ôû§ø]>áÍfц/0lÒúãB@R›ÐeÕ¥…3' ~Î`i)d;mØ]l[ßg:Üí-¢¾¯à/ƒÒ.ñwPAêLÒ¤×{äF´Ñ$XèÆsèÊ#$é‰ß&m¥x'áŒØCäÆ%jb@rÃ#èú‡LVÞøy™¹Áªì²SsP<\K8'û„ êÚr?nɰ#RÜæÌÀOŠ×^çR}°ÛÛKSÍ•º‰“ѳ¤‚ˆŸW˜,êuaúÀvÑm SjéeâUP¦™_q]J6¼esBnì_oòLñƒUÆwCj4À%Ã_rÊÈJ¯Ô€óH yNô–·¦¸ÿBnäq‰Å­Š‹Àãn]‘½_&ß!7Ô~g-ÓùŽ9VuYUdí¯®g‘»c.-²#7díU嘾ÑnQ똺v’s”ˆ¬k•!çPwAî¤÷nÈ3 Q'†‘-2÷ºzE$Á66o«ÜÈI¢hU³ˆÀ>©ÅC+:°l¥wWrƒ‡MÆË7LÞ^Yÿºþ tZrî/hÂc"îVVByÑÿç%¨­OÊV]An¸Î8Ÿ5’‡ü%wÃíM®üuõËï·ðOŒró‹t–`t³oêáð¸n×{}ä†ÝCÿχ¬Á‡‘b–†Y ˆv~¡D!7ÚšzN~Sk«v~‹£œð|WÇ)Î,_L›ÞP„ ÁM·’¿ÈÁÛF•õŸ‡1‰’YRu…<ðncÃ%” î¾9DZ€í­àÙ=Ùü ùbc»’(öMÑü‡Üà{ÞÑÚK’;DÂEDHÀˆ‡Î…M]<9X4z˜ïþ„!Îh`•äÂpH“:7»gÈÆÙ9yrmCŒO cúpz–ŽÜÈt¡µµëÈÓ¬ˆ9°#YÛvƒ*`‘Uhß äÆå—Òn$Xüdµ©_ €îsz8Ž«_Ik|“§žô^HÒ„_ç˜43øÙ`dzr_ÿ22ÔÂ×™ÿøá÷Ê¢]Èj!A¢!\`ÒåÇ·GŒÈ;4NÈ>­ENêÒùeL'YÎ"7/ªo3’¡waáGD¨}4ËÚDjcúó;öA=ººÔ—ÉÁ×6†ÕÈšsÏ¢ßÞµ‚=.+Qsfðáû\Œ›;XíKz»­J+xš‘³í‘¦8¶;…g ¹ax3!Û^†ì4Üðsà…³lÛæšÿ翇œõ‘¦ŒTU½/úðª}µï©©eìCù=Æ2GCÈñ2Px­SØýú.®;‡§ %þ4¾¯ž»õç¥ÙZŽœäú)´ðvnïFW¦ ˜Ô¼,ãÛŠæÿ«—Fm¸¾ë·_o…/»ùIí> (æqN¥åÇ öÂ1ÈFÇG^ßgŸá3dŽ;¨jV}]ÁëBí?Í7Sþs^iß'ÔSÂev¦»ñ ¹ƒ·oHç¿‚µN•àîMJ¯þwc—ÿE÷L䆒áØXmI¨“?³G¹qçˆÝ­‹"(9#ø;è%8TUžýŠÜh>q{yÚ Øq7þë~§>ˆ¶&ºðÃ7C+êM© ]ä©_W‹.¼Tšw¬Êy ûqas¤WÀìÃlÙ5â*P©Á±è)r£ß;A^t•H5+u6i€›kÎë¼ðå¢ÃSk g7!È/ ê¼j5™)Ê€ÛJ1û”‚¤¥Ã¯KW‡‘7²_ŒItc¿ËòjŸßdÅÜ9F¿‘ýžŸ>™3…†G®Høõâ!ưu¬?·Øo‚ß F}¶uÒ£¿jì-"7þMï ’;j÷ا+( 4YåŽ!7˜E몼"pDzKúôšš×9H$ÈγGÃæŒ(Ìb_96ÈÜ„Ç÷'>iÂu^æý Ër;Ú9*ÔU@ô c©4äÆß§]9 yÈí~8›Ê“'ùÁ[xNZ„¾eè†ôôƯ*cÉc’È íÖüA+co“q_ÀÛä¯×Ÿ‘¹ß›"Ãibw¨ˆšÈpWBnü­uçÛ£ݵS ÷}•ƒÞ‡kÞ¥: cñfÍ%صKêÂr#i大>rCÿiXjÿe~Èœ¬#«…梃û#ÆËgϪÃܓΉ×H`뽉ª&rƒ½¼hÿ1tÏ–åÛ3K„)[—öªº²CŽ~ï¾FüÀtfÏnv~(mÑŽÝDnŒÞp[À«ÜjþîGn|Éãö{…Üíæ¹ÿåqq>ÿ]Ep(iÿéGÈ â«lúqt~ÄÍutX ÜÛˆ }?ŽG[±ŸûüXìy2wÐý§&ôJ•dƒ_Ç ¿$C/.šp~˜ì0Ã6‡ÜðæEE X5˜Ùk0C±pä†Cn{½Š='8ý@ík/ê_Ù¿7"ùE¡tfÉ0Ç‚ÆêòW{qUtS6—q÷©!Ã=0{XŽßÖ«.Gn䥈(³‚Å×òžì3ƒò_>æþÝÁ&Çý]ýˬ aê阚0ÙþǸ…ÜÀzÙß8iÃqË[ð/¼“E1QCnœß°TOÞ²†ïJmí:õaû^î3§Ç—±N£…:†P}¨³Y¡ODܾÄ5‡|:qw)F\4ûàd šÆ½ì_? …D^‡¥²‡6à–ìn¹Ñ.>°ÚÑäo¥×†Cû.y×r!À.ÜöIfXz·ªÜ\è™Áöh\‘•:¥ _ní ¥þ4¬Æ„o -+ø¾–ÕùºF*xî´]QØ,¼¢–Ò«&ºqÇQGsC$Á¬D‹õÁõ¸nY·V rCà¤àVrãüzß~uIhÏxCnœñª ÅŽ(‚›dãOoÉ "†á)rƒy/&æ·Pü"g~à“>8WÑÇI:Äï…)ÚSA©ç}›£Ýº0è±Z1íÁ%¬JÚ¹'CÀ7qGn‹ žÂ%¿VÜ`{Ý%Í ®k¤Éwh€=qB¯×x!k+.ccŽ{B£($غP°K¹ñæ¬?M'Qî>—ª€Üx®hš¾G€Ûù |Œ˜¹þŸî},°7—ÞürÃi!˜[¹Ñ÷jbOÛ°1Œ3i{X¼Ž¥´™q›Á˜ßû:Ãè@èÛ/ÇRž ù!7ÜT¢LÓy%Á˜óÀJr£¦ÿs¦¨¬Ÿ½B¿¡Ï(d½³Bnô[3]W±…ÆN6{êG¹€“:&Že5á–WqaI>À¦Å°·€ ô†¿Cnð^™ÍQ±“‡9×:厘‚U¥ïÁ׊·ðQ¿’¾˜U ðB¿VI=ý¡D„¸÷ 7~ü™ ô°4¾‡«7*–1@WtDm‡82fçU*‚½?Ý8µ@‹Ø䆗JâÂ1N%HKzózT3×ï8m;Œ~ö¬P ¶jßl »NW§e{!7ŽÏ‰Óxñ§ØÍcºòzߟðŠ~ S|ª[ÿ'û[òDXu½bƒoXßk( Ã=IÏôø!çíŸ{ê%¡ãøPÿ±[\ "¥ù¦æÿnˆgè• 7–ôœi_æ?\h#—çòW 7¾Ùõ]¿J†#—ú?Ú þ¥|F¸ú¥À$&å·Änø‘ n´_¿$é³Ô =Ÿ›ÓÆZÁ7¹?¦z†Í`ÏvçKƒùŒ~ßöØÈ +(÷öƒ©¨¢XŠ´ÂK1ÇôÌ9Ö†9÷W/4‡œ$E䯓%ù“³›Öð^Çï‰~}XûÙÄæôè2v¾+Â­ÚÆöUåb.Ú`~oˆÄïës¸‡ô…ÆìH@63VúqÄdN„Íâiá“ñX|¼ ˜{â9èÔÏô÷þê×hÃ݃J×›_ ùCÙÇ5àϘxŒ³s«º”žÉä¡ M2EN ÿ6Ÿ}r¤›VpÑ]ýëiŸ¨€&O6ʼ[ òüy{y¬w8>@Z¸, ¢þ’÷hÊáWg=Ëþwãk¨å[äc 鉲$¤(èp@n4“}¯{(–üŒkudð3R4ê r#odHG| pS~l´¶èÅc+Q$g~h›ÜуQE|éóñ]xõ”̆‰Ý3%5wTÀ5YýÇ6‚*PÎ=Âx¹¡óG–F‘[tÆì éÑßEm¬¯ò : ZÆ`ß¼Ê! ^¹U7º 7Þ¹º–èëˆÀ1ÏÂVäËv޾ rC*9¦Àz¡sxò¯žÄ {+ò GƃuëfM!»@Ÿê¸1¼µætãØÅuÌÉô{›¬ŠR¾ã-ND{²À ä[_€s)r£ÞáLÔ:§$°ðU€/Ÿ9»Wõ£ñC6uà_bz$rcÅrÑœESŽsÉìRœälá¡a¿Sšð? ?®Šð ‘E×T9ðbáGßÿyÃX:ì,† jœ0SMQ%מmál­{Ô—-@Ú†Leh¶¸W¡ùdºWr4É«ü47n¿{ ÿa`Þ\¥ñrãW×§gŠ`¸êkb.¯xÓÌáŽÜ˜ølFIQ‚_DèÙ–~ÈAaÙLû‡±±Ø\¡<”Cö²±rß ‚˜ë“NQ÷*¯+óƒBÑwJ1ÂZÀ¸K}Ĺñ¬²à£ÂUuèõ6¨Z‡“6O} GnÈ…z°=Q#@7ΡÞÑe"¼p̨ÿ¸;ìýš"SI€=,,ò9ø!¥înÈo䯮´h“²€Óèku‘­ð} ‚Ý7>?XÂ$%ù¶—Å Àïçuì#È Æ¡ô¯¹þ$8tÀ*:ü·ª¯ñ>÷jglh¸=“ª \gg5á›_QŠ,xÔð„ [ï4_w—æ‡ý¬ÿ&$†íá´q\ êr} l¥ƒÈ›ªÅ{ÙœÀ:¼j¤ZÛ~y 7Ž|8^J†”Š6ŠÌ4îNŽˆæžÄ*37p¿#ÃC ­ŸNÇËAS›éÈ.9×äœ+(кvÍz fæ9$vOî`tü™v(o<-®î•ÒrÆÆÔ~¥µ«è:¥ë`ôßæ…›ÝV¬ÒÈÁÇ]õFÈõq©OAcú0Ý•ÝÚóÇ2&Û4}ÒÔÊÊ8 ¿GyCêÁè§+s¸ØCŸ+–7t›Ø#ƒ“–@E“0ô$ŽVCà‡¡¦9†ÜØÎ½±.n€Õ.o,’àØŽ»Üèrâ9S`$…ëc—éŒ€à‡²‰ƒÈ ~‚ée Æs[ôÀ×Û¬ÐüèK•AäF™ŠÿîƒÈ.úÕÒûÓÆÊÚ°Ž ‹–Qèö™Áè»WŸÏ|¤Û#EžÈwS.j̇Ø$A1ߣè.äÆç¯Þóª"ÐåÑi.Ýmèv–ïŽÜPgóù%, +n‘vßå‡â›ðÌ_Rßøù¸¨Ú|KBP¹¬´6‘/²^ꕇŸ³¼LÁõ7¦à'[øƒô7ì"s ë+ûãæRPYÂ,ÐŽÜpÝ2Šñ43¦Få²Ö0°°xêL rcòòWiŠ`òHH#™K øò>ŸºŒÜH¾ÒLü*¯õ*ÄrNþ’ƒoò~¤±ƬtzÖc²• ¶Ÿ¯urc,ÔÍ©¹ÁpZ×ÕœT>~LŞjòð#ÈËÏ4\W‡ŸB_Š?d%>eÛ;…Èv\œU›«ß®Ú®áY-ö-äF8ñËïúbŒ ¡d_ä䇻wIË‹xr¢¯{xi(l/ÛöÔ'þ¿¾AçÒnÜàrËrúµˆõÝ8W°_ØH¦=;IËoÔú¯<»D‚±!‡¿òÌ©­Qî_/Æñ8Þ Õ$uPì¦xµlI^‹3ØW-Êæd²ÈÐÊ÷,C˜?¼p1´I¹ae0šÀ, &.50˜Àmû`¦ïTèýýýÍwõœà×Sß?å(GIË|èxÜ GöÏÝ$ÃtÖ†·kóDÈJ<¼—“}›S{§5YA†näTŸGEr°DeïÔ]äÆñ~ú<£ÛV°[Ž&Ÿ5ƒ¬–÷X|Gw0â_;<Ç ®îÕLBý±ºFø“%r£IÖ«7ù´6ôÚ¬mÇ)|кùô€rCùùþÇÉÖÓP[1rZF=Oô-c ü)ä/f†0†#½¸bFDmµê?:?‡ ôÏtü¹ªl¦|E|-Ì!é1´ð`ÔÝL“HðO«ñ­þ$Ê´K>…6ÜçÀÙÕçO„À"ãퟋ°ý¸[­mà ¶è;|¤ õ¦‚|ÇýŸ— Á˱ùH|'åu”#à»ãb ˜¦5k¦¯×»Í :Íš=Âópg†ýÿ»1m{ÿ_r#"÷oB¹˜$\yÞJw¹Á_,Ô=ï¥ÚÏ~.ì'vxR]¹G?¤`6jÎ.¿R;¬*rMIŽü°NÒ[ý—¨ðoxý™Ö…÷}>›/aã4Û-CbîÀÞs\*ðoc`—r£ž©©”¤&¬eè­¤&4@Ià›ð^8s#Âm¯6ôû§‰D&IPëtÉ8¹aš_Æf%aŸ€ów䯱òôÛÁÈúÇ…¢&̃LëÆ+,NøÌÞ‹ÜàZ;œ=2c M˜ \çŒá\Sÿ¯ŸulÞÆÚê(ƒÔ„kaGhµšâ®3ÈÁ¦Üt w H}6æX±O<Ñq6Üø]ôµJo?õ”aÃ]];:ðì—;kÈhÏR­ê]¢ðÃ>»ïóK\`„ø¥[ënþ¼àóI7ðì Rwz¼#…‚)àJÖëA}Oyø‘%ÁÑúœ)PRK¢¹œ¹…ïÎøsÒ>'|’ç“`‰þ}£¹IR¤ÇMŒAóÏÚ½M 0i Ó "7÷|l]»£$+~ÿĦ¤Ôï½K@n”›ì!+ÁJšÂä®i9K†ôšM†19-ý¿k©J2°ËA# Ÿßuv³ÈgëO|Ã|àÀ‘ðÈNN-°b˜˜x ¹ÑÂY×ȱe+ï÷ëD¨q?<Ì'´•/žÉ'À¨/ýÁT.~h}qà³rcåòž > 8þ¬#Pæÿõ(ãäÆÝè!ELÓîjn.Ÿ€ísÏ%sñÖéE¡ÿ?ŸRÁðé’ \st}6Žc¦‰Œ ê€ðOìØ¥Mhe-½f)ÎEÌ»` ¦ôŽê?ä‡öZò‘5ë¢ú)\ §w܆¹qÇýí r#âå·÷c³œà‘ø·}ùì$i£@nCnP:JˆÒ·ÈP Ԩ嬵kí›ÄÌr­cÞ•’a6›Ó÷:9Xûóñ&rãò3:ÂÈ-+èt3J@í 3Lo¡ØÁv8í]”3­àîï6n¶Èá€'™'‘“¶ÆWÔý´a¾ÉÖAÖ>˜¢-›Î‡ÜhüÒ>¹n ÏÉß]HXÖ‡MŸH¥îß–±~ ÈmK5„ÎAq§G–4ÃU§Å+~s8‰pã]fkeQÖ9o ÖhæÞ¢…œ½E®Û±Ø#±/çøÀ©Ö¹Ó/$Ûð_g7Í…²„À»Tº´æ h|_ ÷­žÁæ ‹ÊÞØk꺯Ù+† 9KíhÅ»üM«Å5ÎJ*èÖQq˜YKá¦yߺñ»/ì7ûIÞo<­¥9xÝÙØ¾ÿÝX8;åЋÜ0O™ Ô„¤Içâz"•¶rgÕÿ›?ãd0kg⽬F†Šæü“=Cf RDÎ^gJ¬ˆõÖò÷woxkPAí¦€þåe]8ÿø3³ÉvðÄ_£la ¼MdW»ÌŽs›#7¿œwR7íwÓ×ÏÏi€–aÿÀ†¼°ÐôÔÞ9u øß&ßaC¢‹-Èè/‘ûƒˆÀs¡Šrïo˜L•rooyÙ([Õa'”µÙà™‰.ä·ÁË%?䯿;0·ÿŸ1죮;ŸZÇÒó7EßÒšÁ£SL¼›4à1ÕιásíRrrc­ :—c¯$P_KjÊàÓ9# =äÿûÐêÂî Ÿ#êê$˜ëT;}JzÔy`·Ê¢vñŽþˆn ¤>ÃX6>PÇSf|œGKîiÖEn<¸ÙšƒùÊÃÛ{iœƒLånýCçnáì3<©?úÿ羚TÝ!Ã⾪Žì5"L­ `˜ÄBOÙVÝyA†õ#¼ƒ,ÃrP€×Ö'¹A§yR%4Ê .Šb²×Í`'S]gb÷æHÿgäáC+xJ¸¸—C ŒCÿPäÆÐ1á'­ÚPK¨)Ñ­JÞ²ä@nD†¹:j#7È'ªy³7ôa¤ÜkªÛ×eŒÇ¾y×W`éŸ ï[ÓZ‚¶ìOÏá_hO©›øë€|õŠ`Ç«–@éV¥]8-ܰ¿žùç² P~À)ú—Ô÷nM ·áv± Ñ)B ð½Éž²- X¨óI&úý ¦ÍòäYê~mH3þÒúü¦!ÀG+ÂÆ*Vðl£Ý{ÆÊ¨€!{N¿dE |’·k8üµS˜~Ïí- *Ï®ðP>fã–{ʤÓy«´î#È· ‡k9%á9®¼®GÈ cæ|ó#ŠÀy‡ÓG‰ &ƒ¢ë‘²Üÿ½ÍÀ¡_ÑÅËú€?üWí?T¼ÿ\uŽHzÒ£wÖ×uáÝžÛý†K˜Üe¦ž#Ú¦&s`QU|Ývg±Q—Hû@²þ+µ5@ö]…}ÈãwNÐ~UÁ]s‡îO:I ód!ùrcTl?©×MÞ8µ¶™ŽÜ¨™-inü]Ô¡õ^™@5Æbºúí>l ‚¥íÈÛ>^;«Ó¦Ã*Ì÷Ù†1|L³øxóØ:Ö¯½öŠs˦ï±:ø,tt¬Þ÷ý¿¶7î²åÈ a¶5_zIäë–nŽÜb ¨}j.c |®ÑîÒ…º2åV~Èóaóî:D`i^‰nÉ>v-<¼ 'ú¹übâ†ÌÖl*àÈYQæÃÈ«ñ[ØyyXï¾rÞ=ØÈÐgrøÅoáa·"Ξûa”ŠUÞäL*NžkȲ?ŒÁf™R½&-»<ßRO!7V¤E Ès!·¯Ï‡öj]§ më‘1-Á™¾–J°a ¤•¬ÊA9;ÑÜcØ0ÖÉðÞ§ó®Ô>Gïp“ žÆ©[˜ 7ê‡z.—%ò˜,Æ\~F-°åþE©¹Q1êf•|_^¸àŸV°‹&öÈ¦Š’õ€ÇÃë˜Þ9tÓý- ^¶™§iFn¶‹4=M&ÀŒ´[Èþó<•ÈÞé4ƒÜ]‘ ¹QúÛG~¹qS„Ìøõå<¦éóÚ–Õ0×^Q9ŽÜør­0ãy, Ú¥3þ´¡Tœ/†>Ç_‹þQ–ŠQÿÂΞݠ¡@•ô½ŸÝ¥Ø`Nx^?ó]2¤’¾Áìüp[à¬ÅmäFîù=çŸpƒKboRèMàë•ñÈ ýcÛ¼Šz\ÀŠ¡êÀmÔ¿tê~þEnd)¿?s ?ÛÛxû#)ýÙ´“ØåK+…‡Ÿ‘¡å})ýu9Øšz#õ rÃI1jI Ò ³Û¨1ÓšÃ? .²m;Xm.û®+8°¸«šI $CqJ r#ŠíÚéËÚÐnο)}ŒÚˆL3!7> Eˆ^³†þ­ÖÿòwÀÍ>Z—±=äßÔ7„WmIßÕw4Áü?³§ søè6»ÓŒ·øÃ ÇýÂ,ΚYEÐuZx™‹8þ9Ð,Ç‹­ÇlðëÁ¢4ßxÚðkîÿI/*î°<Ü¥ •¬B O*f°Ä¢u°™6¼ÔëíjBk|ëÏ9s”®àÿÆIÖeÅTp‹Ñ!A#RMÊè¸[»ñ¿É‡OI‚GJ¿Øù„ÒpKëòãÿ»aâôP`¹Q>ðÀ’YV3¨¨–#7êÆî_T)±CE7·È€XŸ›’ŠÜp3™Z^í32I·Õ$¶ô£Õ½½jü°-Ôíc€ (¦q5_¡Óƒ»ÿÖ8Åê/aÚ†O´„y|¶†ƒI~n"„G#7ä–ßi\€GHƒûê)¼'mþòB©WT(`@ã £þާ%i)ƒ£ä‹ÏµOŠ@&åÝC!È Jˬç1¾êsê(;l6–²0¹K€b—Ô8ž¢¹Ö¥ìÕ4rc˜~¶(¿€+-Æû­G˜wnÝçúHǦõ‹ríæ0—KWjž0Áq›È|¯æÂDÜò.ä·§!¶C@¶<}ßå²ITRŸ¢ÿe¼ÛM}ßܦ¤Ù ½B‘í«h2Œä½Ç¼±nÚ2ò>An?qìg~ÆX8…$v™ÀÅëŽÍ‘´¹žz¶'¸Àr´PðeZПžÒdŒào ¥ å–[Ýÿvî"Áâ¯ó»›7'°¡¢¦,¥<2tØ#ý¹åag ý7ä†e‘ÞDY¸ä &.\Þc9ôÈ ¯w0¬H0Ý'Ö &Ú`—ÐßSóÛ,En sùí\Ó†â«ðü¸0?ü*q+„¹»çßì^µ†é±ªý%û à;½]vŸ—1»îŠ/ÝÚ†°[»£Ãm7œÛ<¹í:‡kÜè\¸ï©´NÜpŠŠ¶•ûÓ&‚i¡w’5Ë×à.ã+´üÀ‡¦å4[Nj8~4NØ/§ó3jB YŸ÷Å3á[Q„«‘6Œü}Ü›Á4’þþÔ{¾‚ÿ=Ë‘VDI$¹[>ôâ h8W³©©ÿYp]&ç¸$Ðýtªåâücѳÿ»!|Ô/r¹ÑÕÞ¶[Ú,}ˆj@nTŽ©öÔ^U¯ä 4íÕWß,Fù"7 Z««£zÌÀ‰þn¹Dà /'©ØóCΰ!ËŠTP­MâXcÔƒ…ëV?k/a"JY{9,;Øpñ ƒ ´>™SY€ÜÀÖoZ+œüFF'Ðk‚“¬t xa9aÏx’4Ž{N?ØYåj ‘Ž^KŠi¾"POõ6<ŒÜXRêdà•E¹bzçýés¥ØÕg´Lâì3â¡t#rƒ½zàÈ>ä†ïÉ&Ö£ Tøw×Jc§uŒ¡úGí²)T(×ð®)ÞÁ8{¦ü‘QAfkÊÇ, §eF Ä–pnTmMBn›ü¦äå"´?5È3è·çïÊŽ 7X'ë?Ëf‹@õx/UŸ½Ü`sŠÒt¹ ?h/gžDËv­ŠîR ±ÂÈ=¦É»tÃäaBòë >·LÁŽÉÑ[8±÷·Ó´‰fɲ(‚U)¾ã&ÈÝ?=´~`Æ`Ȧ­I‚€1–äÄyt— _bà 5ZÒÆd¥™ Â•à ‹²ÞáT°gÍI?¹AgÏ7á`ÆÜ2ŸìPÀ'Æžg?.n1JW‡ï¿W7¸nAŒOQ£r£¯àdí „Qvoî%Á3¥Ó\´îì°´`àêp$æüÀËÎòòÃ÷çu‹â„Gª>Õr,àêԽѿ‘ Ö½t¦ÈLo–£jK3Ø®¤nÊ_z {Ù=)¹ñ`D©;?O.ëô ÒÁÁ–˜ÇÒÇñÉý‡´oª–iÅÈ+L(Næ¾$Ë_Ê_¾çN†]{%yLø¡Á¡P©êÿóÆ}6ŽÄb.à@™ëÚ¦3N®ZFnœÌqn]¸Ï~¨„ï¿·A’M)¯ÅFC2²RÈ0þ{Ïú½ž½.ÆV&°ŽE‚Ù#2$uWŠnäá]iö~䯧”µ;ì7­`„ÒjïVsHå¢#Uï`±Év]?Qá.ýÒ!¾ED÷Ë;mÈ ï^x,Œ§T0c¥¯éÍ(<}¯¼®ÿÔ[½¾h~T¸ç±œ¨»ƒÝ{„ÿïF,iRe ¹1GÉØ¶% ðï`úÜPS%æ_Wa×8zfx4ÀÂí ¹QR ÎTÔmèÀ?Ñ ãèñ½tvü0H:%|H– mRªË9õ`„þ'–3Kا+~T&6Ùç,×éT ç;+ÃVäF#°º,˜Ó¾ïbÖßÖ3*–yaÝìrKš(vE6ÅÙ¬H€ø”¯g‘]äÃE 埧&rÃŒ)͹±›{c”“…Åq®´K`‡âìTÎZäÉÇí\è”)¼Ýsf3*¿Ž4vëØÚ™WoæL¡Ho{|ó6¦öTÊíüÿû<¹ÖáÜh“ñŽÿ±*5Ÿj7TY•OˆÀ[½?ú7÷èÂ;KUÒd?;a×&ªq±rƒReµ ‘Mø±nwéM^ ­¸‘N»IR6bª‘O£³ï¹ß–‡ÛSÅçîš‚jX¿Ë>t Z}`ü½ÅðÿºrNRèoËÕH#7„ƒ“fù ŒÁ÷„€ŽÎݦK14'sÿn9¯(‚ùÛ®Ö3èü$Ïuwð ²îMQ»Ÿ¤­ |¹Ã,m6Vïªi c=U\-×” •psо*쓉iGnd{7?®äYΣÜk a‘¿‰Ühlfè6ÍQ‡gºSïñ®Áþ³R‘Ÿ| oý#3 îK-¼UÜ8Ò8bÄB€|_éVœøøá…ÊÛ©nÈ ·ÊáÔùyÜóˆ¿VrCÁwç 䯷¶ñÈ —iL†ùÉ×k;ú€Q!ØˆÜØÝ &Ø’C‚O_êg)2ÁU†Ý3ñãxø–Ívê5u |bÛ†-U9+äÙ é.Í·\×ÈðÁä®óÖ´ü°À¸É¸¹1˜}” «àmWwé#7~[±IôPawÀ“Jôz¢OŠ×?"°ï᧪#7$6ÝÌ3ȰèHç îºäi¸4½ÍžS¡G¯&H]ˆµ–‡BÏÏbÈ †$½õÇ7¬ õqàEsxÀFz÷Ów;Xûøî³èõ?qÊ«þQ€®Â>ž)ä†@®ú>BŒ6Ü 4 ú’Êøfaÿ4^ò+Ö°BñÚWUÈsòŒCdõ2ö'ù®“:Éʰs&ÖrR@r{O@¤õþmÏÆü ‹ðËlxµöШ*Ü-}þŒµ‘t·ÅŸMšþ²ñƒ·ŸM©®´mø×R˜(Qº6aë ÇOkwçÌ`-Ez‘rmÔ#Oç$³ýÅ´’Ç+øùÞç¯å ¹²½Ó]Í"¢!ù¶ìÆ5é«mC]%`Ž¿ôôÀ <—=çûÿnŒ³\kúƒÜPßË2R¸"].^J[DnH†ƒãአíé?W º™9ÏˆÜØ’½?­Üiµ^‘2Ïô”7møa©ÖÇy{)*8œÕê²_PZ²äj%—0Ï”*É·ûŒ-®ËÛ!ÀÚɹ_sȃ¾‚ù£À=í—TnMðh° ñï/4OsR‹RçV’–%€ûO‘¦$ä†Gà½ßCEà‡Ë)Ž\È šµ›ÞÈ ñ¸")<ÊÝT/é:;\,MMÿˆÜX=°ÿrãUbk7'E¿çܱ\Çjòxú’~›Bmé}à ÛX]¬orcYN˼¹á-öÙVþ¯Hºp¡ÿ7røŸ*ðŸF¬ÂuaÅ«{îÈŒí\Ô3"pª‰D+ÈÍ Î,„Ùýšðóˆô§WxA×~ƶ¿ :uo¹Áº×£æü}y8i¦û)0ÈØóŒpy ïèMw³Ñlgg—ÈŠà¡Â‹½Èù_ÙÎqzÆ@̲9lÿºŠ'ÏF"7nµÍßéVUD×Õ¦Óëp+¾ë7¹a5gÀ*sE nuO€ë\ò°UâT×q»¬qr0ð’\ïø˜”ŒÜ` ²WÙBn´©„äÐýáéGn¼úCr¼ïx›@ö±®žù|uøòq†”û"V¶øŠ#7ìÖ@6Û>¸ÕÀF‚ܼ˅ÇØah¥èZÜyÔH Ëãç‡6}×wßGn¸«¼/­žÇ™mŨ¹±^g¼S†ÜHº4“"üï7–Mzúݺ>ð­7‰Ü[±}F‚W2¡x=+Lˆ4 «Äã½7?ª&^Qft#{Z¹)Pr¶Å¡K º}Jÿðè}Áœê¹Éµ %;ymžFÝï¸@Ç€ÖZ¸•rVÔ¹aǧòiˆ úóå9.6ÇR„)rcñ¾©Í¾ÇdèÃI‚E£Ãé^³˜Iµ^bÛC2Üaæ¬y7·HwÒÿëÊ<”ëVÐóJ×¾!!sXÐà"®V¾ƒ…QÕ´R¯ZAr†ë¾s è¸ÿø´KËôÑgÁ{Úð‘/ß4G'?Ô==ܰ§ýÿϺ]R—1€N^Á–±¡±ø³¥*†PLQ'q‚Ÿû./4›ÃïŽïߺvP|´{Ë’m ÆG¾þJð§…²ÎZ~ ‡l@ÀÃc÷Žòòƒ*=éÞ_ë­¸¤'×ý`!pq#SRUX†ò~gÌ`Ù›át$m˜a³ùÀ“ÇüŒcéÉXÁo¶/ûKeQÁçú†½œâÀxÛ•W¨ªÇ’±è$ d¢Ç&µ”Oâ¤Bcÿ»‘e6±çrcÏžÌråE 82ÑOÿ‡gË‚¹£Aßr‡R¦²pàdP%ÃÞûã^·› æ" m*+Þ¬Y£ÞÖ|Wµ]Œ H\¬¯—Ѓ.¢+f„%lyJJýü^qëM·M¼@Ö`g{zïÏ”À­üŒÒ£{í´Q­×Æ0n ´]f]ü=/ŒÌ M«ºâó¤["PúŸ­Ð­xwóÇ}䆟c6×ÅK9¸¢à?Ï"v8ŸwHár£Áô×÷(äC .Wßa>*äÛ[ëª`²Ž-=¶>ùÓ¶íû|gÛx Ó/¥z]Bn| !ÝAn¼ê‹O ]”{­ÖÜØ‰Ê`ªÿ!ý¸´ýmZ|Ÿ.ì Ú{/¹e¶8òØŠÀšwõ ¸ÿˆ»ë‹ÓM¸X(x»Ä Žù—΀DU× º hp$Ôø>”‡_8g\酪z6±À-<9àŒ*¨µ“óY÷‰fŠÀõi¹ôL:´,zjË k :œ>ýû¸Þ×ÈŸ‘j{®Oˆ+‚ÊäyóY4×6gø³­öçv'ÚD(A©¢cý¿äaHçC™•al³mQø{€ m‘Ïýßo‚oËE5_TCÒ£Çq–Û«©Aê@'€öo´>»—rû˜|Ê} "7ršBVø Å½¯Y$avñ¨7ä žLAïiL`³ÿg¥`äF\ÝZÄ78þ1°Ìê“Cй‘·ã#«•G†Š?UxœùHÐ=®.úþ䦤sª75‘ mžLVýFýòÆùƒF2ÈíÛûŠ>_³‚‚ëLN¦’æ°¹} ¼òÅf˜±qÁ ®ÿꛢÚ[³~"ȸ¢ÜwIÚÐ —~¯3³tpþ=?‰ÜX’ûûý¯5ÜË>‘¬A0€ÂLW1ÿ·Ë˜RL÷C%C˜÷ý¼“ˆm5¶HÎá×[k3„muÀ÷ó/þˆ=µNTÕN?Zè@¯r°!’_ê…ùÁIEã×çþ´âjvùËAB uà–"«¤&Ô¬ŸÅדg0Ö½¯‡ú•µáÁ™ý±ê‚FÀ뜑æöÃ|)gžM¥‚§>öØðŠƒ=Ì¡_Þtã üSÙt$ŽN´¢Â¿3OûÕÑÿÝ8ãÌòï/rƒ5ìÇÝSðÚÉc¼üÈ ô↱ŠàfÛ¥¶ ¶û`1rüðÛA«°:©f¾E@=ÅÁeéß~~ȯñÐÉZ˜ ´ótžc zð´9î´Oq kÙWwRy7€ÖœÔö5Tø±¬©ŠÜøPS™r7G ¨uôwKj‚þèË:Wyáäí#lˆWXÔ›‘KÇÂß"7"žmXÄÞfíã_k±¡o'ƒëv[g•â#—6j|g‡æ®‡Š*}»8v1!7<Žõg¿¦Â/Ï\rë°uLXâî€Î°)\ÉÎ ÛÄ f]An´¸¬ù<,àë †ÑÎY kwi"7жžº ¥ÒY/˜u!—–`B'rããÄ=™_"pº…ëo³(7ØþÈÂmq¼ ··¿È²oŽüÜ?+˜ð›€~î«u’ȼoTÇ“YòP¼ïšAì#SPèÑ_Ïê»…›Nël~°²ZÄú³.Š ø«ûLrCZz€ªm xccÕ˜H^Úa¥#hápUE@wKF'p™èåØy"7ÈêW.ÞS‚¼”UY+qyX_CN¿ 4Œé;Jiû*Á]Vç#óº×%4‘'tÝ{ãóy®®jÔ:Ïw”³8,‘åŽú6¥À‘cá³Êl0ï2ò%ÃX ŸÍ"ìxœE§ŠÜP¨µ÷kàÓŒz5õ;T8}ãpKrC,óÌ4»70í léÿ…ÜX<2€Ü¸¾Oïb!žkŠ Š©÷ÅÑ ì:s¡£õ]24y×SБ#©û+J¹‘Ú?.ºm[ÁÏ‹w¼ì‘WX=ÈÛÁÞIšNþö³‚…§±ª£›º;_¹Q¯qß>Cn\×q<(+ÿFn¸k–ížùc ½)C©øâø´·Kù2, _+kÓ¯>±Æe)àã=VW'í9üÆG¯9's°Ë¥z…¶Ø˜:®Y݆Fø¨ ÎnÖ'‡¤‹Y£¬d–°}û4fZÕäüþý‡ëœßf:#7Öäß8” €WWß¿ÛPÒÓ<ÙÔ; ¼p×÷«Ì½¸–op+cB0­üdäÒVçÞ]YÙ›&µkw2{Ê™'‘[=Æå«ð' ‡e-X9 ÙtäOñÿ÷§ÈŸ3ö™4…*‡%¤OIQá›7q»·´Ö±_ƒv§>S˜óƱ–+v+˜‹®Fnß(š=„ÜàžbtÓü-Ú“íäN#7t”µþõG‰@¯xÙŽ,V]éY)¬A‚T»W‡µxEà[í¢€n°+J¿îÖ„34n—äEùôÉ©Á°ËXá¶Bn”^dɕΗ‡§j^ñ˜‚+“[¾'·ð“¬ ·7`ÜZkÎÕ_Ð :=Gn´J|ºG13ÅÿvöpEÒ8ʹ!Û㮺£žˆ:I²£œÜQ’‰ÜH+¼”{5S fµV—‡\æÛÛÇ冱ìªòïòžJ0@ñNœu,¾þ‹Ü`Ÿ¹Ütó/Pú¶ð¯ê'Lïû»ß¹ñWá×=îwê°É\:Àk’fëÓøõ‘—{.Þ'ÀBý[qí‚$¸¯»kÿqvxh7o¡ø!ôunø”&Èi„Nhæ„åw¸e¯©ÎãRv—'Ý3­!7´K«ŠnüžKœ˜ÖÌW¹Ø#7’#ËŸ¦@Ôׄ«1"DðàYýœQè8nÇžFŸå«˜—ž¥)0É4¬Â‹Ë?Ï œ"Caz·]'fø ݾsä†GŽíÓíf.p~;x›¹±Ç²¹e¹±ï 3£Œ;7˜°KzW7Lß#9"‘ýÆqÙ/ÉðßÔ09I|É4|.}p+¡ð‡'† +W.­TÉÃë.÷/íAnÈi¾‹úÈþgËÕÌá:”‚Ù;ؽҺ‹3VpŽÃ7øG?>ßÑuFnœ'}×z¬ Lß »'ÁþäǽÈ@ó‰G*ÈÚæ67CÌ>6´«Þ_²ŒiïÆ¤ á¦IêßAe xTB¬—PGnô§ù/é÷Ûål¯-ÑÜ$;I ׄ›>TRmŒŠ"+ñƒ)“ˆ®_­øfŒƒ‰—h?O¶PÕ„².³K ·g0ÞDÕZŠ„6,|Vi°GƸ{âVð‡â&OOß§‚ñ÷!6 bâ@ïà)1–—Ýxyò½{±–’ÀŸa¼àDÙ=¬Ó4½ü7ˆòGÖ‹£ h$ „쨻1rã‘‘§IŠ`åBl°×ºŠ+JæÈ쵄ª‚F3ù›ÚH16zÎó?_XòCƒƒ ÒG¸©Àã/ɉWGÚiœˆXº‚jèoo`ðMUSXú"&‰ŒU\Bn˜6ž}µ·FÄßaxŒ©k‚ÕÅUΖy^(9XO{a~ûóÍI€ž‚‹žÚÈ ¥#çˆÀùLö¬‹È 3çú'¬rJÊqn¦Ö๗&Xt8à·Xû#EÈ+gµ ¦'LáØsV·Jy*ü]zŠøP}û2Òÿ´Û ý6­cÉ×o]CnÔßuírƒ²ôD3æ—ˆä8‡ÜPèrQC9§Æ¨M\ž]~‘9á„ܸŽ' 0m C>_ö^EnPÿÐÔEÀ© §º -ñ“ ö•ïû À謟rÃD×>žï%rÕ'‰ë¥)‹¸wØõènEù•ÊóÊ0ªd(ÆßTS÷¯¬ÜCn,s§ìÖ4?mÎZžC×yzJ‰}!rÃ#®‡,(€Kß5¦)@ù@”^6rC«õØÃŒ%ØÒØ…—«ÈCòÚ¶ª¬ô06jLwê¶» ´âëé@n³ûÅT¢÷K5=gkÏ JTF&º†(ÀÙcõg8rãô$¶šƒ«Ãg~LrU?‰€0ø3èr#kÇ9³>…5[yyˆ¢$j’m:Ü0 Zó)³%@ƒüÒ.W!~˜+Íì‹Üpâ=ïþˆnÿ[tSë(rãÜ®óå1È:‰ ­[’?±Ð–Ì¢q}0m{*¹QnKý×@‚¾øJ'»$ÈfÛE=¿2Ž×®îQÍ9£ô¤oOÈSà ‰Â_µÿ?÷µ‹ôæò=~ô(h‚êÜŠ³>ŽÜ8·‡wýÞW.`)91§¿M…î³]Ý,½TØàþÇ¢¸Á!‰•'/ú‰ ñ½œn rc sòvã+2\^Ù± K‚a4±„=Xýžð”H2¼Õ¸Lü»<,ö~®¼.eŽ]Šõ¹bÍÁÓó|sX3æf–™²ƒxÞ˜Ü f[ˆ1ß馀ã"wZý‘oÅÎFûækÃÕùU™æzx­™ ~Enì×jó>·l 3#3y Ì `ØX’šÁóe,@>Eö­˜! :Ÿ¹I¢ßW?̦”çðåïeÞqú: îU´‹ÈKà–eTèâz ÷Rÿuî]ôö!‘hýâ2sjÅß»ÈT?9!"‡í¥54¡^CÖÕìð,BزYXJªÑÓ}U0òÇ.»(D¯àUY„X*P=võ–º´8¸³#ÐòöY7þöLóÆgSIpÚ*ÓóZ¶Þœû¿6¡gì~‡çC»% ç±¼p'ä†ÅË„ÖTEÀe;ŸzXd~|yJ¹ÑÒmüE¹Þ Ì?û s¿è3©ä/´à‡i3 ï¾±S²šˆ1Ôƒ&ÃÄñw"KØßÀDüï ºK’š-ç0M‹¯ã!rC†CëäZ›è Ùl×Ñ#yãÁ¬ÈžÜg+fÛàõû³úÃÀÚ•¶Ú¹o1^R(eY;ul‘ñwŒ4lû¾¼Z ªÇKÛsßôyrÀbMηO‘Ä#©Ï½‘º ñL*T8t‚§KEe“M/ûÓn S çŸø0¬c×S‚ö_GnˆŠU!7wÇ‘7 •!7™Mè"ŠÀâ‹]èA£p¹¡SþR¯{L¸TUÕ¸Á‹K¯»&ü¨5¯ýÁ TdGåvÀ‘>;È šB(S…< ,¼Ä^n º« F¨‡¶p½õmá–ç`]ÿdyÓEítõúyäFÖ{;æ«dc@¿/žé+_½Oç‘Aë•FûGͦG×$¼Ì©Dn Ü5PÇË”`*ˆ®çчê{ú'ć±’ãçí&\” Ï qÇ[TðìMø‰Üp™TçRâKþºw¦ú( ¶v5$¹áùõ±N>7ç’b!‚‰ZÅÎÛÈ .%ÎZÁÇø¨3Á!^‚§öÔß<Á'¶M‡ÌжE烀0?ì ¸Öd#7N×joUvÏá÷4u#7Rr|d> 7÷¼1þ4Š™JǬ/ŒèƒFjPËMð/¼6ƒ•¼¾:Ì« z&ÓUŒ@è÷¹½–á+ør]å•%t~ðò*É‹LÖÝÉ3¯oTå÷Þk, ø£6úL$Ó1ó+þ‡þwc2Àmu¹aéèW°Ñ*5þù>òBnÎþþô'SÜ-?ïôþ´ø³ÇêöŒ ~3¢¿S\k¸E%ªh €œÜÈ3ç‡QŽÝZ¶,TГWõxÓ\β8—-,atŠ åƧžŸHzϵ«¸s7x ½'ÇMáªw‰›:憼f¬“_Ç HS6†Í¦ð†îÝ€0ã5ÌT.œ> ¹‘&Wxn¹²ï&á‡Pé;§×‡Ü˜ýs´ã{¶<;д¦É¥ ïK9=ùŒÜPûᾜõY–Åž ×àÏ_ŸÙgjÕ„Ã'ØúxÁEÁùuÿ6P­åæ)Bn Ôon¼—‡÷ÈÕ¢ÌïP¸M”úBpØÂ çÜuÃó-ÀîšiòëÅe'äÆ˜f¨ÿ,É´ÅO[± å€ñ‘w‘ÀåÁ(%ç¯ ë݉ÍÔ/šv] ÿŠÜ 9;Hþ¨5à÷ ]yè7øñyÈ0öxtð£ñA%¸ü‘{ÿwt^df¿OcGÑ׿ŒüFÏ à?é±m4£[^±½An쫪ÈùøY¶É•É$‚µOÑò%È—ª>D¿g8(ü`AšËUxNŠžd‡v¹>5í¨eUãô¹q‚éÅ•NäÆšà€V{ÁîûÔZQ¹ñŒI1y¹ñõYÀì+ÏìÞíîJò >øvv2´¹AꟿÜM‚æµuéBJD °/Uõªÿ8~ãŒkeÞ1uÀët1ÀžDÛçOG«³A G2=¥¸ð`R %&!7Z3ü.•r ºÏ9K6©päï‰åÓÈ º îŸ7º¸Áµ‡‰/w«ö@äGä†fp>ÿ{2üiò.𦠭oÛjÀŒoØ7Ÿ¼†rÈÆí¯W÷)Àkw¬O!7¸Ç6®k\´‚/ü,ŒÍaZ oúŒeïé7g+¸›˜õj°‘¼9žL=An„g:íŽ|¥ ã—Ó„q'¸›?!7ž'ÿ~9½h /&õ i8@oª¢Špî26mf üŒß^û­›¯…Q€½¸CÇyñ9üÅÙÉáw$p‹1¦|±…^û”\i¡ÑõW $ 5þ¨ù°?HW—btýÚŠ×Íõw=$$¯ìšhzìxé¥ììäRI ‡6¼~úÆíSêF` ÐæÜéÐüS§ÃÕòp*(ÐTæ{«,L#'—Ywã6\¶Æ˜$Pz™H:wòvÕŸÿ7~ÀÎ!äFø_×%`…ê¾!Èkžd„rA… RØF Øìœ½ñ¹aõøÅ_Õj3`=ÀwÆÝ< Í‘zlÆ÷ÀJ¥:F*x^æ}ËßNÆÞ>¸$Å»„‰Ò[7L`ðÁ™"©'hó{•±¹¡áû­¯Z¬ÕößÙg¡ °+'¨÷gya^ÏDñä² ÞŽSîí‘„|&ÿçÈ !¦|[§÷"°dîé…-#Ð!’y÷&rãäÆïÆ#B’ tdtß3 cw½ë]èÑšs©÷;_# ^gHðo1ׅǪDàs€ÑvÐgßt©¹‡ÕA~vÎ9 þ8ԧ˧Á/†.Ü´#Ã}¹Q=|0Ý¥ #7>óºã>¶iÓ£TXj©H—ŒÜ.ð¬½¾É f¦Uy±f"XgÎfÿŠÜS~ç„“á³d7¢'šÛJ²…;Mh.ó°^!ÃKé'ueàK.œä†jbxÏë VP^~NÄÃÒÞ˜9@¿±ƒ¥¼X›N°·‚*{{óŽÕ ž2ö•æ#r£M|©C¥Rþ¬ËJXñá³<¬ 5-•©7‹|&[øú‰MÀôBÛñ_MXì´0rƒÒð]¬AÕ”>NàWá ¬­¹GnÈm=ie,W "± —) æ¾ÌQ†3äúfLœèV‚–MÛ^.æòð—HÃ>.¾aÌäÛ.s%øöÃÌ…~䯦ÀÅ/äFƇ‡a§Þð€—µýôÍPö:¥a ¹aß;¸´Ú­½óIµ£ÝD ›^n!7ê.¨½6|G€Šý‡"ªQžŸÍŽírñd‡§~ì¥E @Þ>Oý¢üðYønM=äÆFø=`=‡÷[UëÍ„›ã ÏÞ8"7–E¸åƒAŒ¤£!™Ú©N»Ø,!7 »÷ÅŒ‘`ÄkgŒL7K‹dNã•2äw©¦®§²]º¸œÆE[¬ÉBƒ×-ÉPô%#¾“†®ú»5!7\¿1ºÌOr3§Ì–ù‘K×xj‘Ç©‹r¥x@uØZ‰"(ÛǪü ¹±ú²äqH-˽¦¬C‚“+BuØÅοûÛ. ¼qjµÙ(@¿ÝšŸÅaŠ Î[ÁL3ÕvæX¦Eõ ÝÁæ¸Z8'­¬`¬ïÔ™ÝUÐi1uóûÿÿ‡•^¾TùAVJÊl¿„±Æœþ¯‘Wª¬ /XCÝ õ3N§  e'w0}SWtð½Áa?ÄÞ˜L´ByÒŸ;·ƒk·úäàQ#§¶Ä]åƒz-Aû›Ý1Zx(v#ÃCÆÜŽ 2²âžûõÕ u­øâªoôïýBÀq·ˆÆóýšðëâ‰ÀTŸìV×;=µ=ÚÐFùÛM.}#Ð8|Ê*ü îÐäž)L_Ü §jˆë5r±qJ7Žï|¥<¦H‚ûu\ö¹‚O±å¯³þÿ»ñGëkw rã°µB?ö^Ž8Þµ~‚ÜЯ“P¿Z¨âX³¯¾¾­-~Èy 7ÂÓ×D«Ì€vøÝ‰@PóˆÐwß„:0Ë¥ÑQAeAÒ×w=x1'Õ=u ûÄù-Ë»ƒâyG† м_n™ÿ FÇdÚ+(÷Ln½uÖ{lfäFíqÕW'~I«×];$€yMÞèr#Ê>à¾l³¤]|?EntbÜx;rãúðkÇ¢NÜ–5¹E™—^MÿŸˆÜÀ«5Î9!7>¿:æâkL…ƒ|¹ò¯cÛz>í|0…WÎÓKüûKí-›ŒDnÄl:¹^øß×&òt€1\ˆÇ¹q’x]|»R.0hû4óéBÉiç¯"7ŽÔ&¶˜‡ ÃùRÿ¢Lnps,2¹W§ ?P{Þ¾ù /øp§èßç*ø¤oZ¹ï …ªú×.›ZYÅ›M-QïN¶…{53Ø^yh¬?°ßãÿ¡üƒ$h ý2Eš*Æàu*Ót*7n¯ü == ª ¿Ç”§¤eÔ²{)àTkß¶Ø™ÿóƒâõˆa%ˆ_q†oläa[î“Å6®aì¼bE·‚±Äö¹Þ¦‚…·×~%!7ö;FoóÆó€sÿ8ßs6PÀ…{Íé[É¡t‘ýê0wßð•¯Dð,gxCJS\4¼|(¡†} VS%ÁçÅÿ!7bèŠ)2¸í ,büðþä=þÈ?;ÝQ¾Î ©(hFn°¯ºèE#7øç'œ¯ÞìÃæØN0|kÕQÌo9M€ýøwžÐ)tû½ÇªV‹JDLœ£Žã¬Q”[ÕA?Ý¡ä(@e·¿]6Ðbƒ,š?¦ïš! ëàíTÈùúSç%!§b:It› ¬¶ï×ß·J…©odÎõQaÙôÄë[ðýýÀ«¸2"ü44G¸@ŒÔuóN2LÏ1ñ<¾Ÿ;äpuW˜Är{ÍÉAæ€C¹ ðåÆï{È7o$<ð³‚Ù§ʳz™CåÙÙsž;ØM‰¿!ššVÐyçOù ÐÒ*ÔÒ6ls2«m_µ!é…Þ!¾h!øR6/ ¹±.Jr94k Ÿ(ž˜~i©§Yb±ËØ’œØ‘…]†pÓR>rà8D‹<ݘœu|ÚÖ8u@Û;-«?– <»Õß„êÕuQýXlÀ¶ˆþؽüà*§íuÊóV|W’Àw=Šè"­v/ׄ'Ë£œJÎ`G?\á³ZÖ‚œÚK6¶F ÑbÕ¸ÞcOt¥[ýîMÙóÝŠ±&âÀ’¬ü$ãV7Î&sÛDIA$j´î&–`N,Í/þ¾¨ë3“Á 䯹Þh3ã X¼×5ÜaÖ¡Ñ~§f?d™¥”kéñÖ¦b*2C_™ÞÕ3bÊÉ@!¿íf¾!?lXÝõ×¼ÚÐú=X†é„†v±,I`-XA¿3rÚûº0Æãžg"rF¸*hè FÚmÆSç5ÁØXF%á7/,“ûbØmzƒËÕJ€²U¯wt”·¿ŠT÷´ÔQå`^Ý1] ìÞ'åàlþÃTÜÿé÷‹MùÀ ©CÚ%7çýCª-ÆLᛩ,‘"g*œ“¬‹ÞµŽa= EÓE¦P¥±êï.æ?£Ó°Ïÿ÷Oén»År‚,5ÑžOÀóç0rãÔ…èÅ="Љ1BDú3,|>D!ÁºWºÛE„áõÇ1×']¸ÁgÑ«vË6á"[¯ßò #6&G5CoD"7tÛ8NMËÃIƒû„ÉQS°Lüb!¿…ož— ·:±§OæJ) #çøïêpÂãËuycÐëv‘{GuÎ œy7 XžÉÈz磜w=/ @9áXб7aÈÄŸ%&eHy=Ó³yJ’NL8°g«úÂu• ½Âé„5îPA…õ“l¨.¹W·Í b[\,dË)àîÛærOäFex,XP‡,1£ã;µDp/rÿRrãL Û£Q<Õ…t‘›òoYa‡9*Ø?Ó Ü/® ~Xû cQËZÉnݹ0‹+ÈX›#7®~Z³¨¦‚‰ALíX©kJ•>a8‹Ühv¯¡Ù­Cv¹Ä®ZÁÏ)‹Ú.»qÜ©;7UÑDÔ‡HÚ{;P`ëËöWšl´ ¤|UC½ùQ×Ó÷|p#šP‘g(‘Y»¸Ÿ ²€þ Nuž˜HGn¬3¹63ÄòÓž?ÿ’Y?EcÂò°É3°óÖïLîßYÜíÄ×¹8 UšrÒxØW‹[\7¤…MÙO{lÀÔÙ«C^üàím‡åy­x}ªò…$¢xž¿ÂýáŒ&ôcœõ}m3ƒMŠIÄÎiÁÏYÖ|û¦åG¯àO«\SOQÛFÏ qp.Ü.‹Þ÷‹¤dxÊH‰CG¾Fô–b’ þBÿ»1ºŒ;rÃS×ï&L—€¯¥Ÿl#7ªæ¯Ql>*9zS†¸ÐðØí†ÜpåâÕY/6¤›æKÙ Zw™”øá•ÕåEcpÚ˜w“®g»Å[961Så‰ëï0ØXhWÒN€…ÚeÈr“¶u+A@¿ûsjòUM`§ÎúÛo’Ž[6ê·ž±çq /_YŠÜHžv¾ÈöG²<½oЈܘ-ZîŽAn¼*н=ôiç!Û5Éþæ„¡[‡ ƒ‘<5RÊGMáýçïøfS¡6I1Np{ ûdÉáGÎ7…ý%ËeìšLËûXäÆñçŸ7‰Èßk½F²Õ èÛænue_šÙ45,elËݶDuá®·/»W;2è…á]½ ‚;7 ˜IoÂç_o—ó¶Ïf/Ö² S‰U"¹AsÓ;Ÿº$%ÈN_¾Nš‚7/¼ ’[xNõ½®kà×Íü·J@N,ÖéÂ÷ÿ÷y\øúHÖÈyœ€Ùbäº=rcý›ìÓ–Ã à¸fÅt#´ý›JGn< Ö]åQ†JOYúÉÃÔJÎPúaìHÖ̯9%˜MP^zÜðO؃y#7<7‹o–ôpÎáçšÅÀå^´ŠÜ×µ"ÊþS‡;+Aû!(œ½Ø×ÜHl]»6E€Â‹’Œ1¬yZk2ܰY,‘rÚT†wÖÔn_‘ä‡][‘A^È U“cóYüÙã†fIä†ÿ»KvÁÈzžb,ç3vAtm»ë>h=LxŽÜðãôâü¹OJ'§ùÛA<Ÿ€6eÿ8>¥×/o¨°£›¬ÎX_Q'\‡±Á§ž•x+‘!v7äÍó×|ðȉé×GñÖU¡ÒŒÜ þ±k§ï?*lS*ÿÑˆÜØY>W¼ÊÀ è™f›íŸÁew›O: nœ×ûÉp•çwÑ€# 6‹w?(˜À²T˜“áþ9Âx@Ô=ýæèäÆô¸”ÖCo+˜pç_EY9<]óæÈ¡L4åÕ€¢L$4¯o&S€#ɹ‘µk©qPºÙ qSÖ…þßÙ¯ùr#Ó£áý´5T¤¥‡1Ið쪸5WÄ2Ö6÷÷å÷Må8Ô+ÖÏ¢yÓQûõðü,ÞgV7/°WÈßNgDg¢ü û‡õiáû„ž§?il@mæý¯þü`hm¿¶|v+^xÀ-õ—’8ò˜d{V*&ß-3ŸÁV‚l(£“ZІû `ó!#PøÐòàªó Nu‘8~ð8dà9Ë›ÖâàDE ïxh7nÛ¦c&! âÅäŒãÊ0ʲQÈÿû§Œ›.–#7¾ìÿúŽœ$}=ÖÃ8þ’àýûŒ+§jA]_Úñ…Ï`Ù¿ùµrcÉ¡Ò7à…°”7³o)0ˆwlÓ1~XÄÇ-31k Z¿½KуöS©­«‹Ø°e^f_¦ æÚ¶à•†[ÄN䯅–7' ‚Æ ï­aš`k·ØÜó ^XÜ#EûÅ0¥|û^è[tÖ}GnhõIè;lŠÀ6½‡ÈU‡5ÿRäFË)}ƃCø±#¼¿~ssÁŽ‘Û ‘üêݹjÈÂ~–+äãT¸ÑÇAÊ]]Ã,?~½˜ƒzJmŠÅ¾[KXÁ;*ÍÝÿ÷‡­zNvDnè7ì:ûAì~OeQFn4dvSù-Ýõ/‰ë³ævýÍÈ ×µE×i!øfFNæòIn€×Ñýj—é]žb%¼à0k½N] ÙøG?!7^\döSX“‡F½¯®ÕΙ‚Wê_Õö oá¿Üæ ½lfÿ&tŒ™)‘S¢ÎÈ Ñl¯^&cP/dsû³0cF'Ó“‘²;7&›­@1ÞнŽB9y2Ê‘™šL‡öI(C¶ÁÛg³ÎËÃ#óõŸi†±Ó'J0 I%˜ÄÂn(K$»]öÉȨ£yA>¯¸AÙA¶X¬~÷Þ8ž‚ÜøYÚjòlCJÕŽ>¨"IºØß´=@£í‡KØ;—'˜fD‚÷ÒF½Øág¯_êß–•¡ mDœ’?pšnclFn”«”iåT‡¿ï‚Y"¨Mðÿ”`6Ž/ž½-®¯¦æ¥*Ý(Pb©«ÖÑ Jü‹Þµ!C†g?fÚ•–òAñ}¸ä%äFTØEÛg¬Üà,ÁØ;ý/Ú%S»þ"7ªÕ„ïRáOúZ Us‰`øêrCk([:}ˆ ³Øï¤»’ d†ó›gO&0Ÿ²‹?9“!õClzì’´=Ü| ¹ñûìº3—\Îå<½7ÄNðsìmµßÁhz=ye¬ å¿¤ôûPýG"8¹‘výÚe“1m(X<~Z]ò‰ER#Øï°©)kHÕNº”i»-=•×®/cû¯Ö™­ø9‰÷ɾ ÐŽÏàú=‹{¯ÐÝÊ¥ÓÑŽ©€Ñ 4,;]3Ô¡…´JŒiëÖÿÊSs‘8ß KKiÅÃhLȲB@täæzAŽÉb£ìâøÑ˜‚1-¸îä'{ÄŒ Tø2XÁÓ,mXRmÉËlMqðý(—_ÔÕnÜXm†¶UD¨Þò)È(*Çîo´ÿ¿_­Àÿûµu‹Olçß•€¼.Ÿ]¤‘w†mÕBëA_Ä}‰o îÅý)䯹Kí' Í@s®¾²Ä+`HÇ#üÀ€ª<þ¡<´ZH¹‡ÜØ«~–ör#ðÁëˆ×o%Àk2¯r£åýÑQî}¼Ë`^B’3ؘ÷h‘`<³® ïW!XÄ-˜PíÅ tƒÆäó4áÛlÄäëE¼àÙùÝ„{ à}ÿJÆOäF¡hd¦Q€t½_ý1½l^î#<[øü\I´ìy pêñƒÌ®JÀüØÀWmäÆQãŽóRÆ@ˤCU«¶_UýÈ sÖ׃Ÿ À[îùŽj 8óâ+rCÍ—VV†?(I’‹Wä¡À‚¯ÃÆ–¸ê+~NX R*3‘œ³;'jµÇboËÄ¢¹\‘E2ËC¹%”®º¹ñMWŸ¤D‹ú¨é¥}•DÀv9oA¹1¥‰WÑn ÞÄãy”ç«ØÓĽ١õïëƒÓÊð‰ô£äã šäF¢Ï¶»ËÜ ¾Û?*£¹Ñ_\¬<ŒÜ ¶]ŠduDZ…ºÆ/õš¿ØÈ4r㪹þéJu((Ññ÷Œ3¸ñ,Íã–°&ˆO[+že8vŒß³w¾7bƒye5 Fœ ŸˆxTþœFjèܾÜ(tXQææ}¼‹ƒ¨Pÿ g£d?Ù[ÆìÈ rlîWïË&á·\éWÇ”ßÒ¿#C…Ïj_¹“`SÛäY¹šK2ôš“Z''ó˜a…CÈfëÑ¥øÓVðqÚZ­ÛMsÈôžþ©žÕ¦™rö¢˜Œ;Qú[+†®™ÿ‰Ü° ¿nT=© uD©j}…!o“Ñ‹ä†Ñ•ºx>ä†OMÅÑ<(s€}s0xË|¹ðú/€Ubû¬þ¾dhŸr}ãè,ÎGú^NØÒ¾u§hôÙ­€ú±DÕÇš´0ßnñ³õkÀªöÊøG?˜š; À”ЊÿT÷½&.ôšÓÊ]Õ„iŠrÏôf°ÛÒÂý~hÁ{ÁÎRVÇ}‡&f³‚ œkp¡‚x©å3çœÄÁÉ•ÐÇ2»q¿Ãïh%AˆNÊÜSî×SkšÀÿn°Úu©9"7úÒl£% ýÃDE rã†(ÿXâgE ,‚ݵÑš™þ*“¡>nõÛ­À 8î*=|þ½èÍJ‰Õç‡Y†’«Ç£§FÂÑT=¨ÈÕÿ-ia»I{q$î)Gʆüo×£<Œ—«’…?Ù<½ b(º÷5ÁÑå(EäFìÞö¶›ÐØ6e—K€] .RºÊ`iÆ-é+³(<;lABn ð|ÊÛFn¤y¾8¸É0ŠCÅÑm__.X¯_Šùþ¿¾Á®ø‰¹ñ¡£ê7î‹ÜØÊÕ/›]Ã~¸~ÿú7Åæ<‰Ž¥]Ää¿ìxÜGnL鲜xˆÜð¢$oUH€S¯åø.#7".”ï/Y¼üåkžRº0ÊîfˆÜèh­i¯‚ÖN^cŒç¸A"{½ «e±úø”(ŽpŽ%€ÕìèC)€wrüñƒ¼µ© ûxÝÄÒ5ü«dÛÂm¹´3Zü,@ï2u_%àò¨õ¼0rãgXâ»~ cÐ¥i¦ý@ ¢ÚñŸâ $Pl’Õ ªòúBŒ?RÁsýþr£Í ÊZ‰¢ ­B¢o݇¢réA†°Kg¢ßó*A•7ÖQ\qTðj÷Æ¥9äFïeøÃ¹±‘}ßú¼ŸQü‚Ü0;gù¯ˆ ÆCψWAùG8oÜx§¶7Àp— œ')Q-H£\äÜ䆇뇢ŽQe(Õ¶’Gš&ª´7Ò{sÂRQœ|¼zÏ3<*XˆÜàN´H㬡íæ—Î¾åØ½ùÌ«þÏôÁÝ%ó²}B&ÀþbîÏGâêðm¢è»17"¨ûñ35ÇEe6X4ÔÁÍ[¹ žxÁ³ö2• r‹æœá"C“vw_åóÁ¬ÓÊ;ϯl´Ö˸Ÿ¤é?ËThFÙüa‹Üàê‘"‡ð‚HÛQ®±T"ÈÛS‹ÜH™Òeëœ CÓ “$è5ûKêöq “^Ý– µ kÔ;ea™Râ KäÆb©iß)+xún¼Š6‡AŸ •s©;ý—íÚ÷VðÁ™‹oÚÂ) Vâ*Ëäu»‰ÌiC­rïrž0L39J„Ühè¿bnôÛæäÖ¾ØÿÂV‡Eþxi{:ðÀPr À—\•0 àçU“¾40‹G›1m–¬h’ë3}>+pÊÔfe“D ½hÒðõYkà©b÷B#œ¼| pþN+.Ý/r³QP\Ü´²Z¹® w1úehÎ`ϸ¦¿)÷jAÑž;Ì,§À%Î7¸²Å ¾/¥Ç>Ø‘ œíæ?ºŠƒ­?!ÐS­®Rå•E¡Œ™p=~(úë„â˜1`ßûèõ§GzP›çweÕÌ"v>&påG6[RΉKÔ êÃÛ"êÈ çŠûïÞþZw¤.>Ôýé“S§ñÂéÛízÈ£»k&ëá%€oWÕ€rãÃ~ÙI^Qø‹'De{Û¸‹ÝeP~OkMË=†*{:h—›ަ9ܸε|}ØQ³ÜH…-çeÈ“kùòЬêSØ_ý´&J+í–»ÜX‰v |ÜÙÄ^I±,#Õ,䯍€ýÙ¿´¢+sÍo@ZOçÁÈk£%Ô l!8ÛÍìçx|àüÆù’© —)êXyÌ îÚÊÈDÀ›>iä†U9µ¼‹EŠÜúº’@câîµNÜfÜÂS_\ý}öŒp¸Ô¾ÿL¨—çÿ½ýMþr³017æ^Y?WŸ¥[žBnt˜ÖÝ-®DÝ4¯—¼£±óI‚´^dLœ44R†®Ãß?Œ’‡‡_ ÎaS_Ri9”`n‰È@4rc¿™h© ;ð¼vÙ%ŒÜÍ­¸g“N5ŸoþDn8Î*q«1“áõ;3¥Da·³ûrîLW)jŸ ´~ôÍ?m? ÆÜqõa‡™}xMû”áuƒûudøa¬ÓË*-ä†ö{åAêƒ\ñÃá:rCÝã·š rÃ=*áÆÀH¦rá³df®>`ˆ;–¦„ܾe4'§åvk{#‚Â?Ÿ¤iãÛoöo«©ƒÆnæb/ œ›ÿ=ÂdÊS5}éóá£Ôgžorø`ܰ„i-rã+ãß&51n`q¯ÑM¹ñæÈÑÂ䯿PUÁ'¼ ½f´ú´îkvËFn4…%&§ÉÐ>ÿäFû,¼{ûÃðƒ L×P­kÖ’ 7(üÆ]@VÕP®ë!7l¸5Ó¢NZAnïÞëÛwÍáF®n3·Á¶çO„#·4º¤\uåËåðƒ„vä†Á'¶cåËÚÐù[”øâa8¬íü·*.”ùNZÃezå øtð¢_îùeìL§ox€<ñã š·Pß9+®£òm·>²[wIü;*xŠ(j"bŒëލÒBv±³%ÖàZÁȉ°Ûü GNø+¢çªàã¶ÜJýU‘šÐ Q::Em‹|SèµÜ©C¬ž¨ÔûTÉ4.Cê nÅ– aODS‡GÅðx|Þ1¿n<éÇœ’ g“±óÍx%æ{%6*¹aeÚtN¹!yro^•€'yXßFn¬p8•¿ûªéæÖ^¬hýð_[ÉÈqç›j†OÌ@·¦Dg«èV›z¢Ë‰›ž¥‡ŒÁ¹P)²Häz¦159¹ˆYª ™j§bð˜|i}N€\#E3ÈêÔ¿#÷µèv“,Mpž3þáÓŸ¼P€1³n«Â9Ñô\pÌužCn䎮Z‰ˆBÞÃ\çš~u÷X!7žr˜+[‰ýÄ»YrG¦k¹àBÖÌäFAFÖ—Ù!SØ6h6mz‰ /(}Ï©]Ã<@nñ¼’æ¤ïŒ:–KTøt‹%çrc:£·Lö3/àÂM/¦Ý#Žû(¯þ_å‹ÙO³@†.>ŠŽõ#Ašw·æïM`‡­J?1%CsËŸsU„ò—\ÎÂêÎ&h‰7%oÝÑ„ÿ8ócg°õ+Æe­ZPQ`M9Üßô~òá´ÅVð·%9¦öSÁ˜ÛOžâ€-k÷½3ÝxèòŽV«$ø©T|p¯Ý;l¸¶¤0¹ÑÓ5Ïܨ¸gKò¾ ·¹\<‡Üx”þ&¿ù›"à{Ø\2@§ µ{?ŸFn0ŒL¹”>2b¬_=¾€ŒðÕó:üðm—öqšcàIãUp±t°q͓٠<¬;´@ÞÚÄó®É\|{ÄìÿZΙ›£*ž7÷×"7d~a1µ¢6Wª¤8,Þ»€ÜØòœzöŠN\J 2Ü+£ÚÛ?'Õ‘tY8í)Ãa“EÕDyøôòBΛá!Ì V×$ïV‚îiƒrçîRÎó½&¹áôT(`k‰ èN4,;ÇS€bË7&¾V#à±sÌOž ±³é¡Sψ@~¾õŽÜ`ø$Þ¾"¤å¬nè@}ûÀ—Z©ußÐoR†ƒ÷sEdùá©;“ꀆ2â£3ƒ|;?*ƒÜpô1~†Üˆ»wšË¡¯?¿/ x3M<üq€Ã¹qÔÌ™ÕQCšå\´°õ"‚ˆöÃájãøÃDë_òê ´^¬c9‹F×T,PÞ°r¦¾g"Ú&yÆŠ>hAûpö/r£V¥çª"7(cÖç©PA'<òrãš×É|‰E^Ðö³t!† Î2Ô!7xí8ïðü!CùMO“@ÌJ=Ľ36<'êbD†<ÑéS›!­j´®8rï´E%Èà †}­OO7‡†yN„qµŒG1d5y¯|‚Aý)à^FWê#næÑ·µáSÖfª½<0?ûúrÃäÍÂûqkØW R°ôÉž?¤øä”Ï2æq›êå=`C›‚u"ä:KŒkšÅã´†P'´AGrò¤˜²­[Èä•§…ùyäDæ~k Þd©&–Ì\]½ÔŠëŸžŠÉgÏvXÇkÂk7ʤg°çÂߦÏ7iÁ~ý #ðQ*£óˆÎ nñí±k¶9œH©|aqZt^~x²Wžñl©f’×zlj+ßcõùkã‘„µ^¿Õ[¨§¨Í³öùIÀo¹›È,Ç…3ý}Š Žýé£Ý¬š€Âþ ^ ¹Ñ˜uÃX"Ë ¼óõ)6œ]£>>ÚüðVãÎE¯cð—õ`f÷k=Ã|ŒIkd»öú~ëà j$qçÿ®$À k×òäÆŽñ?S¶ç‚ Rãþ†šÀ¤5qM¹±š¶O`‘88›d•'N¾Öô{Ü`{Åpò#A†,>?ŒÜø~E„)¹ñµÛãAe÷ª»rJˆšÙ°8#7<4/¼ÿôÞy~ ¸)Œ Yî ž„ß×0¦bÉÈñHS¸àÀ|ajKcÍ~€ÜÐÑc{ÇtÒÒ¥–8±æK€”ôÕÏlde tÆ5[P^|0¨LRÐ…cyUµ‘{®ù/Ák’OYÅ#¸Ùù‚Öz#þp ¶ûy"/Û¢öŸ9Oþ­f7‘óòOK*ÀJùkÖœf þB^àž•MÙÐhy™e§oå¶] 3GY­eÚRgÈGÞàqMʆ Éú@ƒÕ¾1¹±R—GÑS‡Á_7×ù[{L{–Ò8þ!K& OZLAÏ—(Ðh´KZ²A£w^Z¦»‘.žÞNàƒm7FYyš$á-ŽTæ%5np"¶‚Ök ÿjV´° Pá«K’R| Å¦KX(‚Iî È åcBkdx»l.„û2 ~¥Í˜ÀÄ×L„ÙôÉÐqׇ+I–%ï¼cEn$¶†wÝu·‚4P/áðs¨¸í.^/¿ƒ±¤W–FÓZAÌ3$êà ØúÊç'܈ͥûå¼[þÎÐŽÈx(Ç5+· 7jxy_ü²†—›còs[  á5f²Ã©e,$+IŠv Àa_{ߥQÀä½/^Ÿªgñ«ö¿ºÃ†µPþKXL¶i’WÈiR´PË,šÕ¨ÓXOt gòƒk¿xLÓϵâ—ëž|K£œÚÎá^)šp)ÚQ˜UdãM;½Q£eÇÄœYƒÀ¢v[† yWæó=hLm²§Í/úŠ鱨‡ÊîÝxÓ5 œ…Aè§mkð}À¦:(I÷‘?…º ¾#7Þ)Yê×zJ@K©Tä†rIKÏ"Hýko­&Xäxnº­D†áÏhWÒÌ×øA`Æè,êHÊhñÃu‰þÍNcð‹Ä3‚ëA…°Ù‹W±ÀsÆ"9áô+·,+'@Ñ¡™·È UŸx?‚"J\ê M°7¼æi”²D¶Ù/æ€yù×^/¡¹_¤³ù¹q@/v©—" •ÂFšùÕÉ›·‘®#ͯ¢Ã&pÖgûÒ´í¹ált»‡rCüÜû“ûûÿIÒ`ÑTxÂ$t}½} KÓ]0Ü}Ã>ŠÌ;yƒi W¸—„Üø½;ÿ†ú}ÿNv²ïûØ÷mf3×0¶lI(©¨´Q¢I{YJekÓ"I*‰r…T¤ÈRi‘,Ù²†Pù¾>ÿÅ÷‡÷Îq毹ïÇõ|žÃ±ìŒWáF׎µv^Ö‚~낵,Â(Céá ] {•®:ml‹’2=;7–/k;JUÅôS»Î•ƒUô4îÎÉ—UBö÷ÕŸP€êÜ;oË"ÌàS@íÛ„ÿÔVüi76Â_2ÌÓ”]`Ûq)ÇÍ£ª^ü¨Ê}ºÌ 69‡¾’l0ÐiÏŒD é%Œ»”áMƒ­Ý}€ÜŸj<Ç7ÞG«|5î2„Ë®GtÒ 7Ô”On!ܸð%Ìo`»)†ïÚ5L¿dˆ}RŽ‹Þu²“$™M¿Ñy“Ï|ø).á¤ØF¸Á|®Ót>G>®¿—MN±ªÛW{ndä4Î…èPq“§(åô%+PرÝLŽnîy.17ÇÑûÂòV“±x\ûÂ$á†WV(ù¡)ö Ï?3TÂ'ÚB²2xÍcÿ6åö¡ªø]–•„•W·¨Ôp!ãÈÍ“BÕU2AV'h§íAáIss á†E©¿E —‚C‡Õ[vYAª‚sÖ½¾*Õ`;Ò'M è$e×8í·ÁŽª?’w=$1qÉÝ¡cÿˆïÞ‘¦´E-ÊÉÊÅ~º˜<Þ^öÜy¢ê€P(½å„üO”¶sðûóÒ•Á®>ž_r<™ ´ø¿&S-0—aßµ{˜ýÚWv¸Âuí?óÀÓžÍ-÷0¡ Ûf’|ôN9ü´×DzdÊ"žÝ}Ù¯ÂÆ#ýåýÑñ\ÔÙyÂ-Œ r|2d$à 3{ìÚÓ4m!ÁlÉiÐxýM.ýê8u¤o†![9´Xpø,§l›¶Ç¢‚~kãK ôš ¨w¤ÂNõìw§þ±ñ´ê Ã+¨º à¸RÂã™DÍc£Ëΰ2x4êd/ϬÐ;¹à,+é5@ôeÝ#wØÉ–Rø×èì'å,e8ô¯õÁ…×dlT z¾¨Ô ¯/Œ¶Áý[Ž^­bÁÙL—™¥tüÝ‚ºîdGÈÉ÷âg-W…¬ô’Üg7åñ°ø©—’Äþ-p-"÷Wmë·–+? †ðEÓüÐè”–8í¤‡mÑ;þ…Y#·ótLÌû„{8ðH?ëñ^á„ÖËǚ̶#¯¼Îè¤Ãæ¢Í’ùëmñÙÑÕauîXYã'"?dƒw-/î¼ÜKs-νÏöùö¼áÐA <Özhä8qîä%’s PôÅ)w12$¹>¹n1Kç'WÖÞdB{»PQCµìŒy±sá„9¬–v)’i™g‡|’×7ç@¤¸Ý)ÉD¼Zi=?)A…¥_UjÆÏ³79¯øò–ÀuæräO·Æh9KC y†Äo3YÃc \kÿ,uBÍ×oaì îÿºt¥k¥7œjh ÍÚFÃþwwº´ktÀDR¶tïAsдü—f63Ïöðôýô”‡‰Ä6DÄëâ‹]‚Ú‰DÏ¿Q{L*M^L»5ê.ÃGî'CŽËCñ³]™çâ,Á´G»/}+‘/O‡_›Õ±œð.G¨ 'ßf¡…Ìôñ¿.À.Sdù ÷à黊ùxSýàù=ËéÍÚ¤ãÌ‚–kw‰P…‚áû®\·ÓeºBW uGèjO#€úG¾‡úê`Ô q2í€.®Ï)ر‹F‚Cßê?j„S>›:d¹ bµ×ºŠd Îû(ld€JÑLÄß@&ì^ZóÖm5Ñc à$Ë@§o€ËL5¼¸?eìRYð$_›Dꃮ&~MKŒûT+qOMî-"Ïx§Y"y\g†• ˆO¥nnÎƸôÐ>÷M(›µ/0ÄšëRCŽþ½õS,>xŒ ¥¾ß25‰þµÆ7\ªÇ Ø…Ù(ŸI°¿È÷l€ˆ˜­4ב›ÖG+¾Wï6©“AÌg2õI(‘ ô;!A^f¦÷ÎA~Úy„=´Qõ¼ä»Z½_.˜³š¨òž?·ê0áÆ”õ†\ÿ|”}û.Ÿ u’NH %Pá˹={ìÚ@öZ×ßG“Cì#f 9ÊuæX+ðZà››!¨È®`9RðبՋÖërÈgè!—M¸ñSêþ@¯2{EO¤&§p1ëŒKî{2Ày¥?3=§Ýðcgä7^[ØHÿ×<äÕiCò<À_ÛåÏ3Ãv]™«D¸“áή#Üà_ðwøs“pƒt“QTPÔ¸R’4ËFáúàM ”¼žé‰±ÀŸ7/9œ‰§ƒÐ‚¿ýäMgðI’9[ñƒ‹Bµ÷µóªÝ÷ñé9x¸£ÀiÚL 16‘ÎP†ªWµn2-d¼sÀ}lÙ3x3-Êÿ›è{2'ßýcÔ³ ¢R¦Á΃ŽÖÿD¸Ž Ôæûýz€*|Nô/ÇÓ^–ŠâZÐþrª³G ¿j)9ÒY9^ mcÕö)¾â‡WÉY»D@ëÓvxòn²ÆÏío·T±!FUzTÖŽûŸp öuÂÇýRÇK#ª–®Vm C×0÷Ø¡`[¬Z¹k©^ûÙ .´Áç§Ž|'ƒShØŸ÷öØ}ùÈpr‚®ªLZCF»-á£Ij ¸Wõ¹v· †Ž=s¦¡uŠ×¤Êb&Øž×+¬Ò­Ç g¢N›C*zÿ]:Ïž|]0z„p£ÝTd8õ¸$®`lÎÛâñvŽ}dÏÑÉ&Âkªv« 7¶;—Tå ³Zl^é~ñ[jåy:!Ít¥Yç È( ©†­òË­¥g˜Ûi—x°ˆuà3cÞa4Ùöî“ù0Ï6ò?ž*ÍÇÄwšŠ™Ú)ºXÔþ~Ý2J^ÚX¥* %ªÅqì¢è@–6 ”‡¨œ+úc,aÑWÖÄ¥Htÿ(©‚õ¡’œ,G(¾við[ ×.]y…;ë÷%Æ5UÃH }³A;3ÏÍn ÿ´”Û8ú#n,xUÒ¡wBNNk.ºd2‚Ü|¼ª¾³ÿ-‘»¶iÝL•Q‡HÉSº¹‡tqàõïï=dø™)KJ1B…g»®þ–ãB°Û®?¨g e§E-‚nµ½ü?Ô–‹î à‚\è–”ÄHøÜzbà}ô×ûäÜ;Ê‚„žŒr:1Xü¼jÊVÇ™ ŸÈÉÁý$ioS–è5õíDi¢"RÏð:_Fêï¢,± Ü1ºxšpƒét¡ ŠÈ‡Ç(³÷½2a—Íî7šê–á:ïeúœ@ñnÈͤ·$˜é¬žñ“5ƒötÝêÝúàúí¸ ¼‹üƒ”à 7(Èé~E‚•µ>ÛúhxØ;`_åz”ÉëŸ ÐE¿I5Y}¢J±4Þ{ÝÿòƸ[ÑÂ'æ¿Ü'y]›†©±¼b©Ð­ñ66©ÝÈAÑ·wûÑìú[MæxKµEœ Áfוx–ƒOz"‘CeYŽáFv+eÛ7%6NÎ}‘:s‚‹«{úÄ]-L?7ì8î†áÞé[#ålÁ kz*ñ§ ¤­Œú›Ú¾ÀÎå·i5P™f‡g}Ñ!ÜðוvŸ±GR§÷Øë{ ÜÚ¿ëè1{*TÇô7Gýb£ä""jߥ€è·¯oó[ Ó.—Þ’X:œsQ­,qÆm«òLš>÷I–î¸6á_ªù'…m4‘BÖ’©f¡ÓÊ0ó&àKêG2®Ø!¿'ï¦ü–v›vˆ´Á¦éÑ·­,(~*ºÁ•ŽF}än[SGȃ&w© ª VuÕvô²:Ê6Hü½õ™ ñ¦W>}c¡¯óšîÅZ ÒïÁdÔJxòO j+Rs´É°kýˆ ^Œ™ågÂqƒ´°rر6`]¦98±ö­y5Ïþ.7wkÉÞÆžéŽ;É“àXƒêY ì4B׸ÈYv¤ÏvyÎàj¿yûpã¬ìý—oô9(aâírb’ ôgWc¿9;aT­3ëA ®Œ8ômñó†nZˆÕÞ]4ßð0´–˜¦šõäÓsÐXu+³÷ü<ûõt–N©m>ÑH×ÅÇIot€Œ½aÂ.¶Ò ¢žQ:%Š¿Ó-¶®”‡ì´‰ÅÏw[‚wG“] ¸tÒî©A³ ì&eZ=tw„‘ŸË—³°¬ePçθ Ôç~j=F‚LQý“¹øð…¡QŽ'’¥¦—± »à›ëºBUxÔy[£*ÓL” /T\`@ßÏþ³Åï }u,Õ€ª•]J¼¿Ùª„ŒZ3tzØo9`„UZŸdÉŠ\Øbÿ£ÎÂŒ í ôfÀ:ýNƒ½Ë˜0`'µÊ&ˆ ßÛí¦e ªÎÅ3³œ›cçbAəهÅ@pýmaK ׸~%•_Úü…_¤Zâ·´óÉŠ(Qs_”÷Ž0êõ<ì!§›õuÖ»ÀàC_w €ã;v+ôD2a£Ý%¦·2€Ê‘ŠŠƒNÀü™ä¥÷×}T–˜º^Œ±ÒÇÑ}äÈf92<ÛÖñf7çG¬KªHÐjÛë·î+ “vª]] ‚ó»7Üq ÒEöÔÝÏ·'Ç«úœ_¼’#ܸ¼e Ò½‹ƒ©²ogJÓPO¹“ ½ÔË…R`7Ï|ÿöA¶xVn]¯9ŠžuݼŠeR=º=&š¼_÷öü ‹<ª¨4ª^"Üxø¯ñl£·–ìYp‘‹E÷/Ñ7ˆâÝ‚ÉnøœÃÖt±Ý4Ûs]c.3syµ–é›UL6BÏ_ìÛ¶¥| Âõ£‹^¥n¼S¸­^ËÀMõ+¾‘l¨Ð¡QôÕwg*ùÍŒŸRÀZåuÖ½9s Yof>º‹%›?è.yæ φì;8¨ÎÙ*¨má¯'/±•ÏÊÁá™4})ü!¡Í;¬ iz'&vö‘±Z*¦˜–oùù>¢m‰>ž±el€KR_ºt\Ò|!ÚPßjco|Þ£ j´•²åq÷Ÿ{7øµ€¯¸,2)þGU1ws!-A ‡.lñ<ÿšÝXjk®‡êµ©³Öȯ¢„íÉÃ졤mýY«‰½ë2:q|™¶®¬·ßàïoλM˜È¹ª–îæ«l ×Íøq8pèr”ìÑ—œµÎßþH†ÕºŸ•š_ÙcÞ•Çc.1h'¨ËÝ¸ŽŒbšëVÌŸÇ&É÷ÑÆd8ññ¬ƒá (¼ÔM¼Lxe>¢ìøPz{×ÐÞŸ3‡ÃFæ‹•óì³£nn<×È¿<•Æ*?¿iéÌf‹™/«~³åc57âvW{fXãkú6J¡)ÃZ‚;2ÁÏÁnO‚£n¤‡ú8‘`E»2ß(á†ÑŽ›¿d¢hx&?åâbîj÷=W–7;É_:÷^‘jÕxE™H™xyéÂy]]s[2W·}W1#zŠ”ÄÓ-ã¢Èý«e(î#[ê£ wZ‚qðˆa}ØW:— ‚ôøËS^Ë!6S[w·' y‚Úw"ÏF\²Ì¸•KvÁåÂ!¾8ïN‡­gbÚ\V°@ìóÑžUáôeÓ½/OÙ€âeRÝÔ9LµÖ»´çöT®¿x»èç6º>¾ ‹Î*ñ}!½0hŸvlÏmöPâÂÝ5_E_R­áPÒâèhx%?ã[âÉ„“›ßtusÁW,误• m|¼z×C ä)…˦±@8{Ãñ–Mb/ÒxªÓÜÃ~Äü‘…{K÷<Î:d‰·²ŠDRÑëÂm÷/¥Âøýà{}G 4YÐÒD¸qö˜ð8• ð1í½cIÖt¦Û„)P)G* ;Áú° ®q7I`Oìbe»r)œú¡×;¯jNJ“AdßAWý}TìÜ£Xsî1 øšâù×tÐðû½¬tƒU*¨*þÁ´?Xe×{}ù:^¥Â2õ¶wÂÒîOÆ»98—²-åŒÑk¼r‚z·RaðÓDûÊ0 Þ©L`÷ðÞº¾|ÌO[ïIcBøS!-5 žª¤ p÷Ë£þÒÎWî„1šb¶(ÏF¯¼ÎéâË\ìäÔ+f‚ë7—$¸!;6‰[Ìg V†ßOO¸€AºÐÐûlÏé7nœd?¼ytÐpÃ…¡ïÙL¸qË~ð¼u#÷ôè P!æ^{§—™I<ýµ ¿Ø³çù”9þåkÛN‡üà(oÉWÎ V¿4ì=mÖ7{€ö ú.í9ø;§šü@G »ß:¯þž¤ ³^»ú†É¨G) 9pÑ þd›ª…Úàdð#ëø_,°þÑéüÞŽŽ ÕJ]Çj«#/@¢*¤Ùò¶ÞÊ’Gö–],÷EZðízÚãËTÉ_u’I'Ü`4ú›`#?̈_q3ÔCËË7;–lµÆðÜPŸøÃìÀÖºÓ:;ÍÁ׉|a©>¢w?ÝqC~8_†qËÂoIøÚ¢ôt~›ÛÕ¤,äè¸<«óÌív2øÜ5¯‘{nG~ÞüÜm;®\Ž %£¼]GäÄ <>.‹y¾”L†s‹Â§ºÒp‘µ0¹õ/bâB]4Ju`åNKS æî¼Eh3Ï®4(÷šäÀ15ãÐHâÞ¼È?’uyˆíJcµ#’¿Ù¥†A‘Ö„þ››/^ ܘ.8Ó}˜ÌÁÔŒ}™ uÈì‚8áñ5á—ÏûxÎ$sßvo0H.*Œ¦¡ü´©Z"1×ÅdoÁó“æ°$>âᇀyö¶Ö=¯ÅLlÿ¬¥œ¯‹­ýÚâ½42½S;G–†ã‹NëüÅŽ/¨…ÞòPsýÏΣ‘–`¥vc },Æ×Ô¯ÏçMv?üÁtÆð%Wš$ÔñÈ ºÀüÄIǤ|W&p(Ç=µè~Kè ½êpûbÌ=ŒÍEäÇNߎ·Gl iGijL]©ÿ­ à‡jTNÿ35ð­ßöì’.Ê067_2$Av©ß÷¥ÑF(>àu/L™ ¶­Fõ kX8[±á›<}wÎ*»1¡Èø[üu\ØØ°M겚 ¸–¿,ñ.¥À/ßÔ 8mxXòÚM ¬ÎÞä­"[¢Ø«ra¿dA³uù¢Í-QóûQã˜ÃŠh±ÜG­¢\¿£ágJ`øÂ³«î¡.úsI²& Àb¾O\ ¸ooŒÅÊ4]³Rq‚•«“ße“ C›X¶fPújóƒÁn}ä_º–%+I†V¥¶{ûã©h4å;û€+ë“ßú´ÓPö¤}b¹ ö΄==¾Nÿ]ɱ¨¯:\‹¹Õ„Ýó³ú8XòÏl …—†š2ÛXþ›©àP}áOö'Ý:¸säûCâ÷¯ÿÛëg|.çY¢æÛ‰rÊtìô]¸qOÅ>®835éŽkí„îʱñÎÉë{Êor‘4⦅G €Ìü87¼ú êµü?&Ø&PMá— LuÈÜIüǶ¯ÐïÖMœ`lØìA¸Á—¼\Xü·=޹9àkgài³h£K*Ü+i™0ùÆF¹èy“‚ ”<ºÚFxÖøät€þ:ðk¶µ69Miàï‰VVÙ Œô= coÒ¬ÉU9ØoÉ{Q[K lzäð.^~Öc¯ž$ãF²BBíY3Ô 9‡Û žáÕ8‰ܱnš¥Óq 9R$GùóeîêQUˆZvûšm¦<žÒÛ蟹@‚ÃBôÓ_ô~T¹·,ý8J¸Ñµå‘Tï~¸K+?»B[¿†ÉÍÞfÎëÛZ´E²Í×6n(!òºÍ’çÞNf^žÌ (=òìL«Ö¯«×ü¹Ô¯E-á·ç€îÍaû jtôÙÐÐw«… bŒ7í+Ð5&ö¥Gì¶À§*¾£>adT¨´ï?ñUKDE}L˜dò ²Î¢aŠ©M¿ý<™)ÖÅÐö³l¢/¦”=Ršg?Ÿ¨\J¸ÑZ!f8x‘;t¹[O÷P@a—îÈhÐ û“W¾š×ÿòFCêê^ ³·îBéìé¢JßëcÂÔž¾îÇvN¸¥­Ey< Ú3=Æ 7lÇsö}yD»oÍ]ð°u¨cž1‡ ýA:MvólÇm›ì.H0q]Û‘óºØ¯'QcIÆ+GŸuÐ¥Ó|FäC¿(—žÚèâ! *ʼC[,áüyßêîxÔýNH„ÄüZB#,VÛ(ÓèÈÂ5Û>49ö¹¹dmÿZ¢·‹ dÄB¶ò×?]sÏ•ÃR~3MkX pºKâÅU¸'3ÁmK²coìB‰¼EÎfÞÜûÀþ¡üø³"5\Ñûò†.jϾó•Ð'Á󭻨D‰S®¥>ˆWáÂíÄúoYÖ`'mèrÙ†§&;û¸Lx·AÔJ;” f?ƒÉ#B2ð}Ùð2ã»Ä¹õîZôï, …wm­5ƒé's­-1Ú{e„÷°,„ÕdvÚÄ[bCÌÑÂ©Š˜ã”w¡JUCí®²”ÀÏ™ô?_7šÖˆÒöI;;ù2á§[ĆӲf«[?¨Ž9AΩµÓú§IðØ‚`íd;·Ú¬â~ÓÇ¢+ßë­“a™õ~¥¶d*ŠeK> ¼K«»1ånïhØN|ꥂ–c«D-Cuñǥ׉ƫoì9G¸ñsï~û^^qípÇo*6¦/4¾ ¥B i™ô÷Ïðˆ*s:ÚÏîZtab€fœ,xVC6„¯³×ß +PðçörC?äñ¹íÆò\ÂðO¶ŸsdÙøä·eÊ“»\ì}ŸµU`eŸÑé½n¸èûAï¿™®öCvdʸ•mÞªfÿØšuîÇÙÿ® Üp—önp"Ü“Kõ•ñÖ¦Kc̨ µÕ#Añ7ùÉ©½£€Ð¹Y³Cæx2ìË/Î&:Ð¯Ê­Êøà ¼‰»6sðÍ@ÆãÛZp1ö£¸e¡ðÞÖÞxZC —6‚[M¬2ø=\)þj–Œ—=‡ÄÒÌà¼ö£°ëklpÔëØÙL6$týÚ¬N£#y©—¿º#<íû­°(C*Y ›ÏÈãH†!åÛD®k,Óœí«ªúþý¾{¢ >”j"ÜèÜ&ª¦‡3}â:?#­ñFõÆëG=~²ƒx*-.™C…D£ÔË ÓÚjŸ¬ø•¼2“Ç€V£¡ö¯=mÑðš´Õq&@¾Q*S—ŽÍËŠ%o5‘áç aFV…=îÏ ÿ³Ã§lDÜ6‘q®Õç—Õ;ytrÏ’äq Cbf·ß ?,œt½1Í€#"2ïnëÀñ5·"¨yæP6Qþêåì;wInTϾá¬Ëg@Ò±GÑ¿PÀéý Ñ5w§Ù#»¸‹7nLœ¾^m–i½Ÿ–9Ú³8¨2RÞ¾ñ;±$îªù1œðÆ»Âmsš0Ù!±cµ7´Õ=.i‹¡á€ËéŒ"0¹o¸P”nwç+3:ÕçÙn;’º¥˜èÇŽJV(ÒÅW[ö_5!ã·õÃYÒP¯åQr¢Gñ^[j«<ïtwõ·³åÈ?Ë€†7µËîn„àÃ49¹0GðÝÙ}|–ÅÂôÚú¢Ýß]€÷k±ù{t;ï;C¸q'x²BÌ…¸h/ݵϳ a+ײï‡*ì5¼œ÷e¿ ÜŸˆj¾pŠ‚ [ø¥>ôÉ&}¼y^ dІê‹u1ƒ.2V‡r'Šy,·¡Êô‘_gU¹uíX“£5Pÿ̪°`ÀHtí²À.É©ÝÀÙž¥K&¥¡ÜÓ$PD€ÔÍ´çY@ÙckÑ*&É[ÄöìcZâfføÛYÈQkгă~ߣŠ·5ábôsa¼p·>)Þ@­sÜlÛàÍ7¹þb,¯î¸)ë΄¨ü8£kÒ2/ ©M8¥ÆE‰CIDÿ˜´µÐ÷6!Φ¹ÜÏúȤœÐ^&B†¸šwJ†G©x]/Õ‡SHÏ#‚²œ74>ÝóæÐ°“oÝ ‹sŒd|™?^õrÙžþ»SVyÝCìJ;7FEU·ä(¦Âßµu× ¾:@ÙŠiƒòmýì½[Ö½’´@Ûo=æ†vâcN†‚Á×ý=ÔPà È{gÂ#k7¥Ê°ñ\«ÓËÚ2.*^´¶ÿ@Øð6ž·Û Wnµèš`ÂÓ­z…Ù3.Ðï«÷á/Û¬ÀB°çþ{½+u$pcPEòs,áÆäÎŽé?(圲ŽeLéUVÿڈקªú‘'à &ûÌ‘u¸¸Õ?”Ü\“u«¾9Ãí¿‹­>¿á ÓƒÏTÝB´¹'¯3lžÌ«J!õ3{ëÝ=Êà‘XÏä¡ ˜˜¶x0ƒÇŠ·¦ülðxPà-96pMZ—Ù[ÑQ'ÙGJÙêö­YŸ£ µÏ°;à¤<þ™ð2œ#AºÈA1§×}U'©4…ë„]½ÕG šùAz}íí z¸¶~3‰µÃÂo]>õ“ÝzpìBù#"oïå<õtBgò[Û€‚u¬¬@K—5¼±Äç®:#MçÀ§þü—¦t|7×íWÐH†sÒíí‘kWr$ÒM{U­Øádì=¹ÄïísyœJÒ»üÁ µët,¤Ð0z÷¢~‰IìuXvó¦DÏ=zpƒp+éìÝgõsìª 檿8ð¹ñåËUp Ñöv dzšÜ]Í3ÍnÜ|fu,á†Ø†p³Ý„ì ÊþJ\>ØŸ¨ü• Ë”&hN¸4tt_v½&œØ;4F¸±EkÝ¥±4ÔµàReoé½Ïi¥öYsP núñíßûùFÙSY&¼/ Uª‹,fįD}2Fzš¿ÍàJƒ›Q˜=û›(ÞîSÝrÀIm?‰n´ é.Úü~ˆy·¶z °ùÂÄ­-Žð£Þh³“…qoo÷^úê;Ÿ³¾ ’`at¡vâ¢JDü°]çDÍù¯ÓËBYäë—$5£ ƒ”§ºöØ@)MÈýÍq"ïmÑ4¿Ñ0\ÀJ;|H œÒ×è6=ÔÅ6yò²> ÄÌJìÔÂÐÿHÂË"5.Üžuosµ†!ÆhÎ[Â}áÀ.Œ·c‚Ù‹µ/7n䂾G¢JÎWih¤z~½Iô¿í ‡z/²À;[Û†E!ÕY\Êe‰ƒÇÂÜ´>ËÂ{Ë¥®m{-ñìdìLÕ1E<Èž/YÙ ŒGßUz34$0Z5ðà? Õô€ÂN{€cûgŒ>s˜ðÛ4¸³L@/öEý—<ì°èúM‚¿å“ÇTüÍÀ0ÿiúƒ>Zž*ß*H†ž÷Ÿ¢¢KÓâݤ4^ÕM­§áâ?"îÎ*XüÁœ²w£.ÚN –<>^•Ù}²‚E¸Á_›t„ƒÓ ߯Ôú©ø$LMæn®•KR·v:À_¿{ùÁn«þ«¥b!áS‰‹L Aä^ôô€$/êŸwx´\ENrÅG&<`þv˜òvi6þx¼úõS.þ1¿ÃÑ zâšá°ínˆ½½?™Ðù Üg] <÷REÒ_vbaŠ{Vö([E[X<ŒpãÃϤbÂлÕÚkÇøêÆæ{¼T¨Z®ú½‰ìéœMï;)àÝ%¦ñ¯ËEŸ•¤GÓa™ÏÝç*ýÎðèuY0Ïk†õ$¾¿¡äEùi#¬r98÷œZ¸AY _,´ù]Ü¥ óËç›n RðSv·Þ©T3¨_o±ßx… žòÛøµHƒ æÖêftüTS³·GÁ.E©»òIyì†w+8ÈÃy…o¡Â,!'Úñ€P¶(÷Zþ¦ BîÕ×j»A¡jæ’3…›Ó¶ 7v¸À-¡˜K=D|t‘ìA¸±øgWêCG:„>ÿÜ/½‰;»4³Õà¥ÍÂáî6ðCâYãÂLn€ÏDÞO ^¿C ì¼»í‰.ž ¸í­A‹Ðƒ f„ûÍŸÔ¨sáÒAeÿOžÖðN5'³K“¡z]èLx~ᯀØf.ìö99:òJÖÙeÉ<( åÕú ÿ\¼*ÒWmo…:±¤34KŒ0ýcÊ×* J6©—¢,QyÉ•aãŠÿ|×*z³0ò~»6&' [¿êŸs¸=~å DßW=y™˜7U²ô•8€úûÝÇ4¦ ‚¾D '^ö„¿äz3H:°ÃÕ½M•8qORùÈp¤½é]uOdÿæ“@Ù™þN¿Žèµ;c[dTpdvÓ•ͺ8|1ðbøžñ*ê9¾R„_s"-Æ9HyÞ µTœëݺL{ÎTðE–|s€-Óm=·ý`ç¾ßmðKן¼æ+Õ60„›l³O‹)èY`S5}XƒÊ27DwI¯”bc‘z¬hãs.ΟK*T`‡¶.Û↫=¯ÿ`Ââ¹@µßs.p)Õ€qÆü/»6Dãò·¦Ÿì-oÓîmý_O9?³±“pãÄ[[^Ò,³ÜN©Ó¡B›ôÑØ— l¼U§~¢¿«jòE¿˜#©ãíð:d³Öœju†úŒßcz¯887uúƒœ¤”_c£Äœž{ߤ …ÿä.Ù¦F*Ã>Õ¦ÃJ⌴‰«nN2ƒEw[–Ù`äŸôA #6<üyâÅ>#:5a8µÉ:‚{Ó^õ»ªp Œû,ú°<Þž|¿¼ì ¢³¿¬ËÝIä‡Ï.n|ÉŠÜÒÂ9ÓËDDõ°D9v××]Ö~ì¡T‰ì{ÿ ½ßU?ÌášÕ¡ƒïÜP©[he-À­ïºÇœ`WÆÞȵŽ‹nûªP8àj¸ÃОŽmUvŠ9/ÈÀìò*²ÇµK}É ·Àô¨8{ãmd<ãxTY¦HÏš—g$ù“!àÈÈ‹øï4T^ø0Ä€65]Ý•Wt@Í/SùT9 'ùfϱ{ 3Èo7jNJn.b@Ôê9Ï—Äó½þù|vþ/ö–W—;¯nPbˆ K¸qÍñ¹ç+o*,Bæ¾6&øÞ»³ cá„IÙ³ŒpMèú{|˜pcwÞ±§Ih¸²íHV-1·ó±R›ðEsˆ ëé/à[}c·ÒcE&„MP. .ze•_µW#ã!¥ÒðóõÂü÷¢ØN[§u—%ž/tx7®³„ð\ú L7>ûFU„ôЂwïbáÁ|žÕ6KJÍvð͵»€x ”HjÁý knu_pÀ€×WV‹:ÐaöÆ5íÆ´õ…•WƒZ=™¢ø$SÇ$f@âf~ß÷_LOËv/ TƒOûk}[ku11YÐå±* Z%îØM„á;·¨²÷\Ð;wíÄ—åÖ0¸wwÞ¬"ÊÚh²2T¢¿ÕܼÎ…swV<…Ù•kO\¡@¼Èª™†|0Nù>ùuGîù5 J:[âÏïN  ²pÂǶ–(·±rGúIE4ö8yHþ½0’μ.“@G=Ò*]@BÔl÷G€›Ëݶ[9~Ï»=ŸÄþ^_Ë«ñÛ 2b§¸gIp†×mÓÜ3¨ÜSG½Ü¬Åú×xÈpâBº”ö*ÞëY×òù"Ñg~¼R­¦á·® ò{{ܹÜ0ÿI¸.~û2‘‚ëÆ«* O·ËÎ ùºu¼Û&9x÷³ mÓ{*š«-=wf9^VEöMu9Àþ|Í@‡‘>öÅ·ë£,,0åùª¿lCx[Ò¢\/BÁ±~Å·' 8`-¸™I¸QžÕ?M—dc§}ßÛ×\LÜ4œ(§ PwåÝæ&7,ë_/DôáÐÖ¯k\@ãÎÁ]ÿ°÷¾`ï çfWë¥öï&ÜèYo©Rsx!9™òL9)Œ³iÚ¡ ‡»2>’¥ ¹ò¦wRñf ­ÜéÛÓ¯Œ:wŸÂ†¤iGÏéѱÑjÑ‹)G8ÕÂÿio¹*\OéŽ\”,ÔÙž ¿ã$Xûxã•S_Õ©llºO¸‘ªé-ÛÊŽþ!ûóëáfS ó(kÜ)7)?°b„Ö²ä ,˜CÛ•ÛG>-qBËs`T´ ‹çXÝob_É2¬9¶ø¾ò/-Ç’U•µÁ+èøa×ù‘³µdàý!ý5õ†=–H>;f¸É &êR5·“q¤uñÈý|y¼ßðêÔª2±oBshßh(÷W¸Ò¶Ÿ9f ÆùÄÞý]Ê{ÓÞß¿zŽ÷Àûׇ¥?ÅÆlÊ÷{ h½Sij÷Œ1·J?UŒM²åêDTnh¨M;3ÏZ£ñõ¨ üôíÜhØÌ„˜ &Nxേ‘ SîvÈ&m^ã œ‘–DáV'«ä"1×Ë#llŽ9ìY}¤\þò{»²ÐÕÊLôøÉ£©öBM•u›UÉhéæ®¿RÖp”k<[DQE0Žãb+›l®ÕYB°÷‹“jÉ 0Vc‘" ²íOM7$8BŠ¢#)Í„…v}ÂÚº-.аù%-Y[ x(wm7ž—œ¼t¨(Ìb¥F²`€µæŒ®ÜSIº4¼ÁÂfâ5Ö¯k°øÉ•mÇM"×0ÝÔÀLdz²µAùfŽOh+“àÛ ŽÉ§@#¼*ž-6¤É…+»›ä¿¯²†€»ªÅ¥ˆç÷OMå#q[0ä¾×.xØ O?È'~ÁCžëó)°Å¯8‹u…Bçnˆ%òŠ–Å€›%ÆŒ5ÔÈÂׯ›eÌHK4¬rœ?¥ˆò +¢§> ãNo©Í‹$°K¤üáÆˆ×ÄÎ|g›¯G˜úL965$PKz!¦1çÇåÙSKIPç²}üG´¬vñY?ݨѽ ªþZÁO¹ÒW;ó¨˜ùâ‰@ùYlx+œºø) ]öåè^`¨àË\‚·èâõ‰?/”½Æ«j+µ2ïnthÞ7Œæà¯[þv‹ßRñPô«êO*¤gÏÊ1»à²ØŽï¶>ö×k•S lº±ñw€¦!ljü[Fä{'Í5'ÐzЩsÜ´ã’í”%Øz†QßÔÂÅW‰—Íã¦M,F´BÝP¡mÌ=ì3ÄEßè/ÿç{Wô;úÖú úxÂfݼ´°0–pã÷mþ|ÂH D™hd!²D [N=öŒ~Jûç&)pK¢å¤J‹9®ÎJð(ö¥Ãª†7Ï–.8;À¾5¢†ƒ[¦fª‹yÀÇ=»v9¾‘ƒw]––Âe)ß“WoT†Íq¾ÚÊ|BÏÕkŸ˜4ôæ ºÙ Çš¿i6,6´ßÿ{O‹Ž¢‡{Ø%âŽP*déý®Z~åÏNOÇ›+F‰çí§Ý1¡ÔWúä«x’^ÓÚŸÖÆ短y}âŸ.ö¼>î¶?Ú/oºï!‘5–±íÉ«R°¡^mÝnNø](ÖP? =Â[³ÓŸ¿ ?g=å[QÓœÁ«­zÒñü’I<õŒ U[4ØÏ®Øãáý )Å,ÐÆíöm¹ddϺWøeÈcÁæ¤cÆ›ÉÀüBà ɶŸÙ= Ë~œû/[mvšÃ¬Ä…êAsì8ñ“K÷nT…V™Õ=dÀËOóŒSàÑ%o¡˜d‹ðð]o$Ühìø8¿‡pcÛiŸ„èú\t&µ½f‚ǧžË ðÁºM"š´õý£^«b«ƒÎ&Ò0Òĺ|GŽúð晃6SÙÅ*iŽÍH¬å¨1qv¬äBn£.¸n:6#M䦷7+¤aù"ÿ¹7¢ø¸´žùÅF–ì®Þ h Ö.’ü)„¯ Ç_ë€úþ=Ó)Žààq_Ÿ…5V”)·.À°ñØ»’¦ý¯ÞZ¿;ï€Û·¹K²è SXÊÙÅP8!Œ5ŸRé ²8ß‚¼Ó‰ x²úH@À q=šÌZÔúN£/<=ª@‚;éÆåÕþF¸ÍEÇõ/‰ -%G¾÷­±†G©ÇG´„àS­ù ׄ !äåqc[¹0.1ÌP9. Õë›Ðs(ðæÈ|ëÝk,8yõã)ƒ¢@/Ó‰ªö´D»Œ±ì[²`sýk‘ÀKì«.{rFC˱í»0. .9ª=+ަ·Üäë•“Ž¹|_µøŸ:R>†eÌ ޼ ü þÇ èÕ©U+¸$¨Ù ]- f°THf…[½>ÖM»~˜³U¯Ðê«Tü]ÜÝ“~†ú¾gþü)§¡LQ¢¹MçÆyù·éâ$çÐÎ Œñª¾õînÔ ÙêÌrP¨Í5öfUbùe7»Qáî˜1|ø³5UFzÙÕÝçâ¸X¬\´c—ª!¨¦èû\棠·ý0UÉP•[cx“7Ô+œ&§³ñ祇í\l1züñ›,ŸFé1þ`7\ÛwU£ª æäëü<<®ðbD÷àNË?lÙ…¯6÷³Dçyn,„©gnl;Ê&EÊ2ñm]Μ °kÃ*Ùø)“‘ò`ŽÞ«¾1Çß‹úµŸ.¥ƒÎi;!ðnOþ}9¸ãsú®4AØüy:Ì­M<’‚›„$¥P+`ËZ¯ )®­#QP|ùRfJ”¬ i?¬ëlƒ‡9cž¹²áÊ#¯Ôé¸/)Á袨#ä¾ß8dõZtö´GÉþï÷Vn¾Û›þQd—¾|¡slGóØâÂ#¾ñÞÜ  $teK)`·yäcØé 6U *íáÆÑ°ÏK 7fî]ÂÁÎÿÚâ^2¡|׭ݺNìò·gÇG ¸çl›è 2÷ƒÔ“h˜§Ò¨À½ œíÑù!—‰~Ôsé=tŽM{Êç›é©ÿô[uñÆ:ëì·‹Éø Ycàsˆ4ÜxqkøR½(îž`GRå!ý· âÞÕ–Ä9£4“Sð/-ý÷›7`™ùVþø Gh‘ÈmÖb¡9hÅŽØãyÊÖÆU t6_2v%ܨO®cÍØÑ¡ éý‰?Ñ,x+^!ø×A ’;g"†üm€J®ÞRq€.ß4Ds¿¬¶øy€WK |7õ¼ÿ¨‹¢⎌ʒ@7¼>õÚ #Ü'œç!©Í…Œùé±kxþ·kÒœ‡öŸ‡0¬dº*=’ 3¶#Û÷HÃìv• ‘ ØXT¤{ƒ+áF‘.i YŒ^AŽüM]\ÌþZ6à* àÑ^“<à†ŸÿÔTj¹¶U¯øÕ-^WðÚ~Ôþó<»ÉÚÀh!·½§dßÇÄÖ“¤äd ýš}5Ä>¢í;þî•áïË5®elÜ<²Î0”— §/7è¼2Ç™9†ê;:¼ü02õ\Â<»Ì?=!ÜøÇc'¾ÈòLŸ8|–ƒ"GÞ3ÛKáevÿƒ e˜ù$î…>%Ö„Õn7ƒë矬w°A ¯Êt6^O÷*ÓqPø™â!G¨ÈN/IhS…¡1ûscä1¥InQý‰Èåâ)U½U³«×s* 7ªû‚ž<~Ïd£ g't‘‹|—Ub¬Ñ§zÞŽ¤6ÊVô<üÈÉìÔ]™.N¸0ºªXÎ`©ï’b³m ˜\Úûý‘-®[`thqà[Ø*çtœØ±mÕ 2œ¾¹ÇÜì‚=.b›¬}l~‘5ûÿE‘Ñï…«ã•XyÔÞÚ{°q/êfR—}¤aÂÙÍ¥«¿0 ÕÝåýå³: Ú²BgÕ]sX=ÌY³Ak޽a"(Õpš]%`VQÅ€áÃIB·)ÐÕöÍM¶kœ=%zóîáFx¥|ÿ0áÆÃ!©Í •¦×2Áø2‹Ÿ–f&=:|Sü­»?nLUHÊK¦áÞ›UçTÎék3(5_5ñó~{œæØ’RßjkHLLž‡kºxñõû[ÅBdlj8Q¼QÔL¶x׉¢)g \ÀJûGíZWYBرÕãÌ£ ° ÛÓªÿ@¬6TvÊg8ÂÖÍ J¿ÔXè][qá… TœL2ÿ½Z ®e…?ÍrÀŠ=×¹¶tXÄgs£x b:_i/Wƒ^µåÊã>6`¿{jß@,þ4'Sz.Þ?¬Ì+®j]&o¿é¢YSù*i£‰Ý?±Ì¿EˆíÓÐáÂþÛË]ÇìÁúC×I×ßt<™~CO‡ Qr(;¸@3÷x6¼Nâ÷%•}=K=õŒ[,¸ß”»zEÜÃíüw®°D‰£êõ…²ÐV$»âüKœî{!n’¡ˆÌkƒÏcbÖöé}âx%*}‚³Ù.'Öµ‡xÜéšÌeBwÍ"k%~€µO–©ñ8ƒ­}õJUð¿p˜(È0ƒ_#ZICÕú(5ï*¬‹ë˜{åÃ÷Yc0Ç'yéÚQvð%¹¤%°mƒ€ÏŒ³®·¸”ºà÷Ù›{ð‘–±ìÕO©gës€rý½»HviþÝþˆ 6;›2íQ·î¶ÍZ T ³6ÚCÆ5u>+ì#åñ÷pzµ2hp;[yÞÓ0äñöc¥pú8Ka{ºÿ¼QÏTlv,šcW¦Wòí'ܸ‘ý5”YË€…Ò#?¯R€á©+²Ïrœm›1ÀæwX±Éc+uÏYãò»(³[8øöØ/•7UL°WOÔÑpˆ…•gíÕ€âès¾+×zCàö#’Ú)4~/n:H̽•;w›{Ýô”_;ÔŸcÿ²Ý9ίÃÄkªÕ,»ˆ½ö«çÕ¡Ed´ ß%t|«4dôÏ L<Åê[1fòp,Lô¢ñ K°•ûï|œ E¹k|. À¹¯!÷o]p„®TÊÞÅJ,Ì\>V^QãÁžƒ[µàÌð-9 ᆤ³zìqâ¼'7`çHmXáBPÍïAOÞ<,Àòÿg ½×^ ö}ù­ ߢ5¬ßýÐÅt«[¶%HpK€4°ÅÓyy “Íu¹p6/OèO„5“ÆÔ|ÆèÐrÛ¨à»r[ ÛvrAÈ0gÔÉK¬ôµŠÒ)à×3yK¨ˆ¤bÞ«1r¢P8µ¼ÏÃߟi¬P¢]‘Õ„3åk×YâÏ~ ³S™Šø*õß“¿„ÑdPÌÐá‹8zjêòŠpÃþÞ~¥¥Ë©«BWð2!µô!]g€é¡võEÎpòdsâ. ü6ùÀÈÊ5ƒG&†ÓÖUúxhë¶+ªV`ÝdÑ_|ŸŠ‡ŽÜpV¦ŸýTóÛ8ÎÅè×…C{Ä´ö‰ë>]›î²ûéç=׋¡®¯`¸D”4Ïîv÷ª¾ø½sÜtY:á†zǺÁׄ“ÛY¦×-˜ø3H["Y’ ‡N5²ñ¾Èùc¾RT(œ^hù÷„p£D:LK‡åO†>ýÕtKfÒ•œ2®J9670ãÞ‡ót:†äà¬:[ã’ ú0\=¿B*'#mei©àØ(‡™Á ý{µ3 |±\ÿAN8–Ü[a*MÇ“O^ xáË/F÷ *´ ÖÙ!ëà 2 »IÐV @ è­ XtQ:ŽpãØ¥~¸‚‘Œ^]tòQØÅg‚©]A Å£lûKÑrû,à‚6Ö89ae‘Ö_€ÊÒ g“L:"sÅÒÿ¬ÓñHÕáÀé¶Uµ"ÛéxͲûþ}2 9Ù]Œ>eËùÛô,°ëXŠIÀ>2ú{šžT –ÇCÖçòGÉp2W(ߤ•†u§'™mÄ~®óe¤œÒ@½ //}¡[/ôÚçY¶oÉßß 7.îÑ-Å€½Nn!œl Œ8ñˆ”ø±—ÔFÖ*nœ‹9 ¶pC¿øAúnþVhÝû˜8¯Ý¬e'Ü·“Oü¥½ h) n ^•$ͦáñâYC¾Ó:Ptgé/‡[æðñ”Iéž9ö÷£lkõ˜xŒZíc1 ‹±Ç­ÿXṸhû»¥¡@f¥`N¥(Þ +]k$‚wÍ÷/³„L™Øšç'°lëâ'õ±Pv ¹»ô²#x~ÌX,çC!ò O]àMÁG¡ŸÉZ— ”zÎ3É%{¶ÛÐAÄXÍqü j‚üu¥v«Á«2¥'œm`äãÖŒ(ñÞ/úÀÛjfÎé›*ì–6˜ÓEëî%|‡ÅHà6¡ð<ÈÕû^GÄèqÁ¥˜•¤·Óz$Î÷ÑAWöÕBe&\^.¯Ü¶› áŧÊ$hÒÐË4(©=EÁ÷ýwY°½üƱ¢¨5hÕà]k‰×º²”ó²°Ä¼µ8‚øÚ8nÑ)8§ˆÇ_•õ=˜Fó›Ná_›ÅQè§nï–pp|àÚ׳à ù¾¨Ð4,îe½ˆ]`ómžÓüÎÀëà?ýp®Ê{@å-3èÜø{ç±>ž9søÔá+¬Û¡o^AŸ(rÆ’à¡sÓšš4Œ}ùÅÌOG-Ž%ìÖEå~­×÷±ªµŸµån-ïöáwÀÔ9nÊŠ+ÿûƒ¯ YSáöÛS=­nà÷K±¥›ýãNèXÍv 4K8ôSÔ”§Wvœ›&ã7Ñ•íëE”PàÆFù©Q~ºæž¸0oU7- ùÍÅÐÇä´OÂ$[ýŠ !ÁºUq:ZßÜóãä_È?w),oRôz†GÖm“Gòö{‡¿‘ÀÉ|D ؼ·Jw>âSáF@VÌ´Î'~xò†GȶSÝNùD/Ýo7m‰me žøBI9cóx·¼ä:aÙŽŸa >Û+Ù9?‰ûS|4k·™-^ô߸W@‹“6 f{éèg°™ïf |T.¦]?jwï\T¸ægç;¿*yÇ‘q®b­<¦®m¤¼aÀвÙó–™Xذæàçe£ìx±£e†„-)ÂÑç7ÊNý üÃÁ§¶·6i¡4ª'ƒš,ÀÛ ÚƒeßœfÀ-{wiTtÜraÝt„‰Ëçúâ,l°ì™Ôªp›gÇžÓ‚½|ºÎ:àŠÂ”3itøä+é})…3AkÖ8%©û!gå °ÕëÅ•;к§æ;°à¹d'¼V…é7¯¾ùÏèâkÃõƒÂDÞpy3\#ôñ¸_~HŸ â†Ý7÷XÃ³Žª·¿Òá}¡Vi¸<|J·EEsaô˯[©$i(gܺžqœ§ó¹iQ÷X—OM¼Þ—!¿[C,ÑZ>ÂBôŒ,ˆ×&ö%Þ>4©™Ÿ¥ˆµ{É gyEðé,ÿøÙ—â¸N`{Õá†ÌŒíƒ·D¿R[Û<ÌÎ Š´y6|ëÐ<{RÐ6UH‡/"ÀüÁÃ9÷Í€zêTÒüC}丘­È²‚‘µO¢‘ŠüãÕM™Ñ$ˆéó¤_¥¡^.ÅPSGn18­‹YYoýôߌUÅæ¾¡o&Üø“ìÅ;,è€;Þí×½HÅsR…'S¬¨ ÿ¡µ]jÈ–~)y·ye7û¶Hèm¾X ÌâKÙ//dÿBOROM’qÓNÉcÙöJÈnœ¹–p£+^¢ª[ü1M56 \ü•lï.ÐËPÖJ_â†Zôˆ‹KË™ð]ànp‰°+ÀaC‡€˜9ö› ™øÇJ_Ùû÷oλH¸±*%ÑŸwÎÉ?·•ü&Z :UЉPáµÄUñs—ظýá}ñ3êT(£iß•»oŽ9ÁB&;:45Ÿ b[¸@ÊAÑc%¼_¶B­wÄ´¾[_º5+G^¼xÅ+…WoÔ$¼óT† VñçO8TÙî¸åâ3È:. ¾lƒ¯UúûųAîóŒÅf:î‹l?4çîë“Kªþ¨ÂÉ—§•~‡Ëc¿=/ó+ ”sßoÙ´¨·êíî¾:‡ÔpùwªŸág~ø·ô”¸ÿG]Tü1_zö€5¶ïoäf*±7ñZqð†\í'Ñ›Ðw×oËÓËÀRx[GBWñ5¶Åc+V,>©ÁǶ&Ðqìf‹Qá2<‹¶.9d’ÕÆkÍWZ mÊ ÈhávÚ¢ÈE·)^O¼už °ââ±³oiøÝp±ÒB#ÔÞ?K!Ñ—éüO.>6‡Fç숊¼YvÙÑmädƒwúFξcÀÀ¤N’ÃI ¨¾NQžýÉ6»ß[bC¸qçoäÓ: ÃyyñüXÞ³ûC ^ÿ~•Ï/ë„"§¢üÝ¿ªÃ@Ú~ ÜÐåïöë9JC«¦:ÉûÄÜ­i“òÅæp`‹¾y}Í,{EÑìsc&ž«ß1¨8­‹Z'8ß&¬pCèW»iˆ{¹½þO©(éþxJ[ž¸fWä®zXÂÝPñ×i 8¾(Öñ·«lt Ù|½Øúß‘%ü„Yx1Z0{e™ T­:§}K ÒßJÐvnD….á£Ò‰û—a”£–…‘Vijð,õqûM¦ h¾úm=ÉZç¼Ú÷A¯劼Gªõ2ÀüŸ.¶hjŸ]-H¸1õØF˜Ômñæ˜vff­PŒ³†ÔÑ옾v:XÆòûÙK3ÁÕfrjñ^.̦]ý¾X®ûpÝw¡€Š¬å‡÷YpÍ¿ëuu‰¼x–¥œ¹Á×¥é]:" ÷_6õ³Äž¶æ;3ç1uµ½D€þÞ¶üf\•8ºÎœjÝáQ{¯SoHu¾ìfÀͱGŽ3l˜Xü¾à °3DÕ{.3Ö„ÆùÆz§f0¹ìéKÿR}ŒŠn?ú´ß ‚v©.»úœŠKçÍ’vÀ§–‡t'Ÿ†øçÿPQÁ›akN’öêâ¾³.ÊÇªÖ ì¿mI¸±é\Ø×"xúGü¡¶4*¾¸pró¤)&¯ï0X6ì±]á‹>|goØè·é¶ÜH½gÆg[gå4ö‘ñôCþ‡Y;•Ð/HÖ„JëV¶ß`ãIv^—¾ ^àݱá.€«aR´³ ª5š•2!û™°»¿¨+п߉~¨Gôë>ßÈÞävâë!­<ÂE 퉊„/%ÏwcbÜñWTÐ)ç ìÌfãßCíÃ4]*\éÿ£PlŽ×ª¾=q Óf0ù쇵 ¼ý(üèÖ†ÚiY=è7ï~yžGê×½ò,û+‰ñ‚»fº)Ã5³ÆµÊ®´Z!Äm]eBBæ·ï›Û ÂŒ©aƒî§•Ÿè¸Îþ­ÏöPíâTƒR›Xñå‘j°/î –_~+^ßÖSuPãªhÂF¨ù~..o jÑÅ‘wÁ]ñÖ¸égë *cŒݵ!¸¥Ê¾ éÍ6'ü—ԩ·ÀÏý’óÐeDìÝsØÀÀG¶ô¬^¬Æï5ƒMìT:Ž·WŠ]*$×GõíñÁâ_%·},0£`Ñã2ê6ì±ÙVÓˆ¸DÆî/e.4¬2x°wu=ÑS|6åKÑ 2µ™BOÌAS#‡Ç0q–ýäÆ×ú¸ùÜ(±÷DÞYÊcÅož.Ï)9që6[¢¶É-ñÞÂ'–µúZâäsQŸ‹ŠÈ䡉‹ S¨è$û¡8ò67'Ü V½9–¾àU‘»^nî.Ìm™dÃvÓöý¢Î°¸Jãœn‹&ܼ³sJã%áÆÝ(£›Åú(ø/i¡µ× ¤ùœì{꩸>šÚ°…?sýdz³i¨þl힌oŸ]ó,F?ˆ­û wm¬êëéåÙÓL'z°u1u±^~"|pïQ*þ8Ù˜·Ê ¿¯b§ÿt€±%]ö?ü¾³5Ÿ/D}>eëq­'Å`¨Éʰ¹L¸ÚôiΘp#ëö‘ åË™˜ùK¯$qöÎ/Mãžc£úá”Âc*ü]¿^ò–9~c¤$,!öH‡rê¡‹,yQR[ÈÁÛ"޼èu‡ ý½öéBòpô OáÞß’ØéBòHwR†£%j¼(x‰Ýq‰ÏÇ >¿/húalƒUú|6lØ ‘Rwš—ŽRnßóýåŒáÒfoI5XìÓ°°žpÃÅ7©ü â¿; }»ÑSuoZßâáÆ¹Š°aÊW~ÐxÃ:Ш‹v½5K´Æ¦J1Ïê1¶Ü±V3·÷ ¿ßßsœ0Íû’×N/€C™~bEDÞ¼7úgRÇ£FRæ@‰ÉYæúÓt¼Âðì;}ƒ ­w®xÄÙã–j+K½eøcHæ±ÆA2ÒËýI²ò}¡z½ÍM2øòPíW= ½‹)^wê0“wô`ãAP:½\hO•9¤Ù•m\7Ë®|öSû7ÞX~øz³êê'Pàr¡êY}Û!öçìF_ÂCegIYÖ(¾DÂÇú‡Ûõ’n2JË ÔYì„g¬µ—,: ûeÇ×n¼¸mÅxs‚†%Þkîø$é@aôÚñRsÿ&êµ;e–ë–Ç—mÎDV›s´:ŸöŒ\ê|úà )i*ÕIƒ¶ä·PÝsêWÝQ•ÒÚ2Kˆ\( üx–ÓQÖ²òà“½ìXI¥#x]Ë=xäŸ=v¦ž5<_ì3sÜKoµà¹ŒeÓ*”جO‡-é`2¢·;í y÷dÝUë³Q·Ìm@BÄ%Ò.œb<×ï‹ØïøL¹{Zš›¿—Y¬‡guþÕä%)¯¢Å/#以—kÄ…ˆŒ?¯5R¬Á†õXúT ݧ˄™ðäÊÝíKb¹@öMè–"úÏ®,©$ Ø®é!ß/gDgÅÖÝ"peÖß¶ÕïG)j—î•• Á­YË,qˆëPœ­ˆŸ49Ćõ’Vb$œAÏHj¡=_DJÇÚ>2ƒÕÂ_ÞÒoèãªóoþLuZAØsÓ‰ímT½ 2B‚3—ÛºÂ3ˆ^têá ¸ ®¹w%añ~]|—|åîÉØ±ª®ZÖÁý„J‡E ÎH;`žRTî‘¢§ˆo^Р‚›§êq1ˆÛú}U;È5èï5 Ôj”s˜1€º.ùÒ?Èè%ý•ýUVSU7,#ÜØXÛteÑ"6ê]Ü4i«ê„‹DÄö}kÍ»œÂ†fô6*ì»Ì„.÷vˆ¤+´:t~7˜eûTµŸä¼eKò»(\'ÜP>^÷ÈpC5Mkvz=÷g§ó|œ§€K¯‡Ï޳ñýµq‡Ít*ðHy“>å™c”Û†çv¦t˜»(¢aéåì´@×îküÚcÊŠÿì)~ß‘—‡m·/=mý)‰¾ño7³Wµ»ô‘­,£óv³ÜÌ@²˜­LÖ±Áý©¾eën²AìÝÆ¬ôYd~ÞwDpôßá+&ºÚjÌãa×·F#/o¶Õl!Á‘»Ï?={ª\›Þ½ØF¸1´úá}Ö7~XÌNØhôLűꓬQ´èw‹{ö;sgˆÓ:>KXº!} Û ¨0Ý,ŠØG?cÀ©¥©÷v«Ûâ»kÏëBå8p  âöÇ|:4ûЬ¾Lûµ§=6šôuóxX`æïæ²)dÜù@0VEA-VÆÿ{D†­K£tlëh¸e_û(£Š”¨Ê#Kât@ç×v£ÐZs¨Þi¥;Ëþóa‹ nðÒž‰ÿï÷J}¾…—6í @쓵;ûÙ{.~ân#ܰ˜TÜE¸¡oükëÌYñNÇB>žm®ù¬%à„]m¾—-ÕÔA"•C áF×ciÕǧi˜ô¹%¼˜ûíƒÌ•¯ÌA<‚*hï:Ë~PyIžÂD½3‘{ý¥ôðÒy¡n/V˜+(¼âÒ9iˆ“=czY¥2öfÜ–“‰:ñõ½KÈuútfêFg">õvñìqfÖÐ+GX£{ƒ÷Ä/{tˆ[YvÝLµÆwß›Òv¯B¶5áÆ7F|û4qNŸ—LÿÍbÁñ,…Ž„·jpí©žð^m0â4ˆe@Ù 8ã´¦ôJ£Mª0»ÖXEcrýùM8òáXêq #|ûQ|å}.¤ËH¦zŸ¶†•G>Ì?¤Ã žá뢽æþS"}âiëýÁÍÕÁÉRÎ^R¹Iü0‘7Öî™ï5ƒÞ«c®ê£›tèæñÏVPs´¥äC¿\ßPye ”¼¡ŸWäîÍûóDTðÚJž„º8«/®ª°q¬*Öç¬ nØÊÉ¡É9`Õ’s‚äTlÿi°^… 5%Ù k‰ýºZ·~üÆzS»&¼Ø«_z /›4ÏöS¶SÝdŒXëP(榌Éù¶O ~z@tßìs6Vl_–o¦í„Ýœ ¼ô 6tËLyÕÐÜpœ]/ØÍ„­/²;E¤]A×eÏÝ×-¿ÙÇð;wŸÌK¶Â'õ·77„>-?çK¸±cÈlaâÁ/öûOý¦@Œ÷êug³ÑôèT¦› g¼E²š/˜cYý1^GC:x/Rõý´Â®9\Òýs™ƒJâ“VƒïÝå»þn¾š<\„ ý’Èåœ6f(CÏÑÀo !üYuoã®<ÛµäåzMLôög½)eû/ò<—§l°×"þáÀ X==öb“‰XMŸÑÛà/;Ê4©M$¨jK™WÓê©J[Sà]E¸azÝxÌŽ8Ï~æ_¢ô*t±b]£³Ò!kTi­í«c;u/[%+i šùß[XNèUØ_ìPãjœÓÀ€‚ÝËØ*¶hñÎ"á³4~E/þuƒŽÎÁ%]óÈ <òîÚV{ÜóAâM”›F¬›ªH%#{qÞ¶7"òhã›ø»É 6S-ÔWC—ÞaÇ*ðu dctÀì܋ަ:s`í;=–&0Ëþ©;`¼x–º Ò±ò Xº·ï*†SÀêß’ïý2?ØOCŽ(ï#ÜxÒ<¹6‹pã×ï Ã˜ÍAÿZžG_/2APïgâm^'´n½wþÏlÜsdVw7x-8üÕO£¡ï¹E󉹼¶»W˜ƒÓ/óC9FDÞø·tÀ…ÆÄûÜu*÷òHúkýVxå¸GîÖi°Ud-ÊśͣŠ6ÒòPwã“;m j¦[öÎg3 O\£Éÿ%?¤5²ÝÖäUB_rGíÑNs(´åŠ ìøñeÉNamðñ±¿žî€¼ÏœmÈ&tðÓêYz%›õCÚCO¾ªAôþSi»Õl ·,ÕçX.–X>m›Xújð-ÍWúuÒùU´ô{Ú^ÀoB~{fb„‰ï}-7åBe•²"Óoù.QL®ÈˆFõ<:æ¯OY%p!HrõÌ5)0¼Î^Mä¸à×Þ&ÏXÀ)ëdf)Š@IKáé^Kœ Îà÷ YØ”x4§ÑŧÍ[½kóñÊÃ㩃º"ôi*´å´8Jö½ùI¸qx×…˜áFìô×ñÀ+ßMžìfƒã ËÜg½³r~«&0ÂË[šFÍ`ÂË?ív¾>†=þ1ÍÓa±y¿ò7*ºjYíXE‚wQ)ëUÓ0m©KˆžÒ¸‡éž ‹+·__’±œè))¼a¼„SWœ‹>)8à”ZÕº¶MT¼7òdºZž —täM8À³è ¥«¿±MŸ~_n Ÿ‡aͨ0ê½zÛ¾‘ñÞ:àw@½or7xF†-°ð²"µœð ýÂß±a6̦ñ œ·róF#|Šç˜ð§#ñÉ=Wxh"ð~&þ7žWOEv€–ŽÂ-Â?ÂÜëk7:r7)øG11úmê‡) < ¸¯»æ  u ']:T‡L. tå ¹™|ž{•ÿvi‹;” úÍÔ‘‡øm“Q^Ý’Øùaßï¯Te°Éz[bº‰‚S»=Öd²ÍàÚ©È/gTmðHðß½¬§lp¶w¹}{ÜgR7¿úáÇTBÓJ¨j0îsôÉ·ò¨—uädF# öïž]gñ«»ê Íø ÉÃjèq¼,ñ.F4ÝQ{ ‹k·’×§X£ÒË;Ç¿Œ±Å7óTÕ«XBøäœP‹½ª]=µGÅÀ¦eéŲ6ø© ” *Ú⥂C…Ë$9Ðç*'SBG÷=qÔl2|q:yñÕ&{œ™ÝõÃÙOß¼³~ö?è¿}ûW3^—Y{I†€ÔËI'ŸÑÐ<±Nò#Ñ|Zó}ŒÒ¿i]å¬Wæ°ôõýf_Ù´9há=dÙ“3?0vMNãöz ÐÞÖç¼¼×ËîäŸq:D¸1ZzójáÆFû »ã„ˇnd¿ÙK|žc¦A]”\Üë´Ïõf…ÐgÈen Ú-¥¥ÓðÅŽBÖæhxù`eùsØ›´˜2&:Ë^¯´_sŸ o.ÿ©®®‡¾t«ü}g…+…¯²»" ²Ê#FÙçEñ×ýYß§âòðròÐíT;K8—{ÖuQ~Ì~dUßá‡_ 'ÛáFD‡ÏíA¢§¬ÿW8”GôÿÚ7÷íTµáç ·1 ƒ¸ŽëáFtˆYÝnù,x‘ÕMQƒé%Úw·(Ø@ÐÝåþ7Ö2àñÝd¡ˆ_\Ò>Ù€*4qxêá0Ï7g韚 `ìeg`„wD4ÆžšqA1W–¾ö‚5˜Éú”ª^§ÃD`{jÔ Dï×¼kJ䂜´z•Ê)×)ˆÕЦÀºDwá¸üÝÜç7ô[˜wLOÄYâÍ„~“›«dá{'¹–èL»­zYSxFiMÆ"¸]µ6ù°8’ NoÚæª¯r¶PŽþáÞfwè`ØŸN6È\Õnß/ç :v#Ÿ}5aîù;ñÂY3¨H*ÍÖGvâ¶Jñ÷Và=–º¦—Šçë"óm—“à§“µë¿Ã4Ì\¤îÂË«‚¹&¯uq€GÕYƒ5Vu{rËÕg 'ÜSb’— ì€ªÙ˜₩˜»ûj†Ž4è_¤¾M:€Ì±l…žGlƒÐϵ¬Z ,XžñzãtÆd6=üBFÍk‡N–*cì«UË? {€_÷›Ç,ü'´š%ké„X›o¹©tcL¤™:=ˆè[† ¤º¥i«å\¡H§Ú}•éoö¡À÷z¯×”²­Â{þç†ekgáFÿ'¡LLQûupzœÉ+Œ| ö³Q÷€Ì¦*lž{°éÁis¬}kàâJ¢¿¹)‡ºÁ¾¿~Ñ0‡ƒWÒn4–½q‡vå¡)Æòp»ÚÆQä«$*ä¨?´T†Cµ»>ÙFÁ§1Ÿ2Í`UæT´ÁEf»Ko¼dÃ'¹€Â²ŸDÞè­Üz«Çopš²WƒóÃ_r×,—Gÿ„÷ÚoêIPhJòyÑ]5u>*u-áÆªCEY”n~Ȫ;‘/ÖE¿4ë¢ÃÖ¨­nu@õßûâ\BLœ¾%Œñf~n¶sÂBŠ`J²3ÀŽJ‡Ä× àK’Ö{#k‹JIyÝu‹9pIöò“r:>8%‰tž ¹bõ?×Ûcshß.?®ò¾>FÆ·gØ[ŒË¡Ô•ílï&¡խ«hÈçÜò@ÿ>î$%IæïÔ2^+ÃÂs8ùÓ¨Ôýåo¶¿Ï‘ØK„šž;lO0`ñðhð…5`ñ)¤Øøô°Äâžž"Ü0ÑYï3á†çüöß,­OáȦ3!ûðÍ#s\üSÝi³é˜4)Œ²E×{ƒú.Þ7sD_.ЮúaµKÈbïõÐúØ’|‡³ƒå)|·L‹{Ÿ7¡£‡â›î)µ5Z!ÛòŒ‚ðMiÏKÙn)ЧW¹u8‰ÊÃïæ§ Kè¿1"t‰ógC™üà«å˜õÅ2rÏI>î%ܸ7ÁåÏvÕ‘ÿÌ´a ·Ü5%ͯçÏ]2 CdÈ{áá+,8}#BhÉ_b<áž “²ŠšwfÏ Á¾·0P©p-×TFžÇ§Í›ëᯚã3õ?4"t4MFÇCbT›s¼uCž5Œs¾7ɣÛôMžF“ ø.ãðl[ι»äÇI±Ï¥ùþø%™ßÿœU—vßóþ –¸R¬Tj‡·,Ð^Å.åX¢Kõó—Û¯(¢íŽÒÆRKì0m¿D? Ž¥Êìëß 7R\tèfÛþwWˆ_a@ÿ…xÏl˜vSÿ£à |ëÈc ¶š ±öeN<Ÿ9<éW¤œ×Çþï«TZ­ +Ož7{ŠÛÌbu=I ¬)ÓÚ•Dà ƒ…ˆ+yJØ+·Üçìg]¬÷úl³^vŠ=»ÊgçV%'ìMÑî br± RDZUÈË_j\$C£ñ¯ÞaP¹Ñ ˜EÓÄR_NËV+|#ý]?ÐS<ÞJ“®SPúK•ʶ],‰Ï ªÜé³þ [Iž\tO:&.(þç2‰¿“ Ù«»ÿ2– ÈŸeW„jè þê_ª˜®+üìÚI·×Æi ¯èöÒ8Ä¿ûW×M/Ønwôpů±Gêhk‘¿¢1L÷4‡o ì Jhót¶†/{+Tÿé`i1iø°à|Û²ÛnàÑWpÿ±¨ ¾µûl5ã=ùŸŸd*Âv_…´K­ü¨±|¥È];˜q¸rs9 Ǽ×,2PÓÊÙ‹‰^ât”¾S·ÞÉ«ezX £øOßG(޶m*ù§û¿Õ‹a`¾ä ¥x-pX›·ÜòeUÕ¥UKNé©à-}Êâ"wIxÈvPø\¯Êiã™4ª5¦ó^I²Þ­/xÎÔÿ¸MüÛ¬Çó8hàälûË×~t?²œê²‘à„›R™ VÌ´5 p€è•=2žZ¶(ÖÛæ½Ì¸'Ç“öe³ðAXí^MæxüÅõ—ÎSpUò³×Ž}ULÁ[•®Ú4Ø$Uë³GÑϬÕÖ©¶û:ï×*áڰ§,åÊ øýHo:ž±PÅ»«…?†ß|ù²sw ²àX½m{T©ô¥¤ÖûV `תg÷GK] ¦ûäáûlŒœ]Æ vĈÛ}õá!Vº²Ü¦Ã O½ŒŸ8(Ê_2Ý䇈=â›8• x@wݾmX;ôüª©»¨^¸¼4E`¡ª¶%ʃÓÂDOÕY•ÏLñúÒ|ý ì*¾³t0D†–iæßäÅÜDöðÅyxbI!zŽÏâ¸.Y&ìÚͮݽX·4Ÿ¹ûÚ n\«u‚‚e¾_­#ι‚3Qt‡H?ÖTt«f£öãdí5ƒöPé[E!ÑC÷{‡‚רû>..8¥ÇTo¦CÎWáOó80¶ª‘ß=YÕ¤ –žýѸ¸[ŽDE°0Æ¿®^·sAI9Kôi‡-=7ëÓòµ*¿itèk-ÍóïäÂ^ÛAZå0pÊOçi8üóžÊoZʆ£F¡:Iš’xm̳’k…,4Úi“÷‹=Ò*—·2ÖGaÀOú".ÍZ´?ZÏì,5À_f›+o%»Àó3¤ ÉºŽ€VÑÕU‹_øMÙ Ú&Zïììn2ö1³@rï›ð ް³u⇈©&Üï©/–kÃÄ"×ïlÕ‚¨3ÞKÙGk«RIÓb„—ý¶÷”„?Õh7µ‘/íˆö:º5æ–Õ¯7ÏU„µ! ÚßP ŠòQ4õ"ZVüâæ£ôrÖY{ÈÔ¨—JÁJóðÐPUݶtÜÕÝ)_ÿ‰3 µÝiÉ¿ý,,ùÂòÖñ3Gθ[BX>s‚ÐÕ^§§ª%©²ö±+ yÍH|–xy—z O§-h²y¸Jªe•ªZ-`ò¼f®ý×…ª¯Žœ°µ„Ë…ÇÎ'+°à³åBâƒuPûlèãA!ä³YãE¸1Þçä=ëÿòsÄ’÷ñJK­à.ï.ñµNøéq6Åõ˜L¤ÛìG¸¡ªä;Út°mìv»°¿6ÐomݱÖÇ?EæzßZ¨Z.iBÿÌĈ°Õ·>š"Û´1ja• º(¹Íõ€,¨…ul öàE^׋«Z[äÁîpòÚ3ÙæðOíá¾2&ÜTtžÊEæ²*™ÃN¥`z5†‚ÅáaYÃ\á_β*âyõ‰–Çÿ¬d#gëBPÏg{ LÇÕW°¡çuEAZŠ!656ý"QÀñÇ”ÌY?:˜¯Oø,zn…’sw³¦ª&@y—g°šÿvˆã¾‘'}Î¥ycô¶áÛm°› '+äŽÿ¶…má}íÞúV`¡jÙ ³Ûeµ~6õpaÙ„ñºCò8ïÍ=äG†¿_²Îû±áÐc—\çZI|fÃr ¼k…÷ÃÍz½µ~±÷kž1zácZuæ#x¾,Û× pû†SÿV¶†‚˜ŠqtðYê¨uÓJ~ªq]fÜÀºÞŠom§5ªZ¼*«¬$Ü8§õ7ò€"l¾^±+¬ˆçÅW2_ ©@§ÔíážÓ4üþ=Ð\ç·6ÄDTâ³Ï68¸5¦vóäóŸÓ=/m1¦Ù:^)Žà–1»k‰¿&dtnØñH çZ^¾ ÐÉLƒwK¾cUoŸl`§¾ ÒvÊyHBYÈ[“²TmœxxÆ¡ÙÎù7Ÿ¬¨QùçÕ“Ÿ´©`òœôûWŸÕxmïuú‚¿v:ÙƒºòÅ÷qÜVæ:$Ït€šè_C ÛmñÉk†8C˜YööþZ4 6u1G•ݺ×)XvxCÐ!Ò÷ªûÏ„V‘"i`<´kÛ+Kìä/\Ë­³…õ5)<æNÚ°kßùvr˜4š­³åÁ’íu»7,Ïg•ÒgÁU‹ð†rÐk¦´Ds.N] &Üø”[#ÚÙÌF”5›¯ÏrDáÜ•åwÁ VØ=R¾ã„×ý.;”&ËÇ¿/ùéw2²¾™d6RFÒ;kà õ­• ÛF٥Ńû_dæ-îeb™\+„Ž™bÿÇ,udÚj9wA:ïü¶ãEóé×jûžÈCчÕûûÍ蟆îi1AÖÒG—CÊœ)õÅÎàxçx»÷& ªÏµÍ¿Üà B˜Z$sÞBoàÇïØX^½òêÝF{pWØ“q¯ ì_õ/B¼Õ Ÿ®¿d(°4è´w¡Ã–MÚ‘‰·7þ©”]!ÏTÅ„4È4À›_ÑëU…l=åéðwΕ”l"ãöpáWêÚ­;þÙBàŠþá{³– üFPbG*çÒM¹0sFé¨ç yÌŠe.ùâDq=m3Ál߸în¸¹¾Þ°yëkÙ?:¨vôAH’=C-›uc³Ó eHXÑÀª±.·Â~%ß&5Às>} ~éf'écõ®Õø-n _¿R£ÁÅ'W׈]u!îgÊZß+Tع/ZqÓ.Œò— ¥Ãë}eR'¨®»ùRüšÏÚÈ3$Ûï}WÅ/=3ª¸îVʺ뇹XµŸÎi{\tÞ0ôÑk#hxº“߃xµd!¦¤Àøwºþ¬ÕÁ¿æþ¬v Å'aáªK€O ¼ýÝKkt[¹Î*"ß>U%å)‚‘êË{<çùшêòÅtD¸<][{ïÐpŸqÌØæ^mx»¦Ðî¹ î›žø8]ÁƼŽÓ¿'ÌqÙ5Õ|é]ŽðsjÃÙ™dM =~ò†‘#†UO¤+.Ñ‚ewmËóî<®šc?¹/k ‚OŽ©zJÂóÁŸq›´qÜá÷»&°Æ?ÙÛs†áxØ¡Ï=Txì’rðl:Ãß‘è¯ÈŽ éð¬tw”=Äg;jjm§àÃ2«ucØéñ¦ø¬-:Y™¨ß3„gw¾¿¾[üìŽ4ÝO°ó*ïQ0ªüþ?e™oUéµo¬3hSÿ¾~ò·ž8ÝaøíŽ-ð—í¹£IцÞÞ3 ŠÛ, ǽʎòà°ñ>™/|úq釛4D>;ÐãR§Œ’€)>1$eÅ(¼çît;—¾a#Oß)%‰ûŽX¶òŽn‘…,»·éq–~’¿ºÆû¦DP•²ñ×óG26hï_þí`Ã9§Àû•†ðD(iÂñ|fIÕžÖtxÛ@Ábe…¾8õáw•HyÁó— Ðç÷:¿àërìñſ˻r:¶^ØÇ…„ï[‚M¤ì`£’¢¾q£%x»T–»Ñ¡ñæ…[£\ès1‘QÀ-^žµRÈÀçž®øeÓÿþNÜðcõ~)Œú½®·Å K ~Q(“ì!QÙeFx±0§GD ß„Þ{¨ÿ@s>æ¾±?ÅË/<ÞL¸q,÷øÓeŽŽ°ú—•Ôd´‰oåÝY :{Tàï 3è°f•2…p›C†ò§–Õ:a³Ú^'BNh—®&ÊŸ”ƒŸY£:—R~â¹(èÙ‡|¾0LýÏÿÜøÏÿÜøÏÿÜøÏÿÜøÏÿÜøÏÿÜøçFÏËØöB{%|Ç/¹RcB“EÓe×ÖN³SÎ5²MÅœ 7ï`ß°ª¼ .ê º]æmÖßr;ªñ×¢cîõöØ“ðÏÆ/?š­°zu ¯Z³4,Þ+æOÁíÁAÅW„ °8ÒøáâÍnp|XÜ÷•‹‡¦Ûîæ=wA—.M?ãTÈô]ý¡¥Ù ýÌ©—·sép.¢Q³€á /:tolWÐÁ?3©[ä±ÛòŽ{Æ5/ø–|‹;8ÈÅ•ÃÜäUåöh&ŸTü¨Ø,¼.©xéªg<ßæXg Õ!:ÜwtÐ#|âså:0áPL#{ ìµm™µ.¶ÆI*GL“=àkfã¤*®´¯¶­‰ü0ÏŒÌmU—7.¼ª4|1¯®×¤ n1*/Šlð‡avpq+^•ï{jÖdŽç‹Rû»‚Á~ï/±‰»š NSwö: †ÆÎwh·Zpám0OGrioÜ“+ôUpêC/•„Ô3&ÞºhãÙ´qǸÖèd[§­¤i:e‘™T8{Òwr’ƒçä羬1t„ÃǸ·Þ]³‡³wyâB(ø´ðކ¯Š<¡Ø²´ŸØbÊ–IÓ c Ig>Ñfáž52r_ ÌÑ#¦–Þ§`¯‡Ë¡êOU{·ºœ-§ÁСŽÈÐn ÜHMÑn9g ýoûž“´ÁdãØõ÷Qp6çá¯cßxp³Æ¿Úh …T¹i'hæ[I“wëƒó™Œk·p1®Í~ÙlPâ|¹'Ê_³qÿÆU‘‰o±€¹æ”¿šˆÔŒ,Ùœä„I<ë…ŠóAZ ÇÆb•“¬²ølÀÒ¾Omµµ!$y•Åú ðŠ´¹^ǃ1¯ân½bbÙÙT’NÌ' |ºfŒ—=÷9mk–…äæ â9J¼øìÔy#” €ñdßu&”%ÆÃXÆ2fîa0–êGÚND)’$J^B¤‘¨$©tˆ¬!âÖ¦Hu´Ê.KJG´•w>Æùãù×õJ¹JôÛYÅ$® äòªî ‰-¤} ye ƒfbÖ7\áU–¼ÙNŽ9¶M•Í·XòAL:0x« ŽEH_¸ÎÁ%šÃUö0¸¡ÛÎèBî–Ôø¿5€˜nø3¿–Y©ã2zÖÀj9æ,TÃǾ¢ðÒósÍ1VcÐr—Š©¤£ïÌÒ•aìœssk+ 5ƒKO\þF "†tXaLÙÑvÁR;Ì~4µr£û«¯WÀdÿmµ¡#f(r`2joñ4gmaÙèï^T§<]o*GÆ´ óÒ´vylѼ˜×³Vmc%Ö Ü fÞ{2»Ò:4“$ƒLàI&µ?Ú‚ »å{R§œaK¤æär ¼jÒiÆze î1¾;jhbT‰.÷4Ÿ_ïóvY¡xšâ£×;¥ÀøÆëåÏ 777777777777þ{ntÏÔÓ!ãöhõãõ3ËQo<æécÎ G橘2_Ü ÝYV·¶óPÕÐgŸ¯¹.^~çùҎ€¶d/–#8•HÑ^„jal㉈³føÆ¶åkRÉR€Ü¢n)pc¾÷*‡|À Ýï9˜»*cÓº‡¿¦cùBºBv2°àæo[líÔP ¶3ƹ»[I†_á7f<©I~#ŠÕÂBXÙ%ÿWÄð\æ%[µÌòYh)pc›mùØT^ÔºA%Ä*×Ènû@5Ç:]~oŠ:j¤®²ƒÕŠd¬ªvz13$? œÕA–èô3c•£ßâüá³jo‘”1>”ÐY†OÃÜufaíòù7ƒƒ’¨äÐ}0jÒþ™šÑŸŸÕ@áϲi÷,°í¼i½îW+œ4)Þÿ[N |eÛüê777777777777þ{nÄ´h• £ÎÊ„GN¿–ãjî´Cèg—lvBU 'þö=<\É^óÏÇt°EÑ™úÛ Üg^‡Š:ÂíBµ+ÙZØ¿QEg›Ç«rn] ê§“¬9ts¼ñ9áði= |ð|(3À¸ÇÍÔ xk!š}]š+Ú{)³,À¯pö0é¬+æG†*ù-²†,7ë¾Æ>VåŸ:®‹ïütÞnq ãuŠ…ÏHÁ*xð¼03{„‡jÒß$§ظ…5Ù};’on~ܽpϲÿH2A‰ñýô¡]4¹ö3Ö3ÕœTÊ O¸mí´µö+^aY|ÖÙZœê®‰‘ánQ´x•—îq”zùþ<¸ÜKÞ>²V¨Á›º¬}{ç….Ç0‘w’ÃüºF¿#oÇ´Õ^+&oå×3¯ì°×ò§çÇB8Ò=\¯¬ kÏüXwé~zs£ºsø'¦¯Â#ënl uÌÒÁžjmMVhk ä.A†íoØÁ4KÈï9™ÄÅç¿TUª¡ÒcSʰa™ž¤Û[Gs4ýQ:Ã…J¯gì9ʽ¹•VO…[blfß =rRª‰˜ D€Áöà;æ˜]•qq¶´³¹'QzBWÒ ~¬Ò¬èª¥caúõ ;È|ÏŽ0ù¡ º¥{RRèà“¡½.]W×-·p¸q¾åQ =˜ Ñ)~]­z z«Î¦Ž¥€§«敹@£ËÕßïs0ýéz»>I¦®ÜrMê›)Ä&sò¦¼ð…eá„é§ûmÔùn¼¯ùp-ê" ÎóoÏiƒÝL]9–‰lïÖ„(!LKo¿K·CYï–»·1’³SÚ÷뢘ïÎOÒʽ¹h–?µÿð&¹ª@Rè;­c ²ý¦¯³lÁð¯Ž÷ÌU²¸¾\l|ý1g8c³¯Õ\ÉÅäc›c„ø §ù&ˆB1„“ÑYΦùœhnðÖ?cЧ§©ˆL°þ=«Fƒe›X†OC`º<À´r Oc7ÛÙ ´mMæþnîÉy²«NÒÅzŒ¤ý•á€D¢˜T: )¹Oï%ñàÄËêãbn,øã•çÔo–)ØÜ|YLýÉÍÃtÓÙÜ ¥‹ºîRÃuÞy½ß~˜Áµm¬Là@@OÄ×I$äi\?š#ÅÀšÍn¿z2¦8Å_?¬ItçÇQrŸR%cMšrõ‡<Ö”ZRF…Qª-äaï!oºUçè{Ö)2¹”³¿Þ ±ü K.8CAË}³R(,gŸùZà;/ÝôÚ. ôT»å¢¦Í€·½B£™²LìL÷ˆfÞ’„àaaoó777777777777þ{nÈ$åZ{MªáÅ;iî-ÖC§}~4Ušå”‘ÞG/ððw¤Ä©X’Â÷ìÖÁ!fê±'ïÌ öƒ¤[ޤ#H¬qónР÷"oz,c µY湆KįsÊYbŽÍy9ù›J P”Ÿ(ÿd³+ðžmê”Õâá+Ãü‘|Ü|øåî·®p$ÿÚÍCq®x%§ŒRØÏ½îÒ®n|PÔ©Vk¸­‹»ëΈ…„P°ÔZŸ/pCìP½_Ñ[R¼»ÿgËF½¨A•×h 2“CÝ=ÄÁK]§™4&d×ú[ìÒŰ»–&vZ£<µîm…¬OÜ ºUð鯱!¸œ³Þ)ˆJÖ§'ÏrDpìuW@‚k¿k´Y!ŸZ'¶>SzÏS6û3qïMŸl#ˆœÚW;ÔûwV j;BÕ…†Ø M-ˆ´»Âú¬'«"gÕu Ü Ьú‘ÊÙ8g5_¢¿åTRyžòà4×ðu°_ËË„6KyZáýöΗ”l2äù>pÜ͆,ö»ÏÅù)Oæ…AßªŽ”gφkû›Ž¨Z›£R‰Ë+Q.¤ç­ »lÉÂìK«‹¹…T8S°µ¦®ÅÏ<Ø~&ë‹1JNYÊ©=2GËáC¥íÍ[×­ù`bQ—׈T\¥ã‚zæ…Tw;óKò×ÕómwšNÒ!æÂtÒä[!äÄý¾xBÈJ$è¥-Ñ‚îì©Ýûõ@íw½Óô2®úZ+½Kà†ÿÖ–l©f 7ÕŒÓãát?ìÐ1…èÄbúO{Jqíñöóœ™áõöZ“ðekCçRñ{½uÆ´!Ãcy°Ç!:äTÇÛkµ!¶t–ÛËÛáý{6ÓV!ÆÈ1<|Q÷ ¼µþWBU†.—ºÕëaä§oþž+U@”YÀì7†\ñugRm¡á³aU]›,&?ôóK(v†‰÷Ìj!sÔƒñõWÇ\àh¤™Su´!xßÙø®ö,Ÿì"¥¦ØC@ˆ,?KàY¥û¦ bZ ¶ò µ´¿§èþ–^^pá>Š=®«,}¹Ð<Þh×6jh€gm½ÿ]© 7ô¬‰¤áÝÔK I)<(_`nÉ^Ï‚q IÛAÁ¤øŠÜbB×¼óÏ=¢Np®õ¤ú‹N5tûØòiÔ üUO'áÀZý€øÚ¨k00¬;<­1Åéû')¡³ ’º‡¼_BÆ&a‰ê>1lZqþæÁ aÔ­¯ñÝ)p#þ3gKl˜#´Ö._ºÔžÄ‰†ÄŽÛƒçÏÖÎÕb.°/ á õ«4îÿÄÛ´Ì R6Aì¹& ¬é Yœ%Ë{C¿¿ÈúLL¯ Oß¿K<9”{I„„„„„„„„„„„„ÿ=7|RÃ[_¨¡ùñÐózØíû2,8q–sïFí·y”œ)vKáaòøMÉZœ8c3±ù¡èOÞù»^Ö>½8Ô3¬…WŦ„lx‚aÎ y,ëƪpQs —šK³¥ásié¨]Áë©au™‡Ù}Šæ«ùø[Ts$ÈÜ",äo¹¸bb}ðÃS·™ÀPì{öx5&^•¢}ÓÅ£'™NùK°ßD?ïWþ*PŒ)˜éíá¡^ÞM‹À 6ÖFh™Ý”¦~Ô£ ºuýû”©ª4NéITøéâëŠÒÖ!/k°*¼Ì¸ýÚ v,.Ô°]i…y¿‹HzÀªpk©jW2,>¸bDÁö+ºèA8ìGŸf»[¡ò¨‚ÒÝ8HHˆ\çÂÄs.{oçu13²î&¨à¥~ÒFÆìË%»YhÁQ{ß" iô“ûWÎkR ·†˜‘v\áXDì\‘/pƒ£ŠÂ7ÎõJû¾iÔÆÇáç¾Ñ|¬PÝUÅ?­™ –™JΦX‚òK•Öû1\ŒÙIŽOžq€×^«í)álxºd‹¤¡9ÎÍ<)|Á…³{o-ÙÈBç.Ù#Tør¾Y¹í¢=¶¨l¨4Æv;õöê¼ Æk¸¥l%ûm_Æ>c3²Í™1Œµ”¬%”_¤Ò]*”BJIHÙSˆÙB §¤DBI)-BÙ*Qâñ¼‹ûyç|>×õ½–u£DÆG>±›ÍÅZ~w§)S,y–ŒO\Ã3.R,€×Á*˜õœ1º<îË2É0#+ͧhȇ¿UòC+yXpUbN`÷IkˆMK•]¦­!½²+$ñºî‰Ð2Df¾õÿZËÀÇ‹9{ÔXl,gȼüßs#xsîm‹•‹é[­´~1î8]J?¼äÆ¡™Oa€VëƒbG^`¾¿»îìi2$*k¯s“æÃî´ÓJ¡²¸Î^y½Ó1~T|=&WBDaQ£W¶4¸ ×<ãÁ#vcŸž˜ÊÀöôæÕ. $ȬV+ÝíhOΜ<á¾ ƒxR¯·ÚœåyQó¯T,“—3jçÀÞžõI—_éAïèÄ´¥»_Ô΄[Á 畆Li€¨µ.¹!Õ/xbA <7·÷/íš§Á¬vˆÝ¿ü%Ÿðà©©¢‹.žÞ{äò!º4$&tTØ{ñ¼Ú¶d‹6XÈ„uä„ZÒOˆŒ LvóvqZè0¶ÕulVÄîü»Þ¶‹)‡y.Ïȼ À½Ø“m© @… D:A7LîúâlHÅݼ²²-铌wÿÒ<š!Æd÷ ªÈ!¥ª&OXR=XÓ#ùq[åá¨î8””u'ûGÚÀÃÓ¥óço@zÏLëb‡PrTÍE9 ³%ËNDiIË ‹é fÄ=½¨„ÿËØÒ•1GŒïs: :® ¬{|\^R†®¯}Ìuƒë× ®\7¸npÝàºÁuƒë× ®ÿ=7f6/ô˜ÔÉ¢,ñŠ[­¨žŒy”úq–1Ós0Fï/|Z˜ÂFµêIÞ¥|D~ìý[F‹ÃþÉ6PB\Ü%D@þó©ßÖQQ^Uâ*¬ ¥£-U“T¼5¥]ŠDüòù™‡ð&˜?q½þ8²}&ÆwÛ£šb¹Žš ô¸­xœoç€#â¢m¹tˆtüàènƒfê[µ4qß?4)¢ážš’ð%7'¾uÌïecü¸ú˜[²5V_©%TëAí~sr.×îÜ]¶>(LŽnØ!(`f!$‡§þ8‚àl#ñ7†ÕÙŸÃG€Ù»zðß98Æ˘’Y†ËŸèÛ&:ÈCÎË<`m§á&°y«´Kƒ„²T é¨_!±ÒáV'Ȇ†xã€E[Äÿ÷š,à®á¨þÙ¯D½ÇE0N›ãón@ þ>Êy9WÉ«\m££€B=ÒEÖˆA®ÈD½ó,ZUÆð¦avӛܦ!9X=¢{¹Maa’üæùoS^æ² bOÈ}—|kF?=c Ø5¶B·‰ !Ρoã,ñžøc›ì½:@ðqÛc…–3gþlé$á^í±•…/Qxc¬Ôhemc’÷ð?–‡hð\þñÁ†2ÆpªGe- F<ÎL¥‘6/‡ÔòÈ *°(*œeEê>5-2¡²j~ïX¦5“ãˆi}špoÿ·Kß$ÑÍeªYcÉ[QçU 쨑SØæËÆßä6=0ÓS‘7‚Ålñs‘ÍÓÓfßY½ý g'Aç³Ip>`ýp·FàZ¯ÎÝŠ42D©&5øò¡k~âjUU ¬KêÒ×9OBNø¶&þ‰hd>|ÅOL‡g"2xÐàÏÞù Ëv'ï¸H?žáx“uæà°©ÝQ°sî/üå3bý— ‘û©˜3"ØÉ¨æ@Î%§ŸÞt}è*V>‘ÂÀG}ŒèŽmVÐQs3ò˜"À·ç\bw!ôw‡ÚE ÌW‰å,õ]#aqßáN&IOýC>ËÿsŒ½K½ŽXiJCê>ÆDž%ïM?¨IdCkÏ›8¹Kø[Zu2ˆ ¯Ï•|xXNM bÔq[°øžõë~¡ªÌë—ÿj¤@½hôš/Ù èîž=~!DÅå•Ï0¨xç†VM»ê$£+;úë¨!†·)Æ'¨Ë!Q’'ÔQI—¥óü\a˹ Û}U—ÜÐßß—q0Ξ\øºÙÉjã©¶Aw¬ DjäɘԒ›ë(¿ÃùVaôLþþÞ5˜ž:–•§„Ò‘õ´…OðÑéô¤cj£°û‘gBP¸éVÞ®\7¸npÝàºÁuƒë× ®\7¸npÝøï¹á ^^ÿ2_ÏĈ5(Kiáí}fòÓ®sŒ+#:ÓlL-»º{"‹~ß4Pvý“>þy~–EûåßÉØÀýo‘:n¼·»(3„Š=|Uí(ª?tÊ_S‘Zx8ÏQU½Š$½àÑ Šˆ0GyΑ}Sí1PfEm° 4}¬ÌÔsÀiñ¡CtøúÍ7áª=xÍ“êm7j"án–uð%´1#9÷_X;L_ßO}ÁÆCìÛwÕk¬Ñ—hµ¢m%Jvæ]މd`ûÝYù¨:¤Ñ‹‚)˜rñ¸Ôqe3(Šö éPs‚È“«ÝUixáøÙ£mNàãtVû]¢Pxë¥//òãêZóˆ“$yüôDiä »ß¯‘¸ì¥·«[R‚¥éX¼ƒ´%i ®wºÞ\iˆ‡E{ö ±Àì^ŸE˜¿Tj‡3;EptO™¾Q«„v8Ú…Ö2 V}­P\rcêÕ¶Q18£°Žr<‚€Z=íWý|iÈY´ù,"Ô;¢êïþ˜Â¨É팡=LddÈI+¿eª4/oj—5xdeï17ÆŸRÓ*™p+"TûýuK$ öHöÒå[zËZý¬Ð rØûHÈãN[¾ã1þš˜£­+hŒ½%½Õ³€Z#ìêN2þH–•X0‡ÞÚÏ÷J0ér8å*.jêèláÃÍZz®·˜ÐŸÃáq½f ?ÿ8òÓâ5áO@zœW·Z>"m.å@¦ž³ó|)/tåúC$ó¦¤êT}÷앳l ã{Ô2µuœp¥²½sÉ©¾‰ÛË x"©•àÅé§²÷sÉp &©£«œ/9ÊYõk[àе_ÞV“P*Ä0|”Bħ?5^¦’†Ò>Áù|ve¯»·  ¸Kthž>L‚*ñ Osèš&8zŠ¡‡kÀù*¡%×NÏY²š©¨N¼Ú5“ÅÃJ–K¦>ˆÛí7ªJ``B˜¤º§¬(rõT¸½÷×çÁ“DhxûýL[?èj ËÛnÑá¦Kr˧&œe_ù~z; ¹g˜2RtÑ‚oƒÍ¸¨4($™@¶e‹ÆOé$³¡y1 ºó¬%ø®]V>$L†ŒO>SÒè ·Ó_ꡌ-Ô$êÆˆÉ#¿/™Å,¥À°l-g®€ ¡ýó¥u’xç›æfâÒ.çÇuM'æÆ5±+?’ðÙÊ Qm9\•¬å #ŽG³V ´‹ò£_ð±-¬£|gRAN¶fxòpPZ¯Ý¼ÉsÑ úH’fe øv„Å# )†¿t6ˆÛFÔÐQ»¯[•&üTNý±²¢ÿÒQê~ Ë.R\›Ì2Œ¹npÝàºÁuƒë× ®\7¸npÝàºÁuã¿çÆÉ¶µ×NÊbеÏ"odµUy+°nŽ!xàéݺq6Ö+öï{YÀÆý{~Ÿiä!àDZ8c:’‘ÏMk×ÊÛ€¶B k£7­]ø6ÆR±J ÄìØ˜2Hú+Ýjj¡¢Ž|ÉCï=tŽåÙìîb-¬OkùÙøž%*y¯Âש>ص8m ÉÒ„„vp SÚD‡³Qìüì¡|õ«Çg4±y›À]!ܦ_§ïq~ ¥‹èw±ñ(¡ úÖë -'uA˜ßš8íÍÀe’ï2®Ða~™²Ã‚¢æ†<óõ\n6³<YN ùN똲 ŸÓ⛊*œ Ö.€‡y]̪ÿˆ¿øÂ¯«Œ©ë¤å¡ïÛ‚…4ܶž]m£ÙYL{›¿4̪¾áyŒr×vÅí%á»ðOæq,ØpèíbÍ>58WxkQ³JßuÏÞ÷­UƒÌÔ÷åÊÈÀ¢ygCm¼w­ž®ƒsçºz»¼(ìæíUéOÃë_Y)å¡Öó£ém ¼¼f_íd¢¢ß‰mÁ/Xàt"%ÚgÁ^Æ¿œ.æ5F‰Üâ¾OLˆ7y¿þ™%3bl¾Xé€ëñgiž,+œ ñyM—‘ðÒÀꙌÆØôlµ˜/1Qà¿ø·‰Š/·z±ªÝ£SiØ´ü<ÎÙg ÊÍä”SùR¾ÞèÊñczÔPþ•¿L°ý w¬áïåb3M¸l‘À¼*R:Ù–×9P¦•02YÌ@‘"™çÉgÙø.¤­ýn®xÌ—áÿÀFÅè›$óà ƒ®Æ×ÆKnÌùý›óâ S†òUÉóè*Ô4($ƒ]Êí±ð>0¤7‘,°VÝ7-¿„™ê­¿tQ¡D&|"M’Tª'³xPW¯åÒšii‹j^äG¥+Çó|Í!( ÌNo@ wyÏ¢*õÉû*¨xžWÈÍ%–*&£;ªÇõÁr~N6ñ(ºI~%8ZÁ¶§?Ed Òƒ/aoÅßÚˆ ä~N‰>ZD;Yí“ú™ð¯çr«%?ªh…K/ÓÅH¤ ¿¤àög•®Í‚Dœ£4§°Ï°!LýK~‘%HÖ‰FºUBö7Ñ.Ï(:ü^KU°…ųăòXó{õR2”*fÀ¸ß“@×R¸oáT‘jSÄÂ|+ôÆ?ÜWùtß#aZ…g‚Q5ÛŽ<|Hǹú{/_ñaˆ~E±÷Òn×¶s²làTüæ¡Ï‘úýjkui¼l¾?ØZ«Æ¸QûkQqt?ü;O оFõÂçl”ðóa¦„ RÀb»ëÑñtÜÓzðT®†7­í9¬Èuƒë× ®\7¸npÝàºÁuƒë× ®ÿ=7¬[‹Ÿî’Å/½ë啵° ×ÿk3ù7ÃÃ1çˆÂ(ú‡ KØØd/0¹jèpDñŽ×f <¶Z–¤ l…έ$'`w¡!{{Zô懛•$#åq³šŠ2?¦v¸é#;ÕTfý:xñCƒ}sÞ¥“ ä ºìqÁk—ØÈ+c¸Ÿ$TÊúi}“ËkÛVÓá¾qÒ{ÿ@{È|¯ÚªÓ¦‰ß”Í;O›©¢õ‰†íYYkàÌŸø0ê36nˆþ6.ÈÀ)—fµ·º°A`·‹ÆÀ:åM¢ùèà+3?ù5P’IìÝ9J!'³Ãœ '¸Bé»±}Ôožû?öêôê…‹¸¬·º²Œe²5#ÃŒmŒeÆ6~gF”J%´YR„ÜÂSWQ)"*dIE)‘¢t"%•v*Iˆ$)KÔãϸ/æíys΋ï÷s†³“œ!O>€rí!rmäBï<ÃËû–K“d¸.9ÅÜÑÀFÝ;2ç¯ékBdc ý@/£6gœ β…̬øÈÖ»x>¥ÈûŸZ[°êeGÌO €ºI›dyÒ¼ÓV~¡gÞ xÿ¾‘ø"ß\eHSÁÆ-$Ñ©•2@[úªÄŒŠ‘Ze£¶²Qñ ÕhÆ–Åp¢/Z”̆:¿øƒ~Ûx¨*y`Ã}[ Ù—2 ¨(V¨¯ýfŒÚ9ÿ$lv&íuûaÏe_þÊ¥è€ÿ‡¾ÁT.v–®÷‹=k€E3+|?›à1•;u–m7ˆ°§FŸÙÓ:íH01œùÊ1ê±%,¸ÑâjC…#ÄL ¥Š ~9¾ïØ!b¸H®`Ök†ñfUêšm Ú7å¼Å‡¥PIý_¯}’"Ê6; àIag©R!üW¤Ú€R>žMIi}~ÔÖ »ÞÎ|ÈǼ8K;Ÿ×]ÄßZ«—ÿº´<ŸÅÆçJµ“2ÅŽRaii™kyñÜ^•׫׉a& ÁÌ G>:õ6öàêUž«¦cVÿu­Ž2€&·…GDæúã.þá­Ðnœä€¤ÝŽç~[-AÅN<¿š-‹æ)—5bø° œ YsÚ¢ïþüØ&€žþÖcÚ+õANÂ+#Œ¾«ågÅxýs_@–IxNÃ]H{«tĦ°à[ƒÙ~2Þ1^„któÀbi•Ž–8ºÕÓ'§ZéØLϳ•zG‚ØáŒeCtmµòٟ‡žÑ”-k¬á`]}î商QûfO‚7|Y• ê–Øƒ ‰MZ3²ç;~ 'DzàÅÿÜ‚ËØÖ¥~ÚÛ‡„ o=c.í6FßKŠz-_ˆ×ÜÀ§ 0h1­\ŸŒçB‰Ù\YLɈù+&G¤X½f°c§k²ï;8‘cüñÚ|=8dF™WÄ…×K6¶lÕÀÁ÷²Ù²(ÿ†×TqŠþ +šçSÔ®Ù¦¸é fÖĹtgs0“©–¾§I T_}ª º!tCè†Ð ¡B7„nݺ!tCè†Ðÿžü‡¦]•1þ²ýD¸& CÉwî\<;MŒ>šÞìÑÇG)âÉ´åu>ÎOÛçwÏŠ‚/SMÚ² Öã:¨vÐé³¶´„ŠÖòIŸÜçœ(Ö½©^zA"¤ó˜ÙÆèA·téaŒѣ㲠œº(©õãv˜­>Îß=戼ÑÍ &°êáñÀ㯱ð¨_»‡ò\ßïÆÖ—8Âç˜lõÓ"4¬¶íËð5¥ ýšì–¦tXôÉðùÙ|œÇp¿À"аB'Úµ’Úñ'Ò†ä ÎSŽþÄ#_+ô”F4Q'~óðP+öt›vI&:ƒû³/n¦ÏÌææ?qègØ~(qçä0IoÇjÄÐs§RRH+ÃÝl\!á8xGY¦ývñô°±4?òýÖ*[`¯·èdžj¼Ì{ÜŸo 156¹Ø_åk»{º;Xb_<Ö ³UWxj·e`¾r %Xko.ˆÅ¨P³Úwî¾éÈÃÑÆ˜<ÑŸ¾p¹š•·]Õ‡[z¶ë…HÍÔË×å‚ËSéÄ.@‘OzmÔˆ¸å'j³Àðz{ÁÇHT•U?Òǃ›)ÝÛƒfÅñnï›4³Q:f|p7»O‚{Å{ÒçñRº°*5•åuIª­a÷U·ŠÊQð£e9pàJ›àƒÍ"×ÙžñPÁ¸y/Úw±€ôz¸y¤†/™)ò$Œ+øvZñˆ1: ÒÞýûsˆh&Ô/Þõ7ÀÓ­òæCL2†ÐŽ5û9Ê¢xbÓ­Ql>i5ñ%Jûš3MôJíÀ7Q£ŽÈÒ‘¢¿¾g¸rÁCÕã,ÕP'½´rhr˜¼gç/»b|OŸæVüQEÙ?IfG/ôM5ç÷v>³S ’T½~kô™Ýº!tCè†Ð ¡B7„nݺ!tCèÆÎ í”’ ×èÊÈϾõµG—†%ñaão-~Ö:ÞÕu]|Ìz£–ÚÛÈǽ9GBn¯]‚ñçïZUf³˜úº­:vðCÃrûÈ'*nw’ïh|cŒ£ñûûìRÓƒMõüýÆXÓØ¬Æ«ÔÇÔ©¦)9§e°¶¯r¦ÿ“ö¨¿¸N[†²2¬ÆÇ¹&pU}YƒK±#Ê‹—ªèaCÇŽ¿Ÿ©ívݲæ&† ¦¿íXBE-ïëá-©.ïþ² ùè7ñðâ.éo.¤T£ƒAÝ£&÷·68øðúT޲9®¬bÓVM<®ÛÎ>u·…øäßu†Š´ÏÙáW̰klõï6ÎàËhåRƒÆy5¢"C %^nÝPJ†ºZ‰aŽù§p¹STøà}P»ä<77ûžïè´…¯">þ1@%÷5Ï&¢mÁõ¸Ã½ðF ´£ýÚ>ûxºü´Íúp D—&U‡>&FÎÝY+ASAî.wÂQV¥ŽÐ :('ÿJ.g'ÇöÞKº½¸Õ,óWb8qàTɬ÷]ß§Q` wÈ¢–'¯ðtMtö¹Vc²®m«óæ‚Sþ†™õ\¬MòÖ}¯ Ý<Åø­ñO¹žôÝ@¼õíÒ¥_&¸o@޵ŽôŒXüüÛK'p¾÷ß^ÈÄcú¢Gq–0¿1G,l52Ü–ý{«… _+Vê²Åq2l(¡`’äžFnc6°»g•ÒY¹¥ð(H¼$½Gfʯ(ÀÆ]êžZ§LMô~=ÎÇnEd9#Xø-olò8 Övè^V»›,±‹4º~m,÷¾ØxùÌš€5TPx`´™p3ý§Ìi5q<™:gºÀ •»‡˜´ ‘rÔE噎’'"Â&€lö¨Ëf^rè¶ÈR€¶Ð]óúõÁ¨Ñ.)3Ü“›:eѵ±kôÞ)œîò¯ñ5Fνbê=j—Mù "È.³ $06[Ã=Y† %¸Ý»a ðôùÞ½ eta苹ÄUôú–¥oျ…uð Z^«D ITÿ_«ÿRúXŽF H°U©½qC.k̸ª©|øß¦Üµ¥ý֠Ó4÷2€Õxѳjàr´÷ØC•ߨpû„ ~ªÊ*ÞéÊ÷óó×m"ÀÙîÓ›¡€ L,‚:çþð"ÿö4­mŸ åÃV¥ÎÚ8ò^‘”cJF¯&×õUÅD³Y™=,H¬ìÂhÂ%íV>2,oí÷‰Õ@ãç®Ä)#ðâËÑÒôì ´>!'§‰:³±ÌqcäéØ6¤-W‡¶_GO÷n5ÆÉ§3g¶:`$«zä Ã2ˆðc|í²Ãm饎ËpáÖN±Ù8¸_!¾U#ÑCã–øÞdÝì¥÷=Žáym,-–†¾²K«¨h¯¡WâÑ+¸ÖÍå09ìv}{*3´¯Ò¡6ðÓ£Â_„JOø[˜ƒSJRÈÎ*Mlw,yr-†7Ey§†œÁ{©ÂÃEÉf(ðÌ8£æ ¡3ÑA탘ö`w”ÆlúÙÎ:I†¿ïvFÜàQd½ïÎ5™>•?ÌFï"§à•¶09ûL#ÀÙOºý<%ÐŒÎê¤Í¾¥@HЭ‹O´ ÅòÌúD/ Ì“x#brë)±£lÕ¿ñZ*(»-÷Xœƒ |òáG¤_§`·l¯ÎÝl̵P÷W\ &^®ÑßmçÜÏØ´|-å6Ýå?I·…?ö$Ttà&™ómªÞ¹vP]xÀ8'7ˆs‡ ^¬SÀöÊßÄÀ©ÀJi›‡#ŽE5®™|D‚— ‹¦É¯e¡É¢mÝö|@³Á™ÔLíE. ®J)Ó&‚–vBZð45,ú`éáãISnã²ÑÍÿò¾¬}ýð+ SÝnð5a±WdRç!"6î^ø,{3 ·‡Žž›­b 5©¯Fâ¡ú€ü0׋½–±-yé. w£½¦p*@ÝýÉY8xåÕÄy.U^àwF9¢ÜÈ—ÓðšÓ¸éG#û2 +]mŽyx¦ª=®w÷ç•FSª;§¢±%«ßÒpOœ…/ÛÛ Ä}‡-Hp°+o½ C=ŒÙ¢ˆ¦ªq¿ñaé»7*·tx@øpª(6š ûGÓ[Ÿ'à.Q~Za™Xã¾]§wñP¸©}¶ÄÚOŸðVL´·…¶ß"ær!N¾Ž²ÖþÜ[÷>BóÑ”ORXøPQš\xiCqu© ö“—óyD‡·'xš—Q•qó†•ß<´¶»­mîGÃlóSQךã±Ô ;¦Òôáé°ÿI[9ÔV!:FEèÁ`"Mùl² u¾îr‚bÅÇ»>hã÷㿨>A·»™fŽËçÎUÿÞ Æ#z §Ð€_ÇÌÔ^ÁíËýÆT?pàšd“¿ÀÍwÆb+hÖ7q°mêL Ûvñò3Š¿óÁîjšoN§2–oß~z•%žYên§µUúòļµ@³Pã¿ò…@jþªPwòˆÓªr*äÇ£¤>ؤÜbÉv…È£)šsŒð×§-Ÿ ,í ³Ž½rÏSÔM¯¦¶Ôëao|6é52pQß‹÷k—¼ãmù¢pyÙ#*JÞmj³p2À·“COº£´PÚ©¨zBcxçø[Dw›øúxT>ͱ„3ŒÀOåÀÀ*ÍV%7ˆô3íGtЩ˜´¯}ÒöZ¶*Û¿{ž½»ÇÓ濨­Rg£ÏKmÑT¨—¼“$Ëܹ!sCæ†Ì ™27dnÈܹ!sCæÆÏ ³Û*MŠÏf`2[As¯“–P¨o×Lð–®9 Hh¢èFŸæ?]Bܲ|¤”ia‚;«½îSïØBpøbP`  îñ´ðQ?2V7§7‘˜Øó&§¤º TÜnU+šÇ@õŸß½%4´÷vº«Ã—Àªs§Ú|6Ðþ0U‚&,FŸ­`‚øùþrM?1N{ë=i“À‚dAñÄN1äøÛ ÉoÍð‡üè!¯N2úÇ3.gyB^³­êy!*÷4‹Úx(®ØmÄe[Àûa1ìŒâ"eg…Æ•64ÞQñSÌ"£ÛïÓAŸ½á¯êVӤƳKB07îç£ë]î0°¾M#`üÅU›šýÐQñ§6}±sìW¨†;âêÍr[ñ& ¦Of=äÅB"–¤ŽÎ@àõêø 6˜p-d—›‹ ì.a<ÎS ÁÞã´ÑªxµwÝ‘góˆôð½\8§•xøÝÉ7œ/~šcÀÓ„3žh1DdK?R¶²p IZN Bõ͇CÜóŽ=½q’ìÁÇOÊ-­ Û\ ôî»/!&ãß·'Ø•3Ð"'†Ì°æC"ý™ØäŒ {¬Û™Ù0NÔ¾^ÊAòî~ö2¦ ґƋԴGÓs‡Jn'o'—ýûç5GЯßä×ÛBÃX¦Ý8¶X”+|ÖÖ'A£HO⛇¤fF%Ô[˜ujë>x<óÈ•’ypàbáW¦%g-ظ×ÇSŸ2KEð±7×â@7ów~}'üãsµ™!Ù^v]L"¸ ÑZ™Å‰(øXgâ›Gn‘µÕošî7üg”÷XÍ$Á Íçfuít°ýåwâ·"þžñ {EÀ<ÜQR«ö"‚†K]ô ss Õ¼"Ò‘ϸ–Ë,9Lú¼,`¡Ÿ¬‰«Û$S¡yv”¡Yº8ÛÌxk•¯,µ¨+]E W”ÖmÆ@ëDÓÓ Üàe{„zÏ2D¤4Zí^ÌC“¾å/9Àmõ¾xÂà̃¤4Éj+˜ßµøûàŸÞ×xþ½_`æekþÿÓ¼Ÿ;o[B… vž8¿/$Ù¯Ýrî—[¥ Ìäª}s<,ð a[Ć£B莤ú§Ìr†‡TiÀÄûý M° }6§ÙØÙv?ˆ½F1ƺå¾ëfͰƒÈÓ£:_;þ¿‹ÝZ©®ë„÷Ú1¶j©ß°o?/É R}s?7ÜÍ<Ä1@æ†ì‰à-Zhë7å·ëy$0¶+mŒŽRûPO§Æ+(I·k-À5© …òMnh¡J¤":¾Y?J?ôعÓeÀò°šaRŒÎM´)ìs²ƒ§e‘ä?n|XØ·ý‹¼nÒX®¹Fæ†Ì ™27dnÈܹ!sCæ†Ì ™27þ{nhhUÌ.柮%.>ȆÝ„æåqd,ùZRÅr„­×–]üè%…¶ýAÅX¶pAŒU½;Töø,•#À"Õ®0²³n4ëÝÍ1€‹Ïþ¬JqÄL©Á7ã$¨º:¹€éÈBA—¨ÅŠ#‹Éâò“ï¨(¯4Û$ÅÆ<¿«È5 þuÁRõfUþ8ùfµ%|Ö¹ÌwŸÚÆ{>çlÅw²!Î×¹½¾’« ÇÚ>Eë,$bŠ<›¹…£ÅÚñÁ¾† þB~úÏ~Gˆ[–3Ý•öîˆNZï)ýG=íŠ)H:ÌÀ¸øŒ«>¼zlÏúàŒo§ke­Ž¥À4]‘Äσ_šŒL®mp ‘ÓÚ¤o}AGZk_ó‚»ïGlïp„ñÀaqÞUŽýÐOT1u‚·…ò$(ÉZ>@‡"zP’ÑZ%TYW\›÷‰{DµQ ´SÊzH‚ âÎSyáF˜•K?&‚˜ç‚6óÐToâKU +:¼±g·†-4’,¹ªt!fu?“Rª>ש:maÿ;,yÏ£WÜXÏjæ¯ùãmùÚçžß^Ñá`áZµ³^JøÜiiGbÐ<Œ˜SÕŸJÃy¢bãÌÙæÈ]û)=HÆ´ì{O«Ê¡A™C¯3W6×Ï· ¦‚HJ{j‘å#[Ìë;µ‘rÛŠñJM —f‚nMg ¥íáÏGWÜ éż™W®Òà¾ycA‚M§ y<àÀ¦ûU#ËhDÿØmÉV°j_¡²‰-T'm¢ké8BÛÚÖ[%S\ CQ`²Ck«r ÜJ-qûTŒ¯‡.üX9#t+ݵ 'î aí™Ýl;gÙP¼oÇ5°})×\`{°sß ß¬öXõ·d£Ã½)‹¦ØÁ1›ëE–=<8Ò×Û¸j¹>v¸R:^3pøö²ßaŽ}˜†¸ÀÚÕ»¼ÿ•DWkgïMa`]ÉØH™cz!¥÷u¸X1ò¡ ßa¨9›ƒw&ïR·Á¿ö1¶ÇgW6­WUxËË=»¨wÛ6Œ2%[Ñð °üB‚‚¨ÞQPí$Bçó¯ô>Ñ!Y8ª>•ÐÞé>eË0î­2N¾$àA’ïÙÆ“q$hÝûü䤟1ZœZ#¯PÃùžÁëyxf]ßKöVWTïJ;9D‡ÜàâÅF3„Øs”:òd´îÉŒ¹¯o IA7ÏÀ-¾ó/÷£þšó‹œé£Cxµ³ÿ„Чù÷‡ÍÃ&Z~lM1 ëÔ¿¡`Ý Ó^Êv}˜Ï=”ãÜÿ»® ßk›ÔL2Ž¿½ô— 5”׺y‡@íÐ¥·Uf:˜æ™q­ÓR k™¿î}þf‡ƒ±VïóÝÀ<÷t½ï§fWuˆxHÖú¤Öp`[¸éžÜ€ß'Ìž”YÁ¥WZÛ·Ú‚|5•-dÁ¯ÈXÝ1e˜uT2÷T˜ÞKyb%m°Ä:æfõÙêÂî9Ÿm T7‰V˜uL}ÿc¯¾ÿ¡püŽÛñ!{ž½gœuÎqîÍÝ9»EF‘†–’hJ‰R!ÉȪ”DKhå­$3J’MD)R²¾þŒï÷¼÷ãýx¼žo•Á`R¡)ç#×êóúPÿhª—šB‚uyUb,&¸<1ÚÓÊ«ˆ7U#¾Á£¬Âb`Œ‡½Åe”HákCï§š wÑÉ©¼_hN‹¥Ck 0"}Õ4ÅV¯žyVaš(ŠMÒÏ/q൵^æ6¡,PX(=Z5E‡-¡¬X“mБ_ÊŒ»e ùTKYo|Ñ{%&|F¥b‹Þþðý®4ýXO÷D'×*ˆCèDÅ›µd<ÞÐÄcÎ ŒÛ…JÂ&l7Øn°Ý`»ÁvƒíÛ ¶l7Øn°Ý`»ñÿçÆdúpåÞhi¬¡45:jâu>N¥’õ¡_JÃsέðJ8ÏÀ¥VÞš~¢«HÁ2÷–Â"•>+¸B…ZÔPëðͧ=¦áÉ{”çªÔuÊ.˜5Æ5Ç,¿a›ùøƒÓfŽüõþ.ÛGtü0€ ‰ÃŽ(.J“4…÷|‚Ä ÝGåè.b$-¾¢s Ñ u¸ÈÁg´Ð³&¶Cò¦žeô—ÍœqƒºÉqï΢ÖÏ@cÀ`{%GéNmˆhêL;ÆaƒÜ¾ƒ[–»s=&Òo¯†w÷})É›'½­}Nº ˆ8¹Dˆšc’bÿ­K'œàÆÄX˜Ê€ä»h\ü¹ÿffpÈ‚ÆêòìJ´Àôi>“âåNŽºíy3¸dŽÉ¹B;/üIcñ²,|®iûÕžÃJ癆 UðcùnJ<'€\‹ý×9U %KîBWÍn‘ðA…€1£ «ŠrÊ]¡‚ù×â(ÅÑ$lŽø-ý5—Õß³Îól&ÃøÂ^²-’¤ݺÜí G_5H- N­Ÿ ‡L0ÀãIlß@£ÓLù K¤æ<(Zp´QŒã÷ÒroÏþž1ûk€¸¸7ø«›¡§RÅ 9Í/4?oåúU:d(›Êß®kˆõúó 5b ßÛœnžü¨D8b÷ï¯Ȭܴ¥B„—º·,PÇmáò¯:B„; ,~{c¯® F«öêQìÑo‹vÁh& ´×qŒî¤aîÖB»˜&z^Øÿ¨ÆT¥‚‹kæé8ç"}ÇüÏ¿J‘`Âcße7ŠÿŒÏ´¬yÞõü“ $­=¾pyÙ™zMÅ«[fxñóÇ!!Å +Lº,MÞZfˆwfƒú®káÁÌ%ˆ¾"Dš.O¼Xª<Çzá=-( ÆEVÓ0¨É•&fQ ჰ÷ßâ(±¡¹>ÅÁ4rªTûŒQ¬÷6•yÂÞuOMv†Áo¹Äõ€†{ïk7)XCŸ_’°ò€?³¢_êÁSa{×á$m£qøW¼mó×óXiþ«ùòi‚8?Ý’Ä;¤‹ÚC½#²XžUGœÒÆ}-§bÜ®3 C5Þ8΋ ó­µÓ « ÓÇ÷—…‘@Õt2sƒ äº*Î}ܯˆnÑ´kíDpø§üfŠÒ+–„¹¥ñ{ ­¼›Ë«êþË;ùm69ÿ†õVÃK_o|³“.‹ç[ã2Eñïý¿‡j8ðÞÍŠÝÁ,8q­T£t‘º%µF=!Ú0ÿÅ}Kò9k89´žT»™^^;â3…¤pã–üu/øOç}\¿<ÞùAH™'BݺȆ…]d´ ìmïàŸÄõ”íRl7Øn°Ý`»ÁvƒíÛ ¶l7Øn°Ý`»ñÿ熤ѡ†»¥1„:˜õÛMUnÕP7ˆ,Ò„³(GõŸ10QöêD9/QîºÌ-Òë})IîD¨Š©XÙiK‡,¾€þÙI5”P;ú_ø)SÜ7TjÒ}BĦgÒ31'WÆzÊŒˆVNæéýÆŽðÓô_³Ì:FÔH^'溮^½dkövkÜîc!¿ôÜ·ž sPòø}0-Ål»󇊵ðYÑ´ìjsML“ â¸ã²»¿žJÊ``ÈSJùKàˆØà*jCÒPUaqË\£w‰LA]{ù‰®»:öäp? ÖôÛ__î1YZ%±³fRµ¡DÒÏ Žmívå €ÝíVÍÆß\XÃE¥ô}–Ò{ôù ð¼lus»*¸å6\ú:dŽUôÔêêh:<ó´ˆ?n€9I:4óQ[ðˆÞæßê¥ ‚£6u]ûP‚³ãˆN—2P ëVßí¢-šïe´.»Ñ^ÐÔg*;Ê=÷¯SÆAZHg|, /4|ÜŬ&€àÉÍòZid* s5³Å²†9ž­övЬš@'Múj»Ë‡¿¿u“ð~ý¯ šÙò3¥Õ´`qÈÇÿzN gtÕ à¿ú§tÍPÏþíAcÁZè©ÑõÎdØR×#tÈcÓoGno°ͳáí /U`›‰§€7ìÆ›¶ÞuçCƒ™À¼c¶°3©^xa ¢b×S—}é>—f¤¥¦„sº¹–ÝÈÒ^<àKÃ#~»œ ™˜å8é.–k†.RvCtT .n#’„•º‹‚8*~æÇ7.=|+Ú¢<Ã-Ïw4§¾iÓÆ%ÁïÖä0à×z$yH!ñSÍzP5šó/Η~ow _˄Ԇ+ Š8•ÇÙö¸}"çhàFöàqÆ®­³uâ¦x$t_kò3í΂¹íŸ>^±åwgÊ¢ßÖ(ãñ[¢ødcoß%(¢bGCg^?¶”¾æÖ†Û†ÅóAÖP@¸RLÙÁ‚ò’E©Ñ()T0oò 2Ü££¾ücÌñÝôõÅ8Ã{<ý#ɘ@lñ"çÙ‘º³&l7Øn°Ý`»ÁvƒíÛ ¶l7Øn°Ý`»ñç†×œU„©‹4NžM$¹k¢Àó¯çÃi%f¡ªÒÅ ä7ÛXxD„‰2Ú¾m—Ãìî –Dè+kR¹Ï¤CéµzŒ:Nq½Úñ%ݯiœ½/ë£+=­N5cÏ#ÊÚ•DÜÕÄ0t„±¡Áàët|*Rà#­å„^[ÙŒ˜€ªÒ3•6daèõnûzsÈ”z]–áGÝì ㇵpåÞö!ŽMTï•>í­z=É ¤&°n«{*)-]Îц=†r|º©èøª²Òq¥%ÜöǾ0y5Ì÷ʼóã% >ï×>[ÛçÛ*ûJöša,ÝÇKÈÚ <ä|úòU ñþT’sò/¼.Q-CÇÏ1þY`ŒÈ˜¢–›*ð}Úõ.·Öw¾3yEΦäka¯¼ ~Zít«-¬¢ J&©ÂÊšOº®xÑ»ê½h±2¬¾½ú­BZçFæ¯9ev¿L›È5Ý£´÷‘ÊÈ­ýá93Ž„v©¯Öæ#@¿êRǾ÷dèúÆih‹ Ìø: ;¸°j¨m2@â‘<û§^&¸Mbæ]B™Çÿ˜öiä¾ÀΫê“K J­°—³ÉkE‹â÷ÓÎYD3Üÿ 'ö–Á7ÚÉŠ—O½ál?YFÃÏ·¶XíO` ûC’šï©@”ͪ*{"ÜÙýãÑÝ›|èðX°øâW[H½g)ýá :‡¾®K>¥rÞVŠ(#žpÈ'¥³ 4Ôyp£; !Ïã@=Ûr4‰0‚É_›ù.×Ó1ªœH/‰áÄãÝg\ËnÜXŠþð;¯3V¬ê ÌNX¡»h½âÇvžçÃø…u]/­ðiùFä˜!^ZÓèj¨¡…^œSÛ.5IAYÈ0k{èR%M_ìyi‡µk5w~гÿF(â« Ç´ëQêÑ ý¬íDó#cô’岩gÚƒãéÙfuy"¼~P×èeJÃ+Ï;¾Ÿ¶†_½Ú—ødŠ/èl¨^j ?~ë™o¯$Áóì[;?ÉØA8á‰ë¥6!¤Î~ò=¬®‡)VöC!câp$pCÞß'Ú*u¾×öv3Ëße§BøÑÆ‚ô@ôÖ¦Õ7›rŸÙ—o`™ròˆƒ¹RòâƒSïáªýÑ÷9q6-iÒ¸¡úÜT¬º)vKnjÿÒ2@û§Ço%פýJ‡Ä-X²øï™î†"Q^¬YåŽÉ?]kYp€C!¾[‚í[§o­½¦µßg%¬³†ˆ¹AÁá½,ˆ´n{¾B`¹ëog•cL€ù‰6ã\KÀ¸“û_޾#½è3.ã©dL¶©O[Å®þµBsÝl7Øn°Ý`»ÁvƒíÛ ¶l7Øn°Ý`»ñÿçÆ$õ;‚ñòܯÉ";oMäü.êûâý"ÍêgÆ{·{ <—å'ËÄÙûüŒ©'²¨Ýzçfëò³ŠÒHÁŽtèOñi^g¡Ž’+ ®§šâÙ3v¼V `sR)é‰1Æ(b­qOý:ï®ïy¿¸•RéØâå&à wæðí‰oçé›6'Ýdá¶6y/B¾9øLH â5ð/™Ï¸#¦I¤«:7k¡ÍDÅ­Snаªk¦$ŽÂÌŸpíŒ[ \{é¬6 s’¯'%RqØX¢ÑLϺ*{ÔòV¨!S{Nc$Nb4ENW(éÓ(Ë|c†)ÉéŸ+äÀzn³ã'›å^ -R®ÕráÙ‘Ýâíy2@–É—&#—üw+U¸Ò¹nwó}s|qdKeO ¢ÇJI¥–8DÝm¢ñÜú$s¿?‹R¤?¿.  evr—ÐeX›ÓnäßGóÉç²\vc|íÉ98Ð~u%“¢Œ?MxIÈMÏï[!Ín݃Ëû§‡ÈèÚ✚“©kWät¥^Ýè) …U9› ß ‘Ž2¡/Áü›ólpŽoÞÒ}\ÂÝÇWŤY¡çîÈ» ø››ù“d†£Ìð"ÿí¥Ç#Ýd2í/œRt6Ķ!‘‰$K¨žÌæÒ¸¢ç¼Zî$óí©­¯†ùP)xk˜þ°-\©ê½+Mƒãóö½3W—gŽcÊèG~ålu…g59Þ…9Ò° <]5u‰L¿Õí½Œ`°ðÛ+b1ëÏ|ýqð 7Ê¢ý½°ì†FÆÿØ«ÓF¨0 ಷ¸dߘÈÈŒÁë^©q·Pà “ÎÍÓð©ZãèE [Hú½]¿#™ƒ¿iÙMÁ{ìP1äÉÞ:PB¿*†Æs±'f¿SaœX‰yÕž,²Ç’~×ö±5P'ÓäZ\ùF”#òÎè·Dƒ¡þèáH@–!3ÿ€üʧ4CºAñJu®x G×N©â:ïÛÛ“ðx¾âtðh¾º#X¨RGýC¿l!¿¿FiÕNy0yÓD «æE9‘ÈΖDHœ¦•m£3ñ~¾ú‡ ²* ÿz5 ’e€§weÆ·s Œ¡ÑòCZƒÚ£7•°áîÕå·ÞåªÂ– ;Ójñå(0ëÕ ûT€Þ¯ð|w¬ÎúQž%7.¯ùê¯. µ»S oZìß ÍbàðÃôfZ pâpfÝ0‚Á'VïVgã—-õ»,ࢳI®I3@sÊœŠˆçw•û{”Ð>^¨*h3Ç-™’öÝdXeÚ›d‚H7ºKµ±i“O3ÛL]ßäœìä›`¹÷L=ü› Ú!ï&è !üª¬ 1‚‡N”Æ`ó®e|Õ+i°ÖŸš9MÂFÇÕåClˆëªÍèÏbÁÜpƒ¥OçR¯ "‘÷‘°!l®; — [Ãw0O WËMþ/´\Ú‘ž.E±Bþ[—FRd0ðÉ¡£÷©àû’볉¬¡auû5ì¿×wóÚ=d/ϲɚT€ðŽÄxÝ3º(œ¿Gô[õcÄZÖØBnÛΕr±tP[E~uʇW'¿,¤ÈþÖ]¾\œu¸Têiœ4Ë92¼ÍÉ/ÑÀ\ËGä 5<îÃQucÚ§,q mã35/=¦¶Õ€TU[þdk3Ü#fäe=É}ê+þUÅøÉŒz˜ÉÑWÕ¬`>§[ëvº>šMÉ_ÞÙo ÔÔ‘€yÐM:;›Ë‹¥*×ÃnøÉ@¯úÙ>ÕL,?Ù’'¢ çÓ÷dž`1}ã5ÉAL³ÇWk¡àmɰ©†t6¼¼åÚ{M–§2뜾ã„ÔàeSø×$Þ¯$n€U{Gó¡’<ŠÐ5Å׈B‰j:ûâ5e|eÿ¹4—sì+irÎ °-ô5/9Ì ;‚ùª”Ø(ðBV?Qĺ^8¾?ÿ/À²¨£v˵é8P¦î“à)}óþ¨´5Zó´û P«/ò£ÿ$'n6rÙ‡„P§Rý@ê'{Xkæ¦P¶äFaã÷ÑËGH0{hNní ¬X¾1pÞW9ÆÜ¬D”ia Pñgˆ…•«gæþYe(/ il_~þ……&|šôz&Mä“‚ÕQ ¢——¿¤Á&#EJ$PÊűt}¿ ˜ÆR¦ŸÓÅ¨ÊÆž_­ÀOQÏÚ¸ˆñÖ]AJ,ÌÐí\ao ïZõãÄN ñ3ê¹[(ðÂC¸ÖI ÖL¸[¢póë•?ÖZ@‰ƒÉŧ¢Xp÷Û¨R”–µ¸ÇËü)kïlLÚ¥§ ¾.0‹,Á÷ˆäL[­œ½8í*î¶ú¢~O@jë]¤— îö éìRÁsr&M^þ4¨i1ök\TqÅ -Hg†…G:ëak}‘`©þkVÀ*ž˜7(XwŸ—Ô¸AO t>ÃyÉü¼‹MžÝYn{¹ öé#d=KØÓš3pw¢uÉå1…Œâù“õQ\·—·bd°ýRéMÅûtXõ,(áÜ>y\v‹½›“EƒãNeæ«^0qàoa¾³d>(ÿ9øãS"ááááááááááááÆÿÏú¶*´WRX{ö7¼ð!cÁ£são…x@ØnoZQž%Žô\ÒªÔ·ÂVÝ(=Ói|?ùò—{2^1Üøx32îz{âšê¬[&':£‡2ïo×úœlÇÅ¡­ÆfÐV|é^… „ñ÷Uh îã´ìZ%MìŽ/~â)öÁNá–¸P*¨¹¢PcKQ[S^[×5“Ì0ãú Õ#ÈfÝHmTÅdf·±¬£>Wçû8ÀëGÞ;ôQíjË…z[Ǥ–ȃú–qý¼¸Ùj"5ÂAv[Œ›ü–ܨe|'A×çŒÑwäi85Çÿ £qË”ëè>(ƦÊ&—rT…e©ò…ý„ÑÐÃÓ¬GBld6ð ²Fû2Õc䱿%*ÃMEÌë—ÇîQÆ{Síûò¸Áygs~¨\.w¨1‚OŽA~3Òl¬ï$ée/²Á½WTìí€NOK™Yy:^W tØVS]&ÎÂI[= Çl2¨ƒÃ mª –Ùy*ÊDi#©¤ÓÓÎNô°íxÕ$K®`ö»ï"dIÑñ³¼:¸á}³l=Ë$“?ål#ÁJÓâ¦ïâ4hxg<õ²GŸ¦‡$)¿aä{s­ ²Î>ÒÔQ†Þ¬Ï×lPEz«ë \ÈÓ`÷%é²ðA€Ê{š5êjfsÓ^è@“ ñ‘k>iXàY¸¿¾Žý½ú£=Ä“Ú?p–ܰ•–™•s'ÁWµ¢ÞVÜTmúÙüžÈÝ«P•j‚ þÆ›7RÑ0[*ËZ‹ŒÉ"ƹúÒ`ëá=\üt¡éxÁVÏ–` ÿj¤ÛR ÃMð_ÍÆP:síë¦ <Ò¯xdÖ”ÂÄÄÝt‘úsß«˜>+íVSýû裒,ÜËù«gl “«äV¤L_­P¢À7ÏfN鈄Òn'Êb@âH½‚°¶<»ð„µ\ ]½äm/jaÜíæÑgÄ¡&ßáúÐÀ˜î7§K–Üøô=ÌzËC3ˆË[Ý:;£ Zž÷uÀ§-Ãò.¬@ÈÇö£ù#ÔqâyüÜ‹Ô?ÛwŠ/›ÿîø`鯻>ÛxíÑCQ>öšÝý,oû}if*Ô:ñå3¯ƒ,º\¿pÚùµæ¤Ôɲfšn%µ'¯÷àÂ`aÄ7’¹%\òök_œV‡÷W²2Ô‡M då¿…8.\áS$û‹ÈbJà²ÓŒWt8ëò²å£¹<2k#*_ž ÁóÍw8!_˜¿)<˜Þ «E„ 7"Ü Ü Ü Ü Ü Ü Ü Ü Ü Ü Ü Üøÿ¹ñÑãh¦ìAy¤Þ¢ËÝØ¦Ž±S}·ZmŠÚǼfn…üuã)5}ll­qõÐ3ÀíÉ…Õɱú`5OùÞc?Oú™ ~bu¸H$4èàôÀ‰Œ:Mhþi7eOÑCÛý$©àc’øò8eõçïöà×\õpLž…‰c·Æ‚£,QíXŠ­·€|ò7ÏÏÙ¶˜3lÚ—©b'×Û}%sa™›5¥xàÌÎ÷ÄÑYV¶Äí«`³³Þ¡ˆ3¬(ch]6BÇoîíS}8¦t¾;m…é •ÊšWõ )_€ãú«R*mýc˜ âùHkÝkpõø”:ÂÆŒ-äÔE°k QÇ¡’ žXÉŠ¥‰áüºÜ—rçä náY]~;w·òÖ¬‡Ê\³ökÁ†xÂïà™¢&sÈÍáö‹obâTß@çÀ…BG~sg8—U}±¹L Dîˆ=U…}_Šm*ècM‡Ç’·"1t½ÀV™§f¬ÈØ|t¡Âû?vîôê÷}8Ùɾïc'Ùf3ƒy_ÃØ²U(’¤PQiC–B$íe)•­”IRI”+¤"!´HI–²d ¡ò›ïñ{ðyèÉ\ã=÷ýºÎó8 dwŸù=Lœ Füÿäƒ>o{´X=Þb´€^ð:£›ÛŠ·‰ço¶ÂgÇÖ…Ô»°`M­ì°%Þ5½´çJ?Œ5´X÷ºl0¼ù‚þð!<Þ~xôÄ:2ÊŠ%çéË¡ð‹Ó.|"dHrzrÝdΟžZSwCœïÞ 7ÖhÁžè{OÃ:IÇb©¶"𓬮þ ÂE­O‹'ÒáÕ‹…)1*¬üü¨J=~Øêàýù¬€ëŒÕÈ›n‘2¦ú&², Œßi¸žË 9ÕùÿYiꯛaü îÿº\гÆN7¶eí4ÇoïôhÖj¡¸tÙþCÆ nú/Íhvpuóúô”‹D‚à ãµñÅ^~ÍD2Þ¨;.‘&+ Ë{Õê-ÁG.§n”…’g{3ÏÇ™Âò>Íôth=zmNMDN²B{,í Þœ|{#M¤fŽŠþu¢\~ˆéI‚ÜCgîÊçÛâMÕC¢VÓ ’ åÀ„¶k÷ŠT†ü¡1×-aù*m>:HܸÚ× zÑŽç¡®*4ŠJ;¨›s wï5'Áᯠ¿N3ÀiO½­Òl2ÛoQM²€‡ƒ<å¶ÐA©x6ì¯?ö­¬mv^džê¥YÉRÐíåç8[CwöO)ëT&<É×$‘úE '°…×WÝã>Õ‰ÝS‘{Kȳi¦HžÐše&ÈcÆS‰›Û²1.=hÀe«J'DÄø:Bk}jàq]€fù’CÇPæõ5ƒ_`ýz¯P‰>{ вQºHp ØëœŸ­1Ö’™ÑE3žWo·ª’AÄs*õIê=š{C‚¼ÌL=CæøiÏQbx‹ê:tÙk6z¼8R8o6Yí±p~íŽÓÁ¹¾9û.ûö]CsÔJ:)!@…Ï磢¬;lAúZÏßGSÃÄQ£„Åzc¬ã{Í÷ÕY”¤½™ú<>fö¢ýº òè»ÊdsÜø)q°_‘À~á“©É)lÌ:ë˜ûž pAáÏlßgüØþ•[É ¶Ð>äµ;ˆkUÚ°,°D7ôørÍAkr8nDg¸õ7x}mÿÜä¸AºI¯*È«”&Í(¸èKºIÒ׳}áB&øóæeÛ³ñ4Xôµ™ºéžIRç*¿±pIfÌ1kW Z›˜I‡» ígŒ$ð=ø¨d†"T¿ªs–j#ãƒ.ã«îÁ›aÞß{,QêÔÛô&TVI5Z»ÒÐÖâŸÛÄ:¼¾]÷S†®$Q~ßBY<ãnê'/ªï^Nw÷ñý¨^IwPŒWA«X•ùW¼ð*9k¯è`CÚn7î­Øö®y{Õ0ÑÊ’cÒÆpÂ÷¤ÝF/{|üCâDY@õÊuÊMƒ4èa?¼Ñ «×ì]ééÌ‚×>6ü+§,ñ¹ß飅ßÈ`mòç½ ö^9:’œ`‚fª’Ö“Ñz{èX’ŠîW~®Ù+E†áãÏœÇfÌÑ"Å}Ji)¬õ/èUkÁŽE³gŒ!ÝNü.[ ¦^Žå¸ñn¹ÐHêa:°Ilþ>*Xž;¾Ýµyž8ulª…ãÆµAeëu7lwÙžWf!£FdAá~ñšjä¹Ù£ùò5FÝ'I ¥0¬²ÖLw”eì2ǸÄCż¨]ôÛ±dcˆìú°@øžH•äaà[uùLÍm¬ x÷~Ó 2Š_ÞR¡, ¥Ê%qÄ?atñ#K.÷—…ˆœݿѦ°ä sòr8z¿—Öóó Àæ qÖA¦”\»<ôÕ‰V®)`Ï9Â}± uåHÞlÔÌ̳E£[GC?­¤ÁN–îä¨3^•¾ Ó9© §f4»Š/[‚‰;Ÿ…¯jîhX·Sãfª”*„‹ŸÖÎ=¬ƒ¯ë#“À÷èlyRŠÊ=Û{õ· 6:ïýƒ: W~FØ$€éf»*Þú2@eµpÔ¨d‚¶§$†KAWûÉÁ÷ÕÐÝì™sïú2*øêE`éóêi+-Sd|Ô^(#÷“$=´N›¢ûôדe‰òHM<ËípE©¿‹³DÄp÷XÐÒŽ û‹…Ç)s÷Ý1`¯å¾7ꪦ¡Zï¥ìAþnàͤfÌv×ÌúHÁ»tíš}“ºïçôõ„<Š}C9nPÕûŠ)Jöܺ¼Ú¸M¨]šmOF­bO I˜=pûþüœ0R¸Îœkò•……ŽW-Q¦pŽ/.ÙM‡ª9óÏòÃüü£IRÉÞîêÇH¯eb®íË1ÓŽ U¤8³Ÿ¥9¦%9nÔÇ*l²ñ ÁÁX¡ÞGnLh\‘íÇÊV†H¡ýÑå—,Á"=Û81‡5eÿNɵ¼ûòb£ÿ?0úw¾àçý Ëì ÷6%ËU.ú62sd娀MwÄk—Y@¨Â'ǵth‹ÙšYèÍ€óýŽŸÓüÙ`ó¸¢nL¶F–WQàªÔëŸd‚ãž©ñBð>'¤¦gŠWÇâ/,•™eK{¦›¢R©a·n’<þå|¢yCƒ]LK¼Å°êÆ ïÉMŽ c°È½q9À—jJÑ 8YvA—¦ ©+$ûÃ&=n vÔ ºhòR5‚TJŽJû¨.> 5Œ—È“¡ð]¨Ú¾p*¾œÐÜÑXGew¶}3Ç“ýC(áÛí+ìòüµQ§þÍÀ6ÁÉêûuö Ö7T|/Ù~aá)×ϔ̱D¬êÐl$j­{cÞÛÂÅŽ%W&†C‹]…VŸŒqÇÂ6mS[}ð/’7פà×^M™‹R²xw}Αé_®p>MYåƒ<»þÌÉ`ãËÜvÙr#€ÀèF¦Tg\½ÓÓ¿SÌ fÞ?p!9‚Œ'ý#q‘¸:äóœ2ML.XÞ6æ¸a^xçÀ*Ž×_Ô äVÒ±“ا¸†A…+òòà1›©óW·—S€šœ,”³hŒ~]ã[Z¢hÀ8°æp…̃²‡Ôg~=.Ùp‘â cU§e`8ò­ú{} ,9<÷má¸"(,5}ºØMÆ’uÔС«F ÓØ×}<Ì5ß–©oêfBæç­=ÇÙ4Üæ¾_Šl`6£«u£w*ƒðŠ­g“òdñÚ׋Y÷4àßßÔ;·¿Wïù°üÚ{Žt^ÿý»ya{ù³üeTl ›—»j2âõ lGˆž÷/vJÚÃ;oˬƒ«í‘O¥’Q¹ <~å[«4˜‰¨^âºÎ g~æælc³ ]Ný]- ¥ÅþÞâôÔøå޽±Á ×y-÷bMPá ŸÇîdÔH|òGõê*Ss4ɰws°Zؤ9^ ãeÀ ½æ!Z°=lƒß¦Lc°gƬ±@|“™¿µb’ͱg{ãNÑÁpc¬^Íö S\øî¹K–µØšošÿqÜ8'}ÿå]Šz8žœbíÙÕØ¯öQçÀ|àO‚‚QÛí>Ðkh¶¯9N? ªãÌ QÎzòé¨1¨­½•Ùax=“¥U&À@Ë®cBjéÚøø1é±?DÐÑJO”´B3ʦ…ñwº©íŽ5²6¹ôù>SPaïNx²——OY?Õkå‡}¤L³‡.v°'¼«Ân5ËÛ†´îL8BíÆÜOíÇIé*¬{*×¾Ð7Èq£A²„ßtâ*&ô~uÚT¤ ºo«UgZ‚¡bÑÅÊ‹tøùã\É[€A}/-Ó! j¤E–qÞß\uBF z‹\m¶4ÀjOÒdy6l·äÝ]obË ì÷÷ Ã&Ýn½ý«0h-±Ö2€ ßÞYÏð;HAu½£[f¶ÚÅ.Í;Í„ÒRsŽˆÿæÛ‚\†¦ªv½ •W:ä|_¤šâç“æ’åQ¬ö¾0÷AÔ8çvÄU Ï´ê&jmv„¡‡^B.&'vï“ë gÀëË E¥£•[ä‡ìñ3É]ç! ®{É)­Ò3tºm6¬‹c1äðV2<ÛÙùfäG-J«IÐnÕï³é‹9&íQ¹ºÎO öß± ÐFbún×í©‰ê‡¯d8n\Ù>XåÒÃÂTéæ¹Ÿ’樣C‹ ßC…~ê•"‰¶°·oáÇ®!B4+÷G}¿1 ŸsÚ¶–©}Ú}†ê¼_ß|aÈQ•”š”/sÜxø¯é\“;J¼,¼ÄƇÂVèDpo Ädg|Î"Ô„¬@;Íê|ϸ#DÏ^Y§±|‘`– ÐíqÛªŒ‡ÎqcóØ’Wi7ÞÊÝV ­£ãÖï¯$K*tªñ"ðl¯Ñ²§°PzuoÞ7í¥Aé¶Ú+ž9À³a9›Nª²vðkš¸Âë©Ë„â982›Æç¨+ßÅ4ãG!Mçääž2ÖHD—˜çA~¾§pÇNHÏØ>>È„©/ß®h½©¯k‹u±7º¢”AE0XS![÷ý¹wc‘WxJÊÓâ¿W—°·™'¨àðÅín^󣛃+­ŒuPµ.•o.Ìy•ð]ò1œ´óGÖ:ÎÞu›<±Ê;ÆÖ4Ølðõ5æÞ)Èɹʦ.Æk­8®ñþb±àð•Û9N_rиpû#Öiw)´¾²Á¼‚ÇãŽÑ&hͯÍÞ²‰Œ…"꛼g9ŸÇVñ÷‘ËÈpòã9[ýqs\¤pS·r3à•ñ¨¢ÝC-èï_oþþ¼1´é71^¬Y Î=²‹¸Éqã¹Zþ•é4:Pyy——MRÀh®„ñ²ú7! ¯Æq#.a_[†¾¦í¤-gaHÛÆc 𱵎J°³Ç-´ ßB{x¿Säã¸a°ûæ/©s<›Ÿr©€3wKTÁÊÆ`-þïsw Ç+R ·0)“//_¼  ¥mÌgEÆe5ß”Œ8=EBìéö adÿÕÐõ”…Áí ‘ú{LaÙÆQý†:‡Öú;”óƒäÄËÓî«í 6SS{Ÿ¹Ê7Nž »lšq+—Dá•Q!Ž»½pÁ…;ÎFw8z3A¤ëXßÁÇÊpæÊòý/O[‚üRýôy:LwŒ58¶sÎíé\_Ñw* ›Ûäôø¢6:(ÅP IPŒ´¢€ìÜ•ÛêªÀ†»ë¿¿¤ZÀᤥ‘‘ŽtpO~Ƴ§¶½ééÜÈ/‘€¿^fRP¼åñº½)§:"ÆÁìàm[E ^¨ét·±)†|×Ó‹þ# ÷VF=Î:lŠ·³Š…RäÑýâm—Ïe‚øíÐ{]G;14\ÔÐ}ÄqãÜqÁ *àcÚ{»Ò¬ïN· ‘ RŽ>T±‡Í!ÁNq7I`O{ìhf{s—Èþ®‹×»¯ªOI’A(æ“n »£äkÏ?&OK<ïúNsüv/+]o­*‹~Xþc£6Jovÿ2úe¢Z‰¹ÜuÄÆOJº<™èeá|ÊΔ³Bœ^ãžпƒ CŸ&ß­é´ƒ{ÉãƒD÷­ë«ÇñŒETzCêEŸ h¨Pðte}@uWv¿rḭ.b…²ºçuÏ”\ac7«A>C€´±a[i‚3±Iì+0ëÖÿvfÒôÒ†Þÿ#Ü>¦ß<¶eŠxxóØ-Ç Gº®[+Ç[6C,šèõãCÿ … Ñ÷Þ5²ú ÌLââûQGò‹¨¨çÓÆø—g¼]d ò7Fxˆ¿rÁ†•!ïYhå·¹¥p™+hž¤íÕÌ‘¿óÊÉ´$°·ÙaÝ·$E˜suß;0BFJiàÁKFà÷'{¹J%Nm|dÿ‹ ß»Þ[ÓN¡š©jÙA]MøEHT†4+îö[Y²HlßËtY¢_¯§=~°ú{µìU{©tŽô&_Clâ…ÙBÑg}4½r³sÅ Í òŒ1Bø·×ŸÑÚc ^ö¬WÚãÃaZïÓ`€°²#ùRtˆ[zKÌË %gòÄ,°Ý¥"a"CÃÕYÝgo¿#ƒç]ãZ™ç6xôçÍ®ÎHÜ]p%Ú?ˆŒ²Öá“C²ø¸<úùJ2Î/ îýiŽK,Éíéä¨V¦kö˜.w½h àÂ]Œ– D•^…¯À Ž«, çÜ›ùG³® SàÂxݨøo¢L? Ü‚ã†ï¶ÖK9nÌží=BfáGjFÌß!H6º¨öxb}蕳œ}j -»ÃþeÒáØúpݯß•#r~øaékg0;w“»šlŠ"¯nÈ„ü’õöÕK¶2EõoÇ–E‘G“Õž*•‚xâ-å(/C CŸ]u r„ Ÿ+’ÕÍLä{Æù3€Ý|cH|(”ÿöå{ý¬ç•< }XÒz;QF‘‚vÝ^‹7îÉ¢ÈGï³ÓS®`·ÁZà® wN]ª¸ÉFÒ‚Ç.]?0òãœñꃈײÿ`•@]¿aºSêÆhâ?¦R·W;q’8ԵѕãOòjAÑß68îlwçÏE”šRá^iÛ¤áWe" )PÊ÷èjdz¦'güt·Ó€g£zG{‹˜+ þ=ÙÎÂjK²ž®+tîOš3¼*L¹/ijHàÁ­l߯+Âφãĺ)2n!Ë%Ô3‚!5’CàFKÔÑ¿'¶È„»Ñ-s4ö#K‚dB>Ï_=¦ «n_³Ê”ÅÓ:[|3IpD€væ³Î÷j—¶•Ç8nôl$Ñÿ†îšWœóÖÔÁ/!2ç²wZ ÃæŽ6Mោñ†¦àRN^·\ñ¼»ÐÃCŒ+’™AeGŸmW¡ÃæM ê?WZáõ€ˆ¼6,о9b¬BCÏàÆ[md¡¿yç6¨6“¶ÏŸ*yy†Q®ÊæÇÉ/²X*,ìiÈ ƒ€{€Eî°9¦,·üa3OúƒOfK´@>(x–Íé ú)åˆç“Ukƒ8n´WŠè]¢ÃnmöŽ3}Û«=:0K|rÏWqÿ_ÞhL]×ÏqèÙåC…}=TÉ{ ˜Žè}lmÛ;Ú£dIб”á:ÁqÃj"×Ïv?§/j¬¿«®V¶õŒ³ÆpñG€V‹õa·s«õE1®·î8z¡P舅՚’±àØÓáNš$°ZÏ }ø!ŒËÊNoqt•…F%Eîáí¦pá‚WMo4\ëÿ &$ñCb~­yB€,UÙ"ÕdÇÄõ;?´Ø 8rrɆ8½]Åóm‘·áéú{N4‘ð™mYϹ3=b/>(Ã=©IvG’%x¿±âä-r6ãæþ÷6e'ž«@èwÿËÚ¨9÷ÖKL—Ïwüí!8%N±Žú ^‰ ·?ê63-ÀZRßñŠ%¶LOu°ð6XØL3ˆ F?7’G¤àÛª‘UËîrέGÏ’ç˜à/¸wGÝ2¸‘~Ò?×Â#=Ö„yŒHCHmf·e¼)6F+úž*9öyq«Q9èѾjS1ìʤýùÌq£å ­0 àÀ°¤ƒ½~:‡Ÿ‘0Z×þAyÜrNo˜Ñ=C‚Ç&æ~öF°g‡åZöW],.øÖ`¶” «,(t$SQ$[ü‘ÿ]˜Ý®p~kŽïh¢üOÝ•Ðt|­°i6~¿üZ,ñÁDõã‰-}ç9nüÜ@Ëf…W¼¯éüMŦôŦ7ATH!­’üÖe ¨R‡¨c?ˆž%'7ñ™`Æ©Âgµd}ø2wý­ îò–þ.‹Ï­¶TärÜýdÕ•#Mà“ߦ)Oî²±ÿi|Ö-€(JŒÁ™ýθäÛ!ƒ¿®ò]ztÚØUÊFÿˆBõîz—KÄà¥þÞ7\$=í9nŒË¤¿úBÇx‹å+£¨ ±Ã5Aþ[}d”ÞR@àüœÑ÷ac<òùk+ hWeÖf|pîÄŒ½[Yøf0ãñm W¸ûQÔ´H¸okn9£&+›À¹6V|®}5GÆ+nMÃ"iFpAóQÈõõ–8æ~ü\¦ =¿¶©šÓ¼ÒÝ€WÕžü–[’¡ UL¹mMgeq4CŸòõ Â75•«Ï TWûvß%Qù‡Š ´pÜèÞ)¤¢ƒ³¢Z?Ã-ðFÍ–ëÇ\\•ò&—¡R,‡^æniµCO6üJ^“É¥G³± ›×nV¨MÒìƒ Û$‘©MÃÖU%â·ZÈðÓ[žUiƒrüdÿì6Ái+{!ç­dœo÷üeöVí]²Ä¹lÉ Þë3hŽO9ݘ¡ÃQ¡I©··µàÄú[aÔ°„û“­OÓ¿q;¯ÄG€é¤7/¨€´}ñpC‰6fÐdOÅj‘@æd —éNTš9úëœ2R#®o±³êïŸY•&t¬[õ@Ç9uÁlî[é·bJ*ôY-|ÅðKÝf¾û(QV&í""¼]$*†aŠÛ¡o¬ú¤!G¥u\ ÎõÚx½ŽÉcÜŽ„K‘Ïñâ݆¤x=1´Èqy°3ØZnuúÅX]ÓySÚ…ùq×$¤^,P™´SµKb‡“8ýcÊÊD×ÃX[çs»t‘A9©¹Jˆ qµoôQñºNª'«ˆnGù¥YoÌqäLß›Ã+”Ï*N¶=XçéÉø2¢ú媨a7îN›åõ³°'iüTÜ8•“#`#þn¨¿¦÷Åʽgô*vþ öoßÔ<,n‚V_›LúŒõ!íä ú¼7^÷irU•C¾CBï8nÝ ¿5UŠÀóíö/ëÊÙ(ÉÂæ ¸9ÞÍÏeq:IçÊg2ÔmÒ2‘ünŽ‘û–ü›¢Ã~Û¥!7ojAäü£78n%»û¬¡sž(ª¾h¬ü‹]M×(Ÿ¯Ò?–ìzGL7ûë¸fˆ¦mg×ÅrÜ 5ÚÇqƒVôU`³ð)ßDÅ/ (\m 0in+ƒÆb²Ôáäþñ€qŽÛ56]ÞkŽÚ&lªô--Nï³_£yΔ7¶|ÿúožx¾Eºv¹4Ýç“Ì*ÓF&#ìW¢.ÃÝŒ›3Ø’àlbC|ÆÛÊÛÚËB“Õ'Ñ¢-¦ &Ùc¾p€"nËÚÛ=øaÄxqòÖv;øÞ`°MƒÁĸæÛý—¿8žçÌ/C$X[¬›¼d‹baß­6ÙÓ@}áË̪ &xù$IÌ*ÃeÏéž(K(3pys‚“÷¶«ßè)d¦9¬öéëµ[jc‡,yÕ‰"F¥Ö*¡è{4áe± î ιt8YÀ0},§™ã¾ Æ[3Àèņ—[¶°A×5Q)ç‹$4ÑôݾÜäô¿]‹‡û/1Á#Û;F„!ÕAT‡iŠCÇCœ5º¤á½éJ§Žý¦xn*v¶ú¸<"J×4 â±·Ut51ŒTö?ôã†rº_Q· Àñ³],ü^¾±»\@'ö Eõ—=<ì4ééŠ$Áߊ©ãJ¾F Ÿœ4óAMÏTìà'CßÇvNSѱeé>R! -[ÛKm0Ç¥„\]”°äƒ1eÿm´š>"~b¢:³÷T%“ã]vCþ±QÎ4~WùAÅ'!*Rwý¨p­Bœº£Ûþú„ÙÀèw¢£æ¯V„’ †N'.1Ô¡{‘3ƒâ¼¤{ÁöÑj9:Åt……Û!Š»$ üþrãº×OÙøÇø^4KÓ×÷ †ìrFìWë·ýÉ€ÖðOà2ç¹— M’þ‰E).YÙc„’¦ hÇ?¿“J8nÝ­ÑÜ0NÇW7¶ÝãÖ£B­Àjåo-39[ßwSÀ£GDí_1 ?+MßHƒUžwŸ+ýp€G¯Ë7r½faH_âû ®PœŸ6ʬóÏ©EÁŠøb±ÃçÒ^EXX½Ðr›Ÿ‚Ÿ²{uN§AÃf“˼-ñ´Ï–/ÅjwZ¨úÑðSmíþ>9;¸¡êø¼PB·uøqL'í­ÃgI°Õåðx@Þ@õš£ï¸©à±÷"oyÁ-÷W„„òlÛËsz2õWE&¶þ$bjì[Œa utÝKW{<ätHº<@(…©µÙŠ_Vëèr´ÂuÏ‚wfš³@w!ïÄ* ÛŸþÍo Ã“Þ OÉ{6xu´>Jf‡ æYf™o'cu§È²° Y¼¬ÕZºŠ Iû‹_ö™cå—̽ct8çÁ_¨Ÿ„+«^5†i¥ðõ•ó„ÌUÑÕ7ŸjëùÞ¤CëÒM¿ýš9=ÅZRŠºršèÊOý{†ãÆ!vì#Ž¥¥SÎ,ÌMi{ù‘CÂmôcd{”t9µÒç¢:¬õ÷ó€nçç‡fãÌq¹I¥Ö0gî1ŸW粌!¾|üÚ=O¤Ýµ‘Šeàõ-Éʵñ¥~Ý¿=<ãÖí]ÁÙÏŸ|w~F.ë‘}r¶²pAîk`°)äDÚH ÃvÅ~Óßæü{õµŠí^;«ž½ì`ÎÄmi»›:á–@ôå>N|t‰ìÆqcéÏžÔ‡v4zÞõCr+öô‘mlØçyjlô•$l²Î’zPHʫ͓¾¹LxU¬«ü®IêE’ΚۚbØò?ËyÚ¥AÁò/õr„)*®(YvRãŸï]KkDý¯×ÆeÄpqÇÝ“!ŽåSÑÀéûʧ®pæM×®|%  ú~ßqµ{¨¤—)¡$ÐÉžôßlIw;¹tè¢+îI*.’4·¾Í ¢öÉ òß|(:ÐÞêÖszížØ6)[%Ûú@i›6Ž\ò¿5QM=ÏsR‚ãÆgþœp“ Rž·B]7çûw¬Ò\K…³•<á¥_maûÇt+·ß‰Ü÷ûô~i›à“×}¶^‹ ’Çb_6x«8Nõä~W׿ 6FRçyó#ëiÍ\úݘ4dü×yÅÂùé3ôd\!¥lã5e úÌüû9 ü'sÙ*5\b”[Ž(ˆR0Ü2®¦5ÉåÕÞn_e‰áÒ‡L xøóä‹3¤ÛwHÛKË~Õ»Êp°œý,òˆ,Þžz¿ºü "³?oÊÝÃÉŸ]ôã¸ñ9+|g`/ä̬NÖÁRÅØ½_öZ`èñ‡¥Ò£Ä:¿«¿Ã5³Ã‡ÞºØ£B¯ÀÊ€[ß´ÅŽÛÓÁºœØï϶ÂýKn{)QXà礿[߆†ÕÖò9/È@øó÷¸Ûà†•K>ç…š`zDœÍ²dÌ{/ŒïÌ7iÜeÊ‚Û -î-›LaK'<—‰ü½¾[í·=dÄN³Ïù“à,·óÖùíFPUO½Òª‹Ý%º×¸Èpòbº„æE*ÞëÛÔÖu‰Óg¾¿P®1ǯ=ä÷6J¸gµ~þ“Pmüúy27MTWBžvµ=òôjytL±ðn—¡ùÖ÷T4VYyþìj*¼¬˜î±…ùêþ¶£Ä¥·"LL0åùÚ¿„–>4—¶)6Qpü‡|Ÿó9´à߯à¸Q‘õc†&N`ë]Lók6&nI”Q¨/x{–±Õ e~làôá öh÷kAíΡ=ÿû_{ByGˆÔû8nt†F.³Á0¹5}Ü d.ç;J¢BUf«PQ=J‹ßãÙ?)Ð%«¶Súƒ1®)#]ò¡A°ºSƬdýQÝà\ÏÂcÚKb Ä]¡k{µŠí àËÉ”eÈH`œe£Äž0E8Ò“ññ°4·¾•ˆ7MÅnßÛn–X0qøü} IÞiÇÎëаÉlÉ‹Z ;8ÝÆûi…2\Oé _’,‹Ô¹¾€¿$Øðxý•ý@õéll¹Ïq#UÝC2¶ì|áÕÁm¾ËÅŒ#,pÌ”ì ÷(‘Ö¶¢¡£àöÑO+ìÑô<¯Èâ:^ÿÀƒ³¯dýé,+|_õ×<Ç”ÕUuרÓðÃÞ £çêÈÀý]òKê ,v\« êMÖ§ªï"ãhûÒÑûù²x¿ñÕéµdξ Ê1ÿjŽ2«¬~Ð!ǨqpY>gïý.ã¾i ïï_=Ï}pž8ÈödêN³ )›òÆåÚïT-·~Fè[eŸ*ǧ™z!åÇ7ÔTfç,pÙõˆ€|oúŽuoÑoe@tðGz¥¡=|íjÀÏP‡»ÒIÛÖ{k4…)–Àq«›Yz‰3×Ý5ä@lŽ1D­;Z!{ežØ¥(põŸ"]r©«¼ÐÆåŠÚ­Jòd4uvÕ]# ëYеnm¨ÄÇr´’¾­–×jLa£Ç‹S*ÉtX¦Â$…-á‡U»ž.N°ƒy;Rš!­5µÛ¡qÛKódM à¢Üµòã¸ñ¼ôÔ? AeQ35œ ƒôˆõgµUàžRÒå‘`K™WÛÄy]½¥O vvœ0 _ÏpV#-·ªöFmä™=1©©H‚¯/X†Ÿü ðªh¶È°: öµÈ~[k~w•ÿŠJpžß?¥œ{܆÷Ý·³ÁÕJpæA>ç÷á?ì¶9ŸÛ}J²˜L(8CäŒ0'¯h˜ :›bÜè¨Ac­4|iºYÎ7Eýºa»…Óò(»è9Ý%ˆ{<%¶-á×8nŒºOîÉw°ürŒ¡Ë€±ÙãÓÃBu¤"jóöphB–˜^I‚zÇ]ß#`£çæ™&]Œì_ ®þk?eÊ^íÉ£bæ‹'|çHÜ,˜ºô©9:Æäh_¤+á÷«7nׯë“^(ºOT×UidÞå¸Ñ©~_?v†…¿nùZ/m¦âáÈW5‹nTHÏž“aôÚ‘ƒ,¯Äw§kUÓtl¹±å·Ÿº>lnú[ÎÉ÷öêë›.MÊ¡>õ}÷„+hÆ%[+Št–ÞÐÒÆÆW‰WŒãäf MF5‚œQ®cÜ%¤‹¢ÂotWÿs„ý¿+ØþCH|~ÒG”ŠÚA™€©ÇÛeø•?7u"AoÚz'ÚŽqž·fç¤Â@uГ/ž¢I*xM#&>­ƒ.<[ÿúä?mì{}Âù@¤^ÙzßU,k”²êË«–3î½ÎöøM V_× =ÌC½Û—‡¾>erzÊB^ݘ×™-r£á…Sxúª·«Ï lðȹ”’`´t¾}[f‰9—JŸ Y,Ü–t|Ù62°#…£‡>›c€xÇÏì>:Èd?Îý—­òVË÷ÜØÅªóDœè©•8nTUÕ?¤ÃËOó–=¦À£Ë0Eqñ\oâ¸ÑÔùq!ŠãÆÎ3ž ‘~,ô¼ä@êxÍ×OÝVéÛãƒM» [„Ô!iÇûGý7®Š¬ 8—hŽá†»s´àð‡×ø3Ï4ŠŽfIó‹/±Ž¥ÂÀ¹ñÒ‹¹MÚ¨ç´õø¬$'75߬Zô“„àóžóo„ñqYã³¥,¬ØW¬îo Žâ¼z)_厹öƒ~Dͤح{\Ø}]&ÖšQ¦]›n麹üxÕlñö‚-îÚé"QĤNtQk/@î¤ RT@vZ©s4Àâ¼ óÎ$Òáɺ£õ|=/¥DuÌU`þÄð’ö·Úy¹ðé19ÜI_VQãk€;µœþ’ØÐVzôÛÀz x”zbTCž5êr H^7¾ƒ b#t¥’P³ù·!-‡oŽ.´ß½Æ„SW?žÖÛ- ´r­ˆ7S´ÎϾU) –׿óm7Åšò7gå1п;¾ ââЊcšs¢øhæá ӭްÙA1é¸3À·µKó¨2 åcHƂǑ—þTÿØ­&µÚ›M‚Ú}ÐÓ–`+¤¼t±~Ʊèü(ºÕ\¥âï’Þ¾ô³$Ðõ:ûçO…9J'û˜+á<ß7ïNmœbÞLŸ¨Øìòà ÇÚa+}­9 t8ÅÞ¬§¢R,¯ô6g*Ü_Æßg …¾„ºÒh?ÑYÓ{>Žm‚%ŠÅ»÷*ëƒrŠ®ç zØŒPôåQ±=š;‰ã†j¥ýÔôR>Pxø®“m?~•àQ+;λÑ7 \U«~Çcòu^..'x1ª}héBú`ÑëÅm?ˆFá®C7CT³8nìY‰Ÿú­¬›J´¦( ܲ“÷ËšÎQÂng¶ÒØië/ 9ÙcÉÈù'Ù¾n ÞÛŃèÐ0ŸîÝð ò»áCä»h-Þ÷¦a§3H~J†À{çå~åÚà+çòòfÜ™º„-²Œk²¦8ý)øXìÄ’ÝdàòWÛŸýÉÍ%ì.Í}¥ƒ×‹-×ß\ЂSCN¿Õ‡/_èÀ"œ* é¿ùå÷¯3åœ3J+9•ÿÒÒ¿yæ™Í²'NÚA›X†t«õÈÞ»!Êí´•¥“hm»¼Ì‰ãFCr=sÖš…IïOþ‰dB³h%ÿ_[Hîž öµ*¹f{åA:8~UÎý°ÎäçAn (ôÚÚ÷þ£6 ˆ;:&MíІÔkÞ#˜ç*®É†Œ…AÉñ@ xþ·gʘ‹6]Cô@VX¾6=œ z³V£»¢$an—R€ÐE l)(Ö¾Áo7s-“ÕÂpjøÓbÎ*SÜ¿91*±L˜ï+ˆ¦­¦8›gñ2M©Æ-*âÉkmc¢¨Â _ý˜ã†3±[(Ú o•°ËsiT¹›U ò<׸$¬þφ‰;Š-IødÖ§þ˜L½Î›U¯‹£¥º£³fð'O‹!{‹Š…›‡-¶ ²W¬øàsTu‡:e3%ÃEA»-ÊÝVþòxÜÒ'A—ãF±6i°U„@÷;Þ–6f)t’p}W›<âçŒ]j«Ô9¹¶]§äÕ-n'ðÞu̦kh±Ð3XÌ ¢Jc>á¸a1EJN渡[S«ÇÙGæ1'Þ¾’ã¸Áýr½S9ÛF7éqS¡ñÌ•F­WÆ8;OW~ëJƒ—F§Ÿ‹9‚[qWñŽÿ¸¬E—¸Bž!ÿÛ.(¶ã>»k©^!~¬Ð P„ÙO²ç_èRPÜv}HÝ.#¸~á‰ßf[Ks¯ÚCó$€Ôøz¦_‘†C‚Ïä ØAevziB‡2<Š>-‹)-2KIœ\.šb[Ý_=·n3«ŠãFÍ@À“Çïylpcñܤ6²‘çŠR´zÖ,X“TÆE·#ìMÀZÕ‰1éh‹ckKd|Vz­(1ÚI‡©•ýßYZá¦Ez§º ¾†¬uøàOÃÉÝ;׬$Ù›QÆFmp a¸áÑFô ¯=ð/‚Œ>/œì beQsGÿ¡¦ýd¨ŸM±[õÑÎm+[÷™©.Žï¯œÓá6o­µwaÝk}°Æ<<ª?žR0ª¬¦ÃÈ‘-$Ûèéøê,Ý3AL ß¼;Éq#´JöÇLJãê[X($I]VÇ€eW˜¼1ö˜™ôèÜÆ›jàkáÐû‰ãÆä1¹¤¼dsܳú¼Òy-`n…Ö«Æ ú`Á'Ê~ž—øZWKb`ò<Ø^ëÔÆK¯ßß* cK£ßÉ’-’ b¸KÏ£^—³+øÌdÁîǘuûZS9¾n‚qŒÖ!QíºøÀ,¸ª[6Ãvl[Tø¥ÂD†ØÊ‹/¡òT’ñïupm<+ôi–-VF]óg[Ñ` å’&Dw¿×\­ý*«'<-ÁfßtÌ`,þ´N$Sú.Ý?¢È-ª*=†Í_µÑ¨Ï¾b­$ Ž›‹Ü?¹Ê¿†‰Ä¨i±áÀíÕN!`ñ¡ç”ÓoðŸJ¿¡£Å€ˆÇ‡X”Ýl07v}6²Iâc’Ê¿œ£€ŽZ£jÆ-&ÜoÉ]·“" .¡Ö¾{¼MQ¬Ô fs‘4tK{_6Å™¢†òxѸnãùQALÌÚõ!}@ "Ò'YÛáJbý»@€;B=S™Â è­]b¡À °áï©r.°²©Y£lHÞ¶“…FðkT#i¸F%†ã§Í€ðO·ÞPBE÷Þñ[öGH@úw²¶³”ãÆˆ¨Ì»åJøúhùj§ÝÚø…µ;R[b¢V1ëå9n¼0Uu ÿÇB•†ýæã¨¸#êÕ4¨ÐU7oÎÿÝÇÙÜ÷Gû™Z‘¾&¨Úi$vUZ¢N ¸ïÿKÆÒ Ñ;ÈñPQ”}ðå¸+P”«ß&p•ÃAÅŽl¬{ÚîR$ÆéíúR×:cÄäë˜} p»~c…¬~,uðÈ\¹Ä´¨¥¨ŸÝï|’ã†Ìå• 9n\èrÕÓf`ì™´3ÉÒT¨èzV­vÀsÇNH« RÁ¶½è%©Îõçúã>;Ñ «lûÅ9Ž6ûb³pbÈSíà_Øö6±Ó±WdW \k’ÀTã/Á~Šp‘lémiDÁ›J½ßaFÀs{~æÓÉ«~Ê?ó'ƒÚ£çdih|?Ûu¯,^_cÚýE†ëÛ&5"eqGੳBßI zk#­¿::“ŸW:Yû÷Ü-xýÞÊ®NÑÆMŒý²¡1¸‘噼rññ²LÒ ØÌç9ë`›­&/§®ø}ÿ~:|4ÏXu”j…'¦•—eë²€rý½‹P ùöªÿÝõˆ …–{Zü2mŸ»þ¶åT ±X>EÆõõžÞ6á²ø{$=áZÔØÝí\ïÍ1ðñ®ãeépæSnWº,ûy£áž1(Û[òí^2OT¥Wñà¸q#ûK£Ž‹$G^¥ÝM[(Æt‚°Ê$xmW@lòøíó¸z÷^ÊÜv6ÿ¥ô¦š6ª‰šBjö¶¸æÜ¯ýjPyÞkÍðßuT\3Åß‹.â̽•;›}Ýt_ÛÒ'~Yí™àÕbോ5*¦=œ½ö«ïÕá%d4 Ý+pb‡$dü˜œ|&Œ5·¢+3Œdáxˆð¥eÞ¦`%ó1ÞáŠs×{^âƒó_ïߺh=©”ýK˜˜¹z¼¢²Ö6º uîЀ³#·d(7ÄTcOÐi/ç1µå ¸XGëB9.Ôþr³‘m#Ñt¨èô}ÖØpí5ÿÀçßÊð5RÍâíwmL7»eõXŒ·øHƒÛÝ ›»(ÙX› çòòþ„Y@ i\Åsœm· ¿©1 ·]o¨côsÆìÝ%¡ÑLW£8>}S·Š™@*á¾-# EÓ«\}Mñ™š·‚y4('œ­Ø°Éþ3:)¯Rÿ=9øK ‡Dôm?‹âá§Ë_qܰ¹w@aåj€&êÚ on¤–=¤i-XþDàê8uª5q¯ ~~ gåÁ#Cý‹j]<¼cgò¤X´˜ü(¹OÅÃGo8È"AÃPøÚbsü·±¶á’žˆ¼–æ´WVü¬ïž¯Þ§ÊIäVö¸SîÎÃ;ܶ¨£ôÈ¿¤ˆŠÊ¿]rTp÷΋Lûa Åç#ø½W÷*3!&Ƚ'4í½˜>TfÉVÜ™#ã¦Þ¹­vy´;|÷€Ç—wZB>û©â³e‚‘¯‹†£D4bDµŸ®vÆu-w™‡9ûéç=§KA|N/§¿B˜´@ Œ¸¸×\úFì™X¾*ã†j禡×7¦v1—_7aàÏM±dq*ñã;ÝTDà}¡ ǽ$¨P4³Øöï ÇRÉ~6 V?þôWÝ.IV±ÊY¸6åøüଠxÉÓê–sª„Úe~ ô=¨¿nÁ[ª¦Â­¤Í)8ZɲT 1oÝ{u³tK|±Z÷AN(«ôîy/—¤á©'/È…\vðù½ÖcHÚø>ï–ÅÍ¡…E½$è(”÷£øõWû-¹$Çqã°§È …N^¸üáô~m´÷”ÛËgü©=r%c„ÍåÈA™¸è›µööØGY¢QàPU|¶)™ɤ£R¦Vøg“–kª Ît¬­ÚECÿk¦½÷ï“aØÞúRäi\Íû.A×Ï{ާúÅÑ×mù)…²xØâ|>ýNå ä¶›cý™©&Fg?×{ÑSNk¿NÀç—8}¡W'èZ×áUú÷û7Ž—v iW¼¢Ã~{ç@V6Fí¹„J}Ɖuáu 7ÎGT 渡[ò @u ˽ Úÿ˜s^»{™mŠö³‡Gô¥ j(qܺ*Nš=bŽ'JæôyÎhAñ•¿loÃÇÓ†e¸æ‰oÇZÕé0ð8µÆÓdPcOÈñ[ü1Ãóq‘j÷÷IB¡Ôþœ*a¼8^¶Á@øïù±Ê2¥bkŸŸ¢ÃªKŸ4ÄòAùÁäÞ²+vàö1Ÿo©ŸÊ.>u„7…~&k@^–Ÿ@êy[Ì$—Fí²¤Ð2»‰CL¨ ðՖا߯J•t°„ÑßÛ3"èœçð~õÓïf³óö_•aŸ¤ÞLà¸6Zô®à9"BçI¹çNø#áuX´K˜I:{, ßß^ì ´¥?)2àÊjYÅŽ}l-9].f. ý ½ÒºÓàzÿƒï.vUÜ8^<*uzíjÜLñZ]šrAV·—„q~^·ä4œ—ǯÊ,¢ñMûÐ/­¢(ðS»{¨#Ø=pèó8K¾/,0C“{Y/b `4'Dáun[ß™çª=6ñUÝ2‚î-_ÙwëâÙ³GN5ƒ¡úݺƕTŒ‹ 7b, :´¬¯½aޱ/?ùh)¡É ƒÄÁ}ÚȤܯsÿ6^½áñ³Ž<ŽÅ+ã{=ym1užâ]@ÅeE¯ XPávóé¾öA[H×óù%ßÖK|¿4^»ËRÃÿÖÅ™5çgÈøUxÍ»ÍB Èwc‹ìô˜+Œ<]OTÀ[5-+³1è19í“ ÉÊP7ÏÝUW•}~Ê€rr'[\À ¿>ílœ':Glª®4%Rù>>>Ïq£"V²þÇ^£ðœ4Þ½[K©°,aqwa!±?O5òÈS!st?óß#cLVT½' 4¸#O½§çÿ¤#1à> ß»ŠIùO¹@oµ¹zû¤ Ô<}ÁÇ+bÎÕ{?¯R„…õµû­(è6öÃÅ#Ð$ŠÌM-,ñSŽõ5Ù7jW³Dihq3êû©¿¶þrHÞ”2èôŒnÚ)‹ä]7ö|%½ñ(ßFãþjí…°O-7ü²¢g´>ñ“7\VÝÚè|Ú3rå ¼´u{lãøÁò3%å¬ üÍãÞþ’m廆Èyìª"r~rîCtɱ¬}FVxÉwË~> LYf4í§¡Þ6ž›¥dðTº”vý˜ Þ½sIîš ^èþ¢àGÆùÊU¼už²˜ºæ|-_Â#›»>µš£JlÌýŒV:t_æ÷>¡\iŒ!ôµÉ·'s„7 u–¬OÛø)oèà]>wÁ4“‹Áëu­#âEŽ•ësÜhKŒ<Ïq£üôÿŸÑ,|juk«ÚCȨs ÉÙ£•·ÁÓtQ58±³]~s€äÙ=9ºç¨9ž;rÏ©3W”uíÞmcwúV>Ñ3G´=½»¨ÇÀàsV[ÊÆ´±=-ö ä¬’|ö¸‰‘„«Ìûl cÉI…€1YzÐb¦ ÷n£ô›3t¸eãÂ$mâƒÊÎ[ŽÌ›v09jú\W”‰¦}S•Ž`ùìDÂÃó°ŸG[ìÏ9[ô.J9ûМŸ¼Ä=.§0a6`ýzû$ ²ÖœKÐR¾^Rµ‡íQæ^ƒ‹n+öÀke˜yóê«ï¬6¾Ö?Ô0$ÈÉŽoæm€ž®÷+ë²AT¿÷æ²( xÖY½ûö¼/Ò( •e€gÙvƒˆH6Œ}þu+•$ ô[×3NPàL>;-âàÊé©Áf!èø»=Ð-dÃL„ÏJƒhíÎ2ÅÛ‡§Ôó³ä±n?¹ñ·>ã8÷R7ñíªç¸!5kõ ™Óo§UE6´ŽÐuR­2m€¯êçNñ;À–âJÉÐ%$à[8t$ç¾POŸNZx¨‹,G#ïìa3Ýð´1©È;QÓ’I‚è7JÉUsÔÉå¢è«+áØÃíz‡"µ1+«ÙG÷ÍxulîÚ6Ž’ݹGømqÏÏ»?´/Qñ¼DÑ©3*è~h'1l +?—¾Ý¶¦—¸-t›'Ö³xRÈ èÿ SÔÓSdܺGüx¶M³÷ÏqÜ艫îå'7º¥Ör‘ÿ"’m\øúéŠé+œQƒvie¾ñÝÝX*èpDßÖ/zžxS)ÿXá qàÀ¶¼K7Ö¦$úrÏÛ ùçÎÒŸÀ@³!û*!*¼»*zþ2»Þ=«J…rsÍ»2÷1g£€!Åš-­gGH9d(|¼”…÷˽UúG]@ã›Åå[s2pôÅ‹7ÜxõFmÂ[7Eh}þ„EA¥]vÛ/­7‚¬|¢1dK||­Ê×'ž™®Y“mB4Œ‰zwxÞ\6'—VÿQ†S/Ï(ü•Å^nÆ(æ¾ß¾uIuó¾o­Ã*¸úÕG¿‹þ­<-êûQå¿/”;hï4±3Ɖà£îÞ‡n˜ÀÕ$Z«=zíýmzf˜ îìÌ¢CБê³^ˬð¸·÷ÒSj,ø~Âo‡~ Ço¶Ý!óH‹âÑÃ6(^³lƒñ4ŸvúA2š8Ÿ1)v”Åò×o] x_:~®Ù¿é/UXl¢ƒÊûg)ä£Zð2÷É¥ÇÆÐäV™7G”ÛINæ¸qèÎÀè¹·tœÒJ²=E•#×)Šs? £ûý¥–7îü ZÏqãâH^S^< ?VôíûPʀ׿_åóJÛ£Ðé_—/ª0؃V8nhóöúô3G³–zñûœ¹;Òü¦dKŒáàv]ã†Ú9»xn½ñ2žoØ=$?£'Y_ù&Í0¸^ø‹u‚$ĽÜÕð§LŽö~<­)˹f2W]MáŽvèë4:œXk÷Û‰¶Øn»^b?Þ’Å|™x)’?{M¹#T¯=§yKÒ›ÅÌ÷p܈ZÈC¥qîK\†Í1&Œ™…›¥©À³ÔÇïn2,AýÕo‹‰p:˜w/¨|pïS¬Ì{¤ ÙY/ýŒÿic›ºæ¹uü7¦뛃&õš¼9®Ç†=™YÞòq:–=ðŽ¦±¼>6’ p²œš^ºŸ siWÿ„.•„ëžl—½G) $múáÃ}&\óíy]S*/že)f›â¦4kG¥áþË#Â>¦Ø×Ñzgö‚<¦®³óÂß;Wߌ«E§ÙÓí»Â!bÿuêm?ÉAƒî—½t¸9þÈ®r–€É¥ï  :@DƒÛ*ƒuhZhjp}jS«ž¾ô-ÓňÈwÇžþ0ƒ€½Ê«®>§âÊc¾¤Ý$ð¬ã"ÝÉ7Ç?¼óK¿+)áÍõ§Hûµ1f÷œãƒŠñêõƒn›rÜØz>äËI![<ó=þpG_\<µmj9¦®ïÖ[5b ±=¡þK>|#‚wøûl=l‚m7RïñèÃŽ9µ˜q2žyÈû0kúÄÉ‚ã†Â¦5ïîñxŠÈëÑå·Ç‹Ü»ƒïò8ù%E:8#¿JÓòâ2d?tñvÚ·;‘u8ýzÀ+¼?¹“H|=¬‘ÇqcIã»DyŽa/ÅÏ_pf`㜉W|TЪ` îÉ&ðïáw#æÚT¸&ôã\‰1^«þúÄ–Fs½©gß-¡ù£à£[wXd}¶mÝ Üx¼ïå.YhØôÊ­ü¯8Æóï{è¬׌š6(:QÐÌ[€Ý¾ÖŒoß7¶D¹ÙåBx”íOk>%ðÑp“M³ç®Y[PîáâW[™ñ[d‘¤÷‰«¯4‹6tôUÒݲ6’ãF-=Èø3/l,©h hÓÆÑ·•{â-pëÏöTú8Ù¼±­Ú>ãò›¶öø/©[‰g%€Ëe‡á+tÛuDOÏ G·÷­[ªÂõC-D* 'ÞU‰\."×ÇB5mðÁÒ_¥·=M0£pIÔ²2j7x±ÍJÓîn »Lú¾ÏåŽMæX­÷`ÿºNOyеœ'E .JÕe <1uµ.ýÄ9âÉ/ m7âr#DÞsòÎJ.3Æ x]5òa]!˜öÒ8n,”Ø^øÅqÃpòÀóÃ,äYt\H(f#A#ø¼¸=¦T,ZzÜá¸qNx½ûFèS¨eûž0Gã”Í|G8sÇæÜ»KAgñóksÄæS"ÒAË(ðwT,ó6ž»u¾u`Ä Mlò–¤H‚SÒƒk%ÂøUÁ$^S]Ö°Çíÿ:›‚ë]oóÖ :´|gåE˜ðAëÇÖkzí`{IT~½7 <ˆ-s„Á¢¯co«4 öiÀ³þL[ìö.úíHæäRÿ²¶þ“LØNS?ºö² 4ïÚ¼»„j {^}RÜA‡zÉÌ{#%›HöåW•ARBkŸ2Ÿ’VÊuWóàbø®jkÜ|†\”¡Ïi—ð¹D (•{ݲ¤™¿r7tý[Êñ´K9áV4­^6óþ–€ûª$Eû اU°Ë™ ù4}Á8]žSrâ6m3EMÃ[¢ý 7>œ\ÕîeŠSwŒ…=/É#sˆ7Ð\Tŧˆ‡¢ÈÝÚzb‚ãµúÍñô ¯Š]tr;épwÅPnÛ» —¿; ìK«ÕÎk·©ÃÍ;{¦Õ^rܸap³Dùÿ%-¶÷›$½M_7GR[ý¶“àg®ïDv¶9ª>Û•#§„ñïæÖ?‹ÖÆ"›>è\¯þrfuö ÃìXJ]j‹WžÚŒŠßO5å­Õ§Â¯¥k‰ôŸ¶0¾¢Çæ»Ï7BýùbD×i¤Xäé3õàTZúІŸd|9þoè:'o É·º‚æD Ï^3^°ÝH¢öøþÅãMܼŽ{ž®²uFâÚßœs–~(óÏR'XYº:êBëqòZ_ŠÞð;"œ™ay…ãFP˧ùe7²n X½š™¿tJ—PaÿÂÊ4öyU¤õ-£Â_áÍ›ÅoãWzJ ÎéTL=|‰éBOÂJëŠXxûbØÑý.pñG¿Mº€,{ÃU´ÿ·8v;’\ÓíáX©JØAw ^&:/óxA×û–ïË,±Z—ÇÄ2ƒ€`±”ú3Ü4|”rûž×/[ ”µzˆ«ÀRÏÞ„ÅÍ7½’*> þ›íð×}Õ÷ftM^qÜ8_2Bù jo˜gýš´ñßÞf£D Ìa(•p­'dŽ·9¿7Ý®øžei—Ý÷¸Î4ñ)æäÍ{c¦´¬0rpôÈaE”žcl>CúÛÀ™dh¼SàgƒÛkÌLuV™à÷a©Çj‡ÈH«ð%I›ÈbäŚ͖7ÉpðóC•_ æèQBq¿SO‡Ù¼c‡ši™ÕQÕÆf]f°eÓQõ(ä§æo¼9¸!ôÈ':ôgÒÖM À•"åsºVÃDWv“žÇÃMåçHY(ºBÌÓâ8 G[÷“n2€jžã¯µÔÏZh®XrPH-¯ã¸ñâ¶ýÍIs,õXÇ3I ô#7L”ƒìWa÷})sD®sO¶1™‘ª<:Ø7z¹ûéw3¤¤)Õ|<. šžâÃì"atÉiX{GYHøÌW9šBøb¹ÿÇst˜‰°n’åÏìUÇK«ìÀýZî¡£ÿl°;õœþ…G˜Í˜g_nÖ€çR¦-k9n¤Äf}:bJÃQ}ig™0ÄÅ•uW,ÎeDÜ2¶1!ÇpëP:ˆp]¿/2 `³»‹r÷Œ2´¶,}/µTÏ.éþ«ÎM‚åÜò&¿, Gä–N®Â2þ¼VK±KvÄcÉ4PÖw™)dÀ“‚»»VIJ$,è•Ð+Áé?{³$’(`µ¾|¿‚ žbÝ•;ö AÁœ¯­ùS¼!¯Y¶_”*ùwd­2Åa¶»_I¶<~Rs+S”BÝöE“·EQ§7³}÷vGh¯®zÈé)A òAœº¦B­©wŒ€¢ˆ~‘¸¥@þõëæ\…: +*=Zl5ß÷WhEºøCï`qÿ730Íýzϰ…Ч›—m!ÁŽMsK’³Ìñ•œ§”=:«Zß.术–óžØUÉqã–²îb¶è`éªzˆŠ×Ìn–TjQ¡jàVcû¨-|Ö7ãéì!ôÛ¶^ ~ ä§õ?3¾“ëï”ÚÍÙ'GùvsܸR~dÉW:èJDo—ަ@ðùÔäÕŸ‰§º @Ž—vñ®à¸Á ëªä9Ë¿­ ¶x•ñéÀA{|ì~ýÆ7U¸™L~Eô€X;D8mŽ’5üãê ZЫt_ÖÁg9kÏ,þQ‡¹à ÅfŠêMíYU¸Œ ýšçyt[@Bdf—F5 >—ó+/,%¶<ÓF-ãâú¦$ .þÝæ’=Ndî ´ßÄc +ƒó7·öxœO‰aç`RL;òŒ§W¦ÞÛ§j…o¯=¯’aÁÁ°Ûói8Øê%´î l¾CÄ=6Ød8ÐËåj‚™¿?K§qÏþX%9Y4i\ÿïv¬ŒÐ²ª7Çí1ïÆèÕt DT]§Z¿vÕCƒà¾L3í9âχí†ÈqƒÛü™èÿ¾Wêù5´¬e7bn0Ù󃈺ô‰½“ã†É”2ÿ^ŽºË~í˜=ÇÂaî™XÈgÀ³mµ]|öØÓáuÅTEÄRyƒK9nô<–T~|Æ“ºÚB;8s¿~*øòÈDèü6NsăÚð˲êœ ßï+¡ƒ—/ôº~6Ã\~AïËç%!Nú¬QÐa”ÈØŸq[FÄêE7÷³L!×þÓÙé‹t› ûÔßà sË2³†_ÙÁzíÜ'Ù mÜêðò뎰\cbß½i úå²-8n|¥Ç¿›áœÿç¥3³˜p"K®3¡Y®=Õܯi ¼þzaAtH"›kÀ¼%½Ê`«2äøÍmX¦¤ƒK1¹á¬:ýp<õ„‰6]sß éRâ©g,` ßÑ ipƒkäº0Þw;·¥dòRž‹ðIßiþtX!—Ÿ2a˜Þîfi!*GÏ®ßgŠž/Ù$»Ud}ï¾r1EËž÷·çÉ£Æ/žVAu!Œñ4T¹(Šžwß޽þF˜ö…n(°I¯½ÿ”“¿>quô'@I}ÝÆd -­Ú*z„“76Zè7‚þ«…ãÁWuÑY2hÛD—Ôk+ýÐIÅÏ׃« Ö“@áà›Q*çó ß·í@ž^[ÕPtPçtE•å¶ŒWÇzžSŽV²GsÌel±zÅy~òn*¾û®·Y‰ µ¥Ù‹8ûu ´/Ûññ+!ð¦n}h‰ Ö¼t\5¥nïN[M÷’1lƒm‘ˆ³"&ç[=-üé ‘Å©6ßà¸!ðiõy/Ž»c@ªp;}¶9pú7¢=Öm:{„ÀåǦ3%*œõÊj½hŒå ǹíôià±DÙë“·#\³½¬ýç D§Ì†Þ»ÓkóÝ|Y8´å2~ˆ#›uf™ ]úŽùm ¤àÏê{[¶³àÙÞ/7«[b¢‡/óMo?Ër]™¶Ä~“ø‡ƒC¶`öôø‹­†*`6sV'ØWw—«SýZHPÝ‘² ¢ÑW¶¾Ð£šãÆòëËÆ­9çÙÇøs„N¥6VnjrP8lJíucUã„}蝹Ò⦠žÏ÷­iþîE?Jœjí‚–å4Ò¡pŸÞ*BÉ MÞš$tI²àW@äÒ_7hè°±ô¡S$“Gß^ÛaƒQÄÞD8›`ئéZ¾T2Kóv¾’EK¯Äß5H•ÙZs,|ér¼’P»P:Z ŒÎ¿èo©7fÌ™ñ4¾9â§öಥs,Ð^”Œ•¤ÃÊýW1”fÿV|û!õxxT1†ãƓ֩ Y7~ý8‚Ù,ô­ãzôåøu~&Þæ¶G‹ö{þ «À–¨£sÚ›<À}Ñö¯nš9z_ò1Ÿ37ƒûÃ.—Jc°ÿe|8Ç€“7þ­t4gàý?.‡»å8÷òhúkÝfXpÂ5wGŽ$X)1—ä ãÍÖ1yKIY¨¿ñÉ…F˜‚Êòíû²é'ªÖâû’RXíl±ƒj/‹¹c6h­>ÔV໿^±GP|=m®§Û"÷3K²! |4úVd3¡aXsøÉˆ™©‘¥Â•ï½£y“¶ð,²Ráź¯ÄòçgÞW˜ \׬Ózƒ{ÇW2^Ã[}*¢¿Ç Ç î„ÑáËL|„ÌpM{¹'å ùÞÏÆÿæÄƒç53_ðÓÐ’»Åqã ûúz޹[å|#ÙœúÙvšüîk¯?D`‘öÁÁÇöTðÛ´Õ¥6Ó£,OÆØkÓ &pjñ¨¿#¬NÈeIå³ðü«üæ•m.PÎïy,SKâwNE¸÷Šc÷‡˜ß_¨Š`™Õ\º|+§÷¹®Ï$ŒàÚéðÏg•-ñèÆ¿û™O p°q¼}{ÂgS·M¼ún Ç•‚ÒJ©*0áyìÉWoYÔÉ:z*£‰öÍm2ùÕ[}ÈrÂ[üˆ ºž(O£ã…Ka-wThãùäÍ)¨ðòNá‰Ïã„è6®ê%Sšh³±G•«§£”œ,ÛV^*ï ƒ*_¿¼^.<\´Jœ¾A2R¥4t¹ÑGÍ&ÃgûS—^mµÁÙѹ½ßLðÌÍ;›çŽ’ñƒnó›Ø¿2˜ñºÜâøK2ø¥^I:õÌëÅ?rúg{þ£Zð7­§‚ùÊVâÁßDÁÖmk9nÐW=9û“ã×dÔno¦€ysCÎË{ýD7ï¬ýaŽce7¯VrÜØb3i}‚ãòáÙoös>Ïñå=”lÜoãt³Rh¿3d²8n Y/¤¥›ã‹ÝEÌm‘Zð2üÁšŠ'ư?i)e\xŽØ¬p@=Æ’7WÿÔ UÕÁGŸ{•þ¾5Ã5‚×[H‚´â¨AöaüuÎ멨,¼œ:|;ÕÚÎçžsZ’G‡ïs™5wxaϗ¦©wvp#¬Óóö§§lþW4œÇéÿuoî[+kÂÏÎãr7ôâ:¯‡Ð zÝ8ç3áEV/™TfVhÞÝ.g wWûÞØ@‡Çw“Â~ÿqg¹ÝÉ8S¼™ðÃðæZi¸ûaöÞ)¶):˜ß‹T¾")\cæ-Ë„p—sCòQ$é=œÙºÓ‚k®±vPŽýeߦ{øPÈŸn¤®j¾; ãZÖ£]^ê0ÿü­hÑœT¦ eë"‘¸³Jô½xŒg­ï§â…úð|«Õ$øioáôïˆ9f.QuäæVÂ\Ã×|C‰Ú8鴓ʮ¯¾=µýê3º=F•æ%(Ú¢rö*û¸TÌÝw5CK’ ´ÊÏ_§lAêx¶\ߣnB/¨«ŽYg‚…«3^oÖƒîèÌ–‡ŸÉ¨~í°Ü©2EŒ}µvõ§Wðé}Sk÷‰ÿÖ1¥Míñ£=sÛ-Nn*Û näŒöÂ6Ÿe©~eÚ:'(ÖªqY»ü7qØÿ½Îëõe„YhÿàÿÜp¦ìèä¸ñã³½’ÀA¦¨ü:43Ado/½j”ÚèJ…mó¶>8cŒuÍzŽN$ð/gQƒáó‰ë—ôsXXv£©ü ì±®JY& ·k,턾ˆ£âG–êCSE8\·÷ÃÑ|jýð)ÃÖf>±EyK\b´¯ìÆK>Éø•ÿääþª·úláH°“À´ \ùœ»~µ,ú&¼×|Ó@‚"ã ’ç‹Þêé ©8n¬=\œEéå…¬ú£,ÙmôùnnQ|Ä5UÍ*ÿ'.Í'DÇéšÂ8wfW«µ=QøS’vWÙ&¾î¦O’¤Îi+THÊë­_Ê‚ËÒWvVÐðÁ…‚$Ò2䊈5üÜlƒ­A{}Ø&È3öFtä8›Ï>°1™A‰‚]„G Çu-ªÍ‘Ç¡íî}:ÜIJÏߣåÜfúEÆpê§A™ËËß„¯çÑØË7ÔÝv[-¤ÃÒ‘±×S€É#—béÙG ŠÄ==ÍqÃPkó½.Žn »r¼n°°¬!…%΀ì#7VγñOM·åÖã*Ð"7FoöÕ½Üoæ9}¹P³ú»Ù^- «‰¼×Ac >$yü&6ÊRxnÑ(÷>oRKE·ÞSèh2CÂô¬œàMIÍKÙe‘)ŒgÖ:wÚ ËÂïÖBûtSøq5lTà2¶äÏÑ3yÁKÃ.?â³däžÜÏqãÞ$›7ÛöÔø‡ÿ3Ò„õì §”4[\v=þ² Âß Ž0áÌ09{à ûtˆ„%TÖ¾5zîG1âVÜâ4@Ãå¢ ìåÊ0ú<>mÁXÕž˜mø®ciRZíw°Æ˜ Ìè;‚ó,`‚uè½a Þ¤ou3˜¢Ã7)Ûg;“ØpÞÅ1¯$N–y^^ø±‡¿Äsã2 7%2)¸Q>ÓJßÏ"×ôI¹iX¡HOˆÇ*}`ŸšHŠÉf⃺ýZ[ñÄ‹;›/_ àÚä‹F¯íªü·ªœ4Ía«Dg”¼)žÝ ©U7c6õ¯•B5ÁÛ³<¥ÀÛ~?Ò™‰§/Vsïmãæµ/žìÜ}üL8Þ`õ.¢LRR¼ªø°gí³ûceŽPÛ{êÈýNÃbW17ÚaØí†Ð@3HZSaÙi§_FˆNÅϙβÜ=â•â? xP{SL`˜&l~~u¹‹ (_¼²2…o±º®-Â•ÕÆ@7å=YUÏ–ãÕ•ùº†ØSrgåP 4 ¯RÏ¿ïϹ‰ÄÈ¥AYð{hJáô ®¥q=Ò Ø»¨Û·T··ž½ûÚnÚ q’‚å^_,ÂÎ;¥oqd§H>V—w®!Póq²æú!¨Š¿üµ²ˆÓCBwy„êƒû˜¥K ›íÓ£k¶Ñ ç‹à§Ky,_ÛAÏïªn=RSÏ}ç¿´WŽFD~4Y†Ýîbƒ‚b–ðÓN+(~n4 áe®Õ>ÿ Óh0Ð^–çÛ͆ýú7.¶Ë ÿ´ÖÓP2øæ=•ݺ’€cAZIêâxmÜm€’k†L4Øc™÷‹m—É[k‚c0è#ùG—Že-9)ŽgDó—ñá/£mU·’áùYÒÅdm;@®»÷†—˜Âñt¯¢³Lx¿ùsø³wàßöáQb,/nr%´oE™Â‰3ÅgUq¹rã _r²£cîDZ 0™ê»²XºW\\!EûÏÿÜøÏÿÜøÏÿÜøÏÿÜøÏÿÜøÏÿÜøÿçÆ›å%â’G°îô¿$‰oÚÈðRqMMœ&¢ ,SAÖ•nÌc£Waìþä{Ú¨:ÖÇ·/‘ §)ùÅ‚¶°ùNˆKMª:æfåÔ;1CóXÅXØ=§Sp°upge¤ºæR¯ìr†u%Ö˜:²ÑçÑKt“#:R£ï3¨p&Ö¼ü³ú \õ¸Å™V@ƒÄ÷yoN8AE…IÚ>MäÒ~"…6¶Ô—½7Üaá¬Üë)6J~ð}Òjmƒ¿n­ðûk§>:‰*¬Lˆ×øÖbZk׿š i¡€nK¤Ã«¢'ïZA$Ñ«¦~ÊÏôéÛútºBÆW;jêzyàÿË¡ŽÏÖ~[zNS f4ýíšw›ã‹‰ å„´`]õÒ>Ó–¨²Õ¼î( ö´,ó42A.rÿ›Ð‹v°§}ò»Ðru¸ß×P"Ó*‚‰ß…®ßÙ¡g=VÇêªSI3"Ý7l¯þmã&[~ª˜wßÔDž´£š›h˜[Þ°Ù8W66j~ûNjÊGáÔK,6¬š\¶é°­,.x°øáïÛ¬ >~ì˜ëP'ŽÏ,™öþwÍð~¨Q¿‡Æ/â€úYƒž&¨á_oÜÉ¥€ÜVc|=ÇÅñ[¡1:Ë‹ƒ*s™B‡á·ð.;X»ÂùêÎlˆ¼×³f] ÔþžúìÏe TNòáº#N›‚µáݲ}ûTQ+øÈ@g< Ôj6޶À(åÝ‚þ"PXrëƒÚnüçÆnüçÆnüçÆnüçÆnüçÆnüçÆÿ?7ÐÙLÜc§~XÈsó»6ÊÜørüû41xáöš)I{t¿¬™2éÊÆ+J§7”¯ÕÆš†/Ý—·¡WsûµÓ¶p¢IE?¢]/ËÝ*Í7Cÿ{¹©qRê`îRU²;ž‚þkÎ{.ê`†Ó£ï;a;¯7«—`£³Ãmç_IŽ8_±Ðÿ´œ kΘìå[dÕ¢ó$\ˆ²·5q‚W±ƒ\˯k⦿çÎH£ËAÒ\+ÇÛ¯Žg«±q<âÖQË-6øBéÆbÀ‡®Êö²Ü|úßš#P]y0îƒ>K³h¥AéO¶ã¬3X4˜ñlè¶@e“WåUU7Îkü ?(Û®Wî )æÅÑ5Œ×JÐ-q{¤ïŒ9~ûæo¬õ[¢ÃªðY—%툮ÛVÀ‚|ÞóÚ$M0zy¶–{Š8gÌí]á«ÝúÁ»‰à|Ûë±7~ ž©÷vÅ7¬îöïÖUBóÆ}c2®âPØlXžª‰“ÏÚ¶Z[ ÿ¶S•µò û¼fê“& ¿‘“~Ÿgáºsjb¯mì ^—ÿ×{PP¼ô>Ž‚;ˆe¶Pùk8a—>yíÚ§¥³£«šïo`â!£ËÆ‘ÏViŸöu – 8LúV}ÿ™ÀZR¸9,Þ»óŸ)vóm`×[ÁæÚ.c{MØsá9ÄZFý6Yqaé®ú}7L/d“ÐeÂU“ÐÆ2zÐo¢°Bs.M_ÞÈqãSn­pw+(m´Ðe‡‚¹k*õ³~¤xÇ/mú]~8MV7}[ñÓîdd}5Ìl¢Œ¦ŸpЄªÞíU~& ÝBÙ«Á…^dæ-íg`¹L;/Çßí²øT ‘au¸íüEièN¼;øÛšg^«Ä<‘…â)Ê÷Ð> ßÓ`€ŠyÛMVõ+s¦U—:€Ýï<¶RPu¾cáe°`j±Ô}º¿="°¢fÍÕ»M6à"•q¯‘â×~ÝËúoæíÙó— …¦zÝ6Ž4ؾU3<ñÇJåäÙêèÀF™e‡ôðætU);N»Ùþ7@Ëð¸(6üJݰc÷?+ð÷þ1roÎßð‹í¤AÕ|ºaÈfÏ*s»!‹Y±ŒŸíÉ êÕ1»‘€‰-›î†KàëàÕy5føÕÈðξ;SDÎæKZ_ MЦýžç!>ܤ»ÝÁ'_Ÿ®´<œTË‹«Xª«8n˜\82«I·ƒG™ÓÛ÷Y˜€\ÒÔ•ÖMLÐ|2_ÖçÆms§äOòãñE‡ÅÇ×Má‚ú…ZûuªhË·Nÿóv2\¼OÉP¾i- õ@‘ß1® n|ÿ¹ñŸÿ¹ñŸÿ¹ñŸÿ¹ñŸÿ¹ñŸÿ¹ñŸÿÿÜ8Ö»øÅd=ð¡hã…Þ£˜¿j†ð×['V fâÎK¬aãBòŽ×Ò´PåØƒÀ$w2$º.Y5g ËŒÎlÑ—"ae#³Ö¢Â ð½(ý:¥nQº´í´ÿ~´—HÒÅš½aÊñÛA¶aÚ;:Ÿ\]/R|Õ‘s?S6xPaOL¤üÖag\<á+Dƒ×1å'©N»írüú.Mä–þáqWE/?3¨¼îfŠ~Ú›GØX} ÆSëŒ .¹ ôèµ4>ÝÃëjxµt1º´Ðxw9ý¬Ó¿ÆÝ¾Ìw7䟄„*¯¾Ðwo_Z óšMfaù®ð©ª8Ñ O ”_ÞãºÀ‹TÇÏËGÍÕ³£ÿŽ9Æ,‹ßÖ¯ r×Y?·Ä˜™É3•,hÊë<ó{ÒW]SΗÜk?§ƒÏÍ&«éñ“7ô¬~"Yùp…¬ºkU‘wçqõ<ñä¾´ž><56¬ì&ÁÆC?ã¶jâ„íï·-`²wå ËÉÃ]}~Txì˜rè\: Cß’h¯Èv nû¬l_„ ÄgÛ©kì¢àÃr³ZÕe¶îú¦äœÚ›ªÞчgaw¾½&¾]úìŽ$ÍOyUw‹)QqÿŸ¢Ô×êô:½7æÝð¾aê· ž<Ó©ÿõŽð–GÝQ§hBÿÙFù&EîåǸpdYŒÔg[Hý¸òÃMs&„?;ØçX¯ ôR¿@ $eÅ,ºç.´»W¾!kà´‚Ø};,_sG»ØÄ –ˆÜÛú8Ë?É^]ïq“žwœøÎq£ææ¹ˆÛû:ŽR5AónÆæ+›M ,&`ó›]\8öˆøÉÀñ%¢n¥F¨”¿æ@ì·exõ<ϧ¡Ò`ÿH½zÊ«¿lhV¸! Þs=¿` ×ïEº;ë3 aûÕÚÜ "øËg|oã2@·´lCo ~È9ôÄÍ ´Þ¾ÿé6­&¨‡Ëüõü‘”%ÚÀû—¿ÆÇ: ø,cï¿Jž$MÚ}"ƒç©ÆÍ‚ÍÔY,aAqVЋÓ~W U>ÿpE=oòÙx]’½Â>ûö`ON王1lHø¶}£¡„5lQ×]Öd ŽUiÐôVýâ­16t‹¸JÉáv÷?ÏÚ)dàqI—ÿ¼õ'®ÿ±æ€† üÞÔßf†¥òz¿(”)bXXz•Ÿ ^*JãêRÀ7A÷ê>Çœ¹olNóâê‹·mä¸q<÷ÄÓUvv°î— •Ôb æ“_+>º0Ayîßß!ÐbÎÚ)d àNÛ ÅïOM!¢}Òr*NžÔ,[GïŸ7>³@U6¥âäsaб ìº8BýÏÿÜøÏÿÜøÏÿÜøÏÿÜøÏÿÜøÏÿÜøçFßËØwE6 ø–W|Ú¤6& §Ko¨›!RÎ7ËEì 7ïPß0«Ý /i¡ªuæm>s2l¾å|Lí¯-DFßëï³!á®-Ÿ¿·šaͺhn•V5h\º'VÄ—‚»6”êaIø²‡K·9ÉQ¯T6žé¸›÷Ü{Ô}´NP!Ók݇¶Vgô1¦^ÙŦÁù°&õBº¼èÔ¾±KN ÿÌÚ¦^j“Å^Ó;.×Üákò-öÐ׌°“×VØ ‘lRÉ£0q¿¬ä®¨šñ|§]½Ôj±ÜÑB×ÐÉ®ª'4`Àáè&bì·j›³(±@ï$¥£Ë“]áKfÓ” ÊÃÞ´/V퉼è·ÀÏmWÇ7ŽÜÊMæ8ôbAU§Eœ£Ã”^[âwýì%í,xUóÔ¨Å/§þè °›ý¿D&廙¨¹ªƒûA\æpÇü].6oäêL.«æŽ{RpPW §?tÀÆ•âzÖðb³£&žKûwœmöVõJ‘ ¦eRžI…s§¼†(§Xx^vþóz};8rœ}ëí587x—+.‚O‹î¨y)ÙŠSó‰¦lŸZ~q\ÔiŒ‡£šLŒZ/%óEÏ0lzå} p»:®ùT½‡£Þ¹ s>ÜÔk‚[¨)šmç­àGó²ç$M0Ü2~ý}„ œËyøëøW.ܦö¯.’ã†Ü?ªÌŒ=ÔóÍ$ÉûtÁálƵ[¸7d¿lÕ+už‡ì“¯ <°emxb³2ÖŸöU1¡ÚÑÛ’ì1‰k³@ñcHówÝRÂqcͳ<>°làßS+MML^k²9ÌÜÃm¯×saô«¸[ÍÓ lŒ’šK%áäBÂà§kËðŠ[ŒýÎViHn½(š£À‘Ü)ÏÓdA·Ñú–$Ë.°ËjИ§ñ‡½Ó_Šf¥ü´gx—!n¶ƒ `ÃÄ…gæNÀ/´5dµìɽM ’ êå¾2èYÿÉÊpˆ€°Ú¢úó Öä•“!ãrêàRX?;îÀý€v_®ï*Ξ¯Ž±øÏjõ0UòX¯Ùø~É¡úÅ TÝZ|òê6 $îù¦i gCrM!µ‰òrØ” UÞ.ï~±AêÕ“Òd9tÿ”ñ6Aƒ õÓa9»ØÃ?ëB›ÜR¾½ß ycÇ"#nM>wþ}6FeÅf?S1<œ-bÐ(ŽÏTósº|x‘Ñ'¸Žã†^zÝ›ßnvðv®ZR(ØÞ¤ëuGQ™°ûxWê„lÞ§Zsø® ¿5³~g .1kCTT1²H‹•éD†‘_Ï^~´@ÓR¯Þ÷íwÛþsã?7þsã?7þsã?7þsã?7þsã?7þsã?7þÿ¹ñi¶<Ủn‹R>Q1«:ƒ1ͯ‰Ybi3¿Œ“€=ºXŸµx¼r˼÷¯¥háÕÞUVŠdh8¬¹ÚšÛì‹„ Ú·“0®êäž‹#føñìWR‘À…ë9Rl Ó­W]ÐÃÛg‰'žÁÎàË[·•Ï„ž_˜ÉÄ #F†„¶‰ï¥‚¾®wñÇÎx©'þÁ´ ®m6x°Œp‚m‚ó5M^ZHÁÄJqy<î@²-p‡‚OD3Ï  ¸Æ3ûúm0Ádßmÿ3꛸¡E°K?h$ø‡4M).Þ9§…E§ÃÌÇ®ÐàÌ2–ΖP(ùÂ;%Ýý5,çƒ]A÷’Ö'½òpz8êÎó­¼xý`ä…[Áí½ASÝOs¼PÆÕ¬S¡ µåÚ‡Õ2,1zoWè ‚–I)œ¾eŒë î–ö¯°ƒ= Zñ_Õ!{¬)¢{½^š© çÕÓ€~™ïYb¯Wk´lxöEG ‹¥–æìô‡þ‚bš¶&*>z*édŸßý{õýHõãÅqܵ/׺DeÄu—k\û\\¢²¾Òú˜Q •’‘’¬’¨Œ’ÐŽ”"Ž–JS|Ê(#DKTˆâ{ÿŒÏïÿàüð|=N«,=¥àG¯àKðŸo~©6¨cléÙ©b…鵺­àq[Vòe7C¬\ìÎ!Ûµ©Ò´zÄãoœ3÷~MƒíÞ‡V8ñóÜØÙlDUÐÃ+·ÇÝâxÎ(ÍÖ뉾i¾°‰OdЮ)—3äŸ2jþp]z¼¼l”»ß¸H4DŸß0g@È•â¦~KR^Žk|æ·…F¹+¹ÞÖ`‘‘qSÖÛ‚æ&Ç‹’XY9ø é¦#I”œN}ÂÆáÇ=…Œ¯vxèjùÐnHÐÞ’¥¸Ýw¹—Û¦“ =sL;’çÆÜ»µU¶…€AS.lIu˜½¼È¬u7þNÆqI$ Ë,¾5oŽ­í*ò!º8{/ ¨ƒ*|>êŽËB¢Ï°Lµ +»¤þÙ+nËÜ%ª–éSê”?óÜ6/ÿxwÊñµn”u€J‰àO4C¬Óà¾KVæBØeK·Z: SØã´Kl ?¡}Ù žÍš+šeCp\€tì¬6l{ûËi8“ aOºa…Œ)ȈXLß¼mGþ‰œJüÓTJ–üßGJÛ'-Q‹’Ý'¿½(¥#÷ô¿<ÈrJÝr5–%ˆ™¨&m×é°ªQ–‚)¥ýí ›å@œHkhAG$‰ÆK0!Ãûë¾h6º˜`PPX={ÝŒv•¤=;¥0ÉîÐ]tÊ©J×nõ¨–QĪjûŽéA),úÔŸ=²D-…&_êòܨñÿïcßÞ]‘žÐ…ÀG¢jË­áëÍús3°nùÜÛ2ÊÚöÜ;®¯&¦µæfTPà»ÄñûFL>«_¯ñÓÇõJ¢ç%ÅÀ[â‰_BááááááááááááÆϘ”£VQETwMxlÿw9®±™´Ý~sšÍÞö¨`dÏݺ›ƒ®Ö¯>¿RÇ#Ú¼¬š~(dw.xQ/å¨bß&yõ`a&ŽUåÝ <²”³MÙ C¼ù=!%[S¾<éï 6éÊÚŒ3ʹ.ÎÅ•mï”f6Ï…™Ê',ŒÜ.ëÃg §œMß×p¸ðñsUá‰t üà£Þ`«ˆ×•Œ¼†‹WÃÃ×Næ s*þ‹<)m–ã=w"éðìí­Ï;ùw/ ÝïOf,s*ûp‚ê]ýç–j öòåv:ÇœÁ¼vÒT-Ö‡V—œqpûº«+„áÞ•(‘*wAÜm'Ö9zV llÎ{zI˜ ŠÍìÌàEux¿gŽt1†…œ JŒ5o¯ZÏ ¶¥é¡¹Ú:a);¸~òÒ63UPüúúh([÷F •Sƒu¹¿×ŸÙܨìñ•ç†þ¿‘u<76m·Ë"©#¿7õI“‹ šk“V‘w)ÂÖ·Ö!tic8#Ò›‘hƒ¯ÿ*ÈWIÛA¥Ëæb\d Ë4ÉÎÝv†¨?òwWÙ´ Tº¿´î—´ÄE+ÞÞ>^OƒÛÂÖ¬÷VÈN}D½"¨‡¢þÚ[CîbNUVÑLY{sï!ñ/dø½zIEW-/d¦I+YÀÉQë]z¿Õ@£H¼79™v1Yjë3I¸¦n¹‘#ϳ-K!Ö•\áÓÕª B·ëÌê,¥ñDŠ‚NÁ Ght¼|lê3_l°xOæ`ªkÀU±_ú—Ä.˜ð´Çã _,ôØ=Ý{Ï~ã¹1ZóéêÞ"@uœãÞ™Uƒ e¹8²ölMØKÂã™m÷"(áéo¹ÊSs’Û¢5uÐKØ{ÇWq9ˆò½2Ãàõ.C§@yèKÜþAuAhÖ>“×-ÍAçŸg£¬Õ¸¡\xlÃQÈ5Û×j(kˆÂRqÍ1$.h.y¤¤¤Q§ô Ùø¥¹ÁS+× d®e—5Ÿ¡ÒaÙfK¡LÐ_î¯_¹À‚q¾fhªÒ‡·$ÙÌ7÷æ=¯#k£Hï q?9ˆ=$,–IG¥Ó/î'ràXguº°³%¬ý×mbÞRÌnu–Ðþ°`I Cf·¶3„œÂ©¸Þ³àݯßp5ø\¬kü{wýÜ‘HAŽÊõ#ybL¬ñuþÛ›5Á.ùùÉ#“×[vÕ„‚"Ö—ë¢ý–šò ÆJ#(ö$ôѻÎ Õt»Î3ÐöÚ¯—>¬ ’Égþ~ YƒâK,;²àuÚ ûfÄP@ÒêäžïœLý«7»TÐzÛ‘ªÆ„îw¤‘“,lÏt‰bÝ&Cȱ°°î           Âÿž‹O›ºS±èîñ‡÷ù5Ñ~ßÿ>ëËΰ‹oPF£88i!z"Žƒ”°ÆÝ{¢Ôq•zôùˆûDvÎ#Û¨‡Ó¬gƒ*ž»yËeM Næ7ìX n}ÞbCl.È+Ü\¦BÜCRÏ}€órs»„*o^â¯ä¢oJçÎn'#H+¼zëp¼^Ê»¡t¡š=e±NÎ\Q¯¦6ÜÑÀu¹Â¡¡JXfªes€ç†ðázŸ+ýlIöìÙonš{äßl¤ƒàtmç Ïw5f³è,È*©õ3 ×À°{Æç¾ì0fyj]w…3lÈ|Ú ´…·ÓMõ´]àbÞû š"X~Ý”q†-ˆOÐÂý” ¤vJ¥m… riuÂNªÃ»Æ³J¾~,ÜsË+g…º-DNì«¿³]ûvT ªÙAÕ¹†¸ %ªiqÉò»¦8®ŽœQVçç¹A‹]Rõ;•½iÖd®Tk1Jý®¤qܤÀ~¶áç@Ÿ–ß ùй™àƒ¶öN¥E(L›êOw6†A£è_·Øà\Gòó9;Ð2©£XYÃÕè¦4SC”-uü×eÄ2 \Ã.[bÎù5%6h[¼¥¦®Å snÍ=õCÉÆ’Ôdžh<”§#»õYó³-ë=>é™ÀÞ‹‚—¸ |ò\ê* ôIôSQÃþà»M ˆ97™8ÞMBvü|Ñ1’-”Š2ÊZ¢xÝYѲz¢5:_o?¹Œ‚«ÖЇóÜðÛÒ’#ÖÌF¦š±£šœìƒm*Ãúu¨„ñÇŠç†l|Û«9öôÐ+Õqø±¥¡})Ï{<Õ>ªA–Ëò—à ȫ>`¥ÚBBli/·’²À÷Í&MBu‘­“R¤ñ‰ŽÒžªûKirÐåX·fýF~ýåçæ*B¬bëÀ>]8-²>7ÕξëTÕ=‘À¤G>> %ðe”UíH2DMÛpù£#‰4°¯ŽÒÏ»›>ÔžaãópÊñ‘d+ð•àžZð²rÕæ~&â¤[´·p™ ¶´¯÷ÊW\_^|î ”¸\—_Ú¹Ð<ÖhñdDGϘz~s•ƒ›{=DÒñ^êyéÄd”/°r6X˜EÍVZĸ2kï² kÎáÏn!{ÈoÍPîh§¢ÃÇÏ-_G ÀOÁ!;) iôú~‘ç4“§ÔUabXOD‚µÊûý«Ä„>_=¤ô< ]¬ˆM¢Õï…¥±iåÙ[+P£¾Æ{ÏßÙqavÐX»|éR]x/7fnZÛ×;Â>ÿ„ƒ´Ÿâý#ˆ³y™$o†¸ü&¬yÊJ‚ V:>ÿ(j±0³&"3:œ .l¥û‰„„„„„„„„„„„„ÿ=7¼’#Z;¨h˜¾ý¬‹¨&öxw†…šaß¿Y»ÑyŽƒý¥¹%ÎÉL»E®•WÇ/¹f_|€ÖøÝkõvð}EÇáÞ!U½,ø¦¢¬uÐÝL.\dÞyã Ûø/¨˜»š mÁÓ)A² ¬Ž0«vRþƒ[•VбmÇÊ.F¤ø0&­W™ Üˆ´ì½xuHHˆ\ïÈÂüçóG-l¡®=fZb•J»+g˜-²‹É$g#U8´ÞÇ'*Ž>’ß$ÝÇUAgK¨eÛ%¶Ñ®+ ynØÄ( ÏüwâÞoÕðiDþ/º— *;ÉûoV„ÿÝÐ'Ï$ƒ\§|냌١x iÚÞ¸¯±Rа†ÑìÒ²Ž!έ˜~~¡ÃÎì¹ó³t“%Ê;„vI¤ÑàÇÙf¹'EVØ"¯½±r@Û ì„àKC¤ä ñKUßo6—z4ûÃËt ¯Éœ`àãµQ9ç ,€ädbÛ®µù¼s0E•ãWÖãÇÙ¥Ea|¶p…ò[8<ÕNeQµ4a›ðê>q,ÑN ÛVæѹï}¿Ô³±uáÌNU[Þ`ËÿÔ®ïÊ=L¢´ì±b!;ÀJsš]»ê|ö~ž±SûÉÅ€VîÁ c¯ÕàOwGÉt]¢åæ!ǧÒU¨èÆ]â¾ê°.)÷|V,¥#YB~-G¼>0‹Ÿóa¼ÃçáÇÆò°5û™k“.äV©^w6‡}3SS¼$1˜ä—UòÄ-ó%Ì¿0±L‰²[ÿ™#ìêt?v©KÞ|Ð8pôïî×õS~QV î²HÏF n åÃùµtÈú'd|^ |Õ­ßžuóþš÷Ê­6 wLûÊ<æÃQÙ‰^º«6¦ïŠ¿Ë’ƒ£i/nr×Ñ1_uK†E,äw¿8f ÈJC…“^9>bÁ瀵ŸgÄì¡6ÂÝþ•"º¶ÿ”mw¶¥¶e±ÐYj2èù}Ǩ‹ÃITê£ìïì~5ßë…&zxðtçǾ¥ŠhPYWH–‘Fo?ÛÉ Ñ¸¥b\G¢#”–udøFÛÁýôëòËWBvçÔ“…VÀŒq\f.áÔƒþybhrLPïËX9‰OÏ©`PŽÿ«œßóã7ÍÍÂE ­IJd8>X²ænááááááááááááÆÏ©ÍóF T¤Ò/{ÔKhb~ôÁ‡YC3ì©Î˜ƒ:Ó,ø,xœƒFªUßI¼¾WF½ù[fû}3z)vPJwy%ª†ùY?Òܘ¨´ŒrÅ„±Ò®zTù‰5Ÿâ´®#GGžÿ¼Ñ þ¤”4ÆJsð>gýø·p.ª*›ÓT ÓC¼µÈÁ Ǥ%Úβ AÄùƒ³úLÕ‹4—cäÞ¹½•Qog]iÏÖ£ï‹ÞpþÏ^¿CÝx e+Y†ÛR–±Ä`Œ!3öÏ™…±·([IZ„–»R¡e)Y*K ‘H)K"¥ƒ”(K ’(’õn¡”Çó_|˜¿à\çºÎûu0fL}Ä5ÞKó*É¥:Pû­WŸAàª]ÿÞ `‚—ý:‹§ëvŠˆúššB Õþyÿo™~DùÅd EiÚçàaG`u˜õî T€(–äÅI¹…¸è™žÍ{EHoÏv7§gùnuèÝ!šªbÀD½’éul ¶:I30À;‡,šBþ¿kšðz 5ØœöVÂsL#Wð¼>t«ÁÞ©¼ö™bb¤DÄŒ£­„¢Ü.ˆ»HB†ø·3º—Ș»ôáëÉÀ´º÷u} `6¤s­Màï×ÿý! •ÓÛ3‡Ù²/°Æ9ËdQÅGWÅ}w,Ö©cA ÓžžHK|(õ”“¶_È^®Ç#¬ÐòGÂï-­TÜ¿bdIN»1Šm8N.®|ç9øå¼T|z¸:‚†¼Òá^y ˆŠ4UyD†VN{ŸZ& ôNoË=,ˆòâUus,(.™Ý?’b þ´HÊùNMxxpôªô¨ º:OÖkÌ»q÷(Ýéd - J~Þ\ü68 °±ÖLN‡Þñ—´ÁϹœçgMÿ#R;º”’&œ@»Öi¥àƒÁ×mH†üíû·ÏÓà¨jÜŽjoA\uÆLUÕ«âÚô´/Q‘ìW'´—‚†6Áƒy>²`2ø#$¥ZõP‘¦üëøW¨à#0³r9Øo|á Òºf‹üô²…®Ç Ei]F˜>$ÒJ”ò ýªãwO¦´5Ÿ#ðI'Þâg-e¡QËF_%;ßI=¿ZÔîÌÑav¬D2}¾wعa­,È•ü‡–°·;ĿۭƒÇĺ­4e!ñ1žiIÁ‡Sµg¸Ðøæ}¤B„%ü¹ùàúļK.ì\ÄMiÊÑ-R6`ñ_êÏšT™Õ+úùˆ$Â]¾¤ðúõtôå@”RpPN ŒðÞ­ŠªD[Zø×>  nZ«®€=Ë¥páï‹m„0£:À[uÞ ½ƒGràÙå¯{69êCeŒ‘ÍŽ{VHz6Bšws ýW°àR ÿ‘u°Ã…S“Q©±‘ËQ6ôã'´;BÝ™˜øHlý±fQ(=³ñn¦ ß ¾|7ønðÝà»Áwƒïß ¾|7ønüï¹a¯^ô =K"$«•IZX~ÀTqjõ ‘7ôH»uŠ‹‰·®ÿ;žÊEŸQ ”ßHÆÍqŸ~¿L¢ÃS‰.År¨Õ¦“qÃÃsSð`É ”Pîo:ÕE'',ÓAU= ºe<íáÉ>iº¸‡’iÞ‰v¸Mnq展Pgø©8E×§ä¤ E0áë¨wìu/;ð˜¥>°Ù ‰äû©Öþß–#Ç”êÔuÙvš¼«I|ÅÅ#ÜòûêÖèM±ZÜ´„…»2¯E„øâþ´âÑX&œgæú÷Ò5ðÜ•hR´²)䆻¶¨9Bè ³õª ¼Ô}¢É¼“V|8£ôd¯Í ¡Y¥yÈ)ª"ô<[>t’¯?ºH_óP‡òÒ†sþ²L,ØIÝ·› 7(»V—-1À0‰ß}}l0}Øi´Y È*•ƒ)­â8¼ï–ža£å´8Øî©$²—~½½lÞÉ·~ÃgIHPZC!£Ö›×}¼È›³ÿ,®F÷$Ô?ü6á•åûö±¸¨ «ÜÃUÙ Û¬Á-5mŸ¾”1~×8”x¾˜wCö¬øxÃÉ}anñÚ°hKÇ­F+´‚tîÁj* ¬g,ÚùÞNwÏ0Öd?:~Wv«{6´†¸CF»hø-^NBú¯9tT~~è[H†Ù«×‚é×ipES[÷Tƒ nÒÒ]}÷/ ºÒy«ó­áûo!FŒ&üö½éñš„–O¨›Änò E×Éiö&—Û26C(3'Ijo‚Ýëé¼ÅÓ\ |Ò0¹uŒðÍ+~Ñ:ïÆdçxù¢l@C'j·Èðêìsùš ªˆki+Ä« V]+,°/¿öKO)IÁÃt >ÿ®ÑžxDnvê‹Ì `sÞþõž"r°^ºEólJ„ÇÇÝÍ¡mŠÒíà.‰n«}/•ˆÎ»vvÆ’]o„ê”ëm?Ry`PÌuNÑ)Ûƒ†%±Æyɨ»[ÁâÜÕž½êåû~î=EêžÿšºèÀT‹]Ôt— eÎñ oXÄÍûïl€ Š®¿hBœÓA Áuœ1 YPŠ[ ij”Ï;­Ï…ú9ßðÖ$Kð^µ°¨OŒ¼&g‚®ͤÇr6PqF'rHR…¼ilÖM: ÊWòf² ˆÝÓ5{³Jïjn¢ÌÿåŒlÚ˜ŽÉ8an\q|É'*6/Ù!¡½BE–ÆkÙkKá‰Ô¥Â/$„ÐÇ?j ûD>¬¼M‹ç@=¼|Ü«ùeeW¬ “*czK‰£ÇØÂ2’øS{T”÷ Û~ݺaâÐwõ—tйõJb/I5lÛPQX]gz±Û˜ïß ¾|7ønðÝà»Áwƒïß ¾|7þ÷Ü8Õ´ª%ÿ”<îÈÿ,þ^^ Ù¹¡w·UÍ"‡žß¯ãâƒe]Ú³¹xp߯„Gd?LL…Ò!ô¥Iå*E¬P:ÇÞàIF«m΂Ža‰p¡iÔˆ2Èl^~·®Áµ {6è¢ÓñC›ÖÛƒd{`•?²%dÞ¶Ã5ªµ»ç¦Œ!žN÷’¶Ç¶“ŽAË72!éèøo[;(r±~û4Aëý„ï‹‹ª Ÿ^•žÛ%0¼0&®×ÆÅädÕktª2xd9¡bBÖ”)O¶È|¸˜Ç„Ù…Êö—i`F`³·û"SàL l`;‚æ­(ea¾dÄÔåÞv„J[_Ö 0-ý-õꋾ+16Z#«ýï³ksè·–^ÊQ‡´T–çS+§î¸G±A!wä¾a*~0laú#=sÔ 9çîœf‰8~x=]ã]©)ŸŒ>)#¹³N+”ða~hZ>nÉvid(q]yÚ±hþ¾ »ßë(á…£}YyX`ÓÛªïYßkÚú¦špÍ"-–u]IÚÂi–7xpK+vh¢€@ñ\¹—ñI\üØôâ~†!¸Íêçâ²ð2ªùâAB¿íÑ;ãy7f|ö¦¿º èxQùºÌ%2´åh®ìΡí¹ò‘à¿‚(lÀ«£Z`¥º÷ù¬&*¶Ø§¨7þÔA¥B¹àñó²'¬R:‘*€:º W]¦dAòhýÒ>TXžÙím;|ƒlu»%q·ç4êkñ Xö©ó¶^Z êê|œ*+‡w–ŽéåìŒü™>v•ùJv°¿çÂ)rÙvl»Fý·ÿT†TÐ!ãó¹ð¹L°•_qª¶‹ý‚o®5Z ¡ŠV`Ô…[:ø•zGé' Ê?«´m¡à ½þ7 Aêï³r-A¦J"ÔµØÒF%ÚÜ2á×à*£ãJ60× D9¬ˆ¿Ì.SÓéP{D¿—T@À˜Ï³m«“ðÀßÓ¹ª¾FxN2Èû¶îñmýR¯×©xþ¶{¬+E5›Ž=~L—™5Úß b ÞíÏùý\-òx©8³©ïs¨„¿ÝZz3Æ 6Õô6Vªñ rØ.ÂZB ׇýÊ>JoÃbÉœåø9Œ%ÍA:X¬>±!†‰ûŸÎÐ…h“Ê7aËønðÝà»Áwƒïß ¾|7ønðÝà»ÁwãÏ ëÆ‘ç»åñK‡ÆZEe-ÌîØüµžö‹psH?¦4ÌÅê.‘¾œB.ÖÙ ÷A†Ú[vÏcžZ-ŒSRæ@Γä%´h2¾Î1डRƒîì`½2PåHne¥F(÷mr§¾«rMäÖ®±‡Wß4¸e³”ÏVÐo³Ã¿»%‡ÞCMœèMöw;ìœXTÙdÆ„㸛·ÙAÊGÕFí&MU6o=kªŠÖ'«RS] áwLQ3×…ÈŽŽ‰8é\¯ÖÓªë„ÿ.hcX¥¼Q"ë ¼åFà»ʰ(»†™ êhÆÛáyôÎ;Ã&X–“üõJ‚#\• P+nR€lké š!¼qÄi1­w…ghµ Ô­‘Ì,ÖW‡°º$ð~Û”’¾û2Ò.dž5?¦bfR¾ïÞûl°èg;£ÊÆíÂ% âx;º½$'O Ò{?ôöÖ£2 åZJXçCœY% g´4ÞÜ2!c˜æ‰ Û(a1‘â£ñ'v3 j{lÄv?ê ‡oxÊûKa€ÒBÙêû“F¸ÂLMÚ?›I!nß-ñ¥ÔëßÙjÚàß70O¶Âî"÷í1—¨˜?ë\»uØO+ÕT™·WZŸQ†ÕôËŽ áAÚ»c/ÌA¼²q-5š QÄl´Z9 ¶gmíaü+„K¥¯ýÙ<Ë‚Øg”ª¬áÞÖ¤L³> (#ê·ICiƒ†o¶ù½b•>H/2ŲÞÈÉÖÜnaÐùãH•!ÀeãƒYµ) á«ô.&‰“µ&sÜD&ôP^TùÀ3rmkÍ…èV­3=Ó¬ƒ :WÙ"=$ˆùN±ÑAÁf‹-G“¸ðq")Ô¼Â"ªª³o¬2€”ûoƒÏø2a+½L¼JÕ\H ’ë˜"ŠÙTˆ¡Ã«Cn»†îà÷^9Õw e»¼¢ öáÖ9½ÆE£DÀÚìÉTÌQÔ*ÑWÀŒ ⳕ&¥D‹Fg ¢ƒ½ß$‚{Ö&nÍá@|–ѧb1=8n¢¶ b—tªnlܦ̓(ÞñRW¤Pæ-«¾4™þgœÿ½:{„zqÃnŽìûÒÁØÆØßwÆÒ‘„Ðfi·UøÕQ9¥C„"[%²«¤dKÖ·PŠ´S¡¨,EŽ-êçÏ8sû^=ÏóyÛ…Hʨ­Þ-³í –6Ź äÐñ"U9ãp› (½® æâ¸ÁqƒãÇ Ž78npÜà¸ÁqƒãÇÿž¬Ž“'nrÝn.BŒaò÷î•d-“‹Û½‡X(H<]´¬f¡PúÑ]Ö‘ðUšÉ™bÔyWƒ”š-ôùï&¯-SC+Éäa¯'JuTn¨@¤h“šc„ÞÚl£ÇýºcO j¹8‚S?)­iÆsTfX‡¦ù9º]ºÅ\;’“Þ8`ñé]=Þr+{om*ßç_crT.p‘ñŽÍPf€ µo‹ïhËpÕÃ/²±›âU@#РR+Ú­J4ãϦI8ž)!=L‡¾WêÊN¨£Vüöñ±.:0éHt¯çßfO‹ó§‚çßk(bEÐtè1ð—æ9$©†2r˜Ó^3¼Ô9E>­®$—|·«fàyRù…5·Fú¬¸T•>;ÙåKÀ\UÊ‚ï'#ÜqôskBBkýu”è}íWÄ/~-ˆÑf‹q1PÖÍÇb"N%zééý0ÆQÁ3Üe‡Úˆõ•iTq:ˆ5ØúÐt¨˜ò2”:_a ¿ºfÏPƒýY.ä&*Dë½OúÔÌ‹q›ük/2¡ýêÏË·Ö _[¹mݹµ°4=-û—,Šn¬ç-aþLW W,)‘¡þ˜…n¢RÇ:à ¡ÞâofY% •µÛÍ:ˆ[moÌóVÜ8âNºZ¿â†½¼‡dï!5¨“NP®­¤cêa&³˜¿)^I±Z‡ÝVA=¿ô±¸ª¨¥ãˆ66É Ü/씆;{7‡-àŠ »Ò©ûÒ0º\sƒ©¬ù;F…ä-aáÞ )ŽQaRÔ;+ù GaÊÜH†Èz6´%Ê&î¾¥ºö膨vQ7oF‡.ÏDûéWý3ê¢>R€Ë³0Q=˜Õ=EŸÓ¡¦ü´Ê?CLh8?°'h™[?½M7ÔÆÌ.|¦¥àA_éáŒG|e \ÓÒXPQŸ¨Ôa‡nyVVMêƒ÷ /9Ûž7ºÙ¥ÉvpÜÃöÔ’·"Æq¿ìùrRoÆÛ'j ð{y^²R ãŠ~\ùÇí¿ßÿ5;F´*%­{õñB—¤ùUCÉgÚw9ˆ#_b[cí^l?·nî[޶_4Ö½i ‰ªõD¶p]]5éÆo%ï,56DžóÕÈ%K`Ê៶¥4˜ÎXdTþVBñý%Âh@× {d}‡ŽBŸ¾^ „d%ß_ªCÆ78npÜà¸ÁqƒãÇ Ž78npÜà¸ñŸsCó|Ù–ÛÚrÈÊiü>¨CƲøð™w? +-¿;õý,Ì~«œö©……Grÿ mv_ƒñWZ­NËÑÀ™WÅc§–-ü«j¹gbX ÷8Iö¶¼5ÂÉøc#ª€É‰¶&Ö1#¬miWfVéaÚBÛ‚„“#¸U- ÛâH‘ÊËj²#Š‹ÑZžäÃ-Çû.¥(ÉWžZ9h½ûÿx®|ÈtÊÛÛ(.d,Zü2ºF 5üª#§¹@^¼×«dá.nJGÉAµßœ¯9£ úõm^ï¬ñKGõB®œ9n´R“.uLÒé1K- CÉîPÿÂVg¨LÿšqÃû§6þú×Ú‘] ’¨^Q&*3y‘ÿÕž°-7塾Žt\ŒŽS{Ï3 jðÑï„fÙ3ÜÞp¥·Ï¾súÐGY¯MÏç¢mÀ-ÉþAD zÐÎ}ÈN/T\°ÞA‚è› Awž—ï¹ó“‘ñ/ƒ1ç ®iä¢^ÆI¾–È 1ÃÄ©#’›€q‡fþš—gÿN-[öfâ—£ÕGU‹làž<å¹<Ûs¹ËǬêºëý˜ íT¸ei3ë’½Eu>hÂgO^V‡þ®Ðm ÔÇ‚¨×®ý4Æ££4©ç„‹÷ž¯œè°>=Ò,BÅ3$íÕq– Ԓ˾Q 2=ÿj|L…¾ï•tÌøp>|,¡hž òŸ} =§¬áР«l–ÄZè â+Ë”ÃÓÙ' +‹Ø°õ ŠF*)<‰~ofXø¯çUy oCù‘?5ŸÄÂ"÷^ëÊ›åù’‚&×ÃÏ­|~€-×/mÚ·I GnD*4dÌŠ]PæÃsiB!ìu(70FÝ¢a€¤Ó.Š÷䵑_àldøœ4È›vö[{paà5û‹liè;È}D [l“/FX£`ù¶û}âèÖÒ?ù • úýfjŒþ Ô W ”Aåë&,}àbç”[›£ê•"Æ€U žîÚ<{q䈈˜Œ}3ç¿eLƒAƒ€òŒtð²ÐUûò _Ê×ð¨ò¿‘®½k)èo9Áž,’‚²=-[ò´±Ö”¡”–É‚ÿmËs¿9bZfáæ¾úªÏ“¥L=\ó˜¾Ôìšï™SÄášìÒ7x]ò8ÝF€³íkªE…-‚úVþðê½=黿r§ÖÝtÖÔlj2R¹&òèû§qµ®·8vV¤\k–âÁ_#ö×±a½,Ÿk²…É¡’¥y ¥º¶Pð5W1;Ïœ )òóß$0ÙJ.6å) ¾¹¿ª9÷L M—½—Ó ±ªïÌÝ>:*‹Ùð¶OÀp uCxÇ Ž78npÜà¸ÁqƒãÇ Ž78nü÷ÜÐ9I™Ø*"‡¤‘édC2* †;hÞøIüi+uÉñ a2ášf )–ÇücU‘B™=˜¸`¾, rº®-„5åS$$ÔQk¹(–:c„L-›ûéëU ûçé Ÿváü³¥K;ôñ8íÎÄ {GˆÜEù8Ùo‹»Ù¢kçQdgïrœ1<¬äÛ©šè€añ'ÊÌ@Ûô•ßÝÃés{*=–Œâ£kkî«¡z¤nÑyˆþ#XÚ­~¥‡)áÍMÙ®ÊÔ¼¨ u'©Þ•Öø2Ltn¯…98O ©QLJ²§·cèÐÀÃìMs¿µÒ«SL‘í“ùwŒ²3„-E°U^Í/#Q¼³m¶‡vNþh˜! é¸ÅûªÕÑjp¹Íä™ä)3ô»ê¼aÎæ—Ÿ«îsÖdz'›_œ´Ã,­ôåw$ j,yª!Œë/6%ú’€›ÿ-—qã3b¹ë_ñŠ(¾;ïLœ½ û³"3ªI8 þIëÞ!3̳PÙ+óEŒ}Ý¢§mV܈ÈܶމÛZYO3là*ðÈý±Ðd„ŒÎý$&PNq‹“N2°7ÿ£‰é}MXÞjdÿ¡Ø × Æ”{nÖÇ5ãmºŸyLðñ½`‹œ×„šög4 £Còm×…æÜ>7ø)ÐVë:KžX§g©¤2^RÁXô¢]~jK¹,õÏ2aâWÊ[>>Ì-‚kjÕ!9ðºH×;y¬ñY7c\Ȇn ]Y ^òmi¤KÚaTÍçÕ,C¸ë»Ýäg ŠW¿‘˜ëkŽrþeèµâFøÝŸ VÜú^²/š¡ߣX/Ú©ëÚ­pˆ/´·,º¬CÕÕE!tÂ{}¸[½ÖÂï²ï IË@Ñb£ÅYàB.“ô>óSÒÀ]Þ0`Ø¢¡ÞmY·YBâQ»ã²ø]i.âU^F(w‡¯7B ÁüùØ`L>úåº>$2è~È/m¥ãµlù/ä"ΙHQu x±("H„v£²IA+Þþ¾ÍSR<Í„6ëœÙ² Êîɱ§ ¤=y®–$_Ú¥í ¢µ1ªëdUäE\$|$§ZùË;n‹z÷íÔ_^¿Í@qÍu#mc; óHæŽìSBFÒ㤠·v vðáÑŒèû(i”º˜_gyÛï×$°ýá ¡•³g5y^Õƒ"¬Êèò¸O¥/Nf—8.¸;“{{¸1ÿ—½Ûp$ÒÅ'ÛTÛ‚4ýÚ'ÿP Ù!¯ÝV”íùmŸl¸º9Htñ¸$ÖT÷ÆÒ -åÆÄÞ2%Œ¸}Ë{Èbºá³tôÍí(Nieï¦Ë78npÜà¸ÁqƒãÇ Ž78npÜà¸ñßs#XQùšì”,^NEN7%ã]3§„Rþ%¢¹–òáÄK¦* ?iéfa¬Ó)Ÿý*(0( € ¡U—¢·j µ!‹%4uìçÿY/lŒM ÷ËTØWæV°ËÕÝø…‰‡ú˜:Ï}kËZæÃï}e‹1³¹¢{ñÉ*u¯Æð¸rNûÌù³Åî²èm̉h9æçªD*V<›ë«6vQÇ•Ãóµ).pÄ驪|5 ©V9;Z´4éÞ¤ :uãå:)Öh7FËbxšC—fl•v¡:ÝR¤ ¤ƒ…ÖË]¬þ²™¿šbGï_N §Ç´ºà§­òïy±çäí¢Á0y¸¾ýÚúng:Î…´î[~¦½-›E¸÷›¡ÊæâÀúœ“Òi ’ô’$6ÛÀBš’ßI .q4Ö_DsÆ)Λ\H°Æºuëìÿ^Ó;v<]qÃ7;ƾŽ)^®Á÷§‘°õôÆ7çÿg†ÇwÏÞZ»JxëcÞO¡CE2÷wkW&Q"º.ÆÛ€t]O}¶@­cYµÒÇjÞ/•[1¡êÚÝ-V… ä"qMN—j‚«FkøB¬'e_³ÓGæ-ãt9!<àäRHb½%òC•5+Òéð#gÎ{×'Œ;¬ínîf ‡6¥k«AʇPl|G…,ê…¤.>ç÷}q^chÐÐwŠ4ê| ™i{99©ù`q+ÙóX»ïúøöB ^\ãb¡£}Ùõá­)h°Q¯õOH<¥?%Šö@cÝ-/¦BΞ"ö1Àtß‘h#¦„œJR²­ÑÅ?Ÿ£¿äYÀ¡*æú”7¨šWA}Q­Ž½QG‰HÇ•}­ƒWp¢¿JßðyEEáÀŽ& K »§F^w…*£hc»ŒÂ9I ä\vˆæCÔ“8Á×\p-ûGðc±\¤ûþ{á› *5Ê:ó?¨qÈjLYùÄ´·Spp(Ó'ð¨.N¸]z’âfK[Tû)2qÅ;Wiþ,¨žÅnˆÝ»!vCì†Ø ±b7ÄnˆÝ»!vã¿ç†ÁCù:™fMLdJ+d`™úÁwóoΚ͇¹±/xȿۧô´ƒ‡ÑëÆ -)ú¸§Âã9õ‘9ø­iK.Tý3'hÜ‹„e£ ꈖØóáXAÿ<}(yØ8ûô:*üšX.¤¡ÕrÖU!n5d=¨çbó`CÐË}BÔ¯§Œ7ÿe ‚–ôb%/Îé^>eË€D†¶Ì¹=8æmÁ#uàOÉñLvz&Sod¸Avƒ¹…ÂÊõ4ð›9((Ù¯ËfRàã¨ö„²‘¼§dîÍp&Ô>’÷’É ¡óô¿/6p¢â®ñcš’fA€5fEþzu§ÃÚæ®Ý­ Þ‚ò ^Òh#óKÅl•ðŽYý¥dƒvJÄà="Ì›:ú-Óƒ,Ø7¾˜ ¾w*¢®Î5ÅØÛ{aýŸli"¬ÑKVÀ[½[N6/!€ßËAvßã7…3nØ_û¼X‹£Ý~_¥…)¬&Ç0p0AT¬MÕŠ{/GØWl`Û¼Ú)’«~–{ÑXç…O¾èLN습(¦#åX8‰nâñfçI„:{ŒM1yky” zñ*wò7Ú!i?ÓÇÒÍÆ Q²Â…•†ÖeìvÎ6sú×mШÞáÕû‚†–?#™, KQÑ B-_]Â¨Ë 2E%ºÚ²¨¾,ã|ÌWpmvÍ‘8pøZÞE'" æ$f¸ÿ}P'÷5ûZòa¸7‹r8–ƒ;ö|à͸a¹Á@‡dï:®%h;óÐDŽa·5w¸Jß3›Ô4&j£×µ7¼5‹{ŒçAS©Å ê­ñOx›–ÁiÍú®¿Ö.ÁÝf·n¥á'i®™Ÿ!þpÚ¨t“¯ü“ÊŽ $0á‹ÏÚe^ê°9²L`šH…†E¡:X`oªÙmœ£‚ŒÙ¡7;†ù ~:iS—Mâ^¨yå ïÞnUìñ¡ÁÖ¤Zãý«8(¯ß·.÷°—_;ç p±>!Y¸Á–v¬ššÙûJ·Çé\h+š»ùÿšÓÙKâbKä±ýÜ•´€D#¼}ß¾_"P ,ËÓ»RðµvÜÖí§xÐBõNZ`/©¢µ#®T¤{ç-¬gÀÛEv zöN°ÿõ`Äm²V­óܲ@ÓB.Œ«~kûÿ]ôôýõЏ…÷l°éik¼F=û9 Z!Š;K¨ø¥æÉ¡L;-´Ü~ô·´2š{IM;m—Dmú.Ù¿#ù *ûv¤§ “%䄇(à”P‡¼™ù¦¶¿  E|) Ú©ª†Ÿz,\ΨÑaݦÊQb¸.ÚÆ›æõ±,`ðMQH-iÆOËúv}•…κ¹ë”6‹Ý»!vCì†Ø ±b7ÄnˆÝ»!vCìÆϹÊ%óß×Ä!Óĵ>tMV=Púæ7göÒãÕ®OyÈå8Ïä¡ÎéÇ*«7ëáþäŽfûóæ¼vyO8ƒ ‘E|âIxÀÝïìw¦%ÚÞÏ.éÔƒw=©d:–îb´¦ÖÓpáy»þçvBZz¤>ââß³i÷Î ñ–bå6#¾%@^éM[¦º<}_°š}dËI—d”Ö=z©¢MÆ‚ïþ/†,ÂSú ÓÜ îé¶L›r^ôŸ*t|Í*›³^›Å—~CŸ+s.< ¬:„Ö.í†u‘$,øVPʰ˜Û>׆=DДî÷ÉW`EËÜë] ¬gÅámX©Ð±‰d/1!½ûí´àZó n`’ i}×;G„ò[Sî–6 ävð_Ûq2•_\:@EIÙEúI¦ŽàÞòD†­E;Ü5Š 8:<õaƒVlq\ê2«‰Ó²øRÉI—ª> +c+Á™¦ÏÛT—0I’I ÙÅÀñ|•(OPl•œ÷«ß"Ý“ÍsrÀíwoKs„¤þ“6nVùd4 =NÇȨÔ[íÚðþ“Æ'{ìž§œ±!‚ sÔøB/g;üZ§«‹`Š« vuVØ—ëw²ñA'Ç¿ëùÖ]m60é;*ȾEÃ?5âå²àçàڇ䅻_öÛÉÁ…ê¿¿–û:aæñ¿{öÏ5‡Z¢[ÁŒ‡]Í"rù—*V4óßQHºé¾Ÿq#ŒÑà°yÆÛâà·ïïÍàH^ðìK²ØÂZÓï··.. íßGÃ%ü|½C‹ ‘ñù€ŸüP¶ê½  ZEÖ½öluØù ©…?ø"ÚJ ÖŽEV·« ù¡1ýýl\Ÿ¯íwÉM/½ºé u­Kæß¼Eƒç†µ¹±Z(5âZo;ž¯-ó ¼#â!0-OλÀ*v˜)«Ú@Spãý)GH•ášìVÀåY¹Î…F˜Æ<îéª?×knŒ1£ ŠöËßÏòxüh~Ó¶ç§í1¦ç¹Ê« pñ·ïrpããþGõÐú°ºÔJ) 8czç´QNöõÖ®ÓÀ6'jl['GúLo²éãŽEýV‹§â·û 7zØZH˾)I‹ëRVodI¢êdÍt\Ì/}Ú>À…lB|ìó¸ÁõµÞQmŒQËù3nm¼›û] ·U=ÛgDyÞFc]äf¯*ZÀPÁl[&V8ÌéÝwRVUˆÌv»!vCì†Ø ±b7ÄnˆÝ»!vCì†Øÿž¹g…’šXVA<™Ä3À•áÑÝ‚)N]èõUÙ5ã½iò DìL­<]üö¬-Òú9¤N&ì ±¸ÐИfWF¿Ëî¼¹·Ì©Âô“õÀ§ºôoe:úÿy¼Âmµ0úL! Ö§ßçbýÏ{•õ„øK®Âù6Õæ1Gs£t¨guù]„5¬ƒ9܃ [½œŒêTRZƒübœ[t9uå7H ÝtàM Õ :=Fô}‡NU»OÂG5ýŒx2‰Û$3f]eÂúrÛxy_6í}óä‘¶ œm[Ÿµ=B®C ©&Ö¸Þ5Ú„”ïôSɵ¨ OšK•o’¤ñÚOé©¢Z0hªÖu½Ð#î>œß~ˆÓA òµº ÌÖ’¿úÓ“ Ä'NSHEª‚ÙµùŽÉ\“àN#BaMPɳ2|ëµdn„v›þ‘øò–#ùUñQ:QsÛhÂûöJP^#åE%àå²+„‘Ý L?¬j"› î{ ‹˜p9£Jâ ÛåÚÆ>- p„à {—ÿ+ØV¡rô`«"‰zºrðC= ð¹*KÆ>å¦z“aë&” Yd‡Žè$îU4Åé¹¾'õ¬°ùæŽ0énNÖ¥•½qRL·Æœ=MÃ#¼â«±Ò,Px$­Ð;D€ö–Ö÷êŸÍ ‘7®¸eÑŠõœ=êÏõ¯s9ày©¶4’[J§¼ôr>ìnvFsÝüÃ8xqKß;fŒ*vèž1ƒ,ÿüUºš<ì9ÅÙ8öz¼êµ¦mçݨek9GÚýËÖ!@CÎéÃûÌ (”ÚÞNs~x÷oZ‚u´œˆÊ|j‡ý¬ôÿ@Æ*Í…½ä]°”y̾º*7Ç#Nd ©g»¯Ÿ`S¡’Ü©–}œ³3¯w—¨b²[êív#[þyöå»EÔæ8ƒaÖ…jum3ø8ëhyŸƒ¤¹¼û*í .haJVÀô9Ú×EÆpý½ò.™s¬ 2ES ø¡öCΜڞß4Ÿ%½6ÕáÓ6ÛŒ>s5Ø_)±â˜ õ·I/8Ã>Bw8ÙCCA“”{ª <»þõ½ýq4==W­ÂwÑM³àײúXäJŒúd^wïÙ|âÀÎÕ*9×4ð%âßoŸé¸úêì̲½–¤ÍtÄ2*Æf›~c9háéäÛ÷,+cƒfË•Ôi <³l•5;œzSwbª¿rÁ/œŸBÿ`ÝéÜ´b;(mµ·ÕZ͇^ã•yÉꨑrõÅ(ÐaݧßnëbpRfžª„Ü;È^ÆÄ]ÏëCe¬eWryÁ<ºØ ±b7ÄnˆÝ»!vCì†Ø ±b7Änü÷ÜËî«Úš¤‰YõfõBÌŸ%IYpmŠcÅ÷øóû<œ”[•}å7/x-û¸E›¹5‹0s°=[¬÷Çž kä¤Â_‘¼³¨ß%ØcWÊÆÈœÖƒ§ï"òÊ~Z ‡Zœ­k¯¾±¾”h%„Ì‹A×¹ØøŸîâühenšº%x–ê¨MñÑó£6W¤Â€Y”í‡@£H1ÒɸòqJ‹zÑbÜÏë¼;žìOdžW·ðÉ£+·X†9/j¶BlCëÉ8 6J¯ëþ±åÕLwKã”:Ix!¤÷Ú¹ß à¾é\Q-%—¥ÔXek<ªßY|$Þ G†¢´¡´O´8c¶4JOäuñ$´`±{e^Ú`ö·YôŠ™NbÜ‚ŸtO[cæYÅM!\ÐÝm¡z÷ï8 8K8ÂßN‹i<"øò×ù> €R:ó-$ p<]Jû໎ãî†F‚îû˜^¯ÆR‚¨ê½.gåXz&U‘ÄÀ—±ß5Îê@í§S©21aû‡C—ß3¡YïöÎÓÚMˆ¡¤ OIöAÇ ^7S:¾Ô»ŒWÊÙ²‘KÓ™ôb‘!¦^EâûôLo·'’­&¨Ø(%í½~‘®\p¯PÛ —ã»zá3S î~- ÔK¡á3“ß@î·…Oo\Šv7À"M˜Áü¹>~÷”fát›ß”ý°ûòT'Ö“ÿc¯Î»¡`Ø0€K%^D–±oYÆcÃ`nfÆOJeOŠÙ+ZÈVTT’½¢d+„6InK’’¥EÙËD(RJ¶×Çxþ˜pŸsëœëwÞf©¨Ú›Ô-dÐs7±d"Ç ˆ['9&öѰÀ»Â"á2wæñÝmÑÑખ:Îo&”üþ[/,yÏcÅ ëÊ{ß“óZêúêÞËCªã‰ÅK+δ)Ë\Ý=»?½cñÉR1õâýH K炵‡òT0ñêð`½jyÀ6œ¤¹éD~!ë·^êÛù-ÀkÓK®b/.üìL]ËRC"kp챸0\¨Í}Nš!âÁΘ‡<d+$ëœs6……×­?AMR~žzA½éœ›™ Ñ÷8é] ;ÄÓ®õà£õ_¹g34Éz¼¼~5¿úÓjû9õ°áùÿ OŽÐ¦žY½RÒÄš§nÏöÑÅqêŒÌës9‚³¶ìÏ¡¼}#J¦?Ø ¢®Õ(Õ,ÑA­ºU{ ” Ÿv§%™ÀIÖ6rë.+pvöMÎáE·ÝÅuáªÝç"¸¥°ô_Óc—Hð|kô‹Åý44ö ëù°\/l3Þ+ÊvƒíÛ ¶l7Øn°Ý`»ÁvƒíÛ ¶ÿ=7D´=Ñò#`¨éHî/e”¿ÙbºC`‰¶>×ø¨ÆC^¿:U»–‰(‘'vSUémÆ©N$hHxÌßkN‡\.Ÿá¹é(¼ñèÿ"côð «F·?J6üœÍÊÑÁü1“}Rm ²†ulà»Þß±R:k=Ñkmq=gžbó².l èW*²BnÂüøÀ”ÈnÿžqÙÌûdŠYU*ø°ò§ø?ʘ!Èq5ÁÄý¾Ä¤f30ôqm#plÃÈ&I$B*«¡¢ªÏÙÇ€"ñå·îFÜzuòþáa2l<ÔV»rUªçô1´aGµˆ§-÷î·ä‹¢×Ê/qb §©ñÐ'1¨¹Mo‘0DŸ—¨7ö*€CÁ‹‹_XØ@OonŽ§Ãƒù†É'41?U•f0aÛã÷x½vVÞ ³ç}yPxÕ‡#ª}r`¬uO­ùVmÉ €ñzÅž’¡À!=ð­Ýp⹎ÐB{“ÉxöÅ»ýÌfIà=¹KJ%ƒþ¬Ê{}s|ôb~·¥t(¤Ð%„¥ó}1߯‹£__9ˆ}pyú÷½œ‡ê›s3 U`‰åê•7@ÅéõÙ}-,MüÛö`û5}T·|®Ã;F ‹™x×fG½¡ÏøiabVQôÞF |:²GºQöèîäqá Å·vï[N\¨9ë}í¤9ìKm[¿¸‡ñ·™®øÒŸ”¡­²Q³Ýòû9WÜÈ%.ÅøxÐÐwÌs¿msm¦6hƒÖæ¢,:Êú'5Œ,ÕÿVï¥ãгÖßB7]ì¾þ‰O³<@ìæYmp­p§rá”àŸ÷aT<Ÿ|:§¬K ]cc£E|T0·\5Â¥J¼ŠüþXç.×ó–t‡Ìƒ‡C›Ìâ p9!Ý~Oì‹ËBãÅ=—}¬Q¿dÔþbåŸå»Tx[BÃ1·woµ!m’1dLC«¾Î]6[Ùö݆Á7œWT‡àÉ{î>Ú$øôIÄ>ý ìvÙ²€1I~µ%^œ:õï8§:¾ì”›]- u¾éÏÞq™7¤kK>~ü¦GS˜94y¦C&òÿžó ƒç«}|'™þñEæ‡2¸puy V/yØFÏÓÀâʳƚ€nö¯çž éá‘°ƒ¡VÓŸh¥‹δ¿˜)`ÎíÄGOïXo7ñ¾[öÚ¡‹ø±’Vr8Ð ¶¿è-üÀÍŸÝ5O¹×¡H«jác  ”HfVûZAmõ’èD¬(J´oç Ô…‚£Ü“’˜ïáþåÉ VEìôЦ` ©Ó™|f ˆ—<ÿ;§ËvƒíÛ ¶l7Øn°Ý`»ÁvƒíÛ ¶ÿ97œç©Çô6ðçá¹ d'eä©ûâ¶±D«ÖS T1[ß­âˆC²{RÆ÷Jà•}N‹F†$zÔ._ƤÃòSÅ@1Eœálòýœ¥‡×”N—‰»JC/ÿNjÌK¸kìÈŒ„¾±íL-˜d-òçÑñ@‰+AÅ w2ÓÙ‡òoÑ Ã|û-Û Gœïé£lk8ê`Q‘<ª‚ü=,ŽNeTæ Ä9Àkõ)Ã4š¦X)ºÊÊ._ Î'Â- .µ{¦hÓT_oÃoE^8!µ‹sJÿm$ç âé֡Ͱ§~¨&|Pé®Î|&¶°]Âu¨X^„»cRí8ñWEžp³°N$1ÿb‚À¤ŒŠƒp½ßßUÐj€ûºt›(Wèp=õZD“³&Òã6þ&¼6‡M¦¼"i àk¼å½š=žwi謒ƒŠþy%1@[s=çǼœ$ö7fLh €ß­Û'ZWßÔ1Ï‘Ñ"½É±x\†–?ì¦Oßøêh-s\äa&?7´€³›XÃŽiŽZ>pÖÅ=³])í”5^ljqfÈ–3r­ (ÞI«)©¡âàªvçušˆ_ãìrIúTžŸxSsœvòqã— œ¦ˆ)yj¡w'5(«Ü‚BS;üoËC¬Ù¦Ë5$(õû÷î­\h}·êüsH¿mDxNƒ^Ö—­i1ò áòFš_@1ʺ˜œeaav#nN4ô‡Âí!mL|›_®{L¦ìâºÔFÇØZ½:ažè?µ™±âÆõåø7¿®rõ&nh(—‡ð+)ëÔ–´a°tUÛQ)ˆë—Z¶Æ:sà`ÚwûV+áNîf@÷Ï›Ž×T õëœpÈV86?Â;`Ñ&oëÖñ¬ìßdü´\‚.0ßÓfíZ%ñÜɠƉ.ÜŽ?µù[:ÓÌÚ²&7­{¯V¾ù~¶l7Øn°Ý`»ÁvƒíÛ ¶l7Øn°Ýøï¹1ÍÝÜ%©³r÷cºÒÂEW}ôxÒ½D£~Ïîv¸ÍÀ¤+þ±žâLœ+ãfÌÜGâëÒ¯WzÎ­Ì ÛÐaø²kÇVCEá—´»£‡§OY¬¦JƒVÂIÙÔû:˜ ƒ­:Ix²òÇ' ð/ü±Z6ŽÃQî †-îËç:Ü­ gèî»RoXáž·RÎ’Åà:%<‚׬Á«z!»tSÉWUßíRA³©@Û1ðbSßlõ9îˆ0¸ÏxnÙßñâi"Œ®¢ä¥^0ÅQá—úêFÐW?°±pÝFdç•Æ É`»&-³Êª‡”å<ÓÇËiYŸKÙ‚Éü.›÷f+{­0¼|­•Où õŠé¨øl1‚œRåNšTÈìÝê×Qf€OŽì®¨¦Cüd ¹ÆHY¦~ºJuæ0$Rðõa¬¨¦þþqI‡®¤õñ•ÇüÎHm¯!šk1§ÙÈŠßïLÍ« @HÏU~¦±~×ÓNY{Œ Á¬â¡uRÐa¼Z-œg%V¨Ô˜š9ÎoÔf2Õ, SBM´©` „Ñ`§‹¸Ö |xÀ÷9˜{W¡Îs-9}S†H§o›2¨¸Ó/úVÊcMüeš˜ó¬ÌÈJ÷$­Q•ã®Z´ƒ*fdì´ð-Kul*Õš§¯p*eÊCoRù¾•伊ñnåBÙ`ïQsÈl¼%Oƒá…» äaûæ‡6Û'åГÒdGÍ´‚ÓÊ]64,‰ÌRHa"ÓóŸž{ÎÚ0R1ÞDª¢cÛ©/ÿ†‡¯F©C´?gWÜPÊÞï¶5ð²ñ)ŒÏ–mŽ¡^Nü&S¾žmçÂéÆ˜ "ÑT¼ªT—Ã©ÔØ»ûõÇ•Ñîkþé¨(Üe¬ú¼e¹Þ%qB©AøiûÕ„ª5 ©øáñ c8¾Ã+ù”0 ñ>æYCà·§T¿|œâ Z¹©[Â.Re•˜5 BÍO=ÌÙ´’[6rÛ³ øV·nÏqîâ“s{Å5`_KÉ¡ÁçÚ0ÛÅy?鮑¦£Òš˜.~å Ó>þr2ÿ|]U¿7:¾µÇ€Nß°Kµ‚(B‘\¥Äqb©D?+ØåîÚ#ÃÕœ¹rEÐ}v ÓÐÜÃpœfFÑ磎P\¯¶&*[š‹äŽ-”J¢w™——N ¼ÿÕX÷6t®†u2ÊEÏØn°Ý`»ÁvƒíÛ ¶l7Øn°Ý`»Ávã¿çFÒ‘ÅÜ:QNNw*x*#µÂkæšþ2íŽ@ w!£”ÎR`¢z͈C¿+«O8HÀëtÇùÏf:Œ.-ÝîtUÄs9¡‘JÏôÐȳ"ÊY\*#ʉy:x4ï¬Tæ" »ŸŒ^%ÚÀÙWý—çè¸@J«?ìm‹ÒGžQ÷Ôê‚FäéÈSVø:ÎÏ1/Á˜‚>wOXÃ+ÿ[{͉¨•¹ßqZy‡C-ˆÑÀÅ9«rŠ#½±¬(@)š!%ûnF’…H¦¨kPÈ«de¾Ü±wÿ+ šïcW®³dxµX–xx£=4Üp?¼®\Gƒ"É9ó6Ý[-Ãç! ÔÁz£•œ(ÁÕÞ”$I?I%κlÉVÿ¦¬¬ùþr— 0Ñó"ש6:”‰Mšxx©-vËÿÙ9°&Ž6އûP>TNeQ„ÜÜä È¡<ê§  ’‚Z,"jEEñ»Ö"ÞZÁbµ0hµ¢âWÅDm=QQ¤­ð%,¢ÔÒ¯¢}:¿ç™çÝ óÎÌûßÙÝY6±áà—ûn,3ƒÐE~®»u5J£ "MZmüã÷Ko°*KRµIRÝøbГ˜lsm( ›ù Ñlÿ몓ó˜¨îhît7kc˜.\\¼œ@3ezŸ0s6zZYfìkƒ]–¹””Ïo2Õr£¢—¶ˆ&ï­ÙµªÂ….î+ñ¯²€¡®çs“]åbgU¡-*]Îv££QזΨTzÀwöñ)ÕoÁ6õF}Ã9À¯;v¥:Áô„£ gÀ›  ´»†ˆì7Ø«¡Ò‰'wo¹É†Y§‹Uç± ©n¿gt¥Ô¯5ZŸEhzS•xFOý¯ã`!Í£#•þæ…núN³2¢Úƒß·‡•çsPñ,—ÒƒÔÈÜa÷Û°àTÿœH}µ½_6óÇ}¶%C• §c«K~识Ҟ.­ðËrAõ?G,6²Gõ9KrÛ,P~ÔIŸæ–~0üXðfŽuKYM±Ã ÍzpbtŽbC® (»ËÏ-v†o&z]š7F][÷IéX6òlàîy¤¶Û ž¶7¬Y­5–šL†»ù“ÂB&vlöÎñ®{¤n˜E&À¦\×± ¨‰ÞõüÁ {Xa²ü±x9†Œj(¾a悚ƒý£ÿƒ*|{Õ ³F&¢S1«ÊtáÂÝ51¦I¯wÃ~Jß"Õš/MG3¿pƒ„Ê4u×ýCac¸õ”@M&ئ|½~¢øŒ”]?R¾§s=‹ ÿ[>Ê@`êý,‹ ƒõÑ·v§xÐPù¨ô§ÙK¯³¾!Š¥JŽBÃveòü ЮŠåMGtÙ#½HÅk-e™WfÛ…GqÁfçìÁVžý–fá9 HO¿0榉+ðu7÷‰MãB¦É˜¹*MýÑèˆZv쩚­çÍÖÎO.ÀºuëÖ ¬X7°n`ÝÀºuëÖO7V(lRnê‡âOéžSˆ°@;*žo™º¡…­áòµîzOi;RsûÙx!å}ën-pÒG’Sß©M>b1—YYÑ#8ÀºYgî?s0ªþþÂÞ­×hˆ­‘ç“Wo SOÌͦ|NAê+õϱ)ˆ>S¬sh/,«ßË0‹ƒ÷¶¸ò™ªë“s÷òz*(§ÆžÃEë¶§ñÀYèÙd¹Ù$8+W¥Z¢eÑžÇ-f["m‡)³fN „ºŒâŸl?óD5h½oöb@VëN’…¾–°ÀÌW9ÇÛ ž©’î$p‚³9Ì54C÷Ð}7{!r2¿Tì/—VYÈ¥#·ÇF_Œ¯öûb-ñ4# Ìf5.SD…¦ÛÒwÆéÃyóåÌ‚QÁÔ‡òµÌ@ounlV*Pƒ¶ö­å@ûnÛG6è@ßôÇûsÙpy_HÚù­f ±À±xä#uô _í¤®¦ð£KvܦY5¬¢ï¬Žú 0BZÔ@+ÝAÚ°É,—½vë@tÅ'ûaá2&jboXhl cÒ®*Z¤;É¥]ØHå’}®–œ¾xgõ “ý4l©è'ñfóè¥|õ†|÷‡î(iàê£÷XÀË/]|ƒ]Ðý‹ŸmR_b‹$©/^Vpè(]w÷¦´G¬=£žö ºì3ª—Xº›Ø¡ð•;ŽŒs‚E’ˇ N;0J› µ#Ÿ?'2Õß…O<¹Á†Ï>anÙÈQÜøÓÙ#=ñ«ª‹âýbú¬2 {´”¦Y;±Ðņ½Gn÷÷FÞ¤cÑ7UìK×zQ=ƒƒì-å²ÔÝóI îû÷±Æ›¥º±¾ôÙí/ hLh2¢IM ÉË5Ä©tææÍuAý´9ª3ìÑ/©^£â,Ðó¦o{;õ‡…ìeåææ2tfÞÕç õÀh^гy‚ l?¯¨¼i¿3Ô3om°Fõ ,Ví®Y6 g’((cGéÙKO¼ ΄æí¼‘ Û³½O'`¡E¶i•šþ®pã0}–΀›ÊÌn¨ \š¨^4’fÞÇ]¸¡™LØûäE¯C<`S€ËÚ3 m´êàÓÛ2¬ÑæCã²õ¿Ò…As¿ š7ÁÍaxAmØDÂn‚)ZaèR&‘aÏ!ç¸ÒÞÉÛ70&ë£ß§Oý4˜†—lT-¤_e‰{“f^nƒŠ(¥Ã É”Ç*•gtÐl£¾+ó'µ”ÄTåà‚Îýÿ$ZÐ_ø¡K‚ÊFl*¡Þ¾háÇE7'Vqvc@Å®”ußo÷ôÊÕÉÛ-åäÂ%E¬PUvvÁ©Ô@˜ïŸ2²tª'j.TµÒ,d9†ebke W½ï—nhѶSzf3` ë§ JÍPŽŠc•óH&xg<ܽ2:®׋/¦#ú>žÍ%¾ {ÃÑ~Ë*#0½ë1Mñ¼,˜ a·,Ê⤺Qä(a<#àôÃE·OŒc Òá M=pºëùƒ 2«†Îä¦Îx õãÊ‹DÅ,È[~ÜÊ6u œÏ{4h¸R)ñÚf¼ˆ ù–ì ó(,ôClz‡ì(VK¸ /ÙA™ªsâÖhºµ¿™4T¢ž\Íú­Ï=È&ŽýÌ‘ê†oýFÃq<¼ñüa-2ì5+û¥ü[54}Y„ñ®.¨Yä\Ê ²GKúåy[[ -ç{\zðUWp¦¹lʪÑüC)z`ñëaUª¯ œ JùºÜ Ÿo}2¢FMI­6Ilôé:ºµc)Èþ—È+3/xöùÌÁÏ’ÁÀÿNæä¾,ÁùyÍÙê{öª™ÐðåöšÉ6ð”_Î)¼eiäs ˜0÷V‰±º­œ[sÊ>CC…Ô Œ|×Z£YÊ53?×…=+¶…‚%šYumÎ&©nÜ–îzÔ få÷9ÜøÜ ÖºxþæiÜ­3 ™äjѾ÷Ü›"»‘¤“Ç’¡Åþ«cãuœ^> üAz½>ýÐGNCÚJìA/ªªYQþ‘ ÝLmõôG Pȶ5s‚¯ê ¥ó‹ XÍeûæË6‘ µë§=%Ü=a]Tܱ–s¸³!o‘y ¤öz×<‹ ”L,DZh¾Daó –‡\>tÏÝ9MÛqy:.ò¾ã¤>rDÙ#¦¦P)B-õõA X7°n`ÝÀºuëÖ ¬X7°n`ÝÀºññé ƒÁ`0 ƒÁ`0 ƒÁ`0 ƒÁ`0 ƒù  âÃ$±$LœÀKHŽ—ÄFÅ â#Ãĉ©v b{^;ÄËþÿød) †ÔÒ &¥ÕÒ(”Ör9$ î@%Óéd™D¦RÈ 2‰`üm^þÉb ?‰ Hâð¾DÈîê8a‚ôÀ¨¨ž¨ROò§û_ÂØÅ‹"âºý7d̤ӻì™Ñ©ÿidén‚üü}ƒyÿcZq+•"µNÒ¤,NFH­‚<©I“¢X”JDÅ·Á»›£3©º®h§~Töb{J­Ž¿ˆù/í»¶ø½ï‚lž—¼ß‡¾Öï‰1|Ù—@oë÷ÿµw~±mqwúgi÷Meˆ „6bKÒ¤c0èÒ4³6-IË´§à9Ncͱƒí¬ Ô­^ø#Ø#ÿžùóÊ‚ÁBð‚šø#Б@‰ÜÅçÔ—fÍŸ¶!…ïG:Ï¿³}÷ûÝÏŽ˜x™xG:9‘L'S ÏÌ ÎÌUéUL>Þí  ¡T_ÂuÙ¸7SRjE¥ [²BŒ­ÙŽ¦Ø´Tæ2®;”8FÌX’m»êh6çÓÒy§¨nyÝâ–Wk‚T|Šš`(%Uªo§ Ù®Ò»Eµµ®O©¹©ìÌôä ²-Î,&ý¤Ö€Ž&¢R9ÞnªëçòÔl3Ósi2îIã•„ër¦5§ñ”¶ÖusVª´‘Ûf5ÚF,mžŒ¶Ô[®ÚýERD‘ôǶœ9ûê×à_e›sý¤ÈÕ¯Áš×~µd**››MøLæùÕy3èUýê¸ésõ ‰‚ÅIVÜ—9©7f7åªÇ.¯¯ûú÷s]‚; k}Þ¾ ìk§—…Õ=ó¨ò¹•—uÛà ٷHÂyAØÒG‰ƒ}«Ø´üý·Öú5<ÿ‡c±Úõ?#±¾ÿî0½º÷a ‹{¿V®Y:7úÖñ[®=¾]h$´xü–·~¾tþçÑË;ß^:{û‹ë.îûã›!åÊè¥{¾5ö¾\ùþÇ÷¿Y>»òØuߎ^ºïòÝÉïûേ.~±òÅç—ÒO®¢ßµÊÙõc_½ûûFÈ=ý­«¼«hfýŸ™D|r àmø‡GFàÿÝ š·?›„[¿F£õ£áë?Q—pÜÿ;@£õÏ ¾¯jÖÿÀú¿›¬ÿ ÖN'Ö¾öj´¤ÿÜ{Á}OI]ŒiË¡oü[Ì·iøè—îŽÿëø¾ý^·ø· C—×+î<ÿ-‘ÅÞôû(‹=ÿžÇXìù‰-²Ø{ÿò‹™‘p‘ÅÌHø‘ÅžKVýØõ«~gÞõÁÐêó_; €5\ÿ‹l×üÿS8‚ÿ#Îf°ÔWÌ¿þ×.ÁSô°4ý‘›€vÄ9iiýýOë?5îÿ+~ÿ‰ ÿw–À¤·AÃøÜ÷sé¥-&—~ÿÔ\úAéW.=úêÝ\úÞ7>åÒ‡/=Áÿ+|è=›KKŸ sécÿÄ¥OgÎréwÿ|”Kýõ—޶xù]ŸqéÈ3üù¾ô—þþ.¿îKaÒjÿogÀ6þÿ1AÿïXÿïÿMË÷ÿ6:åªý? ‡#‘•¿ÿàÿŸ;›÷FIØ)Š¥UÑuï' >öxϽgõ}6? ÷˼Gð¿åC‰R$ H™)ÉÈ©Oí {IèÓMåÛÑÃŽÛEžZa ãÅk¤hÛªžuTÛÉÚEY׳ŲîhyMÕsY»´p hÌœÈ$â“ù¤®ÈÉNË×F¢QÇb#áJ<Wöbáè°ŽŠ„¢ÑÐpHE"‡bA m@}WP¶ÙEÁ>YCž¿Z>£H2æó(R'qûÿÖ,Ù쟥Ü>¾‹É·Îè²f0ÉÀsdÏ`%1®Úа<xÄHØVÉVÓÊ î p=“»§æÀ@» °xOF‘uÙJ˜z¹hÐ|G$Ã),ÃÖxj6™JųÒ8IÝ(‹f^” G5 YÔ Ñ)h¶([–|†ˆwdÙÈÉV.ãLÉF½ýëQã€o»a‚Éä¸[úà„ªæD-×-%Í™erºjI$¢êô£ñIz5R Õ:-ëbÞ´Ä…‚¦\UÛªC-P’-¹¨’,¶Hõ¯(eKvÔ¦*6PS±WêWlºnŨdû#eÒÊÅ9Csl’Þ-¸wºAiÆ´5G3 ¯ö|¸“'NÛÊ‘ŒciÆüdG¯OU•` ü­°)+ef’‰Ùt|2{\JOw ~+i®T®æJªâXD£ š‘3¨&Ë¥.m }³ÒT’Ä÷Oi¹’©›;ZQÝðæØW¿rO×­\O»Ía`*ÏH© ÚÊö¬ª»ÖO®~ _µ}ΙUÖ6v¹Þ´š÷%éÁýjÉT T67›¨Ó@wWJæ3h^7eGô”Ìh-2glG-Š$”TŒ©ÊÄ–ÔºUtLÑRµä˜–Ý”õ‚´L¯{ê)Ý£Ûzù1¯|õûØiê¾î=HÕ—˜Œ?HO>¡ËóÝ1¥6VS-í÷àúÅ­±ö¶1i*ž}øÄL2MëySÒ&}—tÈ\õΩæó𢩆ÒÜm3¸nå'¶º"¸¯ùï” ÅR‹ªáȺ_a+Já=Áðs‚€Oâ»ÙúwûïlþýõGmo ä÷¹f¯Ö©§¶…øsרÂë7³G7ª²^òèÆÚhlŽËŽL6Ÿe9ÎQQ¦’£§žVÂBÍsœg¬ÅåBôí^úW;ÐMüÁ\XPà`casacore-3.7.1/ms/MSSel/test/tMSAntennaGram.cc000066400000000000000000000073551476623553700210450ustar00rootroot00000000000000//# Copyright (C) 1995,1996,1997,1999,2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main(int argc, const char* argv[]) { try { if(argc < 3) { cout << "Please input ms file and selection string on command line " << endl; return 3; } const String msName = argv[1]; cout << "ms file is " << msName << endl; MeasurementSet ms(msName); MSSelection mss; mss.setAntennaExpr(String(argv[2])); TableExprNode node = mss.toTableExprNode(&ms); MeasurementSet* mssel = 0; cout << "Original table has rows " << ms.nrow() << endl; Vector selectedAnt1, selectedAnt2; Matrix selectedBaselines; node = msAntennaGramParseCommand(&ms, argv[2], selectedAnt1, selectedAnt2, selectedBaselines); if(node.isNull()) { cout << "NULL node " << endl; return 0; } cout << "TableExprNode has rows = " << node.nrow() << endl; Table tablesel(ms.tableName(), Table::Update); mssel = new MeasurementSet(tablesel(node, node.nrow() )); mssel->rename(ms.tableName()+"/SELECTED_TABLE", Table::New); mssel->flush(); if(mssel->nrow()==0) { cout << "Check your input, No data selected" << endl; } else { cout << "selected table has rows " << mssel->nrow() << endl; cout << "selected ant1 = " << selectedAnt1 << endl << "selected ant2 = " << selectedAnt2 << endl; } delete mssel; } catch (std::exception& x) { cout << "ERROR: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/ms/MSSel/test/tMSAntennaGram.run000066400000000000000000000000421476623553700212460ustar00rootroot00000000000000#! /bin/sh echo "UNTESTED" exit 3 casacore-3.7.1/ms/MSSel/test/tMSAntennaGram2.cc000066400000000000000000000072021476623553700211160ustar00rootroot00000000000000//# Copyright (C) 1995,1996,1997,1999,2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main(int argc, const char* argv[]) { try { if(argc < 3) { cout << "Please input ms file and selection string on command line " << endl; return 3; } const String msName = argv[1]; cout << "ms file is " << msName << endl; MeasurementSet ms(msName); MSSelection mss; for(Int i=2;iflush(); if(mssel->nrow()==0) cout << "Check your input, No data selected" << endl; else { cout << "selected table has rows " << mssel->nrow() << endl; cout << "selected ant1 = " << mss.getAntenna1List() << endl << "selected ant2 = " << mss.getAntenna2List() << endl; } delete mssel; } catch (std::exception& x) { cout << "ERROR: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/ms/MSSel/test/tMSAntennaGram2.run000066400000000000000000000000421476623553700213300ustar00rootroot00000000000000#! /bin/sh echo "UNTESTED" exit 3 casacore-3.7.1/ms/MSSel/test/tMSAntennaGram3.cc000066400000000000000000000102621476623553700211170ustar00rootroot00000000000000//# tMSAntennaGram3.cc: Test program for TMSAntennaGram without need for an MS //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include using namespace casacore; void makeMS() { // Create an empty MS. TableDesc simpleDesc = MS::requiredTableDesc(); SetupNewTable newTab("tMSAntennaGram3_tmp.ms", simpleDesc, Table::New); MeasurementSet ms(newTab); ms.createDefaultSubtables(Table::New); ms.flush(); // Add entries to the ANTENNA subtable. Only names and positions are needed. MSAntenna& msant(ms.antenna()); MSAntennaColumns antcol(msant); msant.addRow(10); antcol.name().put (0, "RT0"); antcol.name().put (1, "RT1"); antcol.name().put (2, "A1.R2"); antcol.name().put (3, "20"); antcol.name().put (4, "1:4"); antcol.name().put (5, "1:5"); antcol.name().put (6, "A2.R1"); antcol.name().put (7, "A2.R2"); antcol.name().put (8, "A2:R1"); antcol.name().put (9, "A2:R2"); Vector xyz(3); double val = 1; for (int i=0; i<10; ++i) { xyz[0] = Quantity(val, "m"); xyz[1] = Quantity(val, "m"); xyz[2] = Quantity(val, "m"); MVPosition pos(xyz); antcol.positionMeas().put (i, MPosition(pos, MPosition::ITRF)); val *= 2; } } void doSel (const MeasurementSet& ms, const String& command, bool showBL=False) { cout << command << endl; Vector selectedAnts1; Vector selectedAnts2; Matrix selectedBaselines; msAntennaGramParseCommand (&ms, command, selectedAnts1, selectedAnts2, selectedBaselines); cout << " " << selectedAnts1 << ' ' << selectedAnts2 << endl; if (showBL) { cout << " " << selectedBaselines << endl; } } void selMS() { MeasurementSet ms("tMSAntennaGram3_tmp.ms"); doSel (ms, "20&&"); doSel (ms, "20"); doSel (ms, "1&20"); doSel (ms, "RT1&A1.R2"); doSel (ms, "RT1&A2:R2"); doSel (ms, "RT1&A2.R[123]"); doSel (ms, "RT1&A2:R[123]"); doSel (ms, "RT1&'A*[.:]R{1,2,3}'"); doSel (ms, "!RT1&'A*[.:]R{1,2,3}'"); doSel (ms, "RT1&A1.*"); doSel (ms, "1:4& 1:5"); doSel (ms, "'1:4'&'1:5'"); doSel (ms, "1,2 & 3,4; 5,6 & 7", True); doSel (ms, "\\RT1 & RT0"); doSel (ms, "/A.*/&", True); doSel (ms, "/A.*/&&", True); doSel (ms, "^A*&&", True); doSel (ms, "^/A.*/&&", True); doSel (ms, "!/A.*/&&", True); doSel (ms, "<3", True); doSel (ms, "1~5"); doSel (ms, "1m~5m", True); doSel (ms, "1.~5.", True); doSel (ms, "/(.*)R1&\\1R2/", True); doSel (ms, "/(.*).R1&\\1.R2/, /(.:)4&\\15/", True); doSel (ms, "^/(.*).R1&\\1.R2/", True); } int main() { try { makeMS(); selMS(); } catch (std::exception& x) { cout << "ERROR: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/ms/MSSel/test/tMSAntennaGram3.out000066400000000000000000000046101476623553700213410ustar00rootroot0000000000000020&& [3] [3] 20 [3] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 1&20 [1] [3] RT1&A1.R2 [1] [2] RT1&A2:R2 [1] [9] RT1&A2.R[123] [1] [6, 7] RT1&A2:R[123] [1] [8, 9] RT1&'A*[.:]R{1,2,3}' [1] [2, 6, 7, 8, 9] !RT1&'A*[.:]R{1,2,3}' [-1] [-2, -6, -7, -8, -9] RT1&A1.* [1] [2] 1:4& 1:5 [4] [5] '1:4'&'1:5' [4] [5] 1,2 & 3,4; 5,6 & 7 [1, 2, 5, 6] [3, 4, 7] Axis Lengths: [6, 2] (NB: Matrix in Row/Column order) [1, 3 1, 4 2, 3 2, 4 5, 7 6, 7] \RT1 & RT0 [1] [0] /A.*/& [2, 6, 7, 8, 9] [2, 6, 7, 8, 9] Axis Lengths: [10, 2] (NB: Matrix in Row/Column order) [2, 6 2, 7 2, 8 2, 9 6, 7 6, 8 6, 9 7, 8 7, 9 8, 9] /A.*/&& [2, 6, 7, 8, 9] [2, 6, 7, 8, 9] Axis Lengths: [15, 2] (NB: Matrix in Row/Column order) [2, 2 2, 6 2, 7 2, 8 2, 9 6, 6 6, 7 6, 8 6, 9 7, 7 7, 8 7, 9 8, 8 8, 9 9, 9] ^A*&& [0, 1, 3, 4, 5] [0, 1, 3, 4, 5] Axis Lengths: [15, 2] (NB: Matrix in Row/Column order) [0, 0 0, 1 0, 3 0, 4 0, 5 1, 1 1, 3 1, 4 1, 5 3, 3 3, 4 3, 5 4, 4 4, 5 5, 5] ^/A.*/&& [0, 1, 3, 4, 5] [0, 1, 3, 4, 5] Axis Lengths: [15, 2] (NB: Matrix in Row/Column order) [0, 0 0, 1 0, 3 0, 4 0, 5 1, 1 1, 3 1, 4 1, 5 3, 3 3, 4 3, 5 4, 4 4, 5 5, 5] !/A.*/&& [-2, -6, -7, -8, -9] [-2, -6, -7, -8, -9] Axis Lengths: [15, 2] (NB: Matrix in Row/Column order) [-2, -2 -2, -6 -2, -7 -2, -8 -2, -9 -6, -6 -6, -7 -6, -8 -6, -9 -7, -7 -7, -8 -7, -9 -8, -8 -8, -9 -9, -9] <3 [] [] Axis Lengths: [11, 2] (NB: Matrix in Row/Column order) [0, 0 0, 1 1, 1 2, 2 3, 3 4, 4 5, 5 6, 6 7, 7 8, 8 9, 9] 1~5 [1, 2, 3, 4, 5] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 1m~5m [] [] Axis Lengths: [2, 2] (NB: Matrix in Row/Column order) [0, 1 1, 2] 1.~5. [] [] Axis Lengths: [2, 2] (NB: Matrix in Row/Column order) [0, 1 1, 2] /(.*)R1&\1R2/ [] [] Axis Lengths: [2, 2] (NB: Matrix in Row/Column order) [6, 7 8, 9] /(.*).R1&\1.R2/, /(.:)4&\15/ [] [] Axis Lengths: [5, 2] (NB: Matrix in Row/Column order) [4, 5 6, 7 6, 9 8, 7 8, 9] ^/(.*).R1&\1.R2/ [] [] Axis Lengths: [55, 2] (NB: Matrix in Row/Column order) [0, 0 0, 1 0, 2 0, 3 0, 4 0, 5 0, 6 0, 7 0, 8 0, 9 1, 1 1, 2 1, 3 1, 4 1, 5 1, 6 1, 7 1, 8 1, 9 2, 2 2, 3 2, 4 2, 5 2, 6 2, 7 2, 8 2, 9 3, 3 3, 4 3, 5 3, 6 3, 7 3, 8 3, 9 4, 4 4, 5 4, 6 4, 7 4, 8 4, 9 5, 5 5, 6 5, 7 5, 8 5, 9 6, 6 6, 8 7, 6 7, 7 7, 8 7, 9 8, 8 9, 6 9, 8 9, 9] casacore-3.7.1/ms/MSSel/test/tMSCorrGram.cc000066400000000000000000000066521476623553700203650ustar00rootroot00000000000000//# Copyright (C) 1995,1996,1997,1999,2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main(int argc, const char* argv[]) { try { if(argc<3) { cout << " please input ms file and selection string on command line " << endl; return 0; } const String msName = argv[1]; MeasurementSet ms(msName); MeasurementSet * mssel; cout << "Original table has rows " << ms.nrow() << endl; if(msCorrGramParseCommand(&ms, argv[2])==0) { const TableExprNode *node = msCorrGramParseNode(); if(node->isNull()) { cout << "NULL node " << endl; return 0; } cout << "TableExprNode has rows = " << node->nrow() << endl; Table tablesel(ms.tableName(), Table::Update); mssel = new MeasurementSet(tablesel(*node, node->nrow() )); mssel->rename(ms.tableName()+"/SELECTED_TABLE", Table::Scratch); if(mssel->isColumnWritable("SELECTED_DATA")) mssel->flush(); if(mssel->nrow()==0) { cout << "Check your input, No data selected" << endl; } else { cout << "selected table has rows " << mssel->nrow() << endl; } delete mssel; } else { cout << "failed to parse expression" << endl; } } catch (std::exception& x) { cout << "ERROR: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/ms/MSSel/test/tMSCorrGram.run000077500000000000000000000000421476623553700205720ustar00rootroot00000000000000#! /bin/sh echo "UNTESTED" exit 3 casacore-3.7.1/ms/MSSel/test/tMSFeedGram.cc000066400000000000000000000073331476623553700203200ustar00rootroot00000000000000//# Copyright (C) 1995,1996,1997,1999,2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main(int argc, const char* argv[]) { try { if(argc < 3) { cout << "Please input ms file and selection string on command line " << endl; return 3; } const String msName = argv[1]; cout << "ms file is " << msName << endl; MeasurementSet ms(msName); MSSelection mss; mss.setFeedExpr(String(argv[2])); cout << "feed selection is " << String(argv[2]) << endl; TableExprNode node = mss.toTableExprNode(&ms); cout << "Original table has rows " << ms.nrow() << endl; Vector selectedFeed1, selectedFeed2; Matrix selectedFeedPairs; node = msFeedGramParseCommand(&ms, argv[2], selectedFeed1, selectedFeed2, selectedFeedPairs); if(node.isNull()) { cout << "NULL node " << endl; return 0; } Table tablesel(ms.tableName(), Table::Update); MeasurementSet* mssel = new MeasurementSet(tablesel(node, node.nrow() )); mssel->rename(ms.tableName()+"/SELECTED_TABLE", Table::New); mssel->flush(); if(mssel->nrow()==0) { cout << "Check your input, No data selected" << endl; } else { cout << "Selected table has nrows " << mssel->nrow() << endl; cout << "Selected feed1 = " << selectedFeed1 << endl << "Selected feed2 = " << selectedFeed2 << endl; } delete mssel; } catch (std::exception& x) { cout << "ERROR: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/ms/MSSel/test/tMSFieldGram.cc000066400000000000000000000066621476623553700205040ustar00rootroot00000000000000//# Copyright (C) 1995,1996,1997,1999,2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main(int argc, const char* argv[]) { try { if(argc < 3) { cout << "Please input selection string on command line " << endl; return 0; } const String msName = argv[1]; MeasurementSet ms(msName); MeasurementSet * mssel; cout << "Original table has rows " << ms.nrow() << endl; Vector selectedIDs; TableExprNode colAsTen=ms.col(MS::columnName(MS::FIELD_ID)); const TableExprNode node = msFieldGramParseCommand(ms.field(), colAsTen, argv[2],selectedIDs); if(node.isNull()) { cout << "NULL node " << endl; return 0; } cout << "TableExprNode has rows = " << node.nrow() << endl; Table tablesel(ms.tableName(), Table::Update); mssel = new MeasurementSet(tablesel(node, node.nrow() )); mssel->rename(ms.tableName()+"/SELECTED_TABLE", Table::Scratch); mssel->flush(); if(mssel->nrow()==0) cout << "Check your input, No data selected" << endl; else { cout << "selected table has rows " << mssel->nrow() << endl; cout << "Field IDs selected = " << selectedIDs << endl; } delete mssel; } catch (std::exception& x) { cout << "ERROR: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/ms/MSSel/test/tMSFieldGram.run000066400000000000000000000000421476623553700207050ustar00rootroot00000000000000#! /bin/sh echo "UNTESTED" exit 3 casacore-3.7.1/ms/MSSel/test/tMSScanGram.cc000066400000000000000000000066211476623553700203400ustar00rootroot00000000000000//# Copyright (C) 1995,1996,1997,1999,2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main(int argc, const char* argv[]) { if (argc != 2) { cout << "Usage: "<< argv[0] << " MS_filename" << endl; return 0; } try { cout << "before ms constructor called " << endl; const String msName = argv[1]; MeasurementSet ms(msName); MeasurementSet * mssel; cout << "Original table has rows " << ms.nrow() << endl; Vector selectedIds; const TableExprNode node = msScanGramParseCommand(&ms, "1", selectedIds); if (!node.isNull()) { cout << "TableExprNode has rows = " << node.nrow() << endl; Table tablesel(ms.tableName(), Table::Update); mssel = new MeasurementSet(tablesel(node, node.nrow() )); cout << "After mssel constructor called " << endl; mssel->rename(ms.tableName()+"/SELECTED_TABLE", Table::Scratch); mssel->flush(); if(mssel->nrow()==0) { cout << "Check your input, No data selected" << endl; } else { cout << "selected table has rows " << mssel->nrow() << endl; } delete mssel; } else { cout << "ERROR: failed to parse expression " << endl; } } catch (std::exception& x) { cout << "ERROR: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/ms/MSSel/test/tMSScanGram.run000066400000000000000000000000421476623553700205460ustar00rootroot00000000000000#! /bin/sh echo "UNTESTED" exit 3 casacore-3.7.1/ms/MSSel/test/tMSSelection.cc000066400000000000000000000255441476623553700205770ustar00rootroot00000000000000//# Copyright (C) 1995,1996,1997,1999,2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include using namespace std; using namespace casacore; // //------------------------------------------------------------------------- // void UI(int argc, char **argv, string& MSNBuf, string& OutMSBuf, bool& deepCopy, string& fieldStr, string& timeStr, string& spwStr, string& baselineStr, string& scanStr, string& arrayStr, string& uvdistStr,string& taqlStr, string& polnStr, string& stateObsModeStr, string& observationStr, bool& installEH) { Input inputs(1); inputs.create("ms",MSNBuf,"Input MS Name"); inputs.create("outms",OutMSBuf,"Output MS Name"); inputs.create("deepcopy","0","Make a deepcopy in the output?"); inputs.create("field",fieldStr,"FIELD selection expr."); inputs.create("time",timeStr,"TIME selection expr."); inputs.create("spw",spwStr,"SPW selection expr."); inputs.create("poln",polnStr,"POLN selection expr."); inputs.create("baseline",baselineStr,"BASELINE selection expr."); inputs.create("scan",scanStr,"SCAN selection expr."); inputs.create("array",arrayStr,"ARRAY selection expr."); inputs.create("uvdist",uvdistStr,"UVDIST selection expr."); inputs.create("stateobsmode",stateObsModeStr,"STATE selection expr."); inputs.create("observation",observationStr,"OBS selection expr."); inputs.create("taql",taqlStr,"TaQL selection expr."); inputs.create("installeh","1","Install LogError handlers?"); inputs.readArguments(argc, argv); MSNBuf=inputs.getString("ms"); OutMSBuf=inputs.getString("outms"); deepCopy=inputs.getBool("deepcopy"); fieldStr=inputs.getString("field"); timeStr=inputs.getString("time"); spwStr=inputs.getString("spw"); polnStr=inputs.getString("poln"); baselineStr=inputs.getString("baseline"); scanStr=inputs.getString("scan"); arrayStr=inputs.getString("array"); uvdistStr=inputs.getString("uvdist"); stateObsModeStr=inputs.getString("stateobsmode"); observationStr=inputs.getString("observation"); taqlStr=inputs.getString("taql"); installEH=inputs.getBool("installeh"); } // //------------------------------------------------------------------------- // void showTableCache() { const TableCache& cache = PlainTable::tableCache(); Vector lockedTables = cache.getTableNames(); Int n=lockedTables.nelements(); if(n > 0) cout << endl << "####WARNING!!!!: The Table Cache has the following " << n << " entries:" << endl; for (Int i=0; i list,ostream& os) { os << "\tBaselines = "; IPosition shp=list.shape(); for(Int j=0;j 0) cout << "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << endl; } // //------------------------------------------------------------------------- // int main(int argc, char **argv) { // //--------------------------------------------------- // // MSSelection msSelection; string MSNBuf,OutMSBuf,fieldStr,timeStr,spwStr,baselineStr, uvdistStr,taqlStr,scanStr,arrayStr, polnStr,stateObsModeStr, observationStr; Bool deepCopy=0,installEH=1; MSNBuf=OutMSBuf=fieldStr=timeStr=spwStr=baselineStr= uvdistStr=taqlStr=scanStr=arrayStr=polnStr=stateObsModeStr=observationStr=""; deepCopy=0; fieldStr=spwStr="*"; fieldStr=spwStr=""; UI(argc, argv, MSNBuf,OutMSBuf, deepCopy, fieldStr,timeStr,spwStr,baselineStr,scanStr,arrayStr, uvdistStr,taqlStr,polnStr,stateObsModeStr,observationStr, installEH); // //--------------------------------------------------- // // MS ms(MSNBuf,Table::Update),selectedMS(ms); // // Make a new scope, outside of which there should be no tables left open. // { try { MS ms(MSNBuf,TableLock(TableLock::AutoNoReadLocking)),selectedMS(ms); // // Setup the MSSelection thingi // MSInterface msInterface(ms); MSSelection msSelection; if (installEH) { // // Install error handlers such that it also tests user // defined error handlers having shorter life-cycle than // the MSSelection object. // MSSelectionLogError mssLEA,mssLES, mssLESpw, mssLEF; msSelection.setErrorHandler(MSSelection::ANTENNA_EXPR, &mssLEA,True); msSelection.setErrorHandler(MSSelection::STATE_EXPR, &mssLES,True); msSelection.setErrorHandler(MSSelection::SPW_EXPR, &mssLESpw,True); msSelection.setErrorHandler(MSSelection::FEED_EXPR, &mssLEF,True); } // msSelection.reset(ms,MSSelection::PARSE_NOW, // timeStr,baselineStr,fieldStr,spwStr, // uvdistStr,taqlStr,polnStr,scanStr,arrayStr, // stateObsModeStr,observationStr); msSelection.reset(msInterface,MSSelection::PARSE_NOW, timeStr,baselineStr,fieldStr,spwStr, uvdistStr,taqlStr,polnStr,scanStr,arrayStr, stateObsModeStr,observationStr); // TableExprNode ten=msSelection.toTableExprNode(&msInterface); // cerr << "TEN rows = " << ten.nrow() << endl; Int nRows=0; try { msSelection.getSelectedMS(selectedMS); nRows = selectedMS.nrow(); } catch(MSSelectionNullSelection& x) { printInfo(msSelection,nRows); throw(x); } printInfo(msSelection,nRows); if (nRows==0) { cout << "###Informational: Nothing selected. "; if (OutMSBuf != "") cout << "New MS not written." << endl; else cout << endl; } else { if (OutMSBuf != "") { if (deepCopy) selectedMS.deepCopy(OutMSBuf,Table::New); else selectedMS.rename(OutMSBuf,Table::New); } } } catch (MSSelectionError& x) { cout << "###MSSelectionError: " << x.what() << endl; cout << "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << endl; } // // Catch any exception thrown by AIPS++ libs. Do your cleanup here // before returning to the UI (if you choose to). Without this, all // exceptions (AIPS++ or otherwise) are caught in the default // exception handler (which is installed by the CLLIB as the // clDefaultErrorHandler). // catch (AipsError& x) { cout << "###AipsError: " << x.what() << endl; } catch (std::exception& x) { cout << "###std::exception: " << x.what() << endl; } } // // There should be no tables in the cache outside the scope of the // MSSelection object. // showTableCache(); } casacore-3.7.1/ms/MSSel/test/tMSSelection.out000066400000000000000000001541521476623553700210170ustar00rootroot00000000000000BE: Baseline Expr=1,2,3 BE: Ant1 = [1, 2, 3] BE: Ant2 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] Baselines = 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 0 2 3 4 5 6 7 8 9 10 11 12 0 3 4 5 6 7 8 9 10 11 12 0 4 5 6 7 8 9 10 11 12 FE: Field Expr= FE: Field = [] SE: SPW Expr= SE: SPW = [] SE: Chan = Axis Lengths: [0, 0] [] SE: Freq = Axis Lengths: [0, 0] [] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr= TE: Time = Axis Lengths: [3, 0] [] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [] StateList = [] Number of selected rows: 276 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=1,2,3& BE: Ant1 = [1, 2, 3] BE: Ant2 = [1, 2, 3] Baselines = 1 1 2 2 3 3 FE: Field Expr= FE: Field = [] SE: SPW Expr= SE: SPW = [] SE: Chan = Axis Lengths: [0, 0] [] SE: Freq = Axis Lengths: [0, 0] [] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr= TE: Time = Axis Lengths: [3, 0] [] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [] StateList = [] Number of selected rows: 0 ###MSSelectionError: MSSelectionNullSelection : The selected table has zero rows. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=1,2,3&* BE: Ant1 = [1, 2, 3] BE: Ant2 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] Baselines = 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 0 2 3 4 5 6 7 8 9 10 11 12 0 3 4 5 6 7 8 9 10 11 12 0 4 5 6 7 8 9 10 11 12 FE: Field Expr= FE: Field = [] SE: SPW Expr= SE: SPW = [] SE: Chan = Axis Lengths: [0, 0] [] SE: Freq = Axis Lengths: [0, 0] [] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr= TE: Time = Axis Lengths: [3, 0] [] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [] StateList = [] Number of selected rows: 276 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=1,2,3&& BE: Ant1 = [1, 2, 3] BE: Ant2 = [1, 2, 3] Baselines = 1 1 1 2 2 3 1 2 3 2 3 3 FE: Field Expr= FE: Field = [] SE: SPW Expr= SE: SPW = [] SE: Chan = Axis Lengths: [0, 0] [] SE: Freq = Axis Lengths: [0, 0] [] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr= TE: Time = Axis Lengths: [3, 0] [] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [] StateList = [] Number of selected rows: 0 ###MSSelectionError: MSSelectionNullSelection : The selected table has zero rows. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=1,2,3&&& BE: Ant1 = [1, 2, 3] BE: Ant2 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] Baselines = 1 2 3 1 2 3 FE: Field Expr= FE: Field = [] SE: SPW Expr= SE: SPW = [] SE: Chan = Axis Lengths: [0, 0] [] SE: Freq = Axis Lengths: [0, 0] [] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr= TE: Time = Axis Lengths: [3, 0] [] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [] StateList = [] Number of selected rows: 0 ###MSSelectionError: MSSelectionNullSelection : The selected table has zero rows. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=!1,2,3&&& BE: Ant1 = [-1, -2, -3] BE: Ant2 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] Baselines = FE: Field Expr= FE: Field = [] SE: SPW Expr= SE: SPW = [] SE: Chan = Axis Lengths: [0, 0] [] SE: Freq = Axis Lengths: [0, 0] [] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr= TE: Time = Axis Lengths: [3, 0] [] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [] StateList = [] Number of selected rows: 1058 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=1,2,3&4,5,6 BE: Ant1 = [1, 2, 3] BE: Ant2 = [4, 5, 6] Baselines = 1 1 1 2 2 2 3 3 3 4 5 6 4 5 6 4 5 6 FE: Field Expr= FE: Field = [] SE: SPW Expr= SE: SPW = [] SE: Chan = Axis Lengths: [0, 0] [] SE: Freq = Axis Lengths: [0, 0] [] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr= TE: Time = Axis Lengths: [3, 0] [] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [] StateList = [] Number of selected rows: 0 ###MSSelectionError: MSSelectionNullSelection : The selected table has zero rows. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=!1,2,3&4,5,6 BE: Ant1 = [-1, -2, -3] BE: Ant2 = [-4, -5, -6] Baselines = -1 -1 -1 -2 -2 -2 -3 -3 -3 -4 -5 -6 -4 -5 -6 -4 -5 -6 FE: Field Expr= FE: Field = [] SE: SPW Expr= SE: SPW = [] SE: Chan = Axis Lengths: [0, 0] [] SE: Freq = Axis Lengths: [0, 0] [] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr= TE: Time = Axis Lengths: [3, 0] [] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [] StateList = [] Number of selected rows: 1058 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=!1,2,3& BE: Ant1 = [-1, -2, -3] BE: Ant2 = [-1, -2, -3] Baselines = -1 -1 -2 -2 -3 -3 FE: Field Expr= FE: Field = [] SE: SPW Expr= SE: SPW = [] SE: Chan = Axis Lengths: [0, 0] [] SE: Freq = Axis Lengths: [0, 0] [] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr= TE: Time = Axis Lengths: [3, 0] [] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [] StateList = [] Number of selected rows: 1058 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=1,2,3&;1,2,3&& BE: Ant1 = [1, 2, 3] BE: Ant2 = [1, 2, 3] Baselines = 1 1 2 1 2 3 2 3 3 1 2 3 FE: Field Expr= FE: Field = [] SE: SPW Expr= SE: SPW = [] SE: Chan = Axis Lengths: [0, 0] [] SE: Freq = Axis Lengths: [0, 0] [] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr= TE: Time = Axis Lengths: [3, 0] [] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [] StateList = [] Number of selected rows: 0 ###MSSelectionError: MSSelectionNullSelection : The selected table has zero rows. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=1,2,3&;1,2,3&&& BE: Ant1 = [1, 2, 3] BE: Ant2 = [1, 2, 3, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12] Baselines = 1 1 2 1 2 3 2 3 3 1 2 3 FE: Field Expr= FE: Field = [] SE: SPW Expr= SE: SPW = [] SE: Chan = Axis Lengths: [0, 0] [] SE: Freq = Axis Lengths: [0, 0] [] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr= TE: Time = Axis Lengths: [3, 0] [] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [] StateList = [] Number of selected rows: 0 ###MSSelectionError: MSSelectionNullSelection : The selected table has zero rows. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=1,2,3&;!1,2,3&&& BE: Ant1 = [1, 2, 3, -1, -2, -3] BE: Ant2 = [1, 2, 3, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12] Baselines = 1 1 2 2 3 3 FE: Field Expr= FE: Field = [] SE: SPW Expr= SE: SPW = [] SE: Chan = Axis Lengths: [0, 0] [] SE: Freq = Axis Lengths: [0, 0] [] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr= TE: Time = Axis Lengths: [3, 0] [] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [] StateList = [] Number of selected rows: 0 ###MSSelectionError: MSSelectionNullSelection : The selected table has zero rows. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=*;!1&2,3,4 BE: Ant1 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, -1] BE: Ant2 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, -2, -3, -4] Baselines = 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 5 5 5 5 5 5 5 6 6 6 6 6 6 7 7 7 7 7 8 8 8 8 9 9 9 10 10 11 -1 -1 -1 1 2 3 4 5 6 7 8 9 10 11 12 2 3 4 5 6 7 8 9 10 11 12 3 4 5 6 7 8 9 10 11 12 4 5 6 7 8 9 10 11 12 5 6 7 8 9 10 11 12 6 7 8 9 10 11 12 7 8 9 10 11 12 8 9 10 11 12 9 10 11 12 10 11 12 11 12 12 -2 -3 -4 FE: Field Expr= FE: Field = [] SE: SPW Expr= SE: SPW = [] SE: Chan = Axis Lengths: [0, 0] [] SE: Freq = Axis Lengths: [0, 0] [] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr= TE: Time = Axis Lengths: [3, 0] [] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [] StateList = [] Number of selected rows: 1058 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11 BE: Ant1 = [5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 3, 4, -5, -11] BE: Ant2 = [9, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12] Baselines = 5 5 6 6 7 7 8 8 9 10 10 12 12 0 0 1 1 2 2 3 3 4 4 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 9 11 9 11 9 11 9 11 11 9 11 9 11 9 11 9 11 9 11 9 11 9 11 0 1 2 3 4 5 6 7 8 9 10 11 12 0 1 2 3 4 5 6 7 8 9 10 11 12 FE: Field Expr= FE: Field = [] SE: SPW Expr= SE: SPW = [] SE: Chan = Axis Lengths: [0, 0] [] SE: Freq = Axis Lengths: [0, 0] [] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr= TE: Time = Axis Lengths: [3, 0] [] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [] StateList = [] Number of selected rows: 460 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=DV*@A1*,DV*@P*,DV*@S*&(DV*)@(P*);!5;!11 BE: Ant1 = [5, 6, 7, 8, 10, 12, 9, 11, -5, -11] BE: Ant2 = [9, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12] Baselines = 5 5 6 6 7 7 8 8 10 10 12 12 9 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 9 11 9 11 9 11 9 11 9 11 9 11 11 0 1 2 3 4 5 6 7 8 9 10 11 12 0 1 2 3 4 5 6 7 8 9 10 11 12 FE: Field Expr= FE: Field = [] SE: SPW Expr= SE: SPW = [] SE: Chan = Axis Lengths: [0, 0] [] SE: Freq = Axis Lengths: [0, 0] [] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr= TE: Time = Axis Lengths: [3, 0] [] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [] StateList = [] Number of selected rows: 230 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=DV*@A1*,DV*@P*,DV*@S*;!20;!21 BE: Ant1 = [5, 6, 7, 8, 10, 12, 9, 11] BE: Ant2 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] Baselines = 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 6 6 6 6 7 7 7 7 7 7 7 7 7 7 8 8 8 8 8 8 8 8 8 10 10 10 10 10 10 10 10 12 12 12 12 12 12 12 9 9 9 9 9 9 11 11 11 11 11 0 1 2 3 4 6 7 8 9 10 11 12 0 1 2 3 4 7 8 9 10 11 12 0 1 2 3 4 8 9 10 11 12 0 1 2 3 4 9 10 11 12 0 1 2 3 4 9 11 12 0 1 2 3 4 9 11 0 1 2 3 4 11 0 1 2 3 4 FE: Field Expr= FE: Field = [] SE: SPW Expr= SE: SPW = [] SE: Chan = Axis Lengths: [0, 0] [] SE: Freq = Axis Lengths: [0, 0] [] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr= TE: Time = Axis Lengths: [3, 0] [] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [] StateList = [] Number of selected rows: 1058 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=1,2,3& BE: Ant1 = [1, 2, 3] BE: Ant2 = [1, 2, 3] Baselines = 1 1 2 2 3 3 FE: Field Expr= FE: Field = [] SE: SPW Expr=ALMA* SE: SPW = [1, 2] SE: Chan = Axis Lengths: [2, 4] (NB: Matrix in Row/Column order) [1, 0, 63, 1 2, 0, 0, 1] SE: Freq = Axis Lengths: [2, 4] (NB: Matrix in Row/Column order) [1, 8.62903e+10, 8.43216e+10, -3.125e+07 2, 8.52903e+10, 8.52903e+10, 1.8125e+09] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr= TE: Time = Axis Lengths: [3, 0] [] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [1, 2] StateList = [] Number of selected rows: 0 ###MSSelectionError: MSSelectionNullSelection : The selected table has zero rows. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=1,2,3& BE: Ant1 = [1, 2, 3] BE: Ant2 = [1, 2, 3] Baselines = 1 1 2 2 3 3 FE: Field Expr= FE: Field = [] SE: SPW Expr=ALMA*FULL*:85290~86290.305MHz SE: SPW = [1] SE: Chan = Axis Lengths: [1, 4] (NB: Matrix in Row/Column order) [1, 1, 32, 1] SE: Freq = Axis Lengths: [1, 4] (NB: Matrix in Row/Column order) [1, 8.62591e+10, 8.52903e+10, -3.125e+07] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr= TE: Time = Axis Lengths: [3, 0] [] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [1] StateList = [] Number of selected rows: 0 ###MSSelectionError: MSSelectionNullSelection : The selected table has zero rows. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=1,2,3& BE: Ant1 = [1, 2, 3] BE: Ant2 = [1, 2, 3] Baselines = 1 1 2 2 3 3 FE: Field Expr= FE: Field = [] SE: SPW Expr=ALMA*FULL*:85290~96290.305MHz SE: SPW = [1] SE: Chan = Axis Lengths: [1, 4] (NB: Matrix in Row/Column order) [1, 0, 32, 1] SE: Freq = Axis Lengths: [1, 4] (NB: Matrix in Row/Column order) [1, 8.62903e+10, 8.52903e+10, -3.125e+07] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr= TE: Time = Axis Lengths: [3, 0] [] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [1] StateList = [] Number of selected rows: 0 ###MSSelectionError: MSSelectionNullSelection : The selected table has zero rows. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=1,2,3& BE: Ant1 = [1, 2, 3] BE: Ant2 = [1, 2, 3] Baselines = 1 1 2 2 3 3 FE: Field Expr= FE: Field = [] SE: SPW Expr=ALMA*FULL*:75290~86290.305MHz SE: SPW = [1] SE: Chan = Axis Lengths: [1, 4] (NB: Matrix in Row/Column order) [1, 1, 63, 1] SE: Freq = Axis Lengths: [1, 4] (NB: Matrix in Row/Column order) [1, 8.62591e+10, 8.43216e+10, -3.125e+07] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr= TE: Time = Axis Lengths: [3, 0] [] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [1] StateList = [] Number of selected rows: 0 ###MSSelectionError: MSSelectionNullSelection : The selected table has zero rows. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*) BE: Ant1 = [5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 3, 4] BE: Ant2 = [9, 11] Baselines = 5 5 6 6 7 7 8 8 9 10 10 12 12 0 0 1 1 2 2 3 3 4 4 9 11 9 11 9 11 9 11 11 9 11 9 11 9 11 9 11 9 11 9 11 9 11 FE: Field Expr= FE: Field = [] SE: SPW Expr=ALMA*FULL*:5~10;20~30;50~70 SE: SPW = [1] SE: Chan = Axis Lengths: [3, 4] (NB: Matrix in Row/Column order) [1, 5, 10, 1 1, 20, 30, 1 1, 50, 63, 1] SE: Freq = Axis Lengths: [3, 4] (NB: Matrix in Row/Column order) [1, 8.61341e+10, 8.59778e+10, -3.125e+07 1, 8.56653e+10, 8.53528e+10, -3.125e+07 1, 8.47278e+10, 8.43216e+10, -3.125e+07] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr= TE: Time = Axis Lengths: [3, 0] [] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [1] StateList = [] Number of selected rows: 529 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*) BE: Ant1 = [5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 3, 4] BE: Ant2 = [9, 11] Baselines = 5 5 6 6 7 7 8 8 9 10 10 12 12 0 0 1 1 2 2 3 3 4 4 9 11 9 11 9 11 9 11 11 9 11 9 11 9 11 9 11 9 11 9 11 9 11 FE: Field Expr= FE: Field = [] SE: SPW Expr="ALMA_RB_03#BB_1#SW-01#FULL_RES" SE: SPW = [1] SE: Chan = Axis Lengths: [1, 4] (NB: Matrix in Row/Column order) [1, 0, 63, 1] SE: Freq = Axis Lengths: [1, 4] (NB: Matrix in Row/Column order) [1, 8.62903e+10, 8.43216e+10, -3.125e+07] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr= TE: Time = Axis Lengths: [3, 0] [] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [1] StateList = [] Number of selected rows: 529 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ###MSSelectionError: No Spw ID(s) matched specifications (near char. 0 in string ""ALMA_RB_03#BB_1#SW-01#FULL_RE"") +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11 BE: Ant1 = [5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 3, 4, -5, -11] BE: Ant2 = [9, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12] Baselines = 5 5 6 6 7 7 8 8 9 10 10 12 12 0 0 1 1 2 2 3 3 4 4 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 9 11 9 11 9 11 9 11 11 9 11 9 11 9 11 9 11 9 11 9 11 9 11 0 1 2 3 4 5 6 7 8 9 10 11 12 0 1 2 3 4 5 6 7 8 9 10 11 12 FE: Field Expr= FE: Field = [] SE: SPW Expr=ALMA*FULL*:5~10;20~30;50~70 SE: SPW = [1] SE: Chan = Axis Lengths: [3, 4] (NB: Matrix in Row/Column order) [1, 5, 10, 1 1, 20, 30, 1 1, 50, 63, 1] SE: Freq = Axis Lengths: [3, 4] (NB: Matrix in Row/Column order) [1, 8.61341e+10, 8.59778e+10, -3.125e+07 1, 8.56653e+10, 8.53528e+10, -3.125e+07 1, 8.47278e+10, 8.43216e+10, -3.125e+07] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr= TE: Time = Axis Lengths: [3, 0] [] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [1] StateList = [] Number of selected rows: 230 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11 BE: Ant1 = [5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 3, 4, -5, -11] BE: Ant2 = [9, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12] Baselines = 5 5 6 6 7 7 8 8 9 10 10 12 12 0 0 1 1 2 2 3 3 4 4 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 9 11 9 11 9 11 9 11 11 9 11 9 11 9 11 9 11 9 11 9 11 9 11 0 1 2 3 4 5 6 7 8 9 10 11 12 0 1 2 3 4 5 6 7 8 9 10 11 12 FE: Field Expr=0 FE: Field = [0] SE: SPW Expr=0~4:5~10;20~30;50~70 SE: SPW = [0, 1, 2] SE: Chan = Axis Lengths: [9, 4] (NB: Matrix in Row/Column order) [0, 3, 3, 1 0, 3, 3, 1 0, 3, 3, 1 1, 5, 10, 1 1, 20, 30, 1 1, 50, 63, 1 2, 0, 0, 1 2, 0, 0, 1 2, 0, 0, 1] SE: Freq = Axis Lengths: [9, 4] (NB: Matrix in Row/Column order) [0, 1.9055e+11, 1.9055e+11, 1.875e+09 0, 1.9055e+11, 1.9055e+11, 1.875e+09 0, 1.9055e+11, 1.9055e+11, 1.875e+09 1, 8.61341e+10, 8.59778e+10, -3.125e+07 1, 8.56653e+10, 8.53528e+10, -3.125e+07 1, 8.47278e+10, 8.43216e+10, -3.125e+07 2, 8.52903e+10, 8.52903e+10, 1.8125e+09 2, 8.52903e+10, 8.52903e+10, 1.8125e+09 2, 8.52903e+10, 8.52903e+10, 1.8125e+09] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr= TE: Time = Axis Lengths: [3, 0] [] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [0, 1, 2] StateList = [] Number of selected rows: 140 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11 BE: Ant1 = [5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 3, 4, -5, -11] BE: Ant2 = [9, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12] Baselines = 5 5 6 6 7 7 8 8 9 10 10 12 12 0 0 1 1 2 2 3 3 4 4 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 9 11 9 11 9 11 9 11 11 9 11 9 11 9 11 9 11 9 11 9 11 9 11 0 1 2 3 4 5 6 7 8 9 10 11 12 0 1 2 3 4 5 6 7 8 9 10 11 12 FE: Field Expr=J16* FE: Field = [0] SE: SPW Expr=ALMA*FULL*:5~10;20~30;50~70 SE: SPW = [1] SE: Chan = Axis Lengths: [3, 4] (NB: Matrix in Row/Column order) [1, 5, 10, 1 1, 20, 30, 1 1, 50, 63, 1] SE: Freq = Axis Lengths: [3, 4] (NB: Matrix in Row/Column order) [1, 8.61341e+10, 8.59778e+10, -3.125e+07 1, 8.56653e+10, 8.53528e+10, -3.125e+07 1, 8.47278e+10, 8.43216e+10, -3.125e+07] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr= TE: Time = Axis Lengths: [3, 0] [] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [1] StateList = [] Number of selected rows: 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11 BE: Ant1 = [5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 3, 4, -5, -11] BE: Ant2 = [9, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12] Baselines = 5 5 6 6 7 7 8 8 9 10 10 12 12 0 0 1 1 2 2 3 3 4 4 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 9 11 9 11 9 11 9 11 11 9 11 9 11 9 11 9 11 9 11 9 11 9 11 0 1 2 3 4 5 6 7 8 9 10 11 12 0 1 2 3 4 5 6 7 8 9 10 11 12 FE: Field Expr= FE: Field = [] SE: SPW Expr=ALMA*FULL*:5~10;20~30;50~70 SE: SPW = [1] SE: Chan = Axis Lengths: [3, 4] (NB: Matrix in Row/Column order) [1, 5, 10, 1 1, 20, 30, 1 1, 50, 63, 1] SE: Freq = Axis Lengths: [3, 4] (NB: Matrix in Row/Column order) [1, 8.61341e+10, 8.59778e+10, -3.125e+07 1, 8.56653e+10, 8.53528e+10, -3.125e+07 1, 8.47278e+10, 8.43216e+10, -3.125e+07] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr=*PHASE* StE: StateObsMode = [6, 7] AE: Array Expr= AE: Array = [] TE: Time Expr= TE: Time = Axis Lengths: [3, 0] [] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [1] StateList = [6, 7] Number of selected rows: 160 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11 BE: Ant1 = [5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 3, 4, -5, -11] BE: Ant2 = [9, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12] Baselines = 5 5 6 6 7 7 8 8 9 10 10 12 12 0 0 1 1 2 2 3 3 4 4 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 9 11 9 11 9 11 9 11 11 9 11 9 11 9 11 9 11 9 11 9 11 9 11 0 1 2 3 4 5 6 7 8 9 10 11 12 0 1 2 3 4 5 6 7 8 9 10 11 12 FE: Field Expr= FE: Field = [] SE: SPW Expr=ALMA*FULL*:5~10;20~30;50~70 SE: SPW = [1] SE: Chan = Axis Lengths: [3, 4] (NB: Matrix in Row/Column order) [1, 5, 10, 1 1, 20, 30, 1 1, 50, 63, 1] SE: Freq = Axis Lengths: [3, 4] (NB: Matrix in Row/Column order) [1, 8.61341e+10, 8.59778e+10, -3.125e+07 1, 8.56653e+10, 8.53528e+10, -3.125e+07 1, 8.47278e+10, 8.43216e+10, -3.125e+07] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr=*OFF* StE: StateObsMode = [0, 1] AE: Array Expr= AE: Array = [] TE: Time Expr= TE: Time = Axis Lengths: [3, 0] [] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [1] StateList = [0, 1] Number of selected rows: 30 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11 BE: Ant1 = [5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 3, 4, -5, -11] BE: Ant2 = [9, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12] Baselines = 5 5 6 6 7 7 8 8 9 10 10 12 12 0 0 1 1 2 2 3 3 4 4 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 9 11 9 11 9 11 9 11 11 9 11 9 11 9 11 9 11 9 11 9 11 9 11 0 1 2 3 4 5 6 7 8 9 10 11 12 0 1 2 3 4 5 6 7 8 9 10 11 12 FE: Field Expr= FE: Field = [] SE: SPW Expr=ALMA*FULL*:5~10;20~30;50~70 SE: SPW = [1] SE: Chan = Axis Lengths: [3, 4] (NB: Matrix in Row/Column order) [1, 5, 10, 1 1, 20, 30, 1 1, 50, 63, 1] SE: Freq = Axis Lengths: [3, 4] (NB: Matrix in Row/Column order) [1, 8.61341e+10, 8.59778e+10, -3.125e+07 1, 8.56653e+10, 8.53528e+10, -3.125e+07 1, 8.47278e+10, 8.43216e+10, -3.125e+07] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr=*OFF* StE: StateObsMode = [0, 1] AE: Array Expr= AE: Array = [] TE: Time Expr=*+0:0:1 TE: Time = Axis Lengths: [3, 1] (NB: Matrix in Row/Column order) [4.91793e+09 4.91793e+09 0] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [1] StateList = [0, 1] Number of selected rows: 10 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11 BE: Ant1 = [5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 3, 4, -5, -11] BE: Ant2 = [9, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12] Baselines = 5 5 6 6 7 7 8 8 9 10 10 12 12 0 0 1 1 2 2 3 3 4 4 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 9 11 9 11 9 11 9 11 11 9 11 9 11 9 11 9 11 9 11 9 11 9 11 0 1 2 3 4 5 6 7 8 9 10 11 12 0 1 2 3 4 5 6 7 8 9 10 11 12 FE: Field Expr= FE: Field = [] SE: SPW Expr=ALMA*FULL*:5~10;20~30;50~70 SE: SPW = [1] SE: Chan = Axis Lengths: [3, 4] (NB: Matrix in Row/Column order) [1, 5, 10, 1 1, 20, 30, 1 1, 50, 63, 1] SE: Freq = Axis Lengths: [3, 4] (NB: Matrix in Row/Column order) [1, 8.61341e+10, 8.59778e+10, -3.125e+07 1, 8.56653e+10, 8.53528e+10, -3.125e+07 1, 8.47278e+10, 8.43216e+10, -3.125e+07] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr=*OFF* StE: StateObsMode = [0, 1] AE: Array Expr= AE: Array = [] TE: Time Expr=*+0:0:1 TE: Time = Axis Lengths: [3, 1] (NB: Matrix in Row/Column order) [4.91793e+09 4.91793e+09 0] UVE: UVRange Expr=>1000m UVE: UVRange = Axis Lengths: [2, 1] (NB: Matrix in Row/Column order) [1000 3.40282e+38] UVE: UV in meters = [1] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [1] StateList = [0, 1] Number of selected rows: 8 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11 BE: Ant1 = [5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 3, 4, -5, -11] BE: Ant2 = [9, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12] Baselines = 5 5 6 6 7 7 8 8 9 10 10 12 12 0 0 1 1 2 2 3 3 4 4 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 9 11 9 11 9 11 9 11 11 9 11 9 11 9 11 9 11 9 11 9 11 9 11 0 1 2 3 4 5 6 7 8 9 10 11 12 0 1 2 3 4 5 6 7 8 9 10 11 12 FE: Field Expr= FE: Field = [] SE: SPW Expr=ALMA*FULL*:5~10;20~30;50~70 SE: SPW = [1] SE: Chan = Axis Lengths: [3, 4] (NB: Matrix in Row/Column order) [1, 5, 10, 1 1, 20, 30, 1 1, 50, 63, 1] SE: Freq = Axis Lengths: [3, 4] (NB: Matrix in Row/Column order) [1, 8.61341e+10, 8.59778e+10, -3.125e+07 1, 8.56653e+10, 8.53528e+10, -3.125e+07 1, 8.47278e+10, 8.43216e+10, -3.125e+07] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr=*OFF* StE: StateObsMode = [0, 1] AE: Array Expr= AE: Array = [] TE: Time Expr=*+0:0:1 TE: Time = Axis Lengths: [3, 1] (NB: Matrix in Row/Column order) [4.91793e+09 4.91793e+09 0] UVE: UVRange Expr=>1000m:50% UVE: UVRange = Axis Lengths: [2, 1] (NB: Matrix in Row/Column order) [500 5.10424e+38] UVE: UV in meters = [1] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [1] StateList = [0, 1] Number of selected rows: 9 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11 BE: Ant1 = [5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 3, 4, -5, -11] BE: Ant2 = [9, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12] Baselines = 5 5 6 6 7 7 8 8 9 10 10 12 12 0 0 1 1 2 2 3 3 4 4 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 9 11 9 11 9 11 9 11 11 9 11 9 11 9 11 9 11 9 11 9 11 9 11 0 1 2 3 4 5 6 7 8 9 10 11 12 0 1 2 3 4 5 6 7 8 9 10 11 12 FE: Field Expr= FE: Field = [] SE: SPW Expr=ALMA*FULL*:85290~86290.305MHz SE: SPW = [1] SE: Chan = Axis Lengths: [1, 4] (NB: Matrix in Row/Column order) [1, 1, 32, 1] SE: Freq = Axis Lengths: [1, 4] (NB: Matrix in Row/Column order) [1, 8.62591e+10, 8.52903e+10, -3.125e+07] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr=*OFF* StE: StateObsMode = [0, 1] AE: Array Expr= AE: Array = [] TE: Time Expr=*+0:0:1 TE: Time = Axis Lengths: [3, 1] (NB: Matrix in Row/Column order) [4.91793e+09 4.91793e+09 0] UVE: UVRange Expr=>1000m:50% UVE: UVRange = Axis Lengths: [2, 1] (NB: Matrix in Row/Column order) [500 5.10424e+38] UVE: UV in meters = [1] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr=XX PE: PolMap = {<0,[0]>, <1,[0]>} PE: CorrMap = {<0,[[0], [1, 2]]>, <1,[[0], [0]]>} =========================================================== DDIDs(Poln) = [1, 2, 0] DDIDs(SPW) = [1] StateList = [0, 1] Number of selected rows: 9 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11 BE: Ant1 = [5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 3, 4, -5, -11] BE: Ant2 = [9, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12] Baselines = 5 5 6 6 7 7 8 8 9 10 10 12 12 0 0 1 1 2 2 3 3 4 4 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 9 11 9 11 9 11 9 11 11 9 11 9 11 9 11 9 11 9 11 9 11 9 11 0 1 2 3 4 5 6 7 8 9 10 11 12 0 1 2 3 4 5 6 7 8 9 10 11 12 FE: Field Expr= FE: Field = [] SE: SPW Expr=ALMA*FULL*:85290~86290.305MHz SE: SPW = [1] SE: Chan = Axis Lengths: [1, 4] (NB: Matrix in Row/Column order) [1, 1, 32, 1] SE: Freq = Axis Lengths: [1, 4] (NB: Matrix in Row/Column order) [1, 8.62591e+10, 8.52903e+10, -3.125e+07] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr=*OFF* StE: StateObsMode = [0, 1] AE: Array Expr= AE: Array = [] TE: Time Expr=*+0:0:1 TE: Time = Axis Lengths: [3, 1] (NB: Matrix in Row/Column order) [4.91793e+09 4.91793e+09 0] UVE: UVRange Expr=>1000m:50% UVE: UVRange = Axis Lengths: [2, 1] (NB: Matrix in Row/Column order) [500 5.10424e+38] UVE: UV in meters = [1] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr=XX,YX PE: PolMap = {<0,[0, 2]>} PE: CorrMap = {<0,[[0, 2], [1, 2]]>} =========================================================== DDIDs(Poln) = [1, 2] DDIDs(SPW) = [1] StateList = [0, 1] Number of selected rows: 9 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ###MSSelectionError: State Expression: No match found for "*JUNK*" +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ###MSSelectionError: Spw Expression: No match found for "x" +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ###MSSelectionError: Antenna Expression: No match found for token(s) "x" +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11 BE: Ant1 = [5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 3, 4, -5, -11] BE: Ant2 = [9, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12] Baselines = 5 5 6 6 7 7 8 8 9 10 10 12 12 0 0 1 1 2 2 3 3 4 4 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 9 11 9 11 9 11 9 11 11 9 11 9 11 9 11 9 11 9 11 9 11 9 11 0 1 2 3 4 5 6 7 8 9 10 11 12 0 1 2 3 4 5 6 7 8 9 10 11 12 FE: Field Expr= FE: Field = [] SE: SPW Expr=ALMA*FULL*:85290~86290.305MHz SE: SPW = [1] SE: Chan = Axis Lengths: [1, 4] (NB: Matrix in Row/Column order) [1, 1, 32, 1] SE: Freq = Axis Lengths: [1, 4] (NB: Matrix in Row/Column order) [1, 8.62591e+10, 8.52903e+10, -3.125e+07] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr=*OFF*,*JUNK* StE: StateObsMode = [0, 1] AE: Array Expr= AE: Array = [] TE: Time Expr=*+0:0:1 TE: Time = Axis Lengths: [3, 1] (NB: Matrix in Row/Column order) [4.91793e+09 4.91793e+09 0] UVE: UVRange Expr=>1000m:50% UVE: UVRange = Axis Lengths: [2, 1] (NB: Matrix in Row/Column order) [500 5.10424e+38] UVE: UV in meters = [1] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr=XX,YX PE: PolMap = {<0,[0, 2]>} PE: CorrMap = {<0,[[0, 2], [1, 2]]>} =========================================================== DDIDs(Poln) = [1, 2] DDIDs(SPW) = [1] StateList = [0, 1] Number of selected rows: 9 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11 BE: Ant1 = [5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 3, 4, -5, -11] BE: Ant2 = [9, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12] Baselines = 5 5 6 6 7 7 8 8 9 10 10 12 12 0 0 1 1 2 2 3 3 4 4 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 9 11 9 11 9 11 9 11 11 9 11 9 11 9 11 9 11 9 11 9 11 9 11 0 1 2 3 4 5 6 7 8 9 10 11 12 0 1 2 3 4 5 6 7 8 9 10 11 12 FE: Field Expr= FE: Field = [] SE: SPW Expr=ALMA*FULL*:85290~86290.305MHz,x SE: SPW = [1] SE: Chan = Axis Lengths: [1, 4] (NB: Matrix in Row/Column order) [1, 1, 32, 1] SE: Freq = Axis Lengths: [1, 4] (NB: Matrix in Row/Column order) [1, 8.62591e+10, 8.52903e+10, -3.125e+07] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr=*OFF* StE: StateObsMode = [0, 1] AE: Array Expr= AE: Array = [] TE: Time Expr=*+0:0:1 TE: Time = Axis Lengths: [3, 1] (NB: Matrix in Row/Column order) [4.91793e+09 4.91793e+09 0] UVE: UVRange Expr=>1000m:50% UVE: UVRange = Axis Lengths: [2, 1] (NB: Matrix in Row/Column order) [500 5.10424e+38] UVE: UV in meters = [1] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr=XX,YX PE: PolMap = {<0,[0, 2]>} PE: CorrMap = {<0,[[0, 2], [1, 2]]>} =========================================================== DDIDs(Poln) = [1, 2] DDIDs(SPW) = [1] StateList = [0, 1] Number of selected rows: 9 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11;x BE: Ant1 = [5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 3, 4, -5, -11] BE: Ant2 = [9, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12] Baselines = 5 5 6 6 7 7 8 8 9 10 10 12 12 0 0 1 1 2 2 3 3 4 4 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 9 11 9 11 9 11 9 11 11 9 11 9 11 9 11 9 11 9 11 9 11 9 11 0 1 2 3 4 5 6 7 8 9 10 11 12 0 1 2 3 4 5 6 7 8 9 10 11 12 FE: Field Expr= FE: Field = [] SE: SPW Expr=ALMA*FULL*:85290~86290.305MHz SE: SPW = [1] SE: Chan = Axis Lengths: [1, 4] (NB: Matrix in Row/Column order) [1, 1, 32, 1] SE: Freq = Axis Lengths: [1, 4] (NB: Matrix in Row/Column order) [1, 8.62591e+10, 8.52903e+10, -3.125e+07] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr=*OFF* StE: StateObsMode = [0, 1] AE: Array Expr= AE: Array = [] TE: Time Expr=*+0:0:1 TE: Time = Axis Lengths: [3, 1] (NB: Matrix in Row/Column order) [4.91793e+09 4.91793e+09 0] UVE: UVRange Expr=>1000m:50% UVE: UVRange = Axis Lengths: [2, 1] (NB: Matrix in Row/Column order) [500 5.10424e+38] UVE: UV in meters = [1] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr=XX,YX PE: PolMap = {<0,[0, 2]>} PE: CorrMap = {<0,[[0, 2], [1, 2]]>} =========================================================== DDIDs(Poln) = [1, 2] DDIDs(SPW) = [1] StateList = [0, 1] Number of selected rows: 9 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11 BE: Ant1 = [5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 3, 4, -5, -11] BE: Ant2 = [9, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12] Baselines = 5 5 6 6 7 7 8 8 9 10 10 12 12 0 0 1 1 2 2 3 3 4 4 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 9 11 9 11 9 11 9 11 11 9 11 9 11 9 11 9 11 9 11 9 11 9 11 0 1 2 3 4 5 6 7 8 9 10 11 12 0 1 2 3 4 5 6 7 8 9 10 11 12 FE: Field Expr= FE: Field = [] SE: SPW Expr=ALMA*FULL*:85290~86290.305MHz SE: SPW = [1] SE: Chan = Axis Lengths: [1, 4] (NB: Matrix in Row/Column order) [1, 1, 32, 1] SE: Freq = Axis Lengths: [1, 4] (NB: Matrix in Row/Column order) [1, 8.62591e+10, 8.52903e+10, -3.125e+07] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr=0,100 StE: StateObsMode = [0] AE: Array Expr= AE: Array = [] TE: Time Expr=*+0:0:1 TE: Time = Axis Lengths: [3, 1] (NB: Matrix in Row/Column order) [4.91793e+09 4.91793e+09 0] UVE: UVRange Expr=>1000m:50% UVE: UVRange = Axis Lengths: [2, 1] (NB: Matrix in Row/Column order) [500 5.10424e+38] UVE: UV in meters = [1] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr=XX,YX PE: PolMap = {<0,[0, 2]>} PE: CorrMap = {<0,[[0, 2], [1, 2]]>} =========================================================== DDIDs(Poln) = [1, 2] DDIDs(SPW) = [1] StateList = [0] Number of selected rows: 0 ###MSSelectionError: MSSelectionNullSelection : The selected table has zero rows. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11 BE: Ant1 = [5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 3, 4, -5, -11] BE: Ant2 = [9, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12] Baselines = 5 5 6 6 7 7 8 8 9 10 10 12 12 0 0 1 1 2 2 3 3 4 4 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 9 11 9 11 9 11 9 11 11 9 11 9 11 9 11 9 11 9 11 9 11 9 11 0 1 2 3 4 5 6 7 8 9 10 11 12 0 1 2 3 4 5 6 7 8 9 10 11 12 FE: Field Expr= FE: Field = [] SE: SPW Expr=0,1,100 SE: SPW = [0, 1] SE: Chan = Axis Lengths: [2, 4] (NB: Matrix in Row/Column order) [0, 0, 3, 1 1, 0, 63, 1] SE: Freq = Axis Lengths: [2, 4] (NB: Matrix in Row/Column order) [0, 1.8455e+11, 1.9055e+11, 1.875e+09 1, 8.62903e+10, 8.43216e+10, -3.125e+07] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr=*OFF* StE: StateObsMode = [0, 1] AE: Array Expr= AE: Array = [] TE: Time Expr=*+0:0:1 TE: Time = Axis Lengths: [3, 1] (NB: Matrix in Row/Column order) [4.91793e+09 4.91793e+09 0] UVE: UVRange Expr=>1000m:50% UVE: UVRange = Axis Lengths: [2, 1] (NB: Matrix in Row/Column order) [500 5.10424e+38] UVE: UV in meters = [1] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr=XX,YX PE: PolMap = {<0,[0, 2]>} PE: CorrMap = {<0,[[0, 2], [1, 2]]>} =========================================================== DDIDs(Poln) = [1, 2] DDIDs(SPW) = [0, 1] StateList = [0, 1] Number of selected rows: 9 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=0,1,2;500 BE: Ant1 = [0, 1, 2] BE: Ant2 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] Baselines = 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 1 2 3 4 5 6 7 8 9 10 11 12 2 3 4 5 6 7 8 9 10 11 12 3 4 5 6 7 8 9 10 11 12 FE: Field Expr= FE: Field = [] SE: SPW Expr=ALMA*FULL*:85290~86290.305MHz SE: SPW = [1] SE: Chan = Axis Lengths: [1, 4] (NB: Matrix in Row/Column order) [1, 1, 32, 1] SE: Freq = Axis Lengths: [1, 4] (NB: Matrix in Row/Column order) [1, 8.62591e+10, 8.52903e+10, -3.125e+07] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr=*OFF* StE: StateObsMode = [0, 1] AE: Array Expr= AE: Array = [] TE: Time Expr=*+0:0:1 TE: Time = Axis Lengths: [3, 1] (NB: Matrix in Row/Column order) [4.91793e+09 4.91793e+09 0] UVE: UVRange Expr=>1000m:50% UVE: UVRange = Axis Lengths: [2, 1] (NB: Matrix in Row/Column order) [500 5.10424e+38] UVE: UV in meters = [1] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr=XX,YX PE: PolMap = {<0,[0, 2]>} PE: CorrMap = {<0,[[0, 2], [1, 2]]>} =========================================================== DDIDs(Poln) = [1, 2] DDIDs(SPW) = [1] StateList = [0, 1] Number of selected rows: 6 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11 BE: Ant1 = [5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 3, 4, -5, -11] BE: Ant2 = [9, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12] Baselines = 5 5 6 6 7 7 8 8 9 10 10 12 12 0 0 1 1 2 2 3 3 4 4 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -5 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 -11 9 11 9 11 9 11 9 11 11 9 11 9 11 9 11 9 11 9 11 9 11 9 11 0 1 2 3 4 5 6 7 8 9 10 11 12 0 1 2 3 4 5 6 7 8 9 10 11 12 FE: Field Expr= FE: Field = [] SE: SPW Expr=ALMA*FULL*:85290~86290.305MHz SE: SPW = [1] SE: Chan = Axis Lengths: [1, 4] (NB: Matrix in Row/Column order) [1, 1, 32, 1] SE: Freq = Axis Lengths: [1, 4] (NB: Matrix in Row/Column order) [1, 8.62591e+10, 8.52903e+10, -3.125e+07] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr=0,100 StE: StateObsMode = [0] AE: Array Expr= AE: Array = [] TE: Time Expr=*+0:0:1 TE: Time = Axis Lengths: [3, 1] (NB: Matrix in Row/Column order) [4.91793e+09 4.91793e+09 0] UVE: UVRange Expr=>1000m:50% UVE: UVRange = Axis Lengths: [2, 1] (NB: Matrix in Row/Column order) [500 5.10424e+38] UVE: UV in meters = [1] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr=XX,YX PE: PolMap = {<0,[0, 2]>} PE: CorrMap = {<0,[[0, 2], [1, 2]]>} =========================================================== DDIDs(Poln) = [1, 2] DDIDs(SPW) = [1] StateList = [0] Number of selected rows: 0 ###MSSelectionError: MSSelectionNullSelection : The selected table has zero rows. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ###MSSelectionError: Spw Expression: No match found for 100, +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ###MSSelectionError: No match found for the antenna specificion [ID(s): [500]] +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=DA41&DV14 BE: Ant1 = [0] BE: Ant2 = [9] Baselines = 0 9 FE: Field Expr= FE: Field = [] SE: SPW Expr=1 SE: SPW = [1] SE: Chan = Axis Lengths: [1, 4] (NB: Matrix in Row/Column order) [1, 0, 63, 1] SE: Freq = Axis Lengths: [1, 4] (NB: Matrix in Row/Column order) [1, 8.62903e+10, 8.43216e+10, -3.125e+07] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr=2014/09/20/10:38:0.9~2014/09/20/10:38:03.88 TE: Time = Axis Lengths: [3, 1] (NB: Matrix in Row/Column order) [4.91793e+09 4.91793e+09 0] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [1] StateList = [] Number of selected rows: 2 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=DA41&DV14 BE: Ant1 = [0] BE: Ant2 = [9] Baselines = 0 9 FE: Field Expr= FE: Field = [] SE: SPW Expr=1 SE: SPW = [1] SE: Chan = Axis Lengths: [1, 4] (NB: Matrix in Row/Column order) [1, 0, 63, 1] SE: Freq = Axis Lengths: [1, 4] (NB: Matrix in Row/Column order) [1, 8.62903e+10, 8.43216e+10, -3.125e+07] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr=[2014/09/20/10:38:1.1~2014/09/20/10:38:03.88] TE: Time = Axis Lengths: [3, 1] (NB: Matrix in Row/Column order) [4.91793e+09 4.91793e+09 0.96] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [1] StateList = [] Number of selected rows: 2 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr=DA41&DV14 BE: Ant1 = [0] BE: Ant2 = [9] Baselines = 0 9 FE: Field Expr= FE: Field = [] SE: SPW Expr=1 SE: SPW = [1] SE: Chan = Axis Lengths: [1, 4] (NB: Matrix in Row/Column order) [1, 0, 63, 1] SE: Freq = Axis Lengths: [1, 4] (NB: Matrix in Row/Column order) [1, 8.62903e+10, 8.43216e+10, -3.125e+07] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr=0.04[2014/09/20/10:38:1.1~2014/09/20/10:38:03.88] TE: Time = Axis Lengths: [3, 1] (NB: Matrix in Row/Column order) [4.91793e+09 4.91793e+09 0.04] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [1] StateList = [] Number of selected rows: 1 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ casacore-3.7.1/ms/MSSel/test/tMSSelection.run000066400000000000000000000200201476623553700207760ustar00rootroot00000000000000#!/bin/sh # ----------------------------------------------------------------------------- # Usage: tMSSelection.run # ----------------------------------------------------------------------------- # This script executes the program tMSSelection to test the # MSSelection module. #----------------------------------------------------------------------------- MS=$CASADEMO"mssel_test_small.ms" runCmd(){ # Uncomment following line to generate an output close to a # script to run the tests #echo "#Test:$1"; echo $3; eval "$2 $3" } getTestMS() { tar zxf $testsrcdir/$MS.tgz } cleanup() { \rm -rf $MS } # Get the test MS (un-tar it from $testsrcdir). getTestMS; runCmd 1 "$casa_checktool" " ./tMSSelection ms=$MS baseline='1,2,3'"; runCmd 2 "$casa_checktool" " ./tMSSelection ms=$MS baseline='1,2,3&'"; runCmd 3 "$casa_checktool" " ./tMSSelection ms=$MS baseline='1,2,3&*'"; runCmd 4 "$casa_checktool" " ./tMSSelection ms=$MS baseline='1,2,3&&'"; runCmd 5 "$casa_checktool" " ./tMSSelection ms=$MS baseline='1,2,3&&&'"; runCmd 6 "$casa_checktool" " ./tMSSelection ms=$MS baseline='!1,2,3&&&'"; runCmd 7 "$casa_checktool" " ./tMSSelection ms=$MS baseline='1,2,3&4,5,6'"; runCmd 8 "$casa_checktool" " ./tMSSelection ms=$MS baseline='!1,2,3&4,5,6'"; runCmd 9 "$casa_checktool" " ./tMSSelection ms=$MS baseline='!1,2,3&'"; runCmd 10 "$casa_checktool" " ./tMSSelection ms=$MS baseline='1,2,3&;1,2,3&&'"; runCmd 11 "$casa_checktool" " ./tMSSelection ms=$MS baseline='1,2,3&;1,2,3&&&'"; runCmd 12 "$casa_checktool" " ./tMSSelection ms=$MS baseline='1,2,3&;!1,2,3&&&'"; runCmd 13 "$casa_checktool" " ./tMSSelection ms=$MS baseline='*;!1&2,3,4'"; runCmd 14 "$casa_checktool" " ./tMSSelection ms=$MS baseline='(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11'" runCmd 15 "$casa_checktool" " ./tMSSelection ms=$MS baseline='DV*@A1*,DV*@P*,DV*@S*&(DV*)@(P*);!5;!11'" runCmd 16 "$casa_checktool" " ./tMSSelection ms=$MS baseline='DV*@A1*,DV*@P*,DV*@S*;!20;!21'" runCmd 17 "$casa_checktool" " ./tMSSelection ms=$MS baseline='1,2,3&' spw='ALMA*'"; # # Check with physical range spec. in SPW selection # runCmd 18 "$casa_checktool" " ./tMSSelection ms=$MS baseline='1,2,3&' spw='ALMA*FULL*:85290~86290.305MHz'"; # Spec. within limits runCmd 18a "$casa_checktool" " ./tMSSelection ms=$MS baseline='1,2,3&' spw='ALMA*FULL*:85290~96290.305MHz'"; # Spec. underflows runCmd 18b "$casa_checktool" " ./tMSSelection ms=$MS baseline='1,2,3&' spw='ALMA*FULL*:75290~86290.305MHz'"; # Spec. overflows # # Check name spec. in SPW selection # runCmd 19 "$casa_checktool" " ./tMSSelection ms=$MS baseline='(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*)' spw='ALMA*FULL*:5~10;20~30;50~70'"; # Name as pattern runCmd 19a "$casa_checktool" " ./tMSSelection ms=$MS baseline='(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*)' spw='\"ALMA_RB_03#BB_1#SW-01#FULL_RES\"'"; # Name as literal runCmd 19b "$casa_checktool" " ./tMSSelection ms=$MS baseline='(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*)' spw='\"ALMA_RB_03#BB_1#SW-01#FULL_RE\"'"; # Name as literal with error runCmd 20 "$casa_checktool" " ./tMSSelection ms=$MS baseline='(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11' spw='ALMA*FULL*:5~10;20~30;50~70'"; runCmd 21 "$casa_checktool" " ./tMSSelection ms=$MS baseline='(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11' spw='0~4:5~10;20~30;50~70' field='0'"; runCmd 22 "$casa_checktool" " ./tMSSelection ms=$MS baseline='(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11' spw='ALMA*FULL*:5~10;20~30;50~70' field='J16*'"; runCmd 23 "$casa_checktool" " ./tMSSelection ms=$MS baseline='(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11' spw='ALMA*FULL*:5~10;20~30;50~70' stateobsmode='*PHASE*'"; runCmd 24 "$casa_checktool" " ./tMSSelection ms=$MS baseline='(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11' spw='ALMA*FULL*:5~10;20~30;50~70' stateobsmode='*OFF*'"; runCmd 25 "$casa_checktool" " ./tMSSelection ms=$MS baseline='(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11' spw='ALMA*FULL*:5~10;20~30;50~70' stateobsmode='*OFF*' time='*+0:0:1'"; runCmd 26 "$casa_checktool" " ./tMSSelection ms=$MS baseline='(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11' spw='ALMA*FULL*:5~10;20~30;50~70' stateobsmode='*OFF*' time='*+0:0:1' uvdist='>1000m'"; runCmd 27 "$casa_checktool" " ./tMSSelection ms=$MS baseline='(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11' spw='ALMA*FULL*:5~10;20~30;50~70' stateobsmode='*OFF*' time='*+0:0:1' uvdist='>1000m:50%'"; runCmd 28 "$casa_checktool" " ./tMSSelection ms=$MS baseline='(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11' stateobsmode='*OFF*' spw='ALMA*FULL*:85290~86290.305MHz' time='*+0:0:1' uvdist='>1000m:50%' poln='XX'"; runCmd 29 "$casa_checktool" " ./tMSSelection ms=$MS baseline='(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11' stateobsmode='*OFF*' spw='ALMA*FULL*:85290~86290.305MHz' time='*+0:0:1' uvdist='>1000m:50%' poln='XX,YX'"; runCmd 30 "$casa_checktool" " ./tMSSelection ms=$MS baseline='(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11' stateobsmode='*OFF*,*JUNK*' spw='ALMA*FULL*:85290~86290.305MHz' time='*+0:0:1' uvdist='>1000m:50%' poln='XX,YX' installeh='0'"; runCmd 31 "$casa_checktool" " ./tMSSelection ms=$MS baseline='(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11' stateobsmode='*OFF*' spw='ALMA*FULL*:85290~86290.305MHz,x' time='*+0:0:1' uvdist='>1000m:50%' poln='XX,YX' installeh='0'"; runCmd 32 "$casa_checktool" " ./tMSSelection ms=$MS baseline='(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11;x' stateobsmode='*OFF*' spw='ALMA*FULL*:85290~86290.305MHz' time='*+0:0:1' uvdist='>1000m:50%' poln='XX,YX' installeh='0'"; runCmd 33 "$casa_checktool" " ./tMSSelection ms=$MS baseline='(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11' stateobsmode='*OFF*,*JUNK*' spw='ALMA*FULL*:85290~86290.305MHz' time='*+0:0:1' uvdist='>1000m:50%' poln='XX,YX' installeh='1'"; runCmd 34 "$casa_checktool" " ./tMSSelection ms=$MS baseline='(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11' stateobsmode='*OFF*' spw='ALMA*FULL*:85290~86290.305MHz,x' time='*+0:0:1' uvdist='>1000m:50%' poln='XX,YX' installeh='1'"; runCmd 35 "$casa_checktool" " ./tMSSelection ms=$MS baseline='(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11;x' stateobsmode='*OFF*' spw='ALMA*FULL*:85290~86290.305MHz' time='*+0:0:1' uvdist='>1000m:50%' poln='XX,YX' installeh='1'"; runCmd 36 "$casa_checktool" " ./tMSSelection ms=$MS baseline='(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11' stateobsmode='0,100' spw='ALMA*FULL*:85290~86290.305MHz' time='*+0:0:1' uvdist='>1000m:50%' poln='XX,YX' installeh='1'"; runCmd 37 "$casa_checktool" " ./tMSSelection ms=$MS baseline='(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11' stateobsmode='*OFF*' spw='0,1,100' time='*+0:0:1' uvdist='>1000m:50%' poln='XX,YX' installeh='1'"; runCmd 38 "$casa_checktool" " ./tMSSelection ms=$MS baseline='0,1,2;500' stateobsmode='*OFF*' spw='ALMA*FULL*:85290~86290.305MHz' time='*+0:0:1' uvdist='>1000m:50%' poln='XX,YX' installeh='1'"; runCmd 39 "$casa_checktool" " ./tMSSelection ms=$MS baseline='(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11' stateobsmode='0,100' spw='ALMA*FULL*:85290~86290.305MHz' time='*+0:0:1' uvdist='>1000m:50%' poln='XX,YX' installeh='0'"; runCmd 40 "$casa_checktool" " ./tMSSelection ms=$MS baseline='(DV*,DA*)@(A1*,P*,S*)&(DV*)@(P*);!5;!11' stateobsmode='*OFF*' spw='0,1,100' time='*+0:0:1' uvdist='>1000m:50%' poln='XX,YX' installeh='0'"; runCmd 41 "$casa_checktool" " ./tMSSelection ms=$MS baseline='0,1,2;500' stateobsmode='*OFF*' spw='ALMA*FULL*:85290~86290.305MHz' time='*+0:0:1' uvdist='>1000m:50%' poln='XX,YX' installeh='0'"; runCmd 42 "$casa_checktool" " ./tMSSelection ms=$MS baseline='DA41&DV14' spw='1' time='2014/09/20/10:38:0.9~2014/09/20/10:38:03.88' installeh='0'"; runCmd 43 "$casa_checktool" " ./tMSSelection ms=$MS baseline='DA41&DV14' spw='1' time='[2014/09/20/10:38:1.1~2014/09/20/10:38:03.88]' installeh='0'"; runCmd 44 "$casa_checktool" " ./tMSSelection ms=$MS baseline='DA41&DV14' spw='1' time='0.04[2014/09/20/10:38:1.1~2014/09/20/10:38:03.88]' installeh='0'"; #Time Expr=2014/09/20/10:38:0.9~2014/09/20/10:38:03.88 #Time Expr=[2014/09/20/10:38:1.10~2014/09/20/10:38:03.88] #Time Expr=0.04[2014/09/20/10:38:1.10~2014/09/20/10:38:03.88] # # Remove the test MS and any other cleanup required. # cleanup; casacore-3.7.1/ms/MSSel/test/tMSSelection2.out000066400000000000000000000122411476623553700210710ustar00rootroot00000000000000BE: Baseline Expr= BE: Ant1 = [] BE: Ant2 = [] Baselines = FE: Field Expr=1,2 FE: Field = [1, 2] SE: SPW Expr= SE: SPW = [] SE: Chan = Axis Lengths: [0, 0] [] SE: Freq = Axis Lengths: [0, 0] [] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr= TE: Time = Axis Lengths: [3, 0] [] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [] StateList = [] Number of selected rows: 3780 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr= BE: Ant1 = [] BE: Ant2 = [] Baselines = FE: Field Expr=2,2 FE: Field = [2] SE: SPW Expr= SE: SPW = [] SE: Chan = Axis Lengths: [0, 0] [] SE: Freq = Axis Lengths: [0, 0] [] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr= TE: Time = Axis Lengths: [3, 0] [] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [] StateList = [] Number of selected rows: 1440 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr= BE: Ant1 = [] BE: Ant2 = [] Baselines = FE: Field Expr=2,1 FE: Field = [1, 2] SE: SPW Expr= SE: SPW = [] SE: Chan = Axis Lengths: [0, 0] [] SE: Freq = Axis Lengths: [0, 0] [] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr= TE: Time = Axis Lengths: [3, 0] [] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [] StateList = [] Number of selected rows: 3780 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr= BE: Ant1 = [] BE: Ant2 = [] Baselines = FE: Field Expr=2,1,2000 FE: Field = [1, 2, 4] SE: SPW Expr= SE: SPW = [] SE: Chan = Axis Lengths: [0, 0] [] SE: Freq = Axis Lengths: [0, 0] [] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr= TE: Time = Axis Lengths: [3, 0] [] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [] StateList = [] Number of selected rows: 5220 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr= BE: Ant1 = [] BE: Ant2 = [] Baselines = FE: Field Expr=2,1,2000 FE: Field = [1, 2, 4] SE: SPW Expr= SE: SPW = [] SE: Chan = Axis Lengths: [0, 0] [] SE: Freq = Axis Lengths: [0, 0] [] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr= TE: Time = Axis Lengths: [3, 0] [] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [] StateList = [] Number of selected rows: 5220 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ BE: Baseline Expr= BE: Ant1 = [] BE: Ant2 = [] Baselines = FE: Field Expr=2000 FE: Field = [4] SE: SPW Expr= SE: SPW = [] SE: Chan = Axis Lengths: [0, 0] [] SE: Freq = Axis Lengths: [0, 0] [] ScE: Scan Expr= ScE: tScan = [] StE: STATE Expr= StE: StateObsMode = [] AE: Array Expr= AE: Array = [] TE: Time Expr= TE: Time = Axis Lengths: [3, 0] [] UVE: UVRange Expr= UVE: UVRange = Axis Lengths: [2, 0] [] UVE: UV in meters = [] OE: Observation Expr= OE: ObservationIDList = [] PE: Poln Expr= PE: PolMap = {} PE: CorrMap = {} =========================================================== DDIDs(Poln) = [] DDIDs(SPW) = [] StateList = [] Number of selected rows: 1440 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ###MSSelectionError: Field Expression: Partial or no match for Field ID list [20000] (near char. 5 in string "20000") +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ casacore-3.7.1/ms/MSSel/test/tMSSelection2.run000066400000000000000000000024231476623553700210670ustar00rootroot00000000000000#!/bin/sh # ----------------------------------------------------------------------------- # Usage: tMSSelection.run # ----------------------------------------------------------------------------- # This script executes the program tMSSelection to test the # MSSelection module. #----------------------------------------------------------------------------- MS=$CASADEMO"mssel_test_small_multifield_spw.ms" runCmd(){ # Uncomment following line to generate an output close to a # script to run the tests #echo "#Test:$1"; echo $3; eval "$2 $3" } getTestMS() { tar zxf $testsrcdir/$MS.tgz } cleanup() { \rm -rf $MS } # Get the test MS (un-tar it from $testsrcdir). getTestMS; runCmd 1 "$casa_checktool" " ./tMSSelection ms=$MS field=1,2"; runCmd 2 "$casa_checktool" " ./tMSSelection ms=$MS field=\"2\",2"; runCmd 3 "$casa_checktool" " ./tMSSelection ms=$MS field=\"2\",1"; runCmd 4 "$casa_checktool" " ./tMSSelection ms=$MS field=\"2\",1,2000"; runCmd 5 "$casa_checktool" " ./tMSSelection ms=$MS field=\"2\",1,2000"; runCmd 6 "$casa_checktool" " ./tMSSelection ms=$MS field=2000"; runCmd 7 "$casa_checktool" " ./tMSSelection ms=$MS field=20000"; # # Remove the test MS and any other cleanup required. # cleanup; casacore-3.7.1/ms/MSSel/test/tMSSpwGram.cc000066400000000000000000000070031476623553700202200ustar00rootroot00000000000000//# Copyright (C) 1995,1996,1997,1999,2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main(int argc, const char* argv[]) { try { if(argc<3) { cout << " please input ms file and selection string on command line " << endl; return 0; } const String msName = argv[1]; MeasurementSet ms(msName); MeasurementSet * mssel; Vector selectedIDs; Matrix selectedChans; cout << "Original table has rows " << ms.nrow() << endl; if(msSpwGramParseCommand(&ms, argv[2], selectedIDs, selectedChans)==0) { const TableExprNode *node = msSpwGramParseNode(); if(node->isNull()) { cout << "NULL node " << endl; return 0; } cout << "TableExprNode has rows = " << node->nrow() << endl; Table tablesel(ms.tableName(), Table::Update); mssel = new MeasurementSet(tablesel(*node, node->nrow() )); mssel->rename(ms.tableName()+"/SELECTED_TABLE", Table::Scratch); mssel->flush(); if(mssel->nrow()==0) { cout << "Check your input, No data selected" << endl; } else { cout << "selected table has rows " << mssel->nrow() << endl; cout << "Selected IDs = " << selectedIDs << endl; } delete mssel; } else { cout << "failed to parse expression" << endl; } } catch (std::exception& x) { cout << "ERROR: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/ms/MSSel/test/tMSSpwGram.run000066400000000000000000000000421476623553700204330ustar00rootroot00000000000000#! /bin/sh echo "UNTESTED" exit 3 casacore-3.7.1/ms/MSSel/test/tMSTimeGram.cc000066400000000000000000000066521476623553700203560ustar00rootroot00000000000000//# tMSTimeGram.cc: Test program for MS Time selection parser //# Copyright (C) 2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main(int argc, const char* argv[]) { try { if (argc < 3) { String mesg = String("Usage: " ) + String(argv[0]) + String("

        // Handle storage and access of telescope data // // //
      • Tables module //
      • Note 229 // // // // // // // The MeasurementSet is where all data are ultimately to be found // in Casacore. Since, this is a collection of // measurements (either actual or simulated), the term MeasurementSet // seems appropriate. Often we use the abbreviation (and typedef) MS for // MeasurementSet. // // // // The MeasurementSets module handles storage of telescope data and access // to it. The MeasurementSet is the class that gives access to the data. // // A MeasurementSet (MS) // is a Table with subtables stored as keywords. // For each of these tables there is a separate class: the main table is // MeasurementSet, the subtables are MSAntenna, // MSArray, MSFeed, MSField, MSObsLog, MSObservation, MSSource, // MSSpectralWindow, MSSysCal, MSWeather. // //

        Class hierarchy and Table layout

        // Each table has a number // of predefined columns and keywords, a subset of which is required to be // present. The column and keyword layout of each table is described in // Note 229 // and in a separate class which contains two enum definitions. // The enum classes, e.g., // MSMainEnums and // MSAntennaEnums, just contain a // PredefinedColumn (PDC) enum and a PredefinedKeyword (PDK) enum. These enum // definitions are used as template arguments for the generic class // MSTable from which MeasurementSet // and all the subtable classes are derived. // Thus, e.g., the class MSAntenna is derived from // MSTable. // // The MSTable class // provides a large number of common column and keyword helper functions. // They are useful when creating a Table following the MeasurementSet // conventions from scratch and assist in following the agreed upon // column and keyword naming conventions. // // MSTable in turn is derived from Table, thus all the MS table classes are // Tables. Many operations on a MeasurementSet are Table operations. See the // Tables module for a list of // those operations. // // The reason for this class hierarchy is to provide each of the table classes // with a separate namespace for its column and keyword enums, so that // e.g., they can all have a column named TIME, while sharing as much code // as possible. The MSTable class forwards any substantial work // to the MSTableImpl class which does the actual work, it is a purely // implementation class with static functions not of interest to the user. // //

        Access to columns

        // To simplify the access of columns, and catch type errors in // the column declarations for column access objects (TableColumns) at // compile time, there is a helper class for each (sub)table (e.g., // MSFieldColumns). The helper class for the MeasurementSet, // MSColumns gives access to the main table columns and the helper objects // for all subtables. // // At present these classes are separate from the Table classes, mainly // to ensure that the member functions are only called on valid, completely // constructed MeasurementSet Tables. They also represent a large amount // of 'state' in the form of TableColumn objects of which only a couple // may actually be used. // //

        Units and Measures

        // Columns in the MeasurementSet and its subtables can have several // keywords attached to describe the contents of the column. // The UNIT keyword contains an ASCII string describing the unit of // the values in the column. The unit member function (in MSTable) will // return this unit string, it can be used to construct a // Unit object for a particular column. // // The MEASURE_TYPE keyword gives the Casacore Measure that applies to the // column (if any). See the // Measures module for a // list of available Measures and their use. // // The MEASURE_REFERENCE keyword gives (part of) the reference frame needed // to interpret the values in a column. An example is J2000 for the POSITION // column. A number of static functions in MeasurementSet are available to // create a 'template' Measure for a column, which has the MEASURE_TYPE filled // in. Currently the following functions exist: directionMeasure, // positionMeasure, epochMeasure and frequencyMeasure. They return a // Measure which can then be filled with a value from a particular row from // the column to obtain, e.g., a proper MDirection Measure for the phase // center. // //

        Reference Tables

        // Each of the MS classes has a member function // referenceCopy which takes the name of a new Table and a list (Block) of // column names to create a Table which references all columns in the // original table, except for those listed. The listed columns are // new columns which can be written to without affecting the original Table. // The main use of this is for the synthesis package where corrected and // model visibilities are stored as new DATA columns in an MS which // references the raw MS for the other columns. Except for these special // cases, the use of this function will be rare. // //

        DATA and FLOAT_DATA columns

        // To accommodate both synthesis and single dish data efficiently, it was // decided that a MeasurementSet can have a Complex DATA column, // a float FLOAT_DATA column or both. If it has only a FLOAT_DATA column, the // corresponding DATA column can be created with the makeComplexData() // member function. In special cases, both columns could be present but // filled for different rows, with an extra index defined indicating in // which column to look (e.g., multi-feed single dish with cross correlations). // The details of this last scheme are yet to be worked out. // The table consistency checks (isValid()) do not require the presence // of either column. // //

        Unset values in MeasurementSet Tables

        // For ID columns, the rule is that a value of -1 indicates that there is // no corresponding subtable in which to look up details. An example is // the PULSAR_ID column, which should be set to -1 if the optional // PULSAR subtable does not exist. // // The rules for non integer unset values in MS tables have not // settled down yet. // For Floating point and Complex values the recommended practice is // to set the values to the FITS and IEEE value NaN, // with a bit pattern of all 1's. // In much of the present filler code unused values are filled with 0 instead. // //

        Table destruction

        // Upon destruction, the table and all subtables are checked to see that the // MeasurementSet remains valid, i.e., all required columns are present. // An exception is thrown if not all required columns are present // Nevertheless, the table will be flushed to disk if it is writable - // preserving its state. // // //

        MS shorthand

        // While the class name, MeasurementSet, is descriptive, it is often // too long for many common uses. The typedef MS is provided as // a convenient shorthand for MeasurementSet. The example below uses this // typedef. // // //
        // // // This example illustrates creation and filling of the MeasurementSet. // // // create the table descriptor // TableDesc simpleDesc = MS::requiredTableDesc() // // set up a new table // SetupNewTable newTab("simpleTab", simpleDesc, Table::New); // // create the MeasurementSet // MeasurementSet simpleMS(newTab); // // now we need to define all required subtables // // the following call does this for us if we don't need to // // specify details of Storage Managers or non-standard columns. // simpleMS.createDummySubtables(Table::New); // // fill MeasurementSet via its Table interface // // For example, construct one of the column access objects. // TableColumn feed(simpleMS, MS::columnName(MS::FEED1)); // uInt rownr = 0; // // add a row // simpleMS.addRow(); // // set the values in that row, e.g. the feed column // feed.putScalar(rownr,1); // // Access the position column in the ANTENNA subtable // ArrayColumn antpos(simpleMS.antenna(), // MSAntenna::columnName(MSAntenna::POSITION)); // // Add a row to it and fill in the position // simpleMS.antenna().addRow(); // Array position(3); // position(0)=1.; position(1)=2.; position(2)=3.; // antpos.put(0,position); // // . // // For standard columns the above can be done more easily using // // the MSColumns object. // // Create the MSColumns // MSColumns msc(simpleMS); // // and fill in the position // msc.antenna().position().put(0,position); // // // // // This example illustrates read only access to an existing MeasurementSet // and creation of an MDirection Measure for the phase center. // // // Create the MeasurementSet from an existing Table on disk // MeasurementSet ms("myMS"); // // Create the RO column access objects for main table and subtables // MSColumns msc(ms); // // show data from row 5 // cout << msc.data()(5) << endl; // // show phase center for row 3 in field table // Vector phaseCtr=msc.field().phaseCenter()(3); // cout << phaseCtr< // // // // // The attempt is to define a single, extensible, Table format that will // be able to cope with all, or at least most, radio telescope data. // Having a single MeasurementSet should make it easier to combine data // from different instruments. The format of the MeasurementSet, // table with subtables, was chosen to be able to cope with items // varying at different rates more efficiently than a 'flat' Table layout // would allow. // // //
      • Incorporate the MSColumn classes in the MeasurementSet classes? //
      • Variable (row to row) ReferenceFrame (e.g., J2000 mixed with // galactic, different Frequency reference frames mixed in the // same MS, etc.). This could be done with a column named // "column_name"_MEASURE_REFERENCE for each column with varying // Measure reference frames. // // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/000077500000000000000000000000001476623553700170675ustar00rootroot00000000000000casacore-3.7.1/ms/MeasurementSets/MSAntenna.cc000066400000000000000000000146411476623553700212300ustar00rootroot00000000000000//# MSAntenna.cc: The MeasurementSet ANTENNA Table //# Copyright (C) 1996,1998,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSAntenna::MSAntenna():hasBeenDestroyed_p(True) { } MSAntenna::MSAntenna(const String &tableName, TableOption option) : MSTable(tableName, option),hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSAntenna(String &, TableOption) - " "table is not a valid MSAntenna")); } MSAntenna::MSAntenna(const String& tableName, const String &tableDescName, TableOption option) : MSTable(tableName, tableDescName,option), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSAntenna(String &, String &, TableOption) - " "table is not a valid MSAntenna")); } MSAntenna::MSAntenna(SetupNewTable &newTab, rownr_t nrrow, Bool initialize) : MSTable(newTab, nrrow, initialize), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSAntenna(SetupNewTable &, rownr_t, Bool) - " "table is not a valid MSAntenna")); } MSAntenna::MSAntenna(const Table &table) : MSTable(table), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSAntenna(const Table &) - " "table is not a valid MSAntenna")); } MSAntenna::MSAntenna(const MSAntenna &other) : MSTable(other), hasBeenDestroyed_p(False) { // verify that other is valid if (&other != this) if (! validate(this->tableDesc())) throw (AipsError("MSAntenna(const MSAntenna &) - " "table is not a valid MSAntenna")); } MSAntenna::~MSAntenna() { // check to make sure that this MSAntenna is still valid if (!hasBeenDestroyed_p && !validate()) { // the table is otherwise OK, so ensure that it is written if necessary this->flush(); LogIO os; os << LogIO::WARN << "~MSAntenna() - Table written is not a valid MSAntenna" << LogIO::POST; } hasBeenDestroyed_p = True; } MSAntenna& MSAntenna::operator=(const MSAntenna &other) { if (&other != this) { MSTable::operator=(other); hasBeenDestroyed_p=other.hasBeenDestroyed_p; } return *this; } MSTableMaps MSAntenna::initMaps() { MSTableMaps maps; // the PredefinedColumns // DISH_DIAMETER colMapDef(maps, DISH_DIAMETER, "DISH_DIAMETER", TpDouble, "Physical diameter of dish","m",""); // FLAG_ROW colMapDef(maps, FLAG_ROW,"FLAG_ROW",TpBool, "Flag for this row","",""); // MOUNT colMapDef(maps, MOUNT,"MOUNT",TpString, "Mount type e.g. alt-az, equatorial, etc.","",""); // NAME colMapDef(maps, NAME,"NAME",TpString, "Antenna name, e.g. VLA22, CA03","",""); // OFFSET colMapDef(maps, OFFSET,"OFFSET",TpArrayDouble, "Axes offset of mount to FEED REFERENCE point", "m","Position"); // POSITION colMapDef(maps, POSITION,"POSITION",TpArrayDouble, "Antenna X,Y,Z phase reference position","m","Position"); // STATION colMapDef(maps, STATION,"STATION",TpString, "Station (antenna pad) name","",""); // TYPE colMapDef(maps, TYPE,"TYPE", TpString, "Antenna type (e.g. SPACE-BASED)","",""); // Optional columns follow // MEAN_ORBIT colMapDef(maps, MEAN_ORBIT,"MEAN_ORBIT",TpArrayDouble, "Mean Keplerian elements","",""); // ORBIT_ID colMapDef(maps, ORBIT_ID,"ORBIT_ID",TpInt, "index into ORBIT table (ignore if<0)","",""); // PHASED_ARRAY_ID colMapDef(maps, PHASED_ARRAY_ID,"PHASED_ARRAY_ID",TpInt, "index into PHASED_ARRAY table","",""); // PredefinedKeywords // init requiredTableDesc // all required keywords uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(maps, PredefinedKeywords(i)); } // all required columns // First define the columns with fixed size arrays IPosition shape(1,3); ColumnDesc::Option option=ColumnDesc::Direct; addColumnToDesc(maps, OFFSET, shape, option); addColumnToDesc(maps, POSITION, shape, option); // Now define all other columns (duplicates are skipped) for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(maps, PredefinedColumns(i)); } return maps; } MSAntenna MSAntenna::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSAntenna(MSTable:: referenceCopy(newTableName,writableColumns)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSAntenna.h000066400000000000000000000114631476623553700210710ustar00rootroot00000000000000//# MSAntenna.h: The MeasurementSet ANTENNA Table //# Copyright (C) 1996,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSANTENNA_H #define MS_MSANTENNA_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A Table intended to hold a MeasurementSet ANTENNA table. // // // // //
      • MeasurementSet //
      • MSTable // // // // MSAntenna stands for the MeasurementSet Antenna table. // // // // An MSAntenna is a table intended to hold the ANTENNA table for // the MeasurementSet. It has an identical set of member functions as // the main MeasurementSet class, except (currently) for the default // calibration members. For further info and examples see the // MeasurementSet class. // // // // See the MeasurementSet for an example of how to access and use this class. // // // // It was found that subtables and the main table of the MeasurementSet have // a lot in common, therefore they derive their interface from the same // base class. Each subtable has its own class to keep the enum definitions // and conversion functions in separate scopes. // // // // see MeasurementSet. // class MSAntenna:public MSAntennaEnums, public MSTable { public: // This constructs an empty MSAntenna MSAntenna (); // // These constructors mirror the Table ones with additional checking // on validity (verifying that the MSAntenna will have the required columns // and keywords). // An exception is thrown if the constructed Table is not a valid MSAntenna // //
      • AipsError // // MSAntenna (const String &tableName, TableOption = Table::Old); MSAntenna (const String &tableName, const String &tableDescName, TableOption = Table::Old); MSAntenna (SetupNewTable &newTab, rownr_t nrrow = 0, Bool initialize = False); MSAntenna (const Table &table); MSAntenna (const MSAntenna &other); // // As with tables, the destructor writes the table if necessary. // Additional checking is done here to verify that all required // columns are still present. // If it is NOT valid, it will write the table and then throw an exception. // //
      • AipsError // ~MSAntenna(); // Assignment operator, reference semantics MSAntenna& operator=(const MSAntenna&); // Make a special copy of this Table which references all columns from // this Table except those mentioned; those are empty and writable. // Each forwarded column has the same writable status as the underlying // column. The mentioned columns all use the AipsIO storage manager. // This function is inherited from MSTable and unlikely to be of use, // except in the class MeasurementSet (see comment there).. MSAntenna referenceCopy(const String& newTableName, const Block& writableColumns) const; // Initialize the statics appropriately. This does not need to be // called by users, it is called by the implementation class // MSTableImpl. static MSTableMaps initMaps(); private: // required by the need to throw an exception in the destructor Bool hasBeenDestroyed_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSAntennaColumns.cc000066400000000000000000000205011476623553700225610ustar00rootroot00000000000000//# MSAntennaColumns.cc: provides easy access to MeasurementSet columns //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSAntennaColumns::MSAntennaColumns() { } MSAntennaColumns::MSAntennaColumns(const MSAntenna& msAntenna) { attach(msAntenna); } MSAntennaColumns::~MSAntennaColumns() {} void MSAntennaColumns::attach(const MSAntenna& msAntenna) { dishDiameter_p.attach(msAntenna, MSAntenna:: columnName(MSAntenna::DISH_DIAMETER)); flagRow_p.attach(msAntenna, MSAntenna:: columnName(MSAntenna::FLAG_ROW)); mount_p.attach(msAntenna, MSAntenna::columnName(MSAntenna::MOUNT)); name_p.attach(msAntenna, MSAntenna::columnName(MSAntenna::NAME)); offset_p.attach(msAntenna, MSAntenna::columnName(MSAntenna::OFFSET)); position_p.attach(msAntenna, MSAntenna:: columnName(MSAntenna::POSITION)); station_p.attach(msAntenna, MSAntenna::columnName(MSAntenna::STATION)); type_p.attach(msAntenna, MSAntenna::columnName(MSAntenna::TYPE)); offsetMeas_p.attach(msAntenna, MSAntenna:: columnName(MSAntenna::OFFSET)); positionMeas_p.attach(msAntenna, MSAntenna:: columnName(MSAntenna::POSITION)); dishDiameterQuant_p.attach(msAntenna, MSAntenna:: columnName(MSAntenna::DISH_DIAMETER)); offsetQuant_p.attach(msAntenna, MSAntenna:: columnName(MSAntenna::OFFSET)); positionQuant_p.attach(msAntenna, MSAntenna:: columnName(MSAntenna::POSITION)); attachOptionalCols(msAntenna); } void MSAntennaColumns::attachOptionalCols(const MSAntenna& msAntenna) { const ColumnDescSet& cds=msAntenna.tableDesc().columnDescSet(); const String& meanOrbit=MSAntenna::columnName(MSAntenna::MEAN_ORBIT); if (cds.isDefined(meanOrbit)) meanOrbit_p.attach(msAntenna,meanOrbit); const String& orbitId=MSAntenna::columnName(MSAntenna::ORBIT_ID); if (cds.isDefined(orbitId)) orbitId_p.attach(msAntenna,orbitId); const String& phasedArrayId=MSAntenna:: columnName(MSAntenna::PHASED_ARRAY_ID); if (cds.isDefined(phasedArrayId)) { phasedArrayId_p.attach(msAntenna, phasedArrayId); } } void MSAntennaColumns::setPositionRef(MPosition::Types ref) { positionMeas_p.setDescRefCode(ref); } void MSAntennaColumns::setOffsetRef(MPosition::Types ref) { offsetMeas_p.setDescRefCode(ref); } Int64 MSAntennaColumns:: matchAntenna(const MPosition& antennaPos, const Quantum& tolerance, Int64 tryRow) { rownr_t r = nrow(); if (r == 0) return -1; // Convert the antenna position to something in m. const MPosition::Types refType = MPosition::castType(antennaPos.getRef().getType()); // If the type does not match then throw an exception! If someone is trying // to do this then they should be doing the conversions elsewhere and the // sooner they know about this error the better. if (MPosition::castType(positionMeas().getMeasRef().getType()) != refType) { throw(AipsError("MSAntennaColumns::matchAntenna(...) - " " cannot match when reference frames differ")); } // Convert the tolerance to meters const Unit m("m"); DebugAssert(tolerance.check(m.getValue()), AipsError); const Double tolInM = tolerance.getValue(m); // Convert the position to meters const Vector& antPosInM = antennaPos.getValue().getValue(); // Main matching loop if (tryRow >= 0) { const rownr_t tr = tryRow; if (tr >= r) { throw(AipsError("MSAntennaColumns::matchAntenna(...) - " "row " + String::toString(tr) + " you suggest is too big")); } if (!flagRow()(tr) && matchPosition(tr, antPosInM, tolInM)) { return tr; } if (tr == r-1) r--; } while (r > 0) { r--; if (!flagRow()(r) && matchPosition(r, antPosInM, tolInM)) { return r; } } return -1; } Int64 MSAntennaColumns::matchAntenna(const String& antName, const MPosition& antennaPos, const Quantum& tolerance, Int64 tryRow) { return matchAntennaAndStation(antName, "", antennaPos, tolerance, tryRow); } Int64 MSAntennaColumns::matchAntennaAndStation(const String& antName, const String& stationName, const MPosition& antennaPos, const Quantum& tolerance, Int64 tryRow) { rownr_t r = nrow(); if (r == 0) return -1; // Convert the antenna position to something in m. const MPosition::Types refType = MPosition::castType(antennaPos.getRef().getType()); // If the type does not match then throw an exception! If someone is trying // to do this then they should be doing the conversions elsewhere and the // sooner they know about this error the better. if (MPosition::castType(positionMeas().getMeasRef().getType()) != refType) { throw(AipsError("MSAntennaColumns::matchAntenna(...) - " " cannot match when reference frames differ")); } // Convert the tolerance to meters const Unit m("m"); DebugAssert(tolerance.check(m.getValue()), AipsError); const Double tolInM = tolerance.getValue(m); // Convert the position to meters const Vector& antPosInM = antennaPos.getValue().getValue(); // Main matching loop if (tryRow >= 0) { const rownr_t tr = tryRow; if (tr >= r) { throw(AipsError("MSAntennaColumns::matchAntenna(...) - " "row " + String::toString(tr) + " you suggest is too big")); } Bool stationMatches = stationName.empty() || matchStation(tr, stationName); if (!flagRow()(tr) && stationMatches && matchName(tr, antName) && matchPosition(tr, antPosInM, tolInM)) { return tr; } if (tr == r-1) r--; } while (r > 0) { r--; Bool stationMatches = stationName.empty() || matchStation(r, stationName); if (!flagRow()(r) && stationMatches && matchName(r, antName) && matchPosition(r, antPosInM, tolInM)) { return r; } } return -1; } Bool MSAntennaColumns::matchName(rownr_t row, const String& antName) const { DebugAssert(row < nrow(), AipsError); return antName.matches(name()(row)); } Bool MSAntennaColumns::matchStation(rownr_t row, const String& stationName) const { DebugAssert(row < nrow(), AipsError); return stationName.matches(station()(row)); } Bool MSAntennaColumns:: matchPosition(rownr_t row, const Vector& antPosInM, const Double tolInM) const { DebugAssert(row < nrow(), AipsError); DebugAssert(antPosInM.nelements() == 3, AipsError); return allNearAbs(position()(row), antPosInM, tolInM); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSAntennaColumns.h000066400000000000000000000222301476623553700224240ustar00rootroot00000000000000//# MSAntennaColumns.h: provides easy access to MSAntenna columns //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSANTENNACOLUMNS_H #define MS_MSANTENNACOLUMNS_H #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class MSAntenna; // // A class to provide easy access to MSAntenna columns // // // // // //
      • MSAntenna //
      • ArrayColumn //
      • ScalarColumn // // // // MSAntennaColumns stands for MeasurementSet Antenna Table columns. // // // // This class provides access to the columns in the MSAntenna Table, // it does the declaration of all the Scalar and ArrayColumns with the // correct types, so the application programmer doesn't have to // worry about getting those right. There is an access function // for every predefined column. Access to non-predefined columns will still // have to be done with explicit declarations. // See MSColumns for an example. // // // // See MSColumns for the motivation. // class MSAntennaColumns { public: // Create a columns object that accesses the data in the specified Table MSAntennaColumns(const MSAntenna& msAntenna); // The destructor does nothing special ~MSAntennaColumns(); // Access to required columns // ScalarColumn& dishDiameter() {return dishDiameter_p;} ScalarQuantColumn& dishDiameterQuant() {return dishDiameterQuant_p;} ScalarColumn& flagRow() {return flagRow_p;} ScalarColumn& mount() {return mount_p;} ScalarColumn& name() {return name_p;} ArrayColumn& offset() {return offset_p;} ArrayQuantColumn& offsetQuant() {return offsetQuant_p;} ScalarMeasColumn& offsetMeas() { return offsetMeas_p;} ArrayColumn& position() {return position_p;} ArrayQuantColumn& positionQuant() {return positionQuant_p;} ScalarMeasColumn& positionMeas() { return positionMeas_p;} ScalarColumn& station() {return station_p;} ScalarColumn& type() {return type_p;} // // Const access to required columns // const ScalarColumn& dishDiameter() const {return dishDiameter_p;} const ScalarQuantColumn& dishDiameterQuant() const {return dishDiameterQuant_p;} const ScalarColumn& flagRow() const {return flagRow_p;} const ScalarColumn& mount() const {return mount_p;} const ScalarColumn& name() const {return name_p;} const ArrayColumn& offset() const {return offset_p;} const ArrayQuantColumn& offsetQuant() const {return offsetQuant_p;} const ScalarMeasColumn& offsetMeas() const { return offsetMeas_p;} const ArrayColumn& position() const {return position_p;} const ArrayQuantColumn& positionQuant() const {return positionQuant_p;} const ScalarMeasColumn& positionMeas() const { return positionMeas_p;} const ScalarColumn& station() const {return station_p;} const ScalarColumn& type() const {return type_p;} // // Access to optional columns // ArrayColumn& meanOrbit() {return meanOrbit_p;} ScalarColumn& orbitId() {return orbitId_p;} ScalarColumn& phasedArrayId() {return phasedArrayId_p;} // // Const access to optional columns // const ArrayColumn& meanOrbit() const {return meanOrbit_p;} const ScalarColumn& orbitId() const {return orbitId_p;} const ScalarColumn& phasedArrayId() const {return phasedArrayId_p;} // // set the position type for the POSITION column. This can only be done when // the table has no rows. Trying to do so at other times will throw an // exception. void setPositionRef(MPosition::Types ref); // set the position type for the OFFSET column. This can only be done when // the table has no rows. Trying to do so at other times will throw an // exception. void setOffsetRef(MPosition::Types ref); // Convenience function that returns the number of rows in any of the columns rownr_t nrow() const {return dishDiameter_p.nrow();} // returns the last row that contains an antenna at the specified position, // to within the specified tolerance. The reference frame of the supplied // position must be the same as the one for the POSITION columns. If not an // AipsError is thrown as such an argument will never match any row of the // Table. The tolerance is the maximum allowed distance between the two // positions and the supplied Quantum must have dimensions of length. This is // checked when compiled in debug mode and an AipsError exception is thrown // if the dimensions are wrong. Returns -1 if no match could be found. Flagged // rows can never match. If tryRow is non-negative, then that row is tested // to see if it matches before any others are tested. Setting tryRow to a // positive value greater than the table length will throw an exception // (AipsError), when compiled in debug mode. Int64 matchAntenna(const MPosition& antennaPos, const Quantum& tolerance, Int64 tryRow=-1); // Same as the previous function except that the antenna name must also // match. Int64 matchAntenna(const String& antName, const MPosition& antennaPos, const Quantum& tolerance, Int64 tryRow=-1); // Same as the previous function except that the station name must also // match. Int64 matchAntennaAndStation(const String& antName, const String& stationName, // ignored when empty const MPosition& antennaPos, const Quantum& tolerance, Int64 tryRow=-1); protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. MSAntennaColumns(); //# attach this object to the supplied table. void attach(const MSAntenna& msAntenna); private: //# Make the assignment operator and the copy constructor private to prevent //# any compiler generated one from being used. MSAntennaColumns(const MSAntennaColumns&); MSAntennaColumns& operator=(const MSAntennaColumns&); //# Check if any optional columns exist and if so attach them. void attachOptionalCols(const MSAntenna& msAntenna); //# Functions which check the supplied values against the relevant column and //# the specified row. Bool matchName(rownr_t row, const String& antName) const; Bool matchStation(rownr_t row, const String& stationName) const; Bool matchPosition(rownr_t row, const Vector& antPosInM, const Double tolInM) const; //# required columns ScalarColumn dishDiameter_p; ScalarColumn flagRow_p; ScalarColumn mount_p; ScalarColumn name_p; ArrayColumn offset_p; ArrayColumn position_p; ScalarColumn station_p; ScalarColumn type_p; //# optional columns ArrayColumn meanOrbit_p; ScalarColumn orbitId_p; ScalarColumn phasedArrayId_p; //# Access to Measure columns ScalarMeasColumn offsetMeas_p; ScalarMeasColumn positionMeas_p; //# Access to Quantum columns ScalarQuantColumn dishDiameterQuant_p; ArrayQuantColumn offsetQuant_p; ArrayQuantColumn positionQuant_p; }; //# Define the RO version for backward compatibility. typedef MSAntennaColumns ROMSAntennaColumns; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSAntennaEnums.h000066400000000000000000000077421476623553700221060ustar00rootroot00000000000000//# MSAntennaEnums.h: Definitions for the MeasurementSet ANTENNA table //# Copyright (C) 1996,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSANTENNAENUMS_H #define MS_MSANTENNAENUMS_H #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Enums for the MeasurementSet ANTENNA table // // // // This class contains the enums for the MeasurementSet ANTENNA table // // // This class does nothing. It is merely a container for the enumerations // used by the MSAntenna class. These enumerations define the // standard columns, keywords. // // // See the documentation for MeasurementSet for examples on the use of these // enumerations. // // // All the todo items which may be related to this class are // grouped with the todo items for MeasurementSet // // class MSAntennaEnums { public: // The ANTENNA table colums with predefined meaning. // Keys: ANTENNA_ID, ARRAY_ID enum PredefinedColumns { // "True" columns are defined.
        // TYPE - UNIT - MEASURE UNDEFINED_COLUMN=0, // Antenna type: "GROUND-BASED", "SPACE-BASED", "TRACKING-STATION"
        // String TYPE, // Physical diameter of dish (if appropriate)
        // Double - m DISH_DIAMETER, // Flag for this row
        // Bool FLAG_ROW, // Mount type: choose from "AZ-EL", "HA-DEC", "X-Y", "orbiting" // or "bizarre" (following VLBA FITS).
        // String. MOUNT, // Antenna name, e.g. VLA22, CA03.
        // String. NAME, // Axes offset of mount to FEED REFERENCE point.
        // Double(3) - m - POSITION OFFSET, // Antenna X,Y,Z phase reference positions in the IERS Terrestrial // Reference Frame (ITRF); // right-handed, X towards the intersection of the ITRF equator and // the Greenwich meridian, Z towards the adopted mean position of the pole.
        // Double(3) - m - POSITION POSITION, // Station (antenna pad) name.
        // String STATION, // Number of required columns NUMBER_REQUIRED_COLUMNS=STATION, // Mean Keplerian orbit elements
        // Double(6) MEAN_ORBIT, // Index into optional ORBIT table.
        // Int. ORBIT_ID, // Index into optional PHASED_ARRAY table.
        // Int. PHASED_ARRAY_ID, // // Not a column, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_COLUMNS=PHASED_ARRAY_ID }; // Keywords with a predefined meaning enum PredefinedKeywords { // // "True" keywords are defined. UNDEFINED_KEYWORD=0, // // Not a keyword, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_KEYWORDS=0 }; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSColumns.cc000066400000000000000000000064201476623553700212600ustar00rootroot00000000000000//# MSColumns.cc: provides easy access to MeasurementSet columns //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Instantiate extern templates for often used types. template class ArrayMeasColumn; template class ScalarMeasColumn; template class ScalarMeasColumn; template class ArrayQuantColumn; template class ScalarQuantColumn; MSColumns::MSColumns(const MeasurementSet& ms): MSMainColumns(ms), antenna_p(ms.antenna()), dataDesc_p(ms.dataDescription()), doppler_p(ms.doppler()), feed_p(ms.feed()), field_p(ms.field()), flagCmd_p(ms.flagCmd()), freqOffset_p(ms.freqOffset()), history_p(ms.history()), observation_p(ms.observation()), pointing_p(ms.pointing()), polarization_p(ms.polarization()), processor_p(ms.processor()), source_p(ms.source()), spectralWindow_p(ms.spectralWindow()), state_p(ms.state()), sysCal_p(ms.sysCal()), weather_p(ms.weather()) { } MSColumns::~MSColumns() {} void MSColumns::setEpochRef(MEpoch::Types ref, Bool tableMustBeEmpty) { // Adjust the relevant columns in the main table MSMainColumns::setEpochRef(ref, tableMustBeEmpty); // Now the same for the subtables. feed().setEpochRef(ref, tableMustBeEmpty); field().setEpochRef(ref, tableMustBeEmpty); flagCmd().setEpochRef(ref, tableMustBeEmpty); history().setEpochRef(ref, tableMustBeEmpty); observation().setEpochRef(ref, tableMustBeEmpty); pointing().setEpochRef(ref, tableMustBeEmpty); if (!freqOffset_p.isNull()) { freqOffset_p.setEpochRef(ref, tableMustBeEmpty); } if (!source_p.isNull()) { source().setEpochRef(ref, tableMustBeEmpty); } if (!sysCal_p.isNull()) { sysCal_p.setEpochRef(ref, tableMustBeEmpty); } if (!weather_p.isNull()) { weather_p.setEpochRef(ref, tableMustBeEmpty); } } void MSColumns::setDirectionRef(MDirection::Types ref) { field().setDirectionRef(ref); pointing().setDirectionRef(ref); if (!source_p.isNull()) { source().setDirectionRef(ref); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSColumns.h000066400000000000000000000212241476623553700211210ustar00rootroot00000000000000//# MSColumns.h: provides easy access to MeasurementSet columns //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSCOLUMNS_H #define MS_MSCOLUMNS_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class MeasurementSet; // // A class to provide easy access to MeasurementSet columns // // // // // //
      • MeasurementSet //
      • ArrayColumn //
      • ScalarColumn // // // // MSColumns stands for MeasurementSet Table columns. // // // // This class provides access to all the subtables and direct access to all the // columns in the MeasurementSet. It does the declaration of all the Scalar // and ArrayColumns with the correct types, so the application programmer // doesn't have to worry about getting those right. There is an access function // for every predefined column. Access to non-predefined columns will still // have to be done with explicit declarations. // // // // // // use as follows // MeasurementSet ms("myMS",Table::Update); // MSColumns msc(ms); // // show data from row 5 // cout << msc.data()(5); // // change name of antenna on row 3 in antenna table // msc.antenna().name().put(3,"NewAnt-3"); // // // // // Having to type long lists of Scalar and Array column declarations gets // very tedious. This class attempts to relieve some of that tedium, while // at the same time concentrating all the declarations in one place, // making Type errors in the column declaration (only caught at run-time) less // probable. Type errors in the use of the columns is caught at compile // time. // // // //
      • We might decide to merge this class with the MeasurementSet // class MSColumns: public MSMainColumns { public: // Create a columns object that accesses the data in the specified MS MSColumns(const MeasurementSet& ms); // The destructor does nothing special ~MSColumns(); // Access to required subtables // MSAntennaColumns& antenna() {return antenna_p;} MSDataDescColumns& dataDescription() {return dataDesc_p;} MSFeedColumns& feed() {return feed_p;} MSFieldColumns& field() {return field_p;} MSFlagCmdColumns& flagCmd() {return flagCmd_p;} MSHistoryColumns& history() {return history_p;} MSObservationColumns& observation() {return observation_p;} MSPointingColumns& pointing() {return pointing_p;} MSPolarizationColumns& polarization() {return polarization_p;} MSProcessorColumns& processor() {return processor_p;} MSSpWindowColumns& spectralWindow() {return spectralWindow_p;} MSStateColumns& state() {return state_p;} // // Access to optional subtables // MSDopplerColumns& doppler() {return doppler_p;} MSFreqOffsetColumns& freqOffset() {return freqOffset_p;} MSSourceColumns& source() {return source_p;} MSSysCalColumns& sysCal() {return sysCal_p;} MSWeatherColumns& weather() {return weather_p;} // // Access to required subtables // const MSAntennaColumns& antenna() const {return antenna_p;} const MSDataDescColumns& dataDescription() const {return dataDesc_p;} const MSFeedColumns& feed() const {return feed_p;} const MSFieldColumns& field() const {return field_p;} const MSFlagCmdColumns& flagCmd() const {return flagCmd_p;} const MSHistoryColumns& history() const {return history_p;} const MSObservationColumns& observation() const {return observation_p;} const MSPointingColumns& pointing() const {return pointing_p;} const MSPolarizationColumns& polarization() const { return polarization_p;} const MSProcessorColumns& processor() const {return processor_p;} const MSSpWindowColumns& spectralWindow() const { return spectralWindow_p;} const MSStateColumns& state() const {return state_p;} // // Access to optional subtables // const MSDopplerColumns& doppler() const {return doppler_p;} const MSFreqOffsetColumns& freqOffset() const {return freqOffset_p;} const MSSourceColumns& source() const {return source_p;} const MSSysCalColumns& sysCal() const {return sysCal_p;} const MSWeatherColumns& weather() const {return weather_p;} // // set the EPOCH reference type in all EPOCH columns in the MS. Note that // only a single EPOCH reference is allowed in the MS. This // // In principle this function can only be used if the table is empty, // otherwise already written values may thereafter have an incorrect // reference, offset, or unit. However, it is possible that part of the // table gets written before these values are known. In that case the // reference, offset, or units can be set by using a False // tableMustBeEmpty argument. // void setEpochRef(MEpoch::Types ref, Bool tableMustBeEmpty=True); // set the DIRECTION reference type for FIELD, POINTING and SOURCE tables // (except for antenna frame directions). void setDirectionRef(MDirection::Types ref); private: // Access to subtables MSAntennaColumns antenna_p; MSDataDescColumns dataDesc_p; MSDopplerColumns doppler_p; //optional MSFeedColumns feed_p; MSFieldColumns field_p; MSFlagCmdColumns flagCmd_p; MSFreqOffsetColumns freqOffset_p; //optional MSHistoryColumns history_p; MSObservationColumns observation_p; MSPointingColumns pointing_p; MSPolarizationColumns polarization_p; MSProcessorColumns processor_p; MSSourceColumns source_p; // optional MSSpWindowColumns spectralWindow_p; MSStateColumns state_p; MSSysCalColumns sysCal_p; //optional MSWeatherColumns weather_p; //optional }; //# Define the RO version for backward compatibility. typedef MSColumns ROMSColumns; //# Declare extern templates for often used types. extern template class ArrayMeasColumn; extern template class ScalarMeasColumn; extern template class ScalarMeasColumn; extern template class ArrayQuantColumn; extern template class ScalarQuantColumn; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSDataDescColumns.cc000066400000000000000000000063441476623553700226560ustar00rootroot00000000000000//# MSDataDescColumns.cc: provides easy access to MeasurementSet columns //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSDataDescColumns::MSDataDescColumns() { } MSDataDescColumns::MSDataDescColumns(const MSDataDescription& msDataDesc) { attach(msDataDesc); } MSDataDescColumns::~MSDataDescColumns() {} void MSDataDescColumns::attach(const MSDataDescription& msDataDesc) { flagRow_p.attach(msDataDesc, MSDataDescription::columnName (MSDataDescription::FLAG_ROW)); polarizationId_p.attach(msDataDesc, MSDataDescription::columnName (MSDataDescription::POLARIZATION_ID)); spectralWindowId_p.attach(msDataDesc, MSDataDescription::columnName (MSDataDescription::SPECTRAL_WINDOW_ID)); attachOptionalCols(msDataDesc); } void MSDataDescColumns:: attachOptionalCols(const MSDataDescription& msDataDesc) { const ColumnDescSet& cds = msDataDesc.tableDesc().columnDescSet(); if (cds.isDefined(MSDataDescription:: columnName(MSDataDescription::LAG_ID))) { lagId_p.attach(msDataDesc, MSDataDescription:: columnName(MSDataDescription::LAG_ID)); } } Int64 MSDataDescColumns::match(uInt spwId, uInt polId, Int64 tryRow) { rownr_t r = nrow(); if (r == 0) return -1; const Int spw = spwId; const Int pol = polId; // Main matching loop if (tryRow >= 0) { const rownr_t tr = tryRow; if (tr >= r) { throw(AipsError("MSDataDescColumns::match(...) - " "row " + String::toString(tr) + " you suggest is too big")); } if (!flagRow()(tr) && spectralWindowId()(tr) == spw && polarizationId()(tr) == pol) { return tr; } if (tr == r-1) r--; } while (r > 0) { r--; if (!flagRow()(r) && spectralWindowId()(r) == spw && polarizationId()(r) == pol) { return r; } } return -1; } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSDataDescColumns.h000066400000000000000000000122401476623553700225100ustar00rootroot00000000000000//# MSDataDescColumns.h: provides easy access to MSDataDescription columns //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSDATADESCCOLUMNS_H #define MS_MSDATADESCCOLUMNS_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class MSDataDescription; // // A class to provide easy access to MSDataDescription columns // // // // // //
      • MSDataDesc //
      • ScalarColumn // // // // MSDataDescColumns stands for MeasurementSet DataDescription Table // columns. // // // // This class provides access to the columns in the MSDataDesc Table, // it does the declaration of all the Scalar and ArrayColumns with the // correct types, so the application programmer doesn't have to // worry about getting those right. There is an access function // for every predefined column. Access to non-predefined columns will still // have to be done with explicit declarations. // See MSColumns for an example. // The Table that is used to construct this class must not // be destroyed (or go out of scope) before this class does. Otherwise the // scalar and array columns use by this class will be left dangling. // // // // See MSColumns for the motivation. // class MSDataDescColumns { public: // Create a columns object that accesses the data in the specified Table MSDataDescColumns(const MSDataDescription& msDataDesc); // The destructor does nothing special ~MSDataDescColumns(); // Access to required columns // ScalarColumn& flagRow() {return flagRow_p;} ScalarColumn& polarizationId() {return polarizationId_p;} ScalarColumn& spectralWindowId() {return spectralWindowId_p;} // // Const access to required columns // const ScalarColumn& flagRow() const {return flagRow_p;} const ScalarColumn& polarizationId() const {return polarizationId_p;} const ScalarColumn& spectralWindowId() const {return spectralWindowId_p;} // // Access to optional columns // ScalarColumn& lagId() {return lagId_p;} // // Const access to optional columns // const ScalarColumn& lagId() const {return lagId_p;} // // Convenience function that returns the number of rows in any of the columns rownr_t nrow() const {return flagRow_p.nrow();} // returns the last row that contains the specified entries in the // SPECTRAL_WINDOW_ID & POLARIZATION_ID columns. Returns -1 if no match could // be found. Flagged rows can never match. If tryRow is non-negative, then // that row is tested to see if it matches before any others are // tested. Setting tryRow to a positive value greater than the table length // will throw an exception (AipsError). Int64 match(uInt spwId, uInt polId, Int64 tryRow=-1); protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. MSDataDescColumns(); //# attach all the columns in the supplied table to this object void attach(const MSDataDescription& msDataDesc); private: //# Make the assignment operator and the copy constructor private to prevent //# any compiler generated one from being used. MSDataDescColumns(const MSDataDescColumns&); MSDataDescColumns& operator=(const MSDataDescColumns&); //# attach optional columns in the supplied Table (if they exist) void attachOptionalCols(const MSDataDescription& msDataDesc); //# required columns ScalarColumn flagRow_p; ScalarColumn polarizationId_p; ScalarColumn spectralWindowId_p; //# optional columns ScalarColumn lagId_p; }; //# Define the RO version for backward compatibility. typedef MSDataDescColumns ROMSDataDescColumns; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSDataDescEnums.h000066400000000000000000000062761476623553700221730ustar00rootroot00000000000000//# MSDataDescEnums.h: Defs for the MS DATA_DESCRIPTION table //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSDATADESCENUMS_H #define MS_MSDATADESCENUMS_H #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Enums for the MeasurementSet DATA_DESCRIPTION table // // // // This class contains the enums for the MeasurementSet DATA_DESCRIPTION table // // // This class does nothing. It is merely a container for the enumerations // used by the MSDataDescription class. These enumerations define the // standard columns, keywords. // // // See the documentation for MeasurementSet for examples on the use of these // enumerations. // // // All the todo items which may be related to this class are // grouped with the todo items for MeasurementSet // // class MSDataDescriptionEnums { public: // The DATA_DESCRIPTION table colums with predefined meaning. enum PredefinedColumns { // "True" columns are defined.
        // TYPE - UNIT - MEASURE UNDEFINED_COLUMN=0, // Flag for this row
        // Bool FLAG_ROW, // polarization id, pointer to polarization table
        // Int. POLARIZATION_ID, // Spectral window id, pointer to spectral window table
        // Int. SPECTRAL_WINDOW_ID, // Number of required columns NUMBER_REQUIRED_COLUMNS=SPECTRAL_WINDOW_ID, // Lag Id - points to LAG subtable which describes lag correlation functions
        // Int LAG_ID, // Not a column, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_COLUMNS=LAG_ID }; // Keywords with a predefined meaning enum PredefinedKeywords { // // "True" keywords are defined. UNDEFINED_KEYWORD=0, // // Not a keyword, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_KEYWORDS=0 }; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSDataDescription.cc000066400000000000000000000131771476623553700227240ustar00rootroot00000000000000//# MSDataDescription.cc: The MeasurementSet DATA_DESCRIPTION Table //# Copyright (C) 1996,1998,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSDataDescription::MSDataDescription():hasBeenDestroyed_p(True) { } MSDataDescription::MSDataDescription(const String &tableName, TableOption option) : MSTable(tableName, option),hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSDataDescription(String &, TableOption) - " "table is not a valid MSDataDescription")); } MSDataDescription::MSDataDescription(const String& tableName, const String &tableDescName, TableOption option) : MSTable(tableName, tableDescName,option), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSDataDescription(String &, String &, TableOption) - " "table is not a valid MSDataDescription")); } MSDataDescription::MSDataDescription(SetupNewTable &newTab, rownr_t nrrow, Bool initialize) : MSTable(newTab, nrrow, initialize), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSDataDescription(SetupNewTable &, rownr_t, Bool) - " "table is not a valid MSDataDescription")); } MSDataDescription::MSDataDescription(const Table &table) : MSTable(table), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSDataDescription(const Table &) - " "table is not a valid MSDataDescription")); } MSDataDescription::MSDataDescription(const MSDataDescription &other) : MSTable(other), hasBeenDestroyed_p(False) { // verify that other is valid if (&other != this) if (! validate(this->tableDesc())) throw (AipsError("MSDataDescription(const MSDataDescription &) - " "table is not a valid MSDataDescription")); } MSDataDescription::~MSDataDescription() { // check to make sure that this MSDataDescription is still valid if (!hasBeenDestroyed_p && !validate()) { // the table is otherwise OK, so ensure that it is written if necessary this->flush(); LogIO os; os << LogIO::WARN << "~MSDataDescription() - Table written is not a valid MSDataDescription" << LogIO::POST; } hasBeenDestroyed_p = True; } MSDataDescription& MSDataDescription::operator=(const MSDataDescription &other) { if (&other != this) { MSTable::operator=(other); hasBeenDestroyed_p=other.hasBeenDestroyed_p; } return *this; } MSTableMaps MSDataDescription::initMaps() { MSTableMaps maps; // the PredefinedColumns // FLAG_ROW colMapDef(maps, FLAG_ROW,"FLAG_ROW", TpBool, "Flag this row","",""); // LAG_ID colMapDef(maps, LAG_ID,"LAG_ID",TpInt,"The lag index","",""); // POLARIZATION_ID colMapDef(maps, POLARIZATION_ID,"POLARIZATION_ID",TpInt, "Pointer to polarization table","",""); // SPECTRAL_WINDOW_ID colMapDef(maps, SPECTRAL_WINDOW_ID, "SPECTRAL_WINDOW_ID", TpInt, "Pointer to spectralwindow table","",""); // PredefinedKeywords // init requiredTableDesc // all required keywords uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(maps, PredefinedKeywords(i)); } // all required columns for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(maps, PredefinedColumns(i)); } return maps; } MSDataDescription MSDataDescription::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSDataDescription(MSTable:: referenceCopy(newTableName,writableColumns)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSDataDescription.h000066400000000000000000000117731476623553700225660ustar00rootroot00000000000000//# MSDataDescription.h: The MeasurementSet DATADESCRIPTION Table //# Copyright (C) 1996,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSDATADESCRIPTION_H #define MS_MSDATADESCRIPTION_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A Table intended to hold a MeasurementSet DATADESCRIPTION table. // // // // //
      • MeasurementSet //
      • MSTable // // // // MSDataDescription stands for the MeasurementSet Datadescription table. // // // // An MSDataDescription is a table intended to hold the DATADESCRIPTION table for // the MeasurementSet. It has an identical set of member functions as // the main MeasurementSet class, except (currently) for the default // calibration members. For further info and examples see the // MeasurementSet class. // // // // See the MeasurementSet for an example of how to access and use this class. // // // // It was found that subtables and the main table of the MeasurementSet have // a lot in common, therefore they derive their interface from the same // base class. Each subtable has its own class to keep the enum definitions // and conversion functions in separate scopes. // // // // see MeasurementSet. // class MSDataDescription:public MSDataDescriptionEnums, public MSTable { public: // This constructs an empty MSDataDescription MSDataDescription (); // // These constructors mirror the Table ones with additional checking // on validity (verifying that the MSDataDescription will have the required columns // and keywords). // An exception is thrown if the constructed Table is not a valid MSDataDescription // //
      • AipsError // // MSDataDescription (const String &tableName, TableOption = Table::Old); MSDataDescription (const String &tableName, const String &tableDescName, TableOption = Table::Old); MSDataDescription (SetupNewTable &newTab, rownr_t nrrow = 0, Bool initialize = False); MSDataDescription (const Table &table); MSDataDescription (const MSDataDescription &other); // // As with tables, the destructor writes the table if necessary. // Additional checking is done here to verify that all required // columns are still present. // If it is NOT valid, it will write the table and then throw an exception. // //
      • AipsError // ~MSDataDescription(); // Assignment operator, reference semantics MSDataDescription& operator=(const MSDataDescription&); // Make a special copy of this Table which references all columns from // this Table except those mentioned; those are empty and writable. // Each forwarded column has the same writable status as the underlying // column. The mentioned columns all use the AipsIO storage manager. // This function is inherited from MSTable and unlikely to be of use, // except in the class MeasurementSet (see comment there).. MSDataDescription referenceCopy(const String& newTableName, const Block& writableColumns) const; // Initialize the statics appropriately. This does not need to be // called by users, it is called by the implementation class // MSTableImpl. static MSTableMaps initMaps(); private: // required by the need to throw an exception in the destructor Bool hasBeenDestroyed_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSDoppler.cc000066400000000000000000000140671476623553700212530ustar00rootroot00000000000000//# MSDoppler.cc: The MeasurementSet DOPPLER Table //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSDoppler::MSDoppler():hasBeenDestroyed_p(True) { } MSDoppler::MSDoppler(const String &tableName, TableOption option) : MSTable(tableName, option),hasBeenDestroyed_p(False) { // verify that the now opened table is valid addVelDef(); if (! validate(this->tableDesc())) throw (AipsError("MSDoppler(String &, TableOption) - " "table is not a valid MSDoppler")); } void MSDoppler::addVelDef() { // For a transition period: add the VELDEF column // silently if it is not there - 2000/09/12, remove next MS update. if (tableDesc().isColumn(columnName(TRANSITION_ID))) { // we probably have a MSDoppler table if (!tableDesc().isColumn(columnName(VELDEF))) { if (!isWritable()) { throw (AipsError("Missing VELDEF column in MSDoppler table -" "please open MS table R/W to have it added")); } else { TableDesc td; addColumnToDesc(td,VELDEF); addColumn(td[0]); ScalarColumn velDef(*this,columnName(VELDEF)); velDef.fillColumn(0); } } } } MSDoppler::MSDoppler(const String& tableName, const String &tableDescName, TableOption option) : MSTable(tableName, tableDescName,option), hasBeenDestroyed_p(False) { // verify that the now opened table is valid addVelDef(); if (! validate(this->tableDesc())) throw (AipsError("MSDoppler(String &, String &, TableOption) - " "table is not a valid MSDoppler")); } MSDoppler::MSDoppler(SetupNewTable &newTab, rownr_t nrrow, Bool initialize) : MSTable(newTab, nrrow, initialize), hasBeenDestroyed_p(False) { // verify that the now opened table is valid addVelDef(); if (! validate(this->tableDesc())) throw (AipsError("MSDoppler(SetupNewTable &, rownr_t, Bool) - " "table is not a valid MSDoppler")); } MSDoppler::MSDoppler(const Table &table) : MSTable(table), hasBeenDestroyed_p(False) { // verify that the now opened table is valid addVelDef(); if (! validate(this->tableDesc())) throw (AipsError("MSDoppler(const Table &) - " "table is not a valid MSDoppler")); } MSDoppler::MSDoppler(const MSDoppler &other) : MSTable(other), hasBeenDestroyed_p(False) { // verify that other is valid if (&other != this) addVelDef(); if (! validate(this->tableDesc())) throw (AipsError("MSDoppler(const MSDoppler &) - " "table is not a valid MSDoppler")); } MSDoppler::~MSDoppler() { // check to make sure that this MSDoppler is still valid if (!hasBeenDestroyed_p && !validate()) { // the table is otherwise OK, so ensure that it is written if necessary this->flush(); LogIO os; os << LogIO::WARN << "~MSDoppler() - Table written is not a valid MSDoppler" << LogIO::POST; } hasBeenDestroyed_p = True; } MSDoppler& MSDoppler::operator=(const MSDoppler &other) { if (&other != this) { MSTable::operator=(other); hasBeenDestroyed_p=other.hasBeenDestroyed_p; } return *this; } MSTableMaps MSDoppler::initMaps() { MSTableMaps maps; // the PredefinedColumns // colMapDef(maps, DOPPLER_ID,"DOPPLER_ID", TpInt, "Doppler tracking id","",""); // SOURCE_ID colMapDef(maps, SOURCE_ID, "SOURCE_ID", TpInt, "Pointer to SOURCE table","",""); // TRANSITION_ID colMapDef(maps, TRANSITION_ID,"TRANSITION_ID",TpInt, "Pointer to list of transitions in SOURCE table","",""); // VELDEF colMapDef(maps, VELDEF, "VELDEF", TpDouble, "Velocity Definition for Doppler shift","m/s","Doppler"); // PredefinedKeywords // init requiredTableDesc // all required keywords uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(maps, PredefinedKeywords(i)); } // all required columns for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(maps, PredefinedColumns(i)); } return maps; } MSDoppler MSDoppler::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSDoppler(MSTable:: referenceCopy(newTableName,writableColumns)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSDoppler.h000066400000000000000000000117041476623553700211100ustar00rootroot00000000000000//# MSDoppler.h: The MeasurementSet DOPPLER Table //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSDOPPLER_H #define MS_MSDOPPLER_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A Table intended to hold a MeasurementSet DOPPLER table. // // // // //
      • MeasurementSet //
      • MSTable // // // // MSDoppler stands for the MeasurementSet Doppler table. // // // // An MSDoppler is a table intended to hold the DOPPLER table for // the MeasurementSet. It has an identical set of member functions as // the main MeasurementSet class, except (currently) for the default // calibration members. For further info and examples see the // MeasurementSet class. // // // // See the MeasurementSet for an example of how to access and use this class. // // // // It was found that subtables and the main table of the MeasurementSet have // a lot in common, therefore they derive their interface from the same // base class. Each subtable has its own class to keep the enum definitions // and conversion functions in separate scopes. // // // // see MeasurementSet. // class MSDoppler:public MSDopplerEnums, public MSTable { public: // This constructs an empty MSDoppler MSDoppler (); // // These constructors mirror the Table ones with additional checking // on validity (verifying that the MSDoppler will have the required columns // and keywords). // An exception is thrown if the constructed Table is not a valid MSDoppler // //
      • AipsError // // MSDoppler (const String &tableName, TableOption = Table::Old); MSDoppler (const String &tableName, const String &tableDescName, TableOption = Table::Old); MSDoppler (SetupNewTable &newTab, rownr_t nrrow = 0, Bool initialize = False); MSDoppler (const Table &table); MSDoppler (const MSDoppler &other); // // As with tables, the destructor writes the table if necessary. // Additional checking is done here to verify that all required // columns are still present. // If it is NOT valid, it will write the table and then throw an exception. // //
      • AipsError // ~MSDoppler(); // Assignment operator, reference semantics MSDoppler& operator=(const MSDoppler&); // Make a special copy of this Table which references all columns from // this Table except those mentioned; those are empty and writable. // Each forwarded column has the same writable status as the underlying // column. The mentioned columns all use the AipsIO storage manager. // This function is inherited from MSTable and unlikely to be of use, // except in the class MeasurementSet (see comment there).. MSDoppler referenceCopy(const String& newTableName, const Block& writableColumns) const; // Initialize the statics appropriately. This does not need to be // called by users, it is called by the implementation class // MSTableImpl. static MSTableMaps initMaps(); private: // temporary function to add the VELDEF // column if it isn't there yet. 2000/09/12 // remove this and the calls next MS update void addVelDef(); // required by the need to throw an exception in the destructor Bool hasBeenDestroyed_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSDopplerColumns.cc000066400000000000000000000045201476623553700226050ustar00rootroot00000000000000//# MSDopplerColumns.cc: provides easy access to MeasurementSet columns //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSDopplerColumns::MSDopplerColumns(): isNull_p(True) { } MSDopplerColumns::MSDopplerColumns(const MSDoppler& msDoppler): isNull_p(True) { attach(msDoppler); } MSDopplerColumns::~MSDopplerColumns() {} void MSDopplerColumns::attach(const MSDoppler& msDoppler) { isNull_p = msDoppler.isNull(); if (!isNull()) { dopplerId_p.attach(msDoppler, MSDoppler:: columnName(MSDoppler::DOPPLER_ID)); sourceId_p.attach(msDoppler, MSDoppler:: columnName(MSDoppler::SOURCE_ID)); transitionId_p.attach(msDoppler, MSDoppler:: columnName(MSDoppler::TRANSITION_ID)); velDef_p.attach(msDoppler, MSDoppler::columnName(MSDoppler::VELDEF)); velDefMeas_p.attach(msDoppler, MSDoppler:: columnName(MSDoppler::VELDEF)); velDefQuant_p.attach(msDoppler, MSDoppler:: columnName(MSDoppler::VELDEF)); } } void MSDopplerColumns::setVelDefRef(MDoppler::Types ref) { velDefMeas_p.setDescRefCode(ref,False); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSDopplerColumns.h000066400000000000000000000120501476623553700224440ustar00rootroot00000000000000//# MSDopplerColumns.h: provides easy access to MSDoppler columns //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSDOPPLERCOLUMNS_H #define MS_MSDOPPLERCOLUMNS_H #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class MSDoppler; // // A class to provide easy access to MSDoppler columns // // // // // //
      • MSDoppler //
      • ScalarColumn // // // // MSDopplerColumns stands for MeasurementSet Doppler Table columns. // // // // This class provides access to the columns in the MSDoppler Table, // it does the declaration of all the Scalar and ArrayColumns with the // correct types, so the application programmer doesn't have to // worry about getting those right. There is an access function // for every predefined column. Access to non-predefined columns will still // have to be done with explicit declarations. // See MSColumns for an example. // // // // See MSColumns for the motivation. // class MSDopplerColumns { public: // Create a columns object that accesses the data in the specified Table MSDopplerColumns(const MSDoppler& msDoppler); // The destructor does nothing special ~MSDopplerColumns(); // Is this object defined? (MSDoppler table is optional) Bool isNull() const {return isNull_p;} // Access to required columns // ScalarColumn& dopplerId() {return dopplerId_p;} ScalarColumn& sourceId() {return sourceId_p;} ScalarColumn& transitionId() {return transitionId_p;} ScalarColumn& velDef() {return velDef_p;} ScalarQuantColumn& velDefQuant(){return velDefQuant_p;} ScalarMeasColumn& velDefMeas() {return velDefMeas_p;} // // Const access to required columns // const ScalarColumn& dopplerId() const {return dopplerId_p;} const ScalarColumn& sourceId() const {return sourceId_p;} const ScalarColumn& transitionId() const {return transitionId_p;} const ScalarColumn& velDef() const {return velDef_p;} const ScalarQuantColumn& velDefQuant() const{return velDefQuant_p;} const ScalarMeasColumn& velDefMeas() const {return velDefMeas_p;} // // Convenience function that returns the number of rows in any of the // columns. Returns zero if the object is null. rownr_t nrow() const {return isNull() ? 0 : dopplerId_p.nrow();} // set the DOPPLER type for the VELDEF column. void setVelDefRef(MDoppler::Types ref); protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. MSDopplerColumns(); //# attach this object to the supplied table. void attach(const MSDoppler& msDoppler); private: //# Make the assignment operator and the copy constructor private to prevent //# any compiler generated one from being used. MSDopplerColumns(const MSDopplerColumns&); MSDopplerColumns& operator=(const MSDopplerColumns&); //# Is the object not attached to a Table. Bool isNull_p; //# required columns ScalarColumn dopplerId_p; ScalarColumn sourceId_p; ScalarColumn transitionId_p; ScalarColumn velDef_p; //# Access to Measure columns ScalarMeasColumn velDefMeas_p; //# Access to Quantum columns ScalarQuantColumn velDefQuant_p; }; //# Define the RO version for backward compatibility. typedef MSDopplerColumns ROMSDopplerColumns; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSDopplerEnums.h000066400000000000000000000062111476623553700221150ustar00rootroot00000000000000//# MSDopplerEnums.h: Defs for the MS DOPPLER table //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSDOPPLERENUMS_H #define MS_MSDOPPLERENUMS_H #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Enums for the MeasurementSet DOPPLER table // // // // This class contains the enums for the MeasurementSet DOPPLER table // // // This class does nothing. It is merely a container for the enumerations // used by the MSDoppler class. These enumerations define the // standard columns, keywords. // // // See the documentation for MeasurementSet for examples on the use of these // enumerations. // // // All the todo items which may be related to this class are // grouped with the todo items for MeasurementSet // // class MSDopplerEnums { public: // The DOPPLER table colums with predefined meaning. enum PredefinedColumns { // "True" columns are defined.
        // TYPE - UNIT - MEASURE UNDEFINED_COLUMN=0, // doppler tracking id, used in SPECTRAL_WINDOW table
        // Int. DOPPLER_ID, // Source id, pointer to SOURCE table
        // Int. SOURCE_ID, // Transition id, index into list of transitions in SOURCE table
        // Int TRANSITION_ID, // Velocity definition for Doppler shift // Double - m/s - DOPPLER VELDEF, // Number of required columns NUMBER_REQUIRED_COLUMNS=VELDEF, // Not a column, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_COLUMNS=NUMBER_REQUIRED_COLUMNS }; // Keywords with a predefined meaning enum PredefinedKeywords { // // "True" keywords are defined. UNDEFINED_KEYWORD=0, // // Not a keyword, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_KEYWORDS=0 }; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSDopplerUtil.cc000066400000000000000000000135071476623553700221070ustar00rootroot00000000000000//# MSDopplerUtil.cc: Implementation of MSDopplerUtil.h //# Copyright (C) 1996,1997,1998,1999,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //---------------------------------------------------------------------------- #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //---------------------------------------------------------------------------- MSDopplerUtil::MSDopplerUtil(const MeasurementSet& ms) : ms_p(ms) { // Construct from an existing MS // Input: // ms const MeasurementSet& Input MS // Output to private data: // ms_p MeasurementSet Private MS copy // } //---------------------------------------------------------------------------- MSDopplerUtil::~MSDopplerUtil() { // Null default destructor // } //---------------------------------------------------------------------------- Bool MSDopplerUtil::dopplerInfo (Vector& restFrequency, Int spwId, Int fieldId) { // Retrieve a list of all rest frequencies used in Doppler // tracking of the specified spectral window id. // Output: // restFrequency Vector List of rest frequencies // dopplerInfo Bool True if Doppler info. found // // Initialization restFrequency.resize(); Int nRestFreq = 0; Bool found = False; // Accessor for the MS columns and sub-tables MSColumns msc (ms_p); // Retrieve the doppler id & source id Int dopId = (msc.spectralWindow().dopplerId().isNull() ? -1 : msc.spectralWindow().dopplerId()(spwId)); Int srcId = msc.field().sourceId()(fieldId); // Use the doppler table if specified and it exists if (dopId >= 0 && (!ms_p.doppler().isNull())) { // Find the matching DOPPLER sub-table rows for this DOPPLER_ID for (uInt idoprow=0; idoprow rows = sourceIndex.getRowNumbers(); for (rownr_t irow=0; irow restFrq = msc.source().restFrequency()(irow); if(restFrq.nelements() >0){ // Does this already exist in the output rest frequency array ? Bool exists = False; for (uInt k=0; k 0)){ // use just the source table if it exists MSSourceIndex sourceIndex(ms_p.source()); sourceIndex.sourceId()= msc.field().sourceId()(fieldId); sourceIndex.spectralWindowId()=spwId; Vector rows = sourceIndex.getRowNumbers(); if (!msc.source().restFrequency().isNull()){ for (rownr_t irow=0; irow restFrq = msc.source().restFrequency()(rows(irow)); // Does this already exist in the output rest frequency array ? for (uInt transId=0; transId #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A utility class for MS Doppler tracking information // // // // // // // //
      • MeasurementSet //
      • MSDoppler // // // // From "MeasurementSet", "Doppler" and "utility". // // // // This class provides utilities for handling Doppler tracking // information in a MeasurementSet. // // // // // // // Collect all utilities for handling MS Doppler tracking information. // // // //
      • //
      • // // // //
      • averaging over other indices. // class MSDopplerUtil { public: // Construct from an existing MeasurementSet MSDopplerUtil (const MeasurementSet& ms); // Null destructor ~MSDopplerUtil(); // Retrieve a list of all rest frequencies used for a given // spectral window id and field id. // Note that the output vector combines information for multiple spectral lines // and (unlikely) multiple source table entries. // If the doppler sub table doesn't exist, the information is // retrieved from directly from the source sub table. Bool dopplerInfo (Vector& restFrequency, Int spwId, Int fieldId); private: // Prohibit null constructor, copy constructor and assignment for now MSDopplerUtil(); MSDopplerUtil& operator= (const MSDopplerUtil&); MSDopplerUtil (const MSDopplerUtil&); // Underlying MeasurementSet MeasurementSet ms_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSFeed.cc000066400000000000000000000161661476623553700205130ustar00rootroot00000000000000//# MSFeed.cc: The MeasurementSet FEED Table //# Copyright (C) 1996,1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSFeed::MSFeed():hasBeenDestroyed_p(True) { } MSFeed::MSFeed(const String &tableName, TableOption option) : MSTable(tableName, option),hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSFeed(String &, TableOption) - " "table is not a valid MSFeed")); } MSFeed::MSFeed(const String& tableName, const String &tableDescName, TableOption option) : MSTable(tableName, tableDescName,option), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSFeed(String &, String &, TableOption) - " "table is not a valid MSFeed")); } MSFeed::MSFeed(SetupNewTable &newTab, rownr_t nrrow, Bool initialize) : MSTable(newTab, nrrow, initialize), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSFeed(SetupNewTable &, rownr_t, Bool) - " "table is not a valid MSFeed")); } MSFeed::MSFeed(const Table &table) : MSTable(table), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSFeed(const Table &) - " "table is not a valid MSFeed")); } MSFeed::MSFeed(const MSFeed &other) : MSTable(other), hasBeenDestroyed_p(False) { // verify that other is valid if (&other != this) if (! validate(this->tableDesc())) throw (AipsError("MSFeed(const MSFeed &) - " "table is not a valid MSFeed")); } MSFeed::~MSFeed() { // check to make sure that this MSFeed is still valid if (!hasBeenDestroyed_p && !validate()) { // the table is otherwise OK, so ensure that it is written if necessary this->flush(); LogIO os; os << LogIO::WARN << "~MSFeed() - Table written is not a valid MSFeed" << LogIO::POST; } hasBeenDestroyed_p = True; } MSFeed& MSFeed::operator=(const MSFeed &other) { if (&other != this) { MSTable::operator=(other); hasBeenDestroyed_p=other.hasBeenDestroyed_p; } return *this; } MSTableMaps MSFeed::initMaps() { MSTableMaps maps; AlwaysAssert (maps.columnMap_p.empty(), AipsError); // the PredefinedColumns // ANTENNA_ID colMapDef(maps, ANTENNA_ID, "ANTENNA_ID", TpInt, "ID of antenna in this array","",""); // BEAM_ID colMapDef(maps, BEAM_ID,"BEAM_ID",TpInt, "Id for BEAM model","",""); // BEAM_OFFSET colMapDef(maps, BEAM_OFFSET,"BEAM_OFFSET",TpArrayDouble, "Beam position offset (on sky but in antenna" "reference frame)","rad","Direction"); // FEED_ID colMapDef(maps, FEED_ID,"FEED_ID",TpInt, "Feed id","",""); // FOCUS_LENGTH colMapDef(maps, FOCUS_LENGTH,"FOCUS_LENGTH",TpDouble, "Focus length","m",""); // INTERVAL colMapDef(maps, INTERVAL,"INTERVAL",TpDouble, "Interval for which this set of parameters is accurate", "s",""); // NUM_RECEPTORS colMapDef(maps, NUM_RECEPTORS,"NUM_RECEPTORS",TpInt, "Number of receptors on this feed (probably 1 or 2)","",""); // PHASED_FEED_ID colMapDef(maps, PHASED_FEED_ID,"PHASED_FEED_ID",TpInt, "index into PHASED_FEED table (ignore if<0)","",""); // POL_RESPONSE colMapDef(maps, POL_RESPONSE,"POL_RESPONSE",TpArrayComplex, "D-matrix i.e. leakage between two receptors","",""); // POLARIZATION_TYPE colMapDef(maps, POLARIZATION_TYPE,"POLARIZATION_TYPE",TpArrayString, "Type of polarization to which a given RECEPTOR responds", "",""); // POSITION colMapDef(maps, POSITION,"POSITION",TpArrayDouble, "Position of feed relative to feed reference position", "m","Position"); // RECEPTOR_ANGLE colMapDef(maps, RECEPTOR_ANGLE,"RECEPTOR_ANGLE",TpArrayDouble, "The reference angle for polarization","rad",""); // SPECTRAL_WINDOW_ID colMapDef(maps, SPECTRAL_WINDOW_ID,"SPECTRAL_WINDOW_ID",TpInt, "ID for this spectral window setup","",""); // TIME colMapDef(maps, TIME,"TIME",TpDouble, "Midpoint of time for which this set of " "parameters is accurate","s","Epoch"); // PredefinedKeywords // init requiredTableDesc // all required keywords uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(maps, PredefinedKeywords(i)); } // all required columns // First define the columns with fixed size arrays IPosition shape(1,3); ColumnDesc::Option option=ColumnDesc::Direct; addColumnToDesc(maps, POSITION, shape, option); // define the columns with known dimensionality addColumnToDesc(maps, BEAM_OFFSET, 2); addColumnToDesc(maps, POLARIZATION_TYPE, 1); addColumnToDesc(maps, POL_RESPONSE, 2); addColumnToDesc(maps, RECEPTOR_ANGLE, 1); // Now define all other columns (duplicates are skipped) for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(maps, PredefinedColumns(i)); } return maps; } MSFeed MSFeed::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSFeed(MSTable::referenceCopy (newTableName,writableColumns)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSFeed.h000066400000000000000000000113041476623553700203420ustar00rootroot00000000000000//# MSFeed.h: The MeasurementSet FEED Table //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSFEED_H #define MS_MSFEED_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A Table intended to hold a MeasurementSet FEED table. // // // // //
      • MeasurementSet //
      • MSTable // // // // MSFeed stands for the MeasurementSet Feed table. // // // // An MSFeed is a table intended to hold the FEED table for // the MeasurementSet. It has an identical set of member functions as // the main MeasurementSet class, except (currently) for the default // calibration members. For further info and examples see the // MeasurementSet class. // // // // See the MeasurementSet for an example of how to access and use this class. // // // // It was found that subtables and the main table of the MeasurementSet have // a lot in common, therefore they derive their interface from the same // base class. Each subtable has its own class to keep the enum definitions // and conversion functions in separate scopes. // // // // see MeasurementSet. // class MSFeed:public MSFeedEnums, public MSTable { public: // This constructs an empty MSFeed. MSFeed (); // These constructors mirror the Table ones with additional checking // on validity (verifying that the MSFeed will have the required columns // and keywords) // An exception is thrown if the constructed Table is not a valid MSFeed // //
      • AipsError // // MSFeed (const String &tableName, TableOption = Table::Old); MSFeed (const String &tableName, const String &tableDescName, TableOption = Table::Old); MSFeed (SetupNewTable &newTab, rownr_t nrrow = 0, Bool initialize = False); MSFeed (const Table &table); MSFeed (const MSFeed &other); // // As with tables, the destructor writes the table if necessary. // Additional checking is done here to verify that all required // columns are still present. // If it is NOT valid, it will write the table and then throw an exception. // //
      • AipsError // ~MSFeed(); // Assignment operator, reference semantics MSFeed& operator=(const MSFeed&); // Make a special copy of this Table which references all columns from // this Table except those mentioned; those are empty and writable. // Each forwarded column has the same writable status as the underlying // column. The mentioned columns all use the AipsIO storage manager. // This function is inherited from MSTable and unlikely to be of use, // except in the class MeasurementSet (see comment there).. MSFeed referenceCopy(const String& newTableName, const Block& writableColumns) const; // Initialize the statics appropriately. This does not need to be // called by users, it is called by the implementation class // MSTableImpl. static MSTableMaps initMaps(); private: // required by the need to throw an exception in the destructor Bool hasBeenDestroyed_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSFeedColumns.cc000066400000000000000000000171201476623553700220430ustar00rootroot00000000000000//# MSFeedColumns.cc: provides easy access to MeasurementSet columns //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSFeedColumns::MSFeedColumns() { } MSFeedColumns::MSFeedColumns(const MSFeed& msFeed) { attach(msFeed); } MSFeedColumns::~MSFeedColumns() {} void MSFeedColumns::attach(const MSFeed& msFeed) { antennaId_p.attach(msFeed, MSFeed::columnName(MSFeed::ANTENNA_ID)); beamId_p.attach(msFeed, MSFeed::columnName(MSFeed::BEAM_ID)); beamOffset_p.attach(msFeed, MSFeed::columnName(MSFeed::BEAM_OFFSET)); feedId_p.attach(msFeed, MSFeed::columnName(MSFeed::FEED_ID)); interval_p.attach(msFeed, MSFeed::columnName(MSFeed::INTERVAL)); numReceptors_p.attach(msFeed, MSFeed::columnName(MSFeed::NUM_RECEPTORS)); polResponse_p.attach(msFeed, MSFeed::columnName(MSFeed::POL_RESPONSE)); polarizationType_p.attach(msFeed, MSFeed:: columnName(MSFeed::POLARIZATION_TYPE)); position_p.attach(msFeed, MSFeed::columnName(MSFeed::POSITION)); receptorAngle_p.attach(msFeed, MSFeed::columnName(MSFeed::RECEPTOR_ANGLE)); spectralWindowId_p.attach(msFeed, MSFeed:: columnName(MSFeed::SPECTRAL_WINDOW_ID)); time_p.attach(msFeed, MSFeed::columnName(MSFeed::TIME)); beamOffsetMeas_p.attach(msFeed, MSFeed::columnName(MSFeed::BEAM_OFFSET)); positionMeas_p.attach(msFeed, MSFeed::columnName(MSFeed::POSITION)); timeMeas_p.attach(msFeed, MSFeed::columnName(MSFeed::TIME)); beamOffsetQuant_p.attach(msFeed, MSFeed::columnName(MSFeed::BEAM_OFFSET)); intervalQuant_p.attach(msFeed, MSFeed::columnName(MSFeed::INTERVAL)); positionQuant_p.attach(msFeed, MSFeed::columnName(MSFeed::POSITION)); receptorAngleQuant_p.attach(msFeed, MSFeed:: columnName(MSFeed::RECEPTOR_ANGLE)); timeQuant_p.attach(msFeed, MSFeed::columnName(MSFeed::TIME)); attachOptionalCols(msFeed); } void MSFeedColumns::attachOptionalCols(const MSFeed& msFeed) { const ColumnDescSet& cds = msFeed.tableDesc().columnDescSet(); const String& focusLength = MSFeed::columnName(MSFeed::FOCUS_LENGTH); if (cds.isDefined(focusLength)) { focusLength_p.attach(msFeed, focusLength); focusLengthQuant_p.attach(msFeed, focusLength); } const String& phasedFeedId =MSFeed::columnName(MSFeed::PHASED_FEED_ID); if (cds.isDefined(phasedFeedId)) phasedFeedId_p.attach(msFeed, phasedFeedId); } void MSFeedColumns::setEpochRef(MEpoch::Types ref, Bool tableMustBeEmpty) { timeMeas_p.setDescRefCode(ref, tableMustBeEmpty); } void MSFeedColumns::setDirectionRef(MDirection::Types ref) { beamOffsetMeas_p.setDescRefCode(ref); } void MSFeedColumns::setPositionRef(MPosition::Types ref) { positionMeas_p.setDescRefCode(ref); } Int64 MSFeedColumns::matchFeed(Quantum& newTimeQ, Quantum& newIntervalQ, Int antId, Int fId, Int spwId, const Quantum& timeQ, const Quantum& intervalQ, Int numRec, const Array >& beamOffsetQ, const Array& polType, const Array& polResp, const Array >& positionQ, const Array >& receptorAngleQ, const RowNumbers& ignoreRows, const Quantum& focusLengthQ ) { const Unit d("deg"); const Unit s("s"); const Unit m("m"); newTimeQ = newIntervalQ = Quantum(0.,s); rownr_t r = nrow(); if (r == 0) return -1; const Double timeInS = timeQ.getValue(s); const Double halfIntervalInS = intervalQ.getValue(s)/2.; const Double pos0InM = positionQ(IPosition(1,0)).getValue(m); const Double pos1InM = positionQ(IPosition(1,1)).getValue(m); const Double pos2InM = positionQ(IPosition(1,2)).getValue(m); // Matching loop while (r > 0) { r--; Bool ignore = False; for(size_t i=0; i= timeInS+halfIntervalInS) ){ // only difference is the validity time newTimeQ = (timeQuant()(r)+timeQ)/2.; Double maxTime = std::max(timeQuant()(r).getValue(s)+modHalfIntervalInS, timeInS+halfIntervalInS); newIntervalQ = Quantum(2*(maxTime-newTimeQ.getValue(s)), s); } return r; } } } } return -1; } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSFeedColumns.h000066400000000000000000000253571476623553700217200ustar00rootroot00000000000000//# MSFeedColumns.h: provides easy access to MSFeed columns //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSFEEDCOLUMNS_H #define MS_MSFEEDCOLUMNS_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class MSFeed; // // A class to provide easy access to MSFeed columns // // // // // //
      • MSFeed //
      • ArrayColumn //
      • ScalarColumn // // // // MSFeedColumns stands for MeasurementSet Feed Table columns. // // // // This class provides access to the columns in the MSFeed Table, // it does the declaration of all the Scalar and ArrayColumns with the // correct types, so the application programmer doesn't have to // worry about getting those right. There is an access function // for every predefined column. Access to non-predefined columns will still // have to be done with explicit declarations. // See MSColumns for an example. // // // // See MSColumns for the motivation. // class MSFeedColumns { public: // Create a columns object that accesses the data in the specified Table MSFeedColumns(const MSFeed& msFeed); // The desctructor does nothing special ~MSFeedColumns(); // Access to required columns // ScalarColumn& antennaId() {return antennaId_p;} ScalarColumn& beamId() {return beamId_p;} ArrayColumn& beamOffset() {return beamOffset_p;} ArrayQuantColumn& beamOffsetQuant() { return beamOffsetQuant_p;} ArrayMeasColumn& beamOffsetMeas() {return beamOffsetMeas_p;} ScalarColumn& feedId() {return feedId_p;} ScalarColumn& interval() {return interval_p;} ScalarQuantColumn& intervalQuant() { return intervalQuant_p;} ScalarColumn& numReceptors() {return numReceptors_p;} ArrayColumn& polResponse() {return polResponse_p;} ArrayColumn& polarizationType() {return polarizationType_p;} ArrayColumn& position() {return position_p;} ArrayQuantColumn& positionQuant() {return positionQuant_p;} ScalarMeasColumn& positionMeas() { return positionMeas_p;} ArrayColumn& receptorAngle() {return receptorAngle_p;} ArrayQuantColumn& receptorAngleQuant() { return receptorAngleQuant_p;} ScalarColumn& spectralWindowId() {return spectralWindowId_p;} ScalarColumn& time() {return time_p;} ScalarQuantColumn& timeQuant() { return timeQuant_p;} ScalarMeasColumn& timeMeas() { return timeMeas_p;} // // Const access to required columns // const ScalarColumn& antennaId() const {return antennaId_p;} const ScalarColumn& beamId() const {return beamId_p;} const ArrayColumn& beamOffset() const {return beamOffset_p;} const ArrayQuantColumn& beamOffsetQuant() const { return beamOffsetQuant_p;} const ArrayMeasColumn& beamOffsetMeas() const {return beamOffsetMeas_p;} const ScalarColumn& feedId() const {return feedId_p;} const ScalarColumn& interval() const {return interval_p;} const ScalarQuantColumn& intervalQuant() const { return intervalQuant_p;} const ScalarColumn& numReceptors() const {return numReceptors_p;} const ArrayColumn& polResponse() const {return polResponse_p;} const ArrayColumn& polarizationType() const {return polarizationType_p;} const ArrayColumn& position() const {return position_p;} const ArrayQuantColumn& positionQuant() const {return positionQuant_p;} const ScalarMeasColumn& positionMeas() const { return positionMeas_p;} const ArrayColumn& receptorAngle() const {return receptorAngle_p;} const ArrayQuantColumn& receptorAngleQuant() const { return receptorAngleQuant_p;} const ScalarColumn& spectralWindowId() const {return spectralWindowId_p;} const ScalarColumn& time() const {return time_p;} const ScalarQuantColumn& timeQuant() const { return timeQuant_p;} const ScalarMeasColumn& timeMeas() const { return timeMeas_p;} // // Access to optional columns // ScalarColumn& focusLength() {return focusLength_p;} ScalarQuantColumn& focusLengthQuant() { return focusLengthQuant_p;} ScalarColumn& phasedFeedId() {return phasedFeedId_p;} // // Const access to optional columns // const ScalarColumn& focusLength() const {return focusLength_p;} const ScalarQuantColumn& focusLengthQuant() const { return focusLengthQuant_p;} const ScalarColumn& phasedFeedId() const {return phasedFeedId_p;} // // set the epoch type for the TIME column. // // In principle this function can only be used if the table is empty, // otherwise already written values may thereafter have an incorrect // reference, offset, or unit. However, it is possible that part of the // table gets written before these values are known. In that case the // reference, offset, or units can be set by using a False // tableMustBeEmpty argument. // void setEpochRef(MEpoch::Types ref, Bool tableMustBeEmpty=True); // set the direction type for the BEAM_OFFSET column. This can only be done // when the table has no rows. Trying to do so at other times will throw an // exception. void setDirectionRef(MDirection::Types ref); // set the position type for the POSITION column. This can only be done when // the table has no rows. Trying to do so at other times will throw an // exception. void setPositionRef(MPosition::Types ref); // Convenience function that returns the number of rows in any of the columns rownr_t nrow() const {return antennaId_p.nrow();} // Returns the last row that contains a feed with the specified values. // If no matching row can be found, but a match is possible if the validity // time interval is widened, return that row and the suggestion for the // new time information. // If no change to time is necessary, newTimeQ and newIntervalQ are zero. // Returns -1 if no match could be found. // Ignore the Feed table rows contained in vector ignoreRows. // focusLengthQ is only compared if this optional column is present and // if the value of focusLengthQ is not dimensionless. Int64 matchFeed(Quantum& newTimeQ, Quantum& newIntervalQ, Int antId, Int feedId, Int spwId, const Quantum& timeQ, const Quantum& intervalQ, Int numReceptor, const Array >& beamOffsetQ, const Array& polType, const Array& polResp, const Array >& positionQ, const Array >& receptorAngleQ, const RowNumbers& ignoreRows, const Quantum& focusLengthQ=Quantum() ); protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. MSFeedColumns(); //# attach this object to the supplied table. void attach(const MSFeed& msFeed); private: //# Make the assignment operator and the copy constructor private to prevent //# any compiler generated one from being used. MSFeedColumns(const MSFeedColumns&); MSFeedColumns& operator=(const MSFeedColumns&); //# Check if any optional columns exist and if so attach them. void attachOptionalCols(const MSFeed& msFeed); //# required columns ScalarColumn antennaId_p; ScalarColumn beamId_p; ArrayColumn beamOffset_p; ScalarColumn feedId_p; ScalarColumn interval_p; ScalarColumn numReceptors_p; ArrayColumn polResponse_p; ArrayColumn polarizationType_p; ArrayColumn position_p; ArrayColumn receptorAngle_p; ScalarColumn spectralWindowId_p; ScalarColumn time_p; //# optional columns ScalarColumn focusLength_p; ScalarColumn phasedFeedId_p; //# Access to Measure columns ArrayMeasColumn beamOffsetMeas_p; ScalarMeasColumn positionMeas_p; ScalarMeasColumn timeMeas_p; //# Access to Quantum columns ArrayQuantColumn beamOffsetQuant_p; ScalarQuantColumn intervalQuant_p; ArrayQuantColumn positionQuant_p; ArrayQuantColumn receptorAngleQuant_p; ScalarQuantColumn timeQuant_p; //# optional Quantum columns ScalarQuantColumn focusLengthQuant_p; }; //# Define the RO version for backward compatibility. typedef MSFeedColumns ROMSFeedColumns; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSFeedEnums.h000066400000000000000000000104441476623553700213560ustar00rootroot00000000000000//# MSFeedEnums.h: Definitions for the MeasurementSet FEED table //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSFEEDENUMS_H #define MS_MSFEEDENUMS_H #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Enums for the MeasurementSet FEED table // // // // This class contains the enums for the MeasurementSet FEED table // // // This class does nothing. It is merely a container for the enumerations // used by the MSFeed class. These enumerations define the // standard columns, keywords. // // // See the documentation for MeasurementSet for examples on the use of these // enumerations. // // // All the todo items which may be related to this class are // grouped with the todo items for MeasurementSet // // class MSFeedEnums { public: // The FEED table colums with predefined meaning. enum PredefinedColumns { // "True" columns are defined.
        // TYPE - UNIT - MEASURE UNDEFINED_COLUMN=0, // Antenna Id.
        // Int. ANTENNA_ID, // Index in BEAM model table. This is a specialized model // e.g. NRAO_VLA_BEAM would have parameters for polynomial.
        // Int. BEAM_ID, // Beam position offset (on sky but in antenna reference frame).
        // Double(2,NUM_RECEPTORS) - rad - DIRECTION BEAM_OFFSET, // Feed id
        // Int FEED_ID, // Interval for which this set of parameters is accurate
        // Double - s INTERVAL, // Number of receptors on this feed (probably 1 or 2)
        // Int NUM_RECEPTORS, // D-matrix i.e. leakage between two receptors i.e. only makes // sense if NUM_RECEPTORS>1. Dimensionless coupling numbers.
        // Complex(NUM_RECEPTORS,NUM_RECEPTORS) POL_RESPONSE, // Type of polarization to which a given RECEPTOR responds. Probably // R, L or X, Y.
        // String(NUM_RECEPTORS) POLARIZATION_TYPE, // Position of feed relative to feed reference position for this antenna
        // Double(3) - m - POSITION POSITION, // The reference angle for polarization. Converts into // Parallactic angle in the Sky domain.
        // Double(2) - rad RECEPTOR_ANGLE, // Spectral Window id
        // Int SPECTRAL_WINDOW_ID, // Midpoint of time for which this set of parameters is accurate
        // Double - s - EPOCH TIME, // Number of required columns NUMBER_REQUIRED_COLUMNS=TIME, // Focus length
        // Double - m FOCUS_LENGTH, // Phased feed id to index into PHASED_FEED table
        // Int PHASED_FEED_ID, // // Not a column, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_COLUMNS=PHASED_FEED_ID }; // Keywords with a predefined meaning enum PredefinedKeywords { // // "True" keywords are defined. UNDEFINED_KEYWORD=0, // // Not a keyword, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_KEYWORDS=0 }; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSField.cc000066400000000000000000000202551476623553700206650ustar00rootroot00000000000000//# MSField.cc: The MeasurementSet FIELD Table //# Copyright (C) 1996,1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSField::MSField():hasBeenDestroyed_p(True) { } MSField::MSField(const String &tableName, TableOption option) : MSTable(tableName, option),hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSField(String &, TableOption) - " "table is not a valid MSField")); } MSField::MSField(const String& tableName, const String &tableDescName, TableOption option) : MSTable(tableName, tableDescName,option), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSField(String &, String &, TableOption) - " "table is not a valid MSField")); } MSField::MSField(SetupNewTable &newTab, rownr_t nrrow, Bool initialize) : MSTable(newTab, nrrow, initialize), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSField(SetupNewTable &, rownr_t, Bool) - " "table is not a valid MSField")); } MSField::MSField(const Table &table) : MSTable(table), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSField(const Table &) - " "table is not a valid MSField")); } MSField::MSField(const MSField &other) : MSTable(other), hasBeenDestroyed_p(False) { // verify that other is valid if (&other != this) if (! validate(this->tableDesc())) throw (AipsError("MSField(const MSField &) - " "table is not a valid MSField")); } MSField::~MSField() { // check to make sure that this MSField is still valid if (!hasBeenDestroyed_p && !validate()) { // the table is otherwise OK, so ensure that it is written if necessary this->flush(); LogIO os; os << LogIO::WARN << "~MSField() - Table written is not a valid MSField" << LogIO::POST; } hasBeenDestroyed_p = True; } MSField& MSField::operator=(const MSField &other) { if (&other != this) { MSTable::operator=(other); hasBeenDestroyed_p=other.hasBeenDestroyed_p; } return *this; } MSTableMaps MSField::initMaps() { MSTableMaps maps; // the PredefinedColumns // CODE colMapDef(maps, CODE, "CODE", TpString, "Special characteristics of field, " "e.g. Bandpass calibrator","",""); // DELAY_DIR colMapDef(maps, DELAY_DIR, "DELAY_DIR", TpArrayDouble, "Direction of delay center (e.g. RA, DEC)" "as polynomial in time.","rad","Direction"); // EPHEMERIS_ID colMapDef(maps, EPHEMERIS_ID,"EPHEMERIS_ID", TpInt, "Ephemeris id, pointer to EPHEMERIS table","",""); // FLAG_ROW colMapDef(maps, FLAG_ROW, "FLAG_ROW", TpBool, "Row Flag","",""); // NAME colMapDef(maps, NAME, "NAME", TpString, "Name of this field","",""); // NUM_POLY colMapDef(maps, NUM_POLY, "NUM_POLY", TpInt, "Polynomial order of _DIR columns","",""); // PHASE_DIR colMapDef(maps, PHASE_DIR, "PHASE_DIR", TpArrayDouble, "Direction of phase center (e.g. RA, DEC).", "rad","Direction"); // REFERENCE_DIR colMapDef(maps, REFERENCE_DIR, "REFERENCE_DIR", TpArrayDouble, "Direction of REFERENCE center (e.g. RA, DEC)." "as polynomial in time.","rad","Direction"); // SOURCE_ID colMapDef(maps, SOURCE_ID, "SOURCE_ID", TpInt, "Source id","",""); // TIME colMapDef(maps, TIME, "TIME", TpDouble, "Time origin for direction and rate","s","Epoch"); // PredefinedKeywords // init requiredTableDesc // all required keywords uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(maps, PredefinedKeywords(i)); } // all required columns // First define the columns with known dimensionality addColumnToDesc(maps, DELAY_DIR, 2); addColumnToDesc(maps, PHASE_DIR, 2); addColumnToDesc(maps, REFERENCE_DIR, 2); // Now define all other columns (duplicates are skipped) for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(maps, PredefinedColumns(i)); } return maps; } MSField MSField::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSField(MSTable:: referenceCopy(newTableName,writableColumns)); } Bool MSField::addEphemeris(const uInt id, const String& inputEphemTableName, const String& comment){ Bool rval=False; if( (inputEphemTableName.empty() && comment.empty()) || Table::isReadable(inputEphemTableName) ){ // add the eph id column if it doesn't exist alreay const String& ephemerisId = MSField::columnName(MSField::EPHEMERIS_ID); if(!this->actualTableDesc().isColumn(ephemerisId)){ if(this->isWritable()){ try{ this->addColumn(ScalarColumnDesc(ephemerisId, "Ephemeris id, pointer to EPHEMERIS table"), False); } catch(...){ return False; } // initialize to -1 ScalarColumn fld(*this, ephemerisId); for(rownr_t i=0; inrow(); i++){ fld.put(i,-1); } rval = True; } else{ return False; } } if(Table::isReadable(inputEphemTableName)){ Directory inputDir(inputEphemTableName); stringstream ss; ss << "/EPHEM" << id << "_" << comment << ".tab"; String destTableName = Path(this->tableName()).absoluteName() + String(ss.str()); removeEphemeris(id); // remove preexisting ephemerides with the same id inputDir.copy(destTableName); rval = True; } } return rval; } Bool MSField::removeEphemeris(const uInt id){ Bool rval=True; Directory fieldDir(Path(this->tableName()).absoluteName()); stringstream ss; ss << "EPHEM" << id << "_*.tab"; Regex ephemTableRegex (Regex::fromPattern(ss.str())); Vector candidates = fieldDir.find(ephemTableRegex, True, False); // followSymLinks=True, recursive=False for(uInt i=0; i #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A Table intended to hold a MeasurementSet FIELD table. // // // // //
      • MeasurementSet //
      • MSTable // // // // MSField stands for the MeasurementSet Field table. // // // // An MSField is a table intended to hold the FIELD table for // the MeasurementSet. It has an identical set of member functions as // the main MeasurementSet class, except (currently) for the default // calibration members. For further info and examples see the // MeasurementSet class. // // // // See the MeasurementSet for an example of how to access and use this class. // // // // It was found that subtables and the main table of the MeasurementSet have // a lot in common, therefore they derive their interface from the same // base class. Each subtable has its own class to keep the enum definitions // and conversion functions in separate scopes. // // // // see MeasurementSet. // class MSField:public MSFieldEnums, public MSTable { public: // This constructs an empty MSField. MSField (); // These constructors mirror the Table ones with additional checking // on validity (verifying that the MSField will have the required columns // and keywords) // An exception is thrown if the constructed Table is not a valid MSField // //
      • AipsError // // MSField (const String &tableName, TableOption = Table::Old); MSField (const String &tableName, const String &tableDescName, TableOption = Table::Old); MSField (SetupNewTable &newTab, rownr_t nrrow = 0, Bool initialize = False); MSField (const Table &table); MSField (const MSField &other); // // As with tables, the destructor writes the table if necessary. // Additional checking is done here to verify that all required // columns are still present. // If it is NOT valid, it will write the table and then throw an exception. // //
      • AipsError // ~MSField(); // Assignment operator, reference semantics MSField& operator=(const MSField&); // Make a special copy of this Table which references all columns from // this Table except those mentioned; those are empty and writable. // Each forwarded column has the same writable status as the underlying // column. The mentioned columns all use the AipsIO storage manager. // This function is inherited from MSTable and unlikely to be of use, // except in the class MeasurementSet (see comment there).. MSField referenceCopy(const String& newTableName, const Block& writableColumns) const; // Add an ephemeris table (there can be many) to the Field table. // The table is copied from inputEphemTableName and named // EPHEM_.tab // If any tables of the same id exist already, they are removed beforehand. // The optional EPHEMERIS_ID column is added if it doesn't exist, yet. // Return False in case of errors. Bool addEphemeris(const uInt id, const String& inputEphemTableName, const String& comment); // Remove (delete) any ephemeris tables with given id (without changes to // the EPHEMERIS_ID column). // Return False in case of errors (but True if the id didn't exist). Bool removeEphemeris(const uInt id); // Initialize the statics appropriately. This does not need to be // called by users, it is called by the implementation class // MSTableImpl. static MSTableMaps initMaps(); private: // required by the need to throw an exception in the destructor Bool hasBeenDestroyed_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSFieldColumns.cc000066400000000000000000000372241476623553700222320ustar00rootroot00000000000000//# MSFieldColumns.cc: provides easy access to MeasurementSet columns //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSFieldColumns::MSFieldColumns() { } MSFieldColumns::MSFieldColumns(const MSField& msField) { attach(msField); } MSFieldColumns::~MSFieldColumns() { // EPHEM for (size_t i=0; i& arrDir, Int numPoly, Double interTime, Double timeOrigin) { Vector vecDir(arrDir); if ((numPoly == 0) || interTime<1 || nearAbs(interTime, timeOrigin)) { return vecDir(0); } else { Vector dir(vecDir(0).getAngle().getValue()), tmp; Double dt = interTime - timeOrigin; Double fac = 1; for (Int i=1; i<(numPoly+1); i++) { fac *= dt; tmp = vecDir(i).getAngle().getValue(); tmp *= fac; dir += tmp; } return MDirection(MVDirection(dir),vecDir(0).getRef()); } } void MSFieldColumns::setEpochRef(MEpoch::Types ref, Bool tableMustBeEmpty) { timeMeas_p.setDescRefCode(ref, tableMustBeEmpty); } void MSFieldColumns::setDirectionRef(MDirection::Types ref) { delayDirMeas_p.setDescRefCode(ref); phaseDirMeas_p.setDescRefCode(ref); referenceDirMeas_p.setDescRefCode(ref); } MDirection MSFieldColumns::delayDirMeas(rownr_t row, Double interTime) const { Int npoly = numPoly()(row); if(npoly>0){ return MSFieldColumns::interpolateDirMeas(delayDirMeasCol()(row), npoly, interTime, time()(row)); } else{ Vector vecDir(delayDirMeasCol()(row)); return extractDirMeas(vecDir(0), measCometIndex(row), interTime, timeMeas()(row)); } } MDirection MSFieldColumns::phaseDirMeas(rownr_t row, Double interTime) const { Int npoly = numPoly()(row); if(npoly>0){ return MSFieldColumns::interpolateDirMeas(phaseDirMeasCol()(row), npoly, interTime, time()(row)); } else{ Vector vecDir(phaseDirMeasCol()(row)); return extractDirMeas(vecDir(0), measCometIndex(row), interTime, timeMeas()(row)); } } MDirection MSFieldColumns::referenceDirMeas(rownr_t row, Double interTime) const { Int npoly = numPoly()(row); if(npoly>0){ return MSFieldColumns::interpolateDirMeas(referenceDirMeasCol()(row), npoly, interTime, time()(row)); } else{ Vector vecDir(referenceDirMeasCol()(row)); return extractDirMeas(vecDir(0), measCometIndex(row), interTime, timeMeas()(row)); } } MDirection MSFieldColumns::ephemerisDirMeas(rownr_t row, Double interTime) const { if(measCometIndex(row)>=0){ const MDirection zeroDir = MDirection(Quantity(0, "deg"), Quantity(0, "deg")); return extractDirMeas(zeroDir, measCometIndex(row), interTime, timeMeas()(row)); } else{ return referenceDirMeas(row, interTime); } } MRadialVelocity MSFieldColumns::radVelMeas(rownr_t row, Double interTime) const { MRadialVelocity rval; if( measCometsV_p.size()>0 ){ Int index = measCometIndex(row); if(index>=0){ Double originMJD, interMJD; getMJDs(originMJD, interMJD, interTime, timeMeas()(row)); MVRadialVelocity mvradvel; if(!measCometsV_p(index)->getRadVel(mvradvel, interMJD)){ stringstream ss; ss << "MSFieldColumns::radVelMeas(...) - No valid ephemeris entry for MJD " << setprecision(11) << interMJD << " for field " << row; throw(AipsError(ss.str())); } MRadialVelocity::Types mType = MRadialVelocity::TOPO; switch(measCometsV_p(index)->getType()){ case MDirection::TOPO: break; case MDirection::APP: default: mType = MRadialVelocity::GEO; break; } return MRadialVelocity(mvradvel, mType); } } return rval; } Quantity MSFieldColumns::rho(rownr_t row, Double interTime) const { Quantity rval(0.,"m"); if( measCometsV_p.size()>0 ){ Int index = measCometIndex(row); if(index>=0){ Double originMJD, interMJD; getMJDs(originMJD, interMJD, interTime, timeMeas()(row)); MVPosition mvpos; if(!measCometsV_p(index)->get(mvpos, interMJD)){ stringstream ss; ss << "MSFieldColumns::rho(...) - No valid ephemeris entry for MJD " << setprecision(11) << interMJD << " for field " << row; throw(AipsError(ss.str())); } rval = Quantity(mvpos.get()(0), "m"); } } return rval; } Bool MSFieldColumns::needInterTime(rownr_t row) const { if( ( measCometsV_p.size()>0 && ephemerisId()(row)>=0 ) || (numPoly()(row)>0) ){ return True; } return False; } Int MSFieldColumns::measCometIndex(rownr_t row) const { Int rval = -1; if( measCometsV_p.size()>0 ){ Int ephId = ephemerisId()(row); if(ephId>=0 && ephIdToMeasComet_p.find(ephId) != ephIdToMeasComet_p.end()){ rval = ephIdToMeasComet_p.at(ephId); } } return rval; } String MSFieldColumns::ephemPath(rownr_t row) const { String rval = ""; Int index = measCometIndex(row); if( index>=0 ){ rval = measCometsV_p(index)->getTablePath(); } return rval; } Bool MSFieldColumns:: matchReferenceDir(rownr_t row, const MVDirection& dirVal, const Double& sepInRad, MVDirection& mvdir, Double time) const { try{ mvdir = referenceDirMeas(row, time).getAngle(); } catch(std::exception& x){ return False; } if (dirVal.separation(mvdir) < sepInRad) { return True; } else { return False; } } Bool MSFieldColumns:: matchDelayDir(rownr_t row, const MVDirection& dirVal, const Double& sepInRad, MVDirection& mvdir, Double time) const { try{ mvdir = delayDirMeas(row, time).getAngle(); } catch(std::exception& x){ return False; } if (dirVal.separation(mvdir) < sepInRad) { return True; } else { return False; } } Bool MSFieldColumns:: matchPhaseDir(rownr_t row, const MVDirection& dirVal, const Double& sepInRad, MVDirection& mvdir, Double time) const { try{ mvdir = phaseDirMeas(row, time).getAngle(); } catch(std::exception& x){ return False; } if (dirVal.separation(mvdir) < sepInRad) { return True; } else { return False; } } Int64 MSFieldColumns::matchDirection(const MDirection& referenceDirection, const MDirection& delayDirection, const MDirection& phaseDirection, const Quantum& maxSeparation, Int64 tryRow, Double time) { rownr_t r = nrow(); if (r == 0) return -1; const MVDirection& referenceDirVal = referenceDirection.getValue(); const MVDirection& delayDirVal = delayDirection.getValue(); const MVDirection& phaseDirVal = phaseDirection.getValue(); // Convert the maximum separation to radians const Unit rad("rad"); DebugAssert(maxSeparation.check(UnitVal::ANGLE), AipsError); const Double tolInRad = maxSeparation.getValue(rad); // Main matching loop MVDirection mvdir; if (tryRow >= 0) { const rownr_t tr = tryRow; if (tr >= r) { throw(AipsError("MSFieldColumns::matchDirection(...) - " "row " + String::toString(tr) + " you suggest is too big")); } if (!flagRow()(tr) && numPoly()(tr) == 0){ // Get the reference frame const MDirection::Types refType = MDirection::castType(referenceDirMeas(tr).getRef().getType()); // for a solar system object only the frame has to match if((refType>=MDirection::MERCURY && refType 0) { r--; if (!flagRow()(r) && numPoly()(r) == 0){ // Get the reference frame const MDirection::Types refType = MDirection::castType(referenceDirMeas(r).getRef().getType()); // for a solar system object only the frame has to match if((refType>=MDirection::MERCURY && refType ephId = ephemerisId_p.getColumn(); for(size_t i=0; i=0 && ephIdToMeasComet_p.find(theEphId) == ephIdToMeasComet_p.end()){ // the id is not yet in use, need to create a new MeasComet object // find the table belonging to this id Directory fieldDir(measCometsPath_p); stringstream ss; ss << theEphId; Regex ephemTableRegex (Regex::fromPattern("EPHEM"+ss.str()+"_*\\.tab")); Vector candidates = fieldDir.find(ephemTableRegex, True, False); // followSymLinks=True, recursive=False if(candidates.size()==0){ throw(AipsError("Ephemeris table "+ephemTableRegex.regexp()+" not found in "+measCometsPath_p)); } String ephemTablePath = measCometsPath_p+"/"+candidates(0); if(!Table::isReadable(ephemTablePath)){ throw(AipsError("Ephemeris table "+ephemTablePath+" is not readable.")); } // create the new MeasComet object and store pointer to it in measCometsV_p MeasComet* mC = new MeasComet(ephemTablePath); size_t nMeasCom = measCometsV_p.size(); measCometsV_p.resize(nMeasCom+1, True); measCometsV_p(nMeasCom) = mC; // remember the connection ephId to the measCometsV_p index ephIdToMeasComet_p.insert(std::make_pair(theEphId, nMeasCom)); //cout << "Found and successfully opened ephemeris table " << ephemTablePath << endl; } } } MDirection MSFieldColumns::extractDirMeas(const MDirection& offsetDir, Int index, Double& interTime, MEpoch originEpoch) const { // this method is only called if numpoly==0 if(index<0){ // no ephemeris available return offsetDir; } else{ Double originMJD, interMJD; getMJDs(originMJD, interMJD, interTime, originEpoch); MVPosition xmvpos; if(!measCometsV_p(index)->get(xmvpos, interMJD)){ stringstream ss; ss << "MSFieldColumns::extractDirMeas(...) - No valid ephemeris entry for MJD " << setprecision(11) << interMJD << " in ephemeris " << measCometsV_p(index)->getTablePath(); throw(AipsError(ss.str())); } MVDirection mvxdir(xmvpos.getAngle()); MVDirection mvodir(offsetDir.getAngle()); mvxdir.shift(offsetDir.getAngle(), True); // shift in true angle, i.e. correcting for DEC return MDirection(mvxdir, measCometsV_p(index)->getType()); } } void MSFieldColumns::getMJDs(Double& originMJD, Double& interMJD, const Double interTime, const MEpoch originEpoch) const { // assume the same time reference frame of originEpoch and interTime MEpoch::Types assumedType = MEpoch::castType(originEpoch.getRef().getType()); Unit days("d"); if(assumedType==MEpoch::UTC){ originMJD = originEpoch.get(days).getValue(); interMJD = interTime/86400.; } else{ originMJD= MEpoch::Convert(originEpoch, MEpoch::UTC)().get(days).getValue(); MEpoch interEpoch(Quantity(interTime, "s"), assumedType); interMJD = MEpoch::Convert(interEpoch, MEpoch::UTC)().get(days).getValue(); } if(interTime==0.){ interMJD = originMJD; } } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSFieldColumns.h000066400000000000000000000313121476623553700220640ustar00rootroot00000000000000//# MSFieldColumns.h: provides easy access to MSField columns //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSFIELDCOLUMNS_H #define MS_MSFIELDCOLUMNS_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class MVDirection; class MSField; template class Quantum; // // A class to provide easy access to MSField columns // // // // // //
      • MSField //
      • ArrayColumn //
      • ScalarColumn // // // // MSFieldColumns stands for MeasurementSet Field Table columns. // // // // This class provides access to the columns in the MSField Table, // it does the declaration of all the Scalar and ArrayColumns with the // correct types, so the application programmer doesn't have to // worry about getting those right. There is an access function // for every predefined column. Access to non-predefined columns will still // have to be done with explicit declarations. // See MSColumns for an example. // // // // See MSColumns for the motivation. // class MSFieldColumns { public: // Construct from the supplied Table MSFieldColumns(const MSField& msField); // The desctructor does nothing special ~MSFieldColumns(); // Access to required columns // // Note that the direction measures with a stored polynomial have Col() added // to their name. They are better accessed via the functions that have the // same name, without the Col suffix, that will do the interpolation for // you. // ScalarColumn& code() {return code_p;} ArrayColumn& delayDir() {return delayDir_p;} ArrayMeasColumn& delayDirMeasCol() {return delayDirMeas_p;} ScalarColumn& flagRow() {return flagRow_p;} ScalarColumn& name() {return name_p;} ScalarColumn& numPoly() {return numPoly_p;} ArrayColumn& phaseDir() {return phaseDir_p;} ArrayMeasColumn& phaseDirMeasCol() {return phaseDirMeas_p;} ArrayColumn& referenceDir() {return referenceDir_p;} ArrayMeasColumn& referenceDirMeasCol() {return referenceDirMeas_p;} ScalarColumn& sourceId() {return sourceId_p;} ScalarColumn& time() {return time_p;} ScalarQuantColumn& timeQuant() { return timeQuant_p;} ScalarMeasColumn& timeMeas() { return timeMeas_p;} // // Const access to required columns // const ScalarColumn& code() const {return code_p;} const ArrayColumn& delayDir() const {return delayDir_p;} const ArrayMeasColumn& delayDirMeasCol() const {return delayDirMeas_p;} const ScalarColumn& flagRow() const {return flagRow_p;} const ScalarColumn& name() const {return name_p;} const ScalarColumn& numPoly() const {return numPoly_p;} const ArrayColumn& phaseDir() const {return phaseDir_p;} const ArrayMeasColumn& phaseDirMeasCol() const {return phaseDirMeas_p;} const ArrayColumn& referenceDir() const {return referenceDir_p;} const ArrayMeasColumn& referenceDirMeasCol() const {return referenceDirMeas_p;} const ScalarColumn& sourceId() const {return sourceId_p;} const ScalarColumn& time() const {return time_p;} const ScalarQuantColumn& timeQuant() const { return timeQuant_p;} const ScalarMeasColumn& timeMeas() const { return timeMeas_p;} // // Access to optional columns // ScalarColumn& ephemerisId() {return ephemerisId_p;} // // Const access to optional columns // const ScalarColumn& ephemerisId() const {return ephemerisId_p;} // // Interpolate the direction Measure polynomial static MDirection interpolateDirMeas(const Array& arrDir, Int numPoly, Double interTime, Double timeOrigin); // set the epoch reference type for the TIME column. // // In principle this function can only be used if the table is empty, // otherwise already written values may thereafter have an incorrect // reference, offset, or unit. However, it is possible that part of the // table gets written before these values are known. In that case the // reference, offset, or units can be set by using a False // tableMustBeEmpty argument. // void setEpochRef(MEpoch::Types ref, Bool tableMustBeEmpty=True); // set the direction reference type for the REFERENCE_DIR, DELAY_DIR & // PHASE_DIR columns. This can only be done when the table has no // rows. Trying to do so at other times will throw an exception. void setDirectionRef(MDirection::Types ref); // Access to interpolated directions from polynomials or ephemerides, // the default time of zero will return the 0th order element of the polynomial. // or, if there is an ephemeris, the position at the time origin of the ephemeris. // // If there is an ephemeris attached to a field table row, the nominal values of the // direction columns are interpreted as an offset to the ephemeris. So if there is // an ephemeris attached (EPHEMERIS_ID column contains value > -1), then the direction // returned by delayDirMeas, phaseDirMeas, and referenceDirMeas is the ephemeris // direction plus the offset taken from the corresponding direction column. // This permits the convinient implementation of mosaics where several field table // rows share one ephemeris and use different offsets in each row to create the // mosaic pattern. // // The unaltered ephemeris direction can be queried with the method ephemerisDirMeas(). // If there is no ephemeris attached, ephemerisDirMeas() will return the same as // referenceDirMeas(). // // In addtion to the directions, if there is an ephemeris available, // also the radial velocity and the distance rho can be accessed. // // The method needInterTime returns True if there is a polynomial or ephemeris // connected to this field table row, and an interpolation time value should // be provided. // The method ephemPath returns the absolute path to the ephemeris table connected to // the field table row, an empty string if there is none. // MDirection delayDirMeas(rownr_t row, Double time = 0) const; MDirection phaseDirMeas(rownr_t row, Double time = 0) const; MDirection referenceDirMeas(rownr_t row, Double time = 0) const; MDirection ephemerisDirMeas(rownr_t row, Double time = 0) const; MRadialVelocity radVelMeas(rownr_t row, Double time = 0) const; Quantity rho(rownr_t row, Double time = 0) const; Bool needInterTime(rownr_t row) const; String ephemPath(rownr_t row) const; // // Convenience function that returns the number of rows in any of the columns rownr_t nrow() const {return name_p.nrow();} // returns the last row that has a reference direction, phase direction and // delay direction that match, to within the specified angular separation, // the supplied values. Only matches on rows where the direction is constant // ie., NUM_POLY is 0 and where FLAG_ROW is False. Throws an exception // (AipsError) if the reference frames do not match or if the separation does // not have angular units (when compiled in debug mode). Returns -1 if no // match could be found. If tryRow is positive, then that row is tested to // see if it matches before any others are tested. Setting tryRow to a // positive value greater than the table length will throw an exception // (AipsError), when compiled in debug mode. Int64 matchDirection(const MDirection& referenceDirection, const MDirection& delayDirection, const MDirection& phaseDirection, const Quantum& maxSeparation, Int64 tryRow=-1, Double time=0); // Update the MeasComets objects belonging to this FIELD table. // Needed when the entries in the EPHEMERIS_ID column have changed. void updateMeasComets(); protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. MSFieldColumns(); //# attach this object to the supplied table. void attach(const MSField& msField); private: //# Make the assignment operator and the copy constructor private to prevent //# any compiler generated one from being used. MSFieldColumns(const MSFieldColumns&); MSFieldColumns& operator=(const MSFieldColumns&); //# Check if any optional columns exist and if so attach them. //# Initialise the necessary MeasComet objects if the EPHEMERIS_ID column is present. void attachOptionalCols(const MSField& msField); //# Functions which check the supplied values against the relevant column and //# the specified row. The row must have a numpoly value of zero. The mvdir //# argument is a temporary that is passed in to prevent it from being //# created inside these small functions. // Bool matchReferenceDir(rownr_t row, const MVDirection& dirVal, const Double& sepInRad, MVDirection& mvdir, Double time=0) const; Bool matchDelayDir(rownr_t row, const MVDirection& dirVal, const Double& sepInRad, MVDirection& mvdir, Double time=0) const; Bool matchPhaseDir(rownr_t row, const MVDirection& dirVal, const Double& sepInRad, MVDirection& mvdir, Double time=0) const; // Int measCometIndex(rownr_t row) const; // Extract the direction Measure from the corresponding ephemeris // using the nominal position as an offset. // Note that interTime is assumed to use the same time reference frame // as originEpoch. MDirection extractDirMeas(const MDirection& offsetDir, Int index, Double& interTime, MEpoch originEpoch) const; void getMJDs(Double& originMJD, Double& interMJD, const Double interTime, const MEpoch originEpoch) const; //# MeasComet data String measCometsPath_p; Vector measCometsV_p; std::map ephIdToMeasComet_p; //# required columns ScalarColumn name_p; ScalarColumn code_p; ScalarColumn time_p; ScalarColumn numPoly_p; ArrayColumn delayDir_p; ArrayColumn phaseDir_p; ArrayColumn referenceDir_p; ScalarColumn sourceId_p; ScalarColumn flagRow_p; //# optional columns ScalarColumn ephemerisId_p; //# Access to Measure columns ScalarMeasColumn timeMeas_p; ArrayMeasColumn delayDirMeas_p; ArrayMeasColumn phaseDirMeas_p; ArrayMeasColumn referenceDirMeas_p; //# Access to Quantum columns ScalarQuantColumn timeQuant_p; }; //# Define the RO version for backward compatibility. typedef MSFieldColumns ROMSFieldColumns; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSFieldEnums.h000066400000000000000000000075671476623553700215520ustar00rootroot00000000000000//# MSFieldEnums.h: Definitions for the MeasurementSet FIELD table //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSFIELDENUMS_H #define MS_MSFIELDENUMS_H #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Enums for the MeasurementSet FIELD table // // // // This class contains the enums for the MeasurementSet FIELD table // // // This class does nothing. It is merely a container for the enumerations // used by the MSField class. These enumerations define the // standard columns and keywords. // // // See the documentation for MeasurementSet for examples on the use of these // enumerations. // // // All the todo items which may be related to this class are // grouped with the todo items for MeasurementSet // // class MSFieldEnums { public: // The FIELD table colums with predefined meaning. // Keys: FIELD_ID, SOURCE_ID enum PredefinedColumns { // "True" columns are defined.
        // TYPE - UNIT - MEASURE UNDEFINED_COLUMN=0, // Special characteristics of field, e.g. Bandpass calibrator. // We need to define a standard set that can be used for // automated data reduction.
        // String. CODE, // Direction of delay center (e.g. RA, DEC) as polynomial in time.
        // Double(2,NUM_POLY+1) - rad - DIRECTION. DELAY_DIR, // Flag for this row
        // Bool FLAG_ROW, // Field Name.
        // String NAME, // Polynomial order for *_DIR columns
        // Int NUM_POLY, // Direction of phase center (e.g. RA, DEC) as polynomial in time
        // Double(2,NUM_POLY+1) - rad - DIRECTION. PHASE_DIR, // Direction of reference center (e.g. RA, DEC).
        // Double(2,NUM_POLY+1) - rad - DIRECTION. REFERENCE_DIR, // Source id (index in SOURCE table)
        // Int SOURCE_ID, // Time origin for the directions and rates.
        // Double - s - EPOCH TIME, // Number of required columns NUMBER_REQUIRED_COLUMNS=TIME, // Ephemeris id, pointer to EPHEMERIS table (for moving objects, with // possible ephemeris updates)
        // Int EPHEMERIS_ID, // Not a column, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_COLUMNS=EPHEMERIS_ID }; // Keywords with a predefined meaning enum PredefinedKeywords { // // "True" keywords are defined. UNDEFINED_KEYWORD=0, // // Not a keyword, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_KEYWORDS=0 }; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSFlagCmd.cc000066400000000000000000000134061476623553700211370ustar00rootroot00000000000000//# MSFlagCmd.cc: The MeasurementSet FLAG_CMD Table //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSFlagCmd::MSFlagCmd():hasBeenDestroyed_p(True) { } MSFlagCmd::MSFlagCmd(const String &tableName, TableOption option) : MSTable(tableName, option), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSFlagCmd(String &, TableOption) - " "table is not a valid MSFlagCmd")); } MSFlagCmd::MSFlagCmd(const String& tableName, const String &tableDescName, TableOption option) : MSTable(tableName, tableDescName,option), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSFlagCmd(String &, String &, TableOption) - " "table is not a valid MSFlagCmd")); } MSFlagCmd::MSFlagCmd(SetupNewTable &newTab, rownr_t nrrow, Bool initialize) : MSTable(newTab, nrrow, initialize), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSFlagCmd(SetupNewTable &, uInt, Bool) - " "table is not a valid MSFlagCmd")); } MSFlagCmd::MSFlagCmd(const Table &table) : MSTable(table), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSFlagCmd(const Table &) - " "table is not a valid MSFlagCmd")); } MSFlagCmd::MSFlagCmd(const MSFlagCmd &other) : MSTable(other), hasBeenDestroyed_p(False) { // verify that other is valid if (&other != this) if (! validate(this->tableDesc())) throw (AipsError("MSFlagCmd(const MSFlagCmd &) - " "table is not a valid MSFlagCmd")); } MSFlagCmd::~MSFlagCmd() { // check to make sure that this MSFlagCmd is still valid if (!hasBeenDestroyed_p && !validate()) { // the table is otherwise OK, so ensure that it is written if necessary this->flush(); LogIO os; os << LogIO::WARN << "~MSFlagCmd() - Table written is not a valid MSFlagCmd" << LogIO::POST; } hasBeenDestroyed_p = True; } MSFlagCmd& MSFlagCmd::operator=(const MSFlagCmd &other) { if (&other != this) { MSTable::operator=(other); hasBeenDestroyed_p=other.hasBeenDestroyed_p; } return *this; } MSTableMaps MSFlagCmd::initMaps() { MSTableMaps maps; // the PredefinedColumns // APPLIED colMapDef(maps, APPLIED, "APPLIED", TpBool, "True if flag has been applied to main table","",""); // COMMAND colMapDef(maps, COMMAND, "COMMAND", TpString, "Flagging command","",""); // INTERVAL colMapDef(maps, INTERVAL,"INTERVAL", TpDouble, "Time interval for which this flag is valid","s",""); // LEVEL colMapDef(maps, LEVEL, "LEVEL", TpInt, "Flag level - revision level ","",""); // REASON colMapDef(maps, REASON, "REASON", TpString, "Flag reason","",""); // SEVERITY colMapDef(maps, SEVERITY, "SEVERITY", TpInt, "Severity code (0-10) ","",""); // TIME colMapDef(maps, TIME, "TIME", TpDouble, "Midpoint of interval for which this flag is valid", "s","Epoch"); // TYPE colMapDef(maps, TYPE, "TYPE", TpString, "Type of flag (FLAG or UNFLAG)","",""); // PredefinedKeywords // init requiredTableDesc // all required keywords uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(maps, PredefinedKeywords(i)); } // all required columns // Now define all other columns (duplicates are skipped) for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(maps, PredefinedColumns(i)); } return maps; } MSFlagCmd MSFlagCmd::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSFlagCmd(MSTable:: referenceCopy(newTableName,writableColumns)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSFlagCmd.h000066400000000000000000000114421476623553700207770ustar00rootroot00000000000000//# MSFlagCmd.h: The MeasurementSet FLAG_CMD Table //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSFLAGCMD_H #define MS_MSFLAGCMD_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A Table intended to hold a MeasurementSet FLAG_CMD table. // // // // //
      • MeasurementSet //
      • MSTable // // // // MSFlagCmd stands for the MeasurementSet FLAG_CMD table. // // // // An MSFlagCmd is a table intended to hold the FLAG_CMD table for // the MeasurementSet. It has an identical set of member functions as // the main MeasurementSet class, except (currently) for the default // calibration members. For further info and examples see the // MeasurementSet class. // // // // See the MeasurementSet for an example of how to access and use this class. // // // // It was found that subtables and the main table of the MeasurementSet have // a lot in common, therefore they derive their interface from the same // base class. Each subtable has its own class to keep the enum definitions // and conversion functions in separate scopes. // // // // see MeasurementSet. // class MSFlagCmd:public MSFlagCmdEnums, public MSTable { public: // This constructs an empty MSFlagCmd. MSFlagCmd (); // These constructors mirror the Table ones with additional checking // on validity (verifying that the MSFlagCmd will have the required columns // and keywords) // An exception is thrown if the constructed Table is not a valid MSFlagCmd // //
      • AipsError // // MSFlagCmd (const String &tableName, TableOption = Table::Old); MSFlagCmd (const String &tableName, const String &tableDescName, TableOption = Table::Old); MSFlagCmd (SetupNewTable &newTab, rownr_t nrrow = 0, Bool initialize = False); MSFlagCmd (const Table &table); MSFlagCmd (const MSFlagCmd &other); // // As with tables, the destructor writes the table if necessary. // Additional checking is done here to verify that all required // columns are still present. // If it is NOT valid, it will write the table and then throw an exception. // //
      • AipsError // ~MSFlagCmd(); // Assignment operator, reference semantics MSFlagCmd& operator=(const MSFlagCmd&); // Make a special copy of this Table which references all columns from // this Table except those mentioned; those are empty and writable. // Each forwarded column has the same writable status as the underlying // column. The mentioned columns all use the AipsIO storage manager. // This function is inherited from MSTable and unlikely to be of use, // except in the class MeasurementSet (see comment there).. MSFlagCmd referenceCopy(const String& newTableName, const Block& writableColumns) const; // Initialize the statics appropriately. This does not need to be // called by users, it is called by the implementation class // MSTableImpl. static MSTableMaps initMaps(); private: // required by the need to throw an exception in the destructor Bool hasBeenDestroyed_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSFlagCmdColumns.cc000066400000000000000000000051111476623553700224720ustar00rootroot00000000000000//# MSFlagCmdColumns.cc: provides easy access to MeasurementSet columns //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSFlagCmdColumns::MSFlagCmdColumns() { } MSFlagCmdColumns::MSFlagCmdColumns(const MSFlagCmd& msFlagCmd) { attach(msFlagCmd); } MSFlagCmdColumns::~MSFlagCmdColumns() {} void MSFlagCmdColumns::attach(const MSFlagCmd& msFlagCmd) { applied_p.attach(msFlagCmd, MSFlagCmd::columnName(MSFlagCmd::APPLIED)); command_p.attach(msFlagCmd, MSFlagCmd::columnName(MSFlagCmd::COMMAND)); interval_p.attach(msFlagCmd, MSFlagCmd:: columnName(MSFlagCmd::INTERVAL)); level_p.attach(msFlagCmd, MSFlagCmd::columnName(MSFlagCmd::LEVEL)); reason_p.attach(msFlagCmd, MSFlagCmd::columnName(MSFlagCmd::REASON)); severity_p.attach(msFlagCmd, MSFlagCmd:: columnName(MSFlagCmd::SEVERITY)); time_p.attach(msFlagCmd, MSFlagCmd::columnName(MSFlagCmd::TIME)); type_p.attach(msFlagCmd, MSFlagCmd::columnName(MSFlagCmd::TYPE)); timeMeas_p.attach(msFlagCmd, MSFlagCmd::columnName(MSFlagCmd::TIME)); intervalQuant_p.attach(msFlagCmd, MSFlagCmd:: columnName(MSFlagCmd::INTERVAL)); timeQuant_p.attach(msFlagCmd, MSFlagCmd::columnName(MSFlagCmd::TIME)); } void MSFlagCmdColumns::setEpochRef(MEpoch::Types ref, Bool tableMustBeEmpty){ timeMeas_p.setDescRefCode(ref, tableMustBeEmpty); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSFlagCmdColumns.h000066400000000000000000000137761476623553700223540ustar00rootroot00000000000000//# MSFlagCmdColumns.h: provides easy access to MSFlagCmd columns //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSFLAGCMDCOLUMNS_H #define MS_MSFLAGCMDCOLUMNS_H #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class MSFlagCmd; // // A class to provide easy access to MSFlagCmd columns // // // // // //
      • MSFlagCmd //
      • ScalarColumn // // // // MSFlagCmdColumns stands for MeasurementSet FlagCmd Table columns. // // // // This class provides access to the columns in the MSFlagCmd Table, it does // the declaration of all the ScalarColumns with the correct types, so the // application programmer doesn't have to worry about getting those // right. There is an access function for every predefined column. Access to // non-predefined columns will still have to be done with explicit // declarations. See MSColumns for an // example. // // // // See MSColumns for the motivation. // class MSFlagCmdColumns { public: // Create a columns object that accesses the data in the specified Table MSFlagCmdColumns(const MSFlagCmd& msFlagCmd); // The destructor does nothing special ~MSFlagCmdColumns(); // Access to required columns // ScalarColumn& applied() {return applied_p;} ScalarColumn& command() {return command_p;} ScalarColumn& interval() {return interval_p;} ScalarQuantColumn& intervalQuant() {return intervalQuant_p;} ScalarColumn& level() {return level_p;} ScalarColumn& reason() {return reason_p;} ScalarColumn& severity() {return severity_p;} ScalarColumn& time() {return time_p;} ScalarQuantColumn& timeQuant() {return timeQuant_p;} ScalarMeasColumn& timeMeas() {return timeMeas_p;} ScalarColumn& type() {return type_p;} // // Const access to required columns // const ScalarColumn& applied() const {return applied_p;} const ScalarColumn& command() const {return command_p;} const ScalarColumn& interval() const {return interval_p;} const ScalarQuantColumn& intervalQuant() const {return intervalQuant_p;} const ScalarColumn& level() const {return level_p;} const ScalarColumn& reason() const {return reason_p;} const ScalarColumn& severity() const {return severity_p;} const ScalarColumn& time() const {return time_p;} const ScalarQuantColumn& timeQuant() const {return timeQuant_p;} const ScalarMeasColumn& timeMeas() const {return timeMeas_p;} const ScalarColumn& type() const {return type_p;} // // Convenience function that returns the number of rows in any of the columns rownr_t nrow() const {return applied_p.nrow();} // set the epoch type for the FLAG_CMD column. // // In principle this function can only be used if the table is empty, // otherwise already written values may thereafter have an incorrect // reference, offset, or unit. However, it is possible that part of the // table gets written before these values are known. In that case the // reference, offset, or units can be set by using a False // tableMustBeEmpty argument. // void setEpochRef(MEpoch::Types ref, Bool tableMustBeEmpty=True); protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. MSFlagCmdColumns(); //# attach this object to the supplied table. void attach(const MSFlagCmd& msFlagCmd); private: //# Make the assignment operator and the copy constructor private to prevent //# any compiler generated one from being used. MSFlagCmdColumns(const MSFlagCmdColumns&); MSFlagCmdColumns& operator=(const MSFlagCmdColumns&); //# required columns ScalarColumn applied_p; ScalarColumn command_p; ScalarColumn interval_p; ScalarColumn level_p; ScalarColumn reason_p; ScalarColumn severity_p; ScalarColumn time_p; ScalarColumn type_p; //# Access to Measure columns ScalarMeasColumn timeMeas_p; //# Access to Quantum columns ScalarQuantColumn intervalQuant_p; ScalarQuantColumn timeQuant_p; }; //# Define the RO version for backward compatibility. typedef MSFlagCmdColumns ROMSFlagCmdColumns; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSFlagCmdEnums.h000066400000000000000000000065061476623553700220140ustar00rootroot00000000000000//# MSFlagCmdEnums.h: Defs for the MS FLAG_CMD table //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSFLAGCMDENUMS_H #define MS_MSFLAGCMDENUMS_H #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Enums for the MeasurementSet FLAG_CMD table // // // // This class contains the enums for the MeasurementSet FLAG_CMD table // // // This class does nothing. It is merely a container for the enumerations // used by the MSFlagCmd class. These enumerations define the // standard columns, keywords. // // // See the documentation for MeasurementSet for examples on the use of these // enumerations. // // // All the todo items which may be related to this class are // grouped with the todo items for MeasurementSet // // class MSFlagCmdEnums { public: // The FLAG_CMD table colums with predefined meaning. enum PredefinedColumns { // "True" columns are defined.
        // TYPE - UNIT - MEASURE UNDEFINED_COLUMN=0, // applied flag - true if flag has been applied to main table
        // Bool APPLIED, // flag command
        // String COMMAND, // Time interval
        // Double INTERVAL, // Flag level - revision
        // Int LEVEL, // Flag reason, user specified
        // String REASON, // severity code (0-10)
        // Int SEVERITY, // Midpoint of time interval for which this flag command applies
        // Double TIME, // Flag type: FLAG or UNFLAG
        // String TYPE, // Number of required columns NUMBER_REQUIRED_COLUMNS=TYPE, // Not a column, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_COLUMNS=NUMBER_REQUIRED_COLUMNS }; // Keywords with a predefined meaning enum PredefinedKeywords { // // "True" keywords are defined. UNDEFINED_KEYWORD=0, // // Not a keyword, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_KEYWORDS=0 }; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSFreqOffColumns.cc000066400000000000000000000056701476623553700225370ustar00rootroot00000000000000//# MSFreqOffsetColumns.cc: provides easy access to MeasurementSet columns //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSFreqOffsetColumns::MSFreqOffsetColumns(): isNull_p(True) { } MSFreqOffsetColumns::MSFreqOffsetColumns(const MSFreqOffset& msFreqOffset): isNull_p(True) { attach(msFreqOffset); } MSFreqOffsetColumns::~MSFreqOffsetColumns() {} void MSFreqOffsetColumns::attach(const MSFreqOffset& msFreqOffset) { isNull_p = msFreqOffset.isNull(); if (!isNull()) { antenna1_p.attach(msFreqOffset, MSFreqOffset:: columnName(MSFreqOffset::ANTENNA1)); antenna2_p.attach(msFreqOffset, MSFreqOffset:: columnName(MSFreqOffset::ANTENNA2)); feedId_p.attach(msFreqOffset, MSFreqOffset:: columnName(MSFreqOffset::FEED_ID)); interval_p.attach(msFreqOffset, MSFreqOffset:: columnName(MSFreqOffset::INTERVAL)); offset_p.attach(msFreqOffset, MSFreqOffset:: columnName(MSFreqOffset::OFFSET)); spectralWindowId_p.attach(msFreqOffset, MSFreqOffset:: columnName(MSFreqOffset::SPECTRAL_WINDOW_ID)); time_p.attach(msFreqOffset, MSFreqOffset:: columnName(MSFreqOffset::TIME)); timeMeas_p.attach(msFreqOffset, MSFreqOffset:: columnName(MSFreqOffset::TIME)); intervalQuant_p.attach(msFreqOffset, MSFreqOffset:: columnName(MSFreqOffset::INTERVAL)); offsetQuant_p.attach(msFreqOffset, MSFreqOffset:: columnName(MSFreqOffset::OFFSET)); timeQuant_p.attach(msFreqOffset, MSFreqOffset:: columnName(MSFreqOffset::TIME)); } } void MSFreqOffsetColumns:: setEpochRef(MEpoch::Types ref, Bool tableMustBeEmpty) { timeMeas_p.setDescRefCode(ref, tableMustBeEmpty); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSFreqOffColumns.h000066400000000000000000000145351476623553700224010ustar00rootroot00000000000000//# MSFreqOffsetColumns.h: provides easy access to FREQ_OFFSET columns //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSFREQOFFCOLUMNS_H #define MS_MSFREQOFFCOLUMNS_H #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class MSFreqOffset; // // A class to provide easy access to MSFreqOffset columns // // // // // //
      • MSFreqOffset //
      • ScalarColumn // // // // MSFreqOffsetColumns stands for MeasurementSet FreqOffset Table // columns. // // // // This class provides access to the columns in the MSFreqOffset Table, // it does the declaration of all the Scalar and ArrayColumns with the // correct types, so the application programmer doesn't have to // worry about getting those right. There is an access function // for every predefined column. Access to non-predefined columns will still // have to be done with explicit declarations. // See MSColumns for an example. // // // // See MSColumns for the motivation. // class MSFreqOffsetColumns { public: // Create a columns object that accesses the data in the specified Table MSFreqOffsetColumns(const MSFreqOffset& msFreqOffset); // The destructor does nothing special ~MSFreqOffsetColumns(); // Is this object defined? (MSFreqOffset table is optional) Bool isNull() const {return isNull_p;} // Access to required columns // ScalarColumn& antenna1() {return antenna1_p;} ScalarColumn& antenna2() {return antenna2_p;} ScalarColumn& feedId() {return feedId_p;} ScalarColumn& interval() {return interval_p;} ScalarQuantColumn& intervalQuant() {return intervalQuant_p;} ScalarColumn& offset() {return offset_p;} ScalarQuantColumn& offsetQuant() {return offsetQuant_p;} ScalarColumn& spectralWindowId() {return spectralWindowId_p;} ScalarColumn& time() {return time_p;} ScalarQuantColumn& timeQuant() {return timeQuant_p;} ScalarMeasColumn& timeMeas() {return timeMeas_p;} // // Const access to required columns // const ScalarColumn& antenna1() const {return antenna1_p;} const ScalarColumn& antenna2() const {return antenna2_p;} const ScalarColumn& feedId() const {return feedId_p;} const ScalarColumn& interval() const {return interval_p;} const ScalarQuantColumn& intervalQuant() const {return intervalQuant_p;} const ScalarColumn& offset() const {return offset_p;} const ScalarQuantColumn& offsetQuant() const {return offsetQuant_p;} const ScalarColumn& spectralWindowId() const {return spectralWindowId_p;} const ScalarColumn& time() const {return time_p;} const ScalarQuantColumn& timeQuant() const {return timeQuant_p;} const ScalarMeasColumn& timeMeas() const {return timeMeas_p;} // // Convenience function that returns the number of rows in any of the // columns. Returns zero if the object is null. rownr_t nrow() const {return isNull() ? 0 : antenna1_p.nrow();} // set the epoch type for the TIME column. // // In principle this function can only be used if the table is empty, // otherwise already written values may thereafter have an incorrect // reference, offset, or unit. However, it is possible that part of the // table gets written before these values are known. In that case the // reference, offset, or units can be set by using a False // tableMustBeEmpty argument. // void setEpochRef(MEpoch::Types ref, Bool tableMustBeEmpty=True); protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. MSFreqOffsetColumns(); //# attach this object to the supplied table. void attach(const MSFreqOffset& msFreqOffset); private: //# Make the assignment operator and the copy constructor private to prevent //# any compiler generated one from being used. MSFreqOffsetColumns(const MSFreqOffsetColumns&); MSFreqOffsetColumns& operator=(const MSFreqOffsetColumns&); //# Is the object not attached to a Table. Bool isNull_p; //# required columns ScalarColumn antenna1_p; ScalarColumn antenna2_p; ScalarColumn feedId_p; ScalarColumn interval_p; ScalarColumn offset_p; ScalarColumn spectralWindowId_p; ScalarColumn time_p; //# Access to Measure columns ScalarMeasColumn timeMeas_p; //# Access to Quantum columns ScalarQuantColumn intervalQuant_p; ScalarQuantColumn offsetQuant_p; ScalarQuantColumn timeQuant_p; }; //# Define the RO version for backward compatibility. typedef MSFreqOffsetColumns ROMSFreqOffsetColumns; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSFreqOffEnums.h000066400000000000000000000063171476623553700220470ustar00rootroot00000000000000//# MSFreqOffsetEnums.h: Defs for the MS FREQ_OFFSET table //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSFREQOFFENUMS_H #define MS_MSFREQOFFENUMS_H #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Enums for the MeasurementSet FREQ_OFFSET table // // // // This class contains the enums for the MeasurementSet FREQ_OFFSET table // // // This class does nothing. It is merely a container for the enumerations // used by the MSFreqOffset class. These enumerations define the // standard columns, keywords. // // // See the documentation for MeasurementSet for examples on the use of these // enumerations. // // // All the todo items which may be related to this class are // grouped with the todo items for MeasurementSet // // class MSFreqOffsetEnums { public: // The FREQ_OFFSET table colums with predefined meaning. enum PredefinedColumns { // "True" columns are defined.
        // TYPE - UNIT - MEASURE UNDEFINED_COLUMN=0, // Antenna 1 id
        // Int ANTENNA1, // Antenna 2 id
        // Int ANTENNA2, // Feed id
        // Int FEED_ID, // Time interval
        // Double - s INTERVAL, // Frequency offset
        // Double - Hz OFFSET, // Spectral window id
        // Int SPECTRAL_WINDOW_ID, // Midpoint of time interval
        // Double - s - EPOCH TIME, // Number of required columns NUMBER_REQUIRED_COLUMNS=TIME, // Not a column, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_COLUMNS=NUMBER_REQUIRED_COLUMNS }; // Keywords with a predefined meaning enum PredefinedKeywords { // // "True" keywords are defined. UNDEFINED_KEYWORD=0, // // Not a keyword, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_KEYWORDS=0 }; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSFreqOffset.cc000066400000000000000000000133611476623553700217060ustar00rootroot00000000000000//# MSFreqOffset.cc: The MeasurementSet FREQ_OFFSET Table //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSFreqOffset::MSFreqOffset():hasBeenDestroyed_p(True) { } MSFreqOffset::MSFreqOffset(const String &tableName, TableOption option) : MSTable(tableName, option), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSFreqOffset(String &, TableOption) - " "table is not a valid MSFreqOffset")); } MSFreqOffset::MSFreqOffset(const String& tableName, const String &tableDescName, TableOption option) : MSTable(tableName, tableDescName, option), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSFreqOffset(String &, String &, TableOption) - " "table is not a valid MSFreqOffset")); } MSFreqOffset::MSFreqOffset(SetupNewTable &newTab, rownr_t nrrow, Bool initialize) : MSTable(newTab, nrrow, initialize), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSFreqOffset(SetupNewTable &, rownr_t, Bool) - " "table is not a valid MSFreqOffset")); } MSFreqOffset::MSFreqOffset(const Table &table) : MSTable(table), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSFreqOffset(const Table &) - " "table is not a valid MSFreqOffset")); } MSFreqOffset::MSFreqOffset(const MSFreqOffset &other) : MSTable(other), hasBeenDestroyed_p(False) { // verify that other is valid if (&other != this) if (! validate(this->tableDesc())) throw (AipsError("MSFreqOffset(const MSFreqOffset &) - " "table is not a valid MSFreqOffset")); } MSFreqOffset::~MSFreqOffset() { // check to make sure that this MSFreqOffset is still valid if (!hasBeenDestroyed_p && !validate()) { // the table is otherwise OK, so ensure that it is written if necessary this->flush(); LogIO os; os << LogIO::WARN << "~MSFreqOffset() - Table written is not a valid MSFreqOffset" << LogIO::POST; } hasBeenDestroyed_p = True; } MSFreqOffset& MSFreqOffset::operator=(const MSFreqOffset &other) { if (&other != this) { MSTable::operator=(other); hasBeenDestroyed_p=other.hasBeenDestroyed_p; } return *this; } MSTableMaps MSFreqOffset::initMaps() { MSTableMaps maps; // the PredefinedColumns // ANTENNA1 colMapDef(maps, ANTENNA1, "ANTENNA1", TpInt, "Antenna1 id","",""); // ANTENNA2 colMapDef(maps, ANTENNA2, "ANTENNA2", TpInt, "Antenna2 id","",""); // FEED_ID colMapDef(maps, FEED_ID, "FEED_ID", TpInt, "Feed id","",""); // INTERVAL colMapDef(maps, INTERVAL, "INTERVAL", TpDouble, "Time interval","s",""); // OFFSET colMapDef(maps, OFFSET, "OFFSET", TpDouble, "Frequency offset - antenna based","Hz",""); // SPECTRAL_WINDOW_ID colMapDef(maps, SPECTRAL_WINDOW_ID, "SPECTRAL_WINDOW_ID", TpInt, "Spectral window id","",""); // TIME colMapDef(maps, TIME, "TIME", TpDouble, "Midpoint of interval","s","Epoch"); // PredefinedKeywords // init requiredTableDesc // all required keywords uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(maps, PredefinedKeywords(i)); } // all required columns // Now define all other columns (duplicates are skipped) for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(maps, PredefinedColumns(i)); } return maps; } MSFreqOffset MSFreqOffset::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSFreqOffset(MSTable:: referenceCopy(newTableName,writableColumns)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSFreqOffset.h000066400000000000000000000115631476623553700215520ustar00rootroot00000000000000//# MSFreqOffset.h: The MeasurementSet FREQ_OFFSET Table //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSFREQOFFSET_H #define MS_MSFREQOFFSET_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A Table intended to hold a MeasurementSet FREQ_OFFSET table. // // // // //
      • MeasurementSet //
      • MSTable // // // // MSFreqOffset stands for the MeasurementSet FREQ_OFFSET table. // // // // An MSFreqOffset is a table intended to hold the FREQ_OFFSET table for // the MeasurementSet. It has an identical set of member functions as // the main MeasurementSet class, except (currently) for the default // calibration members. For further info and examples see the // MeasurementSet class. // // // // See the MeasurementSet for an example of how to access and use this class. // // // // It was found that subtables and the main table of the MeasurementSet have // a lot in common, therefore they derive their interface from the same // base class. Each subtable has its own class to keep the enum definitions // and conversion functions in separate scopes. // // // // see MeasurementSet. // class MSFreqOffset:public MSFreqOffsetEnums, public MSTable { public: // This constructs an empty MSFreqOffset. MSFreqOffset (); // These constructors mirror the Table ones with additional checking // on validity (verifying that the MSFreqOffset will have the required columns // and keywords) // An exception is thrown if the constructed Table is not a valid MSFreqOffset // //
      • AipsError // // MSFreqOffset (const String &tableName, TableOption = Table::Old); MSFreqOffset (const String &tableName, const String &tableDescName, TableOption = Table::Old); MSFreqOffset (SetupNewTable &newTab, rownr_t nrrow = 0, Bool initialize = False); MSFreqOffset (const Table &table); MSFreqOffset (const MSFreqOffset &other); // // As with tables, the destructor writes the table if necessary. // Additional checking is done here to verify that all required // columns are still present. // If it is NOT valid, it will write the table and then throw an exception. // //
      • AipsError // ~MSFreqOffset(); // Assignment operator, reference semantics MSFreqOffset& operator=(const MSFreqOffset&); // Make a special copy of this Table which references all columns from // this Table except those mentioned; those are empty and writable. // Each forwarded column has the same writable status as the underlying // column. The mentioned columns all use the AipsIO storage manager. // This function is inherited from MSTable and unlikely to be of use, // except in the class MeasurementSet (see comment there).. MSFreqOffset referenceCopy(const String& newTableName, const Block& writableColumns) const; // Initialize the statics appropriately. This does not need to be // called by users, it is called by the implementation class // MSTableImpl. static MSTableMaps initMaps(); private: // required by the need to throw an exception in the destructor Bool hasBeenDestroyed_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSHistory.cc000066400000000000000000000137171476623553700213100ustar00rootroot00000000000000//# MSHistory.cc: The MeasurementSet HISTORY Table //# Copyright (C) 1996,1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSHistory::MSHistory() : hasBeenDestroyed_p(True) {} MSHistory::MSHistory(const String &tableName, TableOption option) : MSTable(tableName, option), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSHistory(String &, TableOption) - " "table is not a valid MSHistory")); } MSHistory::MSHistory(const String& tableName, const String &tableDescName, TableOption option) : MSTable(tableName, tableDescName,option), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSHistory(String &, String &, TableOption) - " "table is not a valid MSHistory")); } MSHistory::MSHistory(SetupNewTable &newTab, rownr_t nrrow, Bool initialize) : MSTable(newTab, nrrow, initialize), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSHistory(SetupNewTable &, uInt, Bool) - " "table is not a valid MSHistory")); } MSHistory::MSHistory(const Table &table) : MSTable(table), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSHistory(const Table &) - " "table is not a valid MSHistory")); } MSHistory::MSHistory(const MSHistory &other) : MSTable(other), hasBeenDestroyed_p(False) { // verify that other is valid if (&other != this) if (! validate(this->tableDesc())) throw (AipsError("MSHistory(const MSHistory &) - " "table is not a valid MSHistory")); } MSHistory::~MSHistory() { // check to make sure that this MSHistory is still valid if (!hasBeenDestroyed_p && !validate()) { // the table is otherwise OK, so ensure that it is written if necessary this->flush(); LogIO os; os << LogIO::WARN << "~MSHistory() - Table written is not a valid MSHistory" << LogIO::POST; } hasBeenDestroyed_p = True; } MSHistory& MSHistory::operator=(const MSHistory &other) { if (&other != this) { MSTable::operator=(other); hasBeenDestroyed_p=other.hasBeenDestroyed_p; } return *this; } MSTableMaps MSHistory::initMaps() { MSTableMaps maps; // the PredefinedColumns // APPLICATION colMapDef(maps, APPLICATION,"APPLICATION",TpString, "Application name","",""); // APP_PARAMS colMapDef(maps, APP_PARAMS,"APP_PARAMS",TpArrayString, "Application parameters","",""); // CLI_COMMAND colMapDef(maps, CLI_COMMAND,"CLI_COMMAND",TpArrayString, "CLI command sequence","",""); // MESSAGE colMapDef(maps, MESSAGE,"MESSAGE",TpString, "Log message","",""); // OBJECT_ID colMapDef(maps, OBJECT_ID,"OBJECT_ID",TpInt, "Originating ObjectID","",""); // OBSERVATION_ID colMapDef(maps, OBSERVATION_ID, "OBSERVATION_ID", TpInt, "Observation id (index in OBSERVATION table)","",""); // ORIGIN colMapDef(maps, ORIGIN,"ORIGIN",TpString, "(Source code) origin from which message originated","",""); // PRIORITY colMapDef(maps, PRIORITY,"PRIORITY",TpString, "Message priority","",""); // TIME colMapDef(maps, TIME,"TIME",TpDouble, "Timestamp of message","s","Epoch"); // PredefinedKeywords // init requiredTableDesc // all required keywords uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(maps, PredefinedKeywords(i)); } // define the columns with known dimensionality addColumnToDesc(maps, APP_PARAMS, 1); addColumnToDesc(maps, CLI_COMMAND, 1); // all required columns for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(maps, PredefinedColumns(i)); } return maps; } MSHistory MSHistory::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSHistory(MSTable::referenceCopy (newTableName,writableColumns)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSHistory.h000066400000000000000000000114411476623553700211420ustar00rootroot00000000000000//# MSHistory.h: The MeasurementSet HISTORY Table //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSHISTORY_H #define MS_MSHISTORY_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A Table intended to hold a MeasurementSet OBSERVATIONLOG table. // // // // //
      • MeasurementSet //
      • MSTable // // // // MSHistory stands for the MeasurementSet ObservationLog table. // // // // An MSHistory is a table intended to hold the OBSERVATIONLOG table for // the MeasurementSet. It has an identical set of member functions as // the main MeasurementSet class, except (currently) for the default // calibration members. For further info and examples see the // MeasurementSet class. // // // // See the MeasurementSet for an example of how to access and use this class. // // // // It was found that subtables and the main table of the MeasurementSet have // a lot in common, therefore they derive their interface from the same // base class. Each subtable has its own class to keep the enum definitions // and conversion functions in separate scopes. // // // // see MeasurementSet. // class MSHistory:public MSHistoryEnums, public MSTable { public: // This constructs an empty MSHistory MSHistory (); // These constructors mirror the Table ones with additional checking // on validity (verifying that the MSHistory will have the required columns // and keywords) // An exception is thrown if the constructed Table is not a valid MSHistory // //
      • AipsError // // MSHistory (const String &tableName, TableOption = Table::Old); MSHistory (const String &tableName, const String &tableDescName, TableOption = Table::Old); MSHistory (SetupNewTable &newTab, rownr_t nrrow = 0, Bool initialize = False); MSHistory (const Table &table); MSHistory (const MSHistory &other); // // As with tables, the destructor writes the table if necessary. // Additional checking is done here to verify that all required // columns are still present. // If it is NOT valid, it will write the table and then throw an exception. // //
      • AipsError // ~MSHistory(); // Assignment operator, reference semantics MSHistory& operator=(const MSHistory&); // Make a special copy of this Table which references all columns from // this Table except those mentioned; those are empty and writable. // Each forwarded column has the same writable status as the underlying // column. The mentioned columns all use the AipsIO storage manager. // This function is inherited from MSTable and unlikely to be of use, // except in the class MeasurementSet (see comment there).. MSHistory referenceCopy(const String& newTableName, const Block& writableColumns) const; // Initialize the statics appropriately. This does not need to be // called by users, it is called by the implementation class // MSTableImpl. static MSTableMaps initMaps(); private: // required by the need to throw an exception in the destructor Bool hasBeenDestroyed_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSHistoryColumns.cc000066400000000000000000000052171476623553700226450ustar00rootroot00000000000000//# MSHistoryColumns.cc: provides easy access to MeasurementSet columns //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSHistoryColumns::MSHistoryColumns() { } MSHistoryColumns::MSHistoryColumns(const MSHistory& msHistory) { attach(msHistory); } MSHistoryColumns::~MSHistoryColumns() {} void MSHistoryColumns::attach(const MSHistory& msHistory) { application_p.attach(msHistory, MSHistory:: columnName(MSHistory::APPLICATION)); appParams_p.attach(msHistory, MSHistory:: columnName(MSHistory::APP_PARAMS)); cliCommand_p.attach(msHistory, MSHistory:: columnName(MSHistory::CLI_COMMAND)); message_p.attach(msHistory, MSHistory::columnName(MSHistory::MESSAGE)); objectId_p.attach(msHistory, MSHistory:: columnName(MSHistory::OBJECT_ID)); observationId_p.attach(msHistory, MSHistory:: columnName(MSHistory::OBSERVATION_ID)); origin_p.attach(msHistory, MSHistory::columnName(MSHistory::ORIGIN)); priority_p.attach(msHistory, MSHistory:: columnName(MSHistory::PRIORITY)); time_p.attach(msHistory, MSHistory::columnName(MSHistory::TIME)); timeMeas_p.attach(msHistory, MSHistory::columnName(MSHistory::TIME)); timeQuant_p.attach(msHistory, MSHistory::columnName(MSHistory::TIME)); } void MSHistoryColumns::setEpochRef(MEpoch::Types ref, Bool tableMustBeEmpty) { timeMeas_p.setDescRefCode(ref, tableMustBeEmpty); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSHistoryColumns.h000066400000000000000000000145441476623553700225120ustar00rootroot00000000000000//# MSHistoryColumns.h: provides easy access to MSHistory columns //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSHISTORYCOLUMNS_H #define MS_MSHISTORYCOLUMNS_H #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class MSHistory; // // A class to provide easy access to MSHistory columns // // // // // //
      • MSHistory //
      • ArrayColumn //
      • ScalarColumn // // // // MSHistoryColumns stands for MeasurementSet History Table columns. // // // // This class provides access to the columns in the MSHistory Table, // it does the declaration of all the Scalar and ArrayColumns with the // correct types, so the application programmer doesn't have to // worry about getting those right. There is an access function // for every predefined column. Access to non-predefined columns will still // have to be done with explicit declarations. // See MSColumns for an example. // // Note by GvD 28-Jan-2010: // According to note 229 the OBJECTID column should contain Strings. // It is, however, defined as Int. It has to be left as such, otherwise older // MeasurementSets cannot be read anymore. // // // // See MSColumns for the motivation. // class MSHistoryColumns { public: // Create a columns object that accesses the data in the specified Table MSHistoryColumns(const MSHistory& msHistory); // The destructor does nothing special ~MSHistoryColumns(); // Access to required columns // ScalarColumn& application() {return application_p;} ArrayColumn& appParams() {return appParams_p;} ArrayColumn& cliCommand() {return cliCommand_p;} ScalarColumn& message() {return message_p;} ScalarColumn& objectId() {return objectId_p;} ScalarColumn& observationId() {return observationId_p;} ScalarColumn& origin() {return origin_p;} ScalarColumn& priority() {return priority_p;} ScalarColumn& time() {return time_p;} ScalarQuantColumn& timeQuant() {return timeQuant_p;} ScalarMeasColumn& timeMeas() {return timeMeas_p;} // // Const access to required columns // const ScalarColumn& application() const {return application_p;} const ArrayColumn& appParams() const {return appParams_p;} const ArrayColumn& cliCommand() const {return cliCommand_p;} const ScalarColumn& message() const {return message_p;} const ScalarColumn& objectId() const {return objectId_p;} const ScalarColumn& observationId() const {return observationId_p;} const ScalarColumn& origin() const {return origin_p;} const ScalarColumn& priority() const {return priority_p;} const ScalarColumn& time() const {return time_p;} const ScalarQuantColumn& timeQuant() const {return timeQuant_p;} const ScalarMeasColumn& timeMeas() const {return timeMeas_p;} // // Convenience function that returns the number of rows in any of the columns rownr_t nrow() const {return application_p.nrow();} // set the epoch type for the TIME column. // // In principle this function can only be used if the table is empty, // otherwise already written values may thereafter have an incorrect // reference, offset, or unit. However, it is possible that part of the // table gets written before these values are known. In that case the // reference, offset, or units can be set by using a False // tableMustBeEmpty argument. // void setEpochRef(MEpoch::Types ref, Bool tableMustBeEmpty=True); protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. MSHistoryColumns(); //# attach this object to the supplied table. void attach(const MSHistory& msHistory); private: //# Make the assignment operator and the copy constructor private to prevent //# any compiler generated one from being used. MSHistoryColumns(const MSHistoryColumns&); MSHistoryColumns& operator=(const MSHistoryColumns&); //# required columns ScalarColumn application_p; ArrayColumn appParams_p; ArrayColumn cliCommand_p; ScalarColumn message_p; ScalarColumn objectId_p; ScalarColumn observationId_p; ScalarColumn origin_p; ScalarColumn priority_p; ScalarColumn time_p; //# Access to Measure columns ScalarMeasColumn timeMeas_p; //# Access to Quantum columns ScalarQuantColumn timeQuant_p; }; //# Define the RO version for backward compatibility. typedef MSHistoryColumns ROMSHistoryColumns; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSHistoryEnums.h000066400000000000000000000067231476623553700221610ustar00rootroot00000000000000//# MSHistoryEnums.h: Defns for the MeasurementSet HISTORY table //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSHISTORYENUMS_H #define MS_MSHISTORYENUMS_H #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Enums for the MeasurementSet HISTORY table // // // // This class contains the enums for the MeasurementSet HISTORY table // // // This class does nothing. It is merely a container for the enumerations // used by the MSHistory class. These enumerations define the // standard columns, keywords. // // // See the documentation for MeasurementSet for examples on the use of these // enumerations. // // // All the todo items which may be related to this class are // grouped with the todo items for MeasurementSet // // class MSHistoryEnums { public: // The HISTORY table colums with predefined meaning. // Keys: TIME, OBSERVATION_ID enum PredefinedColumns { // "True" columns are defined.
        // TYPE - UNIT - MEASURE UNDEFINED_COLUMN=0, // Application name
        // String APPLICATION, // Application parameters
        // String(*) APP_PARAMS, // CLI command sequence
        // String(*) CLI_COMMAND, // Log message
        // String MESSAGE, // Originating object ID
        // String OBJECT_ID, // Observation id (index in OBSERVATION table)
        // Int OBSERVATION_ID, // (Source code) Origin of message
        // String ORIGIN, // Priority of message: DEBUGGING, WARN, NORMAL, SEVERE
        // String PRIORITY, // Timestamp of message
        // Double - s - EPOCH TIME, // // Number of required columns NUMBER_REQUIRED_COLUMNS=TIME, // Not a column, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_COLUMNS=NUMBER_REQUIRED_COLUMNS }; // Keywords with a predefined meaning enum PredefinedKeywords { // // "True" keywords are defined. UNDEFINED_KEYWORD=0, // // Not a keyword, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_KEYWORDS=0 }; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSHistoryHandler.cc000066400000000000000000000140341476623553700225770ustar00rootroot00000000000000//# MSHistoryHandler: helper class to modify/access history //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., 675 Mass Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSHistoryHandler::MSHistoryHandler(MeasurementSet& ms, const String& app){ histTable_p = ms.history(); msHistCol_p= new MSHistoryColumns(histTable_p); application_p=app; } MSHistoryHandler &MSHistoryHandler::operator=(const MSHistoryHandler& other){ if(this != &other){ histTable_p=other.histTable_p; msHistCol_p=other.msHistCol_p; application_p=other.application_p; } return *this; } MSHistoryHandler::~MSHistoryHandler(){ histTable_p.flush(); delete msHistCol_p; } void MSHistoryHandler::addMessage(MeasurementSet& ms, const String& message, const String& app, const String& cliComm, const String& origin){ if (message.length() == 0 && cliComm.length() == 0) { // No need to record an entry. return; } MSHistory &histTable=ms.history(); rownr_t row = histTable.nrow(); MSHistoryColumns msHistCol(histTable); histTable.addRow(); Time date; MEpoch now(MVEpoch(date.modifiedJulianDay()), MEpoch::Ref(MEpoch::UTC)); msHistCol.timeMeas().put(row, now); msHistCol.observationId().put(row,-1); msHistCol.priority().put(row,"INFO"); if (origin.length() != 0) { msHistCol.origin().put(row,origin); } else { msHistCol.origin().put(row,"MSHistoryHandler::addMessage()"); } msHistCol.message().put(row,message); msHistCol.application().put(row,app); Vector cliseq(1); cliseq[0]=cliComm; msHistCol.cliCommand().put(row, cliseq); cliseq[0]=""; msHistCol.appParams().put(row, cliseq); histTable.flush(); } void MSHistoryHandler::addMessage(const String& message, const String& cliComm, const String& origin){ if (message.length() == 0 && cliComm.length() == 0) { // No need to record an entry. return; } rownr_t row = histTable_p.nrow(); histTable_p.addRow(); Time date; MEpoch now(MVEpoch(date.modifiedJulianDay()), MEpoch::Ref(MEpoch::UTC)); msHistCol_p->timeMeas().put(row, now); msHistCol_p->observationId().put(row,-1); msHistCol_p->priority().put(row,"INFO"); if (origin.length() != 0) { msHistCol_p->origin().put(row,origin); } else { msHistCol_p->origin().put(row,"MSHistoryHandler::addMessage()"); } msHistCol_p->message().put(row,message); msHistCol_p->application().put(row,application_p); Vector cliseq(1); cliseq[0]=cliComm; msHistCol_p->cliCommand().put(row, cliseq); cliseq[0]=""; msHistCol_p->appParams().put(row, cliseq); } void MSHistoryHandler::addMessage(LogSinkInterface& sink, const String& cliComm){ rownr_t row = histTable_p.nrow(); rownr_t newrows = sink.nelements(); if (newrows == 0 && cliComm.length() == 0) { // No need to record an entry return; } if (newrows == 0) { // cliComm.length() > 0 String m(""); String o("MSHistoryHandler::addMessage()"); this->addMessage(m,cliComm,o); } histTable_p.addRow(newrows); for (rownr_t k=0; k< newrows; ++k){ msHistCol_p->time().put(row, sink.getTime(k)); msHistCol_p->observationId().put(row, -1); msHistCol_p->priority().put(row,sink.getPriority(k)); msHistCol_p->origin().put(row,sink.getLocation(k)); msHistCol_p->message().put(row,sink.getMessage(k)); msHistCol_p->application().put(row,application_p); Vector cliseq(1); cliseq[0]=cliComm; msHistCol_p->cliCommand().put(row, cliseq); cliseq[0]=""; msHistCol_p->appParams().put(row, cliseq); ++row; } sink.clearLocally(); } void MSHistoryHandler::addMessage(LogIO& os, const String& cliComm){ addMessage(os.localSink(), cliComm); } void MSHistoryHandler::cliCommand(const String& cliComm){ String m(""); String o("MSHistoryHandler::cliCommand()"); this->addMessage(m,cliComm,o); } void MSHistoryHandler::cliCommand(LogIO& cliComm){ cliCommand(cliComm.localSink()); } void MSHistoryHandler::cliCommand(LogSinkInterface& sink){ uInt numCliComm=sink.nelements(); if (numCliComm == 0) return; String emptyMessage(""); rownr_t row = histTable_p.nrow(); histTable_p.addRow(); Vector cliComm(numCliComm); for (uInt k=0; k< numCliComm; ++k){ cliComm[k]=sink.getMessage(k); } msHistCol_p->time().put(row, sink.getTime(0)); msHistCol_p->observationId().put(row, -1); msHistCol_p->priority().put(row,sink.getPriority(0)); msHistCol_p->origin().put(row,sink.getLocation(0)); msHistCol_p->cliCommand().put(row, cliComm); msHistCol_p->message().put(row,emptyMessage); msHistCol_p->application().put(row,application_p); Vector dum(1); dum[0]=""; msHistCol_p->appParams().put(row, dum); sink.clearLocally(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSHistoryHandler.h000066400000000000000000000063601476623553700224440ustar00rootroot00000000000000//# MSHistoryHandler: allow simple access to write or read HISTORY subtable //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., 675 Mass Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSHISTORYHANDLER_H #define MS_MSHISTORYHANDLER_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class MSHistoryColumns; class LogIO; class LogSinkInterface; // // A class to provide a simple interface to history writing // // // // Handle the history info that needs to be archived in ms // // // This class provides access to the MS history via single method calls // One of the methods is static and can be called on a MeasurementSet without // constructing any MSHistoryHandler objects. // class MSHistoryHandler { public: //Construct the history handler from an ms MSHistoryHandler(MeasurementSet& ms, const String &app=""); MSHistoryHandler &operator=(const MSHistoryHandler &other); // Destructor ~MSHistoryHandler(); //Add a string message // This method does not need construction ...can be called explicitly // it flushes the history table of the ms static void addMessage(MeasurementSet& ms, const String& message, const String& app="", const String& cliComm="", const String& origin=""); // Add message and/or CLI command to the history table. It does not flush the table (the // destructor will flush). void addMessage(const String& message, const String& cliComm="", const String& origin=""); // In this version the LogIO object need to have a valid LogSink with // messages in it. void addMessage(LogIO& message, const String& cliComm=""); void addMessage(LogSinkInterface& sink, const String& cliComm=""); void cliCommand(const String& cliComm); void cliCommand(LogIO& cliComm); void cliCommand(LogSinkInterface& sink); private: // Prevent use of default constructor MSHistoryHandler() {} MSHistoryColumns *msHistCol_p; MSHistory histTable_p; String application_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSIter.cc000066400000000000000000001023351476623553700205450ustar00rootroot00000000000000//# MSIter.cc: Step through MeasurementSet by table //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN int MSInterval::comp(const void * obj1, const void * obj2) const { double v1 = *(const Double*)obj1; double v2 = *(const Double*)obj2; // Initialize offset_p to first timestamp. // Subtract a bit to avoid rounding problems. // Note that a time is the middle of an interval; ideally half that width // should be subtracted, but we don't know the width. if (offset_p == 0.0) { offset_p = v2 - 0.01; } // Shortcut if values are equal. if (v1 == v2) return 0; // Avoid dividing by interval_p if it is very small. // It only takes a few / DBL_MIN to get inf, // and inf == inf, even if "few" differs. // (Specifying timeInterval = 0 leads to DBL_MAX, which might be the opposite // of what is wanted! Avoiding underflow allows those who want no chunking // by TIME to use an interval_p which is guaranteed to be small enough // without having to read the INTERVAL column.) // // The 2.0 is a fudge factor. The result of the comparison should probably // be cached. if(abs(interval_p) < 2.0 * DBL_MIN) return v1 < v2 ? -1 : 1; // The times are binned in bins with a width of interval_p. double t1 = floor((v1 - offset_p) / interval_p); double t2 = floor((v2 - offset_p) / interval_p); return (t1==t2 ? 0 : (t1>>& sortColumns) : timeInSort_p(false), arrayInSort_p(false), ddInSort_p(false), fieldInSort_p(false), curMS_p(0), lastMS_p(-1), more_p(true), newMS_p(true), newArrayId_p(true), newFieldId_p(true), newSpectralWindowId_p(true), newPolarizationId_p(true), newDataDescId_p(true), storeSorted_p(false), interval_p(0), prevFirstTimeStamp_p(-1.0), allBeamOffsetsZero_p(True), timeComp_p(0) { This = (MSIter*)this; bms_p.resize(1); bms_p[0]=ms; construct(sortColumns); } MSIter::MSIter(const Block& mss, const std::vector>>& sortColumns) : bms_p(mss), timeInSort_p(false), arrayInSort_p(false), ddInSort_p(false), fieldInSort_p(false), curMS_p(0), lastMS_p(-1), more_p(true), newMS_p(true), newArrayId_p(true), newFieldId_p(true), newSpectralWindowId_p(true), newPolarizationId_p(true), newDataDescId_p(true), storeSorted_p(false), interval_p(0), prevFirstTimeStamp_p(-1.0), allBeamOffsetsZero_p(True), timeComp_p(0) { This = (MSIter*)this; construct(sortColumns); } void MSIter::construct( const std::vector>>& sortColumns) { nMS_p=bms_p.nelements(); if (nMS_p==0) throw(AipsError("MSIter::construct - No input MeasurementSets")); for (size_t i=0; i sortColumnNames; Block> sortCompareFunctions; sortColumnNames.resize(sortColumns.size()); sortCompareFunctions.resize(sortColumns.size()); Block sortOrders(sortColumns.size(),TableIterator::Ascending); size_t iCol=0; for(auto element : sortColumns) { sortColumnNames[iCol] = element.first; sortCompareFunctions[iCol] = element.second; ++iCol; if (element.first == "DATA_DESC_ID") { ddInSort_p = true; } else if (element.first == "TIME") { timeInSort_p = true; } else if (element.first == "FIELD_ID") { fieldInSort_p = true; } else if (element.first == "ARRAY_ID") { arrayInSort_p = true; } } // Create the table iterators for (size_t i=0; i& sortColumns, Double timeInterval, Bool addDefaultSortColumns, Bool storeSorted) : curMS_p(0), lastMS_p(-1), more_p(true), newMS_p(true), newArrayId_p(true), newFieldId_p(true), newSpectralWindowId_p(true), newPolarizationId_p(true), newDataDescId_p(true), storeSorted_p(storeSorted), interval_p(timeInterval), prevFirstTimeStamp_p(-1.0), allBeamOffsetsZero_p(True) { bms_p.resize(1); bms_p[0]=ms; construct(sortColumns,addDefaultSortColumns); } MSIter::MSIter(const Block& mss, const Block& sortColumns, Double timeInterval, Bool addDefaultSortColumns, Bool storeSorted) : bms_p(mss), curMS_p(0), lastMS_p(-1), more_p(true), newMS_p(true), newArrayId_p(true), newFieldId_p(true), newSpectralWindowId_p(true), newPolarizationId_p(true), newDataDescId_p(true), storeSorted_p(storeSorted), interval_p(timeInterval), prevFirstTimeStamp_p(-1.0) { construct(sortColumns,addDefaultSortColumns); } Bool MSIter::isSubSet (const Vector& r1, const Vector& r2) { size_t n1 = r1.nelements(); size_t n2 = r2.nelements(); if (n1==0) return True; if (n2& sortColumns, Bool addDefaultSortColumns) { This = (MSIter*)this; nMS_p=bms_p.nelements(); if (nMS_p==0) throw(AipsError("MSIter::construct - No input MeasurementSets")); for (size_t i=0; i cols; // try to reuse the existing sorted table if we didn't specify // any sortColumns if (sortColumns.nelements()==0 && bms_p[0].keywordSet().isDefined("SORT_COLUMNS")) { // note that we use the order of the first MS for all MS's Vector colNames = bms_p[0].keywordSet().asArrayString("SORT_COLUMNS"); size_t n=colNames.nelements(); cols.resize(n); for (size_t i=0; i0 && cols[i] columns; size_t iCol=0; if (addDefaultSortColumns) { columns.resize(cols.nelements()+4-nCol); if (!arrayInSort_p) { // add array if it's not there columns[iCol++]=MS::columnName(MS::ARRAY_ID); arrayInSort_p = true; } if (!fieldInSort_p) { // add field if it's not there columns[iCol++]=MS::columnName(MS::FIELD_ID); fieldInSort_p = true; } if (!ddInSort_p) { // add dd if it's not there columns[iCol++]=MS::columnName(MS::DATA_DESC_ID); ddInSort_p = true; } if (!timeInSort_p) { // add time if it's not there columns[iCol++]=MS::columnName(MS::TIME); timeInSort_p = True; } } else { columns.resize(cols.nelements()); } for (size_t i=0; i > objComp(columns.nelements()); for (size_t i=0; i(interval_p); objComp[i] = timeComp_p; } } Block orders(columns.nelements(),TableIterator::Ascending); // Store the sorted table for future access if possible, // reuse it if already there for (size_t i=0; i(columns.begin(), columns.end()))) { // if not, sort and store it (if possible) store=(bms_p[i].isWritable() && (bms_p[i].tableType() != Table::Memory)); } else { sorted = bms_p[i].keywordSet().asTable("SORTED_TABLE"); // if sorted table is smaller it can't be useful, remake it if (sorted.nrow() < bms_p[i].nrow()) store = bms_p[i].isWritable(); else { // if input is a sorted subset of the stored sorted table // we can use the input in the iterator if (isSubSet(bms_p[i].rowNumbers(),sorted.rowNumbers())) { useIn=True; } else { // check if #rows in input table is the same as the base table // i.e., this is the entire table, if so, use sorted version instead String anttab = bms_p[i].antenna().tableName(); // see comments below Table base (anttab.erase(anttab.length()-8)); if (base.nrow()==bms_p[i].nrow()) { useSorted = True; } else { store=bms_p[i].isWritable(); } } } } if (!useIn && !useSorted) { // we have to resort the input; enclose in >>> <<< to avoid pollution of test .out file if (aips_debug) cout << ">>>"<(columns.begin(), columns.end())); } // create the iterator for each MS // at this stage either the input is sorted already or we are using // the sorted table, so the iterator can avoid sorting. if (useIn) { tabIter_p[i] = new TableIterator(bms_p[i],columns,objComp,orders, TableIterator::NoSort); } else { tabIter_p[i] = new TableIterator(sorted,columns,objComp,orders, TableIterator::NoSort); } tabIterAtStart_p[i]=True; } setMSInfo(); } MSIter::MSIter(const MSIter& other) : nMS_p(0), storeSorted_p(False), allBeamOffsetsZero_p(True) { operator=(other); } MSIter::~MSIter() { for (size_t i=0; icopyState(*other.tabIter_p[i]); } tabIterAtStart_p = other.tabIterAtStart_p; curMS_p = other.curMS_p; lastMS_p = other.lastMS_p; msc_p = other.msc_p; curTable_p = tabIter_p[curMS_p]->table(); curArrayIdFirst_p = other.curArrayIdFirst_p; lastArrayId_p = other.lastArrayId_p; curSourceIdFirst_p = other.curSourceIdFirst_p; curFieldNameFirst_p = other.curFieldNameFirst_p; curSourceNameFirst_p = other.curSourceNameFirst_p; curFieldIdFirst_p = other.curFieldIdFirst_p; lastFieldId_p = other.lastFieldId_p; curSpectralWindowIdFirst_p = other.curSpectralWindowIdFirst_p; lastSpectralWindowId_p = other.lastSpectralWindowId_p; curPolarizationIdFirst_p = other.curPolarizationIdFirst_p; lastPolarizationId_p = other.lastPolarizationId_p; curDataDescIdFirst_p = other.curDataDescIdFirst_p; lastDataDescId_p = other.lastDataDescId_p; more_p = other.more_p; newMS_p = other.newMS_p; newArrayId_p = other.newArrayId_p; newFieldId_p = other.newFieldId_p; newSpectralWindowId_p = other.newSpectralWindowId_p; newPolarizationId_p = other.newPolarizationId_p; newDataDescId_p = other.newDataDescId_p; spwDepFeed_p = other.spwDepFeed_p; checkFeed_p = other.checkFeed_p; storeSorted_p = other.storeSorted_p; interval_p = other.interval_p; colArray_p = other.colArray_p; colDataDesc_p = other.colDataDesc_p; colField_p = other.colField_p; phaseCenter_p = other.phaseCenter_p; receptorAnglesFeed0_p = other.receptorAnglesFeed0_p; receptorAngles_p = other.receptorAngles_p; CJonesFeed0_p = other.CJonesFeed0_p; CJones_p = other.CJones_p; antennaMounts_p = other.antennaMounts_p; beamOffsets_p = other.beamOffsets_p; allBeamOffsetsZero_p = other.allBeamOffsetsZero_p; polFrame_p = other.polFrame_p; freqCacheOK_p = other.freqCacheOK_p; frequency_p = other.frequency_p; frequency0_p = other.frequency0_p; restFrequency_p = other.restFrequency_p; telescopePosition_p = other.telescopePosition_p; timeComp_p = std::make_shared(interval_p); prevFirstTimeStamp_p=other.prevFirstTimeStamp_p; return *this; } MSIter * MSIter::clone() const { return new MSIter(*this); } const MS& MSIter::ms(const size_t id) const { if(id < bms_p.nelements()){ return bms_p[id]; } else{ return bms_p[curMS_p]; } } void MSIter::setInterval(Double timeInterval) { interval_p=timeInterval; if (timeComp_p) { timeComp_p->setInterval(timeInterval); } } void MSIter::origin() { curMS_p=0; checkFeed_p=True; if (!tabIterAtStart_p[curMS_p]) tabIter_p[curMS_p]->reset(); setState(); newMS_p=newArrayId_p=newSpectralWindowId_p=newFieldId_p=newPolarizationId_p= newDataDescId_p=more_p=True; } MSIter & MSIter::operator++(int) { if (!more_p) return *this; advance(); return *this; } MSIter & MSIter::operator++() { if (!more_p) return *this; advance(); return *this; } void MSIter::advance() { newMS_p=newArrayId_p=newSpectralWindowId_p=newPolarizationId_p= newDataDescId_p=newFieldId_p=False; tabIter_p[curMS_p]->next(); tabIterAtStart_p[curMS_p]=False; if (tabIter_p[curMS_p]->pastEnd()) { if (++curMS_p >= nMS_p) { curMS_p--; more_p=False; } } if (more_p) setState(); } void MSIter::setState() { setMSInfo(); if(newMS_p) checkFeed_p=True; curTable_p=tabIter_p[curMS_p]->table(); colArray_p.attach(curTable_p,MS::columnName(MS::ARRAY_ID)); // msc_p is already defined here (it is set in setMSInfo) if(newMS_p) msc_p->antenna().mount().getColumn(antennaMounts_p,True); if(!ddInSort_p) { // If Data Description is not in the sorting columns, then the DD, SPW, pol // can change between elements of the same iteration, so the safest // is to signal that it changes. newDataDescId_p = true; newSpectralWindowId_p = true; newPolarizationId_p = true; freqCacheOK_p= false; // This indicates that the current DD, SPW, Pol Ids are not computed. // Note that the last* variables are not set, since the new* variables // are unconditionally set to true. // These cur* vars wiil be computed lazily if it is needed, together // with some more vars set in cacheExtraDDInfo(). curDataDescIdFirst_p = -1; curSpectralWindowIdFirst_p = -1; curPolarizationIdFirst_p = -1; } else { // First we cache the current DD, SPW, Pol since we know it changed cacheCurrentDDInfo(); // In this case we know that the last* variables were computed and // we can know whether there was a changed in these keywords by // comparing the two. newDataDescId_p=(lastDataDescId_p!=curDataDescIdFirst_p); newSpectralWindowId_p=(lastSpectralWindowId_p!=curSpectralWindowIdFirst_p); newPolarizationId_p=(lastPolarizationId_p!=curPolarizationIdFirst_p); lastDataDescId_p=curDataDescIdFirst_p; lastSpectralWindowId_p = curSpectralWindowIdFirst_p; lastPolarizationId_p = curPolarizationIdFirst_p; // Some extra information depends on the new* keywords, so compute // it now that new* have been set. cacheExtraDDInfo(); } setArrayInfo(); feedInfoCached_p = false; curFieldIdFirst_p=-1; //If field is not in the sorting columns, then the field id //can change between elements of the same iteration, so the safest //is to signal that it changes. if(!fieldInSort_p) newFieldId_p = true; else { setFieldInfo(); newFieldId_p=(lastFieldId_p!=curFieldIdFirst_p); lastFieldId_p = curFieldIdFirst_p; } // If time binning, update the MSInterval's offset to account for glitches. // For example, if averaging to 5s and the input is // TIME STATE_ID INTERVAL // 0 0 1 // 1 0 1 // 2 1 1 // 3 1 1 // 4 1 1 // 5 1 1 // 6 1 1 // 7 0 1 // 8 0 1 // 9 0 1 // 10 0 1 // 11 0 1 // we want the output to be // TIME STATE_ID INTERVAL // 0.5 0 2 // 4 1 5 // 9 0 5 // not what we'd get without the glitch fix: // TIME STATE_ID INTERVAL // 0.5 0 2 // 3 1 3 // 5.5 1 2 // 8 0 3 // 10.5 0 2 // // Resetting the offset with each advance() might be too often, i.e. we might // need different spws to share the same offset. But in testing resetting // with each advance produces results more consistent with expectations than // either not resetting at all or resetting only // if(colTime_p(0) - 0.02 > timeComp_p->getOffset()). // if(timeComp_p){ timeComp_p->setOffset(0.0); } } const Vector& MSIter::frequency() const { if (!freqCacheOK_p) { if(curSpectralWindowIdFirst_p==-1) { cacheCurrentDDInfo(); cacheExtraDDInfo(); } cacheCurrentDDInfo(); freqCacheOK_p = true; Int spw = curSpectralWindowIdFirst_p; msc_p->spectralWindow().chanFreq(). get(spw, frequency_p, true); } return frequency_p; } const MFrequency& MSIter::frequency0() const { if(curSpectralWindowIdFirst_p==-1) { cacheCurrentDDInfo(); cacheExtraDDInfo(); } // set the channel0 frequency measure This->frequency0_p= Vector(msc_p->spectralWindow(). chanFreqMeas()(curSpectralWindowIdFirst_p))(0); // get the reference frame out off the freq measure and set epoch measure. This->frequency0_p.getRefPtr()->getFrame().set(msc_p->timeMeas()(0)); return frequency0_p; } const MFrequency& MSIter::restFrequency(Int line) const { MFrequency freq; if(curFieldIdFirst_p == -1) setFieldInfo(); Int sourceId = msc_p->field().sourceId()(curFieldIdFirst_p); if (!msc_p->source().restFrequency().isNull()) { if (line>=0 && line < msc_p->source().restFrequency()(sourceId).shape()(0)) freq = Vector(msc_p->source(). restFrequencyMeas()(sourceId))(line); } This->restFrequency_p=freq; return restFrequency_p; } void MSIter::setMSInfo() { newMS_p = (lastMS_p != (ssize_t)curMS_p); if (newMS_p) { lastMS_p = curMS_p; if (!tabIterAtStart_p[curMS_p]) tabIter_p[curMS_p]->reset(); msc_p = std::make_shared(bms_p[curMS_p]); // determine the reference frame position String observatory; if (msc_p->observation().nrow() > 0) { observatory = msc_p->observation().telescopeName() (msc_p->observationId()(0)); } if (observatory.length() == 0 || !MeasTable::Observatory(telescopePosition_p,observatory)) { // unknown observatory, use first antenna telescopePosition_p=msc_p->antenna().positionMeas()(0); } // get the reference frame out of the freq measure and set the // position measure. frequency0_p.getRefPtr()->getFrame().set(telescopePosition_p); // force updates lastSpectralWindowId_p=-1; lastArrayId_p=-1; lastPolarizationId_p=-1; lastDataDescId_p=-1; lastFieldId_p=-1; } } void MSIter::setArrayInfo() { // Set the array info curArrayIdFirst_p=colArray_p(0); if(arrayInSort_p) newArrayId_p=(lastArrayId_p!=curArrayIdFirst_p); //If array is not in the sorting columns, then the array id //can change between elements of the same iteration, so the safest //is to signal that it changes. else newArrayId_p = true; lastArrayId_p = curArrayIdFirst_p; } void MSIter::setFeedInfo() const { // Setup CJones and the receptor angle // Time-dependent feed tables are not yet supported due to a lack of // real application. The values for the last time range will be used // if such a table is encountered. // // A reasonable way (plan) to implement the code for time-dependent feed // tables is outlined below // 1. Move the detection of the time dependent table into a separate // method and call it each time the measurement set is changed // 2. In this method build a vector of critical times when the feed // information is changed // 3. Add a set method for MSIterval to be able to access this vector // (using a pointer or reference to avoid unnecessary copying) // and make sure that critical times are known to MSInterval before // tabIter_p[curMS_p]->next() in MSIter::advance // 4. Change MSInterval::comp to break iteration (i.e. return -1 or +1) // if any critical time lies in between *obj1 and *obj2. // A sorted vector of critical times can speed up the search. // 5. Add an additional condition to // if (((!spwDepFeed_p) || spwId(i)==curSpectralWindow_p)) // and to // if ((spwDepFeed_p && newSpectralWindow_p) || first) // in the code below. // A flag newTime_p similar to newSpectralWindow_p is needed for a // better performance // Check for time dependence. Bool first=False; if (checkFeed_p) { Vector feedTimes=msc_p->feed().time().getColumn(); Vector interval=msc_p->feed().interval().getColumn(); // Assume time dependence bool timeDepFeed = true; // if all interval values are <= zero or very large, // there is no time dependence if (allLE(interval,0.0)||allGE(interval,1.e9)) timeDepFeed = false; else { // check if any antennas appear more than once // check for each spectral window and feed in turn.. Block cols(2); cols[0]=MSFeed::columnName(MSFeed::SPECTRAL_WINDOW_ID); cols[1]=MSFeed::columnName(MSFeed::FEED_ID); Bool unique = True; for (TableIterator tabIter(msc_p->feed().time().table(),cols); !tabIter.pastEnd(); tabIter.next()) { MSFeedColumns msfc(MSFeed(tabIter.table())); // check if any antennas appear more than once Vector antennas=msfc.antennaId().getColumn(); Int nRow=antennas.nelements(); Int nUniq=GenSort::sort(antennas,Sort::Ascending, Sort::HeapSort | Sort::NoDuplicates); if (nUniq!=nRow) unique=False; } timeDepFeed = !unique; } Vector spwId=msc_p->feed().spectralWindowId().getColumn(); spwDepFeed_p = !(allEQ(spwId,-1)); first=True; checkFeed_p = False; if (timeDepFeed) { LogIO os; os << LogIO::WARN << LogOrigin("MSIter","setFeedInfo") <<" time dependent feed table encountered - not correctly handled " <<" - continuing anyway"<< LogIO::POST; } } // assume there's no time dependence, if there is we'll end up using the // last entry. if ((spwDepFeed_p && newSpectralWindowId_p) || first) { if(curSpectralWindowIdFirst_p==-1) { cacheCurrentDDInfo(); cacheExtraDDInfo(); } Vector antennaId=msc_p->feed().antennaId().getColumn(); Vector feedId=msc_p->feed().feedId().getColumn(); Int maxAntId=max(antennaId); Int maxFeedId=max(feedId); AlwaysAssert((maxAntId>=0 && maxFeedId>=0),AipsError); CJones_p.resize(maxAntId+1,maxFeedId+1); Vector numRecept=msc_p->feed().numReceptors().getColumn(); uInt maxNumReceptors=max(numRecept); if (maxNumReceptors>2) throw AipsError("Can't handle more than 2 receptors"); receptorAngles_p.resize(maxNumReceptors,maxAntId+1,maxFeedId+1); receptorAnglesFeed0_p.resize(maxNumReceptors,maxAntId+1); beamOffsets_p.resize(maxNumReceptors,maxAntId+1,maxFeedId+1); allBeamOffsetsZero_p=True; Vector spwId=msc_p->feed().spectralWindowId().getColumn(); const ArrayColumn& beamOffsetColumn= msc_p->feed().beamOffset(); DebugAssert(beamOffsetColumn.nrow()==spwId.nelements(),AipsError); for (size_t i=0; i ((Matrix(msc_p->feed().polResponse()(i)))(0,0)); else CJones_p(iAnt,iFeed)=Matrix(msc_p->feed().polResponse()(i)); // Handle variable numRecept IPosition blc(1,0); IPosition trc(1,numRecept(i)-1); receptorAngles_p.xyPlane(iFeed).column(iAnt)(blc,trc)= Vector(msc_p->feed().receptorAngle()(i))(blc,trc); for (uInt rcpt=0;rcpt1e-10) allBeamOffsetsZero_p=False; beamOffsets_p(rcpt,iAnt,iFeed)(j)=beamOffsetBuf; } } } // a bit ugly construction // to preserve the old interface (feed=0 only) receptorAnglesFeed0_p.resize(); receptorAnglesFeed0_p=receptorAngles_p.xyPlane(0); CJonesFeed0_p.resize(); CJonesFeed0_p=CJones_p.column(0); // } feedInfoCached_p = true; } void MSIter::cacheCurrentDDInfo() const { colDataDesc_p.attach(curTable_p,MS::columnName(MS::DATA_DESC_ID)); curDataDescIdFirst_p = colDataDesc_p(0); curSpectralWindowIdFirst_p = msc_p->dataDescription().spectralWindowId() (curDataDescIdFirst_p); curPolarizationIdFirst_p = msc_p->dataDescription().polarizationId() (curDataDescIdFirst_p); } void MSIter::cacheExtraDDInfo() const { if (newSpectralWindowId_p) freqCacheOK_p=False; if (newPolarizationId_p) { polFrame_p=Circular; Int polType = Vector(msc_p->polarization(). corrType()(curPolarizationIdFirst_p))(0); if (polType>=Stokes::XX && polType<=Stokes::YY) polFrame_p=Linear; } } void MSIter::setFieldInfo() const { colField_p.attach(curTable_p,MS::columnName(MS::FIELD_ID)); curFieldIdFirst_p=colField_p(0); } const String& MSIter::fieldName() const { if(newFieldId_p) { if(curFieldIdFirst_p == -1) setFieldInfo(); curFieldNameFirst_p = msc_p->field().name()(curFieldIdFirst_p); } return curFieldNameFirst_p; } const String& MSIter::sourceName() const { if(newFieldId_p){ // Retrieve source name, if specified. This->curSourceNameFirst_p = ""; if (curSourceIdFirst_p >= 0 && !msc_p->source().sourceId().isNull()) { Vector sourceId=msc_p->source().sourceId().getColumn(); size_t i=0; Bool found=False; while (i < sourceId.nelements() && !found) { if (sourceId(i)==curSourceIdFirst_p) { found=True; This->curSourceNameFirst_p=msc_p->source().name()(i); } i++; } } } return curSourceNameFirst_p; } const MDirection& MSIter::phaseCenter() const { if(msc_p){ Double firstTimeStamp=ScalarColumn(curTable_p, MS::columnName(MS::TIME)).get(0); if(newFieldId_p || (firstTimeStamp != prevFirstTimeStamp_p)){ if(curFieldIdFirst_p == -1) setFieldInfo(); prevFirstTimeStamp_p=firstTimeStamp; phaseCenter_p=msc_p->field().phaseDirMeas(curFieldIdFirst_p, firstTimeStamp); } } return phaseCenter_p; } const MDirection MSIter::phaseCenter(const Int fldid, const Double timeStamp) const{ if(msc_p) return msc_p->field().phaseDirMeas(fldid, timeStamp); return phaseCenter_p; } void MSIter::getSpwInFreqRange(Block >& spw, Block >& start, Block >& nchan, Double freqStart, Double freqEnd, Double freqStep){ spw.resize(nMS_p, True, False); start.resize(nMS_p, True, False); nchan.resize(nMS_p, True, False); for (size_t k=0; k < nMS_p; ++k){ MSSpwIndex spwIn(bms_p[k].spectralWindow()); spwIn.matchFrequencyRange(freqStart-0.5*freqStep, freqEnd+0.5*freqStep, spw[k], start[k], nchan[k]); /* Vector freqlist(4); freqlist(0)=freqStart-freqStep; freqlist(1)=freqEnd+freqStep; freqlist(2)=freqStep; freqlist(3)=MSSpwIndex::MSSPW_UNITHZ; Int numSpec; spw[k].resize(); spw[k]=spwIn.convertToSpwIndex(freqlist, numSpec); Vector retchanlist= spwIn.convertToChannelIndex(spw[k], freqlist, numSpec); cout << "retchanlist " << retchanlist << endl; if((retchanlist.nelements()%3) != 0){ cout << "Error in getting channel out " << endl; } else{ start[k].resize(spw[k].nelements()); nchan[k].resize(spw[k].nelements()); for (uInt j=0; j < retchanlist.nelements()/3; ++j){ //convert to channe returns start, stop...change to start, nchan start[k][j]=retchanlist[j*3]; nchan[k][j]=retchanlist[j*3+1]-retchanlist[j*3]+1; } } */ } } // Report Name of slowest column that changes at end of current iteration const String& MSIter::keyChange() const { return tabIter_p[curMS_p]->keyChangeAtLastNext(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSIter.h000066400000000000000000000557411476623553700204170ustar00rootroot00000000000000//# MSIter.h: Step through the MeasurementEquation by table //# Copyright (C) 1996,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSITER_H #define MS_MSITER_H #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# forward decl class MSColumns; class TableIterator; // // Small helper class to specify an 'interval' comparison // // // Small helper class to specify an 'interval' comparison for table iteration // by time interval. // class MSInterval : public BaseCompare { public: explicit MSInterval(Double interval) : interval_p(interval), offset_p(0) {} virtual ~MSInterval() {} virtual int comp(const void * obj1, const void * obj2) const; Double getOffset() const {return offset_p;} virtual void setOffset(Double offset) {offset_p=offset;} Double getInterval() const {return interval_p;} void setInterval(Double interval) {interval_p=interval;} private: Double interval_p; mutable Double offset_p; }; // // An iterator class for MeasurementSets // // // //
      • MeasurementSet // // // // MSIter stands for the MeasurementSet Iterator class. // // // // An MSIter is a class to traverse a MeasurementSet in various orders. It // automatically adds four predefined sort columns to your selection of sort // columns (see constructor) so that it can keep track of changes in frequency // or polarization setup, field position and sub-array. Note that this can // cause iterations to occur in a different way from what you would expect, see // examples below. MSIter implements iteration by time interval for the use of // e.g., calibration tasks that want to calculate solutions over some interval // of time. You can iterate over multiple MeasurementSets with this class. // // // // // // The following code iterates by by ARRAY_ID, FIELD_ID, DATA_DESC_ID and // // TIME (all implicitly added columns) and then by baseline (antenna pair), // // in 3000s intervals. // MeasurementSet ms("3C273XC1.ms"); // Block sort(2); // sort[0] = MS::ANTENNA1; // sort[1] = MS::ANTENNA2; // Double timeInterval = 3000; // MSIter msIter(ms,sort,timeInteval); // for (msIter.origin(); msIter.more(); msIter++) { // // print out some of the iteration state // cout << msIter.fieldId() << endl; // cout << msIter.fieldName() << endl; // cout << msIter.dataDescriptionId() << endl; // cout << msIter.frequency0() << endl; // cout << msIter.table().nrow() << endl; // process(msIter.table()); // process the data in the current iteration // } // // Output shows only 1 row at a time because the table is sorted on TIME // // first and ANTENNA1, ANTENNA2 next and each baseline occurs only once per // // TIME stamp. The interval has no effect in this case. // // // // // // The following code iterates by baseline (antenna pair), TIME, and, // // implicitly, by ARRAY_ID, FIELD_ID and DATA_DESC_ID in 3000s // // intervals. // MeasurementSet ms("3C273XC1.ms"); // Block sort(3); // sort[0] = MS::ANTENNA1; // sort[1] = MS::ANTENNA2; // sort[2] = MS::TIME; // Double timeInterval = 3000; // MSIter msIter(ms,sort,timeInteval); // for (msIter.origin(); msIter.more(); msIter++) { // // print out some of the iteration state // cout << msIter.fieldId() << endl; // cout << msIter.fieldName() << endl; // cout << msIter.dataDescriptionId() << endl; // cout << msIter.frequency0() << endl; // cout << msIter.table().nrow() << endl; // process(msIter.table()); // process the data in the current iteration // // Now the output shows 7 rows at a time, all with identical ANTENNA1 // // and ANTENNA2 values and TIME values within a 3000s interval. // } // // // // // This class was originally part of the VisibilityIterator class, but that // class was getting too large and complicated. By splitting out the toplevel // iteration into this class the code is much easier to understand. It is now // also available through the ms tool. // // // //
      • multiple observatories in a single MS are not handled correctly (need to // sort on observation id and check observatory name to set position frame) // class MSIter { public: enum PolFrame { // Circular polarization Circular=0, // Linear polarization Linear=1 }; // Default constructor - useful only to assign another iterator later. // Use of other member functions on this object is likely to dump core. MSIter(); // Construct from MS and a Block of MS column enums specifying the // iteration order, if none are specified, ARRAY_ID, FIELD_ID, DATA_DESC_ID, // and TIME iteration is implicit (unless addDefaultSortColumns=False) // These columns will be added first if they are not specified. // An optional timeInterval can be given to iterate through chunks of time. // The default interval of 0 groups all times together. // Every 'chunk' of data contains all data within a certain time interval // and with identical values of the other iteration columns (e.g. // DATA_DESCRIPTION_ID and FIELD_ID). // See the examples above for the effect of different sort orders. // // The storeSorted parameter determines how the resulting SORT_TABLE is // managed. If storeSorted is true then the table will be stored on disk; // this potentially allows its reuse in the future but has also been shown // to be a problem when the MS is being read in parallel. If storeSorted is // false then the SORTED_TABLE is constructed and used in memory which keeps // concurrent readers from interfering with each other. MSIter(const MeasurementSet& ms, const Block& sortColumns, Double timeInterval=0, Bool addDefaultSortColumns=True, Bool storeSorted=True); // Same as above with multiple MSs as input. MSIter(const Block& mss, const Block& sortColumns, Double timeInterval=0, Bool addDefaultSortColumns=True, Bool storeSorted=True); // This constructor is similar to the previous ones but the comparison // functions used to group the iterations are given explicitly, making // the constructor more generic. Also, the column is specified as a string, // to support sorting by columns not part of the standard MS definition. // Note that with this constructor TIME is not treated in any special way and // there are no default sorting columns, i.e., the sorting needs have to be // set explicitly. // The last element in vector sortColumns will be the column that will change // faster in the iteration loop, whereas the first element will be the slower. // For instance, if sortColumns[0].first = "DATA_DESC_ID" nad // sortColumns[1].first = "ANTENNA1" then the first iterations will go through // all possible values of ANTENNA1 for the first DDId, then it will start // the iterations for the second DDId and so on. MSIter(const MeasurementSet& ms, const std::vector>>& sortColumns); // Same as above with multiple MSs as input. MSIter(const Block& mss, const std::vector>>& sortColumns); // Copy construct. This calls the assigment operator. MSIter(const MSIter & other); MSIter *clone() const; // Destructor virtual ~MSIter(); // Assigment. This will reset the iterator to the origin. MSIter & operator=(const MSIter &other); //# Members // Set or reset the time interval to use for iteration. // You should call origin() to reset the iteration after // calling this. void setInterval(Double timeInterval); // Reset iterator to start of data virtual void origin(); // Return False if there is no more data virtual Bool more() const; // Advance iterator through data virtual MSIter & operator++(int); virtual MSIter & operator++(); // Report Name of slowest column that changes at end of current iteration const String& keyChange() const; // Return the current Table iteration Table table() const; // Return reference to the current MS const MS& ms() const; // Return reference to the current MSColumns const MSColumns& msColumns() const; // Return the current MS Id (according to the order in which // they appeared in the constructor) size_t msId() const; // Return true if msId has changed since last iteration Bool newMS() const; // Return the current ArrayIds for all rows in this iteration const ScalarColumn& colArrayIds() const; // Return the current FieldIds for all rows in this iteration const ScalarColumn& colFieldIds() const; // Return the current DataDescriptionIds for all rows in this iteration const ScalarColumn& colDataDescriptionIds() const; // Return the ArrayId of the first element in this iteration Int arrayId() const; // Return True if ArrayId has changed since last iteration // Note that if MS_ARRAY is not part of the sorting columns this // will always be true. Bool newArray() const; // Return the FieldId of the first element in this iteration Int fieldId() const; // Return True if FieldId/Source has changed since last iteration // Note that if MS_FIELD_ID is not part of the sorting columns this // will always be true. Bool newField() const; // Return SpectralWindow of the first element in this iteration Int spectralWindowId() const; // Return True if SpectralWindow has changed since last iteration // Note that if MS_DATA_DESC_ID is not part of the sorting columns this // will always be true. Bool newSpectralWindow() const; // Return DataDescriptionId of the first element in this iteration Int dataDescriptionId() const; // Return True if DataDescriptionId has changed since last iteration // Note that if MS_DATA_DESC_ID is not part of the sorting columns this // will always be true. Bool newDataDescriptionId() const; // Return PolarizationId of the first element in this iteration Int polarizationId() const; // Return True if polarization has changed since last iteration // Note that if MS_DATA_DESC_ID is not part of the sorting columns this // will always be true. Bool newPolarizationId() const; // Return frame for polarization of the first element in this iteration // @returns PolFrame enum Int polFrame() const; // Return the frequencies corresponding to the DATA matrix. const Vector& frequency() const; // Return frequency of first channel of the first element in iteration // with reference frame as a Measure. // The reference frame Epoch is that of the first row, reset it as needed // for each row. // The reference frame Position is the average of the antenna positions. const MFrequency& frequency0() const; // Return the rest frequency of the specified line as a Measure const MFrequency& restFrequency(Int line=0) const; // Return the telescope position (if a known telescope) or the // position of the first antenna (if unknown) const MPosition& telescopePosition() const; // Return the feed configuration/leakage matrix for feed 0 on each antenna // TODO: CJonesAll can be used instead of this method in all instances const Vector >& CJones() const; // Return the feed configuration/leakage matrix for all feeds and antennae // First axis is antennaId, 2nd axis is feedId. Result of CJones() is // a reference to the first column of the matrix returned by this method const Matrix >& CJonesAll() const; // Return the receptor angle for feed 0 on each antenna. // First axis is receptor number, 2nd axis is antennaId. // TODO: receptorAngles() can be used instead of this method const Matrix& receptorAngle() const; // Return the receptor angles for all feeds and antennae // First axis is a receptor number, 2nd axis is antennaId, // 3rd axis is feedId. Result of receptorAngle() is just a reference // to the first plane of the cube returned by this method const Cube& receptorAngles() const; // Return a string mount identifier for each antenna const Vector& antennaMounts() const; // Return a cube containing pairs of coordinate offset for each receptor // of each feed (values are in radians, coordinate system is fixed with // antenna and is the same as used to define the BEAM_OFFSET parameter // in the feed table). The cube axes are receptor, antenna, feed. const Cube >& getBeamOffsets() const; // True if all elements of the cube returned by getBeamOffsets are zero Bool allBeamOffsetsZero() const; // Get the spw, start and nchan for all the ms's is this msiter that // match the frequecy "freqstart-freqStep" and "freqEnd+freqStep" range void getSpwInFreqRange(Block >& spw, Block >& start, Block >& nchan, Double freqStart, Double freqEnd, Double freqStep); //Get the number of actual ms's associated wth this iterator size_t numMS() const; //Get a reference to the nth ms in the list of ms associated with this // iterator. If larger than the list of ms's current ms is returned // So better check wth numMS() before making the call const MS& ms(const size_t n) const; //Returns the phasecenter for the first time stamp of the iteration //The time is important for field tables that have polynomial or ephemerides //phasecenters, i.e time varying for a given field_id.. //If the iterator is set so as one iteration has more that 1 time stamp //then this version is correct only for fixed phasecenters const MDirection& phaseCenter() const; //If the iterator is set so as one iteration has more that 1 value of time stamp // or fieldid //then the caller should use the phasecenter with field id and time explicitly const MDirection phaseCenter(const Int fldID, const Double timeStamp) const ; //return FIELD table associated current fieldname and sourcename respectively const String& fieldName() const; const String& sourceName() const; protected: // handle the construction details void construct(const Block& sortColumns, Bool addDefaultSortColumns); // handle the construction details using explicit comparison functions void construct(const std::vector>>& sortColumns); // advance the iteration void advance(); // set the iteration state virtual void setState(); void setMSInfo(); void setArrayInfo(); void setFeedInfo() const; // Store the current DD, SPW, Pol ID. // It can be called in logically const objects although it modifies // caching (mutable) variables for performance reasons. void cacheCurrentDDInfo() const; // Store extra info related to the DD. // It can be called in logically const objects although it modifies // caching (mutable) variables for performance reasons. void cacheExtraDDInfo() const; void setFieldInfo() const; // Determine if the numbers in r1 are a sorted subset of those in r2 Bool isSubSet(const Vector& r1, const Vector& r2); MSIter* This; Block bms_p; PtrBlock tabIter_p; Block tabIterAtStart_p; // This booleans determine if given columns are part of the sorting Bool timeInSort_p, arrayInSort_p, ddInSort_p, fieldInSort_p; size_t nMS_p, curMS_p; ssize_t lastMS_p; std::shared_ptr msc_p; Table curTable_p; Int curArrayIdFirst_p, lastArrayId_p, curSourceIdFirst_p; mutable String curFieldNameFirst_p; String curSourceNameFirst_p; mutable Int curFieldIdFirst_p; Int lastFieldId_p; // These variables point to the current (as in this iteration) // DD, SPW and polarization IDs. They are mutable since they are // evaluated in a lazy way, i.e., only when needed. If the DDId is // part of the sorting columns then it is always computed when calling // next(), otherwise it is only computed when some accesor of // metadata that depends on them is called by the application. mutable Int curDataDescIdFirst_p, curSpectralWindowIdFirst_p, curPolarizationIdFirst_p; // These variables point to the IDs of the previous iteration. Int lastDataDescId_p, lastSpectralWindowId_p, lastPolarizationId_p; Bool more_p, newMS_p, newArrayId_p, newFieldId_p, newSpectralWindowId_p, newPolarizationId_p, newDataDescId_p; mutable bool spwDepFeed_p, checkFeed_p; // Variable to know whether the feed info is already computed mutable bool feedInfoCached_p; // Globally control disk storage of SORTED_TABLE Bool storeSorted_p; // time selection Double interval_p; // This column is mutable since it is only attached when it is // neccesary to read the DD Ids. That might happen when calling // a const accesor like dataDescriptionId(). mutable ScalarColumn colDataDesc_p, colField_p; ScalarColumn colArray_p; mutable MDirection phaseCenter_p; mutable Double prevFirstTimeStamp_p; //cache for access functions mutable Matrix receptorAnglesFeed0_p; // former receptorAngle_p, // temporary retained for compatibility // contain actually a reference to the // first plane of receptorAngles_p mutable Cube receptorAngles_p; mutable Vector > CJonesFeed0_p; // a temporary reference // similar to receptorAngle_p mutable Matrix > CJones_p; Vector antennaMounts_p; // a string mount identifier for each // antenna (e.g. EQUATORIAL, ALT-AZ,...) mutable Cube > beamOffsets_p;// angular offsets (two values for // each element of the cube in radians) // in the antenna coordinate system. // Cube axes are: receptor, antenna, feed. mutable Bool allBeamOffsetsZero_p; // True if all elements of beamOffsets_p // are zero (to speed things up in a // single beam case) mutable PolFrame polFrame_p; // polarization Frame. It is lazily cached, // hence mutable. See cacheExtraDDInfo() mutable Bool freqCacheOK_p; // signal that the frequency cache is fine mutable Vector frequency_p; MFrequency frequency0_p; MFrequency restFrequency_p; MPosition telescopePosition_p; std::shared_ptr timeComp_p; // Points to the time comparator. // 0 if not using a time interval. }; inline Bool MSIter::more() const { return more_p;} inline Table MSIter::table() const {return curTable_p;} inline const MS& MSIter::ms() const {return bms_p[curMS_p];} inline const MSColumns& MSIter::msColumns() const { return *msc_p;} inline Bool MSIter::newMS() const { return newMS_p;} inline Bool MSIter::newArray() const {return newArrayId_p;} inline Bool MSIter::newField() const { return newFieldId_p;} inline Bool MSIter::newSpectralWindow() const { return newSpectralWindowId_p;} inline size_t MSIter::msId() const { return curMS_p;} inline size_t MSIter::numMS() const { return nMS_p;} inline const ScalarColumn& MSIter::colArrayIds() const { return colArray_p;} inline const ScalarColumn& MSIter::colFieldIds() const { return colField_p;} inline const ScalarColumn& MSIter::colDataDescriptionIds() const {if(curDataDescIdFirst_p==-1) {cacheCurrentDDInfo(); cacheExtraDDInfo();} return colDataDesc_p;} inline Int MSIter::arrayId() const {return curArrayIdFirst_p;} inline Int MSIter::fieldId() const {if(curFieldIdFirst_p==-1) setFieldInfo(); return curFieldIdFirst_p;} inline Int MSIter::spectralWindowId() const {if(curSpectralWindowIdFirst_p==-1) {cacheCurrentDDInfo(); cacheExtraDDInfo();} return curSpectralWindowIdFirst_p;} inline Int MSIter::polarizationId() const {if(curPolarizationIdFirst_p==-1) {cacheCurrentDDInfo(); cacheExtraDDInfo();} return curPolarizationIdFirst_p;} inline Int MSIter::dataDescriptionId() const {if(curDataDescIdFirst_p==-1) {cacheCurrentDDInfo(); cacheExtraDDInfo();} return curDataDescIdFirst_p;} inline Bool MSIter::newPolarizationId() const { return newPolarizationId_p;} inline Bool MSIter::newDataDescriptionId() const { return newDataDescId_p;} inline Int MSIter::polFrame() const {if(curPolarizationIdFirst_p==-1) {cacheCurrentDDInfo(); cacheExtraDDInfo();} return polFrame_p;} inline const MPosition& MSIter::telescopePosition() const { return telescopePosition_p;} inline const Vector >& MSIter::CJones() const {if(!feedInfoCached_p) setFeedInfo(); return CJonesFeed0_p;} inline const Matrix >& MSIter::CJonesAll() const {if(!feedInfoCached_p) setFeedInfo(); return CJones_p;} inline const Matrix& MSIter::receptorAngle() const {if(!feedInfoCached_p) setFeedInfo(); return receptorAnglesFeed0_p;} inline const Cube& MSIter::receptorAngles() const {if(!feedInfoCached_p) setFeedInfo(); return receptorAngles_p;} inline const Vector& MSIter::antennaMounts() const {return antennaMounts_p;} inline const Cube >& MSIter::getBeamOffsets() const {if(!feedInfoCached_p) setFeedInfo(); return beamOffsets_p;} inline Bool MSIter::allBeamOffsetsZero() const {if(!feedInfoCached_p) setFeedInfo(); return allBeamOffsetsZero_p;} } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSMainColumns.cc000066400000000000000000000163731476623553700220750ustar00rootroot00000000000000//# MSMainColumns.cc: Easy access to MeasurementSet main table columns //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSMainColumns::MSMainColumns() { } MSMainColumns::MSMainColumns(const MeasurementSet& ms) { attach(ms); } MSMainColumns::~MSMainColumns() {} void MSMainColumns::attach(const MeasurementSet& ms) { antenna1_p.attach(ms, MS::columnName(MS::ANTENNA1)); antenna2_p.attach(ms, MS::columnName(MS::ANTENNA2)); arrayId_p.attach(ms, MS::columnName(MS::ARRAY_ID)); dataDescId_p.attach(ms, MS::columnName(MS::DATA_DESC_ID)); exposure_p.attach(ms, MS::columnName(MS::EXPOSURE)); feed1_p.attach(ms, MS::columnName(MS::FEED1)); feed2_p.attach(ms, MS::columnName(MS::FEED2)); fieldId_p.attach(ms, MS::columnName(MS::FIELD_ID)); flag_p.attach(ms, MS::columnName(MS::FLAG)); flagCategory_p.attach(ms, MS::columnName(MS::FLAG_CATEGORY)); flagRow_p.attach(ms, MS::columnName(MS::FLAG_ROW)); interval_p.attach(ms, MS::columnName(MS::INTERVAL)); observationId_p.attach(ms, MS::columnName(MS::OBSERVATION_ID)); processorId_p.attach(ms, MS::columnName(MS::PROCESSOR_ID)); scanNumber_p.attach(ms, MS::columnName(MS::SCAN_NUMBER)); sigma_p.attach(ms, MS::columnName(MS::SIGMA)); stateId_p.attach(ms, MS::columnName(MS::STATE_ID)); time_p.attach(ms, MS::columnName(MS::TIME)); timeCentroid_p.attach(ms, MS::columnName(MS::TIME_CENTROID)); uvw_p.attach(ms, MS::columnName(MS::UVW)); weight_p.attach(ms, MS::columnName(MS::WEIGHT)); timeMeas_p.attach(ms, MS::columnName(MS::TIME)), timeCentroidMeas_p.attach(ms, MS::columnName(MS::TIME_CENTROID)), uvwMeas_p.attach(ms, MS::columnName(MS::UVW)), exposureQuant_p.attach(ms, MS::columnName(MS::EXPOSURE)), intervalQuant_p.attach(ms, MS::columnName(MS::INTERVAL)), timeQuant_p.attach(ms, MS::columnName(MS::TIME)), timeCentroidQuant_p.attach(ms, MS::columnName(MS::TIME_CENTROID)), uvwQuant_p.attach(ms, MS::columnName(MS::UVW)), attachOptionalCols(ms); } void MSMainColumns::attachOptionalCols(const MeasurementSet& ms) { const ColumnDescSet& cds=ms.tableDesc().columnDescSet(); if (cds.isDefined(MS::columnName(MS::ANTENNA3))) { antenna3_p.attach(ms,MS::columnName(MS::ANTENNA3)); } if (cds.isDefined(MS::columnName(MS::BASELINE_REF))) { baselineRef_p.attach(ms,MS::columnName(MS::BASELINE_REF)); } if (cds.isDefined(MS::columnName(MS::DATA))) { data_p.attach(ms,MS::columnName(MS::DATA)); } if (cds.isDefined(MS::columnName(MS::FEED3))) { feed3_p.attach(ms,MS::columnName(MS::FEED3)); } if (cds.isDefined(MS::columnName(MS::FLOAT_DATA))) { floatData_p.attach(ms,MS::columnName(MS::FLOAT_DATA)); } if (cds.isDefined(MS::columnName(MS::LAG_DATA))) { lagData_p.attach(ms,MS::columnName(MS::LAG_DATA)); } if (cds.isDefined(MS::columnName(MS::PHASE_ID))) { phaseId_p.attach(ms,MS::columnName(MS::PHASE_ID)); } if (cds.isDefined(MS::columnName(MS::PULSAR_BIN))) { pulsarBin_p.attach(ms,MS::columnName(MS::PULSAR_BIN)); } if (cds.isDefined(MS::columnName(MS::PULSAR_GATE_ID))) { pulsarGateId_p.attach(ms,MS::columnName(MS::PULSAR_GATE_ID)); } if (cds.isDefined(MS::columnName(MS::SIGMA_SPECTRUM))) { sigmaSpectrum_p.attach(ms,MS::columnName(MS::SIGMA_SPECTRUM)); } if (cds.isDefined(MS::columnName(MS::TIME_EXTRA_PREC))) { timeExtraPrec_p.attach(ms,MS::columnName(MS::TIME_EXTRA_PREC)); timeExtraPrecQuant_p.attach(ms,MS::columnName(MS::TIME_EXTRA_PREC)); } if (cds.isDefined(MS::columnName(MS::UVW2))) { uvw2_p.attach(ms,MS::columnName(MS::UVW2)); uvw2Meas_p.attach(ms,MS::columnName(MS::UVW2)); uvw2Quant_p.attach(ms,MS::columnName(MS::UVW2)); } if (cds.isDefined(MS::columnName(MS::VIDEO_POINT))) { videoPoint_p.attach(ms,MS::columnName(MS::VIDEO_POINT)); } if (cds.isDefined(MS::columnName(MS::WEIGHT_SPECTRUM))) { weightSpectrum_p.attach(ms,MS::columnName(MS::WEIGHT_SPECTRUM)); } if (cds.isDefined(MS::columnName(MS::CORRECTED_WEIGHT_SPECTRUM))) { weightSpectrumCorrected_p.attach(ms,MS::columnName(MS::CORRECTED_WEIGHT_SPECTRUM)); } if (cds.isDefined(MS::columnName(MS::CORRECTED_DATA))) { correctedData_p.attach(ms,MS::columnName(MS::CORRECTED_DATA)); } if (cds.isDefined(MS::columnName(MS::IMAGING_WEIGHT))) { imagingWeight_p.attach(ms,MS::columnName(MS::IMAGING_WEIGHT)); } if (cds.isDefined(MS::columnName(MS::MODEL_DATA))) { modelData_p.attach(ms,MS::columnName(MS::MODEL_DATA)); } } Vector MSMainColumns::flagCategories() const { const TableRecord& keywords = flagCategory().keywordSet(); const RecordFieldId key("CATEGORY"); DebugAssert(keywords.isDefined(key.fieldName()), AipsError); DebugAssert(keywords.dataType(key) == TpArrayString, AipsError); DebugAssert(keywords.shape(key).nelements() == 1, AipsError); DebugAssert(nrow() == 0 || keywords.shape(key)(0) == flagCategory().shape(0)(2), AipsError); return Vector(keywords.asArrayString(key)); } void MSMainColumns::setFlagCategories(const Vector& categories) { TableRecord& keywords = flagCategory().rwKeywordSet(); const RecordFieldId key("CATEGORY"); DebugAssert(nrow() == 0 || categories.nelements() == static_cast(flagCategory().shape(0)(2)), AipsError); keywords.define(key, categories); } void MSMainColumns::setEpochRef(MEpoch::Types ref, Bool tableMustBeEmpty) { timeMeas_p.setDescRefCode(ref, tableMustBeEmpty); timeCentroidMeas_p.setDescRefCode(ref, tableMustBeEmpty); } void MSMainColumns::setUVWRef(Muvw::Types ref) { uvwMeas_p.setDescRefCode(ref); if (!uvw2_p.isNull()) { uvw2Meas_p.setDescRefCode(ref); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSMainColumns.h000066400000000000000000000351721476623553700217350ustar00rootroot00000000000000//# MSmainColumns.h: provides easy access to MeasurementSet main table columns //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSMAINCOLUMNS_H #define MS_MSMAINCOLUMNS_H #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class MeasurementSet; class String; // // A class for easy access to MeasurementSet main table columns // // // // // //
      • MeasurementSet //
      • ArrayColumn //
      • ScalarColumn // // // // MSMainColumns stands for MeasurementSet main Table columns. // // // // This class provides access to the columns in the MeasurementSet. // It does the declaration of all the Scalar and ArrayColumns with the // correct types, so the application programmer doesn't have to // worry about getting those right. There is an access function // for every predefined column. Access to non-predefined columns will still // have to be done with explicit declarations. // The Table that is used to construct this class must not // be destroyed (or go out of scope) before this class does. Otherwise the // scalar and array columns use by this class will be left dangling. // // // // // // // use as follows // MeasurementSet ms("myMS",Table::Update); // MSColumns msc(ms); // // show data from row 5 // cout << msc.data()(5); // // change name of antenna on row 3 in antenna table // msc.antenna().name().put(3,"NewAnt-3"); // // // // // Having to type long lists of Scalar and Array column declarations gets // very tedious. This class attempts to relieve some of that tedium, while // at the same time concentrating all the declarations in one place, // making Type errors in the column declaration (only caught at run-time) less // probable. Type errors in the use of the columns is caught at compile // time. // // // //
      • We might decide to merge this class with the MeasurementSet // class MSMainColumns { public: // Create a columns object that accesses the data in the specified Table MSMainColumns(const MeasurementSet& ms); // The desctructor does nothing special ~MSMainColumns(); // Access to required columns // ScalarColumn& antenna1() {return antenna1_p;} ScalarColumn& antenna2() {return antenna2_p;} ScalarColumn& arrayId() {return arrayId_p;} ScalarColumn& dataDescId() {return dataDescId_p;} ScalarColumn& exposure() {return exposure_p;} ScalarQuantColumn& exposureQuant() { return exposureQuant_p;} ScalarColumn& feed1() {return feed1_p;} ScalarColumn& feed2() {return feed2_p;} ScalarColumn& fieldId() {return fieldId_p;} ArrayColumn& flag() {return flag_p;} ArrayColumn& flagCategory() {return flagCategory_p;} ScalarColumn& flagRow() {return flagRow_p;} ScalarColumn& interval() {return interval_p;} ScalarQuantColumn& intervalQuant() { return intervalQuant_p;} ScalarColumn& observationId() {return observationId_p;} ScalarColumn& processorId() {return processorId_p;} ScalarColumn& scanNumber() {return scanNumber_p;} ArrayColumn& sigma() {return sigma_p;} ScalarColumn& stateId() {return stateId_p;} ScalarColumn& time() {return time_p;} ScalarQuantColumn& timeQuant() { return timeQuant_p;} ScalarMeasColumn& timeMeas() { return timeMeas_p;} ScalarColumn& timeCentroid() {return timeCentroid_p;} ScalarQuantColumn& timeCentroidQuant() { return timeCentroidQuant_p;} ScalarMeasColumn& timeCentroidMeas() { return timeCentroidMeas_p;} ArrayColumn& uvw() {return uvw_p;} ArrayQuantColumn& uvwQuant() { return uvwQuant_p;} ScalarMeasColumn& uvwMeas() { return uvwMeas_p;} ArrayColumn& weight() {return weight_p;} // // Access to optional columns // ScalarColumn& antenna3() {return antenna3_p;} ScalarColumn& baselineRef() {return baselineRef_p;} ArrayColumn& correctedData() {return correctedData_p;} ArrayColumn& data() {return data_p;} ScalarColumn& feed3() {return feed3_p;} ArrayColumn& floatData() {return floatData_p;} ArrayColumn& imagingWeight() {return imagingWeight_p;} ArrayColumn& lagData() {return lagData_p;} ArrayColumn& modelData() {return modelData_p;} ScalarColumn& phaseId() {return phaseId_p;} ScalarColumn& pulsarBin() {return pulsarBin_p;} ScalarColumn& pulsarGateId() {return pulsarGateId_p;} ArrayColumn& sigmaSpectrum() {return sigmaSpectrum_p;} ScalarColumn& timeExtraPrec() {return timeExtraPrec_p;} ScalarQuantColumn& timeExtraPrecQuant() { return timeExtraPrecQuant_p;} ArrayColumn& uvw2() {return uvw2_p;} ScalarMeasColumn& uvw2Meas() { return uvw2Meas_p;} ArrayQuantColumn& uvw2Quant() { return uvw2Quant_p;} ArrayColumn& videoPoint() {return videoPoint_p;} ArrayColumn& weightSpectrum() {return weightSpectrum_p;} ArrayColumn& weightSpectrumCorrected() {return weightSpectrumCorrected_p;} // // Const access to required columns // const ScalarColumn& antenna1() const {return antenna1_p;} const ScalarColumn& antenna2() const {return antenna2_p;} const ScalarColumn& arrayId() const {return arrayId_p;} const ScalarColumn& dataDescId() const {return dataDescId_p;} const ScalarColumn& exposure() const {return exposure_p;} const ScalarQuantColumn& exposureQuant() const { return exposureQuant_p;} const ScalarColumn& feed1() const {return feed1_p;} const ScalarColumn& feed2() const {return feed2_p;} const ScalarColumn& fieldId() const {return fieldId_p;} const ArrayColumn& flag() const {return flag_p;} const ArrayColumn& flagCategory() const {return flagCategory_p;} const ScalarColumn& flagRow() const {return flagRow_p;} const ScalarColumn& interval() const {return interval_p;} const ScalarQuantColumn& intervalQuant() const { return intervalQuant_p;} const ScalarColumn& observationId() const {return observationId_p;} const ScalarColumn& processorId() const {return processorId_p;} const ScalarColumn& scanNumber() const {return scanNumber_p;} const ArrayColumn& sigma() const {return sigma_p;} const ScalarColumn& stateId() const {return stateId_p;} const ScalarColumn& time() const {return time_p;} const ScalarQuantColumn& timeQuant() const { return timeQuant_p;} const ScalarMeasColumn& timeMeas() const { return timeMeas_p;} const ScalarColumn& timeCentroid() const {return timeCentroid_p;} const ScalarQuantColumn& timeCentroidQuant() const { return timeCentroidQuant_p;} const ScalarMeasColumn& timeCentroidMeas() const { return timeCentroidMeas_p;} const ArrayColumn& uvw() const {return uvw_p;} const ArrayQuantColumn& uvwQuant() const { return uvwQuant_p;} const ScalarMeasColumn& uvwMeas() const { return uvwMeas_p;} const ArrayColumn& weight() const {return weight_p;} // // Access to optional columns // const ScalarColumn& antenna3() const {return antenna3_p;} const ScalarColumn& baselineRef() const {return baselineRef_p;} const ArrayColumn& correctedData() const {return correctedData_p;} const ArrayColumn& data() const {return data_p;} const ScalarColumn& feed3() const {return feed3_p;} const ArrayColumn& floatData() const {return floatData_p;} const ArrayColumn& imagingWeight() const {return imagingWeight_p;} const ArrayColumn& lagData() const {return lagData_p;} const ArrayColumn& modelData() const {return modelData_p;} const ScalarColumn& phaseId() const {return phaseId_p;} const ScalarColumn& pulsarBin() const {return pulsarBin_p;} const ScalarColumn& pulsarGateId() const {return pulsarGateId_p;} const ArrayColumn& sigmaSpectrum() const {return sigmaSpectrum_p;} const ScalarColumn& timeExtraPrec() const {return timeExtraPrec_p;} const ScalarQuantColumn& timeExtraPrecQuant() const { return timeExtraPrecQuant_p;} const ArrayColumn& uvw2() const {return uvw2_p;} const ScalarMeasColumn& uvw2Meas() const { return uvw2Meas_p;} const ArrayQuantColumn& uvw2Quant() const { return uvw2Quant_p;} const ArrayColumn& videoPoint() const {return videoPoint_p;} const ArrayColumn& weightSpectrum() const {return weightSpectrum_p;} const ArrayColumn& weightSpectrumCorrected() const {return weightSpectrumCorrected_p;} // // Convenience function that returns the number of rows in any of the columns rownr_t nrow() const {return antenna1_p.nrow();} // Returns the category labels for the FLAG_CATEGORY column. Vector flagCategories() const; // set the epoch type for the TIME and TIME_CENTROID columns. // // In principle this function can only be used if the table is empty, // otherwise already written values may thereafter have an incorrect // reference, offset, or unit. However, it is possible that part of the // table gets written before these values are known. In that case the // reference, offset, or units can be set by using a False // tableMustBeEmpty argument. // void setEpochRef(MEpoch::Types ref, Bool tableMustBeEmpty=True); // set the UVW reference type for the UVW and UVW2 (if defined) columns. This // can only be done when the table has no rows. Trying to do so at other // times will throw an exception. void setUVWRef(Muvw::Types ref); // Set the flag category labels to the supplied values (in the CATEGORY // keyword of the FLAG_CATEGORY column). Throws an exception, when compiled // in Debug mode, if the length of the supplied Vector is not the same as the // length of the third dimension of the FLAG_CATEGORY column. void setFlagCategories(const Vector& categories); protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. MSMainColumns(); //# attach this object to the supplied table. void attach(const MeasurementSet& ms); private: //# Make the assignment operator and the copy constructor private to prevent //# any compiler generated one from being used. MSMainColumns(const MSMainColumns&); MSMainColumns& operator=(const MSMainColumns&); //# Check if any optional columns exist and if so attach them. void attachOptionalCols(const MeasurementSet& ms); //# required columns ScalarColumn antenna1_p; ScalarColumn antenna2_p; ScalarColumn arrayId_p; ScalarColumn dataDescId_p; ScalarColumn exposure_p; ScalarColumn feed1_p; ScalarColumn feed2_p; ScalarColumn fieldId_p; ArrayColumn flag_p; ArrayColumn flagCategory_p; ScalarColumn flagRow_p; ScalarColumn interval_p; ScalarColumn observationId_p; ScalarColumn processorId_p; ScalarColumn scanNumber_p; ArrayColumn sigma_p; ScalarColumn stateId_p; ScalarColumn time_p; ScalarColumn timeCentroid_p; ArrayColumn uvw_p; ArrayColumn weight_p; //# optional columns ScalarColumn antenna3_p; ScalarColumn baselineRef_p; ArrayColumn data_p; ScalarColumn feed3_p; ArrayColumn floatData_p; ArrayColumn lagData_p; ScalarColumn phaseId_p; ScalarColumn pulsarBin_p; ScalarColumn pulsarGateId_p; ArrayColumn sigmaSpectrum_p; ScalarColumn timeExtraPrec_p; ArrayColumn uvw2_p; ArrayColumn videoPoint_p; ArrayColumn weightSpectrum_p; ArrayColumn weightSpectrumCorrected_p; //# columns required for synthesis applications - all optional ArrayColumn correctedData_p; ArrayColumn imagingWeight_p; ArrayColumn modelData_p; //# Access to Measure columns ScalarMeasColumn timeMeas_p; ScalarMeasColumn timeCentroidMeas_p; ScalarMeasColumn uvwMeas_p; //# optional Measure columns ScalarMeasColumn uvw2Meas_p; //# Access to Quantum columns ScalarQuantColumn exposureQuant_p; ScalarQuantColumn intervalQuant_p; ScalarQuantColumn timeQuant_p; ScalarQuantColumn timeCentroidQuant_p; ArrayQuantColumn uvwQuant_p; //# optional Quantum columns ScalarQuantColumn timeExtraPrecQuant_p; ArrayQuantColumn uvw2Quant_p; }; //# Define the RO version for backward compatibility. typedef MSMainColumns ROMSMainColumns; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSMainEnums.h000066400000000000000000000231411476623553700213750ustar00rootroot00000000000000//# MSMainEnums.h: Class with definitions for the main MeasurementSet table //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSMAINENUMS_H #define MS_MSMAINENUMS_H #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Enums for the MeasurementSet main table // // // // This class contains the enum defininitions for the main MeasurementSet // table. // // // This class does nothing. It is merely a container for the enumerations // used by the MeasurementSet class. These enumerations define the // standard columns, keywords. // // // See the documentation for MeasurementSet for examples on the use of these // enumerations. // // // All the todo items which may be related to this class are // grouped with the todo items for MeasurementSet // // class MSMainEnums { public: // The Main table colums with predefined meaning. enum PredefinedColumns { // "True" columns are defined.
        // TYPE - UNIT - MEASURE UNDEFINED_COLUMN=0, // // ID of first antenna in antenna-pair. This is a key into the // ANTENNA table. Ranges from 0 to NUM_ANT-1.
        // Int ANTENNA1, // // ID of second antenna in antenna-pair. For SD ANTENNA1==ANTENNA2
        // Int ANTENNA2, // // ARRAY id.
        // Int. ARRAY_ID, // // Data description id
        // Int. DATA_DESC_ID, // // Effective integration time (i.e.<=INTERVAL)
        // Double - s. EXPOSURE, // // Feed id on ANTENNA1.
        // Int. FEED1, // // Feed id on ANTENNA2.
        // Int. FEED2, // // Unique id for this pointing (or drift scan)
        // Int FIELD_ID, // // The data flags, array of bools with same shape as data. // Data is flagged bad if FLAG is True.
        // Bool(Nc, Nf) FLAG, // // Flag category, allows for multiple categories of flagging, which can // selectively be reset. The cumulative effect is reflected in FLAG. // This column should have an attached keyword CATEGORY which is a // String (Ncat) of categories (e.g, ONLINE, FLAG_CMD, INTERACTIVE)
        // Bool (Nc, Nf, Ncat) FLAG_CATEGORY, // // Flag all data in this row if True.
        // Bool FLAG_ROW, // // The extent of this sample, sampling interval.
        // Double - s. INTERVAL, // // Index into OBSERVATION table.
        // Int. OBSERVATION_ID, // // Processor Id, points to PROCESSOR table with information on the // correlator or backend setup.
        // Int PROCESSOR_ID, // // Scan number. // Int. SCAN_NUMBER, // // Estimated rms noise for channel with unity bandpass response.
        // Float(Nc) - Same units as the DATA column. SIGMA, // // State Id, points to STATE table with info on current observing mode, // calibration and reference signals etc. (Mainly single dish)
        // Int STATE_ID, // // Modified Julian Day number (JD-2400000.5) for midpoint of integration. // For high precision timing, add the value from TIME_EXTRA_PREC.
        // Double - s - EPOCH. TIME, // // Modified Julian Day number (JD-2400000.5) for centroid of integration. // Double - s - EPOCH. TIME_CENTROID, // // UVW coordinates.
        // Double(3) - m - UVW. UVW, // // Weight of spectrum. This is the weight assigned by the correlator and // does NOT get overwritten by e.g. imaging tasks that do weighting.
        // Float(Nc). WEIGHT, // // Not a column, but just an enum specifying the number of required columns. //# Note: first enum after this one should be assigned value of this enum. NUMBER_REQUIRED_COLUMNS=WEIGHT, // // Antenna3 - for triple correlations products.
        // Int ANTENNA3, // // Reference antenna for this baseline, True for ANTENNA1
        // Bool BASELINE_REF, // // The Corrected complex visibility data (optional).
        // Complex(Nc, Nf) CORRECTED_DATA, // // Complex visibility matrix. The UNITS are unspecified to allow // for the calibrated data to show up as a DATA column as well but in // a calibrated MS.
        // Complex(Nc, Nf) DATA, // // Feed id on ANTENNA3
        // Int FEED3, // // Floating point data column. For simple single dish work this can be used // instead of the complex DATA column.
        // Float(Nc, Nf) FLOAT_DATA, // // The imaging weights (optional).
        // Float(Nf) IMAGING_WEIGHT, // // Complex correlation function or lag spectrum for each correlation // product
        // Complex(Nc, Nl) LAG_DATA, // // The model visibility data (optional).
        // Complex(Nc,Nf) MODEL_DATA, // // Switching phase Id
        // Int PHASE_ID, // // For a pulsar the correlations are assumed to be measured for a // limited number of pulse phase bins. This is the particular bin for // which this data was measured. (optional)
        // Int. PULSAR_BIN, // // Unique id for this pulsar gate. Index into PULSAR_GATE table. // (optional)
        // Int. PULSAR_GATE_ID, // // Estimated rms noise for each data point. To be used instead of // SIGMA if present.
        // Float(Nc,Nf) - Same units as the DATA column. SIGMA_SPECTRUM, // // Additional precision for TIME if required. Add this to TIME to obtain // the exact EPOCH.
        // Double - s. TIME_EXTRA_PREC, // // UVW for second pair of triple correlation product.
        // Double(3) - m UVW2, // // Zero frequency point - needed for transform back to lag domain
        // Complex(Nc) VIDEO_POINT, // // Weight for each channel. To be used instead of WEIGHT if present.
        // Float(Nf). WEIGHT_SPECTRUM, // Corrected Weight for each channel. If present can be used with corrected_data
        // Float(Nf). CORRECTED_WEIGHT_SPECTRUM, // // Not a column, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_COLUMNS=CORRECTED_WEIGHT_SPECTRUM }; // Keywords with a predefined meaning enum PredefinedKeywords { // // "True" keywords are defined. UNDEFINED_KEYWORD=0, // // Antenna subtable. Antenna positions, mount-types etc. ANTENNA, // Data Description subtable. Gives spectral window and polarization id. DATA_DESCRIPTION, // Feed subtable. Responses, offsets, beams etc. FEED, // Field subtable. Position etc. for each pointing. FIELD, // Flag command subtable. List of flag commands. FLAG_CMD, // History information subtable. HISTORY, // MS Version number.
        // Float. MS_VERSION, // Observation subtable. Project, observer, schedule. OBSERVATION, // Pointing information subtable. POINTING, // Polarization setup information subtable. POLARIZATION, // Back-end processor information subtable. Description of correlator etc. PROCESSOR, // Spectral window subtable. Frequencies, bandwidths, polarizations. SPECTRAL_WINDOW, // State subtable. Observing modes and states (cal, ref etc.) STATE, // Not a keyword, but an enum specifying the number of required keywords // The last required keyword should be set to this enum NUMBER_REQUIRED_KEYWORDS=STATE, // Calibration tables associated with this MS.
        // Table(NUM_CAL_TABLES) CAL_TABLES, // Doppler tracking information subtable. DOPPLER, // Frequency offset information subtable. FREQ_OFFSET, // Listing of sort columns for each sorted table.
        // String(NUM_SORTED_TABLES) SORT_COLUMNS, // Listing of sort orders for each sorted table.
        // String(NUM_SORTED_TABLES) SORT_ORDER, // Sorted reference tables of the main table. First one is main table.
        // Table(NUM_SORTED_TABLES) SORTED_TABLES, // Source subtable. Positions etc. for each source. SOURCE, // SysCal subtable. System calibration data (Tsys etc.) SYSCAL, // Weather subtable. Weather info for each antenna. WEATHER, // Not a keyword, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_KEYWORDS=WEATHER }; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSObsColumns.cc000066400000000000000000000062221476623553700217240ustar00rootroot00000000000000//# MSObsColumns.cc: provides easy access to MeasurementSet columns //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSObservationColumns::MSObservationColumns() { } MSObservationColumns:: MSObservationColumns(const MSObservation& msObservation) { attach(msObservation); } MSObservationColumns::~MSObservationColumns() {} void MSObservationColumns::attach(const MSObservation& msObservation) { flagRow_p.attach(msObservation, MSObservation:: columnName(MSObservation::FLAG_ROW)); log_p.attach(msObservation, MSObservation::columnName(MSObservation::LOG)); observer_p.attach(msObservation, MSObservation:: columnName(MSObservation::OBSERVER)); project_p.attach(msObservation, MSObservation:: columnName(MSObservation::PROJECT)); releaseDate_p.attach(msObservation, MSObservation:: columnName(MSObservation::RELEASE_DATE)); schedule_p.attach(msObservation, MSObservation:: columnName(MSObservation::SCHEDULE)); scheduleType_p.attach(msObservation, MSObservation:: columnName(MSObservation::SCHEDULE_TYPE)); telescopeName_p.attach(msObservation, MSObservation:: columnName(MSObservation::TELESCOPE_NAME)); timeRange_p.attach(msObservation, MSObservation:: columnName(MSObservation::TIME_RANGE)); releaseDateMeas_p.attach(msObservation, MSObservation:: columnName(MSObservation::RELEASE_DATE)); timeRangeMeas_p.attach(msObservation, MSObservation:: columnName(MSObservation::TIME_RANGE)); releaseDateQuant_p.attach(msObservation, MSObservation:: columnName(MSObservation::RELEASE_DATE)); timeRangeQuant_p.attach(msObservation, MSObservation:: columnName(MSObservation::TIME_RANGE)); } void MSObservationColumns:: setEpochRef(MEpoch::Types ref, Bool tableMustBeEmpty) { timeRangeMeas_p.setDescRefCode(ref, tableMustBeEmpty); releaseDateMeas_p.setDescRefCode(ref, tableMustBeEmpty); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSObsColumns.h000066400000000000000000000155371476623553700215770ustar00rootroot00000000000000//# MSObservationColumns.h: provides easy access to MSObservation columns //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSOBSCOLUMNS_H #define MS_MSOBSCOLUMNS_H #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class MSObservation; // // A class to provide easy access to MSObservation columns // // // // // //
      • MSObservation //
      • ArrayColumn //
      • ScalarColumn // // // // MSObservationColumns stands for MeasurementSet Observation Table // columns. // // // // This class provides access to the columns in the MSObservation Table, // it does the declaration of all the Scalar and ArrayColumns with the // correct types, so the application programmer doesn't have to // worry about getting those right. There is an access function // for every predefined column. Access to non-predefined columns will still // have to be done with explicit declarations. // See MSColumns for an example. // // // // See MSColumns for the motivation. // class MSObservationColumns { public: // Create a columns object that accesses the data in the specified Table MSObservationColumns(const MSObservation& msObservation); // The desctructor does nothing special ~MSObservationColumns(); // Access to required columns // ScalarColumn& flagRow() {return flagRow_p;} ArrayColumn& log() {return log_p;} ScalarColumn& observer() {return observer_p;} ScalarColumn& project() {return project_p;} ScalarColumn& releaseDate() {return releaseDate_p;} ScalarQuantColumn& releaseDateQuant() {return releaseDateQuant_p;} ScalarMeasColumn& releaseDateMeas() {return releaseDateMeas_p;} ArrayColumn& schedule() {return schedule_p;} ScalarColumn& scheduleType() {return scheduleType_p;} ScalarColumn& telescopeName() {return telescopeName_p;} ArrayColumn& timeRange() {return timeRange_p;} ArrayQuantColumn& timeRangeQuant() {return timeRangeQuant_p;} ArrayMeasColumn& timeRangeMeas() {return timeRangeMeas_p;} // // Const access to required columns // const ScalarColumn& flagRow() const {return flagRow_p;} const ArrayColumn& log() const {return log_p;} const ScalarColumn& observer() const {return observer_p;} const ScalarColumn& project() const {return project_p;} const ScalarColumn& releaseDate() const {return releaseDate_p;} const ScalarQuantColumn& releaseDateQuant() const { return releaseDateQuant_p;} const ScalarMeasColumn& releaseDateMeas() const { return releaseDateMeas_p;} const ArrayColumn& schedule() const {return schedule_p;} const ScalarColumn& scheduleType() const {return scheduleType_p;} const ScalarColumn& telescopeName() const {return telescopeName_p;} const ArrayColumn& timeRange() const {return timeRange_p;} const ArrayQuantColumn& timeRangeQuant() const { return timeRangeQuant_p;} const ArrayMeasColumn& timeRangeMeas() const { return timeRangeMeas_p;} // // Convenience function that returns the number of rows in any of the columns rownr_t nrow() const {return flagRow_p.nrow();} // set the epoch type for the TIME_RANGE & RELEASE_DATE columns. // // In principle this function can only be used if the table is empty, // otherwise already written values may thereafter have an incorrect // reference, offset, or unit. However, it is possible that part of the // table gets written before these values are known. In that case the // reference, offset, or units can be set by using a False // tableMustBeEmpty argument. // void setEpochRef(MEpoch::Types ref, Bool tableMustBeEmpty=True); protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. MSObservationColumns(); //# attach this object to the supplied table. void attach(const MSObservation& msObservation); private: //# Make the assignment operator and the copy constructor private to prevent //# any compiler generated one from being used. MSObservationColumns(const MSObservationColumns&); MSObservationColumns& operator=(const MSObservationColumns&); //# required columns ScalarColumn flagRow_p; ArrayColumn log_p; ScalarColumn observer_p; ScalarColumn project_p; ScalarColumn releaseDate_p; ArrayColumn schedule_p; ScalarColumn scheduleType_p; ScalarColumn telescopeName_p; ArrayColumn timeRange_p; //# Access to Measure columns ScalarMeasColumn releaseDateMeas_p; ArrayMeasColumn timeRangeMeas_p; //# Access to Quantum columns ScalarQuantColumn releaseDateQuant_p; ArrayQuantColumn timeRangeQuant_p; }; //# Define the RO version for backward compatibility. typedef MSObservationColumns ROMSObservationColumns; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSObsEnums.h000066400000000000000000000067621476623553700212460ustar00rootroot00000000000000//# MSObservationEnums.h: Definitions for the MeasurementSet OBSERVATION table //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSOBSENUMS_H #define MS_MSOBSENUMS_H #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Enums for the MeasurementSet OBSERVATION table // // // // This class contains the enums for the MeasurementSet OBSERVATION table // // // This class does nothing. It is merely a container for the enumerations // used by the MSObservation class. These enumerations define the // standard columns, keywords. // // // See the documentation for MeasurementSet for examples on the use of these // enumerations. // // // All the todo items which may be related to this class are // grouped with the todo items for MeasurementSet // // class MSObservationEnums { public: // The OBSERVATION table colums with predefined meaning. // The OBSERVATION_ID is the row number . enum PredefinedColumns { // "True" columns are defined.
        // TYPE - UNIT - MEASURE UNDEFINED_COLUMN=0, // Row flag
        // Bool FLAG_ROW, // Observing log
        // String(*) LOG, // Name of observer(s)
        // String OBSERVER, // Project identification string
        // TpString PROJECT, // Release data, date when data may become public
        // Double - s - EPOCH RELEASE_DATE, // Observing schedule
        // String(*) SCHEDULE, // Observing schedule type
        // String SCHEDULE_TYPE, // Telescope name
        // TpString TELESCOPE_NAME, // Start and end times of observation
        // Double(2) TIME_RANGE, // Number of required columns NUMBER_REQUIRED_COLUMNS=TIME_RANGE, // Not a column, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_COLUMNS=NUMBER_REQUIRED_COLUMNS }; // Keywords with a predefined meaning enum PredefinedKeywords { // // "True" keywords are defined. UNDEFINED_KEYWORD=0, // // Not a keyword, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_KEYWORDS=UNDEFINED_KEYWORD }; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSObservation.cc000066400000000000000000000144371476623553700221420ustar00rootroot00000000000000//# MSObservation.cc: The MeasurementSet OBSERVATION Table //# Copyright (C) 1996,1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSObservation::MSObservation():hasBeenDestroyed_p(True) { } MSObservation::MSObservation(const String &tableName, TableOption option) : MSTable(tableName, option),hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSObservation(String &, TableOption) - " "table is not a valid MSObservation")); } MSObservation::MSObservation(const String& tableName, const String &tableDescName, TableOption option) : MSTable(tableName, tableDescName,option), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSObservation(String &, String &, TableOption) - " "table is not a valid MSObservation")); } MSObservation::MSObservation(SetupNewTable &newTab, rownr_t nrrow, Bool initialize) : MSTable(newTab, nrrow, initialize), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSObservation(SetupNewTable &, rownr_t, Bool) - " "table is not a valid MSObservation")); } MSObservation::MSObservation(const Table &table) : MSTable(table), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSObservation(const Table &) - " "table is not a valid MSObservation")); } MSObservation::MSObservation(const MSObservation &other) : MSTable(other), hasBeenDestroyed_p(False) { // verify that other is valid if (&other != this) if (! validate(this->tableDesc())) throw (AipsError("MSObservation(const MSObservation &) - " "table is not a valid MSObservation")); } MSObservation::~MSObservation() { // check to make sure that this MSObservation is still valid if (!hasBeenDestroyed_p && !validate()) { // the table is otherwise OK, so ensure that it is written if necessary this->flush(); LogIO os; os << LogIO::WARN << "~MSObservation() - Table written is not a valid MSObservation" << LogIO::POST; } hasBeenDestroyed_p = True; } MSObservation& MSObservation::operator=(const MSObservation &other) { if (&other != this) { MSTable::operator=(other); hasBeenDestroyed_p=other.hasBeenDestroyed_p; } return *this; } MSTableMaps MSObservation::initMaps() { MSTableMaps maps; // the PredefinedColumns // FLAG_ROW colMapDef(maps, FLAG_ROW,"FLAG_ROW",TpBool, "Row flag","",""); // LOG colMapDef(maps, LOG,"LOG",TpArrayString, "Observing log","",""); // OBSERVER colMapDef(maps, OBSERVER, "OBSERVER", TpString, "Name of observer(s)","",""); // PROJECT colMapDef(maps, PROJECT,"PROJECT",TpString, "Project identification string","",""); // RELEASE_DATE colMapDef(maps, RELEASE_DATE,"RELEASE_DATE",TpDouble, "Release date when data becomes public","s","Epoch"); // SCHEDULE colMapDef(maps, SCHEDULE,"SCHEDULE",TpArrayString, "Observing schedule","",""); // SCHEDULE_TYPE colMapDef(maps, SCHEDULE_TYPE,"SCHEDULE_TYPE",TpString, "Observing schedule type","",""); // TELESCOPE_NAME colMapDef(maps, TELESCOPE_NAME,"TELESCOPE_NAME",TpString, "Telescope Name (e.g. WSRT, VLBA)"); // TIME_RANGE colMapDef(maps, TIME_RANGE,"TIME_RANGE",TpArrayDouble, "Start and end of observation","s","Epoch"); // PredefinedKeywords // init requiredTableDesc // all required keywords // Define the columns with fixed size arrays IPosition shape(1,2); ColumnDesc::Option option=ColumnDesc::Direct; addColumnToDesc(maps, TIME_RANGE, shape, option); // Define the columns with known dimensionality addColumnToDesc(maps, LOG, 1); addColumnToDesc(maps, SCHEDULE, 1); for (Int i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(maps, PredefinedKeywords(i)); } // all required columns for (Int i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(maps, PredefinedColumns(i)); } return maps; } MSObservation MSObservation::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSObservation(MSTable::referenceCopy (newTableName,writableColumns)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSObservation.h000066400000000000000000000116031476623553700217740ustar00rootroot00000000000000//# MSObservation.h: The MeasurementSet OBSERVATION Table //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSOBSERVATION_H #define MS_MSOBSERVATION_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A Table intended to hold a MeasurementSet OBSERVATION table. // // // // //
      • MeasurementSet //
      • MSTable // // // // MSObservation stands for the MeasurementSet Observation table. // // // // An MSObservation is a table intended to hold the OBSERVATION table for // the MeasurementSet. It has an identical set of member functions as // the main MeasurementSet class, except (currently) for the default // calibration members. For further info and examples see the // MeasurementSet class. // // // // See the MeasurementSet for an example of how to access and use this class. // // // // It was found that subtables and the main table of the MeasurementSet have // a lot in common, therefore they derive their interface from the same // base class. Each subtable has its own class to keep the enum definitions // and conversion functions in separate scopes. // // // // see MeasurementSet. // class MSObservation:public MSObservationEnums, public MSTable { public: // This constructs an empty MSObservation MSObservation (); // These constructors mirror the Table ones with additional checking // on validity (verifying that the MSObservation will have the required columns // and keywords) // An exception is thrown if the constructed Table is not a valid MSObservation // //
      • AipsError // // MSObservation (const String &tableName, TableOption = Table::Old); MSObservation (const String &tableName, const String &tableDescName, TableOption = Table::Old); MSObservation (SetupNewTable &newTab, rownr_t nrrow = 0, Bool initialize = False); MSObservation (const Table &table); MSObservation (const MSObservation &other); // // As with tables, the destructor writes the table if necessary. // Additional checking is done here to verify that all required // columns are still present. // If it is NOT valid, it will write the table and then throw an exception. // //
      • AipsError // ~MSObservation(); // Assignment operator, reference semantics MSObservation& operator=(const MSObservation&); // Make a special copy of this Table which references all columns from // this Table except those mentioned; those are empty and writable. // Each forwarded column has the same writable status as the underlying // column. The mentioned columns all use the AipsIO storage manager. // This function is inherited from MSTable and unlikely to be of use, // except in the class MeasurementSet (see comment there).. MSObservation referenceCopy(const String& newTableName, const Block& writableColumns) const; // Initialize the statics appropriately. This does not need to be // called by users, it is called by the implementation class // MSTableImpl. static MSTableMaps initMaps(); private: // required by the need to throw an exception in the destructor Bool hasBeenDestroyed_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSPointing.cc000066400000000000000000000155261476623553700214360ustar00rootroot00000000000000//# MSPointing.cc: The MeasurementSet POINTING Table //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSPointing::MSPointing():hasBeenDestroyed_p(True) { } MSPointing::MSPointing(const String &tableName, TableOption option) : MSTable(tableName, option),hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSPointing(String &, TableOption) - " "table is not a valid MSPointing")); } MSPointing::MSPointing(const String& tableName, const String &tableDescName, TableOption option) : MSTable(tableName, tableDescName,option), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSPointing(String &, String &, TableOption) - " "table is not a valid MSPointing")); } MSPointing::MSPointing(SetupNewTable &newTab, rownr_t nrrow, Bool initialize) : MSTable(newTab, nrrow, initialize), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSPointing(SetupNewTable &, rownr_t, Bool) - " "table is not a valid MSPointing")); } MSPointing::MSPointing(const Table &table) : MSTable(table), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSPointing(const Table &) - " "table is not a valid MSPointing")); } MSPointing::MSPointing(const MSPointing &other) : MSTable(other), hasBeenDestroyed_p(False) { // verify that other is valid if (&other != this) if (! validate(this->tableDesc())) throw (AipsError("MSPointing(const MSPointing &) - " "table is not a valid MSPointing")); } MSPointing::~MSPointing() { // check to make sure that this MSPointing is still valid if (!hasBeenDestroyed_p && !validate()) { // the table is otherwise OK, so ensure that it is written if necessary this->flush(); LogIO os; os << LogIO::WARN << "~MSPointing() - Table written is not a valid MSPointing" << LogIO::POST; } hasBeenDestroyed_p = True; } MSPointing& MSPointing::operator=(const MSPointing &other) { if (&other != this) { MSTable::operator=(other); hasBeenDestroyed_p=other.hasBeenDestroyed_p; } return *this; } MSTableMaps MSPointing::initMaps() { MSTableMaps maps; // the PredefinedColumns // ANTENNA_ID colMapDef(maps, ANTENNA_ID, "ANTENNA_ID", TpInt, "Antenna Id","",""); // DIRECTION colMapDef(maps, DIRECTION, "DIRECTION", TpArrayDouble, "Antenna pointing direction as polynomial in time","rad" ,"Direction"); // INTERVAL colMapDef(maps, INTERVAL, "INTERVAL", TpDouble, "Time interval","s",""); // NAME colMapDef(maps, NAME, "NAME", TpString, "Pointing position name","",""); // NUM_POLY colMapDef(maps, NUM_POLY, "NUM_POLY", TpInt, "Series order","",""); // TARGET colMapDef(maps, TARGET, "TARGET", TpArrayDouble, "target direction as polynomial in time","rad" ,"Direction"); // TIME colMapDef(maps, TIME, "TIME", TpDouble, "Time interval midpoint","s","Epoch"); // TIME_ORIGIN colMapDef(maps, TIME_ORIGIN, "TIME_ORIGIN", TpDouble, "Time origin for direction","s","Epoch"); // TRACKING colMapDef(maps, TRACKING, "TRACKING", TpBool, "Tracking flag - True if on position","",""); // ENCODER colMapDef(maps, ENCODER, "ENCODER", TpArrayDouble, "Encoder values","rad","Direction"); // ON_SOURCE colMapDef(maps, ON_SOURCE, "ON_SOURCE", TpBool, "On source flag","",""); // OVER_THE_TOP colMapDef(maps, OVER_THE_TOP, "OVER_THE_TOP", TpBool, "Antenna over the top","",""); // POINTING_MODEL_ID colMapDef(maps, POINTING_MODEL_ID,"POINTING_MODEL_ID",TpInt, "Pointing model id","",""); // POINTING_OFFSET colMapDef(maps, POINTING_OFFSET, "POINTING_OFFSET", TpArrayDouble, "A priori pointing correction as polynomial in time", "rad","Direction"); // SOURCE_OFFSET colMapDef(maps, SOURCE_OFFSET, "SOURCE_OFFSET", TpArrayDouble, "Offset from source as polynomial in time","rad","Direction"); // PredefinedKeywords // init requiredTableDesc // all required keywords // First define the columns with known dimensionality addColumnToDesc(maps, DIRECTION, 2); uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(maps, PredefinedKeywords(i)); } // all required columns // Now define all other columns (duplicates are skipped) for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(maps, PredefinedColumns(i)); } return maps; } MSPointing MSPointing::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSPointing(MSTable:: referenceCopy(newTableName,writableColumns)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSPointing.h000066400000000000000000000114471476623553700212760ustar00rootroot00000000000000//# MSPointing.h: The MeasurementSet POINTING Table //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSPOINTING_H #define MS_MSPOINTING_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A Table intended to hold a MeasurementSet POINTING table. // // // // //
      • MeasurementSet //
      • MSTable // // // // MSPointing stands for the MeasurementSet Pointing table. // // // // An MSPointing is a table intended to hold the POINTING table for // the MeasurementSet. It has an identical set of member functions as // the main MeasurementSet class, except (currently) for the default // calibration members. For further info and examples see the // MeasurementSet class. // // // // See the MeasurementSet for an example of how to access and use this class. // // // // It was found that subtables and the main table of the MeasurementSet have // a lot in common, therefore they derive their interface from the same // base class. Each subtable has its own class to keep the enum definitions // and conversion functions in separate scopes. // // // // see MeasurementSet. // class MSPointing:public MSPointingEnums, public MSTable { public: // This constructs an empty MSPointing. MSPointing (); // These constructors mirror the Table ones with additional checking // on validity (verifying that the MSPointing will have the required columns // and keywords) // An exception is thrown if the constructed Table is not a valid MSPointing // //
      • AipsError // // MSPointing (const String &tableName, TableOption = Table::Old); MSPointing (const String &tableName, const String &tableDescName, TableOption = Table::Old); MSPointing (SetupNewTable &newTab, rownr_t nrrow = 0, Bool initialize = False); MSPointing (const Table &table); MSPointing (const MSPointing &other); // // As with tables, the destructor writes the table if necessary. // Additional checking is done here to verify that all required // columns are still present. // If it is NOT valid, it will write the table and then throw an exception. // //
      • AipsError // ~MSPointing(); // Assignment operator, reference semantics MSPointing& operator=(const MSPointing&); // Make a special copy of this Table which references all columns from // this Table except those mentioned; those are empty and writable. // Each forwarded column has the same writable status as the underlying // column. The mentioned columns all use the AipsIO storage manager. // This function is inherited from MSTable and unlikely to be of use, // except in the class MeasurementSet (see comment there).. MSPointing referenceCopy(const String& newTableName, const Block& writableColumns) const; // Initialize the statics appropriately. This does not need to be // called by users, it is called by the implementation class // MSTableImpl. static MSTableMaps initMaps(); private: // required by the need to throw an exception in the destructor Bool hasBeenDestroyed_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSPointingColumns.cc000066400000000000000000000170761476623553700230010ustar00rootroot00000000000000//# MSPointingColumns.cc: provides easy access to MeasurementSet columns //# Copyright (C) 1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSPointingColumns::MSPointingColumns() { } MSPointingColumns::MSPointingColumns(const MSPointing& msPointing) { attach(msPointing); } MSPointingColumns::~MSPointingColumns() {} void MSPointingColumns::attach(const MSPointing& msPointing) { antennaId_p.attach(msPointing, MSPointing:: columnName(MSPointing::ANTENNA_ID)); direction_p.attach(msPointing, MSPointing:: columnName(MSPointing::DIRECTION)); interval_p.attach(msPointing, MSPointing:: columnName(MSPointing::INTERVAL)); name_p.attach(msPointing, MSPointing::columnName(MSPointing::NAME)); numPoly_p.attach(msPointing, MSPointing:: columnName(MSPointing::NUM_POLY)); target_p.attach(msPointing, MSPointing:: columnName(MSPointing::TARGET)); time_p.attach(msPointing, MSPointing::columnName(MSPointing::TIME)); timeOrigin_p.attach(msPointing, MSPointing:: columnName(MSPointing::TIME_ORIGIN)); tracking_p.attach(msPointing, MSPointing:: columnName(MSPointing::TRACKING)); directionMeas_p.attach(msPointing, MSPointing:: columnName(MSPointing::DIRECTION)); targetMeas_p.attach(msPointing, MSPointing:: columnName(MSPointing::TARGET)); timeMeas_p.attach(msPointing, MSPointing:: columnName(MSPointing::TIME)); timeOriginMeas_p.attach(msPointing, MSPointing:: columnName(MSPointing::TIME_ORIGIN)); intervalQuant_p.attach(msPointing, MSPointing:: columnName(MSPointing::INTERVAL)); timeQuant_p.attach(msPointing, MSPointing:: columnName(MSPointing::TIME)); timeOriginQuant_p.attach(msPointing, MSPointing:: columnName(MSPointing::TIME_ORIGIN)); attachOptionalCols(msPointing); } void MSPointingColumns::attachOptionalCols(const MSPointing& msPointing) { const ColumnDescSet& cds = msPointing.tableDesc().columnDescSet(); const String& encoder = MSPointing::columnName(MSPointing::ENCODER); if (cds.isDefined(encoder)) { encoder_p.attach(msPointing, encoder); encoderMeas_p.attach(msPointing, encoder); } const String& onSource = MSPointing::columnName(MSPointing::ON_SOURCE); if (cds.isDefined(onSource)) onSource_p.attach(msPointing, onSource); const String& pointingModelId = MSPointing::columnName(MSPointing::POINTING_MODEL_ID); if (cds.isDefined(pointingModelId)) { pointingModelId_p.attach(msPointing, pointingModelId); } const String& pointingOffset = MSPointing:: columnName(MSPointing::POINTING_OFFSET); if (cds.isDefined(pointingOffset)) { pointingOffset_p.attach(msPointing, pointingOffset); pointingOffsetMeas_p.attach(msPointing, pointingOffset); } const String& sourceOffset = MSPointing:: columnName(MSPointing::SOURCE_OFFSET); if (cds.isDefined(sourceOffset)) { sourceOffset_p.attach(msPointing, sourceOffset); sourceOffsetMeas_p.attach(msPointing, sourceOffset); } const String& overTheTop = MSPointing::columnName(MSPointing::OVER_THE_TOP); if (cds.isDefined(overTheTop)) overTheTop_p.attach(msPointing, overTheTop); } MDirection MSPointingColumns::directionMeas(rownr_t row, Double interTime) const { return MSFieldColumns::interpolateDirMeas(directionMeasCol()(row), numPoly()(row), interTime, time()(row)); } MDirection MSPointingColumns::targetMeas(rownr_t row, Double interTime) const { return MSFieldColumns::interpolateDirMeas(targetMeasCol()(row), numPoly()(row), interTime, time()(row)); } MDirection MSPointingColumns::pointingOffsetMeas(rownr_t row, Double interTime) const { if (pointingOffsetMeasCol().isNull()) return MDirection(); return MSFieldColumns::interpolateDirMeas(pointingOffsetMeasCol()(row), numPoly()(row), interTime, time()(row)); } MDirection MSPointingColumns::sourceOffsetMeas(rownr_t row, Double interTime) const { if (sourceOffsetMeasCol().isNull()) return MDirection(); return MSFieldColumns::interpolateDirMeas(sourceOffsetMeasCol()(row), numPoly()(row), interTime, time()(row)); } Int64 MSPointingColumns::pointingIndex(Int antenna, Double ptime, Int64 guessRow) const { if((this->nrow()) < 1) return -1; // return the first row matching the requirements const Int64 nrow = antennaId().nrow(); //take up from where we left last time //hopefully time is monotonic //otherwise it will go through the table each time if(guessRow <0) guessRow=0; for (Int k=0; k< 2; ++k){ Int64 start=guessRow; Int64 end=nrow; if(k==1){ start=0; end=guessRow; } for (Int64 i=start; i0.0) { if (time()(i) >= ptime - halfInt && time()(i) <= ptime + halfInt) { return i; } } else { // valid for all times (we should also handle interval<0 -> timestamps) return i; } } } } return -1; } void MSPointingColumns:: setEpochRef(MEpoch::Types ref, Bool tableMustBeEmpty) { timeMeas_p.setDescRefCode(ref, tableMustBeEmpty); timeOriginMeas_p.setDescRefCode(ref, tableMustBeEmpty); } void MSPointingColumns::setDirectionRef(MDirection::Types ref) { directionMeas_p.setDescRefCode(ref); targetMeas_p.setDescRefCode(ref); if (!pointingOffsetMeas_p.isNull()) { pointingOffsetMeas_p.setDescRefCode(ref); } if (!sourceOffsetMeas_p.isNull()) { sourceOffsetMeas_p.setDescRefCode(ref); } } void MSPointingColumns::setEncoderDirectionRef(MDirection::Types ref) { if (!encoderMeas_p.isNull()) { encoderMeas_p.setDescRefCode(ref); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSPointingColumns.h000066400000000000000000000260231476623553700226330ustar00rootroot00000000000000//# MSPointingColumns.h: provides easy access to MSPointing columns //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSPOINTINGCOLUMNS_H #define MS_MSPOINTINGCOLUMNS_H #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class MSPointing; // // A class to provide easy access to MSPointing columns // // // // // //
      • MSPointing //
      • ArrayColumn //
      • ScalarColumn // // // // MSPointingColumns stands for MeasurementSet Pointing Table columns. // // // // This class provides access to the columns in the MSPointing Table, // it does the declaration of all the Scalar and ArrayColumns with the // correct types, so the application programmer doesn't have to // worry about getting those right. There is an access function // for every predefined column. Access to non-predefined columns will still // have to be done with explicit declarations. // See MSColumns for an example. // // // // See MSColumns for the motivation. // class MSPointingColumns { public: // Construct from the supplied Table MSPointingColumns(const MSPointing& msPointing); // The destructor does nothing special ~MSPointingColumns(); // Access to required columns // // Note that the direction measures with a stored polynomial have Col() added // to their name. They are better accessed via the functions that have the // same name, without the Col suffix, that will do the interpolation for // you. // ScalarColumn& antennaId() {return antennaId_p;} ScalarColumn& time() {return time_p;} ScalarMeasColumn& timeMeas() {return timeMeas_p;} ScalarQuantColumn& timeQuant() {return timeQuant_p;} ScalarColumn& interval() {return interval_p;} ScalarQuantColumn& intervalQuant() {return intervalQuant_p;} ScalarColumn& name() {return name_p;} ScalarColumn& numPoly() {return numPoly_p;} ScalarColumn& timeOrigin() {return timeOrigin_p;} ScalarQuantColumn& timeOriginQuant() {return timeOriginQuant_p;} ScalarMeasColumn& timeOriginMeas() {return timeOriginMeas_p;} ArrayColumn& direction() {return direction_p;} ArrayMeasColumn& directionMeasCol() {return directionMeas_p;} ArrayColumn& target() {return target_p;} ArrayMeasColumn& targetMeasCol() {return targetMeas_p;} ScalarColumn& tracking() {return tracking_p;} // // Access to optional columns // // Note that the direction measures with a stored polynomial have Col() added // to their name. They are better accessed via the functions that have the // same name, without the Col suffix, that will do the interpolation for // you. // ArrayColumn& pointingOffset() {return pointingOffset_p;} ArrayMeasColumn& pointingOffsetMeasCol() { return pointingOffsetMeas_p;} ArrayColumn& sourceOffset() {return sourceOffset_p;} ArrayMeasColumn& sourceOffsetMeasCol() { return sourceOffsetMeas_p;} ArrayColumn& encoder() {return encoder_p;} ScalarMeasColumn& encoderMeas() {return encoderMeas_p;} ScalarColumn& pointingModelId() {return pointingModelId_p;} ScalarColumn& onSource() {return onSource_p;} ScalarColumn& overTheTop() {return overTheTop_p;} // // Const access to required columns // const ScalarColumn& antennaId() const {return antennaId_p;} const ScalarColumn& time() const {return time_p;} const ScalarQuantColumn& timeQuant() const {return timeQuant_p;} const ScalarMeasColumn& timeMeas() const {return timeMeas_p;} const ScalarColumn& interval() const {return interval_p;} const ScalarQuantColumn& intervalQuant() const { return intervalQuant_p;} const ScalarColumn& name() const {return name_p;} const ScalarColumn& numPoly() const {return numPoly_p;} const ScalarColumn& timeOrigin() const {return timeOrigin_p;} const ScalarQuantColumn& timeOriginQuant() const { return timeOriginQuant_p;} const ScalarMeasColumn& timeOriginMeas() const { return timeOriginMeas_p;} const ArrayColumn& direction() const {return direction_p;} const ArrayMeasColumn& directionMeasCol() const { return directionMeas_p;} const ArrayColumn& target() const {return target_p;} const ArrayMeasColumn& targetMeasCol()const { return targetMeas_p;} const ScalarColumn& tracking() const {return tracking_p;} // // Access to optional columns // const ArrayColumn& pointingOffset() const { return pointingOffset_p;} const ArrayMeasColumn& pointingOffsetMeasCol() const { return pointingOffsetMeas_p;} const ArrayColumn& sourceOffset() const {return sourceOffset_p;} const ArrayMeasColumn& sourceOffsetMeasCol() const { return sourceOffsetMeas_p;} const ArrayColumn& encoder() const {return encoder_p;} const ScalarMeasColumn& encoderMeas() const { return encoderMeas_p;} const ScalarColumn& pointingModelId() const { return pointingModelId_p;} const ScalarColumn& onSource() const {return onSource_p;} const ScalarColumn& overTheTop() const {return overTheTop_p;} // // Access to interpolated directions, the default time of zero will // return the 0th order element of the polynomial. // MDirection directionMeas(rownr_t row, Double time = 0) const; MDirection targetMeas(rownr_t row, Double time = 0) const; MDirection pointingOffsetMeas(rownr_t row, Double time = 0) const; MDirection sourceOffsetMeas(rownr_t row, Double time = 0) const; // // return the first matching row index for this time and antenna, // returns -1 if no match was found // For long tables you may give a guess row...the last return // is usually a good one. Int64 pointingIndex(Int antenna, Double time, Int64 guessRow=0) const; // Convenience function that returns the number of rows in any of the columns rownr_t nrow() const {return antennaId_p.nrow();} // set the epoch reference type for the TIME & TIME_ORIGIN column. // // In principle this function can only be used if the table is empty, // otherwise already written values may thereafter have an incorrect // reference, offset, or unit. However, it is possible that part of the // table gets written before these values are known. In that case the // reference, offset, or units can be set by using a False // tableMustBeEmpty argument. // void setEpochRef(MEpoch::Types ref, Bool tableMustBeEmpty=True); // set the direction reference type for the DIRECTION, TARGET & and, if // defined, the SOURCE_OFFSET & POINTING_OFFSET columns. This can only be // done when the table has no rows. Trying to do so at other times will throw // an exception. Note that the optional ENCODER column must be done // separately as the MSv2 definition requires this column to use the frame(s) // of the antenna mounts. void setDirectionRef(MDirection::Types ref); // set the direction reference type for the ENCODER column (if it is defined). // This can only be done when the table has no rows. Trying to do so at other // times will throw an exception. void setEncoderDirectionRef(MDirection::Types ref); protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. MSPointingColumns(); //# attach this object to the supplied table. void attach(const MSPointing& msPointing); private: //# Make the assignment operator and the copy constructor private to prevent //# any compiler generated one from being used. MSPointingColumns(const MSPointingColumns&); MSPointingColumns& operator=(const MSPointingColumns&); //# Check if any optional columns exist and if so attach them. void attachOptionalCols(const MSPointing& msPointing); //# required columns ScalarColumn antennaId_p; ArrayColumn direction_p; ScalarColumn interval_p; ScalarColumn name_p; ScalarColumn numPoly_p; ArrayColumn target_p; ScalarColumn time_p; ScalarColumn timeOrigin_p; ScalarColumn tracking_p; //# optional columns ArrayColumn encoder_p; ScalarColumn onSource_p; ScalarColumn pointingModelId_p; ArrayColumn pointingOffset_p; ArrayColumn sourceOffset_p; ScalarColumn overTheTop_p; //# Access to Measure columns ArrayMeasColumn directionMeas_p; ArrayMeasColumn targetMeas_p; ScalarMeasColumn timeMeas_p; ScalarMeasColumn timeOriginMeas_p; //# optional Measure columns ScalarMeasColumn encoderMeas_p; ArrayMeasColumn pointingOffsetMeas_p; ArrayMeasColumn sourceOffsetMeas_p; //# Access to Quantum columns ScalarQuantColumn intervalQuant_p; ScalarQuantColumn timeQuant_p; ScalarQuantColumn timeOriginQuant_p; }; //# Define the RO version for backward compatibility. typedef MSPointingColumns ROMSPointingColumns; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSPointingEnums.h000066400000000000000000000101031476623553700222720ustar00rootroot00000000000000//# MSPointingEnums.h: Definitions for the MeasurementSet POINTING table //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSPOINTINGENUMS_H #define MS_MSPOINTINGENUMS_H #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Enums for the MeasurementSet POINTING table // // // // This class contains the enums for the MeasurementSet POINTING table // // // This class does nothing. It is merely a container for the enumerations // used by the MSPointing class. These enumerations define the // standard columns and keywords. // // // See the documentation for MeasurementSet for examples on the use of these // enumerations. // // // All the todo items which may be related to this class are // grouped with the todo items for MeasurementSet // // class MSPointingEnums { public: // The POINTING table colums with predefined meaning. // Keys: ANTENNA_ID, TIME, INTERVAL enum PredefinedColumns { // "True" columns are defined.
        // TYPE - UNIT - MEASURE UNDEFINED_COLUMN=0, // Antenna id
        // Int ANTENNA_ID, // Antenna pointing direction (e.g. RA, DEC) as polynomial in time.
        // Double(2,NUM_POLY+1) - rad - DIRECTION. DIRECTION, // Time interval
        // Double - s INTERVAL, // Pointing Name.
        // String NAME, // Polynomial order for *_DIR columns
        // Int NUM_POLY, // Target direction
        // Double(2,NUM_POLY+1) - rad - DIRECTION TARGET, // Time midpoint for interval.
        // Double - s - EPOCH TIME, // Time origin for the directions and rates.
        // Double - s - EPOCH TIME_ORIGIN, // Track flag - true if on position
        // Bool TRACKING, // Number of required columns
        NUMBER_REQUIRED_COLUMNS=TRACKING, // Encoder values
        // Double(2) ENCODER, // On source flag - true if on source
        // Bool ON_SOURCE, // Over the top flag - true if antenna has been driven over the top
        // Bool OVER_THE_TOP, // Pointing model id
        // Int POINTING_MODEL_ID, // Pointing offset as polynomial in time
        // Double(2,NUM_POLY+1) - rad - DIRECTION. POINTING_OFFSET, // Offset from source as polynomial in time
        // Double(2,NUM_POLY+1) - rad - DIRECTION. SOURCE_OFFSET, // Not a column, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_COLUMNS=SOURCE_OFFSET }; // Keywords with a predefined meaning enum PredefinedKeywords { // // "True" keywords are defined. UNDEFINED_KEYWORD=0, // // Not a keyword, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_KEYWORDS=0 }; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSPolColumns.cc000066400000000000000000000072341476623553700217370ustar00rootroot00000000000000//# MSPolarizationColumns.cc: provides easy access to MeasurementSet columns //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSPolarizationColumns::MSPolarizationColumns() { } MSPolarizationColumns:: MSPolarizationColumns(const MSPolarization& msPolarization) { attach(msPolarization); } MSPolarizationColumns::~MSPolarizationColumns() {} void MSPolarizationColumns:: attach(const MSPolarization& msPolarization) { corrProduct_p.attach(msPolarization, MSPolarization:: columnName(MSPolarization::CORR_PRODUCT)); corrType_p.attach(msPolarization, MSPolarization:: columnName(MSPolarization::CORR_TYPE)); flagRow_p.attach(msPolarization, MSPolarization:: columnName(MSPolarization::FLAG_ROW)); numCorr_p.attach(msPolarization, MSPolarization:: columnName(MSPolarization::NUM_CORR)); } Int64 MSPolarizationColumns:: match(const Vector& polType, Int64 tryRow) { rownr_t r = nrow(); if (r == 0) return -1; // Convert the corrType to Integers. const Int nCorr = polType.nelements(); Vector polInt(nCorr); for (Int p = 0; p < nCorr; p++) { polInt(p) = polType(p); } // Main matching loop if (tryRow >= 0) { const rownr_t tr = tryRow; if (tr >= r) { throw(AipsError("MSPolarszationColumns::match(...) - " "row " + String::toString(tr) + " you suggest is too big")); } if (!flagRow()(tr) && numCorr()(tr) == nCorr && matchCorrType(tr, polInt)) { return tr; } if (tr == r-1) r--; } while (r > 0) { r--; if (!flagRow()(r) && numCorr()(r) == nCorr && matchCorrType(r, polInt)) { return r; } } return -1; } Bool MSPolarizationColumns:: matchCorrType(rownr_t row, const Vector& polType) const { DebugAssert(row < nrow(), AipsError); return allEQ(corrType()(row), polType); } Bool MSPolarizationColumns:: matchCorrProduct(rownr_t row, const Matrix& polProduct) const { DebugAssert(row < nrow(), AipsError); // The static cast is a work around for an SGI compiler Bug return allEQ(corrProduct()(row), static_cast< const Matrix &>(polProduct)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSPolColumns.h000066400000000000000000000121441476623553700215750ustar00rootroot00000000000000//# MSPolColumns.h: provides easy access to MSPolarization columns //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSPOLCOLUMNS_H #define MS_MSPOLCOLUMNS_H #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class MSPolarization; // // A class to provide easy access to MSPolarization columns // // // // // //
      • MSPolarization //
      • ArrayColumn //
      • ScalarColumn // // // // MSPolarizationColumns stands for MeasurementSet Polarization Table columns. // // // // This class provides access to the columns in the MSPolarization Table, // it does the declaration of all the Scalar and ArrayColumns with the // correct types, so the application programmer doesn't have to // worry about getting those right. There is an access function // for every predefined column. Access to non-predefined columns will still // have to be done with explicit declarations. // See MSColumns for an example. // // // // See MSColumns for the motivation. // class MSPolarizationColumns { public: // Create a columns object that accesses the data in the specified Table MSPolarizationColumns(const MSPolarization& msPolarization); // The destructor does nothing special ~MSPolarizationColumns(); // Access to required columns // ArrayColumn& corrProduct() {return corrProduct_p;} ArrayColumn& corrType() {return corrType_p;} ScalarColumn& flagRow() {return flagRow_p;} ScalarColumn& numCorr() {return numCorr_p;} // // Const access to required columns // const ArrayColumn& corrProduct() const {return corrProduct_p;} const ArrayColumn& corrType() const {return corrType_p;} const ScalarColumn& flagRow() const {return flagRow_p;} const ScalarColumn& numCorr() const {return numCorr_p;} // // Convenience function that returns the number of rows in any of the columns rownr_t nrow() const {return corrProduct_p.nrow();} // returns the last row that contains the an entry in the CORR_TYPE column // that matches, in length and value, the supplied corrType Vector. Returns // -1 if no match could be found. Flagged rows can never match. If tryRow is // non-negative, then that row is tested to see if it matches before any // others are tested. Setting tryRow to a positive value greater than the // table length will throw an exception (AipsError), when compiled in debug // mode. Int64 match(const Vector& polType, Int64 tryRow=-1); protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. MSPolarizationColumns(); //# attach this object to the supplied table. void attach(const MSPolarization& msPolarization); private: //# Make the assignment operator and the copy constructor private to prevent //# any compiler generated one from being used. MSPolarizationColumns(const MSPolarizationColumns&); MSPolarizationColumns& operator=(const MSPolarizationColumns&); //# Functions which check the supplied values against the relevant column and //# the specified row. Bool matchCorrType(rownr_t row, const Vector& polType) const; Bool matchCorrProduct(rownr_t row, const Matrix& polProduct) const; //# required columns ArrayColumn corrProduct_p; ArrayColumn corrType_p; ScalarColumn flagRow_p; ScalarColumn numCorr_p; }; //# Define the RO version for backward compatibility. typedef MSPolarizationColumns ROMSPolarizationColumns; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSPolEnums.h000066400000000000000000000065631476623553700212540ustar00rootroot00000000000000//# MSPolarizationEnums.h: Definitions for the MeasurementSet POLARIZATION table //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSPOLENUMS_H #define MS_MSPOLENUMS_H #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Enums for the MeasurementSet POLARIZATION table // // // // This class contains the enums for the MeasurementSet POLARIZATION table // // // This class does nothing. It is merely a container for the enumerations // used by the MSPolarization class. These enumerations define the // standard columns and keywords. // // // See the documentation for MeasurementSet for examples on the use of these // enumerations. // // // All the todo items which may be related to this class are // grouped with the todo items for MeasurementSet // // class MSPolarizationEnums { public: // The POLARIZATION table colums with predefined meaning. // Keys: POLARIZATION_ID is rownumber enum PredefinedColumns { // "True" columns are defined.
        // TYPE - UNIT - MEASURE UNDEFINED_COLUMN=0, // A pair of integers for each correlation product specifying the receptors // from which the signal originated. E.g., (0,1) = receptor 0 on feed1 and // receptor 1 on feed2. This is unused for I,Q,U,V data.
        // Int(2, NUM_CORR) CORR_PRODUCT, // The polarization type for each correlation product, as a Stokes enum.
        // Int(NUM_CORR) CORR_TYPE, // Row flag
        // Bool FLAG_ROW, // Number of correlations.
        // Int NUM_CORR, // Number of required columns
        NUMBER_REQUIRED_COLUMNS=NUM_CORR, // Not a column, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_COLUMNS=NUMBER_REQUIRED_COLUMNS }; // Keywords with a predefined meaning enum PredefinedKeywords { // // "True" keywords are defined. UNDEFINED_KEYWORD=0, // // Not a keyword, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_KEYWORDS=0 }; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSPolarization.cc000066400000000000000000000134131476623553700223130ustar00rootroot00000000000000//# MSPolarization.cc: The MeasurementSet POLARIZATION Table //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSPolarization::MSPolarization():hasBeenDestroyed_p(True) { } MSPolarization::MSPolarization(const String &tableName, TableOption option) : MSTable(tableName, option),hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSPolarization(String &, TableOption) - " "table is not a valid MSPolarization")); } MSPolarization::MSPolarization(const String& tableName, const String &tableDescName, TableOption option) : MSTable(tableName, tableDescName,option), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSPolarization(String &, String &, TableOption) - " "table is not a valid MSPolarization")); } MSPolarization::MSPolarization(SetupNewTable &newTab, rownr_t nrrow, Bool initialize) : MSTable(newTab, nrrow, initialize), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSPolarization(SetupNewTable &, rownr_t, Bool) - " "table is not a valid MSPolarization")); } MSPolarization::MSPolarization(const Table &table) : MSTable(table), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSPolarization(const Table &) - " "table is not a valid MSPolarization")); } MSPolarization::MSPolarization(const MSPolarization &other) : MSTable(other), hasBeenDestroyed_p(False) { // verify that other is valid if (&other != this) if (! validate(this->tableDesc())) throw (AipsError("MSPolarization(const MSPolarization &) - " "table is not a valid MSPolarization")); } MSPolarization::~MSPolarization() { // check to make sure that this MSPolarization is still valid if (!hasBeenDestroyed_p && !validate()) { // the table is otherwise OK, so ensure that it is written if necessary this->flush(); LogIO os; os << LogIO::WARN << "~MSPolarization() - Table written is not a valid MSPolarization" << LogIO::POST; } hasBeenDestroyed_p = True; } MSPolarization& MSPolarization::operator=(const MSPolarization &other) { if (&other != this) { MSTable::operator=(other); hasBeenDestroyed_p=other.hasBeenDestroyed_p; } return *this; } MSTableMaps MSPolarization::initMaps() { MSTableMaps maps; // the PredefinedColumns // CORR_PRODUCT colMapDef(maps, CORR_PRODUCT, "CORR_PRODUCT", TpArrayInt, "Indices describing receptors of feed going into correlation","",""); // CORR_TYPE colMapDef(maps, CORR_TYPE, "CORR_TYPE", TpArrayInt, "The polarization type for each correlation product," " as a Stokes enum.","",""); // FLAG_ROW colMapDef(maps, FLAG_ROW, "FLAG_ROW", TpBool, "Row flag","",""); // NUM_CORR colMapDef(maps, NUM_CORR, "NUM_CORR", TpInt, "Number of correlation products","",""); // PredefinedKeywords // init requiredTableDesc // all required keywords uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(maps, PredefinedKeywords(i)); } // all required columns // First define the columns with known dimensionality addColumnToDesc(maps, CORR_TYPE, 1); addColumnToDesc(maps, CORR_PRODUCT, 2); // Now define all other columns (duplicates are skipped) for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(maps, PredefinedColumns(i)); } return maps; } MSPolarization MSPolarization::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSPolarization(MSTable:: referenceCopy(newTableName,writableColumns)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSPolarization.h000066400000000000000000000116411476623553700221560ustar00rootroot00000000000000//# MSPolarization.h: The MeasurementSet POLARIZATION Table //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSPOLARIZATION_H #define MS_MSPOLARIZATION_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A Table intended to hold a MeasurementSet POLARIZATION table. // // // // //
      • MeasurementSet //
      • MSTable // // // // MSPolarization stands for the MeasurementSet Polarization table. // // // // An MSPolarization is a table intended to hold the POLARIZATION table for // the MeasurementSet. It has an identical set of member functions as // the main MeasurementSet class, except (currently) for the default // calibration members. For further info and examples see the // MeasurementSet class. // // // // See the MeasurementSet for an example of how to access and use this class. // // // // It was found that subtables and the main table of the MeasurementSet have // a lot in common, therefore they derive their interface from the same // base class. Each subtable has its own class to keep the enum definitions // and conversion functions in separate scopes. // // // // see MeasurementSet. // class MSPolarization:public MSPolarizationEnums, public MSTable { public: // This constructs an empty MSPolarization. MSPolarization (); // These constructors mirror the Table ones with additional checking // on validity (verifying that the MSPolarization will have the required columns // and keywords) // An exception is thrown if the constructed Table is not a valid MSPolarization // //
      • AipsError // // MSPolarization (const String &tableName, TableOption = Table::Old); MSPolarization (const String &tableName, const String &tableDescName, TableOption = Table::Old); MSPolarization (SetupNewTable &newTab, rownr_t nrrow = 0, Bool initialize = False); MSPolarization (const Table &table); MSPolarization (const MSPolarization &other); // // As with tables, the destructor writes the table if necessary. // Additional checking is done here to verify that all required // columns are still present. // If it is NOT valid, it will write the table and then throw an exception. // //
      • AipsError // ~MSPolarization(); // Assignment operator, reference semantics MSPolarization& operator=(const MSPolarization&); // Make a special copy of this Table which references all columns from // this Table except those mentioned; those are empty and writable. // Each forwarded column has the same writable status as the underlying // column. The mentioned columns all use the AipsIO storage manager. // This function is inherited from MSTable and unlikely to be of use, // except in the class MeasurementSet (see comment there).. MSPolarization referenceCopy(const String& newTableName, const Block& writableColumns) const; // Initialize the statics appropriately. This does not need to be // called by users, it is called by the implementation class // MSTableImpl. static MSTableMaps initMaps(); private: // required by the need to throw an exception in the destructor Bool hasBeenDestroyed_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSProcessor.cc000066400000000000000000000130331476623553700216150ustar00rootroot00000000000000//# MSProcessor.cc: The MeasurementSet PROCESSOR Table //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSProcessor::MSProcessor():hasBeenDestroyed_p(True) { } MSProcessor::MSProcessor(const String &tableName, TableOption option) : MSTable(tableName, option),hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSProcessor(String &, TableOption) - " "table is not a valid MSProcessor")); } MSProcessor::MSProcessor(const String& tableName, const String &tableDescName, TableOption option) : MSTable(tableName, tableDescName,option), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSProcessor(String &, String &, TableOption) - " "table is not a valid MSProcessor")); } MSProcessor::MSProcessor(SetupNewTable &newTab, rownr_t nrrow, Bool initialize) : MSTable(newTab, nrrow, initialize), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSProcessor(SetupNewTable &, rownr_t, Bool) - " "table is not a valid MSProcessor")); } MSProcessor::MSProcessor(const Table &table) : MSTable(table), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSProcessor(const Table &) - " "table is not a valid MSProcessor")); } MSProcessor::MSProcessor(const MSProcessor &other) : MSTable(other), hasBeenDestroyed_p(False) { // verify that other is valid if (&other != this) if (! validate(this->tableDesc())) throw (AipsError("MSProcessor(const MSProcessor &) - " "table is not a valid MSProcessor")); } MSProcessor::~MSProcessor() { // check to make sure that this MSProcessor is still valid if (!hasBeenDestroyed_p && !validate()) { // the table is otherwise OK, so ensure that it is written if necessary this->flush(); LogIO os; os << LogIO::WARN << "~MSProcessor() - Table written is not a valid MSProcessor" << LogIO::POST; } hasBeenDestroyed_p = True; } MSProcessor& MSProcessor::operator=(const MSProcessor &other) { if (&other != this) { MSTable::operator=(other); hasBeenDestroyed_p=other.hasBeenDestroyed_p; } return *this; } MSTableMaps MSProcessor::initMaps() { MSTableMaps maps; // the PredefinedColumns // FLAG_ROW colMapDef(maps, FLAG_ROW, "FLAG_ROW", TpBool, "Row flag","",""); // colMapDef(maps, MODE_ID, "MODE_ID", TpInt, "Processor mode id","",""); // PASS_ID colMapDef(maps, PASS_ID, "PASS_ID", TpInt, "Processor pass number","",""); // TYPE colMapDef(maps, TYPE, "TYPE", TpString, "Processor type","",""); // TYPE_ID colMapDef(maps, TYPE_ID, "TYPE_ID", TpInt, "Processor type id","",""); // SUB_TYPE colMapDef(maps, SUB_TYPE, "SUB_TYPE", TpString, "Processor sub type","",""); // PredefinedKeywords // init requiredTableDesc // all required keywords uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(maps, PredefinedKeywords(i)); } // all required columns // Now define all other columns (duplicates are skipped) for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(maps, PredefinedColumns(i)); } return maps; } MSProcessor MSProcessor::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSProcessor(MSTable:: referenceCopy(newTableName,writableColumns)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSProcessor.h000066400000000000000000000114151476623553700214610ustar00rootroot00000000000000//# MSProcessor.h: The MeasurementSet PROCESSOR Table //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSPROCESSOR_H #define MS_MSPROCESSOR_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A Table intended to hold a MeasurementSet PROCESSOR table. // // // // //
      • MeasurementSet //
      • MSTable // // // // MSProcessor stands for the MeasurementSet Processor table. // // // // An MSProcessor is a table intended to hold the PROCESSOR table for // the MeasurementSet. It has an identical set of member functions as // the main MeasurementSet class. For further info and examples see the // MeasurementSet class. // // // // See the MeasurementSet for an example of how to access and use this class. // // // // It was found that subtables and the main table of the MeasurementSet have // a lot in common, therefore they derive their interface from the same // base class. Each subtable has its own class to keep the enum definitions // and conversion functions in separate scopes. // // // // see MeasurementSet. // class MSProcessor:public MSProcessorEnums, public MSTable { public: // This constructs an empty MSProcessor. MSProcessor (); // These constructors mirror the Table ones with additional checking // on validity (verifying that the MSProcessor will have the required columns // and keywords) // An exception is thrown if the constructed Table is not a valid MSProcessor // //
      • AipsError // // MSProcessor (const String &tableName, TableOption = Table::Old); MSProcessor (const String &tableName, const String &tableDescName, TableOption = Table::Old); MSProcessor (SetupNewTable &newTab, rownr_t nrrow = 0, Bool initialize = False); MSProcessor (const Table &table); MSProcessor (const MSProcessor &other); // // As with tables, the destructor writes the table if necessary. // Additional checking is done here to verify that all required // columns are still present. // If it is NOT valid, it will write the table and then throw an exception. // //
      • AipsError // ~MSProcessor(); // Assignment operator, reference semantics MSProcessor& operator=(const MSProcessor&); // Make a special copy of this Table which references all columns from // this Table except those mentioned; those are empty and writable. // Each forwarded column has the same writable status as the underlying // column. The mentioned columns all use the AipsIO storage manager. // This function is inherited from MSTable and unlikely to be of use, // except in the class MeasurementSet (see comment there).. MSProcessor referenceCopy(const String& newTableName, const Block& writableColumns) const; // Initialize the statics appropriately. This does not need to be // called by users, it is called by the implementation class // MSTableImpl. static MSTableMaps initMaps(); private: // required by the need to throw an exception in the destructor Bool hasBeenDestroyed_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSProcessorColumns.cc000066400000000000000000000050271476623553700231620ustar00rootroot00000000000000//# MSProcessorColumns.cc: provides easy access to MeasurementSet columns //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSProcessorColumns::MSProcessorColumns() { } MSProcessorColumns::MSProcessorColumns(const MSProcessor& msProcessor) { attach(msProcessor); } MSProcessorColumns::~MSProcessorColumns() {} void MSProcessorColumns::attach(const MSProcessor& msProcessor) { flagRow_p.attach(msProcessor, MSProcessor:: columnName(MSProcessor::FLAG_ROW)); modeId_p.attach(msProcessor, MSProcessor:: columnName(MSProcessor::MODE_ID)); type_p.attach(msProcessor, MSProcessor:: columnName(MSProcessor::TYPE)); typeId_p.attach(msProcessor, MSProcessor:: columnName(MSProcessor::TYPE_ID)); subType_p.attach(msProcessor, MSProcessor:: columnName(MSProcessor::SUB_TYPE)); attachOptionalCols(msProcessor); } void MSProcessorColumns::attachOptionalCols(const MSProcessor& msProcessor) { const ColumnDescSet& cds=msProcessor.tableDesc().columnDescSet(); const String& passId=MSProcessor::columnName(MSProcessor::PASS_ID); if (cds.isDefined(passId)) passId_p.attach(msProcessor, passId); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSProcessorColumns.h000066400000000000000000000112671476623553700230270ustar00rootroot00000000000000//# MSProcessorColumns.h: provides easy access to MSProcessor columns //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSPROCESSORCOLUMNS_H #define MS_MSPROCESSORCOLUMNS_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class MSProcessor; // // A class to provide easy access to MSProcessor columns // // // // // //
      • MSProcessor //
      • ScalarColumn // // // // MSProcessorColumns stands for MeasurementSet Processor Table columns. // // // // This class provides access to the columns in the MSProcessor Table, // it does the declaration of all the Scalar and ArrayColumns with the // correct types, so the application programmer doesn't have to // worry about getting those right. There is an access function // for every predefined column. Access to non-predefined columns will still // have to be done with explicit declarations. // See MSColumns for an example. // // // // See MSColumns for the motivation. // class MSProcessorColumns { public: // Create a columns object that accesses the data in the specified Table MSProcessorColumns(const MSProcessor& msProcessor); // The destructor does nothing special ~MSProcessorColumns(); // Access to required columns // ScalarColumn& flagRow() {return flagRow_p;} ScalarColumn& modeId() {return modeId_p;} ScalarColumn& type() {return type_p;} ScalarColumn& typeId() {return typeId_p;} ScalarColumn& subType() {return subType_p;} // // Access to optional columns // ScalarColumn& passId() {return passId_p;} // // Const access to required columns // const ScalarColumn& flagRow() const {return flagRow_p;} const ScalarColumn& modeId() const {return modeId_p;} const ScalarColumn& type() const {return type_p;} const ScalarColumn& typeId() const {return typeId_p;} const ScalarColumn& subType() const {return subType_p;} // // Const access to optional columns // const ScalarColumn& passId() const {return passId_p;} // // Convenience function that returns the number of rows in any of the columns rownr_t nrow() const {return flagRow_p.nrow();} protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. MSProcessorColumns(); //# attach this object to the supplied table. void attach(const MSProcessor& msProcessor); private: //# Make the assignment operator and the copy constructor private to prevent //# any compiler generated one from being used. MSProcessorColumns(const MSProcessorColumns&); MSProcessorColumns& operator=(const MSProcessorColumns&); //# Check if any optional columns exist and if so attach them. void attachOptionalCols(const MSProcessor& msProcessor); //# required columns ScalarColumn flagRow_p; ScalarColumn modeId_p; ScalarColumn type_p; ScalarColumn typeId_p; ScalarColumn subType_p; //# optional columns ScalarColumn passId_p; }; //# Define the RO version for backward compatibility. typedef MSProcessorColumns ROMSProcessorColumns; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSProcessorEnums.h000066400000000000000000000063241476623553700224740ustar00rootroot00000000000000//# MSProcessorEnums.h: Definitions for the MeasurementSet PROCESSOR table //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSPROCESSORENUMS_H #define MS_MSPROCESSORENUMS_H #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Enums for the MeasurementSet PROCESSER table // // // // This class contains the enums for the MeasurementSet PROCESSOR table // // // This class does nothing. It is merely a container for the enumerations // used by the MSProcessor class. These enumerations define the // standard columns and keywords. // // // See the documentation for MeasurementSet for examples on the use of these // enumerations. // // // All the todo items which may be related to this class are // grouped with the todo items for MeasurementSet // // class MSProcessorEnums { public: // The PROCESSOR table colums with predefined meaning. // Keys: PROCESSOR_ID is rownumber enum PredefinedColumns { // "True" columns are defined.
        // TYPE - UNIT - MEASURE UNDEFINED_COLUMN=0, // Row flag
        // Bool FLAG_ROW, // Processor mode id
        // Int MODE_ID, // The Processor type
        // String TYPE, // Processor type id - points to subtype_type subtable
        // Int TYPE_ID, // Processor sub type
        // String SUB_TYPE, // Number of required columns
        NUMBER_REQUIRED_COLUMNS=SUB_TYPE, // Processor pass number
        // Int PASS_ID, // Not a column, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_COLUMNS=PASS_ID }; // Keywords with a predefined meaning enum PredefinedKeywords { // // "True" keywords are defined. UNDEFINED_KEYWORD=0, // // Not a keyword, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_KEYWORDS=0 }; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSRange.cc000066400000000000000000000447441476623553700207070ustar00rootroot00000000000000///# MSRange.cc: selection and iteration of an MS //# Copyright (C) 1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSRange::MSRange():blockSize_p(10),ddId_p(0),constantShape_p(False),sel_p(0) {} MSRange::MSRange(const MeasurementSet& ms) :ms_p(ms),blockSize_p(10),constantShape_p(False),sel_p(0) {} MSRange::MSRange(const MSSelector& msSel) :ms_p(msSel.selectedTable()),blockSize_p(10),constantShape_p(False), sel_p(&msSel) {ddId_p=msSel.dataDescId();} MSRange::MSRange(const MSRange& other) { operator=(other); } MSRange& MSRange::operator=(const MSRange& other) { if (this==&other) return *this; ms_p=other.ms_p; blockSize_p=other.blockSize_p; ddId_p.resize(0); ddId_p=other.ddId_p; spwId_p.resize(0); spwId_p=other.spwId_p; polId_p.resize(0); polId_p=other.polId_p; constantShape_p=other.constantShape_p; sel_p=other.sel_p; return *this; } Bool MSRange::checkShapes() { Int n=ddId_p.nelements(); // check already done if (n>0 && spwId_p.nelements()>0) return constantShape_p; constantShape_p=True; if (n==0) { ScalarColumn dd(ms_p,MS::columnName(MS::DATA_DESC_ID)); Vector ddId=scalarRange(dd); ddId_p=ddId; } Int n2=ddId_p.nelements(); MSDataDescColumns ddc(ms_p.dataDescription()); spwId_p.resize(n2); polId_p.resize(n2); for (Int i=0; i0) return constantShape_p; // no need to check, done by MSSelector // check if the shape is the same for all spectral windows that occur // in the main table MSSpWindowColumns spwc(ms_p.spectralWindow()); MSPolarizationColumns polc(ms_p.polarization()); for (Int i=1; i& items, Bool useFlags, Bool oneBased) { LogIO os; Int n=items.nelements(); Vector keys(n); // translate strings to enums Int k=0; String keyword; for (Int i=0; i& keys, Bool useFlags, Bool oneBased) { LogIO os; const Int option=Sort::HeapSort | Sort::NoDuplicates; const Sort::Order order=Sort::Ascending; Record out(RecordInterface::Variable); if (ms_p.nrow()==0) { os<< LogIO::WARN << "Table is empty - nothing to do"< want(nFuncType,nDataType,False); // use HeapSort as it's performance is guaranteed, quicksort is often // extremely slow (O(n*n)) for inputs with many successive duplicates Matrix uvw; Bool shapeChangesWarning=False; Int n=keys.nelements(); String keyword; for (Int i=0; i corrTypes= msc.polarization().corrType().getColumnCells(RowNumbers(polId_p)); if (fld==MSS::CORR_NAMES) { Matrix names(corrTypes.shape()); for (rownr_t k=0; k ifr=ifrNumbers(msc.antenna1(),msc.antenna2()); if (oneBased) ifr+=1001; out.define(keyword,ifr); } break; case MSS::IMAGINARY: case MSS::CORRECTED_IMAGINARY: case MSS::MODEL_IMAGINARY: case MSS::RATIO_IMAGINARY: case MSS::RESIDUAL_IMAGINARY: case MSS::OBS_RESIDUAL_IMAGINARY: want(Imag,fld-MSS::IMAGINARY)=True; break; case MSS::NUM_CORR: { checkShapes(); out.define(keyword,msc.polarization().numCorr().getColumnCells(RowNumbers(polId_p))); } break; case MSS::NUM_CHAN: { checkShapes(); out.define(keyword,msc.spectralWindow().numChan().getColumnCells(RowNumbers(spwId_p))); } break; case MSS::PHASE: case MSS::CORRECTED_PHASE: case MSS::MODEL_PHASE: case MSS::RATIO_PHASE: case MSS::RESIDUAL_PHASE: case MSS::OBS_RESIDUAL_PHASE: want(Phase,fld-MSS::PHASE)=True; break; case MSS::PHASE_DIR: { Record phasedir(RecordInterface::Variable); // return 0th order position only rownr_t nField = ms_p.field().nrow(); Matrix phaseDir(2,nField); Vector dir(2); for (rownr_t i=0; i rowNumbers=ms_p.rowNumbers(); Vector rows(n); convertArray(rows,rowNumbers); if (oneBased) rows+=Int64(1); out.define(keyword,rows); } break; case MSS::SCAN_NUMBER: scalarRange(out,keyword,msc.scanNumber(),oneBased); break; case MSS::SIGMA: if (checkShapes()) { Vector range(2); Array sig; if (sel_p) sig=sel_p->getWeight(msc.sigma(),True); else sig=msc.sigma().getColumn(); ::casacore::minMax(range(0),range(1),sig); out.define(keyword,range); } else { shapeChangesWarning = True; } break; case MSS::TIME: { Vector time(2); ::casacore::minMax(time(0),time(1),msc.time().getColumn()); out.define(keyword,time); } break; case MSS::TIMES: { Vector times=msc.time().getColumn(); Int64 n=GenSort::sort (times, order, option); out.define(keyword,times(Slice(0,n))); } break; case MSS::U: case MSS::V: case MSS::W: { Int index=fld-MSS::U; Vector range(2); if (uvw.nelements()==0) uvw=msc.uvw().getColumn(); ::casacore::minMax(range(0),range(1),uvw.row(index)); out.define(keyword,range); } break; case MSS::UVDIST: { if (uvw.nelements()==0) uvw=msc.uvw().getColumn(); Array u2,v2; u2=uvw.row(0); v2=uvw.row(1); u2*=u2; v2*=v2; u2+=v2; Vector uvrange(2); ::casacore::minMax(uvrange(0),uvrange(1),u2); uvrange(0)=sqrt(uvrange(0)); uvrange(1)=sqrt(uvrange(1)); out.define(keyword,uvrange); } break; case MSS::WEIGHT: if (checkShapes()) { Vector range(2); Array wt; if (sel_p) wt=sel_p->getWeight(msc.weight()); else wt=msc.weight().getColumn(); ::casacore::minMax(range(0),range(1),wt); out.define(keyword,range); } else { shapeChangesWarning = True; } break; case MSS::UNDEFINED: default: { } } } // throw away the uvw data (if any) uvw.resize(0,0); for (Int dataType=Observed; dataType colData1, colData2; if (dataType==Observed || dataType==ObsResidual) { colData1.reference(msc.data()); } else if (dataType==Corrected || dataType==Ratio || dataType==Residual){ colData1.reference(msc.correctedData()); } else if (dataType==Model) { colData1.reference(msc.modelData()); } if (dataType>=Ratio && dataType<=ObsResidual) { colData2.reference(msc.modelData()); needCol2=True; } if (dataType!=ObsFloat) { if (!colData1.isNull()&&(!needCol2 || !colData2.isNull())) { if (checkShapes()) { if (anyEQ(want(Slice(Amp,4),dataType),True)) { Matrix minmax(2,4); Vector funcSel(4); for (Int funcType=Amp; funcType<=Imag; funcType++) { funcSel[funcType]=want(funcType,dataType); } minMax(minmax,funcSel,colData1,colData2,msc.flag(), dataType,useFlags); String name; switch (dataType) { case Corrected: name="corrected_"; break; case Model: name="model_";break; case Ratio: name="ratio_"; break; case Residual: name="residual_"; break; case ObsResidual: name="obs_residual_"; break; default:; } Vector funcName(4); funcName(Amp)="amplitude"; funcName(Phase)="phase"; funcName(Real)="real"; funcName(Imag)="imaginary"; for (Int funcType=Amp; funcType<=Imag; funcType++) { if (want(funcType,dataType)) { out.define(name+funcName(funcType),minmax.column(funcType)); } } } if (want(Data,dataType)) { os << LogIO::WARN << "range not available for complex DATA" < amp(2); minMax(amp(0),amp(1),msc.floatData(),msc.flag(),useFlags); out.define("float_data",amp); } } else { os << LogIO::WARN << "FLOAT_DATA column doesn't exist"< key(1); key(0)=item; return range(key,useFlags); } void MSRange::setBlockSize(Int blockSize) { if (blockSize>0) blockSize_p=blockSize; } void MSRange::scalarRange(Record& out, const String& item, const ScalarColumn& id, Bool oneBased) { Vector ids=scalarRange(id); if (oneBased) ids+=1; out.define(item,ids); } Vector MSRange::scalarRange(const ScalarColumn& id) { const Int option=Sort::HeapSort | Sort::NoDuplicates; const Sort::Order order=Sort::Ascending; Vector idvec=id.getColumn(); Int64 n=GenSort::sort (idvec, order, option); Vector ids=idvec(Slice(0,n)); return ids; } void MSRange::minMax(Float& mini, Float& maxi, const ArrayColumn& data, const ArrayColumn& flag, Bool useFlags) { IPosition shp=data.shape(0); rownr_t nrow=data.nrow(); rownr_t numrow=rownr_t(blockSize_p*1.0e6/(sizeof(Float)*shp(0)*shp(1))); for (rownr_t start=0; start avFlag; Array flags = sel_p->getAveragedFlag(avFlag,flag,rowSlicer); Array avData; sel_p->getAveragedData(avData,flags,data,rowSlicer); if (useFlags) { ::casacore::minMax(minf,maxf,avData(!avFlag)); } else { ::casacore::minMax(minf,maxf,avData); } } else { Array tData=data.getColumnRange(rowSlicer); if (useFlags) { Array tFlag=flag.getColumnRange(rowSlicer); ::casacore::minMax(minf,maxf,tData(!tFlag)); } else { ::casacore::minMax(minf,maxf,tData); } } if (start==0) { mini=minf; maxi=maxf; } else { mini=min(mini,minf); maxi=max(maxi,maxf); } } } void MSRange::minMax(Matrix& minmax, const Vector& funcSel, const ArrayColumn& data1, const ArrayColumn& data2, const ArrayColumn& flag, Int dataType, Bool useFlags) { IPosition shp=data1.shape(0); rownr_t nrow=data1.nrow(); rownr_t numrow=rownr_t(blockSize_p*1.0e6/(sizeof(Complex)*shp(0)*shp(1))); for (rownr_t start=0; start minf(4), maxf(4); Slicer rowSlicer(Slice(start,n)); Array avData; if (sel_p) { Array avFlag; Array flags=sel_p->getAveragedFlag(avFlag,flag,rowSlicer); Array tData; sel_p->getAveragedData(tData,flags,data1,rowSlicer); if (dataType>=Ratio && dataType<=ObsResidual) { Array tData2; sel_p->getAveragedData(tData2,flags,data2,rowSlicer); if (dataType==Ratio) { LogicalArray mask(tData2!=Complex(0.)); tData/=tData2(mask); tData(!mask)=1.0; } else { tData-=tData2; } } if (useFlags) avData=tData(!avFlag).getCompressedArray(); else avData.reference(tData); } else { Array tData=data1.getColumnRange(rowSlicer); if (dataType>=Ratio && dataType<=ObsResidual) { Array tData2=data2.getColumnRange(rowSlicer); if (dataType==Ratio) { LogicalArray mask(tData2!=Complex(0.)); tData/=tData2(mask); tData(!mask)=1.0; } else { tData-=tData2; } } if (useFlags) { Array avFlag=flag.getColumnRange(rowSlicer); avData=tData(!avFlag).getCompressedArray(); } else { avData.reference(tData); } } // If any unflagged data, get min/max if (avData.nelements() > 0) { if (funcSel[0]) ::casacore::minMax(minf[0],maxf[0],amplitude(avData)); if (funcSel[1]) ::casacore::minMax(minf[1],maxf[1],phase(avData)); if (funcSel[2]) ::casacore::minMax(minf[2],maxf[2],real(avData)); if (funcSel[3]) ::casacore::minMax(minf[3],maxf[3],imag(avData)); if (start==0) { minmax.row(0)=minf; minmax.row(1)=maxf; } else { minmax.row(0)=::casacore::min(static_cast >(minmax.row(0)), static_cast >(minf)); minmax.row(1)=::casacore::max(static_cast >(minmax.row(1)) ,static_cast >(maxf)); } } } } Vector MSRange::ifrNumbers(const ScalarColumn& ant1, const ScalarColumn& ant2) { const Int option=Sort::HeapSort | Sort::NoDuplicates; const Sort::Order order=Sort::Ascending; Vector a1=ant1.getColumn(); Array a2=ant2.getColumn(); DebugAssert(max(a1)<1000 && max(a2)<1000,AipsError); a1*=1000; a1+=a2; Int64 n=GenSort::sort (a1, order, option); return a1(Slice(0,n)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSRange.h000066400000000000000000000162101476623553700205340ustar00rootroot00000000000000//# MSRange.h: this defines MSRange, which determines ranges of ms values //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSRANGE_H #define MS_MSRANGE_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template class ArrayColumn; template class ScalarColumn; class Record; class MSSelector; // // MSRange determines ranges of values in a MeasurementSet // // // // // //
      • MeasurementSet //
      • Record // // // // MSRange is a class that determines ranges of values in an MS // // // // This class is used to determine the range of values present for // the various columns in a MeasurementSet. // This class is initialized from a MeasurementSet. If the MS contains more // than one DATA_DESC_ID, it can be preselected on this to allow // a consistent set of frequencies to be returned. // The ms DO provides access to this class from glish and GUIs. // // // MSRange myRange(myMS); // Vector items(3); // // fill in some fields // items(0)="field_id"; // items(1)="time"; // items(2)="data_desc_id"; // // get the range of values for the items specified // cout << myRange.range(items)< dd(2); dd(0)=1; dd(1)=2; // mss.selectinit(0,dd); // select data desc ids 1 and 2 // MSRange r2(mss); // items(2)="amplitude"; // cout<< r2.range(items)< // // // // Finding out the range of values in a column is often needed before a // sensible selection of data can be made. This class, formerly part of // MSSelector, separates out this functionality. // // // //
      • //
      • // // // //
      • maybe add channel selection and polarization conversion // class MSRange { public: enum { // spectral window selection and shapes have not been checked UNCHECKED = -3, // multiple spectral windows with varying shapes UNSELECTED = -2, // multiple spectral windows with same shape ALL = -1 }; // Default constructor, only useful to assign to. MSRange(); // Construct from an MS. explicit MSRange(const MeasurementSet& ms); // construct from an MSSelector, if this constructor is used, the data // will be channel selected and polarization converted as specified in // the MSSelector object, and the current selection is used in the range. explicit MSRange(const MSSelector& msSel); // Copy constructor MSRange(const MSRange& other); // Assignment MSRange& operator=(const MSRange& other); // Return the range of values for each of the items specified in // the record. For index-like items a list of values is returned, // for non-index items the minimum and maximum are returned. // Items with varying array shape will not be returned by this function (i.e. // you may need to preselect the MS passed to MSRange). // See the enum description in MSSelector for the list of supported items. // Use the data flags if useFlags is True. // Correct for one-based indexing if oneBased is True. Record range(const Vector& items, Bool useFlags=True, Bool OneBased=False); // Same as previous function, with Vector of MSS::Field keys instead // of Strings Record range(const Vector& items, Bool useFlags=True, Bool OneBased=False); // Similar to above, with a single enum, for convenience Record range(MSS::Field item, Bool useFlags=True); // Set the block size (in Mbytes) to use when reading the data column. // The default is 10 MB. Actual memory used is higher due to // temporaries and caching. void setBlockSize(Int blockSize=10); protected: // check the data description selection (one or more with same shape, or // varying shape) Bool checkShapes(); // get the range of a ScalarColumn, correct for 1-based // indexing if oneBased is True, and add to out record. void scalarRange(Record& out, const String& item, const ScalarColumn& id, Bool oneBased); // get the range of a ScalarColumn Vector scalarRange(const ScalarColumn& id); // get the minimum and maximum of a Complex data column, after // application of some function to convert to Float (e.g., real, // amplitude,...). This function reads the data in blocks of // size blockSize, as set by the setBlockSize function. void minMax(Matrix& minmax, const Vector& funcSel, const ArrayColumn& data1, const ArrayColumn& data2, const ArrayColumn& flag, Int dataType, Bool useFlags); // get the minimum and maximum of a Float data column // This function reads the data in blocks of // size blockSize, as set by the setBlockSize function. void minMax(Float& mini, Float& maxi, const ArrayColumn& data, const ArrayColumn& flag, Bool useFlags); // Get the range of interferometer numbers given the antenna1 and antenna2 // columns. Vector ifrNumbers(const ScalarColumn& ant1, const ScalarColumn& ant2); private: // The function types enum {Amp,Phase,Real,Imag,Data,nFuncType}; // The data types enum {Observed,Corrected,Model,Ratio,Residual,ObsResidual,ObsFloat,nDataType}; MeasurementSet ms_p; // the original ms Int blockSize_p; Vector ddId_p; Vector spwId_p; Vector polId_p; Bool constantShape_p; const MSSelector* sel_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSSource.cc000066400000000000000000000162311476623553700211010ustar00rootroot00000000000000//# MSSource.cc: The MeasurementSet SOURCE Table //# Copyright (C) 1996,1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSSource::MSSource():hasBeenDestroyed_p(True) { } MSSource::MSSource(const String &tableName, TableOption option) : MSTable(tableName, option), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSSource(String &, TableOption) - " "table is not a valid MSSource")); } MSSource::MSSource(const String& tableName, const String &tableDescName, TableOption option) : MSTable(tableName, tableDescName,option), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSSource(String &, String &, TableOption) - " "table is not a valid MSSource")); } MSSource::MSSource(SetupNewTable &newTab, rownr_t nrrow, Bool initialize) : MSTable(newTab, nrrow, initialize), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSSource(SetupNewTable &, rownr_t, Bool) - " "table is not a valid MSSource")); } MSSource::MSSource(const Table &table) : MSTable(table), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSSource(const Table &) - " "table is not a valid MSSource")); } MSSource::MSSource(const MSSource &other) : MSTable(other), hasBeenDestroyed_p(False) { // verify that other is valid if (&other != this) if (! validate(this->tableDesc())) throw (AipsError("MSSource(const MSSource &) - " "table is not a valid MSSource")); } MSSource::~MSSource() { // check to make sure that this MSSource is still valid if (!hasBeenDestroyed_p && !validate()) { // the table is otherwise OK, so ensure that it is written if necessary this->flush(); LogIO os; os << LogIO::WARN << "~MSSource() - Table written is not a valid MSSource" << LogIO::POST; } hasBeenDestroyed_p = True; } MSSource& MSSource::operator=(const MSSource &other) { if (&other != this) { MSTable::operator=(other); hasBeenDestroyed_p=other.hasBeenDestroyed_p; } return *this; } MSTableMaps MSSource::initMaps() { MSTableMaps maps; // the PredefinedColumns // CALIBRATION_GROUP colMapDef(maps, CALIBRATION_GROUP, "CALIBRATION_GROUP", TpInt, "Number of grouping for calibration purpose.","",""); // CODE colMapDef(maps, CODE, "CODE", TpString, "Special characteristics of source, " "e.g. Bandpass calibrator","",""); // DIRECTION colMapDef(maps, DIRECTION, "DIRECTION", TpArrayDouble, "Direction (e.g. RA, DEC).","rad","Direction"); // INTERVAL colMapDef(maps, INTERVAL, "INTERVAL", TpDouble, "Interval of time for which this set of parameters " "is accurate","s",""); // NAME colMapDef(maps, NAME, "NAME", TpString, "Name of source as given during observations","",""); // NUM_LINES colMapDef(maps, NUM_LINES, "NUM_LINES", TpInt, "Number of spectral lines","",""); // POSITION colMapDef(maps, POSITION, "POSITION", TpArrayDouble, "Position (e.g. for solar system objects", "m","Position"); // PROPER_MOTION colMapDef(maps, PROPER_MOTION, "PROPER_MOTION", TpArrayDouble, "Proper motion","rad/s",""); // PULSAR_ID colMapDef(maps, PULSAR_ID, "PULSAR_ID", TpInt, "Pulsar Id, pointer to pulsar table","",""); // REST_FREQUENCY colMapDef(maps, REST_FREQUENCY, "REST_FREQUENCY", TpArrayDouble, "Line rest frequency","Hz","Frequency"); // SOURCE_ID colMapDef(maps, SOURCE_ID, "SOURCE_ID", TpInt, "Source id","",""); // SOURCE_MODEL colMapDef(maps, SOURCE_MODEL, "SOURCE_MODEL", TpRecord, "Component Source Model","",""); // SPECTRAL_WINDOW_ID colMapDef(maps, SPECTRAL_WINDOW_ID,"SPECTRAL_WINDOW_ID",TpInt, "ID for this spectral window setup","",""); // SYSVEL colMapDef(maps, SYSVEL, "SYSVEL", TpArrayDouble, "Systemic velocity at reference","m/s","Radialvelocity"); // TIME colMapDef(maps, TIME, "TIME", TpDouble, "Midpoint of time for which this set of parameters " "is accurate.","s","Epoch"); // TRANSITION colMapDef(maps, TRANSITION, "TRANSITION", TpArrayString, "Line Transition name","",""); // PredefinedKeywords // init requiredTableDesc // all required keywords uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(maps, PredefinedKeywords(i)); } // all required columns // First define the columns with fixed size arrays IPosition shape(1,2); ColumnDesc::Option option=ColumnDesc::Direct; addColumnToDesc(maps, DIRECTION, shape, option); addColumnToDesc(maps, PROPER_MOTION, shape, option); // Now define all other columns (duplicates are skipped) for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(maps, PredefinedColumns(i)); } return maps; } MSSource MSSource::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSSource(MSTable:: referenceCopy(newTableName,writableColumns)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSSource.h000066400000000000000000000113011476623553700207340ustar00rootroot00000000000000//# MSSource.h: The MeasurementSet SOURCE Table //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSOURCE_H #define MS_MSSOURCE_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A Table intended to hold a MeasurementSet SOURCE table. // // // // //
      • MeasurementSet //
      • MSTable // // // // MSSource stands for the MeasurementSet Source table. // // // // An MSSource is a table intended to hold the SOURCE table for // the MeasurementSet. It has an identical set of member functions as // the main MeasurementSet class. For further info and examples see the // MeasurementSet class. // // // // See the MeasurementSet for an example of how to access and use this class. // // // // It was found that subtables and the main table of the MeasurementSet have // a lot in common, therefore they derive their interface from the same // base class. Each subtable has its own class to keep the enum definitions // and conversion functions in separate scopes. // // // // see MeasurementSet. // class MSSource:public MSSourceEnums, public MSTable { public: // This constructs an empty MSSource. MSSource (); // These constructors mirror the Table ones with additional checking // on validity (verifying that the MSSource will have the required columns // and keywords) // An exception is thrown if the constructed Table is not a valid MSSource // //
      • AipsError // // MSSource (const String &tableName, TableOption = Table::Old); MSSource (const String &tableName, const String &tableDescName, TableOption = Table::Old); MSSource (SetupNewTable &newTab, rownr_t nrrow = 0, Bool initialize = False); MSSource (const Table &table); MSSource (const MSSource &other); // // As with tables, the destructor writes the table if necessary. // Additional checking is done here to verify that all required // columns are still present. // If it is NOT valid, it will write the table and then throw an exception. // //
      • AipsError // ~MSSource(); // Assignment operator, reference semantics MSSource& operator=(const MSSource&); // Make a special copy of this Table which references all columns from // this Table except those mentioned; those are empty and writable. // Each forwarded column has the same writable status as the underlying // column. The mentioned columns all use the AipsIO storage manager. // This function is inherited from MSTable and unlikely to be of use, // except in the class MeasurementSet (see comment there).. MSSource referenceCopy(const String& newTableName, const Block& writableColumns) const; // Initialize the statics appropriately. This does not need to be // called by users, it is called by the implementation class // MSTableImpl. static MSTableMaps initMaps(); private: // required by the need to throw an exception in the destructor Bool hasBeenDestroyed_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSSourceColumns.cc000066400000000000000000000124601476623553700224420ustar00rootroot00000000000000//# MSSourceColumns.cc: provides easy access to MeasurementSet columns //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSSourceColumns::MSSourceColumns(): isNull_p(True) { } MSSourceColumns::MSSourceColumns(const MSSource& msSource): isNull_p(True) { attach(msSource); } MSSourceColumns::~MSSourceColumns() {} void MSSourceColumns::attach(const MSSource& msSource) { isNull_p = msSource.isNull(); if (!isNull()) { calibrationGroup_p.attach(msSource, MSSource:: columnName(MSSource::CALIBRATION_GROUP)); code_p.attach(msSource, MSSource::columnName(MSSource::CODE)); direction_p.attach(msSource, MSSource:: columnName(MSSource::DIRECTION)); interval_p.attach(msSource, MSSource:: columnName(MSSource::INTERVAL)); name_p.attach(msSource, MSSource::columnName(MSSource::NAME)); numLines_p.attach(msSource, MSSource:: columnName(MSSource::NUM_LINES)); properMotion_p.attach(msSource, MSSource:: columnName(MSSource::PROPER_MOTION)); sourceId_p.attach(msSource, MSSource:: columnName(MSSource::SOURCE_ID)); spectralWindowId_p.attach(msSource, MSSource:: columnName(MSSource::SPECTRAL_WINDOW_ID)); time_p.attach(msSource, MSSource::columnName(MSSource::TIME)); directionMeas_p.attach(msSource, MSSource:: columnName(MSSource::DIRECTION)); timeMeas_p.attach(msSource, MSSource::columnName(MSSource::TIME)); directionQuant_p.attach(msSource, MSSource:: columnName(MSSource::DIRECTION)); intervalQuant_p.attach(msSource, MSSource:: columnName(MSSource::INTERVAL)); properMotionQuant_p.attach(msSource, MSSource:: columnName(MSSource::PROPER_MOTION)); timeQuant_p.attach(msSource, MSSource::columnName(MSSource::TIME)); attachOptionalCols(msSource); } } void MSSourceColumns::attachOptionalCols(const MSSource& msSource) { const ColumnDescSet& cds = msSource.tableDesc().columnDescSet(); const String& position = MSSource::columnName(MSSource::POSITION); if (cds.isDefined(position)) { position_p.attach(msSource, position); positionMeas_p.attach(msSource, position); positionQuant_p.attach(msSource, position); } const String& pulsarId = MSSource::columnName(MSSource::PULSAR_ID); if (cds.isDefined(pulsarId)) pulsarId_p.attach(msSource, pulsarId); const String& restFrequency = MSSource::columnName(MSSource::REST_FREQUENCY); if (cds.isDefined(restFrequency)) { restFrequency_p.attach(msSource, restFrequency); restFrequencyMeas_p.attach(msSource, restFrequency); restFrequencyQuant_p.attach(msSource, restFrequency); } const String& sourceModel = MSSource::columnName(MSSource::SOURCE_MODEL); if (cds.isDefined(sourceModel)) sourceModel_p.attach(msSource, sourceModel); const String& sysvel = MSSource::columnName(MSSource::SYSVEL); if (cds.isDefined(sysvel)) { sysvel_p.attach(msSource, sysvel); sysvelMeas_p.attach(msSource, sysvel); sysvelQuant_p.attach(msSource, sysvel); } const String& transition = MSSource::columnName(MSSource::TRANSITION); if (cds.isDefined(transition)) transition_p.attach(msSource, transition); } void MSSourceColumns::setEpochRef(MEpoch::Types ref, Bool tableMustBeEmpty) { timeMeas_p.setDescRefCode(ref, tableMustBeEmpty); } void MSSourceColumns::setDirectionRef(MDirection::Types ref) { directionMeas_p.setDescRefCode(ref); } void MSSourceColumns::setPositionRef(MPosition::Types ref) { if (!positionMeas_p.isNull()) { positionMeas_p.setDescRefCode(ref); } } void MSSourceColumns::setFrequencyRef(MFrequency::Types ref) { if (!restFrequencyMeas_p.isNull()) { restFrequencyMeas_p.setDescRefCode(ref); } } void MSSourceColumns::setRadialVelocityRef(MRadialVelocity::Types ref) { if (!sysvelMeas_p.isNull()) { sysvelMeas_p.setDescRefCode(ref); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSSourceColumns.h000066400000000000000000000265411476623553700223110ustar00rootroot00000000000000//# MSSourceColumns.h: provides easy access to MSSource columns //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSOURCECOLUMNS_H #define MS_MSSOURCECOLUMNS_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class MSSource; // // A class to provide easy access to MSSource columns // // // // // //
      • MSSource //
      • ArrayColumn //
      • ScalarColumn // // // // MSSourceColumns stands for MeasurementSet Source Table columns. // // // // This class provides access to the columns in the MSSource Table, // it does the declaration of all the Scalar and ArrayColumns with the // correct types, so the application programmer doesn't have to // worry about getting those right. There is an access function // for every predefined column. Access to non-predefined columns will still // have to be done with explicit declarations. // See MSColumns for an example. // // // // See MSColumns for the motivation. // class MSSourceColumns { public: // Construct from the supplied Table MSSourceColumns(const MSSource& msSource); // The destructor does nothing special ~MSSourceColumns(); // Is this object defined? (MSSource table is optional) Bool isNull() const {return isNull_p;} // Access to required columns // ScalarColumn& calibrationGroup() {return calibrationGroup_p;} ScalarColumn& code() {return code_p;} ArrayColumn& direction() {return direction_p;} ArrayQuantColumn& directionQuant() {return directionQuant_p;} ScalarMeasColumn& directionMeas() {return directionMeas_p;} ScalarColumn& interval() {return interval_p;} ScalarQuantColumn& intervalQuant() {return intervalQuant_p;} ScalarColumn& name() {return name_p;} ScalarColumn& numLines() {return numLines_p;} ArrayColumn& properMotion() {return properMotion_p;} ArrayQuantColumn& properMotionQuant() {return properMotionQuant_p;} ScalarColumn& sourceId() {return sourceId_p;} ScalarColumn& spectralWindowId() {return spectralWindowId_p;} ScalarColumn& time() {return time_p;} ScalarQuantColumn& timeQuant() {return timeQuant_p;} ScalarMeasColumn& timeMeas() {return timeMeas_p;} // // Access to optional columns // ArrayColumn& position() {return position_p;} ArrayQuantColumn& positionQuant() {return positionQuant_p;} ScalarMeasColumn& positionMeas() {return positionMeas_p;} ScalarColumn& pulsarId() {return pulsarId_p;} ArrayColumn& restFrequency() {return restFrequency_p;} ArrayQuantColumn& restFrequencyQuant() {return restFrequencyQuant_p;} ArrayMeasColumn& restFrequencyMeas() { return restFrequencyMeas_p;} ScalarColumn& sourceModel() {return sourceModel_p;} ArrayColumn& sysvel() {return sysvel_p;} ArrayQuantColumn& sysvelQuant() {return sysvelQuant_p;} ArrayMeasColumn& sysvelMeas() {return sysvelMeas_p;} ArrayColumn& transition() {return transition_p;} // // Const access to required columns // const ScalarColumn& calibrationGroup() const { return calibrationGroup_p;} const ScalarColumn& code() const {return code_p;} const ArrayColumn& direction() const {return direction_p;} const ArrayQuantColumn& directionQuant() const { return directionQuant_p;} const ScalarMeasColumn& directionMeas() const { return directionMeas_p;} const ScalarColumn& interval() const {return interval_p;} const ScalarQuantColumn& intervalQuant() const { return intervalQuant_p;} const ScalarColumn& name() const {return name_p;} const ScalarColumn& numLines() const {return numLines_p;} const ArrayColumn& properMotion() const {return properMotion_p;} const ArrayQuantColumn& properMotionQuant() const { return properMotionQuant_p;} const ScalarColumn& sourceId() const {return sourceId_p;} const ScalarColumn& spectralWindowId() const { return spectralWindowId_p;} const ScalarColumn& time() const {return time_p;} const ScalarQuantColumn& timeQuant() const {return timeQuant_p;} const ScalarMeasColumn& timeMeas() const {return timeMeas_p;} // // Const access to optional columns // const ArrayColumn& position() const {return position_p;} const ArrayQuantColumn& positionQuant() const { return positionQuant_p;} const ScalarMeasColumn& positionMeas() const { return positionMeas_p;} const ScalarColumn& pulsarId() const {return pulsarId_p;} const ArrayColumn& restFrequency() const {return restFrequency_p;} const ArrayQuantColumn& restFrequencyQuant() const { return restFrequencyQuant_p;} const ArrayMeasColumn& restFrequencyMeas() const { return restFrequencyMeas_p;} const ScalarColumn& sourceModel() const { return sourceModel_p;} const ArrayColumn& sysvel() const {return sysvel_p;} const ArrayQuantColumn& sysvelQuant() const {return sysvelQuant_p;} const ArrayMeasColumn& sysvelMeas() const { return sysvelMeas_p;} const ArrayColumn& transition() const {return transition_p;} // // Convenience function that returns the number of rows in any of the // columns. Returns zero if the object is null. rownr_t nrow() const {return isNull() ? 0 : calibrationGroup_p.nrow();} // set the epoch type for the TIME column. // // In principle this function can only be used if the table is empty, // otherwise already written values may thereafter have an incorrect // reference, offset, or unit. However, it is possible that part of the // table gets written before these values are known. In that case the // reference, offset, or units can be set by using a False // tableMustBeEmpty argument. // void setEpochRef(MEpoch::Types ref, Bool tableMustBeEmpty=True); // set the direction type for the DIRECTION column. This can only be done // when the table has no rows. Trying to do so at other times will throw an // exception. void setDirectionRef(MDirection::Types ref); // set the position type for the POSITION column. This can only be done when // the table has no rows. Trying to do so at other times will throw an // exception. void setPositionRef(MPosition::Types ref); // set the frequency type for the REST_FREQUENCY column. Does nothing if this // column is not defined. This can only be done when the table has no // rows. Trying to do so at other times will throw an exception. void setFrequencyRef(MFrequency::Types ref); // set the radial velocity type for the SYSVEL column. Does nothing if this // column is not defined. This can only be done when the table has no // rows. Trying to do so at other times will throw an exception. void setRadialVelocityRef(MRadialVelocity::Types ref); protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. MSSourceColumns(); //# attach this object to the supplied table. void attach(const MSSource& msSource); private: //# Make the assignment operator and the copy constructor private to prevent //# any compiler generated one from being used. MSSourceColumns(const MSSourceColumns&); MSSourceColumns& operator=(const MSSourceColumns&); //# Check if any optional columns exist and if so attach them. void attachOptionalCols(const MSSource& msSource); //# Is the object not attached to a Table. Bool isNull_p; //# required columns ScalarColumn calibrationGroup_p; ScalarColumn code_p; ArrayColumn direction_p; ScalarColumn interval_p; ScalarColumn name_p; ScalarColumn numLines_p; ArrayColumn properMotion_p; ScalarColumn sourceId_p; ScalarColumn spectralWindowId_p; ScalarColumn time_p; //# optional columns ArrayColumn position_p; ScalarColumn pulsarId_p; ArrayColumn restFrequency_p; ScalarColumn sourceModel_p; ArrayColumn sysvel_p; ArrayColumn transition_p; //# Access to Measure columns ScalarMeasColumn directionMeas_p; ScalarMeasColumn timeMeas_p; //# Optional Measure columns ScalarMeasColumn positionMeas_p; ArrayMeasColumn restFrequencyMeas_p; ArrayMeasColumn sysvelMeas_p; //# Access to Quantum columns ArrayQuantColumn directionQuant_p; ScalarQuantColumn intervalQuant_p; ArrayQuantColumn properMotionQuant_p; ScalarQuantColumn timeQuant_p; //# Optional Quantum columns ArrayQuantColumn positionQuant_p; ArrayQuantColumn restFrequencyQuant_p; ArrayQuantColumn sysvelQuant_p; }; //# Define the RO version for backward compatibility. typedef MSSourceColumns ROMSSourceColumns; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSSourceEnums.h000066400000000000000000000103761476623553700217570ustar00rootroot00000000000000//# MSSourceEnums.h: Definitions for the MeasurementSet SOURCE table //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSOURCEENUMS_H #define MS_MSSOURCEENUMS_H #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Enums for the MeasurementSet SOURCE table // // // // This class contains the enums for the MeasurementSet SOURCE table // // // This class does nothing. It is merely a container for the enumerations // used by the MSSource class. These enumerations define the // standard columns, keywords. // // // See the documentation for MeasurementSet for examples on the use of these // enumerations. // // // All the todo items which may be related to this class are // grouped with the todo items for MeasurementSet // // class MSSourceEnums { public: // The Antenna table colums with predefined meaning. // Keys: SPECTRAL_WINDOW_ID, INTERVAL, TIME, SOURCE_ID enum PredefinedColumns { // "True" columns are defined.
        // TYPE - UNIT - MEASURE UNDEFINED_COLUMN=0, // Number of grouping for calibration purpose.
        // Int. CALIBRATION_GROUP, // Special characteristics of source, e.g. Bandpass calibrator. // We need to define a standard set that can be used for // automated data reduction..
        // String. CODE, // Direction (e.g. RA, DEC).
        // Double(2) - rad - DIRECTION. DIRECTION, // Interval of time for which this set of parameters is accurate.
        // Double - s INTERVAL, // Name of source as given during observations.
        // String. NAME, // Number of spectral lines
        // Int NUM_LINES, // Proper motion.
        // Double(2) - rad/s - ?. PROPER_MOTION, // Source id.
        // Int. SOURCE_ID, // Spectral window id.
        // Int. SPECTRAL_WINDOW_ID, // Midpoint of time for which this set of parameters is accurate.
        // Double - s - EPOCH. TIME, // Number of required columns NUMBER_REQUIRED_COLUMNS=TIME, // Position (e.g. for solar system objects.
        // Double(3) - m - POSITION. POSITION, // Pulsar Id
        // Int PULSAR_ID, // Line rest frequency
        // Double(NUM_LINES) - Hz - Frequency REST_FREQUENCY, // Default Component Source Model
        // TableRecord SOURCE_MODEL, // Systemic velocity at reference.
        // Double(NUM_LINES) - m/s - RADIALVELOCITY. SYSVEL, // Transition name
        // String(NUM_LINES) TRANSITION, // Not a column, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_COLUMNS=TRANSITION }; // Keywords with a predefined meaning enum PredefinedKeywords { // // "True" keywords are defined. UNDEFINED_KEYWORD=0, // // Not a keyword, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_KEYWORDS=0 }; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSSpWindowColumns.cc000066400000000000000000000466101476623553700227600ustar00rootroot00000000000000//# MSSpWindowColumns.cc: provides easy access to MeasurementSet columns //# Copyright (C) 1996,1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSSpWindowColumns::MSSpWindowColumns() { } MSSpWindowColumns::MSSpWindowColumns(const MSSpectralWindow& msSpWindow) { attach(msSpWindow); } MSSpWindowColumns::~MSSpWindowColumns() {} void MSSpWindowColumns::attach(const MSSpectralWindow& msSpWindow) { chanFreq_p.attach(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::CHAN_FREQ)); chanWidth_p.attach(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::CHAN_WIDTH)); effectiveBW_p.attach(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::EFFECTIVE_BW)); flagRow_p.attach(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::FLAG_ROW)); freqGroup_p.attach(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::FREQ_GROUP)); freqGroupName_p.attach(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::FREQ_GROUP_NAME)); ifConvChain_p.attach(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::IF_CONV_CHAIN)); measFreqRef_p.attach(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::MEAS_FREQ_REF)); name_p.attach(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::NAME)); netSideband_p.attach(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::NET_SIDEBAND)); numChan_p.attach(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::NUM_CHAN)); refFrequency_p.attach(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::REF_FREQUENCY)); resolution_p.attach(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::RESOLUTION)); totalBandwidth_p.attach(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::TOTAL_BANDWIDTH)); chanFreqMeas_p.attach(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::CHAN_FREQ)); refFrequencyMeas_p.attach(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::REF_FREQUENCY)); chanFreqQuant_p.attach(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::CHAN_FREQ)); chanWidthQuant_p.attach(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::CHAN_WIDTH)); effectiveBWQuant_p.attach(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::EFFECTIVE_BW)); refFrequencyQuant_p.attach(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::REF_FREQUENCY)); resolutionQuant_p.attach(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::RESOLUTION)); totalBandwidthQuant_p.attach(msSpWindow, MSSpectralWindow:: columnName(MSSpectralWindow::TOTAL_BANDWIDTH)); attachOptionalCols(msSpWindow); } void MSSpWindowColumns:: attachOptionalCols(const MSSpectralWindow& msSpWindow) { const ColumnDescSet& cds=msSpWindow.tableDesc().columnDescSet(); const String& assocNature= MSSpectralWindow::columnName(MSSpectralWindow::ASSOC_NATURE); if (cds.isDefined(assocNature)) assocNature_p.attach(msSpWindow,assocNature); const String& assocSpwId= MSSpectralWindow::columnName(MSSpectralWindow::ASSOC_SPW_ID); if (cds.isDefined(assocSpwId)) assocSpwId_p.attach(msSpWindow,assocSpwId); const String& bbcNo= MSSpectralWindow::columnName(MSSpectralWindow::BBC_NO); if (cds.isDefined(bbcNo)) bbcNo_p.attach(msSpWindow,bbcNo); const String& bbcSideband= MSSpectralWindow::columnName(MSSpectralWindow::BBC_SIDEBAND); if (cds.isDefined(bbcSideband)) bbcSideband_p.attach(msSpWindow,bbcSideband); const String& dopplerId= MSSpectralWindow::columnName(MSSpectralWindow::DOPPLER_ID); if (cds.isDefined(dopplerId)) dopplerId_p.attach(msSpWindow,dopplerId); const String& receiverId= MSSpectralWindow::columnName(MSSpectralWindow::RECEIVER_ID); if (cds.isDefined(receiverId)) receiverId_p.attach(msSpWindow,receiverId); } Int64 MSSpWindowColumns:: matchSpw(const MFrequency& refFreq, uInt nChan, const Quantum& bandwidth, Int ifChain, const Quantum& tolerance, Int64 tryRow) const { rownr_t r = nrow(); if (r == 0) return -1; // Convert the reference frequency to Hz const MFrequency::Types refType = MFrequency::castType(refFreq.getRef().getType()); const Double refFreqInHz = refFreq.getValue().getValue(); // Convert the totalBandwidth to Hz const Unit Hz("Hz"); DebugAssert(bandwidth.check(Hz.getValue()), AipsError); const Double bandwidthInHz = bandwidth.getValue(Hz); // Convert the tolerance to Hz DebugAssert(tolerance.check(Hz.getValue()), AipsError); const Double tolInHz = tolerance.getValue(Hz); // Main matching loop if (tryRow >= 0) { const rownr_t tr = tryRow; if (tr >= r) { throw(AipsError("MSSpWindowColumns::match(...) - " "row " + String::toString(tr) + " you suggest is too big")); } if (!flagRow()(tr) && matchNumChan(tr, nChan) && matchIfConvChain(tr, ifChain) && //matchTotalBandwidth(tr, bandwidthInHz, nChan*tolInHz/4) && matchTotalBandwidth(tr, bandwidthInHz, bandwidthInHz/4.) && matchRefFrequency(tr, refType, refFreqInHz, tolInHz)) { return tr; } if (tr == r-1) r--; } while (r > 0) { r--; if (!flagRow()(r) && matchNumChan(r, nChan) && matchIfConvChain(r, ifChain) && //matchTotalBandwidth(r, bandwidthInHz, nChan*tolInHz/4) && matchTotalBandwidth(r, bandwidthInHz, bandwidthInHz/4.) && matchRefFrequency(r, refType, refFreqInHz, tolInHz)) { return r; } } return -1; } // this version has info of MeasFrame. Int64 MSSpWindowColumns:: matchSpw(const MFrequency& refFreq, const MFrequency& /*chanFreq1*/, const MeasFrame& measFrm, const MSDopplerColumns& msdopc, const MSSourceColumns& mssrcc, uInt nChan, const Quantum& bandwidth, Int ifChain, const Quantum& tolerance, Int64 tryRow) const { rownr_t r = nrow(); if (r == 0) return -1; // Convert the totalBandwidth to Hz const Unit Hz("Hz"); DebugAssert(bandwidth.check(Hz.getValue()), AipsError); const Double bandwidthInHz = bandwidth.getValue(Hz); // Convert the tolerance to Hz DebugAssert(tolerance.check(Hz.getValue()), AipsError); const Double tolInHz = tolerance.getValue(Hz); // Main matching loop if (tryRow >= 0) { const rownr_t tr = tryRow; if (tr >= r) { throw(AipsError("MSSpWindowColumns::match(...) - " "row " + String::toString(tr) + " you suggest is too big")); } if (!flagRow()(tr) && matchNumChan(tr, nChan) && matchIfConvChain(tr, ifChain) && //matchTotalBandwidth(tr, bandwidthInHz, nChan*tolInHz/4) && matchTotalBandwidth(tr, bandwidthInHz, bandwidthInHz/4.) && ( /*matchRefFreqCnvtrd(tr, chanFreq1, False, measFrm, msdopc, mssrcc, tolInHz)||*/ matchRefFreqCnvtrd(tr, refFreq, True, measFrm, msdopc, mssrcc, tolInHz))) { return tr; } if (tr == r-1) r--; } while (r > 0) { r--; if (!flagRow()(r) && matchNumChan(r, nChan) && matchIfConvChain(r, ifChain) && //matchTotalBandwidth(r, bandwidthInHz, nChan*tolInHz/4) && matchTotalBandwidth(r, bandwidthInHz, bandwidthInHz/4.) && ( /*matchRefFreqCnvtrd(r, chanFreq1, False, measFrm, msdopc, mssrcc, tolInHz)||*/ matchRefFreqCnvtrd(r, refFreq, True, measFrm, msdopc, mssrcc, tolInHz))) { return r; } } return -1; } RowNumbers MSSpWindowColumns:: allMatchedSpw(const MFrequency& refFreq, uInt nChan, const Quantum& bandwidth, Int ifChain, const Quantum& tolerance) const { rownr_t r = nrow(); RowNumbers matched; if (r == 0) return matched; // Convert the reference frequency to Hz const MFrequency::Types refType = MFrequency::castType(refFreq.getRef().getType()); const Double refFreqInHz = refFreq.getValue().getValue(); // Convert the totalBandwidth to Hz const Unit Hz("Hz"); DebugAssert(bandwidth.check(Hz.getValue()), AipsError); const Double bandwidthInHz = bandwidth.getValue(Hz); // Convert the tolerance to Hz DebugAssert(tolerance.check(Hz.getValue()), AipsError); const Double tolInHz = tolerance.getValue(Hz); size_t numMatch=0; for (rownr_t k=0; k < r; ++k){ if (!flagRow()(k) && matchNumChan(k, nChan) && matchIfConvChain(k, ifChain) && //matchTotalBandwidth(k, bandwidthInHz, nChan*tolInHz/4) && matchTotalBandwidth(k, bandwidthInHz, bandwidthInHz/4.) && matchRefFrequency(k, refType, refFreqInHz, tolInHz)) { //matchRefFreqCnvtrd(r, refFreq, True, measFrm, msdopc, mssrcc, tolInHz))) { ++numMatch; matched.resize(numMatch, True); matched(numMatch-1)=k; } } return matched; } Int64 MSSpWindowColumns:: matchSpw(const MFrequency& refFreq, uInt nChan, const Quantum& bandwidth, Int ifChain, const Quantum& tolerance, Vector& otherFreqs, Bool& reversed) const { reversed=False; Int matchedSpw=-1; RowNumbers allMatchSpw= allMatchedSpw(refFreq, nChan, bandwidth, ifChain, tolerance); size_t nMatches=allMatchSpw.size(); if(nMatches==0) return -1; // if only one channel then return the first match if (nChan == 1) return allMatchSpw[0]; Double tolInHz= tolerance.get("Hz").getValue(); for (size_t k=0; k < nMatches; ++k){ matchedSpw=allMatchSpw[k]; if(matchChanFreq(matchedSpw, otherFreqs, tolInHz)){ return matchedSpw; } else{ Vector reverseFreq(otherFreqs.shape()); for (uInt f=0; f < nChan ; ++f){ reverseFreq[f]=otherFreqs[nChan-1-f]; } if(matchChanFreq(matchedSpw, reverseFreq, tolInHz)){ reversed=True; return matchedSpw; } } } return -1; } Bool MSSpWindowColumns:: matchRefFrequency(rownr_t row, MFrequency::Types refType, Double refFreqInHz, Double tolInHz) const { DebugAssert(row < nrow(), AipsError); const MFrequency rowFreq = refFrequencyMeas()(row); if (MFrequency::castType(rowFreq.getRef().getType()) != refType) { return False; } return nearAbs(rowFreq.getValue().getValue(), refFreqInHz, tolInHz); } Bool MSSpWindowColumns:: matchRefFreqCnvtrd(rownr_t row, MFrequency refFreq, const Bool isRefFreq, const MeasFrame& measFrm, const MSDopplerColumns& msdopc, const MSSourceColumns& mssrcc, Double tolInHz) const { // measFrm is the frame info for the current spw. DebugAssert(row < nrow(), AipsError); // Since sometimes when the channel frequency does not match, the reference frequency actually matches. // So we check both and so we may receive either a refrence frequency or a channel frequency. MFrequency rowFreq; if( isRefFreq ) { rowFreq = refFrequencyMeas()(row); //cout<< "[MSSpWindowColumns::matchRefFreqCnvtr()] match reference frequency. "<< endl; }else{ rowFreq = Vector(chanFreqMeas()(row))(0); //cout<< "[MSSpWindowColumns::matchRefFreqCnvtr()] match first channel frequency. " << endl; } //const MFrequency refFreqCtrd; const MFrequency::Types refType = MFrequency::castType(refFreq.getRef().getType()); const MFrequency::Types rowType = MFrequency::castType(rowFreq.getRef().getType()); //cout.precision(8); const Double refFreqInHz = refFreq.getValue().getValue(); const Double rowFreqInHz = rowFreq.getValue().getValue(); Double refFreqInHzCnvtrd = refFreqInHz; Double rowFreqInHzCnvtrd = rowFreqInHz; if (rowType != refType) { MFrequency::Convert freqCnvtr; if( rowType == MFrequency::TOPO ){ // One match for NGC7538 // combined conversion MeasFrame measFrmFrom = MeasFrame( *(measFrm.epoch()), *(measFrm.position()), *(measFrm.direction())); //MRadialVelocity radialVelocity(MVRadialVelocity ( -59000.0 ), MRadialVelocity::Ref (MRadialVelocity::LSRK, measFrmFrom )); //measFrmFrom.set ( radialVelocity); MFrequency::Ref refFrom( refType, measFrmFrom ); MeasFrame measFrmTo = MeasFrame(); // the info for MEpoch of the previous spw are not persisted. Hard coding it in for now. measFrmTo.set( *(measFrm.position()) ); // get the epoch uInt doppler_id = dopplerId()( row ); // Note what is required in operator () of ScalarColumns< Measures > is the row number of the // table. But for subtable DOPPLER, doppler_id is the same as row number. So we can use the // source_id directly in the call below. uInt source_id = msdopc.sourceId()(doppler_id ); MEpoch epochTo = mssrcc.timeMeas()( source_id ); // set the Epoch to that of rowFreq. measFrmTo.set( epochTo ); measFrmTo.set( *(measFrm.direction())); //measFrmTo.set( radialVelocity ); MFrequency::Ref refTo( rowType, measFrmTo ); Unit unit(String("Hz")); MFrequency::Convert cnvtMachine(unit, refFrom, refTo); MFrequency freqFromConverted = cnvtMachine( refFreq ); refFreqInHzCnvtrd = freqFromConverted.getValue().getValue(); }else if ( refType == MFrequency::TOPO ){ // One match for G192 // combined conversion. Should produce the same results as the step by step conversion. MeasFrame measFrmFrom = MeasFrame(); // the info for MEpoch and MDirection of the previous spw are not persisted. Hard coding it in for now. measFrmFrom.set( *(measFrm.position()) ); // get the epoch uInt doppler_id = dopplerId()( row ); // Note what is required in operator () of ScalarColumns< Measures > is the row number of the // table. But for subtable SOURCE, source_id is the same as row number. So we can use the // source_id directly in the call below. uInt source_id = msdopc.sourceId()( doppler_id ); MEpoch epochFrom = mssrcc.timeMeas()( source_id ); // get the field direction MDirection fieldDirFrom = mssrcc.directionMeas()(source_id); measFrmFrom.set( epochFrom ); measFrmFrom.set( fieldDirFrom ); //MRadialVelocity radialVelocity(MVRadialVelocity ( 5700.0 ), MRadialVelocity::Ref (MRadialVelocity::LSRK, measFrmFrom )); //measFrmFrom.set ( radialVelocity); MFrequency::Ref refFrom( rowType, measFrmFrom ); MeasFrame measFrmTo = MeasFrame(); measFrmTo.set( *(measFrm.position()) ); measFrmTo.set( fieldDirFrom ); measFrmTo.set( *(measFrm.epoch()) ); //measFrmTo.set( radialVelocity ); // check the effects of epoch. Comment out the following line after test. // measFrm.set( *(measFrmFrom.epoch())); // This does not afftect the result or maybe the effects is small. MFrequency::Ref refTo( refType, measFrmTo ); Unit unit(String("Hz")); MFrequency::Convert cnvtMachine(unit, refFrom, refTo); // rowFreq does not have frame info in it. So add it in to see if it will affect anything. // did not see any effects of this. // MFrequency freqFrom = MFrequency( MVFrequency( Quantity( rowFreq.getValue().getValue(),"Hz" )),refFrom ); // MFrequency freqFromConverted = cnvtMachine( freqFrom ); MFrequency freqFromConverted = cnvtMachine( rowFreq ); rowFreqInHzCnvtrd = freqFromConverted.getValue().getValue(); }else{ // none of the frequency is of type TOPO MFrequency::Ref refFrom( refType, measFrm ); MeasFrame measFrmTo = MeasFrame(); measFrmTo.set( *(measFrm.position()) ); // get the epoch uInt doppler_id = dopplerId()( row ); // Note what is required in operator () of ScalarColumns< Measures > is the row number of the // table. But for subtable DOPPLER, doppler_id is the same as row number. So we can use the // source_id directly in the call below. uInt source_id = msdopc.sourceId()(doppler_id ); // Note what is required in operator () of ScalarColumns< Measures > is the row number of the // table. But for subtable SOURCE, source_id is the same as row number. So we can use the // source_id directly in the call below. MEpoch epochTo = mssrcc.timeMeas()( source_id ); // set the Epoch to that of rowFreq. // measFrmTo.set( MEpoch( MVEpoch( 49856, 0.391493 ))); measFrmTo.set( epochTo ); measFrmTo.set( *(measFrm.direction())); //measFrmTo.set( radialVelocity ); MFrequency::Ref refTo( rowType, measFrmTo ); Unit unit(String("Hz")); MFrequency::Convert cnvtMachine(unit, refFrom, refTo); MFrequency freqFromConverted = cnvtMachine( refFreq ); refFreqInHzCnvtrd = freqFromConverted.getValue().getValue(); } } return nearAbs(rowFreqInHzCnvtrd, refFreqInHzCnvtrd, tolInHz); } Bool MSSpWindowColumns:: matchChanFreq(rownr_t row, const Vector& chanFreqInHz, Double tolInHz) const { DebugAssert(row < nrow(), AipsError); DebugAssert(chanFreq().ndim(row) == 1, AipsError); // Check the number of channels const uInt nChan = chanFreq().shape(row)(0); if (nChan != chanFreqInHz.nelements()) return False; // Check the values in each channel return allNearAbs(chanFreq()(row), chanFreqInHz, tolInHz); } Bool MSSpWindowColumns:: matchIfConvChain(rownr_t row, Int ifChain) const { DebugAssert(row < nrow(), AipsError); return ifChain == ifConvChain()(row); } Bool MSSpWindowColumns:: matchTotalBandwidth(rownr_t row, Double bandwidthInHz, Double tolInHz) const { DebugAssert(row < nrow(), AipsError); return nearAbs(totalBandwidth()(row), bandwidthInHz, fabs(tolInHz)); } Bool MSSpWindowColumns:: matchNumChan(rownr_t row, Int nChan) const { DebugAssert(row < nrow(), AipsError); return nChan == numChan()(row); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSSpWindowColumns.h000066400000000000000000000307711476623553700226230ustar00rootroot00000000000000//# MSSpWindowColumns.h: provides easy access to MSSpectralWindow columns //# Copyright (C) 1996,1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSPWINDOWCOLUMNS_H #define MS_MSSPWINDOWCOLUMNS_H #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class MSSpectralWindow; // // A class to provide easy access to MSSpectralWindow columns // // // // // //
      • MSSpectralWindow //
      • ArrayColumn //
      • ScalarColumn // // // // MSSpectralWindowColumns stands for MeasurementSet SpectralWindow Table columns. // // // // This class provides access to the columns in the MSSpectralWindow Table, // it does the declaration of all the Scalar and ArrayColumns with the // correct types, so the application programmer doesn't have to // worry about getting those right. There is an access function // for every predefined column. Access to non-predefined columns will still // have to be done with explicit declarations. // See MSColumns for an example. // // // // See MSColumns for the motivation. // class MSSpWindowColumns { public: // Create a columns object that accesses the data in the specified Table MSSpWindowColumns(const MSSpectralWindow& msSpWindow); // The destructor does nothing special ~MSSpWindowColumns(); // Access to required columns // ArrayColumn& chanFreq() {return chanFreq_p;} ArrayMeasColumn& chanFreqMeas() {return chanFreqMeas_p;} ArrayQuantColumn& chanFreqQuant() {return chanFreqQuant_p;} ArrayColumn& chanWidth() {return chanWidth_p;} ArrayQuantColumn& chanWidthQuant() { return chanWidthQuant_p;} ArrayColumn& effectiveBW() {return effectiveBW_p;} ArrayQuantColumn& effectiveBWQuant() { return effectiveBWQuant_p;} ScalarColumn& flagRow() {return flagRow_p;} ScalarColumn& freqGroup() {return freqGroup_p;} ScalarColumn& freqGroupName() {return freqGroupName_p;} ScalarColumn& ifConvChain() {return ifConvChain_p;} ScalarColumn& measFreqRef() {return measFreqRef_p;} ScalarColumn& name() {return name_p;} ScalarColumn& netSideband() {return netSideband_p;} ScalarColumn& numChan() {return numChan_p;} ScalarColumn& refFrequency() {return refFrequency_p;} ScalarQuantColumn& refFrequencyQuant() {return refFrequencyQuant_p;} ScalarMeasColumn& refFrequencyMeas() {return refFrequencyMeas_p;} ArrayColumn& resolution() {return resolution_p;} ArrayQuantColumn& resolutionQuant() { return resolutionQuant_p;} ScalarColumn& totalBandwidth() {return totalBandwidth_p;} ScalarQuantColumn& totalBandwidthQuant() { return totalBandwidthQuant_p;} // // Access to optional columns // ArrayColumn& assocNature() {return assocNature_p;} ArrayColumn& assocSpwId() {return assocSpwId_p;} ScalarColumn& bbcNo() {return bbcNo_p;} ScalarColumn& bbcSideband() {return bbcSideband_p;} ScalarColumn& dopplerId() {return dopplerId_p;} ScalarColumn& receiverId() {return receiverId_p;} // // Const access to columns // const ArrayColumn& chanFreq() const {return chanFreq_p;} const ArrayQuantColumn& chanFreqQuant() const { return chanFreqQuant_p;} const ArrayMeasColumn& chanFreqMeas() const { return chanFreqMeas_p;} const ArrayColumn& chanWidth() const {return chanWidth_p;} const ArrayQuantColumn& chanWidthQuant() const { return chanWidthQuant_p;} const ArrayColumn& effectiveBW() const {return effectiveBW_p;} const ArrayQuantColumn& effectiveBWQuant() const { return effectiveBWQuant_p;} const ScalarColumn& freqGroup() const {return freqGroup_p;} const ScalarColumn& freqGroupName() const {return freqGroupName_p;} const ScalarColumn& ifConvChain() const {return ifConvChain_p;} const ScalarColumn& flagRow() const {return flagRow_p;} const ScalarColumn& measFreqRef() const {return measFreqRef_p;} const ScalarColumn& name() const {return name_p;} const ScalarColumn& netSideband() const {return netSideband_p;} const ScalarColumn& numChan() const {return numChan_p;} const ScalarColumn& refFrequency() const {return refFrequency_p;} const ScalarQuantColumn& refFrequencyQuant() const { return refFrequencyQuant_p;} const ScalarMeasColumn& refFrequencyMeas() const { return refFrequencyMeas_p;} const ArrayColumn& resolution() const {return resolution_p;} const ArrayQuantColumn& resolutionQuant() const { return resolutionQuant_p;} const ScalarColumn& totalBandwidth() const { return totalBandwidth_p;} const ScalarQuantColumn& totalBandwidthQuant() const { return totalBandwidthQuant_p;} // // Const access to optional columns // const ArrayColumn& assocNature() const {return assocNature_p;} const ArrayColumn& assocSpwId() const {return assocSpwId_p;} const ScalarColumn& bbcNo() const {return bbcNo_p;} const ScalarColumn& bbcSideband() const {return bbcSideband_p;} const ScalarColumn& dopplerId() const {return dopplerId_p;} const ScalarColumn& receiverId() const {return receiverId_p;} // // Convenience function that returns the number of rows in any of the columns rownr_t nrow() const {return chanFreq_p.nrow();} // returns the last row that contains a spectral window that has the // specified reference frequency, number of channels, total-bandwidth and IF // conversion chain. All frequencies need to match within the specified // tolerance. Both the totalBandwidth & the tolerance arguments must have the // same dimensions as the Hz and an AipsError exception is thrown, in debug // mode, if the dimensions are wrong. In addition to the numerical values the // frequency reference frame is checked and needs to match the value in the // MEAS_FREQ_REF column. No conversions to other reference frames are // done. Will only try to match on rows where FLAG_ROW is false. If tryRow is // set to a non-negative value then that row is checked first to see if it // matches. An AIpsError exception is thrown if tryRow is bigger than the // number of rows in the Table. Returns -1 if no match could be found. Int64 matchSpw(const MFrequency& refFreq, uInt nChan, const Quantum& bandwidth, Int ifChain, const Quantum& tolerance, Int64 tryRow=-1) const; // Similar to above, but also pass in the frame info. Int64 matchSpw(const MFrequency& refFreq, const MFrequency& chanFreq1, const MeasFrame& measFrm, const MSDopplerColumns& msdopc, const MSSourceColumns& mssrcc, uInt nChan, const Quantum& bandwidth, Int ifChain, const Quantum& tolerance, Int64 tryRow=-1) const; // This is to check that the channels are matched individually // and also if the spw is matched in reverse; //Same as the above but returns all the possible match that it could find // in the spectral window table. RowNumbers allMatchedSpw(const MFrequency& refFreq, uInt nChan, const Quantum& bandwidth, Int ifChain, const Quantum& tolerance) const; //This version does a channel to channel match too and also return // the reversed if it matches but the channels are in inverse order // like an upper or lower side band having same characteristics Int64 matchSpw(const MFrequency& refFreq, uInt nChan, const Quantum& bandwidth, Int ifChain, const Quantum& tolerance, Vector& otherFreqs, Bool& reversed) const; protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. MSSpWindowColumns(); //# attach this object to the supplied table. void attach(const MSSpectralWindow& msSpWindow); private: //# Make the assignment operator and the copy constructor private to prevent //# any compiler generated one from being used. MSSpWindowColumns(const MSSpWindowColumns&); MSSpWindowColumns& operator=(const MSSpWindowColumns&); //# Check if any optional columns exist and if so attach them. void attachOptionalCols(const MSSpectralWindow& msSpWindow); //# functions to match the supplied arguments against the values in the //# specified row. // Bool matchRefFrequency(rownr_t row, MFrequency::Types refType, Double refFreqInHz, Double tolInHz) const; Bool matchRefFreqCnvtrd(rownr_t row, MFrequency refOrChanFreq, const Bool isRefFreq, const MeasFrame& measFrm, const MSDopplerColumns& msdopc, const MSSourceColumns& mssrcc, Double tolInHz) const; Bool matchChanFreq(rownr_t row, const Vector& chanFreqInHz, Double tolInHz) const; Bool matchIfConvChain(rownr_t row, Int ifChain) const; Bool matchTotalBandwidth(rownr_t row, Double bandwidthInHz, Double tolInHz) const; Bool matchNumChan(rownr_t row, Int nChan) const; // //# required columns ArrayColumn chanFreq_p; ArrayColumn chanWidth_p; ArrayColumn effectiveBW_p; ScalarColumn flagRow_p; ScalarColumn freqGroup_p; ScalarColumn freqGroupName_p; ScalarColumn ifConvChain_p; ScalarColumn measFreqRef_p; ScalarColumn name_p; ScalarColumn netSideband_p; ScalarColumn numChan_p; ScalarColumn refFrequency_p; ArrayColumn resolution_p; ScalarColumn totalBandwidth_p; //# optional columns ArrayColumn assocNature_p; ArrayColumn assocSpwId_p; ScalarColumn bbcNo_p; ScalarColumn bbcSideband_p; ScalarColumn dopplerId_p; ScalarColumn receiverId_p; //# Access to Measure columns ArrayMeasColumn chanFreqMeas_p; ScalarMeasColumn refFrequencyMeas_p; //# Access to Quantum columns ArrayQuantColumn chanFreqQuant_p; ArrayQuantColumn chanWidthQuant_p; ArrayQuantColumn effectiveBWQuant_p; ScalarQuantColumn refFrequencyQuant_p; ArrayQuantColumn resolutionQuant_p; ScalarQuantColumn totalBandwidthQuant_p; }; //# Define the RO version for backward compatibility. typedef MSSpWindowColumns ROMSSpWindowColumns; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSSpWindowEnums.h000066400000000000000000000116551476623553700222720ustar00rootroot00000000000000//# MSSpectralWindowEnums.h: Definitions for the MS SPECTRAL_WINDOW table //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSPWINDOWENUMS_H #define MS_MSSPWINDOWENUMS_H #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Enums for the MeasurementSet SPECTRAL_WINDOW table // // // // This class contains the enums for the MeasurementSet SPECTRAL_WINDOW table // // // This class does nothing. It is merely a container for the enumerations // used by the MeasurementSet class. These enumerations define the // standard columns and keywords. // // // See the documentation for MeasurementSet for examples on the use of these // enumerations. // // // All the todo items which may be related to this class are // grouped with the todo items for MeasurementSet // // class MSSpectralWindowEnums { public: // The SpectralWindow table colums with predefined meaning. // The SPECTRAL_WINDOW_ID is the row number in the table. enum PredefinedColumns { // "True" columns are defined.
        // TYPE - UNIT - MEASURE UNDEFINED_COLUMN=0, // Center frequencies for each channel in the data matrix. // Can therefore be non-linear to allow for e.g. AOS
        // Double(NUM_CHAN) - Hz - FREQUENCY CHAN_FREQ, // The channel width for each channel
        // Double(NUM_CHAN) - Hz CHAN_WIDTH, // The effective noise bandwidth of each channel
        // Double(NUM_CHAN) - Hz EFFECTIVE_BW, // Row flag
        // Bool FLAG_ROW, // The frequency group
        // Int FREQ_GROUP, // The frequency group name
        // String FREQ_GROUP_NAME, // The IF conversion chain (to distinguish the separate electronic paths for // simultaneous observations at multiple frequencies). E.g., VLA A-C and // B-D should always be numbered 0 and 1 resp.
        // Int IF_CONV_CHAIN, // The frequency measure reference
        // Int MEAS_FREQ_REF, // Spectral window name
        // String NAME, // Net sideband for this spectral window (+/- 1)
        // Int NET_SIDEBAND, // Number of spectral channels
        // Int NUM_CHAN, // The reference frequency (as specified on-line).
        // Double - Hz - FREQUENCY REF_FREQUENCY, // The effective spectral resolution of each channel // The Vector nature allows for variable-width channels.
        // Double(NUM_CHAN) - Hz RESOLUTION, // The total bandwidth (as specified on-line).
        // Double - Hz TOTAL_BANDWIDTH, // // Not a column, but just an enum specifying the number of required columns. NUMBER_REQUIRED_COLUMNS=TOTAL_BANDWIDTH, // Nature of association with other spectral window id
        // String(*) ASSOC_NATURE, // Associated spectral window id's, e.g. averaged spectra // Int(*) ASSOC_SPW_ID, // Baseband converter number
        // Int BBC_NO, // Baseband converter sideband
        // Int BBC_SIDEBAND, // Doppler id, points to DOPPLER table
        // Int DOPPLER_ID, // Receiver id, identifies receiver used for this spectral window. // May point to optional RECEIVER table
        // Int RECEIVER_ID, // Not a column, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_COLUMNS=RECEIVER_ID }; // Keywords with a predefined meaning enum PredefinedKeywords { // // "True" keywords are defined. UNDEFINED_KEYWORD=0, // // Not a keyword, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_KEYWORDS=0 }; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSSpectralWindow.cc000066400000000000000000000200671476623553700226100ustar00rootroot00000000000000//# MSSpectralWindow.cc: The MeasurementSet FREQUENCY Table //# Copyright (C) 1996,1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSSpectralWindow::MSSpectralWindow():hasBeenDestroyed_p(True) { } MSSpectralWindow::MSSpectralWindow(const String &tableName, TableOption option) : MSTable(tableName, option),hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSSpectralWindow(String &, TableOption) - " "table is not a valid MSSpectralWindow")); } MSSpectralWindow::MSSpectralWindow(const String& tableName, const String &tableDescName, TableOption option) : MSTable(tableName, tableDescName,option), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSSpectralWindow(String &, String &, TableOption) - " "table is not a valid MSSpectralWindow")); } MSSpectralWindow::MSSpectralWindow(SetupNewTable &newTab, rownr_t nrrow, Bool initialize) : MSTable(newTab, nrrow, initialize), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSSpectralWindow(SetupNewTable &, rownr_t, Bool) - " "table is not a valid MSSpectralWindow")); } MSSpectralWindow::MSSpectralWindow(const Table &table) : MSTable(table), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSSpectralWindow(const Table &) - " "table is not a valid MSSpectralWindow")); } MSSpectralWindow::MSSpectralWindow(const MSSpectralWindow &other) : MSTable(other), hasBeenDestroyed_p(False) { // verify that other is valid if (&other != this) if (! validate(this->tableDesc())) throw (AipsError("MSSpectralWindow(const MSSpectralWindow &) - " "table is not a valid MSSpectralWindow")); } MSSpectralWindow::~MSSpectralWindow() { // check to make sure that this MSSpectralWindow is still valid if (!hasBeenDestroyed_p && !validate()) { // the table is otherwise OK, so ensure that it is written if necessary this->flush(); LogIO os; os << LogIO::WARN << "~MSSpectralWindow() - Table written is not a valid MSSpectralWindow" << LogIO::POST; } hasBeenDestroyed_p = True; } MSSpectralWindow& MSSpectralWindow::operator=(const MSSpectralWindow &other) { if (&other != this) { MSTable::operator=(other); hasBeenDestroyed_p=other.hasBeenDestroyed_p; } return *this; } MSTableMaps MSSpectralWindow::initMaps() { MSTableMaps maps; // the PredefinedColumns // // ASSOC_NATURE colMapDef(maps, ASSOC_NATURE,"ASSOC_NATURE", TpArrayString, "Nature of association with other spectral window","",""); // ASSOC_SPW_ID colMapDef(maps, ASSOC_SPW_ID,"ASSOC_SPW_ID",TpArrayInt, "Associated spectral window id","",""); // BBC_NO colMapDef(maps, BBC_NO,"BBC_NO",TpInt, "Baseband converter number","",""); // BBC_SIDEBAND colMapDef(maps, BBC_SIDEBAND,"BBC_SIDEBAND",TpInt, "BBC sideband","",""); // CHAN_FREQ colMapDef(maps, CHAN_FREQ,"CHAN_FREQ", TpArrayDouble, "Center frequencies for each channel in the data matrix", "Hz","Frequency"); // CHAN_WIDTH colMapDef(maps, CHAN_WIDTH,"CHAN_WIDTH",TpArrayDouble, "Channel width for each channel","Hz",""); // DOPPLER_ID colMapDef(maps, DOPPLER_ID,"DOPPLER_ID",TpInt, "Doppler Id, points to DOPPLER table","",""); // EFFECTIVE_BW colMapDef(maps, EFFECTIVE_BW,"EFFECTIVE_BW",TpArrayDouble, "Effective noise bandwidth of each channel","Hz",""); // FLAG_ROW colMapDef(maps, FLAG_ROW,"FLAG_ROW",TpBool, "Row flag","",""); // FREQ_GROUP colMapDef(maps, FREQ_GROUP,"FREQ_GROUP",TpInt, "Frequency group","",""); // FREQ_GROUP_NAME colMapDef(maps, FREQ_GROUP_NAME,"FREQ_GROUP_NAME",TpString, "Frequency group name","",""); // IF_CONV_CHAIN colMapDef(maps, IF_CONV_CHAIN, "IF_CONV_CHAIN", TpInt, "The IF conversion chain number","",""); // MEAS_FREQ_REF colMapDef(maps, MEAS_FREQ_REF,"MEAS_FREQ_REF",TpInt, "Frequency Measure reference","",""); // NAME colMapDef(maps, NAME,"NAME",TpString, "Spectral window name","",""); // NET_SIDEBAND colMapDef(maps, NET_SIDEBAND,"NET_SIDEBAND",TpInt, "Net sideband","",""); // NUM_CHAN colMapDef(maps, NUM_CHAN, "NUM_CHAN", TpInt, "Number of spectral channels","",""); // RECEIVER_ID colMapDef(maps, RECEIVER_ID,"RECEIVER_ID",TpInt, "Receiver Id for this spectral window","",""); // REF_FREQUENCY colMapDef(maps, REF_FREQUENCY, "REF_FREQUENCY", TpDouble, "The reference frequency", "Hz","Frequency"); // RESOLUTION colMapDef(maps, RESOLUTION, "RESOLUTION", TpArrayDouble, "The effective noise bandwidth for each channel", "Hz",""); // TOTAL_BANDWIDTH colMapDef(maps, TOTAL_BANDWIDTH, "TOTAL_BANDWIDTH", TpDouble, "The total bandwidth for this window","Hz",""); // PredefinedKeywords // init requiredTableDesc // all required keywords uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(maps, PredefinedKeywords(i)); } // all required columns // set up the TableMeasure columns with variable reference // first add the variable ref column addColumnToDesc(maps, MEAS_FREQ_REF); addColumnToDesc(maps, CHAN_FREQ,1,"MEAS_FREQ_REF"); addColumnToDesc(maps, REF_FREQUENCY,-1,"MEAS_FREQ_REF"); // define columns with known dimensionality addColumnToDesc(maps, CHAN_WIDTH,1); addColumnToDesc(maps, EFFECTIVE_BW,1); addColumnToDesc(maps, RESOLUTION,1); for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(maps, PredefinedColumns(i)); } return maps; } MSSpectralWindow MSSpectralWindow::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSSpectralWindow(MSTable:: referenceCopy(newTableName,writableColumns)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSSpectralWindow.h000066400000000000000000000117471476623553700224570ustar00rootroot00000000000000//# MSSpectralWindow.h: The MeasurementSet SPECTRALWINDOW Table //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSPECTRALWINDOW_H #define MS_MSSPECTRALWINDOW_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A Table intended to hold a MeasurementSet SPECTRAL_WINDOW table. // // // // //
      • MeasurementSet //
      • MSTable // // // // MSSpectralWindow stands for the MeasurementSet Spectral Window table. // // // // An MSSpectralWindow is a table intended to hold the SPECTRAL_WINDOW table for // the MeasurementSet. It has an identical set of member functions as // the main MeasurementSet class, except (currently) for the default // calibration members. For further info and examples see the // MeasurementSet class. // // // // See the MeasurementSet for an example of how to access and use this class. // // // // It was found that subtables and the main table of the MeasurementSet have // a lot in common, therefore they derive their interface from the same // base class. Each subtable has its own class to keep the enum definitions // and conversion functions in separate scopes. // // // // see MeasurementSet. // class MSSpectralWindow:public MSSpectralWindowEnums, public MSTable { public: // This constructs an empty MSSpectralWindow. MSSpectralWindow (); // These constructors mirror the Table ones with additional checking // on validity (verifying that the MSSpectralWindow will have the required columns // and keywords) // An exception is thrown if the constructed Table is not a valid MSSpectralWindow // //
      • AipsError // // MSSpectralWindow (const String &tableName, TableOption = Table::Old); MSSpectralWindow (const String &tableName, const String &tableDescName, TableOption = Table::Old); MSSpectralWindow (SetupNewTable &newTab, rownr_t nrrow = 0, Bool initialize = False); MSSpectralWindow (const Table &table); MSSpectralWindow (const MSSpectralWindow &other); // // As with tables, the destructor writes the table if necessary. // Additional checking is done here to verify that all required // columns are still present. // If it is NOT valid, it will write the table and then throw an exception. // //
      • AipsError // ~MSSpectralWindow(); // Assignment operator, reference semantics MSSpectralWindow& operator=(const MSSpectralWindow&); // Make a special copy of this Table which references all columns from // this Table except those mentioned; those are empty and writable. // Each forwarded column has the same writable status as the underlying // column. The mentioned columns all use the AipsIO storage manager. // This function is inherited from MSTable and unlikely to be of use, // except in the class MeasurementSet (see comment there).. MSSpectralWindow referenceCopy(const String& newTableName, const Block& writableColumns) const; // Initialize the statics appropriately. This does not need to be // called by users, it is called by the implementation class // MSTableImpl. static MSTableMaps initMaps(); private: // required by the need to throw an exception in the destructor Bool hasBeenDestroyed_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSState.cc000066400000000000000000000127051476623553700207230ustar00rootroot00000000000000//# MSState.cc: The MeasurementSet STATE Table //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSState::MSState():hasBeenDestroyed_p(True) { } MSState::MSState(const String &tableName, TableOption option) : MSTable(tableName, option),hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSState(String &, TableOption) - " "table is not a valid MSState")); } MSState::MSState(const String& tableName, const String &tableDescName, TableOption option) : MSTable(tableName, tableDescName,option), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSState(String &, String &, TableOption) - " "table is not a valid MSState")); } MSState::MSState(SetupNewTable &newTab, rownr_t nrrow, Bool initialize) : MSTable(newTab, nrrow, initialize), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSState(SetupNewTable &, rownr_t, Bool) - " "table is not a valid MSState")); } MSState::MSState(const Table &table) : MSTable(table), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSState(const Table &) - " "table is not a valid MSState")); } MSState::MSState(const MSState &other) : MSTable(other), hasBeenDestroyed_p(False) { // verify that other is valid if (&other != this) if (! validate(this->tableDesc())) throw (AipsError("MSState(const MSState &) - " "table is not a valid MSState")); } MSState::~MSState() { // check to make sure that this MSState is still valid if (!hasBeenDestroyed_p && !validate()) { // the table is otherwise OK, so ensure that it is written if necessary this->flush(); LogIO os; os << LogIO::WARN << "~MSState() - Table written is not a valid MSState" << LogIO::POST; } hasBeenDestroyed_p = True; } MSState& MSState::operator=(const MSState &other) { if (&other != this) { MSTable::operator=(other); hasBeenDestroyed_p=other.hasBeenDestroyed_p; } return *this; } MSTableMaps MSState::initMaps() { MSTableMaps maps; // the PredefinedColumns // CAL colMapDef(maps, CAL,"CAL", TpDouble, "Noise calibration temperature","K",""); // FLAG_ROW colMapDef(maps, FLAG_ROW,"FLAG_ROW", TpBool, "Row flag","",""); // LOAD colMapDef(maps, LOAD,"LOAD", TpDouble, "Load temperature","K",""); // OBS_MODE colMapDef(maps, OBS_MODE,"OBS_MODE", TpString, "Observing mode, e.g., OFF_SPECTRUM","",""); // REF colMapDef(maps, REF,"REF", TpBool, "True for a reference observation","",""); // SIG colMapDef(maps, SIG,"SIG", TpBool, "True for a source observation","",""); // SUB_SCAN colMapDef(maps, SUB_SCAN,"SUB_SCAN", TpInt, "Sub scan number, relative to scan number","",""); // PredefinedKeywords // init requiredTableDesc // all required keywords uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(maps, PredefinedKeywords(i)); } // all required columns for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(maps, PredefinedColumns(i)); } return maps; } MSState MSState::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSState(MSTable:: referenceCopy(newTableName,writableColumns)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSState.h000066400000000000000000000113521476623553700205620ustar00rootroot00000000000000//# MSState.h: The MeasurementSet STATE Table //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSTATE_H #define MS_MSSTATE_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A Table intended to hold a MeasurementSet STATE table. // // // // //
      • MeasurementSet //
      • MSTable // // // // MSState stands for the MeasurementSet State table. // // // // An MSState is a table intended to hold the STATE table for // the MeasurementSet. It has an identical set of member functions as // the main MeasurementSet class, except (currently) for the default // calibration members. For further info and examples see the // MeasurementSet class. // // // // See the MeasurementSet for an example of how to access and use this class. // // // // It was found that subtables and the main table of the MeasurementSet have // a lot in common, therefore they derive their interface from the same // base class. Each subtable has its own class to keep the enum definitions // and conversion functions in separate scopes. // // // // see MeasurementSet. // class MSState:public MSStateEnums, public MSTable { public: // This constructs an empty MSState MSState (); // // These constructors mirror the Table ones with additional checking // on validity (verifying that the MSState will have the required columns // and keywords). // An exception is thrown if the constructed Table is not a valid MSState // //
      • AipsError // // MSState (const String &tableName, TableOption = Table::Old); MSState (const String &tableName, const String &tableDescName, TableOption = Table::Old); MSState (SetupNewTable &newTab, rownr_t nrrow = 0, Bool initialize = False); MSState (const Table &table); MSState (const MSState &other); // // As with tables, the destructor writes the table if necessary. // Additional checking is done here to verify that all required // columns are still present. // If it is NOT valid, it will write the table and then throw an exception. // //
      • AipsError // ~MSState(); // Assignment operator, reference semantics MSState& operator=(const MSState&); // Make a special copy of this Table which references all columns from // this Table except those mentioned; those are empty and writable. // Each forwarded column has the same writable status as the underlying // column. The mentioned columns all use the AipsIO storage manager. // This function is inherited from MSTable and unlikely to be of use, // except in the class MeasurementSet (see comment there).. MSState referenceCopy(const String& newTableName, const Block& writableColumns) const; // Initialize the statics appropriately. This does not need to be // called by users, it is called by the implementation class // MSTableImpl. static MSTableMaps initMaps(); private: // required by the need to throw an exception in the destructor Bool hasBeenDestroyed_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSStateColumns.cc000066400000000000000000000077341476623553700222720ustar00rootroot00000000000000//# MSStateColumns.cc: provides easy access to MeasurementSet columns //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSStateColumns::MSStateColumns() { } MSStateColumns::MSStateColumns(const MSState& msState) { attach(msState); } MSStateColumns::~MSStateColumns() {} void MSStateColumns::attach(const MSState& msState) { cal_p.attach(msState, MSState::columnName(MSState::CAL)); flagRow_p.attach(msState, MSState::columnName(MSState::FLAG_ROW)); load_p.attach(msState, MSState::columnName(MSState::LOAD)); obsMode_p.attach(msState, MSState::columnName(MSState::OBS_MODE)); ref_p.attach(msState, MSState::columnName(MSState::REF)); sig_p.attach(msState, MSState::columnName(MSState::SIG)); subScan_p.attach(msState, MSState::columnName(MSState::SUB_SCAN)); calQuant_p.attach(msState, MSState::columnName(MSState::CAL)); loadQuant_p.attach(msState, MSState::columnName(MSState::LOAD)); } Int64 MSStateColumns::matchState(const Quantum& stateCalQ, const Quantum& stateLoadQ, const String& stateObsMode, const Bool& stateRef, const Bool& stateSig, const Int& stateSubScan, const Quantum& tolerance, Int64 tryRow){ rownr_t r = nrow(); if (r == 0) return -1; // Convert the temperatures and tolerance to Kelvin const Unit k("K"); DebugAssert(tolerance.check(k.getValue()), AipsError); DebugAssert(stateCalQ.check(k.getValue()), AipsError); DebugAssert(stateLoadQ.check(k.getValue()), AipsError); const Double tolInK = tolerance.getValue(k); const Double calInK = stateCalQ.getValue(k); const Double loadInK = stateLoadQ.getValue(k); // Main matching loop if (tryRow >= 0) { const rownr_t tr = tryRow; if (tr >= r) { throw(AipsError("MSStateColumns::matchState(...) - " "row " + String::toString(tr) + " you suggest is too big")); } if (!flagRow()(tr) && std::fabs(calQuant()(tr).getValue(k) - calInK) < tolInK && std::fabs(loadQuant()(tr).getValue(k) - loadInK) < tolInK && obsMode()(tr) == stateObsMode && ref()(tr) == stateRef && sig()(tr) == stateSig && subScan()(tr) == stateSubScan) { return tr; } if (tr == r-1) r--; } while (r > 0) { r--; if (!flagRow()(r) && std::fabs(calQuant()(r).getValue(k) - calInK) < tolInK && std::fabs(loadQuant()(r).getValue(k) - loadInK) < tolInK && obsMode()(r) == stateObsMode && ref()(r) == stateRef && sig()(r) == stateSig && subScan()(r) == stateSubScan) { return r; } } return -1; } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSStateColumns.h000066400000000000000000000132571476623553700221310ustar00rootroot00000000000000//# MSStateColumns.h: provides easy access to MSState columns //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSTATECOLUMNS_H #define MS_MSSTATECOLUMNS_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class MSState; // // A class to provide easy access to MSState columns // // // // // //
      • MSState //
      • ScalarColumn // // // // MSStateColumns stands for MeasurementSet State Table columns. // // // // This class provides access to the columns in the MSState Table, // it does the declaration of all the Scalar and ArrayColumns with the // correct types, so the application programmer doesn't have to // worry about getting those right. There is an access function // for every predefined column. Access to non-predefined columns will still // have to be done with explicit declarations. // See MSColumns for an example. // // // // See MSColumns for the motivation. // class MSStateColumns { public: // Create a columns object that accesses the data in the specified Table MSStateColumns(const MSState& msState); // The destructor does nothing special ~MSStateColumns(); // Access to required columns // ScalarColumn& cal() {return cal_p;} ScalarQuantColumn& calQuant() { return calQuant_p;} ScalarColumn& flagRow() {return flagRow_p;} ScalarColumn& load() {return load_p;} ScalarQuantColumn& loadQuant() { return loadQuant_p;} ScalarColumn& obsMode() {return obsMode_p;} ScalarColumn& ref() {return ref_p;} ScalarColumn& sig() {return sig_p;} ScalarColumn& subScan() {return subScan_p;} // // Const access to required columns // const ScalarColumn& cal() const {return cal_p;} const ScalarQuantColumn& calQuant() const { return calQuant_p;} const ScalarColumn& flagRow() const {return flagRow_p;} const ScalarColumn& load() const {return load_p;} const ScalarQuantColumn& loadQuant() const { return loadQuant_p;} const ScalarColumn& obsMode() const {return obsMode_p;} const ScalarColumn& ref() const {return ref_p;} const ScalarColumn& sig() const {return sig_p;} const ScalarColumn& subScan() const {return subScan_p;} // // Convenience function that returns the number of rows in any of the columns rownr_t nrow() const {return cal_p.nrow();} // Returns the last row that contains a state with the specified values. // For Cal and Load, the tolerance is applied in the match. // Returns -1 if no match could be found. Flagged rows can never match. // If tryRow is non-negative, then that row is tested // to see if it matches before any others are tested. Setting tryRow to a // positive value greater than the table length will throw an exception // (AipsError), when compiled in debug mode. Int64 matchState(const Quantum& stateCalQ, const Quantum& stateLoadQ, const String& stateObsMode, const Bool& stateRef, const Bool& stateSig, const Int& stateSubScan, const Quantum& tolerance, Int64 tryRow=-1); protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. MSStateColumns(); //# attach this object to the supplied table. void attach(const MSState& msState); private: //# Make the assignment operator and the copy constructor private to prevent //# any compiler generated one from being used. MSStateColumns(const MSStateColumns&); MSStateColumns& operator=(const MSStateColumns&); //# required columns ScalarColumn cal_p; ScalarColumn flagRow_p; ScalarColumn load_p; ScalarColumn obsMode_p; ScalarColumn ref_p; ScalarColumn sig_p; ScalarColumn subScan_p; // Access to Quantum columns ScalarQuantColumn calQuant_p; ScalarQuantColumn loadQuant_p; }; //# Define the RO version for backward compatibility. typedef MSStateColumns ROMSStateColumns; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSStateEnums.h000066400000000000000000000063641476623553700216010ustar00rootroot00000000000000//# MSStateEnums.h: Defs for the MS STATE table //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSTATEENUMS_H #define MS_MSSTATEENUMS_H #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Enums for the MeasurementSet STATE table // // // // This class contains the enums for the MeasurementSet STATE table // // // This class does nothing. It is merely a container for the enumerations // used by the MSState class. These enumerations define the // standard columns, keywords. // // // See the documentation for MeasurementSet for examples on the use of these // enumerations. // // // All the todo items which may be related to this class are // grouped with the todo items for MeasurementSet // // class MSStateEnums { public: // The STATE table colums with predefined meaning. enum PredefinedColumns { // "True" columns are defined.
        // TYPE - UNIT - MEASURE UNDEFINED_COLUMN=0, // Noise calibration temperature
        // Double - K CAL, // Row flag
        // Bool FLAG_ROW, // Load temperature
        // Double - K. LOAD, // Observing mode, e.g. OFF_SPECTRUM
        // String OBS_MODE, // True for a reference phase
        // Bool. REF, // True if the source signal is being observed
        // Bool. SIG, // Sub scan number (>=0) relative to scan number in MAIN
        // Int SUB_SCAN, // Number of required columns NUMBER_REQUIRED_COLUMNS=SUB_SCAN, // Not a column, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_COLUMNS=NUMBER_REQUIRED_COLUMNS }; // Keywords with a predefined meaning enum PredefinedKeywords { // // "True" keywords are defined. UNDEFINED_KEYWORD=0, // // Not a keyword, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_KEYWORDS=0 }; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSSysCal.cc000066400000000000000000000201441476623553700210350ustar00rootroot00000000000000//# MSSysCal.cc: The MeasurementSet SYSCAL Table //# Copyright (C) 1996,1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // set hasBeenDestroyed to True to avoid validity check in destructor. MSSysCal::MSSysCal():hasBeenDestroyed_p(True) { } MSSysCal::MSSysCal(const String &tableName, TableOption option) : MSTable(tableName, option), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSSysCal(String &, TableOption) - " "table is not a valid MSSysCal")); } MSSysCal::MSSysCal(const String& tableName, const String &tableDescName, TableOption option) : MSTable(tableName, tableDescName,option), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSSysCal(String &, String &, TableOption) - " "table is not a valid MSSysCal")); } MSSysCal::MSSysCal(SetupNewTable &newTab, rownr_t nrrow, Bool initialize) : MSTable(newTab, nrrow, initialize), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSSysCal(SetupNewTable &, rownr_t, Bool) - " "table is not a valid MSSysCal")); } MSSysCal::MSSysCal(const Table &table) : MSTable(table), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSSysCal(const Table &) - " "table is not a valid MSSysCal")); } MSSysCal::MSSysCal(const MSSysCal &other) : MSTable(other), hasBeenDestroyed_p(False) { // verify that other is valid if (&other != this) if (! validate(this->tableDesc())) throw (AipsError("MSSysCal(const MSSysCal &) - " "table is not a valid MSSysCal")); } MSSysCal::~MSSysCal() { // check to make sure that this MSSysCal is still valid if (!hasBeenDestroyed_p && !validate()) { // the table is otherwise OK, so ensure that it is written if necessary this->flush(); LogIO os; os << LogIO::WARN << "~MSSysCal() - Table written is not a valid MSSysCal" << LogIO::POST; } hasBeenDestroyed_p = True; } MSSysCal& MSSysCal::operator=(const MSSysCal &other) { if (&other != this) { MSTable::operator=(other); hasBeenDestroyed_p=other.hasBeenDestroyed_p; } return *this; } MSTableMaps MSSysCal::initMaps() { MSTableMaps maps; AlwaysAssert (maps.columnMap_p.empty(), AipsError); // the PredefinedColumns // ANTENNA_ID colMapDef(maps, ANTENNA_ID, "ANTENNA_ID", TpInt, "ID of antenna in this array","",""); // FEED_ID colMapDef(maps, FEED_ID,"FEED_ID",TpInt, "Feed id","",""); // INTERVAL colMapDef(maps, INTERVAL,"INTERVAL",TpDouble, "Interval for which this set of parameters is accurate", "s",""); // SPECTRAL_WINDOW_ID colMapDef(maps, SPECTRAL_WINDOW_ID,"SPECTRAL_WINDOW_ID",TpInt, "ID for this spectral window setup","",""); // TIME colMapDef(maps, TIME,"TIME",TpDouble, "Midpoint of time for which this set of " "parameters is accurate","s","Epoch"); // PHASE_DIFF colMapDef(maps, PHASE_DIFF,"PHASE_DIFF",TpFloat, "Phase difference between receptor 2 and receptor 1", "rad",""); // PHASE_DIFF_FLAG colMapDef(maps, PHASE_DIFF_FLAG,"PHASE_DIFF_FLAG",TpBool, "Flag for PHASE_DIFF","",""); // TANT colMapDef(maps, TANT,"TANT",TpArrayFloat, "Antenna temperature for each receptor","K",""); // TANT_FLAG colMapDef(maps, TANT_FLAG,"TANT_FLAG",TpBool, "Flag for TANT","",""); // TANT_SPECTRUM colMapDef(maps, TANT_SPECTRUM,"TANT_SPECTRUM",TpArrayFloat, "Antenna temperature for each channel and receptor","K",""); // TANT_TSYS colMapDef(maps, TANT_TSYS,"TANT_TSYS",TpArrayFloat, "Ratio of Antenna & system temperature for each receptor", "",""); // TANT_TSYS_FLAG colMapDef(maps, TANT_TSYS_FLAG,"TANT_TSYS_FLAG",TpBool, "Flag for TANT_TSYS","",""); // TANT_TSYS_SPECTRUM colMapDef(maps, TANT_TSYS_SPECTRUM,"TANT_TSYS_SPECTRUM",TpArrayFloat, "Ratio of Antenna & system temperature for each channel " "and receptor","",""); // TCAL colMapDef(maps, TCAL,"TCAL",TpArrayFloat, "Calibration temperature for each receptor","K",""); // TCAL_FLAG colMapDef(maps, TCAL_FLAG,"TCAL_FLAG",TpBool, "Flag for TCAL","",""); // TCAL_SPECTRUM colMapDef(maps, TCAL_SPECTRUM,"TCAL_SPECTRUM",TpArrayFloat, "Calibration temperature for each channel and receptor","K",""); // TRX colMapDef(maps, TRX,"TRX",TpArrayFloat, "Receiver temperature for each of the two receptors","K",""); // TRX_FLAG colMapDef(maps, TRX_FLAG,"TRX_FLAG",TpBool, "Flag for TRX","",""); // TRX_SPECTRUM colMapDef(maps, TRX_SPECTRUM,"TRX_SPECTRUM",TpArrayFloat, "Receiver temperature for each channel and receptor","K",""); // TSKY colMapDef(maps, TSKY,"TSKY",TpArrayFloat, "Sky temperature for each of the two receptors","K",""); // TSKY_FLAG colMapDef(maps, TSKY_FLAG,"TSKY_FLAG",TpBool, "Flag for TSKY","",""); // TSKY_SPECTRUM colMapDef(maps, TSKY_SPECTRUM,"TSKY_SPECTRUM",TpArrayFloat, "Sky temperature for each channel and receptor","K",""); // TSYS colMapDef(maps, TSYS,"TSYS",TpArrayFloat, "System temp. for each of the two receptors","K",""); // TSYS_FLAG colMapDef(maps, TSYS_FLAG,"TSYS_FLAG",TpBool, "Flag for TSYS","",""); // TSYS_SPECTRUM colMapDef(maps, TSYS_SPECTRUM,"TSYS_SPECTRUM",TpArrayFloat, "System temperature for each channel and receptor","K",""); // PredefinedKeywords // init requiredTableDesc // all required keywords uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(maps, PredefinedKeywords(i)); } // all required columns for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(maps, PredefinedColumns(i)); } return maps; } MSSysCal MSSysCal::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSSysCal(MSTable::referenceCopy (newTableName,writableColumns)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSSysCal.h000066400000000000000000000114031476623553700206750ustar00rootroot00000000000000//# MSSysCal.h: The MeasurementSet SYSCAL Table //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSYSCAL_H #define MS_MSSYSCAL_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A Table intended to hold a MeasurementSet SYSCAL table. // // // // //
      • MeasurementSet //
      • MSTable // // // // MSSysCal stands for the MeasurementSet SysCal table. // // // // An MSSysCal is a table intended to hold the SYSCAL table for // the MeasurementSet. It has an identical set of member functions as // the main MeasurementSet class, except (currently) for the default // calibration members. For further info and examples see the // MeasurementSet class. // // // // See the MeasurementSet for an example of how to access and use this class. // // // // It was found that subtables and the main table of the MeasurementSet have // a lot in common, therefore they derive their interface from the same // base class. Each subtable has its own class to keep the enum definitions // and conversion functions in separate scopes. // // // // see MeasurementSet. // class MSSysCal:public MSSysCalEnums, public MSTable { public: // This constructs an empty MSSysCal. MSSysCal (); // These constructors mirror the Table ones with additional checking // on validity (verifying that the MSSysCal will have the required columns // and keywords) // An exception is thrown if the constructed Table is not a valid MSSysCal // //
      • AipsError // // MSSysCal (const String &tableName, TableOption = Table::Old); MSSysCal (const String &tableName, const String &tableDescName, TableOption = Table::Old); MSSysCal (SetupNewTable &newTab, rownr_t nrrow = 0, Bool initialize = False); MSSysCal (const Table &table); MSSysCal (const MSSysCal &other); // // As with tables, the destructor writes the table if necessary. // Additional checking is done here to verify that all required // columns are still present. // If it is NOT valid, it will write the table and then throw an exception. // //
      • AipsError // ~MSSysCal(); // Assignment operator, reference semantics MSSysCal& operator=(const MSSysCal&); // Make a special copy of this Table which references all columns from // this Table except those mentioned; those are empty and writable. // Each forwarded column has the same writable status as the underlying // column. The mentioned columns all use the AipsIO storage manager. // This function is inherited from MSTable and unlikely to be of use, // except in the class MeasurementSet (see comment there).. MSSysCal referenceCopy(const String& newTableName, const Block& writableColumns) const; // Initialize the statics appropriately. This does not need to be // called by users, it is called by the implementation class // MSTableImpl. static MSTableMaps initMaps(); private: // required by the need to throw an exception in the destructor Bool hasBeenDestroyed_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSSysCalColumns.cc000066400000000000000000000145241476623553700224030ustar00rootroot00000000000000//# MSSysCalColumns.cc: provides easy access to MeasurementSet columns //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSSysCalColumns::MSSysCalColumns(): isNull_p(True) { } MSSysCalColumns::MSSysCalColumns(const MSSysCal& msSysCal): isNull_p(True) { attach(msSysCal); } MSSysCalColumns::~MSSysCalColumns() {} void MSSysCalColumns::attach(const MSSysCal& msSysCal) { isNull_p = msSysCal.isNull(); if (!isNull()) { antennaId_p.attach(msSysCal, MSSysCal:: columnName(MSSysCal::ANTENNA_ID)); feedId_p.attach(msSysCal, MSSysCal::columnName(MSSysCal::FEED_ID)); interval_p.attach(msSysCal, MSSysCal:: columnName(MSSysCal::INTERVAL)); spectralWindowId_p.attach(msSysCal, MSSysCal:: columnName(MSSysCal::SPECTRAL_WINDOW_ID)); time_p.attach(msSysCal, MSSysCal::columnName(MSSysCal::TIME)); timeMeas_p.attach(msSysCal, MSSysCal::columnName(MSSysCal::TIME)); intervalQuant_p.attach(msSysCal, MSSysCal:: columnName(MSSysCal::INTERVAL)); timeQuant_p.attach(msSysCal, MSSysCal::columnName(MSSysCal::TIME)); const ColumnDescSet& cds = msSysCal.tableDesc().columnDescSet(); const String& phaseDiff = MSSysCal::columnName(MSSysCal::PHASE_DIFF); if (cds.isDefined(phaseDiff)) { phaseDiff_p.attach(msSysCal, phaseDiff); phaseDiffQuant_p.attach(msSysCal, phaseDiff); } const String& phaseDiffFlag = MSSysCal::columnName(MSSysCal::PHASE_DIFF_FLAG); if (cds.isDefined(phaseDiffFlag)) { phaseDiffFlag_p.attach(msSysCal, phaseDiffFlag); } const String& tant = MSSysCal::columnName(MSSysCal::TANT); if (cds.isDefined(tant)) { tant_p.attach(msSysCal, tant); tantQuant_p.attach(msSysCal, tant); } const String& tantFlag = MSSysCal::columnName(MSSysCal::TANT_FLAG); if (cds.isDefined(tantFlag)) tantFlag_p.attach(msSysCal, tantFlag); const String& tantSpectrum = MSSysCal::columnName(MSSysCal::TANT_SPECTRUM); if (cds.isDefined(tantSpectrum)) { tantSpectrum_p.attach(msSysCal, tantSpectrum); tantSpectrumQuant_p.attach(msSysCal, tantSpectrum); } const String& tantTsys = MSSysCal::columnName(MSSysCal::TANT_TSYS); if (cds.isDefined(tantTsys)) tantTsys_p.attach(msSysCal, tantTsys); const String& tantTsysFlag = MSSysCal::columnName(MSSysCal::TANT_TSYS_FLAG); if (cds.isDefined(tantTsysFlag)) { tantTsysFlag_p.attach(msSysCal, tantTsysFlag); } const String& tantTsysSpectrum = MSSysCal::columnName(MSSysCal::TANT_TSYS_SPECTRUM); if (cds.isDefined(tantTsysSpectrum)) { tantTsysSpectrum_p.attach(msSysCal, tantTsysSpectrum); } const String& tcal = MSSysCal::columnName(MSSysCal::TCAL); if (cds.isDefined(tcal)) { tcal_p.attach(msSysCal, tcal); tcalQuant_p.attach(msSysCal, tcal); } const String& tcalFlag = MSSysCal::columnName(MSSysCal::TCAL_FLAG); if (cds.isDefined(tcalFlag)) tcalFlag_p.attach(msSysCal, tcalFlag); const String& tcalSpectrum = MSSysCal::columnName(MSSysCal::TCAL_SPECTRUM); if (cds.isDefined(tcalSpectrum)) { tcalSpectrum_p.attach(msSysCal, tcalSpectrum); tcalSpectrumQuant_p.attach(msSysCal, tcalSpectrum); } const String& trx = MSSysCal::columnName(MSSysCal::TRX); if (cds.isDefined(trx)) { trx_p.attach(msSysCal, trx); trxQuant_p.attach(msSysCal, trx); } const String& trxFlag = MSSysCal::columnName(MSSysCal::TRX_FLAG); if (cds.isDefined(trxFlag)) trxFlag_p.attach(msSysCal, trxFlag); const String& trxSpectrum = MSSysCal::columnName(MSSysCal::TRX_SPECTRUM); if (cds.isDefined(trxSpectrum)) { trxSpectrum_p.attach(msSysCal, trxSpectrum); trxSpectrumQuant_p.attach(msSysCal, trxSpectrum); } const String& tsky = MSSysCal::columnName(MSSysCal::TSKY); if (cds.isDefined(tsky)) { tsky_p.attach(msSysCal, tsky); tskyQuant_p.attach(msSysCal, tsky); } const String& tskyFlag = MSSysCal::columnName(MSSysCal::TSKY_FLAG); if (cds.isDefined(tskyFlag)) tskyFlag_p.attach(msSysCal, tskyFlag); const String& tskySpectrum = MSSysCal::columnName(MSSysCal::TSKY_SPECTRUM); if (cds.isDefined(tskySpectrum)) { tskySpectrum_p.attach(msSysCal, tskySpectrum); tskySpectrumQuant_p.attach(msSysCal, tskySpectrum); } const String& tsys = MSSysCal::columnName(MSSysCal::TSYS); if (cds.isDefined(tsys)) { tsys_p.attach(msSysCal, tsys); tsysQuant_p.attach(msSysCal, tsys); } const String& tsysFlag = MSSysCal::columnName(MSSysCal::TSYS_FLAG); if (cds.isDefined(tsysFlag)) tsysFlag_p.attach(msSysCal, tsysFlag); const String& tsysSpectrum = MSSysCal::columnName(MSSysCal::TSYS_SPECTRUM); if (cds.isDefined(tsysSpectrum)) { tsysSpectrum_p.attach(msSysCal, tsysSpectrum); tsysSpectrumQuant_p.attach(msSysCal, tsysSpectrum); } } } void MSSysCalColumns::setEpochRef(MEpoch::Types ref, Bool tableMustBeEmpty) { timeMeas_p.setDescRefCode(ref, tableMustBeEmpty); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSSysCalColumns.h000066400000000000000000000265741476623553700222550ustar00rootroot00000000000000//# MSSysCalColumns.h: provides easy access to MSSysCal columns //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSYSCALCOLUMNS_H #define MS_MSSYSCALCOLUMNS_H #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class MSSysCal; // // A class to provide easy access to MSSysCal columns // // // // // //
      • MSSysCal //
      • ArrayColumn //
      • ScalarColumn // // // // MSSysCalColumns stands for MeasurementSet SysCal Table columns. // // // // This class provides access to the columns in the MSSysCal Table, // it does the declaration of all the Scalar and ArrayColumns with the // correct types, so the application programmer doesn't have to // worry about getting those right. There is an access function // for every predefined column. Access to non-predefined columns will still // have to be done with explicit declarations. // See MSColumns for an example. // // // // See MSColumns for the motivation. // class MSSysCalColumns { public: // Create a columns object that accesses the data in the specified Table MSSysCalColumns(const MSSysCal& msSysCal); // The destructor does nothing special ~MSSysCalColumns(); // Is this object defined? (MSSysCal table is optional) Bool isNull() const {return isNull_p;} // Access to required columns // ScalarColumn& antennaId() {return antennaId_p;} ScalarColumn& feedId() {return feedId_p;} ScalarColumn& interval() {return interval_p;} ScalarQuantColumn& intervalQuant() {return intervalQuant_p;} ScalarColumn& spectralWindowId() {return spectralWindowId_p;} ScalarColumn& time() {return time_p;} ScalarQuantColumn& timeQuant() {return timeQuant_p;} ScalarMeasColumn& timeMeas() {return timeMeas_p;} // // Access to optional columns // ScalarColumn& phaseDiff() {return phaseDiff_p;} ScalarQuantColumn& phaseDiffQuant() {return phaseDiffQuant_p;} ScalarColumn& phaseDiffFlag() {return phaseDiffFlag_p;} ArrayColumn& tant() {return tant_p;} ArrayQuantColumn& tantQuant() {return tantQuant_p;} ScalarColumn& tantFlag() {return tantFlag_p;} ArrayColumn& tantSpectrum() {return tantSpectrum_p;} ArrayQuantColumn& tantSpectrumQuant() {return tantSpectrumQuant_p;} ArrayColumn& tantTsys() {return tantTsys_p;} ScalarColumn& tantTsysFlag() {return tantTsysFlag_p;} ArrayColumn& tantTsysSpectrum() {return tantTsysSpectrum_p;} ArrayColumn& tcal() {return tcal_p;} ArrayQuantColumn& tcalQuant() {return tcalQuant_p;} ScalarColumn& tcalFlag() {return tcalFlag_p;} ArrayColumn& tcalSpectrum() {return tcalSpectrum_p;} ArrayQuantColumn& tcalSpectrumQuant() {return tcalSpectrumQuant_p;} ArrayColumn& trx() {return trx_p;} ArrayQuantColumn& trxQuant() {return trxQuant_p;} ScalarColumn& trxFlag() {return trxFlag_p;} ArrayColumn& trxSpectrum() {return trxSpectrum_p;} ArrayQuantColumn& trxSpectrumQuant() {return trxSpectrumQuant_p;} ArrayColumn& tsky() {return tsky_p;} ArrayQuantColumn& tskyQuant() {return tskyQuant_p;} ScalarColumn& tskyFlag() {return tskyFlag_p;} ArrayColumn& tskySpectrum() {return tskySpectrum_p;} ArrayQuantColumn& tskySpectrumQuant() {return tskySpectrumQuant_p;} ArrayColumn& tsys() {return tsys_p;} ArrayQuantColumn& tsysQuant() {return tsysQuant_p;} ScalarColumn& tsysFlag() {return tsysFlag_p;} ArrayColumn& tsysSpectrum() {return tsysSpectrum_p;} ArrayQuantColumn& tsysSpectrumQuant() {return tsysSpectrumQuant_p;} // // Const access to columns // const ScalarColumn& antennaId() const {return antennaId_p;} const ScalarColumn& feedId() const {return feedId_p;} const ScalarColumn& interval() const {return interval_p;} const ScalarQuantColumn& intervalQuant() const { return intervalQuant_p;} const ScalarColumn& spectralWindowId() const { return spectralWindowId_p;} const ScalarColumn& time() const {return time_p;} const ScalarQuantColumn& timeQuant() const {return timeQuant_p;} const ScalarMeasColumn& timeMeas() const {return timeMeas_p;} // // Const access to optional columns // const ScalarColumn& phaseDiff() const {return phaseDiff_p;} const ScalarQuantColumn& phaseDiffQuant() const { return phaseDiffQuant_p;} const ScalarColumn& phaseDiffFlag() const {return phaseDiffFlag_p;} const ArrayColumn& tant() const {return tant_p;} const ArrayQuantColumn& tantQuant() const {return tantQuant_p;} const ScalarColumn& tantFlag() const {return tantFlag_p;} const ArrayColumn& tantSpectrum() const {return tantSpectrum_p;} const ArrayQuantColumn& tantSpectrumQuant() const { return tantSpectrumQuant_p;} const ArrayColumn& tantTsys() const {return tantTsys_p;} const ScalarColumn& tantTsysFlag() const {return tantTsysFlag_p;} const ArrayColumn& tantTsysSpectrum() const { return tantTsysSpectrum_p;} const ArrayColumn& tcal() const {return tcal_p;} const ArrayQuantColumn& tcalQuant() const {return tcalQuant_p;} const ScalarColumn& tcalFlag() const {return tcalFlag_p;} const ArrayColumn& tcalSpectrum() const {return tcalSpectrum_p;} const ArrayQuantColumn& tcalSpectrumQuant() const { return tcalSpectrumQuant_p;} const ArrayColumn& trx() const {return trx_p;} const ArrayQuantColumn& trxQuant() const {return trxQuant_p;} const ScalarColumn& trxFlag() const {return trxFlag_p;} const ArrayColumn& trxSpectrum() const {return trxSpectrum_p;} const ArrayQuantColumn& trxSpectrumQuant() const { return trxSpectrumQuant_p;} const ArrayColumn& tsky() const {return tsky_p;} const ArrayQuantColumn& tskyQuant() const {return tskyQuant_p;} const ScalarColumn& tskyFlag() const {return tskyFlag_p;} const ArrayColumn& tskySpectrum() const {return tskySpectrum_p;} const ArrayQuantColumn& tskySpectrumQuant() const { return tskySpectrumQuant_p;} const ArrayColumn& tsys() const {return tsys_p;} const ArrayQuantColumn& tsysQuant() const {return tsysQuant_p;} const ScalarColumn& tsysFlag() const {return tsysFlag_p;} const ArrayColumn& tsysSpectrum() const {return tsysSpectrum_p;} const ArrayQuantColumn& tsysSpectrumQuant() const { return tsysSpectrumQuant_p;} // // Convenience function that returns the number of rows in any of the // columns. Returns zero if the object is null. rownr_t nrow() const {return isNull() ? 0 : antennaId_p.nrow();} // set the epoch type for the TIME column. // // In principle this function can only be used if the table is empty, // otherwise already written values may thereafter have an incorrect // reference, offset, or unit. However, it is possible that part of the // table gets written before these values are known. In that case the // reference, offset, or units can be set by using a False // tableMustBeEmpty argument. // void setEpochRef(MEpoch::Types ref, Bool tableMustBeEmpty=True); protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. MSSysCalColumns(); //# attach this object to the supplied table. void attach(const MSSysCal& msSysCal); private: //# Make the assignment operator and the copy constructor private to prevent //# any compiler generated one from being used. MSSysCalColumns(const MSSysCalColumns&); MSSysCalColumns& operator=(const MSSysCalColumns&); //# Check if any optional columns exist and if so attach them. void attachOptionalCols(const MSSysCal& msSysCal); //# Is the object not attached to a Table. Bool isNull_p; //# required columns ScalarColumn antennaId_p; ScalarColumn feedId_p; ScalarColumn interval_p; ScalarColumn spectralWindowId_p; ScalarColumn time_p; //# optional columns ScalarColumn phaseDiff_p; ScalarColumn phaseDiffFlag_p; ArrayColumn tant_p; ScalarColumn tantFlag_p; ArrayColumn tantSpectrum_p; ArrayColumn tantTsys_p; ScalarColumn tantTsysFlag_p; ArrayColumn tantTsysSpectrum_p; ArrayColumn tcal_p; ScalarColumn tcalFlag_p; ArrayColumn tcalSpectrum_p; ArrayColumn trx_p; ScalarColumn trxFlag_p; ArrayColumn trxSpectrum_p; ArrayColumn tsky_p; ScalarColumn tskyFlag_p; ArrayColumn tskySpectrum_p; ArrayColumn tsys_p; ScalarColumn tsysFlag_p; ArrayColumn tsysSpectrum_p; //# Access to Measure columns ScalarMeasColumn timeMeas_p; //# Access to Quantum columns ScalarQuantColumn intervalQuant_p; ScalarQuantColumn timeQuant_p; //# optional Quantum columns ScalarQuantColumn phaseDiffQuant_p; ArrayQuantColumn tantQuant_p; ArrayQuantColumn tantSpectrumQuant_p; ArrayQuantColumn tcalQuant_p; ArrayQuantColumn tcalSpectrumQuant_p; ArrayQuantColumn trxQuant_p; ArrayQuantColumn trxSpectrumQuant_p; ArrayQuantColumn tskyQuant_p; ArrayQuantColumn tskySpectrumQuant_p; ArrayQuantColumn tsysQuant_p; ArrayQuantColumn tsysSpectrumQuant_p; }; //# Define the RO version for backward compatibility. typedef MSSysCalColumns ROMSSysCalColumns; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSSysCalEnums.h000066400000000000000000000121021476623553700217020ustar00rootroot00000000000000//# MSSysCalEnums.h: Class with definitions for the MSSysCal table //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSSYSCALENUMS_H #define MS_MSSYSCALENUMS_H #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Enums for the MeasurementSet SYSCAL table // // // // This class contains the enums for the MeasurementSet SYSCAL table // // // This class does nothing. It is merely a container for the enumerations // used by the MSSysCal class. These enumerations define the // standard columns, keywords. // // // See the documentation for MeasurementSet for examples on the use of these // enumerations. // // // All the todo items which may be related to this class are // grouped with the todo items for MeasurementSet // // class MSSysCalEnums { public: // The SYSCAL table colums with predefined meaning. // Keys: ANTENNA_ID, ARRAY_ID, FEED_ID, SPECTRAL_WINDOW_ID, INTERVAL, TIME enum PredefinedColumns { // "True" columns are defined.
        // TYPE - UNIT - MEASURE UNDEFINED_COLUMN=0, // Antenna Id.
        // Int. ANTENNA_ID, // Feed id
        // Int FEED_ID, // Interval for which this set of parameters is accurate
        // Double - s INTERVAL, // Spectral window id
        // Int SPECTRAL_WINDOW_ID, // Midpoint of time for which this set of parameters is accurate
        // Double - s - EPOCH TIME, // Enum specifying the number of required columns NUMBER_REQUIRED_COLUMNS=TIME, // Phase difference between receptor 2 and receptor 1. Not used // for single polarization feeds.
        // Float - rad PHASE_DIFF, // Flag for PHASE_DIFF
        // Bool PHASE_DIFF_FLAG, // Antenna temperature
        // Float(NUM_RECEPTORS) - K TANT, // Flag for TANT
        // Bool TANT_FLAG, // Antenna temperature for each channel and receptor
        // Float(NUM_RECEPTORS,NUM_CHAN) - K TANT_SPECTRUM, // Ratio of antenna temperature and system temperature
        // Float(NUM_RECEPTORS) - K TANT_TSYS, // Flag for TANT_TSYS
        // Bool TANT_TSYS_FLAG, // Spectrum of Tant/Tsys ratio for each receptor
        // Float(NUM_RECEPTORS,NUM_CHAN) TANT_TSYS_SPECTRUM, // Calibration temperature for each receptor
        // Float(NUM_RECEPTORS) - K TCAL, // Flag for TCAL
        // Bool TCAL_FLAG, // Calibration temp. for each channel and receptor
        // Float(NUM_RECEPTORS,NUM_CHAN) - K TCAL_SPECTRUM, // Receiver temperature for each of the two receptors. This is // a scalar quantity
        // Float(NUM_RECEPTORS) - K TRX, // Flag for TRX
        // Bool TRX_FLAG, // Receiver temp. for each channel and receptor
        // Float(NUM_RECEPTORS,NUM_CHAN) - K TRX_SPECTRUM, // Sky temperature for each of the two receptors.
        // Float(NUM_RECEPTORS) - K TSKY, // Flag for TSKY
        // Bool TSKY_FLAG, // Sky temp. for each channel and receptor
        // Float(NUM_RECEPTORS,NUM_CHAN) - K TSKY_SPECTRUM, // System temp. for each of the two receptors.
        // Float(NUM_RECEPTORS) - K TSYS, // Flag for TSYS
        // Bool TSYS_FLAG, // System temp. for each channel and receptor
        // Float(NUM_RECEPTORS,NUM_CHAN) - K TSYS_SPECTRUM, // Not a column, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_COLUMNS=TSYS_SPECTRUM }; // Keywords with a predefined meaning enum PredefinedKeywords { // // "True" keywords are defined. UNDEFINED_KEYWORD=0, // // Not a keyword, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_KEYWORDS=0 }; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSTable.cc000066400000000000000000000050131476623553700206640ustar00rootroot00000000000000//# MSTable.cc: the class that hold measurements from telescopes //# Copyright (C) 1996,1997,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Int MSTableMaps::mapType (const std::map& nameMap, const String& name) const { // find first occurrence of name in the map (should be only occurrence) Int type = 0; //# 0=UNDEFINED_COLUMN for all enums for (const auto& kv : nameMap) { if (kv.second == name) { type = kv.first; break; } } return type; } // Instantiate the templates. template class MSTable; template class MSTable; template class MSTable; template class MSTable; template class MSTable; template class MSTable; template class MSTable; template class MSTable; template class MSTable; template class MSTable; template class MSTable; template class MSTable; template class MSTable; template class MSTable; template class MSTable; template class MSTable; template class MSTable; template class MSTable; } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSTable.h000066400000000000000000000331501476623553700205310ustar00rootroot00000000000000//# MSTable.h: A Table to hold astronomical data (a set of Measurements) //# Copyright (C) 1996,1997,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSTABLE_H #define MS_MSTABLE_H #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations, more could be if they weren't part of the //# static classes class TableRecord; template class Block; // // A struct holding the maps used in MSTable. // struct MSTableMaps { // ColEnum -> name std::map columnMap_p; // ColEnum -> DataType std::map colDTypeMap_p; // ColEnum -> comment string std::map colCommentMap_p; // ColEnum -> UNIT string std::map colUnitMap_p; // ColEnum -> MEASURE_TYPE string std::map colMeasureTypeMap_p; // KeyEnum -> name std::map keywordMap_p; // KeyEnum -> DataType std::map keyDTypeMap_p; // KeyEnum -> comment string std::map keyCommentMap_p; // The required TableDesc TableDesc requiredTD_p; // Convert a name to a ColEnum or KeyEnum. Int columnType (const String& name) const { return mapType (columnMap_p, name); } Int keywordType (const String& name) const { return mapType (keywordMap_p, name); } Int mapType (const std::map&, const String& name) const; }; // // A Table intended to hold astronomical data // // // // //
      • Tables module // // // // The MSTable is the base class for all MeasurementSet Tables, hence the // name. // // // // A MSTable is a Table. Most operations on a MSTable are // Table operations. See the Tables // module for a list of those operations. The member functions provided by this // class are primarily convenience functions to help users follow the // agreed upon column and keyword naming conventions. They are useful when // creating a Table following the MSTable conventions from // scratch as well as when creating the column objects to access those // columns. All actual MeasurementSet Tables will be derived from this class. // // The standard way of accessing // table columns is through Strings. Mistakes in typing the column // name will not be caught at compile time (and may not be caught at // run time). We have therefore decided to use an enumeration // to specify columns so that many mistakes will be caught at compile // time. This requires functions to map to and from this enumeration // to the strings that are ultimately used. // // Upon destruction, the table is checked to see that all // required columns and keywords are still present. // If not an exception is thrown. (Not a good idea!) Nevertheless, // the table will be flushed to disk if it is writable - // preserving its state. // // // // // For examples of use, see the MeasurementSet class. // // // // The Table module is more than adequate as a container of data. // However, in order for applications to be useful with data from // different sources, some conventions need to be adopted in the use // of Tables to store data. The MSTable provides the framework for // these conventions and conversion functions. The actual definitions // of columns and keywords are found in the derived classes and their // "enum" base class (e.g. MSAntenna and MSAntennaEnums). // // // //
      • referenceCopy() should be more flexible with the storage managers used // for the columns which are not merely references. //
      • When ForwardColumnEngine is fixed so that it can deal with // tables already in the cache, modify the test program. It may also // be necessary to modify referenceCopy(). // template class MSTable : public Table { public: // Define the column and keyword enuym types. typedef typename MSEnum::PredefinedColumns ColEnum; typedef typename MSEnum::PredefinedKeywords KeyEnum; // ColEnum convenience functions // // check to see if a column exists Bool isColumn(ColEnum which) const; // check to see if a column is writable // Bool isColumnWritable(ColEnum which) const; Bool isColumnWritable (const String& columnName) const { return Table::isColumnWritable(columnName); } Bool isColumnWritable (uInt columnIndex) const { return Table::isColumnWritable(columnIndex); } // // Information about scalar vs array of a column // Bool isScalar(ColEnum which) const; Bool isArray(ColEnum which) const; // // Return the UNIT keyword value associated with the specified column // const String& unit(const String& which) const; const String& unit(ColEnum which) const { return unit(columnName(which)); } // // Convert a ColEnum to the actual column name. static const String& columnName(ColEnum which); // Convert a name to a ColEnum static ColEnum columnType(const String &name); // return the data type for a given ColEnum static DataType columnDataType(ColEnum which); // return the standard comment for a given ColEnum static const String& columnStandardComment(ColEnum which); // return the UNIT string for a given ColEnum static const String& columnUnit(ColEnum which); // return the MEASURE_TYPE string for a given ColEnum static const String& columnMeasureType(ColEnum which); // add a column to a TableDesc // An exception is thrown for an invalid data type. This indicates a // programming error in this class when this occurs. // For Array columns you can optionally define the dimension here. // For Measure columns you can define a variable reference column. // //
      • AipsError // static void addColumnToDesc(TableDesc & tabDesc, ColEnum which, Int ndim=-1,const String& refCol=""); // add a column to a TableDesc, defining the shape and setting // the ColumnDesc option (Fixed, Undefined, Direct) // For Measure columns you can define a variable reference column. static void addColumnToDesc(TableDesc & tabDesc, ColEnum which, const IPosition& shape, ColumnDesc::Option option, const String& refCol=""); static void addColumnToDesc(MSTableMaps&, ColEnum which, Int ndim=-1,const String& refCol=""); // add a column to a TableDesc, defining the shape and setting // the ColumnDesc option (Fixed, Undefined, Direct) // For Measure columns you can define a variable reference column. static void addColumnToDesc(MSTableMaps&, ColEnum which, const IPosition& shape, ColumnDesc::Option option, const String& refCol=""); // // KeyEnum convenience functions // static const String& keywordName(KeyEnum which); static KeyEnum keywordType(const String &name); static DataType keywordDataType(KeyEnum which); static const String& keywordStandardComment(KeyEnum which); // check to see if a keyword exists Bool isKeyword(KeyEnum which) const; // add a keyword to a TableDesc // An exception is thrown for an invalid data type. This indicates a // missing data type in the code.. // //
      • AipsError // static void addKeyToDesc(TableDesc&, KeyEnum key); static void addKeyToDesc(MSTableMaps&, KeyEnum key); // // tableDesc convenience functions // // check that a TableDesc is valid static Bool validate(const TableDesc& tabDesc); // check that the keyword set is valid static Bool validate(const TableRecord& tabKeySet); // validate self (make sure that this MS is valid) Bool validate() const { return this->isNull() ? False : validate(this->tableDesc());} // return the required table description static const TableDesc& requiredTableDesc(); // Add the compress option for the given column to the TableDesc. // It can only be used for a Float or a Complex column. // For complex columns the type determines which CompressComplex // engine is used. "SD" means that CompressComplexSD is used; otherwise // CompressComplex is used. static void addColumnCompression (TableDesc& td, ColEnum which, Bool autoScale = True, const String& type = String()); // protected: // These constructors mirror the Table ones // // Default constructor for use by derived classes MSTable (); MSTable (const String &tableName, TableOption option); MSTable (const String &tableName, const TableLock& lockOptions, TableOption option); MSTable (const String &tableName, const String &tableDescName, TableOption option); MSTable (const String &tableName, const String &tableDescName, const TableLock& lockOptions, TableOption option); MSTable (SetupNewTable &newTab, rownr_t nrrow, Bool initialize); MSTable (SetupNewTable &newTab, const TableLock& lockOptions, rownr_t nrrow, Bool initialize); #ifdef HAVE_MPI MSTable (MPI_Comm comm, SetupNewTable &newTab, rownr_t nrrow, Bool initialize); MSTable (MPI_Comm comm, SetupNewTable &newTab, const TableLock& lockOptions, rownr_t nrrow, Bool initialize); #endif // HAVE_MPI MSTable (const Table &table); MSTable (const MSTable &other); // ~MSTable(); // Assignment operator, reference semantics MSTable& operator=(const MSTable&); // Get the static struct containing all column and keyword mappings. static MSTableMaps& getMaps(); // Define an entry in the column maps static void colMapDef(MSTableMaps& maps, ColEnum col, const String& colName, DataType colType, const String& colComment, const String& colUnit="", const String& colMeasureType=""); // Define an entry in the keyword maps static void keyMapDef(MSTableMaps& maps, KeyEnum key, const String& keyName, DataType keyType, const String& keyComment); // Return a table that references all columns in this table except for // those given in writableColumns, those are empty and writable. Table referenceCopy(const String& newTableName, const Block& writableColumns) const; // Convert a ColEnum to the actual column name. inline static const String& columnName(const MSTableMaps& maps, ColEnum which) { return maps.columnMap_p.at(which); } inline static DataType columnDataType(const MSTableMaps& maps, ColEnum which) { return DataType(maps.colDTypeMap_p.at(which)); } inline static const String& columnStandardComment(const MSTableMaps& maps, ColEnum which) { return maps.colCommentMap_p.at(which); } inline static const String& columnUnit(const MSTableMaps& maps, ColEnum which) { return maps.colUnitMap_p.at(which); } inline static const String& columnMeasureType(const MSTableMaps& maps, ColEnum which) { return maps.colMeasureTypeMap_p.at(which); } inline static const String& keywordName(const MSTableMaps& maps, KeyEnum which) { return maps.keywordMap_p.at(which); } inline static DataType keywordDataType(const MSTableMaps& maps, KeyEnum which) { return DataType(maps.keyDTypeMap_p.at(which)); } inline static const String& keywordStandardComment(const MSTableMaps& maps, KeyEnum which) { return maps.keyCommentMap_p.at(which); } }; } //# NAMESPACE CASACORE - END //# Do not instantiate the templates automatically, because it means that the static //# in function getMaps() might be instantiated in many compilation units. //# The templates are instantiated in MSTable.cc. //#ifndef CASACORE_NO_AUTO_TEMPLATES //#include //#endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/ms/MeasurementSets/MSTable.tcc000066400000000000000000000265241476623553700210620ustar00rootroot00000000000000//# MSTable.cc: the class that hold measurements from telescopes //# Copyright (C) 1996,1997,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSTABLE_TCC #define MS_MSTABLE_TCC #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template MSTable::MSTable() {} template MSTable::MSTable(const String &tableName, Table::TableOption option) : Table(tableName, option) {} template MSTable::MSTable(const String &tableName, const TableLock& lockOptions, Table::TableOption option) : Table(tableName, lockOptions, option) {} template MSTable::MSTable(const String& tableName, const String &tableDescName, Table::TableOption option) : Table(tableName, tableDescName, option) {} template MSTable::MSTable(const String& tableName, const String &tableDescName, const TableLock& lockOptions, Table::TableOption option) : Table(tableName, tableDescName, lockOptions, option) {} template MSTable::MSTable(SetupNewTable &newTab, rownr_t nrrow, Bool initialize) : Table(MSTableImpl::setupCompression(newTab), nrrow, initialize) {} template MSTable::MSTable(SetupNewTable &newTab, const TableLock& lockOptions, rownr_t nrrow, Bool initialize) : Table(MSTableImpl::setupCompression(newTab), lockOptions, nrrow, initialize) {} #ifdef HAVE_MPI template MSTable::MSTable(MPI_Comm comm, SetupNewTable &newTab, rownr_t nrrow, Bool initialize) : Table(comm, MSTableImpl::setupCompression(newTab), nrrow, initialize) {} template MSTable::MSTable(MPI_Comm comm, SetupNewTable &newTab, const TableLock& lockOptions, rownr_t nrrow, Bool initialize) : Table(comm, MSTableImpl::setupCompression(newTab), lockOptions, nrrow, initialize) {} #endif // HAVE_MPI template MSTable::MSTable(const Table &table) : Table(table) {} template MSTable::MSTable(const MSTable &other) : Table(other) {} template MSTable::~MSTable() {} template MSTable& MSTable:: operator=(const MSTable& other) { if (this != &other) Table::operator=(other); return *this; } template MSTableMaps& MSTable::getMaps() { // Create the static and fill it. // Note that C++11 guarantees that it is called once and is thread-safe. static MSTableMaps maps(MSTableImpl::initMaps(static_cast(0))); return maps; } template const String& MSTable::columnName(MSTable::ColEnum which) { return getMaps().columnMap_p.at(which); } template typename MSTable::ColEnum MSTable::columnType(const String &name) { return ColEnum(getMaps().columnType(name)); } template DataType MSTable::columnDataType(MSTable::ColEnum which) { return DataType(getMaps().colDTypeMap_p.at(which)); } template const String& MSTable::columnStandardComment(MSTable::ColEnum which) { return getMaps().colCommentMap_p.at(which); } template const String& MSTable::columnUnit(MSTable::ColEnum which) { return getMaps().colUnitMap_p.at(which); } template const String& MSTable::columnMeasureType(MSTable::ColEnum which) { return getMaps().colMeasureTypeMap_p.at(which); } template const String& MSTable::keywordName(MSTable::KeyEnum which) { return getMaps().keywordMap_p.at(which); } template typename MSTable::KeyEnum MSTable::keywordType(const String &name) { return KeyEnum(getMaps().keywordType(name)); } template DataType MSTable::keywordDataType(MSTable::KeyEnum which) { return DataType(getMaps().keyDTypeMap_p.at(which)); } template const String& MSTable::keywordStandardComment(MSTable::KeyEnum which) { return getMaps().keyCommentMap_p.at(which); } template Bool MSTable::isColumn(MSTable::ColEnum which) const { return tableDesc().isColumn(columnName(which)); } template Bool MSTable::isColumnWritable(MSTable::ColEnum which) const { return Table::isColumnWritable(columnName(which)); } template Bool MSTable::isKeyword(MSTable::KeyEnum which) const { return keywordSet().isDefined(keywordName(which)); } template Bool MSTable::isScalar(MSTable::ColEnum which) const { return tableDesc().columnDesc(columnName(which)).isScalar(); } template Bool MSTable::isArray(MSTable::ColEnum which) const { return tableDesc().columnDesc(columnName(which)).isArray(); } template const String& MSTable::unit(const String& which) const { return tableDesc().columnDesc(which).keywordSet(). asArrayString("QuantumUnits")(IPosition(1,0)); } template void MSTable::addColumnToDesc(TableDesc &td, MSTable::ColEnum which, Int ndim, const String& refCol) { MSTableImpl::addColumnToDesc(td,columnName(which),columnDataType(which), columnStandardComment(which), columnUnit(which), columnMeasureType(which), ndim,IPosition(),0,refCol); } template void MSTable::addColumnToDesc(TableDesc &td, MSTable::ColEnum which, const IPosition& shape, ColumnDesc::Option option, const String& refCol) { MSTableImpl::addColumnToDesc(td,columnName(which),columnDataType(which), columnStandardComment(which), columnUnit(which), columnMeasureType(which), -1,shape,option,refCol); } template void MSTable::addColumnToDesc(MSTableMaps& maps, MSTable::ColEnum which, Int ndim, const String& refCol) { MSTableImpl::addColumnToDesc(maps.requiredTD_p, columnName(maps, which), columnDataType(maps, which), columnStandardComment(maps, which), columnUnit(maps, which), columnMeasureType(maps, which), ndim, IPosition(), 0, refCol); } template void MSTable::addColumnToDesc(MSTableMaps& maps, MSTable::ColEnum which, const IPosition& shape, ColumnDesc::Option option, const String& refCol) { MSTableImpl::addColumnToDesc(maps.requiredTD_p, columnName(maps, which), columnDataType(maps, which), columnStandardComment(maps, which), columnUnit(maps, which), columnMeasureType(maps, which), -1, shape, option, refCol); } template void MSTable::addKeyToDesc(TableDesc& td, MSTable::KeyEnum key) { MSTableImpl::addKeyToDesc(td,keywordName(key),keywordDataType(key), keywordStandardComment(key)); } template void MSTable::addKeyToDesc(MSTableMaps& maps, MSTable::KeyEnum key) { MSTableImpl::addKeyToDesc(maps.requiredTD_p, keywordName(maps, key), keywordDataType(maps, key), keywordStandardComment(maps, key)); } template void MSTable::addColumnCompression (TableDesc& td, MSTable::ColEnum which, Bool autoScale, const String& type) { MSTableImpl::addColumnCompression (td, columnName(which), autoScale, type); } template void MSTable::colMapDef(MSTableMaps& maps, MSTable::ColEnum col, const String& colName, DataType colType, const String& colComment, const String& colUnit, const String& colMeasureType) { MSTableImpl::colMapDef(maps.columnMap_p, maps.colDTypeMap_p, maps.colCommentMap_p, maps.colUnitMap_p, maps.colMeasureTypeMap_p, col,colName,colType,colComment,colUnit,colMeasureType); } template void MSTable::keyMapDef(MSTableMaps& maps, MSTable::KeyEnum key, const String& keyName, DataType keyType, const String& keyComment) { MSTableImpl::keyMapDef(maps.keywordMap_p, maps.keyDTypeMap_p, maps.keyCommentMap_p, key,keyName,keyType,keyComment); } template Bool MSTable::validate(const TableDesc& tabDesc) { return MSTableImpl::validate(tabDesc, getMaps().requiredTD_p); } template Bool MSTable::validate(const TableRecord& tabKeySet) { return MSTableImpl::validate(tabKeySet, getMaps().requiredTD_p); } template const TableDesc& MSTable::requiredTableDesc() { return getMaps().requiredTD_p; } template Table MSTable::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSTableImpl::referenceCopy(*this, newTableName, writableColumns); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSTable2.cc000066400000000000000000000070661476623553700207600ustar00rootroot00000000000000//# MSTable.cc: the class that hold measurements from telescopes //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #if defined(__GNUG__) && (__GNUG__ == 2) && (__GNUC_MINOR__ < 91) #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN #define MSTableStatics(ColEnum,KeyEnum) \ SimpleOrderedMap MSTable::columnMap_p(""); \ SimpleOrderedMap MSTable::colDTypeMap_p(TpOther); \ SimpleOrderedMap MSTable::colCommentMap_p(""); \ SimpleOrderedMap MSTable::colUnitMap_p(""); \ SimpleOrderedMap \ MSTable::colMeasureTypeMap_p(""); \ SimpleOrderedMap MSTable::keywordMap_p(""); \ SimpleOrderedMap MSTable::keyDTypeMap_p(TpOther); \ SimpleOrderedMap MSTable::keyCommentMap_p(""); \ SimpleCountedConstPtr MSTable::requiredTD_p; MSTableStatics(MS::PredefinedColumns,MS::PredefinedKeywords) MSTableStatics(MSAntenna::PredefinedColumns,MSAntenna::PredefinedKeywords) MSTableStatics(MSDataDescription::PredefinedColumns,MSDataDescription::PredefinedKeywords) MSTableStatics(MSFeed::PredefinedColumns,MSFeed::PredefinedKeywords) MSTableStatics(MSField::PredefinedColumns,MSField::PredefinedKeywords) MSTableStatics(MSFlagCmd::PredefinedColumns,MSFlagCmd::PredefinedKeywords) MSTableStatics(MSFreqOffset::PredefinedColumns,MSFreqOffset::PredefinedKeywords) MSTableStatics(MSHistory::PredefinedColumns,MSHistory::PredefinedKeywords) MSTableStatics(MSObservation::PredefinedColumns,MSObservation::PredefinedKeywords) MSTableStatics(MSPointing::PredefinedColumns,MSPointing::PredefinedKeywords) MSTableStatics(MSPolarization::PredefinedColumns,MSPolarization::PredefinedKeywords) MSTableStatics(MSProcessor::PredefinedColumns,MSProcessor::PredefinedKeywords) MSTableStatics(MSSource::PredefinedColumns,MSSource::PredefinedKeywords) MSTableStatics(MSSpectralWindow::PredefinedColumns,MSSpectralWindow::PredefinedKeywords) MSTableStatics(MSState::PredefinedColumns,MSState::PredefinedKeywords) MSTableStatics(MSSysCal::PredefinedColumns,MSSysCal::PredefinedKeywords) MSTableStatics(MSWeather::PredefinedColumns,MSWeather::PredefinedKeywords) MSTableStatics(MSDoppler::PredefinedColumns,MSDoppler::PredefinedKeywords) } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSTableImpl.cc000066400000000000000000000504501476623553700215130ustar00rootroot00000000000000//# MSTableImpl.cc: the class that hold measurements from telescopes //# Copyright (C) 1995,1996,1997,1999,2000,2001,2002,2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN void MSTableImpl::addMeasColumn(TableDesc& td, const String& column, const String& measure, const String& refCol) { String meas = measure; meas.downcase(); TableMeasRefDesc measRef; TableMeasValueDesc measVal(td, column); if (!refCol.empty()) measRef=TableMeasRefDesc(td,refCol); if (meas == "direction") { if (refCol.empty()) measRef=TableMeasRefDesc(MDirection::DEFAULT); TableMeasDesc measCol(measVal, measRef); measCol.write(td); } else if (meas == "doppler") { if (refCol.empty()) measRef=TableMeasRefDesc(MDoppler::DEFAULT); TableMeasDesc measCol(measVal, measRef); measCol.write(td); } else if (meas == "epoch") { if (refCol.empty()) measRef=TableMeasRefDesc(MEpoch::DEFAULT); TableMeasDesc measCol(measVal, measRef); measCol.write(td); } else if (meas == "frequency") { if (refCol.empty()) measRef=TableMeasRefDesc(MFrequency::DEFAULT); TableMeasDesc measCol(measVal, measRef); measCol.write(td); } else if (meas == "position") { if (refCol.empty()) measRef=TableMeasRefDesc(MPosition::DEFAULT); TableMeasDesc measCol(measVal, measRef); measCol.write(td); } else if (meas == "radialvelocity") { if (refCol.empty()) measRef=TableMeasRefDesc(MRadialVelocity::DEFAULT); TableMeasDesc measCol(measVal, measRef); measCol.write(td); } else if (meas == "baseline") { if (refCol.empty()) measRef=TableMeasRefDesc(MBaseline::DEFAULT); TableMeasDesc measCol(measVal, measRef); measCol.write(td); } else if (meas == "uvw") { if (refCol.empty()) measRef=TableMeasRefDesc(Muvw::DEFAULT); TableMeasDesc measCol(measVal, measRef); measCol.write(td); } else if (meas == "earthmagnetic") { if (refCol.empty()) measRef=TableMeasRefDesc(MEarthMagnetic::DEFAULT); TableMeasDesc measCol(measVal, measRef); measCol.write(td); } } void MSTableImpl::addColumnToDesc(TableDesc &td, const String& colName, Int colDType, const String& colComment, const String& colUnit, const String& colMeasure, Int ndim, const IPosition& shape, Int option, const String& refCol) { // if the column already exists, simply return // NOTE: this does NOT check for the correct type or number of dimensions if (td.isColumn(colName)) return; if ((colDType>=TpBool && colDType<=TpString)|| colDType==TpRecord) { switch (colDType) { case TpBool: td.addColumn(ScalarColumnDesc(colName,colComment)); break; case TpInt: td.addColumn(ScalarColumnDesc(colName,colComment)); break; case TpFloat: td.addColumn(ScalarColumnDesc(colName,colComment)); break; case TpDouble: td.addColumn(ScalarColumnDesc(colName,colComment)); // Check if this should be a TableMeasure column if (colMeasure=="Epoch" || colMeasure=="Frequency" || colMeasure=="Doppler") { // Epoch, Frequency and Doppler are scalar TableMeasures addMeasColumn(td, colName, colMeasure, refCol); } break; case TpComplex: td.addColumn(ScalarColumnDesc(colName,colComment)); break; case TpString: td.addColumn(ScalarColumnDesc(colName,colComment)); break; case TpRecord: // note we use TableRecord iso Record, there is no TpTableRecord td.addColumn(ScalarRecordColumnDesc(colName,colComment)); break; /* these are not needed in the MS case TpChar: td.addColumn(ScalarColumnDesc(colName,colComment)); break; case TpUChar: td.addColumn(ScalarColumnDesc(colName,colComment)); break; case TpShort: td.addColumn(ScalarColumnDesc(colName,colComment)); break; case TpUShort: td.addColumn(ScalarColumnDesc(colName,colComment)); break; case TpUInt: td.addColumn(ScalarColumnDesc(colName,colComment)); break; case TpDComplex: td.addColumn(ScalarColumnDesc(colName,colComment)); break; */ default: break; } } else if (colDType>= TpArrayBool && colDType<= TpArrayString) { if (option==0) { switch (colDType) { case TpArrayBool: td.addColumn(ArrayColumnDesc(colName,colComment,ndim)); break; case TpArrayInt: td.addColumn(ArrayColumnDesc(colName,colComment,ndim)); break; case TpArrayFloat: td.addColumn(ArrayColumnDesc(colName,colComment,ndim)); break; case TpArrayDouble: td.addColumn(ArrayColumnDesc(colName,colComment,ndim)); // Check if this should be a TableMeasure column if (colMeasure!="") { addMeasColumn(td, colName, colMeasure, refCol); } break; case TpArrayComplex: td.addColumn(ArrayColumnDesc(colName,colComment,ndim)); break; case TpArrayString: td.addColumn(ArrayColumnDesc(colName,colComment,ndim)); break; /* case TpArrayChar: td.addColumn(ArrayColumnDesc(colName,colComment,ndim)); break; case TpArrayUChar: td.addColumn(ArrayColumnDesc(colName,colComment,ndim)); break; case TpArrayShort: td.addColumn(ArrayColumnDesc(colName,colComment,ndim)); break; case TpArrayUShort: td.addColumn(ArrayColumnDesc(colName,colComment,ndim)); break; case TpArrayUInt: td.addColumn(ArrayColumnDesc(colName,colComment,ndim)); break; case TpArrayDComplex: td.addColumn(ArrayColumnDesc(colName,colComment,ndim)); break; */ default: break; } } else { switch (colDType) { case TpArrayBool: td.addColumn(ArrayColumnDesc(colName,colComment, shape,option)); break; case TpArrayInt: td.addColumn(ArrayColumnDesc(colName,colComment, shape,option)); break; case TpArrayFloat: td.addColumn(ArrayColumnDesc(colName,colComment, shape,option)); break; case TpArrayDouble: td.addColumn(ArrayColumnDesc(colName,colComment, shape,option)); // Check if this should be a TableMeasure column if (colMeasure!="") { addMeasColumn(td, colName, colMeasure, refCol); } break; case TpArrayComplex: td.addColumn(ArrayColumnDesc(colName,colComment, shape,option)); break; case TpArrayString: td.addColumn(ArrayColumnDesc(colName,colComment, shape,option)); break; /* case TpArrayChar: td.addColumn(ArrayColumnDesc(colName,colComment, shape,option)); break; case TpArrayUChar: td.addColumn(ArrayColumnDesc(colName,colComment, shape,option)); break; case TpArrayShort: td.addColumn(ArrayColumnDesc(colName,colComment, shape,option)); break; case TpArrayUShort: td.addColumn(ArrayColumnDesc(colName,colComment, shape,option)); break; case TpArrayUInt: td.addColumn(ArrayColumnDesc(colName,colComment, shape,option)); break; case TpArrayDComplex: td.addColumn(ArrayColumnDesc(colName,colComment, shape,option)); break; */ default: break; } } } else { cerr << "MSTableImpl::addColumnToDesc - Invalid data type: " << colDType <<", "< vu(3,Unit(colUnit)); TableQuantumDesc tqd(td,colName,vu); tqd.write(td); } } void MSTableImpl::addKeyToDesc(TableDesc& td, const String& keyName, Int keyDType, const String& keyComment) { switch (keyDType) { case TpInt: td.rwKeywordSet().define(keyName,Int(0)); td.rwKeywordSet().setComment(keyName, keyComment); break; case TpFloat: td.rwKeywordSet().define(keyName, Float(0)); td.rwKeywordSet().setComment(keyName, keyComment); break; case TpString: td.rwKeywordSet().define(keyName, ""); td.rwKeywordSet().setComment(keyName, keyComment); break; case TpTable: //# cannot define tables in TableDesc (only in actual Table) //# td.rwKeywordSet().keysTable()(keywordName(key)) = Table(); //# td.rwKeywordSet().comment(keywordName(key)) = //# keywordStandardComment(key); break; default: cerr << "Data type: "<< keyDType << ", "<< keyName<< "not handled"< (colName + "_COMPRESSED", "", cdesc.dataManagerType(), cdesc.dataManagerGroup(), cdesc.ndim(), cdesc.options())); cdesc.rwKeywordSet().define ("CompressFloat_AutoScale", autoScale); cdesc.rwKeywordSet().define ("CompressFloat_Type", type); } else { td.addColumn (ArrayColumnDesc (colName + "_COMPRESSED", "", cdesc.dataManagerType(), cdesc.dataManagerGroup(), cdesc.ndim(), cdesc.options())); cdesc.rwKeywordSet().define ("CompressComplex_AutoScale", autoScale); cdesc.rwKeywordSet().define ("CompressComplex_Type", type); } if (cdesc.shape().nelements() > 0) { ColumnDesc& cd = td.rwColumnDesc (colName + "_COMPRESSED"); cd.setShape (cdesc.shape()); } td.addColumn (ScalarColumnDesc (colName + "_SCALE")); td.addColumn (ScalarColumnDesc (colName + "_OFFSET")); } SetupNewTable& MSTableImpl::setupCompression (SetupNewTable& newtab) { // Loop through all columns of the description. // If compression is needed (defined by keyword CompressX_AutoScale) // create a compression engine, bind the compressed column to the // data manager of the original column, and bind the column to the engine. const TableDesc& td = newtab.tableDesc(); for (uInt i=0; i old2new; old2new.insert (std::make_pair(cdesc.name(), cname)); newtab.adjustHypercolumns (old2new, True); } } return newtab; } void MSTableImpl::colMapDef(std::map& columnMap, std::map& colDTypeMap, std::map& colCommentMap, std::map& colUnitMap, std::map& colMeasureTypeMap, Int col, const String& colName, Int colType, const String& colComment, const String& colUnit, const String& colMeasureType) { columnMap[col] = colName; colDTypeMap[col] = colType; colCommentMap[col] = colComment; colUnitMap[col] = colUnit; colMeasureTypeMap[col] = colMeasureType; } void MSTableImpl::keyMapDef(std::map& keywordMap, std::map& keyDTypeMap, std::map& keyCommentMap, Int key, const String& keyName, Int keyType, const String& keyComment) { keywordMap[key] = keyName; keyDTypeMap[key] = keyType; keyCommentMap[key] = keyComment; } Bool MSTableImpl::validate(const TableDesc& tabDesc, const TableDesc& requiredTD) { Bool eqDTypes; Bool temp = tabDesc.columnDescSet(). isSuperset(requiredTD.columnDescSet(), eqDTypes); #if defined(AIPS_DEBUG) if (!temp) { cerr << "MSTableImpl::validate - tabDesc not superset of requiredTD"< colNames(requiredTD.columnNames()); uInt ncol = colNames.nelements(); while (temp && eqDTypes && detail && colnr < ncol) { TableRecord keySet = tabDesc[colNames(colnr)].keywordSet(); TableRecord reqKeySet = requiredTD[colNames(colnr)].keywordSet(); // check the units if defined if (reqKeySet.isDefined("QuantumUnits")) { detail = keySet.isDefined("QuantumUnits"); #if defined(AIPS_DEBUG) if (!detail) { cerr <<"MSTableImpl::validate - column "<& writableColumns) { TableDesc td(tab.tableDesc()); SetupNewTable setup(newTableName, td, Table::New); ForwardColumnEngine fwdEngine(tab); StManAipsIO aipsStMan; // first bind all columns to the forwarding engine setup.bindAll(fwdEngine); // now bind columns specified to AipsIO storage manager for (uInt i=0; i #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations class SetupNewTable; // // An implementation class for the MeasurementSet to share code. // // // // //
      • MeasurementSet // // // // The MSTableImpl implements non-templated static functions shared by // all MSTable objects. // // // // MSTableImpl is only for internal use by the MeasurementSet base class // MSTable. // // // // // // // // The reasons for existance for this class are: sharing of code between // the various MeasurementSet Tables and avoiding duplicate code in the // instantiations of MSTable // // // // class MSTableImpl { public: // add a column to a TableDesc // An exception is thrown for an invalid data type. This indicates a // programming error in this class when this occurs. // If option!=0 shape is used to set the shape of the column and // option defines the type of column (ColumnDesc::Fixed/Direct). // If option==0, shape is ignored and ndim is used to specify the // array dimension if any. // If refCol is not empty, a column with variable reference will be // created, note that refCol should already exist in td. // //
      • AipsError // static void addColumnToDesc(TableDesc &td, const String& colName, Int colDType, const String& colComment, const String& colUnit, const String& colMeasure, Int ndim, const IPosition & shape, Int option, const String& refCol); // add a keyword to a TableDesc // An exception is thrown for an invalid data type. This indicates a // missing data type in the code.. // //
      • AipsError // static void addKeyToDesc(TableDesc& td, const String& keyName, Int keyDType, const String& keyComment); // add a MeasureColumn for the specified Measure, with default reference static void addMeasColumn(TableDesc &td, const String& colName, const String& colMeasure, const String& refCol); // Add the compress option for the given column to the TableDesc. static void addColumnCompression (TableDesc&, const String& colName, Bool autoScale, const String& type); // Setup the compression data managers if needed. static SetupNewTable& setupCompression (SetupNewTable&); // Define an entry in the column maps static void colMapDef(std::map& colMap, std::map& colDTypeMap, std::map& colCommentMap, std::map& colUnitMap, std::map& colMeasureTypeMap, Int col, const String& colName, Int colType, const String& colComment, const String& colUnit, const String& colMeasureType); // Define an entry in the keyword maps static void keyMapDef(std::map& keyMap, std::map& keyDTypeMap, std::map& keyCommentMap, Int key, const String& keyName, Int keyType, const String& keyComment); // tableDesc convenience functions // // check that a TableDesc is valid static Bool validate(const TableDesc& tabDesc, const TableDesc& requiredTD); // check that the keyword set is valid static Bool validate(const TableRecord& tabRec, const TableDesc& requiredTD); // // Return a table that references all columns in this table except for // those given in writableColumns, those are empty and writable. static Table referenceCopy(const Table& tab, const String& newTableName, const Block& writableColumns); // Define the initialization function for each MS table type. // static MSTableMaps initMaps(MSMainEnums*); static MSTableMaps initMaps(MSAntennaEnums*); static MSTableMaps initMaps(MSDataDescriptionEnums*); static MSTableMaps initMaps(MSDopplerEnums*); static MSTableMaps initMaps(MSFeedEnums*); static MSTableMaps initMaps(MSFieldEnums*); static MSTableMaps initMaps(MSFlagCmdEnums*); static MSTableMaps initMaps(MSFreqOffsetEnums*); static MSTableMaps initMaps(MSHistoryEnums*); static MSTableMaps initMaps(MSObservationEnums*); static MSTableMaps initMaps(MSPointingEnums*); static MSTableMaps initMaps(MSPolarizationEnums*); static MSTableMaps initMaps(MSProcessorEnums*); static MSTableMaps initMaps(MSSourceEnums*); static MSTableMaps initMaps(MSSpectralWindowEnums*); static MSTableMaps initMaps(MSStateEnums*); static MSTableMaps initMaps(MSSysCalEnums*); static MSTableMaps initMaps(MSWeatherEnums*); // }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSTileLayout.cc000066400000000000000000000070111476623553700217300ustar00rootroot00000000000000//# MSTileLayout.cc: Implementation of MSTileLayout //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN IPosition MSTileLayout::tileShape(const IPosition& dataShape, Int observationType, Int nIfr, Int nInt) { //Try to bypass most stupid choices if(nIfr<1) nIfr=100; if(nInt<1) nInt=1; const Int ioBlockSize = 131072; // 131072 * sizeOf(Complex) = 1 MB IPosition tileShape(3,0,0,0); if (dataShape.nelements()==2 && dataShape(0)>0 && dataShape(1)>0) { // Always read all polarizations, since we'll often want to do conversion Int corrSize = dataShape(0); // Read all channels, in order to minimize the overhead of the // i/o layer //Read all the channels for fast mosaic Int chanSize = dataShape(1); Int rowSize=max(1, ioBlockSize/corrSize/chanSize); if(observationType==0){ if(chanSize <100){ chanSize=max(1, ioBlockSize/corrSize/nIfr); } else if(chanSize < 10000) { chanSize=Int(floor(sqrt(Float(chanSize)/99.9)))*10; } else{ chanSize=100; } Int elIOBlkSize=ioBlockSize; while(((ioBlockSize/corrSize/chanSize) > 10*nIfr*nInt) && chanSize < dataShape(1)){ chanSize+=2; } if((ioBlockSize/corrSize/chanSize) > 10*nIfr*nInt){ //we are still having too many blocks in one tile elIOBlkSize=10*nIfr*nInt*corrSize*chanSize; } chanSize=(chanSize >= dataShape(1)) ? dataShape(1) : chanSize; rowSize= max(1,elIOBlkSize/corrSize/chanSize); } else{ //fast mosaic mode: no need to make people load several pointings rowSize=nIfr*nInt; chanSize=max(1, ioBlockSize/corrSize/rowSize); chanSize=(chanSize > dataShape(1)) ? dataShape(1) : chanSize; } tileShape(0)=corrSize; tileShape(1)=chanSize; tileShape(2)=rowSize; } return tileShape; } IPosition MSTileLayout::tileShape(const IPosition& dataShape, Int observationType, const String& array) { Int nIfr=200; if (array=="ATCA") nIfr=15; if (array=="VLA") nIfr=351; if (array=="WSRT") nIfr=91; if (array=="BIMA") nIfr=36; if (array=="DRAO") nIfr=21; if (array=="SMA") nIfr=28; return tileShape(dataShape,observationType,nIfr); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSTileLayout.h000066400000000000000000000111721476623553700215750ustar00rootroot00000000000000//# MSTileLayout.h: Determine appropriate tiling for a MeasurementSet //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSTILELAYOUT_H #define MS_MSTILELAYOUT_H #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# forward decl class IPosition; class String; // // An helper class for deciding on tile shapes in MeasurementSets // // // //
      • MeasurementSet //
      • TiledStMan // // // // MSTileLayout stands for the MeasurementSet tile layout chooser // // // // MSTileLayout is a class to determine an appropriate tile shape choice // for the MeasurementSet DATA columns, based on the shape of the DATA, // the observing mode and the number of interferometers // // // // // // The following code returns the tile shape given the DATA shape, // // the observing type and the array name. // IPosition dataShape(2,4,1024); // 4 polarizations, 1024 channels // IPosition tileShape = MSTileLayout::tileShape(dataShape, // MSTileLayout::FastMosaic, // "ATCA"); // cout << "tileShape = "<< tileShape << endl; // // Output is: // tileShape = (4,11,15) // // // // This class is intended to replace bits of code scattered throughout various // fillers and collect all tiling decisions in one place. // // // // class MSTileLayout { public: enum ObsType { // Standard, optimizes i/o by using large tiles (128 kB) Standard=0, // Fast Mosaic, specify this if you have many (>30) fields and you // spend only a few integrations per pointing before moving on. // Avoids useless i/o caching by using small tiles FastMosaic=1 }; // Suggest tile shape based on the data shape, the observing mode, // the number of interferometers and the number of integrations per // pointing. // // First argument should be a 2-dimensional IPosition with the data matrix // shape. // The second argument is one of the enums above specifying the type of // observation. // The third argument is an estimate of the number of interferometers // present throughout most of the data. // The last argument is an estimate of the number of integrations per // pointing. // // The last three arguments only need to be specified if the observing mode // is non Standard. // Basically the choice is between large tiles for efficient I/O and small // tiles when a common access pattern (field_id order) would result // in very inefficient caching. The latter occurs for large tiles if the // field_id changes rapidly AND there are many fields AND the data // matrix is small (e.g., continuum mosaic of a large area with only // one or two integrations per pointing). Note that accessing fast mosaic // data with large tiles in field_id order can be 10-100 times slower than // sequential access. static IPosition tileShape(const IPosition& dataShape, Int observationType = Standard, Int nIfr = 0, Int nInt = 1); // same as above, but pick standard nIfr (number of interferometers) // for named array and default nInt. static IPosition tileShape(const IPosition& dataShape, Int observationType, const String& array); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSWeather.cc000066400000000000000000000163721476623553700212460ustar00rootroot00000000000000//# MSWeather.cc: The MeasurementSet WEATHER Table //# Copyright (C) 1996,1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSWeather::MSWeather():hasBeenDestroyed_p(True) { } MSWeather::MSWeather(const String &tableName, TableOption option) : MSTable(tableName, option),hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSWeather(String &, TableOption) - " "table is not a valid MSWeather")); } MSWeather::MSWeather(const String& tableName, const String &tableDescName, TableOption option) : MSTable(tableName, tableDescName,option), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSWeather(String &, String &, TableOption) - " "table is not a valid MSWeather")); } MSWeather::MSWeather(SetupNewTable &newTab, rownr_t nrrow, Bool initialize) : MSTable(newTab, nrrow, initialize), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSWeather(SetupNewTable &, rownr_t, Bool) - " "table is not a valid MSWeather")); } MSWeather::MSWeather(const Table &table) : MSTable(table), hasBeenDestroyed_p(False) { // verify that the now opened table is valid if (! validate(this->tableDesc())) throw (AipsError("MSWeather(const Table &) - " "table is not a valid MSWeather")); } MSWeather::MSWeather(const MSWeather &other) : MSTable(other), hasBeenDestroyed_p(False) { // verify that other is valid if (&other != this) if (! validate(this->tableDesc())) throw (AipsError("MSWeather(const MSWeather &) - " "table is not a valid MSWeather")); } MSWeather::~MSWeather() { // check to make sure that this MSWeather is still valid if (!hasBeenDestroyed_p && !validate()) { // the table is otherwise OK, so ensure that it is written if necessary this->flush(); LogIO os; os << LogIO::WARN << "~MSWeather() - Table written is not a valid MSWeather" << LogIO::POST; } hasBeenDestroyed_p = True; } MSWeather& MSWeather::operator=(const MSWeather &other) { if (&other != this) { MSTable::operator=(other); hasBeenDestroyed_p=other.hasBeenDestroyed_p; } return *this; } MSTableMaps MSWeather::initMaps() { MSTableMaps maps; // the PredefinedColumns // ANTENNA_ID colMapDef(maps, ANTENNA_ID, "ANTENNA_ID", TpInt, "Antenna number","",""); // INTERVAL colMapDef(maps, INTERVAL, "INTERVAL", TpDouble, "Interval over which data is relevant","s",""); // TIME colMapDef(maps, TIME, "TIME", TpDouble, "An MEpoch specifying the midpoint of the time for" "which data is relevant","s","Epoch"); // DEW_POINT colMapDef(maps, DEW_POINT, "DEW_POINT", TpFloat, "Dew point","K",""); // DEW_POINT_FLAG colMapDef(maps, DEW_POINT_FLAG, "DEW_POINT_FLAG", TpBool, "Flag for dew point","",""); // H2O colMapDef(maps, H2O, "H2O", TpFloat, "Average column density of water-vapor","m-2",""); // H2O_FLAG colMapDef(maps, H2O_FLAG, "H2O_FLAG", TpBool, "Flag for average column density of water-vapor","",""); // IONOS_ELECTRON colMapDef(maps, IONOS_ELECTRON, "IONOS_ELECTRON", TpFloat, "Average column density of electrons","m-2",""); // IONOS_ELECTRON_FLAG colMapDef(maps, IONOS_ELECTRON_FLAG, "IONOS_ELECTRON_FLAG", TpBool, "Flag for average column density of electrons","",""); // PRESSURE colMapDef(maps, PRESSURE, "PRESSURE", TpFloat, "Ambient atmospheric pressure","hPa",""); // PRESSURE_FLAG colMapDef(maps, PRESSURE_FLAG, "PRESSURE_FLAG", TpBool, "Flag for ambient atmospheric pressure","",""); // REL_HUMIDITY colMapDef(maps, REL_HUMIDITY, "REL_HUMIDITY", TpFloat, "Ambient relative humidity","%",""); // REL_HUMIDITY_FLAG colMapDef(maps, REL_HUMIDITY_FLAG, "REL_HUMIDITY_FLAG", TpBool, "Flag for ambient relative humidity","",""); // TEMPERATURE colMapDef(maps, TEMPERATURE, "TEMPERATURE", TpFloat, "Ambient Air Temperature for an antenna","K",""); // TEMPERATURE_FLAG colMapDef(maps, TEMPERATURE_FLAG, "TEMPERATURE_FLAG", TpBool, "Flag for ambient Air Temperature for an antenna","",""); // WIND_DIRECTION colMapDef(maps, WIND_DIRECTION, "WIND_DIRECTION", TpFloat, "Average wind direction","rad",""); // WIND_DIRECTION_FLAG colMapDef(maps, WIND_DIRECTION_FLAG, "WIND_DIRECTION_FLAG", TpBool, "Flag for wind direction","",""); // WIND_SPEED colMapDef(maps, WIND_SPEED, "WIND_SPEED", TpFloat, "Average wind speed","m/s",""); // WIND_SPEED_FLAG colMapDef(maps, WIND_SPEED_FLAG, "WIND_SPEED_FLAG", TpBool, "Flag for wind speed","",""); // PredefinedKeywords // init requiredTableDesc // all required keywords uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_PREDEFINED_KEYWORDS; i++) { addKeyToDesc(maps, PredefinedKeywords(i)); } // all required columns for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(maps, PredefinedColumns(i)); } return maps; } MSWeather MSWeather::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MSWeather(MSTable:: referenceCopy(newTableName,writableColumns)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSWeather.h000066400000000000000000000114411476623553700211000ustar00rootroot00000000000000//# MSWeather.h: The MeasurementSet WEATHER Table //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSWEATHER_H #define MS_MSWEATHER_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A Table intended to hold a MeasurementSet WEATHER table. // // // // //
      • MeasurementSet //
      • MSTable // // // // MSWeather stands for the MeasurementSet Weather table. // // // // An MSWeather is a table intended to hold the WEATHER table for // the MeasurementSet. It has an identical set of member functions as // the main MeasurementSet class, except (currently) for the default // calibration members. For further info and examples see the // MeasurementSet class. // // // // See the MeasurementSet for an example of how to access and use this class. // // // // It was found that subtables and the main table of the MeasurementSet have // a lot in common, therefore they derive their interface from the same // base class. Each subtable has its own class to keep the enum definitions // and conversion functions in separate scopes. // // // // see MeasurementSet. // class MSWeather:public MSWeatherEnums, public MSTable { public: // This constructs an empty MSWeather. MSWeather (); // These constructors mirror the Table ones with additional checking // on validity (verifying that the MSWeather will have the required columns // and keywords) // An exception is thrown if the constructed Table is not a valid MSWeather // //
      • AipsError // // MSWeather (const String &tableName, TableOption = Table::Old); MSWeather (const String &tableName, const String &tableDescName, TableOption = Table::Old); MSWeather (SetupNewTable &newTab, rownr_t nrrow = 0, Bool initialize = False); MSWeather (const Table &table); MSWeather (const MSWeather &other); // // As with tables, the destructor writes the table if necessary. // Additional checking is done here to verify that all required // columns are still present. // If it is NOT valid, it will write the table and then throw an exception. // //
      • AipsError // ~MSWeather(); // Assignment operator, reference semantics MSWeather& operator=(const MSWeather&); // Make a special copy of this Table which references all columns from // this Table except those mentioned; those are empty and writable. // Each forwarded column has the same writable status as the underlying // column. The mentioned columns all use the AipsIO storage manager. // This function is inherited from MSTable and unlikely to be of use, // except in the class MeasurementSet (see comment there).. MSWeather referenceCopy(const String& newTableName, const Block& writableColumns) const; // Initialize the statics appropriately. This does not need to be // called by users, it is called by the implementation class // MSTableImpl. static MSTableMaps initMaps(); private: // required by the need to throw an exception in the destructor Bool hasBeenDestroyed_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSWeatherColumns.cc000066400000000000000000000134211476623553700225770ustar00rootroot00000000000000//# MSWeatherColumns.cc: provides easy access to MeasurementSet columns //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSWeatherColumns::MSWeatherColumns(): isNull_p(True) { } MSWeatherColumns::MSWeatherColumns(const MSWeather& msWeather): isNull_p(True) { attach(msWeather); } MSWeatherColumns::~MSWeatherColumns() {} void MSWeatherColumns::attach(const MSWeather& msWeather) { isNull_p = msWeather.isNull(); if (!isNull()) { antennaId_p.attach(msWeather, MSWeather:: columnName(MSWeather::ANTENNA_ID)); interval_p.attach(msWeather, MSWeather:: columnName(MSWeather::INTERVAL)); time_p.attach(msWeather, MSWeather::columnName(MSWeather::TIME)); timeMeas_p.attach(msWeather, MSWeather::columnName(MSWeather::TIME)); intervalQuant_p.attach(msWeather, MSWeather:: columnName(MSWeather::INTERVAL)); timeQuant_p.attach(msWeather, MSWeather:: columnName(MSWeather::TIME)); const ColumnDescSet& cds = msWeather.tableDesc().columnDescSet(); const String& dewPoint = MSWeather::columnName(MSWeather::DEW_POINT); if (cds.isDefined(dewPoint)) { dewPoint_p.attach(msWeather, dewPoint); dewPointQuant_p.attach(msWeather, dewPoint); } const String& dewPointFlag = MSWeather::columnName(MSWeather::DEW_POINT_FLAG); if (cds.isDefined(dewPointFlag)) { dewPointFlag_p.attach(msWeather, dewPointFlag); } const String& H2O = MSWeather::columnName(MSWeather::H2O); if (cds.isDefined(H2O)) { H2O_p.attach(msWeather, H2O); H2OQuant_p.attach(msWeather, H2O); } const String& H2OFlag = MSWeather::columnName(MSWeather::H2O_FLAG); if (cds.isDefined(H2OFlag)) H2OFlag_p.attach(msWeather, H2OFlag); const String& ionosElectron = MSWeather::columnName(MSWeather::IONOS_ELECTRON); if (cds.isDefined(ionosElectron)) { ionosElectron_p.attach(msWeather, ionosElectron); ionosElectronQuant_p.attach(msWeather, ionosElectron); } const String& ionosElectronFlag = MSWeather::columnName(MSWeather::IONOS_ELECTRON_FLAG); if (cds.isDefined(ionosElectronFlag)) { ionosElectronFlag_p.attach(msWeather, ionosElectronFlag); } const String& pressure = MSWeather::columnName(MSWeather::PRESSURE); if (cds.isDefined(pressure)) { pressure_p.attach(msWeather, pressure); pressureQuant_p.attach(msWeather, pressure); } const String& pressureFlag = MSWeather::columnName(MSWeather::PRESSURE_FLAG); if (cds.isDefined(pressureFlag)) { pressureFlag_p.attach(msWeather, pressureFlag); } const String& relHumidity = MSWeather::columnName(MSWeather::REL_HUMIDITY); if (cds.isDefined(relHumidity)) { relHumidity_p.attach(msWeather, relHumidity); } const String& relHumidityFlag = MSWeather::columnName(MSWeather::REL_HUMIDITY_FLAG); if (cds.isDefined(relHumidityFlag)) { relHumidityFlag_p.attach(msWeather, relHumidityFlag); } const String& temperature = MSWeather::columnName(MSWeather::TEMPERATURE); if (cds.isDefined(temperature)) { temperature_p.attach(msWeather, temperature); temperatureQuant_p.attach(msWeather, temperature); } const String& temperatureFlag = MSWeather::columnName(MSWeather::TEMPERATURE_FLAG); if (cds.isDefined(temperatureFlag)) { temperatureFlag_p.attach(msWeather, temperatureFlag); } const String& windDirection = MSWeather::columnName(MSWeather::WIND_DIRECTION); if (cds.isDefined(windDirection)) { windDirection_p.attach(msWeather, windDirection); windDirectionQuant_p.attach(msWeather, windDirection); } const String& windDirectionFlag = MSWeather::columnName(MSWeather::WIND_DIRECTION_FLAG); if (cds.isDefined(windDirectionFlag)) { windDirectionFlag_p.attach(msWeather, windDirectionFlag); } const String& windSpeed = MSWeather::columnName(MSWeather::WIND_SPEED); if (cds.isDefined(windSpeed)) { windSpeed_p.attach(msWeather, windSpeed); windSpeedQuant_p.attach(msWeather, windSpeed); } const String& windSpeedFlag = MSWeather::columnName(MSWeather::WIND_SPEED_FLAG); if (cds.isDefined(windSpeedFlag)) { windSpeedFlag_p.attach(msWeather, windSpeedFlag); } } } void MSWeatherColumns:: setEpochRef(MEpoch::Types ref, Bool tableMustBeEmpty) { timeMeas_p.setDescRefCode(ref, tableMustBeEmpty); } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MSWeatherColumns.h000066400000000000000000000236431476623553700224500ustar00rootroot00000000000000//# MSWeatherColumns.h: provides easy access to MSWeather columns //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSWEATHERCOLUMNS_H #define MS_MSWEATHERCOLUMNS_H #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class MSWeather; // // A class to provide easy access to MSWeather columns // // // // // //
      • MSWeather //
      • ArrayColumn //
      • ScalarColumn // // // // MSWeatherColumns stands for MeasurementSet Weather Table columns. // // // // This class provides access to the columns in the MSWeather Table, // it does the declaration of all the Scalar and ArrayColumns with the // correct types, so the application programmer doesn't have to // worry about getting those right. There is an access function // for every predefined column. Access to non-predefined columns will still // have to be done with explicit declarations. // See MSColumns for an example. // // // // See MSColumns for the motivation. // class MSWeatherColumns { public: // Create a columns object that accesses the data in the specified Table MSWeatherColumns(const MSWeather& msWeather); // The destructor does nothing special ~MSWeatherColumns(); // Is this object defined? (MSWeather table is optional) Bool isNull() const {return isNull_p;} // Access to required columns // ScalarColumn& antennaId() {return antennaId_p;} ScalarColumn& interval() {return interval_p;} ScalarQuantColumn& intervalQuant() {return intervalQuant_p;} ScalarColumn& time() {return time_p;} ScalarQuantColumn& timeQuant() {return timeQuant_p;} ScalarMeasColumn& timeMeas() {return timeMeas_p;} // // Access to optional columns // ScalarColumn& dewPoint() {return dewPoint_p;} ScalarQuantColumn& dewPointQuant() {return dewPointQuant_p;} ScalarColumn& dewPointFlag() {return dewPointFlag_p;} ScalarColumn& H2O() {return H2O_p;} ScalarQuantColumn& H2OQuant() {return H2OQuant_p;} ScalarColumn& H2OFlag() {return H2OFlag_p;} ScalarColumn& ionosElectron() {return ionosElectron_p;} ScalarQuantColumn& ionosElectronQuant() {return ionosElectronQuant_p;} ScalarColumn& ionosElectronFlag() {return ionosElectronFlag_p;} ScalarColumn& pressure() {return pressure_p;} ScalarQuantColumn& pressureQuant() {return pressureQuant_p;} ScalarColumn& pressureFlag() {return pressureFlag_p;} ScalarColumn& relHumidity() {return relHumidity_p;} ScalarColumn& relHumidityFlag() {return relHumidityFlag_p;} ScalarColumn& temperature() {return temperature_p;} ScalarQuantColumn& temperatureQuant() {return temperatureQuant_p;} ScalarColumn& temperatureFlag() {return temperatureFlag_p;} ScalarColumn& windDirection() {return windDirection_p;} ScalarQuantColumn& windDirectionQuant() {return windDirectionQuant_p;} ScalarColumn& windDirectionFlag() {return windDirectionFlag_p;} ScalarColumn& windSpeed() {return windSpeed_p;} ScalarQuantColumn& windSpeedQuant() {return windSpeedQuant_p;} ScalarColumn& windSpeedFlag() {return windSpeedFlag_p;} // // Const access to columns // const ScalarColumn& antennaId() const {return antennaId_p;} const ScalarColumn& interval() const {return interval_p;} const ScalarQuantColumn& intervalQuant() const { return intervalQuant_p;} const ScalarColumn& time() const {return time_p;} const ScalarQuantColumn& timeQuant() const {return timeQuant_p;} const ScalarMeasColumn& timeMeas() const {return timeMeas_p;} // // Access to optional columns // const ScalarColumn& dewPoint() const {return dewPoint_p;} const ScalarQuantColumn& dewPointQuant() const { return dewPointQuant_p;} const ScalarColumn& dewPointFlag() const {return dewPointFlag_p;} const ScalarColumn& H2O() const {return H2O_p;} const ScalarColumn& H2OFlag() const {return H2OFlag_p;} const ScalarQuantColumn& H2OQuant() const {return H2OQuant_p;} const ScalarColumn& ionosElectron() const {return ionosElectron_p;} const ScalarQuantColumn& ionosElectronQuant() const { return ionosElectronQuant_p;} const ScalarColumn& ionosElectronFlag() const { return ionosElectronFlag_p;} const ScalarColumn& pressure() const {return pressure_p;} const ScalarQuantColumn& pressureQuant() const { return pressureQuant_p;} const ScalarColumn& pressureFlag() const {return pressureFlag_p;} const ScalarColumn& relHumidity() const {return relHumidity_p;} const ScalarColumn& relHumidityFlag() const { return relHumidityFlag_p;} const ScalarColumn& temperature() const {return temperature_p;} const ScalarQuantColumn& temperatureQuant() const { return temperatureQuant_p;} const ScalarColumn& temperatureFlag() const { return temperatureFlag_p;} const ScalarColumn& windDirection() const {return windDirection_p;} const ScalarQuantColumn& windDirectionQuant() const { return windDirectionQuant_p;} const ScalarColumn& windDirectionFlag() const { return windDirectionFlag_p;} const ScalarColumn& windSpeed() const {return windSpeed_p;} const ScalarQuantColumn& windSpeedQuant() const { return windSpeedQuant_p;} const ScalarColumn& windSpeedFlag() const {return windSpeedFlag_p;} // // Convenience function that returns the number of rows in any of the // columns. Returns zero if the object is null. rownr_t nrow() const {return isNull() ? 0 : antennaId_p.nrow();} // set the epoch type for the TIME column. // // In principle this function can only be used if the table is empty, // otherwise already written values may thereafter have an incorrect // reference, offset, or unit. However, it is possible that part of the // table gets written before these values are known. In that case the // reference, offset, or units can be set by using a False // tableMustBeEmpty argument. // void setEpochRef(MEpoch::Types ref, Bool tableMustBeEmpty=True); protected: //# default constructor creates a object that is not usable. Use the attach //# function correct this. MSWeatherColumns(); //# attach this object to the supplied table. void attach(const MSWeather& msWeather); private: //# Make the assignment operator and the copy constructor private to prevent //# any compiler generated one from being used. MSWeatherColumns(const MSWeatherColumns&); MSWeatherColumns& operator=(const MSWeatherColumns&); //# Check if any optional columns exist and if so attach them. void attachOptionalCols(const MSWeather& msWeather); //# Is the object not attached to a Table. Bool isNull_p; //# required columns ScalarColumn antennaId_p; ScalarColumn interval_p; ScalarColumn time_p; //# optional columns ScalarColumn dewPoint_p; ScalarColumn dewPointFlag_p; ScalarColumn H2O_p; ScalarColumn H2OFlag_p; ScalarColumn ionosElectron_p; ScalarColumn ionosElectronFlag_p; ScalarColumn pressure_p; ScalarColumn pressureFlag_p; ScalarColumn relHumidity_p; ScalarColumn relHumidityFlag_p; ScalarColumn temperature_p; ScalarColumn temperatureFlag_p; ScalarColumn windDirection_p; ScalarColumn windDirectionFlag_p; ScalarColumn windSpeed_p; ScalarColumn windSpeedFlag_p; //# Access to Measure columns ScalarMeasColumn timeMeas_p; //# Access to Quantum columns ScalarQuantColumn intervalQuant_p; ScalarQuantColumn timeQuant_p; //# optional Quantum columns ScalarQuantColumn dewPointQuant_p; ScalarQuantColumn H2OQuant_p; ScalarQuantColumn ionosElectronQuant_p; ScalarQuantColumn pressureQuant_p; ScalarQuantColumn temperatureQuant_p; ScalarQuantColumn windDirectionQuant_p; ScalarQuantColumn windSpeedQuant_p; }; //# Define the RO version for backward compatibility. typedef MSWeatherColumns ROMSWeatherColumns; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MSWeatherEnums.h000066400000000000000000000103171476623553700221110ustar00rootroot00000000000000//# MSWeatherEnums.h: Definitions for the MeasurementSet WEATHER table //# Copyright (C) 1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSWEATHERENUMS_H #define MS_MSWEATHERENUMS_H #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Enums for the MeasurementSet WEATHER table // // // // This class contains the enums for the MeasurementSet WEATHER table // // // This class does nothing. It is merely a container for the enumerations // used by the MSWeather class. These enumerations define the // standard columns, keywords. // // // See the documentation for MeasurementSet for examples on the use of these // enumerations. // // // All the todo items which may be related to this class are // grouped with the todo items for MeasurementSet // // class MSWeatherEnums { public: // The WEATHER table colums with predefined meaning. // Keys: ANTENNA_ID, TIME, INTERVAL. enum PredefinedColumns { // "True" columns are defined.
        // TYPE - UNIT - MEASURE UNDEFINED_COLUMN=0, // Antenna number
        // Int ANTENNA_ID, // Interval over which data is relevant
        // Double - s INTERVAL, // An MEpoch specifying the midpoint of the time for which data is // relevant
        // Double - s - EPOCH TIME, // The number of required columns
        NUMBER_REQUIRED_COLUMNS=TIME, // Dew point
        // Float - K DEW_POINT, // Flag for dew point
        // Bool DEW_POINT_FLAG, // Average column density of water-vapor
        // Float - m H2O, // Flag for H2O
        // Bool H2O_FLAG, // Average column density of electrons
        // Float - m^-2 IONOS_ELECTRON, // Flag for IONOS_ELECTRON
        // Bool IONOS_ELECTRON_FLAG, // Ambient atmospheric pressure
        // Float - Pa PRESSURE, // Flag for pressure
        // Bool PRESSURE_FLAG, // Ambient relative humidity
        // Float - \% REL_HUMIDITY, // Flag for rel humidity
        // Bool REL_HUMIDITY_FLAG, // Ambient Air Temperature for an antenna
        // Float - K TEMPERATURE, // Flag for temperature
        // Bool TEMPERATURE_FLAG, // Average wind direction
        // Float - rad WIND_DIRECTION, // Flag for wind direction
        // Bool WIND_DIRECTION_FLAG, // Average wind speed
        // Float - m/s WIND_SPEED, // Flag for wind speed
        // Bool WIND_SPEED_FLAG, // // Not a column, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_COLUMNS=WIND_SPEED_FLAG }; // Keywords with a predefined meaning enum PredefinedKeywords { // // "True" keywords are defined. UNDEFINED_KEYWORD=0, // // Not a keyword, but just a final enum specifying the number of enums. NUMBER_PREDEFINED_KEYWORDS=0 }; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/MeasurementSet.cc000066400000000000000000001241031476623553700223400ustar00rootroot00000000000000//# MeasurementSet.cc: the class that hold measurements from telescopes //# Copyright (C) 1996,1997,1998,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MrsDebugLog(level,message) \ { if ((level) <= mrsDebugLevel_p) { \ LogIO logIo (LogOrigin ("MS")); logIo << (message) << endl; logIo.post();\ }\ } namespace casacore { //# NAMESPACE CASACORE - BEGIN MeasurementSet::MeasurementSet() : doNotLockSubtables_p (False), hasBeenDestroyed_p(True) { } MeasurementSet::MeasurementSet(const String &tableName, TableOption option) : MSTable(tableName, option), doNotLockSubtables_p (False), hasBeenDestroyed_p(False) { // verify that the now opened table is valid checkVersion(); mainLock_p=TableLock(TableLock::AutoNoReadLocking); addCat(); if (! validate(this->tableDesc())) throw (AipsError("MS(String &, TableOption) - " "table is not a valid MS")); initRefs(); } void MeasurementSet::addCat() { // For a transition period: add the CATEGORY keyword to the FLAG_CATEGORY // column silently if it is not there - 2000/08/22, remove next MS update. if (!tableDesc().columnDesc(columnName(FLAG_CATEGORY)). keywordSet().isDefined("CATEGORY")) { if (!isWritable()) { throw (AipsError("Missing CATEGORY keyword in FLAG_CATEGORY column -" "please open MS table R/W to have it added")); } else { ArrayColumn fc(*this,columnName(FLAG_CATEGORY)); fc.rwKeywordSet().define("CATEGORY",Vector(0)); } } } MeasurementSet::MeasurementSet (const String &tableName, const TableLock& lockOptions, bool doNotLockSubtables, TableOption option) : MSTable(tableName, lockOptions, option), doNotLockSubtables_p (doNotLockSubtables), hasBeenDestroyed_p(False) { mainLock_p=lockOptions; // verify that the now opened table is valid checkVersion(); addCat(); if (! validate(this->tableDesc())) throw (AipsError("MS(String &, lockOptions, TableOption) - " "table is not a valid MS")); initRefs(); } MeasurementSet::MeasurementSet(const String &tableName, const TableLock& lockOptions, TableOption option) : MSTable(tableName, lockOptions, option), doNotLockSubtables_p (False), hasBeenDestroyed_p(False) { mainLock_p=lockOptions; // verify that the now opened table is valid checkVersion(); addCat(); if (! validate(this->tableDesc())) throw (AipsError("MS(String &, lockOptions, TableOption) - " "table is not a valid MS")); initRefs(); } MeasurementSet::MeasurementSet(const String& tableName, const String &tableDescName, TableOption option) : MSTable(tableName, tableDescName, option), doNotLockSubtables_p (False), hasBeenDestroyed_p(False) { mainLock_p=TableLock(TableLock::AutoNoReadLocking); // verify that the now opened table is valid checkVersion(); addCat(); if (! validate(this->tableDesc())) throw (AipsError("MS(String &, String &, TableOption) - " "table is not a valid MS")); initRefs(); } MeasurementSet::MeasurementSet(const String& tableName, const String &tableDescName, const TableLock& lockOptions, TableOption option) : MSTable(tableName, tableDescName, lockOptions, option), doNotLockSubtables_p (False), hasBeenDestroyed_p(False) { // verify that the now opened table is valid mainLock_p=lockOptions; checkVersion(); addCat(); if (! validate(this->tableDesc())) throw (AipsError("MS(String &, String &, TableOption) - " "table is not a valid MS")); initRefs(); } MeasurementSet::MeasurementSet(SetupNewTable &newTab, rownr_t nrrow, Bool initialize) : MSTable(newTab, nrrow, initialize), doNotLockSubtables_p (False), hasBeenDestroyed_p(False) { mainLock_p=TableLock(TableLock::AutoNoReadLocking); // verify that the now opened table is valid addCat(); if (! validate(this->tableDesc())) throw (AipsError("MS(SetupNewTable &, rownr_t, Bool) - " "table is not a valid MS")); } MeasurementSet::MeasurementSet(SetupNewTable &newTab, const TableLock& lockOptions, rownr_t nrrow, Bool initialize) : MSTable(newTab, lockOptions, nrrow, initialize), doNotLockSubtables_p (False), hasBeenDestroyed_p(False) { mainLock_p=lockOptions; // verify that the now opened table is valid addCat(); if (! validate(this->tableDesc())) throw (AipsError("MS(SetupNewTable &, rownr_t, Bool) - " "table is not a valid MS")); } MeasurementSet::MeasurementSet(const Table &table, const MeasurementSet * otherMs) : MSTable(table), doNotLockSubtables_p (False), hasBeenDestroyed_p(False) { mainLock_p=TableLock(TableLock::AutoNoReadLocking); checkVersion(); // verify that the now opened table is valid addCat(); if (! validate(this->tableDesc())){ throw (AipsError("MS(const Table &) - " "table is not a valid MS")); } if (otherMs != NULL){ copySubtables (* otherMs); // others will be handled by initRefs } initRefs(); } #ifdef HAVE_MPI MeasurementSet::MeasurementSet (MPI_Comm comm, SetupNewTable &newTab, rownr_t nrrow, Bool initialize) : MSTable(comm, newTab, nrrow, initialize), doNotLockSubtables_p (False), hasBeenDestroyed_p(False) { mainLock_p=TableLock(TableLock::AutoNoReadLocking); // verify that the now opened table is valid addCat(); if (! validate(this->tableDesc())) throw (AipsError("MS(SetupNewTable &, rownr_t, Bool) - " "table is not a valid MS")); } MeasurementSet::MeasurementSet (MPI_Comm comm, SetupNewTable &newTab, const TableLock& lockOptions, rownr_t nrrow, Bool initialize) : MSTable(comm, newTab, lockOptions, nrrow, initialize), doNotLockSubtables_p (False), hasBeenDestroyed_p(False) { mainLock_p=lockOptions; // verify that the now opened table is valid addCat(); if (! validate(this->tableDesc())) throw (AipsError("MS(SetupNewTable &, rownr_t, Bool) - " "table is not a valid MS")); } #endif // HAVE_MPI MeasurementSet::MeasurementSet(const MeasurementSet &other) : MSTable(other), doNotLockSubtables_p(other.doNotLockSubtables_p), hasBeenDestroyed_p (other.hasBeenDestroyed_p) { if (! isNull()) { copySubtables (other); // others will be handled by initRefs mainLock_p=TableLock(TableLock::AutoNoReadLocking); // verify that other is valid addCat(); if (! validate(this->tableDesc())) { throw (AipsError("MS(const MeasurementSet &) - " "MeasurementSet is not a valid MS")); } initRefs(); } } MeasurementSet::~MeasurementSet() { // check to make sure that this MS is still valid if (! isNull()) { if (!hasBeenDestroyed_p && !validate()) { // the table is otherwise OK, so ensure that it is written if necessary this->flush(); LogIO os; os << LogIO::WARN << "~MS() - Table written is not a valid MS" << LogIO::POST; } } hasBeenDestroyed_p = True; } MeasurementSet& MeasurementSet::operator=(const MeasurementSet &other) { if (&other != this) { clearSubtables (); // Make all subtables refer to null tables MSTable::operator=(other); // MRS related components mrsEligibility_p = other.mrsEligibility_p; mrsDebugLevel_p = other.mrsDebugLevel_p; memoryResidentSubtables_p = other.memoryResidentSubtables_p; if (! isNull()) { copySubtables (other); } hasBeenDestroyed_p = other.hasBeenDestroyed_p; if (! isNull()) { initRefs(); } } return *this; } void MeasurementSet::copySubtables (const MeasurementSet & other) { // Replace the current subtables with the ones in the other MS // if they exist in the other MS; otherwise leave them unchanged. copySubtable (other.antenna_p, antenna_p); copySubtable (other.dataDesc_p, dataDesc_p); copySubtable (other.doppler_p, doppler_p); copySubtable (other.feed_p, feed_p); copySubtable (other.field_p, field_p); copySubtable (other.flagCmd_p, flagCmd_p); copySubtable (other.freqOffset_p, freqOffset_p); copySubtable (other.history_p, history_p); copySubtable (other.observation_p, observation_p); copySubtable (other.pointing_p, pointing_p); copySubtable (other.polarization_p, polarization_p); copySubtable (other.processor_p, processor_p); copySubtable (other.source_p, source_p); copySubtable (other.spectralWindow_p, spectralWindow_p); copySubtable (other.state_p, state_p); copySubtable (other.sysCal_p, sysCal_p); copySubtable (other.weather_p, weather_p); } void MeasurementSet::copySubtable (const Table & otherSubtable, Table & subTable) { // If the other table is not null then assign // it to the provided subtable if (! otherSubtable.isNull ()){ subTable = otherSubtable; } } MrsEligibility MeasurementSet::getMrsEligibility () const { return mrsEligibility_p; } MSTableMaps MeasurementSet::initMaps() { // This function is called once (by MSTableImpl::doInitMap), // so the map should be empty. MSTableMaps maps; AlwaysAssert (maps.columnMap_p.empty(), AipsError); // the PredefinedColumns // ANTENNA1 colMapDef(maps, ANTENNA1, "ANTENNA1", TpInt, "ID of first antenna in interferometer","",""); // ANTENNA2 colMapDef(maps, ANTENNA2, "ANTENNA2", TpInt, "ID of second antenna in interferometer","",""); // ANTENNA3 colMapDef(maps, ANTENNA3, "ANTENNA3", TpInt, "ID of third antenna in interferometer","",""); // ARRAY_ID colMapDef(maps, ARRAY_ID, "ARRAY_ID", TpInt, "ID of array or subarray","",""); // BASELINE_REF colMapDef(maps, BASELINE_REF,"BASELINE_REF",TpBool, "Reference antenna for this baseline, True for ANTENNA1","", ""); // the CORRECTED_DATA column, colMapDef(maps, CORRECTED_DATA,"CORRECTED_DATA",TpArrayComplex, "The corrected data column","",""); // the DATA columns, colMapDef(maps, DATA,"DATA",TpArrayComplex,"The data column","",""); // the DATA_DESC_ID colMapDef(maps, DATA_DESC_ID,"DATA_DESC_ID",TpInt, "The data description table index","",""); // EXPOSURE colMapDef(maps, EXPOSURE, "EXPOSURE", TpDouble, "The effective integration time","s",""); // FEED1 colMapDef(maps, FEED1, "FEED1", TpInt, "The feed index for ANTENNA1","",""); // FEED2 colMapDef(maps, FEED2, "FEED2", TpInt, "The feed index for ANTENNA2","",""); // FEED3 colMapDef(maps, FEED3, "FEED3", TpInt, "The feed index for ANTENNA3","",""); // FIELD_ID colMapDef(maps, FIELD_ID,"FIELD_ID", TpInt, "Unique id for this pointing","",""); // FLAG colMapDef(maps, FLAG,"FLAG", TpArrayBool, "The data flags, array of bools with same shape as data","",""); // FLAG_CATEGORY colMapDef(maps, FLAG_CATEGORY,"FLAG_CATEGORY", TpArrayBool, "The flag category, NUM_CAT flags for each datum","",""); // FLAG_ROW colMapDef(maps, FLAG_ROW,"FLAG_ROW", TpBool, "Row flag - flag all data in this row if True","",""); // FLOAT_DATA colMapDef(maps, FLOAT_DATA,"FLOAT_DATA",TpArrayFloat, "Floating point data - for single dish","",""); // IMAGING_WEIGHT colMapDef(maps, IMAGING_WEIGHT,"IMAGING_WEIGHT",TpArrayFloat, "Weight set by imaging task (e.g. uniform weighting)","",""); // INTERVAL colMapDef(maps, INTERVAL, "INTERVAL", TpDouble, "The sampling interval","s",""); // the LAG_DATA column, colMapDef(maps, LAG_DATA,"LAG_DATA",TpArrayComplex, "The lag data column","",""); // the MODEL_DATA column, colMapDef(maps, MODEL_DATA,"MODEL_DATA",TpArrayComplex, "The model data column","",""); // OBSERVATION_ID colMapDef(maps, OBSERVATION_ID, "OBSERVATION_ID", TpInt, "ID for this observation, index in OBSERVATION table","",""); // PHASE_ID colMapDef(maps, PHASE_ID,"PHASE_ID",TpInt, "Id for phase switching","",""); // PROCESSOR_ID colMapDef(maps, PROCESSOR_ID,"PROCESSOR_ID",TpInt, "Id for backend processor, index in PROCESSOR table","",""); // PULSAR_BIN colMapDef(maps, PULSAR_BIN, "PULSAR_BIN", TpInt, "Pulsar pulse-phase bin for this DATA","",""); // PULSAR_GATE_ID colMapDef(maps, PULSAR_GATE_ID, "PULSAR_GATE_ID", TpInt, "ID for this gate, index into PULSAR_GATE table","",""); // SCAN_NUMBER colMapDef(maps, SCAN_NUMBER, "SCAN_NUMBER", TpInt, "Sequential scan number from on-line system","",""); // STATE_ID colMapDef(maps, STATE_ID,"STATE_ID",TpInt, "ID for this observing state","",""); // SIGMA colMapDef(maps, SIGMA, "SIGMA", TpArrayFloat, "Estimated rms noise for channel with unity bandpass response","",""); // SIGMA_SPECTRUM colMapDef(maps, SIGMA_SPECTRUM, "SIGMA_SPECTRUM", TpArrayFloat, "Estimated rms noise for each data point","",""); // TIME colMapDef(maps, TIME, "TIME", TpDouble, "Modified Julian Day","s","Epoch"); // TIME_CENTROID colMapDef(maps, TIME_CENTROID, "TIME_CENTROID", TpDouble, "Modified Julian Day","s","Epoch"); // TIME_EXTRA_PREC colMapDef(maps, TIME_EXTRA_PREC, "TIME_EXTRA_PREC", TpDouble, "Additional precision for TIME","s",""); // UVW colMapDef(maps, UVW, "UVW", TpArrayDouble, "Vector with uvw coordinates (in meters)","m","uvw"); // UVW2 colMapDef(maps, UVW2,"UVW2",TpArrayDouble, "uvw coordinates for second pair of triple corr product", "m","uvw"); // VIDEO_POINT colMapDef(maps, VIDEO_POINT,"VIDEO_POINT",TpArrayComplex, "zero frequency point, needed for transform to lag","",""); // WEIGHT colMapDef(maps, WEIGHT, "WEIGHT", TpArrayFloat, "Weight for each polarization spectrum","",""); // WEIGHT_SPECTRUM colMapDef(maps, WEIGHT_SPECTRUM, "WEIGHT_SPECTRUM", TpArrayFloat, "Weight for each data point","",""); // CORRECTED_WEIGHT_SPECTRUM colMapDef(maps, CORRECTED_WEIGHT_SPECTRUM, "CORRECTED_WEIGHT_SPECTRUM", TpArrayFloat, "Weight for each corrected data point","",""); // PredefinedKeywords // ANTENNA keyMapDef(maps, ANTENNA,"ANTENNA", TpTable, "Antenna subtable. Antenna positions, mount-types etc."); // DATA_DESCRIPTION keyMapDef(maps, DATA_DESCRIPTION,"DATA_DESCRIPTION", TpTable, "DATA_DESCRIPTION subtable. Points to freq and pol layout" "in subtables"); // FEED keyMapDef(maps, FEED,"FEED", TpTable, "Feed subtable. Responses, offsets, beams etc."); // FIELD keyMapDef(maps, FIELD,"FIELD",TpTable, "Field subtable. Position etc. for each pointing."); // FLAG_CMD keyMapDef(maps, FLAG_CMD,"FLAG_CMD",TpTable, "Flag command subtable. Stores global flagging commands"); // HISTORY keyMapDef(maps, HISTORY,"HISTORY",TpTable, "Observation and processing history"); // MS_VERSION keyMapDef(maps, MS_VERSION,"MS_VERSION",TpFloat, "MS version number, i.e., 2.0"); // OBSERVATION keyMapDef(maps, OBSERVATION,"OBSERVATION",TpTable, "Observation subtable. Project, observer, schedule."); // POINTING keyMapDef(maps, POINTING,"POINTING",TpTable, "Pointing subtable. Antenna pointing info."); // POLARIZATION keyMapDef(maps, POLARIZATION,"POLARIZATION",TpTable, "Polarization set up subtable"); // PROCESSOR keyMapDef(maps, PROCESSOR,"PROCESSOR",TpTable, "Backend Processor information subtable"); // SPECTRAL_WINDOW keyMapDef(maps, SPECTRAL_WINDOW,"SPECTRAL_WINDOW",TpTable, "Spectral window subtable. Frequencies, bandwidths," " polarizations"); // STATE keyMapDef(maps, STATE,"STATE",TpTable, "State subtable. State information (cal, ref etc.)"); // CAL_TABLES keyMapDef(maps, CAL_TABLES,"CAL_TABLES",TpTable, "Associated calibration tables, one per row"); // DOPPLER keyMapDef(maps, DOPPLER,"DOPPLER",TpTable, "Doppler tracking info"); // FREQ_OFFSET keyMapDef(maps, FREQ_OFFSET,"FREQ_OFFSET",TpTable, "Frequency offset information"); // SORT_COLUMNS keyMapDef(maps, SORT_COLUMNS,"SORT_COLUMNS",TpArrayString, "Listing of sort columns for each sorted table"); // SORT_ORDER keyMapDef(maps, SORT_ORDER,"SORT_ORDER",TpArrayString, "Listing of sort orders for each sorted table"); // SORTED_TABLES keyMapDef(maps, SORTED_TABLES,"SORTED_TABLES",TpTable, "Sorted reference tables of the main table, first one is" " main table"); // SOURCE keyMapDef(maps, SOURCE,"SOURCE",TpTable, "Source subtable. Positions etc. for each source."); // SYSCAL keyMapDef(maps, SYSCAL,"SYSCAL",TpTable, "SysCal subtable. System calibration data (Tsys etc.)."); // WEATHER keyMapDef(maps, WEATHER,"WEATHER",TpTable, "Weather subtable. Weather info for each antenna."); // define required keywords and columns TableDesc& requiredTD = maps.requiredTD_p; // all required keywords uInt i; for (i = UNDEFINED_KEYWORD+1; i <= NUMBER_REQUIRED_KEYWORDS; i++) { addKeyToDesc(maps, PredefinedKeywords(i)); } // Set MS_VERSION number requiredTD.rwKeywordSet().define("MS_VERSION",Float(2.0)); // all required columns // First define the columns with fixed size arrays IPosition shape(1,3); ColumnDesc::Option option=ColumnDesc::Direct; addColumnToDesc(maps, UVW, shape, option); // Also define columns with Arrays with their correct dimensionality addColumnToDesc(maps, FLAG, 2); addColumnToDesc(maps, FLAG_CATEGORY, 3); addColumnToDesc(maps, WEIGHT, 1); addColumnToDesc(maps, SIGMA, 1); // Now define all other columns (duplicates are skipped) for (i = UNDEFINED_COLUMN+1; i <= NUMBER_REQUIRED_COLUMNS; i++) { addColumnToDesc(maps, PredefinedColumns(i)); } // Add the column keyword for the FLAG_CATEGORY column requiredTD.rwColumnDesc("FLAG_CATEGORY").rwKeywordSet(). define("CATEGORY",Vector(0)); return maps; } MeasurementSet MeasurementSet::referenceCopy(const String& newTableName, const Block& writableColumns) const { return MeasurementSet(MSTable:: referenceCopy(newTableName,writableColumns)); } Bool MeasurementSet::isEligibleForMemoryResidency (const String & subtableName) const { // Convert the name to an Id MrsEligibility::SubtableId subtableId = keywordType (subtableName); ThrowIf (subtableId == MSMainEnums::UNDEFINED_KEYWORD, "No ID defined for subtable '" + subtableName + "'"); Bool isEligible = mrsEligibility_p.isEligible (subtableId); return isEligible; } template void MeasurementSet::openMrSubtable (Subtable & subtable, const String & subtableName) { if (this->keywordSet().isDefined (subtableName) && // exists in this MS isEligibleForMemoryResidency (subtableName) && // is permitted to be MR subtable.tableType() != Table::Memory){ // is not already MR MrsDebugLog (2, tableName() + " ---> Converting " + subtable.tableName() + " to MR."); // If the subtable is part of this measurement set and it is marked as eligible for // memory residency, then replace the current, normal subtable object with a // memory resident copy. The caller will alreayd have checked to enusre that the // MS is not writable and that the memory-resident subtable feature is enabled. String mrSubtableName = subtable.tableName (); // + "_MR"; Subtable memoryResident (subtable.copyToMemoryTable (mrSubtableName)); // Replace the existing subtable with the memory resident one. subtable = memoryResident; } } void MeasurementSet::setMemoryResidentSubtables (const MrsEligibility & mrsEligibility) { mrsEligibility_p = mrsEligibility; // See if the memory resident subtable feature is enabled AipsrcValue::find (memoryResidentSubtables_p, getMrsAipsRcBase () + ".enable", False); AipsrcValue::find (mrsDebugLevel_p, getMrsAipsRcBase () + ".debug.level", 0); Bool mrsEnabled = memoryResidentSubtables_p; MrsDebugLog (1, tableName() + " ---> MR Subtables " + (memoryResidentSubtables_p ? "enabled " : "disabled ")); // Attempt to open the subtables as memory resident if memory resident subtables // are enabled. Per table eligibility for memory residency is handled by openMrSubtable. if (mrsEnabled) { openMrSubtable (antenna_p, "ANTENNA"); openMrSubtable (dataDesc_p, "DATA_DESCRIPTION"); openMrSubtable (doppler_p, "DOPPLER"); openMrSubtable (feed_p, "FEED"); openMrSubtable (field_p, "FIELD"); openMrSubtable (flagCmd_p, "FLAG_CMD"); openMrSubtable (freqOffset_p, "FREQ_OFFSET"); openMrSubtable (history_p, "HISTORY"); openMrSubtable (observation_p, "OBSERVATION"); openMrSubtable (pointing_p, "POINTING"); openMrSubtable (polarization_p, "POLARIZATION"); openMrSubtable (processor_p, "PROCESSOR"); openMrSubtable (source_p, "SOURCE"); openMrSubtable (spectralWindow_p, "SPECTRAL_WINDOW"); openMrSubtable (state_p, "STATE"); openMrSubtable (sysCal_p, "SYSCAL"); openMrSubtable (weather_p, "WEATHER"); } } String MeasurementSet::antennaTableName() const { if (antenna_p.isNull()) { return tableName()+"/ANTENNA"; } return antenna_p.tableName(); } String MeasurementSet::dataDescriptionTableName() const { if (dataDesc_p.isNull()) { return tableName()+"/DATA_DESCRIPTION"; } return dataDesc_p.tableName(); } String MeasurementSet::dopplerTableName() const { if (doppler_p.isNull()) { return tableName()+"/DOPPLER"; } return doppler_p.tableName(); } String MeasurementSet::feedTableName() const { if (feed_p.isNull()) { return tableName()+"/FEED"; } return feed_p.tableName(); } String MeasurementSet::fieldTableName() const { if (field_p.isNull()) { return tableName()+"/FIELD"; } return field_p.tableName(); } String MeasurementSet::flagCmdTableName() const { if (flagCmd_p.isNull()) { return tableName()+"/FLAG_CMD"; } return flagCmd_p.tableName(); } String MeasurementSet::freqOffsetTableName() const { if (freqOffset_p.isNull()) { return tableName()+"/FREQ_OFFSET"; } return freqOffset_p.tableName(); } String MeasurementSet::historyTableName() const { if (history_p.isNull()) { return tableName()+"/HISTORY"; } return history_p.tableName(); } String MeasurementSet::observationTableName() const { if (observation_p.isNull()) { return tableName()+"/OBSERVATION"; } return observation_p.tableName(); } String MeasurementSet::pointingTableName() const { if (pointing_p.isNull()) { return tableName()+"/POINTING"; } return pointing_p.tableName(); } String MeasurementSet::polarizationTableName() const { if (polarization_p.isNull()) { return tableName()+"/POLARIZATION"; } return polarization_p.tableName(); } String MeasurementSet::processorTableName() const { if (processor_p.isNull()) { return tableName()+"/PROCESSOR"; } return processor_p.tableName(); } String MeasurementSet::sourceTableName() const { if (source_p.isNull()) { return tableName()+"/SOURCE"; } return source_p.tableName(); } String MeasurementSet::spectralWindowTableName() const { if (spectralWindow_p.isNull()) { return tableName()+"/SPECTRAL_WINDOW"; } return spectralWindow_p.tableName(); } String MeasurementSet::stateTableName() const { if (state_p.isNull()) { return tableName()+"/STATE"; } return state_p.tableName(); } String MeasurementSet::sysCalTableName() const { if (sysCal_p.isNull()) { return tableName()+"/SYSCAL"; } return sysCal_p.tableName(); } String MeasurementSet::weatherTableName() const { if (weather_p.isNull()) { return tableName()+"/WEATHER"; } return weather_p.tableName(); } void MeasurementSet::clearSubtables () { antenna_p=MSAntenna(); dataDesc_p=MSDataDescription(); doppler_p=MSDoppler(); feed_p=MSFeed(); field_p=MSField(); flagCmd_p=MSFlagCmd(); freqOffset_p=MSFreqOffset(); history_p=MSHistory(); observation_p=MSObservation(); pointing_p=MSPointing(); polarization_p=MSPolarization(); processor_p=MSProcessor(); source_p=MSSource(); spectralWindow_p=MSSpectralWindow(); state_p=MSState(); sysCal_p=MSSysCal(); weather_p=MSWeather(); } template void MeasurementSet::openSubtable (Subtable & subtable, const String & subtableName, Bool useLock) { if (subtable.isNull() && this->keywordSet().isDefined (subtableName)){ // Only open a subtable if it does not already exist in this object and if // the subtable is defined in the on-disk MeasurementSet if (doNotLockSubtables_p){ // Do not lock the subtable based on main table TableLock subtableLock (TableLock::UserNoReadLocking); subtable = Subtable (this->keywordSet().asTable(subtableName, subtableLock)); } else if (useLock){ subtable = Subtable (this->keywordSet().asTable(subtableName, mainLock_p)); } else{ // scratch tables don't use the lock subtable = Subtable (this->keywordSet().asTable(subtableName)); } } } void MeasurementSet::initRefs(Bool clear) { if (isNull()||clear) { clearSubtables (); } if (!isNull()) { // write the table info if needed if (this->tableInfo().type()=="") { String reqdType=this->tableInfo().type(TableInfo::MEASUREMENTSET); this->tableInfo().setType(reqdType); String reqdSubType=this->tableInfo().subType(TableInfo::MEASUREMENTSET); this->tableInfo().setSubType(reqdSubType); this->tableInfo().readmeAddLine("This is a MeasurementSet Table" " holding measurements from a Telescope"); } Bool useLock = (this->tableOption() != Table::Scratch); openSubtable (antenna_p, "ANTENNA", useLock); openSubtable (dataDesc_p, "DATA_DESCRIPTION", useLock); openSubtable (doppler_p, "DOPPLER", useLock); openSubtable (feed_p, "FEED", useLock); openSubtable (field_p, "FIELD", useLock); openSubtable (flagCmd_p, "FLAG_CMD", useLock); openSubtable (freqOffset_p, "FREQ_OFFSET", useLock); openSubtable (history_p, "HISTORY", useLock); openSubtable (observation_p, "OBSERVATION", useLock); openSubtable (pointing_p, "POINTING", useLock); openSubtable (polarization_p, "POLARIZATION", useLock); openSubtable (processor_p, "PROCESSOR", useLock); openSubtable (source_p, "SOURCE", useLock); openSubtable (spectralWindow_p, "SPECTRAL_WINDOW", useLock); openSubtable (state_p, "STATE", useLock); openSubtable (sysCal_p, "SYSCAL", useLock); openSubtable (weather_p, "WEATHER", useLock); } } template static Table create_table(SetupNewTable &tableSetup, T /*comm*/) { return Table(tableSetup); } void MeasurementSet::createDefaultSubtables(Table::TableOption option) { createDefaultSubtables_impl(option, 0); } #ifdef HAVE_MPI template<> Table create_table(SetupNewTable &tableSetup, MPI_Comm comm) { return Table(comm, tableSetup); } void MeasurementSet::createDefaultSubtables(MPI_Comm comm, Table::TableOption option) { createDefaultSubtables_impl(option, comm); } #endif // HAVE_MPI template void MeasurementSet::createDefaultSubtables_impl(Table::TableOption option, T comm) { SetupNewTable antennaSetup(antennaTableName(), MSAntenna::requiredTableDesc(),option); rwKeywordSet().defineTable(MS::keywordName(MS::ANTENNA), create_table(antennaSetup, comm)); SetupNewTable dataDescSetup(dataDescriptionTableName(), MSDataDescription::requiredTableDesc(),option); rwKeywordSet().defineTable(MS::keywordName(MS::DATA_DESCRIPTION), create_table(dataDescSetup, comm)); SetupNewTable feedSetup(feedTableName(), MSFeed::requiredTableDesc(),option); rwKeywordSet().defineTable(MS::keywordName(MS::FEED), create_table(feedSetup, comm)); SetupNewTable flagCmdSetup(flagCmdTableName(), MSFlagCmd::requiredTableDesc(),option); rwKeywordSet().defineTable(MS::keywordName(MS::FLAG_CMD), create_table(flagCmdSetup, comm)); SetupNewTable fieldSetup(fieldTableName(), MSField::requiredTableDesc(),option); rwKeywordSet().defineTable(MS::keywordName(MS::FIELD), create_table(fieldSetup, comm)); SetupNewTable historySetup(historyTableName(), MSHistory::requiredTableDesc(),option); rwKeywordSet().defineTable(MS::keywordName(MS::HISTORY), create_table(historySetup, comm)); SetupNewTable observationSetup(observationTableName(), MSObservation::requiredTableDesc(),option); rwKeywordSet().defineTable(MS::keywordName(MS::OBSERVATION), create_table(observationSetup, comm)); SetupNewTable pointingSetup(pointingTableName(), MSPointing::requiredTableDesc(),option); // Pointing table can be large, set some sensible defaults for storageMgrs IncrementalStMan ismPointing ("ISMPointing"); StandardStMan ssmPointing("SSMPointing",32768); pointingSetup.bindAll(ismPointing,True); pointingSetup.bindColumn(MSPointing::columnName(MSPointing::ANTENNA_ID), ssmPointing); rwKeywordSet().defineTable(MS::keywordName(MS::POINTING), create_table(pointingSetup, comm)); SetupNewTable polarizationSetup(polarizationTableName(), MSPolarization::requiredTableDesc(),option); rwKeywordSet().defineTable(MS::keywordName(MS::POLARIZATION), create_table(polarizationSetup, comm)); SetupNewTable processorSetup(processorTableName(), MSProcessor::requiredTableDesc(),option); rwKeywordSet().defineTable(MS::keywordName(MS::PROCESSOR), create_table(processorSetup, comm)); SetupNewTable spectralWindowSetup(spectralWindowTableName(), MSSpectralWindow::requiredTableDesc(),option); rwKeywordSet().defineTable(MS::keywordName(MS::SPECTRAL_WINDOW), create_table(spectralWindowSetup, comm)); SetupNewTable stateSetup(stateTableName(), MSState::requiredTableDesc(),option); rwKeywordSet().defineTable(MS::keywordName(MS::STATE), create_table(stateSetup, comm)); initRefs(); } Bool MeasurementSet::makeComplexData() { // for now we use an extremely simplistic implementation (should find out // storage managers and tiles and keep things the same) if (tableDesc().isColumn(MS::columnName(MS::DATA))) return False; if (!tableDesc().isColumn(MS::columnName(MS::FLOAT_DATA))) return False; // we have FLOAT_DATA but not DATA // add DATA addColumn(ArrayColumnDesc("DATA",2)); // now copy data across from FLOAT_DATA ArrayColumn floatData(*this,MS::columnName(MS::FLOAT_DATA)); ArrayColumn data(*this,MS::columnName(MS::DATA)); for (rownr_t i=0; i floatArr(floatData(i)); Array dataArr(floatArr.shape()); convertArray(dataArr,floatArr); data.put(i,dataArr); } return True; } Bool MeasurementSet::validateMeasureRefs() { Bool ok=True; // check main table { Int nCol = tableDesc().ncolumn(); for (Int i=0; i=0) { Int refFld = tableDesc()[i].keywordSet().asRecord(fld). fieldNumber("Ref"); if (refFld<0 || tableDesc()[i].keywordSet().asRecord(fld). asString(refFld) == "") { cerr << "Missing Measure reference for column "<=0) { Int refFld = tab.tableDesc()[i].keywordSet().asRecord(fld). fieldNumber("Ref"); if (refFld<0 || tab.tableDesc()[i].keywordSet().asRecord(fld). asString(refFld) == "") { cerr << "Missing Measure reference for column " <::flush(sync); antenna_p.flush(sync); dataDesc_p.flush(sync); if (!doppler_p.isNull()) doppler_p.flush(sync); feed_p.flush(sync); field_p.flush(sync); flagCmd_p.flush(sync); if (!freqOffset_p.isNull()) freqOffset_p.flush(sync); history_p.flush(sync); observation_p.flush(sync); pointing_p.flush(sync); polarization_p.flush(sync); processor_p.flush(sync); if (!source_p.isNull()) source_p.flush(sync); spectralWindow_p.flush(sync); state_p.flush(sync); if (!sysCal_p.isNull()) sysCal_p.flush(sync); if (!weather_p.isNull()) weather_p.flush(sync); } void MeasurementSet::checkVersion() { // Check that the MS is the latest version (2.0). Throw an // exception and advise the user to use the MS converter if it is not. // if (!keywordSet().isDefined("MS_VERSION") || (keywordSet().isDefined("MS_VERSION") && keywordSet().asFloat("MS_VERSION")!=2.0)) { throw(AipsError("These data are not in MSv2 format - use ms1toms2 to convert")); } } Record MeasurementSet::msseltoindex(const String& spw, const String& field, const String& baseline, const String& time, const String& scan, const String& uvrange, const String& observation, const String& poln, const String& taql) { Record retval; MSSelection thisSelection; thisSelection.setSpwExpr(spw); thisSelection.setFieldExpr(field); thisSelection.setAntennaExpr(baseline); thisSelection.setTimeExpr(time); thisSelection.setScanExpr(scan); thisSelection.setUvDistExpr(uvrange); thisSelection.setObservationExpr(observation); thisSelection.setPolnExpr(poln); thisSelection.setTaQLExpr(taql); TableExprNode exprNode=thisSelection.toTableExprNode(this); Vector fieldlist=thisSelection.getFieldList(); Vector spwlist=thisSelection.getSpwList(); Vector scanlist=thisSelection.getScanList(); Vector obslist=thisSelection.getObservationList(); Vector antenna1list=thisSelection.getAntenna1List(); Vector antenna2list=thisSelection.getAntenna2List(); Matrix chanlist=thisSelection.getChanList(); Matrix baselinelist=thisSelection.getBaselineList(); Vector ddIDList=thisSelection.getDDIDList(); Vector spwDDIDList=thisSelection.getSPWDDIDList(); std::map > polMap=thisSelection.getPolMap(); std::map > > corrMap=thisSelection.getCorrMap(); Vector allDDIDList; if (ddIDList.nelements() == 0) allDDIDList = spwDDIDList; else if (spwDDIDList.nelements() == 0) allDDIDList = ddIDList; else allDDIDList = set_intersection(ddIDList, spwDDIDList); // cerr << ddIDList << endl << spwDDIDList << endl << allDDIDList << endl; retval.define("spw", spwlist); retval.define("field", fieldlist); retval.define("scan", scanlist); retval.define("obsids", obslist); retval.define("antenna1", antenna1list); retval.define("antenna2", antenna2list); retval.define("baselines", baselinelist); retval.define("channel", chanlist); retval.define("poldd", ddIDList); retval.define("spwdd", spwDDIDList); retval.define("dd", allDDIDList); // retval.define("polmap",polMap); // retrval.define("corrmap",corrMap); return retval; } const MrsEligibility MrsEligibility::allSubtables_p = allEligible (); MrsEligibility MrsEligibility::allButTheseSubtables (SubtableId subtableId, ...) { va_list vaList; va_start (vaList, subtableId); SubtableId id = subtableId; MrsEligibility ineligible; while (id > MSMainEnums::UNDEFINED_KEYWORD && id <= MSMainEnums::NUMBER_PREDEFINED_KEYWORDS){ ThrowIf (! isSubtable (id), "Invalid subtable ID: " + String::toString (id)); ineligible.eligible_p.insert (id); id = (SubtableId) va_arg (vaList, int); } va_end (vaList); // Get the set of all subtables and then subtract off the // caller specified columns. Return the result MrsEligibility eligible; set_difference (allSubtables_p.eligible_p.begin(), allSubtables_p.eligible_p.end(), ineligible.eligible_p.begin(), ineligible.eligible_p.end(), inserter (eligible.eligible_p, eligible.eligible_p.begin())); return eligible; } MrsEligibility MrsEligibility::allEligible () { MrsEligibility all; // Start out by putting in all of the keywords defined in // the enum for (int i = MSMainEnums::UNDEFINED_KEYWORD+1; i <= MSMainEnums::NUMBER_PREDEFINED_KEYWORDS; ++ i){ all.eligible_p.insert ((SubtableId) i); } // Remove any Ids known not to be subtables all.eligible_p.erase (MSMainEnums::MS_VERSION); all.eligible_p.erase (MSMainEnums::CAL_TABLES); all.eligible_p.erase (MSMainEnums::SORT_COLUMNS); all.eligible_p.erase (MSMainEnums::SORT_ORDER); all.eligible_p.erase (MSMainEnums::SORTED_TABLES); return all; } MrsEligibility MrsEligibility::defaultEligible () { // The following two subtables can become quite large // and should normally not be made memory resident MrsEligibility defaultSubtables = allButTheseSubtables (MSMainEnums::HISTORY, MSMainEnums::POINTING, MSMainEnums::SYSCAL, MSMainEnums::UNDEFINED_KEYWORD); return defaultSubtables; } MrsEligibility MrsEligibility::eligibleSubtables (SubtableId subtableId, ...) { va_list vaList; va_start (vaList, subtableId); SubtableId id = subtableId; MrsEligibility eligible; while (id > MSMainEnums::UNDEFINED_KEYWORD && id <= MSMainEnums::NUMBER_PREDEFINED_KEYWORDS){ ThrowIf (! isSubtable (id), "Invalid subtable ID: " + String::toString (id)); eligible.eligible_p.insert (id); id = (SubtableId) va_arg (vaList, Int); } va_end (vaList); return eligible; } Bool MrsEligibility::isSubtable (SubtableId subtableId) { Bool result = allSubtables_p.eligible_p.find (subtableId) != allSubtables_p.eligible_p.end(); return result; } Bool MrsEligibility::isEligible(SubtableId subtableId) const { Bool result = eligible_p.find (subtableId) != eligible_p.end(); return result; } MrsEligibility MrsEligibility::noneEligible () { return MrsEligibility (); } MrsEligibility operator- (const MrsEligibility & a, MrsEligibility::SubtableId subtableId) { MrsEligibility result = a; result.eligible_p.erase (subtableId); return result; } MrsEligibility operator+ (const MrsEligibility & a, MrsEligibility::SubtableId subtableId) { MrsEligibility result = a; result.eligible_p.insert (subtableId); return result; } MrsEligibility operator- (const MrsEligibility & a, const MrsEligibility & b) { MrsEligibility result; std::set_difference (a.eligible_p.begin(), a.eligible_p.end(), b.eligible_p.begin(), b.eligible_p.end(), inserter (result.eligible_p, result.eligible_p.begin())); return result; } MrsEligibility operator+ (const MrsEligibility & a, const MrsEligibility & b) { MrsEligibility result; std::set_union (a.eligible_p.begin(), a.eligible_p.end(), b.eligible_p.begin(), b.eligible_p.end(), inserter (result.eligible_p, result.eligible_p.begin())); return result; } } //# NAMESPACE CASACORE - END casacore-3.7.1/ms/MeasurementSets/MeasurementSet.h000066400000000000000000000500701476623553700222030ustar00rootroot00000000000000//# MeasurementSet.h: A Table to hold astronomical data (a set of Measurements) //# Copyright (C) 1996,1997,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MEASUREMENTSET_H #define MS_MEASUREMENTSET_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class MrsEligibility { // Memory Resident Subtable (Mrs) Eligibility (no pun intended) public: typedef MSMainEnums::PredefinedKeywords SubtableId; friend MrsEligibility operator- (const MrsEligibility & a, SubtableId subtableId); friend MrsEligibility operator+ (const MrsEligibility & a, SubtableId subtableId); friend MrsEligibility operator- (const MrsEligibility & a, const MrsEligibility & b); friend MrsEligibility operator+ (const MrsEligibility & a, const MrsEligibility & b); // Returns true if the specified subtable is in the set of subtables // eligible for memory residency. Bool isEligible (SubtableId subtableId) const; // Factory methods to create MrsEligibility sets. The two variable argument methods // require that the list be terminated by using the id MSMainEnums::UNDEFINED_KEYWORD. // static MrsEligibility allEligible (); static MrsEligibility defaultEligible (); static MrsEligibility noneEligible (); static MrsEligibility eligibleSubtables (SubtableId subtableId, ...); static MrsEligibility allButTheseSubtables (SubtableId ineligibleSubtableId, ...); private: typedef std::set Eligible; Eligible eligible_p; static const MrsEligibility allSubtables_p; static Bool isSubtable (SubtableId subtableId); }; // Creates a new MrsEligibilitySet by adding or removing the specified subtable or // the specified set of subtables. MrsEligibility operator- (const MrsEligibility & a, MrsEligibility::SubtableId subtableId); MrsEligibility operator+ (const MrsEligibility & a, MrsEligibility::SubtableId subtableId); MrsEligibility operator- (const MrsEligibility & a, const MrsEligibility & b); MrsEligibility operator+ (const MrsEligibility & a, const MrsEligibility & b); //# Forward Declarations, more could be if they weren't part of the //# static classes class SetupNewTable; template class Block; class MDirection; class MEpoch; class MFrequency; class MPosition; class Record; //# forward declared so that the following typedef is up-front class MeasurementSet; // MeasurementSet is too cumbersome for a number of common uses, // so we give a typedef here. typedef MeasurementSet MS; // // A Table intended to hold astronomical data (a set of Measurements). // // // // //
      • Tables module //
      • MSTable // // // // The MeasurementSet is where all data are ultimately to be found // in Casacore. Since, this is a collection of // measurements (either actual or simulated), the term MeasurementSet // seems appropriate. // // // // A MeasurementSet is a Table. Most operations on a MeasurementSet are // Table operations. See the Tables // module for a list of those operations. The member functions provided by this // class are primarily convenience functions to help users follow the // agreed upon column and keyword naming conventions. They are useful when // creating a Table following the MeasurementSet conventions from // scratch as well as when creating the column objects to access those // columns. // // The standard way of accessing // table columns is through Strings. Mistakes in typing the column // name will not be caught at compile time (and may not be caught at // run time). We have therefore decided to use an enumeration // to specify columns so that many mistakes will be caught at compile // time. This requires functions to map to and from this enumeration // to the strings that are ultimately used. // // Upon destruction, the table is checked to see that the // MeasurementSet remains valid, i.e., all required columns are present // An exception is thrown if not all required columns are present // Nevertheless, the table will be flushed to disk if it is writable - // preserving its state. // // A MeasurementSet has a number of required subtables. These are stored // as keywords in the Table. Access to these subtables is provided via // member functions (e.g. antenna() for the ANTENNA table). All subtables // have associated MeasurementSet-like classes defined for them (MSAntenna // for the ANTENNA table) which provide analogous column and keyword mapping // as provided here. // // While the class name, MeasurementSet, is descriptive, it is often // too long for many common uses. The typedef MS is provided as // a convenient shorthand for MeasurementSet. The example below uses this // typedef. // // Due to the inheritance scheme, it was necessary to separate the enumerations // used by MeasurementSet into a separate class, // MSMainEnums. // // // // // This example illustrates a simple use of the MeasurementSet class. // // // create the table descriptor // TableDesc simpleDesc = MS::requiredTableDesc(); // // set up a new table // SetupNewTable newTab("simpleTab", simpleDesc, Table::New); // // create the MeasurementSet // MeasurementSet simpleMS(newTab); // // now we need to define all required subtables // // the following call does this for us if we don't need to // // specify details of Storage Managers for columns. // simpleMS.createDefaultSubtables(Table::New); // // fill MeasurementSet via its Table interface // // For example, construct one of the columns // TableColumn feed(simpleMS, MS::columnName(MS::FEED1)); // rownr_t rownr = 0; // // add a row // simpleMS.addRow(); // // set the values in that row, e.g. the feed column // feed.putScalar(rownr,1); // // Access a subtable // ArrayColumn antpos(simpleMS.antenna(), // MSAntenna::columnName(MSAntenna::POSITION)); // simpleMS.antenna().addRow(); // Array position(3); // position(0)=1.; position(1)=2.; position(2)=3.; // antpos.put(0,position); // // etc. // // // // // // The Table module is more than adequate as a container of data. // However, in order for applications to be useful with data from // different sources, some conventions need to be adopted in the use // of Tables to store data. The MeasurementSet is // where those conventions are defined and, to some extent, enforced. // // There are a number of reasons why MeasurementSet is more // than just a Table. //
          //
        • To provide one location where the column and keyword names, data // types, and table comment strings are found. //
        • To provide one location where the required table descriptor for // the MeasurementSet is found. //
        • To provide a means of verifying the validity of a MeasurementSet // at construction and destruction. //
        • To allow application programmers to catch name or data type // mistakes at compile time rather than at run time. //
        // //
        // // //
      • referenceCopy() should be more flexible with the storage managers used // for the columns which are not merely references. //
      • When ForwardColumnEngine is fixed so that it can deal with // tables already in the cache, modify the test program. It may also // be necessary to modify referenceCopy(). // class MeasurementSet : public MSTable, public MSMainEnums { public: // This constructs an empty MeasurementSet, only useful to assign to // (it is not a valid MS yet). MeasurementSet (); // These constructors mirror the Table ones with additional checking // on validity (verifying that the MS will have the required columns // and keywords) // An exception is thrown if the constructed Table is not a valid MS // //
      • AipsError // // MeasurementSet (const String &tableName, TableOption = Table::Old); MeasurementSet (const String &tableName, const TableLock& lockOptions, TableOption = Table::Old); MeasurementSet (const String &tableName, const TableLock& lockOptions, bool doNotLockSubtables, TableOption = Table::Old); // Allows keeping subtables unlocked/read-locked independent of lock // mode of main table. MeasurementSet (const String &tableName, const String &tableDescName, TableOption = Table::Old); MeasurementSet (const String &tableName, const String &tableDescName, const TableLock& lockOptions, TableOption = Table::Old); MeasurementSet (SetupNewTable &newTab, rownr_t nrrow = 0, Bool initialize = False); MeasurementSet (SetupNewTable &newTab, const TableLock& lockOptions, rownr_t nrrow = 0, Bool initialize = False); MeasurementSet (const Table &table, const MeasurementSet * otherMs = NULL); #ifdef HAVE_MPI MeasurementSet (MPI_Comm comm, SetupNewTable &newTab, rownr_t nrrow = 0, Bool initialize = False); MeasurementSet (MPI_Comm comm, SetupNewTable &newTab, const TableLock& lockOptions, rownr_t nrrow = 0, Bool initialize = False); #endif // HAVE_MPI MeasurementSet (const MeasurementSet &other); // // As with tables, the destructor writes the table if necessary. // Additional checking is done here to verify that all required // columns are still present. // If it is NOT valid, it will write the table and then throw an exception. // //
      • AipsError // virtual ~MeasurementSet(); // Assignment operator, reference semantics MeasurementSet& operator=(const MeasurementSet&); // Make a special copy of this MS which references all columns from // this MS except those mentioned; those are empty and writable. // Each forwarded column has the same writable status as the underlying // column. The mentioned columns all use the AipsIO storage manager. // The main use of this is for the synthesis package where corrected and // model visibilities are stored as new DATA columns in an MS which // references the raw MS for the other columns. Except for these special // cases, the use of this function will be rare. MeasurementSet referenceCopy(const String& newTableName, const Block& writableColumns) const; // Converts the MS to make the specified set of subtables memory resident. void setMemoryResidentSubtables (const MrsEligibility & mrsEligibility); // Return the name of each of the subtables. This should be used by the // filler to create the subtables in the correct location. // String antennaTableName() const; String dataDescriptionTableName() const; String dopplerTableName() const; String feedTableName() const; String fieldTableName() const; String flagCmdTableName() const; String freqOffsetTableName() const; String historyTableName() const; String observationTableName() const; String pointingTableName() const; String polarizationTableName() const; String processorTableName() const; String sourceTableName() const; String spectralWindowTableName() const; String stateTableName() const; String sysCalTableName() const; String weatherTableName() const; // // Access functions for the subtables, using the MS-like interface for each // MSAntenna& antenna() {return antenna_p;} MSDataDescription& dataDescription() {return dataDesc_p;} MSDoppler& doppler() {return doppler_p;} MSFeed& feed() {return feed_p;} MSField& field() {return field_p;} MSFlagCmd& flagCmd() {return flagCmd_p;} MSFreqOffset& freqOffset() {return freqOffset_p;} MSHistory& history() {return history_p;} MSObservation& observation() {return observation_p;} MSPointing& pointing() {return pointing_p;} MSPolarization& polarization() {return polarization_p;} MSProcessor& processor() {return processor_p;} MSSource& source() {return source_p;} MSSpectralWindow& spectralWindow() {return spectralWindow_p;} MSState& state() {return state_p;} MSSysCal& sysCal() {return sysCal_p;} MSWeather& weather() {return weather_p;} const MSAntenna& antenna() const {return antenna_p;} const MSDataDescription& dataDescription() const {return dataDesc_p;} const MSDoppler& doppler() const {return doppler_p;} const MSFeed& feed() const {return feed_p;} const MSField& field() const {return field_p;} const MSFlagCmd& flagCmd() const {return flagCmd_p;} const MSFreqOffset& freqOffset() const {return freqOffset_p;} const MSHistory& history() const {return history_p;} const MSObservation& observation() const {return observation_p;} const MSPointing& pointing() const {return pointing_p;} const MSPolarization& polarization() const {return polarization_p;} const MSProcessor& processor() const {return processor_p;} const MSSource& source() const {return source_p;} const MSSpectralWindow& spectralWindow() const {return spectralWindow_p;} const MSState& state() const {return state_p;} const MSSysCal& sysCal() const {return sysCal_p;} const MSWeather& weather() const {return weather_p;} // MrsEligibility getMrsEligibility () const; // Initialize the references to the subtables. You need to call // this only if you assign new subtables to the table keywords. // This also checks for validity of the table and its subtables. // Set clear to True to clear the subtable references (used in assignment) void initRefs(Bool clear=False); // Create default subtables: fills the required subtable keywords with // tables of the correct type, mainly for testing and as an example of // how to do this for specific fillers. In practice these tables will // often have more things specified, like dimensions of arrays and // storage managers for the various columns. void createDefaultSubtables(Table::TableOption option=Table::Scratch); #ifdef HAVE_MPI void createDefaultSubtables(MPI_Comm comm, Table::TableOption option=Table::Scratch); #endif // HAVE_MPI // Initialize the statics appropriately. This does not need to be // called by users, it is called by the implementation class // MSTableImpl. static MSTableMaps initMaps(); // Create DATA column from existing FLOAT_DATA column. Noop if DATA already // exists or neither exists (returns False in that case). Bool makeComplexData(); // Validate Measure references - check that all Measure columns have their // reference value set, report the ones that don't. Bool validateMeasureRefs(); // Flush all the tables and subtables associated with this // MeasurementSet. This function calls the Table::flush() function on the // main table and all the standard subtables including optional // subtables. See the Table class for a description of the sync argument. void flush(Bool sync=False); // Return a record of the indices that the msselection selection selected Record msseltoindex(const String& spw="", const String& field="", const String& baseline="", const String& time="", const String& scan="", const String& uvrange="", const String& observation="", const String& poln="", const String& taql=""); protected: // Clears all of the subtable components of this object (i.e., set to // value of subtable's default constructor). void clearSubtables (); // Assigns one subtable to another if the original subtable (otherSubtable) // is not null and is also memory resident void copySubtable (const Table & otherSubtable, Table & subTable); // Copies (assigns) all of the non-null subtables from the other MS into this one. void copySubtables (const MeasurementSet & other); // Returns true if the named subtable is eligible for memory residency. Bool isEligibleForMemoryResidency (const String & subtableName) const; // Opens all of the eligible subtables in memory resident form void openMrSubtables (); // The top level name for MRS related CASARC settings static String getMrsAipsRcBase () { return "MemoryResidentSubtables"; } private: // temporary function to add the CATEGORY keyword to the FLAG_CATEGORY // column if it isn't there yet. 2000/08/22 // remove this and the calls next MS update void addCat(); // check that the MS is the latest version (2.0) void checkVersion(); // Creates subtables using an explicit MPI communicator (if MPI support // is enabled) template void createDefaultSubtables_impl(Table::TableOption option, T comm); // Opens a single subtable as memory resident (if permitted). template void openMrSubtable (Subtable & subtable, const String & subtableName); // Opens a single subtable if not present in MS object but defined in on-disk MS template void openSubtable (Subtable & subtable, const String & subtableName, Bool useLock); // keep references to the subtables MSAntenna antenna_p; MSDataDescription dataDesc_p; MSDoppler doppler_p; //optional MSFeed feed_p; MSField field_p; MSFlagCmd flagCmd_p; MSFreqOffset freqOffset_p; //optional MSHistory history_p; MSObservation observation_p; MSPointing pointing_p; MSPolarization polarization_p; MSProcessor processor_p; MSSource source_p; //optional MSSpectralWindow spectralWindow_p; MSState state_p; MSSysCal sysCal_p; //optional MSWeather weather_p; //optional bool doNotLockSubtables_p; // used to prevent subtable locking to allow parallel interprocess sharing int mrsDebugLevel_p; // logging level currently enabled Bool hasBeenDestroyed_p; // required by the need to throw an exception in the destructor TableLock mainLock_p; Bool memoryResidentSubtables_p; // true if memory resident subtables are enabled MrsEligibility mrsEligibility_p; // subtables which can be made memory resident }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/StokesConverter.cc000066400000000000000000000345001476623553700225400ustar00rootroot00000000000000//# StokesConverter.cc: convert polarizations from one frame to another //# Copyright (C) 1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // make a portable float to float sqrt for use in Array::apply extern "C" { static float floatsqrt(float val) {return sqrt(val);} } StokesConverter::StokesConverter() {} StokesConverter::~StokesConverter() {} StokesConverter::StokesConverter(const Vector& out, const Vector& in, Bool rescale) { setConversion(out,in,rescale); } StokesConverter::StokesConverter(const StokesConverter& other) { operator=(other); } StokesConverter& StokesConverter::operator=(const StokesConverter& other) { if (this!=&other) { setConversion(other.out_p,other.in_p,other.rescale_p); } return *this; } void StokesConverter::setConversion(const Vector& out, const Vector& in, Bool rescale) { rescale_p=rescale; doIQUV_p=False; initConvMatrix(); Int nIn=in.nelements(); Int nOut=out.nelements(); out_p.resize(nOut); out_p=out; in_p.resize(nIn); in_p=in; conv_p.resize(nOut,nIn); flagConv_p.resize(nOut,nIn); wtConv_p.resize(nOut,nIn); // Set up the fudge factors for crosscorrelation data that has been // scaled to the level of Stokes I. Vector factor(Stokes::YL+1,1.0); if (rescale) { for (uInt i=Stokes::RR; i<=Stokes::YY; i++) factor(i)=0.5; for (uInt i=Stokes::RX; i<=Stokes::YL; i++) factor(i)=sqrt(2.0)/4.0; } // analyze the input - all inputs have to be in the same frame Bool linear=False, circular=False, iquv=False, circlin=False, lincirc=False; Int count=0; for (Int i=0; i=Stokes::I && in(i) <=Stokes::V) { if (!iquv) count++; iquv=True; } if (in(i)>=Stokes::XX && in(i) <=Stokes::YY) { if (!linear) count++; linear=True; } if (in(i)>=Stokes::RR && in(i) <=Stokes::LL) { if (!circular) count++; circular=True; } if (in(i)>=Stokes::RX && in(i) <=Stokes::LY) { if (!circlin) count++; circlin=True; } if (in(i)>=Stokes::XR && in(i) <=Stokes::YL) { if (!lincirc) count++; lincirc=True; } } if (count==0) { throw(AipsError("StokesConverter::setConversion - input polarization" " frame not supported")); } if (count>1) { throw(AipsError("StokesConverter::setConversion - input polarizations" " cannot be in a mixture of frames")); } // set up the conversion matrix for (Int i=0; i0 && out(i)<=Stokes::YL) { for (Int j=0; j=Stokes::Ptotal && out(i)<=Stokes::Pangle) { if (!doIQUV_p) { doIQUV_p=True; iquvConv_p.resize(4,nIn); for (Int j=0; j Slinear(Slin); SquareMatrix h(H),hconj; h*=(1/sqrt(2.0)); hconj=h; hconj.conj(); SquareMatrix Scirc; directProduct(Scirc,h,hconj); Scirc*=Slinear; SquareMatrix Slincirc; SquareMatrix I2; directProduct(Slincirc,I2,hconj); Slincirc*=Slinear; SquareMatrix Scirclin; directProduct(Scirclin,h,I2); Scirclin*=Slinear; polConv_p.resize(20,20); polConv_p.set(0.0); polConv_p.diagonal().set(1.0); polConv_p(Slice(4,4),Slice(0,4))=Scirc.matrix(); polConv_p(Slice(8,4),Slice(0,4))=Slinear.matrix(); polConv_p(Slice(12,4),Slice(0,4))=Scirclin.matrix(); polConv_p(Slice(16,4),Slice(0,4))=Slincirc.matrix(); polConv_p(Slice(0,4),Slice(4,4))=Scirc.inverse().matrix(); polConv_p(Slice(0,4),Slice(8,4))=Slinear.inverse().matrix(); polConv_p(Slice(0,4),Slice(12,4))=Scirclin.inverse().matrix(); polConv_p(Slice(0,4),Slice(16,4))=Slincirc.inverse().matrix(); SquareMatrix tmp; // circ -> lin tmp=Slinear; tmp*=Scirc.inverse(); polConv_p(Slice(8,4),Slice(4,4))=tmp.matrix(); // circ -> circlin tmp=Scirclin; tmp*=Scirc.inverse(); polConv_p(Slice(12,4),Slice(4,4))=tmp.matrix(); // circ -> lincirc tmp=Slincirc; tmp*=Scirc.inverse(); polConv_p(Slice(16,4),Slice(4,4))=tmp.matrix(); // lin -> circ tmp=Scirc; tmp*=Slinear.inverse(); polConv_p(Slice(4,4),Slice(8,4))=tmp.matrix(); // lin -> circlin tmp=Scirclin; tmp*=Slinear.inverse(); polConv_p(Slice(12,4),Slice(8,4))=tmp.matrix(); // lin -> lincirc tmp=Slincirc; tmp*=Slinear.inverse(); polConv_p(Slice(16,4),Slice(8,4))=tmp.matrix(); // circlin -> circ tmp=Scirc; tmp*=Scirclin.inverse(); polConv_p(Slice(4,4),Slice(12,4))=tmp.matrix(); // circlin -> lin tmp=Slinear; tmp*=Scirclin.inverse(); polConv_p(Slice(8,4),Slice(12,4))=tmp.matrix(); // circlin -> lincirc tmp=Slincirc; tmp*=Scirclin.inverse(); polConv_p(Slice(16,4),Slice(12,4))=tmp.matrix(); // lincirc -> circ tmp=Scirc; tmp*=Slincirc.inverse(); polConv_p(Slice(4,4),Slice(16,4))=tmp.matrix(); // lincirc -> lin tmp=Slinear; tmp*=Slincirc.inverse(); polConv_p(Slice(8,4),Slice(16,4))=tmp.matrix(); // lincirc -> circlin tmp=Scirclin; tmp*=Slincirc.inverse(); polConv_p(Slice(12,4),Slice(16,4))=tmp.matrix(); // remove roundoff for (Int i=0; i<20; i++) { for (Int j=0; j<20; j++) { if (nearAbs(polConv_p(i,j),Complex(0.,0.),1.e-4)) polConv_p(i,j)=Complex(0.,0.); if (nearAbs(polConv_p(i,j),Complex(1.,0.),1.e-4)) polConv_p(i,j)=Complex(1.,0.); if (nearAbs(polConv_p(i,j),Complex(-1.,0.),1.e-4)) polConv_p(i,j)=Complex(-1.,0.); if (nearAbs(polConv_p(i,j),Complex(0.,1.),1.e-4)) polConv_p(i,j)=Complex(0.,1.); if (nearAbs(polConv_p(i,j),Complex(0.,-1.),1.e-4)) polConv_p(i,j)=Complex(0.,-1.); if (nearAbs(polConv_p(i,j),Complex(0.5,0.),1.e-4)) polConv_p(i,j)=Complex(0.5,0.); if (nearAbs(polConv_p(i,j),Complex(-0.5,0.),1.e-4)) polConv_p(i,j)=Complex(-0.5,0.); if (nearAbs(polConv_p(i,j),Complex(0.,0.5),1.e-4)) polConv_p(i,j)=Complex(0.,0.5); if (nearAbs(polConv_p(i,j),Complex(0.,-0.5),1.e-4)) polConv_p(i,j)=Complex(0.,-0.5); } } } void StokesConverter::convert(Array& out, const Array& in) const { IPosition outShape(in.shape()); outShape(0)=out_p.nelements(); Int nDim=in.ndim(); out.resize(outShape); Int nCorrIn=in.shape()(0); DebugAssert(nCorrIn==Int(in_p.nelements()),AipsError); Matrix inMat=in.reform(IPosition(2,nCorrIn,in.nelements()/nCorrIn)); Matrix outMat=out.reform(IPosition(2,outShape(0), out.nelements()/outShape(0))); IPosition iquvShape(outMat.shape()); iquvShape(0)=4; Matrix iquv; if (doIQUV_p) iquv.resize(iquvShape); IPosition outStart(nDim,0),outEnd(outShape-1); for (uInt i=0; i= Stokes::Ptotal && pol<= Stokes::Pangle) { // first convert to IQUV for (Int j=0; j<4; j++) { iquv(Slice(j,1),Slice())=product(iquvConv_p(Slice(j,1),Slice()),inMat); } // now calculate required parameter // todo: there are some possible large temporaries to be optimized here switch (pol) { case Stokes::Ptotal: case Stokes::PFtotal: { Array tmp; Vector outf; for (Int j=1; j<=3; j++) { tmp=iquv.row(j); tmp*=conj(tmp); if (j==1) outf=real(tmp); else outf+=real(tmp); } outf.apply(floatsqrt); if (pol==Stokes::PFtotal) { outf/=amplitude(iquv.row(0)); } for (uInt k=0; k tmp; Vector outf; for (Int j=1; j<=2; j++) { tmp=iquv.row(j); tmp*=conj(tmp); if (j==1) outf=real(tmp); else outf+=real(tmp); } outf.apply(floatsqrt); if (pol==Stokes::PFlinear) { outf/=amplitude(iquv.row(0)); } for (uInt k=0; k outf=atan2(real(iquv.row(2)),real(iquv.row(1))); outf/=2.0f; // convertArray(outMat.row(i),outf); // convertArray is broken 1997/10/09, spell it out for (uInt k=0; k& out, const Array& in) const { IPosition outShape(in.shape()); outShape(0)=out_p.nelements(); out.resize(outShape); Int nCorrIn=in.shape()(0); DebugAssert(nCorrIn==Int(in_p.nelements()),AipsError); Matrix inMat=in.reform(IPosition(2,nCorrIn,in.nelements()/nCorrIn)); Matrix outMat=out.reform(IPosition(2,outShape(0), out.nelements()/outShape(0))); for (uInt i=0; i& out, const Array& in, Bool sigma) const { IPosition outShape(in.shape()); outShape(0)=out_p.nelements(); out.resize(outShape); Int nCorrIn=in.shape()(0); DebugAssert(nCorrIn==Int(in_p.nelements()),AipsError); Matrix inMat=in.reform(IPosition(2,nCorrIn,in.nelements()/nCorrIn)); Matrix outMat=out.reform(IPosition(2,outShape(0), out.nelements()/outShape(0))); // change calculation based on sigma: // for weights we use Wout=1/sum(square(factor(k))*1/Win(k)) // for sigmas we use Sout=sqrt(sum(square(factor(k)*Sin(k)))) for (uInt i=0; i& out, const Array& in) const { IPosition outShape(in.shape()); outShape(0)=in_p.nelements(); // use input if provided, else use unflagged array if (out.nelements()==0) { out.resize(outShape); out.set(False); } Int nCorrIn=in.shape()(0); DebugAssert(out.shape()==outShape,AipsError); DebugAssert(nCorrIn==Int(out_p.nelements()),AipsError); Matrix inMat=in.reform(IPosition(2,nCorrIn,in.nelements()/nCorrIn)); Matrix outMat=out.reform(IPosition(2,outShape(0), out.nelements()/outShape(0))); Matrix first(outMat.shape(),True); // flag or unflag all data depending on the input. // output is flagged if any input is flagged, unflagged if all input unflagged // output is unchanged if independent of inputs. for (Int i=0; i #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // StokesConverter converts any set of polarizations into any other one // // // // // //
      • Stokes // // // // StokesConverter is a class that converts Stokes Parameters // // // // This class is used to convert polarizations from one system to // another. // First the conversion wanted is specified and then large blocks of data // can be converted. // // // // create converter // StokesConverter sc; // Vector out(7),in(4); // // set the input polarizations // in(0)=Stokes::RR; // in(1)=Stokes::LL; // in(2)=Stokes::RL; // in(3)=Stokes::LR; // // set the required output // out(0)=Stokes::I; // out(1)=Stokes::Q; // out(2)=Stokes::U; // out(3)=Stokes::V; // out(4)=Stokes::Ptotal; // out(5)=Stokes::Pangle; // out(6)=Stokes::PFlinear; // // initialize the conversion engine // sc.setConversion(out,in); // // set up some test data // Vector datain(4),dataout(7); // datain(0)=1.0; // datain(1)=0.9; // datain(2)=0.3; // datain(3)=0.2; // // convert the data // sc.convert(dataout,datain); // // // // // // Polarization conversion is needed in various places. It makes sense to // provide all conversion in one place. // // // //
      • //
      • // // // //
      • cope with incomplete input polarizations sensibly //
      • decide what to do about factor 2 between I and RR/LL,XX/YY etc. // class StokesConverter { public: // default constructor, does not set up a conversion StokesConverter(); // Set up a conversion from in to out. // The in and out vectors contain a list of polarization present/wanted // in that order. The in vector should match the data to convert. // (CORR_TYPE column in SPECTRAL_WINDOW table contains this info) // The rescale option will correct for crosscorrelation data that // has been scaled to the level Stokes I (common practice // in radioastronomy: even though officially I=XX+YY, in practice // we need to do I=(XX+YY)/2, set rescale to True to do the latter). StokesConverter(const Vector& out, const Vector& in, Bool rescale=False); // desctructor ~StokesConverter(); // Copy constructor StokesConverter(const StokesConverter& other); // Assignment, StokesConverter& operator=(const StokesConverter& other); // Change or Set the conversion. Arguments are the same as for // constructor above. void setConversion(const Vector& out, const Vector& in, Bool rescale = False); // convert data, first dimension of input must match // that of the input conversion vector used to set up the conversion. // Output is resized as needed. void convert(Array& out, const Array& in) const; // convert flags, first dimension of input must match // that of the input conversion vector used to set up the conversion. // Output is resized as needed. All output depending on a flagged input // will be flagged. void convert(Array& out, const Array& in) const; // convert weights, first dimension of input must match // that of the input conversion vector used to set up the conversion. // Output is resized as needed. // Set sigma to True when converting sigma's using this routine. void convert(Array& out, const Array& in, Bool sigma=False) const; // invert flags, first dimension of input must match // that of the output conversion vector used to set up the conversion. // Output is resized as needed. All output depending on a flagged input // will be flagged. This does the inverse operation of convert, allowing // flagging of converted data to be transferred back to the original data. void invert(Array& out, const Array& in) const; protected: // initialize the polarization conversion matrix void initConvMatrix(); private: Vector in_p,out_p; Bool rescale_p; //# mutable because operator Matrix(Slice,Slice) doesn't have const version mutable Matrix conv_p; mutable Matrix iquvConv_p; Bool doIQUV_p; Matrix flagConv_p; Matrix wtConv_p; Matrix polConv_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/ms/MeasurementSets/test/000077500000000000000000000000001476623553700200465ustar00rootroot00000000000000casacore-3.7.1/ms/MeasurementSets/test/CMakeLists.txt000066400000000000000000000011771476623553700226140ustar00rootroot00000000000000#foreach (files #antenna.params #feed.params #field.params #obs.params #range-obs.params #selector-obs.params #spw.params #) # configure_file (${CMAKE_CURRENT_SOURCE_DIR}/${files} ${CMAKE_CURRENT_BINARY_DIR}#/${files} COPYONLY) #endforeach (files) set (tests tMeasurementSet tMSColumns tMSDataDescBuffer tMSFieldBuffer tMSFieldEphem tMSIter tMSMainBuffer tMSPolBuffer tStokesConverter ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_ms) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/ms/MeasurementSets/test/antenna.params000066400000000000000000000014541476623553700227030ustar00rootroot00000000000000! file format aips++-short ! # antennas 6 ! array x y z position - 6D array of CA 0.0 0.0 0.0 ! ant-id x y z dish-diam mount name (offset station orbit phased-arr) ! ant #0 0 -4752376.3120 2790426.8030 -3200483.7640 22.0 alt-az CA01 ! 0.0 0.0 0.0 STN5 -1 -1 ! ant #1 1 -4752189.8710 2790743.3260 -3200483.7610 22.0 alt-az CA02 ! 0.0 0.0 0.0 STN10 -1 -1 ! ant #2 2 -4751785.9060 2791429.1030 -3200483.7610 22.0 alt-az CA03 ! 0.0 0.0 0.0 STN13 -1 -1 ! ant #3 3 -4751133.3580 2792536.9100 -3200483.7340 22.0 alt-az CA04 ! 0.0 0.0 0.0 STN28 -1 -1 ! ant #4 4 -4751094.5060 2792602.8460 -3200483.7540 22.0 alt-az CA05 ! 0.0 0.0 0.0 STN30 -1 -1 ! ant #5 5 -4749393.1960 2795491.0520 -3200483.7000 22.0 alt-az CA06 ! 0.0 0.0 0.0 STN37 -1 -1 casacore-3.7.1/ms/MeasurementSets/test/feed.params000066400000000000000000000006471476623553700221650ustar00rootroot00000000000000! # entries 2 ! ant-id feed-id spw-id beam-id num-receptors ! beam-offset pol-type position receptor-angle ! entry 1 - ant=-1 -> use this for all antennas -1 0 0 0 2 0.0 0.0 0.0 0.0 X Y 0.0 0.0 0.0 0.0 ! entry 2 - ant=-1 -> use this for all antennas -1 0 1 0 2 0.0 0.0 0.0 0.0 X Y 0.0 0.0 0.0 0.0 ! entry 3 - ant=-1 -> use this for all antennas -1 0 2 0 2 0.0 0.0 0.0 0.0 X Y 0.0 0.0 0.0 0.0 casacore-3.7.1/ms/MeasurementSets/test/field.params000066400000000000000000000002461476623553700223400ustar00rootroot00000000000000! # sources 2 ! name, ra,dec, ! mosx,mosy, mosspacing (0=>FHWM/2), #integrations/pointing ! source 1 src1 120. 45. 1 1 0.0 6 ! source 2 cal 121.0 46.0 1 1 0.0 2 casacore-3.7.1/ms/MeasurementSets/test/obs.params000066400000000000000000000001111476623553700220270ustar00rootroot00000000000000field.params spw.params antenna.params feed.params 10. 0.0 10000.0 100.0 casacore-3.7.1/ms/MeasurementSets/test/range-obs.params000066400000000000000000000001121476623553700231220ustar00rootroot00000000000000field.params spw.params antenna.params feed.params 10. 0.0 43200.0 7200.0 casacore-3.7.1/ms/MeasurementSets/test/selector-obs.params000066400000000000000000000001121476623553700236460ustar00rootroot00000000000000field.params spw.params antenna.params feed.params 10. 0.0 43200.0 7200.0 casacore-3.7.1/ms/MeasurementSets/test/spw.params000066400000000000000000000003751476623553700220710ustar00rootroot00000000000000! # spectral windows 3 ! nchan, start-freq, freq-inc, freq-resolution, #integrations/spw ! ncorr, (corr_type)*ncorr ! spw #1 8 1384.e6 4.e6 6.5e6 8 4 XX XY YX YY ! spw #2 8 2496.e6 4.e6 6.5e6 16 4 XX XY YX YY ! spw #3 16 1420.e6 2.e6 3.0e6 8 2 XX YY casacore-3.7.1/ms/MeasurementSets/test/tMSColumns.cc000066400000000000000000000211201476623553700224150ustar00rootroot00000000000000//# tMSColumns.cc : this program tests the MSColumns classes //# Copyright (C) 1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include int main() { try { { // create a MeasurementSet with all predefined columns TableDesc td; for (uInt i = 1; i < MS::NUMBER_PREDEFINED_COLUMNS; i++) { MS::addColumnToDesc(td, MS::PredefinedColumns(i)); } td.rwKeywordSet().define("MS_VERSION", Float(2.0)); SetupNewTable newtab("tMSColumns_table.ms",td,Table::New); MeasurementSet ms(newtab,1); // now add all subtables, each with all predefined columns TableDesc tdAntenna; for (uInt i = 1 ; i<=MSAntenna::NUMBER_PREDEFINED_COLUMNS; i++) { MSAntenna::addColumnToDesc(tdAntenna, MSAntenna::PredefinedColumns(i)); } SetupNewTable antennaSetup(ms.antennaTableName(),tdAntenna,Table::New); ms.rwKeywordSet().defineTable(MS::keywordName(MS::ANTENNA),Table(antennaSetup)); TableDesc tddataDescription; for (uInt i = 1 ; i<=MSDataDescription::NUMBER_PREDEFINED_COLUMNS; i++) { MSDataDescription::addColumnToDesc(tddataDescription, MSDataDescription::PredefinedColumns(i)); } SetupNewTable dataDescriptionSetup(ms.dataDescriptionTableName(),tddataDescription,Table::New); ms.rwKeywordSet().defineTable(MS::keywordName(MS::DATA_DESCRIPTION), Table(dataDescriptionSetup)); TableDesc tdDoppler; for (uInt i = 1 ; i<=MSDoppler::NUMBER_PREDEFINED_COLUMNS; i++) { MSDoppler::addColumnToDesc(tdDoppler, MSDoppler::PredefinedColumns(i)); } SetupNewTable dopplerSetup(ms.dopplerTableName(),tdDoppler,Table::New); ms.rwKeywordSet().defineTable(MS::keywordName(MS::DOPPLER),Table(dopplerSetup)); TableDesc tdFeed; for (uInt i = 1 ; i<=MSFeed::NUMBER_PREDEFINED_COLUMNS; i++) { MSFeed::addColumnToDesc(tdFeed, MSFeed::PredefinedColumns(i)); } SetupNewTable feedSetup(ms.feedTableName(),tdFeed,Table::New); ms.rwKeywordSet().defineTable(MS::keywordName(MS::FEED),Table(feedSetup)); TableDesc tdField; for (uInt i = 1 ; i<=MSField::NUMBER_PREDEFINED_COLUMNS; i++) { MSField::addColumnToDesc(tdField, MSField::PredefinedColumns(i)); } SetupNewTable fieldSetup(ms.fieldTableName(),tdField,Table::New); ms.rwKeywordSet().defineTable(MS::keywordName(MS::FIELD),Table(fieldSetup)); TableDesc tdFlagCmd; for (uInt i = 1 ; i<=MSFlagCmd::NUMBER_PREDEFINED_COLUMNS; i++) { MSFlagCmd::addColumnToDesc(tdFlagCmd, MSFlagCmd::PredefinedColumns(i)); } SetupNewTable flagCmdSetup(ms.flagCmdTableName(),tdFlagCmd,Table::New); ms.rwKeywordSet().defineTable(MS::keywordName(MS::FLAG_CMD),Table(flagCmdSetup)); TableDesc tdFreqOffset; for (uInt i = 1 ; i<=MSFreqOffset::NUMBER_PREDEFINED_COLUMNS; i++) { MSFreqOffset::addColumnToDesc(tdFreqOffset, MSFreqOffset::PredefinedColumns(i)); } SetupNewTable freqOffsetSetup(ms.freqOffsetTableName(),tdFreqOffset,Table::New); ms.rwKeywordSet().defineTable(MS::keywordName(MS::FREQ_OFFSET),Table(freqOffsetSetup)); TableDesc tdHistory; for (uInt i = 1 ; i<=MSHistory::NUMBER_PREDEFINED_COLUMNS; i++) { MSHistory::addColumnToDesc(tdHistory, MSHistory::PredefinedColumns(i)); } SetupNewTable historySetup(ms.historyTableName(),tdHistory,Table::New); ms.rwKeywordSet().defineTable(MS::keywordName(MS::HISTORY),Table(historySetup)); TableDesc tdObservation; for (uInt i = 1 ; i<=MSObservation::NUMBER_PREDEFINED_COLUMNS; i++) { MSObservation::addColumnToDesc(tdObservation, MSObservation::PredefinedColumns(i)); } SetupNewTable observationSetup(ms.observationTableName(),tdObservation,Table::New); ms.rwKeywordSet().defineTable(MS::keywordName(MS::OBSERVATION),Table(observationSetup)); TableDesc tdPointing; for (uInt i = 1 ; i<=MSPointing::NUMBER_PREDEFINED_COLUMNS; i++) { MSPointing::addColumnToDesc(tdPointing, MSPointing::PredefinedColumns(i)); } SetupNewTable pointingSetup(ms.pointingTableName(),tdPointing,Table::New); ms.rwKeywordSet().defineTable(MS::keywordName(MS::POINTING),Table(pointingSetup)); TableDesc tdPolarization; for (uInt i = 1 ; i<=MSPolarization::NUMBER_PREDEFINED_COLUMNS; i++) { MSPolarization::addColumnToDesc(tdPolarization, MSPolarization::PredefinedColumns(i)); } SetupNewTable polarizationSetup(ms.polarizationTableName(),tdPolarization,Table::New); ms.rwKeywordSet().defineTable(MS::keywordName(MS::POLARIZATION),Table(polarizationSetup)); TableDesc tdProcessor; for (uInt i = 1 ; i<=MSProcessor::NUMBER_PREDEFINED_COLUMNS; i++) { MSProcessor::addColumnToDesc(tdProcessor, MSProcessor::PredefinedColumns(i)); } SetupNewTable processorSetup(ms.processorTableName(),tdProcessor,Table::New); ms.rwKeywordSet().defineTable(MS::keywordName(MS::PROCESSOR),Table(processorSetup)); TableDesc tdSource; for (uInt i = 1 ; i<=MSSource::NUMBER_PREDEFINED_COLUMNS; i++) { MSSource::addColumnToDesc(tdSource, MSSource::PredefinedColumns(i)); } SetupNewTable sourceSetup(ms.sourceTableName(),tdSource,Table::New); ms.rwKeywordSet().defineTable(MS::keywordName(MS::SOURCE),Table(sourceSetup)); TableDesc tdSpectralWindow; for (uInt i = 1 ; i<=MSSpectralWindow::NUMBER_PREDEFINED_COLUMNS; i++) { MSSpectralWindow::addColumnToDesc(tdSpectralWindow, MSSpectralWindow::PredefinedColumns(i)); } SetupNewTable spectralWindowSetup(ms.spectralWindowTableName(),tdSpectralWindow,Table::New); ms.rwKeywordSet().defineTable(MS::keywordName(MS::SPECTRAL_WINDOW),Table(spectralWindowSetup)); TableDesc tdState; for (uInt i = 1 ; i<=MSState::NUMBER_PREDEFINED_COLUMNS; i++) { MSState::addColumnToDesc(tdState, MSState::PredefinedColumns(i)); } SetupNewTable stateSetup(ms.stateTableName(),tdState,Table::New); ms.rwKeywordSet().defineTable(MS::keywordName(MS::STATE),Table(stateSetup)); TableDesc tdSysCal; for (uInt i = 1 ; i<=MSSysCal::NUMBER_PREDEFINED_COLUMNS; i++) { MSSysCal::addColumnToDesc(tdSysCal, MSSysCal::PredefinedColumns(i)); } SetupNewTable sysCalSetup(ms.sysCalTableName(),tdSysCal,Table::New); ms.rwKeywordSet().defineTable(MS::keywordName(MS::SYSCAL),Table(sysCalSetup)); TableDesc tdWeather; for (uInt i = 1 ; i<=MSWeather::NUMBER_PREDEFINED_COLUMNS; i++) { MSWeather::addColumnToDesc(tdWeather, MSWeather::PredefinedColumns(i)); } SetupNewTable weatherSetup(ms.weatherTableName(),tdWeather,Table::New); ms.rwKeywordSet().defineTable(MS::keywordName(MS::WEATHER),Table(weatherSetup)); // intialize the references to the subtables just added ms.initRefs(); } // write the MS to disk // now create the MSColumns and MSColumns objects, testing all // declarations and column definitions for type consistency { MeasurementSet ms("tMSColumns_table.ms",Table::Old); MSColumns romsc(ms); } { MeasurementSet ms("tMSColumns_table.ms",Table::Update); MSColumns msc(ms); // remove the table ms.markForDelete(); } return 0; } catch (std::exception& x) { cerr << x.what() < #include #include #include #include #include #include #include #include #include void putData(MSDataDescColumns& cols) { // test the spectralWindowId functions. cols.spectralWindowId().put(0, 0); cols.spectralWindowId().put(4, 1); // test the polarizationId functions. cols.polarizationId().put(0, 1); cols.polarizationId().put(4, 3); // test the flagRow functions. cols.flagRow().put(0, False); cols.flagRow().put(4, True); } void getData(const MSDataDescColumns& cols) { // test the spectralWindowId functions. AlwaysAssert(cols.spectralWindowId()(0) == 0, AipsError); AlwaysAssert(cols.spectralWindowId()(4) == 1, AipsError); // test the polarizationId functions. AlwaysAssert(cols.polarizationId()(0) == 1, AipsError); AlwaysAssert(cols.polarizationId()(4) == 3, AipsError); // test the flagRow functions. AlwaysAssert(cols.flagRow()(0) == False, AipsError); AlwaysAssert(cols.flagRow()(4) == True, AipsError); // Check the optional columns do not exist AlwaysAssert(cols.lagId().isNull() == True, AipsError); } int main() { try { const String filename = "tMSDataDescColumns_tmp.table"; { // Check the RW class SetupNewTable setup(filename, MSDataDescription::requiredTableDesc(), Table::New); MSDataDescription table(setup, 5); // Check the constructor MSDataDescColumns cols(table); // test the nrow function. AlwaysAssert(cols.nrow() == 5, AipsError); // Put data into the table putData(cols); // Check the data is still there getData(cols); } // Close the table {// Check the RO class const MSDataDescription table(filename, Table::Old); // Check the constructor const MSDataDescColumns cols(table); // Check the data is still there getData(cols); } {// Delete the table MSDataDescription table(filename, Table::Old); table.markForDelete(); } } catch (std::exception& x) { cerr << x.what() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; } // Local Variables: // compile-command: "gmake OPTLIB=1 XLIBLIST=0 tMSDataDescColumns" // End: casacore-3.7.1/ms/MeasurementSets/test/tMSFieldBuffer.cc000066400000000000000000000246541476623553700231710ustar00rootroot00000000000000//# tMSFieldBuffer.cc: //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include // #include // #include // #include // #include // #include // #include // #include // #include // #include // #include // #include // #include // #include int main() { // const String filename = "tMSFieldBuffer_tmp.table"; // try { // // Check the default constructor // MSFieldBuffer newBuffer; // // test the ok function. // AlwaysAssert(newBuffer.ok(), AipsError); // // test the addRow & nrow functions. // AlwaysAssert(newBuffer.nrow() == 0, AipsError); // newBuffer.addRow(20); // AlwaysAssert(newBuffer.ok(), AipsError); // AlwaysAssert(newBuffer.nrow() == 20, AipsError); // { // MSFieldBuffer fieldBuffer; // { // test the addRow & nrow functions. // AlwaysAssert(fieldBuffer.nrow() == 0, AipsError); // fieldBuffer.addRow(1); // AlwaysAssert(fieldBuffer.nrow() == 1, AipsError); // fieldBuffer.addRow(4, 1); // AlwaysAssert(fieldBuffer.nrow() == 5, AipsError); // } // { // test the name functions. // AlwaysAssert(fieldBuffer.name()(0) == String(""), AipsError); // AlwaysAssert(fieldBuffer.name()(4) == String(""), AipsError); // fieldBuffer.name().put(0, "row 0"); // fieldBuffer.name().put(4, "row 4"); // } // { // test the code functions. // AlwaysAssert(fieldBuffer.code()(0) == String(""), AipsError); // AlwaysAssert(fieldBuffer.code()(4) == String(""), AipsError); // fieldBuffer.code().put(0, "code 0"); // fieldBuffer.code().put(4, "code 4"); // } // { // test the numPoly functions. // AlwaysAssert(fieldBuffer.numPoly()(0) == 0, AipsError); // AlwaysAssert(fieldBuffer.numPoly()(4) == 1, AipsError); // } // { // test the time functions. // AlwaysAssert(near(fieldBuffer.time()(0), 0.0), AipsError); // AlwaysAssert(near(fieldBuffer.time()(4), 0.0), AipsError); // AlwaysAssert(fieldBuffer.timeMeas().getMeasRef().getType() == // MEpoch::UTC, AipsError); // fieldBuffer.time().put(0, 1.0); // fieldBuffer.time().put(4, 2.0); // } // { // test the delayDir & delayFrame functions. // AlwaysAssert(fieldBuffer.delayDir()(0).shape() == IPosition(2, 2, 1), // AipsError); // AlwaysAssert(allNear(fieldBuffer.delayDir()(0), 0.0, C::dbl_epsilon), // AipsError); // AlwaysAssert(fieldBuffer.delayDir()(4).shape() == IPosition(2, 2, 2), // AipsError); // AlwaysAssert(allNear(fieldBuffer.delayDir()(4), 0.0, C::dbl_epsilon), // AipsError); // AlwaysAssert(fieldBuffer.delayDirMeas(4, 0.0).getRef().getType() // == MDirection::J2000, AipsError); // fieldBuffer.delayDir().put(0, Matrix(2, 1, 1.0)); // fieldBuffer.delayDir().put(4, Matrix(2, 2, 2.0)); // } // { // test the phaseDir & phaseFrame functions. // AlwaysAssert(fieldBuffer.phaseDir()(0).shape() == IPosition(2, 2, 1), // AipsError); // AlwaysAssert(allNear(fieldBuffer.phaseDir()(0), 0.0, C::dbl_epsilon), // AipsError); // AlwaysAssert(fieldBuffer.phaseDir()(4).shape() == IPosition(2, 2, 2), // AipsError); // AlwaysAssert(allNear(fieldBuffer.phaseDir()(4), 0.0, C::dbl_epsilon), // AipsError); // AlwaysAssert(fieldBuffer.phaseDirMeas(4, 0.0).getRef().getType() // == MDirection::J2000, AipsError); // fieldBuffer.phaseDir().put(0, Matrix(2, 1, 20.0)); // fieldBuffer.phaseDir().put(4, Matrix(2, 2, 30.0)); // } // { // test the referenceDir & referenceFrame functions. // AlwaysAssert(fieldBuffer.referenceDir()(0).shape() == // IPosition(2, 2, 1), AipsError); // AlwaysAssert(allNear(fieldBuffer.referenceDir()(0), 0.0, // C::dbl_epsilon), AipsError); // AlwaysAssert(fieldBuffer.referenceDir()(4).shape() == // IPosition(2, 2, 2), AipsError); // AlwaysAssert(allNear(fieldBuffer.referenceDir()(4), 0.0, // C::dbl_epsilon), AipsError); // AlwaysAssert(fieldBuffer.referenceDirMeas(4, 0.0).getRef().getType() // == MDirection::J2000, AipsError); // fieldBuffer.referenceDir().put(0, Matrix(2, 1, 10.0)); // fieldBuffer.referenceDir().put(4, Matrix(2, 2, 15.0)); // } // { // test the sourceID functions. // AlwaysAssert(fieldBuffer.sourceId()(0) == -1, AipsError); // AlwaysAssert(fieldBuffer.sourceId()(4) == -1, AipsError); // fieldBuffer.sourceId().put(0, 10); // fieldBuffer.sourceId().put(4, 20); // } // { // test the flagRow functions. // AlwaysAssert(fieldBuffer.flagRow()(0) == False, AipsError); // AlwaysAssert(fieldBuffer.flagRow()(4) == False, AipsError); // fieldBuffer.flagRow().put(3, True); // } // { // Check the assignment operator & copy constructor // MSFieldBuffer otherBuffer(fieldBuffer); // AlwaysAssert(otherBuffer.ok(), AipsError); // AlwaysAssert(otherBuffer.nrow() == 5, AipsError); // newBuffer = otherBuffer; // AlwaysAssert(newBuffer.ok(), AipsError); // // Check the reference semantics by adding data here and seeing if it // // is mirrored into the newBuffer object. // fieldBuffer.delayDir().put(1, Matrix(2, 2, 1.2)); // fieldBuffer.name().put(1, "name 1"); // fieldBuffer.flagRow().put(1, True); // // Save the buffer to disk // fieldBuffer.save(filename, True); // } // } // { // check the data has not been lost. // AlwaysAssert(newBuffer.nrow() == 5, AipsError); // AlwaysAssert(newBuffer.name()(0) == String("row 0"), AipsError); // AlwaysAssert(newBuffer.name()(4) == String("row 4"), AipsError); // AlwaysAssert(newBuffer.code()(0) == String("code 0"), AipsError); // AlwaysAssert(newBuffer.code()(4) == String("code 4"), AipsError); // AlwaysAssert(newBuffer.numPoly()(0) == 0, AipsError); // AlwaysAssert(newBuffer.numPoly()(4) == 1, AipsError); // AlwaysAssert(near(newBuffer.time()(0), 1.0), AipsError); // AlwaysAssert(near(newBuffer.time()(4), 2.0), AipsError); // AlwaysAssert(newBuffer.delayDir()(0).shape() == IPosition(2, 2, 1), // AipsError); // AlwaysAssert(allNear(newBuffer.delayDir()(0), 1.0, C::dbl_epsilon), // AipsError); // AlwaysAssert(newBuffer.delayDir()(4).shape() == IPosition(2, 2, 2), // AipsError); // AlwaysAssert(allNear(newBuffer.delayDir()(4), 2.0, C::dbl_epsilon), // AipsError); // AlwaysAssert(newBuffer.phaseDir()(0).shape() == IPosition(2, 2, 1), // AipsError); // AlwaysAssert(allNear(newBuffer.phaseDir()(0), 20.0, C::dbl_epsilon), // AipsError); // AlwaysAssert(newBuffer.phaseDir()(4).shape() == IPosition(2, 2, 2), // AipsError); // AlwaysAssert(allNear(newBuffer.phaseDir()(4), 30.0, C::dbl_epsilon), // AipsError); // AlwaysAssert(newBuffer.referenceDir()(0).shape() == IPosition(2, 2, 1), // AipsError); // AlwaysAssert(allNear(newBuffer.referenceDir()(0), 10.0, C::dbl_epsilon), // AipsError); // AlwaysAssert(newBuffer.referenceDir()(4).shape() == IPosition(2, 2, 2), // AipsError); // AlwaysAssert(allNear(newBuffer.referenceDir()(4), 15.0, C::dbl_epsilon), // AipsError); // AlwaysAssert(newBuffer.sourceId()(0) == 10, AipsError); // AlwaysAssert(newBuffer.sourceId()(4) == 20, AipsError); // AlwaysAssert(newBuffer.flagRow()(0) == False, AipsError); // AlwaysAssert(newBuffer.flagRow()(3) == True, AipsError); // AlwaysAssert(newBuffer.flagRow()(4) == False, AipsError); // // check the reference semantics // AlwaysAssert(allNear(newBuffer.delayDir()(1), 1.2, C::dbl_epsilon), // AipsError); // AlwaysAssert(newBuffer.name()(1) == "name 1", AipsError); // AlwaysAssert(newBuffer.flagRow()(1) == True, AipsError); // } // { // Check the isValid functions // AlwaysAssert(newBuffer.isValid(True) == True, AipsError); // AlwaysAssert(newBuffer.isValid(3u) == True, AipsError); // AlwaysAssert(newBuffer.isValid() == True, AipsError); // } // { // Check the match functions // AlwaysAssert(newBuffer.matchRefDir(Matrix(2, 1, 0.0)) == -1, // AipsError); // AlwaysAssert(newBuffer.matchRefDir(Matrix(2, 1, 10.0), False)==0, // AipsError); // AlwaysAssert(newBuffer.matchRefDir(Matrix(2, 2, 0.0)) == 2, // AipsError); // AlwaysAssert(newBuffer.matchRefDir(Matrix(2, 2, 0.0), False)==3, // AipsError); // } // } // catch (std::exception x) { // cerr << x.what() << endl; // cout << "FAIL" << endl; // return 1; // } // try { // // Check that the Table ended up on disk (after the save function). // MSField ms(filename); // ms.markForDelete(); // } // catch (std::exception x) { // cerr << x.what() << endl; // cout << "FAIL" << endl; // return 1; // } // cout << "OK" << endl; return 3; //untested } // Local Variables: // compile-command: "gmake OPTLIB=1 XLIBLIST=0 tMSFieldBuffer" // End: casacore-3.7.1/ms/MeasurementSets/test/tMSFieldBuffer.run000066400000000000000000000000421476623553700233710ustar00rootroot00000000000000#! /bin/sh echo "UNTESTED" exit 3 casacore-3.7.1/ms/MeasurementSets/test/tMSFieldEphem.cc000066400000000000000000000425031476623553700230070ustar00rootroot00000000000000//# tMSFieldEphem.cc : this program tests the ephemeris handling in MSField and MSFieldColumns //# Copyright (C) 1995-1999,2000-2004 //# Associated Universities, Inc. Washington DC, USA //# Copyright by ESO (in the framework of the ALMA collaboration) //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning CASA should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: CASA Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include int main() { try { { // create MeasurementSet with all predefined columns TableDesc td; for (uInt i = 1; i < MS::NUMBER_PREDEFINED_COLUMNS; i++) { MS::addColumnToDesc(td, MS::PredefinedColumns(i)); } td.rwKeywordSet().define("MS_VERSION", Float(2.0)); SetupNewTable newtab("tMSFieldEphem_table.ms",td,Table::New); MeasurementSet ms(newtab,1); // now add all compulsory subtables, each with all predefined columns TableDesc tdAntenna; for (uInt i = 1 ; i<=MSAntenna::NUMBER_PREDEFINED_COLUMNS; i++) { MSAntenna::addColumnToDesc(tdAntenna, MSAntenna::PredefinedColumns(i)); } SetupNewTable antennaSetup(ms.antennaTableName(),tdAntenna,Table::New); ms.rwKeywordSet().defineTable(MS::keywordName(MS::ANTENNA),Table(antennaSetup)); TableDesc tddataDescription; for (uInt i = 1 ; i<=MSDataDescription::NUMBER_PREDEFINED_COLUMNS; i++) { MSDataDescription::addColumnToDesc(tddataDescription, MSDataDescription::PredefinedColumns(i)); } SetupNewTable dataDescriptionSetup(ms.dataDescriptionTableName(),tddataDescription,Table::New); ms.rwKeywordSet().defineTable(MS::keywordName(MS::DATA_DESCRIPTION), Table(dataDescriptionSetup)); TableDesc tdFeed; for (uInt i = 1 ; i<=MSFeed::NUMBER_PREDEFINED_COLUMNS; i++) { MSFeed::addColumnToDesc(tdFeed, MSFeed::PredefinedColumns(i)); } SetupNewTable feedSetup(ms.feedTableName(),tdFeed,Table::New); ms.rwKeywordSet().defineTable(MS::keywordName(MS::FEED),Table(feedSetup)); TableDesc tdField; for (uInt i = 1 ; i<=MSField::NUMBER_PREDEFINED_COLUMNS; i++) { MSField::addColumnToDesc(tdField, MSField::PredefinedColumns(i)); } SetupNewTable fieldSetup(ms.fieldTableName(),tdField,Table::New); ms.rwKeywordSet().defineTable(MS::keywordName(MS::FIELD),Table(fieldSetup)); TableDesc tdFlagCmd; for (uInt i = 1 ; i<=MSFlagCmd::NUMBER_PREDEFINED_COLUMNS; i++) { MSFlagCmd::addColumnToDesc(tdFlagCmd, MSFlagCmd::PredefinedColumns(i)); } SetupNewTable flagCmdSetup(ms.flagCmdTableName(),tdFlagCmd,Table::New); ms.rwKeywordSet().defineTable(MS::keywordName(MS::FLAG_CMD),Table(flagCmdSetup)); TableDesc tdHistory; for (uInt i = 1 ; i<=MSHistory::NUMBER_PREDEFINED_COLUMNS; i++) { MSHistory::addColumnToDesc(tdHistory, MSHistory::PredefinedColumns(i)); } SetupNewTable historySetup(ms.historyTableName(),tdHistory,Table::New); ms.rwKeywordSet().defineTable(MS::keywordName(MS::HISTORY),Table(historySetup)); TableDesc tdObservation; for (uInt i = 1 ; i<=MSObservation::NUMBER_PREDEFINED_COLUMNS; i++) { MSObservation::addColumnToDesc(tdObservation, MSObservation::PredefinedColumns(i)); } SetupNewTable observationSetup(ms.observationTableName(),tdObservation,Table::New); ms.rwKeywordSet().defineTable(MS::keywordName(MS::OBSERVATION),Table(observationSetup)); TableDesc tdPointing; for (uInt i = 1 ; i<=MSPointing::NUMBER_PREDEFINED_COLUMNS; i++) { MSPointing::addColumnToDesc(tdPointing, MSPointing::PredefinedColumns(i)); } SetupNewTable pointingSetup(ms.pointingTableName(),tdPointing,Table::New); ms.rwKeywordSet().defineTable(MS::keywordName(MS::POINTING),Table(pointingSetup)); TableDesc tdPolarization; for (uInt i = 1 ; i<=MSPolarization::NUMBER_PREDEFINED_COLUMNS; i++) { MSPolarization::addColumnToDesc(tdPolarization, MSPolarization::PredefinedColumns(i)); } SetupNewTable polarizationSetup(ms.polarizationTableName(),tdPolarization,Table::New); ms.rwKeywordSet().defineTable(MS::keywordName(MS::POLARIZATION),Table(polarizationSetup)); TableDesc tdProcessor; for (uInt i = 1 ; i<=MSProcessor::NUMBER_PREDEFINED_COLUMNS; i++) { MSProcessor::addColumnToDesc(tdProcessor, MSProcessor::PredefinedColumns(i)); } SetupNewTable processorSetup(ms.processorTableName(),tdProcessor,Table::New); ms.rwKeywordSet().defineTable(MS::keywordName(MS::PROCESSOR),Table(processorSetup)); TableDesc tdSpectralWindow; for (uInt i = 1 ; i<=MSSpectralWindow::NUMBER_PREDEFINED_COLUMNS; i++) { MSSpectralWindow::addColumnToDesc(tdSpectralWindow, MSSpectralWindow::PredefinedColumns(i)); } SetupNewTable spectralWindowSetup(ms.spectralWindowTableName(),tdSpectralWindow,Table::New); ms.rwKeywordSet().defineTable(MS::keywordName(MS::SPECTRAL_WINDOW),Table(spectralWindowSetup)); TableDesc tdState; for (uInt i = 1 ; i<=MSState::NUMBER_PREDEFINED_COLUMNS; i++) { MSState::addColumnToDesc(tdState, MSState::PredefinedColumns(i)); } SetupNewTable stateSetup(ms.stateTableName(),tdState,Table::New); ms.rwKeywordSet().defineTable(MS::keywordName(MS::STATE),Table(stateSetup)); TableDesc tdWeather; for (uInt i = 1 ; i<=MSWeather::NUMBER_PREDEFINED_COLUMNS; i++) { MSWeather::addColumnToDesc(tdWeather, MSWeather::PredefinedColumns(i)); } SetupNewTable weatherSetup(ms.weatherTableName(),tdWeather,Table::New); ms.rwKeywordSet().defineTable(MS::keywordName(MS::WEATHER),Table(weatherSetup)); // intialize the references to the subtables just added ms.initRefs(); } // write the MS to disk ////////////////////////////////////////////////////////////////////// // Test ephemeris table related features of MSField and MSFieldColumns ////////////////////////////////////////////////////////////////////// { MeasurementSet ms("tMSFieldEphem_table.ms",Table::Old); MSFieldColumns msfc(ms.field()); msfc.updateMeasComets(); // use VGEO and VTOP as example ephemerides String tablePathName; { Table x; Table* y=0; MeasIERS::findTab(x, y, " ", " ", "VTOP"); tablePathName = Path(x.tableName()).absoluteName(); // cout << "Found " << tablePathName << endl; } AlwaysAssertExit( ms.field().addEphemeris(0, tablePathName, "Venus") ); String tablePathName2; { Table x2; Table* y2=0; MeasIERS::findTab(x2, y2, " ", " ", "VGEO"); tablePathName2 = Path(x2.tableName()).absoluteName(); // cout << "Found " << tablePathName2 << endl; } // add using non-absolute path { Directory vgeo(tablePathName2); vgeo.copy(Path(".").absoluteName()+"/VGEO", True); } AlwaysAssertExit( ms.field().addEphemeris(1, "./VGEO", "VenusGeo") ); // test overwrite AlwaysAssertExit( ms.field().addEphemeris(0, "./VGEO", "Venus") ); AlwaysAssertExit( ms.field().addEphemeris(0, tablePathName, "Venus") ); // test error handling AlwaysAssertExit( ms.field().addEphemeris(1, "idontexist", "unknown") == False ); // test removal AlwaysAssertExit( ms.field().addEphemeris(2, "./VGEO", "Venus2") ); AlwaysAssertExit( ms.field().removeEphemeris(2) ); Directory vgeo2(Path(".").absoluteName()+"/VGEO"); vgeo2.removeRecursive(); } { MeasurementSet ms("tMSFieldEphem_table.ms",Table::Update); MSFieldColumns msfc(ms.field()); Vector dir(2); dir(0)=0., dir(1)=0.; // add a row with default entries ms.field().addRow(); msfc.delayDir().put(0, dir); msfc.phaseDir().put(0, dir); msfc.referenceDir().put(0, dir); msfc.time().put(0,50802.708334*86400.); // start of the VTOP ephemeris msfc.ephemerisId().put(0,-1); // ephemeris id -1 msfc.updateMeasComets(); { Int row = 0; Double mjds = 50802.75*86400.; MDirection dDir = msfc.delayDirMeas(row, mjds); // cout << "position for row " << row << ", MJD " << mjds/86400. << ": " << dDir.getAngle(Unit("deg")) << endl; MDirection rDir = msfc.referenceDirMeas(row, mjds); MDirection eDir = msfc.ephemerisDirMeas(row, mjds); MVDirection expDir(rDir.getAngle()); AlwaysAssertExit(expDir.separation(MVDirection(eDir.getAngle())) dirb(2); dirb(0)=Quantity(1.,"deg").getValue("rad"), dirb(1)=dirb(0)/2.; // add one row with ephemeris and non-zero offset ms.field().addRow(); msfc.delayDir().put(2, dirb); msfc.phaseDir().put(2, dirb); msfc.referenceDir().put(2, dirb); msfc.time().put(2,50802.708334*86400.); // start of the VTOP ephemeris msfc.ephemerisId().put(2,0); // ephemeris id 0 msfc.updateMeasComets(); { Int row = 2; Double mjds = 50802.75*86400.; MDirection dDir = msfc.delayDirMeas(row, mjds); // cout << "delaydir for row " << row << ", MJD-50802. " << mjds/86400.-50802. << ": " << dDir.getAngle(Unit("deg")) << endl; MDirection pDir = msfc.phaseDirMeas(row, mjds); // cout << "phasedir for row " << row << ", MJD-50802. " << mjds/86400.-50802. << ": " << pDir.getAngle(Unit("deg")) << endl; MDirection rDir = msfc.referenceDirMeas(row, mjds); // cout << "referencedir for row " << row << ", MJD-50802. " << mjds/86400.-50802. << ": " << rDir.getAngle(Unit("deg")) << endl; MDirection eDir = msfc.ephemerisDirMeas(row, mjds); // cout << "ephemerisdir for row " << row << ", MJD-50802. " << mjds/86400.-50802. << ": " << eDir.getAngle(Unit("deg")) << endl; MVDirection original(Quantity(305.6145129, "deg"), Quantity(-19.8873316, "deg")); MDirection unalteredExpected(original, MDirection::TOPO); original.shift(dirb(0), dirb(1), True); MDirection expected(original, MDirection::TOPO); // MDirection expected(Quantity(305.6145129 + dirb(0)*180./3.14159265/cos(-19.88733167*3.1415926/180.), "deg"), // Quantity(-19.88733167+dirb(1)*180./3.14159265, "deg"), // MDirection::TOPO); MVDirection expDir(expected.getAngle()); MVDirection unalteredExpDir(unalteredExpected.getAngle()); // cout << "separation " << expDir.separation(MVDirection(dDir.getAngle()), "deg") << endl; AlwaysAssertExit(expDir.separation(MVDirection(dDir.getAngle())) #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace casacore; using namespace std; void createMS (int nAnt, int nTime, double msinterval) { // Create the MS (with DATA column) and its subtables. TableDesc td (MS::requiredTableDesc()); MS::addColumnToDesc(td, MS::DATA, 2); SetupNewTable newtab("tMSIter_tmp.ms", td, Table::New); MeasurementSet ms(newtab); ms.createDefaultSubtables(Table::New); // Write main table with columns needed for MSIter. Array data(IPosition(2,4,8)); indgen (data); MSColumns mscols(ms); uInt rownr = 0; for (int it=0; it dir(IPosition(2,2,1)); dir.data()[0] = 1.1; dir.data()[1] = 1.5; fieldcols.delayDir().put (0, dir); fieldcols.phaseDir().put (0, dir); fieldcols.name().put (0, "TESTFIELD"); fieldcols.sourceId().put (0, 0); ms.dataDescription().addRow(1); MSDataDescColumns ddcols(ms.dataDescription()); ddcols.spectralWindowId().put (0, 0); ddcols.polarizationId().put (0, 0); ms.spectralWindow().addRow(1); MSSpWindowColumns spwcols(ms.spectralWindow()); Vectorfreqs(8); indgen (freqs, 1e9, 1e6); spwcols.chanFreq().put (0, freqs); ms.polarization().addRow(1); MSPolarizationColumns polcols(ms.polarization()); Vector corrTypes(2, 0); corrTypes[1] = 1; polcols.numCorr().put (0, 4); polcols.corrType().put (0, corrTypes); ms.antenna().addRow(nAnt); MSAntennaColumns antcols(ms.antenna()); Vector pos(3); indgen (pos, 6.4e6, 1e3); for (int i=0; i(IPosition(2,2,2),0.)); feedcols.receptorAngle().put (i, Vector(2,0.)); feedcols.polResponse().put (i, Array(IPosition(2,2,2))); } } void iterMS (double binwidth) { MeasurementSet ms("tMSIter_tmp.ms"); Block sort(2); sort[0] = MS::ANTENNA1; sort[1] = MS::ANTENNA2; MSIter msIter(ms, sort, binwidth, False); for (msIter.origin(); msIter.more(); msIter++) { cout << "nrow=" << msIter.table().nrow() << " a1=" << ScalarColumn(msIter.table(), "ANTENNA1")(0) << " a2=" << ScalarColumn(msIter.table(), "ANTENNA2")(0) << " time=" << ScalarColumn(msIter.table(), "TIME").getColumn() - 1e9 << " keyCh=" << msIter.keyChange() << endl; } } void iterMSMemory (double binwidth) { MeasurementSet ms("tMSIter_tmp.ms"); Block sort(2); sort[0] = MS::ANTENNA1; sort[1] = MS::ANTENNA2; MSIter msIter(ms, sort, binwidth, False, False); // Use stored table in memory for (msIter.origin(); msIter.more(); msIter++) { cout << "nrow=" << msIter.table().nrow() << " a1=" << ScalarColumn(msIter.table(), "ANTENNA1")(0) << " a2=" << ScalarColumn(msIter.table(), "ANTENNA2")(0) << " time=" << ScalarColumn(msIter.table(), "TIME").getColumn() - 1e9 << endl; } } void iter2MS (double binwidth) { MeasurementSet ms("tMSIter_tmp.ms"); Block sort(2); sort[0] = MS::ANTENNA1; sort[1] = MS::ANTENNA2; MSIter msIter(ms, sort, binwidth, False); unsigned i = 0; for (msIter.origin(); msIter.more(); ++msIter) { cout << "nrow=" << msIter.table().nrow() << " a1=" << ScalarColumn(msIter.table(), "ANTENNA1")(0) << " a2=" << ScalarColumn(msIter.table(), "ANTENNA2")(0) << " time=" << ScalarColumn(msIter.table(), "TIME").getColumn() - 1e9 << " keyCh=" << msIter.keyChange() << endl; if (++i == 4) { cout << "=====" << endl; MSIter msIter1 = msIter; for (msIter1.origin(); msIter1.more(); ++msIter1) { cout << "nrow=" << msIter1.table().nrow() << " a1=" << ScalarColumn(msIter1.table(), "ANTENNA1")(0) << " a2=" << ScalarColumn(msIter1.table(), "ANTENNA2")(0) << " time=" << ScalarColumn(msIter1.table(), "TIME").getColumn() - 1e9 << " keyCh=" << msIter1.keyChange() << endl; } cout << "=====" << endl; } } cout << "=====" << endl; } void iter2MSMemory (double binwidth) { MeasurementSet ms("tMSIter_tmp.ms"); Block sort(2); sort[0] = MS::ANTENNA1; sort[1] = MS::ANTENNA2; MSIter msIter(ms, sort, binwidth, False, False); // Use stored table in memory MSIter msIter1(ms, sort, binwidth, False, False); MSIter *it = &msIter; unsigned i = 0; for (it->origin(); it->more(); ++(*it)) { cout << "nrow=" << it->table().nrow() << " a1=" << ScalarColumn(it->table(), "ANTENNA1")(0) << " a2=" << ScalarColumn(it->table(), "ANTENNA2")(0) << " time=" << ScalarColumn(it->table(), "TIME").getColumn() - 1e9 << endl; if (++i % 2 == 1) { msIter1 = msIter; it = &msIter1; } else { msIter = msIter1; it = &msIter; } } } // This test exercises the generic sorting function constructor // with a trivial comparison which always compares two values equal. // The result is that all the rows are grouped together. void iterMSGenericSortFuncAlwaysTrue () { MeasurementSet ms("tMSIter_tmp.ms"); std::shared_ptr alwaysTrue(new CompareAlwaysTrue()); std::vector>> sortCols; sortCols.push_back(std::make_pair("ANTENNA1", alwaysTrue)); MSIter msIter(ms, sortCols); size_t niter = 0; for (msIter.origin(); msIter.more(); msIter++) { cout << "nrow=" << msIter.table().nrow()<(obj1); const Int& v2 = *static_cast(obj2); double v1_c, v2_c; if( v1 == 0 || v1 == 1) v1_c = 0.5; else v1_c = v1; if( v2 == 0 || v2 == 1) v2_c = 0.5; else v2_c = v2; return (v1_c == v2_c ? 0 : (v1_c < v2_c ? -1 : 1)); } }; // This test exercises the generic sorting function constructor // with a comparison function that groups together antennas 0 and 1 // and leaves antenna 2 in a different group void iterMSGenericSortFuncAntennaGrouping () { MeasurementSet ms("tMSIter_tmp.ms"); std::shared_ptr antennaCluster(new CompareAntennaGrouping()); std::vector>> sortCols; sortCols.push_back(std::make_pair("ANTENNA1", antennaCluster)); MSIter msIter(ms, sortCols); size_t nAnt01 = 0; size_t nAnt2 = 0; for (msIter.origin(); msIter.more(); msIter++) { cout << "nrow=" << msIter.table().nrow()<(msIter.table(), "ANTENNA1")(0); if(antenna == 0 || antenna == 1) nAnt01 += msIter.table().nrow(); else nAnt2 += msIter.table().nrow(); } AlwaysAssertExit(nAnt01 == 25); AlwaysAssertExit(nAnt2 == 5); } void createMSSeveralDDFeedField(int nAnt, int nTime, int nDD, int nField, double msinterval, std::string name) { // Create the MS (with DATA column) and its subtables. TableDesc td (MS::requiredTableDesc()); MS::addColumnToDesc(td, MS::DATA, 2); SetupNewTable newtab(name, td, Table::New); MeasurementSet ms(newtab); ms.createDefaultSubtables(Table::New); // Write main table with columns needed for MSIter. Array data(IPosition(2,4,8)); indgen (data); MSColumns mscols(ms); uInt rownr = 0; for (int iField=0; iField dir(IPosition(2,2,1)); dir.data()[0] = 1.1; dir.data()[1] = 1.5; fieldcols.delayDir().put (iField, dir); fieldcols.phaseDir().put (iField, dir); fieldcols.name().put (iField, std::string("TESTFIELD")+std::to_string(iField)); fieldcols.sourceId().put (iField, 0); } ms.dataDescription().addRow(nDD); MSDataDescColumns ddcols(ms.dataDescription()); for (int ddid=0; ddidfreqs(8); indgen (freqs, 1e9, 1e6); for (int ddid=0; ddid corrTypes1(2, 0); corrTypes1[1] = 1; polcols.numCorr().put (0, 4); polcols.corrType().put (0, corrTypes1); Vector corrTypes2(2, 0); corrTypes2[0] = Stokes::XX; corrTypes2[1] = Stokes::YY; polcols.numCorr().put (1, 4); polcols.corrType().put (1, corrTypes2); ms.antenna().addRow(nAnt); MSAntennaColumns antcols(ms.antenna()); Vector pos(3); indgen (pos, 6.4e6, 1e3); for (int i=0; i(IPosition(2,2,2), i*0.01+ddid*0.1)); feedcols.receptorAngle().put (i + ddid * nAnt, Vector(2, i*0.01+ddid*0.1)); feedcols.polResponse().put (i + ddid * nAnt, Array(IPosition(2,2,2), Complex(i*0.01+ddid*0.1, i*0.01+ddid*0.1))); } } } // This test exercises the lazy caching mechanism for retrieving // metadata related to DD and FEED. void iterMSCachedDDFeedInfo () { // Create synthetic MS with several DDIds int nAnt = 5; int nTime = 5; int nDD = 5; int nField = 1; double msinterval = 60.; createMSSeveralDDFeedField(nAnt, nTime, nDD, nField, msinterval, "tMSIter_severalddfeed_tmp.ms"); MeasurementSet ms("tMSIter_severalddfeed_tmp.ms"); // Create a MSIter with DDID in the sorting columns // There will be as many iterations as DDIDs. // This is done both for the traditional constructor as for the // constructor with the generic sorting definition. cout << "Iteration with DDID sorting" << endl; cout << "===========================" << endl; for(int useGenericSortCons = 0 ; useGenericSortCons < 2 ; useGenericSortCons++) { std::unique_ptr msIter; if(useGenericSortCons) { // Use constructor with generic sorting definitions std::vector>> sortCols; sortCols.push_back(std::make_pair("DATA_DESC_ID", nullptr)); msIter.reset(new MSIter(ms, sortCols)); } else { // Use traditional constructor Block sort(1); sort[0] = MS::DATA_DESC_ID; msIter.reset(new MSIter(ms, sort, 0, False, False)); // Use stored table in memory } // Set the expected DD metadata in the first iteration int expectedDDId = 0; int expectedSPWId = 0; int expectedPolId = 0; int expectedPolFrame = 0; VectorexpectedFreqs(8); indgen (expectedFreqs, 1e9, 1e6); // Set the expected Feed metadata in the first iteration int nReceptors = 2; int nFeed = 1; Cube expectedReceptorAngles; Cube> expectedBeamOffsets; Matrix expectedPartialCJones(IPosition(2,2)); expectedPartialCJones = 0; expectedReceptorAngles.resize(nReceptors, nAnt, nFeed); expectedBeamOffsets.resize(nReceptors, nAnt, nFeed); for(int iAnt = 0; iAnt < nAnt; iAnt++) { expectedReceptorAngles(0, iAnt, 0) = iAnt*0.01; expectedReceptorAngles(1, iAnt, 0) = iAnt*0.01; expectedBeamOffsets(0, iAnt, 0) = RigidVector(iAnt*0.01); expectedBeamOffsets(1, iAnt, 0) = RigidVector(iAnt*0.01); } // Iterate the MS // It is expected that in each iteration a new DDId is retrieved for (msIter->origin(); msIter->more(); (*msIter)++) { cout << "nrow=" << msIter->table().nrow()<frequency0() << endl; // Check that the DD related metadata is what we expect AlwaysAssertExit(msIter->dataDescriptionId() == expectedDDId); AlwaysAssertExit(msIter->spectralWindowId() == expectedSPWId); AlwaysAssertExit(msIter->polarizationId() == expectedPolId); AlwaysAssertExit(msIter->polFrame() == expectedPolFrame); AlwaysAssertExit(allEQ(msIter->frequency(), expectedFreqs)); for (size_t ddrow = 0 ; ddrow < msIter->table().nrow() ; ddrow++) { AlwaysAssertExit(msIter->colDataDescriptionIds()(ddrow) == expectedDDId); } Unit Hz(String("Hz")); AlwaysAssertExit(msIter->frequency0().get(Hz).getValue() == expectedFreqs[0]); // Check that the Feed related metadata is what we expect AlwaysAssertExit(allNear(msIter->receptorAngles(), expectedReceptorAngles, 1e-6)); AlwaysAssertExit(allNear(msIter->receptorAngle(), expectedReceptorAngles[0], 1e-6)); // Need to unwrap the loop because RigidVector doesn't have a comparison operator for(int iRec = 0 ; iRec < nReceptors; ++iRec) { for(int iAnt = 0; iAnt < nAnt; iAnt++) { for(int iFeed = 0; iFeed < nFeed; iFeed++) { AlwaysAssertExit(std::abs(msIter->getBeamOffsets()(iRec, iAnt, iFeed)(0) - expectedBeamOffsets(iRec, iAnt, iFeed)(0)) < 1e-6); AlwaysAssertExit(std::abs(msIter->getBeamOffsets()(iRec, iAnt, iFeed)(1) - expectedBeamOffsets(iRec, iAnt, iFeed)(1)) < 1e-6); } } } // Need to unwrap the loop because SquareMatrix doesn't have a comparison operator for(int iAnt = 0; iAnt < nAnt; iAnt++) { for(int iFeed = 0; iFeed < nFeed; iFeed++) { AlwaysAssertExit(allNear(msIter->CJonesAll()(iAnt, iFeed).matrix(), expectedPartialCJones + Complex(iAnt * 0.01, iAnt * 0.01), 1e-6)); } } // Update the expected values for next iteration expectedDDId++; expectedSPWId++; expectedPolId = expectedDDId % 2; expectedPolFrame = expectedDDId % 2; expectedFreqs += 1e7; expectedReceptorAngles += 0.1; expectedBeamOffsets += RigidVector(0.1); expectedPartialCJones += Complex(0.1, 0.1); } } // Create a MSIter with DDID in the sorting columns // but not the fastest one. There will be as many iterations // as DDIds times the groups in the other sortign column. // Antenna1 has been choosen as the other one. // This is done both for the traditional constructor as for the // constructor with the generic sorting definition. cout << "Iteration with ANTENNA1 and DDID sorting" << endl; cout << "========================================" << endl; for(int useGenericSortCons = 0 ; useGenericSortCons < 2 ; useGenericSortCons++) { std::unique_ptr msIter; if(useGenericSortCons) { // Use constructor with generic sorting definitions std::vector>> sortCols; sortCols.push_back(std::make_pair("DATA_DESC_ID", nullptr)); sortCols.push_back(std::make_pair("ANTENNA1", nullptr)); msIter.reset(new MSIter(ms, sortCols)); } else { // Use traditional constructor Block sort(2); sort[0] = MS::DATA_DESC_ID; sort[1] = MS::ANTENNA1; msIter.reset(new MSIter(ms, sort, 0, False, False)); // Use stored table in memory } // Set the expected DD metadata in the first iteration int expectedDDId = 0; int expectedSPWId = 0; int expectedPolId = 0; int expectedPolFrame = 0; bool newSpw = true; bool newPol = true; bool newDD = true; VectorexpectedFreqs(8); indgen (expectedFreqs, 1e9, 1e6); // Set the expected Feed metadata in the first iteration int nReceptors = 2; int nFeed = 1; Cube expectedReceptorAngles; Cube> expectedBeamOffsets; Matrix expectedPartialCJones(IPosition(2,2)); expectedPartialCJones = 0; expectedReceptorAngles.resize(nReceptors, nAnt, nFeed); expectedBeamOffsets.resize(nReceptors, nAnt, nFeed); for(int iAnt = 0; iAnt < nAnt; iAnt++) { expectedReceptorAngles(0, iAnt, 0) = iAnt*0.01; expectedReceptorAngles(1, iAnt, 0) = iAnt*0.01; expectedBeamOffsets(0, iAnt, 0) = RigidVector(iAnt*0.01); expectedBeamOffsets(1, iAnt, 0) = RigidVector(iAnt*0.01); } // Iterate the MS // It is expected that in each iteration antenna1 index runs faster // than DDId int antena1Idx = 0; for (msIter->origin(); msIter->more(); (*msIter)++) { cout << "nrow=" << msIter->table().nrow()<frequency0() << endl; // Check that the DD related metadata is what we expect AlwaysAssertExit(msIter->dataDescriptionId() == expectedDDId); AlwaysAssertExit(msIter->newDataDescriptionId() == newDD); AlwaysAssertExit(msIter->spectralWindowId() == expectedSPWId); AlwaysAssertExit(msIter->newSpectralWindow() == newSpw); AlwaysAssertExit(msIter->polarizationId() == expectedPolId); AlwaysAssertExit(msIter->newPolarizationId() == newPol); AlwaysAssertExit(msIter->polFrame() == expectedPolFrame); AlwaysAssertExit(allEQ(msIter->frequency(), expectedFreqs)); for (size_t ddrow = 0 ; ddrow < msIter->table().nrow() ; ddrow++) { AlwaysAssertExit(msIter->colDataDescriptionIds()(ddrow) == expectedDDId); } Unit Hz(String("Hz")); AlwaysAssertExit(msIter->frequency0().get(Hz).getValue() == expectedFreqs[0]); // Check that the Feed related metadata is what we expect AlwaysAssertExit(allNear(msIter->receptorAngles(), expectedReceptorAngles, 1e-6)); AlwaysAssertExit(allNear(msIter->receptorAngle(), expectedReceptorAngles[0], 1e-6)); for(int iRec = 0 ; iRec < nReceptors; ++iRec) { for(int iAnt = 0; iAnt < nAnt; iAnt++) { for(int iFeed = 0; iFeed < nFeed; iFeed++) { AlwaysAssertExit(std::abs(msIter->getBeamOffsets()(iRec, iAnt, iFeed)(0) - expectedBeamOffsets(iRec, iAnt, iFeed)(0)) < 1e-6); AlwaysAssertExit(std::abs(msIter->getBeamOffsets()(iRec, iAnt, iFeed)(1) - expectedBeamOffsets(iRec, iAnt, iFeed)(1)) < 1e-6); } } } // Need to unwrap the loop because SquareMatrix doesn't have a comparison operator for(int iAnt = 0; iAnt < nAnt; iAnt++) { for(int iFeed = 0; iFeed < nFeed; iFeed++) { AlwaysAssertExit(allNear(msIter->CJonesAll()(iAnt, iFeed).matrix(), expectedPartialCJones + Complex(iAnt * 0.01, iAnt * 0.01), 1e-6)); } } // Update the expected values for next iteration antena1Idx++; if(antena1Idx > nAnt - 1) { // All ANTENNA1 iterations done, now iterate on a new DD expectedDDId++; expectedSPWId++; expectedPolId = expectedDDId % 2; expectedPolFrame = expectedDDId % 2; expectedFreqs += 1e7; expectedReceptorAngles += 0.1; expectedBeamOffsets += RigidVector(0.1); expectedPartialCJones += Complex(0.1, 0.1); antena1Idx = 0; newSpw = true; newPol = true; newDD = true; } else { // Still iterating on ANTENNA1 newSpw = false; newPol = false; newDD = false; } } } // Create a MSIter without DDID in the sorting columns // Sorting is done per timestamp. For each timestamp // all baselines and all DDIds are present // This is done both for the traditional constructor as for the // constructor with the generic sorting definition. cout << "Iteration with TIME sorting" << endl; cout << "===========================" << endl; for(int useGenericSortCons = 0 ; useGenericSortCons < 2 ; useGenericSortCons++) { std::unique_ptr msIter; if(useGenericSortCons) { // Use constructor with generic sorting definitions std::vector>> sortCols; sortCols.push_back(std::make_pair("TIME", nullptr)); msIter.reset(new MSIter(ms, sortCols)); } else { // Use traditional constructor Block sort(1); sort[0] = MS::TIME; msIter.reset(new MSIter(ms, sort, 1., False, False)); // Use stored table in memory } // Set the expected DD metadata in the first iteration VectorexpectedFreqs(8); indgen (expectedFreqs, 1e9, 1e6); // Set the expected Feed metadata in the first iteration int nReceptors = 2; int nFeed = 1; Cube expectedReceptorAngles; Cube> expectedBeamOffsets; Matrix expectedPartialCJones(IPosition(2,2)); expectedPartialCJones = 0; expectedReceptorAngles.resize(nReceptors, nAnt, nFeed); expectedBeamOffsets.resize(nReceptors, nAnt, nFeed); for(int iAnt = 0; iAnt < nAnt; iAnt++) { expectedReceptorAngles(0, iAnt, 0) = iAnt*0.01; expectedReceptorAngles(1, iAnt, 0) = iAnt*0.01; expectedBeamOffsets(0, iAnt, 0) = RigidVector(iAnt*0.01); expectedBeamOffsets(1, iAnt, 0) = RigidVector(iAnt*0.01); } // Do not read the DD information for each iteration, but skip iterSkip // iterations before reading it. That should exercise the logic for the // lazy computation of the DD for (int iterSkip = 5; iterSkip >=0; iterSkip--) { // Iterate the MS // It is expected that in each iteration a single timestamp is retrieved, // which contains all DDIds int rowsToSkip = iterSkip; for (msIter->origin(); msIter->more(); (*msIter)++) { if(rowsToSkip == 0) { cout << "nrow=" << msIter->table().nrow()<frequency0() << endl; // Check that the DD related metadata is what we expect // For DDId, SPWId, polID: since there is no unique ID in this iteration // the first one (0) is returned AlwaysAssertExit(msIter->dataDescriptionId() == 0); AlwaysAssertExit(msIter->spectralWindowId() == 0); AlwaysAssertExit(msIter->polarizationId() == 0); AlwaysAssertExit(msIter->polFrame() == 0); AlwaysAssertExit(allEQ(msIter->frequency(), expectedFreqs)); for (size_t ddrow = 0 ; ddrow < msIter->table().nrow() ; ddrow++) { // Every 15 rows (number of baselines) a new DDId appears AlwaysAssertExit(msIter->colDataDescriptionIds()(ddrow) == (int)(ddrow / 15)); } Unit Hz(String("Hz")); //Frequency also refers to the first SPW AlwaysAssertExit(msIter->frequency0().get(Hz).getValue() == expectedFreqs[0]); // Check that the Feed related metadata is what we expect // Note that these are computed assuming there is a single SPW // in the iteration, so they will return values corresponding // to the first SPW, DDId present. AlwaysAssertExit(allNear(msIter->receptorAngles(), expectedReceptorAngles, 1e-6)); AlwaysAssertExit(allNear(msIter->receptorAngle(), expectedReceptorAngles[0], 1e-6)); for(int iRec = 0 ; iRec < nReceptors; ++iRec) { for(int iAnt = 0; iAnt < nAnt; iAnt++) { for(int iFeed = 0; iFeed < nFeed; iFeed++) { AlwaysAssertExit(std::abs(msIter->getBeamOffsets()(iRec, iAnt, iFeed)(0) - expectedBeamOffsets(iRec, iAnt, iFeed)(0)) < 1e-6); AlwaysAssertExit(std::abs(msIter->getBeamOffsets()(iRec, iAnt, iFeed)(1) - expectedBeamOffsets(iRec, iAnt, iFeed)(1)) < 1e-6); } } } // Need to unwrap the loop because SquareMatrix doesn't have a comparison operator for(int iAnt = 0; iAnt < nAnt; iAnt++) { for(int iFeed = 0; iFeed < nFeed; iFeed++) { AlwaysAssertExit(allNear(msIter->CJonesAll()(iAnt, iFeed).matrix(), expectedPartialCJones + Complex(iAnt * 0.01, iAnt * 0.01), 1e-6)); } } rowsToSkip = iterSkip; } else { cout << " Skipping row" << endl; rowsToSkip--; } } } } } // This test exercises the lazy caching mechanism for retrieving // metadata related to FIELD void iterMSCachedFieldInfo () { // Create synthetic MS with several Fields int nAnt = 5; int nTime = 5; int nDD = 1; int nField = 5; double msinterval = 60.; createMSSeveralDDFeedField(nAnt, nTime, nDD, nField, msinterval, "tMSIter_severalfield_tmp.ms"); MeasurementSet ms("tMSIter_severalfield_tmp.ms"); // Create a MSIter with DDID in the sorting columns // There will be as many iterations as FIELD_IDs. // This is done both for the traditional constructor as for the // constructor with the generic sorting definition. cout << "Iteration with FIELD sorting" << endl; cout << "===========================" << endl; for(int useGenericSortCons = 0 ; useGenericSortCons < 2 ; useGenericSortCons++) { std::unique_ptr msIter; if(useGenericSortCons) { // Use constructor with generic sorting definitions std::vector>> sortCols; sortCols.push_back(std::make_pair("FIELD_ID", nullptr)); msIter.reset(new MSIter(ms, sortCols)); } else { // Use traditional constructor Block sort(1); sort[0] = MS::FIELD_ID; msIter.reset(new MSIter(ms, sort, 0, False, False)); // Use stored table in memory } // Set the expected Field metadata in the first iteration int expectedFieldId = 0; std::string expectedFieldName("TESTFIELD"); // Iterate the MS // It is expected that in each iteration a new FIELD_ID is retrieved for (msIter->origin(); msIter->more(); (*msIter)++) { cout << "nrow=" << msIter->table().nrow()< msIter; if(useGenericSortCons) { // Use constructor with generic sorting definitions std::vector>> sortCols; sortCols.push_back(std::make_pair("FIELD_ID", nullptr)); sortCols.push_back(std::make_pair("ANTENNA1", nullptr)); msIter.reset(new MSIter(ms, sortCols)); } else { // Use traditional constructor Block sort(2); sort[0] = MS::FIELD_ID; sort[1] = MS::ANTENNA1; msIter.reset(new MSIter(ms, sort, 0, False, False)); // Use stored table in memory } // Set the expected Field metadata in the first iteration int expectedFieldId = 0; bool newField = true; std::string expectedFieldName("TESTFIELD"); // Iterate the MS // It is expected that in each iteration antenna1 index runs faster // than DDId int antena1Idx = 0; for (msIter->origin(); msIter->more(); (*msIter)++) { cout << "nrow=" << msIter->table().nrow()< msIter; if(useGenericSortCons) { // Use constructor with generic sorting definitions std::vector>> sortCols; sortCols.push_back(std::make_pair("TIME", nullptr)); msIter.reset(new MSIter(ms, sortCols)); } else { // Use traditional constructor Block sort(1); sort[0] = MS::TIME; msIter.reset(new MSIter(ms, sort, 1., False, False)); // Use stored table in memory } // Set the expected Field metadata in the first iteration int expectedFieldId = 0; std::string expectedFieldName("TESTFIELD0"); // Do not read the DD information for each iteration, but skip iterSkip // iterations before reading it. That should exercise the logic for the // lazy computation of the DD for (int iterSkip = 5; iterSkip >=0; iterSkip--) { // Iterate the MS // It is expected that in each iteration a single timestamp is retrieved, // which contains all FIELD_IDs int rowsToSkip = iterSkip; for (msIter->origin(); msIter->more(); (*msIter)++) { if(rowsToSkip == 0) { cout << "nrow=" << msIter->table().nrow()< 1) { istringstream iss(argv[1]); iss >> nAnt; } if (argc > 2) { istringstream iss(argv[2]); iss >> nTime; } if (argc > 3) { istringstream iss(argv[3]); iss >> msinterval; } if (argc > 4) { istringstream iss(argv[4]); iss >> binwidth; } createMS(nAnt, nTime, msinterval); iterMS(binwidth); cout << "########" << endl; iterMSMemory(binwidth); cout << "########" << endl; iter2MS(binwidth); cout << "########" << endl; iter2MSMemory(binwidth); cout << "########" << endl; iterMSGenericSortFuncAlwaysTrue(); cout << "########" << endl; iterMSGenericSortFuncAntennaGrouping(); cout << "########" << endl; iterMSCachedDDFeedInfo(); cout << "########" << endl; iterMSCachedFieldInfo(); } catch (std::exception& x) { cerr << "Unexpected exception: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/ms/MeasurementSets/test/tMSIter.out000066400000000000000000000453601476623553700221360ustar00rootroot00000000000000nrow=2 a1=0 a2=0 time=[30, 90] keyCh=TIME nrow=2 a1=0 a2=0 time=[150, 210] keyCh=TIME nrow=1 a1=0 a2=0 time=[270] keyCh=ANTENNA2 nrow=2 a1=0 a2=1 time=[30, 90] keyCh=TIME nrow=2 a1=0 a2=1 time=[150, 210] keyCh=TIME nrow=1 a1=0 a2=1 time=[270] keyCh=ANTENNA2 nrow=2 a1=0 a2=2 time=[30, 90] keyCh=TIME nrow=2 a1=0 a2=2 time=[150, 210] keyCh=TIME nrow=1 a1=0 a2=2 time=[270] keyCh=ANTENNA1 nrow=2 a1=1 a2=1 time=[30, 90] keyCh=TIME nrow=2 a1=1 a2=1 time=[150, 210] keyCh=TIME nrow=1 a1=1 a2=1 time=[270] keyCh=ANTENNA2 nrow=2 a1=1 a2=2 time=[30, 90] keyCh=TIME nrow=2 a1=1 a2=2 time=[150, 210] keyCh=TIME nrow=1 a1=1 a2=2 time=[270] keyCh=ANTENNA1 nrow=2 a1=2 a2=2 time=[30, 90] keyCh=TIME nrow=2 a1=2 a2=2 time=[150, 210] keyCh=TIME nrow=1 a1=2 a2=2 time=[270] keyCh= ######## nrow=2 a1=0 a2=0 time=[30, 90] nrow=2 a1=0 a2=0 time=[150, 210] nrow=1 a1=0 a2=0 time=[270] nrow=2 a1=0 a2=1 time=[30, 90] nrow=2 a1=0 a2=1 time=[150, 210] nrow=1 a1=0 a2=1 time=[270] nrow=2 a1=0 a2=2 time=[30, 90] nrow=2 a1=0 a2=2 time=[150, 210] nrow=1 a1=0 a2=2 time=[270] nrow=2 a1=1 a2=1 time=[30, 90] nrow=2 a1=1 a2=1 time=[150, 210] nrow=1 a1=1 a2=1 time=[270] nrow=2 a1=1 a2=2 time=[30, 90] nrow=2 a1=1 a2=2 time=[150, 210] nrow=1 a1=1 a2=2 time=[270] nrow=2 a1=2 a2=2 time=[30, 90] nrow=2 a1=2 a2=2 time=[150, 210] nrow=1 a1=2 a2=2 time=[270] ######## nrow=2 a1=0 a2=0 time=[30, 90] keyCh=TIME nrow=2 a1=0 a2=0 time=[150, 210] keyCh=TIME nrow=1 a1=0 a2=0 time=[270] keyCh=ANTENNA2 nrow=2 a1=0 a2=1 time=[30, 90] keyCh=TIME ===== nrow=2 a1=0 a2=0 time=[30, 90] keyCh=TIME nrow=2 a1=0 a2=0 time=[150, 210] keyCh=TIME nrow=1 a1=0 a2=0 time=[270] keyCh=ANTENNA2 nrow=2 a1=0 a2=1 time=[30, 90] keyCh=TIME nrow=2 a1=0 a2=1 time=[150, 210] keyCh=TIME nrow=1 a1=0 a2=1 time=[270] keyCh=ANTENNA2 nrow=2 a1=0 a2=2 time=[30, 90] keyCh=TIME nrow=2 a1=0 a2=2 time=[150, 210] keyCh=TIME nrow=1 a1=0 a2=2 time=[270] keyCh=ANTENNA1 nrow=2 a1=1 a2=1 time=[30, 90] keyCh=TIME nrow=2 a1=1 a2=1 time=[150, 210] keyCh=TIME nrow=1 a1=1 a2=1 time=[270] keyCh=ANTENNA2 nrow=2 a1=1 a2=2 time=[30, 90] keyCh=TIME nrow=2 a1=1 a2=2 time=[150, 210] keyCh=TIME nrow=1 a1=1 a2=2 time=[270] keyCh=ANTENNA1 nrow=2 a1=2 a2=2 time=[30, 90] keyCh=TIME nrow=2 a1=2 a2=2 time=[150, 210] keyCh=TIME nrow=1 a1=2 a2=2 time=[270] keyCh= ===== nrow=2 a1=0 a2=1 time=[150, 210] keyCh=TIME nrow=1 a1=0 a2=1 time=[270] keyCh=ANTENNA2 nrow=2 a1=0 a2=2 time=[30, 90] keyCh=TIME nrow=2 a1=0 a2=2 time=[150, 210] keyCh=TIME nrow=1 a1=0 a2=2 time=[270] keyCh=ANTENNA1 nrow=2 a1=1 a2=1 time=[30, 90] keyCh=TIME nrow=2 a1=1 a2=1 time=[150, 210] keyCh=TIME nrow=1 a1=1 a2=1 time=[270] keyCh=ANTENNA2 nrow=2 a1=1 a2=2 time=[30, 90] keyCh=TIME nrow=2 a1=1 a2=2 time=[150, 210] keyCh=TIME nrow=1 a1=1 a2=2 time=[270] keyCh=ANTENNA1 nrow=2 a1=2 a2=2 time=[30, 90] keyCh=TIME nrow=2 a1=2 a2=2 time=[150, 210] keyCh=TIME nrow=1 a1=2 a2=2 time=[270] keyCh= ===== ######## nrow=2 a1=0 a2=0 time=[30, 90] nrow=2 a1=0 a2=0 time=[150, 210] nrow=1 a1=0 a2=0 time=[270] nrow=2 a1=0 a2=1 time=[30, 90] nrow=2 a1=0 a2=1 time=[150, 210] nrow=1 a1=0 a2=1 time=[270] nrow=2 a1=0 a2=2 time=[30, 90] nrow=2 a1=0 a2=2 time=[150, 210] nrow=1 a1=0 a2=2 time=[270] nrow=2 a1=1 a2=1 time=[30, 90] nrow=2 a1=1 a2=1 time=[150, 210] nrow=1 a1=1 a2=1 time=[270] nrow=2 a1=1 a2=2 time=[30, 90] nrow=2 a1=1 a2=2 time=[150, 210] nrow=1 a1=1 a2=2 time=[270] nrow=2 a1=2 a2=2 time=[30, 90] nrow=2 a1=2 a2=2 time=[150, 210] nrow=1 a1=2 a2=2 time=[270] ######## nrow=30 ######## nrow=25 nrow=5 ######## Iteration with DDID sorting =========================== nrow=75 ddid = 0 spwid = 0 polid = 0 freqs = [1e+09, 1.001e+09, 1.002e+09, 1.003e+09, 1.004e+09, 1.005e+09, 1.006e+09, 1.007e+09] freq0 Frequency: 1e+09 nrow=75 ddid = 1 spwid = 1 polid = 1 freqs = [1.01e+09, 1.011e+09, 1.012e+09, 1.013e+09, 1.014e+09, 1.015e+09, 1.016e+09, 1.017e+09] freq0 Frequency: 1.01e+09 nrow=75 ddid = 2 spwid = 2 polid = 0 freqs = [1.02e+09, 1.021e+09, 1.022e+09, 1.023e+09, 1.024e+09, 1.025e+09, 1.026e+09, 1.027e+09] freq0 Frequency: 1.02e+09 nrow=75 ddid = 3 spwid = 3 polid = 1 freqs = [1.03e+09, 1.031e+09, 1.032e+09, 1.033e+09, 1.034e+09, 1.035e+09, 1.036e+09, 1.037e+09] freq0 Frequency: 1.03e+09 nrow=75 ddid = 4 spwid = 4 polid = 0 freqs = [1.04e+09, 1.041e+09, 1.042e+09, 1.043e+09, 1.044e+09, 1.045e+09, 1.046e+09, 1.047e+09] freq0 Frequency: 1.04e+09 nrow=75 ddid = 0 spwid = 0 polid = 0 freqs = [1e+09, 1.001e+09, 1.002e+09, 1.003e+09, 1.004e+09, 1.005e+09, 1.006e+09, 1.007e+09] freq0 Frequency: 1e+09 nrow=75 ddid = 1 spwid = 1 polid = 1 freqs = [1.01e+09, 1.011e+09, 1.012e+09, 1.013e+09, 1.014e+09, 1.015e+09, 1.016e+09, 1.017e+09] freq0 Frequency: 1.01e+09 nrow=75 ddid = 2 spwid = 2 polid = 0 freqs = [1.02e+09, 1.021e+09, 1.022e+09, 1.023e+09, 1.024e+09, 1.025e+09, 1.026e+09, 1.027e+09] freq0 Frequency: 1.02e+09 nrow=75 ddid = 3 spwid = 3 polid = 1 freqs = [1.03e+09, 1.031e+09, 1.032e+09, 1.033e+09, 1.034e+09, 1.035e+09, 1.036e+09, 1.037e+09] freq0 Frequency: 1.03e+09 nrow=75 ddid = 4 spwid = 4 polid = 0 freqs = [1.04e+09, 1.041e+09, 1.042e+09, 1.043e+09, 1.044e+09, 1.045e+09, 1.046e+09, 1.047e+09] freq0 Frequency: 1.04e+09 Iteration with ANTENNA1 and DDID sorting ======================================== nrow=25 ddid = 0 spwid = 0 polid = 0 freqs = [1e+09, 1.001e+09, 1.002e+09, 1.003e+09, 1.004e+09, 1.005e+09, 1.006e+09, 1.007e+09] freq0 Frequency: 1e+09 nrow=20 ddid = 0 spwid = 0 polid = 0 freqs = [1e+09, 1.001e+09, 1.002e+09, 1.003e+09, 1.004e+09, 1.005e+09, 1.006e+09, 1.007e+09] freq0 Frequency: 1e+09 nrow=15 ddid = 0 spwid = 0 polid = 0 freqs = [1e+09, 1.001e+09, 1.002e+09, 1.003e+09, 1.004e+09, 1.005e+09, 1.006e+09, 1.007e+09] freq0 Frequency: 1e+09 nrow=10 ddid = 0 spwid = 0 polid = 0 freqs = [1e+09, 1.001e+09, 1.002e+09, 1.003e+09, 1.004e+09, 1.005e+09, 1.006e+09, 1.007e+09] freq0 Frequency: 1e+09 nrow=5 ddid = 0 spwid = 0 polid = 0 freqs = [1e+09, 1.001e+09, 1.002e+09, 1.003e+09, 1.004e+09, 1.005e+09, 1.006e+09, 1.007e+09] freq0 Frequency: 1e+09 nrow=25 ddid = 1 spwid = 1 polid = 1 freqs = [1.01e+09, 1.011e+09, 1.012e+09, 1.013e+09, 1.014e+09, 1.015e+09, 1.016e+09, 1.017e+09] freq0 Frequency: 1.01e+09 nrow=20 ddid = 1 spwid = 1 polid = 1 freqs = [1.01e+09, 1.011e+09, 1.012e+09, 1.013e+09, 1.014e+09, 1.015e+09, 1.016e+09, 1.017e+09] freq0 Frequency: 1.01e+09 nrow=15 ddid = 1 spwid = 1 polid = 1 freqs = [1.01e+09, 1.011e+09, 1.012e+09, 1.013e+09, 1.014e+09, 1.015e+09, 1.016e+09, 1.017e+09] freq0 Frequency: 1.01e+09 nrow=10 ddid = 1 spwid = 1 polid = 1 freqs = [1.01e+09, 1.011e+09, 1.012e+09, 1.013e+09, 1.014e+09, 1.015e+09, 1.016e+09, 1.017e+09] freq0 Frequency: 1.01e+09 nrow=5 ddid = 1 spwid = 1 polid = 1 freqs = [1.01e+09, 1.011e+09, 1.012e+09, 1.013e+09, 1.014e+09, 1.015e+09, 1.016e+09, 1.017e+09] freq0 Frequency: 1.01e+09 nrow=25 ddid = 2 spwid = 2 polid = 0 freqs = [1.02e+09, 1.021e+09, 1.022e+09, 1.023e+09, 1.024e+09, 1.025e+09, 1.026e+09, 1.027e+09] freq0 Frequency: 1.02e+09 nrow=20 ddid = 2 spwid = 2 polid = 0 freqs = [1.02e+09, 1.021e+09, 1.022e+09, 1.023e+09, 1.024e+09, 1.025e+09, 1.026e+09, 1.027e+09] freq0 Frequency: 1.02e+09 nrow=15 ddid = 2 spwid = 2 polid = 0 freqs = [1.02e+09, 1.021e+09, 1.022e+09, 1.023e+09, 1.024e+09, 1.025e+09, 1.026e+09, 1.027e+09] freq0 Frequency: 1.02e+09 nrow=10 ddid = 2 spwid = 2 polid = 0 freqs = [1.02e+09, 1.021e+09, 1.022e+09, 1.023e+09, 1.024e+09, 1.025e+09, 1.026e+09, 1.027e+09] freq0 Frequency: 1.02e+09 nrow=5 ddid = 2 spwid = 2 polid = 0 freqs = [1.02e+09, 1.021e+09, 1.022e+09, 1.023e+09, 1.024e+09, 1.025e+09, 1.026e+09, 1.027e+09] freq0 Frequency: 1.02e+09 nrow=25 ddid = 3 spwid = 3 polid = 1 freqs = [1.03e+09, 1.031e+09, 1.032e+09, 1.033e+09, 1.034e+09, 1.035e+09, 1.036e+09, 1.037e+09] freq0 Frequency: 1.03e+09 nrow=20 ddid = 3 spwid = 3 polid = 1 freqs = [1.03e+09, 1.031e+09, 1.032e+09, 1.033e+09, 1.034e+09, 1.035e+09, 1.036e+09, 1.037e+09] freq0 Frequency: 1.03e+09 nrow=15 ddid = 3 spwid = 3 polid = 1 freqs = [1.03e+09, 1.031e+09, 1.032e+09, 1.033e+09, 1.034e+09, 1.035e+09, 1.036e+09, 1.037e+09] freq0 Frequency: 1.03e+09 nrow=10 ddid = 3 spwid = 3 polid = 1 freqs = [1.03e+09, 1.031e+09, 1.032e+09, 1.033e+09, 1.034e+09, 1.035e+09, 1.036e+09, 1.037e+09] freq0 Frequency: 1.03e+09 nrow=5 ddid = 3 spwid = 3 polid = 1 freqs = [1.03e+09, 1.031e+09, 1.032e+09, 1.033e+09, 1.034e+09, 1.035e+09, 1.036e+09, 1.037e+09] freq0 Frequency: 1.03e+09 nrow=25 ddid = 4 spwid = 4 polid = 0 freqs = [1.04e+09, 1.041e+09, 1.042e+09, 1.043e+09, 1.044e+09, 1.045e+09, 1.046e+09, 1.047e+09] freq0 Frequency: 1.04e+09 nrow=20 ddid = 4 spwid = 4 polid = 0 freqs = [1.04e+09, 1.041e+09, 1.042e+09, 1.043e+09, 1.044e+09, 1.045e+09, 1.046e+09, 1.047e+09] freq0 Frequency: 1.04e+09 nrow=15 ddid = 4 spwid = 4 polid = 0 freqs = [1.04e+09, 1.041e+09, 1.042e+09, 1.043e+09, 1.044e+09, 1.045e+09, 1.046e+09, 1.047e+09] freq0 Frequency: 1.04e+09 nrow=10 ddid = 4 spwid = 4 polid = 0 freqs = [1.04e+09, 1.041e+09, 1.042e+09, 1.043e+09, 1.044e+09, 1.045e+09, 1.046e+09, 1.047e+09] freq0 Frequency: 1.04e+09 nrow=5 ddid = 4 spwid = 4 polid = 0 freqs = [1.04e+09, 1.041e+09, 1.042e+09, 1.043e+09, 1.044e+09, 1.045e+09, 1.046e+09, 1.047e+09] freq0 Frequency: 1.04e+09 nrow=25 ddid = 0 spwid = 0 polid = 0 freqs = [1e+09, 1.001e+09, 1.002e+09, 1.003e+09, 1.004e+09, 1.005e+09, 1.006e+09, 1.007e+09] freq0 Frequency: 1e+09 nrow=20 ddid = 0 spwid = 0 polid = 0 freqs = [1e+09, 1.001e+09, 1.002e+09, 1.003e+09, 1.004e+09, 1.005e+09, 1.006e+09, 1.007e+09] freq0 Frequency: 1e+09 nrow=15 ddid = 0 spwid = 0 polid = 0 freqs = [1e+09, 1.001e+09, 1.002e+09, 1.003e+09, 1.004e+09, 1.005e+09, 1.006e+09, 1.007e+09] freq0 Frequency: 1e+09 nrow=10 ddid = 0 spwid = 0 polid = 0 freqs = [1e+09, 1.001e+09, 1.002e+09, 1.003e+09, 1.004e+09, 1.005e+09, 1.006e+09, 1.007e+09] freq0 Frequency: 1e+09 nrow=5 ddid = 0 spwid = 0 polid = 0 freqs = [1e+09, 1.001e+09, 1.002e+09, 1.003e+09, 1.004e+09, 1.005e+09, 1.006e+09, 1.007e+09] freq0 Frequency: 1e+09 nrow=25 ddid = 1 spwid = 1 polid = 1 freqs = [1.01e+09, 1.011e+09, 1.012e+09, 1.013e+09, 1.014e+09, 1.015e+09, 1.016e+09, 1.017e+09] freq0 Frequency: 1.01e+09 nrow=20 ddid = 1 spwid = 1 polid = 1 freqs = [1.01e+09, 1.011e+09, 1.012e+09, 1.013e+09, 1.014e+09, 1.015e+09, 1.016e+09, 1.017e+09] freq0 Frequency: 1.01e+09 nrow=15 ddid = 1 spwid = 1 polid = 1 freqs = [1.01e+09, 1.011e+09, 1.012e+09, 1.013e+09, 1.014e+09, 1.015e+09, 1.016e+09, 1.017e+09] freq0 Frequency: 1.01e+09 nrow=10 ddid = 1 spwid = 1 polid = 1 freqs = [1.01e+09, 1.011e+09, 1.012e+09, 1.013e+09, 1.014e+09, 1.015e+09, 1.016e+09, 1.017e+09] freq0 Frequency: 1.01e+09 nrow=5 ddid = 1 spwid = 1 polid = 1 freqs = [1.01e+09, 1.011e+09, 1.012e+09, 1.013e+09, 1.014e+09, 1.015e+09, 1.016e+09, 1.017e+09] freq0 Frequency: 1.01e+09 nrow=25 ddid = 2 spwid = 2 polid = 0 freqs = [1.02e+09, 1.021e+09, 1.022e+09, 1.023e+09, 1.024e+09, 1.025e+09, 1.026e+09, 1.027e+09] freq0 Frequency: 1.02e+09 nrow=20 ddid = 2 spwid = 2 polid = 0 freqs = [1.02e+09, 1.021e+09, 1.022e+09, 1.023e+09, 1.024e+09, 1.025e+09, 1.026e+09, 1.027e+09] freq0 Frequency: 1.02e+09 nrow=15 ddid = 2 spwid = 2 polid = 0 freqs = [1.02e+09, 1.021e+09, 1.022e+09, 1.023e+09, 1.024e+09, 1.025e+09, 1.026e+09, 1.027e+09] freq0 Frequency: 1.02e+09 nrow=10 ddid = 2 spwid = 2 polid = 0 freqs = [1.02e+09, 1.021e+09, 1.022e+09, 1.023e+09, 1.024e+09, 1.025e+09, 1.026e+09, 1.027e+09] freq0 Frequency: 1.02e+09 nrow=5 ddid = 2 spwid = 2 polid = 0 freqs = [1.02e+09, 1.021e+09, 1.022e+09, 1.023e+09, 1.024e+09, 1.025e+09, 1.026e+09, 1.027e+09] freq0 Frequency: 1.02e+09 nrow=25 ddid = 3 spwid = 3 polid = 1 freqs = [1.03e+09, 1.031e+09, 1.032e+09, 1.033e+09, 1.034e+09, 1.035e+09, 1.036e+09, 1.037e+09] freq0 Frequency: 1.03e+09 nrow=20 ddid = 3 spwid = 3 polid = 1 freqs = [1.03e+09, 1.031e+09, 1.032e+09, 1.033e+09, 1.034e+09, 1.035e+09, 1.036e+09, 1.037e+09] freq0 Frequency: 1.03e+09 nrow=15 ddid = 3 spwid = 3 polid = 1 freqs = [1.03e+09, 1.031e+09, 1.032e+09, 1.033e+09, 1.034e+09, 1.035e+09, 1.036e+09, 1.037e+09] freq0 Frequency: 1.03e+09 nrow=10 ddid = 3 spwid = 3 polid = 1 freqs = [1.03e+09, 1.031e+09, 1.032e+09, 1.033e+09, 1.034e+09, 1.035e+09, 1.036e+09, 1.037e+09] freq0 Frequency: 1.03e+09 nrow=5 ddid = 3 spwid = 3 polid = 1 freqs = [1.03e+09, 1.031e+09, 1.032e+09, 1.033e+09, 1.034e+09, 1.035e+09, 1.036e+09, 1.037e+09] freq0 Frequency: 1.03e+09 nrow=25 ddid = 4 spwid = 4 polid = 0 freqs = [1.04e+09, 1.041e+09, 1.042e+09, 1.043e+09, 1.044e+09, 1.045e+09, 1.046e+09, 1.047e+09] freq0 Frequency: 1.04e+09 nrow=20 ddid = 4 spwid = 4 polid = 0 freqs = [1.04e+09, 1.041e+09, 1.042e+09, 1.043e+09, 1.044e+09, 1.045e+09, 1.046e+09, 1.047e+09] freq0 Frequency: 1.04e+09 nrow=15 ddid = 4 spwid = 4 polid = 0 freqs = [1.04e+09, 1.041e+09, 1.042e+09, 1.043e+09, 1.044e+09, 1.045e+09, 1.046e+09, 1.047e+09] freq0 Frequency: 1.04e+09 nrow=10 ddid = 4 spwid = 4 polid = 0 freqs = [1.04e+09, 1.041e+09, 1.042e+09, 1.043e+09, 1.044e+09, 1.045e+09, 1.046e+09, 1.047e+09] freq0 Frequency: 1.04e+09 nrow=5 ddid = 4 spwid = 4 polid = 0 freqs = [1.04e+09, 1.041e+09, 1.042e+09, 1.043e+09, 1.044e+09, 1.045e+09, 1.046e+09, 1.047e+09] freq0 Frequency: 1.04e+09 Iteration with TIME sorting =========================== Skipping row Skipping row Skipping row Skipping row Skipping row Skipping row Skipping row Skipping row Skipping row nrow=75 ddid = 0 spwid = 0 polid = 0 freqs = [1e+09, 1.001e+09, 1.002e+09, 1.003e+09, 1.004e+09, 1.005e+09, 1.006e+09, 1.007e+09] freq0 Frequency: 1e+09 Skipping row Skipping row Skipping row nrow=75 ddid = 0 spwid = 0 polid = 0 freqs = [1e+09, 1.001e+09, 1.002e+09, 1.003e+09, 1.004e+09, 1.005e+09, 1.006e+09, 1.007e+09] freq0 Frequency: 1e+09 Skipping row Skipping row Skipping row nrow=75 ddid = 0 spwid = 0 polid = 0 freqs = [1e+09, 1.001e+09, 1.002e+09, 1.003e+09, 1.004e+09, 1.005e+09, 1.006e+09, 1.007e+09] freq0 Frequency: 1e+09 Skipping row Skipping row Skipping row nrow=75 ddid = 0 spwid = 0 polid = 0 freqs = [1e+09, 1.001e+09, 1.002e+09, 1.003e+09, 1.004e+09, 1.005e+09, 1.006e+09, 1.007e+09] freq0 Frequency: 1e+09 Skipping row nrow=75 ddid = 0 spwid = 0 polid = 0 freqs = [1e+09, 1.001e+09, 1.002e+09, 1.003e+09, 1.004e+09, 1.005e+09, 1.006e+09, 1.007e+09] freq0 Frequency: 1e+09 Skipping row nrow=75 ddid = 0 spwid = 0 polid = 0 freqs = [1e+09, 1.001e+09, 1.002e+09, 1.003e+09, 1.004e+09, 1.005e+09, 1.006e+09, 1.007e+09] freq0 Frequency: 1e+09 nrow=75 ddid = 0 spwid = 0 polid = 0 freqs = [1e+09, 1.001e+09, 1.002e+09, 1.003e+09, 1.004e+09, 1.005e+09, 1.006e+09, 1.007e+09] freq0 Frequency: 1e+09 nrow=75 ddid = 0 spwid = 0 polid = 0 freqs = [1e+09, 1.001e+09, 1.002e+09, 1.003e+09, 1.004e+09, 1.005e+09, 1.006e+09, 1.007e+09] freq0 Frequency: 1e+09 nrow=75 ddid = 0 spwid = 0 polid = 0 freqs = [1e+09, 1.001e+09, 1.002e+09, 1.003e+09, 1.004e+09, 1.005e+09, 1.006e+09, 1.007e+09] freq0 Frequency: 1e+09 nrow=75 ddid = 0 spwid = 0 polid = 0 freqs = [1e+09, 1.001e+09, 1.002e+09, 1.003e+09, 1.004e+09, 1.005e+09, 1.006e+09, 1.007e+09] freq0 Frequency: 1e+09 Skipping row Skipping row Skipping row Skipping row Skipping row Skipping row Skipping row Skipping row Skipping row nrow=75 ddid = 0 spwid = 0 polid = 0 freqs = [1e+09, 1.001e+09, 1.002e+09, 1.003e+09, 1.004e+09, 1.005e+09, 1.006e+09, 1.007e+09] freq0 Frequency: 1e+09 Skipping row Skipping row Skipping row nrow=75 ddid = 0 spwid = 0 polid = 0 freqs = [1e+09, 1.001e+09, 1.002e+09, 1.003e+09, 1.004e+09, 1.005e+09, 1.006e+09, 1.007e+09] freq0 Frequency: 1e+09 Skipping row Skipping row Skipping row nrow=75 ddid = 0 spwid = 0 polid = 0 freqs = [1e+09, 1.001e+09, 1.002e+09, 1.003e+09, 1.004e+09, 1.005e+09, 1.006e+09, 1.007e+09] freq0 Frequency: 1e+09 Skipping row Skipping row Skipping row nrow=75 ddid = 0 spwid = 0 polid = 0 freqs = [1e+09, 1.001e+09, 1.002e+09, 1.003e+09, 1.004e+09, 1.005e+09, 1.006e+09, 1.007e+09] freq0 Frequency: 1e+09 Skipping row nrow=75 ddid = 0 spwid = 0 polid = 0 freqs = [1e+09, 1.001e+09, 1.002e+09, 1.003e+09, 1.004e+09, 1.005e+09, 1.006e+09, 1.007e+09] freq0 Frequency: 1e+09 Skipping row nrow=75 ddid = 0 spwid = 0 polid = 0 freqs = [1e+09, 1.001e+09, 1.002e+09, 1.003e+09, 1.004e+09, 1.005e+09, 1.006e+09, 1.007e+09] freq0 Frequency: 1e+09 nrow=75 ddid = 0 spwid = 0 polid = 0 freqs = [1e+09, 1.001e+09, 1.002e+09, 1.003e+09, 1.004e+09, 1.005e+09, 1.006e+09, 1.007e+09] freq0 Frequency: 1e+09 nrow=75 ddid = 0 spwid = 0 polid = 0 freqs = [1e+09, 1.001e+09, 1.002e+09, 1.003e+09, 1.004e+09, 1.005e+09, 1.006e+09, 1.007e+09] freq0 Frequency: 1e+09 nrow=75 ddid = 0 spwid = 0 polid = 0 freqs = [1e+09, 1.001e+09, 1.002e+09, 1.003e+09, 1.004e+09, 1.005e+09, 1.006e+09, 1.007e+09] freq0 Frequency: 1e+09 nrow=75 ddid = 0 spwid = 0 polid = 0 freqs = [1e+09, 1.001e+09, 1.002e+09, 1.003e+09, 1.004e+09, 1.005e+09, 1.006e+09, 1.007e+09] freq0 Frequency: 1e+09 ######## Iteration with FIELD sorting =========================== nrow=75 fieldid = 0 nrow=75 fieldid = 1 nrow=75 fieldid = 2 nrow=75 fieldid = 3 nrow=75 fieldid = 4 nrow=75 fieldid = 0 nrow=75 fieldid = 1 nrow=75 fieldid = 2 nrow=75 fieldid = 3 nrow=75 fieldid = 4 Iteration with ANTENNA1 and FIELD sorting ======================================== nrow=25 fieldid = 0 nrow=20 fieldid = 0 nrow=15 fieldid = 0 nrow=10 fieldid = 0 nrow=5 fieldid = 0 nrow=25 fieldid = 1 nrow=20 fieldid = 1 nrow=15 fieldid = 1 nrow=10 fieldid = 1 nrow=5 fieldid = 1 nrow=25 fieldid = 2 nrow=20 fieldid = 2 nrow=15 fieldid = 2 nrow=10 fieldid = 2 nrow=5 fieldid = 2 nrow=25 fieldid = 3 nrow=20 fieldid = 3 nrow=15 fieldid = 3 nrow=10 fieldid = 3 nrow=5 fieldid = 3 nrow=25 fieldid = 4 nrow=20 fieldid = 4 nrow=15 fieldid = 4 nrow=10 fieldid = 4 nrow=5 fieldid = 4 nrow=25 fieldid = 0 nrow=20 fieldid = 0 nrow=15 fieldid = 0 nrow=10 fieldid = 0 nrow=5 fieldid = 0 nrow=25 fieldid = 1 nrow=20 fieldid = 1 nrow=15 fieldid = 1 nrow=10 fieldid = 1 nrow=5 fieldid = 1 nrow=25 fieldid = 2 nrow=20 fieldid = 2 nrow=15 fieldid = 2 nrow=10 fieldid = 2 nrow=5 fieldid = 2 nrow=25 fieldid = 3 nrow=20 fieldid = 3 nrow=15 fieldid = 3 nrow=10 fieldid = 3 nrow=5 fieldid = 3 nrow=25 fieldid = 4 nrow=20 fieldid = 4 nrow=15 fieldid = 4 nrow=10 fieldid = 4 nrow=5 fieldid = 4 Iteration with TIME sorting =========================== Skipping row Skipping row Skipping row Skipping row Skipping row Skipping row Skipping row Skipping row Skipping row nrow=75 fieldid = 0 Skipping row Skipping row Skipping row nrow=75 fieldid = 0 nrow=75 fieldid = 0 Skipping row Skipping row nrow=75 fieldid = 0 nrow=75 fieldid = 0 nrow=75 fieldid = 0 Skipping row nrow=75 fieldid = 0 nrow=75 fieldid = 0 nrow=75 fieldid = 0 nrow=75 fieldid = 0 nrow=75 fieldid = 0 nrow=75 fieldid = 0 nrow=75 fieldid = 0 nrow=75 fieldid = 0 nrow=75 fieldid = 0 Skipping row Skipping row Skipping row Skipping row Skipping row Skipping row Skipping row Skipping row Skipping row nrow=75 fieldid = 0 Skipping row Skipping row Skipping row nrow=75 fieldid = 0 nrow=75 fieldid = 0 Skipping row Skipping row nrow=75 fieldid = 0 nrow=75 fieldid = 0 nrow=75 fieldid = 0 Skipping row nrow=75 fieldid = 0 nrow=75 fieldid = 0 nrow=75 fieldid = 0 nrow=75 fieldid = 0 nrow=75 fieldid = 0 nrow=75 fieldid = 0 nrow=75 fieldid = 0 nrow=75 fieldid = 0 nrow=75 fieldid = 0 casacore-3.7.1/ms/MeasurementSets/test/tMSMainBuffer.cc000066400000000000000000000376671476623553700230420ustar00rootroot00000000000000//# tMSMainBuffer.cc: //# Copyright (C) 1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include // #include // #include // #include // #include // #include // #include // #include // #include // #include // #include // #include // #include // #include // #include int main() { // const String filename = "tMSMainBuffer_tmp.table"; // try { // const IPosition row1DataShape(2, 2, 8); // const uInt nCat = 2; // const IPosition row1CatShape(3, row1DataShape(0), row1DataShape(1), nCat); // const IPosition row1SigmaShape(1, row1DataShape(0)); // const IPosition row4DataShape(2, 4, 7); // const IPosition row4CatShape(3, row4DataShape(0), row4DataShape(1), nCat); // const IPosition row4SigmaShape(1, row4DataShape(0)); // // Check the default constructor // MSMainBuffer newBuffer; // // test the ok function. // AlwaysAssert(newBuffer.ok(), AipsError); // // test the addRow & nrow functions. // AlwaysAssert(newBuffer.nrow() == 0, AipsError); // newBuffer.addRow(20, 4, 16); // AlwaysAssert(newBuffer.ok(), AipsError); // AlwaysAssert(newBuffer.nrow() == 20, AipsError); // { // MSMainBuffer buffer(nCat); // { // test the addRow & nrow functions. // AlwaysAssert(buffer.nrow() == 0, AipsError); // buffer.addRow(1, row1DataShape(0), row1DataShape(1)); // AlwaysAssert(buffer.nrow() == 1, AipsError); // buffer.addRow(4, row4DataShape(0), row4DataShape(1)); // AlwaysAssert(buffer.nrow() == 5, AipsError); // } // { // test the antenna1 functions. // AlwaysAssert(buffer.antenna1()(0) == -1, AipsError); // AlwaysAssert(buffer.antenna1()(4) == -1, AipsError); // buffer.antenna1().put(0, 0); // buffer.antenna1().put(4, 1); // } // { // test the antenna2 functions. // AlwaysAssert(buffer.antenna2()(0) == -1, AipsError); // AlwaysAssert(buffer.antenna2()(4) == -1, AipsError); // buffer.antenna2().put(0, 2); // buffer.antenna2().put(4, 3); // } // { // test the arrayId functions. // AlwaysAssert(buffer.arrayId()(0) == 1, AipsError); // AlwaysAssert(buffer.arrayId()(4) == 1, AipsError); // buffer.arrayId().put(0, 4); // buffer.arrayId().put(4, 2); // } // { // test the dataDescId functions. // AlwaysAssert(buffer.dataDescId()(0) == -1, AipsError); // AlwaysAssert(buffer.dataDescId()(4) == -1, AipsError); // buffer.dataDescId().put(0, 10); // buffer.dataDescId().put(4, 11); // } // { // test the exposure functions. // AlwaysAssert(buffer.exposure()(0) < 0.0, AipsError); // AlwaysAssert(buffer.exposure()(4) < 0.0, AipsError); // buffer.exposure().put(0, 10.0); // buffer.exposure().put(4, 20.0); // } // { // test the feed1 functions. // AlwaysAssert(buffer.feed1()(0) == 1, AipsError); // AlwaysAssert(buffer.feed1()(4) == 1, AipsError); // buffer.feed1().put(0, 4); // buffer.feed1().put(4, 5); // } // { // test the feed2 functions. // AlwaysAssert(buffer.feed2()(0) == 1, AipsError); // AlwaysAssert(buffer.feed2()(4) == 1, AipsError); // buffer.feed2().put(0, 2); // buffer.feed2().put(4, 3); // } // { // test the fieldId functions. // AlwaysAssert(buffer.fieldId()(0) == -1, AipsError); // AlwaysAssert(buffer.fieldId()(4) == -1, AipsError); // buffer.fieldId().put(0, 13); // buffer.fieldId().put(4, 14); // } // { // test the flag functions. // AlwaysAssert(buffer.flag()(0).shape().isEqual(row1DataShape), // AipsError); // AlwaysAssert(allEQ(buffer.flag()(0), False), AipsError); // AlwaysAssert(buffer.flag()(4).shape().isEqual(row4DataShape), // AipsError); // AlwaysAssert(allEQ(buffer.flag()(4), False), AipsError); // buffer.flag().put(0, Matrix(row1DataShape, True)); // Matrix flag(row4DataShape, True); // flag(0, 0) = False; // buffer.flag().put(4, flag); // } // { // test the flagCategory functions. // AlwaysAssert(buffer.flagCategory()(0).shape().isEqual(row1CatShape), // AipsError); // AlwaysAssert(allEQ(buffer.flagCategory()(0), False), AipsError); // AlwaysAssert(buffer.flagCategory()(4).shape().isEqual(row4CatShape), // AipsError); // AlwaysAssert(allEQ(buffer.flagCategory()(4), False), AipsError); // buffer.flagCategory().put(0, Cube(row1CatShape, True)); // Cube flag(row4CatShape, True); // flag(0, 0, 0) = False; // buffer.flagCategory().put(3, flag); // buffer.flagCategory().put(4, flag); // } // { // test the flagRow functions. // AlwaysAssert(buffer.flagRow()(0) == False, AipsError); // AlwaysAssert(buffer.flagRow()(4) == False, AipsError); // buffer.flagRow().put(2, True); // buffer.flagRow().put(3, True); // } // { // test the interval functions. // AlwaysAssert(buffer.interval()(0) < 0.0, AipsError); // AlwaysAssert(buffer.interval()(4) < 0.0, AipsError); // buffer.interval().put(0, 9.0); // buffer.interval().put(4, 19.0); // } // { // test the observationId functions. // AlwaysAssert(buffer.observationId()(0) == 1, AipsError); // AlwaysAssert(buffer.observationId()(4) == 1, AipsError); // buffer.observationId().put(0, 21); // buffer.observationId().put(4, 22); // } // { // test the processorId functions. // AlwaysAssert(buffer.processorId()(0) == 1, AipsError); // AlwaysAssert(buffer.processorId()(4) == 1, AipsError); // buffer.processorId().put(0, 23); // buffer.processorId().put(4, 24); // } // { // test the scanNumber functions. // AlwaysAssert(buffer.scanNumber()(0) == 1, AipsError); // AlwaysAssert(buffer.scanNumber()(4) == 1, AipsError); // buffer.scanNumber().put(0, 25); // buffer.scanNumber().put(4, 26); // } // { // test the sigma functions. // AlwaysAssert(buffer.sigma()(0).shape().isEqual(row1SigmaShape), // AipsError); // AlwaysAssert(allNear(buffer.sigma()(0), 1.0f, C::flt_epsilon), // AipsError); // AlwaysAssert(buffer.sigma()(4).shape().isEqual(row4SigmaShape), // AipsError); // AlwaysAssert(allNear(buffer.sigma()(4), 1.0f, C::flt_epsilon), // AipsError); // buffer.sigma().put(0, Vector(row1SigmaShape, 2.0)); // Vector sigma(row4SigmaShape, 3.0); // sigma(0) = 4.0; // buffer.sigma().put(4, sigma); // } // { // test the state_id functions. // AlwaysAssert(buffer.stateId()(0) == 1, AipsError); // AlwaysAssert(buffer.stateId()(4) == 1, AipsError); // buffer.stateId().put(0, 32); // buffer.stateId().put(4, 33); // } // { // test the time functions. // AlwaysAssert(near(buffer.time()(0), 0.0f), AipsError); // AlwaysAssert(near(buffer.time()(4), 0.0f), AipsError); // buffer.time().put(0, 990.0); // buffer.time().put(4, 991.0); // } // { // test the time_centroid functions. // AlwaysAssert(near(buffer.timeCentroid()(0), 0.0f), AipsError); // AlwaysAssert(near(buffer.timeCentroid()(4), 0.0f), AipsError); // buffer.timeCentroid().put(0, 992.0); // buffer.timeCentroid().put(4, 993.0); // } // { // test the uvw functions. // Vector uvw(3, 1E100); // AlwaysAssert(buffer.uvw()(0).shape().isEqual(IPosition(1,3)), // AipsError); // AlwaysAssert(allNear(buffer.uvw()(0), uvw, C::dbl_epsilon), AipsError); // AlwaysAssert(buffer.uvw()(4).shape().isEqual(IPosition(1,3)), // AipsError); // AlwaysAssert(allNear(buffer.uvw()(4), uvw, C::dbl_epsilon), AipsError); // uvw = 0.0; // buffer.uvw().put(0, uvw); // uvw(0) = 10.0; // buffer.uvw().put(4, uvw); // } // { // test the weight functions. // AlwaysAssert(allNear(buffer.weight()(0), 1.0f, C::flt_epsilon), // AipsError); // AlwaysAssert(allNear(buffer.weight()(4), 1.0f, C::flt_epsilon), // AipsError); // buffer.weight().put(0, Vector(row1SigmaShape, 0.1)); // Vector weight(row4SigmaShape, 0.2); // weight(0) = 0.4; // buffer.weight().put(4, weight); // } // { // Check the assignment operator & copy constructor // MSMainBuffer otherBuffer(buffer); // AlwaysAssert(otherBuffer.ok(), AipsError); // AlwaysAssert(otherBuffer.nrow() == 5, AipsError); // newBuffer = otherBuffer; // AlwaysAssert(newBuffer.ok(), AipsError); // // Check the reference semantics by adding data here and seeing if it // // is mirrored into the newBuffer object. // buffer.antenna1().put(1, 100); // buffer.fieldId().put(1, 101); // buffer.flagRow().put(1, True); // // Save the buffer to disk // buffer.save(filename, True); // } // } // { // check the data has not been lost. // AlwaysAssert(newBuffer.nrow() == 5, AipsError); // AlwaysAssert(newBuffer.antenna1()(0) == 0, AipsError); // AlwaysAssert(newBuffer.antenna1()(4) == 1, AipsError); // AlwaysAssert(newBuffer.antenna2()(0) == 2, AipsError); // AlwaysAssert(newBuffer.antenna2()(4) == 3, AipsError); // AlwaysAssert(newBuffer.arrayId()(0) == 4, AipsError); // AlwaysAssert(newBuffer.arrayId()(4) == 2, AipsError); // AlwaysAssert(newBuffer.dataDescId()(0) == 10, AipsError); // AlwaysAssert(newBuffer.dataDescId()(4) == 11, AipsError); // AlwaysAssert(near(newBuffer.exposure()(0), 10.0f), AipsError); // AlwaysAssert(near(newBuffer.exposure()(4), 20.0f), AipsError); // AlwaysAssert(newBuffer.feed1()(0) == 4, AipsError); // AlwaysAssert(newBuffer.feed1()(4) == 5, AipsError); // AlwaysAssert(newBuffer.feed2()(0) == 2, AipsError); // AlwaysAssert(newBuffer.feed2()(4) == 3, AipsError); // AlwaysAssert(newBuffer.fieldId()(0) == 13, AipsError); // AlwaysAssert(newBuffer.fieldId()(4) == 14, AipsError); // AlwaysAssert(newBuffer.flag()(0).shape().isEqual(row1DataShape), // AipsError); // AlwaysAssert(allEQ(newBuffer.flag()(0), True), AipsError); // { // Matrix flag(row4DataShape, True); // flag(0,0) = False; // AlwaysAssert(newBuffer.flag()(4).shape().isEqual(row4DataShape), // AipsError); // AlwaysAssert(allEQ(newBuffer.flag()(4), flag), AipsError); // } // AlwaysAssert(newBuffer.flagCategory()(0).shape().isEqual(row1CatShape), // AipsError); // AlwaysAssert(allEQ(newBuffer.flagCategory()(0), True), AipsError); // { // Cube flag(row4CatShape, True); // flag(0,0, 0) = False; // AlwaysAssert(newBuffer.flagCategory()(4).shape().isEqual(row4CatShape), // AipsError); // AlwaysAssert(allEQ(newBuffer.flagCategory()(4), flag), AipsError); // } // AlwaysAssert(newBuffer.flagRow()(2) == True, AipsError); // AlwaysAssert(newBuffer.flagRow()(3) == True, AipsError); // AlwaysAssert(near(newBuffer.interval()(0), 9.0f), AipsError); // AlwaysAssert(near(newBuffer.interval()(4), 19.0f), AipsError); // AlwaysAssert(newBuffer.observationId()(0) == 21, AipsError); // AlwaysAssert(newBuffer.observationId()(4) == 22, AipsError); // AlwaysAssert(newBuffer.processorId()(0) == 23, AipsError); // AlwaysAssert(newBuffer.processorId()(4) == 24, AipsError); // AlwaysAssert(newBuffer.scanNumber()(0) == 25, AipsError); // AlwaysAssert(newBuffer.scanNumber()(4) == 26, AipsError); // AlwaysAssert(newBuffer.sigma()(0).shape().isEqual(row1SigmaShape), // AipsError); // AlwaysAssert(allNear(newBuffer.sigma()(0), 2.0f, C::flt_epsilon), // AipsError); // { // Vector sigma(row4SigmaShape, 3.0); // sigma(0) = 4.0; // AlwaysAssert(newBuffer.sigma()(4).shape().isEqual(row4SigmaShape), // AipsError); // AlwaysAssert(allNear(newBuffer.sigma()(4), sigma, C::flt_epsilon), // AipsError); // } // AlwaysAssert(newBuffer.stateId()(0) == 32, AipsError); // AlwaysAssert(newBuffer.stateId()(4) == 33, AipsError); // AlwaysAssert(near(newBuffer.time()(0), 990.0f), AipsError); // AlwaysAssert(near(newBuffer.time()(4), 991.0f), AipsError); // AlwaysAssert(near(newBuffer.timeCentroid()(0), 992.0f), AipsError); // AlwaysAssert(near(newBuffer.timeCentroid()(4), 993.0f), AipsError); // { // Vector uvw(3, 0.0); // AlwaysAssert(newBuffer.uvw()(0).shape().isEqual(IPosition(1,3)), // AipsError); // AlwaysAssert(allNear(newBuffer.uvw()(0), uvw, C::dbl_epsilon), // AipsError); // AlwaysAssert(newBuffer.uvw()(4).shape().isEqual(IPosition(1,3)), // AipsError); // uvw(0) = 10.0; // AlwaysAssert(allNear(newBuffer.uvw()(4), uvw, C::dbl_epsilon), // AipsError); // } // AlwaysAssert(newBuffer.weight()(0).shape().isEqual(row1SigmaShape), // AipsError); // AlwaysAssert(allNear(newBuffer.weight()(0), 0.1f, C::flt_epsilon), // AipsError); // { // Vector weight(row4SigmaShape, 0.2); // weight(0) = 0.4; // AlwaysAssert(newBuffer.weight()(4).shape().isEqual(row4SigmaShape), // AipsError); // AlwaysAssert(allNear(newBuffer.weight()(4), weight, C::flt_epsilon), // AipsError); // } // // check the reference semantics // AlwaysAssert(newBuffer.antenna1()(1) == 100, AipsError); // AlwaysAssert(newBuffer.fieldId()(1) == 101, AipsError); // AlwaysAssert(newBuffer.flagRow()(1) == True, AipsError); // } // { // Check the isValid functions // AlwaysAssert(newBuffer.isValid(True) == True, AipsError); // AlwaysAssert(newBuffer.isValid(3u) == False, AipsError); // AlwaysAssert(newBuffer.isValid(4u) == True, AipsError); // AlwaysAssert(newBuffer.isValid() == False, AipsError); // } // { // Check the match functions // } // } // catch (std::exception x) { // cerr << x.what() << endl; // cout << "FAIL" << endl; // return 1; // } // try { // // Check that the Table ended up on disk (after the save function). This // // line may fail if the MS ctr checks for the existance of subtables (it // // doesn't at the moment). // MS ms(filename); // ms.markForDelete(); // } // catch (std::exception x) { // cerr << x.what() << endl; // cout << "FAIL" << endl; // return 1; // } // cout << "OK" << endl; return 3; //untested } // Local Variables: // compile-command: "gmake OPTLIB=1 XLIBLIST=0 tMSMainBuffer" // End: casacore-3.7.1/ms/MeasurementSets/test/tMSMainBuffer.run000066400000000000000000000000421476623553700232320ustar00rootroot00000000000000#! /bin/sh echo "UNTESTED" exit 3 casacore-3.7.1/ms/MeasurementSets/test/tMSPolBuffer.cc000066400000000000000000000172271476623553700226760ustar00rootroot00000000000000//# tMSPolBuffer.cc: //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include // #include // #include // #include // #include // #include // #include // #include // #include // #include // #include // // #include // // #include // #include int main() { // const String filename = "tMSPolBuffer_tmp.table"; // try { // // Check the default constructor // MSPolarizationBuffer newBuffer; // // test the ok function. // AlwaysAssert(newBuffer.ok(), AipsError); // // test the addRow & nrow functions. // AlwaysAssert(newBuffer.nrow() == 0, AipsError); // newBuffer.addRow(20, 1); // AlwaysAssert(newBuffer.ok(), AipsError); // AlwaysAssert(newBuffer.nrow() == 20, AipsError); // { // MSPolarizationBuffer polBuffer; // { // test the addRow & nrow functions. // AlwaysAssert(polBuffer.nrow() == 0, AipsError); // polBuffer.addRow(1, 1); // AlwaysAssert(polBuffer.nrow() == 1, AipsError); // polBuffer.addRow(4, 4); // AlwaysAssert(polBuffer.nrow() == 5, AipsError); // } // { // test the numCorr functions. // AlwaysAssert(polBuffer.numCorr()(0) == 1, AipsError); // AlwaysAssert(polBuffer.numCorr()(4) == 4, AipsError); // } // { // test the corrType functions. // AlwaysAssert(polBuffer.corrType()(0).shape() == IPosition(1, 1), // AipsError); // AlwaysAssert(allEQ(polBuffer.corrType()(0), // static_cast(Stokes::Undefined)), AipsError); // AlwaysAssert(polBuffer.corrType()(4).shape() == IPosition(1, 4), // AipsError); // AlwaysAssert(allEQ(polBuffer.corrType()(4), // static_cast(Stokes::Undefined)), AipsError); // polBuffer.corrType() // .put(0, Vector(1, static_cast(Stokes::RR))); // Vector ct(4); // ct(0) = static_cast(Stokes::XX); // ct(1) = static_cast(Stokes::XY); // ct(2) = static_cast(Stokes::YY); // ct(3) = static_cast(Stokes::YX); // polBuffer.corrType().put(4, ct); // } // { // test the corrProduct functions. // AlwaysAssert(polBuffer.corrProduct()(0).shape() == IPosition(2, 2, 1), // AipsError); // AlwaysAssert(allEQ(polBuffer.corrProduct()(0), 0), AipsError); // AlwaysAssert(polBuffer.corrProduct()(4).shape() == IPosition(2, 2, 4), // AipsError); // AlwaysAssert(allEQ(polBuffer.corrProduct()(4), 0), AipsError); // polBuffer.corrProduct().put(0, Matrix(2, 1, 1)); // Matrix pr(2, 4, 0); // pr(1,1) = pr(2,0) = pr(2,1) = pr(3,0) = 1; // polBuffer.corrProduct().put(4, pr); // } // { // test the flagRow functions. // AlwaysAssert(polBuffer.flagRow()(0) == False, AipsError); // AlwaysAssert(polBuffer.flagRow()(4) == False, AipsError); // polBuffer.flagRow().put(3, True); // } // { // Check the assignment operator & copy constructor // MSPolarizationBuffer otherBuffer(polBuffer); // AlwaysAssert(otherBuffer.ok(), AipsError); // AlwaysAssert(otherBuffer.nrow() == 5, AipsError); // newBuffer = otherBuffer; // AlwaysAssert(newBuffer.ok(), AipsError); // // Check the reference semantics by adding data here and seeing if it // // is mirrored into the newBuffer object. // polBuffer.corrType() // .put(1, Vector(4, static_cast(Stokes::I))); // polBuffer.corrProduct().put(1, Matrix(2, 4, 1)); // polBuffer.flagRow().put(1, True); // // Save the buffer to disk // polBuffer.save(filename, True); // } // } // { // check the data has not been lost. // AlwaysAssert(newBuffer.nrow() == 5, AipsError); // AlwaysAssert(newBuffer.numCorr()(0) == 1, AipsError); // AlwaysAssert(newBuffer.numCorr()(4) == 4, AipsError); // AlwaysAssert(newBuffer.corrType()(0).shape() == IPosition(1, 1), // AipsError); // AlwaysAssert(allEQ(newBuffer.corrType()(0), // static_cast(Stokes::RR)), AipsError); // AlwaysAssert(newBuffer.corrType()(4).shape() == IPosition(1, 4), // AipsError); // Vector ct(4); // ct(0) = static_cast(Stokes::XX); // ct(1) = static_cast(Stokes::XY); // ct(2) = static_cast(Stokes::YY); // ct(3) = static_cast(Stokes::YX); // AlwaysAssert(allEQ(newBuffer.corrType()(4), ct), AipsError); // AlwaysAssert(newBuffer.corrProduct()(0).shape() == IPosition(2, 2, 1), // AipsError); // AlwaysAssert(allEQ(newBuffer.corrProduct()(0), 1), AipsError); // AlwaysAssert(newBuffer.corrProduct()(4).shape() == IPosition(2, 2, 4), // AipsError); // Matrix pr(2, 4, 0); // pr(1,1) = pr(2,0) = pr(2,1) = pr(3,0) = 1; // AlwaysAssert(allEQ(newBuffer.corrProduct()(4), pr), AipsError); // AlwaysAssert(newBuffer.flagRow()(0) == False, AipsError); // AlwaysAssert(newBuffer.flagRow()(3) == True, AipsError); // AlwaysAssert(newBuffer.flagRow()(4) == False, AipsError); // // check the reference semantics // AlwaysAssert(allEQ(newBuffer.corrType()(1), // static_cast(Stokes::I)), AipsError); // AlwaysAssert(allEQ(newBuffer.corrProduct()(1), 1), AipsError); // AlwaysAssert(newBuffer.flagRow()(1) == True, AipsError); // } // { // Check the isValid functions // AlwaysAssert(newBuffer.isValid(True) == False, AipsError); // AlwaysAssert(newBuffer.isValid(4u) == True, AipsError); // AlwaysAssert(newBuffer.isValid(3u) == False, AipsError); // AlwaysAssert(newBuffer.isValid(2u) == False, AipsError); // AlwaysAssert(newBuffer.isValid() == False, AipsError); // } // { // Check the match functions // } // } // catch (std::exception x) { // cerr << x.what() << endl; // cout << "FAIL" << endl; // return 1; // } // try { // // Check that the Table ended up on disk (after the save function). // MSPolarization ms(filename); // ms.markForDelete(); // } // catch (std::exception x) { // cerr << x.what() << endl; // cout << "FAIL" << endl; // return 1; // } // cout << "OK" << endl; return 3; //untested } // Local Variables: // compile-command: "gmake OPTLIB=1 XLIBLIST=0 tMSPolBuffer" // End: casacore-3.7.1/ms/MeasurementSets/test/tMSPolBuffer.run000066400000000000000000000000421476623553700231000ustar00rootroot00000000000000#! /bin/sh echo "UNTESTED" exit 3 casacore-3.7.1/ms/MeasurementSets/test/tMeasurementSet.cc000066400000000000000000000457571476623553700235240ustar00rootroot00000000000000//# tMeasurementSet.cc : this program tests the MeasurementSet class //# Copyright (C) 1995,1996,1997,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // test functions, all return the number of errors unless otherwise stated // test PredefinedColumns static functions in MeasurementSet uInt tColumnStatics() { // ensure that the conversions are consistent uInt errCount = 0; for (Int i=1;i("test_column")); td.defineHypercolumn ("TiledData", 3, stringToVector("FLOAT_DATA")); SetupNewTable setup(sdmsName, td, Table::New); StManAipsIO aipsioman; TiledShapeStMan tsm("TiledData", IPosition(3,2)); setup.bindAll(aipsioman); setup.bindColumn("FLOAT_DATA", tsm); // small table, ten rows MeasurementSet ms(setup, 10); Record dminfo = ms.dataManagerInfo(); // Check that the CompressFloat engine is created. Bool fnd = False; for (uInt i=0; i vec = dminfo.subRecord(i).asArrayString ("COLUMNS"); if (vec.nelements() == 1 && vec(0) == "FLOAT_DATA") { fnd = True; } } } AlwaysAssertExit(fnd); AlwaysAssertExit (ms.isColumnStored ("FLOAT_DATA_COMPRESSED")); AlwaysAssertExit (ms.isColumnStored ("FLOAT_DATA_SCALE")); AlwaysAssertExit (ms.isColumnStored ("FLOAT_DATA_OFFSET")); AlwaysAssertExit (! ms.isColumnStored ("FLOAT_DATA")); AlwaysAssertExit (ms.isColumnStored ("SIGMA")); // Check that the compressed column uses TiledShapeStMan. fnd = False; for (uInt i=0; i vec = dminfo.subRecord(i).asArrayString ("COLUMNS"); if (vec.nelements() == 1 && vec(0) == "FLOAT_DATA_COMPRESSED") { fnd = True; } } } AlwaysAssertExit(fnd); ms.createDefaultSubtables(Table::New); ArrayColumn fldata(ms,MS::columnName(MS::FLOAT_DATA)); ScalarColumn flrow(ms,MS::columnName(MS::FLAG_ROW)); for (Int i=0; i<10; i++) { Matrix arr(4,2); arr=Float(i); fldata.put(i,arr); flrow.put(i,False); } // verify that it is valid if (! ms.validate()) { cerr << "self validation failed" < writableColumn(1, MS::columnName(MS::TIME)); // this also tests the assignment operator MeasurementSet refCopyMS = ms.referenceCopy(refMSName, writableColumn); Vector colNames(refCopyMS.tableDesc().columnNames()); // tests below will be useful when we can open the table with Old for (uInt i=0;i 0) { cout << newErrors << " errors!" << endl; } else { cout << "ok." << endl; } } int main() { try { uInt errCount = 0; uInt newErrors; String msName = "tMeasurementSet_tmp.Table"; String refMSName = "tMeasurementSet_tmp.Ref-Table"; cout << "\nMS::PredefinedColumns - test of static functions ... "; newErrors = tColumnStatics(); checkErrors(newErrors); errCount += newErrors; cout << "\nMS::PredefinedKeywords - test of static functions ... "; newErrors = tKeywordStatics(); checkErrors(newErrors); errCount += newErrors; cout << "\naddColumnToDesc() test on all possible columns ... "; newErrors = tAddAllColumns(); checkErrors(newErrors); errCount += newErrors; cout << "\nMake a MS to test the non-static functions ... "; newErrors = tNonStatic(msName); checkErrors(newErrors); errCount += newErrors; cout << "\nTest other constructors ... "; newErrors = tConstructors(msName); checkErrors(newErrors); errCount += newErrors; cout << "\nTest referenceCopy() ... "; newErrors = tReferenceCopy(msName, refMSName); checkErrors(newErrors); errCount += newErrors; cout << "\nTest null MS ... "; newErrors = tNullMS(msName); checkErrors(newErrors); errCount += newErrors; cout << "\nTest exceptions" << endl; cout << "in Constructors ... "; newErrors = tSetupNewTabError(); checkErrors(newErrors); errCount += newErrors; cout << "in destructor ... "; newErrors = tDestructorError(msName); checkErrors(newErrors); errCount += newErrors; // delete the msName table Table ms(msName); ms.markForDelete(); if (errCount > 0) { cout << "tMeasurementSet ends with " << errCount << " errors." << endl; } else { cout << "tMeasurementSet ends successfully" << endl; } return errCount; } catch (std::exception& x) { cerr << x.what() << endl; } return 1; } casacore-3.7.1/ms/MeasurementSets/test/tStokesConverter.cc000066400000000000000000000075751476623553700237170ustar00rootroot00000000000000//# tStokesConverter.cc: test program for StokesConverter class //# Copyright (C) 1997,1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include int main() { Int err=0; try { StokesConverter sc; { Vector out(7),in(4); in(0)=Stokes::RR; in(1)=Stokes::LL; in(2)=Stokes::RL; in(3)=Stokes::LR; out(0)=Stokes::I; out(1)=Stokes::Q; out(2)=Stokes::U; out(3)=Stokes::V; out(4)=Stokes::Ptotal; out(5)=Stokes::Pangle; out(6)=Stokes::PFlinear; sc.setConversion(out,in); Vector datain(4),dataout(7); datain(0)=Complex(0.6,0.3); datain(1)=Complex(0.4,0.2); datain(2)=Complex(0.225,0.3); datain(3)=Complex(0.375,0.0); sc.convert(dataout,datain); if (!nearAbs(dataout(0),Complex(1.0,0.5),1.e-6)|| !nearAbs(dataout(1),Complex(0.6,0.3),1.e-6)|| !nearAbs(dataout(2),Complex(0.3,0.15),1.e-6)|| !nearAbs(dataout(3),Complex(0.2,0.1),1.e-6)|| !nearAbs(dataout(4),Complex(0.782624,0.0),1.e-6)|| !nearAbs(dataout(5),Complex(0.231824,0.0),1.e-6)|| !nearAbs(dataout(6),Complex(0.67082,0.0),1.e-6)) { err++; cerr << "dataout="< flagout, flagin(4); flagin.set(False); flagin(2)=True; sc.convert(flagout,flagin); if (flagout(0) || !flagout(1) || !flagout(2) || flagout(3) || !flagout(4) || !flagout(5) || !flagout(6)) { err++; cerr << "flagout="< #include #include #include #include #include #include #include using namespace casacore; using namespace std; void select (const String& msin, const String& out, const String& baseline, bool deep) { MeasurementSet ms(msin); MSSelection select; // Set given selection strings. if (!baseline.empty()) { select.setAntennaExpr (baseline); } // Create the selection table expression for the MS. TableExprNode node = select.toTableExprNode (&ms); // Make the selection and write the resulting RefTable. // If no selection was made, create it explicitly from all rows // to be sure there is a RefTable. Table mssel = ms(node); if (mssel.nrow() == ms.nrow()) { RowNumbers allRows(ms.nrow()); indgen (allRows); mssel = ms(allRows); } if (deep) { mssel.deepCopy (out, Table::New); cout << "Created MeasurementSet " << out; } else { mssel.rename (out, Table::New); cout << "Created RefTable " << out; } cout << " containing " << mssel.nrow() << " rows (out of " << ms.nrow() << ')' << endl; } // Copy (or symlink) directories that are not a subtable. // In that way possible instrument and sky model tables can be copied. void copyOtherDirs (const String& msName, const String& outName, bool deep) { // Get all table keywords. Table tab(msName); const TableRecord& keys = tab.keywordSet(); // Loop over all files in the MS directory. Directory dir(msName); DirectoryIterator iter(dir); for (DirectoryIterator iter(dir); !iter.pastEnd(); ++iter) { // Test if a directory basename (also via a symlink) is a subtable. // If not, it is an extra directory that needs to be copied. if (iter.file().isDirectory()) { String bname = iter.file().path().baseName(); if (!(keys.isDefined(bname) && keys.dataType(bname) == TpTable)) { if (deep) { Directory sdir(iter.file()); sdir.copyRecursive (outName + '/' + bname); cout << "Copied subdirectory " << bname << endl; } else { // Resolve a possible symlink created by another msselect. // Do it only one deep. Path newName; if (iter.file().isSymLink()) { newName = SymLink(iter.file()).readSymLink(); } else { newName = iter.file().path(); } // Create a symlink to the directory. SymLink slink(outName + '/' + bname); slink.create (newName.absoluteName(), False); cout << "Created symlink to subdirectory " << bname << endl; } } } } } int main (int argc, char* argv[]) { try { // enable input in no-prompt mode Input inputs(1); // define the input structure inputs.version("20130819GvD"); inputs.create ("in", "", "Name of input MeasurementSet", "string"); inputs.create ("out", "", "Name of output table", "string"); inputs.create ("deep", "false", "Is the output a deep copy of the MeasurementSet selection?", "bool"); inputs.create ("baseline", "", "Selection string for antennae and baselines", "string"); /* inputs.create ("time", "", "selection string for times", "string"); inputs.create ("uv", "", "selection string for uv distance", "string"); */ // Fill the input structure from the command line. inputs.readArguments (argc, argv); // Get and check the input specification. String msin (inputs.getString("in")); if (msin.empty()) { throw AipsError(" an input MeasurementSet must be given"); } // Get the output name. String out(inputs.getString("out")); if (out.empty()) { throw AipsError(" an output table name must be given"); } // Get the deep option. bool deep = inputs.getBool("deep"); // Get the baseline selection string. string baseline(inputs.getString("baseline")); // Do the selection and copying. select (msin, out, baseline, deep); copyOtherDirs (msin, out, deep); } catch (std::exception& x) { cerr << "Error: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/ms/apps/readms.cc000066400000000000000000000730271476623553700165010ustar00rootroot00000000000000//# readms.cc : this program reads one or more MeasurementSets //# Copyright (C) 2017 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace casacore; // Define the global variables shared between the main functions. bool myIsHDF5; bool myReadWeightSpectrum; bool myReadData; bool myReadFloatData; bool myReadFlag; bool myReadRowWise; bool myDoSinglePart; int myNPart; int myNPol; int myStartChan; int myChanSize; int myNChan; int myCacheSizeData; int myCacheSizeFlag; int myCacheSizeWeight; String myMsName; String myBaselines; String mySelection; Vector myIterCols1; Vector myIterCols2; Vector mySortCols; void showHelp() { cout << "The program reads one or more MeasurementSets" << endl; cout << "Run as:" << endl; cout << " readms parm=value parm=value ..." << endl; cout << "Use readms -h to see the possible parameters." << endl; } bool readParms (int argc, char* argv[]) { // enable input in no-prompt mode Input params(1); // define the input structure params.version("2017Oct31GvD"); params.create ("nms", "0", "Number of MeasurementSets to read (0 means 1 MS without suffix)", "int"); params.create ("msname", "", "Name of the MeasurementSet (suffix _p is added if nms>0)", "string"); params.create ("data", "true", "Read DATA or FLOAT_DATA column?", "bool"); params.create ("flag", "true", "Read FLAG column?", "bool"); params.create ("weightspectrum", "false", "Read WEIGHT_SPECTRUM column?", "bool"); params.create ("npol", "0", "Number of polarizations to read (0=all, 1=XX, 2=XX/YY)", "int"); params.create ("startchan", "0", "First channel to read", "int"); params.create ("nchan", "0", "Number of channels to read (0=all)", "int"); params.create ("chansize", "0", "Number of channels to read jointly; allows channel iteration (0=all)", "int"); params.create ("baselines", "", "Baselines to read (CASA baseline selection)", "string"); params.create ("selection", "", "TaQL selection string", "string"); params.create ("iteration1", "DATA_DESC_ID", "Columns to iterate on before channel iteration", "string vector"); params.create ("iteration2", "TIME", "Columns to iterate on after channel iteration", "string vector"); params.create ("sort", "", "Columns to sort on (default none); . is iteration columns", "string vector"); params.create ("rowwise", "false", "Read the data row wise (thus a get per row)", "bool"); params.create ("datacachesize", "0", "TiledStMan cache size for DATA column (in tiles)", "int"); params.create ("flagcachesize", "0", "TiledStMan cache size for FLAG column (in tiles)", "int"); params.create ("weightcachesize", "0", "TiledStMan cache size for WEIGHT column (in tiles)", "int"); // Fill the input structure from the command line. params.readArguments (argc, argv); // Get the various parameters. myMsName = params.getString ("msname"); if (myMsName.empty()) { showHelp(); return false; } myStartChan = params.getInt ("startchan"); myChanSize = params.getInt ("chansize"); myNChan = params.getInt ("nchan"); myNPol = params.getInt ("npol"); myNPart = params.getInt ("nms"); myDoSinglePart = (myNPart == 0); if (myDoSinglePart) { myNPart = 1; } // Determine nr of bands per part (i.e., ms). if (myNPol == 0) myNPol = 4; AlwaysAssert (myNPol==1 || myNPol==2 || myNPol==4, AipsError); // Get remaining parameters. myBaselines = params.getString ("baselines"); mySelection = params.getString ("selection"); myReadData = params.getBool ("data"); myReadFloatData = myReadData; myReadFlag = params.getBool ("flag"); myReadWeightSpectrum = params.getBool ("weightspectrum"); myReadRowWise = params.getBool ("rowwise"); myCacheSizeData = params.getInt ("datacachesize"); myCacheSizeFlag = params.getInt ("flagcachesize"); myCacheSizeWeight = params.getInt ("weightcachesize"); myIterCols1 = stringToVector(params.getString ("iteration1")); myIterCols2 = stringToVector(params.getString ("iteration2")); mySortCols = stringToVector(params.getString ("sort")); if (mySortCols.size() == 1 && mySortCols[0] == ".") { mySortCols.resize (myIterCols1.size() + myIterCols2.size()); std::copy (myIterCols1.begin(), myIterCols1.end(), mySortCols.begin()); Vector scols(mySortCols(Slice(myIterCols1.size(), mySortCols.size() - myIterCols1.size()))); std::copy (myIterCols2.begin(), myIterCols2.end(), scols.begin()); } return true; } String makeMSName (int seqnr, const String& msName) { String name; if (msName.find ("%d") != String::npos) { name = String::format (msName.c_str(), seqnr); } else { name = msName; if (!myDoSinglePart) { name += String::format ("_p%d", seqnr); } } return name; } void showParmsHDF5 (const IPosition& tileShape, uInt tileSize, uInt nspw, const Record& attr) { cout << " nms = " << myNPart << " " << myMsName << endl; cout << " nthread = " << OMP::maxThreads() << endl; cout << " nant = " << attr.asInt("NAntenna") << " (" << attr.asInt("NBaseline") << " baselines)" << endl; cout << " nspw = " << nspw << endl; cout << " nfield = " << attr.asInt("NField"); int ntimefield = attr.asInt("NTimeField"); if (ntimefield > 0) { cout << " (" << ntimefield << "times per field)"; } cout << endl; cout << " nchan = " << myNChan << endl; cout << " startchan = " << myStartChan << endl; if (myChanSize > 0) { cout << " chansize = " << myChanSize << endl; } cout << " npol = " << myNPol << endl; cout << " rowwise = " << myReadRowWise << endl; cout << " readdata = " << myReadData << endl; cout << " readfloatdata = " << myReadFloatData << endl; cout << " readflag = " << myReadFlag << endl; cout << " readweightspectrum = " << myReadWeightSpectrum << endl; cout << " data tileshape = " << tileShape << " (tilesize = " << tileSize << " bytes)" << endl; } void showParms() { String name = makeMSName (0, myMsName); if (!Table::isReadable(name)) { myIsHDF5 = True; return; } Table tab(name); if (! tab.tableDesc().isColumn ("DATA")) { myReadData = False; } if (! tab.tableDesc().isColumn ("FLOAT_DATA")) { myReadFloatData = False; } if (! tab.tableDesc().isColumn ("WEIGHT_SPECTRUM")) { myReadWeightSpectrum = False; } Block parts = tab.getPartNames(); cout << " nms = " << myNPart << " " << myMsName; if (parts.size() > 1) { cout << " (MultiMS with " << parts.size() << " MSs)"; } cout << endl; cout << " nthread = " << OMP::maxThreads() << endl; // Since all parts are the same, only use the first one to determine sizes. MeasurementSet ms(parts[0]); TableIterator iter(ms, "TIME", TableIterator::Ascending, TableIterator::NoSort); Table tab1 = iter.table(); Int64 ntime = ms.nrow() / tab1.nrow(); Int64 nspw = tableCommand("select unique DATA_DESC_ID from $1", tab1).table().nrow(); Int64 nbl = tableCommand("select unique ANTENNA1,ANTENNA2 from $1", tab1).table().nrow(); Int64 nant = ms.antenna().nrow(); Int64 nfield = ms.field().nrow(); Int64 ntimefield = 0; if (Int64(tab1.nrow()) != nbl*nspw*nfield) { TableIterator iterfld(ms, "FIELD_ID", TableIterator::Ascending, TableIterator::NoSort); ntimefield = iterfld.table().nrow() / tab1.nrow(); } cout << " nant = " << nant << " (" << nbl << " baselines)" << endl; cout << " nspw = " << ms.spectralWindow().nrow() << " (" << nspw << " per ms)" << endl; cout << " nfield = " << ms.field().nrow(); if (ntimefield > 0) { cout << " (" << ntimefield << "times per field)"; } cout << endl; cout << " ntime = " << ntime << endl; IPosition dataShape; if (ms.tableDesc().isColumn("DATA")) { dataShape = ArrayColumn(ms, "DATA").shape(0); } else { dataShape = ArrayColumn(ms, "FLOAT_DATA").shape(0); } cout << " nchan = " << dataShape[1] << endl; cout << " startchan = " << myStartChan << endl; cout << " chansize = " << myChanSize << endl; cout << " npol = " << dataShape[0] << endl; cout << " rowwise = " << myReadRowWise << endl; cout << " readdata = " << myReadData << endl; cout << " readfloatdata = " << myReadFloatData << endl; cout << " readflag = " << myReadFlag << endl; cout << " readweightspectrum = " << myReadWeightSpectrum << endl; try { ROTiledStManAccessor acc(ms, ms.tableDesc().isColumn("DATA") ? "DATA" : "FLOAT_DATA", True); cout << " data tileshape = " << acc.tileShape(0) << " (tilesize = " << acc.bucketSize(0) << " bytes)" << endl; } catch (const AipsError&) { } const StorageOption& opt = ms.storageOption(); if (opt.option() == StorageOption::MultiFile || opt.option() == StorageOption::MultiHDF5) { cout << " multi" << (opt.option() == StorageOption::MultiFile ? "file" : "hdf5"); cout << " (blocksize = " << opt.blockSize() << " bytes)" << endl; } if (! myBaselines.empty()) { cout << "baseline selection = " << myBaselines << endl; } if (! mySelection.empty()) { cout << "TaQL selection = " << mySelection << endl; } cout << " iteration1 columns = " << myIterCols1 << endl; cout << " iteration2 columns = " << myIterCols2 << endl; if (mySortCols.size() > 0) { cout << " sort columns = " << mySortCols << endl; } ///possibly show ntimes, etc. of selection subset } void readRows (ArrayColumn& dataCol, ArrayColumn& floatDataCol, ArrayColumn& flagCol, ArrayColumn& weightCol) { if (myReadRowWise) { for (rownr_t row=0; row& dataCol, ArrayColumn& floatDataCol, ArrayColumn& flagCol, ArrayColumn& weightCol, const Slicer& slicer) { if (myReadRowWise) { for (rownr_t row=0; row data; Array floatData; Array flags; Array weights; ArrayColumn dataCol; if (myReadData) dataCol.attach(tab, "DATA"); ArrayColumn floatDataCol; if (myReadFloatData) floatDataCol.attach(tab, "FLOAT_DATA"); ArrayColumn flagCol(tab, "FLAG"); ArrayColumn weightSpectrumCol; if (myReadWeightSpectrum) weightSpectrumCol.attach(tab, "WEIGHT_SPECTRUM"); const RecordInterface& attr = tab.keywordSet().asRecord ("ATTR"); rownr_t ntoread = attr.asInt("NBaseline"); if (attr.asInt("NTimeField") == 0) ntoread *= attr.asInt("NField"); // Determine if to read by channel groups. niter = 0; IPosition shape = flagCol.shape(0); int lastchan = myStartChan + myNChan; if (myNChan == 0) { lastchan = shape[1]; } int chansize = myChanSize; if (chansize == 0) { chansize = shape[1] - myStartChan; } for (int fchan=myStartChan; fchan 0 || lchan < shape[1] || myNPol < shape[0]) { AlwaysAssert (fchan < shape[1], AipsError); int nchan = std::min(lchan, int(shape[1])) - fchan; IPosition stride(2,1,1); int npol = myNPol; if (npol < shape[0]) { stride[0] = 3; // achieves XX,YY if 4 pols are present } else { npol = shape[0]; } Slicer slicer(IPosition(2,0,fchan), IPosition(2,npol,nchan), stride); // Read the data per time step. if (myReadData) dataCol.getColumnRange (rowRange, slicer, data, True); if (myReadFloatData) floatDataCol.getColumnRange (rowRange, slicer, floatData, True); if (myReadFlag) flagCol.getColumnRange (rowRange, slicer, flags, True); if (myReadWeightSpectrum) weightSpectrumCol.getColumnRange (rowRange, slicer, weights, True); niter++; } else { if (myReadData) dataCol.getColumnRange (rowRange, data, True); if (myReadFloatData) floatDataCol.getColumnRange (rowRange, floatData, True); if (myReadFlag) flagCol.getColumnRange (rowRange, flags, True); if (myReadWeightSpectrum) weightSpectrumCol.getColumnRange (rowRange, weights, True); niter++; } } } // Do not iterate, but read the data per time step. for (rownr_t row=0; row itercols1(myIterCols1.size()); std::copy (myIterCols1.begin(), myIterCols1.end(), itercols1.begin()); Block itercols2(myIterCols2.size()); std::copy (myIterCols2.begin(), myIterCols2.end(), itercols2.begin()); TableIterator iter1(ms, itercols1, TableIterator::Ascending, TableIterator::NoSort); while (!iter1.pastEnd()) { Table tab1 (iter1.table()); IPosition shape = ArrayColumn(tab1, "FLAG").shape(0); int lastchan = myStartChan + myNChan; if (myNChan == 0) { lastchan = shape[1]; } int chansize = myChanSize; if (chansize == 0) { chansize = shape[1] - myStartChan; } for (int fchan=myStartChan; fchan flagCol(tab2, "FLAG"); ArrayColumn dataCol; if (myReadData) { dataCol.attach (tab2, "DATA"); } ArrayColumn floatDataCol; if (myReadFloatData) { floatDataCol.attach (tab2, "FLOAT_DATA"); } ArrayColumn weightCol; if (myReadWeightSpectrum) { weightCol.attach (tab2, "WEIGHT_SPECTRUM"); } IPosition shape = flagCol.shape(0); int lchan = fchan + chansize; if (fchan > 0 || lchan < shape[1] || myNPol < shape[0]) { AlwaysAssert (fchan < shape[1], AipsError); int nchan = std::min(lchan, int(shape[1])) - fchan; IPosition stride(2,1,1); int npol = myNPol; if (npol < shape[0]) { stride[0] = 3; // achieves XX,YY if 4 pols are present } else { npol = shape[0]; } Slicer slicer(IPosition(2,0,fchan), IPosition(2,npol,nchan), stride); readRows (dataCol, floatDataCol, flagCol, weightCol, slicer); } else { readRows (dataCol, floatDataCol, flagCol, weightCol); } iter2.next(); niter++; } } nrow += tab1.nrow(); iter1.next(); } return nrow; } void showCacheStatistics (const MeasurementSet& ms) { // Ignore exceptions (because datamanagers might be called differently). try { if (myReadData) { cout << "DATA: "; RODataManAccessor(ms, "DATA", True).showCacheStatistics (cout); } } catch (std::exception&) { } try { if (myReadFloatData) { cout << "FLOAT_DATA: "; RODataManAccessor(ms, "FLOAT_DATA", True).showCacheStatistics (cout); } } catch (std::exception&) { } try { if (myReadFlag) { cout << "FLAG: "; RODataManAccessor(ms, "FLAG", True).showCacheStatistics (cout); } } catch (std::exception&) { } try { if (myReadWeightSpectrum) { cout << "WEIGHT_SPECTRUM: "; RODataManAccessor(ms, "WEIGHT_SPECTRUM", True).showCacheStatistics (cout); } } catch (std::exception&) { } } void setTSMCacheSize (const Table& tab, const String& columnName, int cacheSize) { if (cacheSize > 0) { ROTiledStManAccessor acc(tab, columnName, True); for (uInt i=0; i& hflag, const std::shared_ptr& hdata, const std::shared_ptr& hfloatdata, const std::shared_ptr& hweight, Int64 row, Int64 nrow) { if (myReadRowWise) { IPosition shp(hflag->shape()); shp[2] = 1; Array data(shp); Array fdata(shp); Array flags(shp); Array weights(shp); IPosition s(3,0); IPosition e(shp-1); Slicer slicer(s, shp); for (Int64 r=0; rget(slicer, data); } if (myReadFloatData) { hfloatdata->get(slicer, fdata); } if (myReadFlag) { hflag->get(slicer, flags); } if (myReadWeightSpectrum) { hweight->get(slicer, weights); } } } else { IPosition shp(hflag->shape()); shp[2] = nrow; Slicer slicer(IPosition(3,0,0,row), shp); if (myReadData) { Array data(shp); hdata->get (slicer, data); } if (myReadFloatData) { Array fdata(shp); hfloatdata->get (slicer, fdata); } if (myReadFlag) { Array flags(shp); hflag->get (slicer, flags); } if (myReadWeightSpectrum) { Array weights(shp); hweight->get (slicer, weights); } } } void readRowsHDF5 (const std::shared_ptr& hflag, const std::shared_ptr& hdata, const std::shared_ptr& hfloatdata, const std::shared_ptr& hweight, const Slicer& slicer) { IPosition shp(slicer.length()); IPosition s(slicer.start()); IPosition e(slicer.end()); Int64 srow = s[2]; Int64 erow = e[2]; if (myReadRowWise) { shp[2] = 1; Array data(shp); Array fdata(shp); Array flags(shp); Array weights(shp); // Make a slicer with length 1. s[2] = 1; Slicer slicer(s, shp); for (Int64 row=srow; row<=erow; ++row) { s[2] = row; e[2] = row; slicer.setStart (s); slicer.setEnd (e); if (myReadData) { hdata->get (slicer, data); } if (myReadFloatData) { hfloatdata->get (slicer, fdata); } if (myReadFlag) { hflag->get (slicer, flags); } if (myReadWeightSpectrum) { hweight->get (slicer, weights); } } } else { if (myReadData) { Array data(shp); hdata->get (slicer, data); } if (myReadFloatData) { Array fdata(shp); hfloatdata->get (slicer, fdata); } if (myReadFlag) { Array flags(shp); hflag->get (slicer, flags); } if (myReadWeightSpectrum) { Array weights(shp); hweight->get (slicer, weights); } } } Int64 readStepsHDF5 (const std::shared_ptr& hflag, const std::shared_ptr& hdata, const std::shared_ptr& hfloatdata, const std::shared_ptr& hweightspectrum, Int64 nrow, Int64& niter) { niter = 0; IPosition shape = hflag->shape(); int lastchan = myStartChan + myNChan; if (myNChan == 0) { lastchan = shape[1]; } int chansize = myChanSize; if (chansize == 0) { chansize = shape[1] - myStartChan; } for (int fchan=myStartChan; fchan 0 || lchan < shape[1] || myNPol < shape[0]) { AlwaysAssert (fchan < shape[1], AipsError); int nchan = std::min(lchan, int(shape[1])) - fchan; IPosition stride(3,1,1,1); int npol = myNPol; if (npol < shape[0]) { stride[0] = 3; // achieves XX,YY if 4 pols are present } else { npol = shape[0]; } Slicer slicer(IPosition(3,0,fchan,row), IPosition(3,npol,nchan,nrow), stride); // Read the data per time step. readRowsHDF5 (hflag, hdata, hfloatdata, hweightspectrum, slicer); } else { readRowsHDF5 (hflag, hdata, hfloatdata, hweightspectrum, row, nrow); } niter++; } } return shape[2]; } std::vector doHDF5 (int seqnr, const String& name) { Timer timer; std::vector res(2); res[0] = 0; HDF5File hfile(name, ByteIO::Old); // There is a group per spectral window. Get all groups. std::vector groupNames (HDF5Group::linkNames (hfile)); for (uInt i=0; i hdata; std::shared_ptr hfloatdata; std::shared_ptr hweightspectrum; std::shared_ptr hflag = std::make_shared(hspw, "FLAG", (Bool*)0); IPosition shape = hflag->shape(); IPosition tileShape = hflag->tileShape(); uInt tileSize = 0; uInt cacheSize = (shape[1] + tileShape[1] - 1) / tileShape[1]; if (myReadFlag) { hflag->setCacheSize (myCacheSizeFlag==0 ? cacheSize:myCacheSizeFlag); } try { hdata = std::make_shared(hspw, "DATA", (Complex*)0); tileShape = hdata->tileShape(); if (myReadData) { hdata->setCacheSize (myCacheSizeData==0 ? cacheSize:myCacheSizeData); tileSize = tileShape.product() * sizeof(Complex); myReadFloatData = False; } } catch (const std::exception&) { myReadData = False; } if (! myReadData) { try { hfloatdata = std::make_shared(hspw, "FLOAT_DATA", (Complex*)0); tileShape = hdata->tileShape(); if (myReadFloatData) { hfloatdata->setCacheSize (myCacheSizeData==0 ? cacheSize:myCacheSizeData); tileSize = tileShape.product() * sizeof(Float); } } catch (const std::exception&) { myReadFloatData = False; } } if (myReadWeightSpectrum) { hweightspectrum = std::make_shared(hspw, "WEIGHT_SPECTRUM", (float*)0); hweightspectrum->setCacheSize (myCacheSizeWeight==0 ? cacheSize:myCacheSizeWeight); } // Show some parms for the very first spw. if (seqnr == 0 && i == 0) { if (myNChan == 0) { myNChan = shape[1]; } showParmsHDF5 (tileShape, tileSize, groupNames.size(), attr); cout << "HDF5 cache size = " << cacheSize << endl; } // Determine nr of rows to read per time step. int ntoread = attr.asInt("NBaseline"); if (attr.asInt("NTimeField") > 0) ntoread *= attr.asInt("NField"); Int64 niter; Int64 nrow = readStepsHDF5 (hflag, hdata, hfloatdata, hweightspectrum, ntoread, niter); res[0] += nrow; timer.show ("Read " + String::toString(nrow) + " rows (in " + String::toString(niter) + " iterations) from MS " + name); } res[1] = res[0]; return res; } std::vector doOne (int seqnr, const String& msName) { // Form the MS name. // If it contains %d, use that to fill in the seqnr. // Otherwise append _seqnr to the name (unless a single part is done). String name = makeMSName (seqnr, msName); if (myIsHDF5) { return doHDF5 (seqnr, name); } vector res(2); // Open the MS. Timer timer; Table tab(name); // Set cache sizes where applicable. if (myReadData) { setTSMCacheSize (tab, "DATA", myCacheSizeData); } if (myReadFloatData) { setTSMCacheSize (tab, "FLOAT_DATA", myCacheSizeData); } if (myReadFlag) { setTSMCacheSize (tab, "FLAG", myCacheSizeFlag); } if (myReadWeightSpectrum) { setTSMCacheSize (tab, "WEIGHT_SPECTRUM", myCacheSizeWeight); } // Set the cache sizes if needed. res[0] = tab.nrow(); timer.show ("Opened MS " + name); timer.mark(); // Do the selection (if given). if (!mySelection.empty() || !myBaselines.empty()) { MeasurementSet ms(tab); MSSelection mssel(ms); if (!mySelection.empty()) { mssel.setTaQLExpr (mySelection); } if (! myBaselines.empty()) { mssel.setAntennaExpr (myBaselines); } tab = tab(mssel.getTEN()); // do the actual selection timer.show ("selection"); timer.mark(); } res[1] = tab.nrow(); if (! mySortCols.empty()) { Block keys(mySortCols.size()); std::copy (mySortCols.begin(), mySortCols.end(), keys.begin()); tab = tab.sort (keys); timer.show ("sort "); timer.mark(); } MeasurementSet ms(tab); Int64 niter; Int64 nrow = readSteps (ms, niter); timer.show ("Read " + String::toString(nrow) + " rows (in " + String::toString(niter) + " iterations) from MS " + msName); if (seqnr == 0) { showCacheStatistics (ms); } return res; } void doAll() { Int64 nrow = 0, selNrow = 0; int nthread = OMP::maxThreads(); #ifdef _OPENMP #pragma omp parallel for schedule(dynamic) #endif for (int i=0; i res = doOne (i, myMsName); if (i == 0) { nrow = res[0]; selNrow = res[1]; } } if (myNPart == 1) { cout << "Read 1 MS part" << endl; } else { cout << "Read " << myNPart << " MS parts in " << nthread << " threads" << endl; } cout << "For each part " << selNrow << " rows out of " << nrow << " have been read" << endl; } int main (int argc, char* argv[]) { #ifdef HAVE_MPI MPI_Init(0,0); #endif try { if (readParms (argc, argv)) { showParms(); doAll(); } } catch (const std::exception& x) { std::cerr << x.what() << std::endl; return 1; } #ifdef HAVE_MPI MPI_Finalize(); #endif return 0; } casacore-3.7.1/ms/apps/writems.cc000066400000000000000000002165441476623553700167230ustar00rootroot00000000000000//# writems.cc: Create one or MeasurementSets //# Copyright (C) 2017 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_ADIOS2 #include #endif #ifdef HAVE_MPI #include #endif #include #include #include #include #include #include #include #include using namespace casacore; using namespace std; // This struct contains the data items needed to fill a spectral window in // the main table in HDF5. struct HDF5Spw { std::shared_ptr spw; std::shared_ptr data; std::shared_ptr floatData; std::shared_ptr modelData; std::shared_ptr corrData; std::shared_ptr flag; std::shared_ptr weightSpectrum; std::shared_ptr metaData; }; struct HDF5MetaData { double time; double timeCentroid; double interval; double exposure; double uvw[3]; float weight[4]; float sigma[4]; Int antenna1; Int antenna2; Int arrayId; Int fieldId; Int dataDescId; Int stateId; Int flagRow; Int feed1; Int feed2; Int processorId; Int scanNumber; Int observationId; HDF5MetaData() : time(0), timeCentroid(0), interval(0), exposure(0), // uvw ({0,0,0}), // only possible in C++11 //weight ({1,1,1,1}), //sigma ({1,1,1,1}), antenna1(0), antenna2(0), arrayId(0), fieldId(0), dataDescId(0), stateId(0), flagRow(False), feed1(0), feed2(0), processorId(0), scanNumber(0), observationId(0) {} }; // Define the global variables shared between the main functions. vector myRa; vector myDec; Matrix myAntPos; bool myCalcUVW; bool myWriteAutoCorr; bool myWriteFloatData; bool myWriteWeightSpectrum; bool myCreateImagerColumns; bool myWriteRowWise; bool myDoSinglePart; int myNPart; int myTotalNBand; int myFirstBand; int myNBand; Vector myNPol; Vector myNChan; int myNTime; int myNTimeField; int myTileSizePol; int myTileSizeFreq; int myTileSize; //# in bytes int myNFlagBits; Vector myStartFreq; Vector myStepFreq; double myStartTime; double myStepTime; String myMsName; String myAntennaTableName; String myFlagColumn; int myUseMultiFile; //# 0=not 1=multifile 2=multihdf5 int myMultiBlockSize; //# in bytes bool myWriteHDF5; bool myUseAdios2; // // Class for creating and filling one or more MeasurementSets. // The number of time slots, baselines, spectral windows, channels and fields // can be given. It can be specified if the WEIGHT_SPECTRUM column has to be // created. The data, flags and weights are set to zero, false and 1. // All required meta info (like UVW) are filled in. // Simulator software (like BBS or MeqTrees) can be used to write actual data. // class MSCreate { public: MSCreate(); // Initialize and construct the MS with a given name. // The timeStep (in sec) is used by the write function // to calculate the time from the starting time and the timeCounter. // If the antenna table name is not empty, the ANTENNA table will be // copied instead of putting default values in that table. // The antenna positions have to be given in ITRF coordinates as XYZ. // So antPos must have shape [3,nantennas]. // If flagColumn is given and nFlagBits>0, an integer flag column is // created and column FLAG is mapped to it void init (const vector& ra, const vector& dec, const Matrix& antPos, bool calcUVW, bool writeAutoCorr, bool writeFloatData, bool writeWeightSpectrum, bool createImagerColumns, const Vector& npol, const Vector& nfreq, const Vector& startFreq, const Vector& stepFreq, int spw, int nspw, int ntimeField, double startTime, double stepTime, const String& msName, const String& antennaTableName, int nflagBits, const String& flagColumn, const IPosition& dataTileShape, int useMultiFile, //# 0=not 1=multifile 2=multihdf5 int multiBlockSize); // Destructor virtual ~MSCreate(); // Fill the various subtables. // virtual void fillAntenna (const Block& antMPos, const String& antennaTableName) = 0; virtual void fillSpwPol() = 0; virtual void fillField() = 0; virtual void fillFeed() = 0; virtual void fillObservation() = 0; virtual void fillProcessor() = 0; virtual void fillState() = 0; // // Close all subtables to limit the nr of open files. // This can be done once all subtables have been written. virtual void closeSubTables() = 0; // Write all rows for a single time step. // It sets the shape of the data array. // All flags are set to False. void writeTimeStep (int ntimeField, bool perRow); // Write a spectral window row by row. virtual void writeTimeStepRows (int band, int field, const vector >& antuvw) = 0; // Write a spectral window as a block. virtual void writeTimeStepSpw (int band, int field, const vector >& antuvw) = 0; // Extend the MS with the given nr of rows. virtual void addRows (int nbasel, int nfield) = 0; // Flush and fsync the MS. virtual void flush() = 0; // Return the nr of rows in the MS. virtual Int64 nrow() const = 0; // Show the cache statistics. virtual void showCacheStatistics() const = 0; // Get the number of baselines. int nbaselines() const; private: // Forbid copy constructor and assignment by making them private. // MSCreate (const MSCreate&); MSCreate& operator= (const MSCreate&); // // Create the MS and fill its subtables as much as possible. virtual void createMS (const String& msName, int ntimeField, int useMultiFile, int multiBlockSize, bool createImagerColumns, const String& flagColumn, int flagBits) = 0; // Add a polarization to the subtable. // Return the row number where it is added. int addPolarization (int npolarizations); // Fill the vector of baselines itsAntBL. void fillBaseLines (const Matrix& antPos); // Update the times in various subtables at the end of the observation. virtual void updateTimes() = 0; protected: //# Define the data. int itsNrTimes; //# Nr of time slots written vector itsRa; vector itsDec; int itsNrAnt; //# Nr of antennae bool itsCalcUVW; //# calculate UVW coordinates? bool itsWriteAutoCorr; //# write autocorrelations? bool itsWriteFloatData; //# write floatdata and only autocorr? bool itsWriteWeightSpectrum; Vector itsNFreq; //# nr of freq channels for each band Vector itsNPol; //# nr of polarizations for each band Vector itsStartFreq; Vector itsStepFreq; int itsSpw; int itsNSpw; double itsStartTime; //# start time of observation (sec) double itsStepTime; //# duration of each exposure (sec) String itsMsName; IPosition itsDataTileShape; vector itsPolnr; //# rownr in POL subtable for each band Block itsAntBL; //# Baseline vector for each antenna MPosition itsArrayPos; //# Position of array center MeasFrame itsFrame; //# Frame to convert to apparent coordinates Block itsPhaseDir; //# Phase directions of fields }; class MSCreateCasa: public MSCreate { public: MSCreateCasa(); // Destructor virtual ~MSCreateCasa(); // Write a spectral window row by row. virtual void writeTimeStepRows (int band, int field, const vector >& antuvw); // Write a spectral window as a block. virtual void writeTimeStepSpw (int band, int field, const vector >& antuvw); // Extend the MS with the given nr of rows. virtual void addRows (int nbasel, int nfield); // Flush and fsync the MS. virtual void flush() { itsMS.flush(True); } // Return the nr of rows in the MS. virtual Int64 nrow() const { return itsMS.nrow(); } // Show the cache statistics. virtual void showCacheStatistics() const; private: // Forbid copy constructor and assignment by making them private. // MSCreateCasa (const MSCreateCasa&); MSCreateCasa& operator= (const MSCreateCasa&); // // Create the MS and fill its subtables as much as possible. virtual void createMS (const String& msName, int ntimeField, int useMultiFile, int multiBlockSize, bool createImagerColumns, const String& flagColumn, int flagBits); // Close all subtables to limit the nr of open files. // This can be done once all subtables have been written. virtual void closeSubTables(); // Write the never changing columns only once. void writeSimpleMainColumns(); // Update the times in various subtables at the end of the observation. virtual void updateTimes(); // Add a band to the SPW subtable. void addBand (int band, int npolarizations, int nchannels, double startFreq, double chanWidth); // Add a field to the FIELD subtable. void addField (int field); // Add a polarization to the subtable. // Return the row number where it is added. int addPolarization (int npolarizations); // Add the extra columns needed for the imager for every column not existing. // These are CORRECTED_DATA and MODEL_DATA. // Furthermore it sets the CHANNEL_SELECTION keyword for VisSet. void addImagerColumns(); // Fill the various subtables. // virtual void fillAntenna (const Block& antMPos, const String& antennaTableName); virtual void fillSpwPol(); virtual void fillField(); virtual void fillFeed(); virtual void fillObservation(); virtual void fillProcessor(); virtual void fillState(); // //# Define the data. Int64 itsNrRow; MeasurementSet itsMS; MSMainColumns* itsMSCol; }; class MSCreateHDF5: public MSCreate { public: MSCreateHDF5(); // Destructor virtual ~MSCreateHDF5(); // Write a spectral window row by row. virtual void writeTimeStepRows (int band, int field, const vector >& antuvw); // Write a spectral window as a block. virtual void writeTimeStepSpw (int band, int field, const vector >& antuvw); // Extend the MS with the given nr of rows. virtual void addRows (int nbasel, int nfield); // Flush and fsync the MS. virtual void flush(); // Return the nr of rows in the MS. virtual Int64 nrow() const; // Show the cache statistics. virtual void showCacheStatistics() const; private: // Forbid copy constructor and assignment by making them private. // MSCreateHDF5 (const MSCreateHDF5&); MSCreateHDF5& operator= (const MSCreateHDF5&); // // Create the MS and fill its subtables as much as possible. virtual void createMS (const String& msName, int ntimeField, int useMultiFile, int multiBlockSize, bool createImagerColumns, const String& flagColumn, int flagBits); // Close all subtables to limit the nr of open files. // This can be done once all subtables have been written. virtual void closeSubTables(); // Update the times in various subtables at the end of the observation. virtual void updateTimes(); // Add a polarization to the subtable. // Return the row number where it is added. int addPolarization (int npolarizations); // Fill the various subtables. // virtual void fillAntenna (const Block& antMPos, const String& antennaTableName); virtual void fillSpwPol(); virtual void fillField(); virtual void fillFeed(); virtual void fillObservation(); virtual void fillProcessor(); virtual void fillState(); // // Create the HDF5 meta data type. void makeMetaType(); //# Define the data. Int64 itsNrRow; HDF5DataType itsMetaType; std::shared_ptr itsFile; vector itsSpws; }; //# Implementation of the classes. MSCreate::MSCreate() : itsNrTimes (0) {} MSCreate::~MSCreate() {} void MSCreate::init (const vector& ra, const vector& dec, const Matrix& antPos, bool calcUVW, bool writeAutoCorr, bool writeFloatData, bool writeWeightSpectrum, bool createImagerColumns, const Vector& npol, const Vector& nfreq, const Vector& startFreq, const Vector& stepFreq, int spw, int nspw, int ntimeField, double startTime, double stepTime, const String& msName, const String& antennaTableName, int nflagBits, const String& flagColumn, const IPosition& dataTileShape, int useMultiFile, int multiBlockSize) { itsRa = ra; itsDec = dec; itsCalcUVW = calcUVW; itsWriteAutoCorr = writeAutoCorr; itsWriteFloatData = writeFloatData; itsWriteWeightSpectrum = writeWeightSpectrum; itsNPol = npol; itsNFreq = nfreq; itsStartFreq = startFreq; itsStepFreq = stepFreq; itsSpw = spw; itsNSpw = nspw; itsStartTime = startTime; itsStepTime = stepTime; itsDataTileShape = dataTileShape; itsNrAnt = antPos.ncolumn(); AlwaysAssert (itsNrAnt > 0, AipsError); AlwaysAssert (itsNFreq.size() > 0, AipsError); AlwaysAssert (itsNPol.size() == itsNFreq.size(), AipsError); AlwaysAssert (itsStartFreq.size() == itsNFreq.size(), AipsError); AlwaysAssert (itsStepFreq.size() == itsNFreq.size(), AipsError); // Keep the antenna positions in ITRF coordinates. Block antMPos(itsNrAnt); for (Int i=0; i > antuvw(itsNrAnt); if (itsCalcUVW) { for (int j=0; jset(itsFrame); // attach frame MBaseline::Convert mcvt(mbl, MBaseline::J2000); MVBaseline bas = mcvt().getValue(); MVuvw jvguvw(bas, itsPhaseDir[field].getValue()); antuvw[j] = Muvw(jvguvw, Muvw::J2000).getValue().getVector(); } } // Write each spectral window. for (int band=itsSpw; band& antPos) { uInt nr = antPos.ncolumn(); itsAntBL.resize (nr); for (uInt j=0; j 1) { if (nflagBits == 8) { td.addColumn(ArrayColumnDesc(flagColumn, 2)); } else if (nflagBits == 16) { td.addColumn(ArrayColumnDesc(flagColumn, 2)); } else { td.addColumn(ArrayColumnDesc(flagColumn, 2)); } if (itsNSpw == 1) { td.rwColumnDesc(flagColumn).setShape (dataShape); } } // Create WEIGHT_SPECTRUM if needed. if (itsWriteWeightSpectrum) { td.addColumn (ArrayColumnDesc(MS::columnName(MS::WEIGHT_SPECTRUM), 2)); if (itsNSpw == 1) { td.rwColumnDesc(MS::columnName(MS::WEIGHT_SPECTRUM)).setShape (dataShape); } } // Add imager columns if needed. if (createImagerColumns) { addImagerColumns(); } // Set the reference frame of UVW to J2000. { ColumnDesc& col(td.rwColumnDesc("UVW")); TableRecord rec = col.keywordSet().asRecord ("MEASINFO"); rec.define ("Ref", "J2000"); col.rwKeywordSet().defineRecord ("MEASINFO", rec); } int ts = itsDataTileShape.product()*8; // tilesize // Setup the new table. // Use the required StorageOption. // Most columns use the IncrStMan; some use others. StorageOption stopt (StorageOption::SepFile); if (useMultiFile == 1) { stopt = StorageOption (StorageOption::MultiFile, multiBlockSize); } else if (useMultiFile == 2) { stopt = StorageOption (StorageOption::MultiHDF5, multiBlockSize); } SetupNewTable newTab(msName, td, Table::New, stopt); if(myUseAdios2){ #ifdef HAVE_ADIOS2 Adios2StMan a2man; newTab.bindAll (a2man); #else throw(std::runtime_error("ADIOS 2 is not built.")); #endif } else{ IncrementalStMan incrStMan("ISMData", ts); newTab.bindAll (incrStMan); StandardStMan stanStMan("SSMData", ts); newTab.bindColumn(MS::columnName(MS::TIME), stanStMan); newTab.bindColumn(MS::columnName(MS::TIME_CENTROID), stanStMan); newTab.bindColumn(MS::columnName(MS::ANTENNA1), stanStMan); newTab.bindColumn(MS::columnName(MS::ANTENNA2), stanStMan); newTab.bindColumn(MS::columnName(MS::DATA_DESC_ID), stanStMan); newTab.bindColumn(MS::columnName(MS::FIELD_ID), stanStMan); newTab.bindColumn(MS::columnName(MS::UVW), stanStMan); // Use a TiledColumnStMan or TiledShapeStMan for the data and flags. if (itsNSpw == 1) { TiledColumnStMan tiledData("TiledData", itsDataTileShape); if (itsWriteFloatData) { newTab.bindColumn(MS::columnName(MS::FLOAT_DATA), tiledData); } else { newTab.bindColumn(MS::columnName(MS::DATA), tiledData); } } else { TiledShapeStMan tiledData("TiledData", itsDataTileShape); if (itsWriteFloatData) { newTab.bindColumn(MS::columnName(MS::FLOAT_DATA), tiledData); } else { newTab.bindColumn(MS::columnName(MS::DATA), tiledData); } } // Create the FLAG column. // Only needed if bit flags engine is not used. String dmName = "TiledFlag"; if (nflagBits <= 1) { IPosition tileShape(itsDataTileShape); tileShape[2] *= 8; if (itsNSpw == 1) { TiledColumnStMan tiledFlag(dmName, tileShape); newTab.bindColumn(MS::columnName(MS::FLAG), tiledFlag); } else { TiledShapeStMan tiledFlag(dmName, tileShape); newTab.bindColumn(MS::columnName(MS::FLAG), tiledFlag); } dmName = "TiledFlagBits"; } else { // Create the flag bits column. if (itsNSpw == 1) { TiledColumnStMan tiledFlagBits(dmName, itsDataTileShape); newTab.bindColumn(flagColumn, tiledFlagBits); } else { TiledShapeStMan tiledFlagBits(dmName, itsDataTileShape); newTab.bindColumn(flagColumn, tiledFlagBits); } if (nflagBits > 1) { // Map the flag bits column to the FLAG column. if (nflagBits == 8) { BitFlagsEngine fbe(MS::columnName(MS::FLAG), flagColumn); newTab.bindColumn(MS::columnName(MS::FLAG), fbe); } else if (nflagBits == 16) { BitFlagsEngine fbe(MS::columnName(MS::FLAG), flagColumn); newTab.bindColumn(MS::columnName(MS::FLAG), fbe); } else { BitFlagsEngine fbe(MS::columnName(MS::FLAG), flagColumn); newTab.bindColumn(MS::columnName(MS::FLAG), fbe); } } } if (itsWriteWeightSpectrum) { if (itsNSpw == 1) { TiledColumnStMan tiledWSpec("TiledWeightSpectrum", itsDataTileShape); newTab.bindColumn(MS::columnName(MS::WEIGHT_SPECTRUM), tiledWSpec); } else { TiledShapeStMan tiledWSpec("TiledWeightSpectrum", itsDataTileShape); newTab.bindColumn(MS::columnName(MS::WEIGHT_SPECTRUM), tiledWSpec); } } } // Create the MS and its subtables. // Get access to its columns. itsMS = MeasurementSet(newTab); itsMSCol = new MSMainColumns(itsMS); // Create all subtables. // Do this after the creation of optional subtables, // so the MS will know about those optional sutables. itsMS.createDefaultSubtables (Table::New); // Store some attributes defining nr of baselines and fields. Record rec; rec.define ("NAntenna", itsNrAnt); rec.define ("NBaseline", nbaselines()); rec.define ("NField", int(itsRa.size())); rec.define ("NTimeField", ntimeField); itsMS.rwKeywordSet().defineRecord ("ATTR", rec); } void MSCreateCasa::closeSubTables() { // Don't do this yet. // It requires at least that the throw is removed from NullTable::flush // or that MS::flush tests if a subtable is null. // Furthermore, probably the Tables in the TableKeyword objects have to // be set to an empty Table object too to really close the subtables. // That could be checked by activating TableTrace in the .casarc file. return; itsMS.antenna() = MSAntenna(); itsMS.dataDescription() = MSDataDescription(); itsMS.doppler() = MSDoppler(); itsMS.feed() = MSFeed(); itsMS.field() = MSField(); itsMS.flagCmd() = MSFlagCmd(); itsMS.freqOffset() = MSFreqOffset(); itsMS.history() = MSHistory(); itsMS.observation() = MSObservation(); itsMS.pointing() = MSPointing(); itsMS.polarization() = MSPolarization(); itsMS.processor() = MSProcessor(); itsMS.source() = MSSource(); itsMS.spectralWindow() = MSSpectralWindow(); itsMS.state() = MSState(); itsMS.sysCal() = MSSysCal(); itsMS.weather() = MSWeather(); itsMS.keywordSet().closeTables(); } void MSCreateCasa::fillAntenna (const Block& antMPos, const String& antennaTableName) { // If a column is contained in the input ANTENNA table, copy it from there. // Otherwise fill in a default value. // Only the array positions are written directly. // Fill the ANTENNA subtable. Table antTab; if (! antennaTableName.empty()) { antTab = Table(antennaTableName, TableLock(TableLock::AutoNoReadLocking)); } MSAntenna msant = itsMS.antenna(); msant.addRow (itsNrAnt); Vector antOffset(3); antOffset = 0; MSAntennaColumns msantCol(msant); // First copy the possible input columns. TableCopy::copyRows (msant, antTab); // Write default values if there was no such input column. if (! antTab.tableDesc().isColumn("NAME")) { for (Int i=0; i 0, AipsError); AlwaysAssert (npolarizations==1 || npolarizations==2 || npolarizations==4, AipsError); double refFreq = startFreq + 0.5*(nchannels+1)*chanWidth; Vector chanWidths(nchannels); Vector chanFreqs(nchannels); chanWidths = chanWidth; indgen (chanFreqs, startFreq + chanWidth/2., chanWidth); // Find out if this nr of polarizations has already been given. Int polnr = -1; for (int i=0; i stFreqs = chanFreqs - chanWidths/2.; Vector endFreqs = chanFreqs + chanWidths/2.; double totalBW = max(endFreqs) - min(stFreqs); MSSpectralWindow msspw = itsMS.spectralWindow(); MSSpWindowColumns msspwCol(msspw); msspw.addRow(); msspwCol.numChan().put (rownr, nchannels); msspwCol.name().put (rownr, ""); msspwCol.refFrequency().put (rownr, refFreq); msspwCol.chanFreq().put (rownr, chanFreqs); msspwCol.chanWidth().put (rownr, chanWidths); msspwCol.measFreqRef().put (rownr, MFrequency::TOPO); msspwCol.effectiveBW().put (rownr, chanWidths); msspwCol.resolution().put (rownr, chanWidths); msspwCol.totalBandwidth().put (rownr, totalBW); msspwCol.netSideband().put (rownr, 0); msspwCol.ifConvChain().put (rownr, 0); msspwCol.freqGroup().put (rownr, 0); msspwCol.freqGroupName().put (rownr, ""); msspwCol.flagRow().put (rownr, False); // Now add the band to the internal blocks. itsPolnr.push_back (polnr); } int MSCreateCasa::addPolarization (int npolarizations) { MSPolarization mspol = itsMS.polarization(); MSPolarizationColumns mspolCol(mspol); uInt rownr = mspol.nrow(); Vector corrType(npolarizations); corrType(0) = Stokes::XX; if (npolarizations == 2) { corrType(1) = Stokes::YY; } else if (npolarizations == 4) { corrType(1) = Stokes::XY; corrType(2) = Stokes::YX; corrType(3) = Stokes::YY; } Matrix corrProduct(2, npolarizations); for (Int i=0; i outdir(1); outdir[0] = itsPhaseDir[field]; // Put the direction into the FIELD subtable. { MSField msfield = itsMS.field(); MSFieldColumns msfieldCol(msfield); uInt rownr = msfield.nrow(); msfield.addRow(); msfieldCol.name().put (rownr, "BEAM_" + String::toString(rownr)); msfieldCol.code().put (rownr, ""); msfieldCol.time().put (rownr, itsStartTime); // really startTime; everywhere else it is midpoint with an interval msfieldCol.numPoly().put (rownr, 0); msfieldCol.delayDirMeasCol().put (rownr, outdir); msfieldCol.phaseDirMeasCol().put (rownr, outdir); msfieldCol.referenceDirMeasCol().put (rownr, outdir); msfieldCol.sourceId().put (rownr, -1); msfieldCol.flagRow().put (rownr, False); } // Put the direction for each antenna into the POINTING subtable. { MSPointing mspointing = itsMS.pointing(); MSPointingColumns mspointingCol(mspointing); uInt rownr = mspointing.nrow(); mspointing.addRow(itsNrAnt); for (Int i=0; i feedOffset(2,nRec); feedOffset = 0; Matrix feedResponse(nRec,nRec); feedResponse = Complex(0.0,0.0); for (Int rec=0; rec feedType(nRec); feedType(0) = "X"; feedType(1) = "Y"; Vector feedPos(3); feedPos = 0.0; Vector feedAngle(nRec); feedAngle = -M_PI_4; // 0 for parallel dipoles // Fill the FEED subtable. MSFeed msfeed = itsMS.feed(); MSFeedColumns msfeedCol(msfeed); msfeed.addRow (itsNrAnt); for (Int i=0; i corrSchedule(1); corrSchedule = "corrSchedule"; Vector timeRange(2); timeRange(0) = itsStartTime; timeRange(1) = itsStartTime + itsNrTimes*itsStepTime; // Data is public one year after end of observation. Double releaseDate = timeRange(1) + 365.25*24*60*60; // Fill the columns msobs.addRow(); msobsCol.telescopeName().put (0, "LOFAR"); msobsCol.timeRange().put (0, timeRange); msobsCol.observer().put (0, "MSCreate"); msobsCol.scheduleType().put (0, "LOFAR"); msobsCol.schedule().put (0, corrSchedule); msobsCol.project().put (0, "MSCreate"); msobsCol.releaseDate().put (0, releaseDate); msobsCol.flagRow().put (0, False); msobs.flush(); } void MSCreateCasa::fillProcessor() { MSProcessor msproc = itsMS.processor(); MSProcessorColumns msprocCol(msproc); // Fill the columns msproc.addRow(); msprocCol.type().put (0, "CORRELATOR"); msprocCol.subType().put (0, ""); msprocCol.typeId().put (0, -1); msprocCol.modeId().put (0, -1); msprocCol.flagRow().put (0, False); msproc.flush(); } void MSCreateCasa::fillState() { MSState msstate = itsMS.state(); MSStateColumns msstateCol(msstate); // Fill the columns msstate.addRow(); msstateCol.sig().put (0, True); msstateCol.ref().put (0, False); msstateCol.cal().put (0, 0.); msstateCol.load().put (0, 0.); msstateCol.subScan().put (0, 0); msstateCol.obsMode().put (0, ""); msstateCol.flagRow().put (0, False); msstate.flush(); } void MSCreateCasa::updateTimes() { // Calculate the interval, end, and central time. Double interval = itsNrTimes*itsStepTime; Double endTime = itsStartTime + interval; Double midTime = (itsStartTime + endTime) / 2; // Update all rows in FEED subtable. { MSFeed mssub (itsMS.keywordSet().asTable("FEED")); MSFeedColumns mssubCol(mssub); Vector val(mssub.nrow()); val = midTime; mssubCol.time().putColumn (val); val = interval; mssubCol.interval().putColumn (val); } // Update all rows in POINTING subtable. { MSPointing mssub (itsMS.keywordSet().asTable("POINTING")); MSPointingColumns mssubCol(mssub); Vector val(mssub.nrow()); val = midTime; mssubCol.time().putColumn (val); val = interval; mssubCol.interval().putColumn (val); } // Update all rows in OBSERVATION subtable. { MSObservation msobs (itsMS.keywordSet().asTable("OBSERVATION")); MSObservationColumns msobsCol(msobs); Vector timeRange(2); timeRange(0) = itsStartTime; timeRange(1) = itsStartTime + itsNrTimes*itsStepTime; for (rownr_t i=0; ifeed1().put(0, 0); itsMSCol->feed2().put(0, 0); itsMSCol->processorId().put(0, 0); itsMSCol->scanNumber().put(0, 0); itsMSCol->arrayId().put(0, 0); itsMSCol->observationId().put(0, 0); itsMSCol->stateId().put(0, 0); itsMSCol->interval().put(0, itsStepTime); itsMSCol->exposure().put(0, itsStepTime); itsMSCol->flagRow().put(0, False); Vector ones(4, 1.0f); itsMSCol->weight().put(0, ones); itsMSCol->sigma().put(0, ones); } void MSCreateCasa::writeTimeStepRows (int band, int field, const vector >& antuvw) { if (itsNrRow == 0) { writeSimpleMainColumns(); } // Find the shape of the data array in each table row. IPosition shape(2, itsNPol[band], itsNFreq[band]); Array defFlags(shape, False); Array defData; Array defFloatData; if (itsWriteFloatData) { defFloatData.resize (shape); // Make data non-zero to avoid possible file system optimizations. indgen (defFloatData, 0.0f, 0.03f); } else { defData.resize (shape); indgen (defData, Complex(), Complex(0.01, 0.02)); } Array sigma(IPosition(1, shape(0))); sigma = 1; Array weight(IPosition(1, shape(0))); weight = 1; Array weightSpectrum; if (itsWriteWeightSpectrum) { weightSpectrum.resize (shape); weightSpectrum = 1; } Double time = itsStartTime + itsNrTimes*itsStepTime + itsStepTime/2; Vector myuvw(3, 0); for (int j=0; jfloatData().put(itsNrRow, defFloatData); } else { itsMSCol->data().put(itsNrRow, defData); } itsMSCol->flag().put(itsNrRow, defFlags); if (itsWriteWeightSpectrum) { itsMSCol->weightSpectrum().put(itsNrRow, weightSpectrum); } itsMSCol->time().put (itsNrRow, time); itsMSCol->timeCentroid().put (itsNrRow, time); itsMSCol->antenna1().put (itsNrRow, j); itsMSCol->antenna2().put (itsNrRow, i); itsMSCol->dataDescId().put (itsNrRow, band); itsMSCol->fieldId().put (itsNrRow, field); itsMSCol->uvw().put (itsNrRow, myuvw); itsNrRow++; } } } void MSCreateCasa::writeTimeStepSpw (int band, int field, const vector >& antuvw) { if (itsNrRow == 0) { writeSimpleMainColumns(); } int nrbasel = nbaselines(); // Find the shape of the data array in each table row. IPosition shape(3, itsNPol[band], itsNFreq[band], nrbasel); Double time = itsStartTime + itsNrTimes*itsStepTime + itsStepTime/2; Vector times(nrbasel, time); Vector vecint(nrbasel); Vector vecint2(nrbasel); Matrix myuvw(3, nrbasel, 0); RefRows rows (itsNrRow, itsNrRow+nrbasel-1); VectorIterator uvwiter(myuvw); int inx=0; for (int j=0; j arr(shape); indgen (arr, 0.0f, 0.03f); itsMSCol->floatData().putColumnCells(rows, arr); } else { Array arr(shape); indgen (arr, Complex(), Complex(0.01, 0.02)); itsMSCol->data().putColumnCells(rows, Array(shape)); } itsMSCol->flag().putColumnCells(rows, Array(shape, False)); if (itsWriteWeightSpectrum) { itsMSCol->weightSpectrum().putColumnCells(rows, Array(shape, 1)); } itsMSCol->time().putColumnCells (rows, times); itsMSCol->timeCentroid().putColumnCells (rows, times); itsMSCol->antenna1().putColumnCells (rows, vecint); itsMSCol->antenna2().putColumnCells (rows, vecint2); vecint = band; itsMSCol->dataDescId().putColumnCells (rows, vecint); vecint = field; itsMSCol->fieldId().putColumnCells (rows, vecint); itsMSCol->uvw().putColumnCells (rows, myuvw); itsNrRow += nrbasel; } void MSCreateCasa::addImagerColumns() { // Find data shape from FLAG column. // Make tiles of appr. 1 MB. IPosition shape = TableColumn(itsMS, MS::columnName(MS::FLAG)).shapeColumn(); String colName = MS::columnName(MS::CORRECTED_DATA); if (! itsMS.tableDesc().isColumn(colName)) { TableDesc td; if (shape.empty()) { td.addColumn (ArrayColumnDesc(colName, "corrected data")); } else { td.addColumn (ArrayColumnDesc(colName, "corrected data", shape, ColumnDesc::FixedShape)); } TiledColumnStMan stMan("TiledCorrectedData", itsDataTileShape); itsMS.addColumn (td, stMan); } colName = MS::columnName(MS::MODEL_DATA); if (! itsMS.tableDesc().isColumn(colName)) { TableDesc td; if (shape.empty()) { td.addColumn (ArrayColumnDesc(colName, "model data")); } else { td.addColumn (ArrayColumnDesc(colName, "model data", shape, ColumnDesc::FixedShape)); } TiledColumnStMan stMan("TiledModelData", itsDataTileShape); itsMS.addColumn (td, stMan); // Set MODEL_DATA keyword for casa::VisSet. // Sort out the channel selection. if (itsMS.spectralWindow().isNull()) { itsMS.spectralWindow() = MSSpectralWindow(itsMS.keywordSet().asTable("SPECTRAL_WINDOW")); } MSSpWindowColumns msSpW(itsMS.spectralWindow()); Matrix selection(2, msSpW.nrow()); // Fill in default selection (all bands and channels). selection.row(0) = 0; //start selection.row(1) = msSpW.numChan().getColumn(); ArrayColumn mcd(itsMS, colName); mcd.rwKeywordSet().define ("CHANNEL_SELECTION",selection); } } void MSCreateCasa::showCacheStatistics() const { cout << (itsWriteFloatData ? "FLOAT_DATA: " : "DATA: "); RODataManAccessor(itsMS, "TiledData", False).showCacheStatistics (cout); RODataManAccessor(itsMS, "SSMData", False).showCacheStatistics (cout); RODataManAccessor(itsMS, "ISMData", False).showCacheStatistics (cout); } MSCreateHDF5::MSCreateHDF5() : itsNrRow (0) { // Create the meta data type. makeMetaType(); } MSCreateHDF5::~MSCreateHDF5() { } void MSCreateHDF5::createMS (const String& msName, int ntimeField, int /*useMultiFile*/, int /*multiBlockSize*/, bool createImagerColumns, const String& /*flagColumn*/, int /*nflagBits*/) { Timer timer; // Create the file. itsFile = std::make_shared(msName, ByteIO::New); int nrbasel = nbaselines(); // Store some attributes defining nr of baselines and fields. Record rec; rec.define ("NAntenna", itsNrAnt); rec.define ("NBaseline", nrbasel); rec.define ("NField", int(itsRa.size())); rec.define ("NTimeField", ntimeField); // Create a group per spectral window. for (int band=itsSpw; band(*itsFile, "SPW_"+String::toString(band)); // Write the attributes. HDF5Record::writeRecord (*(spw.spw), "ATTR", rec); // Create the data in the spw. IPosition shape(3, itsNPol[band], itsNFreq[band], 0); IPosition shape1(1, 0); IPosition tileShape1(1, nrbasel); uInt freqPerTile = itsDataTileShape[1]; uInt cacheSize = (itsNFreq[band] + freqPerTile - 1) / freqPerTile; cout << "HDF5 cacheSize = " << cacheSize << endl; if (itsWriteFloatData) { spw.floatData = std::make_shared(*spw.spw, "FLOAT_DATA", shape, itsDataTileShape, (float*)0); spw.floatData->setCacheSize (cacheSize); } else { spw.data = std::make_shared(*spw.spw, "DATA", shape, itsDataTileShape, (Complex*)0); spw.data->setCacheSize (cacheSize); } IPosition tileShape(itsDataTileShape); tileShape[2] *= 8; spw.flag = std::make_shared(*spw.spw, "FLAG", shape, tileShape, (Bool*)0); spw.flag->setCacheSize (cacheSize); if (itsWriteWeightSpectrum) { spw.weightSpectrum = std::make_shared(*spw.spw, "WEIGHT_SPECTRUM", shape, itsDataTileShape, (float*)0); spw.weightSpectrum->setCacheSize (cacheSize); } spw.metaData = std::make_shared(*spw.spw, "METADATA", shape1, tileShape1, itsMetaType); if (createImagerColumns) { spw.modelData = std::make_shared(*spw.spw, "MODEL_DATA", shape, tileShape, (Complex*)0); spw.corrData = std::make_shared(*spw.spw, "CORRECTED_DATA", shape, tileShape, (Complex*)0); // Not written, so no need to set their cache sizes. } itsSpws.push_back (spw); } } void MSCreateHDF5::makeMetaType() { // Push the fields in the same order as defined in the HDF5MetaData struct. vector types; vector names; types.push_back (HDF5DataType((double*)0)); names.push_back ("time"); types.push_back (HDF5DataType((double*)0)); names.push_back ("timeCentroid"); types.push_back (HDF5DataType((double*)0)); names.push_back ("interval"); types.push_back (HDF5DataType((double*)0)); names.push_back ("exposure"); types.push_back (HDF5DataType (HDF5DataType((double*)0), IPosition(1,3))); names.push_back ("uvw"); types.push_back (HDF5DataType (HDF5DataType((float*)0), IPosition(1,4))); names.push_back ("weight"); types.push_back (HDF5DataType (HDF5DataType((float*)0), IPosition(1,4))); names.push_back ("sigma"); types.push_back (HDF5DataType((Int*)0)); names.push_back ("antenna1"); types.push_back (HDF5DataType((Int*)0)); names.push_back ("antenna2"); types.push_back (HDF5DataType((Int*)0)); names.push_back ("arrayId"); types.push_back (HDF5DataType((Int*)0)); names.push_back ("fieldId"); types.push_back (HDF5DataType((Int*)0)); names.push_back ("dataDescId"); types.push_back (HDF5DataType((Int*)0)); names.push_back ("stateId"); types.push_back (HDF5DataType((Int*)0)); names.push_back ("flagRow"); types.push_back (HDF5DataType((Int*)0)); names.push_back ("feed1"); types.push_back (HDF5DataType((Int*)0)); names.push_back ("feed2"); types.push_back (HDF5DataType((Int*)0)); names.push_back ("processorId"); types.push_back (HDF5DataType((Int*)0)); names.push_back ("scanNumber"); types.push_back (HDF5DataType((Int*)0)); names.push_back ("observationId"); itsMetaType = HDF5DataType (names, types); } void MSCreateHDF5::addRows (int nbasel, int nfield) { rownr_t nrow = rownr_t(nbasel)*nfield; for (int band=itsSpw; bandextend (newShape3); itsSpws[band].flag->extend (newShape3); if (itsWriteWeightSpectrum) { itsSpws[band].weightSpectrum->extend (newShape3); } itsSpws[band].metaData->extend (newShape1); } } void MSCreateHDF5::writeTimeStepSpw (int band, int field, const vector >& antuvw) { int nrbasel = nbaselines(); // Get the time. Double time = itsStartTime + itsNrTimes*itsStepTime + itsStepTime/2; // Fill meta data. Vector meta(nrbasel); RefRows rows (itsNrRow, itsNrRow+nrbasel-1); int inx=0; for (int j=0; j uvw (antuvw[i] - antuvw[j]); for (int i=0; i<3; ++i) meta[inx].uvw[i] = uvw[i]; for (int i=0; i<4; ++i) meta[inx].weight[i] = 1; for (int i=0; i<4; ++i) meta[inx].sigma[i] = 1; meta[inx].time = time; meta[inx].timeCentroid = time; meta[inx].interval = itsStepTime; meta[inx].exposure = itsStepTime; meta[inx].dataDescId = band; meta[inx].fieldId = field; inx++; } } // Define the shape of the data arrays. IPosition shape3(3, itsNPol[band], itsNFreq[band], nrbasel); IPosition shape1(1, nrbasel); // Define the slicers to put the data arrays. Slicer slicer3(IPosition(3,0,0,itsNrRow), shape3); Slicer slicer1(IPosition(1,itsNrRow), shape1); // Put the data. if (itsWriteFloatData) { Array arr(shape3); indgen (arr, 0.0f, 0.03f); itsSpws[band].floatData->put (slicer3, arr); } else { Array arr(shape3); indgen (arr, Complex(), Complex(0.01, 0.02)); itsSpws[band].data->put (slicer3, arr); } itsSpws[band].flag->put (slicer3, Array(shape3, False)); if (itsWriteWeightSpectrum) { itsSpws[band].weightSpectrum->put (slicer3, Array(shape3, 1)); } itsSpws[band].metaData->put (slicer1, meta); // Increase nr of rows. itsNrRow += nrbasel; } void MSCreateHDF5::writeTimeStepRows (int band, int field, const vector >& antuvw) { // Find the shape of the data array in each table row. IPosition shape3(3, itsNPol[band], itsNFreq[band], 1); IPosition shape2(2, itsNPol[band], 1); IPosition shapeu(2, 3, 1); IPosition shape1(1, 1); Array defFlags(shape3, False); Array defData; Array defFloatData; if (itsWriteFloatData) { defFloatData.resize (shape3); // Make data non-zero to avoid possible file system optimizations. indgen (defFloatData, 0.0f, 0.03f); } else { defData.resize (shape3); indgen (defData, Complex(), Complex(0.01, 0.02)); } Matrix weightsigma(shape3[0], 1, 1.); Array weightSpectrum; if (itsWriteWeightSpectrum) { weightSpectrum.resize (shape3); weightSpectrum = 1; } Double time = itsStartTime + itsNrTimes*itsStepTime + itsStepTime/2; // Fill meta data. Vector meta(1); for (int i=0; i<4; ++i) meta[0].weight[i] = 1; for (int i=0; i<4; ++i) meta[0].sigma[i] = 1; meta[0].time = time; meta[0].timeCentroid = time; meta[0].interval = itsStepTime; meta[0].exposure = itsStepTime; meta[0].dataDescId = band; meta[0].fieldId = field; // Define the slicers to put the data arrays. for (int j=0; jput (slicer3, defFloatData); } else { itsSpws[band].data->put (slicer3, defData); } itsSpws[band].flag->put (slicer3, defFlags); if (itsWriteWeightSpectrum) { itsSpws[band].weightSpectrum->put (slicer3, Array(shape3, 1)); } meta[0].antenna1 = j; meta[0].antenna2 = i; itsSpws[band].metaData->put (slicer1, meta); itsNrRow++; } } } void MSCreateHDF5::fillAntenna (const Block& /*antMPos*/, const String& /*antennaTableName*/) {} void MSCreateHDF5::fillSpwPol() {} void MSCreateHDF5::fillField() {} void MSCreateHDF5::fillFeed() {} void MSCreateHDF5::fillObservation() {} void MSCreateHDF5::fillProcessor() {} void MSCreateHDF5::fillState() {} void MSCreateHDF5::updateTimes() {} void MSCreateHDF5::closeSubTables() {} void MSCreateHDF5::showCacheStatistics() const {} void MSCreateHDF5::flush() { itsFile->flush(); } Int64 MSCreateHDF5::nrow() const { return itsNrRow; } IPosition formTileShape (int tileSize, int tileNPol, int tileNFreq, bool writeFloatData, const Vector& npol, const Vector& nfreq) { // Determine the tile size to use. // Store all polarisations in a single tile. // Flags are stored as bits, so take care each tile has multiple of 8 flags. int tsp = tileNPol; int tsf = tileNFreq; int ts = tileSize; if (tsp <= 0) { tsp = max(npol); // default is all polarizations } if (tsf <= 0) { tsf = max(nfreq); // default is all channels } if (ts <= 0) { ts = 1024*1024; // default is 1 MByte } int tsr = std::max (1, ts / (tsp*tsf*8)); if (writeFloatData) { tsr = std::max (1, ts / (tsp*tsf*4)); } return IPosition(3,tsp,tsf,tsr); } void showHelp() { cout << "The program creates one or more MeasurementSets with a given number of" < parmArrayInt (Input& params, const String& name) { return RecordGram::expr2ArrayInt (params.getString(name)); } Array parmArrayDouble (Input& params, const String& name, const String& unit=String()) { return RecordGram::expr2ArrayDouble (params.getString(name), Record(), unit); } bool readParms (int argc, char* argv[]) { // enable input in no-prompt mode Input params(1); // define the input structure params.version("2017Oct31GvD"); params.create ("nms", "0", "Number of MeasurementSets to create (0 means 1 MS without suffix, >0 also creates MultiMS)", "int"); params.create ("msname", "", "Name of the output MeasurementSet (suffix _p is added if nms>0)", "string"); params.create ("ra", "", "One or more J2000 Right Ascensions (as hh:mm:ss.sss); defines the fields", "string"); params.create ("dec", "", "One or more J2000 Declinations (as dd.mm.ss.sss); defines the fields", "string"); params.create ("anttab", "", "Name of the ANTENNA table giving the antenna parameters to use (zero/empty values if not given)", "string"); params.create ("startfreq", "1 GHz", "Start frequency (Hz) per spw (1 value counts on for other spws)", "double"); params.create ("chanwidth", "1 MHz", "Channel frequency width (Hz) per spw (1 value applies to all spws)", "double"); params.create ("starttime", "", "Start time (e.g., 23Mar2016/12:00:00)", "string"); params.create ("timestep", "1", "Time interval (sec)", "double"); params.create ("ntime", "1", "Number of time steps", "int"); params.create ("ntimefield", "0", "Number of time steps per field (0=all fields for all times)", "int"); params.create ("totalspw", "nspw", "Total number of spectral windows (to write in SPECTRAL_WINDOW)", "int"); params.create ("firstspw", "0", "First spectral window to write in this set of MSs", "int"); params.create ("nspw", "1", "Number of spectral windows to write in this set of MSs", "int"); params.create ("npol", "4", "Number of polarizations per spectral window; 1 value applies to all spws", "int"); params.create ("nchan", "256", "Number of channels per spectral window; 1 value applies to all spws", "int"); params.create ("nant", "0", "Number of antennae to use; it anttab is given maximum to use is its size", "int"); params.create ("calcuvw", "false", "Calculate UVW coordinates?", "bool"); params.create ("autocorr", "true", "Write autocorrelations?", "bool"); params.create ("floatdata", "false", "Write only autocorrelations and FLOAT_DATA instead of DATA?", "bool"); params.create ("weightspectrum", "false", "Write WEIGHT_SPECTRUM column?", "bool"); params.create ("imagercolumns", "false", "Write imager columns (MODEL_DATA, CORRECTED_DATA)?", "bool"); params.create ("rowwise", "false", "Write the data row wise (thus a put per row)", "bool"); params.create ("nflagbits", "0", "Write multiple flag bits (0, 8, 16 or 32) mapped to FLAG", "int"); params.create ("flagcolumn", "FLAG_BITS", "Name of the FlagBits column if nflagbits>0", "string"); params.create ("tilesize", "-1", "Size of data tiles (bytes); default is 1024*1024", "int"); params.create ("tilesizepol", "-1", "Number of polarizations in data tiles; default is all", "int"); params.create ("tilesizefreq", "-1", "Number of channels in data tiles; default is all", "int"); params.create ("multifile", "false", "Use the MultiFile feature?", "bool"); params.create ("multihdf5", "false", "Use the MultiHDF5 feature?", "bool"); params.create ("mfsize", "-1", "MultiFile/HDF5 block size (bytes); default is tilesize", "int"); params.create ("ashdf5", "false", "Write the data in HDF5 format", "bool"); params.create ("useadios2", "false", "Use Adios2StMan for all columns", "bool"); // Fill the input structure from the command line. params.readArguments (argc, argv); // Get the various parameters. myMsName = params.getString ("msname"); if (myMsName.empty()) { showHelp(); return false; } myStepFreq = Vector (parmArrayDouble (params, "chanwidth", "Hz")); myStartFreq = Vector (parmArrayDouble (params, "startfreq", "Hz")); myStepTime = params.getDouble ("timestep"); String startTimeStr = params.getString ("starttime"); Quantity qn; AlwaysAssertExit (MVTime::read (qn, startTimeStr, true)); myStartTime = qn.getValue ("s"); Vector raStr = stringToVector (params.getString ("ra")); Vector decStr = stringToVector (params.getString ("dec")); AlwaysAssertExit (raStr.size() > 0 && raStr.size() == decStr.size()); for (uint i=0; i 0); AlwaysAssertExit (myNBand > 0); if (myNBand > myNPart) { // Multiple bands per part. AlwaysAssertExit (myNBand%myNPart == 0); } else { // If fewer bands than parts, bands are spread over parts which is the // same as having as many bands as parts. AlwaysAssertExit (myNPart%myNBand == 0); myNBand = myNPart; } // firstspw and totalspw can be an expression of nspw. Record vars; vars.define ("nspw", myNBand); myFirstBand = parmInt (params, "firstspw", vars); myTotalNBand = parmInt (params, "totalspw", vars); AlwaysAssertExit (myTotalNBand >= myNBand); Block nchanBlock = params.getIntArray ("nchan"); myNChan = Vector (nchanBlock.begin(), nchanBlock.end()); Block npolBlock = params.getIntArray ("npol"); myNPol = Vector (npolBlock.begin(), npolBlock.end()); myNTime = params.getInt ("ntime"); myNTimeField = params.getInt ("ntimefield"); // Determine possible tile size. Default is no tiling. myTileSizePol = parmInt (params, "tilesizepol"); myTileSizeFreq = parmInt (params, "tilesizefreq"); myTileSize = parmInt (params, "tilesize"); AlwaysAssertExit (myNPol.size() == 1 || myNPol.size() == uInt(myTotalNBand)); if (myNPol.size() != uInt(myTotalNBand)) { int np = myNPol[0]; myNPol.resize (myTotalNBand, True); myNPol = np; } AlwaysAssertExit (myNChan.size() == 1 || myNChan.size() == uInt(myTotalNBand)); if (myNChan.size() != uInt(myTotalNBand)) { int nf = myNChan[0]; myNChan.resize (myTotalNBand); myNChan = nf; } // Determine start and step frequency per band. AlwaysAssertExit (myStepFreq.size() == 1 || myStepFreq.size() == uInt(myTotalNBand)); if (myStepFreq.size() != uInt(myTotalNBand)) { double f = myStepFreq[0]; myStepFreq.resize (myTotalNBand, True); myStepFreq = f; } AlwaysAssertExit (myStartFreq.size() == 1 || myStartFreq.size() == uInt(myTotalNBand)); if (myStartFreq.size() != uInt(myTotalNBand)) { myStartFreq.resize (myTotalNBand, True); for (int i=1; i 0); AlwaysAssertExit (myStartFreq[i] > 0); } AlwaysAssertExit (myStepTime > 0); // Get remaining parameters. myWriteAutoCorr = params.getBool ("autocorr"); myWriteFloatData = params.getBool ("floatdata"); if (myWriteFloatData) { myWriteAutoCorr = True; } myCalcUVW = params.getBool ("calcuvw"); myWriteWeightSpectrum = params.getBool ("weightspectrum"); myCreateImagerColumns = params.getBool ("imagercolumns"); myWriteRowWise = params.getBool ("rowwise"); bool useMultiFile = params.getBool ("multifile"); bool useMultiHDF5 = params.getBool ("multihdf5"); myUseMultiFile = 0; if (useMultiFile) { myUseMultiFile = 1; } else if (useMultiHDF5) { myUseMultiFile = 2; } myMultiBlockSize = params.getInt ("mfsize"); myWriteHDF5 = params.getBool ("ashdf5"); myUseAdios2 = params.getBool ("useadios2"); myFlagColumn = params.getString ("flagcolumn"); myNFlagBits = params.getInt ("nflagbits"); // Get the station info from the given antenna table. uInt nant = params.getInt ("nant"); myAntennaTableName = params.getString ("anttab"); if (myAntennaTableName.empty()) { myAntPos.resize (3, nant); myAntPos = 0.; } else { Table tab(myAntennaTableName, TableLock(TableLock::AutoNoReadLocking)); if (nant == 0 || nant >= tab.nrow()) { nant = tab.nrow(); } else { RowNumbers rows(nant); indgen (rows); tab = tab(rows); } AlwaysAssert (nant>0, AipsError); ArrayColumn posCol(tab, "POSITION"); posCol.getColumn (myAntPos); } return true; } void showParms() { cout << " nms = " << myNPart << " " << myMsName << endl; cout << " nthread = " << OMP::maxThreads() << endl; int nant = myAntPos.ncolumn(); cout << " nant = " << nant << " ("; if (myWriteFloatData) { cout << nant; } else if (myWriteAutoCorr) { cout << nant*(nant+1)/2; } else { cout << nant*(nant-1)/2; } cout << " baselines)" << endl; cout << " totalspw = " << myTotalNBand << " (firstspw = " << myFirstBand << ')' << endl; cout << " nspw = " << myNBand << " (" << myNBand/myNPart << " per ms)" << endl; cout << " nfield = " << myRa.size(); if (myNTimeField > 0) { cout << " (" << myNTimeField << "times per field)"; } cout << endl; cout << " ntime = " << myNTime << endl; cout << " nchan = " << myNChan << endl; cout << " npol = " << myNPol << endl; cout << " rowwise = " << myWriteRowWise << endl; cout << " writeweightspectrum = " << myWriteWeightSpectrum << endl; cout << " createimagercolumns = " << myCreateImagerColumns << endl; if (!myFlagColumn.empty() && myNFlagBits > 0) { cout << " FLAG written as " << myNFlagBits << " bits per flag" << endl; } IPosition tileShape = formTileShape(myTileSize, myTileSizePol, myTileSizeFreq, myWriteFloatData, myNPol, myNChan); int tileSize = tileShape.product() * 8; if (myWriteFloatData) { tileSize /= 2; } cout << " data tileshape = " << tileShape << " (tilesize = " << tileSize << " bytes)" << endl; if (!myWriteHDF5 && myUseMultiFile) { cout << " multi" << (myUseMultiFile==1 ? "file" : "hdf5"); int multiBlockSize = myMultiBlockSize; if (multiBlockSize <= 0) { multiBlockSize = tileSize; } cout << " (blocksize = " << multiBlockSize << " bytes)" << endl; } } String doOne (int seqnr, const String& msName) { int nbpp = myNBand / myNPart; // Form the MS name. // If it contains %d, use that to fill in the seqnr. // Otherwise append _seqnr to the name (unless a single part is done). String name; if (msName.find ("%d") != String::npos) { name = String::format (msName.c_str(), seqnr); } else { name = msName; if (!myDoSinglePart) { name += String::format ("_p%d", seqnr); } } // Create the MS. Timer timer; std::shared_ptr msmaker; if (myWriteHDF5) { msmaker = std::make_shared(); } else { msmaker = std::make_shared(); } IPosition dataTileShape = formTileShape (myTileSize, myTileSizePol, myTileSizeFreq, myWriteFloatData, myNPol, myNChan); myTileSize = dataTileShape.product() * 8; if (myMultiBlockSize < 0) { myMultiBlockSize = myTileSize; } msmaker->init (myRa, myDec, myAntPos, myCalcUVW, myWriteAutoCorr, myWriteFloatData, myWriteWeightSpectrum, myCreateImagerColumns, myNPol, myNChan, myStartFreq, myStepFreq, myFirstBand+seqnr*nbpp, nbpp, myNTimeField, myStartTime, myStepTime, name, myAntennaTableName, myNFlagBits, myFlagColumn, dataTileShape, myUseMultiFile, myMultiBlockSize); // Close all subtables to reduce nr of open files. msmaker->closeSubTables(); timer.show ("Created MS " + msName); timer.mark(); for (int i=0; iwriteTimeStep (myNTimeField, myWriteRowWise); } msmaker->flush(); timer.show ("Wrote " + String::toString(msmaker->nrow()) + " rows into MS " + msName); if (seqnr == 0 && !myUseAdios2) { msmaker->showCacheStatistics(); } return name; } void doAll() { int nthread = OMP::maxThreads(); Block msnames(myNPart); #ifdef _OPENMP #pragma omp parallel for schedule(dynamic) #endif for (int i=0; i //
      • MeasurementSets: // Handling of visibility data in MeasurementSets (structured tables). //
      • MSSel: // Handling of MeasurementSet selection. //
      • MSOper: // Miscellaneous MeasurementSet operations. // casacore-3.7.1/msfits/000077500000000000000000000000001476623553700146315ustar00rootroot00000000000000casacore-3.7.1/msfits/CMakeLists.txt000066400000000000000000000030651476623553700173750ustar00rootroot00000000000000# # CASA msfits # add_library (casa_msfits MSFits/FitsIDItoMS.cc MSFits/MSFitsIDI.cc MSFits/MSFitsInput.cc MSFits/MSFitsOutput.cc MSFits/MSFitsOutputAstron.cc MSFits/SDAntennaHandler.cc MSFits/SDDataDescHandler.cc MSFits/SDFeedHandler.cc MSFits/SDFieldHandler.cc MSFits/SDFITSHandler.cc MSFits/SDHistoryHandler.cc MSFits/SDMainHandler.cc MSFits/SDObservationHandler.cc MSFits/SDPointingHandler.cc MSFits/SDPolarizationHandler.cc MSFits/SDSourceHandler.cc MSFits/SDSpWinHandler.cc MSFits/SDSysCalHandler.cc MSFits/SDWeatherHandler.cc ) set(top_level_headers MSFits.h ) init_pch_support(casa_msfits ${top_level_headers}) target_link_libraries (casa_msfits casa_ms casa_fits ${CASACORE_ARCH_LIBS}) add_subdirectory (apps) install ( TARGETS casa_msfits LIBRARY DESTINATION lib${LIB_SUFFIX} ARCHIVE DESTINATION lib${LIB_SUFFIX} LIBRARY PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) install (FILES MSFits/FitsIDItoMS.h MSFits/MSFitsIDI.h MSFits/MSFitsInput.h MSFits/MSFitsOutput.h MSFits/MSFitsOutputAstron.h MSFits/SDAntennaHandler.h MSFits/SDDataDescHandler.h MSFits/SDFITSHandler.h MSFits/SDFeedHandler.h MSFits/SDFieldHandler.h MSFits/SDHistoryHandler.h MSFits/SDMainHandler.h MSFits/SDObservationHandler.h MSFits/SDPointingHandler.h MSFits/SDPolarizationHandler.h MSFits/SDSourceHandler.h MSFits/SDSpWinHandler.h MSFits/SDSysCalHandler.h MSFits/SDWeatherHandler.h DESTINATION include/casacore/msfits/MSFits ) install (FILES ${top_level_headers} DESTINATION include/casacore/msfits ) add_subdirectory (MSFits/test ${EXCL_ALL}) casacore-3.7.1/msfits/MSFits.h000066400000000000000000000044411476623553700161520ustar00rootroot00000000000000//# MSFits.h: FITS related functionality for MeasurementSets //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MSFITS_MSFITS_H #define MSFITS_MSFITS_H #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // // // FITS related functionality for MeasurementSets // // //
      • MeasurementSets module // // // // // // // The MeasurementSets module handles storage of telescope data in Casacore // tables and defines functions to access the data. // The MSFits module is an extra layer on top of MeasurementSets to convert // a MeasurementSet to and from FITS. // There are classes for three FITS formats: //
          //
        • Conventional UVFits //
        • IDIFits //
        • Single Dish FITS //
        //
        // // To avoid that package ms depends on FITS (and cfitsio), // all FITS-related MS code is put in a separate package. // //# //# // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/msfits/MSFits/000077500000000000000000000000001476623553700157765ustar00rootroot00000000000000casacore-3.7.1/msfits/MSFits/FitsIDItoMS.cc000066400000000000000000004076021476623553700203540ustar00rootroot00000000000000//# FITSIDItoMS.cc: Convert a FITS-IDI binary table to an AIPS++ Table. //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., 675 Mass Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //local debug switch int mydebug = 0; #ifdef MYDEBUG mydebug = 1; #endif // Returns the 0-based position of the key string in the map, // which is a list of strings. Looks for the "Which" occurrance // of the key. static Int getIndex(Vector& map, const String& key, uInt which = 0) { uInt count = 0; const uInt nMap = map.nelements(); for (uInt i = 0; i < nMap; i++) { if (map(i) == key) { if (count == which) { return i; } else { count++; } } } return -1; } // Like getIndex, but only checks for containment, not exact identity static Int getIndexContains(Vector& map, const String& key, uInt which = 0) { uInt count = 0; const uInt nMap = map.nelements(); for (uInt i = 0; i < nMap; i++) { if (map(i).contains(key)) { if (count == which) { return i; } else { count++; } } } return -1; } Bool FITSIDItoMS1::firstMain = True; // initialize the class variable firstMain Bool FITSIDItoMS1::firstSyscal = True; // initialize the class variable firstSyscal Bool FITSIDItoMS1::firstWeather = True; // initialize the class variable firstWeather Bool FITSIDItoMS1::firstGainCurve = True; // initialize the class variable firstGainCurve Bool FITSIDItoMS1::firstPhaseCal = True; // initialize the class variable firstPhaseCal Bool FITSIDItoMS1::firstEOP = True; // initialize the class variable firstEOP Double FITSIDItoMS1::rdate = 0.; // initialize the class variable rdate String FITSIDItoMS1::array_p = ""; // initialize the class variable array_p std::map FITSIDItoMS1::antIdFromNo; // initialize the class variable antIdFromNo std::map FITSIDItoMS1::digiLevels; // initialize the class variable digiLevels Vector FITSIDItoMS1::effChBw; // // Constructor // FITSIDItoMS1::FITSIDItoMS1(FitsInput& fitsin, const String& correlat, const Int& obsType, const Bool& initFirstMain, const Float& vanVleck, const Float& corVer) : BinaryTableExtension(fitsin), itsNrMSKs(10), itsMSKC(itsNrMSKs," "), itsMSKN(itsNrMSKs," "), itsMSKV(itsNrMSKs," "), itsgotMSK(itsNrMSKs,False), ///infile_p(fitsin), itsObsType(obsType), itsCorrelat(correlat), itsCorVer(corVer), itsVanVleck(vanVleck), msc_p(0) { itsLog = new LogIO(); // // Get some things to remember. // Int nfield = tfields(); // nr of fields in the FITS table itsNelem.resize(nfield); // nrs of elements per field itsNelem = 0; itsIsArray.resize(nfield); // array flags per field itsIsArray = False; // assume scalar-type if(initFirstMain){ firstMain = True; firstSyscal = True; firstWeather = True; firstGainCurve = True; firstPhaseCal = True; firstEOP = True; weather_hasWater_p = False; weather_hasElectron_p = False; antIdFromNo.clear(); digiLevels.clear(); rdate = 0.; array_p = ""; } // // Step 0: The mandatory and reserved FITS keywords have been read // and put into the data members by the BinaryTableExtension // constructor. // // Step 1: Now read the rest of the FITS keywords and put them // into the itsMSK... buffers (the ones with names like MSK*, // i.e. the MS-specific keywords) and into TableRecord itsKwSet // (the rest of the FITS keywords and EXTVER). // convertKeywords(); if (itsCorrelat.length() == 0 && array_p == "VLBA") itsCorrelat = "VLBA"; // // Step 1a: Read the table.info from the MSK table keywords TYPE, // SUBTYPE and README, and clear the relevant MSK buffer entries. // for (uInt ikey=0; ikey 0) { // // Read the first row of the FITS table into memory. // read(1); // // Fill the single row in itsCurRowTab from memory. // fillRow(); } } } void FITSIDItoMS1::fillRow() { // // Loop over each field. // for (Int icol=0; icol thisfield = *(FitsField* )&field(icol); Vector vec(itsNelem(icol)); for (Int ie=0; ie arrcol(tabcol); arrcol.put(0,vec.reform(tabcol.shapeColumn())); } else if (itsNelem(icol) == 1) { tabcol.putScalar(0,vec(0)); } } break; case FITS::BIT: { FitsField thisfield = *(FitsField* )&field(icol); Vector vec(itsNelem(icol)); for (uInt ie=0; ie arrcol(tabcol); arrcol.put(0,vec.reform(tabcol.shapeColumn())); } else if (itsNelem(icol) == 1) { tabcol.putScalar(0,vec(0)); } } break; case FITS::BYTE: { FitsField thisfield = *(FitsField* )&field(icol); Vector vec(itsNelem(icol)); for (Int ie=0; ie arrcol(tabcol); arrcol.put(0,vec.reform(tabcol.shapeColumn())); } else if (itsNelem(icol) == 1) { tabcol.putScalar(0,vec(0)); } } break; case FITS::CHAR: case FITS::STRING: { FitsField thisfield = *(FitsField* )&field(icol); char* cptr = (char* )thisfield.data(); uInt length = thisfield.nelements(); if (itsIsArray(icol)) { // // Decode the string into a vector of strings. // Using whitespac,etc. as separator. // IPosition shp (tabcol.shapeColumn()); uInt nr = shp.product(); istringstream istr(String(cptr,length)); Vector vec; istr >> vec; ArrayColumn arrcol(tabcol); if (vec.nelements() != nr) { // // Whitespace is too much; use newspace as separator. // First look for the true end (remove trailing blanks). // Remove leading and trailing [] if there. // while (length > 0 && (cptr[length-1] == '\0' || cptr[length-1] == ' ')) { length--; } if (length>1 && cptr[0] == '[' && cptr[length-1] == ']') { cptr++; length -= 2; } String str = String(cptr,length); Vector strvec = stringToVector (str, '\n'); vec.reference (strvec); if (vec.nelements() != nr) { cerr << "**Error: " << vec.nelements() << " values expected for column " << tabcol.columnDesc().name() << ", found " << nr << endl; vec.resize (nr, True); } } arrcol.put(0,vec.reform(shp)); } else { // // Look for the true end (remove trailing blanks). // while (length > 0 && (cptr[length-1] == '\0' || cptr[length-1] == ' ')) { length--; } String str = String(cptr,length); tabcol.putScalar(0,str); } } break; case FITS::SHORT: { FitsField thisfield = *(FitsField* )&field(icol); Vector vec(itsNelem(icol)); for (Int ie=0; ie arrcol(tabcol); arrcol.put(0,vec.reform(tabcol.shapeColumn())); } else if (itsNelem(icol) == 1) { tabcol.putScalar(0,vec(0)); } } break; case FITS::LONG: { FitsField thisfield = * (FitsField* )&field(icol); Vector vec(itsNelem(icol)); for (Int ie=0; ie arrcol(tabcol); arrcol.put(0,vec.reform(tabcol.shapeColumn())); } else if (itsNelem(icol) == 1) { tabcol.putScalar(0,vec(0)); } } break; case FITS::FLOAT: { FitsField thisfield = *(FitsField* )&field(icol); Vector vec(itsNelem(icol)); for (Int ie=0; ie arrcol(tabcol); arrcol.put(0,vec.reform(tabcol.shapeColumn())); } else if (itsNelem(icol) == 1) { tabcol.putScalar(0,vec(0)); } } break; case FITS::DOUBLE: { FitsField thisfield = *(FitsField* )&field(icol); Vector vec(itsNelem(icol)); for (Int ie=0; ie arrcol(tabcol); arrcol.put(0,vec.reform(tabcol.shapeColumn())); } else if (itsNelem(icol) == 1) { tabcol.putScalar(0,vec(0)); } } break; case FITS::COMPLEX: { FitsField thisfield = *(FitsField* )&field(icol); Vector vec(itsNelem(icol)); for (Int ie=0; ie arrcol(tabcol); arrcol.put(0,vec.reform(tabcol.shapeColumn())); } else if (itsNelem(icol) == 1) { tabcol.putScalar(0,vec(0)); } } break; case FITS::DCOMPLEX: { FitsField thisfield = *(FitsField* )&field(icol); Vector vec(itsNelem(icol)); for (Int ie=0; ie arrcol(tabcol); arrcol.put(0,vec.reform(tabcol.shapeColumn())); } else if (itsNelem(icol) == 1) { tabcol.putScalar(0,vec(0)); } } break; case FITS::ICOMPLEX: { FitsField thisfield = *(FitsField *)&field(icol); Vector vec(itsNelem(icol)); for (Int ie=0; ie arrcol(tabcol); arrcol.put(0,vec.reform(tabcol.shapeColumn())); } else if (itsNelem(icol) == 1) { tabcol.putScalar(0,vec(0)); } } break; default: // VADESC or NOVALUE (which shouldn't occur here cerr << "Error: unrecognized table data type for field " << icol << endl; cerr << "That should not have happened" << endl; continue; } // end of loop over switch } // end of loop over fields // // Loop over all virtual columns if necessary. // if (itsKwSet.nfields() > 0) { for (uInt icol=0; icolname(); if (!kw->isreserved()) { // // Non-reserved keyword: // // // Get the name of the keyword. // -- At present (1998, March 11) non-reserved FITS // keywords are not recognised as indexed. The index is // just considered part of the name. -- // kwname = kw->name(); // // If the name already occurs in itsKwSet, issue a warning // and overwrite the old keyword. // if (itsKwSet.isDefined(kwname)) { *itsLog << LogIO::WARN << "Duplicate keyword name : " << kwname << " most recent occurrance takes precedence" << LogIO::POST; itsKwSet.removeField(kwname); } // // Buffer the MS-specific keywords. // if (kwname(0,3)=="MSK") { iMSK = atoi(kwname.after(3).chars()); if (iMSK > 0) { if (iMSK > itsNrMSKs) { // Extend the MSK buffers with 10 elements. itsNrMSKs += 10; itsMSKC.resize(itsNrMSKs,True); itsMSKN.resize(itsNrMSKs,True); itsMSKV.resize(itsNrMSKs,True); itsgotMSK.resize(itsNrMSKs,True); for (uInt ikey=iMSK-1; ikeyasString(); val = val.before(trailing); if (kwname(3,1)=="C") { itsMSKC(iMSK-1) = val; } else if (kwname(3,1)=="N") { itsMSKN(iMSK-1) = val; } else if (kwname(3,1)=="V") { itsMSKV(iMSK-1) = val; } else { *itsLog << LogIO::WARN << "MSBinaryTable found unknown MSK keyword: " << kwname << ". It will be ignored" << LogIO::POST; } } else { *itsLog << LogIO::WARN << "MSBinaryTable found unknown MSK keyword: " << kwname << ". It will be ignored" << LogIO::POST; } } else { // Add a keyword of the proper type to the keyword // list. // switch (kw->type()) { case FITS::NOVALUE: itsKwSet.define(kwname,""); // NOVALUE fields become string keywords with an emtpy string. *itsLog << LogIO::NORMAL << "FITS::NOVALUE found" << LogIO::POST; break; case FITS::LOGICAL: itsKwSet.define(kwname, kw->asBool()); break; case FITS::CHAR: itsKwSet.define(kwname, kw->asString()); break; case FITS::STRING: itsKwSet.define(kwname, kw->asString()); break; case FITS::LONG: itsKwSet.define(kwname, kw->asInt()); break; case FITS::FLOAT: itsKwSet.define(kwname, kw->asFloat()); break; case FITS::DOUBLE: itsKwSet.define(kwname, kw->asDouble()); break; case FITS::COMPLEX: itsKwSet.define(kwname, kw->asComplex()); break; default: *itsLog << LogIO::WARN << "Internal error: unrecognized table data type for keyword " << kwname << " type = " << kw->type() << LogIO::POST; continue; } // // Add any comment in. // itsKwSet.setComment(kwname, kw->comm()); } } else { // // Reserved keywords are handled elsewhere. // } // end of if(!kw->isreserved()) } // end of loop over kw list // // Handle the version keyword; make it a table keyword. The // VERSION should be defined in a proper MSFITS file (written by // ms2fits), but if it is not, the version will get the value // FITS::minInt. // itsKwSet.define("VERSION", extver()); } // // Convert FITS field descriptions to TableColumn descriptions. Also // take into account the storage options specified in the MSK's. // void FITSIDItoMS1::describeColumns() { Int defaultOption = ColumnDesc::FixedShape; Int option = defaultOption; // // Loop over the fields in the FITS table. // Regex trailing(" *$"); // trailing blanks Int nfield = tfields(); // nr of fields in the FITS table ConstFitsKeywordList& kwl = kwlist(); // // Get shape vector from MAXISn fields for UV_DATA extensions // and determine if there are WEIGHTS in the UV data // String extname(FITSIDItoMS1::extname()); extname = extname.before(trailing); Vector maxis(0); if(extname=="UV_DATA"){ const FitsKeyword* kw; String kwname; kwl.first(); uInt ctr=0; weightypKwPresent_p = False; weightyp_p = ""; if (itsCorrelat == "DIFX" || itsCorrelat == "VLBA") weightyp_p = "CORRELAT"; nStokes_p = 1; nBand_p = 1; visScl_p = 1.0; while((kw = kwl.next())){ kwname = kw->name(); if(kwname.at(0,5)=="MAXIS"){ maxis.resize(++ctr,True); maxis(ctr-1)=kw->asInt(); // cout << "**maxis=" << maxis << endl; } else if(kwname.at(0,7)=="NO_STKD"){ nStokes_p = kw->asInt(); // cout << "**nStokes=" << nStokes_p << endl; } else if(kwname.at(0,7)=="NO_BAND"){ nBand_p = kw->asInt(); // cout << "**nBand=" << nBand_p << endl; } else if(kwname.at(0,8)=="WEIGHTYP"){ weightypKwPresent_p = True; weightyp_p = kw->asString(); weightyp_p.upcase(); weightyp_p.trim(); if(weightyp_p!="NORMAL" && weightyp_p!="CORRELAT"){ *itsLog << LogIO::WARN << "Found WEIGHTYP keyword with value \"" << weightyp_p << "\" in UV_DATA table. Presently this keyword is ignored." << LogIO::POST; } } else if(kwname.at(0,8)=="VIS_SCAL"){ visScl_p = kw->asDouble(); } } if(maxis.nelements()>1){ if(maxis(1)==2){ uv_data_hasWeights_p = False; } else if(maxis(1)==3){ uv_data_hasWeights_p = True; } else{ uv_data_hasWeights_p = False; *itsLog << LogIO::WARN << "Invalid value for MAXIS1 keyword in UV_DATA table " << maxis(1) << " should be 2 or 3. Will try to continue ..." << LogIO::POST; } } else{ uv_data_hasWeights_p = False; *itsLog << LogIO::WARN << "Could not find MAXIS1 keyword in UV_DATA table. FITS IDI file probably invalid." << LogIO::POST; } } for (Int icol=0; icol 1) { // multi-element vector or other array itsIsArray(icol) = True; // cout << " a multi-element non-String-type Array column"; } else { // cout << " a non-String-type column"; // // See whether MSK SHAPE is defined. If so: array. // for (uInt ikey=0; ikey dimvec(stringToVector(dimstr)); ndim = dimvec.nelements(); shape.resize(ndim); for (Int id=0; id (colname,"",shape,option)); } else { itsTableDesc.addColumn(ScalarColumnDesc(colname,"")); } break; case FITS::BYTE: // BYTE stored as uChar. if (itsIsArray(icol)) { itsTableDesc.addColumn(ArrayColumnDesc (colname,"",shape,option)); } else { itsTableDesc.addColumn(ScalarColumnDesc(colname,"")); } break; case FITS::SHORT: if (itsIsArray(icol)) { itsTableDesc.addColumn(ArrayColumnDesc (colname,"",shape,option)); } else { itsTableDesc.addColumn(ScalarColumnDesc(colname,"")); } break; case FITS::LONG: if (itsIsArray(icol)) { itsTableDesc.addColumn(ArrayColumnDesc (colname,"",shape,option)); } else { itsTableDesc.addColumn(ScalarColumnDesc(colname,"")); } break; case FITS::CHAR: case FITS::STRING: // was: A CHAR and STRING type is always a string, never an array. if (itsIsArray(icol)) { itsTableDesc.addColumn(ArrayColumnDesc (colname,"",shape,option)); } else { itsTableDesc.addColumn(ScalarColumnDesc(colname,"")); } break; case FITS::FLOAT: if (itsIsArray(icol)) { itsTableDesc.addColumn(ArrayColumnDesc (colname,"",shape,option)); } else { itsTableDesc.addColumn(ScalarColumnDesc(colname,"")); } break; case FITS::DOUBLE: if (itsIsArray(icol)) { itsTableDesc.addColumn(ArrayColumnDesc (colname,"",shape,option)); } else { itsTableDesc.addColumn(ScalarColumnDesc(colname,"")); } break; case FITS::COMPLEX: if (itsIsArray(icol)) { itsTableDesc.addColumn(ArrayColumnDesc (colname,"",shape,option)); } else { itsTableDesc.addColumn(ScalarColumnDesc(colname,"")); } break; case FITS::ICOMPLEX: // ICOMPLEX is promoted to DCOMPLEX so no precision is lost. case FITS::DCOMPLEX: if (itsIsArray(icol)) { itsTableDesc.addColumn(ArrayColumnDesc (colname,"",shape,option)); } else { itsTableDesc.addColumn(ScalarColumnDesc(colname,"")); } break; default: // VADESC or NOVALUE should not happen in a table. *itsLog << LogIO::WARN << "Internal error: column " << icol << " has untranslatable type " << field(icol).fieldtype() << LogIO::POST; continue; } // end of switch on FITS type // // Set the comment string if appropriate. (I don't really // understand, why it must be icol+1, but only that way the // comments are associated with the proper column names.) // if (kwl(FITS::TTYPE, icol+1)) { /// cout << "icol = " << icol << endl; /// cout << "colname = " << colname << endl; /// cout << "comment = " << kwl(FITS::TTYPE,icol+1)->comm() << endl; itsTableDesc.rwColumnDesc(colname).comment() = kwl(FITS::TTYPE,icol+1)->comm(); } // // Attach associated information. // // Units. Again, strings shorter than 8 characters were padded // with blanks; remove those. // String unitstr(tunit(icol)); unitstr = unitstr.before(trailing); itsTableDesc.rwColumnDesc(colname).rwKeywordSet() .define("UNIT", unitstr); } // end of loop over columns /* // // For a MeasurementSet main table we will work with tiled storage // managers. Define the hypercolumns needed. // //String extname(FITSIDItoMS1::extname()); //extname = extname.before(trailing); if (extname == "UV_DATA") { // // Define the tiled hypercube for the data and flag columns. // itsTableDesc.defineHypercolumn( "TiledData",7, //stringToVector(MS::columnName(MS::DATA)+","+ // MS::columnName(MS::FLAG))); stringToVector(MS::columnName(MS::DATA))); // // Define the tiled hypercube for the UVW column. // itsTableDesc.defineHypercolumn( "TiledUVW",2, stringToVector(MS::columnName(MS::UVW))); } */ } // // Convert the MS-specific keywords in the FITS binary table extension // header to a TableRecord (itsKwSet). // void FITSIDItoMS1::convertMSKeywords() { for (uInt ikey=0; ikey maxis(0); if(extname=="UV_DATA"){ Int nAxis = 0; uInt imaxis = 0; uInt idx = 0; // Bool setMAXIS = False; const FitsKeyword* kw; String kwname; kwl.first(); //while((kw = kwl.next())&& setMAXIS == False) while((kw = kwl.next())){ kwname = kw->name(); //cout << "kwname1=" << kwname <asInt(); //cout << "nAxis=" << nAxis << endl;; // setMAXIS = True; } } if (nAxis < 1) { throw(AipsError( "UV_DATA has no axes!")); } nPixel_p.resize(nAxis); refVal_p.resize(nAxis); refPix_p.resize(nAxis); delta_p.resize(nAxis); coordType_p.resize(nAxis); kwl.first(); while((kw = kwl.next())){ kwname = kw->name(); idx = kw->index() - 1; //cout << "kwname=" << kwname <5){ nPixel_p(imaxis++)=kw->asInt(); } if(kwname.at(0,5)=="CTYPE"){ coordType_p(idx)=kw->asString(); coordType_p(idx)=coordType_p(idx).before(trailing); } else if(kwname.at(0,5)=="CDELT"){ delta_p(idx)=kw->asDouble(); } else if(kwname.at(0,5)=="CRPIX"){ refPix_p(idx)=kw->asDouble(); } else if(kwname.at(0,5)=="CRVAL" ){ refVal_p(idx)=kw->asDouble(); } else {} } /** for(Int ctr=0;ctr(priGroup_p.crval(ctr)); //refPix_p(ctr) = static_cast(priGroup_p.crpix(ctr)); //delta_p(ctr) = static_cast(priGroup_p.cdelt(ctr)); coordType_p(ctr) = ctype(ctr); coordType_p(ctr) = coordType_p(ctr).before(trailing); refVal_p(ctr) = static_cast(crval(ctr)); refPix_p(ctr) = static_cast(crpix(ctr)); delta_p(ctr) = static_cast(cdelt(ctr)); } **/ // cout << "nPixel_p=" << nPixel_p << endl; // cout << "coordType_p=" << coordType_p << endl; // cout << "refVal_p=" << refVal_p << endl; // cout << "refPix_p=" << refPix_p << endl; // cout << "delta_p=" << delta_p << endl; } // Check if required axes are there if (getIndex(coordType_p, "COMPLEX") < 0) { *itsLog << "Data does not have a COMPLEX axis" << LogIO::EXCEPTION; } if (getIndex(coordType_p, "STOKES") < 0) { *itsLog << "Data does not have a STOKES axis" << LogIO::EXCEPTION; } if (getIndex(coordType_p, "FREQ") < 0) { *itsLog << "Data does not have a FREQ axis" << LogIO::EXCEPTION; } if ((getIndex(coordType_p, "RA") < 0) && (getIndex(coordType_p, "RA---SIN") < 0) && (getIndex(coordType_p, "RA---NCP") < 0) && (getIndex(coordType_p, "RA---SCP") < 0)) { *itsLog << "Data does not have a RA axis" << LogIO::EXCEPTION; } if ((getIndex(coordType_p, "DEC") < 0) && (getIndex(coordType_p, "DEC--SIN") < 0) && (getIndex(coordType_p, "DEC--NCP") < 0) && (getIndex(coordType_p, "DEC--SCP") < 0)) { *itsLog << "Data does not have a DEC axis" << LogIO::EXCEPTION; } // Sort out the order of the polarizations and find the sort indices // to put them in 'standard' order: PP,PQ,QP,QQ const uInt iPol = getIndex(coordType_p, "STOKES"); const uInt numCorr = nPixel_p(iPol); corrType_p.resize(numCorr); for (uInt i = 0; i < numCorr; i++) { // note: 1-based ref pix corrType_p(i) = ifloor(refVal_p(iPol) + (i+1-refPix_p(iPol))*delta_p(iPol)+0.5); // convert AIPS-convention Stokes description to Casacore enum // cout << "corrType_p="<< corrType_p(i) < 4) { *itsLog << "Unknown Correlation type: " << corrType_p(i) << LogIO::EXCEPTION; } } } Vector tmp(corrType_p.copy()); // Sort the polarizations to standard order. Could probably use // GenSortIndirect here. GenSort::sort(corrType_p); corrIndex_p.resize(numCorr); // Get the sort indices to rearrange the data to standard order for (uInt i = 0; i < numCorr; i++) { for (uInt j = 0; j < numCorr; j++) { if (corrType_p(j) == tmp(i)) corrIndex_p[i] = j; } } // Get the sort indexes to rearrange the data in swapped // cross-polarization order corrSwapIndex_p.resize(numCorr); for (uInt i = 0; i < numCorr; i++) { Int tmpType = tmp(i); switch (tmpType) { case Stokes::XY: tmpType = Stokes::YX; break; case Stokes::YX: tmpType = Stokes::XY; break; case Stokes::RL: tmpType = Stokes::LR; break; case Stokes::LR: tmpType = Stokes::RL; break; } for (uInt j = 0; j < numCorr; j++) { if (corrType_p(j) == tmpType) corrSwapIndex_p[i] = j; } } // Figure out the correlation products from the polarizations corrProduct_p.resize(2, numCorr); corrProduct_p = 0; for (uInt i = 0; i < numCorr; i++) { const Stokes::StokesTypes cType = Stokes::type(corrType_p(i)); Fallible receptor = Stokes::receptor1(cType); if (receptor.isValid()) { corrProduct_p(0,i) = receptor; // cout << "corrProcut_p(0,"<< i <<")=" << corrProduct_p(0,i); } else { *itsLog << "Cannot deduce receptor 1 for correlations of type: " << Stokes::name(cType) << LogIO::EXCEPTION; } receptor = Stokes::receptor2(cType); if (receptor.isValid()) { corrProduct_p(1,i) = receptor; // cout << "corrProcut_p(1,"<< i <<")=" << corrProduct_p(1,i); } else { *itsLog << "Cannot deduce receptor 2 for correlations of type: " << Stokes::name(cType) << LogIO::EXCEPTION; } } // Save the object name, we may need it (for single source fits) const FitsKeyword* kwp; object_p = (kwp=kw(FITS::OBJECT)) ? kwp->asString() : "unknown"; object_p=object_p.before(trailing); // Save the array name if(array_p=="" || array_p=="unknown"){ array_p = (kwp=kw(FITS::TELESCOP)) ? kwp->asString() : "unknown"; array_p=array_p.before(trailing); } if(array_p=="" || array_p=="unknown"){ array_p = (kwp=kw("ARRNAM")) ? kwp->asString() : "unknown"; array_p=array_p.before(trailing); } // Save the RA/DEC epoch (for ss fits) epoch_p = (kwp=kw(FITS::EPOCH)) ? kwp->asFloat() : 2000.0; // Get the spectral information freqsys_p = MFrequency::TOPO; restfreq_p = 0.0; Record header; Vector ignore; Bool ok = FITSKeywordUtil::getKeywords(header, kwlist(), ignore); if (ok) { Int spectralAxis; Double referenceChannel, referenceFrequency, deltaFrequency; Vector frequencies; MDoppler::Types velPref; // Many of the following aren't used since they have been obtained // in other ways. ok = FITSSpectralUtil::fromFITSHeader(spectralAxis, referenceChannel, referenceFrequency, deltaFrequency, frequencies, freqsys_p, velPref, restfreq_p, *itsLog, header); } } void FITSIDItoMS1::setupMeasurementSet(const String& MSFileName, Bool useTSM, Bool mainTbl, Bool addCorrMod, Bool addSyscal, Bool addWeather, Bool addGainCurve, Bool addPhaseCal, Bool addEOP) { Int nCorr = 0; Int nChan = 0; Int nIF_p = 0; String telescop; if(mainTbl) { nCorr = nPixel_p(getIndex(coordType_p,"STOKES")); nChan = nPixel_p(getIndex(coordType_p,"FREQ")); nIF_p = getIndex(coordType_p,"BAND"); if (nIF_p>=0) { nIF_p=nPixel_p(nIF_p); } else { nIF_p=1; } // determine telescop here } //cout << "===> nIF_p=" << nIF_p < tiledDataNames; String hcolName=String("Tiled")+String("DATA"); td.defineHypercolumn(hcolName, 3, stringToVector("DATA")); tiledDataNames.resize(1); tiledDataNames[0] = hcolName; // Add this optional column (random group fits can have a // weight per visibility) if the FITS IDI data actually contains it if(uv_data_hasWeights_p){ MS::addColumnToDesc(td, MS::WEIGHT_SPECTRUM, 2); MS::addColumnToDesc(td, MS::SIGMA_SPECTRUM, 2); } if(mainTbl && useTSM) { td.defineHypercolumn("TiledDATA",3, stringToVector(MS::columnName(MS::DATA))); td.defineHypercolumn("TiledFlag",3, stringToVector(MS::columnName(MS::FLAG))); td.defineHypercolumn("TiledFlagCategory",4, stringToVector(MS::columnName(MS::FLAG_CATEGORY))); if(uv_data_hasWeights_p){ td.defineHypercolumn("TiledWgtSpectrum",3, stringToVector(MS::columnName(MS::WEIGHT_SPECTRUM))); } td.defineHypercolumn("TiledUVW",2, stringToVector(MS::columnName(MS::UVW))); td.defineHypercolumn("TiledWgt",2, stringToVector(MS::columnName(MS::WEIGHT))); td.defineHypercolumn("TiledSigma", 2, stringToVector(MS::columnName(MS::SIGMA))); } SetupNewTable newtab(MSFileName, td, Table::New); // Set the default Storage Manager to be the Incr one uInt cache_val=32768; IncrementalStMan incrStMan ("ISMData",cache_val); newtab.bindAll(incrStMan, True); // Choose an appropriate tileshape IPosition dataShape(2, nCorr, nChan); IPosition tshape = MSTileLayout::tileShape(dataShape, itsObsType, telescop); if(tshape.nelements() != 3){ throw(AipsError("TileShape has to have 3 elememts ") ); } IPosition tileShape(tshape); if(mainTbl){ IncrementalStMan incrStMan0("Array_ID",cache_val); newtab.bindColumn(MS::columnName(MS::ARRAY_ID), incrStMan0); IncrementalStMan incrStMan1("EXPOSURE",cache_val); newtab.bindColumn(MS::columnName(MS::EXPOSURE), incrStMan1); IncrementalStMan incrStMan2("FEED1",cache_val); newtab.bindColumn(MS::columnName(MS::FEED1), incrStMan2); IncrementalStMan incrStMan3("FEED2",cache_val); newtab.bindColumn(MS::columnName(MS::FEED2), incrStMan3); IncrementalStMan incrStMan4("FIELD_ID",cache_val); newtab.bindColumn(MS::columnName(MS::FIELD_ID), incrStMan4); IncrementalStMan incrStMan6("INTERVAL",cache_val); newtab.bindColumn(MS::columnName(MS::INTERVAL), incrStMan6); IncrementalStMan incrStMan7("OBSERVATION_ID",cache_val); newtab.bindColumn(MS::columnName(MS::OBSERVATION_ID), incrStMan7); IncrementalStMan incrStMan8("PROCESSOR_ID",cache_val); newtab.bindColumn(MS::columnName(MS::PROCESSOR_ID), incrStMan8); IncrementalStMan incrStMan9("SCAN_NUMBER",cache_val); newtab.bindColumn(MS::columnName(MS::SCAN_NUMBER), incrStMan9); IncrementalStMan incrStMan10("STATE_ID",cache_val); newtab.bindColumn(MS::columnName(MS::STATE_ID), incrStMan10); IncrementalStMan incrStMan11("TIME",cache_val); newtab.bindColumn(MS::columnName(MS::TIME), incrStMan11); IncrementalStMan incrStMan12("TIME_CENTROID",cache_val); newtab.bindColumn(MS::columnName(MS::TIME_CENTROID), incrStMan12); // Bind FLAG_ROW, ANTENNA1, ANTENNA2 and DATA_DESC_ID to the standardStMan // as they may change sufficiently frequently to make the // incremental storage manager inefficient for these columns. StandardStMan aipsStMan0("ANTENNA1", cache_val); newtab.bindColumn(MS::columnName(MS::ANTENNA1), aipsStMan0); StandardStMan aipsStMan1("ANTENNA2", cache_val); newtab.bindColumn(MS::columnName(MS::ANTENNA2), aipsStMan1); StandardStMan aipsStMan2("DATA_DESC_ID", cache_val); newtab.bindColumn(MS::columnName(MS::DATA_DESC_ID), aipsStMan2); StandardStMan aipsStMan3("FLAG_ROW",cache_val/4); newtab.bindColumn(MS::columnName(MS::FLAG_ROW), aipsStMan3); TiledShapeStMan tiledStMan1f("TiledFlag",tileShape); TiledShapeStMan tiledStMan1fc("TiledFlagCategory", IPosition(4,tileShape(0),tileShape(1),1, tileShape(2))); TiledShapeStMan tiledStMan2("TiledWgtSpectrum",tileShape); TiledColumnStMan tiledStMan3("TiledUVW",IPosition(2,3,1024)); TiledShapeStMan tiledStMan4("TiledWgt", IPosition(2,tileShape(0),tileShape(2))); TiledShapeStMan tiledStMan5("TiledSigma", IPosition(2,tileShape(0),tileShape(2))); // Bind the DATA, FLAG & WEIGHT_SPECTRUM columns to the tiled stman TiledShapeStMan tiledStMan1Data("TiledDATA",tileShape); newtab.bindColumn(MS::columnName(MS::DATA), tiledStMan1Data); newtab.bindColumn(MS::columnName(MS::FLAG),tiledStMan1f); newtab.bindColumn(MS::columnName(MS::FLAG_CATEGORY),tiledStMan1fc); if(uv_data_hasWeights_p){ newtab.bindColumn(MS::columnName(MS::WEIGHT_SPECTRUM),tiledStMan2); } newtab.bindColumn(MS::columnName(MS::UVW),tiledStMan3); newtab.bindColumn(MS::columnName(MS::WEIGHT),tiledStMan4); newtab.bindColumn(MS::columnName(MS::SIGMA),tiledStMan5); } // avoid lock overheads by locking the table permanently TableLock lock(TableLock::AutoLocking); MeasurementSet ms(newtab,lock); //MeasurementSet ms(newtab); // create all subtables // we make new tables with 0 rows Table::TableOption option=Table::New; // Set up the subtables for the UVFITS MS ms.createDefaultSubtables(option); // Since the MS SOURCE table is presently not filled, // its creation is commented out here. // // add the optional Source sub table to allow for // // specification of the rest frequency // TableDesc sourceTD=MSSource::requiredTableDesc(); // SetupNewTable sourceSetup(ms.sourceTableName(),sourceTD,option); // ms.rwKeywordSet().defineTable(MS::keywordName(MS::SOURCE), // Table(sourceSetup,0)); if(addCorrMod){ cout << "Correlator model table setup needs to be inplemented." << endl; } if(addSyscal){ TableDesc td = MSSysCal::requiredTableDesc(); MSSysCal::addColumnToDesc(td, MSSysCal::TSYS); SetupNewTable tabSetup(ms.sysCalTableName(), td, option); ms.rwKeywordSet().defineTable(MS::keywordName(MS::SYSCAL), Table(tabSetup)); } if(addWeather){ TableDesc td = MSWeather::requiredTableDesc(); MSWeather::addColumnToDesc(td, MSWeather::DEW_POINT); if(weather_hasWater_p) MSWeather::addColumnToDesc(td, MSWeather::H2O); if(weather_hasElectron_p) MSWeather::addColumnToDesc(td, MSWeather::IONOS_ELECTRON); MSWeather::addColumnToDesc(td, MSWeather::PRESSURE); MSWeather::addColumnToDesc(td, MSWeather::TEMPERATURE); MSWeather::addColumnToDesc(td, MSWeather::WIND_DIRECTION); MSWeather::addColumnToDesc(td, MSWeather::WIND_SPEED); SetupNewTable tabSetup(ms.weatherTableName(), td, option); ms.rwKeywordSet().defineTable(MS::keywordName(MS::WEATHER), Table(tabSetup)); } if(addGainCurve){ TableDesc td; String name = "GAIN_CURVE"; td.comment() = "Gain curve table"; td.addColumn(ScalarColumnDesc("ANTENNA_ID", "Antenna identifier")); td.addColumn(ScalarColumnDesc("FEED_ID", "Feed identifier")); td.addColumn(ScalarColumnDesc("SPECTRAL_WINDOW_ID", "Spectral window identifier")); td.addColumn(ScalarColumnDesc("TIME", "Midpoint of time for which this set of parameters is accurate")); td.addColumn(ScalarColumnDesc("INTERVAL", "Interval for which this set of parameters is accurate")); td.addColumn(ScalarColumnDesc("TYPE", "Gain curve type")); td.addColumn(ScalarColumnDesc("NUM_POLY", "Number of terms in polynomial")); td.addColumn(ArrayColumnDesc("GAIN", "Gain polynomial")); td.addColumn(ArrayColumnDesc("SENSITIVITY", "Antenna sensitivity")); TableMeasValueDesc measVal(td, "TIME"); TableMeasDesc measCol(measVal); measCol.write(td); TableQuantumDesc timeTqd(td, "TIME", Unit("s")); timeTqd.write(td); TableQuantumDesc intervalTqd(td, "INTERVAL", Unit("s")); intervalTqd.write(td); TableQuantumDesc sensTqd(td, "SENSITIVITY", Unit("K/Jy")); sensTqd.write(td); SetupNewTable tableSetup(ms.tableName() + "/" + name, td, option); ms.rwKeywordSet().defineTable("GAIN_CURVE", Table(tableSetup)); } if(addPhaseCal){ TableDesc td; String name = "PHASE_CAL"; td.comment() = "Phase calibration table"; td.addColumn(ScalarColumnDesc("ANTENNA_ID", "Antenna identifier")); td.addColumn(ScalarColumnDesc("FEED_ID", "Feed identifier")); td.addColumn(ScalarColumnDesc("SPECTRAL_WINDOW_ID", "Spectral window identifier")); td.addColumn(ScalarColumnDesc("TIME", "Midpoint of time for which this set of parameters is accurate")); td.addColumn(ScalarColumnDesc("INTERVAL", "Interval for which this set of parameters is accurate")); td.addColumn(ScalarColumnDesc("NUM_TONES", "Number of phase-cal tones")); td.addColumn(ArrayColumnDesc("TONE_FREQUENCY", "Phase-cal tone frequency")); td.addColumn(ArrayColumnDesc("PHASE_CAL", "Phase-cal measurement")); td.addColumn(ScalarColumnDesc("CABLE_CAL", "Cable calibration measurement")); TableMeasValueDesc measVal(td, "TIME"); TableMeasDesc measCol(measVal); measCol.write(td); TableQuantumDesc timeTqd(td, "TIME", Unit("s")); timeTqd.write(td); TableQuantumDesc intervalTqd(td, "INTERVAL", Unit("s")); intervalTqd.write(td); TableQuantumDesc toneFreqTqd(td, "TONE_FREQUENCY", Unit("Hz")); toneFreqTqd.write(td); TableQuantumDesc cableCalTqd(td, "CABLE_CAL", Unit("s")); cableCalTqd.write(td); SetupNewTable tableSetup(ms.tableName() + "/" + name, td, option); ms.rwKeywordSet().defineTable("PHASE_CAL", Table(tableSetup)); } if(addEOP){ TableDesc td; String name = "EARTH_ORIENTATION"; td.comment() = "Earth orientation parameters table"; td.addColumn(ScalarColumnDesc("TIME", "Time for which this set of parameters is accurate")); td.addColumn(ScalarColumnDesc("OBSERVATION_ID", "Observation identifier")); td.addColumn(ScalarColumnDesc("UT1_UTC", "UT1-UTC")); td.addColumn(ArrayColumnDesc("PM", "Position of celestial pole")); td.addColumn(ScalarColumnDesc("TYPE", "EOP type")); TableMeasValueDesc measVal(td, "TIME"); TableMeasDesc measCol(measVal); measCol.write(td); TableQuantumDesc timeTqd(td, "TIME", Unit("s")); timeTqd.write(td); TableQuantumDesc ut1utcTqd(td, "UT1_UTC", Unit("s")); ut1utcTqd.write(td); TableQuantumDesc pmTqd(td, "PM", Unit("rad")); pmTqd.write(td); SetupNewTable tableSetup(ms.tableName() + "/" + name, td, option); ms.rwKeywordSet().defineTable("EARTH_ORIENTATION", Table(tableSetup)); } // update the references to the subtable keywords ms.initRefs(); { // Set the TableInfo TableInfo& info(ms.tableInfo()); info.setType(TableInfo::type(TableInfo::MEASUREMENTSET)); info.setSubType(String("FITS-IDI")); info.readmeAddLine ("This is a measurement set Table holding astronomical observations"); } ms_p=ms; msc_p=new MSColumns(ms_p); } // Van Vleck relationship static Double rho_2(Double r) { return sin((M_PI * r) / 2); } // Fred Schwab's rational approximation for 2-bit sampling with n = // 3.336 and optimally chosen v_0. See VLBA Correlator Memo 75. static Double rho_4(Double r) { Double rr = r*r; Double num, den; num = 1.1329552 - 3.1056902 * rr + 2.9296994 * rr*rr - 0.90122460 * rr*rr*rr; den = 1 - 2.7056559 * rr + 2.5012473 * rr*rr - 0.73985978 * rr*rr*rr; return (num / den) * r; } void FITSIDItoMS1::fillMSMainTable(const String& MSFileName, Int& nField, Int& nSpW) { // Get access to the MS columns //MSColumns& msc(*msc_p); MeasurementSet ms(MSFileName,Table::Update); MSColumns msc(ms); if(!firstMain){ ms_p = ms; msc_p = new MSColumns(ms_p); } const Regex trailing(" *$"); // trailing blanks // get the random group parameter names Int tFields; //(nParams) Int nRows; //(nGroups) Int MSnRows; MSnRows = msc.nrow(); Vector scans; scans=0; msc.scanNumber().getColumn(scans); //cout << msc.scanNumber().getColumn() < flagCat(nCorr,nChan,nCat,False); Matrix flag = flagCat.xyPlane(0); // references flagCat's storage // find out the indices for U, V and W, there are several naming schemes Int iU,iV,iW; iU = getIndexContains(tType,"UU"); iV = getIndexContains(tType,"VV"); iW = getIndexContains(tType,"WW"); if (iU < 0 || iV < 0 || iW < 0) { throw(AipsError("FitsIDItoMS: Cannot find UVW information")); } // get index for baseline Int iBsln = getIndex(tType, "BASELINE"); // get indices for time Int iTime0 = getIndex(tType, "DATE"); Int iTime1 = getIndex(tType, "TIME"); // get index for source Int iSource = getIndex(tType, "SOURCE_ID"); if (iSource < 0) iSource = getIndex(tType, "SOURCE"); // get index for Freq Int iFreq = getIndex(tType, "FREQID"); // get index for FLUX Int iFlux = getIndex(tType, "FLUX"); // get index for Integration time Int iInttim = getIndex(tType, "INTTIM"); // get index for weight Int iWeight = getIndex(tType, "WEIGHT"); /* cout << "iU=" << iU << endl; cout << "iV=" << iV << endl; cout << "iW=" << iW << endl; cout << "iBsln=" << iBsln << endl; cout << "iTime0=" << iTime0 << endl; cout << "iTime1=" << iTime1 << endl; cout << "iSource=" << iSource << endl; cout << "iFreq=" << iFreq << endl; cout << "iFlux=" << iFlux << endl; */ receptorAngle_p.resize(1); nAnt_p=0; *itsLog << LogIO::NORMAL << "Reading and writing visibility data"<< LogIO::POST; Int row=-1; Double startTime; Float interval; startTime=0.0; interval=1; ProgressMeter meter(0.0, nRows*1.0, "FITS-IDI Filler", "Rows copied", "", "", True, nRows/100); Vector uvw(3); // Move this temporary out of the loop Vector _uvw(3); Int lastSpW; lastSpW=-1; Int putrow = -1; // Double lastTime=0; // Bool lastRowFlag=False; Int nScan = 0; if (firstMain) { putrow = -1; } else { putrow = MSnRows - 1; nScan = scans(putrow) + 1; } //cout << "scanNumber=" << nScan<< endl; for (Int trow=0; trow(data_addr[iTime0])), sizeof(Double)); time *= tscal(iTime0); time += tzero(iTime0); time -= JDofMJD0; //cout << "TIME=" << time << endl; if (iTime1>=0){ Double time1; memcpy(&time1, (static_cast(data_addr[iTime1])), sizeof(Double)); time1 *= tscal(iTime1); time1 += tzero(iTime1); time += time1; } //cout << "TIME=" << time << endl; Int _baseline; Float baseline; memcpy(&_baseline, (static_cast(data_addr[iBsln])), sizeof(Int)); baseline=static_cast(_baseline); baseline *= tscal(iBsln); baseline += tzero(iBsln); //cout << "BASELINE=" << baseline << endl; if(field(iU).fieldtype() == FITS::FLOAT) { uvw(0) = *static_cast(data_addr[iU]); } else { uvw(0) = *static_cast(data_addr[iU]); } uvw(0) *= tscal(iU); uvw(0) += tzero(iU); //cout << "uvw(0)=" << uvw(0) << endl; if(field(iV).fieldtype() == FITS::FLOAT) { uvw(1) = *static_cast(data_addr[iV]); } else { uvw(1) = *static_cast(data_addr[iV]); } uvw(1) *= tscal(iV); uvw(1) += tzero(iV); //cout << "uvw(1)=" << uvw(1) << endl; if(field(iW).fieldtype() == FITS::FLOAT) { uvw(2) = *static_cast(data_addr[iW]); } else { uvw(2) = *static_cast(data_addr[iW]); } uvw(2) *= tscal(iW); uvw(2) += tzero(iW); //cout << "uvw(2)=" << uvw(2) << endl; time *= C::day; // cout << "TIME=" << setprecision(11) << time << endl; if (row<0) { startTime = time; if (firstMain){ startTime_p = startTime; // cout << "startTime is set to " << startTime << endl; } } // If integration time is available, use it: if (iInttim > -1) { memcpy(&interval, (static_cast(data_addr[iInttim])), sizeof(Float)); interval *= tscal(iInttim); // cout << "INTTIM=" << setprecision(11) << interval << endl; } else { // make a guess at the integration time if (row<0) { *itsLog << LogIO::WARN << "UV_DATA table contains no integration time information. Will try to derive it from TIME." << LogIO::POST; } if (time > startTime) { interval=time-startTime; msc.interval().fillColumn(interval); msc.exposure().fillColumn(interval); startTime = DBL_MAX; // do this only once } } if(trow==nRows-1){ lastTime_p = time+interval; } Int array = Int(100.0*(baseline - Int(baseline)+0.001)); Int ant1 = Int(baseline)/256; Int ant2 = Int(baseline) - ant1*256; if(antIdFromNo.find(ant1) != antIdFromNo.end()){ ant1 = antIdFromNo[ant1]; } else{ *itsLog << LogIO::SEVERE << "Inconsistent input dataset: unknown ANTENNA_NO " << ant1 << " in baseline used in UV_DATA table." << LogIO::EXCEPTION; } if(antIdFromNo.find(ant2) != antIdFromNo.end()){ ant2 = antIdFromNo[ant2]; } else{ *itsLog << LogIO::SEVERE << "Inconsistent input dataset: unknown ANTENNA_NO " << ant2 << " in baseline used in UV_DATA table." << LogIO::EXCEPTION; } nAnt_p = max(nAnt_p,ant1+1); nAnt_p = max(nAnt_p,ant2+1); Bool doConjugateVis = False; if(ant1>ant2){ // swap indices and multiply UVW by -1 Int tant = ant1; ant1 = ant2; ant2 = tant; uvw *= -1.; doConjugateVis = True; } // Convert U,V,W from units of seconds to meters uvw *= C::c; Int count = 0; Float test_baseline; memcpy(&test_baseline,fitsrow,sizeof(Int)); //cout << "*****TEST_BASELINE FITS=" << test_baseline << endl; memcpy(&test_baseline,table,sizeof(Int)); //cout << "*****TEST_BASELINE TABLE=" << test_baseline << endl; Int new_baseline; memcpy(&new_baseline,static_cast(data_addr[iBsln]),sizeof(Int)); //cout << "*****NEW_BASELINE ADDR=" << new_baseline << endl; Float visReal = 0.; Float visImag = 0.; Float visWeight = 1.; Int nIF_p = 0; nIF_p = getIndex(coordType_p,"BAND"); if (nIF_p>=0) { nIF_p=nPixel_p(nIF_p); } else { nIF_p=1; } Double weightScale = visScl_p; if (itsCorrelat == "VLBA" && itsCorVer >= 4.17) weightScale *= interval; //cout <<"ifnomax ="<(data_addr[iFlux])) + count++, sizeof(Float)); // if(count<9){ // cout << "COUNT=" << count <<"ifno="<< ifno <<"chan="<< chan<<"pol="<< pol << " corrindex " << corrIndex_p[pol] << endl; // } //visReal *= tscal(iFlux); //visReal += tzero(iFlux); memcpy(&visImag, (static_cast(data_addr[iFlux])) + count++, sizeof(Float)); //visImag *= tscal(iFlux); //visImag += tzero(iFlux); // if(count<10){ // cout<<" visReal="<< visReal << "visImag="<< visImag<(data_addr[iFlux])) + count++, sizeof(Float)); } else if (iWeight>=0) { memcpy(&visWeight, (static_cast(data_addr[iWeight])) + ifno * nStokes_p + pol, sizeof(Float)); } //const Float wt = priGroup_p(count++); const Int p = doConjugateVis ? corrSwapIndex_p[pol] : corrIndex_p[pol]; if (visWeight <= 0.0) { weightSpec(p, chan) = -visWeight; flag(p, chan) = True; } else { weightSpec(p, chan) = visWeight; flag(p, chan) = False; } if(doConjugateVis){ // need a conjugation to follow the ant1<=ant2 rule vis(p, chan) = Complex(visReal, visImag); // NOTE: this means no conjugation of visibility because of FITS-IDI convention! } else{ vis(p, chan) = Complex(visReal, -visImag); // NOTE: conjugation of visibility! // FITS-IDI convention is conjugate of AIPS and CASA convention! } } } // Apply digital corrections to data correlated with the DiFX // correlator as these have not been applied at the correlator. // Various constants have been taken from VLBA Scientific Memo // 12 as the DiFX tries to emulate the original VLBA FX // correlator as closely as possible. Note that DiFX doesn't // suffer from saturation so the saturation correction is // omitted here. DiFX currently doesn't support Hanning // weighting. // // The correction applied here matches what the AIPS FITLD task // does for DiFX-correlated data as closely as possible. Two // notable differences in the implementation. This code simply // uses the FFTPACK cosine tranform where FITLD implements its // own cosine tranform based on an FFT. And this code simply // uses the expressions for rho_2 and rho_4 given in the // literature instead of using a lookup table. if (itsCorrelat == "DIFX" || itsCorrelat == "VLBA") { const double A = 5.36; const Double H = 0.87890625; Double bfacta, bfactc; Double Rm, gamma, alfa, sat; Double (*rho)(Double) = NULL; if (digiLevels[ant1] == 4 && digiLevels[ant2] == 4) { Rm = 4.3048; alfa = 0.882518; gamma = 3.335875 * 64.0 / 63.0; rho = rho_4; } else if (digiLevels[ant1] == 2 && digiLevels[ant2] == 2) { Rm = 1.0; alfa = 2.0 / M_PI; gamma = 1.0 * 64.0 / 63.0; rho = rho_2; } else if ((digiLevels[ant1] == 2 && digiLevels[ant2] == 4) || (digiLevels[ant1] == 4 && digiLevels[ant2] == 2)) { Rm = 5.8784; alfa = 0.882518; gamma = 3.335875 * 64.0 / 63.0; } else { // Unsupported. Assume a large number of levels (alpha = // 1.0) and trust the user is going to normalize the // visibilities (e.g. by running the accor task in CASA). Rm = 1.0 / (A * H); alfa = 1.0; gamma = 1.0; } if (itsVanVleck != 0.0) { alfa = 1.0; rho = NULL; } bfactc = (gamma*gamma) / (A * Rm * alfa * H); bfacta = (gamma*gamma) / (A * Rm * H); if (ant1 != ant2 || rho == NULL) { // Cross-correlations vis *= Complex(bfactc); } else { // Auto-correlations for (Int p=0; p 2 ? 0.25 : 0.125) * weightSpec(p, chan); else sat = 1.0; vis(p, chan) = sat * fftIn[chan] / (2*nChan); } } else { for (Int chan=0; chan=0) { memcpy(&spW, (static_cast(data_addr[iFreq])), sizeof(Int)); spW *= (Int)tscal(iFreq); spW += (Int)tzero(iFreq); spW--; // make 0-based if (nIF_p>0) { spW *=nIF_p; spW+=ifno; } } if (spW!=lastSpW) { msc.dataDescId().put(putrow,spW); nSpW = max(nSpW, spW+1); lastSpW=spW; } for (Int chan=0; chan 0.0) sigmaSpec(p, chan) = 1.0f / sqrt(weightSpec(p, chan)); else sigmaSpec(p, chan) = 1.0f; } } // fill in values for all the unused columns msc.feed1().put(putrow,0); msc.feed2().put(putrow,0); msc.flagRow().put(putrow,False); msc.processorId().put(putrow,-1); msc.observationId().put(putrow,0); msc.stateId().put(putrow,-1); Vector tmpValue(nCorr); tmpValue=1.0; msc.sigma().put(putrow,tmpValue); tmpValue=0.0; msc.weight().put(putrow,tmpValue); msc.interval().put(putrow,interval); msc.exposure().put(putrow,interval); msc.scanNumber().put(putrow,nScan); msc.data().put(putrow,vis); const Vector sigma = partialMedians(sigmaSpec, IPosition(1, 1)); const Vector weight = partialMedians(weightSpec, IPosition(1, 1)); msc.sigma().put(putrow,sigma); msc.weight().put(putrow,weight); if(uv_data_hasWeights_p){ msc.sigmaSpectrum().put(putrow,sigmaSpec); msc.weightSpectrum().put(putrow,weightSpec); } msc.flag().put(putrow,flag); msc.flagCategory().put(putrow,flagCat); Bool rowFlag=allEQ(flag,True); msc.flagRow().put(putrow,rowFlag); msc.antenna1().put(putrow,ant1); msc.antenna2().put(putrow,ant2); msc.arrayId().put(putrow,array); msc.time().put(putrow,time); msc.timeCentroid().put(putrow,time+interval/2.); msc.uvw().put(putrow,uvw); // store the sourceId Int sourceId = 0; if (iSource>=0) { // make 0-based memcpy(&sourceId, (static_cast(data_addr[iSource])), sizeof(Int)); sourceId *= (Int)tscal(iSource); sourceId += (Int)tzero(iSource); sourceId--; // make 0-based } msc.fieldId().put(putrow,sourceId); nField = max(nField, sourceId+1); } // end for(ifno=0 ... meter.update((trow+1)*1.0); } // end for(trow=0 ... // fill the receptorAngle with defaults, just in case there is no AN table receptorAngle_p=0; } // fill Observation table void FITSIDItoMS1::fillObsTables() { const Regex trailing(" *$"); // trailing blanks const FitsKeyword* kwp; Vector times(2); if(firstMain) { ms_p.observation().addRow(); String observer; observer = (kwp=kw(FITS::OBSERVER)) ? kwp->asString() : "unknown"; observer=observer.before(trailing); MSObservationColumns msObsCol(ms_p.observation()); msObsCol.observer().put(0,observer); String obscode; obscode = (kwp=kw("OBSCODE")) ? kwp->asString() : ""; obscode=obscode.before(trailing); msObsCol.project().put(0,obscode); String telescope= (kwp=kw(FITS::TELESCOP)) ? kwp->asString() : array_p; telescope=telescope.before(trailing); if(telescope=="" || telescope=="unknown"){ telescope= (kwp=kw("ARRNAM")) ? kwp->asString() : "unknown"; telescope=telescope.before(trailing); } msObsCol.telescopeName().put(0,telescope); msObsCol.scheduleType().put(0, ""); //String date; //date = (kwp=kw(FITS::DATE_OBS)) ? kwp->asString() : ""; //if (date=="") { // try FITS::DATE instead // (but this will find DATE-MAP which may not be correct...) // date = (kwp=kw(FITS::DATE)) ? kwp->asString() : ""; //} times(0)=startTime_p; times(1)=lastTime_p; // time in the last record of the input data //cout << "times=" << times(0) << "," << times(1)<< endl; msObsCol.timeRange().put(0,times); msObsCol.releaseDate().put(0,times(0)); //just use TIME_RANGE for now msObsCol.flagRow().put(0,False); } else { times = msc_p->observation().timeRange()(0); MSObservationColumns msObsCol(ms_p.observation()); times(1)=lastTime_p; msObsCol.timeRange().put(0,times); } // Store all keywords from the first HISTORY keyword onwards in History table String date; date = (kwp=kw(FITS::DATE_OBS)) ? kwp->asString() : ""; if (date=="") { date = (kwp=kw(FITS::DATE)) ? kwp->asString() : ""; } if (date=="") date = "2000-01-01"; MVTime timeVal; MEpoch::Types epochRef; FITSDateUtil::fromFITS(timeVal,epochRef,date,"UTC"); Double time=timeVal.second(); String history = (kwp=kw(FITS::HISTORY)) ? kwp->comm(): ""; history = history.before(trailing); MSHistoryColumns msHisCol(ms_p.history()); Int row=-1; while (history!="") { ms_p.history().addRow(); row++; msHisCol.observationId().put(row,0); msHisCol.time().put(row,time); msHisCol.priority().put(row,"NORMAL"); msHisCol.origin().put(row,"FITSIDItoMS1::fillObsTables"); msHisCol.application().put(row,"ms"); msHisCol.message().put(row,history); history = (kwp=nextkw()) ? kwp->comm(): ""; history = history.before(trailing); } } void FITSIDItoMS1::fillAntennaTable() { *itsLog << LogOrigin("FitsIDItoMS", "fillAntennaTable"); const Regex trailing(" *$"); // trailing blanks TableRecord btKeywords=getKeywords(); Int nAnt=nrows(); receptorAngle_p.resize(2*nAnt); Vector arrayXYZ(3); arrayXYZ=0.0; if(!btKeywords.isDefined("ARRAYX")||!btKeywords.isDefined("ARRAYY")|| !btKeywords.isDefined("ARRAYZ")) { throw(AipsError("FITSIDItoMS: Illegal ANTENNA file: no antenna positions")); } arrayXYZ(0)=getKeywords().asdouble("ARRAYX"); arrayXYZ(1)=getKeywords().asdouble("ARRAYY"); arrayXYZ(2)=getKeywords().asdouble("ARRAYZ"); *itsLog << LogIO::NORMAL << "number of antennas = "<antenna()); ScalarColumn name(anTab,"ANNAME"); ArrayColumn antXYZ(anTab,"STABXYZ"); ArrayColumn dantXYZ(anTab,"DERXYZ"); // following is for space-born telescope //ScalarColumn orbp(anTab,"ORBPARM"); ScalarColumn anNo(anTab,"NOSTA"); ScalarColumn mntid(anTab,"MNTSTA"); ArrayColumn offset(anTab,"STAXOF"); ScalarColumn diam; if(anTab.tableDesc().isColumn("DIAMETER")){ diam.attach(anTab,"DIAMETER"); // this column is optional } else{ if (arrnam=="ATCA") diameter=22.; if (arrnam=="SMA") diameter=6.; *itsLog << LogIO::WARN << "ARRAY_GEOMETRY input table does not contain dish DIAMETER column.\n Will assume default diameter for TELESCOPE " << arrnam << " which is " << diameter <<" m." << LogIO::POST; } // All "VLBI" (==arrayXYZ<1000) requires y-axis reflection: // (ATCA looks like "VLBI" in UVFITS, but is already correct) //Bool doVLBIRefl= ((array_p!="ATCA") && allLE(abs(arrayXYZ),1000.0)); // continue definition of antenna number to antenna id mapping for (Int inRow=0; inRow antenna ID " << antIdFromNo[ii] << endl; } } // add antenna info to table (TT) ant.setPositionRef(MPosition::ITRF); // take into account that the ANTENNA table may already contain rows Int newRows = antIdFromNo.size() - ms_p.antenna().nrow(); ms_p.antenna().addRow(newRows); Int row=0; for (Int i=0; i tempf=offset(i); Vector tempd(3); for (Int j=0; j<3; j++) tempd[j]=tempf[j]; ant.offset().put(row,tempd); ant.type().put(row,"GROUND-BASED"); // Do UVFITS-dependent position corrections: // ArrayColumn antXYZ(i) may need coord transform; do it in corXYZ: Vector corXYZ=antXYZ(i); /*** // If nec, rotate coordinates out of local VLA frame to ITRF if ( doVLARot ) corXYZ=product(posRot,corXYZ); // If nec, reflect y-coord to yield right-handed geocentric: if ( doVLBIRefl ) corXYZ(1)=-corXYZ(1); ***/ ant.position().put(row,arrayXYZ+corXYZ); } // store these items in non-standard keywords for now ant.name().rwKeywordSet().define("ARRAY_NAME",arrnam); ant.position().rwKeywordSet().define("ARRAY_POSITION",arrayXYZ); } void FITSIDItoMS1::fillFeedTable() { const Regex trailing(" *$"); // trailing blanks MSFeedColumns& msfc(msc_p->feed()); ConstFitsKeywordList& kwl = kwlist(); const FitsKeyword* fkw; String kwname; kwl.first(); Int nIF = 1; Int nPcal = 0; while ((fkw = kwl.next())){ kwname = fkw->name(); if (kwname == "NO_STKD") { //noSTKD = fkw->asInt(); //cout << kwname << "=" << noSTKD << endl; } if (kwname == "STK_1") { //firstSTK = fkw->asInt(); //cout << kwname << "=" << firstSTK << endl; } if (kwname == "NO_BAND") { nIF = fkw->asInt(); //cout << kwname << "=" << nIF << endl; } if (kwname == "NOPCAL") { nPcal = fkw->asInt(); //cout << kwname << "=" << nPcal << endl; } } //access fitsidi AN table Table anTab = oldfullTable(""); ScalarColumn time(anTab, "TIME"); ScalarColumn timeint; ScalarColumn timeintd; try{ timeint.attach(anTab, "TIME_INTERVAL"); } catch(std::exception&){ timeintd.attach(anTab, "TIME_INTERVAL"); *itsLog << LogIO::NORMAL << "Note: this ANTENNA table uses double precision for TIME_INTERVAL. Convention is single." << LogIO::POST; } ScalarColumn name(anTab, "ANNAME"); ScalarColumn anNo(anTab, "ANTENNA_NO"); ScalarColumn array(anTab, "ARRAY"); ScalarColumn fqid(anTab, "FREQID"); ScalarColumn digLev(anTab, "NO_LEVELS"); ScalarColumn poltya(anTab, "POLTYA"); ScalarColumn poltyb(anTab, "POLTYB"); // if the values for all bands are the same, POLAA, POLAB, POLCALA and POLCALB can be scalar Bool POLAisScalar = False; ArrayColumn polaa; ArrayColumn polab; ScalarColumn polaaS; ScalarColumn polabS; try{ polaa.attach(anTab, "POLAA"); polab.attach(anTab, "POLAB"); } catch(std::exception& x){ polaaS.attach(anTab, "POLAA"); polabS.attach(anTab, "POLAB"); POLAisScalar = True; *itsLog << LogIO::NORMAL << "Treating POLAA and POLAB columns in input ANTENNA table as scalar," << endl << " i.e. using same value for all bands." << LogIO::POST; } ArrayColumn polcala; ArrayColumn polcalb; if(nPcal > 0){ if(anTab.tableDesc().isColumn("POLCALA") && anTab.tableDesc().isColumn("POLCALB")){ polcala.attach(anTab, "POLCALA"); polcalb.attach(anTab, "POLCALB"); } else{ *itsLog << LogIO::WARN << "POLCALA and/or POLCALB column is missing in ANTENNA table." << LogIO::POST; } } // ArrayColumn beamfwhm(anTab, "BEAMFWHM"); // this column is optional and there is presently // no place for this information in the MS Matrix polResponse(2,2); polResponse=0.; polResponse(0,0)=polResponse(1,1)=1.; Matrix offset(2,2); offset=0.; Int nAnt = anTab.nrow(); //cout <<"nAnt="< antenna ID " << antIdFromNo[ii] << endl; } } // record number of digitizer levels for each antenna for (Int inRow=0; inRowspectralWindow()); MSDataDescColumns& msDD(msc_p->dataDescription()); MSPolarizationColumns& msPol(msc_p->polarization()); ConstFitsKeywordList& kwl = kwlist(); const FitsKeyword* kw; String kwname; Int nCorr = 1; Int nIF_p = 0; Int nChan = 0; Double zeroRefFreq = 0.0; Double refChan = 0.0; kwl.first(); while ((kw = kwl.next())) { kwname = kw->name(); if (kwname == "NO_STKD") { nCorr = kw->asInt(); } if (kwname == "NO_BAND") { nIF_p = kw->asInt(); //cout << "nIF_p=" << nIF_p << endl; } if (kwname == "NO_CHAN") { nChan = kw->asInt(); //cout << "nChan=" << nChan << endl; } if (kwname == "REF_FREQ") { zeroRefFreq = kw->asDouble(); //cout << "zeroRefFreq=" << zeroRefFreq << endl; } if (kwname == "REF_PIXL") { refChan = kw->asDouble(); //cout << "refChan=" << refChan << endl; } } // Int iFreq = getIndex(coordType_p, "FREQ"); // Int nChan = nPixel_p(iFreq); // Int nCorr = nPixel_p(getIndex(coordType_p,"STOKES")); // fill out the polarization info (only single entry allowed in fits input) corrType_p = 1; corrProduct_p=0; ms_p.polarization().addRow(); msPol.numCorr().put(0,nCorr); msPol.corrType().put(0,corrType_p); msPol.corrProduct().put(0,corrProduct_p); msPol.flagRow().put(0,False); //access fitsidi FQ table //Table fqTab=bt.fullTable("",Table::Scratch); Table fqTab=oldfullTable(""); Int nRow=fqTab.nrow(); ScalarColumn colFqid(fqTab,"FREQID"); Matrix ifFreq(nIF_p,nRow); Matrix chWidth(nIF_p,nRow); Matrix totalBandwidth(nIF_p,nRow); Matrix sideband(nIF_p,nRow); //cout << "nIF_p=" << nIF_p << endl; //cout << "nRow=" << nRow << endl; Int nSpW = nIF_p * nRow; effChBw.resize(nSpW); nFreqid_p = nRow; // The type of the column changes according to the number of entries if (nIF_p==1) { ScalarColumn colIFFreq(fqTab, "BANDFREQ"); ScalarColumn colChWidth(fqTab, "CH_WIDTH"); ScalarColumn colTotalBW(fqTab,"TOTAL_BANDWIDTH"); ScalarColumn colSideBand(fqTab, "SIDEBAND"); for (Int i=0; i colIFFreq(fqTab, "BANDFREQ"); ArrayColumn colChWidth(fqTab, "CH_WIDTH"); ArrayColumn colTotalBW(fqTab, "TOTAL_BANDWIDTH"); ArrayColumn colSideBand(fqTab, "SIDEBAND"); colIFFreq.getColumn(ifFreq); colChWidth.getColumn(chWidth); colTotalBW.getColumn(totalBandwidth); colSideBand.getColumn(sideband); } for (Int spw=0; spw0) { ifc=spw%nIF_p; freqGroup = spw/nIF_p; } Int fqRow=spw/max(1,nIF_p); //if (fqRow != colFrqSel(fqRow)-1) if (fqRow != colFqid(fqRow)-1) *itsLog << LogIO::WARN << "Trouble interpreting FQ table, ids may be wrong" << LogIO::POST; msSpW.name().put(spw,"none"); msSpW.ifConvChain().put(spw,ifc); msSpW.numChan().put(spw,nChan); //Double refChan = refPix_p(iFreq); //Double refFreq=refVal_p(iFreq)+ifFreq(ifc,fqRow); Double refFreq=zeroRefFreq+ifFreq(ifc,fqRow); Double chanBandwidth=chWidth(ifc,fqRow); Vector chanFreq(nChan),resolution(nChan); for (Int i=0; i < nChan; i++) { chanFreq(i)= refFreq + (i+1-refChan) * chanBandwidth; } effChBw(spw)=abs(chanBandwidth); resolution=abs(chanBandwidth); msSpW.chanFreq().put(spw,chanFreq); msSpW.chanWidth().put(spw,resolution); msSpW.effectiveBW().put(spw,resolution); msSpW.refFrequency().put(spw,refFreq); msSpW.resolution().put(spw,resolution); msSpW.totalBandwidth().put(spw,totalBandwidth(ifc,fqRow)); msSpW.netSideband().put(spw,sideband(ifc, fqRow)); msSpW.freqGroup().put(spw,freqGroup); msSpW.freqGroupName().put(spw,"none"); msSpW.flagRow().put(spw,False); // set the reference frames for frequency freqsys_p = MFrequency::TOPO; msSpW.measFreqRef().put(spw,freqsys_p); } } // method to fill Field Table void FITSIDItoMS1::fillFieldTable() { *itsLog << LogOrigin("FitsIDItoMS()", "fillFieldTable"); MSFieldColumns& msField(msc_p->field()); //Table suTab=bt.fullTable("",Table::Scratch); Table suTab=oldfullTable(""); //access the columns in source FITS-IDI subtable ScalarColumn id; if(suTab.tableDesc().isColumn("SOURCE_ID")){ id.attach(suTab, "SOURCE_ID"); } else if(suTab.tableDesc().isColumn("ID_NO.")){ *itsLog << LogIO::NORMAL << "No SOURCE_ID column in input SOURCE table. Using deprecated ID_NO column." << LogIO::POST; id.attach(suTab, "ID_NO."); } else{ throw(AipsError("No SOURCE_ID column in input SOURCE table.")); } ScalarColumn name(suTab,"SOURCE"); ScalarColumn qual(suTab,"QUAL"); ScalarColumn code(suTab,"CALCODE"); ScalarColumn fqid(suTab,"FREQID"); // if the values are the same for all bands, the flux, alpha, freqoff, sysvel, and restfreq columns can be scalar ArrayColumn iflux; ArrayColumn qflux; ArrayColumn uflux; ArrayColumn vflux; ArrayColumn alpha; ArrayColumn foffset; ArrayColumn foffsetD; ArrayColumn sysvel; ArrayColumn restfreq; ScalarColumn ifluxS; ScalarColumn qfluxS; ScalarColumn ufluxS; ScalarColumn vfluxS; ScalarColumn alphaS; ScalarColumn foffsetS; ScalarColumn foffsetSD; ScalarColumn sysvelS; ScalarColumn restfreqS; try{ // try array column first iflux.attach(suTab,"IFLUX"); // I (Jy) qflux.attach(suTab,"QFLUX"); // Q uflux.attach(suTab,"UFLUX"); // U vflux.attach(suTab,"VFLUX"); // V alpha.attach(suTab,"ALPHA"); // sp. index try{ foffset.attach(suTab,"FREQOFF"); // fq. offset } catch(std::exception& x){ foffsetD.attach(suTab,"FREQOFF"); // fq. offset *itsLog << LogIO::NORMAL << "Column FREQOFF is Double but should be Float." << LogIO::POST; } sysvel.attach(suTab,"SYSVEL"); // sys vel. (m/s) restfreq.attach(suTab,"RESTFREQ"); // rest freq. (hz) } catch(std::exception& x){ ifluxS.attach(suTab,"IFLUX"); // I (Jy) qfluxS.attach(suTab,"QFLUX"); // Q ufluxS.attach(suTab,"UFLUX"); // U vfluxS.attach(suTab,"VFLUX"); // V alphaS.attach(suTab,"ALPHA"); // sp. index try{ foffsetS.attach(suTab,"FREQOFF"); // fq. offset } catch(std::exception& x){ foffsetSD.attach(suTab,"FREQOFF"); // fq. offset *itsLog << LogIO::NORMAL << "Column FREQOFF is Double but should be Float." << LogIO::POST; } sysvelS.attach(suTab,"SYSVEL"); // sys vel. (m/s) restfreqS.attach(suTab,"RESTFREQ"); // rest freq. (hz) *itsLog << LogIO::NORMAL << "Treating ?FLUX, ALPHA, FREQOFF, SYSVEL, and RESTFREQ columns in input SOURCE table as scalar," << endl << " i.e. using same value for all bands." << LogIO::POST; } ScalarColumn ra(suTab,"RAEPO"); //degrees ScalarColumn dec(suTab,"DECEPO"); //degrees ScalarColumn equinox; ScalarColumn epoch; //years, alternative for equinox if(suTab.tableDesc().isColumn("EQUINOX")){ equinox.attach(suTab,"EQUINOX"); // string } else if(suTab.tableDesc().isColumn("EPOCH")){ epoch.attach(suTab,"EPOCH"); } ScalarColumn raapp(suTab,"RAAPP"); //degrees ScalarColumn decapp(suTab,"DECAPP"); //degrees ScalarColumn veltype(suTab,"VELTYP"); // ScalarColumn veldef(suTab,"VELDEF"); // ScalarColumn pmra(suTab,"PMRA"); //deg/day ScalarColumn pmdec(suTab,"PMDEC"); //deg/day ScalarColumn pllx(suTab,"PARALLAX"); //arcsec //if (Int(suTab.nrow())setDirectionRef(epochRefZero); for (Int inRow=0; inRow<(Int)suTab.nrow(); inRow++) { if (id(inRow) < 1) { *itsLog << LogIO::WARN << "Input source id < 1, invalid source id!" << LogIO::POST; } Int fld = id(inRow)-1; // temp. fix for wrong source id in sma data if (fld == -2) fld = fld + 2 ; // add empty rows until the row number in the output matches the source id while (fld > outRow) { // Append a flagged, empty row to the FIELD table ms_p.field().addRow(); outRow++; Vector nullDir(1); nullDir(0).set(MVDirection(0.0,0.0), MDirection::Ref(epochRefZero)); msField.phaseDirMeasCol().put(outRow,nullDir); msField.delayDirMeasCol().put(outRow,nullDir); msField.referenceDirMeasCol().put(outRow,nullDir); msField.flagRow().put(outRow,True); } msField.sourceId().put(fld,-1); // source table not yet filled in msField.code().put(fld,code(inRow)); msField.name().put(fld,name(inRow)); Int numPoly = 0; //cout << "pmra = "<< pmra(inRow)<< endl; //cout << "pmdec = "<< pmdec(inRow) << endl; //cout << "abs(pmra) = "<< abs(pmra(inRow)) << endl; //cout << "abs(pmdec) = "<< abs(pmdec(inRow)) < 1000.0 || abs(pmdec(inRow)) > 1000.0) { *itsLog << LogIO::WARN << " unreasonably large proper motion parameter(s)" << " (> 1000 deg/day)! Check input data." << LogIO::POST; } else { numPoly = 1; } } // The code below will write the direction in B1950 or J2000 coordinates if // the direction is constant. However it will use apparent Coordinates (I // am not sure if this means APP, JTRUE, BTRUE or what), if the proper // motion is non-zero. In all cases the time will be the date of the start // of the observation. MDirection::Types epochRef=MDirection::APP; MVDirection refDir; if (numPoly == 0) { if(equinox.isNull()){ if (near(epoch(inRow),2000.0,0.01)) { epochRef = MDirection::J2000; } else if (numPoly == 0 && nearAbs(epoch(inRow),1950.0,0.01)) { epochRef = MDirection::B1950; } else { *itsLog << " Cannot handle epoch in SU table: " << epoch(inRow) << LogIO::EXCEPTION; } } else{ // have equinox if (equinox(inRow).contains("J2000")) { epochRef = MDirection::J2000; } else if (numPoly == 0 && equinox(inRow).contains("1950.0B")) { epochRef = MDirection::B1950; } else { *itsLog << " Cannot handle equinox in SU table: " << equinox(inRow) << LogIO::EXCEPTION; } } refDir = MVDirection(ra(inRow)*C::degree,dec(inRow)*C::degree); } else { // for numPoly!=0, use apparent direction. // Need to define 'frame' to use APP. frame needs the observatory // positions and time of observation (can be obtained from RDATE in // Fits key). At this momonet, use J2000. epochRef = MDirection::J2000; refDir = MVDirection(raapp(inRow)*C::degree,decapp(inRow)*C::degree); } Vector radecMeas(numPoly+1); radecMeas(0).set(refDir, MDirection::Ref(epochRef)); if (numPoly==1) { radecMeas(1).set(MVDirection(pmra(inRow)*C::degree/C::day, pmdec(inRow)*C::degree/C::day), MDirection::Ref(epochRef)); } // time will be filled later (once Observation Table is filled) // at this moment just put 0.0. msField.time().put(fld, 0.0); msField.numPoly().put(fld,numPoly); msField.delayDirMeasCol().put(fld,radecMeas); msField.phaseDirMeasCol().put(fld,radecMeas); msField.referenceDirMeasCol().put(fld,radecMeas); msField.flagRow().put(fld,False); } } Bool FITSIDItoMS1::fillCorrelatorModelTable() { *itsLog << LogOrigin("FitsIDItoMS()", "fillCorrelatorModelTable"); // MSCorrelatorModelColumns& msCorrMod(msc_p->correlatorModel()); *itsLog << LogIO::WARN << "not yet implemented" << LogIO::POST; Table imTab = oldfullTable(""); return True; } Bool FITSIDItoMS1::fillSysCalTable() { *itsLog << LogOrigin("FitsIDItoMS()", "fillSysCalTable"); MSSysCalColumns& msSysCal(msc_p->sysCal()); ConstFitsKeywordList& kwl = kwlist(); const FitsKeyword* fkw; String kwname; kwl.first(); Int nIF = 1; while ((fkw = kwl.next())){ kwname = fkw->name(); if (kwname == "NO_BAND") { nIF = fkw->asInt(); } } Int nVal=nrows(); Bool dualPol=False; Bool TSYSisScalar=False; Table tyTab = oldfullTable(""); ScalarColumn time(tyTab, "TIME"); ScalarColumn timeint(tyTab, "TIME_INTERVAL"); ScalarColumn anNo(tyTab, "ANTENNA_NO"); ArrayColumn tsys_1; ArrayColumn tsys_2; ScalarColumn tsys_1S; ScalarColumn tsys_2S; try{ tsys_1.attach(tyTab, "TSYS_1"); if(tyTab.tableDesc().isColumn("TSYS_2")) { tsys_2.attach(tyTab, "TSYS_2"); // this column is optional dualPol=True; } } catch(std::exception&){ tsys_1S.attach(tyTab, "TSYS_1"); if(tyTab.tableDesc().isColumn("TSYS_2")) { tsys_2S.attach(tyTab, "TSYS_2"); // this column is optional dualPol=True; } TSYSisScalar=True; if (nIF > 1) { *itsLog << LogIO::NORMAL << "Treating TSYS_1 and TSYS_2 columns in input SYSTEM_TEMPERATURE table as scalar," << endl << " i.e. using same value for all bands." << LogIO::POST; } } Vector tsys(dualPol ? 2 : 1); Int outRow=-1; for (Int inRow=0; inRowflagCmd()); ConstFitsKeywordList& kwl = kwlist(); const FitsKeyword* fkw; String kwname; kwl.first(); Int noSTKD = 0; Int firstSTK = -1; Int nIF = 1; while ((fkw = kwl.next())){ kwname = fkw->name(); if (kwname == "NO_STKD") { noSTKD = fkw->asInt(); //cout << kwname << "=" << noSTKD << endl; } if (kwname == "STK_1") { firstSTK = fkw->asInt(); //cout << kwname << "=" << firstSTK << endl; } if (kwname == "NO_BAND") { nIF = fkw->asInt(); //cout << kwname << "=" << nIF << endl; } } const char *stokes[] = { "RR", "LL", "RL", "LR", "XX", "YY", "XY", "YX" }; if (firstSTK >= 0 || noSTKD < 1 || noSTKD > 4 || firstSTK - noSTKD < -9) { *itsLog << LogIO::SEVERE << "Unsupported stokes STK_1 " << firstSTK << "NO_STKD" << noSTKD << LogIO::EXCEPTION; } Int nVal=nrows(); Table flagTab = oldfullTable(""); ScalarColumn srcid(flagTab, "SOURCE_ID"); ScalarColumn array(flagTab, "ARRAY"); ArrayColumn ants(flagTab, "ANTS"); ScalarColumn fqid(flagTab, "FREQID"); ArrayColumn timerang(flagTab, "TIMERANG"); Bool BANDSisScalar = False; ArrayColumn bands; ScalarColumn bandsS; ArrayColumn chans(flagTab, "CHANS"); ArrayColumn pflags(flagTab, "PFLAGS"); ScalarColumn reason(flagTab, "REASON"); ScalarColumn severity(flagTab, "SEVERITY"); try { bands.attach(flagTab, "BANDS"); } catch(std::exception& x){ bandsS.attach(flagTab, "BANDS"); BANDSisScalar = True; } Int outRow=-1; for (Int inRow=0; inRow chan2 || chan1 < -1 || chan2 < -1) { *itsLog << LogIO::SEVERE << "incorrect channel range " << chan1 << "-" << chan2 << LogIO::EXCEPTION; } ms_p.flagCmd().addRow(); outRow++; Double time = (timerang(inRow)(IPosition(1, 0)) + timerang(inRow)(IPosition(1, 1))) / 2; Double interval = timerang(inRow)(IPosition(1, 1)) - timerang(inRow)(IPosition(1, 0)); msFlagCmd.time().put(outRow, time * C::day + rdate); msFlagCmd.interval().put(outRow, interval * C::day); msFlagCmd.type().put(outRow, "FLAG"); msFlagCmd.reason().put(outRow, reason(inRow)); msFlagCmd.level().put(outRow, 0); msFlagCmd.severity().put(outRow, severity(inRow)); msFlagCmd.applied().put(outRow, 0); // Build command string ostringstream cmd; // antenna selection ant1 = (ant1 == 0) ? -1 : antIdFromNo[ant1]; ant2 = (ant2 == 0) ? -1 : antIdFromNo[ant2]; if (ant1 != -1) { cmd << "antenna='" << ant1; if (ant2 != -1) cmd << "&" << ant2; cmd << "'"; } // field selection if (srcid(inRow) != 0) { if (cmd.str().size() > 0) cmd << " "; cmd << "field='" << srcid(inRow) - 1 << "'"; } // spw selection ostringstream spw; Vector bandsV; Bool needSpw = False; Int startFreqid = 0; Int endFreqid = 0; if (fqid(inRow) > 0) startFreqid = endFreqid = fqid(inRow) - 1; else if (nFreqid_p > 0) endFreqid = nFreqid_p - 1; if (BANDSisScalar) bandsV = Vector(1, bandsS(inRow)); else bandsV = bands(inRow); for (int freqid = startFreqid; freqid <= endFreqid; freqid++) { for (int band = 0; band < bandsV.shape()(0); band++) { if (bandsV[band]) { if (spw.str().size() > 0) spw << ","; spw << (freqid * nIF) + band; } else { needSpw = True; } } } if (!needSpw) spw.str("*"); if (needSpw || chan1 > 0) { if (cmd.str().size() > 0) cmd << " "; cmd << "spw='" << spw.str(); if (chan1 > 0) { cmd << ":"<< chan1 - 1 << "~" << chan2 - 1 << "'"; } cmd << "'"; } // correlation selection ostringstream corr; Vector pflagsV = pflags(inRow); Bool needCorr = False; for (int stk = 0; stk < noSTKD; stk++) { if (pflagsV[stk]) { if (corr.str().size()) corr << ","; corr << stokes[stk - firstSTK - 1]; } else { needCorr = True; } } if (needCorr) { if (cmd.str().size() > 0) cmd << " "; cmd << "correlation='" << corr.str() << "'"; } msFlagCmd.command().put(outRow, cmd.str()); } return True; } Bool FITSIDItoMS1::fillWeatherTable() { *itsLog << LogOrigin("FitsIDItoMS()", "fillWeatherTable"); MSWeatherColumns& msWeather(msc_p->weather()); Int nVal=nrows(); Table wxTab = oldfullTable(""); ScalarColumn time(wxTab, "TIME"); ScalarColumn timeint(wxTab, "TIME_INTERVAL"); ScalarColumn anNo(wxTab, "ANTENNA_NO"); ScalarColumn temperature(wxTab, "TEMPERATURE"); ScalarColumn pressure(wxTab, "PRESSURE"); ScalarColumn dewpoint(wxTab, "DEWPOINT"); ScalarColumn wind_velocity(wxTab, "WIND_VELOCITY"); ScalarColumn wind_direction(wxTab, "WIND_DIRECTION"); ScalarColumn wvr_h2o; if(weather_hasWater_p) wvr_h2o.attach(wxTab, "WVR_H2O"); ScalarColumn ionos_electron; if(weather_hasElectron_p) wvr_h2o.attach(wxTab, "IONOS_ELECTRON"); Int outRow=-1; for (Int inRow=0; inRowname(); if (kwname == "NO_BAND") { nIF = fkw->asInt(); } if (kwname == "NO_TABS") { nTabs = fkw->asInt(); } } TableDesc td; String name = "GAIN_CURVE"; td.comment() = "Gain curve table"; td.addColumn(ScalarColumnDesc("ANTENNA_ID", "Antenna identifier")); td.addColumn(ScalarColumnDesc("FEED_ID", "Feed identifier")); td.addColumn(ScalarColumnDesc("SPECTRAL_WINDOW_ID", "Spectral window identifier")); td.addColumn(ScalarColumnDesc("TIME", "Midpoint of time for which this set of parameters is accurate")); td.addColumn(ScalarColumnDesc("INTERVAL", "Interval for which this set of parameters is accurate")); td.addColumn(ScalarColumnDesc("TYPE", "Gain curve type")); td.addColumn(ScalarColumnDesc("NUM_POLY", "Number of terms in polynomial")); td.addColumn(ArrayColumnDesc("GAIN", "Gain polynomial")); td.addColumn(ArrayColumnDesc("SENSITIVITY", "Antenna sensitivity")); TableMeasValueDesc measVal(td, "TIME"); TableMeasDesc measCol(measVal); measCol.write(td); TableQuantumDesc timeTqd(td, "TIME", Unit("s")); timeTqd.write(td); TableQuantumDesc intervalTqd(td, "INTERVAL", Unit("s")); intervalTqd.write(td); TableQuantumDesc sensTqd(td, "SENSITIVITY", Unit("K/Jy")); sensTqd.write(td); SetupNewTable tableSetup(ms_p.tableName() + "/" + name, td, Table::New); ms_p.rwKeywordSet().defineTable("GAIN_CURVE", Table(tableSetup)); Int nVal=nrows(); Bool dualPol=False; Bool GCisScalar=False; Table gcTab = oldfullTable(""); ScalarColumn anNo(gcTab, "ANTENNA_NO"); ScalarColumn array(gcTab, "ARRAY"); ScalarColumn fqid(gcTab, "FREQID"); ArrayColumn type_1; ArrayColumn nterm_1; ArrayColumn xtyp_1; ArrayColumn ytyp_1; ArrayColumn gain_1; ArrayColumn sens_1; ArrayColumn type_2; ArrayColumn nterm_2; ArrayColumn xtyp_2; ArrayColumn ytyp_2; ArrayColumn gain_2; ArrayColumn sens_2; ScalarColumn type_1S; ScalarColumn nterm_1S; ScalarColumn xtyp_1S; ScalarColumn ytyp_1S; ScalarColumn gain_1S; ScalarColumn sens_1S; ScalarColumn type_2S; ScalarColumn nterm_2S; ScalarColumn xtyp_2S; ScalarColumn ytyp_2S; ScalarColumn gain_2S; ScalarColumn sens_2S; Int ytyp = 0, nterm = 0; try { type_1.attach(gcTab, "TYPE_1"); nterm_1.attach(gcTab, "NTERM_1"); xtyp_1.attach(gcTab, "X_TYP_1"); ytyp_1.attach(gcTab, "Y_TYP_1"); sens_1.attach(gcTab, "SENS_1"); if (gcTab.tableDesc().isColumn("TYPE_2")) { type_2.attach(gcTab, "TYPE_2"); nterm_2.attach(gcTab, "NTERM_2"); xtyp_2.attach(gcTab, "X_TYP_2"); ytyp_2.attach(gcTab, "Y_TYP_2"); sens_2.attach(gcTab, "SENS_2"); dualPol=True; } } catch (std::exception&) { type_1S.attach(gcTab, "TYPE_1"); nterm_1S.attach(gcTab, "NTERM_1"); xtyp_1S.attach(gcTab, "X_TYP_1"); ytyp_1S.attach(gcTab, "Y_TYP_1"); sens_1S.attach(gcTab, "SENS_1"); if (gcTab.tableDesc().isColumn("TYPE_2")) { type_2S.attach(gcTab, "TYPE_2"); nterm_2S.attach(gcTab, "NTERM_2"); xtyp_2S.attach(gcTab, "X_TYP_2"); ytyp_2S.attach(gcTab, "Y_TYP_2"); sens_2S.attach(gcTab, "SENS_2"); dualPol=True; } GCisScalar=True; if (nIF > 1) { *itsLog << LogIO::NORMAL << "Treating columns in input GAIN_CURVE table as scalar," << endl << " i.e. using same value for all bands." << LogIO::POST; } } if (GCisScalar && nTabs == 1) { gain_1S.attach(gcTab, "GAIN_1"); if (gcTab.tableDesc().isColumn("GAIN_2")) gain_2S.attach(gcTab, "GAIN_2"); } else { gain_1.attach(gcTab, "GAIN_1"); if (gcTab.tableDesc().isColumn("GAIN_2")) gain_2.attach(gcTab, "GAIN_2"); } Table msgc = ms_p.rwKeywordSet().asTable("GAIN_CURVE"); Vector sens(dualPol ? 2 : 1); Int outRow=-1; for (Int inRow=0; inRow antennaIdCol(msgc, "ANTENNA_ID"); ScalarColumn feedIdCol(msgc, "FEED_ID"); ScalarColumn spwIdCol(msgc, "SPECTRAL_WINDOW_ID"); ScalarColumn timeCol(msgc, "TIME"); ScalarColumn intervalCol(msgc, "INTERVAL"); ScalarColumn typeCol(msgc, "TYPE"); ScalarColumn npolyCol(msgc, "NUM_POLY"); ArrayColumn gainCol(msgc,"GAIN"); ArrayColumn sensCol(msgc, "SENSITIVITY"); IPosition thisIF = IPosition(1,inIF); if (GCisScalar && inIF == 0) { if (dualPol && (type_1S(inRow) != type_2S(inRow) || xtyp_1S(inRow) != xtyp_2S(inRow) || ytyp_1S(inRow) != ytyp_2S(inRow) || nterm_1S(inRow) != nterm_2S(inRow))) { *itsLog << "Distinct gain curve types per polarisation" << LogIO::POST; continue; } if (type_1S(inRow) != 2) { *itsLog << "Unsupported gain curve type " << type_1S(inRow) << LogIO::POST; continue; } ytyp = ytyp_1S(inRow); nterm = nterm_1S(inRow); } else if (!GCisScalar) { if (dualPol && (type_1(inRow)(thisIF) != type_2(inRow)(thisIF) || xtyp_1(inRow)(thisIF) != xtyp_2(inRow)(thisIF) || ytyp_1(inRow)(thisIF) != ytyp_2(inRow)(thisIF) || nterm_1(inRow)(thisIF) != nterm_2(inRow)(thisIF))) { *itsLog << "Distinct gain curve types per polarisation" << LogIO::POST; continue; } if (type_1(inRow)(thisIF) != 2) { *itsLog << "Unsupported gain curve type " << type_1(inRow)(thisIF) << LogIO::POST; continue; } ytyp = ytyp_1(inRow)(thisIF); nterm = nterm_1(inRow)(thisIF); } switch (ytyp) { case 1: case 2: break; default: *itsLog << "Unsupported gain curve coordinate type " << ytyp << LogIO::POST; continue; } if (antIdFromNo.find(anNo(inRow)) != antIdFromNo.end()) { antennaIdCol.put(outRow, antIdFromNo[anNo(inRow)]); } else { *itsLog << LogIO::SEVERE << "Internal error: no mapping for ANTENNA_NO " << anNo(inRow) << LogIO::EXCEPTION; } feedIdCol.put(outRow,-1); spwIdCol.put(outRow,inIF); switch (ytyp) { case 1: typeCol.put(outRow,"POWER(EL)"); break; case 2: typeCol.put(outRow,"POWER(ZA)"); break; } timeCol.put(outRow, (startTime_p + lastTime_p) / 2); intervalCol.put(outRow, lastTime_p - startTime_p); npolyCol.put(outRow,nterm); Matrix gain(dualPol ? 2 : 1, nterm); if (GCisScalar && nTabs == 1) { gain.row(0)=gain_1S(inRow); if (dualPol) gain.row(1)=gain_2S(inRow); } else { gain.row(0)=gain_1(inRow)(Slice(inIF * nTabs, nterm)); if (dualPol) gain.row(1)=gain_2(inRow)(Slice(inIF * nTabs, nterm)); } gainCol.put(outRow,gain); if (GCisScalar) { sens(0)=sens_1S(inRow); if (dualPol) sens(1)=sens_2S(inRow); } else { sens(0)=sens_1(inRow)(thisIF); if (dualPol) sens(1)=sens_2(inRow)(thisIF); } sensCol.put(outRow,sens); } } ms_p.rwKeywordSet().asTable("GAIN_CURVE").flush(); return True; } Bool FITSIDItoMS1::handlePhaseCal() { *itsLog << LogOrigin("FitsIDItoMS()", "handlePhaseCal"); ConstFitsKeywordList& kwl = kwlist(); const FitsKeyword* fkw; String kwname; kwl.first(); Int nIF = 1; Int nTones = 0; while ((fkw = kwl.next())){ kwname = fkw->name(); if (kwname == "NO_BAND") { nIF = fkw->asInt(); } if (kwname == "NO_TONES") { nTones = fkw->asInt(); } } TableDesc td; String name = "PHASE_CAL"; td.comment() = "Phase calibration table"; td.addColumn(ScalarColumnDesc("ANTENNA_ID", "Antenna identifier")); td.addColumn(ScalarColumnDesc("FEED_ID", "Feed identifier")); td.addColumn(ScalarColumnDesc("SPECTRAL_WINDOW_ID", "Spectral window identifier")); td.addColumn(ScalarColumnDesc("TIME", "Midpoint of time for which this set of parameters is accurate")); td.addColumn(ScalarColumnDesc("INTERVAL", "Interval for which this set of parameters is accurate")); td.addColumn(ScalarColumnDesc("NUM_TONES", "Number of phase-cal tones")); td.addColumn(ArrayColumnDesc("TONE_FREQUENCY", "Phase-cal tone frequency")); td.addColumn(ArrayColumnDesc("PHASE_CAL", "Phase-cal measurement")); td.addColumn(ScalarColumnDesc("CABLE_CAL", "Cable calibration measurement")); TableMeasValueDesc measVal(td, "TIME"); TableMeasDesc measCol(measVal); measCol.write(td); TableQuantumDesc timeTqd(td, "TIME", Unit("s")); timeTqd.write(td); TableQuantumDesc intervalTqd(td, "INTERVAL", Unit("s")); intervalTqd.write(td); TableQuantumDesc toneFreqTqd(td, "TONE_FREQUENCY", Unit("Hz")); toneFreqTqd.write(td); TableQuantumDesc cableCalTqd(td, "CABLE_CAL", Unit("s")); cableCalTqd.write(td); SetupNewTable tableSetup(ms_p.tableName() + "/" + name, td, Table::New); ms_p.rwKeywordSet().defineTable("PHASE_CAL", Table(tableSetup)); Int nVal=nrows(); Bool dualPol=False; Bool PCisScalar=False; Table pcTab = oldfullTable(""); ScalarColumn time(pcTab, "TIME"); ScalarColumn timeint(pcTab, "TIME_INTERVAL"); ScalarColumn anNo(pcTab, "ANTENNA_NO"); ScalarColumn array(pcTab, "ARRAY"); ScalarColumn fqid(pcTab, "FREQID"); ScalarColumn cable_cal(pcTab, "CABLE_CAL"); ArrayColumn pc_freq_1; ArrayColumn pc_real_1; ArrayColumn pc_imag_1; ArrayColumn pc_freq_2; ArrayColumn pc_real_2; ArrayColumn pc_imag_2; ScalarColumn pc_freq_1S; ScalarColumn pc_real_1S; ScalarColumn pc_imag_1S; ScalarColumn pc_freq_2S; ScalarColumn pc_real_2S; ScalarColumn pc_imag_2S; try{ pc_freq_1.attach(pcTab, "PC_FREQ_1"); pc_real_1.attach(pcTab, "PC_REAL_1"); pc_imag_1.attach(pcTab, "PC_IMAG_1"); if(pcTab.tableDesc().isColumn("PC_FREQ_2")) { pc_freq_2.attach(pcTab, "PC_FREQ_2"); // this column is optional pc_real_2.attach(pcTab, "PC_REAL_2"); // this column is optional pc_imag_2.attach(pcTab, "PC_IMAG_2"); // this column is optional dualPol=True; } } catch(std::exception&){ pc_freq_1S.attach(pcTab, "PC_FREQ_1"); pc_real_1S.attach(pcTab, "PC_REAL_1"); pc_imag_1S.attach(pcTab, "PC_IMAG_1"); if(pcTab.tableDesc().isColumn("PC_FREQ_2")) { pc_freq_2S.attach(pcTab, "PC_FREQ_2"); // this column is optional pc_real_2S.attach(pcTab, "PC_REAL_2"); // this column is optional pc_imag_2S.attach(pcTab, "PC_IMAG_2"); // this column is optional dualPol=True; } PCisScalar=True; } Table mspc = ms_p.rwKeywordSet().asTable("PHASE_CAL"); Int outRow=-1; for (Int inRow=0; inRow antennaIdCol(mspc, "ANTENNA_ID"); ScalarColumn feedIdCol(mspc, "FEED_ID"); ScalarColumn spwIdCol(mspc, "SPECTRAL_WINDOW_ID"); ScalarColumn timeCol(mspc, "TIME"); ScalarColumn intervalCol(mspc, "INTERVAL"); ScalarColumn ntonesCol(mspc, "NUM_TONES"); ArrayColumn freqCol(mspc, "TONE_FREQUENCY"); ArrayColumn phaseCalCol(mspc, "PHASE_CAL"); ScalarColumn cableCalCol(mspc, "CABLE_CAL"); if (antIdFromNo.find(anNo(inRow)) != antIdFromNo.end()) { antennaIdCol.put(outRow, antIdFromNo[anNo(inRow)]); } else { *itsLog << LogIO::SEVERE << "Internal error: no mapping for ANTENNA_NO " << anNo(inRow) << LogIO::EXCEPTION; } feedIdCol.put(outRow,-1); spwIdCol.put(outRow,inIF); timeCol.put(outRow, time(inRow)*C::day + rdate); intervalCol.put(outRow, timeint(inRow)*C::day); ntonesCol.put(outRow, nTones); Matrix freq(dualPol ? 2 : 1, nTones); if (PCisScalar) { freq.row(0)=pc_freq_1S(inRow); if (dualPol) freq.row(1)=pc_freq_2S(inRow); } else { freq.row(0)=pc_freq_1(inRow)(Slice(inIF * nTones, nTones)); if (dualPol) freq.row(1)=pc_freq_2(inRow)(Slice(inIF * nTones, nTones)); } freqCol.put(outRow, freq); Matrix phase_cal(dualPol ? 2 : 1, nTones); if (PCisScalar) { phase_cal.row(0)=Complex(pc_real_1S(inRow), pc_imag_1S(inRow)); if (dualPol) phase_cal.row(1)=Complex(pc_real_2S(inRow), pc_imag_2S(inRow)); } else { for (Int i=0; i("TIME", "Time for which this set of parameters is accurate")); td.addColumn(ScalarColumnDesc("OBSERVATION_ID", "Observation identifier")); td.addColumn(ScalarColumnDesc("UT1_UTC", "UT1-UTC")); td.addColumn(ArrayColumnDesc("PM", "Position of celestial pole")); td.addColumn(ScalarColumnDesc("TYPE", "EOP type")); TableMeasValueDesc measVal(td, "TIME"); TableMeasDesc measCol(measVal); measCol.write(td); TableQuantumDesc timeTqd(td, "TIME", Unit("s")); timeTqd.write(td); TableQuantumDesc ut1utcTqd(td, "UT1_UTC", Unit("s")); ut1utcTqd.write(td); TableQuantumDesc pmTqd(td, "PM", Unit("rad")); pmTqd.write(td); SetupNewTable tableSetup(ms_p.tableName() + "/" + name, td, Table::New); ms_p.rwKeywordSet().defineTable("EARTH_ORIENTATION", Table(tableSetup)); Int nVal=nrows(); Table eopTab = oldfullTable(""); ScalarColumn time(eopTab, "TIME"); ScalarColumn ut1utc(eopTab, "UT1-UTC"); ArrayColumn wobxy(eopTab, "WOBXY"); ScalarColumn ut1type(eopTab, "UT1 TYPE"); ScalarColumn wobtype(eopTab, "WOB TYPE"); Table mseop = ms_p.rwKeywordSet().asTable("EARTH_ORIENTATION"); Int outRow=-1; for (Int inRow=0; inRow timeCol(mseop, "TIME"); ScalarColumn obsIdCol(mseop, "OBSERVATION_ID"); ScalarColumn ut1utcCol(mseop, "UT1_UTC"); ArrayColumn pmCol(mseop, "PM"); ScalarColumn typeCol(mseop, "TYPE"); timeCol.put(outRow, time(inRow)*C::day + rdate); obsIdCol.put(outRow, 0); ut1utcCol.put(outRow, ut1utc(inRow)); pmCol.put(outRow, wobxy(inRow)*C::arcsec); if (ut1type(inRow) == "E" || wobtype(inRow) == "E") typeCol.put(outRow, "PREDICTED"); else if (ut1type(inRow) == "P" || wobtype(inRow) == "P") typeCol.put(outRow, "PRELIMINARY"); else if (ut1type(inRow) == "F" || wobtype(inRow) == "F") typeCol.put(outRow, "FINAL"); } ms_p.rwKeywordSet().asTable("EARTH_ORIENTATION").flush(); return True; } Bool FITSIDItoMS1::handleModelComps() { *itsLog << LogOrigin("FitsIDItoMS()", "handleModelComps"); ConstFitsKeywordList& kwl = kwlist(); const FitsKeyword* fkw; String kwname; kwl.first(); Int fftTwid = 0; String taperFn; while ((fkw = kwl.next())){ kwname = fkw->name(); if (kwname == "FFT_TWID") { fftTwid = fkw->asInt(); } if (kwname == "TAPER_FN") { taperFn = fkw->asString(); taperFn.trim(); } } if (array_p == "VLBA" && fftTwid != 1) { *itsLog << LogIO::SEVERE << "Data needs FFT artifact corrections; this is not yet implemented." << LogIO::POST; } if (array_p == "VLBA" && taperFn != "UNIFORM") { *itsLog << LogIO::SEVERE << "Data was correlated with " << taperFn << " taper; support for this taper is not yet implemented." << LogIO::POST; } // make the content of the MODEL_COMPS table available in the MS (t.b.d.) Table mcTab = oldfullTable(""); return True; } void FITSIDItoMS1::fixEpochReferences() { *itsLog << LogOrigin("FitsIDItoMS()", "fixEpochReferences"); if (timsys_p=="IAT") timsys_p="TAI"; if (timsys_p=="UTC" || timsys_p=="TAI") { if (timsys_p=="UTC") msc_p->setEpochRef(MEpoch::UTC, False); if (timsys_p=="TAI") msc_p->setEpochRef(MEpoch::TAI, False); } else { if (timsys_p!="") *itsLog << LogIO::SEVERE << "Unhandled time reference frame: "< obsTime = msc_p->observation().timeRange()(0); //update polarization table //this should be a polarization table in _tmp directory //This is expected to call after getAxisInfo. String MSFileName; MSFileName = MStmpDir + "/FREQUENCY"; MeasurementSet mssub(MSFileName,Table::Update); ms_p = mssub; msc_p = new MSColumns(ms_p); MSPolarizationColumns& msPol(msc_p->polarization()); msPol.corrType().put(0,corrType_p); msPol.corrProduct().put(0,corrProduct_p); delete msc_p; //update time in the field table MSFileName = MStmpDir + "/SOURCE"; MeasurementSet mssub2(MSFileName,Table::Update); ms_p = mssub2; msc_p = new MSColumns(ms_p); Int nrow = ms_p.field().nrow(); MSFieldColumns& msFld(msc_p->field()); for (Int row = 0; row < nrow; row++) { msFld.time().put(row,obsTime(0)); // cout << "update: obsTime=" << obsTime(0) << endl; } delete msc_p; // update time in the feed table MSFileName = MStmpDir + "/ANTENNA"; MeasurementSet mssub3(MSFileName,Table::Update); ms_p = mssub3; msc_p = new MSColumns(ms_p); nrow = ms_p.feed().nrow(); MSFeedColumns& msFeed(msc_p->feed()); for (Int row = 0; row < nrow; row++) { // cout << "update: feed time =" << msFeed.time()(row) << ", rdate " << rdate << endl; msFeed.time().put(row, msFeed.time()(row) + rdate); // add the reference date // cout << "update: feed time new =" << msFeed.time()(row) << endl; } delete msc_p; msc_p = 0; } bool FITSIDItoMS1::readFitsFile(const String& msFile) { *itsLog << LogOrigin("FitsIDItoMS()", "readFitsFile"); Int nField=0, nSpW=0; String tmpPolTab; const Regex trailing(" *$"); // trailing blanks String extname(FITSIDItoMS1::extname()); extname=extname.before(trailing); *itsLog << LogIO::NORMAL << "Found binary table " << extname << LogIO::POST; if(extname=="UV_DATA") { String tmpdir = msFile + "_tmp"; getAxisInfo(); if(firstMain){ Bool useTSM=True; Bool mainTbl=True; setupMeasurementSet(msFile, useTSM, mainTbl); fillMSMainTable(msFile, nField, nSpW); fillObsTables(); fixEpochReferences(); updateTables(tmpdir); firstMain=False; } else{ fillMSMainTable(msFile, nField, nSpW); fillObsTables(); } } else{ Bool useTSM=False; Bool mainTbl=False; Bool addCorrMode=False; Bool addSyscal=False; Bool addWeather=False; Bool addGainCurve=False; Bool addPhaseCal=False; Bool addEOP=False; if (firstSyscal && extname == "SYSTEM_TEMPERATURE") { addSyscal=True; firstSyscal=False; } if (firstWeather && extname == "WEATHER") { addWeather=True; firstWeather=False; } if (firstGainCurve && extname == "GAIN_CURVE") { addGainCurve=True; firstGainCurve=False; } if (firstPhaseCal && extname == "PHASE-CAL") { addPhaseCal=True; firstPhaseCal=False; } if (firstPhaseCal && extname == "CALC") { addEOP=True; firstEOP=False; } setupMeasurementSet(msFile, useTSM, mainTbl, addCorrMode, addSyscal, addWeather, addGainCurve, addPhaseCal, addEOP); Bool success = True; // for the optional tables, we have a return value permitting us // to skip them if they cannot be read if(extname=="ARRAY_GEOMETRY") fillAntennaTable(); else if (extname=="SOURCE") fillFieldTable(); else if (extname=="FREQUENCY") fillSpectralWindowTable(); else if (extname=="ANTENNA") fillFeedTable(); else if (extname=="INTERFEROMETER_MODEL") success = fillCorrelatorModelTable(); else if (extname=="SYSTEM_TEMPERATURE") success = fillSysCalTable(); else if (extname=="FLAG") success = fillFlagCmdTable(); else if (extname=="GAIN_CURVE") success = handleGainCurve(); else if (extname=="PHASE-CAL") success = handlePhaseCal(); else if (extname=="WEATHER") success = fillWeatherTable(); else if (extname=="MODEL_COMPS") success = handleModelComps(); else if (extname=="CALC") success = handleCalc(); else if(extname =="BASELINE" || extname =="BANDPASS" || extname =="CALIBRATION" ){ *itsLog << LogIO::WARN << "FITS-IDI table " << extname << " not yet supported. Will ignore it." << LogIO::POST; return False; } else { if (extname != "TAPE_STATISTICS" && nrows() > 0) { *itsLog << LogIO::WARN << "Extension " << extname << " not part of the FITS-IDI convention. Will ignore it." << LogIO::POST; } return False; } if(!success){ *itsLog << LogIO::WARN << "The optional FITS-IDI table " << extname << " could not be read. Will ignore it." << LogIO::POST; return False; } } return True; } } //# NAMESPACE CASACORE - END casacore-3.7.1/msfits/MSFits/FitsIDItoMS.h000066400000000000000000000250361476623553700202130ustar00rootroot00000000000000//# FITSIDItoMS.h: Convert a FITS-IDI binary table to an AIPS++ Table. //# Copyright (C) 1995,1996,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Mod 2010: DP #ifndef MS_FITSIDITOMS_H #define MS_FITSIDITOMS_H #include #include #include // #include // #include // #include // #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class MSColumns; class FitsInput; // // FITSIDItoMS converts a FITS-IDI file to a CASA Measurement Set // // // // //# Classes you should understand before using this one. //
      • FitsInput //
      • HeaderDataUnit //
      • BinaryTableExtension //
      • Tables module // // // FITSIDItoMS inherits from the FITS BinaryTableExtension class and // its primary use is to convert such an object to a CASA Table. // This explains it's use but not its name. A better name should be // found. // // // The class starts with an already existing FitsInput object, which // should be set at a BinaryTableExtension HDU. Member functions // provide a TableDesc appropriate for the FITS data (to help in // constructing a CASA Table compatible with the // BinaryTableExtension), a Table containing the current row of FITS // data and a Table containing the next row of FITS data (which can be // used to step through the FitsInput, copying each row using the // RowCopier class), and a Table containin the entire FITS binary // table from the current row to the end of the table. // // // We need a way to get FITS-IDI data (typically from VLBI observations) into CASA. // // // Open a FitsInput from a disk file, if the HDU is a // BinaryTableExtension, then instantiate a MSBinaryTable object and // get the entire table. A fair amount of error checking has been // eliminated from this example. // // FitsInput infits("myFITSFile", FITS::Disk); // switch (infits.hdutype()) { // case FITS::BinaryTableHDU: // MSBinaryTable bintab(infits); // Table tab = bintab.fullTable("myTable"); // break; // } // // There would obviously be other cases to the switch to deal with any // other HDUs (e.g. skip them via infits.skip_hdu()). The Table // destructor would write "myTable" to disk. // // // // class FITSIDItoMS1 : public BinaryTableExtension { public: // // The only constructor is from a FitsInput. The correlat string // should be set to the correlator name/type as contained in the // CORRELAT keyword from the FITS-IDI primary header. If it is set // to "DIFX" (case-sensitive) additional digital corrections // appropriate for the DiFX software correlator will be applied. // Other valid values include "VLBA" (for the original VLBA hardware // correlator; currently unsupported) and "SFXC" (for the SFXC // software correlator used by the EVN). // FITSIDItoMS1(FitsInput& in, const String& correlat, const Int& obsType=0, const Bool& initFirstMain=True, const Float& vanVleck=0.0, const Float& corVer=0.0); ~FITSIDItoMS1(); // // Get the full table, using the supplied arguments to construct // the table. The table will contain all data from the current // row to the end of the BinaryTableExtension. // Table oldfullTable(const String& tabName); // Fill the Observation and ObsLog tables void fillObsTables(); // Read a binary table extension of type ANTENNA and create an antenna table //void fillAntennaTable(BinaryTable& bt); void fillAntennaTable(); // fill the Feed table with minimal info needed for synthesis processing void fillFeedTable(); //fill the Field table //void fillFieldTable(Int nField); void fillFieldTable(); //fill the Spectral Window table with the content of FREQUENCY void fillSpectralWindowTable(); //fill the optional Correlator Model table with the content of INTERFEROMETER_MODEL Bool fillCorrelatorModelTable(); //fill the optional SysCal table with the content of SYSTEM_TEMPERATURE Bool fillSysCalTable(); //fill the optional FlagCmd table with the content of FLAG Bool fillFlagCmdTable(); //fill the optional Weather table with the content of WEATHER Bool fillWeatherTable(); //store the information from the GAIN_CURVE table in a calibration table Bool handleGainCurve(); //store the information from the PHASE-CAL table in a calibration table Bool handlePhaseCal(); //store the information from the CALC table in a calibration table Bool handleCalc(); //store the information from the MODEL_COMPS table Bool handleModelComps(); // fix up the EPOCH MEASURE_REFERENCE keywords void fixEpochReferences(); //update the Polarization table void updateTables(const String& tabName); // // Get an appropriate TableDesc (this is the same TableDesc used // to construct any Table objects returned by this class. // const TableDesc& getDescriptor(); // // Return the Table keywords (this is the same TableRecord used in // any Table objects returned by this class. // TableRecord& getKeywords(); // // Get a Table with a single row, the current row of the FITS // table. The returned Table is a Scratch table. The standard // BinaryTableExtension manipulation functions are available to // position the FITS input at the desired location. // const Table &thisRow(); // // Get a Table with a single row, the next row of the FITS table. // The returned Table is a Scratch table. The FITS input is // positioned to the next row and the values translated and // returned in a Table object. // const Table &nextRow(); // Get the version of the archived MS. Float msVersion() const { return itsVersion; } // Read all the data from the FITS file and create the MeasurementSet. Throws // an exception when it has severe trouble interpreting the FITS file. // Returns False if it encounters an unsupported extension. Bool readFitsFile(const String& msFile); //is this the first UV_DATA extension Bool isfirstMain(){return firstMain;} protected: // Read the axis info, throws an exception if required axes are missing. void getAxisInfo(); // Set up the MeasurementSet, including StorageManagers and fixed columns. // If useTSM is True, the Tiled Storage Manager will be used to store // DATA, FLAG and WEIGHT_SPECTRUM void setupMeasurementSet(const String& MSFileName, Bool useTSM=True, Bool mainTbl=False, Bool addCorrMod=False, Bool addSyscal=False, Bool addWeather=False, Bool addGainCurve=False, Bool addPhaseCal=False, Bool addEOP=False); // Fill the main table from the Primary group data void fillMSMainTable(const String& MSFileName, Int& nField, Int& nSpW); private: // //# Data Members // // The scratch table containing the current row Table itsCurRowTab; // The number of elements for each column of the // BinaryTableExtension Vector itsNelem; // For each column: is it an array? Vector itsIsArray; // Table keyword set TableRecord itsKwSet; // Table descriptor for construction TableDesc itsTableDesc; // Table info TableInfo itsTableInfo; // The MS version. Float itsVersion; // // Buffer for storing the MSK's, MS-specific FITS keywords. // uInt itsNrMSKs; Vector itsMSKC; Vector itsMSKN; Vector itsMSKV; Vector itsgotMSK; //# FitsInput &infile_p; String msFile_p; Vector nPixel_p,corrType_p; Block corrIndex_p, corrSwapIndex_p; Matrix corrProduct_p; Vector coordType_p; Vector refVal_p, refPix_p, delta_p; static String array_p; String object_p,timsys_p; Double epoch_p; static Double rdate; Int nAnt_p; Vector receptorAngle_p; MFrequency::Types freqsys_p; Double restfreq_p; LogIO* itsLog; //# Int nIF_p; Double startTime_p; Double lastTime_p; Int itsObsType; String itsCorrelat; Float itsCorVer; Float itsVanVleck; MeasurementSet ms_p; MSColumns* msc_p; static Bool firstMain; static Bool firstSyscal; static Bool firstWeather; static Bool firstGainCurve; static Bool firstPhaseCal; static Bool firstEOP; Bool weather_hasWater_p; Bool weather_hasElectron_p; Bool uv_data_hasWeights_p; Bool weightypKwPresent_p; String weightyp_p; Int nStokes_p; Int nBand_p; Double visScl_p; static std::map antIdFromNo; static std::map digiLevels; static Vector effChBw; Int nFreqid_p; // //# Member Functions // // Fill in each row as needed void fillRow(); // Build part of the keywords of the itsCurRowTab void convertKeywords(); // Convert FITS field descriptions to TableColumn descriptions. void describeColumns(); // Convert the MS-specific keywords in the FITS binary table. void convertMSKeywords(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/msfits/MSFits/MSFitsIDI.cc000066400000000000000000000410271476623553700200040ustar00rootroot00000000000000//# MSFitsIDI.cc: Implementation of MSFitsIDI.h //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //---------------------------------------------------------------------------- #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //---------------------------------------------------------------------------- MSFitsIDI::MSFitsIDI(const Path& tapeDevice, const String& msOut, const Bool& overWrite, const Int& obsType) : itsDataSource(""), itsDeviceType(FITS::Tape9), itsMSOut(""), itsMSExists(False), itsSelectedFiles(0) { // Construct from a tape device and output MS file name // Input: // tapeDevice const String& Tape device name // msOut const String& Output MS name // overWrite const Bool& True if existing MS is to // be overwritten // Output to private data: // itsDataSource String Tape name or input file name // itsDeviceType FITS::DeviceType FITS device type (disk or tape) // itsMSOut String Output MS name //DP// itsMS MeasurementSet* Pointer to output MS // itsMSExists Bool True if output MS already exists //DP// itsOverWrite Bool True if existing MS is to //DP// be overwritten // itsSelectedFiles Vector Input file numbers selected // itsAllFilesSelected Bool True if all files selected // init(tapeDevice.absoluteName(), FITS::Tape9, msOut, overWrite, obsType); // } //---------------------------------------------------------------------------- MSFitsIDI::MSFitsIDI(const String& inFile, const String& msOut, const Bool& overWrite, const Int& obsType) : itsDataSource(""), itsDeviceType(FITS::Disk), itsMSOut(""), //DP itsMS(0), itsMSExists(False), //DP itsOverWrite(False), itsSelectedFiles(0) { // Construct from an input FITS-IDI file name and an output MS file name // Input: // inFile const String& Input FITS-IDI file name // msOut const String& Output MS name // overWrite const Bool& True if existing MS is to // be overwritten // Output to private data: // itsDataSource String Tape name or input file name // itsDeviceType FITS::DeviceType FITS device type (disk or tape) // itsMSOut String Output MS name //DP// itsMS MeasurementSet* Pointer to output MS // itsMSExists Bool True if output MS already exists //DP// itsOverWrite Bool True if existing MS is to // be overwritten // itsSelectedFiles Vector Input file numbers selected // itsAllFilesSelected Bool True if all files selected // init(inFile, FITS::Disk, msOut, overWrite, obsType); // } //---------------------------------------------------------------------------- MSFitsIDI::~MSFitsIDI() { //DP // Default desctructor //DP // Output to private data: //DP // itsMS MeasurementSet* Pointer to output MS //DP // //DP if (itsMS) { //DP delete (itsMS); //DP } } //---------------------------------------------------------------------------- void MSFitsIDI::selectFiles(const Vector& files) { // Select input tape files by number (1-relative) // Input: // files const Vector List of selected file numbers // Output to private data: // itsSelectedFiles Vector Input file numbers selected // itsAllFilesSelected Bool True if all files selected // itsSelectedFiles.resize(files.nelements()); itsSelectedFiles = files; if (itsSelectedFiles.nelements() > 0) { itsAllFilesSelected = False; } } //---------------------------------------------------------------------------- Bool MSFitsIDI::fillMS() { // Convert the FITS-IDI data to MS format // LogIO os(LogOrigin("MSFitsIDI", "fillMS()", WHERE)); // Delete the MS if it already exits and overwrite selected if (itsMSExists){ TableUtil::deleteTable(itsMSOut); } // new MS will be created within readFITSFile // // Tape input: loop over all selected input files // Bool atEnd = False; if (itsDeviceType == FITS::Tape9) { uInt fileIndex = 0; Int currentFile = 1; Int fileno = currentFile; while (!atEnd) { // Skip to next file selected if (itsAllFilesSelected) { fileno = currentFile; } else { atEnd = (fileIndex >= itsSelectedFiles.nelements()-1); if (!atEnd) fileno = itsSelectedFiles(fileIndex++); } if (!atEnd) { // Advance tape if necessary Int nskip = fileno - currentFile; if (nskip > 0) { TapeIO tapeDev(itsDataSource); tapeDev.skip(nskip); currentFile = currentFile + nskip; } // Read and process the selected input file readFITSFile(atEnd); // Increment file counter currentFile = currentFile + 1; } } // // Disk input: // } else if (itsDeviceType == FITS::Disk) { readFITSFile(atEnd); } return True; } //---------------------------------------------------------------------------- void MSFitsIDI::init(const String& dataSource, const FITS::FitsDevice& deviceType, const String& msOut, const Bool& overWrite, const Int& obsType) { // Initialization (called by all constructors) // Input: // dataSource const String& Input file name or tape device // deviceType const FITS::FitsDevice FITS device type (tape or disk) // msOut const String& Output MS name // overWrite const Bool& True if existing MS is to // be overwritten // Output to private data: // itsDataSource String Tape name or input file name // itsDeviceType FITS::DeviceType FITS device type (disk or tape) // itsMSOut String Output MS name // itsMS MeasurementSet* Pointer to output MS // itsMSExists Bool True if output MS already exists // itsOverWrite Bool True if existing MS is to // be overwritten // itsSelectedFiles Vector Input file numbers selected // itsAllFilesSelected Bool True if all files selected // LogIO os(LogOrigin("MSFitsIDI", "init()", WHERE)); // Check for valid FITS-IDI data source Path sourcePath(dataSource); if (!sourcePath.isValid() || !File(sourcePath).exists() || !File(sourcePath).isReadable()) { os << LogIO::SEVERE << "FITS-IDI data source is not readable" << LogIO::EXCEPTION; } itsDataSource = sourcePath.absoluteName(); itsDeviceType = deviceType; // Check for valid output MS specification Path msPath(msOut); itsMSExists = File(msPath).exists(); if (itsMSExists){ if(!overWrite){ os << LogIO::SEVERE << "Output MS exists and should not be overwritten." << LogIO::EXCEPTION; } else if(!File(msPath).isWritable()){ os << LogIO::SEVERE << "Output MS is not writable" << LogIO::EXCEPTION; } } else if(!File(msPath).canCreate()){ os << LogIO::SEVERE << "Output MS cannot be created" << LogIO::EXCEPTION; } itsMSOut = msOut; //DP itsOverWrite = overWrite; itsObsType = obsType; // Set remaining default parameters itsAllFilesSelected = True; } //---------------------------------------------------------------------------- void MSFitsIDI::readFITSFile(Bool& atEnd) { // Read and process the current FITS-IDI input file (on tape or disk) // Output: // atEnd Bool True if at EOF // LogIO os(LogOrigin("MSFitsIDI", "readFITSFile()", WHERE)); atEnd = False; // Construct a FitsInput object FitsInput infits(itsDataSource.chars(), itsDeviceType); if (infits.err() != FitsIO::OK) { os << LogIO::SEVERE << "Error reading FITS input" << LogIO::EXCEPTION; } // Make sure this is a FITS-IDI file; the Astropy FITS code may // inadvertedly turn an valid FITS-IDI into a random groups FITS file. if (infits.hdutype() != FITS::PrimaryArrayHDU) { os << LogIO::SEVERE << "Not a FITS-IDI file" << LogIO::EXCEPTION; } // Regular expression for trailing blanks Regex trailing(" *$"); // Create a temporary work directory for the sub-tables Directory tmpDir(itsMSOut + "_tmp"); tmpDir.create(); // Vector of sub-table names Vector subTableName; Int subTableNr = -1; Table maintab; // Correlator String correlat; Float corVer = 0.0; Float vanVleck = 0.0; // Loop over all HDU in the FITS-IDI file Bool initFirstMain = True; while (infits.err() == FitsIO::OK && !infits.eof()) { // Fetch correlator info from the primary HDU if (infits.hdutype() == FITS::PrimaryArrayHDU) { BytePrimaryArray tab(infits); if (tab.kw("CORRELAT")) { correlat = tab.kw("CORRELAT")->asString(); correlat.trim(); os << LogIO::NORMAL << "Correlator: " << correlat << LogIO::POST; } if(tab.kw("FXCORVER")){ corVer = std::stof(tab.kw("FXCORVER")->asString()); os << LogIO::NORMAL << "CorVer: " << corVer << LogIO::POST; } if (tab.kw("VANVLECK")) { vanVleck = tab.kw("VANVLECK")->asFloat(); os << LogIO::NORMAL << "VanVleck: " << vanVleck << LogIO::POST; } } // Skip non-binary table HDU's if (infits.hdutype() != FITS::BinaryTableHDU) { os << LogIO::DEBUG1 << "Skipping non-binary table HDU" << LogIO::POST; infits.skip_hdu(); } else if (infits.rectype() == FITS::SpecialRecord) { os << LogIO::WARN << "Skipping FITS special record" << LogIO::POST; infits.read_sp(); } else { // Process the FITS-IDI input from the position of this binary table FITSIDItoMS1 bintab(infits, correlat, itsObsType, initFirstMain, vanVleck, corVer); initFirstMain = False; String hduName = bintab.extname(); hduName = hduName.before(trailing); String tableName = itsMSOut; if (hduName != "") { if (hduName != "UV_DATA") { tableName = tableName + "_tmp/" + hduName; } // Process the FITS-IDI input Bool success = bintab.readFitsFile(tableName); if (infits.err() != FitsIO::OK) { os << LogIO::SEVERE << "Error reading FITS input" << LogIO::EXCEPTION; } if(success){ if (hduName != "UV_DATA") { subTableNr++; subTableName.resize(subTableNr+1, True); subTableName(subTableNr) = hduName; } } else{ // ignore this subtable if (infits.currsize() > 0) infits.skip_all(FITS::BinaryTableHDU); } } } } // end while // Move the subtables in the proper place and add the subtable // references to the main table description. // os << LogIO::NORMAL << "Subtables found: " << subTableName << LogIO::POST; // Open the main table to be updated. Table msmain (itsMSOut, Table::Update); // Loop over all subtables. for (Int isub=0; isub<=subTableNr; isub++) { //cout << "renaming subtable " << subTableName(isub) << endl; // Open the subtable to be updated. if (subTableName(isub)=="ARRAY_GEOMETRY") { Table mssub(itsMSOut+"_tmp/"+subTableName(isub)+"/ANTENNA",Table::Update); // Rename the subtable. mssub.rename (itsMSOut+"/ANTENNA",Table::Update); // Attach the subtable to the main table. msmain.rwKeywordSet().defineTable("ANTENNA",mssub); } if (subTableName(isub)=="SOURCE") { Table mssub(itsMSOut+"_tmp/"+subTableName(isub)+"/FIELD",Table::Update); mssub.rename (itsMSOut+"/FIELD",Table::Update); msmain.rwKeywordSet().defineTable("FIELD",mssub); } if (subTableName(isub)=="FREQUENCY") { Table mssub(itsMSOut+"_tmp/"+subTableName(isub)+"/SPECTRAL_WINDOW",Table::Update); mssub.rename (itsMSOut+"/SPECTRAL_WINDOW",Table::Update); msmain.rwKeywordSet().defineTable("SPECTRAL_WINDOW",mssub); Table mssub2(itsMSOut+"_tmp/"+subTableName(isub)+"/DATA_DESCRIPTION",Table::Update); mssub2.rename (itsMSOut+"/DATA_DESCRIPTION",Table::Update); msmain.rwKeywordSet().defineTable("DATA_DESCRIPTION",mssub2); Table mssub3(itsMSOut+"_tmp/"+subTableName(isub)+"/POLARIZATION",Table::Update); mssub3.rename (itsMSOut+"/POLARIZATION",Table::Update); msmain.rwKeywordSet().defineTable("POLARIZATION",mssub3); } if (subTableName(isub)=="ANTENNA") { Table mssub(itsMSOut+"_tmp/"+subTableName(isub)+"/FEED",Table::Update); mssub.rename (itsMSOut+"/FEED",Table::Update); msmain.rwKeywordSet().defineTable("FEED",mssub); } if (subTableName(isub)=="POINTING_DATA") { Table mssub(itsMSOut+"_tmp/"+subTableName(isub)+"/POINTING",Table::Update); mssub.rename (itsMSOut+"/POINTING",Table::Update); msmain.rwKeywordSet().defineTable("POINTING",mssub); } if (subTableName(isub)=="SYSTEM_TEMPERATURE") { Table mssub(itsMSOut+"_tmp/"+subTableName(isub)+"/SYSCAL",Table::Update); mssub.rename (itsMSOut+"/SYSCAL",Table::New); msmain.rwKeywordSet().defineTable("SYSCAL",mssub); } if (subTableName(isub)=="FLAG") { Table mssub(itsMSOut+"_tmp/"+subTableName(isub)+"/FLAG_CMD",Table::Update); mssub.rename (itsMSOut+"/FLAG_CMD",Table::New); msmain.rwKeywordSet().defineTable("FLAG_CMD",mssub); } if (subTableName(isub)=="WEATHER") { Table mssub(itsMSOut+"_tmp/"+subTableName(isub)+"/WEATHER",Table::Update); mssub.rename (itsMSOut+"/WEATHER",Table::New); msmain.rwKeywordSet().defineTable("WEATHER",mssub); } if (subTableName(isub)=="GAIN_CURVE") { Table mssub(itsMSOut+"_tmp/"+subTableName(isub)+"/GAIN_CURVE",Table::Update); mssub.rename (itsMSOut+"/GAIN_CURVE",Table::New); msmain.rwKeywordSet().defineTable("GAIN_CURVE",mssub); } if (subTableName(isub)=="PHASE-CAL") { Table mssub(itsMSOut+"_tmp/"+subTableName(isub)+"/PHASE_CAL",Table::Update); mssub.rename (itsMSOut+"/PHASE_CAL",Table::New); msmain.rwKeywordSet().defineTable("PHASE_CAL",mssub); } if (subTableName(isub)=="CALC") { Table mssub(itsMSOut+"_tmp/"+subTableName(isub)+"/EARTH_ORIENTATION",Table::Update); mssub.rename (itsMSOut+"/EARTH_ORIENTATION",Table::New); msmain.rwKeywordSet().defineTable("EARTH_ORIENTATION",mssub); } //if (subTableName(isub)=="INTERFEROMETER_MODEL") { // Table mssub(itsMSOut+"_tmp/"+subTableName(isub)+"/IDI_CORRELATOR_MODEL",Table::Update); // mssub.rename (itsMSOut+"/IDI_CORRELATOR_MODEL",Table::Update); // msmain.rwKeywordSet().defineTable("IDI_CORRELATOR_MODEL",mssub); //} } tmpDir.removeRecursive(False); } } //# NAMESPACE CASACORE - END casacore-3.7.1/msfits/MSFits/MSFitsIDI.h000066400000000000000000000070521476623553700176460ustar00rootroot00000000000000//# MSFitsIDI.h: Convert FITS-IDI data to MS format //# Copyright (C) 1996,1997,1998,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSFITSIDI_H #define MS_MSFITSIDI_H #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // MSFitsIDI: Convert FITS-IDI data to MS format // // // // //
      • MeasurementSet module // // // // From "MS" and "FITS-IDI // // // // The MSFitsIDI class converts FITS-IDI data, on tape or disk, // to MeasurementSet (MS) format. // // // // // // // // // Encapsulate all FITS-IDI to MS conversion capabilities. // // // // (i) General input filtering. // (ii) VLBA digital correlator corrections // (iii) Convert all sub-tables // class MSFitsIDI { public: // Construct from a tape device name and MS output file name MSFitsIDI(const Path& tapeDevice, const String& msOut, const Bool& overWrite, const Int& obsType=0); // Construct from an input file name and an MS output file name MSFitsIDI(const String& inFile, const String& msOut, const Bool& overWrite, const Int& obsType=0); // Destructor ~MSFitsIDI(); // Set which files are selected (1-rel; for tape-based data) void selectFiles(const Vector& files); // Convert the FITS-IDI data to MS format Bool fillMS(); protected: // Initialization (called by all constructors) void init(const String& dataSource, const FITS::FitsDevice& deviceType, const String& msOut, const Bool& overWrite, const Int& obsType); // Read and process a FITS-IDI file void readFITSFile(Bool& atEnd); private: // Data source and device type String itsDataSource; FITS::FitsDevice itsDeviceType; // MS, status and write options String itsMSOut; Bool itsMSExists; Int itsObsType; // 0=standard, 1=fastmosaic, requiring small tiles in the measurement set // Selected file numbers (1-relative) Vector itsSelectedFiles; Bool itsAllFilesSelected; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/msfits/MSFits/MSFitsInput.cc000066400000000000000000004515371476623553700205110ustar00rootroot00000000000000//# MSFitsInput: uvfits (random group) to MeasurementSet filler //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., 675 Mass Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using std::make_pair; namespace casacore { //# NAMESPACE CASACORE - BEGIN extern void showBinaryTable(BinaryTableExtension &x); // Returns the 0-based position of the key string in the map, // which is a list of strings. Looks for the "Which" occurrance // of the key. static Int getIndex(Vector& map, const String& key, uInt which = 0) { uInt count = 0; const uInt nMap = map.nelements(); for (uInt i = 0; i < nMap; i++) { if (map(i) == key) { if (count == which) { return i; } else { count++; } } } return -1; } // Like getIndex, but only checks for containment, not exact identity static Int getIndexContains(Vector& map, const String& key, uInt which = 0) { uInt count = 0; const uInt nMap = map.nelements(); for (uInt i = 0; i < nMap; i++) { if (map(i).contains(key)) { if (count == which) { return i; } else { count++; } } } return -1; } MSPrimaryGroupHolder::MSPrimaryGroupHolder() : hdu_p(0), ps(0), pl(0), pf(0) { } MSPrimaryGroupHolder::MSPrimaryGroupHolder(FitsInput& infile) : ps(0), pl(0), pf(0) { attach(infile); } void MSPrimaryGroupHolder::attach(FitsInput& infile) { detach(); switch (infile.datatype()) { case FITS::SHORT: ps = new PrimaryGroup (infile); hdu_p = ps; break; case FITS::LONG: pl = new PrimaryGroup (infile); hdu_p = pl; break; case FITS::FLOAT: pf = new PrimaryGroup (infile); hdu_p = pf; break; default: throw(AipsError("PrimaryGroupHolder(infile): unhandled FITS datatype")); } } MSPrimaryGroupHolder::~MSPrimaryGroupHolder() { detach(); } void MSPrimaryGroupHolder::detach() { if (ps) delete ps; if (pl) delete pl; if (pf) delete pf; ps = 0; pl = 0; pf = 0; } //---------------------------- MSPrimaryTableHolder::MSPrimaryTableHolder() : hdu_p(0), ps(0), pl(0), pf(0), pb(0) { } MSPrimaryTableHolder::MSPrimaryTableHolder(FitsInput& infile) : ps(0), pl(0), pf(0), pb(0) { attach(infile); } void MSPrimaryTableHolder::attach(FitsInput& infile) { detach(); switch (infile.datatype()) { case FITS::SHORT: ps = new PrimaryTable (infile); hdu_p = ps; break; case FITS::LONG: pl = new PrimaryTable (infile); hdu_p = pl; break; case FITS::FLOAT: pf = new PrimaryTable (infile); hdu_p = pf; break; case FITS::BYTE: pb = new PrimaryTable (infile); hdu_p = pb; break; default: throw(AipsError("PrimaryTableHolder(infile): unhandled FITS datatype")); } } MSPrimaryTableHolder::~MSPrimaryTableHolder() { detach(); } void MSPrimaryTableHolder::detach() { if (ps) delete ps; if (pl) delete pl; if (pf) delete pf; if (pb) delete pb; ps = 0; pl = 0; pf = 0; pb = 0; } //------------------------------------------------------------ MSFitsInput::MSFitsInput(const String& msFile, const String& fitsFile, const Bool useNewStyle) : _infile(0), _msc(0), _uniqueAnts(), _nAntRow(0), _restfreq(0), _addSourceTable(False), _log(LogOrigin("MSFitsInput", "MSFitsInput")), _newNameStyle(useNewStyle), _msCreated(False) { // First, lets verify that fitsfile exists and that it appears to be a // FITS file. File f(fitsFile); if (! f.exists() || ! f.isReadable()) { _log << LogOrigin("MSFitsInput", "MSFitsInput") << "File " << fitsFile << " does not exist or is not readable" << LogIO::EXCEPTION; } // First attempt at validating that it's a FITS file if (!f.isRegular()) { _log << LogOrigin("MSFitsInput", "MSFitsInput") << "File " << fitsFile << " is not a plain file (maybe a directory?)" << LogIO::EXCEPTION; } // We should probably look for SIMPLE = here String errmsg; NewFile fileOK(True); if (!fileOK.valueOK(msFile, errmsg)) { _log << LogOrigin("MSFitsInput", "MSFitsInput") << "Error in output file: " << errmsg << LogIO::EXCEPTION; } _msFile = msFile; _log << LogOrigin("MSFitsInput", "MSFitsInput") << LogIO::NORMAL << "Converting FITS file '" << fitsFile << "' to MeasurementSet '" << msFile << "'" << LogIO::POST; // Open the FITS file for reading _infile = new FitsInput(fitsFile.chars(), FITS::Disk); _obsTime.resize(2); MVTime timeVal; MEpoch::Types epochRef; FITSDateUtil::fromFITS(timeVal, epochRef, "2000-01-01", "UTC"); _obsTime(0) = timeVal.second(); _obsTime(1) = timeVal.second(); if (_infile) { if (_infile->err() == FitsIO::IOERR) { ThrowCc("Failed to read file " + fitsFile); } else if (_infile->err()) { _log << LogOrigin("MSFitsInput", "MSFitsInput") << "Failed to read initial record -- exiting." << LogIO::EXCEPTION; } else { if (_checkInput(*_infile)) { if (_infile->hdutype() == FITS::PrimaryGroupHDU) { _priGroup.attach(*_infile); } if (_infile->hdutype() == FITS::PrimaryTableHDU) { _priTable.attach(*_infile); } } } } else { _log << LogOrigin("MSFitsInput", "MSFitsInput") << "Failed to open fits file " << fitsFile << LogIO::EXCEPTION; } } void MSFitsInput::readRandomGroupUVFits(Int obsType) { _log << LogOrigin("MSFitsInput", __func__) << LogIO::POST; Int nField = 0, nSpW = 0; _useAltrval = False; getPrimaryGroupAxisInfo(); Bool useTSM = True; setupMeasurementSet(_msFile, useTSM, obsType); // fill the OBSERVATION table fillObsTables(); Int totMem = HostInfo::memoryTotal(); // 8 bytes per complex number and the other data like flag, weight is // is 1/2 of the total Int estMem = _priGroup.gcount() * max(1, _nIF) / 1024 * _nPixel(getIndex(_coordType, "STOKES")) * _nPixel(getIndex(_coordType, "FREQ")) * 8 * 2; Int ns = max(1, _nIF); Int nc = _nPixel( getIndex(_coordType, "STOKES")); Int nf = _nPixel( getIndex(_coordType, "FREQ")); Long estStor = _priGroup.gcount() * ns / 1024 * (7 * 8 + 11 * 4 + (2 * nc + 3 * nc * nf) * 4 + nc * nf + 1); Float needS = estStor / 1024. ; Directory curD(_msFile); Float freeS = curD.freeSpaceInMB(); _log << LogOrigin("MSFitsInput", __func__) << ((needS > freeS) ? LogIO::WARN : LogIO::DEBUG1) << "Estimate of Needed Storage Space in MB: " << 0.9 * needS << "~" << 1.6 * needS << "\n Free Space in MB: " << freeS << LogIO::POST; // In reality it can be twice that number // We can remove the estMem limit of 1 Gbyte, below, // if we are fully in 64 bits world // // fill the main table if ((estMem < totMem) && (estMem < 1000000)) { //fill column wise and keep columns in memory try { fillMSMainTableColWise(nField, nSpW); } catch(const AipsError& ex) { _log << LogOrigin("MSFitsInput", __func__) << ex.what() << LogIO::EXCEPTION; } } else { //else fill row wise try { fillMSMainTable(nField, nSpW); } catch(const AipsError& ex) { _log << LogOrigin("MSFitsInput", __func__) << ex.what() << LogIO::EXCEPTION; } } // now handle the BinaryTable extensions for the subtables Bool haveAn = False, haveField = False, haveSpW = False, haveSysPower = False; while (_infile->rectype() != FITS::EndOfFile && !_infile->err()) { if (_infile->hdutype() != FITS::BinaryTableHDU) { _log << LogOrigin("MSFitsInput", __func__) << LogIO::NORMAL << "Skipping unhandled extension" << LogIO::POST; _infile->skip_hdu(); } else { BinaryTable binTab(*_infile); // see if we can recognize the type String type = binTab.extname(); _log << LogOrigin("MSFitsInput", __func__) << LogIO::DEBUG1 << "Found binary table of type " << type << " following data" << LogIO::POST; _log << LogOrigin("MSFitsInput", __func__) << LogIO::NORMAL << "extname=" << type << " nrows=" << binTab.nrows() << " ncols=" << binTab.ncols() << " rowsize=" << binTab.rowsize() << " pcount=" << binTab.pcount() << " gcount=" << binTab.gcount() << LogIO::POST; if (type.contains("AN") && !haveAn) { haveAn = True; fillAntennaTable(binTab); } else if (type.contains("FQ") && !haveSpW) { haveSpW = True; fillSpectralWindowTable(binTab, nSpW); } else if (type.contains("SU") && !haveField) { haveField = True; fillFieldTable(binTab, nField); setFreqFrameVar(binTab); //in case spectral window was already filled if (haveSpW) { updateSpectralWindowTable(); } } else if (type.contains("SY") && ! haveSysPower) { haveSysPower = True; _fillSysPowerTable(binTab); } else { _log << LogOrigin("MSFitsInput", __func__) << LogIO::NORMAL << "Skipping table, duplicate or unrecognized type: " << type << LogIO::POST; binTab.fullTable(); } } } if (!haveSpW) { // single freq. case fillSpectralWindowTable(); } if (!haveField) { // single source case fillFieldTable(nField); } //this is uselessly slow thus replace it fillExtraTables(); fixEpochReferences(); if (!haveAn) { _log << LogOrigin("MSFitsInput", __func__) << "Cannot find an AN Table. This is required." << LogIO::EXCEPTION; } fillFeedTable(); } void MSFitsInput::readPrimaryTableUVFits(Int obsType) { _log << LogOrigin("MSFitsInput", __func__) << "_msFile=" << _msFile << "obsType=" << obsType << LogIO::POST; _useAltrval = False; Bool useTSM = False; _epochRef = getDirectionFrame(2000.0); setupMeasurementSet(_msFile, useTSM, obsType); ConstFitsKeywordList kwlist = _priTable.kwlist(); const FitsKeyword* kw; kwlist.first(); //this date is not source observation time! String date = "2000-01-01"; date = (kw = kwlist(FITS::DATE)) ? kw->asString() : date; MVTime timeVal; MEpoch::Types epochRef; FITSDateUtil::fromFITS(timeVal, epochRef, date, "UTC"); _obsTime(0) = timeVal.second(); _obsTime(1) = timeVal.second(); fillHistoryTable(kwlist); Bool moreToDo = true; while (moreToDo && _infile->rectype() != FITS::EndOfFile && !_infile->err()) { if (//_infile->rectype() != FITS::HDURecord || _infile->hdutype() != FITS::BinaryTableHDU) { _log << LogOrigin("MSFitsInput", __func__) << LogIO::NORMAL << "Skipping unhandled extension" << LogIO::POST; _infile->skip_hdu(); } else { _log << LogOrigin("MSFitsInput", __func__) << LogIO::DEBUG1 << "Binary Table HDU ------>>>" << LogIO::POST; BinaryTable* fqTab = 0; while (moreToDo && _infile->hdutype() == FITS::BinaryTableHDU) { _log << LogOrigin("MSFitsInput", __func__) << LogIO::DEBUG1 << "Found binary table of type " << _infile->rectype() << " following data" << LogIO::POST; BinaryTable* bt = new BinaryTable(*_infile); String type = bt->extname(); _log << LogOrigin("MSFitsInput", __func__) << LogIO::NORMAL << "extname=" << bt->extname() << " nrows=" << bt->nrows() << " ncols=" << bt->ncols() << " rowsize=" << bt->rowsize() << LogIO::POST; if (type.contains("AN")) { fillAntennaTable(*bt); } else if (type.contains("FQ")) { fqTab = &(*bt); } else if (type.contains("SU")) { fillFieldTable(*bt); setFreqFrameVar(*bt); //in case spectral window was already filled //if (haveSpW) { // updateSpectralWindowTable(); //} } else if (type.contains("SY")) { _fillSysPowerTable(*bt); } else if (type.contains("UV")) { //showBinaryTable(*bt); //fillOtherUVTables(*bt, *fqTab); //TableRecord btKeywords = bt.getKeywords(); ConstFitsKeywordList kwl = bt->kwlist(); //FitsKeywordList pkw = kwl; fillObservationTable(kwl); fillHistoryTable(kwl); getAxisInfo(kwl); sortPolarizations(); fillPolarizationTable(); fillSpectralWindowTable(*fqTab); try { fillMSMainTable(*bt); } catch(const AipsError& ex) { _log << LogOrigin("MSFitsInput", __func__) << ex.what() << LogIO::EXCEPTION; } //fillPointingTable(); fillSourceTable(); fillFeedTable(); moreToDo = false; } else { _infile->skip_hdu(); _log << LogOrigin("MSFitsInput", __func__) << LogIO::NORMAL << "skip " << type << LogIO::POST; } _log << LogOrigin("MSFitsInput", __func__) << LogIO::DEBUG1 << "<<<------ Binary Table HDU" << LogIO::POST; } //fill source table } } } void MSFitsInput::readFitsFile(Int obsType) { _log << LogOrigin("MSFitsInput", __func__) << LogIO::DEBUG2 << "hdutype=" << _infile->hdutype() << LogIO::POST; try { if (_infile->hdutype() == FITS::PrimaryGroupHDU) { readRandomGroupUVFits(obsType); } else if (_infile->hdutype() == FITS::PrimaryTableHDU) { readPrimaryTableUVFits(obsType); } else { ThrowCc("Unhandled extension type"); } } catch(const AipsError& ex) { if (_msCreated) { String name = _ms.tableName(); _log << LogIO::NORMAL << "Exception while processing UVFITS file. Deleting incomplete MS '" << name << "'" << LogIO::POST; _ms.closeSubTables(); _ms.relinquishAutoLocks(True); // detach to close _ms = MeasurementSet(); TableUtil::deleteTable(name, True); } ThrowCc(ex.what()); } } MSFitsInput::~MSFitsInput() { delete _infile; delete _msc; } Bool MSFitsInput::_checkInput(FitsInput& infile) { // Check that we have a valid UV fits file if (infile.rectype() != FITS::HDURecord) { _log << LogOrigin("MSFitsInput", __func__) << "file does not start with standard hdu record." << LogIO::EXCEPTION; } _log << LogOrigin("MSFitsInput", __func__) << LogIO::DEBUG1 << "infile.hdutype(): " << infile.hdutype() << LogIO::POST; //visibilty must be one of these type if (infile.hdutype() != FITS::PrimaryGroupHDU && infile.hdutype() != FITS::PrimaryArrayHDU && infile.hdutype() != FITS::PrimaryTableHDU) { _log << LogOrigin("MSFitsInput", __func__) << "Error, neither primary group nor primary table" << LogIO::EXCEPTION; } FITS::ValueType dataType = infile.datatype(); if (dataType != FITS::FLOAT && dataType != FITS::SHORT && dataType != FITS::LONG && dataType != FITS::BYTE) { _log << LogOrigin("MSFitsInput", __func__) << "Error, this class handles only FLOAT, SHORT, LONG and BYTE data " << "(BITPIX=-32,16,32,8) at present" << LogIO::EXCEPTION; } return True; } void MSFitsInput::getPrimaryGroupAxisInfo() { _log << LogOrigin("MSFitsInput", "getPrimaryGroupAxisInfo"); // Extracts the axis related info. from the PrimaryGroup object and // returns them in the form of arrays. const Regex trailing(" *$"); // trailing blanks const Int nAxis = _priGroup.dims(); if (nAxis < 1) { _log << "Data has no axes!" << LogIO::EXCEPTION; } _nPixel.resize(nAxis); _refVal.resize(nAxis); _refPix.resize(nAxis); _delta.resize(nAxis); _coordType.resize(nAxis); for (Int i = 0; i < nAxis; i++) { _nPixel(i) = _priGroup.dim(i); if (_nPixel(i) < 0) { _log << "Axes " << i << " cannot have a negative value" << LogIO::EXCEPTION; } _coordType(i) = _priGroup.ctype(i); _coordType(i) = _coordType(i).before(trailing); _refVal(i) = static_cast (_priGroup.crval(i)); _refPix(i) = static_cast (_priGroup.crpix(i)); _delta(i) = static_cast (_priGroup.cdelt(i)); } // Check if required axes are there if (getIndex(_coordType, "COMPLEX") < 0) { _log << "Data does not have a COMPLEX axis" << LogIO::EXCEPTION; } if (getIndex(_coordType, "STOKES") < 0) { _log << "Data does not have a STOKES axis" << LogIO::EXCEPTION; } if (getIndex(_coordType, "FREQ") < 0) { _log << "Data does not have a FREQ axis" << LogIO::EXCEPTION; } if ((getIndex(_coordType, "RA") < 0) && (getIndex(_coordType, "RA---SIN") < 0) && (getIndex(_coordType, "RA---NCP") < 0) && (getIndex( _coordType, "RA---SCP") < 0)) { _log << "Data does not have a RA axis" << LogIO::EXCEPTION; } if ((getIndex(_coordType, "DEC") < 0) && (getIndex(_coordType, "DEC--SIN") < 0) && (getIndex( _coordType, "DEC--NCP") < 0) && (getIndex(_coordType, "DEC--SCP") < 0)) { _log << "Data does not have a DEC axis" << LogIO::EXCEPTION; } // Sort out the order of the polarizations and find the sort indices // to put them in 'standard' order: PP,PQ,QP,QQ const uInt iPol = getIndex(_coordType, "STOKES"); const uInt numCorr = _nPixel(iPol); _corrType.resize(numCorr); for (uInt i = 0; i < numCorr; i++) { // note: 1-based ref pix _corrType(i) = ifloor(_refVal(iPol) + (i + 1 - _refPix(iPol)) * _delta(iPol) + 0.5); // convert AIPS-convention Stokes description to Casacore enum switch (_corrType(i)) { case -8: _corrType(i) = Stokes::YX; break; case -7: _corrType(i) = Stokes::XY; break; case -6: _corrType(i) = Stokes::YY; break; case -5: _corrType(i) = Stokes::XX; break; case -4: _corrType(i) = Stokes::LR; break; case -3: _corrType(i) = Stokes::RL; break; case -2: _corrType(i) = Stokes::LL; break; case -1: _corrType(i) = Stokes::RR; break; case 4: // _corrType(i) = Stokes::V; ThrowCc( "Stokes V cannot be decomposed into proper correlation types " "without making assumptions. This functionality is not supported" ); break; case 3: //_corrType(i) = Stokes::U; ThrowCc( "Stokes U cannot be decomposed into proper correlation types " "without making assumptions. This functionality is not supported" ); break; case 2: //_corrType(i) = Stokes::Q; ThrowCc( "Stokes Q cannot be decomposed into proper correlation types " "without making assumptions. This functionality is not supported" ); break; case 1: //_corrType(i) = Stokes::I; ThrowCc( "Stokes I cannot be decomposed into proper correlation types " "without making assumptions. This functionality is not supported" ); break; default: if (_corrType(i) < 0) { _log << "Unknown Correlation type: " << _corrType(i) << LogIO::EXCEPTION; } } } Vector tmp(_corrType.copy()); // Sort the polarizations to standard order. Could probably use // GenSortIndirect here. GenSort::sort(_corrType); _corrIndex.resize(numCorr); // Get the sort indices to rearrange the data to standard order for (uInt i = 0; i < numCorr; i++) { for (uInt j = 0; j < numCorr; j++) { if (_corrType(j) == tmp(i)) _corrIndex[i] = j; } } // Figure out the correlation products from the polarizations _corrProduct.resize(2, numCorr); _corrProduct = 0; for (uInt i = 0; i < numCorr; i++) { const Stokes::StokesTypes cType = Stokes::type(_corrType(i)); Fallible receptor = Stokes::receptor1(cType); Bool warn = False; if (receptor.isValid()) { _corrProduct(0, i) = receptor; } else if (!warn) { warn = True; _log << LogIO::WARN << "Cannot deduce receptor 1 for correlations of type: " << Stokes::name(cType) << LogIO::POST; } receptor = Stokes::receptor2(cType); if (receptor.isValid()) { _corrProduct(1, i) = receptor; } else if (!warn) { warn = True; _log << LogIO::WARN << "Cannot deduce receptor 2 for correlations of type: " << Stokes::name(cType) << LogIO::POST; } } // Save the object name, we may need it (for single source fits) const FitsKeyword* kwp; _object = (kwp = _priGroup.kw(FITS::OBJECT)) ? kwp->asString() : "unknown"; _object = _object.before(trailing); // Save the array name _array = (kwp = _priGroup.kw(FITS::TELESCOP)) ? kwp->asString() : "unknown"; _array = _array.before(trailing); // Save the RA/DEC epoch (for ss fits) if (_priGroup.kw(FITS::EPOCH)) _epoch = (_priGroup.kw(FITS::EPOCH))->asFloat(); else if (_priGroup.kw(FITS::EQUINOX)) _epoch = (_priGroup.kw(FITS::EQUINOX))->asFloat(); else { _epoch = 2000.0; _log << LogIO::WARN << "Cannot find epoch of data, defaulting to J2000" << LogIO::POST; } //epoch_p = (kwp=priGroup_p.kw(FITS::EPOCH)) ? kwp->asFloat() : 2000.0; _epochRef = getDirectionFrame(_epoch); // Get the spectral information _freqsys = MFrequency::TOPO; _restfreq = 0.0; Record header; Vector ignore; Bool ok = FITSKeywordUtil::getKeywords(header, _priGroup.kwlist(), ignore); if (ok) { Int spectralAxis; Double referenceChannel, referenceFrequency, deltaFrequency; Vector frequencies; MDoppler::Types velPref; // Many of the following aren't used since they have been obtained // in other ways. ok = FITSSpectralUtil::fromFITSHeader(spectralAxis, referenceChannel, referenceFrequency, deltaFrequency, frequencies, _freqsys, velPref, _restfreq, _log, header); // Override freqsys_p from FITSSpectralUtil, if SPECSYS keyword present if (header.isDefined(String("specsys"))) { String fframe; header.get("specsys", fframe); MFrequency::getType(_freqsys, fframe); } // Be strict about use of ALTREF-derived frequencies: // Only if frame not enforced with SPECSYS keyword, // sufficient info is available to do the back-calculation, // and if that back calculation takes us out of the TOPO // Otherwise, we assume that the header and FQ frequencies are // SPECSYS (or TOPO) and are correct. _useAltrval = (!header.isDefined(String("specsys")) && header.isDefined( String("altrval")) && header.isDefined(String("restfreq")) && _freqsys != MFrequency::TOPO); _refFreq = referenceFrequency; _chanFreq = frequencies; } } void MSFitsInput::setupMeasurementSet(const String& MSFileName, Bool useTSM, Int obsType) { // Make the MS table TableDesc td = MS::requiredTableDesc(); // Even though we know the data is going to be the same shape throughout I'll // still create a column that has a variable shape as this will permit MS's // with other shapes to be appended. MS::addColumnToDesc(td, MS::DATA, 2); // add this optional column because random group fits has a // weight per visibility MS::addColumnToDesc(td, MS::WEIGHT_SPECTRUM, 2); if (useTSM) { td.defineHypercolumn("TiledData", 3, stringToVector(MS::columnName( MS::DATA))); td.defineHypercolumn("TiledFlag", 3, stringToVector(MS::columnName( MS::FLAG))); td.defineHypercolumn("TiledFlagCategory", 4, stringToVector( MS::columnName(MS::FLAG_CATEGORY))); td.defineHypercolumn("TiledWgtSpectrum", 3, stringToVector( MS::columnName(MS::WEIGHT_SPECTRUM))); td.defineHypercolumn("TiledUVW", 2, stringToVector(MS::columnName( MS::UVW))); td.defineHypercolumn("TiledWgt", 2, stringToVector(MS::columnName( MS::WEIGHT))); td.defineHypercolumn("TiledSigma", 2, stringToVector(MS::columnName( MS::SIGMA))); } SetupNewTable newtab(MSFileName, td, Table::New); // Set the default Storage Manager to be the Incr one IncrementalStMan incrStMan("ISMData"); newtab.bindAll(incrStMan, True); // Bind ANTENNA1, ANTENNA2 and DATA_DESC_ID to the standardStMan // as they may change sufficiently frequently to make the // incremental storage manager inefficient for these columns. StandardStMan aipsStMan(32768); newtab.bindColumn(MS::columnName(MS::ANTENNA1), aipsStMan); newtab.bindColumn(MS::columnName(MS::ANTENNA2), aipsStMan); newtab.bindColumn(MS::columnName(MS::DATA_DESC_ID), aipsStMan); if (useTSM) { Int nCorr = _nPixel(getIndex(_coordType, "STOKES")); Int nChan = _nPixel(getIndex(_coordType, "FREQ")); _nIF = getIndex(_coordType, "IF"); if (_nIF >= 0) { _nIF = _nPixel(_nIF); } else { _nIF = 1; } // Choose an appropriate tileshape IPosition dataShape(2, nCorr, nChan); IPosition tileShape = MSTileLayout::tileShape(dataShape, obsType, _array); _log << LogOrigin("MSFitsInput", __func__); _log << LogIO::NORMAL << "Using tile shape " << tileShape << " for " << _array << " with obstype=" << obsType << LogIO::POST; TiledShapeStMan tiledStMan1("TiledData", tileShape); TiledShapeStMan tiledStMan1f("TiledFlag", tileShape); TiledShapeStMan tiledStMan1fc("TiledFlagCategory", IPosition(4, tileShape(0), tileShape(1), 1, tileShape(2))); TiledShapeStMan tiledStMan2("TiledWgtSpectrum", tileShape); TiledColumnStMan tiledStMan3("TiledUVW", IPosition(2, 3, 1024)); TiledShapeStMan tiledStMan4("TiledWgt", IPosition(2, tileShape(0), tileShape(2))); TiledShapeStMan tiledStMan5("TiledSigma", IPosition(2, tileShape(0), tileShape(2))); // Bind the DATA, FLAG & WEIGHT_SPECTRUM columns to the tiled stman newtab.bindColumn(MS::columnName(MS::DATA), tiledStMan1); newtab.bindColumn(MS::columnName(MS::FLAG), tiledStMan1f); newtab.bindColumn(MS::columnName(MS::FLAG_CATEGORY), tiledStMan1fc); newtab.bindColumn(MS::columnName(MS::WEIGHT_SPECTRUM), tiledStMan2); newtab.bindColumn(MS::columnName(MS::UVW), tiledStMan3); newtab.bindColumn(MS::columnName(MS::WEIGHT), tiledStMan4); newtab.bindColumn(MS::columnName(MS::SIGMA), tiledStMan5); } else { newtab.bindColumn(MS::columnName(MS::DATA), aipsStMan); newtab.bindColumn(MS::columnName(MS::FLAG), aipsStMan); newtab.bindColumn(MS::columnName(MS::WEIGHT_SPECTRUM), aipsStMan); newtab.bindColumn(MS::columnName(MS::UVW), aipsStMan); } // avoid lock overheads by locking the table permanently TableLock lock(TableLock::AutoLocking); MeasurementSet ms(newtab, lock); _msCreated = True; // Set up the subtables for the UVFITS MS // we make new tables with 0 rows Table::TableOption option = Table::New; ms.createDefaultSubtables(option); // add the optional Source sub table to allow for // specification of the rest frequency TableDesc sourceTD = MSSource::requiredTableDesc(); MSSource::addColumnToDesc(sourceTD, MSSource::POSITION); MSSource::addColumnToDesc(sourceTD, MSSource::REST_FREQUENCY); MSSource::addColumnToDesc(sourceTD, MSSource::SYSVEL); MSSource::addColumnToDesc(sourceTD, MSSource::TRANSITION); MSSource::addColumnToDesc(sourceTD, MSSource::SOURCE_MODEL); SetupNewTable sourceSetup(ms.sourceTableName(), sourceTD, option); ms.rwKeywordSet().defineTable(MS::keywordName(MS::SOURCE), Table( sourceSetup, 0)); // update the references to the subtable keywords ms.initRefs(); { // Set the TableInfo TableInfo& info(ms.tableInfo()); info.setType(TableInfo::type(TableInfo::MEASUREMENTSET)); info.setSubType(String("UVFITS")); info.readmeAddLine( "This is a measurement set Table holding astronomical observations"); } _ms = ms; _msc = new MSColumns(_ms); _msc->setDirectionRef(_epochRef); // Does the subtables. // UVW is the only Direction type Measures column in the main table. _msc->setUVWRef(Muvw::castType(_epochRef)); } void MSFitsInput::fillObsTables() { const Regex trailing(" *$"); // trailing blanks const FitsKeyword* kwp; _ms.observation().addRow(); String observer; observer = (kwp = _priGroup.kw(FITS::OBSERVER)) ? kwp->asString() : ""; observer = observer.before(trailing); MSObservationColumns msObsCol(_ms.observation()); msObsCol.observer().put(0, observer); String telescope = (kwp = _priGroup.kw(FITS::TELESCOP)) ? kwp->asString() : "unknown"; telescope = telescope.before(trailing); if (telescope == "HATCREEK") telescope = "BIMA"; msObsCol.telescopeName().put(0, telescope); msObsCol.scheduleType().put(0, ""); msObsCol.project().put(0, ""); String date; date = (kwp = _priGroup.kw(FITS::DATE_OBS)) ? kwp->asString() : ""; if (date == "") { // try FITS::DATE instead // (but this will find DATE-MAP which may not be correct...) date = (kwp = _priGroup.kw(FITS::DATE)) ? kwp->asString() : ""; } if (date == "") date = "2000-01-01"; MVTime timeVal; MEpoch::Types epochRef; FITSDateUtil::fromFITS(timeVal, epochRef, date, "UTC"); Vector times(2); times(0) = timeVal.second(); times(1) = timeVal.second(); // change this to last time in input _obsTime(0) = times(0); _obsTime(1) = times(1); msObsCol.timeRange().put(0, times); msObsCol.releaseDate().put(0, times(0)); // just use TIME_RANGE for now Double time = timeVal.second(); msObsCol.flagRow().put(0, False); // Store all keywords from the first HISTORY keyword onwards in History table String history = (kwp = _priGroup.kw(FITS::HISTORY)) ? kwp->comm() : ""; history = history.before(trailing); MSHistoryColumns msHisCol(_ms.history()); Int row = -1; while (history != "") { _ms.history().addRow(); row++; msHisCol.observationId().put(row, 0); msHisCol.time().put(row, time); msHisCol.priority().put(row, "NORMAL"); msHisCol.origin().put(row, "MSFitsInput::fillObsTables"); msHisCol.application().put(row, "ms"); Vector cliComm(1); cliComm[0] = ""; msHisCol.cliCommand().put(row, cliComm); msHisCol.appParams().put(row, cliComm); msHisCol.message().put(row, history); history = (kwp = _priGroup.nextkw()) ? kwp->comm() : ""; history = history.before(trailing); } } // void MSFitsInput::fillHistoryTable(ConstFitsKeywordList &kwl) { kwl.first(); const FitsKeyword *kw; const Regex trailing(" *$"); String date; date = (kw = kwl(FITS::DATE_OBS)) ? kw->asString() : ""; if (date == "") { date = (kw = kwl(FITS::DATE)) ? kw->asString() : ""; } if (date == "") date = "2000-01-01"; MVTime timeVal; MEpoch::Types epochRef; FITSDateUtil::fromFITS(timeVal, epochRef, date, "UTC"); Double time = timeVal.second(); String history; MSHistoryColumns msHisCol(_ms.history()); Int row = _ms.history().nrow() - 1; kwl.first(); while ((kw = kwl.next())) { String nm = kw->name(); if (nm == "HISTORY" || nm == "COMMENT" || nm == "") { history = kw->comm(); history = history.before(trailing); _ms.history().addRow(); row++; msHisCol.observationId().put(row, 0); msHisCol.time().put(uInt(row), time); msHisCol.priority().put(row, "NORMAL"); msHisCol.origin().put(row, "MSFitsInput::fillHistoryTables"); msHisCol.application().put(row, history.before(' ')); Vector cliComm(1); cliComm[0] = ""; msHisCol.cliCommand().put(row, cliComm); msHisCol.appParams().put(row, cliComm); msHisCol.message().put(row, history.after(' ')); } } } // Extract the data from the PrimaryGroup object and stick it into // the MeasurementSet // keep the arrays of data in memory before dumping them in columns void MSFitsInput::fillMSMainTableColWise(Int& nField, Int& nSpW) { _log << LogOrigin("MSFitsInput", "fillMSMainTable"); // Get access to the MS columns MSColumns& msc(*_msc); const Regex trailing(" *$"); // trailing blanks // get the random group parameter names Int nParams; Int nGroups; nParams = _priGroup.pcount(); nGroups = _priGroup.gcount(); Vector pType(nParams); for (Int i = 0; i < nParams; i++) { pType(i) = _priGroup.ptype(i); pType(i) = pType(i).before(trailing); } Int totRows = nGroups * max(1, _nIF); Int nCorr = _nPixel(getIndex(_coordType, "STOKES")); Int nChan = _nPixel(getIndex(_coordType, "FREQ")); Cube vis(nCorr, nChan, totRows); Matrix sigma(nCorr, totRows); Cube weightSpec(nCorr, nChan, totRows); Matrix weight(nCorr, totRows); const Int nCat = 3; // three initial categories // define the categories Vector cat(nCat); cat(0) = "FLAG_CMD"; cat(1) = "ORIGINAL"; cat(2) = "USER"; msc.flagCategory().rwKeywordSet().define("CATEGORY", cat); Cube flagCat(nCorr, nChan, nCat, False); // Matrix flag = flagCat.xyPlane(0); // references flagCat's storage Cube flag(nCorr, nChan, totRows); // find out the indices for U, V and W, there are several naming schemes Int iU, iV, iW; iU = getIndexContains(pType, "UU"); iV = getIndexContains(pType, "VV"); iW = getIndexContains(pType, "WW"); if (iU < 0 || iV < 0 || iW < 0) { throw(AipsError("MSFitsInput: Cannot find UVW information")); } // get index for baseline Int iBsln = getIndex(pType, "BASELINE"); // get indices for subarray, antenna1 and antenna2 Int iSubarr = getIndex(pType, "SUBARRAY"); Int iAnt1 = getIndex(pType, "ANTENNA1"); Int iAnt2 = getIndex(pType, "ANTENNA2"); // get indices for time Int iTime0 = getIndex(pType, "DATE", 0); Int iTime1 = getIndex(pType, "DATE", 1); // get index for source Int iSource = getIndex(pType, "SOURCE"); if ( iSource == -1 ) { _log << LogOrigin("MSFitsInput", __func__) << LogIO::NORMAL << "SOURCE not found in"; for ( auto p = pType.begin( ); p != pType.end( ); ++p ) _log << " " << *p ; _log << LogIO::POST; } // get index for Freq Int iFreq = getIndex(pType, "FREQSEL"); if ( iFreq == -1 ) { _log << LogOrigin("MSFitsInput", __func__) << LogIO::NORMAL << "FREQSEL not found in"; for ( auto p = pType.begin( ); p != pType.end( ); ++p ) _log << " " << *p ; _log << LogIO::POST; } // get index for Integration time Int iInttim = getIndex(pType, "INTTIM"); _receptorAngle.resize(1); _log << LogIO::NORMAL << "Reading and writing " << nGroups << " visibility groups" << LogIO::POST; Int row = -1; Double interval, exposure; interval = 0.0; exposure = 0.0; Bool discernIntExp(True); Double discernedInt(DBL_MAX); // ProgressMeter meter(0.0, nGroups*1.0, "UVFITS Filler", "Groups copied", "",// "", True, nGroups/100); Matrix uvw(3, totRows); // Remember last-filled values for TSM use Int lastFillArrayId, lastFillFieldId, lastFillScanNumber; lastFillArrayId = -1; lastFillFieldId = -1, lastFillScanNumber = 0; Double lastFillTime = 0; // Keep track of array-specific scanNumbers, FieldIds and FreqIds Vector scanNumber(1); Vector lastFieldId(1), lastFreqId(1); scanNumber = 0; lastFieldId = -1, lastFreqId = -1; // nArray_p was uninitialized so could be HUGE ie causing std::bad_alloc // to be thrown as it is used below for dimensioning, using the construction // nArray_p = max(nArray_p, arrayId+1). In order for this to work you'd better // initialize nArray_p first... _nArray = -1; Bool lastRowFlag = False; Vector ant1(totRows); Vector ant2(totRows); Vector interv(totRows); Vector expos(totRows); Vector datDescId(totRows); _ms.addRow(totRows); Int nif = max(1, _nIF); // Loop over groups for (Int group = 0; group < nGroups; group++) { // Read next group and _priGroup.read(); // Extract time in MJD seconds const Double JDofMJD0 = 2400000.5; Double time = _priGroup.parm(iTime0); time -= JDofMJD0; if (iTime1 >= 0) time += _priGroup.parm(iTime1); time *= C::day; // Extract fqid Int freqId = iFreq > 0 ? Int(_priGroup.parm(iFreq)) : 1; // Extract field Id Int fieldId = 0; if (iSource >= 0) { // make 0-based fieldId = (Int) _priGroup.parm(iSource) - 1; } Int arrayId = 0; std::pair ants; if (iBsln >= 0) { Float baseline = _priGroup.parm(iBsln); ants = _extractAntennas(baseline); arrayId = Int(100.0 * (baseline - Int(baseline) + 0.001)); } else { Int antenna1 = _priGroup.parm(iAnt1); Int antenna2 = _priGroup.parm(iAnt2); ants = _extractAntennas(antenna1, antenna2); arrayId = _priGroup.parm(iSubarr); } _nArray = max(_nArray, arrayId + 1); for (Int k = 0; k < nif; ++k) { Int index = group * nif + k; // Extract uvw uvw(0, index) = _priGroup.parm(iU) * C::c; uvw(1, index) = _priGroup.parm(iV) * C::c; uvw(2, index) = _priGroup.parm(iW) * C::c; // Convert from units of seconds to meters ant1[index] = ants.first; ant2[index] = ants.second; } // Ensure arrayId-specific params are of correct length: if (scanNumber.shape() < _nArray) { scanNumber.resize(_nArray, True); lastFieldId.resize(_nArray, True); lastFreqId.resize(_nArray, True); scanNumber(_nArray - 1) = 0; lastFieldId(_nArray - 1) = -1; lastFreqId(_nArray - 1) = -1; } // Detect new scan (field or freqid change) for each arrayId if (fieldId != lastFieldId(arrayId) || freqId != lastFreqId(arrayId) || time - lastFillTime > 300.0) { scanNumber(arrayId)++; lastFieldId(arrayId) = fieldId; lastFreqId(arrayId) = freqId; } // If integration time is a RP, use it: if (iInttim > -1) { discernIntExp = False; exposure = _priGroup.parm(iInttim); interval = exposure; } else { // keep track of minimum which is the only one // (if time step is larger than UVFITS precision (and zero)) discernIntExp = True; Double tempint; tempint = time - lastFillTime; if (tempint > 0.01) { discernedInt = min(discernedInt, tempint); } } // Work out which axis increments fastests, pol or channel // The COMPLEX axis is assumed to be first, and the IF axis is assumed // to be after STOKES and FREQ. Bool polFastest = (getIndex(_coordType, "STOKES") < getIndex( _coordType, "FREQ")); const Int nx = (polFastest ? nChan : nCorr); const Int ny = (polFastest ? nCorr : nChan); Int count = 0; for (Int ifno = 0; ifno < nif; ifno++) { // IFs go to separate rows in the MS row++; // fill in values for all the unused columns if (row == 0) { msc.feed1().put(row, 0); msc.feed2().put(row, 0); msc.flagRow().put(row, False); lastRowFlag = False; msc.processorId().put(row, -1); msc.observationId().put(row, 0); msc.stateId().put(row, -1); } // Fill scanNumber if changed since last row if (scanNumber(arrayId) != lastFillScanNumber) { msc.scanNumber().put(row, scanNumber(arrayId)); lastFillScanNumber = scanNumber(arrayId); } weight.column(row).set(0.0); // Loop over chans and corrs: for (Int ix = 0; ix < nx; ix++) { for (Int iy = 0; iy < ny; iy++) { const Float visReal = _priGroup(count++); const Float visImag = _priGroup(count++); const Float wt = _priGroup(count++); const Int pol = (polFastest ? _corrIndex[iy] : _corrIndex[ix]); const Int chan = (polFastest ? ix : iy); if (wt <= 0.0) { weightSpec(pol, chan, row) = abs(wt); flag(pol, chan, row) = True; weight(pol, row) += abs(wt); } else { weightSpec(pol, chan, row) = wt; flag(pol, chan, row) = False; // weight column is sum of weight_spectrum (each pol): weight(pol, row) += wt; } vis(pol, chan, row) = Complex(visReal, visImag); } } // calculate sigma (weight = inverse variance) for (Int nc = 0; nc < nCorr; nc++) { if (weight(nc, row) > 0.0) { sigma(nc, row) = sqrt(1.0 / weight(nc, row)); } else { sigma(nc, row) = 0.0; } } if (!discernIntExp) { interv(row) = interval; expos(row) = exposure; } Bool rowFlag = allEQ(flag.xyPlane(row), True); if (rowFlag != lastRowFlag) { msc.flagRow().put(row, rowFlag); lastRowFlag = rowFlag; } if (arrayId != lastFillArrayId) { msc.arrayId().put(row, arrayId); lastFillArrayId = arrayId; } // Always put antenna1 & antenna2 since it is bound to the // aipsStMan and is assumed to change every row // msc.antenna1().put(row,ant1); // msc.antenna2().put(row,ant2); if (time != lastFillTime) { msc.time().put(row, time); msc.timeCentroid().put(row, time); lastFillTime = time; } // determine the spectralWindowId Int spW = ifno; if (iFreq >= 0) { spW = (Int) _priGroup.parm(iFreq) - 1; // make 0-based if (_nIF > 0) { spW *= _nIF; spW += ifno; } } nSpW = max(nSpW, spW + 1); // Always put DDI (SSM) since it might change rapidly // msc.dataDescId().put(row,spW); datDescId[row] = spW; // store the fieldId if (fieldId != lastFillFieldId) { msc.fieldId().put(row, fieldId); nField = max(nField, fieldId + 1); lastFillFieldId = fieldId; } } } // If determining interval on-the-fly, fill interval/exposure columns // now: if (discernIntExp) { discernedInt = floor(100.0 * discernedInt + 0.5) / 100.0; msc.interval().fillColumn(discernedInt); msc.exposure().fillColumn(discernedInt); } else { msc.interval().putColumn(interv); msc.exposure().putColumn(expos); } msc.uvw().putColumn(uvw); msc.antenna1().putColumn(ant1); msc.antenna2().putColumn(ant2); msc.dataDescId().putColumn(datDescId); msc.data().putColumn(vis); msc.weight().putColumn(weight); msc.sigma().putColumn(sigma); msc.weightSpectrum().putColumn(weightSpec); msc.flag().putColumn(flag); // fill the receptorAngle with defaults, just in case there is no AN table _receptorAngle = 0; } // Extract the data from the PrimaryGroup object and stick it into // the MeasurementSet // Doing it row by row void MSFitsInput::fillMSMainTable(Int& nField, Int& nSpW) { _log << LogOrigin("MSFitsInput", "fillMSMainTable"); // Get access to the MS columns MSColumns& msc(*_msc); const Regex trailing(" *$"); // trailing blanks // get the random group parameter names Int nParams; Int nGroups; nParams = _priGroup.pcount(); nGroups = _priGroup.gcount(); Vector pType(nParams); for (Int i = 0; i < nParams; i++) { pType(i) = _priGroup.ptype(i); pType(i) = pType(i).before(trailing); } Int nCorr = _nPixel(getIndex(_coordType, "STOKES")); Int nChan = _nPixel(getIndex(_coordType, "FREQ")); Matrix vis(nCorr, nChan); Vector sigma(nCorr); Matrix weightSpec(nCorr, nChan); Vector weight(nCorr); const Int nCat = 3; // three initial categories // define the categories Vector cat(nCat); cat(0) = "FLAG_CMD"; cat(1) = "ORIGINAL"; cat(2) = "USER"; msc.flagCategory().rwKeywordSet().define("CATEGORY", cat); Cube flagCat(nCorr, nChan, nCat, False); Matrix flag = flagCat.xyPlane(0); // references flagCat's storage // find out the indices for U, V and W, there are several naming schemes Int iU, iV, iW; iU = getIndexContains(pType, "UU"); iV = getIndexContains(pType, "VV"); iW = getIndexContains(pType, "WW"); if (iU < 0 || iV < 0 || iW < 0) { throw(AipsError("MSFitsInput: Cannot find UVW information")); } // get index for baseline Int iBsln = getIndex(pType, "BASELINE"); // get indices for subarray, antenna1 and antenna2 Int iSubarr = getIndex(pType, "SUBARRAY"); Int iAnt1 = getIndex(pType, "ANTENNA1"); Int iAnt2 = getIndex(pType, "ANTENNA2"); // get indices for time Int iTime0 = getIndex(pType, "DATE", 0); Int iTime1 = getIndex(pType, "DATE", 1); // get index for source Int iSource = getIndex(pType, "SOURCE"); if ( iSource == -1 ) { _log << LogOrigin("MSFitsInput", __func__) << LogIO::NORMAL << "SOURCE not found in"; for ( auto p = pType.begin( ); p != pType.end( ); ++p ) _log << " " << *p ; _log << LogIO::POST; } // get index for Freq Int iFreq = getIndex(pType, "FREQSEL"); if ( iFreq == -1 ) { _log << LogOrigin("MSFitsInput", __func__) << LogIO::NORMAL << "FREQSEL not found in"; for ( auto p = pType.begin( ); p != pType.end( ); ++p ) _log << " " << *p ; _log << LogIO::POST; } // get index for Integration time Int iInttim = getIndex(pType, "INTTIM"); _receptorAngle.resize(1); _log << LogIO::NORMAL << "Reading and writing " << nGroups << " visibility groups" << LogIO::POST; Int row = -1; Double interval, exposure; interval = 0.0; exposure = 0.0; Bool discernIntExp(True); Double discernedInt(DBL_MAX); ProgressMeter meter(0.0, nGroups * 1.0, "UVFITS Filler", "Groups copied", "", "", True, nGroups / 100); Vector uvw(3); // Remember last-filled values for TSM use Int lastFillArrayId, lastFillFieldId, lastFillScanNumber; lastFillArrayId = -1; lastFillFieldId = -1, lastFillScanNumber = 0; Double lastFillTime = 0; // Keep track of array-specific scanNumbers, FieldIds and FreqIds Vector scanNumber(1); Vector lastFieldId(1), lastFreqId(1); scanNumber = 0; lastFieldId = -1, lastFreqId = -1; // nArray_p was uninitialized so could be HUGE ie causing std::bad_alloc // to be thrown as it is used below for dimensioning, using the construction // nArray_p = max(nArray_p, arrayId+1). In order for this to work you'd better // initialize nArray_p first... _nArray = -1; Bool lastRowFlag = False; // Loop over groups for (Int group = 0; group < nGroups; group++) { // Read next group and _priGroup.read(); // Extract time in MJD seconds // (this has VERY limited precision [~0.01s]) const Double JDofMJD0 = 2400000.5; Double time = _priGroup.parm(iTime0); time -= JDofMJD0; if (iTime1 >= 0) time += _priGroup.parm(iTime1); time *= C::day; // Extract fqid Int freqId = iFreq > 0 ? Int(_priGroup.parm(iFreq)) : 1; // Extract field Id Int fieldId = 0; if (iSource >= 0) { // make 0-based fieldId = (Int) _priGroup.parm(iSource) - 1; } // Extract uvw uvw(0) = _priGroup.parm(iU); uvw(1) = _priGroup.parm(iV); uvw(2) = _priGroup.parm(iW); // Convert from units of seconds to meters uvw *= C::c; // Extract array/baseline/antenna info Int arrayId = 0; std::pair ants; if (iBsln >= 0) { Float baseline = _priGroup.parm(iBsln); ants = _extractAntennas(baseline); arrayId = Int(100.0 * (baseline - Int(baseline) + 0.001)); } else { Int antenna1 = _priGroup.parm(iAnt1); Int antenna2 = _priGroup.parm(iAnt2); ants = _extractAntennas(antenna1, antenna2); arrayId = _priGroup.parm(iSubarr); } _nArray = max(_nArray, arrayId + 1); Int ant1 = ants.first; Int ant2 = ants.second; // Ensure arrayId-specific params are of correct length: if (scanNumber.shape() < _nArray) { scanNumber.resize(_nArray, True); lastFieldId.resize(_nArray, True); lastFreqId.resize(_nArray, True); scanNumber(_nArray - 1) = 0; lastFieldId(_nArray - 1) = -1; lastFreqId(_nArray - 1) = -1; } // Detect new scan (field or freqid change) for each arrayId if (fieldId != lastFieldId(arrayId) || freqId != lastFreqId(arrayId) || time - lastFillTime > 300.0) { scanNumber(arrayId)++; lastFieldId(arrayId) = fieldId; lastFreqId(arrayId) = freqId; } // If integration time is a RP, use it: if (iInttim > -1) { discernIntExp = False; exposure = _priGroup.parm(iInttim); interval = exposure; } else { // keep track of minimum which is the only one // (if time step is larger than UVFITS precision (and zero)) discernIntExp = True; Double tempint; tempint = time - lastFillTime; if (tempint > 0.01) { discernedInt = min(discernedInt, tempint); } } // Work out which axis increments fastests, pol or channel // The COMPLEX axis is assumed to be first, and the IF axis is assumed // to be after STOKES and FREQ. Bool polFastest = (getIndex(_coordType, "STOKES") < getIndex( _coordType, "FREQ")); const Int nx = (polFastest ? nChan : nCorr); const Int ny = (polFastest ? nCorr : nChan); Int count = 0; for (Int ifno = 0; ifno < max(1, _nIF); ifno++) { // IFs go to separate rows in the MS _ms.addRow(); row++; // fill in values for all the unused columns if (row == 0) { msc.feed1().put(row, 0); msc.feed2().put(row, 0); msc.flagRow().put(row, False); lastRowFlag = False; msc.processorId().put(row, -1); msc.observationId().put(row, 0); msc.stateId().put(row, -1); } // Fill scanNumber if changed since last row if (scanNumber(arrayId) != lastFillScanNumber) { msc.scanNumber().put(row, scanNumber(arrayId)); lastFillScanNumber = scanNumber(arrayId); } weight = 0.0; // Loop over chans and corrs: for (Int ix = 0; ix < nx; ix++) { for (Int iy = 0; iy < ny; iy++) { const Float visReal = _priGroup(count++); const Float visImag = _priGroup(count++); const Float wt = _priGroup(count++); const Int pol = (polFastest ? _corrIndex[iy] : _corrIndex[ix]); const Int chan = (polFastest ? ix : iy); if (wt <= 0.0) { weightSpec(pol, chan) = abs(wt); flag(pol, chan) = True; weight(pol) += abs(wt); } else { weightSpec(pol, chan) = wt; flag(pol, chan) = False; // weight column is sum of weight_spectrum (each pol): weight(pol) += wt; } vis(pol, chan) = Complex(visReal, visImag); } } // calculate sigma (weight = inverse variance) for (Int nc = 0; nc < nCorr; nc++) { if (weight(nc) > 0.0) { sigma(nc) = sqrt(1.0 / weight(nc)); } else { sigma(nc) = 0.0; } } // If available, store interval/exposure if (!discernIntExp) { msc.interval().put(row, interval); msc.exposure().put(row, exposure); } msc.data().put(row, vis); msc.weight().put(row, weight); msc.sigma().put(row, sigma); msc.weightSpectrum().put(row, weightSpec); msc.flag().put(row, flag); msc.flagCategory().put(row, flagCat); Bool rowFlag = allEQ(flag, True); if (rowFlag != lastRowFlag) { msc.flagRow().put(row, rowFlag); lastRowFlag = rowFlag; } if (arrayId != lastFillArrayId) { msc.arrayId().put(row, arrayId); lastFillArrayId = arrayId; } // Always put antenna1 & antenna2 since it is bound to the // aipsStMan and is assumed to change every row msc.antenna1().put(row, ant1); msc.antenna2().put(row, ant2); if (time != lastFillTime) { msc.time().put(row, time); msc.timeCentroid().put(row, time); lastFillTime = time; } msc.uvw().put(row, uvw); // determine the spectralWindowId Int spW = ifno; if (iFreq >= 0) { spW = (Int) _priGroup.parm(iFreq) - 1; // make 0-based if (_nIF > 0) { spW *= _nIF; spW += ifno; } } nSpW = max(nSpW, spW + 1); // Always put DDI (SSM) since it might change rapidly msc.dataDescId().put(row, spW); // store the fieldId if (fieldId != lastFillFieldId) { msc.fieldId().put(row, fieldId); nField = max(nField, fieldId + 1); lastFillFieldId = fieldId; } } meter.update((group + 1) * 1.0); } // If determining interval on-the-fly, fill interval/exposure columns // now: if (discernIntExp) { discernedInt = floor(100.0 * discernedInt + 0.5) / 100.0; msc.interval().fillColumn(discernedInt); msc.exposure().fillColumn(discernedInt); } // fill the receptorAngle with defaults, just in case there is no AN table _receptorAngle = 0; } void MSFitsInput::_fillSysPowerTable(BinaryTable& bt) { static const Regex trailing(" *$"); // trailing blanks const TableRecord btKeywords = bt.getKeywords(); const auto nIF = btKeywords.asInt("NO_IF"); ThrowIf(nIF > 1, "Currently SYSPOWER tables with only a single IF may be imported"); ThrowIf(nIF == 0, "Number of IFs in SY table cannot be 0"); const auto nPol = btKeywords.asInt("NO_POL"); Table syTab = bt.fullTable(); //syTab.tableDesc().show(); const static String name = "SYSPOWER"; const String casaTableName = _ms.tableName() + "/" + name; { // table creation code copied from casa ASDM2MSFiller.cc TableDesc tableDesc; // Key columns. tableDesc.comment() = "System calibration from Cal diode demodulation (EVLA)."; tableDesc.addColumn(ScalarColumnDesc("ANTENNA_ID", "Antenna identifier.")); tableDesc.addColumn(ScalarColumnDesc("FEED_ID", "Feed's index.")); tableDesc.addColumn(ScalarColumnDesc("SPECTRAL_WINDOW_ID", "Spectral window identifier.")); tableDesc.addColumn(ScalarColumnDesc("TIME", "Midpoint of time measurement.")); tableDesc.addColumn(ScalarColumnDesc("INTERVAL", "Interval of measurement.")); // Data columns. tableDesc.addColumn(ArrayColumnDesc("SWITCHED_DIFF", "Switched power difference (cal on - off).")); tableDesc.addColumn(ArrayColumnDesc("SWITCHED_SUM", "Switched power sum (cal on + off).")); tableDesc.addColumn(ArrayColumnDesc("REQUANTIZER_GAIN", "Requantizer gain.")); SetupNewTable tableSetup(casaTableName, tableDesc, Table::New); _ms.rwKeywordSet().defineTable(name, Table(tableSetup)); _ms.rwKeywordSet().asTable(name).flush(); } Table casaTable(casaTableName, Table::Update); casaTable.addRow(syTab.nrow() * nIF); ScalarColumn timeCol(syTab, "TIME"); ScalarColumn timeIntCol(syTab, "TIME INTERVAL"); ScalarColumn antNoCol(syTab, "ANTENNA NO."); ScalarColumn freqIDCol(syTab, "FREQ ID"); if (nIF == 1) { ScalarColumn powerDif1Col(syTab, "POWER DIF1"); ScalarColumn powerSum1Col(syTab, "POWER SUM1"); ScalarColumn postGain1Col(syTab, "POST GAIN1"); ScalarColumn powerDif2Col; ScalarColumn powerSum2Col; ScalarColumn postGain2Col; if (nPol == 2) { powerDif2Col = ScalarColumn(syTab, "POWER DIF2"); powerSum2Col = ScalarColumn(syTab, "POWER SUM2"); postGain2Col = ScalarColumn(syTab, "POST GAIN2"); } _doFillSysPowerSingleIF( casaTableName, timeCol, timeIntCol, antNoCol, freqIDCol, powerDif1Col, powerSum1Col, postGain1Col, powerDif2Col, powerSum2Col, postGain2Col ); } else { ArrayColumn powerDif1Col(syTab, "POWER DIF1"); ArrayColumn powerSum1Col(syTab, "POWER SUM1"); ArrayColumn postGain1Col(syTab, "POST GAIN1"); } } void MSFitsInput::_doFillSysPowerSingleIF( const String& casaTableName, const ScalarColumn& timeCol, const ScalarColumn& intervalCol, const ScalarColumn& antNoCol, const ScalarColumn& freqIDCol, const ScalarColumn& powerDif1Col, const ScalarColumn& powerSum1Col, const ScalarColumn& postGain1Col, const ScalarColumn& powerDif2Col, const ScalarColumn& powerSum2Col, const ScalarColumn& postGain2Col ) { Table casaTable(casaTableName, Table::Update); const auto nrow = timeCol.nrow(); const auto npol = powerDif2Col.nrow() == 0 ? 1 : 2; { ScalarColumn sysPowerAnt(casaTable, "ANTENNA_ID"); const auto antVals = antNoCol.getColumn() - 1; sysPowerAnt.putColumn(antVals); } { ScalarColumn sysPowerFeed(casaTable, "FEED_ID"); const Vector feedVals(nrow, 0); sysPowerFeed.putColumn(feedVals); } { ScalarColumn sysPowerSpw(casaTable, "SPECTRAL_WINDOW_ID"); const auto spwVals = freqIDCol.getColumn() - 1; sysPowerSpw.putColumn(spwVals); } { ScalarColumn sysPowerTime(casaTable, "TIME"); const auto timeVals = timeCol.getColumn() * C::day; sysPowerTime.putColumn(timeVals); } { ScalarColumn sysPowerInterval(casaTable, "INTERVAL"); const auto intervalVals = intervalCol.getColumn() * (float)C::day; sysPowerInterval.putColumn(intervalVals); } { ArrayColumn sysPowerDiff(casaTable, "SWITCHED_DIFF"); Array diffs(IPosition(2, npol, nrow)); for (uInt i=0; i sysPowerSum(casaTable, "SWITCHED_SUM"); Array sums(IPosition(2, npol, nrow)); for (uInt i=0; i sysPowerGain(casaTable, "REQUANTIZER_GAIN"); Array gains(IPosition(2, npol, nrow)); for (uInt i=0; i id(anTab, "NOSTA"); Vector ids = id.getColumn(); std::set sids(ids.begin(), ids.end()); _nAntRow = *std::max_element(sids.begin(), sids.end()); if (_uniqueAnts.empty()) { ThrowIf( _nAntRow < nAnt, "Logic Error: Please submit a defect report and include where we can find your dataset" ); if (_nAntRow > nAnt) { _log << LogOrigin("MSFitsInput", __func__) << LogIO::WARN << _array << " there is at least one gap in the antenna " << "sequence found in the FITS AN table. Empty " << "rows will be inserted into the ANTENNA table " << "representing the gap(s)." << LogIO::POST; } } else { Int nAntVis = _uniqueAnts.size(); ThrowIf( nAntVis > nAnt, "The number of antennas in the visibilities exceeds " "the number of rows in the AN table. Cannot proceed" ); Int maxAntVis = *std::max_element(_uniqueAnts.begin(), _uniqueAnts.end()); ThrowIf( maxAntVis > _nAntRow, "This data set has (1-based) antenna number " + String::toString(maxAntVis) + " in the visibility data, but there is no corresponding " "antenna ID in the FITS AN table. Cannot proceed." ); std::set::const_iterator iter = _uniqueAnts.begin(); std::set::const_iterator end = _uniqueAnts.end(); for (; iter!=end; ++iter) { ThrowIf( std::find(sids.begin(), sids.end(), *iter) == sids.end(), "(1-based) antenna " + String::toString(*iter) + " exists in the visibility data, but there is no " " record which references it in the FITS AN table. " "Cannot proceed" ); } } std::set::const_iterator iter = sids.begin(); std::set::const_iterator end = sids.end(); Int i = 1; for (; iter!=end; ++iter, ++i) { if (*iter != i) { _log << LogOrigin("MSFitsInput", __func__) << LogIO::WARN << _array << " there is at least one gap in the antenna " << "sequence found in the FITS AN table. Empty " << "rows will be inserted into the ANTENNA table " << "representing the gaps." << LogIO::POST; break; } } _receptorAngle.resize(2 * _nAntRow); _receptorAngle = 0.0; Vector arrayXYZ(3); arrayXYZ = 0.0; if ( !btKeywords.isDefined("ARRAYX") || !btKeywords.isDefined("ARRAYY") || !btKeywords.isDefined("ARRAYZ") ) { throw(AipsError("MSFitsInput: Illegal AN file: no antenna positions")); } arrayXYZ(0) = bt.getKeywords().asdouble("ARRAYX"); arrayXYZ(1) = bt.getKeywords().asdouble("ARRAYY"); arrayXYZ(2) = bt.getKeywords().asdouble("ARRAYZ"); static const String xyzHand = "XYZHAND"; String handed; if (bt.getKeywords().isDefined(xyzHand)) { handed = bt.getKeywords().asString(xyzHand); } else { _log << LogOrigin("MSFitsInput", __func__) << LogIO::WARN << xyzHand + " keyword not found in AN table. Will assume " << "antenna coordinate system is right handed." << LogIO::POST; } Bool leftHanded = handed == "LEFT"; if (leftHanded) { _log << LogOrigin("MSFitsInput", __func__) << LogIO::NORMAL << "Antenna positions in the uvfits " << "AN table are in a left handed coordinate system " << "and so will undergo a y -> -y transformation when " << "written to the MS" << LogIO::POST; } // Since we cannot write these quantities, we cannot rely upon // their presence in any UVFITS file that we read: Double rdate = 0.0; String srdate; if (btKeywords.isDefined("RDATE")) { srdate = btKeywords.asString("RDATE"); } Double gst = 0.0; if (btKeywords.isDefined("GSTIA0")) { gst = btKeywords.asdouble("GSTIA0") * C::degree; } Double degpdy = 0.0; if (btKeywords.isDefined("DEGPDY")) { degpdy = btKeywords.asdouble("DEGPDY"); } String timsys = "TAI"; if (btKeywords.isDefined("TIMSYS")) { timsys = btKeywords.asString("TIMSYS"); timsys = timsys.before(trailing); } MVTime timeVal; MEpoch::Types epochRef; FITSDateUtil::fromFITS(timeVal, epochRef, srdate, timsys); // convert to canonical form timsys = MEpoch::showType(epochRef); rdate = timeVal.second(); // MJD seconds // store the time keywords _ms.antenna().rwKeywordSet().define(String("RDATE"), rdate); _ms.antenna().rwKeywordSet().define(String("GSTIA0"), gst); _ms.antenna().rwKeywordSet().define(String("DEGPDY"), degpdy); _ms.antenna().rwKeywordSet().define(String("TIMSYS"), timsys); //save value to set time reference frame elsewhere _timsys = timsys; // Fill in some likely values Float diameter = 25; Bool doSMA = (_array == "SMA"); if (_array == "ATCA") { diameter = 22; } else if (doSMA) { diameter = 6; } else if (_array == "ATA") { diameter = 6.1; } else if (_array == "HATCREEK" || _array == "BIMA") { diameter = 6.1; } else if (_array == "GMRT") { diameter = 45.0; } else if (_array == "IRAM_PDB" || _array == "IRAM PDB") { diameter = 15.0; } MSAntennaColumns& ant(_msc->antenna()); ScalarColumn name(anTab, "ANNAME"); ScalarColumn mountType(anTab, "MNTSTA"); ScalarColumn offset(anTab, "STAXOF"); ScalarColumn polangleA(anTab, "POLAA"); ScalarColumn polangleB(anTab, "POLAB"); ArrayColumn antXYZ(anTab, "STABXYZ"); Vector antDiams(nAnt); antDiams.set(diameter); //If it has a column called DIAMETER ...make use of it if // any of the values are valid Bool positiveDiamsFound = False; if (anTab.tableDesc().isColumn("DIAMETER")) { Vector tmpDiams = ScalarColumn(anTab, "DIAMETER").getColumn(); if (anyGT(tmpDiams, 0.0f)) { antDiams = tmpDiams; positiveDiamsFound = True; } } if (! positiveDiamsFound) { if (_array == "OVRO" || _array == "CARMA") { for (Int i = 0; i < nAnt; ++i) { //Crystal Brogan has guaranteed that it is always this order antDiams[i] = id(i) <= 6 ? 10.4 : 6.1; } } else if (_array == "ALMA") { // CAS-8875, algorithm from Jen Meyer for (Int i = 0; i < nAnt; ++i) { const String& myName = name(i); if (myName.startsWith("CM")) { antDiams[i] = 7.0; } else if ( myName.startsWith("DA") || myName.startsWith("DV") || myName.startsWith("PM") ) { antDiams[i] = 12.0; } else { antDiams[i] = diameter; } } } } // Prepare handling of UVFITS Antenna position coord conventions: // VLA requires rotation of local coords in some cases Bool rotate = False; Matrix posRot = Rot3D(0, 0.0); String arrnam = "Unknown"; if (btKeywords.isDefined("ARRNAM")) { arrnam = btKeywords.asString("ARRNAM"); arrnam.trim(); } // For old VLA archive files, antenna positions are stored in a non-standard // frame and so must be rotated. This is necessary for CASA VLA users, see // eg CAS-11726 // The file is an old VLA archive file if either the FRAME keyword is not // present, or if the array position is approximately the VLA location // (a smaller magnitude position vector indicates a third part db should // be used for the array position, such as the Observatories table). // Usually if it is not the VLA position it will be 0, but we allow // some slop here by only considering position vectors with magnitudes // in excess of 1000km of indicating an old VLA archive file if ( arrnam == "VLA" && ( ! btKeywords.isDefined("FRAME") || norm(arrayXYZ) > 1e6 ) ) { _log << LogOrigin("MSFitsInput", __FUNCTION__) << LogIO::NORMAL << "This looks like an old VLA archive UVFITS file" << LogIO::POST; // Array position for VLA from aips may be wrong, so use // authoritative position from measures (station positions // are from on-line system and are relative to this) MPosition vlaCenter; AlwaysAssert(MeasTable::Observatory(vlaCenter, "VLA"), AipsError); const auto diff = abs(arrayXYZ - vlaCenter.getValue().getValue()); const auto diff2 = sqrt(sum(diff*diff)); _log << LogIO::NORMAL << "UVFITS file telescope position is " << diff2 << " meters from CASA Observatories table VLA position" << LogIO::POST; // give a pretty large tolerance (10km) for uvftis files VLA position // to differ from CASA Observatories VLA position if (diff2 <= 10000.0) { // if the difference between the magnitudes of the array positions // is less than or equal to 10km, we can be certain the file // is an old VLA archive file. arrayXYZ = vlaCenter.getValue().getValue(); // Form rotation around Z axis by VLA longitude=atan(arrayY/arrayX) Double vlaLong = atan2(arrayXYZ(1), arrayXYZ(0)); posRot = Rot3D(2, vlaLong); // Applied to each ant position below rotate = True; _log << LogIO::NORMAL << "Performing transformation of antenna " << "positions from coordinate frame used by MODCOMPs to ITRF" << LogIO::POST; } else { _log << LogIO::WARN << "Array position from UVFITS file is not " << "near that of the position from the Observatories table. " << "No rotation of antenna positions will be performed." << LogIO::POST; } } // add antenna info to table ant.setPositionRef(MPosition::ITRF); _ms.antenna().addRow(_nAntRow); for (Int i = 0; i < _nAntRow; ++i) { // This loop initially flags all rows. // The good rows will be unflagged in the next loop. // Bad rows (representing gaps in the antenna IDs) will remain flagged. ant.flagRow().put(i, True); } for (Int i = 0; i < nAnt; ++i) { Int row = id(i) - 1; ant.dishDiameter().put(row, antDiams(i)); String mount; switch (mountType(i)) { case 0: mount = "ALT-AZ"; break; case 1: mount = "EQUATORIAL"; break; case 3: mount = "X-Y"; break; case 2: mount = "ORBITING"; break; case 4: mount = "ALT-AZ+NASMYTH-R"; break; case 5: mount = "ALT-AZ+NASMYTH-L"; break; case 6: mount = "ALT-AZ+BWG-R"; break; case 7: mount = "ALT-AZ+BWG-L"; break; case 8: mount = "BIZARRE"; break; default: mount = "SPACE_HALCA"; break; } //overwrite mount type for SMA if (doSMA) { mount = "ALT-AZ"; } ant.flagRow().put(row, False); ant.mount().put(row, mount); if (_array == "CARMA" && _newNameStyle) { ostringstream oss; oss << "CA" << id(i); ant.name().put(row, oss.str()); } else if (_array == "EVLA" && _newNameStyle) { ostringstream oss; oss << "EA" << setw(2) << setfill('0') << id(i); ant.name().put(row, oss.str()); } else if (_array == "VLA" && _newNameStyle) { ostringstream oss; oss << "VA" << setw(2) << setfill('0') << id(i); //cerr << name(i) << endl; ant.name().put(row, oss.str()); } else { ant.name().put(row, String::toString(id(i))); } Vector offsets(3); offsets = 0.; offsets(0) = offset(i); ant.offset().put(row, offsets); ant.station().put(row, name(i).before('\0')); ant.type().put(row, "GROUND-BASED"); // Do UVFITS-dependent position corrections: // ArrayColumn antXYZ(i) may need coord transform; do it in corXYZ: Vector corXYZ = antXYZ(i); if (rotate) { corXYZ = product(posRot, corXYZ); } if (leftHanded) { corXYZ(1) = -corXYZ(1); } ant.position().put(row, arrayXYZ + corXYZ); // store the angle for use in the feed table _receptorAngle(2 * i + 0) = polangleA(i) * C::degree; _receptorAngle(2 * i + 1) = polangleB(i) * C::degree; } // store these items in non-standard keywords for now ant.name().rwKeywordSet().define("ARRAY_NAME", arrnam); ant.position().rwKeywordSet().define("ARRAY_POSITION", arrayXYZ); } void MSFitsInput::fillSpectralWindowTable(BinaryTable& bt, Int nSpW) { MSSpWindowColumns& msSpW(_msc->spectralWindow()); MSDataDescColumns& msDD(_msc->dataDescription()); MSPolarizationColumns& msPol(_msc->polarization()); Int iFreq = getIndex(_coordType, "FREQ"); Int nChan = _nPixel(iFreq); Int nCorr = _nPixel(getIndex(_coordType,"STOKES")); // assume spectral line, make source table to allow restfreq to be entered //if (nChan>33) addSourceTable_p=True; if (nChan>0) _addSourceTable=True; // fill out the polarization info (only single entry allowed in fits input) _ms.polarization().addRow(); msPol.numCorr().put(0,nCorr); msPol.corrType().put(0,_corrType); msPol.corrProduct().put(0,_corrProduct); msPol.flagRow().put(0,False); // Table fqTab=bt.fullTable("",Table::Scratch); Table fqTab=bt.fullTable(); Int nRow=fqTab.nrow(); ScalarColumn colFrqSel(fqTab,"FRQSEL"); Matrix ifFreq(_nIF,nRow); Matrix chWidth(_nIF,nRow); Matrix totalBandwidth(_nIF,nRow); // The type of the column changes according to the number of entries if (_nIF==1) { ScalarColumn colIFFreq(fqTab,"IF FREQ"); ScalarColumn colChWidth(fqTab,"CH WIDTH"); ScalarColumn colTotalBandwidth(fqTab,"TOTAL BANDWIDTH"); for (Int i=0; i colIFFreq(fqTab,"IF FREQ"); ArrayColumn colChWidth(fqTab,"CH WIDTH"); ArrayColumn colTotalBandwidth(fqTab,"TOTAL BANDWIDTH"); try{ colIFFreq.getColumn(ifFreq); colChWidth.getColumn(chWidth); colTotalBandwidth.getColumn(totalBandwidth); }catch(std::exception& x) { _log << LogOrigin("MSFitsInput", "fillSpectralWindowTable") << LogIO::DEBUG1 << x.what() << LogIO::POST; } catch(...) { _log << LogOrigin("MSFitsInput", "fillSpectralWindowTable") << LogIO::DEBUG1 << "unknown Error" << LogIO::POST; } } for (Int spw=0; spw0) { ifc=spw%_nIF; freqGroup = spw/_nIF; } Int fqRow=spw/max(1,_nIF); if (fqRow != colFrqSel(fqRow)-1) _log << LogIO::SEVERE << "Trouble interpreting FQ table, id's may be wrong" << LogIO::POST; msSpW.name().put(spw,"none"); msSpW.ifConvChain().put(spw,ifc); msSpW.numChan().put(spw,nChan); Double refChan = _refPix(iFreq); // using data from FQ table Double refFreq=_refVal(iFreq)+ifFreq(ifc,fqRow); Double chanBandwidth=chWidth(ifc,fqRow); //TT debug Vector chanFreq(nChan),resolution(nChan); for (Int i=0; i < nChan; i++) { chanFreq(i)= refFreq + (i+1-refChan) * chanBandwidth; } resolution=abs(chanBandwidth); //if altrval (and altrpix) fits keywords exist use //recalucalated values instead of the data form FQ table if (_useAltrval) { refFreq = _refFreq; chanFreq = _chanFreq; } msSpW.chanFreq().put(spw,chanFreq); msSpW.chanWidth().put(spw,resolution); msSpW.effectiveBW().put(spw,resolution); msSpW.refFrequency().put(spw,refFreq); msSpW.resolution().put(spw,resolution); msSpW.totalBandwidth().put(spw,totalBandwidth(ifc,fqRow)); if (chanBandwidth>0) { msSpW.netSideband().put(spw,1); } else { msSpW.netSideband().put(spw,-1); } msSpW.freqGroup().put(spw,freqGroup); msSpW.freqGroupName().put(spw,"none"); msSpW.flagRow().put(spw,False); // set the reference frames for frequency msSpW.measFreqRef().put(spw,_freqsys); } } void MSFitsInput::fillSpectralWindowTable() { MSSpWindowColumns& msSpW(_msc->spectralWindow()); MSDataDescColumns& msDD(_msc->dataDescription()); MSPolarizationColumns& msPol(_msc->polarization()); Int iFreq = getIndex(_coordType, "FREQ"); Int nChan = _nPixel(iFreq); Int nCorr = _nPixel(getIndex(_coordType,"STOKES")); // assume spectral line, make source table to allow restfreq to be entered //if (nChan>33) addSourceTable_p=True; if (nChan>0) _addSourceTable=True; // fill out the polarization info (only single entry allowed in fits input) _ms.polarization().addRow(); msPol.numCorr().put(0,nCorr); msPol.corrType().put(0,_corrType); msPol.corrProduct().put(0,_corrProduct); msPol.flagRow().put(0,False); Int spw=0; _ms.spectralWindow().addRow(); _ms.dataDescription().addRow(); msDD.spectralWindowId().put(spw,spw); msDD.polarizationId().put(spw,0); msDD.flagRow().put(spw,False); msSpW.name().put(spw,"none"); msSpW.ifConvChain().put(spw,0); msSpW.numChan().put(spw,nChan); Double refChan = _refPix(iFreq); Double refFreq=_refVal(iFreq); Double chanBandwidth=_delta(iFreq); Vector chanFreq(nChan),resolution(nChan); for (Int i=0; i < nChan; i++) { chanFreq(i)= refFreq + (i+1-refChan) * chanBandwidth; } resolution=chanBandwidth; //if altrval (and altrpix) fits keywords exist use //recalucalated values if (_useAltrval) { refFreq = _refFreq; chanFreq = _chanFreq; } msSpW.chanFreq().put(spw,chanFreq); msSpW.chanWidth().put(spw,resolution); msSpW.effectiveBW().put(spw,resolution); msSpW.refFrequency().put(spw,refFreq); msSpW.resolution().put(spw,resolution); msSpW.totalBandwidth().put(spw,abs(nChan*chanBandwidth)); if (chanBandwidth>0) { msSpW.netSideband().put(spw,1); } else { msSpW.netSideband().put(spw,-1); } msSpW.freqGroup().put(spw,0); msSpW.freqGroupName().put(spw,"none"); msSpW.flagRow().put(spw,False); // set the reference frames for frequency msSpW.measFreqRef().put(spw,_freqsys); } // Returns the Direction Measure reference for UVW and other appropriate columns // in _msc (which must exist but have empty columns before you can set it!). MDirection::Types MSFitsInput::getDirectionFrame(Double epoch) { MDirection::Types epochRef = MDirection::J2000; if (nearAbs(epoch, 1950.0, 0.01)) epochRef = _array == "VLA" ? MDirection::B1950_VLA : MDirection::B1950; _log << LogOrigin("MSFitsInput", "getDirectionFrame") << LogIO::DEBUG1 << "epochRef ok " << LogIO::POST; return epochRef; } void MSFitsInput::fillFieldTable(BinaryTable& bt, Int nField) { MSFieldColumns& msField(_msc->field()); TableRecord btKeywords = bt.getKeywords(); if (!btKeywords.isDefined("NO_IF")) { throw(AipsError("MSFitsInput: Illegal SU file: no number of IFs")); } uInt noif = bt.getKeywords().asuInt("NO_IF"); // Table suTab=bt.fullTable("",Table::Scratch); Table suTab = bt.fullTable(); ScalarColumn id(suTab, "ID. NO."); ScalarColumn name(suTab, "SOURCE"); ScalarColumn qual(suTab, "QUAL"); Bool multiqual = False; Int minqual, maxqual; minMax(minqual, maxqual, qual.getColumn()); if (minqual != maxqual) multiqual = True; ScalarColumn code(suTab, "CALCODE"); // ScalarColumn iflux(suTab,"IFLUX"); // etc Q, U, V (Jy) ScalarColumn ra(suTab, "RAEPO"); //degrees ScalarColumn dec(suTab, "DECEPO"); //degrees ScalarColumn raapp(suTab, "RAAPP"); //degrees ScalarColumn decapp(suTab, "DECAPP"); //degrees ScalarColumn epoch(suTab, "EPOCH"); //years ScalarColumn pmra(suTab, "PMRA"); //deg/day ScalarColumn pmdec(suTab, "PMDEC"); //deg/day if (Int(suTab.nrow()) < nField) { _log << LogOrigin("MSFitsInput", __func__) << LogIO::NORMAL << "Input Source id's not sequential, adding empty rows in output" << LogIO::POST; } Int outRow = -1; // RESTFREQ and LSRVEL are 2D columns according to the AIPS Memo 117 //restFreq_p.resize(noif, suTab.nrow()); _sysVel.resize(noif, suTab.nrow()); Bool throwImmediately = False; try { ArrayColumn restfreq(suTab,"RESTFREQ"); // Hz ArrayColumn sysvel(suTab,"LSRVEL"); // m/s restfreq.getColumn(_restFreq); // purposeful assignment of throwImmediately // because it appears that the sense of rows and columns are reversed here uInt nrestfreqs = _restFreq.nrow(); throwImmediately = nrestfreqs != noif; ThrowIf( throwImmediately, "Inconsistent SU table, number of elements in rest frequency column is " + String::toString(nrestfreqs) + " but number of IFs is " + String::toString(noif) ); sysvel.getColumn(_sysVel); } catch (const std::exception& x) { ThrowIf(throwImmediately, x.what()); if(noif>1){ _log << LogOrigin("MSFitsInput", __func__) << LogIO::WARN << x.what() << ": " << "Inconsistent setup of RESTFREQ and LSRVEL columns." << endl << "With NO_IF>1, they should be arrays not scalars." << LogIO::POST; } _restFreq.resize(noif, suTab.nrow()); ScalarColumn restfreq(suTab,"RESTFREQ"); // Hz ScalarColumn sysvel(suTab,"LSRVEL"); // m/s Vector tmprf(suTab.nrow()); Vector tmpsv(suTab.nrow()); restfreq.getColumn(tmprf); sysvel.getColumn(tmpsv); for(uInt ii=0; ii outRow) { // Append a flagged, empty row to the FIELD table _ms.field().addRow(); outRow++; Vector nullDir(1); nullDir(0).set(MVDirection(0.0, 0.0), MDirection::Ref(epochRefZero)); msField.phaseDirMeasCol().put(outRow, nullDir); msField.delayDirMeasCol().put(outRow, nullDir); msField.referenceDirMeasCol().put(outRow, nullDir); msField.flagRow().put(outRow, True); } msField.sourceId().put(fld, -1); // source table not filled in msField.code().put(fld, code(inRow)); String theFldName; if (multiqual) theFldName = name(inRow) + "_" + String::toString(qual(inRow)); else theFldName = name(inRow); msField.name().put(fld, theFldName); Int numPoly = 0; if (!nearAbs(pmra(inRow), 0.0) || !nearAbs(pmdec(inRow), 0.0)) { numPoly = 1; } // The code below will write the direction in B1950 or J2000 coordinates if // the direction is constant. However it will use apparent Coordinates (I // am not sure if this means APP, JTRUE, BTRUE or what), if the proper // motion is non-zero. If the epoch in the incoming SU // table is "-1" (via AIPS UVFITS, a planet tracked by the correlator), it // will adopt the epochRefZero and use the ra/dec (not raapp/decapp). // The handling of planets should be cleaned up (defect 3636). // In all cases the time will be the date of the start of the observation. MDirection::Types epochRef = MDirection::APP; MVDirection refDir; if (numPoly == 0) { if (near(epoch(inRow), 2000.0, 0.01)) { epochRef = MDirection::J2000; } else if (nearAbs(epoch(inRow), 1950.0, 0.01)) { if (_array == "VLA") epochRef = MDirection::B1950_VLA; else epochRef = MDirection::B1950; } else if (epoch(inRow) == -1.0) { epochRef = epochRefZero; _log << LogOrigin("MSFitsInput", __func__) << " Assuming standard epoch " << " for " << name(inRow) << ". Be aware that this may not be correct." << endl; } else { _log << LogOrigin("MSFitsInput", __func__) << " Cannot handle epoch in SU table: " << epoch(inRow) << LogIO::EXCEPTION; } refDir = MVDirection(ra(inRow) * C::degree, dec(inRow) * C::degree); } else { refDir = MVDirection(raapp(inRow) * C::degree, decapp(inRow) * C::degree); } Vector radecMeas(numPoly + 1); radecMeas(0).set(refDir, MDirection::Ref(epochRef)); if (numPoly == 1) { radecMeas(1).set(MVDirection(pmra(inRow) * C::degree / C::day, pmdec(inRow) * C::degree / C::day), MDirection::Ref( epochRef)); } // Get the time from the observation subtable. I have assumed that this bit // of the observation table has been filled by now. const Vector obsTimes = _msc->observation().timeRange()(0); msField.time().put(fld, obsTimes(0)); msField.numPoly().put(fld, numPoly); msField.delayDirMeasCol().put(fld, radecMeas); msField.phaseDirMeasCol().put(fld, radecMeas); msField.referenceDirMeasCol().put(fld, radecMeas); msField.flagRow().put(fld, False); } } // single source fits case void MSFitsInput::fillFieldTable(Int nField) { // some UVFITS files have the source number set, but have no SU // table. We will assume there is only a single source in that case // and set all fieldId's back to zero if (nField > 1) { _msc->fieldId().fillColumn(0); } MSFieldColumns& msField(_msc->field()); _ms.field().addRow(); Int fld = 0; msField.sourceId().put(fld, -1); // source table not used msField.code().put(fld, " "); msField.name().put(fld, _object); Vector radecMeas(1); radecMeas(0).set(MVDirection(_refVal(getIndex(_coordType, "RA")) * C::degree, _refVal(getIndex(_coordType, "DEC")) * C::degree), MDirection::Ref(_epochRef)); msField.numPoly().put(fld, 0); msField.delayDirMeasCol().put(fld, radecMeas); msField.phaseDirMeasCol().put(fld, radecMeas); msField.referenceDirMeasCol().put(fld, radecMeas); // Use TIME_RANGE in OBSERVATION table to set TIME here. const Vector obsTimes = _msc->observation().timeRange()(0); msField.time().put(fld, obsTimes(0)); } void MSFitsInput::fillFeedTable() { MSFeedColumns& msfc(_msc->feed()); // find out the POLARIZATION_TYPE // In the fits files we handle there can be only a single, uniform type // of polarization so the following should work. MSPolarizationColumns& msPolC(_msc->polarization()); Int numCorr = msPolC.numCorr()(0); Vector rec_type(2); rec_type = "?"; if (_corrType(0) >= Stokes::RR && _corrType(numCorr - 1) <= Stokes::LL) { rec_type(0) = "R"; rec_type(1) = "L"; } if (_corrType(0) >= Stokes::XX && _corrType(numCorr - 1) <= Stokes::YY) { rec_type(0) = "X"; rec_type(1) = "Y"; } Matrix polResponse(2, 2); polResponse = 0.; polResponse(0, 0) = polResponse(1, 1) = 1.; Matrix offset(2, 2); offset = 0.; Vector position(3); position = 0.; // fill the feed table Int row = -1; // Use TIME_RANGE in OBSERVATION table to set TIME here. const Vector obsTimes = _msc->observation().timeRange()(0); // nAnt as here ensures ANTENNA and FEED have the same number // of rows since some ants may not be present in the visibility data Int nAnt = _msc->antenna().nrow(); for (Int ant = 0; ant < nAnt; ++ant) { _ms.feed().addRow(); row++; msfc.antennaId().put(row, ant); msfc.beamId().put(row, -1); msfc.feedId().put(row, 0); msfc.interval().put(row, 0); // msfc.phasedFeedId().put(row,-1); msfc.spectralWindowId().put(row, -1); // all msfc.time().put(row, obsTimes(0)); msfc.numReceptors().put(row, 2); msfc.beamOffset().put(row, offset); msfc.polarizationType().put(row, rec_type); msfc.polResponse().put(row, polResponse); msfc.position().put(row, position); msfc.receptorAngle().put(row, _receptorAngle(Slice(2 * ant, 2))); } } void MSFitsInput::fillExtraTables() { // fill the pointing table and possibly the source table // run though the main table, find field changes, and add pointing rows // as needed by looking up the field info in the field table // If requested also look for new spectralwindows and add source // table entries for each field/spw combination if (_addSourceTable) _log << LogOrigin("MSFitsInput", __func__) << LogIO::NORMAL << "Filling SOURCE table (this may take some time)." << LogIO::POST; Int nrow = _ms.nrow(); Int nAnt = _ms.antenna().nrow(); Int lastFieldId = -1; Int lastDDId = -1; Double lastTime = 0; Vector fieldId = _msc->fieldId().getColumn(); Vector ddId; if (_addSourceTable){ ddId = _msc->dataDescId().getColumn(); } std::map, Int> sourceFieldIndex; // for the case we need to write the source table ProgressMeter meter(0.0, nrow * 1.0, "UVFITS Filler", "rows copied", "", "", True, nrow / 100); for (Int i = 0; i < nrow; i++) { if (fieldId(i) != lastFieldId || (_addSourceTable && ddId(i) != lastDDId)) { lastFieldId = fieldId(i); if (i > 0) lastTime = _msc->time()(i - 1); Array pointingDir = _msc->field().phaseDir()(lastFieldId); String name = _msc->field().name()(lastFieldId); //Int numPoly = _msc->field().numPoly()(lastFieldId); Double time = _msc->time()(i); Int np = _ms.pointing().nrow(); if (np > 0) { // fix up time and interval for previous entries Double midTime = (lastTime + _msc->pointing().time()(np - 1)) / 2; Double interval = lastTime - _msc->pointing().time()(np - 1) + _msc->interval()(i - 1); for (Int j = 0; j < nAnt; j++) { _msc->pointing().time().put(np - j - 1, midTime); _msc->pointing().timeOrigin().put(np - j - 1, midTime); _msc->pointing().interval().put(np - j - 1, interval); } } /* This is not right for concatenating later for mosaicing As it is a useless piece of info copy from Field...field table will do // The ISMStMan is used for all but antennaId, so only put once for (Int j=0; jpointing().antennaId().put(np+j, j); if (j==0) { _msc->pointing().time().put(np+j,time); _msc->pointing().timeOrigin().put(np+j,time); _msc->pointing().interval().put(np+j,0); _msc->pointing().name().put(np+j, name); _msc->pointing().numPoly().put(np+j, numPoly); _msc->pointing().direction().put(np+j,pointingDir); _msc->pointing().target().put(np+j,pointingDir); _msc->pointing().tracking().put(np+j,True); } } */ if (_addSourceTable) { lastDDId = ddId(i); Int spwId = _msc->dataDescription().spectralWindowId()(lastDDId); // now check if we've seen this field for this spectral window // Use indexed access to the SOURCE sub-table pair myfldspw = make_pair(lastFieldId, spwId); if(sourceFieldIndex.find(myfldspw) == sourceFieldIndex.end()){ sourceFieldIndex.insert(std::make_pair(myfldspw, 1)); _ms.source().addRow(); Int j = _ms.source().nrow() - 1; MSSourceColumns & mss = _msc->source(); mss.sourceId().put(j, lastFieldId); _msc->field().sourceId().put(lastFieldId, lastFieldId); mss.name().put(j, name); Matrix phaseDir = _msc->field().phaseDir()( lastFieldId); Vector srcDir = phaseDir.column(0), rate(2); if (phaseDir.ncolumn() > 1) rate = phaseDir.column(1); else rate = 0.0; mss.direction().put(j, srcDir); mss.properMotion().put(j, rate); mss.time().put(j, time); mss.interval().put(j, DBL_MAX); mss.spectralWindowId().put(j, spwId); Vector sysVel(1); // sysVel was extracted from LSRVEL in SU table if(0<=lastFieldId && (uInt)lastFieldId<_sysVel.ncolumn()){ sysVel(0) = _sysVel(0, lastFieldId); } else{ _log << LogOrigin("MSFitsInput", "fillExtraTable") << LogIO::WARN << "No systemic velocity for field " << lastFieldId << LogIO::POST; sysVel(0) = 0.; } mss.sysvel().put(j, sysVel); mss.numLines().put(j, 1); Vector transition(1); transition(0) = ""; mss.transition().put(j, transition); Vector restFreqs(1); if(0<=lastFieldId && (uInt)lastFieldId<_restFreq.ncolumn()){ restFreqs(0) = _restFreq(0, lastFieldId); } else{ _log << LogOrigin("MSFitsInput", "fillExtraTable") << LogIO::WARN << "No rest frequency for field " << lastFieldId << LogIO::POST; restFreqs(0) = 0.; } mss.restFrequency().put(j, restFreqs); mss.calibrationGroup().put(j, -1); // sourceModel is left as is (we have no model information to fill in) } } } meter.update((i + 1) * 1.0); } // fix up last interval lastTime = _msc->time()(nrow - 1); Int np = _ms.pointing().nrow(); if (np > 0) { // fix up time and interval for previous entries Double midTime = (lastTime + _msc->pointing().time()(np - 1)) / 2; Double interval = lastTime - _msc->pointing().time()(np - 1) + _msc->interval()(nrow - 1); for (Int j = 0; j < nAnt; j++) { _msc->pointing().time().put(np - j - 1, midTime); _msc->pointing().timeOrigin().put(np - j - 1, midTime); _msc->pointing().interval().put(np - j - 1, interval); } } } void MSFitsInput::fixEpochReferences() { if (_timsys == "IAT") _timsys = "TAI"; if (_timsys == "UTC" || _timsys == "TAI") { if (_timsys == "UTC") _msc->setEpochRef(MEpoch::UTC, False); if (_timsys == "TAI") _msc->setEpochRef(MEpoch::TAI, False); } else { if (_timsys != "") _log << LogOrigin("MSFitsInput", "fixEpochReferences") << LogIO::SEVERE << "Unhandled time reference frame: " << _timsys << LogIO::POST; } } void MSFitsInput::setFreqFrameVar(BinaryTable& binTab) { ConstFitsKeywordList kwlist = binTab.kwlist(); const FitsKeyword* kw; kwlist.first(); String frame; while ((kw = kwlist.next())) { String kwname = kw->name(); if (kwname == "VELTYP") { frame = kw->asString(); } } if (frame.contains("LSR")) { _freqsys = MFrequency::LSRK; // because some smart people use only LSR if (frame.contains("LSRD")) // in uvfits ! _freqsys = MFrequency::LSRD; } else if (frame.contains("REST")) { _freqsys = MFrequency::REST; } else if (frame.contains("BARY")) { _freqsys = MFrequency::BARY; } else if (frame.contains("GEO")) { _freqsys = MFrequency::GEO; } else if (frame.contains("TOPO")) { _freqsys = MFrequency::TOPO; } else if (frame.contains("GALAC")) { _freqsys = MFrequency::GALACTO; } else if (frame.contains("LOCAL") || frame.contains("LGROUP")) { _freqsys = MFrequency::LGROUP; } else if (frame.contains("CMB")) { _freqsys = MFrequency::CMB; } } void MSFitsInput::updateSpectralWindowTable() { MSSpWindowColumns& msSpW(_msc->spectralWindow()); msSpW.measFreqRef().fillColumn(_freqsys); } void MSFitsInput::checkRequiredAxis() { // Check if required axes are there if (getIndex(_coordType, "COMPLEX") < 0) { _log << "Data does not have a COMPLEX axis" << LogIO::EXCEPTION; } if (getIndex(_coordType, "STOKES") < 0) { _log << "Data does not have a STOKES axis" << LogIO::EXCEPTION; } if (getIndex(_coordType, "FREQ") < 0) { _log << "Data does not have a FREQ axis" << LogIO::EXCEPTION; } if ((getIndex(_coordType, "RA") < 0) && (getIndex(_coordType, "RA---SIN") < 0) && (getIndex(_coordType, "RA---NCP") < 0) && (getIndex( _coordType, "RA---SCP") < 0)) { _log << "Data does not have a RA axis" << LogIO::EXCEPTION; } if ((getIndex(_coordType, "DEC") < 0) && (getIndex(_coordType, "DEC--SIN") < 0) && (getIndex( _coordType, "DEC--NCP") < 0) && (getIndex(_coordType, "DEC--SCP") < 0)) { _log << "Data does not have a DEC axis" << LogIO::EXCEPTION; } } void MSFitsInput::getAxisInfo(ConstFitsKeywordList& kwl) { // Extracts the axis related info. from the UV table keyword list and // saves them in the arrays. kwl.first(); const Regex trailing(" *$"); const FitsKeyword *kw; String table = (kw = kwl(FITS::EXTNAME)) ? kw->asString() : ""; if (!table.contains("UV")) { _log << "This is not a uv table!" << LogIO::EXCEPTION; } const Int nAxis = kwl(FITS::TFIELDS)->asInt(); if (nAxis < 1) { _log << "Data has no axes!" << LogIO::EXCEPTION; } String tdim = kwl("TDIM")->asString(); IPosition ipos; FITSKeywordUtil::fromTDIM(ipos, tdim); uInt shp =ipos.nelements(); _nPixel.resize(shp); _refVal.resize(shp); _refPix.resize(shp); _delta.resize(shp); _coordType.resize(shp); for (uInt i = 0; i < shp; i++) { _nPixel(i) = ipos(i); if (_nPixel(i) < 0) { _log << "Axes " << i << " cannot have a negative value" << LogIO::EXCEPTION; } const char* tmp; tmp = (String::toString(i + 1).append("CTYP").append(String::toString(nAxis))).chars(); _coordType(i) = String(kwl(tmp)->asString()).before(trailing); tmp = (String::toString(i + 1).append("CRVL").append(String::toString(nAxis))).chars(); _refVal(i) = kwl(tmp)->asDouble(); tmp = (String::toString(i + 1).append("CRPX").append(String::toString(nAxis))).chars(); _refPix(i) = kwl(tmp)->asDouble(); tmp = (String::toString(i + 1).append("CDLT").append(String::toString(nAxis))).chars(); _delta(i) = kwl(tmp)->asDouble(); //tmp = (String::toString(i + 1).append("CROT").append(String::toString(nAxis))).chars(); //cRot_p(i) = kwl(tmp)->asDouble(); } _log << LogOrigin("MSFitsInput", "fillMSMainTable") << LogIO::DEBUG1 << "coordType=" << _coordType << "\nrefVal=" << _refVal << "\nrefPix=" << _refPix << "\ndelta=" << _delta << "\n_nPixel=" << _nPixel << LogIO::POST; } void MSFitsInput::sortPolarizations() { // Sort out the order of the polarizations and find the sort indices // to put them in 'standard' order: PP,PQ,QP,QQ const uInt iPol = getIndex(_coordType, "STOKES"); const uInt numCorr = _nPixel(iPol); _corrType.resize(numCorr); for (uInt i = 0; i < numCorr; i++) { // note: 1-based ref pix _corrType(i) = ifloor(_refVal(iPol) + (i + 1 - _refPix(iPol)) * _delta(iPol) + 0.5); // convert AIPS-convention Stokes description to Casacore enum switch (_corrType(i)) { case -8: _corrType(i) = Stokes::YX; break; case -7: _corrType(i) = Stokes::XY; break; case -6: _corrType(i) = Stokes::YY; break; case -5: _corrType(i) = Stokes::XX; break; case -4: _corrType(i) = Stokes::LR; break; case -3: _corrType(i) = Stokes::RL; break; case -2: _corrType(i) = Stokes::LL; break; case -1: _corrType(i) = Stokes::RR; break; default: if (_corrType(i) < 0) { _log << "Unknown Correlation type: " << _corrType(i) << LogIO::EXCEPTION; } } } Vector tmp(_corrType.copy()); // Sort the polarizations to standard order. Could probably use // GenSortIndirect here. GenSort::sort(_corrType); _corrIndex.resize(numCorr); // Get the sort indices to rearrange the data to standard order for (uInt i = 0; i < numCorr; i++) { for (uInt j = 0; j < numCorr; j++) { if (_corrType(j) == tmp(i)) _corrIndex[i] = j; } } // Figure out the correlation products from the polarizations _corrProduct.resize(2, numCorr); _corrProduct = 0; for (uInt i = 0; i < numCorr; i++) { const Stokes::StokesTypes cType = Stokes::type(_corrType(i)); Fallible receptor = Stokes::receptor1(cType); Bool warn = False; if (receptor.isValid()) { _corrProduct(0, i) = receptor; } else if (!warn) { warn = True; _log << LogIO::WARN << "Cannot deduce receptor 1 for correlations of type: " << Stokes::name(cType) << LogIO::POST; } receptor = Stokes::receptor2(cType); if (receptor.isValid()) { _corrProduct(1, i) = receptor; } else if (!warn) { warn = True; _log << LogIO::WARN << "Cannot deduce receptor 2 for correlations of type: " << Stokes::name(cType) << LogIO::POST; } } } void MSFitsInput::fillPolarizationTable() { MSPolarizationColumns& msPol(_msc->polarization()); Int nCorr = _nPixel(getIndex(_coordType, "STOKES")); // fill out the polarization info (only single entry allowed in fits input) _ms.polarization().addRow(); msPol.numCorr().put(0, nCorr); msPol.corrType().put(0, _corrType); msPol.corrProduct().put(0, _corrProduct); msPol.flagRow().put(0, False); } void MSFitsInput::fillSpectralWindowTable(BinaryTable& bt) { MSSpWindowColumns& msSpW(_msc->spectralWindow()); MSDataDescColumns& msDD(_msc->dataDescription()); const Regex trailing(" *$"); ConstFitsKeywordList kwl = bt.kwlist(); const FitsKeyword* kw; kwl.first(); Int nIF = (kw = kwl("NO_IF")) ? kw->asInt() : 1; _nIF = nIF; Table fqTab = bt.fullTable(); Int nRow = fqTab.nrow(); ScalarColumn colFrqSel(fqTab, "FRQSEL"); Matrix ifFreq(_nIF, nRow); Matrix chWidth(_nIF, nRow); Matrix totalBandwidth(_nIF, nRow); // The type of the column changes according to the number of entries if (_nIF == 1) { ScalarColumn colIFFreq(fqTab, "IF FREQ"); ScalarColumn colChWidth(fqTab, "CH WIDTH"); ScalarColumn colTotalBandwidth(fqTab, "TOTAL BANDWIDTH"); for (Int i = 0; i < nRow; i++) { ifFreq(0, i) = colIFFreq(i); chWidth(0, i) = colChWidth(i); totalBandwidth(0, i) = colTotalBandwidth(i); } } else { ArrayColumn colIFFreq(fqTab, "IF FREQ"); ArrayColumn colChWidth(fqTab, "CH WIDTH"); ArrayColumn colTotalBandwidth(fqTab, "TOTAL BANDWIDTH"); colIFFreq.getColumn(ifFreq); colChWidth.getColumn(chWidth); colTotalBandwidth.getColumn(totalBandwidth); } Int nSpW = nIF; Int iFreq = getIndex(_coordType, "FREQ"); Int nChan = _nPixel(iFreq); if (nChan > 0) _addSourceTable = True; for (Int spw = 0; spw < nSpW; spw++) { _ms.spectralWindow().addRow(); _ms.dataDescription().addRow(); msDD.spectralWindowId().put(spw, spw); msDD.polarizationId().put(spw, 0); msDD.flagRow().put(spw, False); Int ifc = 0; Int freqGroup = 0; if (_nIF > 0) { ifc = spw % _nIF; freqGroup = spw / _nIF; } Int fqRow = spw / max(1, _nIF); if (fqRow != colFrqSel(fqRow) - 1) _log << LogOrigin("MSFitsInput", "fillSpectralWindowTable") << LogIO::SEVERE << "Trouble interpreting FQ table, id's may be wrong" << LogIO::POST; msSpW.name().put(spw, "none"); msSpW.ifConvChain().put(spw, ifc); msSpW.numChan().put(spw, nChan); Double refChan = _refPix(iFreq); // using data from FQ table Double refFreq = _refVal(iFreq) + ifFreq(ifc, fqRow); Double chanBandwidth = chWidth(ifc, fqRow); Vector chanFreq(nChan), resolution(nChan); for (Int i = 0; i < nChan; i++) { chanFreq(i) = refFreq + (i + 1 - refChan) * chanBandwidth; } resolution = abs(chanBandwidth); //if altrval (and altrpix) fits keywords exist use //recalucalated values instead of the data form FQ table /* if (useAltrval) { refFreq = refFreq_p; chanFreq = chanFreq_p; } */ msSpW.chanFreq().put(spw, chanFreq); msSpW.chanWidth().put(spw, resolution); msSpW.effectiveBW().put(spw, resolution); msSpW.refFrequency().put(spw, refFreq); msSpW.resolution().put(spw, resolution); msSpW.totalBandwidth().put(spw, totalBandwidth(ifc, fqRow)); if (chanBandwidth > 0) { msSpW.netSideband().put(spw, 1); } else { msSpW.netSideband().put(spw, -1); } msSpW.freqGroup().put(spw, freqGroup); msSpW.freqGroupName().put(spw, "none"); msSpW.flagRow().put(spw, False); // set the reference frames for frequency //msSpW.measFreqRef().put(spw, freqsys_p); } } void MSFitsInput::fillMSMainTable(BinaryTable& bt) { MSColumns& msc(*_msc); const Regex trailing(" *$"); ConstFitsKeywordList kwl = bt.kwlist(); //FitsKeywordList pkw = kwl; const FitsKeyword* kw; kwl.first(); // get the uv table column names Int nFields = (kw = kwl("TFIELDS")) ? kw->asInt() : -1; if (nFields == -1) { _log << LogOrigin("MSFitsInput", "fillMSMainTable") << "Could not find the number of fields of the uv table" << LogIO::EXCEPTION; } String object = (kw = kwl(FITS::OBJECT)) ? kw->asString() : "unknown"; Vector TType(nFields); Vector TScal(nFields); Vector TZero(nFields); kwl.first(); for (Int i = 0; i < nFields; i++) { TType(i) = (kw = kwl(FITS::TTYPE, i + 1)) ? String(kw->asString()) : ""; TType(i) = TType(i).before(trailing); TScal(i) = (kw = kwl(FITS::TSCAL, i + 1)) ? kw->asDouble() : 1; TZero(i) = (kw = kwl(FITS::TZERO, i + 1)) ? kw->asDouble() : 0; } _log << LogOrigin("MSFitsInput", "fillMSMainTable") << LogIO::DEBUG1 << "TType=" << TType << "\nTScal=" << TScal << "\nTZero=" << TZero << LogIO::POST; Int nCorr = _nPixel(getIndex(_coordType, "STOKES")); Int nChan = _nPixel(getIndex(_coordType, "FREQ")); Int ns = max(1, _nIF); Int nrows = bt.nrows(); long estStor = nrows * ns / 1024 * (7 * 8 + 11 * 4 + (2 * nCorr + 3 * nCorr * nChan) * 4 + nCorr * nChan + 1); float needS = estStor / 1024.; Directory curD(_msFile); float freeS = curD.freeSpaceInMB(); _log << LogOrigin("MSFitsInput", "MSFitsInput") << ((needS > freeS) ? LogIO::WARN : LogIO::DEBUG1) << "Estimate of Needed Storage Space in MB: " << 0.9 * needS << "~" << 1.6 * needS << "\n Free Space in MB: " << freeS << LogIO::POST; Matrix vis(nCorr, nChan); Vector sigma(nCorr); Matrix weightSpec(nCorr, nChan); Vector weight(nCorr); const Int nCat = 3; Vector cat(nCat); cat(0) = "FLAG_CMD"; cat(1) = "ORIGINAL"; cat(2) = "USER"; msc.flagCategory().rwKeywordSet().define("CATEGORY", cat); Cube flagCat(nCorr, nChan, nCat, False); Matrix flag = flagCat.xyPlane(0); // references flagCat's storage Int iU, iV, iW; iU = getIndexContains(TType, "UU"); iV = getIndexContains(TType, "VV"); iW = getIndexContains(TType, "WW"); if (iU < 0 || iV < 0 || iW < 0) { throw(AipsError("MSFitsInput: Cannot find UVW information")); } Int iBsln = getIndex(TType, "BASELINE"); Int iSubarr = getIndex(TType, "SUBARRAY"); Int iAnt1 = getIndex(TType, "ANTENNA1"); Int iAnt2 = getIndex(TType, "ANTENNA2"); Int iTime0 = getIndex(TType, "DATE"); Int iSource = getIndex(TType, "SOURCE"); Int iFreq = getIndex(TType, "FREQSEL"); Int iVis = getIndex(TType, "VISIBILITIES"); _log << LogIO::NORMAL << "Fill MS Main Table of " << nrows << " rows uvfits visibility data " << LogIO::POST; Int row = -1; Double interval, exposure; interval = 0.0; exposure = 0.0; Bool discernIntExp(True); Double discernedInt(DBL_MAX); ProgressMeter meter(0.0, nrows * 1.0, "UVFITS Filler", "rows copied", "", "", True, nrows / 100); Vector uvw(3); // Remember last-filled values for TSM use Int lastFillArrayId, lastFillFieldId, lastFillScanNumber; lastFillArrayId = -1; lastFillFieldId = -1, lastFillScanNumber = 0; Double lastFillTime = 0; // Keep track of array-specific scanNumbers, FieldIds and FreqIds Vector scanNumber(1); Vector lastFieldId(1); Vector lastFreqId(1); scanNumber = 0; lastFieldId = -1; lastFreqId = -1; _nArray = -1; Bool lastRowFlag = False; for (Int group = 0; group < nrows; group++) { const Table tb = (group < 1) ? bt.thisRow() : bt.nextRow(); try { ScalarColumn colDate(tb, TType(iTime0)); ScalarColumn colUU(tb, TType(iU)); ScalarColumn colVV(tb, TType(iV)); ScalarColumn colWW(tb, TType(iW)); ScalarColumn colBL; ScalarColumn colSubarr, colAnt1, colAnt2; ArrayColumn colVis(tb, TType(iVis)); if (iBsln >= 0) { colBL = ScalarColumn(tb, TType(iBsln)); } else { colSubarr = ScalarColumn(tb, TType(iSubarr)); colAnt1 = ScalarColumn(tb, TType(iAnt1)); colAnt2 = ScalarColumn(tb, TType(iAnt2)); } Int visL = 1; for (uInt i = 0; i < _nPixel.nelements(); i++) visL *= _nPixel(i); Vector visib(visL); visib = colVis.getColumn(); Double time = (colDate.asdouble(0) - 2400000.5) * C::day ; //Double time = colDate.asdouble(0); //Time tm(time); // Extract fqid Int freqId = 1; if (iFreq >= 0) { ScalarColumn colFrqSel(tb, TType(iFreq)); freqId = (Int) colFrqSel.asfloat(0); } // Extract field Id Int fieldId = 0; if (iSource >= 0) { ScalarColumn colSU(tb, TType(iSource)); // make 0-based fieldId = (Int) colSU.asfloat(0) - 1; } // Extract uvw uvw(0) = colUU.asdouble(0); uvw(1) = colVV.asdouble(0); uvw(2) = colWW.asdouble(0); // Convert from units of seconds to meters uvw *= C::c; // Extract array/baseline/antenna info Int arrayId; std::pair ants; if (iBsln >= 0) { Float baseline = colBL.asfloat(0); ants = _extractAntennas(baseline); arrayId = Int(100.0 * (baseline - Int(baseline) + 0.001)); } else { Int antenna1 = _priGroup.parm(iAnt1); Int antenna2 = _priGroup.parm(iAnt2); ants = _extractAntennas(antenna1, antenna2); arrayId = _priGroup.parm(iSubarr) - 1; } _nArray = max(_nArray, arrayId + 1); Int ant1 = ants.first; Int ant2 = ants.second; // Ensure arrayId-specific params are of correct length: if (scanNumber.shape() < _nArray) { scanNumber.resize(_nArray, True); lastFieldId.resize(_nArray, True); lastFreqId.resize(_nArray, True); scanNumber(_nArray - 1) = 0; lastFieldId(_nArray - 1) = -1; lastFreqId(_nArray - 1) = -1; } // Detect new scan (field or freqid change) for each arrayId if (fieldId != lastFieldId(arrayId) || freqId != lastFreqId(arrayId) || time - lastFillTime > 300.0) { scanNumber(arrayId)++; lastFieldId(arrayId) = fieldId; lastFreqId(arrayId) = freqId; } // keep track of minimum which is the only one // (if time step is larger than UVFITS precision (and zero)) discernIntExp = True; Double tempint; tempint = time - lastFillTime; if (tempint > 0.01) { discernedInt = min(discernedInt, tempint); } // Work out which axis increments fastests, pol or channel Bool polFastest = (getIndex(_coordType, "STOKES") < getIndex( _coordType, "FREQ")); const Int nx = (polFastest ? nChan : nCorr); const Int ny = (polFastest ? nCorr : nChan); Int count = 0; for (Int ifno = 0; ifno < max(1, _nIF); ifno++) { // IFs go to separate rows in the MS _ms.addRow(); row++; // fill in values for all the unused columns if (row == 0) { msc.feed1().put(row, 0); msc.feed2().put(row, 0); msc.flagRow().put(row, False); lastRowFlag = False; msc.processorId().put(row, -1); msc.observationId().put(row, 0); msc.stateId().put(row, -1); } // Fill scanNumber if changed since last row if (scanNumber(arrayId) != lastFillScanNumber) { msc.scanNumber().put(row, scanNumber(arrayId)); lastFillScanNumber = scanNumber(arrayId); } weight = 0.0; count = 0; // Loop over chans and corrs: for (Int ix = 0; ix < nx; ix++) { for (Int iy = 0; iy < ny; iy++) { const Float visReal = visib(count++); const Float visImag = visib(count++); const Float wt = visib(count++); const Int pol = (polFastest ? _corrIndex[iy] : _corrIndex[ix]); const Int chan = (polFastest ? ix : iy); if (wt <= 0.0) { weightSpec(pol, chan) = abs(wt); flag(pol, chan) = True; weight(pol) += abs(wt); } else { weightSpec(pol, chan) = wt; flag(pol, chan) = False; weight(pol) += wt; } vis(pol, chan) = Complex(visReal, visImag); } } // calculate sigma (weight = inverse variance) for (Int nc = 0; nc < nCorr; nc++) { if (weight(nc) > 0.0) { sigma(nc) = sqrt(1.0 / weight(nc)); } else { sigma(nc) = 0.0; } } // If available, store interval/exposure if (!discernIntExp) { msc.interval().put(row, interval); msc.exposure().put(row, exposure); } msc.data().put(row, vis); msc.weight().put(row, weight); msc.sigma().put(row, sigma); msc.weightSpectrum().put(row, weightSpec); msc.flag().put(row, flag); msc.flagCategory().put(row, flagCat); Bool rowFlag = allEQ(flag, True); if (rowFlag != lastRowFlag) { msc.flagRow().put(row, rowFlag); lastRowFlag = rowFlag; } if (arrayId != lastFillArrayId) { msc.arrayId().put(row, arrayId); lastFillArrayId = arrayId; } msc.antenna1().put(row, ant1); msc.antenna2().put(row, ant2); if (time != lastFillTime) { msc.time().put(row, time); msc.timeCentroid().put(row, time); lastFillTime = time; } msc.uvw().put(row, uvw); // determine the spectralWindowId Int spW = ifno; if (iFreq >= 0) { //spW = (Int) colFrqSel.asfloat(0) - 1; // make 0-based spW = freqId - 1; // make 0-based if (_nIF > 0) { spW *= _nIF; spW += ifno; } } // nSpW = max(nSpW, spW + 1); // Always put DDI (SSM) since it might change rapidly msc.dataDescId().put(row, spW); // store the fieldId if (fieldId != lastFillFieldId) { msc.fieldId().put(row, fieldId); // nField = max(nField, fieldId + 1); lastFillFieldId = fieldId; } } meter.update((group + 1) * 1.0); } catch (const std::exception& x) { _log << LogOrigin("MSFitsInput", "fillMSMainTable") << "Exception while filling MS main table. " << x.what() << LogIO::EXCEPTION; } } // If determining interval on-the-fly, fill interval/exposure columns // now: if (discernIntExp) { discernedInt = floor(100.0 * discernedInt + 0.5) / 100.0; msc.interval().fillColumn(discernedInt); msc.exposure().fillColumn(discernedInt); } // fill the receptorAngle with defaults, just in case there is no AN table _receptorAngle.resize(2 * _nAntRow); _receptorAngle = 0; // set the Measure References if (iSource < 0) { double ra=0.; double dec=0.; ra = _refVal(getIndex(_coordType, "RA")); dec = _refVal(getIndex(_coordType, "DEC")); fillFieldTable(ra, dec, object); } } std::pair MSFitsInput::_extractAntennas(Int ant1, Int ant2) { _nAntRow = max(_nAntRow, ant1); _nAntRow = max(_nAntRow, ant2); _uniqueAnts.insert(ant1); _uniqueAnts.insert(ant2); // make 0-based ant1--; ant2--; return make_pair(ant1, ant2); } std::pair MSFitsInput::_extractAntennas(Float baseline) { Int ant1 = Int(baseline) / 256; Int ant2 = Int(baseline) - ant1 * 256; return _extractAntennas(ant1, ant2); } void MSFitsInput::fillObservationTable(ConstFitsKeywordList& kwl) { const FitsKeyword* kw; const Regex trailing(" *$"); // trailing blanks kwl.first(); _ms.observation().addRow(); String observer; observer = (kw = kwl(FITS::OBSERVER)) ? kw->asString() : ""; observer = observer.before(trailing); MSObservationColumns msObsCol(_ms.observation()); msObsCol.observer().put(0, observer); String telescope = (kw = kwl(FITS::TELESCOP)) ? kw->asString() : "unknown"; telescope = telescope.before(trailing); if (telescope == "HATCREEK") telescope = "BIMA"; String instrume = (kw = kwl(FITS::INSTRUME)) ? kw->asString() : "unknown"; instrume = instrume.before(trailing); msObsCol.telescopeName().put(0, telescope); msObsCol.scheduleType().put(0, ""); msObsCol.project().put(0, ""); //Double epoch = (kw = kwl(FITS::EPOCH)) ? kw->asDouble() : 2000; String date; date = (kw = kwl(FITS::DATE_OBS)) ? kw->asString() : ""; if (date == "") date = "2000-01-01"; String date_map; date_map = (kw = kwl(FITS::DATE_MAP)) ? kw->asString() : ""; MVTime timeVal, timeRel; MEpoch::Types epochRef; FITSDateUtil::fromFITS(timeVal, epochRef, date, "UTC"); FITSDateUtil::fromFITS(timeRel, epochRef, date_map, "UTC"); Vector times(2); times(0) = timeVal.second(); times(1) = timeVal.second(); _obsTime(0) = times(0); _obsTime(1) = times(1); msObsCol.timeRange().put(0, times); msObsCol.releaseDate().put(0, timeRel.second()); msObsCol.flagRow().put(0, False); } void MSFitsInput::fillPointingTable() { // fill the pointing table. run though the main table, find field changes, // and add pointing rows as needed by looking up the field info in the field table if (_addSourceTable) _log << LogOrigin("MSFitsInput", "fillPointingTable") << LogIO::NORMAL << "Filling Pointing table." << LogIO::POST; Int nrow = _ms.nrow(); Int nAnt = _ms.antenna().nrow(); Int lastFieldId = -1; Double lastTime = 0; Vector fieldId = _msc->fieldId().getColumn(); Vector ddId; if (_addSourceTable) ddId = _msc->dataDescId().getColumn(); ProgressMeter meter(0.0, nrow * 1.0, "UVFITS Filler", "rows copied", "", "", True, nrow / 100); for (Int i = 0; i < nrow; i++) { if (fieldId(i) != lastFieldId) { lastFieldId = fieldId(i); if (i > 0) lastTime = _msc->time()(i - 1); Array pointingDir = _msc->field().phaseDir()(lastFieldId); String name = _msc->field().name()(lastFieldId); Int numPoly = _msc->field().numPoly()(lastFieldId); Double time = _msc->time()(i); Int np = _ms.pointing().nrow(); if (np > 0) { // fix up time and interval for previous entries Double midTime = (lastTime + _msc->pointing().time()(np - 1)) / 2; Double interval = lastTime - _msc->pointing().time()(np - 1) + _msc->interval()(i - 1); for (Int j = 0; j < nAnt; j++) { _msc->pointing().time().put(np - j - 1, midTime); _msc->pointing().timeOrigin().put(np - j - 1, midTime); _msc->pointing().interval().put(np - j - 1, interval); } } for (Int j = 0; j < nAnt; j++) { _ms.pointing().addRow(); _msc->pointing().antennaId().put(np + j, j); if (j == 0) { _msc->pointing().time().put(np + j, time); _msc->pointing().timeOrigin().put(np + j, time); _msc->pointing().interval().put(np + j, 0); _msc->pointing().name().put(np + j, name); _msc->pointing().numPoly().put(np + j, numPoly); _msc->pointing().direction().put(np + j, pointingDir); _msc->pointing().target().put(np + j, pointingDir); _msc->pointing().tracking().put(np + j, True); } } } meter.update((i + 1) * 1.0); } // fix up last interval lastTime = _msc->time()(nrow - 1); Int np = _ms.pointing().nrow(); if (np > 0) { // fix up time and interval for previous entries Double midTime = (lastTime + _msc->pointing().time()(np - 1)) / 2; Double interval = lastTime - _msc->pointing().time()(np - 1) + _msc->interval()(nrow - 1); for (Int j = 0; j < nAnt; j++) { _msc->pointing().time().put(np - j - 1, midTime); _msc->pointing().timeOrigin().put(np - j - 1, midTime); _msc->pointing().interval().put(np - j - 1, interval); } } } void MSFitsInput::fillSourceTable() { _log << LogOrigin("MSFitsInput", "fillSourceTable") << LogIO::NORMAL << "Filling SOURCE table." << LogIO::POST; Int numRow = 1; if (numRow > 0) { String tName = _ms.tableName(); MSSummary mss(&_ms, tName); Record mainRec; mss.listMain(_log, mainRec); //Record fieldRec; //mss.listField(_log, fieldRec, True); ProgressMeter meter(0.0, mainRec.nfields() * 1.0, "UVFITS Filler", "rows copied", "", "", True, mainRec.nfields() * 300 / 100); for (uInt i = 0; i < mainRec.nfields() - 5; i++) { Int fnum = mainRec.fieldNumber(String("scan_").append( String::toString(i + 1))); Record rec = mainRec.subRecord(fnum).subRecord(String('0')); Double time1 = rec.asDouble("BeginTime"); Double time2 = rec.asDouble("IntegrationTime"); Int fid = rec.asInt("FieldId"); String name = rec.asString("FieldName"); //Int numPoly = ; Vector spwIds = rec.asArrayInt("SpwIds"); for (uInt spwId = 0; spwId < spwIds.nelements(); spwId++) { MSSourceIndex sourceIndex(_ms.source()); sourceIndex.sourceId() = fid; sourceIndex.spectralWindowId() = spwIds(spwId); Vector rows = sourceIndex.getRowNumbers(); if (rows.nelements() == 0) { _ms.source().addRow(); Int j = _ms.source().nrow() - 1; MSSourceColumns & msc = _msc->source(); msc.sourceId().put(j, fid); msc.name().put(j, name); Matrix phaseDir = _msc->field().phaseDir()(fid); Vector srcDir = phaseDir.column(0); Vector rate(2); if (phaseDir.ncolumn() > 1) rate = phaseDir.column(1); else rate = 0.0; msc.direction().put(j, srcDir); msc.properMotion().put(j, rate); msc.time().put(j, time1 * C::day); msc.interval().put(j,time2); msc.spectralWindowId().put(j, spwId); Vector sysVel(1); sysVel(0) = 0.; msc.sysvel().put(j, sysVel); msc.numLines().put(j, 1); Vector transition(1); transition(0) = ""; msc.transition().put(j, transition); Vector restFreqs(1); restFreqs(0) = _restfreq; if (restFreqs(0) <= 0.0) { restFreqs(0) = _msc->spectralWindow().refFrequency()( spwId); } msc.restFrequency().put(j, restFreqs); msc.calibrationGroup().put(j, -1); //String code = _msc->field().c.code().asString(); msc.code().put(j, _msc->field().code()(fid)); } //meter.update((i + 1) * 1.0); } } } else { //////////////////this is uselessly slow // fill the source table. run though the main table look for new spectralwindows // and add source table entries for each field/spw combination Int nrow = _ms.nrow(); Int lastFieldId = -1; Int lastDDId = -1; Vector fieldId = _msc->fieldId().getColumn(); Vector ddId = _msc->dataDescId().getColumn(); ProgressMeter meter(0.0, nrow * 1.0, "UVFITS Filler", "rows copied", "", "", True, nrow / 100); for (Int i = 0; i < nrow; i++) { if (fieldId(i) != lastFieldId || (ddId(i) != lastDDId)) { lastFieldId = fieldId(i); Array pointingDir = _msc->field().phaseDir()( lastFieldId); String name = _msc->field().name()(lastFieldId); //Int numPoly = _msc->field().numPoly()(lastFieldId); Double time = _msc->time()(i); lastDDId = ddId(i); Int spwId = _msc->dataDescription().spectralWindowId()( lastDDId); // now check if we've seen this field for this spectral window // Use indexed access to the SOURCE sub-table MSSourceIndex sourceIndex(_ms.source()); sourceIndex.sourceId() = lastFieldId; sourceIndex.spectralWindowId() = spwId; Vector rows = sourceIndex.getRowNumbers(); if (rows.nelements() == 0) { _ms.source().addRow(); Int j = _ms.source().nrow() - 1; MSSourceColumns & mss = _msc->source(); mss.sourceId().put(j, lastFieldId); _msc->field().sourceId().put(lastFieldId, lastFieldId); mss.name().put(j, name); Matrix phaseDir = _msc->field().phaseDir()( lastFieldId); Vector srcDir = phaseDir.column(0), rate(2); if (phaseDir.ncolumn() > 1) rate = phaseDir.column(1); else rate = 0.0; mss.direction().put(j, srcDir); mss.properMotion().put(j, rate); mss.time().put(j, time); mss.interval().put(j, DBL_MAX); mss.spectralWindowId().put(j, spwId); Vector sysVel(1); sysVel(0) = 0.; mss.sysvel().put(j, sysVel); mss.numLines().put(j, 1); Vector transition(1); transition(0) = ""; mss.transition().put(j, transition); Vector restFreqs(1); restFreqs(0) = _restfreq; if (restFreqs(0) <= 0.0) { // put in the reference freq as default for the rest frequency restFreqs(0) = _msc->spectralWindow().refFrequency()( spwId); } mss.restFrequency().put(j, restFreqs); mss.calibrationGroup().put(j, -1); } } meter.update((i + 1) * 1.0); } //////////////////this is uselessly slow } } void MSFitsInput::fillFieldTable(BinaryTable& bt) { Int nField = bt.nrows(); TableRecord btKeywords = bt.getKeywords(); if (!btKeywords.isDefined("NO_IF")) { throw(AipsError("MSFitsInput: Illegal SU file: no number of IFs")); } uInt noif = bt.getKeywords().asuInt("NO_IF"); MSFieldColumns& msField(_msc->field()); // Table suTab=bt.fullTable("",Table::Scratch); Table suTab = bt.fullTable(); ScalarColumn id(suTab, "ID. NO."); ScalarColumn name(suTab, "SOURCE"); ScalarColumn qual(suTab, "QUAL"); Bool multiqual = False; Int minqual, maxqual; minMax(minqual, maxqual, qual.getColumn()); if (minqual != maxqual) multiqual = True; ScalarColumn code(suTab, "CALCODE"); // ScalarColumn iflux(suTab,"IFLUX"); // etc Q, U, V (Jy) ScalarColumn ra(suTab, "RAEPO"); //degrees ScalarColumn dec(suTab, "DECEPO"); //degrees ScalarColumn raapp(suTab, "RAAPP"); //degrees ScalarColumn decapp(suTab, "DECAPP"); //degrees ScalarColumn epoch(suTab, "EPOCH"); //years ScalarColumn pmra(suTab, "PMRA"); //deg/day ScalarColumn pmdec(suTab, "PMDEC"); //deg/day if (Int(suTab.nrow()) < nField) { _log << LogOrigin("MSFitsInput", __func__) << LogIO::NORMAL << "Input Source id's not sequential, adding empty rows in output" << LogIO::POST; } Int outRow = -1; // RESTFREQ and LSRVEL are 2D columns according to the AIPS Memo 117 _restFreq.resize(noif, suTab.nrow()); _sysVel.resize(noif, suTab.nrow()); try{ ArrayColumn restfreq(suTab,"RESTFREQ"); // Hz ArrayColumn sysvel(suTab,"LSRVEL"); // m/s restfreq.getColumn(_restFreq); sysvel.getColumn(_sysVel); } catch (std::exception& x) { if(noif>1){ _log << LogOrigin("MSFitsInput", __func__) << LogIO::WARN << x.what() << ": " << "Inconsistent setup of RESTFREQ and LSRVEL columns." << endl << "With NO_IF>1, they should be arrays not scalars." << LogIO::POST; } ScalarColumn restfreq(suTab,"RESTFREQ"); // Hz ScalarColumn sysvel(suTab,"LSRVEL"); // m/s Vector tmprf(suTab.nrow()); Vector tmpsv(suTab.nrow()); restfreq.getColumn(tmprf); sysvel.getColumn(tmpsv); for(uInt ii=0; ii outRow) { // Append a flagged, empty row to the FIELD table _ms.field().addRow(); outRow++; Vector nullDir(1); nullDir(0).set(MVDirection(0.0, 0.0), MDirection::Ref(epochRefZero)); msField.phaseDirMeasCol().put(outRow, nullDir); msField.delayDirMeasCol().put(outRow, nullDir); msField.referenceDirMeasCol().put(outRow, nullDir); msField.flagRow().put(outRow, True); } msField.sourceId().put(fld, fld); msField.code().put(fld, code(inRow)); String theFldName; if (multiqual) theFldName = name(inRow) + "_" + String::toString(qual(inRow)); else theFldName = name(inRow); msField.name().put(fld, theFldName); Int numPoly = 0; if (!nearAbs(pmra(inRow), 0.0) || !nearAbs(pmdec(inRow), 0.0)) { numPoly = 1; } // The code below will write the direction in B1950 or J2000 coordinates if // the direction is constant. However it will use apparent Coordinates (I // am not sure if this means APP, JTRUE, BTRUE or what), if the proper // motion is non-zero. If the epoch in the incoming SU // table is "-1" (via AIPS UVFITS, a planet tracked by the correlator), it // will adopt the epochRefZero and use the ra/dec (not raapp/decapp). // The handling of planets should be cleaned up (defect 3636). // In all cases the time will be the date of the start of the observation. MDirection::Types epochRef = MDirection::APP; MVDirection refDir; if (numPoly == 0) { if (near(epoch(inRow), 2000.0, 0.01)) { epochRef = MDirection::J2000; } else if (nearAbs(epoch(inRow), 1950.0, 0.01)) { if (_array == "VLA") epochRef = MDirection::B1950_VLA; else epochRef = MDirection::B1950; } else if (epoch(inRow) == -1.0) { epochRef = epochRefZero; _log << LogOrigin("MSFitsInput", __func__) << " Assuming standard epoch " << " for " << name(inRow) << ". Be aware that this may not be correct." << endl; } else { _log << LogOrigin("MSFitsInput", __func__) << " Cannot handle epoch in SU table: " << epoch(inRow) << LogIO::EXCEPTION; } refDir = MVDirection(ra(inRow) * C::degree, dec(inRow) * C::degree); } else { refDir = MVDirection(raapp(inRow) * C::degree, decapp(inRow) * C::degree); } Vector radecMeas(numPoly + 1); radecMeas(0).set(refDir, MDirection::Ref(epochRef)); if (numPoly == 1) { radecMeas(1).set(MVDirection(pmra(inRow) * C::degree / C::day, pmdec(inRow) * C::degree / C::day), MDirection::Ref( epochRef)); } msField.time().put(fld, _obsTime(0)); msField.numPoly().put(fld, numPoly); msField.delayDirMeasCol().put(fld, radecMeas); msField.phaseDirMeasCol().put(fld, radecMeas); msField.referenceDirMeasCol().put(fld, radecMeas); msField.flagRow().put(fld, False); } } void MSFitsInput::fillFieldTable(double ra, double dec, String source) { MSFieldColumns& msField(_msc->field()); _ms.field().addRow(); msField.sourceId().put(0, 0); msField.code().put(0, ""); msField.name().put(0, source); Int numPoly = 0; //MDirection::Types epochRef = MDirection::APP; MDirection::Types epochRef = _epochRef; MVDirection refDir; refDir = MVDirection(ra * C::degree, dec * C::degree); Vector radecMeas(1); radecMeas(0).set(refDir, MDirection::Ref(epochRef)); msField.time().put(0, _obsTime(0)); msField.numPoly().put(0, numPoly); msField.delayDirMeasCol().put(0, radecMeas); msField.phaseDirMeasCol().put(0, radecMeas); msField.referenceDirMeasCol().put(0, radecMeas); msField.flagRow().put(0, False); } } //# NAMESPACE CASACORE - END casacore-3.7.1/msfits/MSFits/MSFitsInput.h000066400000000000000000000327531476623553700203460ustar00rootroot00000000000000//# MSFitsInput: simple uvfits (random group) to MeasurementSet conversion //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., 675 Mass Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSFITSINPUT_H #define MS_MSFITSINPUT_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class FitsInput; class BinaryTable; class MSColumns; template class ScalarColumn; // // A helper class for MSFitsInput // // // // This class can hold a primary array of several datatypes // // // This is a helper class to avoid cumbersome switch statements on the // template type of the primary array // It forwards all the PrimaryArray member functions we need in the filler. // class MSPrimaryTableHolder { // This is a helper class to avoid cumbersome switch statements on the // template type of the primary array // It forwards all the PrimaryTable member function we need in the filler. public: // Construct an empty holder, used to attach to later MSPrimaryTableHolder(); // Construct from an input file containing a FITS primary group hdu. // Throws an exception if the datatype is not Short, FitsLong or Float MSPrimaryTableHolder(FitsInput& infile); ~MSPrimaryTableHolder(); // Attach to the input file, create the appropriate PrimaryArray. // Throws an exception if the datatype is not Short, FitsLong or Float void attach(FitsInput& infile); // Detach from the input file void detach(); //# forwarding functions // Number of dimensions Int dims() {return hdu_p->dims();} // Length of i'th axis Int dim(Int i) {return hdu_p->dim(i);} // Coordinate type Char* ctype(Int i) { return pf ? pf->ctype(i) : (pl ? pl->ctype(i) : ps->ctype(i));} // Coordinate reference value Double crval(Int i) { return pf ? pf->crval(i) : (pl ? pl->crval(i) : ps->crval(i));} // Coordinate reference pixel Double crpix(Int i) { return pf ? pf->crpix(i) : (pl ? pl->crpix(i) : ps->crpix(i));} // Coordinate delta Double cdelt(Int i) { return pf ? pf->cdelt(i) : (pl ? pl->cdelt(i) : ps->cdelt(i));} // Keyword of given type const FitsKeyword* kw(const FITS::ReservedName& n) { return hdu_p->kw(n);} // All keywords ConstFitsKeywordList& kwlist() { return hdu_p->kwlist();} // Advance to next keyword const FitsKeyword* nextkw() { return hdu_p->nextkw();} // Read the next group Int read() { if (pf) return pf->read(); else if (pl) return pl->read(); else if (ps) return ps->read(); else if (pb) return pb->read(); else cout << "can not read the table" << endl; return 0; } private: HeaderDataUnit* hdu_p; PrimaryTable* ps; PrimaryTable* pl; PrimaryTable* pf; PrimaryTable* pb; }; // // A helper class for MSFitsInput // // // // This class can hold a primary group of several datatypes // // // This is a helper class to avoid cumbersome switch statements on the // template type of the primary group // It forwards all the PrimaryGroup member functions we need in the filler. // class MSPrimaryGroupHolder { // This is a helper class to avoid cumbersome switch statements on the // template type of the primary group // It forwards all the PrimaryGroup member function we need in the filler. public: // Construct an empty holder, used to attach to later MSPrimaryGroupHolder(); // Construct from an input file containing a FITS primary group hdu. // Throws an exception if the datatype is not Short, FitsLong or Float MSPrimaryGroupHolder(FitsInput& infile); ~MSPrimaryGroupHolder(); // Attach to the input file, create the appropriate PrimaryGroup. // Throws an exception if the datatype is not Short, FitsLong or Float void attach(FitsInput& infile); // Detach from the input file void detach(); //# forwarding functions // Number of dimensions Int dims() {return hdu_p->dims();} // Length of i'th axis Int dim(Int i) {return hdu_p->dim(i);} // Coordinate type Char* ctype(Int i) { return pf ? pf->ctype(i) : (pl ? pl->ctype(i) : ps->ctype(i));} // Coordinate reference value Double crval(Int i) { return pf ? pf->crval(i) : (pl ? pl->crval(i) : ps->crval(i));} // Coordinate reference pixel Double crpix(Int i) { return pf ? pf->crpix(i) : (pl ? pl->crpix(i) : ps->crpix(i));} // Coordinate delta Double cdelt(Int i) { return pf ? pf->cdelt(i) : (pl ? pl->cdelt(i) : ps->cdelt(i));} // Keyword of given type const FitsKeyword* kw(const FITS::ReservedName& n) { return hdu_p->kw(n);} // All keywords ConstFitsKeywordList& kwlist() { return hdu_p->kwlist();} // Advance to next keyword const FitsKeyword* nextkw() { return hdu_p->nextkw();} // Number of groups Int gcount() const { return pf ? pf->gcount() : ( pl ? pl->gcount() : ps->gcount());} // Number of parameters Int pcount() const { return pf ? pf->pcount() : ( pl ? pl->pcount() : ps->pcount());} // Parameter type Char* ptype(Int i) const { return pf ? pf->ptype(i) : ( pl ? pl->ptype(i) : ps->ptype(i));} // Read the next group Int read() { return pf ? pf->read() : ( pl ? pl->read() : ps->read());} // Get i'th parameter Double parm(Int i) { return pf ? pf->parm(i) : ( pl ? pl->parm(i) : ps->parm(i));} // Get group data with index i, scaled and converted to Double Double operator () (Int i) const { return pf ? (*pf)(i) : ( pl ? (*pl)(i) : (*ps)(i));} private: HeaderDataUnit* hdu_p; PrimaryGroup* ps; PrimaryGroup* pl; PrimaryGroup* pf; }; // // UV FITS to MeasurementSet filler // // // //
      • MeasurementSet //
      • FITS classes // // // // MSFitsInput handles the conversion of FITS files to MeasurementSets // // // // UV FITS to MeasurementSet filler. This can handle single source fits and // multi source fits as written by classic AIPS. Also copes with multiple // arrays (i.e. multiple AN tables) but doesn't correct for 5 day offsets // introduced by DBCON. // class MSFitsInput { // This is an implementation helper class used to store 'local' data // during the filling process. public: MSFitsInput() = delete; // Create from output and input file names. This function opens the input // file, and checks the output file is writable. MSFitsInput(const String& msFile, const String& fitsFile, const Bool NewNameStyle=False); MSFitsInput(const MSFitsInput& other) = delete; // The destructor is fairly trivial. ~MSFitsInput(); MSFitsInput& operator=(const MSFitsInput& other) = delete; // Read all the data from the FITS file and create the MeasurementSet. Throws // an exception when it has severe trouble interpreting the FITS file. // void readFitsFile(Int obsType = MSTileLayout::Standard); private: FitsInput* _infile; String _msFile; MSPrimaryGroupHolder _priGroup; MSPrimaryTableHolder _priTable; MeasurementSet _ms; MSColumns* _msc; Int _nIF; Vector _nPixel, _corrType; Block _corrIndex; Matrix _corrProduct; Vector _coordType; Vector _refVal, _refPix, _delta; String _array, _object, _timsys; Double _epoch; MDirection::Types _epochRef; // This is a direction measure reference code // determined by epoch_p, hence the name and type. // unique antennas found in the visibility data // NOTE These are 1-based std::set _uniqueAnts; // number of rows in the created MS ANTENNA table Int _nAntRow; Int _nArray; Vector _receptorAngle; MFrequency::Types _freqsys; Double _restfreq; // used for images Bool _addSourceTable; LogIO _log; Record _header; Double _refFreq; Bool _useAltrval; Vector _chanFreq; Bool _newNameStyle; Vector _obsTime; Matrix _restFreq; // used for UVFITS Matrix _sysVel; Bool _msCreated; // Check that the input is a UV fits file with required contents. // Returns False if not ok. Bool _checkInput(FitsInput& infile); // Read the axis info of the primary group, throws an exception if required // axes are missing. void getPrimaryGroupAxisInfo(); // Set up the MeasurementSet, including StorageManagers and fixed columns. // If useTSM is True, the Tiled Storage Manager will be used to store // DATA, FLAG and WEIGHT_SPECTRUM. Use obsType to choose the tiling // scheme. void setupMeasurementSet(const String& MSFileName, Bool useTSM=True, Int obsType = MSTileLayout::Standard); ///////////////fillers for primary table form uvfits////////////////////// // Read a binary table extension of type AIPS AN and create an antenna table void fillAntennaTable(BinaryTable& bt); // Read a binary table extension and update history table void fillHistoryTable(ConstFitsKeywordList& kwl); // Read a binary table extension and update history table void fillObservationTable(ConstFitsKeywordList& kwl); //extract axis information void getAxisInfo(ConstFitsKeywordList&); //extract axis information void sortPolarizations(); void fillPolarizationTable(); //verify that the fits contains visibility data void checkRequiredAxis(); void fillSpectralWindowTable(BinaryTable& bt); // fill Field table void fillFieldTable(BinaryTable& bt); void fillFieldTable(double, double, String); void fillMSMainTable(BinaryTable& bt); void fillPointingTable(); void fillSourceTable(); // fill the Feed table with minimal info needed for synthesis processing void fillFeedTable(); ///////////////fillers for primary table form uvfits////////////////////// // Fill the Observation and ObsLog tables void fillObsTables(); // Fill the main table from the Primary group data // if we have enough memory try to do it in mem void fillMSMainTableColWise(Int& nField, Int& nSpW); //else do it row by row void fillMSMainTable(Int& nField, Int& nSpW); // fill spectralwindow table from FITS FQ table + header info void fillSpectralWindowTable(BinaryTable& bt, Int nSpW); // fill spectralwindow table from header void fillSpectralWindowTable(); // fill Field table from FITS SU table void fillFieldTable(BinaryTable& bt, Int nField); // fill Field table from header (single source fits) void fillFieldTable(Int nField); // fill the Pointing table (from Field table, all antennas are assumed // to point in the field direction) and possibly the Source table. void fillExtraTables(); // fix up the EPOCH MEASURE_REFERENCE keywords using the value found // in the (last) AN table void fixEpochReferences(); // Returns the Direction Measure reference for UVW and other appropriate columns // in msc_p (which must exist but have empty columns before you can set it!). MDirection::Types getDirectionFrame(Double epoch); // Check the frame if there is an SU table void setFreqFrameVar(BinaryTable& binTab); // update a the Spectral window post filling if necessary void updateSpectralWindowTable(); void readRandomGroupUVFits(Int obsType); void readPrimaryTableUVFits(Int obsType); std::pair _extractAntennas(Int antenna1, Int antenna2); std::pair _extractAntennas(Float baseline); void _fillSysPowerTable(BinaryTable& bt); void _doFillSysPowerSingleIF( const String& casaTableName, const ScalarColumn& timeCol, const ScalarColumn& intervalCol, const ScalarColumn& antNoCol, const ScalarColumn& freqIDCol, const ScalarColumn& powerDif1Col, const ScalarColumn& powerSum1Col, const ScalarColumn& postGain1Col, const ScalarColumn& powerDif2Col, const ScalarColumn& powerSum2Col, const ScalarColumn& postGain2Col ); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/msfits/MSFits/MSFitsOutput.cc000066400000000000000000003551051476623553700207040ustar00rootroot00000000000000//# MSFITSOutput: MS to UVFITS //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., 675 Mass Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // for atoi() #include #include #include #include #include namespace casacore { MSFitsOutput::MSFitsOutput( const String& fitsfile, const MeasurementSet& ms, const String& column ) : _fitsfile(fitsfile), _column(column), _ms(ms), _startChan(0), _nchan(1), _stepChan(1), _avgChan(1), _writeSysCal(False), _asMultiSource(False), _combineSpw(False), _writeStation(False), _padWithFlags(False), _overwrite(False), _sensitivity(1.0), _fieldNumber(0) {} void MSFitsOutput::setChannelInfo( Int startChan, Int nchan, Int stepChan, Int avgChan ) { _startChan = startChan; _nchan = nchan; _stepChan = stepChan; _avgChan = avgChan; } void MSFitsOutput::setWriteSysCal(Bool s) { _writeSysCal = s; } void MSFitsOutput::setAsMultiSource(Bool asMultiSource) { _asMultiSource = asMultiSource; } void MSFitsOutput::setCombineSpw(Bool combineSpw) { _combineSpw = combineSpw; } void MSFitsOutput::setWriteStation(Bool writeStation) { _writeStation = writeStation; } void MSFitsOutput::setSensitivity(Double sensitivity) { _sensitivity = sensitivity; } void MSFitsOutput::setPadWitFlags(Bool padWithFlags) { _padWithFlags = padWithFlags; } void MSFitsOutput::setFieldNumber(uInt fieldNumber) { _fieldNumber = fieldNumber; } void MSFitsOutput::setOverwrite(Bool overwrite) { _overwrite = overwrite; } static String toFITSDate(const MVTime &time) { String date, timesys; FITSDateUtil::toFITS(date, timesys, time); return date; } // MJD seconds to day number and day fraction void MSFitsOutput::timeToDay(Int &day, Double &dayFraction, Double time) { const Double JDofMJD0 = 2400000.5; time /= C::day; // now in days; time += JDofMJD0; // now in JD day = Int(time); dayFraction = time - floor(time); } void MSFitsOutput::write() const { MSObservationColumns obsCols(_ms.observation()); if (obsCols.nrow() > 0 && obsCols.telescopeName()(0) == "WSRT") { ThrowIf( ! MSFitsOutputAstron::writeFitsFile(_fitsfile, _ms, _column, _startChan, _nchan, _stepChan, _writeSysCal, _asMultiSource, _combineSpw, _writeStation, _sensitivity ), "Unable to write Astron-specific UVFITS file" ); return; } LogIO os(LogOrigin("MSFitsOutput", __func__)); os << LogIO::NORMAL << " nchan=" << _nchan << " startchan=" << _startChan << " stepchan=" << _stepChan << " avgchan=" << _avgChan << LogIO::POST; const uInt nrow = _ms.nrow(); String msfile = _ms.tableName(); String outfile = _fitsfile; if (outfile.empty()) { outfile = msfile.contains(Regex("\\.ms$")) ? msfile.before(Regex("\\.ms"), 0) + ".fits" : msfile + ".fits"; } String errmsg; if (_overwrite && File(outfile).exists()) { RegularFile(outfile).remove(); os << LogIO::NORMAL << "Removing existing file " << outfile << LogIO::POST; } else if (! _overwrite) { NewFile fileOK(True); ThrowIf ( ! fileOK.valueOK(outfile, errmsg), "Error in output file : " + errmsg ); } os << LogIO::NORMAL << "Converting MeasurementSet " << _ms.tableName() << " to FITS file '" << outfile << "'" << LogIO::POST; // Determine if this MS is a subset of a main MS. Bool isSubset = nrow != (1 + max(_ms.rowNumbers())); if (isSubset) { os << LogIO::NORMAL << "MS " << _ms.tableName() << " is a subset of another MS" << LogIO::POST; } // Find the number of IF's (spectral-windows). Block spwidMap; Vector spwids; uInt nrspw; { MSMetaData md(&_ms, 100); std::set spwIDs = md.getSpwIDs(); Vector allids(spwIDs.size()); copy(spwIDs.begin(), spwIDs.end(), allids.begin()); nrspw = _makeIdMap(spwidMap, spwids, allids); } // If not asMultiSource, check if multiple sources are present. Block fieldidMap; uInt nrfield; Bool doMultiSource = _asMultiSource; { ScalarColumn fldidcol(_ms, MS::columnName(MS::FIELD_ID)); Vector fldid = fldidcol.getColumn(); if (! doMultiSource) { if (! allEQ(fldid, fldid(0))) { doMultiSource = True; os << LogIO::WARN << "Multiple sources are present, thus written " "as a multi-source FITS file" << LogIO::POST; } } Vector fieldids; nrfield = _makeIdMap(fieldidMap, fieldids, fldid); } // Write main table. Get freq and channel-width back. Int refPixelFreq; Double refFreq, chanbw; auto fitsOutput = _writeMain( refPixelFreq, refFreq, chanbw, outfile, spwidMap, nrspw, fieldidMap, doMultiSource ); ThrowIf ( ! fitsOutput, "Could not write main table\n" ); os << LogIO::NORMAL << "Writing AIPS FQ table" << LogIO::POST; ThrowIf( ! _writeFQ( fitsOutput, _ms, spwidMap, nrspw, refFreq, refPixelFreq, chanbw, _combineSpw, _startChan, _nchan, _stepChan, _avgChan ), "Could not write FQ table" ); os << LogIO::NORMAL << "Writing AIPS AN table" << LogIO::POST; ThrowIf( ! _writeAN(fitsOutput, _ms, refFreq, _writeStation), "Could not write AN table" ); if (doMultiSource) { os << LogIO::NORMAL << "Writing AIPS SU table" << LogIO::POST; if ( ! _writeSU(fitsOutput, _ms, fieldidMap, nrfield, spwidMap, nrspw) ) { os << LogIO::NORMAL << "Could not write SU table" << LogIO::POST; } } // If needed, create tables from the SYSCAL table. // Determine if we have to skip the first SYSCAL time. // This is needed for WSRT MS's, where the first time in the SYSCAL // table is the average at the middle of the observation. if (_writeSysCal) { if (_ms.sysCal().tableDesc().ncolumn() == 0) { os << LogIO::WARN << "MS has no or empty SYSCAL subtable, " << "could not write AIPS TY table and AIPS GC table" << LogIO::POST; } else if (_ms.sysCal().nrow() == 0) { os << LogIO::WARN << "MS has empty SYSCAL subtable, " << "could not write AIPS TY table and AIPS GC table" << LogIO::POST; } else { Table syscal = handleSysCal(_ms, spwids, isSubset); os << LogIO::NORMAL << "writing AIPS TY table" << LogIO::POST; Bool bk = _writeTY( fitsOutput, _ms, syscal, spwidMap, nrspw, _combineSpw ); if (! bk) { os << LogIO::WARN << "Could not write TY table\n" << LogIO::POST; } else { os << LogIO::NORMAL << "Writing AIPS GC table" << LogIO::POST; bk = _writeGC( fitsOutput, _ms, syscal, spwidMap, nrspw, _combineSpw, _sensitivity, refPixelFreq, refFreq, chanbw ); } if (!bk) { os << LogIO::WARN << "Could not write GC table\n" << LogIO::POST; } } } if (_ms.weather().tableDesc().ncolumn() != 0) { os << LogIO::NORMAL << "Writing AIPS WX table" << LogIO::POST; Bool bk = _writeWX(fitsOutput, _ms); if (!bk) { os << LogIO::WARN << "Could not write WX table" << LogIO::POST; } } /* * FIXME there are still some issues with this, but I have to move on to * another issue for now, so commenting out SYSPOWER table support until * I can work on it again * dmehring 17feb2021 * // support for adhoc NRAO SYSPOWER table static const String SYSPOWER = "SYSPOWER"; File syspower_f( _ms.tableName() + "/" + SYSPOWER); if (_ms.keywordSet().isDefined(SYSPOWER)) { const auto tableName = _ms.keywordSet().asTable("SYSPOWER").tableName(); if(Table::isReadable(tableName)) { Table syspower(tableName); os << LogIO::NORMAL << "Found SYSPOWER table" << LogIO::POST; if (_writeSY(fitsOutput, _ms, syspower, nrspw, spwidMap, _combineSpw)) { os << LogIO::NORMAL << "Wrote SY table" << LogIO::POST; } else { os << LogIO::WARN << "Could not write SY table" << LogIO::POST; } } } */ } Bool MSFitsOutput::writeFitsFile( const String& fitsfile, const MeasurementSet& ms, const String& column, Int startchan, Int nchan, Int stepchan, Bool writeSysCal, Bool asMultiSource, Bool combineSpw, Bool writeStation, Double sensitivity, const Bool padWithFlags, Int avgchan, uInt fieldNumber, Bool overwrite ) { // A FITS table can handle only Int nrows. if (ms.nrow() > static_cast(std::numeric_limits::max())) { throw AipsError("MS " + ms.tableName() + " is too big (#rows exceeds MAX_INT)"); } MSFitsOutput out(fitsfile, ms, column); out.setChannelInfo(startchan, nchan, stepchan, avgchan); out.setWriteSysCal(writeSysCal); out.setAsMultiSource(asMultiSource); out.setCombineSpw(combineSpw); out.setWriteStation(writeStation); out.setSensitivity(sensitivity); out.setPadWitFlags(padWithFlags); out.setFieldNumber(fieldNumber); out.setOverwrite(overwrite); out.write(); return True; } uInt MSFitsOutput::get_tbf_end(const uInt rownr, const uInt nrow, const uInt nif, const ScalarColumn& intimec, const ScalarColumn& timewidthcol, const ScalarColumn& inant1, const ScalarColumn& inant2, const Bool asMultiSource, const ScalarColumn& infieldid) { const Double maxTime = intimec(rownr) + 0.2 * timewidthcol(rownr); const Int startant1 = inant1(rownr); const Int startant2 = inant2(rownr); Int startfld = 0; if (asMultiSource) startfld = infieldid(rownr); uInt tbfend = rownr; for (uInt currrow = rownr + 1; currrow - rownr < nif && currrow < nrow; ++currrow) { if (intimec(currrow) <= maxTime && inant1(currrow) == startant1 && inant2(currrow) == startant2 && (!asMultiSource || infieldid(currrow) == startfld)) tbfend = currrow; else break; } return tbfend; } // Define a FITS random group parameter with default scaling and offset static void defineRandomParam(Record& ek, Int n, const String& name) { String ptype = "ptype" + String::toString(n); String pscal = "pscal" + String::toString(n); String pzero = "pzero" + String::toString(n); ek.define(ptype, name); ek.define(pscal, 1.0); ek.define(pzero, 0.0); } static void defineRandomParam(Record& ek, Int n, const String& name, const String& comment) { String ptype = "ptype" + String::toString(n); defineRandomParam(ek, n, name); ek.setComment(ptype, comment); } std::shared_ptr MSFitsOutput::_writeMain(Int& refPixelFreq, Double& refFreq, Double& chanbw, const String &outFITSFile, const Block& spwidMap, Int nrspw, const Block& fieldidMap, Bool asMultiSource ) const { Int avgchan = _avgChan < 0 ? 1 : _avgChan; std::shared_ptr outfile(nullptr); LogIO os(LogOrigin("MSFitsOutput", __func__)); const uInt nrow = _ms.nrow(); if (nrow == 0) { os << LogIO::SEVERE << "Empty measurement set!" << LogIO::POST; return 0; } Record ek; // ek == extra keys Vector radec; String objectname; { // field table info MSField fieldTable(_ms.field()); MSFieldColumns msfc(fieldTable); if (asMultiSource) { // UVFITS expects zeros for multi-source radec = Vector (2, 0.0); } else { // Use the actual RA/Decl radec = msfc.phaseDirMeas(_fieldNumber).getAngle().getValue(); radec *= 180.0 / M_PI; // convert to degrees for FITS if (radec(0) < 0) { radec(0) += 360.0; } objectname = msfc.name()(_fieldNumber); } uInt myfield = asMultiSource ? 0 : _fieldNumber; Bool foundEpoch = False; String dirtype = msfc.phaseDirMeas(myfield).getRefString(); if (dirtype.contains("2000")) { ek.define("epoch", 2000.0); foundEpoch = True; } else if (dirtype.contains("1950")) { ek.define("epoch", 1950.0); foundEpoch = True; } if (!foundEpoch) { os << LogIO::SEVERE << "Cannot deduce MS epoch. Assuming J2000" << LogIO::POST; ek.define("epoch", 2000.0); } } // First scan the SPECTRAL_WINDOW table to make sure that the data // shape is constant, the correlation type is constant, and that the // frequencies can be represented as f = f0 + i*inc MSDataDescription ddTable = _ms.dataDescription(); MSSpectralWindow spectralTable = _ms.spectralWindow(); MSPolarization polTable = _ms.polarization(); MSSource srcTable; uInt nsrc = 0; try { srcTable = _ms.source(); nsrc = srcTable.nrow(); } catch (const std::exception& x) { os << LogOrigin("MSFitsOutput", __func__) << LogIO::WARN << "No source table in MS. " << x.what() << LogIO::POST; } const uInt ndds = ddTable.nrow(); const uInt nspec = spectralTable.nrow(); const uInt npol = polTable.nrow(); if (ndds == 0) { os << LogIO::SEVERE << "No data description table in MS" << LogIO::POST; return 0; } if (nspec == 0) { os << LogIO::SEVERE << "No spectral window table in MS" << LogIO::POST; return 0; } if (npol == 0) { os << LogIO::SEVERE << "No polarization table in MS" << LogIO::POST; return 0; } ScalarColumn spwId(ddTable, MSDataDescription::columnName( MSDataDescription::SPECTRAL_WINDOW_ID)); ScalarColumn polId(ddTable, MSDataDescription::columnName( MSDataDescription::POLARIZATION_ID)); ScalarColumn numcorr(polTable, MSPolarization::columnName( MSPolarization::NUM_CORR)); ScalarColumn numchan(spectralTable, MSSpectralWindow::columnName( MSSpectralWindow::NUM_CHAN)); ArrayColumn frequencies(spectralTable, MSSpectralWindow::columnName(MSSpectralWindow::CHAN_FREQ)); ArrayColumn stokesTypes(polTable, MSPolarization::columnName( MSPolarization::CORR_TYPE)); ScalarColumn totalbw(spectralTable, MSSpectralWindow::columnName( MSSpectralWindow::TOTAL_BANDWIDTH)); ScalarColumn measFreq(spectralTable, MSSpectralWindow::columnName( MSSpectralWindow::MEAS_FREQ_REF)); Double restFreq(0.0); if (nsrc > 0 && srcTable.isColumn(MSSource::REST_FREQUENCY)) { ArrayColumn restfreqcol(srcTable, MSSource::columnName( MSSource::REST_FREQUENCY)); if (restfreqcol.isDefined(0) && restfreqcol(0).nelements() > 0) { IPosition ip = restfreqcol(0).shape(); ip = 0; restFreq = restfreqcol(0)(ip); } } Vector antnumbers; _handleAntNumbers(_ms, antnumbers); Int maxant = max(antnumbers); // Also find out what the Stokes are and make sure that they are the same // throughout the MS. In principle we could handle the same stokes in // different order by transposing, but this may well never happen. Int numcorr0 = 0; Int numchan0 = 0; Double delta = 0; Double f0 = 0; Double bw0 = 0; Vector stokes; Int measFreq0 = -1; uInt i; Int nchan = _nchan; Int chanstep = _stepChan; Int chanstart = _startChan; for (i = 0; i < ndds; i++) { if (i < spwidMap.nelements() && spwidMap[i] >= 0) { const Int s = spwId(i); const Int p = polId(i); // Get channel width. Vector freqs = frequencies(s); if (freqs.nelements() > 1) { delta = freqs(1) - freqs(0); } else { delta = totalbw(0); } // If first time, set the various values. if (numcorr0 == 0) { numcorr0 = numcorr(p); numchan0 = numchan(s); if (numcorr0 <= 0 || numchan0 <= 0) { os << LogIO::SEVERE << "Number of correlations or channels is zero" << LogIO::POST; return 0; } f0 = freqs(0); bw0 = delta; chanbw = abs(delta); stokes = stokesTypes(p); if ( (_nchan > 0) && (_stepChan > 0) && (_startChan >= 0) && ((_nchan * _stepChan + _startChan) <= numchan0) ) { f0 = freqs(_startChan); bw0 = delta * _stepChan; } else { nchan = numchan0; chanstep = 1; chanstart = 0; } measFreq0 = measFreq(s); } // Check if values match. if (numcorr(p) != numcorr0) { os << LogIO::SEVERE << "Number of correlations varies in the MS" << LogIO::POST; return 0; } if (numchan(s) != numchan0) { os << LogIO::SEVERE << "Number of channels varies in the MS, i.e. the is more than one SPW shape!" << endl << "Please split out SPWs of identical shape and export them separately." << LogIO::POST; return 0; } if (!allEQ(stokes, stokesTypes(p))) { os << LogIO::SEVERE << "Stokes types vary for different spectral windows" << LogIO::POST; return 0; } if (!near(abs(delta), chanbw, 1.0e-5)) { os << LogIO::SEVERE << "Bandwidth varies across spectral windows" << LogIO::POST; return 0; } if (nchan > 1) { Vector selChans(nchan); for (uInt j = 0; j < (uInt)nchan; ++j) { uInt k = chanstart + j * chanstep; selChans(j) = freqs(k); } delta = selChans(1) - selChans(0); for (uInt j = 1; j < selChans.nelements(); ++j) { if (!near(delta, selChans(j) - selChans(j - 1), 1.0e-5)) { os << LogIO::SEVERE << "Channel width varies across the band" << LogIO::POST; return 0; } } } if (measFreq(s) != measFreq0) { os << LogIO::SEVERE << "Frequency frame varies in the MS" << LogIO::POST; return 0; } } } Int f0RefPix(0); f0RefPix = 1 + nchan / 2; if (f0RefPix == 1) { // single-channel out refFreq = f0 + bw0 / 2.0 - delta / 2.0; } else { // multi-channel out (f0RefPix is a *one* - based index!) refFreq = f0 + (f0RefPix - 1) * bw0; } refPixelFreq = f0RefPix; // OK, turn the stokes into FITS values. for (Int j = 0; j < numcorr0; j++) { stokes(j) = Stokes::FITSValue(Stokes::StokesTypes(stokes(j))); } // OK, get an index vector that sorts these in ascending order if // stokes(0) >= 0, or descending order if < 0. Vector stokesIndex(numcorr0); if (stokes(0) >= 0) { GenSortIndirect::sort(stokesIndex, stokes); } else { GenSortIndirect::sort(stokesIndex, stokes, Sort::Descending); } // OK, make sure that we can represent the stokes in FITS if (stokes.nelements() > 2) { Int delta = stokes(stokesIndex(1)) - stokes(stokesIndex(0)); for (i = 2; i < stokes.nelements(); i++) { if (stokes(stokesIndex(i)) - stokes(stokesIndex(i - 1)) != delta) { os << LogIO::SEVERE << "These STOKES are not representable in FITS" << LogIO::POST; return 0; } } } // DATA: COMPLEX(2)+WEIGHT, NUM_CORR, NUM_CHAN, IF, RA, DEC RecordDesc desc; String columnName; String col = _column; col.upcase(); if (col == "OBSERVED" || col == MS::columnName(MS::DATA)) { columnName = MS::columnName(MS::DATA); os << "Writing DATA column" << LogIO::POST; } else if (col == "MODEL" || col == "MODEL_DATA") { if (_ms.tableDesc().isColumn("MODEL_DATA")) { columnName = "MODEL_DATA"; os << "Writing MODEL_DATA column" << LogIO::POST; } else { columnName = MS::columnName(MS::DATA); os << LogIO::SEVERE << "MODEL_DATA does not exist, writing DATA" << LogIO::POST; } } else if (col == "CORRECTED" || col == "CORRECTED_DATA") { if (_ms.tableDesc().isColumn("CORRECTED_DATA")) { columnName = "CORRECTED_DATA"; os << "Writing CORRECTED_DATA column" << LogIO::POST; } else { columnName = MS::columnName(MS::DATA); os << LogIO::NORMAL << "CORRECTED_DATA does not exist, writing DATA" << LogIO::POST; } } else { columnName = MS::columnName(MS::DATA); os << LogIO::SEVERE << "Unrecognized column " << _column << ", writing DATA" << LogIO::POST; } // Does the MS have a WEIGHT_SPECTRUM? Bool hasWeightArray = _ms.tableDesc(). isColumn(MS::columnName( MS::WEIGHT_SPECTRUM)); if (hasWeightArray) { MSMainColumns tempCols(_ms); if (!tempCols.weightSpectrum().isDefined(0)) hasWeightArray = False; } IPosition dataShape(6, 3, numcorr0, nchan, 1, 1, 1); if (_combineSpw) { dataShape(3) = nrspw; } desc.addField("data", TpArrayFloat, dataShape); // Random Parameters // UU VV WW desc.addField("u", TpFloat); desc.addField("v", TpFloat); desc.addField("w", TpFloat); // DATE desc.addField("date1", TpFloat); desc.addField("date2", TpFloat); // BASELINE if maximum antenna number < 256 // SUBARRAY, ANTENNA1 and ANTENNA2 otherwise // (see AIPS memo 117, section 3.1.2) if (maxant < 256) { desc.addField("baseline", TpFloat); } else { desc.addField("subarray", TpFloat); desc.addField("antenna1", TpFloat); desc.addField("antenna2", TpFloat); } // FREQSEL ScalarColumn inddid(_ms, MS::columnName(MS::DATA_DESC_ID)); desc.addField("freqsel", TpFloat); // SOURCE and INTTIM only in multi-source table if (asMultiSource) { desc.addField("source", TpFloat); desc.addField("inttim", TpFloat); } // "Optional" keywords // BSCALE BZERO BUNIT ek.define("bscale", 1.0); ek.define("bzero", 0.0); String bunit = "UNCALIB"; { TableColumn indata(_ms, columnName); if (indata.keywordSet().isDefined("QuantumUnit") && indata.keywordSet().dataType("QuantumUnit") == TpString) { indata.keywordSet().get("QuantumUnit", bunit); bunit.upcase(); } } ek.define("bunit", bunit); // CTYPE CRVAL CDELT CRPIX CROTA ek.define("ctype2", "COMPLEX"); ek.define("crval2", 1.0); ek.define("cdelt2", 1.0); ek.define("crpix2", 1.0); ek.define("crota2", 0.0); ek.define("ctype3", "STOKES"); ek.define("crval3", stokes(stokesIndex(0)) * 1.0); if (stokes.nelements() > 1) { ek.define("cdelt3", (stokes(stokesIndex(1)) - stokes(stokesIndex(0))) * 1.0); } else { ek.define("cdelt3", 1.0); } ek.define("crpix3", 1.0); ek.define("crota3", 0.0); ek.define("ctype4", "FREQ"); ek.define("crval4", refFreq); ek.define("cdelt4", bw0); ek.define("crpix4", Double(refPixelFreq)); ek.define("crota4", 0.0); ek.define("ctype5", "IF"); ek.define("crval5", 1.0); ek.define("cdelt5", 1.0); ek.define("crpix5", 1.0); ek.define("crota5", 0.0); ek.define("ctype6", "RA"); ek.define("crval6", radec(0)); ek.define("cdelt6", 1.0); ek.define("crpix6", 1.0); ek.define("crota6", 0.0); ek.define("ctype7", "DEC"); ek.define("crval7", radec(1)); ek.define("cdelt7", 1.0); ek.define("crpix7", 1.0); ek.define("crota7", 0.0); // PTYPE PSCALE PZERO Int idx = 1; defineRandomParam(ek, idx++, "UU"); defineRandomParam(ek, idx++, "VV"); defineRandomParam(ek, idx++, "WW"); defineRandomParam(ek, idx++, "DATE", "Day number"); defineRandomParam(ek, idx++, "DATE", "Day fraction"); if (maxant < 256) { defineRandomParam(ek, idx++, "BASELINE"); } else { defineRandomParam(ek, idx++, "SUBARRAY"); defineRandomParam(ek, idx++, "ANTENNA1"); defineRandomParam(ek, idx++, "ANTENNA2"); } defineRandomParam(ek, idx++, "FREQSEL"); if (asMultiSource) { defineRandomParam(ek, idx++, "SOURCE"); defineRandomParam(ek, idx++, "INTTIM"); } // EXTEND - already written by FITSGroupWriter // ek.define("extend", True); // BLOCKED - already written by FITSGroupWriter // ek.define("blocked", True); // OBJECT if (asMultiSource) { ek.define("object", "MULTI"); } else { ek.define("object", objectname); } // OBS-TIME { ScalarColumn intm(_ms, MS::columnName(MS::TIME)); ek.define("date-obs", toFITSDate(intm(0) / C::day)); // First time entry } // TELESCOP INSTRUME MSObservationColumns obsC(_ms.observation()); if (obsC.nrow() == 0) { os << LogIO::SEVERE << "No Observation info!" << LogIO::POST; return 0; } ek.define("telescop", obsC.telescopeName()(0)); ek.define("instrume", obsC.telescopeName()(0)); ek.define("observer", obsC.observer()(0)); ek.define("sortord", "TB"); // Write a WCS keyword to indicate the frequency frame // in which the Freq axis (and FQ table) is defined String fframe = MFrequency::showType(measFreq0); if (fframe == "TOPO" || fframe == "BARY") fframe = fframe + "CENT"; else if (fframe == "GEO") fframe = fframe + "CENTR"; else if (fframe == "GALACTO") fframe = fframe + "C"; else if (fframe == "REST") fframe = "SOURCE"; os << LogIO::NORMAL << "Frequency reference frame is " << fframe << LogIO::POST; ek.define("specsys", fframe); if (restFreq > 0.0) ek.define("restfreq", restFreq); // TBD: NEED TO WRITE VELREF AND ALT* KEYWORDS HERE?! // Miriad needs a weight scale factor (otherwise all weights get 0). // It is the proper AIPS way to do it as a history record. ek.define("history", "AIPS WTSCAL = 1.0"); // Similarly, record the sort order (the following didn't work....) // ek.define("history aips sort order", "TB"); Bool deleteIptr; Matrix indatatmp(IPosition(2, numcorr0, numchan0)); const Complex *iptr = indatatmp.getStorage(deleteIptr); Bool deleteWtPtr; Matrix inwttmp(numcorr0, numchan0); const Float *wptr = inwttmp.getStorage(deleteWtPtr); Bool deleteFlagPtr; Matrix inflagtmp(IPosition(2, numcorr0, numchan0)); const Bool *fptr = inflagtmp.getStorage(deleteFlagPtr); Bool deleteIndPtr; const uInt *indptr = stokesIndex.getStorage(deleteIndPtr); // Do we need to check units? I think the MS rules are that units cannot // be changed. Vector uvw(3); Int day; Double dayFraction; const Double oneOverC = 1.0 / C::c; // Sort the table in order of TIME, ANTENNA1, ANTENNA2, FIELDID, SPWID. // Iterate through the table on the first 4 fields. Block sortNames(_combineSpw ? 4 : 5); sortNames[0] = MS::columnName(MS::TIME_CENTROID); sortNames[1] = MS::columnName(MS::ANTENNA1); sortNames[2] = MS::columnName(MS::ANTENNA2); sortNames[3] = MS::columnName(MS::FIELD_ID); Vector sortIndex; if (_combineSpw) { // combineSpw will do its own // DATA_DESC_ID sorting. sortIndex.resize(nrow); } else { sortNames[4] = MS::columnName(MS::DATA_DESC_ID); } Table sortTable = _ms.sort(sortNames); // Make objects for the various columns. ArrayColumn indata(sortTable, columnName); ArrayColumn inweightscalar(sortTable, MS::columnName(MS::WEIGHT)); ArrayColumn inweightarray; if (hasWeightArray) { inweightarray.attach(sortTable, MS::columnName(MS::WEIGHT_SPECTRUM)); } ScalarColumn inrowflag(sortTable, MS::columnName(MS::FLAG_ROW)); ArrayColumn indataflag(sortTable, MS::columnName(MS::FLAG)); ArrayColumn inuvw(sortTable, MS::columnName(MS::UVW)); ScalarColumn intimec(sortTable, MS::columnName(MS::TIME_CENTROID)); ScalarColumn inant1(sortTable, MS::columnName(MS::ANTENNA1)); ScalarColumn inant2(sortTable, MS::columnName(MS::ANTENNA2)); ScalarColumn inarray(sortTable, MS::columnName(MS::ARRAY_ID)); ScalarColumn inspwinid(sortTable, MS::columnName(MS::DATA_DESC_ID)); ScalarColumn inexposure; ScalarColumn infieldid; if (asMultiSource) { infieldid.attach(sortTable, MS::columnName(MS::FIELD_ID)); // Why is exposure only done for multisource files? inexposure.attach(sortTable, MS::columnName(MS::EXPOSURE)); } uInt nif = 1; Bool padWithFlags = _padWithFlags; if (_combineSpw) { nif = nrspw; } if (nif < 2) { padWithFlags = False; } ScalarColumn ininterval; if (padWithFlags) ininterval.attach(sortTable, MS::columnName(MS::INTERVAL)); // (another) check whether the SPWs naturally fit the IF paradigm. Do this // before creating the writer so that a partial UVFITS file isn't left on // disk if this exits. Vector expectedDDIDs; uInt nOutRow = nrow; Vector tbfends; if (_combineSpw) { // Prepare a list of the expected DDIDs as a function of rownr % nif. // If inspwinid(rownr) != expectedDDIDs[rownr % nif], something has gone // wrong (probably combinespw && multiple tunings, CAS-2048). expectedDDIDs.resize(nif); expectedDDIDs.set(0); // Default, but would catching errors with -1 be better? uInt ifnum = 0; for (uInt i = 0; i < ndds; ++i) { if (i < spwidMap.nelements() && spwidMap[i] >= 0) { if (ifnum < nif) { expectedDDIDs[ifnum] = i; ++ifnum; } else { os << LogIO::WARN << "spwidMap selects more spws than there are IFs. Expect problems." << LogIO::POST; } } } if (nif > 1) { // Don't bother counting all the inspwinids Vector nperIF; // unless there is > 1 kind. nperIF.resize(nif); nperIF.set(0); tbfends.resize(nrow); uInt rownr = 0; while (rownr < nrow) { uInt tbfend = rownr + nif - 1; if (padWithFlags) { tbfend = get_tbf_end(rownr, nrow, nif, intimec, asMultiSource ? inexposure : ininterval, inant1, inant2, asMultiSource, infieldid); nOutRow += nif - (tbfend + 1 - rownr); // Increment by # of padded rows. } else if (tbfend >= nrow) tbfend = nrow - 1; Vector miniDDIDs; Vector miniSort; uInt nrowsThisTBF = tbfend + 1 - rownr; miniDDIDs.resize(nrowsThisTBF); miniSort.resize(nrowsThisTBF); for (uInt rowInTBF = 0; rowInTBF < nrowsThisTBF; ++rowInTBF) { miniDDIDs[rowInTBF] = spwidMap[inspwinid(rownr + rowInTBF)]; ++nperIF[miniDDIDs[rowInTBF]]; } GenSortIndirect::sort(miniSort, miniDDIDs); for (uInt rowInTBF = 0; rowInTBF < nrowsThisTBF; ++rowInTBF) { sortIndex[rownr] = rownr + miniSort[rowInTBF] - rowInTBF; tbfends[rownr] = tbfend; ++rownr; } } os << LogIO::DEBUG1 << "rownr = " << rownr << LogIO::POST; os << LogIO::DEBUG1 << "nrow = " << nrow << LogIO::POST; os << LogIO::DEBUG1 << "nOutRow * nif = " << nOutRow << LogIO::POST; if (nOutRow % nif) { os << LogIO::SEVERE << "The expected # of output rows, " << nOutRow << " is not a multiple of the number of IFs, " << nif << ".\n" << "Expect problems." << LogIO::POST; // Commented out for now. // return 0; } nOutRow /= nif; if (!padWithFlags) { Bool haveProblem = false; for (uInt m = 1; m < nif; ++m) { if (nperIF[m] != nperIF[0]) { haveProblem = true; break; } } if (haveProblem) { os << LogIO::SEVERE << "The number of rows per spectral window varies:\n" << " Output SpW # of rows\n"; for (uInt m = 0; m < nif; ++m) os << " " << m << " " << nperIF[m] << "\n"; os << " the spectral windows cannot be combined without padwithflags." << LogIO::POST; return 0; } } else { os << LogIO::NORMAL << outFITSFile << " will be " << 100.0 * (1.0 - nrow / static_cast (nOutRow * nif)) << "% padded by flags to fit into IFs." << LogIO::POST; } } } // Finally, make the writer. If it breaks past this point, the user gets to // look at the pieces. FITSGroupWriter writer(outFITSFile, desc, nOutRow, ek, False); outfile.reset(writer.writer()); // DATA - out RecordFieldPtr > odata(writer.row(), "data"); Bool deleteOptr; Float *optr = (*odata).getStorage(deleteOptr); os << LogIO::DEBUG1 << "output data shape = " << writer.row().asArrayFloat( writer.row().fieldNumber("data")).shape() << " " << writer.row().asArrayFloat("data").shape() << " " << writer.row().asArrayFloat(writer.row().fieldNumber("data")).data() << " " << LogIO::POST; RecordFieldPtr ouu(writer.row(), "u"); RecordFieldPtr ovv(writer.row(), "v"); RecordFieldPtr oww(writer.row(), "w"); RecordFieldPtr odate1(writer.row(), "date1"); RecordFieldPtr odate2(writer.row(), "date2"); RecordFieldPtr obaseline; RecordFieldPtr osubarray; RecordFieldPtr oantenna1; RecordFieldPtr oantenna2; if (maxant < 256) { obaseline = RecordFieldPtr (writer.row(), "baseline"); } else { osubarray = RecordFieldPtr (writer.row(), "subarray"); oantenna1 = RecordFieldPtr (writer.row(), "antenna1"); oantenna2 = RecordFieldPtr (writer.row(), "antenna2"); } RecordFieldPtr ofreqsel(writer.row(), "freqsel"); RecordFieldPtr osource; RecordFieldPtr ointtim; if (asMultiSource) { osource = RecordFieldPtr (writer.row(), "source"); ointtim = RecordFieldPtr (writer.row(), "inttim"); } // Check if first cell has a WEIGHT of correct shape. if (hasWeightArray) { IPosition shp = inweightarray.shape(0); if (shp.nelements() > 0 && !shp.isEqual(inwttmp.shape())) { hasWeightArray = False; os << LogIO::WARN << "WEIGHT_SPECTRUM is ignored (incorrect shape)" << LogIO::POST; } } // Loop through all rows. ProgressMeter meter(0.0, nOutRow * 1.0, "UVFITS Writer", "Rows copied", "", "", True, nOutRow / 100); uInt tbfrownr = 0; // Input row # of (time, baseline, field). uInt outrownr = 0; // Output row #. //Double lasttime(0.0); Vector realcorr(numcorr0); Vector imagcorr(numcorr0); Vector wgtaver(numcorr0); Vector realcorrf(numcorr0); Vector imagcorrf(numcorr0); Vector wgtaverf(numcorr0); Int old_nspws_found = -1; // Just for debugging curiosity. while (tbfrownr < nrow) { if (outrownr >= nOutRow) { // Shouldn't happen, but just in case... os << LogIO::WARN << "The loop over output rows failed to stop when expected...stopping it now." << LogIO::POST; break; } // Will only write a record if some non-flagged data found // Bool dowrite(True); // temporarily disable, because FITSGroupWriter chokes Float* outptr = optr; // reset for each spectral-window // Loop over the IFs, whether or not the corresponding spws are present for // this (time, baseline, field). // rownr should only be used inside this loop; use tbfrownr outside. uInt rawrownr = tbfrownr; // Essentially tbfrownr + m - # of missing spws // so far. uInt rownr = rawrownr; uInt tbfend = tbfrownr + nif - 1; if (_combineSpw && nif > 1) { tbfend = tbfends[rownr]; rownr = sortIndex[rawrownr]; } for (uInt m = 0; m < nif; ++m) { Bool rowFlag; // FLAG_ROW if (_combineSpw && (rownr >= nrow // flag remaining IFs in tbfrownr || inspwinid(rownr) != expectedDDIDs[m])) { if (padWithFlags) { // Save this row for the next one, and fill in with flagged junk. indatatmp.set(0.0); // DATA matrix //indata.get(rownr, indatatmp); // DATA matrix rowFlag = true; inflagtmp.set(true); inwttmp.set(0.0); // Don't update lasttime. } else { os << LogIO::SEVERE << "A DATA_DESC_ID appeared out of the expected order.\n" << "MSes with multiple tunings (i.e. spw varies with time) cannot" << "\nbe exported with combinespw. Export each tuning separately." << LogIO::POST; return 0; } } else { // The spw is present, use it. if (rownr >= nrow) { // Shouldn't happen, but just in case... os << LogIO::WARN << "The loop over input rows failed to stop when expected...stopping it now." << LogIO::POST; break; } indata.get(rownr, indatatmp); // DATA matrix rowFlag = inrowflag(rownr); indataflag.get(rownr, inflagtmp); // FLAG // WEIGHT_SPECTRUM (defaults to WEIGHT) Bool getwt = True; if (hasWeightArray) { IPosition shp = inweightarray.shape(rownr); if (shp.isEqual(inwttmp.shape())) { inweightarray.get(rownr, inwttmp); getwt = False; } } if (getwt) { //weight_spectrum may not exist but flag and data always will. IPosition shp = indatatmp.shape(); Int nchan = shp(1); // either num of channels of num of lags //cout << "shp1=" << shp << " shp2=" << inflagtmp.shape() // << " nchan=" << nchan << endl; if (nchan < 1) nchan = 1; const Vector wght = inweightscalar(rownr); for (Int p = 0; p < numcorr0; p++) { inwttmp.row(p) = wght(p) / nchan; } } /* Double rtime(86400.0*floor(intimec(0)/86400.0)); cout << "rtime = " << rtime << " " << "nrows = " << intimec.nrow() << endl; cout << "time = " << intimec(rownr)-rtime; if (intimec(rownr)!=lasttime) cout << " ***NEW*** "; cout << endl; */ // lasttime = intimec(rownr); if (! padWithFlags || rawrownr <= tbfend) { ++rawrownr; // register that the spw was present. rownr = _combineSpw && nif > 1 ? sortIndex[rawrownr] : rawrownr; } } // We should optimize this loop more, probably do frequency as // the inner loop? realcorr.set(0); imagcorr.set(0); wgtaver.set(0); realcorrf.set(0); imagcorrf.set(0); wgtaverf.set(0); Int chancounter = 0; Vector flagcounter(numcorr0); flagcounter.set(0); //cout << "chanstart=" << chanstart << " nchan=" << nchan // << " chanstep=" << chanstep << " avgchan=" << avgchan << endl; for (Int k = chanstart; k < (nchan * chanstep + chanstart); k += chanstep) { //cout << "row = " << tbfrownr << " " // << outptr << " " << optr << " " << outptr-optr << " "; // << "ddi = " << m << " (" << inspwinid(i) << ") " // << "chan = " << k << " / " // << boolalpha // << "hasWeightArray = " << hasWeightArray << " " // << "wt(chan) = " << inwttmp.column(k) << "; " // << "flag(chan) = " << inflagtmp.column(k) << " " // << "shapes: " // << indatatmp.shape() << " " // << inwttmp.shape() << " " // << inflagtmp.shape() << " " // << endl; /* if (chancounter != avgchan) { for (Int j = 0; j < numcorr0; j++) { Int offset = indptr[j] + k * numcorr0; //cout << "j=" << j << " real=" << iptr[offset].real() // << " imag=" << iptr[offset].imag() << endl; if (!fptr[offset]) { realcorr[j] += iptr[offset].real() * wptr[offset]; imagcorr[j] += iptr[offset].imag() * wptr[offset]; wgtaver[j] += wptr[offset]; flagcounter++; } else { realcorrf[j] += iptr[offset].real() * wptr[offset]; imagcorrf[j] += iptr[offset].imag() * wptr[offset]; wgtaverf[j] += wptr[offset]; } //cout << "j=" << j << " k=" << k // << " real=" << realcorr[j] << " image=" << imagcorr[j] // << " offset=" << offset << " chancounter=" << chancounter << endl; } ++chancounter; } if (chancounter == avgchan) { for (Int j = 0; j < numcorr0; j++) { if (wgtaver[j] > 0) { outptr[0] = realcorr[j] / wgtaver[j]; outptr[1] = imagcorr[j] / wgtaver[j]; outptr[2] = wgtaver[j] / flagcounter * numcorr0; } else if (wgtaverf[j] > 0) { outptr[0] = realcorrf[j] / wgtaverf[j]; outptr[1] = imagcorrf[j] / wgtaverf[j]; outptr[2] = -wgtaverf[j] / avgchan; } else { outptr[0] = realcorrf[j] / avgchan; outptr[1] = imagcorrf[j] / avgchan; outptr[2] = 0; } if (rowFlag) { //calculate the average even if row flagged, just in case //unflag the row and it has some reasonable data there outptr[2] = -abs(outptr[2]); } outptr += 3; } realcorr.set(0); imagcorr.set(0); wgtaver.set(0); realcorrf.set(0); imagcorrf.set(0); wgtaverf.set(0); chancounter = 0; flagcounter = 0; } */ if (chancounter != avgchan) { for (Int j = 0; j < numcorr0; j++) { Int offset = indptr[j] + k * numcorr0; //cout << "j=" << j << " real=" << iptr[offset].real() // << " imag=" << iptr[offset].imag() << endl; if (!fptr[offset]) { realcorr[j] += iptr[offset].real(); imagcorr[j] += iptr[offset].imag(); wgtaver[j] += wptr[offset]; flagcounter[j]++; } else { realcorrf[j] += iptr[offset].real(); imagcorrf[j] += iptr[offset].imag(); wgtaverf[j] += wptr[offset]; } //cout << "j=" << j << " k=" << k // << " real=" << realcorr[j] << " image=" << imagcorr[j] // << " offset=" << offset << " chancounter=" << chancounter << endl; } ++chancounter; } if (chancounter == avgchan) { for (Int j = 0; j < numcorr0; j++) { if (flagcounter[j] > 0) { outptr[0] = realcorr[j] / flagcounter[j]; outptr[1] = imagcorr[j] / flagcounter[j]; outptr[2] = wgtaver[j] / flagcounter[j]; } else if (wgtaverf[j] > 0) { outptr[0] = realcorrf[j] / avgchan; outptr[1] = imagcorrf[j] / avgchan; outptr[2] = -wgtaverf[j] / avgchan; } else { outptr[0] = realcorrf[j] / avgchan; outptr[1] = imagcorrf[j] / avgchan; outptr[2] = 0; } if (rowFlag) { //calculate the average even if row flagged, just in case //unflag the row and it has some reasonable data there outptr[2] = -abs(outptr[2]); } outptr += 3; } realcorr.set(0); imagcorr.set(0); wgtaver.set(0); realcorrf.set(0); imagcorrf.set(0); wgtaverf.set(0); chancounter = 0; flagcounter.set(0); } } // if(nOutRow - outtbfrownr < 5) // os << LogIO::DEBUG1 << "(outtbfrownr, m) = (" << outtbfrownr // << ", " << m << ")" << LogIO::POST; } // Ends loop over IFs. // If found data at this timestamp, write it out // if (dowrite) { // Random parameters // UU VV WW inuvw.get(tbfrownr, uvw); *ouu = uvw(0) * oneOverC; *ovv = uvw(1) * oneOverC; *oww = uvw(2) * oneOverC; // TIME timeToDay(day, dayFraction, intimec(tbfrownr)); *odate1 = day; *odate2 = dayFraction; // BASELINE if (maxant < 256) { *obaseline = antnumbers(inant1(tbfrownr)) * 256 + antnumbers(inant2(tbfrownr)) + inarray(tbfrownr) * 0.01; } else { *osubarray = inarray(tbfrownr) + 1; *oantenna1 = antnumbers(inant1(tbfrownr)); *oantenna2 = antnumbers(inant2(tbfrownr)); } // FREQSEL (in the future it might be FREQ_GRP+1) // *ofreqsel = inddid(i) + 1; *ofreqsel = _combineSpw ? 1 : 1 + spwidMap[inspwinid(tbfrownr)]; // SOURCE // INTTIM if (asMultiSource) { *osource = 1 + fieldidMap[infieldid(tbfrownr)]; *ointtim = inexposure(tbfrownr); } writer.write(); ++outrownr; meter.update(outrownr); // How many spws showed up for this (time_centroid, ant1, ant2, field)? if (rawrownr == tbfrownr) { os << LogIO::WARN << "No spectral windows were present for row # " << tbfrownr << "\n" << " input (time_centroid, ant1, ant2, field) =\n" << " (" << intimec(tbfrownr) << ", " << inant1(tbfrownr) << ", " << inant2(tbfrownr) << ", " << infieldid(tbfrownr) << ")" << LogIO::POST; } else { Int nspws_found = rawrownr - tbfrownr; // Just for debugging curiosity. if (nspws_found != old_nspws_found) { old_nspws_found = nspws_found; os << LogIO::DEBUG1 << "Beginning with row # " << tbfrownr << LogIO::POST; os << LogIO::DEBUG1 << " input (time_centroid, ant1, ant2, field) =" << LogIO::POST; // intimec is in modified julian day seconds, but Time::Time() takes // julian days. Double mjd_in_s = intimec(tbfrownr); Time juldate(2400000.5 + mjd_in_s / 86400.0); os << LogIO::DEBUG1 << " (" << juldate.year() << "-"; if (juldate.month() < 10) os << "0"; os << juldate.month() << "-"; if (juldate.dayOfMonth() < 10) os << "0"; os << juldate.dayOfMonth() << "-"; if (juldate.hours() < 10) // Time stores things internally as days. os << "0"; // Do we really want to use it for sub-day units os << juldate.hours() << ":"; // when we start with intimec in s? if (juldate.minutes() < 10) os << "0"; os << juldate.minutes() << ":"; mjd_in_s -= 60.0 * static_cast (mjd_in_s / 60.0); os << mjd_in_s; os << ", " << inant1(tbfrownr) << ", " << inant2(tbfrownr) << ", " // infieldid is unattached and segfaultable if !asMultiSource. << (asMultiSource ? infieldid(tbfrownr) : 0) << "):" << LogIO::POST; os << LogIO::DEBUG1 << nspws_found << " spws present out of " << nif << " IFs." << LogIO::POST; } tbfrownr = rawrownr; // Increment it by the # of spws found. } // } // dowrite } os << LogIO::DEBUG1 << "tbfrownr = " << tbfrownr << LogIO::POST; os << LogIO::DEBUG1 << "outrownr = " << outrownr << LogIO::POST; os << LogIO::DEBUG1 << "nrow = " << nrow << LogIO::POST; os << LogIO::DEBUG1 << "nOutRow = " << nOutRow << LogIO::POST; // changing chanbw to output one chanbw = bw0; return outfile; } Bool MSFitsOutput::_writeFQ(std::shared_ptr output, const MeasurementSet &ms, const Block& spwidMap, Int nrspw, Double refFreq, Int refPixelFreq, Double chanbw, Bool combineSpw, Int chanstart, Int nchan, Int chanstep, Int avgchan) { LogIO os(LogOrigin("MSFitsOutput", "writeFQ")); MSSpectralWindow specTable(ms.spectralWindow()); ArrayColumn inchanfreq(specTable, MSSpectralWindow::columnName( MSSpectralWindow::CHAN_FREQ)); ScalarColumn intotbw(specTable, MSSpectralWindow::columnName( MSSpectralWindow::TOTAL_BANDWIDTH)); ScalarColumn insideband(specTable, MSSpectralWindow::columnName( MSSpectralWindow::NET_SIDEBAND)); String telescopeName; { MSObservation obsTable(ms.observation()); if (obsTable.nrow() > 0) { ScalarColumn inarrayname( obsTable, MSObservation::columnName(MSObservation::TELESCOPE_NAME) ); telescopeName = inarrayname(0); } } // ##### Header Record header; // NO_IF const uInt nwin = specTable.nrow(); os << LogIO::NORMAL << "Found " << nrspw << " spectral windows " << LogIO::POST; // If all spw's are combined, we have a single freq group. // Otherwise each spectral-window is a group. IPosition shape(1, 1); Int nentr = nrspw; if (combineSpw) { shape(0) = nrspw; nentr = 1; } header.define("EXTNAME", "AIPS FQ"); // EXTNAME header.define("EXTVER", 1); // EXTVER header.define("NO_IF", Int(shape(0))); // NO_IF // Table description RecordDesc desc; Record stringLengths; // no strings Record units; desc.addField("FRQSEL", TpInt); // FRQSEL desc.addField("IF FREQ", TpArrayDouble, shape); // IF FREQ units.define("IF FREQ", "HZ"); desc.addField("CH WIDTH", TpArrayFloat, shape); // CH WIDTH units.define("CH WIDTH", "HZ"); desc.addField("TOTAL BANDWIDTH", TpArrayFloat, shape); // TOTAL BANDWIDTH units.define("TOTAL BANDWIDTH", "HZ"); desc.addField("SIDEBAND", TpArrayInt, shape); // SIDEBAND FITSTableWriter writer(output.get(), desc, stringLengths, nentr, header, units, False); RecordFieldPtr freqsel(writer.row(), "FRQSEL"); RecordFieldPtr > iffreq(writer.row(), "IF FREQ"); RecordFieldPtr > ifwidth(writer.row(), "CH WIDTH"); RecordFieldPtr > totbw(writer.row(), "TOTAL BANDWIDTH"); RecordFieldPtr > sideband(writer.row(), "SIDEBAND"); if (avgchan < 1) avgchan = 1; if (avgchan > nchan) avgchan = nchan; IPosition inx(1, 0); for (uInt i = 0; i < nwin; i++) { if (i < spwidMap.nelements() && spwidMap[i] >= 0) { *freqsel = 1 + spwidMap[i]; Vector freqs = inchanfreq(i); if (telescopeName == "IRAM PDB" || telescopeName == "IRAM_PDB") { (*iffreq)(inx) = 0.0; } else { os << LogIO::DEBUG2 << "refPixelFreq=" << refPixelFreq << " refFreq=" << refFreq //<< "\nfreqs=" << freqs << LogIO::POST; Int chancounter = 0; Int nselectedchan = 0; for (Int k = chanstart; k < (nchan * chanstep + chanstart); k += chanstep) { if (++chancounter == avgchan) { nselectedchan++; chancounter = 0; } } if (nselectedchan == 0) nselectedchan = 1; Vector sfreq(nselectedchan); Double frq = 0; chancounter = 0; nselectedchan = 0; for (Int k = chanstart; k < (nchan * chanstep + chanstart); k += chanstep) { frq += freqs(k); chancounter++; if (chancounter == avgchan) { sfreq(nselectedchan) = frq / avgchan; nselectedchan++; chancounter = 0; frq = 0; } } os << LogIO::DEBUG2 << "\nsfreq=" << sfreq << LogIO::POST; (*iffreq)(inx) = sfreq(refPixelFreq - 1) - refFreq; } if (freqs.nelements() > 1) { if (telescopeName == "ALMA") { if (freqs(1) < freqs(0)) { (*ifwidth)(inx) = -abs(chanbw); } else { (*ifwidth)(inx) = abs(chanbw); } } else { (*ifwidth)(inx) = (chanbw); } } else { (*ifwidth)(inx) = intotbw(i); } (*totbw)(inx) = intotbw(i); if (telescopeName == "ALMA") { if (freqs(1) < freqs(0)) { (*sideband)(inx) = -1; } else { (*sideband)(inx) = 1; } } else { (*sideband)(inx) = insideband(i); } // Write the current row if not combined. if (combineSpw) { inx(0)++; } else { writer.write(); } } } // Write the row if everything is combined. if (combineSpw) { *freqsel = 1; writer.write(); } return True; } Bool MSFitsOutput::_writeAN(std::shared_ptr output, const MeasurementSet &ms, Double refFreq, Bool writeStation) { LogIO os(LogOrigin("MSFitsOutput", "writeAN")); MSObservation obsTable(ms.observation()); ScalarColumn inarrayname(obsTable, MSObservation::columnName( MSObservation::TELESCOPE_NAME)); const uInt narray = obsTable.nrow(); if (narray == 0) { os << LogIO::SEVERE << "No Observation info!" << LogIO::POST; return False; } // Calculate GSTIA0, DEGPDY, UT1UTC, and IATUTC. MEpoch measTime = MSColumns(ms).timeMeas()(0); MEpoch utctime = MEpoch::Convert(measTime, MEpoch::UTC)(); MEpoch iattime = MEpoch::Convert(measTime, MEpoch::IAT)(); MEpoch ut1time = MEpoch::Convert(measTime, MEpoch::UT1)(); Double utcsec = utctime.get("s").getValue(); Double ut1sec = ut1time.get("s").getValue(); Double iatsec = iattime.get("s").getValue(); // Use the beginning of the IAT day to calculate the GMST. Double utcday = floor(utctime.get("d").getValue()); Double iatday = floor(iattime.get("d").getValue()); Double gstday, gstday1; { // Use IAT=0 to get GST: Quantum itime(iatday, "d"); MEpoch ia0time(itime, MEpoch::UTC); MEpoch gsttime = MEpoch::Convert(ia0time, MEpoch::GMST)(); gstday = gsttime.get("d").getValue(); } Double gstdeg = 360 * (gstday - floor(gstday)); { // #degrees/IATday is the difference between this and the next day. Quantum itime(iatday + 1, "d"); MEpoch ia0time(itime, MEpoch::UTC); MEpoch gsttime = MEpoch::Convert(ia0time, MEpoch::GMST)(); gstday1 = gsttime.get("d").getValue(); } Double degpdy = 360 * (gstday1 - gstday); // PolarMotion gives -x and -y. // Need to be multiplied by earth radius to get them in meters. const Euler& polarMotion = MeasTable::polarMotion(utcday); // Each array gets its own antenna table for (uInt arraynum = 0; arraynum < narray; ++arraynum) { // Get the observatory's position and convert to ITRF. String obsName = inarrayname(arraynum); MPosition pos; MeasTable::Observatory(pos, obsName); MPosition itrfpos = MPosition::Convert(pos, MPosition::ITRF)(); MVPosition mvpos = itrfpos.getValue(); MSAntennaColumns antCols(ms.antenna()); // Nominally arraypos+antpos will be ITRF (see below), // unless we tinker with it, in which case it is // a local convention String posref("ITRF"); // #### Header Record header; header.define("EXTNAME", "AIPS AN"); // EXTNAME header.define("EXTVER", Int(arraynum + 1)); // EXTVER // we are now writing antenna positions in ITRF, so the // corresponding array center is always the origin of this // coordinate system header.define("ARRAYX", 0); header.define("ARRAYY", 0); header.define("ARRAYZ", 0); header.define("GSTIA0", gstdeg); // GSTIA0 header.define("DEGPDY", degpdy); // DEGPDY header.define("FREQ", refFreq); // FREQ header.define("RDATE", toFITSDate(measTime.get("s"))); // RDATE header.define("POLARX", -polarMotion(0) * 6356752.31); // POLARX header.define("POLARY", -polarMotion(1) * 6356752.31); // POLARY header.define("UT1UTC", ut1sec - utcsec); // UT1UTC header.define("IATUTC", iatsec - utcsec); // IATUTC header.define("TIMSYS", measTime.getRefString()); // TIMSYS header.define("ARRNAM", inarrayname(arraynum)); // ARRNAM header.define("NUMORB", 0); // NUMORB header.define("NOPCAL", 0); // NOPCAL header.define("POLTYPE", " "); // POLTYPE header.define("XYZHAND", "RIGHT"); // handedness of antenna coord system // Added Nov 2009, following AIPS addition header.define("FRAME", posref); // FRAME os << LogIO::NORMAL // Requested by CAS-437. << "Using " << posref << " frame for antenna positions." << LogIO::POST; // NOT in going aips // header.define("DATUTC", 0.0); // header.define("P_REFANT", 15); // header.define("P_DIFF01", 0.0); // #### Row description RecordDesc desc; Record strlengths, units; desc.addField("ANNAME", TpString); // ANNAME strlengths.define("ANNAME", 8); desc.addField("STABXYZ", TpArrayDouble, // STABXYZ IPosition(1, 3)); units.define("STABXYZ", "METERS"); desc.addField("ORBPARM", TpArrayDouble, // ORBPARM IPosition(1, 0)); desc.addField("NOSTA", TpInt); // NOSTA desc.addField("MNTSTA", TpInt); // MNTSTA desc.addField("STAXOF", TpFloat); // STAXOF units.define("STAXOF", "METERS"); desc.addField("POLTYA", TpString); // POLTYA strlengths.define("POLTYA", 1); desc.addField("POLAA", TpFloat); // POLAA units.define("POLAA", "DEGREES"); /// desc.addField("POLCALA", TpArrayFloat, // POLCALA /// IPosition(1,0)); desc.addField("POLCALA", TpFloat); // POLCALA desc.addField("POLTYB", TpString); // POLTYB strlengths.define("POLTYB", 1); desc.addField("POLAB", TpFloat); // POLAB units.define("POLAB", "DEGREES"); /// desc.addField("POLCALB", TpArrayFloat, // POLCALB /// IPosition(1,0)); desc.addField("POLCALB", TpFloat); // POLCALB desc.addField("DIAMETER", TpFloat); MSAntenna antennaTable = ms.antenna(); MSAntennaColumns antennaCols(antennaTable); // SELECT antennas for the current sub-array // MSAntenna antennaTable = ms.antenna() //(ms.antenna().col(MSAntenna::columnName(MSAntenna::ARRAY_ID)) == // Int(arraynum)); ScalarColumn inantname(antennaCols.station()); ScalarColumn antid(antennaCols.name()); ScalarColumn inantmount(antennaCols.mount()); MPosition::ScalarColumn inantposition(antennaCols.positionMeas()); ArrayColumn inantoffset(antennaCols.offset()); const uInt nant = antennaTable.nrow(); os << LogIO::NORMAL << "Found " << nant << " antennas in array #" << arraynum + 1 << LogIO::POST; MSFeed feedTable = ms.feed(); MSFeedColumns feedCols(feedTable); ArrayColumn inpoltype(feedCols.polarizationType()); ScalarColumn inantid(feedCols.antennaId()); ScalarColumn spwids(feedCols.spectralWindowId()); MSMetaData msmd(&ms, 100); std::set uSpws = msmd.getUniqueSpwIDs(); ArrayQuantColumn receptorAngle(feedCols.receptorAngleQuant()); FITSTableWriter writer(output.get(), desc, strlengths, nant, header, units, False); RecordFieldPtr anname(writer.row(), "ANNAME"); RecordFieldPtr > stabxyz(writer.row(), "STABXYZ"); RecordFieldPtr > orbparm(writer.row(), "ORBPARM"); RecordFieldPtr nosta(writer.row(), "NOSTA"); RecordFieldPtr mntsta(writer.row(), "MNTSTA"); RecordFieldPtr staxof(writer.row(), "STAXOF"); RecordFieldPtr poltya(writer.row(), "POLTYA"); RecordFieldPtr polaa(writer.row(), "POLAA"); RecordFieldPtr polcala(writer.row(), "POLCALA"); RecordFieldPtr poltyb(writer.row(), "POLTYB"); RecordFieldPtr polab(writer.row(), "POLAB"); RecordFieldPtr polcalb(writer.row(), "POLCALB"); RecordFieldPtr diam(writer.row(), "DIAMETER"); // Set the ones we're not going to change once *orbparm = 0.0; *poltya = " "; *polaa = 0.0; *polcala = 0.0; *poltyb = " "; *polab = 0.0; *polcalb = 0.0; Vector id; _handleAntNumbers(ms, id); // A hack for old WSRT observations which stored the antenna name // in the STATION column instead of the NAME column. // So if all NAMES are equal use STATIONS (unless they are all equal). // Also: if writeStation==True use station names instead of antenna names // for the output fits file (input fits file tends to have this). Vector anames = antid.getColumn(); Vector antDiams = msmd.getAntennaDiameters().getValue("m"); if (anames.nelements() > 0) { if (writeStation || allEQ(anames, anames(0))) { Vector stations = inantname.getColumn(); if (!allEQ(stations, stations(0))) { anames = stations; } } } // antenna -> receptor angles std::map > antToRA; for (uInt antnum = 0; antnum < nant; ++antnum) { *anname = anames(antnum); // Get antenna position in ITRF coordinates. // Take difference with array position. MPosition antpos = inantposition.convert(antnum, MPosition::ITRF); Vector corstabxyz = antpos.getValue().getValue(); *stabxyz = corstabxyz; *nosta = id[antnum]; String mount = upcase(inantmount(antnum)); // MS has "EQUATORIAL", "ALT-AZ", "X-Y", "SPACE-HALCA" if (mount.contains("ALT-AZ+NASMYTH-R")) { *mntsta = 4; } else if (mount.contains("ALT-AZ+NASMYTH-L")) { *mntsta = 5; } else if (mount.contains("ALT-AZ+BWG-R")) { *mntsta = 6; } else if (mount.contains("ALT-AZ+BWG-L")) { *mntsta = 7; } else if (mount.contains("ALT-AZ")) { *mntsta = 0; } else if (mount.contains("EQUATORIAL")) { *mntsta = 1; } else if (mount.contains("ORBIT")) { *mntsta = 2; } else if (mount.contains("X-Y")) { *mntsta = 3; } else if (mount.contains("SPACE-HALCA")) { *mntsta = 9; } else if (mount.contains("BIZARRE")) { *mntsta = 8; } else { *mntsta = 9; // fits does not use anyway, put it 9 } *staxof = inantoffset(antnum)(IPosition(1, 0)); // OK, try to find if we're L/R or X/Y // This probably breaks down when we have more than one // polarization type on different feeds (unlikely) or // different spectral windows (more likely). const uInt nmax = feedTable.nrow(); Bool found = False; *poltya = " "; *poltyb = " "; for (uInt i = 0; i < nmax; ++i) { // filter out irrelevant spws. spw = -1 in the FEED table // inicates that row applies for all spectral windows if ( Int(antnum) == inantid(i) && ( spwids(i) == -1 || uSpws.find(spwids(i)) != uSpws.end() ) ) { found = True; Vector poltypes = inpoltype(i); Vector ra; receptorAngle.get(i, ra); if (antToRA.find(antnum) == antToRA.end()) { if (poltypes.nelements() >= 1) { *poltya = poltypes(0); *polaa = ra[0].getValue("deg"); } if (poltypes.nelements() >= 2) { *poltyb = poltypes(1); *polab = ra[1].getValue("deg"); } antToRA[antnum] = ra; } else { try { _checkReceptorAngles(ra, antToRA[antnum], antnum); } catch (const std::exception& x) { os << LogOrigin("MSFitsOutput", __func__) << LogIO::WARN << "Receptor Angles in FEED table are not constant for antenna " << antnum << ". Will try to ignore this. If this data is not yet calibrated, a future calibration of the uvfits data may fail: " << x.what() << LogIO::POST; } } } } if (!found) { os << LogIO::SEVERE << "Could not find polarization types for antenna " << antnum << LogIO::POST; } *diam = antDiams[antnum]; writer.write(); } } return True; } void MSFitsOutput::_checkReceptorAngles( const Vector& ra0, Vector& ra1, Int antnum ) { if (ra0.size() != ra1.size()) { ostringstream oss; oss << "Varying number of receptor angles found for " << "specified spectral windows for antenna " << antnum << " is not supported by uvfits"; ThrowCc(oss.str()); } uInt nra = ra0.size(); for (uInt j=0; j output, const MeasurementSet &ms, const Block& fieldidMap, Int nrfield, const Block& /*spwidMap*/, Int nrspw) { LogIO os(LogOrigin("MSFitsOutput", "writeSU")); // Basically we make the FIELD_ID the source ID. MSField fieldTable(ms.field()); MSFieldColumns msfc(fieldTable); const ScalarColumn& insrcid = msfc.sourceId(); const ScalarColumn& inname = msfc.name(); // If source table exists, access it // This is for case where SOURCE guaranteed to exist: // MSSource sourceTable(ms.source()); // MSSourceColumns sourceColumns(sourceTable); // ColumnsIndex srcInx(sourceTable, "SOURCE_ID"); // RecordFieldPtr srcInxFld(srcInx.accessKey(), "SOURCE_ID"); // This is for case where SOURCE may not exist: // (doesn't work yet!) MSSource* sourceTable = 0; MSSourceColumns* sourceColumns = 0; ColumnsIndex* srcInx = 0; RecordFieldPtr* srcInxFld = 0; if (!ms.source().isNull()) { sourceTable = new MSSource(ms.source()); sourceColumns = new MSSourceColumns(*sourceTable); // Create an index for the SOURCE table. // Make a RecordFieldPtr for the SOURCE_ID field in the index key record. srcInx = new ColumnsIndex(*sourceTable, "SOURCE_ID"); srcInxFld = new RecordFieldPtr (srcInx->accessKey(), "SOURCE_ID"); } MSSpectralWindow spectralTable(ms.spectralWindow()); const uInt nrow = fieldTable.nrow(); if (nrow == 0) { os << LogIO::SEVERE << "No field table!" << LogIO::POST; return False; } if (spectralTable.nrow() == 0) { os << LogIO::SEVERE << "No spectral window table!" << LogIO::POST; return False; } ScalarColumn totalbw(spectralTable, MSSpectralWindow::columnName( MSSpectralWindow::TOTAL_BANDWIDTH)); Double totalBandwidth = totalbw(0); // const uInt nsource = sourceTable.nrow(); // this is allowed to be 0 // #### Header Record header; header.define("EXTNAME", "AIPS SU"); // EXTNAME header.define("EXTVER", 1); // EXTVER header.define("NO_IF", nrspw); header.define("FREQID", 1); String velDef; String velType; if (spectralTable.tableDesc().isColumn("NFRA_VELOCDEFINITION")) { ScalarColumn velDefCol(spectralTable, "NFRA_VELOCDEFINITION"); header.define("VELTYP", ""); header.define("VELDEF", ""); } else { os << LogIO::NORMAL << "Not setting velocity types" << LogIO::POST; } // #### Row description RecordDesc desc; Record strlengths, units; desc.addField("ID. NO.", TpInt); desc.addField("SOURCE", TpString); strlengths.define("SOURCE", 20); desc.addField("QUAL", TpInt); desc.addField("CALCODE", TpString); strlengths.define("CALCODE", 4); desc.addField("IFLUX", TpArrayFloat, IPosition(1, nrspw)); units.define("IFLUX", "JY"); desc.addField("QFLUX", TpArrayFloat, IPosition(1, nrspw)); units.define("QFLUX", "JY"); desc.addField("UFLUX", TpArrayFloat, IPosition(1, nrspw)); units.define("UFLUX", "JY"); desc.addField("VFLUX", TpArrayFloat, IPosition(1, nrspw)); units.define("VFLUX", "JY"); desc.addField("FREQOFF", TpArrayDouble, IPosition(1, nrspw)); units.define("FREQOFF", "HZ"); desc.addField("BANDWIDTH", TpDouble); units.define("BANDWIDTH", "HZ"); desc.addField("RAEPO", TpDouble); units.define("RAEPO", "DEGREES"); desc.addField("DECEPO", TpDouble); units.define("DECEPO", "DEGREES"); desc.addField("EPOCH", TpDouble); units.define("EPOCH", "YEARS"); desc.addField("RAAPP", TpDouble); units.define("RAAPP", "DEGREES"); desc.addField("DECAPP", TpDouble); units.define("DECAPP", "DEGREES"); desc.addField("LSRVEL", TpArrayDouble, IPosition(1, nrspw)); units.define("LSRVEL", "M/SEC"); desc.addField("RESTFREQ", TpArrayDouble, IPosition(1, nrspw)); units.define("RESTFREQ", "HZ"); desc.addField("PMRA", TpDouble); units.define("PMRA", "DEG/DAY"); desc.addField("PMDEC", TpDouble); units.define("PMDEC", "DEG/DAY"); FITSTableWriter writer(output.get(), desc, strlengths, nrfield, header, units, False); RecordFieldPtr idno(writer.row(), "ID. NO."); RecordFieldPtr source(writer.row(), "SOURCE"); RecordFieldPtr qual(writer.row(), "QUAL"); RecordFieldPtr calcode(writer.row(), "CALCODE"); RecordFieldPtr > iflux(writer.row(), "IFLUX"); RecordFieldPtr > qflux(writer.row(), "QFLUX"); RecordFieldPtr > uflux(writer.row(), "UFLUX"); RecordFieldPtr > vflux(writer.row(), "VFLUX"); RecordFieldPtr > freqoff(writer.row(), "FREQOFF"); RecordFieldPtr bandwidth(writer.row(), "BANDWIDTH"); RecordFieldPtr raepo(writer.row(), "RAEPO"); RecordFieldPtr decepo(writer.row(), "DECEPO"); RecordFieldPtr epoch(writer.row(), "EPOCH"); RecordFieldPtr raapp(writer.row(), "RAAPP"); RecordFieldPtr decapp(writer.row(), "DECAPP"); RecordFieldPtr > lsrvel(writer.row(), "LSRVEL"); RecordFieldPtr > restfreq(writer.row(), "RESTFREQ"); RecordFieldPtr pmra(writer.row(), "PMRA"); RecordFieldPtr pmdec(writer.row(), "PMDEC"); // Default them all, then we can gradually add more in the loop without // worrying about it. *idno = 0; *source = " "; *qual = 0; *calcode = " "; *iflux = 0.0; *qflux = 0.0; *uflux = 0.0; *vflux = 0.0; *freqoff = 0.0; *bandwidth = totalBandwidth; *raepo = 0.0; *decepo = 0.0; *epoch = 2000.0; *raapp = 0.0; *decapp = 0.0; *lsrvel = 0.0; *restfreq = 0.0; *pmra = 0.0; *pmdec = 0.0; MDirection dir; Vector fnames(nrow); for(uInt fieldnum = 0; fieldnum < nrow; fieldnum++) { ostringstream oss; oss.fill(' '); oss.flags(std::ios::left); oss.width(20); oss << inname(fieldnum); fnames(fieldnum) = oss.str(); } // Only take those fields which are part of the fieldidMap // (which represents the fields written in the main table). for (uInt fieldnum = 0; fieldnum < nrow; fieldnum++) { if (fieldnum < fieldidMap.nelements() && fieldidMap[fieldnum] >= 0) { *idno = 1 + fieldidMap[fieldnum]; dir = msfc.phaseDirMeas(fieldnum); *source = fnames(fieldnum); // check if name is unique *qual = 0; for(uInt ifld=0; ifld rownrs = srcInx->getRowNumbers(); if (rownrs.nelements() > 0) { uInt rownr = rownrs(0); if (!sourceColumns->sysvel().isNull() && sourceColumns->sysvel().isDefined(rownr)) { Vector sv(sourceColumns->sysvel()(rownr)); if (sv.nelements() > 0) { *lsrvel = sv(0); } } if (sourceTable->isColumn(MSSource::REST_FREQUENCY) && sourceColumns->restFrequency().isDefined(rownr) ) { Vector rf(sourceColumns->restFrequency()(rownr)); if (rf.nelements() > 0) { *restfreq = rf(0); } } if (sourceColumns->properMotion().isDefined(rownr)) { try{ Vector > pm; sourceColumns->properMotionQuant().get(rownr, pm, True); *pmra = pm(0).getValue(Unit("deg/d")); *pmdec = pm(1).getValue(Unit("deg/d")); } catch (const std::exception& e) { String unit="UNCALIB"; unit = sourceColumns->properMotionQuant().getUnits()[0]; Vector pm; sourceColumns->properMotion().get(rownr, pm, True); *pmra = pm(0); *pmdec = pm(1); os << LogIO::WARN << "Proper motion for source in SOURCE table row #"<code()(rownr) + " "; // Directions have to be converted from radians to degrees. //if (sourceColumns->direction().isDefined(rownr)) { // dir = sourceColumns->directionMeas()(rownr); //} //if (dir.type() == MDirection::B1950) { // *epoch = 1950.; //} } } // Write ra/dec as epoch and apparent (in degrees). // Use the time in the field table to calculate apparent. { *raepo = dir.getAngle("deg").getValue()(0); *decepo = dir.getAngle("deg").getValue()(1); MeasFrame frame; frame.set(msfc.timeMeas()(fieldnum)); MDirection::Ref typeout(MDirection::APP, frame); MDirection dirout = MDirection::Convert(dir, typeout)(); *raapp = dirout.getAngle("deg").getValue()(0); *decapp = dirout.getAngle("deg").getValue()(1); } writer.write(); } } os << LogIO::NORMAL << "writing " << nrfield << " sources" << LogIO::POST; // Delete dynamic memory, if nec: if (sourceTable) delete sourceTable; if (sourceColumns) delete sourceColumns; if (srcInx) delete srcInx; if (srcInxFld) delete srcInxFld; return True; } Bool MSFitsOutput::_writeTY(std::shared_ptr output, const MeasurementSet &ms, const Table& syscal, const Block& spwidMap, uInt nrif, Bool combineSpw) { LogIO os(LogOrigin("MSFitsOutput", "writeTY")); const MSSysCal subtable(syscal); MSSysCalColumns sysCalColumns(subtable); const uInt nrow = syscal.nrow(); if (nrow == 0 || sysCalColumns.tsys().isNull()) { os << LogIO::SEVERE << "No SysCal TY info!" << LogIO::POST; return False; } // Get #pol by taking shape of first tsys from the column. const Int npol = sysCalColumns.tsys().shape(0)(0); if (!combineSpw) { nrif = 1; } IPosition ifShape(1, nrif); const uInt nentries = nrow / nrif; os << LogIO::NORMAL << "Found " << nentries << " TY table entries (" << nrif << " IFs)" << LogIO::POST; // Get reference time (i.e. start time) from the main table. Double refTime; { // get starttime (truncated to days) MSColumns mscol(ms); refTime = floor(mscol.time()(0) / C::day) * C::day; } // ##### Header Record header; header.define("EXTNAME", "AIPS TY"); // EXTNAME header.define("EXTVER", 1); // EXTVER header.define("NO_IF", Int(nrif)); // NO_IF header.define("NO_POL", npol); // NO_POL header.define("REVISION", 10); // REVISION // Table description RecordDesc desc; Record stringLengths; // no strings Record units; desc.addField("TIME", TpFloat); units.define("TIME", "DAYS"); desc.addField("TIME INTERVAL", TpFloat); units.define("TIME INTERVAL", "DAYS"); desc.addField("SOURCE ID", TpInt); desc.addField("ANTENNA NO.", TpInt); desc.addField("SUBARRAY", TpInt); desc.addField("FREQ ID", TpInt); desc.addField("TSYS 1", TpArrayFloat, ifShape); units.define("TSYS 1", "KELVINS"); desc.addField("TANT 1", TpArrayFloat, ifShape); units.define("TANT 1", "KELVINS"); if (npol == 2) { desc.addField("TSYS 2", TpArrayFloat, ifShape); units.define("TSYS 2", "KELVINS"); desc.addField("TANT 2", TpArrayFloat, ifShape); units.define("TANT 2", "KELVINS"); } FITSTableWriter writer(output.get(), desc, stringLengths, nentries, header, units, False); RecordFieldPtr time(writer.row(), "TIME"); RecordFieldPtr interval(writer.row(), "TIME INTERVAL"); RecordFieldPtr sourceId(writer.row(), "SOURCE ID"); RecordFieldPtr antenna(writer.row(), "ANTENNA NO."); RecordFieldPtr arrayId(writer.row(), "SUBARRAY"); RecordFieldPtr spwId(writer.row(), "FREQ ID"); RecordFieldPtr > tsys1(writer.row(), "TSYS 1"); RecordFieldPtr > tant1(writer.row(), "TANT 1"); RecordFieldPtr > tsys2; RecordFieldPtr > tant2; if (npol == 2) { tsys2 = RecordFieldPtr > (writer.row(), "TSYS 2"); tant2 = RecordFieldPtr > (writer.row(), "TANT 2"); } Vector antnums; _handleAntNumbers(ms, antnums); Vector tsysval; for (uInt i = 0; i < nrow; i += nrif) { Double tim = sysCalColumns.time()(i); *time = (tim - refTime) / C::day; *interval = sysCalColumns.interval()(i) / C::day; *sourceId = 1; // *antenna = 1 + sysCalColumns.antennaId()(i); *antenna = antnums(sysCalColumns.antennaId()(i)); *arrayId = 1; *spwId = 1 + spwidMap[sysCalColumns.spectralWindowId()(i)]; sysCalColumns.tsys().get(i, tsysval); Vector ts1(nrif); Vector ts2(nrif); Vector ta(nrif); ta = 0.; for (uInt j = 0; j < nrif; j++) { sysCalColumns.tsys().get(i + j, tsysval); ts1(j) = tsysval(0); if (npol == 2) { ts2(j) = tsysval(1); } if (j > 0) { if (sysCalColumns.time()(i + j) != tim) { throw(AipsError("Irregularity in times in SYSCAL subtable")); } } } *tsys1 = ts1; *tant1 = ta; ; if (npol == 2) { *tsys2 = ts2; *tant2 = ta; } // Write the current row writer.write(); } return True; } Bool MSFitsOutput::_writeGC(std::shared_ptr output, const MeasurementSet &ms, const Table& syscal, const Block& /*spwidMap*/, uInt nrif, Bool combineSpw, Double sensitivity, Int refPixelFreq, Double refFreq, Double chanbw) { LogIO os(LogOrigin("MSFitsOutput", "writeGC")); // We need to write an entry per antenna (and spw if !combineSpw). // So sort the SYSCAL table in that order and skip duplicate // spectral-windows. Use insertion sort, since the table is already in order. Block sortNames(2); sortNames[0] = MSSysCal::columnName(MSSysCal::ANTENNA_ID); sortNames[1] = MSSysCal::columnName(MSSysCal::TIME); Table sorcal = syscal.sort(sortNames, Sort::Ascending, Sort::InsSort + Sort::NoDuplicates); // Sort again (without duplicates) to get the nr of antennas. // Remove TIME from the sort columns. // Use insertion sort, because the table is already in order. Int nrant; sortNames.resize(1, True, True); { Table sorcal2 = sorcal.sort(sortNames, Sort::Ascending, Sort::InsSort + Sort::NoDuplicates); nrant = sorcal2.nrow(); } if (nrant == 0) { os << LogIO::SEVERE << "No SysCal GC info!" << LogIO::POST; return False; } // Find nr of IF's or SPW's. Int nrspw = 1; if (!combineSpw) { nrspw = nrif; nrif = 1; } // Get #pol from 1st row in FEED table. const Int npol = MSFeedColumns(ms.feed()).numReceptors()(0); IPosition ifShape(1, nrif); const uInt nentries = nrant * nrspw; os << LogIO::NORMAL << "Found " << nentries << " GC table entries (" << nrif << " IFs, " << npol << " polarizations)" << LogIO::POST; // Get some info from the main table. Int nchan, nstk; Double startTime, startHA; { MSColumns mscol(ms); IPosition shp = mscol.data().shape(0); nstk = shp(0); nchan = shp(1); // Find the start time and HA (from the first row). getStartHA(startTime, startHA, ms, 0); } // Create an iterator (on antenna) for the already sorted table. // Use the first chunk to create the hourangle vector. TableIterator tabiter(sorcal, sortNames, TableIterator::Ascending, TableIterator::NoSort); Vector havec; { Table tableChunk(tabiter.table()); uInt n = tableChunk.nrow(); MSSysCal syscal(tableChunk); MSSysCalColumns sysCalColumns(syscal); // Fill the hourangle vector (which is the same for all subsets). // Its unit is degrees; startHA is in fractions of a circle. // The time is in seconds, so convert that to a full day (circle). // Start the hourangle in degrees. havec.resize(n); Double factor = (Double(366.25) / 365.25) / (24 * 3600); for (uInt i = 0; i < n; i++) { havec(i) = 360 * (startHA + factor * (sysCalColumns.time()(i) - startTime)); } } // For the time being write only 2 values (first and last HA). // Until we know how to calculate the gain factor resulting // from the deformation of the mirror at given hourangles. IPosition shape(1, 2); Vector havec2(2 * nrif, 0.); for (uInt i = 0; i < nrif; i++) { havec2(2 * i) = havec(0); havec2(2 * i + 1) = havec(havec.nelements() - 1); } // Write the data for each antenna. // ##### Header Record header; header.define("EXTNAME", "AIPS GC"); // EXTNAME header.define("EXTVER", 1); // EXTVER header.define("OBSCODE", ""); // OBSCODE header.define("NO_POL", npol); // NO_POL header.define("NO_STKD", nstk); // NO_STKD header.define("STK_1", -5); // STK_1 (XX = -5) header.define("NO_BAND", Int(nrif)); // NO_BAND header.define("NO_CHAN", nchan); // NO_CHAN header.define("REF_FREQ", refFreq); // REF_FREQ header.define("CHAN_BW", abs(chanbw)); // CHAN_BW header.define("REF_PIXL", Double(1 + refPixelFreq)); // REF_PIXL (==CRPIX4) header.define("NO_TABS", Int(shape(0))); // NO_TABS header.define("TABREV", 2); // TABREV // Table description RecordDesc desc; Record stringLengths; // no strings Record units; // default to Hz desc.addField("ANTENNA_NO", TpInt); desc.addField("SUBARRAY", TpInt); desc.addField("FREQ ID", TpInt); desc.addField("TYPE_1", TpArrayInt, ifShape); desc.addField("NTERM_1", TpArrayInt, ifShape); desc.addField("X_TYP_1", TpArrayInt, ifShape); desc.addField("Y_TYP_1", TpArrayInt, ifShape); desc.addField("X_VAL_1", TpArrayFloat, ifShape); desc.addField("Y_VAL_1", TpArrayFloat, shape * nrif); units.define("Y_VAL_1", "DEGREES"); desc.addField("GAIN_1", TpArrayFloat, shape * nrif); desc.addField("SENS_1", TpArrayFloat, ifShape); units.define("SENS_1", "K/JY"); if (npol == 2) { desc.addField("TYPE_2", TpArrayInt, ifShape); desc.addField("NTERM_2", TpArrayInt, ifShape); desc.addField("X_TYP_2", TpArrayInt, ifShape); desc.addField("Y_TYP_2", TpArrayInt, ifShape); desc.addField("X_VAL_2", TpArrayFloat, ifShape); desc.addField("Y_VAL_2", TpArrayFloat, shape * nrif); units.define("Y_VAL_2", "DEGREES"); desc.addField("GAIN_2", TpArrayFloat, shape * nrif); desc.addField("SENS_2", TpArrayFloat, ifShape); units.define("SENS_2", "K/JY"); } FITSTableWriter writer(output.get(), desc, stringLengths, nentries, header, units, False); RecordFieldPtr antenna(writer.row(), "ANTENNA_NO"); RecordFieldPtr arrayId(writer.row(), "SUBARRAY"); RecordFieldPtr spwId(writer.row(), "FREQ ID"); RecordFieldPtr > type1(writer.row(), "TYPE_1"); RecordFieldPtr > nterm1(writer.row(), "NTERM_1"); RecordFieldPtr > xtype1(writer.row(), "X_TYP_1"); RecordFieldPtr > ytype1(writer.row(), "Y_TYP_1"); RecordFieldPtr > xval1(writer.row(), "X_VAL_1"); RecordFieldPtr > yval1(writer.row(), "Y_VAL_1"); RecordFieldPtr > gain1(writer.row(), "GAIN_1"); RecordFieldPtr > sens1(writer.row(), "SENS_1"); RecordFieldPtr > type2; RecordFieldPtr > nterm2; RecordFieldPtr > xtype2; RecordFieldPtr > ytype2; RecordFieldPtr > xval2; RecordFieldPtr > yval2; RecordFieldPtr > gain2; RecordFieldPtr > sens2; if (npol == 2) { type2 = RecordFieldPtr > (writer.row(), "TYPE_2"); nterm2 = RecordFieldPtr > (writer.row(), "NTERM_2"); xtype2 = RecordFieldPtr > (writer.row(), "X_TYP_2"); ytype2 = RecordFieldPtr > (writer.row(), "Y_TYP_2"); xval2 = RecordFieldPtr > (writer.row(), "X_VAL_2"); yval2 = RecordFieldPtr > (writer.row(), "Y_VAL_2"); gain2 = RecordFieldPtr > (writer.row(), "GAIN_2"); sens2 = RecordFieldPtr > (writer.row(), "SENS_2"); } // The antenna numbers Vector antnums; _handleAntNumbers(ms, antnums); // Iterate through the table. // Each chunk should have the same size. while (!tabiter.pastEnd()) { Table tableChunk(tabiter.table()); MSSysCal syscal(tableChunk); MSSysCalColumns sysCalColumns(syscal); // *antenna = sysCalColumns.antennaId()(0) + 1; *antenna = antnums(sysCalColumns.antennaId()(0)); // *arrayId = sysCalColumns.arrayId()(0) + 1; *arrayId = 1; if (tableChunk.nrow() != havec.nelements()) { os << LogIO::SEVERE << "SysCal table is irregular!" << " Mismatching #rows for antenna " << *antenna << LogIO::POST; return False; } for (Int spw = 0; spw < nrspw; spw++) { *spwId = spw + 1; *type1 = 1; // tabulated values *nterm1 = shape(0); *xtype1 = 0; // none *ytype1 = 3; // hourangle *xval1 = 0; *yval1 = havec2; *gain1 = 1.0; *sens1 = sensitivity; if (npol == 2) { *type2 = 1; // tabulated values *nterm2 = shape(0); *xtype2 = 0; *ytype2 = 3; *xval2 = 0; *yval2 = havec2; *gain2 = 1.0; *sens2 = sensitivity; } // Write the current row writer.write(); } tabiter++; } return True; } Bool MSFitsOutput::_writeWX(std::shared_ptr output, const MeasurementSet &ms) { LogIO os(LogOrigin("MSFitsOutput", __func__)); const MSWeather subtable(ms.weather()); MSWeatherColumns weatherColumns(subtable); const uInt nrow = subtable.nrow(); if (nrow == 0) { os << LogIO::WARN << "No weather info" << LogIO::POST; return False; } // Get reference time (i.e. start time) from the main table. Double refTime; //{ // get starttime (truncated to days) MSColumns mscol(ms); refTime = floor(mscol.time()(0) / C::day) * C::day; //} //MEpoch measTime = MSColumns(ms).timeMeas()(0); MEpoch measTime = mscol.timeMeas()(0); // ##### Header Record header; header.define("EXTNAME", "AIPS WX"); // EXTNAME header.define("EXTVER", 1); // EXTVER header.define("OBSCODE", ""); // PROGRAM CODE??? header.define("RDATE", toFITSDate(measTime.get("s"))); // OBSERVING DATE (YYYYMMDD) header.define("TABREV", 3); // REVISION // Table description RecordDesc desc; Record stringLengths; // no strings Record units; desc.addField("TIME", TpDouble); units.define("TIME", "DAYS"); desc.addField("TIME_INTERVAL", TpFloat); units.define("TIME_INTERVAL", "DAYS"); desc.addField("ANTENNA_NO", TpInt); desc.addField("SUBARRAY", TpInt); desc.addField("TEMPERATURE", TpFloat); units.define("TEMPERATURE", "CENTIGRADE"); desc.addField("PRESSURE", TpFloat); units.define("PRESSURE", "MILLIBAR"); desc.addField("DEWPOINT", TpFloat); units.define("DEWPOINT", "CENTIGRADE"); desc.addField("WIND_VELOCITY", TpFloat); units.define("WIND_VELOCITY", "M/SEC"); desc.addField("WIND_DIRECTION", TpFloat); units.define("WIND_DIRECTION", "DEGREES"); // east from north desc.addField("WVR_H2O", TpFloat); // sometimes labeled as H2O COLUMN units.define("WVR_H2O", "m-2"); desc.addField("IONOS_ELECTRON", TpFloat); //sometimes labeled as ELECTRON COL. units.define("IONOS_ELECTRON", "m-2"); FITSTableWriter writer(output.get(), desc, stringLengths, nrow, header, units, False); RecordFieldPtr time(writer.row(), "TIME"); RecordFieldPtr interval(writer.row(), "TIME_INTERVAL"); RecordFieldPtr antenna(writer.row(), "ANTENNA_NO"); RecordFieldPtr arrayId(writer.row(), "SUBARRAY"); RecordFieldPtr temperature(writer.row(), "TEMPERATURE"); RecordFieldPtr pressure(writer.row(), "PRESSURE"); RecordFieldPtr dewpoint(writer.row(), "DEWPOINT"); RecordFieldPtr windvelocity(writer.row(), "WIND_VELOCITY"); RecordFieldPtr winddirection(writer.row(), "WIND_DIRECTION"); RecordFieldPtr wvrh2o(writer.row(), "WVR_H2O"); RecordFieldPtr ionoselectron(writer.row(), "IONOS_ELECTRON"); Vector antnums; _handleAntNumbers(ms, antnums); //check optional columns Bool hasTemperature = !(weatherColumns.temperature().isNull()); Bool hasPressure = !(weatherColumns.pressure().isNull()); Bool hasDewPoint = !(weatherColumns.dewPoint().isNull()); Bool hasWindVelocity = !(weatherColumns.windSpeed().isNull()); Bool hasWindDirection = !(weatherColumns.windDirection().isNull()); Bool hasWVRH2O = !(weatherColumns.H2O().isNull()); Bool hasIonosElectron = !(weatherColumns.ionosElectron().isNull()); for (uInt i = 0; i < nrow; i++) { Double tim = weatherColumns.time()(i); *time = (tim - refTime) / C::day; *interval = weatherColumns.interval()(i) / C::day; //*antenna = antnums( weatherColumns.antennaId()(i) ); *antenna = (weatherColumns.antennaId()(i) == -1 ? 0 : antnums( weatherColumns.antennaId()(i))); //read optional columns // default 0.0 // temperature, dewpoint in MS should be kelvin but // current WIDAR data looks like in C! if (hasTemperature) { *temperature = weatherColumns.temperature()(i) - 273.15; } else { *temperature = 0.0; } if (hasPressure) { //covert whatever units to mbar *pressure = weatherColumns.pressureQuant()(i).getValue(Unit("mbar")); } else { *pressure = 0.0; } if (hasDewPoint) { *dewpoint = weatherColumns.dewPoint()(i) - 273.15; } else { *dewpoint = 0.0; } if (hasWindVelocity) { *windvelocity = weatherColumns.windSpeed()(i); } else { *windvelocity = 0.0; } // direction in MS should be in rad but looks like deg... if (hasWindDirection) { *winddirection = weatherColumns.windDirectionQuant()(i).getValue( "deg"); } else { *winddirection = 0.0; } if (hasWVRH2O) { *wvrh2o = weatherColumns.H2O()(i); } else { *wvrh2o = 0.0; } if (hasIonosElectron) { *ionoselectron = weatherColumns.ionosElectron()(i); } else { *ionoselectron = 0.0; } writer.write(); } return True; } // TODO uncommoment nspw when multiple IFs are supported Bool MSFitsOutput::_writeSY( std::shared_ptr output, const MeasurementSet &ms, Table& syspower, Int /*nspw*/, const Block& spwIDMap, Bool combineSpw ) { LogIO os(LogOrigin("MSFitsOutput", __func__)); static const String TIME = "TIME"; static const String ANTENNA_ID = "ANTENNA_ID"; static const String FEED_ID = "FEED_ID"; static const String SPECTRAL_WINDOW_ID = "SPECTRAL_WINDOW_ID"; //Table subtable(syspower.path().originalName()); const auto td = syspower.tableDesc(); const auto nrows = syspower.nrow(); if (nrows == 0) { os << LogIO::WARN << "SYSPOWER table is empty." << LogIO::POST; return False; } static const std::vector expColNames { ANTENNA_ID, FEED_ID, SPECTRAL_WINDOW_ID, TIME, "INTERVAL", "SWITCHED_DIFF", "SWITCHED_SUM", "REQUANTIZER_GAIN" }; for (const String& cname: expColNames) { if (! td.isColumn(cname)) { os << LogIO::WARN << "Required column " << cname << " not found in SYSPOWER table" << LogIO::POST; return False; } } const ScalarColumn feedID(syspower, FEED_ID); const auto fv = feedID.getColumn(); if (! allEQ(fv[0], fv)) { os << LogIO::WARN << "All FEED_IDs in SYSPOWER table are not identical" << LogIO::POST; return False; } // What to do based on the value of combineSpw follows the pattern // in _writeTy() // TODO currently not supporting writing multiple IFs. This code has been written in // a way that should make supporting multiple IFs fairly straight forward (the issue will // be adding support to MSFitsInput.cc, and in fact the reason I'm not supporting writing multiple // IFs to the uvfits table currently is because the only way to test that is to read the uvfits file // back in which requires support for multiple IFs in MSFitsInput.cc. The current requirement is to // write the SYSPOWER table to uvfits, not read it from uvfits, so the current implementation is // sufficient CAS-11860 ) Int nrif = 1; if (combineSpw) { os << LogIO::WARN << "Combining spectral windows (multiple IFs) is currently not supported " << "for the SYSPOWER table" << LogIO::POST; combineSpw = False; } /* // this code is for when multiple IFs are supported if (combineSpw) { if (nrows % nspw == 0) { nrif = nspw; } else { nrif = 1; combineSpw = False; os << LogIO::WARN << "Number of rows (" << nrows << ") in SYSPOWER " << "table is not a multiple of the number of spectral windows (" << nspw << "). Cannot combine spectral windows in the UVFITS " << "SY table. Will instead write one spw per row." << LogIO::POST; } } */ IPosition ifShape(1, nrif); const uInt nentries = nrows/nrif; os << LogIO::NORMAL << "Found " << nentries << " SY table entries (" << nrif << " IFs)" << LogIO::POST; if (combineSpw) { // ensure that the table is sorted correctly Block sortNames(3); sortNames[0] = TIME; sortNames[1] = ANTENNA_ID; sortNames[2] = SPECTRAL_WINDOW_ID; syspower = syspower.sort(sortNames); } const ArrayColumn switchedDiff (syspower, "SWITCHED_DIFF"); const Int npol = switchedDiff.shape(0)[0]; Record header; header.define("EXTNAME", "AIPS SY"); header.define("NO_IF", nrif); header.define("NO_POL", npol); header.define("NO_ANT", (Int)ms.antenna().nrow()); RecordDesc desc; Record stringLengths; // no strings Record units; desc.addField(TIME, TpDouble); units.define(TIME, "DAYS"); desc.addField("TIME INTERVAL", TpFloat); units.define("TIME INTERVAL", "DAYS"); desc.addField("SOURCE ID", TpInt); desc.addField("ANTENNA NO.", TpInt); desc.addField("SUBARRAY", TpInt); desc.addField("FREQ ID", TpInt); desc.addField("POWER DIF1", TpArrayFloat, ifShape); units.define("POWER DIF1", "counts"); desc.addField("POWER SUM1", TpArrayFloat, ifShape); units.define("POWER SUM1", "counts"); desc.addField("POST GAIN1", TpArrayFloat, ifShape); if (npol == 2) { desc.addField("POWER DIF2", TpArrayFloat, ifShape); units.define("POWER DIF2", "counts"); desc.addField("POWER SUM2", TpArrayFloat, ifShape); units.define("POWER SUM2", "counts"); desc.addField("POST GAIN2", TpArrayFloat, ifShape); } FITSTableWriter writer( output.get(), desc, stringLengths, nentries, header, units, False ); RecordFieldPtr time(writer.row(), TIME); RecordFieldPtr interval(writer.row(), "TIME INTERVAL"); RecordFieldPtr sourceId(writer.row(), "SOURCE ID"); RecordFieldPtr antenna(writer.row(), "ANTENNA NO."); RecordFieldPtr arrayId(writer.row(), "SUBARRAY"); RecordFieldPtr freqId(writer.row(), "FREQ ID"); RecordFieldPtr> powerDif1(writer.row(), "POWER DIF1"); RecordFieldPtr> powerSum1(writer.row(), "POWER SUM1"); RecordFieldPtr> postGain1(writer.row(), "POST GAIN1"); RecordFieldPtr> powerDif2; RecordFieldPtr> powerSum2; RecordFieldPtr> postGain2; if (npol == 2) { powerDif2 = RecordFieldPtr>(writer.row(), "POWER DIF2"); powerSum2 = RecordFieldPtr>(writer.row(), "POWER SUM2"); postGain2 = RecordFieldPtr>(writer.row(), "POST GAIN2"); } Vector antnums; _handleAntNumbers(ms, antnums); MSMetaData md(&ms, 100); const ScalarColumn timeCol(syspower, TIME); const ScalarColumn intervalCol(syspower, "INTERVAL"); const ScalarColumn antCol(syspower, ANTENNA_ID); const ScalarColumn spwCol(syspower, SPECTRAL_WINDOW_ID); const ArrayColumn switchedSum (syspower, "SWITCHED_SUM"); const ArrayColumn qGDiff (syspower, "REQUANTIZER_GAIN"); Vector pdv, psv, pgv; for (uInt i = 0; i < nrows; i += nrif) { const auto myTime = timeCol(i); *time = myTime/C::day; const auto myInterval = intervalCol(i); *interval = myInterval/(float)C::day; const auto fields = md.getFieldsForTimes(*time, *interval/2); const auto nfields = fields.size(); if (nfields > 1) { os << LogIO::SEVERE << "Multiple fields found for time " << myTime << " and interval " << myInterval << ". Please file a bug report " << LogIO::POST; return False; } else if (nfields == 0) { os << LogIO::SEVERE << "No fields found for time " << myTime << " and interval " << myInterval << ". Please file a bug report " << LogIO::POST; return False; } *sourceId = *(fields.cbegin()) + 1; *antenna = antnums(antCol(i)); // FIXME arrayid *arrayId = 1; *freqId = combineSpw ? 1 : 1 + spwIDMap[spwCol(i)]; Vector pd1(nrif); Vector pd2(nrif); Vector ps1(nrif); Vector ps2(nrif); Vector pg1(nrif); Vector pg2(nrif); std::set spwSet; for (Int j = 0; j < nrif; ++j) { if (j > 0) { if (timeCol(i + j) != myTime) { os << LogIO::SEVERE << "Irregularities in time values " << "in the SYSPOWER subtable, so spectral window " << "data cannot be combined when writing UVFITS " << "SY table. Perhaps try combineSpw=False" << LogIO::POST; return False; } else if (antnums(antCol(i + j)) != *antenna) { os << LogIO::SEVERE << "Irregularities in antenna_id " << "values in the SYSPOWER subtable, so spectral " << "window data cannot be combined when writing " << "UVFITS SY table. Perhaps try combineSpw=False" << LogIO::POST; return False; } } switchedDiff.get(i + j, pdv); pd1[j] = pdv[0]; switchedSum.get(i + j, psv); ps1[j] = psv[0]; qGDiff.get(i + j, pgv); pg1[j] = pgv[0]; if (npol == 2) { pd2[j] = pdv[1]; ps2[j] = psv[1]; pg2[j] = pgv[1]; } } *powerDif1 = pd1; *powerSum1 = ps1; *postGain1 = pg1; if (npol == 2) { *powerDif2 = pd2; *powerSum2 = ps2; *postGain2 = pg2; } writer.write(); } return True; } void MSFitsOutput::getStartHA(Double& startTime, Double& startHA, const MeasurementSet& ms, uInt rownr) { MSColumns mscol(ms); startTime = mscol.time()(rownr); MEpoch stTime = mscol.timeMeas()(rownr); Int fieldId = mscol.fieldId()(rownr); Int obsId = mscol.observationId()(rownr); // Get RA and DEC with their unit. MDirection delay(mscol.field().delayDirMeas(fieldId)); // Get the observatory's position. String obsName = mscol.observation().telescopeName()(obsId); MPosition pos; MeasTable::Observatory(pos, obsName); // Use this position in a frame MeasFrame frame(pos); frame.set(stTime); MDirection out = MDirection::Convert(delay, MDirection::Ref( MDirection::HADEC, frame))(); startHA = out.getAngle().getBaseValue()(0) / C::circle; } Table MSFitsOutput::handleSysCal(const MeasurementSet& ms, const Vector& spwids, Bool isSubset) { LogIO os(LogOrigin("MSFitsOutput", "handleSysCal")); Table syscal(ms.sysCal()); // Only take the antennas found in the main table. // This is better and also solves an NFRA problem where incorrect // antennas were written in the SYSCAL table. Block antFlag; { // Find the maximum antenna number. // Assure that the minimum >= 0. ScalarColumn ant1col(ms, MS::columnName(MS::ANTENNA1)); ScalarColumn ant2col(ms, MS::columnName(MS::ANTENNA2)); Vector ant1 = ant1col.getColumn(); Vector ant2 = ant2col.getColumn(); Int minant1, minant2, maxant1, maxant2; minMax(minant1, maxant1, ant1); minMax(minant2, maxant2, ant2); if (minant1 < 0 || minant2 < 0) { throw(AipsError("Antenna1 or antenna2 < 0 in MS " + ms.tableName())); } // Make an array which contains a flag True for all antennas in the // main table. Int nrant = 1 + max(maxant1, maxant2); antFlag.resize(nrant); antFlag = False; Bool delAnt1, delAnt2; const Int* ant1ptr = ant1.getStorage(delAnt1); const Int* ant2ptr = ant2.getStorage(delAnt2); uInt nrrow = ant1.nelements(); for (uInt i = 0; i < nrrow; i++) { antFlag[ant1ptr[i]] = True; antFlag[ant2ptr[i]] = True; } ant1.freeStorage(ant1ptr, delAnt1); ant2.freeStorage(ant2ptr, delAnt2); } { // Now skip all antennas in SYSCAL not present in the main table. ScalarColumn antcol(syscal, MSSysCal::columnName( MSSysCal::ANTENNA_ID)); Vector ant = antcol.getColumn(); Int minant, maxant; minMax(minant, maxant, ant); if (minant < 0) { throw(AipsError("Antenna_id < 0 in SYSCAL " + syscal.tableName())); } uInt nrrow = ant.nelements(); Block rowFlag(nrrow); rowFlag = True; Bool flagged = False; Bool delAnt; const Int* antptr = ant.getStorage(delAnt); for (uInt i = 0; i < nrrow; i++) { if (!antFlag[antptr[i]]) { rowFlag[i] = False; flagged = True; } } ant.freeStorage(antptr, delAnt); if (flagged) { syscal = syscal(rowFlag); os << LogIO::NORMAL << "Skipped unused antennas in SYSCAL table (" << nrrow - syscal.nrow() << " entries)" << LogIO::POST; } } // Skip first rows which maybe contain an average for each antenna. // This is an old WSRT feature/problem. { MSSysCalColumns sysCalColumns(ms.sysCal()); Double sttim = sysCalColumns.time()(0); uInt nrow = sysCalColumns.time().nrow(); for (uInt i = 0; i < nrow; i++) { Double tim = sysCalColumns.time()(i); if (tim != sttim) { if (tim < sttim) { os << LogIO::NORMAL << "First time in SYSCAL table is " "an average and will be skipped" << LogIO::POST; syscal = syscal(syscal.nodeRownr() >= Int(i)); } break; } } } // If the table is a subset, select the spectral-windows found in // the MS. if (isSubset) { syscal = syscal(syscal.col(MSSysCal::columnName( MSSysCal::SPECTRAL_WINDOW_ID)) .in(TableExprNode(spwids))); } // Sort the SYSCAL table in order of antenna, time, spectral-window. Block sortNames(3); sortNames[0] = MSSysCal::columnName(MSSysCal::ANTENNA_ID); sortNames[1] = MSSysCal::columnName(MSSysCal::TIME); sortNames[2] = MSSysCal::columnName(MSSysCal::SPECTRAL_WINDOW_ID); return syscal.sort(sortNames); } /* allids: (input) IDs to consider map: (output) map from allids to 0,1,...,nr selids: (output) inverse of map returns: nr, number of selected IDs in allids */ Int MSFitsOutput::_makeIdMap(Block& map, Vector& selids, const Vector< Int>& allids) { // Determine the number of ids and make a mapping of // id number in the table to id number in fits. // Even if the MS is not a subset (by selection), we have to // determine this mapping explicitly (because then some ids // might be left out). Int nrid = 1 + max(allids); map.resize(nrid, True, True); map = -1; // Find out which fields are actually used, because only those // fields need to be written from the FIELD table. Bool deleteIt; const Int* data = allids.getStorage(deleteIt); Block idUsed(nrid, False); Int nrow = allids.nelements(); for (Int i = 0; i < nrow; i++) { idUsed[data[i]] = True; } allids.freeStorage(data, deleteIt); Int nr = 0; for (Int i = 0; i < nrid; i++) { if (idUsed[i]) { map[i] = nr++; // form the mapping } } selids.resize(nr); nr = 0; for (Int i = 0; i < nrid; i++) { if (idUsed[i]) { selids(nr++) = i; // determine which ids are selected } } return nr; } void MSFitsOutput::_handleAntNumbers(const MeasurementSet& ms, Vector& antnumbers) { // This method parses the MS ANTENNA NAME into a antenna // number appropriate for the UVFITS output // For VLA antennas, the names are nominally numbers, and // may be prepended with EA or VA. These prefixes are // properly stripped before the remaining string is parsed // as a number. // For other telescopes, the name is used if it is a pure // integer; otherwise the index + 1 is used (NB: AIPS demands // one-basedness.) // Discern if which telescope MSObservationColumns obscol(ms.observation()); String arrayName; if (obscol.nrow() > 0) arrayName = obscol.telescopeName()(0); MSAntennaColumns antcol(ms.antenna()); ScalarColumn antname(antcol.name()); Int nAnt = antcol.nrow(); antnumbers.resize(nAnt); for (Int iant = 0; iant < nAnt; ++iant) { String name; if (arrayName.contains("VLA")) // Trim leading EA/VA, if present name = antname(iant).from(RXint); else name = antname(iant); if (name.matches(RXint)) antnumbers(iant) = atoi(name.chars()); else { // at least one name isn't a number, so use use index+1 for ALL indgen(antnumbers); antnumbers += 1; break; } } // cout << "antnumbers = " << antnumbers << endl; } // Local Variables: // compile-command: "gmake MSFitsOutput" // End: } //# NAMESPACE CASACORE - END casacore-3.7.1/msfits/MSFits/MSFitsOutput.h000066400000000000000000000250711476623553700205420ustar00rootroot00000000000000//# MSFitsOutput.h: Write a MeasurementSet to a random group uvfits file //# Copyright (C) 1996,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., 675 Mass Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_MSFITSOUTPUT_H #define MS_MSFITSOUTPUT_H #include #include #include #include #include namespace casacore { //# Forward Declarations class File; class FitsOutput; template class ScalarColumn; class Table; template class Block; // // Write a MeasurementSet to a random group uvfits file. // class MSFitsOutput { public: // @param fitsfile Output filename // @param ms input // @param column specifies which "data" column to write // ("observed", "calibrated", "model") MSFitsOutput( const String& fitsfile, const MeasurementSet& ms, const String& column ); // @param startChan 1st channel // @param nchan # of channels // @param stepChan # of channels to stride by // @param avgChan average every N channels void setChannelInfo( Int startChan, Int nchan, Int stepChan, Int avgChan ); // @param writeSysCal whether to write the system calibration table void setWriteSysCal(Bool writeSysCal); // @param asMultiSource If true a multi-source UVFits file is written. void setAsMultiSource(Bool asMultiSource); // @param combineSpw If true it attempts to write the spectral windows as // IFs. This is necessary for many aips tasks, and // for difmap. void setCombineSpw(Bool combineSpw); // @param writeStation If true uses pad instead of antenna names. void setWriteStation(Bool writeStation); void setSensitivity(Double sensitivity); // @param padWithFlags If true and combineSpw==true, fill spws with flags // as needed to fit the IF structure. Does not yet // support spws with different shapes. void setPadWitFlags(Bool padWithFlags); void setFieldNumber(uInt fieldNumber); // @param overwrite overwrite existing file? void setOverwrite(Bool overwrite); // write the uvfits file. void write() const; // Convert a MeasurementSet to random group UVFITS. // @param fitsfile Output filename // @param ms input // @param column specifies which "data" column to write // ("observed", "calibrated", "model") // @param startchan 1st channel // @param nchan # of channels // @param stepchan # of channels to stride by // @param writeSysCal whether to write the system calibration table // @param asMultiSource If true a multi-source UVFits file is written. // @param combineSpw If true it attempts to write the spectral windows as // IFs. This is necessary for many aips tasks, and // for difmap. // @param writeStation If true uses pad instead of antenna names. // @param sensitivity // @param padWithFlags If true and combineSpw==true, fill spws with flags // as needed to fit the IF structure. Does not yet // support spws with different shapes. // @param avgchan average every N channels // @param fieldNumber field number // @param overwrite overwrite existing file? static Bool writeFitsFile( const String& fitsfile, const MeasurementSet& ms, const String& column, Int startchan=0, Int nchan=1, Int stepchan=1, Bool writeSysCal = False, Bool asMultiSource = False, Bool combineSpw=False, Bool writeStation=False, Double sensitivity=1.0, const Bool padWithFlags=false, Int avgchan=1, uInt fieldNumber=0, Bool overwrite=False ); private: const String _fitsfile, _column; const MeasurementSet _ms; Int _startChan, _nchan, _stepChan, _avgChan; Bool _writeSysCal, _asMultiSource, _combineSpw, _writeStation, _padWithFlags, _overwrite; Double _sensitivity; uInt _fieldNumber; // Write the main table. // @param refPixelFreq // @param refFreq // @param chanbw // @param outFITSFile // @param spwidMap spwidMap[inp_spw] = output_spw, if inp_spw is selected // -1 otherwise. // @param nrspw # of selected spws. // @param fieldidMap fieldidMap[inp_fld] = output_fld, if inp_fld is selected // -1 otherwise. // @param asMultiSource If true, write a multisource UVFITS file. std::shared_ptr _writeMain( Int& refPixelFreq, Double& refFreq, Double& chanbw, const String& outFITSFile, const Block& spwidMap, Int nrspw, const Block& fieldidMap, Bool asMultiSource ) const; // Write the FQ table. // If combineSpw is True, all spectral-windows are written in one // row of the FITS table. static Bool _writeFQ(std::shared_ptr output, const MeasurementSet& ms, const Block& spwidMap, Int nrspw, Double refFreq, Int refPixelFreq, Double chanbw, Bool combineSpw, Int chanstart = 0, Int nchan = -1, Int chanstep = 1, Int avgchan = 1 ); // Write the AN table. static Bool _writeAN( std::shared_ptr output, const MeasurementSet& ms, Double refFreq, Bool writeStation ); // Write the SU table. static Bool _writeSU( std::shared_ptr output, const MeasurementSet& ms, const Block& fieldidMap, Int nrfield, const Block& spwidMap, Int nrspw ); // Write the TY table. static Bool _writeTY( std::shared_ptr output, const MeasurementSet& ms, const Table& syscal, const Block& spwidMap, uInt nrif, Bool combineSpw ); // Write the GC table. static Bool _writeGC( std::shared_ptr output, const MeasurementSet& ms, const Table& syscal, const Block& spwidMap, uInt nrif, Bool combineSpw, Double sensitivity, Int refPixelFreq, Double refFreq, Double chanbw ); // Write the WX table. static Bool _writeWX(std::shared_ptr output, const MeasurementSet& ms); // Write the SY table. static Bool _writeSY( std::shared_ptr output, const MeasurementSet& ms, Table& syspower, Int nspw, const Block& spwIDMap, Bool combineSpw ); // Convert time to day and fraction. static void timeToDay(Int& day, Double& dayFraction, Double time); // Get the time and hourangle from the MS at the given row. // It uses the field-id and observation-id to calculate the hourangle. static void getStartHA ( Double& startTime, Double& startHA, const MeasurementSet& ms, uInt rownr ); // Discern the antenna numbers that go into UVFITS static void _handleAntNumbers(const MeasurementSet& ms,Vector& antnumbers); // Handle the SYSCAL table. // It skips the entries not needed and sorts it in the correct order. static Table handleSysCal ( const MeasurementSet& ms, const Vector& spwids, Bool isSubset ); // Determine which ids are selected in the main table // (used for fields and spectral-window). // @param map (Really an output here, not an input.) // spwidMap[inp_id] = output_id, if inp_id is selected // -1 otherwise. // @param selids (Really an output here, not an input.) // A list of the selected input IDs. // @param allids (Really is an input, not an output!) // IDs to consider. // @return number of selected IDs in allids static Int _makeIdMap( Block& map, Vector& selids, const Vector& allids ); // Find the end of a group of rows with the same // time(_centroid) (within 0.25 * ininterval(rownr)), // baseline #, // and, if asMultiSource, field ID. // @param rownr Row # to start from. // @param nrow # of rows in the columns. // @param nif # of IFs // @param timec time(_centroid) col // @param ininterval used to set tolerance on changes in timec. // @param ant1 ID of baseline's antenna 1. // @param ant2 ID of baseline's antenna 2. // @param asMultiSource If false, treat fieldid as unattached + prone to segfault // @param fieldid // @return Last row # with the same time, baseline, and apparent field as rownr. // @warning Assumes that the columns are sorted by time(_centroid), ant1, // ant2 (, field, DDID). static uInt get_tbf_end( const uInt rownr, const uInt nrow, const uInt nif, const ScalarColumn& timec, const ScalarColumn& ininterval, const ScalarColumn& ant1, const ScalarColumn& ant2, const Bool asMultiSource, const ScalarColumn& fieldid ); static void _checkReceptorAngles( const Vector& ra0, Vector& ra1, Int antnum ); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/msfits/MSFits/MSFitsOutputAstron.cc000066400000000000000000002115431476623553700220700ustar00rootroot00000000000000//# MSFitsOutputAstron: Astron MS to UVFITS //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., 675 Mass Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // for atoi() #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN static String toFITSDate(const MVTime &time) { String date, timesys; FITSDateUtil::toFITS(date, timesys, time); return date; } // MJD seconds to day number and day fraction void MSFitsOutputAstron::timeToDay(Int &day, Double &dayFraction, Double time) { const Double JDofMJD0=2400000.5; time /= C::day; // now in days; time += JDofMJD0; // now in JD day = Int(time); dayFraction = time - floor(time); } Bool MSFitsOutputAstron::writeFitsFile(const String& fitsfile, const MeasurementSet& ms, const String& column, Int startchan, Int nchan, Int stepchan, Bool writeSysCal, Bool asMultiSource, Bool combineSpw, Bool writeStation, Double sensitivity) { LogIO os(LogOrigin("MSFitsOutputAstron", "writeFitsFile")); // A FITS table can handle only Int nrows. if (ms.nrow() > static_cast(std::numeric_limits::max())) { throw AipsError("MS " + ms.tableName() + " is too big (#rows exceeds MAX_INT)"); } const uInt nrow = ms.nrow(); String msfile=ms.tableName(); String outfile; // OK, get the output name if (fitsfile == "") { if (msfile.contains(Regex("\\.ms$"))) { String copy = msfile; // need a copy because .before is non-const outfile = copy.before(Regex("\\.ms"),0) + ".fits"; } else { outfile = msfile + ".fits"; } } else { outfile = fitsfile; // Use the supplied name } String errmsg; NewFile fileOK(True); if (!fileOK.valueOK(outfile, errmsg)) { os << LogIO::SEVERE << "Error in output file : " << errmsg << LogIO::POST; return False; } os << LogIO::NORMAL << "Converting MeasurementSet " << ms.tableName() << " to FITS file '" << outfile << "'" << LogIO::POST; // Determine if this MS is a subset of a main MS. Bool isSubset = (nrow != 1+max(ms.rowNumbers())); if (isSubset) { os << LogIO::NORMAL << "MS " << ms.tableName() << " is a subset of another MS" << LogIO::POST; } // Find the number of IF's (spectral-windows). Block spwidMap; Vector spwids; uInt nrspw; { ScalarColumn ddidcol(ms, MS::columnName(MS::DATA_DESC_ID)); nrspw = makeIdMap (spwidMap, spwids, ddidcol.getColumn(), isSubset); } // If not asMultiSource, check if multiple sources are present. Block fieldidMap; uInt nrfield; { ScalarColumn fldidcol(ms, MS::columnName(MS::FIELD_ID)); Vector fldid = fldidcol.getColumn(); if (!asMultiSource) { if (!allEQ (fldid, fldid(0))) { asMultiSource = True; os << LogIO::WARN << "Multiple sources are present, thus written " "as a multi-source FITS file" << LogIO::POST; } } Vector fieldids; nrfield = makeIdMap (fieldidMap, fieldids, fldid, isSubset); } // Write main table. Get freqs and channel-width back. // For non-WSRT, refFreq and refFreq1 are the same, for WSRT they may // be different (line observations). refFreq1 will be handed to the AN // table, so this is consistent with the Main table (WSRT and non-WSRT). Int refPixelFreq; Double refFreq, refFreq1, chanbw; FitsOutput* fitsOutput = writeMain(refPixelFreq, refFreq, refFreq1, chanbw, outfile, ms, column, spwidMap, nrspw, startchan, nchan, stepchan, fieldidMap, asMultiSource, combineSpw); Bool ok = (fitsOutput != 0); if (!ok) { os << LogIO::SEVERE << "Could not write main table\n" << LogIO::POST; } else { os << LogIO::NORMAL << "Writing AIPS FQ table" << LogIO::POST; // Note: handing over refFreq otherwise this goes wrong...The FQ table // lists the offset frequencies wrt a single reference. ok = writeFQ(fitsOutput, ms, spwidMap, nrspw, refFreq, refPixelFreq, chanbw, combineSpw); } if (!ok) { os << LogIO::SEVERE << "Could not write FQ table\n" << LogIO::POST; } else { os << LogIO::NORMAL << "Writing AIPS AN table" << LogIO::POST; // Note: handing over refFreq1 instead of refFreq; see above. Only the // refreq1 of the first IF is needed. ok = writeAN(fitsOutput, ms, refFreq1, writeStation); } if (!ok) { os << LogIO::SEVERE << "Could not write AN table\n" << LogIO::POST; } // Write the SOURCE table. if (ok) { os << LogIO::NORMAL << "Writing AIPS SU table" << LogIO::POST; ok = writeSU(fitsOutput, ms, fieldidMap, nrfield, spwidMap, nrspw); if (!ok) { os << LogIO::SEVERE << "Could not write SU table\n" << LogIO::POST; } } // If needed, create tables from the SYSCAL table. // Determine if we have to skip the first SYSCAL time. // This is needed for WSRT MS's, where the first time in the SYSCAL // table is the average at the middle of the observation. if (ok && writeSysCal) { Table syscal = handleSysCal (ms, spwids, isSubset); os << LogIO::NORMAL << "writing AIPS TY table" << LogIO::POST; ok = writeTY(fitsOutput, ms, syscal, spwidMap, nrspw, combineSpw); if (!ok) { os << LogIO::SEVERE << "Could not write TY table\n" << LogIO::POST; } else { os << LogIO::NORMAL << "Writing AIPS GC table" << LogIO::POST; // Note: handing over refFreq1 instead of refFreq; see above. // Only the refreq1 of the first IF is needed. ok = writeGC(fitsOutput, ms, syscal, spwidMap, nrspw, combineSpw, sensitivity, refPixelFreq, refFreq1, chanbw); } if (!ok) { os << LogIO::SEVERE << "Could not write GC table\n" << LogIO::POST; } } // flush output to disk delete fitsOutput; return ok; } FitsOutput *MSFitsOutputAstron::writeMain(Int& refPixelFreq, Double& refFreq, Double& refFreq1, Double& chanbw, const String &outFITSFile, const MeasurementSet &rawms, const String &column, const Block& spwidMap, Int nrspw, Int chanstart, Int nchan, Int chanstep, const Block& fieldidMap, Bool asMultiSource, Bool combineSpw) { FitsOutput *outfile = 0; LogIO os(LogOrigin("MSFitsOutputAstron", "writeMain")); const uInt nrow = rawms.nrow(); if (nrow == 0) { os << LogIO::SEVERE << "Empty measurement set!" << LogIO::POST; return 0; } Bool doWsrt = False; { MSObservation obsTable(rawms.observation()); if (obsTable.nrow() > 0) { ScalarColumn inarrayname(obsTable, MSObservation::columnName (MSObservation::TELESCOPE_NAME)); doWsrt = inarrayname(0) == "WSRT"; } } MSField fieldTable(rawms.field()); MSFieldColumns msfc(fieldTable); Vector radec = msfc.phaseDirMeas(0).getAngle().getValue(); radec *=180.0/M_PI; // convert to degrees for FITS if (radec(0) < 0) { radec(0) += 360; } String objectname = msfc.name()(0); // First scan the SPECTRAL_WINDOW table to make sure that the data // shape is constant, the correlation type is constant, and that the // frequencies can be represented as f = f0 + i*inc MSDataDescription ddTable = rawms.dataDescription(); MSSpectralWindow spectralTable = rawms.spectralWindow(); MSPolarization polTable = rawms.polarization(); const uInt ndds = ddTable.nrow(); const uInt nspec = spectralTable.nrow(); const uInt npol = polTable.nrow(); if (ndds == 0) { os << LogIO::SEVERE << "No data description table in MS" << LogIO::POST; return 0; } if (nspec == 0) { os << LogIO::SEVERE << "No spectral window table in MS" << LogIO::POST; return 0; } if (npol == 0) { os << LogIO::SEVERE << "No polarization table in MS" << LogIO::POST; return 0; } ScalarColumn spwId(ddTable, MSDataDescription::columnName(MSDataDescription::SPECTRAL_WINDOW_ID)); ScalarColumn polId(ddTable, MSDataDescription::columnName(MSDataDescription::POLARIZATION_ID)); ScalarColumn numcorr(polTable, MSPolarization::columnName(MSPolarization::NUM_CORR)); ScalarColumn numchan(spectralTable, MSSpectralWindow::columnName(MSSpectralWindow::NUM_CHAN)); ArrayColumn frequencies(spectralTable, MSSpectralWindow::columnName(MSSpectralWindow::CHAN_FREQ)); ArrayColumn stokesTypes(polTable, MSPolarization::columnName(MSPolarization::CORR_TYPE)); ScalarColumn totalbw(spectralTable, MSSpectralWindow::columnName(MSSpectralWindow::TOTAL_BANDWIDTH)); ScalarColumn meas_freq_ref(spectralTable, MSSpectralWindow::columnName(MSSpectralWindow::MEAS_FREQ_REF)); // Also find out what the Stokes are and make sure that they are the same // throughout the MS. In principle we could handle the same stokes in // different order by transposing, but this may well never happen. Int numcorr0 = 0; Int numchan0 = 0; Double delta = 0; // Must be a vector Double f0 = 0; Double f0_org = 0 ; // Needed for WSRT, to remember the frequency of chan0 Double bw0 = 0; Vector stokes; uInt i; for (i=0; i= 0) { const Int s = spwId(i); const Int p = polId(i); // Get channel width. Vector freqs = frequencies(s); if (freqs.nelements() > 1) { delta = freqs(1) - freqs(0); } else { delta = totalbw(0); if (doWsrt && (delta > 0)) delta = - delta; } // If first time, set the various values. if (numcorr0 == 0) { numcorr0 = numcorr(p); numchan0 = numchan(s); if (numcorr0 <= 0 || numchan0 <= 0) { os << LogIO::SEVERE << "Number of correlations or channels is zero" << LogIO::POST; return 0; } f0 = freqs(0); bw0 = delta; chanbw = abs(delta); stokes = stokesTypes(p); if((nchan >0 ) && (chanstep > 0 ) && (chanstart >= 0) && ((nchan*chanstep+chanstart) <= numchan0) ){ f0 = freqs(chanstart); bw0= delta*chanstep; } else { nchan=numchan0; chanstep=1; chanstart=0; } } // If WSRT line, we need to take the central frequency from the NFRA_ // table and calculate the frequency of the first channel based on // the fact that this frequency corresponds to that of channel n/2 + 1. // Note that for WSRT the channel frequencies go from high to low, so // delta is negative. f0_org is set to the frequency of channel 0 as // it is in the MS // Do this for the first IVC-band only! if (doWsrt && meas_freq_ref(0) != 5 && i ==0 ) { f0_org = f0; if (rawms.keywordSet().isDefined ("NFRA_TMS_PARAMETERS")) { Table tmsParm = rawms.keywordSet().asTable ("NFRA_TMS_PARAMETERS"); Table sel; TableColumn tc(tmsParm, "NAME"); String tmp; tc.getScalar(0, tmp); // // Get FW1.GeoSkyFreq // Double fw1_geoskyfreq; sel = tmsParm (tmsParm.col("NAME") == "FW1.GeoSkyFreq"); if (sel.nrow() == 0){ cout << "ERROR - FW1.GeoSkyFreq not found - cannot process this MS.\n"; return 0; } else { String aValue; aValue = ScalarColumn(sel, "VALUE")(0); // // Find the comma's, // Add the substring as Double // // The first comma is located before the loop // The last Double is added after the loop // vector rtn; Int j = aValue.find(','); while (j > 0){ Double d = atof(aValue.substr(0, j).c_str()); rtn.push_back(d); aValue = aValue.substr(j+1); j = aValue.find(','); } // Line ends like "0,0 MHz"; make sure the 'MHz' is stripped off. j = aValue.find(' '); Double d = atof(aValue.substr(0, j).c_str()); rtn.push_back(d); fw1_geoskyfreq = rtn[i] * 1e+6; f0 = fw1_geoskyfreq - bw0 * nchan/2; } } } // Check if values match. if (numcorr(p) != numcorr0) { os << LogIO::SEVERE << "Number of correlations varies in the MS" << LogIO::POST; return 0; } if (numchan(s) != numchan0) { os << LogIO::SEVERE << "Number of channels varies in the MS" << LogIO::POST; return 0; } if (!allEQ(stokes, stokesTypes(p))) { os << LogIO::SEVERE << "Stokes types vary for different spectral windows" << LogIO::POST; return 0; } if (!near(abs(delta), chanbw, 1.0e-5)) { os << LogIO::SEVERE << "Bandwidth varies across spectral windows" << LogIO::POST; return 0; } for (uInt j=1; j= 0, or descending order if < 0. Vector stokesIndex(numcorr0); if (stokes(0) >= 0) { GenSortIndirect::sort(stokesIndex, stokes); } else { GenSortIndirect::sort(stokesIndex, stokes, Sort::Descending); } // OK, make sure that we can represent the stokes in FITS if (stokes.nelements() > 2) { Int delta = stokes(stokesIndex(1)) - stokes(stokesIndex(0)); for (i=2; i inddid(rawms, MS::columnName(MS::DATA_DESC_ID)); desc.addField("freqsel", TpFloat); // SOURCE and INTTIM only in multi-source table if (asMultiSource) { desc.addField("source", TpFloat); desc.addField("inttim", TpFloat); } // "Optional" keywords Record ek; // ek == extra keys // BSCALE BZERO BUNIT ek.define("bscale", 1.0); ek.define("bzero", 0.0); String bunit = "UNCALIB"; { TableColumn indata (rawms, columnName); if (indata.keywordSet().isDefined("QuantumUnit") && indata.keywordSet().dataType("QuantumUnit") == TpString) { indata.keywordSet().get("QuantumUnit", bunit); bunit.upcase(); } } ek.define("bunit", bunit); // CTYPE CRVAL CDELT CRPIX CROTA ek.define("ctype2", "COMPLEX"); ek.define("crval2", 1.0); ek.define("cdelt2", 1.0); ek.define("crpix2", 1.0); ek.define("crota2", 0.0); ek.define("ctype3", "STOKES"); ek.define("crval3", stokes(stokesIndex(0))*1.0); if (stokes.nelements() > 1) { ek.define("cdelt3", (stokes(stokesIndex(1)) - stokes(stokesIndex(0)))*1.0); } else { ek.define("cdelt3", 1.0); } ek.define("crpix3", 1.0); ek.define("crota3", 0.0); ek.define("ctype4", "FREQ"); ek.define("crval4", refFreq); ek.define("cdelt4", bw0); if (doWsrt) { if (refPixelFreq != 1){ ek.define("crpix4", Double(1+refPixelFreq)); } else { ek.define("crpix4", Double(refPixelFreq)); } } else { ek.define("crpix4", Double(refPixelFreq)); } ek.define("crota4", 0.0); ek.define("ctype5", "IF"); ek.define("crval5", 1.0); ek.define("cdelt5", 1.0); ek.define("crpix5", 1.0); ek.define("crota5", 0.0); ek.define("ctype6", "RA"); ek.define("crval6", radec(0)); ek.define("cdelt6", 1.0); ek.define("crpix6", 1.0); ek.define("crota6", 0.0); ek.define("ctype7", "DEC"); ek.define("crval7", radec(1)); ek.define("cdelt7", 1.0); ek.define("crpix7", 1.0); ek.define("crota7", 0.0); // PTYPE PSCALE PZERO ek.define("ptype1", "UU"); ek.define("pscal1", 1.0); ek.define("pzero1", 0.0); ek.define("ptype2", "VV"); ek.define("pscal2", 1.0); ek.define("pzero2", 0.0); ek.define("ptype3", "WW"); ek.define("pscal3", 1.0); ek.define("pzero3", 0.0); ek.define("ptype4", "DATE"); ek.define("pscal4", 1.0); ek.define("pzero4", 0.0); ek.setComment("ptype4", "Day number"); ek.define("ptype5", "DATE"); ek.define("pscal5", 1.0); ek.define("pzero5", 0.0); ek.setComment("ptype5", "Day fraction"); ek.define("ptype6", "BASELINE"); ek.define("pscal6", 1.0); ek.define("pzero6", 0.0); ek.define("ptype7", "FREQSEL"); ek.define("pscal7", 1.0); ek.define("pzero7", 0.0); if (asMultiSource) { ek.define("ptype8", "SOURCE"); ek.define("pscal8", 1.0); ek.define("pzero8", 0.0); ek.define("ptype9", "INTTIM"); ek.define("pscal9", 1.0); ek.define("pzero9", 0.0); } // EXTEND - already written by FITSGroupWriter // ek.define("extend", True); // BLOCKED - already written by FITSGroupWriter // ek.define("blocked", True); // OBJECT if (asMultiSource) { ek.define("object", "MULTI"); } else { ek.define("object", objectname); } // OBS-TIME { ScalarColumn intm(rawms, MS::columnName(MS::TIME)); ek.define("date-obs", toFITSDate(intm(0)/C::day)); // First time entry } // EPOCH Bool foundEpoch = False; String dirtype = msfc.phaseDirMeas(0).getRefString(); if (dirtype.contains("2000")) { ek.define("epoch", 2000.0); foundEpoch = True; } else if (dirtype.contains("1950")) { ek.define("epoch", 1950.0); foundEpoch = True; } if (!foundEpoch) { os << LogIO::SEVERE << "Cannot deduce MS epoch. Assuming J2000" << LogIO::POST; ek.define("epoch", 2000.0); } // TELESCOP INSTRUME MSObservationColumns obsC(rawms.observation()); if (obsC.nrow() == 0) { os << LogIO::SEVERE << "No Observation info!" << LogIO::POST; return 0; } ek.define("telescop", obsC.telescopeName()(0)); ek.define("instrume", obsC.telescopeName()(0)); ek.define("observer", obsC.observer()(0)); ek.define("sortord", "TB"); // Miriad needs a weight scale factor (otherwise all weights get 0). // It is the proper AIPS way to do it as a history record. ek.define("history", "AIPS WTSCAL = 1.0"); // Check that an integral number of SPWs fit in the MS. uInt nif = 1; if (combineSpw) { nif = nrspw; if (nrow%nif != 0) { os << LogIO::SEVERE << "The number of rows per spectral-window varies;" " cannot combine spectral windows" << LogIO::POST; return 0; } } // Finally, make the writer FITSGroupWriter writer(outFITSFile, desc, nrow/nif, ek, False); outfile = writer.writer(); // DATA - out RecordFieldPtr< Array > odata(writer.row(), "data"); RecordFieldPtr ouu(writer.row(), "u"); RecordFieldPtr ovv(writer.row(), "v"); RecordFieldPtr oww(writer.row(), "w"); RecordFieldPtr odate1(writer.row(), "date1"); RecordFieldPtr odate2(writer.row(), "date2"); RecordFieldPtr obaseline(writer.row(), "baseline"); RecordFieldPtr ofreqsel(writer.row(), "freqsel"); RecordFieldPtr osource; RecordFieldPtr ointtim; if (asMultiSource) { osource = RecordFieldPtr (writer.row(), "source"); ointtim = RecordFieldPtr (writer.row(), "inttim"); } Bool deleteIptr; Array indatatmp(IPosition(2, numcorr0, numchan0)); const Complex *iptr = indatatmp.getStorage(deleteIptr); Bool deleteWtPtr; Matrix inwttmp(numcorr0, numchan0); const Float *wptr = inwttmp.getStorage(deleteWtPtr); Bool deleteFlagPtr; Array inflagtmp(IPosition(2, numcorr0, numchan0)); const Bool *fptr = inflagtmp.getStorage(deleteFlagPtr); Bool deleteOptr; Float *optr = (*odata).getStorage(deleteOptr); Bool deleteIndPtr; const uInt *indptr = stokesIndex.getStorage(deleteIndPtr); // Do we need to check units? I think the MS rules are that units cannot // be changed. Vector uvw(3); Int day; Double dayFraction; const Double oneOverC = 1.0 / C::c; // Sort the table in order of TIME, ANTENNA1, ANTENNA2, FIELDID, SPWID. // Iterate through the table on the first 4 fields. Block sortNames(5); sortNames[0] = MS::columnName(MS::TIME); sortNames[1] = MS::columnName(MS::ANTENNA1); sortNames[2] = MS::columnName(MS::ANTENNA2); sortNames[3] = MS::columnName(MS::FIELD_ID); sortNames[4] = MS::columnName(MS::DATA_DESC_ID); Table sortTable = rawms.sort (sortNames); // Make objects for the various columns. ArrayColumn indata(sortTable, columnName); ArrayColumn inweightscalar(sortTable, MS::columnName(MS::WEIGHT)); ArrayColumn inweightarray; if (hasWeightArray) { inweightarray.attach(sortTable, MS::columnName(MS::WEIGHT_SPECTRUM)); } ScalarColumn inrowflag(sortTable, MS::columnName(MS::FLAG_ROW)); ArrayColumn indataflag(sortTable, MS::columnName(MS::FLAG)); ArrayColumn inuvw(sortTable, MS::columnName(MS::UVW)); ScalarColumn intime(sortTable, MS::columnName(MS::TIME)); ScalarColumn inant1(sortTable, MS::columnName(MS::ANTENNA1)); ScalarColumn inant2(sortTable, MS::columnName(MS::ANTENNA2)); ScalarColumn inarray(sortTable, MS::columnName(MS::ARRAY_ID)); ScalarColumn inspwinid(sortTable, MS::columnName(MS::DATA_DESC_ID)); ScalarColumn infieldid; ScalarColumn inexposure; if (asMultiSource) { infieldid.attach (sortTable, MS::columnName(MS::FIELD_ID)); inexposure.attach (sortTable, MS::columnName(MS::EXPOSURE)); } // Check if first cell has a WEIGHT of correct shape. if (hasWeightArray) { IPosition shp = inweightarray.shape(0); if (shp.nelements() > 0 && !shp.isEqual(inwttmp.shape())) { hasWeightArray = False; os << LogIO::WARN << "WEIGHT_SPECTRUM is ignored (incorrect shape)" << LogIO::POST; } } // Loop through all rows. ProgressMeter meter(0.0, nrow*1.0, "UVFITS Writer", "Rows copied", "", "", True, nrow/100); Int rownr = -1; for (i=0; i wght = inweightscalar(rownr); for (Int p = 0; p < numcorr0; p++) { inwttmp.row(p) = wght(p); } } // We should optimize this loop more, probably do frequency as // the inner loop? Vector realcorr(numcorr0); realcorr.set(0); Vector imagcorr(numcorr0); imagcorr.set(0); Vector wgtaver(numcorr0); wgtaver.set(0); Int chancounter=0; for (Int k=chanstart; k< (nchan*chanstep+chanstart); k++) { if(chancounter == chanstep){ realcorr.set(0); imagcorr.set(0); wgtaver.set(0); chancounter=0; } ++chancounter; for (Int j=0; j 0){ outptr[0] = realcorr[j]/wgtaver[j]; outptr[1] = imagcorr[j]/wgtaver[j]; } else{ outptr[0]=0.0; outptr[1]=0.0; } if (rowFlag) { // FLAGged outptr[2] = -wgtaver[j]; } else { // NOT FLAGged outptr[2] = wgtaver[j]; } outptr += 3; } } } } // Random parameters // UU VV WW inuvw.get(i, uvw); *ouu = uvw(0) * oneOverC; *ovv = uvw(1) * oneOverC; *oww = uvw(2) * oneOverC; // TIME timeToDay(day, dayFraction, intime(i)); *odate1 = day; *odate2 = dayFraction; // BASELINE *obaseline = (inant1(i)+1)*256 + inant2(i) + 1 + inarray(i)*0.01; // FREQSEL (in the future it might be FREQ_GRP+1) if (combineSpw) { *ofreqsel = 1; } else { *ofreqsel = 1 + spwidMap[inspwinid(i)]; } // SOURCE // INTTIM if (asMultiSource) { *osource = 1 + fieldidMap[infieldid(i)]; *ointtim = inexposure(i); } writer.write(); } // changing chanbw to output one chanbw=bw0; // Make sure refFreq is again the frequency as in the spectral window table // This is required to correctly write the FQ table (uses refFreq and the // original channelfreqs as in the MS to determine a 1-channel offset) // refFreq1 is WSRT specific; must also be handed over to the AN table // and the GC table for consequent values in the UVFits file and tables. // For non-line-WSRT and non-WSRT, reqFreq1 and refFreq are the same, so // all goes well. refFreq1 = refFreq; if (doWsrt && f0_org > 0) { refFreq = f0_org + f0RefPix * bw0; } return outfile; } Bool MSFitsOutputAstron::writeFQ(FitsOutput *output, const MeasurementSet &ms, const Block& spwidMap, Int nrspw, Double refFreq, Int refPixelFreq, Double chanbw, Bool combineSpw) { LogIO os(LogOrigin("MSFitsOutputAstron", "writeFQ")); MSSpectralWindow specTable(ms.spectralWindow()); ArrayColumn inchanfreq (specTable, MSSpectralWindow::columnName(MSSpectralWindow::CHAN_FREQ)); ScalarColumn intotbw (specTable, MSSpectralWindow::columnName(MSSpectralWindow::TOTAL_BANDWIDTH)); ScalarColumn insideband (specTable, MSSpectralWindow::columnName(MSSpectralWindow::NET_SIDEBAND)); Bool doWsrt = False; String telescopeName; { MSObservation obsTable(ms.observation()); if (obsTable.nrow() > 0) { ScalarColumn inarrayname(obsTable, MSObservation::columnName (MSObservation::TELESCOPE_NAME)); doWsrt = inarrayname(0) == "WSRT"; telescopeName=inarrayname(0); } } // ##### Header Record header; // NO_IF const uInt nwin = specTable.nrow(); os << LogIO::NORMAL << "Found " << nrspw << " spectral windows " << LogIO::POST; // If all spw's are combined, we have a single freq group. // Otherwise each spectral-window is a group. IPosition shape(1, 1); Int nentr = nrspw; if (combineSpw) { shape(0) = nrspw; nentr = 1; } header.define("EXTNAME", "AIPS FQ"); // EXTNAME header.define("EXTVER", 1); // EXTVER header.define("NO_IF", Int(shape(0))); // NO_IF // Table description RecordDesc desc; Record stringLengths; // no strings Record units; desc.addField("FRQSEL", TpInt); // FRQSEL desc.addField("IF FREQ", TpArrayDouble, shape); // IF FREQ units.define ("IF FREQ", "HZ"); desc.addField("CH WIDTH", TpArrayFloat, shape); // CH WIDTH units.define ("CH WIDTH", "HZ"); desc.addField("TOTAL BANDWIDTH", TpArrayFloat, shape); // TOTAL BANDWIDTH units.define ("TOTAL BANDWIDTH", "HZ"); desc.addField("SIDEBAND", TpArrayInt, shape); // SIDEBAND FITSTableWriter writer(output, desc, stringLengths, nentr, header, units, False); RecordFieldPtr freqsel(writer.row(), "FRQSEL"); RecordFieldPtr< Array > iffreq(writer.row(), "IF FREQ"); RecordFieldPtr< Array > ifwidth(writer.row(), "CH WIDTH"); RecordFieldPtr< Array > totbw(writer.row(), "TOTAL BANDWIDTH"); RecordFieldPtr< Array > sideband(writer.row(), "SIDEBAND"); IPosition inx(1,0); for (uInt i=0; i= 0) { *freqsel = 1 + spwidMap[i]; Vector freqs = inchanfreq(i); if (telescopeName == "IRAM PDB" || telescopeName == "IRAM_PDB") { (*iffreq)(inx)=0.0; } else { (*iffreq)(inx) = freqs(refPixelFreq-1) - refFreq; } if (freqs.nelements() > 1) { if (doWsrt) { (*ifwidth)(inx) = abs(chanbw); } else { (*ifwidth)(inx) = (chanbw); } } else { (*ifwidth)(inx) = intotbw(i); } (*totbw)(inx) = intotbw(i); if (doWsrt) { if (freqs(1) < freqs(0)) { (*sideband)(inx) = -1; } else { (*sideband)(inx) = 1; } } else { (*sideband)(inx) = insideband(i); } // Write the current row if not combined. if (combineSpw) { inx(0)++; } else { writer.write(); } } } // Write the row if everything is combined. if (combineSpw) { *freqsel = 1; writer.write(); } return True; } Bool MSFitsOutputAstron::writeAN(FitsOutput *output, const MeasurementSet &ms, Double refFreq, Bool writeStation) { LogIO os(LogOrigin("MSFitsOutputAstron", "writeAN")); MSObservation obsTable(ms.observation()); ScalarColumn inarrayname(obsTable, MSObservation::columnName (MSObservation::TELESCOPE_NAME)); const uInt narray = obsTable.nrow(); if (narray == 0) { os << LogIO::SEVERE << "No Observation info!" << LogIO::POST; return False; } // Calculate GSTIA0, DEGPDY, UT1UTC, and IATUTC. MEpoch measTime = MSColumns(ms).timeMeas()(0); MEpoch utctime = MEpoch::Convert (measTime, MEpoch::UTC) (); MEpoch iattime = MEpoch::Convert (measTime, MEpoch::IAT) (); MEpoch ut1time = MEpoch::Convert (measTime, MEpoch::UT1) (); Double utcsec = utctime.get("s").getValue(); Double ut1sec = ut1time.get("s").getValue(); Double iatsec = iattime.get("s").getValue(); // Use the beginning of the IAT day to calculate the GMST. Double utcday = floor(utctime.get("d").getValue()); Double iatday = floor(iattime.get("d").getValue()); Double gstday, gstday1; { // Use IAT=0 to get GST: Quantum itime(iatday, "d"); MEpoch ia0time (itime, MEpoch::UTC); MEpoch gsttime = MEpoch::Convert (ia0time, MEpoch::GMST) (); gstday = gsttime.get("d").getValue(); } Double gstdeg = 360 * (gstday - floor(gstday)); { // #degrees/IATday is the difference between this and the next day. Quantum itime(iatday+1, "d"); MEpoch ia0time (itime, MEpoch::UTC); MEpoch gsttime = MEpoch::Convert (ia0time, MEpoch::GMST) (); gstday1 = gsttime.get("d").getValue(); } Double degpdy = 360 * (gstday1 - gstday); // PolarMotion gives -x and -y. // Need to be multiplied by earth radius to get them in meters. const Euler& polarMotion = MeasTable::polarMotion (utcday); // Each array gets its own antenna table for (uInt arraynum=0; arraynum < narray; arraynum++) { // Get the observatory's position and convert to ITRF. String obsName = inarrayname(arraynum); MPosition pos; MeasTable::Observatory(pos, obsName); MPosition itrfpos = MPosition::Convert (pos, MPosition::ITRF)(); MVPosition mvpos = itrfpos.getValue(); Vector arraypos = mvpos.getValue(); // Prepare handling of peculiar UVFITS antenna position conventions: // VLA and WSRT requires rotation into local frame: String arrayName = inarrayname(arraynum); Bool doRot = (arrayName=="VLA" || arrayName=="WSRT"); Matrix posRot = Rot3D(0,0.0); if (doRot) { // form rotation around Z-axis by longitude: Double posLong = mvpos.getLong(); posRot=Rot3D(2,-posLong); // opposite rotation cf MSFitsInput } // "VLBI" (==arraypos<1000m) requires y-axis reflection: // (ATCA looks like VLBI in UVFITS, but is already RHed.) // It looks as if WSRT needs y-axis reflection for UVFIX. Bool doRefl=((arrayName=="WSRT") || ((arrayName!="ATCA") && allLE(abs(arraypos),1000.0))); // #### Header Record header; header.define("EXTNAME", "AIPS AN"); // EXTNAME header.define("EXTVER", Int(arraynum+1)); // EXTVER header.define("ARRAYX", arraypos(0)); // ARRAYX header.define("ARRAYY", arraypos(1)); // ARRAYY header.define("ARRAYZ", arraypos(2)); // ARRAYZ header.define("GSTIA0", gstdeg); // GSTIA0 header.define("DEGPDY", degpdy); // DEGPDY header.define("FREQ", refFreq); // FREQ header.define("RDATE", toFITSDate(measTime.get("s"))); // RDATE header.define("POLARX", -polarMotion(0) * 6356752.31); // POLARX header.define("POLARY", -polarMotion(1) * 6356752.31); // POLARY header.define("UT1UTC", ut1sec-utcsec); // UT1UTC header.define("IATUTC", iatsec-utcsec); // IATUTC header.define("TIMSYS", measTime.getRefString()); // TIMSYS header.define("ARRNAM", inarrayname(arraynum)); // ARRNAM header.define("NUMORB", 0); // NUMORB header.define("NOPCAL", 0); // NOPCAL header.define("POLTYPE", " "); // POLTYPE // NOT in going aips // header.define("DATUTC", 0.0); // header.define("P_REFANT", 15); // header.define("P_DIFF01", 0.0); // #### Row description RecordDesc desc; Record strlengths, units; desc.addField("ANNAME", TpString); // ANNAME strlengths.define("ANNAME", 8); desc.addField("STABXYZ", TpArrayDouble, // STABXYZ IPosition(1, 3)); units.define ("STABXYZ", "METERS"); desc.addField("ORBPARM", TpArrayDouble, // ORBPARM IPosition(1,0)); desc.addField("NOSTA", TpInt); // NOSTA desc.addField("MNTSTA", TpInt); // MNTSTA desc.addField("STAXOF", TpFloat); // STAXOF units.define ("STAXOF", "METERS"); desc.addField("POLTYA", TpString); // POLTYA strlengths.define("POLTYA", 1); desc.addField("POLAA", TpFloat); // POLAA units.define ("POLAA", "DEGREES"); /// desc.addField("POLCALA", TpArrayFloat, // POLCALA /// IPosition(1,0)); desc.addField("POLCALA", TpFloat); // POLCALA desc.addField("POLTYB", TpString); // POLTYB strlengths.define("POLTYB", 1); desc.addField("POLAB", TpFloat); // POLAB units.define ("POLAB", "DEGREES"); /// desc.addField("POLCALB", TpArrayFloat, // POLCALB /// IPosition(1,0)); desc.addField("POLCALB", TpFloat); // POLCALB MSAntenna antennaTable = ms.antenna(); MSAntennaColumns antennaCols (antennaTable); // SELECT antennas for the current sub-array // MSAntenna antennaTable = ms.antenna() //(ms.antenna().col(MSAntenna::columnName(MSAntenna::ARRAY_ID)) == // Int(arraynum)); ScalarColumn inantname(antennaCols.station()); ScalarColumn antid(antennaCols.name()); ScalarColumn inantmount(antennaCols.mount()); MPosition::ScalarColumn inantposition(antennaCols.positionMeas()); ArrayColumn inantoffset(antennaCols.offset()); const uInt nant = antennaTable.nrow(); os << LogIO::NORMAL << "Found " << nant << " antennas in array #" << arraynum+1 << LogIO::POST; MSFeed feedTable = ms.feed(); MSFeedColumns feedCols (feedTable); ArrayColumn inpoltype(feedCols.polarizationType()); ScalarColumn inantid(feedCols.antennaId()); FITSTableWriter writer(output, desc, strlengths, nant, header, units, False); RecordFieldPtr anname(writer.row(), "ANNAME"); RecordFieldPtr< Array > stabxyz(writer.row(), "STABXYZ"); RecordFieldPtr< Array > orbparm(writer.row(), "ORBPARM"); RecordFieldPtr nosta(writer.row(), "NOSTA"); RecordFieldPtr mntsta(writer.row(), "MNTSTA"); RecordFieldPtr staxof(writer.row(), "STAXOF"); RecordFieldPtr poltya(writer.row(), "POLTYA"); RecordFieldPtr polaa(writer.row(), "POLAA"); /// RecordFieldPtr< Array > polcala(writer.row(), "POLCALA"); RecordFieldPtr polcala(writer.row(), "POLCALA"); RecordFieldPtr poltyb(writer.row(), "POLTYB"); RecordFieldPtr polab(writer.row(), "POLAB"); /// RecordFieldPtr< Array > polcalb(writer.row(), "POLCALB"); RecordFieldPtr polcalb(writer.row(), "POLCALB"); // Set the ones we're not going to change once *orbparm = 0.0; *poltya = " "; *polaa = 0.0; *polcala = 0.0; *poltyb = " "; *polab = 0.0; *polcalb = 0.0; Block id(nant); Bool useAntId = True; for (uInt a = 0; a < nant; a++) { const String& antName = antid(a) ; if (antName.matches(RXint)) { id[a] = atoi(antName.chars()); } else { useAntId = False; break; } } if (useAntId == False) { for (uInt a = 0; a < nant; a++) { id[a] = a + 1; // 1 relative antenna numbers in FITS } } // A hack for old WSRT observations which stored the antenna name // in the STATION column instead of the NAME column. // So if all NAMES are equal use STATIONS (unless they are all equal). // Also: if writeStation==True use station names instead of antenna names // for the output fits file (input fits file tends to have this). Vector anames = antid.getColumn(); if (anames.nelements() > 0) { if (writeStation || allEQ (anames, anames(0))) { Vector stations = inantname.getColumn(); if (! allEQ (stations, stations(0))) { anames = stations; } } } for (uInt antnum=0; antnum corstabxyz = antpos.getValue().getValue() - arraypos; // Do UVFITS-dependent position corrections: if (doRot) corstabxyz = product(posRot,corstabxyz); if (doRefl) corstabxyz(1)=-corstabxyz(1); *stabxyz = corstabxyz; *nosta = id[antnum]; String mount = upcase(inantmount(antnum)); if (mount.contains("ALT-AZ")) { *mntsta = 0; } else if (mount.contains("EQUATORIAL")) { *mntsta = 1; } else if (mount.contains("ORBIT")) { *mntsta = 2; } else { *mntsta = -1; // ??? } *staxof = inantoffset(antnum)(IPosition(1,0)); // OK, try to find if we're L/R or X/Y // This probably breaks down when we have more than one // polarization type on different feeds (unlikely) or // different spectral windows (more likely). const uInt nmax = feedTable.nrow(); Bool found = False; *poltya = " "; *poltyb = " "; for (uInt i=0; i poltypes = inpoltype(i); if (poltypes.nelements() >= 1) { *poltya = poltypes(0); } if (poltypes.nelements() >= 2) { *poltyb = poltypes(1); } } } if (!found) { os << LogIO::SEVERE << "Could not find polarization types for antenna " << antnum << LogIO::POST; } writer.write(); } } return True; } Bool MSFitsOutputAstron::writeSU(FitsOutput *output, const MeasurementSet &ms, const Block& fieldidMap, Int nrfield, const Block& /*spwidMap*/, Int nrspw) { LogIO os(LogOrigin("MSFitsOutputAstron", "writeSU")); // Basically we make the FIELD_ID the source ID. MSField fieldTable(ms.field()); MSFieldColumns msfc(fieldTable); const ScalarColumn& insrcid=msfc.sourceId(); const ScalarColumn& inname=msfc.name(); // If source table exists, access it // This is for case where SOURCE guaranteed to exist: // MSSource sourceTable(ms.source()); // MSSourceColumns sourceColumns(sourceTable); // ColumnsIndex srcInx(sourceTable, "SOURCE_ID"); // RecordFieldPtr srcInxFld(srcInx.accessKey(), "SOURCE_ID"); // This is for case where SOURCE may not exist: // (doesn't work yet!) MSSource* sourceTable=0; MSSourceColumns* sourceColumns=0; ColumnsIndex* srcInx=0; RecordFieldPtr* srcInxFld=0; if (!ms.source().isNull()) { sourceTable = new MSSource(ms.source()); sourceColumns = new MSSourceColumns(*sourceTable); // Create an index for the SOURCE table. // Make a RecordFieldPtr for the SOURCE_ID field in the index key record. srcInx=new ColumnsIndex(*sourceTable, "SOURCE_ID"); srcInxFld= new RecordFieldPtr(srcInx->accessKey(), "SOURCE_ID"); } MSSpectralWindow spectralTable(ms.spectralWindow()); const uInt nrow = fieldTable.nrow(); if (nrow == 0) { os << LogIO::SEVERE << "No field table!" << LogIO::POST; return False; } if (spectralTable.nrow() == 0) { os << LogIO::SEVERE << "No spectral window table!" << LogIO::POST; return False; } ScalarColumn totalbw(spectralTable, MSSpectralWindow::columnName(MSSpectralWindow::TOTAL_BANDWIDTH)); Double totalBandwidth = totalbw(0); // const uInt nsource = sourceTable.nrow(); // this is allowed to be 0 // #### Header Record header; header.define("EXTNAME", "AIPS SU"); // EXTNAME header.define("EXTVER", 1); // EXTVER header.define("NO_IF", nrspw); header.define ("FREQID", 1); String velDef; String velType; // WSRT specific issue: // This is tricky... The AIPS standard says that VELTYP = bary, lsr, etc // and VELDEF = radio or optical. The NFRA keywords in the Spectral Window // table are NFRA_VELOCDEFINITION and NFRA_CONVERSIONTYPE , respectively! // Note that datasets with mixed vel.frames or mixed line/continuum cannot // be handled. if (spectralTable.tableDesc().isColumn ("NFRA_VELOCDEFINITION")) { ScalarColumn velTypeCol (spectralTable, "NFRA_VELOCDEFINITION"); velType = velTypeCol(0); velType.upcase(); header.define("VELTYP", velType); if (spectralTable.tableDesc().isColumn ("NFRA_CONVERSIONTYPE")) { ScalarColumn velDefCol (spectralTable, "NFRA_CONVERSIONTYPE"); velDef = velDefCol(0); velDef.upcase(); header.define("VELDEF", velDef); } } else { os << LogIO::NORMAL << "Not setting velocity types" << LogIO::POST; } // #### Row description RecordDesc desc; Record strlengths, units; desc.addField("ID. NO.", TpInt); desc.addField("SOURCE", TpString); strlengths.define("SOURCE", 16); desc.addField("QUAL", TpInt); desc.addField("CALCODE", TpString); strlengths.define("CALCODE", 4); desc.addField("IFLUX", TpArrayFloat, IPosition(1, nrspw)); units.define ("IFLUX", "JY"); desc.addField("QFLUX", TpArrayFloat, IPosition(1, nrspw)); units.define ("QFLUX", "JY"); desc.addField("UFLUX", TpArrayFloat, IPosition(1, nrspw)); units.define ("UFLUX", "JY"); desc.addField("VFLUX", TpArrayFloat, IPosition(1, nrspw)); units.define ("VFLUX", "JY"); desc.addField("FREQOFF", TpArrayDouble, IPosition(1, nrspw)); units.define ("FREQOFF", "HZ"); desc.addField("BANDWIDTH", TpDouble); units.define ("BANDWIDTH", "HZ"); desc.addField("RAEPO", TpDouble); units.define ("RAEPO", "DEGREES"); desc.addField("DECEPO", TpDouble); units.define ("DECEPO", "DEGREES"); desc.addField("EPOCH", TpDouble); units.define ("EPOCH", "YEARS"); desc.addField("RAAPP", TpDouble); units.define ("RAAPP", "DEGREES"); desc.addField("DECAPP", TpDouble); units.define ("DECAPP", "DEGREES"); desc.addField("LSRVEL", TpArrayDouble, IPosition(1, nrspw)); units.define ("LSRVEL", "M/SEC"); desc.addField("RESTFREQ", TpArrayDouble, IPosition(1, nrspw)); units.define ("RESTFREQ", "HZ"); desc.addField("PMRA", TpDouble); units.define ("PMRA", "DEG/DAY"); desc.addField("PMDEC", TpDouble); units.define ("PMDEC", "DEG/DAY"); FITSTableWriter writer(output, desc, strlengths, nrfield, header, units, False); RecordFieldPtr idno(writer.row(), "ID. NO."); RecordFieldPtr source(writer.row(), "SOURCE"); RecordFieldPtr qual(writer.row(), "QUAL"); RecordFieldPtr calcode(writer.row(), "CALCODE"); RecordFieldPtr< Array > iflux(writer.row(), "IFLUX"); RecordFieldPtr< Array > qflux(writer.row(), "QFLUX"); RecordFieldPtr< Array > uflux(writer.row(), "UFLUX"); RecordFieldPtr< Array > vflux(writer.row(), "VFLUX"); RecordFieldPtr< Array > freqoff(writer.row(), "FREQOFF"); RecordFieldPtr bandwidth(writer.row(), "BANDWIDTH"); RecordFieldPtr raepo(writer.row(), "RAEPO"); RecordFieldPtr decepo(writer.row(), "DECEPO"); RecordFieldPtr epoch(writer.row(), "EPOCH"); RecordFieldPtr raapp(writer.row(), "RAAPP"); RecordFieldPtr decapp(writer.row(), "DECAPP"); RecordFieldPtr< Array > lsrvel(writer.row(), "LSRVEL"); RecordFieldPtr< Array > restfreq(writer.row(), "RESTFREQ"); RecordFieldPtr pmra(writer.row(), "PMRA"); RecordFieldPtr pmdec(writer.row(), "PMDEC"); // Default them all, then we can gradually add more in the loop without // worrying about it. *idno = 0; *source = " "; *qual = 0; *calcode = " "; *iflux = 0.0; *qflux = 0.0; *uflux = 0.0; *vflux = 0.0; *freqoff = 0.0; *bandwidth = totalBandwidth; *raepo = 0.0; *decepo = 0.0; *epoch = 2000.0; *raapp = 0.0; *decapp = 0.0; *lsrvel = 0.0; *restfreq = 0.0; *pmra = 0.0; *pmdec = 0.0; MDirection dir; // Only take those fields which are part of the fieldidMap // (which represents the fields written in the main table). for (uInt fieldnum=0; fieldnum= 0) { *idno = 1 + fieldidMap[fieldnum]; dir=msfc.phaseDirMeas(fieldnum); *source = inname(fieldnum) + " "; if (dir.getRef().getType()==MDirection::B1950) { *epoch = 1950.; } // Use info from SOURCE table if available. // Try to find the SOURCE_ID in the SOURCE table. // If multiple rows found, use the first one. // Use the first spectral line. // Optional access to SOURCE table if (sourceTable) { **srcInxFld = insrcid(fieldnum); Vector rownrs = srcInx->getRowNumbers(); if (rownrs.nelements() > 0) { uInt rownr = rownrs(0); // Name in SOURCE table overides name in FIELD table *source = sourceColumns->name()(rownr) + " ";; if(sourceColumns->sysvel().isDefined(rownr)) { Vector sv (sourceColumns->sysvel()(rownr)); if (sv.nelements() > 0) { *lsrvel = sv(0); } } if(sourceColumns->restFrequency().isDefined(rownr)) { Vector rf (sourceColumns->restFrequency()(rownr)); if (rf.nelements() > 0) { *restfreq = rf(0); } } if (sourceColumns->properMotion().isDefined(rownr)) { Vector pm = sourceColumns->properMotion()(rownr); *pmra = pm(0); *pmdec = pm(1); } *qual = sourceColumns->calibrationGroup()(rownr); *calcode = sourceColumns->code()(rownr) + " "; // Directions have to be converted from radians to degrees. if (sourceColumns->direction().isDefined(rownr)) { dir = sourceColumns->directionMeas()(rownr); } if (dir.getRef().getType()==MDirection::B1950) { *epoch = 1950.; } } } // Write ra/dec as epoch and apparent (in degrees). // Use the time in the field table to calculate apparent. { *raepo = dir.getAngle("deg").getValue()(0); *decepo = dir.getAngle("deg").getValue()(1); MeasFrame frame; frame.set(msfc.timeMeas()(fieldnum)); MDirection::Ref typeout (MDirection::APP, frame); MDirection dirout = MDirection::Convert(dir, typeout)(); *raapp = dirout.getAngle("deg").getValue()(0); *decapp = dirout.getAngle("deg").getValue()(1); } writer.write(); } } os << LogIO::NORMAL << "writing " << nrfield << " sources" << LogIO::POST; // Delete dynamic memory, if nec: if (sourceTable) delete sourceTable; if (sourceColumns) delete sourceColumns; if (srcInx) delete srcInx; if (srcInxFld) delete srcInxFld; return True; } Bool MSFitsOutputAstron::writeTY(FitsOutput *output, const MeasurementSet &ms, const Table& syscal, const Block& spwidMap, uInt nrif, Bool combineSpw) { LogIO os(LogOrigin("MSFitsOutputAstron", "writeTY")); const MSSysCal subtable(syscal); MSSysCalColumns sysCalColumns(subtable); const uInt nrow = syscal.nrow(); if (nrow == 0 || sysCalColumns.tsys().isNull()) { os << LogIO::SEVERE << "No SysCal TY info!" << LogIO::POST; return False; } // Get #pol by taking shape of first tsys from the column. const Int npol = sysCalColumns.tsys().shape(0)(0); if (!combineSpw) { nrif = 1; } IPosition ifShape(1,nrif); const uInt nentries = nrow / nrif; os << LogIO::NORMAL << "Found " << nentries << " TY table entries (" << nrif << " IFs)" << LogIO::POST; // Get reference time (i.e. start time) from the main table. Double refTime; { // get starttime (truncated to days) MSColumns mscol(ms); refTime = floor(mscol.time()(0) / C::day) * C::day; } // ##### Header Record header; header.define("EXTNAME", "AIPS TY"); // EXTNAME header.define("EXTVER", 1); // EXTVER header.define("NO_IF", Int(nrif)); // NO_IF header.define("NO_POL", npol); // NO_POL header.define("REVISION", 10); // REVISION // Table description RecordDesc desc; Record stringLengths; // no strings Record units; desc.addField("TIME", TpFloat); units.define ("TIME", "DAYS"); desc.addField("TIME INTERVAL", TpFloat); units.define ("TIME INTERVAL", "DAYS"); desc.addField("SOURCE ID", TpInt); desc.addField("ANTENNA NO.", TpInt); desc.addField("SUBARRAY", TpInt); desc.addField("FREQ ID", TpInt); desc.addField("TSYS 1", TpArrayFloat, ifShape); units.define ("TSYS 1", "KELVINS"); desc.addField("TANT 1", TpArrayFloat, ifShape); units.define ("TANT 1", "KELVINS"); if (npol == 2) { desc.addField("TSYS 2", TpArrayFloat, ifShape); units.define ("TSYS 2", "KELVINS"); desc.addField("TANT 2", TpArrayFloat, ifShape); units.define ("TANT 2", "KELVINS"); } FITSTableWriter writer(output, desc, stringLengths, nentries, header, units, False); RecordFieldPtr time(writer.row(), "TIME"); RecordFieldPtr interval(writer.row(), "TIME INTERVAL"); RecordFieldPtr sourceId(writer.row(), "SOURCE ID"); RecordFieldPtr antenna(writer.row(), "ANTENNA NO."); RecordFieldPtr arrayId(writer.row(), "SUBARRAY"); RecordFieldPtr spwId(writer.row(), "FREQ ID"); RecordFieldPtr > tsys1(writer.row(), "TSYS 1"); RecordFieldPtr > tant1(writer.row(), "TANT 1"); RecordFieldPtr > tsys2; RecordFieldPtr > tant2; if (npol == 2) { tsys2 = RecordFieldPtr > (writer.row(), "TSYS 2"); tant2 = RecordFieldPtr > (writer.row(), "TANT 2"); } Vector tsysval; for (uInt i=0; i ts1(nrif); Vector ts2(nrif); Vector ta(nrif); ta = 0.; for (uInt j=0; j 0) { if (sysCalColumns.time()(i+j) != tim) { throw (AipsError ("Irregularity in times in SYSCAL subtable")); } } } *tsys1 = ts1; *tant1 = ta;; if (npol == 2) { *tsys2 = ts2; *tant2 = ta; } // Write the current row writer.write(); } return True; } Bool MSFitsOutputAstron::writeGC(FitsOutput *output, const MeasurementSet &ms, const Table& syscal, const Block& /*spwidMap*/, uInt nrif, Bool combineSpw, Double sensitivity, Int refPixelFreq, Double refFreq, Double chanbw) { LogIO os(LogOrigin("MSFitsOutputAstron", "writeGC")); // We need to write an entry per antenna (and spw if !combineSpw). // So sort the SYSCAL table in that order and skip duplicate // spectral-windows. Use insertion sort, since the table is already in order. Block sortNames(2); sortNames[0] = MSSysCal::columnName(MSSysCal::ANTENNA_ID); sortNames[1] = MSSysCal::columnName(MSSysCal::TIME); Table sorcal = syscal.sort (sortNames, Sort::Ascending, Sort::InsSort + Sort::NoDuplicates); // Sort again (without duplicates) to get the nr of antennas. // Remove TIME from the sort columns. // Use insertion sort, because the table is already in order. Int nrant; sortNames.resize (1, True, True); { Table sorcal2 = sorcal.sort (sortNames, Sort::Ascending, Sort::InsSort + Sort::NoDuplicates); nrant = sorcal2.nrow(); } if (nrant == 0) { os << LogIO::SEVERE << "No SysCal GC info!" << LogIO::POST; return False; } // Find nr of IF's or SPW's. Int nrspw = 1; if (!combineSpw) { nrspw = nrif; nrif = 1; } // Get #pol from 1st row in FEED table. const Int npol = MSFeedColumns(ms.feed()).numReceptors()(0); IPosition ifShape(1,nrif); const uInt nentries = nrant*nrspw; os << LogIO::NORMAL << "Found " << nentries << " GC table entries (" << nrif << " IFs, " << npol << " polarizations)" << LogIO::POST; // Get some info from the main table. Int nchan, nstk; Double startTime, startHA; { MSColumns mscol(ms); IPosition shp = mscol.data().shape(0); nstk = shp(0); nchan = shp(1); // Find the start time and HA (from the first row). getStartHA (startTime, startHA, ms, 0); } // Create an iterator (on antenna) for the already sorted table. // Use the first chunk to create the hourangle vector. TableIterator tabiter (sorcal, sortNames, TableIterator::Ascending, TableIterator::NoSort); Vector havec; { Table tableChunk (tabiter.table()); uInt n = tableChunk.nrow(); MSSysCal syscal (tableChunk); MSSysCalColumns sysCalColumns (syscal); // Fill the hourangle vector (which is the same for all subsets). // Its unit is degrees; startHA is in fractions of a circle. // The time is in seconds, so convert that to a full day (circle). // Start the hourangle in degrees. havec.resize (n); Double factor = (Double(366.25) / 365.25) / (24*3600); for (uInt i=0; i havec2(2*nrif, 0.); for (uInt i=0; i antenna(writer.row(), "ANTENNA_NO"); RecordFieldPtr arrayId(writer.row(), "SUBARRAY"); RecordFieldPtr spwId(writer.row(), "FREQ ID"); RecordFieldPtr > type1(writer.row(), "TYPE_1"); RecordFieldPtr > nterm1(writer.row(), "NTERM_1"); RecordFieldPtr > xtype1(writer.row(), "X_TYP_1"); RecordFieldPtr > ytype1(writer.row(), "Y_TYP_1"); RecordFieldPtr > xval1(writer.row(), "X_VAL_1"); RecordFieldPtr > yval1(writer.row(), "Y_VAL_1"); RecordFieldPtr > gain1(writer.row(), "GAIN_1"); RecordFieldPtr > sens1(writer.row(), "SENS_1"); RecordFieldPtr > type2; RecordFieldPtr > nterm2; RecordFieldPtr > xtype2; RecordFieldPtr > ytype2; RecordFieldPtr > xval2; RecordFieldPtr > yval2; RecordFieldPtr > gain2; RecordFieldPtr > sens2; if (npol == 2) { type2 = RecordFieldPtr > (writer.row(), "TYPE_2"); nterm2 = RecordFieldPtr > (writer.row(), "NTERM_2"); xtype2 = RecordFieldPtr > (writer.row(), "X_TYP_2"); ytype2 = RecordFieldPtr > (writer.row(), "Y_TYP_2"); xval2 = RecordFieldPtr > (writer.row(), "X_VAL_2"); yval2 = RecordFieldPtr > (writer.row(), "Y_VAL_2"); gain2 = RecordFieldPtr > (writer.row(), "GAIN_2"); sens2 = RecordFieldPtr > (writer.row(), "SENS_2"); } // Iterate through the table. // Each chunk should have the same size. while (!tabiter.pastEnd()) { Table tableChunk (tabiter.table()); MSSysCal syscal (tableChunk); MSSysCalColumns sysCalColumns (syscal); *antenna = sysCalColumns.antennaId()(0) + 1; // *arrayId = sysCalColumns.arrayId()(0) + 1; *arrayId = 1; if (tableChunk.nrow() != havec.nelements()) { os << LogIO::SEVERE << "SysCal table is irregular!" << " Mismatching #rows for antenna " << *antenna << LogIO::POST; return False; } for (Int spw=0; spw& spwids, Bool isSubset) { LogIO os(LogOrigin("MSFitsOutputAstron", "handleSysCal")); Table syscal(ms.sysCal()); // Only take the antennas found in the main table. // This is better and also solves an NFRA problem where incorrect // antennas were written in the SYSCAL table. Block antFlag; { // Find the maximum antenna number. // Assure that the minimum >= 0. ScalarColumn ant1col(ms, MS::columnName(MS::ANTENNA1)); ScalarColumn ant2col(ms, MS::columnName(MS::ANTENNA2)); Vector ant1 = ant1col.getColumn(); Vector ant2 = ant2col.getColumn(); Int minant1, minant2, maxant1, maxant2; minMax (minant1, maxant1, ant1); minMax (minant2, maxant2, ant2); if (minant1 < 0 || minant2 < 0) { throw (AipsError ("Antenna1 or antenna2 < 0 in MS " + ms.tableName())); } // Make an array which contains a flag True for all antennas in the // main table. Int nrant = 1 + max (maxant1, maxant2); antFlag.resize (nrant); antFlag = False; Bool delAnt1, delAnt2; const Int* ant1ptr = ant1.getStorage (delAnt1); const Int* ant2ptr = ant2.getStorage (delAnt2); uInt nrrow = ant1.nelements(); for (uInt i=0; i antcol(syscal, MSSysCal::columnName(MSSysCal::ANTENNA_ID)); Vector ant = antcol.getColumn(); Int minant, maxant; minMax (minant, maxant, ant); if (minant < 0) { throw (AipsError ("Antenna_id < 0 in SYSCAL " + syscal.tableName())); } uInt nrrow = ant.nelements(); Block rowFlag(nrrow); rowFlag = True; Bool flagged = False; Bool delAnt; const Int* antptr = ant.getStorage (delAnt); for (uInt i=0; i= Int(i)); } break; } } } // If the table is a subset, select the spectral-windows found in // the MS. if (isSubset) { syscal = syscal (syscal.col(MSSysCal::columnName(MSSysCal::SPECTRAL_WINDOW_ID)) .in (TableExprNode(spwids))); } // Sort the SYSCAL table in order of antenna, time, spectral-window. Block sortNames(3); sortNames[0] = MSSysCal::columnName(MSSysCal::ANTENNA_ID); sortNames[1] = MSSysCal::columnName(MSSysCal::TIME); sortNames[2] = MSSysCal::columnName(MSSysCal::SPECTRAL_WINDOW_ID); return syscal.sort (sortNames); } Int MSFitsOutputAstron::makeIdMap (Block& map, Vector& selids, const Vector& allids, Bool isSubset) { // Determine the number of ids and make a mapping of // id number in the table to id number in fits. // Only if the MS is a subset, we have to determine this mapping // explicitly (because then some ids might be left out). Int nrid = 1 + max(allids); map.resize (nrid, True, True); map = -1; if (!isSubset) { selids.resize (nrid); for (Int i=0; i idUsed(nrid, False); Int nrow = allids.nelements(); for (Int i=0; i #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class String; class FitsOutput; class MeasurementSet; class Table; template class Block; // // Write a MeasurementSet to a random group uvfits file. // class MSFitsOutputAstron { public: // Convert a MeasurementSet to random group UVFITS, // specifying the column to write ("observed", "calibrated", "model") and // whether to write the system calibration table. //
        If asMultiSource=True a multi-source UVFits file is written. //
        If combineSpw=True, all spectral-windows of a frequency group // are combined. static Bool writeFitsFile(const String& fitsfile, const MeasurementSet& ms, const String& column, Int startchan=-1, Int nchan=-1, Int stepchan=-1, Bool writeSysCal = False, Bool asMultiSource = False, Bool combineSpw=False, Bool writeStation=False, Double sensitivity = 1.0); private: // Write the main table. static FitsOutput *writeMain(Int& refPixelFreq, Double& refFreq, Double& refFreq1, Double& chanbw, const String& outFITSFile, const MeasurementSet& rawms, const String& column, const Block& spwidMap, Int nrspw, Int startchan, Int nchan, Int stepchan, const Block& fieldidMap, Bool asMultiSource, Bool combineSpw); // Write the FQ table. // If combineSpw is True, all spectral-windows are written in one // row of the FITS table. static Bool writeFQ(FitsOutput *output, const MeasurementSet& ms, const Block& spwidMap, Int nrspw, Double refFreq, Int refPixelFreq, Double chanbw, Bool combineSpw); // Write the AN table. static Bool writeAN(FitsOutput *output, const MeasurementSet& ms, Double refFreq, Bool writeStation); // Write the SU table. static Bool writeSU(FitsOutput *output, const MeasurementSet& ms, const Block& fieldidMap, Int nrfield, const Block& spwidMap, Int nrspw); // Write the TY table. static Bool writeTY(FitsOutput *output, const MeasurementSet& ms, const Table& syscal, const Block& spwidMap, uInt nrif, Bool combineSpw); // Write the GC table. static Bool writeGC(FitsOutput *output, const MeasurementSet& ms, const Table& syscal, const Block& spwidMap, uInt nrif, Bool combineSpw, Double sensitivity, Int refPixelFreq, Double refFreq, Double chanbw); // Convert time to day and fraction. static void timeToDay(Int& day, Double& dayFraction, Double time); // Get the time and hourangle from the MS at the given row. // It uses the field-id and observation-id to calculate the hourangle. static void getStartHA (Double& startTime, Double& startHA, const MeasurementSet& ms, uInt rownr); // Handle the SYSCAL table. // It skips the entries not needed and sorts it in the correct order. static Table handleSysCal (const MeasurementSet& ms, const Vector& spwids, Bool isSubset); // Determine which ids are selected in the main table // (used for fields and spectral-window). // It fills a block for all possible ids, where -1 tells that the // id is not selected. Furthermore it fills a vector with the // selected id numbers. // The input is a vector containing all ids in the main table. // If isSubset is False the main table is not a selection, but // represents an entire MS. In that case the map and selids are // simply filled with values 0-nrid. static Int makeIdMap (Block& map, Vector& selids, const Vector& allids, Bool isSubset); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/msfits/MSFits/SDAntennaHandler.cc000066400000000000000000000430361476623553700214240ustar00rootroot00000000000000//# SDAntennaFiller.cc: an ANTENNA filler for SDFITS data //# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN SDAntennaHandler::SDAntennaHandler() : index_p(0), msAnt_p(0), msAntCols_p(0), rownr_p(-1), siteLongFldNum_p(-1), siteLatFldNum_p(-1), siteElevFldNum_p(-1) {;} SDAntennaHandler::SDAntennaHandler(MeasurementSet &ms, Vector &handledCols, const Record &row) : index_p(0), msAnt_p(0), msAntCols_p(0), rownr_p(-1), siteLongFldNum_p(-1), siteLatFldNum_p(-1), siteElevFldNum_p(-1) { initAll(ms, handledCols, row); } SDAntennaHandler::SDAntennaHandler(const SDAntennaHandler &other) : index_p(0), msAnt_p(0), msAntCols_p(0), rownr_p(-1), siteLongFldNum_p(-1), siteLatFldNum_p(-1), siteElevFldNum_p(-1) { *this = other; } SDAntennaHandler &SDAntennaHandler::operator=(const SDAntennaHandler &other) { if (this != &other) { clearAll(); index_p = new ColumnsIndex(*(other.index_p)); AlwaysAssert(index_p, AipsError); // need to avoid the assignment operator here because we want // this to point to the field in index_p, not in other.index_p nameKey_p.attachToRecord(index_p->accessKey(), MSAntenna::columnName(MSAntenna::NAME)); msAnt_p = new MSAntenna(*(other.msAnt_p)); AlwaysAssert(msAnt_p, AipsError); msAntCols_p = new MSAntennaColumns(*msAnt_p); AlwaysAssert(msAntCols_p, AipsError); rownr_p = other.rownr_p; // this should point to the same field as that in other telescopField_p = other.telescopField_p; siteLongFldNum_p = other.siteLongFldNum_p; siteLatFldNum_p = other.siteLatFldNum_p; siteElevFldNum_p = other.siteElevFldNum_p; mountField_p = other.mountField_p; msNameField_p = other.msNameField_p; stationField_p = other.stationField_p; orbitIdField_p = other.orbitIdField_p; phasedArrayIdField_p = other.phasedArrayIdField_p; dishDiameterField_p = other.dishDiameterField_p; offsetField_p = other.offsetField_p; positionField_p = other.positionField_p; flagRowField_p = other.flagRowField_p; } return *this; } void SDAntennaHandler::attach(MeasurementSet &ms, Vector &handledCols, const Record &row) { clearAll(); initAll(ms, handledCols, row); } void SDAntennaHandler::resetRow(const Record &row) { clearRow(); Vector dummyHandled; initRow(dummyHandled, row); } void SDAntennaHandler::fill(const Record &row) { // don't bother unless there is something there if (msAnt_p) { if (telescopField_p.isAttached()) { // fill the key with the telescope value *nameKey_p = *telescopField_p; // correct for a few quirks - mostly involving extra spaces or "_" if ((*nameKey_p).contains("NRAO")) { if ((*nameKey_p).contains("12M")) { *nameKey_p = "NRAO12M"; } else if ((*nameKey_p).contains("43M")) { // 140' position IS the GB position in the observatories list *nameKey_p = "GB"; } else if ((*nameKey_p).contains("GBT")) { // early versions had NRAO_GBT *nameKey_p = "GBT"; } } } else { // use ANTENNA_NAME only if TELESCOP field is not present if (msNameField_p.isAttached()) { *nameKey_p = *msNameField_p; } else { // just use an empty string as the key *nameKey_p = ""; } } if (mountField_p.isAttached()) { *mountKey_p = *mountField_p; } if (stationField_p.isAttached()) { *stationKey_p = *stationField_p; } if (dishDiameterField_p.isAttached()) { *dishDiameterKey_p = *dishDiameterField_p; } if (phasedIdKey_p.isAttached()) { *phasedIdKey_p = *phasedArrayIdField_p; } if (orbitIdKey_p.isAttached()) { *orbitIdKey_p = *orbitIdField_p; } if (flagRowKey_p.isAttached()) { *flagRowKey_p = *flagRowField_p; } Vector foundRows = index_p->getRowNumbers(); Bool found = False; MPosition pos; Vector offset(3,0.0); if (siteLongFldNum_p >= 0) { // construct an MPosition from these values Double siteLong = row.asDouble(siteLongFldNum_p); Double siteLat = row.asDouble(siteLatFldNum_p); Double siteElev = row.asDouble(siteElevFldNum_p); pos = MPosition(Quantity(siteLong,"m"), Quantity(siteLat,"deg"), Quantity(siteElev,"deg"), MPosition::WGS84); } else { // SITE* keywords take precendence over ARRAY_POSITION if (positionField_p.isAttached()) { // we write out this column as ITRF with all values in meters pos = MPosition(MVPosition(Quantum >(*positionField_p,"m")), MPosition::ITRF); } else { // if this returns False, pos will still be set at its unset value (0,0,0) MeasTable::Observatory(pos,*nameKey_p); } } // convert this to the coordinate system in the table pos = MPosition::Convert(pos, msAntCols_p->positionMeas().getMeasRef())(); if (offsetField_p.isAttached()) { offset = *offsetField_p; } if (foundRows.nelements() > 0) { // we have at least 1 candidate uInt whichOne = 0; // if there are no positions, stop and use the first one if (siteLongFldNum_p < 0) { found = True; } else { while (!found && whichOnepositionMeas()(foundRows(whichOne)).getValue() && allEQ(offset,msAntCols_p->offset()(foundRows(whichOne))); if (!found) whichOne++; } } if (found) { rownr_p = foundRows(whichOne); } } if (!found) { // we need to add one rownr_p = msAnt_p->nrow(); msAnt_p->addRow(); if (dishDiameterKey_p.isAttached()) { msAntCols_p->dishDiameter().put(rownr_p,*dishDiameterKey_p); } else { msAntCols_p->dishDiameter().put(rownr_p,0.0); } if (flagRowKey_p.isAttached()) { msAntCols_p->flagRow().put(rownr_p,*flagRowKey_p); } else { msAntCols_p->flagRow().put(rownr_p,False); } if (mountKey_p.isAttached()) { msAntCols_p->mount().put(rownr_p,*mountKey_p); } else { msAntCols_p->mount().put(rownr_p,""); } msAntCols_p->name().put(rownr_p, *nameKey_p); msAntCols_p->offset().put(uInt(rownr_p),offset); msAntCols_p->positionMeas().put(rownr_p,pos); if (stationKey_p.isAttached()) { msAntCols_p->station().put(rownr_p,*stationKey_p); } else { // for want of something better to put in here ... msAntCols_p->station().put(rownr_p,*nameKey_p); } if (orbitIdField_p.isAttached()) { if (*orbitIdField_p >= 0 && !orbitIdKey_p.isAttached()) { // apparently this is actually used, set the index, add the column addOrbitIdColumn(); } if (orbitIdKey_p.isAttached()) { // this means that the column is also available msAntCols_p->orbitId().put(rownr_p,*orbitIdField_p); } } if (phasedArrayIdField_p.isAttached()) { if (*phasedArrayIdField_p >= 0 && !phasedIdKey_p.isAttached()) { // apparently this is actually used, set the index, add the column addPhasedArrayIdColumn(); } if (phasedIdKey_p.isAttached()) { // this means that the column is also available msAntCols_p->phasedArrayId().put(rownr_p,*phasedArrayIdField_p); } } } name_p = *nameKey_p; position_p = msAntCols_p->positionMeas()(rownr_p); } } void SDAntennaHandler::clearAll() { delete index_p; index_p = 0; delete msAnt_p; msAnt_p = 0; delete msAntCols_p; msAntCols_p = 0; clearRow(); } void SDAntennaHandler::clearRow() { telescopField_p.detach(); mountField_p.detach(); msNameField_p.detach(); stationField_p.detach(); orbitIdField_p.detach(); phasedArrayIdField_p.detach(); dishDiameterField_p.detach(); offsetField_p.detach(); positionField_p.detach(); flagRowField_p.detach(); siteLongFldNum_p = siteLatFldNum_p = siteElevFldNum_p = -1; rownr_p = -1; } void SDAntennaHandler::initAll(MeasurementSet &ms, Vector &handledCols, const Record &row) { msAnt_p = new MSAntenna(ms.antenna()); AlwaysAssert(msAnt_p, AipsError); msAntCols_p = new MSAntennaColumns(*msAnt_p); AlwaysAssert(msAntCols_p, AipsError); initRow(handledCols, row); // index on NAME column, but when you get that index, it // might be a number of rows and so it will be necessary to check // the position at each row before a true match is found // Optionally index on other columns as necessary String indxStr = MSAntenna::columnName(MSAntenna::NAME); if (mountField_p.isAttached()) { indxStr += ","; indxStr += MSAntenna::columnName(MSAntenna::MOUNT); } if (stationField_p.isAttached()) { indxStr += ","; indxStr += MSAntenna::columnName(MSAntenna::STATION); } if (dishDiameterField_p.isAttached()) { indxStr += ","; indxStr += MSAntenna::columnName(MSAntenna::DISH_DIAMETER); } if (flagRowField_p.isAttached()) { indxStr += ","; indxStr += MSAntenna::columnName(MSAntenna::FLAG_ROW); } // ORBIT_ID and PHASED_ARRAY_ID are dealt with later, if necessary index_p = new ColumnsIndex(*msAnt_p, stringToVector(indxStr)); AlwaysAssert(index_p, AipsError); nameKey_p.attachToRecord(index_p->accessKey(), MSAntenna::columnName(MSAntenna::NAME)); if (stationField_p.isAttached()) { stationKey_p.attachToRecord(index_p->accessKey(), MSAntenna::columnName(MSAntenna::STATION)); } if (mountField_p.isAttached()) { mountKey_p.attachToRecord(index_p->accessKey(), MSAntenna::columnName(MSAntenna::MOUNT)); } if (dishDiameterField_p.isAttached()) { dishDiameterKey_p.attachToRecord(index_p->accessKey(), MSAntenna::columnName(MSAntenna::DISH_DIAMETER)); } if (flagRowField_p.isAttached()) { flagRowKey_p.attachToRecord(index_p->accessKey(), MSAntenna::columnName(MSAntenna::FLAG_ROW)); } // orbit_id and phased_array_id columns are dealt with elsewhere } void SDAntennaHandler::initRow(Vector &handledCols, const Record &row) { AlwaysAssert(handledCols.nelements()==row.description().nfields(), AipsError); if (row.fieldNumber("TELESCOP") >= 0) { telescopField_p.attachToRecord(row, "TELESCOP"); handledCols(row.fieldNumber("TELESCOP")) = True; } siteLongFldNum_p = row.fieldNumber("SITELONG"); siteLatFldNum_p = row.fieldNumber("SITELAT"); siteElevFldNum_p = row.fieldNumber("SITEELEV"); // its all or nothing with these if (siteLongFldNum_p >= 0 && siteLatFldNum_p >= 0 && siteElevFldNum_p >= 0) { handledCols(siteLongFldNum_p) = True; handledCols(siteLatFldNum_p) = True; handledCols(siteElevFldNum_p) = True; } else { siteLongFldNum_p = siteLatFldNum_p = siteElevFldNum_p = -1; } if (row.fieldNumber("ANTENNA_MOUNT") >= 0) { mountField_p.attachToRecord(row, "ANTENNA_MOUNT"); handledCols(row.fieldNumber("ANTENNA_MOUNT")) = True; } if (row.fieldNumber("ANTENNA_NAME") >= 0) { msNameField_p.attachToRecord(row, "ANTENNA_NAME"); handledCols(row.fieldNumber("ANTENNA_NAME")) = True; } if (row.fieldNumber("ANTENNA_STATION") >= 0) { stationField_p.attachToRecord(row, "ANTENNA_STATION"); handledCols(row.fieldNumber("ANTENNA_STATION")) = True; } if (row.fieldNumber("ANTENNA_DISH_DIAMETER") >= 0 && row.dataType("ANTENNA_DISH_DIAMETER") == TpDouble) { dishDiameterField_p.attachToRecord(row, "ANTENNA_DISH_DIAMETER"); handledCols(row.fieldNumber("ANTENNA_DISH_DIAMETER")) = True; } if (row.fieldNumber("ANTENNA_OFFSET") >= 0 && row.dataType("ANTENNA_OFFSET") == TpArrayDouble) { offsetField_p.attachToRecord(row, "ANTENNA_OFFSET"); handledCols(row.fieldNumber("ANTENNA_OFFSET")) = True; } if (row.fieldNumber("ANTENNA_ORBIT_ID") >= 0 && row.dataType("ANTENNA_ORBIT_ID") == TpInt) { orbitIdField_p.attachToRecord(row, "ANTENNA_ORBIT_ID"); handledCols(row.fieldNumber("ANTENNA_ORBIT_ID")) = True; } if (row.fieldNumber("ANTENNA_PHASED_ARRAY_ID") >= 0 && row.dataType("ANTENNA_PHASED_ARRAY_ID") == TpInt) { phasedArrayIdField_p.attachToRecord(row, "ANTENNA_PHASED_ARRAY_ID"); handledCols(row.fieldNumber("ANTENNA_PHASED_ARRAY_ID")) = True; } if (row.fieldNumber("ANTENNA_POSITION") >= 0 && row.dataType("ANTENNA_POSITION") == TpArrayDouble) { positionField_p.attachToRecord(row, "ANTENNA_POSITION"); handledCols(row.fieldNumber("ANTENNA_POSITION")) = True; } if (row.fieldNumber("ANTENNA_FLAG_ROW") >= 0 && row.dataType("ANTENNA_FLAG_ROW") == TpBool) { flagRowField_p.attachToRecord(row, "ANTENNA_FLAG_ROW"); handledCols(row.fieldNumber("ANTENNA_FLAG_ROW")) = True; } // row number isn't set until the following fill rownr_p = -1; } void SDAntennaHandler::addPhasedArrayIdColumn() { // if the index field is already attached, do nothing if (!phasedIdKey_p.isAttached() && index_p) { Vector indexNames = index_p->columnNames(); delete index_p; index_p = 0; delete msAntCols_p; msAntCols_p = 0; // we need to add a new column to the ANTENNA table TableDesc td; MSAntenna::addColumnToDesc(td,MSAntenna::PHASED_ARRAY_ID); msAnt_p->addColumn(td[0]); // remake the columns object msAntCols_p = new MSAntennaColumns(*msAnt_p); AlwaysAssert(msAntCols_p, AipsError); // and the index indexNames.resize(indexNames.nelements()+1, True); indexNames(indexNames.nelements()-1) = MSAntenna::columnName(MSAntenna::PHASED_ARRAY_ID); index_p = new ColumnsIndex(*msAnt_p, indexNames); AlwaysAssert(index_p, AipsError); nameKey_p.attachToRecord(index_p->accessKey(), MSAntenna::columnName(MSAntenna::NAME)); if (stationField_p.isAttached()) { stationKey_p.attachToRecord(index_p->accessKey(), MSAntenna::columnName(MSAntenna::STATION)); } if (mountField_p.isAttached()) { mountKey_p.attachToRecord(index_p->accessKey(), MSAntenna::columnName(MSAntenna::MOUNT)); } if (dishDiameterField_p.isAttached()) { dishDiameterKey_p.attachToRecord(index_p->accessKey(), MSAntenna::columnName(MSAntenna::DISH_DIAMETER)); } phasedIdKey_p.attachToRecord(index_p->accessKey(), MSAntenna::columnName(MSAntenna::PHASED_ARRAY_ID)); if (anyEQ(indexNames, MSAntenna::columnName(MSAntenna::ORBIT_ID))) { orbitIdKey_p.attachToRecord(index_p->accessKey(), MSAntenna::columnName(MSAntenna::ORBIT_ID)); } } } void SDAntennaHandler::addOrbitIdColumn() { // if the index field is already attached, do nothing if (!orbitIdKey_p.isAttached() && index_p) { Vector indexNames = index_p->columnNames(); delete index_p; index_p = 0; delete msAntCols_p; msAntCols_p = 0; // we need to add a new column to the ANTENNA table TableDesc td; MSAntenna::addColumnToDesc(td,MSAntenna::ORBIT_ID); msAnt_p->addColumn(td[0]); // remake the columns object msAntCols_p = new MSAntennaColumns(*msAnt_p); AlwaysAssert(msAntCols_p, AipsError); // and the index indexNames.resize(indexNames.nelements()+1, True); indexNames(indexNames.nelements()-1) = MSAntenna::columnName(MSAntenna::ORBIT_ID); index_p = new ColumnsIndex(*msAnt_p, indexNames); AlwaysAssert(index_p, AipsError); nameKey_p.attachToRecord(index_p->accessKey(), MSAntenna::columnName(MSAntenna::NAME)); if (stationField_p.isAttached()) { stationKey_p.attachToRecord(index_p->accessKey(), MSAntenna::columnName(MSAntenna::STATION)); } if (mountField_p.isAttached()) { mountKey_p.attachToRecord(index_p->accessKey(), MSAntenna::columnName(MSAntenna::MOUNT)); } if (dishDiameterField_p.isAttached()) { dishDiameterKey_p.attachToRecord(index_p->accessKey(), MSAntenna::columnName(MSAntenna::DISH_DIAMETER)); } orbitIdKey_p.attachToRecord(index_p->accessKey(), MSAntenna::columnName(MSAntenna::ORBIT_ID)); if (anyEQ(indexNames, MSAntenna::columnName(MSAntenna::PHASED_ARRAY_ID))) { phasedIdKey_p.attachToRecord(index_p->accessKey(), MSAntenna::columnName(MSAntenna::PHASED_ARRAY_ID)); } } } } //# NAMESPACE CASACORE - END casacore-3.7.1/msfits/MSFits/SDAntennaHandler.h000066400000000000000000000117241476623553700212650ustar00rootroot00000000000000//# SDAntennaFiller.h: fills the ANTENNA table for the SDFITS filler //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_SDANTENNAHANDLER_H #define MS_SDANTENNAHANDLER_H #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class ColumnsIndex; class MeasurementSet; class MSAntenna; class MSAntennaColumns; class Record; // // // or // // // //
      • SomeClass //
      • SomeOtherClass //
      • some concept // // // // // // // // // // // // // // // //
      • //
      • // // // //
      • //
      • // // // //
      • add this feature //
      • fix this bug //
      • start discussion of this possible extension // class SDAntennaHandler { public: // default ctor is not attached to a MS and hence is useless until attached SDAntennaHandler(); // attach this to a MS, mark the appropriate columns as handled given // the indicated row SDAntennaHandler(MeasurementSet &ms, Vector &handledCols, const Record &row); // copy ctor SDAntennaHandler(const SDAntennaHandler &other); ~SDAntennaHandler() {clearAll();} // assignment operator, uses copy semantics SDAntennaHandler &operator=(const SDAntennaHandler &other); // attach to a MS, mark the appropriate columns as handled given the void attach(MeasurementSet &ms, Vector &handledCols, const Record &row); // reset internals given indicated row, use the same MS void resetRow(const Record &row); // fill - a new row is added only when necessary void fill(const Record &row); // get the current antenna ID Int antennaId() {return rownr_p;} // get the telescope name String telescopeName() {return name_p;} // get the telescope position const MPosition &telescopePosition() {return position_p;} private: ColumnsIndex *index_p; RecordFieldPtr nameKey_p, stationKey_p, mountKey_p; RecordFieldPtr dishDiameterKey_p; RecordFieldPtr orbitIdKey_p, phasedIdKey_p; RecordFieldPtr flagRowKey_p; MSAntenna *msAnt_p; MSAntennaColumns *msAntCols_p; Int rownr_p; // pointers to fields in record, only used if attached RORecordFieldPtr telescopField_p; // telescope position might be a double or float, // just remember its location Int siteLongFldNum_p, siteLatFldNum_p, siteElevFldNum_p; String name_p; MPosition position_p; // fields which might exist if this SDFITS was converted from an MS using ms2sdfits RORecordFieldPtr mountField_p, msNameField_p, stationField_p; RORecordFieldPtr orbitIdField_p, phasedArrayIdField_p; RORecordFieldPtr dishDiameterField_p; RORecordFieldPtr > offsetField_p, positionField_p; RORecordFieldPtr flagRowField_p; // I expect these will never be used, nevertheless, put them here just in case I'm wrong void addPhasedArrayIdColumn(); void addOrbitIdColumn(); // cleanup everything void clearAll(); // cleanup things which depend on the row description being fixed void clearRow(); // initialize everything void initAll(MeasurementSet &ms, Vector &handledCols, const Record &row); // initialize the things which depend on the row void initRow(Vector &handledCols, const Record &row); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/msfits/MSFits/SDDataDescHandler.cc000066400000000000000000000115461476623553700215110ustar00rootroot00000000000000//# SDDataDescHandler.cc: a DATA_DESCRIPTION handler for SDFITS data //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN SDDataDescHandler::SDDataDescHandler() : index_p(0), msDataDesc_p(0), msDataDescCols_p(0), rownr_p(-1) {;} SDDataDescHandler::SDDataDescHandler(MeasurementSet &ms) : index_p(0), msDataDesc_p(0), msDataDescCols_p(0), rownr_p(-1) { initAll(ms); } SDDataDescHandler::SDDataDescHandler(const SDDataDescHandler &other) : index_p(0), msDataDesc_p(0), msDataDescCols_p(0), rownr_p(-1) { *this = other; } SDDataDescHandler &SDDataDescHandler::operator=(const SDDataDescHandler &other) { if (this != &other) { clearAll(); index_p = new ColumnsIndex(*(other.index_p)); AlwaysAssert(index_p, AipsError); // need to avoid the assignment operator here because we want // this to point to the field in index_p, not in other.index_p spwinIdKey_p.attachToRecord(index_p->accessKey(), MSDataDescription::columnName(MSDataDescription::SPECTRAL_WINDOW_ID)); polIdKey_p.attachToRecord(index_p->accessKey(), MSDataDescription::columnName(MSDataDescription::POLARIZATION_ID)); msDataDesc_p = new MSDataDescription(*(other.msDataDesc_p)); AlwaysAssert(msDataDesc_p, AipsError); msDataDescCols_p = new MSDataDescColumns(*msDataDesc_p); AlwaysAssert(msDataDescCols_p, AipsError); rownr_p = other.rownr_p; } return *this; } void SDDataDescHandler::attach(MeasurementSet &ms, Vector &, const Record &) { clearAll(); initAll(ms); } void SDDataDescHandler::fill(const Record &, Int spwinId, Int polId) { // don't bother unless there is something there if (msDataDesc_p) { *spwinIdKey_p = spwinId; *polIdKey_p = polId; Bool found = False; uInt foundRow = index_p->getRowNumber(found); if (found) { // we have a winner rownr_p = foundRow; } else { // we need to add one rownr_p = msDataDesc_p->nrow(); msDataDesc_p->addRow(); msDataDescCols_p->spectralWindowId().put(rownr_p, *spwinIdKey_p); msDataDescCols_p->polarizationId().put(rownr_p, *polIdKey_p); msDataDescCols_p->flagRow().put(rownr_p, False); } } } void SDDataDescHandler::clearAll() { delete index_p; index_p = 0; delete msDataDesc_p; msDataDesc_p = 0; delete msDataDescCols_p; msDataDescCols_p = 0; } void SDDataDescHandler::initAll(MeasurementSet &ms) { msDataDesc_p = new MSDataDescription(ms.dataDescription()); AlwaysAssert(msDataDesc_p, AipsError); msDataDescCols_p = new MSDataDescColumns(*msDataDesc_p); AlwaysAssert(msDataDescCols_p, AipsError); Vector indexCols(2); indexCols(0) = MSDataDescription::columnName(MSDataDescription::SPECTRAL_WINDOW_ID); indexCols(1) = MSDataDescription::columnName(MSDataDescription::POLARIZATION_ID); index_p = new ColumnsIndex(*msDataDesc_p, indexCols); AlwaysAssert(index_p, AipsError); spwinIdKey_p.attachToRecord(index_p->accessKey(), MSDataDescription::columnName(MSDataDescription::SPECTRAL_WINDOW_ID)); polIdKey_p.attachToRecord(index_p->accessKey(), MSDataDescription::columnName(MSDataDescription::POLARIZATION_ID)); rownr_p = -1; // nothing depends directly on the row, no columns are handled here } } //# NAMESPACE CASACORE - END casacore-3.7.1/msfits/MSFits/SDDataDescHandler.h000066400000000000000000000070111476623553700213430ustar00rootroot00000000000000//# SDDataDescFiller.h: fills the DATA_DESCRIPTION table for the SDFITS filler //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_SDDATADESCHANDLER_H #define MS_SDDATADESCHANDLER_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class ColumnsIndex; class MeasurementSet; class MSDataDescription; class MSDataDescColumns; class Record; // // // or // // // //
      • SomeClass //
      • SomeOtherClass //
      • some concept // // // // // // // // // // // // // // // //
      • //
      • // // // //
      • //
      • // // // //
      • add this feature //
      • fix this bug //
      • start discussion of this possible extension // class SDDataDescHandler { public: // default ctor is not attached to a MS and hence is useless until attached SDDataDescHandler(); // attach this to a MS - no columns are explicitly handled here SDDataDescHandler(MeasurementSet &ms); // copy ctor SDDataDescHandler(const SDDataDescHandler &other); ~SDDataDescHandler() {clearAll();} // assignment operator, uses copy semantics SDDataDescHandler &operator=(const SDDataDescHandler &other); // attach to a MS, the handledCols and row arguments are ignored here void attach(MeasurementSet &ms, Vector &handledCols, const Record &row); // reset internals given indicated row, use the same MS; just resets the id pointer void resetRow(const Record &) {rownr_p=-1;} // fill - a new row is added only when necessary void fill(const Record &row, Int spwinId, Int polId); // get the current dataDesc ID Int dataDescId() {return rownr_p;} private: RecordFieldPtr spwinIdKey_p, polIdKey_p; ColumnsIndex *index_p; MSDataDescription *msDataDesc_p; MSDataDescColumns *msDataDescCols_p; Int rownr_p; // cleanup everything void clearAll(); // initialize everything void initAll(MeasurementSet &ms); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/msfits/MSFits/SDFITSHandler.cc000066400000000000000000000215771476623553700206130ustar00rootroot00000000000000//# SDFITSHandler.cc: fills all otherwise unhandled columns for the SDFITS filler //# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN SDFITSHandler::SDFITSHandler() : tab_p(0), copier_p(0) {;} SDFITSHandler::SDFITSHandler(MeasurementSet &ms, Vector &handledCols, const Record &row) : tab_p(0), copier_p(0) { initAll(ms, handledCols, row); } SDFITSHandler::SDFITSHandler(const SDFITSHandler &other) : tab_p(0), copier_p(0) { *this = other; } SDFITSHandler &SDFITSHandler::operator=(const SDFITSHandler &other) { if (this != &other) { clearAll(); tab_p = new Table(*(other.tab_p)); AlwaysAssert(tab_p, AipsError); timeMeas_p.attach(*tab_p, "TIME"); intervalQuant_p.attach(*tab_p, "INTERVAL"); copier_p = new CopyRecordToTable(*other.copier_p); } return *this; } void SDFITSHandler::attach(MeasurementSet &ms, Vector &handledCols, const Record &row) { clearAll(); initAll(ms, handledCols, row); } void SDFITSHandler::fill(const Record &, const MEpoch &time, const Double &interval) { // don't bother unless there is something there if (tab_p) { // fill it Int rownr = tab_p->nrow(); tab_p->addRow(); timeMeas_p.put(rownr, time); intervalQuant_p.put(rownr, Quantity(interval,"s")); copier_p->copy(rownr); } } void SDFITSHandler::clearAll() { delete tab_p; tab_p = 0; clearRow(); } void SDFITSHandler::clearRow() { delete copier_p; copier_p = 0; } void SDFITSHandler::initAll(MeasurementSet &ms, Vector &handledCols, const Record &row) { // static_cast is a workaround for an SGI compiler bug! wky 2000/11/02 // don't bother unless there are some unhandled columns if (anyEQ(static_cast >(handledCols), False)) { Vector colNames; TableDesc td = requiredTableDesc(handledCols, colNames, row); // is there already an SDFITS table, or is one needed if (ms.keywordSet().fieldNumber("NS_SDFITS") >= 0 && ms.keywordSet().dataType("NS_SDFITS") == TpTable) { tab_p = new Table(ms.keywordSet().asTable("NS_SDFITS")); AlwaysAssert(tab_p, AipsError); // only add columns not already in tab_p Vector colNames(td.columnNames()); for (uInt i=0;itableDesc().isColumn(colNames(i))) { td.addColumn(td[i]); } } } else { // create a new table and attach it to the MAIN ms table SetupNewTable newtab(ms.tableName() + "/NS_SDFITS", td, Table::New); StandardStMan stman; newtab.bindAll(stman); tab_p = new Table(newtab); AlwaysAssert(tab_p, AipsError); ms.rwKeywordSet().defineTable("NS_SDFITS", *tab_p); } // attach the TIME and INTERVAL measure columns timeMeas_p.attach(*tab_p, "TIME"); intervalQuant_p.attach(*tab_p, "INTERVAL"); initRow(handledCols, colNames, row); } } void SDFITSHandler::initRow(Vector &handledCols, const Vector &colNames, const Record &row) { // need to get the mapping between row fields and tab columns // there are always the same number of elements in handledCols // as there are fields in row Vector fieldMap(handledCols.nelements(),-1); for (uInt i=0;i= 0) { fieldMap(field) = i; handledCols(field) = True; } } copier_p = new CopyRecordToTable(*tab_p, row, fieldMap); AlwaysAssert(copier_p, AipsError); } TableDesc SDFITSHandler::requiredTableDesc(Vector &handledCols, Vector &colNames, const Record &row) { // build a TableDesc using row and any un-handled columns TableDesc td; Regex sdfPrefix("^NS_SDFITS_"); Regex sdfPrefixMatch("^NS_SDFITS_.*"); colNames.resize(handledCols.nelements()); colNames = ""; uInt colCount = 0; for (uInt i=0;i(colName)); break; case TpUChar: td.addColumn(ScalarColumnDesc(colName)); break; case TpShort: td.addColumn(ScalarColumnDesc(colName)); break; case TpInt: td.addColumn(ScalarColumnDesc(colName)); break; case TpFloat: td.addColumn(ScalarColumnDesc(colName)); break; case TpDouble: td.addColumn(ScalarColumnDesc(colName)); break; case TpComplex: td.addColumn(ScalarColumnDesc(colName)); break; case TpDComplex: td.addColumn(ScalarColumnDesc(colName)); break; case TpString: td.addColumn(ScalarColumnDesc(colName)); break; case TpArrayBool: td.addColumn(ArrayColumnDesc(colName)); break; case TpArrayUChar: td.addColumn(ArrayColumnDesc(colName)); break; case TpArrayShort: td.addColumn(ArrayColumnDesc(colName)); break; case TpArrayInt: td.addColumn(ArrayColumnDesc(colName)); break; case TpArrayFloat: td.addColumn(ArrayColumnDesc(colName)); break; case TpArrayDouble: td.addColumn(ArrayColumnDesc(colName)); break; case TpArrayComplex: td.addColumn(ArrayColumnDesc(colName)); break; case TpArrayDComplex: td.addColumn(ArrayColumnDesc(colName)); break; case TpArrayString: td.addColumn(ArrayColumnDesc(colName)); break; default: LogIO os; os << LogIO::SEVERE << WHERE << "Field " << colName << " has an invalid type, " << Int(row.type(i)) << " -- this should never happen." << LogIO::EXCEPTION; break; } } } } colNames.resize(colCount, True); // add the TIME and INTERVAL columns // TIME is an MEpoch column td.addColumn(ScalarColumnDesc("TIME")); TableMeasDesc measCol(TableMeasValueDesc(td,"TIME"), TableMeasRefDesc(MEpoch::DEFAULT)); measCol.write(td); // and change the units to "s" from the default of "d" TableQuantumDesc timeqd(td,"TIME",Unit("s")); timeqd.write(td); // INTERVAL is a Quantity column td.addColumn(ScalarColumnDesc("INTERVAL")); TableQuantumDesc quantCol(td, "INTERVAL", Unit("s")); quantCol.write(td); return td; } } //# NAMESPACE CASACORE - END casacore-3.7.1/msfits/MSFits/SDFITSHandler.h000066400000000000000000000077731476623553700204570ustar00rootroot00000000000000//# SDFITSFiller.h: fills all otherwise unhandled columns for the SDFITS filler //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_SDFITSHANDLER_H #define MS_SDFITSHANDLER_H #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class CopyRecordToTable; class MeasurementSet; class Record; class Table; // // // or // // // //
      • SomeClass //
      • SomeOtherClass //
      • some concept // // // // // // // // // // // // // // // //
      • //
      • // // // //
      • //
      • // // // //
      • add this feature //
      • fix this bug //
      • start discussion of this possible extension // class SDFITSHandler { public: // default ctor is not attached to a MS and hence is useless until attached SDFITSHandler(); // attach this to a MS - any unhandled fields in row are handled here. // This handler must be attached last. SDFITSHandler(MeasurementSet &ms, Vector &handledCols, const Record &row); // copy ctor SDFITSHandler(const SDFITSHandler &other); ~SDFITSHandler() {clearAll();} // assignment operator, uses copy semantics SDFITSHandler &operator=(const SDFITSHandler &other); // attach to a MS - any unhandled fields in row are handled here. // This handler must be attached last. void attach(MeasurementSet &ms, Vector &handledCols, const Record &row); // fill - a new row is always added void fill(const Record &row, const MEpoch &time, const Double &interval); private: // the output table Table *tab_p; // the TIME column ScalarMeasColumn timeMeas_p; // the INTERVAL column ScalarQuantColumn intervalQuant_p; // this copies everything from the row to the table CopyRecordToTable *copier_p; // cleanup everything void clearAll(); // cleanup the row related stuff void clearRow(); // initialize everything void initAll(MeasurementSet &ms, Vector &handledCols, const Record &row); // intialize the row related stuff void initRow(Vector &handledCols, const Vector &colNames, const Record &row); // get the required table desc given the unhandled columns and the row TableDesc requiredTableDesc(Vector &handledCols, Vector &colNames, const Record &row); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/msfits/MSFits/SDFeedHandler.cc000066400000000000000000000412051476623553700206770ustar00rootroot00000000000000//# SDFeedHandler.cc: an FEED handler for SDFITS data //# Copyright (C) 2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN SDFeedHandler::SDFeedHandler() : index_p(0), msFeed_p(0), msFeedCols_p(0), feedId_p(-1), nextFeedId_p(0), nrecpt_p(0) {;} SDFeedHandler::SDFeedHandler(MeasurementSet &ms, Vector &handledCols, const Record &row) : index_p(0), msFeed_p(0), msFeedCols_p(0), feedId_p(-1), nextFeedId_p(0), nrecpt_p(0) { initAll(ms, handledCols, row); } SDFeedHandler::SDFeedHandler(const SDFeedHandler &other) : index_p(0), msFeed_p(0), msFeedCols_p(0), feedId_p(-1), nextFeedId_p(0), nrecpt_p(0) { *this = other; } SDFeedHandler &SDFeedHandler::operator=(const SDFeedHandler &other) { if (this != &other) { clearAll(); index_p = new ColumnsIndex(*(other.index_p)); AlwaysAssert(index_p, AipsError); // need to avoid the assignment operator here because we want // this to point to the field in index_p, not in other.index_p numRecpKey_p.attachToRecord(index_p->accessKey(), MSFeed::columnName(MSFeed::NUM_RECEPTORS)); msFeed_p = new MSFeed(*(other.msFeed_p)); AlwaysAssert(msFeed_p, AipsError); msFeedCols_p = new MSFeedColumns(*msFeed_p); AlwaysAssert(msFeedCols_p, AipsError); feedId_p = other.feedId_p; nextFeedId_p = other.nextFeedId_p; nrecpt_p = other.nrecpt_p; feed1Field_p = other.feed1Field_p; feed2Field_p = other.feed2Field_p; beamIdField_p = other.beamIdField_p; phasedFeedIdField_p = other.phasedFeedIdField_p; numReceptorsField_p = other.numReceptorsField_p; intervalField_p = other.intervalField_p; timeField_p = other.timeField_p; beamOffsetField_p = other.beamOffsetField_p; positionField_p = other.positionField_p; receptorAngleField_p = other.receptorAngleField_p; scaReceptorAngleField_p = other.scaReceptorAngleField_p; polResponseField_p = other.polResponseField_p; polarizationTypeField_p = other.polarizationTypeField_p; } return *this; } void SDFeedHandler::attach(MeasurementSet &ms, Vector &handledCols, const Record &row) { clearAll(); initAll(ms, handledCols, row); } void SDFeedHandler::resetRow(const Record &row) { clearRow(); Vector dummyCols(row.nfields()); initRow(dummyCols, row); } void SDFeedHandler::fill(const Record &, Int antennaId, Int spwinId, const Vector &stokes) { // don't bother unless there is something there if (msFeed_p) { Vector polType; stokesToPolType(stokes, polType); *numRecpKey_p = polType.nelements(); nrecpt_p = *numRecpKey_p; Bool found = False; feedId_p = -1; Vector foundRows = index_p->getRowNumbers(); uInt whichOne = 0; // this is True if the row has probably come from a MS AND FEED1 == FEED2 // When true, fill will try and reuse the same feed number if possible Bool doMSCheck = feed1Field_p.isAttached() && feed2Field_p.isAttached() && *feed1Field_p == *feed2Field_p; // also, ignore the MS row columns if NUM_RECEPTORS there doesn't match numRecpKey_p doMSCheck = doMSCheck && numReceptorsField_p.isAttached() && *numReceptorsField_p == *numRecpKey_p; // also, ignore the MS row columns if POLARIZATION_TYPE doesn't match polType if (doMSCheck && polarizationTypeField_p.isAttached()) { // turn this into an array istringstream istr(String((*polarizationTypeField_p).chars(), (*polarizationTypeField_p).length())); Array polTypeArr; // decode it - [#,#,#,#...] - individual brackets separated by commas istr >> polTypeArr; // if polTypeArr is empty, interpret that as sufficient unknown values so // that polTypeArr has same shape as polType if (polTypeArr.nelements() == 0) { polTypeArr.resize(polType.shape()); polTypeArr = Stokes::name(Stokes::Undefined); } doMSCheck = allEQ(polType, polTypeArr); } while (!found && whichOnepolarizationType()(thisRow))) { // we can reuse this feed id, at least feedId_p = msFeedCols_p->feedId()(thisRow); // the antennaId and spwinId need to match to reuse this row if (msFeedCols_p->antennaId()(thisRow) == antennaId && msFeedCols_p->spectralWindowId()(thisRow) == spwinId) { // we have a winner found = True; if (doMSCheck) { // double check to see if this row matches the one in the table if (found && beamIdField_p.isAttached()) { found = *beamIdField_p == msFeedCols_p->beamId()(thisRow); } if (found && phasedFeedIdField_p.isAttached() && !msFeedCols_p->phasedFeedId().isNull()) { found = *phasedFeedIdField_p == msFeedCols_p->phasedFeedId()(thisRow); } if (found && intervalField_p.isAttached()) { found = *intervalField_p == msFeedCols_p->interval()(thisRow); } if (found && timeField_p.isAttached()) { found = *timeField_p == msFeedCols_p->time()(thisRow); } if (found && beamOffsetField_p.isAttached()) { found = allEQ(*beamOffsetField_p,msFeedCols_p->beamOffset()(thisRow)); } if (found && positionField_p.isAttached()) { found = allEQ(*positionField_p,msFeedCols_p->position()(thisRow)); } if (found && receptorAngleField_p.isAttached()) { found = allEQ(*receptorAngleField_p,msFeedCols_p->receptorAngle()(thisRow)); } if (found && scaReceptorAngleField_p.isAttached()) { found = allEQ(msFeedCols_p->receptorAngle()(thisRow),*scaReceptorAngleField_p); } if (found && polResponseField_p.isAttached()) { found = allEQ(*polResponseField_p,msFeedCols_p->polResponse()(thisRow)); } } } } if (!found) whichOne++; } // if it was found, nothing else to do if (!found) { // we need to add one Int newRow = msFeed_p->nrow(); if (doMSCheck) { // we're reusing what is in the row from a previous MS // the feed number // but only if the feed is still < 0 -> no match yet so we can use this one if (feedId_p < 0) feedId_p = *feed1Field_p; // make sure we can't ever automatically reuse this one by accident if (feedId_p >= nextFeedId_p) nextFeedId_p = feedId_p + 1; } else { // new feed id needed? if (feedId_p < 0) feedId_p = nextFeedId_p++; } msFeed_p->addRow(); msFeedCols_p->antennaId().put(newRow, antennaId); msFeedCols_p->feedId().put(newRow, feedId_p); msFeedCols_p->spectralWindowId().put(newRow, spwinId); if (timeField_p.isAttached()) { msFeedCols_p->time().put(newRow, *timeField_p); } else { msFeedCols_p->time().put(newRow, 0.0); } if (intervalField_p.isAttached()) { msFeedCols_p->interval().put(newRow, *intervalField_p); } else { msFeedCols_p->interval().put(newRow, 0.0); } msFeedCols_p->numReceptors().put(newRow, *numRecpKey_p); if (beamIdField_p.isAttached()) { msFeedCols_p->beamId().put(newRow, *beamIdField_p); } else { msFeedCols_p->beamId().put(newRow, -1); } if (beamOffsetField_p.isAttached()) { msFeedCols_p->beamOffset().put(newRow, *beamOffsetField_p); } else { msFeedCols_p->beamOffset().put(newRow, Matrix(2,*numRecpKey_p,0.0)); } msFeedCols_p->polarizationType().put(newRow, polType); if (polResponseField_p.isAttached()) { msFeedCols_p->polResponse().put(newRow, *polResponseField_p); } else { Matrix polResponse(*numRecpKey_p, *numRecpKey_p, 0.0); // assume no cross talk polResponse.diagonal() = 1.0; msFeedCols_p->polResponse().put(newRow, polResponse); } if (positionField_p.isAttached()) { msFeedCols_p->position().put(newRow, *positionField_p); } else { msFeedCols_p->position().put(newRow, Vector(3,0.0)); } if (receptorAngleField_p.isAttached()) { msFeedCols_p->receptorAngle().put(newRow, *receptorAngleField_p); } else if (scaReceptorAngleField_p.isAttached()) { msFeedCols_p->receptorAngle().put(newRow, Vector(*numRecpKey_p, *scaReceptorAngleField_p)); } else { msFeedCols_p->receptorAngle().put(newRow, Vector(*numRecpKey_p, 0.0)); } if (phasedFeedIdField_p.isAttached()) { if (msFeedCols_p->phasedFeedId().isNull() && *phasedFeedIdField_p >= 0) { // add this optional column when necessary delete msFeedCols_p; msFeedCols_p = 0; TableDesc td; MSFeed::addColumnToDesc(td, MSFeed::PHASED_FEED_ID); msFeed_p->addColumn(td[0]); msFeedCols_p = new MSFeedColumns(*msFeed_p); AlwaysAssert(msFeedCols_p, AipsError); } if (!msFeedCols_p->phasedFeedId().isNull()) { msFeedCols_p->phasedFeedId().put(newRow, *phasedFeedIdField_p); } } } } } void SDFeedHandler::clearAll() { delete index_p; index_p = 0; delete msFeed_p; msFeed_p = 0; delete msFeedCols_p; msFeedCols_p = 0; feedId_p = -1; nextFeedId_p = 0; nrecpt_p = 0; clearRow(); } void SDFeedHandler::clearRow() { feedId_p = -1; feed1Field_p.detach(); feed2Field_p.detach(); beamIdField_p.detach(); phasedFeedIdField_p.detach(); numReceptorsField_p.detach(); intervalField_p.detach(); timeField_p.detach(); beamOffsetField_p.detach(); positionField_p.detach(); receptorAngleField_p.detach(); scaReceptorAngleField_p.detach(); polResponseField_p.detach(); polarizationTypeField_p.detach(); } void SDFeedHandler::initAll(MeasurementSet &ms, Vector &handledCols, const Record &row) { msFeed_p = new MSFeed(ms.feed()); AlwaysAssert(msFeed_p, AipsError); msFeedCols_p = new MSFeedColumns(*msFeed_p); AlwaysAssert(msFeedCols_p, AipsError); index_p = new ColumnsIndex(*msFeed_p, MSFeed::columnName(MSFeed::NUM_RECEPTORS)); AlwaysAssert(index_p, AipsError); numRecpKey_p.attachToRecord(index_p->accessKey(), MSFeed::columnName(MSFeed::NUM_RECEPTORS)); feedId_p = -1; nextFeedId_p = 0; nrecpt_p = 0; initRow(handledCols, row); } void SDFeedHandler::initRow(Vector &handledCols, const Record &row) { AlwaysAssert(handledCols.nelements()==row.description().nfields(), AipsError); if (row.fieldNumber("MAIN_FEED1") >= 0 && row.dataType("MAIN_FEED1") == TpInt) { feed1Field_p.attachToRecord(row, "MAIN_FEED1"); handledCols(row.fieldNumber("MAIN_FEED1")) = True; } if (row.fieldNumber("MAIN_FEED2") >= 0 && row.dataType("MAIN_FEED2") == TpInt) { feed2Field_p.attachToRecord(row, "MAIN_FEED2"); handledCols(row.fieldNumber("MAIN_FEED2")) = True; } if (row.fieldNumber("FEED_BEAM_ID") >= 0 && row.dataType("FEED_BEAM_ID") == TpInt) { beamIdField_p.attachToRecord(row, "FEED_BEAM_ID"); handledCols(row.fieldNumber("FEED_BEAM_ID")) = True; } if (row.fieldNumber("FEED_PHASED_FEED_ID") >= 0 && row.dataType("FEED_PHASED_FEED_ID") == TpInt) { phasedFeedIdField_p.attachToRecord(row, "FEED_PHASED_FEED_ID"); handledCols(row.fieldNumber("FEED_PHASED_FEED_ID")) = True; } if (row.fieldNumber("FEED_NUM_RECEPTORS") >= 0 && row.dataType("FEED_NUM_RECEPTORS") == TpInt) { numReceptorsField_p.attachToRecord(row, "FEED_NUM_RECEPTORS"); handledCols(row.fieldNumber("FEED_NUM_RECEPTORS")) = True; } if (row.fieldNumber("FEED_INTERVAL") >= 0 && row.dataType("FEED_INTERVAL") == TpDouble) { intervalField_p.attachToRecord(row, "FEED_INTERVAL"); handledCols(row.fieldNumber("FEED_INTERVAL")) = True; } if (row.fieldNumber("FEED_TIME") >= 0 && row.dataType("FEED_TIME") == TpDouble ) { timeField_p.attachToRecord(row, "FEED_TIME"); handledCols(row.fieldNumber("FEED_TIME")) = True; } if (row.fieldNumber("FEED_BEAM_OFFSET") >= 0 && row.dataType("FEED_BEAM_OFFSET") == TpArrayDouble) { beamOffsetField_p.attachToRecord(row, "FEED_BEAM_OFFSET"); handledCols(row.fieldNumber("FEED_BEAM_OFFSET")) = True; } if (row.fieldNumber("FEED_POSITION") >= 0 && row.dataType("FEED_POSITION") == TpArrayDouble) { positionField_p.attachToRecord(row, "FEED_POSITION"); handledCols(row.fieldNumber("FEED_POSITION")) = True; } if (row.fieldNumber("FEED_RECEPTOR_ANGLE") >= 0) { if (row.dataType("FEED_RECEPTOR_ANGLE") == TpArrayDouble) { receptorAngleField_p.attachToRecord(row, "FEED_RECEPTOR_ANGLE"); handledCols(row.fieldNumber("FEED_RECEPTOR_ANGLE")) = True; } else if (row.dataType("FEED_RECEPTOR_ANGLE") == TpDouble) { scaReceptorAngleField_p.attachToRecord(row, "FEED_RECEPTOR_ANGLE"); handledCols(row.fieldNumber("FEED_RECEPTOR_ANGLE")) = True; } } if (row.fieldNumber("FEED_POL_RESPONSE") >= 0 && row.dataType("FEED_POL_RESPONSE") == TpArrayComplex) { polResponseField_p.attachToRecord(row, "FEED_POL_RESPONSE"); handledCols(row.fieldNumber("FEED_POL_RESPONSE")) = True; } if (row.fieldNumber("FEED_POLARIZATION_TYPE") >= 0 && row.dataType("FEED_POLARIZATION_TYPE") == TpString) { polarizationTypeField_p.attachToRecord(row, "FEED_POLARIZATION_TYPE"); handledCols(row.fieldNumber("FEED_POLARIZATION_TYPE")) = True; } } void SDFeedHandler::stokesToPolType(const Vector &stokes, Vector &polType) { std::map polTypeMap; for (uInt i=0;i #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class ColumnsIndex; class MeasurementSet; class MSFeed; class MSFeedColumns; class Record; // // // or // // // //
      • SomeClass //
      • SomeOtherClass //
      • some concept // // // // // // // // // // // // // // // //
      • //
      • // // // //
      • //
      • // // // //
      • add this feature //
      • fix this bug //
      • start discussion of this possible extension // class SDFeedHandler { public: // default ctor is not attached to a MS and hence is useless until attached SDFeedHandler(); // attach this to a MS - no columns are explicitly handled here SDFeedHandler(MeasurementSet &ms, Vector &handledCols, const Record &row); // copy ctor SDFeedHandler(const SDFeedHandler &other); ~SDFeedHandler() {clearAll();} // assignment operator, uses copy semantics SDFeedHandler &operator=(const SDFeedHandler &other); // attach to a MS, the handledCols and row arguments are ignored here void attach(MeasurementSet &ms, Vector &handledCols, const Record &row); // reset internals given indicated row, use the same MS void resetRow(const Record &row); // fill - a new row is added only when necessary void fill(const Record &row, Int antennaId, Int spwinId, const Vector &stokes); // get the current feed ID Int feedId() {return feedId_p;} // the current NUM_RECEPTORS value Int numReceptors() {return nrecpt_p;} private: RecordFieldPtr numRecpKey_p; ColumnsIndex *index_p; MSFeed *msFeed_p; MSFeedColumns *msFeedCols_p; Int feedId_p, nextFeedId_p, nrecpt_p; // fields which might be the result of saving via ms2sdfits RORecordFieldPtr feed1Field_p, feed2Field_p, beamIdField_p, phasedFeedIdField_p, numReceptorsField_p; RORecordFieldPtr intervalField_p, timeField_p, scaReceptorAngleField_p; RORecordFieldPtr > beamOffsetField_p, positionField_p, receptorAngleField_p; RORecordFieldPtr > polResponseField_p; RORecordFieldPtr polarizationTypeField_p; // get the polarization type from the stokes vector void stokesToPolType(const Vector &stokes, Vector &polType); // cleanup everything void clearAll(); void clearRow(); // initialize everything void initAll(MeasurementSet &ms, Vector &handledCols, const Record &row); // initialize things which depend on row void initRow(Vector &handledCols, const Record &row); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/msfits/MSFits/SDFieldHandler.cc000066400000000000000000000326251476623553700210650ustar00rootroot00000000000000//# SDFieldHandler.cc: a FIELD handler for SDFITS data //# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN SDFieldHandler::SDFieldHandler() : msField_p(0), msFieldCols_p(0), rownr_p(-1), index_p(0) {;} SDFieldHandler::SDFieldHandler(MeasurementSet &ms, Vector &handledCols, const Record &row) : msField_p(0), msFieldCols_p(0), rownr_p(-1), index_p(0) { initAll(ms, handledCols, row); } SDFieldHandler::SDFieldHandler(const SDFieldHandler &other) : msField_p(0), msFieldCols_p(0), rownr_p(-1), index_p(0) { *this = other; } SDFieldHandler &SDFieldHandler::operator=(const SDFieldHandler &other) { if (this != &other) { clearAll(); msField_p = new MSField(*(other.msField_p)); AlwaysAssert(msField_p, AipsError); msFieldCols_p = new MSFieldColumns(*msField_p); AlwaysAssert(msFieldCols_p, AipsError); rownr_p = other.rownr_p; fieldIdField_p = other.fieldIdField_p; codeField_p = other.codeField_p; nameField_p = other.nameField_p; timeField_p = other.timeField_p; delayDirField_p = other.delayDirField_p; delayDirRateField_p = other.delayDirRateField_p; phaseDirField_p = other.phaseDirField_p; phaseDirRateField_p = other.phaseDirRateField_p; referenceDirField_p = other.referenceDirField_p; referenceDirRateField_p = other.referenceDirRateField_p; flagRowField_p = other.flagRowField_p; delete index_p; index_p = new ColumnsIndex(*msField_p, stringToVector("NAME,SOURCE_ID,TIME")); AlwaysAssert(index_p, AipsError); // attach the keys nameKey_p.attachToRecord(index_p->accessKey(),"NAME"); sourceIdKey_p.attachToRecord(index_p->accessKey(),"SOURCE_ID"); timeKey_p.attachToRecord(index_p->accessKey(),"TIME"); *nameKey_p = *other.nameKey_p; *sourceIdKey_p = *other.sourceIdKey_p; *timeKey_p = *other.timeKey_p; } return *this; } void SDFieldHandler::attach(MeasurementSet &ms, Vector &handledCols, const Record &row) { clearAll(); initAll(ms, handledCols, row); } void SDFieldHandler::resetRow(const Record &row) { clearRow(); Vector dummyCols(row.nfields()); initRow(dummyCols, row); } void SDFieldHandler::fill(const Record &, const String &name, Int directionRefType, const Matrix &directionPoly, Double time, Int sourceId) { // don't bother unless there is something there if (msField_p) { Bool found = False; Bool checkPhase, checkRef; checkPhase = checkRef = False; Matrix dirPoly = directionPoly; Matrix phasePoly = directionPoly; Matrix referencePoly = directionPoly; Int npoly = dirPoly.nrow() - 1; // adjustments to the above given possible former MS columns if (delayDirField_p.isAttached()) { // old MS 1 is always accompanied by a delayDirRateField_p if (delayDirRateField_p.isAttached()) { // only use this if the rate is non-zero AND non-inf AND not a NaN Vector ddRate(*delayDirRateField_p); Double d0, d1; d0 = ddRate(0); d1 = ddRate(1); if (!near(d0,0.0) && !near(d1,0.0) && !isInf(d0) && !isInf(d1) && !isNaN(d0) && !isNaN(d1)) { npoly = 1; } dirPoly.resize(2,npoly+1); dirPoly.column(0) = *delayDirField_p; if (npoly == 1) dirPoly.column(1) = ddRate; } else { dirPoly.resize((*delayDirField_p).shape()); dirPoly = *delayDirField_p; npoly = dirPoly.nrow() - 1; } } if (phaseDirField_p.isAttached()) { checkPhase = True; // old MS 1 is always accompanied by a phaseDirRateField_p if (phaseDirRateField_p.isAttached()) { // only use this if the rate is non-zero AND non-inf AND not a NaN Vector pdRate(*phaseDirRateField_p); Double p0, p1; p0 = pdRate(0); p1 = pdRate(1); if (!near(p0,0.0) && !near(p1,0.0) && !isInf(p0) && !isInf(p1) && !isNaN(p0) && !isNaN(p1)) { npoly = 1; } phasePoly.resize(2,npoly+1); phasePoly.column(0) = *phaseDirField_p; if (npoly == 1) phasePoly.column(1) = pdRate; } else { phasePoly.resize((*phaseDirField_p).shape()); phasePoly = *phaseDirField_p; npoly = dirPoly.nrow() - 1; } } if (referenceDirField_p.isAttached()) { checkRef = True; // old MS 1 is always accompanied by a referenceDirRateField_p if (referenceDirRateField_p.isAttached()) { // only use this if the rate is non-zero AND non-inf AND not a NaN Vector rdRate(*referenceDirRateField_p); Double r0, r1; r0 = rdRate(0); r1 = rdRate(1); if (!near(r0,0.0) && !near(r1,0.0) && !isInf(r0) && !isInf(r1) && !isNaN(r0) && !isNaN(r1)) { npoly = 1; } referencePoly.resize(2,npoly+1); referencePoly.column(0) = *referenceDirField_p; if (npoly == 1) referencePoly.column(1) = rdRate; } else { referencePoly.resize((*referenceDirField_p).shape()); referencePoly = *referenceDirField_p; npoly = dirPoly.nrow() - 1; } } if (fieldIdField_p.isAttached() && *fieldIdField_p >= 0) { // see if this row can be reused Int thisRow = *fieldIdField_p; Bool found = thisRow >= 0 && uInt(thisRow) < msField_p->nrow(); found = found && msFieldCols_p->sourceId()(thisRow) == sourceId; if (found && codeField_p.isAttached()) { found = *codeField_p == msFieldCols_p->code()(thisRow); } if (found && nameField_p.isAttached()) { found = name == *nameField_p && *nameField_p == msFieldCols_p->name()(thisRow); } if (found && timeField_p.isAttached()) { found = time == *timeField_p && *timeField_p == msFieldCols_p->time()(thisRow); } if (found && flagRowField_p.isAttached()) { found = *flagRowField_p == msFieldCols_p->flagRow()(thisRow); } found = found && npoly == msFieldCols_p->numPoly()(thisRow); found = found && allEQ(dirPoly,msFieldCols_p->delayDir()(thisRow)); found = found && checkPhase && allEQ(phasePoly, msFieldCols_p->phaseDir()(thisRow)); found = found && checkRef && allEQ(referencePoly, msFieldCols_p->referenceDir()(thisRow)); if (found) rownr_p = thisRow; } if (!found) { // try and look for it *nameKey_p = name; *sourceIdKey_p = sourceId; *timeKey_p = time; Vector rows = index_p->getRowNumbers(); uInt i=0; while (inumPoly()(thisRow); found = found && allEQ(msFieldCols_p->delayDir()(thisRow),dirPoly); // that is enough for a standard SDFITS fill, the following additional // tests are done for the case where this SDFITS originated as a MS // either as version 1 or 2 if (found && codeField_p.isAttached()) { found = msFieldCols_p->code()(thisRow) == *codeField_p; } if (found && checkPhase) { found = allEQ(msFieldCols_p->phaseDir()(thisRow),phasePoly); } if (found && checkRef) { found = allEQ(msFieldCols_p->referenceDir()(thisRow),referencePoly); } if (found && flagRowField_p.isAttached()) { found = msFieldCols_p->flagRow()(thisRow) == *flagRowField_p; } if (found) rownr_p = thisRow; else i++; } } if (!found) { // add it in rownr_p = msField_p->nrow(); if (rownr_p ==0) { // set the column direction references to the value of this direction msFieldCols_p->delayDirMeasCol().setDescRefCode(directionRefType); msFieldCols_p->phaseDirMeasCol().setDescRefCode(directionRefType); msFieldCols_p->referenceDirMeasCol().setDescRefCode(directionRefType); } msField_p->addRow(); msFieldCols_p->name().put(rownr_p, name); if (codeField_p.isAttached()) { msFieldCols_p->code().put(rownr_p,*codeField_p); } else { msFieldCols_p->code().put(rownr_p,""); } msFieldCols_p->time().put(rownr_p, time); msFieldCols_p->numPoly().put(rownr_p, npoly); msFieldCols_p->delayDir().put(rownr_p, dirPoly); msFieldCols_p->phaseDir().put(rownr_p, phasePoly); msFieldCols_p->referenceDir().put(rownr_p, referencePoly); msFieldCols_p->sourceId().put(rownr_p, sourceId); if (flagRowField_p.isAttached()) { msFieldCols_p->flagRow().put(rownr_p, *flagRowField_p); } else { msFieldCols_p->flagRow().put(rownr_p, False); } } } } void SDFieldHandler::clearAll() { delete msField_p; msField_p = 0; delete msFieldCols_p; msFieldCols_p = 0; delete index_p; index_p = 0; clearRow(); } void SDFieldHandler::clearRow() { rownr_p = -1; fieldIdField_p.detach(); codeField_p.detach(); nameField_p.detach(); timeField_p.detach(); delayDirField_p.detach(); delayDirRateField_p.detach(); phaseDirField_p.detach(); phaseDirRateField_p.detach(); referenceDirField_p.detach(); referenceDirRateField_p.detach(); flagRowField_p.detach(); } void SDFieldHandler::initAll(MeasurementSet &ms, Vector &handledCols, const Record &row) { msField_p = new MSField(ms.field()); AlwaysAssert(msField_p, AipsError); msFieldCols_p = new MSFieldColumns(*msField_p); AlwaysAssert(msFieldCols_p, AipsError); index_p = new ColumnsIndex(*msField_p, stringToVector("NAME,SOURCE_ID,TIME")); AlwaysAssert(index_p, AipsError); nameKey_p.attachToRecord(index_p->accessKey(),"NAME"); sourceIdKey_p.attachToRecord(index_p->accessKey(),"SOURCE_ID"); timeKey_p.attachToRecord(index_p->accessKey(),"TIME"); initRow(handledCols, row); } void SDFieldHandler::initRow(Vector &handledCols, const Record &row) { rownr_p = -1; if (row.fieldNumber("MAIN_FIELD_ID") >= 0 && row.dataType("MAIN_FIELD_ID") == TpInt) { fieldIdField_p.attachToRecord(row,"MAIN_FIELD_ID"); handledCols(row.fieldNumber("MAIN_FIELD_ID")) = True; } if (row.fieldNumber("FIELD_CODE") >= 0 && row.dataType("FIELD_CODE") == TpString) { codeField_p.attachToRecord(row,"FIELD_CODE"); handledCols(row.fieldNumber("FIELD_CODE")) = True; } if (row.fieldNumber("FIELD_NAME") >= 0 && row.dataType("FIELD_NAME") == TpString) { nameField_p.attachToRecord(row,"FIELD_NAME"); handledCols(row.fieldNumber("FIELD_NAME")) = True; } if (row.fieldNumber("FIELD_TIME") >= 0 && row.dataType("FIELD_TIME") == TpDouble) { timeField_p.attachToRecord(row,"FIELD_TIME"); handledCols(row.fieldNumber("FIELD_TIME")) = True; } if (row.fieldNumber("FIELD_DELAY_DIR") >= 0 && row.dataType("FIELD_DELAY_DIR") == TpArrayDouble) { delayDirField_p.attachToRecord(row,"FIELD_DELAY_DIR"); handledCols(row.fieldNumber("FIELD_DELAY_DIR")) = True; } if (row.fieldNumber("FIELD_DELAY_DIR_RATE") >= 0 && row.dataType("FIELD_DELAY_DIR_RATE") == TpArrayDouble) { delayDirRateField_p.attachToRecord(row,"FIELD_DELAY_DIR_RATE"); handledCols(row.fieldNumber("FIELD_DELAY_DIR_RATE")) = True; } if (row.fieldNumber("FIELD_PHASE_DIR") >= 0 && row.dataType("FIELD_PHASE_DIR") == TpArrayDouble) { phaseDirField_p.attachToRecord(row,"FIELD_PHASE_DIR"); handledCols(row.fieldNumber("FIELD_PHASE_DIR")) = True; } if (row.fieldNumber("FIELD_PHASE_DIR_RATE") >= 0 && row.dataType("FIELD_PHASE_DIR_RATE") == TpArrayDouble) { phaseDirRateField_p.attachToRecord(row,"FIELD_PHASE_DIR_RATE"); handledCols(row.fieldNumber("FIELD_PHASE_DIR_RATE")) = True; } if (row.fieldNumber("FIELD_REFERENCE_DIR") >= 0 && row.dataType("FIELD_REFERENCE_DIR") == TpArrayDouble) { referenceDirField_p.attachToRecord(row,"FIELD_REFERENCE_DIR"); handledCols(row.fieldNumber("FIELD_REFERENCE_DIR")) = True; } if (row.fieldNumber("FIELD_REFERENCE_DIR_RATE") >= 0 && row.dataType("FIELD_REFERENCE_DIR_RATE") == TpArrayDouble) { referenceDirRateField_p.attachToRecord(row,"FIELD_REFERENCE_DIR_RATE"); handledCols(row.fieldNumber("FIELD_REFERENCE_DIR_RATE")) = True; } if (row.fieldNumber ("FIELD_FLAG_ROW") >= 0 && row.dataType("FIELD_FLAG_ROW") == TpBool) { flagRowField_p.attachToRecord(row, "FIELD_FLAG_ROW"); handledCols(row.fieldNumber("FIELD_FLAG_ROW")) = True; } } } //# NAMESPACE CASACORE - END casacore-3.7.1/msfits/MSFits/SDFieldHandler.h000066400000000000000000000105571476623553700207270ustar00rootroot00000000000000//# SDFieldFiller.h: fills the FIELD table for the SDFITS filler //# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_SDFIELDHANDLER_H #define MS_SDFIELDHANDLER_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MeasurementSet; class MSField; class MSFieldColumns; class String; class Record; class ColumnsIndex; // // // or // // // //
      • SomeClass //
      • SomeOtherClass //
      • some concept // // // // // // // // // // // // // // // //
      • //
      • // // // //
      • //
      • // // // //
      • add this feature //
      • fix this bug //
      • start discussion of this possible extension // class SDFieldHandler { public: // default ctor is not attached to a MS and hence is useless until attached SDFieldHandler(); // attach this to a MS - no columns are explicitly handled here SDFieldHandler(MeasurementSet &ms, Vector &handledCols, const Record &row); // copy ctor SDFieldHandler(const SDFieldHandler &other); ~SDFieldHandler() {clearAll();} // assignment operator, uses copy semantics SDFieldHandler &operator=(const SDFieldHandler &other); // attach to a MS, the handledCols and row arguments are ignored here void attach(MeasurementSet &ms, Vector &handledCols, const Record &row); // reset internals given indicated row, use the same MS; just resets the id pointer void resetRow(const Record &row); // fill - a new row is added at each call unless the data is from a previous MS fill // in which case an existing MAIN_FIELD_ID is used to see if that existing row might // be reused void fill(const Record &row, const String &name, Int directionRefType, const Matrix &directionPoly, Double time, Int sourceId); // get the current field ID Int fieldId() {return rownr_p;} private: MSField *msField_p; MSFieldColumns *msFieldCols_p; Int rownr_p; // fields which might be present if the data is originally from a MS RORecordFieldPtr fieldIdField_p; RORecordFieldPtr codeField_p, nameField_p; RORecordFieldPtr timeField_p; RORecordFieldPtr > delayDirField_p, delayDirRateField_p, phaseDirField_p, phaseDirRateField_p, referenceDirField_p, referenceDirRateField_p; RORecordFieldPtr flagRowField_p; ColumnsIndex *index_p; RecordFieldPtr nameKey_p; RecordFieldPtr sourceIdKey_p; RecordFieldPtr timeKey_p; // cleanup everything void clearAll(); void clearRow(); // initialize everything void initAll(MeasurementSet &ms, Vector &handledCols, const Record &row); // initialize things which depend on the row void initRow(Vector &handledCols, const Record &row); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/msfits/MSFits/SDHistoryHandler.cc000066400000000000000000000110611476623553700214720ustar00rootroot00000000000000//# SDHistoryHandler.cc: an HISTORY handler for SDFITS data //# Copyright (C) 2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN SDHistoryHandler::SDHistoryHandler() : msHis_p(0), msHisCols_p(0) {;} SDHistoryHandler::SDHistoryHandler(MeasurementSet &ms, const Vector &handledCols, const Record &row) : msHis_p(0), msHisCols_p(0) { initAll(ms, handledCols, row); } SDHistoryHandler::SDHistoryHandler(const SDHistoryHandler &other) : msHis_p(0), msHisCols_p(0) { *this = other; } SDHistoryHandler &SDHistoryHandler::operator=(const SDHistoryHandler &other) { if (this != &other) { clearAll(); msHis_p = new MSHistory(*(other.msHis_p)); AlwaysAssert(msHis_p, AipsError); msHisCols_p = new MSHistoryColumns(*msHis_p); AlwaysAssert(msHisCols_p, AipsError); timesys_p = other.timesys_p; } return *this; } void SDHistoryHandler::attach(MeasurementSet &ms, Vector &handledCols, const Record &row) { clearAll(); initAll(ms, handledCols, row); } void SDHistoryHandler::fill(const Record &, Int observationId, const String &message, const String &priority) { // this always just fills as is // don't bother unless there is something there if (msHis_p) { uInt rownr = msHis_p->nrow(); msHis_p->addRow(); // get the current time Quantity now; MVTime::read(now,"today"); MEpoch::Types timesys = MEpoch::UTC; // see if there is a timesys pointer which might override this if (timesys_p.isAttached()) { // there doesn't seem to be a simpler way MVTime dummy; if (!FITSDateUtil::fromFITS(dummy, timesys, "2000-01-01", *timesys_p)) { timesys = MEpoch::UTC; } } msHisCols_p->timeMeas().put(rownr, MEpoch(now, timesys)); msHisCols_p->observationId().put(rownr, observationId); msHisCols_p->message().put(rownr, message); msHisCols_p->priority().put(rownr, priority); msHisCols_p->objectId().put(rownr, -1); msHisCols_p->application().put(rownr, ""); msHisCols_p->cliCommand().put(rownr, Vector(1)); msHisCols_p->appParams().put(rownr, Vector(1)); } } void SDHistoryHandler::clearAll() { delete msHis_p; msHis_p = 0; delete msHisCols_p; msHisCols_p = 0; clearRow(); } void SDHistoryHandler::clearRow() { timesys_p.detach(); } void SDHistoryHandler::initAll(MeasurementSet &ms, const Vector &handledCols, const Record &row) { msHis_p = new MSHistory(ms.history()); AlwaysAssert(msHis_p, AipsError); msHisCols_p = new MSHistoryColumns(*msHis_p); AlwaysAssert(msHisCols_p, AipsError); initRow(handledCols, row); } void SDHistoryHandler::initRow(const Vector &, const Record &row) { // look for a TIMESYS field in row if (row.fieldNumber("TIMESYS") >= 0) { timesys_p.attachToRecord(row, "TIMESYS"); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/msfits/MSFits/SDHistoryHandler.h000066400000000000000000000072001476623553700213340ustar00rootroot00000000000000//# SDHistoryFiller.h: fills the HISTORY table for the SDFITS filler //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_SDHISTORYHANDLER_H #define MS_SDHISTORYHANDLER_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MeasurementSet; class MSHistory; class MSHistoryColumns; class Record; // // // or // // // //
      • SomeClass //
      • SomeOtherClass //
      • some concept // // // // // // // // // // // // // // // //
      • //
      • // // // //
      • //
      • // // // //
      • add this feature //
      • fix this bug //
      • start discussion of this possible extension // class SDHistoryHandler { public: // default ctor is not attached to a MS and hence is useless until attached SDHistoryHandler(); // attach this to a MS - no columns are explicitly handled here SDHistoryHandler(MeasurementSet &ms, const Vector &handledCols, const Record &row); // copy ctor SDHistoryHandler(const SDHistoryHandler &other); ~SDHistoryHandler() {clearAll();} // assignment operator, uses copy semantics SDHistoryHandler &operator=(const SDHistoryHandler &other); // attach to a MS void attach(MeasurementSet &ms, Vector &handledCols, const Record &row); // reset internals given indicated row, use the same MS void resetRow(const Record &row); // fill - a new row is added on each call, the message time stamp is the current time void fill(const Record& row, Int observationId, const String &message, const String &priority); private: MSHistory *msHis_p; MSHistoryColumns *msHisCols_p; // TIMESYS field pointer when available RORecordFieldPtr timesys_p; // cleanup everything void clearAll(); // clean up row-dependent stuff void clearRow(); // initialize everything void initAll(MeasurementSet &ms, const Vector &handledCols, const Record &row); // initialize stuff which depends on the row void initRow(const Vector &handledCols, const Record &row); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/msfits/MSFits/SDMainHandler.cc000066400000000000000000000205631476623553700207240ustar00rootroot00000000000000//# SDMainHandler.cc: a MAIN handler for SDFITS data //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN SDMainHandler::SDMainHandler() : ms_p(0), msCols_p(0), scanNumberId_p(-1), arrayIdId_p(-1), sigmaId_p(-1), flagRowId_p(-1), intervalId_p(-1), weightId_p(-1), flagId_p(-1), timeCentroidId_p(-1) {;} SDMainHandler::SDMainHandler(MeasurementSet &ms, Vector &handledCols, const Record &row) : ms_p(0), msCols_p(0), scanNumberId_p(-1), arrayIdId_p(-1), sigmaId_p(-1), flagRowId_p(-1), intervalId_p(-1), weightId_p(-1), flagId_p(-1), timeCentroidId_p(-1) { initAll(ms, handledCols, row); } SDMainHandler::SDMainHandler(const SDMainHandler &other) : ms_p(0), msCols_p(0), scanNumberId_p(-1), arrayIdId_p(-1), sigmaId_p(-1), flagRowId_p(-1), intervalId_p(-1), weightId_p(-1), flagId_p(-1), timeCentroidId_p(-1) { *this = other; } SDMainHandler &SDMainHandler::operator=(const SDMainHandler &other) { if (this != &other) { clearAll(); ms_p = new MeasurementSet(*(other.ms_p)); AlwaysAssert(ms_p, AipsError); msCols_p = new MSMainColumns(*ms_p); AlwaysAssert(msCols_p, AipsError); scanNumberId_p = other.scanNumberId_p; arrayIdId_p = other.arrayIdId_p; sigmaId_p = other.sigmaId_p; flagRowId_p = other.flagRowId_p; intervalId_p = other.intervalId_p; weightId_p = other.weightId_p; flagId_p = other.flagId_p; timeCentroidId_p = other.timeCentroidId_p; } return *this; } void SDMainHandler::attach(MeasurementSet &ms, Vector &handledCols, const Record &row) { clearAll(); initAll(ms, handledCols, row); } void SDMainHandler::resetRow(const Record &row) { clearRow(); Vector dummyHandledCols; initRow(dummyHandledCols, row); } void SDMainHandler::fill(const Record &row, const MEpoch &time, Int antennaId, Int feedId, Int dataDescId, Int fieldId, const MVTime &exposure, Int observationId, const Matrix &floatData) { // don't bother unless there is something there if (ms_p) { // fill it Int rownr = ms_p->nrow(); ms_p->addRow(); Int ncorr = floatData.nrow(); msCols_p->timeMeas().put(rownr, time); msCols_p->antenna1().put(rownr,antennaId); msCols_p->antenna2().put(rownr,antennaId); msCols_p->feed1().put(rownr,feedId); msCols_p->feed2().put(rownr,feedId); msCols_p->dataDescId().put(rownr, dataDescId); msCols_p->processorId().put(rownr, -1); msCols_p->fieldId().put(rownr, fieldId); Double texp = exposure.get("s").getValue(); if (intervalId_p >= 0) { msCols_p->interval().put(rownr, row.asDouble(intervalId_p)); } else { msCols_p->interval().put(rownr, texp); } msCols_p->exposure().put(rownr, texp); Int scanNumber = -1; if (scanNumberId_p >= 0) { switch (scanNumberType_p) { case TpInt: case TpShort: scanNumber = row.asInt(scanNumberId_p); break; case TpDouble: case TpFloat: scanNumber = Int(row.asDouble(scanNumberId_p)+0.5); break; default: // a warning should be issued when the type is initially determined scanNumber = -1; break; } } msCols_p->scanNumber().put(rownr, scanNumber); if (arrayIdId_p>=0) { msCols_p->arrayId().put(rownr, row.asInt(arrayIdId_p)); } else { msCols_p->arrayId().put(rownr, -1); } msCols_p->observationId().put(rownr, observationId); msCols_p->stateId().put(rownr, -1); msCols_p->uvw().put(rownr, Vector(3,0.0)); msCols_p->floatData().put(rownr, floatData); if (sigmaId_p >= 0) { msCols_p->sigma().put(rownr, row.asArrayFloat(sigmaId_p)); } else { // should this be TSYS and exposure based? msCols_p->sigma().put(rownr, Vector(ncorr, 1.0)); } if (weightId_p >= 0) { msCols_p->weight().put(rownr, row.asArrayFloat(weightId_p)); } else { msCols_p->weight().put(rownr, Vector(ncorr, 1.0)); } if (flagId_p >= 0) { msCols_p->flag().put(rownr, row.asArrayBool(flagId_p)); } else { msCols_p->flag().put(rownr, Matrix(floatData.shape(), False)); } if (timeCentroidId_p >= 0) { msCols_p->timeCentroid().put(rownr, row.asDouble(timeCentroidId_p)); } else { msCols_p->timeCentroid().put(rownr,msCols_p->time()(rownr)); } IPosition emptyFlagCatShape(3,0); emptyFlagCatShape(0) = ncorr; emptyFlagCatShape(1) = floatData.ncolumn(); msCols_p->flagCategory().put(rownr, Array(emptyFlagCatShape)); if (flagRowId_p >= 0) { msCols_p->flagRow().put(rownr, row.asBool(flagRowId_p)); } else { msCols_p->flagRow().put(rownr, False); } } } void SDMainHandler::clearAll() { delete ms_p; ms_p = 0; delete msCols_p; msCols_p = 0; clearRow(); } void SDMainHandler::clearRow() { scanNumberId_p = arrayIdId_p = sigmaId_p = flagRowId_p = intervalId_p = weightId_p = flagId_p = timeCentroidId_p = -1; } void SDMainHandler::initAll(MeasurementSet &ms, Vector &handledCols, const Record &row) { ms_p = new MeasurementSet(ms); AlwaysAssert(ms_p, AipsError); initRow(handledCols, row); msCols_p = new MSMainColumns(*ms_p); AlwaysAssert(msCols_p, AipsError); } void SDMainHandler::initRow(Vector &handledCols, const Record &row) { scanNumberId_p = row.fieldNumber("SCAN"); if (scanNumberId_p >= 0) { handledCols(scanNumberId_p) = True; scanNumberType_p = row.dataType(scanNumberId_p); } arrayIdId_p = row.fieldNumber("MAIN_ARRAY_ID"); if (arrayIdId_p >= 0) handledCols(arrayIdId_p) = True; sigmaId_p = row.fieldNumber("MAIN_SIGMA"); if (sigmaId_p >= 0) handledCols(sigmaId_p) = True; flagRowId_p = row.fieldNumber("MAIN_FLAG_ROW"); if (flagRowId_p >= 0) handledCols(flagRowId_p) = True; intervalId_p = row.fieldNumber("MAIN_INTERVAL"); if (intervalId_p >= 0) handledCols(intervalId_p) = True; weightId_p = row.fieldNumber("MAIN_WEIGHT"); if (weightId_p >= 0) handledCols(weightId_p) = True; flagId_p = row.fieldNumber("MAIN_FLAG"); if (flagId_p >= 0) handledCols(flagId_p) = True; timeCentroidId_p = row.fieldNumber("MAIN_TIME_CENTROID"); if (timeCentroidId_p >= 0) handledCols(timeCentroidId_p) = True; // RADECSYS is fully covered elsewhere, ignore it if it exists if (row.fieldNumber("RADECSYS") >= 0) handledCols(row.fieldNumber("RADECSYS")) = True; // the following fields generated when MS v 1 was converted to an SDFITS file are ignored // There is no CORRELATOR table in MS 2 and it should never have been used for SD data // in MS 1. if (row.fieldNumber("MAIN_CORRELATOR_ID") >= 0) handledCols(row.fieldNumber("MAIN_CORRELATOR_ID")) = True; // there is no PULSAR_BIN in MS 2 and its unlikely it will have been used by // single dish data in MS 1 if (row.fieldNumber("MAIN_PULSAR_BIN") >= 0) handledCols(row.fieldNumber("MAIN_PULSAR_BIN")) = True; } } //# NAMESPACE CASACORE - END casacore-3.7.1/msfits/MSFits/SDMainHandler.h000066400000000000000000000074231476623553700205660ustar00rootroot00000000000000//# SDMainFiller.h: fills the MAIN table for the SDFITS filler //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_SDMAINHANDLER_H #define MS_SDMAINHANDLER_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MeasurementSet; class MSMainColumns; class MEpoch; class MVTime; class Record; // // // or // // // //
      • SomeClass //
      • SomeOtherClass //
      • some concept // // // // // // // // // // // // // // // //
      • //
      • // // // //
      • //
      • // // // //
      • add this feature //
      • fix this bug //
      • start discussion of this possible extension // class SDMainHandler { public: // default ctor is not attached to a MS and hence is useless until attached SDMainHandler(); // attach this to a MS - mark fields in row as handled SDMainHandler(MeasurementSet &ms, Vector &handledCols, const Record &row); // copy ctor SDMainHandler(const SDMainHandler &other); ~SDMainHandler() {clearAll();} // assignment operator, uses copy semantics SDMainHandler &operator=(const SDMainHandler &other); // attach to a MS, mark fields in row as handled void attach(MeasurementSet &ms, Vector &handledCols, const Record &row); // reset internals given indicated row, use the same MS void resetRow(const Record &row); // fill - a new row is always added void fill(const Record &row, const MEpoch &time, Int antennaId, Int feedId, Int dataDescId, Int fieldId, const MVTime &exposure, Int observationId, const Matrix &floatData); private: MeasurementSet *ms_p; MSMainColumns *msCols_p; Int scanNumberId_p; DataType scanNumberType_p; // fields from sdfits2ms, independent of MS version number so far Int arrayIdId_p, sigmaId_p, flagRowId_p, intervalId_p, weightId_p, flagId_p, timeCentroidId_p; // cleanup everything void clearAll(); // cleanup row-related stuff void clearRow(); // initialize everything void initAll(MeasurementSet &ms, Vector &handledCols, const Record &row); // intialize the row related stuff void initRow(Vector &handledCols, const Record &row); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/msfits/MSFits/SDObservationHandler.cc000066400000000000000000000263371476623553700223400ustar00rootroot00000000000000//# SDObservationHandler.cc: an OBSERVATION handler for SDFITS data //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN SDObservationHandler::SDObservationHandler() : index_p(0), msObs_p(0), msObsCols_p(0), rownr_p(-1) {;} SDObservationHandler::SDObservationHandler(MeasurementSet &ms, Vector &handledCols, const Record &row) : index_p(0), msObs_p(0), msObsCols_p(0), rownr_p(-1) { initAll(ms, handledCols, row); } SDObservationHandler::SDObservationHandler(const SDObservationHandler &other) : index_p(0), msObs_p(0), msObsCols_p(0), rownr_p(-1) { *this = other; } SDObservationHandler &SDObservationHandler::operator=(const SDObservationHandler &other) { if (this != &other) { clearAll(); index_p = new ColumnsIndex(*(other.index_p)); AlwaysAssert(index_p, AipsError); // need to avoid the assignment operator here because we want // this to point to the field in index_p, not in other.index_p telescopeKey_p.attachToRecord(index_p->accessKey(), MSObservation::columnName(MSObservation::TELESCOPE_NAME)); observerKey_p.attachToRecord(index_p->accessKey(), MSObservation::columnName(MSObservation::OBSERVER)); projectKey_p.attachToRecord(index_p->accessKey(), MSObservation::columnName(MSObservation::PROJECT)); if (index_p->accessKey().fieldNumber("NS_OBSID") >= 0) { ns_obsidKey_p.attachToRecord(index_p->accessKey(), "NS_OBSID"); } releaseDateKey_p.attachToRecord(index_p->accessKey(), MSObservation::columnName(MSObservation::RELEASE_DATE)); flagRowKey_p.attachToRecord(index_p->accessKey(), MSObservation::columnName(MSObservation::FLAG_ROW)); msObs_p = new MSObservation(*(other.msObs_p)); AlwaysAssert(msObs_p, AipsError); msObsCols_p = new MSObservationColumns(*msObs_p); AlwaysAssert(msObsCols_p, AipsError); if (ns_obsidKey_p.isAttached()) { nsObsIdCol_p.attach(*msObs_p, "NS_OBSID"); } rownr_p = other.rownr_p; // this should point to the same field as that in other observer_p = other.observer_p; projid_p = other.projid_p; obsid_p = other.obsid_p; releaseDate_p = other.releaseDate_p; flagRow_p = other.flagRow_p; timeRange_p = other.timeRange_p; } return *this; } void SDObservationHandler::attach(MeasurementSet &ms, Vector &handledCols, const Record &row) { clearAll(); initAll(ms, handledCols, row); } void SDObservationHandler::resetRow(const Record &row) { clearRow(); Vector dummyHandled; initRow(dummyHandled, row); } void SDObservationHandler::fill(const Record &, const String &telescopeName, const Vector &timeRange) { // don't bother unless there is something there if (msObs_p) { // NS_OBSID key must be set first since it might cause a new column // to be add which will require that the index be remade, which will // un-attach the other index key field pointers if (obsid_p.isAttached() && (*obsid_p).length() > 0) { if (!ns_obsidKey_p.isAttached()) { // need to add a new column to hold this field msObs_p->addColumn(ScalarColumnDesc("NS_OBSID", "SDFITS OBSID keyword/column value")); nsObsIdCol_p.attach(*msObs_p, "NS_OBSID"); // and renake the index with this column makeIndex(); } *ns_obsidKey_p = *obsid_p; } *telescopeKey_p = telescopeName; if (observer_p.isAttached()) { // fill the key with the observer name *observerKey_p = *observer_p; } else { // just use an empty string as the observer key *observerKey_p = ""; } if (projid_p.isAttached()) { // fill the key with the observer name *projectKey_p = *projid_p; } else { // just use an empty string as the project key *projectKey_p = ""; } if (releaseDate_p.isAttached()) { *releaseDateKey_p = *releaseDate_p; } else { // use a time of 0.0 as the default release date *releaseDateKey_p = 0.0; } if (flagRow_p.isAttached()) { *flagRowKey_p = *flagRow_p; } else { // default is not flag the row *flagRowKey_p = False; } Bool found = False; uInt whichRow =0; // if there is a time range field, there may be more than one matching row if (timeRange_p.isAttached()) { Vector rows = index_p->getRowNumbers(); uInt whichElement = 0; while (!found && whichElement < rows.nelements()) { whichRow = rows(whichElement++); if (allEQ(*timeRange_p, msObsCols_p->timeRange()(whichRow))) found = True; } } else { // otherwise, there should be just a single match whichRow = index_p->getRowNumber(found); } if (found) { // we have a winner rownr_p = whichRow; if (!timeRange_p.isAttached()) { updateTimeRange(timeRange); } } else { // we need to add one rownr_p = msObs_p->nrow(); msObs_p->addRow(); Vector emptySVec(1); msObsCols_p->flagRow().put(rownr_p, *flagRowKey_p); msObsCols_p->log().put(rownr_p, emptySVec); msObsCols_p->observer().put(rownr_p, *observerKey_p); msObsCols_p->project().put(rownr_p, *projectKey_p); msObsCols_p->releaseDate().put(rownr_p, *releaseDateKey_p); msObsCols_p->schedule().put(rownr_p,emptySVec); msObsCols_p->scheduleType().put(rownr_p,""); msObsCols_p->telescopeName().put(rownr_p, *telescopeKey_p); if (timeRange_p.isAttached()) { msObsCols_p->timeRange().put(rownr_p, *timeRange_p); } else { msObsCols_p->timeRange().put(rownr_p, timeRange); } // the NS_OBSID column if available if (!nsObsIdCol_p.isNull()) { nsObsIdCol_p.put(rownr_p, *ns_obsidKey_p); } } } } void SDObservationHandler::clearAll() { delete index_p; index_p = 0; delete msObs_p; msObs_p = 0; delete msObsCols_p; msObsCols_p = 0; clearRow(); } void SDObservationHandler::clearRow() { observer_p.detach(); projid_p.detach(); obsid_p.detach(); releaseDate_p.detach(); flagRow_p.detach(); timeRange_p.detach(); rownr_p = -1; } void SDObservationHandler::initAll(MeasurementSet &ms, Vector &handledCols, const Record &row) { msObs_p = new MSObservation(ms.observation()); AlwaysAssert(msObs_p, AipsError); msObsCols_p = new MSObservationColumns(*msObs_p); AlwaysAssert(msObsCols_p, AipsError); if (msObs_p->tableDesc().isColumn(String("NS_OBSID"))) { nsObsIdCol_p.attach(*msObs_p, "NS_OBSID"); } makeIndex(); initRow(handledCols, row); } void SDObservationHandler::makeIndex() { // ensure that any existing index is first deleted delete index_p; index_p = 0; Int nKeys = 5; if (!nsObsIdCol_p.isNull()) nKeys++; Vector keys(nKeys); keys(0) = MSObservation::columnName(MSObservation::TELESCOPE_NAME); keys(1) = MSObservation::columnName(MSObservation::OBSERVER); keys(2) = MSObservation::columnName(MSObservation::PROJECT); keys(3) = MSObservation::columnName(MSObservation::RELEASE_DATE); keys(4) = MSObservation::columnName(MSObservation::FLAG_ROW); if (!nsObsIdCol_p.isNull()) { keys(5) = "NS_OBSID"; } index_p = new ColumnsIndex(*msObs_p, keys); AlwaysAssert(index_p, AipsError); telescopeKey_p.attachToRecord(index_p->accessKey(), MSObservation::columnName(MSObservation::TELESCOPE_NAME)); observerKey_p.attachToRecord(index_p->accessKey(), MSObservation::columnName(MSObservation::OBSERVER)); projectKey_p.attachToRecord(index_p->accessKey(), MSObservation::columnName(MSObservation::PROJECT)); releaseDateKey_p.attachToRecord(index_p->accessKey(), MSObservation::columnName(MSObservation::RELEASE_DATE)); flagRowKey_p.attachToRecord(index_p->accessKey(), MSObservation::columnName(MSObservation::FLAG_ROW)); if (!nsObsIdCol_p.isNull()) { ns_obsidKey_p.attachToRecord(index_p->accessKey(),"NS_OBSID"); } } void SDObservationHandler::initRow(Vector &handledCols, const Record &row) { AlwaysAssert(handledCols.nelements()==row.description().nfields(), AipsError); if (row.fieldNumber("OBSERVER") >= 0) { observer_p.attachToRecord(row, "OBSERVER"); handledCols(row.fieldNumber("OBSERVER")) = True; } if (row.fieldNumber("PROJID") >= 0) { projid_p.attachToRecord(row, "PROJID"); handledCols(row.fieldNumber("PROJID")) = True; } if (row.fieldNumber("OBSID") >= 0) { obsid_p.attachToRecord(row, "OBSID"); handledCols(row.fieldNumber("OBSID")) = True; } if (row.fieldNumber("OBSERVATION_FLAG_ROW") >= 0) { flagRow_p.attachToRecord(row, "OBSERVATION_FLAG_ROW"); handledCols(row.fieldNumber("OBSERVATION_FLAG_ROW")) = True; } if (row.fieldNumber("OBSERVATION_RELEASE_DATE") >= 0) { releaseDate_p.attachToRecord(row, "OBSERVATION_RELEASE_DATE"); handledCols(row.fieldNumber("OBSERVATION_RELEASE_DATE")) = True; } if (row.fieldNumber("OBSERVATION_TIME_RANGE") >= 0) { timeRange_p.attachToRecord(row, "OBSERVATION_TIME_RANGE"); handledCols(row.fieldNumber("OBSERVATION_TIME_RANGE")) = True; } // ignore MAIN_OBSERVATION_ID if (row.fieldNumber("MAIN_OBSERVATION_ID") >= 0) { handledCols(row.fieldNumber("MAIN_OBSERVATION_ID")) = True; } // row number isn't set until the following fill rownr_p = -1; } void SDObservationHandler::updateTimeRange(const Vector &timeRange) { if (rownr_p >= 0) { Vector oldTimeRange = msObsCols_p->timeRange()(rownr_p); oldTimeRange(0) = min(oldTimeRange(0),timeRange(0)); oldTimeRange(1) = max(oldTimeRange(1),timeRange(1)); msObsCols_p->timeRange().put(rownr_p, oldTimeRange); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/msfits/MSFits/SDObservationHandler.h000066400000000000000000000107311476623553700221710ustar00rootroot00000000000000//# SDObservationFiller.h: fills the OBSERVATION table for the SDFITS filler //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_SDOBSERVATIONHANDLER_H #define MS_SDOBSERVATIONHANDLER_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class ColumnsIndex; class MeasurementSet; class MSObservation; class MSObservationColumns; class Record; class String; // // // or // // // //
      • SomeClass //
      • SomeOtherClass //
      • some concept // // // // // // // // // // // // // // // //
      • //
      • // // // //
      • //
      • // // // //
      • add this feature //
      • fix this bug //
      • start discussion of this possible extension // class SDObservationHandler { public: // default ctor is not attached to a MS and hence is useless until attached SDObservationHandler(); // attach this to a MS, mark the appropriate columns as handled given // the indicated row SDObservationHandler(MeasurementSet &ms, Vector &handledCols, const Record &row); // copy ctor SDObservationHandler(const SDObservationHandler &other); ~SDObservationHandler() {clearAll();} // assignment operator, uses copy semantics SDObservationHandler &operator=(const SDObservationHandler &other); // attach to a MS, mark the appropriate columns as handled given the row void attach(MeasurementSet &ms, Vector &handledCols, const Record &row); // reset internals given indicated row, use the same MS void resetRow(const Record &row); // fill - a new row is added only when necessary void fill(const Record &row, const String &telescopeName, const Vector &timeRange); // get the current observation ID Int observationId() {return rownr_p;} // update the time range void updateTimeRange(const Vector &timeRange); private: ColumnsIndex *index_p; RecordFieldPtr telescopeKey_p, observerKey_p, projectKey_p, ns_obsidKey_p; RecordFieldPtr releaseDateKey_p; RecordFieldPtr flagRowKey_p; MSObservation *msObs_p; MSObservationColumns *msObsCols_p; Int rownr_p; ScalarColumn nsObsIdCol_p; // pointers to fields in record, only used if attached RORecordFieldPtr observer_p, projid_p, obsid_p; RORecordFieldPtr releaseDate_p; RORecordFieldPtr flagRow_p; RORecordFieldPtr > timeRange_p; // cleanup everything void clearAll(); // cleanup things which depend on the row description being fixed void clearRow(); // initialize everything void initAll(MeasurementSet &ms, Vector &handledCols, const Record &row); // initialize the things which depend on the row void initRow(Vector &handledCols, const Record &row); // initialize the index void makeIndex(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/msfits/MSFits/SDPointingHandler.cc000066400000000000000000000242371476623553700216310ustar00rootroot00000000000000//# SDPointingHandler.cc: an POINTING handler for SDFITS data //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN SDPointingHandler::SDPointingHandler() : msPointing_p(0), msPointingCols_p(0), time_p(0.0), antId_p(-1), directionRate_p(2), name_p(""), rownr_p(-1) {;} SDPointingHandler::SDPointingHandler(MeasurementSet &ms, Vector &handledCols, const Record &row) : msPointing_p(0), msPointingCols_p(0), time_p(0.0), antId_p(-1),directionRate_p(2), name_p(""), rownr_p(-1) { initAll(ms, handledCols, row); } SDPointingHandler::SDPointingHandler(const SDPointingHandler &other) : msPointing_p(0), msPointingCols_p(0), time_p(0.0), antId_p(-1), directionRate_p(2), name_p(""), rownr_p(-1) { *this = other; } SDPointingHandler &SDPointingHandler::operator=(const SDPointingHandler &other) { if (this != &other) { clearAll(); msPointing_p = new MSPointing(*(other.msPointing_p)); AlwaysAssert(msPointing_p, AipsError); msPointingCols_p = new MSPointingColumns(*msPointing_p); AlwaysAssert(msPointingCols_p, AipsError); time_p = other.time_p; antId_p = other.antId_p; direction_p = other.direction_p; directionRate_p = other.directionRate_p; name_p = other.name_p; rownr_p = other.rownr_p; objectField_p = other.objectField_p; pointingDirRateField_p = other.pointingDirRateField_p; } return *this; } void SDPointingHandler::attach(MeasurementSet &ms, Vector &handledCols, const Record &row) { clearAll(); initAll(ms, handledCols, row); } void SDPointingHandler::fill(const Record &, Int antennaId, Double time, const Vector &timeRange, const MDirection &direction, const MeasFrame &frame) { // don't bother unless there is something there if (msPointing_p) { String name = ""; if (objectField_p.isAttached()) name = *objectField_p; Bool newRow = rownr_p < 0; newRow = newRow || name != name_p; newRow = newRow || antennaId != antId_p; newRow = newRow || direction.getRef() != direction_p.getRef() || direction.getValue() != direction_p.getValue(); if (!newRow && pointingDirRateField_p.isAttached()) { newRow = !allEQ(*pointingDirRateField_p, directionRate_p); } if (!newRow && nameField_p.isAttached()) { newRow = *nameField_p == msPointingCols_p->name()(rownr_p); } if (!newRow && trackingField_p.isAttached()) { newRow = *trackingField_p == msPointingCols_p->tracking()(rownr_p); } Double interval = timeRange(1) - timeRange(0); Double thisTime = time; // or should a former MS time and interval be used here instead if (timeField_p.isAttached()) { thisTime = *timeField_p; // MS interval can't exist without MS time if (intervalField_p.isAttached()) { interval = *intervalField_p; } } if (!newRow) { // all of the fields except TIME and INTERVAL match. // if the time falls within the row interval of the row time // or the row time falls within the interval of time, then the rows overlap and // can be reused Double rowTime = msPointingCols_p->time()(rownr_p); Double rowInterval = msPointingCols_p->interval()(rownr_p); Double rid2 = rowInterval/2.0; Double id2 = interval/2.0; newRow = !(((time-id2)<(rowTime+rid2)) && ((rowTime-rid2)<(time+id2))); } if (newRow) { // a new row is in order rownr_p = msPointing_p->nrow(); if (rownr_p == 0) { // set the column direction reference to the value of this direction dirColRef_p = direction_p.getRef(); msPointingCols_p->directionMeasCol().setDescRefCode(dirColRef_p.getType()); msPointingCols_p->targetMeasCol().setDescRefCode(dirColRef_p.getType()); } msPointing_p->addRow(); antId_p = antennaId; direction_p = direction; name_p = name; msPointingCols_p->antennaId().put(rownr_p, antId_p); msPointingCols_p->time().put(rownr_p, thisTime); time_p = thisTime; msPointingCols_p->interval().put(rownr_p, interval); if (nameField_p.isAttached()) { msPointingCols_p->name().put(rownr_p, *nameField_p); } else { msPointingCols_p->name().put(rownr_p, name_p); } msPointingCols_p->timeOrigin().put(rownr_p, 0.0); // direction is tricky Int npoly = 0; if (pointingDirRateField_p.isAttached()) { directionRate_p = *pointingDirRateField_p; // only add this if the rates here are non-zero AND non-inf AND not a NaN Double d0 = directionRate_p(0); Double d1 = directionRate_p(1); if (!near(d0,0.0) && !near(d1,0.0) && !isInf(d0) && !isInf(d1) && !isNaN(d0) && !isNaN(d1)) { npoly = 1; } } msPointingCols_p->numPoly().put(rownr_p, npoly); Vector dirs(npoly+1); dirs(0) = direction_p; if (npoly == 1) { // assumes the direction reference is the same as for dirs(0) dirs(1) = MDirection(Quantum >(*pointingDirRateField_p), direction_p.getRef()); } if (dirColRef_p != direction_p.getRef()) { MDirection::Ref mref(dirColRef_p); mref.set(frame); dirs(0) = MDirection::Convert(dirs(0), mref)(); // I'm not sure how the polynomial terms convert } msPointingCols_p->directionMeasCol().put(rownr_p, dirs); // reuse the direction here msPointingCols_p->targetMeasCol().put(rownr_p, dirs); if (trackingField_p.isAttached()) { msPointingCols_p->tracking().put(rownr_p, *trackingField_p); } else { // assume it was tracking msPointingCols_p->tracking().put(rownr_p, True); } // extraction the direction poly for use by the FIELD table as necessary directionPoly_p = msPointingCols_p->direction()(rownr_p); } else { // re-use this row, make sure that the time range is fully set // and place the time in the center of it Double rowTime = msPointingCols_p->time()(rownr_p); Double rowInterval = msPointingCols_p->interval()(rownr_p); Double minTime, maxTime; minTime = min(time-interval/2.0, rowTime-rowInterval/2.0); maxTime = max(time+interval/2.0, rowTime+rowInterval/2.0); time_p = (maxTime+minTime)/2.0; msPointingCols_p->time().put(rownr_p, time_p); msPointingCols_p->interval().put(rownr_p, (maxTime-minTime)); } } } void SDPointingHandler::clearAll() { delete msPointing_p; msPointing_p = 0; delete msPointingCols_p; msPointingCols_p = 0; clearRow(); } void SDPointingHandler::clearRow() { rownr_p = -1; objectField_p.detach(); pointingDirRateField_p.detach(); intervalField_p.detach(); timeField_p.detach(); nameField_p.detach(); trackingField_p.detach(); } void SDPointingHandler::initAll(MeasurementSet &ms, Vector &handledCols, const Record &row) { msPointing_p = new MSPointing(ms.pointing()); AlwaysAssert(msPointing_p, AipsError); msPointingCols_p = new MSPointingColumns(*msPointing_p); AlwaysAssert(msPointingCols_p, AipsError); antId_p = -1; direction_p = MDirection(); name_p = ""; initRow(handledCols, row); } void SDPointingHandler::initRow(Vector &handledCols, const Record &row) { rownr_p = -1; if (row.fieldNumber("OBJECT") >= 0) { objectField_p.attachToRecord(row, "OBJECT"); handledCols(row.fieldNumber("OBJECT")) = True; } if (row.fieldNumber("FIELD_POINTING_DIR_RATE") >= 0 && row.dataType("FIELD_POINTING_DIR_RATE") == TpArrayDouble) { pointingDirRateField_p.attachToRecord(row, "FIELD_POINTING_DIR_RATE"); handledCols(row.fieldNumber("FIELD_POINTING_DIR_RATE")) = True; } if (row.fieldNumber("POINTING_INTERVAL") >= 0 && row.dataType("POINTING_INTERVAL") == TpDouble) { intervalField_p.attachToRecord(row, "POINTING_INTERVAL"); handledCols(row.fieldNumber("POINTING_INTERVAL")) = True; } if (row.fieldNumber("POINTING_TIME") >= 0 && row.dataType("POINTING_TIME") == TpDouble) { timeField_p.attachToRecord(row, "POINTING_TIME"); handledCols(row.fieldNumber("POINTING_TIME")) = True; } if (row.fieldNumber("POINTING_NAME") >= 0 && row.dataType("POINTING_NAME") == TpString) { nameField_p.attachToRecord(row, "POINTING_NAME"); handledCols(row.fieldNumber("POINTING_NAME")) = True; } if (row.fieldNumber("POINTING_TRACKING") >= 0 && row.dataType("POINTING_TRACKING") == TpBool) { trackingField_p.attachToRecord(row, "POINTING_TRACKING"); handledCols(row.fieldNumber("POINTING_TRACKING")) = True; } } } //# NAMESPACE CASACORE - END casacore-3.7.1/msfits/MSFits/SDPointingHandler.h000066400000000000000000000116531476623553700214710ustar00rootroot00000000000000//# SDPointingFiller.h: fills the POINTING table for the SDFITS filler //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_SDPOINTINGHANDLER_H #define MS_SDPOINTINGHANDLER_H #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MeasurementSet; class MSPointing; class MSPointingColumns; class Record; // // // or // // // //
      • SomeClass //
      • SomeOtherClass //
      • some concept // // // // // // // // // // // // // // // //
      • //
      • // // // //
      • //
      • // // // //
      • add this feature //
      • fix this bug //
      • start discussion of this possible extension // class SDPointingHandler { public: // default ctor is not attached to a MS and hence is useless until attached SDPointingHandler(); // attach this to a MS, mark fields row which are handled here SDPointingHandler(MeasurementSet &ms, Vector &handledCols, const Record &row); // copy ctor SDPointingHandler(const SDPointingHandler &other); ~SDPointingHandler() {clearAll();} // assignment operator, uses copy semantics SDPointingHandler &operator=(const SDPointingHandler &other); // attach to a MS, mark fields in row which are handled here void attach(MeasurementSet &ms, Vector &handledCols, const Record &row); // reset internals given indicated row, use the same MS void resetRow(const Record &); // fill - a new row is added when // a) the name changes // b) the time changes such that it would be outside of the new interval when // added to the old interval (i.e. intervals do not overlap) // c) the direction changes // d) the antennaId changes // There is no look-back to see if a previous row could be re-used void fill(const Record &row, Int antennaId, Double time, const Vector &timeRange, const MDirection &direction, const MeasFrame &frame); // convenience functions for use when filling the FIELD table, which is mostly // just a clone of this table for SD data Int nrow() {return rownr_p+1;} const String &name() {return name_p;} Int directionRefType() {return dirColRef_p.getType();} const Matrix &directionPoly() {return directionPoly_p;} Double time() {return time_p;} private: MSPointing *msPointing_p; MSPointingColumns *msPointingCols_p; Double time_p; Int antId_p; MDirection direction_p; Matrix directionPoly_p; Vector directionRate_p; String name_p; Int rownr_p; MDirection::Ref dirColRef_p; RORecordFieldPtr objectField_p; // these might come from an MS table // this can just come from an MS v1 table RORecordFieldPtr > pointingDirRateField_p; RORecordFieldPtr intervalField_p, timeField_p; RORecordFieldPtr nameField_p; RORecordFieldPtr trackingField_p; // cleanup everything void clearAll(); // cleanup things which depend on the row void clearRow(); // initialize everything void initAll(MeasurementSet &ms, Vector &handledCols, const Record &row); // initialize everythign which depends on row void initRow(Vector &handledCols, const Record &row); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/msfits/MSFits/SDPolarizationHandler.cc000066400000000000000000000225031476623553700225070ustar00rootroot00000000000000//# SDPolarizationHandler.cc: an POLARIZATION handler for SDFITS data //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN SDPolarizationHandler::SDPolarizationHandler() : index_p(0), msPol_p(0), msPolCols_p(0), rownr_p(-1) {;} SDPolarizationHandler::SDPolarizationHandler(MeasurementSet &ms, Vector &handledCols, const Record &row) : index_p(0), msPol_p(0), msPolCols_p(0), rownr_p(-1) { initAll(ms, handledCols, row); } SDPolarizationHandler::SDPolarizationHandler(const SDPolarizationHandler &other) : index_p(0), msPol_p(0), msPolCols_p(0), rownr_p(-1) { *this = other; } SDPolarizationHandler &SDPolarizationHandler::operator=(const SDPolarizationHandler &other) { if (this != &other) { clearAll(); index_p = new ColumnsIndex(*(other.index_p)); AlwaysAssert(index_p, AipsError); // need to avoid the assignment operator here because we want // this to point to the field in index_p, not in other.index_p numCorrKey_p.attachToRecord(index_p->accessKey(), MSPolarization::columnName(MSPolarization::NUM_CORR)); msPol_p = new MSPolarization(*(other.msPol_p)); AlwaysAssert(msPol_p, AipsError); msPolCols_p = new MSPolarizationColumns(*msPol_p); AlwaysAssert(msPolCols_p, AipsError); rownr_p = other.rownr_p; numCorrField_p = other.numCorrField_p; corrTypeField_p = other.corrTypeField_p; corrProductField_p = other.corrProductField_p; flagRowField_p = other.flagRowField_p; } return *this; } void SDPolarizationHandler::attach(MeasurementSet &ms, Vector &handledCols, const Record &row) { clearAll(); initAll(ms, handledCols, row); } void SDPolarizationHandler::resetRow(const Record &row) { clearRow(); Vector dummyCols(row.nfields()); initRow(dummyCols, row); } void SDPolarizationHandler::fill(const Record &, const Vector &stokes) { // don't bother unless there is something there if (msPol_p) { *numCorrKey_p = stokes.nelements(); Bool found = False; Vector foundRows = index_p->getRowNumbers(); uInt whichOne = 0; while (!found && whichOnecorrType()(foundRows(whichOne))) && (!flagRowField_p.isAttached() || *flagRowField_p == msPolCols_p->flagRow()(foundRows(whichOne)))) { // we have a winner found = True; } else { whichOne++; } } if (found) { rownr_p = foundRows(whichOne); } else { // we need to add one rownr_p = msPol_p->nrow(); msPol_p->addRow(); msPolCols_p->numCorr().put(rownr_p, *numCorrKey_p); msPolCols_p->corrType().put(rownr_p, stokes); Matrix corrProduct(2,*numCorrKey_p); // can we reuse whats alread in the row from when this was a MS if (numCorrField_p.isAttached() && *numCorrField_p == *numCorrKey_p && corrTypeField_p.isAttached() && allEQ(*corrTypeField_p, stokes) && corrProductField_p.isAttached()) { // apparently so corrProduct = *corrProductField_p; } else { // construct the corrProduct given the stokes values // first, we need to determine the decomposition of the stokes values std::map polTypeMap; for (uInt i=0;icorrProduct().put(rownr_p, corrProduct); if (flagRowField_p.isAttached()) { msPolCols_p->flagRow().put(rownr_p, *flagRowField_p); } else { msPolCols_p->flagRow().put(rownr_p, False); } } } } void SDPolarizationHandler::clearAll() { delete index_p; index_p = 0; delete msPol_p; msPol_p = 0; delete msPolCols_p; msPolCols_p = 0; clearRow(); } void SDPolarizationHandler::clearRow() { rownr_p = -1; numCorrField_p.detach(); corrTypeField_p.detach(); corrProductField_p.detach(); } void SDPolarizationHandler::initAll(MeasurementSet &ms, Vector &handledCols, const Record &row) { msPol_p = new MSPolarization(ms.polarization()); AlwaysAssert(msPol_p, AipsError); msPolCols_p = new MSPolarizationColumns(*msPol_p); AlwaysAssert(msPolCols_p, AipsError); index_p = new ColumnsIndex(*msPol_p, MSPolarization::columnName(MSPolarization::NUM_CORR)); AlwaysAssert(index_p, AipsError); numCorrKey_p.attachToRecord(index_p->accessKey(), MSPolarization::columnName(MSPolarization::NUM_CORR)); initRow(handledCols, row); } void SDPolarizationHandler::initRow(Vector &handledCols, const Record &row) { rownr_p = -1; // try MS 2 version first, then MS 1 Int ncorrId = row.fieldNumber("POLARIZATION_NUM_CORR"); if (ncorrId < 0) ncorrId = row.fieldNumber("SPECTRAL_WINDOW_NUM_CORR"); if (ncorrId >= 0) { numCorrField_p.attachToRecord(row, ncorrId); handledCols(ncorrId) = True; } Int corrTypeId = row.fieldNumber("POLARIZATION_CORR_TYPE"); if (corrTypeId < 0) corrTypeId = row.fieldNumber("SPECTRAL_WINDOW_CORR_TYPE"); if (corrTypeId >= 0) { corrTypeField_p.attachToRecord(row, corrTypeId); handledCols(corrTypeId) = True; } Int corrProductId = row.fieldNumber("POLARIZATION_CORR_PRODUCT"); if (corrProductId < 0) corrProductId = row.fieldNumber("SPECTRAL_WINDOW_CORR_PRODUCT"); if (corrProductId >= 0) { corrProductField_p.attachToRecord(row, corrProductId); handledCols(corrProductId) = True; } Int flagRowId = row.fieldNumber("POLARIZATION_FLAG_ROW"); if (flagRowId >= 0) { flagRowField_p.attachToRecord(row, flagRowId); handledCols(flagRowId) = True; } } void SDPolarizationHandler::stokesKeys(Int stokesValue, Int &key1, Int &key2) { switch (Stokes::type(stokesValue)) { // the cases which are tricky are the cross products // we need to set the keys to something which remembers // that XX is a product of just a receptors while // XY and YX are procucts of the same two receptors and one // of theme is also the one used in XX. So, use the non-cross // procucts to be synonymous with their receptors when deconstructing // the cross product values. case Stokes::RL: key1 = Stokes::RR; key2 = Stokes::LL; break; case Stokes::LR: key1 = Stokes::LL; key2 = Stokes::RR; break; case Stokes::XY: key1 = Stokes::XX; key2 = Stokes::YY; break; case Stokes::YX: key1 = Stokes::YY; key2 = Stokes::XX; break; case Stokes::RX: key1 = Stokes::RR; key2 = Stokes::XX; break; case Stokes::RY: key1 = Stokes::RR; key2 = Stokes::YY; break; case Stokes::LX: key1 = Stokes::LL; key2 = Stokes::XX; break; case Stokes::LY: key1 = Stokes::LL; key2 = Stokes::YY; break; case Stokes::XR: key1 = Stokes::XX; key2 = Stokes::RR; break; case Stokes::YR: key1 = Stokes::YY; key2 = Stokes::RR; break; case Stokes::XL: key1 = Stokes::XX; key2 = Stokes::LL; break; case Stokes::YL: key1 = Stokes::YY; key2 = Stokes::LL; break; case Stokes::PQ: key1 = Stokes::PP; key2 = Stokes::QQ; break; case Stokes::QP: key1 = Stokes::QQ; key2 = Stokes::PP; break; default: // the two keys are identical to each other and to the stokes type key1 = key2 = stokesValue; break; } } } //# NAMESPACE CASACORE - END casacore-3.7.1/msfits/MSFits/SDPolarizationHandler.h000066400000000000000000000100611476623553700223450ustar00rootroot00000000000000//# SDPolarizationFiller.h: fills the POLARIZATION table for the SDFITS filler //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_SDPOLARIZATIONHANDLER_H #define MS_SDPOLARIZATIONHANDLER_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class ColumnsIndex; class MeasurementSet; class MSPolarization; class MSPolarizationColumns; class Record; // // // or // // // //
      • SomeClass //
      • SomeOtherClass //
      • some concept // // // // // // // // // // // // // // // //
      • //
      • // // // //
      • //
      • // // // //
      • add this feature //
      • fix this bug //
      • start discussion of this possible extension // class SDPolarizationHandler { public: // default ctor is not attached to a MS and hence is useless until attached SDPolarizationHandler(); // attach this to a MS - no columns are explicitly handled here SDPolarizationHandler(MeasurementSet &ms, Vector &handledCols, const Record &row); // copy ctor SDPolarizationHandler(const SDPolarizationHandler &other); ~SDPolarizationHandler() {clearAll();} // assignment operator, uses copy semantics SDPolarizationHandler &operator=(const SDPolarizationHandler &other); // attach to a MS, the handledCols and row arguments are ignored here void attach(MeasurementSet &ms, Vector &handledCols, const Record &row); // reset internals given indicated row, use the same MS; just resets the id pointer void resetRow(const Record &row); // fill - a new row is added only when necessary void fill(const Record &row, const Vector &stokes); // get the current polarization ID Int polarizationId() {return rownr_p;} private: RecordFieldPtr numCorrKey_p; ColumnsIndex *index_p; MSPolarization *msPol_p; MSPolarizationColumns *msPolCols_p; Int rownr_p; // from a pre-existing MS RORecordFieldPtr numCorrField_p; RORecordFieldPtr > corrTypeField_p, corrProductField_p; RORecordFieldPtr flagRowField_p; // decompose a stokes value into constituent parts for use // in making the CORR_PRODUCT matrix void stokesKeys(Int stokesValue, Int &key1, Int &key2); // cleanup everything void clearAll(); void clearRow(); // initialize everything void initAll(MeasurementSet &ms, Vector &handledCols, const Record &row); void initRow(Vector &handledCols, const Record &row); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/msfits/MSFits/SDSourceHandler.cc000066400000000000000000000374041476623553700213020ustar00rootroot00000000000000//# SDSourceFiller.cc: an SOURCE filler for SDFITS data //# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN SDSourceHandler::SDSourceHandler() : index_p(0), msSource_p(0), msSourceCols_p(0), sourceId_p(-1), nextSourceId_p(0), restfreq_p(-1), vframe_p(-1), hasTransition_p(False), hasRestFreq_p(False), hasSysVel_p(False), hasPosition_p(False) {;} SDSourceHandler::SDSourceHandler(MeasurementSet &ms, Vector &handledCols, const Record &row) : index_p(0), msSource_p(0), msSourceCols_p(0), sourceId_p(-1), nextSourceId_p(0), restfreq_p(-1), vframe_p(-1), hasTransition_p(False), hasRestFreq_p(False), hasSysVel_p(False), hasPosition_p(False) { initAll(ms, handledCols, row); } SDSourceHandler::SDSourceHandler(const SDSourceHandler &other) : index_p(0), msSource_p(0), msSourceCols_p(0), sourceId_p(-1), nextSourceId_p(0), restfreq_p(-1), vframe_p(-1), hasTransition_p(False), hasRestFreq_p(False), hasSysVel_p(False), hasPosition_p(False) { *this = other; } SDSourceHandler &SDSourceHandler::operator=(const SDSourceHandler &other) { if (this != &other) { clearAll(); index_p = new ColumnsIndex(*(other.index_p)); AlwaysAssert(index_p, AipsError); // need to avoid the assignment operator here because we want // this to point to the field in index_p, not in other.index_p nameKey_p.attachToRecord(index_p->accessKey(), MSSource::columnName(MSSource::NAME)); codeKey_p.attachToRecord(index_p->accessKey(), MSSource::columnName(MSSource::CODE)); msSource_p = new MSSource(*(other.msSource_p)); AlwaysAssert(msSource_p, AipsError); msSourceCols_p = new MSSourceColumns(*msSource_p); AlwaysAssert(msSourceCols_p, AipsError); sourceId_p = other.sourceId_p; nextSourceId_p = other.nextSourceId_p; // this should point to the same field as that in other restfreq_p = other.restfreq_p; vframe_p = other.vframe_p; transiti_p = other.transiti_p; object_p = other.object_p; obsmode_p = other.obsmode_p; hasTransition_p = other.hasTransition_p; hasRestFreq_p = other.hasRestFreq_p; hasSysVel_p = other.hasSysVel_p; hasPosition_p = other.hasPosition_p; calibrationGroupField_p = other.calibrationGroupField_p; pulsarIdField_p = other.pulsarIdField_p; timeField_p = other.timeField_p; intervalField_p = other.intervalField_p; directionField_p = other.directionField_p; positionField_p = other.positionField_p; properMotionField_p = other.properMotionField_p; } return *this; } void SDSourceHandler::attach(MeasurementSet &ms, Vector &handledCols, const Record &row) { clearAll(); initAll(ms, handledCols, row); } void SDSourceHandler::resetRow(const Record &row) { clearRow(); Vector dummyHandled; initRow(dummyHandled, row); } void SDSourceHandler::fill(const Record &row, Int spectralWindowId) { // don't bother unless there is something there if (msSource_p) { if (object_p.isAttached()) { *nameKey_p = *object_p; } else { *nameKey_p = ""; } if (obsmode_p.isAttached()) { *codeKey_p = *obsmode_p; } else { *codeKey_p = ""; } uInt rownr = 0; Vector foundRows = index_p->getRowNumbers(); Bool rowFound, sourceFound; rowFound = sourceFound = False; String transition = ""; if (transiti_p.isAttached()) transition = *transiti_p; if (molecule_p.isAttached()) { // always add the "," delimiter so that the molecule string can be recovered on the end // assumes that the molecule string has no commas in it. transition += ", "; transition += *molecule_p; } Double restfreq = 0.0; if (restfreq_p >= 0) restfreq = row.asDouble(restfreq_p); Double sysvel = 0.0; if (vframe_p >= 0) { sysvel = row.asDouble(vframe_p); } if (foundRows.nelements() > 0) { // we have at least 1 candidate, look for a matching spectral window ID uInt whichOne = 0; while (!rowFound && whichOnecalibrationGroup()(rownr) == *calibrationGroupField_p; } if (rowFound && timeField_p.isAttached()) { rowFound = msSourceCols_p->time()(rownr) == *timeField_p; } if (rowFound && intervalField_p.isAttached()) { rowFound = msSourceCols_p->interval()(rownr) == *intervalField_p; } if (rowFound && directionField_p.isAttached()) { rowFound = allEQ(msSourceCols_p->direction()(rownr),*directionField_p); } if (rowFound && properMotionField_p.isAttached()) { rowFound = allEQ(msSourceCols_p->properMotion()(rownr),*properMotionField_p); } if (rowFound && hasSysVel_p) { rowFound = allEQ(msSourceCols_p->sysvel()(rownr), sysvel); } if (rowFound && hasPosition_p) { rowFound = allEQ(msSourceCols_p->position()(rownr), *positionField_p); } if (rowFound && pulsarIdField_p.isAttached()) { if (msSourceCols_p->pulsarId().isNull()) rowFound = !(*pulsarIdField_p>=0); else rowFound = msSourceCols_p->pulsarId()(rownr) == *pulsarIdField_p; } // if we're here, the source ID is probably okay if (rowFound) { sourceFound = True; sourceId_p = msSourceCols_p->sourceId()(rownr); // we still might not have the right row, though rowFound = spectralWindowId == msSourceCols_p->spectralWindowId()(foundRows(whichOne)); if (rowFound && hasTransition_p) { rowFound = allEQ(msSourceCols_p->transition()(rownr), transition); } if (rowFound && hasRestFreq_p) { rowFound = allEQ(msSourceCols_p->restFrequency()(rownr), restfreq); } } if (!rowFound) whichOne++; if (rowFound && hasSysVel_p) { rowFound = allEQ(msSourceCols_p->sysvel()(rownr), sysvel); } if (rowFound && hasPosition_p) { rowFound = allEQ(msSourceCols_p->position()(rownr), *positionField_p); } if (rowFound && pulsarIdField_p.isAttached()) { if (msSourceCols_p->pulsarId().isNull()) rowFound = !(*pulsarIdField_p>=0); else rowFound = msSourceCols_p->pulsarId()(rownr) == *pulsarIdField_p; } // if we're here, the source ID is probably okay if (rowFound) { sourceFound = True; sourceId_p = msSourceCols_p->sourceId()(rownr); // we still might not have the right row, though rowFound = spectralWindowId == msSourceCols_p->spectralWindowId()(foundRows(whichOne)); if (rowFound && hasTransition_p) { rowFound = allEQ(msSourceCols_p->transition()(rownr), transition); } if (rowFound && hasRestFreq_p) { rowFound = allEQ(msSourceCols_p->restFrequency()(rownr), restfreq); } } if (!rowFound) whichOne++; } } if (!rowFound) { // we need to add one rownr = msSource_p->nrow(); msSource_p->addRow(); if (!sourceFound) sourceId_p = nextSourceId_p++; msSourceCols_p->sourceId().put(rownr,sourceId_p); if (timeField_p.isAttached()) { msSourceCols_p->time().put(rownr,*timeField_p); } else { msSourceCols_p->time().put(rownr,0.0); } if (intervalField_p.isAttached()) { msSourceCols_p->interval().put(rownr,*intervalField_p); } else { msSourceCols_p->interval().put(rownr,0.0); } msSourceCols_p->spectralWindowId().put(rownr, spectralWindowId); if (hasRestFreq_p || hasTransition_p || hasSysVel_p) { msSourceCols_p->numLines().put(rownr, 1); } else { msSourceCols_p->numLines().put(rownr, 0); } if (hasTransition_p) { msSourceCols_p->transition().put(rownr,Vector(1,transition)); } if (hasRestFreq_p) { msSourceCols_p->restFrequency().put(rownr,Vector(1,restfreq)); } if (hasSysVel_p) { msSourceCols_p->sysvel().put(rownr,Vector(1,sysvel)); } if (hasPosition_p) { msSourceCols_p->position().put(rownr,*positionField_p); } String name = ""; if (object_p.isAttached()) name = *object_p; msSourceCols_p->name().put(rownr,name); if (calibrationGroupField_p.isAttached()) { msSourceCols_p->calibrationGroup().put(rownr, *calibrationGroupField_p); } else { msSourceCols_p->calibrationGroup().put(rownr,-1); } String code = ""; if (obsmode_p.isAttached()) code = *obsmode_p; msSourceCols_p->code().put(rownr,code); if (directionField_p.isAttached()) { msSourceCols_p->direction().put(rownr,*directionField_p); } else { msSourceCols_p->direction().put(rownr,Vector(2,0.0)); } if (properMotionField_p.isAttached()) { msSourceCols_p->properMotion().put(rownr,*properMotionField_p); } else { msSourceCols_p->properMotion().put(rownr,Vector(2,0.0)); } if (pulsarIdField_p.isAttached()) { if (*pulsarIdField_p >= 0) { if (msSourceCols_p->pulsarId().isNull()) { // add this column delete msSourceCols_p; msSourceCols_p = 0; TableDesc td; MSSource::addColumnToDesc(td, MSSource::PULSAR_ID); msSource_p->addColumn(td[0]); msSourceCols_p = new MSSourceColumns(*msSource_p); AlwaysAssert(msSourceCols_p, AipsError); msSourceCols_p->pulsarId().put(rownr, *pulsarIdField_p); } else { msSourceCols_p->pulsarId().put(rownr, *pulsarIdField_p); } } } // transition, rest_frequency, sysvel are inserted outside this loop } else { // set the source ID to what was actually found sourceId_p = msSourceCols_p->sourceId()(rownr); } } } void SDSourceHandler::clearAll() { delete index_p; index_p = 0; delete msSource_p; msSource_p = 0; delete msSourceCols_p; msSourceCols_p = 0; sourceId_p = -1; nextSourceId_p = 0; clearRow(); } void SDSourceHandler::clearRow() { transiti_p.detach(); molecule_p.detach(); object_p.detach(); obsmode_p.detach(); restfreq_p = vframe_p = -1; hasTransition_p = hasRestFreq_p = hasSysVel_p = hasPosition_p = False; calibrationGroupField_p.detach(); pulsarIdField_p.detach(); timeField_p.detach(); intervalField_p.detach(); directionField_p.detach(); positionField_p.detach(); properMotionField_p.detach(); } void SDSourceHandler::initAll(MeasurementSet &ms, Vector &handledCols, const Record &row) { msSource_p = new MSSource(ms.source()); AlwaysAssert(msSource_p, AipsError); // things in row might trigger the need for optional columns initRow(handledCols, row); TableDesc td; if (restfreq_p >= 0) { MSSource::addColumnToDesc(td,MSSource::REST_FREQUENCY); hasRestFreq_p = True; } if (vframe_p >= 0) { MSSource::addColumnToDesc(td,MSSource::SYSVEL); hasSysVel_p = True; } if (transiti_p.isAttached() || molecule_p.isAttached()) { MSSource::addColumnToDesc(td, MSSource::TRANSITION); hasTransition_p = True; } if (positionField_p.isAttached()) { MSSource::addColumnToDesc(td,MSSource::POSITION); hasPosition_p = True; } // and add these columns in, if there any for (uInt i=0;iaddColumn(td[i],"StandardStMan", False); } msSourceCols_p = new MSSourceColumns(*msSource_p); AlwaysAssert(msSourceCols_p, AipsError); Vector indexCols(2); indexCols(0) = MSSource::columnName(MSSource::NAME); indexCols(1) = MSSource::columnName(MSSource::CODE); index_p = new ColumnsIndex(*msSource_p, indexCols); AlwaysAssert(index_p, AipsError); nameKey_p.attachToRecord(index_p->accessKey(), MSSource::columnName(MSSource::NAME)); codeKey_p.attachToRecord(index_p->accessKey(), MSSource::columnName(MSSource::CODE)); sourceId_p = -1; nextSourceId_p = 0; } void SDSourceHandler::initRow(Vector &handledCols, const Record &row) { AlwaysAssert(handledCols.nelements()==row.description().nfields(), AipsError); restfreq_p = row.fieldNumber("RESTFREQ"); if (restfreq_p >= 0) handledCols(restfreq_p) = True; // VFRAME == SYSVEL vframe_p = row.fieldNumber("VFRAME"); if (vframe_p >= 0) handledCols(vframe_p) = True; Int tmp = row.fieldNumber("TRANSITI"); if (tmp >= 0) { transiti_p.attachToRecord(row, tmp); handledCols(tmp) = True; } tmp = row.fieldNumber("MOLECULE"); if (tmp >= 0) { molecule_p.attachToRecord(row, tmp); handledCols(tmp) = True; } tmp = row.fieldNumber("OBJECT"); if (tmp >= 0) { object_p.attachToRecord(row, tmp); handledCols(tmp) = True; } tmp = row.fieldNumber("OBSMODE"); if (tmp >= 0) { obsmode_p.attachToRecord(row, tmp); handledCols(tmp) = True; } tmp = row.fieldNumber("SOURCE_CALIBRATION_GROUP"); if (tmp >= 0 && row.dataType(tmp) == TpInt) { calibrationGroupField_p.attachToRecord(row, tmp); handledCols(tmp) = True; } tmp = row.fieldNumber("SOURCE_DIRECTION"); if (tmp >= 0 && row.dataType(tmp) == TpArrayDouble) { directionField_p.attachToRecord(row, tmp); handledCols(tmp) = True; } tmp = row.fieldNumber("SOURCE_INTERVAL"); if (tmp >= 0 && row.dataType(tmp) == TpDouble) { intervalField_p.attachToRecord(row, tmp); handledCols(tmp) = True; } tmp = row.fieldNumber("SOURCE_POSITION"); if (tmp >= 0 && row.dataType(tmp) == TpArrayDouble) { positionField_p.attachToRecord(row, tmp); handledCols(tmp) = True; } tmp = row.fieldNumber("SOURCE_PROPER_MOTION"); if (tmp >= 0 && row.dataType(tmp) == TpArrayDouble) { properMotionField_p.attachToRecord(row, tmp); handledCols(tmp) = True; } tmp = row.fieldNumber("SOURCE_TIME"); if (tmp >= 0 && row.dataType(tmp) == TpDouble) { timeField_p.attachToRecord(row, tmp); handledCols(tmp) = True; } tmp = row.fieldNumber("MAIN_PULSAR_ID"); if (tmp >= 0 && row.dataType(tmp) == TpInt) { pulsarIdField_p.attachToRecord(row, tmp); handledCols(tmp) = True; } // these are ignored - they were produced via ms2sdfits with MS 1 and they contain // duplicate information found in other standard SDFITS columns tmp = row.fieldNumber("SOURCE_SYSVEL"); if (tmp >= 0) handledCols(tmp) = True; tmp = row.fieldNumber("SPECTRAL_WINDOW_REST_FREQUENCY"); if (tmp >= 0) handledCols(tmp) = True; } } //# NAMESPACE CASACORE - END casacore-3.7.1/msfits/MSFits/SDSourceHandler.h000066400000000000000000000106471476623553700211440ustar00rootroot00000000000000//# SDSourceFiller.h: fills the SOURCE table for the SDFITS filler //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_SDSOURCEHANDLER_H #define MS_SDSOURCEHANDLER_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class ColumnsIndex; class MeasurementSet; class MSSource; class MSSourceColumns; class Record; // // // or // // // //
      • SomeClass //
      • SomeOtherClass //
      • some concept // // // // // // // // // // // // // // // //
      • //
      • // // // //
      • //
      • // // // //
      • add this feature //
      • fix this bug //
      • start discussion of this possible extension // class SDSourceHandler { public: // default ctor is not attached to a MS and hence is useless until attached SDSourceHandler(); // attach this to a MS, marking fields in row which are explicitly handled here SDSourceHandler(MeasurementSet &ms, Vector &handledCols, const Record &row); // copy ctor SDSourceHandler(const SDSourceHandler &other); ~SDSourceHandler() {clearAll();} // assignment operator, uses copy semantics SDSourceHandler &operator=(const SDSourceHandler &other); // attach to a MS, the handledCols and row arguments are ignored here void attach(MeasurementSet &ms, Vector &handledCols, const Record &row); // reset internals given indicated row, use the same MS; just resets the id pointer void resetRow(const Record &); // fill - a source is unique in source name and code void fill(const Record &row, Int spectralWindowId); // get the current source ID Int sourceId() {return sourceId_p;} private: RecordFieldPtr nameKey_p, codeKey_p; ColumnsIndex *index_p; MSSource *msSource_p; MSSourceColumns *msSourceCols_p; // the current source ID Int sourceId_p; // the next source ID to use Int nextSourceId_p; // fields possibly mined from the SDFITS row // floating point fields that we can't be certain of their type Int restfreq_p, vframe_p; // String fields RORecordFieldPtr transiti_p, molecule_p, object_p, obsmode_p; // which optional colums exist Bool hasTransition_p, hasRestFreq_p, hasSysVel_p, hasPosition_p; // fields which might come from a pre-existin MS RORecordFieldPtr calibrationGroupField_p, pulsarIdField_p; RORecordFieldPtr timeField_p, intervalField_p; RORecordFieldPtr > directionField_p, positionField_p, properMotionField_p; // cleanup everything void clearAll(); // clean up items related to the row void clearRow(); // initialize everything void initAll(MeasurementSet &ms, Vector &handledCols, const Record &row); // initialize the stuff dependent on the row void initRow(Vector &handledCols, const Record &row); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/msfits/MSFits/SDSpWinHandler.cc000066400000000000000000000407411476623553700211000ustar00rootroot00000000000000//# SDSpWindowFiller.cc: an SPECTRAL_WINDOW filler for SDFITS data //# Copyright (C) 2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN SDSpWindowHandler::SDSpWindowHandler() : fNCachePtr_p(0), f0CachePtr_p(0), bwCachePtr_p(0), index_p(0), theCache_p(0), msSpWin_p(0), msSpWinCols_p(0), nextCacheRow_p(0), cacheSize_p(1000), rownr_p(-1), bandwidField_p(-1), freqresField_p(-1) {;} SDSpWindowHandler::SDSpWindowHandler(MeasurementSet &ms, Vector &handledCols, const Record &row) : fNCachePtr_p(0), f0CachePtr_p(0), bwCachePtr_p(0), index_p(0), theCache_p(0), msSpWin_p(0), msSpWinCols_p(0), nextCacheRow_p(0), cacheSize_p(1000), rownr_p(-1), bandwidField_p(-1), freqresField_p(-1) { initAll(ms, handledCols, row); } SDSpWindowHandler::SDSpWindowHandler(const SDSpWindowHandler &other) : fNCachePtr_p(0), f0CachePtr_p(0), bwCachePtr_p(0), index_p(0), theCache_p(0), msSpWin_p(0), msSpWinCols_p(0), nextCacheRow_p(0), cacheSize_p(1000), rownr_p(-1), bandwidField_p(-1), freqresField_p(-1) { *this = other; } SDSpWindowHandler &SDSpWindowHandler::operator=(const SDSpWindowHandler &other) { if (this != &other) { clearAll(); index_p = new ColumnsIndex(*(other.index_p)); AlwaysAssert(index_p, AipsError); theCache_p = new Table(*(other.theCache_p)); AlwaysAssert(theCache_p, AipsError); fNCache_p.resize(other.fNCache_p.nelements()); fNCache_p = other.fNCache_p; fNCachePtr_p = fNCache_p.getStorage(deleteItFN_p); f0Cache_p.resize(other.f0Cache_p.nelements()); f0Cache_p = other.f0Cache_p; f0CachePtr_p = f0Cache_p.getStorage(deleteItF0_p); bwCache_p.resize(other.bwCache_p.nelements()); bwCache_p = other.bwCache_p; bwCachePtr_p = bwCache_p.getStorage(deleteItBw_p); // need to avoid the assignment operator here because we want // this to point to the field in index_p, not in other.index_p nchanKey_p.attachToRecord(index_p->accessKey(), "NCHAN"); freqRefTypeKey_p.attachToRecord(index_p->accessKey(), "FREQREFTYPE"); ifConvChainKey_p.attachToRecord(index_p->accessKey(), "IF_CONV_CHAIN"); freqGroupKey_p.attachToRecord(index_p->accessKey(), "FREQ_GROUP"); netSidebandKey_p.attachToRecord(index_p->accessKey(), "NET_SIDEBAND"); flagRowKey_p.attachToRecord(index_p->accessKey(), "FLAG_ROW"); // reattach the table pointers as well idCol_p.attach(*theCache_p, "ID"); nchanCol_p.attach(*theCache_p, "NCHAN"); freqRefTypeCol_p.attach(*theCache_p, "FREQREFTYPE"); ifConvChainCol_p.attach(*theCache_p, "IF_CONV_CHAIN"); freqGroupCol_p.attach(*theCache_p, "FREQ_GROUP"); netSidebandCol_p.attach(*theCache_p, "NET_SIDEBAND"); flagRowCol_p.attach(*theCache_p, "FLAG_ROW"); msSpWin_p = new MSSpectralWindow(*(other.msSpWin_p)); AlwaysAssert(msSpWin_p, AipsError); msSpWinCols_p = new MSSpWindowColumns(*msSpWin_p); AlwaysAssert(msSpWinCols_p, AipsError); nextCacheRow_p = other.nextCacheRow_p; cacheSize_p = other.cacheSize_p; rownr_p = other.rownr_p; spWinIdField_p = other.spWinIdField_p; ifConvChainField_p = other.ifConvChainField_p; freqGroupField_p = other.freqGroupField_p; netSidebandField_p = other.netSidebandField_p; flagRowField_p = other.flagRowField_p; } return *this; } void SDSpWindowHandler::attach(MeasurementSet &ms, Vector &handledCols, const Record &row) { clearAll(); initAll(ms, handledCols, row); } void SDSpWindowHandler::resetRow(const Record &row) { clearRow(); Vector dummyHandled; initRow(dummyHandled, row); } void SDSpWindowHandler::fill(const Record &row, const Vector &frequency, Double refFrequency, Double originalFreqDelt, Int freqRefType) { // don't bother unless there is something there if (msSpWin_p) { // this is arbitrary we have match if things are within this fraction of a channel Double chanTol = 0.001; *nchanKey_p = frequency.nelements(); *freqRefTypeKey_p = freqRefType; Double thisFN, thisF0, thisBW; thisFN = thisF0 = thisBW = 0.0; if (bandwidField_p >= 0) { thisBW = row.asDouble(bandwidField_p); } if (*nchanKey_p > 0) { thisF0 = frequency(0); if (*nchanKey_p > 1) { thisFN = frequency(*nchanKey_p-1); if (thisBW == 0.0) thisBW = abs(thisFN - thisF0); } } Double thisFreqRes = 0.0; if (freqresField_p >= 0) { thisFreqRes = row.asDouble(freqresField_p); } if (ifConvChainField_p.isAttached()) { *ifConvChainKey_p = *ifConvChainField_p; } else { *ifConvChainKey_p = -1; } if (freqGroupField_p.isAttached()) { *freqGroupKey_p = *freqGroupField_p; } else { *freqGroupKey_p = -1; } if (netSidebandField_p.isAttached()) { *netSidebandKey_p = *netSidebandField_p; } else { *netSidebandKey_p = -1; } Bool found = False; // find any potential matches Vector cacheRows = index_p->getRowNumbers(); if (cacheRows.nelements()>0) { // do the fN, f0, and bw also match const rownr_t *rowPtr; Bool deleteItRows; rowPtr = cacheRows.getStorage(deleteItRows); uInt i = 0; while (i chWidth(nchan); if (nchan > 2) { chWidth = frequency; chWidth(Slice(1,(nchan-2))) = 0.5 * (chWidth(Slice(2,(nchan-2))) - chWidth(Slice(0,(nchan-2)))); } if (nchan > 1) { chWidth(nchan-1) = frequency(nchan-1)-frequency(nchan-2); } if (nchan > 0) { chWidth(0) = frequency(1) - frequency(0); } chWidth = abs(chWidth); Vector freqres(nchan); if (freqresField_p >= 0) { freqres = thisFreqRes; } else { // just reuse the computed channel widths freqres = chWidth; } // one last try, if this is from a MS, try the indicated row in the main table if (spWinIdField_p.isAttached() && *spWinIdField_p >= 0 && uInt(*spWinIdField_p) < msSpWin_p->nrow()) { Int rownr = *spWinIdField_p; found = msSpWinCols_p->numChan()(rownr) == nchan; found = found && msSpWinCols_p->refFrequency()(rownr) == refFrequency; // for SDFITS, these test should be sufficient - i.e. only necessary to look // around the first and last channels, not all of them if (found) { IPosition beg(msSpWinCols_p->chanFreq()(rownr).shape()), end; end = beg-1; beg = 0; if (nchan > 1) { Double shift = abs((msSpWinCols_p->chanFreq()(rownr)(beg)-thisF0)/chWidth(0)); if (nchan > 2) { shift = max(shift, abs((msSpWinCols_p->chanFreq()(rownr)(end)-thisFN)/chWidth(nchan-1))); } found = shift < chanTol; } else if (nchan == 1) { found = near(msSpWinCols_p->chanFreq()(rownr)(beg),thisF0); } } // if it passed the frequency test, there should be no need to check the channel // widths since they have the same nchan and the same first an last channel value // the widths should be the same found = found && msSpWinCols_p->measFreqRef()(rownr) == freqRefType; found = found && near(msSpWinCols_p->totalBandwidth()(rownr), thisBW); found = found && msSpWinCols_p->ifConvChain()(rownr) == *ifConvChainKey_p; found = found && msSpWinCols_p->freqGroup()(rownr) == *freqGroupKey_p; found = found && msSpWinCols_p->netSideband()(rownr) == *netSidebandKey_p; found = found && msSpWinCols_p->flagRow()(rownr) == *flagRowKey_p; if (found) rownr_p = rownr; } if (!found) { // we need to add one rownr_p = msSpWin_p->nrow(); msSpWin_p->addRow(); msSpWinCols_p->numChan().put(rownr_p, nchan); msSpWinCols_p->name().put(rownr_p, ""); msSpWinCols_p->refFrequency().put(rownr_p, refFrequency); msSpWinCols_p->chanFreq().put(rownr_p, frequency); msSpWinCols_p->chanWidth().put(rownr_p, chWidth); msSpWinCols_p->measFreqRef().put(rownr_p, freqRefType); msSpWinCols_p->effectiveBW().put(rownr_p, chWidth); msSpWinCols_p->resolution().put(rownr_p, freqres); msSpWinCols_p->totalBandwidth().put(rownr_p, thisBW); msSpWinCols_p->netSideband().put(rownr_p, *netSidebandKey_p); msSpWinCols_p->ifConvChain().put(rownr_p, *ifConvChainKey_p); msSpWinCols_p->freqGroup().put(rownr_p, *freqGroupKey_p); msSpWinCols_p->freqGroupName().put(rownr_p,""); msSpWinCols_p->flagRow().put(rownr_p, *flagRowKey_p); } // either way, update the cache if (theCache_p->nrow() < cacheSize_p) theCache_p->addRow(); if (nextCacheRow_p >= cacheSize_p) nextCacheRow_p = 0; idCol_p.putScalar(nextCacheRow_p, spWindowId()); nchanCol_p.putScalar(nextCacheRow_p, nchan); freqRefTypeCol_p.putScalar(nextCacheRow_p, freqRefType); ifConvChainCol_p.putScalar(nextCacheRow_p, *ifConvChainKey_p); freqGroupCol_p.putScalar(nextCacheRow_p, *freqGroupKey_p); netSidebandCol_p.putScalar(nextCacheRow_p, *netSidebandKey_p); flagRowCol_p.putScalar(nextCacheRow_p, *flagRowKey_p); bwCachePtr_p[nextCacheRow_p] = thisBW; f0CachePtr_p[nextCacheRow_p] = thisF0; fNCachePtr_p[nextCacheRow_p] = thisFN; nextCacheRow_p++; } } } void SDSpWindowHandler::clearAll() { delete index_p; index_p = 0; delete theCache_p; theCache_p = 0; fNCache_p.putStorage(fNCachePtr_p, deleteItFN_p); f0Cache_p.putStorage(f0CachePtr_p, deleteItF0_p); bwCache_p.putStorage(bwCachePtr_p, deleteItBw_p); fNCachePtr_p = f0CachePtr_p = bwCachePtr_p = 0; delete msSpWin_p; msSpWin_p = 0; delete msSpWinCols_p; msSpWinCols_p = 0; nextCacheRow_p = 0; clearRow(); } void SDSpWindowHandler::clearRow() { bandwidField_p = freqresField_p = -1; spWinIdField_p.detach(); ifConvChainField_p.detach(); freqGroupField_p.detach(); netSidebandField_p.detach(); flagRowField_p.detach(); rownr_p = -1; } void SDSpWindowHandler::initAll(MeasurementSet &ms, Vector &handledCols, const Record &row) { msSpWin_p = new MSSpectralWindow(ms.spectralWindow()); AlwaysAssert(msSpWin_p, AipsError); msSpWinCols_p = new MSSpWindowColumns(*msSpWin_p); AlwaysAssert(msSpWinCols_p, AipsError); // construct a cache table with zero rows TableDesc td; td.addColumn(ScalarColumnDesc("ID")); td.addColumn(ScalarColumnDesc("NCHAN")); td.addColumn(ScalarColumnDesc("FREQREFTYPE")); td.addColumn(ScalarColumnDesc("IF_CONV_CHAIN")); td.addColumn(ScalarColumnDesc("FREQ_GROUP")); td.addColumn(ScalarColumnDesc("NET_SIDEBAND")); td.addColumn(ScalarColumnDesc("FLAG_ROW")); SetupNewTable newTab("",td,Table::Scratch); theCache_p = new Table(newTab, TableLock::PermanentLocking); AlwaysAssert(theCache_p, AipsError); // and attach the columns idCol_p.attach(*theCache_p, "ID"); nchanCol_p.attach(*theCache_p, "NCHAN"); freqRefTypeCol_p.attach(*theCache_p, "FREQREFTYPE"); ifConvChainCol_p.attach(*theCache_p, "IF_CONV_CHAIN"); freqGroupCol_p.attach(*theCache_p, "FREQ_GROUP"); netSidebandCol_p.attach(*theCache_p, "NET_SIDEBAND"); flagRowCol_p.attach(*theCache_p, "FLAG_ROW"); // and the floating point things we cache on the side fNCache_p.resize(cacheSize_p); f0Cache_p.resize(cacheSize_p); bwCache_p.resize(cacheSize_p); fNCachePtr_p = fNCache_p.getStorage(deleteItFN_p); f0CachePtr_p = f0Cache_p.getStorage(deleteItF0_p); bwCachePtr_p = bwCache_p.getStorage(deleteItBw_p); // create the index index_p = new ColumnsIndex(*theCache_p, stringToVector("NCHAN,FREQREFTYPE,IF_CONV_CHAIN,FREQ_GROUP,NET_SIDEBAND,FLAG_ROW")); AlwaysAssert(index_p, AipsError); // and attach the key fields nchanKey_p.attachToRecord(index_p->accessKey(), "NCHAN"); freqRefTypeKey_p.attachToRecord(index_p->accessKey(), "FREQREFTYPE"); ifConvChainKey_p.attachToRecord(index_p->accessKey(), "IF_CONV_CHAIN"); freqGroupKey_p.attachToRecord(index_p->accessKey(), "FREQ_GROUP"); netSidebandKey_p.attachToRecord(index_p->accessKey(), "NET_SIDEBAND"); flagRowKey_p.attachToRecord(index_p->accessKey(), "FLAG_ROW"); nextCacheRow_p = 0; initRow(handledCols, row); } void SDSpWindowHandler::initRow(Vector &handledCols, const Record &row) { AlwaysAssert(handledCols.nelements()==row.description().nfields(), AipsError); bandwidField_p = row.fieldNumber("BANDWID"); if (bandwidField_p < 0) { // allow for this alternative, older, spelling bandwidField_p = row.fieldNumber("BANDWIDT"); } if (bandwidField_p >= 0) handledCols(bandwidField_p) = True; freqresField_p = row.fieldNumber("FREQRES"); if (freqresField_p >= 0) handledCols(freqresField_p) = True; if (row.fieldNumber("MAIN_SPECTRAL_WINDOW_ID") >= 0 && row.dataType("MAIN_SPECTRAL_WINDOW_ID") == TpInt) { spWinIdField_p.attachToRecord(row,"MAIN_SPECTRAL_WINDOW_ID"); handledCols(row.fieldNumber("MAIN_SPECTRAL_WINDOW_ID")) = True; } if (row.fieldNumber("SPECTRAL_WINDOW_IF_CONV_CHAIN") >= 0 && row.dataType("SPECTRAL_WINDOW_IF_CONV_CHAIN") == TpInt) { ifConvChainField_p.attachToRecord(row,"SPECTRAL_WINDOW_IF_CONV_CHAIN"); handledCols(row.fieldNumber("SPECTRAL_WINDOW_IF_CONV_CHAIN")) = True; } if (row.fieldNumber("SPECTRAL_WINDOW_FREQ_GROUP") >= 0 && row.dataType("SPECTRAL_WINDOW_FREQ_GROUP") == TpInt) { freqGroupField_p.attachToRecord(row,"SPECTRAL_WINDOW_FREQ_GROUP"); handledCols(row.fieldNumber("SPECTRAL_WINDOW_FREQ_GROUP")) = True; } if (row.fieldNumber("SPECTRAL_WINDOW_NET_SIDEBAND") >= 0 && row.dataType("SPECTRAL_WINDOW_NET_SIDEBAND") == TpInt) { netSidebandField_p.attachToRecord(row,"SPECTRAL_WINDOW_NET_SIDEBAND"); handledCols(row.fieldNumber("SPECTRAL_WINDOW_NET_SIDEBAND")) = True; } if (row.fieldNumber("SPECTRAL_WINDOW_FLAG_ROW") >= 0 && row.dataType("SPECTRAL_WINDOW_FLAG_ROW") == TpBool) { flagRowField_p.attachToRecord(row,"SPECTRAL_WINDOW_FLAG_ROW"); handledCols(row.fieldNumber("SPECTRAL_WINDOW_FLAG_ROW")) = True; } // ignore this field, produced by ms2sdfits for MS version 1, it doesn't carry any additional information if (row.fieldNumber("SPECTRAL_WINDOW_NUM_CHAN") >= 0) handledCols(row.fieldNumber("SPECTRAL_WINDOW_NUM_CHAN")) = True; // row number isn't set until the following fill rownr_p = -1; } } //# NAMESPACE CASACORE - END casacore-3.7.1/msfits/MSFits/SDSpWinHandler.h000066400000000000000000000121171476623553700207360ustar00rootroot00000000000000//# SDSpWindowFiller.h: fills the SPECTRAL_WINDOW table for the SDFITS filler //# Copyright (C) 2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_SDSPWINHANDLER_H #define MS_SDSPWINHANDLER_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class ColumnsIndex; class MeasurementSet; class MSSpectralWindow; class MSSpWindowColumns; class Record; class Table; // // // or // // // //
      • SomeClass //
      • SomeOtherClass //
      • some concept // // // // // // // // // // // // // // // //
      • //
      • // // // //
      • //
      • // // // //
      • add this feature //
      • fix this bug //
      • start discussion of this possible extension // class SDSpWindowHandler { public: // default ctor is not attached to a MS and hence is useless until attached SDSpWindowHandler(); // attach this to a MS, marking fields in row which are explicitly handled here SDSpWindowHandler(MeasurementSet &ms, Vector &handledCols, const Record &row); // copy ctor SDSpWindowHandler(const SDSpWindowHandler &other); ~SDSpWindowHandler() {clearAll();} // assignment operator, uses copy semantics SDSpWindowHandler &operator=(const SDSpWindowHandler &other); // attach to a MS, the handledCols and row arguments are ignored here void attach(MeasurementSet &ms, Vector &handledCols, const Record &row); // reset internals given indicated row, use the same MS; just resets the id pointer void resetRow(const Record &); // fill - a circular buffer of last 100 spectral windows is checked void fill(const Record &row, const Vector &frequency, Double refFrequency, Double originalFreqDelt, Int freqRefType); // get the current spWindow ID Int spWindowId() {return rownr_p;} private: RecordFieldPtr nchanKey_p, freqRefTypeKey_p, ifConvChainKey_p, freqGroupKey_p, netSidebandKey_p; Vector fNCache_p, f0Cache_p, bwCache_p; Double *fNCachePtr_p, *f0CachePtr_p, *bwCachePtr_p; Bool deleteItFN_p, deleteItF0_p, deleteItBw_p; RecordFieldPtr flagRowKey_p; // the cache table is the one that is indexed ColumnsIndex *index_p; // temporary table to hold the fields we are indexing on, can't index on array column Table *theCache_p; MSSpectralWindow *msSpWin_p; MSSpWindowColumns *msSpWinCols_p; // the columns in the cache table TableColumn idCol_p, nchanCol_p, freqRefTypeCol_p, freqresCol_p, ifConvChainCol_p, freqGroupCol_p, netSidebandCol_p, flagRowCol_p; // the next row number to use in the cached uInt nextCacheRow_p; // the maximum number of rows in the cache - currently this is 1000 uInt cacheSize_p; // the current row number in the SPECTRAL_WINDOW table, i.e. the id Int rownr_p; // fields possibly mined from the SDFITS row // floating point fields that we can't be certain of their type Int bandwidField_p, freqresField_p; // fields from a previous life as a MS RORecordFieldPtr spWinIdField_p, ifConvChainField_p, freqGroupField_p, netSidebandField_p; RORecordFieldPtr flagRowField_p; // cleanup everything void clearAll(); // clean up items related to the row void clearRow(); // initialize everything void initAll(MeasurementSet &ms, Vector &handledCols, const Record &row); // initialize the stuff dependent on the row void initRow(Vector &handledCols, const Record &row); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/msfits/MSFits/SDSysCalHandler.cc000066400000000000000000000326411476623553700212360ustar00rootroot00000000000000//# SDSysCalHandler.cc: a SYSCAL handler for SDFITS data //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN SDSysCalHandler::SDSysCalHandler() : msSysCal_p(0), msSysCalCols_p(0), rownr_p(-1), nrecpt_p(0), tcalId_p(-1), tsysId_p(-1), trxId_p(-1), hasTsysCol_p(False), hasTcalCol_p(False), hasTrxCol_p(False) {;} SDSysCalHandler::SDSysCalHandler(MeasurementSet &ms, Vector &handledCols, const Record &row) : msSysCal_p(0), msSysCalCols_p(0), rownr_p(-1), nrecpt_p(0), tcalId_p(-1), tsysId_p(-1), trxId_p(-1), hasTsysCol_p(False), hasTcalCol_p(False), hasTrxCol_p(False) { initAll(ms, handledCols, row); } SDSysCalHandler::SDSysCalHandler(const SDSysCalHandler &other) : msSysCal_p(0), msSysCalCols_p(0), rownr_p(-1), nrecpt_p(0), tcalId_p(-1), tsysId_p(-1), trxId_p(-1), hasTsysCol_p(False), hasTcalCol_p(False), hasTrxCol_p(False) { *this = other; } SDSysCalHandler &SDSysCalHandler::operator=(const SDSysCalHandler &other) { if (this != &other) { clearAll(); msSysCal_p = new MSSysCal(*(other.msSysCal_p)); AlwaysAssert(msSysCal_p, AipsError); msSysCalCols_p = new MSSysCalColumns(*msSysCal_p); AlwaysAssert(msSysCalCols_p, AipsError); rownr_p = other.rownr_p; nrecpt_p = other.nrecpt_p; tcalId_p = other.tcalId_p; tsysId_p = other.tsysId_p; trxId_p = other.trxId_p; hasTsysCol_p = other.hasTsysCol_p; hasTcalCol_p = other.hasTcalCol_p; hasTrxCol_p = other.hasTrxCol_p; intervalField_p = other.intervalField_p; timeField_p = other.timeField_p; phaseDiffField_p = other.phaseDiffField_p; tcalFlagField_p = other.tcalFlagField_p; trxFlagField_p = other.trxFlagField_p; tsysFlagField_p = other.tsysFlagField_p; tcalField_p = other.tcalField_p; trxField_p = other.trxField_p; tsysField_p = other.tsysField_p; } return *this; } void SDSysCalHandler::attach(MeasurementSet &ms, Vector &handledCols, const Record &row) { clearAll(); initAll(ms, handledCols, row); } void SDSysCalHandler::resetRow(const Record &row) { clearRow(); Vector dummyHandledCols; initRow(dummyHandledCols, row); } void SDSysCalHandler::fill(const Record &row, Int antennaId, Int feedId, Int spectralWindowId, Double time, Vector timeRange, uInt numReceptors) { // don't bother unless there is something there if (msSysCal_p) { Vector tsys(numReceptors), tcal(numReceptors), trx(numReceptors); Bool tsysFlag, tcalFlag, trxFlag; tsysFlag = tcalFlag = trxFlag = False; tsys = tcal = trx = 0.0; // prefer the MS TSYS, TCAL, TRX since they have the correct dimensionality // but also fall back to SDFITS single values if Nr inferred from the MS is inconsistent if (tsysField_p.isAttached() && ((*tsysField_p).nelements() == numReceptors)) { tsys = *tsysField_p; } else if (tsysId_p >= 0) { tsys = row.asFloat(tsysId_p); } if (tsysFlagField_p.isAttached()) tsysFlag = *tsysFlagField_p; if (tcalField_p.isAttached() && ((*tcalField_p).nelements() == numReceptors)) { tcal = *tcalField_p; } else if (tcalId_p >= 0) { tcal = row.asFloat(tcalId_p); } if (tcalFlagField_p.isAttached()) tcalFlag = *tcalFlagField_p; if (trxField_p.isAttached() && ((*trxField_p).nelements() == numReceptors)) { trx = *tcalField_p; } else if (trxId_p >= 0) { trx = row.asFloat(trxId_p); } if (trxFlagField_p.isAttached()) trxFlag = *trxFlagField_p; Bool newRow = rownr_p < 0; newRow = newRow || numReceptors != nrecpt_p; if (!newRow && hasTsysCol_p) { newRow = tsysFlag != msSysCalCols_p->tsysFlag()(rownr_p); newRow = newRow || !allEQ(tsys, msSysCalCols_p->tsys()(rownr_p)); } if (!newRow && hasTcalCol_p) { newRow = tcalFlag != msSysCalCols_p->tcalFlag()(rownr_p); newRow = newRow || !allEQ(tcal, msSysCalCols_p->tcal()(rownr_p)); } if (!newRow && hasTrxCol_p) { newRow = trxFlag != msSysCalCols_p->trxFlag()(rownr_p); newRow = newRow || !allEQ(trx, msSysCalCols_p->trx()(rownr_p)); } newRow = newRow || numReceptors != nrecpt_p; newRow = newRow || msSysCalCols_p->antennaId()(rownr_p) != antennaId || msSysCalCols_p->feedId()(rownr_p) != feedId || msSysCalCols_p->spectralWindowId()(rownr_p) != spectralWindowId; if (!newRow && phaseDiffField_p.isAttached() && !near(*phaseDiffField_p, 0.0) && !isNaN(*phaseDiffField_p) && !isInf(*phaseDiffField_p)) { // we seem to have a valid phase diff value // is it flagged // newRow != True here -> PHASE_DIFF col must exist -> PHASE_DIFF_FLAG must also exist newRow = !newRow && msSysCalCols_p->phaseDiff()(rownr_p) != *phaseDiffField_p; newRow = !newRow && phaseDiffFlagField_p.isAttached() && *phaseDiffFlagField_p != msSysCalCols_p->phaseDiffFlag()(rownr_p); } Double interval = timeRange(1) - timeRange(0); // former MS time or the time used in this function argument? Double thisTime = time; if (timeField_p.isAttached()) { // former MS time thisTime = *timeField_p; // MS interval can't exist with MS time if (intervalField_p.isAttached()) { interval = *intervalField_p; } } if (!newRow) { // all of the fields except TIME and INTERVAL match. // if the time falls within the row interval of the row time // or the row time falls within the interval of time, then the rows overlap and // can be reused Double rowTime = msSysCalCols_p->time()(rownr_p); Double rowInterval = msSysCalCols_p->interval()(rownr_p); Double rid2 = rowInterval/2.0; Double id2 = interval/2.0; newRow = !(((time-id2)<(rowTime+rid2)) && ((rowTime-rid2)<(time+id2))); } if (newRow) { // fill it rownr_p = msSysCal_p->nrow(); msSysCal_p->addRow(); nrecpt_p = numReceptors; msSysCalCols_p->antennaId().put(rownr_p,antennaId); msSysCalCols_p->feedId().put(rownr_p,feedId); msSysCalCols_p->spectralWindowId().put(rownr_p, spectralWindowId); msSysCalCols_p->time().put(rownr_p, thisTime); msSysCalCols_p->interval().put(rownr_p, interval); if (hasTsysCol_p) { msSysCalCols_p->tsys().put(rownr_p, tsys); msSysCalCols_p->tsysFlag().put(rownr_p, tsysFlag); } if (hasTcalCol_p) { msSysCalCols_p->tcal().put(rownr_p, tcal); msSysCalCols_p->tcalFlag().put(rownr_p, tcalFlag); } if (hasTrxCol_p) { msSysCalCols_p->trx().put(rownr_p, trx); msSysCalCols_p->trxFlag().put(rownr_p, trxFlag); } if (phaseDiffField_p.isAttached()) { if (msSysCalCols_p->phaseDiff().isNull()) { if (!near(*phaseDiffField_p, 0.0) && !isNaN(*phaseDiffField_p) && !isInf(*phaseDiffField_p)) { // need to add this column delete msSysCalCols_p; msSysCalCols_p = 0; TableDesc td; MSSysCal::addColumnToDesc(td, MSSysCal::PHASE_DIFF); MSSysCal::addColumnToDesc(td, MSSysCal::PHASE_DIFF_FLAG); msSysCal_p->addColumn(td[0]); msSysCal_p->addColumn(td[1]); msSysCalCols_p = new MSSysCalColumns(*msSysCal_p); AlwaysAssert(msSysCalCols_p, AipsError); msSysCalCols_p->phaseDiff().put(rownr_p, *phaseDiffField_p); if (phaseDiffFlagField_p.isAttached()) { msSysCalCols_p->phaseDiffFlag().put(rownr_p, *phaseDiffFlagField_p); } else { msSysCalCols_p->phaseDiffFlag().put(rownr_p, False); } } } else { msSysCalCols_p->phaseDiff().put(rownr_p, *phaseDiffField_p); if (phaseDiffFlagField_p.isAttached()) { msSysCalCols_p->phaseDiffFlag().put(rownr_p, *phaseDiffFlagField_p); } else { msSysCalCols_p->phaseDiffFlag().put(rownr_p, False); } } } } else { // reuse this row, make sure that the time range is fully set // and place the time in the center of it Double rowTime = msSysCalCols_p->time()(rownr_p); Double rowInterval = msSysCalCols_p->interval()(rownr_p); Double minTime, maxTime; minTime = min(time-interval/2.0, rowTime-rowInterval/2.0); maxTime = max(time+interval/2.0, rowTime+rowInterval/2.0); msSysCalCols_p->time().put(rownr_p, (maxTime+minTime)/2.0); msSysCalCols_p->interval().put(rownr_p, (maxTime-minTime)); } } } void SDSysCalHandler::clearAll() { delete msSysCal_p; msSysCal_p = 0; delete msSysCalCols_p; msSysCalCols_p = 0; clearRow(); } void SDSysCalHandler::clearRow() { tcalId_p = tsysId_p = trxId_p = -1; intervalField_p.detach(); timeField_p.detach(); phaseDiffField_p.detach(); phaseDiffFlagField_p.detach(); tcalFlagField_p.detach(); trxFlagField_p.detach(); tsysFlagField_p.detach(); tcalField_p.detach(); trxField_p.detach(); tsysField_p.detach(); } void SDSysCalHandler::initAll(MeasurementSet &ms, Vector &handledCols, const Record &row) { msSysCal_p = new MSSysCal(ms.sysCal()); AlwaysAssert(msSysCal_p, AipsError); initRow(handledCols, row); // do we need to add any optional columns TableDesc td; if (tsysId_p >= 0 || tsysField_p.isAttached()) { hasTsysCol_p = True; MSSysCal::addColumnToDesc(td,MSSysCal::TSYS); MSSysCal::addColumnToDesc(td,MSSysCal::TSYS_FLAG); } if (tcalId_p >= 0 || tcalField_p.isAttached()) { hasTcalCol_p = True; MSSysCal::addColumnToDesc(td,MSSysCal::TCAL); MSSysCal::addColumnToDesc(td,MSSysCal::TCAL_FLAG); } if (trxId_p >= 0 || trxField_p.isAttached()) { hasTrxCol_p = True; MSSysCal::addColumnToDesc(td,MSSysCal::TRX); MSSysCal::addColumnToDesc(td,MSSysCal::TRX_FLAG); } for (uInt i=0;iaddColumn(td[i]); } msSysCalCols_p = new MSSysCalColumns(*msSysCal_p); AlwaysAssert(msSysCalCols_p, AipsError); nrecpt_p = 0; rownr_p = -1; } void SDSysCalHandler::initRow(Vector &handledCols, const Record &row) { tcalId_p = row.fieldNumber("TCAL"); if (tcalId_p >= 0) handledCols(tcalId_p) = True; tsysId_p = row.fieldNumber("TSYS"); if (tsysId_p >= 0) handledCols(tsysId_p) = True; trxId_p = row.fieldNumber("TRX"); if (trxId_p >= 0) handledCols(trxId_p) = True; Int tmp; tmp = row.fieldNumber("SYSCAL_INTERVAL"); if (tmp >= 0 && row.dataType(tmp) == TpDouble) { intervalField_p.attachToRecord(row, tmp); handledCols(tmp) = True; } tmp = row.fieldNumber("SYSCAL_TIME"); if (tmp >= 0 && row.dataType(tmp) == TpDouble) { timeField_p.attachToRecord(row, tmp); handledCols(tmp) = True; } tmp = row.fieldNumber("SYSCAL_PHASE_DIFF"); if (tmp >= 0 && row.dataType(tmp) == TpFloat) { phaseDiffField_p.attachToRecord(row, tmp); handledCols(tmp) = True; } tmp = row.fieldNumber("SYSCAL_PHASE_DIFF_FLAG"); if (tmp >= 0 && row.dataType(tmp) == TpBool) { phaseDiffFlagField_p.attachToRecord(row, tmp); handledCols(tmp) = True; } tmp = row.fieldNumber("SYSCAL_TCAL"); if (tmp >= 0 && row.dataType(tmp) == TpArrayFloat) { tcalField_p.attachToRecord(row, tmp); handledCols(tmp) = True; } tmp = row.fieldNumber("SYSCAL_TCAL_FLAG"); if (tmp >= 0 && row.dataType(tmp) == TpBool) { tcalFlagField_p.attachToRecord(row, tmp); handledCols(tmp) = True; } tmp = row.fieldNumber("SYSCAL_TRX"); if (tmp >= 0 && row.dataType(tmp) == TpArrayFloat) { trxField_p.attachToRecord(row, tmp); handledCols(tmp) = True; } tmp = row.fieldNumber("SYSCAL_TRX_FLAG"); if (tmp >= 0 && row.dataType(tmp) == TpBool) { trxFlagField_p.attachToRecord(row, tmp); handledCols(tmp) = True; } tmp = row.fieldNumber("SYSCAL_TSYS"); if (tmp >= 0 && row.dataType(tmp) == TpArrayFloat) { tsysField_p.attachToRecord(row, tmp); handledCols(tmp) = True; } tmp = row.fieldNumber("SYSCAL_TSYS_FLAG"); if (tmp >= 0 && row.dataType(tmp) == TpBool) { tsysFlagField_p.attachToRecord(row, tmp); handledCols(tmp) = True; } // ignore this field as it add no useful additional information if (row.fieldNumber("SYSCAL_NUM_RECEPTORS") >= 0) handledCols(row.fieldNumber("SYSCAL_NUM_RECEPTORS")) = True; } } //# NAMESPACE CASACORE - END casacore-3.7.1/msfits/MSFits/SDSysCalHandler.h000066400000000000000000000101231476623553700210670ustar00rootroot00000000000000//# SDSysCalFiller.h: fills the SYSCAL table for the SDFITS filler //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_SDSYSCALHANDLER_H #define MS_SDSYSCALHANDLER_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MeasurementSet; class MSSysCal; class MSSysCalColumns; class Record; // // // or // // // //
      • SomeClass //
      • SomeOtherClass //
      • some concept // // // // // // // // // // // // // // // //
      • //
      • // // // //
      • //
      • // // // //
      • add this feature //
      • fix this bug //
      • start discussion of this possible extension // class SDSysCalHandler { public: // default ctor is not attached to a MS and hence is useless until attached SDSysCalHandler(); // attach this to a MS - mark fields in row as handled SDSysCalHandler(MeasurementSet &ms, Vector &handledCols, const Record &row); // copy ctor SDSysCalHandler(const SDSysCalHandler &other); ~SDSysCalHandler() {clearAll();} // assignment operator, uses copy semantics SDSysCalHandler &operator=(const SDSysCalHandler &other); // attach to a MS, mark fields in row as handled void attach(MeasurementSet &ms, Vector &handledCols, const Record &row); // reset internals given indicated row, use the same MS void resetRow(const Record &row); // fill - a new row is added as necessary, there is no lookback to see if a row could be // reused. Only the current row might be reused. void fill(const Record &row, Int antennaId, Int feedId, Int spectralWindowId, Double time, Vector timeRange, uInt numReceptors); private: MSSysCal *msSysCal_p; MSSysCalColumns *msSysCalCols_p; Int rownr_p; uInt nrecpt_p; Int tcalId_p, tsysId_p, trxId_p; Bool hasTsysCol_p, hasTcalCol_p, hasTrxCol_p; // fields which come from a previous incarnation as a MS RORecordFieldPtr intervalField_p, timeField_p; RORecordFieldPtr phaseDiffField_p; RORecordFieldPtr tcalFlagField_p, trxFlagField_p, tsysFlagField_p, phaseDiffFlagField_p; RORecordFieldPtr > tcalField_p, trxField_p, tsysField_p; // cleanup everything void clearAll(); // cleanup row-related stuff void clearRow(); // initialize everything void initAll(MeasurementSet &ms, Vector &handledCols, const Record &row); // intialize the row related stuff void initRow(Vector &handledCols, const Record &row); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/msfits/MSFits/SDWeatherHandler.cc000066400000000000000000000365431476623553700214440ustar00rootroot00000000000000//# SDWeatherHandler.cc: a WEATHER handler for SDFITS data //# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN SDWeatherHandler::SDWeatherHandler() : msWeather_p(0), msWeatherCols_p(0), rownr_p(-1), humidityId_p(-1), tambientId_p(-1), pressureId_p(-1), dewpointId_p(-1), windspeeId_p(-1), winddireId_p(-1) {;} SDWeatherHandler::SDWeatherHandler(MeasurementSet &ms, Vector &handledCols, const Record &row) : msWeather_p(0), msWeatherCols_p(0), rownr_p(-1), humidityId_p(-1), tambientId_p(-1), pressureId_p(-1),dewpointId_p(-1), windspeeId_p(-1), winddireId_p(-1) { initAll(ms, handledCols, row); } SDWeatherHandler::SDWeatherHandler(const SDWeatherHandler &other) : msWeather_p(0), msWeatherCols_p(0), rownr_p(-1), humidityId_p(-1), tambientId_p(-1), pressureId_p(-1), dewpointId_p(-1), windspeeId_p(-1), winddireId_p(-1) { *this = other; } SDWeatherHandler &SDWeatherHandler::operator=(const SDWeatherHandler &other) { if (this != &other) { clearAll(); msWeather_p = new MSWeather(*(other.msWeather_p)); AlwaysAssert(msWeather_p, AipsError); msWeatherCols_p = new MSWeatherColumns(*msWeather_p); AlwaysAssert(msWeatherCols_p, AipsError); rownr_p = other.rownr_p; humidityId_p = other.humidityId_p; tambientId_p = other.tambientId_p; pressureId_p = other.pressureId_p; dewpointId_p = other.dewpointId_p; windspeeId_p = other.windspeeId_p; winddireId_p = other.winddireId_p; H2OField_p = other.H2OField_p; ionosElectronField_p = other.ionosElectronField_p; timeField_p = other.timeField_p; intervalField_p = other.intervalField_p; pressureField_p = other.pressureField_p; humidityField_p = other.humidityField_p; temperatureField_p = other.temperatureField_p; windDirField_p = other.windDirField_p; windSpeedField_p = other.windSpeedField_p; } return *this; } void SDWeatherHandler::attach(MeasurementSet &ms, Vector &handledCols, const Record &row) { clearAll(); initAll(ms, handledCols, row); } void SDWeatherHandler::resetRow(const Record &row) { clearRow(); Vector dummyHandledCols; initRow(dummyHandledCols, row); } void SDWeatherHandler::fill(const Record &row, Int antennaId, Double time, Vector &timeRange) { // don't bother unless there is something there and something to add if (msWeather_p && (humidityId_p >= 0 || tambientId_p >= 0 || pressureId_p >= 0 || dewpointId_p >= 0 || windspeeId_p >= 0 || winddireId_p >= 0 || (H2OField_p.isAttached() && !isNaN(*H2OField_p) && !isInf(*H2OField_p)) || (ionosElectronField_p.isAttached() && !isNaN(*ionosElectronField_p) && !isInf(*ionosElectronField_p)))) { Float thisHumidity, thisTambient, thisDewpoint, thisWindspee, thisWinddire, thisPressure; thisHumidity = thisTambient = thisDewpoint = thisWindspee = thisWinddire = thisPressure = 0.0; if (humidityId_p >= 0) thisHumidity = row.asFloat(humidityId_p); else if (humidityField_p.isAttached()) thisHumidity = *humidityField_p; if (tambientId_p >= 0) thisTambient = row.asFloat(tambientId_p); else if (temperatureField_p.isAttached()) thisTambient = *temperatureField_p; if (pressureId_p >= 0) thisPressure = row.asFloat(pressureId_p); else if (pressureField_p.isAttached()) thisPressure = *pressureField_p; if (dewpointId_p >= 0) thisDewpoint = row.asFloat(dewpointId_p); if (windspeeId_p >= 0) thisWindspee = row.asFloat(windspeeId_p); else if (windSpeedField_p.isAttached()) thisWindspee = *windSpeedField_p; if (winddireId_p >= 0) thisWinddire = row.asFloat(winddireId_p); else if (windDirField_p.isAttached()) thisWinddire = *windDirField_p; Bool newRow = rownr_p < 0; if (!newRow && !msWeatherCols_p->relHumidity().isNull()) { newRow = thisHumidity != msWeatherCols_p->relHumidity()(rownr_p); } if (!newRow && !msWeatherCols_p->temperature().isNull()) { newRow = thisTambient != msWeatherCols_p->temperature()(rownr_p); } if (!newRow && !msWeatherCols_p->pressure().isNull()) { newRow = thisPressure != msWeatherCols_p->pressure()(rownr_p); } if (!newRow && !msWeatherCols_p->dewPoint().isNull()) { newRow = thisDewpoint != msWeatherCols_p->dewPoint()(rownr_p); } if (!newRow && !msWeatherCols_p->windSpeed().isNull()) { newRow = thisWindspee != msWeatherCols_p->windSpeed()(rownr_p); } if (!newRow && !msWeatherCols_p->windDirection().isNull()) { newRow = thisWinddire != msWeatherCols_p->windDirection()(rownr_p); } if (!newRow && H2OField_p.isAttached() && !isNaN(*H2OField_p) && !isInf(*H2OField_p)) { // we seem to have a valid H2O value newRow = msWeatherCols_p->H2O().isNull(); newRow = !newRow && msWeatherCols_p->H2O()(rownr_p) != *H2OField_p; } if (!newRow && ionosElectronField_p.isAttached() && !isNaN(*ionosElectronField_p) && !isInf(*ionosElectronField_p)) { // we seem to have a valid IONOS_ELECTRON value newRow = msWeatherCols_p->ionosElectron().isNull(); newRow = !newRow && msWeatherCols_p->ionosElectron()(rownr_p) != *ionosElectronField_p; } newRow = newRow || antennaId != msWeatherCols_p->antennaId()(rownr_p);; Double interval = timeRange(1) - timeRange(0); Double thisTime = time; // or should former MS time and interval be used here instead if (timeField_p.isAttached()) { thisTime = *timeField_p; // MS interval can't exist without MS time if (intervalField_p.isAttached()) { interval = *intervalField_p; } } if (!newRow) { // all of the fields except TIME and INTERVAL match. // if the time falls within the row interval of the row time // or the row time falls within the interval of time, then the rows overlap and // can be reused Double rowTime = msWeatherCols_p->time()(rownr_p); Double rowInterval = msWeatherCols_p->interval()(rownr_p); Double rid2 = rowInterval/2.0; Double id2 = interval/2.0; newRow = !(((time-id2)<(rowTime+rid2)) && ((rowTime-rid2)<(time+id2))); } if (newRow) { // fill it rownr_p = msWeather_p->nrow(); msWeather_p->addRow(); msWeatherCols_p->antennaId().put(rownr_p,antennaId); msWeatherCols_p->time().put(rownr_p, thisTime); msWeatherCols_p->interval().put(rownr_p, interval); if (!msWeatherCols_p->relHumidity().isNull()) { msWeatherCols_p->relHumidity().put(rownr_p, thisHumidity); msWeatherCols_p->relHumidityFlag().put(rownr_p, False); } if (!msWeatherCols_p->temperature().isNull()) { msWeatherCols_p->temperature().put(rownr_p, thisTambient); msWeatherCols_p->temperatureFlag().put(rownr_p, False); } if (!msWeatherCols_p->pressure().isNull()) { msWeatherCols_p->pressure().put(rownr_p, thisPressure); msWeatherCols_p->pressureFlag().put(rownr_p, False); } if (!msWeatherCols_p->dewPoint().isNull()) { msWeatherCols_p->dewPoint().put(rownr_p, thisDewpoint); msWeatherCols_p->dewPointFlag().put(rownr_p, False); } if (!msWeatherCols_p->windSpeed().isNull()) { msWeatherCols_p->windSpeed().put(rownr_p, thisWindspee); msWeatherCols_p->windSpeedFlag().put(rownr_p, False); } if (!msWeatherCols_p->windDirection().isNull()) { msWeatherCols_p->windDirection().put(rownr_p, thisWinddire); msWeatherCols_p->windDirectionFlag().put(rownr_p, False); } if (H2OField_p.isAttached()) { if (msWeatherCols_p->H2O().isNull()) { if (!isNaN(*H2OField_p) && !isInf(*H2OField_p)) { // need to add this column delete msWeatherCols_p; msWeatherCols_p = 0; TableDesc td; MSWeather::addColumnToDesc(td, MSWeather::H2O); MSWeather::addColumnToDesc(td, MSWeather::H2O_FLAG); msWeather_p->addColumn(td[0]); msWeather_p->addColumn(td[1]); msWeatherCols_p = new MSWeatherColumns(*msWeather_p); AlwaysAssert(msWeatherCols_p, AipsError); msWeatherCols_p->H2O().put(rownr_p, *H2OField_p); msWeatherCols_p->H2OFlag().put(rownr_p, False); } } else { msWeatherCols_p->H2O().put(rownr_p, *H2OField_p); msWeatherCols_p->H2OFlag().put(rownr_p, False); } } if (ionosElectronField_p.isAttached()) { if (msWeatherCols_p->ionosElectron().isNull()) { if (!isNaN(*ionosElectronField_p) && !isInf(*ionosElectronField_p)) { // need to add this column delete msWeatherCols_p; msWeatherCols_p = 0; TableDesc td; MSWeather::addColumnToDesc(td, MSWeather::IONOS_ELECTRON); MSWeather::addColumnToDesc(td, MSWeather::IONOS_ELECTRON_FLAG); msWeather_p->addColumn(td[0]); msWeather_p->addColumn(td[1]); msWeatherCols_p = new MSWeatherColumns(*msWeather_p); AlwaysAssert(msWeatherCols_p, AipsError); msWeatherCols_p->ionosElectron().put(rownr_p, *ionosElectronField_p); msWeatherCols_p->ionosElectronFlag().put(rownr_p, False); } } else { msWeatherCols_p->ionosElectron().put(rownr_p, *ionosElectronField_p); msWeatherCols_p->ionosElectronFlag().put(rownr_p, False); } } } else { // reuse this row, make sure that the time range is fully set // and place the time in the center of it Double rowTime = msWeatherCols_p->time()(rownr_p); Double rowInterval = msWeatherCols_p->interval()(rownr_p); Double minTime, maxTime; minTime = min(time-interval/2.0, rowTime-rowInterval/2.0); maxTime = max(time+interval/2.0, rowTime+rowInterval/2.0); msWeatherCols_p->time().put(rownr_p, (maxTime+minTime)/2.0); msWeatherCols_p->interval().put(rownr_p, (maxTime-minTime)); } } } void SDWeatherHandler::clearAll() { delete msWeather_p; msWeather_p = 0; delete msWeatherCols_p; msWeatherCols_p = 0; clearRow(); } void SDWeatherHandler::clearRow() { humidityId_p = tambientId_p = pressureId_p = dewpointId_p = windspeeId_p = winddireId_p = -1; H2OField_p.detach(); ionosElectronField_p.detach(); timeField_p.detach(); intervalField_p.detach(); pressureField_p.detach(); humidityField_p.detach(); temperatureField_p.detach(); windDirField_p.detach(); windSpeedField_p.detach(); } void SDWeatherHandler::initAll(MeasurementSet &ms, Vector &handledCols, const Record &row) { msWeather_p = new MSWeather(ms.weather()); AlwaysAssert(msWeather_p, AipsError); initRow(handledCols, row); // do we need to add any optional columns TableDesc td; if (humidityId_p >= 0 || humidityField_p.isAttached()) { MSWeather::addColumnToDesc(td,MSWeather::REL_HUMIDITY); MSWeather::addColumnToDesc(td,MSWeather::REL_HUMIDITY_FLAG); } if (tambientId_p >= 0 || temperatureField_p.isAttached()) { MSWeather::addColumnToDesc(td,MSWeather::TEMPERATURE); MSWeather::addColumnToDesc(td,MSWeather::TEMPERATURE_FLAG); } if (pressureId_p >= 0 || pressureField_p.isAttached()) { MSWeather::addColumnToDesc(td,MSWeather::PRESSURE); MSWeather::addColumnToDesc(td,MSWeather::PRESSURE_FLAG); } if (dewpointId_p >= 0) { MSWeather::addColumnToDesc(td,MSWeather::DEW_POINT); MSWeather::addColumnToDesc(td,MSWeather::DEW_POINT_FLAG); } if (windspeeId_p >= 0 || windSpeedField_p.isAttached()) { MSWeather::addColumnToDesc(td,MSWeather::WIND_SPEED); MSWeather::addColumnToDesc(td,MSWeather::WIND_SPEED_FLAG); } if (winddireId_p >= 0 || windDirField_p.isAttached()) { MSWeather::addColumnToDesc(td,MSWeather::WIND_DIRECTION); MSWeather::addColumnToDesc(td,MSWeather::WIND_DIRECTION_FLAG); } for (uInt i=0;iaddColumn(td[i]); } msWeatherCols_p = new MSWeatherColumns(*msWeather_p); AlwaysAssert(msWeatherCols_p, AipsError); } void SDWeatherHandler::initRow(Vector &handledCols, const Record &row) { humidityId_p = row.fieldNumber("HUMIDITY"); if (humidityId_p >= 0) handledCols(humidityId_p) = True; tambientId_p = row.fieldNumber("TAMBIENT"); if (tambientId_p >= 0) handledCols(tambientId_p) = True; pressureId_p = row.fieldNumber("PRESSURE"); if (pressureId_p >= 0) handledCols(pressureId_p) = True; dewpointId_p = row.fieldNumber("DEWPOINT"); if (dewpointId_p >= 0) handledCols(dewpointId_p) = True; windspeeId_p = row.fieldNumber("WINDSPEE"); if (windspeeId_p >= 0) handledCols(windspeeId_p) = True; winddireId_p = row.fieldNumber("WINDDIRE"); if (winddireId_p >= 0) handledCols(winddireId_p) = True; Int tmp; tmp = row.fieldNumber("WEATHER_H2O"); if (tmp >= 0 && row.dataType(tmp) == TpFloat) { H2OField_p.attachToRecord(row, tmp); handledCols(tmp) = True; } tmp = row.fieldNumber("WEATHER_IONOS_ELECTRON"); if (tmp >= 0 && row.dataType(tmp) == TpFloat) { ionosElectronField_p.attachToRecord(row, tmp); handledCols(tmp) = True; } tmp = row.fieldNumber("WEATHER_TIME"); if (tmp >= 0 && row.dataType(tmp) == TpDouble) { timeField_p.attachToRecord(row, tmp); handledCols(tmp) = True; } tmp = row.fieldNumber("WEATHER_INTERVAL"); if (tmp >= 0 && row.dataType(tmp) == TpDouble) { intervalField_p.attachToRecord(row, tmp); handledCols(tmp) = True; } tmp = row.fieldNumber("WEATHER_PRESSURE"); if (tmp >= 0 && row.dataType(tmp) == TpFloat) { pressureField_p.attachToRecord(row, tmp); handledCols(tmp) = True; } tmp = row.fieldNumber("WEATHER_REL_HUMIDITY"); if (tmp >= 0 && row.dataType(tmp) == TpFloat) { humidityField_p.attachToRecord(row, tmp); handledCols(tmp) = True; } tmp = row.fieldNumber("WEATHER_TEMPERATURE"); if (tmp >= 0 && row.dataType(tmp) == TpFloat) { temperatureField_p.attachToRecord(row, tmp); handledCols(tmp) = True; } tmp = row.fieldNumber("WEATHER_WIND_DIRECTION"); if (tmp >= 0 && row.dataType(tmp) == TpFloat) { windDirField_p.attachToRecord(row, tmp); handledCols(tmp) = True; } tmp = row.fieldNumber("WEATHER_WIND_SPEED"); if (tmp >= 0 && row.dataType(tmp) == TpFloat) { windSpeedField_p.attachToRecord(row, tmp); handledCols(tmp) = True; } rownr_p = -1; } } //# NAMESPACE CASACORE - END casacore-3.7.1/msfits/MSFits/SDWeatherHandler.h000066400000000000000000000077501476623553700213040ustar00rootroot00000000000000//# SDWeatherFiller.h: fills the WEATHER table for the SDFITS filler //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef MS_SDWEATHERHANDLER_H #define MS_SDWEATHERHANDLER_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MeasurementSet; class MSWeather; class MSWeatherColumns; class Record; // // // or // // // //
      • SomeClass //
      • SomeOtherClass //
      • some concept // // // // // // // // // // // // // // // //
      • //
      • // // // //
      • //
      • // // // //
      • add this feature //
      • fix this bug //
      • start discussion of this possible extension // class SDWeatherHandler { public: // default ctor is not attached to a MS and hence is useless until attached SDWeatherHandler(); // attach this to a MS - mark fields in row as handled SDWeatherHandler(MeasurementSet &ms, Vector &handledCols, const Record &row); // copy ctor SDWeatherHandler(const SDWeatherHandler &other); ~SDWeatherHandler() {clearAll();} // assignment operator, uses copy semantics SDWeatherHandler &operator=(const SDWeatherHandler &other); // attach to a MS, mark fields in row as handled void attach(MeasurementSet &ms, Vector &handledCols, const Record &row); // reset internals given indicated row, use the same MS void resetRow(const Record &row); // fill - a new row is added as necessary, there is no lookback to see if a row could be // reused. Only the current row might be reused. void fill(const Record &row, Int antennaId, Double time, Vector &timeRange); private: MSWeather *msWeather_p; MSWeatherColumns *msWeatherCols_p; Int rownr_p; Int humidityId_p, tambientId_p, pressureId_p, dewpointId_p, windspeeId_p, winddireId_p; // additional fields from an SDFITS file that had a previous life as a MS RORecordFieldPtr H2OField_p, ionosElectronField_p, pressureField_p, humidityField_p, temperatureField_p, windDirField_p, windSpeedField_p; RORecordFieldPtr timeField_p, intervalField_p; // cleanup everything void clearAll(); // cleanup row-related stuff void clearRow(); // initialize everything void initAll(MeasurementSet &ms, Vector &handledCols, const Record &row); // intialize the row related stuff void initRow(Vector &handledCols, const Record &row); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/msfits/MSFits/test/000077500000000000000000000000001476623553700167555ustar00rootroot00000000000000casacore-3.7.1/msfits/MSFits/test/CMakeLists.txt000066400000000000000000000005271476623553700215210ustar00rootroot00000000000000set (tests tfits2ms tMSConcat tMSFITSInput tMSFITSOutput tMSFitsSelection ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_msfits) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/msfits/MSFits/test/tMSConcat.cc000066400000000000000000000072071476623553700211250ustar00rootroot00000000000000//# Copyright (C) 1995,1996,1997,1999,2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include int main(int argc, const char* argv[]) { try { Input inputs(1); inputs.create("ms", "", "Initial measurement set"); inputs.create("append", "", "Measurement set to append"); inputs.create("fits", "", "Initial fits file"); inputs.create("fitsappend", "", "Fits file to append"); inputs.readArguments (argc, argv); const String fitsName = inputs.getString("fits"); const String fitsAppendName = inputs.getString("fitsappend"); const String msName = inputs.getString("ms"); const String appendName = inputs.getString("append"); if (!Table::isReadable(msName)) { if (fitsName.length() == 0) { String errorMsg = "Input ms called " + msName + " does not exist\n" + " and no FITS file is specified"; throw(AipsError(errorMsg)); } cout << "Converting FITS file called " << fitsName << " to and MS called " << msName << endl; MSFitsInput msfitsin(msName, fitsName); msfitsin.readFitsFile(); } if (!Table::isReadable(appendName)) { if (fitsAppendName.length() == 0) { String errorMsg = "Input ms called " + msName + " does not exist\n" + " and no FITS file is specified"; throw(AipsError(errorMsg)); } cout << "Converting FITS file called " << fitsAppendName << " to and MS called " << appendName << endl; MSFitsInput msfitsin(appendName, fitsAppendName); msfitsin.readFitsFile(); } if (!Table::isWritable(msName)) { throw(AipsError("MS to append to is not writable")); } if (!Table::isReadable(appendName)) { throw(AipsError("MS to append is not readable")); } MeasurementSet ms(msName, Table::Update); MeasurementSet appendedMS(appendName, Table::Old); MSConcat mscat(ms); mscat.concatenate(appendedMS); } catch (std::exception& x) { cerr << x.what() << endl; cout << "FAIL" << endl; return 1; } catch (...) { cerr << "Exception not derived from AipsError" << endl; cout << "FAIL" << endl; return 2; } cout << "OK" << endl; return 0; } // Local Variables: // compile-command: "gmake OPTLIB=1 tMSConcat" // End: casacore-3.7.1/msfits/MSFits/test/tMSConcat.run000066400000000000000000000023171476623553700213410ustar00rootroot00000000000000#!/bin/sh #----------------------------------------------------------------------------- # Usage: tMSConcat.run #----------------------------------------------------------------------------- # This script executes the program tMSConcat to test if new the # measurement set concatrenation is working. # The script supplies the names of all test tables found in the system. # It is meant to be run from assay, but can also be used standalone. #----------------------------------------------------------------------------- if [ ${#AIPSPATH} = 0 ] then echo "UNTESTED: tMSConcat.run (AIPSPATH not defined)" exit 3 fi IN1='BLLAC.fits' IN2='3C273XC1.fits' AIPSDEMO=`echo $AIPSPATH | awk '{printf("%s/data/demo",$1)}'` FITS1=`echo $AIPSDEMO $IN1 | awk '{printf("%s/%s", $1,$2)}'` FITS2=`echo $AIPSDEMO $IN2 | awk '{printf("%s/%s", $1,$2)}'` MS1=`echo $IN1 | sed 's/.fits/_tmp.ms/'` MS2=`echo $IN2 | sed 's/.fits/_tmp.ms/'` if [ ! -e $FITS1 ] then echo "UNTESTED: tMSConcat.run ($FITS1 not found)" exit 3 fi if [ ! -e $FITS2 ] then echo "UNTESTED: tMSConcat.run ($FITS2 not found)" exit 3 fi $casa_checktool ./tMSConcat fits=$FITS1 ms=$MS1 fitsappend=$FITS2 append=$MS2 casacore-3.7.1/msfits/MSFits/test/tMSFITSInput.cc000066400000000000000000000053171476623553700215030ustar00rootroot00000000000000//# Copyright (C) 1995,1996,1997,1999,2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include using namespace casacore; void removeIfNecessary(const String& msname) { Directory d(msname); if (d.exists()) { d.removeRecursive(); } } int main() { try { String *parts = new String[2]; split(EnvironmentVariable::get("CASAPATH"), parts, 2, String(" ")); String datadir = parts[0] + "/data/"; delete [] parts; String fitsfile = datadir + "regression/unittest/uvfits/1331+305_I.UVFITS"; if (! File(fitsfile).exists()) { cout << "Cannot find test fixture so tests cannot be run" << endl; return 0; } String msfile = "myoutms.ms"; removeIfNecessary(msfile); MSFitsInput msfitsin(msfile, fitsfile); Bool thrown = False; try { msfitsin.readFitsFile(); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); } catch (const std::exception& x) { cerr << x.what() << endl; cout << "FAIL" << endl; return 1; } catch (...) { cerr << "Exception not derived from AipsError" << endl; cout << "FAIL" << endl; return 2; } cout << "OK" << endl; return 0; } casacore-3.7.1/msfits/MSFits/test/tMSFITSOutput.cc000066400000000000000000000063761476623553700217120ustar00rootroot00000000000000//# Copyright (C) 1995,1996,1997,1999,2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include #include #include #include #include #include #include using namespace casacore; int main() { try { String *parts = new String[2]; split(EnvironmentVariable::get("CASAPATH"), parts, 2, String(" ")); String datadir = parts[0] + "/data/"; delete [] parts; String msname = datadir + "regression/unittest/uvfits/uvfits_test.ms"; if (! File(msname).exists()) { cout << "Cannot find test fixture so tests cannot be run" << endl; return 0; } MeasurementSet ms(msname); cout << "Test overwrite parameter" << endl; String fitsFile = "test1.ms"; AlwaysAssert( MSFitsOutput::writeFitsFile( fitsFile, ms, "DATA", 0, 1, 1, False, False, False, False, 1.0, False, 1, 0, True ), AipsError ); // this should fail since overwrite is False Bool thrown = False; try { MSFitsOutput::writeFitsFile( fitsFile, ms, "DATA", 0, 1, 1, False, False, False, False, 1.0, False, 1, 0, False ); } catch (const AipsError&) { thrown = True; } AlwaysAssert(thrown, AipsError); // this should succeed, since overwrite is True AlwaysAssert( MSFitsOutput::writeFitsFile( fitsFile, ms, "DATA", 0, 1, 1, False, False, False, False, 1.0, False, 1, 0, True ), AipsError ); // clean up RegularFile(fitsFile).remove(); } catch (const std::exception& x) { cerr << x.what() << endl; cout << "FAIL" << endl; return 1; } catch (...) { cerr << "Exception not derived from AipsError" << endl; cout << "FAIL" << endl; return 2; } cout << "OK" << endl; return 0; } casacore-3.7.1/msfits/MSFits/test/tMSFitsSelection.cc000066400000000000000000000104561476623553700224710ustar00rootroot00000000000000//# Copyright (C) 1995,1996,1997,1999,2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void convert(String fitsName, String msName) { if (!Table::isReadable(msName)) { cout << "Converting FITS file called " << fitsName << " to and MS called " << msName << endl; MSFitsInput msfitsin(msName, fitsName); msfitsin.readFitsFile(); } } // This is a very simple test, not in the repository int main(int argc, const char* argv[]) { Input inputs(1); inputs.create("ms", "", "Initial measurement set"); inputs.create("fits", "", "Initial fits file"); inputs.readArguments (argc, argv); const String fitsName = inputs.getString("fits"); String msName = inputs.getString("ms"); if(msName.length() == 0) msName = "3C273XC1_tmp.ms"; if(fitsName.length() != 0) convert(fitsName, msName); // Do selection over newly created ms try { for(int i=0; i<4; i++) { MeasurementSet ms(msName); MSSelection select; switch(i) { case 0: select.setFieldExpr("0"); select.setSpwExpr("0"); break; case 1: select.setFieldExpr("1"); select.setSpwExpr("1"); break; case 2: select.setFieldExpr("0,1"); select.setSpwExpr("0,1"); break; case 3: select.setFieldExpr(">0"); select.setSpwExpr("0"); break; default: break; } cout << "Original table has rows " << ms.nrow() << endl; TableExprNode node = select.toTableExprNode(&ms); MS *mssel_ = new MS((ms)(node)); delete mssel_; cout << "TableExprNode has rows = " << node.nrow() << endl; Table tablesel(ms.tableName(), Table::Update); MeasurementSet mssel(tablesel(node, node.nrow() )); cout << "After mssel constructor called " << endl; mssel.rename(ms.tableName()+"/SELECTED_TABLE", Table::Scratch); mssel.flush(); if(mssel.nrow()==0){ cout << "Check your input, No data selected" << endl; } else { cout << "selected table has rows " << mssel.nrow() << endl; } } } catch (std::exception& x) { cout << "ERROR: " << x.what() << endl; } return 1; } casacore-3.7.1/msfits/MSFits/test/tMSFitsSelection.run000077500000000000000000000020051476623553700227020ustar00rootroot00000000000000#!/bin/sh #----------------------------------------------------------------------------- # Usage: tMSSelection.run #----------------------------------------------------------------------------- # This script executes the program tMSSelection to test if new the # measurement set selection is working. # The script supplies the names of all test tables found in the system. # It is meant to be run from assay, but can also be used standalone. #----------------------------------------------------------------------------- if [ ${#AIPSPATH} = 0 ] then echo "UNTESTED: tMSFitsSelection.run (AIPSPATH not defined)" exit 3 fi IN='3C273XC1.fits' AIPSDEMO=`echo $AIPSPATH | awk '{printf("%s/data/demo",$1)}'` FITS=`echo $AIPSDEMO $IN | awk '{printf("%s/%s", $1,$2)}'` MS=`echo $IN | sed 's/.fits/_tmp.ms/'` echo $AIPSDEMO echo $FITS echo $MS if [ ! -e $FITS ] then echo "UNTESTED: tMSFitsSelection.run ($FITS not found)" exit 3 fi $casa_checktool ./tMSFitsSelection fits=$FITS ms=$MS casacore-3.7.1/msfits/MSFits/test/tfits2ms.cc000066400000000000000000000051601476623553700210410ustar00rootroot00000000000000//# Copyright (C) 1995,1996,1997,1999,2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include int main(int argc, const char* argv[]) { try { Input inputs(1); inputs.create("ms", "", "Initial measurement set"); inputs.create("fits", "", "Initial fits file"); inputs.readArguments (argc, argv); const String fitsName = inputs.getString("fits"); const String msName = inputs.getString("ms"); if (!Table::isReadable(msName)) { if (fitsName.length() == 0) { String errorMsg = "Input ms called " + msName + " does not exist\n" + " and no FITS file is specified"; throw(AipsError(errorMsg)); } cout << "Converting FITS file called " << fitsName << " to and MS called " << msName << endl; MSFitsInput msfitsin(msName, fitsName); msfitsin.readFitsFile(); } } catch (std::exception& x) { cerr << x.what() << endl; cout << "FAIL!!!" << endl; return 1; } catch (...) { cerr << "Exception not derived from AipsError" << endl; cout << "FAIL" << endl; return 2; } cout << "OK" << endl; return 0; } // Local Variables: // compile-command: "gmake OPTLIB=1 tfits2ms" // End: casacore-3.7.1/msfits/MSFits/test/tfits2ms.run000077500000000000000000000017401476623553700212630ustar00rootroot00000000000000#!/bin/sh #----------------------------------------------------------------------------- # Usage: tfits2ms.run #----------------------------------------------------------------------------- # This script executes the program tfits2MS to test if the # conversion of MS to fits is working. # The script supplies the names of all test tables found in the system. # It is meant to be run from assay, but can also be used standalone. #----------------------------------------------------------------------------- if [ ${#AIPSPATH} = 0 ] then echo "UNTESTED: tfits2ms.run (AIPSPATH not defined)" exit 3 fi IN='3C273XC1.fits' AIPSDEMO=`echo $AIPSPATH | awk '{printf("%s/data/demo",$1)}'` FITS=`echo $AIPSDEMO $IN | awk '{printf("%s/%s", $1,$2)}'` MS=`echo $IN | sed 's/.fits/_tmp.ms/'` echo $AIPSDEMO echo $FITS echo $MS if [ ! -e $FITS ] then echo "UNTESTED: tfits2ms.run ($FITS not found)" exit 3 fi $casa_checktool ./tfits2ms fits=$FITS ms=$MS casacore-3.7.1/msfits/apps/000077500000000000000000000000001476623553700155745ustar00rootroot00000000000000casacore-3.7.1/msfits/apps/CMakeLists.txt000066400000000000000000000003341476623553700203340ustar00rootroot00000000000000foreach(prog ms2uvfits) add_executable (${prog} ${prog}.cc) add_pch_support(${prog}) target_link_libraries (${prog} casa_msfits ${CASACORE_ARCH_LIBS}) install(TARGETS ${prog}) endforeach(prog ms2uvfits) casacore-3.7.1/msfits/apps/ms2uvfits.cc000066400000000000000000000105031476623553700200440ustar00rootroot00000000000000//# j2convert.cc: This program demonstrates conversion of UVW for WSRT //# Copyright (C) 1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include int main (int argc, const char* argv[]) { try { // enable input in no-prompt mode Input inputs(1); // define the input structure inputs.version("20080124APS"); inputs.create ("ms", "", "Name of input MeasurementSet", "string"); inputs.create ("in", "", "Name of input MeasurementSet (synonym of ms)", "string"); inputs.create ("fitsfile", "", "Name of output FITS file", "string"); inputs.create ("out", "", "Name of output FITS file (synonym of out)", "string"); inputs.create ("column", "DATA", "Name of data column to write", "string"); inputs.create ("writesyscal", "T", "write SYSCAL info (TY and GC)", "bool"); inputs.create ("multisource", "T", "write multi-source FITS", "bool"); inputs.create ("combinespw", "T", "Combine spectral windows as one IF group", "bool"); inputs.create ("writestation", "F", "Write station names instead of antenna names", "bool"); inputs.create ("sensitivity", "0.1", "Sensitivity", "double"); // Fill the input structure from the command line. inputs.readArguments (argc, argv); // get and check the input file specification String msin (inputs.getString("ms")); if (msin == "") { msin = inputs.getString("in"); } if (msin == "") { throw (AipsError(" the MeasurementSet ms must be given")); } Path measurementSet (msin); cout << "The input MeasurementSet is: " << measurementSet.absoluteName() << endl; if (!measurementSet.isValid()) { throw (AipsError(" The MeasurementSet path is not valid")); } if (!File(measurementSet).exists()) { throw (AipsError(" The MeasurementSet file does not exist")); } // Get the fitsfile name. String fitsfile(inputs.getString("fitsfile")); if (fitsfile == "") { fitsfile = inputs.getString("out"); } if (fitsfile == "") { fitsfile = msin; fitsfile = fitsfile.before(Regex("\\.MS$")) + ".UVF"; } // Get the column name. String column(inputs.getString("column")); // Get the writesyscal. Bool writeSyscal(inputs.getBool("writesyscal")); // Get the multisource. Bool multisource(inputs.getBool("multisource")); // Get the multisource. Bool combinespw(inputs.getBool("combinespw")); // Get the writestation. Bool writestation(inputs.getBool("writestation")); // Get the sensitivity. Double sensitivity(inputs.getDouble("sensitivity")); // Now write the fits file. MSFitsOutput::writeFitsFile(fitsfile, MeasurementSet(msin), column, -1, -1, -1, writeSyscal, multisource, combinespw, writestation, sensitivity); } catch (std::exception& x) { cout << x.what() << endl; return 1; } cout << "ms2uvfits normally ended" << endl; return 0; } casacore-3.7.1/msfits/msfits.dox000066400000000000000000000027171476623553700166610ustar00rootroot00000000000000//# msfits.dox: doxygen description of msfits package //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ // \defgroup msfits msfits package (libcasa_msfits) // // The msfits package handles conversion of MeasurementSets to/from FITS //
          //
        • MSFits: // MeasurementSet to/from UVFITS or FITSIDI //
        casacore-3.7.1/python/000077500000000000000000000000001476623553700146455ustar00rootroot00000000000000casacore-3.7.1/python/CMakeLists-cmake3.14.txt000066400000000000000000000064451476623553700210020ustar00rootroot00000000000000message(STATUS "Looking for python2 specific environment...") find_package(Python2 REQUIRED COMPONENTS Interpreter Development NumPy) if (Python2_FOUND) find_package(Boost REQUIRED) if(${Boost_MAJOR_VERSION} EQUAL 1 AND ${Boost_MINOR_VERSION} GREATER 66) # Boost>=1.67 Python components require a Python version suffix set(BOOST_PYTHON_LIBRARY_NAME python${Python2_VERSION_MAJOR}${Python2_VERSION_MINOR} CACHE STRING "The name of the boost python library to search for") else() set(BOOST_PYTHON_LIBRARY_NAME python CACHE STRING "The name of the boost python library to search for") endif() find_package(Boost REQUIRED COMPONENTS ${BOOST_PYTHON_LIBRARY_NAME}) # copy the variables to their final destination set(PYTHON2_EXECUTABLE ${Python2_EXECUTABLE} CACHE FILEPATH "Path to Python2 interpreter") set(PYTHON2_LIBRARY ${Python2_LIBRARIES} CACHE PATH "Python2 library") set(PYTHON2_INCLUDE_DIR ${Python2_INCLUDE_DIRS} CACHE PATH "Python2 include folder") set(PYTHON2_FIND_PACKAGE_MESSAGE_DETAILS_PythonLibs ${FIND_PACKAGE_MESSAGE_DETAILS_PythonLibs} CACHE STRING "") set(PYTHON2_LIBRARIES ${Python2_LIBRARIES} PARENT_SCOPE) set(PYTHON2_NUMPY_INCLUDE_DIRS ${Python2_NumPy_INCLUDE_DIRS} PARENT_SCOPE) set(PYTHON2_Boost_LIBRARIES ${Boost_LIBRARIES} PARENT_SCOPE) set(PYTHON2_Boost_INCLUDE_DIRS ${Boost_INCLUDE_DIRS} PARENT_SCOPE) set(PYTHON2_Boost_PYTHON_LIBRARY_RELEASE ${Boost_PYTHON_LIBRARY_RELEASE} PARENT_SCOPE) set(PYTHON2_INCLUDE_DIRS ${Python2_INCLUDE_DIRS} PARENT_SCOPE) # to access the variables here we also need to set them in the local scope set(PYTHON2_LIBRARIES ${Python2_LIBRARIES} ) set(PYTHON2_NUMPY_INCLUDE_DIRS ${Python2_NumPy_INCLUDE_DIRS} ) set(PYTHON2_Boost_LIBRARIES ${Boost_LIBRARIES} ) set(PYTHON2_Boost_INCLUDE_DIRS ${Boost_INCLUDE_DIRS} ) set(PYTHON2_Boost_PYTHON_LIBRARY_RELEASE ${Boost_PYTHON_LIBRARY_RELEASE} ) set(PYTHON2_INCLUDE_DIRS ${Python2_INCLUDE_DIRS} ) endif(Python2_FOUND) include_directories (${PYTHON2_Boost_INCLUDE_DIRS} ${PYTHON2_NUMPY_INCLUDE_DIRS} ${PYTHON2_INCLUDE_DIRS}) add_library (casa_python Converters/PycArray.cc Converters/PycArrayNP.cc Converters/PycBasicData.cc Converters/PycExcp.cc Converters/PycImport.cc Converters/PycRecord.cc Converters/PycValueHolder.cc Converters/PycArray.h Converters/PycArrayComCC.h Converters/PycArrayComH.h Converters/PycArrayNP.h Converters/PycBasicData.h Converters/PycExcp.h Converters/PycRecord.h Converters/PycValueHolder.h Converters/PycArray.tcc ) target_link_libraries (casa_python casa_casa ${PYTHON2_Boost_LIBRARIES} ${PYTHON2_LIBRARIES} ${CASACORE_ARCH_LIBS}) install (TARGETS casa_python RUNTIME DESTINATION bin LIBRARY DESTINATION lib${LIB_SUFFIX} ARCHIVE DESTINATION lib${LIB_SUFFIX} LIBRARY PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) install (FILES Converters/PycArray.h Converters/PycArrayComCC.h Converters/PycArrayComH.h Converters/PycArrayNP.h Converters/PycBasicData.h Converters/PycExcp.h Converters/PycRecord.h Converters/PycValueHolder.h Converters/PycArray.tcc DESTINATION include/casacore/python/Converters ) install (FILES Converters.h DESTINATION include/casacore/python ) add_subdirectory (Converters/test ${EXCL_ALL}) casacore-3.7.1/python/CMakeLists-older-cmake.txt000066400000000000000000000101041476623553700215620ustar00rootroot00000000000000message(STATUS "Looking for python2 specific environment...") # tempororarly set variables used by findpython if (PYTHON2_EXECUTABLE) set(PYTHON_EXECUTABLE ${PYTHON2_EXECUTABLE} CACHE FILEPATH "") endif() if (PYTHON2_LIBRARY) set(PYTHON_LIBRARY ${PYTHON2_LIBRARY} CACHE FILEPATH "") endif() if (PYTHON2_INCLUDE_DIR) set(PYTHON_INCLUDE_DIR ${PYTHON2_INCLUDE_DIR} CACHE FILEPATH "") endif() if (PYTHON2_FIND_PACKAGE_MESSAGE_DETAILS_PythonLibs) set(FIND_PACKAGE_MESSAGE_DETAILS_PythonLibs ${PYTHON2_FIND_PACKAGE_MESSAGE_DETAILS_PythonLibs} CACHE FILEPATH "") endif() # Detect the python properties set(Python_FIND_VERSION 2) set(PythonInterp_FIND_VERSION_MAJOR 2) find_package(Python REQUIRED) if (PYTHONINTERP_FOUND) find_package(Boost REQUIRED) if(${Boost_MAJOR_VERSION} EQUAL 1 AND ${Boost_MINOR_VERSION} GREATER 66) # Boost>=1.67 Python components require a Python version suffix set(BOOST_PYTHON_LIBRARY_NAME python${PYTHON_VERSION_MAJOR}${PYTHON_VERSION_MINOR} CACHE STRING "The name of the boost python library to search for") else() set(BOOST_PYTHON_LIBRARY_NAME python CACHE STRING "The name of the boost python library to search for") endif() find_package(Boost REQUIRED COMPONENTS ${BOOST_PYTHON_LIBRARY_NAME}) find_package (NUMPY REQUIRED) # copy the variables to their final destination set(PYTHON2_EXECUTABLE ${PYTHON_EXECUTABLE} CACHE FILEPATH "Path to Python2 interpreter") set(PYTHON2_LIBRARY ${PYTHON_LIBRARY} CACHE PATH "Python2 library") set(PYTHON2_INCLUDE_DIR ${PYTHON_INCLUDE_DIR} CACHE PATH "Python2 include folder") set(PYTHON2_FIND_PACKAGE_MESSAGE_DETAILS_PythonLibs ${FIND_PACKAGE_MESSAGE_DETAILS_PythonLibs} CACHE STRING "") set(PYTHON2_LIBRARIES ${PYTHON_LIBRARIES} PARENT_SCOPE) set(PYTHON2_NUMPY_INCLUDE_DIRS ${NUMPY_INCLUDE_DIRS} PARENT_SCOPE) set(PYTHON2_Boost_LIBRARIES ${Boost_LIBRARIES} PARENT_SCOPE) set(PYTHON2_Boost_INCLUDE_DIRS ${Boost_INCLUDE_DIRS} PARENT_SCOPE) set(PYTHON2_Boost_PYTHON_LIBRARY_RELEASE ${Boost_PYTHON_LIBRARY_RELEASE} PARENT_SCOPE) set(PYTHON2_INCLUDE_DIRS ${PYTHON_INCLUDE_DIRS} PARENT_SCOPE) # to access the variables here we also need to set them in the local scope set(PYTHON2_LIBRARIES ${PYTHON_LIBRARIES} ) set(PYTHON2_NUMPY_INCLUDE_DIRS ${NUMPY_INCLUDE_DIRS} ) set(PYTHON2_Boost_LIBRARIES ${Boost_LIBRARIES} ) set(PYTHON2_Boost_INCLUDE_DIRS ${Boost_INCLUDE_DIRS} ) set(PYTHON2_Boost_PYTHON_LIBRARY_RELEASE ${Boost_PYTHON_LIBRARY_RELEASE} ) set(PYTHON2_INCLUDE_DIRS ${PYTHON_INCLUDE_DIRS} ) # Remove cached variable to not confuse user unset(PYTHON_EXECUTABLE CACHE) unset(PYTHON_LIBRARY CACHE) unset(PYTHON_INCLUDE_DIR CACHE) unset(FIND_PACKAGE_MESSAGE_DETAILS_PythonLibs CACHE) endif(PYTHONINTERP_FOUND) include_directories (${PYTHON2_Boost_INCLUDE_DIRS} ${PYTHON2_NUMPY_INCLUDE_DIRS} ${PYTHON2_INCLUDE_DIRS}) add_library (casa_python Converters/PycArray.cc Converters/PycArrayNP.cc Converters/PycBasicData.cc Converters/PycExcp.cc Converters/PycImport.cc Converters/PycRecord.cc Converters/PycValueHolder.cc Converters/PycArray.h Converters/PycArrayComCC.h Converters/PycArrayComH.h Converters/PycArrayNP.h Converters/PycBasicData.h Converters/PycExcp.h Converters/PycRecord.h Converters/PycValueHolder.h Converters/PycArray.tcc ) target_link_libraries (casa_python casa_casa ${PYTHON2_Boost_LIBRARIES} ${PYTHON2_LIBRARIES} ${CASACORE_ARCH_LIBS}) install (TARGETS casa_python RUNTIME DESTINATION bin LIBRARY DESTINATION lib${LIB_SUFFIX} ARCHIVE DESTINATION lib${LIB_SUFFIX} LIBRARY PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) install (FILES Converters/PycArray.h Converters/PycArrayComCC.h Converters/PycArrayComH.h Converters/PycArrayNP.h Converters/PycBasicData.h Converters/PycExcp.h Converters/PycRecord.h Converters/PycValueHolder.h Converters/PycArray.tcc DESTINATION include/casacore/python/Converters ) install (FILES Converters.h DESTINATION include/casacore/python ) add_subdirectory (Converters/test ${EXCL_ALL}) casacore-3.7.1/python/CMakeLists.txt000066400000000000000000000005221476623553700174040ustar00rootroot00000000000000# It would be nice to use CMAKE_VERSION instead but that requires CMake 2.6.3 if(${CMAKE_MAJOR_VERSION} GREATER 3 OR (${CMAKE_MAJOR_VERSION} EQUAL 3 AND ${CMAKE_MINOR_VERSION} GREATER 13)) include (${CMAKE_CURRENT_LIST_DIR}/CMakeLists-cmake3.14.txt) else() include (${CMAKE_CURRENT_LIST_DIR}/CMakeLists-older-cmake.txt) endif() casacore-3.7.1/python/Converters.h000066400000000000000000000120301476623553700171440ustar00rootroot00000000000000//# Converters.h: The Converters module - Boost.Python converters //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef PYRAP_CONVERTERS_H #define PYRAP_CONVERTERS_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // // Convert Casacore objects to/from Python (using Boost.Python) // // // // // //
      • Record class //
      • Record class // // // Converters contains functions to convert the important Casacore objects // to/from Python using the Boost.Python package. // Converters for the following Casacore classes exist: //
          //
        • Scalars of basic data types like Bool, Int, Float, Complex. //
        • casacore::String and std::string. //
        • casacore::Vector and std::vector of any type which can be converted // from a Python scalar, list, tuple, or 1-dim array. They are converted // back to a Python list. //
        • Record which is converted to/from a Python dict. //
        • ValueHolder which is converted to/from the appropriate Python type. // A ValueHolder is a class which can hold various value types: //
            //
          • Scalar of any basic data type. //
          • Record. //
          • N-dim Array of any basic data type. A casacore::Array can be // constructed from Python types like tuple, list, and numpy array // A Py_None object results in an empty array. // The conversion back is done to a numpy array. // An empty array is returned as an empty numpy array. //
            Because Casacore arrays are in Fortran order and numpy arrays // (preferably) in C order, the axes are reversed during conversion. //
            A 1-dim Array object is converted to a list, // while a higher dimensioned Array object is converted to/from // a dict containing the shape and the values as a list. // However, conversion from a numpy string array is supported. //
          // A ValueHolder is for instance used by the Table System to be able // to get or put data in a column of any type. // It can be used by any Python binding to convert arrays. Class // ValueHolder has functions to get or set the array. //
        • casacore::IPosition which can be converted // from a Python scalar, list, tuple, or 1-dim array. // It is converted back to a Python list. // An IPosition object represents an array shape or position, so its // values are reversed because of the different ordening of // Casacore and Python arrays. //
        • Exceptions, which are mapped to a Python RuntimeError // exception. Only the casacore::IterError exception is mapped // to a Python StopIteration exception. //
        // The converts from Python to C++ can handle some special numpy objects. // Such objects can also be contained in sequences or dicts. //
          //
        • Elements in a numpy array are called array scalars. They do not have // a python type such as int, but instead a type as // numpy.int32. // The converters can handle such types and convert them correctly to // a scalar. //
        • A numpy scalar array (e.g. array(1.0) is a somewhat // peculiar numpy object. It has an empty shape and cannot be indexed. // It is handled correctly by the from converters and handled as a // scalar value. //
        • An empty numpy object (e.g. array([])) is handled as // a None value. //
        //
        // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/python/Converters/000077500000000000000000000000001476623553700167775ustar00rootroot00000000000000casacore-3.7.1/python/Converters/PycArray.cc000066400000000000000000000104731476623553700210450ustar00rootroot00000000000000//# PycArray.cc: Class to convert an Array to/from Python //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION #include #include #include #include #include #include using namespace boost::python; namespace casacore { namespace python { Bool PycArrayCheck (PyObject* obj_ptr) { return numpy::PycArrayCheck(obj_ptr); } Bool PycArrayScalarCheck (PyObject* obj_ptr) { int type; return numpy::PycArrayScalarCheck(obj_ptr, type); } DataType PycArrayScalarType (PyObject* obj_ptr) { return numpy::PycArrayScalarType(obj_ptr); } ValueHolder casa_array_from_python::makeArray (PyObject* obj_ptr, Bool copyData) { if (! numpy::PycArrayCheck(obj_ptr)) { throw AipsError ("PycArray: python object is not a numpy array"); } return numpy::makeArray (obj_ptr, copyData); } ValueHolder casa_array_from_python::makeScalar (PyObject* obj_ptr) { int type; if (!numpy::PycArrayScalarCheck(obj_ptr, type)) { throw AipsError ("PycArray: python object is not a numpy array scalar"); } return numpy::makeScalar (obj_ptr, type); } ValueHolder casa_array_from_python::makeArrayFromDict (PyObject* obj_ptr) { if (! PyDict_Check(obj_ptr)) { throw AipsError ("PycArray: python object is not a dict"); } dict d = extract(obj_ptr)(); IPosition shp = extract(d.get("shape").ptr())(); Array arr = extract >(d.get("array").ptr())(); if (Int(arr.size()) != shp.product()) { throw AipsError("PycArray: array size mismatches the shape"); } return ValueHolder(arr.reform (shp)); } template <> object makePyArrayObject (casacore::Array const& arr) { object a = to_list< Array >::makeobject (arr); if (arr.ndim() == 1) { return a; } dict d; d.setdefault (std::string("shape"), to_list::makeobject (arr.shape())); d.setdefault (std::string("array"), a); return d; } // Instantiate the templates. template boost::python::object makePyArrayObject (casacore::Array const& arr); template boost::python::object makePyArrayObject (casacore::Array const& arr); template boost::python::object makePyArrayObject (casacore::Array const& arr); template boost::python::object makePyArrayObject (casacore::Array const& arr); template boost::python::object makePyArrayObject (casacore::Array const& arr); template boost::python::object makePyArrayObject (casacore::Array const& arr); template boost::python::object makePyArrayObject (casacore::Array const& arr); template boost::python::object makePyArrayObject (casacore::Array const& arr); template boost::python::object makePyArrayObject (casacore::Array const& arr); template boost::python::object makePyArrayObject (casacore::Array const& arr); template boost::python::object makePyArrayObject (casacore::Array const& arr); }} casacore-3.7.1/python/Converters/PycArray.h000066400000000000000000000070141476623553700207040ustar00rootroot00000000000000//# PycArray.h: Class to convert an Array to/from Python //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef PYRAP_PYCARRAY_H #define PYRAP_PYCARRAY_H //# Includes // include first to avoid _POSIX_C_SOURCE redefined warnings #include #include #include #include #include #include #include namespace casacore { namespace python { // // A class to convert an Array to/from Python objects. // // // // // // // Check if the PyObject is an array object. Bool PycArrayCheck (PyObject* obj_ptr); // Check if the PyObject is an array scalar object. Bool PycArrayScalarCheck (PyObject* obj_ptr); // Get the data type of the array scalar object. // It returns TpBool, TpInt, TpFloat, or TpComplex. // TpOther is returned if unrecognized. DataType PycArrayScalarType (PyObject* obj_ptr); struct casa_array_from_python { // Constructs an Array from a Python object. // If copyData=False, the array data is only copied if needed meaning // that the Array object in the ValueHolder can reference the data in // Python array. // That should only be used if the ValueHolder and its Array will be // destructed before the Python array. static ValueHolder makeArray(PyObject* obj_ptr, Bool copyData=False); // Construct an Array from a special Python dict object. static ValueHolder makeArrayFromDict (PyObject* obj_ptr); // Construct a scalar from an array scalar (i.e. element in array). static ValueHolder makeScalar (PyObject* obj_ptr); }; // Do the actual making of the PyArrayObject. // Specialize for strings. // template boost::python::object makePyArrayObject (casacore::Array const& arr); template <> boost::python::object makePyArrayObject (casacore::Array const& arr); // // Convert Array to Python. template struct casa_array_to_python { static boost::python::object makeobject (Array const& arr) { return makePyArrayObject (arr); } static PyObject* convert (Array const& c) { return boost::python::incref(makeobject(c).ptr()); } }; }} #endif casacore-3.7.1/python/Converters/PycArray.tcc000066400000000000000000000031251476623553700212250ustar00rootroot00000000000000//# PycArray.h: Class to convert an Array to/from Python //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef PYRAP_PYCARRAY_TCC #define PYRAP_PYCARRAY_TCC #include #include #include namespace casacore { namespace python { template boost::python::object makePyArrayObject (casacore::Array const& arr) { return numpy::makePyArrayObject (arr); } }} #endif casacore-3.7.1/python/Converters/PycArrayComCC.h000066400000000000000000000352371476623553700215610ustar00rootroot00000000000000//# PycArrayCom.h: Common code to convert an Array to/from a Python array //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #if PY_MAJOR_VERSION >= 3 #define IS_PY3K #endif Bool PycArrayCheck (PyObject* obj_ptr) { if (!PyArray_API) { if (!isImported()) return False; loadAPI(); } return PyArray_Check (obj_ptr); } Bool isImported() { using namespace boost::python; // PySys_GetObject uses char* instead of const char*, so use a cast. const char* modStr = "modules"; PyObject* mods = PySys_GetObject(const_cast(modStr)); dict d = extract(mods)(); return d.has_key(PYC_USE_PYARRAY); } void loadAPI() { if (!PyArray_API) { if (!importArray() || !PyArray_API) { throw AipsError ("PycArray: failed to load the " PYC_USE_PYARRAY " API"); } } } template struct TypeConvTraits { typedef T casa_type; typedef void* python_type; static NPY_TYPES pyType() { throw AipsError ("PycArray: unknown casa type"); } }; template <> struct TypeConvTraits { typedef casacore::Bool casa_type; typedef npy_bool python_type; static NPY_TYPES pyType() { return NPY_BOOL; } }; template <> struct TypeConvTraits { typedef casacore::uChar casa_type; typedef npy_uint16 python_type; // Note: numarray uInt8 is Bool static NPY_TYPES pyType() { return NPY_UINT16; } }; template <> struct TypeConvTraits { typedef casacore::Short casa_type; typedef npy_int16 python_type; static NPY_TYPES pyType() { return NPY_INT16; } }; template <> struct TypeConvTraits { typedef casacore::uShort casa_type; typedef npy_uint16 python_type; static NPY_TYPES pyType() { return NPY_UINT16; } }; template <> struct TypeConvTraits { typedef casacore::Int casa_type; typedef npy_int32 python_type; static NPY_TYPES pyType() { return NPY_INT32; } }; template <> struct TypeConvTraits { typedef casacore::uInt casa_type; typedef npy_uint32 python_type; static NPY_TYPES pyType() { return NPY_UINT32; } }; template <> struct TypeConvTraits { typedef casacore::Int64 casa_type; typedef npy_int64 python_type; static NPY_TYPES pyType() { return NPY_INT64; } }; template <> struct TypeConvTraits { typedef casacore::uInt64 casa_type; typedef npy_uint64 python_type; static NPY_TYPES pyType() { return NPY_UINT64; } }; template <> struct TypeConvTraits { typedef casacore::Float casa_type; typedef npy_float32 python_type; static NPY_TYPES pyType() { return NPY_FLOAT32; } }; template <> struct TypeConvTraits { typedef casacore::Double casa_type; typedef npy_float64 python_type; static NPY_TYPES pyType() { return NPY_FLOAT64; } }; template <> struct TypeConvTraits { typedef casacore::Complex casa_type; typedef npy_complex64 python_type; static NPY_TYPES pyType() { return NPY_COMPLEX64; } }; template <> struct TypeConvTraits { typedef casacore::DComplex casa_type; typedef npy_complex128 python_type; static NPY_TYPES pyType() { return NPY_COMPLEX128; } }; template <> struct TypeConvTraits { typedef casacore::String casa_type; typedef ::PyObject* python_type; static NPY_TYPES pyType() { return NPY_OBJECT; } }; // This one is only used to convert numpy BYTE and SBYTE to casa short. // There is no back conversion, so an exception is thrown. template <> struct TypeConvTraits { typedef signed char casa_type; typedef npy_int8 python_type; static NPY_TYPES pyType() { throw AipsError ("PycArray: unknown casa type"); } }; template void ArrayCopy::toPy (void* to, const T* from, size_t nr) { if (sizeof(T) == sizeof(typename TypeConvTraits::python_type)) { ::memcpy (to, from, nr*sizeof(T)); } else { typename TypeConvTraits::python_type* dst = static_cast::python_type*>(to); for (size_t i=0; i void ArrayCopy::fromPy (T* to, const void* from, size_t nr) { if (sizeof(T) == sizeof(typename TypeConvTraits::python_type)) { ::memcpy (to, from, nr*sizeof(T)); } else { const typename TypeConvTraits::python_type* src = static_cast::python_type*>(from); for (size_t i=0; i Array ArrayCopy::toArray (const IPosition& shape, void* data, bool copy) { // If the python array was contiguous, etc., we can directly use // its data because the Array used is only temporary. // However, if a copy of the Python array was made in PycArray.cc, // we cannot do that because the Python copy is out of scope when // the Array object gets used. if (!copy) { if (sizeof(T) == sizeof(typename TypeConvTraits::python_type)) { return Array (shape, static_cast(data), SHARE); } } Array arr(shape); fromPy (arr.data(), data, arr.size()); return arr; } void ArrayCopy::toPy (void* to, const Complex* from, size_t nr) { if (sizeof(Complex) != sizeof(TypeConvTraits::python_type)) { throw AipsError("PycArray: size of Complex data type mismatches"); } ::memcpy (to, from, nr*sizeof(Complex)); } void ArrayCopy::fromPy (Complex* to, const void* from, size_t nr) { if (sizeof(Complex) != sizeof(TypeConvTraits::python_type)) { throw AipsError("PycArray: size of Complex data type mismatches"); } ::memcpy (to, from, nr*sizeof(Complex)); } Array ArrayCopy::toArray (const IPosition& shape, void* data, bool copy) { if (!copy) { if (sizeof(Complex) == sizeof(TypeConvTraits::python_type)) { return Array (shape, static_cast(data), SHARE); } } Array arr(shape); fromPy (arr.data(), data, arr.size()); return arr; } void ArrayCopy::toPy (void* to, const DComplex* from, size_t nr) { if (sizeof(DComplex) != sizeof(TypeConvTraits::python_type)) { throw AipsError("PycArray: size of DComplex data type mismatches"); } ::memcpy (to, from, nr*sizeof(DComplex)); } void ArrayCopy::fromPy (DComplex* to, const void* from, size_t nr) { if (sizeof(DComplex) != sizeof(TypeConvTraits::python_type)) { throw AipsError("PycArray: size of DComplex data type mismatches"); } ::memcpy (to, from, nr*sizeof(DComplex)); } Array ArrayCopy::toArray (const IPosition& shape, void* data, bool copy) { if (!copy) { if (sizeof(DComplex) == sizeof(TypeConvTraits::python_type)) { return Array (shape, static_cast(data), SHARE); } } Array arr(shape); fromPy (arr.data(), data, arr.size()); return arr; } void ArrayCopy::toPy (void* to, const String* from, size_t nr) { PyObject** dst = static_cast(to); for (size_t i=0; i::fromPy (String* to, const void* from, size_t nr) { using namespace boost::python; PyObject** src = (PyObject**)from; for (size_t i=0; i py_elem_hdl(src[i]); object py_elem_obj(py_elem_hdl); extract elem_proxy(py_elem_obj); to[i] = elem_proxy(); } } Array ArrayCopy::toArray (const IPosition& shape, void* data, bool) { Array arr(shape); fromPy (arr.data(), data, arr.size()); return arr; } ValueHolder makeArray (PyObject* obj_ptr, Bool copyData) { if (! PycArrayCheck(obj_ptr)) { throw AipsError ("PycArray: python object is not an array"); } PyArrayObject* po = (PyArrayObject*)obj_ptr; boost::python::object obj; bool docopy = copyData; // copy data if wanted or needed if (! PyArray_ISCONTIGUOUS(po) || ! PyArray_ISALIGNED(po) || PyArray_ISBYTESWAPPED(po)) { boost::python::handle<> py_hdl(obj_ptr); boost::python::object py_obj(py_hdl); // incr refcount, because ~object decrements it boost::python::incref(obj_ptr); obj = py_obj.attr("copy")(); po = (PyArrayObject*)(obj.ptr()); docopy = true; } // Swap axes, because Casacore has row minor and Python row major order. // A scalar is treated as a vector with length 1. int nd = PyArray_NDIM(po); IPosition shp(1, 1); if (nd > 0) { shp.resize (nd); for (int i=0; i 0) { AlwaysAssert (PyArray_ISCONTIGUOUS(po) ///&& PyArray_ISALIGNED(po) fails on MIPS (see issue 531) && !PyArray_ISBYTESWAPPED(po), AipsError); } // Create the correct array. switch (PyArray_TYPE(po)) { case NPY_BOOL: return ValueHolder (ArrayCopy::toArray(shp, PyArray_DATA(po), docopy)); case NPY_INT16: return ValueHolder (ArrayCopy::toArray(shp, PyArray_DATA(po), docopy)); case NPY_UINT16: return ValueHolder (ArrayCopy::toArray(shp, PyArray_DATA(po), docopy)); case NPY_INT32: return ValueHolder (ArrayCopy::toArray(shp, PyArray_DATA(po), docopy)); case NPY_UINT32: return ValueHolder (ArrayCopy::toArray(shp, PyArray_DATA(po), docopy)); case NPY_INT64: return ValueHolder (ArrayCopy::toArray(shp, PyArray_DATA(po), docopy)); case NPY_FLOAT32: return ValueHolder (ArrayCopy::toArray(shp, PyArray_DATA(po), docopy)); case NPY_FLOAT64: return ValueHolder (ArrayCopy::toArray(shp, PyArray_DATA(po), docopy)); case NPY_COMPLEX64: return ValueHolder (ArrayCopy::toArray(shp, PyArray_DATA(po), docopy)); case NPY_COMPLEX128: return ValueHolder (ArrayCopy::toArray(shp, PyArray_DATA(po), docopy)); case NPY_OBJECT: return ValueHolder (ArrayCopy::toArray(shp, PyArray_DATA(po), docopy)); default: // Some types can be the same as other types, so they cannot // be used in the switch (compiler complains). // This is true for BYTE and SBYTE which can equal to BOOL in numarray. // Similarly for STRING which exists for numpy and is set to // INT for numarray. if (PyArray_TYPE(po) == NPY_UINT64) { Array arr = ArrayCopy::toArray(shp, PyArray_DATA(po), False); Array res(arr.shape()); convertArray (res, arr); return ValueHolder(res); } else if (PyArray_TYPE(po) == NPY_INT8) { Array arr = ArrayCopy::toArray(shp, PyArray_DATA(po), False); Array res(arr.shape()); convertArray (res, arr); return ValueHolder(res); } else if (PyArray_TYPE(po) == NPY_UINT8) { // Copy using signed char, because uChar is mapped to Short in the Traits. Array arr = ArrayCopy::toArray(shp, PyArray_DATA(po), False); Array res(arr.shape()); void* varr = &arr; Array* uarr = static_cast*>(varr); convertArray (res, *uarr); return ValueHolder(res); } else if (PyArray_TYPE(po) == NPY_STRING) { size_t slen = 0; if (nd > 0) { slen = PyArray_STRIDES(po)[nd-1]; } return ValueHolder (ArrayCopyStr_toArray(shp, PyArray_DATA(po), slen)); } else if (PyArray_TYPE(po) == NPY_UNICODE) { size_t slen = 0; if (nd > 0) { slen = PyArray_STRIDES(po)[nd-1]; } return ValueHolder (ArrayCopyUnicode_toArray(shp, PyArray_DATA(po), slen)); } break; } throw AipsError ("PycArray: unknown python array data type"); } // Instantiate the various templates. template struct ArrayCopy; template struct ArrayCopy; template struct ArrayCopy; template struct ArrayCopy; template struct ArrayCopy; template struct ArrayCopy; template struct ArrayCopy; template struct ArrayCopy; template struct ArrayCopy; template struct ArrayCopy; template struct ArrayCopy; template boost::python::object makePyArrayObject (casacore::Array const& arr); template boost::python::object makePyArrayObject (casacore::Array const& arr); template boost::python::object makePyArrayObject (casacore::Array const& arr); template boost::python::object makePyArrayObject (casacore::Array const& arr); template boost::python::object makePyArrayObject (casacore::Array const& arr); template boost::python::object makePyArrayObject (casacore::Array const& arr); template boost::python::object makePyArrayObject (casacore::Array const& arr); template boost::python::object makePyArrayObject (casacore::Array const& arr); template boost::python::object makePyArrayObject (casacore::Array const& arr); template boost::python::object makePyArrayObject (casacore::Array const& arr); template boost::python::object makePyArrayObject (casacore::Array const& arr); casacore-3.7.1/python/Converters/PycArrayComH.h000066400000000000000000000063341476623553700214570ustar00rootroot00000000000000//# PycArrayComH.h: Common code to convert an Array to/from a Python array //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // Check if the PyObject is an array object. Bool PycArrayCheck (PyObject* obj_ptr); // Check if the API is or can be imported. // Bool isImported(); inline Bool canImport() { return True; } Bool importArray(); void loadAPI(); // // Convert the python array to a Casacore array in the ValueHolder. // If copyData is True, the array data is always copied. // Otherwise only if needed. ValueHolder makeArray (PyObject* obj_ptr, Bool copyData); // Copy/convert the array data as needed. // Specializations are defined for complex and string. // template struct ArrayCopy { static void toPy (void* to, const T* from, size_t nr); static void fromPy (T* to, const void* from, size_t nr); static Array toArray (const IPosition& shape, void* data, bool copy); }; template <> struct ArrayCopy { static void toPy (void* to, const Complex* from, size_t nr); static void fromPy (Complex* to, const void* from, size_t nr); static Array toArray (const IPosition& shape, void* data, bool copy); }; template <> struct ArrayCopy { static void toPy (void* to, const DComplex* from, size_t nr); static void fromPy (DComplex* to, const void* from, size_t nr); static Array toArray (const IPosition& shape, void* data, bool copy); }; template <> struct ArrayCopy { static void toPy (void* to, const String* from, size_t nr); static void fromPy (String* to, const void* from, size_t nr); static Array toArray (const IPosition& shape, void* data, bool copy); }; // Array ArrayCopyStr_toArray (const IPosition& shape, void* data, size_t slen); Array ArrayCopyUnicode_toArray (const IPosition& shape, void* data, size_t slen); // Convert a Casacore array to a Python array object. template boost::python::object makePyArrayObject (casacore::Array const& arr); casacore-3.7.1/python/Converters/PycArrayNP.cc000066400000000000000000000246651476623553700213130ustar00rootroot00000000000000//# PycArrayNP.cc: Convert a Casacore Array to a Python numpy array //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION #include #include #include #include #include #include #define PYC_USE_PYARRAY "numpy" namespace casacore { namespace python { namespace numpy { Bool importArray() { // numpy has diferent versions of import_array (from version 1.0.1 on). // Therefore import_array1 is used. import_array1(True); return True; } Array ArrayCopyStr_toArray (const IPosition& shape, void* data, size_t slen) { // This code converts from a numpy String array. // The longest string determines the length of each value. // They are padded with zeroes if shorter. Array arr(shape); String* to = arr.data(); const char* src = static_cast(data); size_t nr = arr.size(); for (size_t i=0; i ArrayCopyUnicode_toArray (const IPosition& shape, void* data, size_t slen) { // This code converts from a numpy Unicode array (which // is encoded as UTF32). // The longest string determines the length of each value. // They are padded with zeroes if shorter. // slen is the byte stride. // // This is likely to be slow because each string is converted to UTF-8 // via the Python C API. Array arr(shape); String* to = arr.data(); const char* src = static_cast(data); size_t nr = arr.size(); for (size_t i=0; i( PyUnicode_DecodeUTF32(src, slen, NULL, NULL))); if (unicode) { boost::python::object utf8(boost::python::handle<>( PyUnicode_AsUTF8String(unicode.ptr()))); if (utf8) to[i] = String(PyBytes_AS_STRING(utf8.ptr())); } src += slen; } return arr; } //# Code to deal with numpy array scalars (i.e. a value returned //# by taking an element from a numpy array). // Check if the object is an array scalar and return its type. Bool PycArrayScalarCheck (PyObject* obj_ptr, int& type) { if (!PyArray_API) { if (!isImported()) return False; loadAPI(); } // No scalar if array scalar nor 0-dim array. if (! PyArray_CheckScalar(obj_ptr)) { return False; } // See if the object is a 0-dim array. Bool is0dim = PyArray_Check(obj_ptr); const int ntypes = 13; // Define them in order of expected usage. int types[ntypes] = { NPY_INT32, NPY_INT64, NPY_FLOAT32, NPY_FLOAT64, NPY_COMPLEX64, NPY_COMPLEX128, NPY_UINT32, NPY_UINT64, NPY_BOOL, NPY_INT16, NPY_UINT16, NPY_INT8, NPY_UINT8}; for (int i=0; iob_type == (PyTypeObject*)PyArray_TypeObjectFromType(types[i])) { type = types[i]; return True; } } } return False; } DataType PycArrayScalarType (PyObject* obj_ptr) { int type; if (! PycArrayScalarCheck(obj_ptr, type)) { return TpOther; } switch (type) { case NPY_BOOL: return TpBool; case NPY_INT8: case NPY_UINT8: case NPY_INT16: case NPY_UINT16: case NPY_INT32: case NPY_UINT32: return TpInt; case NPY_INT64: case NPY_UINT64: return TpInt64; case NPY_FLOAT32: case NPY_FLOAT64: return TpDouble; case NPY_COMPLEX64: case NPY_COMPLEX128: return TpDComplex; default: return TpOther; } } ValueHolder makeScalar (PyObject* obj_ptr, int type) { if (PyArray_Check(obj_ptr)) { PyArrayObject* obj = (PyArrayObject*)obj_ptr; switch (type) { case NPY_BOOL: return ValueHolder(*(::npy_bool*)(PyArray_DATA(obj)) != 0); case NPY_INT8: return ValueHolder(int(*(::npy_int8*)(PyArray_DATA(obj)))); case NPY_UINT8: return ValueHolder(uint(*(::npy_uint8*)(PyArray_DATA(obj)))); case NPY_INT16: return ValueHolder(int(*(::npy_int16*)(PyArray_DATA(obj)))); case NPY_UINT16: return ValueHolder(uint(*(::npy_uint16*)(PyArray_DATA(obj)))); case NPY_INT32: return ValueHolder(int(*(::npy_int32*)(PyArray_DATA(obj)))); case NPY_UINT32: return ValueHolder(uint(*(::npy_uint32*)(PyArray_DATA(obj)))); case NPY_INT64: return ValueHolder(Int64(*(::npy_int64*)(PyArray_DATA(obj)))); case NPY_UINT64: return ValueHolder(Int64(*(::npy_uint64*)(PyArray_DATA(obj)))); case NPY_FLOAT32: return ValueHolder(float(*(::npy_float32*)(PyArray_DATA(obj)))); case NPY_FLOAT64: return ValueHolder(double(*(::npy_float64*)(PyArray_DATA(obj)))); case NPY_COMPLEX64: return ValueHolder(*(Complex*)(PyArray_DATA(obj))); case NPY_COMPLEX128: return ValueHolder(*(DComplex*)(PyArray_DATA(obj))); default: break; } } else { char buffer[32]; PyArray_ScalarAsCtype(obj_ptr, buffer); switch (type) { case NPY_BOOL: { ::npy_bool* ptr = (::npy_bool* )buffer; return ValueHolder(*ptr != 0); } case NPY_INT8: { ::npy_int8* ptr = (::npy_int8*)buffer; return ValueHolder(Short(*ptr)); } case NPY_UINT8: { ::npy_uint8* ptr = (::npy_uint8*)buffer; return ValueHolder(uShort(*ptr)); } case NPY_INT16: { ::npy_int16* ptr = (::npy_int16*)buffer; return ValueHolder(Short(*ptr)); } case NPY_UINT16: { ::npy_uint16* ptr = (::npy_uint16*)buffer; return ValueHolder(uShort(*ptr)); } case NPY_INT32: { ::npy_int32* ptr = (::npy_int32*)buffer; return ValueHolder(Int(*ptr)); } case NPY_UINT32: { ::npy_uint32* ptr = (::npy_uint32*)buffer; return ValueHolder(uInt(*ptr)); } case NPY_INT64: { ::npy_int64* ptr = (::npy_int64*)buffer; return ValueHolder(Int64(*ptr)); } case NPY_UINT64: { ::npy_uint64* ptr = (::npy_uint64*)buffer; return ValueHolder(Int64(*ptr)); } case NPY_FLOAT32: { ::npy_float32* ptr = (::npy_float32*)buffer; return ValueHolder(float(*ptr)); } case NPY_FLOAT64: { ::npy_float64* ptr = (::npy_float64*)buffer; return ValueHolder(double(*ptr)); } case NPY_COMPLEX64: { Complex* ptr = (Complex*)buffer; return ValueHolder(*ptr); } case NPY_COMPLEX128: { DComplex* ptr = (DComplex*)buffer; return ValueHolder(*ptr); } default: break; } } throw AipsError("invalid data type"); } void register_convert_arrayscalars() { // Register as casa types. // A type like ssize_t maps to Int or Long (depending on machine). array_scalar_from_python(); array_scalar_from_python(); array_scalar_from_python(); array_scalar_from_python(); array_scalar_from_python(); array_scalar_from_python(); array_scalar_from_python(); array_scalar_from_python(); array_scalar_from_python(); array_scalar_from_python(); array_scalar_from_python(); array_scalar_from_python(); array_scalar_from_python(); array_scalar_from_python(); array_scalar_from_python(); } #include template boost::python::object makePyArrayObject (casacore::Array const& arr) { // Load the API if needed. if (!PyArray_API) loadAPI(); // Swap axes, because Casacore has row minor and Python row major order. // A Python array needs at least 1 dimension, otherwise it's a scalar. int nd = arr.ndim(); vector newshp(1, 0); if (nd == 0) { nd = 1; } else { newshp.resize (nd); const IPosition& shp = arr.shape(); for (int i=0; i::pyType())); // Copy the data to numarray. if (arr.size() > 0) { casacore::Bool deleteIt; const T* src = arr.getStorage(deleteIt); ArrayCopy::toPy (PyArray_DATA(po), src, arr.size()); arr.freeStorage(src, deleteIt); } // Return the python array. return boost::python::object(boost::python::handle<>((PyObject*)po)); } }}} casacore-3.7.1/python/Converters/PycArrayNP.h000066400000000000000000000117041476623553700211430ustar00rootroot00000000000000//# PycArrayNP.h: Class to convert an Array to/from a Python numpy array //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef PYRAP_PYCARRAYNP_H #define PYRAP_PYCARRAYNP_H // include first to avoid _POSIX_C_SOURCE redefined warnings #include #include #include #include #include namespace casacore { namespace python { namespace numpy { #define PYC_USE_PYARRAY "numpy" #include #undef PYC_USE_PYARRAY //# Define functions to deal with numpy array scalars. // Check if it is an array scalar object. bool PycArrayScalarCheck (PyObject* obj, int& type); // Get the data type of the array scalar object. // It returns TpBool, TpInt, TpFloat, or TpComplex. // TpOther is returned if unrecognized. DataType PycArrayScalarType (PyObject* obj_ptr); // Make a scalar object. ValueHolder makeScalar (PyObject* obj, int type); // Register all array scalar converters. void register_convert_arrayscalars(); // Templated helper function to get a value from a ValueHolder. // Specialize for each type supported. // template T getScalar (const ValueHolder&); template<> inline Bool getScalar (const ValueHolder& vh) { return vh.asBool(); } template<> inline Char getScalar (const ValueHolder& vh) { return vh.asShort(); } template<> inline uChar getScalar (const ValueHolder& vh) { return vh.asuChar(); } template<> inline Short getScalar (const ValueHolder& vh) { return vh.asShort(); } template<> inline uShort getScalar (const ValueHolder& vh) { return vh.asuShort(); } template<> inline Int getScalar (const ValueHolder& vh) { return vh.asInt(); } template<> inline uInt getScalar (const ValueHolder& vh) { return vh.asuInt(); } template<> inline Long getScalar (const ValueHolder& vh) { return vh.asInt(); } template<> inline uLong getScalar (const ValueHolder& vh) { return vh.asuInt(); } template<> inline Int64 getScalar (const ValueHolder& vh) { return vh.asInt(); } template<> inline uInt64 getScalar (const ValueHolder& vh) { return vh.asuInt(); } template<> inline Float getScalar (const ValueHolder& vh) { return vh.asFloat(); } template<> inline Double getScalar (const ValueHolder& vh) { return vh.asDouble(); } template<> inline Complex getScalar (const ValueHolder& vh) { return vh.asComplex(); } template<> inline DComplex getScalar (const ValueHolder& vh) { return vh.asDComplex(); } // // Struct with static functions to convert a numpy array scalar to // the templated type (e.g. Int). template struct array_scalar_from_python { array_scalar_from_python() { boost::python::converter::registry::push_back( &convertible, &construct, boost::python::type_id()); } // Check if it is a type we can convert. static void* convertible(PyObject* obj_ptr) { int type; if (PycArrayScalarCheck(obj_ptr, type)) { return obj_ptr; } return 0; } // Constructs a T from a Python array scalar object. static void construct( PyObject* obj_ptr, boost::python::converter::rvalue_from_python_stage1_data* data) { using namespace boost::python; void* storage = ((converter::rvalue_from_python_storage*) data)->storage.bytes; new (storage) T(); data->convertible = storage; int type; PycArrayScalarCheck (obj_ptr, type); *static_cast(storage) = getScalar (makeScalar(obj_ptr, type)); } }; }}} #endif casacore-3.7.1/python/Converters/PycBasicData.cc000066400000000000000000000112561476623553700216020ustar00rootroot00000000000000//# PycBasicData.cc: Convert casa data types to/from python //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION #include #include using namespace boost::python; namespace casacore { namespace python { std::map pyregistry::_registry; bool pyregistry::get (const std::string& name) { return _registry[name]; } void pyregistry::set (const std::string& name) { _registry[name] = true; } void convert_casa_string::reg() { std::string tname(typeid(casacore::String).name()); if (! pyregistry::get (tname)) { pyregistry::set (tname); boost::python::to_python_converter(); casa_string_from_python_str(); } } void convert_casa_iposition::reg() { std::string tname(typeid(casacore::IPosition).name()); if (! pyregistry::get (tname)) { pyregistry::set (tname); casa_iposition_to_list(); from_python_sequence < casacore::IPosition, casa_reversed_variable_capacity_policy > (); } } void register_convert_basicdata() { casacore::python::numpy::register_convert_arrayscalars(); casacore::python::register_convert_casa_string(); casacore::python::register_convert_casa_iposition(); casacore::python::register_convert_casa_vector(); casacore::python::register_convert_casa_vector(); casacore::python::register_convert_casa_vector(); casacore::python::register_convert_casa_vector(); casacore::python::register_convert_casa_vector(); casacore::python::register_convert_casa_vector(); casacore::python::register_convert_casa_vector(); } bool getSeqObject (object& py_obj) { // Restriction to list, tuple, iter, xrange until // Boost.Python overload resolution is enhanced. // PySequence_Check() is used for numarray. PyObject* obj_ptr = py_obj.ptr(); if (!(PyList_Check(obj_ptr) || PyTuple_Check(obj_ptr) || PyIter_Check(obj_ptr) || PyRange_Check(obj_ptr) || PySequence_Check(obj_ptr) )) return false; // Must be a measurable sequence. int obj_size = -1; bool done = false; // Try to get attribute size, because length fails for a numpy scalar. try { // A numpy scalar size should be 1. object py_tmp = py_obj.attr("size"); if (extract(py_tmp) == 1) { done = true; } } catch (...) { PyErr_Clear(); } // If it failed, try to get the length. if (!done) { obj_size = PyObject_Length(obj_ptr); if (obj_size < 0) { done = true; PyErr_Clear(); } } // If we seem to have a numpy/numarray scalar, try to flatten it. // Return the flattened object. if (done) { done = false; object py_flat; // Try if the object is a scalar numarray/numpy object which // can be flattened to a vector num object. try { py_flat = py_obj.attr("flatten")(); // numpy attr name done = true; } catch (...) { PyErr_Clear(); } if (!done) { try { py_flat = py_obj.attr("flat"); // numarray attr name done = true; } catch (...) { PyErr_Clear(); } } if (done) py_obj = py_flat; } // If it failed, try to get the length. if (!done) { obj_size = PyObject_Length(obj_ptr); if (obj_size >= 0) { done = true; } else { PyErr_Clear(); } } return done; } }} casacore-3.7.1/python/Converters/PycBasicData.h000066400000000000000000000502121476623553700214370ustar00rootroot00000000000000//# PycBasicData.h: Convert casa data types to/from python //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef PYRAP_PYCBASICDATA_H #define PYRAP_PYCBASICDATA_H // include python first to avoid _POSIX_C_SOURCE redefined warnings #include #include #include #include #include #include #include #include #include #include #if PY_MAJOR_VERSION >= 3 #define IS_PY3K #endif // Define classes and functions to convert the basic data types and // containers to and from Python. namespace casacore { namespace python { // Prevent a converter from being registered multiple times. class pyregistry { public: static bool get (const std::string& name); static void set (const std::string& name); private: static std::map _registry; }; // Check if the given object is a sequence object. // If so, return true. // py_obj gets changed if the given object was a scalar numpy/numarray. // In that case it is flattened. bool getSeqObject (boost::python::object& py_obj); // Convert a String object to python. struct casa_string_to_python_str { static boost::python::object makeobject(String const& s) { return boost::python::object((const std::string&)s); } static PyObject* convert(String const& s) { return boost::python::incref(makeobject(s).ptr()); } }; // Convert a String object from python. struct casa_string_from_python_str { casa_string_from_python_str() { boost::python::converter::registry::push_back( &convertible, &construct, boost::python::type_id()); } static void* convertible(PyObject* obj_ptr) { #ifdef IS_PY3K if (!PyUnicode_Check(obj_ptr)) return 0; #else if (!PyString_Check(obj_ptr) && !PyUnicode_Check(obj_ptr)) return 0; #endif return obj_ptr; } static void construct( PyObject* obj_ptr, boost::python::converter::rvalue_from_python_stage1_data* data) { char* value = NULL; boost::python::object temp_bytes_obj; if (PyUnicode_Check(obj_ptr)) { PyObject * temp_bytes = PyUnicode_AsEncodedString(obj_ptr, "UTF-8", "strict"); // Owned reference if (temp_bytes != NULL) { // Take over lifetime management of temp_bytes temp_bytes_obj = boost::python::object(boost::python::handle<>(temp_bytes)); value = PyBytes_AS_STRING(temp_bytes); } else { boost::python::throw_error_already_set(); } #ifndef IS_PY3K } else if (PyString_Check(obj_ptr)) { value = PyString_AsString(obj_ptr); #endif } else { boost::python::throw_error_already_set(); } if (value == 0) boost::python::throw_error_already_set(); void* storage = ( (boost::python::converter::rvalue_from_python_storage*) data)->storage.bytes; new (storage) String(value); data->convertible = storage; } }; // Default operations on all containers for conversion from Python // container to C++ one. // Copied from // scitbx/include/scitbx/boost_python/container_conversions.h that is // described in the // Boost.Python FAQ. // @author Ralf W. Grosse-Kunstleve of // Lawrence Berkeley National Laboratory struct default_policy { static bool check_convertibility_per_element() { return true; } template static bool check_size(boost::type, std::size_t) { return true; } template static void assert_size(boost::type, std::size_t) {} template static void reserve(ContainerType&, std::size_t) {} }; // Operations on containers that have variable capacity for // conversion from Python container to C++ one. // Copied from // scitbx/include/scitbx/boost_python/container_conversions.h that is // described in the // Boost.Python FAQ. // @author Ralf W. Grosse-Kunstleve of // Lawrence Berkeley National Laboratory struct stl_variable_capacity_policy : default_policy { template static void reserve(ContainerType& a, std::size_t sz) { a.reserve(sz); } template static void set_value(ContainerType& a, std::size_t i, ValueType const& v) { AlwaysAssert(a.size() == i, AipsError); a.push_back(v); } }; struct casa_variable_capacity_policy : default_policy { template static void reserve(ContainerType& a, std::size_t sz) { a.resize(sz); } template static void set_value(ContainerType& a, std::size_t i, ValueType const& v) { assert(a.size() > i); a[i] = v; } }; struct casa_reversed_variable_capacity_policy : default_policy { template static void reserve(ContainerType& a, std::size_t sz) { a.resize(sz); } template static void set_value(ContainerType& a, std::size_t i, ValueType const& v) { assert(a.size() > i); a[a.size() - i - 1] = v; } }; // A wrapper of a conversion function to convert a STL vector to a // Python list. This class satisfies the requirements of the // boost::python::to_python_converter conversion template argument. // Copied from // scitbx/include/scitbx/boost_python/container_conversions.h that is // described in the // Boost.Python FAQ. // @author Ralf W. Grosse-Kunstleve of // Lawrence Berkeley National Laboratory template < typename ContainerType > struct to_list { // Creates and returns a Python list from the elements copied // from a STL container. The ContainerType must be a container // with STL iterators defined on it. // It may contain any type of object supported by the // boost::python::object constructor. static boost::python::object makeobject (ContainerType const& c) { boost::python::list result; typename ContainerType::const_iterator i = c.begin(); typename ContainerType::const_iterator iEnd = c.end(); for( ; i != iEnd; ++i) { result.append(*i); } return result; } static PyObject* convert (ContainerType const& c) { return boost::python::incref(makeobject(c).ptr()); } }; //# Make specialisations for various types. template <> struct to_list { typedef IPosition ContainerType; static boost::python::list makeobject (ContainerType const& c) { // Reverse IPosition values. boost::python::list result; for (int i=c.size()-1; i>=0; --i) { result.append(c[i]); } return result; } static PyObject* convert (ContainerType const& c) { return boost::python::incref(makeobject(c).ptr()); } }; //# This specialisation is needed because on OS-X 10.9 clang-3.5 with //# Boost-Python 1.57 gives a compile error //# /opt/casa/01/include/boost/python/converter/arg_to_python.hpp:209:9: //# error: no matching constructor for initialization of //# 'boost::python::converter::detail::arg_to_python_base' //# : arg_to_python_base(&x, registered::converters) template <> struct to_list > { typedef std::vector ContainerType; static boost::python::list makeobject (ContainerType const& c) { boost::python::list result; ContainerType::const_iterator i = c.begin(); ContainerType::const_iterator iEnd = c.end(); for( ; i != iEnd; ++i) { bool b = *i; result.append(b); } return result; } static PyObject* convert (ContainerType const& c) { return boost::python::incref(makeobject(c).ptr()); } }; template <> struct to_list > { typedef std::vector ContainerType; static boost::python::list makeobject (ContainerType const& c) { boost::python::list result; ContainerType::const_iterator i = c.begin(); ContainerType::const_iterator iEnd = c.end(); for( ; i != iEnd; ++i) { result.append((std::string const&)(*i)); } return result; } static PyObject* convert (ContainerType const& c) { return boost::python::incref(makeobject(c).ptr()); } }; template <> struct to_list > { typedef casacore::Array ContainerType; static boost::python::object makeobject (ContainerType const& c) { boost::python::list result; ContainerType::const_iterator i = c.begin(); ContainerType::const_iterator iEnd = c.end(); for( ; i != iEnd; ++i) { result.append((std::string const&)(*i)); } return result; } static PyObject* convert (ContainerType const& c) { return boost::python::incref(makeobject(c).ptr()); } }; template <> struct to_list > { typedef casacore::Vector ContainerType; static boost::python::object makeobject (ContainerType const& c) { boost::python::list result; ContainerType::const_iterator i = c.begin(); ContainerType::const_iterator iEnd = c.end(); for( ; i != iEnd; ++i) { result.append((std::string const&)(*i)); } return result; } static PyObject* convert (ContainerType const& c) { return boost::python::incref(makeobject(c).ptr()); } }; // Converts an STL vector or casa Array of T objects to Python list. // Copied from // scitbx/include/scitbx/boost_python/container_conversions.h that is // described in the // Boost.Python FAQ. // @author Ralf W. Grosse-Kunstleve of // Lawrence Berkeley National Laboratory template < typename T > struct std_vector_to_list { std_vector_to_list () { boost::python::to_python_converter < std::vector < T >, to_list < std::vector < T > > > (); } }; template < typename T > struct casa_array_to_list { casa_array_to_list () { boost::python::to_python_converter < casacore::Array < T >, to_list < casacore::Array < T > > > (); } }; template < typename T > struct casa_vector_to_list { casa_vector_to_list () { boost::python::to_python_converter < casacore::Vector < T >, to_list < casacore::Vector < T > > > (); } }; struct casa_iposition_to_list { casa_iposition_to_list () { boost::python::to_python_converter < casacore::IPosition, to_list < casacore::IPosition > > (); } }; // Conversion of Python sequence to C++ container. // Copied from // scitbx/include/scitbx/boost_python/container_conversions.h that is // described in the // Boost.Python FAQ. // @author Ralf W. Grosse-Kunstleve of // Lawrence Berkeley National Laboratory template struct from_python_sequence { typedef typename ContainerType::value_type container_element_type; from_python_sequence() { boost::python::converter::registry::push_back( &convertible, &construct, boost::python::type_id()); } // Appears to return @a obj_ptr if it is type of Python sequence // that can be convertible to C++ container. static void* convertible(PyObject* obj_ptr) { using namespace boost::python; handle<> py_hdl(obj_ptr); if (PyErr_Occurred()) { PyErr_Clear(); return 0; } object py_obj(py_hdl); incref(obj_ptr); // incr refcount, because ~object decrements it // Accept single values. if (PyBool_Check(obj_ptr) || PyLong_Check(obj_ptr) || PyFloat_Check(obj_ptr) || PyComplex_Check(obj_ptr) #ifndef IS_PY3K || PyInt_Check(obj_ptr) || PyString_Check(obj_ptr) #endif || PyUnicode_Check(obj_ptr)) { extract elem_proxy(py_obj); if (!elem_proxy.check()) return 0; return obj_ptr; } // An array scalar is accepted. if (PycArrayScalarCheck(obj_ptr)) { return obj_ptr; } // Get the sequence object. // It can be a numarray/numpy scalar in which case // it fills py_obj with a flattened array. if (! getSeqObject (py_obj)) { return 0; } // Check the sequence. // It must be convertible to an iterator. handle<> obj_iter(allow_null(PyObject_GetIter(py_obj.ptr()))); if (!obj_iter.get()) { PyErr_Clear(); return 0; } if (!check_convertibility (py_obj.ptr())) { return 0; } return obj_ptr; } // Constructs a C++ container from a Python sequence. static void construct( PyObject* obj_ptr, boost::python::converter::rvalue_from_python_stage1_data* data) { using namespace boost::python; using boost::python::converter::rvalue_from_python_storage; void* storage = ( (rvalue_from_python_storage*) data)->storage.bytes; new (storage) ContainerType(); data->convertible = storage; ContainerType& result = *((ContainerType*)storage); if (PyBool_Check(obj_ptr) || PyLong_Check(obj_ptr) || PyFloat_Check(obj_ptr) || PyComplex_Check(obj_ptr) || PyUnicode_Check(obj_ptr) #ifndef IS_PY3K || PyString_Check(obj_ptr) || PyInt_Check(obj_ptr) #endif || PycArrayScalarCheck(obj_ptr)) { extract elem_proxy(obj_ptr); ConversionPolicy::reserve(result, 1); ConversionPolicy::set_value(result, 0, elem_proxy()); return; } handle<> py_hdl(obj_ptr); object py_obj = object(py_hdl); incref(obj_ptr); // incr refcount, because ~object decrements it assert (getSeqObject (py_obj)); fill_container (result, py_obj.ptr()); // ConversionPolicy::reserve(result, 1); // ConversionPolicy::set_value(result, 0, // extract(py_flat.attr("__getitem__")(0))); } // Constructs a C++ container from a Python sequence. static ContainerType make_container(PyObject* obj_ptr) { ContainerType result; fill_container (result, obj_ptr); return result; } private: static void fill_container (ContainerType& result, PyObject* obj_ptr) { using namespace boost::python; int obj_size = PyObject_Length(obj_ptr); handle<> obj_iter(PyObject_GetIter(obj_ptr)); ConversionPolicy::reserve(result, obj_size); std::size_t i=0; for(;;i++) { handle<> py_elem_hdl(allow_null(PyIter_Next(obj_iter.get()))); if (PyErr_Occurred()) throw_error_already_set(); if (!py_elem_hdl.get()) break; // end of iteration object py_elem_obj(py_elem_hdl); extract elem_proxy(py_elem_obj); ConversionPolicy::set_value(result, i, elem_proxy()); } ConversionPolicy::assert_size(boost::type(), i); } static bool check_convertibility(PyObject* obj_ptr) { using namespace boost::python; handle<> obj_iter(allow_null(PyObject_GetIter(obj_ptr))); if (!obj_iter.get()) { // must be convertible to an iterator PyErr_Clear(); return false; } int obj_size = PyObject_Length(obj_ptr); if (obj_size < 0) { // must be a measurable sequence PyErr_Clear(); return false; } if (ConversionPolicy::check_convertibility_per_element()) { if (!ConversionPolicy::check_size( boost::type(), obj_size)) return false; // All elements in a range and array have the same type, so // need to check the first element only. bool is_same = PyRange_Check(obj_ptr) || (PySequence_Check(obj_ptr) && !PyTuple_Check(obj_ptr) && !PyList_Check(obj_ptr)); int i = 0; for (;;i++) { handle<> py_elem_hdl(allow_null(PyIter_Next(obj_iter.get()))); if (PyErr_Occurred()) { PyErr_Clear(); return false; } if (!py_elem_hdl.get()) break; // end of iteration object py_elem_obj(py_elem_hdl); extract elem_proxy(py_elem_obj); if (!elem_proxy.check()) return false; if (is_same) break; // all elements are of the same type } if (!is_same) assert(i == obj_size ); } return true; } }; // Register the String conversion. struct convert_casa_string { static void reg(); }; inline void register_convert_casa_string() { convert_casa_string::reg(); } // Register the IPosition conversion. struct convert_casa_iposition { static void reg(); }; inline void register_convert_casa_iposition() { convert_casa_iposition::reg(); } // Register the std::vector conversions. template < typename T > struct convert_std_vector { static void reg() { std::string tname(typeid(std::vector).name()); if (! pyregistry::get (tname)) { pyregistry::set (tname); std_vector_to_list < T > (); from_python_sequence < std::vector < T >, stl_variable_capacity_policy > (); } } }; template < typename T > inline void register_convert_std_vector() { convert_std_vector::reg(); } // Register the casacore::Vector conversions. template < typename T > struct convert_casa_vector { static void reg() { std::string tname(typeid(casacore::Vector).name()); if (! pyregistry::get (tname)) { pyregistry::set (tname); casa_array_to_list < T > (); casa_vector_to_list < T > (); from_python_sequence < casacore::Vector < T >, casa_variable_capacity_policy > (); } } }; template < typename T > inline void register_convert_casa_vector() { convert_casa_vector::reg(); } // Register all standard basic conversions. // These are String, IPosition, Vector, Vector, // Vector, Vector. void register_convert_basicdata(); }} #endif casacore-3.7.1/python/Converters/PycExcp.cc000066400000000000000000000036671476623553700206750ustar00rootroot00000000000000//# PycExcp.cc: Functions to convert a C++ exception to Python //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include //# The following include is necessary to work around a Boost-Python problem. #ifndef PYRAP_NO_BOOSTPYTHON_FIX # include #endif #include namespace casacore { namespace python { void translate_iterexcp (const casacore::IterError& e) { // Use the Python 'C' API to set up an exception object PyErr_SetString(PyExc_StopIteration, e.what()); } //# Note that the most general exception must be registered first. void register_convert_excp() { boost::python::register_exception_translator (&translate_iterexcp); } }} casacore-3.7.1/python/Converters/PycExcp.h000066400000000000000000000031551476623553700205270ustar00rootroot00000000000000//# PycExcp.h: Functions to convert a C++ exception to Python //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef PYRAP_PYCEXCP_H #define PYRAP_PYCEXCP_H #include namespace casacore { namespace python { // Convert an IterError exception to a Python StopIteration. // In this way an iteration loop can be done. void translate_iterexcp (const casacore::IterError& e); // Register exception translators for casacore::IterError. void register_convert_excp(); }} #endif casacore-3.7.1/python/Converters/PycImport.cc000066400000000000000000000053711476623553700212420ustar00rootroot00000000000000//# PycImport.cc: Function to import a module and class in Python //# Copyright (C) 2015 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #if PY_MAJOR_VERSION >= 3 #define IS_PY3K #endif namespace casacore { namespace python { boost::python::object PycImport (const String& moduleName, const String& className) { try { // Initialize the Python interpreter. // Note: a second call is a no-op. Py_Initialize(); // Insert the current working directory into the python path, // because Boost-Python does not do that. // (from http://stackoverflow.com/questions/9285384/ // how-does-import-work-with-boost-python-from-inside-python-files) string workingDir = Path(".").absoluteName(); char path[] = "path"; // warning if "path" is used below PyObject* sysPath = PySys_GetObject(path); #ifdef IS_PY3K PyList_Insert (sysPath, 0, PyUnicode_FromString(workingDir.c_str())); #else PyList_Insert (sysPath, 0, PyString_FromString(workingDir.c_str())); #endif // First import main. boost::python::object mainModule = boost::python::import ("__main__"); // Import the given module. boost::python::object pyModule = boost::python::import (moduleName.c_str()); // Get the python class object from the imported module. return pyModule.attr (className.c_str()); } catch (boost::python::error_already_set const &) { // handle the exception in some way PyErr_Print(); throw; } } }} casacore-3.7.1/python/Converters/PycImport.h000066400000000000000000000060601476623553700211000ustar00rootroot00000000000000//# PycImport.h: Function to import a module and class in Python //# Copyright (C) 2015 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef PYTHON_PYCIMPORT #define PYTHON_PYCIMPORT #include #include #include namespace casacore { namespace python { // This function can be used to create a Python class from C++. //
        It returns the object to be used to create a class instance. // It initializes Python, imports the main module and the given // module, and finally creates the class object. // Before the import it adds the working directory to the Python // module search path, because Boost-Python does not do that. //
        For example: // // // Create the class object. // boost::python::object pyClass = // casacore::python::PycImport("modulenm", "classnm"); // // Register the converters needed. // casacore::python::register_convert_excp(); // casacore::python::register_convert_basicdata(); // casacore::python::register_convert_casa_valueholder(); // casacore::python::register_convert_casa_record(); // casacore::python::register_convert_std_vector(); // casacore::python::register_convert_std_vector(); // casacore::python::register_convert_std_vector(); // // Instantiate the Python class object with possible arguments // // for the __init__ function. // boost::python::object classObject = pyClass(arg1, arg2); // // Invoke the 'setup' function in the Python object // boost::python::object result = // classObject.attr("setup")(arg1, arg2, arg3); // // Extract the result (a dict in this case) as a Record. // Record rec = boost::python::extract(result); // boost::python::object PycImport (const String& moduleName, const String& className); }} #endif casacore-3.7.1/python/Converters/PycRecord.cc000066400000000000000000000064201476623553700212020ustar00rootroot00000000000000//# PycRecord.cc: Class to convert a Record to/from Python //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { namespace python { boost::python::dict casa_record_to_python::makeobject (Record const& rec) { boost::python::dict d; // Copy over the record field by field uInt nf = rec.nfields(); for (uInt i=0; i*) data)->storage.bytes; new (storage) Record(); data->convertible = storage; Record& result = *((Record*)storage); result = makeRecord (obj_ptr); } Record casa_record_from_python::makeRecord (PyObject* obj_ptr) { using namespace boost::python; AlwaysAssert (PyDict_Check(obj_ptr), AipsError); dict d = extract(obj_ptr)(); list keys = d.keys(); Record result; handle<> obj_iter(PyObject_GetIter(keys.ptr())); std::size_t i=0; for(;;i++) { handle<> py_elem_hdl(allow_null(PyIter_Next(obj_iter.get()))); if (PyErr_Occurred()) throw_error_already_set(); if (!py_elem_hdl.get()) break; // end of iteration object py_elem_key(py_elem_hdl); result.defineFromValueHolder (extract(py_elem_key)(), casa_value_from_python::makeValueHolder(d.get(py_elem_key).ptr())); } return result; } bool convert_casa_record::_done = false; void convert_casa_record::reg() { if (! _done) { _done = true; boost::python::to_python_converter(); casa_record_from_python(); } } }} casacore-3.7.1/python/Converters/PycRecord.h000066400000000000000000000052731476623553700210510ustar00rootroot00000000000000//# PycRecord.h: Class to convert a Record to/from Python //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef PYRAP_PYCRECORD_H #define PYRAP_PYCRECORD_H //# Includes // include first to avoid _POSIX_C_SOURCE redefined warnings #include #include #include namespace casacore { namespace python { // // A class to convert a (Table)Record to/from Python objects. // // // // // // // convert casacore::Record to PyDict struct casa_record_to_python { static boost::python::dict makeobject (Record const& rec); static PyObject* convert (Record const& rec) { return boost::python::incref(makeobject(rec).ptr()); } }; struct casa_record_from_python { casa_record_from_python() { boost::python::converter::registry::push_back( &convertible, &construct, boost::python::type_id()); } // Check if it is a type we can convert. static void* convertible(PyObject* obj_ptr); // Constructs a Record from a Python object. static void construct( PyObject* obj_ptr, boost::python::converter::rvalue_from_python_stage1_data* data); static Record makeRecord (PyObject* obj_ptr); }; // Register the Record conversion. struct convert_casa_record { static void reg(); static bool _done; }; inline void register_convert_casa_record() { convert_casa_record::reg(); } }} #endif casacore-3.7.1/python/Converters/PycValueHolder.cc000066400000000000000000000252501476623553700222000ustar00rootroot00000000000000//# PycValueHolder.cc: Class to convert a ValueHolder to/from Python //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #if PY_MAJOR_VERSION >= 3 #define IS_PY3K #endif namespace casacore { namespace python { boost::python::object casa_value_to_python::makeobject (ValueHolder const& vh) { if (vh.isNull()) { return boost::python::object(boost::python::handle<>(Py_None)); } switch (vh.dataType()) { case TpBool: return boost::python::object(vh.asBool()); case TpShort: case TpInt: return boost::python::object(vh.asInt()); case TpUChar: case TpUShort: case TpUInt: return boost::python::object(vh.asuInt()); case TpInt64: return boost::python::object(vh.asInt64()); case TpFloat: case TpDouble: return boost::python::object(vh.asDouble()); case TpComplex: case TpDComplex: return boost::python::object(vh.asDComplex()); case TpString: return boost::python::object((std::string const&)(vh.asString())); case TpArrayBool: return casa_array_to_python::makeobject (vh.asArrayBool()); case TpArrayUChar: return casa_array_to_python::makeobject (vh.asArrayuChar()); case TpArrayShort: return casa_array_to_python::makeobject (vh.asArrayShort()); case TpArrayInt: return casa_array_to_python::makeobject (vh.asArrayInt()); case TpArrayUInt: return casa_array_to_python::makeobject (vh.asArrayuInt()); case TpArrayInt64: return casa_array_to_python::makeobject (vh.asArrayInt64()); case TpArrayFloat: return casa_array_to_python::makeobject (vh.asArrayFloat()); case TpArrayDouble: return casa_array_to_python::makeobject (vh.asArrayDouble()); case TpArrayComplex: return casa_array_to_python::makeobject (vh.asArrayComplex()); case TpArrayDComplex: return casa_array_to_python::makeobject (vh.asArrayDComplex()); case TpArrayString: return casa_array_to_python::makeobject (vh.asArrayString()); case TpRecord: return casa_record_to_python::makeobject (vh.asRecord()); default: throw AipsError ("PycValueHolder: unknown casa data type " + String::toString(vh.dataType())); } } void* casa_value_from_python::convertible(PyObject* obj_ptr) { if (! (PyBool_Check(obj_ptr) #ifndef IS_PY3K || PyInt_Check(obj_ptr) || PyString_Check(obj_ptr) #endif || PyLong_Check(obj_ptr) || PyFloat_Check(obj_ptr) || PyComplex_Check(obj_ptr) || PyUnicode_Check(obj_ptr) || PyDict_Check(obj_ptr) || PyList_Check(obj_ptr) || PyTuple_Check(obj_ptr) || PyIter_Check(obj_ptr) || PyRange_Check(obj_ptr) || PySequence_Check(obj_ptr) || PycArrayCheck(obj_ptr) || PycArrayScalarCheck(obj_ptr) )) { // An empty numarray is Py_None, so accept that. if (obj_ptr != Py_None) { return 0; } } return obj_ptr; } void casa_value_from_python::construct (PyObject* obj_ptr, boost::python::converter::rvalue_from_python_stage1_data* data) { using namespace boost::python; using boost::python::converter::rvalue_from_python_storage; // dito using boost::python::throw_error_already_set; // dito void* storage = ((rvalue_from_python_storage*) data)->storage.bytes; new (storage) ValueHolder(); data->convertible = storage; ValueHolder& result = *((ValueHolder*)storage); result = makeValueHolder (obj_ptr); } ValueHolder casa_value_from_python::makeValueHolder (PyObject* obj_ptr) { using namespace boost::python; // An empty numarray is Py_None, so return an empty 0-dim Array. if (obj_ptr == Py_None) { return ValueHolder(0, True); } // First do array scalar check, otherwise PyInt_Check or so might // match depending on the machine type (32 or 64 bit). // In such a case an np.int64 is treated as Int instead of Int64. if (PycArrayScalarCheck(obj_ptr)) { return casa_array_from_python::makeScalar (obj_ptr); } else if (PyBool_Check(obj_ptr)) { return ValueHolder(extract(obj_ptr)()); } else if (PyLong_Check(obj_ptr)) { return ValueHolder(extract(obj_ptr)()); #ifndef IS_PY3K } else if (PyInt_Check(obj_ptr)) { Int64 v = extract(obj_ptr)(); if (Int(v) == v) { return ValueHolder(Int(v)); } return ValueHolder(v); #endif } else if (PyFloat_Check(obj_ptr)) { return ValueHolder(extract(obj_ptr)()); } else if (PyComplex_Check(obj_ptr)) { return ValueHolder(extract >(obj_ptr)()); #ifdef IS_PY3K } else if (PyUnicode_Check(obj_ptr)) { #else } else if (PyString_Check(obj_ptr) || PyUnicode_Check(obj_ptr)) { #endif return ValueHolder(String(extract(obj_ptr)())); } else if (PyDict_Check(obj_ptr)) { dict d = extract(obj_ptr)(); if (d.has_key("shape") && d.has_key("array")) { return casa_array_from_python::makeArrayFromDict(obj_ptr); } return ValueHolder(casa_record_from_python::makeRecord (obj_ptr)); } else if (PycArrayCheck(obj_ptr)) { return casa_array_from_python::makeArray (obj_ptr); } else { return toVector (obj_ptr); } throw AipsError ("PycValueHolder: unknown python data type"); } // A bit similar to PycBasicData.h ValueHolder casa_value_from_python::toVector (PyObject* obj_ptr) { DataType dt = checkDataType (obj_ptr); switch (dt) { case TpBool: return ValueHolder(from_python_sequence< Vector, casa_variable_capacity_policy >::make_container (obj_ptr)); case TpInt: return ValueHolder(from_python_sequence< Vector, casa_variable_capacity_policy >::make_container (obj_ptr)); case TpUInt: return ValueHolder(from_python_sequence< Vector, casa_variable_capacity_policy >::make_container (obj_ptr)); case TpInt64: return ValueHolder(from_python_sequence< Vector, casa_variable_capacity_policy >::make_container (obj_ptr)); case TpDouble: return ValueHolder(from_python_sequence< Vector, casa_variable_capacity_policy >::make_container (obj_ptr)); case TpDComplex: return ValueHolder(from_python_sequence< Vector, casa_variable_capacity_policy >::make_container (obj_ptr)); case TpString: return ValueHolder(from_python_sequence< Vector, casa_variable_capacity_policy >::make_container (obj_ptr)); case TpOther: // empty sequence is set as empty 1-dim array return ValueHolder(1, True); default: break; } throw AipsError ("PycValueHolder: python data type could not be handled"); } DataType casa_value_from_python::checkDataType (PyObject* obj_ptr) { using namespace boost::python; // Restriction to list, tuple, iter, xrange until // Boost.Python overload resolution is enhanced. if (!(PyList_Check(obj_ptr) || PyTuple_Check(obj_ptr) || PyIter_Check(obj_ptr) || PyRange_Check(obj_ptr) || PySequence_Check(obj_ptr) )) { return TpOther; } handle<> obj_iter(allow_null(PyObject_GetIter(obj_ptr))); if (!obj_iter.get()) { // must be convertible to an iterator PyErr_Clear(); return TpOther; } DataType result = TpOther; int i = 0; for (;;i++) { handle<> py_elem_hdl(allow_null(PyIter_Next(obj_iter.get()))); if (PyErr_Occurred()) { PyErr_Clear(); return TpOther; } if (!py_elem_hdl.get()) break; // end of iteration object py_elem_obj(py_elem_hdl); DataType dt; if (PycArrayScalarCheck (py_elem_obj.ptr())) { dt = PycArrayScalarType (py_elem_obj.ptr()); } else if (PyBool_Check (py_elem_obj.ptr())) { dt = TpBool; #ifndef IS_PY3K } else if (PyInt_Check (py_elem_obj.ptr())) { dt = TpInt; } else if (PyString_Check(py_elem_obj.ptr())) { dt = TpString; #endif } else if (PyLong_Check (py_elem_obj.ptr())) { dt = TpInt64; } else if (PyFloat_Check (py_elem_obj.ptr())) { dt = TpDouble; } else if (PyComplex_Check (py_elem_obj.ptr())) { dt = TpDComplex; } else if (PyUnicode_Check (py_elem_obj.ptr())) { dt = TpString; } else { throw AipsError ("PycValueHolder: unknown python data type"); } if (result == TpOther) { result = dt; // first time } else if (dt != result) { // bool, string, and numeric cannot be mixed. if (result == TpBool || result == TpString || dt == TpBool || dt == TpString) { throw AipsError ("PycValueHolder: incompatible types in sequence"); } // Use the 'highest' type. if (result != TpDComplex) { if (dt == TpDComplex) { result = dt; } else if (result != TpDouble) { if (dt == TpDouble) { result = dt; } else if (result != TpInt64) { result = dt; } } } } } return result; } bool convert_casa_valueholder::_done = false; void convert_casa_valueholder::reg() { if (! _done) { _done = true; boost::python::to_python_converter(); casa_value_from_python(); } } }} casacore-3.7.1/python/Converters/PycValueHolder.h000066400000000000000000000057651476623553700220530ustar00rootroot00000000000000//# PycValueHolder.h: Class to convert a ValueHolder to/from Python //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef PYRAP_PYCVALUEHOLDER_H #define PYRAP_PYCVALUEHOLDER_H //# Includes // include first to avoid _POSIX_C_SOURCE redefined warnings #include #include #include namespace casacore { namespace python { // // A class to convert a ValueHolder to/from Python objects. // // // // // // struct casa_value_to_python { static boost::python::object makeobject (ValueHolder const&); static PyObject* convert (ValueHolder const& c) { return boost::python::incref(makeobject(c).ptr()); } }; struct casa_value_from_python { casa_value_from_python() { boost::python::converter::registry::push_back( &convertible, &construct, boost::python::type_id()); } // Check if it is a type we can convert. static void* convertible(PyObject* obj_ptr); // Constructs a ValueHolder from a Python object. static void construct( PyObject* obj_ptr, boost::python::converter::rvalue_from_python_stage1_data* data); // Make a ValueHolder from all possible python data types. static ValueHolder makeValueHolder (PyObject* obj_ptr); // Make a vector from a python sequence. static ValueHolder toVector (PyObject* obj_ptr); // Get (and check) the data type in a python sequence. static DataType checkDataType (PyObject* obj_ptr); }; // Register the ValueHolder conversion. struct convert_casa_valueholder { static void reg(); static bool _done; }; inline void register_convert_casa_valueholder() { convert_casa_valueholder::reg(); } }} #endif casacore-3.7.1/python/Converters/test/000077500000000000000000000000001476623553700177565ustar00rootroot00000000000000casacore-3.7.1/python/Converters/test/CMakeLists.txt000066400000000000000000000004421476623553700225160ustar00rootroot00000000000000include_directories ("..") add_library(tConvert MODULE tConvert.cc) SET_TARGET_PROPERTIES(tConvert PROPERTIES PREFIX "_") target_link_libraries (tConvert casa_python ${PYTHON_LIBRARIES}) add_test (tConvert ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./tConvert) add_dependencies(check tConvert) casacore-3.7.1/python/Converters/test/tConvert.cc000066400000000000000000000134061476623553700220750ustar00rootroot00000000000000//# tConvert.cc: Test program for libpython's C++/Python converters //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include using namespace boost::python; namespace casacore { namespace python { struct TConvert { TConvert() {} Bool testbool (Bool in) {cout << "bool " << in << endl; return in;} Int testint (Int in) {cout << "Int " << in << endl; return in;} Int64 testint64 (Int64 in) {cout << "Int64 " << in << endl; return in;} Int testssize (::ssize_t in) {cout << "ssize " << in << endl; return in;} Float testfloat (Float in) {cout << "Float " << in << endl; return in;} Double testdouble (Double in) {cout << "Double " << in << endl; return in;} Complex testcomplex (const Complex& in) {cout << "Complex " << in << endl; return in;} DComplex testdcomplex (const DComplex& in) {cout << "DComplex " << in << endl; return in;} String teststring (const String& in) {cout << "String " << in << endl; String out=in; return out;} String testunicode (const String& in) {cout << "Unicode " << in << endl; String out=in; return out;} Record testrecord (const Record& in) {cout << "Record "; in.print(cout); cout << endl; return in;} ValueHolder testvh (const ValueHolder& in) {cout << "VH " << in.dataType() << endl; return in;} Vector testvecbool (const Vector& in) {cout << "VecBool " << in << endl; return in;} Vector testvecint (const Vector& in) {cout << "VecInt " << in << endl; return in;} Vector testveccomplex (const Vector& in) {cout << "VecComplex " << in << endl; return in;} Vector testvecstr (const Vector& in) {cout << "VecStr " << in << endl; return in;} std::vector teststdvecbool (const std::vector& in) {cout << "vecbool " << in << endl; return in;} std::vector teststdvecuint (const std::vector& in) {cout << "vecuInt " << in << endl; return in;} std::vector > teststdvecvecuint (const std::vector >& in) {cout << "vecvecuInt " << in << endl; return in;} std::vector teststdvecvh (const std::vector& in) {cout << "vecvh " << in.size() << endl; return in;} IPosition testipos (const IPosition& in) {cout << "IPos " << in << endl; return in;} void testIterError() {throw IterError();} }; void testConvert() { class_ ("tConvert", init<>()) .def ("testbool", &TConvert::testbool) .def ("testint", &TConvert::testint) .def ("testint64", &TConvert::testint64) .def ("testssize", &TConvert::testssize) .def ("testfloat", &TConvert::testfloat) .def ("testdouble", &TConvert::testdouble) .def ("testcomplex", &TConvert::testcomplex) .def ("testdcomplex", &TConvert::testdcomplex) .def ("teststring", &TConvert::teststring) .def ("testunicode", &TConvert::testunicode) .def ("testrecord", &TConvert::testrecord) .def ("testvh", &TConvert::testvh) .def ("testvecbool", &TConvert::testvecbool) .def ("testvecint", &TConvert::testvecint) .def ("testveccomplex", &TConvert::testveccomplex) .def ("testvecstr", &TConvert::testvecstr) .def ("teststdvecbool", &TConvert::teststdvecbool) .def ("teststdvecuint", &TConvert::teststdvecuint) .def ("teststdvecvecuint", &TConvert::teststdvecvecuint) .def ("teststdvecvh" , &TConvert::teststdvecvh) .def ("testipos", &TConvert::testipos) .def ("testitererror", &TConvert::testIterError) ; } }} BOOST_PYTHON_MODULE(_tConvert) { // Register the required converters. casacore::python::register_convert_excp(); casacore::python::register_convert_basicdata(); casacore::python::register_convert_casa_valueholder(); casacore::python::register_convert_casa_record(); casacore::python::register_convert_std_vector(); casacore::python::register_convert_std_vector(); casacore::python::register_convert_std_vector >(); casacore::python::register_convert_std_vector(); // Execute the test. casacore::python::testConvert(); } casacore-3.7.1/python/Converters/test/tConvert.out000066400000000000000000000116121476623553700223140ustar00rootroot00000000000000Doing numpy/array test ... VH Array [-1 -2] VH Array [211 212] >>> VH Array <<< (0,) VH Array {'shape': [2, 2], 'array': ['abcd', 'c', '12', 'x12']} testarrb testarrvh VH Array [ True False] VH Bool True VH Array [ True] VH Array [ True False] bool 1 True bool 0 False testarri testarrvh VH Array [-6 -7] VH Short -6 VH Array [-6] VH Array [-6 -7] Int -6 -6 Int -7 -7 Int64 -6 -6 ssize -6 -6 Float -6 -6.0 Complex (-6,0) (-6+0j) testarri testarrvh VH Array [5 6] VH uShort 5 VH Array [5] VH Array [5 6] Int 5 5 Int 6 6 Int64 5 5 ssize 5 5 Float 5 5.0 Complex (5,0) (5+0j) testarri testarrvh VH Array [-16 -17] VH Short -16 VH Array [-16] VH Array [-16 -17] Int -16 -16 Int -17 -17 Int64 -16 -16 ssize -16 -16 Float -16 -16.0 Complex (-16,0) (-16+0j) testarri testarrvh VH Array [15 16] VH uShort 15 VH Array [15] VH Array [15 16] Int 15 15 Int 16 16 Int64 15 15 ssize 15 15 Float 15 15.0 Complex (15,0) (15+0j) testarri testarrvh VH Array [-26 -27] VH Int -26 VH Array [-26] VH Array [-26 -27] Int -26 -26 Int -27 -27 Int64 -26 -26 ssize -26 -26 Float -26 -26.0 Complex (-26,0) (-26+0j) testarri testarrvh VH Array [25 26] VH uInt 25 VH Array [25] VH Array [25 26] Int 25 25 Int 26 26 Int64 25 25 ssize 25 25 Float 25 25.0 Complex (25,0) (25+0j) testarri testarrvh VH Array [-36 -37] VH Int64 -36 VH Array [-36] VH Array [-36 -37] Int -36 -36 Int -37 -37 Int64 -36 -36 ssize -36 -36 Float -36 -36.0 Complex (-36,0) (-36+0j) testarri testarrvh VH Array [35 36] VH Int64 35 VH Array [35] VH Array [35 36] Int 35 35 Int 36 36 Int64 35 35 ssize 35 35 Float 35 35.0 Complex (35,0) (35+0j) testarrf testarrvh VH Array [-46. -47.] VH float -46.0 VH Array [-46.] VH Array [-46. -47.] Float -46 -46.0 Float -47 -47.0 Complex (-46,0) (-46+0j) testarrf testarrvh VH Array [ 45. 46.] VH double 45.0 VH Array [ 45.] VH Array [ 45. 46.] Float 45 45.0 Float 46 46.0 Complex (45,0) (45+0j) testarrc testarrvh VH Array [-56.-66.j -57.-67.j] VH Complex (-56-66j) VH Array [-56.-66.j] VH Array [-56.-66.j -57.-67.j] Complex (-56,-66) (-56-66j) Complex (-57,-67) (-57-67j) testarrc testarrvh VH Array [-76.-86.j -77.-87.j] VH DComplex (-76-86j) VH Array [-76.-86.j] VH Array [-76.-86.j -77.-87.j] Complex (-76,-86) (-76-86j) Complex (-77,-87) (-77-87j) testarrstr testarrvh VH Array ['1', '2'] VH String 1 VH Array ['1'] VH Array ['1', '2'] String 1 1 begin dotest bool 1 True bool 0 False Int -1 -1 Int 10 10 Int64 -123456789013 -123456789013 Int64 123456789014 123456789014 ssize -2 -2 ssize 11 11 Float 3.14 3.1400001049 Float 12 12.0 String this is a string this is a string String this is a unicode string this is a unicode string IPos [4, 3, 2] [2, 3, 4] IPos [1] [1] IPos [2] [2] IPos [3] [3] VecBool [1, 0, 0, 1] [True, False, False, True] VecInt [1, 2, 3, 4] [1, 2, 3, 4] VecInt [] [] VecInt [-1, -2, -3, -4] [-1, -2, -3, -4] VecInt [-10] [-10] VecInt [10, 11, 12] [10, 11, 12] VecInt [1] [1] VecComplex [(1,2), (-1,-3), (-1.5,2.5)] [(1+2j), (-1-3j), (-1.5+2.5j)] VecStr [a1, a2, b1, b2] ['a1', 'a2', 'b1', 'b2'] VecStr [] [] VecStr [sc1] ['sc1'] VecStr [unicode sc1] ['unicode sc1'] VecStr [uni1, uni2] ['uni1', 'uni2'] vecbool [0,1] [False, True] vecuInt [1,2,4] [1, 2, 4] vecuInt [] [] vecuInt [10] [10] vecvecuInt [[1,2,4]] [[1, 2, 4]] vecvecuInt [] [] vecvecuInt [] [] vecvecuInt [[1],[2],[4]] [[1], [2], [4]] vecvecuInt [[20]] [[20]] VH Bool True VH Int 2 VH Int64 1234567890123 VH double 1.3 VH DComplex (10-11j) VH String str VH String unistr VH Array [1] VH Array [ 2 4 6 8 10] VH Array [ 1.3 4. 5. 6. ] VH Array [ 10.-11.j 1. +2.j] VH Array ['str1', 'str2'] VH Array ['ustr1', 'ustr2'] VH Array {'shape': [2, 2], 'array': ['str1', 'str2', 'str3', 'str4']} VH Array {'shape': [3, 1], 'array': ['ustr1', 'ustr2', 'ustr3']} VH Array {'shape': [2, 2], 'array': ['str1', 'str2', 'str3', 'str4']} VH Array {'shape': [2, 2], 'array': ['str1', 'str2', 'str3', 'str4']} VH Array (2,) VH Array [ 10.-11.j 1. +2.j] [[2 3] [4 5]] VH Array [[2 3] [4 5]] [3 5 7 9] VH Array [3 5 7 9] [ 3. 5. 7. 9.] VH Array [ 3. 5. 7. 9.] VH Array [ 3. 5. 7. 9.] VH Array [ 20.+10.j] VH Array [ 21.] VH double 21.0 >>> VH Array <<< (0,) >>> VH Array <<< (1, 0) vecvh 3 [2, 1.3, array([ True, False], dtype=bool)] Record int: Int 1 int64: Int64 123456789012 ustr: String "ubc" str: String "bc" vecint: Int array with shape [3] [1, 2, 3] >>> {'int': 1, 'int64': 123456789012L, 'str': 'bc', 'vecint': array([1, 2, 3], dtype=int32)} <<< end dotest casacore-3.7.1/python/Converters/test/tConvert.py000066400000000000000000000143161476623553700221410ustar00rootroot00000000000000#!/usr/bin/env python from _tConvert import * def dotest(t): print (''); print ('begin dotest'); print (t.testbool (True)); print (t.testbool (False)); print (t.testint (-1)); print (t.testint (10L)); print (t.testint64 (-123456789013L)); print (t.testint64 (123456789014L)); print (t.testssize (-2)); print (t.testssize (11)); print (t.testfloat (3.14)); print (t.testfloat (12)); print (t.teststring ("this is a string")); print (t.teststring (u"this is a unicode string")); print (t.testipos ([2,3,4])); print (t.testipos (1)); print (t.testipos (NUM.array([2]))); print (t.testipos (NUM.array(3))); print (t.testvecbool ([True,False,False,True])); print (t.testvecint ([1,2,3,4])); print (t.testvecint ([])); print (t.testvecint ((-1,-2,-3,-4))); print (t.testvecint (-10)); print (t.testvecint (NUM.array((10,11,12)))); print (t.testvecint (NUM.array(1))); print (t.testveccomplex ([1+2j, -1-3j, -1.5+2.5j])); print (t.testvecstr (["a1","a2","b1","b2"])); print (t.testvecstr (())); print (t.testvecstr ("sc1")); print (t.testvecstr (u"unicode sc1")); print (t.testvecstr ([u"uni1", "uni2"])); print (t.teststdvecbool ([False,True])); print (t.teststdvecuint ([1,2,4])); print (t.teststdvecuint (())); print (t.teststdvecuint (10)); print (t.teststdvecvecuint ([[1,2,4]])); print (t.teststdvecvecuint ((()))); print (t.teststdvecvecuint (())); print (t.teststdvecvecuint ([1,2,4])); print (t.teststdvecvecuint (20)); print (t.testvh (True)); print (t.testvh (2)); print (t.testvh (1234567890123L)); print (t.testvh (1.3)); print (t.testvh (10-11j)); print (t.testvh ("str")); print (t.testvh (u"unistr")); print (t.testvh ([True]) + 0); # add 0 to convert numpy to integer print (t.testvh ([2,4,6,8,10])); print (t.testvh ([1.3,4,5,6])); print (t.testvh ([10-11j,1+2j])); # print (t.testvh ([])); print (t.testvh (["str1","str2"])); print (t.testvh ([u"ustr1",u"ustr2"])); print (t.testvh ({"shape":[2,2],"array":["str1","str2","str3","str4"]})); print (t.testvh ({"shape":[3,1],"array":[u"ustr1",u"ustr2",u"ustr3"]})); a = t.testvh ({"shape":[2,2],"array":["str1","str2","str3","str4"]}); print (a); print (t.testvh (a)); a = t.testvh ([10-11j,1+2j]); print (a.shape); print (t.testvh (a)); b = NUM.int32([[2,3],[4,5]]); print (b); print (t.testvh (b)); b = NUM.int32([1,2,3,4,5,6,7,8,9,10]); print (b[2:9:2]); print (t.testvh (b[2:9:2])); b = NUM.array([1,2,3,4,5,6,7,8,9,10.]); print (b[2:9:2]); print (t.testvh (b[2:9:2])); a = b[2:9:2]; print (t.testvh (a)); print (t.testvh(NUM.array([20.+10j]))); print (t.testvh(NUM.array([21.]))); print (t.testvh(NUM.array(21.))); print ('>>>'); res = t.testvh (NUM.array([])); print ('<<<'); print (res.shape); print ('>>>'); res = t.testvh (NUM.array([[]])); print ('<<<'); print (res.shape); # Test a sequence of ValueHolders print (t.teststdvecvh([2, 1.3, [True,False]])); # On 64-bit machines the output also contains 'dtype=int32' # So leave it out. a = t.testrecord({"int":1, "int64":123456789012L, "str":"bc", "ustr":"ubc", 'vecint':[1,2,3]}) print ('>>>'); print (a); print ('<<<'); print ('end dotest'); print (''); def testarrvh(arr): print (' testarrvh'); print (t.testvh(arr)); print (t.testvh(arr[0])); print (t.testvh([arr[0]])); print (t.testvh([arr[0], arr[1]])); def testarrb(arr): print (' testarrb') # Test array testarrvh(arr); # Test array scalar print (t.testbool(arr[0])); # Test scalar array print (t.testbool(NUM.array(arr[1]))); def testarri(arr): print (' testarri') testarrvh(arr); print (t.testint(arr[0])); print (t.testint(NUM.array(arr[1]))); print (t.testint64(arr[0])); print (t.testssize(arr[0])); print (t.testfloat(arr[0])); print (t.testcomplex(arr[0])); def testarrf(arr): print (' testarrf') testarrvh(arr); print (t.testfloat(arr[0])); print (t.testfloat(NUM.array(arr[1]))); print (t.testcomplex(arr[0])); def testarrc(arr): print (' testarrc') testarrvh(arr); print (t.testcomplex(arr[0])); print (t.testcomplex(NUM.array(arr[1]))); def testarrstr(arr): print (' testarrstr') testarrvh(arr); print (t.teststring(arr[0])); # numpy scalar array of string not supported #print (t.teststring(NUM.array(arr[1]))); def testnps(): testarrb(NUM.array([True,False])); testarri(NUM.int8([-6,-7])); testarri(NUM.uint8([5,6])); testarri(NUM.int16([-16,-17])); testarri(NUM.uint16([15,16])); testarri(NUM.int32([-26,-27])); testarri(NUM.uint32([25,26])); testarri(NUM.int64([-36,-37])); testarri(NUM.uint64([35,36])); testarrf(NUM.float32([-46,-47])); testarrf(NUM.float64([45,46])); testarrc(NUM.complex64([-56-66j,-57-67j])); testarrc(NUM.complex128([-76-86j,-77-87j])); testarrstr(NUM.array(['1','2'])); def testexcp(): # Test a normal exception. excp = False try: b = t.testvh([1, "str"]) # incompatible types except: excp = True if not excp: print ("Normal exception in testexcp was not converted"); # Test an IterError exception. excp = False try: b = t.testitererror() except StopIteration: excp = True if not excp: print ("IterError exception in testexcp was not converted"); def testnp(): # Test byte and sbyte. b = NUM.int8([-1,-2]); print (t.testvh(b)); b = NUM.uint8([211,212]); print (t.testvh(b)); print ('>>>'); res = t.testvh(NUM.array([])); print ('<<<'); print (res.shape); print (t.testvh(NUM.array([["abcd","c"],["12","x12"]]))); testnps(); testexcp(); if __name__ == "__main__": import numpy as NUM from distutils.version import LooseVersion if LooseVersion(NUM.version.version) >= LooseVersion("1.14"): NUM.set_printoptions(legacy="1.13") t = tConvert(); print ("Doing numpy/array test ..."); testnp(); # Do other tests. dotest(t) casacore-3.7.1/python/Converters/test/tConvert.run000077500000000000000000000002021476623553700223050ustar00rootroot00000000000000#!/bin/sh # Use .run file for 2 reasons: # 1. tConvert.py is not executable # 2. Do not use valgrind on it python tConvert.py casacore-3.7.1/python/python.dox000066400000000000000000000030701476623553700167020ustar00rootroot00000000000000//# python.dox: doxygen description of python package //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id: python.dox 21537 2015-01-06 10:40:21Z gervandiepen $ namespace casacore { // \defgroup python python package (libcasa_python) // // The python package contains converters for casacore data types to and from python //
          //
        • Converters: // convert casacore data types to and from python. //
        } casacore-3.7.1/python3/000077500000000000000000000000001476623553700147305ustar00rootroot00000000000000casacore-3.7.1/python3/CMakeLists-cmake3.14.txt000066400000000000000000000065761476623553700210720ustar00rootroot00000000000000message(STATUS "Looking for python3 specific environment...") find_package(Python3 REQUIRED COMPONENTS Interpreter Development NumPy) if (Python3_FOUND) find_package(Boost REQUIRED) if(${Boost_MAJOR_VERSION} EQUAL 1 AND ${Boost_MINOR_VERSION} GREATER 66) # Boost>=1.67 Python components require a Python version suffix set(BOOST_PYTHON3_LIBRARY_NAME python${Python3_VERSION_MAJOR}${Python3_VERSION_MINOR} CACHE STRING "The name of the boost python3 library to search for") else() set(BOOST_PYTHON3_LIBRARY_NAME python${Python3_VERSION_MAJOR} CACHE STRING "The name of the boost python3 library to search for") endif() find_package(Boost REQUIRED COMPONENTS ${BOOST_PYTHON3_LIBRARY_NAME}) # copy the variables to their final destination set(PYTHON3_EXECUTABLE ${Python3_EXECUTABLE} CACHE FILEPATH "Path to Python3 interpreter") set(PYTHON3_LIBRARY ${Python3_LIBRARIES} CACHE PATH "Python3 library") set(PYTHON3_INCLUDE_DIR ${Python3_INCLUDE_DIRS} CACHE PATH "Python3 include folder") set(PYTHON3_FIND_PACKAGE_MESSAGE_DETAILS_PythonLibs ${FIND_PACKAGE_MESSAGE_DETAILS_PythonLibs} CACHE STRING "") set(PYTHON3_LIBRARIES ${Python3_LIBRARIES} PARENT_SCOPE) set(PYTHON3_NUMPY_INCLUDE_DIRS ${Python3_NumPy_INCLUDE_DIRS} PARENT_SCOPE) set(PYTHON3_Boost_LIBRARIES ${Boost_LIBRARIES} PARENT_SCOPE) set(PYTHON3_Boost_INCLUDE_DIRS ${Boost_INCLUDE_DIRS} PARENT_SCOPE) set(PYTHON3_Boost_PYTHON_LIBRARY_RELEASE ${Boost_PYTHON_LIBRARY_RELEASE} PARENT_SCOPE) set(PYTHON3_INCLUDE_DIRS ${Python3_INCLUDE_DIRS} PARENT_SCOPE) # to access the variables here we also need to set them in the local scope set(PYTHON3_LIBRARIES ${Python3_LIBRARIES} ) set(PYTHON3_NUMPY_INCLUDE_DIRS ${Python3_NumPy_INCLUDE_DIRS} ) set(PYTHON3_Boost_LIBRARIES ${Boost_LIBRARIES} ) set(PYTHON3_Boost_INCLUDE_DIRS ${Boost_INCLUDE_DIRS} ) set(PYTHON3_Boost_PYTHON_LIBRARY_RELEASE ${Boost_PYTHON_LIBRARY_RELEASE} ) set(PYTHON3_INCLUDE_DIRS ${Python3_INCLUDE_DIRS} ) endif(Python3_FOUND) include_directories (${PYTHON3_Boost_INCLUDE_DIRS} ${PYTHON3_NUMPY_INCLUDE_DIRS} ${PYTHON3_INCLUDE_DIRS}) add_library (casa_python3 ../python/Converters/PycArray.cc ../python/Converters/PycArrayNP.cc ../python/Converters/PycBasicData.cc ../python/Converters/PycExcp.cc ../python/Converters/PycImport.cc ../python/Converters/PycRecord.cc ../python/Converters/PycValueHolder.cc ) target_link_libraries (casa_python3 casa_casa ${PYTHON3_Boost_LIBRARIES} ${PYTHON3_LIBRARIES}) install (TARGETS casa_python3 RUNTIME DESTINATION bin LIBRARY DESTINATION lib${LIB_SUFFIX} ARCHIVE DESTINATION lib${LIB_SUFFIX} LIBRARY PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) # don't install headers if python2 cmake logic already installs it if (NOT BUILD_PYTHON) install (FILES ../python/Converters/PycArray.h ../python/Converters/PycArrayComCC.h ../python/Converters/PycArrayComH.h ../python/Converters/PycArrayNP.h ../python/Converters/PycBasicData.h ../python/Converters/PycExcp.h ../python/Converters/PycRecord.h ../python/Converters/PycValueHolder.h ../python/Converters/PycArray.tcc DESTINATION include/casacore/python/Converters ) install (FILES ../python/Converters.h DESTINATION include/casacore/python ) endif (NOT BUILD_PYTHON) casacore-3.7.1/python3/CMakeLists-older-cmake.txt000066400000000000000000000102121476623553700216450ustar00rootroot00000000000000message(STATUS "Looking for python3 specific environment...") # tempororarly set variables used by findpython if (PYTHON3_EXECUTABLE) set(PYTHON_EXECUTABLE ${PYTHON3_EXECUTABLE} CACHE FILEPATH "") endif() if (PYTHON3_LIBRARY) set(PYTHON_LIBRARY ${PYTHON3_LIBRARY} CACHE FILEPATH "") endif() if (PYTHON3_INCLUDE_DIR) set(PYTHON_INCLUDE_DIR ${PYTHON3_INCLUDE_DIR} CACHE FILEPATH "") endif() if (PYTHON3_FIND_PACKAGE_MESSAGE_DETAILS_PythonLibs) set(FIND_PACKAGE_MESSAGE_DETAILS_PythonLibs ${PYTHON3_FIND_PACKAGE_MESSAGE_DETAILS_PythonLibs} CACHE FILEPATH "") endif() # Detect the python properties set(Python_ADDITIONAL_VERSIONS 3.7 3.6 3.5 3.4) find_package(Python REQUIRED) if (PYTHONINTERP_FOUND) find_package(Boost REQUIRED) if(${Boost_MAJOR_VERSION} EQUAL 1 AND ${Boost_MINOR_VERSION} GREATER 66) # Boost>=1.67 Python components require a Python version suffix set(BOOST_PYTHON3_LIBRARY_NAME python${PYTHON_VERSION_MAJOR}${PYTHON_VERSION_MINOR} CACHE STRING "The name of the boost python3 library to search for") else() set(BOOST_PYTHON3_LIBRARY_NAME python${PYTHON_VERSION_MAJOR} CACHE STRING "The name of the boost python3 library to search for") endif() find_package(Boost REQUIRED COMPONENTS ${BOOST_PYTHON3_LIBRARY_NAME}) find_package (NUMPY REQUIRED) # copy the variables to their final destination set(PYTHON3_EXECUTABLE ${PYTHON_EXECUTABLE} CACHE FILEPATH "Path to Python3 interpreter") set(PYTHON3_LIBRARY ${PYTHON_LIBRARY} CACHE PATH "Python3 library") set(PYTHON3_INCLUDE_DIR ${PYTHON_INCLUDE_DIR} CACHE PATH "Python3 include folder") set(PYTHON3_FIND_PACKAGE_MESSAGE_DETAILS_PythonLibs ${FIND_PACKAGE_MESSAGE_DETAILS_PythonLibs} CACHE STRING "") set(PYTHON3_LIBRARIES ${PYTHON_LIBRARIES} PARENT_SCOPE) set(PYTHON3_NUMPY_INCLUDE_DIRS ${NUMPY_INCLUDE_DIRS} PARENT_SCOPE) set(PYTHON3_Boost_LIBRARIES ${Boost_LIBRARIES} PARENT_SCOPE) set(PYTHON3_Boost_INCLUDE_DIRS ${Boost_INCLUDE_DIRS} PARENT_SCOPE) set(PYTHON3_Boost_PYTHON_LIBRARY_RELEASE ${PYTHON3_Boost_LIBRARIES} PARENT_SCOPE) set(PYTHON3_INCLUDE_DIRS ${PYTHON_INCLUDE_DIRS} PARENT_SCOPE) # to access the variables here we also need to set them in the local scope set(PYTHON3_LIBRARIES ${PYTHON_LIBRARIES} ) set(PYTHON3_NUMPY_INCLUDE_DIRS ${NUMPY_INCLUDE_DIRS} ) set(PYTHON3_Boost_LIBRARIES ${Boost_LIBRARIES} ) set(PYTHON3_Boost_INCLUDE_DIRS ${Boost_INCLUDE_DIRS} ) set(PYTHON3_Boost_PYTHON_LIBRARY_RELEASE ${Boost_PYTHON_LIBRARY_RELEASE} ) set(PYTHON3_INCLUDE_DIRS ${PYTHON_INCLUDE_DIRS} ) # Remove cached variable to not confuse user unset(PYTHON_EXECUTABLE CACHE) unset(PYTHON_LIBRARY CACHE) unset(PYTHON_INCLUDE_DIR CACHE) unset(FIND_PACKAGE_MESSAGE_DETAILS_PythonLibs CACHE) endif(PYTHONINTERP_FOUND) include_directories (${PYTHON3_Boost_INCLUDE_DIRS} ${PYTHON3_NUMPY_INCLUDE_DIRS} ${PYTHON3_INCLUDE_DIRS}) add_library (casa_python3 ../python/Converters/PycArray.cc ../python/Converters/PycArrayNP.cc ../python/Converters/PycBasicData.cc ../python/Converters/PycExcp.cc ../python/Converters/PycImport.cc ../python/Converters/PycRecord.cc ../python/Converters/PycValueHolder.cc ) target_link_libraries (casa_python3 casa_casa ${PYTHON3_Boost_LIBRARIES} ${PYTHON3_LIBRARIES}) install (TARGETS casa_python3 RUNTIME DESTINATION bin LIBRARY DESTINATION lib${LIB_SUFFIX} ARCHIVE DESTINATION lib${LIB_SUFFIX} LIBRARY PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) # don't install headers if python2 cmake logic already installs it if (NOT BUILD_PYTHON) install (FILES ../python/Converters/PycArray.h ../python/Converters/PycArrayComCC.h ../python/Converters/PycArrayComH.h ../python/Converters/PycArrayNP.h ../python/Converters/PycBasicData.h ../python/Converters/PycExcp.h ../python/Converters/PycRecord.h ../python/Converters/PycValueHolder.h ../python/Converters/PycArray.tcc DESTINATION include/casacore/python/Converters ) install (FILES ../python/Converters.h DESTINATION include/casacore/python ) endif (NOT BUILD_PYTHON) casacore-3.7.1/python3/CMakeLists.txt000066400000000000000000000005221476623553700174670ustar00rootroot00000000000000# It would be nice to use CMAKE_VERSION instead but that requires CMake 2.6.3 if(${CMAKE_MAJOR_VERSION} GREATER 3 OR (${CMAKE_MAJOR_VERSION} EQUAL 3 AND ${CMAKE_MINOR_VERSION} GREATER 13)) include (${CMAKE_CURRENT_LIST_DIR}/CMakeLists-cmake3.14.txt) else() include (${CMAKE_CURRENT_LIST_DIR}/CMakeLists-older-cmake.txt) endif() casacore-3.7.1/scimath/000077500000000000000000000000001476623553700147545ustar00rootroot00000000000000casacore-3.7.1/scimath/CMakeLists.txt000066400000000000000000000240551476623553700175220ustar00rootroot00000000000000# # CASA Scimath # set (buildfiles Fitting/FittingProxy.cc Fitting/LSQFit.cc Fitting/LSQFit3.cc Fitting/LSQMatrix2.cc Fitting/LSQMatrix.cc Functionals/FuncExpression.cc Functionals/FuncExprData.cc Functionals/FunctionFactoryErrors.cc Functionals/FunctionalProxy.cc Functionals/SerialHelper.cc Mathematics/Combinatorics.cc Mathematics/FFTServer.cc Mathematics/FFTW.cc Mathematics/GaussianBeam.cc Mathematics/Geometry.cc Mathematics/Interpolate2D.cc Mathematics/MathFunc2.cc Mathematics/MatrixSolver.cc Mathematics/MedianSlider.cc Mathematics/NNLSMatrixSolver.cc Mathematics/NumericTraits.cc Mathematics/RigidVector2.cc Mathematics/SCSL.cc Mathematics/SquareMatrix2.cc Mathematics/VectorKernel.cc Mathematics/VanVleck.cc StatsFramework/BiweightStatisticsData.cc StatsFramework/ClassicalStatisticsData.cc StatsFramework/StatisticsData.cc StatsFramework/ZScoreCalculator.cc ) if (BUILD_FFTPACK_DEPRECATED) list (APPEND buildfiles Mathematics/FFTPack.cc ) install (FILES Mathematics/FFTPack.h DESTINATION include/casacore/scimath/Mathematics ) endif () add_library (casa_scimath ${buildfiles}) set (top_level_headers Fitting.h Functionals.h Mathematics.h ) init_pch_support(casa_scimath ${top_level_headers}) if (FFTW3_FOUND) target_link_libraries (casa_scimath casa_scimath_f ${FFTW3_LIBRARIES} ${CASACORE_ARCH_LIBS}) else (FFTW3_FOUND) target_link_libraries (casa_scimath casa_scimath_f ${CASACORE_ARCH_LIBS}) endif (FFTW3_FOUND) install (TARGETS casa_scimath LIBRARY DESTINATION lib${LIB_SUFFIX} ARCHIVE DESTINATION lib${LIB_SUFFIX} LIBRARY PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_WRITE GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) install (FILES Fitting/FitGaussian.h Fitting/FitGaussian.tcc Fitting/FittingProxy.h Fitting/GenericL2Fit.h Fitting/GenericL2Fit.tcc Fitting/LSQFit.h Fitting/LSQFit2.tcc Fitting/LSQMatrix.h Fitting/LSQTraits.h Fitting/LSQaips.h Fitting/LSQaips.tcc Fitting/LinearFit.h Fitting/LinearFit.tcc Fitting/LinearFitSVD.h Fitting/LinearFitSVD.tcc Fitting/NonLinearFit.h Fitting/NonLinearFit.tcc Fitting/NonLinearFitLM.h Fitting/NonLinearFitLM.tcc DESTINATION include/casacore/scimath/Fitting ) install (FILES Functionals/AbstractFunctionFactory.h Functionals/ArraySampledFunctional.h Functionals/ArraySampledFunctional.tcc Functionals/Chebyshev.h Functionals/Chebyshev.tcc Functionals/ChebyshevParam.h Functionals/ChebyshevParam.tcc Functionals/Combi2Function.tcc Functionals/CombiFunction.h Functionals/CombiFunction.tcc Functionals/CombiParam.h Functionals/CombiParam.tcc Functionals/CompiledFunction.h Functionals/CompiledFunction.tcc Functionals/CompiledParam.h Functionals/CompiledParam.tcc Functionals/Compound2Function.tcc Functionals/CompoundFunction.h Functionals/CompoundFunction.tcc Functionals/CompoundParam.h Functionals/CompoundParam.tcc Functionals/ConstantND.h Functionals/ConstantND.tcc Functionals/ConstantNDParam.h Functionals/ConstantNDParam.tcc Functionals/DiracDFunction.h Functionals/DiracDFunction.tcc Functionals/DiracDParam.h Functionals/DiracDParam.tcc Functionals/EclecticFunctionFactory.h Functionals/EclecticFunctionFactory.tcc Functionals/EvenPolynomial.h Functionals/EvenPolynomial.tcc Functionals/EvenPolynomial2.tcc Functionals/EvenPolynomialParam.h Functionals/EvenPolynomialParam.tcc Functionals/FuncExprData.h Functionals/FuncExpression.h Functionals/Function.h Functionals/Function.tcc Functionals/Function1D.h Functionals/FunctionFactoryErrors.h Functionals/FunctionHolder.h Functionals/FunctionHolder.tcc Functionals/FunctionMarshallable.h Functionals/FunctionOrder.h Functionals/FunctionOrder.tcc Functionals/FunctionParam.h Functionals/FunctionParam.tcc Functionals/FunctionTraits.h Functionals/FunctionWrapper.h Functionals/FunctionWrapper.tcc Functionals/FunctionalProxy.h Functionals/GNoiseFunction.h Functionals/GNoiseFunction.tcc Functionals/GNoiseParam.h Functionals/GNoiseParam.tcc Functionals/Gaussian1D.h Functionals/Gaussian1D.tcc Functionals/Gaussian1D2.tcc Functionals/Gaussian1DParam.h Functionals/Gaussian1DParam.tcc Functionals/Gaussian2D.h Functionals/Gaussian2D.tcc Functionals/Gaussian2D2.tcc Functionals/Gaussian2DParam.h Functionals/Gaussian2DParam.tcc Functionals/Gaussian3D.h Functionals/Gaussian3D.tcc Functionals/Gaussian3D2.tcc Functionals/Gaussian3DParam.h Functionals/Gaussian3DParam.tcc Functionals/GaussianND.h Functionals/GaussianND.tcc Functionals/GaussianNDParam.h Functionals/GaussianNDParam.tcc Functionals/HyperPlane.h Functionals/HyperPlane.tcc Functionals/HyperPlane2.tcc Functionals/HyperPlaneParam.h Functionals/HyperPlaneParam.tcc Functionals/Interpolate1D.h Functionals/Interpolate1D.tcc Functionals/KaiserBFunction.h Functionals/KaiserBFunction.tcc Functionals/KaiserBParam.h Functionals/KaiserBParam.tcc Functionals/Lorentzian1D.h Functionals/Lorentzian1D.tcc Functionals/Lorentzian1D2.tcc Functionals/Lorentzian1DParam.h Functionals/Lorentzian1DParam.tcc Functionals/MarshButterworthBandpass.h Functionals/MarshButterworthBandpass.tcc Functionals/MarshallableChebyshev.h Functionals/MarshallableChebyshev.tcc Functionals/OddPolynomial.h Functionals/OddPolynomial.tcc Functionals/OddPolynomial2.tcc Functionals/OddPolynomialParam.h Functionals/OddPolynomialParam.tcc Functionals/PoissonFunction.h Functionals/PoissonFunction.tcc Functionals/PoissonFunction2.tcc Functionals/PoissonParam.h Functionals/PoissonParam.tcc Functionals/Polynomial.h Functionals/Polynomial.tcc Functionals/Polynomial2.tcc Functionals/PolynomialParam.h Functionals/PolynomialParam.tcc Functionals/PowerLogarithmicPolynomial.h Functionals/PowerLogarithmicPolynomial.tcc Functionals/PowerLogarithmicPolynomial2.tcc Functionals/PowerLogarithmicPolynomialParam.h Functionals/PowerLogarithmicPolynomialParam.tcc Functionals/SPolynomial.h Functionals/SPolynomial.tcc Functionals/SPolynomialParam.h Functionals/SPolynomialParam.tcc Functionals/SampledFunctional.h Functionals/ScalarSampledFunctional.h Functionals/ScalarSampledFunctional.tcc Functionals/SerialHelper.h Functionals/SimButterworthBandpass.h Functionals/SimButterworthBandpass.tcc Functionals/SincFunction.h Functionals/SincFunction.tcc Functionals/SincParam.h Functionals/SincParam.tcc Functionals/Sinusoid1D.h Functionals/Sinusoid1D.tcc Functionals/Sinusoid1D2.tcc Functionals/Sinusoid1DParam.h Functionals/Sinusoid1DParam.tcc Functionals/SpecificFunctionFactory.h Functionals/UnaryFunction.h Functionals/UnaryFunction.tcc Functionals/UnaryParam.h Functionals/UnaryParam.tcc Functionals/WrapperBase.h Functionals/WrapperData.h Functionals/WrapperParam.h Functionals/WrapperParam.tcc DESTINATION include/casacore/scimath/Functionals ) install (FILES Mathematics/AutoDiff.h Mathematics/AutoDiff.tcc Mathematics/AutoDiffA.h Mathematics/AutoDiffIO.h Mathematics/AutoDiffIO.tcc Mathematics/AutoDiffMath.h Mathematics/AutoDiffMath.tcc Mathematics/AutoDiffX.h Mathematics/Combinatorics.h Mathematics/ConvolveGridder.h Mathematics/ConvolveGridder.tcc Mathematics/Convolver.h Mathematics/Convolver.tcc Mathematics/DFTServer.h Mathematics/DFTServer.tcc Mathematics/FFTServer.h Mathematics/FFTW.h Mathematics/GaussianBeam.h Mathematics/Geometry.h Mathematics/Gridder.h Mathematics/Gridder.tcc Mathematics/HistAcc.h Mathematics/HistAcc.tcc Mathematics/Interpolate2D.h Mathematics/Interpolate2D2.tcc Mathematics/InterpolateArray1D.h Mathematics/InterpolateArray1D.tcc Mathematics/MathFunc.h Mathematics/MathFunc.tcc Mathematics/MatrixMathLA.h Mathematics/MatrixMathLA.tcc Mathematics/MatrixSolver.h Mathematics/MedianSlider.h Mathematics/NNGridder.h Mathematics/NNGridder.tcc Mathematics/NNLSMatrixSolver.h Mathematics/NumericTraits.h Mathematics/NumericTraits2.h Mathematics/RigidVector.h Mathematics/RigidVector.tcc Mathematics/SCSL.h Mathematics/Smooth.h Mathematics/Smooth.tcc Mathematics/SparseDiff.h Mathematics/SparseDiff.tcc Mathematics/SparseDiffA.h Mathematics/SparseDiffIO.h Mathematics/SparseDiffIO.tcc Mathematics/SparseDiffMath.h Mathematics/SparseDiffMath.tcc Mathematics/SparseDiffRep.h Mathematics/SparseDiffRep.tcc Mathematics/SparseDiffX.h Mathematics/SquareMatrix.h Mathematics/SquareMatrix.tcc Mathematics/StatAcc.h Mathematics/StatAcc.tcc Mathematics/VanVleck.h Mathematics/VectorKernel.h DESTINATION include/casacore/scimath/Mathematics ) install (FILES StatsFramework/BiweightStatistics.h StatsFramework/BiweightStatistics.tcc StatsFramework/BiweightStatisticsData.h StatsFramework/ChauvenetCriterionStatistics.h StatsFramework/ChauvenetCriterionStatistics.tcc StatsFramework/ClassicalStatistics.h StatsFramework/ClassicalStatistics.tcc StatsFramework/ClassicalStatisticsData.h StatsFramework/ClassicalQuantileComputer.h StatsFramework/ClassicalQuantileComputer.tcc StatsFramework/ConstrainedRangeStatistics.h StatsFramework/ConstrainedRangeStatistics.tcc StatsFramework/ConstrainedRangeQuantileComputer.h StatsFramework/ConstrainedRangeQuantileComputer.tcc StatsFramework/FitToHalfStatistics.h StatsFramework/FitToHalfStatistics.tcc StatsFramework/FitToHalfStatisticsData.h StatsFramework/HingesFencesStatistics.h StatsFramework/HingesFencesStatistics.tcc StatsFramework/HingesFencesQuantileComputer.h StatsFramework/HingesFencesQuantileComputer.tcc StatsFramework/StatsDataProvider.h StatsFramework/StatsDataProvider.tcc StatsFramework/StatisticsAlgorithm.h StatsFramework/StatisticsAlgorithm.tcc StatsFramework/StatisticsAlgorithmFactory.h StatsFramework/StatisticsAlgorithmFactory.tcc StatsFramework/StatisticsAlgorithmFactoryData.h StatsFramework/StatisticsAlgorithmQuantileComputer.h StatsFramework/StatisticsAlgorithmQuantileComputer.tcc StatsFramework/StatisticsData.h StatsFramework/StatisticsDataset.h StatsFramework/StatisticsDataset.tcc StatsFramework/StatisticsIncrementer.h StatsFramework/StatisticsTypes.h StatsFramework/StatisticsTypes.tcc StatsFramework/StatisticsUtilities.h StatsFramework/StatisticsUtilities.tcc StatsFramework/StatsHistogram.tcc StatsFramework/StatsHistogram.h StatsFramework/ZScoreCalculator.h DESTINATION include/casacore/scimath/StatsFramework ) install (FILES ${top_level_headers} DESTINATION include/casacore/scimath ) add_subdirectory (Fitting/test ${EXCL_ALL}) add_subdirectory (Functionals/test ${EXCL_ALL}) add_subdirectory (Mathematics/test ${EXCL_ALL}) add_subdirectory (StatsFramework/test ${EXCL_ALL}) casacore-3.7.1/scimath/Fitting.h000066400000000000000000000207051476623553700165350ustar00rootroot00000000000000//# Fitting.h: Module for various forms of mathematical fitting //# Copyright (C) 1995,1999-2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_FITTING_H #define SCIMATH_FITTING_H #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // // // Module for various forms of mathematical fitting. // // // //
      • Basic principles can be found in // Note 224. // // // // // // // // The Fitting module holds various classes and functions related // to fitting models to data. Currently only least-squares fits // are handled. // //

        Least-Squares Fits

        // // We are given N data points, which // we will fit to a function with M adjustable parameters. // N should normally be greater than M, and at least M non-dependent relations // between the parameters should be given. In cases where there are less than // M independent points, Singular-Value-Deconvolution methods are available. // Each condition equation can be given an // (estimated) standard deviation, which is comparable to the statistical // weight, which is often used in place of the standard deviation. // // The best fit is assumed to be the one which minimises the 'chi-squared'. // // In the (rather common) case that individual errors are not known for // the individual data points, one can assume that the // individual errors are unity, calculate the best fit function, and then // estimate the errors (assuming they are all identical) by // inverting the normal equations. // Of course, in this case we do not have an independent estimate of // chi2. // // The methods used in the Fitting module are described in // Note 224. // The methods (both standard and // SVD) are based on a Cholesky decomposition of the normal equations. // // General background can also be found in Numerical Recipes by // Press et al.. // //

        Linear Least-Squares Fits

        // // The linear least squares solution assumes that the fit function // is a linear combination of M linear condition equations. // It is important to note that linear refers to the dependence on // the parameters; the condition equations may be very non-linear in the // dependent arguments. // // The linear least squares problem is solved by explicitly // forming and inverting the normal equations. // If the normal equations are close to // singular, the singular value decomposition (SVD) method may be // used. Numerical Recipes suggests the SVD be always used, however // this advice is not universally accepted. // //

        Linear Least-Squares Fits with Known Linear Constraints

        // // Sometimes there are not enough independent observations, i.e., the // number of data points N is less than the number of adjustable // parameters M. In this case the least-squares problem cannot be // solved unless additional ``constraints'' on the adjustable parameters can // be introduced. Under other circumstances, we may want to introduce // constraints on the adjustable // parameters to add additional information, e.g., the sum of angles // of a triangle. In more complex cases, the forms of the constraints // are unknown. Here we confine ourselves to // least-squares fit problems in which the forms of constraints are known. // // If the forms of constraint equations are known, the least-squares // problem can be solved. (In the case where not // enough independent observations are available, a minimum number of // sufficient constraint equations have to be provided. The singular value // decomposition method can be used to calculate the minimum number of // orthogonal constraints needed). // //

        Nonlinear Least-Squares Fits

        // // We now consider the situation where the fitted function // depends nonlinearly on the set of // M adjustable parameters. // But with nonlinear dependences the minimisation of chi2 cannot // proceed as in the linear case. // However, we can linearise the problem, find an // approximate solution, and then iteratively seek the minimising solution. // The iteration stops when e.g. the adjusted parameters do not change // anymore. In general it is very difficult to find a general solution that // finds a global minimum, and the solution has to be matched with the problem. // The Levenberg-Marquardt algorithm is a general non-linear fitting method // which can produce correct results in many cases. It has been included, but // always be aware of possible problems with non-linear solutions. // //

        What Is Available?

        // // The basic classes are LSQFit and // LSQaips. // They provide the basic framework for normal equation generation, solving // the normal equations and iterating in the case of non-linear equations. // // The LSQFit class uses a native C++ interface (pointers and // iterators). They handle real data and complex data. // The LSQaips class offers the functionality of LSQFit, // but with an additional Casacore Array interface.
        // // Functionality is //
          //
        1. Fit a linear combination of functions to data points, and, optionally, // use supplied constraint conditions on the adjustable parameters. //
        2. Fit a nonlinear function to data points. The adjustable parameters // are parameters inside the function. //
        3. Repetitively perform a linear fit for every line of pixels parallel // to any axis in a Lattice. //
        4. Solve (as opposed to fit to a set of data), a set of equations //
        // // In addition to the basic Least Squares routines in the LSQFit and // LSQaips classes, this module contains also a set of direct // data fitters: //
          //
        • Fit2D //
        • LinearFit //
        • LinearFitSVD //
        • NonLinearFit //
        • NonLinearFitLM //
        // Furthermore class LatticeFit can do fitting on lattices. // // Note that the basic functions have LSQ in their title; the // one-step fitting functions Fit. // // The above fitting problems can usually be solved by directly // calling the fit() member function provided by one of the // Fit classes above, or by // gradually building the normal equation matrix and solving the // normal equations (solve()). // // A Distributed Object interface to the classes is available // (DOfitting) for use in the Glish dfit // object, available through the fitting.g script. // //
        // // // This module was motivated by baseline subtraction/continuum fitting in the // first instance. // // // //
      • extend the Fit interface classes to cater for building the // normal equations in parts. // // // // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Fitting/000077500000000000000000000000001476623553700163605ustar00rootroot00000000000000casacore-3.7.1/scimath/Fitting/FitGaussian.h000066400000000000000000000215071476623553700207530ustar00rootroot00000000000000//# FitGaussian.h: Multidimensional fitter class for Gaussians //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_FITGAUSSIAN_H #define SCIMATH_FITGAUSSIAN_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Multidimensional fitter class for Gaussians. // // // //
      • Gaussian1D class //
      • Gaussian2D class //
      • Gaussian3D class //
      • NonLinearFitLM class // // // Fits Gaussians to data. // // // FitGaussian is specially designed for fitting procedures in // code that must be generalized for general dimensionality and // number of components, and for complicated fits where the failure rate of // the standard nonlinear fitter is unacceptibly high. // FitGaussian essentially provides a Gaussian-adapted // interface for NonLinearFitLM. The user specifies the dimension, // number of gaussians, initial estimate, retry factors, and the data, // and the fitting proceeds automatically. Upon failure of the fitter it will // retry the fit according to the retry factors until a fit is completed // successfully. The user can optionally require as a criterion for success // that the RMS of the fit residuals not exceed some maximum value. // The retry factors are applied in different ways: the height and widths // are multiplied by the retry factors while the center and angles are // increased by their factors. As of 2002/07/12 these are applied randomly // (instead of sequentially) to different components and combinations of // components. The factors can be specified by the user, but a default // set is available. This random method is better than the sequential method // for a limited number of retries, but true optimization of the retry system // would demand the use of a more sophisticated method. // // // // FitGaussian fitgauss(1,1); // Matrix x(5,1); x(0,0) = 0; x(1,0) = 1; x(2,0) = 2; x(3,0) = 3; x(4,0) = 4; // Vector y(5); y(0) = 0; y(1) = 1; y(2) = 4; y(3) = 1; y(4) = 1; // Matrix estimate(1,3); // estimate(0,0) = 1; estimate(0,1) = 1; estimate(0,2) = 1; // fitgauss.setFirstEstimate(estimate); // Matrix solution; // solution = fitgauss.fit(x,y); // cout << solution; // // // // Fitting multiple Gaussians is required for many different applications, // but requires a substantial amount of coding - especially if the // dimensionality of the image is not known to the programmer. Furthermore, // fitting multiple Gaussians has a very high failure rate. So, a specialized // Gaussian fitting class that retries from different initial estimates // until an acceptible fit was found was needed. // // //
      • T must be a real data type compatible with NonLinearFitLM - Float or // Double. // // //
      • AipsError if dimension is not 1, 2, or 3 //
      • AipsError if incorrect parameter number specified. //
      • AipsError if estimate/retry/data arrays are of wrong dimension // // //
      • Optimize the default retry matrix //
      • Send fitting messages to logger instead of console //
      • Consider using a more sophisticated retry ststem (above). //
      • Check the estimates for reasonability, especially on failure of fit. //
      • Consider adding other models (polynomial, etc) to make this a Fit3D // class. // template class FitGaussian { public: // Create the fitter. The dimension and the number of gaussians to fit // can be modified later if necessary. // FitGaussian(); FitGaussian(uInt dimension); FitGaussian(uInt dimension, uInt numgaussians); // // Adjust the number of dimensions void setDimensions(uInt dimensions); // Adjust the number of gaussians to fit void setNumGaussians(uInt numgaussians); // Set the initial estimate (the starting point of the first fit.) void setFirstEstimate(const Matrix& estimate); // Set the maximum number of retries. void setMaxRetries(uInt nretries) {itsMaxRetries = nretries;}; // Set the maximum amount of time to spend (in seconds). If time runs out // during a fit the process will still complete that fit. void setMaxTime(Double maxtime) {itsMaxTime = maxtime;}; // Set the retry factors, the values that are added/multiplied with the // first estimate on subsequent attempts if the first attempt fails. // Using the function with no argument sets the retry factors to the default. // void setRetryFactors(); void setRetryFactors(const Matrix& retryfactors); // // Return the number of retry options available uInt nRetryFactors() {return itsRetryFctr.nrow();}; // Mask out some parameters so that they are not modified during fitting Bool &mask(uInt gaussian, uInt parameter); const Bool &mask(uInt gaussian, uInt parameter) const; // Run the fit, using the data provided in the arguments pos and f. // The fit will retry from different initial estimates until it converges // to a value with an RMS error less than maximumRMS. If this cannot be // accomplished it will simply take the result that generated the best RMS. Matrix fit(const Matrix& pos, const Vector& f, T maximumRMS = 1.0, uInt maxiter = 1024, T convcriteria = 0.0001); Matrix fit(const Matrix& pos,const Vector& f, const Vector& sigma, T maximumRMS = 1.0, uInt maxiter = 1024, T convcriteria = 0.0001); // Allow access to the fit parameters from this class const Matrix &solution(){return itsSolutionParameters;}; const Matrix &errors(){return itsSolutionErrors;}; // Internal function for ensuring that parameters stay within their stated // domains (see Gaussian2D and Gaussian3D.) void correctParameters(Matrix& parameters); // Return the chi squared of the fit T chisquared(); // Return the RMS of the fit T RMS(); // Returns True if the fit (eventually) converged to a value. Bool converged(); private: uInt itsDimension; // how many dimensions (1, 2, or 3) uInt itsNGaussians; // number of gaussians to fit uInt itsMaxRetries; // maximum number of retries to attempt Double itsMaxTime; // maximum time to spend fitting in secs T itsChisquare; // chisquare of fit T itsRMS; // RMS of fit (sqrt[chisquare / N]) Bool itsSuccess; // flags success or failure LogIO os; Matrix itsFirstEstimate; // user's estimate. Matrix itsRetryFctr; // source of retry information Matrix itsMask; // masks parameters not to change in fitting // Sets the retry matrix to a default value. This is done automatically if // the retry matrix is not set directly. Matrix defaultRetryMatrix(); //Add one or more rows to the retry matrix. void expandRetryMatrix(uInt rowstoadd); //Find the number of unmasked parameters to be fit uInt countFreeParameters(); // The solutions to the fit Matrix itsSolutionParameters; // The errors on the solution parameters Matrix itsSolutionErrors; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Fitting/FitGaussian.tcc000066400000000000000000000520651476623553700213000ustar00rootroot00000000000000//# FitGaussian.cc: Multidimensional fitter class for Gaussians //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_FITGAUSSIAN_TCC #define SCIMATH_FITGAUSSIAN_TCC #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // The parameter chisqcriteria has been replaced with maximumRMS, which is // the square root of the average error-squared per pixel. // The fitter no longer returns the last (failed) fit if nothing meets the RMS // criteria. Instead it returns the fit which yielded the lowest chisquared. // IMPR: The retry system is unwieldy and very slow. It would be much // better generate retry paramters entirely automatically, requiring only // the number of tries to attempt from the user. This could be pretty easy // or quite complicated depending on how sophisticated a retry system is // desired: // Simple: control size of retry matrix and skip all tries above ntries // Complicated: actually use properties of failed attempts to move the start // point in an intelligent manner (ie orthogonal to start-end axis) template FitGaussian::FitGaussian() { itsDimension = 0; itsNGaussians = 0; itsMaxRetries = 0; itsMaxTime = DBL_MAX; itsSuccess = 0; } template FitGaussian::FitGaussian(uInt dimensions) { if ((dimensions == 0) || (dimensions > 3)) throw(AipsError("FitGaussian::FitGaussian(uInt dimensions) - " "dimensions must be 1, 2, or 3")); itsDimension = dimensions; itsNGaussians = 0; itsMaxRetries = 0; itsMaxTime = DBL_MAX; itsSuccess = 0; } template FitGaussian::FitGaussian(uInt dimensions, uInt numgaussians) { if ((dimensions == 0) || (dimensions > 3)) throw(AipsError("FitGaussian::FitGaussian(uInt dimensions, " "uInt numgaussians) - dimensions must be 1, 2, or 3")); itsDimension = dimensions; itsNGaussians = numgaussians; itsMask.resize(itsNGaussians, itsDimension*3); itsMask = 1; itsMaxRetries = 0; itsMaxTime = DBL_MAX; itsSuccess = 0; } template void FitGaussian::setDimensions(uInt dimensions) { if ((dimensions == 0) || (dimensions > 3)) throw(AipsError("FitGaussian::setDimenions(uInt dimensions)" " - dimensions must be 1, 2, or 3")); itsDimension = dimensions; itsMaxRetries = 0; itsMaxTime = DBL_MAX; itsRetryFctr.resize(); itsFirstEstimate.resize(); itsMask.resize(); if (itsNGaussians) { itsMask.resize(itsNGaussians, itsDimension*3); itsMask = 1; } } template void FitGaussian::setNumGaussians(uInt numgaussians) { itsNGaussians = numgaussians; itsMaxRetries = 0; itsMaxTime = DBL_MAX; itsRetryFctr.resize(); itsFirstEstimate.resize(); itsMask.resize(); if (itsDimension*3 != 0 && itsNGaussians) { itsMask.resize(itsNGaussians, itsDimension*3); itsMask = 1; } } template void FitGaussian::setFirstEstimate(const Matrix& estimate) { if ((estimate.nrow() != itsNGaussians) || (estimate.ncolumn() != itsDimension*3)) throw(AipsError("FitGaussian::setfirstestimate(const Matrix& " "estimate) - estimate must be of shape " "[(ngaussians) , (dimension x 3)]")); itsFirstEstimate.resize(); itsFirstEstimate = estimate; } template void FitGaussian::setRetryFactors() { setRetryFactors(defaultRetryMatrix()); } template void FitGaussian::setRetryFactors(const Matrix& retryfactors) { if (retryfactors.ncolumn() != itsDimension*3) throw(AipsError("FitGaussian::setretryfactors(const Matrix&" " retryfactors) - retryfactors must have numcolumns = " " dimension x 3")); itsRetryFctr.resize(); itsRetryFctr = retryfactors; } template Bool &FitGaussian::mask(uInt gaussian, uInt parameter) { if ((gaussian >= itsNGaussians) || (parameter >= itsDimension*3)) throw(AipsError("FitGaussian::mask(uInt gaussian, uInt parameter)" " - index out of range")); return itsMask(gaussian, parameter); } template const Bool &FitGaussian::mask(uInt gaussian, uInt parameter) const { if ((gaussian >= itsNGaussians) || (parameter >= itsDimension*3)) throw(AipsError("FitGaussian::mask(uInt gaussian, uInt parameter" " const - index out of range")); return itsMask(gaussian, parameter); } template Matrix FitGaussian::fit(const Matrix& pos, const Vector& f, T maximumRMS, uInt maxiter, T convcriteria) { //Same as below, with all sigma = 1. Vector sigma(f.nelements(), 1); return fit(pos, f, sigma, maximumRMS, maxiter, convcriteria); } template Matrix FitGaussian::fit(const Matrix& pos, const Vector& f, const Vector& sigma, T maximumRMS, uInt maxiter, T convcriteria) { //Perform the fitting to the data. Sets up NonLinearFitLM with the specified //number of gaussians and starts fitting. If the fit fails or converges //with an RMS above maximumRMS, it retries by multiplying certain //estimate gaussians by the retry matrix. uInt const ngpars = itsDimension*3; if (pos.ncolumn() != itsDimension) throw(AipsError("FitGaussian::fit(const Matrix& pos, const" " Vector& f, const Vector& sigma, T maximumRMS," " uInt maxiter, T convcriteria) - " " pos is of wrong number of dimensions.")); if ((pos.nrow() != f.nelements()) || (pos.nrow() != sigma.nelements())) throw(AipsError("FitGaussian::fit(const Matrix& pos, const" " Vector& f, const Vector& sigma, T maximumRMS," " uInt maxiter, T convcriteria) - " " pos, f, and sigma must all have same length.")); if (pos.nrow() <= 0) throw(AipsError("FitGaussian::fit(const Matrix& pos, const" " Vector& f, const Vector& sigma, T maximumRMS," " uInt maxiter, T convcriteria) - " " pos contains no data.")); NonLinearFitLM fitter(0); Vector solution,errors; Matrix startparameters(itsNGaussians, ngpars); itsSolutionParameters.resize(itsNGaussians, ngpars); itsSolutionErrors.resize(itsNGaussians,ngpars); Block > > gausscomp1d((itsDimension==1)*itsNGaussians); Block > > gausscomp2d((itsDimension==2)*itsNGaussians); Block > > gausscomp3d((itsDimension==3)*itsNGaussians); fitter.setMaxIter(maxiter); fitter.setCriteria(convcriteria); Vector targetmask(itsNGaussians,-1); //should rename this... uInt attempt = 0; //overall attempt number Int fitfailure; T bestRMS = FLT_MAX; //how to template this properly... itsSuccess = 0; if (itsMaxRetries > 0 && nRetryFactors() == 0) { setRetryFactors(); } //If there are not enough data points, fix some parameters to the estimate if (itsNGaussians >= pos.nrow()) { for (uInt p = 1; p < ngpars; p++) { for (uInt g = 0; g < itsNGaussians; g++) { mask(g,p) = 0; } } if (itsNGaussians > pos.nrow()) { uInt g = 0; while (countFreeParameters() > pos.nrow()) { mask(g,0) = 0; g++; } } } uInt fixpar = ngpars; while (countFreeParameters() > pos.nrow()) { fixpar--; if (fixpar == itsDimension * 2) fixpar = itsDimension; //fix widths last if (fixpar == 0) fixpar = itsDimension * 2; for (uInt g = 0; g < itsNGaussians; g++) { mask(g,fixpar) = 1; } } //Begin fitting Timer timer; timer.mark(); do { // Modify the estimate according to the retry factors, if necessary. if ((attempt) && (attempt <= itsMaxRetries)) { if (pow(Int(nRetryFactors()),Int(itsNGaussians)) = 0) { //apply retry factors Int retry = targetmask(g); if (itsDimension == 1) { if (p == 1) startparameters(g,p) += itsRetryFctr(retry,p); else startparameters(g,p) *= itsRetryFctr(retry,p); } if (itsDimension == 2) { if ((p == 1) || (p == 2) || (p == 5)) { startparameters(g,p) += itsRetryFctr(retry,p); } else { startparameters(g,p) *= itsRetryFctr(retry,p); } } if (itsDimension == 3) { if ((p == 1) || (p == 2) || (p == 3) || (p == 7) || (p == 8)) { startparameters(g,p) += itsRetryFctr(retry,p); } else { startparameters(g,p) *= itsRetryFctr(retry,p); } } } if (itsDimension==1) { gausscomp1d[g][p]=AutoDiff(startparameters(g,p), ngpars, p); gausscomp1d[g].mask(p) = itsMask(g,p); } if (itsDimension==2) { gausscomp2d[g][p]=AutoDiff(startparameters(g,p), ngpars, p); gausscomp2d[g].mask(p) = itsMask(g,p); } if (itsDimension==3) { gausscomp3d[g][p]=AutoDiff(startparameters(g,p), ngpars, p); gausscomp3d[g].mask(p) = itsMask(g,p); } } } // Create the fitting function by summing up the component gaussians. CompoundFunction > sumfunc; for (uInt g = 0; g < itsNGaussians; g++) { if (itsDimension==1) sumfunc.addFunction(gausscomp1d[g]); if (itsDimension==2) sumfunc.addFunction(gausscomp2d[g]); if (itsDimension==3) sumfunc.addFunction(gausscomp3d[g]); } fitter.setFunction(sumfunc); //sumgauss fitter.setCriteria(convcriteria); solution.resize(0); errors.resize(0); fitfailure = 0; attempt++; LogIO os(LogOrigin("FitGaussian", "fit", WHERE)); os << LogIO::DEBUG1 << "Attempt " << attempt << ": "; // Perform the fit, and check for problems with the results. try { solution = fitter.fit(pos, f, sigma); errors = fitter.errors(); } catch (AipsError& fittererror) { string errormessage; errormessage = fittererror.getMesg(); os << LogIO::DEBUG1 << "Unsuccessful - Error during fitting." << LogIO::POST; os << LogIO::DEBUG1 << errormessage << LogIO::POST; fitfailure = 2; } if (!fitter.converged() && !fitfailure) { fitfailure = 1; os < maximumRMS) { os << LogIO::DEBUG1 << "Unsuccessful - RMS of " << itsRMS; os << LogIO::DEBUG1 << " is outside acceptible limits." << LogIO::POST; fitfailure = 5; } else { os << LogIO::DEBUG1 << "Converged after " << fitter.currentIteration() << " iterations" << LogIO::POST; } if (itsRMS < bestRMS) { //best fit so far - write parameters to solution matrix for (uInt g = 0; g < itsNGaussians; g++) { for (uInt p = 0; p < ngpars; p++) { itsSolutionParameters(g,p) = solution(g*ngpars+p); itsSolutionErrors(g,p) = errors(g*ngpars+p); } } bestRMS = itsRMS; itsSuccess = 1; //even if it's not a complete success } } } } } while ((fitfailure) && (attempt <= itsMaxRetries) && (timer.real() < itsMaxTime)); // If at least one convergent solution has been found, return its parameters if (itsSuccess) { if (fitfailure) { if (attempt > itsMaxRetries) { os << LogIO::DEBUG1 << "Retry limit reached, "; } else if (timer.real() >= itsMaxTime) { os << LogIO::DEBUG1 << "Time limit reached, "; } os << LogIO::DEBUG1 << "no fit satisfies RMS criterion; using best available fit"<< LogIO::POST; } correctParameters(itsSolutionParameters); return itsSolutionParameters; } // Otherwise, return all zeros os<< LogIO::WARN << "FAILURE - could not find acceptible convergent solution." << endl; itsSuccess = 0; for (uInt g = 0; g < itsNGaussians; g++) { for (uInt p = 0; p < ngpars; p++) { itsSolutionParameters(g,p) = T(0.0); itsSolutionErrors(g,p) = T(0.0); } } // return itsSolutionParameters; } template void FitGaussian::correctParameters(Matrix& parameters) { //bring rotation/axis values into the stated domain. for (uInt g = 0; g < itsNGaussians; g++) { if (itsDimension == 2) { if (parameters(g,4) > 1) { parameters(g,3) *= parameters(g,4); parameters(g,4) = 1/parameters(g,4); //swap axes parameters(g,5) += M_PI_2; } if (abs(parameters(g,5)) > 1e+5) continue; //spin control //IMPR: a useful thing to do would be to retry the fit with all other //params fixed if the PA ends up crazy like this. while (parameters(g,5) < 0) parameters(g,5) += M_PI; while (parameters(g,5) > M_PI) parameters(g,5) -= M_PI; } if (itsDimension == 3) { if (abs(parameters(g,7)) > 1e+5) continue; //spin control while (parameters(g,7) < -M_PI_2) parameters(g,7) += M_PI; while (parameters(g,7) > M_PI_2) parameters(g,7) -= M_PI; if (abs(parameters(g,8)) > 1e+5) continue; //spin control while (parameters(g,8) < -M_PI_2) parameters(g,8) += M_PI; while (parameters(g,8) > M_PI_2) parameters(g,8) -= M_PI; if (abs(parameters(g,7)) > M_PI_4) { //swap y/x axes T temp = parameters(g,4); parameters(g,4) = parameters(g,5); parameters(g,5) = temp; if (parameters(g,7) > 0) parameters(g,7) -= M_PI_2; else parameters(g,7) += M_PI_2; } if (abs(parameters(g,8)) > M_PI_4) { //swap z/x axes T temp = parameters(g,4); parameters(g,4) = parameters(g,6); parameters(g,6) = temp; if (parameters(g,8) > 0) { parameters(g,8) -= M_PI_2; } else { parameters(g,8) += M_PI_2; } } } } return; } template Bool FitGaussian::converged() { //Did the fitter converge to an acceptible value? return itsSuccess; } template T FitGaussian::chisquared() { //Chisquared of completed fit IMPR: shouldn't work if no convergence? return itsChisquare; } template T FitGaussian::RMS() { //RMS of completed fit return itsRMS; } template Matrix FitGaussian::defaultRetryMatrix() { Matrix rt(7,itsDimension * 3); rt.column(0) = 1; if (itsDimension == 1) { rt.column(1) = 0; rt(0,2) = 0.5; rt(1,2) = 0.6; rt(2,2) = 0.7; rt(3,2) = 0.8; rt(4,2) = 0.9; rt(5,2) = 1.3; rt(6,2) = 2.0; } if (itsDimension == 2) { rt.column(1) = 0; rt.column(2) = 0; rt(0,3) = 1; rt(0,4) = 0.6; rt(0,5) = 0; rt(1,3) = 0.5; rt(1,4) = 1; rt(1,5) = 0; rt(2,3) = 1; rt(2,4) = 1; rt(2,5) = 0.52; rt(3,3) = 1; rt(3,4) = 1; rt(3,5) = -0.52; rt(4,3) = 1.5; rt(4,4) = 1; rt(4,5) = 0; rt(5,3) = 1; rt(5,4) = 0.6; rt(5,5) = 0.52; rt(6,4) = 1; rt(6,4) = 0.6; rt(6,5) = -0.52; } if (itsDimension == 3) { rt.column(1) = 0; rt.column(2) = 0; rt.column(3) = 0; rt(0,4) = 1.5; rt(0,5) = 0.9; rt(0,6) = 0.5; rt(0,7) = 0; rt(0,8) = 0; rt(1,4) = 0.4; rt(1,5) = 0.4; rt(1,6) = 0.4; rt(1,7) = 0; rt(1,8) = 0; rt(2,4) = 1.5; rt(2,5) = 1.5; rt(2,6) = 1; rt(2,7) = 0.5; rt(2,8) = 0; rt(3,4) = 1.2; rt(3,5) = 1.2; rt(3,6) = 1.5; rt(3,7) = 0; rt(3,8) = 0.5; rt(4,4) = 1.5; rt(4,5) = 1.5; rt(4,6) = 1; rt(4,7) =-0.5; rt(4,8) = 0; rt(5,4) = 1.5; rt(5,5) = 1.5; rt(5,6) = 1.5; rt(5,7) = 0.5; rt(5,8) = 0.5; rt(6,4) = 1.5; rt(6,5) = 1.5; rt(6,6) = 1.5; rt(6,7) =-0.5; rt(6,8) =-0.5; //increasing axis sizes on rotation only useful if estimated rotation is 0 } return rt; } template void FitGaussian::expandRetryMatrix(uInt rowstoadd) { //use random numbers to expand the retry matrix by a given number of rows. uInt initnrows = itsRetryFctr.shape()(0); uInt npars = itsRetryFctr.shape()(1); Matrix rt(initnrows + rowstoadd, npars); for (uInt r = 0; r < initnrows; r++) { for (uInt p = 0; p < npars; p++) { rt(r,p) = itsRetryFctr(r,p); } } Time tmptime(1982,8,31,10); MLCG gen(Int(tmptime.age())); Uniform fgen(&gen, 0.0, 1.0); for (uInt r = initnrows; r < initnrows + rowstoadd; r++) { if (itsDimension == 1) { rt(r,0) = 1; rt(r,1) = 0; rt(r,2) = fgen() + 0.5; } if (itsDimension == 2) { rt(r,0) = 1; rt(r,1) = 0; rt(r,2) = 0; rt(r,3) = fgen() + 0.5; rt(r,4) = fgen() * 0.7 + 0.3; rt(r,5) = fgen() - 0.5; } if (itsDimension == 3) { rt(r,0) = 1; rt(r,1) = 0; rt(r,2) = 0; rt(r,3) = 0; rt(r,4) = fgen() + 0.5; rt(r,5) = fgen() + 0.5; rt(r,6) = fgen() + 0.5; rt(r,7) = fgen() - 0.5; rt(r,8) = fgen() - 0.5; } } itsRetryFctr.resize(); itsRetryFctr = rt; } template uInt FitGaussian::countFreeParameters() { uInt nfreepars = 0; for (uInt g = 0; g < itsNGaussians; g++) { for (uInt p = 0; p < itsDimension*3; p++) { if (!itsMask(g,p)) nfreepars++; } } return nfreepars; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Fitting/FittingProxy.cc000066400000000000000000000436171476623553700213500ustar00rootroot00000000000000//# FittingProxy: This class gives access to fitting //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // FitType // Constructor FittingProxy::FitType::FitType() : fitter_p(0), fitterCX_p(0), n_p(0), nceq_p(0), nreal_p(0), typ_p(0), colfac_p(1e-8), lmfac_p(1e-3), soldone_p(False), nr_p(0) {} FittingProxy::FitType::~FitType() { delete fitter_p; fitter_p = 0; delete fitterCX_p; fitterCX_p = 0; } // Methods void FittingProxy::FitType::setFitter(GenericL2Fit *ptr) { delete fitter_p; fitter_p = 0; delete fitterCX_p; fitterCX_p = 0; fitter_p = ptr; } void FittingProxy::FitType::setFitterCX(GenericL2Fit *ptr) { delete fitter_p; fitter_p = 0; delete fitterCX_p; fitterCX_p = 0; fitterCX_p = ptr; } GenericL2Fit *const& FittingProxy::FitType::getFitter() const { return fitter_p; } GenericL2Fit *const& FittingProxy::FitType::getFitterCX() const { return fitterCX_p; } void FittingProxy::FitType::setStatus(Int n, Int typ, Double colfac, Double lmfac) { n_p = n; typ_p = typ; nceq_p = (typ == 3 || typ == 11) ? 2*n_p : n_p; nreal_p = (typ_p != 0) ? 2*n_p : n_p; colfac_p = colfac; lmfac_p = lmfac; } void FittingProxy::FitType::setSolved(Bool solved) { soldone_p = solved; } // FittingProxy // Constructors FittingProxy::FittingProxy() : nFitter_p(0), list_p(0) {} // Destructor FittingProxy::~FittingProxy() { for (uInt i=0; igetFitter()) { res.define(String("n"), list_p[id]->getN()); res.define(String("typ"), list_p[id]->getType()); res.define(String("colfac"), list_p[id]->getColfac()); res.define(String("lmfac"), list_p[id]->getLMfac()); } return res; } Bool FittingProxy::init(Int id, Int n, Int tp, Double colfac, Double lmfac) { // init: init a fitter if (tp == 0) { if (!list_p[id]->getFitter()) { list_p[id]->setFitter(new LinearFitSVD); } list_p[id]->getFitter()->set(n); list_p[id]->getFitter()->set(abs(colfac), abs(lmfac)); } else { if (!list_p[id]->getFitterCX()) { list_p[id]->setFitterCX(new LinearFitSVD); } list_p[id]->getFitterCX()->set(n); list_p[id]->getFitterCX()->set(abs(colfac), abs(lmfac)); } list_p[id]->setStatus(n, tp, abs(colfac), abs(lmfac)); list_p[id]->setSolved(False); return True; } Bool FittingProxy::done(Int id) { if (!list_p[id]->getFitter() && !list_p[id]->getFitterCX()) { throw(AipsError("Trying to undo a non-existing fitter")); } list_p[id]->setFitter(0); return True; } Bool FittingProxy::reset(Int id) { if (!list_p[id]->getFitter() && !list_p[id]->getFitterCX()) { throw(AipsError("Trying to reset a non-existing fitter")); } if (list_p[id]->getFitter()) list_p[id]->getFitter()->reset(); else list_p[id]->getFitterCX()->reset(); list_p[id]->setSolved(False); return True; } Bool FittingProxy::set(Int id, Int nin, Int tpin, Double colfac, Double lmfac) { if (!list_p[id]->getFitter() && !list_p[id]->getFitterCX()) { throw(AipsError("Trying to set properties of non-existing fitter")); } Int n = nin; Int tp = tpin; Double cf = colfac; Double lmf = lmfac; if (n == -1) n = list_p[id]->getN(); if (tp== -1) tp = list_p[id]->getType(); if (cf < 0) cf = list_p[id]->getColfac(); if (lmf< 0) lmf = list_p[id]->getLMfac(); if (list_p[id]->getFitter()) { list_p[id]->getFitter()->set(n); list_p[id]->getFitter()->set(cf, lmf); } else { list_p[id]->getFitter()->set(n); list_p[id]->getFitter()->set(cf, lmf); } list_p[id]->setStatus(n, tp, cf, lmf); list_p[id]->setSolved(False); return True; } Record FittingProxy::functional(Int id, const Record& fnc, const Vector& xval, const Vector& yval, const Vector& wt, Int mxit, const Record& constraint) { Int rank, deficiency; Double sd, mu, chi2; Vector constr, err, returnval; Array covar; String errmsg; NonLinearFitLM fitter; fitter.setMaxIter(mxit); fitter.asWeight(True); FunctionHolder fnh; Function > *fn(0); if (!fnh.getRecord(errmsg, fn, fnc)) throw(AipsError(errmsg)); fitter.setFunction(*fn); if (xval.nelements() != fn->ndim()*yval.nelements()) { throw(AipsError("Functional fitter x and y lengths disagree")); } for (uInt i=0; i x; con.get(RecordFieldId("x"), x); Double y; con.get(RecordFieldId("y"), y); HyperPlane > constrFun(x.nelements()); fitter.addConstraint(constrFun, x, y); } else { throw(AipsError("Illegal definition of a constraint in addconstraint")); } } IPosition ip2(2, xval.nelements(), fn->ndim()); if (fn->ndim() > 1) ip2[0] /= fn->ndim(); Matrix mval(ip2); Array::const_iterator cit = xval.begin(); for (ArrayAccessor > i(mval); i!=i.end(); ++i) { for (uInt j=0; jndim(); ++cit, ++j) i.index >(j) = *cit; } if (wt.nelements() == 0 || (wt.nelements() == 1 && yval.nelements() != 1)) { returnval = fitter.fit(mval, yval); } else { returnval = fitter.fit(mval, yval, wt); } rank = fitter.getRank(); deficiency = fitter.getDeficiency(); sd = fitter.getSD(); mu = fitter.getWeightedSD(); chi2 = fitter.getChi2(); constr.resize(returnval.nelements()*fitter.getDeficiency()); Double *conit = constr.data(); casacore::Vector ctmp(returnval.nelements()); Double *ctit = ctmp.data(); for (uInt i=0; isetSolved(True); Record out; out.define("rank", rank); out.define("sd", sd); out.define("mu", mu); out.define("chi2", chi2); out.define("constr", constr); out.define("covar", covar); out.define("error", err); out.define("deficiency", deficiency); out.define("sol", returnval); return out; } Record FittingProxy::linear(Int id, const Record& fnc, const Vector& xval, const Vector& yval, const Vector& wt, const Record& constraint) { Int rank, deficiency; Double sd, mu, chi2; Vector constr, err, returnval; Array covar; String errmsg; LinearFitSVD fitter; fitter.asWeight(True); FunctionHolder fnh; Function > *fn(0); if (!fnh.getRecord(errmsg, fn, fnc)) throw(AipsError(errmsg)); fitter.setFunction(*fn); if (xval.nelements() != fn->ndim()*yval.nelements()) { throw(AipsError("Linear fitter x and y lengths disagree")); } for (uInt i=0; i x; con.get(RecordFieldId("x"), x); Double y; con.get(RecordFieldId("y"), y); HyperPlane > constrFun(x.nelements()); fitter.addConstraint(constrFun, x, y); } else { throw(AipsError("Illegal definition of a constraint in addconstraint")); } } IPosition ip2(2, xval.nelements(), fn->ndim()); if (fn->ndim() > 1) ip2[0] /= fn->ndim(); Matrix mval(ip2); Array::const_iterator cit = xval.begin(); for (ArrayAccessor > i(mval); i!=i.end(); ++i) { for (uInt j=0; jndim(); ++cit, ++j) i.index >(j) = *cit; } if (wt.nelements() == 0 || (wt.nelements() == 1 && yval.nelements() != 1)) { returnval = fitter.fit(mval, yval); } else { returnval = fitter.fit(mval, yval, wt); } rank = fitter.getRank(); deficiency = fitter.getDeficiency(); sd = fitter.getSD(); mu = fitter.getWeightedSD(); chi2 = fitter.getChi2(); constr.resize(returnval.nelements()*fitter.getDeficiency()); Double *conit = constr.data(); casacore::Vector ctmp(returnval.nelements()); for (uInt i=0; isetSolved(True); Record out; out.define("rank", rank); out.define("sd", sd); out.define("mu", mu); out.define("chi2", chi2); out.define("constr", constr); out.define("covar", covar); out.define("error", err); out.define("deficiency", deficiency); out.define("sol", returnval); return out; } Record FittingProxy::cxfunctional(Int id, const Record& fnc, const Vector& xval, const Vector& yval, const Vector& wt, Int mxit, const Record& constraint) { Int rank, deficiency; Double sd, mu, chi2; Vector err, returnval; Vector constr; Array covar; String errmsg; NonLinearFitLM fitter; fitter.setMaxIter(mxit); fitter.asWeight(True); FunctionHolder fnh; Function > *fn(0); if (!fnh.getRecord(errmsg, fn, fnc)) throw(AipsError(errmsg)); fitter.setFunction(*fn); if (xval.nelements() != fn->ndim()*yval.nelements()) { throw(AipsError("Functional fitter x and y lengths disagree")); } for (uInt i=0; i x; con.get(RecordFieldId("x"), x); DComplex y; con.get(RecordFieldId("y"), y); HyperPlane > constrFun(x.nelements()); fitter.addConstraint(constrFun, x, y); } else { throw(AipsError("Illegal definition of a constraint in addconstraint")); } } IPosition ip2(2, xval.nelements(), fn->ndim()); if (fn->ndim() > 1) ip2[0] /= fn->ndim(); Matrix mval(ip2); Array::const_iterator cit = xval.begin(); for (ArrayAccessor > i(mval); i!=i.end(); ++i) { for (uInt j=0; jndim(); ++cit, ++j) i.index >(j) = *cit; } if (wt.nelements() == 0 || (wt.nelements() == 1 && yval.nelements() != 1)) { returnval = fitter.fit(mval, yval); } else { returnval = fitter.fit(mval, yval, wt); } rank = fitter.getRank(); deficiency = fitter.getDeficiency(); sd = fitter.getSD(); mu = fitter.getWeightedSD(); chi2 = fitter.getChi2(); constr.resize(returnval.nelements()*fitter.getDeficiency()); Double *conit = constr.data(); casacore::Vector ctmp(returnval.nelements()); Double *ctit = ctmp.data(); for (uInt i=0; isetSolved(True); Record out; out.define("rank", rank); out.define("sd", sd); out.define("mu", mu); out.define("chi2", chi2); out.define("constr", constr); out.define("covar", covar); out.define("error", err); out.define("deficiency", deficiency); out.define("sol", returnval); return out; } Record FittingProxy::cxlinear(Int id, const Record& fnc, const Vector& xval, const Vector& yval, const Vector& wt, const Record& constraint) { Int rank, deficiency; Double sd, mu, chi2; Vector err, returnval; Vector constr; Array covar; String errmsg; LinearFitSVD fitter; fitter.asWeight(True); FunctionHolder fnh; Function > *fn(0); if (!fnh.getRecord(errmsg, fn, fnc)) throw(AipsError(errmsg)); fitter.setFunction(*fn); if (xval.nelements() != fn->ndim()*yval.nelements()) { throw(AipsError("Linear fitter x and y lengths disagree")); } for (uInt i=0; i x; con.get(RecordFieldId("x"), x); DComplex y; con.get(RecordFieldId("y"), y); HyperPlane > constrFun(x.nelements()); fitter.addConstraint(constrFun, x, y); } else { throw(AipsError("Illegal definition of a constraint in addconstraint")); } } IPosition ip2(2, xval.nelements(), fn->ndim()); if (fn->ndim() > 1) ip2[0] /= fn->ndim(); Matrix mval(ip2); Array::const_iterator cit = xval.begin(); for (ArrayAccessor > i(mval); i!=i.end(); ++i) { for (uInt j=0; jndim(); ++cit, ++j) i.index >(j) = *cit; } if (wt.nelements() == 0 || (wt.nelements() == 1 && yval.nelements() != 1)) { returnval = fitter.fit(mval, yval); } else { returnval = fitter.fit(mval, yval, wt); } rank = fitter.getRank(); deficiency = fitter.getDeficiency(); sd = fitter.getSD(); mu = fitter.getWeightedSD(); chi2 = fitter.getChi2(); constr.resize(returnval.nelements()*fitter.getDeficiency()); Double *conit = constr.data(); casacore::Vector ctmp(returnval.nelements()); for (uInt i=0; isetSolved(True); Record out; out.define("rank", rank); out.define("sd", sd); out.define("mu", mu); out.define("chi2", chi2); out.define("constr", constr); out.define("covar", covar); out.define("error", err); out.define("deficiency", deficiency); out.define("sol", returnval); return out; } } //# NAMESPACE CASACORE - END casacore-3.7.1/scimath/Fitting/FittingProxy.h000066400000000000000000000131471476623553700212050ustar00rootroot00000000000000//# DittingProxy.h: This class gives object access to Fitting //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_FITTINGPROXY_H #define SCIMATH_FITTINGPROXY_H //# Includes #include #include #include //# Forward declarations namespace casacore { //# NAMESPACE CASACORE - BEGIN class String; template class GenericL2Fit; // This class gives Proxy to Fitting connection // // // // //
      • Fitting // // // Distributed Object and fitting // // // The class makes the connection between the // Fitting module and // other object system. It provides a series of proxy callable // methods. See Note 197 for details.
        // Operations supported // are all the fitting methods supported in the Fitting module //
        // // // // To provide a direct user interface between the user and // Fitting related calculations. // // //
      • Nothing I know of // class FittingProxy { public: //# Standard constructors/destructors FittingProxy(); virtual ~FittingProxy(); Int getid(); Record getstate(Int id); Bool init(Int id, Int n, Int tp, Double colfac, Double lmfac); Bool done(Int id); Bool reset(Int id); Bool set(Int id, Int nin, Int tpin, Double colfac, Double lmfac); Record functional(Int id, const Record& fnc, const Vector& xval, const Vector& yval, const Vector& wt, Int mxit, const Record& constraint); Record linear(Int id, const Record& fnc, const Vector& xval, const Vector& yval, const Vector& wt, const Record& constraint); Record cxfunctional(Int id, const Record& fnc, const Vector& xval, const Vector& yval, const Vector& wt, Int mxit, const Record& constraint); Record cxlinear(Int id, const Record& fnc, const Vector& xval, const Vector& yval, const Vector& wt, const Record& constraint); private: // Class to aid in distributing different fitters class FitType { public: //# Constructors // Default constructor: no method known FitType(); // Destructor ~FitType(); //# Method // Set a fitter pointer (real or complex) // void setFitter (GenericL2Fit *ptr); void setFitterCX (GenericL2Fit *ptr); // // Get a fitter pointer (real or complex) // GenericL2Fit *const &getFitter() const; GenericL2Fit *const &getFitterCX() const; // // Set the status void setStatus(Int n, Int typ, Double colfac, Double lmfac); // Get the number of terms in condition equation Int getNceq() const { return nceq_p;} ; // Get the number of unknowns Int getN() const { return n_p;} ; // Get the number of real unknowns Int getNreal() const { return nreal_p;} ; // Get the type Int getType() const { return typ_p;} ; // Get the collinearity factor Double getColfac() const { return colfac_p;} ; // Get the Levenberg-Marquardt factor Double getLMfac() const { return lmfac_p;} ; // Set solution done or not void setSolved(Bool solved); // Solution done? Bool getSolved() const { return soldone_p;} ; private: // Copy constructor: not implemented FitType(const FitType &other); // Assignment: not implemented FitType &operator=(const FitType &other); //# Data // Pointer to a Fitting Machine: real or complex // casacore::GenericL2Fit *fitter_p; casacore::GenericL2Fit *fitterCX_p; // // Number of unknowns Int n_p; // Number of terms in condition equation Int nceq_p; // Number of real unknowns Int nreal_p; // Type Int typ_p; // Collinearity factor Double colfac_p; // Levenberg-Marquardt factor Double lmfac_p; // Solution done? Bool soldone_p; // System's rank deficiency uInt nr_p; }; //# Member functions //# Data // Number of FitType obkects present uInt nFitter_p; // List of FitTypes FitType **list_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Fitting/GenericL2Fit.h000066400000000000000000000574111476623553700207560ustar00rootroot00000000000000//# GenericL2Fit.h: Generic base class for least-squares fit. //# //# Copyright (C) 2001,2002,2004,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_GENERICL2FIT_H #define SCIMATH_GENERICL2FIT_H //# Includes #include #include #include #include #include #include #include #include #include namespace casacore { // begin namespace casa //# Forward declarations template class Function; // Generic base class for least-squares fit. // // // // // // //
      • Function //
      • Fitting // // // // A set of data point is fit with some functional equation. // The class acts as a generic base class for L2 type // fits. // // // // NOTE: Constraints added. Documentation out of date at moment, check // the tLinearFitSVD and tNonLinearFitLM programs for examples. // // The class acts as a base class for L2-type (least-squares) fitting. // Actual classes (se e.g. LinearFit and // NonLinearFit. // // The following is a brief summary of the linear least-squares fit problem. // See module header, Fitting, // for a more complete description. // // Given a set of N data points (measurements), (x(i), y(i)) i = 0,...,N-1, // along with a set of standard deviations, sigma(i), for the data points, // and M specified functions, f(j)(x) j = 0,...,M-1, we form a linear // combination of the functions: // // z(i) = a(0)f(0)(x(i)) + a(1)f(1)(x(i)) + ... + a(M-1)f(M-1)(x(i)), // // where a(j) j = 0,...,M-1 are a set of parameters to be determined. // The linear least-squares fit tries to minimize // // chi-square = [(y(0)-z(0))/sigma(0)]^2 + [(y(1)-z(1))/sigma(1)]^2 + ... // + [(y(N-1)-z(N-1))/sigma(N-1)]^2. // // by adjusting {a(j)} in the equation. // // For complex numbers, [(y(i)-z(i))/sigma(i)]^2 in chi-square // is replaced by // [(y(i)-z(i))/sigma(i)]*conjugate([(y(i)-z(i))/sigma(i)]) // // For multidimensional functions, x(i) is a vector, and // // f(j)(x(i)) = f(j)(x(i,0), x(i,1), x(i,2), ...) // // // Normally, it is necessary that N > M for the solutions to be valid, since // there must be more data points than model parameters to be solved. // // If the measurement errors (standard deviation sigma) are not known // at all, they can all be set to one initially. In this case, we assume all // measurements have the same standard deviation, after minimizing // chi-square, we recompute // // sigma^2 = {(y(0)-z(0))^2 + (y(1)-z(1))^2 + ... // + (y(N-1)-z(N-1))^2}/(N-M) = chi-square/(N-M). // // // A statistic weight can also be assigned to each measurement if the // standard deviation is not available. sigma can be calculated from // // sigma = 1/ sqrt(weight) // // Alternatively a 'weight' switch can be set with asWeight(). // For best arithmetic performance, weight should be normalized to a maximum // value of one. Having a large weight value can sometimes lead to overflow // problems. // // The function to be fitted to the data can be given as an instance of the // Function class. // One can also form a sum of functions using the // CompoundFunction. // // For small datasets the usage of the calls is: //
          //
        • Create a functional description of the parameters //
        • Create a fitter: GenericL2Fit fitter(); //
        • Set the functional representation: fitter.setFunction() //
        • Do the fit to the data: fitter.fit(x, data, sigma) // (or do a number of calls to buildNormalMatrix(x, data, sigma) // and finish of with fitter.fit() or fitter.sol()) //
        • if needed the covariance; residuals; chiSquared, parameter errors // can all be obtained //
        // Note that the fitter is reusable. An example is given in the following. // // The solution of a fit always produces the total number of parameters given // to the fitter. I.e. including any parameters that were fixed. In the // latter case the solution returned will be the fixed value. // // //
      • The following data types can be used to instantiate the GenericL2Fit // templated class: // Known classes for FunctionTraits. I.e simple numerical like // Float, Double, Complex, // DComplex; and the AutoDiff<> versions. // // // If there are a large number of unknowns or a large number of data points // machine memory limits (or timing reasons) may not allow a complete // in-core fitting to be performed. In this case one can incrementally // build the normal equation (see buildNormalMatrix()). // // The normal operation of the class tests for real inversion problems // only. If tests are needed for almost collinear columns in the // solution matrix, the collinearity can be set as the square of the sine of // the minimum angle allowed. // // Singular Value Decomposition is supported by the // asSVD() (which will also set the // default collinearity to 1e-8). // // Other information (see a.o. LSQFit) can // be set and obtained as well. // // // // The creation of this class was driven by the need to write code // to perform baseline fitting or continuum subtraction. // // // In the following a polynomial is fitted through the first 20 prime numbers. // The data is given in the x vector (1 to 20) and in the primesTable // (2, 3, ..., 71) (see tLinearFitSVD test program). In the following // all four methods to calculate a polynomial through the data is used // // // The list of coordinate x-values // Vector x(nPrimes); // indgen(x, 1.0); // 1, 2, ... // Vector primesTable(nPrimes); // for (uInt i=1; i < nPrimes; i++) { // primesTable(i) = // Primes::nextLargerPrimeThan(Int(primesTable(i-1)+0.01)); // } // Vector sigma(nPrimes); // sigma = 1.0; // // The fitter // LinearFit fitter; // // Linear combination of functions describing 1 + x + x*x // combination.setCoefficient(0, 1.0); // 1 // combination.setCoefficient(1, 1.0); // x // combination.setCoefficient(2, 1.0); // x^2 // // Get the solution // fitter.setFunction(combination); // Vector solution = fitter.fit(x, primesTable, sigma); // // Try with a function with automatic derivatives (note that default // // polynomial has zero first guess) // LinearFit > fitad; // Polynomial > sqre(2); // fitad.setFunction(sqre); // solution = fitad.fit(x, primesTable, sigma); // // In the test program examples are given on how to get the other // information, and other examples. // template class GenericL2Fit : public LSQaips { public: //# Constants // Default collinearity test for SVD const Double COLLINEARITY; //# Constructors // Create a fitter: the normal way to generate a fitter object. Necessary // data will be deduced from the Functional provided with // setFunction() GenericL2Fit(); // Copy constructor (deep copy) GenericL2Fit(const GenericL2Fit &other); // Assignment (deep copy) GenericL2Fit &operator=(const GenericL2Fit &other); // Destructor virtual ~GenericL2Fit(); // Sets the function to be fitted. Upon entry, the argument function object // is cloned. The cloned copy is used in the later fitting process. // A valid function should be an instance of the // Function class, // so that derivatives with respect to the adjustable parameters // can be calculated. The current values of the "available" parameters // of the function are taken as the initial guess for the non-linear fitting. template void setFunction(const Function &function) { resetFunction(); ptr_derive_p = function.cloneAD(); setFunctionEx(); } // Set the possible constraint functions. The addConstraint // will add one; the setConstraint will [re-]set the // nth constraint. If unsucessful, False returned.
        // Constraint functional can only be set when the function to be fitted // has been set. It should have the same number of parameters as the function // to be fitted. The x should have the correct dimension. // template Bool setConstraint(const uInt n, const Function &function, const Vector::BaseType> &x, const typename FunctionTraits::BaseType y= typename FunctionTraits::BaseType(0)) { if (n >= constrFun_p.nelements() || !ptr_derive_p || ptr_derive_p->nparameters() != function.nparameters() || function.ndim() != x.nelements()) return False; delete constrFun_p[n]; constrFun_p[n] = 0; constrFun_p[n] = function.cloneAD(); return setConstraintEx(n, x, y); } Bool setConstraint(const uInt n, const Vector::BaseType> &x, const typename FunctionTraits::BaseType y= typename FunctionTraits::BaseType(0)); Bool setConstraint(const uInt n, const typename FunctionTraits::BaseType y= typename FunctionTraits::BaseType(0)); Bool addConstraint(const Function::DiffType, typename FunctionTraits::DiffType> &function, const Vector::BaseType> &x, const typename FunctionTraits::BaseType y= typename FunctionTraits::BaseType(0)); Bool addConstraint(const Vector::BaseType> &x, const typename FunctionTraits::BaseType y= typename FunctionTraits::BaseType(0)); Bool addConstraint(const typename FunctionTraits::BaseType y= typename FunctionTraits::BaseType(0)); // // Set the collinearity factor as the square of the sine of the // minimum angle allowed between input vectors (default zero for non-SVD, // 1e-8 for SVD) void setCollinearity(const Double cln); // Set sigma values to be interpreted as weight (i.e. 1/sigma/sigma). // A value of zero or -1 will be skipped. The switch will stay in effect // until set False again explicitly. Default is False. void asWeight(const Bool aswgt) { asweight_p = aswgt; } // Set the use of SVD or not (default). When set the default collinearity // is set as well. void asSVD(const Bool svd); // Return a pointer to the function being fitted. Should // never delete this pointer. // Function::DiffType, typename FunctionTraits::DiffType> *fittedFunction() { return ptr_derive_p; } const Function::DiffType, typename FunctionTraits::DiffType>* fittedFunction() const { return ptr_derive_p; } // // Return the number of fitted parameters uInt fittedNumber() const { return aCount_ai; } // Return the number of constraints, and pointers to constraint functions. // A 0-pointer will be returned if no such constraint present. // This pointer should never be destroyed. // uInt NConstraints() { return constrFun_p.nelements(); } Function::DiffType, typename FunctionTraits::DiffType> *getConstraint(const uInt n) { return (n >= constrFun_p.nelements() ? 0 : constrFun_p[n]); } // // Return the nth constraint equation derived from SVD // Note that the number present will be given by getDeficiency() Vector:: BaseType>::base> getSVDConstraint(uInt n); // Set the parameter values. The input is a vector of parameters; all // or only the masked ones' values will be set, using the input values // void setParameterValues (const Vector::BaseType> &parms); void setMaskedParameterValues (const Vector::BaseType> &parms); // // Fit the function to the data. If no sigma provided, all ones assumed. // In the case of no x,y,sigma the fitting equations are supposed to be // generated by previous calls to buildNormalMatrix. Note that the ones // with a scalar sigma will assume sigma=1 (overloading problem). The mask // assumes that if present, points with False will be skipped. // //
      • AipsError if unmatched array sizes given //
      • AipsError if equations cannot be inverted (not in SVD case and in // the case of the Bool versions.) // // Vector::BaseType> fit(const Vector::BaseType> &x, const Vector::BaseType> &y, const Vector::BaseType> &sigma, const Vector *const mask=0); Vector::BaseType> fit(const Matrix::BaseType> &x, const Vector::BaseType> &y, const Vector::BaseType> &sigma, const Vector *const mask=0); Vector::BaseType> fit(const Vector::BaseType> &x, const Vector::BaseType> &y, const Vector *const mask=0); Vector::BaseType> fit(const Matrix::BaseType> &x, const Vector::BaseType> &y, const Vector *const mask=0); Vector::BaseType> fit(const Vector *const mask=0); Bool fit(Vector::BaseType> &sol, const Vector::BaseType> &x, const Vector::BaseType> &y, const Vector::BaseType> &sigma, const Vector *const mask=0); Bool fit(Vector::BaseType> &sol, const Matrix::BaseType> &x, const Vector::BaseType> &y, const Vector::BaseType> &sigma, const Vector *const mask=0); Bool fit(Vector::BaseType> &sol, const Vector::BaseType> &x, const Vector::BaseType> &y, const typename FunctionTraits::BaseType &sigma, const Vector *const mask=0); Bool fit(Vector::BaseType> &sol, const Matrix::BaseType> &x, const Vector::BaseType> &y, const typename FunctionTraits::BaseType &sigma, const Vector *const mask=0); Bool fit(Vector::BaseType> &sol, const Vector *const mask=0); // // Obtain the chi squared. It has already been calculated during the // fitting process. // Double chiSquare() const { return getChi(); } // // Get the errors on the solved values // //
      • AipsError if none present (or Bool returned) // // const Vector::BaseType> &errors() const; Bool errors(Vector::BaseType> &err) const; // // Get covariance matrix // Matrix compuCovariance(); void compuCovariance(Matrix &cov); // // Generate the normal equations by one or more calls to the // buildNormalMatrix(), before calling a fit() without arguments. // The arguments are the same as for the fit(arguments) function. // A False is returned if the Array sizes are unmatched. // void buildNormalMatrix (const Vector::BaseType> &x, const Vector::BaseType> &y, const Vector::BaseType> &sigma, const Vector *const mask=0); void buildNormalMatrix (const Matrix::BaseType> &x, const Vector::BaseType> &y, const Vector::BaseType> &sigma, const Vector *const mask=0); void buildNormalMatrix (const Vector::BaseType> &x, const Vector::BaseType> &y, const Vector *const mask=0); void buildNormalMatrix (const Matrix::BaseType> &x, const Vector::BaseType> &y, const Vector *const mask=0); // // Return the residual after a fit in y. x can // be a vector (if 1D function) or a matrix (ND functional), as in the // fit() methods. If sol is given, it is the solution derived from // a fit and its value will be used; otherwise only the parameters // in the fitted functional will be used. // If model is given as True, the model, rather // the residual - will be returned in y. // False is returned if residuals cannot be calculated. // //
      • Aipserror if illegal array sizes // // Bool residual(Vector::BaseType> &y, const Array::BaseType> &x, const Vector::BaseType> &sol, const Bool model=False); Bool residual(Vector::BaseType> &y, const Array::BaseType> &x, const Bool model=False); // // Get the rank of the solution (or zero of no fit() done yet). A // valid solution will have the same rank as the number of unknowns (or // double that number in the complex case). For SVD solutions the // rank could be less. uInt getRank() const { return (solved_p ? nUnknowns()-getDeficiency() : 0); } protected: //#Data // Adjustable uInt aCount_ai; // SVD indicator Bool svd_p; // Function to use in evaluating condition equation Function::DiffType, typename FunctionTraits::DiffType> *ptr_derive_p; // List of functions describing the possible constraint equations // e.g. The sum of 3 angles w`could be described by a // HyperPlane(3) function with [1,1,1] // as parameters; giving [1,1,1] as argument vector and // 3.1415 as value. // PtrBlock::DiffType, typename FunctionTraits::DiffType>*> constrFun_p; // List of vectors describing the constraint equations' arguments PtrBlock::BaseType>*> constrArg_p; // List of values describing the constraint equations' value PtrBlock::BaseType *> constrVal_p; // // Number of available parameters uInt pCount_p; // Number of dimensions of input data uInt ndim_p; // No normal equations yet. Bool needInit_p; // Have solution Bool solved_p; // Have errors Bool errors_p; mutable Bool ferrors_p; // Interpret as weights rather than as sigma the given values. Bool asweight_p; // The rank of the solution uInt nr_p; // Condition equation parameters (for number of adjustable parameters) mutable Vector::BaseType> condEq_p; // Equation for all available parameters mutable Vector::BaseType> fullEq_p; // Contiguous argument areas // mutable Vector::ArgType> arg_p; mutable Vector::ArgType> carg_p; // // Local solution area // mutable Vector::BaseType> sol_p; mutable Vector::BaseType> fsol_p; // // Local error area // mutable Vector::BaseType> err_p; mutable Vector::BaseType> ferr_p; // // Local value and derivatives mutable typename FunctionTraits::DiffType valder_p; // Local SVD constraints mutable Vector:: BaseType>::base> > consvd_p; //# Member functions // Generalised fitter virtual Bool fitIt (Vector::BaseType> &sol, const Array::BaseType> &x, const Vector::BaseType> &y, const Vector::BaseType> *const sigma, const Vector *const mask=0) = 0; // Build the normal matrix void buildMatrix(const Array::BaseType> &x, const Vector::BaseType> &y, const Vector::BaseType> *const sigma, const Vector *const mask=0); // Build the constraint equations void buildConstraint(); // Get the SVD constraints void fillSVDConstraints(); // Calculate residuals Bool buildResidual(Vector::BaseType> &y, const Array::BaseType> &x, const Vector::BaseType> *const sol, const Bool model=False); // Function to get evaluated functional value typename FunctionTraits::BaseType getVal_p(const Array::BaseType> &x, uInt j, uInt i) const; // Initialise the fitter with number of solvable parameters void initfit_p(uInt parcnt); // Return number of condition equations and check sizes x, y, sigma // //
      • Aipserror if size inconsistencies // uInt testInput_p (const Array::BaseType> &x, const Vector::BaseType> &y, const Vector::BaseType> *const sigma); // Reset all the input void resetFunction(); private: //# Data //# Member functions // Set function properties void setFunctionEx(); // Set Constraint properties Bool setConstraintEx(const uInt n, const Vector::BaseType> &x, const typename FunctionTraits::BaseType y); }; } //# End namespace casacore #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Fitting/GenericL2Fit.tcc000066400000000000000000000517311476623553700212770ustar00rootroot00000000000000//# GenericL2Fit.cc: Generic base lass for least-squares fit. //# //# Copyright (C) 2001,2002,2003,2004,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_GENERICL2FIT_TCC #define SCIMATH_GENERICL2FIT_TCC //# Includes #include #include #include #include namespace casacore { //# Begin namespace casa //# Constants // Default svd collinearity template GenericL2Fit::GenericL2Fit() : LSQaips(), COLLINEARITY(1e-8), aCount_ai(0), svd_p(False), ptr_derive_p(0), constrFun_p(), constrArg_p(), constrVal_p(), pCount_p(0), ndim_p(0), needInit_p(True), solved_p(False), errors_p(False), ferrors_p(False), asweight_p(False), nr_p(0), condEq_p(0), fullEq_p(0), arg_p(0), sol_p(0), fsol_p(0), err_p(0), ferr_p(0), valder_p(typename FunctionTraits::DiffType(0)), consvd_p(0) { if (!svd_p) set(0.0); } template GenericL2Fit::GenericL2Fit(const GenericL2Fit &other) : LSQaips(other), COLLINEARITY(1e-8), aCount_ai(other.aCount_ai), svd_p(other.svd_p), ptr_derive_p(0), constrFun_p(other.constrFun_p.nelements()), constrArg_p(other.constrArg_p.nelements()), constrVal_p(other.constrVal_p.nelements()), pCount_p(other.pCount_p), ndim_p(other.ndim_p), needInit_p(other.needInit_p), solved_p(other.solved_p), errors_p(other.errors_p), ferrors_p(other.ferrors_p), asweight_p(other.asweight_p) , nr_p(other.nr_p), condEq_p(0), fullEq_p(0), arg_p(0), sol_p(0), fsol_p(0), err_p(0), ferr_p(0), valder_p(typename FunctionTraits::DiffType(0)), consvd_p(0) { if (other.ptr_derive_p) ptr_derive_p = other.ptr_derive_p->clone(); for (uInt i=0; iclone(); for (uInt i=0; i::BaseType> (other.constrArg_p[i]->copy()); for (uInt i=0; i::BaseType(*(other.constrVal_p[i])); condEq_p = other.condEq_p; fullEq_p = other.fullEq_p; arg_p = other.arg_p; sol_p = other.sol_p; fsol_p = other.fsol_p; err_p = other.err_p; ferr_p = other.ferr_p; valder_p = other.valder_p; consvd_p = other.consvd_p; } template GenericL2Fit &GenericL2Fit::operator=(const GenericL2Fit &other) { if (this != &other) { LSQaips::operator=(other); aCount_ai = other.aCount_ai; svd_p = other.svd_p; if (other.ptr_derive_p) ptr_derive_p = other.ptr_derive_p->clone(); else ptr_derive_p = 0; constrFun_p.resize(other.constrFun_p.nelements()); for (uInt i=0; iclone(); constrArg_p.resize(other.constrArg_p.nelements()); for (uInt i=0; i::BaseType> (other.constrArg_p[i]->copy()); constrVal_p.resize(other.constrVal_p.nelements()); for (uInt i=0; i::BaseType(*(other.constrVal_p[i])); pCount_p = other.pCount_p; ndim_p = other.ndim_p; needInit_p = other.needInit_p; solved_p = other.solved_p; errors_p = other.errors_p; ferrors_p = other.ferrors_p; asweight_p = other.asweight_p; nr_p = other.nr_p; condEq_p = other.condEq_p; fullEq_p = other.fullEq_p; arg_p = other.arg_p; sol_p = other.sol_p; fsol_p = other.fsol_p; err_p = other.err_p; ferr_p = other.ferr_p; valder_p = other.valder_p; consvd_p = other.consvd_p; } return *this; } template GenericL2Fit::~GenericL2Fit() { resetFunction(); } template void GenericL2Fit:: setFunctionEx() { pCount_p = ptr_derive_p->nparameters(); aCount_ai = ptr_derive_p->parameters().nMaskedParameters(); ndim_p = ptr_derive_p->ndim(); initfit_p(aCount_ai); } template Bool GenericL2Fit:: setConstraintEx(const uInt n, const Vector::BaseType> &x, const typename FunctionTraits::BaseType y) { delete constrArg_p[n]; constrArg_p[n] = 0; constrArg_p[n] = new Vector::BaseType> (x.copy()); delete constrVal_p[n]; constrVal_p[n] = 0; constrVal_p[n] = new typename FunctionTraits::BaseType(y); for (uInt i=0; i::DiffType ((*constrFun_p[n])[i].value(), pCount_p, i); } return True; } template Bool GenericL2Fit:: setConstraint(const uInt n, const Vector::BaseType> &x, const typename FunctionTraits::BaseType y) { if (!ptr_derive_p) return False; HyperPlane::DiffType> function(ptr_derive_p->nparameters()); return setConstraint(n, function, x, y); } template Bool GenericL2Fit:: setConstraint(const uInt n, const typename FunctionTraits::BaseType y) { if (!ptr_derive_p) return False; HyperPlane::DiffType> function(ptr_derive_p->nparameters()); Vector::BaseType> x(function.ndim()); return setConstraint(n, function, x, y); } template Bool GenericL2Fit:: addConstraint(const Function::DiffType> &function, const Vector::BaseType> &x, const typename FunctionTraits::BaseType y) { uInt n = constrFun_p.nelements(); constrFun_p.resize(n+1); constrFun_p[n] = 0; constrArg_p.resize(n+1); constrArg_p[n] = 0; constrVal_p.resize(n+1); constrVal_p[n] = 0; return setConstraint(n, function, x, y); } template Bool GenericL2Fit:: addConstraint(const Vector::BaseType> &x, const typename FunctionTraits::BaseType y) { if (!ptr_derive_p) return False; HyperPlane::DiffType> function(ptr_derive_p->nparameters()); return addConstraint(function, x, y); } template Bool GenericL2Fit:: addConstraint(const typename FunctionTraits::BaseType y) { if (!ptr_derive_p) return False; HyperPlane::DiffType> function(ptr_derive_p->nparameters()); Vector::BaseType> x(function.ndim()); return addConstraint(function, x, y); } template void GenericL2Fit::asSVD(const Bool svd) { svd_p = svd; if (!svd_p) set(0.0); else set(COLLINEARITY); } template void GenericL2Fit:: setParameterValues(const Vector::BaseType> &parms) { for (uInt i=0; i void GenericL2Fit::setMaskedParameterValues (const Vector::BaseType> &parms) { for (uInt i=0, k=0; imask(i)) (*ptr_derive_p)[i].value() = parms[k++]; } } template Vector:: BaseType>::base> GenericL2Fit::getSVDConstraint(uInt n) { Vector:: BaseType>::base> tmp(pCount_p, 0.0); if (n >= consvd_p.nelements()) { throw(AipsError("GenericL2Fit::getSVDConstraint(n)" " -- Illegal constraint number")); } for (uInt i=0, k=0; imask(i)) tmp[i] = consvd_p[n][k++]; } return tmp; } template Vector::BaseType> GenericL2Fit:: fit(const Vector::BaseType> &x, const Vector::BaseType> &y, const Vector::BaseType> &sigma, const Vector *const mask) { fitIt(fsol_p, x, y, &sigma, mask); return fsol_p; } template Vector::BaseType> GenericL2Fit:: fit(const Matrix::BaseType> &x, const Vector::BaseType> &y, const Vector::BaseType> &sigma, const Vector *const mask) { fitIt(fsol_p, x, y, &sigma, mask); return fsol_p; } template Vector::BaseType> GenericL2Fit:: fit(const Vector::BaseType> &x, const Vector::BaseType> &y, const Vector *const mask) { fitIt(fsol_p, x, y, static_cast::BaseType> *const>(0), mask); return fsol_p; } template Vector::BaseType> GenericL2Fit:: fit(const Matrix::BaseType> &x, const Vector::BaseType> &y, const Vector *const mask) { fitIt(fsol_p, x, y, static_cast::BaseType> *const>(0), mask); return fsol_p; } template Vector::BaseType> GenericL2Fit:: fit(const Vector *const mask) { fit(fsol_p, mask); return fsol_p; } template Bool GenericL2Fit:: fit(Vector::BaseType> &sol, const Vector::BaseType> &x, const Vector::BaseType> &y, const Vector::BaseType> &sigma, const Vector *const mask) { return fitIt(sol, x, y, &sigma, mask); } template Bool GenericL2Fit:: fit(Vector::BaseType> &sol, const Matrix::BaseType> &x, const Vector::BaseType> &y, const Vector::BaseType> &sigma, const Vector *const mask) { return fitIt(sol, x, y, &sigma, mask); } template Bool GenericL2Fit:: fit(Vector::BaseType> &sol, const Vector::BaseType> &x, const Vector::BaseType> &y, const typename FunctionTraits::BaseType &, const Vector *const mask) { return fitIt(sol, x, y, static_cast::BaseType> *const>(0), mask); } template Bool GenericL2Fit:: fit(Vector::BaseType> &sol, const Matrix::BaseType> &x, const Vector::BaseType> &y, const typename FunctionTraits::BaseType &, const Vector *const mask) { return fitIt(sol, x, y, static_cast::BaseType> *const>(0), mask); } template Bool GenericL2Fit:: fit(Vector::BaseType> &, const Vector *const) { throw(AipsError("GenericL2: A001: not implemented yet; ask Wim Brouw")); return False; } template const Vector::BaseType> &GenericL2Fit:: errors() const { if (!errors_p) throw(AipsError("GenericL2Fit: no solution to get errors")); if (!ferrors_p) { ferrors_p = True; ferr_p.resize(pCount_p); ferr_p = 0; for (uInt i=0, k=0; imask(i)) ferr_p[i] = err_p[k++]; } } return ferr_p; } template Bool GenericL2Fit:: errors(Vector::BaseType> &err) const { if (errors_p) { if (!ferrors_p) { ferrors_p = True; ferr_p.resize(pCount_p); ferr_p = 0; for (uInt i=0, k=0; imask(i)) ferr_p[i] = err_p[k++]; } } err.resize(ferr_p.nelements()); err = ferr_p; } return errors_p; } template Matrix GenericL2Fit::compuCovariance() { Matrix tmp; compuCovariance(tmp); return tmp; } template void GenericL2Fit::compuCovariance(Matrix &cov) { Double *tmp = new Double[nUnknowns()*nUnknowns()]; getCovariance(tmp); IPosition iw(2, pCount_p, pCount_p); if (!(cov.shape().conform(iw) && cov.shape() == iw)) { cov.resize(); cov.resize(iw); } for (uInt i=0, l=0; imask(i)) { for (uInt j=0, k=0; jmask(j)) cov(j, i) = tmp[nUnknowns()*k++ + l]; else cov(j, i) = 0; } l++; } else for (uInt j=0; j void GenericL2Fit:: buildNormalMatrix(const Vector::BaseType> &x, const Vector::BaseType> &y, const Vector::BaseType> &sigma, const Vector *const mask) { buildMatrix(x, y, &sigma, mask); } template void GenericL2Fit:: buildNormalMatrix(const Matrix::BaseType> &x, const Vector::BaseType> &y, const Vector::BaseType> &sigma, const Vector *const mask) { buildMatrix(x, y, &sigma, mask); } template void GenericL2Fit:: buildNormalMatrix(const Vector::BaseType> &x, const Vector::BaseType> &y, const Vector *const mask) { buildMatrix(x, y, static_cast::BaseType> *const>(0), mask); } template void GenericL2Fit:: buildNormalMatrix(const Matrix::BaseType> &x, const Vector::BaseType> &y, const Vector *const mask) { buildMatrix(x, y, static_cast::BaseType> *const>(0), mask); } template Bool GenericL2Fit:: residual(Vector::BaseType> &y, const Array::BaseType> &x, const Vector::BaseType> &sol, const Bool model) { return buildResidual(y, x, &sol, model); } template Bool GenericL2Fit:: residual(Vector::BaseType> &y, const Array::BaseType> &x, const Bool model) { return buildResidual(y, x, static_cast::BaseType> *const>(0), model); } template void GenericL2Fit::initfit_p(uInt parcnt) { if (needInit_p) { needInit_p = False; solved_p = False; errors_p = False; ferrors_p = False; set(parcnt, typename LSQTraits::BaseType>::num_type()); condEq_p.resize(aCount_ai); fullEq_p.resize(pCount_p); arg_p.resize(ndim_p); sol_p.resize(aCount_ai); fsol_p.resize(pCount_p); err_p.resize(aCount_ai); ferr_p.resize(pCount_p); valder_p = typename FunctionTraits::DiffType(0, pCount_p); if (ptr_derive_p) { for (uInt i=0; i::DiffType ((*ptr_derive_p)[i].value(), pCount_p, i); } } consvd_p.resize(0); } } template uInt GenericL2Fit:: testInput_p(const Array::BaseType> &x, const Vector::BaseType> &y, const Vector::BaseType> *const sigma) { uInt xRows = (x.ndim() == 1 || x.ndim() == 2) ? x.shape()(0) : 0; if (xRows*ndim_p != y.nelements()*ndim_p || (sigma && xRows != sigma->nelements())) { throw(AipsError("GenericL2Fit::buildNormalMatrix()" " -- Illegal argument Array sizes")); } xRows = y.nelements(); initfit_p(aCount_ai); return xRows; } template void GenericL2Fit::resetFunction() { delete ptr_derive_p; ptr_derive_p = 0; pCount_p = 0; ndim_p = 0; aCount_ai = 0; needInit_p = True; solved_p = False; errors_p = False; ferrors_p = False; for (uInt i=0; i typename FunctionTraits::BaseType GenericL2Fit:: getVal_p(const Array::BaseType> &x, uInt, uInt i) const { if (ptr_derive_p) { if (x.ndim() == 1) { valder_p = (*ptr_derive_p)(static_cast::BaseType> &>(x)[i]); } else { const Matrix::BaseType> &xt = static_cast::BaseType> &>(x); for (uInt k=0; k void GenericL2Fit:: buildMatrix(const Array::BaseType> &x, const Vector::BaseType> &y, const Vector::BaseType> *const sigma, const Vector *const mask) { if (!needInit_p) needInit_p = solved_p; uInt nrows = testInput_p(x, y, sigma); typename FunctionTraits::BaseType b(0.0); typename FunctionTraits::BaseType sig(1.0); VectorSTLIterator::BaseType> ceqit(condEq_p); ptr_derive_p->lockParam(); // Parameters will not change during loop for (uInt i=0; i::BaseType(0) || (*sigma)[i] == typename FunctionTraits::BaseType(-1)) continue; sig = (*sigma)[i]; if (!asweight_p) { sig = abs(typename FunctionTraits::BaseType(1.0)/sig); sig *= sig; } } if (ptr_derive_p) { b = y(i) - getVal_p(x, 0, i); for (uInt j=0, k=0; jmask(j)) condEq_p[k++] = fullEq_p[j]; } } makeNorm(ceqit, abs(sig), b); } ptr_derive_p->unlockParam(); } template void GenericL2Fit::buildConstraint() { VectorSTLIterator::BaseType> ceqit(condEq_p); for (uInt i=0; i::BaseType b(*constrVal_p[i]); // known value // Get arguments carg_p.resize(constrArg_p[i]->nelements()); for (uInt k=0; knelements(); ++k) carg_p[k] = (*constrArg_p[i])[k]; // calculate constraint equations valder_p = (*constrFun_p[i])(carg_p); valder_p.derivatives(fullEq_p); b -= valder_p.value(); for (uInt j=0, k=0; jmask(j)) condEq_p[k++] = fullEq_p[j]; } if (i void GenericL2Fit::fillSVDConstraints() { uInt n=LSQFit::getDeficiency(); consvd_p.resize(n); for (uInt i=0; i:: BaseType>::base> conit(consvd_p[i]); LSQFit::getConstraint(i, conit); } } template Bool GenericL2Fit:: buildResidual(Vector::BaseType> &y, const Array::BaseType> &x, const Vector::BaseType> *const sol, const Bool model) { uInt nrows = testInput_p(x, y, static_cast::BaseType> *const>(0)); if (sol && sol->nelements() != pCount_p) return False; for (uInt i=0; i::BaseType(0); y[i] -= getVal_p(x, 0, i); if (sol) { for (uInt j=0; jmask(j)) y[i] -= sol->operator()(j) * fullEq_p[j]; } } } if (model) y[i] = -y[i]; } return True; } } //#End namesapce casa #endif casacore-3.7.1/scimath/Fitting/LSQFit.cc000066400000000000000000000603701476623553700177770ustar00rootroot00000000000000//# LSQFit.cc: Basic class for least squares fitting //# Copyright (C) 1999,2000,2002,2004-2006,2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Static values LSQFit::Real LSQFit::REAL = LSQFit::Real(); LSQFit::Complex LSQFit::COMPLEX = LSQFit::Complex(); LSQFit::Separable LSQFit::SEPARABLE = LSQFit::Separable(); LSQFit::AsReal LSQFit::ASREAL = LSQFit::AsReal(); LSQFit::Conjugate LSQFit::CONJUGATE = LSQFit::Conjugate(); //# Constructors LSQFit::LSQFit(uInt nUnknowns, uInt nConstraints) : state_p(0), nun_p(nUnknowns), ncon_p(nConstraints), n_p(0), r_p(0), prec_p(1e-12), startnon_p(1e-3), nonlin_p(1), stepfactor_p(10), epsval_p(1e-6), epsder_p(1e-6), balanced_p(False), maxiter_p(0), niter_p(0), ready_p(NONREADY), piv_p(0), norm_p(0), nnc_p(0), nceq_p(0), known_p(0), error_p(0), constr_p(0), sol_p(0), nar_p(0), lar_p(0), wsol_p(0), wcov_p(0) { init(); clear(); } LSQFit::LSQFit(uInt nUnknowns, const LSQReal &, uInt nConstraints) : state_p(0), nun_p(nUnknowns), ncon_p(nConstraints), n_p(0), r_p(0), prec_p(1e-12), startnon_p(1e-3), nonlin_p(1), stepfactor_p(10), epsval_p(1e-6), epsder_p(1e-6), balanced_p(False), maxiter_p(0), niter_p(0), ready_p(NONREADY), piv_p(0), norm_p(0), nnc_p(0), nceq_p(0), known_p(0), error_p(0), constr_p(0), sol_p(0), nar_p(0), lar_p(0), wsol_p(0), wcov_p(0) { init(); clear(); } LSQFit::LSQFit(uInt nUnknowns, const LSQComplex &, uInt nConstraints) : state_p(0), nun_p(2*nUnknowns), ncon_p(2*nConstraints), n_p(0), r_p(0), prec_p(1e-12), startnon_p(1e-3), nonlin_p(1), epsval_p(1e-8), epsder_p(1e-8), balanced_p(False), maxiter_p(0), niter_p(0), ready_p(NONREADY), piv_p(0), norm_p(0), nnc_p(0), nceq_p(0), known_p(0), error_p(0), constr_p(0), sol_p(0), nar_p(0), lar_p(0), wsol_p(0), wcov_p(0) { init(); clear(); } LSQFit::LSQFit() : state_p(0), nun_p(0), ncon_p(0), n_p(0), r_p(0), prec_p(1e-12), startnon_p(1e-3), nonlin_p(1), stepfactor_p(10), epsval_p(1e-8), epsder_p(1e-8), balanced_p(False), maxiter_p(0), niter_p(0), ready_p(NONREADY), piv_p(0), norm_p(0), nnc_p(0), nceq_p(0), known_p(0), error_p(0), constr_p(0), sol_p(0), nar_p(0), lar_p(0), wsol_p(0), wcov_p(0) {} LSQFit::LSQFit(const LSQFit &other) : state_p(other.state_p), nun_p(other.nun_p), ncon_p(other.ncon_p), n_p(other.n_p), r_p(other.r_p), prec_p(other.prec_p), startnon_p(other.startnon_p), nonlin_p(other.nonlin_p), stepfactor_p(other.stepfactor_p), epsval_p(other.epsval_p), epsder_p(other.epsder_p), balanced_p(other.balanced_p), maxiter_p(other.maxiter_p), niter_p(other.niter_p), ready_p(other.ready_p), piv_p(0), norm_p(0), nnc_p(other.nnc_p), nceq_p(0), known_p(0), error_p(0), constr_p(0), sol_p(0), nar_p(0), lar_p(0), wsol_p(0), wcov_p(0) { init(); copy(other); } LSQFit &LSQFit::operator=(const LSQFit &other) { if (this != &other) { deinit(); state_p = other.state_p; nun_p = other.nun_p; ncon_p = other.ncon_p; n_p = other.n_p; r_p = other.r_p; prec_p = other.prec_p; startnon_p = other.startnon_p; nonlin_p = other.nonlin_p; stepfactor_p = other.stepfactor_p; epsval_p = other.epsval_p; epsder_p = other.epsder_p; balanced_p = other.balanced_p; maxiter_p = other.maxiter_p; niter_p = other.niter_p; ready_p = other.ready_p; nnc_p = other.nnc_p; init(); copy(other); } return *this; } //# Destructor LSQFit::~LSQFit() { deinit(); } void LSQFit::init() { n_p = nun_p + ncon_p; r_p = n_p; if (nun_p) { norm_p = new LSQMatrix(nun_p); if (ncon_p) constr_p = new Double[nun_p*ncon_p]; } if (n_p) known_p = new Double[n_p]; error_p = new Double[N_ErrorField]; } void LSQFit::clear() { if (piv_p) for (uInt *i=piv_p; i!=piv_p+n_p; ++i) *i = i-piv_p; if (norm_p) norm_p->clear(); if (known_p) std::fill_n(known_p, n_p, 0.0); if (error_p) std::fill_n(error_p, uInt(N_ErrorField), 0.0); if (ncon_p) std::fill_n(constr_p, ncon_p*nun_p, 0.0); state_p = 0; } void LSQFit::deinit() { delete [] piv_p; piv_p=0; delete norm_p; norm_p=0; delete [] known_p; known_p=0; delete [] error_p; error_p=0; delete [] sol_p; sol_p=0; delete [] constr_p; constr_p=0; delete nceq_p; nceq_p=0; delete nar_p; nar_p=0; delete [] lar_p; lar_p=0; delete [] wsol_p; wsol_p=0; delete [] wcov_p; wcov_p=0; } void LSQFit::copy(const LSQFit &other, Bool all) { if (!nun_p) return; if (other.known_p && !known_p) known_p = new Double[n_p]; if (other.error_p && !error_p) error_p = new Double[N_ErrorField]; if (other.constr_p && !constr_p) constr_p= new Double[ncon_p*nun_p]; if (other.nceq_p && !nceq_p) nceq_p = new LSQMatrix(nnc_p); if (other.norm_p && !norm_p) norm_p = new LSQMatrix(nun_p); if (all) { if (other.piv_p && !piv_p) piv_p = new uInt[nnc_p]; if (other.sol_p && !sol_p) sol_p = new Double[nnc_p]; if (other.lar_p && !lar_p) lar_p = new Double[n_p*n_p]; } if (other.norm_p) norm_p->copy(*(other.norm_p)); if (other.known_p) std::copy(other.known_p, other.known_p+n_p, known_p); if (other.error_p) std::copy(other.error_p, other.error_p+N_ErrorField, error_p); if (other.constr_p) std::copy(other.constr_p, other.constr_p+ncon_p*nun_p, constr_p); if (other.nceq_p) nceq_p->copy(*(other.nceq_p)); if (all) { if (other.piv_p) std::copy(other.piv_p, other.piv_p+nnc_p, piv_p); if (other.sol_p) std::copy(other.sol_p, other.sol_p+nnc_p, sol_p); if (other.lar_p) std::copy(other.lar_p, other.lar_p+n_p*n_p, lar_p); } } //# Member functions Bool LSQFit::invert(uInt &nRank, Bool doSVD) { // Already done if ((n_p != nun_p) && (state_p & INVERTED)) return True; // Copy the data for solution equations createNCEQ(); Double d0(0); //collinearity test // Assume non-linear state_p &= ~NONLIN; // Make diagonal != 0 nceq_p->doDiagonal(nun_p); // Special if constraints if (nnc_p != nun_p) { if (!invertRect()) return False; } else { // decompose for (uInt i=0; irow(i); //row pointer while (True) { d0 = i3[i]; //get collinearity for (uInt i2=0; i2row(i2); //row pointer d0 -= i4[i]*i4[i]/i4[i2]; } if (d0*d0/i3[i] <= prec_p) { //dependancy if (!doSVD) return False; //should be ok if (irow(i2); //row pointer std::swap(i4[i], i4[j0]); } std::swap(i3[i], nceq_p->row(j0)[j0]); for (uInt i2=i+1; i2row(i2)[j0]); } Double *i4 = nceq_p->row(j0); //row pointer for (uInt i2=j0+1; i2row(i2); //row pointer i3[i1] -= i4[i]*i4[i1]/i4[i2]; } } } } // constraints for (uInt i1=r_p; i1=0; i--) { Double *i3 = nceq_p->row(i); //row pointer for (uInt i2=i+1; i2row(i2)[i1]; } i3[i1] /= -i3[i]; } } // rank basis (a=i+g1'*.g1') for (uInt i=r_p; irow(i); //row pointer for (uInt i1=i; i1row(i2); //row pointer i3[i1] += i4[i]*i4[i1]; } } i3[i] += 1.0; } // triangular a for (uInt i=r_p; irow(i); //row pointer for (uInt i1=i; i1row(i2); //row pointer i3[i1] -= i4[i]*i4[i1]/i4[i2]; } } } } // nRank = r_p; //rank // ready return True; } void LSQFit::solveIt() { getWorkSOL(); if (state_p & INVERTED) { //constraints inverted for (uInt i1=0; i1row(i1); sol_p[i1] = 0; for (uInt i2=0; i2row(i2)[i1]*known_p[i2]; } for (uInt i2=i1; i2row(i2); //row pointer sol_p[i1] -= i3[i1]*sol_p[i2]/i3[i2]; //step 1 } } for (uInt i1=r_p-1; (Int)i1>=0; i1--) { Double *i3 = nceq_p->row(i1); //row pointer for (uInt i2=i1+1; i2maxDiagonal(nun_p); // start factor stepfactor_p = 2; niter_p = maxiter_p; fit = 1.0; // loop more ready_p = LSQFit::NONREADY; if (normInfKnown(known_p) <= epsder_p) ready_p = DERIVLEVEL; // known small createNCEQ();;; save(False); // save current information state_p |= NONLIN; // non-first loop } else { Double d0((error_p[SUMLL] + nar_p->error_p[SUMLL])/2.0); // Get fitting goodness (interim) if (d0>0) fit = (error_p[SUMLL] - nar_p->error_p[SUMLL])/d0; else fit = -1e-10; // dummy // Get expected improvement d0 = 0; if (balanced_p) for (uInt i=0; isol_p[i]*(nonlin_p*nar_p->sol_p[i]+known_p[i]); else for (uInt i=0; isol_p[i]*(nonlin_p*nar_p->sol_p[i]*(*norm_p->diag(i))+known_p[i]); d0 *= 0.5; Double f = 0.5*(nar_p->error_p[SUMLL] - error_p[SUMLL]); if (d0>0 && f>0) { if (balanced_p) { Double t0(2.0*f/d0-1.0), t1(1.0/3.0); t0 *= -t0*t0; t0 += 1.0; nonlin_p *= (t0>t1 ? t0 : t1); // new factor } else nonlin_p *= 0.3; stepfactor_p = 2; save(False); if (normInfKnown(known_p) <= epsder_p) ready_p = DERIVLEVEL; // known } else { nonlin_p *= stepfactor_p; stepfactor_p *= 2; if (stepfactor_p > 1e10) ready_p = NOREDUCTION; /// make it a constant for (Double *i=wsol_p, *i1=nar_p->sol_p; i!=wsol_p+nun_p; ++i,++i1) *i-=*i1; // new solution restore(False); // restore info } } if (!ready_p && (maxiter_p==0 || niter_p>0)) { if (maxiter_p>0) --niter_p; if (balanced_p) norm_p->addDiagonal(nun_p, nonlin_p); // apply factor else norm_p->mulDiagonal(nun_p, nonlin_p); if (!invert(nRank, doSVD)) { ready_p = SINGULAR; return False; // decompose } std::copy(wsol_p, wsol_p+nun_p, nar_p->sol_p);// save current solution solveIt(); // solve if (normSolution(wsol_p) <= epsval_p*(normSolution(nar_p->sol_p)+epsval_p)) ready_p = SOLINCREMENT; std::swap_ranges(wsol_p, wsol_p+nun_p, nar_p->sol_p); // restore sol for (Double *i=wsol_p, *i1=nar_p->sol_p; i!=wsol_p+nun_p; ++i,++i1) *i+=*i1; nar_p->error_p[CHI2] = error_p[CHI2];;; nar_p->error_p[NC] = error_p[NC];;; nar_p->error_p[SUMWEIGHT] = error_p[SUMWEIGHT];;; clear(); // clear for next part state_p |= NONLIN; // set in non-linear loop } else if (!ready_p) ready_p = MAXITER; if (ready_p) fit = -1e-10; // force fit (old system) else fit = 1.0; return True; } void LSQFit::solveMR(uInt nin) { // missing rank for (uInt i1=r_p; i1row(i2)[i1]; } } // sol_pe x2 for (uInt i1=r_p; i1row(i2); //row pointer sol_p[i1] -= i3[i1]*sol_p[i2]/i3[i2]; //step 1 } } for (uInt i1=nin-1; (Int)i1>=(Int)r_p; i1--) { Double *i3 = nceq_p->row(i1); //row pointer for (uInt i2=i1+1; i2row(i1); //row pointer for (uInt i2=r_p; i2row(i); //input row Double *j1 = rowrt(i); //output row j1[i] = j0[i]; //diagonal for (uInt i1=i+1; i1d0) d0 = std::abs(j1[i]); } if (d0 == 0) return False; //cannot solve sol_p[i] = 1./d0; //save scaling } // do crout for (uInt i1=0; i1= d0) { //find best pivot i4 = i; d0 = sol_p[i]*std::abs(j0[i]); } } if (i1 != i4) { //interchange rows for (uInt i2=0; i2=0; i--) { //backward Double *j0 = rowrt(i); for (uInt i1=i+1; i1row(i3); //row result for (uInt i=i3; irow(i2); //row pointer sol_p[i1] -= i3[i1]*sol_p[i2]/i3[i2]; //step 1 } } for (uInt i1=r_p-1; (Int)i1>=0; i1--) { Double *i3 = nceq_p->row(i1); //row pointer for (uInt i2=i1+1; i2row(i); //output row Double *j1 = rowru(i); //input row for (uInt i1=i; i1row(0); Double *i3 = other.norm_p->row(0); for (uInt i=0; irow(i); for (uInt i1=i; i1row(nEqIndex[i])[nEqIndex[i1]] += i3[i1]; } else norm_p->row(nEqIndex[i1])[nEqIndex[i]] += i3[i1]; } } } } // Copy known terms Double *i2 = known_p; Double *i3 = other.known_p; for (uInt i=0; i(nEqIndex), other.constr_p + i*other.nun_p, other.known_p[nun_p+i]); } return True; } void LSQFit::reset() { clear(); } void LSQFit::extendConstraints(uInt n) { if ((constr_p && ncon_p == n) || nun_p==0) return; // Already right size if (n==0) { delete [] constr_p; constr_p = 0; } else { Double *newcon = new Double[n*nun_p]; // Newly sized area Double *newknw = new Double[n+nun_p]; Double *cptr = newcon; // Prepare copying Double *vptr = newknw; Double *inc = constr_p; Double *inv = known_p; for (uInt j=0; jtrian_p; Double *se = nceq_p->trian_p; for (uInt i=0; icopy(*this, all); } } void LSQFit::restore(Bool all) { if (nar_p) copy(*nar_p, all); } Double LSQFit::getChi() const { Double *erv(error_p); if ((state_p & NONLIN) && nar_p) erv = nar_p->error_p; Double x = erv[CHI2]; return x*x*(erv[NC] - nun_p); } Double LSQFit::getSD() const { Double *erv(error_p); if ((state_p & NONLIN) && nar_p) erv = nar_p->error_p; return erv[CHI2]; } Double LSQFit::getWeightedSD() const { Double *erv(error_p); if ((state_p & NONLIN) && nar_p) erv = nar_p->error_p; Double x = erv[NC]; if (erv[SUMWEIGHT] > 0.0) x /= erv[SUMWEIGHT]; return erv[CHI2] * sqrt(std::max(0.0, x)); } Double LSQFit::normSolution(const Double *sol) const { Double ret(0); for (const Double *i=sol; i!=sol+nun_p; ++i) ret += *i * *i; return sqrt(ret); } Double LSQFit::normInfKnown(const Double *known) const { Double tmp(0), ret(0); for (const Double *i=known; i!=known+nun_p; ++i) if (ret < (tmp=std::abs(*i))) ret=tmp; return ret; } void LSQFit::debugIt(uInt &nun, uInt &np, uInt &ncon, uInt &ner, uInt &rank, Double *&nEq, Double *&known, Double *&constr, Double *&er, uInt *&piv, Double *&sEq, Double *&sol, Double &prec, Double &nonlin) const { nun = nun_p; np = n_p; ncon = ncon_p; ner = N_ErrorField; rank = r_p; nEq = (norm_p ? norm_p->trian_p : 0); known = known_p; constr = constr_p; er = error_p; piv = piv_p; sEq = (nceq_p ? nceq_p->trian_p : 0); sol = wsol_p; prec = sqrt(prec_p); nonlin = nonlin_p; } void LSQFit::getWorkSOL() { if (!wsol_p) wsol_p = new Double[n_p]; } void LSQFit::getWorkCOV() { if (!wcov_p) wcov_p = new Double[n_p*n_p]; } } //# NAMESPACE CASACORE - END casacore-3.7.1/scimath/Fitting/LSQFit.h000066400000000000000000001127421476623553700176420ustar00rootroot00000000000000//# LSQFit.h: Basic class for least squares fitting //# Copyright (C) 1999-2001,2004-2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_LSQFIT_H #define SCIMATH_LSQFIT_H //# Includes #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // Basic class for the least squares fitting // // // //
      • Some knowledge of Matrix operations //
      • The background information provided in // Note 224. //
      • Fitting module // // // // From Least SQuares and Fitting // // // // The LSQFit class contains the basic functions to do all the fitting // described in the // Note // about fitting. // It handles real, and complex equations;
        // linear and non-linear (Levenberg-Marquardt) solutions;
        // regular (with optional constraints) or Singular Value Decomposition // (SVD).
        // In essence they are a set of routines to generate normal equations // (makeNorm()) in triangular form from a set of condition // equations;
        // to do a Cholesky-type decomposition of the normal // equations (either regular or SVD) and test its rank // (invert());
        // to do a quasi inversion of the decomposed equations (solve()) to // obtain the solution and/or the errors. // // All calculations are done in place. // Methods to obtain additional information about the fitting process are // available. // // This class can be used as a stand-alone class outside of the Casacore // environment. In that case the aips.h include file // can be replaced if necessary by appropriate typedefs for Double, Float and // uInt.
        // The interface to the methods have standard data or standard STL iterator // arguments only. They can be used with any container having an STL // random-access iterator interface. Especially they can be used with // carrays (necessary templates provided), // Casacore Vectors (necessary templates // provided in LSQaips), // standard random access STL containers (like std::vector). // // The normal operation of the class consists of the following steps: //
          //
        • Create an LSQFit object. // The information that can be provided in the constructor of the object, // either directly, or indirectly using the set() commands, is // (see Note 224): //
            //
          • The number of unknowns that have to be solved for (mandatory) //
          • The number of constraint equations you want to use explicitly // (defaults to 0, but can be changed on-the-fly) //
          // Separately settable are: //
            //
          • A collinearity test factor (defaults to 1e-8) // // The collinearity factor is the square of the sine of the angle between // a column in the normal equations, and the hyper-plane through // all the other columns. In special cases (e.g. fitting a polynomial // in a very narrow bandwidth window, it could be advisable to set this // factor to zero if you want a solution (whatever the truth of it maybe). // //
          • A Levenberg-Marquardt adjustment factor (if appropriate, // defaults to 1e-3) //
          // //
        • Create the normal equations used in solving the set of condition // equations of the user, by using the makeNorm() methods. // Separate makenorm() methods are provided for sparse condition // equations (e.g. if data for 3 antennas are provided, rather than for all 64) // //
        • If there are user provided constraints, either limiting constraints like // the sum of the angles in a triangle is 180 degrees, or constraints to add // missing information if e.g. only differences between parameters have been // measured, they can be added to the normal // equations with the setConstraint() or // the addConstraint() methods. Lagrange multipliers will be used to // solve the extended normal equations. // //
        • The normal equations are triangu;arised (using the collinearity factor // as a check for solvability) with the invert() method. If the // normal equations are non-solvable an error is returned, or a switch to // an SVD solution is made if indicated in the invert call. // //
        • The solutions and adjustment errors are obtained with the // solve() method. // A non-linear loop in a Levenberg-Marquardt adjustment can be obtained // (together with convergence information), with the solveLoop() // method (see below) replacing the combination of // invert and solve. // //
        • Non-linear loops are done by looping through the data using // makeNorm() calls, and upgrade the solution with the // solveLoop() method. // The normal equations are upgraded by changing LM factors. Upgrade depends // on the 'balanced' factor. The LM factor is either added in some way to all // diagonal elements (if balanced) or all diagonal elements are multiplied by // (1+factor) After each loop convergence can be tested // by the isReady() call; which will return False or // a non-zero code indicating the ready reason. Reasons for stopping can be: //
            //
          • SOLINCREMENT: the relative change in the norm of the parameter // solutions is less than // (a settable, setEpsValue(), default 1e-8) value. //
          • DERIVLEVEL: the inf-norm of the known vector of the equations to be // solved is less than the settable, setEpsDerivative(), default // 1e-8, value. //
          • MAXITER: maximum number of iterations reached (only possible if a // number is explicitly set) //
          • NOREDUCTION: if the Levenberg-Marquardt correction factor goes towards // infinity. I.e. if no Chi2 improvement seems possible. Have to redo the // solution with a different start condition for the unknowns. //
          • SINGULAR: can only happen due to numeric rounding, since the LM // equations are always positive-definite. Best solution is to indicate SVD // needed in the solveLoop call, which is cost-free //
          // //
        • Covariance information in various forms can be obtained with the // getCovariance(), getErrors(), getChi() // (or getChi2), getSD and getWeightedSD // methods after a solve() or after the final loop in a non-linear // solution (of course, when necessary only). //
        // // An LSQFit object can be re-used by issuing the reset() command, // or set() of new // values. If an unknown has not been used in the condition equations at all, // the doDiagonal() will make sure a proper solution is obtained, // with missing unknowns zeroed. // // Most of the calculations are done in place; however, enough data is saved // that it is possible to continue // with the same (partial) normal equations after e.g. an interim solution. // // If the normal equations are produced in separate partial sets (e.g. // in a multi-processor environment) a merge() method can combine // them. // // It is suggested to add any possible constraint equations after the merge. // // // A debugIt() method provides read access to all internal // information. // // The member definitions are split over three files. The second // one contains the templated member function definitions, to bypass the // problem of duplicate definitions of non-templated members when // pre-compiling them. The third contains methods for saving objects as // Records or through AipsIO. // // No boundary checks on input and output containers // is done for faster execution. In general these tests should be done at // the higher level routines, like the // LinearFit and // NonLinearFit classes which should be // checked for usage of LSQFit. // // // The contents can be saved in a record (toRecord), // and an object can be created from a record (fromRecord). // The record identifier is 'lfit'. //
        The object can also be saved or restored using AipsIO. //
        // // // See the tLSQFit.cc and tLSQaips.cc program for extensive examples. // // The following example will first create 2 condition equations for // 3 unknowns (the third is degenerate). It will first create normal equations // for a 2 unknown solution and solve; then it will create normal equations // for a 3 unknown solution, and solve (note that the degenerate will be // set to 0. The last one will use SVD and one condition equation.r // // #include // #include // #include // // int main() { // // Condition equations for x+y=2; x-y=4; // Double ce[2][3] = {{1, 1, 0}, {1, -1, 0}}; // Double m[2] = {2, 4}; // // Solution and error area // Double sol[3]; // Double sd, mu; // uInt rank; // Bool ok; // // // LSQ object // LSQFit fit(2); // // // Make normal equation // for (uInt i=0; i<2; i++) fit.makeNorm(ce[i], 1.0, m[i]); // // Invert(decompose) and show // ok = fit.invert(rank); // cout << "ok? " << ok << "; rank: " << rank << endl; // // Solve and show // if (ok) { // fit.solve(sol, &sd, &mu); // for (uInt i=0; i<2; i++) cout << "Sol" << i << ": " << sol[i] << endl; // cout << "sd: "<< sd << "; mu: " << mu << endl; // }; // cout << "----------" << endl; // // // Retry with 3 unknowns: note auto fill of unmentioned one // fit.set(uInt(3)); // for (uInt i=0; i<2; i++) fit.makeNorm(ce[i], 1.0, m[i]); // ok = fit.invert(rank); // cout << "ok? " << ok << "; rank: " << rank << endl; // if (ok) { // fit.solve(sol, &sd, &mu); // for (uInt i=0; i<3; i++) cout << "Sol" << i << ": " << sol[i] << endl; // cout << "sd: "<< sd << "; mu: " << mu << endl; // }; // cout << "----------" << endl; // // // Retry with 3 unknowns; but 1 condition equation and use SVD // fit.reset(); // for (uInt i=0; i<1; i++) fit.makeNorm(ce[i], 1.0, m[i]); // ok = fit.invert(rank, True); // cout << "ok? " << ok << "; rank: " << rank << endl; // if (ok) { // fit.solve(sol, &sd, &mu); // for (uInt i=0; i<3; i++) cout << "Sol" << i << ": " << sol[i] << endl; // cout << "sd: "<< sd << "; mu: " << mu << endl; // }; // cout << "----------" << endl; // // // Without SVD it would be: // fit.reset(); // for (uInt i=0; i<1; i++) fit.makeNorm(ce[i], 1.0, m[i]); // ok = fit.invert(rank); // cout << "ok? " << ok << "; rank: " << rank << endl; // if (ok) { // fit.solve(sol, &sd, &mu); // for (uInt i=0; i<3; i++) cout << "Sol" << i << ": " << sol[i] << endl; // cout << "sd: "<< sd << "; mu: " << mu << endl; // }; // cout << "----------" << endl; // // exit(0); // } // // Which will produce the output: // // ok? 1; rank: 2 // Sol0: 3 // Sol1: -1 // sd: 0; mu: 0 // ---------- // ok? 1; rank: 3 // Sol0: 3 // Sol1: -1 // Sol2: 0 // sd: 0; mu: 0 // ---------- // ok? 1; rank: 2 // Sol0: 1 // Sol1: 1 // Sol2: 0 // sd: 0; mu: 0 // ---------- // ok? 0; rank: 2 // ---------- // // // // // The class was written to be able to do complex, real standard and SVD // solutions in a simple and fast way. // // // //
      • a thorough check if all loops are optimal in the makeNorm() methods //
      • input of condition equations with cross covariance // class LSQFit { public: // Simple classes to overload templated memberfunctions struct Real { enum normType { REAL }; }; struct Complex { enum normType { COMPLEX }; }; struct Separable { enum normType { SEPARABLE }; }; struct AsReal { enum normType { ASREAL }; }; struct Conjugate { enum normType { CONJUGATE }; }; // And values to use static Real REAL; static Complex COMPLEX; static Separable SEPARABLE; static AsReal ASREAL; static Conjugate CONJUGATE; //# Public enums // State of the non-linear solution enum ReadyCode { NONREADY=0, SOLINCREMENT, DERIVLEVEL, MAXITER, NOREDUCTION, SINGULAR, N_ReadyCode }; // Offset of fields in error_p data area. enum ErrorField { // Number of condition equations NC, // Sum weights of condition equations SUMWEIGHT, // Sum known terms squared SUMLL, // Calculated chi^2 CHI2, // Number of error fields N_ErrorField }; //# Constructors // Construct an object with the number of unknowns and // constraints, using the default collinearity factor and the // default Levenberg-Marquardt adjustment factor. // // Assume real explicit LSQFit(uInt nUnknowns, uInt nConstraints=0); // Allow explicit Real specification LSQFit(uInt nUnknowns, const LSQReal &, uInt nConstraints=0); // Allow explicit Complex specification LSQFit(uInt nUnknowns, const LSQComplex &, uInt nConstraints=0); // // Default constructor (empty, only usable after a set(nUnknowns)) LSQFit(); // Copy constructor (deep copy) LSQFit(const LSQFit &other); // Assignment (deep copy) LSQFit &operator=(const LSQFit &other); //# Destructor ~LSQFit(); //# Operators //# General Member Functions // Triangularize the normal equations and determine // the rank nRank of the normal equations and, in the case of // an SVD solution, the constraint // equations. The collinearity factor is used // to determine if the system can be solved (in essence it is the square // of the sine of the angle between a column in the normal equations and // the plane suspended by the other columns: if too // parallel, the equations are degenerate). // If doSVD is given as False, False is returned if rank not // maximal, else an SVD solution is done. Bool invert(uInt &nRank, Bool doSVD=False); // Copy date from beg to end; converting if necessary to complex data // template void copy(const Double *beg, const Double *end, U &sol, LSQReal); template void copy(const Double *beg, const Double *end, U &sol, LSQComplex); template void copy(const Double *beg, const Double *end, U *sol, LSQReal); template void copy(const Double *beg, const Double *end, U *sol, LSQComplex); template void uncopy(Double *beg, const Double *end, U &sol, LSQReal); template void uncopy(Double *beg, const Double *end, U &sol, LSQComplex); template void uncopy(Double *beg, const Double *end, U *sol, LSQReal); template void uncopy(Double *beg, const Double *end, U *sol, LSQComplex); template void copyDiagonal(U &errors, LSQReal); template void copyDiagonal(U &errors, LSQComplex); // // Solve normal equations. // The solution will be given in sol. // template void solve(U *sol); template void solve(std::complex *sol); template void solve(U &sol); // // Solve a loop in a non-linear set. // The methods with the fit argument are deprecated. Use // the combination without the 'fit' parameter, and the isReady() // call. The 'fit' parameter returns // for each loop a goodness // of fit indicator. If it is >0; more loops are necessary. // If it is negative, // and has an absolute value of say less than .001, it is probably ok, and // the iterations can be stopped. // Other arguments are as for solve() and invert(). // The sol is used for both input (parameter guess) and output. // template Bool solveLoop(uInt &nRank, U *sol, Bool doSVD=False); template Bool solveLoop(uInt &nRank, std::complex *sol, Bool doSVD=False); template Bool solveLoop(uInt &nRank, U &sol, Bool doSVD=False); template Bool solveLoop(Double &fit, uInt &nRank, U *sol, Bool doSVD=False); template Bool solveLoop(Double &fit, uInt &nRank, std::complex *sol, Bool doSVD=False); template Bool solveLoop(Double &fit, uInt &nRank, U &sol, Bool doSVD=False); // // Make normal equations using the cEq condition equation (cArray) // (with nUnknowns elements) and a weight weight, // given the known observed value obs. // // doNorm and doKnown can be used // to e.g. re-use existing normal equations, i.e. the condition equations, // but make a new known side (i.e. new observations). // // The versions with cEqIndex[] indicate which of the // nUnknowns are actually present in the condition equation // (starting indexing at 0); the other terms are supposed to be zero. E.g. // if a 12-telescope array has an equation only using telescopes 2 and 4, // the lengths of cEqIndex and cEq will be both 2, // and the index will contain 1 and 3 (when telescope numbering starts at 1) // or 2 and 4 (when telescope numbering starts at 0. The index is given // as an iterator (and hence can be a raw pointer) // // The complex versions can have different interpretation of the inputs, // where the complex number can be seen either as a complex number; as two // real numbers, or as coefficients of equations with complex conjugates. // See Note 224) // for the details. // // Versions with pair assume that the pairs are created by the // SparseDiff automatic differentiation class. The pair is an index // and a value. The indices are assumed to be sorted. // // Special (makeNormSorted) indexed versions exist which assume // that the given indices are sorted (which is the case for the // LOFAR BBS environment). // // Some versions exist with two sets of equations (cEq2, obs2). // If two simultaneous equations are created they will be faster. // // Note that the // use of const U & is due to a Float->Double conversion problem // on Solaris. Linux was ok. // template void makeNorm(const V &cEq, const U &weight, const U &obs, Bool doNorm=True, Bool doKnown=True); template void makeNorm(const V &cEq, const U &weight, const U &obs, LSQFit::Real, Bool doNorm=True, Bool doKnown=True); template void makeNorm(const V &cEq, const U &weight, const std::complex &obs, Bool doNorm=True, Bool doKnown=True); template void makeNorm(const V &cEq, const U &weight, const std::complex &obs, LSQFit::Complex, Bool doNorm=True, Bool doKnown=True); template void makeNorm(const V &cEq, const U &weight, const std::complex &obs, LSQFit::Separable, Bool doNorm=True, Bool doKnown=True); template void makeNorm(const V &cEq, const U &weight, const std::complex &obs, LSQFit::AsReal, Bool doNorm=True, Bool doKnown=True); template void makeNorm(const V &cEq, const U &weight, const std::complex &obs, LSQFit::Conjugate, Bool doNorm=True, Bool doKnown=True); // template void makeNorm(uInt nIndex, const W &cEqIndex, const V &cEq, const U &weight, const U &obs, Bool doNorm=True, Bool doKnown=True); template void makeNorm(uInt nIndex, const W &cEqIndex, const V &cEq, const V &cEq2, const U &weight, const U &obs, const U &obs2, Bool doNorm=True, Bool doKnown=True); template void makeNorm(uInt nIndex, const W &cEqIndex, const V &cEq, const U &weight, const U &obs, LSQFit::Real, Bool doNorm=True, Bool doKnown=True); template void makeNorm(uInt nIndex, const W &cEqIndex, const V &cEq, const U &weight, const std::complex &obs, Bool doNorm=True, Bool doKnown=True); template void makeNorm(uInt nIndex, const W &cEqIndex, const V &cEq, const U &weight, const std::complex &obs, LSQFit::Complex, Bool doNorm=True, Bool doKnown=True); template void makeNorm(uInt nIndex, const W &cEqIndex, const V &cEq, const U &weight, const std::complex &obs, LSQFit::Separable, Bool doNorm=True, Bool doKnown=True); template void makeNorm(uInt nIndex, const W &cEqIndex, const V &cEq, const U &weight, const std::complex &obs, LSQFit::AsReal, Bool doNorm=True, Bool doKnown=True); template void makeNorm(uInt nIndex, const W &cEqIndex, const V &cEq, const U &weight, const std::complex &obs, LSQFit::Conjugate, Bool doNorm=True, Bool doKnown=True); // template void makeNorm(const std::vector > &cEq, const U &weight, const U &obs, Bool doNorm=True, Bool doKnown=True); template void makeNorm(const std::vector > &cEq, const U &weight, const U &obs, LSQFit::Real, Bool doNorm=True, Bool doKnown=True); template void makeNorm(const std::vector > &cEq, const U &weight, const std::complex &obs, Bool doNorm=True, Bool doKnown=True); template void makeNorm(const std::vector > &cEq, const U &weight, const std::complex &obs, LSQFit::Complex, Bool doNorm=True, Bool doKnown=True); template void makeNorm(const std::vector > &cEq, const U &weight, const std::complex &obs, LSQFit::Separable, Bool doNorm=True, Bool doKnown=True); template void makeNorm(const std::vector > &cEq, const U &weight, const std::complex &obs, LSQFit::AsReal, Bool doNorm=True, Bool doKnown=True); template void makeNorm(const std::vector > &cEq, const U &weight, const std::complex &obs, LSQFit::Conjugate, Bool doNorm=True, Bool doKnown=True); // template void makeNormSorted(uInt nIndex, const W &cEqIndex, const V &cEq, const U &weight, const U &obs, Bool doNorm=True, Bool doKnown=True); template void makeNormSorted(uInt nIndex, const W &cEqIndex, const V &cEq, const V &cEq2, const U &weight, const U &obs, const U &obs2, Bool doNorm=True, Bool doKnown=True); // // Get the n-th (from 0 to the rank deficiency, or missing rank, // see e.g. getDeficiency()) // constraint equation as determined by invert() in SVD-mode in // cEq[nUnknown]. False returned for illegal n. Note // that nMissing will be equal to the number of unknowns // (nUnknowns, or double that for the complex case) minus the // rank as returned from the invert() method. // template Bool getConstraint(uInt n, U *cEq) const; template Bool getConstraint(uInt n, std::complex *cEq) const; template Bool getConstraint(uInt n, U &cEq) const; // // Add a new constraint equation (updating nConstraints); or set a // numbered constraint equation (0..nConstraints-1). False if illegal // number n. The constraints are equations with nUnknowns terms, // and a constant value. E.g. measuring three angles of a triangle // could lead to equation [1,1,1] with obs as // 3.1415. Note that each complex constraint will be // converted into two real constraints (see // Note 224). // template Bool setConstraint(uInt n, const V &cEq, const U &obs); template Bool setConstraint(uInt n, const V &cEq, const std::complex &obs); template Bool setConstraint(uInt n, uInt nIndex, const W &cEqIndex, const V &cEq, const U &obs); template Bool setConstraint(uInt n, uInt nIndex, const W &cEqIndex, const V &cEq, const std::complex &obs); template Bool addConstraint(const V &cEq, const U &obs); template Bool addConstraint(const V &cEq, const std::complex &obs); template Bool addConstraint(uInt nIndex, const W &cEqIndex, const V &cEq, const U &obs); template Bool addConstraint(uInt nIndex, const W &cEqIndex, const V &cEq, const std::complex &obs); // // Merge other LSQFit object (i.e. the normal equation and // related information) into this. Both objects must have the // same number of unknowns, and be pure normal equations (i.e. no // invert(), solve(), solveLoop() or statistics calls // should have been made). If merging cannot be done, False // is returned. The index case (the index is an iterator) assumes that // the normal equations to be merged are a sparse subset of the complete // matrix. The index 'vector' specifies which unknowns are present. An index // outside the scope of the final equations will be skipped. // For highest numerical precision in the case of a larger // number of partial normal equations to be merged, it is best to merge // them in pairs (and repeat). // // Bool merge(const LSQFit &other); Bool merge(const LSQFit &other, uInt nIndex, const uInt *nEqIndex) { return mergeIt(other, nIndex, nEqIndex); } Bool merge(const LSQFit &other, uInt nIndex, const std::vector &nEqIndex) { return mergeIt(other, nIndex, &nEqIndex[0]); } template Bool merge(const LSQFit &other, uInt nIndex, const W &nEqIndex) { std::vector ix(nIndex); for (uInt i=0; i // Reset status to empty void reset(); // Set new sizes (default is for Real) // void set(uInt nUnknowns, uInt nConstraints=0); void set(Int nUnknowns, Int nConstraints=0) { set (static_cast(nUnknowns), static_cast(nConstraints)); }; void set(uInt nUnknowns, const LSQReal &, uInt nConstraints=0) { set (nUnknowns, nConstraints); }; void set(Int nUnknowns, const LSQReal &, Int nConstraints=0) { set (nUnknowns, nConstraints); }; void set(uInt nUnknowns, const LSQComplex &, uInt nConstraints=0); void set(Int nUnknowns, const LSQComplex &, Int nConstraints=0) { set (static_cast(nUnknowns), LSQComplex(), static_cast(nConstraints)); }; // // Set new factors (collinearity factor, and Levenberg-Marquardt // LMFactor) void set(Double factor=1e-6, Double LMFactor=1e-3); // Set new value solution test void setEpsValue(Double epsval=1e-8) {epsval_p = epsval; }; // Set new derivative test void setEpsDerivative(Double epsder=1e-8) {epsder_p = epsder; }; // Set maximum number of iterations void setMaxIter(uInt maxiter=0) { maxiter_p = maxiter; }; // Get number of iterations done uInt nIterations() const { return (maxiter_p>0 ? maxiter_p-niter_p : 0); }; // Set the expected form of the normal equations void setBalanced(Bool balanced=False) { balanced_p = balanced; }; // Ask the state of the non-linear solutions // LSQFit::ReadyCode isReady() const { return ready_p; }; const std::string &readyText() const; // // Get the covariance matrix (of size nUnknowns * nUnknowns) // template Bool getCovariance(U *covar); template Bool getCovariance(std::complex *covar); // // Get main diagonal of covariance function (of size nUnknowns) // template Bool getErrors(U *errors); template Bool getErrors(std::complex *errors); template Bool getErrors(U &errors); // // Get the number of unknowns uInt nUnknowns() const { return nun_p; }; // Get the number of constraints uInt nConstraints() const { return ncon_p; }; // Get the rank deficiency Note that the number is // returned assuming real values. For complex values it has to be halved // uInt getDeficiency() const { return n_p-r_p; }; // Get chi^2 (both are identical); the standard deviation (per observation) // and the standard deviation per weight unit. // Double getChi() const; Double getChi2() const { return getChi(); }; Double getSD() const; Double getWeightedSD() const; // // Debug: //
          //
        • nun = number of unknowns //
        • np = total number of solved unknowns (nun+ncon) //
        • ncon = number of constraint equations //
        • ner = number of elements in chi2 vector //
        • rank = rank) //
        • nEq = normal equation (nun*nun as triangular matrix) //
        • known = known vector (np) //
        • constr = constraint matrix (ncon*nun) //
        • er = error info vector (ner) //
        • piv = pivot vector (np) //
        • sEq = normal solution equation (np*np triangular) //
        • sol = internal solution vector (np) //
        • prec = collinearity precision //
        • nonlin = current Levenberg factor-1 //
        // Note that all pointers may be 0. void debugIt(uInt &nun, uInt &np, uInt &ncon, uInt &ner, uInt &rank, Double *&nEq, Double *&known, Double *&constr, Double *&er, uInt *&piv, Double *&sEq, Double *&sol, Double &prec, Double &nonlin) const; // // Create an LSQFit object from a record. // An error message is generated, and False // returned if an invalid record is given. A valid record will return True. // Error messages are postfixed to error. // Bool fromRecord(String &error, const RecordInterface &in); // // Create a record from an LSQFit object. // The return will be False and an error // message generated only if the object does not contain a valid object. // Error messages are postfixed to error. Bool toRecord(String &error, RecordInterface &out) const; // Get identification of record const String &ident() const; // // Save or restore using AipsIO. // void toAipsIO (AipsIO&) const; void fromAipsIO (AipsIO&); // // protected: //# enum // Bits that can be set/referenced enum StateBit { // Inverted matrix present INVERTED = 1, // Triangularised TRIANGLE = 2*INVERTED, // Non-linear solution NONLIN = 2*TRIANGLE, // Filler for cxx2html N_StateBit }; // Record field names // static const String recid; static const String state; static const String nun; static const String ncon; static const String prec; static const String startnon; static const String nonlin; static const String rank; static const String nnc; static const String piv; static const String constr; static const String known; static const String errors; static const String sol; static const String lar; static const String wsol; static const String wcov; static const String nceq; static const String nar; // //# Data // Bits set to indicate state uInt state_p; // Number of unknowns uInt nun_p; // Number of constraints uInt ncon_p; // Matrix size (will be n_p = nun_p + ncon_p) uInt n_p; // Rank of normal equations (normally n_p) uInt r_p; // Collinearity precision Double prec_p; // Levenberg start factor Double startnon_p; // Levenberg current factor Double nonlin_p; // Levenberg step factor Double stepfactor_p; // Test value for [incremental] solution in non-linear loop. // The ||sol increment||/||sol|| is tested Double epsval_p; // Test value for known vector in non-linear loop. // ||known||inf is tested Double epsder_p; // Indicator for a well balanced normal equation. A balanced equation is // one with similar values in the main diagonal. Bool balanced_p; // Maximum number of iterations for non-linear solution. If a non-zero // maximum number of iterations is set, the value is tested in non-linear // loops uInt maxiter_p; // Iteration count for non-linear solution uInt niter_p; // Indicate the non-linear state. A non-zero code indicates that non-linear // looping is ready. ReadyCode ready_p; // Pivot table (n_p) uInt *piv_p; // Normal equations (triangular nun_p * nun_p) LSQMatrix *norm_p; // Current length nceq_p uInt nnc_p; // Normal combined with constraint equations for solutions // (triangular nnc_p*nnc_p) LSQMatrix *nceq_p; // Known part equations (n_p) Double *known_p; // Counts for errors (N_ErrorField) Double *error_p; // Constraint equation area (nun_p*ncon_p)) Double *constr_p; // Solution area (n_p) Double *sol_p; // Save area for non-linear case (size determined internally) LSQFit *nar_p; // Save area for non-symmetric (i.e. with constraints) (n_p * n_p) Double *lar_p; // Work areas for interim solutions and covariance // Double *wsol_p; Double *wcov_p; // //# Member functions // Get pointer in rectangular array // Double *rowrt(uInt i) const { return &lar_p[n_p*i]; }; Double *rowru(uInt i) const { return &lar_p[nun_p*i]; }; // // Calculate the real or imag part of x*conj(y) // static Double realMC(const std::complex &x, const std::complex &y) { return (x.real()*y.real() + x.imag()*y.imag()); }; static Double imagMC(const std::complex &x, const std::complex &y) { return (x.imag()*y.real() - x.real()*y.imag()); }; static Float realMC(const std::complex &x, const std::complex &y) { return (x.real()*y.real() + x.imag()*y.imag()); }; static Float imagMC(const std::complex &x, const std::complex &y) { return (x.imag()*y.real() - x.real()*y.imag()); }; // // Initialise areas void init(); // Clear areas void clear(); // De-initialise area void deinit(); // Solve normal equations void solveIt(); // One non-linear LM loop Bool solveItLoop(Double &fit, uInt &nRank, Bool doSVD=False); // Solve missing rank part void solveMR(uInt nin); // Invert rectangular matrix (i.e. when constraints present) Bool invertRect(); // Get the norm of the current solution vector Double normSolution(const Double *sol) const; // Get the infinite norm of the known vector Double normInfKnown(const Double *known) const; // Merge sparse normal equations Bool mergeIt(const LSQFit &other, uInt nIndex, const uInt *nEqIndex); // Save current status (or part) void save(Bool all=True); // Restore current status void restore(Bool all=True); // Copy data. If all False, only the relevant data for non-linear // solution are copied (normal equations, knows and errors). void copy(const LSQFit &other, Bool all=True); // Extend the constraint equation area to the specify number of // equations. void extendConstraints(uInt n); // Create the solution equation area nceq_p and fill it. void createNCEQ(); // Get work areas for solutions, covariance // void getWorkSOL(); void getWorkCOV(); // // }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Fitting/LSQFit2.tcc000066400000000000000000001123341476623553700202430ustar00rootroot00000000000000//# LSQFit2.cc: Basic class for least squares fitting: templated methods //# Copyright (C) 1999,2000,2002,2004-2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_LSQFIT2_TCC #define SCIMATH_LSQFIT2_TCC //# // This separation of definitions necessary to get pre-compilation of // templates done without having duplicate entries problems for // non-templated member functions // //# Includes #include #include using namespace std; namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Static values //# Constructors //# Member functions template void LSQFit::copy(const Double *beg, const Double *end, U &sol, LSQReal) { std::copy(beg, end, sol); } template void LSQFit::copy(const Double *beg, const Double *end, U *sol, LSQReal) { std::copy(beg, end, sol); } template void LSQFit::copy(const Double *beg, const Double *end, U &sol, LSQComplex) { typename std::iterator_traits::pointer tsol = sol.pos(); for (const Double *i=beg; i!=end; i+=2) { *tsol++ = typename U::value_type(*i, *(i+1)); } } template void LSQFit::copy(const Double *beg, const Double *end, U *sol, LSQComplex) { for (const Double *i=beg; i!=end; i+=2) { *sol++ = U(*i, *(i+1)); } } template void LSQFit::uncopy(Double *beg, const Double *end, U &sol, LSQReal) { std::copy(sol, sol + (end-beg), beg); } template void LSQFit::uncopy(Double *beg, const Double *end, U *sol, LSQReal) { std::copy(sol, sol + (end-beg), beg); } template void LSQFit::uncopy(Double *beg, const Double *end, U &sol, LSQComplex) { typename U::difference_type n=(end-beg)/2; U solend = sol+n; Double *tbeg = beg; for (U i=sol; i void LSQFit::uncopy(Double *beg, const Double *end, U *sol, LSQComplex) { Double *tbeg = beg; for (U *i=sol; i void LSQFit::solve(U *sol) { solveIt(); copy(wsol_p, wsol_p+nun_p, sol, LSQReal()); } template void LSQFit::solve(std::complex *sol) { solveIt(); copy(wsol_p, wsol_p+nun_p, sol, LSQComplex()); } template void LSQFit::solve(U &sol) { solveIt(); copy(wsol_p, wsol_p+nun_p, sol, typename LSQTraits ::value_type>::num_type()); } template Bool LSQFit::solveLoop(uInt &nRank, U *sol, Bool doSVD) { Double fit; return solveLoop(fit, nRank, sol, doSVD); } template Bool LSQFit::solveLoop(uInt &nRank, std::complex *sol, Bool doSVD) { Double fit; return solveLoop(fit, nRank, sol, doSVD); } template Bool LSQFit::solveLoop(uInt &nRank, U &sol, Bool doSVD) { Double fit; return solveLoop(fit, nRank, sol, doSVD); } template Bool LSQFit::solveLoop(Double &fit, uInt &nRank, U *sol, Bool doSVD) { getWorkSOL(); uncopy(wsol_p, wsol_p+nun_p, sol, LSQReal()); if (solveItLoop(fit, nRank, doSVD)) { copy(wsol_p, wsol_p+nun_p, sol, LSQReal()); return True; } return False; } template Bool LSQFit::solveLoop(Double &fit, uInt &nRank, std::complex *sol, Bool doSVD) { getWorkSOL(); uncopy(wsol_p, wsol_p+nun_p, sol, LSQComplex()); if (solveItLoop(fit, nRank, doSVD)) { copy(wsol_p, wsol_p+nun_p, sol, LSQComplex()); return True; } return false; } template Bool LSQFit::solveLoop(Double &fit, uInt &nRank, U &sol, Bool doSVD) { getWorkSOL(); uncopy(wsol_p, wsol_p+nun_p, sol, typename LSQTraits ::value_type>::num_type()); if (solveItLoop(fit, nRank, doSVD)) { copy(wsol_p, wsol_p+nun_p, sol, typename LSQTraits ::value_type>::num_type()); return True; } return false; } // Note that the explicit conversions to Double are necessary for Solaris // (Linux is ok). Solaris does not zero the low-order part of a Double from // a Float, giving non-repeatable results. template void LSQFit::makeNorm(const V &cEq, const U &weight, const U &obs, Bool doNorm, Bool doKnown) { if (doNorm) { Double *i2 = norm_p->row(0); for (V cEqp=cEq; cEqp!=cEq+nun_p; ++cEqp) { if (*cEqp != 0) { for (V i1=cEqp; i1!=cEq+nun_p; ++i1) { *i2++ += Double(*cEqp)*Double(weight)*Double(*i1); } } else i2 += cEq-cEqp+nun_p; } state_p &= ~TRIANGLE; } if (doKnown) { Double obswt = obs*weight; V cEqp = cEq; for (Double *kp = known_p; kp!=known_p+nun_p; kp++) *kp += Double(*cEqp++)*obswt; error_p[NC] += 1; //cnt equations error_p[SUMWEIGHT] += weight; //sum weight error_p[SUMLL] += obs*obswt; //sum rms } } template void LSQFit::makeNorm(const V &cEq, const U &weight, const U &obs, LSQFit::Real, Bool doNorm, Bool doKnown) { makeNorm(cEq, weight, obs, doNorm, doKnown); } template void LSQFit::makeNorm(const V &cEq, const U &weight, const std::complex &obs, Bool doNorm, Bool doKnown) { uInt ln(nun_p/2); if (doNorm) { std::complex dci; for (uInt i=0; irow(2*i); //row pointer for (uInt i1=i; i1row(2*i+1); //next line duplicate for (uInt i1=2*i+1; i1 dci; for (uInt i1=0; i1 void LSQFit::makeNorm(const V &cEq, const U &weight, const std::complex &obs, LSQFit::Complex, Bool doNorm, Bool doKnown) { makeNorm(cEq, weight, obs, doNorm, doKnown); } template void LSQFit::makeNorm(const V &cEq, const U &weight, const std::complex &obs, LSQFit::Separable, Bool doNorm, Bool doKnown) { if (doNorm) { for (uInt i=0; irow(i); //row pointer for (uInt i1=i; i1row(i); //row pointer for (uInt i1=i; i1 void LSQFit::makeNorm(const V &cEq, const U &weight, const std::complex &obs, LSQFit::AsReal, Bool doNorm, Bool doKnown) { uInt ln(nun_p/2); if (doNorm) { for (uInt i=0; irow(2*i); //row pointer real Double *i2i= norm_p->row(2*i+1); //row pointer imag for (uInt i1=i; i1 void LSQFit::makeNorm(const V &cEq, const U &weight, const std::complex &obs, LSQFit::Conjugate, Bool doNorm, Bool doKnown) { if (doNorm) { for (uInt i=0; irow(i); //row pointer for (uInt i1=i; i1row(i); //row pointer for (uInt i1=i; i1 void LSQFit::makeNorm(uInt nIndex, const W &cEqIndex, const V &cEq, const U &weight, const U &obs, Bool doNorm, Bool doKnown) { if (doNorm) { for (uInt i=0; irow(cEqIndex_i); //row pointer Double eq(cEq[i]); eq *= weight; for (uInt i1=i; i1row(cEqIndex_i1)[cEqIndex_i] += eq*Double(cEq[i1]); } } state_p &= ~TRIANGLE; } if (doKnown) { Double obswt = obs*weight; for (uInt i1=0; i1 void LSQFit::makeNorm(uInt nIndex, const W &cEqIndex, const V &cEq, const V &cEq2, const U &weight, const U &obs, const U &obs2, Bool doNorm, Bool doKnown) { if (doNorm) { for (uInt i=0; irow(cEqIndex_i); //row pointer Double eq(cEq[i]); Double eq2(cEq2[i]); eq *= weight; eq2 *= weight; for (uInt i1=i; i1row(cEqIndex_i1)[cEqIndex_i] += eq*Double(cEq[i1]) + eq2*Double(cEq2[i1]); } } state_p &= ~TRIANGLE; } if (doKnown) { Double obswt = obs*weight; Double obswt2 = obs2*weight; for (uInt i1=0; i1 void LSQFit::makeNorm(uInt nIndex, const W &cEqIndex, const V &cEq, const U &weight, const U &obs, LSQFit::Real, Bool doNorm, Bool doKnown) { makeNorm(nIndex, cEqIndex, cEq, weight, obs, doNorm, doKnown); } template void LSQFit::makeNorm(uInt nIndex, const W &cEqIndex, const V &cEq, const U &weight, const std::complex &obs, Bool doNorm, Bool doKnown) { if (doNorm) { std::complex dci; for (uInt i=0; irow(2*cEqIndex[i]); //row pointer for (uInt i1=0; i1row(2*cEqIndex[i]+1); //next line row pointer for (uInt i1=2*cEqIndex[i]+1; i1 dci; for (uInt i1=0; i1 void LSQFit::makeNorm(uInt nIndex, const W &cEqIndex, const V &cEq, const U &weight, const std::complex &obs, LSQFit::Complex, Bool doNorm, Bool doKnown) { makeNorm(nIndex, cEqIndex, cEq, weight, obs, doNorm, doKnown); } template void LSQFit::makeNorm(uInt nIndex, const W &cEqIndex, const V &cEq, const U &weight, const std::complex &obs, LSQFit::Separable, Bool doNorm, Bool doKnown) { if (doNorm) { for (uInt i=0; irow(cEqIndex[i]); //row pointer if (cEqIndex[i]%2 == 0) { for (uInt i1=0; i1 void LSQFit::makeNorm(uInt nIndex, const W &cEqIndex, const V &cEq, const U &weight, const std::complex &obs, LSQFit::AsReal, Bool doNorm, Bool doKnown) { if (doNorm) { for (uInt i=0; irow(2*cEqIndex[i]); //row pointer real Double *i2i= norm_p->row(2*cEqIndex[i]+1); //row pointer imag for (uInt i1=0; i1 void LSQFit::makeNorm(uInt nIndex, const W &cEqIndex, const V &cEq, const U &weight, const std::complex &obs, LSQFit::Conjugate, Bool doNorm, Bool doKnown) { if (doNorm) { std::complex tmp(0); for (uInt i=0; irow(cEqIndex[i]); //row pointer for (uInt i1=0; i1row(cEqIndex[i]); //row pointer if (cEqIndex[i1]%2 == 0) { if (cEqIndex[i] <= cEqIndex[i1]) { i2[cEqIndex[i1]] += tmp.real(); i2[cEqIndex[i1]+1] += tmp.imag(); } i2 = norm_p->row(cEqIndex[i]+1); if (cEqIndex[i] <= cEqIndex[i1] && cEqIndex[i1]+2 < nun_p) { i2[cEqIndex[i1]+1] += tmp.real(); } if (cEqIndex[i] < cEqIndex[i1]) { i2[cEqIndex[i1]] -= tmp.imag(); } } else { if (cEqIndex[i] < cEqIndex[i1]) { i2[cEqIndex[i1]-1] += tmp.real(); i2[cEqIndex[i1]] -= tmp.imag(); } i2 = norm_p->row(cEqIndex[i]+1); if (cEqIndex[i] < cEqIndex[i1] && cEqIndex[i1]+1 < nun_p) { i2[cEqIndex[i1]] -= tmp.real(); } if (cEqIndex[i]+2 < cEqIndex[i1] && cEqIndex[i1] < nun_p) { i2[cEqIndex[i1]-1] -= tmp.imag(); } } } else { i2 = norm_p->row(cEqIndex[i]-1); if (cEqIndex[i1]%2 == 0) { if (cEqIndex[i] <= cEqIndex[i1]+1) { i2[cEqIndex[i1]] += tmp.real(); i2[cEqIndex[i1]+1] += tmp.imag(); } i2 = norm_p->row(cEqIndex[i]); if (cEqIndex[i] <= cEqIndex[i1]+1 && cEqIndex[i1]+2 < nun_p) { i2[cEqIndex[i1]+1] -= tmp.real(); } if (cEqIndex[i] < cEqIndex[i1]) { i2[cEqIndex[i1]] += tmp.imag(); } } else { if (cEqIndex[i] <= cEqIndex[i1]) { i2[cEqIndex[i1]-1] += tmp.real(); i2[cEqIndex[i1]] -= tmp.imag(); } i2 = norm_p->row(cEqIndex[i]); if (cEqIndex[i] <= cEqIndex[i1] && cEqIndex[i1]+1 < nun_p) { i2[cEqIndex[i1]] += tmp.real(); } if (cEqIndex[i] < cEqIndex[i1] && cEqIndex[i1] < nun_p) { i2[cEqIndex[i1]-1] += tmp.imag(); } } } } if (cEqIndex[i1] == nun_p-1) { i2 = norm_p->row(nun_p-1); if (cEqIndex[i] == cEqIndex[i1]) { i2[cEqIndex[i1]] += tmp.real(); } if (cEqIndex[i] == nun_p-2) { i2[cEqIndex[i1]] -= tmp.real(); } } if (cEqIndex[i1] == nun_p-2) { i2 = norm_p->row(nun_p-1); if (cEqIndex[i] == cEqIndex[i1]) { i2[cEqIndex[i1]+1] += tmp.real(); } if (cEqIndex[i] == nun_p-1) { i2[cEqIndex[i1]+1] -= tmp.real(); } } } } } state_p &= ~TRIANGLE; } if (doKnown) { std::complex tmp(0); for (uInt i1=0; i1 void LSQFit::makeNorm(const std::vector > &cEq, const U &weight, const U &obs, Bool doNorm, Bool doKnown) { if (doNorm) { for (typename std::vector >::const_iterator i=cEq.begin(); i != cEq.end(); ++i) { Double *i2 = norm_p->row(i->first); //row pointer Double eq(i->second); eq *= weight; for (typename std::vector >::const_iterator i1=i; i1 != cEq.end(); ++i1) { i2[i1->first] += eq*Double(i1->second); //equations } } state_p &= ~TRIANGLE; } if (doKnown) { Double obswt = obs*weight; for (typename std::vector >::const_iterator i1=cEq.begin(); i1 != cEq.end(); ++i1) { known_p[i1->first] += Double(i1->second)*obswt; //data vector } error_p[NC] += 1; //cnt equations error_p[SUMWEIGHT] += weight; //sum weight error_p[SUMLL] += obs*obswt; //sum rms } } template void LSQFit::makeNorm(const std::vector > &cEq, const U &weight, const U &obs, LSQFit::Real, Bool doNorm, Bool doKnown) { makeNorm(cEq, weight, obs, doNorm, doKnown); } template void LSQFit::makeNorm(const std::vector > &cEq, const U &weight, const std::complex &obs, Bool doNorm, Bool doKnown) { if (doNorm) { std::complex dci; for (typename std::vector >::const_iterator i=cEq.begin(); i != cEq.end(); ++i) { Double *i2 = norm_p->row(2*i->first); //row pointer for (typename std::vector >::const_iterator i1=cEq.begin(); i1 != cEq.end(); ++i1) { if (i->first<=i1->first) { dci = i->second*conj(i1->second); i2[2*i1->first] += dci.real()*weight; //real equations i2[2*i1->first+1] += dci.imag()*weight; //imag. equations } } Double *i4 = norm_p->row(2*i->first+1); //next line row pointer for (uInt i1=2*i->first+1; i1first+2; i1 dci; for (typename std::vector >::const_iterator i1=cEq.begin(); i1 != cEq.end(); ++i1) { dci = obs*conj(i1->second); known_p[2*i1->first] += dci.real()*weight; //real part known_p[2*i1->first+1] += dci.imag()*weight; //imag part } // errors error_p[NC] += 2; //cnt equations error_p[SUMWEIGHT] += 2*weight; //sum weight error_p[SUMLL] += weight*norm(obs); //sum rms } } template void LSQFit::makeNorm(const std::vector > &cEq, const U &weight, const std::complex &obs, LSQFit::Complex, Bool doNorm, Bool doKnown) { makeNorm(cEq, weight, obs, doNorm, doKnown); } template void LSQFit::makeNorm(const std::vector > &cEq, const U &weight, const std::complex &obs, LSQFit::Separable, Bool doNorm, Bool doKnown) { if (doNorm) { for (typename std::vector >::const_iterator i=cEq.begin(); i != cEq.end(); ++i) { Double *i2 = norm_p->row(i->first); //row pointer if (i->first%2 == 0) { for (typename std::vector >::const_iterator i1=cEq.begin(); i1 != cEq.end(); ++i1) { if (i->first<=i1->first && i1->first+1 < nun_p) { if (i1->first%2 == 0) { i2[i1->first] += realMC(i->second, i1->second)*weight; } else { i2[i1->first] += imagMC(i->second, i1->second)*weight; } } } } else { for (typename std::vector >::const_iterator i1=cEq.begin(); i1 != cEq.end(); ++i1) { if (i->first<=i1->first) { if (i1->first != nun_p-1) { if (i1->first%2 != 0) { i2[i1->first] += realMC(i->second, i1->second)*weight; } else { i2[i1->first] -= imagMC(i->second, i1->second)*weight; } } else { i2[i1->first] += realMC(i->second, i1->second)*weight; } } } } } state_p &= ~TRIANGLE; } if (doKnown) { for (typename std::vector >::const_iterator i1=cEq.begin(); i1 != cEq.end(); ++i1) { if (i1->first%2 == 0) { known_p[i1->first] += realMC(obs, i1->second)*weight; } else { known_p[i1->first] += imagMC(obs, i1->second)*weight; } } // errors error_p[NC] += 2; //cnt equations error_p[SUMWEIGHT] += 2*weight; //sum weight error_p[SUMLL] += weight*norm(obs); //sum rms } } template void LSQFit::makeNorm(const std::vector > &cEq, const U &weight, const std::complex &obs, LSQFit::AsReal, Bool doNorm, Bool doKnown) { if (doNorm) { for (typename std::vector >::const_iterator i=cEq.begin(); i != cEq.end(); ++i) { Double *i2 = norm_p->row(2*i->first); //row pointer real Double *i2i= norm_p->row(2*i->first+1); //row pointer imag for (typename std::vector >::const_iterator i1=cEq.begin(); i1 != cEq.end(); ++i1) { if (i->first<=i1->first) { i2[2*i1->first] += i->second.real()*i1->second.real()*weight; //real i2i[2*i1->first+1] += i->second.imag()*i1->second.imag()*weight; //imag } } } state_p &= ~TRIANGLE; } if (doKnown) { for (typename std::vector >::const_iterator i1=cEq.begin(); i1 != cEq.end(); ++i1) { known_p[2*i1->first] += i1->second.real()*obs.real()*weight ; //real known_p[2*i1->first+1] += i1->second.imag()*obs.imag()*weight ; //imag } // errors error_p[NC] += 2; //cnt equations error_p[SUMWEIGHT] += 2*weight; //sum weight error_p[SUMLL] += weight*norm(obs); //sum rms } } template void LSQFit::makeNorm(const std::vector > &cEq, const U &weight, const std::complex &obs, LSQFit::Conjugate, Bool doNorm, Bool doKnown) { if (doNorm) { std::complex tmp(0); for (typename std::vector >::const_iterator i=cEq.begin(); i != cEq.end(); ++i) { if (i->first < nun_p) { Double *i2 = norm_p->row(i->first); //row pointer for (typename std::vector >::const_iterator i1=cEq.begin(); i1 != cEq.end(); ++i1) { if (i1->first < nun_p) { tmp = (i->second*conj(i1->second))*weight; if (i->first%2 == 0) { Double *i2 = norm_p->row(i->first); //row pointer if (i1->first%2 == 0) { if (i->first <= i1->first) { i2[i1->first] += tmp.real(); i2[i1->first+1] += tmp.imag(); } i2 = norm_p->row(i->first+1); if (i->first <= i1->first && i1->first+2 < nun_p) { i2[i1->first+1] += tmp.real(); } if (i->first < i1->first) { i2[i1->first] -= tmp.imag(); } } else { if (i->first < i1->first) { i2[i1->first-1] += tmp.real(); i2[i1->first] -= tmp.imag(); } i2 = norm_p->row(i->first+1); if (i->first < i1->first && i1->first+1 < nun_p) { i2[i1->first] -= tmp.real(); } if (i->first+2 < i1->first && i1->first < nun_p) { i2[i1->first-1] -= tmp.imag(); } } } else { i2 = norm_p->row(i->first-1); if (i1->first%2 == 0) { if (i->first <= i1->first+1) { i2[i1->first] += tmp.real(); i2[i1->first+1] += tmp.imag(); } i2 = norm_p->row(i->first); if (i->first <= i1->first+1 && i1->first+2 < nun_p) { i2[i1->first+1] -= tmp.real(); } if (i->first < i1->first) { i2[i1->first] += tmp.imag(); } } else { if (i->first <= i1->first) { i2[i1->first-1] += tmp.real(); i2[i1->first] -= tmp.imag(); } i2 = norm_p->row(i->first); if (i->first <= i1->first && i1->first+1 < nun_p) { i2[i1->first] += tmp.real(); } if (i->first < i1->first && i1->first < nun_p) { i2[i1->first-1] += tmp.imag(); } } } } if (i1->first == nun_p-1) { i2 = norm_p->row(nun_p-1); if (i->first == i1->first) { i2[i1->first] += tmp.real(); } if (i->first == nun_p-2) { i2[i1->first] -= tmp.real(); } } if (i1->first == nun_p-2) { i2 = norm_p->row(nun_p-1); if (i->first == i1->first) { i2[i1->first+1] += tmp.real(); } if (i->first == nun_p-1) { i2[i1->first+1] -= tmp.real(); } } } } } state_p &= ~TRIANGLE; } if (doKnown) { std::complex tmp(0); for (typename std::vector >::const_iterator i1=cEq.begin(); i1 != cEq.end(); ++i1) { tmp = obs*conj(i1->second)*weight; if (i1->first%2 == 0) { known_p[i1->first] += tmp.real(); known_p[i1->first+1] += tmp.imag(); } else { known_p[i1->first-1] += tmp.real(); known_p[i1->first] -= tmp.imag(); } } // errors error_p[NC] += 2; //cnt equations error_p[SUMWEIGHT] += 2*weight; //sum weight error_p[SUMLL] += weight*norm(obs); //sum rms } } // template void LSQFit::makeNormSorted(uInt nIndex, const W &cEqIndex, const V &cEq, const U &weight, const U &obs, Bool doNorm, Bool doKnown) { if (doNorm) { for (uInt i=0; irow(cEqIndex[i]); // row pointer Double eq(cEq[i]); eq *= weight; for (uInt i1=i; i1 void LSQFit::makeNormSorted(uInt nIndex, const W &cEqIndex, const V &cEq, const V &cEq2, const U &weight, const U &obs, const U &obs2, Bool doNorm, Bool doKnown) { if (doNorm) { for (uInt i=0; irow(cEqIndex[i]); // row pointer Double eq(cEq[i]); Double eq2(cEq2[i]); eq *= weight; eq2 *= weight; for (uInt i1=i; i1 Bool LSQFit::getConstraint(uInt n, U *cEq) const { n += r_p; if (nrow(i1)[n]; //copy constraint Double r1 = std::abs(cEq[piv_p[i1]]); if (r1 > 1e-8) r0 = std::min(r0, r1); //normalisation } for (uInt i1=r_p; i1 Bool LSQFit::getConstraint(uInt n, std::complex *cEq) const { if (2*n+1 < nun_p) { U *eqp = new U[2*nun_p]; if (getConstraint(2*n, eqp) && getConstraint(2*n+1, eqp+nun_p)) { for (uInt j=0; 2*j+1(eqp[2*j], -eqp[2*j+1]); } delete [] eqp; return True; } delete [] eqp; } return False; } template Bool LSQFit::getConstraint(uInt n, U &cEq) const { if (n::pointer eqp = new typename std::iterator_traits::value_type[nun_p]; if (getConstraint(n, eqp)) { typename U::difference_type l = nun_p/LSQTraits:: value_type >::size; std::copy(eqp, eqp+l, cEq); delete [] eqp; return True; } delete [] eqp; } return False; } template Bool LSQFit::setConstraint(uInt n, const V &cEq, const U &obs) { if (n>=ncon_p || nun_p==0) return False; std::copy(cEq, cEq+nun_p, constr_p+n*nun_p); known_p[nun_p+n] = obs; state_p &= ~TRIANGLE; return True; } template Bool LSQFit::setConstraint(uInt n, const V &cEq, const std::complex &obs) { if (2*n+1>=ncon_p || nun_p==0) return False; for (uInt i=0; i Bool LSQFit::setConstraint(uInt n, uInt nIndex, const W &cEqIndex, const V &cEq, const U &obs) { if (n>=ncon_p || nun_p==0) return False; for (uInt i=0; i Bool LSQFit::setConstraint(uInt n, uInt nIndex, const W &cEqIndex, const V &cEq, const std::complex &obs) { if (2*n+1>=ncon_p || nun_p == 0) return False; for (uInt i=0; i Bool LSQFit::addConstraint(const V &cEq, const U &obs) { extendConstraints(ncon_p+1); return setConstraint(ncon_p-1, cEq, obs); } template Bool LSQFit::addConstraint(const V &cEq, const std::complex &obs) { extendConstraints(ncon_p+2); return setConstraint((ncon_p-2)/2, cEq, obs); } template Bool LSQFit::addConstraint(uInt nIndex, const W &cEqIndex, const V &cEq, const U &obs) { extendConstraints(ncon_p+1); return setConstraint(ncon_p-1, nIndex, cEqIndex, cEq, obs); } template Bool LSQFit::addConstraint(uInt nIndex, const W &cEqIndex, const V &cEq, const std::complex &obs) { extendConstraints(ncon_p+2); return setConstraint((ncon_p-2)/2, nIndex, cEqIndex, cEq, obs);} template Bool LSQFit::getCovariance(U *covar) { if (!invertRect()) return False; for (uInt i=0; irow(i); U *j2 = covar + i*nun_p; for (uInt i1=0; i1(nceq_p->row(i1)[i]); } for (uInt i1=i; i1(j0[i1]); } } return True; } template Bool LSQFit::getCovariance(std::complex *covar) { getWorkCOV(); if (!LSQFit::getCovariance(wcov_p)) return False; for (uInt i=0; i(wcov_p[i*n_p + j], wcov_p[i*n_p + j+1]); } } return True; } template Bool LSQFit::getErrors(U *errors) { if (!invertRect()) return False; for (uInt i=0; irow(i)[i]))*error_p[CHI2]; } return True; } template Bool LSQFit::getErrors(std::complex *errors) { if (!invertRect()) return False; for (uInt i=0; i+1(std::sqrt(std::abs(nceq_p->row(i)[i])), std::sqrt(std::abs(nceq_p->row(i+1)[i+1])))* static_cast(error_p[CHI2]); } return True; } template Bool LSQFit::getErrors(U &errors) { if (!invertRect()) return False; copyDiagonal(errors, typename LSQTraits ::value_type>::num_type()); return True; } template void LSQFit::copyDiagonal(U &errors, LSQReal) { for (uInt i=0; idiag(i)))*error_p[CHI2]; } } template void LSQFit::copyDiagonal(U &errors, LSQComplex) { for (uInt i=0; i+1row(i)[i])), std::sqrt(std::abs(nceq_p->row(i+1)[i+1])))* (error_p[CHI2]); } } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Fitting/LSQFit3.cc000066400000000000000000000221461476623553700200610ustar00rootroot00000000000000//# LSQFit3.cc: Basic class for leastr squares fitting -- Record from/to //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Constants const String LSQFit::recid = String("recid"); const String LSQFit::state = String("state"); const String LSQFit::nun = String("nun"); const String LSQFit::ncon = String("ncon"); const String LSQFit::prec = String("prec"); const String LSQFit::startnon = String("startnon"); const String LSQFit::nonlin = String("nonlin"); const String LSQFit::rank = String("rank"); const String LSQFit::nnc = String("nnc"); const String LSQFit::piv = String("piv"); const String LSQFit::constr = String("constr"); const String LSQFit::known = String("known"); const String LSQFit::errors = String("error"); const String LSQFit::sol = String("sol"); const String LSQFit::lar = String("lar"); const String LSQFit::wsol = String("wsol"); const String LSQFit::wcov = String("wcov"); const String LSQFit::nceq = String("nceq"); const String LSQFit::nar = String("nar"); Bool LSQFit::toRecord(String &error, RecordInterface &out) const { out.define(RecordFieldId(recid), ident()); out.define(RecordFieldId(state), static_cast(state_p)); out.define(RecordFieldId(nun), static_cast(nun_p)); out.define(RecordFieldId(ncon), static_cast(ncon_p)); out.define(RecordFieldId(prec), prec_p); out.define(RecordFieldId(startnon), startnon_p); out.define(RecordFieldId(nonlin), nonlin_p); out.define(RecordFieldId(rank), static_cast(r_p)); out.define(RecordFieldId(nnc), static_cast(nnc_p)); if (!norm_p->toRecord(error, out)) return False; if (piv_p && !LSQMatrix::putCArray(error, out, piv, n_p, piv_p)) return False; if (constr_p && !LSQMatrix::putCArray(error, out, constr, n_p*ncon_p, constr_p)) return False; if (known_p && !LSQMatrix::putCArray(error, out, known, n_p, known_p)) return False; if (error_p && !LSQMatrix::putCArray(error, out, errors, N_ErrorField, error_p)) return False; if (sol_p && !LSQMatrix::putCArray(error, out, sol, n_p, sol_p)) return False; if (lar_p && !LSQMatrix::putCArray(error, out, lar, n_p*n_p, lar_p)) return False; if (wsol_p && !LSQMatrix::putCArray(error, out, wsol, n_p, wsol_p)) return False; if (wcov_p && !LSQMatrix::putCArray(error, out, wcov, n_p*n_p, wcov_p)) return False; if (nceq_p) { Record rnceq; if (!nceq_p->toRecord(error, rnceq)) return False; out.defineRecord(RecordFieldId(nceq), rnceq); } if (nar_p) { Record rnar; if (!nar_p->toRecord(error, rnar)) return False; out.defineRecord(RecordFieldId(nar), rnar); } return True; } Bool LSQFit::fromRecord(String &error, const RecordInterface &in) { if (in.isDefined(recid) && in.type(in.idToNumber(RecordFieldId(recid))) == TpString && in.isDefined(state) && in.type(in.idToNumber(RecordFieldId(state))) == TpInt && in.isDefined(nun) && in.type(in.idToNumber(RecordFieldId(nun))) == TpInt && in.isDefined(ncon) && in.type(in.idToNumber(RecordFieldId(ncon))) == TpInt && in.isDefined(prec) && in.type(in.idToNumber(RecordFieldId(prec))) == TpDouble && in.isDefined(startnon) && in.type(in.idToNumber(RecordFieldId(startnon))) == TpDouble && in.isDefined(nonlin) && in.type(in.idToNumber(RecordFieldId(nonlin))) == TpDouble && in.isDefined(rank) && in.type(in.idToNumber(RecordFieldId(rank))) == TpInt && in.isDefined(nnc) && in.type(in.idToNumber(RecordFieldId(nnc))) == TpInt) { String rrecid; in.get(RecordFieldId(recid), rrecid); if (rrecid != ident()) { error += String("Unknown record identity ") + rrecid + " for fitting record"; return False; } Int rnun; Int rncon; in.get(RecordFieldId(nun), rnun); in.get(RecordFieldId(ncon), rncon); set(uInt(rnun), uInt(rncon)); in.get(RecordFieldId(prec), prec_p); in.get(RecordFieldId(startnon), startnon_p); in.get(RecordFieldId(nonlin), nonlin_p); Int tmp; in.get(RecordFieldId(rank), tmp); r_p = tmp; in.get(RecordFieldId(state), tmp); state_p = tmp; in.get(RecordFieldId(nnc), tmp); state_p = tmp; if (!norm_p->fromRecord(error, in)) return False; if (in.isDefined(piv) && !LSQMatrix::getCArray(error, in, piv, n_p, piv_p)) return False; if (in.isDefined(constr) && !LSQMatrix::getCArray(error, in, constr, 0, constr_p)) return False; if (in.isDefined(known) && !LSQMatrix::getCArray(error, in, known, n_p, known_p)) return False; if (in.isDefined(errors) && !LSQMatrix::getCArray(error, in, errors, N_ErrorField, error_p)) return False; if (in.isDefined(sol) && !LSQMatrix::getCArray(error, in, sol, n_p, sol_p)) return False; if (in.isDefined(lar) && !LSQMatrix::getCArray(error, in, lar, 0, lar_p)) return False; if (in.isDefined(wsol) && !LSQMatrix::getCArray(error, in, wsol, n_p, wsol_p)) return False; if (in.isDefined(wcov) && !LSQMatrix::getCArray(error, in, wcov, 0, wcov_p)) return False; if (in.isDefined(nceq)) { if (!nceq_p) nceq_p = new LSQMatrix; if (!nceq_p->fromRecord(error, in.asRecord(RecordFieldId(nceq)))) { return False; } } if (in.isDefined(nar)) { if (!nar_p) nar_p = new LSQFit; if (!nar_p->fromRecord(error, in.asRecord(RecordFieldId(nar)))) { return False; } } } else { error += String("Incorrect fields for fitting record"); return False; } return True; } const String &LSQFit::ident() const { static String myid = "lfit"; return myid; } void LSQFit::toAipsIO (AipsIO& out) const { out.putstart (ident(), 1); out << state_p << nun_p << ncon_p; out << prec_p << startnon_p << nonlin_p << r_p << nnc_p; if (norm_p) { out << True; norm_p->toAipsIO (out); } else { out << False; } LSQMatrix::putCArray (out, n_p, piv_p); LSQMatrix::putCArray (out, n_p*ncon_p, constr_p); LSQMatrix::putCArray (out, n_p, known_p); LSQMatrix::putCArray (out, N_ErrorField, error_p); LSQMatrix::putCArray (out, n_p, sol_p); LSQMatrix::putCArray (out, n_p*n_p, lar_p); LSQMatrix::putCArray (out, n_p, wsol_p); LSQMatrix::putCArray (out, n_p*n_p, wcov_p); LSQMatrix::putCArray (out, n_p, piv_p); if (nceq_p) { out << True; nceq_p->toAipsIO (out); } else { out << False; } if (nar_p) { out << True; nar_p->toAipsIO (out); } else { out << False; } out.putend(); } void LSQFit::fromAipsIO (AipsIO& in) { bool flag; in.getstart (ident()); in >> state_p >> nun_p >> ncon_p; set (nun_p, ncon_p); in >> prec_p >> startnon_p >> nonlin_p >> r_p >> nnc_p; in >> flag; if (flag) { if (!norm_p) norm_p = new LSQMatrix; norm_p->fromAipsIO (in); } LSQMatrix::getCArray (in, n_p, piv_p); LSQMatrix::getCArray (in, n_p*ncon_p, constr_p); LSQMatrix::getCArray (in, n_p, known_p); LSQMatrix::getCArray (in, N_ErrorField, error_p); LSQMatrix::getCArray (in, n_p, sol_p); LSQMatrix::getCArray (in, n_p*n_p, lar_p); LSQMatrix::getCArray (in, n_p, wsol_p); LSQMatrix::getCArray (in, n_p*n_p, wcov_p); LSQMatrix::getCArray (in, n_p, piv_p); in >> flag; if (flag) { if (!nceq_p) nceq_p = new LSQMatrix; nceq_p->fromAipsIO (in); } in >> flag; if (flag) { if (!nar_p) nar_p = new LSQFit; nar_p->fromAipsIO (in); } in.getend(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/scimath/Fitting/LSQMatrix.cc000066400000000000000000000065661476623553700205300ustar00rootroot00000000000000//# LSQMatrix.cc: Support class for the LSQ package //# Copyright (C) 2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors LSQMatrix::LSQMatrix() : n_p(0), len_p(0), nm1_p(0), n2m1_p(0), n2p1_p(0), trian_p(0) {} LSQMatrix::LSQMatrix(uInt n) : n_p(n), len_p(0), nm1_p(0), n2m1_p(0), n2p1_p(0), trian_p(0) { init(); clear(); } LSQMatrix::LSQMatrix(uInt n, Bool) : n_p(2*n), len_p(0), nm1_p(0), n2m1_p(0), n2p1_p(0), trian_p(0) { init(); clear(); } LSQMatrix::LSQMatrix(const LSQMatrix &other) : RecordTransformable(), n_p(other.n_p), len_p(0), nm1_p(0), n2m1_p(0), n2p1_p(0), trian_p(0) { init(); copy(other); } LSQMatrix &LSQMatrix::operator=(const LSQMatrix &other) { if (this != &other) { deinit(); n_p = other.n_p; init(); copy(other); } return *this; } //# Destructor LSQMatrix::~LSQMatrix() { deinit(); } void LSQMatrix::init() { if (n_p) { len_p = (n_p*(n_p+1))/2; nm1_p = n_p-1; n2m1_p = 2*n_p-1; n2p1_p = 2*n_p+1; trian_p = new Double[len_p]; } else { len_p = 0; nm1_p = 0; n2m1_p = 0; n2p1_p = 0; trian_p = 0; } } void LSQMatrix::clear() { std::fill_n(trian_p, len_p, 0.0); } void LSQMatrix::deinit() { delete [] trian_p; trian_p=0; } void LSQMatrix::set(uInt n) { deinit(); n_p = n; init(); clear(); } void LSQMatrix::set(uInt n, Bool) { deinit(); n_p = 2*n; init(); clear(); } void LSQMatrix::copy(const LSQMatrix &other) { if (!trian_p && len_p) trian_p = new Double[len_p]; std::copy(other.trian_p, other.trian_p+len_p, trian_p); } //# Member functions void LSQMatrix::doDiagonal(uInt n) { for (uInt i=0; i #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class AipsIO; // Support class for the LSQ package // // // //
      • Some knowledge of Matrix operations // // // // From Least SQuares and Matrix // // // // The LSQMatrix class contains the handling of the basic container used // in the LSQFit class and its derivatives. // This basic container is a triangular matrix. // // The basic operations provided are referencing and indexing of cells, // rows, columns and diagonal of the triangular matrix. // The class is a private structure, with explicit friends. // // The class contains a number of public methods (with _pub in name) that // can be used anywhere, and which perform index range checking. // // The contents can be saved in a record (toRecord), // and an object can be created from a record (fromRecord). // The record identifier is 'tmat'. // // // // See the LSQFit class for its use. // // // // The class was written to isolate the handling of the normal equations // used in the LSQ classes. // // // //
      • Look in possibility of an STL iterator along row, column and // diagonal // class LSQMatrix : public RecordTransformable { //# Friends friend class LSQFit; public: // A set of public interface functions. Checks for index ranges are made, // and zero or null returned if error. // // Get row pointer in normal equation (points to element [i][0]) Double *row_pub(uInt i) const { return (irow is at row i. // void incRow_pub(Double *&row, uInt i) const { if (i0) decRow(row,i); }; // // Get diagonal element pointer [i][i] Double *diag_pub(uInt i) const { return ((iinvert() is called). Only n-length sub-matrix is done. void doDiagonal_pub(uInt n) { if (n1+fac void mulDiagonal_pub(uInt n, Double fac) { if (nfac to n-length of diagonal void addDiagonal_pub(uInt n, Double fac) { if (n private: //# Constructors // Default constructor (empty, only usable after a set(n)) LSQMatrix(); // Construct an object with the number of rows and columns indicated. // If a Bool argument is present, the number // will be taken as double the number given (assumes complex). // explicit LSQMatrix(uInt n); LSQMatrix(uInt n, Bool); // // Copy constructor (deep copy) LSQMatrix(const LSQMatrix &other); // Assignment (deep copy) LSQMatrix &operator=(const LSQMatrix &other); //# Destructor ~LSQMatrix(); //# Operators // Index an element in the triangularised matrix // Double &operator[](uInt index) { return (trian_p[index]); }; Double operator[](uInt index) const { return (trian_p[index]); }; // //# General Member Functions // Reset all data to zero void reset() { clear(); }; // Set new sizes (default is for Real, a Bool argument will make it complex) // void set(uInt n); void set(uInt n, Bool); // // Get row pointer in normal equation (points to element [i][0]) Double *row(uInt i) const { return &trian_p[((n2m1_p-i)*i)/2]; }; // Get next row or previous row pointer in normal equation if the pointer // row is at row i. // void incRow(Double *&row, uInt i) const { row += nm1_p-i; }; void decRow(Double *&row, uInt i) const { row -= n_p-i; }; // // Get diagonal element pointer [i][i] Double *diag(uInt i) const { return &trian_p[((n2p1_p-i)*i)/2]; }; // Get length of triangular array uInt nelements() const { return (len_p); }; // Get number of rows uInt nrows() const { return n_p; }; // Copy data. void copy(const LSQMatrix &other); // Initialise matrix void init(); // Clear matrix void clear(); // De-initialise matrix void deinit(); // Make diagonal element 1 if zero (Note that this is always called when // invert() is called). Only n-length sub-matrix is done. void doDiagonal(uInt n); // Multiply n-length of diagonal with 1+fac void mulDiagonal(uInt n, Double fac); // Add fac to n-length of diagonal void addDiagonal(uInt n, Double fac); // Determine max of abs values of n-length of diagonal Double maxDiagonal(uInt n); // Create a Matrix from a record. An error message is generated, and False // returned if an invalid record is given. A valid record will return True. // Error messages are postfixed to error. // Bool fromRecord(String &error, const RecordInterface &in); // // Create a record from an LSQMatrix. The return will be False and an error // message generated only if the object does not contain a valid Matrix. // Error messages are postfixed to error. Bool toRecord(String &error, RecordInterface &out) const; // Get identification of record const String &ident() const; // Convert a carray to/from a record. Field only written if // non-zero length. No carray created if field does not exist on input. // False returned if unexpectedly no data available for non-zero length // (put), or a field has zero length vector(get). // static Bool putCArray(String &error, RecordInterface &out, const String &fname, uInt len, const Double * const in); static Bool getCArray(String &error, const RecordInterface &in, const String &fname, uInt len, Double *&out); static Bool putCArray(String &error, RecordInterface &out, const String &fname, uInt len, const uInt * const in); static Bool getCArray(String &error, const RecordInterface &in, const String &fname, uInt len, uInt *&out); // // Save or restore using AipsIO. void fromAipsIO (AipsIO& in); void toAipsIO (AipsIO& out) const; static void putCArray (AipsIO& out, uInt len, const Double* const in); static void getCArray (AipsIO& in, uInt len, Double*& out); static void putCArray (AipsIO& out, uInt len, const uInt* const in); static void getCArray (AipsIO& in, uInt len, uInt*& out); //# Data // Matrix size (linear size) uInt n_p; // Derived sizes (all 0 if n_p equals 0) // // Total size uInt len_p; // n-1 uInt nm1_p; // 2n-1 Int n2m1_p; // 2n+1 Int n2p1_p; // // Matrix (triangular n_p * n_p) Double *trian_p; // Record field names static const String tmatsiz; static const String tmatdat; // // // }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Fitting/LSQMatrix2.cc000066400000000000000000000130751476623553700206030ustar00rootroot00000000000000//# LSQMatrix2.cc: Support class for the LSQ package -- Record from/to //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Constants const String LSQMatrix::tmatsiz = String("tmatsiz"); const String LSQMatrix::tmatdat = String("tmatdat"); Bool LSQMatrix::fromRecord(String &error, const RecordInterface &in) { set(0); Int vlen; if (in.isDefined(tmatsiz) && in.type(in.idToNumber(RecordFieldId(tmatsiz))) == TpInt) { in.get(RecordFieldId(tmatsiz), vlen); } else { error += String("No triangular matrix length present"); return False; } set(vlen); return getCArray(error, in, tmatdat, len_p, trian_p); } Bool LSQMatrix::toRecord(String &error, RecordInterface &out) const { out.define(RecordFieldId(tmatsiz), static_cast(n_p)); if (n_p) return putCArray(error, out, tmatdat, len_p, trian_p); return True; } const String &LSQMatrix::ident() const { static String myid = "tmat"; return myid; } Bool LSQMatrix::putCArray(String &error, RecordInterface &out, const String &fname, uInt len, const Double * const in) { if (len) { if (in) { Vector vt(len); std::copy(in, in+len, vt.data()); out.define(RecordFieldId(fname), vt); } else { error += String("No data for non-empty ") + fname + "vector"; return False; } } return True; } Bool LSQMatrix::getCArray(String &error, const RecordInterface &in, const String &fname, uInt len, Double *&out) { if (in.isDefined(fname) && in.type(in.idToNumber(RecordFieldId(fname))) == TpArrayDouble) { Vector vt; in.get(RecordFieldId(fname), vt); uInt vlen = vt.nelements(); if (!out) out = new Double[vlen]; if (len && vlen != len) { error += String("Inconsistency between lengths in " + fname + "field in record"); return False; } std::copy(vt.data(), vt.data()+len, out); } return True; } Bool LSQMatrix::putCArray(String &error, RecordInterface &out, const String &fname, uInt len, const uInt * const in) { if (len) { if (in) { Vector vt(len); std::copy(in, in+len, vt.data()); out.define(RecordFieldId(fname), vt); } else { error += String("No data for non-empty ") + fname + "vector"; return False; } } return True; } Bool LSQMatrix::getCArray(String &error, const RecordInterface &in, const String &fname, uInt len, uInt *&out) { if (in.isDefined(fname) && in.type(in.idToNumber(RecordFieldId(fname))) == TpArrayInt) { Vector vt; in.get(RecordFieldId(fname), vt); uInt vlen = vt.nelements(); if (!out) out = new uInt[vlen]; if (len && vlen != len) { error += String("Inconsistency between lengths in " + fname + "field in record"); return False; } std::copy(vt.data(), vt.data()+len, out); } return True; } void LSQMatrix::toAipsIO (AipsIO& out) const { out << n_p; if (n_p) putCArray(out, len_p, trian_p); } void LSQMatrix::fromAipsIO (AipsIO& in) { set(0); uInt n; in >> n; set(n); if (n > 0) getCArray (in, len_p, trian_p); } void LSQMatrix::putCArray (AipsIO& out, uInt len, const Double* const in) { if (in) { out << True; out.put (len, in); } else { out << False; } } void LSQMatrix::getCArray (AipsIO& in, uInt len, Double*& out) { Bool flag; in >> flag; if (flag) { uInt vlen; in >> vlen; if (vlen > 0) { if (!out) out = new Double[vlen]; AlwaysAssert (vlen == len, AipsError); in.get (len, out); } } } void LSQMatrix::putCArray (AipsIO& out, uInt len, const uInt* const in) { if (in) { out << True; out.put (len, in); } else { out << False; } } void LSQMatrix::getCArray (AipsIO& in, uInt len, uInt*& out) { Bool flag; in >> flag; if (flag) { uInt vlen; in >> vlen; if (vlen > 0) { if (!out) out = new uInt[vlen]; AlwaysAssert (vlen == len, AipsError); in.get (len, out); } } } } //# NAMESPACE CASACORE - END casacore-3.7.1/scimath/Fitting/LSQTraits.h000066400000000000000000000111121476623553700203530ustar00rootroot00000000000000//# LSQTraits.h: Typing support classes for LSQ classes //# Copyright (C) 2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_LSQTRAITS_H #define SCIMATH_LSQTRAITS_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // Typing support classes for LSQ classes // // // // The following classes are used in detremining the type of iterator // presented to the LSQFit class. They are for a large part based on ideas by // Alexandrescu(2001), 'Modern C++ design'. // // // See LSQFit class, especially the // LSQFit2.cc // defintion file. // // // To ease the interface to Fitting (and probably other) classes, by producing // a framework that can be used with Casacore containers. // // //
      • Add more type info if and when needed // // Type of real numeric class indicator class LSQReal { typedef LSQReal value_type; }; // Type of complex numeric class indicator class LSQComplex { typedef LSQComplex value_type; }; // Non relevant class indicator class LSQNull {}; // Determine if pointer type template class LSQType { private: template struct PointerTraits { enum { result = False }; typedef LSQNull Pointee; }; template struct PointerTraits { enum { result = True }; typedef U Pointee; }; public: enum { isPointer = PointerTraits::result }; typedef typename PointerTraits::Pointee Pointee; }; // Traits for numeric classes used template class LSQTraits { public: // Defining type typedef T value_type; // Numeric base type typedef Char base; // Numeric type typedef Char num_type; // Number of basic numeric type elements enum { size = 0 }; }; #if defined LSQTraits_F #undef LSQTraits_F #endif #define LSQTraits_F LSQTraits // LSQTraits specialization for Float template <> class LSQTraits_F { public: typedef Float value_type; typedef Float base; typedef LSQReal num_type; enum { size = 1 }; }; #undef LSQTraits_F #if defined LSQTraits_D #undef LSQTraits_D #endif #define LSQTraits_D LSQTraits // LSQTraits specialization for Double template <> class LSQTraits_D { public: typedef Double value_type; typedef Double base; typedef LSQReal num_type; enum { size = 1 }; }; #undef LSQTraits_D #if defined LSQTraits_CD #undef LSQTraits_CD #endif #define LSQTraits_CD LSQTraits // LSQTraits specialization for DComplex template <> class LSQTraits_CD > { public: typedef std::complex value_type; typedef Double base; typedef LSQComplex num_type; enum { size = 2 }; }; #undef LSQTraits_CD #if defined LSQTraits_CF #undef LSQTraits_CF #endif #define LSQTraits_CF LSQTraits // LSQTraits specialization for Complex template <> class LSQTraits_CF > { public: typedef std::complex value_type; typedef Float base; typedef LSQComplex num_type; enum { size = 2 }; }; #undef LSQTraits_CF } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Fitting/LSQaips.h000066400000000000000000000151421476623553700200500ustar00rootroot00000000000000//# LSQaips.h: Interface for Casacore Vectors in least squares fitting //# Copyright (C) 1999,2000,2001,2004,2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_LSQAIPS_H #define SCIMATH_LSQAIPS_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // Interface for Casacore Vectors in least squares fitting // // // // //
      • LSQFit class // // // // From Least SQuares and aips++ (now Casacore) // // // // The interface used in the LSQaips class is in terms of // Casacore Vectors directly, rather than an STL iterator (like // VectorSTLIterator) based // on it. // // Its functionality is identical to that of the // LSQFit class, although it will be faster to use // the iterator interface directly, since constructing of temporary iterators // can be avoided // // // // See the tLSQaips.cc program for extensive examples. // Note: this class is in an interim state. // // // // The class was written to enable easy tranistion from the current Vector // to the Vector::iterator interface. // // // //
      • create all the method interfaces // class LSQaips : public LSQFit { public: //# Constructors // Construct an object with the number of unknown, knowns and // constraints, and type, using the default collinearity factor and the // default Levenberg-Marquardt adjustment factor // // Assume real LSQaips(uInt nUnknowns, uInt nConstraints=0) : LSQFit(nUnknowns, nConstraints) {;} // Allow explicit complex/real specification // LSQaips(uInt nUnknowns, const LSQReal &, uInt nConstraints=0) : LSQFit(nUnknowns, LSQReal(), nConstraints) {;} LSQaips(uInt nUnknowns, const LSQComplex &, uInt nConstraints=0) : LSQFit(nUnknowns, LSQComplex(), nConstraints) {;} // // // Default constructor (empty, real, only usable after a set(nUnknowns)) LSQaips() : LSQFit() {;} // Copy constructor (deep copy) LSQaips(const LSQaips &other) : LSQFit(other) {;} // Assignment (deep copy) LSQaips &operator=(const LSQaips &other) { if (this != &other) LSQFit::operator=(other); return *this; } //# Destructor ~LSQaips() {;} //# Operators //# General Member Functions // Solve normal equations. // The solution will be given in sol. // template void solve(U *sol) { LSQFit::solve(sol); } template void solve(std::complex *sol) { LSQFit::solve(sol); } template void solve(U &sol) { LSQFit::solve(sol); } template void solve(Vector &sol) { sol.resize(nUnknowns()/LSQTraits::size); LSQFit::solve(sol.data()); } // // Solve a Levenberg-Marquardt loop. Note that the solution sol // is used both and input and output. No check on the size is done. // template Bool solveLoop(uInt &nRank, U *sol, Bool doSVD=False) { return LSQFit::solveLoop(nRank, sol, doSVD); } template Bool solveLoop(uInt &nRank, std::complex *sol, Bool doSVD=False) { return LSQFit::solveLoop(nRank, sol, doSVD); } template Bool solveLoop(uInt &nRank, U &sol, Bool doSVD=False) { return LSQFit::solveLoop(nRank, sol, doSVD); } template Bool solveLoop(uInt &nRank, Vector &sol, Bool doSVD=False); template Bool solveLoop(Double &fit, uInt &nRank, U *sol, Bool doSVD=False) { return LSQFit::solveLoop(fit, nRank, sol, doSVD); } template Bool solveLoop(Double &fit, uInt &nRank, std::complex *sol, Bool doSVD=False) { return LSQFit::solveLoop(fit, nRank, sol, doSVD); } template Bool solveLoop(Double &fit, uInt &nRank, U &sol, Bool doSVD=False) { return LSQFit::solveLoop(fit, nRank, sol, doSVD); } template Bool solveLoop(Double &fit, uInt &nRank, Vector &sol, Bool doSVD=False); // // Get the covariance matrix. False if an error occurred // (of size nUnknowns * nUnknowns) // template Bool getCovariance(U *covar) { return LSQFit::getCovariance(covar); } template Bool getCovariance(std::complex *covar) { return LSQFit::getCovariance(covar); } template Bool getCovariance(Array &covar); // // Get main diagonal of covariance function (of size nUnknowns) // template Bool getErrors(U *errors) { return LSQFit::getErrors(errors); } template Bool getErrors(std::complex *errors) { return LSQFit::getErrors(errors); } template Bool getErrors(U &errors) { return LSQFit::getErrors(errors); } template Bool getErrors(Vector &errors) { errors.resize(nUnknowns()/LSQTraits::size); return LSQFit::getErrors(errors.data()); } // private: //# Data }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Fitting/LSQaips.tcc000066400000000000000000000042741476623553700203760ustar00rootroot00000000000000//# LSQaips.cc: Interface for Casacore Vectors in least squares fitting //# Copyright (C) 1998,1999,2000,2001,2004,2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_LSQAIPS_TCC #define SCIMATH_LSQAIPS_TCC //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Destructor //# Member functions template Bool LSQaips::getCovariance(Array &covar) { if (!invertRect()) return False; covar.resize(); uInt n = nUnknowns()/LSQTraits::size; covar.resize(IPosition(2, n, n)); return LSQFit::getCovariance(covar.data()); } template Bool LSQaips::solveLoop(Double &fit, uInt &nRank, Vector &sol, Bool doSVD) { VectorSTLIterator solit(sol); return LSQFit::solveLoop(fit, nRank, solit, doSVD); } template Bool LSQaips::solveLoop(uInt &nRank, Vector &sol, Bool doSVD) { VectorSTLIterator solit(sol); return LSQFit::solveLoop(nRank, solit, doSVD); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Fitting/LinearFit.h000066400000000000000000000230771476623553700204170ustar00rootroot00000000000000//# LinearFit.h: Class for linear least-squares fit. //# //# Copyright (C) 1995,1999,2000,2001,2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_LINEARFIT_H #define SCIMATH_LINEARFIT_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // Class for linear least-squares fit. // // // // // // //
      • Functional //
      • Function //
      • Fitting // // // // A set of data point is fit with some functional equation. // The equations solved are linear equations. The functions // themselves however can be wildly nonlinear. // // // // NOTE: Constraints added. Documentation out of date at moment, check // the tLinearFitSVD and tNonLinearFirLM programs for examples. // // The following is a brief summary of the linear least-squares fit problem. // See module header, Fitting, // for a more complete description. // // Given a set of N data points (measurements), (x(i), y(i)) i = 0,...,N-1, // along with a set of standard deviations, sigma(i), for the data points, // and M specified functions, f(j)(x) j = 0,...,M-1, we form a linear // combination of the functions: // // z(i) = a(0)f(0)(x(i)) + a(1)f(1)(x(i)) + ... + a(M-1)f(M-1)(x(i)), // // where a(j) j = 0,...,M-1 are a set of parameters to be determined. // The linear least-squares fit tries to minimize // // chi-square = [(y(0)-z(0))/sigma(0)]^2 + [(y(1)-z(1))/sigma(1)]^2 + ... // + [(y(N-1)-z(N-1))/sigma(N-1)]^2. // // by adjusting {a(j)} in the equation. // // For complex numbers, [(y(i)-z(i))/sigma(i)]^2 in chi-square // is replaced by // [(y(i)-z(i))/sigma(i)]*conjugate([(y(i)-z(i))/sigma(i)]) // // For multidimensional functions, x(i) is a vector, and // // f(j)(x(i)) = f(j)(x(i,0), x(i,1), x(i,2), ...) // // // Normally, it is necessary that N > M for the solutions to be valid, since // there must be more data points than model parameters to be solved. // // If the measurement errors (standard deviation sigma) are not known // at all, they can all be set to one initially. In this case, we assume all // measurements have the same standard deviation, after minimizing // chi-square, we recompute // // sigma^2 = {(y(0)-z(0))^2 + (y(1)-z(1))^2 + ... // + (y(N-1)-z(N-1))^2}/(N-M) = chi-square/(N-M). // // // A statistic weight can also be assigned to each measurement if the // standard deviation is not available. sigma can be calculated from // // sigma = 1/ sqrt(weight) // // Alternatively a 'weight' switch can be set with asWeight(). // For best arithmetic performance, weight should be normalized to a maximum // value of one. Having a large weight value can sometimes lead to overflow // problems. // // The function to be fitted to the data can be given as an instance of the // Function class. // One can also form a sum of functions using the // CompoundFunction. // // For small datasets the usage of the calls is: //
          //
        • Create a functional description of the parameters //
        • Create a fitter: LinearFit fitter(); //
        • Set the functional representation: fitter.setFunction() //
        • Do the fit to the data: fitter.fit(x, data, sigma) // (or do a number of calls to buildNormalMatrix(x, data, sigma) // and finish of with fitter.fit() or fitter.sol()) //
        • if needed the covariance; residuals; chiSquared, parameter errors // can all be obtained //
        // Note that the fitter is reusable. An example is given in the following. // // The solution of a fit always produces the total number of parameters given // to the fitter. I.e. including any parameters that were fixed. In the // latter case the solution returned will be the fixed value. // // //
      • Float //
      • Double //
      • Complex //
      • DComplex // // // If there are a large number of unknowns or a large number of data points // machine memory limits (or timing reasons) may not allow a complete // in-core fitting to be performed. In this case one can incrementally // build the normal equation (see buildNormalMatrix()). // // The normal operation of the class tests for real inversion problems // only. If tests are needed for almost collinear columns in the // solution matrix, the collinearity can be set as the square of the sine of // the minimum angle allowed. // // Singular Value Decomposition is supported by the // LinearFitSVD class, // which has a behaviour completely identical to this class (apart from a // default collinearity of 1e-8). // // Other information (see a.o. LSQFit) can // be set and obtained as well. // // // // The creation of this class was driven by the need to write code // to perform baseline fitting or continuum subtraction. // // //# /// redo example // In the following a polynomial is fitted through the first 20 prime numbers. // The data is given in the x vector (1 to 20) and in the primesTable // (2, 3, ..., 71) (see tLinearFitSVD test program). In the following // all four methods to calculate a polynomial through the data is used // // // The list of coordinate x-values // Vector x(nPrimes); // indgen((Array&)x, 1.0); // 1, 2, ... // Vector primesTable(nPrimes); // for (uInt i=1; i < nPrimes; i++) { // primesTable(i) = // Primes::nextLargerPrimeThan(Int(primesTable(i-1)+0.01)); // }; // Vector sigma(nPrimes); // sigma = 1.0; // // The fitter // LinearFit fitter; // Polynomial > combination(2); // // Get the solution // fitter.setFunction(combination); // Vector solution = fitter.fit(x, primesTable, sigma); // // create a special function (should probably at beginning) // static void myfnc(Vector &y, const Double x) { // y(0) = 1; for (uInt i=1; i xx(nPrimes, 3); // for (uInt i=0; i // In the test program examples are given on how to get the other // information, and other examples. // template class LinearFit : public GenericL2Fit { public: //# Constructors // Create a fitter: the normal way to generate a fitter object. Necessary // data will be deduced from the Functional provided with // setFunction() LinearFit(); // Copy constructor (deep copy) LinearFit(const LinearFit &other); // Assignment (deep copy) LinearFit &operator=(const LinearFit &other); // Destructor virtual ~LinearFit(); //# Member functions protected: //#Data //# Member functions // Generalised fitter virtual Bool fitIt (Vector::BaseType> &sol, const Array::BaseType> &x, const Vector::BaseType> &y, const Vector::BaseType> *const sigma, const Vector *const mask=0); private: //# Data //# Member functions protected: //# Make members of parent classes known. using GenericL2Fit::pCount_p; using GenericL2Fit::ptr_derive_p; using GenericL2Fit::sol_p; using GenericL2Fit::solved_p; using GenericL2Fit::nr_p; using GenericL2Fit::svd_p; using GenericL2Fit::condEq_p; using GenericL2Fit::err_p; using GenericL2Fit::errors_p; using GenericL2Fit::COLLINEARITY; using GenericL2Fit::buildConstraint; using GenericL2Fit::fillSVDConstraints; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Fitting/LinearFit.tcc000066400000000000000000000057351476623553700207420ustar00rootroot00000000000000//# LinearFit.cc: Class for linear least-squares fit. //# //# Copyright (C) 1995,1999,2000,2001,2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_LINEARFIT_TCC #define SCIMATH_LINEARFIT_TCC //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constants template LinearFit::LinearFit() : GenericL2Fit() {} template LinearFit::LinearFit(const LinearFit &other) : GenericL2Fit(other) {} template LinearFit &LinearFit::operator=(const LinearFit &other) { if (this != &other) { GenericL2Fit::operator=(other); } return *this; } template LinearFit::~LinearFit() {} template Bool LinearFit:: fitIt(Vector::BaseType> &sol, const Array::BaseType> &x, const Vector::BaseType> &y, const Vector::BaseType> *const sigma, const Vector *const mask) { // Initialise fitter sol.resize(pCount_p); for (uInt i=0, k=0; imask(i)) sol_p[k++] = sol[i]; } // Build normal equations this->buildMatrix(x, y, sigma, mask); // Build constraint equations buildConstraint(); // Invert normal equations solved_p = this->invert(nr_p, svd_p); // Get solution and errors if (solved_p) { this->solve(condEq_p); sol_p += condEq_p; this->getErrors(err_p); errors_p = True; for (uInt i=0, k=0; imask(i)) sol[i] = sol_p[k++]; (*ptr_derive_p)[i].value() = sol[i]; } } return solved_p; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Fitting/LinearFitSVD.h000066400000000000000000000065321476623553700207710ustar00rootroot00000000000000//# LinearFitSVD.h: Linear fit using Singular Value Decomposition method. //# //# Copyright (C) 1995,1999,2000,2001,2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_LINEARFITSVD_H #define SCIMATH_LINEARFITSVD_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Linear least-squares fit using Singular Value Decomposition method. // // // // // // //
      • LinearFit //
      • Fitting // // // // Solves the linear least-squares fit problem using the singular value // decomposition method. // // // // The operation, calls and results are identical to those for the // LinearFit class. The only difference is a collinearity default of 1e-8 // rather than 0. The actual calculations do a singular value // decomposition solution. A method exists to get the constraints // used in solving for missing rank. // // // // // The creation of this class was driven by the need to provide users with // a reliable least-squares fit method. "Numerical Recipes" recommends that // singular value decomposition (SVD) method be always used for linear // least-squares problems, because of its robustness. // Not everybody agrees with this. // template class LinearFitSVD: public LinearFit { public: //# Constructors // Create a fitter: the normal way to generate a fitter object. Necessary // data will be deduced from the Functional provided with // setFunction() LinearFitSVD(); // Copy constructor (deep copy) LinearFitSVD(const LinearFitSVD &other); // Assignment (deep copy) LinearFitSVD &operator=(const LinearFitSVD &other); // Destructor virtual ~LinearFitSVD(); protected: //# Make members of parent classes known. using LinearFit::svd_p; using LinearFit::COLLINEARITY; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Fitting/LinearFitSVD.tcc000066400000000000000000000035741476623553700213160ustar00rootroot00000000000000//# LinearFitSVD.cc: Linear fit using Singular Value Decomposition. //# Copyright (C) 1995,1999,2000,2001,2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_LINEARFITSVD_TCC #define SCIMATH_LINEARFITSVD_TCC #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template LinearFitSVD::LinearFitSVD() : LinearFit() { svd_p = True; this->set(COLLINEARITY); } template LinearFitSVD::LinearFitSVD(const LinearFitSVD &other) : LinearFit(other) {} template LinearFitSVD &LinearFitSVD::operator=(const LinearFitSVD &other) { if (this != &other) GenericL2Fit::operator=(other); return *this; } template LinearFitSVD::~LinearFitSVD() {} } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Fitting/NonLinearFit.h000066400000000000000000000215611476623553700210660ustar00rootroot00000000000000//# NonLinearFit.h: Class for non-linear least-squares fit. //# Copyright (C) 1994,1995,1996,1999,2000,2001,2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_NONLINEARFIT_H #define SCIMATH_NONLINEARFIT_H //# Includes #include #include namespace casacore { //# begin namesapce casa //# Forward declarations // // Class for non-linear least-squares fit. // // // // // // //
      • Functional //
      • Function //
      • Fitting // // // // A nonlinear function is used to fit a set of data points. // // // // NOTE: Constraints added. Documentation out of date at moment, check // the tLinearFitSVD and tNonLinearFirLM programs for examples. // // The following is a brief summary of the non-linear least-squares fit // problem. // See module header, Fitting, // for a more complete description. // // Given a set of N data points (measurements), (x(i), y(i)) i = 0,...,N-1, // along with a set of standard deviations, sigma(i), for the data points, // and a specified non-linear function, f(x;a) where a = a(j) j = 0,...,M-1 // are a set of parameters to be // determined, the non-linear least-squares fit tries to minimize // // chi-square = [(y(0)-f(x(0);a)/sigma(0)]^2 + [(y(1)-f(x(1);a)/sigma(1)]^2 + // ... + [(y(N-1)-f(x(N-1);a))/sigma(N-1)]^2. // // by adjusting {a(j)} in the equation. // // For multidimensional functions, x(i) is a vector, and // // f(x(i);a) = f(x(i,0), x(i,1), x(i,2), ...;a) // // // If the measurement errors (standard deviation sigma) are not known // at all, they can all be set to one initially. In this case, we assume all // measurements have the same standard deviation, after minimizing // chi-square, we recompute // // sigma^2 = {(y(0)-z(0))^2 + (y(1)-z(1))^2 + ... // + (y(N-1)-z(N-1))^2}/(N-M) = chi-square/(N-M). // // // A statistic weight can be also be assigned to each measurement if the // standard deviation is not available. sigma can be calculated from // // sigma = 1/ sqrt(weight) // // Alternatively a 'weight' switch can be set with asWeight(). // For best arithmetic performance, weight should be normalized to a maximum // value of one. Having a large weight value can sometimes lead to overflow // problems. // // The function to be fitted to the data can be given as an instance of the // Function class. // One can also form a sum of functions using the // CompoundFunction. // // For small datasets the usage of the calls is: //
          //
        • Create a functional description of the parameters //
        • Create a fitter: NonLinearFit fitter(); //
        • Set the functional representation: fitter.setFunction() //
        • Do the fit to the data: fitter.fit(x, data, sigma) // (or do a number of calls to buildNormalMatrix(x, data, sigma) // and finish of with fitter.fit() or fitter.sol()) //
        • if needed the covariance; residuals; chiSquared, parameter errors // can all be obtained //
        // Note that the fitter is reusable. An example is given in the following. // // The solution of a fit always produces the total number of parameters given // to the fitter. I.e. including any parameters that were fixed. In the // latter case the solution returned will be the fixed value. // // //
      • Float //
      • Double //
      • Complex //
      • DComplex // // // If there are a large number of unknowns or a large number of data points // machine memory limits (or timing reasons) may not allow a complete // in-core fitting to be performed. In this case one can incrementally // build the normal equation (see buildNormalMatrix()). // // The normal operation of the class tests for real inversion problems // only. If tests are needed for almost collinear columns in the // solution matrix, the collinearity can be set as the square of the sine of // the minimum angle allowed. // // Singular Value Decomposition is supported by setting the 'svd' switch, // which has a behaviour completely identical to, apart from a // default collinearity check of 1e-8. // // Other information (see a.o. LSQaips) can // be set and obtained as well. // // // // The creation of the class module was driven by the need to write code // to fit Gaussian functions to data points. // // // // template class NonLinearFit : public GenericL2Fit { public: //# Constants // Default maximum number of iterations (30) static const uInt MAXITER = 30; // Default convergence criterium (0.001) static const Double CRITERIUM; //# Constructors // Create a fitter: the normal way to generate a fitter object. Necessary // data will be deduced from the Functional provided with // setFunction(). // Create optionally a fitter with SVD behaviour specified. explicit NonLinearFit(Bool svd=False); // Copy constructor (deep copy) NonLinearFit(const NonLinearFit &other); // Assignment (deep copy) NonLinearFit &operator=(const NonLinearFit &other); // Destructor virtual ~NonLinearFit(); // setMaxIter() sets the maximum number of iterations to do before stopping. // Default value is 30. void setMaxIter(uInt maxIter=MAXITER); // getMaxIter() queries what the maximum number of iterations currently is uInt getMaxIter() const { return maxiter_p; }; // currentIteration() queries what the current iteration is uInt currentIteration() const { return maxiter_p - curiter_p; }; // setCriteria() sets the convergence criteria. The actual value and // its interpretation depends on the derived class used to do the // actual iteration. Default value is 0.001. void setCriteria(const Double criteria=CRITERIUM) {criterium_p = criteria; }; // getCriteria() queries the current criteria Double getCriteria() const { return criterium_p; }; // Check to see if the fit has converged Bool converged() const { return converge_p; }; protected: //#Data // Maximum number of iterations uInt maxiter_p; // Current iteration number uInt curiter_p; // Convergence criteria Double criterium_p; // Has fit converged Bool converge_p; //# Member functions // Generalised fitter virtual Bool fitIt (Vector::BaseType> &sol, const Array::BaseType> &x, const Vector::BaseType> &y, const Vector::BaseType> *const sigma, const Vector *const mask=0) = 0; private: //# Data //# Member functions protected: //# Make members of parent classes known. using GenericL2Fit::pCount_p; using GenericL2Fit::ptr_derive_p; using GenericL2Fit::arg_p; using GenericL2Fit::sol_p; using GenericL2Fit::fsol_p; using GenericL2Fit::solved_p; using GenericL2Fit::nr_p; using GenericL2Fit::svd_p; using GenericL2Fit::condEq_p; using GenericL2Fit::fullEq_p; using GenericL2Fit::err_p; using GenericL2Fit::ferr_p; using GenericL2Fit::errors_p; using GenericL2Fit::valder_p; using GenericL2Fit::set; using GenericL2Fit::buildConstraint; using GenericL2Fit::fillSVDConstraints; using GenericL2Fit::isReady; }; } //# End namespace casacore #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Fitting/NonLinearFit.tcc000066400000000000000000000056301476623553700214070ustar00rootroot00000000000000//# NonLinearFit.cc: Class for non-linear least-squares fit. //# Copyright (C) 1995,1999,2000,2001,2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_NONLINEARFIT_TCC #define SCIMATH_NONLINEARFIT_TCC #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constants // Default convergence criterium template const Double NonLinearFit::CRITERIUM = 0.001; //# Constructors template NonLinearFit::NonLinearFit(Bool svd) : GenericL2Fit(), maxiter_p(MAXITER), curiter_p(MAXITER), criterium_p(CRITERIUM), converge_p(False) { svd_p = svd; if (!svd_p) set(0.0); } template NonLinearFit::NonLinearFit(const NonLinearFit &other) : GenericL2Fit(other), maxiter_p(other.maxiter_p), curiter_p(other.curiter_p), criterium_p(other.criterium_p), converge_p(other.converge_p) { if (other.ptr_derive_p) ptr_derive_p = other.ptr_derive_p->clone(); condEq_p = other.condEq_p; fullEq_p = other.fullEq_p; arg_p = other.arg_p; sol_p = other.sol_p; fsol_p = other.fsol_p; err_p = other.err_p; ferr_p = other.ferr_p; valder_p =other.valder_p; } template NonLinearFit &NonLinearFit::operator=(const NonLinearFit &other) { if (this != &other) { GenericL2Fit::operator=(other); maxiter_p = other.maxiter_p; curiter_p = other.curiter_p; criterium_p = other.criterium_p; converge_p = other.converge_p; } return *this; } template NonLinearFit::~NonLinearFit() {} template void NonLinearFit::setMaxIter(uInt maxIter) { maxiter_p = (maxIter > 0 ? maxIter : 1); curiter_p = (curiter_p > maxiter_p ? maxiter_p : curiter_p); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Fitting/NonLinearFitLM.h000066400000000000000000000112321476623553700213110ustar00rootroot00000000000000//# NonLinearFitLM.h: Solve non-linear fit using Levenberg-Marquardt method. //# Copyright (C) 1995,1999-2002,2004,2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_NONLINEARFITLM_H #define SCIMATH_NONLINEARFITLM_H //# Includes #include #include namespace casacore { //# begin namespace casa //# Forward declarations // // // Solve non-linear fit with Levenberg-Marquardt method. // // // // // // //
      • NonLinearFit //
      • Fitting // // // // This class uses the Levenberg-Marquardt method to solve the non-linear // least-squares fit problem hence NonLinearFitLM // // // // NOTE: Constraints added. Documentation out of date at moment, check // the tLinearFitSVD and tNonLinearFirLM programs for examples. // // See the NonLinearFit class for a // general description. // // This class is derived from the general NonLinearFit class. It does // a non-linear least-squares fit using the Levenberg-Marquardt method. // // See Numerical Recipes for more information // on the Levenberg-Marquardt method. // // // //
      • Float //
      • Double //
      • Complex //
      • DComplex // // // // Levenberg-Marquardt method is a standard method for non-linear // least-squares fits. It works well in practice over a wide range of // problems. // // // // template class NonLinearFitLM : public NonLinearFit { public: //# Constructors // Create a fitter: the normal way to generate a fitter object. Necessary // data will be deduced from the Functional provided with // setFunction(). // Optionally, a fitter with SVD behaviour explicit NonLinearFitLM(Bool svd=False); // Copy constructor (deep copy) NonLinearFitLM(const NonLinearFitLM &other); // Assignment (deep copy) NonLinearFitLM &operator=(const NonLinearFitLM &other); // Destructor virtual ~NonLinearFitLM(); protected: //# Member functions // Generalised fitter virtual Bool fitIt (Vector::BaseType> &sol, const Array::BaseType> &x, const Vector::BaseType> &y, const Vector::BaseType> *const sigma, const Vector *const mask=0); private: //# Data // The parameter that makes this the Levenberg-Marquardt method. Double lamda_p; // The current fit state Double fitit_p; protected: //# Make members of parent classes known. using NonLinearFit::curiter_p; using NonLinearFit::maxiter_p; using NonLinearFit::converge_p; using NonLinearFit::pCount_p; using NonLinearFit::ptr_derive_p; using NonLinearFit::sol_p; using NonLinearFit::solved_p; using NonLinearFit::nr_p; using NonLinearFit::svd_p; using NonLinearFit::condEq_p; using NonLinearFit::err_p; using NonLinearFit::errors_p; using NonLinearFit::valder_p; using NonLinearFit::buildConstraint; using NonLinearFit::setMaskedParameterValues; using NonLinearFit::fillSVDConstraints; using NonLinearFit::isReady; }; } //# End namespace casacore #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Fitting/NonLinearFitLM.tcc000066400000000000000000000071651476623553700216450ustar00rootroot00000000000000//# NonLinearFitLM.cc: Solve non-linear fit using Levenberg-Marquardt method. //# Copyright (C) 1995,1999-2002,2004,2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_NONLINEARFITLM_TCC #define SCIMATH_NONLINEARFITLM_TCC //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template NonLinearFitLM::NonLinearFitLM(Bool svd) : NonLinearFit(svd), lamda_p(0.001) {} template NonLinearFitLM::NonLinearFitLM(const NonLinearFitLM &other) : NonLinearFit(other), lamda_p(other.lamda_p) {} template NonLinearFitLM &NonLinearFitLM::operator=(const NonLinearFitLM &other) { if (this != &other) { NonLinearFit::operator=(other); lamda_p = other.lamda_p; } return *this; } template NonLinearFitLM::~NonLinearFitLM() {} template Bool NonLinearFitLM:: fitIt(Vector::BaseType> &sol, const Array::BaseType> &x, const Vector::BaseType> &y, const Vector::BaseType> *const sigma, const Vector *const mask) { // Initialise loops curiter_p = maxiter_p; converge_p = False; // Initialise fitter sol.resize(pCount_p); for (uInt i=0, k=0; imask(i)) sol_p[k++] = sol[i]; } // And loop while (curiter_p > 0 && (!this->isReady() || curiter_p == maxiter_p)) { setMaskedParameterValues(sol_p); // Build normal equations this->buildMatrix(x, y, sigma, mask); // Build constraint equations buildConstraint(); // Do an LM loop VectorSTLIterator::BaseType> csolit(sol_p); if (!this->solveLoop(nr_p, csolit)) { throw(AipsError("NonLinearFitLM: error in loop solution")); } curiter_p--; } converge_p = curiter_p; solved_p = True; // Solve last time setMaskedParameterValues(sol_p); this->buildMatrix(x, y, sigma, mask); buildConstraint(); this->invert(nr_p, True); this->solve(condEq_p); sol_p += condEq_p; this->getErrors(err_p); errors_p = True; for (uInt i=0, k=0; imask(i)) sol[i] = sol_p[k++]; (*ptr_derive_p)[i].value() = sol[i]; } solved_p = converge_p; return converge_p; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Fitting/test/000077500000000000000000000000001476623553700173375ustar00rootroot00000000000000casacore-3.7.1/scimath/Fitting/test/CMakeLists.txt000066400000000000000000000005521476623553700221010ustar00rootroot00000000000000set (tests dConstraints dLSQFit tFitGaussian tLinearFitSVD tLSQaips tLSQFit tNonLinearFitLM ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_scimath) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/scimath/Fitting/test/dConstraints.cc000066400000000000000000000151311476623553700223220ustar00rootroot00000000000000//# dConstraints.cc.cc: Demo nonlinear least squares classes with constraints //# Copyright (C) 2004 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main (int argc, const char* argv[]) { uChar tp = '0'; // # of constraints uChar ft = '0'; // Function type (compiled or compound) if (argc>1) tp = argv[1][0]; if (argc>2) ft = argv[2][0]; NonLinearFitLM fitter; Vector solution; Double newChiSquare; const uInt n = 100; Vector x(n); for (uInt i=0; i y(n); Vector sigma(n, 1.0); Matrix z(n,2); Vector constrArg(7, 0.0); MLCG generator; Normal noise(&generator, 0.0, 0.3); sigma = 1.0; fitter.setMaxIter(100); // Set converge criteria. Default is 0.001 ///fitter.setCriteria(0.0001); // Function Function, AutoDiff > *gauss; cout << endl << "****** Fit a double 1D gaussian function *****" << endl; cout << "Run program as " << endl << "dConstraints x y" << endl << "with x = # of constraints ([0],1,2,3)," << endl << " y = compiled/compound function ([0]/1)" << endl; // Make some fake data sets // 20.0 * exp (-((x-10)/4)^2) + 10.0 * exp (-((x-33)/4)^2) + 10 Double v[7] = {20, 10, 4, 10, 33, 4, 10}; // Must give an initial guess for the set of fitted parameters. Double vi[7] = {22, 11, 5, 10, 30, 5, 9}; // Select compiled or Gaussian1Ds switch (ft) { case '1': { v[2] = v[5] = 4.0*sqrt(log(16.0)); vi[2] = vi[5] = 5.0*sqrt(log(16.0)); gauss = new CompoundFunction >; Gaussian1D > g1; Gaussian1D > g2; Polynomial > p1(0); dynamic_cast > *>(gauss) ->addFunction(g1); dynamic_cast > *>(gauss) ->addFunction(g2); dynamic_cast > *>(gauss) ->addFunction(p1); /// for (uInt i=0; i<7; ++i) (*gauss)[i] = AutoDiff(v[i],7,i); cout << "Using a Compound of Gaussians and Polynomial" << endl; } break; default: { gauss = new CompiledFunction >; dynamic_cast > *>(gauss) ->setFunction("p6+p0*exp(-((x-p1)/p2)^2) + p3*exp(-((x-p4)/p5)^2)"); ///for (uInt i=0; i<7; ++i) (*gauss)[i] = v[i]; cout << "Using a Compiled string function" << endl; } break; } for (uInt i=0; i<7; ++i) (*gauss)[i] = AutoDiff(v[i],7,i); for (uInt i=0; i solution = fitter.fit(x, y, sigma); // compute new chi-square for the solution newChiSquare = fitter.chiSquare(); // Show data cout << "# solutions, parameters, constraints: " << solution.nelements() << ", " << gauss->nparameters() << ", " << fitter.nConstraints() << endl; cout << "Converged after " << fitter.currentIteration() <<" iterations" <nparameters()-1; ++i) cout << vi[i] << ", "; cout << vi[gauss->nparameters()-1] << "]" << endl; cout << "Solution for fitted parameters:" << endl << solution < covariance = fitter.compuCovariance(); Vector errors = fitter.errors(); // Compare solution with gauss1 parameters for (uInt i=0; iparameters().nMaskedParameters(); i++) { cout << "Expected, Computed Parameter " << v[i]; cout << ", " << solution[i] << " Std Dev " << errors[i] << endl; } delete gauss; cout << "---------------------------------------------------" << endl; } catch (std::exception& x) { cout << x.what() << endl; } return 0; } casacore-3.7.1/scimath/Fitting/test/dLSQFit.cc000066400000000000000000000126161476623553700211220ustar00rootroot00000000000000//# dLSQBase.cc -- LSQ demonstration //# Copyright (C) 1999,2000,2001,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include void showdt(const LSQFit &lsq, Int n) { uInt nun, np, ncon, ner, rk, *piv; Double *norm, *known, *constr, *err, *sEq, *sol, prec, nonlin; lsq.debugIt(nun, np, ncon, ner, rk, norm, known, constr, err, piv, sEq, sol, prec, nonlin); if (norm) { for (Int i=0; i<(n*(n+1))/2; i++) { std::cout << " Norm" << i << ": " << norm[i]; } } std::cout << std::endl; if (err) { for (Int i1=0; i1<4; i1++) { std::cout << " Erra" << i1 << ": " << err[i1]; } } std::cout << std::endl; if (known) { for (Int i2=0; i2 #include #include #include #include #include #include void printfparameters(Function &f); void printparameters(Matrix &m); void createdata(casacore::Matrix &pos, casacore::Vector &f, Float range, uInt n, casacore::Matrix &components); Int ipow(Int base, uInt power); int main() { Bool fail = 0; casacore::Matrix pos; casacore::Vector f; casacore::Matrix components; casacore::Matrix estimate; casacore::Matrix retryfactors; casacore::Matrix solution; casacore::Matrix errors; FitGaussian fitgauss; cout << "TEST 1: 1 Gaussian in 1 Dimension." << endl; fitgauss.setDimensions(1); fitgauss.setNumGaussians(1); components.resize(1,3); components(0,0) = 5; components(0,1) = 2; components(0,2) = 4; createdata(pos, f, 10.0, 11, components); estimate.resize(1, 3); estimate(0,0) = 1; estimate(0,1) = 1; estimate(0,2) = 1; fitgauss.setFirstEstimate(estimate); try { solution = fitgauss.fit(pos, f); } catch (std::exception& err) { cout << "ERROR: " << err.what() << endl; fail = 1; } cout << " Given Parameters:"; printparameters(components); cout << "Solution Parameters:"; printparameters(solution); solution = fitgauss.solution(); cout << "Internal Parameters:"; printparameters(solution); errors = fitgauss.errors(); cout << "Error in Parameters::"; printparameters(errors); if (max(errors) > 1e-15) { cout << "Max error" << max(errors) << endl; fail = 1; } cout << endl << "TEST 2: 1 Gaussian in 2 Dimensions" << endl; fitgauss.setDimensions(2); fitgauss.setNumGaussians(1); components.resize(1,2*3); components(0,0) = 3; components(0,1) = -1; components(0,2) = 1; components(0,3) = 3; components(0,4) = 0.5; components(0,5) = 1; createdata(pos, f, 4.0, 9, components); estimate.resize(1,6); estimate(0,0) = 1; estimate(0,1) = 0; estimate(0,2) = 0; estimate(0,3) = 1; estimate(0,4) = 0.5; estimate(0,5) = 1; fitgauss.setFirstEstimate(estimate); solution.resize(); errors.resize(); try { solution = fitgauss.fit(pos, f); } catch (std::exception& err) { cout << "ERROR: " << err.what() << endl; fail = 1; } cout << " Given Parameters:"; printparameters(components); cout << "Solution Parameters:"; printparameters(solution); solution = fitgauss.solution(); cout << "Internal Parameters:"; printparameters(solution); errors = fitgauss.errors(); cout << "Error in Parameters:"; printparameters(errors); if (max(errors) > 1e-15) { cout << "Max error" << max(errors) << endl; fail = 1; } cout << endl << "TEST 3: 2 Gaussians in 2 Dimensions" << endl; fitgauss.setDimensions(2); fitgauss.setNumGaussians(2); components.resize(2,6); components(0,0) = 3; components(0,1) = 1; components(0,2) = 1; components(0,3) = 2.2; components(0,4) = 0.85; components(0,5) = 0.25; components(1,0) = 3; components(1,1) = -2; components(1,2) = -2; components(1,3) = 2.5; components(1,4) = 0.75; components(1,5) = 2.9; createdata(pos, f, 4, 9, components); estimate.resize(2,6); estimate(0,0) = 1; estimate(0,1) = 1; estimate(0,2) = 1; estimate(0,3) = 1; estimate(0,4) = 0.5; estimate(0,5) = 1; estimate(1,0) = 1; estimate(1,1) = -2; estimate(1,2) = -2; estimate(1,3) = 1; estimate(1,4) = 0.5; estimate(1,5) = 1; fitgauss.setFirstEstimate(estimate); retryfactors.resize(2,6); retryfactors(0,0) = 2; retryfactors(0,1) = 0; retryfactors(0,2) = 2; retryfactors(0,3) = 2; retryfactors(0,4) = 1.1; retryfactors(0,5) = 0; retryfactors(1,0) = 1.5; retryfactors(1,1) = 0; retryfactors(1,2) = 1.5; retryfactors(1,3) = 1.5; retryfactors(1,4) = 1.2; retryfactors(1,5) = 0; fitgauss.setRetryFactors(retryfactors); solution.resize(); errors.resize(); try { solution = fitgauss.fit(pos, f); } catch (std::exception& err) { cout << "ERROR: " << err.what() << endl; fail = 1; } cout << " Given Parameters:"; printparameters(components); cout << "Solution Parameters:"; printparameters(solution); solution = fitgauss.solution(); cout << "Internal Parameters:"; printparameters(solution); errors = fitgauss.errors(); cout << "Error in Parameters:"; printparameters(errors); if (max(errors) > 1e-15) { cout << "Max error" << max(errors) << endl; fail = 1; } cout << endl << "TEST 4: 1 Gaussian in 3 Dimensions" << endl; components.resize(1,9); components(0,0) = 3; components(0,1) = -1; components(0,2) = 1; components(0,3) = 1; components(0,4) = 1.5; components(0,5) = 1.0; components(0,6) = 2.0; components(0,7) = 0.7; components(0,8) = -0.4; createdata(pos, f, 2.0, 5, components); fitgauss.setDimensions(3); fitgauss.setNumGaussians(1); estimate.resize(1,9); estimate(0,0) = 1; estimate(0,1) = -1; estimate(0,2) = 1; estimate(0,3) = 0; estimate(0,4) = 1.1; estimate(0,5) = 1.5; estimate(0,6) = 1.8; estimate(0,7) = -0.7; estimate(0,8) = 0; fitgauss.setFirstEstimate(estimate); solution.resize(); errors.resize(); try { solution = fitgauss.fit(pos, f, 0.001); } catch (std::exception& err) { cout << "ERROR: " << err.what() << endl; fail = 1; } cout << " Given Parameters:"; printparameters(components); cout << "Solution Parameters:"; printparameters(solution); solution = fitgauss.solution(); cout << "Internal Parameters:"; printparameters(solution); errors = fitgauss.errors(); cout << "Error in Parameters:"; printparameters(errors); if (max(errors) > 1e-15) { cout << "Max error" << max(errors) << endl; fail = 1; } cout << endl << "TEST 5: 3 Gaussians in 3 Dimensions" << endl; fitgauss.setDimensions(3); fitgauss.setNumGaussians(3); components.resize(3,9); components(0,0) = 3; components(0,1) = 0; components(0,2) = 0; components(0,3) = 1; components(0,4) = 2; components(0,5) = 1.5; components(0,6) = 3; components(0,7) = 0.3; components(0,8) = 0.1; components(1,0) = 2.5; components(1,1) = -2; components(1,2) = -2; components(1,3) = -1; components(1,4) = 3; components(1,5) = 2.6; components(1,6) = 1.8; components(1,7) = 0.5; components(1,8) = -0.5; components(2,0) = 2.1; components(2,1) = 2; components(2,2) = 2; components(2,3) = -2; components(2,4) = 3; components(2,5) = 3.5; components(2,6) = 2; components(2,7) = 0; components(2,8) = 0; createdata(pos, f, 3.0, 7, components); estimate.resize(3,9); estimate(0,0) = 3; estimate(0,1) = 0; estimate(0,2) = 0; estimate(0,3) = 1; estimate(0,4) = 2.5; estimate(0,5) = 2.2; estimate(0,6) = 3; estimate(0,7) = 0; estimate(0,8) = 0; estimate(1,0) = 2.5; estimate(1,1) = -2; estimate(1,2) = -2; estimate(1,3) = -1; estimate(1,4) = 1.2; estimate(1,5) = 2.2; estimate(1,6) = 3; estimate(1,7) = 0; estimate(1,8) = 0; estimate(2,0) = 2.1; estimate(2,1) = 2; estimate(2,2) = 2; estimate(2,3) = -2; estimate(2,4) = 1.2; estimate(2,5) = 2.2; estimate(2,6) = 3; estimate(2,7) = 0; estimate(2,8) = 0; fitgauss.setFirstEstimate(estimate); fitgauss.setMaxRetries(6); fitgauss.setMaxTime(120.0); solution.resize(); errors.resize(); try { solution = fitgauss.fit(pos, f, 0.01, 256); } catch (std::exception& err) { cout << "ERROR: " << err.what() << endl; fail = 1; } // I'm only testing one of these if (solution(0,1) != fitgauss.solution()(0,1)) fail = 1; cout << " Given Parameters:"; printparameters(components); cout << "Solution Parameters:"; printparameters(solution); solution = fitgauss.solution(); cout << "Internal Parameters:"; printparameters(solution); errors = fitgauss.errors(); cout << "Error in Parameters:"; printparameters(errors); if (max(errors) > 1e-15) { cout << "Max error" << max(errors) << endl; fail = 1; } return fail; } void createdata(casacore::Matrix &pos, casacore::Vector &f, Float range, uInt n, casacore::Matrix &components) { uInt i = 0; uInt dim = components.ncolumn() / 3; uInt imax = ipow(n,dim); pos.resize(imax,dim); f.resize(imax); //set up functions Block > datagauss1d((dim==1) * components.nrow()); Block > datagauss2d((dim==2) * components.nrow()); Block > datagauss3d((dim==3) * components.nrow()); for (uInt g = 0; g < components.nrow(); g++) for (uInt p = 0; p < components.ncolumn(); p++) { if (dim==1) datagauss1d[g][p] = components(g,p); if (dim==2) datagauss2d[g][p] = components(g,p); if (dim==3) datagauss3d[g][p] = components(g,p); } //create the data casacore::Vector curpos(dim); curpos = -range; Float inc = 2.0 * range / (n-1); while(i < imax) { f(i) = 0; for (uInt g = 0; g < components.nrow(); g++) { if (dim==1) f(i) += datagauss1d[g](curpos); if (dim==2) f(i) += datagauss2d[g](curpos); if (dim==3) f(i) += datagauss3d[g](curpos(0), curpos(1), curpos(2)); //! } pos.row(i) = curpos; //cout << i << ") " << curpos << " = " << f(i) << endl; curpos(dim-1) += inc; for (uInt a = dim-1; a > 0; a--) if (curpos(a) >= range + inc*0.1) {curpos(a) = -range; curpos(a-1) += inc;} i++; } } void printfparameters(Function &f) { uInt p; for (p = 0; p < f.nparameters() - 1; p++) cout << f[p] << ", "; cout << f[p] << endl; } void printparameters(Matrix &m) { cout.precision(3); uInt g,p; for (g = 0; g < m.nrow(); g++) { for (p = 0; p < m.ncolumn() - 1; p++) cout << m(g,p) << ", "; cout << m(g,p) << endl; if (g < m.nrow() - 1) cout << " "; } } Int ipow(Int base, uInt power) { Int ans = 1; while (power--) ans *= base; return ans; } // Relic //retryfactors.resize(1,9); //retryfactors(0,0) = 1; retryfactors(0,1) = 0; retryfactors(0,2) = 0; //retryfactors(0,3) = 0; retryfactors(0,4) = 3; retryfactors(0,5) = 1; //retryfactors(0,6) = 2; retryfactors(0,7) = 0; retryfactors(0,8) = 0; //fitgauss.setRetryFactors(retryfactors); casacore-3.7.1/scimath/Fitting/test/tLSQFit.cc000066400000000000000000001270661476623553700211500ustar00rootroot00000000000000//# tLSQFit.cc -- test LSQFit //# Copyright (C) 1999-2002,2004-2006,2008 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include Double Y(const Double x, const Double y=3e-15) { return (abs(x) < y) ? 0 : x; } Float Y(const Float x, const Double y=3e-15) { return (abs(x) < y) ? 0 : x; } DComplex Y(const DComplex x, const Double y=4e-15) { return DComplex(Y(real(x), y), Y(imag(x), y)); } Complex Y(const Complex x, const Double y=4e-15) { return Complex(Y(real(x), y), Y(imag(x), y)); } void showdt(const LSQFit &lsq) { uInt nun, np, ncon, ner, rank; Double *norm, *known, *constr, *err, *sEq, *sol; uInt *piv; Double prec, nonlin; lsq.debugIt(nun, np, ncon, ner, rank, norm, known, constr, err, piv, sEq, sol, prec, nonlin); cout << "nun, np, ncon, ner, rank: " << nun << ", " << np << ", " << ncon << ", " << ner << ", " << rank << endl; cout << "collinearity, factor-1: " << prec << ", " << nonlin << endl; cout << "Norm"; if (norm) { Int i00=0; for (uInt i=0; i()); lsq5.toAipsIO (aio); LSQFit lsq6; aio.setpos (0); lsq6.fromAipsIO (aio); cout << "Invert = " << lsq6.invert(nr1); cout << ", rank=" << nr1 << endl; lsq6.solve(sol1); sd1 = lsq6.getSD(); mu1 = lsq6.getWeightedSD(); for (uInt i=0; i<6; i++) { cout << "Sol" << i << ": " << Y(sol1[i],1e-12) << ", " << Y(sd1, 1e-5) << ", " << Y(mu1, 1e-5) << endl; } aio.setpos (0); lsq6.fromAipsIO (aio); cout << "Invert = " << lsq6.invert(nr1); cout << ", rank=" << nr1 << endl; lsq6.solve(sol1); sd1 = lsq6.getSD(); mu1 = lsq6.getWeightedSD(); for (uInt i=0; i<6; i++) { cout << "Sol" << i << ": " << Y(sol1[i],1e-12) << ", " << Y(sd1, 1e-5) << ", " << Y(mu1, 1e-5) << endl; } } cout << "---------------------------------------------------" << endl; cout << "Real -- 6 unknowns --- float --- merged ---" << endl; { LSQFit lsq5(6); LSQFit lsq5a(6); for (Int j0=0; j0<511; j0++) { val1f[0] = 1; for (uInt j1=1; j1<6; j1++) val1f[j1] = val1f[j1-1]*j0; if (j0<300) lsq5.makeNorm(val1f, 1.0f, val12f[j0]); else lsq5a.makeNorm(val1f, 1.0f, val12f[j0]); } val1f[0] = 1; for (uInt j1=1; j1<6; j1++) val1f[j1] = val1f[j1-1]*511; lsq5.makeNorm(val1f, 1.0f, val12f[511]); cout << "Merge = " << lsq5.merge(lsq5a); cout << ", Invert = " << lsq5.invert(nr1); cout << ", rank=" << nr1 << endl; lsq5.solve(sol1); sd1 = lsq5.getSD(); mu1 = lsq5.getWeightedSD(); for (uInt i=0; i<6; i++) { cout << "Sol" << i << ": " << Y(sol1[i],1e-12) << ", " << Y(sd1, 1e-5) << ", " << Y(mu1, 1e-5) << endl; } } cout << "---------------------------------------------------" << endl; cout << "Real -- 6 unknowns --- float --- merged --- indexed ---" << endl; { LSQFit lsq5(6); LSQFit lsq5a(6); uInt ixa[6] = {0,4,3,2,5,1}; uInt ixreva[6] = {0,5,3,2,1,4}; uInt *ix =ixa; uInt *ixrev = ixreva; for (Int j0=0; j0<511; j0++) { if (j0<300) { val1f[0] = 1; for (uInt j1=1; j1<6; j1++) val1f[j1] = val1f[j1-1]*j0; lsq5.makeNorm(val1f, 1.0f, val12f[j0]); } else { val1f[ixrev[0]] = 1; for (uInt j1=1; j1<6; j1++) val1f[ixrev[j1]] = val1f[ixrev[j1-1]]*j0; lsq5a.makeNorm(val1f, 1.0f, val12f[j0]); } } val1f[0] = 1; for (uInt j1=1; j1<6; j1++) val1f[j1] = val1f[j1-1]*511; lsq5.makeNorm(val1f, 1.0f, val12f[511]); cout << "Merge = " << lsq5.merge(lsq5a, 6, ix); cout << ", Invert = " << lsq5.invert(nr1); cout << ", rank=" << nr1 << endl; lsq5.solve(sol1); sd1 = lsq5.getSD(); mu1 = lsq5.getWeightedSD(); for (uInt i=0; i<6; i++) { cout << "Sol" << i << ": " << Y(sol1[i],1e-12) << ", " << Y(sd1, 1e-5) << ", " << Y(mu1, 1e-5) << endl; } } cout << "---------------------------------------------------" << endl; cout << "Real -- 6 unknowns --- float --- merged --- indexed[2] -" << endl; { LSQFit lsq5(6); LSQFit lsq5a(6); uInt ixa[6] = {0,4,3,2,5,1}; uInt ixreva[6] = {0,5,3,2,1,4}; uInt *ixrev = ixreva; std::vector ixv; std::vector ixrevv; for (uInt i=0; i<6; ++i) { ixv.push_back(ixa[i]); ixrevv.push_back(ixreva[i]); } for (Int j0=0; j0<511; j0++) { if (j0<300) { val1f[0] = 1; for (uInt j1=1; j1<6; j1++) val1f[j1] = val1f[j1-1]*j0; lsq5.makeNorm(val1f, 1.0f, val12f[j0]); } else { val1f[ixrev[0]] = 1; for (uInt j1=1; j1<6; j1++) val1f[ixrev[j1]] = val1f[ixrev[j1-1]]*j0; lsq5a.makeNorm(val1f, 1.0f, val12f[j0]); } } val1f[0] = 1; for (uInt j1=1; j1<6; j1++) val1f[j1] = val1f[j1-1]*511; lsq5.makeNorm(val1f, 1.0f, val12f[511]); cout << "Merge = " << lsq5.merge(lsq5a, 6, ixv); cout << ", Invert = " << lsq5.invert(nr1); cout << ", rank=" << nr1 << endl; lsq5.solve(sol1); sd1 = lsq5.getSD(); mu1 = lsq5.getWeightedSD(); for (uInt i=0; i<6; i++) { cout << "Sol" << i << ": " << Y(sol1[i],1e-12) << ", " << Y(sd1, 1e-5) << ", " << Y(mu1, 1e-5) << endl; } } cout << "---------------------------------------------------" << endl; cout << "Real -- 6 unknowns --- float ------- indexed ---" << endl; { LSQFit lsq5(6); uInt ixa[6] = {0,4,3,2,5,1}; uInt ixreva[6] = {0,5,3,2,1,4}; uInt *ix =ixa; uInt *ixrev = ixreva; for (Int j0=0; j0<511; j0++) { val1f[ixrev[0]] = 1; for (uInt j1=1; j1<6; j1++) val1f[ixrev[j1]] = val1f[ixrev[j1-1]]*j0; lsq5.makeNorm(6, ix, val1f, 1.0f, val12f[j0]); } val1f[0] = 1; for (uInt j1=1; j1<6; j1++) val1f[j1] = val1f[j1-1]*511; lsq5.makeNorm(val1f, 1.0f, val12f[511]); cout << "Invert = " << lsq5.invert(nr1); cout << ", rank=" << nr1 << endl; lsq5.solve(sol1); sd1 = lsq5.getSD(); mu1 = lsq5.getWeightedSD(); for (uInt i=0; i<6; i++) { cout << "Sol" << i << ": " << Y(sol1[i],1e-12) << ", " << Y(sd1, 1e-5) << ", " << Y(mu1, 1e-5) << endl; } } cout << "---------------------------------------------------" << endl; cout << "Real -- 6 unknowns --- float ------- paired ---" << endl; { LSQFit lsq5(6); uInt ixreva[6] = {0,5,3,2,1,4}; uInt *ixrev = ixreva; std::vector > valpf(6); for (Int j0=0; j0<511; ++j0) { val1f[ixrev[0]] = 1; for (uInt j1=1; j1<6; ++j1) val1f[ixrev[j1]] = val1f[ixrev[j1-1]]*j0; for (uInt j1=0; j1<6; ++j1) valpf[j1] = std::make_pair(j1, val1f[ixrev[j1]]); lsq5.makeNorm(valpf, 1.0f, val12f[j0]); } val1f[0] = 1; for (uInt j1=1; j1<6; j1++) val1f[j1] = val1f[j1-1]*511; lsq5.makeNorm(val1f, 1.0f, val12f[511]); cout << "Invert = " << lsq5.invert(nr1); cout << ", rank=" << nr1 << endl; lsq5.solve(sol1); sd1 = lsq5.getSD(); mu1 = lsq5.getWeightedSD(); for (uInt i=0; i<6; i++) { cout << "Sol" << i << ": " << Y(sol1[i],1e-12) << ", " << Y(sd1, 1e-5) << ", " << Y(mu1, 1e-5) << endl; } } cout << "---------------------------------------------------" << endl; cout << "Real -- 6 unknowns --- float ---- sorted index ---" << endl; { LSQFit lsq5(6); uInt ixa[6] = {0,4,3,2,5,1}; uInt ixreva[6] = {0,5,3,2,1,4}; uInt *ixrev = ixreva; uInt *ix = ixa; Float valpf[6]; Float *valsf = valpf; for (Int j0=0; j0<511; ++j0) { val1f[ixrev[0]] = 1; for (uInt j1=1; j1<6; ++j1) val1f[ixrev[j1]] = val1f[ixrev[j1-1]]*j0; for (uInt j1=0; j1<6; ++j1) { ix[j1] = j1; valpf[j1] = val1f[ixrev[j1]]; } lsq5.makeNormSorted(6, ix, valsf, valsf, 1.0f, val12f[j0], val12f[j0]); } val1f[0] = 1; for (uInt j1=1; j1<6; j1++) val1f[j1] = val1f[j1-1]*511; lsq5.makeNorm(val1f, 1.0f, val12f[511]); val1f[0] = 1; for (uInt j1=1; j1<6; j1++) val1f[j1] = val1f[j1-1]*511; lsq5.makeNorm(val1f, 1.0f, val12f[511]); cout << "Invert = " << lsq5.invert(nr1); cout << ", rank=" << nr1 << endl; lsq5.solve(sol1); sd1 = lsq5.getSD(); mu1 = lsq5.getWeightedSD(); for (uInt i=0; i<6; i++) { cout << "Sol" << i << ": " << Y(sol1[i],1e-12) << ", " << Y(sd1, 1e-5) << ", " << Y(mu1, 1e-5) << endl; } } cout << "---------------------------------------------------" << endl; cout << "Real -- 6 unknowns --- float ---- unsorted index-2- ---" << endl; { LSQFit lsq5(6); uInt ixa[6] = {0,4,3,2,5,1}; uInt ixreva[6] = {0,5,3,2,1,4}; uInt *ixrev = ixreva; uInt *ix = ixa; for (Int j0=0; j0<511; ++j0) { val1f[ixrev[0]] = 1; for (uInt j1=1; j1<6; ++j1) val1f[ixrev[j1]] = val1f[ixrev[j1-1]]*j0; lsq5.makeNorm(6, ix, val1f, val1f, 1.0f, val12f[j0], val12f[j0]); } val1f[0] = 1; for (uInt j1=1; j1<6; j1++) val1f[j1] = val1f[j1-1]*511; lsq5.makeNorm(val1f, 1.0f, val12f[511]); val1f[0] = 1; for (uInt j1=1; j1<6; j1++) val1f[j1] = val1f[j1-1]*511; lsq5.makeNorm(val1f, 1.0f, val12f[511]); cout << "Invert = " << lsq5.invert(nr1); cout << ", rank=" << nr1 << endl; lsq5.solve(sol1); sd1 = lsq5.getSD(); mu1 = lsq5.getWeightedSD(); for (uInt i=0; i<6; i++) { cout << "Sol" << i << ": " << Y(sol1[i],1e-12) << ", " << Y(sd1, 1e-5) << ", " << Y(mu1, 1e-5) << endl; } } cout << "---------------------------------------------------" << endl; cout << "Real -- 6 unknowns --- set --------" << endl; { LSQFit lsq5; lsq5.set(6,LSQReal()); lsq5.set(1e-8); for (Int j0=0; j0<512; j0++) { val1[0] = 1; for (uInt j1=1; j1<6; j1++) val1[j1] = val1[j1-1]*j0; lsq5.makeNorm(val1, 1.0, val12[j0]); } cout << "Invert = " << lsq5.invert(nr1); cout << ", rank=" << nr1 << endl; lsq5.solve(sol1); sd1 = lsq5.getSD(); mu1 = lsq5.getWeightedSD(); for (uInt i=0; i<6; i++) { cout << "Sol" << i << ": " << Y(sol1[i], 1e-12) << ", " << sd1 << ", " << mu1 << endl; } } cout << "---------------------------------------------------" << endl; cout << "Real -- 6 unknowns --- = --------" << endl; { LSQFit lsq6; lsq6 = lsq5; lsq6.solve(sol1); sd1 = lsq5.getSD(); mu1 = lsq5.getWeightedSD(); for (uInt i=0; i<6; i++) { cout << "Sol" << i << ": " << Y(sol1[i], 1e-12) << ", " << Y(sd1, 0.0006) << ", " << Y(mu1, 0.0006) << endl; } } cout << "---------------------------------------------------" << endl; cout << "Real -- 6 unknowns --- copy -------" << endl; { LSQFit lsq6(lsq5); lsq6.solve(sol1); sd1 = lsq6.getSD(); mu1 = lsq6.getWeightedSD(); for (uInt i=0; i<6; i++) { cout << "Sol" << i << ": " << Y(sol1[i], 1e-12) << ", " << Y(sd1, 0.0006) << ", " << Y(mu1, 0.0006) << endl; } } cout << "---------------------------------------------------" << endl; cout << "Real -- 3 angles -------" << endl; { LSQFit lsq5(3); Float *val = new Float[3]; val[0] = 1; val[1] = 0; val[2] = 0; lsq5.makeNorm(val, 1.0f, -90.0f); val[0] = 1; val[1] = 1; val[2] = 0; lsq5.makeNorm(val, 1.0f, -45.0f); val[0] = 1; val[1] = 1; val[2] = 1; lsq5.makeNorm(val, 1.0f, 1.0f); lsq5.invert(nr1); lsq5.solve(sol1); sd1 = lsq5.getSD(); mu1 = lsq5.getWeightedSD(); for (uInt i=0; i<3; i++) { cout << "Sol" << i << ": " << Y(sol1[i], 1e-12) << ", " << Y(sd1, 8e-7) << ", " << Y(mu1, 8e-7) << endl; } delete [] val; } cout << "---------------------------------------------------" << endl; cout << "Real -- 3 angles - constraint 180" << endl; { LSQFit lsq5(3, 1); Float *val = new Float[3]; val[0] = 1; val[1] = 0; val[2] = 0; lsq5.makeNorm(val, 1.0f, 90.0f); val[0] = 0; val[1] = 1; val[2] = 0; lsq5.makeNorm(val, 1.0f, 45.0f); val[0] = 0; val[1] = 0; val[2] = 1; lsq5.makeNorm(val, 1.0f, 46.0f); val[0] = 1; val[1] = 1; val[2] = 1; lsq5.setConstraint(0, val, 180.0f); lsq5.invert(nr1); lsq5.solve(sol1); sd1 = lsq5.getSD(); mu1 = lsq5.getWeightedSD(); for (uInt i=0; i<3; i++) { cout << "Sol" << i << ": " << Y(sol1[i], 1e-12) << ", " << sd1 << ", " << mu1 << endl; } delete [] val; } cout << "---------------------------------------------------" << endl; cout << "Real -- 3 angles - add constraint 180" << endl; { LSQFit lsq5(3); Float *val = new Float[3]; val[0] = 1; val[1] = 0; val[2] = 0; lsq5.makeNorm(val, 1.0f, 90.0f); val[0] = 0; val[1] = 1; val[2] = 0; lsq5.makeNorm(val, 1.0f, 45.0f); val[0] = 0; val[1] = 0; val[2] = 1; lsq5.makeNorm(val, 1.0f, 46.0f); val[0] = 1; val[1] = 1; val[2] = 1; lsq5.addConstraint(val, 180.0f); lsq5.invert(nr1); lsq5.solve(sol1); sd1 = lsq5.getSD(); mu1 = lsq5.getWeightedSD(); for (uInt i=0; i<3; i++) { cout << "Sol" << i << ": " << Y(sol1[i], 1e-12) << ", " << sd1 << ", " << mu1 << endl; } delete [] val; } cout << "---------------------------------------------------" << endl; cout << "Complex-----------------------" << endl; LSQFit lsqc1(N, LSQComplex()); for (uInt i=0; i #include #include #include #include #include #include #include #include #include #include #include #include #include Double Y(const Double x, const Double y=3e-15) { return (abs(x) < y) ? 0 : x; } Float Y(const Float x, const Double y=3e-15) { return (abs(x) < y) ? 0 : x; } DComplex Y(const DComplex x, const Double y=4e-15) { return DComplex(Y(real(x), y), Y(imag(x), y)); } Complex Y(const Complex x, const Double y=4e-15) { return Complex(Y(real(x), y), Y(imag(x), y)); } void showdt(const LSQaips &lsq) { uInt nun, np, ncon, ner, rank; Double *norm, *known, *constr, *err, *sEq, *sol; uInt *piv; Double prec, nonlin; lsq.debugIt(nun, np, ncon, ner, rank, norm, known, constr, err, piv, sEq, sol, prec, nonlin); cout << "nun, np, ncon, ner, rank: " << nun << ", " << np << ", " << ncon << ", " << ner << ", " << rank << endl; cout << "collinearity, factor-1: " << prec << ", " << nonlin << endl; cout << "Norm"; if (norm) { Int i00=0; for (uInt i=0; i csol(2*N1); Double mu, me; Vector sol(4*N1); Complex vcce[M][N] = { {Complex(1,0),Complex(1,0),Complex(1,0)}, {Complex(1,0),Complex(0,-1),Complex(2,0)}, {Complex(1,0),Complex(-2,0),Complex(0,2)}, {Complex(1,0),Complex(1,0),Complex(1,0)}, {Complex(1,0),Complex(0,-1),Complex(2,0)}, {Complex(1,0),Complex(-2,0),Complex(0,2)} }; Complex cob[M] = { Complex(6,4),Complex(3,8),Complex(-15,9), Complex(6,4),Complex(3,8),Complex(-15,9)}; DComplex vdcce[M][N] = { {DComplex(1,0),DComplex(1,0),DComplex(1,0)}, {DComplex(1,0),DComplex(0,-1),DComplex(2,0)}, {DComplex(1,0),DComplex(-2,0),DComplex(0,2)}, {DComplex(1,0),DComplex(1,0),DComplex(1,0)}, {DComplex(1,0),DComplex(0,-1),DComplex(2,0)}, {DComplex(1,0),DComplex(-2,0),DComplex(0,2)} }; Vector cce(N); Vector dcce(N); VectorSTLIterator > cceit(cce); VectorSTLIterator > dcceit(dcce); DComplex dcob[M] = { DComplex(6,4),DComplex(3,8),DComplex(-15,9), DComplex(6,4),DComplex(3,8),DComplex(-15,9)}; Float wt[M] = { 1,5,2,7,3,4}; Double vceq[2*N][2*N]; Complex vcceq[N][N]; DComplex vdcceq[N][N]; Vector ceq(2*N); Vector cceq(N); Vector dcceq(N); VectorSTLIterator ceqit(ceq); VectorSTLIterator > cceqit(cceq); VectorSTLIterator > dcceqit(dcceq); Double val12[512]; Float val12f[512]; for (uInt j=0; j<512; j++) val12f[j] = val12[j] = 1+2*j; Vector sol1(6); Double sd1, mu1; Vector err1(6); Vector sol1f(6); Float sdf, muf; Vector err1f(6); Vector val1(6); VectorSTLIterator valit(val1); Vector val1f(6); VectorSTLIterator valfit(val1f); Matrix cv1(6,6); Matrix cv1f(6,6); uInt nr1; try { cout << "Test LSQaips" << endl; cout << "---------------------------------------------------" << endl; cout << "Real -- 6 unknowns --- ctor --------" << endl; LSQaips lsq5(6); for (Int j0=0; j0<511; j0++) { val1[0] = 1; for (uInt j1=1; j1<6; j1++) val1[j1] = val1[j1-1]*j0; lsq5.makeNorm(valit, 1.0, val12[j0]); } val1[0] = 1; for (uInt j1=1; j1<6; j1++) val1[j1] = val1[j1-1]*511; lsq5.makeNorm(valit, 1.0, val12[511]); cout << "Invert = " << lsq5.invert(nr1); cout << ", rank=" << nr1 << endl; lsq5.solve(sol1); sd1 = lsq5.getSD(); mu1 = lsq5.getWeightedSD(); for (uInt i=0; i<6; i++) { cout << "Sol" << i << ": " << Y(sol1[i], 1e-12) << ", " << sd1 << ", " << mu1 << endl; } cout << "Chi2: " << lsq5.getChi() << endl; lsq5.getErrors(err1); cout << "Errors: "; for (uInt i=0; i<6; i++) { if (i != 0) cout << ", "; cout << err1[i]; } cout << endl; lsq5.getCovariance(cv1); for (uInt i5=0; i5<6; i5++) { cout << "Cov(" << i5 << ")"; for (uInt i6=0; i6<6; i6++) { cout << ": " << Y(cv1(i5,i6), 1e-12); } cout << endl; } cout << "Float: " << endl; lsq5.solve(sol1f); sdf = lsq5.getSD(); muf = lsq5.getWeightedSD(); for (uInt i=0; i<6; i++) { cout << "Sol" << i << ": " << Y(sol1f[i], 1e-12) << ", " << Y(sdf, 0.0006) << ", " << Y(muf, 0.0006) << endl; } lsq5.getErrors(err1f); cout << "Errors: "; for (uInt i=0; i<6; i++) { if (i != 0) cout << ", "; cout << Y(err1f[i], 0.00015); } cout << endl; lsq5.getCovariance(cv1f); for (uInt i5=0; i5<6; i5++) { cout << "Cov(" << i5 << ")"; for (uInt i6=0; i6<6; i6++) { cout << ": " << Y(cv1f(i5,i6), 1e-12); } cout << endl; } cout << "---------------------------------------------------" << endl; cout << "Real -- 6 unknowns --- float -------" << endl; { LSQaips lsq5(6); for (Int j0=0; j0<511; j0++) { val1f[0] = 1; for (uInt j1=1; j1<6; j1++) val1f[j1] = val1f[j1-1]*j0; lsq5.makeNorm(valfit, 1.0f, val12f[j0]); } val1f[0] = 1; for (uInt j1=1; j1<6; j1++) val1f[j1] = val1f[j1-1]*511; lsq5.makeNorm(valfit, 1.0f, val12f[511]); cout << "Invert = " << lsq5.invert(nr1); cout << ", rank=" << nr1 << endl; lsq5.solve(sol1); sd1 = lsq5.getSD(); mu1 = lsq5.getWeightedSD(); for (uInt i=0; i<6; i++) { cout << "Sol" << i << ": " << Y(sol1[i],1e-12) << ", " << Y(sd1, 1e-5) << ", " << Y(mu1, 1e-5) << endl; } } cout << "---------------------------------------------------" << endl; cout << "Real -- 6 unknowns --- float ------- indexed ---" << endl; { LSQaips lsq5(6); uInt cix[6] = {0,4,3,2,5,1}; uInt cixrev[6] = {0,5,3,2,1,4}; Vector ix(6); Vector ixrev(6); VectorSTLIterator ixit(ix); for (uInt i=0; i<6; ++i) { ix[i] = cix[i]; ixrev[i] = cixrev[i]; } for (Int j0=0; j0<511; j0++) { val1f[ixrev[0]] = 1; for (uInt j1=1; j1<6; j1++) val1f[ixrev[j1]] = val1f[ixrev[j1-1]]*j0; lsq5.makeNorm(6, ixit, valfit, 1.0f, val12f[j0]); } val1f[0] = 1; for (uInt j1=1; j1<6; j1++) val1f[j1] = val1f[j1-1]*511; lsq5.makeNorm(valfit, 1.0f, val12f[511]); cout << "Invert = " << lsq5.invert(nr1); cout << ", rank=" << nr1 << endl; lsq5.solve(sol1); sd1 = lsq5.getSD(); mu1 = lsq5.getWeightedSD(); for (uInt i=0; i<6; i++) { cout << "Sol" << i << ": " << Y(sol1[i],1e-12) << ", " << Y(sd1, 1e-5) << ", " << Y(mu1, 1e-5) << endl; } } cout << "---------------------------------------------------" << endl; cout << "Real -- 6 unknowns --- set --------" << endl; { LSQaips lsq5; lsq5.set(6,LSQReal()); lsq5.set(1e-8); for (Int j0=0; j0<512; j0++) { val1[0] = 1; for (uInt j1=1; j1<6; j1++) val1[j1] = val1[j1-1]*j0; lsq5.makeNorm(valit, 1.0, val12[j0]); } cout << "Invert = " << lsq5.invert(nr1); cout << ", rank=" << nr1 << endl; lsq5.solve(sol1); sd1 = lsq5.getSD(); mu1 = lsq5.getWeightedSD(); for (uInt i=0; i<6; i++) { cout << "Sol" << i << ": " << Y(sol1[i], 1e-12) << ", " << sd1 << ", " << mu1 << endl; } } cout << "---------------------------------------------------" << endl; cout << "Real -- 6 unknowns --- = --------" << endl; { LSQaips lsq6; lsq6 = lsq5; lsq6.solve(sol1); sd1 = lsq5.getSD(); mu1 = lsq5.getWeightedSD(); for (uInt i=0; i<6; i++) { cout << "Sol" << i << ": " << Y(sol1[i], 1e-12) << ", " << Y(sd1, 0.0006) << ", " << Y(mu1, 0.0006) << endl; } } cout << "---------------------------------------------------" << endl; cout << "Real -- 6 unknowns --- copy -------" << endl; { LSQaips lsq6(lsq5); lsq6.solve(sol1); sd1 = lsq6.getSD(); mu1 = lsq6.getWeightedSD(); for (uInt i=0; i<6; i++) { cout << "Sol" << i << ": " << Y(sol1[i], 1e-12) << ", " << Y(sd1, 0.0006) << ", " << Y(mu1, 0.0006) << endl; } } cout << "---------------------------------------------------" << endl; cout << "Real -- 3 angles -------" << endl; { LSQaips lsq5(3); Vector val(3); VectorSTLIterator valot(val); val[0] = 1; val[1] = 0; val[2] = 0; lsq5.makeNorm(valot, 1.0f, -90.0f); val[0] = 1; val[1] = 1; val[2] = 0; lsq5.makeNorm(valot, 1.0f, -45.0f); val[0] = 1; val[1] = 1; val[2] = 1; lsq5.makeNorm(valot, 1.0f, 1.0f); lsq5.invert(nr1); lsq5.solve(sol1); sd1 = lsq5.getSD(); mu1 = lsq5.getWeightedSD(); for (uInt i=0; i<3; i++) { cout << "Sol" << i << ": " << Y(sol1[i], 1e-12) << ", " << Y(sd1, 8e-7) << ", " << Y(mu1, 8e-7) << endl; } } cout << "---------------------------------------------------" << endl; cout << "Real -- 3 angles - constraint 180" << endl; { LSQaips lsq5(3, 1); Vector val(3); VectorSTLIterator valot(val); val[0] = 1; val[1] = 0; val[2] = 0; lsq5.makeNorm(valot, 1.0f, 90.0f); val[0] = 0; val[1] = 1; val[2] = 0; lsq5.makeNorm(valot, 1.0f, 45.0f); val[0] = 0; val[1] = 0; val[2] = 1; lsq5.makeNorm(valot, 1.0f, 46.0f); val[0] = 1; val[1] = 1; val[2] = 1; lsq5.setConstraint(0, valot, 180.0f); lsq5.invert(nr1); lsq5.solve(sol1); sd1 = lsq5.getSD(); mu1 = lsq5.getWeightedSD(); for (uInt i=0; i<3; i++) { cout << "Sol" << i << ": " << Y(sol1[i], 1e-12) << ", " << sd1 << ", " << mu1 << endl; } } cout << "---------------------------------------------------" << endl; cout << "Complex-----------------------" << endl; LSQaips lsqc1(N, LSQComplex()); for (uInt i=0; i cv(2*N,2*N); lsqc1.getCovariance(cv); for (uInt i5=0; i5<2*N; i5++) { cout << "Cov(" << i5 << ")"; for (uInt i6=0; i6<2*N; i6++) { cout << ": " << Y(cv(i5,i6)); } cout << endl; } } cout << "---------------------------------------------------" << endl; cout << "Complex------ other calls ----------" << endl; { LSQaips lsqc1(N, LSQComplex()); for (uInt i=0; i dcsol(N1); lsqc1.solve(dcsol); mu = lsqc1.getSD(); me = lsqc1.getWeightedSD(); cout << "Sol"; for (uInt i4=0; i4 cv(2*N,2*N); Matrix ccv(N,N); Matrix dccv(N,N); lsqc1.getCovariance(cv); for (uInt i5=0; i5<2*N; i5++) { cout << "Cov(" << i5 << ")"; for (uInt i6=0; i6<2*N; i6++) { cout << ": " << Y(cv(i6,i5)); } cout << endl; } lsqc1.getCovariance(ccv); for (uInt i5=0; i5 cv(2*N,2*N); lsqc1.getCovariance(cv); for (uInt i5=0; i5<2*N; i5++) { cout << "Cov(" << i5 << ")"; for (uInt i6=0; i6<2*N; i6++) { cout << ": " << Y(cv(i5,i6)); } cout << endl; } } cout << "---------------------------------------------------" << endl; } cout << "Complex+Constraint------------" << endl; { LSQaips lsqc1(N, LSQComplex(), i2/2); for (uInt i=0; i cv(2*N,2*N); lsqc1.getCovariance(cv); for (uInt i5=0; i5<2*N; i5++) { cout << "Cov(" << i5 << ")"; for (uInt i6=0; i6<2*N; i6++) { cout << ": " << Y(cv(i5,i6)); } cout << endl; } } } cout << "---------------------------------------------------" << endl; cout << "Complex+Complex Constraint------------" << endl; { LSQaips lsqc1(N, LSQComplex(), i2/2); for (uInt i=0; i cv(2*N,2*N); lsqc1.getCovariance(cv); for (uInt i5=0; i5<2*N; i5++) { cout << "Cov(" << i5 << ")"; for (uInt i6=0; i6<2*N; i6++) { cout << ": " << Y(cv(i5,i6)); } cout << endl; } } } cout << "---------------------------------------------------" << endl; cout << "Complex+DComplex Constraint------------" << endl; { LSQaips lsqc1(N, LSQComplex(), i2/2); for (uInt i=0; i cv(2*N,2*N); lsqc1.getCovariance(cv); for (uInt i5=0; i5<2*N; i5++) { cout << "Cov(" << i5 << ")"; for (uInt i6=0; i6<2*N; i6++) { cout << ": " << Y(cv(i5,i6)); } cout << endl; } } } cout << "---------------------------------------------------" << endl; cout << "DComplex Non-linear------------" << endl; { LSQaips lnl(3, LSQComplex()); const uInt n=100; Double x[n]; Double y[n]; for (uInt i=0; i un(3); VectorSTLIterator unitit(un); Vector vsol(3); for (uInt i=0; i<3; ++i) vsol[i] = sol[i]; DComplex kn[1]; const Int Niter = 30; lnl.setMaxIter(Niter); uInt nr; Timer tim1; tim1.mark(); while (!lnl.isReady()) { for (uInt i=0; i un(3); VectorSTLIterator unitit(un); Vector vsol(3); VectorSTLIterator solit(vsol); std::copy(sol, sol+3, solit); Complex kn[1]; Float mu, me; const Int Niter = 30; lnl.setMaxIter(Niter); uInt nr; Timer tim1; tim1.mark(); while (!lnl.isReady()) { for (uInt i=0; i ce(3); Vector cer(3); VectorSTLIterator ceit(ce); VectorSTLIterator cerit(cer); uInt vcindex[2] = {1,0}; Vector cindex(2); VectorSTLIterator cindexit(cindex); for (uInt j=0; j<2; ++j) cindex[j] =vcindex[j]; DComplex m[2] = {DComplex(2,3), DComplex(4,1)}; // Solution and error area Vector sol(3); Double sd, mu; uInt rank; Bool ok; // LSQFit area LSQaips fit(2, LSQComplex()); // Make normal equation for (uInt i=0; i<2; i++) { for (uInt j=0; j<3; ++j) ce[j] =vce[i][j]; fit.makeNorm(ceit, 1.0, m[i], LSQFit::COMPLEX); } // Invert and show ok = fit.invert(rank); cout << "ok? " << ok << "; rank: " << rank << endl; // Solve and show if (ok) { fit.solve(sol); sd = fit.getSD(); mu = fit.getWeightedSD(); for (uInt i=0; i<2; i++) cout << "Sol" << i << ": " << sol[i] << endl; cout << "sd: "<< sd << "; mu: " << mu << endl; } cout << "Complex -- COMPLEX ------------ indexed ---" << endl; fit.set(2, LSQComplex()); // Make normal equation for (uInt i=0; i<2; i++) { for (uInt j=0; j<3; ++j) cer[j] = vcer[i][j]; fit.makeNorm(2, cindexit, cerit, 1.0, m[i], LSQFit::COMPLEX); } // Invert and show ok = fit.invert(rank); cout << "ok? " << ok << "; rank: " << rank << endl; // Solve and show if (ok) { fit.solve(sol); sd = fit.getSD(); mu = fit.getWeightedSD(); for (uInt i=0; i<2; i++) cout << "Sol" << i << ": " << sol[i] << endl; cout << "sd: "<< sd << "; mu: " << mu << endl; } cout << "Complex -- ASREAL -------------" << endl; // Retry with ASREAL type fit.set(2, LSQComplex()); for (uInt i=0; i<2; i++) { for (uInt j=0; j<3; ++j) ce[j] = vce[i][j]; fit.makeNorm(ceit, 1.0, m[i], LSQFit::ASREAL); } ok = fit.invert(rank); cout << "ok? " << ok << "; rank: " << rank << endl; if (ok) { fit.solve(sol); sd = fit.getSD(); mu = fit.getWeightedSD(); for (uInt i=0; i<2; i++) cout << "Sol" << i << ": " << sol[i] << endl; cout << "sd: "<< sd << "; mu: " << mu << endl; } cout << "Complex -- ASREAL ------------- indexed ---" << endl; fit.set(2, LSQComplex()); for (uInt i=0; i<2; i++) { for (uInt j=0; j<3; ++j) cer[j] = vcer[i][j]; fit.makeNorm(2, cindexit, cerit, 1.0, m[i], LSQFit::ASREAL); } ok = fit.invert(rank); cout << "ok? " << ok << "; rank: " << rank << endl; if (ok) { fit.solve(sol); sd = fit.getSD(); mu = fit.getWeightedSD(); for (uInt i=0; i<2; i++) cout << "Sol" << i << ": " << sol[i] << endl; cout << "sd: "<< sd << "; mu: " << mu << endl; } cout << "Complex -- SEPARABLE ----------" << endl; // Retry with SEPARABLE type: note # of unknowns! fit.set(1, LSQComplex()); m[0] = DComplex(2,3); m[1] = DComplex(2,-3); for (uInt i=0; i<2; i++) { for (uInt j=0; j<3; ++j) ce[j] = vce[i][j]; fit.makeNorm(ceit, 1.0, m[i], LSQFit::SEPARABLE); } ok = fit.invert(rank); cout << "ok? " << ok << "; rank: " << rank << endl; if (ok) { fit.solve(sol); sd = fit.getSD(); mu = fit.getWeightedSD(); for (uInt i=0; i<1; i++) cout << "Sol" << i << ": " << sol[i] << endl; if (sd == mu && mu < 1e-7) { cout << "sd: " << 0.0 << "; mu: " << 0.0 << endl; } else { cout << "sd: " << sd << "; mu: " << mu << endl; } } cout << "Complex -- SEPARABLE ---------- indexed ---" << endl; // Retry with SEPARABLE type: note # of unknowns! fit.set(1, LSQComplex()); for (uInt i=0; i<2; i++) { for (uInt j=0; j<3; ++j) cer[j] = vcer[i][j]; fit.makeNorm(2, cindexit, cerit, 1.0, m[i], LSQFit::SEPARABLE); } ok = fit.invert(rank); cout << "ok? " << ok << "; rank: " << rank << endl; if (ok) { fit.solve(sol); sd = fit.getSD(); mu = fit.getWeightedSD(); for (uInt i=0; i<1; i++) cout << "Sol" << i << ": " << sol[i] << endl; if (sd == mu && mu < 1e-7) { cout << "sd: " << 0.0 << "; mu: " << 0.0 << endl; } else { cout << "sd: " << sd << "; mu: " << mu << endl; } } cout << "Complex -- CONJUGATE ----------" << endl; // Retry with CONJUGATE type: note # of unknowns! fit.set(1, LSQComplex()); m[0] = DComplex(2,0); m[1] = DComplex(0,1); for (uInt i=0; i<2; i++) { for (uInt j=0; j<3; ++j) ce[j] = vce[i][j]; fit.makeNorm(ceit, 1.0, m[i], LSQFit::CONJUGATE); } ok = fit.invert(rank, True); cout << "ok? " << ok << "; rank: " << rank << endl; if (ok) { fit.solve(sol); sd = fit.getSD(); mu = fit.getWeightedSD(); for (uInt i=0; i<1; i++) cout << "Sol" << i << ": " << sol[i] << endl; cout << "sd: "<< sd << "; mu: " << mu << endl; } cout << "Complex -- CONJUGATE ---------- indexed ---" << endl; // Retry with CONJUGATE type: note # of unknowns! fit.set(1, LSQComplex()); m[0] = DComplex(2,0); m[1] = DComplex(0,1); for (uInt i=0; i<2; i++) { for (uInt j=0; j<3; ++j) cer[j] = vcer[i][j]; fit.makeNorm(2, cindexit, cerit, 1.0, m[i], LSQFit::CONJUGATE); } ok = fit.invert(rank, True); cout << "ok? " << ok << "; rank: " << rank << endl; if (ok) { fit.solve(sol); sd = fit.getSD(); mu = fit.getWeightedSD(); for (uInt i=0; i<1; i++) cout << "Sol" << i << ": " << sol[i] << endl; cout << "sd: "<< sd << "; mu: " << mu << endl; } } cout << "---------------------------------------------------" << endl; cout << "Non-linear------------" << endl; { LSQaips lnl(3); const uInt n=100; Double x[n]; Double y[n]; for (uInt i=0; i vsol(3); VectorSTLIterator solit(vsol); std::copy(sol, sol+3, solit); Vector un(3); VectorSTLIterator unit(un); Double kn[1]; const Int Niter = 30; lnl.setMaxIter(Niter); uInt nr; Timer tim1; tim1.mark(); while (!lnl.isReady()) { for (uInt i=0; i vsol(3); VectorSTLIterator solit(vsol); std::copy(sol, sol+3, solit); Vector un(3); VectorSTLIterator unit(un); Double kn[1]; const Int Niter = 30; lnl.setMaxIter(Niter); uInt nr; Timer tim1; tim1.mark(); while (!lnl.isReady()) { for (uInt i=0; i sold(3); VectorSTLIterator soldit(sold); Double covd[9]; lnl.solve(soldit); mu = lnl.getSD(); me = lnl.getWeightedSD(); lnl.getCovariance(covd); cout << "Sol: " << soldit[0] << ", " << soldit[1] << ", " << soldit[2] << endl; cout << "me: " << mu << ", " << me << endl; for (uInt i=0; i<9; i += 3) { cout << "Covar: " << Y(covd[i+0], 1e-16) << ", " << Y(covd[i+1], 1e-16) << ", " << Y(covd[i+2], 1e-16) << endl; } cerr << "User time: " << tim1.user() << endl; } cout << "Non-linear---- Float --------" << endl; { LSQaips lnl(3); const uInt n=100; Double x[n]; Double y[n]; for (uInt i=0; i vsol(3); VectorSTLIterator solit(vsol); std::copy(sol, sol+3, solit); Float muf, mef; Vector un(3); VectorSTLIterator unit(un); Double kn[1]; const Int Niter = 30; lnl.setMaxIter(Niter); uInt nr; Timer tim1; tim1.mark(); while (!lnl.isReady()) { for (uInt i=0; i #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Some C++ functions // To use them in Fitting, have to have parameters and also AutoDiff static Double func0(const Vector &) {return 1;} // 1 static Double func1(const Vector &x) {return x(0);} // x static Double func2(const Vector &x) {return sin(x(1));} // sin(y) static Double func3(const Vector &x) {return x(0)*x(0);} // x^2 void checkLinearFit(LinearFitSVD &fitter) { //*********** Test one ************* // fit data to polynomial { // Generate fake data const uInt nPrimes = 20; Vector primesTable(nPrimes); Vector x(nPrimes); Vector sigma(nPrimes); indgen((Array&)x, 1.0); // 1, 2, ... primesTable(0) = 2; for (uInt i=1; i < nPrimes; i++) { primesTable(i) = Primes::nextLargerPrimeThan(Int(primesTable(i-1)+0.01)); } sigma = 1.0; Vector actualParameters(3); actualParameters(0) = -1.92368; actualParameters(1) = 2.2055; actualParameters(2) = 0.0746753; Matrix actualCovariance(3, 3); actualCovariance(0,0) = 0.553509; actualCovariance(0,1) = -0.107895; actualCovariance(0,2) = 0.00438596; actualCovariance(1,0) = -0.107895; actualCovariance(1,1) = 0.0266234; actualCovariance(1,2) = -0.00119617; actualCovariance(2,0) = 0.00438596; actualCovariance(2,1) = -0.00119617; actualCovariance(2,2) = 0.0000569606; Double actualChiSquare = 22.9901; // construct a linear combination of functions: a(0)+a(1)*x+a(2)*x^2 Polynomial > combination(2); combination.setCoefficient(0, 1.0); combination.setCoefficient(1, 1.0); combination.setCoefficient(2, 1.0); // perform least-squares fit fitter.setFunction(combination); Vector solution = fitter.fit(x,primesTable,sigma); Matrix covariance = fitter.compuCovariance(); // Get the residual Vector yres(nPrimes); yres = primesTable; AlwaysAssertExit(fitter.residual(yres, x)); yres = yres*yres; cout << "******** test one *************" << endl; // Print actual parameters and computed parameters for (uInt i = 0; i < combination.nparameters(); i++) { cout << "Actual Parameter " << actualParameters(i) << ", Computed Parameter " << solution(i) << endl; } // Print actual covariance and computed covariance for (uInt i = 0; i < combination.nparameters(); i++) { for (uInt j = 0; j < combination.nparameters(); j++) { cout << "Actual Covariance " << actualCovariance(i,j) << ", Computed Covariance " << covariance(i,j) << endl; } } cout << "actual ChiSquare " << actualChiSquare << " Computed ChiSquare " << fitter.chiSquare() << endl; cout << "fromResidual ChiSquare: " << sum(yres) << endl; cout << "Missing rank: " << fitter.fittedNumber()-fitter.getRank() << endl; // Compare actualParameters with the solution vector AlwaysAssertExit(allNear(actualParameters, solution, 1.0e-5)); // Compare actualCovariance with the covariance matrix AlwaysAssertExit(allNear(actualCovariance, covariance, 1.0e-5)); // Compare actualChiSquare with the chiSquare value AlwaysAssertExit(near(actualChiSquare, fitter.chiSquare(), 1.0e-5)); AlwaysAssertExit(near(actualChiSquare, sum(yres), 1.0e-5)); AlwaysAssertExit(fitter.fittedNumber()-fitter.getRank() == 0); } //****** Test two ************ // fitting polynomial to data with some coefficient being held fixed { Int j; // Make some fake data sets // -1 + 6*x + 10*x^2 + 3*x^3 Polynomial poly(3); poly.setCoefficient(0, -1.0); poly.setCoefficient(1, 6.0); poly.setCoefficient(2, 10.0); poly.setCoefficient(3, 3.0); const uInt n = 1000; Vector x(n); Vector y(n); Vector sigma(n); indgen((Array&)x); x /= Double(Double(n)/10); // 0.00 - 9.99 MLCG generator; Normal noise(&generator, 0.0, 1.0); for (uInt i=0; i < n; i++) { // -1 + 6*x + 10*x^2 + 3*x^3 + unit gaussian noise y(i) = poly(x(i)) + noise(); } // Uniform variances sigma = 1.0; // construct a linear combination of functions: // a(0)+a(1)*x+a(2)*x^2+a(3)*x^3 Polynomial > combination(3); for (uInt i=0; i<4; i++) combination[i] = 1.0; // Hold the coefficient for square fixed combination.mask(2) = False; // set the parameter value combination[2] = 10; // Indicate which function to fit fitter.setFunction(combination); Vector solution = fitter.fit(x, y, sigma); Matrix covariance = fitter.compuCovariance(); cout << endl << "******** test two *************" << endl; cout << "Expect -1 + 6*x + 10*x^2 + 3*x^3 " << endl; for (uInt i = 0; i < solution.nelements(); i++) { if (i == 2) cout << "Fixed coefficient "; else cout << "Computed "; cout << solution(i) << " Std Dev " << sqrt(covariance(i,i)) << endl; } cout << "Solved for " << fitter.fittedNumber() << " parameters" << endl; AlwaysAssertExit(fitter.fittedNumber() == combination.parameters().nMaskedParameters()); AlwaysAssertExit(fitter.fittedNumber()-fitter.getRank() == 0); cout << "Missing rank: " << fitter.fittedNumber()-fitter.getRank() << endl; // compare solution with poly parameters. See if they are within 3*sigma. Int factor = 3; j = 0; for (uInt i = 0; i < solution.nelements(); i++) { if (i == 2) { j++; continue; } AlwaysAssertExit(nearAbs(solution(i), poly[j], factor*sqrt(covariance(i,i)))); j++; } } //************ test three **************** { // fitting a 2D polynomial to data points: // f(x,y) = a0 + a1*x+ a2*y + a3*x*x { // Convert C++ functions to Functionals FunctionWrapper Func0(func0,2); FunctionWrapper Func1(func1,2); FunctionWrapper Func2(func2,2); FunctionWrapper Func3(func3,2); CombiFunction combination; // form linear combination of functions // f(x,y) = a0 + a1*x+ a2*sin(y) + a3*x*x combination.addFunction(Func0); combination.addFunction(Func1); combination.addFunction(Func2); combination.addFunction(Func3); // Now use this combination to generate some fake data combination[0] = 4; combination[1] = 5; combination[2] = 6; combination[3] = 0.2; Int npoints = 100; Matrix x(npoints,2); // coordinates Vector z(npoints); // data values Vector sigma(npoints); // standard deviation MLCG generator; Normal noise(&generator, 0.0, 1.0); for (Int i = 0; i < npoints; i++) { x(i,0) = 0.2*i; x(i,1) = x(i,0)*2; Double nois = noise()/4.0; z(i) = combination(x.row(i)) + nois; } sigma = 1.0; cout << endl << "******** test three *************" << endl; Vector z0(2); z0[0] = 2; z0[1] = 3; cout << "x,y: " << z0[0] << ", " << z0[1] << endl; cout << "Expect: " << 4 + 5*z0[0]+ 6*sin(z0[1]) + 0.2*z0[0]*z0[0] << endl; cout << "Got: " << combination(z0) << endl; // For fitting the functions have to have AutoDiff and parameters // A combi did create problems when cleaning at exit the // static PoolStack data: crashed in memory /* fitter.setFunction(combination); Vector solution = fitter.fit(x,z,sigma); Matrix covariance = fitter.compuCovariance(); cout << "Expect f(x,y) = 4 + 5*x+ 6*sin(y) + 0.2*x*x" << endl; cout << "Computed " << (Array&)solution << endl; cout << "Std Dev "; for (uInt i = 0; i < solution.nelements(); i++) { cout << sqrt(covariance(i,i)) << " "; } cout << endl; // See if they are within 3*sigma. Int factor = 3; for (uInt i = 0; i < solution.nelements(); i++) { AlwaysAssertExit(nearAbs(solution(i), combination[i], factor*sqrt(covariance(i,i)))); } AlwaysAssertExit(fitter.fittedNumber()-fitter.getRank() == 0); cout << "Missing rank: " << fitter.fittedNumber()-fitter.getRank() << endl; */ } } } //****** Test on complex fitting ************ void checkComplexLinearFit(LinearFitSVD &fitter) { // fitting polynomial to data const uInt n = 1000; // Make some fake data sets // (-1.0,2.0) + (6.0,4.0)*x + (10.0,-1.5)*x^2 + (3.0,2.3)*x^3 Polynomial poly(3); poly.setCoefficient(0, Complex(-1.0,2.0)); poly.setCoefficient(1, Complex(6.0,4.0)); poly.setCoefficient(2, Complex(10.0,-1.5)); poly.setCoefficient(3, Complex(3.0,2.3)); Vector x(n); Vector y(n); Vector sigma(n); MLCG generator; Normal noise(& generator, 0.0, 1.0); // randomly generate data on a complex plane. for (uInt i = 0; i < n; i++) { x(i) = Complex(noise(), noise()); y(i) = poly(x(i))+Complex(noise())/Complex(2.0); } sigma = Complex(1.0,1.0); // construct a linear combination of functions: // a(0)+a(1)*x+a(2)*x^2+a(3)*x^3 Polynomial > combination(3); combination.setCoefficient(0, AutoDiff(1.0,4,0)); // 1 combination.setCoefficient(1, AutoDiff(1.0,4,1)); // x combination.setCoefficient(2, AutoDiff(1.0,4,2)); // x^2 combination.setCoefficient(3, AutoDiff(1.0,4,3)); // x^3 // Indicate which function to fit fitter.setFunction(combination); Vector solution = fitter.fit(x, y, sigma); Matrix covariance = fitter.compuCovariance(); cout << endl << "******** test four complex fitting*************" << endl; cout << "fitted function "; cout << "(-1.0,2.0) + (6.0,4.0)*x + (10.0,-1.5)*x^2 + (3.0,2.3)*x^3" << endl; for (uInt i = 0; i < solution.nelements(); i++) { cout << "Expected: (" << poly[i].real() << "," << poly[i].imag() << ") "; cout << "Computed: (" << solution(i).real() << "," << solution(i).imag() << ") "; cout << "Std Dev: " << sqrt(covariance(i,i)) << endl; } cout << "Missing rank: " << 2*fitter.fittedNumber()-fitter.getRank() << endl; // compare solution with poly parameters. See if they are within 3*sigma. Double factor = 3; for (uInt i = 0; i < solution.nelements(); i++) { AlwaysAssertExit(nearAbs(abs(solution(i)), abs(poly[i]), factor*abs(sqrt(covariance(i,i))))); } AlwaysAssertExit(2*fitter.fittedNumber()-fitter.getRank() == 0); } void checkConstraintLinearFit(LinearFitSVD &fitter) { //*********** Test constraint one ************* // fit data to measured angles { // Generate fake data (3 angles) const uInt n = 100; Matrix arg(3*n,3); arg = 0.0; Vector y(3*n); Vector angle(3); angle[0] = 50; angle[1] = 60; angle[2] = 70; for (uInt i=0; i<3; ++i) { for (uInt j=0; j > combination(3); fitter.setFunction(combination); cout << endl << "******** test constraint one *************" << endl; // Perform fit Vector solution = fitter.fit(arg, y); Matrix covariance = fitter.compuCovariance(); Vector errors = fitter.errors(); // Get the residuals Vector yres(3*n); yres = y; AlwaysAssertExit(fitter.residual(yres, arg)); yres = yres*yres; // Print actual parameters and computed parameters for (uInt i = 0; i < combination.nparameters(); i++) { cout << "Expected: " << angle[i] << " Computed: " << solution[i] << " Std Dev: " << errors[i] << endl; } cout <<"Sum solution: " << sum(solution) << endl; cout << "Expected ChiSquare: " << sum(yres) << " Computed ChiSquare: " << fitter.chiSquare() << endl; cout << "Missing rank: " << fitter.fittedNumber()-fitter.getRank() << ", fitted: " << fitter.fittedNumber() << ", rank: " << fitter.getRank() << endl; // Compare actualParameters with the solution vector AlwaysAssertExit(allNearAbs(angle, solution, 0.5)); AlwaysAssertExit(nearAbs(sum(solution), 180.0, 0.1)); // Compare ChiSquare AlwaysAssertExit(nearAbs(sum(yres), fitter.chiSquare(), 1.0e-5)); AlwaysAssertExit(fitter.fittedNumber()-fitter.getRank() == 0); // Redo it to see if same cout << endl << "******** test constraint repeat *************" << endl; solution = fitter.fit(arg, y); covariance = fitter.compuCovariance(); errors = fitter.errors(); // Get the residual yres = y; AlwaysAssertExit(fitter.residual(yres, arg)); yres = yres*yres; // Print actual parameters and computed parameters for (uInt i = 0; i < combination.nparameters(); i++) { cout << "Expected: " << angle[i] << " Computed: " << solution[i] << " Std Dev: " << errors[i] << endl; } cout <<"Sum solution: " << sum(solution) << endl; cout << "Expected ChiSquare: " << sum(yres) << " Computed ChiSquare: " << fitter.chiSquare() << endl; cout << "Missing rank: " << fitter.fittedNumber()-fitter.getRank() << ", fitted: " << fitter.fittedNumber() << ", rank: " << fitter.getRank() << endl; // Compare actualParameters with the solution vector AlwaysAssertExit(allNearAbs(angle, solution, 0.5)); AlwaysAssertExit(nearAbs(sum(solution), 180.0, 0.1)); // Compare ChiSquare AlwaysAssertExit(nearAbs(sum(yres), fitter.chiSquare(), 1.0e-5)); AlwaysAssertExit(fitter.fittedNumber()-fitter.getRank() == 0); // Add constraint ------------------------- // Specify functional HyperPlane > combinationA(3); fitter.setFunction(combinationA); // Specify constraint Vector constrArg(3, 1.0); HyperPlane > constrFun(3); fitter.addConstraint(constrFun, constrArg, 180.0); cout << endl << "******** test constraint sum to 180 ********" << endl; // Perform least-squares fit solution = fitter.fit(arg, y); covariance = fitter.compuCovariance(); errors = fitter.errors(); // Get the residual yres = y; AlwaysAssertExit(fitter.residual(yres, arg)); yres = yres*yres; // Print actual parameters and computed parameters for (uInt i = 0; i < combination.nparameters(); i++) { cout << "Expected: " << angle[i] << " Computed: " << solution[i] << " Std Dev: " << errors[i] << endl; } cout <<"Sum solution: " << sum(solution) << endl; cout << "Expected ChiSquare: " << sum(yres) << " Computed ChiSquare: " << fitter.chiSquare() << endl; cout << "Missing rank: " << fitter.fittedNumber()-fitter.getRank() << ", fitted: " << fitter.fittedNumber() << ", rank: " << fitter.getRank() << endl; // Compare actualParameters with the solution vector AlwaysAssertExit(allNearAbs(angle, solution, 0.6)); AlwaysAssertExit(nearAbs(sum(solution), 180.0, 1.0e-20)); // Compare ChiSquare AlwaysAssertExit(nearAbs(sum(yres), fitter.chiSquare(), 1.0e-5)); AlwaysAssertExit(fitter.fittedNumber()-fitter.getRank() == 0); cout << endl << "******** test constraint to 180 (repeat)********" << endl; solution = fitter.fit(arg, y); covariance = fitter.compuCovariance(); errors = fitter.errors(); // Get the residual yres = y; AlwaysAssertExit(fitter.residual(yres, arg)); yres = yres*yres; // Print actual parameters and computed parameters for (uInt i = 0; i < combination.nparameters(); i++) { cout << "Expected: " << angle[i] << " Computed: " << solution[i] << " Std Dev: " << errors[i] << endl; } cout <<"Sum solution: " << sum(solution) << endl; cout << "Expected ChiSquare: " << sum(yres) << " Computed ChiSquare: " << fitter.chiSquare() << endl; cout << "Missing rank: " << fitter.fittedNumber()-fitter.getRank() << ", fitted: " << fitter.fittedNumber() << ", rank: " << fitter.getRank() << endl; // Compare actualParameters with the solution vector AlwaysAssertExit(allNearAbs(angle, solution, 0.6)); AlwaysAssertExit(nearAbs(sum(solution), 180.0, 1.0e-20)); // Compare ChiSquare AlwaysAssertExit(nearAbs(sum(yres), fitter.chiSquare(), 1.0e-5)); AlwaysAssertExit(fitter.fittedNumber()-fitter.getRank() == 0); } cout << endl; } int main() { LinearFitSVD fitsvd; checkLinearFit(fitsvd); LinearFitSVD fit_complex; checkComplexLinearFit(fit_complex); LinearFitSVD fitcon; checkConstraintLinearFit(fitcon); cout << "OK" << endl; return 0; } casacore-3.7.1/scimath/Fitting/test/tNonLinearFitLM.cc000066400000000000000000001030441476623553700226150ustar00rootroot00000000000000//# tNonLinearFitLM.cc: Test nonlinear least squares classes //# Copyright (C) 1995,1996,1999,2000,2001,2002,2004,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern "C" float dtime(float *p); int main() { NonLinearFitLM fitter; Vector solution; Double user_time; Double oldChiSquare; Double newChiSquare; const uInt n = 100; Vector x(n); Vector y(n); Vector sigma(n); Matrix z(n,2); Double value; MLCG generator; Normal noise(&generator, 0.0, 1.0); sigma = 1.0; fitter.setMaxIter(100); // Set converge criteria. Default is 0.001 fitter.setCriteria(0.0001); // ***** test one: fit 1Gaussian function to data ****** // Make some fake data sets // 20.0 * exp (-((x-25)/4)^2) Gaussian1D gauss1(20, 25.0, 4.0); for (uInt j=0; j > gauss; // Must give an initial guess for the set of fitted parameters. Vector v(3); v(0) = 2; v(1) = 20; v(2) = 10; for (uInt i=0; i<3; i++) gauss[i] = AutoDiff(v[i], 3, i); // Set the function fitter.setFunction(gauss); Timer timer1; timer1.mark(); // perform fit solution = fitter.fit(x, y, sigma); user_time = timer1.user (); // Compute chi-square for the initial guess oldChiSquare = fitter.chiSquare(); // compute new chi-square for the solution newChiSquare = fitter.chiSquare(); if (fitter.converged()) { cout << "****** Test One: fit a 1D Gaussian function ******" << endl; cout << "User time: " << user_time << endl; cout << "Converged after "<< fitter.currentIteration() << " iterations" << endl; cout << "Initial guess for fitted parameters " << v << endl; cout << "chi-square for initial guess " << oldChiSquare << endl; cout << "chi-square after convergence " << newChiSquare << endl; cout << "Converge criteria " << fitter.getCriteria() << endl; Matrix covariance = fitter.compuCovariance(); cout << "Covariance matrix " << covariance; // Compare solution with gauss1 parameters for (uInt i=0; i > gaussA; for (uInt i=0; i<3; i++) gaussA[i] = v[i]; // Set the function fitter.setFunction(gaussA); timer1.mark(); // perform fit solution = fitter.fit(x, y, sigma); user_time = timer1.user (); // compute new chi-square for the solution newChiSquare = fitter.chiSquare(); if (fitter.converged()) { cout << "****** Test oneA: fit a 1D Gaussian (non-auto param) ******" << endl; cout << "User time: " << user_time << endl; cout << "Converged after "<< fitter.currentIteration() << " iterations" < covariance = fitter.compuCovariance(); cout << "Covariance matrix " << covariance; // Compare solution with gauss1 parameters for (uInt i=0; i > gaussB0; for (uInt i=0; i<3; i++) { gaussB0[i] = AutoDiff(v[i], gaussB0.nparameters(), i); } CompoundFunction > gaussB; gaussB.addFunction(gaussB0); // Set the function fitter.setFunction(gaussB); timer1.mark(); // perform fit solution = fitter.fit(x, y, sigma); user_time = timer1.user (); // compute new chi-square for the solution newChiSquare = fitter.chiSquare(); if (fitter.converged()) { cout << "****** Test oneB: fit a 1D Gaussian (use compound) ******" << endl; cout << "User time: " << user_time << endl; cout << "Converged after "<< fitter.currentIteration() << " iterations" < covariance = fitter.compuCovariance(); cout << "Covariance matrix " << covariance; // Compare solution with gauss1 parameters for (uInt i=0; i(24.5, 3, 1); gauss[0] = AutoDiff(2, 3, 0); gauss[2] = AutoDiff(10, 3, 2); // Set the mask of center to false to mask it gauss.mask(1) = False; // Set the function fitter.setFunction(gauss); // Must give an initial guess for the set of fitted parameters. v.resize(2); v[0] = 2; v[1] = 10; // perform fit solution.resize(0); solution = fitter.fit(x, y, sigma); // Compute chi-square for the initial guess oldChiSquare = fitter.chiSquare(); // compute new chi-square for the solution newChiSquare = fitter.chiSquare(); if (fitter.converged()) { cout << endl; cout << "****** Test Two: fit a 1D Gaussian function with center fixed "; cout << "******" << endl; cout << "Converged after " << fitter.currentIteration() << " iterations" << endl; cout << "Initial guess for fitted parameters " << v < covariance = fitter.compuCovariance(); cout << "Covariance matrix " << covariance << endl; // Compare solution with gauss1 parameters for (uInt i=0; i gauss2d1; gauss2d1.setMajorAxis(2.0); gauss2d1.setAxialRatio(0.5); gauss2d1.setPA(1); // randomly generate data on a 2D plane. data is perturbed with some noise for (uInt i=0; i > gauss2d; Vector > V2(2); V2(0) = AutoDiff(0.05,6,Gaussian2D >::XCENTER); V2(1) = AutoDiff(0.05,6,Gaussian2D >::YCENTER); gauss2d.setHeight(AutoDiff (1.0,6,Gaussian2D >::HEIGHT)); gauss2d[Gaussian2D >::YWIDTH] = AutoDiff(2.0,6,Gaussian2D >::YWIDTH); gauss2d[Gaussian2D >::RATIO] = AutoDiff(0.5,6,Gaussian2D >::RATIO); gauss2d.setPA( AutoDiff(0.5,6,Gaussian2D >::PANGLE)); gauss2d.setCenter(V2); // Note: For circular Gaussian fitting, the axial ratio should be set to one // (default value is one if not set) and the rotation angle should be set // to zero (default value is zero if not set), and the two parameters // should be masked nonadjustable. Noncircular Gaussian fitting, the // initial guess for the axial ratio cannot be equal to one. If noncircular // Gaussian is used to fit precisely circular Gaussian data. The rotation // angle becomes meaningless as the fitted Gaussian function becomes circular // and the fitting process may fail. // The current parameter values are used as the initial guess. Save them // for later checking Vector parameters(gauss2d.nparameters()); for (uInt i=0; i covariance = fitter.compuCovariance(); cout << "Covariance matrix " << covariance; // Compare solution with gauss2d1 parameters for (uInt j=0; j::YWIDTH] = 2.0; gauss2d1[Gaussian2D::RATIO] = 1.0; gauss2d1.setPA(0); // randomly generate data on a 2D plane. data is perturbed with some noise for (uInt i=0; i > gauss2d_auto; Vector > V(2); V(0) = AutoDiff(0.05,6,Gaussian2D >::XCENTER); V(1) = AutoDiff(0.05,6,Gaussian2D >::YCENTER); gauss2d_auto.setHeight(AutoDiff (1.0,6,Gaussian2D >::HEIGHT)); gauss2d_auto.setCenter(V); gauss2d_auto[Gaussian2D >::YWIDTH] = AutoDiff(2.0,6,Gaussian2D >::YWIDTH); gauss2d_auto[Gaussian2D >::RATIO] = AutoDiff(1.0,6,Gaussian2D >::RATIO); gauss2d_auto.setPA(AutoDiff (0.05,6,Gaussian2D >::PANGLE)); gauss2d_auto.mask(4) = False; gauss2d_auto.mask(5) = False; Gaussian2D > gauss2d2 = gauss2d_auto; // Note: For circular Gaussian fitting, the axial ratio should be set to one // (default value is one if not set) and the rotation angle should be set // to zero (default value is zero if not set), and the two parameters // should be masked nonadjustable. Noncircular Gaussian fitting, the // initial guess for the axial ratio cannot be equal to one. If noncircular // Gaussian is used to fit precisely circular Gaussian data. The rotation // angle becomes meaningless as the fitted Gaussian function becomes // circular and the fitting process may fail. NonLinearFitLM afitter; // The current parameter values are used as the initial guess. A // slight perturbation is given to them. for (uInt i=0; i covariance = afitter.compuCovariance(); cout << "Covariance matrix " << covariance; // Compare solution with gauss2d1 parameters for (uInt j=0; j fitter; // Generate fake data (3 angles) const uInt n = 100; Matrix arg(3*n,3); arg = 0.0; Vector y(3*n); Vector angle(3); angle[0] = 50; angle[1] = 60; angle[2] = 70; for (uInt i=0; i<3; ++i) { for (uInt j=0; j > combination(3); fitter.setFunction(combination); cout << endl << "******** test constraint one *************" << endl; // Perform fit Vector solution = fitter.fit(arg, y); Matrix covariance = fitter.compuCovariance(); Vector errors = fitter.errors(); // Get the residuals Vector yres(3*n); yres = y; AlwaysAssertExit(fitter.residual(yres, arg)); yres = yres*yres; // Print actual parameters and computed parameters for (uInt i = 0; i < combination.nparameters(); i++) { cout << "Expected: " << angle[i] << " Computed: " << solution[i] << " Std Dev: " << errors[i] << endl; } cout <<"Sum solution: " << sum(solution) << endl; cout << "Expected ChiSquare: " << sum(yres) << " Computed ChiSquare: " << fitter.chiSquare() << endl; cout << "Missing rank: " << fitter.fittedNumber()-fitter.getRank() << ", fitted: " << fitter.fittedNumber() << ", rank: " << fitter.getRank() << endl; // Compare actualParameters with the solution vector AlwaysAssertExit(allNearAbs(angle, solution, 0.5)); AlwaysAssertExit(nearAbs(sum(solution), 180.0, 0.1)); // Compare ChiSquare AlwaysAssertExit(nearAbs(sum(yres), fitter.chiSquare(), 1.0e-5)); AlwaysAssertExit(fitter.fittedNumber()-fitter.getRank() == 0); // Redo it to see if same cout << endl << "******** test constraint repeat *************" << endl; solution = fitter.fit(arg, y); covariance = fitter.compuCovariance(); errors = fitter.errors(); // Get the residual yres = y; AlwaysAssertExit(fitter.residual(yres, arg)); yres = yres*yres; // Print actual parameters and computed parameters for (uInt i = 0; i < combination.nparameters(); i++) { cout << "Expected: " << angle[i] << " Computed: " << solution[i] << " Std Dev: " << errors[i] << endl; } cout <<"Sum solution: " << sum(solution) << endl; cout << "Expected ChiSquare: " << sum(yres) << " Computed ChiSquare: " << fitter.chiSquare() << endl; cout << "Missing rank: " << fitter.fittedNumber()-fitter.getRank() << ", fitted: " << fitter.fittedNumber() << ", rank: " << fitter.getRank() << endl; // Compare actualParameters with the solution vector AlwaysAssertExit(allNearAbs(angle, solution, 0.5)); AlwaysAssertExit(nearAbs(sum(solution), 180.0, 0.1)); // Compare ChiSquare AlwaysAssertExit(nearAbs(sum(yres), fitter.chiSquare(), 1.0e-5)); AlwaysAssertExit(fitter.fittedNumber()-fitter.getRank() == 0); // Add constraint ------------------------- // Specify functional HyperPlane > combinationA(3); fitter.setFunction(combinationA); // Specify constraint Vector constrArg(3, 1.0); HyperPlane > constrFun(3); fitter.addConstraint(constrFun, constrArg, 180.0); cout << endl << "******** test constraint sum to 180 ********" << endl; // Perform least-squares fit solution = fitter.fit(arg, y); covariance = fitter.compuCovariance(); errors = fitter.errors(); // Get the residual yres = y; AlwaysAssertExit(fitter.residual(yres, arg)); yres = yres*yres; // Print actual parameters and computed parameters for (uInt i = 0; i < combination.nparameters(); i++) { cout << "Expected: " << angle[i] << " Computed: " << solution[i] << " Std Dev: " << errors[i] << endl; } cout <<"Sum solution: " << sum(solution) << endl; cout << "Expected ChiSquare: " << sum(yres) << " Computed ChiSquare: " << fitter.chiSquare() << endl; cout << "Missing rank: " << fitter.fittedNumber()-fitter.getRank() << ", fitted: " << fitter.fittedNumber() << ", rank: " << fitter.getRank() << endl; // Compare actualParameters with the solution vector AlwaysAssertExit(allNearAbs(angle, solution, 0.6)); AlwaysAssertExit(nearAbs(sum(solution), 180.0, 1.0e-20)); // Compare ChiSquare AlwaysAssertExit(nearAbs(sum(yres), fitter.chiSquare(), 1.0e-5)); AlwaysAssertExit(fitter.fittedNumber()-fitter.getRank() == 0); cout << endl << "******** test constraint to 180 (repeat)********" << endl; solution = fitter.fit(arg, y); covariance = fitter.compuCovariance(); errors = fitter.errors(); // Get the residual yres = y; AlwaysAssertExit(fitter.residual(yres, arg)); yres = yres*yres; // Print actual parameters and computed parameters for (uInt i = 0; i < combination.nparameters(); i++) { cout << "Expected: " << angle[i] << " Computed: " << solution[i] << " Std Dev: " << errors[i] << endl; } cout <<"Sum solution: " << sum(solution) << endl; cout << "Expected ChiSquare: " << sum(yres) << " Computed ChiSquare: " << fitter.chiSquare() << endl; cout << "Missing rank: " << fitter.fittedNumber()-fitter.getRank() << ", fitted: " << fitter.fittedNumber() << ", rank: " << fitter.getRank() << endl; // Compare actualParameters with the solution vector AlwaysAssertExit(allNearAbs(angle, solution, 0.6)); AlwaysAssertExit(nearAbs(sum(solution), 180.0, 1.0e-20)); // Compare ChiSquare AlwaysAssertExit(nearAbs(sum(yres), fitter.chiSquare(), 1.0e-5)); AlwaysAssertExit(fitter.fittedNumber()-fitter.getRank() == 0); } { // ***** test constraint: fit two 1D Gaussian functions to data ****** cout << endl << "****** Fit a double 1D Gaussian function *****" << endl; // Make some fake data sets // 20.0 * exp (-((x-25)/4)^2) // 10.0 * exp (-((x-23)/4)^2) // Noise generation MLCG generator; Normal noise(&generator, 0.0, 0.3); // Must give an initial guess for the set of fitted parameters. Double v[7] = {20, 10, 4, 10, 33, 4, 10}; Double vi[7] = {22, 11, 5, 10, 30, 5, 9}; NonLinearFitLM fitter; fitter.setMaxIter(100); CompiledFunction > gauss; gauss.setFunction("p6+p0*exp(-((x-p1)/p2)^2) + p3*exp(-((x-p4)/p5)^2)"); for (uInt i=0; i<7; ++i) gauss[i] = v[i]; for (uInt i=0; i solution = fitter.fit(x, y, sigma); // compute new chi-square for the solution newChiSquare = fitter.chiSquare(); if (fitter.converged()) { cout << "Converged after " << fitter.currentIteration() <<" iterations" < covariance = fitter.compuCovariance(); Vector errors = fitter.errors(); // Compare solution with gauss1 parameters for (uInt i=0; i fittera; fittera.setMaxIter(100); CompiledFunction > gaussa; gaussa.setFunction("p6+p0*exp(-((x-p1)/p2)^2) + p3*exp(-((x-p4)/p5)^2)"); for (uInt i=0; i<7; ++i) gaussa[i] = v[i]; for (uInt i=0; i(vi[i],7,i); fittera.setFunction(gaussa); // Add constraint Vector constrArg(7, 0.0); constrArg[0] = 1.0; constrArg[3] = -2.0; fittera.addConstraint(constrArg); // Perform fit solution = fittera.fit(x, y, sigma); // compute new chi-square for the solution newChiSquare = fittera.chiSquare(); if (fittera.converged()) { cout << "Converged after " << fittera.currentIteration() <<" iterations" < covariance = fittera.compuCovariance(); Vector errors = fittera.errors(); // Compare solution with gauss1 parameters for (uInt i=0; i fittera; fittera.setFunction(gaussa); // Add constraint constrArg = 0.0; constrArg[2] = 1.0; constrArg[5] = -1.0; fittera.addConstraint(constrArg); // Perform fit solution = fittera.fit(x, y, sigma); // compute new chi-square for the solution newChiSquare = fittera.chiSquare(); if (fittera.converged()) { cout << "Converged after " << fittera.currentIteration() <<" iterations" < covariance = fittera.compuCovariance(); Vector errors = fittera.errors(); // Compare solution with gauss1 parameters for (uInt i=0; i fittera; fittera.setFunction(gaussa); // Add constraints constrArg = 0.0; constrArg[2] = 1.0; constrArg[5] = -1.0; fittera.addConstraint(constrArg); constrArg = 0.0; constrArg[2] = 1.0; fittera.addConstraint(constrArg, 4.0); constrArg = 0.0; constrArg[0] = 1.0; constrArg[3] = -2.0; fittera.addConstraint(constrArg); // Perform fit solution = fittera.fit(x, y, sigma); // compute new chi-square for the solution newChiSquare = fittera.chiSquare(); if (fittera.converged()) { cout << "Converged after " << fittera.currentIteration() <<" iterations" < covariance = fittera.compuCovariance(); Vector errors = fittera.errors(); // Compare solution with gauss1 parameters for (uInt i=0; i #include #include #include #include #include //# Combination methods #include #include #include //# remainder will be removed #include //# 1-D Functions #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // // A module that represents various function-like classes. // // // The term Functional was chosen to roughly follow the usage in // Barton and Nackman's Scientific and Engineering C++. // Functional classes map a Domain object into a Range object, rather like a // mathematical function. They use operator(), // so they look much like single argument C++ functions. // // // // Functionals and their derived classes map an input // Domain object into an output Range object using the // operator(). // Often the input and output types are numeric, but it can be of any type. // // class Offspring : public Functional, List > { // public: // List operator()(List); // }; // // would be a legal Functional. // // The Functions and their derived classes map, again using the // operator(), numeric value(s) into a numeric value. Since they are // numeric, the Domain and Range base type can be of type // AutoDiff (where T is numeric base type) or one // of its derivations, in which case the value and its derivatives will be // calculated. // // In the current version the Domain and // Range are the same for Functions // // The basic classes are: //
        //
        Functional //
        // A base class that maps a Domain object into a Range // object using the Range operator(const Domain &). All // information necessary to convert the Domain into a // Range will be available in the class // or in the input information. No variable class state (parameters) // are available. // //
        FunctionParam //
        A helper base class that acts as a container for parameters // (state) used in Function classes. The class contains // a list of parameters, and a list of flags associated with the parameters. // Methods to set and obtain the parameters (using operator[]) // and their flags (using methods mask()) are available. The flags // can e.g. be used to indicate to Fitting routines if a certain // parameter has to be updated ('fitted') or not. // // The FunctionParam class does not assume anything about the uses of the // class, but leaves that to the final users. This means that a lot of // copying between intermediate and final users is not necessary // (like between a Gaussian fitter with fixed parameters // and the Fitting routines: the Gaussian fitter just sets a flag to False, and // let the Fitting worry about what to do internally). // // //
        Function //
        Base class for function objects with zero or more parameters (i.e. // Functionals with state). // All parameters should be of the same type T as the // Function. Function objects are specifically geared // towards use in the Fitting classes, but // can be used anywhere where the value (and/or derivatives) of functions // are needed. // // The Function class is derived from Functional // and contains a FunctionParam object. // The parameters act as state for the function // (e.g. a width for a Gaussian). A function object is called using the // T operator(const T&) (ndim=1), or the // T operator(const Vector&) (all values of ndim), or // T operator(const T&, const T&) (for ndim=2 only). // If the template argument is AutoDiff, the parameters and the // returned value will be AutoDiff; the arguments of the // operator() will be of type T. The returned value // of the function will be the function value at x (and the // derivatives w.r.t. the non-masked parameters) Using AutoDiffA // the derivatives can be calculated w.r.t. parameters and/or arguments, see // AutoDiff and // FunctionTraits for details. // // // A Function1D is provided for 1-dimensional function objects // //
        // // Actual functional classes: //
        //
        e.g. Gaussian1D //
        An actual function object will be derived from // Function. The minimum functionality of a Function // object will be support for the operator() methods (through a // single, hidden, eval() method); for the manipulation of the // associated parameters (using operator[index] and // mask(index)) and some administrative aids (ndim(), // nparameters() and the like. // // In most cases it is advantageous to have a special parameter handling // class (e.g. Gaussian1DParam), to separate the (template // independent) parameter handling from the possible specialization of // the eval() method, and to more easily incorporate // special parameter handling (e.g. using flux rather than amplitude // of a Gaussian). All of this is transparent to the end-user. //
        // Combinatory Function objects are provided to easily combine and create // function objects: //
        //
        CompoundFunction //
        creates // a new, compound, function object from one or more other function objects // (including compounds...). The new function will have the sum of the // parameters of the input functions as the new parameters (i.e. the compound // function created from a 1-dimensional Gaussian (with 3 parameters) and a // third-order polynomial (with 4 parameters) will have 7 parameters). //
        CombiFunction //
        creates // a (linear) combination of a number of input functions. The number of // parameters of the newly created function will be equal to the number of // input functions (i.e. the combi // function created from a 1-dimensional Gaussian (with 3 parameters) and a // third-order polynomial (with 4 parameters) will have 2 parameters). The // function will be param0*gauss(x) + param1*poly(x) //
        FunctionWrapper //
        will take // a global function (or by the use of the STL function adapters // mem_fun* also member functions) of any dimension, and with // any number of parameters. The function is assumed to be called as // f(x, p), and is wrapped like // FunctionWrapper(&func, param&, ndim) (see example). // //
        // //
        // // A function to find a bracketed root by bisection could be written // as follows: // // template // Domain findRoot(const Functional &func, Domain left, // Domain right, Domain tol) { // Range fr = func(right); // Range fl = func(left); // Range sign = fr > 0 ? 1 : -1 ; // AlwaysAssertExit(fl*fr < 0.0 && right > left); // while (right - left > tol) { // Domain mid = (left + right) / 2; // Range fmid = func(mid); // if (sign*fmid > 0.0) right = mid; // else left = mid; // }; // return (left + right)/2; // } // // Since Function1D is derived from Functional, the // above function will also work with classes derived from Function1D. To // behave sensibly, the Domain and Range types should be real, i.e., // Float or Double. // // To calculate the value of a polynomial // 2 + 4x2 + 6x4 // at x=5.1: // // Polynomial pol(4); // pol[0] = 2; pol[2] = 4; pol[4] = 6; // cout << "Polynomial value at 5.1: " << pol(5.1) << endl; // // // Create a simple function (1-dimensional) with 2 parameters (A and B): // // Double myf(const Double x, const Vector p) { // return p[0]*sin(p[1]*x); } // // make it into a function object for initial parameters 2 and pi: // // Vector p(2); // p[0] = 2; p[1] = C::pi; // FunctionWrapper f0(myf, p, 2); // // Make the first parameter 3: // // f0[0] = 3; // // (for the global function you have to change p[0]). // Calculate the value of the function: // // cout << "The value " << f0(3) << " should be 1.5 times the value " << // myf(3) << endl; // // A function object could be created as: // // template class objf : public Function { // public: // objf() : Function(2) {}; // 2 parameters // objf(const objf &other) : Function(other) {}; // virtual ~objf() {}; // // The actual method called for the evaluation operator(): // virtual T eval(typename Function::FunctionArg x) const { // return param_p[0] * sin(param_p[1] * x[0]); }; // // Return a copy of function (used for combination e.g.) // virtual Function *clone() const { // return new objf(*this); }; // }; // // Which can be called as: // // objf f1; // f1[0] = 2; f1[1] = C::pi; // cout << "The value " << myf(3) << " should be equal to the value " << // f1(3) << endl; // // // // The immediate motivations for this module were: //
          //
        1. To represent functions which are used in linear and non-linear least // squares fitting //
        //
        // //
      • It could be convenient to have a letter/envelope class, and to // define ``function arithmetic.'' // // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/000077500000000000000000000000001476623553700172415ustar00rootroot00000000000000casacore-3.7.1/scimath/Functionals/AbstractFunctionFactory.h000066400000000000000000000047561476623553700242270ustar00rootroot00000000000000//# FunctionFactory.h: a class for creating Function objects from Records //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_ABSTRACTFUNCTIONFACTORY_H #define SCIMATH_ABSTRACTFUNCTIONFACTORY_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class Record; // // a class for creating Function objects from Records // // // // // //
      • FunctionFactory // // // // This class is based on the Factory pattern, similar to the // ApplicationObjectFactory // // // // // // // // // // // // // // // // // // // // // // // // // // template class FunctionFactory { public: FunctionFactory() {} FunctionFactory(const FunctionFactory& factory) {} virtual ~FunctionFactory() {} virtual Function *create(const Record& gr) const throw (FunctionFactoryError) = 0; FunctionFactory& operator=(const FunctionFactory& factory) { return *this; } }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/ArraySampledFunctional.h000066400000000000000000000127761476623553700240360ustar00rootroot00000000000000//# ArraySampledFunctional: //# Copyright (C) 1996,1997,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_ARRAYSAMPLEDFUNCTIONAL_H #define SCIMATH_ARRAYSAMPLEDFUNCTIONAL_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Index into an array using the longest axis // // // // //
      • SampledFunctional //
      • Array // // // A SampledFunctional is an interface that allows random access to a fixed // size data set. An ArraySampledFunctional allows you to access slices of // an an Array object. // // // // An ArraySampledFunctional allows an Array object to be sliced up with // each sample being one slice of the original Array. The slices are always // the same size and the indexing is always done along the last // non-degenerate dimension. For example a(4,3,20,1) is interpreted as a // SampledFunctional with 20 elements, each one being a 4 by 3 matrix. // // The Array that is passed to the constructor is copied by this class but // because Arrays themselves use reference symantics, the actual data is not // copied but referenced. This means that modifying the data in the original // array will correspondingly modify the data accessed by this class. // // Similarly the Array that is returned for each Slice is a reference to the // actual data so that modifying this array really modifies the original // data. This is not recommended as the operator() function is not supposed // to be used to get a modifiable portion of the data. // // // Constructing and using ArraySampledFunctionals // // Array a(IPosition(4,4,3,20,1)); // Create an array // ... Fill the array any way you like ... // ArraySampledFunctional >fa(a); // for(uInt i = 0; i < 20; i++) // cout << "f(" << i << ") = " << fa(i) << endl; // // Each 'slice' is a 4 by 3 Matrix // // // // I needed a SampledFunctional which could return Arrays of arbitrary (but // fixed) size for each index. This could be done using a // ScalarSampledFunctional > but is ineffecient as each // element is stored as a separate Array. This class is a more efficient way // to solve this problem. // // //
      • The template type MUST be an Array of some arbitrary type. This is // because this class will return a slice of this Array. The Array template // type cannot be subsumed into the class definition because the definition // of the inherited operator() function means that the return type must be // the template type // // //
      • Exceptions are not thrown directly by this class. // // //
      • Nothing I can think of. // template class ArraySampledFunctional :public SampledFunctional { public: // These constructors copy the array that is passed to them. But because // arrays use reference symantics the data is not copied. The default // constructor is basically useless, as there is no way to add the data // once the class has been constructed. // ArraySampledFunctional(); ArraySampledFunctional(const T & data); // // The standard copy constructor and assignment operator // ArraySampledFunctional(ArraySampledFunctional & other); ArraySampledFunctional & operator=(ArraySampledFunctional &other); // // Define the functions for the SampledFunction interface // virtual T operator()(const uInt & index) const; virtual uInt nelements() const; virtual ~ArraySampledFunctional(); // // An alternate version of the sampling function which is more effecient // because it does not need to create as many temporary objects or copy the // Array data. // const T operator()(const uInt & index); // private: T theRefData; IPosition theEnd; uInt theLastAxis; uInt theNelements; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/ArraySampledFunctional.tcc000066400000000000000000000071451476623553700243520ustar00rootroot00000000000000//# ArraySampledFunctional.cc: //# Copyright (C) 1996,1997,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_ARRAYSAMPLEDFUNCTIONAL_TCC #define SCIMATH_ARRAYSAMPLEDFUNCTIONAL_TCC #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template ArraySampledFunctional:: ArraySampledFunctional() :theRefData(), theEnd(), theLastAxis(0), theNelements(0){ } template ArraySampledFunctional:: ArraySampledFunctional(const T & data) :theRefData(data), theEnd(data.endPosition()), theLastAxis(0), theNelements(0) { const uInt ndim = theEnd.nelements(); for (uInt i = 0; i < ndim; i++) if (theEnd(i) > 0) theLastAxis = i; theNelements = theEnd(theLastAxis) + 1; theEnd(theLastAxis) = 0; } template ArraySampledFunctional:: ArraySampledFunctional(ArraySampledFunctional & other) : SampledFunctional(other), theRefData(other.theRefData), theEnd(other.theEnd), theLastAxis(other.theLastAxis), theNelements(other.theNelements) { } template ArraySampledFunctional & ArraySampledFunctional:: operator=(ArraySampledFunctional &other){ if (this != &other) { theRefData.reference(other.theRefData); theEnd = other.theEnd; theLastAxis = other.theLastAxis; theNelements = other.theNelements; } return *this; } template T ArraySampledFunctional:: operator()(const uInt & index) const { IPosition blc(theEnd.nelements(), 0); blc(theLastAxis) = index; IPosition trc(theEnd); trc(theLastAxis) = index; // Because refData is const I cannot use the operator() function as this // returns a reference. The way around this is to create a non const // pointer to the array, call the operator() function and then create a // copy (using the copy() function). T *nonConstPtr = (T *) &theRefData; T theSubArray = nonConstPtr->operator()(blc, trc); return theSubArray.nonDegenerate(theLastAxis); } template const T ArraySampledFunctional:: operator()(const uInt & index) { IPosition blc(theEnd.nelements(), 0); blc(theLastAxis) = index; theEnd(theLastAxis) = index; return theRefData(blc, theEnd).nonDegenerate(theLastAxis); } template uInt ArraySampledFunctional:: nelements() const { return theNelements; } template ArraySampledFunctional:: ~ArraySampledFunctional() { } // Local Variables: // compile-command: "gmake OPTLIB=1 ArraySampledFunctional" // End: } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/Chebyshev.h000066400000000000000000000315471476623553700213440ustar00rootroot00000000000000//# Chebyshev.h A function class that defines a Chebyshev polynomial //# Copyright (C) 2000,2001,2002,2003,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //#! ======================================================================== #ifndef SCIMATH_CHEBYSHEV_H #define SCIMATH_CHEBYSHEV_H #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // A function class that defines a Chebyshev polynomial // // // // // //
      • Function // // // // This class is named after Chebyshev Type I polynomials // // // // This class allows one to form and evaluate a function as a // Chebyshev series, a linear combination of so-called Chebyshev // polynomials. // // This class's implementation is split into two parts: // the parent class ChebyshevParam<T>, // which manages the function's parameters, and this class, which handles // how the function is evaluated. Thus, be sure to also consult // ChebyshevParam<T> for // the full interface of this function. // // //

        About Chebyshev Polynomials

        // // Chebyshev polynomials are a special type of ultraspheric polynomials // that are useful in such contexts as numerical analysis and circuit // design. They form an orthogobnal set. // A (type I) Chebyshev polynomial, T_n, is generated via the // equation: // // T_n(x) = cos n(arccos x) // // Through clever use of trigometric identities, one can express T_n // as a real polynomial expression of the form // // n // T_n(x) = SUM C_i t^i // i=0 // // The low order polynomials look like this: // // T_0 = 1 // T_1 = x // T_2 = 2x^2 - 1 // T_3 = 4x^3 - 3x // T_4 = 8x^4 - 8x^2 + 1 // T_5 = 16x^5 - 20x^3 + 5x // // Higher order polynomials satisfy the recurrance relation, // // T_(n+1) = 2xT_(n) - T_(n-1). // // // A common use of Chebyshev polynomials is in approximating // functions. In particular, any function that is approximated by // a power series, // // f(x) ~ SUM P_i x^i, // // over the interval [-1, 1] can be approximated by a linear // combination of Chebyshev polynomials: // // f(x) ~ SUM C_i T_i(x), // // where C_i is the set of so-called Chebyshev coefficients. // // Approximating a function with Chebyshev polynomials has some // important advantages. For one, if the function is well approximated // by a converging power series, one can obtain an equally accurate // estimate using fewer terms of the corresponding Chebyshev series. // More important, though, is the property over the interval [-1, 1], // each polynomial has a domain of [-1, 1]; thus, the series is nicely // bounded. And because of this bounded property, approximations // calculated from a Chebyshev series are less susceptible to machine // rounding errors than the equivalent power series. // // //

        Using the Chebyshev Function class

        // // With a simple change of variable, it is possible to approximate a // continuous function over any restricted interval using a // Chebyshev series. This documention refers to this interval as the // Chebyshev interval (set with the // setInterval() function). The // other important input parameters, of course, include the // coefficients of the polynomials. // // Like all Functions, the Chebyshev series is evaluated via the // function operator, operator(). If the input value is // within the range set by // setInterval(), it is // transformed to the range [-1, 1] via, // // y = x - (min + max)/2) / ((max - min)/2) // // The series is then evaluated with the coefficients set either at // construction or via setCoefficients(). The value that is returned // when the input value is outside the Chebyshev interval depends on // the out-of-interval mode (set via // setOutOfIntervalMode()). The // default mode is to return a default value which can be set via // setDefault(). The supported // modes are identified by the // enumeration OutOfIntervalMode; see the // documentation for ChebyshevParam // for a detailed description of these modes. In practice, though, it is // expected that this class will be configured for the interval of interest. // // The derivative of a Chebyshev series with respect to the independent // variable (i.e. the argument x) is easily calculated analytically // and can be expressed as another Chebyshev series; this is what the // derivative() function returns. However, the more general way to // obtain derivatives is via the AutoDiff // templated type. // //
        // // // In this example, a 2nd order Chebyshev polynomial series is // created. // // // set coeffs to desired values // Vector coeffs(3, 1); // // // configure the function // Chebyshev cheb; // cheb.setInterval(-0.8, 7.2); // cheb.setDefault(1.0); // cheb.setCoefficients(coeffs); // // // evaluate the function as necessary // Double z = cheb(-0.5); // -0.5 is within range, z = 0.78625 // z = cheb(4.2); // 4.2 is within range, z = 0.375 // z = cheb(-3); // -3 is out of the interval, z = 1 // // // The next example illustrates how to use the // AutoDiff class to simultaneously // calculate derivatives. Here, we replace the Double type with // AutoDiff. // // Chebyshev > cheb; // cheb.setDefault(AutoDiffA(1)); // cheb.setInterval(AutoDiffA(-0.8), AutoDiffA(7.2)); // // // we'll track derivatives with respect to x and each of our // // coefficients; for a second-order series, this makes 4 // // derivatives total. x will be the first variable; the // // coefficients will the 2nd-4th variables // cheb.setCoefficient(0, AutoDiffA(3.1, 4, 1)); // c0 = 3.1 // cheb.setCoefficient(1, AutoDiffA(2.4, 4, 2)); // c1 = 2.4 // cheb.setCoefficient(2, AutoDiffA(0.5, 4, 3)); // c2 = 0.5 // // // now evaluate the function // AutoDiffA x(1.2, 4, 0); // x = 1.2 // AutoDiffA y = cheb(x); // y = 1.65 // Double dydx = y.derivative(0); // dy/dx = 0.35 // Double dydc1 = y.derivative(2); // dy/dc1 = -0.5 // // // // // This class was created to support systematic errors in the simulator tool. // It can be used by Jones matrix classes to vary gains in a predictable way, // mimicing natural processes of the atmosphere or instrumental effects. // // //
      • T should have standard numerical operators. Current // implementation only tested for real types (and their AutoDiffs). // // //
      • Assertion in debug mode if attempt is made to address incorrect // coefficients // // // //
      • It would be helpful to be able to convert to and from the // Polynomial type; this would be supported via a function, // Polynomial polynomial(), and constructor, // Chebyshev(Polynomial) // template class Chebyshev : public ChebyshevParamModeImpl { public: //# Constructors // create a zero-th order Chebyshev polynomial with the first coefficient // equal to zero. The bounded domain is [T(-1), T(1)]. The // OutOfDomainMode is CONSTANT, and the default value is T(0). Chebyshev() : ChebyshevParamModeImpl() {} // create an n-th order Chebyshev polynomial with the coefficients // equal to zero. The bounded domain is [T(-1), T(1)]. The // OutOfDomainMode is CONSTANT, and the default value is T(0). explicit Chebyshev(const uInt n) : ChebyshevParamModeImpl(n) {} // create a zero-th order Chebyshev polynomical with the first coefficient // equal to one. // min is the minimum value of its Chebyshev interval, and // max is the maximum value. // mode sets the behavior of the function outside the Chebyshev interval // (see setOutOfIntervalMode() and OutOfIntervalMode enumeration // definition for details). // defval is the value returned when the function is evaluated outside // the Chebyshev interval and mode=CONSTANT. Chebyshev(const T &min, const T &max, const typename ChebyshevEnums:: OutOfIntervalMode mode=ChebyshevEnums::CONSTANT, const T &defval=T(0)) : ChebyshevParamModeImpl(min, max, mode, defval) {} // create a fully specified Chebyshev polynomial. // coeffs holds the coefficients of the Chebyshev polynomial (see // setCoefficients() for details). // min is the minimum value of its canonical range, and // max is the maximum value. // mode sets the behavior of the function outside the Chebyshev interval // (see setOutOfIntervalMode() and OutOfIntervalMode enumeration // definition for details). // defval is the value returned when the function is evaluated outside // the canonical range and mode=CONSTANT. Chebyshev(const Vector &coeffs, const T &min, const T &max, const typename ChebyshevEnums:: OutOfIntervalMode mode=ChebyshevEnums::CONSTANT, const T &defval=T(0)) : ChebyshevParamModeImpl(coeffs, min, max, mode, defval) {} // create a fully specified Chebyshev polynomial. // config is a record that contains the non-coefficient data // that configures this class. // The fields recognized by this class are those documented for the // ChebyshevPara::setMode() // function. // Chebyshev(uInt order, const RecordInterface& mode) : ChebyshevParamModeImpl(order, mode) { } Chebyshev(const Vector &coeffs, const RecordInterface& mode) : ChebyshevParamModeImpl(coeffs, mode) { } // // create a deep copy of another Chebyshev polynomial // Chebyshev(const Chebyshev &other) : ChebyshevParamModeImpl(other) {} // // make this instance a (deep) copy of another Chebyshev polynomial Chebyshev &operator=(const Chebyshev &other) { ChebyshevParam::operator=(other); return *this; } // Destructor virtual ~Chebyshev() {} //# Operators // Evaluate the Chebyshev at x. virtual T eval(const typename FunctionTraits::ArgType *x) const; //# Member functions // Return the Chebyshev polynomial which is the derivative of this one // (with respect to the argument x). Chebyshev derivative() const; // Create a new copy of this object. The caller is responsible // for deleting the pointer. // virtual Function *clone() const { return new Chebyshev(*this); } // }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/Chebyshev.tcc000066400000000000000000000061751476623553700216650ustar00rootroot00000000000000//# Chebyshev.cc a function class that defines a Chebyshev polynomial //# Copyright (C) 2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //#! ======================================================================== #ifndef SCIMATH_CHEBYSHEV_TCC #define SCIMATH_CHEBYSHEV_TCC //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Operators template T Chebyshev::eval(const typename FunctionTraits::ArgType *x) const { T xp = x[0]; // handle out-of-interval values if (xp < this->minx_p || xp > this->maxx_p) { switch (this->mode_p) { case ChebyshevEnums::CONSTANT: return this->def_p; case ChebyshevEnums::ZEROTH: return this->param_p[0]; case ChebyshevEnums::CYCLIC: { T period = (this->maxx_p-this->minx_p); while (xp < this->minx_p) xp += period; while (xp > this->maxx_p) xp -= period; } break; case ChebyshevEnums::EDGE: { T tmp(0); if (xpminx_p) { for (uInt i=0; inparameters(); i+=2) tmp += this->param_p[i]; for (uInt i=1; inparameters(); i+=2) tmp -= this->param_p[i]; } else { for (uInt i=0; inparameters(); ++i) tmp += this->param_p[i]; } return tmp; } break; default: break; } } T yi1=T(0); T yi2=T(0); T tmp; // map Chebeshev range [this->minx_p, this->maxx_p] into [-1, 1] xp = (T(2)*xp-this->minx_p-this->maxx_p)/(this->maxx_p-this->minx_p); // evaluate using Clenshaw recursion relation for (Int i=this->nparameters()-1; i>0; i--) { tmp = T(2)*xp*yi1 - yi2 + this->param_p[i]; yi2 = yi1; yi1 = tmp; } return xp*yi1 - yi2 + this->param_p[0]; } template Chebyshev Chebyshev::derivative() const { Vector ce(this->nparameters()); ce = this->parameters().getParameters(); this->derivativeCoeffs(ce, this->minx_p, this->maxx_p); return Chebyshev(ce, this->minx_p, this->maxx_p, this->mode_p, T(0)); } //# Member functions } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/ChebyshevParam.h000066400000000000000000000524011476623553700223150ustar00rootroot00000000000000//# ChebyshevParam.h: Parameter handling for Chebyshev polynomial //# Copyright (C) 2000,2001,2002,2003,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //#! ======================================================================== #ifndef SCIMATH_CHEBYSHEVPARAM_H #define SCIMATH_CHEBYSHEVPARAM_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class RecordInterface; // // Define enums for Chebyshev classes // class ChebyshevEnums { public: // Modes that identify how this function behaves outside its Chebyshev // interval (see setInterval()). enum OutOfIntervalMode { // return a constant, default value. The value returned is // set with setDefault(). CONSTANT, // return a constant value equal to the zero-th order coefficient ZEROTH, // evaluate the polynomial based on its coefficients just as it // would be inside the interval. Thus, the function's range is not // guaranteed to remain within the characteristic bounds of the // Chebyshev interval. EXTRAPOLATE, // evaluate the function as if the range is cyclic, repeating the // range values from its canonical domain. The period of the cycle // will be equal to getIntervalMax()-getIntervalMin(). When the // function is evaluated outside this interval, the input value will // shifted an integer number of periods until it falls within the // Chebyshev interval; the value returned is the polynomial evaluated // at the shifted (x-axis) value. Obviously, this mode is most // expensive computationally when evaluating outside the range. CYCLIC, // evaluate the function at nearest interval edge EDGE, // number of enumerators NOutOfIntervalModes }; }; // Parameter handling for Chebyshev polynomial parameters // // // // // //
      • FunctionParam class //
      • Function1D //
      • Chebyshev // // // // This class is named after Chebyshev Type I polynomials; it handles the // "fixed" parameters for the function. // // // // This class assists in forming and evaluating a function as a // Chebyshev series, a linear combination of so-called Chebyshev // polynomials. Users do not instantiate this abstract class directly; // instead they instantiate the child class // Chebyshev. This class holds the part // of the implementation used by the // Chebyshev class that manages the "fixed" // parameters of the function (e.g. the polynomial coefficients, interval of // interest, etc.) // // For a full description, see the // Chebyshev class. // // // // // In this example, a 2nd order Chebyshev polynomial series is // created. // // // set coeffs to desired values // Vector coeffs(3, 1); // // // configure the function // Chebyshev cheb; // cheb.setInterval(-0.8, 7.2); // cheb.setDefault(1.0); // cheb.setCoefficients(coeffs); // // // evaluate the function as necessary // Double z = cheb(-0.5); // -0.5 is within range, z = 0.78625 // z = cheb(4.2); // 4.2 is within range, z = 0.375 // z = cheb(-3); // -3 is out of the interval, z = 1 // // // // // This class was created to support systematic errors in the simulator tool. // It can be used by Jones matrix classes to vary gains in a predictable way, // mimicing natural processes of the atmosphere or instrumental effects. // // The Chebyshev implementation is split between this class, // ChebyshevParam and its child // Chebyshev to better support the // AutoDiff framework for evaluating // derivatives. // // // //
      • T should have standard numerical operators. Current // implementation only tested for real types (and their AutoDiffs). // // // //
      • Assertion if indices out-of-range // // // //
      • It would be helpful to be able to convert to and from the // Polynomial type; this would be supported via a function, // Polynomial polynomial(), and constructor, // Chebyshev(Polynomial) // template class ChebyshevParam : public Function1D { public: //# Constructors // create a zero-th order Chebyshev polynomial with the first coefficient // equal to zero. The bounded domain is [T(-1), T(1)]. The // OutOfDomainMode is CONSTANT, and the default value is T(0). ChebyshevParam(); // create an n-th order Chebyshev polynomial with the coefficients // equal to zero. The bounded domain is [T(-1), T(1)]. The // OutOfDomainMode is CONSTANT, and the default value is T(0). explicit ChebyshevParam(const uInt n); // create a zero-th order Chebyshev polynomical with the first coefficient // equal to one. // min is the minimum value of its Chebyshev interval, and // max is the maximum value. // mode sets the behavior of the function outside the Chebyshev interval // (see setOutOfIntervalMode() and OutOfIntervalMode enumeration // definition for details). // defval is the value returned when the function is evaluated outside // the Chebyshev interval and mode=CONSTANT. ChebyshevParam(const T &min, const T &max, ChebyshevEnums::OutOfIntervalMode mode=ChebyshevEnums::CONSTANT, const T &defval=T(0)); // create a fully specified Chebyshev polynomial. // coeffs holds the coefficients of the Chebyshev polynomial (see // setCoefficients() for details). // min is the minimum value of its canonical range, and // max is the maximum value. // mode sets the behavior of the function outside the Chebyshev interval // (see setOutOfIntervalMode() and OutOfIntervalMode enumeration // definition for details). // defval is the value returned when the function is evaluated outside // the canonical range and mode=CONSTANT. ChebyshevParam(const Vector &coeffs, const T &min, const T &max, ChebyshevEnums::OutOfIntervalMode mode=ChebyshevEnums::CONSTANT, const T &defval=T(0)); // create a fully specified Chebyshev polynomial. // config is a record that contains the non-coefficient data // that configures this class. // The fields recognized by this class are those documented for the // setMode() function below. // ChebyshevParam(uInt order, const RecordInterface& mode); ChebyshevParam(const Vector &coeffs, const RecordInterface& mode); // // create a deep copy of another Chebyshev polynomial // ChebyshevParam(const ChebyshevParam &other); template ChebyshevParam(const ChebyshevParam &other) : Function1D(other), def_p(other.getDefault()), minx_p(other.getIntervalMin()), maxx_p(other.getIntervalMax()), mode_p(other.getOutOfIntervalMode()) {} // // make a (deep) copy of another Chebyshev polynomial ChebyshevParam &operator=(const ChebyshevParam &other); // Destructor virtual ~ChebyshevParam(); // set the Chebyshev coefficients. // coeffs holds the coefficients in order, beginning with the zero-th // order term. The order of the polynomial, then, would be the size // of the Vector minus one. void setCoefficients(const Vector &coeffs); // set a particular Chebyshev coefficient. // which is the coefficient order (i.e. 0 refers to the constant offset). // value is the coefficient value. // If which is larger than current order of the function, the order will // be increased to the value of which, and that coefficient is set to // value; missing coefficients less than this value will be set to zero. // Thus, the order can be increased with this function; however, it cannot // be decreased (even if the highest order coefficient is set to zero). // To lower the order, use setCoefficients() with a Vector having the // desired number of coefficients. void setCoefficient(const uInt which, const T &value); // return the current set of coefficients into a given Vector. const Vector &getCoefficients() const; // return a particular coefficient. // which is the coefficient order (i.e. 0 refers to the constant offset). // If which is out of range, zero is returned. T getCoefficient(const uInt which) const { return ((which < nparameters()) ? param_p[which] : T(0)); } // return the number of coeefficients currently loaded. This does not // guarantee that the coefficients are non-zero uInt nCoefficients() const { return nparameters(); } // set the Chebyshev interval for this function. The function will // be scaled and shifted to such that the central bounded range of the // Chebyshev polynomials ([-1, 1] in untransformed space) spans the // given range. // min is the minimum value for the interval, and // max is the maximum value. See setOutOfIntervalMode() for the behavior // of this function outside the set range. void setInterval(T xmin, T xmax) { if (xmin < xmax) { minx_p = xmin; maxx_p = xmax; } else { minx_p = xmax; maxx_p = xmin; } } // return the minimum value for the currently Chebyshev interval. // See setInterval() for additional details. T getIntervalMin() const { return minx_p; } // return the maximum value for the currently Chebyshev interval. // See setInterval() for additional details. T getIntervalMax() const { return maxx_p; } // set the behavior of this function when it is evaluated outside its // Chebyshev interval void setOutOfIntervalMode(ChebyshevEnums::OutOfIntervalMode mode) { mode_p = mode; } // return the behavior of this function when it is evaluated outside of // its Chebyshev interval. ChebyshevEnums::OutOfIntervalMode getOutOfIntervalMode() const { return mode_p; } // set the default value of this function. This value is used when // the getOutOfIntervalMode() returns Chebyshev::CONSTANT; it is returned // when the a value outside of the Chebyshev interval is passed to // the () operator. void setDefault(const T &val) { def_p = val; } // return the currently set default value. See setDefault() for details // on the use of this value. const T &getDefault() const { return def_p; } // return the order of this polynomial. This returns the value of // nCoefficients()-1; uInt order() const { return param_p.nelements() - 1; } // transform a set of Chebyshev polynomial coefficients into a set // representing the series' derivative. coeffs should be assuming // an interval of [-1, 1]. xmin and xmax can be provided to transform // the series to another interval. static void derivativeCoeffs(Vector &coeffs, const T &xmin=T(-1), const T &xmax=T(1)); // convert a set of Chebyshev polynomial coefficients to power series // coefficients. The values passed in coeffs are taken to // be chebyshev coefficients; these values will be replaced with the // power series coefficients. They should be ordered beginning // with the zero-th order coefficient. static void chebyshevToPower(Vector &coeffs); // convert a set of power series coefficients to Chebyshev // polynomial coefficients. The values passed in coeffs are taken to // be power series coefficients; these values will be replaced with the // Chebyshev polynomial coefficients. They should be ordered beginning // with the zero-th order coefficient. static void powerToChebyshev(Vector &coeffs); // Give name of function virtual const String &name() const { static String x("chebyshev"); return x; } protected: // Default value if outside interval T def_p; // Lowest interval bound T minx_p; // Highest inetrval bound T maxx_p; // Out-of-interval handling type ChebyshevEnums::OutOfIntervalMode mode_p; static Vector modes_s; //# Make members of parent classes known. protected: using Function1D::param_p; public: using Function1D::nparameters; using Function1D::setMode; }; // A ChebyshevParam with the get/setMode implementation // // // The get/setMode() implementation is separated from ChebyshevParam // to enable simple specialization for AutoDiff. See // ChebyshevParam for documentation // template class ChebyshevParamModeImpl : public ChebyshevParam { public: ChebyshevParamModeImpl() : ChebyshevParam() { } explicit ChebyshevParamModeImpl(const uInt n) : ChebyshevParam(n) {} ChebyshevParamModeImpl(const T &min, const T &max, typename ChebyshevEnums::OutOfIntervalMode mode=ChebyshevEnums::CONSTANT, const T &defval=T(0)) : ChebyshevParam(min, max, mode, defval) {} ChebyshevParamModeImpl(const Vector &coeffs, const T &min, const T &max, typename ChebyshevEnums::OutOfIntervalMode mode=ChebyshevEnums::CONSTANT, const T &defval=T(0)) : ChebyshevParam(coeffs, min, max, mode, defval) {} ChebyshevParamModeImpl(uInt order, const RecordInterface& mode) : ChebyshevParam(order, mode) { setMode(mode); } ChebyshevParamModeImpl(const Vector &coeffs, const RecordInterface& mode) : ChebyshevParam(coeffs, mode) { setMode(mode); } ChebyshevParamModeImpl(const ChebyshevParamModeImpl &other) : ChebyshevParam(other) {} // get/set the function mode. This is an alternate way to get/set the // non-coefficient data for this function. The supported record fields // are as follows: //
            // Field Name     Type            Role
            // -------------------------------------------------------------------
            // min            template type   the minimum value of the Chebyshev 
            //              			interval of interest
            // max            template type   the maximum value of the Chebyshev 
            //              			interval of interest
            // intervalMode   TpString        the out-of-interval mode; recognized
            //                                  values are "constant", "zeroth",
            //                                  "extrapolate", "cyclic", and "edge".
            //                                  setMode() recognizes a 
            //                                  case-insensitive, minimum match.
            // default        template type   the out-of-range value that is returned
            //                                  when the out-of-interval mode is 
            //                                  "constant".
            // 
        // An exception is thrown if interval mode is unrecognized. // virtual void setMode(const RecordInterface& mode); virtual void getMode(RecordInterface& mode) const; // // return True if the implementing function supports a mode. This // implementation always returns True. virtual Bool hasMode() const; //# Make members of parent classes known. protected: using ChebyshevParam::modes_s; public: using ChebyshevParam::setOutOfIntervalMode; using ChebyshevParam::getOutOfIntervalMode; using ChebyshevParam::getIntervalMin; using ChebyshevParam::getIntervalMax; using ChebyshevParam::getDefault; }; #define ChebyshevParamModeImpl_PS ChebyshevParamModeImpl // Partial specialization of ChebyshevParamModeImpl for // AutoDiff // // // The name ChebyshevParamModeImpl_PS is only // for cxx2html limitations. // // template class ChebyshevParamModeImpl_PS > : public ChebyshevParam > { public: ChebyshevParamModeImpl_PS() : ChebyshevParam >() { } explicit ChebyshevParamModeImpl_PS(const uInt n) : ChebyshevParam >(n) {} ChebyshevParamModeImpl_PS(const AutoDiff &min, const AutoDiff &max, typename ChebyshevEnums::OutOfIntervalMode mode=ChebyshevEnums::CONSTANT, const AutoDiff &defval=AutoDiff(0)) : ChebyshevParam >(min, max, mode, defval) {} ChebyshevParamModeImpl_PS(const Vector > &coeffs, const AutoDiff &min, const AutoDiff &max, typename ChebyshevEnums::OutOfIntervalMode mode=ChebyshevEnums::CONSTANT, const AutoDiff &defval=AutoDiff(0)) : ChebyshevParam >(coeffs, min, max, mode, defval) {} ChebyshevParamModeImpl_PS(uInt order, const RecordInterface& mode) : ChebyshevParam >(order, mode) {} ChebyshevParamModeImpl_PS(const Vector > &coeffs, const RecordInterface& mode) : ChebyshevParam >(coeffs, mode) {} ChebyshevParamModeImpl_PS(const ChebyshevParamModeImpl_PS &other) : ChebyshevParam >(other) {} virtual void setMode(const RecordInterface& mode); virtual void getMode(RecordInterface& mode) const; //# Make members of parent classes known. protected: using ChebyshevParam >::modes_s; public: using ChebyshevParam >::setOutOfIntervalMode; using ChebyshevParam >::getOutOfIntervalMode; using ChebyshevParam >::getIntervalMin; using ChebyshevParam >::getIntervalMax; using ChebyshevParam >::getDefault; }; #define ChebyshevParamModeImpl_PSA ChebyshevParamModeImpl // Partial specialization of ChebyshevParamModeImpl for // AutoDiff // // // The name ChebyshevParamModeImpl_PS is only // for cxx2html limitations. // // template class ChebyshevParamModeImpl_PSA > : public ChebyshevParam > { public: ChebyshevParamModeImpl_PSA() : ChebyshevParam >() { } explicit ChebyshevParamModeImpl_PSA(const uInt n) : ChebyshevParam >(n) {} ChebyshevParamModeImpl_PSA(const AutoDiffA &min, const AutoDiffA &max, typename ChebyshevEnums::OutOfIntervalMode mode=ChebyshevEnums::CONSTANT, const AutoDiffA &defval=AutoDiffA(0)) : ChebyshevParam >(min, max, mode, defval) {} ChebyshevParamModeImpl_PSA(const Vector > &coeffs, const AutoDiffA &min, const AutoDiffA &max, typename ChebyshevEnums::OutOfIntervalMode mode=ChebyshevEnums::CONSTANT, const AutoDiffA &defval=AutoDiffA(0)) : ChebyshevParam >(coeffs, min, max, mode, defval) {} ChebyshevParamModeImpl_PSA(uInt order, const RecordInterface& mode) : ChebyshevParam >(order, mode) {} ChebyshevParamModeImpl_PSA(const Vector > &coeffs, const RecordInterface& mode) : ChebyshevParam >(coeffs, mode) {} ChebyshevParamModeImpl_PSA(const ChebyshevParamModeImpl_PSA &other) : ChebyshevParam >(other) {} virtual void setMode(const RecordInterface& mode); virtual void getMode(RecordInterface& mode) const; //# Make members of parent classes known. protected: using ChebyshevParam >::modes_s; public: using ChebyshevParam >::setOutOfIntervalMode; using ChebyshevParam >::getOutOfIntervalMode; using ChebyshevParam >::getIntervalMin; using ChebyshevParam >::getIntervalMax; using ChebyshevParam >::getDefault; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/ChebyshevParam.tcc000066400000000000000000000304451476623553700226430ustar00rootroot00000000000000//# ChebyshevParam.cc a function class that defines a ChebyshevParam polynomial //# Copyright (C) 2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //#! ======================================================================== #ifndef SCIMATH_CHEBYSHEVPARAM_TCC #define SCIMATH_CHEBYSHEVPARAM_TCC //# Includes #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors template ChebyshevParam::ChebyshevParam() : Function1D(1), def_p(T(0)), minx_p(T(-1)), maxx_p(T(1)), mode_p(ChebyshevEnums::CONSTANT) {} template ChebyshevParam::ChebyshevParam(const uInt n) : Function1D(n+1), def_p(T(0)), minx_p(T(-1)), maxx_p(T(1)), mode_p(ChebyshevEnums::CONSTANT) {} template ChebyshevParam::ChebyshevParam(const uInt n, const RecordInterface&) : Function1D(n+1), def_p(T(0)), minx_p(T(-1)), maxx_p(T(1)), mode_p(ChebyshevEnums::CONSTANT) { } template ChebyshevParam::ChebyshevParam(const T &min, const T &max, ChebyshevEnums::OutOfIntervalMode mode, const T &defval) : Function1D(1), def_p(defval), mode_p(mode) { param_p[0] = 1; this->setInterval(min, max); } template ChebyshevParam::ChebyshevParam(const Vector &coeffs, const T &min, const T &max, ChebyshevEnums::OutOfIntervalMode, const T &defval) : Function1D(coeffs.nelements()), def_p(defval), minx_p(min), maxx_p(max), mode_p(ChebyshevEnums::CONSTANT) { setCoefficients(coeffs); } template ChebyshevParam::ChebyshevParam(const Vector &coeffs, const RecordInterface& mode) : Function1D(coeffs.nelements()), def_p(T(0)), minx_p(T(-1)), maxx_p(T(1)), mode_p(ChebyshevEnums::CONSTANT) { setMode(mode); setCoefficients(coeffs); } template ChebyshevParam::ChebyshevParam(const ChebyshevParam &other) : Function1D(other), def_p(other.def_p), minx_p(other.minx_p), maxx_p(other.maxx_p), mode_p(other.mode_p) {} template ChebyshevParam & ChebyshevParam::operator=(const ChebyshevParam &other) { if (this != &other) { mode_p = other.mode_p; minx_p = other.minx_p; maxx_p = other.maxx_p; def_p = other.def_p; } return *this; } template ChebyshevParam::~ChebyshevParam() {} //# Operators //# Member functions template void ChebyshevParam::setCoefficients(const Vector &coeffs) { if (coeffs.nelements() == 0) { throw AipsError("ChebyshevParam::setCoeffiecients(): " "empty Vector passed"); } for (uInt i=0; i void ChebyshevParam::setCoefficient(const uInt which, const T &value) { if (which >= nparameters()) { uInt sz = nparameters(); FunctionParam cfp(param_p); param_p = FunctionParam(which+1); for (uInt i=0; i const Vector &ChebyshevParam::getCoefficients() const { return param_p.getParameters(); } template void ChebyshevParam::derivativeCoeffs(Vector &coeffs, const T &xmin, const T &xmax) { // first get power series coefficients Vector ce(coeffs); chebyshevToPower(ce); // take the derivative Vector &dce = coeffs; dce.resize(ce.nelements()-1); for (uInt i=1; i void ChebyshevParam::powerToChebyshev(Vector &coeffs) { uInt n = coeffs.nelements(); // Create an inverse transformation matrix Matrix poly(n, n, T(0)); poly(0,0) = T(1); poly(1,1) = T(1); T scale; for (uInt i=2; i1; j-=2, k++) { poly(j,i) = scale; scale *= T((i - k + 1) / k); } poly(j,i) = scale; if (j == 0) poly(j,i) /= 2; } // multiply transformation matrix by coefficient vector for (uInt i=0; i void ChebyshevParam::chebyshevToPower(Vector &coeffs) { uInt n = coeffs.nelements(); // Create a transformation matrix Matrix cheb(n, n, T(0)); cheb(0,0) = T(1); cheb(1,1) = T(1); for (uInt i=2; i0; j -= 2) { if (j > 1) cheb(j-2,i) -= cheb(j-2,i-2); cheb(j,i) += T(2)*cheb(j-1,i-1); } } // multiply transformation matrix by coefficient vector for (uInt i=0; i Vector ChebyshevParam::modes_s = stringToVector("constant zeroth extrapolate cyclic edge", ' '); template Bool ChebyshevParamModeImpl::hasMode() const { return True; } template void ChebyshevParamModeImpl::setMode(const RecordInterface& in) { // interval of interest if (in.isDefined(String("interval"))) { RecordFieldId fld("interval"); if (in.type(in.idToNumber(fld)) == TpArrayDouble || in.type(in.idToNumber(fld)) == TpArrayComplex || in.type(in.idToNumber(fld)) == TpArrayDComplex || in.type(in.idToNumber(fld)) == TpArrayFloat || in.type(in.idToNumber(fld)) == TpArrayInt) { Vector intv; in.get(fld, intv); if (intv(0) < intv(1)) this->setInterval(intv(0), intv(1)); else this->setInterval(intv(0), intv(1)); } } // default value if (in.isDefined(String("default"))) { RecordFieldId fld("default"); if (in.type(in.idToNumber(fld)) == TpDouble || in.type(in.idToNumber(fld)) == TpComplex || in.type(in.idToNumber(fld)) == TpDComplex || in.type(in.idToNumber(fld)) == TpFloat || in.type(in.idToNumber(fld)) == TpInt) { T def; in.get(fld, def); this->setDefault(def); } } // out-of-interval mode if (in.isDefined(String("intervalMode"))) { RecordFieldId fld("intervalMode"); if (in.type(in.idToNumber(fld)) == TpString) { String mode; in.get(fld, mode); uInt match = MUString::minimaxNC(mode, modes_s); if (mode.length() > 0 && match < modes_s.nelements()) setOutOfIntervalMode(static_cast(match)); else throw AipsError(String("Unrecognized intervalMode: ") + mode); } } } template void ChebyshevParamModeImpl::getMode(RecordInterface& out) const { Vector intv(2); intv(0) = getIntervalMin(); intv(1) = getIntervalMax(); out.define(RecordFieldId("interval"), intv); out.define(RecordFieldId("default"), getDefault()); out.define(RecordFieldId("intervalMode"), modes_s(getOutOfIntervalMode())); } // specialization for AutoDiff template void ChebyshevParamModeImpl >::setMode(const RecordInterface& in) { // interval of interest if (in.isDefined(String("interval"))) { RecordFieldId fld("interval"); if (in.type(in.idToNumber(fld)) == TpArrayDouble || in.type(in.idToNumber(fld)) == TpArrayComplex || in.type(in.idToNumber(fld)) == TpArrayDComplex || in.type(in.idToNumber(fld)) == TpArrayFloat || in.type(in.idToNumber(fld)) == TpArrayInt) { Vector intv; in.get(fld, intv); if (intv(0) < intv(1)) this->setInterval(intv(0), intv(1)); else this->setInterval(intv(0), intv(1)); } } // default value if (in.isDefined(String("default"))) { RecordFieldId fld("default"); if (in.type(in.idToNumber(fld)) == TpDouble || in.type(in.idToNumber(fld)) == TpComplex || in.type(in.idToNumber(fld)) == TpDComplex || in.type(in.idToNumber(fld)) == TpFloat || in.type(in.idToNumber(fld)) == TpInt) { T def; in.get(fld, def); this->setDefault(def); } } // out-of-interval mode if (in.isDefined(String("intervalMode"))) { RecordFieldId fld("intervalMode"); if (in.type(in.idToNumber(fld)) == TpString) { String mode; in.get(fld, mode); uInt match = MUString::minimaxNC(mode, this->modes_s); if (mode.length() > 0 && match < this->modes_s.nelements()) this->setOutOfIntervalMode(static_cast(match)); else throw AipsError(String("Unrecognized intervalMode: ") + mode); } } } template void ChebyshevParamModeImpl >::getMode(RecordInterface& out) const { Vector intv(2); intv(0) = this->getIntervalMin().value(); intv(1) = this->getIntervalMax().value(); out.define(RecordFieldId("interval"), intv); out.define(RecordFieldId("default"), this->getDefault().value()); out.define(RecordFieldId("intervalMode"), this->modes_s(this->getOutOfIntervalMode())); } // specialization for AutoDiffA template void ChebyshevParamModeImpl >::setMode(const RecordInterface& in) { // interval of interest if (in.isDefined(String("interval"))) { RecordFieldId fld("interval"); if (in.type(in.idToNumber(fld)) == TpArrayDouble || in.type(in.idToNumber(fld)) == TpArrayComplex || in.type(in.idToNumber(fld)) == TpArrayDComplex || in.type(in.idToNumber(fld)) == TpArrayFloat || in.type(in.idToNumber(fld)) == TpArrayInt) { Vector intv; in.get(fld, intv); if (intv(0) < intv(1)) this->setInterval(intv(0), intv(1)); else this->setInterval(intv(0), intv(1)); } } // default value if (in.isDefined(String("default"))) { RecordFieldId fld("default"); if (in.type(in.idToNumber(fld)) == TpDouble || in.type(in.idToNumber(fld)) == TpComplex || in.type(in.idToNumber(fld)) == TpDComplex || in.type(in.idToNumber(fld)) == TpFloat || in.type(in.idToNumber(fld)) == TpInt) { T def; in.get(fld, def); this->setDefault(def); } } // out-of-interval mode if (in.isDefined(String("intervalMode"))) { RecordFieldId fld("intervalMode"); if (in.type(in.idToNumber(fld)) == TpString) { String mode; in.get(fld, mode); uInt match = MUString::minimaxNC(mode, this->modes_s); if (mode.length() > 0 && match < this->modes_s.nelements()) this->setOutOfIntervalMode(static_cast(match)); else throw AipsError(String("Unrecognized intervalMode: ") + mode); } } } template void ChebyshevParamModeImpl >::getMode(RecordInterface& out) const { Vector intv(2); intv(0) = this->getIntervalMin().value(); intv(1) = this->getIntervalMax().value(); out.define(RecordFieldId("interval"), intv); out.define(RecordFieldId("default"), this->getDefault().value()); out.define(RecordFieldId("intervalMode"), this->modes_s(this->getOutOfIntervalMode())); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/Combi2Function.tcc000066400000000000000000000042501476623553700225560ustar00rootroot00000000000000//# Combi2Function.cc: Combination of Functions AutoDiff specialization //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_COMBI2FUNCTION_TCC #define SCIMATH_COMBI2FUNCTION_TCC //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Operators template AutoDiff CombiFunction >:: eval(typename Function >::FunctionArg x) const { AutoDiff tmp(0); for (uInt i=0; inparameters(); ++i) { if (this->param_p[i].nDerivatives() > 0) { tmp = this->param_p[i]; break; } } for (uInt j=0; jnparameters(); ++i) { T v = (this->function(i))(x).value(); tmp.value() += this->param_p[i].value()*v; // get derivatives (assuming either all or none) if (tmp.nDerivatives()>0 && this->param_p.mask(i)) tmp.deriv(i) = v; } return tmp; } //# Member functions } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/CombiFunction.h000066400000000000000000000171041476623553700221540ustar00rootroot00000000000000//# CombiFunction.h: Form a linear combination of Functions //# Copyright (C) 2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_COMBIFUNCTION_H #define SCIMATH_COMBIFUNCTION_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // // Form a linear combination of function objects. // // // // // // // // //
      • Function class // // // // Given N function objects, the class describes a linear combination of the // form: // // f(x) = a(0)*f(0)(x) + a(1)*f(1)(x) + ... + a(N-1)*f(N-1)(x) // // where a = {a(n)} are parameters. If the combi function is used in // a functional fitting process (see // LinearFit) these parameters canm be // solved for. In all aspects they behave as // FunctionParam values. // // Member functions are added with the addFunction() method. // // // Check CompoundFunction class // for a combination of functions behaving as one object. // // // // In the following example a second order polynomial is built from 3 separate // polynomials. // // Polynomial constant(0); // Polynomial linear(1); // Polynomial square(2); // // constant.setCoefficient(0, 1.0); // 1 // linear.setCoefficient(1, 1.0); // x // square[2] = 1.0; // x^2 // // CombiFunction combination; // // // form function, e0 + e1*x + e2*x^2 // combination.addFunction(constant); // combination.addFunction(linear); // combination.addFunction(square); // // // //
      • T should have standard numerical operators and exp() function. Current // implementation only tested for real types. //
      • To obtain derivatives, the derivatives should be defined. // // //
      • AipsError in debug mode if incorrect function index // // // // This class was created to allow specialization of the evaluation in // a simple way. // // // //
      • Nothing I know of // template class CombiFunction : public CombiParam { public: //# Constructors // The default constructor -- no functions, no parameters, nothing, the // function operator returns a 0. CombiFunction() : CombiParam() {} // Make this object a (deep) copy of other. // CombiFunction(const CombiFunction &other) : CombiParam(other) {} CombiFunction(const CombiFunction &other, Bool) : CombiParam(other, True) {} template CombiFunction(const CombiFunction &other) : CombiParam(other) {} template CombiFunction(const CombiFunction &other, Bool) : CombiParam(other, True) {} // // Make this object a (deep) copy of other. CombiFunction &operator=(const CombiFunction &other) { CombiParam::operator=(other); return *this; } // Destructor virtual ~CombiFunction() {} //# Operators // Evaluate the function at x. virtual T eval(typename Function::FunctionArg x) const; //# Member functions // Return a copy of this object from the heap. The caller is responsible for // deleting the pointer. // virtual Function *clone() const { return new CombiFunction(*this); } virtual Function::DiffType> *cloneAD() const { return new CombiFunction::DiffType>(*this); } virtual Function::BaseType> *cloneNonAD() const { return new CombiFunction::BaseType> (*this, True); } // //# Make members of parent classes known. public: using CombiParam::nparameters; }; #define CombiFunction_PS CombiFunction // Partial specialization of CombiFunction for AutoDiff // // // The name CombiFunction_PS is only for cxx2html // documentation problems. Use CombiFunction in your code. // template class CombiFunction_PS > : public CombiParam > { public: //# Constructors // The default constructor -- no functions, no parameters, nothing, the // function operator returns a 0. CombiFunction_PS() : CombiParam >() {} // Make this object a (deep) copy of other. // CombiFunction_PS(const CombiFunction_PS > &other) : CombiParam >(other) {} template CombiFunction_PS(const CombiFunction_PS &other) : CombiParam >(other) {} // // Make this object a (deep) copy of other. CombiFunction_PS > & operator=(const CombiFunction_PS > &other) { CombiParam >::operator=(other); return *this; } // Destructor virtual ~CombiFunction_PS() {} //# Operators // Evaluate the function and its derivatives at x wrt // to the coefficients. virtual AutoDiff eval(typename Function >::FunctionArg x) const; //# Member functions // Return a copy of this object from the heap. The caller is responsible for // deleting the pointer. // virtual Function > *clone() const { return new CombiFunction_PS >(*this); } virtual Function >::DiffType> *cloneAD() const { return new CombiFunction >::DiffType> (*this); } virtual Function >::BaseType> *cloneNonAD() const { return new CombiFunction >::BaseType> (*this, True); } // //# Make members of parent classes known. public: using CombiParam >::nparameters; }; #undef CombiFunction_PS } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/CombiFunction.tcc000066400000000000000000000033201476623553700224710ustar00rootroot00000000000000//# CombiFunction.cc: Form a linear combination of Functions //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_COMBIFUNCTION_TCC #define SCIMATH_COMBIFUNCTION_TCC //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Operators template T CombiFunction::eval(typename Function::FunctionArg x) const { T tmp(0); for (uInt i = 0; i< this->nFunctions(); ++i) { tmp += this->param_p[i]*(this->function(i))(x); } return tmp; } //# Member functions } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/CombiParam.h000066400000000000000000000144741476623553700214360ustar00rootroot00000000000000//# CombiParam.h: Parameters for a linear combination of Functions //# Copyright (C) 2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_COMBIPARAM_H #define SCIMATH_COMBIPARAM_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // // Parameters for a linear combination of function objects. // // // // // // // // //
      • CombiFunction class // // // // Given N function objects, the class describes a linear combination of the // form: // // f(x) = a(0)*f(0)(x) + a(1)*f(1)(x) + ... + a(N-1)*f(N-1)(x) // // where a = {a(n)} are parameters. If the combi function is used in // a functional fitting process (see // LinearFit) these parameters canm be // solved for. In all aspects they behave as // FunctionParam values. // // Member functions are added with the addFunction() method. // // // // In the following example a second order polynomial is built from 3 separate // polynomials. // // Polynomial constant(0); // Polynomial linear(1); // Polynomial square(2); // // constant.setCoefficient(0, 1.0); // 1 // linear.setCoefficient(1, 1.0); // x // square[2] = 1.0; // x^2 // // CombiParam combination; // // // form function, e0 + e1*x + e2*x^2 // combination.addFunction(constant); // combination.addFunction(linear); // combination.addFunction(square); // // // //
      • T should have standard numerical operators and exp() function. Current // implementation only tested for real types. //
      • To obtain derivatives, the derivatives should be defined. // // //
      • AipsError if dimensions of functions added different // // // This class was created to allow specialization of the evaluation in // a simple way. // // // //
      • Nothing I know of // template class CombiParam : public Function { public: //# Constructors // The default constructor -- no functions, no parameters, nothing, the // function operator returns a 0. CombiParam(); // Make this object a (deep) copy of other. // CombiParam(const CombiParam &other); CombiParam(const CombiParam &other, Bool) : Function(other), ndim_p(other.ndim_p), functionPtr_p(other.functionPtr_p.nelements()) { for (uInt i=0; i CombiParam(const CombiParam &other) : Function(other), ndim_p(other.ndim()), functionPtr_p(other.nFunctions()) { for (uInt i=0; i CombiParam(const CombiParam &other, Bool) : Function(other), ndim_p(other.ndim()), functionPtr_p(other.nFunctions()) { for (uInt i=0; i // Make this object a (deep) copy of other. CombiParam &operator=(const CombiParam &other); // Destructor virtual ~CombiParam(); //# Operators //# Member functions // Give name of function virtual const String &name() const { static String x("combi"); return x; } // Add a function. All functions must have the same ndim() // as the first one. Returns the (zero relative) number (i) // of the function just added. // The default initial parameter value (a(i)) is // initialized to 1. The parameter mask is set True. uInt addFunction(const Function &newFunction); // Return the total number of functions. The number is equal to the // number of functions that have been added. uInt nFunctions() const { return nparameters(); } // Return a reference to a specific Function in the combination. // const Function &function(uInt which) const { DebugAssert(nFunctions() > which, AipsError); return *(functionPtr_p[which]); } const Function &function(uInt which) { DebugAssert(nFunctions() > which, AipsError); return *(functionPtr_p[which]); } // // Returns the dimension of functions in the linear combination virtual uInt ndim() const { return ndim_p; } protected: //# Data // Number of dimensions of underlying functions uInt ndim_p; // Pointer to each added function PtrBlock *> functionPtr_p; //# Make members of parent classes known. public: using Function::nparameters; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/CombiParam.tcc000066400000000000000000000060251476623553700217510ustar00rootroot00000000000000//# CombiParam.cc: Parameters for a linear combination of Functions //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_COMBIPARAM_TCC #define SCIMATH_COMBIPARAM_TCC //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template CombiParam::CombiParam() : Function(), ndim_p(0), functionPtr_p(0) {} template CombiParam::CombiParam(const CombiParam &other) : Function(other), ndim_p(other.ndim_p), functionPtr_p(other.functionPtr_p.nelements()) { for (uInt i=0; i CombiParam::~CombiParam() { for (uInt i=0; i CombiParam& CombiParam::operator=(const CombiParam &other) { if (this != &other) { Function::operator=(other); ndim_p = other.ndim_p; for (uInt i=0; i *>(other.functionPtr_p.nelements()); for (uInt i=0; i uInt CombiParam::addFunction(const Function &newFunction) { if (functionPtr_p.nelements() != 0 && newFunction.ndim() != ndim_p) { throw(AipsError("CombiParam::addFunction() -- " "Inconsistent function dimension")); } // Add the function uInt i = functionPtr_p.nelements(); functionPtr_p.resize(i + 1); functionPtr_p[i] = newFunction.clone(); ndim_p = (*(functionPtr_p[i])).ndim(); // Set parameters this->param_p = FunctionParam(i+1); for (uInt j=0; jparam_p[j] = T(1.0); return i; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/CompiledFunction.h000066400000000000000000000141331476623553700226560ustar00rootroot00000000000000//# CompiledFunction.h: Form a linear combination of Functions //# Copyright (C) 2002,2004,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_COMPILEDFUNCTION_H #define SCIMATH_COMPILEDFUNCTION_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // // Form a linear combination of function objects. // // // // // // // // //
      • Function class // // // // Given a string describing an expression // (see FuncExpression class for // details of the expression), the CompiledFunctionclass wraps // this expression as a // Function (see Function class) which can // be used in all places where functions can be used (e.g. see // Fitting). // // The CompiledParam class takes // care of the parameter interface. // // // // In the following example a Gaussian profile with three parameters // (height, center and halfwidth) is specified and its value and // derivatives with respect to the parameters are calculated at // x=[1.9,2,2.1]. // // // the Gaussian // CompiledFunction prof; // prof.setFunction("p0*exp(-((x-p1)/p2)^2)"); // prof[0] = 2; // the height // prof[1] = 1.5; // the center // prof[2] = 1; // the width // Vector x(3); // x[0] = 1.9; x[1] = 2.0; x[2] = 2.1; // for (uInt i=0; i<3; ++i) { // cout << "Gaussian at x=" << x[i] << ": " << prof(x[i]) << endl; // } // // Calculate automatic derivatives of same function: // CompiledFunction > profad; // profad.setFunction("p0*exp(-((x-p1)/p2)^2)"); // // Set the parameters (note the specification of the number of // // derivatives and which derivative the parameter is) // profad[0] = AutoDiff(2, 3,0); // the height // profad[1] = AutoDiff(1.5,3,1); // the center // profad[2] = AutoDiff(1, 3,2); // the width // for (uInt i=0; i<3; ++i) { // cout << "Gaussian at x=" << x[i] << ": " << profad(x[i]) << endl; // } // cout << "Value (x=2): " << profad(x[1]).value() << endl; // cout << "Derivatives: " << profad(x[1]).derivatives() << endl; // cout << "Derivative1: " << profad(x[1]).derivatives()[1] << endl; // // will produce the output: // // Gaussian at x=1.9: 1.70429 // Gaussian at x=2: 1.5576 // Gaussian at x=2.1: 1.39535 // Gaussian at x=1.9: (1.70429, [0.852144, 1.36343, 0.545372]) // Gaussian at x=2: (1.5576, [0.778801, 1.5576, 0.778801]) // Gaussian at x=2.1: (1.39535, [0.697676, 1.67442, 1.00465]) // Value (x=2): 1.5576 // Derivatives: [0.778801, 1.5576, 0.778801] // Derivative1: 1.5576 // // // //
      • T should have standard numerical operators and functions. //
      • To obtain derivatives, the derivatives should be defined. // // // // // // This class was created to allow specialization of the function evaluation in // a simple way. // // // //
      • Nothing I know of // template class CompiledFunction : public CompiledParam { public: //# Constructors // The default constructor -- no functions, no parameters, nothing, the // function operator returns a 0. CompiledFunction() : CompiledParam() {} // Make this object a (deep) copy of other. // CompiledFunction(const CompiledFunction &other) : CompiledParam(other) {} template CompiledFunction(const CompiledFunction &other) : CompiledParam(other) {} // // Make this object a (deep) copy of other. CompiledFunction &operator=(const CompiledFunction &other) { CompiledParam::operator=(other); return *this; } // Destructor virtual ~CompiledFunction() {} //# Operators // Evaluate the function at x. virtual T eval(typename Function::FunctionArg x) const; //# Member functions // Return a copy of this object from the heap. The caller is responsible for // deleting the pointer. // virtual Function *clone() const { return new CompiledFunction(*this); } virtual Function::DiffType> *cloneAD() const { return new CompiledFunction::DiffType>(*this); } virtual Function::BaseType> *cloneNonAD() const { return new CompiledFunction::BaseType>(*this); } // }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/CompiledFunction.tcc000066400000000000000000000174011476623553700232010ustar00rootroot00000000000000//# CompiledFunction.cc: Form a linear combination of Functions //# Copyright (C) 2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_COMPILEDFUNCTION_TCC #define SCIMATH_COMPILEDFUNCTION_TCC //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Operators template T CompiledFunction::eval(typename Function::FunctionArg x) const { String error_p = ""; T res(0); if (!this->functionPtr_p) { error_p = "No CompiledFunction specified"; return res; } vector exec_p; exec_p.resize(0); vector::const_iterator constp = this->functionPtr_p->getConst().begin(); for (vector::const_iterator pos = this->functionPtr_p->getCode().begin(); pos != this->functionPtr_p->getCode().end(); pos++) { T t(0); if (pos->narg == 2 || (pos->code == FuncExprData::ATAN && pos->state.argcnt == 2)) { t = exec_p.back(); exec_p.pop_back(); } switch (pos->code) { case FuncExprData::UNAMIN: exec_p.back() = -exec_p.back(); case FuncExprData::UNAPLUS: break; case FuncExprData::POW: exec_p.back() = pow(exec_p.back(), t); break; case FuncExprData::GTE: exec_p.back() = exec_p.back() >= t ? T(1) : T(0); break; case FuncExprData::LTE: exec_p.back() = exec_p.back() <= t ? T(1) : T(0); break; case FuncExprData::EQ: exec_p.back() = exec_p.back() == t ? T(1) : T(0); break; case FuncExprData::NEQ: exec_p.back() = exec_p.back() != t ? T(1) : T(0); break; case FuncExprData::OR: exec_p.back() = (exec_p.back() != T(0) || t != T(0)) ? T(1) : T(0); break; case FuncExprData::AND: exec_p.back() = (t*exec_p.back() != T(0)) ? T(1) : T(0); break; case FuncExprData::ADD: exec_p.back() += t; break; case FuncExprData::SUB: exec_p.back() -= t; break; case FuncExprData::MUL: exec_p.back() *= t; break; case FuncExprData::DIV: exec_p.back() /= t; break; case FuncExprData::CONDEX3: exec_p.back() = t; break; case FuncExprData::CONST: exec_p.push_back(T(constp[pos->info])); break; case FuncExprData::PARAM: exec_p.push_back(T(this->param_p[pos->info])); break; case FuncExprData::ARG: exec_p.push_back(T(x[pos->info])); break; case FuncExprData::TOIMAG: NumericTraits::setValue(exec_p.back(), NumericTraits::getValue(exec_p.back(), 0), 1); NumericTraits::setValue(exec_p.back(), typename NumericTraits::BaseType(0.0), 0); break; case FuncExprData::NOP: break; case FuncExprData::GOTO: pos += pos->info - (static_cast(pos-this->functionPtr_p->getCode().begin())+1); break; case FuncExprData::GOTOF: if (exec_p.back() == T(0.0)) { pos += pos->info - (static_cast(pos-this->functionPtr_p->getCode().begin())+1); } break; case FuncExprData::GOTOT: if (exec_p.back() != T(0.0)) { pos += pos->info - (static_cast(pos-this->functionPtr_p->getCode().begin())+1); } break; case FuncExprData::SIN: exec_p.back() = sin(exec_p.back()); break; case FuncExprData::COS: exec_p.back() = cos(exec_p.back()); break; case FuncExprData::ATAN: if (pos->state.argcnt == 1) { exec_p.back() = atan(exec_p.back()); break; } CASACORE_FALLTHROUGH; case FuncExprData::ATAN2: exec_p.back() = atan2(exec_p.back(), t); break; case FuncExprData::ASIN: exec_p.back() = asin(exec_p.back()); break; case FuncExprData::ACOS: exec_p.back() = acos(exec_p.back()); break; case FuncExprData::EXP: exec_p.back() = exp(exec_p.back()); break; case FuncExprData::EXP2: exec_p.back() = exp(exec_p.back()* static_cast::BaseType> (M_LN2)); break; case FuncExprData::EXP10: exec_p.back() = exp(exec_p.back()* static_cast::BaseType> (M_LN10)); break; case FuncExprData::LOG: exec_p.back() = log(exec_p.back()); break; case FuncExprData::LOG2: exec_p.back() = log(exec_p.back())/ static_cast::BaseType>(M_LN2); break; case FuncExprData::LOG10: exec_p.back() = log10(exec_p.back()); break; case FuncExprData::ERF: exec_p.back() = erf(exec_p.back()); break; case FuncExprData::ERFC: exec_p.back() = erfc(exec_p.back()); break; case FuncExprData::PI: { if (pos->state.argcnt == 0) { exec_p.push_back(T(static_cast::BaseType> (M_PI))); } else { exec_p.back() *= static_cast::BaseType> (M_PI); } break; } case FuncExprData::EE: { if (pos->state.argcnt == 0) { exec_p.push_back(T(static_cast::BaseType> (M_E))); } else { exec_p.back() *= static_cast::BaseType> (M_E); } break; } case FuncExprData::ABS: exec_p.back() = abs(exec_p.back()); break; case FuncExprData::FLOOR: exec_p.back() = floor(exec_p.back()); break; case FuncExprData::CEIL: exec_p.back() = ceil(exec_p.back()); break; case FuncExprData::ROUND: exec_p.back() = floor(exec_p.back()+T(0.5)); break; case FuncExprData::INT: if (exec_p.back() < T(0)) exec_p.back() = floor(exec_p.back()); else exec_p.back() = ceil(exec_p.back()); break; case FuncExprData::FRACT: if (exec_p.back() < T(0)) exec_p.back() -= ceil(exec_p.back()); else exec_p.back() -= floor(exec_p.back()); break; case FuncExprData::SQRT: exec_p.back() = sqrt(exec_p.back()); break; case FuncExprData::REAL: break; case FuncExprData::IMAG: exec_p.back() = T(0); break; case FuncExprData::AMPL: break; case FuncExprData::PHASE: exec_p.back() = T(0); break; default: error_p = String("Unknown execution code '") + pos->name + "': programming error"; break; } } if (exec_p.size() != 1 && error_p.empty()) error_p = "No value returned"; if (error_p.empty()) res = exec_p.back(); return res; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/CompiledParam.h000066400000000000000000000130011476623553700221220ustar00rootroot00000000000000//# CompiledParam.h: Parameters for a compiled string function //# Copyright (C) 2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_COMPILEDPARAM_H #define SCIMATH_COMPILEDPARAM_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Parameters for a compiled string function object. // // // // // // // // //
      • FuncExpression class // // // // Given a string describing an expression // (see FuncExpression class for // details of the expression), the CompiledFunctionclass wraps // this expression as a // Function (see Function class) which can // be used in all places where functions can be used (e.g. see // Fitting). // // This class takes care of the // CompiledFunction parameter interface // (see FunctionParam class for details). // // // // In the following example a Gaussian profile with three parameters // (height, center and halfwidth) is specified and its value and // derivatives with respect to the parameters are calculated at x=2. // // // the Gaussian // CompiledFunction prof("p0*exp(-((x-p1)/p2)^2)"); // prof[0] = 2; // the height // prof[1] = 1.5; // the center // prof[2] = 1; // the width // Vector x(3); // X[0] = 1.9; x[1] = 2.0; x[2] = 2.1; // cout << "Gaussian at x=" << x << ": " << prof(x) << endl; // // and an automatic derivative one: // CompiledFunction > profad("p0*exp(-((x-p1)/p2)^2)"); // cout << "Gaussian at x=" << x << ": " << profad(x) << endl; // // will produce the output: // // // // //
      • T should have standard numerical operators and functions. //
      • To obtain derivatives, the derivatives should be defined. // // // // // This class was created to allow specialization of the function evaluation in // a simple way. // // // //
      • Nothing I know of // template class CompiledParam : public Function { public: //# Constructors // The default constructor -- no functions, no parameters, nothing, the // function operator returns a 0. CompiledParam(); // Make this object a (deep) copy of other. // CompiledParam(const CompiledParam &other); template CompiledParam(const CompiledParam &other) : Function(other), ndim_p(other.ndim()), msg_p(other.errorMessage()), text_p(other.getText()), functionPtr_p(new FuncExpression(*other.getFunctionPtr())) {} // // Make this object a (deep) copy of other. CompiledParam &operator=(const CompiledParam &other); // Destructor virtual ~CompiledParam(); //# Operators //# Member functions // Give name of function virtual const String &name() const { static String x("compiled"); return x; } // Set a function. The return will be False (and an error message will be // set) if a compilation error occurs Bool setFunction(const String &newFunction); // Return the error message of the compilation const String &errorMessage() const { return msg_p; } // Return the expression const FuncExpression &function() const; // Returns the dimension of function virtual uInt ndim() const { return ndim_p; } // Returns the text of the function string const String &getText() const { return text_p; } // Returns the function pointer (for debugging) const FuncExpression* getFunctionPtr() const { return functionPtr_p; } protected: //# Data // Number of dimensions of underlying function uInt ndim_p; // Possible error message String msg_p; // Input text string String text_p; // Pointer to function FuncExpression *functionPtr_p; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/CompiledParam.tcc000066400000000000000000000056221476623553700224560ustar00rootroot00000000000000//# CompiledParam.cc: Parameters for a compiled string Functions //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_COMPILEDPARAM_TCC #define SCIMATH_COMPILEDPARAM_TCC //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template CompiledParam::CompiledParam() : Function(), ndim_p(0), msg_p(), text_p(), functionPtr_p(0) {} template CompiledParam::CompiledParam(const CompiledParam &other) : Function(other), ndim_p(other.ndim_p), msg_p(other.msg_p), text_p(other.text_p), functionPtr_p(new FuncExpression(*other.functionPtr_p)) {} template CompiledParam::~CompiledParam() { if(functionPtr_p) delete functionPtr_p; functionPtr_p = 0; } template CompiledParam& CompiledParam::operator=(const CompiledParam &other) { if (this != &other) { if(functionPtr_p) delete functionPtr_p; functionPtr_p = 0; ndim_p = other.ndim_p; msg_p = other.msg_p; text_p = other.text_p; functionPtr_p = new FuncExpression(*other.functionPtr_p); } return *this; } template Bool CompiledParam::setFunction(const String &newFunction) { // Add the function if(functionPtr_p) delete functionPtr_p; functionPtr_p=0; functionPtr_p = new FuncExpression(); ndim_p = 0; msg_p = ""; text_p = ""; if (!functionPtr_p->create(newFunction)) { this->param_p = FunctionParam(0); msg_p = functionPtr_p->errorMessage(); delete functionPtr_p; functionPtr_p=0; return False; } ndim_p = functionPtr_p->getNdim(); this->param_p = FunctionParam(functionPtr_p->getNpar()); text_p = newFunction; return True; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/Compound2Function.tcc000066400000000000000000000111141476623553700233060ustar00rootroot00000000000000//# Compound2Function.cc: Compound of functions AutoDiff specialization //# Copyright (C) 2001,2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_COMPOUND2FUNCTION_TCC #define SCIMATH_COMPOUND2FUNCTION_TCC //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Operators template AutoDiff CompoundFunction >:: eval(typename Function >::FunctionArg x) const { if (this->parset_p) fromParam_p(); AutoDiff tmp(T(0), this->nparameters()); tmp.value() = 0; for (uInt j=0; jnFunctions(); ++i) { AutoDiff t = this->function(i)(x); tmp.value() += t.value(); for (uInt j=0; jparoff_p[i]+j) += t.deriv(j); } } return tmp; } //# Member functions template uInt CompoundFunction >:: addFunction(const Function > &newFunction) { uInt nf = CompoundParam >::addFunction(newFunction); toParam_p(); return nf; } template void CompoundFunction >::fromParam_p() const { if (this->parset_p) { for (uInt i=0; inparameters(); ++i) { uInt k = this->functionPtr_p[this->funpar_p[i]]->nparameters(); uInt l = (*this->functionPtr_p[this->funpar_p[i]])[this->locpar_p[i]].nDerivatives(); // Set correct number of derivatives in sub-functions if (this->param_p[i].nDerivatives() < this->paroff_p[this->funpar_p[i]] + k) { if (l != 0) (*this->functionPtr_p[this->funpar_p[i]])[this->locpar_p[i]] = AutoDiff(); l = 0; } else if (k != l) { (*this->functionPtr_p[this->funpar_p[i]])[this->locpar_p[i]] = AutoDiff(T(0), k); l = k; } // Set the parameter data for (uInt j=0; jfunctionPtr_p[this->funpar_p[i]])[this->locpar_p[i]].deriv(j) = this->param_p[i].deriv(j+this->paroff_p[this->funpar_p[i]]); } (*this->functionPtr_p[this->funpar_p[i]])[this->locpar_p[i]].value() = this->param_p[i].value(); this->functionPtr_p[this->funpar_p[i]]->mask(this->locpar_p[i]) = this->param_p.mask(i); } this->parset_p = False; } } template void CompoundFunction >::toParam_p() { for (uInt i=0; inparameters(); ++i) { // Set derivatives if (this->nparameters() != this->param_p[i].nDerivatives()) { this->param_p[i] = AutoDiff(this->param_p[i].value(), this->nparameters()); } uInt k = this->functionPtr_p[this->funpar_p[i]]->nparameters(); uInt l = (*this->functionPtr_p[this->funpar_p[i]])[this->locpar_p[i]].nDerivatives(); // Set correct number of derivatives in sub-functions if (this->param_p[i].nDerivatives() < this->paroff_p[this->funpar_p[i]] + k) { if (l != 0) (*this->functionPtr_p[this->funpar_p[i]])[this->locpar_p[i]] = AutoDiff(); l = 0; } else if (k != l) { (*this->functionPtr_p[this->funpar_p[i]])[this->locpar_p[i]] = AutoDiff(T(0), k); l = k; } // Set the parameter data for (uInt j=0; jparam_p[i].deriv(j+this->paroff_p[this->funpar_p[i]]) = (*this->functionPtr_p[this->funpar_p[i]])[this->locpar_p[i]].deriv(j); } this->param_p[i].value() = (*this->functionPtr_p[this->funpar_p[i]])[this->locpar_p[i]].value(); this->param_p.mask(i) = this->functionPtr_p[this->funpar_p[i]]->mask(this->locpar_p[i]); } } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/CompoundFunction.h000066400000000000000000000251401476623553700227060ustar00rootroot00000000000000//# CompoundFunction.h: Sum of a collection of Functions //# Copyright (C) 2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_COMPOUNDFUNCTION_H #define SCIMATH_COMPOUNDFUNCTION_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // // Sum of a collection of Functions which behaves as one Function object. // // // // // //
      • Function class // // // // This class takes an arbitrary number of Function objects, and generates // a new, single function object. The parameters of the compound object // are the union of all the parameters in the input objects. // // When CompoundFunction is evaluated, the result is the sum of // all the individual function values. // // Member functions are added with the addFunction() method. // // In general the interaction with the function parameters should be through // the overall function parameters (i.e. through the parameters of the // CompoundFunction). If for any reason you want to set the // parameters of an individual function (see e.g. the example in the // Fit2D), call consolidate() before // abd after the actual setting. // // // Check CombiFunction class // for a simple linear combination of function objects // // // // Suppose for some reason we wanted the sum of x^2 plus a gaussian. // We could form it as follows: // // Polynomial x2(2); // x[2] = 1.0; // x^2 // Gaussian1D gauss(1.0, 0.0, 1.0); // e^{-x^2} // CompoundParam sum; // sum == 0.0 // sum.addFunction(x2); // sum == x^2 // sum.addFunction(gauss); // sum == x^2+e^{-x^2} // sum(2.0); // == 4 + e^-4 // CompoundParam[0] = 2.0; // sum ==2x^2+e^{-x^2} // sum(2.0); // == 8 + e^-4 // // // //
      • T should have standard numerical operators and exp() function. Current // implementation only tested for real types. //
      • To obtain derivatives, the derivatives should be defined. // // //
      • AipsError if dimensions of functions added different // // // This class was created to allow a non-linear least squares fitter to fit a // (potentially) arbitrary number of functions (typically Gaussians). // // // //
      • Nothing I know of // template class CompoundFunction : public CompoundParam { public: //# Constructors // The default constructor -- no functions, no parameters, nothing, the // function operator returns a 0. CompoundFunction() : CompoundParam() {} // Make this object a (deep) copy of other. If parameters have been set // without an intervening calculation, a consolidate() could // be necessary on other first. // CompoundFunction(const CompoundFunction &other) : CompoundParam(other) {} CompoundFunction(const CompoundFunction &other, Bool) : CompoundParam(other, True) {} template CompoundFunction(const CompoundFunction &other) : CompoundParam(other) {} template CompoundFunction(const CompoundFunction &other, Bool) : CompoundParam(other, True) {} // // Make this object a (deep) copy of other. CompoundFunction &operator=(const CompoundFunction &other) { other.fromParam_p(); CompoundParam::operator=(other); return *this; } // Destructor virtual ~CompoundFunction() {} //# Operators // Evaluate the function at x. virtual T eval(typename Function::FunctionArg x) const; //# Member functions // Consolidate the parameter settings. This could be necessary if // parameters have been set, and a copy constructor called. This is // necessary before and after the setting of local parameters; i.e. // the parameters of the individual functions. CompoundFunction &consolidate() { fromParam_p(); toParam_p(); return *this; } // Return a copy of this object from the heap. The caller is responsible for // deleting the pointer. // virtual Function *clone() const { fromParam_p(); return new CompoundFunction(*this); } virtual Function::DiffType> *cloneAD() const { return new CompoundFunction::DiffType>(*this); } virtual Function::BaseType> *cloneNonAD() const { return new CompoundFunction::BaseType> (*this, True); } // private: //# Member functions // Copy the local parameters from general block void fromParam_p() const; // Make the general block from local parameters void toParam_p(); //# Make members of parent classes known. protected: using CompoundParam::parset_p; using CompoundParam::param_p; using CompoundParam::funpar_p; using CompoundParam::locpar_p; using CompoundParam::paroff_p; using CompoundParam::functionPtr_p; public: using CompoundParam::nparameters; using CompoundParam::nFunctions; using CompoundParam::function; }; #define CompoundFunction_PS CompoundFunction // Partial AutoDiff specialization of CompoundFunction // // // The name CompoundFunction_PS is only // for cxx2html documentation problems. Use // CompoundFunction in your code. // template class CompoundFunction_PS > : public CompoundParam > { public: //# Constructors // The default constructor -- no functions, no parameters, nothing, the // function operator returns a 0. CompoundFunction_PS() : CompoundParam >() {} // Make this object a (deep) copy of other. If parameters have been set // without an intervening calculation, a consolidate() could // be necessary on other first. // CompoundFunction_PS(const CompoundFunction_PS > &other) : CompoundParam >(other) {} template CompoundFunction_PS(const CompoundFunction_PS &other) : CompoundParam >(other) {} // // Make this object a (deep) copy of other. CompoundFunction_PS > & operator=(const CompoundFunction_PS > &other) { fromParam_p(); CompoundParam >::operator=(other); return *this; } // Destructor virtual ~CompoundFunction_PS() {} //# Operators // Evaluate the function and its derivatives at x wrt // to the coefficients. virtual AutoDiff eval(typename Function >::FunctionArg x) const; //# Member functions // Add a function to the sum. All functions must have the same // ndim() as the first one. Returns the (zero relative) number // of the function just added. uInt addFunction(const Function > &newFunction); // Consolidate the parameter settings. This could be necessary if // parameters have been set, and a copy constructor called. This is // necessary before and after the setting of local parameters; i.e. // the parameters of the individual functions. CompoundFunction_PS > &consolidate() { fromParam_p(); toParam_p(); return *this; } // Return a copy of this object from the heap. The caller is responsible for // deleting the pointer. // virtual Function > *clone() const { fromParam_p(); return new CompoundFunction >(*this); } virtual Function >::DiffType> *cloneAD() const { return new CompoundFunction >::DiffType> (*this); } virtual Function >::BaseType> *cloneNonAD() const { return new CompoundFunction >::BaseType> (*this, True); } // private: //# Member functions // Copy the local parameters to/from general block void fromParam_p() const; // Make the general block from local parameters void toParam_p(); //# Make members of parent classes known. protected: using CompoundParam >::parset_p; using CompoundParam >::param_p; using CompoundParam >::funpar_p; using CompoundParam >::locpar_p; using CompoundParam >::paroff_p; using CompoundParam >::functionPtr_p; public: using CompoundParam >::nparameters; using CompoundParam >::nFunctions; using CompoundParam >::function; }; #undef CompoundFunction_PS } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/CompoundFunction.tcc000066400000000000000000000043611476623553700232320ustar00rootroot00000000000000//# CompoundFunction.cc: Sum of functions to behave as a single function //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_COMPOUNDFUNCTION_TCC #define SCIMATH_COMPOUNDFUNCTION_TCC //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Operators template T CompoundFunction::eval(typename Function::FunctionArg x) const { if (parset_p) fromParam_p(); T tmp(0); for (uInt i = 0; i void CompoundFunction::fromParam_p() const { if (parset_p) { parset_p = False; for (uInt i=0; imask(locpar_p[i]) = param_p.mask(i); } } } template void CompoundFunction::toParam_p() { for (uInt i=0; imask(locpar_p[i]); } } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/CompoundParam.h000066400000000000000000000174701476623553700221700ustar00rootroot00000000000000//# CompoundParam.h: Parameters for sum of parameterized Functions //# Copyright (C) 2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_COMPOUNDPARAM_H #define SCIMATH_COMPOUNDPARAM_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Parameters for sum of parameterized Functions // // // // // // //
      • Function // // // // This class takes an arbitrary number of Function objects, and generates // a new, single function object. The parameters of the compound object // are the union of all the parameters in the input objects. // // When CompoundFunction is evaluated, the result is the sum of // all the individual function values. // // Note that any Function object (including another Compound object) can be // part of a compound object. // // // // Suppose for some reason we wanted the sum of x^2 plus a gaussian. // We could form it as follows: // // Polynomial x2(2); // x[2] = 1.0; // x^2 // Gaussian1D gauss(1.0, 0.0, 1.0); // e^{-x^2} // CompoundParam sum; // sum == 0.0 // sum.addFunction(x2); // sum == x^2 // sum.addFunction(gauss); // sum == x^2+e^{-x^2} // sum(2.0); // == 4 + e^-4 // CompoundParam[0] = 2.0; // sum ==2x^2+e^{-x^2} // sum(2.0); // == 8 + e^-4 // // Set the height of the gaussian // sum[parameterOffset[1] + Gaussian1D::HEIGHT] = 2.5; // // // //
      • T should have standard numerical operators and exp() function. Current // implementation only tested for real types. //
      • To obtain derivatives, the derivatives should be defined. // // //
      • AipsError if dimensions of functions added different // // // This class was created to allow a non-linear least squares fitter to fit a // (potentially) arbitrary number of functions (typically gaussians). // // // //
      • Nothing I know of // template class CompoundParam : public Function { public: //# Constructors // The default constructor -- no functions, no parameters, nothing, the // function operator returns a 0. CompoundParam(); // Make this object a (deep) copy of other. // CompoundParam(const CompoundParam &other); CompoundParam(const CompoundParam &other, Bool) : Function(other), ndim_p(other.ndim_p), functionPtr_p(other.functionPtr_p.nelements()), paroff_p(other.paroff_p.nelements()), funpar_p(other.funpar_p.nelements()), locpar_p(other.locpar_p.nelements()) { for (uInt i=0; iclone(); paroff_p[i] = other.paroff_p[i]; } for (uInt i=0; i CompoundParam(const CompoundParam &other) : Function(other), ndim_p(other.ndim()), functionPtr_p(other.nFunctions()), paroff_p(other.nFunctions()), funpar_p(other.nparameters()), locpar_p(other.nparameters()) { for (uInt i=0; i CompoundParam(const CompoundParam &other, Bool) : Function(other), ndim_p(other.ndim()), functionPtr_p(other.nFunctions()), paroff_p(other.nFunctions()), funpar_p(other.nparameters()), locpar_p(other.nparameters()) { for (uInt i=0; i &operator=(const CompoundParam &other); // virtual ~CompoundParam(); //# Operators //# Member functions // Give name of function virtual const String &name() const { static String x("compound"); return x; } // Add a function to the sum. All functions must have the same // ndim() as the first one. Returns the (zero relative) number // of the function just added. uInt addFunction(const Function &newFunction); // Return the number of functions in the sum. uInt nFunctions() const { return functionPtr_p.nelements(); } // Return a reference to a specific Function. // const Function &function(uInt which) const { DebugAssert(nFunctions() > which, AipsError); return *(functionPtr_p[which]); } // // Get the offset in function parameterlist for function which uInt parameterOffset(uInt which) const { DebugAssert(nFunctions() > which, AipsError); return paroff_p[which]; } // Get the function number belonging to parameter list element which uInt parameterFunction(uInt which) const { DebugAssert(nparameters() > which, AipsError); return funpar_p[which]; } // Return locpar uInt parameterLocation(uInt which) const { DebugAssert(nparameters() > which, AipsError); return locpar_p[which]; } // Returns the dimension of functions in the linear combination virtual uInt ndim() const { return ndim_p; } private: //# Data // Number of dimensions of underlying functions uInt ndim_p; protected: //# Data // Pointer to each added function PtrBlock *> functionPtr_p; // Index of offset for each function to its parameters in general list Block paroff_p; // Index of function belonging to parameter Block funpar_p; // Index of local parameter Block locpar_p; //# Make members of parent classes known. protected: using Function::parset_p; using Function::param_p; public: using Function::nparameters; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/CompoundParam.tcc000066400000000000000000000103131476623553700224770ustar00rootroot00000000000000//# CompoundParam.cc: Parameters for sum of parameterized Functions //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_COMPOUNDPARAM_TCC #define SCIMATH_COMPOUNDPARAM_TCC //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template CompoundParam::CompoundParam() : Function(), ndim_p(0), functionPtr_p(0), paroff_p(0), funpar_p(0), locpar_p(0) {} template CompoundParam::CompoundParam(const CompoundParam &other) : Function(other), ndim_p(other.ndim_p), functionPtr_p(other.functionPtr_p.nelements()), paroff_p(other.paroff_p.nelements()), funpar_p(other.funpar_p.nelements()), locpar_p(other.locpar_p.nelements()) { for (uInt i=0; iclone(); paroff_p[i] = other.paroff_p[i]; } for (uInt i=0; i CompoundParam::~CompoundParam() { for (uInt i=0; i CompoundParam& CompoundParam:: operator=(const CompoundParam &other) { if (this != &other) { Function::operator=(other); ndim_p = other.ndim_p; for (uInt i=0; i *>(other.functionPtr_p.nelements()); paroff_p = Block(other.paroff_p.nelements()); funpar_p = Block(other.funpar_p.nelements()); locpar_p = Block(other.locpar_p.nelements()); for (uInt i=0; iclone(); paroff_p[i] = other.paroff_p[i]; } for (uInt i=0; i uInt CompoundParam::addFunction(const Function &newFunction) { if (functionPtr_p.nelements() != 0 && newFunction.ndim() != ndim_p) { throw(AipsError("CompoundParam::addFunction() -- " "Inconsistent function dimension")); } // Add the function uInt i = functionPtr_p.nelements(); functionPtr_p.resize(i+1); functionPtr_p[i] = newFunction.clone(); ndim_p = functionPtr_p[i]->ndim(); // Set parameters uInt np = nparameters(); paroff_p.resize(i+1); paroff_p[i] = np; FunctionParam old(param_p); param_p = FunctionParam(np + newFunction.nparameters()); funpar_p.resize(np + newFunction.nparameters()); locpar_p.resize(np + newFunction.nparameters()); for (uInt j=0; j #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // A constant function. // // // // // // // //
      • Function // // // // This class represents a constant function in a space // of arbitrary dimension // f(x0,x1,..,xm-1) = constant // where xi // are independent arguments and m is the number of dimensions of the space. // // // Since the Constant is a Function, the derivatives // can be obtained as well (and are in fact 0 of course). // // The parameter interface (see // FunctionParam class), // is used to provide an interface to the // Fitting classes. // // This class is in general used implicitly by the Constant // class only. // // // // // // form the constant function in 4-D space // Constant constant(4); // 4-dim hyperplane // constant.parameters()[0] = 22; // // Evaluate at x0=5, x3=7 // Vector x(4); // x=0; x[0]=5; x[3]=7; // cout << "constant value: " << constant(x) << endl; // constant value: 22 // // // //
      • T should have standard numerical operators. Current // implementation only tested for real types (and their AutoDiffs). // // //
      • Assertion in debug mode if attempt is made to address incorrect // coefficients // // // This class was created because HyperPlane does not support a constant // offset and modifying that class really required an interface change // (ie that the constant offset be at the beginning of the parameter vector // and that the parameter vector increase by one) so rather than breaking // any code that already used HyperPlane I simply made a trivial Constant // class. // // // //
      • Nothing I know of // template class ConstantND : public ConstantNDParam { public: //# Constructors // Construct a constant in an a space of dimensionality m. By // default, the constant value is initialised to zero, and m=0 explicit ConstantND(const uInt m=0) : ConstantNDParam(m) {;}; // Copy constructor/assignment (deep copy) // ConstantND(const ConstantND &other) : ConstantNDParam(other) {}; template ConstantND(const ConstantND &other) : ConstantNDParam(other) {} ConstantND &operator=(const ConstantND &other) { ConstantNDParam::operator=(other); return *this; }; // // Destructor virtual ~ConstantND() {}; //# Operators // Evaluate the hyper plane function at // (x0,x1,..,xm-1). virtual T eval(typename Function::FunctionArg x) const; // Return a copy of this object from the heap. The caller is responsible for // deleting the pointer. // virtual Function *clone() const { return new ConstantND(*this); }; virtual Function::DiffType> *cloneAD() const { return new ConstantND::DiffType>(*this); }; virtual Function::BaseType> *cloneNonAD() const { return new ConstantND::BaseType>(*this); }; // //# Make members of parent classes known. protected: using ConstantNDParam::param_p; public: using ConstantNDParam::nparameters; }; #define ConstantND_PS ConstantND // Partial specialization of ConstantND for AutoDiff // // // The name HyperPlane_PS is only for cxx2html // documentation problems. Use HyperPlane in your code. // template class ConstantND_PS > : public ConstantNDParam > { public: //# Construct // Constructors a constant in a space of dimensionality m. By // default, the coefficients are initialized to zero, and m=0 explicit ConstantND_PS(const uInt m=0) : ConstantNDParam >(m) {}; // Copy constructor/assignment (deep copy) // ConstantND_PS(const ConstantND_PS > &other) : ConstantNDParam >(other) {}; template ConstantND_PS(const ConstantND_PS &other) : ConstantNDParam >(other) {} ConstantND_PS > & operator=(const ConstantND_PS > &other) { ConstantNDParam >::operator=(other); return *this; }; // // Destructor virtual ~ConstantND() {}; //# Operators // Evaluate the constant function at // (x0,x1,..,xm-1). virtual AutoDiff eval(typename Function >::FunctionArg x) const; // Return a copy of this object from the heap. The caller is responsible for // deleting the pointer. // virtual Function > *clone() const { return new ConstantND_PS >(*this); }; virtual Function >::DiffType> *cloneAD() const { return new ConstantND >::DiffType> (*this); }; virtual Function >::BaseType> *cloneNonAD() const { return new ConstantND >::BaseType> (*this); }; // //# Make members of parent classes known. protected: using ConstantNDParam >::param_p; public: using ConstantNDParam >::nparameters; }; #undef ConstantND_PS } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/ConstantND.tcc000066400000000000000000000033471476623553700217560ustar00rootroot00000000000000//# HyperPlane.cc: Defines HyperPlane //# Copyright (C) 2001,2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_CONSTANTND_TCC #define SCIMATH_CONSTANTND_TCC //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Operators template T ConstantND::eval(typename Function::FunctionArg) const { return param_p[0]; } template AutoDiff ConstantND >:: eval(typename Function >::FunctionArg) const { AutoDiff tmp = param_p[0]; return tmp; } //# Member functions } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/ConstantNDParam.h000066400000000000000000000122721476623553700224120ustar00rootroot00000000000000//# HyperPlaneParam.h: Parameters For a hyper plane function //# Copyright (C) 2001,2002,2004,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_CONSTANTNDPARAM_H #define SCIMATH_CONSTANTNDPARAM_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Parameter handling for a constant function in a space of arbitrary dimensionality. // // // // // // // //
      • FunctionParam class //
      • Function class // // // // This class forms a function of the form // f(x0,x1,..,xm-1) = constant // in an m-dimensional parameter space where // xi are independent arguments. // // Since the Constant is a Function, the derivatives // can be obtained as well (and in fact are 0 of course). // // The parameter interface (see // FunctionParam class), // is used to provide an interface to the // Fitting classes. // // This class is in general used implicitly by the Constant // class only. // // // // // form a constant function in 4-D space // Constant constant(4); // constant in 4-D param space // constant.parameters()[0] = 22; // // Evaluate at x0=5, x3=7 // Vector x(4); // x=0; x[0]=5; x[3]=7; // cout << "constant value: " << constant(x) << endl; // constant value: 22 // // // //
      • T should have standard numerical operators and exp() function. Current // implementation only tested for real types (and their AutoDiffs). // // //
      • Assertion in debug mode if attempt is made to set a negative width //
      • AipsError if incorrect parameter number specified. // // // This class was created because HyperPlane does not support a constant // offset and modifying that class really required an interface change // (ie that the constant offset be at the beginning of the parameter vector // and that the parameter vector increase by one) so rather than breaking // any code that already used HyperPlane I simply made a trivial Constant // class. // // //
      • Nothing I know of // template class ConstantNDParam : public Function { public: //# Constructors // Construct a constant in m-dimensional space. By // default, the constant value is initialized to zero. // explicit ConstantNDParam(uInt m=0); // // Copy constructor (deep copy) // ConstantNDParam(const ConstantNDParam &other); template ConstantNDParam(const ConstantNDParam &other) : Function(other), _ndim(other.ndim()) {} // // Copy assignment (deep copy) ConstantNDParam &operator=(const ConstantNDParam &other); // Destructor virtual ~ConstantNDParam(); //# Operators // Comparisons. // HyperPlanes are equal if they are of the same order and have the same // parameters // Bool operator==(const ConstantNDParam &other) const { return (this->param_p == other.param_p); }; Bool operator!=(const ConstantNDParam &other) const { return (this->param_p != other.param_p); }; // //# Member functions // Give name of function virtual const String &name() const { static String x("constant"); return x; }; // What is the dimension of the parameter list virtual uInt ndim() const { return _ndim; }; //# Make members of parent classes known. protected: using Function::param_p; public: using Function::nparameters; private: uInt _ndim; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/ConstantNDParam.tcc000066400000000000000000000036711476623553700227370ustar00rootroot00000000000000//# HyperPlaneParam.cc: Parameters for a hyperplane function //# Copyright (C) 2001,2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_CONSTANTNDPARAM_TCC #define SCIMATH_CONSTANTNDPARAM_TCC //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template ConstantNDParam::ConstantNDParam(uInt m) : Function(1), _ndim(m) {} template ConstantNDParam::ConstantNDParam(const ConstantNDParam &other) : Function(other), _ndim(other._ndim) {} template ConstantNDParam::~ConstantNDParam() {} template ConstantNDParam & ConstantNDParam::operator=(const ConstantNDParam &other) { if (this != &other) { Function::operator=(other); _ndim = other._ndim; } return *this; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/DiracDFunction.h000066400000000000000000000111241476623553700222450ustar00rootroot00000000000000//# DiracDFunction.h: A one dimensional Dirac delta function //# Copyright (C) 2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_DIRACDFUNCTION_H #define SCIMATH_DIRACDFUNCTION_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // A one dimensional Dirac delta function // // // // // //
      • DiracDParam //
      • Function // // // A 1-dimensional Dirac delta. // // // A DiracD is described by a height, a center and a width // (halfwidth). The value is: // // height (|x-center| == 0.0) // 0 (|x-center| != 0.0) // // The parameters are enumerated by HEIGHT, CENTER and WIDTH. They have // default values of (1, 0). // // // // // DiracDFunction sf(5.0, 25.0); // sf(25); // = 5.0 // // // //
      • T should have standard numerical operators // // //
      • AipsError if incorrect parameter number specified. // // template class DiracDFunction : public DiracDParam { public: //# Constructors // Constructs the DiracDFunction, Defaults: // height=1, center=0. // Could not use default arguments // that worked both with gcc and IRIX // DiracDFunction() : DiracDParam() {} explicit DiracDFunction(const T &height) : DiracDParam(height) {} DiracDFunction(const T &height, const T ¢er) : DiracDParam(height, center) {} // // Copy constructor (deep copy) // DiracDFunction(const DiracDFunction &other) : DiracDParam(other) {} template DiracDFunction(const DiracDFunction &other) : DiracDParam(other) {} // // Copy assignment (deep copy) DiracDFunction &operator=(const DiracDFunction &other) { DiracDParam::operator=(other); return *this; } // Destructor virtual ~DiracDFunction() {} //# Operators // Evaluate the DiracD at x. // If a vector is used as the argument only its first element is used. // virtual T eval(typename Function::FunctionArg x) const; // //# Member functions // Return a copy of this object from the heap. The caller is responsible // for deleting this pointer. // virtual Function *clone() const { return new DiracDFunction(*this); } virtual Function::DiffType> *cloneAD() const { return new DiracDFunction::DiffType>(*this); } virtual Function::BaseType> *cloneNonAD() const { return new DiracDFunction::BaseType>(*this); } // //# Make members of parent classes known. protected: using DiracDParam::param_p; public: using DiracDParam::nparameters; using DiracDParam::HEIGHT; using DiracDParam::CENTER; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/DiracDFunction.tcc000066400000000000000000000033661476623553700226000ustar00rootroot00000000000000//# DiracDFunction.cc: A one dimensional Dirac delta function //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_DIRACDFUNCTION_TCC #define SCIMATH_DIRACDFUNCTION_TCC #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Operators template T DiracDFunction::eval(typename Function::FunctionArg x) const { T tmp(x[0] - param_p[CENTER]); if (tmp == T(0.0)) return param_p[HEIGHT]; return T(0.0); } //# Member functions } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/DiracDParam.h000066400000000000000000000074241476623553700215300ustar00rootroot00000000000000//# DiracDParam.h: A one dimensional Dirac delta function //# Copyright (C) 2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_DIRACDPARAM_H #define SCIMATH_DIRACDPARAM_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // A one dimensional Dirac delta function // // // // // //
      • FunctionParam class //
      • Function class // // // A 1-dimensional Dirac delta. // // // A DiracD is described by a height and a center // The value is: // // height (|x-center| == 0.0) // 0 (|x-center| != 0.0) // // The parameters are enumerated by HEIGHT, CENTER. They have // default values of (1, 0). // // // // // DiracDFunction sf(5.0, 25.0); // sf(25); // = 5.0 // // // //
      • T should have standard numerical operators // // //
      • AipsError if incorrect parameter number specified. // template class DiracDParam : public Function { public: //# Enumerations // Parameter numbers enum { HEIGHT=0, CENTER}; //# Constructors // Constructs the DiracD, Defaults: // height=1, center=0. // Could not use default arguments // that worked both with gcc and IRIX // DiracDParam(); explicit DiracDParam(const T &height); DiracDParam(const T &height, const T ¢er); // // Copy constructor (deep copy) // DiracDParam(const DiracDParam &other); template DiracDParam(const DiracDParam &other) : Function(other) {} // // Copy assignment (deep copy) DiracDParam &operator=(const DiracDParam &other); // Destructor virtual ~DiracDParam(); //# Operators virtual uInt ndim() const { return 1; } //# Member functions // Give name of function virtual const String &name() const { static String x("diracdelta"); return x; } //# Make members of parent classes known. protected: using Function::param_p; public: using Function::nparameters; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/DiracDParam.tcc000066400000000000000000000042511476623553700220450ustar00rootroot00000000000000//# DiracDParam.cc: A one dimensional Dirac delta function //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_DIRACDPARAM_TCC #define SCIMATH_DIRACDPARAM_TCC //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template DiracDParam::DiracDParam() : Function(2) { param_p[HEIGHT] = T(1.0); param_p[CENTER] = T(0.0); } template DiracDParam::DiracDParam(const T &height) : Function(2) { param_p[HEIGHT] = height; param_p[CENTER] = T(0.0); } template DiracDParam::DiracDParam(const T &height, const T ¢er) : Function(2) { param_p[HEIGHT] = height; param_p[CENTER] = center; } template DiracDParam::DiracDParam(const DiracDParam &other) : Function(other) {} template DiracDParam::~DiracDParam() {} //# Operators template DiracDParam &DiracDParam::operator=(const DiracDParam &other) { if (this != &other) Function::operator=(other); return *this; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/EclecticFunctionFactory.h000066400000000000000000000110171476623553700241630ustar00rootroot00000000000000//# EclecticFunctionFactory.cc: a class for creating various Function objects from Records //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_ECLECTICFUNCTIONFACTORY_H #define SCIMATH_ECLECTICFUNCTIONFACTORY_H #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class Record; // // // // // // // // // // // //
      • FunctionFactory // // // // This class is based on the Factory pattern, similar to the // ApplicationObjectFactory // // // // // // // // // // // // // // // // // // // // // // //
      • Function must have a constructor for the form T(const Record&) // // // //
      • UnrecognizedFunctionError by create() if the Record field // "functype" does not match a Function added via addFactory() //
      • InvalidSerializationError by create() if //
          //
        • Record does not contain a "functype" field containing // a string //
        • the associated specific factory throws an // InvalidSerializationError //
        // // // //
      • //
      • //
      • // template class EclecticFunctionFactory : public FunctionFactory { public: // create an empty EclecticFunctionFactory EclecticFunctionFactory(); // create a shallow copy of another EclecticFunctionFactory EclecticFunctionFactory(const EclecticFunctionFactory& factory); // delete this EclecticFunctionFactory. Those specific factories added // via addFactory() with own=True will be deleted. virtual ~EclecticFunctionFactory(); // create the Function object described in the given Record. This // implementation will use the value of the "functype" field to lookup // the specific factory to use to create the function. That is, the // the "functype" value will be matched against the type names loaded // via addFactory(). virtual Function *create(const Record&) const throw(FunctionFactoryError); // add a factory for creating a specific type of function, associating // it with a given "functype" name. void addFactory(const String& type, FunctionFactory *factory, Bool own=True); // return the number of factories that have been loaded thus far. Int ndefined() { return lookup.ndefined(); } // return True if a factory with a given "functype" name has been // loaded. Bool isDefined(const String& type) { return lookup.isDefined(type); } // a shallow assignment operator EclecticFunctionFactory& operator=(const EclecticFunctionFactory& factory); protected: private: OrderedMap*, Bool> > lookup; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/EclecticFunctionFactory.tcc000066400000000000000000000060131476623553700245050ustar00rootroot00000000000000//# EclecticFunctionFactory.cc: a class for creating various Function objects from Records //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_ECLECTICFUNCTIONFACTORY_TCC #define SCIMATH_ECLECTICFUNCTIONFACTORY_TCC #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template EclecticFunctionFactory::EclecticFunctionFactory() : FunctionFactory(), lookup(OrderedPair*, Bool>(0,False)) { } template EclecticFunctionFactory::~EclecticFunctionFactory() { MapIter*, Bool> > iter(lookup); OrderedPair*, Bool> val; for(; ! iter.atEnd(); ++iter) { val = iter.getVal(); if (val.x() != 0 && val.y()) delete val.x(); } } template Function *EclecticFunctionFactory::create(const Record& gr) const throw(FunctionFactoryError) { if (! gr.isDefined("functype")) throw InvalidSerializationError("No functype field defined"); // try { String ftype; ftype = gr.asString(RecordFieldId("functype")); if(!ftype.size() ){ throw InvalidSerializationError("Empty value for functype field"); } if (! lookup.isDefined(ftype)) throw UnrecognizedFunctionError(ftype); FunctionFactory *fac = lookup(ftype).x(); if (fac == 0) throw UnrecognizedFunctionError(ftype); return fac->create(gr); // } catch (AipsError(x)) { // throw InvalidSerializationError("Wrong type for functype field"); // } } template void EclecticFunctionFactory::addFactory(const String& type, FunctionFactory *factory, Bool own) { lookup.define(type, OrderedPair*, Bool>(factory, own)); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/EvenPolynomial.h000066400000000000000000000155471476623553700223670ustar00rootroot00000000000000//# EvenPolynomial.h: A one dimensional even polynomial class //# Copyright (C) 2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_EVENPOLYNOMIAL_H #define SCIMATH_EVENPOLYNOMIAL_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // A one dimensional odd polynomial class // // // // //
      • Function // // // // An EvenPolynomial contains a set of coefficients; // its fundamental operation is evaluating itself at some "x". // The number of coefficients is the order of the polynomial divided by two, // plus one, so is the number of available parameters. // // // // // // EvenPolynomial pf(3); // Second order polynomial - coeffs 0 by default // pf.setCoefficient(0, 1.0); // pf[1] = 2.0; // 2x^2 + 1x^0 // pf(2); // == 8 // // // //
      • T should have standard numerical operators. Current // implementation only tested for real types (and their AutoDiffs). // // //
      • Assertion in debug mode if attempt is made to address incorrect // coefficients // // //
      • Nothing I know of // template class EvenPolynomial: public EvenPolynomialParam { public: //# Enumerations //# Constructors // Constructs a zeroth order polynomial, with a coeficcient of 0.0. EvenPolynomial() : EvenPolynomialParam() {} // Makes a polynomial of the given order, with all coeficcients set to // zero. explicit EvenPolynomial(uInt order) : EvenPolynomialParam(order) {} // Copy constructor/assignment (deep copy) // EvenPolynomial(const EvenPolynomial &other) : EvenPolynomialParam(other) {} template EvenPolynomial(const EvenPolynomial &other) : EvenPolynomialParam(other) {} EvenPolynomial &operator=(const EvenPolynomial &other) { EvenPolynomialParam::operator=(other); return *this; } // // Destructor virtual ~EvenPolynomial() {} //# Operators // Evaluate the polynomial at x. virtual T eval(typename Function1D::FunctionArg x) const; //# Member functions // Return a copy of this object from the heap. The caller is responsible for // deleting the pointer. // virtual Function *clone() const { return new EvenPolynomial(*this); } virtual Function::DiffType> *cloneAD() const { return new EvenPolynomial::DiffType>(*this); } virtual Function::BaseType> *cloneNonAD() const { return new EvenPolynomial::BaseType>(*this); } // //# Make members of parent classes known. protected: using EvenPolynomialParam::param_p; public: using EvenPolynomialParam::nparameters; }; #define EvenPolynomial_PS EvenPolynomial // Partial specialization of EvenPolynomial for AutoDiff // // // The name EvenPolynomial_PS is only for cxx2html // documentation problems. Use EvenPolynomial in your code. // template class EvenPolynomial_PS > : public EvenPolynomialParam > { public: //# Constructors // Constructs one dimensional EvenPolynomials. // EvenPolynomial_PS() : EvenPolynomialParam >() {} explicit EvenPolynomial_PS(uInt order) : EvenPolynomialParam >(order) {} // // Copy constructor (deep copy) // EvenPolynomial_PS(const EvenPolynomial_PS > &other) : EvenPolynomialParam >(other) {} template EvenPolynomial_PS(const EvenPolynomial_PS &other) : EvenPolynomialParam >(other) {} // // Copy assignment (deep copy) EvenPolynomial_PS > & operator=(const EvenPolynomial_PS > &other) { EvenPolynomialParam >::operator=(other); return *this; } // Destructor virtual ~EvenPolynomial_PS() {} //# Operators // Evaluate the polynomial and its derivatives at x wrt // to the coefficients. // virtual AutoDiff eval(typename Function >::FunctionArg x) const; // //# Member functions // Return a copy of this object from the heap. The caller is responsible // for deleting this pointer. // virtual Function > *clone() const { return new EvenPolynomial >(*this); } virtual Function >::DiffType> *cloneAD() const { return new EvenPolynomial >::DiffType> (*this); } virtual Function >::BaseType> *cloneNonAD() const { return new EvenPolynomial >::BaseType> (*this); } // //# Make members of parent classes known. protected: using EvenPolynomialParam >::param_p; public: using EvenPolynomialParam >::nparameters; }; #undef EvenPolynomial_PS } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/EvenPolynomial.tcc000066400000000000000000000033511476623553700226770ustar00rootroot00000000000000//# EvenPolynomial.cc: A one dimensional odd polynomial class //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_EVENPOLYNOMIAL_TCC #define SCIMATH_EVENPOLYNOMIAL_TCC //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Operators template T EvenPolynomial::eval(typename Function1D::FunctionArg x) const { Int j = nparameters(); T accum = param_p[--j]; while (--j >= 0) { accum *= x[0]; accum *= x[0]; accum += param_p[j]; } return accum; } //# Member functions } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/EvenPolynomial2.tcc000066400000000000000000000044721476623553700227660ustar00rootroot00000000000000//# EvenPolynomial2.cc: Even polynomial class specialized for AutoDiff //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_EVENPOLYNOMIAL2_TCC #define SCIMATH_EVENPOLYNOMIAL2_TCC //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Operators template AutoDiff EvenPolynomial >:: eval(typename Function >::FunctionArg x) const { AutoDiff tmp; for (uInt i=0; inparameters(); ++i) { if (this->param_p[i].nDerivatives() > 0) { tmp = this->param_p[i]; break; } } // function value Int j = this->nparameters(); tmp.value() = this->param_p[--j].value(); while (--j >= 0) { tmp.value() *= x[0]; tmp.value() *= x[0]; tmp.value() += this->param_p[j].value(); } // get derivatives (assuming either all or none) if (tmp.nDerivatives()>0) { for (uInt j=0; jnparameters(); ++i) { if (this->param_p.mask(i)) tmp.deriv(i) = dev; dev *= x[0]; dev *= x[0]; } } return tmp; } //# Member functions } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/EvenPolynomialParam.h000066400000000000000000000125611476623553700233410ustar00rootroot00000000000000//# EvenPolynomialParam.h: Parameter handling for even polynomials //# Copyright (C) 2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_EVENPOLYNOMIALPARAM_H #define SCIMATH_EVENPOLYNOMIALPARAM_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Parameter handling for even polynomials // // // // //
      • FunctionParam class //
      • Function1D // // // A 1-dimensional EvenPolynomial's parameters. // // // // An EvenPolynomial is described by a set of coefficients; // its fundamental operation is evaluating itself at some "x". // The number of coefficients is the order of the polynomial divided // by two, plus one. // // Since the EvenPolynomial is a Function, // the derivatives can be obtained as well. // // The parameter interface (see // FunctionParam class), // is used to provide an interface to the // Fitting classes. // // This class is in general used implicitly by the EvenPolynomial // class only. // // // // // EvenPolynomial pf(3); // Second order polynomial - coeffs 0 by default // pf.setCoefficient(0, 1.0); // pf[1] = 2.0; // 2x^2 + 1x^0 // pf(2); // == 8 // // // //
      • T should have standard numerical operators. Current // implementation only tested for real types (and their AutoDiffs). // // //
      • Assertion in debug mode if attempt is made to address incorrect // coefficients // // //
      • Nothing I know of // template class EvenPolynomialParam: public Function1D { public: //# Constructors // Constructs a zero'th order polynomial, with a coeficcient of 0.0. EvenPolynomialParam(); // Makes a polynomial of the given order, with all coeficcients set to // zero. explicit EvenPolynomialParam(uInt order); // Make this a copy of other (deep copy). // EvenPolynomialParam(const EvenPolynomialParam &other); template EvenPolynomialParam(const EvenPolynomialParam &other) : Function1D(other) {} EvenPolynomialParam &operator=(const EvenPolynomialParam &other); // // Destructor ~EvenPolynomialParam(); //# Operators // Comparisons. // EvenPolynomials are equal if they are the same order // Bool operator==(const EvenPolynomialParam &other) const { return (param_p == other.param_p); } Bool operator!=(const EvenPolynomialParam &other) const { return (param_p != other.param_p); } // //# Member functions // Give name of function virtual const String &name() const { static String x("evenpolynomial"); return x; } // What is the order of the polynomial, i.e. maximum exponent of "x". uInt order() const { return 2*param_p.nelements() - 2; } // What is the which'th coefficient of the polynomial. For an nth // degree polynomial, which varies between zero and n/2. T coefficient(uInt which) const { DebugAssert(which<=order(), AipsError); return param_p[which]; } // Return all the coefficients as a vector. const Vector &coefficients() const; // Set the which'th coefficient to value. void setCoefficient(uInt which, const T value) { DebugAssert(which<=order(), AipsError); param_p[which] = value; } // Set all the coefficients at once, throw away all existing coefficients. void setCoefficients(const Vector &coefficients); //# Make members of parent classes known. protected: using Function1D::param_p; public: using Function1D::nparameters; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/EvenPolynomialParam.tcc000066400000000000000000000045031476623553700236600ustar00rootroot00000000000000//# EvenPolynomialParam.cc: Parameter handling for even polynomials //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_EVENPOLYNOMIALPARAM_TCC #define SCIMATH_EVENPOLYNOMIALPARAM_TCC //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template EvenPolynomialParam::EvenPolynomialParam() : Function1D(1) {} template EvenPolynomialParam::EvenPolynomialParam(uInt order) : Function1D(order/2 + 1) {} template EvenPolynomialParam::EvenPolynomialParam(const EvenPolynomialParam &other) : Function1D(other) {} template EvenPolynomialParam::~EvenPolynomialParam() {} template EvenPolynomialParam & EvenPolynomialParam::operator=(const EvenPolynomialParam &other) { if (this != &other) Function1D::operator=(other); return *this; } template const Vector &EvenPolynomialParam::coefficients() const { return param_p.getParameters(); } template void EvenPolynomialParam::setCoefficients(const Vector &coefficients) { param_p.setParameters(coefficients); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/FuncExprData.cc000066400000000000000000000177471476623553700221140ustar00rootroot00000000000000//# FuncExprData.cc: Data and enumerations for functional expressions //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors FuncExprData::FuncExprData() : una2_p(), una1_p(), bin2_p(), bin1_p(), spop_p(), func_p() { ExprCompState state = {0,0,0,0}; static const ExprOperator olist[] = { {UNAMIN, "-", UNA1, RTLPRI, 1, 0, 0, 0, NONE, state}, {UNAPLUS, "+", UNA1, RTLPRI, 1, 0, 0, 0, NONE, state}, {NON, "!", UNA1, 28, 1, 0, 0, 0, NONE, state}, {POW, "**", BIN2, RTLPRI+4, 2, 0, 1, 0, NONE, state}, {GTE, ">=", BIN2, 32, 2, 0, 1, 0, NONE, state}, {LTE, "<=", BIN2, 32, 2, 0, 1, 0, NONE, state}, {EQ, "==", BIN2, 32, 2, 0, 1, 0, NONE, state}, {NEQ, "!=", BIN2, 32, 2, 0, 1, 0, NONE, state}, {OR, "||", BIN2, 20, 2, 0, 1, 0, NONE, state}, {AND, "&&", BIN2, 20, 2, 0, 1, 0, NONE, state}, {ADD, "+", BIN1, 36, 2, 0, 1, 0, NONE, state}, {SUB, "-", BIN1, 36, 2, 0, 1, 0, NONE, state}, {MUL, "*", BIN1, 40, 2, 0, 1, 0, NONE, state}, {DIV, "/", BIN1, 40, 2, 0, 1, 0, NONE, state}, {POW, "^", BIN1, RTLPRI+4, 2, 0, 1, 0, NONE, state}, {GT, ">", BIN1, 32, 2, 0, 1, 0, NONE, state}, {LT, "<", BIN1, 32, 2, 0, 1, 0, NONE, state}, {CONDEX, "?", BIN1, 16, 2, 0, 1, 0, SAVENV, state}, {CONDEX3, "CONDEX3", BIN1, 16, 2, 0, 1, 0, NONE, state}, {CONDEX2, ":", SPEC, 17, 2, 0, 1, 0, FINAL, state}, {CONST, "CONST", SPEC, SPCPRI, 0, 0,-1, 0, NONE, state}, {PARAM, "PARAM", SPEC, SPCPRI, 0, 0,-1, 0, NONE, state}, {ARG, "ARG", SPEC, SPCPRI, 0, 0,-1, 0, NONE, state}, {TOIMAG, "TOIMAG", SPEC, SPCPRI, 0, 0, 0, 0, NONE, state}, {LBRACE, "{", SPEC, SPCPRI, 0, 0, 0, 0, NONE, state}, {RBRACE, "}", SPEC, FINPRI, 0, 0, 0, 0, NONE, state}, {LPAREN, "(", SPEC, SPCPRI, 0, 0, 0, 0, SAVENV, state}, {RPAREN, ")", SPEC, FINPRI, 0, 0, 0, 0, FINAL, state}, {LBR, "[", SPEC, SPCPRI, 0, 0, 0, 0, SAVENV, state}, {RBR, "]", SPEC, FINPRI, 0, 0, 0, 0, FINAL, state}, {COMMA, ",", BIN1, FINPRI, 0, 0, 0, 0, FINAL, state}, {FINISH, "FINISH", SPEC, FINPRI, 0, 0, 0, 0, FINAL, state}, {GOTO, "GOTO", SPEC, FINPRI, 0, 0, 0, 0, GOTOPC, state}, {GOTOF, "GOTOF", SPEC, FINPRI, 0, 0, 0, 0, GOTOPC, state}, {GOTOT, "GOTOT", SPEC, FINPRI, 0, 0, 0, 0, GOTOPC, state}, {SIN, "sin", FUNC, SPCPRI, 1, 1, 1, 0, SAVENV, state}, {COS, "cos", FUNC, SPCPRI, 1, 1, 1, 0, SAVENV, state}, {ATAN, "atan", FUNC, SPCPRI, 1, 2, 1, 0, SAVENV, state}, {ATAN2, "atan2", FUNC, SPCPRI, 2, 2, 1, 0, SAVENV, state}, {ASIN, "asin", FUNC, SPCPRI, 1, 1, 1, 0, SAVENV, state}, {ACOS, "acos", FUNC, SPCPRI, 1, 1, 1, 0, SAVENV, state}, {EXP, "exp", FUNC, SPCPRI, 1, 1, 1, 0, SAVENV, state}, {EXP2, "exp2", FUNC, SPCPRI, 1, 1, 1, 0, SAVENV, state}, {EXP10, "exp10", FUNC, SPCPRI, 1, 1, 1, 0, SAVENV, state}, {LOG, "ln", FUNC, SPCPRI, 1, 1, 1, 0, SAVENV, state}, {LOG2, "log2", FUNC, SPCPRI, 1, 1, 1, 0, SAVENV, state}, {LOG10, "log", FUNC, SPCPRI, 1, 1, 1, 0, SAVENV, state}, {ERF, "erf", FUNC, SPCPRI, 1, 1, 1, 0, SAVENV, state}, {ERFC, "erfc", FUNC, SPCPRI, 1, 1, 1, 0, SAVENV, state}, {PI, "pi", FUNC, SPCPRI, 0, 1, 1, 0, SAVENV, state}, {EE, "ee", FUNC, SPCPRI, 0, 1, 1, 0, SAVENV, state}, {ABS, "abs", FUNC, SPCPRI, 1, 1, 1, 0, SAVENV, state}, {FLOOR, "floor", FUNC, SPCPRI, 1, 1, 1, 0, SAVENV, state}, {CEIL, "ceil", FUNC, SPCPRI, 1, 1, 1, 0, SAVENV, state}, {ROUND, "round", FUNC, SPCPRI, 1, 1, 1, 0, SAVENV, state}, {INT, "int", FUNC, SPCPRI, 1, 1, 1, 0, SAVENV, state}, {FRACT, "fract", FUNC, SPCPRI, 1, 1, 1, 0, SAVENV, state}, {SQRT, "sqrt", FUNC, SPCPRI, 1, 1, 1, 0, SAVENV, state}, {COMPLEX, "complex", FUNC, SPCPRI, 1, 1, 1, 0, SAVENV, state}, {REAL, "real", FUNC, SPCPRI, 1, 1, 1, 0, SAVENV, state}, {IMAG, "imag", FUNC, SPCPRI, 1, 1, 1, 0, SAVENV, state}, {AMPL, "ampl", FUNC, SPCPRI, 1, 1, 1, 0, SAVENV, state}, {PHASE, "phase", FUNC, SPCPRI, 1, 1, 1, 0, SAVENV, state}, // End of list {NOP, "NOP", SPEC, FINPRI, 0, 0, 0, 0, NONE, state} }; uInt i = 0; for (i=0; olist[i].code != NOP; ++i) { switch (olist[i].category) { case UNA2: una2_p[olist[i].name] = olist[i]; break; case UNA1: una1_p[olist[i].name] = olist[i]; break; case BIN2: bin2_p[olist[i].name] = olist[i]; break; case BIN1: bin1_p[olist[i].name] = olist[i]; break; case SPEC: spop_p[olist[i].name] = olist[i]; break; case FUNC: func_p[olist[i].name] = olist[i]; break; default: break; } allop_p[olist[i].code] = olist[i]; } spop_p[olist[i].name] = olist[i]; allop_p[olist[i].code] = olist[i]; } //# Operators //# Member functions void FuncExprData::print(ostream &os, const map &m) const { for (map::const_iterator pos = m.begin(); pos != m.end(); pos++) print(os, pos->second); } void FuncExprData::print(ostream &os, const FuncExprData::ExprOperator &pos) const { os << setfill('0') << setw(2) << pos.code << ": " << pos.name << setfill(' ') << setw(9-pos.name.length()) << ":" << pos.category << ":" << setfill('0') << setw(2) << pos.priority << ":" << pos.narg << ":" << setfill('0') << setw(2) << pos.nresult << ":" << pos.info << ":" << endl; } //# Global functions ostream &operator<<(ostream &os, const FuncExprData &ed) { os << "Unary operators with 2 characters:" << endl; ed.print(os, ed.unary2()); os << "Unary operators with 1 character:" << endl; ed.print(os, ed.unary1()); os << "Binary operators with 2 characters:" << endl; ed.print(os, ed.binary2()); os << "Binary operators with 1 character:" << endl; ed.print(os, ed.binary1()); os << "Special operations:" << endl; ed.print(os, ed.special()); os << "Functions:" << endl; ed.print(os, ed.function()); return os; } } //# NAMESPACE CASACORE - END casacore-3.7.1/scimath/Functionals/FuncExprData.h000066400000000000000000000144331476623553700217430ustar00rootroot00000000000000//# FuncExprData.h: Data and enumerations for functional expressions //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_FUNCEXPRDATA_H #define SCIMATH_FUNCEXPRDATA_H //# Includes #include #include #include //# Forward Declarations #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Data and enumerations for functional expressions // // // // // //
      • Function class // // // // This class provides enumerations for expression analysis; and data element // descriptions // // // // // // // To tie the Glish language to non-linear fitting procedures // // // //
      • // // // //
      • nothing directly // class FuncExprData { public: //# Enumerations // Operations enum opTypes { NOP=0, UNAMIN, UNAPLUS, NON, POW, GTE, LTE, EQ, NEQ, OR, AND, CONDEX, CONDEX2, CONDEX3, ADD, SUB, MUL, DIV, LT, GT, CONST, PARAM, ARG, TOIMAG, LBRACE, RBRACE, LPAREN, RPAREN, LBR, RBR, COMMA, FINISH, GOTO, GOTOF, GOTOT, SIN, COS, ATAN, ATAN2, ASIN, ACOS, EXP, EXP10, EXP2, LOG, LOG10, LOG2, ERF, ERFC, PI, EE, ABS, FLOOR, CEIL, ROUND, INT, FRACT, SQRT, COMPLEX, REAL, IMAG, AMPL, PHASE, // Number NopTypes }; // Operation category enum opCategories { // Unary, binary 1 or 2 character UNA2, UNA1, BIN2, BIN1, // Special and functions SPEC, FUNC, // Number NopCategories }; // Special categories enum specAction { NONE, // Save environment while compiling SAVENV, // Indicate a GOTO GOTOPC, // Final expression codes FINAL }; // Special priority levels enum specPriority { // Lowest priority at which right-to-left rather than left-to-right // execution RTLPRI = 44, // Priority for specials - start SPCPRI = 60, // Priority for finals FINPRI = 00 }; // The compilation state descriptor struct ExprCompState { // Old index of low RPS boundary uInt rpslow; // # of values available on value stack uInt nval; // Argument count uInt argcnt; // Previous saved program counter uInt pcptr; }; // The operator description: code; priority; # of arguments; # of arguments // used up (or produced for functions) struct ExprOperator { // The operator code opTypes code; // The name (or characters) String name; // The category opCategories category; // Execution priority uInt priority; // # of arguments necessary (or minimum) uInt narg; // max # of arguments (for function) uInt nmaxarg; // # of results produced/used Int nresult; // code info (like par/x index; jump distance Int info; // special action specAction special; // state ExprCompState state; }; //# Constructors // Construct the data for the expression analysis FuncExprData(); // Destructor ~FuncExprData() {} //# Member functions // Accessors of the various maps // map &unary2() { return una2_p; } const map &unary2() const { return una2_p; } map &unary1() { return una1_p; } const map &unary1() const { return una1_p; } map &binary2() { return bin2_p; } const map &binary2() const { return bin2_p; } map &binary1() { return bin1_p; } const map &binary1() const { return bin1_p; } map &special() { return spop_p; } const map &special() const { return spop_p; } map &function() { return func_p; } const map &function() const { return func_p; } // // Print an operator map void print(ostream &os, const map &m) const; // Print an operation void print(ostream &os, const FuncExprData::ExprOperator &pos) const; private: //# Data // Unary operators of 2 characters map una2_p; // Unary operators of 1 character map una1_p; // Binary operators of 2 characters map bin2_p; // Binary operators of 1 character map bin1_p; // Special operators map spop_p; // Function names map func_p; // All operators map allop_p; }; //# Global Functions // Output function // // Show a list of all defined operators and functions ostream &operator<<(ostream &os, const FuncExprData &ed); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/FuncExpression.cc000066400000000000000000000423171476623553700225320ustar00rootroot00000000000000//# FuncExpression.cc: An expression executable as function //# Copyright (C) 2001,2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors FuncExpression::FuncExpression() : exd(), error_p(), code_p(), rps_p(), const_p(), npar_p(0), ndim_p(0), exec_p () { initState(); } FuncExpression::FuncExpression(const String &prog) : exd(), error_p(), code_p(), rps_p(), const_p(), npar_p(0), ndim_p(0), exec_p () { initState(); if (!create(prog)) { throw(AipsError(String("Illegal program string in " "FuncExpression ctor:\n") + error_p)); } } FuncExpression::FuncExpression(const FuncExpression &other) : exd(other.exd), error_p(other.error_p), code_p(other.code_p), rps_p(other.rps_p), const_p(other.const_p), npar_p(other.npar_p), ndim_p(other.ndim_p), exec_p () { initState(); } FuncExpression &FuncExpression::operator=(const FuncExpression &other) { if (this != &other) { exd = other.exd; error_p = other.error_p; code_p = other.code_p; rps_p = other.rps_p; const_p = other.const_p; npar_p = other.npar_p; ndim_p = other.ndim_p; exec_p.resize(0); initState(); } return *this; } //# Member functions Bool FuncExpression::create(const String &prog) { // Initialise program error_p = ""; code_p.resize(0); rps_p.resize(0); initState(); const_p.resize(0); MUString prg(prog); prg.skipBlank(); while (!prg.eos()) { // Skip blank prg.skipBlank(); // Compile a statement if (!compStmt(prg)) { code_p.resize(0); error_p += " at: \n'" + prg.get(0, prg.getPtr()) + "''" + prg.get() + "'"; return False; } } return (setOp(exd.special()["FINISH"])); } Bool FuncExpression::compStmt(MUString &prg) { /// Only expressions now return (compExpr(prg)); } Bool FuncExpression::compExpr(MUString &prg) { prg.skipBlank(); String t; // Check unary t = prg.get().at(0,2); while (exd.unary1().find(t.at(0,1)) != exd.unary1().end() || exd.unary2().find(t) != exd.unary2().end()) { if (exd.unary2().find(t) != exd.unary2().end()) { if (!setOp(exd.unary2().find(t)->second)) return False; prg.skipChar(); } else { if (!setOp(exd.unary1().find(t.at(0,1))->second)) return False; } prg.skipChar(); prg.skipBlank(); t = prg.get().at(0,2); } if (!compTerm(prg)) return False; // Get binary prg.skipBlank(); if (prg.testChar(':')) { prg.skipChar(); if (!setOp(exd.special()[":"])) return False; if (!compExpr(prg)) return False; prg.skipBlank(); } t = prg.get().at(0,2); while (!prg.eos() && (exd.binary1().find(t.at(0,1)) != exd.binary1().end() || exd.binary2().find(t) != exd.binary2().end())) { if (exd.binary2().find(t) != exd.binary2().end()) { if (!setOp(exd.binary2().find(t)->second)) return False; prg.skipChar(); } else { if (!setOp(exd.binary1().find(t.at(0,1))->second)) return False; } prg.skipChar(); if (!compExpr(prg)) return False; prg.skipBlank(); t = prg.get().at(0,2); } return True; } Bool FuncExpression::compTerm(MUString &prg) { prg.skipBlank(); // Get a value if (prg.testChar('(')) { prg.skipChar(); if (!setOp(exd.special()["("])) return False; prg.skipBlank(); if (!compExpr(prg)) return False; prg.skipBlank(); if (!prg.testChar(')')) { error_p = "Missing closing right parenthesis"; return False; } prg.skipChar(); if (!setOp(exd.special()[")"])) return False; } else if (prg.testAlpha()) { Regex parrx("p[0-9]*$"); Regex argrx("x[0-9]*$"); String t = prg.getAlphaNum(); t.downcase(); MUString tmu(t); if (exd.function().find(t) != exd.function().end()) { if (!setOp(exd.function().find(t)->second)) return False; prg.skipBlank(); if (prg.testChar('(')) { prg.skipChar(); if (!compExpr(prg)) return False; prg.skipBlank(); if (!prg.testChar(')')) { error_p = "No closing function paranethesis"; return False; } prg.skipChar(); } if (!setOp(exd.special()[")"])) return False; } else if (t.matches(parrx) || t.matches(argrx)) { tmu.skipChar(); uInt n = tmu.getuInt(); prg.skipBlank(); if (prg.testChar('[')) { prg.skipChar(); prg.skipBlank(); uInt m = prg.getuInt(); if (m == 0) { error_p = "Illegal index for argument or parameter"; return False; } n += m-1; prg.skipBlank(); if (!prg.testChar(']')) { error_p = "Missing closing bracket"; return False; } prg.skipChar(); } FuncExprData::ExprOperator oper; if (t.matches(parrx)) { oper = exd.special()["PARAM"]; if (n >= npar_p) npar_p = n+1; } else { oper = exd.special()["ARG"]; if (n >= ndim_p) ndim_p = n+1; } oper.info = n; if (!setOp(oper)) return False; } else { error_p = String("Unknown function name ") + t; return False; } } else if (prg.testDouble()) { Double d = prg.getDouble(); FuncExprData::ExprOperator oper; oper = exd.special()["CONST"]; oper.info = const_p.size(); if (!setVal(d)) return False; if (!setCode(oper)) return False; if (prg.testChar('i')) { prg.skipChar(); oper = exd.special()["TOIMAG"]; if (!setCode(oper)) return False; } } else { error_p = "Missing value"; return False; } return True; } Bool FuncExpression::setOp(FuncExprData::ExprOperator &oper) { // Check the work stack for priorities while (rps_p.size() > state_p.rpslow) { // High new priority or equal and left-to-right: roll up stack if (oper.priority < rps_p.back().priority || (oper.priority == rps_p.back().priority && oper.priority < FuncExprData::RTLPRI)) { if (!setCode(rps_p.back())) return False; // Are there enough values to operate upon? if (state_p.nval < rps_p.back().narg) { error_p = "Not enough operands for operator '"; error_p += rps_p.back().name + "'"; return False; } state_p.nval -= rps_p.back().nresult; rps_p.pop_back(); } else break; } // Add the new code FuncExprData::ExprOperator gotoit; switch (oper.special) { case FuncExprData::SAVENV: { oper.state = state_p; state_p.nval =0; state_p.rpslow = rps_p.size()+1; state_p.argcnt = 0; rps_p.push_back(oper); if (oper.code == FuncExprData::CONDEX) { if (!setCode(exd.special()["GOTOF"])) return False; code_p.back().state = state_p; state_p.pcptr = static_cast(code_p.end()-code_p.begin()); } } break; case FuncExprData::FINAL: { switch (oper.code) { case FuncExprData::CONDEX2: { if (rps_p.size() != state_p.rpslow || state_p.rpslow < 1) { error_p = "':' not expected"; return False; } if (rps_p[state_p.rpslow-1].code != FuncExprData::CONDEX) { error_p = "No '?' belonging to a ':' found"; return False; } if (state_p.nval != 1) { error_p = "No value between '?' and ':'"; return False; } state_p.rpslow = rps_p[state_p.rpslow-1].state.rpslow; rps_p.pop_back(); code_p[state_p.pcptr-1].info = static_cast(code_p.end()-code_p.begin())+1; if (!setCode(exd.special()["GOTO"])) return False; code_p.back().state = state_p; code_p.back().state.pcptr = code_p[state_p.pcptr-1].state.pcptr; state_p.pcptr = static_cast(code_p.end()-code_p.begin()); if (!setOp(exd.binary1()["CONDEX3"])) return False; } break; case FuncExprData::COMMA: { if (rps_p.size() != state_p.rpslow || state_p.rpslow < 1 || rps_p[state_p.rpslow-1].category != FuncExprData::FUNC) { error_p = "Parameter comma separator not expected"; return False; } rps_p[state_p.rpslow-1].state.argcnt += state_p.nval; state_p.nval = 0; } break; case FuncExprData::FINISH: { if (rps_p.size() != state_p.rpslow || state_p.rpslow != 0 || state_p.nval != 1) { error_p = "Unexpected EOS"; return False; } } break; case FuncExprData::RPAREN: { if (rps_p.size() != state_p.rpslow || state_p.rpslow < 1) { error_p = "Right parenthesis not expected"; return False; } if (rps_p[state_p.rpslow-1].code == FuncExprData::LPAREN) { if (state_p.nval != 1) { error_p = "No value between ()"; return False; } state_p.nval += rps_p[state_p.rpslow-1].state.nval; state_p.rpslow = rps_p[state_p.rpslow-1].state.rpslow; rps_p.pop_back(); } else if (rps_p[state_p.rpslow-1].category == FuncExprData::FUNC) { if (state_p.nval > 1) { error_p = "Incorrect value stack for function evaluation"; return False; } rps_p[state_p.rpslow-1].state.argcnt += state_p.nval; if (rps_p[state_p.rpslow-1].state.argcnt < rps_p[state_p.rpslow-1].narg || rps_p[state_p.rpslow-1].state.argcnt > rps_p[state_p.rpslow-1].nmaxarg) { error_p = "Incorrect number of arguments in function"; return False; } if (!setCode(rps_p[state_p.rpslow-1])) return False; state_p.nval = rps_p.back().state.nval + rps_p.back().nresult; state_p.rpslow = rps_p.back().state.rpslow; state_p.argcnt = 0; rps_p.pop_back(); } else { error_p = "Right parenthesis not expected"; return False; } } break; default : error_p = "Unexpected final code"; return False; } } break; default: rps_p.push_back(oper); break; } return True; } Bool FuncExpression::setVal(const Double &val) { const_p.push_back(val); ++state_p.nval; return True; } Bool FuncExpression::setCode(const FuncExprData::ExprOperator &oper) { code_p.push_back(oper); if (oper.code == FuncExprData::CONDEX3) { code_p[state_p.pcptr-1].info = static_cast(code_p.end()-code_p.begin())-1; state_p.pcptr = code_p[state_p.pcptr-1].state.pcptr; } if (code_p.back().special == FuncExprData::GOTOPC) { code_p.back().state.pcptr = state_p.pcptr; state_p.pcptr = code_p.size()-1; } return True; } void FuncExpression::initState() { state_p.rpslow = 0; state_p.nval = 0; state_p.argcnt = 0; state_p.pcptr = 0; npar_p = 0; ndim_p = 0; } const vector &FuncExpression::getCode() const{ return code_p; } Bool FuncExpression::exec(Double &res) const { error_p = ""; res = Double(0); exec_p.resize(0); vector::const_iterator constp = const_p.begin(); for (vector::const_iterator pos=code_p.begin(); pos != code_p.end(); pos++) { switch (pos->category) { case FuncExprData::UNA1: case FuncExprData::UNA2: { switch (pos->code) { case FuncExprData::UNAMIN: exec_p.back() = -exec_p.back(); case FuncExprData::UNAPLUS: break; default: error_p = String("Unknown execution code '") + pos->name + "': programming error"; break; } break; } case FuncExprData::BIN1: case FuncExprData::BIN2: { Double t(0); if (pos->narg == 2) { t = exec_p.back(); exec_p.pop_back(); } switch (pos->code) { case FuncExprData::POW: exec_p.back() = pow(exec_p.back(), t); break; case FuncExprData::GTE: exec_p.back() = exec_p.back() >= t ? Double(1) : Double(0); break; case FuncExprData::LTE: exec_p.back() = exec_p.back() <= t ? Double(1) : Double(0); break; case FuncExprData::EQ: exec_p.back() = exec_p.back() == t ? Double(1) : Double(0); break; case FuncExprData::NEQ: exec_p.back() = exec_p.back() != t ? Double(1) : Double(0); break; case FuncExprData::OR: exec_p.back() = (exec_p.back() != Double(0) || t != Double(0)) ? Double(1) : Double(0); break; case FuncExprData::AND: exec_p.back() = (t*exec_p.back() != Double(0)) ? Double(1) : Double(0); break; case FuncExprData::ADD: exec_p.back() += t; break; case FuncExprData::SUB: exec_p.back() -= t; break; case FuncExprData::MUL: exec_p.back() *= t; break; case FuncExprData::DIV: exec_p.back() /= t; break; case FuncExprData::CONDEX3: exec_p.back() = t; break; default: error_p = String("Unknown execution code '") + pos->name + "': programming error"; break; } break; } case FuncExprData::SPEC: { switch (pos->code) { case FuncExprData::CONST: exec_p.push_back(constp[pos->info]); break; case FuncExprData::TOIMAG: break; case FuncExprData::NOP: break; case FuncExprData::GOTO: pos += pos->info - (static_cast(pos-code_p.begin())+1); break; case FuncExprData::GOTOF: if (!exec_p.back()) { pos += pos->info - (static_cast(pos-code_p.begin())+1); } break; case FuncExprData::GOTOT: if (exec_p.back()) { pos += pos->info - (static_cast(pos-code_p.begin())+1); } break; default: error_p = String("Unknown execution code '") + pos->name + "': programming error"; break; } break; } case FuncExprData::FUNC: { switch (pos->code) { case FuncExprData::SIN: exec_p.back() = sin(exec_p.back()); break; case FuncExprData::COS: exec_p.back() = cos(exec_p.back()); break; case FuncExprData::ATAN: if (pos->state.argcnt == 1) { exec_p.back() = atan(exec_p.back()); break; } CASACORE_FALLTHROUGH; case FuncExprData::ATAN2: { Double t(exec_p.back()); exec_p.pop_back(); exec_p.back() = atan2(exec_p.back(), t); break; } case FuncExprData::ASIN: exec_p.back() = asin(exec_p.back()); break; case FuncExprData::ACOS: exec_p.back() = acos(exec_p.back()); break; case FuncExprData::EXP: exec_p.back() = exp(exec_p.back()); break; case FuncExprData::EXP2: exec_p.back() = exp(exec_p.back()*M_LN2); break; case FuncExprData::EXP10: exec_p.back() = exp(exec_p.back()*M_LN10); break; case FuncExprData::LOG: exec_p.back() = log(exec_p.back()); break; case FuncExprData::LOG2: exec_p.back() = log(exec_p.back())/M_LN2; break; case FuncExprData::LOG10: exec_p.back() = log10(exec_p.back()); break; case FuncExprData::ERF: exec_p.back() = ::erf(exec_p.back()); break; case FuncExprData::ERFC: exec_p.back() = ::erfc(exec_p.back()); break; case FuncExprData::PI: { if (pos->state.argcnt == 0) exec_p.push_back(M_PI); else exec_p.back() *= M_PI; break; } case FuncExprData::EE: { if (pos->state.argcnt == 0) exec_p.push_back(M_E); else exec_p.back() *= M_E; break; } case FuncExprData::ABS: exec_p.back() = abs(exec_p.back()); break; case FuncExprData::FLOOR: exec_p.back() = floor(exec_p.back()); break; case FuncExprData::CEIL: exec_p.back() = ceil(exec_p.back()); break; case FuncExprData::ROUND: exec_p.back() = floor(exec_p.back()+Double(0.5)); break; case FuncExprData::INT: if (exec_p.back() < 0) exec_p.back() = floor(exec_p.back()); else exec_p.back() = ceil(exec_p.back()); break; case FuncExprData::FRACT: if (exec_p.back() < 0) exec_p.back() -= ceil(exec_p.back()); else exec_p.back() -= floor(exec_p.back()); break; case FuncExprData::SQRT: exec_p.back() = sqrt(exec_p.back()); break; case FuncExprData::REAL: break; case FuncExprData::IMAG: exec_p.back() = Double(0); break; case FuncExprData::AMPL: break; case FuncExprData::PHASE: exec_p.back() = Double(0); break; default: error_p = String("Unknown execution code '") + pos->name + "': programming error"; break; } break; } default: error_p = String("Unknown execution code '") + pos->name + "': programming error"; break; } if (!error_p.empty()) break; } if (exec_p.size() != 1 && error_p.empty()) error_p = "No value returned"; if (error_p.empty()) { res = exec_p.back(); return True; } return False; } void FuncExpression::print(ostream &os) const { for (vector::const_iterator pos=code_p.begin(); pos != code_p.end(); pos++) exd.print(os, *pos); } //# Global functions ostream &operator<<(ostream &os, const FuncExpression &ed) { ed.print(os); return os; } } //# NAMESPACE CASACORE - END casacore-3.7.1/scimath/Functionals/FuncExpression.h000066400000000000000000000132231476623553700223660ustar00rootroot00000000000000//# FuncExpression.h: An expression executable as function //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_FUNCEXPRESSION_H #define SCIMATH_FUNCEXPRESSION_H //# Includes #include #include #include #include #include //# Forward Declarations #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class MUString; // An expression executable as function // // // // // //
      • Function class // // // // This class acts as an interface between a program given as a string (e.g. // from a command line interface) and a // Function class. The grammar of the language // use to express the function is given below. The FuncEXpression // can be used in all places where Functions can be used (like in the // linear and non-linear Fitting classes. // // An expression is created by either supplying a String to a // constructor, or be setting a String. // // // // // // // To tie the Glish language to non-linear fitting procedures // // // //
      • AipsError if an illegal program passed in constructor // // // //
      • nothing directly // class FuncExpression { public: //# Enumerations //# Constructors // Construct an empty executable expression FuncExpression(); // Construct an executable expression from the given string explicit FuncExpression(const String &prog); // Make this object a (deep) copy of other. FuncExpression(const FuncExpression &other); // Make this object a (deep) copy of other. FuncExpression &operator=(const FuncExpression &other); // Destructor ~FuncExpression() {} //# Member functions // Create an executable program Bool create(const String &prog); // Get the current error message const String &errorMessage() { return error_p; } // Get the executable program const vector &getCode() const; // Get the number of parameters in executable program uInt getNpar() const { return npar_p; } // Get the number of dimensions of executable program uInt getNdim() const {return ndim_p; } // Get reference to the compiled program const vector &getCode() { return code_p; } // Get reference to compiled constants const vector &getConst() { return const_p; } // Execute the program Bool exec(Double &res) const; // Print the stack information (mainly for debugging) void print(ostream &os) const; private: //# Data // The expression data /// later into a singleton FuncExprData exd; // The latest error message mutable String error_p; // The executable code stack (a vector, since it is a re-usable stack) vector code_p; // The reverse Polish work stack (a vector, since deque did not work on gcc) vector rps_p; // The current state of the compilation FuncExprData::ExprCompState state_p; // The current constant stack vector const_p; // The number of parameters in code uInt npar_p; // The number of dimensions of expression uInt ndim_p; // Executing stack mutable vector exec_p; //# Member functions // Compile a statement (in prg, which will be adjusted) Bool compStmt(MUString &prg); // Compile an expression (in prg, which will be adjusted) Bool compExpr(MUString &prg); // Compile a term (in prg, which will be adjusted) Bool compTerm(MUString &prg); // Save an operation on compilation RP stack. Bool setOp(FuncExprData::ExprOperator &oper); // Save a value on constant stack. Bool setVal(const Double &val); // Save an executable code Bool setCode(const FuncExprData::ExprOperator &oper); // Initialise the state void initState(); }; //# Global Functions // Output function // // Show the program ostream &operator<<(ostream &os, const FuncExpression &ed); // // Execute function // // Execute the program template T FuncExecute(const Vector &x, const Vector &par); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/Function.h000066400000000000000000000343101476623553700212000ustar00rootroot00000000000000//# Function.h: Numerical functional interface class //# Copyright (C) 2001,2002,2003,2004,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_FUNCTION_H #define SCIMATH_FUNCTION_H //# Includes #include #include #include #include #include #include //# Forward declarations #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations class String; class RecordInterface; // Numerical functional interface class // // // // // //
      • Functional //
      • FunctionParam // // // // A Function is used for classes which map a // scalar or n-dimensional Vector of type T into a T. // The object also has zero or more parameters which can be masked // if necessary, and be used in the Fitting module, and, implicitly, // in the AutoDiff differentiation module. // // The parameter interface is provided by the // FunctionParam class. // // A Function can have a name() which can be used in generic // interfaces. // // The function calls implemented are: //
          //
        • operator()() //
        • operator()(const T &x) //
        • operator()(const Vector &x) //
        • operator()(Function::FunctionArg x) //
        • operator()(const T &x, const T &y) (for 2D) //
        • operator()(const T &x, const T &y, const T &z) (for 3D) //
        // The T in the above is the Function::ArgType // as derived from the FunctionTraits // class. // These calls are (in debug mode) tested for the correct number of arguments, // after which they call a T eval(FunctionArg x) const = 0 to // be implemented in derived classes. The derived class should also implement // an uInt ndim() const = 0. The derived class can access the // nth parameter with the [n] operator, and the corresponding // mask with mask(n) method. // The variables are referenced with x[i]. // //
        // // A complete implementation of say an A.sin(2pi.f.x) with // parameters amplitude(A) and frequency(f) and variable // time(x) could be: // // //# Sinusoid.h // #include // #include // #include // #include // // The sinusoid class // template class Sinusoid : public Function { // public: // // For easy reference of the parameters // enum { AMPL=0, FREQ }; // // Constructors. Defaults are A=1, f=1 // Sinusoid() : Function(2) { // param_p[AMPL] = T(1.0); param_p[FREQ] = T(1.0); } // explicit Sinusoid(const T &l) : Function(2) { // param_p[AMPL] = ampl; param_p[FREQ] = T(1.0); } // Sinusoid(const T &l, const T &freq) : Function(2) { // param_p[AMPL] = ampl; param_p[FREQ] = freq; } // Sinusoid(const Sinusoid &other) : Function(2) { // param_p[AMPL] = other.param_p[AMPL]; // param_p[FREQ] = other.parameter[FREQ]; } // Sinusoid &operator=(const Sinusoid &other) { // if (this != &other) param_p = other.param_p; // return *this; } // virtual ~Sinusoid() {} // // Dimensionality // virtual uInt ndim() const { return 2; } // // Evaluate // virtual T eval(Function::FunctionArg x) const { // return param_p[AMPL]*sin(T(C::_2pi)*param_p[FREQ]*x[0]); } // // Copy it // virtual Function *clone() const { return new Sinusoid(param_p); } // }; // // The following will calculate the value and the derivative for // A=2; f=3; x=0.1; // // // The function objects for value, and for value + derivative // Sinusoid soid1(2.0, 3.0); // typedef AutoDiff Adif; // Sinusoid soid2(Adif(2,2,0), Adif(3,2,1)); // cout << "Value: " << soid1(0.1) << endl; // cout << "(val, deriv): " << soid2(Adif(0.1)) << endl; // // // A shorter version, where all parameter handling is done at user level // could be: // // //# Sinusoid.h // #include // #include // #include // #include // template class Sinusoid : public Function { // public: // enum { AMPL=0, FREQ }; // Sinusoid() : Function(2){param_p[AMPL] T(1);param_p[FREQ]=T(1);} // virtual ~Sinusoid() {} // virtual uInt ndim() const { return 2; } // virtual T eval(Function::FunctionArg x) const { // return param_p[AMPL]*sin(T(C::_2pi)*param_p[FREQ]*x[0]); } // virtual Function *clone() const { return new Sinusoidparam_p; } // }; // // The following will calculate the value and the derivative for // A=2; f=3; x=0.1; // // // The function objects for value, and for value + derivative // typedef AutoDiff Adif; // typedef Function FD; // typedef Function > FAdif // Sinusoid soid1; // Sinusoid soid2; // soid1[FD::AMPL] = 2; soid1[FD::FREQ] = 3; // soid2[FAdif::AMPL] = Adif(2,2,0); // soid2[FAdif::FREQ] = Adif(3,2,1); // cout << "Value: " << soid1(0.1) << endl; // cout << "(val, deriv): " << soid2(Adif(0.1)) << endl; // // // // A function of more than one variable was required for a function which // represents the sky brightness. Adjustable parameters were required for // non-linear least squares fitting. // // // //
      • Besides the requirements set by the // Functional base class, it must be // possible to form a Vector. // // // //
      • At some point, we may want to implement a letter-envelope class, // implement function arithmetic, etc. //
      • use maybe Poolstack for static Vector // template class Function : public Functional::ArgType, U>, public Functional::ArgType>, U> { public: //# Typedefs typedef typename FunctionTraits::ArgType ArgType; typedef const ArgType* FunctionArg; //# Constructors // Constructors // Function() : param_p(), arg_p(0), parset_p(False), locked_p(False) {} explicit Function(const uInt n) : param_p(n), arg_p(0), parset_p(False), locked_p(False) {} explicit Function(const Vector &in) : param_p(in), arg_p(0), parset_p(False), locked_p(False) {} Function(const FunctionParam &other) : param_p(other), arg_p(0), parset_p(False), locked_p(False) {} template Function(const Function &other) : param_p(other.parameters()), arg_p(0), parset_p(other.parsetp()), locked_p(False) {} // // Destructor virtual ~Function() {} // Returns the number of dimensions of function virtual uInt ndim() const = 0; // Returns the number of parameters uInt nparameters() const { return param_p.nelements(); } // Evaluate the function object virtual U eval(FunctionArg x) const = 0; //# Operators // Manipulate the nth parameter (0-based) with no index check // T &operator[](const uInt n) { parset_p |= !locked_p; return param_p[n]; } const T &operator[](const uInt n) const { return param_p[n]; } // // Evaluate this function object at xor at x, y. // The length of x must be greater than or equal to // ndim(). // virtual U operator()() const { DebugAssert(ndim()==0, AipsError); return eval(FunctionArg(0)); } virtual U operator()(const ArgType &x) const { DebugAssert(ndim()<=1, AipsError); return eval(&x); } virtual U operator()(const Vector &x) const; virtual U operator()(FunctionArg x) const { return eval(x); } virtual U operator()(const ArgType &x, const ArgType &y) const; virtual U operator()(const ArgType &x, const ArgType &y, const ArgType &z) const; // //# Member functions // Specify the name associated with the function (default will be // unknown) virtual const String &name() const; // Manipulate the mask associated with the nth parameter // (e.g. to indicate whether the parameter is adjustable or // nonadjustable). // Note: no index check. // Bool &mask(const uInt n) { parset_p |= !locked_p; return param_p.mask(n); } const Bool &mask(const uInt n) const { return param_p.mask(n); } // // Return the parameter interface // const FunctionParam ¶meters() const { return param_p; } FunctionParam ¶meters() { parset_p = True; return param_p; } // // Get arg_p and parset_p. Necessary for reasons // of protection in the copying of non-conforming Functions. // const Vector &argp() const { return arg_p; } Bool parsetp() const { return parset_p; } // // Compiler cannot always find the correct 'const' version of parameter // access. In cases where this would lead to excessive overheads in // moving parameters around (like in CompoundFunction) the // parameter changing can be set to be locked, and no changes are // assumed. // void lockParam() { locked_p = True; } void unlockParam() { locked_p = False; } // // get/set the function mode. These provide an interface to // function-specific configuration or state that controls how the // function calculates its values but otherwise does not qualify as // a parameter. Some part of the state, for example, might have a // type different from that of T. The state is passed as fields of a // record, mode--the names, types and values of which are specific to // the implementing function and should be documented in the implementing // class. It is recommended that all possible inputs passed to this // function via setMode() be considered optional such that if the // record omits a legal field, that part of the state is left unchanged. // Fields not recognized by the implementing class should be ignored. // An exception should be thrown if a recognized field contains illegal // data. The default implementations for both getMode() and setMode() // ignore the input record. // virtual void setMode(const RecordInterface& mode); virtual void getMode(RecordInterface& mode) const; // // return True if the implementing function supports a mode. The default // implementation returns False. virtual Bool hasMode() const; // Print the function (i.e. the parameters) ostream &print(ostream &os) const { return param_p.print(os); } // Return a copy of this object from the heap. The caller is responsible // for deleting this pointer. The cloneAD will return a clone // with an AutoDef; the cloneNonAD a clone // with . An AipsError will be thrown if the // cloneAD() or cloneNonAD() is not implemented // for a specific function. // virtual Function *clone() const = 0; virtual Function::DiffType> *cloneAD() const; virtual Function::BaseType> *cloneNonAD() const; // protected: //# Data // The parameters and masks FunctionParam param_p; // Aid for non-contiguous argument storage mutable Vector arg_p; // Indicate parameter written mutable Bool parset_p; // Indicate that parameters are expected to be locked from changing mutable Bool locked_p; }; //# Global functions // Global functions // // Output declaration template ostream &operator<<(ostream &os, const Function &fun); // //# Inlines template inline ostream &operator<<(ostream &os, const Function &fun) { return fun.print(os); } } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/Function.tcc000066400000000000000000000061531476623553700215260ustar00rootroot00000000000000//# Function.cc: Numerical functional interface class //# Copyright (C) 2001,2002,2003,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_FUNCTION_TCC #define SCIMATH_FUNCTION_TCC //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template U Function::operator()(const Vector &x) const { DebugAssert(ndim()<=x.nelements(), AipsError); if (x.contiguousStorage() || ndim()<2) return this->eval(&(x[0])); uInt j=ndim(); arg_p.resize(j); for (uInt i=0; ieval(&(arg_p[0])); } template U Function::operator()(const ArgType &x, const ArgType &y) const { DebugAssert(ndim()==2, AipsError); arg_p.resize(ndim()); arg_p[0] = x; arg_p[1] = y; return this->eval(&(arg_p[0])); } template U Function::operator()(const ArgType &x, const ArgType &y, const ArgType &z) const { DebugAssert(ndim()==3, AipsError); arg_p.resize(ndim()); arg_p[0] = x; arg_p[1] = y; arg_p[2] = z; return this->eval(&(arg_p[0])); } template const String &Function::name() const { static String x("unknown"); return x; } template void Function::setMode(const RecordInterface&) { } template void Function::getMode(RecordInterface&) const { } template Bool Function::hasMode() const { return False; } template Function::DiffType> * Function::cloneAD() const { throw(AipsError(String("Function `") + name() + "' has no cloneAD() method")); } template Function::BaseType> * Function::cloneNonAD() const { throw(AipsError(String("Function `") + name() + "' has no cloneNonAD() method")); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/Function1D.h000066400000000000000000000066471476623553700214010ustar00rootroot00000000000000//# Function1D.h: Numerical functional interface class for 1 dimension //# Copyright (C) 2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_FUNCTION1D_H #define SCIMATH_FUNCTION1D_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // Numerical functional interface class for 1 dimension // // // // // //
      • Function // // // // A Function1D is used for classes which map a // scalar or n-dimensional Vector of type T into a T. // The object also has one parameter which can be masked // if necessary, and be used in the Fitting module, and, implicitly, // in the AutoDiff differentiation module. // // The only method implemented in Function1D is the // ndim() method. The rest is inhereted from // Function. // // // See Function. // // // //
      • Besides the requirements set by the // Functional base class, it must be // possible to form a Vector. // template class Function1D : public Function { public: //# Typedefs typedef const T* FunctionArg; //# Constructors // Constructors // Function1D() : Function() {} explicit Function1D(const uInt n) : Function(n) {} explicit Function1D(const Vector &in) : Function(in) {} Function1D(const FunctionParam &other) : Function(other) {} template Function1D(const Function1D &other) : Function(other) {} // // Destructor virtual ~Function1D() {} // Returns the number of dimensions of function virtual uInt ndim() const { return 1; } //# Make members of parent classes known. protected: using Function::param_p; public: using Function::nparameters; using Function::setMode; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/FunctionFactoryErrors.cc000066400000000000000000000033301476623553700240610ustar00rootroot00000000000000//# FunctionFactoryErrors: Exception classes for use by FunctionFactories & clients //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include namespace casacore { //# NAMESPACE CASACORE - BEGIN const String InvalidSerializationError::preamble("Invalid function description in record: "); FunctionFactoryError::~FunctionFactoryError() noexcept { } UnrecognizedFunctionError::~UnrecognizedFunctionError( ) noexcept { } InvalidSerializationError::~InvalidSerializationError() noexcept { } FieldNotFoundError::~FieldNotFoundError() noexcept { } } //# NAMESPACE CASACORE - END casacore-3.7.1/scimath/Functionals/FunctionFactoryErrors.h000066400000000000000000000057341476623553700237350ustar00rootroot00000000000000//# FunctionFactoryErrors: Exception classes for use by FunctionFactories & clients //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_FUNCTIONFACTORYERRORS_H #define SCIMATH_FUNCTIONFACTORYERRORS_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class FunctionFactoryError : public AipsError { public: FunctionFactoryError(const String& message,Category c=GENERAL) : AipsError(message,c) {} virtual ~FunctionFactoryError() noexcept; }; class UnrecognizedFunctionError : public FunctionFactoryError { public: // create an exception indicating that the a function of the given name // is not recognized UnrecognizedFunctionError(const String& name, Category c=INVALID_ARGUMENT) : FunctionFactoryError(String("Unrecognized function: ") + name,c), fname(name) {} virtual ~UnrecognizedFunctionError() noexcept; const String& getName() { return fname; } private: String fname; }; class InvalidSerializationError : public FunctionFactoryError { public: // create an exception indicating a Record serialization of a // Function is invalid. The error message will be a "Invalid function // description in record: " + reason. InvalidSerializationError(const String& reason,Category c=GENERAL) : FunctionFactoryError(preamble + reason,c), reas(reason) {} virtual ~InvalidSerializationError() noexcept; const String& getReason() { return reas; } static const String preamble; private: String reas; }; class FieldNotFoundError : public InvalidSerializationError { public: FieldNotFoundError(const String& field,Category c=GENERAL) : InvalidSerializationError(String("No ") + field + " defined",c), fname(field) {} virtual ~FieldNotFoundError() noexcept; private: String fname; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/FunctionHolder.h000066400000000000000000000161361476623553700223440ustar00rootroot00000000000000//# FunctionHolder.h: A holder for Functions to enable record conversions //# Copyright (C) 2002,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_FUNCTIONHOLDER_H #define SCIMATH_FUNCTIONHOLDER_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // A holder for Functions to enable record conversions // // // // //
      • RecordInterface class //
      • Function class // // // // A Holder of general Measures // // // // This class can be used to handle heterogeneous collections of Functions, // e.g. as a Vector. With the aid of the // toRecord() and fromRecord() functions it can be used // to convert a Function object into or from a record. // A FunctionHolder is created from a Function, or can be empty. // // // // // // TableRecord rec; // MDirection dir(MVDirection(Quantity(12.5, 'deg'), Quantity(-2, 'deg')), // MDirection::J2000); // String error; // error message // if (!FunctionHolder(dir).toRecord(error, rec)) { // cout << error << endl; // } // Record grec; // a Record // if (!FunctionHolder(dir).toRecord(error, grec)) { // make record // cout << error << endl; // } // // Note that for GlishRecords use can be made of the // // GlishRecord::to/fromrecord() methods. // // // // // To make general conversions between Functions and records, without knowing // the actual Function being converted. // template class FunctionHolder : public RecordTransformable { public: //# Enumerations // Types of functions enum Types { GAUSSIAN1D, GAUSSIAN2D, GAUSSIAN3D, GAUSSIANND, HYPERPLANE, POLYNOMIAL, EVENPOLYNOMIAL, ODDPOLYNOMIAL, SINUSOID1D, CHEBYSHEV, BUTTERWORTH, COMBINE, COMPOUND, COMPILED, N_Types }; //# Structures // Structure to hold functional status struct FuncStat { // Name String nam; // type Types tp; // Order (True if needed) Bool order; }; //# Constructors // Creates an empty holder FunctionHolder(); // Create from a Function (copy made) FunctionHolder(const Function &in); // Copy a holder (copy semantics) FunctionHolder(const FunctionHolder &other); //# Destructor ~FunctionHolder(); //# Operators // Assignment (copy semantics) FunctionHolder &operator=(const FunctionHolder &other); //# Member Functions // Check the the FunctionHolder holds the specified type. Return // True if if does and False otherwise. // Bool isEmpty() const; // // Get the known names const Vector &names() const; // Get a specific Function from the holder (with lifetime as long // as holder exists). // //
      • AipsError if holder empty //
      • AipsError if holder contains wrong Function // // const Function &asFunction() const; // // Add a function Bool addFunction(const Function &fnc); // Get the type of currently filled holder Types type() const; // Create a Function from a record. An error message is generated, and False // returned if an invalid record is given. A valid record will return True. // A valid record contains at least the following fields (any additional fields are // ignored): //
          //
        • tp = TpString: type of Function (gaussian1d, etc; case // insensitive) -- OR an enumeration code //
        • order = TpInt: the order needed to create a Function (-1 if not // necessary or default) //
        • ndim, npar, params are optional //
        • nfunc, funcs are required for COMBI or COMPOUND //
        // A Function can be created from a string. In that case the string // will only indicate the type of function (like polynomial), and will // create a default polynomial of that given type. // Error messages are postfixed to error. // virtual Bool fromRecord(String &error, const RecordInterface &in); virtual Bool fromString(String &error, const String &in); template Bool getRecord(String &error, Function *&fn, const RecordInterface &in); // // Create a record from a Function. The return will be False and an error // message generated only if the FunctionHolder does not contain a Function. // Error messages are postfixed to error. virtual Bool toRecord(String &error, RecordInterface &out) const; // Get identification of record virtual const String &ident() const; private: //# Data Members // Pointer to a Function std::unique_ptr> hold_p; // Aids (only filled after a succesful to/fromRecord // mutable Types nf_p; mutable Int order_p; mutable String text_p; mutable std::unique_ptr mode_p; // // List of known names mutable Vector nam_p; // Filled list? mutable Bool isFilled; //# Member functions // Initialise and check the name list void init() const; // Aid for to/from Record, String // Bool putType(String &error, RecordInterface &out) const; template Bool getType(String &error, Function *&fn, const RecordInterface &in); template Bool getType(String &error, Function *&fn); void setParameters(Function *&fn, const Vector ¶ms); void setParameters(Function> *&fn, const Vector ¶ms); // }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/FunctionHolder.tcc000066400000000000000000000350741476623553700226700ustar00rootroot00000000000000//# FunctionHolder.cc: A holder for Functions to enable record conversions //# Copyright (C) 2002,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_FUNCTIONHOLDER_TCC #define SCIMATH_FUNCTIONHOLDER_TCC //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors template FunctionHolder::FunctionHolder() : nam_p(N_Types), isFilled(False) {} template FunctionHolder::FunctionHolder(const Function &in) : hold_p(in.clone()), nam_p(N_Types), isFilled(False) { if (in.hasMode()) { mode_p.reset(new Record(RecordInterface::Variable)); in.getMode(*mode_p); } } template FunctionHolder::FunctionHolder(const FunctionHolder &other) : nam_p(N_Types), isFilled(False) { if (other.hold_p) hold_p.reset(other.hold_p->clone()); if (other.mode_p) mode_p.reset(other.mode_p->clone()); } //# Destructor template FunctionHolder::~FunctionHolder() {} //# Operators template FunctionHolder &FunctionHolder:: operator=(const FunctionHolder &other) { if (this != &other) { if (other.hold_p) { hold_p.reset(other.hold_p->clone()); } else { hold_p.reset(); } if (other.mode_p) { mode_p.reset(other.mode_p->clone()); } else { mode_p.reset(); } } return *this; } //# Member Functions template Bool FunctionHolder::isEmpty() const { return (!hold_p); } template const Vector &FunctionHolder::names() const { init(); return nam_p; } template const Function &FunctionHolder::asFunction() const { if (!hold_p) { throw(AipsError("Empty FunctionHolder argument for asFunction")); } return *hold_p; } template Bool FunctionHolder::addFunction(const Function &fnc) { if (nf_p == COMBINE) { dynamic_cast &>(*hold_p).addFunction(fnc); } else if (nf_p == COMPOUND) { dynamic_cast &>(*hold_p).addFunction(fnc); } else return False; return True; } template typename FunctionHolder::Types FunctionHolder::type() const { if (!hold_p) { throw(AipsError("Empty FunctionHolder argument for type()")); } return nf_p; } template void FunctionHolder::init() const { static FuncStat fnc[N_Types] = { { String("gaussian1d"), GAUSSIAN1D, False}, { String("gaussian2d"), GAUSSIAN2D, False}, { String("gaussian3d"), GAUSSIAN3D, False}, { String("gaussianNd"), GAUSSIANND, True}, { String("hyperplane"), HYPERPLANE, True}, { String("polynomial"), POLYNOMIAL, True}, { String("evenpolynomial"), EVENPOLYNOMIAL, True}, { String("oddpolynomial"), ODDPOLYNOMIAL, True}, { String("sinusoid1d"), SINUSOID1D, False}, { String("chebyshev"), CHEBYSHEV, True}, { String("butterworth"), BUTTERWORTH, True}, { String("combine"), COMBINE, False}, { String("compound"), COMPOUND, False}, { String("compiled"), COMPILED, False} }; if (!isFilled) { isFilled = True; for (uInt i=0; i(fnc[i].tp)) { throw(AipsError("Lists in FunctionHolder incorrect order")); } } } } template Bool FunctionHolder::fromRecord(String &error, const RecordInterface &in) { hold_p.reset(); Function *fn(0); if (!getRecord(error, fn, in)) { delete fn; fn = 0; return False; } hold_p.reset(fn); return True; } template template Bool FunctionHolder::getRecord(String &error, Function *&fn, const RecordInterface &in) { try { if (!getType(error, fn, in)) return False; if ((nf_p == COMBINE || nf_p == COMPOUND) && in.isDefined(String("nfunc")) && in.isDefined(String("funcs")) && in.type(in.idToNumber(RecordFieldId("funcs"))) == TpRecord) { Int nfunc; in.get(RecordFieldId("nfunc"), nfunc); Record fnsrec = in.asRecord(RecordFieldId("funcs")); for (Int i=0; i fnch; Function *fnc(0); if (!fnch.getRecord(error, fnc, fnr)) { delete fnc; fnc = 0; return False; } if (nf_p == COMBINE) { dynamic_cast *>(fn)-> addFunction(*fnc); } else { dynamic_cast *>(fn)-> addFunction(*fnc); } delete fnc; fnc = 0; } } if (in.isDefined(String("params"))) { Vector params; in.get(RecordFieldId("params"), params); setParameters(fn, params); } if (in.isDefined(String("masks"))) { Vector masks; in.get(RecordFieldId("masks"), masks); for (uInt i=0; inparameters(); ++i) fn->mask(i) = masks[i]; } return True; } catch (const AipsError& x) { error = x.what(); } error = String("Illegal Function record in " "FunctionHolder::fromRecord\n" + error); return False; } template Bool FunctionHolder::fromString(String &error, const String &in) { order_p = -1; text_p = ""; Int nf; init(); nf = MUString::minimaxNC(in, nam_p); nf_p = static_cast(nf); Function *fn(0); if (getType(error, fn)) { hold_p.reset(fn); return True; } delete fn; fn = 0; return False; } template Bool FunctionHolder::toRecord(String &error, RecordInterface &out) const { if (hold_p && putType(error, out)) { out.define(RecordFieldId("ndim"), static_cast(hold_p->ndim())); out.define(RecordFieldId("npar"), static_cast(hold_p->nparameters())); out.define(RecordFieldId("params"), hold_p->parameters().getParameters()); out.define(RecordFieldId("masks"), hold_p->parameters().getParamMasks()); Record mode; hold_p->getMode(mode); if (mode.nfields() > 0) out.defineRecord(RecordFieldId("mode"), mode); if (nf_p == COMBINE || nf_p == COMPOUND) { Int x(0); if (nf_p == COMBINE) { x = dynamic_cast *> (hold_p.get())->nFunctions(); } else { x = dynamic_cast *> (hold_p.get())->nFunctions(); } out.define("nfunc", x); Record func; for (Int i=0; i fn(dynamic_cast *> (hold_p.get())->function(i)); if (!fn.toRecord(error, fnc)) return False; } else { FunctionHolder fn(dynamic_cast *> (hold_p.get())->function(i)); if (!fn.toRecord(error, fnc)) return False; } ostringstream oss; oss << "__*" << i; func.defineRecord(String(oss), fnc); } out.defineRecord("funcs", func); } return True; } error += String("No Function specified in FunctionHolder::toRecord\n"); return False; } template const String &FunctionHolder::ident() const { static String myid = "fnc"; return myid; } template Bool FunctionHolder::putType(String &error, RecordInterface &out) const { order_p = -1; text_p = ""; if (dynamic_cast *>(hold_p.get())) { nf_p = GAUSSIAN1D; } else if (dynamic_cast *>(hold_p.get())) { nf_p = GAUSSIAN2D; } else if (dynamic_cast *>(hold_p.get())) { nf_p = GAUSSIAN3D; } else if (dynamic_cast *>(hold_p.get())) { nf_p = GAUSSIANND; order_p = Int(-3.0+sqrt(1.0+8.0*hold_p->nparameters())+0.1)/2; } else if (dynamic_cast *>(hold_p.get())) { nf_p = HYPERPLANE; order_p = hold_p->nparameters(); } else if (dynamic_cast *>(hold_p.get())) { nf_p = POLYNOMIAL; order_p = hold_p->nparameters()-1; } else if (dynamic_cast *>(hold_p.get())) { nf_p = EVENPOLYNOMIAL; order_p = 2*hold_p->nparameters()-1; } else if (dynamic_cast *>(hold_p.get())) { nf_p = ODDPOLYNOMIAL; order_p = 2*hold_p->nparameters()-1; } else if (dynamic_cast *>(hold_p.get())) { nf_p = SINUSOID1D; } else if (dynamic_cast *>(hold_p.get())) { nf_p = CHEBYSHEV; order_p = hold_p->nparameters()-1; } else if (dynamic_cast *>(hold_p.get())) { nf_p = BUTTERWORTH; } else if (dynamic_cast *>(hold_p.get())) { nf_p = COMBINE; } else if (dynamic_cast *>(hold_p.get())) { nf_p = COMPOUND; } else if (dynamic_cast *>(hold_p.get())) { nf_p = COMPILED; text_p = dynamic_cast *>(hold_p.get())-> getText(); } else { error += String("Unknown functional in FunctionHolder::putType()\n"); return False; } out.define(RecordFieldId("type"), nf_p); out.define(RecordFieldId("order"), order_p); if (nf_p == COMPILED) out.define(RecordFieldId("progtext"), text_p); return True; } template template Bool FunctionHolder::getType(String &error, Function *&fn, const RecordInterface &in) { in.get(RecordFieldId("order"), order_p); if (in.isDefined(String("progtext")) && in.type(in.idToNumber(RecordFieldId("progtext"))) == TpString) { in.get(RecordFieldId("progtext"), text_p); } // mode can hold function-specific configuration data if (in.isDefined(String("mode")) && in.type(in.idToNumber(RecordFieldId("mode"))) == TpRecord) { mode_p.reset(new Record(in.asRecord(RecordFieldId("mode")))); } Int nf; if (in.type(in.idToNumber(RecordFieldId("type"))) == TpString) { String tp; in.get(RecordFieldId("type"), tp); init(); nf = MUString::minimaxNC(tp, nam_p); } else { in.get(RecordFieldId("type"), nf); } nf_p = static_cast(nf); return getType(error, fn); } template template Bool FunctionHolder::getType(String &error, Function *&fn) { if (nf_p<0 || nf_p >= N_Types) { error += "Unknown type in FunctionHolder::getType()\n"; return False; } switch (nf_p) { case GAUSSIAN1D: fn = (new Gaussian1D); break; case GAUSSIAN2D: fn = (new Gaussian2D); break; case GAUSSIAN3D: fn = (new Gaussian3D); break; case GAUSSIANND: if (order_p >= 0) { fn = (new GaussianND(order_p)); } else fn = (new GaussianND); break; case HYPERPLANE: if (order_p >= 0) { fn = (new HyperPlane(order_p)); } else fn = (new HyperPlane); break; case POLYNOMIAL: if (order_p >= 0) { fn = (new Polynomial(order_p)); } else fn = (new Polynomial); break; case EVENPOLYNOMIAL: if (order_p >= 0) { fn = (new EvenPolynomial(order_p)); } else fn = (new EvenPolynomial); break; case ODDPOLYNOMIAL: if (order_p >= 0) { fn = (new OddPolynomial(order_p)); } else fn = (new OddPolynomial); break; case SINUSOID1D: fn = (new Sinusoid1D); break; case CHEBYSHEV: if (mode_p) fn = (new Chebyshev(order_p, *mode_p)); else fn = (new Chebyshev(order_p)); break; case BUTTERWORTH: if (mode_p) fn = (new SimButterworthBandpass(*mode_p)); else fn = (new SimButterworthBandpass(0, 0)); break; case COMBINE: fn = (new CombiFunction); break; case COMPOUND: fn = (new CompoundFunction); break; case COMPILED: fn = (new CompiledFunction); if (!dynamic_cast *>(fn)->setFunction(text_p)) { error += String("Illegal compiled expression:\n") + dynamic_cast *>(fn)->errorMessage() + "\n"; return False; } break; default: error += "Unknown type in FunctionHolder::getType()\n"; return False; break; } return True; } template void FunctionHolder::setParameters(Function *&fn, const Vector ¶ms) { for (uInt i=0; inparameters(); ++i) (*fn)[i] = params[i]; } template void FunctionHolder::setParameters(Function> *&fn, const Vector ¶ms) { for (uInt i=0; inparameters(); ++i) { (*fn)[i] = AutoDiff(params[i], fn->nparameters(), i); } } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/FunctionMarshallable.h000066400000000000000000000070411476623553700235110ustar00rootroot00000000000000//# FunctionMarshallable.h: a class for serializing/reconstituting Function objects to/from Records //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_FUNCTIONMARSHALLABLE_H #define SCIMATH_FUNCTIONMARSHALLABLE_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // a class for serializing/reconstituting Function objects to/from Records // // // // // //
      • FunctionFactory //
      • Function // // // // Marshalling (a.k.a. serialization) is the process of converting the // state of an object into a transmitable form so that an another object with // identical state can be created in another execution context. This class // defines an interface for marshalling Functions. // // // // // // // // // // // // // // // // // // // // // // // // // // class FunctionMarshallable { public: // create a FunctionMarshallable. functype is the name that // store() will load into the Record's functype field. FunctionMarshallable(const String& functype) : ftype(functype) {} FunctionMarshallable(const FunctionMarshallable& other) : ftype() { ftype = other.ftype; } virtual ~FunctionMarshallable() {} // store the state of this Function into a Record // //
      • InvalidSerializationError if an error during serialization // virtual void store(Record& gr) const = 0; virtual FunctionMarshallable& operator=(const FunctionMarshallable& other) { ftype = other.ftype; return *this; } // return the name representing the Function type that will be placed // in the functype field of Record passed to store(). const String& getFuncType() const { return ftype; } // load functype field into the given Record void loadFuncType(Record& gr) const { gr.define(SerialHelper::FUNCTYPE.c_str(), ftype.c_str()); } private: FunctionMarshallable() : ftype() {} String ftype; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/FunctionOrder.h000066400000000000000000000123011476623553700221700ustar00rootroot00000000000000//# FunctionOrder.h: Container of function description details //# Copyright (C) 2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_FUNCTIONORDER_H #define SCIMATH_FUNCTIONORDER_H //# Include files #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations class RecordInterface; // Container of function description details // // // // // // // // // FunctionOrder is used to provide an interface to an entity which // has special fixed parameters (like dimension of Gaussian; oder of // Polynomial). // This is useful, for example, in implementinggeneric function factories. // // // // // See the FunctionHolder // class for a usage interface. // // // // Generically manipulatable parameters are important for Glish interface // // // //
      • T must have a default constructor, assignment operator, // and copy constructor (for the Vector interface). //
      • Complex/DComplex or Float/Double supported // // // //
      • Nothing I know of // template class FunctionOrder : public RecordTransformable { public: //# Constructors // Construct a default FunctionOrder with 0 parameters FunctionOrder(); // Copy constructor (deep copy) FunctionOrder(const FunctionOrder &other); // Destructor virtual ~FunctionOrder(); //# Operators // Copy assignment (deep copy) FunctionOrder &operator=(const FunctionOrder &other); //# Member functions // Get and set the various parameters (no check for index range). // Automatic extension for write. // Int &getInt(const uInt n); const Int &getInt(const uInt n) const; T &getPar(const uInt n); const T &getPar(const uInt n) const; String &getString(); const String &getString() const; T &getScale(const uInt n); const T &getScale(const uInt n) const; T &getCenter(const uInt n); const T &getCenter(const uInt n) const; T &getWidth(const uInt n); const T &getWidth(const uInt n) const; const Function &getFunction(const uInt n) const; void setFunction(const uInt n, Function &other); // // Create a FunctionOrder from a record // Error messages are postfixed to error. // virtual Bool fromRecord(String &error, const RecordInterface &in); virtual Bool fromString(String &error, const String &in); // // Create a record from a FunctionOrder. // Error messages are postfixed to error. virtual Bool toRecord(String &error, RecordInterface &out) const; // Get identification of record virtual const String &ident() const; // Output the parameters ostream &print(ostream &os) const; private: //# Data // All data vectors can be empty // // Integer details (order etc) Vector int_p; // Double parameters Vector double_p; // String parameters String string_p; // List of functions (say for Combi and Compound) PtrBlock *> function_p; // Scale of y (length 1) Vector scale_p; // Centers of x (length ndim) Vector center_p; // Width of x (ndim) Vector width_p; // }; //# Global functions // Global functions // // Output declaration template ostream &operator<<(ostream &os, const FunctionOrder &par); // //# Inlines template inline ostream &operator<<(ostream &os, const FunctionOrder &par) { return par.print(os); } } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/FunctionOrder.tcc000066400000000000000000000154511476623553700225230ustar00rootroot00000000000000//# FunctionOrder.cc: Container of function description details //# Copyright (C) 2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_FUNCTIONORDER_TCC #define SCIMATH_FUNCTIONORDER_TCC #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template FunctionOrder::FunctionOrder() : int_p(0), double_p(0), string_p(""), function_p(0), scale_p(0), center_p(0), width_p(0) {} template FunctionOrder::FunctionOrder(const FunctionOrder &other) : int_p(other.int_p.copy()), double_p(other.double_p.copy()), string_p(other.string_p), function_p(other.function_p.nelements()), scale_p(other.scale_p.copy()), center_p(other.center_p.copy()), width_p(other.width_p.copy()) { for (uInt i=0; i FunctionOrder::~FunctionOrder() { for (uInt i=0; i FunctionOrder &FunctionOrder::operator=(const FunctionOrder &other) { if (this != &other) { int_p.resize(other.int_p.nelements()); int_p = other.int_p; double_p.resize(other.double_p.nelements()); double_p = other.double_p; string_p = other.string_p; scale_p.resize(other.scale_p.nelements()); scale_p = other.scale_p; center_p.resize(other.center_p.nelements()); center_p = other.center_p; width_p.resize(other.width_p.nelements()); width_p = other.width_p; for (uInt i=0; i *>(other.function_p.nelements()); for (uInt i=0; i Int &FunctionOrder::getInt(const uInt n) { if (n>=int_p.nelements()) int_p.resize(n+1, True); return int_p[n]; } template const Int &FunctionOrder::getInt(const uInt n) const { return int_p[n]; } template T &FunctionOrder::getPar(const uInt n) { if (n>=double_p.nelements()) double_p.resize(n+1, True); return double_p[n]; } template const T &FunctionOrder::getPar(const uInt n) const { return double_p[n]; } template String &FunctionOrder::getString() { return string_p; } template const String &FunctionOrder::getString() const { return string_p; } template T &FunctionOrder::getScale(const uInt n) { if (n>=scale_p.nelements()) scale_p.resize(n+1, True); return scale_p[n]; } template const T &FunctionOrder::getScale(const uInt n) const { return scale_p[n]; } template T &FunctionOrder::getCenter(const uInt n) { if (n>=center_p.nelements()) center_p.resize(n+1, True); return center_p[n]; } template const T &FunctionOrder::getCenter(const uInt n) const { return center_p[n]; } template T &FunctionOrder::getWidth(const uInt n) { if (n>=width_p.nelements()) width_p.resize(n+1, True); return width_p[n]; } template const T &FunctionOrder::getWidth(const uInt n) const { return width_p[n]; } template const Function &FunctionOrder::getFunction(const uInt n) const { return *(function_p[n]); } template void FunctionOrder::setFunction(const uInt n, Function &other) { if (n>=function_p.nelements()) function_p.resize(n+1, True); delete function_p[n]; function_p[n] = other.clone(); } //# Global functions template ostream &FunctionOrder::print(ostream &os) const { os << "["; os << "["; for (uInt i=0; i Bool FunctionOrder::fromRecord(String &, const RecordInterface &in) { if (in.isDefined(String("ord"))) in.get(RecordFieldId("ord"), int_p); if (in.isDefined(String("par"))) in.get(RecordFieldId("par"), double_p); if (in.isDefined(String("str"))) in.get(RecordFieldId("str"), string_p); if (in.isDefined(String("sca"))) in.get(RecordFieldId("sca"), scale_p); if (in.isDefined(String("cen"))) in.get(RecordFieldId("cen"), center_p); if (in.isDefined(String("wid"))) in.get(RecordFieldId("wid"), width_p); return True; } template Bool FunctionOrder::fromString(String &, const String &in) { string_p = in; return True; } template Bool FunctionOrder::toRecord(String &, RecordInterface &out) const { out.define(RecordFieldId("ord"), int_p); out.define(RecordFieldId("par"), double_p); out.define(RecordFieldId("str"), string_p); out.define(RecordFieldId("sca"), scale_p); out.define(RecordFieldId("cen"), center_p); out.define(RecordFieldId("wid"), width_p); /// Add the functionals!! return True; } template const String &FunctionOrder::ident() const { static String myid = "fncord"; return myid; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/FunctionParam.h000066400000000000000000000160461476623553700221670ustar00rootroot00000000000000//# FunctionParam.h: Container of function parameters with masking flags //# Copyright (C) 2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_FUNCTIONPARAM_H #define SCIMATH_FUNCTIONPARAM_H //# Include files #include #include #include //# Forward declarations #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Container of function parameters with masking flags // // // // // // // // // FunctionParam is used to provide an interface to an entity which // has parameters that can be flagged. // This is useful, for example, in implementing parameter // fitting which operates on generic function objects. // // Each parameter can be masked. The mask can, e.g., be used to indicate to a // generic least-squares fitting routine to only adjust parameters with // a True mask (the default). For that reason methods that only // handle True data items have names with Adjust in // the names. In general the user should not be concerned with these // methods, but should only manipulate the parameter flags and // values. // // // // // See the Function class for a usage // interface. // // // // Generically manipulatable adjustable parameters are important for fitting. // // // //
      • T must have a default constructor, assignment operator, // and copy constructor (for the Vector interface). //
      • all standard mathematical should be applicable if the // parameter interface is used for the calculation of // Functions. // // // //
      • Nothing I know of // template class FunctionParam { public: //# Constructors // Construct a default FunctionParam with 0 parameters FunctionParam(); // Construct a FunctionParam with n parameters with zero value and // all masks True explicit FunctionParam(const uInt n); // Construct a FunctionParam from the given vector, with all masks // True explicit FunctionParam(const Vector &in); // Copy constructor (deep copy) FunctionParam(const FunctionParam &other); // Copy from different type (deep copy) template FunctionParam(const FunctionParam &other) : npar_p(other.getParameters().nelements()), param_p(npar_p), mask_p(npar_p), maskedPtr_p(0) { for (uInt i=0; i:: setValue(param_p[i], FunctionTraits::getValue(other.getParameters()[i]), npar_p, i); } mask_p = other.getParamMasks(); } // Destructor virtual ~FunctionParam(); //# Operators // Copy assignment (deep copy) FunctionParam &operator=(const FunctionParam &other); // Manipulate the nth parameter (0-based) with no index check // T &operator[](const uInt n) { return param_p[n]; } const T &operator[](const uInt n) const { return param_p[n]; } // // Compare two parameter sets for equal size, values and masks. // Bool operator==(const FunctionParam &other) const; Bool operator!=(const FunctionParam &other) const; // //# Member functions // Return the number of parameters uInt nelements() const { return param_p.nelements(); } // Manipulate the nth parameter (0-based) with no index check // T ¶meter(const uInt n) { return param_p[n]; } const T ¶meter(const uInt n) const{ return param_p[n]; } // // Manipulate the mask associated with the nth parameter // (e.g. to indicate whether the parameter is adjustable or nonadjustable). // Note no index check. // Bool &mask(const uInt n); const Bool &mask(const uInt n) const { return mask_p[n]; } // // Get all parameters at once. Returns zero length // Vector if there are no parameters. const Vector &getParameters() const { return param_p; } // Set all the parameters at once. Only the minimum of the input number and // the object number of parameters will be set. void setParameters(const Vector ¶ms); // Get all parameter masks at once. Returns zero length // Vector if there are no parameters. const Vector &getParamMasks() const { return mask_p; } // Set all parameter masks at once. Only the minimum of the input number and // the object number of parameters will be set. void setParamMasks(const Vector &masks); // Operations on the masked parameters only. For possible re-use the // results are cached. // // Number of masked (=True) parameters uInt nMaskedParameters() const; // All masked parameters only // Vector &getMaskedParameters() const; void setMaskedParameters(Vector &in); // // // Output the parameters ostream &print(ostream &os) const; private: //# Data // Number of parameters uInt npar_p; // Parameters Vector param_p; // Masks Vector mask_p; // Cached masked data mutable Vector *maskedPtr_p; //# Methods // Create a cached version of the masked parameter list void createMaskedPtr() const; // Clear the masked parameter list void clearMaskedPtr() const; }; //# Global functions // Global functions // // Output declaration template ostream &operator<<(ostream &os, const FunctionParam &par); // //# Inlines template inline ostream &operator<<(ostream &os, const FunctionParam &par) { return par.print(os); } } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/FunctionParam.tcc000066400000000000000000000113441476623553700225050ustar00rootroot00000000000000//# FunctionParam.cc: Container of function parameters with masking flags //# Copyright (C) 2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_FUNCTIONPARAM_TCC #define SCIMATH_FUNCTIONPARAM_TCC #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template FunctionParam::FunctionParam() : npar_p(0), param_p(npar_p), mask_p(npar_p), maskedPtr_p(0) {} template FunctionParam::FunctionParam(const uInt n) : npar_p(n), param_p(npar_p), mask_p(npar_p, True), maskedPtr_p(0) { for (uInt i=0; i FunctionParam::FunctionParam(const Vector &in) : npar_p(in.nelements()), param_p(npar_p), mask_p(npar_p, True), maskedPtr_p(0) { for (uInt i=0; i FunctionParam::FunctionParam(const FunctionParam &other) : npar_p(other.param_p.nelements()), param_p(npar_p), mask_p(npar_p), maskedPtr_p(0) { for (uInt i=0; i FunctionParam::~FunctionParam() { clearMaskedPtr(); } //# Operators template FunctionParam &FunctionParam::operator=(const FunctionParam &other) { if (this != &other) { npar_p = other.npar_p; param_p.resize(npar_p); param_p = other.param_p; mask_p.resize(npar_p); mask_p = other.mask_p; clearMaskedPtr(); } return *this; } template Bool FunctionParam::operator==(const FunctionParam &other) const { if (npar_p != other.npar_p) return False; for (uInt i=0; i Bool FunctionParam::operator!=(const FunctionParam &other) const { return (!((*this) == other)); } //# Member functions template Bool &FunctionParam::mask(const uInt n) { clearMaskedPtr(); return mask_p[n]; } template void FunctionParam::setParameters(const Vector ¶ms) { uInt n = ((params.nelements() < npar_p) ? params.nelements() : npar_p); for (uInt i=0; i void FunctionParam::setParamMasks(const Vector &masks) { uInt n = ((masks.nelements() < npar_p) ? masks.nelements() : npar_p); for (uInt i=0; i uInt FunctionParam::nMaskedParameters() const { createMaskedPtr(); return maskedPtr_p->nelements(); } template Vector &FunctionParam::getMaskedParameters() const { createMaskedPtr(); return *maskedPtr_p; } template void FunctionParam::setMaskedParameters(Vector &in) { for (uInt i(0), n(0); i void FunctionParam::createMaskedPtr() const { if (!maskedPtr_p) { clearMaskedPtr(); Vector tmp(npar_p); uInt n(0); for (uInt i(0); i(tmp); } } template void FunctionParam::clearMaskedPtr() const { delete maskedPtr_p; maskedPtr_p = 0; } //# Global functions template ostream &FunctionParam::print(ostream &os) const { os << "["; for (uInt i=0; i #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Function data types for parameters and arguments // // // // // // //
      • Function //
      • AutoDiff // // // A trait is a characteristic feature. FunctionTraits defines relationships // between the data types of result, parameters and arguments of function // objects. // // // // This templated class contains a number of typedefs that // describe the relationship between different numeric data types used for // the calculation of Function and the function parameter and // arguments. // // Its main use is to optimize the speed of the calculation of function // values and derivatives in the case of AutoDiff use and // manual calculation of derivatives, by allowing the data type of the // the arguments and/or parameters to be plain numeric in cases where the // derivatives wrt these are not needed. // To enable this, the following definitions are used for the use of the // Function template. Bear in mind that the Function operator is defined // as result = f(x; parameters). //
          //
        1. Simple numeric type (Double, Complex, etc): result, parameters and // arguments: all the one defined templated type. //
        2. AutoDiff indicates the calculation (either automatic or // with specialized implementations) of the result with a T // function value for T arg, and AutoDiff // parameters. //
        3. AutoDiffA calculate form AutoDiff // arguments and parameters (note that either could be simple // values with zero derivatives) //
        4. AutoDiffX : calculate only with respect to // the arguments the derivatives, by using T // parameters //
        // The following types are defined: //
        //
        Type //
        The template argument //
        BaseType //
        One down in the template hierarchy if possible (e.g. Double // for AutoDiff) //
        NumericType //
        Ultimate numeric type (e.g. Double for // AutoDiff > //
        ParamType //
        Type used for parameters //
        ArgType //
        Type used for arguments //
        DiffType //
        The default differentiation type (e.g. AutoDiff // for AutoDiff) //
        getValue() //
        get the value of a simple numeric or of an AutoDiff //
        setValue() //
        set the value of a simple numeric or of an AutoDiff //
        // // The specializations are done in such a way that higher order // derivatives (e.g. AutoDiff >) are catered for. // // Note that the class names in the following definitions are extended with // some individual id (like _PA): do not use them in programming, // they are only necessary for the cxx2html interpreter) // // This class is implemented as a number of specializations for the // following data types. //
          //
        • T //
        • AutoDiff //
        • AutoDiffA //
        • AutoDiffX //
        //
        // // // See the Function class code. // // // // To keep the Function class single templated // // // //
      • Additional AutoDiff* classes if and when needed // // template class FunctionTraits { public: // Actual template type typedef T Type; // Template base type typedef T BaseType; // Numeric type of template typedef T NumericType; // Type for parameters typedef T ParamType; // Type for arguments typedef T ArgType; // Default type for differentiation typedef AutoDiff DiffType; // Get the value static const T &getValue(const T &in) { return in; } // Set a value (and possible derivative) static void setValue(T &out, const T &val, const uInt, const uInt) { out = val; } }; //# Following are specializations. Naming only for documentation //# purposes (a problem with cxx2html) #define FunctionTraits_P FunctionTraits // FunctionTraits specialization for AutoDiff // template class FunctionTraits_P > { public: // Actual template type typedef AutoDiff Type; // Template base type typedef T BaseType; // Template numeric type typedef typename FunctionTraits_P::NumericType NumericType; // Type for parameters typedef AutoDiff ParamType; // Type for arguments typedef T ArgType; // Default type for differentiation typedef AutoDiff DiffType; // Get the value static const T &getValue(const Type &in) { return FunctionTraits::getValue(in.value()); } // Set a value (and possible derivative) static void setValue(Type &out, const T &val, const uInt nder, const uInt i) { out = Type(val, nder, i); } }; #undef FunctionTraits_P #define FunctionTraits_PA FunctionTraits // FunctionTraits specialization for AutoDiffA // template class FunctionTraits_PA > { public: // Actual template type typedef AutoDiffA Type; // Template base type typedef T BaseType; // Template numeric type typedef typename FunctionTraits_PA::NumericType NumericType; // Type for parameters typedef AutoDiffA ParamType; // Type for arguments typedef AutoDiffA ArgType; // Default type for differentiation typedef AutoDiffA DiffType; // Get the value static const T &getValue(const Type &in) { return FunctionTraits::getValue(in.value()); } // Set a value (and possible derivative) static void setValue(Type &out, const T &val, const uInt nder, const uInt i) { out = Type(val, nder, i); } }; #undef FunctionTraits_PA #define FunctionTraits_PX FunctionTraits // FunctionTraits specialization for AutoDiffX // template class FunctionTraits_PX > { public: // Actual template type typedef AutoDiffX Type; // Template base type typedef T BaseType; // Template numeric type typedef typename FunctionTraits_PX::NumericType NumericType; // Type for parameters typedef T ParamType; // Type for arguments typedef AutoDiffX ArgType; // Default type for differentiation typedef AutoDiffX DiffType; // Get the value static const T &getValue(const Type &in) { return FunctionTraits::getValue(in.value()); } // Set a value (and possible derivative) static void setValue(Type &out, const T &val, const uInt nder, const uInt i) { out = Type(val, nder, i); } }; #undef FunctionTraits_PX } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/FunctionWrapper.h000066400000000000000000000125721476623553700225470ustar00rootroot00000000000000//# FunctionWrapper.h: Construct function objects from C++ functions //# Copyright (C) 2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_FUNCTIONWRAPPER_H #define SCIMATH_FUNCTIONWRAPPER_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations template class WrapperBase; // Construct nD function objects from C++ functions // // // // // // // // //
      • Function class //
      • FunctionParam // // // // This class is provided so that user can quickly construct a function // object from a C++ function pointer without having to write a function // class. The constructor constructs a function object from a function // pointer, and an optional parameter list. // Parameters are necessary if // the function has to be used in a functional fitting process (see // GenericL2Fit). // // The general function signature is f(x;p), where x // represents the arguments, and p the parameters. // The allowed signatures of the function include all combinations of // arguments and parameters, and are: //
          //
        • f() no arguments e.g. random number or constant //
        • f(x) 1-dimensional, e.g. sin(x) //
        • f(Vectorx) n-dimensional, e.g. sin(x+2y) //
        // //
        // // // // Float func(const Vector& x) {return x(0)*x(1);} // x*y // // Convert C++ functions to Functionals // FunctionWrapper Func(func,2); // // template class FunctionWrapper : public WrapperParam { public: //# Constructors // Default constructor, to enable arrays FunctionWrapper(); // A function with no parameters and no arguments. FunctionWrapper(T(*f)()); // A function with parameter and no arguments // (Note value of isPar irrelevant) FunctionWrapper(T(*f)( const T&), const Bool isPar); // A function with parameters and no arguments. // (Note value of isPar irrelevant) FunctionWrapper(T(*f)(const Vector&), const Bool isPar); // Construct a 1-dimensional function with no parameters. FunctionWrapper(T(*f)(const T&)); // Construct a 1-dimensional function with parameter. FunctionWrapper(T(*f)(const T&, const T&), const T &par); // Construct a 1-dimensional function with parameters. FunctionWrapper(T(*f)(const T&, const Vector&), const Vector &par); // Construct an n-dimensional function with no parameters. FunctionWrapper(T(*f)(const Vector&), const Int dim=1); // Construct an n-dimensional function with parameter. FunctionWrapper(T(*f)(const Vector&, const T&), const T &par, const uInt dim=1); // Construct an n-dimensional function with parameters. FunctionWrapper(T(*f)(const Vector&, const Vector&), const Vector &par, const uInt dim=1); // Copy constructor (reference semantics) // FunctionWrapper(const FunctionWrapper &other); // // Copy assignment (reference semantics) FunctionWrapper &operator=(const FunctionWrapper &other); // Destructor virtual ~FunctionWrapper() {} //# Operators // Evaluate the function at x. // virtual T eval(typename Function::FunctionArg x) const; // //# Member functions // Get the dimensionality virtual uInt ndim() const; // Return a copy of this object from the heap. The caller is responsible // for deleting this pointer. // virtual Function *clone() const { return new FunctionWrapper(*this); } // protected: //# Data // The function aid object std::shared_ptr> doit_p; //# Make members of parent classes known. protected: using WrapperParam::param_p; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/FunctionWrapper.tcc000066400000000000000000000100501476623553700230560ustar00rootroot00000000000000//# FunctionWrapper.cc: Construct function objects from C++ functions //# Copyright (C) 1995,1996,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_FUNCTIONWRAPPER_TCC #define SCIMATH_FUNCTIONWRAPPER_TCC //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors template FunctionWrapper::FunctionWrapper() : WrapperParam(0) {} template FunctionWrapper::FunctionWrapper(T(*f)(const T&), const Bool) : WrapperParam(0), doit_p(new WrapperData(f)) {} template FunctionWrapper::FunctionWrapper(T(*f)(const Vector&), const Bool) : WrapperParam(0), doit_p(new WrapperData,False,True>(f)) {} template FunctionWrapper::FunctionWrapper(T(*f)()) : WrapperParam(0), doit_p(new WrapperData(f)) {} template FunctionWrapper::FunctionWrapper(T(*f)(const T&)) : WrapperParam(0), doit_p(new WrapperData(f,1)) {} template FunctionWrapper::FunctionWrapper(T(*f)(const T&, const T&), const T &par) : WrapperParam(1), doit_p(new WrapperData(f,1)) { param_p[0] = par; } template FunctionWrapper::FunctionWrapper(T(*f)(const T&, const Vector&), const Vector &par) : WrapperParam(par), doit_p(new WrapperData,True,True>(f,1)) {} template FunctionWrapper::FunctionWrapper(T(*f)(const Vector&), const Int dim) : WrapperParam(0), doit_p(new WrapperData,T,True,False>(f,dim)) {} template FunctionWrapper::FunctionWrapper(T(*f)(const Vector&, const T&), const T &par, const uInt dim) : WrapperParam(1), doit_p(new WrapperData,T,True,True>(f,dim)) { param_p[0] = par; } template FunctionWrapper::FunctionWrapper(T(*f)(const Vector&, const Vector&), const Vector &par, const uInt dim) : WrapperParam(par), doit_p(new WrapperData,Vector,True,True>(f,dim)) {} template FunctionWrapper:: FunctionWrapper(const FunctionWrapper &other) : WrapperParam(other), doit_p(other.doit_p) {} /// check if to clone template FunctionWrapper &FunctionWrapper:: operator=(const FunctionWrapper &other) { if (this != &other) { WrapperParam::operator=(other); doit_p = other.doit_p; /// check clone } return *this; } //# Operators template T FunctionWrapper::eval(typename Function::FunctionArg x) const { if (doit_p) return doit_p->eval(x, param_p.getParameters()); return T(0); } //# Member functions template uInt FunctionWrapper::ndim() const { return (doit_p ? doit_p->ndim() : 0); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/FunctionalProxy.cc000066400000000000000000000172151476623553700227220ustar00rootroot00000000000000//# FunctionalProxy.cc: This class gives a common object to functionals //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include // Constructors FunctionalProxy::FunctionalProxy(const Record& rec, Int type) : type_(type) { if (type == 0) rec2fhd(rec); else rec2fhdc(rec); } FunctionalProxy::~FunctionalProxy() {} Record FunctionalProxy::fhd2rec() { Record rec; String err; if (! fhd_.toRecord(err, rec) ) throw AipsError(err); return rec; } Record FunctionalProxy::fhdc2rec() { Record rec; String err; if (! fhdc_.toRecord(err, rec) ) throw AipsError(err); return rec; } uInt FunctionalProxy::ndim() const { if (type_ == 0) return fhd_.asFunction().ndim(); else return fhdc_.asFunction().ndim(); } Record FunctionalProxy::asrecord() { if (type_ == 0) return fhd2rec(); else return fhdc2rec(); } void FunctionalProxy::rec2fhdc(const Record& rec) { String err; if (! fhdc_.fromRecord(err, rec) ) throw AipsError(err); } void FunctionalProxy::rec2fhd(const Record& rec) { String err; if (! fhd_.fromRecord(err, rec) ) throw AipsError(err); } Vector FunctionalProxy::f(const Vector& val) { Int nd=1; if (fhd_.asFunction().ndim() != 0) nd = fhd_.asFunction().ndim(); Vector out(val.nelements()/nd); Vector in(nd); for (uInt i=0; i FunctionalProxy::fdf(const Vector& val) { String errmsg; // this is a workaround until I understand AutoDiff FunctionHolder fnh; Record rec = fhd2rec(); Function > *fn(0); if (!fnh.getRecord(errmsg, fn, rec)) throw(AipsError(errmsg)); // Int nd=1; if (fn->ndim() != 0) nd = fn->ndim(); Vector out(val.nelements()/nd * (fn->nparameters()+1)); Vector in(nd); for (uInt i=0; i res = (*fn)(in); out[i] = res.value(); for (uInt k=0; knparameters(); ++k) { out[(k+1)*val.nelements()/nd+i] = res.deriv(k); } } return out; } void FunctionalProxy::add(const FunctionalProxy& func) { if (!fhd_.addFunction(func.fhd_.asFunction())) { throw(AipsError("Cannot add Function")); } } Vector FunctionalProxy::fc(const Vector& val) { Int nd=1; if (fhdc_.asFunction().ndim() != 0) nd = fhdc_.asFunction().ndim(); Vector out(val.nelements()/nd); Vector in(nd); for (uInt i=0; i FunctionalProxy::fdfc(const Vector& val) { String errmsg; // this is a workaround until I understand AutoDiff FunctionHolder fnh; Record rec = fhd2rec(); Function > *fn(0); if (!fnh.getRecord(errmsg, fn, rec)) throw(AipsError(errmsg)); // Int nd=1; if (fn->ndim() != 0) nd = fn->ndim(); Vector out(val.nelements()/nd * (fn->nparameters()+1)); Vector in(nd); for (uInt i=0; i res = (*fn)(in); out[i] = res.value(); for (uInt k=0; knparameters(); ++k) { out[(k+1)*val.nelements()/nd+i] = res.deriv(k); } } return out; } void FunctionalProxy::addc(const FunctionalProxy& func) { if (!fhdc_.addFunction(func.fhdc_.asFunction())) { throw(AipsError("Cannot add Function")); } } Int FunctionalProxy::npar() const { if (type_ == 0) return fhd_.asFunction().nparameters(); else return fhdc_.asFunction().nparameters(); } void FunctionalProxy::setparameters(const Vector& val) { uInt n = (fhd_.asFunction()).nparameters(); if (val.nelements() != n) throw(AipsError("number of parameters doesn't match functional")); Record rec = fhd2rec(); rec.define("params", val); rec2fhd(rec); } void FunctionalProxy::setparametersc(const Vector& val) { uInt n = (fhdc_.asFunction()).nparameters(); if (val.nelements() != n) throw(AipsError("number of parameters doesn't match functional")); Record rec = fhdc2rec(); rec.define("params", val); rec2fhdc(rec); } void FunctionalProxy::setmasks(const Vector& val) { uInt n; if (type_ == 0) n = (fhd_.asFunction()).nparameters(); else n = (fhdc_.asFunction()).nparameters(); if (val.nelements() != n) throw(AipsError("number of parameters doesn't match functional")); Record rec; if (type_ == 0) { rec = fhd2rec(); rec.define("masks", val); rec2fhd(rec); } else { rec = fhdc2rec(); rec.define("masks", val); rec2fhdc(rec); } } void FunctionalProxy::setmask(Int idx, Bool val) { Int n; if (type_ == 0) { n = (fhd_.asFunction()).nparameters(); } else { n = (fhdc_.asFunction()).nparameters(); } if (idx < 0 || idx >= n ) throw(AipsError("mask index out of bounds")); if (type_ == 0) { Record rec = fhd2rec(); Vector v = rec.toArrayBool("masks"); v[idx] = val; rec.define("masks", v); rec2fhd(rec); } else { Record rec = fhdc2rec(); Vector v = rec.toArrayBool("masks"); v[idx] = val; rec.define("masks", v); rec2fhdc(rec); } } void FunctionalProxy::setpar(Int idx, Double val) { Int n = (fhd_.asFunction()).nparameters(); if (idx < 0 || idx >= n ) throw(AipsError("parameter index out of bounds")); Record rec = fhd2rec(); Vector v = rec.toArrayDouble("params"); v[idx] = val; rec.define("params", v); rec2fhd(rec); } void FunctionalProxy::setparc(Int idx, DComplex val) { Int n = (fhdc_.asFunction()).nparameters(); if (idx < 0 || idx >= n ) throw(AipsError("parameter index out of bounds")); Record rec = fhdc2rec(); Vector v = rec.toArrayDComplex("params"); v[idx] = val; rec.define("params", v); rec2fhdc(rec); } Vector FunctionalProxy::masks() const { if (type_ == 0) return (fhd_.asFunction()).parameters().getParamMasks(); else return (fhdc_.asFunction()).parameters().getParamMasks(); } Vector FunctionalProxy::parameters() const { return (fhd_.asFunction()).parameters().getParameters() ; } Vector FunctionalProxy::parametersc() const { return (fhdc_.asFunction()).parameters().getParameters() ; } casacore-3.7.1/scimath/Functionals/FunctionalProxy.h000066400000000000000000000052361476623553700225640ustar00rootroot00000000000000//# FunctionFactory.h: a class for creating Function objects from Records //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_FUNCTIONALSPROXY_H #define SCIMATH_FUNCTIONALSPROXY_H #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class FunctionalProxy { public: FunctionalProxy() {;} // type 0==Double, other == DComplex FunctionalProxy(const Record& rec, Int type=0); virtual ~FunctionalProxy(); Vector f(const Vector& val); Vector fdf(const Vector& val); void add(const FunctionalProxy& func); Vector fc(const Vector& val); Vector fdfc(const Vector& val); void addc(const FunctionalProxy& func); Record asrecord(); Int npar() const; uInt ndim() const; void setparameters(const Vector& val); void setparametersc(const Vector& val); void setmasks(const Vector& val); void setmask(Int i, Bool val); void setpar(Int i, Double val); void setparc(Int i, DComplex val); Vector masks() const; Vector parameters() const; Vector parametersc() const; private: Record fhd2rec(); Record fhdc2rec(); void rec2fhdc(const Record& rec); void rec2fhd(const Record& rec); Int type_; FunctionHolder fhd_; FunctionHolder fhdc_; }; } #endif casacore-3.7.1/scimath/Functionals/GNoiseFunction.h000066400000000000000000000076251476623553700223160ustar00rootroot00000000000000//# GNoiseFunction.h: A one dimensional normal distribution //# Copyright (C) 2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_GNOISEFUNCTION_H #define SCIMATH_GNOISEFUNCTION_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // A one dimensional normal distribution // // // // // //
      • GNoiseParam //
      • Function // // // Gaussian Noise generator. // // // A GNoise is described by a mean and a variance (Note these are // not parameters in the Function sense, but more like the // order of a polynomial. The defaults are 0 and 1. // // // // // GNoiseFunction sf; // sf(); // = 0.12 // // // //
      • T should have standard numerical operators // // //
      • AipsError if incorrect parameter number specified. // // template class GNoiseFunction : public GNoiseParam { public: //# Constructors // Constructs the GNoise, Defaults: // mean=0, var=1.0 // GNoiseFunction() : GNoiseParam() {} GNoiseFunction(const Double &mean, const Double &var) : GNoiseParam(mean, var) {} // // Copy constructor (deep copy) // GNoiseFunction(const GNoiseFunction &other) : GNoiseParam(other) {} template GNoiseFunction(const GNoiseFunction &other) : GNoiseParam(other) {} // // Copy assignment (deep copy) GNoiseFunction &operator=(const GNoiseFunction &other) { GNoiseParam::operator=(other); return *this; } // Destructor virtual ~GNoiseFunction() {} //# Operators // Evaluate the GNoise at x. // If a vector is used as the argument only its first element is used. // virtual T eval(typename Function::FunctionArg x) const; // //# Member functions // Return a copy of this object from the heap. The caller is responsible // for deleting this pointer. // virtual Function *clone() const { return new GNoiseFunction(*this); } // //# Make members of parent classes known. protected: using GNoiseParam::param_p; public: using GNoiseParam::noise_p; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/GNoiseFunction.tcc000066400000000000000000000032551476623553700226330ustar00rootroot00000000000000//# GNoiseFunction.cc: A one dimensional normal distribution //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_GNOISEFUNCTION_TCC #define SCIMATH_GNOISEFUNCTION_TCC #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Operators template T GNoiseFunction::eval(typename Function::FunctionArg ) const { return T(noise_p()); } //# Member functions } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/GNoiseParam.h000066400000000000000000000067651476623553700215750ustar00rootroot00000000000000//# GNoiseParam.h: A one dimensional normal distribution //# Copyright (C) 2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_GNOISEPARAM_H #define SCIMATH_GNOISEPARAM_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // A one dimensional normal distribution // // // // // //
      • FunctionParam class //
      • Function class // // // Gaussian Noise generator. // // // A GNoise is described by a mean and a variance (Note these are // not parameters in the Function sense, but more like the // order of a polynomial. The defaults are 0 and 1. // // // // // GNoiseFunction sf; // sf(); // = 0.12 // // // //
      • T should have standard numerical operators // // //
      • AipsError if incorrect parameter number specified. // template class GNoiseParam : public Function { public: //# Enumerations //# Constructors // Constructs the GNoise, Defaults: // mean=0, var=1.0 // GNoiseParam(); GNoiseParam(const Double &mean, const Double &var); // // Copy constructor (deep copy) // GNoiseParam(const GNoiseParam &other); // // Copy assignment (deep copy) GNoiseParam &operator=(const GNoiseParam &other); // Destructor virtual ~GNoiseParam(); //# Operators virtual uInt ndim() const { return 0; } //# Member functions // Give name of function virtual const String &name() const { static String x("gaussnoise"); return x; } protected: //# Data // Random generator ACG genit_p; // Normal noise mutable Normal noise_p; //# Make members of parent classes known. protected: using Function::param_p; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/GNoiseParam.tcc000066400000000000000000000043221476623553700221020ustar00rootroot00000000000000//# GNoiseParam.cc: A one dimensional normal distribution //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_GNOISEPARAM_TCC #define SCIMATH_GNOISEPARAM_TCC //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template GNoiseParam::GNoiseParam() : Function(0), genit_p(), noise_p(&genit_p, 0.0, 1.0) {} template GNoiseParam::GNoiseParam(const Double &mean, const Double &var) : Function(0), genit_p(), noise_p(&genit_p, mean, var) {} template GNoiseParam::GNoiseParam(const GNoiseParam &other) : Function(other), genit_p(other.genit_p), noise_p(&genit_p, other.noise_p.mean(), other.noise_p.variance()) {} template GNoiseParam::~GNoiseParam() {} //# Operators template GNoiseParam &GNoiseParam::operator=(const GNoiseParam &other) { if (this != &other) { Function::operator=(other); genit_p = other.genit_p; noise_p = Normal(&genit_p, other.noise_p.mean(), other.noise_p.variance()); } return *this; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/Gaussian1D.h000066400000000000000000000240741476623553700213600ustar00rootroot00000000000000//# Gaussian1D.h: A one-dimensional Gaussian class //# Copyright (C) 1995,1996,1997,2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_GAUSSIAN1D_H #define SCIMATH_GAUSSIAN1D_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // A one dimensional Gaussian class. // // // // //
      • Gaussian1DParam //
      • Function // // // A Gaussian1D functional is designed exclusively for calculating a // Gaussian (or Normal) distribution in one dimension. Other classes exist // for calculating these functions in two // (Gaussian2D) and N // (GaussianND) dimensions. // // // A Gaussian1D is described by a height, center, and width. Its // fundamental operation is evaluating itself at some x. // The parameters (height, center and width) may be changed at run time. // // The width of the Gaussian (for the constructors or the setWidth // function) is always specified in terms of the full width at half // maximum (FWHM). It is always positive and attempts to set a non-positive // width will throw an assertion when in debug mode. // // The peak height of the Gaussian can be specified at construction time or // by using the setHeight function. Alternatively the // setFlux function can be used to implicitly set the peak height by // specifying the integrated area under the Gaussian. The height (or flux) // can be positive, negative or zero, as this class makes no assumptions on // what quantity the height represents. // // Changing the width of the Gaussian will not affect // its peak height but will change its flux. So you should always set the // width before setting the flux. // // The parameter interface (see // Gaussian1DParam class), // is used to provide an interface to the // Fitting classes. // // There are 3 parameters that are used to describe the Gaussian: //
          //
        1. The height of the Gaussian. This is identical to the value // returned using the height() member function. //
        2. The center of the Gaussian in the x direction. This is identical to // the value returned using the center() member function. //
        3. The width (FWHM) of the Gaussian. To aid convergence of // the non-linear fitting routines this parameter is allowed to be // negative. This does not affect the shape of the Gaussian as the // square of the width is used when evaluating the function. //
        // // An enumeration for the HEIGHT, WIDTH and // CENTER parameter index is provided, enabling the setting // and reading of parameters with the [] operator. The // mask() methods can be used to check and set the parameter masks. // //
        // // // Gaussian gf(5.0, 25.0, 7); // gf(25); // = 5.0 // gf[HEIGHT](1.0); // gf.setWidth(2.0); // gf[CENTER](0.0); // gf(1); // = 0.5*height = 0.5 // // // //
      • T should have standard numerical operators and exp() function. Current // implementation only tested for real types. //
      • To obtain derivatives, the derivatives should be defined. // // //
      • Assertion in debug mode if attempt is made to set a negative width //
      • AipsError if incorrect parameter number specified. //
      • Assertion in debug mode if operator(Vector<>) with empty Vector // // //
      • Gaussians that know about their DFT's could be required eventually. // template class Gaussian1D : public Gaussian1DParam { public: //# Enumerations //# Constructors // Constructs the one dimensional Gaussians. Defaults: // height=1, center=0, width(FWHM)=1. // Could not use default arguments // that worked both with gcc and IRIX // Gaussian1D() : Gaussian1DParam() {} explicit Gaussian1D(const T &height) : Gaussian1DParam(height) {} Gaussian1D(const T &height, const T ¢er) : Gaussian1DParam(height, center) {} Gaussian1D(const T &height, const T ¢er, const T &width) : Gaussian1DParam(height, center, width) {} // // Copy constructor (deep copy) // Gaussian1D(const Gaussian1D &other) : Gaussian1DParam(other) {} template Gaussian1D(const Gaussian1D &other) : Gaussian1DParam(other) {} // // Copy assignment (deep copy) Gaussian1D &operator=(const Gaussian1D &other) { Gaussian1DParam::operator=(other); return *this; } // Destructor virtual ~Gaussian1D() {} //# Operators // Evaluate the Gaussian at x. // virtual T eval(typename Function1D::FunctionArg x) const; // //# Member functions // Return a copy of this object from the heap. The caller is responsible // for deleting this pointer. // virtual Function *clone() const { return new Gaussian1D(*this); } virtual Function::DiffType> *cloneAD() const { return new Gaussian1D::DiffType>(*this); } virtual Function::BaseType> *cloneNonAD() const { return new Gaussian1D::BaseType>(*this); } // //# Make members of parent classes known. protected: using Gaussian1DParam::param_p; public: using Gaussian1DParam::HEIGHT; using Gaussian1DParam::CENTER; using Gaussian1DParam::WIDTH; using Gaussian1DParam::fwhm2int; }; #define Gaussian1D_PS Gaussian1D // Partial specialization of Gaussian1D for AutoDiff // // // The name Gaussian1D_PS is only for cxx2html // documentation problems. Use Gaussian1D in your code. // template class Gaussian1D_PS > : public Gaussian1DParam > { public: //# Constructors // Constructs one dimensional Gaussians. // Gaussian1D_PS() : Gaussian1DParam >() {} explicit Gaussian1D_PS(const AutoDiff &height) : Gaussian1DParam >(height) {} Gaussian1D_PS(const AutoDiff &height, const AutoDiff ¢er) : Gaussian1DParam >(height, center) {} Gaussian1D_PS(const AutoDiff &height, const AutoDiff ¢er, const AutoDiff &width) : Gaussian1DParam >(height, center, width) {} // // Copy constructor (deep copy) // Gaussian1D_PS(const Gaussian1D_PS &other) : Gaussian1DParam >(other) {} template Gaussian1D_PS(const Gaussian1D_PS &other) : Gaussian1DParam >(other) {} // // Copy assignment (deep copy) Gaussian1D_PS > & operator=(const Gaussian1D_PS > &other) { Gaussian1DParam >::operator=(other); return *this; } // Destructor virtual ~Gaussian1D_PS() {} //# Operators // Evaluate the Gaussian and its derivatives at x. // virtual AutoDiff eval(typename Function >::FunctionArg x) const; // //# Member functions // Return a copy of this object from the heap. The caller is responsible // for deleting this pointer. // virtual Function > *clone() const { return new Gaussian1D >(*this); } virtual Function >::DiffType> *cloneAD() const { return new Gaussian1D >::DiffType> (*this); } virtual Function >::BaseType> *cloneNonAD() const { return new Gaussian1D >::BaseType> (*this); } // //# Make members of parent classes known. protected: using Gaussian1DParam >::param_p; public: using Gaussian1DParam >::HEIGHT; using Gaussian1DParam >::CENTER; using Gaussian1DParam >::WIDTH; using Gaussian1DParam >::fwhm2int; }; #undef Gaussian1D_PS } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/Gaussian1D.tcc000066400000000000000000000033451476623553700217000ustar00rootroot00000000000000//# Gaussian1D.cc: A one-dimensional Gaussian class //# Copyright (C) 1994,1995,1996,1999,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_GAUSSIAN1D_TCC #define SCIMATH_GAUSSIAN1D_TCC //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Operators template T Gaussian1D::eval(typename Function1D::FunctionArg x) const { T value = (x[0] - param_p[CENTER])/param_p[WIDTH]/fwhm2int; return param_p[HEIGHT] * exp(-(value*value)); } //# Member functions } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/Gaussian1D2.tcc000066400000000000000000000055211476623553700217600ustar00rootroot00000000000000//# Gaussian1D2.cc: One dimensional Gaussian class specialized for AutoDiff //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_GAUSSIAN1D2_TCC #define SCIMATH_GAUSSIAN1D2_TCC //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Operators template AutoDiff Gaussian1D >:: eval(typename Function >::FunctionArg x) const { AutoDiff tmp; if (this->param_p[this->HEIGHT].nDerivatives() > 0) tmp = this->param_p[this->HEIGHT]; else if (this->param_p[this->CENTER].nDerivatives() > 0) tmp = this->param_p[this->CENTER]; else if (this->param_p[this->WIDTH].nDerivatives() > 0) tmp = this->param_p[this->WIDTH]; T x_norm = (x[0] - this->param_p[this->CENTER].value())/ this->param_p[this->WIDTH].value()/this->fwhm2int.value(); T exponential = exp(-(x_norm*x_norm)); // function value tmp.value() = this->param_p[this->HEIGHT].value() * exponential; // get derivatives (assuming either all or none) if (tmp.nDerivatives()>0) { for (uInt j=0; jparam_p.mask(this->HEIGHT)) tmp.deriv(this->HEIGHT) = dev; // derivative wrt center dev *= this->param_p[this->HEIGHT].value()*x_norm*T(2.0)/this->param_p[this->WIDTH].value()/ this->fwhm2int.value(); if (this->param_p.mask(this->CENTER)) tmp.deriv(this->CENTER) = dev; // derivative wrt width if (this->param_p.mask(this->WIDTH)) tmp.deriv(this->WIDTH) = dev* x_norm*this->fwhm2int.value(); } return tmp; } //# Member functions } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/Gaussian1DParam.h000066400000000000000000000162261476623553700223410ustar00rootroot00000000000000//# Gaussian1DParam.h: Parameter handling for one-dimensional Gaussian class //# Copyright (C) 2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_GAUSSIAN1DPARAM_H #define SCIMATH_GAUSSIAN1DPARAM_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // Parameter handling for one dimensional Gaussian class. // // // // //
      • FunctionParam class //
      • Function1D class // // // A 1-dimensional Gaussian's parameters. // // // A Gaussian1D is described by a height, center, and width. // The parameters (height, center and width) may be changed at run time. // // The width of the Gaussian (for the constructors or the setWidth // function) is always specified in terms of the full width at half // maximum (FWHM). It is always positive and attempts to set a non-positive // width will throw an assertion when in debug mode. // // The peak height of the Gaussian can be specified at construction time or // by using the setHeight function. Alternatively the // setFlux function can be used to implicitly set the peak height by // specifying the integrated area under the Gaussian. The height (or flux) // can be positive, negative or zero, as this class makes no assumptions on // what quantity the height represents. // // Changing the width of the Gaussian will not affect // its peak height but will change its flux. So you should always set the // width before setting the flux. // // The parameter interface (see // FunctionParam class), // is used to provide an interface to the // Fitting classes. // // There are 3 parameters that are used to describe the Gaussian: //
          //
        1. The height of the Gaussian. This is identical to the value // returned using the height() member function. //
        2. The center of the Gaussian in the x direction. This is identical to // the value returned using the center() member function. //
        3. The width (FWHM) of the Gaussian. To aid convergence of // the non-linear fitting routines this parameter is allowed to be // negative. This does not affect the shape of the Gaussian as the // square of the width is used when evaluating the function. //
        // // An enumeration for the HEIGHT, WIDTH and // CENTER parameter index is provided, enabling the setting // and reading of parameters with the [] operator. The // mask() methods can be used to check and set the parameter masks. // // This class is in general used implicitly by the Gaussian1D // class only. //
        // // // Gaussian1D gf(5.0, 25.0, 7); // gf(25); // = 5.0 // gf.setHeight(1.0); // gf[WIDTH](2.0); // gf[CENTER](0.0); // gf(1); // = 0.5*height = 0.5 // // // //
      • T should have standard numerical operators and exp() function. Current // implementation only tested for real types (and their AutoDiffs). // // //
      • Assertion in debug mode if attempt is made to set a negative width //
      • AipsError if incorrect parameter number specified. // // //
      • Gaussians that know about their DFT's could be required eventually. // template class Gaussian1DParam : public Function1D { public: //# Enumerations enum { HEIGHT=0, CENTER, WIDTH }; //# Constructors // Constructs the one dimensional Gaussians. Defaults: // height=1, center=0, width(FWHM)=1. // Could not use default arguments // that worked both with gcc and IRIX and all templates // Gaussian1DParam(); explicit Gaussian1DParam(const T &height); Gaussian1DParam(const T &height, const T ¢er); Gaussian1DParam(const T &height, const T ¢er, const T &width); // // Copy constructor (deep copy) // Gaussian1DParam(const Gaussian1DParam &other); template Gaussian1DParam(const Gaussian1DParam &other) : Function1D(other), fwhm2int(T(1.0)/sqrt(log(T(16.0)))) {} // // Copy assignment (deep copy) Gaussian1DParam &operator=(const Gaussian1DParam &other); // Destructor virtual ~Gaussian1DParam(); //# Operators //# Member functions // Give name of function virtual const String &name() const { static String x("gaussian1d"); return x; } // Get or set the peak height of the Gaussian // T height() const { return param_p[HEIGHT]; } void setHeight(const T &height) { param_p[HEIGHT] = height; } // // Get or set the analytical integrated area underneath the Gaussian. // Use these functions as an alternative to the height functions. // T flux() const; void setFlux(const T &flux); // // Get or set the center ordinate of the Gaussian // T center() const { return param_p[CENTER]; } void setCenter(const T &cnter) { param_p[CENTER] = cnter; } // // Get or set the FWHM of the Gaussian. // T width() const { return param_p[WIDTH]; } void setWidth(const T &width) { param_p[WIDTH] = width; } // protected: // Constant to scale halfwidth at 1/e to FWHM T fwhm2int; //# Make members of parent classes known. protected: using Function1D::param_p; public: using Function1D::nparameters; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/Gaussian1DParam.tcc000066400000000000000000000064441476623553700226640ustar00rootroot00000000000000//# Gaussian1DParam.cc: Parameter handling for one-dimensional Gaussian class //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_GAUSSIAN1DPARAM_TCC #define SCIMATH_GAUSSIAN1DPARAM_TCC //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Statics ///template ///const T Gaussian1DParam::fwhm2int = T(1.0)/sqrt(log(T(16.0))); //# Constructors template Gaussian1DParam::Gaussian1DParam() : Function1D(3), fwhm2int(T(1.0)/sqrt(log(T(16.0)))) { param_p[HEIGHT] = T(1.0); param_p[CENTER] = T(0.0); param_p[WIDTH] = T(1.0); } template Gaussian1DParam::Gaussian1DParam(const T &height) : Function1D(3), fwhm2int(T(1.0)/sqrt(log(T(16.0)))) { param_p[HEIGHT] = height; param_p[CENTER] = T(0.0); param_p[WIDTH] = T(1.0); } template Gaussian1DParam::Gaussian1DParam(const T &height, const T ¢er) : Function1D(3), fwhm2int(T(1.0)/sqrt(log(T(16.0)))) { param_p[HEIGHT] = height; param_p[CENTER] = center; param_p[WIDTH] = T(1.0); } template Gaussian1DParam::Gaussian1DParam(const T &height, const T ¢er, const T &width) : Function1D(3), fwhm2int(T(1.0)/sqrt(log(T(16.0)))) { param_p[HEIGHT] = height; param_p[CENTER] = center; param_p[WIDTH] = width; } template Gaussian1DParam::Gaussian1DParam(const Gaussian1DParam &other) : Function1D(other), fwhm2int(T(1.0)/sqrt(log(T(16.0)))) {} template Gaussian1DParam::~Gaussian1DParam() {} //# Operators template Gaussian1DParam & Gaussian1DParam::operator=(const Gaussian1DParam &other) { if (this != &other) { fwhm2int = other.fwhm2int; Function1D::operator=(other); } return *this; } //# Member functions template T Gaussian1DParam::flux() const { return param_p[HEIGHT]*abs(param_p[WIDTH])*fwhm2int/T(0.5 * M_2_SQRTPI); } template void Gaussian1DParam::setFlux(const T &flux) { param_p[HEIGHT] = flux*T(0.5 * M_2_SQRTPI)/abs(param_p[WIDTH])/fwhm2int; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/Gaussian2D.h000066400000000000000000000330471476623553700213610ustar00rootroot00000000000000//# Gaussian2D.h: A two-dimensional Gaussian class //# Copyright (C) 1995,1996,1997,2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_GAUSSIAN2D_H #define SCIMATH_GAUSSIAN2D_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // A two dimensional Gaussian class. // // // // //
      • Gaussian2DParam //
      • Function // // // A Gaussian2D functional is designed exclusively for calculating a // Gaussian (or Normal) distribution in two dimensions. Other classes exist // for calculating these functions in two // (Gaussian1D) and N // (GaussianND) dimensions. // // // A Gaussian2D is described by a height, center, and width, // and position angle. Its fundamental operation is evaluating itself // at some (x,y) // coordinate. Its parameters (height, center and width, position angle) may // be changed at run time. // // The width of the Gaussian (for the constructors or the setWidth // function) is always specified in terms of the full width at half // maximum (FWHM). The major axis is parallel with the y axis when the // position angle is zero. The major axis will always have a larger width // than the minor axis. // // It is not possible to set the width of the major axis (using the // setMajorAxis function) smaller than the width of the current minor // axis. Similarly it is not possible to set the width of the minor axis // (using the setMinorAxis function) to be larger than the // current major axis. Exceptions are thrown if these rules are violated or // if the either the major or minor axis is set to a non-positive width. To // set both axis in one hit use the setWidth function. All // these restrictions can be overcome when the parameters interface is used // (see below). // // The position angle is the angle between the y axis and the major axis and // is measured counterclockwise, so a position angle of 45 degrees rotates // the major axis to the line where y=-x. The position angle is always // specified and returned in radians. When using the setPA // function its value must be between -2pi and + 2pi, and the returned value // from the pa function will always be a value between 0 and // pi. // // The axial ratio can be used as an alternative to specifying the width of // the minor axis. It is the ratio between the minor and major axis // widths. The axial ratio is constrained to be between zero and one, and // specifying something different (using setAxialRatio) will throw an // exception. // // The peak height of the Gaussian can be specified at construction time or // by using the setHeight function. Alternatively the // setFlux function can be used to implicitly set the peak height by // specifying the integrated area under the Gaussian. The height (or flux) // can be positive, negative or zero, as this class makes no assumptions on // what quantity the height represents. // // Changing the width of the Gaussian will not affect // its peak height but will change its flux. So you should always set the // width before setting the flux. // // The parameter interface (see // Gaussian2DParam class), // is used to provide an interface to the // Fitting classes. // // There are 6 parameters that are used to describe the Gaussian: //
          //
        1. The height of the Gaussian. This is identical to the value // returned using the height member function. //
        2. The center of the Gaussian in the x direction. This is identical to // the value returned using the xCenter member function. //
        3. The center of the Gaussian in the y direction. This is identical to // the value returned using the yCenter member function. //
        4. The width (FWHM) of the Gaussian on one axis. Initially this will be // the major axis, but if the parameters are adjusted by a Fitting // class, it may become the axis with the smaller width. To aid // convergence of the non-linear fitting routines this parameter is // allowed to be negative. This does not affect the shape of the // Gaussian as the squares of the widths are used when evaluating the // function. //
        5. A modified axial ratio. This parameter is the ratio of the width on // the 'other' axis (which initially is the minor axis) and axis given // by parameter YWIDTH. Because these internal widths are allowed to be // negative and because there is no constraints on which axis is the // larger one the modified axial ratio is not constrained to be between // zero and one. //
        6. The position angle. This represents the angle (in radians) between // the axis used by parameter 4, and the y axis, measured // counterclockwise. If parameter 4 represents the major axis width // then this parameter will be identical to the position angle, // otherwise it will be different by 90 degrees. The tight constraints // on the value of the rotation angle enforced by the setPA() function // are relaxed so that any value between -6000 and 6000 is allowed. It // is still interpreted in radians. //
        // // An enumeration for the parameter index is provided, enabling the setting // and reading of parameters with the [] operator. The // mask() methods can be used to check and set the parameter masks. // //
        // // // Gaussian2D g(10.0, 0.0, 0.0, 2.0, 1.0, 0.0); // Vector x(2); // x(0) = 1.0; x(1) = 0.5; // cout << "g(" << x(0) << "," << x(1) << ") = " << g(x) << endl; // // // //
      • T should have standard numerical operators and exp() function. Current // implementation only tested for real types. //
      • To obtain derivatives, the derivatives should be defined. // // //
      • Assertion in debug mode if attempt is made to set a negative width //
      • AipsError if incorrect parameter number specified. //
      • Assertion in debug mode if operator(Vector<>) with empty Vector // // //
      • Gaussians that know about their DFT's could be required eventually. // template class Gaussian2D : public Gaussian2DParam { public: //# Enumerations //# Constructors // Constructs the two dimensional Gaussians. Defaults: // height=1, center=0, width(FWHM)=1, PA=0. The center and width vectors // must have two elements // Could not use default arguments // that worked both with gcc and IRIX // Gaussian2D() : Gaussian2DParam() {} Gaussian2D(const T &height, const Vector ¢er, const Vector &width, const T &pa) : Gaussian2DParam(height, center, width, pa) {} Gaussian2D(const T &height, const T &xCenter, const T &yCenter, const T &majorAxis, const T &axialRatio, const T &pa) : Gaussian2DParam(height, xCenter, yCenter, majorAxis, axialRatio, pa) {} // // Copy constructor (deep copy) // Gaussian2D(const Gaussian2D &other) : Gaussian2DParam(other) {} template Gaussian2D(const Gaussian2D &other) : Gaussian2DParam(other) {} // // Copy assignment (deep copy) Gaussian2D &operator=(const Gaussian2D &other) { Gaussian2DParam::operator=(other); return *this; } // Destructor virtual ~Gaussian2D() {} //# Operators // Evaluate the Gaussian at x. // virtual T eval(typename Function::FunctionArg x) const; // //# Member functions // Return a copy of this object from the heap. The caller is responsible // for deleting this pointer. // virtual Function *clone() const { return new Gaussian2D(*this); } virtual Function::DiffType> *cloneAD() const { return new Gaussian2D::DiffType>(*this); } virtual Function::BaseType> *cloneNonAD() const { return new Gaussian2D::BaseType>(*this); } // //# Make members of parent classes known. protected: using Gaussian2DParam::param_p; using Gaussian2DParam::thePA; using Gaussian2DParam::theCpa; using Gaussian2DParam::theSpa; using Gaussian2DParam::theXwidth; public: using Gaussian2DParam::HEIGHT; using Gaussian2DParam::XCENTER; using Gaussian2DParam::YCENTER; using Gaussian2DParam::YWIDTH; using Gaussian2DParam::RATIO; using Gaussian2DParam::PANGLE; using Gaussian2DParam::fwhm2int; }; #define Gaussian2D_PS Gaussian2D // Partial specialization of Gaussian2D for AutoDiff // // // The name Gaussian2D_PS is only for cxx2html // documentation problems. Use Gaussian2D in your code. // template class Gaussian2D_PS > : public Gaussian2DParam > { public: //# Constructors // Constructs two dimensional Gaussians. // Gaussian2D_PS() : Gaussian2DParam >() {} Gaussian2D_PS(const AutoDiff &height, const Vector > ¢er, const Vector > &width, const AutoDiff &pa) : Gaussian2DParam >(height, center, width, pa) {} Gaussian2D_PS(const AutoDiff &height, const AutoDiff &xCenter, const AutoDiff &yCenter, const AutoDiff &majorAxis, const AutoDiff &axialRatio, const AutoDiff &pa) : Gaussian2DParam >(height, xCenter, yCenter, majorAxis, axialRatio, pa) {} // // Copy constructor (deep copy) // Gaussian2D_PS(const Gaussian2D_PS &other) : Gaussian2DParam >(other) {} template Gaussian2D_PS(const Gaussian2D_PS &other) : Gaussian2DParam >(other) {} // // Copy assignment (deep copy) Gaussian2D_PS > & operator=(const Gaussian2D_PS > &other) { Gaussian2DParam >::operator=(other); return *this; } // Destructor virtual ~Gaussian2D_PS() {} //# Operators // Evaluate the Gaussian and its derivatives at x. // virtual AutoDiff eval(typename Function >::FunctionArg x) const; // //# Member functions // Return a copy of this object from the heap. The caller is responsible // for deleting this pointer. // virtual Function > *clone() const { return new Gaussian2D >(*this); } virtual Function >::DiffType> *cloneAD() const { return new Gaussian2D >::DiffType> (*this); } virtual Function >::BaseType> *cloneNonAD() const { return new Gaussian2D >::BaseType> (*this); } // //# Make members of parent classes known. protected: using Gaussian2DParam >::param_p; using Gaussian2DParam >::thePA; using Gaussian2DParam >::theCpa; using Gaussian2DParam >::theSpa; using Gaussian2DParam >::theXwidth; public: using Gaussian2DParam >::HEIGHT; using Gaussian2DParam >::XCENTER; using Gaussian2DParam >::YCENTER; using Gaussian2DParam >::YWIDTH; using Gaussian2DParam >::RATIO; using Gaussian2DParam >::PANGLE; using Gaussian2DParam >::fwhm2int; }; #undef Gaussian2D_PS } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/Gaussian2D.tcc000066400000000000000000000041101476623553700216700ustar00rootroot00000000000000//# Gaussian2D.cc: A two-dimensional Gaussian class //# Copyright (C) 1994,1995,1996,1999,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_GAUSSIAN2D_TCC #define SCIMATH_GAUSSIAN2D_TCC //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Operators template T Gaussian2D::eval(typename Function::FunctionArg x) const { T xnorm = x[0] - param_p[XCENTER]; T ynorm = x[1] - param_p[YCENTER]; if (param_p[PANGLE] != thePA) { thePA = param_p[PANGLE]; theCpa = cos(thePA); theSpa = sin(thePA); } const T temp(xnorm); xnorm = theCpa*temp + theSpa*ynorm; ynorm = - theSpa*temp + theCpa*ynorm; xnorm /= param_p[YWIDTH]*param_p[RATIO]*fwhm2int; ynorm /= param_p[YWIDTH]*fwhm2int; return param_p[HEIGHT]*exp(-(xnorm*xnorm + ynorm*ynorm)); } //# Member functions //# Member functions } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/Gaussian2D2.tcc000066400000000000000000000111601476623553700217550ustar00rootroot00000000000000//# Gaussian2D2.cc: Two dimensional Gaussian class specialized for AutoDiff //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_GAUSSIAN2D2_TCC #define SCIMATH_GAUSSIAN2D2_TCC //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Operators template AutoDiff Gaussian2D >:: eval(typename Function >::FunctionArg x) const { AutoDiff tmp; if (this->param_p[this->HEIGHT].nDerivatives() > 0) tmp = this->param_p[this->HEIGHT]; else if (this->param_p[this->XCENTER].nDerivatives() > 0) tmp = this->param_p[this->XCENTER]; else if (this->param_p[this->YCENTER].nDerivatives() > 0) tmp = this->param_p[this->YCENTER]; else if (this->param_p[this->YWIDTH].nDerivatives() > 0) tmp = this->param_p[this->YWIDTH]; else if (this->param_p[this->RATIO].nDerivatives() > 0) tmp = this->param_p[this->RATIO]; else if (this->param_p[this->PANGLE].nDerivatives() > 0) tmp = this->param_p[this->PANGLE]; T x2mean = x[0] - this->param_p[this->XCENTER].value(); T y2mean = x[1] - this->param_p[this->YCENTER].value(); if (this->param_p[this->PANGLE] != this->thePA) { this->thePA = this->param_p[this->PANGLE]; this->theCpa = cos(this->thePA); this->theSpa = sin(this->thePA); } T xnorm = x2mean*this->theCpa.value() + y2mean*this->theSpa.value(); T ynorm = -x2mean*this->theSpa.value() + y2mean*this->theCpa.value(); T xnorm2 = xnorm*xnorm; T ynorm2 = ynorm*ynorm; this->theXwidth.value() = this->param_p[this->YWIDTH].value() * this->param_p[this->RATIO].value(); T xwidth2 = this->theXwidth.value()*this->theXwidth.value()* this->fwhm2int.value()*this->fwhm2int.value(); T ywidth2 = this->param_p[this->YWIDTH].value()*this->param_p[this->YWIDTH].value()* this->fwhm2int.value()*this->fwhm2int.value(); T x2w = T(2.0)*xnorm/xwidth2; T y2w = T(2.0)*ynorm/ywidth2; T x2w2 = x2w*xnorm; T y2w2 = y2w*ynorm; T exponential = exp(-(xnorm2/xwidth2 + ynorm2/ywidth2)); // function value tmp.value() = this->param_p[this->HEIGHT].value()*exponential; // get derivatives (assuming either all or none) if (tmp.nDerivatives()>0) { for (uInt k = 0; k < tmp.nDerivatives(); k++) tmp.deriv(k) = 0.0; // derivative wrt height T dev = exponential; if (this->param_p.mask(this->HEIGHT)) tmp.deriv(this->HEIGHT) = dev; // derivative wrt x0 (mean) dev *= this->param_p[this->HEIGHT].value(); if (this->param_p.mask(this->XCENTER)) tmp.deriv(this->XCENTER) = dev* (x2w*this->theCpa.value() - y2w*this->theSpa.value()); // derivative wrt y0 (mean) if (this->param_p.mask(this->YCENTER)) tmp.deriv(this->YCENTER) = dev* (this->theSpa.value()*x2w + this->theCpa.value()*y2w); // derivative wrt wy (width) if (this->param_p.mask(this->YWIDTH)) tmp.deriv(this->YWIDTH) = dev* ((x2w2+y2w2)/this->param_p[this->YWIDTH].value()); // derivative wrt ratio (r=wx/wy, df/dr=(df/wx)*(dwx/dr), and dwx/dr=wy) if (this->param_p.mask(this->RATIO)) tmp.deriv(this->RATIO) = dev* x2w2*this->param_p[this->YWIDTH].value()/ (this->theXwidth.value()); // derivative wrt theta (rotation) if (this->param_p.mask(this->PANGLE)) tmp.deriv(this->PANGLE) = -dev* (x2w*(-x2mean*this->theSpa.value() + y2mean*this->theCpa.value()) + y2w*(-x2mean*this->theCpa.value() - y2mean*this->theSpa.value())); } return tmp; } //# Member functions } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/Gaussian2DParam.h000066400000000000000000000264451476623553700223460ustar00rootroot00000000000000//# Gaussian2DParam.h: Parameter handling for 2 dimensional Gaussian class //# Copyright (C) 2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_GAUSSIAN2DPARAM_H #define SCIMATH_GAUSSIAN2DPARAM_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Parameter handling for 2 dimensional Gaussian class // // // // // //
      • FunctionParam class //
      • Function class // // // A 2-dimensional Gaussian's parameters. // // // A Gaussian2D is described by a height, center, and width, // and position angle. // The width of the Gaussian (for the constructors or the setWidth // function) is always specified in terms of the full width at half // maximum (FWHM). The major axis is parallel with the y axis when the // position angle is zero. The major axis will always have a larger width // than the minor axis. // // It is not possible to set the width of the major axis (using the // setMajorAxis function) smaller than the width of the current minor // axis. Similarly it is not possible to set the width of the minor axis // (using the setMinorAxis function) to be larger than the // current major axis. Exceptions are thrown if these rules are violated or // if either the major or minor axis is set to a non-positive width. To // set both axis in one hit use the setWidth function. All // these restrictions can be overcome when the parameters interface is used // (see below). // // The position angle is the angle between the y axis and the major axis and // is measured counter-clockwise, so a position angle of 45 degrees rotates // the major axis to the line where y=-x. // The position angle is always // specified and returned in radians. When using the setPA // function its value must be between -2pi and + 2pi, and the returned value // from the pa function will always be a value between 0 and // pi. // // The axial ratio can be used as an alternative to specifying the width of // the minor axis. It is the ratio between the minor and major axis // widths. The axial ratio is constrained to be between zero and one, and // specifying something different (using setAxialRatio) will throw an // exception. // // The peak height of the Gaussian can be specified at construction time or // by using the setHeight function. Alternatively the // setFlux function can be used to implicitly set the peak height by // specifying the integrated area under the Gaussian. The height (or flux) // can be positive, negative or zero, as this class makes no assumptions on // what quantity the height represents. // // Changing the width of the Gaussian will not affect // its peak height but will change its flux. So you should always set the // width before setting the flux. // // The parameter interface (see // FunctionParam class), // is used to provide an interface to the // Fitting classes. // // There are 6 parameters that are used to describe the Gaussian: //
          //
        1. The height of the Gaussian. This is identical to the value // returned using the height member function. //
        2. The center of the Gaussian in the x direction. This is identical to // the value returned using the xCenter member function. //
        3. The center of the Gaussian in the y direction. This is identical to // the value returned using the yCenter member function. //
        4. The width (FWHM) of the Gaussian on one axis. Initially this will be // the major axis, but if the parameters are adjusted by a Fitting // class, it may become the axis with the smaller width. To aid // convergence of the non-linear fitting routines this parameter is // allowed to be negative. This does not affect the shape of the // Gaussian as the squares of the widths are used when evaluating the // function. //
        5. A modified axial ratio. This parameter is the ratio of the width on // the 'other' axis (which initially is the minor axis) and axis given // by parameter 4. Because these internal widths are allowed to be // negative and because there is no constraints on which axis is the // larger one the modified axial ratio is not constrained to be between // zero and one. //
        6. The rotation angle. This represents the angle (in radians) between // the axis used by parameter 4, and the y axis, measured // counterclockwise. If parameter 4 represents the major axis width // then this parameter will be identical to the position angle, // otherwise it will be different by 90 degrees. The tight constraints // on the value of the rotation angle enforced by the setPA() function // are relaxed so that any value between -6000 and 6000 is allowed. It // is still interpreted in radians. //
        // // An enumeration for the HEIGHT, XCENTER, // YCENTER, YWIDTH, RATIO, PANGLE // parameter index is provided, enabling the setting // and reading of parameters with the [] operator. The // mask() methods can be used to check and set the parameter masks. // // This class is in general used implicitly by the Gaussian2D // class only. // // // Other points to bear in mind when fitting this class to measured data // are: //
          //
        • If you need to fit a circular Gaussian to data you MUST set the // axial ratio to one, and mask the position angle and axial ratio // parameters. This avoids rank deficiency in the fitting routines as // the position angle is meaningless when the major and minor axis are // equal. //
        • If fitting an elliptical Gaussian your initial model should not be a // circular Gaussian. //
        //
        // //
        // // // Gaussian2D g(10.0, 0.0, 0.0, 2.0, 1.0, 0.0); // Vector x(2); // x(0) = 1.0; x(1) = 0.5; // cout << "g(" << x(0) << "," << x(1) << ") = " << g(x) << endl; // // // // Gaussian2D objects allow us to represent models of // the sky in a more conventional way than the generic interface used in the // GaussianND class does. // // //
      • T should have standard numerical operators and exp() function. Current // implementation only tested for real types (and AutoDiff of them). // // //
      • Assertion in debug mode if attempt is made to set a negative width //
      • AipsError if incorrect parameter number specified. // // //
      • Gaussians that know about their DFT's could be required eventually. // template class Gaussian2DParam : public Function { public: //# Enumerations enum { HEIGHT=0, XCENTER, YCENTER, YWIDTH, RATIO, PANGLE}; //# Constructors // Constructs the two dimensional Gaussians. Defaults: // height=1, center=0, width(FWHM)=1, pa=0. // Gaussian2DParam(); Gaussian2DParam(const T &height, const Vector ¢er, const Vector &width, const T &pa); Gaussian2DParam(const T &height, const T &xCenter, const T &yCenter, const T &majorAxis, const T &axialRatio, const T &pa); // // Copy constructor (deep copy) // Gaussian2DParam(const Gaussian2DParam &other); template Gaussian2DParam(const Gaussian2DParam &other) : Function(other), fwhm2int(T(1.0)/sqrt(log(T(16.0)))) { majorAxis(); setPA(PA()); } // // Copy assignment (deep copy) Gaussian2DParam &operator=(const Gaussian2DParam &other); // Destructor virtual ~Gaussian2DParam(); //# Operators // Variable dimensionality virtual uInt ndim() const { return 2; } //# Member functions // Give name of function virtual const String &name() const { static String x("gaussian2d"); return x; } // Get or set the peak height of the Gaussian // T height() const { return param_p[HEIGHT]; } void setHeight(const T &height) { param_p[HEIGHT] = height; } // // Get or set the analytical integrated area underneath the Gaussian. // Use these functions as an alternative to the height functions. // T flux() const; void setFlux(const T &flux); // // Get or set the center ordinate of the Gaussian // Vector center() const; void setCenter(const Vector ¢er); T xCenter() const { return param_p[XCENTER]; } void setXcenter(const T &cnter) { param_p[XCENTER] = cnter; } T yCenter() const { return param_p[YCENTER]; } void setYcenter(const T &cnter) { param_p[YCENTER] = cnter; } // // Set or get the FWHM of the Gaussian. // Vector width() const; void setWidth(const Vector &width); T majorAxis() const; void setMajorAxis(const T &width); T minorAxis() const; void setMinorAxis(const T &width); T axialRatio() const; void setAxialRatio(const T &axialRatio); // // Set/get the rotation angle (orientation) of the Gaussian. PA is given // in radians counterclockwise. // T PA() const; void setPA(const T &pa); // protected: // Constant to scale halfwidth at 1/e to FWHM T fwhm2int; // cached vale of the PA mutable T thePA; // cached values of the cos and sine of thePA // mutable T theSpa; mutable T theCpa; // // cached vale of the Xwidth = ratio*theYwidth; mutable T theXwidth; //# Make members of parent classes known. protected: using Function::param_p; public: using Function::nparameters; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/Gaussian2DParam.tcc000066400000000000000000000176351476623553700226710ustar00rootroot00000000000000//# Gaussian2DParam.cc: Parameter handling for 2 dimensional Gaussian class //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_GAUSSIAN2DPARAM_TCC #define SCIMATH_GAUSSIAN2DPARAM_TCC //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Statics ///template ///const T Gaussian2DParam::fwhm2int = T(1.0)/sqrt(log(T(16.0))); //# Constructors template Gaussian2DParam::Gaussian2DParam() : Function(6), fwhm2int(T(1.0)/sqrt(log(T(16.0)))), thePA(0), theSpa(T(0.0)), theCpa(T(1.0)) { param_p[HEIGHT] = T(1.0); param_p[XCENTER] = T(0.0); param_p[YCENTER] = T(0.0); param_p[YWIDTH] = T(1.0); param_p[RATIO] = T(1.0); param_p[PANGLE] = T(0.0); theXwidth = T(1.0); } template Gaussian2DParam::Gaussian2DParam(const T &height, const T &xCenter, const T &yCenter, const T &majorAxis, const T &axialRatio, const T &pa) : Function(6), fwhm2int(T(1.0)/sqrt(log(T(16.0)))) { param_p[HEIGHT] = height; param_p[XCENTER] = xCenter; param_p[YCENTER] = yCenter; param_p[YWIDTH] = majorAxis; param_p[RATIO] = T(1.0); param_p[PANGLE] = T(0.0); theXwidth = T(0.0); setMajorAxis(majorAxis); setAxialRatio(axialRatio); setPA(pa); } template Gaussian2DParam::Gaussian2DParam(const T &height, const Vector ¢er, const Vector &width, const T &pa) : Function(6), fwhm2int(T(1.0)/sqrt(log(T(16.0)))) { param_p[HEIGHT] = height; param_p[YWIDTH] = T(0.0); theXwidth = T(0.0); setCenter(center); setWidth(width); setPA(pa); } template Gaussian2DParam::Gaussian2DParam(const Gaussian2DParam &other) : Function(other), fwhm2int(T(1.0)/sqrt(log(T(16.0)))) { theXwidth = other.theXwidth; thePA = other.thePA; theSpa = other.theSpa; theCpa = other.theCpa; } template Gaussian2DParam::~Gaussian2DParam() {} //# Operators template Gaussian2DParam & Gaussian2DParam::operator=(const Gaussian2DParam &other) { if (this != &other) { fwhm2int = other.fwhm2int; Function::operator=(other); theXwidth = other.theXwidth; theSpa = other.theSpa; theCpa = other.theCpa; } return *this; } //# Member functions template T Gaussian2DParam::flux() const { theXwidth = param_p[YWIDTH]*param_p[RATIO]; return param_p[HEIGHT]*abs(param_p[YWIDTH]*theXwidth* fwhm2int*fwhm2int*T(M_PI)); } template void Gaussian2DParam::setFlux(const T &flux) { theXwidth = param_p[YWIDTH]*param_p[RATIO]; param_p[HEIGHT] = flux/(abs(param_p[YWIDTH]*theXwidth*T(M_PI))* fwhm2int*fwhm2int); } template Vector Gaussian2DParam::center() const { Vector center(2); center(0) = param_p[XCENTER]; center(1) = param_p[YCENTER]; return center; } template void Gaussian2DParam::setCenter(const Vector ¢er) { DebugAssert(center.nelements() == 2, AipsError); param_p[XCENTER] = center(0); param_p[YCENTER] = center(1); } template Vector Gaussian2DParam::width() const { Vector width(2); width(0) = majorAxis(); width(1) = minorAxis(); return width; } template void Gaussian2DParam::setWidth(const Vector &width) { DebugAssert(width.nelements() == 2, AipsError); if (abs(width(0)) > minorAxis()) { setMajorAxis(width(0)); setMinorAxis(width(1)); } else { setMinorAxis(width(1)); setMajorAxis(width(0)); } } template T Gaussian2DParam::majorAxis() const { theXwidth = param_p[YWIDTH]*param_p[RATIO]; return max(abs(param_p[YWIDTH]), abs(theXwidth)); } template void Gaussian2DParam::setMajorAxis(const T &width) { if (width <= T(0.0)) { throw(AipsError("Gaussian2DParam::setMajorAxis(const T &width)" " - width must be positive")); } // The near function is necessary for Intel processors (and doesn't hurt for // other architectures) because of the extra precision that floating point // variables have when returned in floating point registers. See // http://aips2.nrao.edu/mail/aips2-lib/1101 for a discussion of this. The // near function was added here and in the setMinorAxis function to fix // defect AOCso00071 const T minorWidth = minorAxis(); if (width < minorWidth && !near(width, minorWidth)) { throw(AipsError("Gaussian2DParam::setMajorAxis(const T &width)" " - major axis is smaller than minor axis")); } theXwidth = param_p[YWIDTH]*param_p[RATIO]; if (abs(theXwidth) > abs(param_p[YWIDTH])) theXwidth = width; else param_p[YWIDTH] = width; param_p[RATIO] = theXwidth/param_p[YWIDTH]; } template T Gaussian2DParam::minorAxis() const { theXwidth = param_p[YWIDTH]*param_p[RATIO]; return min(abs(param_p[YWIDTH]),abs(theXwidth)); } template void Gaussian2DParam::setMinorAxis(const T &width) { if (width <= T(0.0)) { throw(AipsError("Gaussian2DParam::setMinorAxis(const T &width)" " - width must be positive")); } const T majorWidth = majorAxis(); if (width > majorWidth && !near(width, majorWidth)) { throw(AipsError("Gaussian2DParam::setMinorAxis(const T &width)" " - minor axis is greater than major axis")); } theXwidth = param_p[YWIDTH]*param_p[RATIO]; if (abs(theXwidth) <= abs(param_p[YWIDTH])) theXwidth = width; else param_p[YWIDTH] = width; param_p[RATIO] = theXwidth/param_p[YWIDTH]; } template T Gaussian2DParam::axialRatio() const { return minorAxis()/majorAxis(); } template void Gaussian2DParam::setAxialRatio(const T &axialRatio) { if (axialRatio <= T(0.0) || axialRatio > T(1.0)) { throw(AipsError("Gaussian2DParam::setAxialRatio(const T &axialRatio)" " - axialRatio must be between (0,1]")); } setMinorAxis(axialRatio*majorAxis()); } template T Gaussian2DParam::PA() const { T pa; theXwidth = param_p[YWIDTH]*param_p[RATIO]; if (abs(param_p[YWIDTH]) >= abs(theXwidth)) pa = fmod(param_p[PANGLE], T(M_PI)); else pa = fmod(param_p[PANGLE]+T(M_PI_2), T(M_PI)); if (pa < T(0.0)) pa += T(M_PI); return pa; } template void Gaussian2DParam::setPA(const T &pa) { if (abs(pa) > T(2.0*M_PI)) { throw(AipsError("Gaussian2DParam::setPA(const T &pa)" " - PA must be in radians and between -2pi and 2pi")); } theXwidth = param_p[YWIDTH]*param_p[RATIO]; if (abs(param_p[YWIDTH]) >= abs(theXwidth)) param_p[PANGLE] = pa; else param_p[PANGLE] = pa - T(M_PI_2); theCpa = cos(param_p[PANGLE]); theSpa = sin(param_p[PANGLE]); thePA = param_p[PANGLE]; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/Gaussian3D.h000066400000000000000000000266501476623553700213640ustar00rootroot00000000000000//# Gaussian3D.h: A three-dimensional Gaussian class //# Copyright (C) 1995,1996,1997,2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_GAUSSIAN3D_H #define SCIMATH_GAUSSIAN3D_H #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // A three dimensional Gaussian class. // // // // //
      • Gaussian3DParam //
      • Function // // // A Gaussian3D functional is designed exclusively for calculating a // Gaussian (or Normal) distribution in three dimensions. Other classes exist // for calculating these functions in one // (Gaussian1D), two // (Gaussian2D), and N // (GaussianND) dimensions. // // // A Gaussian3D is described by a height, center, and width, // and position angles. Its fundamental operation is evaluating itself // at some (x,y,z) // coordinate. Its parameters (height, center and width, position angles) may // be changed at run time. // The width of the Gaussian is now specified in terms of the full width // at half maximum (FWHM), like the 2D and 1D Gaussian functional classes. // The three axis values refer to the x, y, and z axes, and unlike with the // 2D Gaussian any of the three axes may be the longest; instead, the position // angles are restricted. The first position angle, theta, is the longitudinal // angle, referring to the rotation (counterclockwise) around the z-axis. The // second, phi, is the latidudinal angle, referring to the rotation around // the theta-rotated y axis. The domain of both angles is -pi/4 < A < pi/4, // although the angles are not constrained when fitting and can be set outside // the domain by setting the parameters directly using Functional operator[]. // (Note that the use of theta and phi corresponds to the mathematics // convention for these angles, not the physics convention.) // The parameter interface (see // Gaussian3DParam class), // is used to provide an interface to the // Fitting classes. // // There are 9 parameters that are used to describe the Gaussian: //
          //
        1. The height of the Gaussian. This is identical to the value // returned using the height member function. //
        2. The center of the Gaussian in the x direction. This is identical to // the value returned using the xCenter member function. //
        3. The center of the Gaussian in the y direction. This is identical to // the value returned using the yCenter member function. //
        4. The center of the Gaussian in the z direction. This is identical to // the value returned using the zCenter member function. //
        5. The width of the Gaussian along the x-axis. //
        6. The width of the Gaussian along the y-axis. //
        7. The width of the Gaussian along the z-axis. //
        8. The longitudinal position angle, theta (in radians) //
        9. The latitudinal position angle, phi (also in radians). //
        // An enumeration for the parameter index is provided, enabling the setting // and reading of parameters with the [] operator. The // mask() methods can be used to check and set the parameter masks. // //
        // // // Gaussian3D g(9.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0); // Vector x(3); // x(0) = 1.0; x(1) = 0.5; x(2) = 0.0 // cout << "g(" << x(0) << "," << x(1) << "," << x(2) << ")=" << g(x) << endl; // // // // The GaussianND class does not contain explicit derivatives // and was insufficient for fitting 3D Gaussians to data. // // //
      • T should have standard numerical operators and exp() function. Current // implementation only tested for real types. //
      • To obtain derivatives, the derivatives should be defined. // // //
      • Assertion in debug mode if attempt is made to set a negative width //
      • AipsError if incorrect parameter number specified. //
      • Assertion in debug mode if operator(Vector<>) with empty Vector // // //
      • Optimize derivative calculations for faster fitting? // template class Gaussian3D : public Gaussian3DParam { public: // A functional for a rotated, 3D Gaussian. Similar to Gaussian2D, but // the xWidth, yWidth, and zWidth parameters are not adjusted for FWHM; // they are identical to the parameters used in the function. // Constructs the three-dimensional Gaussians. Defaults: // height = 1, center = {0,0,0}, width = {1,1,1}, theta = phi = 0. // The center and width vectors must have three elements. // Gaussian3D(); Gaussian3D(T height, const Vector& center, const Vector& width, T theta, T phi); Gaussian3D(T &height, T &xCenter, T &yCenter, T &zCenter, T &xWidth, T &yWidth, T &zWidth, T &theta, T &phi); // // Copy constructor // Gaussian3D(const Gaussian3D &other); template Gaussian3D(const Gaussian3D &other) : Gaussian3DParam(other) {} // // Destructor virtual ~Gaussian3D(); // Assignment operator Gaussian3D &operator=(const Gaussian3D &other); // Evaluate the Gaussian at x. virtual T eval(typename Function::FunctionArg x) const; // Return a copy of this object from the heap. The caller is responsible // for deleting this pointer. // virtual Function *clone() const; virtual Function::DiffType> *cloneAD() const { return new Gaussian3D::DiffType>(*this); } virtual Function::BaseType> *cloneNonAD() const { return new Gaussian3D::BaseType>(*this); } // private: // AutoDiff does not have a square() function, so one is provided here. T sq(T v) const; //# Make members of parent classes known. protected: using Gaussian3DParam::param_p; using Gaussian3DParam::stoT_p; using Gaussian3DParam::stoP_p; using Gaussian3DParam::cosT_p; using Gaussian3DParam::cosP_p; using Gaussian3DParam::sinT_p; using Gaussian3DParam::sinP_p; using Gaussian3DParam::cosTcosP_p; using Gaussian3DParam::cosTsinP_p; using Gaussian3DParam::sinTcosP_p; using Gaussian3DParam::sinTsinP_p; public: using Gaussian3DParam::H; using Gaussian3DParam::CX; using Gaussian3DParam::CY; using Gaussian3DParam::CZ; using Gaussian3DParam::AX; using Gaussian3DParam::AY; using Gaussian3DParam::AZ; using Gaussian3DParam::THETA; using Gaussian3DParam::PHI; using Gaussian3DParam::fwhm2int; using Gaussian3DParam::settrigvals; }; // AUTODIFF SPECIALIZATION #define Gaussian3D_PS Gaussian3D // Partial specialization of Gaussian3D for AutoDiff // // // The name Gaussian3D_PS is only for cxx2html // documentation problems. Use Gaussian3D in your code. // template class Gaussian3D_PS > : public Gaussian3DParam > { public: Gaussian3D_PS(); Gaussian3D_PS(const AutoDiff &height, const Vector >& center, const Vector >& width, const AutoDiff& theta, const AutoDiff& phi); Gaussian3D_PS(AutoDiff& height, AutoDiff& xCenter, AutoDiff& yCenter, AutoDiff& zCenter, AutoDiff& xWidth, AutoDiff& yWidth, AutoDiff& zWidth, AutoDiff& theta, AutoDiff& phi); Gaussian3D_PS(const Gaussian3D_PS > &other); template Gaussian3D_PS(const Gaussian3D_PS &other) : Gaussian3DParam >(other) {} virtual ~Gaussian3D_PS(); // Gaussian3D_PS > &operator=(const Gaussian3D_PS > &other); // virtual AutoDiff eval(typename Function >::FunctionArg x) const; virtual Function > *clone() const; virtual Function >::DiffType> *cloneAD() const { return new Gaussian3D >::DiffType> (*this); } virtual Function >::BaseType> *cloneNonAD() const { return new Gaussian3D >::BaseType> (*this); } private: T sq(T v) const; //# Make members of parent classes known. protected: using Gaussian3DParam >::param_p; using Gaussian3DParam >::stoT_p; using Gaussian3DParam >::stoP_p; using Gaussian3DParam >::cosT_p; using Gaussian3DParam >::cosP_p; using Gaussian3DParam >::sinT_p; using Gaussian3DParam >::sinP_p; using Gaussian3DParam >::cosTcosP_p; using Gaussian3DParam >::cosTsinP_p; using Gaussian3DParam >::sinTcosP_p; using Gaussian3DParam >::sinTsinP_p; public: using Gaussian3DParam >::H; using Gaussian3DParam >::CX; using Gaussian3DParam >::CY; using Gaussian3DParam >::CZ; using Gaussian3DParam >::AX; using Gaussian3DParam >::AY; using Gaussian3DParam >::AZ; using Gaussian3DParam >::THETA; using Gaussian3DParam >::PHI; using Gaussian3DParam >::fwhm2int; using Gaussian3DParam >::settrigvals; }; #undef Gaussian3D_PS } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/Gaussian3D.tcc000066400000000000000000000064771476623553700217130ustar00rootroot00000000000000//# Gaussian3D.cc: A three-dimensional Gaussian class //# Copyright (C) 1994,1995,1996,1999,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_GAUSSIAN3D_TCC #define SCIMATH_GAUSSIAN3D_TCC #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template Gaussian3D::Gaussian3D() : Gaussian3DParam() {} template Gaussian3D::Gaussian3D(T &height, T &xCenter, T &yCenter, T &zCenter, T &xWidth, T &yWidth, T &zWidth, T &theta, T &phi) : Gaussian3DParam(height, xCenter, yCenter, zCenter, xWidth, yWidth, zWidth, theta, phi) {} template Gaussian3D::Gaussian3D(T height, const Vector& center, const Vector& width, T theta, T phi) : Gaussian3DParam(height, center, width, theta, phi) {} template Gaussian3D::~Gaussian3D() {} template Gaussian3D::Gaussian3D(const Gaussian3D& other) : Gaussian3DParam(other) {} template Gaussian3D& Gaussian3D::operator=(const Gaussian3D& other) { Gaussian3DParam::operator=(other); return *this; } template T Gaussian3D::eval(typename Function::FunctionArg x) const { T Nx = x[0] - param_p[CX]; T Ny = x[1] - param_p[CY]; T Nz = x[2] - param_p[CZ]; T Ax = param_p[AX]*fwhm2int; T Ay = param_p[AY]*fwhm2int; T Az = param_p[AZ]*fwhm2int; T v; if (stoT_p != param_p[THETA] || stoP_p != param_p[PHI]) settrigvals(); v = param_p[H] * exp( - sq((cosTcosP_p*Nx + sinT_p*Ny - cosTsinP_p*Nz)/Ax) - sq((-sinTcosP_p*Nx + cosT_p*Ny +sinTsinP_p*Nz)/Ay) - sq((sinP_p*Nx + cosP_p*Nz)/Az)); return v; } template Function* Gaussian3D::clone() const { Function *tmp = new Gaussian3D(*this); return tmp; } template T Gaussian3D::sq(T v) const { return v*v; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/Gaussian3D2.tcc000066400000000000000000000217501476623553700217640ustar00rootroot00000000000000//# Gaussian3D2.cc: Three dimensional Gaussian class specialized for AutoDiff //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_GAUSSIAN3D2_TCC #define SCIMATH_GAUSSIAN3D2_TCC #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template Gaussian3D >::Gaussian3D() : Gaussian3DParam >() {} template Gaussian3D >::Gaussian3D(AutoDiff &height, AutoDiff &xCenter, AutoDiff &yCenter, AutoDiff &zCenter, AutoDiff &xWidth, AutoDiff &yWidth, AutoDiff &zWidth, AutoDiff &theta, AutoDiff &phi) : Gaussian3DParam >(height, xCenter, yCenter, zCenter, xWidth, yWidth, zWidth, theta, phi) {} template Gaussian3D >::Gaussian3D(const AutoDiff& height, const Vector >& center, const Vector >& width, const AutoDiff& theta, const AutoDiff& phi) : Gaussian3DParam >(height, center, width, theta, phi) {} template Gaussian3D >::~Gaussian3D() {} template Gaussian3D >::Gaussian3D(const Gaussian3D >& other) : Gaussian3DParam >(other) {} template Gaussian3D >& Gaussian3D >::operator=(const Gaussian3D >& other) { Gaussian3DParam >::operator=(other); return *this; } template AutoDiff Gaussian3D >::eval(typename Function >::FunctionArg x) const { uInt k; AutoDiff tmp; // if (this->stoT_p != this->param_p[Gaussian3DParam >::THETA] || this->stoP_p != this->param_p[Gaussian3DParam >::PHI]) { this->settrigvals(); } const T cosTV = this->cosT_p.value(); const T cosPV = this->cosP_p.value(); const T sinTV = this->sinT_p.value(); const T sinPV = this->sinP_p.value(); const T cosTcosPV = this->cosTcosP_p.value(); const T cosTsinPV = this->cosTsinP_p.value(); const T sinTcosPV = this->sinTcosP_p.value(); const T sinTsinPV = this->sinTsinP_p.value(); if (this->param_p[Gaussian3DParam >::H].nDerivatives() > 0) { tmp = this->param_p[Gaussian3DParam >::H]; } else if (this->param_p[Gaussian3DParam >::CX].nDerivatives() > 0) { tmp = this->param_p[Gaussian3DParam >::CX]; } else if (this->param_p[Gaussian3DParam >::CY].nDerivatives() > 0) { tmp = this->param_p[Gaussian3DParam >::CY]; } else if (this->param_p[Gaussian3DParam >::CZ].nDerivatives() > 0) { tmp = this->param_p[Gaussian3DParam >::CZ]; } else if (this->param_p[Gaussian3DParam >::AX].nDerivatives() > 0) { tmp = this->param_p[Gaussian3DParam >::AX]; } else if (this->param_p[Gaussian3DParam >::AY].nDerivatives() > 0) { tmp = this->param_p[Gaussian3DParam >::AY]; } else if (this->param_p[Gaussian3DParam >::AZ].nDerivatives() > 0) { tmp = this->param_p[Gaussian3DParam >::AZ]; } else if (this->param_p[Gaussian3DParam >::THETA].nDerivatives() > 0) { tmp = this->param_p[Gaussian3DParam >::THETA]; } else if (this->param_p[Gaussian3DParam >::PHI].nDerivatives() > 0) { tmp = this->param_p[Gaussian3DParam >::PHI]; } T value; /// const T Ax = this->param_p[Gaussian3DParam >::AX].value() * this->fwhm2int.value(); const T Ay = this->param_p[Gaussian3DParam >::AY].value() * this->fwhm2int.value(); const T Az = this->param_p[Gaussian3DParam >::AZ].value() * this->fwhm2int.value(); const T Nx = x[0] - this->param_p[Gaussian3DParam >::CX].value(); const T Ny = x[1] - this->param_p[Gaussian3DParam >::CY].value(); const T Nz = x[2] - this->param_p[Gaussian3DParam >::CZ].value(); const T Ax2 = Ax * Ax; const T Ay2 = Ay * Ay; const T Az2 = Az * Az; const T xrowterm = cosTcosPV*Nx + sinTV*Ny - cosTsinPV*Nz; const T yrowterm = -sinTcosPV*Nx + cosTV*Ny + sinTsinPV*Nz; const T zrowterm = sinPV*Nx + cosPV*Nz; const T xwidthterm = xrowterm/Ax; const T ywidthterm = yrowterm/Ay; const T zwidthterm = zrowterm/Az; const T xwidthterm2 = xwidthterm * xwidthterm; const T ywidthterm2 = ywidthterm * ywidthterm; const T zwidthterm2 = zwidthterm * zwidthterm; const T expterm = exp(-xwidthterm2 - ywidthterm2 - zwidthterm2); value = expterm * this->param_p[Gaussian3DParam >::H].value(); const T tvalue = value * 2.0; //function value tmp.value() = value; if (tmp.nDerivatives() > 0) { for (k = 0; k < tmp.nDerivatives(); k++) tmp.deriv(k) = 0.0; // derivative wrt height if (this->param_p.mask(Gaussian3DParam >::H)) tmp.deriv(Gaussian3DParam >::H) = expterm; // derivative wrt Cx (mean) if (this->param_p.mask(Gaussian3DParam >::CX)) tmp.deriv(Gaussian3DParam >::CX) = tvalue * ( cosTcosPV * xrowterm / Ax2 - sinTcosPV * yrowterm / Ay2 + sinPV * zrowterm / Az2); // derivative wrt Cy (mean) if (this->param_p.mask(Gaussian3DParam >::CY)) tmp.deriv(Gaussian3DParam >::CY) = tvalue * ( sinTV * xrowterm / Ax2 + cosTV * yrowterm / Ay2); // derivative wrt Cz (mean) if (this->param_p.mask(Gaussian3DParam >::CZ)) tmp.deriv(Gaussian3DParam >::CZ) = tvalue * (- cosTsinPV * xrowterm / Ax2 + sinTsinPV * yrowterm / Ay2 + cosPV * zrowterm / Az2); // derivative wrt Ax if (this->param_p.mask(Gaussian3DParam >::AX)) tmp.deriv(Gaussian3DParam >::AX) = tvalue * xwidthterm2/this->param_p[Gaussian3DParam >::AX].value(); // derivative wrt Ay if (this->param_p.mask(Gaussian3DParam >::AY)) tmp.deriv(Gaussian3DParam >::AY) = tvalue * ywidthterm2/this->param_p[Gaussian3DParam >::AY].value(); // derivative wrt Az if (this->param_p.mask(Gaussian3DParam >::AZ)) tmp.deriv(Gaussian3DParam >::AZ) = tvalue * zwidthterm2/this->param_p[Gaussian3DParam >::AZ].value(); // derivative wrt theta if (this->param_p.mask(Gaussian3DParam >::THETA)) tmp.deriv(Gaussian3DParam >::THETA) = tvalue * ( xrowterm * yrowterm / Ay2 - xrowterm * yrowterm / Ax2); // derivative wrt phi if (this->param_p.mask(Gaussian3DParam >::PHI)) tmp.deriv(Gaussian3DParam >::PHI) = -tvalue *(xrowterm * (-Nx*cosTsinPV - Nz*cosTcosPV)/ Ax2 + yrowterm * (Nx*sinTsinPV + Nz*sinTcosPV) / Ay2 + zrowterm * (Nx*cosPV - Nz*sinPV) / Az2); } return tmp; } template Function >* Gaussian3D >::clone() const { Function > *tmp = new Gaussian3D >(*this); return tmp; } template T Gaussian3D >::sq(T v) const { return v*v; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/Gaussian3DParam.h000066400000000000000000000232701476623553700223400ustar00rootroot00000000000000//# Gaussian3DParam.h: Parameter handling for 3 dimensional Gaussian class //# Copyright (C) 2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_GAUSSIAN3DPARAM_H #define SCIMATH_GAUSSIAN3DPARAM_H #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Parameter handling for 3 dimensional Gaussian class // // // // // //
      • FunctionParam class //
      • Function class // // // A 3-dimensional Gaussian's parameters. // // // A Gaussian3D is described by a height, center, width, // and two position angles. // The width of the Gaussian is now specified in terms of the full width // at half maximum (FWHM), as with the 1D and 2D Gaussian functional classes. // The three axis values refer to the x, y, and z axes, and unlike with the // 2D Gaussian any of the three axes may be the longest. Instead, the position // angles are restricted: The first position angle, theta, is the longitudinal // angle, referring to the rotation (counterclockwise) around the z-axis. The // second, phi, is the latidudinal angle, referring to the rotation around // the theta-rotated y axis. The domain of both angles is -pi/4 < A < pi/4. // (Note that the use of theta and phi corresponds to the mathematical // convention for these angles, not the physics convention.) // The parameter interface (see // FunctionParam class), // is used to provide an interface to the // Fitting classes. // // There are 9 parameters that are used to describe the Gaussian: //
          //
        1. The height of the Gaussian. This is identical to the value // returned using the height member function. //
        2. The center of the Gaussian in the x direction. This is identical to // the value returned using the xCenter member function. //
        3. The center of the Gaussian in the y direction. This is identical to // the value returned using the yCenter member function. //
        4. The center of the Gaussian in the z direction. This is identical to // the value returned using the zCenter member function. //
        5. The width of the Gaussian along the x-axis. //
        6. The width of the Gaussian along the y-axis. //
        7. The width of the Gaussian along the z-axis. //
        8. The longitudinal position angle, theta (in radians) //
        9. The latitudinal position angle, phi (also in radians). //
        // An enumeration for the H, CX, // CY,CZ, AX, AY, // AZ, THETA, PHI // parameter index is provided, enabling the setting // and reading of parameters with the [] operator. The // mask() methods can be used to check and set the parameter masks. // // This class is in general used implicitly by the Gaussian3D // class only. // // // Other points to bear in mind when fitting this class to measured data // are: //
          //
        • If you need to fit a circular Gaussian to data you should mask one or // both position angles. This avoids rank deficiency in the fitting // routines as the position angle is meaningless when the axes are // equal. //
        //
        // //
        // // // Gaussian3D g(9.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0); // Vector x(3); // x(0) = 1.0; x(1) = 0.5; x(2) = 0.0 // cout << "g(" << x(0) << "," << x(1) << "," << x(2) << ")=" << g(x) << endl; // // // //
      • T should have standard numerical operators and exp() function. Current // implementation only tested for real types (and AutoDiff of them). // // //
      • Assertion in debug mode if attempt is made to set a negative width //
      • AipsError if incorrect parameter number specified. //
      • others? // // //
      • Gaussians that know about their DFT's could be required eventually. // template class Gaussian3DParam : public Function { // Parameter handling for the functional for 3D Gaussian Class. // Similar to Gaussian2DParam, but width parameters are not adjusted // for FWHM; they are identical to the parameters used in the function. // Position angle parameters are restricted to -PI/4 < angle < PI/4. public: //#Enumerations enum { H=0, // value of Gaussian at the center CX, // X center value CY, // Y center value CZ, // Z center value AX, // width along X axis when T = P = 0 AY, // width along Y axis when T = P = 0 AZ, // width along Z axis when T = P = 0 THETA, // rotation about Z axis. PHI, // rotation around X and Y axes (which depends on T). NPAR // number of total parameters (9) }; // Constructs the three dimensional Gaussians. Defaults: // height = 1, center = {0,0,0}, width = {1,1,1}, theta = phi = 0 // Gaussian3DParam(); Gaussian3DParam(Type height, const Vector& center, const Vector& width, Type theta, Type phi); Gaussian3DParam(Type &height, Type &xCenter, Type &yCenter, Type &zCenter, Type &xWidth, Type &yWidth, Type &zWidth, Type &theta, Type &phi); // // Copy construcor // Gaussian3DParam(const Gaussian3DParam &other); template Gaussian3DParam(const Gaussian3DParam &other) : Function(other), fwhm2int(Type(1.0)/sqrt(log(Type(16.0)))) { settrigvals(); } // // Copy assignment Gaussian3DParam &operator=(const Gaussian3DParam &other); // Destructor virtual ~Gaussian3DParam(); //# Member functions // Give name of function virtual const String &name() const { static String x("gaussian3d"); return x; } // Return dimensionality virtual uInt ndim() const {return 3;} // Get or set the peak height of the Gaussian // Type height() const; void setHeight(const Type & height); // // Get or set the total flux of the Gaussian. (Note: Since this changes // the height of the Gaussian but not its width, always set the width // before setting the flux.) // Type flux() const; void setFlux(const Type & flux); // // Get or cet the center coordinates of the Gaussian // Vector center() const; void setCenter(const Vector& center); Type xCenter() const; void setXcenter(const Type & xcenter); Type yCenter() const; void setYcenter(const Type & ycenter); Type zCenter() const; void setZcenter(const Type & zcenter); // // Get or set the sigma-width of the Gaussian // Vector width() const; void setWidth(const Vector& width); void setXwidth(const Type & xwidth); Type xWidth() const; void setYwidth(const Type & ywidth); Type yWidth() const; void setZwidth(const Type & zwidth); Type zWidth() const; // // Get or set the rotation angles of the Gaussian. // Theta=logitude, phi=latitude // Type theta() const; void settheta(const Type & sT); Type phi() const; void setphi(const Type & sP); // protected: void settrigvals() const; Type fwhm2int; // const to scale halfwidth at 1/e to FWHM mutable Type stoT_p; // used to check if cached values below are updated mutable Type stoP_p; // mutable Type cosT_p,sinT_p; // cached values of the cos and sine of THETA mutable Type cosP_p,sinP_p; // PHI mutable Type cosTcosP_p; //cached values of products of cos/sine of angles mutable Type cosTsinP_p; mutable Type sinTcosP_p; mutable Type sinTsinP_p; //# Make members of parent classes known. protected: using Function::param_p; public: using Function::nparameters; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/Gaussian3DParam.tcc000066400000000000000000000202721476623553700226610ustar00rootroot00000000000000//# Gaussian3DParam.cc: A three-dimensional Gaussian class //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_GAUSSIAN3DPARAM_TCC #define SCIMATH_GAUSSIAN3DPARAM_TCC #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template Gaussian3DParam::Gaussian3DParam() : Function(NPAR) { param_p[H] = Type(1.0); param_p[CX] = Type(0.0); param_p[CY] = Type(0.0); param_p[CZ] = Type(0.0); param_p[AX] = Type(1.0); param_p[AY] = Type(1.0); param_p[AZ] = Type(1.0); param_p[THETA] = Type(0.0); param_p[PHI] = Type(0.0); fwhm2int = Type(1.0)/sqrt(log(Type(16.0))); settrigvals(); } template Gaussian3DParam::Gaussian3DParam(Type &height, Type &xCenter, Type &yCenter, Type &zCenter, Type &xWidth, Type &yWidth, Type &zWidth, Type &theta, Type &phi) : Function(NPAR) { param_p[H] = height; param_p[CX] = xCenter; param_p[CY] = yCenter; param_p[CZ] = zCenter; param_p[AX] = xWidth; param_p[AY] = yWidth; param_p[AZ] = zWidth; param_p[THETA] = theta; param_p[PHI] = phi; fwhm2int = Type(1.0)/sqrt(log(Type(16.0))); settrigvals(); } template Gaussian3DParam::Gaussian3DParam(Type /*height*/, const Vector& center, const Vector& width, Type T, Type P) : Function(NPAR) { fwhm2int = Type(1.0)/sqrt(log(Type(16.0))); setCenter(center); setWidth(width); settheta(T); setphi(P); settrigvals(); } template Gaussian3DParam::Gaussian3DParam(const Gaussian3DParam& other) : Function(other) { fwhm2int = Type(1.0)/sqrt(log(Type(16.0))); settrigvals(); //IMPR: could set vals explicitly to speed things up } template Gaussian3DParam::~Gaussian3DParam() {} template Gaussian3DParam& Gaussian3DParam::operator=(const Gaussian3DParam& other) { if (this != &other) { Function::operator=(other); settrigvals(); //IMPR: explicit fwhm2int = other.fwhm2int; } return *this; } template Type Gaussian3DParam::height() const { return param_p[H]; } template void Gaussian3DParam::setHeight(const Type& height) { param_p[H] = height; } template Type Gaussian3DParam::flux() const { return param_p[H]*param_p[AX]*param_p[AY]*param_p[AZ]* fwhm2int*fwhm2int*fwhm2int*Type(M_PI*sqrt(M_PI)); } template void Gaussian3DParam::setFlux(const Type& flux) { param_p[H]= flux / (param_p[AX]*param_p[AY]*param_p[AZ]* fwhm2int*fwhm2int*fwhm2int*Type(M_PI*sqrt(M_PI))); } template Vector Gaussian3DParam::center() const { Vector center(3); center(0) = param_p[CX]; center(1) = param_p[CY]; center(2) = param_p[CZ]; return center; } template void Gaussian3DParam::setCenter(const Vector& center) { if (center.nelements() != 3) throw(AipsError("Gaussian3D::setCenter(const Vector& center)" " - center must be of length 3")); param_p[CX] = center(0); param_p[CY] = center(1); param_p[CZ] = center(2); } template Type Gaussian3DParam::xCenter() const { return param_p[CX]; } template Type Gaussian3DParam::yCenter() const { return param_p[CY]; } template Type Gaussian3DParam::zCenter() const { return param_p[CZ]; } template void Gaussian3DParam::setXcenter(const Type& xcenter) { param_p[CX] = xcenter; } template void Gaussian3DParam::setYcenter(const Type& ycenter) { param_p[CY] = ycenter; } template void Gaussian3DParam::setZcenter(const Type& zcenter) { param_p[CZ] = zcenter; } template Vector Gaussian3DParam::width() const { Vector width(3); width(0) = param_p[AX]; width(1) = param_p[AY]; width(2) = param_p[AZ]; return width; } template void Gaussian3DParam::setWidth(const Vector& width) { if (width.nelements() != 3) throw(AipsError("Gaussian3DParam::setWidth" "(const Vector& width)" " - width must be of length 3")); param_p[AX] = width(0); param_p[AY] = width(1); param_p[AZ] = width(2); } template void Gaussian3DParam::setXwidth(const Type & xwidth) { if (xwidth <= Type(0)) throw(AipsError("Gaussian3DParam::setXwidth(const Type& xwidth)" " - width must be positive")); param_p[AX] = xwidth; } template void Gaussian3DParam::setYwidth(const Type & ywidth) { if (ywidth <= Type(0)) throw(AipsError("Gaussian3DParam::setYwidth(const Type& ywidth)" " - width must be positive")); param_p[AY] = ywidth; } template void Gaussian3DParam::setZwidth(const Type & zwidth) { if (zwidth <= Type(0)) throw(AipsError("Gaussian3DParam::setZwidth(const Type& zwidth)" " - width must be positive")); param_p[AZ] = zwidth; } template Type Gaussian3DParam::xWidth() const { return param_p[AX]; } template Type Gaussian3DParam::yWidth() const { return param_p[AY]; } template Type Gaussian3DParam::zWidth() const { return param_p[AZ]; } template Type Gaussian3DParam::theta() const { //IMPR: force to be in stated range by using a correctParameters fn // (see FitGaussian) return param_p[THETA]; } template Type Gaussian3DParam::phi() const { //IMPR: force to be in stated range return param_p[PHI]; } template void Gaussian3DParam::settheta(const Type& theta) { if (abs(theta) > Type(M_PI_4)) throw(AipsError("Gaussian3DParam::settheta(const Type& theta)" " - theta must be in radians and between -pi/4 and pi/4")); param_p[THETA] = theta; settrigvals(); } template void Gaussian3DParam::setphi(const Type& phi) { if (abs(phi) > Type(M_PI_4)) throw(AipsError("Gaussian3D::setphi(const Type& phi)" " - phi must be in radians and between -pi/4 and pi/4")); param_p[PHI] = phi; settrigvals(); } template void Gaussian3DParam::settrigvals() const { stoT_p = param_p[THETA]; stoP_p = param_p[PHI]; sinT_p = sin(param_p[THETA]); cosT_p = cos(param_p[THETA]); sinP_p = sin(param_p[PHI]); cosP_p = cos(param_p[PHI]); cosTcosP_p = cosT_p * cosP_p; cosTsinP_p = cosT_p * sinP_p; sinTcosP_p = sinT_p * cosP_p; sinTsinP_p = sinT_p * sinP_p; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/GaussianND.h000066400000000000000000000231011476623553700214030ustar00rootroot00000000000000//# GaussianND.h: A multidimensional Gaussian class //# Copyright (C) 1995,1996,1998,1999,2001,2002,2004,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_GAUSSIANND_H #define SCIMATH_GAUSSIANND_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // A Multi-dimensional Gaussian functional. // // // // //
      • GaussianNDParam //
      • Function // // // A GaussianND is used to calculate Gaussian functions of any // dimension. A Gaussian1D class exists // which is more appropriate for one dimensional Gaussian functions, and a // Gaussian2D class exists for two // dimensional functions. // // A statistical description of the multi-dimensional Gaussian is used (see // Kendall & Stuart "The Advanced Theory of Statistics"). A Gaussian is // defined in terms of its height, mean (which is the location of the peak // value), variance, (a measure of the width of the Gaussian), and // covariance which skews the distribution with respect to the Axes. // // In the general description the variance and covariance are specified // using a covariance matrix. This is defined as (for a 4 dimensional // Gaussian): // // V = | s1*s1 r12*s1*s2 r13*s1*s3 r14*s1*s4 | // | r12*s1*s2 s2*s2 r23*s2*s3 r24*s2*s4 | // | r13*s1*s3 r23*s2*s3 s3*s3 r34*s3*s4 | // | r14*s1*s4 r24*s2*s4 r34*s3*s4 s4*s4 | // // where s1 (sigma1) is the standard deviation of the Gaussian with // respect to the first axis, and r12 (rho12) is the correlation // between the the first and second axis. The correlation MUST be between -1 // and 1, and this class checks this as well as ensuring that the diagonal // is positive. // // It is possible to have symmetric matrices that are of // the above described form (ie. symmetric with -1 <= rho(ij) <=1) // that do // not generate a Gaussian function. This is because the Matrix is NOT // positive definite (The limits on rho(ij) are upper limits). // This class // does check that the covariance Matrix is positive definite and will throw // an exception (AipsError) if it is not. // // The covariance Matrix can be specified by only its upper or lower // triangular regions (ie. with zeros in the other triangle), otherwise it // MUST be symmetric. // // The Gaussian that is constructed from this covariance Matrix (V), along // with mean (u) and height (h) is: // // f(x) = h*exp( -1/2 * (x-u) * V^(-1) * (x-u)) // // where x, and u are vectors whose length is the dimensionality of the // Gaussian and V^(-1) is the inverse of the covariance Matrix defined // above. For a two dimensional Gaussian with zero mean this expression // reduces to: // // f(x) = h*exp(-1/(2*(1-r12^2))*(x1^2/s1^2 - 2*r12*x1*x2/(s1*s2) + x2^2/s2^2)) // // // The amplitude of the Gaussian can be defined in two ways, either using // the peak height (as is done in the constructors, and the setHeight // function) or using the setFlux function. The flux in this context is the // analytic integral of the Gaussian over all dimensions. Using the setFlux // function does not modify the shape of the Gaussian just its height. // // All the parameters of the Gaussian except its dimensionality can be // modified using the set/get functions. // // The parameter interface (see // FunctionParam class), // is used to provide an interface to the // Fitting classes. // There are always 4 // parameter sets. The parameters are, in order: //
          //
        1. height (1 term). No assumptions on what quantity the height // represents, and it can be negative //
        2. mean (ndim terms). //
        3. variance (ndim terms). The variance is always positive, and an // exception (AipsError) will be thrown if you try to set a negative // value. //
        4. covariance (ndim*(ndim-1)/2 terms) The order is (assuming ndim=5) // v12,v13,v14,v15,v23,v24,v25,v34,v35,v45. The restrictions described // above for the covariance (ie. -1 < r12 < +1) are enforced. //
        //
        // // Construct a two dimensional Gaussian with mean=(0,1), variance=(.1,7) and // height = 1; // // uInt ndim = 2; // Float height = 1; // Vector mean(ndim); mean(0) = 0, mean(1) = 1; // Vector variance(ndim); variance(0) = .1, variance(1) = 7; // GaussianND g(ndim, height, mean, variance); // Vector x(ndim); x = 0; // cout << "g("<< x <<") = " << g(x) < // // // A Gaussian Functional was needed for modeling the sky with a series of // components. It was later realised that it was too general and Gaussian2D // was written. // // //
      • T should have standard numerical operators and exp() function. Current // implementation only tested for real types. // // //
      • Nothing I know off, apart from possible optimization // template class GaussianND : public GaussianNDParam { public: //# Constructors // Makes a Gaussian using the indicated height, mean, variance & // covariance. // ndim defaults to 2, // mean defaults to 0, // height to Pi^(-ndim/2) (the flux is unity) // variance defaults to 1.0, // covariance defaults to 0.0, // GaussianND() : GaussianNDParam() {} explicit GaussianND(uInt ndim) : GaussianNDParam(ndim) {} GaussianND(uInt ndim, const T &height) : GaussianNDParam(ndim, height) {} GaussianND(uInt ndim, const T &height, const Vector &mean) : GaussianNDParam(ndim, height, mean) {} GaussianND(uInt ndim, const T &height, const Vector &mean, const Vector &variance) : GaussianNDParam(ndim, height, mean, variance) {} GaussianND(uInt ndim, const T &height, const Vector &mean, const Matrix &covar) : GaussianNDParam(ndim, height, mean, covar) {} // // Copy constructor (deep copy) // GaussianND(const GaussianND &other) : GaussianNDParam(other) {} // // Copy assignment (deep copy) GaussianND &operator=(const GaussianND &other) { GaussianNDParam::operator=(other); return *this; } // Destructor virtual ~GaussianND() {} //# Operators // Evaluate the Gaussian at x. // virtual T eval(typename Function::FunctionArg x) const; // //# Member functions // Return a copy of this object from the heap. The caller is responsible for // deleting this pointer. // virtual Function *clone() const { return new GaussianND(*this); } // //# Make members of parent classes known. protected: using GaussianNDParam::param_p; using GaussianNDParam::itsDim; public: using GaussianNDParam::HEIGHT; using GaussianNDParam::CENTER; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/GaussianND.tcc000066400000000000000000000042001476623553700217240ustar00rootroot00000000000000//# GaussianND.cc: GaussianND class //# Copyright (C) 1996,1998,1999,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_GAUSSIANND_TCC #define SCIMATH_GAUSSIANND_TCC #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template T GaussianND::eval(typename Function::FunctionArg x) const { Vector norm(itsDim); for (uInt i=0; i #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // A Multi-dimensional Gaussian parameter handling. // // // // //
      • FunctionParam class //
      • Function class // // // A GaussianND is used to calculate Gaussian functions of any // dimension. A Gaussian1D class exists // which is more appropriate for one dimensional Gaussian functions, and a // Gaussian2D class exists for two // dimensional functions. // // A statistical description of the multi-dimensional Gaussian is used (see // Kendall & Stuart "The Advanced Theory of Statistics"). A Gaussian is // defined in terms of its height, mean (which is the location of the peak // value), variance, (a measure of the width of the Gaussian), and // covariance which skews the distribution with respect to the Axes. // // In the general description the variance and covariance are specified // using a covariance matrix. This is defined as (for a 4 dimensional // Gaussian): // // V = | s1*s1 r12*s1*s2 r13*s1*s3 r14*s1*s4 | // | r12*s1*s2 s2*s2 r23*s2*s3 r24*s2*s4 | // | r13*s1*s3 r23*s2*s3 s3*s3 r34*s3*s4 | // | r14*s1*s4 r24*s2*s4 r34*s3*s4 s4*s4 | // // where s1 (sigma1) is the standard deviation of the Gaussian with // respect to the first axis, and r12 (rho12) is the correlation // between the the first and second axis. The correlation MUST be between -1 // and 1, and this class checks this as well as ensuring that the diagonal // is positive. // // It is possible to have symmetric matrices that are of // the above described form (ie. symmetric with -1 <= rho(ij) <=1) // that do // not generate a Gaussian function. This is because the Matrix is NOT // positive definite (The limits on rho(ij) are upper limits). // This class // does check that the covariance Matrix is positive definite and will throw // an exception (AipsError) if it is not. // // The covariance Matrix can be specified by only its upper or lower // triangular regions (ie. with zeros in the other triangle), otherwise it // MUST be symmetric. // // The Gaussian that is constructed from this covariance Matrix (V), along // with mean (u) and height (h) is: // // f(x) = h*exp( -1/2 * (x-u) * V^(-1) * (x-u)) // // where x, and u are vectors whose length is the dimensionality of the // Gaussian and V^(-1) is the inverse of the covariance Matrix defined // above. For a two dimensional Gaussian with zero mean this expression // reduces to: // // f(x) = h*exp(-1/(2*(1-r12^2))*(x1^2/s1^2 - 2*r12*x1*x2/(s1*s2) + x2^2/s2^2)) // // // The amplitude of the Gaussian can be defined in two ways, either using // the peak height (as is done in the constructors, and the setHeight // function) or using the setFlux function. The flux in this context is the // analytic integral of the Gaussian over all dimensions. Using the setFlux // function does not modify the shape of the Gaussian just its height. // // All the parameters of the Gaussian except its dimensionality can be // modified using the set/get functions. // // The parameter interface (see // FunctionParam class), // is used to provide an interface to the // Fitting classes. // There are always 4 parameter sets. // Note that the actual variance/covariance // parameters are the inverse matrix of the variance/covariance matrix given // by the user. // The actual parameters are in order: //
          //
        1. height (1 term). No assumptions on what quantity the height // represents, and it can be negative (enumerated by HEIGHT) //
        2. mean (ndim terms) (enumerated by CENTER). //
        3. variance (ndim terms). The variance is always positive, and an // exception (AipsError) will be thrown if you try to set a negative // value. //
        4. covariance (ndim*(ndim-1)/2 terms) The order is (assuming ndim=5) // v12,v13,v14,v15,v23,v24,v25,v34,v35,v45. The restrictions described // above for the covariance (ie. -1 < r12 < +1) are enforced. //
        //
        // // Construct a two dimensional Gaussian with mean=(0,1), variance=(.1,7) and // height = 1; // // uInt ndim = 2; // Float height = 1; // Vector mean(ndim); mean(0) = 0, mean(1) = 1; // Vector variance(ndim); variance(0) = .1, variance(1) = 7; // GaussianND g(ndim, height, mean, variance); // Vector x(ndim); x = 0; // cout << "g("<< x <<") = " << g(x) < // // // A Gaussian Functional was needed for modeling the sky with a series of // components. It was later realised that it was too general and Gaussian2D // was written. // // //
      • T should have standard numerical operators and exp() function. Current // implementation only tested for real types. // // //
      • Nothing I know off, apart from possible optimization // template class GaussianNDParam : public Function { public: //# Enumerations enum { HEIGHT=0, CENTER}; //# Constructors // Constructs a Gaussian using the indicated height, mean, variance & // covariance. // ndim defaults to 2, // mean defaults to 0, // height to Pi^(-ndim/2) (the flux is unity) // variance defaults to 1.0, // covariance defaults to 0.0, // GaussianNDParam(); explicit GaussianNDParam(uInt ndim); GaussianNDParam(uInt ndim, const T &height); GaussianNDParam(uInt ndim, const T &height, const Vector &mean); GaussianNDParam(uInt ndim, const T &height, const Vector &mean, const Vector &variance); GaussianNDParam(uInt ndim, const T &height, const Vector &mean, const Matrix &covar); // // Copy constructor (deep copy) // GaussianNDParam(const GaussianNDParam &other); template GaussianNDParam(const GaussianNDParam &other) : Function(other), itsDim(other.itsDim), itsFlux2Hgt(other.itsFlux2Hgt) {} // // Copy assignment (deep copy) GaussianNDParam &operator=(const GaussianNDParam &other); // Destructor virtual ~GaussianNDParam(); //# Operators //# Member functions // Give name of function virtual const String &name() const { static String x("gaussiannd"); return x; } // Variable dimensionality virtual uInt ndim() const { return itsDim; } // Get or set the peak height of the Gaussian // T height() const { return param_p[HEIGHT]; } void setHeight(const T &height) { param_p[HEIGHT] = height; } // // The analytical integrated area underneath the Gaussian. Use these // functions as an alternative to the height functions. // T flux() const; void setFlux(const T &flux); // // The center ordinate of the Gaussian // Vector mean() const; void setMean(const Vector &mean); // // The FWHM of the Gaussian is sqrt(8*variance*log(2)). // The variance MUST be positive // Vector variance() const; void setVariance(const Vector &variance); // //The covariance Matrix defines the correlations between all the axes. // Matrix covariance() const; void setCovariance(const Matrix &covar); // protected: //# Data // dimensionality uInt itsDim; // factor to convert from flux to height T itsFlux2Hgt; //# Methods // Functions to convert between internal Vector of parameters // and the Covariance // Matrix // void repack(Matrix &covar) const; void unpack(const Matrix &covar); // //# Make members of parent classes known. protected: using Function::param_p; public: using Function::nparameters; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/GaussianNDParam.tcc000066400000000000000000000220141476623553700227100ustar00rootroot00000000000000//# GaussianNDParam.cc: Multidimensional Gaussian class parameters //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_GAUSSIANNDPARAM_TCC #define SCIMATH_GAUSSIANNDPARAM_TCC #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template GaussianNDParam::GaussianNDParam() : Function(6), itsDim(2), itsFlux2Hgt(pow(T(2.0*M_PI),T(-1))) { setFlux(T(1)); for (uInt i=0; i GaussianNDParam::GaussianNDParam(uInt nDim) : Function((nDim+3)*nDim/2+1), itsDim(nDim), itsFlux2Hgt(pow(T(2.0*M_PI),-T(nDim)/T(2))) { setFlux(T(1)); for (uInt i=0; i GaussianNDParam::GaussianNDParam(uInt nDim, const T &height) : Function((nDim+3)*nDim/2+1), itsDim(nDim), itsFlux2Hgt(pow(T(2.0*M_PI),-T(nDim)/T(2))) { param_p[HEIGHT] = height; for (uInt i=0; i GaussianNDParam::GaussianNDParam(uInt nDim, const T &height, const Vector &mean) : Function((nDim+3)*nDim/2+1), itsDim(nDim), itsFlux2Hgt(pow(T(2.0*M_PI),-T(nDim)/T(2))) { param_p[HEIGHT] = height; if (mean.nelements() != itsDim) { throw(AipsError("GaussianNDParam::GaussianNDParam(uInt nDim, " "T height, " "Vector mean) - mean must have nDim values.")); } for (uInt i=0; i GaussianNDParam::GaussianNDParam(uInt nDim, const T &height, const Vector &mean, const Vector &variance) : Function((nDim+3)*nDim/2+1), itsDim(nDim) { param_p[HEIGHT] = height; if (mean.nelements() != itsDim) { throw(AipsError("GaussianNDParam::GaussianNDParam(uInt nDim, " "T height, Vector mean, Vector variance)" " - mean must have nDim values.")); } if (variance.nelements() != itsDim) { throw(AipsError("GaussianNDParam::GaussianNDParam(uInt nDim, " "T height, Vector mean, Vector variance)" " - variance must have nDim values.")); } for (uInt i=0; i::GaussianNDParam(uInt nDim," " T height, Vector mean, Vector variance) " " - variance must be positive")); } param_p[CENTER+itsDim+i] = T(1)/variance[i]; } T det = param_p[CENTER+itsDim]; for (uInt i=1; i GaussianNDParam::GaussianNDParam(uInt nDim, const T &height, const Vector &mean, const Matrix &covar) : Function((nDim+3)*nDim/2+1), itsDim(nDim) { param_p[HEIGHT] = height; if (mean.nelements() != itsDim) { throw(AipsError("GaussianNDParam::GaussianNDParam(uInt nDim, " "T height, " "Vector mean, Matrix covar)" " - mean must have nDim values.")); } for (uInt i=0; i GaussianNDParam::~GaussianNDParam() {} template GaussianNDParam::GaussianNDParam(const GaussianNDParam &other) : Function(other), itsDim(other.itsDim), itsFlux2Hgt(other.itsFlux2Hgt) {} template GaussianNDParam &GaussianNDParam:: operator=(const GaussianNDParam &other) { if (this != &other) { Function::operator=(other); itsDim = other.itsDim; itsFlux2Hgt = other.itsFlux2Hgt; } return *this; } template T GaussianNDParam::flux() const { return param_p[HEIGHT] / itsFlux2Hgt; } template void GaussianNDParam::setFlux(const T &flux) { param_p[HEIGHT] = flux * itsFlux2Hgt; } template Vector GaussianNDParam::mean() const { Vector m(itsDim); for (uInt i=0; i void GaussianNDParam::setMean(const Vector &mean) { if (mean.nelements() != itsDim) { throw(AipsError("GaussianNDParam::setMean(const Vector &mean)" " - mean must have nDim values.")); } for (uInt i=0; i Vector GaussianNDParam::variance() const { Vector variance(itsDim); Matrix locCovariance(covariance()); for (uInt i=0; i void GaussianNDParam::setVariance(const Vector &variance) { if (variance.nelements() != itsDim) { throw(AipsError("GaussianNDParam::setVariance(const Vector " "&variance)" " - variance must have nDim values.")); } // This Matrix should be symmetric positive definite. invertSymPosDef // throws an exception if it is not. Matrix locCovariance(itsDim, itsDim); repack(locCovariance); for (uInt i=0; i Matrix GaussianNDParam::covariance() const { Matrix locCovariance(itsDim, itsDim); repack(locCovariance); return invertSymPosDef(locCovariance); } template void GaussianNDParam::setCovariance(const Matrix &covar) { Matrix locCovariance(covar.shape()); locCovariance = covar; if (locCovariance.shape() != IPosition(2,itsDim,itsDim)) { throw(AipsError("GaussianNDParam::setCovariance(" "const Matrix &covar)" " - covariance must have nDim rows and columns")); } Vector sigma(itsDim); for (uInt i=0; i T(0)) sigma[i] = sqrt(locCovariance(i,i)); else throw(AipsError("GaussianNDParam::setCovariance" "(const Matrix &covar)" " - variance must be positive")); } for (uInt i=0; i::setCovariance(" "const Matrix &covar)" " - covariance Matrix is not symmetric" " or triangular")); } // Now check that each covariance is in a possible range. (-1 < rho < 1) if (abs(locCovariance(i,j)) > sigma[i]*sigma[j]) { throw(AipsError("GaussianNDParam::setCovariance(" "const Matrix &covar)" " - a covariance entry is too big")); } } } // This Matrix should be symmetric positive definite. invertSymPosDef // throws an exception if it is not. T det; invertSymPosDef(locCovariance, det, locCovariance); unpack(locCovariance); itsFlux2Hgt = pow(T(2.0*M_PI),-T(itsDim)/T(2)) / sqrt(abs(det)); } template void GaussianNDParam::unpack(const Matrix &covar) { for (uInt row=0, k=0; row void GaussianNDParam::repack(Matrix &covar) const { for (uInt row=0, k=0; row #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // A hyper plane function. // // // // // // // //
      • Function // // // // This class forms a function of the form // f(x0,x1,..,xm-1) = // p0*x0 + p1*x1 + ... // + pm-1*xm-1, // where pi are coefficients (parameters) and xi // are independent arguments. // // f(x0,x1,..,xm-1) represents a hyper plane // of dimension m. // // Since the HyperPlane is a Function, the derivatives // can be obtained as well. // // The parameter interface (see // FunctionParam class), // is used to provide an interface to the // Fitting classes. // // This class is in general used implicitly by the HyperPlane // class only. // // // // // // form the hyper plane function of this form: // // 6*x0 + 2*x3 // HyperPlane hyper(4); // 4-dim hyperplane // hyper.parameters()[0] = 6; // hyper.parameters()[3] = 2; // // Evaluate at x0=5, x3=7 // Vector x(4); // x=0; x[0]=5; x[3]=7; // cout << "Hypervalue: " << hyper(x) << endl; // Hypervalue: 44 // // // //
      • T should have standard numerical operators. Current // implementation only tested for real types (and their AutoDiffs). // // //
      • Assertion in debug mode if attempt is made to address incorrect // coefficients // // // This class was created to allow the creation of linear constraint functions // for the use of linear least-squares fit. // // // // // // //
      • FunctionParam class //
      • Function class // // // // This class forms a function of the form // f(x0,x1,..,xm-1) = // p0*x0 + p1*x1 + ... // + pm-1*xm-1, // where pi are coefficients (parameters) and xi // are independent arguments. // // f(x0,x1,..,xm-1) represents a hyper plane // of dimension m. // // Since the HyperPlane is a Function, the derivatives // can be obtained as well. // // The parameter interface (see // FunctionParam class), // is used to provide an interface to the // Fitting classes. // // This class is in general used implicitly by the HyperPlane // class only. // // // // // // form the hyper plane function of this form: // // 6*x0 + 2*x3 // HyperPlane hyper(4); // 4-dim hyperplane // hyper.parameters()[0] = 6; // hyper.parameters()[3] = 2; // // Evaluate at x0=5, x3=7 // Vector x(4); // x=0; x[0]=5; x[3]=7; // cout << "Hypervalue: " << hyper(x) << endl; // Hypervalue: 44 // // // //
      • T should have standard numerical operators and exp() function. Current // implementation only tested for real types (and their AutoDiffs). // // //
      • Assertion in debug mode if attempt is made to set a negative width //
      • AipsError if incorrect parameter number specified. // // // This class was created to facilitate linear constraint functions // for the use of least-squares fits. // // //
      • Nothing I know of // template class HyperPlaneParam : public Function { public: //# Constructors // Construct an m-dimensional hyper plane which has m parameters. By // default, the coefficients are initialized to zero. The default plane has // m=0 // explicit HyperPlaneParam(uInt m=0); // // Copy constructor (deep copy) // HyperPlaneParam(const HyperPlaneParam &other); template HyperPlaneParam(const HyperPlaneParam &other) : Function(other) {} // // Copy assignment (deep copy) HyperPlaneParam &operator=(const HyperPlaneParam &other); // Destructor virtual ~HyperPlaneParam(); //# Operators // Comparisons. // HyperPlanes are equal if they are of the same order and have the same // parameters // Bool operator==(const HyperPlaneParam &other) const { return (this->param_p == other.param_p); } Bool operator!=(const HyperPlaneParam &other) const { return (this->param_p != other.param_p); } // //# Member functions // Give name of function virtual const String &name() const { static String x("hyperplane"); return x; } // What is the dimension of the parameter list virtual uInt ndim() const { return this->param_p.nelements(); } //# Make members of parent classes known. protected: using Function::param_p; public: using Function::nparameters; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/HyperPlaneParam.tcc000066400000000000000000000035721476623553700227730ustar00rootroot00000000000000//# HyperPlaneParam.cc: Parameters for a hyperplane function //# Copyright (C) 2001,2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_HYPERPLANEPARAM_TCC #define SCIMATH_HYPERPLANEPARAM_TCC //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template HyperPlaneParam::HyperPlaneParam(uInt m) : Function(m) {} template HyperPlaneParam::HyperPlaneParam(const HyperPlaneParam &other) : Function(other) {} template HyperPlaneParam::~HyperPlaneParam() {} template HyperPlaneParam & HyperPlaneParam::operator=(const HyperPlaneParam &other) { if (this != &other) Function::operator=(other); return *this; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/Interpolate1D.h000066400000000000000000000206041476623553700220670ustar00rootroot00000000000000//# Interpolate1D.h: Interpolate in one dimension //# Copyright (C) 1996,1997,1999,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_INTERPOLATE1D_H #define SCIMATH_INTERPOLATE1D_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template class SampledFunctional; // Interpolate in one dimension // // // // //
      • SampledFunctional //
      • Function1D // // // The Interpolate1D class does interpolation in one dimension only. // // // This class will, given the abscissa and ordinates of a set of one // dimensional data, interpolate on this data set giving the value at any // specified ordinate. It will extrapolate if necessary, but this is will // usually give a poor result. There is no requirement for the ordinates to // be regularly spaced, or even sorted in any numerical order. However each // abscissa should have a unique value. // // Interpolation can be done using the following methods: //
          //
        • Nearest Neighbour (default if there is one data point) //
        • Linear (default unless there is only one data point) //
        • Cubic Polynomial //
        • Natural Cubic Spline //
        // // The restriction that each abcissus has a unique value can be lifted // by setting the uniq=True option in the appropriate // functions. This imposes the following additional restrictions on // interpolation. //
          //
        • You cannot use cubic spline interpolation. //
        • You cannot cannot interpolate within two data points of a repeated // x-value when using cubic interpolation. //
        • You cannot interpolate within one data point of a repeated // x-value when using linear or nearest neighbour interpolation. //
        // // The abscissa must be a SampledFunctional that returns a scalar value that // can be ordered. ie. an uInt, Int, Float or Double (not Complex). The // ordinate can be any data type that has addition, and subtraction defined // as well as multiplication by a scalar. So the ordinate can be complex // numbers, where the interpolation is done separately on the real and // imaginary components, or an array, where the interpolation is done // separately on an element by element basis. // // This class will curently make an internal copy of the data supplied to // it, and sort the data if it is not told it is already sorted, by using // the sorted=True flag. //
        // // This code fragment sets the interpolation method to cubic before // interpolating on the supplied (x,y) vectors. // // Vector x(4); indgen(x); // Vector y(4); indgen(y); y = y*y*y; // ScalarSampledFunctional fx(x) // ScalarSampledFunctional fy(y); // Interpolate1D gain(fx, fy); // gain.setMethod(Interpolate1D::cubic); // for (Float xs = -1; xs < 5; xs += 0.1) // cout << "gain(" << xs << "):" << gain(xs) << endl; // // // // This class is motivated by the need to interpolate over the gain // solutions obtained from calibrator observations, in order to get the gain // at arbitrary times. // // //
      • The Domain class must be a type that can be ordered in a mathematical // sense. This includes uInt, Int, Float, Double, but not Complex. // // //
      • The Range class must have addition and subtraction of Range objects with // each other as well as multiplication by a scalar defined. Besides the // scalar types listed above this includes Complex, DComplex, and Arrays of // any of these types. // // //
      • AipsError // // //
      • avoid an internal copy of the data and have an index array as the // only private data (plus the interpolation method and pointers to // the actual data). //
      • Review the use of copy semantics in the copy constructor & // assignment operator after making the above change. // template class Interpolate1D : public Function1D { public: // The different interpolation methods are enumerated here enum Method { // Crude but sometimes useful nearestNeighbour, // The most common method and the Default linear, // Fits a third order polynomial to 4 pts cubic, // Natural Cubic Splines spline }; // The default constructor generates a useless object until the setData // function has been called. Interpolate1D(); // Construct an object with the specified data Interpolate1D(const SampledFunctional &x, const SampledFunctional &y, const Bool sorted=False, const Bool uniq=False); // Define a new data set for the class to operate on. Equivalent in many // aspects to creating a new object. void setData(const SampledFunctional &x, const SampledFunctional &y, const Bool sorted=False, const Bool uniq=False); // The standard copy constructor, assignment operator and // destructor. Internal data is copied in both cases (copy semantics) // Interpolate1D(const Interpolate1D& other); Interpolate1D & operator=(const Interpolate1D & other); ~Interpolate1D(); // // Name of function virtual const String &name() const { static String x("interpolate1d"); return x; } // Interpolation is done using the () operator (see example above). Actual // use is through the virtual eval() function. virtual Range eval(typename Function1D::FunctionArg x) const; // inquire/set the current interpolation method. uInts are used as // arguments instead of the Interpolate1D::Method enumerator due to // compiler limitations. See the example above (or the demo code) for the // recommended way to call these functions. // uInt getMethod() const {return curMethod;} void setMethod(uInt method); // // Access the data set that interpolation is done over. This will usually be // sorted. // Vector getX() const; Vector getY() const; // // A function to copy the Interpolate1D object // virtual Function *clone() const; // private: // A private function for doing polynomial interpolation Range polynomialInterpolation(const Domain x, uInt n, uInt offset) const; uInt curMethod; // interpolation method to use uInt nElements; // how many elements in the data set Block xValues; // the abscissa of the data set (sorted) Block yValues; // The corresponding ordinate of the data set Block y2Values; // The numerical second derivates (only for splines) }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/Interpolate1D.tcc000066400000000000000000000267421476623553700224220ustar00rootroot00000000000000//# Interpolate1D.cc: implements Interpolation in one dimension //# Copyright (C) 1996,1997,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_INTERPOLATE1D_TCC #define SCIMATH_INTERPOLATE1D_TCC #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template Interpolate1D:: Interpolate1D() { } template Interpolate1D:: Interpolate1D(const SampledFunctional &x, const SampledFunctional &y, const Bool sorted, const Bool uniq){ setData(x, y, sorted, uniq); } // Do all the real construction work here template void Interpolate1D:: setData(const SampledFunctional &x, const SampledFunctional &y, const Bool sorted, const Bool uniq){ nElements = x.nelements(); // Set the default interpolation method if (nElements == 0){ throw(AipsError("Interpolate1D::setData" " abcissa is of zero length")); } else if (nElements == 1) curMethod = nearestNeighbour; else curMethod = linear; // Now check that the ordinate has enough elements to correspond to all the // elements in the abcissa. if (nElements != y.nelements()) throw(AipsError("Interpolate1D::setData" " ordinate is a different length from the abcissa")); // Sort the x and y data if required. xValues.resize(nElements); yValues.resize(nElements); if (sorted == False) { Vector index; // I will copy the data to a block prior to sorting as the // genSort function cannot handle a SampledFunctional for (uInt j = 0; j < nElements; j++) xValues[j] = x(j); (void) genSort(index, xValues); Int idx; for (uInt i = 0; i < nElements; i++) { idx = index(i); xValues[i] = x(idx); yValues[i] = y(idx); } } else { for (uInt k = 0; k < nElements; k++) { xValues[k] = x(k); yValues[k] = y(k); } } // Check that each x_value is unique. If it isn't then throw an // exception. This check can be turned off (by setting uniq=True), but the // user will then have to interpolate under the following restrictions: // 1/ spline interpolation cannot be used // 2/ linear and nearestNeighbour interpolation cannot be used when when the // specified x value is within one data point of a repeated x value. // 3/ cubic interpolation cannot be used when when the specified x value is // within two data points of a repeated x value. if (uniq == False) for (uInt i=0; i < nElements-1; i++) { if (nearAbs(xValues[i], xValues[i+1])) { throw(AipsError("Interpolate1D::setData" " data has repeated x values")); } } // I will not initialise the y2Values as they are not used unless the // interpolation method is changed to spline. The y2Values are hence // initialised by method. } template Interpolate1D:: Interpolate1D(const Interpolate1D & other): Function1D (other), curMethod(other.curMethod), nElements(other.nElements), xValues(other.xValues), yValues(other.yValues), y2Values(other.y2Values){ } template Interpolate1D & Interpolate1D:: operator=(const Interpolate1D & other){ if (this != &other){ curMethod = other.curMethod; nElements = other.nElements; xValues = other.xValues; yValues = other.yValues; y2Values = other.y2Values; } return *this; } template Interpolate1D:: ~Interpolate1D(){} template Function *Interpolate1D::clone() const { return new Interpolate1D(*this); } template Range Interpolate1D:: polynomialInterpolation(const Domain x_req, uInt n, uInt offset) const { // A private function for doing polynomial interpolation // Based on Nevilles Algorithm (Numerical Recipies 2nd ed., Section 3.1) // x is the point we want to estimate, n is the number of points to use // in the interpolation, and offset controls which n points are used // (normally the nearest points) // copy the x, y data into the working arrays Block c(n), d(n); Block x(n); uInt i; for (i = 0; i < n; i++){ d[i] = c[i] = yValues[offset]; x[i] = xValues[offset]; offset++; } // Now do the interpolation using the rather opaque algorithm Range w, y; y = c[0]; const Float one = 1; for (i = 1; i < n; i++){ // Calculate new C's and D's for each interation for (uInt j = 0; j < n-i; j++){ if (nearAbs(x[j+i], x[j])) throw(AipsError("Interpolate1D::polynomailInterpolation" " data has repeated x values")); w = (c[j+1] - d[j]) * (one / (x[j] - x[j+i])); c[j] = (x[j] - x_req) * w; d[j] = (x[j+i] - x_req) * w; } y += c[0]; } return y; } template void Interpolate1D:: setMethod(uInt newMethod) { // Are we are switching to spline interpolation from something else? if (newMethod == spline && curMethod != spline){ // Calculate the y2Values y2Values.resize(nElements); // The y2Values are initialised here. I need to calculate the second // derivates of the interpolating curve at each x_value. As described // in Numerical Recipies 2nd Ed. Sec. 3.3, this is done by requiring // that the first derivative is continuous at each data point. This // leads to a set of equations that has a tridiagonal form that can be // solved using an order(N) algorithm. // // The first part of this solution is to do the Gaussian elimination so // that all the coefficients on the diagonal are one, and zero below the // diagonal. Because the system is tridiagonal the only non-zero // coefficients are in the diagonal immediately above the main // one. These values are stored in y2Values temporarily. The temporary // storage t, is used to hold the right hand side. Block t(nElements); Domain c; t[0] = 0; y2Values[0] = t[0] * yValues[0]; // This obscure initialisation is to // ensure that if y2Values is a block // of arrays, it gets initialised to the // right size. y2Values[nElements-1] = y2Values[0]; c = xValues[1] - xValues[0]; if (nearAbs(xValues[1], xValues[0])) throw(AipsError("Interpolate1D::setMethod" " data has repeated x values")); Domain a, b, delta; const Domain six = 6; const Float one = 1; Range r; uInt i; for (i = 1; i < nElements-1; i++){ a = c; b = 2*(xValues[i+1] - xValues[i-1]); if (nearAbs(xValues[i+1], xValues[i])) throw(AipsError("Interpolate1D::setMethod" " data has repeated x values")); c = (xValues[i+1] - xValues[i]); r = (one/c) * (yValues[i+1] - yValues[i]) - (one/a) * (yValues[i] - yValues[i-1]); delta = a * t[i-1]; if (nearAbs(b, delta)) throw(AipsError("Interpolate1D::setMethod" " trouble constructing second derivatives")); delta = b - delta; t[i] = c/delta; y2Values[i] = (one/delta)*(six*r - a*y2Values[i-1]); } // The second part of the solution is to do the back-substitution to // iteratively obtain the second derivatives. for (i = nElements-2; i > 1; i--){ y2Values[i] = y2Values[i] - t[i]*y2Values[i+1]; } } else if (curMethod == spline && newMethod != spline){ // Delete the y2Values y2Values.resize(uInt(0)); } curMethod = newMethod; } template Vector Interpolate1D:: getX() const{ Vector x(xValues.begin(), xValues.begin()+nElements); return x; } template Vector Interpolate1D:: getY() const { Vector y(yValues.begin(), yValues.begin()+nElements); return y; } template Range Interpolate1D:: eval(typename Function1D::FunctionArg x) const { Bool found; uInt where = binarySearchBrackets(found, xValues, x[0], nElements); Domain x1,x2; Range y1,y2; switch (curMethod) { case nearestNeighbour: // This does nearest neighbour interpolation if (where == nElements) return yValues[nElements-1]; else if (where == 0) return yValues[0]; else if (xValues[where] - x[0] < .5) return yValues[where]; else return yValues[where-1]; case linear: // Linear interpolation is the default if (where == nElements) where--; else if (where == 0) where++; x2 = xValues[where]; y2 = yValues[where]; where--; x1 = xValues[where]; y1 = yValues[where]; if (nearAbs(x1, x2)) throw(AipsError("Interpolate1D::operator()" " data has repeated x values")); return y1 + ((x[0]-x1)/(x2-x1)) * (y2-y1); case cubic:// fit a cubic polynomial to the four nearest points // It is relatively simple to change this to any order polynomial if (where > 1 && where < nElements - 1) where = where - 2; else if (where <= 1) where = 0; else where = nElements - 4; return polynomialInterpolation(x[0], (uInt) 4, where); case spline: // natural cubic splines { if (where == nElements) where--; else if (where == 0) where++; Domain dx, h, a, b; Range y1d, y2d; x2 = xValues[where]; y2 = yValues[where]; y2d = y2Values[where]; where--; x1 = xValues[where]; y1 = yValues[where]; y1d = y2Values[where]; if (nearAbs(x1, x2)) throw(AipsError("Interpolate1D::operator()" " data has repeated x values")); dx = x2-x1; a = (x2-x[0])/dx; b = 1-a; h = static_cast(dx*dx/6.); return a*y1 + b*y2 + h*(a*a*a-a)*y1d + h*(b*b*b-b)*y2d; } default: throw AipsError("Interpolate1D::operator() - unknown type"); } return y1; // to make compiler happy } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/KaiserBFunction.h000066400000000000000000000107331476623553700224440ustar00rootroot00000000000000//# KaiserBFunction.h: A one dimensional Kaiser-Bessel function //# Copyright (C) 2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_KAISERBFUNCTION_H #define SCIMATH_KAISERBFUNCTION_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // A one dimensional Kaiser-Bessel function // // // // // //
      • KaiserBParam //
      • Function // // // A Kaiser-Bessel function // // // A Kaiser-Bessel is described by a height, a center, a width // (halfwidth) and a parameter. // The parameters are enumerated by HEIGHT, CENTER, WIDTH, KBPAR. They have // default values of (1, 0, 1, 2.5). // // // // // KaiserBFunction sf; // sf(0); // = 1.0 // // // //
      • T should have standard numerical operators // // //
      • AipsError if incorrect parameter number specified. // // template class KaiserBFunction : public KaiserBParam { public: //# Constructors // Constructs the KaiserBFunction, Defaults: // height=1, center=0, width=1, kbpar=2.5. // Could not use default arguments // that worked both with gcc and IRIX // KaiserBFunction() : KaiserBParam() {} explicit KaiserBFunction(const T &kbpar) : KaiserBParam(kbpar) {} // // Copy constructor (deep copy) // KaiserBFunction(const KaiserBFunction &other) : KaiserBParam(other) {} template KaiserBFunction(const KaiserBFunction &other) : KaiserBParam(other) {} // // Copy assignment (deep copy) KaiserBFunction &operator=(const KaiserBFunction &other) { KaiserBParam::operator=(other); return *this; } // Destructor virtual ~KaiserBFunction() {} //# Operators // Evaluate the KaiserB at x. // If a vector is used as the argument only its first element is used. // virtual T eval(typename Function::FunctionArg x) const; // //# Member functions // Return a copy of this object from the heap. The caller is responsible // for deleting this pointer. // virtual Function *clone() const { return new KaiserBFunction(*this); } virtual Function::DiffType> *cloneAD() const { return new KaiserBFunction::DiffType>(*this); } virtual Function::BaseType> *cloneNonAD() const { return new KaiserBFunction::BaseType>(*this); } // //# Make members of parent classes known. protected: using KaiserBParam::param_p; public: using KaiserBParam::KBPAR; using KaiserBParam::CENTER; using KaiserBParam::WIDTH; using KaiserBParam::HEIGHT; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/KaiserBFunction.tcc000066400000000000000000000040721476623553700227650ustar00rootroot00000000000000//# KaiserBFunction.cc: A one dimensional Kaiser-Bessel function //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_KAISERBFUNCTION_TCC #define SCIMATH_KAISERBFUNCTION_TCC #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Operators template T KaiserBFunction::eval(typename Function::FunctionArg x) const { T par2 = param_p[KBPAR] * param_p[KBPAR]; T x1 = T(M_PI) * param_p[KBPAR]; T x2 = T(M_PI) * sqrt(par2 - T(1.0)); T x3 = T(M_PI) * sqrt(par2 - T(4.0)); T a = sinh(x1); T b = sinh(x2) * T(2.0); T c = sinh(x3) * T(2.0); T sum = a + b + c; a /= sum; b /= sum; c /= sum; T y = (x[0]-param_p[CENTER]) * T(M_PI) / param_p[WIDTH]; return param_p[HEIGHT]*(a + b * cos(y) + c * cos(T(2.0) * y)); } //# Member functions } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/KaiserBParam.h000066400000000000000000000072031476623553700217150ustar00rootroot00000000000000//# KaiserBParam.h: A one dimensional Kaiser-Bessel function //# Copyright (C) 2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_KAISERBPARAM_H #define SCIMATH_KAISERBPARAM_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // A one dimensional Kaiser-Bessel function // // // // // //
      • FunctionParam class //
      • Function class // // // A Kaiser-Bessel function // // // A Kaiser-Bessel is described by a height, a center, a width // (halfwidth) and a parameter. // The parameters are enumerated by HEIGHT, CENTER, WIDTH, KBPAR. They have // default values of (1, 0, 1, 2.5). // // // // // KaiserBFunction sf; // sf(0); // = 1.0 // // // //
      • T should have standard numerical operators // // //
      • AipsError if incorrect parameter number specified. // template class KaiserBParam : public Function { public: //# Enumerations // Parameter numbers enum { HEIGHT=0, CENTER, WIDTH, KBPAR }; //# Constructors // Constructs the KaiserB, Defaults: // height=1, center=0, width=1, kbpar=2.5. // Could not use default arguments // that worked both with gcc and IRIX // KaiserBParam(); explicit KaiserBParam(const T &kbpar); // // Copy constructor (deep copy) KaiserBParam(const KaiserBParam &other); // Copy assignment (deep copy) KaiserBParam &operator=(const KaiserBParam &other); template KaiserBParam(const KaiserBParam &other) : Function(other) {} // Destructor virtual ~KaiserBParam(); //# Operators virtual uInt ndim() const { return 1; } //# Member functions // Give name of function virtual const String &name() const { static String x("kaiserbessel"); return x; } //# Make members of parent classes known. protected: using Function::param_p; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/KaiserBParam.tcc000066400000000000000000000042051476623553700222360ustar00rootroot00000000000000//# KaiserBParam.cc: A one dimensional Kaiser-Bessel function //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_KAISERBPARAM_TCC #define SCIMATH_KAISERBPARAM_TCC //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template KaiserBParam::KaiserBParam() : Function(4) { param_p[HEIGHT] = T(1.0); param_p[CENTER] = T(0.0); param_p[WIDTH] = T(1.0); param_p[KBPAR] = T(2.5); } template KaiserBParam::KaiserBParam(const T &kbpar) : Function(4) { param_p[HEIGHT] = T(1.0); param_p[CENTER] = T(0.0); param_p[WIDTH] = T(1.0); param_p[KBPAR] = kbpar; } template KaiserBParam::KaiserBParam(const KaiserBParam &other) : Function(other) {} template KaiserBParam::~KaiserBParam() {} //# Operators template KaiserBParam &KaiserBParam::operator=(const KaiserBParam &other) { if (this != &other) Function::operator=(other); return *this; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/Lorentzian1D.h000066400000000000000000000243521476623553700217320ustar00rootroot00000000000000//# Lorentzian1D.h: A one-dimensional Lorentzian class //# Copyright (C) 1995,1996,1997,2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_LORENTZIAN1D_H #define SCIMATH_LORENTZIAN1D_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // A one dimensional Lorentzian class. // // // // //
      • Lorentzian1DParam //
      • Function // // // A Lorentzian1D functional is designed exclusively for calculating a // Lorentzian (or Normal) distribution in one dimension. //# Other classes exist (not yet!) //# for calculating these functions in two //# (Lorentzian2D) and N //# (LorentzianND) dimensions. // // // A Lorentzian1D is described by a height, center, and width. Its // fundamental operation is evaluating itself at some x. // The parameters (height, center and width) may be changed at run time. // // The width of the Lorentzian (for the constructors or the setWidth // function) is always specified in terms of the full width at half // maximum (FWHM). It is always positive and attempts to set a non-positive // width will throw an assertion when in debug mode. // // The peak height of the Lorentzian can be specified at construction time or // by using the setHeight function. Alternatively the // setFlux function can be used to implicitly set the peak height by // specifying the integrated area under the Lorentzian. The height (or flux) // can be positive, negative or zero, as this class makes no assumptions on // what quantity the height represents. // // Changing the width of the Lorentzian will not affect // its peak height but will change its flux. So you should always set the // width before setting the flux. // // The parameter interface (see // Lorentzian1DParam class), // is used to provide an interface to the // Fitting classes. // // There are 3 parameters that are used to describe the Lorentzian: //
          //
        1. The height of the Lorentzian. This is identical to the value // returned using the height() member function. //
        2. The center of the Lorentzian in the x direction. This is identical to // the value returned using the center() member function. //
        3. The width (FWHM) of the Lorentzian. To aid convergence of // the non-linear fitting routines this parameter is allowed to be // negative. This does not affect the shape of the Lorentzian as the // square of the width is used when evaluating the function. //
        // // An enumeration for the HEIGHT, WIDTH and // CENTER parameter index is provided, enabling the setting // and reading of parameters with the [] operator. The // mask() methods can be used to check and set the parameter masks. // //
        // // // Lorentzian gf(5.0, 25.0, 7); // gf(25); // = 5.0 // gf[HEIGHT](1.0); // gf.setWidth(2.0); // gf[CENTER](0.0); // gf(1); // = 0.5*height = 0.5 // // // //
      • T should have standard numerical operators and exp() function. Current // implementation only tested for real types. //
      • To obtain derivatives, the derivatives should be defined. // // //
      • Assertion in debug mode if attempt is made to set a negative width //
      • AipsError if incorrect parameter number specified. //
      • Assertion in debug mode if operator(Vector<>) with empty Vector // // //
      • Lorentzians that know about their DFT's could be required eventually. // template class Lorentzian1D : public Lorentzian1DParam { public: //# Enumerations //# Constructors // Constructs the one dimensional Lorentzians. Defaults: // height=1, center=0, width(FWHM)=1. // Could not use default arguments // that worked both with gcc and IRIX // Lorentzian1D() : Lorentzian1DParam() {}; explicit Lorentzian1D(const T &height) : Lorentzian1DParam(height) {}; Lorentzian1D(const T &height, const T ¢er) : Lorentzian1DParam(height, center) {}; Lorentzian1D(const T &height, const T ¢er, const T &width) : Lorentzian1DParam(height, center, width) {}; // // Copy constructor (deep copy) // Lorentzian1D(const Lorentzian1D &other) : Lorentzian1DParam(other) {}; template Lorentzian1D(const Lorentzian1D &other) : Lorentzian1DParam(other) {} // // Copy assignment (deep copy) Lorentzian1D &operator=(const Lorentzian1D &other) { Lorentzian1DParam::operator=(other); return *this; }; // Destructor virtual ~Lorentzian1D() {}; //# Operators // Evaluate the Lorentzian at x. // virtual T eval(typename Function1D::FunctionArg x) const; // //# Member functions // Return a copy of this object from the heap. The caller is responsible // for deleting this pointer. // virtual Function *clone() const { return new Lorentzian1D(*this); }; virtual Function::DiffType> *cloneAD() const { return new Lorentzian1D::DiffType>(*this); }; virtual Function::BaseType> *cloneNonAD() const { return new Lorentzian1D::BaseType>(*this); }; // //# Make members of parent classes known. protected: using Lorentzian1DParam::param_p; public: using Lorentzian1DParam::HEIGHT; using Lorentzian1DParam::CENTER; using Lorentzian1DParam::WIDTH; using Lorentzian1DParam::fwhm2int; }; #define Lorentzian1D_PS Lorentzian1D // Partial specialization of Lorentzian1D for AutoDiff // // // The name Lorentzian1D_PS is only for cxx2html // documentation problems. Use Lorentzian1D in your code. // template class Lorentzian1D_PS > : public Lorentzian1DParam > { public: //# Constructors // Constructs one dimensional Lorentzians. // Lorentzian1D_PS() : Lorentzian1DParam >() {}; explicit Lorentzian1D_PS(const AutoDiff &height) : Lorentzian1DParam >(height) {}; Lorentzian1D_PS(const AutoDiff &height, const AutoDiff ¢er) : Lorentzian1DParam >(height, center) {}; Lorentzian1D_PS(const AutoDiff &height, const AutoDiff ¢er, const AutoDiff &width) : Lorentzian1DParam >(height, center, width) {}; // // Copy constructor (deep copy) // Lorentzian1D_PS(const Lorentzian1D_PS &other) : Lorentzian1DParam >(other) {}; template Lorentzian1D_PS(const Lorentzian1D_PS &other) : Lorentzian1DParam >(other) {} // // Copy assignment (deep copy) Lorentzian1D_PS > & operator=(const Lorentzian1D_PS > &other) { Lorentzian1DParam >::operator=(other); return *this; }; // Destructor virtual ~Lorentzian1D_PS() {}; //# Operators // Evaluate the Lorentzian and its derivatives at x. // virtual AutoDiff eval(typename Function >::FunctionArg x) const; // //# Member functions // Return a copy of this object from the heap. The caller is responsible // for deleting this pointer. // virtual Function > *clone() const { return new Lorentzian1D >(*this); }; virtual Function >::DiffType> *cloneAD() const { return new Lorentzian1D >::DiffType> (*this); }; virtual Function >::BaseType> *cloneNonAD() const { return new Lorentzian1D >::BaseType> (*this); }; // //# Make members of parent classes known. protected: using Lorentzian1DParam >::param_p; public: using Lorentzian1DParam >::HEIGHT; using Lorentzian1DParam >::CENTER; using Lorentzian1DParam >::WIDTH; using Lorentzian1DParam >::fwhm2int; }; #undef Lorentzian1D_PS } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include "Lorentzian1D.tcc" #include "Lorentzian1D2.tcc" #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/Lorentzian1D.tcc000066400000000000000000000033641476623553700222540ustar00rootroot00000000000000//# Lorentzian1D.cc: A one-dimensional Lorentzian class //# Copyright (C) 1994,1995,1996,1999,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_LORENTZIAN1D_TCC #define SCIMATH_LORENTZIAN1D_TCC //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Operators template T Lorentzian1D::eval(typename Function1D::FunctionArg x) const { T value = (x[0] - param_p[CENTER])/param_p[WIDTH]/fwhm2int; return param_p[HEIGHT] / (T(1.0) + value*value); } //# Member functions } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/Lorentzian1D2.tcc000066400000000000000000000055211476623553700223330ustar00rootroot00000000000000//# Lorentzian1D2.cc: One dimensional Lorentzian class specialized for AutoDiff //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_LORENTZIAN1D2_TCC #define SCIMATH_LORENTZIAN1D2_TCC //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Operators template AutoDiff Lorentzian1D >:: eval(typename Function >::FunctionArg x) const { AutoDiff tmp; if (this->param_p[this->HEIGHT].nDerivatives() > 0) tmp = this->param_p[this->HEIGHT]; else if (this->param_p[this->CENTER].nDerivatives() > 0) tmp = this->param_p[this->CENTER]; else if (this->param_p[this->WIDTH].nDerivatives() > 0) tmp = this->param_p[this->WIDTH]; T x_norm = (x[0] - this->param_p[this->CENTER].value())/ this->param_p[this->WIDTH].value()/this->fwhm2int.value(); T exponential = T(1.0)/(T(1.0) + x_norm*x_norm); // function value tmp.value() = this->param_p[this->HEIGHT].value() * exponential; // get derivatives (assuming either all or none) if (tmp.nDerivatives()>0) { for (uInt j=0; jparam_p.mask(this->HEIGHT)) tmp.deriv(this->HEIGHT) = dev; // derivative wrt center T dev2 = this->param_p[this->HEIGHT].value()*dev*dev*T(2.0)*x_norm/ this->param_p[this->WIDTH].value(); if (this->param_p.mask(this->CENTER)) tmp.deriv(this->CENTER) = dev2/this->fwhm2int.value(); // derivative wrt width if (this->param_p.mask(this->WIDTH)) tmp.deriv(this->WIDTH) = dev2*x_norm; } return tmp; } //# Member functions } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/Lorentzian1DParam.h000066400000000000000000000163021476623553700227070ustar00rootroot00000000000000//# Lorentzian1DParam.h: Parameter handling for one-dimensional Lorentzian class //# Copyright (C) 2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_LORENTZIAN1DPARAM_H #define SCIMATH_LORENTZIAN1DPARAM_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // Parameter handling for one dimensional Lorentzian class. // // // // //
      • FunctionParam class //
      • Function1D class // // // A 1-dimensional Lorentzian's parameters. // // // A Lorentzian1D is described by a height, center, and width. // The parameters (height, center and width) may be changed at run time. // // The width of the Lorentzian (for the constructors or the setWidth // function) is always specified in terms of the full width at half // maximum (FWHM). It is always positive and attempts to set a non-positive // width will throw an assertion when in debug mode. // // The peak height of the Lorentzian can be specified at construction time or // by using the setHeight function. Alternatively the // setFlux function can be used to implicitly set the peak height by // specifying the integrated area under the Lorentzian. The height (or flux) // can be positive, negative or zero, as this class makes no assumptions on // what quantity the height represents. // // Changing the width of the Lorentzian will not affect // its peak height but will change its flux. So you should always set the // width before setting the flux. // // The parameter interface (see // FunctionParam class), // is used to provide an interface to the // Fitting classes. // // There are 3 parameters that are used to describe the Lorentzian: //
          //
        1. The height of the Lorentzian. This is identical to the value // returned using the height() member function. //
        2. The center of the Lorentzian in the x direction. This is identical to // the value returned using the center() member function. //
        3. The width (FWHM) of the Lorentzian. To aid convergence of // the non-linear fitting routines this parameter is allowed to be // negative. This does not affect the shape of the Lorentzian as the // square of the width is used when evaluating the function. //
        // // An enumeration for the HEIGHT, WIDTH and // CENTER parameter index is provided, enabling the setting // and reading of parameters with the [] operator. The // mask() methods can be used to check and set the parameter masks. // // This class is in general used implicitly by the Lorentzian1D // class only. //
        // // // Lorentzian1D gf(5.0, 25.0, 7); // gf(25); // = 5.0 // gf.setHeight(1.0); // gf[WIDTH](2.0); // gf[CENTER](0.0); // gf(1); // = 0.5*height = 0.5 // // // //
      • T should have standard numerical operators and exp() function. Current // implementation only tested for real types (and their AutoDiffs). // // //
      • Assertion in debug mode if attempt is made to set a negative width //
      • AipsError if incorrect parameter number specified. // // //
      • Lorentzians that know about their DFT's could be required eventually. // template class Lorentzian1DParam : public Function1D { public: //# Enumerations enum { HEIGHT=0, CENTER, WIDTH }; //# Constructors // Constructs the one dimensional Lorentzians. Defaults: // height=1, center=0, width(FWHM)=1. // Could not use default arguments // that worked both with gcc and IRIX and all templates // Lorentzian1DParam(); explicit Lorentzian1DParam(const T &height); Lorentzian1DParam(const T &height, const T ¢er); Lorentzian1DParam(const T &height, const T ¢er, const T &width); // // Copy constructor (deep copy) // Lorentzian1DParam(const Lorentzian1DParam &other); template Lorentzian1DParam(const Lorentzian1DParam &other) : Function1D(other), fwhm2int(T(1.0)/T(2.0)) {} // // Copy assignment (deep copy) Lorentzian1DParam &operator=(const Lorentzian1DParam &other); // Destructor virtual ~Lorentzian1DParam(); //# Operators //# Member functions // Give name of function virtual const String &name() const { static String x("lorentzian1d"); return x; }; // Get or set the peak height of the Lorentzian // T height() const { return param_p[HEIGHT]; }; void setHeight(const T &height) { param_p[HEIGHT] = height; }; // // Get or set the analytical integrated area underneath the Lorentzian. // Use these functions as an alternative to the height functions. // T flux() const; void setFlux(const T &flux); // // Get or set the center ordinate of the Lorentzian // T center() const { return param_p[CENTER]; }; void setCenter(const T &cnter) { param_p[CENTER] = cnter; }; // // Get or set the FWHM of the Lorentzian. // T width() const { return param_p[WIDTH]; }; void setWidth(const T &width) { param_p[WIDTH] = width; }; // protected: // Constant to scale halfwidth at 1/e to FWHM T fwhm2int; //# Make members of parent classes known. protected: using Function1D::param_p; public: using Function1D::nparameters; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include "Lorentzian1DParam.tcc" #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/Lorentzian1DParam.tcc000066400000000000000000000064051476623553700232340ustar00rootroot00000000000000//# Lorentzian1DParam.cc: Parameter handling for one-dimensional Lorentzian class //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_LORENTZIAN1DPARAM_TCC #define SCIMATH_LORENTZIAN1DPARAM_TCC //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Statics ///template ///const T Lorentzian1DParam::fwhm2int = T(1.0)/sqrt(log(T(16.0))); //# Constructors template Lorentzian1DParam::Lorentzian1DParam() : Function1D(3), fwhm2int(T(1.0)/T(2.0)) { param_p[HEIGHT] = T(1.0); param_p[CENTER] = T(0.0); param_p[WIDTH] = T(1.0); } template Lorentzian1DParam::Lorentzian1DParam(const T &height) : Function1D(3), fwhm2int(T(1.0)/T(2.0)) { param_p[HEIGHT] = height; param_p[CENTER] = T(0.0); param_p[WIDTH] = T(1.0); } template Lorentzian1DParam::Lorentzian1DParam(const T &height, const T ¢er) : Function1D(3), fwhm2int(T(1.0)/T(2.0)) { param_p[HEIGHT] = height; param_p[CENTER] = center; param_p[WIDTH] = T(1.0); } template Lorentzian1DParam::Lorentzian1DParam(const T &height, const T ¢er, const T &width) : Function1D(3), fwhm2int(T(1.0)/T(2.0)) { param_p[HEIGHT] = height; param_p[CENTER] = center; param_p[WIDTH] = width; } template Lorentzian1DParam::Lorentzian1DParam(const Lorentzian1DParam &other) : Function1D(other), fwhm2int(T(1.0)/T(2.0)) {} template Lorentzian1DParam::~Lorentzian1DParam() {} //# Operators template Lorentzian1DParam & Lorentzian1DParam::operator=(const Lorentzian1DParam &other) { if (this != &other) { fwhm2int = other.fwhm2int; Function1D::operator=(other); } return *this; } //# Member functions template T Lorentzian1DParam::flux() const { return param_p[HEIGHT]*abs(param_p[WIDTH])*fwhm2int*T(C::pi); } template void Lorentzian1DParam::setFlux(const T &flux) { param_p[HEIGHT] = flux*T(C::_1_pi)/abs(param_p[WIDTH])/fwhm2int; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/MarshButterworthBandpass.h000066400000000000000000000130711476623553700244140ustar00rootroot00000000000000//# MarshButterworthBandpass.h: a Marshallable SimButterworthBandpass //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_MARSHBUTTERWORTHBANDPASS_H #define SCIMATH_MARSHBUTTERWORTHBANDPASS_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // A Butterworth function class that supports serialization // // // // // //
      • Function // // // // "Marsh" is short for "Marshallable" which means that the class can // be serialized into a form that can be transmitted to another // execution context. "ButterBandpass" refers to its parent class: // SimButterworthBandpass. // // // // This class is a specialization of SimButterworthBandpass class that // supports serialization. That is, it allows one to write the state of the // SimButterworthBandpass function object into a Record. This record // can then be transmitted to another execution context // where it can be "reconstituted" as a new object with // identical state as this one. This documentation focusses on this // serialization functionality (also known as "marshalling"); for details // about the general features of this Butterworth function, see the // SimButterworthBandpass // class. // // // // // // // Making SimButterworthBandpass Marshallable provides a convenient way of // configuring the simulator tool from . // // // //
      • Assertion in debug mode if attempt is made to address incorrect // coefficients // // template class MarshButterworthBandpass : public SimButterworthBandpass, public FunctionMarshallable { public: static const String FUNCTYPE; static const String FUNCFIELDS[]; // definitions of the fields stored in a serialized Record. The // actual string names are stored in FUNCFIELDS enum FieldNames { // the minimum cutoff, center, and maximum cutoff values BPASS, // the orders of the transitions between pass and no-pass ORDER, // the peak value PEAK, // the number of supported fields NFieldNames }; //# Constructors // create a zero-th order (all-pass) Butterworth bandpass. MarshButterworthBandpass() : SimButterworthBandpass(), FunctionMarshallable(FUNCTYPE) {} // create a Butterworth bandpass function. MarshButterworthBandpass(uInt minord, uInt maxord, T mincut=T(-1), T maxcut=T(1), T center=T(0), T peak=T(1)) : SimButterworthBandpass(minord, maxord, mincut, maxcut, center, peak), FunctionMarshallable(FUNCTYPE) {} // create a fully specified Butterworth polynomial from parameters // stored in a Record. explicit MarshButterworthBandpass(const Record& gr) throw(InvalidSerializationError); // create a deep copy of another Butterworth polynomial // MarshButterworthBandpass(const SimButterworthBandpass &other) : SimButterworthBandpass(other), FunctionMarshallable(FUNCTYPE) {} MarshButterworthBandpass(const MarshButterworthBandpass &other) : SimButterworthBandpass(other), FunctionMarshallable(other) {} // // make a (deep) copy of another Butterworth polynomial // MarshButterworthBandpass &operator=( const MarshButterworthBandpass &other) { FunctionMarshallable::operator=(other); SimButterworthBandpass::operator=(other); return *this; } MarshButterworthBandpass &operator=( const SimButterworthBandpass &other) { SimButterworthBandpass::operator=(other); return *this; } // // Destructor virtual ~MarshButterworthBandpass() {} // store the state of this Function into a Record virtual void store(Record& gr) const; // Create a copy of this object. The caller is responsible for // deleting the pointer. virtual Function *clone() const { return new MarshButterworthBandpass(*this); } }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/MarshButterworthBandpass.tcc000066400000000000000000000062411476623553700247370ustar00rootroot00000000000000//# MarshButterworthBandpass.cc: a Marshallable SimButterworthBandpass //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_MARSHBUTTERWORTHBANDPASS_TCC #define SCIMATH_MARSHBUTTERWORTHBANDPASS_TCC #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template const String MarshButterworthBandpass::FUNCTYPE("butterworthbp"); template const String MarshButterworthBandpass::FUNCFIELDS[] = { "bpass", "order", "peak" }; template void MarshButterworthBandpass::store(Record& out) const { loadFuncType(out); Vector bpass(3); bpass(0) = this->getMinCutoff(); bpass(1) = this->getCenter(); bpass(2) = this->getMaxCutoff(); out.define(FUNCFIELDS[BPASS], bpass); Vector order(2); order(0) = this->getMinOrder(); order(1) = this->getMaxOrder(); out.define(FUNCFIELDS[ORDER], order); out.define(FUNCFIELDS[PEAK], this->getPeak()); } template MarshButterworthBandpass::MarshButterworthBandpass(const Record& gr) throw(InvalidSerializationError) : SimButterworthBandpass(), FunctionMarshallable(FUNCTYPE) { SerialHelper input(gr); input.checkFuncType(FUNCTYPE); if (input.exists(FUNCFIELDS[BPASS])) { Vector bpass; input.get(bpass, FUNCFIELDS[BPASS]); if (bpass.nelements() < 3) throw InvalidSerializationError(FUNCFIELDS[BPASS] + " field contains fewer than three elements"); this->setMinCutoff(bpass(0)); this->setCenter(bpass(1)); this->setMaxCutoff(bpass(2)); } if (input.exists(FUNCFIELDS[ORDER])) { Vector order; input.get(order, FUNCFIELDS[ORDER]); if (order.nelements() < 2) throw InvalidSerializationError(FUNCFIELDS[ORDER] + " field contains fewer than two elements"); this->setMinOrder(order(0)); this->setMaxOrder(order(1)); } if (input.exists(FUNCFIELDS[PEAK])) { T peak(0); input.get(peak, FUNCFIELDS[PEAK]); this->setPeak(peak); } } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/MarshallableChebyshev.h000066400000000000000000000163771476623553700236600ustar00rootroot00000000000000//# MarshalableChebyshev.h: a Marshallable Chebyshev polynomial //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //#! ======================================================================== #ifndef SCIMATH_MARSHALLABLECHEBYSHEV_H #define SCIMATH_MARSHALLABLECHEBYSHEV_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // A Chebyshev function class that supports serialization // // // // // //
      • Function // // // // This class is named after Chebyshev Type I polynomials. "Marshallable" // means that the class can be serialized into a form that can be transmitted // to another execution context. // // // // This class is a specialization of the Chebyshev class that supports // serialization. That is, it allows one to write the state of the Chebyshev // polynomial object into a Record. This record can then be transmitted // to another execution context where it // can be "reconstituted" as a new object with identical state as this one. // This documentation focusses on this serialization functionality (also known // as "marshalling"); for details about the general features of the Chebyshev // polynomial series, see the Chebyshev // class. // // // // // // // Making Chebyshev Marshallable provides a convenient way of configuring // the simulator tool from . // // // //
      • Assertion in debug mode if attempt is made to address incorrect // coefficients // // template class MarshallableChebyshev : public Chebyshev, public FunctionMarshallable { private: static const String modenames[]; public: static const String FUNCTYPE; static const String FUNCFIELDS[]; // definitions of the fields stored in a serialized Record. The // actual string names are stored in FUNCFIELDS enum FieldNames { // the array of coefficients COEFFS, // the default mode MODE, // the default value to use when mode=CONSTANT DEF, // the 2-element double array INTERVAL, // the number of supported fields NFieldNames }; //# Constructors // create a zero-th order Chebyshev polynomial with the first coefficient // equal to zero. The bounded domain is [T(-1), T(1)]. The // OutOfDomainMode is CONSTANT, and the default value is T(0). MarshallableChebyshev() : Chebyshev(), FunctionMarshallable(FUNCTYPE) {} // create an n-th order Chebyshev polynomial with the coefficients // equal to zero. The bounded domain is [T(-1), T(1)]. The // OutOfDomainMode is CONSTANT, and the default value is T(0). explicit MarshallableChebyshev(const uInt n) : Chebyshev(n), FunctionMarshallable(FUNCTYPE) {} // create a zero-th order Chebyshev polynomical with the first coefficient // equal to one. // min is the minimum value of its Chebyshev interval, and // max is the maximum value. // mode sets the behavior of the function outside the Chebyshev interval // (see setOutOfIntervalMode() and OutOfIntervalMode enumeration // definition for details). // defval is the value returned when the function is evaluated outside // the Chebyshev interval and mode=CONSTANT. MarshallableChebyshev(const T &min, const T &max, const typename ChebyshevEnums:: OutOfIntervalMode mode=ChebyshevEnums::CONSTANT, const T &defval=T(0)) : Chebyshev(min, max, mode, defval), FunctionMarshallable(FUNCTYPE) {} // create a fully specified Chebyshev polynomial. // coeffs holds the coefficients of the Chebyshev polynomial (see // setCoefficients() for details). // min is the minimum value of its canonical range, and // max is the maximum value. // mode sets the behavior of the function outside the Chebyshev interval // (see setOutOfIntervalMode() and OutOfIntervalMode enumeration // definition for details). // defval is the value returned when the function is evaluated outside // the canonical range and mode=CONSTANT. MarshallableChebyshev(const Vector &coeffs, const T &min, const T &max, const typename ChebyshevEnums:: OutOfIntervalMode mode=ChebyshevEnums::CONSTANT, const T &defval=T(0)) : Chebyshev(coeffs, min, max, mode, defval), FunctionMarshallable(FUNCTYPE) {} // create a fully specified Chebyshev polynomial from parameters // stored in a Record. explicit MarshallableChebyshev(const Record& gr) throw(InvalidSerializationError); // create a deep copy of another Chebyshev polynomial // MarshallableChebyshev(const Chebyshev &other) : Chebyshev(other), FunctionMarshallable(FUNCTYPE) {} MarshallableChebyshev(const MarshallableChebyshev &other) : Chebyshev(other), FunctionMarshallable(other) {} // // make a (deep) copy of another Chebyshev polynomial // MarshallableChebyshev &operator=( const MarshallableChebyshev &other) { FunctionMarshallable::operator=(other); Chebyshev::operator=(other); return *this; } MarshallableChebyshev &operator=( const Chebyshev &other) { Chebyshev::operator=(other); return *this; } // // Destructor virtual ~MarshallableChebyshev() {} // store the state of this Function into a Record virtual void store(Record& gr) const; // Create a copy of this object. The caller is responsible for // deleting the pointer. virtual Function *clone() const { return new MarshallableChebyshev(*this); } }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/MarshallableChebyshev.tcc000066400000000000000000000066671476623553700242030ustar00rootroot00000000000000//# MarshalableChebyshev.h: a Marshallable Chebyshev polynomial //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //#! ======================================================================== #ifndef SCIMATH_MARSHALLABLECHEBYSHEV_TCC #define SCIMATH_MARSHALLABLECHEBYSHEV_TCC #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template const String MarshallableChebyshev::modenames[] = { "default", "zeroth", "extrapolate", "cyclic", "edge" }; template const String MarshallableChebyshev::FUNCTYPE("chebyshev"); template const String MarshallableChebyshev::FUNCFIELDS[] = { "coeffs", "mode", "def", "interval" }; template void MarshallableChebyshev::store(Record& out) const { loadFuncType(out); out.define(FUNCFIELDS[COEFFS], this->getCoefficients()); out.define(FUNCFIELDS[MODE], modenames[this->getOutOfIntervalMode()]); out.define(FUNCFIELDS[DEF], this->getDefault()); Vector intv(2); intv(0) = this->getIntervalMin(); intv(1) = this->getIntervalMax(); out.define(FUNCFIELDS[INTERVAL], intv); } template MarshallableChebyshev::MarshallableChebyshev(const Record& gr) throw(InvalidSerializationError) : Chebyshev(), FunctionMarshallable(FUNCTYPE) { SerialHelper input(gr); input.checkFuncType(FUNCTYPE); if (input.exists(FUNCFIELDS[COEFFS])) { Vector coeffs; input.get(coeffs, FUNCFIELDS[COEFFS]); this->setCoefficients(coeffs); } if (input.exists(FUNCFIELDS[MODE])) { String modename; uInt i=0; input.get(modename, FUNCFIELDS[MODE]); for(i=0; i < ChebyshevEnums::NOutOfIntervalModes; i++) { if (modename == modenames[i]) break; } if (i == ChebyshevEnums::NOutOfIntervalModes) throw InvalidSerializationError(String("Unrecognized mode: ") + modename); this->setOutOfIntervalMode( static_cast(i)); } if (input.exists(FUNCFIELDS[DEF])) { T defval(0); input.get(defval, FUNCFIELDS[DEF]); this->setDefault(defval); } if (input.exists(FUNCFIELDS[INTERVAL])) { T mn, mx; input.get(mn, FUNCFIELDS[INTERVAL], 0); input.get(mx, FUNCFIELDS[INTERVAL], 1); this->setInterval(mn, mx); } } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/OddPolynomial.h000066400000000000000000000154561476623553700221770ustar00rootroot00000000000000//# OddPolynomial.h: A one dimensional odd polynomial class //# Copyright (C) 2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_ODDPOLYNOMIAL_H #define SCIMATH_ODDPOLYNOMIAL_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // A one dimensional odd polynomial class // // // // //
      • Function // // // // A OddPolynomial contains a set of coefficients; // its fundamental operation is evaluating itself at some "x". // The number of coefficients is the order of the polynomial divided by two, // plus one, so is the number of available parameters. // // // // // // OddPolynomial pf(3); // Third order polynomial - coeffs 0 by default // pf.setCoefficient(0, 1.0); // pf[1] = 2.0; // 2x^3 + 1x^1 // pf(2); // == 18 // // // //
      • T should have standard numerical operators. Current // implementation only tested for real types (and their AutoDiffs). // // //
      • Assertion in debug mode if attempt is made to address incorrect // coefficients // // //
      • Nothing I know off // template class OddPolynomial: public OddPolynomialParam { public: //# Enumerations //# Constructors // Constructs a first order polynomial, with a coeficcient of 0.0. OddPolynomial() : OddPolynomialParam() {} // Makes a polynomial of the given order, with all coeficcients set to // zero. explicit OddPolynomial(uInt order) : OddPolynomialParam(order) {} // Copy constructor/assignment (deep copy) // OddPolynomial(const OddPolynomial &other) : OddPolynomialParam(other) {} template OddPolynomial(const OddPolynomial &other) : OddPolynomialParam(other) {} OddPolynomial &operator=(const OddPolynomial &other) { OddPolynomialParam::operator=(other); return *this; } // // Destructor virtual ~OddPolynomial() {} //# Operators // Evaluate the polynomial at x. virtual T eval(typename Function1D::FunctionArg x) const; //# Member functions // Return a copy of this object from the heap. The caller is responsible for // deleting the pointer. // virtual Function *clone() const { return new OddPolynomial(*this); } virtual Function::DiffType> *cloneAD() const { return new OddPolynomial::DiffType>(*this); } virtual Function::BaseType> *cloneNonAD() const { return new OddPolynomial::BaseType>(*this); } // //# Make members of parent classes known. protected: using OddPolynomialParam::param_p; public: using OddPolynomialParam::nparameters; }; #define OddPolynomial_PS OddPolynomial // Partial specialization of OddPolynomial for AutoDiff // // // The name OddPolynomial_PS is only for cxx2html // documentation problems. Use OddPolynomial in your code. // template class OddPolynomial_PS > : public OddPolynomialParam > { public: //# Constructors // Constructs one dimensional OddPolynomials. // OddPolynomial_PS() : OddPolynomialParam >() {} explicit OddPolynomial_PS(uInt order) : OddPolynomialParam >(order) {} // // Copy constructor (deep copy) // OddPolynomial_PS(const OddPolynomial_PS > &other) : OddPolynomialParam >(other) {} template OddPolynomial_PS(const OddPolynomial_PS &other) : OddPolynomialParam >(other) {} // // Copy assignment (deep copy) OddPolynomial_PS > & operator=(const OddPolynomial_PS > &other) { OddPolynomialParam >::operator=(other); return *this; } // Destructor virtual ~OddPolynomial_PS() {} //# Operators // Evaluate the polynomial and its derivatives at x wrt // to the coefficients. // virtual AutoDiff eval(typename Function >::FunctionArg x) const; // //# Member functions // Return a copy of this object from the heap. The caller is responsible // for deleting this pointer. // virtual Function > *clone() const { return new OddPolynomial >(*this); } virtual Function >::DiffType> *cloneAD() const { return new OddPolynomial >::DiffType> (*this); } virtual Function >::BaseType> *cloneNonAD() const { return new OddPolynomial >::BaseType> (*this); } // //# Make members of parent classes known. protected: using OddPolynomialParam >::param_p; public: using OddPolynomialParam >::nparameters; }; #undef OddPolynomial_PS } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/OddPolynomial.tcc000066400000000000000000000033511476623553700225100ustar00rootroot00000000000000//# OddPolynomial.cc: A one dimensional odd polynomial class //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_ODDPOLYNOMIAL_TCC #define SCIMATH_ODDPOLYNOMIAL_TCC //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Operators template T OddPolynomial::eval(typename Function1D::FunctionArg x) const { Int j = nparameters(); T accum = param_p[--j]*x[0]; while (--j >= 0) { accum *= x[0]; accum += param_p[j]; accum *= x[0]; } return accum; } //# Member functions } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/OddPolynomial2.tcc000066400000000000000000000044741476623553700226010ustar00rootroot00000000000000//# OddPolynomial2.cc: Odd polynomial class specialized for AutoDiff //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_ODDPOLYNOMIAL2_TCC #define SCIMATH_ODDPOLYNOMIAL2_TCC //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Operators template AutoDiff OddPolynomial >:: eval(typename Function >::FunctionArg x) const { AutoDiff tmp; for (uInt i=0; inparameters(); ++i) { if (this->param_p[i].nDerivatives() > 0) { tmp = this->param_p[i]; break; } } // function value Int j = this->nparameters(); tmp.value() = this->param_p[--j].value()*x[0]; while (--j >= 0) { tmp.value() *= x[0]; tmp.value() += this->param_p[j].value(); tmp.value() *= x[0]; } // get derivatives (assuming either all or none) if (tmp.nDerivatives()>0) { for (uInt j=0; jnparameters(); ++i) { if (this->param_p.mask(i)) tmp.deriv(i) = dev; dev *= x[0]; dev *= x[0]; } } return tmp; } //# Member functions } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/OddPolynomialParam.h000066400000000000000000000126021476623553700231460ustar00rootroot00000000000000//# OddPolynomialParam.h: Parameter handling for odd polynomials //# Copyright (C) 2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_ODDPOLYNOMIALPARAM_H #define SCIMATH_ODDPOLYNOMIALPARAM_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Parameter handling for odd polynomials // // // // //
      • FunctionParam class //
      • Function1D // // // A 1-dimensional OddPolynomial's parameters. // // // // An OddPolynomial is described by a set of coefficients; // its fundamental operation is evaluating itself at some "x". // The number of coefficients is the order of the polynomial divided // by two, plus one. // // Since the OddPolynomial is a Function, the derivatives // can be obtained as well. // // The parameter interface (see // FunctionParam class), // is used to provide an interface to the // Fitting classes. // // This class is in general used implicitly by the OddPolynomial // class only. // // // // // OddPolynomial pf(3); // Third order polynomial - coeffs 0 by default // pf.setCoefficient(0, 1.0); // pf[1] = 2.0; // 2x^3 + 1x^1 // pf(2); // == 18 // // // //
      • T should have standard numerical operators. Current // implementation only tested for real types (and their AutoDiffs). // // //
      • Assertion in debug mode if attempt is made to address incorrect // coefficients // // //
      • Nothing I know off // template class OddPolynomialParam: public Function1D { public: //# Constructors // Constructs a first order polynomial, with a coeficcient of 0.0. OddPolynomialParam(); // Makes a polynomial of the given order, with all coeficcients set to // zero. explicit OddPolynomialParam(uInt order); // Make this a copy of other (deep copy). // OddPolynomialParam(const OddPolynomialParam &other); template OddPolynomialParam(const OddPolynomialParam &other) : Function1D(other) {} OddPolynomialParam &operator=(const OddPolynomialParam &other); // // Destructor ~OddPolynomialParam(); //# Operators // Comparisons. // OddPolynomials are equal if they are the same order // Bool operator==(const OddPolynomialParam &other) const { return (param_p == other.param_p); } Bool operator!=(const OddPolynomialParam &other) const { return (param_p != other.param_p); } // //# Member functions // Give name of function virtual const String &name() const { static String x("oddpolynomial"); return x; } // What is the order of the polynomial, i.e. maximum exponent of "x". uInt order() const { return 2*param_p.nelements() - 1; } // What is the which'th coefficient of the polynomial. For an nth // degree polynomial, which varies between zero and n/2. T coefficient(uInt which) const { DebugAssert(which<=order(), AipsError); return param_p[which]; } // Return all the coefficients as a vector. const Vector &coefficients() const; // Set the which'th coefficient to value. void setCoefficient(uInt which, const T value) { DebugAssert(which<=order(), AipsError); param_p[which] = value; } // Set all the coefficients at once, throw away all existing coefficients. void setCoefficients(const Vector &coefficients); //# Make members of parent classes known. protected: using Function1D::param_p; public: using Function1D::nparameters; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/OddPolynomialParam.tcc000066400000000000000000000044601476623553700234730ustar00rootroot00000000000000//# OddPolynomialParam.cc: Parameter handling for odd polynomials //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_ODDPOLYNOMIALPARAM_TCC #define SCIMATH_ODDPOLYNOMIALPARAM_TCC //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template OddPolynomialParam::OddPolynomialParam() : Function1D(1) {} template OddPolynomialParam::OddPolynomialParam(uInt order) : Function1D(order/2 + 1) {} template OddPolynomialParam::OddPolynomialParam(const OddPolynomialParam &other) : Function1D(other) {} template OddPolynomialParam::~OddPolynomialParam() {} template OddPolynomialParam & OddPolynomialParam::operator=(const OddPolynomialParam &other) { if (this != &other) Function1D::operator=(other); return *this; } template const Vector &OddPolynomialParam::coefficients() const { return param_p.getParameters(); } template void OddPolynomialParam::setCoefficients(const Vector &coefficients) { param_p.setParameters(coefficients); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/PoissonFunction.h000066400000000000000000000154031476623553700225550ustar00rootroot00000000000000//# Copyright (C) 2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_POISSONFUNCTION_H #define SCIMATH_POISSONFUNCTION_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // A one dimensional Poisson function // // // // // //
      • PoissonParam //
      • Function // // // A 1-dimensional Poisson. // // // A Poisson is described by lambda. // The parameters are enumerated by LAMBDA. They have // default values of 1. // // // // // PoissonFunction sf(5.0); // sf(25); // = 5.0 // // // //
      • T should have standard numerical operators // // //
      • AipsError if incorrect parameter number specified. // // template class PoissonFunction : public PoissonParam { public: //# Constructors // Constructs the PoissonFunction, Defaults: // lambda=1. // Could not use default arguments // that worked both with gcc and IRIX // PoissonFunction() : PoissonParam() {} explicit PoissonFunction(const T &lambda) : PoissonParam(lambda) {} PoissonFunction( const T& lambda, const T& height ): PoissonParam(lambda,height){} // // Copy constructor (deep copy) // PoissonFunction(const PoissonFunction &other) : PoissonParam(other) {} template PoissonFunction(const PoissonFunction &other) : PoissonParam(other) {} // // Copy assignment (deep copy) PoissonFunction &operator=(const PoissonFunction &other) { PoissonParam::operator=(other); return *this; } // Destructor virtual ~PoissonFunction() {} //# Operators // Evaluate the Poisson at x. // If a vector is used as the argument only its first element is used. // virtual T eval(typename Function::FunctionArg x) const; // //# Member functions // Return a copy of this object from the heap. The caller is responsible // for deleting this pointer. // virtual Function *clone() const { return new PoissonFunction(*this); } virtual Function::DiffType> *cloneAD() const { return new PoissonFunction::DiffType>(*this); } virtual Function::BaseType> *cloneNonAD() const { return new PoissonFunction::BaseType>(*this); } // //# Make members of parent classes known. protected: using PoissonParam::param_p; public: using PoissonParam::nparameters; using PoissonParam::LAMBDA; using PoissonParam::HEIGHT; }; #define PoissonFunction_PS PoissonFunction // Partial specialization of PoissonFunction for AutoDiff // // // The name PoissonFunction_PS is only for cxx2html // documentation problems. Use PoissonFunction in your code. // template class PoissonFunction_PS > : public PoissonParam > { public: //# Constructors // Constructs one dimensional Poisson. // PoissonFunction_PS() : PoissonParam >() {} explicit PoissonFunction_PS(const AutoDiff &lambda) : PoissonParam >(lambda) {} PoissonFunction_PS( const AutoDiff & lambda, const AutoDiff& height): PoissonParam >(lambda,height){} // // Copy constructor (deep copy) // PoissonFunction_PS(const PoissonFunction_PS &other) : PoissonParam >(other) {} template PoissonFunction_PS(const PoissonFunction_PS &other) : PoissonParam >(other) {} // // Copy assignment (deep copy) PoissonFunction_PS > & operator=(const PoissonFunction_PS > &other) { PoissonFunction >::operator=(other); return *this; } // Destructor virtual ~PoissonFunction_PS() {} //# Operators // Evaluate the Poisson and its derivatives at x. // virtual AutoDiff eval(typename Function >::FunctionArg x) const; // //# Member functions // Return a copy of this object from the heap. The caller is responsible // for deleting this pointer. // virtual Function > *clone() const { return new PoissonFunction >(*this); } virtual Function >::DiffType> *cloneAD() const { return new PoissonFunction >::DiffType> (*this); } virtual Function >::BaseType> *cloneNonAD() const { return new PoissonFunction >::BaseType> (*this); } // //# Make members of parent classes known. protected: using PoissonParam >::param_p; public: using PoissonParam >::LAMBDA; using PoissonParam >::HEIGHT; }; #undef PoissonFunction_PS } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/PoissonFunction.tcc000066400000000000000000000034651476623553700231040ustar00rootroot00000000000000//# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_POISSONFUNCTION_TCC #define SCIMATH_POISSONFUNCTION_TCC #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Operators template T PoissonFunction::eval(typename Function::FunctionArg x) const { int xVal = static_cast(x[0]); return param_p[HEIGHT]*(pow( param_p[LAMBDA], xVal ) * exp(-1 * param_p[LAMBDA] ) / Combinatorics::factorial( xVal )); } //# Member functions } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/PoissonFunction2.tcc000066400000000000000000000050401476623553700231550ustar00rootroot00000000000000//# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_POISSONFUNCTION2_TCC #define SCIMATH_POISSONFUNCTION2_TCC //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Operators template AutoDiff PoissonFunction >:: eval(typename Function >::FunctionArg x) const { AutoDiff tmp; if (param_p[LAMBDA].nDerivatives() > 0){ tmp = param_p[LAMBDA]; } else if (param_p[HEIGHT].nDerivatives() > 0 ){ tmp = param_p[HEIGHT]; } // function value int xVal = static_cast(x[0]); double lambdaVal = param_p[LAMBDA].value(); double heightVal = param_p[HEIGHT].value(); tmp.value() = heightVal * ( pow( lambdaVal, xVal ) * exp(-1 * lambdaVal ) / Combinatorics::factorial( xVal )); // get derivatives (assuming either all or none) if (tmp.nDerivatives()>0) { for (uInt j=0; j #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // A one dimensional Poisson function // // // // // //
      • FunctionParam class //
      • Function class // // // A 1-dimensional Poisson. // // // A Poisson is described by lambda // The value is: // // height (|x-center| == 0.0) // 0 (|x-center| != 0.0) // // The parameters are enumerated by LAMDA. They have // default values of 1. // // // // // PoissonFunction sf(5.0); // sf(25); // = 5.0 // // // //
      • T should have standard numerical operators // // //
      • AipsError if incorrect parameter number specified. // template class PoissonParam : public Function { public: //# Enumerations // Parameter numbers enum { LAMBDA=0, HEIGHT}; //# Constructors // Constructs the Poisson, Defaults: // lambda=1. // Could not use default arguments // that worked both with gcc and IRIX // PoissonParam(); explicit PoissonParam(const T &lambda); PoissonParam( const T &lambda, const T &height ); // // Copy constructor (deep copy) // PoissonParam(const PoissonParam &other); template PoissonParam(const PoissonParam &other) : Function(other) {} // // Copy assignment (deep copy) PoissonParam &operator=(const PoissonParam &other); // Destructor virtual ~PoissonParam(); //# Operators virtual uInt ndim() const { return 1; } //# Member functions // Give name of function virtual const String &name() const { static String x("poisson"); return x; } // Get or set lambda T lambda() const { return param_p[LAMBDA]; } void setLambda(const T &lambda) { param_p[LAMBDA] = lambda; } T height() const { return param_p[HEIGHT]; } void setHeight(const T &height) { param_p[HEIGHT] = height; } //# Make members of parent classes known. protected: using Function::param_p; public: using Function::nparameters; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/PoissonParam.tcc000066400000000000000000000041761476623553700223570ustar00rootroot00000000000000//# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_POISSONPARAM_TCC #define SCIMATH_POISSONPARAM_TCC //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template PoissonParam::PoissonParam() : Function(2) { param_p[LAMBDA] = T(1.0); param_p[HEIGHT] = T(1.0); } template PoissonParam::PoissonParam(const T &lambda) : Function(2) { param_p[LAMBDA] = lambda; param_p[HEIGHT] = T(1.0); } template PoissonParam::PoissonParam( const T &lambda, const T &height ) : Function(2){ param_p[LAMBDA] = lambda; param_p[HEIGHT] = height; } template PoissonParam::PoissonParam(const PoissonParam &other) : Function(other) {} template PoissonParam::~PoissonParam() {} //# Operators template PoissonParam &PoissonParam::operator=(const PoissonParam &other) { if (this != &other) Function::operator=(other); return *this; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/Polynomial.h000066400000000000000000000163221476623553700215410ustar00rootroot00000000000000//# Polynomial.h: A one dimensional polynomial class //# Copyright (C) 1994,1995,1996,2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_POLYNOMIAL_H #define SCIMATH_POLYNOMIAL_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // A one dimensional polynomial class // // // // //
      • Function // // // // A Polynomial contains a set of coefficients; its fundamental operations // is evaluating itself at some "x". The number of coefficients is the order // of the polynomial plus one, so is the number of available parameters. // // // The present implementation merely stores the coefficients in a Block. In the // unlikely case that we need to deal with polynomials with many zero // coefficients, a more efficient representation would be possible. // // // // // // Polynomial pf(3); // Third order polynomial - coeffs 0 by default // pf.setCoefficient(1, 1.0); // pf[2] = 2.0; // pf.setCoefficient(3, 3.0); // 3x^3 + 2x^2 + x // pf(2); // == 34 // // // //
      • T should have standard numerical operators. Current // implementation only tested for real types (and their AutoDiffs). // // //
      • Assertion in debug mode if attempt is made to address incorrect // coefficients // // //
      • Global functions to make various ``special'' polynomials of various // orders will be useful eventually. // template class Polynomial: public PolynomialParam { public: //# Enumerations //# Constructors // Constructs a zero'th order polynomial, with a coeficcient of 0.0. Polynomial() : PolynomialParam() {} // Makes a polynomial of the given order, with all coeficcients set to // zero. explicit Polynomial(uInt order) : PolynomialParam(order) {} // Copy constructor/assignment (deep copy) // Polynomial(const Polynomial &other) : PolynomialParam(other) {} template Polynomial(const Polynomial &other) : PolynomialParam(other) {} Polynomial &operator=(const Polynomial &other) { PolynomialParam::operator=(other); return *this; } // // Destructor virtual ~Polynomial() {} //# Operators // Evaluate the polynomial at x. virtual T eval(typename Function1D::FunctionArg x) const; //# Member functions // Return the polynomial which is the derivative of this one. e.g., // 2+4x+5x^2 --> 0+4+10x . Polynomial derivative() const; // Return a copy of this object from the heap. The caller is responsible for // deleting the pointer. // virtual Function *clone() const { return new Polynomial(*this); } virtual Function::DiffType> *cloneAD() const { return new Polynomial::DiffType>(*this); } virtual Function::BaseType> *cloneNonAD() const { return new Polynomial::BaseType>(*this); } // //# Make members of parent classes known. protected: using PolynomialParam::param_p; public: using PolynomialParam::nparameters; using PolynomialParam::order; }; #define Polynomial_PS Polynomial // Partial specialization of Polynomial for AutoDiff // // // The name Polynomial_PS is only for cxx2html // documentation problems. Use Polynomial in your code. // template class Polynomial_PS > : public PolynomialParam > { public: //# Constructors // Constructs one dimensional Polynomials. // Polynomial_PS() : PolynomialParam >() {} explicit Polynomial_PS(uInt order) : PolynomialParam >(order) {} // // Copy constructor (deep copy) // Polynomial_PS(const Polynomial_PS > &other) : PolynomialParam >(other) {} template Polynomial_PS(const Polynomial_PS &other) : PolynomialParam >(other) {} // // Copy assignment (deep copy) Polynomial_PS > & operator=(const Polynomial_PS > &other) { PolynomialParam >::operator=(other); return *this; } // Destructor virtual ~Polynomial_PS() {} //# Operators // Evaluate the polynomial and its derivatives at x wrt // to the coefficients. // virtual AutoDiff eval(typename Function >::FunctionArg x) const; // //# Member functions // Return a copy of this object from the heap. The caller is responsible // for deleting this pointer. // virtual Function > *clone() const { return new Polynomial >(*this); } virtual Function >::DiffType> *cloneAD() const { return new Polynomial >::DiffType> (*this); } virtual Function >::BaseType> *cloneNonAD() const { return new Polynomial >::BaseType> (*this); } // //# Make members of parent classes known. protected: using PolynomialParam >::param_p; public: using PolynomialParam >::nparameters; using PolynomialParam >::order; }; #undef Polynomial_PS } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/Polynomial.tcc000066400000000000000000000037201476623553700220610ustar00rootroot00000000000000//# Polynomial.cc: A one dimensional polynomial class //# Copyright (C) 1994,1995,1996,1998,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_POLYNOMIAL_TCC #define SCIMATH_POLYNOMIAL_TCC //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Operators template T Polynomial::eval(typename Function1D::FunctionArg x) const { Int j = nparameters(); T accum = param_p[--j]; while (--j >= 0) { accum *= x[0]; accum += param_p[j]; } return accum; } template Polynomial Polynomial::derivative() const { Int ord = order() - 1; if (ord < 0) return Polynomial(0); Polynomial result(ord); for (uInt i=1; i <= order(); ++i) result[i-1] = T(i)*(*this)[i]; return result; } //# Member functions } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/Polynomial2.tcc000066400000000000000000000044121476623553700221420ustar00rootroot00000000000000//# Polynomial2.cc: One dimensional polynomial class specialized for AutoDiff //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_POLYNOMIAL2_TCC #define SCIMATH_POLYNOMIAL2_TCC //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Operators template AutoDiff Polynomial >:: eval(typename Function >::FunctionArg x) const { AutoDiff tmp; for (uInt i=0; inparameters(); ++i) { if (this->param_p[i].nDerivatives() > 0) { tmp = this->param_p[i]; break; } } // function value Int j = this->nparameters(); tmp.value() = this->param_p[--j].value(); while (--j >= 0) { tmp.value() *= x[0]; tmp.value() += this->param_p[j].value(); } // get derivatives (assuming either all or none) if (tmp.nDerivatives()>0) { for (uInt j=0; jnparameters(); ++i) { if (this->param_p.mask(i)) tmp.deriv(i) = dev; dev *= x[0]; } } return tmp; } //# Member functions } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/PolynomialParam.h000066400000000000000000000126671476623553700225320ustar00rootroot00000000000000//# PolynomialParam.h: Parameter handling for one-dimensional polynomials //# Copyright (C) 2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_POLYNOMIALPARAM_H #define SCIMATH_POLYNOMIALPARAM_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Parameter handling for one-dimensional polynomials // // // // //
      • FunctionParam class //
      • Function1D // // // A 1-dimensional Polynomial's parameters. // // // // A Polynomial is described by a set of coefficients; // its fundamental operation is evaluating itself at some "x". // The number of coefficients is the order of the polynomial plus one. // // Since the Polynomial is a Function, the derivatives // can be obtained as well. // // The parameter interface (see // FunctionParam class), // is used to provide an interface to the // Fitting classes. // // This class is in general used implicitly by the Polynomial // class only. // // // // // Polynomial pf(3); // Third order polynomial - coeffs 0 by default // pf.setCoefficient(1, 1.0); // pf[2] = 2.0; // pf.setCoefficient(3, 3.0); // 3x^3 + 2x^2 + x // pf(2); // == 34 // // // //
      • T should have standard numerical operators. Current // implementation only tested for real types (and their AutoDiffs). // // //
      • Assertion in debug mode if attempt is made to address incorrect // coefficients // // //
      • Global functions to make various ``special'' polynomials of various // orders will be useful eventually. // template class PolynomialParam: public Function1D { public: //# Constructors // Constructs a zero'th order polynomial, with a coeficcient of 0.0. PolynomialParam(); // Makes a polynomial of the given order, with all coeficcients set to // zero. explicit PolynomialParam(uInt order); // Make this a copy of other (deep copy). // PolynomialParam(const PolynomialParam &other); template PolynomialParam(const PolynomialParam &other) : Function1D(other) {} PolynomialParam &operator=(const PolynomialParam &other); // // Destructor ~PolynomialParam(); //# Operators // Comparisons. // Polynomials are equal if they are the same order // Bool operator==(const PolynomialParam &other) const { return (param_p == other.param_p); } Bool operator!=(const PolynomialParam &other) const { return (param_p != other.param_p); } // //# Member functions // Give name of function virtual const String &name() const { static String x("polynomial"); return x; } // What is the order of the polynomial, i.e. maximum exponent of "x". uInt order() const { return param_p.nelements() - 1; } // What is the which'th coefficient of the polynomial. For an nth // degree polynomial, which varies between zero and n. T coefficient(uInt which) const { DebugAssert(which<=order(), AipsError); return param_p[which]; } // Return all the coefficients as a vector. const Vector &coefficients() const; // Set the which'th coefficient to value. void setCoefficient(uInt which, const T value) { DebugAssert(which<=order(), AipsError); param_p[which] = value; } // Set all the coefficients at once, throw away all existing coefficients. void setCoefficients(const Vector &coefficients); //# Make members of parent classes known. protected: using Function1D::param_p; public: using Function1D::nparameters; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/PolynomialParam.tcc000066400000000000000000000044071476623553700230450ustar00rootroot00000000000000//# PolynomialParam.cc: Parameter handling for one-dimensional polynomials //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_POLYNOMIALPARAM_TCC #define SCIMATH_POLYNOMIALPARAM_TCC //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template PolynomialParam::PolynomialParam() : Function1D(1) {} template PolynomialParam::PolynomialParam(uInt order) : Function1D(order+1) {} template PolynomialParam::PolynomialParam(const PolynomialParam &other) : Function1D(other) {} template PolynomialParam::~PolynomialParam() {} template PolynomialParam & PolynomialParam::operator=(const PolynomialParam &other) { if (this != &other) Function1D::operator=(other); return *this; } template const Vector &PolynomialParam::coefficients() const { return param_p.getParameters(); } template void PolynomialParam::setCoefficients(const Vector &coefficients) { param_p.setParameters(coefficients); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/PowerLogarithmicPolynomial.h000066400000000000000000000170151476623553700247410ustar00rootroot00000000000000//# Polynomial.h: A one dimensional polynomial class //# Copyright (C) 1994,1995,1996,2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_POWERLOGARITHMICPOLYNOMIAL_H #define SCIMATH_POWERLOGARITHMICPOLYNOMIAL_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // A one dimensional power logarithmic polynomial class of form // y = c_0 * x**( c_1 + c_2*ln(x) + c_3*ln(x)**2 + ... c_n*ln(x)**(n-1)) // // // // //
      • Function // // // // A Power Logarithmic Polynomial contains a set of coefficients; its fundamental operations // is evaluating itself at some "x". // // // The present implementation merely stores the coefficients in a Block. In the // unlikely case that we need to deal with polynomials with many zero // coefficients, a more efficient representation would be possible. // // // // // // // // //
      • T should have standard numerical operators. Current // implementation only tested for real types (and their AutoDiffs). // // //
      • Assertion in debug mode if attempt is made to address incorrect // coefficients // template class PowerLogarithmicPolynomial: public PowerLogarithmicPolynomialParam { public: // Constructs an empty PowerLogarithmicPolynomial PowerLogarithmicPolynomial() : PowerLogarithmicPolynomialParam() {} // Makes a power logaritmic polynomial with the specified number of coefficients, all set to // zero. explicit PowerLogarithmicPolynomial(uInt n) : PowerLogarithmicPolynomialParam(n) {} // Make a function with the specified params. PowerLogarithmicPolynomial(const vector& parms) : PowerLogarithmicPolynomialParam(parms) {} // Copy constructor/assignment (deep copy) // PowerLogarithmicPolynomial(const PowerLogarithmicPolynomial &other) : PowerLogarithmicPolynomialParam(other) {} template PowerLogarithmicPolynomial(const PowerLogarithmicPolynomial &other) : PowerLogarithmicPolynomialParam(other) {} PowerLogarithmicPolynomial &operator=(const PowerLogarithmicPolynomial &other) { PowerLogarithmicPolynomialParam::operator=(other); return *this; } // // Destructor virtual ~PowerLogarithmicPolynomial() {} //# Operators // Evaluate the polynomial at x. virtual T eval(typename Function1D::FunctionArg x) const; // Return a copy of this object from the heap. The caller is responsible for // deleting the pointer. // virtual Function *clone() const { return new PowerLogarithmicPolynomial(*this); } virtual Function::DiffType> *cloneAD() const { return new PowerLogarithmicPolynomial::DiffType>(*this); } virtual Function::BaseType> *cloneNonAD() const { return new PowerLogarithmicPolynomial::BaseType>(*this); } // //# Make members of parent classes known. protected: using PowerLogarithmicPolynomialParam::param_p; public: using PowerLogarithmicPolynomialParam::nparameters; }; #define PowerLogarithmicPolynomial_PS PowerLogarithmicPolynomial // Partial specialization of PowerLogarithmicPolynomial for AutoDiff // // // The name PowerLogarithmicPolynomial_PS is only for cxx2html // documentation problems. Use PowerLogarithmicPolynomial in your code. // template class PowerLogarithmicPolynomial_PS > : public PowerLogarithmicPolynomialParam > { public: //# Constructors // Constructs one dimensional Polynomials. // PowerLogarithmicPolynomial_PS() : PowerLogarithmicPolynomialParam >() {} explicit PowerLogarithmicPolynomial_PS(uInt n) : PowerLogarithmicPolynomialParam >(n) {} // // Copy constructor (deep copy) // PowerLogarithmicPolynomial_PS(const PowerLogarithmicPolynomial_PS > &other) : PowerLogarithmicPolynomialParam >(other) {} template PowerLogarithmicPolynomial_PS(const PowerLogarithmicPolynomial_PS &other) : PowerLogarithmicPolynomialParam >(other) {} // // Copy assignment (deep copy) PowerLogarithmicPolynomial_PS > & operator=(const PowerLogarithmicPolynomial_PS > &other) { PowerLogarithmicPolynomialParam >::operator=(other); return *this; } // Destructor virtual ~PowerLogarithmicPolynomial_PS() {} //# Operators // Evaluate the function and its derivatives at x wrt // to the coefficients. // virtual AutoDiff eval(typename Function >::FunctionArg x) const; // //# Member functions // Return a copy of this object from the heap. The caller is responsible // for deleting this pointer. // virtual Function > *clone() const { return new PowerLogarithmicPolynomial >(*this); } virtual Function >::DiffType> *cloneAD() const { return new PowerLogarithmicPolynomial >::DiffType> (*this); } virtual Function >::BaseType> *cloneNonAD() const { return new PowerLogarithmicPolynomial >::BaseType> (*this); } // //# Make members of parent classes known. protected: using PowerLogarithmicPolynomialParam >::param_p; public: using PowerLogarithmicPolynomialParam >::nparameters; }; #undef PowerLogarithmicPolynomial_PS } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/PowerLogarithmicPolynomial.tcc000066400000000000000000000040351476623553700252610ustar00rootroot00000000000000//# Polynomial.cc: A one dimensional polynomial class //# Copyright (C) 1994,1995,1996,1998,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_POWERLOGARITHMICPOLYNOMIAL_TCC #define SCIMATH_POWERLOGARITHMICPOLYNOMIAL_TCC //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Operators template T PowerLogarithmicPolynomial::eval(typename Function1D::FunctionArg x) const { // Test below outcommented, because pointer can never be <0. // Test on x[0]<=0 gives compile error if T is AutoDiffA. ///if (x <= 0) { /// throw AipsError("PowerLogarithmicPolynomial::eval(): x must be greater than zero"); /// } T lnx = log(x[0]); Int j = nparameters(); T accum = param_p[--j]; while (--j >= 1) { accum *= lnx; accum += param_p[j]; } return param_p[0]*pow(x[0], accum); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/PowerLogarithmicPolynomial2.tcc000066400000000000000000000047301476623553700253450ustar00rootroot00000000000000//# Polynomial2.cc: One dimensional polynomial class specialized for AutoDiff //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_POWERLOGARITHMICPOLYNOMIAL2_TCC #define SCIMATH_POWERLOGARITHMICPOLYNOMIAL2_TCC //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Operators template AutoDiff PowerLogarithmicPolynomial >:: eval(typename Function >::FunctionArg x) const { AutoDiff tmp; for (uInt i=0; inparameters(); ++i) { if (this->param_p[i].nDerivatives() > 0) { tmp = this->param_p[i]; break; } } // function value T lnx = log(x[0]); Int j = nparameters(); T accum = this->param_p[--j].value(); while (--j >= 1) { accum *= lnx; accum += param_p[j].value(); } T value = param_p[0].value()*pow(x[0], accum); tmp.value() = value; // get derivatives Double prod = value; if (tmp.nDerivatives() > 0) { for (uInt j=0; jnparameters(); ++i) { if (i == 0 && this->param_p.mask(0)) { tmp.deriv(0) = value/this->param_p[0].value(); } else { prod *= lnx; if (this->param_p.mask(i)) { tmp.deriv(i) = prod; } } } } return tmp; } //# Member functions } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/PowerLogarithmicPolynomialParam.h000066400000000000000000000124241476623553700257210ustar00rootroot00000000000000//# PolynomialParam.h: Parameter handling for one-dimensional polynomials //# Copyright (C) 2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_POWERLOGARITHMICPOLYNOMIALPARAM_H #define SCIMATH_POWERLOGARITHMICPOLYNOMIALPARAM_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Parameter handling for one-dimensional power logarithmic polynomials // // // // //
      • FunctionParam class //
      • Function1D // // // A 1-dimensional power logaritmic olynomial's parameters. // // // // A power logarithmic polynomial is described by a set of coefficients; // its fundamental operation is evaluating itself at some "x". // // Since the power logarithmic olynomial is a Function, the derivatives // can be obtained as well. // // The parameter interface (see // FunctionParam class), // is used to provide an interface to the // Fitting classes. // // This class is in general used implicitly by the PowerLogarithmicPolynomial // class only. // // // // // // // //
      • T should have standard numerical operators. Current // implementation only tested for real types (and their AutoDiffs). // // //
      • Assertion in debug mode if attempt is made to address incorrect // coefficients // template class PowerLogarithmicPolynomialParam: public Function1D { public: //# Constructors // Constructs a function with two coefficients, both 1 (so y = x). PowerLogarithmicPolynomialParam(); // Makes a polynomial of the specified number of coefficients, all set to zero. explicit PowerLogarithmicPolynomialParam(uInt n); PowerLogarithmicPolynomialParam(const vector& parms); // Make this a copy of other (deep copy). // PowerLogarithmicPolynomialParam(const PowerLogarithmicPolynomialParam &other); template PowerLogarithmicPolynomialParam(const PowerLogarithmicPolynomialParam &other) : Function1D(other) {} PowerLogarithmicPolynomialParam &operator=(const PowerLogarithmicPolynomialParam &other); // // Destructor virtual ~PowerLogarithmicPolynomialParam(); //# Operators // Comparisons. // Bool operator==(const PowerLogarithmicPolynomialParam &other) const { return (param_p == other.param_p); } Bool operator!=(const PowerLogarithmicPolynomialParam &other) const { return (param_p != other.param_p); } // //# Member functions // Give name of function virtual const String &name() const { static String x("power logarithmic polynomial"); return x; } // What is the which'th coefficient of the polynomial. For an nth // degree polynomial, which varies between zero and n. T coefficient(uInt which) const { DebugAssert(which<=nparameters(), AipsError); return param_p[which]; } // Return all the coefficients as a vector. const Vector &coefficients() const; // Set the which'th coefficient to value. void setCoefficient(uInt which, const T value) { DebugAssert(which<=nparameters(), AipsError); param_p[which] = value; } // Set all the coefficients at once, throw away all existing coefficients. void setCoefficients(const Vector &coefficients); //# Make members of parent classes known. protected: using Function1D::param_p; public: using Function1D::nparameters; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/PowerLogarithmicPolynomialParam.tcc000066400000000000000000000062771476623553700262540ustar00rootroot00000000000000//# PolynomialParam.cc: Parameter handling for one-dimensional polynomials //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_POWERLOGARITHMICPOLYNOMIALPARAM_TCC #define SCIMATH_POWERLOGARITHMICPOLYNOMIALPARAM_TCC //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template PowerLogarithmicPolynomialParam::PowerLogarithmicPolynomialParam() : Function1D(2) { setCoefficient(0, 1); setCoefficient(1, 1); } template PowerLogarithmicPolynomialParam::PowerLogarithmicPolynomialParam(uInt n) : Function1D(n) { if (n < 2) { throw AipsError("PowerLogarithmicPolynomialParam constructor: n must be at least 2"); } } template PowerLogarithmicPolynomialParam::PowerLogarithmicPolynomialParam(const vector& parms) : Function1D(Vector(parms)) { if (parms.size() < 2) { throw AipsError("PowerLogarithmicPolynomialParam constructor: n must be at least 2"); } if (parms[0] == 0) { throw AipsError("PowerLogarithmicPolynomialParam constructor: p0 cannot be zero"); } } template PowerLogarithmicPolynomialParam::PowerLogarithmicPolynomialParam(const PowerLogarithmicPolynomialParam &other) : Function1D(other) {} template PowerLogarithmicPolynomialParam::~PowerLogarithmicPolynomialParam() {} template PowerLogarithmicPolynomialParam & PowerLogarithmicPolynomialParam::operator=(const PowerLogarithmicPolynomialParam &other) { if (this != &other) Function1D::operator=(other); return *this; } template const Vector &PowerLogarithmicPolynomialParam::coefficients() const { return param_p.getParameters(); } template void PowerLogarithmicPolynomialParam::setCoefficients(const Vector &coefficients) { if (coefficients.size() < 2) { throw AipsError("PowerLogarithmicPolynomialParam::setCoefficients(): Number of coefficients must be at least 2"); } param_p.setParameters(coefficients); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/SPolynomial.h000066400000000000000000000110031476623553700216530ustar00rootroot00000000000000//# SPolynomial.h: A one dimensional scaled polynomial class //# Copyright (C) 2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_SPOLYNOMIAL_H #define SCIMATH_SPOLYNOMIAL_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // A one dimensional scaled polynomial class // // // // //
      • Function // // // // A SPolynomial contains a set of coefficients; its fundamental operations // is evaluating itself at some "x". The number of coefficients is the order // of the polynomial plus one, plus an additional 3 as height, center, width. // // // // // // SPolynomial pf(3); // Third order polynomial - coeffs 0 by default // pf.setCoefficient(1, 1.0); // pf[5] = 2.0; // pf.setCoefficient(3, 3.0); // 3x^3 + 2x^2 + x // pf(2); // == 34 // // // //
      • T should have standard numerical operators. Current // implementation only tested for real types (and their AutoDiffs). // // //
      • Assertion in debug mode if attempt is made to address incorrect // coefficients // // //
      • Nothing I know of // template class SPolynomial: public SPolynomialParam { public: //# Enumerations //# Constructors // Constructs a zero'th order polynomial, with a coeficcient of 0.0. SPolynomial() : SPolynomialParam() {} // Makes a polynomial of the given order, with all coeficcients set to // zero, and height, center, width to 1,0,1. explicit SPolynomial(uInt order) : SPolynomialParam(order) {} // Copy constructor/assignment (deep copy) // SPolynomial(const SPolynomial &other) : SPolynomialParam(other) {} template SPolynomial(const SPolynomial &other) : SPolynomialParam(other) {} SPolynomial &operator=(const SPolynomial &other) { SPolynomialParam::operator=(other); return *this; } // // Destructor virtual ~SPolynomial() {} //# Operators // Evaluate the polynomial at x. virtual T eval(typename Function::FunctionArg x) const; //# Member functions // Return a copy of this object from the heap. The caller is responsible for // deleting the pointer. // virtual Function *clone() const { return new SPolynomial(*this); } virtual Function::DiffType> *cloneAD() const { return new SPolynomial::DiffType>(*this); } virtual Function::BaseType> *cloneNonAD() const { return new SPolynomial::BaseType>(*this); } // //# Make members of parent classes known. protected: using SPolynomialParam::param_p; public: using SPolynomialParam::nparameters; using SPolynomialParam::mask; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/SPolynomial.tcc000066400000000000000000000033531476623553700222060ustar00rootroot00000000000000//# SPolynomial.cc: A one dimensional scaled polynomial class //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_SPOLYNOMIAL_TCC #define SCIMATH_SPOLYNOMIAL_TCC //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Operators template T SPolynomial::eval(typename Function::FunctionArg x) const { Int j = nparameters(); T accum = param_p[--j]; while (--j >= 3) { accum *= (x[0]-param_p[1])/param_p[2]; accum += param_p[j]; } return param_p[0]*accum; } //# Member functions } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/SPolynomialParam.h000066400000000000000000000131041476623553700226400ustar00rootroot00000000000000//# SPolynomialParam.h: Parameter handling for scaled 1-D polynomials //# Copyright (C) 2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_SPOLYNOMIALPARAM_H #define SCIMATH_SPOLYNOMIALPARAM_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Parameter handling for scaled 1-D polynomials // // // // //
      • FunctionParam class //
      • Function // // // A 1-dimensional Scaled Polynomial's parameters. // // // // A SPolynomial is described by a set of coefficients; // its fundamental operation is evaluating itself at some "x". // The number of coefficients is the order of the polynomial plus one, // plus three, describing a height, center and width. These three parameters // are the first three, and have default values of 1, 0, 1. // // Since the SPolynomial is a Function, the derivatives // can be obtained as well. // // The parameter interface (see // FunctionParam class), // is used to provide an interface to the // Fitting classes. // // This class is in general used implicitly by the SPolynomial // class only. // // // // // SPolynomial pf(3); // Third order polynomial - coeffs 0 by default // pf.setCoefficient(1, 1.0); // pf[5] = 2.0; // pf.setCoefficient(3, 3.0); // 3x^3 + 2x^2 + x // pf(2); // == 34 // // // //
      • T should have standard numerical operators. Current // implementation only tested for real types (and their AutoDiffs). // // //
      • Assertion in debug mode if attempt is made to address incorrect // coefficients // // //
      • Nothing I know of // template class SPolynomialParam: public Function { public: //# Constructors // Constructs a zero'th order polynomial, with a coeficcient of 0.0. SPolynomialParam(); // Makes a polynomial of the given order, with all coeficcients set to // zero. explicit SPolynomialParam(uInt order); // Make this a copy of other (deep copy). // SPolynomialParam(const SPolynomialParam &other); template SPolynomialParam(const SPolynomialParam &other) : Function(other) {} SPolynomialParam &operator=(const SPolynomialParam &other); // // Destructor ~SPolynomialParam(); //# Operators // Comparisons. // SPolynomials are equal if they are of the same order // Bool operator==(const SPolynomialParam &other) const { return (param_p == other.param_p); } Bool operator!=(const SPolynomialParam &other) const { return (param_p != other.param_p); } // //# Member functions // Give name of function virtual const String &name() const { static String x("spolynomial"); return x; } // What is the order of the polynomial, i.e. maximum exponent of "x". uInt order() const { return param_p.nelements() - 4; } // What is the which'th coefficient of the polynomial. For an nth // degree polynomial, which varies between zero and n. T coefficient(uInt which) const { DebugAssert(which<=order(), AipsError); return param_p[which+3]; } // Return all the coefficients as a vector. Vector coefficients() const; // Set the which'th coefficient to value. void setCoefficient(uInt which, const T value) { DebugAssert(which<=order(), AipsError); param_p[which+3] = value; } // Set all the coefficients at once, throw away all existing coefficients. void setCoefficients(const Vector &coefficients); // Returns the dimension of function virtual uInt ndim() const { return 1; } //# Make members of parent classes known. protected: using Function::param_p; public: using Function::nparameters; using Function::mask; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/SPolynomialParam.tcc000066400000000000000000000050101476623553700231570ustar00rootroot00000000000000//# SPolynomialParam.cc: Parameter handling for scaled 1-D polynomials //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_SPOLYNOMIALPARAM_TCC #define SCIMATH_SPOLYNOMIALPARAM_TCC //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template SPolynomialParam::SPolynomialParam() : Function(4) { param_p[0] = 1; param_p[2] = 1; for (uInt i=0; i<3; ++i) mask(i) = False; } template SPolynomialParam::SPolynomialParam(uInt order) : Function(order+4) { param_p[0] = 1; param_p[2] = 1; for (uInt i=0; i<3; ++i) mask(i) = False; } template SPolynomialParam::SPolynomialParam(const SPolynomialParam &other) : Function(other) {} template SPolynomialParam::~SPolynomialParam() {} template SPolynomialParam & SPolynomialParam::operator=(const SPolynomialParam &other) { if (this != &other) Function::operator=(other); return *this; } template Vector SPolynomialParam::coefficients() const { Vector tmp(order()+1); for (uInt i=3; i void SPolynomialParam::setCoefficients(const Vector &coefficients) { for (uInt i=3; i #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // A base class for indexing into arbitrary data types // // // //
      • Functional // // // A Functional is simply a mapping from a Domain type to a Range // type. Experimental data is usually sampled, and is can be represented as // a mapping from the unsigned integers to an arbitrary Domain. // // // This abstract class defines an interface for functions that map from the // unsigned integers to an arbitrary type. It defines two functions: the // operator() function which it inherits from the Functional class, and the // nelements function which is necessary to know how many data elements. // // This class is useful for freeing the writer of other classes from having // to know how how a linear data set is stored or represented. For example, // four floating point numbers will probably be stored as a Vector, // and kept in memory for fast access. But 400 million floating point // numbers cannot usually be kept in memory, and may be stored on disk as a // Table. By using a SampledFunctional writers of other classes // (Interpolate1D is an example), can ignore these details if all they are // interested in is random access to individual elements of the data. // // // Because this is an abstract class the example will be inside a function // // T sum(SampledFunctional data) // { // T result = 0; // for (uInt i = 0; i < data.nelements(); i++) // result += data(i); // return result; // } // // // // If all you need to do is random access indexing into arbitrary data sets // this class provides a suitable abstraction of that functionality. // // //
      • Templating restrictions will depend on the actual derived class that is // used. // // //
      • Exceptions will depend on derived classes and the templating // arguements. This abstract class only defines an interface and does not // throw any exceptions. // // //
      • I cannot think of anything // template class SampledFunctional: public Functional { public: // Access the specified element of the data virtual Range operator()(const uInt &index) const = 0; // Return the total size of the data set. virtual uInt nelements() const = 0; // The virtual destructor does nothing virtual ~SampledFunctional(){} }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/ScalarSampledFunctional.h000066400000000000000000000134411476623553700241530ustar00rootroot00000000000000//# ScalarSampledFunctional.h: //# Copyright (C) 1996 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_SCALARSAMPLEDFUNCTIONAL_H #define SCIMATH_SCALARSAMPLEDFUNCTIONAL_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template class Block; // A unified interface for indexing into Vectors or Blocks // // // //
      • SampledFunctional //
      • Vector //
      • Block // // // A SampledFunctional is an interface that allows random access to a fixed // size data set. I originally conceived this class as being used to access // scalar values (Int's Float's etc.) stored in Vectors, using the // SampledFunctional interface. It became generalised to incorporate Blocks // and I now realise that a better name might be MemorySampledFunctional, to // highlight that the data is stored in memory (and not on disk). // // // This derived class allows allows a Block or Vector object to be // accessed through the SampledFunctional interface. The principle // advantage of this is that it unifies the different indexing operators // (ie. [] for Blocks and () for Vectors). The disadvantage is that it hides // just about all the other functionality of Vectors and Blocks. If all you // are interested in is random access to various elements of these objects // then this class is a suitable abstraction. // Reference semantics are used (ie. the class does not make a copy of the // data but refers to the original data) whenever possible. It is not // possible to use reference semantics (so a physical copy of the data is // made), in the following cases: //
          //
        • When constructing the class from a Block //
        • When constructing the class from a const Vector //
        // Reference semantics are always used for the copy constructor and // assignment operators when the ScalarSampledFunctional is // non-const. Otherwise copy semantics are used. // When reference semantics are used you need to be aware that modifying the // contents of the original Vector will modify the data used by this class. // This class is always more efficient if reference semantics are used, so // avoid using const arguments unless you really need to. //
        // // Constructing and using ScalarSampledFunctional's // // Block b(10); // Create a block of ten elements // // ... Fill the block any way you like ... // ScalarSampledFunctional fb(b); // for(uInt i = 0; i < 10; i++) // cout << "f(" << i << ") = " << fb(i) << endl; // // // // The SampledFunctional is a useful interface. But it needs some concrete // classes to back it up. This is the first one that was written. // // //
      • Very few assumptions are made on the templating type. So this class // should work for a wide variety of templates types. // // //
      • Exceptions are not thrown directly by this class. // // //
      • Nothing I can think of // template class ScalarSampledFunctional :public SampledFunctional { public: // See the description above to determine whether a copy or a reference is // made to the original data. // ScalarSampledFunctional(); ScalarSampledFunctional(Vector & data); ScalarSampledFunctional(const Vector & data); ScalarSampledFunctional(const Block & data); // // The standard copy constructor and assignment operator. These functions // use reference semantics when the ScalarSampledFunctional is // non-const, and copy semantics otherwise. // ScalarSampledFunctional(ScalarSampledFunctional & other); ScalarSampledFunctional(const ScalarSampledFunctional & other); ScalarSampledFunctional & operator=(ScalarSampledFunctional &other); ScalarSampledFunctional & operator=(const ScalarSampledFunctional &other); // // Define the functions for the SampledFunctional interface // virtual T operator()(const uInt &index) const; virtual uInt nelements() const; virtual ~ScalarSampledFunctional(); // private: Vector refData; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/ScalarSampledFunctional.tcc000066400000000000000000000056741476623553700245060ustar00rootroot00000000000000//# ScalarSampledFunctional.cc //# Copyright (C) 1996 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_SCALARSAMPLEDFUNCTIONAL_TCC #define SCIMATH_SCALARSAMPLEDFUNCTIONAL_TCC #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template ScalarSampledFunctional:: ScalarSampledFunctional(){ } template ScalarSampledFunctional:: ScalarSampledFunctional(const Block & data) :refData(data.begin(), data.end()) { } template ScalarSampledFunctional:: ScalarSampledFunctional(Vector & data) :refData(data) { } template ScalarSampledFunctional:: ScalarSampledFunctional(const Vector & data) :refData(data.copy()){ } template ScalarSampledFunctional:: ScalarSampledFunctional(ScalarSampledFunctional & other) : SampledFunctional(other), refData(other.refData){ } template ScalarSampledFunctional:: ScalarSampledFunctional(const ScalarSampledFunctional & other) : SampledFunctional(other), refData(other.refData.copy()){ } template ScalarSampledFunctional & ScalarSampledFunctional:: operator=(ScalarSampledFunctional &other) { if (this != &other){ refData.reference(other.refData); } return *this; } template ScalarSampledFunctional & ScalarSampledFunctional:: operator=(const ScalarSampledFunctional &other) { if (this != &other){ refData = other.refData; } return *this; } template T ScalarSampledFunctional:: operator()(const uInt &index) const { return refData(index); } template uInt ScalarSampledFunctional:: nelements() const { return refData.nelements(); } template ScalarSampledFunctional:: ~ScalarSampledFunctional(){ } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/SerialHelper.cc000066400000000000000000000407241476623553700221360ustar00rootroot00000000000000//# SerialHelper: a helper class for (un)serializing a Function object //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN const String SerialHelper::FUNCTYPE("functype"); const String SerialHelper::gtype[] = { "Bool", "Byte", "Short", "Int", "Float", "Double", "Complex", "DComplex", "String" }; Bool SerialHelper::getFuncType(String& ftype) const { if (! gr.isDefined(FUNCTYPE)) return False; try { ftype = gr.asString(RecordFieldId(FUNCTYPE)); if(!ftype.size() ){ throw InvalidSerializationError("Empty value for functype field"); } } catch (std::exception& x) { throw InvalidSerializationError("Wrong type for functype field"); } return True; } void SerialHelper::checkFuncType(const String& ftype) const { String thistype; if (! getFuncType(thistype)) throw InvalidSerializationError("No functype field defined"); if (ftype != thistype) throw InvalidSerializationError(String("Wrong functype (need ") + ftype + ", found " + thistype); } template <> void getArrayVal(Bool& val, Int, const Record& gr, const String& name, uInt index) { if (! gr.isDefined(name)) throw FieldNotFoundError(name); //std::cerr << name << " "<< gr.dataType(RecordFieldId(name)) << endl; switch(gr.dataType(RecordFieldId(name))){ case TpBool : val = gr.asBool(RecordFieldId(name)); break; case TpArrayBool : { Array tmp = gr.asArrayBool(RecordFieldId(name)); val = tmp(IPosition(tmp.nelements(), index)); } break; default : throw InvalidSerializationError(String("Wrong type for ") + name + " field (need TpBool," + " found record)"); break; } } template <> void getArrayVal(Short& val, Int, const Record& gr, const String& name, uInt index) { if (! gr.isDefined(name)) throw FieldNotFoundError(name); //std::cerr << name << " "<< gr.dataType(RecordFieldId(name)) << endl; switch(gr.dataType(RecordFieldId(name))){ case TpShort : val = gr.asShort(RecordFieldId(name)); break; case TpArrayShort : { Array tmp = gr.asArrayShort(RecordFieldId(name)); val = tmp(IPosition(tmp.nelements(), index)); } break; default : throw InvalidSerializationError(String("Wrong type for ") + name + " field (need TpShort," + " found record)"); break; } } template <> void getArrayVal(Int& val, Int, const Record& gr, const String& name, uInt index) { if (! gr.isDefined(name)) throw FieldNotFoundError(name); //std::cerr << name << " "<< gr.dataType(RecordFieldId(name)) << endl; switch(gr.dataType(RecordFieldId(name))){ case TpInt : val = gr.asInt(RecordFieldId(name)); break; case TpArrayInt : { Array tmp = gr.asArrayInt(RecordFieldId(name)); val = tmp(IPosition(tmp.nelements(), index)); } break; default : throw InvalidSerializationError(String("Wrong type for ") + name + " field (need TpInt," + " found record)"); break; } } template <> void getArrayVal(Float& val, Int, const Record& gr, const String& name, uInt index) { if (! gr.isDefined(name)) throw FieldNotFoundError(name); //std::cerr << name << " "<< gr.dataType(RecordFieldId(name)) << endl; switch(gr.dataType(RecordFieldId(name))){ case TpFloat : val = gr.asFloat(RecordFieldId(name)); break; case TpArrayFloat : { Array tmp = gr.asArrayFloat(RecordFieldId(name)); val = tmp(IPosition(tmp.nelements(), index)); } break; default : throw InvalidSerializationError(String("Wrong type for ") + name + " field (need TpFloat," + " found record)"); break; } } template <> void getArrayVal(Double& val, Int, const Record& gr, const String& name, uInt index) { if (! gr.isDefined(name)) throw FieldNotFoundError(name); //std::cerr << name << " "<< gr.dataType(RecordFieldId(name)) << endl; switch(gr.dataType(RecordFieldId(name))){ case TpDouble : val = gr.asDouble(RecordFieldId(name)); break; case TpArrayDouble : { Array tmp = gr.asArrayDouble(RecordFieldId(name)); val = tmp(IPosition(tmp.nelements(), index)); } break; default : throw InvalidSerializationError(String("Wrong type for ") + name + " field (need TpDouble," + " found record)"); break; } } template <> void getArrayVal(Complex& val, Int, const Record& gr, const String& name, uInt index) { if (! gr.isDefined(name)) throw FieldNotFoundError(name); //std::cerr << name << " "<< gr.dataType(RecordFieldId(name)) << endl; switch(gr.dataType(RecordFieldId(name))){ case TpComplex : val = gr.asComplex(RecordFieldId(name)); break; case TpArrayComplex : { Array tmp = gr.asArrayComplex(RecordFieldId(name)); val = tmp(IPosition(tmp.nelements(), index)); } break; default : throw InvalidSerializationError(String("Wrong type for ") + name + " field (need TpComplex," + " found record)"); break; } } template <> void getArrayVal(DComplex& val, Int, const Record& gr, const String& name, uInt index) { if (! gr.isDefined(name)) throw FieldNotFoundError(name); //std::cerr << name << " "<< gr.dataType(RecordFieldId(name)) << endl; switch(gr.dataType(RecordFieldId(name))){ case TpDComplex : val = gr.asDComplex(RecordFieldId(name)); break; case TpArrayDComplex : { Array tmp = gr.asArrayDComplex(RecordFieldId(name)); val = tmp(IPosition(tmp.nelements(), index)); } break; default : throw InvalidSerializationError(String("Wrong type for ") + name + " field (need TpDComplex," + " found record)"); break; } } template <> void getArrayVal(String& val, Int, const Record& gr, const String& name, uInt index) { if (! gr.isDefined(name)) throw FieldNotFoundError(name); //std::cerr << name << " "<< gr.dataType(RecordFieldId(name)) << endl; switch(gr.dataType(RecordFieldId(name))){ case TpString : val = gr.asString(RecordFieldId(name)); break; case TpArrayString : { Array tmp = gr.asArrayString(RecordFieldId(name)); val = tmp(IPosition(tmp.nelements(), index)); } break; default : throw InvalidSerializationError(String("Wrong type for ") + name + " field (need TpString," + " found record)"); break; } } template <> void getArray(Array& val, Int, const Record& gr, const String& name) { if (! gr.isDefined(name)) throw FieldNotFoundError(name); //std::cerr << name << " "<< gr.dataType(RecordFieldId(name)) << endl; if (gr.dataType(RecordFieldId(name)) != TpArrayBool) throw InvalidSerializationError(String("Wrong type for ") + name + " field (need array bool," + " found record)"); val = gr.asArrayBool(RecordFieldId(name)); } template <> void getArray(Array& val, Int, const Record& gr, const String& name) { if (! gr.isDefined(name)) throw FieldNotFoundError(name); //std::cerr << name << " "<< gr.dataType(RecordFieldId(name)) << endl; if (gr.dataType(RecordFieldId(name)) != TpArrayShort) throw InvalidSerializationError(String("Wrong type for ") + name + " field (need array short," + " found record)"); val = gr.asArrayShort(RecordFieldId(name)); } template <> void getArray(Array& val, Int, const Record& gr, const String& name) { if (! gr.isDefined(name)) throw FieldNotFoundError(name); //std::cerr << name << " "<< gr.dataType(RecordFieldId(name)) << endl; if (gr.dataType(RecordFieldId(name)) != TpArrayInt) throw InvalidSerializationError(String("Wrong type for ") + name + " field (need array int," + " found record)"); val = gr.asArrayInt(RecordFieldId(name)); } template <> void getArray(Array& val, Int, const Record& gr, const String& name) { if (! gr.isDefined(name)) throw FieldNotFoundError(name); //std::cerr << name << " "<< gr.dataType(RecordFieldId(name)) << endl; if (gr.dataType(RecordFieldId(name)) != TpArrayFloat) throw InvalidSerializationError(String("Wrong type for ") + name + " field (need array float," + " found record)"); val = gr.asArrayFloat(RecordFieldId(name)); } template <> void getArray(Array& val, Int, const Record& gr, const String& name) { if (! gr.isDefined(name)) throw FieldNotFoundError(name); //std::cerr << name << " "<< gr.dataType(RecordFieldId(name)) << endl; if (gr.dataType(RecordFieldId(name)) != TpArrayDouble) throw InvalidSerializationError(String("Wrong type for ") + name + " field (need array double," + " found record)"); val = gr.asArrayDouble(RecordFieldId(name)); } template <> void getArray(Array& val, Int, const Record& gr, const String& name) { if (! gr.isDefined(name)) throw FieldNotFoundError(name); //std::cerr << name << " "<< gr.dataType(RecordFieldId(name)) << endl; if (gr.dataType(RecordFieldId(name)) != TpArrayComplex) throw InvalidSerializationError(String("Wrong type for ") + name + " field (need array complex," + " found record)"); val = gr.asArrayComplex(RecordFieldId(name)); } template <> void getArray(Array& val, Int, const Record& gr, const String& name) { if (! gr.isDefined(name)) throw FieldNotFoundError(name); //std::cerr << name << " "<< gr.dataType(RecordFieldId(name)) << endl; if (gr.dataType(RecordFieldId(name)) != TpArrayDComplex) throw InvalidSerializationError(String("Wrong type for ") + name + " field (need array dcomplex," + " found record)"); val = gr.asArrayDComplex(RecordFieldId(name)); } template <> void getArray(Array& val, Int, const Record& gr, const String& name) { if (! gr.isDefined(name)) throw FieldNotFoundError(name); //std::cerr << name << " "<< gr.dataType(RecordFieldId(name)) << endl; if (gr.dataType(RecordFieldId(name)) != TpArrayString) throw InvalidSerializationError(String("Wrong type for ") + name + " field (need array string," + " found record)"); val = gr.asArrayString(RecordFieldId(name)); } void SerialHelper::get(Bool &val, const String& name, uInt index) const { getArrayVal(val, SerialHelper::shtBOOL, gr, name, index); } void SerialHelper::get(String &val, const String& name, uInt index) const { getArrayVal(val, SerialHelper::shtSTRING, gr, name, index); } // void SerialHelper::get(Byte &val, const String& name, uInt index) const // { // getArrayVal(val, Array::BYTE, gr, name, index); // } void SerialHelper::get(Short &val, const String& name, uInt index) const { getArrayVal(val, SerialHelper::shtSHORT, gr, name, index); } void SerialHelper::get(Int &val, const String& name, uInt index) const { getArrayVal(val, SerialHelper::shtINT, gr, name, index); } void SerialHelper::get(Float &val, const String& name, uInt index) const { getArrayVal(val, SerialHelper::shtFLOAT, gr, name, index); } void SerialHelper::get(Double &val, const String& name, uInt index) const { getArrayVal(val, SerialHelper::shtDOUBLE, gr, name, index); } void SerialHelper::get(Complex &val, const String& name, uInt index) const { getArrayVal(val, SerialHelper::shtCOMPLEX, gr, name, index); } void SerialHelper::get(DComplex &val, const String& name, uInt index) const { getArrayVal(val, SerialHelper::shtDCOMPLEX, gr, name, index); } void SerialHelper::get(Array &val, const String& name) const { getArray(val, SerialHelper::shtBOOL, gr, name); } void SerialHelper::get(Array &val, const String& name) const { getArray(val, SerialHelper::shtSHORT, gr, name); } void SerialHelper::get(Array &val, const String& name) const { getArray(val, SerialHelper::shtINT, gr, name); } void SerialHelper::get(Array &val, const String& name) const { getArray(val, SerialHelper::shtFLOAT, gr, name); } void SerialHelper::get(Array &val, const String& name) const { getArray(val, SerialHelper::shtDOUBLE, gr, name); } void SerialHelper::get(Array &val, const String& name) const { getArray(val, SerialHelper::shtCOMPLEX, gr, name); } void SerialHelper::get(Array &val, const String& name) const { getArray(val, SerialHelper::shtDCOMPLEX, gr, name); } void SerialHelper::get(Array &val, const String& name) const { getArray(val, SerialHelper::shtSTRING, gr, name); } void SerialHelper::get(Record &val, const String& name) const { if (! gr.isDefined(name)) throw FieldNotFoundError(name); //std::cerr << name << " "<< gr.dataType(RecordFieldId(name)) << endl; if (gr.dataType(RecordFieldId(name)) != TpRecord) throw InvalidSerializationError(String("Wrong type for ") + name + " field (need record," + " found array)"); val = gr.asRecord(RecordFieldId(name)); } /* template void getArrayVal(V &val, Int gtype, const Record& gr, const String& name, uInt index) { if (! gr.isDefined(name)) throw FieldNotFoundError(name); if (gr.dataType(RecordFieldId(name)) != TpArray) throw InvalidSerializationError(String("Wrong type for ") + name + " field (need array," + " found record)"); Array gv = gr.get(RecordFieldId(name)); if (! gv.get(RecordFieldId(val), index)) throw InvalidSerializationError(String("Wrong type for ") + name + " field (need " + SerialHelper::gtype[gtype] + "found " + SerialHelper::gtype[gv.elementType()]); } */ /* template void getArray(Array &val, Int gtype, const Record& gr, const String& name) { if (! gr.isDefined(name)) throw FieldNotFoundError(name); if (gr.dataType(RecordFieldId(name)) != TpArray) throw InvalidSerializationError(String("Wrong type for ") + name + " field (need array," + " found record)"); Array gv = gr.get(RecordFieldId(name)); if (! gv.get(RecordFieldId(val))) throw InvalidSerializationError(String("Wrong type for ") + name + " field (need " + SerialHelper::gtype[gtype] + "found " + SerialHelper::gtype[gv.elementType()]); } */ } //# NAMESPACE CASACORE - END casacore-3.7.1/scimath/Functionals/SerialHelper.h000066400000000000000000000140061476623553700217720ustar00rootroot00000000000000//# SerialHelper: a helper class for (un)serializing a Function object //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_SERIALHELPER_H #define SCIMATH_SERIALHELPER_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template void getArrayVal(V &val, int type, const Record& gr, const String& name, uInt index=0); template void getArray(Array &val, int type, const Record& gr, const String& name); // // // // // // // // // // // //
      • FunctionFactory // // // // // // // // // // // // // // // // // // // // // // // // // // // //
      • InvalidSerializationError by getFuncType() if Record // does not contain a "functype" field containing a string. //
      • InvalidSerializationError // // // //
      • //
      • //
      • // class SerialHelper { public: static const String FUNCTYPE; static const String gtype[]; enum shType { shtBOOL=0, shtBYTE, shtSHORT, shtINT, shtFLOAT, shtDOUBLE, shtCOMPLEX, shtDCOMPLEX, shtSTRING}; SerialHelper(const Record& record) : gr(record) { } SerialHelper(const SerialHelper& other) { gr = other.gr; } virtual ~SerialHelper() { } // load the function type name as given in the record's "functype" // field into the given String ftype. gr is the // record to extract from. False is returned if the record // does not contain this field. // //
      • InvalidSerializationError if "functype" exists but is // empty or the incorrect type // Bool getFuncType(String& ftype) const; // ensure that the Function type stored in the given record, gr, // matches ftype. If it does not, an // InvalidSerializationError is thrown. void checkFuncType(const String& ftype) const; // return True if a field with the given name exists Bool exists(const String &name) const { return gr.isDefined(name); } // Get the indexth element of the name field // This should be // particularly useful for Array objects with only one element, // i.e. a scalar. // Note that unlike the native classes, indexing is zero-relative. // // InvalidSerializationError is thrown if: //
          //
        • if the given record does not contain a field called name //
        • if the field is not a vector of the correct type. //
        • if the index is out of range. //
        // void get(Bool &val, const String& name, uInt index = 0) const; // void get(uChar &val, const String& name, uInt index = 0) const; void get(Short &val, const String& name, uInt index = 0) const; void get(Int &val, const String& name, uInt index = 0) const; void get(Float &val, const String& name, uInt index = 0) const; void get(Double &val, const String& name, uInt index = 0) const; void get(Complex &val, const String& name, uInt index = 0) const; void get(DComplex &val, const String& name, uInt index = 0) const; void get(String &val, const String& name, uInt index = 0) const; void get(Record &val, const String& name) const; // // Get the indexth element of the name field // This should be // particularly useful for Array objects with only one element, // i.e. a scalar. // Note that unlike the native classes, indexing is zero-relative. // // InvalidSerializationError is thrown if: //
          //
        • if the given record does not contain a field called name //
        • if the field is not a vector of the correct type. //
        • if the index is out of range. //
        // void get(Array &val, const String& name) const; // void get(Array &val, const String& name) const; void get(Array &val, const String& name) const; void get(Array &val, const String& name) const; void get(Array &val, const String& name) const; void get(Array &val, const String& name) const; void get(Array &val, const String& name) const; void get(Array &val, const String& name) const; // SerialHelper& operator=(const SerialHelper& other) { gr = other.gr; return *this; } protected: SerialHelper() { } private: Record gr; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/SimButterworthBandpass.h000066400000000000000000000242351476623553700240760ustar00rootroot00000000000000//# SimButterworthBandpass.h: Declares a Butterworth function //# Copyright (C) 2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_SIMBUTTERWORTHBANDPASS_H #define SCIMATH_SIMBUTTERWORTHBANDPASS_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // a class for evaluating a Butterworth filter transfer function. // // // // // //
      • FunctionParam class //
      • Function1D // // // // "Butterworth" refers to the Butterworth function for describing // filter transfer functions (Butterworth, S, "On the theory of filter // amplifiers," Wireless Engineer, vol. 7 pp. 536-541, October 1930). // "Bandpass" reflects that the transfer function is has both low and high // frequency cutoffs. // "Sim" indicates that this implementation is not necessarily appropriate // characterizing real bandpass filters; in the future, there may be a // more general class called simply "Butterworth". // // // // This function class simulates the (amplitude) transfer function for a // wideband bandpass filter constructed from the combination of a low-pass // and a high-pass Butterworth filter. // // In analog electronic filter design, a Butterworth low-pass filter is // one in which the amplitude transfer function, |H(jw)| (where j = sqrt(-1) // and w is the angular frequency), is given by: // // |H(jw)| = 1 / sqrt(1 + (w/w_c)^(2*n)) // // where n refers to the filter "order" and w_c is the "cutoff frequency". // When w = w_c, the filter output is 1/sqrt(2) that of the input, and the // higher the order, the steeper the drop off towards zero and the closer // the approximation to a idealized step function. // // Filter theory provides transformations for deriving transfer functions // of high-pass and band-pass filters which reflect how the electrical // circuits actually work. However, to simplify this class's implementation // and to make the transfer function behavior more predictable by the naive // user, THIS CLASS DOES NOT ACTUALLY USE THE PROPER TRANSFORMATIONS (see // Etymology section above). // Instead, the Butterworth bandpass transfer function is approximated by // low pass component, given above, combined with a pseudo high-pass function // that is of the same form but with w substituted with -w. Both components // are shifted such that its peak transfer point is at a given "center" // position. The cutoff value and order can be set independently for both // ends of the passband. // // // // // // Create a bandpass function centered on x=0.8 and cutoffs at 0 and 2.5. // // The orders of the drop-offs will 4 at the low end and 5 at the high // // end. The peak will by 1.0 by default. // SimButterworthBandpass butt(4, 5, 0, 2.5, 0.8); // // Double z = butt(1); // z = 1.0 // z = butt(0); // z = 1/sqrt(2) // z = butt(2.5); // z = 1/sqrt(2) // z = butt(-25); // z ~ 9.24e-9 ~ 0 // // // change the low-end cutoff to -25.0 // butt.setMinCutoff(-25); // z = butt(-25); // z = 1/sqrt(2) // // // // // This class was created to simulate systemtic Butterworth bandpasses // within the simulator tool. It can used by the SimBJones class to vary the // bandpass in a predictable way. However, it has limited value for real // filter analysis, and it is not expected to be a realistic representation // of real bandpass filters in use with radio telescopes backends. // // // //
      • T should have standard numerical operators. Current // implementation only tested for real types (and their AutoDiffs). // // // //
      • Assertion if indices out-of-range // // // //
      • Nothing I know of // template class SimButterworthBandpass : public Function1D { public: //# Enumerations // Enumeration of the function parameters enum { CENTER, MINCUTOFF, MAXCUTOFF, PEAK }; //# Constructors // create a zero-th order (all-pass) Butterworth bandpass function. SimButterworthBandpass(); // create a Butterworth bandpass function. SimButterworthBandpass(const uInt minord, const uInt maxord, const T &mincut=T(-1), const T &maxcut=T(1), const T ¢er=T(0), const T &peak=T(1)); // create a fully specified Butterworth bandpass in which the // low and high pass orders are stored in a Record explicit SimButterworthBandpass(const RecordInterface& gr, T mincut=T(-1), T maxcut=T(1), T center=T(0), T peak=T(1)); // create a copy of another Butterworth bandpass function SimButterworthBandpass(const SimButterworthBandpass &other); // copy(deep) another Butterworth function SimButterworthBandpass & operator=(const SimButterworthBandpass &other); // Destructor virtual ~SimButterworthBandpass(); //# Operators // Evaluate the bandpass at "x". virtual T eval(const typename FunctionTraits::ArgType *x) const; //# Member functions // set the center of the bandpass. This is the x-ordinate value that // evaluates to the peak of the function. void setCenter(const T &x) { param_p[CENTER] = x; } // return the center of the bandpass. This is the x-ordinate value that // evaluates to the peak of the function. const T &getCenter() const { return param_p[CENTER]; } // set the characteristic minimum (high-pass) cutoff value. At this // x-ordinate value, the function has a value reduced 30 dB from its // peak. void setMinCutoff(const T &x) { param_p[MINCUTOFF] = x; } // set the characteristic maximum (low-pass) cutoff value. At this // x-ordinate value, the function has a value reduced 30 dB from its // peak. void setMaxCutoff(const T &x) { param_p[MAXCUTOFF] = x; } // set the order of the Butterworth function for the minimum (high-pass) // portion of the bandpass void setMinOrder(uInt order) { nl_p = order; } // set the order of the Butterworth function for the maximum (low-pass) // portion of the bandpass void setMaxOrder(uInt order) { nh_p = order; } // return the characteristic minimum (high-pass) cutoff value. At this // x-ordinate value, the function has a value reduced 30 dB from its // peak. const T &getMinCutoff() const { return param_p[MINCUTOFF]; } // return the characteristic maximum (low-pass) cutoff value. At this // x-ordinate value, the function has a value reduced 30 dB from its // peak. const T &getMaxCutoff() const { return param_p[MAXCUTOFF]; } // return the order of the Butterworth function for the minimum (high-pass) // portion of the bandpass uInt getMinOrder() const { return nl_p; } // return the order of the Butterworth function for the maximum (low-pass) // portion of the bandpass uInt getMaxOrder() const { return nh_p; } // set the scale of the function by setting its peak value. By default, // the peak value is T(1); void setPeak(T val) { param_p[PEAK] = val; } // return the scale of the function const T &getPeak() const { return param_p[PEAK]; } // get/set the function mode. This is an alternate way to get/set the // non-coefficient data for this function. The supported record fields // are as follows: //
            // Field Name     Type            Role
            // -------------------------------------------------------------------
            // minOrder   TpInt   the order of the Butterworth function for the 
            //                    minimum (high-pass) portion of the bandpass
            // maxOrder   TpInt   the order of the Butterworth function for the 
            //                    maximum (low-pass) portion of the bandpass
            // An exception is thrown if either value is less than zero
            // 
        // virtual void setMode(const RecordInterface& mode); virtual void getMode(RecordInterface& mode) const; // // return True if the implementing function supports a mode. This // implementation always returns True. virtual Bool hasMode() const; // clone this function virtual Function *clone() const { return new SimButterworthBandpass(*this); } private: //# Non-parameter Data // Minimum order uInt nl_p; // Maximum order uInt nh_p; //# Make members of parent classes known. protected: using Function::param_p; public: using Function::nparameters; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/SimButterworthBandpass.tcc000066400000000000000000000113351476623553700244150ustar00rootroot00000000000000//# SimButterworthBandpass.cc: Defines a Butterworth function //# Copyright (C) 2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_SIMBUTTERWORTHBANDPASS_TCC #define SCIMATH_SIMBUTTERWORTHBANDPASS_TCC //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors template SimButterworthBandpass::SimButterworthBandpass() : Function1D(4), nl_p(0), nh_p(0) { param_p[MINCUTOFF] = T(-1); param_p[MAXCUTOFF] = T(1); param_p[PEAK] = T(1); } template SimButterworthBandpass::SimButterworthBandpass(const uInt minord, const uInt maxord, const T &mincut, const T &maxcut, const T ¢er, const T &peak) : Function1D(4), nl_p(minord), nh_p(maxord) { param_p[MINCUTOFF] = mincut; param_p[MAXCUTOFF] = maxcut; param_p[CENTER] = center; param_p[PEAK] = peak; } template SimButterworthBandpass:: SimButterworthBandpass(const RecordInterface& gr, T mincut, T maxcut, T center, T peak) : Function1D(4), nl_p(0), nh_p(0) { setMode(gr); param_p[MINCUTOFF] = mincut; param_p[MAXCUTOFF] = maxcut; param_p[CENTER] = center; param_p[PEAK] = peak; } template SimButterworthBandpass:: SimButterworthBandpass(const SimButterworthBandpass &other) : Function1D(other), nl_p(other.nl_p), nh_p(other.nh_p) {} template SimButterworthBandpass::~SimButterworthBandpass() {} //# Operators template SimButterworthBandpass & SimButterworthBandpass:: operator=(const SimButterworthBandpass &other) { if (this != &other) { Function1D::operator=(other); nl_p = other.nl_p; nh_p = other.nh_p; } return *this; } template T SimButterworthBandpass:: eval(const typename FunctionTraits::ArgType *x) const { // this does not reflect the true responses of Butterworth filters // calculate the low-pass portion T hp = T(1); if (x[0] > param_p[CENTER]) { hp = T(1) / sqrt(T(1) + pow((x[0] - param_p[CENTER])/ (param_p[MAXCUTOFF] - param_p[CENTER]), T(2*nh_p))); } // calculate the high-pass portion if (x[0] < param_p[CENTER]) { hp *= T(1) / sqrt(T(1) + pow((param_p[CENTER] - x[0])/ (param_p[MINCUTOFF] - param_p[CENTER]), T(2*nl_p))); } return param_p[PEAK]*hp; } template Bool SimButterworthBandpass::hasMode() const { return True; } template void SimButterworthBandpass::setMode(const RecordInterface& in) { uInt order=0; // min order if (in.isDefined(String("minOrder"))) { RecordFieldId fld("minOrder"); if (in.type(in.idToNumber(fld)) == TpInt) { Int tmp; in.get(fld, tmp); order = static_cast(abs(tmp)); } else if (in.type(in.idToNumber(fld)) == TpUInt) { in.get(fld, order); } setMinOrder(order); } // max order if (in.isDefined(String("maxOrder"))) { RecordFieldId fld("maxOrder"); if (in.type(in.idToNumber(fld)) == TpInt) { Int tmp; in.get(fld, tmp); order = static_cast(abs(tmp)); } else if (in.type(in.idToNumber(fld)) == TpUInt) { in.get(fld, order); } setMaxOrder(order); } } template void SimButterworthBandpass::getMode(RecordInterface& out) const { out.define(RecordFieldId("minOrder"), getMinOrder()); out.define(RecordFieldId("maxOrder"), getMaxOrder()); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/SincFunction.h000066400000000000000000000113461476623553700220210ustar00rootroot00000000000000//# SincFunction.h: A one dimensional sin(x)/x //# Copyright (C) 2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_SINCFUNCTION_H #define SCIMATH_SINCFUNCTION_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // A one dimensional sin(x)/x // // // // // //
      • SincParam //
      • Function // // // A 1-dimensional sinc function. // // // A Sinc is described by a height, a center and a width // (halfwidth). The value is: // // let y = (x-center)/width // height (x == center) // height*sin(pi*y)/(pi*y) (x |= center) // // The parameters are enumerated by HEIGHT, CENTER and WIDTH. They have // default values of (1, 0, 1). // // // // // SincFunction sf(5.0, 25.0, 7); // sf(25); // = 5.0 // // // //
      • T should have standard numerical operators // // //
      • AipsError if incorrect parameter number specified. // // template class SincFunction : public SincParam { public: //# Constructors // Constructs the SincFunction, Defaults: // height=1, center=0, width=1. // Could not use default arguments // that worked both with gcc and IRIX // SincFunction() : SincParam() {} explicit SincFunction(const T &height) : SincParam(height) {} SincFunction(const T &height, const T ¢er) : SincParam(height, center) {} SincFunction(const T &height, const T ¢er, const T &width) : SincParam(height, center, width) {} // // Copy constructor (deep copy) // SincFunction(const SincFunction &other) : SincParam(other) {} template SincFunction(const SincFunction &other) : SincParam(other) {} // // Copy assignment (deep copy) SincFunction &operator=(const SincFunction &other) { SincParam::operator=(other); return *this; } // Destructor virtual ~SincFunction() {} //# Operators // Evaluate the Sinc at x. // If a vector is used as the argument only its first element is used. // virtual T eval(typename Function::FunctionArg x) const; // //# Member functions // Return a copy of this object from the heap. The caller is responsible // for deleting this pointer. // virtual Function *clone() const { return new SincFunction(*this); } virtual Function::DiffType> *cloneAD() const { return new SincFunction::DiffType>(*this); } virtual Function::BaseType> *cloneNonAD() const { return new SincFunction::BaseType>(*this); } // //# Make members of parent classes known. protected: using SincParam::param_p; public: using SincParam::nparameters; using SincParam::CENTER; using SincParam::WIDTH; using SincParam::HEIGHT; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/SincFunction.tcc000066400000000000000000000034171476623553700223430ustar00rootroot00000000000000//# SincFunction.cc: A one dimensional sin(x)/x //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_SINCFUNCTION_TCC #define SCIMATH_SINCFUNCTION_TCC #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Operators template T SincFunction::eval(typename Function::FunctionArg x) const { T tmp = T(M_PI)*(x[0] - param_p[CENTER]); if (tmp == T(0.0)) return param_p[HEIGHT]; return sin(tmp/param_p[WIDTH])/tmp*param_p[WIDTH]; } //# Member functions } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/SincParam.h000066400000000000000000000076361476623553700213030ustar00rootroot00000000000000//# SincParam.h: A one dimensional sin(x)/x //# Copyright (C) 2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_SINCPARAM_H #define SCIMATH_SINCPARAM_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // A one dimensional sin(x)/x // // // // // //
      • FunctionParam class //
      • Function class // // // A 1-dimensional sinc function. // // // A Sinc is described by a height, a center and a width // (halfwidth). The value is: // // let y = (x-center)/width // height (x == center) // height*sin(pi*y)/(pi*y) (x |= center) // // The parameters are enumerated by HEIGHT, CENTER and WIDTH. They have // default values of (1, 0, 1). // // // // // SincFunction sf(5.0, 25.0, 7); // sf(25); // = 5.0 // // // //
      • T should have standard numerical operators // // //
      • AipsError if incorrect parameter number specified. // template class SincParam : public Function { public: //# Enumerations // Parameter numbers enum { HEIGHT=0, CENTER, WIDTH }; //# Constructors // Constructs the Sinc, Defaults: // height=1, center=0, width=1. // Could not use default arguments // that worked both with gcc and IRIX // SincParam(); explicit SincParam(const T &height); SincParam(const T &height, const T ¢er); SincParam(const T &height, const T ¢er, const T &width); // // Copy constructor (deep copy) // SincParam(const SincParam &other); template SincParam(const SincParam &other) : Function(other) {} // // Copy assignment (deep copy) SincParam &operator=(const SincParam &other); // Destructor virtual ~SincParam(); //# Operators virtual uInt ndim() const { return 1; } //# Member functions // Give name of function virtual const String &name() const { static String x("sinc"); return x; } //# Make members of parent classes known. protected: using Function::param_p; public: using Function::nparameters; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/SincParam.tcc000066400000000000000000000046251476623553700216200ustar00rootroot00000000000000//# SincParam.cc: A one dimensional sin(x)/x //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_SINCPARAM_TCC #define SCIMATH_SINCPARAM_TCC //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template SincParam::SincParam() : Function(3) { param_p[HEIGHT] = T(1.0); param_p[CENTER] = T(0.0); param_p[WIDTH] = T(1.0); } template SincParam::SincParam(const T &height) : Function(3) { param_p[HEIGHT] = height; param_p[CENTER] = T(0.0); param_p[WIDTH] = T(1.0); } template SincParam::SincParam(const T &height, const T ¢er) : Function(3) { param_p[HEIGHT] = height; param_p[CENTER] = center; param_p[WIDTH] = T(1.0); } template SincParam::SincParam(const T &height, const T ¢er, const T &width) : Function(3) { param_p[HEIGHT] = height; param_p[CENTER] = center; param_p[WIDTH] = width; } template SincParam::SincParam(const SincParam &other) : Function(other) {} template SincParam::~SincParam() {} //# Operators template SincParam &SincParam::operator=(const SincParam &other) { if (this != &other) Function::operator=(other); return *this; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/Sinusoid1D.h000066400000000000000000000215171476623553700214020ustar00rootroot00000000000000//# Sinusoid1D.h: A one dimensional Sinusoid class //# Copyright (C) 1997,2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_SINUSOID1D_H #define SCIMATH_SINUSOID1D_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // A one dimensional Sinusoid class. // // // // // //
      • Sinusoid1DParam //
      • Function // // // A Sinusoid1D functional is designed for calculating a // Sinusoid in one dimension. // // // A Sinusoid1D is described by an amplitude, a period, // and a location of a peak. Its fundamental operation is evaluating itself // at some x. The // parameters (amplitude, period, and x0) may be changed at run time. // // The functional form is A*cos(2*pi(x-x0)/P) // // The parameter interface (see // Sinusoid1DParam class), // is used to provide an interface to the // Fitting classes. // // There are 3 parameters that are used to describe the Sinusoid: //
          //
        1. The amplitude of the Sinusoid. This is the value // returned using the amplitude member function. //
        2. The period of the Sinusoid in the x direction. This is // the value returned using the period member function. // The period is expressed in full cycles. //
        3. The location of a peak of the Sinusoid (i.e. where // x=pi+k.2pi) //
        // // An enumeration for the AMPLITUDE, PERIOD and // X0 parameter index is provided, enabling the setting // and reading of parameters with the [] operator. The // mask() methods can be used to check and set the parameter masks. // //
        // // // Sinusoid1D sf(5.0, 25.0, 7); // sf(25); // = -0.9369 // sf.setAmplitude(1.0); // sf[PERIOD] = 2.0; // sf.setX0(0.0); // sf(0.5); // = 0.0 // // // //
      • T should have standard numerical operators and cos() function. Current // implementation only tested for real types. //
      • To obtain derivatives, the derivatives should be defined. // // //
      • AipsError if incorrect parameter number specified. //
      • Assertion in debug mode if operator(Vector<>) with empty Vector // template class Sinusoid1D : public Sinusoid1DParam { public: //# Enumerations //# Constructors // Constructs the Sinusoids, Defaults: // amplitude=1, period==1, x0=0. I.e. a cosinusoid with cos(x). // Could not use default arguments // that worked both with gcc and IRIX // Sinusoid1D() : Sinusoid1DParam() {} explicit Sinusoid1D(const T &litude) : Sinusoid1DParam(amplitude) {} Sinusoid1D(const T &litude, const T &period) : Sinusoid1DParam(amplitude, period) {} Sinusoid1D(const T &litude, const T &period, const T &x0) : Sinusoid1DParam(amplitude, period, x0) {} // // Copy constructor (deep copy) // Sinusoid1D(const Sinusoid1D &other) : Sinusoid1DParam(other) {} template Sinusoid1D(const Sinusoid1D &other) : Sinusoid1DParam(other) {} // // Copy assignment (deep copy) Sinusoid1D &operator=(const Sinusoid1D &other) { Sinusoid1DParam::operator=(other); return *this; } // Destructor virtual ~Sinusoid1D() {} //# Operators // Evaluate the Sinusoid at x. // If a vector is used as the argument only its first element is used. // virtual T eval(typename Function1D::FunctionArg x) const; // //# Member functions // Return a copy of this object from the heap. The caller is responsible // for deleting this pointer. // virtual Function *clone() const { return new Sinusoid1D(*this); } virtual Function::DiffType> *cloneAD() const { return new Sinusoid1D::DiffType>(*this); } virtual Function::BaseType> *cloneNonAD() const { return new Sinusoid1D::BaseType>(*this); } // //# Make members of parent classes known. protected: using Sinusoid1DParam::param_p; public: using Sinusoid1DParam::nparameters; using Sinusoid1DParam::AMPLITUDE; using Sinusoid1DParam::PERIOD; using Sinusoid1DParam::X0; }; #define Sinusoid1D_PS Sinusoid1D // Partial specialization of Sinusoid1D for AutoDiff // // // The name Sinusoid1D_PS is only for cxx2html // documentation problems. Use Sinusoid1D in your code. // template class Sinusoid1D_PS > : public Sinusoid1DParam > { public: //# Constructors // Constructs one dimensional Sinusoids. // Sinusoid1D_PS() : Sinusoid1DParam >() {} explicit Sinusoid1D_PS(const AutoDiff &litude) : Sinusoid1DParam >(amplitude) {} Sinusoid1D_PS(const AutoDiff &litude, const AutoDiff &period) : Sinusoid1DParam >(amplitude, period) {} Sinusoid1D_PS(const AutoDiff &litude, const AutoDiff &period, const AutoDiff &x0) : Sinusoid1DParam >(amplitude, period, x0) {} // // Copy constructor (deep copy) // Sinusoid1D_PS(const Sinusoid1D_PS &other) : Sinusoid1DParam >(other) {} template Sinusoid1D_PS(const Sinusoid1D_PS &other) : Sinusoid1DParam >(other) {} // // Copy assignment (deep copy) Sinusoid1D_PS > & operator=(const Sinusoid1D_PS > &other) { Sinusoid1DParam >::operator=(other); return *this; } // Destructor virtual ~Sinusoid1D_PS() {} //# Operators // Evaluate the Sinusoid at x. // virtual AutoDiff eval(typename Function >::FunctionArg x) const; // //# Member functions // Return a copy of this object from the heap. The caller is responsible // for deleting this pointer. // virtual Function > *clone() const { return new Sinusoid1D >(*this); } virtual Function >::DiffType> *cloneAD() const { return new Sinusoid1D >::DiffType> (*this); } virtual Function >::BaseType> *cloneNonAD() const { return new Sinusoid1D >::BaseType> (*this); } // protected: //# Make members of parent classes known. using Sinusoid1DParam >::param_p; using Sinusoid1DParam >::nparameters; using Sinusoid1DParam >::AMPLITUDE; using Sinusoid1DParam >::PERIOD; using Sinusoid1DParam >::X0; }; #undef Sinusoid1D_PS } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/Sinusoid1D.tcc000066400000000000000000000033411476623553700217170ustar00rootroot00000000000000//# Sinusoid1D.cc: A one dimensional Sinusoid class //# Copyright (C) 1997,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_SINUSOID1D_TCC #define SCIMATH_SINUSOID1D_TCC #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Operators template T Sinusoid1D::eval(typename Function1D::FunctionArg x) const { return param_p[AMPLITUDE]* cos(T(2.0*M_PI)*(x[0] - param_p[X0])/param_p[PERIOD]); } //# Member functions } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/Sinusoid1D2.tcc000066400000000000000000000071311476623553700220020ustar00rootroot00000000000000//# Sinusoid1D2.cc: specialized Sinusoid1D class for AutoDiff //# Copyright (C) 1997,1998,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_SINUSOID1D2_TCC #define SCIMATH_SINUSOID1D2_TCC //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Operators template AutoDiff Sinusoid1D >:: eval(typename Function >::FunctionArg x) const { AutoDiff tmp; if (this->param_p[Sinusoid1DParam >::AMPLITUDE].nDerivatives() > 0) tmp = this->param_p[Sinusoid1DParam >::AMPLITUDE]; else if (this->param_p[Sinusoid1DParam >::PERIOD].nDerivatives() > 0) tmp = this->param_p[Sinusoid1DParam >::PERIOD]; else if (this->param_p[Sinusoid1DParam >::X0].nDerivatives() > 0) tmp = this->param_p[Sinusoid1DParam >::X0]; typename AutoDiff::value_type arg = static_cast::value_type>(2.0*M_PI) * (x[0] - this->param_p[Sinusoid1DParam >::X0].value())/this->param_p[Sinusoid1DParam >::PERIOD].value(); typename AutoDiff::value_type cosarg = cos(arg); typename AutoDiff::value_type sinarg = sin(arg); // Function value tmp.value() = this->param_p[Sinusoid1DParam >::AMPLITUDE].value() * cosarg; // get derivatives (assuming either all or none) if (tmp.nDerivatives()>0) { for (uInt j = 0; j < tmp.nDerivatives(); j++) tmp.deriv(j) = 0.0; // derivative wrt amplitude typename AutoDiff::value_type dev = cosarg; if (this->param_p.mask(Sinusoid1DParam >::AMPLITUDE)) tmp.deriv(Sinusoid1DParam >::AMPLITUDE) = dev; // derivative wrt period dev = this->param_p[Sinusoid1DParam >::AMPLITUDE].value() * arg * sinarg / this->param_p[Sinusoid1DParam >::PERIOD].value(); if (this->param_p.mask(Sinusoid1DParam >::PERIOD)) tmp.deriv(Sinusoid1DParam >::PERIOD) = dev; // derivative wrt x0 dev = this->param_p[Sinusoid1DParam >::AMPLITUDE].value() * static_cast::value_type>(2.0*M_PI) * sinarg / this->param_p[Sinusoid1DParam >::PERIOD].value(); if (this->param_p.mask(Sinusoid1DParam >::X0)) tmp.deriv(Sinusoid1DParam >::X0) = dev; } return tmp; } //# Member functions } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/Sinusoid1DParam.h000066400000000000000000000133011476623553700223530ustar00rootroot00000000000000//# Sinusoid1DParam.h: Parameter handling for one dimensional Sinusoid class //# Copyright (C) 2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_SINUSOID1DPARAM_H #define SCIMATH_SINUSOID1DPARAM_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // Parameter handling for one dimensional Sinusoid class // // // // // //
      • FunctionParam class //
      • Function1D class // // // A 1-dimensional sinusoid's parameters. // // // A Sinusoid1D is described by an amplitude, a period, // and a location of a peak. // The parameters (amplitude, period, and x0) may be changed at run time. // // The functional form is A*cos(2*pi(x-x0)/P) // // The parameter interface (see // FunctionParam class), // is used to provide an interface to the // Fitting classes. // // There are 3 parameters that are used to describe the Sinusoid: //
          //
        1. The amplitude of the Sinusoid. This is the value // returned using the amplitude member function. //
        2. The period of the Sinusoid in the x direction. This is // the value returned using the period member function. // The period is expressed in full cycles. //
        3. The location of a peak of the Sinusoid (i.e. where // x=pi+k.2pi) //
        // // An enumeration for the AMPLITUDE, PERIOD and // X0 parameter index is provided. // // This class is in general used implicitly by the Sinusoid1D // class only. //
        // // // Sinusoid1D sf(5.0, 25.0, 7); // sf(25); // = -4.911 // sf.setAmplitude(1.0); // sf[Sinusoid1D::PERIOD] = 2.0; // sf.setX0(0.0); // sf(0.5); // = 1.0 // // // //
      • T should have standard numerical operators and exp() function. Current // implementation only tested for real types (and their AutoDiffs). // // //
      • Assertion in debug mode if attempt is made to set a negative width //
      • AipsError if incorrect parameter number specified. // template class Sinusoid1DParam : public Function1D { public: //# Enumerations // Parameter numbers enum { AMPLITUDE=0, PERIOD, X0 }; //# Constructors // Constructs the Sinusoids, Defaults: // amplitude=1, period==1, x0=0. I.e. a cosinusoid with cos(x). // Could not use default arguments // that worked both with gcc and IRIX // Sinusoid1DParam(); explicit Sinusoid1DParam(const T &litude); Sinusoid1DParam(const T &litude, const T &period); Sinusoid1DParam(const T &litude, const T &period, const T &x0); // // Copy constructor (deep copy) // Sinusoid1DParam(const Sinusoid1DParam &other); template Sinusoid1DParam(const Sinusoid1DParam &other) : Function1D(other) {} // // Copy assignment (deep copy) Sinusoid1DParam &operator=(const Sinusoid1DParam &other); // Destructor virtual ~Sinusoid1DParam(); //# Operators //# Member functions // Give name of function virtual const String &name() const { static String x("sinusoid1d"); return x; } // Get or set the amplitude of the Sinusoid // T amplitude() const { return param_p[AMPLITUDE]; } void setAmplitude(const T &litude) { param_p[AMPLITUDE] = amplitude; } // // Get or set the x0 of the Sinusoid, the location of a peak. // T x0() const { return param_p[X0]; } void setX0(const T &x0) { param_p[X0] = x0; } // // Get or set the period of the Sinusoid in full cycles. // T period() const { return param_p[PERIOD]; } void setPeriod(const T &period) { param_p[PERIOD] = period; } // //# Make members of parent classes known. protected: using Function1D::param_p; public: using Function1D::nparameters; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/Sinusoid1DParam.tcc000066400000000000000000000051301476623553700226760ustar00rootroot00000000000000//# Sinusoid1DParam.cc: Parameter handling for one dimensional Sinusoid class //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_SINUSOID1DPARAM_TCC #define SCIMATH_SINUSOID1DPARAM_TCC //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template Sinusoid1DParam::Sinusoid1DParam() : Function1D(3) { param_p[AMPLITUDE] = T(1.0); param_p[X0] = T(0.0); param_p[PERIOD] = T(1.0); } template Sinusoid1DParam::Sinusoid1DParam(const T &litude) : Function1D(3) { param_p[AMPLITUDE] = T(amplitude); param_p[X0] = T(0.0); param_p[PERIOD] = T(1.0); } template Sinusoid1DParam::Sinusoid1DParam(const T &litude, const T &period) : Function1D(3) { param_p[AMPLITUDE] = T(amplitude); param_p[X0] = T(0.0); param_p[PERIOD] = T(period); } template Sinusoid1DParam::Sinusoid1DParam(const T &litude, const T &period, const T &x0) : Function1D(3) { param_p[AMPLITUDE] = T(amplitude); param_p[X0] = T(x0); param_p[PERIOD] = T(period); } template Sinusoid1DParam::Sinusoid1DParam(const Sinusoid1DParam &other) : Function1D(other) {} template Sinusoid1DParam::~Sinusoid1DParam() {} //# Operators template Sinusoid1DParam & Sinusoid1DParam::operator=(const Sinusoid1DParam &other) { if (this != &other) Function1D::operator=(other); return *this; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/SpecificFunctionFactory.h000066400000000000000000000054651476623553700242070ustar00rootroot00000000000000//# SpecificFunctionFactory.h: a class for creating a Function object from Records //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_SPECIFICFUNCTIONFACTORY_H #define SCIMATH_SPECIFICFUNCTIONFACTORY_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class Record; // // // // // // // // // // // //
      • FunctionFactory // // // // This class is based on the Factory pattern, similar to the // ApplicationObjectFactory // // // // // // // // // // // // // // // // // // // // // // //
      • F class must be a subclass of Function //
      • F class must have a constructor for the form F(const Record&) // // // //
      • //
      • // // // //
      • //
      • //
      • // template class SpecificFunctionFactory : public FunctionFactory { public: SpecificFunctionFactory() {} SpecificFunctionFactory(const SpecificFunctionFactory& factory) {} virtual ~SpecificFunctionFactory() {} virtual Function *create(const Record& gr) const throw (FunctionFactoryError) { return new F(gr); } SpecificFunctionFactory& operator=(const SpecificFunctionFactory& factory) { return *this; } }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/UnaryFunction.h000066400000000000000000000113601476623553700222170ustar00rootroot00000000000000//# UnaryFunction.h: A one dimensional unary function //# Copyright (C) 2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_UNARYFUNCTION_H #define SCIMATH_UNARYFUNCTION_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // A one dimensional unary function // // // // // //
      • UnaryParam //
      • Function // // // A 1-dimensional unary hat. // // // A Unary is described by a height, a center and a width // (halfwidth). The value is: // // height (|x-center| < width) // 0.5height (|x-center| == width) // 0 (|x-center| > width) // // The parameters are enumerated by HEIGHT, CENTER and WIDTH. They have // default values of (1, 0, 1). // // // // // UnaryFunction sf(5.0, 25.0, 7); // sf(25); // = 5.0 // // // //
      • T should have standard numerical operators // // //
      • AipsError if incorrect parameter number specified. // // template class UnaryFunction : public UnaryParam { public: //# Constructors // Constructs the UnaryFunction, Defaults: // height=1, center=0, width=1. // Could not use default arguments // that worked both with gcc and IRIX // UnaryFunction() : UnaryParam() {} explicit UnaryFunction(const T &height) : UnaryParam(height) {} UnaryFunction(const T &height, const T ¢er) : UnaryParam(height, center) {} UnaryFunction(const T &height, const T ¢er, const T &width) : UnaryParam(height, center, width) {} // // Copy constructor (deep copy) // UnaryFunction(const UnaryFunction &other) : UnaryParam(other) {} template UnaryFunction(const UnaryFunction &other) : UnaryParam(other) {} // // Copy assignment (deep copy) UnaryFunction &operator=(const UnaryFunction &other) { UnaryParam::operator=(other); return *this; } // Destructor virtual ~UnaryFunction() {} //# Operators // Evaluate the Unary at x. // If a vector is used as the argument only its first element is used. // virtual T eval(typename Function::FunctionArg x) const; // //# Member functions // Return a copy of this object from the heap. The caller is responsible // for deleting this pointer. // virtual Function *clone() const { return new UnaryFunction(*this); } virtual Function::DiffType> *cloneAD() const { return new UnaryFunction::DiffType>(*this); } virtual Function::BaseType> *cloneNonAD() const { return new UnaryFunction::BaseType>(*this); } // //# Make members of parent classes known. protected: using UnaryParam::param_p; public: using UnaryParam::nparameters; using UnaryParam::CENTER; using UnaryParam::WIDTH; using UnaryParam::HEIGHT; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/UnaryFunction.tcc000066400000000000000000000034641476623553700225470ustar00rootroot00000000000000//# UnaryFunction.cc: A one dimensional unary function //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_UNARYFUNCTION_TCC #define SCIMATH_UNARYFUNCTION_TCC #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors //# Operators template T UnaryFunction::eval(typename Function::FunctionArg x) const { T tmp(abs(x[0] - param_p[CENTER])); if (tmp == param_p[WIDTH]) return T(0.5)*param_p[HEIGHT]; if (tmp < param_p[WIDTH]) return param_p[HEIGHT]; return T(0.0); } //# Member functions } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/UnaryParam.h000066400000000000000000000077011476623553700214760ustar00rootroot00000000000000//# UnaryParam.h: Parameter handling for one dimensional unary function //# Copyright (C) 2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_UNARYPARAM_H #define SCIMATH_UNARYPARAM_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // Parameter handling for one dimensional unary function // // // // // //
      • FunctionParam class //
      • Function class // // // A 1-dimensional unary hat. // // // A Unary is described by a height, a center and a width // (halfwidth). The value is: // // height (|x-center| < width) // 0.5height (|x-center| == width) // 0 (|x-center| > width) // // The parameters are enumerated by HEIGHT, CENTER and WIDTH. They have // default values of (1, 0, 1). // // // // // UnaryFunction sf(5.0, 25.0, 7); // sf(25); // = 5.0 // // // //
      • T should have standard numerical operators // // //
      • AipsError if incorrect parameter number specified. // template class UnaryParam : public Function { public: //# Enumerations // Parameter numbers enum { HEIGHT=0, CENTER, WIDTH }; //# Constructors // Constructs the Unary, Defaults: // height=1, center=0, width=1. // Could not use default arguments // that worked both with gcc and IRIX // UnaryParam(); explicit UnaryParam(const T &height); UnaryParam(const T &height, const T ¢er); UnaryParam(const T &height, const T ¢er, const T &width); // // Copy constructor (deep copy) // UnaryParam(const UnaryParam &other); template UnaryParam(const UnaryParam &other) : Function(other) {} // // Copy assignment (deep copy) UnaryParam &operator=(const UnaryParam &other); // Destructor virtual ~UnaryParam(); //# Operators virtual uInt ndim() const { return 1; } //# Member functions // Give name of function virtual const String &name() const { static String x("unary"); return x; } //# Make members of parent classes known. protected: using Function::param_p; public: using Function::nparameters; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/UnaryParam.tcc000066400000000000000000000047041476623553700220200ustar00rootroot00000000000000//# UnaryParam.cc: Parameter handling for one dimensional unary function //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_UNARYPARAM_TCC #define SCIMATH_UNARYPARAM_TCC //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template UnaryParam::UnaryParam() : Function(3) { param_p[HEIGHT] = T(1.0); param_p[CENTER] = T(0.0); param_p[WIDTH] = T(1.0); } template UnaryParam::UnaryParam(const T &height) : Function(3) { param_p[HEIGHT] = height; param_p[CENTER] = T(0.0); param_p[WIDTH] = T(1.0); } template UnaryParam::UnaryParam(const T &height, const T ¢er) : Function(3) { param_p[HEIGHT] = height; param_p[CENTER] = center; param_p[WIDTH] = T(1.0); } template UnaryParam::UnaryParam(const T &height, const T ¢er, const T &width) : Function(3) { param_p[HEIGHT] = height; param_p[CENTER] = center; param_p[WIDTH] = width; } template UnaryParam::UnaryParam(const UnaryParam &other) : Function(other) {} template UnaryParam::~UnaryParam() {} //# Operators template UnaryParam &UnaryParam::operator=(const UnaryParam &other) { if (this != &other) Function::operator=(other); return *this; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/WrapperBase.h000066400000000000000000000062101476623553700216240ustar00rootroot00000000000000//# WrapperBase.h: Aid in constructing function objects from C++ functions //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_WRAPPERBASE_H #define SCIMATH_WRAPPERBASE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // Aid in constructing function objects from C++ functions // // // // // // // // //
      • FunctionWrapper class //
      • WrapperData class // // // // This base class is provided to enable compile time selection of the // appropriate function call through WrapperData. // // // // // Float func(const Vector& x) { return x(0)*x(1); } // x*y // // Convert C++ functions to Functionals // FunctionWrapper Func(func, 2); // // template class WrapperBase { public: //# Constructors // Default constructor: zero dimension WrapperBase() : ndim_p(0), arg_p(0) {} // Standard constructor explicit WrapperBase(const uInt dim) : ndim_p(dim), arg_p(dim) {} // Destructor virtual ~WrapperBase() {} //# Operators // Evaluate the function at x. // virtual T eval(typename Function::FunctionArg x, const Vector &par) const = 0; // //# Member functions // Get the dimensionality virtual uInt ndim() const { return ndim_p; } protected: //# Data // Dimensionality uInt ndim_p; // Vector argument interface mutable Vector arg_p; private: // Copy constructor and assignment (not implemented) // WrapperBase(const WrapperBase &other); WrapperBase &operator=(const WrapperBase &other); // }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/WrapperData.h000066400000000000000000000300531476623553700216250ustar00rootroot00000000000000//# WrapperData.h: Aid in constructing function objects from C++ functions //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_WRAPPERDATA_H #define SCIMATH_WRAPPERDATA_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // Aid in constructing function objects from C++ functions // // // // // // // // //
      • Function class //
      • FunctionParam // // // // This class is provided to enable compile time selection of the // appropriate function call. Each template incarnation represent a // function call interface definition. // // // // // Float func(const Vector& x) {return x(0)*x(1);} // x*y // // Convert C++ functions to Functionals // FunctionWrapper Func(func, 2); // // template class WrapperData : public WrapperBase { public: //# Constructors // Default constructor: to allow arrays of functions WrapperData(); // Destructor virtual ~WrapperData() {} //# Operators // Evaluate the function at x. // virtual T eval(typename Function::FunctionArg, const V&) const {} // //# Member functions protected: //# Make members of parent classes known. using WrapperBase::ndim_p; using WrapperBase::arg_p; }; #define WrapperData_TT WrapperData // Specialization for calls with argument and parameter // // Note that the actual name of the class is // WrapperData. The special name is only for the use of // cxx2html. // template class WrapperData_TT : public WrapperBase { typedef WrapperData_TT myData; public: //# Constructors // Standard constructor explicit WrapperData_TT(T(*f)(const T&, const T&), uInt dim=1) : WrapperBase(dim), pf_p(f) {} // Destructor virtual ~WrapperData_TT() {} //# Operators // Evaluate the function at x. virtual T eval(typename Function::FunctionArg x, const Vector &par) const { if (pf_p) { return pf_p((*static_cast::FunctionArg>(x)), par[0]); } return T(0); } //# Member functions protected: //# Data // Function to call T (*pf_p)(const T&, const T&); private: // Copy constructor and assignment (not implemented) // WrapperData_TT(const myData &other); myData &operator=(const myData &other); // protected: //# Make members of parent classes known. using WrapperBase::ndim_p; using WrapperBase::arg_p; }; #undef WrapperData_TT #define WrapperData_VT WrapperData // Specialization for calls with argument and parameter // // Note that the actual name of the class is // WrapperData. The special name is only for the use of // cxx2html. // template class WrapperData_VT,T,True,True> : public WrapperBase { typedef WrapperData_VT,T,True,True> myData; public: explicit WrapperData_VT(T(*f)(const Vector&, const T&), uInt dim=1) : WrapperBase(dim), pf_p(f) {} virtual ~WrapperData_VT() {} virtual T eval(typename Function::FunctionArg x, const Vector &par) const { if (pf_p) { for (uInt i=0; i&, const T&); private: WrapperData_VT(const myData &other); myData &operator=(const myData &other); protected: //# Make members of parent classes known. using WrapperBase::ndim_p; using WrapperBase::arg_p; }; #undef WrapperData_VT #define WrapperData_TV WrapperData // Specialization for calls with argument and parameters // // Note that the actual name of the class is // WrapperData. The special name is only for the use of // cxx2html. // template class WrapperData_TV,True,True> : public WrapperBase { typedef WrapperData_TV,True,True> myData; public: explicit WrapperData_TV(T(*f)(const T&, const Vector&), uInt dim=1) : WrapperBase(dim), pf_p(f) {} virtual ~WrapperData_TV() {} virtual T eval(typename Function::FunctionArg x, const Vector &par) const { if (pf_p) { return pf_p((*static_cast::FunctionArg>(x)), par); } return T(0); } protected: T (*pf_p)(const T&, const Vector&); private: WrapperData_TV(const myData &other); myData &operator=(const myData &other); protected: //# Make members of parent classes known. using WrapperBase::ndim_p; using WrapperBase::arg_p; }; #undef WrapperData_TV #define WrapperData_VV WrapperData // Specialization for calls with argument and parameters // // Note that the actual name of the class is // WrapperData. The special name is only for the use of // cxx2html. // template class WrapperData_VV,Vector,True,True> : public WrapperBase { typedef WrapperData_VV,Vector,True,True> myData; public: explicit WrapperData_VV(T(*f)(const Vector&, const Vector&), uInt dim=1) : WrapperBase(dim), pf_p(f) {} virtual ~WrapperData_VV() {} virtual T eval(typename Function::FunctionArg x, const Vector &par) const { if (pf_p) { for (uInt i=0; i&, const Vector&); private: WrapperData_VV(const myData &other); myData &operator=(const myData &other); protected: //# Make members of parent classes known. using WrapperBase::ndim_p; using WrapperBase::arg_p; }; #undef WrapperData_VV #define WrapperData_FT WrapperData // Specialization for calls with no arguments and parameter // // Note that the actual name of the class is // WrapperData. The special name is only for the use of // cxx2html. // template class WrapperData_FT : public WrapperBase { typedef WrapperData_FT myData; public: explicit WrapperData_FT(T(*f)(const T&)) : WrapperBase(0), pf_p(f) {} virtual ~WrapperData_FT() {} virtual T eval(typename Function::FunctionArg, const Vector &par) const { if (pf_p) return pf_p(par[0]); return T(0); } protected: T (*pf_p)(const T&); private: WrapperData_FT(const myData &other); myData &operator=(const myData &other); protected: //# Make members of parent classes known. using WrapperBase::ndim_p; using WrapperBase::arg_p; }; #undef WrapperData_FT #define WrapperData_FV WrapperData // Specialization for calls with no arguments and parameters // // Note that the actual name of the class is // WrapperData. The special name is only for the use of // cxx2html. // template class WrapperData_FV,False,True> : public WrapperBase { typedef WrapperData_FV,False,True> myData; public: explicit WrapperData_FV(T(*f)(const Vector&)) : WrapperBase(0), pf_p(f) {} virtual ~WrapperData_FV() {} virtual T eval(typename Function::FunctionArg, const Vector &par) const { if (pf_p) return pf_p(par); return T(0); } protected: T (*pf_p)(const Vector&); private: WrapperData_FV(const myData &other); myData &operator=(const myData &other); protected: //# Make members of parent classes known. using WrapperBase::ndim_p; using WrapperBase::arg_p; }; #undef WrapperData_FV #define WrapperData_TF WrapperData // Specialization for calls with argument and no parameters // // Note that the actual name of the class is // WrapperData. The special name is only for the use of // cxx2html. // template class WrapperData_TF : public WrapperBase { typedef WrapperData_TF myData; public: explicit WrapperData_TF(T(*f)(const T&), uInt dim=1) : WrapperBase(dim), pf_p(f) {} virtual ~WrapperData_TF() {} virtual T eval(typename Function::FunctionArg x, const Vector&) const { if (pf_p) { return pf_p((*static_cast::FunctionArg>(x))); } return T(0); } protected: T (*pf_p)(const T&); private: WrapperData_TF(const myData &other); myData &operator=(const myData &other); protected: //# Make members of parent classes known. using WrapperBase::ndim_p; using WrapperBase::arg_p; }; #undef WrapperData_TF #define WrapperData_VF WrapperData // Specialization for calls with argument and no parameters // // Note that the actual name of the class is // WrapperData. The special name is only for the use of // cxx2html. // template class WrapperData_VF,T,True,False> : public WrapperBase { typedef WrapperData_VF,T,True,False> myData; public: explicit WrapperData_VF(T(*f)(const Vector&), uInt dim=1) : WrapperBase(dim), pf_p(f) {} virtual ~WrapperData_VF() {} virtual T eval(typename Function::FunctionArg x, const Vector &) const { if (pf_p) { for (uInt i=0; i&); private: WrapperData_VF(const myData &other); myData &operator=(const myData &other); protected: //# Make members of parent classes known. using WrapperBase::ndim_p; using WrapperBase::arg_p; }; #undef WrapperData_VF #define WrapperData_FF WrapperData // Specialization for calls with no arguments and no parameters // // Note that the actual name of the class is // WrapperData. The special name is only for the use of // cxx2html. // template class WrapperData_FF : public WrapperBase { typedef WrapperData_FF myData; public: explicit WrapperData_FF(T(*f)()) : WrapperBase(0), pf_p(f) {} virtual ~WrapperData_FF() {} virtual T eval(typename Function::FunctionArg, const Vector&) const { if (pf_p) return pf_p(); return T(0); } protected: T (*pf_p)(); private: WrapperData_FF(const myData &other); myData &operator=(const myData &other); protected: //# Make members of parent classes known. using WrapperBase::ndim_p; using WrapperBase::arg_p; }; #undef WrapperData_FF } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/WrapperParam.h000066400000000000000000000070371476623553700220220ustar00rootroot00000000000000//# WrapperParam.h: Parameter handling for wrapped function objects //# Copyright (C) 2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_WRAPPERPARAM_H #define SCIMATH_WRAPPERPARAM_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // Parameter handling for wrapped function objects // // // // // // // // //
      • Function class //
      • FunctionWrapper // // // // This class is provided to enable easy specialization for the actual // FunctionWrapper class. // // // // Float func(const Vector& x) {return x(0)*x(1);} // x*y // // Convert C++ functions to Function // FunctionWrapper Func(func, 2); // // //
      • T should have standard numerical operators and exp() function. Current // implementation only tested for real types (and their AutoDiffs). // // //
      • Assertion in debug mode if attempt is made to set a negative width //
      • AipsError if incorrect parameter number specified. // // //
      • Nothing I know of // template class WrapperParam : public Function { public: //# Constructors // Construct with the given parameters // WrapperParam(); explicit WrapperParam(const T &par); explicit WrapperParam(const Vector &par); // // Copy constructor (deep copy) // WrapperParam(const WrapperParam &other); // // Copy assignment (deep copy) WrapperParam &operator=(const WrapperParam &other); // Destructor virtual ~WrapperParam(); //# Operators //# Member functions // Give name of function virtual const String &name() const { static String x("wrapper"); return x; } protected: //# Make members of parent classes known. using Function::param_p; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Functionals/WrapperParam.tcc000066400000000000000000000040721476623553700223400ustar00rootroot00000000000000//# WrapperParam.cc: Parameter handling for wrapped function objects //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_WRAPPERPARAM_TCC #define SCIMATH_WRAPPERPARAM_TCC //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors template WrapperParam::WrapperParam() : Function(0) {} template WrapperParam::WrapperParam(const T &par) : Function(1) { param_p[0] = par; } template WrapperParam::WrapperParam(const Vector &par) : Function(par) {} template WrapperParam::WrapperParam(const WrapperParam &other) : Function(other) {} template WrapperParam::~WrapperParam() {} //# Operators template WrapperParam & WrapperParam::operator=(const WrapperParam &other) { if (this != &other) Function::operator=(other); return *this; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Functionals/test/000077500000000000000000000000001476623553700202205ustar00rootroot00000000000000casacore-3.7.1/scimath/Functionals/test/CMakeLists.txt000066400000000000000000000011471476623553700227630ustar00rootroot00000000000000set (tests dFunction dGaussianND tChebyshev tCombiFunction tCompoundFunction tConstantND tFuncExpression tFunctionHolder tFunctionOrder tFunctionWrapper tGaussian1D tGaussian2D tGaussian3D tGaussianND tHyperPlane tInterpolate1D tPoisson tPolynomial tPowerLogarithmicPolynomial tSampledFunctional tSimButterworthBandpass tSinusoid1D tSPolynomial ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_scimath) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/scimath/Functionals/test/dFunction.cc000066400000000000000000000233671476623553700224730ustar00rootroot00000000000000//# dFunction.cc: test program for functional (AutoDiff) timing //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // //# Includes #include #include #include #include #include #include #include #include #include #include #include #include // 3 flavours of calculating a Gaussian // The inputs are the parameters and the x value Double a0(const Vector &par, const Double x0) { return par(0)*exp(-(x0-par(1))*(x0-par(1))/par(2)/par(2)); } Double a1(const Vector &par, const Double x0) { Double g = (x0-par(1))/par(2); return par(0)*exp(-g*g); } Double a2(const Vector &par, const Double x0) { Double y(x0); y -= par(1); y /= par(2); y *= y; y *= -1.0; y = exp(y); y *= par(0); return y; } AutoDiff a0(const Vector > &par, const Double x0) { return par(0)*exp(-(x0-par(1))*(x0-par(1))/par(2)/par(2)); } AutoDiff a1(const Vector > &par, const Double x0) { AutoDiff g = (x0-par(1))/par(2); return par(0)*exp(-g*g); } AutoDiff a2(const Vector > &par, const Double x0) { AutoDiff y(x0); y -= par(1); y /= par(2); y *= y; y *= -1.0; y = exp(y); y *= par(0); return y; } AutoDiff a0(const Vector > &par, const AutoDiff x0) { return par(0)*exp(-(x0-par(1))*(x0-par(1))/par(2)/par(2)); } AutoDiff a1(const Vector > &par, const AutoDiff x0) { AutoDiff g = (x0-par(1))/par(2); return par(0)*exp(-g*g); } AutoDiff a2(const Vector > &par, const AutoDiff x0) { AutoDiff y(x0); y -= par(1); y /= par(2); y *= y; y *= -1.0; y = exp(y); y *= par(0); return y; } // Manual derivatives Double mv(Vector &res, const Vector &par, const Double x) { res.resize(par.nelements()); Double nm = (x-par(1))/par(2); Double e = exp(-nm*nm); Double val = par(0)*e; res(0) = e; res(1) = 2.0*val*nm/par(2); res(2) = res(1)*nm; return val; } int main(int argc, const char* argv[]) { // Inputs cout << ">>>" << endl; Input inputs(1); inputs.version("$Id$"); inputs.create("n", "100000", "n"); inputs.readArguments(argc, argv); Int N = inputs.getInt("n"); cout << "<<<" << endl; cout << "N = " << N << endl; // Parameters Vector par(3); par(0) = 1000; par(1) = 2; par(2) = 3; Double x = 4; AutoDiff xa(4, 3); Vector > para(3); para(0) = AutoDiff(1000, 3, 0); para(1) = AutoDiff(2, 3, 1); para(2) = AutoDiff(3, 3, 2); Vector va(3); // Check results cout << "Values (g0: 1 line; g1: 2 lines; g2: RPN type):" << endl; cout << "g0: " << a0(par, x) << endl; cout << "g1: " << a1(par, x) << endl; cout << "g2: " << a2(par, x) << endl; cout << "Manual (mv): " << mv(va, par, x); cout << " " << va << endl; Gaussian1D g1d((Double(par(0))), (Double(par(1))), (Double(par(2))/(1.0/sqrt(log(16.0))))); // Autoderivatives cout << "AutoDiff (a0): " << a0(para, x) << endl; cout << "AutoDiff (a1): " << a1(para, x) << endl; cout << "AutoDiff (a2): " << a2(para, x) << endl; cout << "--------- Values original (OPTLIB N=1000000/100000) ---------" << "\ng0: 3.91 real 3.91 user 0 system" "\ng1: 2.77 real 2.77 user 0 system" "\ng2: 2.9 real 2.89 user 0 system" "\nmd: 18.06 real 18.05 user 0 system" "\nmv: 6.47 real 6.44 user 0 system" "\nao: 0.99 real 0.99 user 0 system" "\na0: 16.6 real 16.6 user 0 system" "\na1: 13.6 real 13.4 user 0 system" "\na2: 6.72 real 6.32 user 0 system" << endl; cout << "--------- Values original (OPT=1 N=1000000/100000) -----------" << "\ng0: 1.32 real 1.31 user 0 system" "\ng1: 0.82 real 0.82 user 0 system" "\ng2: 0.83 real 0.83 user 0 system" "\nmd: 11.74 real 11.74 user 0 system" "\nmv: 1.21 real 1.22 user 0 system" "\nao: 0.93 real 0.93 user 0 system" "\na0: 6.81 real 6.78 user 0 system" "\na1: 5.27 real 5.27 user 0 system" "\na2: 1.88 real 1.88 user 0 system" << endl; cout << "--------- Function access; Rep (OPT=1 N=100000) ----" << "\na0: 7.54 real 7.48 user 0 system" "\na1: 5.81 real 5.8 user 0 system" "\na2: 1.86 real 1.86 user 0 system" << endl; cout << "--------- Public access; Rep (OPT=1 N=100000) ------" << "\na0: 6.49 real 6.18 user 0 system" "\na1: 4.84 real 4.63 user 0 system" "\na2: 1.52 real 1.43 user 0 system" << endl; cout << "--------- Pool simple (OPT=1 N=100000) ------" << "\na0: 5.83 real 5.83 user 0 system" "\na1: 4.58 real 4.58 user 0 system" "\na2: 1.61 real 1.61 user 0 system" << endl; cout << "--------- Pool no copy of temp (OPT=1 N=100000) ----" << "\na0: 3.59 real 3.58 user 0 system" "\na1: 2.65 real 2.65 user 0 system" "\na2: 1.38 real 1.35 user 0 system" << endl; cout << "--------- Pool as g0 etc (OPT=1 N=100000) ----------" << "\na0: 2.29 real 2.27 user 0 system" "\na1: 1.84 real 1.82 user 0 system" "\na2: 1.35 real 1.33 user 0 system" << endl; cout << "--------- Revamp; vector g0 etc (OPT=1 N=100000) ----------" << "\na0: 1.49 real 1.49 user 0 system" "\na1: 1.09 real 1.09 user 0 system" "\na2: 0.77 real 0.77 user 0 system" << endl; // Loop values cout << "\nTiming values:" << endl; Timer tim; tim.mark(); for (Int i=0; i resa; for (uInt j=0; j<4; j++) { /// for (uInt j=3; j<4; j++) { cout << endl << "--------- " << j << " derivatives: "; Vector > para(3); if (j == 0) { para(0) = AutoDiff(1000); para(1) = AutoDiff(2); para(2) = AutoDiff(3); } else if (j==1) { para(0) = AutoDiff(1000, j, 0); para(1) = AutoDiff(2, j); para(2) = AutoDiff(3, j); } else if (j==2) { para(0) = AutoDiff(1000, j, 0); para(1) = AutoDiff(2, j, 1); para(2) = AutoDiff(3, j); } else { para(0) = AutoDiff(1000, 3, 0); para(1) = AutoDiff(2, 3, 1); para(2) = AutoDiff(3, 3, 2); } cout << "N = " << N << " (at x=Double)" << endl; tim.mark(); for (Int i=0; i xa(4, j); tim.mark(); for (Int i=0; i #include #include #include #include int main(){ cout << "The example from the Header File" << endl; uInt ndim = 2; Float height = 1; Vector mean(ndim); mean(0) = 0, mean(1) = 1; Vector variance(ndim); variance(0) = .1, variance(1) = 7; GaussianND g(ndim, height, mean, variance); Vector x(ndim); x = 0; cout << "g("<< x <<") = " << g(x) < #include #include #include #include #include #include #include #include #include #include #include #include int main() { Chebyshev cheb; Vector coeffs(4, 0); coeffs(3) = 2.0; cheb.setCoefficients(coeffs); #ifdef DIAGNOSTICS cout << "Chebyshev " << coeffs; #endif cheb.chebyshevToPower(coeffs); #ifdef DIAGNOSTICS cout << " maps to polynomials coeffs: " << coeffs << endl; #endif AlwaysAssertExit(coeffs(0) == 0 && coeffs(1) == -6 && coeffs(2) == 0 && coeffs(3) == 8 ); #ifdef DIAGNOSTICS cout << "And power series coeffs " << coeffs; #endif cheb.powerToChebyshev(coeffs); #ifdef DIAGNOSTICS cout << " maps to chebyshev coeffs: " << coeffs << endl; cout << "coeffs: " << coeffs << ", " << cheb << endl; #endif AlwaysAssertExit(coeffs(0) == cheb.getCoefficient(0) && coeffs(1) == cheb.getCoefficient(1) && coeffs(2) == cheb.getCoefficient(2) && coeffs(3) == cheb.getCoefficient(3) ); coeffs = 2.0; coeffs(0) += 1.0; cheb.setCoefficients(coeffs); #ifdef DIAGNOSTICS cout << "Chebyshev " << coeffs; #endif cheb.chebyshevToPower(coeffs); #ifdef DIAGNOSTICS cout << " maps to power series coeffs: " << coeffs << endl; #endif AlwaysAssertExit(coeffs(0) == 1 && coeffs(1) == -4 && coeffs(2) == 4 && coeffs(3) == 8 ); Double xmin = 0, xmax = 4, xp; cheb.setInterval(xmin, xmax); AlwaysAssertExit(xmin == cheb.getIntervalMin()); AlwaysAssertExit(xmax == cheb.getIntervalMax()); Polynomial poly(3); poly.setCoefficients(coeffs); Chebyshev chebp = cheb.derivative(); #ifdef DIAGNOSTICS Vector dce; dce = chebp.getCoefficients(); chebp.chebyshevToPower(dce); cout << "dcheb: " << dce << endl; #endif Polynomial polyp = poly.derivative(); polyp.setCoefficients(polyp.coefficients() * (2/(xmax-xmin))); #ifdef DIAGNOSTICS cout << "dpoly: " << polyp.coefficients() << endl; #endif for (Double x=xmin; x <= xmax; x += 0.1) { xp = (2*x-xmin-xmax)/(xmax-xmin); AlwaysAssertExit(nearAbs(cheb(x), poly(xp), 1.0e-14)); AlwaysAssertExit(nearAbs(chebp(x), polyp(xp), 1.0e-14)); } // Test auto differentiation wrt x Chebyshev > chebad(cheb.nparameters()); chebad.setInterval(cheb.getIntervalMin(), cheb.getIntervalMax()); /// for (uInt i=0; i<4; ++i) chebad[i] = AutoDiffA(cheb[i]); for (uInt i=0; i<4; ++i) chebad[i] = cheb[i]; for (AutoDiffA x(xmin, 1, 0); x <= xmax; x += 0.1) { AlwaysAssertExit(nearAbs(chebp(x.value()), chebad(x).deriv(0), 1.0e-14)); } // test out-of-interval modes AlwaysAssertExit(0 == cheb.getDefault()); cheb.setDefault(5); AlwaysAssertExit(5 == cheb.getDefault()); AlwaysAssertExit(cheb(xmin-1) == cheb.getDefault()); cheb.setOutOfIntervalMode(ChebyshevEnums::EXTRAPOLATE); AlwaysAssertExit(cheb(xmin-0.2) != cheb.getDefault()); xp = (2*(xmin-0.2)-xmin-xmax)/(xmax-xmin); #ifdef DIAGNOSTICS cout << xmin-0.2 << ": cheb-poly=" << cheb(xmin-0.2)-poly(xp) << endl; #endif AlwaysAssertExit(nearAbs(cheb(xmin-0.2), poly(xp), 1.0e-14)); cheb.setOutOfIntervalMode(ChebyshevEnums::CYCLIC); #ifdef DIAGNOSTICS cout << xmin-1.3 << ": cheb(-1.3)-cheb(2.7)=" << cheb(xmin-1.3)-cheb(xmin-1.3+(xmax-xmin)) << endl; #endif AlwaysAssertExit(nearAbs(cheb(xmin-1.3), cheb(xmin-1.3+(xmax-xmin)), 1.0e-15)); #ifdef DIAGNOSTICS cout << xmax+1.3 << ": cheb(5.3)-cheb(1.3)=" << cheb(xmin+1.3)-cheb(xmin+1.3-(xmax-xmin)) << endl; #endif AlwaysAssertExit(nearAbs(cheb(xmax+1.3), cheb(xmax+1.3-(xmax-xmin)), 1.0e-15)); cheb.setOutOfIntervalMode(ChebyshevEnums::ZEROTH); cheb.setCoefficient(0, 1); AlwaysAssertExit(cheb(xmax+1) == cheb.getCoefficient(0)); // Test setMode() AlwaysAssertExit(cheb.hasMode()); Record rec; Vector intv(2); intv(0) = -10.0; intv(1) = 10.0; rec.define(RecordFieldId("intervalMode"), "cyclic"); rec.define(RecordFieldId("interval"), intv); rec.define(RecordFieldId("default"), 80.0); cheb.setMode(rec); #ifdef DIAGNOSTICS cout << "Results from setMode():" << endl << " Interval Mode: " << cheb.getOutOfIntervalMode() << endl << " Range: " << cheb.getIntervalMin() << ", " << cheb.getIntervalMax() << endl << " Default: " << cheb.getDefault() << endl; #endif AlwaysAssertExit(cheb.getOutOfIntervalMode() == ChebyshevEnums::CYCLIC && cheb.getIntervalMin() == -10.0 && cheb.getIntervalMax() == 10.0 && cheb.getDefault() == 80.0); // test setMode() via constructor Chebyshev cheb2(2, rec); AlwaysAssertExit(cheb2.getOutOfIntervalMode() == ChebyshevEnums::CYCLIC && cheb2.getIntervalMin() == -10.0 && cheb2.getIntervalMax() == 10.0 && cheb2.getDefault() == 80.0); // test getMode() Record rec2; cheb.setInterval(-15.0, 15.0); cheb.setDefault(70.0); cheb.setOutOfIntervalMode(ChebyshevEnums::ZEROTH); cheb.getMode(rec2); try { Vector tmp(2); rec2.get(RecordFieldId("interval"), tmp); Double def; rec2.get(RecordFieldId("default"), def); String mode; rec2.get(RecordFieldId("intervalMode"), mode); AlwaysAssertExit(mode == String("zeroth") && tmp(0) == -15.0 && tmp(1) == 15.0 && def == 70.0); } catch (std::exception& ex) { cerr << "Exception: " << ex.what() << endl; exit(1); } cout << "OK" << endl; return 0; } casacore-3.7.1/scimath/Functionals/test/tCombiFunction.cc000066400000000000000000000115661476623553700234630ustar00rootroot00000000000000//# tCombiFunction.cc: Test the CombiFunction class //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main() { int i; // construct a linear combination of functions: a(0)+a(1)*x+a(2)*x^2 Polynomial constant(0); Polynomial linear(1); Polynomial square(2); constant[0] = 1.0; // 1 linear[1] = 1.0; // x square[2] = 1.0; // x^2 // The default constructor -- no functions, no parameters, nothing, the // function operator returns a 0. CombiFunction combination; // Add a function. All functions must have the same ndim() // as the first one. Returns the (zero relative) number of the function // just added. In the meantime, the coefficient a(i) which is also the // ith available parameter, and the mask for the "available parameter" are // initialized with "one" and "True," respectively. combination.addFunction(constant); combination.addFunction(linear); combination.addFunction(square); // Make this object a copy of other. //CombiFunction(const CombiFunction &other); CombiFunction comb2(combination); // Make this object a copy of other. //CombiFunction &operator=(const CombiFunction &other); comb2 = combination; // Return the total number of coefficients. The number is equal to the // number of functins that have been added. // uInt nCoefficients() const; cout << "n: " << combination.nFunctions() << ", " << combination.nparameters() << ", " << comb2.nFunctions() << ", " << endl; AlwaysAssertExit(combination.nFunctions() == 3 && combination.nparameters() == comb2.nFunctions()); // Return the total number of functions. The number is equal to the // number of functins that have been added. //uInt nFunctions() const; AlwaysAssertExit(combination.nFunctions() == 3 && combination.nFunctions() == comb2.nFunctions()); Vector v(3); // Set the value of a coefficient. // f(x) = 10 + 11*x + 12*x^2 for (i = 0; i < 3; i++) { combination[i] = i+10; AlwaysAssertExit(combination[i] == Double(i+10)); v(i) = i+10; } // Set all coefficients at once. combination.parameters().setParameters(v); AlwaysAssertExit(allEQ(combination.parameters().getParameters(), v)); // Return a reference to a specific Function in the combination. // f(x) = 10 + 11*x + 12*x^2 AlwaysAssertExit((combination.function(0))(10) == Double(1)); AlwaysAssertExit((combination.function(1))(10) == Double(10)); AlwaysAssertExit((combination.function(2))(10) == Double(100)); // Evaluate the linear combination at x. //virtual T operator()(const Vector &x) const; // Evaluate the linear combination at x. // This operator is used when combination is 1D //virtual T operator()(const T &x) const; // f(x) = 10 + 11*x + 12*x^2 v.resize(1); v(0) = 5; AlwaysAssertExit((combination(v) - Double(36365)) < 1.e-6); AlwaysAssertExit((combination(5) - Double(36365)) < 1.e-6); // Returns the dimension of functions in the linear combination //virtual uInt ndim() const; AlwaysAssertExit(combination.ndim() == 1); cout << "OK" << endl; return 0; } casacore-3.7.1/scimath/Functionals/test/tCompoundFunction.cc000066400000000000000000000076331476623553700242160ustar00rootroot00000000000000//# tCompoundFunction: Test the CompoundFunction class //# Copyright (C) 1995,1996,1999,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include int main() { // CompoundFunction(); CompoundFunction sumfunc; AlwaysAssertExit(sumfunc.nparameters() == 0 && sumfunc(-11.0) == 0.0); // uInt addFunction(const Function &newFunction); Polynomial poly(2); poly[2] = 1.0; // x^2 Gaussian1D gauss(1.0, 0.0, sqrt(log(16.0))); // e^{-x^2} sumfunc.addFunction(poly); sumfunc.addFunction(gauss); // x^2 + e^{-x^2} // T operator()(const T &x) const; AlwaysAssertExit(near(sumfunc(2.0), 2.0*2.0 + 1.0/M_E/M_E/M_E/M_E)); Double xvec = 1.0; AlwaysAssertExit(near(sumfunc(xvec), 1.0*1.0 + 1.0/M_E)); // uInt nparameters() AlwaysAssertExit(sumfunc.nparameters() == 6); // CompoundFunction(const CompoundFunction &other); // operator=(const CompoundFunction &other); CompoundFunction f2(sumfunc); CompoundFunction f3; f3 = sumfunc; // void setParameter(uInt which, const T &val); AlwaysAssertExit(allEQ(sumfunc.parameters().getParameters(), f2.parameters().getParameters()) && allEQ(sumfunc.parameters().getParameters(), f3.parameters().getParameters())); // uInt nFunctions() const { return functions_p.nelements(); } AlwaysAssertExit(sumfunc.nFunctions() == 2 && f2.nFunctions() == 2 && f3.nFunctions() == 2); // const Function *function(uInt which) const // Function *function(uInt which); const CompoundFunction sfref = sumfunc; AlwaysAssertExit( (sumfunc.function(0))(3.0) == 9.0); AlwaysAssertExit( near((sfref.function(1))(-1.0), 1.0/M_E )); // T getparameter(uInt which) const; AlwaysAssertExit(sumfunc[0] == 0.0 && sumfunc[1] == 0.0 && sumfunc[2] == 1.0 && sumfunc[3] == 1.0 && sumfunc[4] == 0.0 && near(sumfunc[5] , sqrt(log(16.0)))); // virtual void setParameter(uInt which, const T &val); sumfunc[4] = 2.0; AlwaysAssertExit(near(sumfunc(3.0), 3.0*3.0 + 1.0/M_E)); // virtual Function *cloneFunction() const; // ~CompoundFunction(); Function *fptr = sumfunc.clone(); AlwaysAssertExit(allEQ(sumfunc.parameters().getParameters(), fptr->parameters().getParameters())); delete fptr; cout << "OK" << endl; return 0; } casacore-3.7.1/scimath/Functionals/test/tConstantND.cc000066400000000000000000000120701476623553700227260ustar00rootroot00000000000000//# tHyperPlane.cc: Test the HyperPlane class //# Copyright (C) 2001,2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include int main() { try { // Construct a constant in m dimensional space. By ConstantND constant(3); ConstantND const2(constant); const2[0] = 22; ConstantND const3 = const2; cout << "nparms " << constant.nparameters() << endl; AlwaysAssertExit( constant.nparameters() == 1 && const2.nparameters() == 1 && const3.nparameters() == 1 ); cout << "ndim " << constant.ndim() << endl; cout << "ndim " << const2.ndim() << endl; AlwaysAssertExit( constant.ndim() == 3 && const2.ndim() == 3 && const3.ndim() == 3 ); cout << "parms " << constant.parameters()[0] << endl; cout << "parms " << const2.parameters()[0] << endl; cout << "parms " << const3.parameters()[0] << endl; AlwaysAssertExit( constant.parameters()[0] == 0 && const2.parameters()[0] == 22 && const3.parameters()[0] == 22 ); Vector v(1, 45.5); // Set the value of a coefficient. // Get the value of a coefficient. // f(x,y,z) = 45.5 constant[0] = 45.5; AlwaysAssertExit(constant[0] == Double(45.5)); // Set all coefficients at once. // Get all the values of coefficients at once. constant.parameters().setParameters(v); AlwaysAssertExit(allEQ(constant.parameters().getParameters(), v)); Vector x(3); x[0] = 20; x[1] = 40; x[2] = 90; // Evaluate the function at x. // f(x,y,z) = 45.5 AlwaysAssertExit((constant(x) - Double(45.5)) < 1.e-6); constant.mask(0) = False; AlwaysAssertExit(! constant.mask(0)); AlwaysAssertExit(constant.parameters().nMaskedParameters() == 0); constant.mask(0) = True; AlwaysAssertExit(constant.parameters().nMaskedParameters() == 1); AlwaysAssertExit(constant.parameters().getMaskedParameters()[0] == 45.5); // test specialized AutoDiff Vector > v6(1, 0); ConstantND > s5(3); v.resize(3); for (uInt i=0; i<3; i++) { s5[0] = AutoDiff(0,1,0); cout << "s5 " << i << " " << s5[0] << endl; v[i] = i+10; } //Double y = s5(v).value(); Vector z = s5(v).derivatives(); cout << "AutoDiff " << s5(v) << endl; /* // f(x,y,z) = 10x + 11y + 12*z + 13 Vector > v6(3); HyperPlane > s5(3); for (uInt i=0; i<3; i++) { s5[i] = AutoDiff(i+10,3,i); AlwaysAssertExit(s5[i] == Double(i+10)); v[i] = i+10; v6(i) = i+10; } Double y50 = s5(v).value(); Vector y51; y51 = s5(v).derivatives(); cout << "AutoDiff: " << s5(v) << endl; // Generic AutoDiff HyperPlane > s6(3); for (uInt i=0; i<3; i++) { s6[i] = AutoDiffA(i+10,3,i); AlwaysAssertExit(s6[i].value() == Double(i+10)); v6(i) = i+10; } Double y60 = s6(v6).value(); Vector y61; y61 = s6(v6).derivatives(); cout << "AutoDiffA: " << s6(v6) << endl; AlwaysAssertExit(near(y60, y50) && near(y61(0), y51[0]) && near(y61(1), y51[1]) && near(y61(2), y51[2])); */ } catch (std::exception& x) { cout << "Exception : " << x.what() << endl; } cout << "OK" << endl; return 0; } casacore-3.7.1/scimath/Functionals/test/tFuncExpression.cc000066400000000000000000000110551476623553700236700ustar00rootroot00000000000000//# tFuncExpression.cc: This program test the functional run-time expressions //# Copyright (C) 2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include int main() { try { cout << "------------ test functional expressions ------------" << endl; cout << "--- Check base -----" << endl; // Make an operator base FuncExprData base; cout << base << endl; cout << "--- Check expression syntax ----" << endl; const uInt n=27; String exprlist[n] = { String("+-(25*30+2)--(75+2)"), String("1+2-3"), String("1+2/3"), String("2^2^3"), String("1+(2+3"), String("1"), String(""), String("pi"), String("sin(1)"), String("cop(1)"), String("pi(2)+1.5"), String("sin(1,2)"), String("2*p"), String("2*p1"), String("2*p[1]"), String("2*x0"), String("sin(sin(2))"), String("x==0"), String("(x==0)+1"), String("(x==0)+1"), String("((x==0) * 1)+((x!=0) * sin(x+(x==0)*1)/(x+(1)))"), String("1+(1==2)?5:8+20"), String("1+(2==2)?5:(8+20)"), String("1+((1==2)?5:8)+20"), String("1+((2==2)?5:(8+20))"), String("erf(1)"), String("erfc(1)") }; for (uInt i=0; i expr; String myexpr = exprlist[i]; cout << "Expression: '" << myexpr << "'" << endl; if (!expr.setFunction(myexpr)) { cout << expr.errorMessage() << endl; } if (expr.nparameters() > 0) expr[0] = 1.5; if (expr.nparameters() > 1) expr[1] = 2.5; cout << "Value(3.5, 0): "; cout << expr(3.5) << ", " << expr(0.0) << endl; cout << "----------------------------------------------------" << endl; } for (uInt i=0; i > expr; String myexpr = exprlist[i]; cout << "Expression: '" << myexpr << "'" << endl; if (!expr.setFunction(myexpr)) { cout << expr.errorMessage() << endl; } if (expr.nparameters() > 0) { expr[0] = AutoDiff(1.5, expr.nparameters(), 0); } if (expr.nparameters() > 1) { expr[1] = AutoDiff(2.5, expr.nparameters(), 1); } cout << "Value(3.5, 0): "; cout << expr(3.5) << ", " << expr(0.0) << endl; cout << "----------------------------------------------------" << endl; } } catch (std::exception& x) { cerr << x.what() << endl; cout << "FAIL" << endl; return 1; } catch (...) { cerr << "Exception not derived from AipsError" << endl; cout << "FAIL" << endl; return 2; } } casacore-3.7.1/scimath/Functionals/test/tFuncExpression.out000066400000000000000000000344551476623553700241230ustar00rootroot00000000000000------------ test functional expressions ------------ --- Check base ----- Unary operators with 2 characters: Unary operators with 1 character: 03: ! :1:28:1:00:0: 02: + :1:44:1:00:0: 01: - :1:44:1:00:0: Binary operators with 2 characters: 08: != :2:32:2:01:0: 10: && :2:20:2:01:0: 04: ** :2:48:2:01:0: 06: <= :2:32:2:01:0: 07: == :2:32:2:01:0: 05: >= :2:32:2:01:0: 09: || :2:20:2:01:0: Binary operators with 1 character: 16: * :3:40:2:01:0: 14: + :3:36:2:01:0: 30: , :3:00:0:00:0: 15: - :3:36:2:01:0: 17: / :3:40:2:01:0: 18: < :3:32:2:01:0: 19: > :3:32:2:01:0: 11: ? :3:16:2:01:0: 13: CONDEX3 :3:16:2:01:0: 04: ^ :3:48:2:01:0: Special operations: 26: ( :4:60:0:00:0: 27: ) :4:00:0:00:0: 12: : :4:17:2:01:0: 22: ARG :4:60:0:-1:0: 20: CONST :4:60:0:-1:0: 31: FINISH :4:00:0:00:0: 32: GOTO :4:00:0:00:0: 33: GOTOF :4:00:0:00:0: 34: GOTOT :4:00:0:00:0: 00: NOP :4:00:0:00:0: 21: PARAM :4:60:0:-1:0: 23: TOIMAG :4:60:0:00:0: 28: [ :4:60:0:00:0: 29: ] :4:00:0:00:0: 24: { :4:60:0:00:0: 25: } :4:00:0:00:0: Functions: 51: abs :5:60:1:01:0: 40: acos :5:60:1:01:0: 61: ampl :5:60:1:01:0: 39: asin :5:60:1:01:0: 37: atan :5:60:1:01:0: 38: atan2 :5:60:2:01:0: 53: ceil :5:60:1:01:0: 58: complex :5:60:1:01:0: 36: cos :5:60:1:01:0: 50: ee :5:60:0:01:0: 47: erf :5:60:1:01:0: 48: erfc :5:60:1:01:0: 41: exp :5:60:1:01:0: 42: exp10 :5:60:1:01:0: 43: exp2 :5:60:1:01:0: 52: floor :5:60:1:01:0: 56: fract :5:60:1:01:0: 60: imag :5:60:1:01:0: 55: int :5:60:1:01:0: 44: ln :5:60:1:01:0: 45: log :5:60:1:01:0: 46: log2 :5:60:1:01:0: 62: phase :5:60:1:01:0: 49: pi :5:60:0:01:0: 59: real :5:60:1:01:0: 54: round :5:60:1:01:0: 35: sin :5:60:1:01:0: 57: sqrt :5:60:1:01:0: --- Check expression syntax ---- Expression: '+-(25*30+2)--(75+2)' 20: CONST :4:60:0:-1:0: 20: CONST :4:60:0:-1:1: 16: * :3:40:2:01:0: 20: CONST :4:60:0:-1:2: 14: + :3:36:2:01:0: 01: - :1:44:1:00:0: 02: + :1:44:1:00:0: 20: CONST :4:60:0:-1:3: 20: CONST :4:60:0:-1:4: 14: + :3:36:2:01:0: 01: - :1:44:1:00:0: 15: - :3:36:2:01:0: Value: -675 ---------------------------------------------------- Expression: '1+2-3' 20: CONST :4:60:0:-1:0: 20: CONST :4:60:0:-1:1: 14: + :3:36:2:01:0: 20: CONST :4:60:0:-1:2: 15: - :3:36:2:01:0: Value: 0 ---------------------------------------------------- Expression: '1+2/3' 20: CONST :4:60:0:-1:0: 20: CONST :4:60:0:-1:1: 20: CONST :4:60:0:-1:2: 17: / :3:40:2:01:0: 14: + :3:36:2:01:0: Value: 1.66667 ---------------------------------------------------- Expression: '2^2^3' 20: CONST :4:60:0:-1:0: 20: CONST :4:60:0:-1:1: 20: CONST :4:60:0:-1:2: 04: ^ :3:48:2:01:0: 04: ^ :3:48:2:01:0: Value: 256 ---------------------------------------------------- Expression: '1+(2+3' Missing closing right parenthesis at: '1+(2+3''' Value: No value returned ---------------------------------------------------- Expression: '1' 20: CONST :4:60:0:-1:0: Value: 1 ---------------------------------------------------- Expression: '' Unexpected EOS Value: No value returned ---------------------------------------------------- Expression: 'pi' 49: pi :5:60:0:01:0: Value: 3.14159 ---------------------------------------------------- Expression: 'sin(1)' 20: CONST :4:60:0:-1:0: 35: sin :5:60:1:01:0: Value: 0.841471 ---------------------------------------------------- Expression: 'cop(1)' Unknown function name cop at: 'cop''(1)' Value: No value returned ---------------------------------------------------- Expression: 'pi(2)+1.5' 20: CONST :4:60:0:-1:0: 49: pi :5:60:0:01:0: 20: CONST :4:60:0:-1:1: 14: + :3:36:2:01:0: Value: 7.78319 ---------------------------------------------------- Expression: 'sin(1,2)' Incorrect number of arguments in function at: 'sin(1,2)''' Value: No value returned ---------------------------------------------------- Expression: '2*p' 20: CONST :4:60:0:-1:0: 21: PARAM :4:60:0:-1:0: 16: * :3:40:2:01:0: Value: Unknown execution code 'PARAM': programming error ---------------------------------------------------- Expression: '2*p1' 20: CONST :4:60:0:-1:0: 21: PARAM :4:60:0:-1:1: 16: * :3:40:2:01:0: Value: Unknown execution code 'PARAM': programming error ---------------------------------------------------- Expression: '2*p[1]' 20: CONST :4:60:0:-1:0: 21: PARAM :4:60:0:-1:0: 16: * :3:40:2:01:0: Value: Unknown execution code 'PARAM': programming error ---------------------------------------------------- Expression: '2*x0' 20: CONST :4:60:0:-1:0: 22: ARG :4:60:0:-1:0: 16: * :3:40:2:01:0: Value: Unknown execution code 'ARG': programming error ---------------------------------------------------- Expression: 'sin(sin(2))' 20: CONST :4:60:0:-1:0: 35: sin :5:60:1:01:0: 35: sin :5:60:1:01:0: Value: 0.789072 ---------------------------------------------------- Expression: 'x==0' 22: ARG :4:60:0:-1:0: 20: CONST :4:60:0:-1:0: 07: == :2:32:2:01:0: Value: Unknown execution code 'ARG': programming error ---------------------------------------------------- Expression: '(x==0)+1' 22: ARG :4:60:0:-1:0: 20: CONST :4:60:0:-1:0: 07: == :2:32:2:01:0: 20: CONST :4:60:0:-1:1: 14: + :3:36:2:01:0: Value: Unknown execution code 'ARG': programming error ---------------------------------------------------- Expression: '(x==0)+1' 22: ARG :4:60:0:-1:0: 20: CONST :4:60:0:-1:0: 07: == :2:32:2:01:0: 20: CONST :4:60:0:-1:1: 14: + :3:36:2:01:0: Value: Unknown execution code 'ARG': programming error ---------------------------------------------------- Expression: '((x==0) * 1)+((x!=0) * sin(x+(x==0)*1)/(x+(1)))' 22: ARG :4:60:0:-1:0: 20: CONST :4:60:0:-1:0: 07: == :2:32:2:01:0: 20: CONST :4:60:0:-1:1: 16: * :3:40:2:01:0: 22: ARG :4:60:0:-1:0: 20: CONST :4:60:0:-1:2: 08: != :2:32:2:01:0: 22: ARG :4:60:0:-1:0: 22: ARG :4:60:0:-1:0: 20: CONST :4:60:0:-1:3: 07: == :2:32:2:01:0: 20: CONST :4:60:0:-1:4: 16: * :3:40:2:01:0: 14: + :3:36:2:01:0: 35: sin :5:60:1:01:0: 16: * :3:40:2:01:0: 22: ARG :4:60:0:-1:0: 20: CONST :4:60:0:-1:5: 14: + :3:36:2:01:0: 17: / :3:40:2:01:0: 14: + :3:36:2:01:0: Value: Unknown execution code 'ARG': programming error ---------------------------------------------------- Expression: '1+(1==2)?5:8+20' 20: CONST :4:60:0:-1:0: 20: CONST :4:60:0:-1:1: 20: CONST :4:60:0:-1:2: 07: == :2:32:2:01:0: 14: + :3:36:2:01:0: 33: GOTOF :4:00:0:00:8: 20: CONST :4:60:0:-1:3: 32: GOTO :4:00:0:00:11: 20: CONST :4:60:0:-1:4: 20: CONST :4:60:0:-1:5: 14: + :3:36:2:01:0: 13: CONDEX3 :3:16:2:01:0: Value: 5 ---------------------------------------------------- Expression: '1+(2==2)?5:(8+20)' 20: CONST :4:60:0:-1:0: 20: CONST :4:60:0:-1:1: 20: CONST :4:60:0:-1:2: 07: == :2:32:2:01:0: 14: + :3:36:2:01:0: 33: GOTOF :4:00:0:00:8: 20: CONST :4:60:0:-1:3: 32: GOTO :4:00:0:00:11: 20: CONST :4:60:0:-1:4: 20: CONST :4:60:0:-1:5: 14: + :3:36:2:01:0: 13: CONDEX3 :3:16:2:01:0: Value: 5 ---------------------------------------------------- Expression: '1+((1==2)?5:8)+20' 20: CONST :4:60:0:-1:0: 20: CONST :4:60:0:-1:1: 20: CONST :4:60:0:-1:2: 07: == :2:32:2:01:0: 33: GOTOF :4:00:0:00:7: 20: CONST :4:60:0:-1:3: 32: GOTO :4:00:0:00:8: 20: CONST :4:60:0:-1:4: 13: CONDEX3 :3:16:2:01:0: 14: + :3:36:2:01:0: 20: CONST :4:60:0:-1:5: 14: + :3:36:2:01:0: Value: 29 ---------------------------------------------------- Expression: '1+((2==2)?5:(8+20))' 20: CONST :4:60:0:-1:0: 20: CONST :4:60:0:-1:1: 20: CONST :4:60:0:-1:2: 07: == :2:32:2:01:0: 33: GOTOF :4:00:0:00:7: 20: CONST :4:60:0:-1:3: 32: GOTO :4:00:0:00:10: 20: CONST :4:60:0:-1:4: 20: CONST :4:60:0:-1:5: 14: + :3:36:2:01:0: 13: CONDEX3 :3:16:2:01:0: 14: + :3:36:2:01:0: Value: 6 ---------------------------------------------------- Expression: 'erf(1)' 20: CONST :4:60:0:-1:0: 47: erf :5:60:1:01:0: Value: 0.842701 ---------------------------------------------------- Expression: 'erfc(1)' 20: CONST :4:60:0:-1:0: 48: erfc :5:60:1:01:0: Value: 0.157299 ---------------------------------------------------- Expression: '+-(25*30+2)--(75+2)' Value(3.5, 0): -675, -675 ---------------------------------------------------- Expression: '1+2-3' Value(3.5, 0): 0, 0 ---------------------------------------------------- Expression: '1+2/3' Value(3.5, 0): 1.66667, 1.66667 ---------------------------------------------------- Expression: '2^2^3' Value(3.5, 0): 256, 256 ---------------------------------------------------- Expression: '1+(2+3' Missing closing right parenthesis at: '1+(2+3''' Value(3.5, 0): 0, 0 ---------------------------------------------------- Expression: '1' Value(3.5, 0): 1, 1 ---------------------------------------------------- Expression: '' Unexpected EOS Value(3.5, 0): 0, 0 ---------------------------------------------------- Expression: 'pi' Value(3.5, 0): 3.14159, 3.14159 ---------------------------------------------------- Expression: 'sin(1)' Value(3.5, 0): 0.841471, 0.841471 ---------------------------------------------------- Expression: 'cop(1)' Unknown function name cop at: 'cop''(1)' Value(3.5, 0): 0, 0 ---------------------------------------------------- Expression: 'pi(2)+1.5' Value(3.5, 0): 7.78319, 7.78319 ---------------------------------------------------- Expression: 'sin(1,2)' Incorrect number of arguments in function at: 'sin(1,2)''' Value(3.5, 0): 0, 0 ---------------------------------------------------- Expression: '2*p' Value(3.5, 0): 3, 3 ---------------------------------------------------- Expression: '2*p1' Value(3.5, 0): 5, 5 ---------------------------------------------------- Expression: '2*p[1]' Value(3.5, 0): 3, 3 ---------------------------------------------------- Expression: '2*x0' Value(3.5, 0): 7, 0 ---------------------------------------------------- Expression: 'sin(sin(2))' Value(3.5, 0): 0.789072, 0.789072 ---------------------------------------------------- Expression: 'x==0' Value(3.5, 0): 0, 1 ---------------------------------------------------- Expression: '(x==0)+1' Value(3.5, 0): 1, 2 ---------------------------------------------------- Expression: '(x==0)+1' Value(3.5, 0): 1, 2 ---------------------------------------------------- Expression: '((x==0) * 1)+((x!=0) * sin(x+(x==0)*1)/(x+(1)))' Value(3.5, 0): -0.0779518, 1 ---------------------------------------------------- Expression: '1+(1==2)?5:8+20' Value(3.5, 0): 5, 5 ---------------------------------------------------- Expression: '1+(2==2)?5:(8+20)' Value(3.5, 0): 5, 5 ---------------------------------------------------- Expression: '1+((1==2)?5:8)+20' Value(3.5, 0): 29, 29 ---------------------------------------------------- Expression: '1+((2==2)?5:(8+20))' Value(3.5, 0): 6, 6 ---------------------------------------------------- Expression: 'erf(1)' Value(3.5, 0): 0.842701, 0.842701 ---------------------------------------------------- Expression: 'erfc(1)' Value(3.5, 0): 0.157299, 0.157299 ---------------------------------------------------- Expression: '+-(25*30+2)--(75+2)' Value(3.5, 0): (-675, []), (-675, []) ---------------------------------------------------- Expression: '1+2-3' Value(3.5, 0): (0, []), (0, []) ---------------------------------------------------- Expression: '1+2/3' Value(3.5, 0): (1.66667, []), (1.66667, []) ---------------------------------------------------- Expression: '2^2^3' Value(3.5, 0): (256, []), (256, []) ---------------------------------------------------- Expression: '1+(2+3' Missing closing right parenthesis at: '1+(2+3''' Value(3.5, 0): (0, []), (0, []) ---------------------------------------------------- Expression: '1' Value(3.5, 0): (1, []), (1, []) ---------------------------------------------------- Expression: '' Unexpected EOS Value(3.5, 0): (0, []), (0, []) ---------------------------------------------------- Expression: 'pi' Value(3.5, 0): (3.14159, []), (3.14159, []) ---------------------------------------------------- Expression: 'sin(1)' Value(3.5, 0): (0.841471, []), (0.841471, []) ---------------------------------------------------- Expression: 'cop(1)' Unknown function name cop at: 'cop''(1)' Value(3.5, 0): (0, []), (0, []) ---------------------------------------------------- Expression: 'pi(2)+1.5' Value(3.5, 0): (7.78319, []), (7.78319, []) ---------------------------------------------------- Expression: 'sin(1,2)' Incorrect number of arguments in function at: 'sin(1,2)''' Value(3.5, 0): (0, []), (0, []) ---------------------------------------------------- Expression: '2*p' Value(3.5, 0): (3, [2]), (3, [2]) ---------------------------------------------------- Expression: '2*p1' Value(3.5, 0): (5, [0, 2]), (5, [0, 2]) ---------------------------------------------------- Expression: '2*p[1]' Value(3.5, 0): (3, [2]), (3, [2]) ---------------------------------------------------- Expression: '2*x0' Value(3.5, 0): (7, []), (0, []) ---------------------------------------------------- Expression: 'sin(sin(2))' Value(3.5, 0): (0.789072, []), (0.789072, []) ---------------------------------------------------- Expression: 'x==0' Value(3.5, 0): (0, []), (1, []) ---------------------------------------------------- Expression: '(x==0)+1' Value(3.5, 0): (1, []), (2, []) ---------------------------------------------------- Expression: '(x==0)+1' Value(3.5, 0): (1, []), (2, []) ---------------------------------------------------- Expression: '((x==0) * 1)+((x!=0) * sin(x+(x==0)*1)/(x+(1)))' Value(3.5, 0): (-0.0779518, []), (1, []) ---------------------------------------------------- Expression: '1+(1==2)?5:8+20' Value(3.5, 0): (5, []), (5, []) ---------------------------------------------------- Expression: '1+(2==2)?5:(8+20)' Value(3.5, 0): (5, []), (5, []) ---------------------------------------------------- Expression: '1+((1==2)?5:8)+20' Value(3.5, 0): (29, []), (29, []) ---------------------------------------------------- Expression: '1+((2==2)?5:(8+20))' Value(3.5, 0): (6, []), (6, []) ---------------------------------------------------- Expression: 'erf(1)' Value(3.5, 0): (0.842701, []), (0.842701, []) ---------------------------------------------------- Expression: 'erfc(1)' Value(3.5, 0): (0.157299, []), (0.157299, []) ---------------------------------------------------- casacore-3.7.1/scimath/Functionals/test/tFunctionHolder.cc000066400000000000000000000124231476623553700236400ustar00rootroot00000000000000//# tFunctionHolder.cc: Test the one-dimensional scaled polynomial class //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Make near zero zero Double Y(const Double in) { return (abs(in)<1e-15 ? Double(0.0) : in); } AutoDiff Y(AutoDiff in) { in.value() = Y(in.value()); for (uInt i=0; i fn(5, 7, 3); for (Double x=3; x<11.2; x+=1.0) cout << "x=" << Y(x) << ": " << Y(fn(x)) << endl; UnaryFunction > fnd; fnd[0] = AutoDiff(5, 3, 0); fnd[1] = AutoDiff(7, 3, 1); fnd[2] = AutoDiff(3, 3, 2); for (Double x=3; x<11.2; x+=1.0) cout << "x=" << Y(x) << ": " << Y(fnd(x)) << endl; } cout << "------------------------ Dirac delta ----------------" << endl; { DiracDFunction fn(5, 7); for (Double x=6; x<8.2; x+=1.0) cout << "x=" << Y(x) << ": " << Y(fn(x)) << endl; DiracDFunction > fnd; fnd[0] = AutoDiff(5, 2, 0); fnd[1] = AutoDiff(7, 2, 1); for (Double x=6; x<8.2; x+=1.0) cout << "x=" << Y(x) << ": " << Y(fnd(x)) << endl; } cout << "------------------------ Normal noise ---------------" << endl; { GNoiseFunction fn(0, 2.0); for (uInt i=0; i<10; ++i) cout << fn() << endl; GNoiseFunction > fnd; for (uInt i=0; i<10; ++i) cout << fnd() << endl; } cout << "------------------------ Kaiser-Bessel --------------" << endl; { KaiserBFunction fn; for (Double x=-1.2; x<1.21; x+=0.2) cout << "x=" << Y(x) << ": " << Y(fn(x)) << endl; for (Double x=6; x<8.2; x+=1.0) cout << "x=" << Y(x) << ": " << Y(fn(x)) << endl; KaiserBFunction > fnd; fnd[0] = AutoDiff(1, 4, 0); fnd[1] = AutoDiff(0, 4, 1); fnd[2] = AutoDiff(1, 4, 2); fnd[3] = AutoDiff(2.5, 4, 2); for (Double x=-1.2; x<1.21; x+=0.2) cout << "x=" << Y(x) << ": " << Y(fnd(x)) << endl; } cout << "------------------------ sinc -----------------------" << endl; { SincFunction fn; for (Double x=-1.2; x<1.21; x+=0.2) cout << "x=" << Y(x) << ": " << Y(fn(x)) << endl; SincFunction > fnd; fnd[0] = AutoDiff(1, 3, 0); fnd[1] = AutoDiff(0, 3, 1); fnd[2] = AutoDiff(1, 3, 2); for (Double x=-1.19999; x<1.21; x+=0.2) cout << "x=" << Y(x) << ": " << Y(fnd(x)) << endl; } cout << "------------------------ Gaussian1D -----------------" << endl; { Gaussian1D fn; FunctionHolder fh(fn); for (Double x=-1.2; x<1.21; x+=0.2) cout << "x=" << Y(x) << ": " << fh.asFunction()(x) << endl; Gaussian1D > fnd; fnd[0] = AutoDiff(1, 3, 0); fnd[1] = AutoDiff(0, 3, 1); fnd[2] = AutoDiff(1, 3, 2); /// FunctionHolder > fhd(fnd); for (Double x=-1.2; x<1.21; x+=0.2) cout << "x=" << Y(x) << ": " << /// fhd.asFunction()(x) << endl; Y(fnd(x)) << endl; } cout << "-----------------------------------------------------" << endl; cout << "OK" << endl; return 0; } casacore-3.7.1/scimath/Functionals/test/tFunctionHolder.out000066400000000000000000000066451476623553700240730ustar00rootroot00000000000000---------------- test FunctionHolder --------------- ------------------------ Unary --------------------- x=3: 0 x=4: 2.5 x=5: 5 x=6: 5 x=7: 5 x=8: 5 x=9: 5 x=10: 2.5 x=11: 0 x=3: (0, []) x=4: (2.5, [0.5, 0, 0]) x=5: (5, [1, 0, 0]) x=6: (5, [1, 0, 0]) x=7: (5, [1, 0, 0]) x=8: (5, [1, 0, 0]) x=9: (5, [1, 0, 0]) x=10: (2.5, [0.5, 0, 0]) x=11: (0, []) ------------------------ Dirac delta ---------------- x=6: 0 x=7: 5 x=8: 0 x=6: (0, []) x=7: (5, [1, 0]) x=8: (0, []) ------------------------ Normal noise --------------- -0.569721 -2.0255 2.0087 -0.445608 0.302747 -0.687715 -0.892452 0.302263 -1.51307 -0.610897 (-0.402854, []) (-1.43224, []) (1.42036, []) (-0.315093, []) (0.214074, []) (-0.486288, []) (-0.631059, []) (0.213732, []) (-1.0699, []) (-0.43197, []) ------------------------ Kaiser-Bessel -------------- x=-1.2: 0.0879273 x=-1: 0.0227113 x=-0.8: 0.0879273 x=-0.6: 0.286772 x=-0.4: 0.588771 x=-0.2: 0.87857 x=0: 1 x=0.2: 0.87857 x=0.4: 0.588771 x=0.6: 0.286772 x=0.8: 0.0879273 x=1: 0.0227113 x=1.2: 0.0879273 x=6: 1 x=7: 0.0227113 x=8: 1 x=-1.2: (0.0879273, [0.0879273, 0.659253, -0.897684, 0]) x=-1: (0.0227113, [0.0227113, 0, -0.0597192, 0]) x=-0.8: (0.0879273, [0.0879273, -0.659253, 0.420821, 0]) x=-0.6: (0.286772, [0.286772, -1.30976, 0.609155, 0]) x=-0.4: (0.588771, [0.588771, -1.61021, 0.485836, 0]) x=-0.2: (0.87857, [0.87857, -1.14539, 0.170811, 0]) x=0: (1, [1, 0, 0, 0]) x=0.2: (0.87857, [0.87857, 1.14539, 0.170811, 0]) x=0.4: (0.588771, [0.588771, 1.61021, 0.485836, 0]) x=0.6: (0.286772, [0.286772, 1.30976, 0.609155, 0]) x=0.8: (0.0879273, [0.0879273, 0.659253, 0.420821, 0]) x=1: (0.0227113, [0.0227113, 0, -0.0597192, 0]) x=1.2: (0.0879273, [0.0879273, -0.659253, -0.897684, 0]) ------------------------ sinc ----------------------- x=-1.2: -0.155915 x=-1: 0 x=-0.8: 0.233872 x=-0.6: 0.504551 x=-0.4: 0.756827 x=-0.2: 0.935489 x=0: 1 x=0.2: 0.935489 x=0.4: 0.756827 x=0.6: 0.504551 x=0.8: 0.233872 x=1: 0 x=1.2: -0.155915 x=-1.19999: (-0.155909, [0, -0.544276, 0.653126]) x=-0.99999: (1.00001e-05, [0, -1.00002, 1.00001]) x=-0.79999: (0.233885, [0, -1.30362, 1.04288]) x=-0.59999: (0.504565, [0, -1.35594, 0.813552]) x=-0.39999: (0.756838, [0, -1.11951, 0.447791]) x=-0.19999: (0.935496, [0, -0.632332, 0.12646]) x=1e-05: (1, [0, 3.28987e-05, 3.28987e-10]) x=0.20001: (0.935483, [0, 0.632391, 0.126484]) x=0.40001: (0.756816, [0, 1.11954, 0.447828]) x=0.60001: (0.504538, [0, 1.35595, 0.813584]) x=0.80001: (0.233859, [0, 1.3036, 1.04289]) x=1.00001: (-9.9999e-06, [0, 0.99998, 0.99999]) x=1.20001: (-0.15592, [0, 0.544227, 0.653078]) ------------------------ Gaussian1D ----------------- x=-1.2: 0.018453 x=-1: 0.0625 x=-0.8: 0.169576 x=-0.6: 0.368567 x=-0.4: 0.641713 x=-0.2: 0.895025 x=0: 1 x=0.2: 0.895025 x=0.4: 0.641713 x=0.6: 0.368567 x=0.8: 0.169576 x=1: 0.0625 x=1.2: 0.018453 x=-1.2: (0.018453, [0.018453, -0.12279, 0.147348]) x=-1: (0.0625, [0.0625, -0.346574, 0.346574]) x=-0.8: (0.169576, [0.169576, -0.752261, 0.601809]) x=-0.6: (0.368567, [0.368567, -1.22626, 0.735758]) x=-0.4: (0.641713, [0.641713, -1.42336, 0.569346]) x=-0.2: (0.895025, [0.895025, -0.992615, 0.198523]) x=0: (1, [1, 0, 0]) x=0.2: (0.895025, [0.895025, 0.992615, 0.198523]) x=0.4: (0.641713, [0.641713, 1.42336, 0.569346]) x=0.6: (0.368567, [0.368567, 1.22626, 0.735758]) x=0.8: (0.169576, [0.169576, 0.752261, 0.601809]) x=1: (0.0625, [0.0625, 0.346574, 0.346574]) x=1.2: (0.018453, [0.018453, 0.12279, 0.147348]) ----------------------------------------------------- OK casacore-3.7.1/scimath/Functionals/test/tFunctionOrder.cc000066400000000000000000000044011476623553700234730ustar00rootroot00000000000000//# tFunctionOrder.cc: Test the one-dimensional scaled polynomial class //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include int main() { cout << "---------------- test FunctionOrder ---------------" << endl; { FunctionOrder x; FunctionOrder z; String errormsg; Record y; cout << "x: " << x << endl; cout << "To: " << x.toRecord(errormsg, y) << endl; cout << "From: "; cout << z.fromRecord(errormsg, y) << ": " << z << endl; cout << "-----------------------------------------------------" << endl; x.getInt(0) = 0; x.getInt(1) = 5; cout << "x: " << x << endl; cout << "To: " << x.toRecord(errormsg, y) << endl; cout << "From: " << z.fromRecord(errormsg, y) << ": " << z << endl; cout << "-----------------------------------------------------" << endl; } cout << "-----------------------------------------------------" << endl; cout << "OK" << endl; return 0; } casacore-3.7.1/scimath/Functionals/test/tFunctionOrder.out000066400000000000000000000005651476623553700237240ustar00rootroot00000000000000---------------- test FunctionOrder --------------- x: [[], [], "", 0, [], [], []] To: 1 From: 1: [[], [], "", 0, [], [], []] ----------------------------------------------------- x: [[0, 5], [], "", 0, [], [], []] To: 1 From: 1: [[0, 5], [], "", 0, [], [], []] ----------------------------------------------------- ----------------------------------------------------- OK casacore-3.7.1/scimath/Functionals/test/tFunctionWrapper.cc000066400000000000000000000061731476623553700240500ustar00rootroot00000000000000//# tFunctionWrapper.cc: Test function wrappers //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include // Some C++ functions static Double func0(const Vector &) {return 1;} // 1 static Double func1(const Vector &x) {return x(0);} // x static Double func2(const Vector &x) {return sin(x(1));} // sin(y) static Double func3(const Vector &x) {return x(0)*x(0);} // x^2 /*static void myfnc(Vector &y, const Double x) { y(0) = 1; for (uInt i=1; i Func0(func0,2); FunctionWrapper Func1(func1,2); FunctionWrapper Func2(func2,2); FunctionWrapper Func3(func3,2); CombiFunction combination; // form linear combination of functions // f(x,y) = a0 + a1*x+ a2*sin(y) + a3*x*x Vector z0(2); z0[0] = 2; z0[1] = 3; combination.addFunction(Func0); combination.addFunction(Func1); combination.addFunction(Func2); combination.addFunction(Func3); // Now use this combination to generate some fake data combination[0] = 4; combination[1] = 5; combination[2] = 6; combination[3] = 0.2; cout << "******* test one *************" << endl; cout << "Combination: " << endl; cout << 4+5*z0[0]+6*sin(z0[1])+0.2*z0[0]*z0[0] << ", " << combination(z0) << endl; AlwaysAssertExit(near(4+5*z0[0]+6*sin(z0[1])+0.2*z0[0]*z0[0], combination(z0))); } cout << "OK" << endl; return 0; } casacore-3.7.1/scimath/Functionals/test/tGaussian1D.cc000066400000000000000000000222561476623553700226610ustar00rootroot00000000000000//# tGaussian1D: Test the Gaussian1D class //# Copyright (C) 1995,1996,1997,1999,2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include int main() { Gaussian1D null; AlwaysAssertExit(null.height() == 1.0 && null.center() == 0.0 && null.width() == 1.0); AlwaysAssertExit(near(null(0.5), 0.5) && near(null(0.0), 1.0)); // name() cout << "Name of function: " << null.name() << endl; AlwaysAssertExit(null.name() == "gaussian1d"); // Gaussian1D(const T& h, const T& c, const T& w); // T height() const // void setHeight(const T & height) // T flux() const; // void setFlux(const T & flux); // T center() const // setCenter(const T & center) // T width() const; // void setWidth(const T & width); Gaussian1D gauss1(4.0, 6.0, 8.0); AlwaysAssertExit(gauss1.height() == 4.0 && gauss1.center() == 6.0 && gauss1.width() == 8.0); const Gaussian1D &cgauss1 = gauss1; AlwaysAssertExit(cgauss1.height() == 4.0 && cgauss1.center() == 6.0 && cgauss1.width() == 8.0); gauss1.setHeight(2.0); AlwaysAssertExit(near(gauss1.flux(), 2.0*8.0*sqrt(M_PI/log(16.0)))); gauss1.setCenter(3.0); gauss1.setWidth(4.0); gauss1.setFlux(1.0); AlwaysAssertExit(gauss1[Gaussian1D::WIDTH] == 4.0 && gauss1[Gaussian1D::CENTER] == 3.0 && near(gauss1[Gaussian1D::HEIGHT] , 1.0/4.0/sqrt(M_PI/log(16.0)))); gauss1[Gaussian1D::HEIGHT] = 2.0; // << cout << "Function Parameters: " << gauss1 << endl; // T operator()(const T &x) const; AlwaysAssertExit(near(gauss1(3.0), 2.0)); Vector xvec(1); xvec = 5.0; cout << "Value at 5: " << gauss1(xvec(0)) << endl; /// // Copy constructor Gaussian1D g1c(gauss1); cout << "Copy: " << g1c << "; f(5) = " << g1c(xvec(0)) << endl; Gaussian1D > g1adc(gauss1); cout << "AD: " << g1adc << endl << "f(5) = " << g1adc(xvec(0)) << endl; Gaussian1D g1cb(g1adc); cout << "Copy back: " << g1cb << endl << "f(5) = " << g1cb(xvec(0)) << endl; AlwaysAssertExit(near(gauss1(xvec(0)), 2.0 / 2.0)); AlwaysAssertExit(near(g1c(xvec(0)), 2.0 / 2.0)); AlwaysAssertExit(near(g1cb(xvec(0)), 2.0 / 2.0)); xvec = -1.0; AlwaysAssertExit(near(gauss1(xvec(0)), 2.0/2.0/2.0/2.0/2.0)); // Test Auto differentiation - specialized Gaussian1D > gauss5(AutoDiff(4.0), AutoDiff(6.0), AutoDiff(8.0)); AlwaysAssertExit(gauss5.height() == 4.0 && gauss5.center() == 6.0 && gauss5.width() == 8.0); const Gaussian1D > &cgauss5 = gauss5; AlwaysAssertExit(cgauss5.height() == 4.0 && cgauss5.center() == 6.0 && cgauss5.width() == 8.0); gauss5.setHeight(AutoDiff(2.0)); AlwaysAssertExit(near(gauss5.flux(), 2.0*8.0*sqrt(M_PI/log(16.0)))); gauss5.setCenter(AutoDiff(3.0,3,1)); gauss5.setWidth(AutoDiff(4.0,3,2)); gauss5.setFlux(AutoDiff(1.0,3,0)); AlwaysAssertExit(gauss5[Gaussian1D >::WIDTH] == 4.0 && gauss5[Gaussian1D >::CENTER] == 3.0 && near(gauss5[Gaussian1D >::HEIGHT] , 1.0/4.0/sqrt(M_PI/log(16.0)))); gauss5.setHeight(AutoDiff(2.0,3,0)); cout << "Specialized(3): " << gauss5(3.0) << endl; cout << "Specialized(5): " << gauss5(5.0) << endl; AlwaysAssertExit(near(gauss1(3.0), 2.0)); // Test Auto differentiation Gaussian1D > gauss6(AutoDiffA(4.0), AutoDiffA(6.0), AutoDiffA(8.0)); AlwaysAssertExit(gauss6.height() == 4.0 && gauss6.center() == 6.0 && gauss6.width() == 8.0); const Gaussian1D > &cgauss6 = gauss6; AlwaysAssertExit(cgauss6.height() == 4.0 && cgauss6.center() == 6.0 && cgauss6.width() == 8.0); gauss6.setHeight(AutoDiffA(2.0)); AlwaysAssertExit(near(gauss6.flux(), 2.0*8.0*sqrt(M_PI/log(16.0)))); gauss6.setCenter(AutoDiffA(3.0,3,1)); gauss6.setWidth(AutoDiffA(4.0,3,2)); gauss6.setFlux(AutoDiffA(1.0,3,0)); AlwaysAssertExit(gauss6[Gaussian1D >::WIDTH] == 4.0 && gauss6[Gaussian1D >::CENTER] == 3.0 && near(gauss6[Gaussian1D >::HEIGHT] , 1.0/4.0/sqrt(M_PI/log(16.0)))); gauss6.setHeight(AutoDiffA(2.0,3,0)); cout << "Generic(3): " << gauss6(AutoDiffA(3.0)) << endl; cout << "Generic(5): " << gauss6(AutoDiffA(5.0)) << endl; cout << "Generic(1): " << gauss6(AutoDiffA(1.0)) << endl; AlwaysAssertExit(near(gauss1(3.0), 2.0)); // Gaussian1D(const Gaussian1D &other); // Gaussian1D &operator=(const Gaussian1D &other); // virtual uInt nAvailableParams() const; // virtual void setAvailableParam(uInt which, const Type &value); // virtual Type getAvailableParam(uInt which) const; // virtual void setAvailableParamMask(uInt which, const Bool mask); // virtual Bool getAvailableParamMask(uInt which) const; Gaussian1D gauss2(gauss1); Gaussian1D gauss3; gauss3 = gauss2; AlwaysAssertExit(gauss1.nparameters() == 3); Vector parms = gauss1.parameters().getParameters(); AlwaysAssertExit(parms(0) == 2.0 && parms(1) == 3.0 && parms(2) == 4.0); AlwaysAssertExit(allEQ(parms, gauss2.parameters().getParameters()) && allEQ(parms, gauss3.parameters().getParameters())); gauss1.mask(Gaussian1D::CENTER) = False; AlwaysAssertExit(gauss1.parameters().nMaskedParameters() == 2); Vector parms2 = gauss1.parameters().getMaskedParameters(); AlwaysAssertExit(parms2(0) == 2.0 && parms2(1) == 4.0); gauss1.mask(Gaussian1D::CENTER) = True; gauss1[0] = 1.0; gauss1[1] = 2.0; gauss1[2] = 3.0; AlwaysAssertExit(gauss1.height() == 1.0 && gauss1.center() == 2.0 && gauss1.width() == 3.0); AlwaysAssertExit(near(gauss5(5.0).value(), gauss6(AutoDiffA(5.0)).value()) && allNear(gauss5(5.0).derivatives(), gauss6(AutoDiffA(5.0)).derivatives(), 1e-13)); parms = 11.0; gauss1.parameters().setParameters(parms); AlwaysAssertExit(allEQ(gauss1.parameters().getParameters(), 11.0)); // clone(); // ~Gaussian1D(); cout << "Cloning:" << endl; cout << "Original value f(1): " << gauss1(1.0) << endl; AlwaysAssertExit(nearAbs(gauss1(1.0), 1.11238, 1e-5)) Function *gauss4ptr = gauss1.clone(); cout << "f.clone(1): " << (*gauss4ptr)(1.0) << endl; AlwaysAssertExit(near(gauss1(1.0), (*gauss4ptr)(1.0))) Function *gauss4a = gauss1.cloneNonAD(); cout << "f.cloneNonAD(1): " << (*gauss4a)(1.0) << endl; AlwaysAssertExit(near(gauss1(1.0), (*gauss4a)(1.0))) Function > *gauss4b = gauss1.cloneAD(); cout << "f.cloneAD(1): " << (*gauss4b)(1.0) << endl; AlwaysAssertExit(near(gauss1(1.0), (*gauss4b)(1.0).value())) Function > *gauss4ca = gauss1.cloneAD(); Function > *gauss4c = gauss4ca->cloneAD(); cout << "f.cloneAD.cloneAD(1): " << (*gauss4c)(1.0) << endl; AlwaysAssertExit(near(gauss1(1.0), (*gauss4c)(1.0).value())) Function > *gauss4da = gauss1.cloneAD(); Function *gauss4d = gauss4da->cloneNonAD(); cout << "f.cloneAD.cloneNonAD(1): " << (*gauss4d)(1.0) << endl; AlwaysAssertExit(near(gauss1(1.0), (*gauss4d)(1.0))) AlwaysAssertExit(allEQ(gauss4ptr->parameters().getParameters(), 11.0)); delete gauss4ptr; delete gauss4a; delete gauss4b; delete gauss4c; delete gauss4ca; delete gauss4da; delete gauss4d; cout << "OK" << endl; return 0; } casacore-3.7.1/scimath/Functionals/test/tGaussian2D.cc000066400000000000000000000212131476623553700226520ustar00rootroot00000000000000//# tGaussian2D.cc: Test the Gaussian2D class //# Copyright (C) 1996,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main() { try { Bool anyFailures = False; /* { Bool failed = False; Gaussian2D g; if (g.ndim() != 2) failed = True; Vector z(2); z = 0; Float sum = 0; Float inc = 0.1; for (Float x = -3; x < 3.01; x+=inc) { z(0) = x; for (Float y = -3; y < 3.01; y+=inc) { z(1) = y; if (!near(Double(g(z)), exp(-log(16.0)*(z(0)*z(0)+z(1)*z(1))), 1E-4)) { failed = True; cout << "Expected value for g(" << z << ") is " << exp(-log(16.0)*(z(0)*z(0)+z(1)*z(1))) << " calculated value is " << g(z) << endl; } sum += g(z); } } if (failed) cout << "Failed"; else cout << "Passed"; cout << " the default Gaussian test" << endl; if (!failed) { if (!near(Double(sum*inc*inc), C::pi/log(16.0), 1E-6)) { failed = True; cout << "Failed (value was " << sum*inc*inc << " instead of " << C::pi/log(16.0) << ")" ; } else cout << "Passed"; cout << " the total flux test" << endl; } if (!failed) { if (!near(C::pi/log(16.0), Double(g.flux()), 1E-7)) { failed = True; } else { g.setFlux(2*g.flux()); if (!near(g(0,0), 2.0f)) failed = True; } if (!failed) cout << "Passed"; else cout << "Failed"; cout << " the set/get Flux test" << endl; } if (failed) anyFailures = True; } { Bool failed = False; Gaussian2D g; g.setHeight(2.0); if (near(g.height(), 2.0)) cout << "Passed"; else { cout << "Failed"; failed=True; } cout << " the set/get height test" << endl; g.setXcenter(-10.0); if (near(g.xCenter(), -10.0)) cout << "Passed"; else { cout << "Failed"; failed=True; } cout << " the set/get X centre test" << endl; g.setYcenter(0.1); if (near(g.yCenter(), 0.1)) cout << "Passed"; else { cout << "Failed"; failed=True; } cout << " the set/get Y centre test" << endl; Vector c(2); c(0) = -2.0; c(1) = .5; g.setCenter(c); if (allNear(g.center(), c, 1E-10)) cout << "Passed"; else { cout << "Failed"; failed=True; } cout << " the set/get centre test" << endl; g.setMajorAxis(10.0); if (near(g.majorAxis(), 10.0)) cout << "Passed"; else { cout << "Failed"; failed=True; } cout << " the set/get major axis length test" << endl; g.setMinorAxis(0.1); if (near(g.minorAxis(), 0.1)) cout << "Passed"; else { cout << "Failed"; failed=True; } cout << " the set/get minor axis length test" << endl; Vector w(2); w(0) = 2.0; w(1) = 1.0; g.setWidth(w); if (allNear(g.width(), w, 1E-10)) cout << "Passed"; else { cout << "Failed"; failed=True; } cout << " the set/get width test" << endl; g.setAxialRatio(1.0); if (near(g.axialRatio(), 1.0)) cout << "Passed"; else { cout << "Failed"; failed=True; } cout << " the set/get axial ratio test" << endl; g.setPA(-C::pi_2); if (near(g.PA(), C::pi_2)) cout << "Passed"; else { cout << "Failed"; failed=True; } cout << " the set/get PA test" << endl; if (!failed) { /// if (g.nAvailableParams() != 6) failed = True; /// Vector parms = g.getAvailableParams(); Vector parms = g.parameters().getParameters(); Vector expectedParms(6); expectedParms(0) = 2.; expectedParms(1) = -2.; expectedParms(2) = 0.5; expectedParms(3) = 2.0; expectedParms(4) = 1.0; expectedParms(5) = -C::pi_2; if (!allNear(expectedParms, parms, 1E-12)) failed = True; parms = -1.0*parms; /// g.setAvailableParams(parms); g.parameters().setParameters(parms); /// parms = g.getAvailableParams(); parms = g.parameters().getParameters(); if (!allNear(-1.0*expectedParms, parms, 1E-10)) failed = True; // Mask parameters 5 and 6 */ /* g.setAvailableParamMask(4, False); g.setAvailableParamMask(5, False); for (uInt i = 0; i < 4; i++) { if (g.getAvailableParamMask(i) == False) failed = True; } if (g.getAvailableParamMask(4) == True) failed = True; if (g.getAvailableParamMask(5) == True) failed = True; */// /* if (!failed) cout << "Passed"; else { cout << "Failed"; failed=True; } cout << " the set/get parameters test" << endl; } if (failed) anyFailures = True; }*/ { Bool failed = False; Vector mean(2), fwhm(2); mean(0) = .5; mean(1) = -1; fwhm(0) = 2; fwhm(1) = .5; Double pa = M_PI/6, height = 2; Gaussian2D g(height, mean, fwhm, pa), g1; Gaussian2D g2(height, mean(0), mean(1), fwhm(0), fwhm(1)/fwhm(0), pa); Double x = mean(0), y = mean(1); if (!near(g(x,y), height)) failed = True; g1 = g; x -= sin(pa)*fwhm(0)/2; y += cos(pa)*fwhm(0)/2; if (!near(g1(x,y), height/2.0, 1E-6)) failed = True; if (!near(g2(x,y), height/2.0, 1E-6)) failed = True; Gaussian2D g3(g); x = mean(0) - cos(pa)*fwhm(1)/2; y = mean(1) - sin(pa)*fwhm(1)/2; if (!near(g3(x,y), height/2.0, 1E-6)) failed = True; if (!failed) cout << "Passed"; else { cout << "Failed"; failed=True; } cout << " the arbitrary Gaussian test" << endl; if (failed) anyFailures = True; // Test Auto differentiation - specialized Double fww(fwhm[1]/fwhm[0]); AutoDiff adheight(height,6,0); AutoDiff admean0(mean[0],6,1); AutoDiff admean1(mean[1],6,2); AutoDiff adfwhm0(fwhm[0],6,3); AutoDiff adfww(fww,6,4); AutoDiff adpa(pa,6,5); Gaussian2D > g4(adheight, admean0, admean1, adfwhm0, adfww, adpa); cout << "Value: " << g2(x,y) << endl; Double adx(x); Double ady(y); cout << "Specialized: " << g4(adx, ady) << endl; // Test Auto differentiation AutoDiffA adaheight(height,6,0); AutoDiffA adamean0(mean[0],6,1); AutoDiffA adamean1(mean[1],6,2); AutoDiffA adafwhm0(fwhm[0],6,3); AutoDiffA adafww(fww,6,4); AutoDiffA adapa(pa,6,5); Gaussian2D > g5(adaheight, adamean0, adamean1, adafwhm0, adafww, adapa); AutoDiffA adax(x); AutoDiffA aday(y); cout << "Generic: " << g5(adax, aday) << endl; AlwaysAssertExit(near(g4(adx, ady).value(), g5(adax, aday).value()) && allNearAbs(g4(adx, ady).derivatives(), g5(adax, aday).derivatives(), 1e-13)); } if (anyFailures) { cout << "FAIL" << endl; return 1; } else { cout << "OK" << endl; return 0; } } catch (std::exception& x) { cerr << x.what() << endl; cout << "Failed" << endl; return 1; } } casacore-3.7.1/scimath/Functionals/test/tGaussian3D.cc000066400000000000000000000076741476623553700226720ustar00rootroot00000000000000//# tGaussian3D.cc: Test program for class Gaussian3D //# Copyright (C) 2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include int main() { Double height = 9.0; Double xCen = 0.0; Double yCen = 0.0; Double zCen = 0.0; Double xWidth = 1.0; Double yWidth = 1.0; Double zWidth = 1.0; Double theta = 0.0; Double phi = 0.0; // Specialized (hand-coded derivatives) AutoDiff heightAD(height,9,0); AutoDiff xCenAD(xCen,9,1); AutoDiff yCenAD(yCen,9,2); AutoDiff zCenAD(zCen,9,3); AutoDiff xWidthAD(xWidth,9,4); AutoDiff yWidthAD(yWidth,9,5); AutoDiff zWidthAD(zWidth,9,6); AutoDiff thetaAD(theta,9,7); AutoDiff phiAD(phi,9,8); Gaussian3D > gauss3dAD(heightAD, xCenAD, yCenAD, zCenAD, xWidthAD, yWidthAD, zWidthAD, thetaAD, phiAD); // Automatic AutoDiffA heightADA(height,9,0); AutoDiffA xCenADA(xCen,9,1); AutoDiffA yCenADA(yCen,9,2); AutoDiffA zCenADA(zCen,9,3); AutoDiffA xWidthADA(xWidth,9,4); AutoDiffA yWidthADA(yWidth,9,5); AutoDiffA zWidthADA(zWidth,9,6); AutoDiffA thetaADA(theta,9,7); AutoDiffA phiADA(phi,9,8); Gaussian3D > gauss3dADA(heightADA, xCenADA, yCenADA, zCenADA, xWidthADA, yWidthADA, zWidthADA, thetaADA, phiADA); // /* uInt npar = argc - 1; if (npar > 9) npar = 9; for (uInt i = 0; i < npar; i++) { gauss3dAD[i] = atof(argv[i+1]); //set parameter } */ for (Double z = -0.25; z < 0.26; z+=0.25) { for (Double y = -1.0; y < 1.01; y+=0.25) { for (Double x = -1.0; x < 1.01; x+=0.25) { cout << "[" << Int(gauss3dAD(x,y,z).value()) << Int(gauss3dADA(x,y,z).value()) << "] "; AlwaysAssertExit(near(gauss3dAD(x,y,z).value(), gauss3dADA(x,y,z).value())); } cout << endl; } cout << endl; } cout << endl << endl; // Check specialized and auto-derivatives for (Double z = -0.25; z < 0.26; z+=0.25) { for (Double y = -1.0; y < 1.01; y+=0.25) { for (Double x = -1.0; x < 1.01; x+=0.25) { AlwaysAssertExit(allNearAbs(gauss3dAD(x,y,z).derivatives(), gauss3dADA(x,y,z).derivatives(), 1.0e-13)); } } } cout << endl; return 0; } casacore-3.7.1/scimath/Functionals/test/tGaussianND.cc000066400000000000000000000307161476623553700227160ustar00rootroot00000000000000//# tGaussianND.cc: //# Copyright (C) 1996,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include int main(){ try { Bool anyFailures = False; { Bool failed = False; GaussianND default2D; Vector z(2); z = 0; Float sum = 0; Float inc = .5; for (Float x = -5; x < 5; x+=inc) { z(0) = x; for (Float y = -5; y < 5; y+=inc) { z(1) = y; if (!near(Double(default2D(z)), exp(-(z(0)*z(0)+z(1)*z(1))/2)/(2.*M_PI),1E-5)) { failed = True; cout << "Expected value for f(" << z << ") is " << exp(-(z(0)*z(0)+z(1)*z(1))/2)/(2.0*M_PI) << " calculated value is " << default2D(z) << endl; } sum += default2D(z); } } if (failed) cout << "Failed"; else cout << "Passed"; cout << " the default Gaussian test" << endl; if (!failed) { if (!near(sum*inc*inc, Float(1.0))) { failed = True; cout << "Failed (value was " << sum*inc*inc << ")"; } else cout << "Passed"; cout << " the total flux test" << endl; } if (failed) anyFailures = True; } { Bool failed = False; Vector w(1); GaussianND g(1), g2; GaussianND g1(g); g1.setHeight(2.0f); w(0) = 2.0f; g1.setVariance(w); g2 = g; g2.setHeight(3.0f); w(0) = 3.0f; g2.setVariance(w); Vector z(1); z = 0; for (Float x = -2; x < 2; x+=.1) { z(0) = x; if (!near(Double(g(z)), exp(-(z(0)*z(0))/2)/sqrt(2.0*M_PI),1E-5)) { failed = True; cout << "Expected value for g(" << z << ") is " << exp(-(z(0)*z(0)/2))/sqrt(2.0*M_PI) << " calculated value is " << g(z) << endl; } if (!near(Double(g1(z)), 2.0*exp(-(z(0)*z(0))/2/2),1E-5)) { failed = True; cout << "Expected value for g1(" << z << ") is " << 2.0*exp(-(z(0)*z(0)/2/2)) << " calculated value is " << g1(z) << endl; } if (!near(Double(g2(z)), 3.0*exp(-(z(0)*z(0))/2/3),1E-5)) { failed = True; cout << "Expected value for g2(" << z << ") is " << 3.0*exp(-(z(0)*z(0)/2/3)) << " calculated value is " << g2(z) << endl; } } if (!failed) cout << "Passed"; else cout << "Failed"; cout << " the default 1-D and copy semantics test" << endl; if (failed) anyFailures = True; } { Bool failed = False; GaussianND gauss3D(3, 2.0); gauss3D.setHeight(1.0); if (near(gauss3D.height(), 1.0f)) cout << "Passed"; else { cout << "Failed"; failed=True; } cout << " the set/get height test" << endl; Vector z(3); z = 0; Float sum = 0; Float inc = .5; for (Float x = -5; x < 5; x+=inc) { z(0) = x; for (Float y = -5; y < 5; y+=inc) { z(1) = y; for (Float p = -5; p < 5; p+=inc) { z(2) = p; if (!near(Double(gauss3D(z)), exp(-(z(0)*z(0)+z(1)*z(1)+z(2)*z(2))/2), 1E-5)) { failed = True; cout << "Expected value for f(" << z << ") is " << exp(-(z(0)*z(0)+z(1)*z(1)+z(2)*z(2))/2) << " calculated value is " << gauss3D(z) << endl; } sum += gauss3D(z); } } } if (failed) cout << "Failed"; else cout << "Passed"; cout << " the 3-D specified height test" << endl; if (!failed) { if (!near(sum*inc*inc*inc, pow(Float(2.0*M_PI),Float(1.5)), 1E-5)) { failed = True; cout << "Failed (value was " << sum*inc*inc*inc << " not " << pow(Float(2.0*M_PI),Float(1.5)) << ")"; } else cout << "Passed"; cout << " the total flux test" << endl; } if (failed) anyFailures = True; } { Bool failed = False; Vector mean(2); mean(0) = .5; mean(1) = -1; GaussianND gauss2D(2, 1.0, mean); mean *= 2.; gauss2D.setMean(mean); if ((allNear(gauss2D.mean(), mean, 1E-7))) cout << "Passed"; else { cout << "Failed"; failed=True; } cout << " the set/get mean test" << endl; Vector z(2); z = 0; Double sum = 0; Double inc = .25; for (Double x = -4; x < 6; x+=inc) { z(0) = x; for (Double y = -7; y < 3; y+=inc) { z(1) = y; if (!near(gauss2D(z), exp(-(square(z(0)-mean(0))+ square(z(1)-mean(1)))/2), 1E-5)) { failed = True; cout << "Expected value for f(" << z << ") is " << exp(-(square(z(0)-mean(0))+square(z(1)-mean(1)))/2) << " calculated value is " << gauss2D(z) << endl; } sum += gauss2D(z); } } if (failed) cout << "Failed"; else cout << "Passed"; cout << " the 2-D specified height & mean test" << endl; if (!failed) { if (!near(sum*inc*inc, 2.0*M_PI, 1E-5)) { failed = True; cout << "Failed (value was " << sum*inc*inc << " not " << 2.0*M_PI << ")"; } else cout << "Passed"; cout << " the total flux test" << endl; } if (failed) anyFailures = True; } { Bool failed = False; Vector mean(3); mean(0) = .6; mean(1) = -.1; mean(2) = -.5; Vector variance(3); variance(0) = 1; variance(1) = .25; variance(2) = 0.75; Double height = 2.0; GaussianND gauss3D(3, height, mean, variance); variance *= 2.; gauss3D.setVariance(variance); if ((allNear(gauss3D.variance(), variance, 1E-7))) cout << "Passed"; else { cout << "Failed"; failed=True; } cout << " the set/get variance test" << endl; Vector z(3); z = 0; Double sum = 0; Double inc = .5; for (Double x = -5; x < 7; x+=inc) { z(0) = x; for (Double y = -5; y < 5; y+=inc) { z(1) = y; for (Double p = -5; p < 5; p+=inc) { z(2) = p; if (!near(gauss3D(z), height* exp(-(square(z(0)-mean(0))/variance(0) +square(z(1)-mean(1))/variance(1) +square(z(2)-mean(2))/variance(2))/2))) { failed = True; cout << "Expected value for f(" << z << ") is " << height* exp(-(square(z(0)-mean(0))/variance(0) +square(z(1)-mean(1))/variance(1) +square(z(2)-mean(2))/variance(2))/2) << " calculated value is " << gauss3D(z) << endl; } sum += gauss3D(z); } } } if (failed) cout << "Failed"; else cout << "Passed"; cout << " the 3-D specified height, mean & variance test" << endl; if (!failed) { if (!near(sum*inc*inc*inc, height*sqrt(variance(0)*variance(1)*variance(2)) *pow(2.0*M_PI,1.5) , 1E-4)) { failed = True; cout << "Failed (value was " << sum*inc*inc*inc << " not " << height*sqrt(variance(0)*variance(1)*variance(2)) *pow(2.0*M_PI,1.5) << ")"; } else cout << "Passed"; cout << " the total flux test" << endl; } if (failed) anyFailures = True; } { Bool failed = False; Vector mean(3); mean(0) = .6; mean(1) = -.1; mean(2) = -.5; mean(0) = 0; mean(1) = 0; mean(2) = 0; Matrix corr(3,3), covariance(3,3); Vector variance = covariance.diagonal(); covariance = Float(0); corr(1,0) = .5; corr(2,0) = .4; corr(2,1) = -.3; variance(0) = 4.5; variance(1) = .125; variance(2) = 0.50; covariance(1,0) = corr(1,0)*sqrt(variance(1)*variance(0)); covariance(2,0) = corr(2,0)*sqrt(variance(2)*variance(0)); covariance(2,1) = corr(2,1)*sqrt(variance(2)*variance(1)); Float height = 2.0; GaussianND gauss3D(3, height, mean, covariance); covariance *= Float(2.); covariance(0,1) = covariance(1,0); covariance(0,2) = covariance(2,0); covariance(1,2) = covariance(2,1); gauss3D.setCovariance(covariance); if ((allNear(gauss3D.covariance(), covariance, 1E-5))) cout << "Passed"; else { cout << "Failed"; failed=True; } cout << " the set/get covariance test" << endl; if (!failed) { Vector z(3); z = 0; Float sum = 0; Float inc = 0.5; Float ev; z = 0; for (Float x = -12; x < 12; x+=inc) { z(0) = x; for (Float y = -2; y < 2; y+=inc) { z(1) = y; for (Float p = -5; p < 5; p+=inc) { z(2) = p; ev = height* exp(-0.5*(square(z(0)-mean(0))* (1-square(corr(2,1)))/variance(0) +square(z(1)-mean(1))* (1-square(corr(2,0)))/variance(1) +square(z(2)-mean(2))* (1-square(corr(1,0)))/variance(2) -2*(z(0)-mean(0))*(z(1)-mean(1))* (corr(1,0)-corr(2,1)*corr(2,0))/ sqrt(variance(0)*variance(1)) -2*(z(0)-mean(0))*(z(2)-mean(2))* (corr(2,0)-corr(1,0)*corr(2,1))/ sqrt(variance(0)*variance(2)) -2*(z(1)-mean(1))*(z(2)-mean(2))* (corr(2,1)-corr(1,0)*corr(2,0))/ sqrt(variance(1)*variance(2))) /(1-square(corr(1,0))-square(corr(2,0))-square(corr(2,1)) +2*corr(1,0)*corr(2,0)*corr(2,1))); if (!nearAbs(gauss3D(z), ev, 3.1E-4)) { failed = True; cout << "Expected value for f(" << z << ") is " << ev << " calculated value is " << gauss3D(z) << endl; } sum += gauss3D(z); } } } if (failed) cout << "Failed"; else cout << "Passed"; cout << " the 3-D specified height, mean & covariance test" << endl; if (!failed) { if (!nearAbs(sum*inc*inc*inc, gauss3D.flux(), 1E-2)) { failed = True; cout << "Failed (value was " << sum*inc*inc*inc << " not " << gauss3D.flux() << ")"; } else cout << "Passed"; cout << " the total flux test" << endl; } } if (!failed) { if (gauss3D.nparameters() != 10) failed=True; if (!failed) { Vector parms(10); parms(0) = 10; parms(1) = 1; parms(2) = 2; parms(3) = 3; parms(4) = 10; parms(5) = 20; parms(6) = 30; parms(7) = 0.1; parms(8) = 0.2; parms(9) = 0.3; Matrix cov(3,3); cov(0,0) = parms(4); cov(1,1) = parms(5); cov(2,2) = parms(6); cov(0,1) = parms(7); cov(1,0) = cov(0,1); cov(0,2) = parms(8); cov(2,0) = cov(0,2); cov(1,2) = parms(9); cov(2,1) = cov(1,2); Matrix invertCov(3,3); invertCov = invertSymPosDef(cov); gauss3D.parameters().setParameters(parms); if (!near(gauss3D.height(), parms(0))) failed = True; Vector mean(3); mean(0) = parms(1); mean(1) = parms(2); mean(2) = parms(3); if (!(allNear(gauss3D.mean(), mean, 1E-6))) failed=True; Vector var(3); var(0) = invertCov(0,0); var(1) = invertCov(1,1); var(2) = invertCov(2,2); if (!(allNear(gauss3D.variance(), var, 1E-6))) failed=True; if (!(allNear(gauss3D.covariance(), invertCov, 1E-6))) failed=True; for (uInt i = 0; i < 10; i++) gauss3D[i] = Float(2)*gauss3D[i]; parms *= Float(2); if (!(allNear(gauss3D.parameters().getParameters(), parms, 1E-6))) { failed=True; } if (failed) cout << "Failed"; else cout << "Passed"; cout << " the Parameters tests" << endl; } } if (failed) anyFailures = True; } if (anyFailures) { cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } catch (std::exception& x) { cerr << x.what() << endl; cout << "FAIL" << endl; return 1; } } casacore-3.7.1/scimath/Functionals/test/tHyperPlane.cc000066400000000000000000000111451476623553700227640ustar00rootroot00000000000000//# tHyperPlane.cc: Test the HyperPlane class //# Copyright (C) 2001,2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include int main() { try { // Construct an m dimensional hyper plane which has m coefficients. By // default, the coefficients are initialized to zero. //HyperPlane(uInt m); HyperPlane hyper(3); // Make this object a copy of other. //HyperPlane(const HyperPlane &other); HyperPlane comb2(hyper); // Make this object a copy of other. //HyperPlane &operator=(const HyperPlane &other); comb2 = hyper; // Return the total number of coefficients, which is the dimension of the // hyper plane. AlwaysAssertExit(hyper.nparameters() == 3 && hyper.nparameters() == comb2.nparameters()); Vector v(3); // Set the value of a coefficient. // Get the value of a coefficient. // f(x,y,z) = 10x + 11y + 12*z for (uInt i=0; i<3; i++) { hyper[i] = i+10; AlwaysAssertExit(hyper[i] == Double(i+10)); v(i) = i+10; } // Set all coefficients at once. // Get all the values of coefficients at once. hyper.parameters().setParameters(v); AlwaysAssertExit(allEQ(hyper.parameters().getParameters(), v)); // Evaluate the function at v. // f(x,y,z) = 10x + 11y + 12*z AlwaysAssertExit((hyper(v) - Double(365)) < 1.e-6); // Returns the dimension of functions in the linear hyper // uInt ndim() const; AlwaysAssertExit(hyper.ndim() == 3); // Set coefficients for (uInt i=0; i > v6(3); HyperPlane > s5(3); for (uInt i=0; i<3; i++) { s5[i] = AutoDiff(i+10,3,i); AlwaysAssertExit(s5[i] == Double(i+10)); v[i] = i+10; v6(i) = i+10; } Double y50 = s5(v).value(); Vector y51; y51 = s5(v).derivatives(); cout << "AutoDiff: " << s5(v) << endl; // Generic AutoDiff HyperPlane > s6(3); for (uInt i=0; i<3; i++) { s6[i] = AutoDiffA(i+10,3,i); AlwaysAssertExit(s6[i].value() == Double(i+10)); v6(i) = i+10; } Double y60 = s6(v6).value(); Vector y61; y61 = s6(v6).derivatives(); cout << "AutoDiffA: " << s6(v6) << endl; AlwaysAssertExit(near(y60, y50) && near(y61(0), y51[0]) && near(y61(1), y51[1]) && near(y61(2), y51[2])); } catch (std::exception& x) { cout << "Exception : " << x.what() << endl; } cout << "OK" << endl; return 0; } casacore-3.7.1/scimath/Functionals/test/tInterpolate1D.cc000066400000000000000000000223571476623553700233770ustar00rootroot00000000000000//# tInterpolate1D.cc: This program tests the Interpolate1D class //# Copyright (C) 1996,1997,1998,1999,2000,2001,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Main program to test the Interpolate1D class int main() { Bool anyFailures = False; // Test the Interpolate1D class with Floating point Vectors and linear // interpolation { Bool failed = False; Vector x(5); indgen(x); Vector y(5); indgen(y); ScalarSampledFunctional fx(x), fy(y); Interpolate1D value(fx, fy); Float xs; for (xs = -1; xs < 5; xs += 0.1) if (near(value(xs), xs) == False){ cout << "value(" << xs << ") = " << value(xs) << " which is not near the expected value of " << xs << endl; failed = True; } // Check the assignment operator and copy constructor use copy symantics Interpolate1D v1(value), v2; v2 = v1; Vector y1(5); indgen(y1, 1.0f); Vector y2(5); indgen(y2, 2.0f); ScalarSampledFunctional fy1(y1), fy2(y2); v1.setData(fx, fy1); v2.setData(fx, fy2); for (xs = -1; xs < 5; xs += 0.1) { if (near(v1(xs), xs+1.0f, 1.0E-5) == False){ cout << "v1(" << xs << ") = " << v1(xs) << " which is not near the expected value of " << xs+1.0f << endl; failed = True; } if (near(v2(xs), xs+2.0f, 1.0E-5) == False){ cout << "v2(" << xs << ") = " << v2(xs) << " which is not near the expected value of " << xs+2.0f << endl; failed = True; } } if (failed){ cout << "Failed "; anyFailures = True; } else cout << "Passed "; cout << "the Interpolate1D test with linear interpolation" << endl; } // Test the Interpolate1D class with Int/Double Vectors and cubic // interpolation { Bool failed = False; Vector x(5); indgen(x); Vector y(5); indgen(y); y = y*y*y; ScalarSampledFunctional fx(x); ScalarSampledFunctional fy(y); Interpolate1D value(fx, fy); value.setMethod(Interpolate1D::cubic); for (Int xs = -5; xs < 10; xs += 1) if (near(value(xs),(Double) xs*xs*xs,1E-6) == False){ cout << "value(" << xs << ") = " << value(xs) << " which is not near the expected value of " << xs*xs*xs << endl; failed = True; } if (!failed){ Vector xd = value.getX(); if (xd.nelements() != 5) failed = True; if (!failed) for (Int i = 0; i < 5; i++) if (x(i) != xd(i)) failed = True; Vector yd = value.getY(); if (yd.nelements() != 5) failed = True; if (!failed) for (Int j = 0; j < 5; j++) if (y(j) != yd(j)) failed = True; } if (failed){ cout << "Failed "; anyFailures = True; } else cout << "Passed "; cout << "the Interpolate1D test with cubic interpolation" << endl; } // Test the Interpolate1D class with Double/Complex Blocks and nearest // neighbour interpolation { Bool failed = False; Vector x(5); indgen(x); Vector y(5); indgen(y); const DComplex j(0.,1.); y = y+j*y*y; Block bx = makeBlock(x); Block by = makeBlock(y); ScalarSampledFunctional fx(bx); ScalarSampledFunctional fy(by); Interpolate1D value(fx, fy); value.setMethod(Interpolate1D::nearestNeighbour); Double ev; for (Float xs = -5.0000001; xs < 5; xs += .1){ ev = max(min((Int) (xs+0.5),4),0); if (near((value(xs)).real(), ev) == False || near((value(xs)).imag(), ev*ev) == False) { cout << "value(" << xs << ") = " << value(xs) << " is not near the expected value of (" << ev << ", " << ev*ev << ")" << endl; failed = True; } } if (failed){ cout << "Failed "; anyFailures = True; } else cout << "Passed "; cout << "the Interpolate1D test with nearest neighbour" << endl << " " << " interpolation (using Blocks)" << endl; } // Test the Interpolate1D class with Float / Float Array and spline // interpolation { Bool failed = False; Vector x(5); indgen(x); IPosition shape(3, 3, 5, 1); Array y(shape); IPosition xshape(3, 1, 5, 1); Array xa(xshape); indgen(xa); IPosition trc(3,0,4,0), blc(3,0,0,0), step(3,1,0,0); y(blc,trc) = xa; trc += step; blc += step; y(blc,trc) = xa*xa; trc += step; blc += step; y(blc,trc) = xa*xa*xa; ScalarSampledFunctional fx(x); ArraySampledFunctional > fy(y); Interpolate1D > value(fx,fy); value.setMethod(Interpolate1D >::spline); trc(0) = 2; trc(1) = 0; trc(2) = 0; blc(0) = 0; blc(1) = 0; blc(2) = 0; step(0) = 0; step(1) = 1; Array iv; for (Float xs = 0; xs < 5; xs += 1){ iv = value(xs); if ((near(iv(IPosition(1, 0)), y(IPosition(3, 0,(uInt) xs, 0))) == False) || (near(iv(IPosition(1, 1)), y(IPosition(3, 1, (uInt) xs, 0))) == False) || (near(iv(IPosition(1, 2)), y(IPosition(3, 2, (uInt) xs, 0))) == False)){ cout << "value(" << xs << ")" << endl << iv << " is not near the expected value of " << endl << y(blc, trc) << endl; failed = True; } trc += step; blc += step; } iv = value((Float) 5); if ((near(iv(IPosition(1, 0)), (Float) 5) == False) || (near(iv(IPosition(1, 1)), (Float) 23) == False) || (near(iv(IPosition(1, 2)), (Float) 101) == False)) failed = True; // Switch out of spline mode back to cubic interpolation value.setMethod(Interpolate1D >::cubic); if (value.getMethod() != Interpolate1D >::cubic) { failed = True; cout << "Could not change the interpolation method" << endl; } iv = value(Float(-1)); if (near(iv(IPosition(1,1)), Float(1)) == False){ failed = True; cout << "Did not really change the interpolation method" << endl; } if (failed){ cout << "Failed "; anyFailures = True; } else cout << "Passed "; cout << "the Interpolate1D > test with " << "spline interpolation" << endl; } // Now test the table system interface. // This requires the construction of a table (vrtually done) { Vector time (6); Vector amp (6); for (uInt i=0; i < 6; i++) { time[i] = i; amp[i] = i*i; } // Now I have constructed a table with two scalar and one array column // Test the Interpolate1D class with the scalar columns { Bool failed = False; Vector x(time); Vector y(amp); ScalarSampledFunctional fx(x); ScalarSampledFunctional fy(y); Interpolate1D value(fx, fy); value.setMethod(Interpolate1D::cubic); for (Float xs = -5; xs < 10; xs += .5) if (near(value(xs),(Double) xs*xs) == False){ cout << "value(" << xs << ") = " << value(xs) << " which is not near the expected value of " << xs*xs << endl; failed = True; } if (failed){ cout << "Failed "; anyFailures = True; } else cout << "Passed "; cout << "the Interpolate1D test with cubic interpolation" << endl << " " << " using table scalar columns as inputs" << endl; } } if (anyFailures) { cout << "FAIL" << endl; return 1; } else { cout << "OK" << endl; return 0; } } // Local Variables: // compile-command: "gmake OPTLIB=1 tInterpolate1D" // End: casacore-3.7.1/scimath/Functionals/test/tPoisson.cc000066400000000000000000000110311476623553700223410ustar00rootroot00000000000000//# Copyright (C) 1995,1996,1997,1999,2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include int main() { PoissonFunction null; AlwaysAssertExit(null.lambda() == 1.0 && null.height() == 1.0); const float ERROR_TOL = .00001; AlwaysAssertExit(near(null(0.0), .367879, ERROR_TOL) && near(null(1.0), .367879, ERROR_TOL)); // name() cout << "Name of function: " << null.name() << endl; AlwaysAssertExit(null.name() == "poisson"); PoissonFunction poisson1(4.0, 2.0); AlwaysAssertExit(poisson1.lambda() == 4.0 && poisson1.height()==2.0); const PoissonFunction &cpoisson1 = poisson1; AlwaysAssertExit(cpoisson1.lambda() == 4.0 && cpoisson1.height()==2.0 ); poisson1.setLambda(2.0); poisson1.setHeight(3.0); AlwaysAssertExit(poisson1[PoissonFunction::LAMBDA] == 2.0 ); AlwaysAssertExit(poisson1[PoissonFunction::HEIGHT] == 3.0 ); poisson1[PoissonFunction::LAMBDA] = 3.0; poisson1[PoissonFunction::HEIGHT] = 2.0; // << cout << "Function Parameters: " << poisson1 << endl; AlwaysAssertExit(near(poisson1(3.0),0.4480836, ERROR_TOL )); Vector xvec(1); xvec = 5.0; cout << "Value at 5: " << poisson1(xvec(0)) << endl; /// // Copy constructor PoissonFunction pc(poisson1); cout << "Copy: " << pc << "; " <<"f(5) = " << pc(xvec(0)) << endl; PoissonFunction pcb(pc); cout << "Copy back: " << pcb << endl <<"f(5) = " << pcb(xvec(0)) << endl; AlwaysAssertExit(near(poisson1(xvec(0)), .2016376, ERROR_TOL)); AlwaysAssertExit(near(pc(xvec(0)), .2016376, ERROR_TOL)); AlwaysAssertExit(near(pcb(xvec(0)), .2016376, ERROR_TOL)); xvec = 7.0; AlwaysAssertExit(near(poisson1(xvec(0)), .043208063, ERROR_TOL)); //Available parameters PoissonFunction poisson2(poisson1); PoissonFunction poisson3; poisson3 = poisson2; cout << "NParameters="< parms = poisson1.parameters().getParameters(); AlwaysAssertExit(parms(0) == 3.0); AlwaysAssertExit(parms(1) == 2.0); AlwaysAssertExit(allEQ(parms, poisson2.parameters().getParameters()) && allEQ(parms, poisson3.parameters().getParameters())); poisson1[0] = 1.0; poisson1[1] = 4.0; AlwaysAssertExit(poisson1.lambda() == 1.0 ); AlwaysAssertExit(poisson1.height() == 4.0 ); parms[0] = 11.0; parms[1] = 2.0; poisson1.parameters().setParameters(parms); AlwaysAssertExit(allEQ(poisson1.parameters().getParameters(), parms)); // clone(); cout << "Original value f(1): " << poisson1(1.0) << endl; AlwaysAssertExit(nearAbs(poisson1(1.0), 0.0003674374, 1e-5)) Function *poisson4ptr = poisson1.clone(); cout << "f.clone(1): " << (*poisson4ptr)(1.0) << endl; AlwaysAssertExit(near(poisson1(1.0), (*poisson4ptr)(1.0))) delete poisson4ptr; cout << "OK" << endl; return 0; } casacore-3.7.1/scimath/Functionals/test/tPolynomial.cc000066400000000000000000000131231476623553700230360ustar00rootroot00000000000000//# tPolynomial.cc: Test the one-dimensional polynomial class //# Copyright (C) 1995,1996,1999,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include int main() { // Polynomial(); Polynomial null; // Polynomial(uInt order); // void setCoefficient(uInt which, T value); // virtual void setAdjustParameter(uInt which, const T &val); Polynomial linear(1); linear.setCoefficient(1, 1); // x Polynomial square(2); square.setCoefficient(2, 1); // x^2 // virtual T operator()(const T &x) const; AlwaysAssertExit(linear(3.0) == 3.0f && square(3.0f) == 9.0f); // virtual uInt nAdjustParameters() const; // uInt order() const {return coefficients_p.nelements() - 1;} // virtual T getAdjustParameter(uInt which) const; // T coefficient(uInt which) const {return coefficients_p[which];} // Vector coefficients() const; // virtual Vector getAdjustParameters() const; AlwaysAssertExit(null.order() == 0 && linear.order() == 1 && square.order() == 2 && null[0] == 0.0f && linear[0] == 0.0f && linear[1] == 1.0f && square[0] == 0.0f && square[1] == 0.0f && square.coefficient(2) == 1.0f); AlwaysAssertExit(null.nparameters() == 1 && square.nparameters() == 3); Vector sqrCoeff1, sqrCoeff2; sqrCoeff1 = square.coefficients(); sqrCoeff2 = square.parameters().getParameters(); AlwaysAssertExit(allEQ(sqrCoeff1, sqrCoeff2)); AlwaysAssertExit(sqrCoeff1.nelements() == 3); AlwaysAssertExit(sqrCoeff1(0) == 0.0f); AlwaysAssertExit(sqrCoeff1(1) == 0.0f); AlwaysAssertExit(sqrCoeff1(2) == 1.0f); // Polynomial(const Polynomial &other); // Polynomial &operator=(const Polynomial &other); Polynomial squareCopy1(square); Polynomial squareCopy2; squareCopy2 = square; AlwaysAssertExit(square == squareCopy1 && square == squareCopy2); // void setCoefficients(const Vector &coefficients); // virtual void setAdjustParameters(const Vector &val); Polynomial tmp1(3), tmp2(3); Vector coefficients(4); indgen(coefficients); // x + 2x^2 + 3x^3 tmp1.setCoefficients(coefficients); tmp2.parameters().setParameters(coefficients); AlwaysAssertExit(allEQ(coefficients, tmp1.coefficients()) && allEQ(coefficients, tmp1.parameters().getParameters())); // Bool operator==(const Polynomial &other) const; // Bool operator!=(const Polynomial &other) const; AlwaysAssertExit(null != linear && null != square && square != linear && null == null && linear == linear && square == square); // Polynomial derivative() const; Polynomial der1 = square.derivative(); AlwaysAssertExit(der1.order() == 1 && der1.coefficient(0) == 0.0f && der1.coefficient(1) == 2.0f); Polynomial der2 = tmp1.derivative(); AlwaysAssertExit(der2.order() == 2 && der2.coefficient(0) == 1.0f && der2.coefficient(1) == 4.0f && der2.coefficient(2) == 9.0f); // clone() // ~Polynomial(); Function *tmp3ptr = tmp2.clone(); AlwaysAssertExit(tmp3ptr->nparameters() == 4 && (*tmp3ptr)[0] == 0.0f && (*tmp3ptr)[1] == 1.0f && (*tmp3ptr)[2] == 2.0f && (*tmp3ptr)[3] == 3.0f); delete tmp3ptr; // Test Auto differentiation // 1 + 2x + 3x^2 Polynomial > sq2(2); sq2[0] = AutoDiffA(1.0,3,0); sq2[1] = AutoDiffA(2.0,3,1); sq2[2] = AutoDiffA(3.0,3,2); cout << "Generic(3): " << sq2(AutoDiffA(3.0)) << endl; // Test manual differentiation // 1 + 2x + 3x^2 Polynomial > sq3(2); sq3[0] = AutoDiff(1.0,3,0); sq3[1] = AutoDiff(2.0,3,1); sq3[2] = AutoDiff(3.0,3,2); cout << "Specific(3): " << sq3(3.0) << endl; AlwaysAssertExit(near(sq2(AutoDiffA(3.0)).value(), sq3(3.0).value()) && allNear(sq2(AutoDiffA(3.0)).derivatives(), sq3(3.0).derivatives(), 1e-13)); cout << "OK" << endl; return 0; } casacore-3.7.1/scimath/Functionals/test/tPowerLogarithmicPolynomial.cc000066400000000000000000000135431476623553700262440ustar00rootroot00000000000000//# tPowerLogarithmicPolynomial.cc: Test the one-dimensional PowerLogarithmicPolynomial class //# Copyright (C) 1995,1996,1999,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include int main() { // PowerLogarithmicPolynomial(); PowerLogarithmicPolynomial null; // PowerLogarithmicPolynomial(uInt order); // void setCoefficient(uInt which, T value); // virtual void setAdjustParameter(uInt which, const T &val); PowerLogarithmicPolynomial linear(2); Vector coeff(2, 1); linear.setCoefficients(coeff); PowerLogarithmicPolynomial square(2); square.setCoefficient(0, 1); square.setCoefficient(1, 2); // virtual T operator()(const T &x) const; AlwaysAssertExit(linear(3.0) == 3.0f && square(3.0f) == 9.0f); PowerLogarithmicPolynomial curve(4); curve.setCoefficient(0, 1); curve.setCoefficient(1, 0.5); curve.setCoefficient(2, 1); curve.setCoefficient(3, 2); cout << "curve " << curve(3.0) << endl; AlwaysAssertExit(near(curve(3.0), 82.1209389f)); // virtual uInt nAdjustParameters() const; // uInt order() const {return coefficients_p.nelements() - 1;} // virtual T getAdjustParameter(uInt which) const; // T coefficient(uInt which) const {return coefficients_p[which];} // Vector coefficients() const; // virtual Vector getAdjustParameters() const; AlwaysAssertExit(linear.nparameters() == 2 && square.nparameters() == 2 && curve.nparameters() == 4); Vector curveCoeff1, curveCoeff2; curveCoeff1 = curve.coefficients(); curveCoeff2 = curve.parameters().getParameters(); AlwaysAssertExit(allEQ(curveCoeff1, curveCoeff2)); AlwaysAssertExit(curveCoeff1.nelements() == 4); AlwaysAssertExit(curveCoeff1(0) == 1.0f); AlwaysAssertExit(curveCoeff1(1) == 0.5f); AlwaysAssertExit(curveCoeff1(2) == 1.0f); AlwaysAssertExit(curveCoeff1(3) == 2.0f); // PowerLogarithmicPolynomial(const PowerLogarithmicPolynomial &other); // PowerLogarithmicPolynomial &operator=(const PowerLogarithmicPolynomial &other); PowerLogarithmicPolynomial curveCopy1(curve); PowerLogarithmicPolynomial curveCopy2; curveCopy2 = curve; AlwaysAssertExit(curve == curveCopy1 && curve == curveCopy2); // void setCoefficients(const Vector &coefficients); // virtual void setAdjustParameters(const Vector &val); PowerLogarithmicPolynomial tmp1(4), tmp2(4); Vector coefficients(4); indgen(coefficients); tmp1.setCoefficients(coefficients); tmp2.parameters().setParameters(coefficients); AlwaysAssertExit(allEQ(coefficients, tmp1.coefficients()) && allEQ(coefficients, tmp1.parameters().getParameters())); // Bool operator==(const PowerLogarithmicPolynomial &other) const; // Bool operator!=(const PowerLogarithmicPolynomial &other) const; AlwaysAssertExit(null == linear && null != square && square != linear && null == null && linear == linear && square == square); // clone() // ~PowerLogarithmicPolynomial(); Function *tmp3ptr = tmp2.clone(); AlwaysAssertExit(tmp3ptr->nparameters() == 4 && (*tmp3ptr)[0] == 0.0f && (*tmp3ptr)[1] == 1.0f && (*tmp3ptr)[2] == 2.0f && (*tmp3ptr)[3] == 3.0f); delete tmp3ptr; // Test Auto differentiation // 0.5 * x**(2 + 3ln(x) + 4ln(x)**2) PowerLogarithmicPolynomial > curve2(4); curve2[0] = AutoDiffA(0.5,4,0); curve2[1] = AutoDiffA(2.0,4,1); curve2[2] = AutoDiffA(3.0,4,2); curve2[3] = AutoDiffA(4.0,4,3); cout << "Generic(3): " << curve2(AutoDiffA(3.0)) << endl; // Test manual differentiation // 0.5 * x**(2 + 3ln(x) + 4ln(x)**2) PowerLogarithmicPolynomial > curve3(4); curve3[0] = AutoDiff(0.5,4,0); curve3[1] = AutoDiff(2.0,4,1); curve3[2] = AutoDiff(3.0,4,2); curve3[3] = AutoDiff(4.0,4,3); cout << "Specific(3): " << curve3(3.0) << endl; AlwaysAssertExit( near( curve2(AutoDiffA(3.0)).value(), curve3(3.0).value() ) && allNear( curve2(AutoDiffA(3.0)).derivatives(), curve3(3.0).derivatives(), 1e-13 ) ); cout << "OK" << endl; return 0; } casacore-3.7.1/scimath/Functionals/test/tSPolynomial.cc000066400000000000000000000120221476623553700231560ustar00rootroot00000000000000//# tSPolynomial.cc: Test the one-dimensional scaled polynomial class //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include int main() { // SPolynomial(); SPolynomial null; // SPolynomial(uInt order); // void setCoefficient(uInt which, T value); // virtual void setAdjustParameter(uInt which, const T &val); SPolynomial linear(1); linear.setCoefficient(1, 1); // x SPolynomial square(2); square.setCoefficient(2, 1); // x^2 // virtual T operator()(const T &x) const; AlwaysAssertExit(linear(3.0) == 3.0f && square(3.0f) == 9.0f); // virtual uInt nAdjustParameters() const; // uInt order() const {return coefficients_p.nelements() - 1;} // virtual T getAdjustParameter(uInt which) const; // T coefficient(uInt which) const {return coefficients_p[which];} // Vector coefficients() const; // virtual Vector getAdjustParameters() const; AlwaysAssertExit(null.order() == 0 && linear.order() == 1 && square.order() == 2 && null[0+3] == 0.0f && linear[0+3] == 0.0f && linear[1+3] == 1.0f && square[0+3] == 0.0f && square[1+3] == 0.0f && square.coefficient(2) == 1.0f); AlwaysAssertExit(null.nparameters() == 1+3 && square.nparameters() == 3+3); Vector sqrCoeff1, sqrCoeff2; sqrCoeff1 = square.coefficients(); AlwaysAssertExit(sqrCoeff1.nelements() == 3); AlwaysAssertExit(sqrCoeff1(0) == 0.0f); AlwaysAssertExit(sqrCoeff1(1) == 0.0f); AlwaysAssertExit(sqrCoeff1(2) == 1.0f); // SPolynomial(const SPolynomial &other); // SPolynomial &operator=(const SPolynomial &other); SPolynomial squareCopy1(square); SPolynomial squareCopy2; squareCopy2 = square; AlwaysAssertExit(square == squareCopy1 && square == squareCopy2); // void setCoefficients(const Vector &coefficients); // virtual void setAdjustParameters(const Vector &val); SPolynomial tmp1(3), tmp2(3); Vector coefficients(4); indgen(coefficients); // x + 2x^2 + 3x^3 tmp1.setCoefficients(coefficients); AlwaysAssertExit(allEQ(coefficients, tmp1.coefficients())); // Bool operator==(const SPolynomial &other) const; // Bool operator!=(const SPolynomial &other) const; AlwaysAssertExit(null != linear && null != square && square != linear && null == null && linear == linear && square == square); // clone() // ~SPolynomial(); Function *tmp3ptr = tmp2.clone(); /* AlwaysAssertExit(tmp3ptr->nparameters() == 4+3 && (*tmp3ptr)[0+3] == 0.0f && (*tmp3ptr)[1+3] == 1.0f && (*tmp3ptr)[2+3] == 2.0f && (*tmp3ptr)[3+3] == 3.0f); */ delete tmp3ptr; // Test Auto differentiation // 1 + 2x + 3x^2 SPolynomial > sq2(2); sq2[0] = AutoDiffA(1.0,3,0); sq2[1] = AutoDiffA(2.0,3,1); sq2[2] = AutoDiffA(3.0,3,2); cout << "Generic(3): " << sq2(AutoDiffA(3.0)) << endl; // Test manual differentiation // 1 + 2x + 3x^2 SPolynomial > sq3(2); sq3[0] = AutoDiff(1.0,3,0); sq3[1] = AutoDiff(2.0,3,1); sq3[2] = AutoDiff(3.0,3,2); cout << "Specific(3): " << sq3(3.0) << endl; AlwaysAssertExit(near(sq2(AutoDiffA(3.0)).value(), sq3(3.0).value()) && allNear(sq2(AutoDiffA(3.0)).derivatives(), sq3(3.0).derivatives(), 1e-13)); cout << "OK" << endl; return 0; } casacore-3.7.1/scimath/Functionals/test/tSampledFunctional.cc000066400000000000000000000117401476623553700243260ustar00rootroot00000000000000//# tSampledFunctional.cc: //# Copyright (C) 1996,1999,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include int main() { Bool anyFailures = False; { Bool Failed = False; uInt i; Vector v(10); indgen(v); ScalarSampledFunctional fv(v); for (i = 0; i < fv.nelements(); i++) if (!near(fv(i), Float(i))) Failed = True; // Check the assignment operator and copy constructor // for const ScalarSampledFunctionals use copy semantics const ScalarSampledFunctional cfv(v); ScalarSampledFunctional cfv1(cfv), cfv2; cfv2 = cfv; v(0) = 100.0f; if (!near(cfv(0), 100.0f)) Failed = True; for (i = 1; i < cfv.nelements(); i++) if (!near(cfv(i), Float(i))) Failed = True; for (i = 0; i < cfv1.nelements(); i++) if (!near(cfv1(i), Float(i))) Failed = True; for (i = 0; i < cfv2.nelements(); i++) if (!near(cfv2(i), Float(i))) Failed = True; // Check the copy constructor uses reference sematics ScalarSampledFunctional fv1(fv); for (i = 1; i < fv.nelements(); i++) if (!near(fv1(i), Float(i))) Failed = True; if (!near(fv(0),100.0f)) Failed = True; if (!near(fv1(0),100.0f)) Failed = True; // Check the assignment operator uses reference sematics ScalarSampledFunctional fv2; fv2 = fv1; for (i = 1; i < fv.nelements(); i++) if (!near(fv1(i), Float(i))) Failed = True; if (!near(fv1(0),100.0f)) Failed = True; // The block constructor uses copy semantics Block b(10); for (i = 0; i < fv.nelements(); i++) b[i] = Float(i); ScalarSampledFunctional fb(b); b = 0.0f; for (i = 0; i < fb.nelements(); i++) if (!near(fb(i), Float(i))) Failed = True; if (Failed) { cout << "Failed"; anyFailures = True; } else cout << "Passed"; cout << " the ScalarSampledFunctional test" << endl; } { Bool Failed = False; uInt i; Array a(IPosition(4,2,3,10,1)); indgen(a); ArraySampledFunctional > f(a); Matrix m; for (i = 0; i < f.nelements(); i++) { m = f(i); if (!near(m(0,0), Double(6*i+0))) Failed = True; if (!near(m(1,0), Double(6*i+1))) Failed = True; if (!near(m(0,1), Double(6*i+2))) Failed = True; if (!near(m(1,1), Double(6*i+3))) Failed = True; if (!near(m(0,2), Double(6*i+4))) Failed = True; if (!near(m(1,2), Double(6*i+5))) Failed = True; // cout << " f(" << i << ") = " << m << endl;; } ArraySampledFunctional > f1(f); a(IPosition(4,0,0,0,0)) = 100.0; ArraySampledFunctional > f2; f2 = f1; for (i = 1; i < f2.nelements(); i++) { m = f2(i); if (!near(m(0,0), Double(6*i+0))) Failed = True; if (!near(m(1,0), Double(6*i+1))) Failed = True; if (!near(m(0,1), Double(6*i+2))) Failed = True; if (!near(m(1,1), Double(6*i+3))) Failed = True; if (!near(m(0,2), Double(6*i+4))) Failed = True; if (!near(m(1,2), Double(6*i+5))) Failed = True; } m = f2(0); if (!near(m(0,0), 100.0)) Failed = True; if (Failed) { cout << "Failed"; anyFailures = True; } else cout << "Passed"; cout << " the ArraySampledFunctional test" << endl; } if (anyFailures) { cout << "FAIL" << endl; return 1; } else { cout << "OK" << endl; return 0; } } casacore-3.7.1/scimath/Functionals/test/tSimButterworthBandpass.cc000066400000000000000000000074231476623553700253770ustar00rootroot00000000000000//# tSimButterworthBandpass: test the SimButterworthBandpass class //# Copyright (C) 2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #define DIAGNOSTICS #ifdef DEBUG #define DIAGNOSTICS #endif #include #include #include #include #include #include #include #include int main() { SimButterworthBandpass butt(1,1,-1.0,1.0,0.0,1.0); AlwaysAssertExit(butt.getCenter() == 0.0 && butt.getPeak() == 1.0 ); AlwaysAssertExit(butt.getMinOrder() == 1.0 && butt.getMaxOrder() == 1.0 ); AlwaysAssertExit(butt.getMinCutoff() == -1.0 && butt.getMaxCutoff() == 1.0 ); // AlwaysAssertExit(butt.getCenter() - 0.0 < DBL_EPSILON && // butt.getPeak() - 1.0 < DBL_EPSILON ); // AlwaysAssertExit(butt.getMinOrder() - 1.0 < DBL_EPSILON && // butt.getMaxOrder() - 1.0 < DBL_EPSILON ); // AlwaysAssertExit(butt.getMinCutoff() + 1.0 < DBL_EPSILON && // butt.getMaxCutoff() - 1.0 < DBL_EPSILON ); butt.setPeak(10.0); AlwaysAssertExit(butt.getPeak() == 10.0); butt.setCenter(5.0); AlwaysAssertExit(butt.getCenter() == 5.0); butt.setMinOrder(4); AlwaysAssertExit(butt.getMinOrder() == 4); butt.setMaxOrder(5); AlwaysAssertExit(butt.getMaxOrder() == 5); butt.setMinCutoff(1.0); AlwaysAssertExit(butt.getMinCutoff() == 1.0); butt.setMaxCutoff(9.0); AlwaysAssertExit(butt.getMaxCutoff() == 9.0); Double pk = butt.getPeak(); Double cen = butt.getCenter(); //Double irt2 = 1.0/sqrt(2.0); AlwaysAssertExit(butt(cen) == pk); //AlwaysAssertExit(butt(fabs(butt.getMinCutoff()) - irt2*pk) < DBL_EPSILON && // butt(fabs(butt.getMaxCutoff()) - irt2*pk) < DBL_EPSILON); AlwaysAssertExit(butt(6*butt.getMinCutoff()-5*cen) < 1e-2*pk); AlwaysAssertExit(butt(6*butt.getMaxCutoff()+5*cen) < 1e-2*pk); // test get/setMode() AlwaysAssertExit(butt.hasMode()); Record rec; rec.define(RecordFieldId("minOrder"), 2); rec.define(RecordFieldId("maxOrder"), 3); butt.setMode(rec); AlwaysAssertExit(butt.getMinOrder() == 2 && butt.getMaxOrder() == 3); Record rec2; butt.getMode(rec2); try { uInt mino, maxo; rec2.get(RecordFieldId("minOrder"), mino); rec2.get(RecordFieldId("maxOrder"), maxo); AlwaysAssertExit(mino == 2 && maxo ==3); } catch (std::exception& ex) { cerr << "Exception: " << ex.what() << endl; exit(1); } cout << "OK" << endl; return 0; } casacore-3.7.1/scimath/Functionals/test/tSinusoid1D.cc000066400000000000000000000131051476623553700226750ustar00rootroot00000000000000//# tSinusoid1D: Test the Sinusoid1D class //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include int main() { // Sinusoid1D(); Sinusoid1D null; AlwaysAssertExit(null.amplitude()==1.0 && null.period() == 1.0 && null.x0()==0.0); // use nearAbs because one value is 0.0, which always // causes near() to return False as per the documentation AlwaysAssertExit(nearAbs(null(0.25), 0.0) && near(null(0.0), 1.0)); // Sinusoid1D(const T& h, const T& c, const T& w); // T amplitude() const // void setAmplitude(const T & amplitude) // T period() const // setPeriod(const T & period) // T x0() const; // void setX0(const T & x0); // virtual Type getAvailableParam(uInt which) const; Sinusoid1D s1(4.0, 6.0, 8.0); AlwaysAssertExit(s1.amplitude()==4.0 && s1.period() == 6.0 && s1.x0()==8.0); const Sinusoid1D &cs1 = s1; AlwaysAssertExit(cs1.amplitude()==4.0 && cs1.period() == 6.0 && cs1.x0()==8.0); s1.setAmplitude(2.0); s1.setPeriod(3.0); s1.setX0(4.0); AlwaysAssertExit(s1[Sinusoid1D::X0] == 4.0 && s1[Sinusoid1D::PERIOD] == 3.0 && s1[Sinusoid1D::AMPLITUDE] == 2.0); // T operator()(const T &x) const; AlwaysAssertExit(near(s1(7.0), 2.0)); Vector xvec(1); xvec = 4.5; AlwaysAssertExit(near(s1(xvec(0)), 1.0)); xvec = 5.125; AlwaysAssertExit(near(s1(xvec(0)), -2.0/sqrt(2.))); // test specialized AutoDiff Sinusoid1D > s5; s5.setAmplitude(AutoDiff(2.0, 3, 0)); s5.setPeriod(AutoDiff(3.0, 3, 1)); s5.setX0(AutoDiff(4.0, 3, 2)); Double y50 = s5(4.5).value(); Vector y51; y51 = s5(4.5).derivatives(); cout << "AutoDiff: " << s5(4.5) << endl; Double y1 = (2.0*M_PI) * 0.5/3.0; AlwaysAssertExit(near(y50, 1.0) && near(y51(0), cos(y1)) && near(y51(1), 2.0/3.0*y1*sin(y1)) && near(y51(2), 2.0/3.0*(2.0*M_PI)*sin(y1))); // Generic AutoDiff Sinusoid1D > s6; s6.setAmplitude(AutoDiffA(2.0, 3, 0)); s6.setPeriod(AutoDiffA(3.0, 3, 1)); s6.setX0(AutoDiffA(4.0, 3, 2)); Double y60 = s6(AutoDiffA(4.5)).value(); Vector y61; y61 = s6(4.5).derivatives(); cout << "AutoDiffA: " << s6(4.5) << endl; AlwaysAssertExit(near(y60, 1.0) && near(y61(0), cos(y1)) && near(y61(1), 2.0/3.0*y1*sin(y1)) && near(y61(2), 2.0/3.0*(2.0*M_PI)*sin(y1))); // Sinusoid1D(const Sinusoid1D &other); // Sinusoid1D &operator=(const Sinusoid1D &other); // virtual uInt nAvailableParams() const; // virtual void setAvailableParam(uInt which, const Type &value); // virtual Type getAvailableParam(uInt which) const; // virtual void setAvailableParamMask(uInt which, const Bool mask); // virtual Bool getAvailableParamMask(uInt which) const; Sinusoid1D s2(s1); Sinusoid1D s3; s3 = s2; AlwaysAssertExit(s1.nparameters() == 3); Vector parms = s1.parameters().getParameters(); AlwaysAssertExit(parms(0) == 2.0 && parms(1) == 3.0 && parms(2) == 4.0); AlwaysAssertExit(allEQ(parms, s2.parameters().getParameters()) && allEQ(parms, s3.parameters().getParameters())); s1.mask(Sinusoid1D::PERIOD) = False; AlwaysAssertExit(s1.parameters().nMaskedParameters() == 2); Vector parms2 = s1.parameters().getMaskedParameters(); AlwaysAssertExit(parms2(0) == 2.0 && parms2(1) == 4.0); s1.mask(Sinusoid1D::PERIOD) = True; s1[0] = 1.0; s1[1] = 2.0; s1[2] = 3.0; AlwaysAssertExit(s1.amplitude()==1.0 && s1.period() == 2.0 && s1.x0()==3.0); parms = 11.0; s1.parameters().setParameters(parms); AlwaysAssertExit(allEQ(s1.parameters().getParameters(), 11.0)); // clone() // ~Sinusoid1D(); Function *s4ptr = s1.clone(); AlwaysAssertExit(allEQ(s4ptr->parameters().getParameters(), 11.0)); delete s4ptr; cout << "OK" << endl; return 0; } casacore-3.7.1/scimath/Mathematics.h000066400000000000000000000101241476623553700173620ustar00rootroot00000000000000//# Mathematics.h: Module header for Mathematical operations //# Copyright (C) 1995,1996,1997,1998,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_MATHEMATICS_H #define SCIMATH_MATHEMATICS_H #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Mathematical types, constants, operations // //
      • At least high school (and preferably undergraduate // level) understanding of mathematics. // // // // // Mathematicians may argue that everything is mathematics, and hence all of // Casacore should be in this module. However this module will only contain // core mathematical operations that are independent of astronomical // applications. // // // The Mathematics module has a variety of mathematical classes and functions. // Not all numerical operations are found herein. Very complicated operations // might be in their own module. such as deconvolution. // Many whole array operations // are in the ArrayMath global functions // (part of the Arrays module). Mathematical // operations on Lattices are found in the Lattices module. A wide variety of special // Mathematical functions is planned for the Functionals module. // // The classes presently in this module fall into the following categories: //
          //
        • A wrapper around the system math.h functions called // Math.h. This contains generic // mathematical functions. It is required that you always include this // file rather than the system math.h file as deficiencies in the system // math.h file will be implemented here. //
        • Relationships // between different numerical data types. //
        • Multi-dimensional Fourier transforms are done in the // FFTServer class. This decomposes // the transforms into a one-dimensional transforms. //
        • Numerical Convolution // (both linear and circular) of multi-dimensional Arrays. //
        • Random numbers in a wide // variety of distributions. //
        • Interpolation in one dimension is performed by the // Interpolate1D class in // the Functionals module //
        //
        // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/000077500000000000000000000000001476623553700172135ustar00rootroot00000000000000casacore-3.7.1/scimath/Mathematics/AutoDiff.h000066400000000000000000000314571476623553700210770ustar00rootroot00000000000000//# AutoDiff.h: An automatic differentiating class for functions //# Copyright (C) 1995,1998,1999,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_AUTODIFF_H #define SCIMATH_AUTODIFF_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Class that computes partial derivatives by automatic differentiation. // // // // // // // // //
      • // // // // Class that computes partial derivatives by automatic differentiation, thus // AutoDiff. // // // // Class that computes partial derivatives by automatic differentiation. // It does this by storing the value of a function and the values of its first // derivatives with respect to its independent parameters. When a mathematical // operation is applied to an AutoDiff object, the derivative values of the // resulting new object are computed according to chain rules // of differentiation. // // Suppose we have a function f(x0,x1,...,xn) and its differential is // // df = (df/dx0)*dx0 + (df/dx1)*dx1 + ... + (df/dxn)*dxn // // We can build a class that has the value of the function, // f(x0,x1,...,xn), and the values of the derivatives, (df/dx0), (df/dx1), // ..., (df/dxn) at (x0,x1,...,xn), as class members. // // Now if we have another function, g(x0,x1,...,xn) and its differential is // dg = (dg/dx0)*dx0 + (dg/dx1)*dx1 + ... + (dg/dxn)*dxn, // since // // d(f+g) = df + dg, // d(f*g) = g*df + f*dg, // d(f/g) = df/g - fdg/g^2, // dsin(f) = cos(f)df, // ..., // // we can calculate // // d(f+g), d(f*g), ..., // based on our information on // // df/dx0, df/dx1, ..., dg/dx0, dg/dx1, ..., dg/dxn. // // All we need to do is to define the operators and derivatives of common // mathematical functions. // // To be able to use the class as an automatic differentiator of a function // object, we need a templated function object, i.e. an object with: //
          //
        • a template T operator()(const T) //
        • or multiple variable input like: // template T operator()(const Vector &) //
        • all variables and constants used in the calculation of the function // value should have been typed with T //
        // A simple example of such a function object could be: // // template f { // public: // T operator()(const T &x, const T &a, const T &b) { // return a*b*x; } // }; // // Instantiate the following versions: // template class f; // template class f >; // // A call with values will produce the function value: // // cout << f(7.0, 2.0, 3.0) << endl; // // will produce the value at x=7 for a=2; b=3: // 42 // // But a call indicating that we want derivatives to a and b: // cout << f(AutoDiff(7.0), AutoDiff(2.0, 2, 0), // AutoDiff(3.0, 2, 1)) << endl; // // will produce the value at x=7 for a=2; b=3: // // and the partial derivatives wrt a and b at x=7: // (42, [21, 14]) // // The following will calculate the derivate wrt x: // cout << f(AutoDiff(7.0, 1, 0), AutoDiff(2.0), // AutoDiff(3.0)) << endl; // (42,[6]) // // In actual practice, there are a few rules to obey for the structure of // the function object if you want to use the function object and its // derivatives in least squares fitting procedures in the Fitting // module. The major one is to view the function object having 'fixed' and // 'variable' parameters. I.e., rather than viewing the function as // depending on parameters a, b, x (f(a,b,x)), the // function is considered to be f(x; a,b), where a, b // are 'fixed' parameters, and x a variable parameter. // Fixed parameters should be contained in a // FunctionParam container object; // while the variable parameter(s) are given in the function // operator(). See Function class // for details. // // A Gaussian spectral profile would in general have the center frequency, // the width and the amplitude as fixed parameters, and the frequency as // a variable. Given a spectrum, you would solve for the fixed parameters, // given spectrum values. However, in other cases the role of the // parameters could be reversed. An example could be a whole stack of // observed (in the laboratory) spectra at different temperatures at // one frequency. In that case the width would be the variable parameter, // and the frequency one of the fixed (and to be solved for)parameters. // // Since the calculation of the derivatives is done with simple overloading, // the calculation of second (and higher) derivatives is easy. It should be // noted that higher deivatives are inefficient in the current incarnation // (there is no knowledge e.g. about symmetry in the Jacobian). However, // it is a very good way to get the correct answers of the derivatives. In // practice actual production code will be better off with specialization // of the f > implementation. // // The AutoDiff class is the class the user communicates with. // Alias classes (AutoDiffA and // AutoDiffX) exists // to make it possible to have different incarnations of a templated // method (e.g. a generic one and a specialized one). See the // dAutoDiff demo for an example of its use. // // All operators and functions are declared in // AutoDiffMath. The output operator in // AutoDiffIO. //
        // // // // // First a simple example. // // We have a function of the form f(x,y,z); and want to know the // // value of the function for x=10; y=20; z=30; and for // // the derivatives at those point. // // Specify the values; and indicate 3 derivatives: // AutoDiff x(10.0, 3, 0); // AutoDiff y(20.0, 3, 1); // AutoDiff z(30.0, 3, 2); // // The result will be: // AutoDiff result = x*y + sin(z); // cout << result.value() << endl; // // 199.012 // cout << result.derivatives() << endl; // // [20, 10, 0.154251] // // Note: sin(30) = -0.988; cos(30) = 0.154251; // // // See for an extensive example the demo program dAutoDiff. It is // based on the example given above, and shows also the use of second // derivatives (which is just using AutoDiff > // as template argument). // // // The function, with fixed parameters a,b: // template class f { // public: // T operator()(const T& x) { return a_p*a_p*a_p*b_p*b_p*x; } // void set(const T& a, const T& b) { a_p = a; b_p = b; } // private: // T a_p; // T b_p; // }; // // Call it with different template arguments: // Double a0(2), b0(3), x0(7); // f f0; f0.set(a0, b0); // cout << "Value: " << f0(x0) << endl; // // AutoDiff a1(2,2,0), b1(3,2,1), x1(7); // f > f1; f1.set(a1, b1); // cout << "Diff a,b: " << f1(x1) << endl; // // AutoDiff a2(2), b2(3), x2(7,1,0); // f > f2; f2.set(a2, b2); // cout << "Diff x: " << f2(x2) << endl; // // AutoDiff > a3(AutoDiff(2,2,0),2,0), // b3(AutoDiff(3,2,1),2,1), x3(AutoDiff(7),2); // f > > f3; f3.set(a3, b3); // cout << "Diff2 a,b: " << f3(x3) << endl; // // AutoDiff > a4(AutoDiff(2),1), // b4(AutoDiff(3),1), // x4(AutoDiff(7,1,0),1,0); // f > > f4; f4.set(a4, b4); // cout << "Diff2 x: " << f4(x4) << endl; // // // Result will be: // // Value: 504 // // Diff a,b: (504, [756, 336]) // // Diff x: (504, [72]) // // Diff2 a,b: ((504, [756, 336]), [(756, [756, 504]), (336, [504, 112])]) // // Diff2 x: ((504, [72]), [(72, [0])]) // // // It needed the template instantiations definitions: // template class f; // template class f >; // template class f > >; // // // // // The creation of the class was motivated by least-squares non-linear fit where // partial derivatives of a fitted function are needed. It would be tedious // to create functionals for all partial derivatives of a function. // // // //
      • any class that has the standard mathematical and comparisons // defined // // // //
      • Nothing I know // template class AutoDiff { public: //# Typedefs typedef T value_type; typedef value_type& reference; typedef const value_type& const_reference; typedef value_type* iterator; typedef const value_type* const_iterator; //# Constructors // Construct a constant with a value of zero. Zero derivatives. AutoDiff(); // Construct a constant with a value of v. Zero derivatives. AutoDiff(const T &v); // A function f(x0,x1,...,xn,...) with a value of v. The // total number of derivatives is ndiffs, the nth derivative is one, and all // others are zero. AutoDiff(const T &v, const uInt ndiffs, const uInt n); // A function f(x0,x1,...,xn,...) with a value of v. The // total number of derivatives is ndiffs. // All derivatives are zero. AutoDiff(const T &v, const uInt ndiffs); // Construct one from another AutoDiff(const AutoDiff &other); // Construct a function f(x0,x1,...,xn) of a value v and a vector of // derivatives derivs(0) = df/dx0, derivs(1) = df/dx1, ... AutoDiff(const T &v, const Vector &derivs); ~AutoDiff(); // Assignment operator. Assign a constant to variable. All derivatives // are zero. AutoDiff &operator=(const T &v); // Assign one to another. AutoDiff &operator=(const AutoDiff &other); // In-place mathematical operators // void operator*=(const AutoDiff &other); void operator/=(const AutoDiff &other); void operator+=(const AutoDiff &other); void operator-=(const AutoDiff &other); void operator*=(const T other); void operator/=(const T other); void operator+=(const T other); void operator-=(const T other); // // Returns the value of the function // T &value() { return val_p; } const T &value() const { return val_p; } // // Returns a vector of the derivatives of an AutoDiff // const Vector& derivatives() const {return grad_p; } Vector& derivatives() {return grad_p; } void derivatives(Vector &res) const; // // Returns a specific derivative. The second set does not check for // a valid which; the first set does through Vector addressing. // T &derivative(uInt which) { return grad_p(which); } const T &derivative(uInt which) const { return grad_p(which); } T &deriv(uInt which) { return grad_p[which]; } const T &deriv(uInt which) const { return grad_p[which]; } // // Return total number of derivatives uInt nDerivatives() const { return nd_p; } // Is it a constant, i.e., with zero derivatives? Bool isConstant() const { return nd_p == 0; } private: //# Data // The function value T val_p; // The number of derivatives uInt nd_p; // The derivatives Vector grad_p; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Mathematics/AutoDiff.tcc000066400000000000000000000115341476623553700214130ustar00rootroot00000000000000//# AutoDiff.cc: An automatic differentiating class for functions //# Copyright (C) 1995,1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_AUTODIFF_TCC #define SCIMATH_AUTODIFF_TCC //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template AutoDiff::AutoDiff() : val_p(T(0.0)), nd_p(0), grad_p(0) {} template AutoDiff::AutoDiff(const T &v) : val_p(v), nd_p(0), grad_p(0) {} template AutoDiff::AutoDiff(const T &v, const uInt ndiffs, const uInt n) : val_p(v), nd_p(ndiffs), grad_p(ndiffs) { grad_p = T(0); grad_p[n] = T(1); } template AutoDiff::AutoDiff(const T &v, const uInt ndiffs) : val_p(v), nd_p(ndiffs), grad_p(ndiffs) { grad_p = T(0); } template AutoDiff::AutoDiff(const AutoDiff &other) : val_p(other.val_p), nd_p(other.nd_p), grad_p(other.nd_p) { grad_p = other.grad_p; } template AutoDiff::AutoDiff(const T &v, const Vector &derivs) : val_p(v), nd_p(derivs.nelements()), grad_p(nd_p) { grad_p = derivs; } template AutoDiff::~AutoDiff() {} template AutoDiff &AutoDiff::operator=(const T &v) { val_p = v; nd_p = 0; grad_p.resize(nd_p); return *this; } template AutoDiff &AutoDiff::operator=(const AutoDiff &other) { if (this != &other) { val_p = other.val_p; nd_p = other.nd_p; grad_p.resize(nd_p); grad_p = other.grad_p; } return *this; } template void AutoDiff::operator*=(const AutoDiff &other) { if (other.nd_p != 0) { if (nd_p == 0) { nd_p = other.nd_p; grad_p = other.grad_p * val_p; } else { AlwaysAssert (nd_p == other.nd_p, AipsError); for (uInt i=0; i void AutoDiff::operator/=(const AutoDiff &other) { T temp = other.val_p * other.val_p; if (other.nd_p != 0) { if (nd_p == 0) { nd_p = other.nd_p; grad_p = other.grad_p * (-val_p/temp); ///val_p = other.val_p; } else { AlwaysAssert (nd_p == other.nd_p, AipsError); for (uInt i=0; i void AutoDiff::operator+=(const AutoDiff &other) { if (other.nd_p != 0) { if (nd_p == 0) { nd_p = other.nd_p; grad_p = other.grad_p; } else { AlwaysAssert (nd_p == other.nd_p, AipsError); grad_p += other.grad_p; } } val_p += other.val_p; } template void AutoDiff::operator-=(const AutoDiff &other) { if (other.nd_p != 0) { if (nd_p == 0) { nd_p = other.nd_p; grad_p = -other.grad_p; } else { AlwaysAssert (nd_p == other.nd_p, AipsError); grad_p -= other.grad_p; } } val_p -= other.val_p; } template void AutoDiff::operator*=(const T other) { grad_p *= other; val_p *= other; } template void AutoDiff::operator/=(const T other) { grad_p /= other; val_p /= other; } template void AutoDiff::operator+=(const T other) { val_p += other; } template void AutoDiff::operator-=(const T other) { val_p -= other; } template void AutoDiff::derivatives(Vector &res) const { res.resize(nd_p); res = grad_p; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/AutoDiffA.h000066400000000000000000000122751476623553700211750ustar00rootroot00000000000000//# AutoDiffA.h: An automatic differentiating class for functions //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_AUTODIFFA_H #define SCIMATH_AUTODIFFA_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Class that computes partial derivatives by automatic differentiation. // // // // // // // // //
      • AutoDiff // // // // Class that computes partial derivatives by automatic differentiation, thus // AutoDiff. // // // // AutoDiffA is an AutoDiff. It is used // to be able to distinguish between two template incarnations; e.g. to // have one or more specializations, in addition to the general template // version. // // // // See for an extensive example the demo program dAutoDiff. It is // based on the example given in the AutoDiff // class, and shows how to have both an automatic and a specific version // of a function object. // // // The function, with fixed parameters a,b: // template class f { // public: // T operator()(const T& x) { return a_p*a_p*a_p*b_p*b_p*x; } // void set(const T& a, const T& b) { a_p = a; b_p = b; } // private: // T a_p; // T b_p; // }; // // The specialized function // template <> class f > { // public: // T operator()(const T& x) { return a_p*a_p*a_p*b_p*b_p*x; } // void set(const T& a, const T& b) { a_p = a; b_p = b; } // private: // T a_p; // T b_p; // }; // // Call it with different template arguments: // AutoDiff a1(2,2,0), b1(3,2,1), x1(7); // f > f1; f1.set(a1, b1); // cout << "Diff a,b: " << f1(x1) << endl; // // f > f12; f12.set(a1, b1); // cout << "Same....: " << f12(x1) << endl; // // // Result will be: // // Diff a,b: (504, [756, 336]) // // Same....: (504, [756, 336]) // // // It needed the template instantiations definitions: // template class f >; // // // // // The class was created to enable separate calculations of the same // function. // // // //
      • any class that has the standard mathematical and comparisons // defined // // // //
      • Nothing I know // template class AutoDiffA : public AutoDiff { public: //# Constructors // Construct a constant with a value of zero. Zero derivatives. AutoDiffA() : AutoDiff() {} // Construct a constant with a value of v. Zero derivatives. AutoDiffA(const T &v) : AutoDiff(v) {} // A function f(x0,x1,...,xn,...) with a value of v. The // total number of derivatives is ndiffs, the nth derivative is one, and all // others are zero. AutoDiffA(const T &v, const uInt ndiffs, const uInt n) : AutoDiff(v, ndiffs, n) {} // A function f(x0,x1,...,xn,...) with a value of v. The // total number of derivatives is ndiffs. // All derivatives are zero. AutoDiffA(const T &v, const uInt ndiffs) : AutoDiff(v, ndiffs) {} // Construct one from another AutoDiffA(const AutoDiff &other) : AutoDiff(other) {} // Construct a function f(x0,x1,...,xn) of a value v and a vector of // derivatives derivs(0) = df/dx0, derivs(1) = df/dx1, ... AutoDiffA(const T &v, const Vector &derivs) : AutoDiff(v, derivs) {} ~AutoDiffA() {} // Assignment operator. Assign a constant to variable. All derivatives // are zero. AutoDiffA &operator=(const T &v) { AutoDiff::operator=(v); return *this; } // Assign one to another. AutoDiffA &operator=(const AutoDiff &other) { AutoDiff::operator=(other); return *this; } private: //# Data }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/AutoDiffIO.h000066400000000000000000000042731476623553700213230ustar00rootroot00000000000000//# AutoDiffIO.h: Output for AutoDiff objects //# Copyright (C) 1995,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_AUTODIFFIO_H #define SCIMATH_AUTODIFFIO_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations template class AutoDiff; // // Implements all IO operators and functions for AutoDiff. // // // // // // //
      • AutoDiff class // // // // Implements all IO operators and functions for AutoDiff. // // // //
      • Nothing I know of // // template ostream &operator << (ostream &os, const AutoDiff &ad); // } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Mathematics/AutoDiffIO.tcc000066400000000000000000000033541476623553700216440ustar00rootroot00000000000000//# AutoDiffIO.cc: text output for AutoDiff //# Copyright (C) 1995,1996,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_AUTODIFFIO_TCC #define SCIMATH_AUTODIFFIO_TCC //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template ostream &operator<<(ostream &os, const AutoDiff &ad) { os << "(" << ad.value() << ", " << ad.derivatives() << ")"; return os; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/AutoDiffMath.h000066400000000000000000000221011476623553700216730ustar00rootroot00000000000000//# AutoDiffMath.h: Implements all mathematical functions for AutoDiff. //# Copyright (C) 1995,1999,2001,2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_AUTODIFFMATH_H #define SCIMATH_AUTODIFFMATH_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Implements all mathematical operators and functions for AutoDiff. // // // // // // //
      • AutoDiff class // // // // Implements all mathematical operators and functions for AutoDiff. // // // //
      • nothing I know of // // // Unary arithmetic operators. // template AutoDiff operator+(const AutoDiff &other); template AutoDiff operator-(const AutoDiff &other); // // Arithmetic on two AutoDiff objects, returning an AutoDiff object // template AutoDiff operator+(const AutoDiff &left, const AutoDiff &right); template AutoDiff operator-(const AutoDiff &left, const AutoDiff &right); template AutoDiff operator*(const AutoDiff &left, const AutoDiff &right); template AutoDiff operator/(const AutoDiff &left, const AutoDiff &right); // // Arithmetic on an AutoDiff and a scalar, returning an AutoDiff // template AutoDiff operator+(const AutoDiff &left, const T &right); template AutoDiff operator-(const AutoDiff &left, const T &right); template AutoDiff operator*(const AutoDiff &left, const T &right); template AutoDiff operator/(const AutoDiff &left, const T &right); // // Arithmetic between a scalar and an AutoDiff returning an AutoDiff // template AutoDiff operator+(const T &left, const AutoDiff &right); template AutoDiff operator-(const T &left, const AutoDiff &right); template AutoDiff operator*(const T &left, const AutoDiff &right); template AutoDiff operator/(const T &left, const AutoDiff &right); // // Transcendental functions // template AutoDiff acos(const AutoDiff &ad); template AutoDiff asin(const AutoDiff &ad); template AutoDiff atan(const AutoDiff &ad); template AutoDiff atan2(const AutoDiff &y, const AutoDiff &x); template AutoDiff cos(const AutoDiff &ad); template AutoDiff cosh(const AutoDiff &ad); template AutoDiff exp(const AutoDiff &ad); template AutoDiff log(const AutoDiff &ad); template AutoDiff log10(const AutoDiff &ad); template AutoDiff erf(const AutoDiff &ad); template AutoDiff erfc(const AutoDiff &ad); template AutoDiff pow(const AutoDiff &a, const AutoDiff &b); template AutoDiff pow(const AutoDiff &a, const T &b); template AutoDiff square(const AutoDiff &ad); template AutoDiff cube(const AutoDiff &ad); template AutoDiff sin(const AutoDiff &ad); template AutoDiff sinh(const AutoDiff &ad); template AutoDiff sqrt(const AutoDiff &ad); template AutoDiff tan(const AutoDiff &ad); template AutoDiff tanh(const AutoDiff &ad); template AutoDiff abs(const AutoDiff &ad); // // Floating-point remainder of x/c, with the same sign as x, where c is // a constant. // template AutoDiff fmod(const AutoDiff &x, const T &c); template AutoDiff fmod(const AutoDiff &x, const AutoDiff &c); // // Floor and ceil of values // template AutoDiff floor(const AutoDiff &ad); template AutoDiff ceil(const AutoDiff &ad); // // Comparison operators. Only the values are compared: in the actual // functions, comparisons are used to decide on algorithms. To check // if two AutoDiff values are equal, use comparison for both // value and derivatives. // To check if two AutoDiff values are equal, use the // member method equals() (e.g. for debugging and testing). // // // Compare two AutoDiff's template Bool operator>(const AutoDiff &left, const AutoDiff &right); template Bool operator<(const AutoDiff &left, const AutoDiff &right); template Bool operator>=(const AutoDiff &left, const AutoDiff &right); template Bool operator<=(const AutoDiff &left, const AutoDiff &right); template Bool operator==(const AutoDiff &left, const AutoDiff &right); template Bool operator!=(const AutoDiff &left, const AutoDiff &right); template Bool near(const AutoDiff &left, const AutoDiff &right); template Bool near(const AutoDiff &left, const AutoDiff &right, const Double tol); template Bool allnear(const AutoDiff &left, const AutoDiff &right, const Double tol); template Bool nearAbs(const AutoDiff &left, const AutoDiff &right, const Double tol); template Bool allnearAbs(const AutoDiff &left, const AutoDiff &right, const Double tol); // // Compare an AutoDiff and a constant // template Bool operator>(const AutoDiff &left, const T &right); template Bool operator<(const AutoDiff &left, const T &right); template Bool operator>=(const AutoDiff &left, const T &right); template Bool operator<=(const AutoDiff &left, const T &right); template Bool operator==(const AutoDiff &left, const T &right); template Bool operator!=(const AutoDiff &left, const T &right); template Bool near(const AutoDiff &left, const T &right); template Bool near(const AutoDiff &left, const T &right, const Double tol); template Bool allnear(const AutoDiff &left, const T &right, const Double tol); template Bool nearAbs(const AutoDiff &left, const T &right, const Double tol); template Bool allnearAbs(const AutoDiff &left, const T &right, const Double tol); // // Compare a constant and an AutoDiff // template Bool operator>(const T &left, const AutoDiff &right); template Bool operator<(const T &left, const AutoDiff &right); template Bool operator>=(const T &left, const AutoDiff &right); template Bool operator<=(const T &left, const AutoDiff &right); template Bool operator==(const T &left, const AutoDiff &right); template Bool operator!=(const T &left, const AutoDiff &right); template Bool near(const T &left, const AutoDiff &right, const Double tol); template Bool allnear(const T &left, const AutoDiff &right, const Double tol); template Bool nearAbs(const T &left, const AutoDiff &right, const Double tol); template Bool allnearAbs(const T &left, const AutoDiff &right, const Double tol); // // Test special values // template Bool isNaN(const AutoDiff &val); template Bool isInf(AutoDiff &val); // // Minimum/maximum // template AutoDiff min(const AutoDiff &left, const AutoDiff &right); template AutoDiff max(const AutoDiff &left, const AutoDiff &right); // // } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Mathematics/AutoDiffMath.tcc000066400000000000000000000357451476623553700222370ustar00rootroot00000000000000//# AutoDiffMath.cc: Implements all mathematical functions for AutoDiff. //# Copyright (C) 1995,1996,1999,2001,2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_AUTODIFFMATH_TCC #define SCIMATH_AUTODIFFMATH_TCC //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Unary arithmetic operators. template AutoDiff operator+(const AutoDiff &other) { AutoDiff tmp(other); return tmp; } template AutoDiff operator-(const AutoDiff &other) { AutoDiff tmp(other); tmp *= T(-1); return tmp; } // Binary arithmetic operators template AutoDiff operator+(const AutoDiff &left, const AutoDiff &right) { AutoDiff tmp(left); tmp += right; return tmp; } template AutoDiff operator-(const AutoDiff &left, const AutoDiff &right) { AutoDiff tmp(left); tmp -= right; return tmp; } template AutoDiff operator*(const AutoDiff &left, const AutoDiff &right) { AutoDiff tmp(left); tmp *= right; return tmp; } template AutoDiff operator/(const AutoDiff &left, const AutoDiff &right) { AutoDiff tmp(left); tmp /= right; return tmp; } template AutoDiff operator+(const AutoDiff &left, const T &right) { AutoDiff tmp(left); tmp += right; return tmp; } template AutoDiff operator-(const AutoDiff &left, const T &right) { AutoDiff tmp(left); tmp -= right; return tmp; } template AutoDiff operator* (const AutoDiff &left, const T &right) { AutoDiff tmp(left); tmp *= right; return tmp; } template AutoDiff operator/(const AutoDiff &left, const T &right) { AutoDiff tmp(left); tmp /= right; return tmp; } template AutoDiff operator+(const T &left, const AutoDiff &right) { AutoDiff tmp(right); tmp += left; return tmp; } template AutoDiff operator-(const T &left, const AutoDiff &right) { AutoDiff tmp(right); tmp *= T(-1); tmp += left; return tmp; } template AutoDiff operator*(const T &left, const AutoDiff &right) { AutoDiff tmp(right); tmp *= left; return tmp; } template AutoDiff operator/(const T &left, const AutoDiff &right) { AutoDiff tmp(right); T tv = right.value(); tmp.value() = left/tv; tmp.derivatives() *= -tmp.value()/tv; return tmp; } template AutoDiff acos(const AutoDiff &ad) { AutoDiff tmp(ad); T tv = tmp.value(); tmp.derivatives() /= T(-sqrt(T(1) - tv*tv)); tmp.value() = acos(tv); return tmp; } template AutoDiff asin(const AutoDiff &ad) { AutoDiff tmp(ad); T tv = tmp.value(); tmp.derivatives() /= T(sqrt(T(1) - tv*tv)); tmp.value() = asin(tv); return tmp; } template AutoDiff atan(const AutoDiff &ad) { AutoDiff tmp(ad); T tv = tmp.value(); tmp.derivatives() /= T(1) + tv*tv; tmp.value() = atan(tv); return tmp; } template AutoDiff atan2(const AutoDiff &y, const AutoDiff &x) { // this gets the derivative right, via the chain rule using the already // defined / and atan functions, but the value may be wrong AutoDiff tmp = atan(y/x); // get the value right tmp.value() = atan2(y.value(), x.value()); return tmp; } template AutoDiff cos(const AutoDiff &ad) { AutoDiff tmp(ad); T tv = tmp.value(); tmp.derivatives() *= T(-sin(tv)); tmp.value() = cos(tv); return tmp; } template AutoDiff cosh(const AutoDiff &ad) { AutoDiff tmp(ad); T tv = tmp.value(); tmp.derivatives() *= T(sinh(tv)); tmp.value() = cosh(tv); return tmp; } template AutoDiff sin(const AutoDiff &ad) { AutoDiff tmp(ad); T tv = tmp.value(); tmp.derivatives() *= T(cos(tv)); tmp.value() = sin(tv); return tmp; } template AutoDiff sinh(const AutoDiff &ad) { AutoDiff tmp(ad); T tv = tmp.value(); tmp.derivatives() *= T(cosh(tv)); tmp.value() = sinh(tv); return tmp; } template AutoDiff exp(const AutoDiff &ad) { AutoDiff tmp(ad); tmp.value() = exp(ad.value()); tmp.derivatives() *= tmp.value(); return tmp; } template AutoDiff log(const AutoDiff &ad) { AutoDiff tmp(ad); T tv = tmp.value(); tmp.derivatives() /= tv; tmp.value() = log(tv); return tmp; } template AutoDiff log10(const AutoDiff &ad) { static const T l10 = T(log(10.0)); AutoDiff tmp(ad); T tv = tmp.value(); tmp.derivatives() /= tv*l10; tmp.value() = log10(tv); return tmp; } template AutoDiff erf(const AutoDiff &ad) { AutoDiff tmp(ad); T tv = tmp.value(); tmp.derivatives() *= T(T(M_2_SQRTPI)*exp(-tv*tv)); tmp.value() = erf(tv); return tmp; } template AutoDiff erfc(const AutoDiff &ad) { AutoDiff tmp(ad); T tv = tmp.value(); tmp.derivatives() *= T(T(-M_2_SQRTPI)*exp(-tv*tv)); tmp.value() = erfc(tv); return tmp; } template AutoDiff pow(const AutoDiff &a, const AutoDiff &b) { if (b.nDerivatives() == 0) return pow(a, b.value()); T ta = a.value(); T tb = b.value(); T value = pow(ta, tb); T temp2 = tb * pow(ta, tb - T(1)); AutoDiff tmp(b); tmp.derivatives() *= value * T(log(ta)); for (uInt i=0; i AutoDiff pow(const AutoDiff &a, const T &b) { AutoDiff tmp(a); T ta = a.value(); tmp.derivatives() *= b*pow(ta, b-T(1)); tmp.value() = pow(ta, b); return tmp; } template AutoDiff square(const AutoDiff &ad) { AutoDiff tmp(ad); tmp.value() = square(tmp.value()); tmp.derivatives() *= T(2)*tmp.value(); return tmp; } template AutoDiff cube(const AutoDiff &ad) { AutoDiff tmp(ad); tmp.value() = cube(tmp.value()); tmp.derivatives() *= T(3)*square(tmp.value()); return tmp; } template AutoDiff sqrt(const AutoDiff &ad) { AutoDiff tmp(ad); tmp.value() = sqrt(tmp.value()); tmp.derivatives() /= T(2)*tmp.value(); return tmp; } template AutoDiff tan(const AutoDiff &ad) { AutoDiff tmp(ad); T tv = tmp.value(); T temp = cos(tv); temp *= temp; tmp.derivatives() /= temp; tmp.value() = tan(tv); return tmp; } template AutoDiff tanh(const AutoDiff &ad) { AutoDiff tmp(ad); T tv = tmp.value(); T temp = cosh(tv); temp *= temp; tmp.derivatives() /= temp; tmp.value() = tanh(tv); return tmp; } template AutoDiff abs(const AutoDiff &ad) { // Here we assume that function F represented by ad is continous and // differentiable in a small enough neighborhood where F is // evaluated. So if ad.value() is positive, F is positive in // the small neighborhood. AutoDiff tmp(ad); if (ad.value() < T(0)) tmp *= T(-1); return tmp; } template AutoDiff fmod(const AutoDiff &x, const T &c) { // Floating-point remainder of x/c, with the same sign as x, where c is // a constant. Since fmod(x,c) = x - ((int)(x/c))*c and d[(int)(z)]/dz = 0, // d(fmod(x,c))/dx = 1. At z = integer, (int)(z) is discontinuous, but // away from the point (int)(z) is well defined and has derivative (=0) // we use the derivative at z = integer+epsilon as the derivative at // z = integer. AutoDiff tmp(x); tmp.value() = fmod(x.value(), c); return tmp; } template AutoDiff fmod(const AutoDiff &x, const AutoDiff &c) { AutoDiff tmp(x); tmp.value() = fmod(x.value(), c.value()); return tmp; } template AutoDiff floor(const AutoDiff &ad) { AutoDiff tmp(ad); tmp.value() = floor(ad.value()); tmp.derivatives() = T(0); return tmp; } template AutoDiff ceil(const AutoDiff &ad) { AutoDiff tmp(ad); tmp.value() = ceil(ad.value()); tmp.derivatives() = T(0); return tmp; } template Bool operator>(const AutoDiff &left, const AutoDiff &right) { return (left.value() > right.value()); } template Bool operator<(const AutoDiff &left, const AutoDiff &right) { return (left.value() < right.value()); } template Bool operator>=(const AutoDiff &left, const AutoDiff &right) { return (left.value() >= right.value()); } template Bool operator<=(const AutoDiff &left, const AutoDiff &right) { return (left.value() <= right.value()); } template Bool operator==(const AutoDiff &left, const AutoDiff &right) { return (left.value() == right.value()); } template Bool operator!=(const AutoDiff &left, const AutoDiff &right) { return (left.value() != right.value()); } // Compare an AutoDiff and a constant template Bool operator>(const AutoDiff &left,const T &right) { return (left.value() > right); } template Bool operator<(const AutoDiff &left,const T &right) { return (left.value() < right); } template Bool operator>=(const AutoDiff &left,const T &right) { return (left.value() >= right); } template Bool operator<=(const AutoDiff &left,const T &right) { return (left.value() <= right); } template Bool operator==(const AutoDiff &left,const T &right) { return (left.value() == right); } template Bool operator!=(const AutoDiff &left,const T &right) { return (left.value() != right); } // Compare a constant and an AutoDiff template Bool operator>(const T &left, const AutoDiff &right) { return (left > right.value()); } template Bool operator<(const T &left, const AutoDiff &right) { return (left < right.value()); } template Bool operator>=(const T &left, const AutoDiff &right) { return (left >= right.value()); } template Bool operator<=(const T &left, const AutoDiff &right) { return (left <= right.value()); } template Bool operator==(const T &left, const AutoDiff &right) { return (left == right.value()); } template Bool operator!=(const T &left, const AutoDiff &right) { return (left != right.value()); } // Near comparisons template Bool near(const AutoDiff &left, const AutoDiff &right) { return (near(left.value(), right.value())); } template Bool near(const T &left, const AutoDiff &right) { return near(left, right.value()); } template Bool near(const AutoDiff &left, const T &right) { return near(left.value(), right); } template Bool near(const AutoDiff &left, const AutoDiff &right, const Double tol) { return near(left.value(), right.value(), tol); } template Bool near(const T &left, const AutoDiff &right, const Double tol) { return near(left, right.value(), tol); } template Bool near(const AutoDiff &left, const T &right, const Double tol) { return near(left.value(), right, tol); } template Bool allnear(const AutoDiff &left, const AutoDiff &right) { return (near(left.value(), right.value())); } template Bool allnear(const T &left, const AutoDiff &right) { return near(left, right.value()); } template Bool allnear(const AutoDiff &left, const T &right) { return near(left.value(), right); } template Bool allnear(const AutoDiff &left, const AutoDiff &right, const Double tol) { return near(left.value(), right.value(), tol); } template Bool allnear(const T &left, const AutoDiff &right, const Double tol) { return near(left, right.value(), tol); } template Bool allnear(const AutoDiff &left, const T &right, const Double tol) { return near(left.value(), right, tol); } template Bool nearAbs(const AutoDiff &left, const AutoDiff &right) { return (nearAbs(left.value(), right.value())); } template Bool nearAbs(const T &left, const AutoDiff &right) { return nearAbs(left, right.value()); } template Bool nearAbs(const AutoDiff &left, const T &right) { return nearAbs(left.value(), right); } template Bool nearAbs(const AutoDiff &left, const AutoDiff &right, const Double tol) { return nearAbs(left.value(), right.value(), tol); } template Bool nearAbs(const T &left, const AutoDiff &right, const Double tol) { return nearAbs(left, right.value(), tol); } template Bool nearAbs(const AutoDiff &left, const T &right, const Double tol) { return nearAbs(left.value(), right, tol); } template Bool allnearAbs(const AutoDiff &left, const AutoDiff &right) { return (nearAbs(left.value(), right.value())); } template Bool allnearAbs(const T &left, const AutoDiff &right) { return nearAbs(left, right.value()); } template Bool allnearAbs(const AutoDiff &left, const T &right) { return nearAbs(left.value(), right); } template Bool allnearAbs(const AutoDiff &left, const AutoDiff &right, const Double tol) { return nearAbs(left.value(), right.value(), tol); } template Bool allnearAbs(const T &left, const AutoDiff &right, const Double tol) { return nearAbs(left, right.value(), tol); } template Bool allnearAbs(const AutoDiff &left, const T &right, const Double tol) { return nearAbs(left.value(), right, tol); } // Test special values template Bool isNaN (const AutoDiff &val) { return isNaN(val.value()); } template Bool isInf(AutoDiff &val) { return isInf(val.value()); } template AutoDiff min(const AutoDiff &left, const AutoDiff &right) { AutoDiff tmp = (left.value() <= right.value()) ? left : right; return tmp; } template AutoDiff max(const AutoDiff &left, const AutoDiff &right) { AutoDiff tmp = (left.value() <= right.value()) ? right : left; return tmp; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/AutoDiffX.h000066400000000000000000000122701476623553700212170ustar00rootroot00000000000000//# AutoDiffX.h: An automatic differentiating class for functions //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_AUTODIFFX_H #define SCIMATH_AUTODIFFX_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Class that computes partial derivatives by automatic differentiation. // // // // // // // // //
      • AutoDiff // // // // Class that computes partial derivatives by automatic differentiation, thus // AutoDiff. // // // // AutoDiffX is an AutoDiff. It is used // to be able to distinguish between two template incarnations; e.g. to // have one or more specializations, in addition to the general template // version. // // // // See for an extensive example the demo program dAutoDiff. It is // based on the example given in the AutoDiff // class, and shows how to have both an automatic and a specific version // of a function object. // // // The function, with fixed parameters a,b: // template class f { // public: // T operator()(const T& x) { return a_p*a_p*a_p*b_p*b_p*x; } // void set(const T& a, const T& b) { a_p = a; b_p = b; } // private: // T a_p; // T b_p; // }; // // The specialized function // template <> class f > { // public: // T operator()(const T& x) { return a_p*a_p*a_p*b_p*b_p*x; } // void set(const T& a, const T& b) { a_p = a; b_p = b; } // private: // T a_p; // T b_p; // }; // // Call it with different template arguments: // AutoDiff a1(2,2,0), b1(3,2,1), x1(7); // f > f1; f1.set(a1, b1); // cout << "Diff a,b: " << f1(x1) << endl; // // f > f12; f12.set(a1, b1); // cout << "Same....: " << f12(x1) << endl; // // // Result will be: // // Diff a,b: (504, [756, 336]) // // Same....: (504, [756, 336]) // // // It needed the template instantiations definitions: // template class f >; // // // // // The class was created to enable separate calculations of the same // function. // // // //
      • any class that has the standard mathematical and comparisons // defined // // // //
      • Nothing I know // template class AutoDiffX : public AutoDiff { public: //# Constructors // Construct a constant with a value of zero. Zero derivatives. AutoDiffX() : AutoDiff() {} // Construct a constant with a value of v. Zero derivatives. AutoDiffX(const T &v) : AutoDiff(v) {} // A function f(x0,x1,...,xn,...) with a value of v. The // total number of derivatives is ndiffs, the nth derivative is one, and all // others are zero. AutoDiffX(const T &v, const uInt ndiffs, const uInt n) : AutoDiff(v, ndiffs, n) {} // A function f(x0,x1,...,xn,...) with a value of v. The // total number of derivatives is ndiffs. // All derivatives are zero. AutoDiffX(const T &v, const uInt ndiffs) : AutoDiff(v, ndiffs) {} // Construct one from another AutoDiffX(const AutoDiff &other) : AutoDiff(other) {} // Construct a function f(x0,x1,...,xn) of a value v and a vector of // derivatives derivs(0) = df/dx0, derivs(1) = df/dx1, ... AutoDiffX(const T &v, const Vector &derivs) : AutoDiff(v, derivs) {} ~AutoDiffX() {} // Assignment operator. Assign a constant to variable. All derivatives // are zero. AutoDiffX &operator=(const T &v) { AutoDiff::operator=(v); return *this; } // Assign one to another. AutoDiffX &operator=(const AutoDiff &other) { AutoDiff::operator=(other); return *this; } private: //# Data }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/Combinatorics.cc000066400000000000000000000055011476623553700223170ustar00rootroot00000000000000//# Copyright (C) 2010 by ESO (in the framework of the ALMA collaboration) //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Initialize factorial with first 2 values (0! and 1! are both 1). Vector Combinatorics::_factorialCache(2,1); volatile uInt Combinatorics::_factorialCacheSize = 2; std::mutex Combinatorics::theirMutex; void Combinatorics::fillCache(const uInt n) { // Make updating the cache thread-safe. // After acquiring a lock, test again if an update needs to be done // because another thread might have done it in the mean time. // Need C++11 or later to implement double checked locking correctly. std::lock_guard lock(theirMutex); if (n >= _factorialCacheSize) { // Create a new cache vector. // Note: do not resize the existing one, because that makes // simultaneous read-access non thread-safe. Vector newCache(n+1); for (uInt i=0; i<_factorialCacheSize; ++i) { newCache[i] = _factorialCache[i]; } for (uInt i=_factorialCacheSize; i<=n; ++i) { newCache[i] = i * newCache[i-1]; } _factorialCache.reference (newCache); _factorialCacheSize = _factorialCache.size(); } } uInt Combinatorics::choose(const uInt n, const uInt k) { if (k > n) { throw AipsError("k cannot be greater than n"); } return factorial(n)/(factorial(k)*factorial(n-k)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/scimath/Mathematics/Combinatorics.h000066400000000000000000000050701476623553700221620ustar00rootroot00000000000000//# Smooth.h: smooth vectors and arrays //# Copyright (C) 2010 by ESO (in the framework of the ALMA collaboration) //# Copyright (C) 1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_COMBINATORICS_H #define SCIMATH_COMBINATORICS_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Combinatorics related functions. // // //# Dave Mehringer // // // //
      • Vector //
      • Array // // // self-explanatory // // // Various factorial and combinatorical functions. // // // Binomial coefficients needed for Images/ImageProfileFitter // class Combinatorics { public: // Get n! static uInt factorial(const uInt n) { fillCache(n); return _factorialCache[n]; } // "n choose k" = n!/(k!(n-k)!) // Exception is thrown if k > n. static uInt choose(const uInt n, const uInt k); private: static void fillCache(const uInt n); static Vector _factorialCache; static volatile uInt _factorialCacheSize; //# volatile for double checked lock static std::mutex theirMutex; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/ConvolveGridder.h000066400000000000000000000060511476623553700224620ustar00rootroot00000000000000//# ConvolveGridder.h: Definition for Convolutional Gridder //# Copyright (C) 1996,1997,1999,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_CONVOLVEGRIDDER_H #define SCIMATH_CONVOLVEGRIDDER_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Does convolutional gridding // template class ConvolveGridder : public Gridder { public: ConvolveGridder(const IPosition& shape, const Vector& scale, const Vector& offset, const String& convType="SF"); virtual void setConvolutionFunction(const String& type); virtual ~ConvolveGridder() {} virtual Bool grid(Array& gridded, const Vector& position, const Range& value); virtual Bool degrid(const Array& gridded, const Vector& position, Range& value); Vector& cFunction(); Vector& cSupport(); Int& cSampling(); protected: virtual Range correctionFactor1D(Int loc, Int len); private: Vector convFunc; Vector supportVec; Vector loc; Int sampling; Int support; String cType; public: using Gridder::onGrid; protected: //# Make members of parent classes known. using Gridder::ndim; using Gridder::shape; using Gridder::scale; using Gridder::offset; using Gridder::posVec; using Gridder::locVec; using Gridder::shapeVec; using Gridder::zeroShapeVec; using Gridder::offsetVec; using Gridder::centerVec; using Gridder::fillCorrectionVectors; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Mathematics/ConvolveGridder.tcc000066400000000000000000000313511476623553700230050ustar00rootroot00000000000000//# ConvolveGridder.cc: Convolutional Gridder //# Copyright (C) 1996,1997,1999,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_CONVOLVEGRIDDER_TCC #define SCIMATH_CONVOLVEGRIDDER_TCC #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN #define NEED_UNDERSCORES #if defined(NEED_UNDERSCORES) #define grdsf grdsf_ #define cgrd1d cgrd1d_ #define cgrd2d cgrd2d_ #define cgrd3d cgrd3d_ #define cdgrd1d cdgrd1d_ #define cdgrd2d cdgrd2d_ #define cdgrd3d cdgrd3d_ #define dgrd1d dgrd1d_ #define dgrd2d dgrd2d_ #define dgrd3d dgrd3d_ #define ddgrd1d ddgrd1d_ #define ddgrd2d ddgrd2d_ #define ddgrd3d ddgrd3d_ #define fgrd1d fgrd1d_ #define fgrd2d fgrd2d_ #define fgrd3d fgrd3d_ #define fdgrd1d fdgrd1d_ #define fdgrd2d fdgrd2d_ #define fdgrd3d fdgrd3d_ #endif extern "C" { void grdsf(Double*, Double*); void cgrd1d(Int*, Int*, Complex*, const Complex*, Int*, Int*, Double*, Double*); void cgrd2d(Int*, Int*, Int*, Int*, Complex*, const Complex*, Int*, Int*, Double*, Double*, Double*); void cgrd3d(Int*, Int*, Int *, Int*, Int*, Int *, Complex*, const Complex*, Int*, Int*, Double*, Double*, Double*, Double*); void cdgrd1d(Int*, Int*, const Complex*, Complex*, Int*, Int*, Double*, Double*); void cdgrd2d(Int*, Int*, Int*, Int*, const Complex*, Complex*, Int*, Int*, Double*, Double*, Double*); void cdgrd3d(Int*, Int*, Int *, Int*, Int*, Int *, const Complex*, Complex*, Int*, Int*, Double*, Double*, Double*, Double*); void fgrd1d(Int*, Int*, Float*, const Float*, Int*, Int*, Double*, Double*); void fgrd2d(Int*, Int*, Int*, Int*, Float*, const Float*, Int*, Int*, Double*, Double*, Double*); void fgrd3d(Int*, Int*, Int *, Int*, Int*, Int *, Float*, const Float*, Int*, Int*, Double*, Double*, Double*, Double*); void fdgrd1d(Int*, Int*, const Float*, Float*, Int*, Int*, Double*, Double*); void fdgrd2d(Int*, Int*, Int*, Int*, const Float*, Float*, Int*, Int*, Double*, Double*, Double*); void fdgrd3d(Int*, Int*, Int *, Int*, Int*, Int *, const Float*, Float*, Int*, Int*, Double*, Double*, Double*, Double*); void dgrd1d(Int*, Int*, Double*, const Double*, Int*, Int*, Double*, Double*); void dgrd2d(Int*, Int*, Int*, Int*, Double*, const Double*, Int*, Int*, Double*, Double*, Double*); void dgrd3d(Int*, Int*, Int *, Int*, Int*, Int *, Double*, const Double*, Int*, Int*, Double*, Double*, Double*, Double*); void ddgrd1d(Int*, Int*, const Double*, Double*, Int*, Int*, Double*, Double*); void ddgrd2d(Int*, Int*, Int*, Int*, const Double*, Double*, Int*, Int*, Double*, Double*, Double*); void ddgrd3d(Int*, Int*, Int *, Int*, Int*, Int *, const Double*, Double*, Int*, Int*, Double*, Double*, Double*, Double*); } // Double versions inline void grd1d(Int* ni, Int* li, Double* grid, const Double* value, Int* sampling, Int* support, Double* posi, Double* convFunc) { dgrd1d(ni, li, grid, value, sampling, support, posi, convFunc); } inline void grd2d(Int* ni, Int* nj, Int* li, Int* lj, Double* grid, const Double* value, Int* sampling, Int* support, Double* posi, Double* posj, Double* convFunc) { dgrd2d(ni, nj, li, lj, grid, value, sampling, support, posi, posj, convFunc); } inline void grd3d(Int* ni, Int* nj, Int *nk, Int* li, Int* lj, Int* lk, Double* grid, const Double* value, Int* sampling, Int* support, Double* posi, Double* posj, Double* posk, Double* convFunc) { dgrd3d(ni, nj, nk, li, lj, lk, grid, value, sampling, support, posi, posj, posk, convFunc); } inline void dgrd1d(Int* ni, Int* li, const Double* grid, Double* value, Int* sampling, Int* support, Double* posi, Double* convFunc) { ddgrd1d(ni, li, grid, value, sampling, support, posi, convFunc); } inline void dgrd2d(Int* ni, Int* nj, Int* li, Int* lj, const Double* grid, Double* value, Int* sampling, Int* support, Double* posi, Double* posj, Double* convFunc) { ddgrd2d(ni, nj, li, lj, grid, value, sampling, support, posi, posj, convFunc); } inline void dgrd3d(Int* ni, Int* nj, Int *nk, Int* li, Int* lj, Int* lk, const Double* grid, Double* value, Int* sampling, Int* support, Double* posi, Double* posj, Double* posk, Double* convFunc) { ddgrd3d(ni, nj, nk, li, lj, lk, grid, value, sampling, support, posi, posj, posk, convFunc); } // Complex versions inline void grd1d(Int* ni, Int* li, Complex* grid, const Complex* value, Int* sampling, Int* support, Double* posi, Double* convFunc) { cgrd1d(ni, li, grid, value, sampling, support, posi, convFunc); } inline void grd2d(Int* ni, Int* nj, Int* li, Int* lj, Complex* grid, const Complex* value, Int* sampling, Int* support, Double* posi, Double* posj, Double* convFunc) { cgrd2d(ni, nj, li, lj, grid, value, sampling, support, posi, posj, convFunc); } inline void grd3d(Int* ni, Int* nj, Int *nk, Int* li, Int* lj, Int* lk, Complex* grid, const Complex* value, Int* sampling, Int* support, Double* posi, Double* posj, Double* posk, Double* convFunc) { cgrd3d(ni, nj, nk, li, lj, lk, grid, value, sampling, support, posi, posj, posk, convFunc); } inline void dgrd1d(Int* ni, Int* li, const Complex* grid, Complex* value, Int* sampling, Int* support, Double* posi, Double* convFunc) { cdgrd1d(ni, li, grid, value, sampling, support, posi, convFunc); } inline void dgrd2d(Int* ni, Int* nj, Int* li, Int* lj, const Complex* grid, Complex* value, Int* sampling, Int* support, Double* posi, Double* posj, Double* convFunc) { cdgrd2d(ni, nj, li, lj, grid, value, sampling, support, posi, posj, convFunc); } inline void dgrd3d(Int* ni, Int* nj, Int *nk, Int* li, Int* lj, Int* lk, const Complex* grid, Complex* value, Int* sampling, Int* support, Double* posi, Double* posj, Double* posk, Double* convFunc) { cdgrd3d(ni, nj, nk, li, lj, lk, grid, value, sampling, support, posi, posj, posk, convFunc); } // Float versions inline void grd1d(Int* ni, Int* li, Float* grid, const Float* value, Int* sampling, Int* support, Double* posi, Double* convFunc) { fgrd1d(ni, li, grid, value, sampling, support, posi, convFunc); } inline void grd2d(Int* ni, Int* nj, Int* li, Int* lj, Float* grid, const Float* value, Int* sampling, Int* support, Double* posi, Double* posj, Double* convFunc) { fgrd2d(ni, nj, li, lj, grid, value, sampling, support, posi, posj, convFunc); } inline void grd3d(Int* ni, Int* nj, Int *nk, Int* li, Int* lj, Int* lk, Float* grid, const Float* value, Int* sampling, Int* support, Double* posi, Double* posj, Double* posk, Double* convFunc) { fgrd3d(ni, nj, nk, li, lj, lk, grid, value, sampling, support, posi, posj, posk, convFunc); } inline void dgrd1d(Int* ni, Int* li, const Float* grid, Float* value, Int* sampling, Int* support, Double* posi, Double* convFunc) { fdgrd1d(ni, li, grid, value, sampling, support, posi, convFunc); } inline void dgrd2d(Int* ni, Int* nj, Int* li, Int* lj, const Float* grid, Float* value, Int* sampling, Int* support, Double* posi, Double* posj, Double* convFunc) { fdgrd2d(ni, nj, li, lj, grid, value, sampling, support, posi, posj, convFunc); } inline void dgrd3d(Int* ni, Int* nj, Int *nk, Int* li, Int* lj, Int* lk, const Float* grid, Float* value, Int* sampling, Int* support, Double* posi, Double* posj, Double* posk, Double* convFunc) { fdgrd3d(ni, nj, nk, li, lj, lk, grid, value, sampling, support, posi, posj, posk, convFunc); } template ConvolveGridder::ConvolveGridder(const IPosition& shape, const Vector& scale, const Vector& offset, const String& convType) : Gridder(shape, scale, offset) { setConvolutionFunction(convType); fillCorrectionVectors(); loc.resize(ndim); loc=0; supportVec.resize(ndim); supportVec=support; } template Bool ConvolveGridder::grid(Array &gridded, const Vector& p, const Range& value) { loc=this->location(loc,p); loc-=offsetVec; if(onGrid(loc,supportVec)) { Bool del; posVec=this->position(posVec, p); const IPosition& fs = gridded.shape(); std::vector s(fs.begin(), fs.end()); switch(loc.nelements()) { case 1: grd1d(&s[0], &loc(0), gridded.getStorage(del), &value, &support, &sampling, &posVec(0), convFunc.getStorage(del)); break; case 2: grd2d(&s[0], &s[1], &loc(0), &loc(1), gridded.getStorage(del), &value, &support, &sampling, &posVec(0), &posVec(1), convFunc.getStorage(del)); break; case 3: grd3d(&s[0], &s[1], &s[2], &loc(0), &loc(1), &loc(2), gridded.getStorage(del), &value, &support, &sampling, &posVec(0), &posVec(1), &posVec(2), convFunc.getStorage(del)); break; default: return False; break; } return True; } else { cout<<"Off grid"< Bool ConvolveGridder::degrid(const Array &gridded, const Vector& p, Range& value) { loc=this->location(loc,p); if(onGrid(loc,supportVec)) { Bool del; posVec=this->position(posVec, p); const IPosition& fs = gridded.shape(); std::vector s(fs.begin(), fs.end()); switch(loc.nelements()) { case 1: dgrd1d(&s[0], &loc(0), gridded.getStorage(del), &value, &support, &sampling, &posVec(0), convFunc.getStorage(del)); break; case 2: dgrd2d(&s[0], &s[1], &loc(0), &loc(1), gridded.getStorage(del), &value, &support, &sampling, &posVec(0), &posVec(1), convFunc.getStorage(del)); break; case 3: dgrd3d(&s[0], &s[1], &s[2], &loc(0), &loc(1), &loc(2), gridded.getStorage(del), &value, &support, &sampling, &posVec(0), &posVec(1), &posVec(2), convFunc.getStorage(del)); break; default: return False; break; } return True; } else { return False; } } template Range ConvolveGridder::correctionFactor1D(Int loc, Int len) { Int offset=loc-len/2; if(cType=="BOX") { if(offset!=0.0) { Double arg=C::pi*Double(offset)/Double(len); return sin(arg)/arg; } else { return 1.0; } } else { Double nu=abs(Double(offset)/Double(len/2)); Double val; grdsf(&nu, &val); return val; } return 1.0; } template void ConvolveGridder::setConvolutionFunction(const String& type) { cType=type; if(type=="BOX") { support=0; sampling=100; convFunc.resize(sampling*(support+1)); convFunc=0.0; for (Int i=0;i Vector& ConvolveGridder::cFunction() { return convFunc; } template Vector& ConvolveGridder::cSupport() { return supportVec; } template Int& ConvolveGridder::cSampling() { return sampling; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/Convolver.h000066400000000000000000000317041476623553700213460ustar00rootroot00000000000000//# Convolver.h: this defines Convolver a class for doing convolution //# Copyright (C) 1996,1999,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_CONVOLVER_H #define SCIMATH_CONVOLVER_H #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Forward Declarations template class Convolver; // Typedefs typedef Convolver FloatConvolver; typedef Convolver DoubleConvolver; // // A class for doing multi-dimensional convolution // // // // // //
      • The mathematical concept of convolution // // // // The convolver class performs convolution! // // // // This class will perform linear or circular convolution on arrays. // // The dimension of the convolution done is determined by the dimension of // the point spread function (psf), so for example, if the psf is a Vector, // one dimensional convolution will be done. The dimension of the model // that is to be convolved must be at least the same as the point // spread function, but it can be larger. If it is then the convolution will // be repeated for each row or plane of the model. // // This class strips all degenerate axes when determining the dimensionality // of the psf or model. So a psf with shapes of [1,1,16] or [16,1,1] is // treated the same as a Vector of length 16, and will result in one // dimensional convolutions along the first non-degenerate axis of the // supplied model. // // Repeated convolution can only be done along the fastest moving axes of // the supplied image. For example, if a one dimensional psf is used (so // that one dimensional convolution is being done), and a cube of data is // supplied then the convolution will be repeated for each row in the // cube. It is not currently possible to have this class do repeated one // dimensional convolution along all the columns or along the z // axis. To do this you need to use an iterator external to the class to // successively feed in the appropriate slices of your Array. // The difference between linear and circular convolution can best be // explained with a one dimensional example. // Suppose the psf and data to be convolved are: // // psf = [0 .5 1 .1]; data = [1 0 0 0 0 0] // // then their linear and circular convolutions are: // // circular convolution = [1 .1 0 0 0 .5] // linear convolution = [1 .1 0 0 0 0] (fullSize == False) // linear convolution = [0 .5 1 .1 0 0 0 0 0] (fullSize == True) // // The circular convolution "wraps around" whereas the linear one does not. // Usage of the fullSize option is explained below. As can be seen from the // above example this class does not normalise the convolved result by any // factor that depends on the psf, so if the "beam area" is not unity the // flux scales will vary. // The "centre" of the convolution is at the point (NX/2, NY/2) (assuming a // 2 dimensional psf) where the first point in the psf is at (0,0) and the // last is at (NX-1, NY-1). This means that a psf that is all zero except // for 1 at the "centre" pixel will when convolved with any model leave the // model unchanged. // The convolution is done in the Fourier domain and the transform of the // psf (the transfer function) is cached by this class. If the cached // transfer function is the wrong size for a given model it will be // automatically be recomputed to the right size (this will involve two // FFT's) // Each convolution requires two Fourier transforms which dominate the // computational load. Hence the computational expense is // n Log(n) for 1 dimensional and // n^2 Log(n) for 2 dimensional convolutions. // The size of the convolved result is always the same as the input model // unless linear convolution is done with the fullSize option set to True. // In this case the result will be larger than the model and include the // full linear convolution (resultSize = psfSize+modelSize-1), rather than // the central portion. // If the convolver is constructed with an expected model size (as in the // example below) then the cached transfer function will be computed to a // size appropriate for linear convolution of models of that size. If no // model size is given then the cached transfer function will be computed // with a size appropriate for circular convolution. These guidelines also // apply when using the setPsf functions. // // If you are intending to do 'fullsize' linear convolutions // you should also set the fullsize option to True as the cached transfer // function is a different size for fullsize linear convolutions. // // For linear convolution the psf can be larger, the same size or smaller // than the model but for circular convolution the psf must be smaller or the // same size. // The size of the cached transfer function (and also the length of the // FFT's calculated) depends on the sizes of the psf and the model, as well // as whether you are doing linear or circular convolution and the fullSize // option. It is always advantageous to use the smallest possible psf // (ie. do not pad the psf prior to supplying it to this class). Be aware // that using odd length images will lead to this class doing odd length // FFT's, which are less computationally effecient (particularly is the // length of the transform is a prime number) in general than even length // transforms. // There are only two valid template types namely, //
          //
        1. FType=Float or //
        2. FType=Double //
        // and the user may prefer to use the following typedef's: // // FloatConvolver (= Convolver) or // DoubleConvolver (= Convolver) // // rather than explicitly specifying the template arguements. // // The typedefs need to be redeclared when using the gnu compiler making // them essentially useless. // // When this class is constructed you may choose to have the psf // explicitly stored by the class (by setting cachePsf=True). This will // allow speedy access to the psf when using the getPsf function. However // the getPsf function can still be called even if the psf has not been // cached. Then the psf will be computed by FFT'ing the transfer // function, and the psf will also then be cached (unless // cachePsf=Flase). Cacheing the psf is also a good idea if you will be // switching between different sized transfer functions (eg. mixing // linear and circular convolution) as it will save one of the two // FFT's. Note that even though the psf is returned as a const Array, it // is possible to inadvertently modify it using the array copy constructor // as this uses reference symantics. Modifying the psf is NOT // recommended. eg. // // DoubleConvolver conv(); // { // Matrix psf(20,20); // conv.setPsf(psf); // } // Matrix convPsf = conv.getPsf(); // Get the psf used by the convolver // convPsf(0,0) = -100; // And modify it. This modifies // // This internal psf used by the // // convolver also! (unless it is // // not caching the psf) // // //
        // // // Calculate the convolution of two Matrices (psf and model); // // Matrix psf(4,4), model(12,12); // ...put meaningful values into the above two matrices... // FloatConvolver conv(psf, model.shape()); // conv.linearConv(result, model); // result = Convolution(psf, model) // // // // // I needed to do linear convolution to write a clean algorithm. It // blossomed into this class. // // // //
      • AipsError: if psf has more dimensions than the model. // // // //
      • the class should detect if the psf or image is small and do the // convolution directly rather than use the Fourier domain //
      • Add a lattice interface, and more flexible iteration scheme //
      • Allow the psf to be specified with a // Function. // template class Convolver { public: // When using the default constructor the psf MUST be specified using the // setPsf function prior to doing any convolution. // Convolver(){} // // Create the cached Transfer function assuming that circular convolution // will be done // Convolver(const Array& psf, Bool cachePsf=False); // // Create the cached Transfer function assuming that linear convolution // with an array of size imageSize will be done. // Convolver(const Array& psf, const IPosition& imageSize, Bool fullSize=False, Bool cachePsf=False); // // The copy constructor and the assignment operator make copies (and not // references) of all the internal data arrays, as this object could get // really screwed up if the private data was silently messed with. // Convolver(const Convolver& other); Convolver & operator=(const Convolver & other); // // The destructor does nothing! // ~Convolver(); // // Perform linear convolution of the model with the previously // specified psf. Return the answer in result. Set fullSize to True if you // want the full convolution, rather than the central portion (the same // size as the model) returned. // void linearConv(Array& result, const Array& model, Bool fullSize=False); // // Perform circular convolution of the model with the previously // specified psf. Return the answer in result. // void circularConv(Array& result, const Array& model); // // Set the transfer function for future convolutions to psf. // Assume circular convolution will be done // void setPsf(const Array& psf, Bool cachePsf=False); // // Set the transfer function for future convolutions to psf. // Assume linear convolution with a model of size imageSize // void setPsf(const Array& psf, IPosition imageShape, Bool fullSize=False, Bool cachePsf=False); // // Get the psf currently used by this convolver // const Array getPsf(Bool cachePsf=True); // // Set to use convolution with lesser flips // void setFastConvolve(); // private: IPosition thePsfSize; IPosition theFFTSize; Array::ConjugateType> theXfr; Array thePsf; FFTServer::ConjugateType> theFFT; FFTServer::ConjugateType> theIFFT; void makeXfr(const Array& psf, const IPosition& imageSize, Bool linear, Bool fullSize); void makePsf(Array& psf); IPosition defaultShape(const Array& psf); IPosition extractShape(IPosition& psfSize, const IPosition& imageSize); void doConvolution(Array& result, const Array& model, Bool fullSize); void resizeXfr(const IPosition& imageShape, Bool linear, Bool fullSize); //# void padArray(Array& paddedArr, const Array& origArr, //# const IPosition & blc); Bool valid; Bool doFast_p; void validate(); }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Mathematics/Convolver.tcc000066400000000000000000000237221476623553700216710ustar00rootroot00000000000000//# Convolver.cc: this defines Convolver a class for doing convolution //# Copyright (C) 1996,1997,1999,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_CONVOLVER_TCC #define SCIMATH_CONVOLVER_TCC #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template Convolver:: Convolver(const Array& psf, Bool){ // if (cachePsf) thePsf = psf; thePsf = psf; valid = False; doFast_p=False; } template Convolver:: Convolver(const Array& psf, const IPosition&, Bool, Bool){ // if (cachePsf) thePsf = psf; thePsf = psf; valid = False; doFast_p=False; } template Convolver:: Convolver(const Convolver& other){ thePsfSize = other.thePsfSize; theFFTSize = other.theFFTSize; theXfr = other.theXfr; thePsf = other.thePsf; theFFT = other.theFFT; theIFFT = other.theIFFT; valid = other.valid; doFast_p=False; } template Convolver & Convolver::operator=(const Convolver & other){ if (this != &other) { thePsfSize.resize(other.thePsfSize.nelements(), False); thePsfSize = other.thePsfSize; theFFTSize.resize(other.theFFTSize.nelements(), False); theFFTSize = other.theFFTSize; theXfr.resize(other.theXfr.shape()); theXfr = other.theXfr; thePsf.resize(other.thePsf.shape()); thePsf = other.thePsf; theFFT = other.theFFT; theIFFT = other.theIFFT; valid = other.valid; doFast_p=False; } return *this; } template Convolver:: ~Convolver(){} template void Convolver::validate() { if(!valid) { valid = True; makeXfr(thePsf, defaultShape(thePsf), False, False); } } template IPosition Convolver:: defaultShape(const Array& psf){ // If the user has not specified an image size assume that it is // the same size as the psf. return psf.shape().nonDegenerate(); } template IPosition Convolver:: extractShape(IPosition& psfSize, const IPosition& imageSize){ // return an IPosition that has the same number of dimensions as the psf // but with the lengths of the image return imageSize.getFirst(psfSize.nonDegenerate().nelements()); } template void Convolver:: makeXfr(const Array& psf, const IPosition& imageSize, Bool linear, Bool fullSize){ const Array psfND1 = psf.nonDegenerate(); Array psfND= psfND1.copy(); thePsfSize = psfND.shape(); IPosition imageNDSize = imageSize.nonDegenerate(); uInt psfDim = thePsfSize.nelements(); IPosition convImageSize = extractShape(thePsfSize, imageNDSize); theFFTSize.resize(psfDim); if (linear) if (fullSize) theFFTSize = thePsfSize+extractShape(thePsfSize, imageNDSize); else for (uInt i = 0; i < psfDim; i++) theFFTSize(i) = std::max(thePsfSize(i), convImageSize(i)+2*Int((thePsfSize(i)+3)/4)); else for (uInt i = 0; i < psfDim; i++) theFFTSize(i) = std::max(thePsfSize(i), convImageSize(i)); { IPosition tmp = theXfr.shape(); tmp = 0; theXfr.resize(tmp); // I am to lazy to work out the correct size } // Pad the psf (if necessary) if (theFFTSize != thePsfSize){ Array paddedPsf(theFFTSize); IPosition blc = theFFTSize/2-thePsfSize/2; IPosition trc = blc + thePsfSize - 1; paddedPsf = 0.; paddedPsf(blc, trc) = psfND; // And do the fft if(doFast_p){ //theFFT.flip(paddedPsf, True, False); theFFT.fft0(theXfr, paddedPsf, False); } else{ theFFT.fft(theXfr, paddedPsf, False); } } else{ if(doFast_p){ //theFFT.flip(psfND, True, False); theFFT.fft0(theXfr, psfND); } else{ theFFT.fft(theXfr, psfND); } } } template void Convolver:: makePsf(Array& psf){ validate(); if (thePsf.nelements() == 0) { Array paddedPsf(theFFTSize); // theIFFT.flip(paddedPsf, True, False); if(doFast_p){ theIFFT.fft0(paddedPsf, theXfr, True); theIFFT.flip(paddedPsf, False, False); } else{ theIFFT.fft(paddedPsf, theXfr, True); } IPosition trc, blc; blc = (theFFTSize-thePsfSize)/2; trc = blc + thePsfSize - 1; psf = paddedPsf(blc, trc); } else psf.reference(thePsf); } template void Convolver:: linearConv(Array& result, const Array& model, Bool fullSize) { validate(); // Check the dimensions of the model are compatible with the current psf IPosition imageSize = extractShape(thePsfSize, model.shape()); if (fullSize){ if (imageSize+thePsfSize > theFFTSize){ resizeXfr(imageSize, True, True); } } else { Bool doResize = False; for (uInt i = 0; i < thePsfSize.nelements(); i++) { if (theFFTSize < std::max(thePsfSize(i), imageSize(i)+2*Int((thePsfSize(i)+3)/4))) doResize=True; } if (doResize) resizeXfr(imageSize, True, False); } // Calculate to output array size IPosition resultSize = model.shape(); if (fullSize) resultSize.setFirst(imageSize+thePsfSize-1); // create space in the output array to hold the data result.resize(resultSize); ReadOnlyArrayIterator from(model, thePsfSize.nelements()); ArrayIterator to(result, thePsfSize.nelements()); for (from.origin(), to.origin(); (from.pastEnd() || to.pastEnd()) == False; from.next(), to.next()) { doConvolution(to.array(), from.array(), fullSize); } } template void Convolver:: doConvolution(Array& result, const Array& model, Bool fullSize) { validate(); IPosition modelSize = model.shape(); Array::ConjugateType> fftModel; if (theFFTSize != modelSize){ // Pad the model Array paddedModel(theFFTSize); IPosition blc = (theFFTSize-modelSize)/2; IPosition trc = blc + modelSize - 1; paddedModel = 0.; paddedModel(blc, trc) = model; // And calculate its transform // theFFT.flip(paddedModel, True, False); if(doFast_p){ theFFT.fft0(fftModel, paddedModel); } else{ theFFT.fft(fftModel, paddedModel); } } else{ Array paddedModel=model; if(doFast_p){ Array paddedModel=model; // theFFT.flip(paddedModel, True, False); theFFT.fft0(fftModel, paddedModel); } else{ theFFT.fft(fftModel, model); } } // Multiply by the transfer function fftModel *= theXfr; // Do the inverse transform Array convolvedData(theFFTSize); if(doFast_p){ theIFFT.fft0(convolvedData, fftModel); theIFFT.flip(convolvedData, False, False); } else{ theIFFT.fft(convolvedData, fftModel); } // Extract the required part of the convolved data IPosition trc, blc; if (fullSize) { blc = IPosition(thePsfSize.nelements(), 0); trc = thePsfSize + modelSize - 2; } else { blc = (theFFTSize-modelSize)/2; trc = blc + modelSize - 1; } result = convolvedData(blc, trc); } template void Convolver:: setPsf(const Array& psf, Bool){ thePsf.resize(psf.shape()); thePsf = psf; valid=False; doFast_p=False; } template void Convolver:: setPsf(const Array& psf, IPosition, Bool, Bool){ thePsf.resize(psf.shape()); thePsf = psf; valid=False; doFast_p=False; } template void Convolver:: resizeXfr(const IPosition& imageSize, Bool linear, Bool fullSize){ Array psf; makePsf(psf); makeXfr(psf, imageSize, linear, fullSize); } template void Convolver:: circularConv(Array& result, const Array& model){ // Check the dimensions of the model are compatible with the current psf validate(); IPosition imageSize = extractShape(thePsfSize, model.shape()); if (casacore::max(imageSize.asVector(), thePsfSize.asVector()) != theFFTSize){ resizeXfr(model.shape(), False, False); } // create space in the output array to hold the data result.resize(model.shape()); ReadOnlyArrayIterator from(model, thePsfSize.nelements()); ArrayIterator to(result, thePsfSize.nelements()); for (from.origin(), to.origin(); (from.pastEnd() || to.pastEnd()) == False; from.next(), to.next()) { doConvolution(to.array(), from.array(), False); } } template const Array Convolver:: getPsf(Bool cachePsf){ validate(); Array psf; makePsf(psf); if ((cachePsf == True) && (thePsf.nelements() == 0)) thePsf.reference(psf); return psf; } template void Convolver:: setFastConvolve(){ doFast_p=True; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/DFTServer.h000066400000000000000000000074351476623553700212010ustar00rootroot00000000000000//# DFTServer.h: This class contains methods for doing n-D slow Fourier transforms //# Copyright (C) 1994,1995,1996,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_DFTSERVER_H #define SCIMATH_DFTSERVER_H #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template class Matrix; // // Error class for DFTServer class // // // Error class for DFTServer class. // class DFTError: public AipsError { public: DFTError(): AipsError("DFTError") {} DFTError(const Char *m) : AipsError(m) {} DFTError(const String &m) : AipsError(m) {} virtual ~DFTError() noexcept {} }; // // Class containing methods for doing n-D slow Fourier transforms // // // The DFTServer class contains methods for doing n-dimensional // Slow Fourier Transforms. (In practice, the maximum dimension is 3). // // template class DFTServer { public: // default constructor DFTServer(); // copy constructor DFTServer(const DFTServer &); // Other constructors // DFTServer(Array &, Array &); DFTServer(int, int, int); DFTServer(IPosition &, IPosition &); // // destructor ~DFTServer(); // assignment DFTServer &operator=(const DFTServer &); // n-d real <-> complex dft void rcdft(Array &, Array &); // n-d complex <-> real dft void crdft(Array &, Array &); // n-d complex <-> complex dft void cxdft(Array &, Array &, int); // display only the real component of the data void showReal(Array &); // display both the real and the imaginary components of the data void showComplex(Array &); private: // dimension of the both input and output data int dimension; // number of time data points int numTime; // number of frequency data points int numFreq; // set to 1 (true) if a crfft is done int crFlag; // does a complex to complex DFT void c2c(Matrix &, Matrix &, int); // turn a general array into a matrix Matrix getMatrix(Array &); }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif //DFT_SERVER casacore-3.7.1/scimath/Mathematics/DFTServer.tcc000066400000000000000000000232271476623553700215200ustar00rootroot00000000000000//# DFTServer.cc: This class contains methods for doing n-D slow Fourier transforms //# Copyright (C) 1994,1995,1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_DFTSERVER_TCC #define SCIMATH_DFTSERVER_TCC #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template DFTServer& DFTServer::operator=(const DFTServer &other) { if (this == &other) return *this; dimension = other.dimension; numTime = other.numTime; numFreq = other.numFreq; crFlag = other.crFlag; return *this; } template DFTServer::DFTServer() // // default constructor. Throw an exception since we require parameters. // { throw(DFTError("DFTServer constructor: Error--required parameter missing")); } template DFTServer::DFTServer(const DFTServer &other) // //copy constructor // { dimension=other.dimension; numTime=other.numTime; numFreq=other.numFreq; crFlag = other.crFlag; } template DFTServer::DFTServer(Array &Time, Array &Freq) // // DFTServer can fourier transform a point into a vector, a vector // into a point, or a vector into a vector // { crFlag = 0; IPosition shapeTime(Time.ndim()); IPosition shapeFreq(Freq.ndim()); shapeTime = Time.shape(); shapeFreq = Freq.shape(); int dimTime, dimFreq; switch (Time.ndim()) { case 1: numTime = 1; dimTime = shapeTime(0) - 2; break; case 2: numTime = shapeTime(0); dimTime = shapeTime(1) - 2; break; default: throw(DFTError("DFTServer constructor -- Error: time array should" " be a vector or a matrix") ); break; } switch (Freq.ndim()) { case 1: numFreq = 1; dimFreq = shapeFreq(0) - 2; break; case 2: numFreq = shapeFreq(0); dimFreq = shapeFreq(1) - 2; break; default: throw(DFTError("DFTServer constructor -- Error: Frequency array " "should be a vector or a matrix") ); break; } if (dimTime != dimFreq) throw(DFTError("DFTServer::DFTServer - Error: Time and Freq should" " have same dimensions")); dimension = dimTime; if (dimension > 3) throw(DFTError("DFTServer::dft: Error--cannot handle data points which " " has more than 3 dimensions" )); } template DFTServer::DFTServer(int dim, int numTimepts, int numFreqpts) { crFlag = 0; dimension = dim; if (dimension > 3) throw(DFTError("DFTServer::dft: Error--cannot handle data points which " " has more than 3 dimensions" )); numTime = numTimepts; numFreq = numFreqpts; } template DFTServer::DFTServer(IPosition &shapeTime, IPosition &shapeFreq) { crFlag = 0; int dimTime, dimFreq; switch (shapeTime.nelements()) { case 1: dimTime = shapeTime(0)-2; numTime=1; break; case 2: dimTime = shapeTime(1)-2; numTime = shapeTime(0); break; default: throw(DFTError("DFTServer constructor -- Error: time array should" " be a vector or a matrix") ); break; } switch (shapeFreq.nelements()) { case 1: dimFreq = shapeFreq(0)-2; numFreq=1; break; case 2: dimFreq = shapeFreq(1)-2; numFreq = shapeFreq(0); break; default: throw(DFTError("DFTServer constructor -- Error: frequency array should" " be a vector or a matrix") ); break; } if (dimTime != dimFreq) throw(DFTError("DFTServer constructor: Error--time and UV data must" "have same dimension" )); dimension = dimTime; if (dimension > 2) throw(DFTError("DFTServer::dft: Error--cannot handle data points which " " have more than 3 dimensions" )); } template DFTServer::~DFTServer() // // destructor // { } template Matrix DFTServer::getMatrix(Array &data) { IPosition Shape(data.ndim()); Shape = data.shape(); if (data.ndim() == 1) { if ( (Shape(0) - 2) != dimension) throw(DFTError("DFTServer::getMatrix -- Error: data has " " incorrect dimension")); Vector vec; vec.reference(data); int cols = dimension + 2; Matrix mat(1,cols); mat.row(0) = vec; return mat; } else if (data.ndim() == 2) { if ( (Shape(1) - 2) != dimension) throw(DFTError("DFTServer::getMatrix -- Error: data has " " incorrect dimension")); Matrix mat; mat.reference(data); return mat; } else { throw(DFTError("DFTServer::dft: Error--cannot handle time data " " which has more than 3 dimensions" )); } } template void DFTServer::rcdft(Array &Time, Array &Freq) // // Time is data from time domain // Freq is data from frequency domain // { Matrix matIn = getMatrix(Time); Matrix matOut = getMatrix(Freq); c2c(matIn, matOut, 1); if (Time.ndim() == 1) Time = matIn.row(0); if (Freq.ndim() == 1) Freq = matOut.row(0); } template void DFTServer::crdft(Array &Time, Array &Freq) // // Time is data from time domain // Freq is data from frequency domain // { crFlag = 1; Matrix matIn = getMatrix(Freq); Matrix matOut = getMatrix(Time); c2c(matOut, matIn, 0); if (Time.ndim() == 1) Time = matIn.row(0); if (Freq.ndim() == 1) Freq = matOut.row(0); crFlag = 0; } template void DFTServer::cxdft(Array &Time, Array &Freq, int dir) // // Time is data from time domain // Freq is data from frequency domain // dir > 0 forward dft ( from time to frequency) // dir <= 0 backward dft ( from frequency to time) // { Matrix matIn = getMatrix(Time); Matrix matOut = getMatrix(Freq); c2c(matIn, matOut, dir); if (Time.ndim() == 1) Time = matIn.row(0); if (Freq.ndim() == 1) Freq = matOut.row(0); } template void DFTServer::c2c(Matrix &Time, Matrix &Freq, int dir) // dir > 0 forward dft ( from time to frequency) // dir <= 0 backward dft ( from frequency to time) // { T sum_real, sum_imag; if (dir>0) { // DFT from time to frequency domain for( int j=0; j<=numFreq-1; j++) { sum_real=T(0.0); sum_imag=T(0.0); for( int k=0; k<=numTime-1; k++) { T phase = T(0.0); for( int i=2; i<=(dimension+2)-1; i++) { phase += (Time(k,i) * Freq(j,i)); } phase=-(C::_2pi)*phase; sum_real += (Time(k,0) * cos(phase) + Time(k,1) * cos(phase + (C::pi_2)) ); sum_imag += (Time(k,0) * sin(phase) + Time(k,1) * sin(phase + (C::pi_2)) ); } Freq(j,0) = sum_real; Freq(j,1) = sum_imag; } } else { // DFT from frequency domain to time domain for( int j=0; j<=numTime-1; j++) { sum_real=T(0.0); sum_imag=T(0.0); for( int k=0; k<=numFreq-1; k++) { T phase = T(0.0); for( int i=2; i<=(dimension+2)-1; i++) { phase += (Time(j,i) * Freq(k,i)); } phase=(C::_2pi)*phase; sum_real+= (Freq(k,0) * cos(phase) + Freq(k,1) * cos(phase+ (C::pi_2))); if (crFlag == 0) sum_imag+= (Freq(k,0) * sin(phase) + Freq(k,1) * sin(phase + (C::pi_2))); } Time(j,0) = sum_real/numFreq; Time(j,1) = sum_imag/numFreq; } } } template void DFTServer::showComplex(Array &data) { Vector vec; Matrix mat; switch (data.ndim()) { case 1: vec.reference(data); cout << "( " << vec(0)<<"," << vec(1) << endl; break; case 2: mat.reference(data); cout << "real components" << endl; cout << mat.column(0); cout << endl; cout << "Imaginary components" << endl; cout << mat.column(1); cout << endl; break; default: cerr << "in showComplex, Eorr: cannot handle data which has" << " dimension greater than 2"; exit(0); } } template void DFTServer::showReal(Array &data) { Vector vec; Matrix mat; switch (data.ndim()) { case 1: vec.reference(data); cout << vec(0); cout << endl; break; case 2: mat.reference(data); cout << mat.column(0); cout << endl; break; default: cerr << "in showReal, Eorr: cannot handle data which has" << " dimension greater than 2"; exit(0); } } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/FFTPack.cc000066400000000000000000000172571476623553700207540ustar00rootroot00000000000000//# FFTPack.cc: C++ wrapper functions for Fortran FFTPACK code //# Copyright (C) 1993,1994,1995,1997,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN extern "C" { void cffti_(int*, float*); void cfft2i_(const Int*, const Int*, Float*, const Int*, Int*); void dcffti_(int*, double*); void cfftf_(int*, float*, float*); void dcfftf_(int*, double*, double*); void cfftb_(int*, float*, float*); void dcfftb_(int*, double*, double*); void cfft2f_(const Int*, const Int*, const Int*, Complex*, const Float*, const Int*, Float* , const Int*, Int*); void cfft2b_(const Int*, const Int*, const Int*, Complex*, const Float*, const Int*, Float* , const Int*, Int*); } extern "C" { void rfftf_(int*, float*, float*); void rfftb_(int*, float*, float*); void rffti_(int*, float*); void drfftf_(int*, double*, double*); void drfftb_(int*, double*, double*); void drffti_(int*, double*); } extern "C" { void ezffti_(int*, float*); void ezfftf_(int*, float*, float*, float*, float*, float*); void ezfftb_(int* n, float*, float*, float*, float*, float*); } extern "C" { void sinti_(int*, float*); void dsinti_(int*, double*); void sint_(int*, float*, float*); void dsint_(int*, double*, double*); } extern "C" { void costi_(int*, float*); void dcosti_(int*, double*); void cost_(int*, float*, float*); void dcost_(int*, double*, double*); } extern "C" { void sinqi_(int*, float*); void dsinqi_(int*, double*); void sinqf_(int*, float*, float*); void dsinqf_(int*, double*, double*); void sinqb_(int*, float*, float*); void dsinqb_(int*, double*, double*); } extern "C" { void cosqi_(int*, float*); void dcosqi_(int*, double*); void cosqf_(int*, float*, float*); void dcosqf_(int*, double*, double*); void cosqb_(int*, float*, float*); void dcosqb_(int*, double*, double*); } void FFTPack::cffti(Int n, Float* work) { cffti_((int*) &n, (float*) work); } void FFTPack::cffti(Int n, Double* work) { dcffti_((int*) &n, (double*) work); } void FFTPack::cfftf(Int n, Complex* rdata, Float* work) { DebugAssert(sizeof(Complex) == 2*sizeof(float), AipsError); cfftf_((int*) &n, (float*) rdata, (float*) work); } void FFTPack::cfftf(Int n, DComplex* rdata, Double* work) { DebugAssert(sizeof(DComplex) == 2*sizeof(double), AipsError); dcfftf_((int*) &n, (double*) rdata, (double*) work); } void FFTPack::cfftb(Int n, Complex* rdata, Float* work) { DebugAssert(sizeof(Complex) == 2*sizeof(float), AipsError); cfftb_((int*) &n, (float*) rdata, (float*) work); } void FFTPack::cfftb(Int n, DComplex* rdata, Double* work) { DebugAssert(sizeof(DComplex) == 2*sizeof(double), AipsError); dcfftb_((int*) &n, (double*) rdata, (double*) work); } void FFTPack::cfft2i(const Int& n, const Int& m, Float *& wsave, const Int& lensav, Int& ier){ cfft2i_(&n, &m, wsave, &lensav, &ier); } void FFTPack::cfft2f (const Int& ldim, const Int& l, const Int& m, Complex*& c, Float*& wsave, const Int& lensav, Float *& work, const Int& lenwrk, Int& ier){ cfft2f_(&ldim, &l, &m, c, wsave, &lensav, work, &lenwrk, &ier); } void FFTPack::cfft2b (const Int& ldim, const Int& l, const Int& m, Complex* & c, Float *& wsave, const Int& lensav, Float*& work, const Int& lenwrk, Int& ier){ cfft2b_(&ldim, &l, &m, c, wsave, &lensav, work, &lenwrk, &ier); } void FFTPack::rffti(Int n, Float* work) { rffti_((int*) &n, (float*) work); } void FFTPack::rffti(Int n, Double* work) { drffti_((int*) &n, (double*) work); } void FFTPack::rfftf(Int n, Float* rdata, Float* work) { rfftf_((int*) &n, (float*) rdata, (float*) work); } void FFTPack::rfftf(Int n, Double* rdata, Double* work) { drfftf_((int*) &n, (double*) rdata, (double*) work); } void FFTPack::rfftb(Int n, Float* rdata, Float* work) { rfftb_((int*) &n, (float*) rdata, (float*) work); } void FFTPack::rfftb(Int n, Double* rdata, Double* work) { drfftb_((int*) &n, (double*) rdata, (double*) work); } void FFTPack::ezffti(Int n, Float* wsave) { ezffti_((int*) &n, (float*) wsave); } void FFTPack::ezfftf(Int n, Float* r, Float* azero, Float* a, Float* b, Float* wsave) { ezfftf_((int*) &n, (float*) r, (float*) azero, (float*) a, (float*) b, (float*) wsave); } void FFTPack::ezfftb(Int n, Float* r, Float* azero, Float* a, Float* b, Float* wsave) { ezfftb_((int*) &n, (float*) r, (float*) azero, (float*) a, (float*) b, (float*) wsave); } void FFTPack::sinti(Int n, Float* wsave) { sinti_((int*) &n, (float*) wsave); } void FFTPack::sinti(Int n, Double* wsave) { dsinti_((int*) &n, (double*) wsave); } void FFTPack::sint(Int n, Float* x, Float* wsave) { sint_((int*) &n, (float*) x, (float*) wsave); } void FFTPack::sint(Int n, Double* x, Double* wsave) { dsint_((int*) &n, (double*) x, (double*) wsave); } void FFTPack::costi(Int n, Float* wsave) { costi_((int*) &n, (float*) wsave); } void FFTPack::costi(Int n, Double* wsave) { dcosti_((int*) &n, (double*) wsave); } void FFTPack::cost(Int n, Float* x, Float* wsave) { cost_((int*) &n, (float*) x, (float*) wsave); } void FFTPack::cost(Int n, Double* x, Double* wsave) { dcost_((int*) &n, (double*) x, (double*) wsave); } void FFTPack::sinqi(Int n, Float* wsave) { sinqi_((int*) &n, (float*) wsave); } void FFTPack::sinqi(Int n, Double* wsave) { dsinqi_((int*) &n, (double*) wsave); } void FFTPack::sinqf(Int n, Float* x, Float* wsave) { sinqf_((int*) &n, (float*) x, (float*) wsave); } void FFTPack::sinqf(Int n, Double* x, Double* wsave) { dsinqf_((int*) &n, (double*) x, (double*) wsave); } void FFTPack::sinqb(Int n, Float* x, Float* wsave) { sinqb_((int*) &n, (float*) x, (float*) wsave); } void FFTPack::sinqb(Int n, Double* x, Double* wsave) { dsinqb_((int*) &n, (double*) x, (double*) wsave); } void FFTPack::cosqi(Int n, Float* wsave) { cosqi_((int*) &n, (float*) wsave); } void FFTPack::cosqi(Int n, Double* wsave) { dcosqi_((int*) &n, (double*) wsave); } void FFTPack::cosqf(Int n, Float* x, Float* wsave) { cosqf_((int*) &n, (float*) x, (float*) wsave); } void FFTPack::cosqf(Int n, Double* x, Double* wsave) { dcosqf_((int*) &n, (double*) x, (double*) wsave); } void FFTPack::cosqb(Int n, Float* x, Float* wsave) { cosqb_((int*) &n, (float*) x, (float*) wsave); } void FFTPack::cosqb(Int n, Double* x, Double* wsave) { dcosqb_((int*) &n, (double*) x, (double*) wsave); } // Local Variables: // compile-command: "gmake OPTLIB=1 FFTPack" // End: } //# NAMESPACE CASACORE - END casacore-3.7.1/scimath/Mathematics/FFTPack.h000066400000000000000000001065641476623553700206160ustar00rootroot00000000000000//# FFTPack.h: C++ wrapper functions for Fortran FFTPACK code //# Copyright (C) 1993,1994,1995,1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_FFTPACK_H #define SCIMATH_FFTPACK_H #include //# The SGI compiler with -LANG:std has some trouble including both Complexfwd.h //# and Complex.h so we bypass the problem by include Complex.h only. #if defined(AIPS_USE_NEW_SGI) #include #else #include #endif #warning "FFTPack is deprecated and will be removed in a future version of casacore. Please use FFTW." namespace casacore { //# NAMESPACE CASACORE - BEGIN // C++ interface to the Fortran FFTPACK library // // // // The static functions in this class are C++ wrappers to the Fortran FFTPACK // library. This library contains functions that perform fast Fourier // transforms (FFT's) and related transforms. // An additional purpose of these definitions is to overload the functions so // that C++ users can access the functions in either fftpak (single precision) // or dfftpack (double precision) with identical function names. // These routines only do one-dimensional transforms with the first element of // the array being the "origin" of the transform. The FFTServer class uses some of these functions to // implement multi-dimensional transforms with the origin of the transform // either at the centre or the first element of the Array. // You must initialise the work array wsave before using the forward // transform (function with a suffix of f) or the backward transform (with a // suffix of b). // The transforms done by the functions in this class can be categorised as // follows: //
          //
        • Complex to Complex Transforms
          // Done by the cttfi, cfftf & cfftb functions //
        • Real to Complex Transforms
          // Done by the rffti, rfftf & rfftb functions. A simpler interface is // provided by the ezffti, ezfftf & ezfftb functions. The 'ez' functions // do not destroy the input array and provide the result in a slightly // less packed format. They are available in single precision only and // internally use the rfft functions. //
        • Sine Transforms
          // Done by the sinti & sint functions. As the sine transform is its own // inverse there is no need for any distinction between forward and // backward transforms. //
        • Cosine Transforms
          // Done by the costi & cost functions. As the cosine transform is its own // inverse there is no need for any distinction between forward and // backward transforms. //
        • Sine quarter wave Transforms
          // Done by the sinqi, sinqf & sinqb functions. //
        • Cosine quarter wave Transforms
          // Done by the cosqi, cosqf & cosqb functions. //
        // // These functions assume that it is possible to convert between Casacore numeric // types and those used by Fortran. That it is possible to convert between // Float & float, Double & double and Int & int. // // // These function also assume that a Complex array is stored as pairs of // floating point numbers, with no intervening gaps, and with the real // component first ie., [re0,im0,re1,im1, ...] so that the following // type casts work, // // Complex* complexPtr; // Float* floatPtr = (Float* ) complexPtr; // // and allow a Complex number to be accessed as a pair of real numbers. If this // assumption is bad then float Arrays will have to generated by copying the // complex ones. When compiled in debug mode mode the functions that require // this assumption will throw an exception (AipsError) if this assumption is // bad. Ultimately this assumption about Complex<->Float Array conversion // should be put somewhere central like Array2Math.cc. // //
        class FFTPack { public: // cffti initializes the array wsave which is used in both cfftf and // cfftb. The prime factorization of n together with a tabulation of // the trigonometric functions are computed and stored in wsave. // // Input parameter: //
        //
        n //
        The length of the sequence to be transformed //
        // Output parameter: //
        //
        wsave //
        A work array which must be dimensioned at least 4*n+15 // The same work array can be used for both cfftf and cfftb // as long as n remains unchanged. Different wsave arrays // are required for different values of n. The contents of // wsave must not be changed between calls of cfftf or cfftb. //
        // static void cffti(Int n, Float* wsave); static void cffti(Int n, Double* wsave); //Here is the doc from FFTPack 5.1 //You can convert the linguo from fortran to C/C++ /* Input Arguments L Integer number of elements to be transformed in the first dimension. The transform is most efficient when L is a product of small primes. M Integer number of elements to be transformed in the second dimension. The transform is most efficient when M is a product of small primes. LENSAV Integer dimension of WSAVE array. LENSAV must be at least 2*(L+M) + INT(LOG(REAL(L))/LOG(2.)) + INT(LOG(REAL(M))/LOG(2.)) + 8. Output Arguments WSAVE Real work array with dimension LENSAV, containing the prime factors of L and M, and also containing certain trigonometric values which will be used in routines CFFT2B or CFFT2F. WSAVE Real work array with dimension LENSAV. The WSAVE array must be initialized with a call to subroutine CFFT2I before the first call to CFFT2B or CFFT2F, and thereafter whenever the values of L, M or the contents of array WSAVE change. Using different WSAVE arrays for different transform lengths or types in the same program may reduce computation costs because the array contents can be re-used. IER Integer error return = 0 successful exit = 2 input parameter LENSAV not big enough = 20 input error returned by lower level routine */ static void cfft2i(const Int& n, const Int& m, Float *& wsave, const Int& lensav, Int& ier); // // cfftf computes the forward complex discrete Fourier // transform (the Fourier analysis). Equivalently, cfftf computes // the Fourier coefficients of a complex periodic sequence. // the transform is defined below at output parameter c. // // The transform is not normalized. To obtain a normalized transform // the output must be divided by n. Otherwise a call of cfftf // followed by a call of cfftb will multiply the sequence by n. // // The array wsave which is used by cfftf must be // initialized by calling cffti(n,wsave). // // Input parameters: //
        //
        n //
        The length of the complex sequence c. The method is // more efficient when n is the product of small primes. //
        c //
        A complex array of length n which contains the sequence to be // transformed. //
        wsave //
        A real work array which must be dimensioned at least 4n+15 // by the program that calls cfftf. The wsave array must be // initialized by calling cffti(n,wsave) and a // different wsave array must be used for each different // value of n. This initialization does not have to be // repeated so long as n remains unchanged thus subsequent // transforms can be obtained faster than the first. // The same wsave array can be used by cfftf and cfftb. //
        // Output parameters: //
        //
        c //
        for j=1,...,n
        // c(j)=the sum from k=1,...,n of
        // c(k)*exp(-i*(j-1)*(k-1)*2*pi/n)
        // where i=sqrt(-1)
        //
        wsave //
        Contains initialization calculations which must not be // destroyed between calls of cfftf or cfftb //
        // static void cfftf(Int n, Complex* c, Float* wsave); static void cfftf(Int n, DComplex* c, Double* wsave); //Description from FFTPack 5.1 /* Input Arguments LDIM Integer first dimension of two-dimensional complex array C. L Integer number of elements to be transformed in the first dimension of the two-dimensional complex array C. The value of L must be less than or equal to that of LDIM. The transform is most efficient when L is a product of small primes. M Integer number of elements to be transformed in the second dimension of the two-dimensional complex array C. The transform is most efficient when M is a product of small primes. C Complex array of two dimensions containing the (L,M) subarray to be transformed. C's first dimension is LDIM, its second dimension must be at least M. WSAVE Real work array with dimension LENSAV. WSAVE's contents must be initialized with a call to subroutine CFFT2I before the first call to routine CFFT2F or CFFT2B with transform lengths L and M. WSAVE's contents may be re-used for subsequent calls to CFFT2F and CFFT2B having those same transform lengths. LENSAV Integer dimension of WSAVE array. LENSAV must be at least 2*(L+M) + INT(LOG(REAL(L))/LOG(2.)) + INT(LOG(REAL(M))/LOG(2.)) + 8. WORK Real work array. LENWRK Integer dimension of WORK array. LENWRK must be at least 2*L*M. */ static void cfft2f (const Int& ldim, const Int& L, const Int& M, Complex*& C, Float*& WSAVE, const Int& LENSAV, Float *& WORK, const Int& LENWRK, Int& IER); // // cfftb computes the backward complex discrete Fourier // transform (the Fourier synthesis). Equivalently, cfftb computes // a complex periodic sequence from its Fourier coefficients. // The transform is defined below with output parameter c. // // A call of cfftf followed by a call of cfftb will multiply the // sequence by n. // // The array wsave which is used by cfftb must be // initialized by calling cffti(n,wsave). // // Input parameters: //
        //
        n //
        The length of the complex sequence c. The method is // more efficient when n is the product of small primes. //
        c //
        A complex array of length n which contains the sequence to be // transformed. //
        wsave //
        A real work array which must be dimensioned at least 4n+15 // in the program that calls cfftb. The wsave array must be // initialized by calling cffti(n,wsave) // and a different wsave array must be used for each different // value of n. This initialization does not have to be // repeated so long as n remains unchanged thus subsequent // transforms can be obtained faster than the first. // The same wsave array can be used by cfftf and cfftb. //
        // Output parameters: //
        //
        c //
        for j=1,...,n
        // c(j)=the sum from k=1,...,n of
        // c(k)*exp(i*(j-1)*(k-1)*2*pi/n)
        //
        wsave //
        Contains initialization calculations which must not be // destroyed between calls of cfftf or cfftb //
        // static void cfftb(Int n, Complex* c, Float* wsave); static void cfftb(Int n, DComplex* c, Double* wsave); //Documentation from FFTPack 5.1 /* Input Arguments LDIM Integer first dimension of two-dimensional complex array C. L Integer number of elements to be transformed in the first dimension of the two-dimensional complex array C. The value of L must be less than or equal to that of LDIM. The transform is most efficient when L is a product of small primes. M Integer number of elements to be transformed in the second dimension of the two-dimensional complex array C. The transform is most efficient when M is a product of small primes. C Complex array of two dimensions containing the (L,M) subarray to be transformed. C's first dimension is LDIM, its second dimension must be at least M. WSAVE Real work array with dimension LENSAV. WSAVE's contents must be initialized with a call to subroutine CFFT2I before the first call to routine CFFT2F or CFFT2B with transform lengths L and M. WSAVE's contents may be re-used for subsequent calls to CFFT2F and CFFT2B with the same transform lengths L and M. LENSAV Integer dimension of WSAVE array. LENSAV must be at least 2*(L+M) + INT(LOG(REAL(L))/LOG(2.)) + INT(LOG(REAL(M))/LOG(2.)) + 8. WORK Real work array. LENWRK Integer dimension of WORK array. LENWRK must be at least 2*L*M. Output Arguments C Complex output array. For purposes of exposition, assume the index ranges of array C are defined by C(0:L-1,0:M-1). For I=0,...,L-1 and J=0,...,M-1, the C(I,J)'s are given in the traditional aliased form by L-1 M-1 C(I,J) = SUM SUM C(L1,M1)* L1=0 M1=0 EXP(SQRT(-1)*2*PI*(I*L1/L + J*M1/M)) And in unaliased form, the C(I,J)'s are given by LF MF C(I,J) = SUM SUM C(L1,M1,K1)* L1=LS M1=MS EXP(SQRT(-1)*2*PI*(I*L1/L + J*M1/M)) where LS= -L/2 and LF=L/2-1 if L is even; LS=-(L-1)/2 and LF=(L-1)/2 if L is odd; MS= -M/2 and MF=M/2-1 if M is even; MS=-(M-1)/2 and MF=(M-1)/2 if M is odd; and C(L1,M1) = C(L1+L,M1) if L1 is zero or negative; C(L1,M1) = C(L1,M1+M) if M1 is zero or negative; The two forms give different results when used to interpolate between elements of the sequence. IER Integer error return = 0 successful exit = 2 input parameter LENSAV not big enough = 3 input parameter LENWRK not big enough = 5 input parameter L > LDIM = 20 input error returned by lower level routine */ static void cfft2b (const Int& LDIM, const Int& L, const Int& M, Complex* & C, Float *& WSAVE, const Int& LENSAV, Float*& WORK, const Int& LENWRK, Int& IER); // // rffti initializes the array wsave which is used in both rfftf and // rfftb. The prime factorization of n together with a tabulation of // the trigonometric functions are computed and stored in wsave. // // Input parameter: //
        //
        n //
        The length of the sequence to be transformed. //
        // Output parameter: //
        //
        wsave //
        A work array which must be dimensioned at least 2*n+15. // The same work array can be used for both rfftf and rfftb // as long as n remains unchanged. Different wsave arrays // are required for different values of n. The contents of // wsave must not be changed between calls of rfftf or rfftb. //
        // static void rffti(Int n, Float* wsave); static void rffti(Int n, Double* wsave); // // rfftf computes the Fourier coefficients of a real perodic sequence (Fourier // analysis). The transform is defined below at output parameter r. // // Input parameters: //
        //
        n //
        The length of the array r to be transformed. The method // is most efficient when n is a product of small primes. // n may change so long as different work arrays are provided //
        r //
        A real array of length n which contains the sequence // to be transformed //
        wsave //
        A work array which must be dimensioned at least 2*n+15 // in the program that calls rfftf. The wsave array must be // initialized by calling rffti(n,wsave) and a // different wsave array must be used for each different // value of n. This initialization does not have to be // repeated so long as n remains unchanged thus subsequent // transforms can be obtained faster than the first. // The same wsave array can be used by rfftf and rfftb. //
        // output parameters //
        //
        r //
        r(1) = the sum from i=1 to i=n of r(i)
        // if n is even set l = n/2 , if n is odd set l = (n+1)/2
        // then for k = 2,...,l
        // r(2*k-2) = the sum from i = 1 to i = n of
        // r(i)*cos((k-1)*(i-1)*2*pi/n)
        // r(2*k-1) = the sum from i = 1 to i = n of
        // -r(i)*sin((k-1)*(i-1)*2*pi/n)
        // if n is even
        // r(n) = the sum from i = 1 to i = n of
        // (-1)**(i-1)*r(i)
        // // note: // this transform is unnormalized since a call of rfftf // followed by a call of rfftb will multiply the input // sequence by n. //
        wsave //
        Contains results which must not be destroyed between // calls of rfftf or rfftb. //
        // static void rfftf(Int n, Float* r, Float* wsave); static void rfftf(Int n, Double* r, Double* wsave); // // rfftb computes the real perodic sequence from its Fourier coefficients // (Fourier synthesis). The transform is defined below at output parameter r. // // Input parameters: //
        //
        n //
        The length of the array r to be transformed. The method // is most efficient when n is a product of small primes. // n may change so long as different work arrays are provided //
        r //
        A real array of length n which contains the sequence // to be transformed //
        wsave //
        A work array which must be dimensioned at least 2*n+15 // in the program that calls rfftb. The wsave array must be // initialized by calling rffti(n,wsave) and a // different wsave array must be used for each different // value of n. This initialization does not have to be // repeated so long as n remains unchanged thus subsequent // transforms can be obtained faster than the first. // The same wsave array can be used by rfftf and rfftb. //
        // Output parameters: //
        //
        r //
        for n even and for i = 1,...,n
        // r(i) = r(1)+(-1)**(i-1)*r(n)
        // plus the sum from k=2 to k=n/2 of
        // 2.*r(2*k-2)*cos((k-1)*(i-1)*2*pi/n)
        // -2.*r(2*k-1)*sin((k-1)*(i-1)*2*pi/n)
        // for n odd and for i = 1,...,n
        // r(i) = r(1) plus the sum from k=2 to k=(n+1)/2 of
        // 2.*r(2*k-2)*cos((k-1)*(i-1)*2*pi/n)
        // -2.*r(2*k-1)*sin((k-1)*(i-1)*2*pi/n)
        // // note: // this transform is unnormalized since a call of rfftf // followed by a call of rfftb will multiply the input // sequence by n. //
        wsave //
        Contains results which must not be destroyed between // calls of rfftb or rfftf. //
        // static void rfftb(Int n, Float* r, Float* wsave); static void rfftb(Int n, Double* r, Double* wsave); // // ezffti initializes the array wsave which is used in both ezfftf // and ezfftb. The prime factorization of n together with a // tabulation of the trigonometric functions are computed and stored in wsave. // // Input parameter: //
        //
        n //
        The length of the sequence to be transformed. //
        // Output parameter: //
        //
        wsave //
        A work array which must be dimensioned at least 3*n+15. // The same work array can be used for both ezfftf and ezfftb // as long as n remains unchanged. Different wsave arrays // are required for different values of n. //
        static void ezffti(Int n, Float* wsave); // ezfftf computes the Fourier coefficients of a real // perodic sequence (Fourier analysis). The transform is defined // below at output parameters azero, a and b. ezfftf is a simplified // but slower version of rfftf. // // Input parameters: //
        //
        n //
        The length of the array r to be transformed. The method // is most efficient when n is the product of small primes. //
        r //
        A real array of length n which contains the sequence // to be transformed. r is not destroyed. //
        wsave //
        A work array which must be dimensioned at least 3*n+15 // in the program that calls ezfftf. The wsave array must be // initialized by calling ezffti(n,wsave) and a // different wsave array must be used for each different // value of n. This initialization does not have to be // repeated so long as n remains unchanged thus subsequent // transforms can be obtained faster than the first. // The same wsave array can be used by ezfftf and ezfftb. //
        // Output parameters: //
        //
        azero //
        The sum from i=1 to i=n of r(i)/n //
        a,b //
        Real arrays of length n/2 (n even) or (n-1)/2 (n odd)
        // for n even
        // b(n/2)=0, and
        // a(n/2) is the sum from i=1 to i=n of (-1)**(i-1)*r(i)/n
        // // for n even define kmax=n/2-1
        // for n odd define kmax=(n-1)/2
        // then for k=1,...,kmax
        // a(k) equals the sum from i=1 to i=n of
        // 2./n*r(i)*cos(k*(i-1)*2*pi/n)
        // b(k) equals the sum from i=1 to i=n of
        // 2./n*r(i)*sin(k*(i-1)*2*pi/n)
        //
        static void ezfftf(Int n, Float* r, Float* azero, Float* a, Float* b, Float* wsave); // ezfftb computes a real perodic sequence from its // Fourier coefficients (Fourier synthesis). The transform is // defined below at output parameter r. ezfftb is a simplified // but slower version of rfftb. // // Input parameters: //
        //
        n //
        The length of the output array r. The method is most // efficient when n is the product of small primes. //
        azero //
        The constant Fourier coefficient //
        a,b //
        Arrays which contain the remaining Fourier coefficients // these arrays are not destroyed. // The length of these arrays depends on whether n is even or // odd. // If n is even n/2 locations are required, // if n is odd (n-1)/2 locations are required. //
        wsave //
        A work array which must be dimensioned at least 3*n+15. // in the program that calls ezfftb. The wsave array must be // initialized by calling ezffti(n,wsave) and a // different wsave array must be used for each different // value of n. This initialization does not have to be // repeated so long as n remains unchanged thus subsequent // transforms can be obtained faster than the first. // The same wsave array can be used by ezfftf and ezfftb. //
        // Output parameters: //
        //
        r //
        if n is even define kmax=n/2
        // if n is odd define kmax=(n-1)/2
        // then for i=1,...,n
        // r(i)=azero plus the sum from k=1 to k=kmax of
        // a(k)*cos(k*(i-1)*2*pi/n)+b(k)*sin(k*(i-1)*2*pi/n)
        // where
        // c(k) = .5*cmplx(a(k),-b(k)) for k=1,...,kmax
        // c(-k) = conjg(c(k))
        // c(0) = azero
        // and i=sqrt(-1)
        //
        static void ezfftb(Int n, Float* r, Float* azero, Float* a, Float* b, Float* wsave); // sinti initializes the array wsave which is used in // sint. The prime factorization of n together with a tabulation of // the trigonometric functions are computed and stored in wsave. // // Input parameter: //
        //
        n //
        The length of the sequence to be transformed. the method // is most efficient when n+1 is a product of small primes. //
        // Output parameter: //
        //
        wsave //
        A work array with at least int(2.5*n+15) locations. // Different wsave arrays are required for different values // of n. The contents of wsave must not be changed between // calls of sint. //
        // static void sinti(Int n, Float* wsave); static void sinti(Int n, Double* wsave); // // sint computes the discrete Fourier sine transform // of an odd sequence x(i). The transform is defined below at // output parameter x. // sint is the unnormalized inverse of itself since a call of sint // followed by another call of sint will multiply the input sequence // x by 2*(n+1). // The array wsave which is used by sint must be // initialized by calling sinti(n,wsave). // // Input parameters: //
        //
        n //
        The length of the sequence to be transformed. The method // is most efficient when n+1 is the product of small primes. //
        x //
        An array which contains the sequence to be transformed //
        wsave //
        A work array with dimension at least int(2.5*n+15) // in the program that calls sint. The wsave array must be // initialized by calling sinti(n,wsave) and a // different wsave array must be used for each different // value of n. This initialization does not have to be // repeated so long as n remains unchanged thus subsequent // transforms can be obtained faster than the first. //
        // Output parameters: //
        //
        x //
        for i=1,...,n
        // x(i) = the sum from k=1 to k=n
        // 2*x(k)*sin(k*i*pi/(n+1))
        // // a call of sint followed by another call of // sint will multiply the sequence x by 2*(n+1). // Hence sint is the unnormalized inverse // of itself. // //
        wsave //
        Contains initialization calculations which must not be // destroyed between calls of sint. //
        // static void sint(Int n, Float* x, Float* wsave); static void sint(Int n, Double* x, Double* wsave); // // costi initializes the array wsave which is used in // cost. The prime factorization of n together with a tabulation of // the trigonometric functions are computed and stored in wsave. // // Input parameter: //
        //
        n //
        The length of the sequence to be transformed. The method // is most efficient when n-1 is a product of small primes. //
        // Output parameter: //
        //
        wsave //
        A work array which must be dimensioned at least 3*n+15. // Different wsave arrays are required for different values // of n. The contents of wsave must not be changed between // calls of cost. //
        // static void costi(Int n, Float* wsave); static void costi(Int n, Double* wsave); // // cost computes the discrete Fourier cosine transform // of an even sequence x(i). The transform is defined below at output // parameter x. // cost is the unnormalized inverse of itself since a call of cost // followed by another call of cost will multiply the input sequence // x by 2*(n-1). The transform is defined below at output parameter x. // The array wsave which is used by cost must be // initialized by calling costi(n,wsave). // // Input parameters: //
        //
        n //
        The length of the sequence x. n must be greater than 1. // The method is most efficient when n-1 is a product of // small primes. //
        x //
        An array which contains the sequence to be transformed //
        wsave //
        A work array which must be dimensioned at least 3*n+15 // in the program that calls cost. The wsave array must be // initialized by calling costi(n,wsave) and a // different wsave array must be used for each different // value of n. This initialization does not have to be // repeated so long as n remains unchanged thus subsequent // transforms can be obtained faster than the first. //
        // Output parameters: //
        //
        x //
        for i=1,...,n
        // x(i) = x(1)+(-1)**(i-1)*x(n)
        // + the sum from k=2 to k=n-1
        // 2*x(k)*cos((k-1)*(i-1)*pi/(n-1))
        // // a call of cost followed by another call of // cost will multiply the sequence x by 2*(n-1) // hence cost is the unnormalized inverse // of itself. //
        wsave //
        Contains initialization calculations which must not be // destroyed between calls of cost. //
        // static void cost(Int n, Float* x, Float* wsave); static void cost(Int n, Double* x, Double* wsave); // // sinqi initializes the array wsave which is used in both sinqf and // sinqb. The prime factorization of n together with a tabulation of // the trigonometric functions are computed and stored in wsave. // // Input parameter: //
        //
        n //
        The length of the sequence to be transformed. The method // is most efficient when n is a product of small primes. //
        // Output parameter: //
        //
        wsave //
        A work array which must be dimensioned at least 3*n+15. // The same work array can be used for both sinqf and sinqb // as long as n remains unchanged. Different wsave arrays // are required for different values of n. The contents of // wsave must not be changed between calls of sinqf or sinqb. //
        // static void sinqi(Int n, Float* wsave); static void sinqi(Int n, Double* wsave); // // sinqf computes the fast Fourier transform of quarter wave data. That is, // sinqf computes the coefficients in a sine series representation with only // odd wave numbers. The transform is defined below at output parameter x. // // sinqb is the unnormalized inverse of sinqf since a call of sinqf followed by // a call of sinqb will multiply the input sequence x by 4*n. // // The array wsave which is used by sinqf must be initialized by calling // sinqi(n,wsave). // // Input parameters: //
        //
        n //
        The length of the array x to be transformed. The method // is most efficient when n is a product of small primes. //
        x //
        An array which contains the sequence to be transformed //
        wsave // A work array which must be dimensioned at least 3*n+15. // in the program that calls sinqf. The wsave array must be // initialized by calling sinqi(n,wsave) and a // different wsave array must be used for each different // value of n. This initialization does not have to be // repeated so long as n remains unchanged thus subsequent // transforms can be obtained faster than the first. //
        // Output parameters: //
        //
        x //
        for i=1,...,n
        // x(i) = (-1)**(i-1)*x(n)
        // + the sum from k=1 to k=n-1 of
        // 2*x(k)*sin((2*i-1)*k*pi/(2*n))
        // // a call of sinqf followed by a call of // sinqb will multiply the sequence x by 4*n. // therefore sinqb is the unnormalized inverse // of sinqf. //
        wsave //
        Contains initialization calculations which must not // be destroyed between calls of sinqf or sinqb. //
        // static void sinqf(Int n, Float* x, Float* wsave); static void sinqf(Int n, Double* x, Double* wsave); // // sinqb computes the fast Fourier transform of quarter // wave data. that is, sinqb computes a sequence from its // representation in terms of a sine series with odd wave numbers. // the transform is defined below at output parameter x. // // sinqf is the unnormalized inverse of sinqb since a call of sinqb // followed by a call of sinqf will multiply the input sequence x // by 4*n. // // The array wsave which is used by sinqb must be // initialized by calling sinqi(n,wsave). // // Input parameters: //
        //
        n //
        The length of the array x to be transformed. The method // is most efficient when n is a product of small primes. //
        x //
        An array which contains the sequence to be transformed //
        wsave // A work array which must be dimensioned at least 3*n+15. // in the program that calls sinqb. The wsave array must be // initialized by calling sinqi(n,wsave) and a // different wsave array must be used for each different // value of n. This initialization does not have to be // repeated so long as n remains unchanged thus subsequent // transforms can be obtained faster than the first. //
        // Output parameters: //
        //
        x //
        for i=1,...,n
        // x(i)= the sum from k=1 to k=n of
        // 4*x(k)*sin((2k-1)*i*pi/(2*n))
        // // a call of sinqb followed by a call of // sinqf will multiply the sequence x by 4*n. // Therefore sinqf is the unnormalized inverse // of sinqb. //
        wsave //
        Contains initialization calculations which must not // be destroyed between calls of sinqb or sinqf. //
        // static void sinqb(Int n, Float* x, Float* wsave); static void sinqb(Int n, Double* x, Double* wsave); // // static void cosqi(Int n, Float* wsave); static void cosqi(Int n, Double* wsave); // // static void cosqf(Int n, Float* x, Float* wsave); static void cosqf(Int n, Double* x, Double* wsave); // // static void cosqb(Int n, Float* x, Float* wsave); static void cosqb(Int n, Double* x, Double* wsave); // }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/FFTServer.cc000066400000000000000000000027711476623553700213370ustar00rootroot00000000000000//# FFTServer.cc: A class with methods for Fast Fourier Transforms //# Copyright (C) 1994,1995,1996,1997,1998,1999,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Instantiate the templates. template class FFTServer; template class FFTServer; } //# NAMESPACE CASACORE - END casacore-3.7.1/scimath/Mathematics/FFTServer.h000066400000000000000000000453731476623553700212060ustar00rootroot00000000000000//# FFTServer.h: A class with methods for Fast Fourier Transforms //# Copyright (C) 1994,1995,1996,1997,1999,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_FFTSERVER_H #define SCIMATH_FFTSERVER_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Lists the different types of FFT's that can be done // This enumerator is brought out as a separate class because g++ // currently cannot handle enumerators in a templated class. When it can this // class will go away and this enumerator moved into the FFTServer // class class FFTEnums { public: enum TransformType { // Forward Complex to Complex transforms. COMPLEX, // Inverse Complex to Complex transforms. INVCOMPLEX, // Real to Complex or Complex to Real transforms. REALTOCOMPLEX, // Real to Complex or Complex to Real transforms. COMPLEXTOREAL, // Real to Real transforms with symmetric Arrays (not used) REALSYMMETRIC }; }; // A class with methods for Fast Fourier Transforms // // // // //
      • Basic concepts of Fast Fourier Transforms. //
      • The Arrays module // // The FFTServer class, can do Fast Fourier Transforms of // any length and dimensionality. // // // The FFTServer class provides methods for performing n-dimensional Fast // Fourier Transforms with real and complex Array's of arbitrary size and // dimensionality. It can do either real to complex, complex to real, or // complex to complex transforms with the "origin" of the transform either at // the centre of the Array or at the first element. // Because the output from a real to complex transform is Hermitian only half // of the complex result is returned. Similarly with a complex to real // transform only half of the complex plane is required, the other half is // implicitly assumed to be the complex conjugate of the supplied half-plane. // The complex to real transform does not check that the // imaginary component of the values where u=0 are zero // This class can be initialised with a shape that indicates the length of the // transforms that will be performed, and whether they are going to be // real<->complex transforms or complex<->complex ones. This initialisation // sets up a variety of internal buffers and computes factorizations and // twiddle factors used during the transform. The initialised transform shape // is always compared with the shape of the supplied arguments when a transform // is done and the FFTServer class will automatically resize itself if // necessary. So the default constructor is perfectly safe to use. // With any transform the output Array must either be the correct shape for the // desired output or zero length (ie not contain any elements). If it is zero // length then it will be resized to the correct shape. For a complex->complex // transform the output Array will be the same shape as the input Array. For a // real->complex transform the output Array will be the same size as the input // Array except in the first dimension which will have a length of (nx+2)/2. So // if nx=7 the output length will be 4 and if nx=8 the output length will be 5, // on the first axis. nx is the length of the first axis on the real // input Array and cx (which is used later) is the length of the first axis on // the complex input Array. // For complex to real transforms the output length on the first axis // is not uniquely defined by the shape of the complex input // Array. This class uses the following algorithm to work out the // length of the first axis on the output Array. //
          //
        • If the size of the output Array is non-zero then its shape must match // the size of the input Array except for the first axis. The length of the // first axis must either be 2*cx-2 or 2*cx-1 and this determines the length of // the transform on the first axis. //
        • If the size of the output Array is zero then scan the imaginary // components of the values at the end of the first axis on the input Array (ie // at [cx-1,....] If any of these are non-zero the output Array // will have an odd length. //
        • Otherwise if all the imaginary components described above are zero then // look at the current size of the FFTServer object (either defined at // construction time or with the resize function). If it matches the size of // the input Array except for the first axis and if the length on this axis is // either 2*cx-2 or 2*cx-1 then use that to determine the size of the output // Array. //
        • Otherwise assume the output Array will an even length of 2*cx-2 on its // first axis. //
        // This class does transforms using // the highly optimized FFTW package. //
        // P.N. Swarztrauber, Vectorizing the FFTs, in Parallel Computations // (G. Rodrigue, ed.), Academic Press, 1982, pp. 51--83.
        //
        If at build time it is chosen to use FFTW in a multi-threaded way, // it will try to use as many cores as possible. // In this class a forward transform is defined as one that goes from the real // to the complex (or the time to frequency) domain. In a forward transform the // sign of the exponent is negative and no scaling is done on the output. The // backward transform goes from the complex to the real (or the frequency to // the time) domain. The sign of the exponent is positive and the result is // always scaled by 1/N were N is the total number of elements in the Array. // The origin of the transform is defined as the point where setting only that // element to one, and then doing a forward transform results in an Array that // is all one. The fft member functions in this class all assume // that the origin of the Transform is at the centre of the Array ie. at // [nx/2,ny/2,...] were the indexing begins at zero. Because the // fftpack software assumes the origin of the transform is at the first element // ie.,[0,0,...] this class flips the data in the Array around to // compensate. For fftpack this flipping takes about one 20% of the total // transform time, while for FFTW it can easily exceed the transform time. // Flipping can be avoided by using the fft0 member // functions which do not flip the data. // Some of the member functions in this class scramble the input Array, // possibly by flipping the quandrants of the data although this is not // guaranteed. Modification of the input Array can be avoided, at the expense // of copying the data to temporary storage, by either: //
        • Ensuring the input Array is a const Array. //
        • Setting the constInput Flag to True. //
        // The latter option is provided to avoid users having to cast non-const // Arrays to const ones in order to prevent there input Array from being // scrambled. // This class assumes that a Complex array is stored as // pairs of floating point numbers, with no intervening gaps, and with the real // component first ie., [re0,im0,re1,im1, ...]. This means that the // following type casts work, // // S * complexPtr; // T * realPtr = (T * ) complexPtr; // // and allow a Complex number to be accessed as a pair of real numbers. If this // assumption is bad then real Arrays will have to generated by copying the // complex ones. Ultimately this assumption about Complex<->Real Array // conversion should be put somewhere central like Array2Math.cc. // //
        // //
      • The T argument must be of type Float or Double. These are the only // possible instantiations of this class. // // //
      • The S argument must be of type Complex, if T is Float, or DComplex, if T is // Double. These are the only possible instantiations of this class. // // // // Do a real to complex Transform of a 1-Dimensional Vector. The following // example can trivially be extended to any number of dimensions. // // FFTServer server; // Vector input(32); // Vector output(17); // input = 0.0f; // input(16) = 1.0f; // cout << "Input:" << input << endl; // server.fft(output, input); // cout << "Output:" << output << endl; // // // // //
      • AipsError: If the input and output Array have bad or incompatible // shapes. See the individual function descriptions for what Array shapes are // required. // // // //
      • The time taken to flip the Array can be reduced, if all the Array // dimensions are even, by pre-multiplying the every other element on the // input Array by -1. Then no flipping needs to be done on the output Array. // template class FFTServer { public: // The default constructor. The server will automatically resize to do // transforms of the appropriate length when necessary. FFTServer(); // Initialise the server to do transforms on Arrays of the specified // shape. The server will, however, resize to do transforms of other lengths // if necessary. See the resize function for a description of the // TransformType enumerator. FFTServer(const IPosition & fftSize, const FFTEnums::TransformType transformType = FFTEnums::REALTOCOMPLEX); // copy constructor. The copied server is initialised to do transforms of the // same length as the other server. Uses copy (and not reference) semantics // so that changing the transform length of one server does not affect the // other server. FFTServer(const FFTServer & other); // destructor ~FFTServer(); // The assignment operator which does the same thing as the copy // constructor. FFTServer & operator=(const FFTServer & other); // Modify the FFTServer object to do transforms of the supplied shape. The // amount of internal storage, and the initialisation, depends on the type of // transform that will be done. The transform type is specified with the // TransformTypes enumerator. Currently there is no difference in // initialisation for the COMPLEXTOREAL and REALTOCOMPLEX transforms. The // shape argument is the shape of the real array (or complex one if complex // to complex transforms are being done). In general it is not necessary to // use this function as all the fft & fft0 functions will automatically // resize the server, if necessary, to match their input arguments. void resize(const IPosition & fftSize, const FFTEnums::TransformType transformType = FFTEnums::REALTOCOMPLEX); // Real to complex fft. The origin of the transform is in the centre of the // Array. Because of the Hermitian property the output Array only contains // half of the complex result. The output Array must either have no elements // or be a size that is appropriate to the input Array size, // ie. shape = [(nx+2)/2, ny, nz,...]. Otherwise an AipsError is // thrown. See the synopsis for a description of the constInput flag. // void fft(Array & cResult, Array & rData, const Bool constInput=False); void fft(Array & cResult, const Array & rData); // // Complex to real fft. The origin of the transform is in the centre of the // Array. Because of the Hermitian property the input Array only contains // half of the complex values. The output Array must either have no elements, // or be a size that is appropriate to the input Array size ie.,
        // shape = [2*cx-2, cy, cz,...] or
        // shape = [2*cx-1, cy, cz,...].
        // Otherwise an AipsError is thrown. See the description in the synopsis for // the algorithm used to choose between the two possible output shapes and a // description of the constInput Flag. // void fft(Array & rResult, Array & cData, const Bool constInput=False); void fft(Array & rResult, const Array & cData); // // Complex to complex in-place fft. The origin of the transform is in the // centre of the Array. The direction of the transform is controlled by the // toFrequency variable. If True then a forward, or time to frequency, // transform is performed. If False a backward or frequency to time transform // is done. Scaling is always done on the backward transform. void fft(Array & cValues, const Bool toFrequency=True); // Complex to complex fft. The origin of the transform is in the centre of // the Array. The direction of the transform is controlled by the toFrequency // variable. If True then a forward, or time to frequency, transform is // performed. If False a backward or frequency to time transform is // done. Scaling is always done on the backward transform. The output Array // must either either contain no elements or be the same as the input Array, // ie. shape = [cx, cy, cz,...]. Otherwise an AipsError is // thrown. void fft(Array & cResult, const Array & cData, const Bool toFrequency=True); // The fft0 functions are equivalent to the fft // functions described above except that the origin of the transform is the // first element of the Array, ie. [0,0,0...], rather than the centre // element, ie [nx/2, ny/2, nz/2, ...]. As the underlying functions // assume that the origin of the transform is the first element these // routines are in general faster than the equivalent ones with the origin // at the centre of the Array. // void fft0(Array & cResult, Array & rData, const Bool constInput=False); void fft0(Array & cResult, const Array & rData); void fft0(Array & rResult, Array & cData, const Bool constInput=False); void fft0(Array & rResult, const Array & cData); void fft0(Array & cValues, const Bool toFrequency=True); void fft0(Array & cResult, const Array & cData, const Bool toFrequency=True); //# void fft0(Array & rValues, const Bool toFrequency=True); // //# Flips the quadrants in a complex Array so that the point at //# cData.shape()/2 moves to the origin. This moves, for example, the point //# at [8,3] to the origin ([0,0]) in an array of shape [16,7]. Usually two //# flips will restore an Array to its original state. But for Array's //# where one or more dimension is an odd length two flips do NOT restore //# the data to its original state. So the when toZero=False this routine //# does an unflip operation (ie moves the data at [0,0] to the centre) and //# restores the data to its original state for odd length arrays. When //# passed a Hermitian Array where half the complex plane is implicit (eg as //# produced by a real->complex Transform) it is not necessary to flip the //# first dimension of the Array. In this case the isHermitian flag should //# be set to True. For complex<->complex transforms this should be False. // void flip(Array & rData, const Bool toZero, const Bool isHermitian); void flip(Array & cData, const Bool toZero, const Bool isHermitian); // // N-D in-place complex->complex FFT shift (FFT - phase-mult - inverse FFT) // If toFrequency is true, the first FFT will be from time to frequency. // relshift is the freq shift normalised to the bandwidth. // Only transform over selected dimension. Iterate over the others. void fftshift(Array & cValues, const uInt& whichAxis, const Double& relshift, const Bool toFrequency=True); // N-D complex->complex FFT shift (FFT - phase-mult - inverse FFT) // with flagging. // If toFrequency is true, the first FFT will be from time to frequency. // relshift is the freq shift normalised to the bandwidth. // Only transform over selected dimension. Iterate over the others. void fftshift(Array & outValues, Array & outFlags, const Array & cValues, const Array& inFlags, const uInt& whichAxis, const Double& relshift, const Bool goodIsTrue=False, const Bool toFrequency=True); // N-D real->real FFT shift (FFT to complex - phase-mult - inverse FFT) // with flagging. // relshift is the freq shift normalised to the bandwidth. // Only transform over selected dimension. Iterate over the others. void fftshift(Array & outValues, Array & outFlags, const Array & rValues, const Array& inFlags, const uInt& whichAxis, const Double& relshift, const Bool goodIsTrue=False); private: //# finds the shape of the output array when doing complex->real transforms IPosition determineShape(const IPosition & rShape, const Array & cData); //# Data members. // The size of the last FFT done by this object IPosition itsSize; // Whether the last FFT was complex<->complex or not FFTEnums::TransformType itsTransformType; // buffer for copying non-contigious arrays to contigious ones. This is done // so that the FFT's have a better chance of fitting into cache and hence // going faster. // This buffer is also used as temporary storage when flipping the data. Block itsBuffer; // FFTW specific members. FFTW itsFFTW; std::vector itsWorkIn; std::vector itsWorkOut; std::vector itsWorkC2C; }; } //# NAMESPACE CASACORE - END //# Do NOT include the .tcc file here like done for other templated classes. //# The instantiations are done explicitly. //# In this way the HAVE_FFTW ifdef is only used in .cc files and does //# not appear in headers, so other packages using FFTServer do not need //# to (un)set HAVE_FFTW. #endif casacore-3.7.1/scimath/Mathematics/FFTServer.hcc000066400000000000000000000525351476623553700215120ustar00rootroot00000000000000//# FFTServer.tcc: A class with methods for Fast Fourier Transforms //# Copyright (C) 1994,1995,1996,1997,1998,1999,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include //# This file contains the templated functions dependent on using FFTW3. //# It used to use FFTPack as alternative implementation, but this has //# been removed in 2020 because FFTPack did not compile with new compilers. //# Note that the file is called .hcc (and not. tcc) to emphasize that it //# should not be used for automatic template instantiation (that knowledge //# about using FFTW3 is only needed when building casacore). namespace casacore { //# NAMESPACE CASACORE - BEGIN template FFTServer:: FFTServer() : itsTransformType (FFTEnums::REALTOCOMPLEX) {} template FFTServer:: FFTServer(const IPosition & fftSize, const FFTEnums::TransformType transformType) : itsTransformType (transformType) { resize (fftSize, transformType); } template FFTServer:: FFTServer(const FFTServer & other) : itsTransformType (other.itsTransformType) { resize (other.itsSize, other.itsTransformType); } template FFTServer:: ~FFTServer() { } template FFTServer & FFTServer:: operator=(const FFTServer & other) { if (this != &other) { resize (other.itsSize, other.itsTransformType); } return *this; } template void FFTServer:: resize(const IPosition & fftSize, const FFTEnums::TransformType transformType) { DebugAssert(fftSize.nelements() > 0, AipsError); DebugAssert(fftSize.product() > 0, AipsError); // Only resize if different type or size. uInt ndim = fftSize.nelements(); if (transformType != itsTransformType || itsSize.nelements() != ndim || fftSize != itsSize) { itsTransformType = transformType; itsSize.resize (ndim, False); // to make assignment work! itsSize = fftSize; size_t nelem = itsSize.product(); itsWorkIn.resize (nelem); itsWorkOut.resize (nelem / itsSize[0] * (itsSize[0]/2+1)); itsWorkC2C.resize (nelem); IPosition transpose(ndim); for (uInt i=0; i void FFTServer:: fft(Array & cResult, Array & rData, const Bool constInput) { if (constInput) { Array rCopy = rData.copy(); flip(rCopy,True,False); fft0(cResult, rCopy, False); } else { flip(rData,True,False); fft0(cResult, rData, False); } flip(cResult,False,True); } template void FFTServer:: fft(Array & cResult, const Array & rData) { fft(cResult, (Array &) rData, True); } template void FFTServer:: fft(Array & rResult, Array & cData, const Bool constInput) { if (constInput) { Array cCopy = cData.copy(); flip(cCopy, True, True); fft0(rResult, cCopy, False); } else { flip(cData, True, True); fft0(rResult, cData, False); } flip(rResult, False, False); } template void FFTServer:: fft(Array & rResult, const Array & cData) { fft(rResult, (Array &) cData, True); } template void FFTServer:: fft(Array & cValues, const Bool toFrequency) { flip(cValues, True, False); fft0(cValues, toFrequency); flip(cValues, False, False); } template void FFTServer:: fft(Array & cResult, const Array & cData, const Bool toFrequency) { if (cResult.nelements() != 0) { AlwaysAssert(cResult.conform(cData), AipsError); } else { cResult.resize(cData.shape()); } cResult = cData; fft(cResult, toFrequency); } template void FFTServer:: fft0(Array & cResult, Array & rData, const Bool) { const IPosition shape = rData.shape(); // Ensure the output Array is the required size IPosition resultShape = shape; resultShape(0) = (shape(0)+2)/2; if (cResult.nelements() != 0) { AlwaysAssert(resultShape.isEqual(cResult.shape()), AipsError); } else { cResult.resize(resultShape); } // Early exit if the Array is all zero; if (allNearAbs(rData, T(0), NumericTraits::minimum)) { cResult = S(0); return; } // Initialise the work arrays if (!shape.isEqual(itsSize) || itsTransformType != FFTEnums::REALTOCOMPLEX) { resize(shape, FFTEnums::REALTOCOMPLEX); } // get a pointer to the array holding the result Bool resultIsAcopy, dataIsAcopy; S * resultPtr = cResult.getStorage(resultIsAcopy); const T* dataPtr = rData.getStorage(dataIsAcopy); IPosition fftwShape(resultShape); objcopy(&(itsWorkIn[0]), dataPtr, itsWorkIn.size()); itsFFTW.r2c(itsSize, &(itsWorkIn[0]), &(itsWorkOut[0])); objcopy(resultPtr, &(itsWorkOut[0]), itsWorkOut.size()); rData.freeStorage(dataPtr, dataIsAcopy); cResult.putStorage(resultPtr, resultIsAcopy); } template void FFTServer:: fft0(Array & cResult, const Array & rData) { fft0(cResult, (Array &) rData, True); } template void FFTServer:: fft0(Array & rResult, Array & cData, const Bool constInput) { Array cCopy; if (constInput) { cCopy = cData; } else { cCopy.reference(cData); } const IPosition cShape = cCopy.shape(); const IPosition rShape = determineShape(rResult.shape(), cCopy); rResult.resize(rShape); // Early exit if the Array is all zero; if (allNearAbs(cData, S(0), NumericTraits::minimum)) { rResult = T(0); return; } // resize the server if necessary if (!rShape.isEqual(itsSize) || itsTransformType != FFTEnums::COMPLEXTOREAL) { resize(rShape, FFTEnums::COMPLEXTOREAL); } Bool dataIsAcopy, resultIsAcopy; S * dataPtr = cCopy.getStorage(dataIsAcopy); T *resultPtr = rResult.getStorage(resultIsAcopy); objcopy(&(itsWorkOut[0]), dataPtr, itsWorkOut.size()); itsFFTW.c2r(itsSize, &(itsWorkOut[0]), &(itsWorkIn[0])); for (uInt i = 0; i < itsWorkIn.size(); i++) { itsWorkIn[i] /= 1.0*itsWorkIn.size(); } objcopy(resultPtr, &(itsWorkIn[0]), itsWorkIn.size()); rResult.putStorage(resultPtr, resultIsAcopy); cCopy.freeStorage((const S*&)dataPtr, dataIsAcopy); } template void FFTServer:: fft0(Array & rResult, const Array & cData) { fft0(rResult, (Array &) cData, True); } template void FFTServer:: fft0(Array & cValues, const Bool toFrequency) { // Early exit if the Array is all zero; if (allNearAbs(cValues, S(0), NumericTraits::minimum)) { return; } // resize the server if necessary const IPosition shape = cValues.shape(); if (toFrequency) { if (!shape.isEqual(itsSize) || itsTransformType != FFTEnums::COMPLEX) { resize(shape, FFTEnums::COMPLEX); } } else { if (!shape.isEqual(itsSize) || itsTransformType != FFTEnums::INVCOMPLEX) { resize(shape, FFTEnums::INVCOMPLEX); } } Bool valuesIsAcopy; S * complexPtr = cValues.getStorage(valuesIsAcopy); objcopy(&(itsWorkC2C[0]), complexPtr, itsWorkC2C.size()); itsFFTW.c2c(itsSize, &(itsWorkC2C[0]), toFrequency); if (!toFrequency) { for (uInt i = 0; i < itsWorkC2C.size(); ++i) { itsWorkC2C[i] /= 1.0*itsWorkC2C.size(); } } objcopy(complexPtr, &(itsWorkC2C[0]), itsWorkC2C.size()); cValues.putStorage(complexPtr, valuesIsAcopy); } template void FFTServer:: fft0(Array & cResult, const Array & cData, const Bool toFrequency) { if (cResult.nelements() != 0) { AlwaysAssert(cResult.conform(cData), AipsError); } else { cResult.resize(cData.shape()); } cResult = cData; fft0(cResult, toFrequency); } template IPosition FFTServer:: determineShape(const IPosition & rShape, const Array & cData){ const IPosition cShape=cData.shape(); const uInt cDim = cShape.nelements(); DebugAssert(cDim > 0, AipsError); // If rShape is non-zero then it must match one of the two possible shapes if (rShape.product() != 0) { DebugAssert(cDim == rShape.nelements(), AipsError); IPosition reqShape(cShape); reqShape(0) = 2*cShape(0)-2; if (reqShape.isEqual(rShape)) { return reqShape; } reqShape(0) += 1; if (reqShape.isEqual(rShape)) { return reqShape; } throw(AipsError("FFTServer::determineShape() -" " output array has the wrong shape")); } // Scan the imaginary components of the last samples on the first axis in // the cData to see if there are any non-zero terms. If so the output array // must be odd length in its first axis. { VectorIterator iter((Array &) cData); uInt lastElem = cShape(0)-1; while (!iter.pastEnd()) { if (!near(iter.vector()(lastElem).imag(), (T)0.0)) { IPosition oddLength(cShape); oddLength(0) = cShape(0)*2-1; return oddLength; } iter.next(); } } // See if the FFTServer size can be used to guess the output Array size; if (itsSize.nelements() == cDim) { Bool match = True; for (uInt i = 1; i < cDim; ++i) { if (itsSize(i) != cShape(i)) { match = False; } } if (match == True && ((itsSize(0) == 2*cShape(0) - 2) || (itsSize(0) == 2*cShape(0) - 1))) { return itsSize; } } IPosition defShape(cShape); defShape(0) = 2*cShape(0) - 2; return defShape; } template void FFTServer:: flip(Array & cData, const Bool toZero, const Bool isHermitian) { const IPosition shape = cData.shape(); const uInt ndim = shape.nelements(); const uInt nElements = cData.nelements(); if (nElements == 1) { return; } AlwaysAssert(nElements != 0, AipsError); { Int buffLen = itsBuffer.nelements(); for (uInt i = 0; i < ndim; ++i) { buffLen = max(buffLen, shape(i)); } itsBuffer.resize(buffLen, False, False); } Bool dataIsAcopy; S * dataPtr = cData.getStorage(dataIsAcopy); S * buffPtr = itsBuffer.storage(); S * rowPtr = 0; S * rowPtr2 = 0; S * rowPtr2o = 0; uInt rowLen, rowLen2, rowLen2o; uInt nFlips; uInt stride = 1; uInt r; uInt n=0; if (isHermitian) { n = 1; stride = shape(0); } for (; n < ndim; ++n) { rowLen = shape(n); if (rowLen > 1) { rowLen2 = rowLen/2; rowLen2o = (rowLen+1)/2; nFlips = nElements/rowLen; rowPtr = dataPtr; r = 0; while (r < nFlips) { rowPtr2 = rowPtr + stride * rowLen2; rowPtr2o = rowPtr + stride * rowLen2o; if (toZero) { objcopy(buffPtr, rowPtr2, rowLen2o, 1u, stride); objcopy(rowPtr2o, rowPtr, rowLen2, stride, stride); objcopy(rowPtr, buffPtr, rowLen2o, stride, 1u); } else { objcopy(buffPtr, rowPtr, rowLen2o, 1u, stride); objcopy(rowPtr, rowPtr2o, rowLen2, stride, stride); objcopy(rowPtr2, buffPtr, rowLen2o, stride, 1u); } r++; rowPtr++; if (r%stride == 0) { rowPtr += stride*(rowLen-1); } } stride *= rowLen; } } cData.putStorage(dataPtr, dataIsAcopy); } template void FFTServer:: flip(Array & rData, const Bool toZero, const Bool isHermitian) { const IPosition shape = rData.shape(); const uInt ndim = shape.nelements(); const uInt nElements = rData.nelements(); if (nElements == 1) { return; } AlwaysAssert(nElements != 0, AipsError); { Int buffLen = itsBuffer.nelements(); for (uInt i = 0; i < ndim; ++i) { buffLen = max(buffLen, (shape(i)+1)/2); } itsBuffer.resize(buffLen, False, False); } Bool dataIsAcopy; T * dataPtr = rData.getStorage(dataIsAcopy); T * buffPtr = (T *) itsBuffer.storage(); T * rowPtr = 0; T * rowPtr2 = 0; T * rowPtr2o = 0; uInt rowLen, rowLen2, rowLen2o; uInt nFlips; uInt stride = 1; uInt r; uInt n=0; if (isHermitian) { n = 1; stride = shape(0); } for (; n < ndim; ++n) { rowLen = shape(n); if (rowLen > 1) { rowLen2 = rowLen/2; rowLen2o = (rowLen+1)/2; nFlips = nElements/rowLen; rowPtr = dataPtr; r = 0; while (r < nFlips) { rowPtr2 = rowPtr + stride * rowLen2; rowPtr2o = rowPtr + stride * rowLen2o; if (toZero) { objcopy(buffPtr, rowPtr2, rowLen2o, 1u, stride); objcopy(rowPtr2o, rowPtr, rowLen2, stride, stride); objcopy(rowPtr, buffPtr, rowLen2o, stride, 1u); } else { objcopy(buffPtr, rowPtr, rowLen2o, 1u, stride); objcopy(rowPtr, rowPtr2o, rowLen2, stride, stride); objcopy(rowPtr2, buffPtr, rowLen2o, stride, 1u); } r++; rowPtr++; if (r%stride == 0) { rowPtr += stride*(rowLen-1); } } stride *= rowLen; } } rData.putStorage(dataPtr, dataIsAcopy); } template void FFTServer:: fftshift(Array & cValues, const uInt& whichAxis, const Double& relshift, const Bool toFrequency) { const IPosition arrayShape = cValues.shape(); const uInt vsize = arrayShape[whichAxis]; DebugAssert(vsize > 0, AipsError); // relshift is the freq shift normalised to the bandwidth if(relshift==0.){ return; } const Complex exponent = 2.*M_PI*Complex(0.,1.)*relshift; ArrayIterator ait(cValues, IPosition(1,whichAxis), True); // axes are the cursor while(!ait.pastEnd()){ Array cv = ait.array(); // reference fft0(cv, toFrequency); for(uInt i=0; i void FFTServer:: fftshift(Array & outValues, Array & outFlags, const Array & cValues, const Array & inFlags, const uInt& whichAxis, const Double& relshift, const Bool goodIsTrue, const Bool toFrequency){ const IPosition arrayShape = cValues.shape(); const Int vsize = arrayShape[whichAxis]; if(vsize<2){ // nothing to do return; } const IPosition fArrayShape = inFlags.shape(); AlwaysAssert(arrayShape==fArrayShape, AipsError); AlwaysAssert(abs(relshift)<1.,AipsError); outValues.assign(cValues); outFlags.assign(inFlags); // relshift is the freq shift normalised to the bandwidth if(relshift==0.){ return; } const Complex exponent = 2.*M_PI*Complex(0.,1.)*relshift; Int numToFlag = static_cast (ceil(vsize*fabs(relshift))); Int numToFlag2 = static_cast (floor(vsize*fabs(relshift))); Bool flagNeighbour = (numToFlag2 ait(outValues, IPosition(1,whichAxis), True); // axes are the cursor ArrayIterator fait(outFlags, IPosition(1,whichAxis), True); // axes are the cursor while(!ait.pastEnd()){ Array cv = ait.array(); // reference Array flags = fait.array(); // reference // set flagged channels to average of neighbours to avoid ringing from sharp features if(flags(IPosition(1,0))!=goodIsTrue){ // first channel is flagged cv(IPosition(1,0)) = cv(IPosition(1,1)); } for(Int i=1; i0.){ for(Int i=vsize-1-numToFlag; i>=0; i--){ if( (flags(IPosition(1,i))!=goodIsTrue) && (i+numToFlag < vsize)){ // this channel is flagged flags(IPosition(1,i+numToFlag)) = !goodIsTrue; flags(IPosition(1,i)) = goodIsTrue; if(flagNeighbour && i+numToFlag2>=0){ flags(IPosition(1,i+numToFlag2)) = !goodIsTrue; } } } } else{ for(Int i=numToFlag; i0.){ // start at bottom for(Int i=0; ivsize-1-numToFlag; i--){ flags(IPosition(1,i)) = !goodIsTrue; } } ait.next(); fait.next(); } } template void FFTServer:: fftshift(Array & outValues, Array & outFlags, const Array & rValues, const Array & inFlags, const uInt& whichAxis, const Double& relshift, const Bool goodIsTrue){ const IPosition arrayShape = rValues.shape(); const Int vsize = arrayShape[whichAxis]; if(vsize<2){ // nothing to do return; } const IPosition fArrayShape = inFlags.shape(); AlwaysAssert(arrayShape==fArrayShape, AipsError); AlwaysAssert(abs(relshift)<1.,AipsError); outValues.assign(rValues); outFlags.assign(inFlags); // relshift is the freq shift normalised to the bandwidth if(relshift==0.){ return; } const Complex exponent = -2.*M_PI*Complex(0.,1.)*relshift; // note: opposite sign compared to complex case Int numToFlag = static_cast (ceil(vsize*fabs(relshift))); Int numToFlag2 = static_cast (floor(vsize*fabs(relshift))); Bool flagNeighbour = (numToFlag2 ait(outValues, IPosition(1,whichAxis), True); // axes are the cursor ArrayIterator fait(outFlags, IPosition(1,whichAxis), True); // axes are the cursor while(!ait.pastEnd()){ Array rv = ait.array(); // reference Array flags = fait.array(); // reference // set flagged channels to average of neighbours to avoid ringing from sharp features if(flags(IPosition(1,0))!=goodIsTrue){ // first channel is flagged rv(IPosition(1,0)) = rv(IPosition(1,1)); } for(Int i=1; i cResult; fft0(cResult, rv); for(uInt i=0; i0.){ for(Int i=vsize-1-numToFlag; i>=0; i--){ if( (flags(IPosition(1,i))!=goodIsTrue) && (i+numToFlag < vsize)){ // this channel is flagged flags(IPosition(1,i+numToFlag)) = !goodIsTrue; flags(IPosition(1,i)) = goodIsTrue; if(flagNeighbour && i+numToFlag2>=0){ flags(IPosition(1,i+numToFlag2)) = !goodIsTrue; } } } } else{ for(Int i=numToFlag; i0.){ // start at bottom for(Int i=0; ivsize-1-numToFlag; i--){ flags(IPosition(1,i)) = !goodIsTrue; } } ait.next(); fait.next(); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/scimath/Mathematics/FFTW.cc000066400000000000000000000230431476623553700202720ustar00rootroot00000000000000//# Copyright (C) 1994,1995,1996,1997,1998,1999,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #ifdef HAVE_FFTW3 # include #endif #ifdef _OPENMP # include #endif #include namespace casacore { bool FFTW::is_initialized_fftw = false; std::mutex FFTW::theirMutex; #ifdef HAVE_FFTW3 class FFTWPlan { public: explicit FFTWPlan (fftw_plan plan) : itsPlan(plan) {} ~FFTWPlan() { fftw_destroy_plan(itsPlan); } fftw_plan getPlan() { return itsPlan; } private: FFTWPlan (const FFTWPlan&); FFTWPlan& operator= (const FFTWPlan&); fftw_plan itsPlan; }; class FFTWPlanf { public: explicit FFTWPlanf (fftwf_plan plan) : itsPlan(plan) {} ~FFTWPlanf() { fftwf_destroy_plan(itsPlan); } fftwf_plan getPlan() { return itsPlan; } private: FFTWPlanf (const FFTWPlanf&); FFTWPlanf& operator= (const FFTWPlanf&); fftwf_plan itsPlan; }; FFTW::FFTW() : flags(FFTW_ESTIMATE) { initialize_fftw(); } void FFTW::initialize_fftw() { std::lock_guard lock(theirMutex); if (!is_initialized_fftw) { int numCPUs = HostInfo::numCPUs(); int nthreads = 1; if (numCPUs > 1) { nthreads = numCPUs; } #ifdef HAVE_FFTW3_THREADS fftwf_init_threads(); fftw_init_threads(); fftwf_plan_with_nthreads(nthreads); fftw_plan_with_nthreads(nthreads); #endif is_initialized_fftw = true; } } FFTW::~FFTW() { // We cannot deinitialize FFTW as in the following because // there may be other instances of this class around // Could do it when keeping a static counter, but must be made thread-safe. #if 0 fftw_cleanup(); fftwf_cleanup(); fftw_cleanup_threads(); fftwf_cleanup_threads(); #endif } void FFTW::plan_r2c(const IPosition &size, float *in, std::complex *out) { itsPlanR2Cf.reset( new FFTWPlanf (fftwf_plan_dft_r2c(size.nelements(), size.asStdVector().data(), in, reinterpret_cast(out), flags)) ); } void FFTW::plan_r2c(const IPosition &size, double *in, std::complex *out) { itsPlanR2C.reset( new FFTWPlan (fftw_plan_dft_r2c(size.nelements(), size.asStdVector().data(), in, reinterpret_cast(out), flags)) ); } void FFTW::plan_c2r(const IPosition &size, std::complex *in, float *out) { itsPlanC2Rf.reset( new FFTWPlanf (fftwf_plan_dft_c2r(size.nelements(), size.asStdVector().data(), reinterpret_cast(in), out, flags)) ); } void FFTW::plan_c2r(const IPosition &size, std::complex *in, double *out) { itsPlanC2R.reset( new FFTWPlan (fftw_plan_dft_c2r(size.nelements(), size.asStdVector().data(), reinterpret_cast(in), out, flags)) ); } void FFTW::plan_c2c_forward(const IPosition &size, std::complex *in) { itsPlanC2CF.reset( new FFTWPlan (fftw_plan_dft(size.nelements(), size.asStdVector().data(), reinterpret_cast(in), reinterpret_cast(in), FFTW_FORWARD, flags)) ); } void FFTW::plan_c2c_forward(const IPosition &size, std::complex *in) { itsPlanC2CFf.reset( new FFTWPlanf (fftwf_plan_dft(size.nelements(), size.asStdVector().data(), reinterpret_cast(in), reinterpret_cast(in), FFTW_FORWARD, flags)) ); } void FFTW::plan_c2c_backward(const IPosition &size, std::complex *in) { itsPlanC2CB.reset( new FFTWPlan (fftw_plan_dft(size.nelements(), size.asStdVector().data(), reinterpret_cast(in), reinterpret_cast(in), FFTW_BACKWARD, flags)) ); } void FFTW::plan_c2c_backward(const IPosition &size, std::complex *in) { itsPlanC2CBf.reset( new FFTWPlanf (fftwf_plan_dft(size.nelements(), size.asStdVector().data(), reinterpret_cast(in), reinterpret_cast(in), FFTW_BACKWARD, flags)) ); } // the parameters are used only in order to overload this function void FFTW::r2c(const IPosition&, float*, std::complex*) { fftwf_execute(itsPlanR2Cf->getPlan()); } void FFTW::r2c(const IPosition&, double*, std::complex*) { fftw_execute(itsPlanR2C->getPlan()); } void FFTW::c2r(const IPosition&, std::complex*, float*) { fftwf_execute(itsPlanC2Rf->getPlan()); } void FFTW::c2r(const IPosition&, std::complex*, double*) { fftw_execute(itsPlanC2R->getPlan()); } void FFTW::c2c(const IPosition&, std::complex*, bool forward) { if (forward) { fftwf_execute(itsPlanC2CFf->getPlan()); } else { fftwf_execute(itsPlanC2CBf->getPlan()); } } void FFTW::c2c(const IPosition&, std::complex*, bool forward) { if (forward) { fftw_execute(itsPlanC2CF->getPlan()); } else { fftw_execute(itsPlanC2CB->getPlan()); } } FFTW::Plan FFTW::plan_redft00(const IPosition &size, float *in, float *out) { initialize_fftw(); std::vector kinds(size.nelements(), FFTW_REDFT00); return Plan( new FFTWPlanf( fftwf_plan_r2r(size.nelements(), size.asStdVector().data(), in, out, kinds.data(), FFTW_ESTIMATE)) ); } FFTW::Plan FFTW::plan_redft00(const IPosition &size, double *in, double *out) { initialize_fftw(); std::vector kinds(size.nelements(), FFTW_REDFT00); return Plan( new FFTWPlan( fftw_plan_r2r(size.nelements(), size.asStdVector().data(), in, out, kinds.data(), FFTW_ESTIMATE)) ); } void FFTW::Plan::Execute(float *in, float *out) { fftwf_execute_r2r(_planf->getPlan(), in, out); } void FFTW::Plan::Execute(double *in, double *out) { fftw_execute_r2r(_plan->getPlan(), in, out); } #else class FFTWPlan { }; class FFTWPlanf { }; FFTW::FFTW() {} FFTW::~FFTW() {} void FFTW::plan_r2c(const IPosition&, float*, std::complex*) {} void FFTW::plan_r2c(const IPosition&, double*, std::complex*) {} void FFTW::plan_c2r(const IPosition&, std::complex*, float*) {} void FFTW::plan_c2r(const IPosition&, std::complex*, double*) {} void FFTW::plan_c2c_forward(const IPosition&, std::complex*) {} void FFTW::plan_c2c_forward(const IPosition&, std::complex*) {} void FFTW::plan_c2c_backward(const IPosition&, std::complex*) {} void FFTW::plan_c2c_backward(const IPosition&, std::complex*) {} void FFTW::r2c(const IPosition&, float*, std::complex*) {} void FFTW::r2c(const IPosition&, double*, std::complex*) {} void FFTW::c2r(const IPosition&, std::complex*, float*) {} void FFTW::c2r(const IPosition&, std::complex*, double*) {} void FFTW::c2c(const IPosition&, std::complex*, Bool) {} void FFTW::c2c(const IPosition&, std::complex*, Bool) {} FFTW::Plan FFTW::plan_redft00(const IPosition &, float *, float *) { throw std::runtime_error("FFTW not available"); } FFTW::Plan FFTW::plan_redft00(const IPosition &, double *, double *) { throw std::runtime_error("FFTW not available"); } void FFTW::Plan::Execute(float *, float *) { throw std::runtime_error("FFTW not available"); } void FFTW::Plan::Execute(double *, double *) { throw std::runtime_error("FFTW not available"); } #endif FFTW::Plan::Plan(Plan&&) = default; FFTW::Plan::Plan(FFTWPlan* plan) : _plan(plan) { } FFTW::Plan::Plan(FFTWPlanf* plan) : _planf(plan) { } FFTW::Plan::~Plan() noexcept { } FFTW::Plan& FFTW::Plan::operator=(Plan&&) = default; } //# NAMESPACE CASACORE - END casacore-3.7.1/scimath/Mathematics/FFTW.h000066400000000000000000000122151476623553700201330ustar00rootroot00000000000000//# Copyright (C) 1993,1994,1995,1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_FFTW_H #define SCIMATH_FFTW_H #include #include #include #include #include namespace casacore { //# Forward Declarations. class FFTWPlan; class FFTWPlanf; // C++ interface to the FFTWw library // // // // This is a wrapper of FFTW3. // It is only active if FFTW3 was found during the build. // If not found, all functions won't do anything at all. // // The interface is such that the presence of FFTW3 is only visible // in the implementation. The header file does not need to know. // In this way external code using this class does not need to set HAVE_FFTW. // class FFTW { public: FFTW() ; ~FFTW() ; // overloaded interface to fftw[f]_plan... void plan_r2c(const IPosition &size, float *in, std::complex *out) ; void plan_r2c(const IPosition &size, double *in, std::complex *out) ; void plan_c2r(const IPosition &size, std::complex *in, float *out) ; void plan_c2r(const IPosition &size, std::complex *in, double *out) ; void plan_c2c_forward(const IPosition &size, std::complex *in) ; void plan_c2c_forward(const IPosition &size, std::complex *in) ; void plan_c2c_backward(const IPosition &size, std::complex *in) ; void plan_c2c_backward(const IPosition &size, std::complex *in) ; // TODO These overloads do not use their parameters at all. This should // be written to use an interface like plan_redft00(). // overloaded interface to fftw[f]_execute... void r2c(const IPosition &size, float *in, std::complex *out) ; void r2c(const IPosition &size, double *in, std::complex *out) ; void c2r(const IPosition &size, std::complex *in, float *out); void c2r(const IPosition &size, std::complex *in, double *out); void c2c(const IPosition &size, std::complex *in, bool forward); void c2c(const IPosition &size, std::complex *in, bool forward); class Plan { public: ~Plan() noexcept; Plan(const Plan&) = delete; Plan(Plan&&); Plan& operator=(const Plan&) = delete; Plan& operator=(Plan&&); // Perform the FFT associated with this plan with the given // in data, and store it in the given out data. // void Execute(float* in, float* out); void Execute(double* in, double* out); // private: friend FFTW; Plan(FFTWPlan* plan); Plan(FFTWPlanf* plan); std::unique_ptr _plan; std::unique_ptr _planf; }; static Plan plan_redft00(const IPosition &size, float *in, float *out); static Plan plan_redft00(const IPosition &size, double *in, double *out); private: static void initialize_fftw(); std::unique_ptr itsPlanR2Cf; std::unique_ptr itsPlanR2C; std::unique_ptr itsPlanC2Rf; std::unique_ptr itsPlanC2R; std::unique_ptr itsPlanC2CFf; // forward std::unique_ptr itsPlanC2CF; std::unique_ptr itsPlanC2CBf; // backward std::unique_ptr itsPlanC2CB; std::unique_ptr itsPlanR2Rf; std::unique_ptr itsPlanR2R; unsigned flags; static bool is_initialized_fftw; // FFTW needs initialization // only once per process, // not once per object // TODO this mutex does not make FFTW thread safe, because // planning an FFT with FFTW is not thread safe either. // So either the plan..() methods should take the mutex, or // FFTW should leave synchronization fully to the user of // this class: currently it's halfway in between. static std::mutex theirMutex; // Initialization mutex }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/GaussianBeam.cc000066400000000000000000000214341476623553700220650ustar00rootroot00000000000000//# Copyright (C) 1995,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { const GaussianBeam GaussianBeam::NULL_BEAM = GaussianBeam(); GaussianBeam::GaussianBeam() : _major(Quantity(0, "arcsec")), _minor(Quantity(0, "arcsec")), _pa(Quantity(0, "deg")) { } GaussianBeam::GaussianBeam( const Quantity& major, const Quantity& minor, const Quantity& pa ) { setMajorMinor(major, minor); setPA(pa); } GaussianBeam::GaussianBeam( const Vector& parms ) { if (parms.size() != 3) { throw AipsError( "GaussianBeam(const Vector& parms): parms must have exactly three elements" ); } setMajorMinor(parms[0], parms[1]); setPA(parms[2]); } GaussianBeam::GaussianBeam(const GaussianBeam& other) : _major(other._major), _minor(other._minor), _pa(other._pa) {} GaussianBeam::~GaussianBeam() {} GaussianBeam& GaussianBeam::operator=(const GaussianBeam& other) { if (this != &other) { _major = other._major; _minor = other._minor; _pa = other._pa; } return *this; } Bool GaussianBeam::operator==(const GaussianBeam& other) const { return _major == other._major && _minor == other._minor && _pa == other._pa; /* return _major.getValue("rad") == other._major.getValue("rad") && _minor.getValue("rad") == other._minor.getValue("rad") && getPA(True).getValue("rad") == other.getPA(True).getValue("rad"); */ } Bool GaussianBeam::operator!=(const GaussianBeam& other) const { return ! operator==(other); } const Quantity& GaussianBeam::getMajor() const { return _major; } Double GaussianBeam::getMajor(const Unit& u) const { return _major.getValue(u); } const Quantity& GaussianBeam::getMinor() const { return _minor; } Double GaussianBeam::getMinor(const Unit& u) const { return _minor.getValue(u); } Quantity GaussianBeam::getPA(const Bool unwrap) const { if (unwrap) { return _unwrap(_pa); } return _pa; } Double GaussianBeam::getPA(const Unit& u, const Bool unwrap) const { return getPA(unwrap).getValue(u); } Quantity GaussianBeam::_unwrap(const Quantity& pa) { if (pa > QC::qTurn( ) || pa <= -QC::qTurn( )) { Quantity upa((fmod(pa.getValue("deg"), 180)), "deg"); if (upa > QC::qTurn( )) { upa -= QC::hTurn( ); } else if (upa <= -QC::qTurn( )) { upa += QC::hTurn( ); } upa.convert(pa.getUnit()); return upa; } return pa; } void GaussianBeam::setMajorMinor( const Quantity& majAx, const Quantity& minAx ) { static ostringstream oss; auto majVal = majAx.getValue(); auto minVal = minAx.getValue(); ThrowIf( isInf(majVal) || isInf(minVal) || isNaN(majVal) || isNaN(minVal), "Neither the major nor the minor axis value is permitted " "to be infinity or NaN" ); ThrowIf( majVal < 0, "Major axis cannot be less than zero." ); ThrowIf( minVal < 0, "Minor axis cannot be less than zero." ); ThrowIf ( ! majAx.isConform("rad"), "Major axis must have angular units (" + majAx.getUnit() + " is not)." ); ThrowIf ( ! minAx.isConform("rad"), "Major axis must have angular units (" + minAx.getUnit() + " is not)." ); ThrowIf( majAx < minAx, "Major axis must be greater or equal to minor axis" ); _major = majAx; _minor = minAx; } void GaussianBeam::setPA(const Quantity& pa, Bool unwrap) { auto paVal = pa.getValue(); ThrowIf( isInf(paVal) || isNaN(paVal), "The position angle value is not permitted to be infinity or NaN" ); ThrowIf( ! pa.isConform("rad"), "Position angle must have angular units (" + pa.getUnit() + " is not)." ); _pa = unwrap ? _unwrap(pa) : pa; } Bool GaussianBeam::isNull() const { return _major.getValue() == 0 || _minor.getValue() == 0; } Double GaussianBeam::getArea(const Unit& unit) const { // NOTE we never want to return a Qauntity because of the // nonstandard handling of solid angle units in CASA Quantity qunit(1, unit); if (qunit.isConform("sr") || qunit.isConform("rad2")) { static const Double coeff = M_PI/(4.0*M_LN2); return coeff * (_major * _minor).getValue(unit); } else { ostringstream oss; oss << className() << "::" << __FUNCTION__ << ": Unit " << unit.getName() << " is not a solid angle."; throw AipsError(oss.str()); } } const String& GaussianBeam::className() { static const String c = "GaussianBeam"; return c; } Record GaussianBeam::toRecord() const { Record outRec; QuantumHolder qh(_major); Record tmp; String error; // there's no need for error checking, this object holds bona-fide quantities. qh.toRecord(error, tmp); outRec.defineRecord("major", tmp); qh = QuantumHolder(_minor); qh.toRecord(error, tmp); outRec.defineRecord("minor", tmp); qh = QuantumHolder(_pa); qh.toRecord(error, tmp); outRec.defineRecord("positionangle", tmp); return outRec; } GaussianBeam GaussianBeam::fromRecord( const Record& rec ) { if (rec.nfields() != 3) { throw AipsError("Beam record does not contain 3 fields"); } QuantumHolder qh; if (! rec.isDefined("major")) { throw AipsError("Field major missing from restoring beam record"); } const RecordInterface& subRec0 = rec.asRecord("major"); String error; if (! qh.fromRecord(error, subRec0)) { throw AipsError(error); } Quantity major = qh.asQuantumDouble(); if (! rec.isDefined("minor")) { throw AipsError("Field minor missing from restoring beam record"); } const RecordInterface& subRec1 = rec.asRecord("minor"); if (! qh.fromRecord(error, subRec1)) { throw AipsError(error); } Quantity minor = qh.asQuantumDouble(); if (! rec.isDefined("positionangle")) { throw AipsError("Field positionangle missing from restoring beam record"); } const RecordInterface& subRec2 = rec.asRecord("positionangle"); if (! qh.fromRecord(error, subRec2)) { throw AipsError(error); } Quantity pa = qh.asQuantumDouble(); return GaussianBeam(major, minor, pa); } ostream &operator<<(ostream &os, const GaussianBeam& beam) { os << "major: " << beam.getMajor() << ", minor: " << beam.getMinor() << ", pa: " << beam.getPA(True); return os; } LogIO &operator<<(LogIO &os, const GaussianBeam& beam) { ostringstream oss; oss << beam; os << oss.str(); return os; } Vector GaussianBeam::toVector(const Bool unwrap) const { Vector beam(3); beam[0] = _major; beam[1] = _minor; beam[2] = unwrap ? getPA(True) : _pa; return beam; } void GaussianBeam::convert( const String& majUnit, const String& minUnit, const String& paUnit ) { _major.convert(majUnit); _minor.convert(minUnit); _pa.convert(paUnit); } Bool near( const GaussianBeam& left, const GaussianBeam& other, const Double relWidthTol, const Quantity& absPATol ) { if (! absPATol.isConform("rad")) { throw AipsError( "GaussianBeam::near(): absPATol does not have angular units" ); } return casacore::near(left.getMajor(), other.getMajor(), relWidthTol) && casacore::near(left.getMinor(), other.getMinor(), relWidthTol) && casacore::nearAbs(left.getPA(True), other.getPA(True), absPATol); } } casacore-3.7.1/scimath/Mathematics/GaussianBeam.h000066400000000000000000000122731476623553700217300ustar00rootroot00000000000000//# Copyright (C) 1996,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_GAUSSIANBEAM_H #define SCIMATH_GAUSSIANBEAM_H #include #include namespace casacore { // // Represents a Gaussian restoring beam associated with an image. // // // // // // // // A Gaussian Beam. // // // This class represents a Gaussian restoring beam associated with // a deconvolved image. // // // // // // Restoring beams are used many places in image analysis tasks. // // // class GaussianBeam { public: static const GaussianBeam NULL_BEAM; // create a beam with all quantities zero (a null beam). GaussianBeam(); // Construct a beam from a set of Quantities. If minor > major // an exception is thrown. If any units are not angular, an // exception is thrown GaussianBeam( const Quantity& major, const Quantity& minor, const Quantity& pa ); // Construct a beam from a 3-Vector of Quantities representing // the major axis, the minor axis and the position angle (in that order). // If parms[1] > parms[0] (minor axis > major axis), // an exception is thrown. If any units are not angular, an // exception is thrown GaussianBeam( const Vector& parms ); GaussianBeam(const GaussianBeam& other); ~GaussianBeam(); GaussianBeam& operator=(const GaussianBeam& other); Bool operator==(const GaussianBeam& other) const; Bool operator!=(const GaussianBeam& other) const; // returns the major axis in the same units as it had at construction const Quantity& getMajor() const; // returns the value portion of the major axis in the specified units Double getMajor(const Unit& u) const; // returns the minor axis in the same units as it had at construction const Quantity& getMinor() const; // returns the value portion of the minor axis in the specified units Double getMinor(const Unit& u) const; // returns the position angle's value as it was at construction, // unless unwrap is True, in which case the value of the angle // returned will be between -90 and 90 degrees (but with unit the same // as it had when this object was constructed). Quantity getPA(const Bool unwrap=True) const; // returns the value portion of the position angle in the specified units Double getPA(const Unit& u, const Bool unwrap=True) const; // returns the beam area in the specified unit, which much conform to // solid angle units. Double getArea(const Unit& unit) const; // is this object a null beam (ie is either its major and/or minor axis zero)? Bool isNull() const; // returns GassianBeam. static const String& className(); Record toRecord() const; void setMajorMinor(const Quantity& majAx, const Quantity& minAx); // if unwrap=True, unwrap pa so its value lies in the range // -90 to 90 degrees before setting it. void setPA(const Quantity& pa, Bool unwrap=False); static GaussianBeam fromRecord(const Record& rec); // convert this object to a three-Vector of (major FWHM, minor FWHM, and pa). // If unwrap is True, the returned pa will fall between -90 and +90 // degrees. Vector toVector(const Bool unwrap=True) const; // convert stored Quantities to the specified units void convert(const String& majUnit, const String& minUnit, const String& paUnit); protected: Quantity _major, _minor, _pa; private: static Quantity _unwrap(const Quantity& pa); }; ostream &operator<<(ostream &os, const GaussianBeam& beam); LogIO &operator<<(LogIO &os, const GaussianBeam& beam); Bool near(const GaussianBeam& left, const GaussianBeam& other, const Double relWidthTol, const Quantity& absPaTol); } //# end namespace #endif casacore-3.7.1/scimath/Mathematics/Geometry.cc000066400000000000000000000052371476623553700213240ustar00rootroot00000000000000//# Copyright (C) 2010 by ESO (in the framework of the ALMA collaboration) //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // #include #include namespace casacore { std::pair Geometry::rotate2D( Double x, Double y, const Quantity& theta ) { Double thetaRad = theta.getValue("rad"); Double c = cos(thetaRad); Double s = sin(thetaRad); return std::make_pair(x*c - y*s, x*s + y*c); } Bool Geometry::doLineSegmentsIntersect( Double a0x, Double a0y, Double a1x, Double a1y, Double b0x, Double b0y, Double b1x, Double b1y ) { Vector line0point0(2); line0point0[0] = a0x; line0point0[1] = a0y; Vector line0point1(2); line0point1[0] = a1x; line0point1[1] = a1y; Vector line1point0(2); line1point0[0] = b0x; line1point0[1] = b0y; Vector line1point1(2); line1point1[0] = b1x; line1point1[1] = b1y; Vector p = line0point0; Vector r = line0point1 - line0point0; Vector q = line1point0; Vector s = line1point1 - line1point0; Double rCrossS = crossProduct2D(r, s); Vector diffQP = q-p; if (rCrossS == 0) { if (crossProduct2D(diffQP, r) == 0) { // lines are coincident return True; } else { // lines are parallel return False; } } Double t = crossProduct2D(diffQP, s)/rCrossS; Double u = crossProduct2D(diffQP, r)/rCrossS; return t >= 0 && t <= 1 && u >= 0 && u <= 1; } } //# NAMESPACE CASACORE - END casacore-3.7.1/scimath/Mathematics/Geometry.h000066400000000000000000000053521476623553700211640ustar00rootroot00000000000000//# Copyright (C) 2010 by ESO (in the framework of the ALMA collaboration) //# Copyright (C) 1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_GEOMETRY_H #define SCIMATH_GEOMETRY_H #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Geometry related methods. // // //# Dave Mehringer // // // // self-explanatory // // // Various geometry related functions // // // Commonly used geometrical functions for various image and region // applications. // class Geometry { public: // Get result of rotating a 2-D vector counterclockwise through an angle // theta. The output pair will have x as the first // member and y as the second. theta must have angular units (no explicit // checking is done, but an exception will be thrown from the Quanta code if not). static std::pair rotate2D( Double x, Double y, const Quantity& theta ); // Determine if two coplanar line segments, a and b, intersect. Line segment a // has end points a0 and a1, and line segment b has endpoints b0 and b1. // Algorithm from // http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect static Bool doLineSegmentsIntersect( Double a0x, Double a0y, Double a1x, Double a1y, Double b0x, Double b0y, Double b1x, Double b1y ); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/Gridder.h000066400000000000000000000066741476623553700207610ustar00rootroot00000000000000//# Gridder.h: Definition for Gridder //# Copyright (C) 1996,1997,1999,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_GRIDDER_H #define SCIMATH_GRIDDER_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class IPosition; // // A base class for gridding // template class Gridder { public: Gridder(); Gridder(const IPosition& shape, const Vector& scale, const Vector& offset); virtual ~Gridder(); virtual Bool grid(Array&, const Vector& position, const Range& value) = 0; virtual Bool degrid(const Array&, const Vector& position, Range& value) = 0; virtual Range correct(const IPosition& loc); // Return a correction vector in x for loc y virtual void correctX1D(Vector& factor, const Int locy); Vector& location(Vector& loc, const Vector& pos); Vector& position(Vector& gpos, const Vector& pos); virtual Bool onGrid(const Vector& loc); virtual Bool onGrid(const Vector& loc, const Vector& delta); virtual Bool onGrid(const Vector& pos); void setOffset(const Vector& off); void setOffset(const IPosition& off); protected: Int nint(Double val) {return Int(std::floor(val+0.5));} virtual void fillCorrectionVectors(); // Correction factor for 1 dimension. This is virtual and // must be assigned appropriately for derived classes virtual Range correctionFactor1D(Int loc, Int len) = 0; Int ndim; IPosition shape; // Shape of array Vector scale; // Scaling from world to pixel Vector offset; // Scaling from world to pixel Vector posVec; // Scaled location Vector locVec; // Vector for location type quantities Vector shapeVec; // Vector for shape Vector zeroShapeVec; // Vector for zero shape Vector offsetVec; // Offset to be added to coordinates Vector centerVec; // IPosition for center Vector > correctionVectors; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Mathematics/Gridder.tcc000066400000000000000000000132131476623553700212660ustar00rootroot00000000000000//# Gridder.cc: Nearest Neighbour Gridder //# Copyright (C) 1996,1997,1999,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_GRIDDER_TCC #define SCIMATH_GRIDDER_TCC #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template Gridder::Gridder() {} template Gridder::~Gridder() {} template Gridder::Gridder(const IPosition& ishape, const Vector& iscale, const Vector& ioffset) { // Scaling from world to pixels scale=iscale; offset=ioffset; // Shape of array shape=ishape; ndim=shape.nelements(); offsetVec.resize(ndim); offsetVec=0; // Time-savers locVec.resize(ndim); shapeVec=shape.asVector(); zeroShapeVec.resize(ndim); zeroShapeVec=0; posVec.resize(ndim); } // Turn a Domain position into a grid location. This should move // to the nearest grid point (27.8->28, 28.2->28, -27.4->-27, etc) template Vector& Gridder::location(Vector& loc, const Vector& pos) { for (Int axis=0;axis Vector& Gridder::position(Vector& gpos, const Vector& pos) { for (Int axis=0;axis Bool Gridder::onGrid(const Vector& loc) { for (Int i=0;i=shapeVec(i)) return False; if(loc(i)<0) return False; } return True; } // Is the location (plus of minus deltas) on the grid? template Bool Gridder::onGrid(const Vector& loc, const Vector& delta) { for (Int i=0;i=shapeVec(i)) return False; if((loc(i)+delta(i))<0) return False; if((loc(i)-delta(i))>=shapeVec(i)) return False; if((loc(i)-delta(i))<0) return False; } return True; } // Is the position on the grid? template Bool Gridder::onGrid(const Vector& pos) { Int loc; for (Int i=0;i=shapeVec(i)) return False; if(loc<0) return False; } return True; } // Set the offset IP template void Gridder::setOffset(const Vector& off) { offsetVec=off; } template void Gridder::setOffset(const IPosition& off) { offsetVec=off.asVector(); } // Return correction factor. This is the value that // must be divided to get a correct flux. template Range Gridder::correct(const IPosition& loc) { Range factor=1.0; for (Int dim=0;dim void Gridder::correctX1D(Vector& factor, const Int locy) { factor=correctionVectors(0); Range yFactor; yFactor=correctionVectors(1)(locy); factor*=yFactor; } // Fill in the cache of corrections. This must be in the // constructor of the derived class. We use the virtual // method for the 1D case. template void Gridder::fillCorrectionVectors() { correctionVectors.resize(ndim); for (Int dim=0;dim1) { for (Int loc=0;loc #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // forward declarations: class String; // // //
      • module Arrays //
      • Arrays module // // // // Makes a histogram from input values. // // // // HistAcc stands for `Histogram Accumulator'. // // // //
      • The accepted input types are real, i.e. Int, uInt, Float, Double, // but not Complex. // // // Makes a histogram from input values. The histogram bin parameters // may be defined, or determined from the first n input values. // The input values are fed to HistAcc via the member function `put'. // They can be fed individually, or in the form of an Array. // // The histogram `bins' can be defined via the constructor in the // form of loop variables: low bin, high bin, bin-width. // It is also possible to let the bin parameters be determined // automatically from the first n (e.g. n=50) input values. // If the actual nr of input values is less than n when the histogram // is interrogated in some way, the bin parameters will be determined // from what is available. // // // // It is usually convenient to let the bins be defined automatically: // // Matrix vv(30,100); // an array of input values // vv = ... // fill the array // HistAcc h(25); // use the first 25 values to define bins // h.put(vv); // accumulate values into histogram // h.printHistogram(cout,"vv"); // print the histogram of vv // Fallible median = h1.getMedian(); // return the median // // // In some cases the bin parameters are pre-defined: // // Vector vv(100,0); // a vector (array) of values // vv = ... // fill the vector // HistAcc h(-10,20,3); // bins with width 3, between -10 and 20 // h.put(vv); // accumulate values into histogram // uInt n = h.getSpurious(l,h);// get the number outside the bins // // // The internal statistics accumulator can be interrogated explicitly // or implicitly: // // StatAcc s = h.getStatistics(); // return the internal StatAcc // Fallible mean = s.getMean(); // get the mean of the input values // Fallible mean = h.getStatistics().getMean(); // alternative // // // // // // // // // *************************************************************************** template class HistAcc { public: // Constructors and destructor. If the bin-parameters low, high // and width (for lowest and highest bin, and binwidth) are not // specified, they will be determined automatically from the // first nBuff input values (which are stored in a temporary buffer). // HistAcc(const uInt nBuff); //# fully automatic HistAcc(const uInt nBuff, const T width); //# semi-automatic HistAcc(const T low, const T high, const T width); //# fully specified HistAcc(const HistAcc&); //# copy an existing one ~HistAcc(){;} // // Copy operations. // void copy(const HistAcc&); //# idem HistAcc& operator= (const HistAcc&); // // Accumulate (put) value(s) into the histogram. // inline void put(const T v); //# single value void put(const Array& vv); //# array void put(const Block& vv); //# block (simple array) // // Reset the contents of the bins to zero, but retain the current // bin definition. void reset(); // Empty all bins whose contents is < nmin (e.g. nmin=2). // This is useful to remove `noise' values from the histogram. void emptyBinsWithLessThan(const uInt nmin); // The median is the 50-percentile (getPercentile(50)), i.e. the // value which has 50 percent of the input values below it. // Calculation takes into account the spurious // input values, i.e. values that fell outside the bins. Fallible getPercentile(const Float p); Fallible getMedian(); // All bins have the same width. Fallible getBinWidth() const; // Get the internal Statistics accumulator (see StatAcc,h). // It can be used to obtain statistics of the input values. const StatAcc& getStatistics(); // The return value is the nr of histogram bins, and is invalid // if the number is zero. The given blocks/vectors are resized, // and contain the contents and centre values of the bins. Fallible getHistogram(Block& bins, Block& values); // Get the nr of `spurious' values, i.e. the ones that fell // outside the defined bins. uInt getSpurious(uInt& tooSmall, uInt& tooLarge); // Print histogram. // void printHistogram(ostream&, const String& caption); // private: Block itsBinContents; //# Contents of histogram bins Block itsBinHighLimit; //# High limit of each bin T itsUserDefinedBinWidth; //# if defined StatAcc itsStatAcc; //# private Statistics Accumulator Bool itsAutoDefineMode; //# If true: automatic mode Block itsBuffer; //# temporary storage of input T-values uInt itsBufferContents; //# nr of T-values in buffer // Accumulate a single value into the histogram. void put1(const T); // Definition of histogram bins with given parameters. void defineBins(const T low, const T high, const T width); // Internal helper functions for the automatic definition of // histogram parameters, using the contents of itsBuffer. // void initBuffer(const uInt size); void putBuffer(const T v); //# add input value to itsBuffer void clearBuffer(); //# transfer from buffer to bins void autoDefineBins(); // // Other internal helper function(s). // void init(); Fallible getBinValue(const uInt index) const; //# bin centre value // }; //*************************** inline functions, have to be in HistAcc.h **** // Accumulate a single value: template inline void HistAcc::put(const T v) { put1(v); } } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Mathematics/HistAcc.tcc000066400000000000000000000362441476623553700212350ustar00rootroot00000000000000//# HistAcc.cc: Statistics Accumulator //# Copyright (C) 1996,1998,1999,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_HISTACC_TCC #define SCIMATH_HISTACC_TCC #include #include #include // #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Constructor: Fully automatic bin definition template HistAcc::HistAcc(const uInt n) { init(); // bring into a known state initBuffer(n); // initialise temporary buffer } // Constructor: Semi-automatic (give bin width only). template HistAcc::HistAcc(const uInt n, const T width) { init(); // bring into a known state itsUserDefinedBinWidth = width; // user-defined bin width initBuffer(n); // initialise temporary buffer } // Constructor: Define bins fully: template HistAcc::HistAcc(const T low, const T high, const T width) { init(); // bring into a known state defineBins(low,high,width); itsAutoDefineMode = False; } // Constructor: copy an existing histogram (that): template HistAcc::HistAcc(const HistAcc& that) { copy(that); } // Initialise the histogram definition, i.e. bring it into a // known state: template void HistAcc::init () { reset(); itsBinContents.resize(0); itsBinHighLimit.resize(0); itsUserDefinedBinWidth = 0; initBuffer(0); } // Reset, i.e. set the contents of the histogram bins to zero // (but do NOT change their definition!) template void HistAcc::reset () { itsStatAcc.reset(); for (uInt i=0; i void HistAcc::copy (const HistAcc& that) { itsStatAcc = that.itsStatAcc; itsUserDefinedBinWidth = that.itsUserDefinedBinWidth; uInt n = that.itsBinContents.nelements(); itsBinContents.resize(n); itsBinHighLimit.resize(n); uInt i; for (i=0; i HistAcc& HistAcc::operator= (const HistAcc& that) { copy(that); return *this; } //************************************************************************ // Initialise a temporary buffer to store input values from which the // bin parameters can be determined automatically: template void HistAcc::initBuffer (const uInt bufferLength) { if (bufferLength > 0) { itsAutoDefineMode = True; itsBuffer.resize(bufferLength); } else { itsAutoDefineMode = False; itsBuffer.resize(0); } itsBufferContents = 0; } // Put a value into the temporary buffer. // If the buffer is full, define the histogram bins. template void HistAcc::putBuffer (const T v) { if (itsBufferContents < itsBuffer.nelements()) { itsBuffer[itsBufferContents] = v; itsBufferContents++; } if (itsBufferContents >= itsBuffer.nelements()) { autoDefineBins(); // define bin parameters } } // Transfer the values from the buffer to the (defined!) bins, // and clear up the `automatic-mode' machinery. template void HistAcc::clearBuffer () { itsAutoDefineMode = False; // BEFORE put1! // Transfer values from buffer to histogram itsStatAcc.reset(); for (uInt i=0; i void HistAcc::autoDefineBins () { if (itsBufferContents == 0) { // no values in itsBuffer // ............? } else { // Calculate statistics of the values in the buffer itsStatAcc.reset(); // reset statistics itsStatAcc.put(itsBuffer); // accumulate buffer values // Calculate bin range from statistics: Double hw = itsStatAcc.getRms(); // w.r.t. mean hw *= 3; // 3 sigma? Double low = itsStatAcc.getMean();// lowest bin Double high = low; // highest bin low -= hw; // mean - 3 rms high += hw; // mean + 3 rms // Calculate bin width: T width = itsUserDefinedBinWidth; // use if defined Int k = 0; if (width <= 0) { // if not defined width = T((high - low)/11); // default: 11 bins? if (width <= 0) { // if still not OK width = 1; // ....? } else { // Truncate the width to decimal units (pretty): Double q = 10000000; Double q1; for (uInt i=0; i<15; i++) { q /= 10; q1 = q; if (width < q1) {q1 = q/2;} if (width < q1) {q1 = q/5;} if (width >= q1) { k = Int((width+q1/2)/q1); // truncate width = T(k * q1); break; // escape } } } } // Make bin centres multiples of the bin width (pretty): k = Int(high / width); // nr of multiples high = k * width; // adjust highest bin k = Int(low / width); // nr of multiples low = (k-1) * width; // adjust highest bin // Go ahead: defineBins(T(low),T(high),width); // define the bins clearBuffer(); // transfer values to bins } } // Define the bin parameters. Low and high represent the centres of the // lowest and highest bins. The first and last bins contain nr of // `spurious' values, i.e. values that are either too small or too large // for the defined bins. template void HistAcc::defineBins(const T low, const T high, const T width) { T v; uInt n = 0; for (v=low; v void HistAcc::put(const Array& v) { uInt ntotal = v.nelements(); Bool vDelete; const T* vStorage = v.getStorage(vDelete); const T* vs = vStorage; while (ntotal--) {put1(*vs++);} v.freeStorage(vStorage, vDelete); } // Accumulate a Block (simple vector) of values: template void HistAcc::put(const Block& v) { for (uInt i=0; i < v.nelements(); i++){ put1(v[i]); } } // Accumulate a single value into the histogram: template void HistAcc::put1(const T v) { if (itsAutoDefineMode) { putBuffer(v); // put into temporary buffer } else { // put into histogram bins itsStatAcc.put(v); // accumulate statistics (all values!) for (uInt i=0; i const StatAcc& HistAcc::getStatistics() { if (itsAutoDefineMode) { itsStatAcc.reset(); itsStatAcc.put(itsBuffer); } return itsStatAcc; } // Get the nr of spurious values: template uInt HistAcc::getSpurious(uInt& nlow, uInt& nhigh) { if (itsAutoDefineMode) { autoDefineBins(); } nlow = itsBinContents[0]; // tooSmall nhigh = itsBinContents[itsBinContents.nelements()-1]; // tooLarge return nlow + nhigh; // total spurious } // Result: get the actual bin-width. Use the difference in high limit // between the first two bins. template Fallible HistAcc::getBinWidth() const { T width = itsBinHighLimit[1] - itsBinHighLimit[0]; if (width <= 0) { return Fallible(); } else { return Fallible(width); } } // Result: get the Median (= the 50-percentile) template Fallible HistAcc::getMedian () { return getPercentile(50.0); } // Result: get the n-percentile, i.e. the value which has n% of the // input values below it. (the Median is the 50-percentile). template Fallible HistAcc::getPercentile (const Float p) { if (itsAutoDefineMode) { autoDefineBins(); } Double target = itsStatAcc.getWtot() * p/100; // target value uInt n1 = itsBinContents[0]; // spurious low if (n1 > target) { return Fallible(); // not defined } // Go through the regular bins, excuding spurious high for (uInt i=1; i target) { return getBinValue(i); // OK } } return Fallible(); // none of the regular bins } // Result: get the centre value for the bin with the given index. // For the bins for low and high `spurious' values, return a value // that is a full histogram-width away from the extreme bins. template Fallible HistAcc::getBinValue (const uInt index) const { if (getBinWidth().isValid()) { T width = getBinWidth(); if (index == 0) { return Fallible(itsBinHighLimit[0] - itsBinContents.nelements() * width); } else if (index < itsBinContents.nelements()-1) { // Regular bins. The strange construct of using -1+0.5 for -0.5 // is to get the correct result for integers. return Fallible(itsBinHighLimit[index] - width + width/2); } else if (index == itsBinContents.nelements()-1) { return Fallible(itsBinHighLimit[0] + itsBinContents.nelements() * width * 2); } else { return Fallible(); } } else { return Fallible(); } } // Result: get the Histogram itself in two Blocks (simple vectors) template Fallible HistAcc::getHistogram (Block& binContents, Block& binValues) { if (itsAutoDefineMode) { autoDefineBins(); } uInt n = itsBinContents.nelements(); // nr of bins if (n>0) { binContents.resize(n-2); binValues.resize(n-2); for (uInt i=1; i(n); } else { return Fallible(); // error } } //********************************************************************* // Remove bins with contents smaller than nmin. // Remove all the `spurious' values, assuming that they are so widely // spaced that their average density is less than one/bin. template void HistAcc::emptyBinsWithLessThan (const uInt nmin) { if (itsAutoDefineMode) { autoDefineBins(); } if (nmin > 1) { // Remove the spurious values: itsBinContents[0]=0; // low itsBinContents[itsBinContents.nelements()-1]=0; // high // Deal with the regular bins: itsStatAcc.reset(); // reset accumulator Float wgt; for (uInt i=1; i void HistAcc::printHistogram (ostream& os, const String& caption) { if (itsAutoDefineMode) {autoDefineBins();} ios::fmtflags flags = os.flags(); // save current setting os << " " << endl; // skip line os << " Histogram: " << caption << endl; uInt pv = 3; // precision for bin values uInt pc = 3; // precision for bin contents for (uInt i=1; i #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Interpolate2D::Interpolate2D(Interpolate2D::Method method) { // Set up function pointers to correct method if (method==Interpolate2D::LINEAR) { itsFuncPtrFloat = &Interpolate2D::interpLinear; itsFuncPtrDouble = &Interpolate2D::interpLinear; itsFuncPtrBool = &Interpolate2D::interpLinearBool; } else if (method==Interpolate2D::CUBIC) { itsFuncPtrFloat = &Interpolate2D::interpCubic; itsFuncPtrDouble = &Interpolate2D::interpCubic; itsFuncPtrBool = &Interpolate2D::interpCubicBool; } else if (method==Interpolate2D::NEAREST) { itsFuncPtrFloat = &Interpolate2D::interpNearest; itsFuncPtrDouble = &Interpolate2D::interpNearest; itsFuncPtrBool = &Interpolate2D::interpNearestBool; } else if (method==Interpolate2D::LANCZOS) { itsFuncPtrFloat = &Interpolate2D::interpLanczos; itsFuncPtrDouble = &Interpolate2D::interpLanczos; itsFuncPtrBool = &Interpolate2D::interpLanczosBool; } } Interpolate2D::Interpolate2D(const Interpolate2D &other) : itsFuncPtrFloat (other.itsFuncPtrFloat), itsFuncPtrDouble(other.itsFuncPtrDouble), itsFuncPtrBool (other.itsFuncPtrBool) {} Interpolate2D::~Interpolate2D() {} Interpolate2D &Interpolate2D::operator=(const Interpolate2D &other) { itsFuncPtrFloat = other.itsFuncPtrFloat; itsFuncPtrDouble = other.itsFuncPtrDouble; itsFuncPtrBool = other.itsFuncPtrBool; return *this; } // Float Versions Bool Interpolate2D::interp(Float &result, const Vector &where, const Matrix &data) const { const Matrix* maskPtr(0); return ((*this).*itsFuncPtrFloat)(result, where, data, maskPtr); } Bool Interpolate2D::interp(Float &result, const Vector &where, const Matrix &data, const Matrix &mask) const { const Matrix* maskPtr = &mask; return ((*this).*itsFuncPtrFloat)(result, where, data, maskPtr); } // Double versions Bool Interpolate2D::interp(Double &result, const Vector &where, const Matrix &data) const { const Matrix* maskPtr(0); return ((*this).*itsFuncPtrDouble)(result, where, data, maskPtr); } Bool Interpolate2D::interp(Double &result, const Vector &where, const Matrix &data, const Matrix &mask) const { const Matrix* maskPtr = &mask; return ((*this).*itsFuncPtrDouble)(result, where, data, maskPtr); } // Complex versions Bool Interpolate2D::interp( Complex &result, const Vector &where, const Matrix &data ) const { Float realRes, imagRes; Matrix realData = (Matrix)real(data); Matrix imagData = (Matrix)imag(data); const Matrix* maskPtr(0); Bool realFunc = ((*this).*itsFuncPtrFloat)(realRes, where, realData, maskPtr); if (! realFunc) { return False; } Bool imagFunc = ((*this).*itsFuncPtrFloat)(imagRes, where, imagData, maskPtr); if (! imagFunc) { return False; } result = Complex(realRes, imagRes); return True; } Bool Interpolate2D::interp( Complex &result, const Vector &where, const Matrix &data, const Matrix &mask ) const { Float realRes, imagRes; Matrix realData = (Matrix)real(data); Matrix imagData = (Matrix)imag(data); const Matrix* maskPtr = &mask; Bool realFunc = ((*this).*itsFuncPtrFloat)(realRes, where, realData, maskPtr); if (! realFunc) { return False; } Bool imagFunc = ((*this).*itsFuncPtrFloat)(imagRes, where, imagData, maskPtr); if (! imagFunc) { return False; } result = Complex(realRes, imagRes); return True; } // DComplex versions Bool Interpolate2D::interp( DComplex &result, const Vector &where, const Matrix &data ) const { Double realRes, imagRes; Matrix realData = (Matrix)real(data); Matrix imagData = (Matrix)imag(data); const Matrix* maskPtr(0); Bool realFunc = ((*this).*itsFuncPtrDouble)(realRes, where, realData, maskPtr); if (! realFunc) { return False; } Bool imagFunc = ((*this).*itsFuncPtrDouble)(imagRes, where, imagData, maskPtr); if (! imagFunc) { return False; } result = DComplex(realRes, imagRes); return True; } Bool Interpolate2D::interp( DComplex &result, const Vector &where, const Matrix &data, const Matrix &mask ) const { Double realRes, imagRes; Matrix realData = (Matrix)real(data); Matrix imagData = (Matrix)imag(data); const Matrix* maskPtr = &mask; Bool realFunc = ((*this).*itsFuncPtrDouble)(realRes, where, realData, maskPtr); if (! realFunc) { return False; } Bool imagFunc = ((*this).*itsFuncPtrDouble)(imagRes, where, imagData, maskPtr); if (! imagFunc) { return False; } result = DComplex(realRes, imagRes); return True; } // Double version with two identicals and mask Bool Interpolate2D::interp(Double &resultI, Double &resultJ, const Vector &where, const Matrix &dataI, const Matrix &dataJ, const Matrix &mask) const { return interpLinear2(resultI, resultJ, where, dataI, dataJ, mask); } // Bool versions Bool Interpolate2D::interp(Bool &result, const Vector &where, const Matrix &data) const { return ((*this).*itsFuncPtrBool)(result, where, data); } // Private functions Bool Interpolate2D::interpNearestBool(Bool &result, const Vector &where, const Matrix &data) const { AlwaysAssert(where.nelements()==2, AipsError); const IPosition &shape = data.shape(); // Find nearest pixel; (i,j) = centre Int i = Int(where[0]+0.5); Int j = Int(where[1]+0.5); Bool ok = False; if (i >= 0 && i <= shape(0)-1 && j >= 0 && j <= shape(1)-1) { result = data(i,j); ok = True; } // return ok; } Bool Interpolate2D::interpLinearBool(Bool &result, const Vector &where, const Matrix &data) const { AlwaysAssert(where.nelements()==2, AipsError); const IPosition &shape = data.shape(); // Find nearest pixel; (i,j) = centre Int i = Int(where[0]+0.5); Int j = Int(where[1]+0.5); // Handle edge. Just move start left/down by one, if (i==shape(0)-1) i--; if (j==shape(1)-1) j--; // 2x2 starting from [i,j] Bool ok = False; if (i >= 0 && i+1 <= shape(0)-1 && j >= 0 && j+1 <= shape(1)-1) { result = !(!data(i,j) || !data(i+1,j) || !data(i,j+1) || !data(i+1,j+1)); ok = True; } // return ok; } Bool Interpolate2D::interpCubicBool(Bool &result, const Vector &where, const Matrix &data) const { // // bi-cubic interpolation // AlwaysAssert(where.nelements()==2, AipsError); const IPosition &shape = data.shape(); // Find nearest pixel; (i,j) = centre Int i = Int(where[0]+0.5); Int j = Int(where[1]+0.5); // Interpolation grid is 4x4 : [i-1,j-1] -> [i+2,j+2] // Handle edge (and beyond) by using linear. if (i<=0 || i>=shape(0)-2 || j<=0 || j>=shape(1)-2) { return interpLinearBool(result, where, data); } // const Matrix* p = &data; result = !(anyBadMaskPixels(p, i-1, i+2, j-1, j+2)); return True; } Bool Interpolate2D::interpLanczosBool(Bool &/*result*/, const Vector &/*where*/, const Matrix &/*data*/) const { throw(AipsError("Interpolate2D::interpLanczosBool() is not implemented")); } void Interpolate2D::bcucof (Double c[4][4], const Double y[4], const Double y1[4], const Double y2[4], const Double y12[4]) const { // // Numerical recipes 3.6 (p99) // static const Double wt[16][16] = { {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0}, {-3,0,0,3,0,0,0,0,-2,0,0,-1,0,0,0,0}, {2,0,0,-2,0,0,0,0,1,0,0,1,0,0,0,0}, {0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0}, {0,0,0,0,-3,0,0,3,0,0,0,0,-2,0,0,-1}, {0,0,0,0,2,0,0,-2,0,0,0,0,1,0,0,1}, {-3,3,0,0,-2,-1,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,-3,3,0,0,-2,-1,0,0}, {9,-9,9,-9,6,3,-3,-6,6,-6,-3,3,4,2,1,2}, {-6,6,-6,6,-4,-2,2,4,-3,3,3,-3,-2,-1,-1,-2}, {2,-2,0,0,1,1,0,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,2,-2,0,0,1,1,0,0}, {-6,6,-6,6,-3,-3,3,3,-4,4,2,-2,-2,-2,-1,-1}, {4,-4,4,-4,2,2,-2,-2,2,-2,-2,2,1,1,1,1} }; static Double X[16], CL[16]; // Pack temporary for (uInt i=0; i<4; ++i) { X[i] = y[i]; X[i+4] = y1[i]; X[i+8] = y2[i]; X[i+12] = y12[i]; } // Matrix multiply the stored table for (uInt i=0; i<16; ++i) { CL[i] = 0.0; for (uInt k=0; k<16; ++k) CL[i] += wt[i][k] * X[k]; } // Unpack the result into the output table for (uInt i=0, l=0; i<4; ++i) for (uInt j=0; j<4; ++j) c[i][j] = CL[l++]; } Interpolate2D::Method Interpolate2D::stringToMethod (const String &method) { String typeU = method; typeU.upcase(); String tmp = String(typeU.at(0, 1)); Interpolate2D::Method method2; if (tmp==String("N")) { method2 = Interpolate2D::NEAREST; } else if (tmp==String("L")) { String tmp2 = String(typeU.at(1, 1)); if (tmp2==String("A")) { method2 = Interpolate2D::LANCZOS; } else { method2 = Interpolate2D::LINEAR; } } else if (tmp==String("C")) { method2 = Interpolate2D::CUBIC; } else if (tmp==String("Z")) { method2 = Interpolate2D::LANCZOS; } else { throw AipsError("Unknown interpolation method " + method); } return method2; } Bool Interpolate2D::anyBadMaskPixels (const Matrix* &maskPtr, Int i1, Int i2, Int j1, Int j2) const { if (maskPtr) { for (Int j=j1; j<=j2; ++j) for (Int i=i1; i<=i2; ++i) if (!(*maskPtr)(i,j)) return True; } return False; } } //# NAMESPACE CASACORE - END casacore-3.7.1/scimath/Mathematics/Interpolate2D.h000066400000000000000000000227561476623553700220540ustar00rootroot00000000000000//# Interpolate2D.h: this defines the Interpolate2D class //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_INTERPOLATE2D_H #define SCIMATH_INTERPOLATE2D_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations class String; // // A two dimension interpolator for Matrices or Arrays // // // // // //
      • Arrays // // // // This class is called Interpolate2D because it does 2 dimensional interpolations // // // // Given a regular Array or Matrix and a vector of pixel // coordinates, interpolate the values of that array/matrix onto those // pixel coordinates. // // Absolutely no checking of the consistency of the input data // is done in order to preserve maximum speed. The coordinate vector // *must* have at least 2 elements (others will be ignored). If // you supply data and mask, those arrays *must* be the same shape. // Failure to follow these rules will result in your program // crashing. // // // // // // Matrix matt(10,10); // Vector where(2); // where(0) = 3.452; where(1) = 6.1; // Interpolate2D myInterp(Interpolate2D::LINEAR); // Float result; // Bool ok = myInterp.interp(result, where, matt); // // // // // // 2-D interpolation is required in geometry transformation routines // such as in ImageRegrid. // // // // //
      • Alternative approach: instantiate with an Array, take a block of // vector locations, return a block of interpolation results // class Interpolate2D { public: enum Method { // Nearest neighbour NEAREST, // Bilinear LINEAR, // Bicubic CUBIC, // Lanczos LANCZOS}; // Constructor Interpolate2D(Interpolate2D::Method method=Interpolate2D::LINEAR); // Copy constructor (copy semantics) Interpolate2D(const Interpolate2D &other); // destructor ~Interpolate2D(); // Assignment operator (copy semantics) Interpolate2D &operator=(const Interpolate2D &other); // Do one Float interpolation, supply Matrix and mask (True is good), // and pixel coordinate. Returns False if coordinate out of range or data // are masked. No shape integrity checking is done (see above). // Bool interp (Float &result, const Vector &where, const Matrix &data) const; Bool interp (Float &result, const Vector &where, const Matrix &data, const Matrix &mask) const; // // Do one Double interpolation, supply Matrix/Array and mask (True is good), // and pixel coordinate. Returns False if coordinate out of range or data // are masked. No shape integrity checking is done (see above). // Bool interp (Double &result, const Vector &where, const Matrix &data) const; Bool interp (Double &result, const Vector &where, const Matrix &data, const Matrix &mask) const; // // Do one Complex interpolation, supply Matrix/Array and mask (True is good), // and pixel coordinate. Returns False if coordinate out of range or data // are masked. No shape integrity checking is done (see above). The real // and imaginary parts are treated independently (see CAS-11375). // Bool interp (Complex &result, const Vector &where, const Matrix &data) const; Bool interp (Complex &result, const Vector &where, const Matrix &data, const Matrix &mask) const; // // Do one DComplex interpolation, supply Matrix/Array and mask (True is good), // and pixel coordinate. Returns False if coordinate out of range or data // are masked. No shape integrity checking is done (see above). The real // and imaginary parts are treated independently (see CAS-11375). // Bool interp (DComplex &result, const Vector &where, const Matrix &data) const; Bool interp (DComplex &result, const Vector &where, const Matrix &data, const Matrix &mask) const; // // Do two linear interpolations simultaneously. The second call is direct. // The first call transfers to the second call. It is assumed that the // structure (shape, steps) of the mask and data files are the same. // Bool interp(Double &resultI, Double &resultJ, const Vector &where, const Matrix &dataI, const Matrix &dataJ, const Matrix &mask) const; template Bool interpLinear2(T &resultI, T &resultJ, const Vector &where, const Matrix &dataI, const Matrix &dataJ, const Matrix &mask) const; // // Do one interpolation, supply boolean Matrix (True is good), // and pixel coordinate. Returns False if coordinate // out of range. The result is False if any data value in the interpolation // grid are False (bad), else True. No shape integrity checking is done. // Bool interp (Bool &result, const Vector &where, const Matrix &data) const; // // Convert string ("nearest", "linear", "cubic", "lanczos") to interpolation // method. The match is case insensitive. static Interpolate2D::Method stringToMethod(const String &method); private: // Are any of the mask pixels bad ? Returns False if no mask. Bool anyBadMaskPixels (const Matrix* &mask, Int i1, Int i2, Int j1, Int j2) const; // nearest neighbour interpolation template Bool interpNearest(T &result, const Vector &where, const Matrix &data, const Matrix* &maskPtr) const; Bool interpNearestBool(Bool &result, const Vector &where, const Matrix &data) const; // bi-linear interpolation template Bool interpLinear(T &result, const Vector &where, const Matrix &data, const Matrix* &maskPtr) const; Bool interpLinearBool(Bool &result, const Vector &where, const Matrix &data) const; // bi-cubic interpolation template Bool interpCubic(T &result, const Vector &where, const Matrix &data, const Matrix* &maskPtr) const; Bool interpCubicBool(Bool &result, const Vector &where, const Matrix &data) const; // Lanczos interpolation template Bool interpLanczos(T &result, const Vector &where, const Matrix &data, const Matrix* &maskPtr) const; Bool interpLanczosBool(Bool &result, const Vector &where, const Matrix &data) const; // Lanczos interpolation: helper functions template T sinc(const T x) const; template T L(const T x, const Int a) const; // helping routine from numerical recipes void bcucof (Double c[4][4], const Double y[4], const Double y1[4], const Double y2[4], const Double y12[4]) const; // Typedefs for function pointers typedef Bool(Interpolate2D::*FuncPtrFloat) (Float &result, const Vector &where, const Matrix &data, const Matrix* &maskPtr) const; typedef Bool(Interpolate2D::*FuncPtrDouble) (Double &result, const Vector &where, const Matrix &data, const Matrix* &maskPtr) const; typedef Bool(Interpolate2D::*FuncPtrBool) (Bool &result, const Vector &where, const Matrix &data) const; // FuncPtrFloat itsFuncPtrFloat; FuncPtrDouble itsFuncPtrDouble; FuncPtrBool itsFuncPtrBool; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Mathematics/Interpolate2D2.tcc000066400000000000000000000220331476623553700224440ustar00rootroot00000000000000//# Interpolate2D2.cc: this implements Interpolate2D templates //# Copyright (C) 2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_INTERPOLATE2D2_TCC #define SCIMATH_INTERPOLATE2D2_TCC #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template Bool Interpolate2D::interpNearest(T &result, const Vector &where, const Matrix &data, const Matrix* &maskPtr) const { // definition of the 'neighborhood' of outer edge data elements. static const Double half= .5001; const IPosition &shape = data.shape(); Double imax = shape(0) - 1.; Double wi = where[0]; if(wi < 0. - half || wi > imax + half || imax < 0) return False; Double jmax = shape(1) - 1.; Double wj = where[1]; if(wj < 0 - half || wj > jmax + half || jmax < 0) return False; uInt i = (wi <= 0.)? 0 : (wi >= imax)? uInt(imax) : uInt(wi + .5); uInt j = (wj <= 0.)? 0 : (wj >= jmax)? uInt(jmax) : uInt(wj + .5); Bool dataValid = !maskPtr || (*maskPtr)(i,j); if (dataValid) result = data(i,j); return dataValid; } template Bool Interpolate2D::interpLinear(T &result, const Vector &where, const Matrix &data, const Matrix* &maskPtr) const { const IPosition &shape = data.shape(); // We find 4 points surrounding the one of interest. // Negatives will give big positive value for the uInt // and these will fail the shape test below. // Make sure we don't access i+1 or j+1 because the // big positive plus 1 may become 0 and then we will spuriously // pass the shape test uInt i = Int(where[0]); // Assuming Int does (1.2 -> 1) uInt j = Int(where[1]); uInt si = uInt(shape(0)-1); uInt sj = uInt(shape(1)-1); // Handle edge. Just move start left/down by one, if (i==si) --i; if (j==sj) --j; // 2x2 starting from [i,j] // mask==True is a good pixel if (i < si && j < sj) { if (maskPtr) { if (!(*maskPtr)(i,j) || !(*maskPtr)(i+1,j) || !(*maskPtr)(i,j+1) || !(*maskPtr)(i+1,j+1)) return False; } Double TT = where[0] - i; Double UU = where[1] - j; result = (1.0-TT)*(1.0-UU)*data(i,j) + TT*(1.0-UU)*data(i+1,j) + TT*UU*data(i+1,j+1) + (1.0-TT)*UU*data(i,j+1); return True; } else return False; } template Bool Interpolate2D::interpLinear2(T &resultI, T &resultJ, const Vector &where, const Matrix &dataI, const Matrix &dataJ, const Matrix &mask) const { const IPosition &shape = mask.shape(); // We find 4 points surrounding the one of interest. // Negatives will give big positive value for the uInt // and these will fail the shape test below. // Make sure we don't access i+1 or j+1 because the // big positive plus 1 may become 0 and then we will spuriously // pass the shape test uInt i = Int(where[0]); // Assuming Int does (1.2 -> 1) uInt j = Int(where[1]); uInt si = uInt(shape[0]-1); uInt sj = uInt(shape[1]-1); // Handle edge. Just move start left/down by one, if (i==si) --i; if (j==sj) --j; // 2x2 starting from [i,j] // mask==True is a good pixel if (i < si && j < sj) { uInt k0 = dataI.steps()[0]; uInt k1 = dataI.steps()[1]; const Bool *m = &mask(i,j); if ( !*m || !*(m+k0) || !*(m+k1) || !*(m+k0+k1)) return False; Double TT = where[0] - i; Double UU = where[1] - j; Double x1 = (1.0-TT); Double y1 = (1.0-UU); Double x = x1*y1; const T *dI = &dataI(i,j); const T *dJ = &dataJ(i,j); resultI = x * *dI; resultJ = x * *dJ; x = TT*y1; resultI += x * *(dI+k0); resultJ += x * *(dJ+k0); x = TT*UU;;; resultI += x * *(dI+k0+k1); resultJ += x * *(dJ+k0+k1); x = x1*UU;;; resultI += x * *(dI+k1); resultJ += x * *(dJ+k1); return True; } else return False; } template Bool Interpolate2D::interpCubic(T &result, const Vector &where, const Matrix &data, const Matrix* &maskPtr) const { // // bi-cubic interpolation // const IPosition &shape = data.shape(); // We find 4 points surrounding the one of interest. // Points are labelled: // // 1 2 // 0 3 // // where point 0 is [i,j]. // // we use points in a 4 x 4 grid in total (to get derivatives) // [i-1,j-1] -> [i+2,j+2] Int i = Int(where[0]); Int j = Int(where[1]); // Handle edge (and beyond) by using linear. if (i<=0 || i>=shape[0]-2 || j<=0 || j>=shape[1]-2) { return interpLinear(result, where, data, maskPtr); } // Handle mask if (anyBadMaskPixels(maskPtr, i-1, i+2, j-1, j+2)) return False; // Do it Double TT = where[0] - i; Double UU = where[1] - j; Double itsY[4]; Double itsY1[4]; Double itsY2[4]; Double itsY12[4]; Double itsC[4][4]; // // define values of function and its derivatives on the // square of points bounding "where" itsY[0] = data(i, j); itsY[1] = data(i+1,j); itsY[2] = data(i+1,j+1); itsY[3] = data(i, j+1); // x-derivatives (points 0->3) itsY1[0] = data(i+1, j) - data(i-1, j); itsY1[1] = data(i+2, j) - data(i, j); itsY1[2] = data(i+2, j+1) - data(i, j+1); itsY1[3] = data(i+1, j+1) - data(i-1, j+1); // y-derivatives (points 0->3) itsY2[0] = data(i, j+1) - data(i, j-1); itsY2[1] = data(i+1, j+1) - data(i+1, j-1); itsY2[2] = data(i+1, j+2) - data(i+1, j); itsY2[3] = data(i, j+2) - data(i, j); // cross derivatives (points 0->3) itsY12[0] = data(i+1, j+1) + data(i-1, j-1) - data(i-1, j+1) - data(i+1, j-1); itsY12[1] = data(i+2, j+1) + data(i, j-1) - data(i, j+1) - data(i+2, j-1); itsY12[2] = data(i+2, j+2) + data(i, j) - data(i, j+2) - data(i+2, j); itsY12[3] = data(i+1, j+2) + data(i-1, j) - data(i-1, j+2) - data(i+1, j); for (uInt i=0; i<4; ++i) { itsY1[i] /= 2.0; itsY2[i] /= 2.0; itsY12[i] /= 4.0; } // Get result bcucof(itsC, itsY, itsY1, itsY2, itsY12); result = 0.0; for (Int i=3; i>=0; --i) { result = TT*result + ((itsC[i][3]*UU + itsC[i][2])*UU + itsC[i][1])*UU + itsC[i][0]; } // return True; } template Bool Interpolate2D::interpLanczos(T &result, const Vector &where, const Matrix &data, const Matrix* &maskPtr) const { // // Lanczos 2D interpolation // // Hardcoded kernel size const Double a = 3; const IPosition& shape = data.shape(); const Double x = where[0]; const Double y = where[1]; const T floorx = std::floor(x); const T floory = std::floor(y); // Handle mask if (anyBadMaskPixels(maskPtr, x-a+1, x+a, y-a+1, y+a)) return False; // Where we can't sum over the full support of the kernel due to proximity // to the edge, set the pixel value to zero. This is just one way of // dealing with edge effects, another could be to revert to linear // interpolation. if (floorx < a || floorx >= shape[0] - a || floory < a || floory >= shape[1] - a) { result = 0; return True; } // Interpolate result = 0; for (T i = floorx - a + 1; i <= floorx + a; ++i) { for (T j = floory - a + 1; j <= floory + a; ++j) { result += data(i, j) * L(x - i, a) * L(y - j, a); } } return True; } // Lanczos interpolation: helper function template T Interpolate2D::sinc(const T x) const { if (x == 0) { return 1; } return sin(M_PI * x) / (M_PI * x); } // Lanczos interpolation: helper function template T Interpolate2D::L(const T x, const Int a) const { if (-a < x && x < a) { return sinc(x) * sinc (x/a); } return 0; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/InterpolateArray1D.h000066400000000000000000000261041476623553700230410ustar00rootroot00000000000000//# Interpolate1DArray.h: Interpolation in last dimension of an Array //# Copyright (C) 1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_INTERPOLATEARRAY1D_H #define SCIMATH_INTERPOLATEARRAY1D_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template class PtrBlock; template class Block; // Interpolate in one dimension // // // // //
      • Array //
      • Vector // // // The InterpolateArray1D class does interpolation in one dimension of // an Array only. // // // This class will, given the abscissa and ordinates of a set of one // dimensional data, interpolate on this data set giving the value at any // specified ordinate. It will extrapolate if necessary, but this is will // usually give a poor result. There is no requirement for the ordinates to // be regularly spaced, however they do need to be sorted and each // abscissa should have a unique value. // // Interpolation can be done using the following methods: //
          //
        • Nearest Neighbour //
        • Linear (default unless there is only one data point) //
        • Cubic Polynomial //
        • Natural Cubic Spline //
        // // The abscissa must be a simple type (scalar value) that // can be ordered. ie. an uInt, Int, Float or Double (not Complex). The // ordinate can be an Array of any data type that has addition, and // subtraction defined as well as multiplication by a scalar of the abcissa // type. // So the ordinate can be complex numbers, where the interpolation is done // separately on the real and imaginary components. // Use of Arrays as the the Range type is discouraged, operations will // be very slow, it would be better to construct a single higher dimensional // array that contains all the data. // // Note: this class (and these docs) are heavily based on the // Interpolate1D // class in aips/Functionals. That class proved to be // too slow for interpolation of large data volumes (i.e. spectral line // visibility datasets) mainly due to the interface which forced the // creation of large numbers of temporary Vectors and Arrays. // This class is 5-10 times faster than Interpolate1D in cases where // large amounts of data are to be interpolated. //
        // // This code fragment does cubic interpolation on (xin,yin) pairs to // produce (xout,yout) pairs. // // Vector xin(4); indgen(xin); // Vector yin(4); indgen(yin); yin = yin*yin*yin; // Vector xout(20); // for (Int i=0; i<20; i++) xout(i) = 1 + i*0.1; // Vector yout; // InterpolateArray1D::interpolate(yout, xout, xin, yin, // InterpolateArray1D::cubic); // // // // This class was motivated by the need to interpolate visibilities // in frequency to allow selection and gridding in velocity space // with on-the-fly doppler correction. // // //
      • The Domain class must be a type that can be ordered in a mathematical // sense. This includes uInt, Int, Float, Double, but not Complex. // // //
      • The Range class must have addition and subtraction of Range objects with // each other as well as multiplication by a scalar defined. Besides the // scalar types listed above this includes Complex, DComplex, and Arrays of // any of these types. Use of Arrays is discouraged however. // // //
      • AipsError // // //
      • Implement flagging in cubic and spline interpolation // template class InterpolateArray1D { public: // Interpolation methods enum InterpolationMethod { // nearest neighbour nearestNeighbour, // linear linear, // cubic cubic, // cubic spline spline }; // Interpolate in the last dimension of array yin whose x coordinates // along this dimension are given by xin. // Output array yout has interpolated values for x coordinates xout. // E.g., interpolate a Cube(pol,chan,time) in the time direction, all // values in the pol-chan plane are interpolated to produce the output // pol-chan plane. static void interpolate(Array& yout, const Vector& xout, const Vector& xin, const Array& yin, Int method); // deprecated version of previous function using Blocks - no longer needed // now that Vector has a fast index operator []. static void interpolate(Array& yout, const Block& xout, const Block& xin, const Array& yin, Int method); // Interpolate in the last dimension of array yin whose x coordinates // along this dimension are given by xin. // Output array yout has interpolated values for x coordinates xout. // This version handles flagged data in a simple way: all outputs // depending on a flagged input are flagged. // If goodIsTrue==True, then that means // a good data point has a flag value of True (usually for // visibilities, good is False and for images good is True) // If extrapolate==False, then xout points outside the range of xin // will always be marked as flagged. // TODO: implement flags for cubic and spline (presently input flags // are copied to output). static void interpolate(Array& yout, Array& youtFlags, const Vector& xout, const Vector& xin, const Array& yin, const Array& yinFlags, Int method, Bool goodIsTrue=False, Bool extrapolate=False); // deprecated version of previous function using Blocks - no longer needed // now that Vector has a fast index operator []. static void interpolate(Array& yout, Array& youtFlags, const Block& xout, const Block& xin, const Array& yin, const Array& yinFlags, Int method, Bool goodIsTrue=False, Bool extrapolate=False); // Interpolate in the middle axis in 3D array (yin) whose x coordinates along the // this dimension are given by xin. // Interpolate a Cube(pol,chan,time) in the chan direction. // Currently only linear interpolation method is implemented. // TODO: add support for nearest neiborhood, cubic, and cubic spline. static void interpolatey(Cube& yout, const Vector& xout, const Vector& xin, const Cube& yin, Int method); // Interpolate in the middle dimension of 3D array yin whose x coordinates // along this dimension are given by xin. // Output array yout has interpolated values for x coordinates xout. // This version handles flagged data in a simple way: all outputs // depending on a flagged input are flagged. // If goodIsTrue==True, then that means // a good data point has a flag value of True (usually for // visibilities, good is False and for images good is True) // If extrapolate==False, then xout points outside the range of xin // will always be marked as flagged. // Currently only linear interpolation method is implemented. // TODO: add support for nearest neiborhood, cubic, and cubic spline. static void interpolatey(Cube& yout, Cube& youtFlags, const Vector& xout, const Vector& xin, const Cube& yin, const Cube& yinFlags, Int method, Bool goodIsTrue=False, Bool extrapolate=False); private: // Interpolate the y-vectors of length ny from x values xin to xout. static void interpolatePtr(PtrBlock& yout, Int ny, const Vector& xout, const Vector& xin, const PtrBlock& yin, Int method); // Interpolate the y-vectors of length ny from x values xin to xout. // Take flagging into account static void interpolatePtr(PtrBlock& yout, PtrBlock& youtFlags, Int ny, const Vector& xout, const Vector& xin, const PtrBlock& yin, const PtrBlock& yinFlags, Int method, Bool goodIsTrue, Bool extrapolate); // Interpolate along yaxis static void interpolateyPtr(PtrBlock& yout, Int na, Int nb, Int nc, const Vector& xout, const Vector& xin, const PtrBlock& yin, Int method); // Take flagging into account static void interpolateyPtr(PtrBlock& yout, PtrBlock& youtFlags, Int na, Int nb, Int nc, const Vector& xout, const Vector& xin, const PtrBlock& yin, const PtrBlock& yinFlags, Int method, Bool goodIsTrue, Bool extrapolate); // Interpolate the y-vectors of length ny from x values xin to xout // using polynomial interpolation with specified order. static void polynomialInterpolation(PtrBlock& yout, Int ny, const Vector& xout, const Vector& xin, const PtrBlock& yin, Int order); }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Mathematics/InterpolateArray1D.tcc000066400000000000000000000710541476623553700233670ustar00rootroot00000000000000//# Interpolate1DArray.cc: implements Interpolation in one dimension //# Copyright (C) 1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_INTERPOLATEARRAY1D_TCC #define SCIMATH_INTERPOLATEARRAY1D_TCC #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template void InterpolateArray1D::interpolate(Array& yout, const Vector& xout, const Vector& xin, const Array& yin, Int method) { const uInt ndim = yin.ndim(); Int nxin=xin.nelements(), nxout=xout.nelements(); IPosition yinShape=yin.shape(); DebugAssert(nxin==yinShape(ndim-1),AipsError); Bool deleteYin, deleteYout; const Range* pyin=yin.getStorage(deleteYin); Int yStep=1; Int i; for (i=0; i yinPtrs(nxin); PtrBlock youtPtrs(nxout); for (i=0; i void InterpolateArray1D::interpolate(Array& yout, const Block& xout, const Block& xin, const Array& yin, Int method) { Vector vxout(xout.begin(), xout.end()); Vector vxin(xin.begin(), xin.end()); interpolate(yout,vxout,vxin,yin,method); } template void InterpolateArray1D::interpolate(Array& yout, Array& youtFlags, const Vector& xout, const Vector& xin, const Array& yin, const Array& yinFlags, Int method, Bool goodIsTrue, Bool extrapolate) { const uInt ndim = yin.ndim(); Int nxin=xin.nelements(), nxout=xout.nelements(); IPosition yinShape=yin.shape(); DebugAssert(nxin==yinShape(ndim-1),AipsError); DebugAssert((yinFlags.shape() == yinShape), AipsError); Bool deleteYin, deleteYout, deleteYinFlags, deleteYoutFlags; const Range* pyin=yin.getStorage(deleteYin); const Bool* pyinFlags=yinFlags.getStorage(deleteYinFlags); Int yStep=1; Int i; for (i=0; i yinPtrs(nxin); PtrBlock yinFlagPtrs(nxin); PtrBlock youtPtrs(nxout); PtrBlock youtFlagPtrs(nxout); for (i=0; i void InterpolateArray1D::interpolate(Array& yout, Array& youtFlags, const Block& xout, const Block& xin, const Array& yin, const Array& yinFlags, Int method, Bool goodIsTrue, Bool extrapolate) { Vector vxout(xout.begin(), xout.end()); Vector vxin(xin.begin(), xin.end()); interpolate(yout,youtFlags,vxout,vxin,yin,yinFlags, method,goodIsTrue,extrapolate); } template void InterpolateArray1D::interpolatey(Cube& yout, const Vector& xout, const Vector& xin, const Cube& yin, Int method) { Int nxout=xout.nelements(); IPosition yinShape=yin.shape(); //check the number of elements in y DebugAssert(xin.nelements()==yinShape(2),AipsError); Bool deleteYin, deleteYout; const Range* pyin=yin.getStorage(deleteYin); Int na=yinShape(0); Int nb=yinShape(1); Int nc=yinShape(2); IPosition youtShape=yinShape; youtShape(1)=nxout; // pick y of cube //youtShape(2)=nxout; // pick z of cube yout.resize(youtShape); Range* pyout=yout.getStorage(deleteYout); PtrBlock yinPtrs(na*nb*nc); PtrBlock youtPtrs(na*nxout*nc); Int i; for (i=0; i<(na*nb*nc); i++) yinPtrs[i]=pyin+i; for (i=0; i<(na*nxout*nc); i++) { youtPtrs[i]=pyout+i; } interpolateyPtr(youtPtrs, na, nb, nc, xout, xin, yinPtrs, method); yin.freeStorage(pyin,deleteYin); yout.putStorage(pyout,deleteYout); } template void InterpolateArray1D::interpolatey(Cube& yout, Cube& youtFlags, const Vector& xout, const Vector& xin, const Cube& yin, const Cube& yinFlags, Int method, Bool goodIsTrue, Bool extrapolate) { Int nxout=xout.nelements(); IPosition yinShape=yin.shape(); DebugAssert(xin.nelements()==yinShape(2),AipsError); DebugAssert((yinFlags.shape() == yinShape), AipsError); Bool deleteYin, deleteYout, deleteYinFlags, deleteYoutFlags; const Range* pyin=yin.getStorage(deleteYin); const Bool* pyinFlags=yinFlags.getStorage(deleteYinFlags); Int na=yinShape(0); Int nb=yinShape(1); Int nc=yinShape(2); IPosition youtShape=yinShape; youtShape(1)=nxout; // pick y of cube yout.resize(youtShape); youtFlags.resize(youtShape); youtFlags.set(False); Range* pyout=yout.getStorage(deleteYout); Bool* pyoutFlags=youtFlags.getStorage(deleteYoutFlags); PtrBlock yinPtrs(na*nb*nc); PtrBlock yinFlagPtrs(na*nb*nc); PtrBlock youtPtrs(na*nxout*nc); PtrBlock youtFlagPtrs(na*nxout*nc); Int i; for (i=0; i<(na*nb*nc); i++) { yinPtrs[i]=pyin+i; yinFlagPtrs[i]=pyinFlags+i; } for (i=0; i<(na*nxout*nc); i++) { youtPtrs[i]=pyout+i; youtFlagPtrs[i]=pyoutFlags+i; } interpolateyPtr(youtPtrs, youtFlagPtrs, na, nb, nc, xout, xin, yinPtrs, yinFlagPtrs, method, goodIsTrue, extrapolate); yin.freeStorage(pyin,deleteYin); yinFlags.freeStorage(pyinFlags,deleteYinFlags); yout.putStorage(pyout,deleteYout); youtFlags.putStorage(pyoutFlags,deleteYoutFlags); } template void InterpolateArray1D::interpolatePtr(PtrBlock& yout, Int ny, const Vector& xout, const Vector& xin, const PtrBlock& yin, Int method) { uInt nElements=xin.nelements(); AlwaysAssert (nElements>0, AipsError); Domain x_req; switch (method) { case nearestNeighbour: // This does nearest neighbour interpolation { for (uInt i=0; i y2(nElements); // The y2 values are initialised here. I need to calculate the second // derivates of the interpolating curve at each x_value. As described // in Numerical Recipies 2nd Ed. Sec. 3.3, this is done by requiring // that the first derivative is continuous at each data point. This // leads to a set of equations that has a tridiagonal form that can be // solved using an order(N) algorithm. // // The first part of this solution is to do the Gaussian elimination so // that all the coefficients on the diagonal are one, and zero below the // diagonal. Because the system is tridiagonal the only non-zero // coefficients are in the diagonal immediately above the main // one. These values are stored in y2Values temporarily. The temporary // storage t, is used to hold the right hand side. Block t(nElements); t[0] = 0; for (Int j=0; j 1; i--){ y2[i] -= t[i]*y2[i+1]; } for (i=0; i void InterpolateArray1D::interpolatePtr(PtrBlock& yout, PtrBlock& youtFlags, Int ny, const Vector& xout, const Vector& xin, const PtrBlock& yin, const PtrBlock& yinFlags, Int method, Bool goodIsTrue, Bool extrapolate) { uInt nElements=xin.nelements(); Domain x_req; Bool flag = !(goodIsTrue); switch (method) { case nearestNeighbour: // This does nearest neighbour interpolation { for (Int i=0; i::epsilon(); // y1 + ((x_req-x1)/(x2-x1)) * (y2-y1); if (frac>limit && frac<1.-limit) { //cout << "two: frac " << setprecision(12) << xfrac << endl; if (goodIsTrue) { for (Int j=0; j= 1.-limit for (Int j=0; j y2(nElements); // The y2 values are initialised here. I need to calculate the second // derivates of the interpolating curve at each x_value. As described // in Numerical Recipies 2nd Ed. Sec. 3.3, this is done by requiring // that the first derivative is continuous at each data point. This // leads to a set of equations that has a tridiagonal form that can be // solved using an order(N) algorithm. // // The first part of this solution is to do the Gaussian elimination so // that all the coefficients on the diagonal are one, and zero below the // diagonal. Because the system is tridiagonal the only non-zero // coefficients are in the diagonal immediately above the main // one. These values are stored in y2Values temporarily. The temporary // storage t, is used to hold the right hand side. Block t(nElements); t[0] = 0; for (Int j=0; j 1; i--){ y2[i] -= t[i]*y2[i+1]; } for (i=0; i void InterpolateArray1D::interpolateyPtr(PtrBlock& yout, Int na, Int nb, Int nc, const Vector& xout, const Vector& xin, const PtrBlock& yin, Int method) { uInt nElements=xin.nelements(); AlwaysAssert (nElements>0, AipsError); Domain x_req; switch (method) { case nearestNeighbour: // This does nearest neighbour interpolation { throw(AipsError("Interpolate1DArray::interpolateyPtr(): method=nearestNeigbour is not implemented yet")); return; } case linear: // Linear interpolation is the default { Int h; Int nxout=xout.nelements(); for (Int j=0; j void InterpolateArray1D::interpolateyPtr(PtrBlock& yout, PtrBlock& youtFlags, Int na, Int nb, Int nc, const Vector& xout, const Vector& xin, const PtrBlock& yin, const PtrBlock& yinFlags, Int method, Bool goodIsTrue, Bool extrapolate) { uInt nElements=xin.nelements(); Domain x_req; Bool flag = !(goodIsTrue); switch (method) { case nearestNeighbour: // This does nearest neighbour interpolation { throw(AipsError("Interpolate1DArray::interpolateyPtr(): method=nearestNeigbour is not implemented yet")); return; } case linear: // Linear interpolation is the default { Int h; Int nxout=xout.nelements(); for (Int j=0; j void InterpolateArray1D::polynomialInterpolation (PtrBlock& yout, Int ny, const Vector& xout, const Vector& xin, const PtrBlock& yin, Int order) { // Based on Nevilles Algorithm (Numerical Recipies 2nd ed., Section 3.1) // x is the point we want to estimate, n is the number of points to use // in the interpolation, and offset controls which n points are used // (normally the nearest points) // n = #points used in interpolation Int n = order+1; Block c(n), d(n); Block x(n); Int nElements = xin.nelements(); DebugAssert((n<=nElements),AipsError); for (Int i=0; i 1 && where < nElements - 1) where = where - n/2; else if (where <= 1) where = 0; else where = nElements - n; for (Int j=0; j #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Error class for MathFunc class // // // Error class for MathFunc class // class MathFuncError : public AipsError { public: MathFuncError() : AipsError("MathFuncError") {} MathFuncError(const Char *m) : AipsError(m) {} MathFuncError(const String &m) : AipsError(m) {} virtual ~MathFuncError() noexcept {} }; // // Fred Schwab function to calculate spheriodal functions // // // Fred Schwab function to calculate spheriodal functions. // // // Fred Schwab function to calculate spheriodal functions, in C. extern "C" { Int sphfn(Int *, Int *, Int *, float *, float *, Int *); } // C++ wrapper to Fred Schwab function to calculate spheriodal functions. float sphfn(Int ialf, Int im, float eta); // // // Enum used to identify function type for // MathFunc class // // // Enum used to identify function type for // MathFunc class // //############################################################################ //# NOTE: Delete the following listing of the enumerations when enumerations //# are handled properly by the documentation extractor. //############################################################################ // // The following enum documentation is currently // extracted by hand, and thus // could be out of date if this documentation was not updated when the // enum was modified. // // The FUNCTYPE enum is: // // // enum FUNCTYPE { UNARY, GAUSSIAN, KB, MOD_KB, SINC, SPHEROIDAL, EXP_SINC }; // // // enum FUNCTYPE { UNARY, GAUSSIAN, KB, MOD_KB, SINC, SPHEROIDAL, EXP_SINC }; // // // Function ID, for use by MathFunc class // // // Function ID, for use by MathFunc class. // struct FuncId { String name; Vector args;}; // A class to generate values of mathematical functions // // // // This class is the abstract base class for 1-dimensional math functions. // // Actual math functions are then an inherited class from the base // class. This approach allows one to define actual function values // for each derived class. Then, one can pass a generic MathFunc // pointer to other objects, but the other objects will still get // function values from the actual inherited function. // // By defining each math function as an object, we can place // parameters which will not change from one call to the function value // to another in the class definition and they only have to be // initialized once. // // // // MathFunc is the base class for 1-dimensional math functions // template class MathFunc { public: // // constructors // MathFunc(FUNCTYPE); // accept up to 4 arguments, the first being the support radius MathFunc(FUNCTYPE, T cutoff, T arg1 = 1.0e+30, T arg2 = 1.0e+30, T arg3 = 1.0e+30); MathFunc(String &, Vector &); MathFunc(const MathFunc&); // Copy constructor MathFunc(MathFunc *); // // Destructor // virtual ~MathFunc(); // // Assignment operator - Note: this function works only for envelops. // Polymorphism flaws will let you pass a letter as an argument but an // exception will be thrown at run time. // MathFunc& operator=(const MathFunc&); // // return value of support width // virtual T sup_value() const; // // compute and return a value of the math function // virtual T value(const T &a) const; // // create a new math function // static MathFunc *newMathFunc(const MathFunc&); // // return a FuncId structure for Table storage/retrieval. // virtual FuncId id() const; // // These functions return the static constants used as default // parameters for the various derived functions // // The default support radius static T defcutoff() {return defcutoff_p;} // The default width for Gaussian_Conv static T defwidth() {return defwidth_p;} // The default width for KB_Conv and Mod_KB_Conv static T defKBwidth() {return defKBwidth_p;} // A default parameter for KB_Conv and Mod_KB_Conv static T defKBparm() {return defKBparm_p;} // A default parameter for Mod_KB_Conv static T defmodKBparm() {return defmodKBparm_p;} // The default support radius for Sinc_Conv and Sph_Conv static T defSphcutoff() {return defSphcutoff_p;} // The default Sinc parameter for Sinc_Conv and Exp_Sinc_Conv static T defSincparm() {return defSincparm_p;} // The default parameter for Sph_Conv static T defSphparm() {return defSphparm_p;} // The default exponential power for Exp_Sinc_Conv static T defExpPower() {return defExpPower_p;} // The default exponential scale length for Exp_Sinc_Conv static T defExpScale() {return defExpScale_p;} protected: // // for every derived class, return new of that class with its own parameters // virtual MathFunc * clone() const; // // Default constructor (Null) // MathFunc(); // // pointer to letter class // MathFunc *object; private: static T defcutoff_p; static T defwidth_p; static T defKBparm_p; static T defKBwidth_p; static T defmodKBparm_p; static T defSphcutoff_p; static T defSphparm_p; static T defSincparm_p; static T defExpPower_p; static T defExpScale_p; }; //# ======================================================== //# Now we define actual math classes as inherited classes of //# the base class MathFunc //# ========================================================= // // Unary // // // A Unary function (always returns the value 1.0) // // template class Unary: public MathFunc { public: // //default constructor // Unary( T cut = MathFunc::defcutoff()); // //copy constructor // Unary(const Unary&); Unary& operator=(const Unary&); T sup_value() const{ return sup_width;} T value(const T &) const; // // return a FuncId structure for Table storage/retrieval. // FuncId id() const; private: MathFunc * clone() const; T sup_width; }; // // // Gaussian // // // A Gaussian // // template class GaussianConv: public MathFunc { public: // //default constructor // GaussianConv( T cut = MathFunc::defcutoff(), T wparm = MathFunc::defwidth()); // //copy constructor // GaussianConv(const GaussianConv&); GaussianConv& operator=(const GaussianConv&); T sup_value() const{ return sup_width;} T value(const T &) const; // // return a FuncId structure for Table storage/retrieval. // FuncId id() const; private: MathFunc * clone() const; T sup_width, fw2; const T ln16; }; // // // A Kaiser-Bessel function // // // A Kaiser-Bessel function // // template class KB_Conv: public MathFunc { public: //default constructor KB_Conv(T cut = MathFunc::defcutoff(), T wparm = MathFunc::defKBwidth(), T kbparm = MathFunc::defKBparm()); // copy constructor KB_Conv(const KB_Conv &); KB_Conv& operator=(const KB_Conv&); T sup_value() const { return sup_width;} T value(const T &) const; // // return a FuncId structure for Table storage/retrieval. // FuncId id() const; private: MathFunc * clone() const; T kbparm, fw, sup_width; }; // // // A Kaiser-Bessel function multiplied by a Gaussian // // // A Kaiser-Bessel function multiplied by a Gaussian // // template class Mod_KB_Conv: public MathFunc { public: //default constructor Mod_KB_Conv (T cut = MathFunc::defcutoff(), T wparm = MathFunc::defKBwidth(), T kbparm = MathFunc::defKBparm(), T gwparm = MathFunc::defmodKBparm()); //copy constructor Mod_KB_Conv(const Mod_KB_Conv&); Mod_KB_Conv& operator=(const Mod_KB_Conv&); T sup_value() const { return sup_width;} T value(const T &) const; // // return a FuncId structure for Table storage/retrieval. // FuncId id() const; private: MathFunc* clone() const; T kbparm, gw2, sup_width, widthparm; const T ln16; }; // // Sine x / x function // // // // Sine x / x function // // template class Sinc_Conv: public MathFunc { public: //default constructor Sinc_Conv(T cut = MathFunc::defSphcutoff(), T sincparm = MathFunc::defSincparm()); //copy constructor Sinc_Conv(const Sinc_Conv&); Sinc_Conv& operator=(const Sinc_Conv&); T sup_value() const { return sup_width;} T value(const T &) const; // // return a FuncId structure for Table storage/retrieval. // FuncId id() const; private: MathFunc * clone() const; T Sinc_parm, sup_width; }; // // // Spheroidal function // // // Spheroidal function - calls Fred Schwab function converted by f2c // // template class Sph_Conv: public MathFunc // // Spheroidal function - calls Fred Schwab function converted by f2c // { public: //default constructor Sph_Conv(T cut = MathFunc::defSphcutoff(), T Sphparm = MathFunc::defSphparm()); //copy constructor Sph_Conv(const Sph_Conv&); Sph_Conv& operator=(const Sph_Conv&); T sup_value() const { return sup_width;} float value(const float &) const; // // return a FuncId structure for Table storage/retrieval. // FuncId id() const; private: MathFunc * clone() const; T sup_width, sphparm; }; // // Exponential times a Sinc // // // An Exponential times a Sinc // // The value(T &x) is given by // Exp(-(abs(x) / expscale) ** exppow) * Sinc( pi * x / sincparm) // // where the 3 paramaters correspond to those in the default constructor // Note that the default case of exppow = 2 is // a Gaussian times a Sinc. // Since this is often a useful case, that parameter appears last in the // constructor. // // template class ExpSincConv: public MathFunc { public: // // default constructor // ExpSincConv ( T cut = MathFunc::defcutoff(), T sincparm = MathFunc::defSincparm(), T exppow = MathFunc::defExpPower(), T expscale = MathFunc::defExpScale()); // copy constructor ExpSincConv (const ExpSincConv&); // assignment operator ExpSincConv& operator=(const ExpSincConv&); // get access to the support width T sup_value() const { return sup_width; } // and get the value of the function T value(const T &) const; // // return FuncID structure for Table storage/retrieval. // FuncId id() const; private: MathFunc * clone() const; T sup_width, scale, exponent, sincpByPi; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif //AIPS_MATHFUNC_H casacore-3.7.1/scimath/Mathematics/MathFunc.tcc000066400000000000000000000417221476623553700214210ustar00rootroot00000000000000//# MathFunc.cc: Templated letter/envelope classes for single dependent variable functions //# Copyright (C) 1993,1994,1995,1996,1998,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_MATHFUNC_TCC #define SCIMATH_MATHFUNC_TCC #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // This file implements an abstract base class of MathFunc objects // // Actual math functions are an inherited class from the base // class. This approach allows one to define actual function values // for each derived class. Then, one can pass a generic MathFunc // pointer to other objects, but the other objects will still get // function values from the actual inherited function. // // By defining each math function as an object, we can place // parameters which will not change from one call of the member function // value to another in the class definition and they only have to be // initialized once. // // // Define static members of MathFunc // (for g++ 3.3 and lower they are defined in MathFunc2.cc) // #if !defined(__GNUC__) || __GNUC__>3 || (__GNUC__==3 && __GNUC_MINOR__>3) template T MathFunc::defcutoff_p = T(2.0); template T MathFunc::defwidth_p = T(1.3); template T MathFunc::defKBwidth_p = T(2.0); template T MathFunc::defKBparm_p = T(2.5); template T MathFunc::defmodKBparm_p = T(3.0); template T MathFunc::defSphcutoff_p = T(3.0); template T MathFunc::defSphparm_p = T(1.0); template T MathFunc::defSincparm_p = T(1.14); // note that these defaults are equivalent to a Gaussian (exponent = 2) // having the same width as defwidth above, FWHM = 1.3 template T MathFunc::defExpPower_p = T(2.0); template T MathFunc::defExpScale_p = T(1.3/sqrt(4.0*M_LN2)); #endif template MathFunc::MathFunc(FUNCTYPE type) { switch(type) { case MOD_KB: object = new Mod_KB_Conv(); break; case GAUSSIAN: object = new GaussianConv(); break; case KB: object = new KB_Conv(); break; case SPHEROIDAL: object = new Sph_Conv(); break; case SINC: object = new Sinc_Conv(); break; case UNARY: object = new Unary(); break; case EXP_SINC: object = new ExpSincConv(); break; default: throw(MathFuncError(" MathFunc::MathFunc: Invalid enumerated" " type as argument" )); break; } } template MathFunc::MathFunc(String &type, Vector &args) { if (type.matches("MOD_KB")) object = new Mod_KB_Conv((T)args(0), (T)args(1), (T)args(2), (T)args(3)); else if (type.matches( "GAUSSIAN")) object = new GaussianConv((T)args(0), (T)args(1)); else if (type.matches( "KB")) object = new KB_Conv((T)args(0), (T)args(1), (T)args(2)); else if (type.matches( "SPHEROIDAL")) object = new Sph_Conv((T)args(0), (T)args(1)); else if (type.matches( "SINC")) object = new Sinc_Conv((T)args(0), (T)args(1)); else if (type.matches( "UNARY")) object = new Unary((T)args(0)); else if (type.matches( "EXP_SINC")) object = new ExpSincConv((T)args(0), (T)args(1), (T)args(2), (T)args(3)); else throw(MathFuncError(" MathFunc::MathFunc: Invalid String value" " as argument" )); } // // this function is the MathFunc intitializer // template MathFunc::MathFunc(FUNCTYPE type, T cut, T arg1, T arg2, T arg3) { T wparm; T kbparm; T gwparm; T sphparm; T sincparm; T exppower; T expscale; switch(type) { case MOD_KB: wparm = arg1; kbparm = arg2; gwparm = arg3; object = new Mod_KB_Conv(cut, wparm, kbparm, gwparm); break; case GAUSSIAN: wparm = arg1; object = new GaussianConv(cut, wparm); break; case KB: wparm = arg1; kbparm = arg2; object = new KB_Conv(cut, wparm, kbparm); break; case SPHEROIDAL: sphparm = arg1; object = new Sph_Conv(cut, sphparm); break; case SINC: sincparm = arg1; object = new Sinc_Conv(cut, sincparm); break; case UNARY: object = new Unary(cut); break; case EXP_SINC: sincparm = arg1; expscale = arg2; exppower = arg3; object = new ExpSincConv(cut, sincparm, expscale, exppower); break; default: throw(MathFuncError("MathFunc::MathFunc: Invalid enumerated" " type as argument." )); break; } } // // copy constructor // template MathFunc::MathFunc(const MathFunc &other):object(static_cast *>(0)) { if(other.object!=static_cast *>(0)) delete object; // thus we can't allow references to letter classes. object = other.object->clone(); } // // backdoor new type constructor without enumerated type list addition // template MathFunc::MathFunc(MathFunc *other) : object(static_cast *>(0)) { if(other->object==static_cast *>(0)){ object = other; } else *this = *other; } // // destructor // template MathFunc::~MathFunc() { delete object;} // // assignment operator // template MathFunc& MathFunc::operator=(const MathFunc& other) { if(this == &other) return *this; if(other.object==static_cast *>(0)) throw (MathFuncError("MathFunc::operator=: attempt to use derived class" " in a base class only function (polymorph flaw).")); delete object; object = other.object->clone(); return *this; } // // compute and return a value of the math function // template T MathFunc::value(const T &i) const { return object->value(i);} // // return the value of the supported width // template T MathFunc::sup_value() const { return object->sup_value();} template MathFunc* MathFunc::newMathFunc(const MathFunc& prototype) { return new MathFunc(prototype.clone());} template FuncId MathFunc::id() const { return object->id();} template MathFunc * MathFunc::clone() const { return new MathFunc(object->clone());} // // Note: The inheritance schema is purely to allow polymorphism. // Thus, the data members associated with the MathFunc base class // are not needed as data members of the derived class. To save // space, the default constructor of the MathFunc base class // initializes its data members to zero. // template MathFunc::MathFunc(): object(static_cast *>(0)) { // nothing } // ==========================letter classes========================= // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // -------------------------Unary function-------------------- // // default constructor // template Unary::Unary(T cut): MathFunc(), sup_width(cut) { // nothing } // // copy constructor // template Unary::Unary(const Unary& other): MathFunc(), sup_width(other.sup_width) { // nothing } // // assignment operator // template Unary& Unary::operator=(const Unary& other) { if(this == &other) return *this; sup_width = other.sup_width; return *this; } // // this function the value 1.0 for any location 'i' // template T Unary::value(const T &i) const { (void)i; return T(1.0); } template FuncId Unary::id() const { FuncId tmp; tmp.name = "UNARY"; tmp.args.resize(1); tmp.args(0)=sup_width; return tmp; } template MathFunc * Unary::clone() const { return new Unary(*this);} // use Unary copy ctor // -------------------------Gaussian Convolution-------------------- // // default constructor // template GaussianConv::GaussianConv(T cut, T wparm): MathFunc(), sup_width(cut), fw2(wparm*wparm), ln16(4.0*M_LN2) { // nothing } // // copy constructor // template GaussianConv::GaussianConv(const GaussianConv& other): MathFunc(), sup_width(other.sup_width), fw2(other.fw2), ln16(4.0*M_LN2) { // nothing } // // assignment operator // template GaussianConv& GaussianConv::operator=(const GaussianConv& other) { if(this == &other) return *this; sup_width = other.sup_width; fw2 = other.fw2; return *this; } // // this function computes values for a convolution vector conv_ptr // at location 'i' // template T GaussianConv::value(const T &i) const { return exp(-ln16 * i*i / fw2);} template FuncId GaussianConv::id() const { FuncId tmp; tmp.name = "GAUSSIAN"; tmp.args.resize(2); tmp.args(0)=sup_width; tmp.args(1)=sqrt(fw2); return tmp; } template MathFunc * GaussianConv::clone() const { return new GaussianConv(*this);} // use GaussianConv copy ctor // ---------------------Kaiser-Bessel convolution------------------ // // default constructor // template KB_Conv::KB_Conv(T cut, T wparm, T KBparm): MathFunc(), kbparm(KBparm), fw(wparm), sup_width(cut) { // nothing } // // copy constructor // template KB_Conv::KB_Conv(const KB_Conv& other): MathFunc(), kbparm(other.kbparm), fw(other.fw), sup_width(other.sup_width) { //nothing } // // assignment operator // template KB_Conv& KB_Conv::operator=(const KB_Conv& other) { if(this == &other) return *this; kbparm = other.kbparm; fw = other.fw; sup_width = other.sup_width; return *this; } // // this function computes values for a convolution vector conv_ptr // at location 'i' // template T KB_Conv::value(const T &i) const { T par2 = kbparm * kbparm; T x1 = M_PI * kbparm; T x2 = M_PI * sqrt(par2 - 1.0); T x3 = M_PI * sqrt(par2 - 4.0); T a = sinh(x1); T b = sinh(x2) * 2.0; T c = sinh(x3) * 2.0; T sum = a + b + c; a /= sum; b /= sum; c /= sum; T x = i * M_PI / fw; return (a + b * cos(x) + c * cos(2.0 * x)); } template FuncId KB_Conv::id() const { FuncId tmp; tmp.name = "KB"; tmp.args.resize(3); tmp.args(0)=sup_width; tmp.args(1)=fw; tmp.args(2)=kbparm; return tmp; } template MathFunc * KB_Conv::clone() const { return new KB_Conv(*this);} // use KB_Conv copy constructor //----------A Kaiser-Bessel function modified by a Gaussian----- // // default constructor // template Mod_KB_Conv::Mod_KB_Conv(T cut, T wparm, T KBparm, T gwparm): MathFunc(), kbparm(KBparm), gw2(gwparm*gwparm), sup_width(cut), widthparm(wparm), ln16(4.0*M_LN2) { // nothing } // // copy constructor // template Mod_KB_Conv::Mod_KB_Conv(const Mod_KB_Conv& other): MathFunc(), kbparm(other.kbparm), gw2(other.gw2), sup_width(other.sup_width), widthparm(other.widthparm), ln16(4.0*M_LN2) { // nothing } // // Assignment operator // template Mod_KB_Conv &Mod_KB_Conv::operator=(const Mod_KB_Conv& other) { if(this == &other) return *this; sup_width = other.sup_width; widthparm = other.widthparm; kbparm = other.kbparm; gw2 = other.gw2; return *this; } // // this function computes values for a convolution vector conv_ptr // at location 'i' // template T Mod_KB_Conv::value(const T &i) const { T par2 = kbparm * kbparm; T x1 = M_PI * kbparm; T x2 = M_PI * sqrt(par2 - 1.0); T x3 = M_PI * sqrt(par2 - 4.0); T a = sinh(x1); T b = sinh(x2) * 2.0; T c = sinh(x3) * 2.0; T sum = a + b + c; a /= sum; b /= sum; c /= sum; T i2 = i * i; T x = i * M_PI / widthparm; T fx = a + b * cos(x) + c * cos(2.0 * x); return (fx * exp(-ln16 * (i2 / gw2))); } template FuncId Mod_KB_Conv::id() const { FuncId tmp; tmp.name = "MOD_KB"; tmp.args.resize(4); tmp.args(0)=sup_width; tmp.args(1)=widthparm; tmp.args(2)=kbparm; tmp.args(3)=gw2; return tmp; } template MathFunc * Mod_KB_Conv::clone() const { return new Mod_KB_Conv(*this);} // use Mod_KB_Conv copy ctor // ------------------------Sinc convolution------------------------ // // default constructor // template Sinc_Conv::Sinc_Conv(T cut, T sincparm): MathFunc(), Sinc_parm(sincparm), sup_width(cut) { // nothing } // // copy constructor // template Sinc_Conv::Sinc_Conv(const Sinc_Conv& other): MathFunc(), Sinc_parm(other.Sinc_parm), sup_width(other.sup_width) { // nothing } // // assignment operator // template Sinc_Conv& Sinc_Conv::operator=(const Sinc_Conv& other) { if(this == &other) return *this; sup_width = other.sup_width; Sinc_parm = other.Sinc_parm; return *this; } // // this function computes values for a convolution vector conv_ptr // at location 'i' // template T Sinc_Conv::value(const T &i) const { T ret_value; if (i == 0.0) ret_value = 1.0; else { T parm = M_PI * i / Sinc_parm; ret_value = sin(parm) / parm; } return ret_value; } template FuncId Sinc_Conv::id() const { FuncId tmp; tmp.name = "SINC"; tmp.args.resize(2); tmp.args(0)=sup_width; tmp.args(1)=Sinc_parm; return tmp; } template MathFunc * Sinc_Conv::clone() const { return new Sinc_Conv(*this);} //use Sinc_Conv copy ctor // ------------------------Spheroidal Convolution-------------------- // // default constructor // template Sph_Conv::Sph_Conv(T cut, T Sphparm): MathFunc(), sup_width(cut), sphparm(Sphparm) { //nothing } // // copy constructor // template Sph_Conv::Sph_Conv(const Sph_Conv& other): MathFunc(), sup_width(other.sup_width), sphparm(other.sphparm) { // nothing } // // assignment operator // template Sph_Conv& Sph_Conv::operator=(const Sph_Conv& other) { if(this == &other) return *this; sup_width = other.sup_width; sphparm = other.sphparm; return *this; } // // this function computes values for a convolution vector conv_ptr // at location 'i' // template float Sph_Conv::value(const float &j) const { int isupp = int(2.0 * sup_width); if (isupp < 4) isupp = 4; if (isupp > 8) isupp = 8; int ialpha = int(2.0 * sphparm + 1.0); if (ialpha < 1) ialpha = 1; if (ialpha > 5) ialpha = 5; int jmax = int(sup_width); if (jmax > 7) jmax = 7; // call Fred Schwab function return sphfn(ialpha, isupp, j/jmax); } template FuncId Sph_Conv::id() const { FuncId tmp; tmp.name = "SPHEROIDAL"; tmp.args.resize(2); tmp.args(0)=sup_width; tmp.args(1)=sphparm; return tmp; } template MathFunc * Sph_Conv::clone() const { return new Sph_Conv(*this);} // use Sph_Conv copy ctor // -------------------------Exponential*Sinc Convolution---------------- // // default constructor // template ExpSincConv::ExpSincConv(T cut, T sincparm, T expscale, T exppower): MathFunc(), sup_width(cut), scale(expscale), exponent(exppower), sincpByPi(sincparm/M_PI) { // nothing } // // copy constructor // template ExpSincConv::ExpSincConv(const ExpSincConv& other): MathFunc(), sup_width(other.sup_width), scale(other.scale), exponent(other.exponent), sincpByPi(other.sincpByPi) { // nothing } // // assignment operator // template ExpSincConv& ExpSincConv::operator=(const ExpSincConv& other) { if(this == &other) return *this; sup_width = other.sup_width; scale = other.scale; exponent = other.exponent; sincpByPi = other.sincpByPi; return *this; } // // this function computes values for a convolution vector conv_ptr // at location 'i' // template T ExpSincConv::value(const T &i) const { T absI = fabs(i); T ret_value = exp(- pow(absI/scale,exponent)); if (absI != 0.0) { T parm = i / sincpByPi; ret_value = ret_value * sin(parm) / parm; } return ret_value; } template FuncId ExpSincConv::id() const { FuncId tmp; tmp.name = "EXP_SINC"; tmp.args.resize(4); tmp.args(0)=sup_width; tmp.args(1)=sincpByPi * M_PI; tmp.args(2)=scale; tmp.args(3)=exponent; return tmp; } template MathFunc * ExpSincConv::clone() const { return new ExpSincConv(*this);} // use ExpSincConv copy ctor } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/MathFunc2.cc000066400000000000000000000421311476623553700213120ustar00rootroot00000000000000//# MathFunc2.cc: non templated static data for MathFunc //# Copyright (C) 1993,1994,1995,1996,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Define static members of MathFunc for g++ // #if defined(__GNUC__) && (__GNUC__<3 || (__GNUC__==3 && __GNUC_MINOR__<4)) Float MathFunc::defcutoff_p = (2.0); Float MathFunc::defwidth_p = (1.3); Float MathFunc::defKBwidth_p = (2.0); Float MathFunc::defKBparm_p = (2.5); Float MathFunc::defmodKBparm_p = (3.0); Float MathFunc::defSphcutoff_p = (3.0); Float MathFunc::defSphparm_p = (1.0); Float MathFunc::defSincparm_p = (1.14); Float MathFunc::defExpPower_p = (2.0); Float MathFunc::defExpScale_p = (1.3/sqrt(4.0*C::ln2)); Double MathFunc::defcutoff_p = (2.0); Double MathFunc::defwidth_p = (1.3); Double MathFunc::defKBwidth_p = (2.0); Double MathFunc::defKBparm_p = (2.5); Double MathFunc::defmodKBparm_p = (3.0); Double MathFunc::defSphcutoff_p = (3.0); Double MathFunc::defSphparm_p = (1.0); Double MathFunc::defSincparm_p = (1.14); Double MathFunc::defExpPower_p = (2.0); Double MathFunc::defExpScale_p = (1.3/sqrt(4.0*C::ln2)); #endif /* Fred Schwab's SPHFN.f -- translated by f2c (version of 22 July 1992 22:54:52). */ #if defined(__cplusplus) extern "C" { #endif /* Table of constant values static Int c__1 = 1; static Int c__8 = 8; */ /* Subroutine */ Int sphfn(Int *ialf, Int *im, Int *iflag, float * eta, float *psi, Int *ierr) { /* Initialized data */ static float alpha[5] = { (float)0.,(float).5,(float)1.,(float)1.5,(float) 2. }; static float p7l[25] /* was [5][5] */ = { (float).02460495,(float) -.1640964,(float).434011,(float)-.5705516,(float).4418614,(float) .03070261,(float)-.1879546,(float).4565902,(float)-.5544891,( float).389279,(float).03770526,(float)-.2121608,(float).4746423,( float)-.5338058,(float).3417026,(float).04559398,(float)-.236267,( float).4881998,(float)-.5098448,(float).2991635,(float).054325,( float)-.2598752,(float).4974791,(float)-.4837861,(float).2614838 } ; static float q7l[10] /* was [2][5] */ = { (float)1.124957,(float).3784976,( float)1.07542,(float).3466086,(float)1.029374,(float).3181219,( float).9865496,(float).2926441,(float).9466891,(float).2698218 }; static float p7u[25] /* was [5][5] */ = { (float)1.924318e-4,(float) -.005044864,(float).02979803,(float)-.06660688,(float).06792268,( float)5.030909e-4,(float)-.008639332,(float).04018472,(float) -.07595456,(float).06696215,(float).001059406,(float)-.01343605,( float).0513536,(float)-.08386588,(float).06484517,(float) .001941904,(float)-.01943727,(float).06288221,(float)-.09021607,( float).06193,(float).003224785,(float)-.02657664,(float).07438627, (float)-.09500554,(float).05850884 }; static float q7u[10] /* was [2][5] */ = { (float)1.45073,(float).6578685,( float)1.353872,(float).5724332,(float)1.269924,(float).5032139,( float)1.196177,(float).4460948,(float)1.130719,(float).3982785 }; static float p8l[30] /* was [6][5] */ = { (float).0137803,(float)-.1097846, (float).3625283,(float)-.6522477,(float).6684458,(float)-.4703556, (float).01721632,(float)-.1274981,(float).3917226,(float) -.6562264,(float).6305859,(float)-.4067119,(float).02121871,( float)-.1461891,(float).4185427,(float)-.6543539,(float).590466,( float)-.3507098,(float).02580565,(float)-.1656048,(float).4426283, (float)-.6473472,(float).5494752,(float)-.3018936,(float) .03098251,(float)-.1854823,(float).4637398,(float)-.6359482,( float).5086794,(float)-.2595588 }; static float q8l[10] /* was [2][5] */ = { (float)1.076975,(float).3394154,( float)1.036132,(float).3145673,(float).9978025,(float).2920529,( float).9617584,(float).2715949,(float).9278774,(float).2530051 }; static float p8u[30] /* was [6][5] */ = { (float)4.29046e-5,(float) -.001508077,(float).01233763,(float)-.0409127,(float).06547454,( float)-.05664203,(float)1.201008e-4,(float)-.002778372,(float) .01797999,(float)-.05055048,(float).07125083,(float)-.05469912,( float)2.698511e-4,(float)-.004628815,(float).0247089,(float) -.06017759,(float).07566434,(float)-.05202678,(float)5.259595e-4,( float)-.007144198,(float).03238633,(float)-.06946769,(float) .07873067,(float)-.0488949,(float)9.255826e-4,(float)-.01038126,( float).04083176,(float)-.07815954,(float).08054087,(float) -.04552077 }; static float q8u[10] /* was [2][5] */ = { (float)1.379457,(float).5786953,( float)1.300303,(float).5135748,(float)1.230436,(float).4593779,( float)1.168075,(float).4135871,(float)1.111893,(float).3744076 }; static float p4[25] /* was [5][5] */ = { (float).01584774,(float) -.1269612,(float).2333851,(float)-.1636744,(float).05014648,( float).03101855,(float)-.1641253,(float).23855,(float)-.1417069,( float).03773226,(float).050079,(float)-.1971357,(float).2363775,( float)-.1215569,(float).02853104,(float).0720126,(float)-.225158,( float).2293715,(float)-.1038359,(float).02174211,(float).09585932, (float)-.2481381,(float).2194469,(float)-.08862132,(float) .01672243 }; static float q4[10] /* was [2][5] */ = { (float).4845581,(float).07457381, (float).4514531,(float).0645864,(float).4228767,(float).05655715,( float).3978515,(float).04997164,(float).3756999,(float).044488 }; static float p5[35] /* was [7][5] */ = { (float).003722238,(float) -.04991683,(float).1658905,(float)-.238724,(float).1877469,(float) -.08159855,(float).03051959,(float).008182649,(float)-.07325459,( float).1945697,(float)-.2396387,(float).1667832,(float)-.06620786, (float).02224041,(float).01466325,(float)-.09858686,(float) .2180684,(float)-.2347118,(float).1464354,(float)-.05350728,( float).01624782,(float).02314317,(float)-.1246383,(float).2362036, (float)-.2257366,(float).1275895,(float)-.04317874,(float) .01193168,(float).03346886,(float)-.1503778,(float).2492826,( float)-.2142055,(float).1106482,(float)-.03486024,(float) .008821107 }; static float q5[5] = { (float).241882,(float).2291233,(float).2177793,( float).2075784,(float).1983358 }; static float p6l[25] /* was [5][5] */ = { (float).05613913,(float) -.3019847,(float).6256387,(float)-.6324887,(float).3303194,(float) .06843713,(float)-.3342119,(float).6302307,(float)-.5829747,( float).27657,(float).08203343,(float)-.3644705,(float).627866,( float)-.5335581,(float).2312756,(float).09675562,(float)-.3922489, (float).6197133,(float)-.485747,(float).1934013,(float).1124069,( float)-.4172349,(float).6069622,(float)-.4405326,(float).1618978 } ; static float q6l[10] /* was [2][5] */ = { (float).9077644,(float).2535284,( float).8626056,(float).22914,(float).8212018,(float).2078043,( float).7831755,(float).1890848,(float).7481828,(float).1726085 }; static float p6u[25] /* was [5][5] */ = { (float)8.531865e-4,(float) -.01616105,(float).06888533,(float)-.1109391,(float).07747182,( float).00206076,(float)-.02558954,(float).08595213,(float) -.1170228,(float).07094106,(float).004028559,(float)-.03697768,( float).1021332,(float)-.1201436,(float).06412774,(float) .006887946,(float)-.04994202,(float).1168451,(float)-.1207733,( float).0574421,(float).01071895,(float)-.06404749,(float).1297386, (float)-.1194208,(float).05112822 }; static float q6u[10] /* was [2][5] */ = { (float)1.10127,(float).3858544,( float)1.025431,(float).3337648,(float).9599102,(float).2918724,( float).9025276,(float).2575336,(float).851747,(float).2289667 }; /* Format strings */ /* System generated locals */ float r__1; double d__1, d__2; /* Builtin functions Int s_wsfe(cilist *), do_fio(Int *, char *, ftnlen), e_wsfe(); */ /* Local variables */ static Int j, k; static float x; extern /* Subroutine */ int msgwrt_(Int *); static float eta2; /* Fortran I/O blocks static cilist io___23 = { 0, 0, 0, fmt_1900, 0 }; */ /* ----------------------------------------------------------------------- */ /* ! Evaluate rational approx. to selected spheriodial functions. */ /* */ /* This software is the subject of a User agreement and is confidential */ /* in nature. It shall not be sold or otherwise made available or */ /* disclosed to third parties. */ /* ----------------------------------------------------------------------- */ /* SPHFN is a subroutine to evaluate rational approximations to */ /* selected zero-order spheroidal functions, psi(c,eta), which are, in */ /* a sense defined in VLA Scientific Memorandum No. 132, optimal for */ /* gridding interferometer data. The approximations are taken from */ /* VLA Computer Memorandum No. 156. The parameter c is related to the */ /* support width, m, of the convoluting function according to */ /* c=pi*m/2. The parameter alpha determines a weight function in the */ /* definition of the criterion by which the function is optimal. */ /* SPHFN incorporates approximations to 25 of the spheroidal func- */ /* tions, corresponding to 5 choices of m (4, 5, 6, 7, or 8 cells) */ /* and 5 choices of the weighting exponent (0, 1/2, 1, 3/2, or 2). */ /* Inputs: */ /* IALF I Selects the weighting exponent, alpha */ /* (IALF = 1, 2, 3, 4, and 5 correspond to */ /* alpha = 0, 1/2, 1, 3/2, and 2, resp.). */ /* IM I Selects the support width m, (=IM) and, */ /* correspondingly, the parameter c of the */ /* spheroidal function (only the choices 4, */ /* 5, 6, 7, and 8 are allowed). */ /* IFLAG I Chooses whether the spheroidal function */ /* itself, or its Fourier transform, is to be */ /* approximated. The latter is appropriate */ /* for gridding, and the former for the u-v */ /* plane convolution. The two differ by a */ /* factor (1-eta**2)**alpha. IFLAG less than */ /* or equal to zero chooses the function */ /* appropriate for gridding, and IFLAG positive */ /* chooses its Fourier transform. */ /* ETA R Eta, as the argument of the spheroidal */ /* function, is a variable which ranges from 0 */ /* at the center of the convoluting function to */ /* 1 at its edge (also from 0 at the center of */ /* the gridding correction function to unity at */ /* the edge of the map). */ /* Output: */ /* PSI R function value which, on entry to the */ /* subroutine, was to have been computed. */ /* IERR I Error return code: */ /* 0 => No error */ /* 1 => IALF out of range */ /* 2 => IM out of range */ /* 3 => ABS(ETA).GT.1 */ /* 12 => IALF and IM both out of range */ /* 13 => IALF and ETA both illegal */ /* 23 => IM and ETA both illegal */ /* 123 => IALF, IM, and ETA all illegal */ /* ----------------------------------------------------------------------- */ /* INCLUDE 'INCS:DMSG.INC' */ /* ----------------------------------------------------------------------- */ *ierr = 0; /* Check inputs. */ if (*ialf < 1 || *ialf > 5) { *ierr = 1; } if (*im < 4 || *im > 8) { *ierr = *ierr * 10 + 2; } if (fabs(*eta) > (float)1.) { *ierr = *ierr * 10 + 3; } if (*ierr != 0) { goto L900; } /* So far, so good. */ /* Computing 2nd power */ r__1 = *eta; eta2 = r__1 * r__1; j = *ialf; k = *im - 3; /* Branch on support width. */ switch (k) { case 1: goto L100; case 2: goto L200; case 3: goto L300; case 4: goto L400; case 5: goto L500; } /* Support width = 4 cells. */ L100: x = eta2 - (float)1.; *psi = (p4[j * 5 - 5] + x * (p4[j * 5 - 4] + x * (p4[j * 5 - 3] + x * (p4[ j * 5 - 2] + x * p4[j * 5 - 1])))) / (x * (q4[(j << 1) - 2] + x * q4[(j << 1) - 1]) + (float)1.); goto L800; /* Support width = 5 cells. */ L200: x = eta2 - (float)1.; *psi = (p5[j * 7 - 7] + x * (p5[j * 7 - 6] + x * (p5[j * 7 - 5] + x * (p5[ j * 7 - 4] + x * (p5[j * 7 - 3] + x * (p5[j * 7 - 2] + x * p5[j * 7 - 1])))))) / (x * q5[j - 1] + (float)1.); goto L800; /* Support width = 6 cells. */ L300: if (fabs(*eta) > (float).75) { goto L350; } x = eta2 - (float).5625; *psi = (p6l[j * 5 - 5] + x * (p6l[j * 5 - 4] + x * (p6l[j * 5 - 3] + x * ( p6l[j * 5 - 2] + x * p6l[j * 5 - 1])))) / (x * (q6l[(j << 1) - 2] + x * q6l[(j << 1) - 1]) + (float)1.); goto L800; L350: x = eta2 - (float)1.; *psi = (p6u[j * 5 - 5] + x * (p6u[j * 5 - 4] + x * (p6u[j * 5 - 3] + x * ( p6u[j * 5 - 2] + x * p6u[j * 5 - 1])))) / (x * (q6u[(j << 1) - 2] + x * q6u[(j << 1) - 1]) + (float)1.); goto L800; /* Support width = 7 cells. */ L400: if (fabs(*eta) > (float).775) { goto L450; } x = eta2 - (float).600625; *psi = (p7l[j * 5 - 5] + x * (p7l[j * 5 - 4] + x * (p7l[j * 5 - 3] + x * ( p7l[j * 5 - 2] + x * p7l[j * 5 - 1])))) / (x * (q7l[(j << 1) - 2] + x * q7l[(j << 1) - 1]) + (float)1.); goto L800; L450: x = eta2 - (float)1.; *psi = (p7u[j * 5 - 5] + x * (p7u[j * 5 - 4] + x * (p7u[j * 5 - 3] + x * ( p7u[j * 5 - 2] + x * p7u[j * 5 - 1])))) / (x * (q7u[(j << 1) - 2] + x * q7u[(j << 1) - 1]) + (float)1.); goto L800; /* Support width = 8 cells. */ L500: if (fabs(*eta) > (float).775) { goto L550; } x = eta2 - (float).600625; *psi = (p8l[j * 6 - 6] + x * (p8l[j * 6 - 5] + x * (p8l[j * 6 - 4] + x * ( p8l[j * 6 - 3] + x * (p8l[j * 6 - 2] + x * p8l[j * 6 - 1]))))) / ( x * (q8l[(j << 1) - 2] + x * q8l[(j << 1) - 1]) + (float)1.); goto L800; L550: x = eta2 - (float)1.; *psi = (p8u[j * 6 - 6] + x * (p8u[j * 6 - 5] + x * (p8u[j * 6 - 4] + x * ( p8u[j * 6 - 3] + x * (p8u[j * 6 - 2] + x * p8u[j * 6 - 1]))))) / ( x * (q8u[(j << 1) - 2] + x * q8u[(j << 1) - 1]) + (float)1.); /* Normal return. */ L800: if (*iflag > 0 || *ialf == 1 || *eta == (float)0.) { goto L999; } if (fabs(*eta) == (float)1.) { goto L850; } d__1 = (double) ((float)1. - eta2); d__2 = (double) alpha[*ialf - 1]; *psi = pow(d__1, d__2) * *psi; goto L999; L850: *psi = (float)0.; goto L999; /* Error exit. */ L900: /* io___23.ciunit = msgtxt; s_wsfe(&io___23); do_fio(&c__1, (char *)&(*ierr), (ftnlen)sizeof(Int)); e_wsfe(); msgwrt_(&c__8); */ L999: return 0; /* ----------------------------------------------------------------------- */ } /* sphfn */ #if defined(__cplusplus) } #endif float sphfn(Int ialf, Int im, float eta) { Int ialphahold, imhold, iflaghold, ierrhold; ialphahold = ialf; imhold = im; iflaghold = 0; ierrhold = 0; float psihold, etahold; psihold = 0; etahold = eta; sphfn(&ialphahold, &imhold, &iflaghold, &etahold, &psihold, &ierrhold); return psihold; } } //# NAMESPACE CASACORE - END casacore-3.7.1/scimath/Mathematics/MatrixMathLA.h000066400000000000000000000223571476623553700216700ustar00rootroot00000000000000//# MatrixMath.h: The Casacore linear algebra functions //# Copyright (C) 1994,1995,1996,1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_MATRIXMATHLA_H #define SCIMATH_MATRIXMATHLA_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Linear algebra functions on Vectors and Matrices. // // // // // // Linear Algebra -- Linear algebra functions // on Vectors and Matrices. // // // // Routines which calculate the inverse of a matrix. The inverse is very // often the worst way to do a calculation. Nevertheless it is often // convenient. The present implementation uses LU decomposition implemented // by LAPACK. The determinate can be calculated "for free" as it is the // product of the diagonal terms after decomposition. If the input matrix is // singular, a matrix with no rows or columns is returned. in // must be a square matrix. // This function will only work for complex types if // Complex and DComplex map onto their FORTRAN counterparts. //# We could special case small matrices for efficiency. // template void invert(Matrix & out, T& determinate, const Matrix &in); template Matrix invert(const Matrix &in); template T determinate(const Matrix &in); // // This function inverts a symmetric positive definite matrix. It is // written in C++, so it should work with any data type for which // operators +, -, *, /, =, and sqrt are defined. The function uses // the Cholesky decomposition method to invert the matrix. Cholesky // decomposition is about a factor of 2 better than LU decomposition // where symmetry is ignored. template void invertSymPosDef(Matrix & out, T& determinate, const Matrix &in); template Matrix invertSymPosDef(const Matrix &in); // //# These are actually used by invertSymPosDef. They will not //# normally be called by the end user. //# This function performs Cholesky decomposition. //# A is a positive-definite symmetric matrix. Only the upper triangle of //# A is needed on input. On output, the lower triangle of A contains the //# Cholesky factor L. The diagonal elements of L are returned in vector //# diag. template void CholeskyDecomp(Matrix &A, Vector &diag); //# Solve linear equation A*x = b, where A positive-definite symmetric. //# On input, A contains Cholesky factor L in its low triangle except the //# diagonal elements which are in vector diag. On return x contains the //# solution. b and x can be the same vector to save memory space. template void CholeskySolve(Matrix &A, Vector &diag, Vector &b, Vector &x); //# These are the LAPACK routines actually used by invert. They will not //# normally be called by the end user. #if !defined(NEED_FORTRAN_UNDERSCORES) #define NEED_FORTRAN_UNDERSCORES 1 #endif #if NEED_FORTRAN_UNDERSCORES #define sgetrf sgetrf_ #define dgetrf dgetrf_ #define cgetrf cgetrf_ #define zgetrf zgetrf_ #define sgetri sgetri_ #define dgetri dgetri_ #define cgetri cgetri_ #define zgetri zgetri_ #define sposv sposv_ #define dposv dposv_ #define cposv cposv_ #define zposv zposv_ #define spotri spotri_ #define dpotri dpotri_ #define cpotri cpotri_ #define zpotri zpotri_ #endif extern "C" { void sgetrf(const int *m, const int *n, float *a, const int *lda, int *ipiv, int *info); void dgetrf(const int *m, const int *n, double *a, const int *lda, int *ipiv, int *info); void cgetrf(const int *m, const int *n, Complex *a, const int *lda, int *ipiv, int *info); void zgetrf(const int *m, const int *n, DComplex *a, const int *lda, int *ipiv, int *info); void sgetri(const int *m, float *a, const int *lda, const int *ipiv, float *work, const int *lwork, int *info); void dgetri(const int *m, double *a, const int *lda, const int *ipiv, double *work, const int *lwork, int *info); void cgetri(const int *m, Complex *a, const int *lda, const int *ipiv, Complex *work, const int *lwork, int *info); void zgetri(const int *m, DComplex *a, const int *lda, const int *ipiv, DComplex *work, const int *lwork, int *info); void sposv(const char *uplo, const int *n, const int* nrhs, float *a, const int *lda, float *b, const int *ldb, int *info); void dposv(const char *uplo, const int *n, const int* nrhs, double *a, const int *lda, double *b, const int *ldb, int *info); void cposv(const char *uplo, const int *n, const int* nrhs, Complex *a, const int *lda, Complex *b, const int *ldb, int *info); void zposv(const char *uplo, const int *n, const int* nrhs, DComplex *a, const int *lda, DComplex *b, const int *ldb, int *info); void spotri(const char *uplo, const int *n, float *a, const int *lda, int *info); void dpotri(const char *uplo, const int *n, double *a, const int *lda, int *info); void cpotri(const char *uplo, const int *n, Complex *a, const int *lda, int *info); void zpotri(const char *uplo, const int *n, DComplex *a, const int *lda, int *info); } //# Overloaded versions of the above to make templating work more easily inline void getrf(const int *m, const int *n, float *a, const int *lda, int *ipiv, int *info) { sgetrf(m, n, a, lda, ipiv, info); } inline void getrf(const int *m, const int *n, double *a, const int *lda, int *ipiv, int *info) { dgetrf(m, n, a, lda, ipiv, info); } inline void getrf(const int *m, const int *n, Complex *a, const int *lda, int *ipiv, int *info) { cgetrf(m, n, a, lda, ipiv, info); } inline void getrf(const int *m, const int *n, DComplex *a, const int *lda, int *ipiv, int *info) { zgetrf(m, n, a, lda, ipiv, info); } inline void getri(const int *m, float *a, const int *lda, const int *ipiv, float *work, const int *lwork, int *info) { sgetri(m, a, lda, ipiv, work, lwork, info); } inline void getri(const int *m, double *a, const int *lda, const int *ipiv, double *work, const int *lwork, int *info) { dgetri(m, a, lda, ipiv, work, lwork, info); } inline void getri(const int *m, Complex *a, const int *lda, const int *ipiv, Complex *work, const int *lwork, int *info) { cgetri(m, a, lda, ipiv, work, lwork, info); } inline void getri(const int *m, DComplex *a, const int *lda, const int *ipiv, DComplex *work, const int *lwork, int *info) { zgetri(m, a, lda, ipiv, work, lwork, info); } inline void posv(const char *uplo, const int *n, const int* nrhs, float *a, const int *lda, float *b, const int *ldb, int *info) { sposv(uplo, n, nrhs, a, lda, b, ldb, info); } inline void posv(const char *uplo, const int *n, const int* nrhs, double *a, const int *lda, double *b, const int *ldb, int *info) { dposv(uplo, n, nrhs, a, lda, b, ldb, info); } inline void posv(const char *uplo, const int *n, const int* nrhs, Complex *a, const int *lda, Complex *b, const int *ldb, int *info) { cposv(uplo, n, nrhs, a, lda, b, ldb, info); } inline void posv(const char *uplo, const int *n, const int* nrhs, DComplex *a, const int *lda, DComplex *b, const int *ldb, int *info) { zposv(uplo, n, nrhs, a, lda, b, ldb, info); } inline void potri(const char *uplo, const int *n, float *a, const int *lda, int *info) { spotri(uplo, n, a, lda, info); } inline void potri(const char *uplo, const int *n, double *a, const int *lda, int *info) { dpotri(uplo, n, a, lda, info); } inline void potri(const char *uplo, const int *n, Complex *a, const int *lda, int *info) { cpotri(uplo, n, a, lda, info); } inline void potri(const char *uplo, const int *n, DComplex *a, const int *lda, int *info) { zpotri(uplo, n, a, lda, info); } } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Mathematics/MatrixMathLA.tcc000066400000000000000000000142061476623553700222040ustar00rootroot00000000000000//# MatrixMath.cc: The Casacore linear algebra functions //# Copyright (C) 1994,1995,1996,1998,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_MATRIXMATHLA_TCC #define SCIMATH_MATRIXMATHLA_TCC #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template Matrix invert(const Matrix &in){ Matrix out; T det; invert(out, det, in); return out; } template T determinate(const Matrix &in){ Matrix out; T det; invert(out, det, in); return det; } template void invert(Matrix &out, T& det, const Matrix &in) { AlwaysAssert(in.nrow() == in.ncolumn(), AipsError); Int m = in.nrow(); Int lda = m; Int n = m; // m, n, lda out.resize(in.shape()); out = in; Bool deleteIt; T *a = out.getStorage(deleteIt); // a Block ipiv(m); // ipiv Int info; // info getrf(&m, &n, a, &lda, ipiv.storage(), &info); if (info == 0) { // LU decomposition worked! // Calculate the determinate // It is just the product of the diagonal elements det = out(0,0); for (Int i = 1; i < n; i++) det *= out(i,i); // Calculate the inverse using back substitution Int lwork = 32 * n; // Lazy - we should really get this from ilaenv Block work(lwork); getri(&m, a, &lda, ipiv.storage(), work.storage(), &lwork, &info); } out.putStorage(a, deleteIt); AlwaysAssert(info >= 0, AipsError); // illegal argument to *getri or *getrf if (info > 0) { out.resize(0,0); } } template Matrix invertSymPosDef(const Matrix &in) { Int i, j, k, n; n = in.nrow(); Vector diag(n); Vector b(n); Matrix tmp(n,n); Matrix out(n,n); for(i = 0; i < n; i++) { for(j = 0; j < n; j++) { tmp(i,j) = in(i,j); } } // Cholesky decomposition: A = L*trans(L) CholeskyDecomp(tmp, diag); // Solve inverse of A by forward and backward substitution. The right // hand side is a unit matrix, the solution is thus the inverse of A. for(j = 0; j < n; j++) { // one column at a time for(k = 0; k < n; k++) { b(k) = T(0.0); } b(j) = T(1.0); CholeskySolve(tmp, diag, b, b); for(k = 0; k < n; k++) { out(k,j) = b(k); } } return out; } template void invertSymPosDef(Matrix & out, T& determinate, const Matrix &in) { // Resize out to match in out.resize(in.shape()); Int i, j, k, n; n = in.nrow(); Vector diag(n); Vector b(n); Matrix tmp(n,n); for(i = 0; i < n; i++) { for(j = 0; j < n; j++) { tmp(i,j) = in(i,j); } } // Cholesky decomposition: A = L*trans(L) CholeskyDecomp(tmp, diag); // Is the following correct? determinate = diag(0)*diag(0); for(k = 1; k < n; k++) determinate = determinate*diag(k)*diag(k); // Solve inverse of A by forward and backward substitution. The right // hand side is a unit matrix, the solution is thus the inverse of A. for(j = 0; j < n; j++) { // one column at a time for(k = 0; k < n; k++) { b(k) = T(0.0); } b(j) = T(1.0); CholeskySolve(tmp, diag, b, b); for(k = 0; k < n; k++) { out(k,j) = b(k); } } } template void CholeskyDecomp(Matrix &A, Vector &diag) { // This function performs Cholesky decomposition. // A is a positive-definite symmetric matrix. Only the upper triangle of // A is needed on input. On output, the lower triangle of A contains the // Cholesky factor L. The diagonal elements of L are returned in vector // diag. Int i, j, k, n; T sum; n = A.nrow(); // Cholesky decompose A = L*trans(L) for(i = 0; i < n; i++) { for(j = i; j < n; j++) { sum = A(i,j); for(k = i-1; k >=0; k--) { sum = sum - A(i,k)*A(j,k); } if(i == j) { if(sum <= T(0.0)) { throw(AipsError("CholeskyDecomp: Matrix is" "not positive definite")); } diag(i) = sqrt(sum); } else { A(j,i) = sum/diag(i); } } } } template void CholeskySolve(Matrix &A, Vector &diag, Vector &b, Vector &x) { // Solve linear equation A*x = b, where A positive-definite symmetric. // On input, A contains Cholesky factor L in its low triangle except the // diagonal elements which are in vector diag. On return x contains the // solution. b and x can be the same vector to save memory space. Int i, k, n; T sum; n = A.nrow(); // Ensure solution vector has same length as input vector x.resize(b.shape()); // solve by forward and backward substitution. // L*y = b for(i = 0; i < n; i++) { sum = b(i); for(k = i-1; k >=0; k--) { sum = sum - A(i,k)*x(k); } x(i) = sum/diag(i); } // trans(L)*x = y for(i = n-1; i >= 0; i--) { sum = x(i); for(k = i+1; k < n; k++) { sum = sum - A(k,i)*x(k); } x(i) = sum/diag(i); } } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/MatrixSolver.cc000066400000000000000000000110311476623553700221550ustar00rootroot00000000000000//# MatrixSolver.cc: Abstract base class for solvers of AX=B //# Copyright (C) 1994,1995,1999,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MatrixSolver::MatrixSolver():SolTolerance(0.0), MaxIterations(0), solved(False), gain(1.0){} MatrixSolver::MatrixSolver(const MatrixSolver & other) { AMatrix.reference((Matrix &)other.AMatrix); BVector.reference((Vector &)other.BVector); RVector.reference((Vector &)other.RVector); XVector.reference((Vector &)other.XVector); BNorm=other.BNorm; RNorm=other.RNorm; solved=other.solved; MaxIterations=other.MaxIterations; SolTolerance=other.SolTolerance; gain=other.gain; } MatrixSolver::MatrixSolver(const Matrix & amatrix, const Vector & bvector) : SolTolerance(0.0), MaxIterations(0), solved(False), gain(1.0) { AMatrix.reference((Matrix &)amatrix); BVector.reference((Vector &)bvector); XVector.resize(AMatrix.shape()(1)); RVector.resize(bvector.shape()); BNorm=norm(BVector); RNorm=BNorm; } void MatrixSolver::setAB(const Matrix & amatrix, const Vector & bvector) { AMatrix.reference((Matrix &)amatrix); BVector.reference((Vector &)bvector); XVector.resize(AMatrix.shape()(1)); RVector.resize(bvector.shape()); BNorm=norm(BVector); RNorm=BNorm; } void MatrixSolver::setX(const Vector & xvector) { XVector.reference((Vector &)xvector); } MatrixSolver & MatrixSolver::operator=(const MatrixSolver & other) { if (this==&other) return *this; AMatrix.reference((Matrix &)other.AMatrix); BVector.reference((Vector &)other.BVector); RVector.reference((Vector &)other.RVector); XVector.reference((Vector &)other.XVector); BNorm=other.BNorm; RNorm=other.RNorm; solved=other.solved; MaxIterations=other.MaxIterations; SolTolerance=other.SolTolerance; gain=other.gain; return *this; } // Virtual destructor MatrixSolver::~MatrixSolver() {} // Virtual solve method Bool MatrixSolver::solve() {return False;} // Returning the residual vector is a general operation. const Vector & MatrixSolver::getResidual() { // Calculate residual vector RVector=BVector-product(AMatrix, XVector); // Calculate norm of RVector RNorm = norm(RVector); return RVector; } const Vector & MatrixSolver::getSolution() { return XVector; } // Determine if the solution has small enough residual vector. Bool MatrixSolver::accurateSolution() { LogMessage message(LogOrigin("MatrixSolver", "accurateSolution")); // Calculate norm of RVector assuming that RVector is current RNorm = norm(RVector); // Now determine if the residual vector norm is less than the // Solution tolerance times the original BVector norm. ostringstream o;o<<"MatrixSolver: Norms of initial and residual vectors "<< BNorm<<", "< #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN typedef Float FType; // floating type (Float, Double) // // MatrixSolver.h: the base class for solvers of linear equations AX=B // // // // // //
      • Matrix, Vector // // // // The MatrixSolver class name reflects its use as the base class for solving // Linear Equations of the form AX=B. This class is purely virtual // and provides the essential implementation for derived solvers // classes. // // // // The MatrixSolver class is a purely virtual base class. The programmer needs // to define the following functions in a class derived from MatrixSolver: //
          //
        1. the derived destructor. //
        2. void setImageAndPsf(const Array & image, const // Array & psf); Set the image and the Point Spread Function // (beam). Setting this should reset the internal state, e.g. // CurrentIter()==0. //
        3. Bool solve(); Perform solution of AX=B. // Returns True if algorithm has converged or stop criterium reached. //
        //
        // // // class MatrixSolver { public: // Default Constructor MatrixSolver(); // Copy Constructor MatrixSolver(const MatrixSolver & other); // Create a MatrixSolver from a matrix A and a Vector B // A and B are accessed by reference, so do not // modify them during the lifetime of the MatrixSolver MatrixSolver(const Matrix & A, const Vector & B); // Virtual destructor: calls all derived class destructors virtual ~MatrixSolver(); // Assignment operator: uses reference semantics, i.e., it // references the internal arrays of other MatrixSolver & operator=(const MatrixSolver & other); // Set A matrix and B vector void setAB(const Matrix & A, const Vector & B); // Set initial value of X void setX(const Vector & X); // Solve for the X vector. virtual Bool solve(); // Is the current solution good enough? Bool accurateSolution(); // Return residual vector B-AX const Vector & getResidual(); // Return solution vector const Vector & getSolution(); // Set the tolerance for solution void setTolerance(FType tol); // Return the tolerance for solution FType Tolerance(); // Set the maximum number of iterations. void setMaxIters(uInt maxiters); // Return the maximum number of iterations. uInt MaxIters(); // Set the gain for solution void setGain(FType g); // Return the gain for solution FType Gain(); // Set status of solution void setSolved(Bool s); // Return status of solution Bool Solved(); // Return norm of solution i.e. ||B-AX|| FType getNorm(); protected: LogSink logSink_p; virtual LogSink& logSink() {return logSink_p;} // the A matrix data member Matrix AMatrix; // the constraint vector data member Vector BVector; // The residual vector data member Vector RVector; // The solution vector data member Vector XVector; // The solution norm i.e. ||B-AX|| FType RNorm; // The data norm i.e. ||B|| FType BNorm; private: // Tolerance for solution i.e. ||B-AX||/||B|| must be less than this FType SolTolerance; // Maximum number of iterations uInt MaxIterations; // Has a solution been found? Bool solved; // Gain FType gain; }; inline void MatrixSolver::setTolerance(FType tol) {SolTolerance=tol;} inline FType MatrixSolver::Tolerance() {return SolTolerance;} inline void MatrixSolver::setMaxIters(uInt maxiters) {MaxIterations = maxiters;} inline uInt MatrixSolver::MaxIters() {return MaxIterations;} inline void MatrixSolver::setGain(FType g) {gain=g;} inline FType MatrixSolver::Gain() {return gain;} inline void MatrixSolver::setSolved(Bool s) {solved=s;} inline Bool MatrixSolver::Solved() {return solved;} inline FType MatrixSolver::getNorm() {return RNorm;} } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/MedianSlider.cc000066400000000000000000000133501476623553700220640ustar00rootroot00000000000000//# MedianSlider.h: Optimized sliding-median computator //# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include //# for memcpy with gcc-4.3 namespace casacore { //# NAMESPACE CASACORE - BEGIN MedianSlider::MedianSlider () : buf(0),index(0),valid(0) { } MedianSlider::MedianSlider ( int hw ) { halfwin = hw; fullwin = hw*2+1; index = new uInt[fullwin]; buf = new Float[fullwin]; valid = new Bool[fullwin]; // buffer initially all-null and totally invalid for( uInt i=0; i &d,const Vector &flag ) { Float med=0; for( uInt i=0; i &d ) { Float med=0; for( uInt i=0; i= fullwin ) ibuf = 0; if( val_out ) // A) valid outgoing datum... { if( val_in ) // A.1) ..replaced by a valid datum { if( doutdin ) // inserting smaller value { uInt j=0; // skip indices up to incoming value for( ; j= m ) c2++; } if( std::abs(c1-c2) > 1 ) { throw(AipsError("MedianSlider::assure() failed")); return False; } return True; } } //# NAMESPACE CASACORE - END casacore-3.7.1/scimath/Mathematics/MedianSlider.h000066400000000000000000000107401476623553700217260ustar00rootroot00000000000000//# MedianSlider.h: Optimized sliding-median computator //# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_MEDIANSLIDER_H #define SCIMATH_MEDIANSLIDER_H //#! Includes go here #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // Class to compute sliding median // // // // // // MedianSlider is a class for efficient computing of sliding medians. // // // // // // // Flagging Agents make extended use of sliding medians. // // // //
      • think about a 2D sliding median // class MedianSlider { public: MedianSlider (); MedianSlider ( int halfwin ); MedianSlider ( const MedianSlider &other ); ~MedianSlider (); MedianSlider & operator = ( const MedianSlider &other ); void cleanup (); // Adds a datum to the slider. Once the window is full, newer values will // push out older values. Returns the new median value. // If flag is set to true, adds a "flagged" datum, one which takes // up space in the window but is skipped during median computations. Float add ( Float d,Bool flag=False ); // Adds a flagged datum Float add () { return add(0,True); } // Adds N flagged datums Float next ( uInt n=1 ); // Adds several datums at once (with corresponding flags) Float add ( const Vector &d,const Vector &flag ); // Adds several non-flagged datums at once Float add ( const Vector &d ); // Returns the number of values currently in the window. This is less // than the window width initially. // Int size (); // Returns the number of non-flagged values in window Int nval (); // Returns the current median value Float median (); // Returns a previous value (from n steps ago) from the sliding window Float prevVal ( uInt n,Bool &flag ); // Returns value from midpoint (center) of window, possibly with flag Float midpoint ( Bool &flag ); Float midpoint () { Bool dum; return midpoint(dum); } // Returns the difference between the current median and the value // at window center. Optionally, also returns flag of median center Float diff ( Bool &flag ) { return midpoint(flag) - median(); } Float diff () { Bool dum; return diff(dum); } // returns total memory usage (in bytes) for a given halfwin size static size_t objsize ( int halfwin ) { return sizeof(MedianSlider)+(sizeof(Float)+sizeof(uInt)+sizeof(Bool))*(halfwin*2+1); } // For testing purposes only: verifies current value of median. // Throws an exception if it fails. Bool assure (); private: uInt halfwin,fullwin; Float *buf; uInt *index; Bool *valid; uInt ibuf,nind; }; inline Int MedianSlider::nval () { return nind; } inline Float MedianSlider::median () { if( !nind ) return 0; return nind%2 ? buf[ index[nind/2] ] : ( buf[ index[nind/2-1] ] + buf[ index[nind/2] ] )/2; // return nind%2 ? buf[ index[nind/2] ] // : buf[ index[nind/2-1] ]; } inline Float MedianSlider::midpoint ( Bool &flag ) { return prevVal(halfwin+1,flag); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/NNGridder.h000066400000000000000000000045661476623553700212130ustar00rootroot00000000000000//# NNGridder.h: Definition for Nearest Neighbour Gridder //# Copyright (C) 1996,1997,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_NNGRIDDER_H #define SCIMATH_NNGRIDDER_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A non-negative gridding class // template class NNGridder : public Gridder { public: NNGridder(const IPosition& shape, const Vector& scale, const Vector& offset); virtual ~NNGridder() {} virtual Bool grid(Array& gridded, const Vector& position, const Range& value); virtual Bool degrid(const Array& gridded, const Vector& position, Range& value); protected: virtual Range correctionFactor1D(Int loc, Int len); Vector loc; protected: //# Make members of parent classes known. using Gridder::ndim; using Gridder::offsetVec; using Gridder::fillCorrectionVectors; using Gridder::onGrid; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Mathematics/NNGridder.tcc000066400000000000000000000055451476623553700215330ustar00rootroot00000000000000//# NNGridder.cc: Nearest Neighbour Gridder //# Copyright (C) 1996,1997,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_NNGRIDDER_TCC #define SCIMATH_NNGRIDDER_TCC #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Constructor template NNGridder::NNGridder(const IPosition& shape, const Vector& scale, const Vector& offset) : Gridder(shape, scale, offset) { fillCorrectionVectors(); loc.resize(ndim); loc=0; } // Grid a value by moving to nearest neighbour template Bool NNGridder::grid(Array &gridded, const Vector& position, const Range& value) { loc=location(loc, position); loc-=offsetVec; if(onGrid(loc)) { gridded(loc)+=value; return True; } else { return False; } } // Degrid a value by taking value of nearest neighbour template Bool NNGridder::degrid(const Array& gridded, const Vector& position, Range& value) { loc=location(loc, position); loc-=offsetVec; if(onGrid(loc)) { value=gridded(loc); return True; } else { return False; } } // Correction factor for 1 dimension. This is the value that // must be divided to get a correct flux. template Range NNGridder::correctionFactor1D(Int loc, Int len) { Int offset=loc-len/2; if(offset!=0) { Double arg=C::pi*Double(offset)/Double(len); return sin(arg)/arg; } else { return 1.0; } } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/NNLSMatrixSolver.cc000066400000000000000000000074411476623553700226620ustar00rootroot00000000000000//# NNLSMatrixSolver.cc: concrete class for NNLS solvers of AX=B //# Copyright (C) 1994,1995,1999,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN #if !defined(NEED_FORTRAN_UNDERSCORES) #define NEED_FORTRAN_UNDERSCORES 1 #endif #if NEED_FORTRAN_UNDERSCORES #define nnls nnls_ #endif extern "C" { void nnls(FType*,int*,int*,int*,FType*,FType*,float*,FType*,FType*,int*,int*,int*); } // Default Constructor NNLSMatrixSolver::NNLSMatrixSolver(): MatrixSolver() {} // Copy Constructor NNLSMatrixSolver::NNLSMatrixSolver(const NNLSMatrixSolver & other) : MatrixSolver(other) {} // Create a NNLSMatrixSolver from a matrix A and a Vector B // A and B are accessed by reference, so don't // modify them during the lifetime of the NNLSMatrixSolver NNLSMatrixSolver::NNLSMatrixSolver(const Matrix & A, const Vector & B) : MatrixSolver(A,B) {} // Destructor NNLSMatrixSolver::~NNLSMatrixSolver() {} Bool NNLSMatrixSolver::solve() // Solve AX=B for X { LogMessage message(LogOrigin("NNLSMatrixSolver","solve")); Bool delete_it; FType *a_data = AMatrix.getStorage(delete_it); FType *x_data = XVector.getStorage(delete_it); FType *b_data = BVector.getStorage(delete_it); int nflux=XVector.nelements(); int ndata=BVector.nelements(); float rnorm=0.0; FType *w=new FType[nflux]; FType *zz=new FType[ndata]; int *index=new int[nflux]; int itmax=MaxIters(); if(itmax==0) itmax=3*nflux; int mode=0; // Call Fortran NNLS routine nnls(a_data,&ndata,&ndata,&nflux,b_data,x_data,&rnorm,w,zz,index,&itmax, &mode); RVector=BVector-product(AMatrix,XVector); // Update residual vector if (mode==2) { ostringstream o;o<<"dimensions set up incorrectly"; message.priority(LogMessage::SEVERE); message.message(o);logSink().post(message); setSolved(False); return Solved(); } if (mode==3) { ostringstream o;o<<"Exceeded number of iterations"; message.priority(LogMessage::SEVERE); message.message(o);logSink().post(message); setSolved(False); return Solved(); } if(accurateSolution()) { ostringstream o;o<<"Solution acheived"; message.message(o);logSink().post(message); setSolved(True); } else { ostringstream o;o<<"Solution not formally accurate enough"; message.message(o);logSink().post(message); setSolved(False); } return Solved(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/scimath/Mathematics/NNLSMatrixSolver.h000066400000000000000000000061401476623553700225170ustar00rootroot00000000000000//# NNLSMatrixSolver.h: the base class for NNLS solvers of AX=B //# Copyright (C) 1994,1995,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_NNLSMATRIXSOLVER_H #define SCIMATH_NNLSMATRIXSOLVER_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // NNLSMatrixSolver.h: the base class for NNLS solvers of linear equations AX=B // // // // // //
      • Matrix, Vector // // // // NNLS stands for Projection Onto Convex Sets. The idea is very simple: to // find a solution to AX=B simply take the residual vector B-AX and operate // on it to keep only the bits that obey some constraint e.g. positivity. // Add this part to the current estimate of the solution vector and iterate. // Both CLEAN and Gerchberg-Saxon are NNLS algorithms. If the projection // Operators are convex then the process is guaranteed to converge (Youla, 1970). // // // // NNLSMatrixSolver is a complete class. To use it, simply add Operators //
          //
        1. I do not know how to do this yet but it should look something like // NNLSMatrixSolver NNLS(amatrix, bvector);NNLS.addOperator(foo); //
        //
        // // //
      • Add list of operators // class NNLSMatrixSolver : public MatrixSolver { public: // Default Constructor NNLSMatrixSolver(); // Copy Constructor NNLSMatrixSolver(const NNLSMatrixSolver & other); // Create a NNLSMatrixSolver from a matrix A and a Vector B // A and B are accessed by reference, so don't // modify them during the lifetime of the NNLSMatrixSolver NNLSMatrixSolver(const Matrix & A, const Vector & B); // Destructor ~NNLSMatrixSolver(); // Solve for the X vector. Bool solve(); protected: private: }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/NumericTraits.cc000066400000000000000000000040471476623553700223200ustar00rootroot00000000000000//# NumericTraits.cc: //# Copyright (C) 1997 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN const Double & NumericTraits::epsilon = FLT_EPSILON; const Double & NumericTraits::epsilon = DBL_EPSILON; const Double & NumericTraits::epsilon = FLT_EPSILON; const Double & NumericTraits::epsilon = DBL_EPSILON; const Double & NumericTraits::minimum = FLT_MIN; const Double & NumericTraits::minimum = DBL_MIN; const Double & NumericTraits::minimum = FLT_MIN; const Double & NumericTraits::minimum = DBL_MIN; const Double & NumericTraits::maximum = FLT_MAX; const Double & NumericTraits::maximum = DBL_MAX; const Double & NumericTraits::maximum = FLT_MAX; const Double & NumericTraits::maximum = DBL_MAX; } //# NAMESPACE CASACORE - END casacore-3.7.1/scimath/Mathematics/NumericTraits.h000066400000000000000000000343401476623553700221610ustar00rootroot00000000000000//# NumericTraits.h: Defines relationships between numeric data types //# Copyright (C) 1996,1997,1998,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_NUMERICTRAITS_H #define SCIMATH_NUMERICTRAITS_H //# Include files #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Relationships between numeric data types // // // // // // // A trait is a characteristic feature. NumericTraits defines relationships // between and characteristics of Numeric data types. // // // // This templated class contains a number of typedefs and definitions that // describe the relationship between different numeric data types and there // characteristics. Its use is in templated classes either where the use of one // type implictly implies the use of a corresponding one or where a // characteristic value differs with templating argument. Use of this class // often avoids the need for double templating. // // Currently this class defines the following relationships: //
        //
        value_type //
        The template type itself. The name value_type is the // C++ standard (e.g. DComplex::value_type equals // double) //
        BaseType //
        The numeric base type. I.e. Double for Double // and DComplex; Float for Float and // Complex //
        ConjugateType //
        The corresponding complex type for a real type, and real type // for a complex type. It is the type of the result if a Fourier // Transform was to be done. //
        PrecisionType //
        The Type of the next higher numerical precision. I.e. Double // or DComplex //
        // // And the following characteristics: //
        //
        epsilon //
        A Double containing the smallest value such that 1+epsilon is different // from 1. //
        minimum //
        A Double containing the smallest positive representable number, // excluding denormalised numbers. //
        maximum //
        A Double containing the largest representable number. //
        size() //
        The number of numeric values in the templated entity. It will be // 2 for complex numbers; 1 for real numbers. //
        setImag(T &other, const BaseType &val) //
        Set an imaginary part (for complex numbers) or a NOP (for reals). //
        getValue(T &other, const uInt n) //
        Get the n%size()-th value in the argument. // For complex numbers the sequence is real, imaginary part. //
        setValue(T &other, const BaseType &val, const uInt n) //
        Set the n%size()-th value in the argument. // For complex numbers the sequence is real, imaginary part. //
        // // For complex numbers these values are applicable to the real or imaginary // components separately. // // The use of this class is best illustrated in a number of examples. // // A default template declaration is required by the C++ standard. // It should never be used, except through the specialisations. // The default types for ConjugateType and PrecisionType are deliberatly set to // a non-numeric type to further discourage the use of the non-specialized // class defined below. It also helps when using this class with the Sun native // compiler. // The specialized instantiations seem to have a name with // an appended code. This is only for cxx2html reasons. The name is in all // cases NumericTraits // // // This class is implemented as a number of specialisations for the // following data types. //
          //
        • Float //
        • Double //
        • Complex //
        • DComplex //
        // This class should not be used with other template types and does nothing // except return its template type if it is used. ie.
        // NumericTraits\::ConjugateType returns // Char and
        // NumericTraits\::PrecisionType returns // Char
        // NumericTraits\::epsilon is undefined
        // NumericTraits\::minimum is undefined
        // NumericTraits\::maximum is undefined //
        // // //

        Example 1:

        // Suppose you are writing a templated class that needs to do Fourier // Transforms. The FFTServer class can do FFT's of Float or Double data // types, but you need to doubly template it on the conjugate data type. To // avoid having the conjugate data type appear as a template in the class // you are writing you can use the ConjugateType typedef. // // template class myClass { // private: // FFTServer::ConjugateType> server; // } // // The ConjugateType transforms //
          //
        • Float -> Complex //
        • Double -> DComplex //
        • Complex -> Float //
        • DComplex -> Double //
        // //

        Example 2:

        // Suppose you have a templated numerical integrator class. Because the // individual samples can be negative it is possible to add two numbers // of nearly equal magnitude but opposite sign and lose precision // considerably. One way to combat this is to make the accumulator variable // the next higher precision numerical type. The PrecisionType typedef // defines what type this is // // template class Integrator { // private: // NumericTraits::PrecisionType accumulator; // } // // The PrecisionType transforms //
          //
        • Float -> Double //
        • Double -> Double //
        • Complex -> DComplex //
        • DComplex -> DComplex //
        //

        Example 3:

        // Suppose you have a templated class that needs to use the allNear // functions from // ArrayMath // to determine if a templated Array is near // one. The tolerance argument to the allNear function will depend on the // template type and this is not known until the template is instantiated. The // epsilon trait can be used to supply this value. // // template void myClass::myFunction(Array & aArray) { // if (allNear(aArray, T(1), NumericTraits::epsilon)) // return; // // Do something // } // //
        //
        NumericTraits\::epsilon //
        is FLT_EPSILON for Float and Complex types and DBL_EPSILON for Double // and DComplex data types. //
        NumericTraits\::minimum //
        is FLT_MIN for Float and complex Types and DBL_MIN for Double and // DComplex data types. //
        NumericTraits\::maximum //
        is FLT_MAX for Float and complex Types and DBL_MAX for Double and // DComplex data types. //
        // See the C class/namespace // for the values of these variables. //
        // // // This is a nice way to make the Convolver class singly templated (as it // should be), even though the FFTServer it contains is doubly templated. // // // //
      • This class does not throw any exceptions // // // //
      • Nothing (I hope!) // // template class NumericTraits { public: // Template argument typedef T value_type; // Numeric type typedef Char BaseType; // Conjugate (real<->complex) type typedef Char ConjugateType; // Higher precision type (Float->Double) typedef Char PrecisionType; // Relevant minimum and maximum numbers // static const Double & epsilon; static const Double & minimum; static const Double & maximum; // // Number of relevant numeric values static uInt size() { return 0; } // Set the imaginary part of a complex value only (a NOP for reals) static void setImag(T &, const BaseType &) {;} // Get the n%size()-th numeric value static BaseType getValue(const T &, const uInt) { return 0; } // Set the n%size()-th numeric value static void setValue(T &, const BaseType &, const uInt) {;} }; #if defined NumericTraits_F #undef NumericTraits_F #endif #define NumericTraits_F NumericTraits // NumericTraits specialization for Float template <> class NumericTraits_F { public: // Template argument typedef Float value_type; // Numeric type typedef Float BaseType; // Conjugate (real<->complex) type typedef Complex ConjugateType; // Higher precision type (Float->Double) typedef Double PrecisionType; // Relevant minimum and maximum numbers // static const Double & epsilon; static const Double & minimum; static const Double & maximum; // // Number of relevant numeric values static uInt size() { return 1; } // Set the imaginary part of a complex value only (a NOP for reals) static void setImag(value_type &, const BaseType &) {;} // Get the n%size()-th numeric value static BaseType getValue(const value_type &other, const uInt) { return other; } // Set the n%size()-th numeric value static void setValue(value_type &other, const BaseType &val, const uInt) { other = val; } }; #undef NumericTraits_F #if defined NumericTraits_D #undef NumericTraits_D #endif #define NumericTraits_D NumericTraits // NumericTraits specialization for Double template <> class NumericTraits_D { public: // Template argument typedef Double value_type; // Numeric type typedef Double BaseType; // Conjugate (real<->complex) type typedef DComplex ConjugateType; // Higher precision type (Float->Double) typedef Double PrecisionType; // Relevant minimum and maximum numbers // static const Double & epsilon; static const Double & minimum; static const Double & maximum; // // Number of relevant numeric values static uInt size() { return 1; } // Set the imaginary part of a complex value only (a NOP for reals) static void setImag(value_type &, const BaseType &) {;} // Get the n%size()-th numeric value static BaseType getValue(const value_type &other, const uInt) { return other; } // Set the n%size()-th numeric value static void setValue(value_type &other, const BaseType &val, const uInt) { other = val; } }; #undef NumericTraits_D #if defined NumericTraits_C #undef NumericTraits_C #endif #define NumericTraits_C NumericTraits // NumericTraits specialization for Complex template <> class NumericTraits_C { public: // Template argument typedef Complex value_type; // Numeric type typedef Float BaseType; // Conjugate (real<->complex) type typedef Float ConjugateType; // Higher precision type (Float->Double) typedef DComplex PrecisionType; // Relevant minimum and maximum numbers // static const Double & epsilon; static const Double & minimum; static const Double & maximum; // // Number of relevant numeric values static uInt size() { return 2; } // Set the imaginary part of a complex value only (a NOP for reals) static void setImag(value_type &other, const BaseType &val) { other = value_type(other.real(), val); } // Get the n%size()-th numeric value static BaseType getValue(const value_type &other, const uInt n) { return ((n%2 == 0) ? other.real() : other.imag()); } // Set the n%size()-th numeric value static void setValue(value_type &other, const BaseType &val, const uInt n) { other = (n%2 == 0) ? value_type(val, other.imag()) : value_type(other.real(), val); } }; #undef NumericTraits_C #if defined NumericTraits_DC #undef NumericTraits_DC #endif #define NumericTraits_DC NumericTraits // NumericTraits specialization for DComplex template <> class NumericTraits_DC { public: // Template argument typedef DComplex value_type; // Numeric type typedef Double BaseType; // Conjugate (real<->complex) type typedef Double ConjugateType; // Higher precision type (Float->Double) typedef DComplex PrecisionType; // Relevant minimum and maximum numbers // static const Double & epsilon; static const Double & minimum; static const Double & maximum; // // Number of relevant numeric values static uInt size() { return 2; } // Set the imaginary part of a complex value only (a NOP for reals) static void setImag(value_type &other, const BaseType &val) { other = value_type(other.real(), val); } // Get the n%size()-th numeric value static BaseType getValue(const value_type &other, const uInt n) { return ((n%2 == 0) ? other.real() : other.imag()); } // Set the n%size()-th numeric value static void setValue(value_type &other, const BaseType &val, const uInt n) { other = (n%2 == 0) ? value_type(val, other.imag()) : value_type(other.real(), val); } }; #undef NumericTraits_DC } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/NumericTraits2.h000066400000000000000000000064351476623553700222470ustar00rootroot00000000000000//# NumericTraits2.h: Specialisations of the NumericTraits class //# Copyright (C) 1996,1997,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_NUMERICTRAITS2_H #define SCIMATH_NUMERICTRAITS2_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // A specialisation for T=Float of the NumericTraits class // // template <> class NumericTraits { public: typedef Complex ConjugateType; typedef Double PrecisionType; static const Double & epsilon; static const Double & minimum; static const Double & maximum; }; // A specialisation for T=Double of the NumericTraits class // // template <> class NumericTraits { public: typedef DComplex ConjugateType; typedef Double PrecisionType; static const Double & epsilon; static const Double & minimum; static const Double & maximum; }; // A specialisation for T=Complex of the NumericTraits class // // template <> class NumericTraits { public: typedef Float ConjugateType; typedef DComplex PrecisionType; static const Double & epsilon; static const Double & minimum; static const Double & maximum; }; // A specialisation for T=DComplex of the NumericTraits class // // // // // // This file contains specialisations of the templated NumericTraits class. // See the description in // NumericTraits.h // for a summary of the purpose and usage of this class and its // specialisations. // template <> class NumericTraits { public: typedef Double ConjugateType; typedef DComplex PrecisionType; static const Double & epsilon; static const Double & minimum; static const Double & maximum; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/RigidVector.h000066400000000000000000000230061476623553700216060ustar00rootroot00000000000000//# RigidVector.h: Fast Vector classes with fixed (templated) length //# Copyright (C) 1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_RIGIDVECTOR_H #define SCIMATH_RIGIDVECTOR_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# forward template class SquareMatrix; // Fast Vector classes with fixed (templated) length // // // // //
      • Vector //
      • Complex // // // // RigidVector is a vector with a size fixed at compile time, i.e. Rigid // as compared to the normal Vector class. // // // // RigidVector is a specialized Vector class for short (<25 elements) vectors. // It has a size fixed at compile time, avoids new and delete and uses // copy semantics throughout. // Unlike Vectors, RigidVectors have fixed zero origin and no strides, // allowing fast indexing. // The more common mathematical operations are defined for RigidVector, // allowing element by element arithmetic, innerproduct and matrix // multiplication (by a SquareMatrix). Conversion to and from normal // vectors is provided. // // // // // // Create two RigidVectors // RigidVector rv1(1.0,2.0,3.0),rv2(3.0,2.0,1.0); // // Compute sum // RigidVector rv3=rv1+rv2; // // Compute innerproduct // Float inprod=rv1*rv2; // // Write out results // cout << "rv1+rv2="<< rv3 <<", rv1*rv2="<< inprod< // // // // The standard Vector class is rather inefficient for short vectors, this // class is designed for speed and simplicity. // // // //
      • this class is meant for computation and assumes operators // +,-,* to be defined. // // // //
      • no exceptions // // // //
      • not all operations defined for Vectors are defined //
      • default implementation of innerProduct is wrong for Complex vectors // template class RigidVector { //# friends (could be out of line if compiler accepted that) // Add two RigidVectors. friend RigidVector operator+(const RigidVector& l, const RigidVector& r) { RigidVector result=l; return result+=r; } // Subtract two RigidVectors. friend RigidVector operator-(const RigidVector& l, const RigidVector& r) { RigidVector result=l; return result-=r; } // The innerproduct of 2 RigidVectors. friend T operator*(const RigidVector& l, const RigidVector& r) { T sum=T(0); for (Int i=0; i operator*(const T& f, const RigidVector& v) { RigidVector r(v); return r*=f; } // Multiply a RigidVector by a scalar. friend RigidVector operator*(const RigidVector& v, const T& f) { RigidVector r(v); return r*=f; } // Write out a RigidVector using the Vector output method. friend ostream& operator<<(ostream& os, const RigidVector& v) { os << v.vector(); return os; } // Special matrix multiply of Complex matrix * Float vector. friend RigidVector operator*(const SquareMatrix& m, const RigidVector& v); public: // RigidVector(Int dummy) { // for (Int i=0; i & v) { for (Int i=0; i& v) { for (Int i=0; i& operator=(const RigidVector& v) { for (Int i=0; i& operator=(const Vector& v) { for (Int i=0; i& operator=(const T& c) { for (Int i=0; i& operator-() { for (Int i=0; i& operator+=(const RigidVector& v) { for (Int i=0; i& operator*=(const RigidVector& v) { for (Int i=0; i& operator-=(const RigidVector& v) { for (Int i=0; i& operator*=(const T& val) { for (Int i=0; i& operator*=(const SquareMatrix& m); // Indexing by reference T& operator()(Int i) { return v_p[i];} // Indexing by const reference const T& operator()(Int i) const { return v_p[i];} //# Get const access to the underlying c-array //#const T*& cArray() const { return v_p;} // Convert to a regular Vector Vector vector() const { Vector v(n); for (Int i=0; i sqrt(const RigidVector& v); // // The following are needed for Image // static IPosition shape() {return IPosition(1,n);} // static void* newCopyInfo (const TableRecord& record, // const IPosition& sourceElementShape); // static void deleteCopyInfo (void*); // static void set (void* copyInfo, void* out, // const Array& in, // const IPosition& shape); // static void get (void* copyInfo, Array& out, // const void* in, // const IPosition& shape); protected: T v_p[n]; }; // Mathematical operations involving RigidVectors // //#Fails to compile //#// Multiply vector by matrix. //#template //#inline RigidVector operator*(const SquareMatrix& m, //# const RigidVector& v) { //# RigidVector result(v); //# return result*=m; //#} // Multiply vector by matrix. inline RigidVector operator*(const SquareMatrix& m, const RigidVector& v) { RigidVector result(v); return result*=m; } // Multiply vector by matrix. inline RigidVector operator*(const SquareMatrix& m, const RigidVector& v) { RigidVector result(v); return result*=m; } // } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Mathematics/RigidVector.tcc000066400000000000000000000066701476623553700221400ustar00rootroot00000000000000//# RigidVector.cc: Fast Vector classes with fixed (templated) length //# Copyright (C) 1996,1998,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_RIGIDVECTOR_TCC #define SCIMATH_RIGIDVECTOR_TCC #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template RigidVector& RigidVector::operator*=(const SquareMatrix& m) { switch (m.type_p) { case SquareMatrix::ScalarId: { for (Int i=0; i::Diagonal: { for (Int i=0; i::General: default: { T v[n], tmp; Int i; for (i=0; i RigidVector sqrt(const RigidVector& v) { RigidVector tmp; for (Int i=0; i // template // void* RigidVector::newCopyInfo (const TableRecord&, const IPosition&) // { return 0; } // template // void RigidVector::deleteCopyInfo (void*) // {} // // For set and get, we don't need to do anything sophisticated // template // void RigidVector::set (void* ci, void* vout, // const Array& in, // const IPosition& shape) // { // Array >& out = *(Array >*)vout; // if (shape.nelements() == 1 && shape(0) == n) { // retypedArrayEngineSet (out, in); // }else{ // throw (DataManError ("RigidVector::set")); // } // } // template // void RigidVector::get (void* ci, Array& out, // const void* vin, // const IPosition& shape) // { // const Array >& in = *(const Array >*)vin; // if (shape.nelements() == 1 && shape(0) == n) { // retypedArrayEngineGet (out, in); // }else{ // throw (DataManError ("RigidVector::get")); // } // } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/RigidVector2.cc000066400000000000000000000046111476623553700220270ustar00rootroot00000000000000//# RigidVector2.cc: explicit instantiations for RigidVector //# Copyright (C) 1996,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN RigidVector operator*(const SquareMatrix& m, const RigidVector& v) { Complex v0,v1,v2,v3; switch (m.type_p) { case SquareMatrix::ScalarId: { v0=v.v_p[0]*m.a_p[0][0]; v1=v.v_p[1]*m.a_p[0][0]; v2=v.v_p[2]*m.a_p[0][0]; v3=v.v_p[3]*m.a_p[0][0]; } break; case SquareMatrix::Diagonal: { v0=v.v_p[0]*m.a_p[0][0]; v1=v.v_p[1]*m.a_p[1][1]; v2=v.v_p[2]*m.a_p[2][2]; v3=v.v_p[3]*m.a_p[3][3]; } break; case SquareMatrix::General: { v0=m.a_p[0][0]*v.v_p[0]+m.a_p[0][1]*v.v_p[1]+m.a_p[0][2]*v.v_p[2]+ m.a_p[0][3]*v.v_p[3]; v1=m.a_p[1][0]*v.v_p[0]+m.a_p[1][1]*v.v_p[1]+m.a_p[1][2]*v.v_p[2]+ m.a_p[1][3]*v.v_p[3]; v2=m.a_p[2][0]*v.v_p[0]+m.a_p[2][1]*v.v_p[1]+m.a_p[2][2]*v.v_p[2]+ m.a_p[2][3]*v.v_p[3]; v3=m.a_p[3][0]*v.v_p[0]+m.a_p[3][1]*v.v_p[1]+m.a_p[3][2]*v.v_p[2]+ m.a_p[3][3]*v.v_p[3]; } } return RigidVector(v0,v1,v2,v3); } } //# NAMESPACE CASACORE - END casacore-3.7.1/scimath/Mathematics/SCSL.cc000066400000000000000000000357601476623553700203010ustar00rootroot00000000000000//# extern_fft.cc: C++ wrapper functions for FORTRAN FFT code //# Copyright (C) 1993,1994,1995,1997,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN #if ! defined(HAVE_SCSL) #define PN(a) #else #define PN(a) a extern "C" { void ccfft_(int*, int*, float*, float*, float*, float*, float*, int*); void zzfft_(int*, int*, double*, double*, double*, double*, double*, int*); } extern "C" { void scfft_(int*, int*, float*, float*, float*, float*, float*, int*); void dzfft_(int*, int*, double*, double*, double*, double*, double*, int*); void csfft_(int*, int*, float*, float*, float*, float*, float*, int*); void zdfft_(int*, int*, double*, double*, double*, double*, double*, int*); } extern "C" { void ccfftm_(int*, int*, int*, float*, float*, int*, float*, int*, float*, float*, int*); void zzfftm_(int*, int*, int*, double*, double*, int*, double*, int*, double*, double*, int*); } extern "C" { void scfftm_(int*, int*, int*, float*, float*, int*, float*, int*, float*, float*, int*); void dzfftm_(int*, int*, int*, double*, double*, int*, double*, int*, double*, double*, int*); void csfftm_(int*, int*, int*, float*, float*, int*, float*, int*, float*, float*, int*); void zdfftm_(int*, int*, int*, double*, double*, int*, double*, int*, double*, double*, int*); } extern "C" { void ccfft2d_(int*, int*, int*, float*, float*, int*, float*, int*, float*, float*, int*); void zzfft2d_(int*, int*, int*, double*, double*, int*, double*, int*, double*, double*, int*); } extern "C" { void scfft2d_(int*, int*, int*, float*, float*, int*, float*, int*, float*, float*, int*); void dzfft2d_(int*, int*, int*, double*, double*, int*, double*, int*, double*, double*, int*); void csfft2d_(int*, int*, int*, float*, float*, int*, float*, int*, float*, float*, int*); void zdfft2d_(int*, int*, int*, double*, double*, int*, double*, int*, double*, double*, int*); } extern "C" { void ccfft3d_(int*, int*, int*, int*, float*, float*, int*, int*, float*, int*, int*, float*, float*, int*); void zzfft3d_(int*, int*, int*, int*, double*, double*, int*, int*, double*, int*, int*, double*, double*, int*); } extern "C" { void scfft3d_(int*, int*, int*, int*, float*, float*, int*, int*, float*, int*, int*, float*, float*, int*); void dzfft3d_(int*, int*, int*, int*, double*, double*, int*, int*, double*, int*, int*, double*, double*, int*); void csfft3d_(int*, int*, int*, int*, float*, float*, int*, int*, float*, int*, int*, float*, float*, int*); void zdfft3d_(int*, int*, int*, int*, double*, double*, int*, int*, double*, int*, int*, double*, double*, int*); } #endif void SCSL::ccfft(Int PN(isign), Int PN(n), Float PN(scale), Complex* PN(x), Complex* PN(y), Float* PN(table), Float* PN(work), Int PN(isys)) { #if defined(HAVE_SCSL) DebugAssert(sizeof(Complex) == 2*sizeof(float), AipsError); ccfft_((int*) &isign, (int*) &n, (float*) &scale, (float*) x, (float*) y, (float*) table, (float*) work, (int*) &isys); #endif } void SCSL::ccfft(Int PN(isign), Int PN(n), Double PN(scale), DComplex* PN(x), DComplex* PN(y), Double* PN(table), Double* PN(work), Int PN(isys)) { #if defined(HAVE_SCSL) DebugAssert(sizeof(DComplex) == 2*sizeof(double), AipsError); zzfft_((int*) &isign, (int*) &n, (double*) &scale, (double*) x, (double*) y, (double*) table, (double*) work, (int*) &isys); #endif } void SCSL::scfft(Int PN(isign), Int PN(n), Float PN(scale), Float* PN(x), Complex* PN(y), Float* PN(table), Float* PN(work), Int PN(isys)) { #if defined(HAVE_SCSL) DebugAssert(sizeof(Complex) == 2*sizeof(float), AipsError); scfft_((int*) &isign, (int*) &n, (float*) &scale, (float*) x, (float*) y, (float*) table, (float*) work, (int*) &isys); #endif } void SCSL::scfft(Int PN(isign), Int PN(n), Double PN(scale), Double* PN(x), DComplex* PN(y), Double* PN(table), Double* PN(work), Int PN(isys)) { #if defined(HAVE_SCSL) DebugAssert(sizeof(DComplex) == 2*sizeof(double), AipsError); dzfft_((int*) &isign, (int*) &n, (double*) &scale, (double*) x, (double*) y, (double*) table, (double*) work, (int*) &isys); #endif } void SCSL::csfft(Int PN(isign), Int PN(n), Float PN(scale), Complex* PN(x), Float* PN(y), Float* PN(table), Float* PN(work), Int PN(isys)) { #if defined(HAVE_SCSL) DebugAssert(sizeof(Complex) == 2*sizeof(float), AipsError); csfft_((int*) &isign, (int*) &n, (float*) &scale, (float*) x, (float*) y, (float*) table, (float*) work, (int*) &isys); #endif } void SCSL::csfft(Int PN(isign), Int PN(n), Double PN(scale), DComplex* PN(x), Double* PN(y), Double* PN(table), Double* PN(work), Int PN(isys)) { #if defined(HAVE_SCSL) DebugAssert(sizeof(DComplex) == 2*sizeof(double), AipsError); zdfft_((int*) &isign, (int*) &n, (double*) &scale, (double*) x, (double*) y, (double*) table, (double*) work, (int*) &isys); #endif } void SCSL::ccfftm(Int PN(isign), Int PN(n), Int PN(lot), Float PN(scale), Complex* PN(x), Int PN(ldx), Complex* PN(y), Int PN(ldy), Float* PN(table), Float* PN(work), Int PN(isys)) { #if defined(HAVE_SCSL) DebugAssert(sizeof(Complex) == 2*sizeof(float), AipsError); ccfftm_((int*) &isign, (int*) &n, (int*) &lot, (float*) &scale, (float*) x, (int*) &ldx, (float*) y, (int*) &ldy, (float*) table, (float*) work, (int*) &isys); #endif } void SCSL::zzfftm(Int PN(isign), Int PN(n), Int PN(lot), Double PN(scale), DComplex* PN(x), Int PN(ldx), DComplex* PN(y), Int PN(ldy), Double* PN(table), Double* PN(work), Int PN(isys)) { #if defined(HAVE_SCSL) DebugAssert(sizeof(DComplex) == 2*sizeof(double), AipsError); zzfftm_((int*) &isign, (int*) &n, (int*) &lot, (double*) &scale, (double*) x, (int*) &ldx, (double*) y, (int*) &ldy, (double*) table, (double*) work, (int*) &isys); #endif } void SCSL::scfftm(Int PN(isign), Int PN(n), Int PN(lot), Float PN(scale), Float* PN(x), Int PN(ldx), Complex* PN(y), Int PN(ldy), Float* PN(table), Float* PN(work), Int PN(isys)) { #if defined(HAVE_SCSL) DebugAssert(sizeof(Complex) == 2*sizeof(float), AipsError); scfftm_((int*) &isign, (int*) &n, (int*) &lot, (float*) &scale, (float*) x, (int*) &ldx, (float*) y, (int*) &ldy, (float*) table, (float*) work, (int*) &isys); #endif } void SCSL::dzfftm(Int PN(isign), Int PN(n), Int PN(lot), Double PN(scale), Double* PN(x), Int PN(ldx), DComplex* PN(y), Int PN(ldy), Double* PN(table), Double* PN(work), Int PN(isys)) { #if defined(HAVE_SCSL) DebugAssert(sizeof(DComplex) == 2*sizeof(double), AipsError); dzfftm_((int*) &isign, (int*) &n, (int*) &lot, (double*) &scale, (double*) x, (int*) &ldx, (double*) y, (int*) &ldy, (double*) table, (double*) work, (int*) &isys); #endif } void SCSL::csfftm(Int PN(isign), Int PN(n), Int PN(lot), Float PN(scale), Complex* PN(x), Int PN(ldx), Float* PN(y), Int PN(ldy), Float* PN(table), Float* PN(work), Int PN(isys)) { #if defined(HAVE_SCSL) DebugAssert(sizeof(Complex) == 2*sizeof(float), AipsError); csfftm_((int*) &isign, (int*) &n, (int*) &lot, (float*) &scale, (float*) x, (int*) &ldx, (float*) y, (int*) &ldy, (float*) table, (float*) work, (int*) &isys); #endif } void SCSL::zdfftm(Int PN(isign), Int PN(n), Int PN(lot), Double PN(scale), DComplex* PN(x), Int PN(ldx), Double* PN(y), Int PN(ldy), Double* PN(table), Double* PN(work), Int PN(isys)) { #if defined(HAVE_SCSL) DebugAssert(sizeof(DComplex) == 2*sizeof(double), AipsError); dzfftm_((int*) &isign, (int*) &n, (int*) &lot, (double*) &scale, (double*) x, (int*) &ldx, (double*) y, (int*) &ldy, (double*) table, (double*) work, (int*) &isys); #endif } void SCSL::ccfft2d(Int PN(isign), Int PN(n1), Int PN(n2), Float PN(scale), Complex* PN(x), Int PN(ldx), Complex* PN(y), Int PN(ldy), Float* PN(table), Float* PN(work), Int PN(isys)) { #if defined(HAVE_SCSL) DebugAssert(sizeof(Complex) == 2*sizeof(float), AipsError); ccfft2d_((int*) &isign, (int*) &n1, (int*) &n2, (float*) &scale, (float*) x, (int*) &ldx, (float*) y, (int*) &ldy, (float*) table, (float*) work, (int*) &isys); #endif } void SCSL::zzfft2d(Int PN(isign), Int PN(n1), Int PN(n2), Double PN(scale), DComplex* PN(x), Int PN(ldx), DComplex* PN(y), Int PN(ldy), Double* PN(table), Double* PN(work), Int PN(isys)) { #if defined(HAVE_SCSL) DebugAssert(sizeof(DComplex) == 2*sizeof(double), AipsError); zzfft2d_((int*) &isign, (int*) &n1, (int*) &n2, (double*) &scale, (double*) x, (int*) &ldx, (double*) y, (int*) &ldy, (double*) table, (double*) work, (int*) &isys); #endif } void SCSL::scfft2d(Int PN(isign), Int PN(n1), Int PN(n2), Float PN(scale), Float* PN(x), Int PN(ldx), Complex* PN(y), Int PN(ldy), Float* PN(table), Float* PN(work), Int PN(isys)) { #if defined(HAVE_SCSL) DebugAssert(sizeof(Complex) == 2*sizeof(float), AipsError); scfft2d_((int*) &isign, (int*) &n1, (int*) &n2, (float*) &scale, (float*) x, (int*) &ldx, (float*) y, (int*) &ldy, (float*) table, (float*) work, (int*) &isys); #endif } void SCSL::dzfft2d(Int PN(isign), Int PN(n1), Int PN(n2), Double PN(scale), Double* PN(x), Int PN(ldx), DComplex* PN(y), Int PN(ldy), Double* PN(table), Double* PN(work), Int PN(isys)) { #if defined(HAVE_SCSL) DebugAssert(sizeof(DComplex) == 2*sizeof(double), AipsError); dzfft2d_((int*) &isign, (int*) &n1, (int*) &n2, (double*) &scale, (double*) x, (int*) &ldx, (double*) y, (int*) &ldy, (double*) table, (double*) work, (int*) &isys); #endif } void SCSL::csfft2d(Int PN(isign), Int PN(n1), Int PN(n2), Float PN(scale), Complex* PN(x), Int PN(ldx), Float* PN(y), Int PN(ldy), Float* PN(table), Float* PN(work), Int PN(isys)) { #if defined(HAVE_SCSL) DebugAssert(sizeof(Complex) == 2*sizeof(float), AipsError); csfft2d_((int*) &isign, (int*) &n1, (int*) &n2, (float*) &scale, (float*) x, (int*) &ldx, (float*) y, (int*) &ldy, (float*) table, (float*) work, (int*) &isys); #endif } void SCSL::zdfft2d(Int PN(isign), Int PN(n1), Int PN(n2), Double PN(scale), DComplex* PN(x), Int PN(ldx), Double* PN(y), Int PN(ldy), Double* PN(table), Double* PN(work), Int PN(isys)) { #if defined(HAVE_SCSL) DebugAssert(sizeof(DComplex) == 2*sizeof(double), AipsError); dzfft2d_((int*) &isign, (int*) &n1, (int*) &n2, (double*) &scale, (double*) x, (int*) &ldx, (double*) y, (int*) &ldy, (double*) table, (double*) work, (int*) &isys); #endif } void SCSL::ccfft3d(Int PN(isign), Int PN(n1), Int PN(n2), Int PN(n3), Float PN(scale), Complex* PN(x), Int PN(ldx), Int PN(ldx2), Complex* PN(y), Int PN(ldy), Int PN(ldy2), Float* PN(table), Float* PN(work), Int PN(isys)) { #if defined(HAVE_SCSL) DebugAssert(sizeof(Complex) == 2*sizeof(float), AipsError); ccfft3d_((int*) &isign, (int*) &n1, (int*) &n2, (int*) &n3, (float*) &scale, (float*) x, (int*) &ldx, (int*) &ldx2, (float*) y, (int*) &ldy, (int*) &ldy2, (float*) table, (float*) work, (int*) &isys); #endif } void SCSL::zzfft3d(Int PN(isign), Int PN(n1), Int PN(n2), Int PN(n3), Double PN(scale), DComplex* PN(x), Int PN(ldx), Int PN(ldx2), DComplex* PN(y), Int PN(ldy), Int PN(ldy2), Double* PN(table), Double* PN(work), Int PN(isys)) { #if defined(HAVE_SCSL) DebugAssert(sizeof(DComplex) == 2*sizeof(double), AipsError); zzfft3d_((int*) &isign, (int*) &n1, (int*) &n2, (int*) &n3, (double*) &scale, (double*) x, (int*) &ldx, (int*) &ldx2, (double*) y, (int*) &ldy, (int*) &ldy2, (double*) table, (double*) work, (int*) &isys); #endif } void SCSL::scfft3d(Int PN(isign), Int PN(n1), Int PN(n2), Int PN(n3), Float PN(scale), Float* PN(x), Int PN(ldx), Int PN(ldx2), Complex* PN(y), Int PN(ldy), Int PN(ldy2), Float* PN(table), Float* PN(work), Int PN(isys)) { #if defined(HAVE_SCSL) DebugAssert(sizeof(Complex) == 2*sizeof(float), AipsError); scfft3d_((int*) &isign, (int*) &n1, (int*) &n2, (int*) &n3, (float*) &scale, (float*) x, (int*) &ldx, (int*) &ldx2, (float*) y, (int*) &ldy, (int*) &ldy2, (float*) table, (float*) work, (int*) &isys); #endif } void SCSL::dzfft3d(Int PN(isign), Int PN(n1), Int PN(n2), Int PN(n3), Double PN(scale), Double* PN(x), Int PN(ldx), Int PN(ldx2), DComplex* PN(y), Int PN(ldy), Int PN(ldy2), Double* PN(table), Double* PN(work), Int PN(isys)) { #if defined(HAVE_SCSL) DebugAssert(sizeof(DComplex) == 2*sizeof(double), AipsError); dzfft3d_((int*) &isign, (int*) &n1, (int*) &n2, (int*) &n3, (double*) &scale, (double*) x, (int*) &ldx, (int*) &ldx2, (double*) y, (int*) &ldy, (int*) &ldy2, (double*) table, (double*) work, (int*) &isys); #endif } void SCSL::csfft3d(Int PN(isign), Int PN(n1), Int PN(n2), Int PN(n3), Float PN(scale), Complex* PN(x), Int PN(ldx), Int PN(ldx2), Float* PN(y), Int PN(ldy), Int PN(ldy2), Float* PN(table), Float* PN(work), Int PN(isys)) { #if defined(HAVE_SCSL) DebugAssert(sizeof(Complex) == 2*sizeof(float), AipsError); csfft3d_((int*) &isign, (int*) &n1, (int*) &n2, (int*) &n3, (float*) &scale, (float*) x, (int*) &ldx, (int*) &ldx2, (float*) y, (int*) &ldy, (int*) &ldy2, (float*) table, (float*) work, (int*) &isys); #endif } void SCSL::zdfft3d(Int PN(isign), Int PN(n1), Int PN(n2), Int PN(n3), Double PN(scale), DComplex* PN(x), Int PN(ldx), Int PN(ldx2), Double* PN(y), Int PN(ldy), Int PN(ldy2), Double* PN(table), Double* PN(work), Int PN(isys)) { #if defined(HAVE_SCSL) DebugAssert(sizeof(DComplex) == 2*sizeof(double), AipsError); dzfft3d_((int*) &isign, (int*) &n1, (int*) &n2, (int*) &n3, (double*) &scale, (double*) x, (int*) &ldx, (int*) &ldx2, (double*) y, (int*) &ldy, (int*) &ldy2, (double*) table, (double*) work, (int*) &isys); #endif } } //# NAMESPACE CASACORE - END casacore-3.7.1/scimath/Mathematics/SCSL.h000066400000000000000000002517531476623553700201450ustar00rootroot00000000000000//# extern_fft.h: C++ wrapper functions for FORTRAN FFT code //# Copyright (C) 1993,1994,1995,1997,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_SCSL_H #define SCIMATH_SCSL_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // C++ Interface to the Sgi/Cray Scientific Library (SCSL) // // These are C++ wrapper functions for the transform routines in the SGI/Cray // Scientific Library (SCSL). The purpose of these definitions is to overload // the functions so that C++ users can access the functions in SCSL with // identical function names. // // // Currently, the SCSL is available only on SGI machines. // // class SCSL { public: // These routines compute the Fast Fourier Transform (FFT) of the complex // vector x, and store the result in vector y. ccfft does the // complex-to-complex transform and zzfft does the same for double // precision arrays. // // In FFT applications, it is customary to use zero-based subscripts; the // formulas are simpler that way. Suppose that the arrays are // dimensioned as follows: // // // COMPLEX X(0:N-1), Y(0:N-1) // // // The output array is the FFT of the input array, using the following // formula for the FFT: // // // n-1 // Y(k) = scale * Sum [ X(j)*w**(isign*j*k) ] for k = 0, ..., n-1 // j=0 // // where: // w = exp(2*pi*i/n), // i = + sqrt(-1), // pi = 3.14159..., // isign = +1 or -1 // // // Different authors use different conventions for which of the // transforms, isign = +1 or isign = -1, is the forward or inverse // transform, and what the scale factor should be in either case. You // can make this routine compute any of the various possible definitions, // however, by choosing the appropriate values for isign and scale. // // The relevant fact from FFT theory is this: If you take the FFT with // any particular values of isign and scale, the mathematical inverse // function is computed by taking the FFT with -isign and 1/(n*scale). // In particular, if you use isign = +1 and scale = 1.0 you can compute // the inverse FFT by using isign = -1 and scale = 1.0/n. // // The output array may be the same as the input array. // //

        Initialization

        // The table array stores the trigonometric tables used in calculation of // the FFT. You must initialize table by calling the routine with isign // = 0 prior to doing the transforms. If the value of the problem size, // n, does not change, table does not have to be reinitialized. // //

        Dimensions

        // In the preceding description, it is assumed that array subscripts were // zero-based, as is customary in FFT applications. Thus, the input and // output arrays are declared as follows: // // // COMPLEX X(0:N-1) // COMPLEX Y(0:N-1) // // // However, if you prefer to use the more customary FORTRAN style with // subscripts starting at 1 you do not have to change the calling // sequence, as in the following (assuming N > 0): // // // COMPLEX X(N) // COMPLEX Y(N) // // // // These examples use the table and workspace sizes appropriate to the // Origin series. // // Example 1: Initialize the complex array table in preparation for // doing an FFT of size 1024. Only the isign, n, and table arguments are // used in this case. You can use dummy arguments or zeros for the other // arguments in the subroutine call. // // // REAL TABLE(30 + 2048) // CALL CCFFT(0, 1024, 0.0, DUMMY, DUMMY, TABLE, DUMMY, 0) // // // Example 2: x and y are complex arrays of dimension (0:1023). Take // the FFT of x and store the results in y. Before taking the FFT, // initialize the table array, as in example 1. // // // COMPLEX X(0:1023), Y(0:1023) // REAL TABLE(30 + 2048) // REAL WORK(2048) // CALL CCFFT(0, 1024, 1.0, X, Y, TABLE, WORK, 0) // CALL CCFFT(1, 1024, 1.0, X, Y, TABLE, WORK, 0) // // // Example 3: Using the same x and y as in example 2, take the inverse // FFT of y and store it back in x. The scale factor 1/1024 is used. // Assume that the table array is already initialized. // // // CALL CCFFT(-1, 1024, 1.0/1024.0, Y, X, TABLE, WORK, 0) // // // Example 4: Perform the same computation as in example 2, but assume // that the lower bound of each array is 1, rather than 0. No change was // needed in the subroutine calls. // // // COMPLEX X(1024), Y(1024) // CALL CCFFT(0, 1024, 1.0, X, Y, TABLE, WORK, 0) // CALL CCFFT(1, 1024, 1.0, X, Y, TABLE, WORK, 0) // // // Example 5: Do the same computation as in example 4, but put the // output back in array x to save storage space. Assume that table is // already initialized. // // // COMPLEX X(1024) // CALL CCFFT(1, 1024, 1.0, X, X, TABLE, WORK, 0) // // // // Input parameters: //
        //
        isign //
        Integer. // Specifies whether to initialize the table array or to do the // forward or inverse Fourier transform, as follows: // // If isign = 0, the routine initializes the table array and // returns. In this case, the only arguments used or checked // are isign, n, and table. // // If isign = +1 or -1, the value of isign is the sign of the // exponent used in the FFT formula. //
        n //
        Integer. Size of the transform (the number of values in // the input array). n >= 1. //
        scale //
        Scale factor. // ccfft: real. // zzfft: double precision. // Each element of the output array is multiplied by scale // after taking the Fourier transform, as defined by the previous // formula. //
        x //
        Array of dimension (0:n-1). // ccfft: complex array. // zzfft: double complex array. // // Input array of values to be transformed. //
        isys //
        Integer. // Algorithm used; value dependent on hardware system. Currently, no // special options are supported; therefore, you must always specify // an isys argument as constant 0. //
        // Output parameters: //
        //
        y //
        Array of dimension (0:n-1). // ccfft: complex array. // zzfft: double complex array. // Output array of transformed values. The output array may be // the same as the input array. In that case, the transform is // done in place and the input array is overwritten with the // transformed values. //
        table //
        Real array; dimension 2*n+30. // // Table of factors and trigonometric functions. // // If isign = 0, the routine initializes table (table is output // only). // // If isign = +1 or -1, the values in table are assumed to be // initialized already by a prior call with isign = 0 (table is // input only). //
        work //
        Real array; dimension 2*n. // // Work array. This is a scratch array used for intermediate // calculations. Its address space must be different address // space from that of the input and output arrays. //
        // static void ccfft(Int isign, Int n, Float scale, Complex* x, Complex* y, Float* table, Float* work, Int isys); static void ccfft(Int isign, Int n, Double scale, DComplex* x, DComplex* y, Double* table, Double* work, Int isys); static void zzfft(Int isign, Int n, Double scale, DComplex* x, DComplex* y, Double* table, Double* work, Int isys); // // scfft/dzfft computes the FFT of the real array x, and it stores // the results in the complex array y. csfft/zdfft computes the // corresponding inverse complex-to-real transform. // // It is customary in FFT applications to use zero-based subscripts; the // formulas are simpler that way. For these routines, suppose that the // arrays are dimensioned as follows: // // // REAL X(0:n-1) // COMPLEX Y(0:n/2) // // // Then the output array is the FFT of the input array, using the // following formula for the FFT: // // // n-1 // Y(k) = scale * Sum [ X(j)*w**(isign*j*k) ] for k = 0, ..., n/2 // j=0 // // where: // w = exp(2*pi*i/n), // i = + sqrt(-1), // pi = 3.14159..., // isign = +1 or -1. // // // Different authors use different conventions for which of the // transforms, isign = +1 or isign = -1, is the forward or inverse // transform, and what the scale factor should be in either case. You // can make these routines compute any of the various possible // definitions, however, by choosing the appropriate values for isign and // scale. // // The relevant fact from FFT theory is this: If you call scfft // with any particular values of isign and scale, the mathematical inverse // function is computed by calling csfft with -isign and // 1/(n*scale). In particular, if you use isign = +1 and scale = 1.0 in // scfft for the forward FFT, you can compute the inverse FFT by // using ccfft with isign = -1 and scale = 1.0/n. // //

        Real-to-complex FFTs

        // Notice in the preceding formula that there are n real input values, // and n/2 + 1 complex output values. This property is characteristic of // real-to-complex FFTs. // // The mathematical definition of the Fourier transform takes a sequence // of n complex values and transforms it to another sequence of n complex // values. A complex-to-complex FFT routine, such as ccfft, will // take n complex input values, and produce n complex output values. In // fact, one easy way to compute a real-to-complex FFT is to store the // input data in a complex array, then call routine ccfft to // compute the FFT. You get the same answer when using the scfft // routine. // // The reason for having a separate real-to-complex FFT routine is // efficiency. Because the input data is real, you can make use of this // fact to save almost half of the computational work. The theory of // Fourier transforms tells us that for real input data, you have to // compute only the first n/2 + 1 complex output values, because the // remaining values can be computed from the first half of the values by // the simple formula: // // // Y(k) = conjg(Y(n-k)) for n/2 <= k <= n-1 // // // where the notation conjgY represents the complex conjugate of y. // // In fact, in many applications, the second half of the complex output // data is never explicitly computed or stored. Likewise, as explained // later, only the first half of the complex data has to be supplied for // the complex-to-real FFT. // // Another implication of FFT theory is that, for real input data, the // first output value, Y(0), will always be a real number; therefore, the // imaginary part will always be 0. If n is an even number, Y(n/2) will // also be real and thus, have zero imaginary parts. // //

        Complex-to-real FFTs

        // Consider the complex-to-real case. The effect of the computation is // given by the preceding formula, but with X complex and Y real. // // Generally, the FFT transforms a complex sequence into a complex // sequence. However, in a certain application we may know the output // sequence is real. Often, this is the case because the complex input // sequence was the transform of a real sequence. In this case, you can // save about half of the computational work. // // According to the theory of Fourier transforms, for the output // sequence, Y, to be a real sequence, the following identity on the // input sequence, X, must be true: // // // X(k) = conjg(X(n-k)) for n/2 <= k <= n-1 // // // And, in fact, the input values X(k) for k > n/2 need not be supplied; // they can be inferred from the first half of the input. // // Thus, in the complex-to-real routine, csfft, the arrays can be // dimensioned as follows: // // // COMPLEX X(0:n/2) // REAL Y(0:n-1) // // // There are n/2 + 1 complex input values and n real output values. Even // though only n/2 + 1 input values are supplied, the size of the // transform is still n in this case, because implicitly you are using // the FFT formula for a sequence of length n. // // Another implication of the theory is that X(0) must be a real number // (that is, it must have zero imaginary part). Also, if n is even, // X(n/2) must also be real. Routine CSFFT assumes that these // values are real; if you specify a nonzero imaginary part, it is ignored. // //

        Table Initialization

        // The table array stores the trigonometric tables used in calculation of // the FFT. This table must be initialized by calling the routine with // isign = 0 prior to doing the transforms. The table does not have to // be reinitialized if the value of the problem size, n, does not change. // Because scfft and csfft use the same format for // table, either can be used to initialize it (note that CCFFT uses a // different table format). // //

        Dimensions

        // In the preceding description, it is assumed that array subscripts were // zero-based, as is customary in FFT applications. Thus, the input and // output arrays are declared (assuming n > 0): // // // REAL X(0:n-1) // COMPLEX Y(0:n/2) // // // No change is needed in the calling sequence; however, if you prefer // you can use the more customary Fortran style with subscripts starting // at 1, as in the following: // // // REAL X(n) // COMPLEX Y(n/2 + 1) // // // // These examples use the table and workspace sizes appropriate to Origin // series. // // Example 1: Initialize the complex array TABLE in preparation for // doing an FFT of size 1024. In this case only the arguments isign, n, // and table are used. You can use dummy arguments or zeros for the other // arguments in the subroutine call. // // // REAL TABLE(15 + 1024) // CALL SCFFT(0, 1024, 0.0, DUMMY, DUMMY, TABLE, DUMMY, 0) // // // Example 2: X is a real array of dimension (0:1023), and Y is a // complex array of dimension (0:512). Take the FFT of X and store the // results in Y. Before taking the FFT, initialize the TABLE array, as // in example 1. // // // REAL X(0:1023) // COMPLEX Y(0:512) // REAL TABLE(15 + 1024) // REAL WORK(1024) // CALL SCFFT(0, 1024, 1.0, X, Y, TABLE, WORK, 0) // CALL SCFFT(1, 1024, 1.0, X, Y, TABLE, WORK, 0) // // // Example 3: With X and Y as in example 2, take the inverse FFT of Y // and store it back in X. The scale factor 1/1024 is used. Assume that // the TABLE array is initialized already. // // // CALL CSFFT(-1, 1024, 1.0/1024.0, Y, X, TABLE, WORK, 0) // // // Example 4: Perform the same computation as in example 2, but assume // that the lower bound of each array is 1, rather than 0. The // subroutine calls are not changed. // // // REAL X(1024) // COMPLEX Y(513) // CALL SCFFT(0, 1024, 1.0, X, Y, TABLE, WORK, 0) // CALL SCFFT(1, 1024, 1.0, X, Y, TABLE, WORK, 0) // // // Example 5: Perform the same computation as in example 4, but // equivalence the input and output arrays to save storage space. Assume // that the TABLE array is initialized already. // // // REAL X(1024) // COMPLEX Y(513) // EQUIVALENCE ( X(1), Y(1) ) // CALL SCFFT(1, 1024, 1.0, X, Y, TABLE, WORK, 0) // // // // Input parameters: //
        //
        isign //
        Integer. // Specifies whether to initialize the table array or to do the // forward or inverse Fourier transform, as follows: // // If isign = 0, the routine initializes the table array and // returns. In this case, the only arguments used or checked // are isign, n, and table. // // If isign = +1 or -1, the value of isign is the sign of the // exponent used in the FFT formula. //
        n //
        Integer. // Size of transform. If n <= 2, scfft/dzfft // returns without calculating the transform. //
        scale //
        Scale factor. // scfft: real. // dzfft: double precision. // csfft: real. // zdfft: double precision. // Each element of the output array is multiplied by scale // after taking the Fourier transform, as defined by the previous // formula. //
        x //
        Input array of values to be transformed. // scfft: real array of dimension (0:n-1). // dzfft: double precision array of dimension (0:n-1). // csfft: complex array of dimension (0:n/2). // zdfft: double complex array of dimension (0:n/2). //
        isys //
        Integer array of dimension (0:isys(0)). // Use isys to specify certain processor-specific parameters or // options. The first element of the array specifies how many // more elements are in the array. // // If isys(0) = 0, the default values of such parameters are // used. In this case, you can specify the argument value as // the scalar integer constant 0. If isys(0) > 0, isys(0) // gives the upper bound of the isys array; that is, if // il = isys(0), user-specified parameters are expected in // isys(1) through isys(il). //
        // Output parameters: //
        //
        y //
        Output array of transformed values. // scfft: complex array of dimension (0:n/2). // dzfft: double complex array of dimension (0:n/2). // csfft: real array of dimension (0:n-1). // zdfft: double precision array of dimension (0:n-1). // // The output array, y, is the FFT of the the input array, x, // computed according to the preceding formula. The output // array may be equivalenced to the input array in the calling // program. Be careful when dimensioning the arrays, in this // case, to allow for the fact that the complex array contains // two (real) words more than the real array. //
        table //
        Real array; dimension n+15. // // Table of factors and trigonometric functions. // // If isign = 0, the table array is initialized to contain // trigonometric tables needed to compute an FFT of size n. // // If isign = +1 or -1, the values in table are assumed to be // initialized already by a prior call with isign = 0. //
        work //
        Real array; dimension n. // // Work array used for intermediate calculations. Its address // space must be different from that of the input and output // arrays. //
        // static void scfft(Int isign, Int n, Float scale, Float* x, Complex* y, Float* table, Float* work, Int isys); static void scfft(Int isign, Int n, Double scale, Double* x, DComplex* y, Double* table, Double* work, Int isys); static void dzfft(Int isign, Int n, Double scale, Double* x, DComplex* y, Double* table, Double* work, Int isys); static void csfft(Int isign, Int n, Float scale, Complex* x, Float* y, Float* table, Float* work, Int isys); static void csfft(Int isign, Int n, Double scale, DComplex* x, Double* y, Double* table, Double* work, Int isys); static void zdfft(Int isign, Int n, Double scale, DComplex* x, Double* y, Double* table, Double* work, Int isys); // // ccfftm/zzfftm computes the FFT of each column of the // complex matrix x, and stores the results in the columns of complex // matrix y. // // Suppose the arrays are dimensioned as follows: // // // COMPLEX X(0:ldx-1, 0:lot-1) // COMPLEX Y(0:ldy-1, 0:lot-1) // // where ldx >= n, ldy >= n. // // // Then column L of the output array is the FFT of column L of the // input array, using the following formula for the FFT: // // // n-1 // Y(k, L) = scale * Sum [ X(j)*w**(isign*j*k) ] // j=0 // for k = 0, ..., n-1 // L = 0, ..., lot-1 // where: // w = exp(2*pi*i/n), // i = + sqrt(-1), // pi = 3.14159..., // isign = +1 or -1 // lot = the number of columns to transform // // // Different authors use different conventions for which of the // transforms, isign = +1 or isign = -1, is the forward or inverse // transform, and what the scale factor should be in either case. You // can make this routine compute any of the various possible definitions, // however, by choosing the appropriate values for isign and scale. // // The relevant fact from FFT theory is this: If you take the FFT with // any particular values of isign and scale, the mathematical inverse // function is computed by taking the FFT with -isign and 1/(n * scale). // In particular, if you use isign = +1 and scale = 1.0 for the forward // FFT, you can compute the inverse FFT by using the following: isign = // -1 and scale = 1.0/n. // // This section contains information about the algorithm for these // routines, the initialization of the table array, the declaration of // dimensions for x and y arrays, some performance tips, and some // implementation-dependent details. // //

        Algorithm

        // These routines use decimation-in-frequency type FFT. It takes the FFT // of the columns and vectorizes the operations along the rows of the // matrix. Thus, the vector length in the calculations depends on the // row size, and the strides for vector loads and stores are the leading // dimensions, ldx and ldy. // //

        Initialization

        // The table array stores the trigonometric tables used in calculation of // the FFT. You must initialize the table array by calling the routine // with isign = 0 prior to doing the transforms. If the value of the // problem size, n, does not change, table does not have to be // reinitialized. // //

        Dimensions

        // In the preceding description, it is assumed that array subscripts were // zero-based, as is customary in FFT applications. Thus, the input and // output arrays are declared as follows: // // // COMPLEX X(0:ldx-1, 0:lot-1) // COMPLEX Y(0:ldy-1, 0:lot-1) // // // The calling sequence does not have to change, however, if you prefer // to use the more customary Fortran style with subscripts starting at 1. // The same values of ldx and ldy would be passed to the subroutine even // if the input and output arrays were dimensioned as follows: // // // COMPLEX X(ldx, lot) // COMPLEX Y(ldy, lot) // // // // Example 1: Initialize the TABLE array in preparation for doing an FFT // of size 128. Only the isign, n, and table arguments are used in this // case. You can use dummy arguments or zeros for the other arguments in // the subroutine call. // // // REAL TABLE(30 + 256) // CALL CCFFTM(0, 128, 0, 0., DUMMY, 1, DUMMY, 1, TABLE, DUMMY, 0) // // // Example 2: X and Y are complex arrays of dimension (0:128) by (0:55). // The first 128 elements of each column contain data. For performance // reasons, the extra element forces the leading dimension to be an odd // number. Take the FFT of the first 50 columns of X and store the // results in the first 50 columns of Y. Before taking the FFT, // initialize the TABLE array, as in example 1. // // // COMPLEX X(0:128, 0:55) // COMPLEX Y(0:128, 0:55) // REAL TABLE(30 + 256) // REAL WORK(256) // ... // CALL CCFFTM(0, 128, 50, 1.0, X, 129, Y, 129, TABLE, WORK, 0) // CALL CCFFTM(1, 128, 50, 1.0, X, 129, Y, 129, TABLE, WORK, 0) // // // Example 3: With X and Y as in example 2, take the inverse FFT of Y // and store it back in X. The scale factor 1/128 is used. Assume that // the TABLE array is already initialized. // // // CALL CCFFTM(-1, 128, 50, 1./128., Y, 129, X, 129, TABLE,WORK,0) // // // Example 4: Perform the same computation as in example 2, but assume // that the lower bound of each array is 1, rather than 0. The // subroutine calls are not changed. // // // COMPLEX X(129, 55) // COMPLEX Y(129, 55) // ... // CALL CCFFTM(0, 128, 50, 1.0, X, 129, Y, 129, TABLE, WORK, 0) // CALL CCFFTM(1, 128, 50, 1.0, X, 129, Y, 129, TABLE, WORK, 0) // // // Example 5: Perform the same computation as in example 4, but put the // output back in array X to save storage space. Assume that the TABLE // array is already initialized. // // // COMPLEX X(129, 55) // ... // CALL CCFFTM(1, 128, 50, 1.0, X, 129, X, 129, TABLE, WORK, 0) // // // // // Input parameters: //
        //
        isign //
        Integer. // Specifies whether to initialize the table array or to do the // forward or inverse Fourier transform, as follows: // // If isign = 0, the routine initializes the table array and // returns. In this case, the only arguments used or checked // are isign, n, and table. // // If isign = +1 or -1, the value of isign is the sign of the // exponent used in the FFT formula. //
        n //
        Integer. // Size of each transform (the number of elements in each // column of the input and output matrix to be transformed). // Performance depends on the value of n, as explained above. // n >= 0; if n = 0, the routine returns. //
        lot //
        Integer. // The number of transforms to be computed (lot size). This is // the number of elements in each row of the input and output // matrix. lot >= 0. If lot = 0, the routine returns. //
        scale //
        Scale factor. // ccfftm: real. // zzfftm: double precision. // Each element of the output array is multiplied by scale // factor after taking the Fourier transform, as defined // previously. //
        x //
        Array of dimension (0:ldx-1, 0:n2-1). // ccfftm: real array. // zzfftm: double precision array. // Input array of values to be transformed. //
        ldx //
        The number of rows in the x array, as it was declared in the // calling program (the leading dimension of X). ldx >= MAX(n, 1). //
        ldy //
        Integer. // The number of rows in the y array, as it was declared in the // calling program (the leading dimension of y). ldy >= MAX(n, // 1). //
        isys //
        Integer array of dimension (0:isys(0)). // The first element of the array specifies how many more // elements are in the array. Use isys to specify certain // processor-specific parameters or options. // // If isys(0) = 0, the default values of such parameters are // used. In this case, you can specify the argument value as // the scalar integer constant 0. // // If isys(0) > 0, isys(0) gives the upper bound of the isys // array. Therefore, if il = isys(0), user-specified // parameters are expected in isys(1) through isys(il). //
        // Output parameters: //
        //
        y //
        Array of dimension (0:ldy-1, 0:lot-1). // ccfftm: complex array. // zzfftm: double complex array. // Output array of transformed values. Each column of the // output array, y, is the FFT of the corresponding column of // the input array, x, computed according to the preceding // formula. // // The output array may be the same as the input array. In that // case, the transform is done in place. The input array is // overwritten with the transformed values. In this case, it // is necessary that ldx = ldy. //
        table //
        Real array; dimension (30 + 2n). // Table of factors and trigonometric functions. // // If isign = 0, the routine initializes table (table is output // only). // // If isign = +1 or -1, the values in table are assumed to be // initialized already by a prior call with isign = 0 (table is // input only). //
        work //
        Real array; dimension 2n. // Work array. This is a scratch array used for intermediate // calculations. Its address space must be different from that // of the input and output arrays. //
        // static void ccfftm(Int isign, Int n, Int lot, Float scale, Complex* x, Int ldx, Complex* y, Int ldy, Float* table, Float* work, Int isys); static void zzfftm(Int isign, Int n, Int lot, Double scale, DComplex* x, Int ldx, DComplex* y, Int ldy, Double* table, Double* work, Int isys); // // scfftm/dzfftm computes the FFT of each column of the real matrix // X, and it stores the results in the corresponding column of the complex // matrix Y. csfftm/zdfftm computes the corresponding inverse // transforms. // // In FFT applications, it is customary to use zero-based subscripts; the // formulas are simpler that way. First, the function of scfftm is // described. Suppose that the arrays are dimensioned as follows: // // // REAL X(0:ldx-1, 0:lot-1) // COMPLEX Y(0:ldy-1, 0:lot-1) // // where ldx >= n, ldy >= n/2 + 1. // // // Then column L of the output array is the FFT of column L of the input // array, using the following formula for the FFT: // // // n-1 // Y(k, L) = scale * Sum [ X(j, L)*w**(isign*j*k) ] // j=0 // // for k = 0, ..., n/2 // L = 0, ..., lot-1 where: // w = exp(2*pi*i/n), // i = + sqrt(-1) // pi = 3.14159..., // isign = +1 or -1, // lot = the number of columns to transform // // // Different authors use different conventions for which transform // (isign = +1 or isign = -1) is used in the real-to-complex case, and // what the scale factor should be. Some adopt the convention that isign // = 1 for the real-to-complex transform, and isign = -1 for the // complex-to-real inverse. Others use the opposite convention. You can // make these routines compute any of the various possible definitions, // however, by choosing the appropriate values for isign and scale. // // The relevant fact from FFT theory is this: If you use scfftm to // take the real-to-complex FFT, using any particular values of isign and // scale, the mathematical inverse function is computed by using // csfftm with -isign and 1/ (n*scale). In particular, if you call // scfftm with isign = +1 and scale = 1.0, you can use // csfftm to compute the inverse complex-to-real FFT by using isign // = -1 and scale = 1.0/n. // //

        Real-to-complex FFTs

        // Notice in the preceding formula that there are n real input values and // (n/2) + 1 complex output values for each column. This property is // characteristic of real-to-complex FFTs. // // The mathematical definition of the Fourier transform takes a sequence // of n complex values and transforms it to another sequence of n complex // values. A complex-to-complex FFT routine, such as ccfftm, will // take n complex input values and produce n complex output values. In fact, // one easy way to compute a real-to-complex FFT is to store the input // data x in a complex array, then call routine ccfftm to compute // the FFT. You get the same answer when using the scfftm routine. // // A separate real-to-complex FFT routine is more efficient than the // equivalent complex-to-complex routine. Because the input data is // real, you can make use of this fact to save almost half of the // computational work. According to the theory of Fourier transforms, // for real input data, you have to compute only the first n/2 + 1 // complex output values in each column, because the second half of the // FFT values in each column can be computed from the first half of the // values by the simple formula: // // // Y = conjgY for n/2 <= k <= n-1 // k,L n-k, L // // where the notation conjg(z) represents the complex conjugate of z. // // // In fact, in many applications, the second half of the complex output // data is never explicitly computed or stored. Likewise, you must // supply only the first half of the complex data in each column has to // be supplied for the complex-to-real FFT. // // Another implication of FFT theory is that for real input data, the // first output value in each column, Y(0, L), will always be a real // number; therefore, the imaginary part will always be 0. If n is an // even number, Y(n/2, L) will also be real and have 0 imaginary parts. // //

        Complex-to-real FFTs

        // Consider the complex-to-real case. The effect of the computation is // given by the preceding formula, but with X complex and Y real. // // In general, the FFT transforms a complex sequence into a complex // sequence; however, in a certain application you may know the output // sequence is real, perhaps because the complex input sequence was the // transform of a real sequence. In this case, you can save about half // of the computational work. // // According to the theory of Fourier transforms, for the output // sequence, Y, to be a real sequence, the following identity on the // input sequence, X, must be true: // // // X = conjgX for n/2 <= k <= n-1 // k,L n-k,L // And, in fact, the following input values // // X for k > n/2 // k,L // do not have to be supplied, because they can be inferred from the // first half of the input. // // // Thus, in the complex-to-real routine, CSFFTM, the arrays can be // dimensioned as follows: // // // COMPLEX X(0:ldx-1, 0:lot-1) // REAL Y(0:ldy-1, 0:lot-1) // // where ldx >= n/2 + 1, ldy >= n. // // // In each column, there are (n/2) + 1 complex input values and n real // output values. Even though only (n/2) + 1 input values are supplied, // the size of the transform is still n in this case, because implicitly // the FFT formula for a sequence of length n is used. // // Another implication of the theory is that X(0, L) must be a real // number (that is, must have zero imaginary part). If n is an even // number, X(n/2, L) must also be real. Routine CSFFTM assumes that each // of these values is real; if a nonzero imaginary part is given, it is // ignored. // //

        Table Initialization

        // The table array contains the trigonometric tables used in calculation // of the FFT. You must initialize this table by calling the routine // with isign = 0 prior to doing the transforms. table does not have to // be reinitialized if the value of the problem size, n, does not change. // //

        Dimensions

        // In the preceding description, it is assumed that array subscripts were // zero-based, as is customary in FFT applications. Thus, the input and // output arrays are declared (for SCFFTM): // // // REAL X(0:ldx-1, 0:lot-1) // COMPLEX Y(0:ldy-1, 0:lot-1) // // // No change is made in the calling sequence, however, if you prefer to // use the more customary Fortran style with subscripts starting at 1. // The same values of ldx and ldy would be passed to the subroutine even // if the input and output arrays were dimensioned as follows: // // // REAL X(ldx, lot) // COMPLEX Y(ldy, lot) // // // Example 1: Initialize the complex array TABLE in preparation for // doing an FFT of size 128. In this case only the isign, n, and table // arguments are used; you may use dummy arguments or zeros for the other // arguments in the subroutine call. // // // REAL TABLE(15 + 128) // CALL SCFFTM(0, 128, 1, 0.0, DUMMY, 1, DUMMY, 1, // & TABLE, DUMMY, 0) // // // Example 2: X is a real array of dimension (0:128, 0:55), and Y is a // complex array of dimension (0:64, 0:55). The first 128 elements in // each column of X contain data; the extra element forces an odd leading // dimension. Take the FFT of the first 50 columns of X and store the // results in the first 50 columns of Y. Before taking the FFT, // initialize the TABLE array, as in example 1. // // // REAL X(0:128, 0:55) // COMPLEX Y(0:64, 0:55) // REAL TABLE(15 + 128) // REAL WORK((128) // ... // CALL SCFFTM(0, 128, 50, 1.0, X, 129, Y, 65, TABLE, WORK, 0) // CALL SCFFTM(1, 128, 50, 1.0, X, 129, Y, 65, TABLE, WORK, 0) // // // Example 3: With X and Y as in example 2, take the inverse FFT of Y // and store it back in X. The scale factor 1/128 is used. Assume that // the TABLE array is initialized already. // // // CALL CSFFTM(-1, 128, 50, 1.0/128.0, Y, 65, X, 129, // & TABLE, WORK, 0) // // // Example 4: Perform the same computation as in example 2, but assume // that the lower bound of each array is 1, rather than 0. No change is // made in the subroutine calls. // // // REAL X(129, 56) // COMPLEX Y(65, 56) // ... // CALL SCFFTM(0, 128, 50, 1.0, X, 129, Y, 65, TABLE, WORK, 0) // CALL SCFFTM(1, 128, 50, 1.0, X, 129, Y, 65, TABLE, WORK, 0) // // // Example 5: Perform the same computation as in example 4, but // equivalence the input and output arrays to save storage space. In // this case, a row must be added to X, because it is equivalenced to a // complex array. The leading dimension of X is two times an odd number; // therefore, memory bank conflicts are minimal. Assume that TABLE is // initialized already. // // // REAL X(130, 56) // COMPLEX Y(65, 56) // EQUIVALENCE ( X(1, 1), Y(1, 1) ) // ... // CALL SCFFTM(1, 128, 50, 1.0, X, 130, Y, 65, TABLE, WORK, 0) // // // // Input parameters: //
        //
        isign //
        Integer. // Specifies whether to initialize the table array or to do the // forward or inverse Fourier transform, as follows: // // If isign = 0, the routine initializes the table array and // returns. In this case, the only arguments used or checked // are isign, n, and table. // // If isign = +1 or -1, the value of isign is the sign of the // exponent used in the FFT formula. //
        n //
        Integer. // Size of the transforms (the number of elements in each // column of the input and output matrix to be transformed). // If n is not positive, scfftm or csfftm returns // without computing a transforms. //
        lot //
        Integer. // The number of transforms to be computed (or "lot size"). // This is the number of elements in each row of the input and // output matrix. If lot is not positive, csfftm or // scfftm returns without computing a transforms. //
        scale //
        Scale factor. // scfftm: real. // dzfftm: double precision. // csfftm: real. // zdfftm: double precision. // Each element of the output array is multiplied by scale // after taking the transform, as defined in the preceding // formula. //
        x //
        Input array of values to be transformed. Dimension (0:ldx-1, // 0:lot-1). // scfftm: real array. // dzfftm: double precision array. // csfftm: complex array. // zdfftm: double complex array. //
        ldx //
        Integer. // The number of rows in the x array, as it was declared in the // calling program. That is, the leading dimension of x. // scfftm, dzfftm: ldx >= MAX(n, 1). // csfftm, zdfftm: ldx >= MAX(n/2 + 1, 1). //
        ldy //
        Integer. // The number of rows in the y array, as it was declared in the // calling program (the leading dimension of y). // scfftm, dzfftm: ldy >= MAX(n/2 + 1, 1). // csfftm, zdfftm: ldy >= MAX(n, 1). //
        isys //
        Integer array of dimension (0:isys(0)). // The first element of the array specifies how many more // elements are in the array. Use isys to specify certain // processor-specific parameters or options. // // If isys(0) = 0, the default values of such parameters are // used. In this case, you can specify the argument value as // the scalar integer constant 0. // // If isys(0) > 0, isys(0) gives the upper bound of the isys // array. Therefore, if il = isys(0), user-specified // parameters are expected in isys(1) through isys(il). //
        // Output parameters: //
        //
        y //
        Output array of transformed values. Dimension (0:ldy-1, // 0:lot-1). // scfftm: complex array. // dzfftm: double complex array. // csfftm: real array. // zdfftm: double precision array. // // Each column of the output array, y, is the FFT of the // corresponding column of the input array, x, computed // according to the preceding formula. The output array may be // equivalenced to the input array. In that case, the transform // is done in place and the input array is overwritten with the // transformed values. In this case, the following conditions // on the leading dimensions must hold: // // scfftm, dzfftm: ldx = 2ldy. // csfftm, zdfftm: ldy = 2ldx. //
        table //
        Real array; dimension (15 + n). // Table of factors and trigonometric functions. // This array must be initialized by a call to scfftm (or // csfftm) with isign = 0. // // If isign = 0, table is initialized to contain trigonometric // tables needed to compute an FFT of length n. //
        work //
        Real array; dimension n. // Work array used for intermediate calculations. Its address // space must be different from that of the input and output // arrays. //
        // static void scfftm(Int isign, Int n, Int lot, Float scale, Float* x, Int ldx, Complex* y, Int ldy, Float* table, Float* work, Int isys); static void dzfftm(Int isign, Int n, Int lot, Double scale, Double* x, Int ldx, DComplex* y, Int ldy, Double* table, Double* work, Int isys); static void csfftm(Int isign, Int n, Int lot, Float scale, Complex* x, Int ldx, Float* y, Int ldy, Float* table, Float* work, Int isys); static void zdfftm(Int isign, Int n, Int lot, Double scale, DComplex* x, Int ldx, Double* y, Int ldy, Double* table, Double* work, Int isys); // // These routines compute the two-dimensional complex Fast Fourier // Transform (FFT) of the complex matrix x, and store the results in the // complex matrix y. ccfft2d does the complex-to-complex // transform and zzfft does the same for double // precision arrays. // // In FFT applications, it is customary to use zero-based subscripts; the // formulas are simpler that way. Suppose that the arrays are // dimensioned as follows: // // // COMPLEX X(0:n1-1, 0:n2-1) // COMPLEX Y(0:n1-1, 0:n2-1) // // // These routines compute the formula: // // // n2-1 n1-1 // Y(k1, k2) = scale * Sum Sum [ X(j1, j2)*w1**(j1*k1)*w2**(j2*k2) ] // j2=0 j1=0 // // for k1 = 0, ..., n1-1 // k2 = 0, ..., n2-1 // // where: // w1 = exp(isign*2*pi*i/n1) // w2 = exp(isign*2*pi*i/n2) // i = + sqrt(-1) // pi = 3.14159..., // isign = +1 or -1 // // // Different authors use different conventions for which of the // transforms, isign = +1 or isign = -1, is the forward or inverse // transform, and what the scale factor should be in either case. You // can make this routine compute any of the various possible definitions, // however, by choosing the appropriate values for isign and scale. // // The relevant fact from FFT theory is this: If you take the FFT with // any particular values of isign and scale, the mathematical inverse // function is computed by taking the FFT with -isign and // 1/(n1*n2*scale). In particular, if you use isign = +1 and scale = 1.0 // for the forward FFT, you can compute the inverse FFT by using isign = // -1 and scale = 1.0/(n1*n2). // //

        Algorithm

        // These routines use a routine very much like ccfftm/zzfftm to do // multiple FFTs first on all columns in an input matrix and then on all // of the rows. // //

        Initialization

        // The table array stores factors of n1 and n2 and also trigonometric // tables that are used in calculation of the FFT. This table must be // initialized by calling the routine with isign = 0. If the values of // the problem sizes, n1 and n2, do not change, the table does not have // to be reinitialized. // //

        Dimensions

        // In the preceding description, it is assumed that array subscripts were // zero-based, as is customary in FFT applications. Thus, the input and // output arrays are declared as follows: // // // COMPLEX X(0:ldx-1, 0:n2-1) // COMPLEX Y(0:ldy-1, 0:n2-1) // // // However, the calling sequence does not change if you prefer to use the // more customary Fortran style with subscripts starting at 1. The same // values of ldx and ldy would be passed to the subroutine even if the // input and output arrays were dimensioned as follows: // // // COMPLEX X(ldx, n2) // COMPLEX Y(ldy, n2) // // // // All examples here are for Origin series only. // // Example 1: Initialize the TABLE array in preparation for doing a // two-dimensional FFT of size 128 by 256. In this case only the isign, // n1, n2, and table arguments are used; you can use dummy arguments or // zeros for other arguments. // // // REAL TABLE ((30 + 256) + (30 + 512)) // CALL CCFFT2D (0, 128, 256, 0.0, DUMMY, 1, DUMMY, 1, // & TABLE, DUMMY, 0) // // // Example 2: X and Y are complex arrays of dimension (0:128, 0:255). // The first 128 elements of each column contain data. For performance // reasons, the extra element forces the leading dimension to be an odd // number. Take the two-dimensional FFT of X and store it in Y. // Initialize the TABLE array, as in example 1. // // // COMPLEX X(0:128, 0:255) // COMPLEX Y(0:128, 0:255) // REAL TABLE((30 + 256) + (30 + 512)) // REAL WORK(2*128*256) // ... // CALL CCFFT2D(0, 128, 256, 1.0, X, 129, Y, 129, TABLE, WORK, 0) // CALL CCFFT2D(1, 128, 256, 1.0, X, 129, Y, 129, TABLE, WORK, 0) // // // Example 3: With X and Y as in example 2, take the inverse FFT of Y // and store it back in X. The scale factor 1/(128*256) is used. Assume // that the TABLE array is already initialized. // // // CALL CCFFT2D(-1, 128, 256, 1.0/(128.0*256.0), Y, 129, // & X, 129, TABLE, WORK, 0) // // // Example 4: Perform the same computation as in example 2, but assume // that the lower bound of each array is 1, rather than 0. The // subroutine calls are not changed. // // // COMPLEX X(129, 256) // COMPLEX Y(129, 256) // ... // CALL CCFFT2D(0, 128, 256, 1.0, X, 129, Y, 129, TABLE, WORK, 0) // CALL CCFFT2D(1, 128, 256, 1.0, X, 129, Y, 129, TABLE, WORK, 0) // // // Example 5: Perform the same computation as in example 4, but put the // output back in array X to save storage space. Assume that the TABLE // array is already initialized. // // // COMPLEX X(129, 256) // ... // CALL CCFFT2D(1, 128, 256, 1.0, X, 129, X, 129, TABLE, WORK, 0) // // // // Input parameters: //
        //
        isign //
        Integer. // Specifies whether to initialize the table array or to do the // forward or inverse transform as follows: // // If isign = 0, the routine initializes the table array and // returns. In this case, the only arguments used or checked // are isign, n1, n2, table. // // If isign = +1 or -1, the value of isign is the sign of the // exponent used in the FFT formula. //
        n1 //
        Integer. // Transform size in the first dimension. If n1 is not // positive, the routine returns without performing a // transform. //
        n2 //
        Integer. // Transform size in the second dimension. If n2 is not // positive, the routine returns without performing a // transform. //
        scale //
        Scale factor. // ccfft2d: real. // zzfft2d: double precision. // Each element of the output array is multiplied by scale // factor after taking the Fourier transform, as defined // previously. //
        x //
        Array of dimension (0:ldx-1, 0:n2-1). // ccfft2d: complex array. // zzfft2d: double complex array. // Input array of values to be transformed. //
        ldx //
        Integer. // The number of rows in the x array, as it was declared in the // calling program (the leading dimension of x). ldx >= // MAX(n1, 1). //
        ldy //
        Integer. // // The number of rows in the y array, as it was declared in the // calling program (the leading dimension of y). ldy >= // MAX(n1, 1). //
        isys //
        Algorithm used; value dependent on hardware system. Currently, no // special options are supported; therefore, you must always specify // an isys argument as constant 0. //
        // Output parameters: //
        //
        y //
        Array of dimension (0:ldy-1, 0:n2-1). // ccfft2d: complex array. // zzfft2d: double complex array. // Output array of transformed values. The output array may be // the same as the input array, in which case, the transform is // done in place (the input array is overwritten with the // transformed values). In this case, it is necessary that // ldx = ldy. //
        table //
        Real array; dimension (30+ 2 * n1) + (30 + 2 * n2). // // Table of factors and trigonometric functions. // // If isign = 0, the routine initializes table (table is output // only). // // If isign = +1 or -1, the values in table are assumed to be // initialized already by a prior call with isign = 0 (table is // input only). //
        work //
        Real array; dimension 2 * (n1*n2). // // Work array. This is a scratch array used for intermediate // calculations. Its address space must be different from that // of the input and output arrays. //
        // static void ccfft2d(Int isign, Int n1, Int n2, Float scale, Complex* x, Int ldx, Complex* y, Int ldy, Float* table, Float* work, Int isys); static void zzfft2d(Int isign, Int n1, Int n2, Double scale, DComplex* x, Int ldx, DComplex* y, Int ldy, Double* table, Double* work, Int isys); // // scfft2d/dzfft2d computes the two-dimensional Fast Fourier // Transform (FFT) of the real matrix x, and it stores the results in the // complex matrix y. csfft2d/zdfft2d computes the corresponding // inverse transform. // // In FFT applications, it is customary to use zero-based subscripts; the // formulas are simpler that way. First the function of scfft2d is // described. Suppose the arrays are dimensioned as follows: // // // REAL X(0:ldx-1, 0:n2-1) // COMPLEX Y(0:ldy-1, 0:n2-1) // // where ldx >= n1 ldy >= (n1/2) + 1. // // // scfft2d computes the formula: // // // n2-1 n1-1 // Y(k1, k2) = scale * Sum Sum [ X(j1, j2)*w1**(j1*k1)*w2**(j2*k2) ] // j2=0 j1=0 // // for k1 = 0, ..., n1/2 + 1 // k2 = 0, ..., n2-1 // // where: // w1 = exp(isign*2*pi*i/n1) // w2 = exp(isign*2*pi*i/n2) // i = + sqrt(-1) // pi = 3.14159..., // isign = +1 or -1 // // // Different authors use different conventions for which of the // transforms, isign = +1 or isign = -1, is the forward or inverse // transform, and what the scale factor should be in either case. You // can make these routines compute any of the various possible // definitions, however, by choosing the appropriate values for isign and // scale. // // The relevant fact from FFT theory is this: If you take the FFT with // any particular values of isign and scale, the mathematical inverse // function is computed by taking the FFT with -isign and 1/(n1 * n2 * // scale). In particular, if you use isign = +1 and scale = 1.0 for the // forward FFT, you can compute the inverse FFT by using isign = -1 and // scale = 1.0/(n1 . n2). // // scfft2d is very similar in function to ccfft2d, but // it takes the real-to-complex transform in the first dimension, followed by // the complex-to-complex transform in the second dimension. // // csfft2d does the reverse. It takes the complex-to-complex FFT // in the second dimension, followed by the complex-to-real FFT in the first // dimension. // // See the scfft man page for more information about real-to-complex // and complex-to-real FFTs. The two-dimensional analog of the conjugate // formula is as follows: // // // Y = conjg Y // k , k n1 - k , n2 - k // 1 2 1 2 // // for n1/2 < k <= n1 - 1 // 1 // // 0 <= k <= n2 - 1 // 2 // where the notation conjg(z) represents the complex conjugate of z. // // // Thus, you have to compute only (slightly more than) half of the output // values, namely: // // // Y for 0 <= k <= n1/2 0 <= k <= n2 - 1 // k , k 1 2 // 1 2 // // //

        Algorithm

        // scfft2d uses a routine similar to scfftm to do a // real-to-complex FFT on the columns, then uses a routine similar to // ccfftm to do a complex-to-complex FFT on the rows. // // csfft2d uses a routine similar to ccfftm to do a // complex-to-complex FFT on the rows, then uses a routine similar to // csfftm to do a complex-to-real FFT on the columns. // //

        Table Initialization

        // The table array stores factors of n1 and n2, and trigonometric tables // that are used in calculation of the FFT. table must be initialized by // calling the routine with isign = 0. table does not have to be // reinitialized if the values of the problem sizes, n1 and n2, do not // change. // //

        Dimensions

        // In the preceding description, it is assumed that array subscripts were // zero-based, as is customary in FFT applications. Thus, the input and // output arrays are declared: // // // REAL X(0:ldx-1, 0:n2-1) // COMPLEX Y(0:ldy-1, 0:n2-1) // // // No change is made in the calling sequence, however, if you prefer to // use the more customary Fortran style with subscripts starting at 1. // The same values of ldx and ldy would be passed to the subroutine even // if the input and output arrays were dimensioned as follows: // // // REAL X(ldx, n2) // COMPLEX Y(ldy, n2) // // // // The following examples are for Origin series only. // // Example 1: Initialize the TABLE array in preparation for doing a // two-dimensional FFT of size 128 by 256. In this case, only the isign, // n1, n2, and table arguments are used; you can use dummy arguments or // zeros for other arguments. // // // REAL TABLE ((15 + 128) + 2(15 + 256)) // CALL SCFFT2D (0, 128, 256, 0.0, DUMMY, 1, DUMMY, 1, // & TABLE, DUMMY, 0) // // // Example 2: X is a real array of size (0:128, 0: 255), and Y is a // complex array of dimension (0:64, 0:255). The first 128 elements of // each column of X contain data; for performance reasons, the extra // element forces the leading dimension to be an odd number. Take the // two-dimensional FFT of X and store it in Y. Initialize the TABLE // array, as in example 1. // // // REAL X(0:128, 0:255) // COMPLEX Y(0:64, 0:255) // REAL TABLE ((15 + 128) + 2(15 + 256)) // REAL WORK(128*256) // ... // CALL SCFFT2D(0, 128, 256, 1.0, X, 129, Y, 65, TABLE, WORK, 0) // CALL SCFFT2D(1, 128, 256, 1.0, X, 129, Y, 65, TABLE, WORK, 0) // // // Example 3: With X and Y as in example 2, take the inverse FFT of Y // and store it back in X. The scale factor 1/(128*256) is used. Assume // that the TABLE array is initialized already. // // // CALL CSFFT2D(-1, 128, 256, 1.0/(128.0*256.0), Y, 65, // & X, 130, TABLE, WORK, 0) // // // Example 4: Perform the same computation as in example 2, but assume // that the lower bound of each array is 1, rather than 0. No change is // needed in the subroutine calls. // // // REAL X(129, 256) // COMPLEX Y(65, 256) // ... // CALL SCFFT2D(0, 128, 256, 1.0, X, 129, Y, 65, TABLE, WORK, 0) // CALL SCFFT2D(1, 128, 256, 1.0, X, 129, Y, 65, TABLE, WORK, 0) // // // Example 5: Perform the same computation as in example 4, but // equivalence the input and output arrays to save storage space. In // this case, a row must be added to X, because it is equivalenced to a // complex array. Assume that TABLE is already initialized. // // // REAL X(130, 256) // COMPLEX Y(65, 256) // EQUIVALENCE ( X(1, 1), Y(1, 1) ) // ... // CALL SCFFT2D(1, 128, 256, 1.0, X, 130, Y, 65, TABLE, WORK, 0) // // // // Input parameters: //
        //
        isign //
        Integer. // Specifies whether to initialize the table array or to do the // forward or inverse Fourier transform, as follows: // // If isign = 0, the routine initializes the table array and // returns. In this case, the only arguments used or checked // are isign, n, and table. // // If isign = +1 or -1, the value of isign is the sign of the // exponent used in the FFT formula. //
        n1 //
        Integer. // Transform size in the first dimension. If n1 is not // positive, scfft2d returns without calculating a // transform. //
        n2 //
        Integer. // Transform size in the second dimension. If n2 is not // positive, scfft2d returns without calculating a // transform. //
        scale //
        Scale factor. // scfft2d: real. // dzfft2d: double precision. // csfft2d: real. // zdfft2d: double precision. // Each element of the output array is multiplied by scale // factor after taking the Fourier transform, as defined // previously. //
        x //
        Array of dimension (0:ldx-1, 0:n2-1). // scfft2d: real array. // dzfft2d: double precision array. // csfft2d: complex array. // zdfft2d: double complex array. // // Array of values to be transformed. //
        ldx //
        Integer. // The number of rows in the x array, as it was declared in the // calling program. That is, the leading dimension of x. // scfft2d, dzfft2d: ldx >= MAX(n1, 1). // csfft2d, zdfft2d: ldx >= MAX(n1/2 + 1, 1). //
        ldy //
        Integer. // // The number of rows in the y array, as it was declared in the // calling program (the leading dimension of y). // // scfft2d, dzfft2d: ldy >= MAX(n1/2 + 1, 1). // csfft2d, zdfft2d: ldy >= MAX(n1 + 2, 1). // // In the complex-to-real routine, two extra elements are in // the first dimension (ldy >= n1 + 2, rather than just ldy >= // n1). These elements are needed for intermediate storage // during the computation. On exit, their value is undefined. //
        isys //
        Integer array of dimension (0:isys(0)). // The first element of the array specifies how many more // elements are in the array. Use isys to specify certain // processor-specific parameters or options. // // If isys(0) = 0, the default values of such parameters are // used. In this case, you can specify the argument value as // the scalar integer constant 0. // // If isys(0) > 0, isys(0) gives the upper bound of the isys // array. Therefore, if il = isys(0), user-specified // parameters are expected in isys(1) through isys(il). //
        isys //
        Algorithm used; value dependent on hardware system. Currently, no // special options are supported; therefore, you must always specify // an isys argument as constant 0. //
        // Output parameters: //
        //
        y //
        scfft2d: complex array. // dzfft2d: double complex array. // csfft2d: real array. // zdfft2d: double precision array. // // Output array of transformed values. The output array can be // the same as the input array, in which case, the transform is // done in place and the input array is overwritten with the // transformed values. In this case, it is necessary that the // following equalities hold: // // scfft2d, dzfft2d: ldx = 2 * ldy. // csfft2d, zdfft2d: ldy = 2 * ldx. //
        table //
        Real array; dimension (15 + n1) + 2(15 + n2). // // Table of factors and trigonometric functions. // // If isign = 0, the routine initializes table (table is output // only). // // If isign = +1 or -1, the values in table are assumed to be // initialized already by a prior call with isign = 0 (table is // input only). //
        work //
        Real array; dimension (n1 * n2). // // Work array. This is a scratch array used for intermediate // calculations. Its address space must be different from that // of the input and output arrays. //
        // static void scfft2d(Int isign, Int n1, Int n2, Float scale, Float* x, Int ldx, Complex* y, Int ldy, Float* table, Float* work, Int isys); static void dzfft2d(Int isign, Int n1, Int n2, Double scale, Double* x, Int ldx, DComplex* y, Int ldy, Double* table, Double* work, Int isys); static void csfft2d(Int isign, Int n1, Int n2, Float scale, Complex* x, Int ldx, Float* y, Int ldy, Float* table, Float* work, Int isys); static void zdfft2d(Int isign, Int n1, Int n2, Double scale, DComplex* x, Int ldx, Double* y, Int ldy, Double* table, Double* work, Int isys); // // These routines compute the three-dimensional complex FFT of the // complex matrix X, and store the results in the complex matrix Y. // // In FFT applications, it is customary to use zero-based subscripts; the // formulas are simpler that way. So suppose the arrays are dimensioned // as follows: // // // COMPLEX X(0:n1-1, 0:n2-1, 0:n3-1) // COMPLEX Y(0:n1-1, 0:n2-1, 0:n3-1) // // // These routines compute the formula: // // // Y(k1,k2,k3) = // n1-1 n2-1 n3-1 // scale * Sum Sum Sum [X(j1,j2,j3)*w1**(j1*k1)*w2**(j2*k2)*w3**(j3*k3)] // j1=0 j2=0 j3=0 // // for k1 = 0, ..., n1 - 1, // k2 = 0, ..., n2 - 1, // k3 = 0, ..., n3 - 1, // // where: // w1 = exp(isign*2*pi*i/n1), // w2 = exp(isign*2*pi*i/n2), // w3 = exp(isign*2*pi*i/n3), // i = + sqrt(-1) // pi = 3.14159... // isign = +1 or -1 // // // Different authors use different conventions for which of the // transforms, isign = +1 or isign = -1, is the forward or inverse // transform, and what the scale factor should be in either case. You // can make this routine compute any of the various possible definitions, // however, by choosing the appropriate values for isign and scale. // // The relevant fact from FFT theory is this: If you take the FFT with // any particular values of isign and scale, the mathematical inverse // function is computed by taking the FFT with -isign and 1/(n1 * n2 * n3 // * scale). In particular, if you use isign = +1 and scale = 1.0 for // the forward FFT, you can compute the inverse FFT by using isign = -1 // and scale = 1/(n1 . n2 . n3). // // // The following examples are for Origin series only. // // Example 1: Initialize the TABLE array in preparation for doing a // three-dimensional FFT of size 128 by 128 by 128. In this case, only // the isign, n1, n2, n3, and table arguments are used; you can use dummy // arguments or zeros for other arguments. // // // REAL TABLE ((30 + 256) + (30 + 256) + (30 + 256)) // CALL CCFFT3D (0, 128, 128, 128, 0.0, DUMMY, 1, 1, DUMMY, 1, 1, // & TABLE, DUMMY, 0) // // // Example 2: X and Y are complex arrays of dimension (0:128, 0:128, // 0:128). The first 128 elements of each dimension contain data; for // performance reasons, the extra element forces the leading dimensions // to be odd numbers. Take the three-dimensional FFT of X and store it // in Y. Initialize the TABLE array, as in example 1. // // // COMPLEX X(0:128, 0:128, 0:128) // COMPLEX Y(0:128, 0:128, 0:128) // REAL TABLE ((30+256) + (30 + 256) + (30 + 256)) // REAL WORK 2(128*128*128) // ... // CALL CCFFT3D(0, 128, 128, 128, 1.0, DUMMY, 1, 1, // & DUMMY, 1, 1, TABLE, WORK, 0) // CALL CCFFT3D(1, 128, 128, 128, 1.0, X, 129, 129, // & Y, 129, 129, TABLE, WORK, 0) // // // Example 3: With X and Y as in example 2, take the inverse FFT of Y // and store it back in X. The scale factor 1.0/(128.0**3) is used. // Assume that the TABLE array is already initialized. // // // CALL CCFFT3D(-1, 128, 128, 128, 1.0/(128.0**3), Y, 129, 129, // & X, 129, 129, TABLE, WORK, 0) // // // Example 4: Perform the same computation as in example 2, but assume // that the lower bound of each array is 1, rather than 0. The // subroutine calls do not change. // // // COMPLEX X(129, 129, 129) // COMPLEX Y(129, 129, 129) // ... // CALL CCFFT3D(0, 128, 128, 128, 1.0, DUMMY, 1, 1, // & DUMMY, 1, 1, TABLE, WORK, 0) // CALL CCFFT3D(1, 128, 128, 128, 1.0, X, 129, 129, // & Y, 129, 129, TABLE, WORK, 0) // // // Example 5: Perform the same computation as in example 4, but put the // output back in the array X to save storage space. Assume that the // TABLE array is already initialized. // // // COMPLEX X(129, 129, 129) // ... // CALL CCFFT3D(1, 128, 128, 128, 1.0, X, 129, 129, // & X, 129, 129, TABLE, WORK, 0) // // // // Input parameters: //
        //
        isign //
        Integer. // Specifies whether to initialize the table array or to do the // forward or inverse Fourier transform, as follows: // // If isign = 0, the routine initializes the table array and // returns. In this case, the only arguments used or checked // are isign, n1, n2, n3, and table. // // If isign = +1 or -1, the value of isign is the sign of the // exponent used in the FFT formula. // //
        n1 //
        Integer. // Transform size in the first dimension. If n1 is not // positive, the routine returns without computing a transform. // //
        n2 //
        Integer. // Transform size in the second dimension. If n2 is not // positive, the routine returns without computing a transform. // //
        n3 //
        Integer. // Transform size in the third dimension. If n3 is not // positive, the routine returns without computing a transform. // //
        scale //
        Scale factor. // ccfft3d: real. // zzfft3d: double precision. // // Each element of the output array is multiplied by scale // after taking the Fourier transform, as defined previously. // //
        x //
        Array of dimension (0:ldx-1, 0:ldx2-1, 0:n3-1). // ccfft3d: complex array. // zzfft3d: double complex array. // // Input array of values to be transformed. // //
        ldx //
        Integer. // The first dimension of x, as it was declared in the calling // program (the leading dimension of x). ldx >= MAX(n1, 1). // //
        ldx2 //
        Integer. // The second dimension of x, as it was declared in the calling // program. ldx2 >= MAX(n2, 1). // //
        ldy //
        Integer. // The first dimension of y, as it was declared in the calling // program (the leading dimension of y). ldy >= MAX(n1, 1). // //
        ldy2 //
        Integer. // The second dimension of y, as it was declared in the calling // program. ldy2 >= MAX(n2, 1). // //
        isys //
        Algorithm used; value dependent on hardware system. Currently, no // special options are supported; therefore, you must always specify // an isys argument as constant 0. // // isys = 0 or 1 depending on the amount of workspace the user // can provide to the routine. //
        // Output parameters: //
        //
        y //
        Array of dimension (0:ldy-1, 0:ldy2-1, 0:n3-1). // ccfft3d: complex array. // zzfft3d: double complex array. // // Output array of transformed values. The output array may be // the same as the input array, in which case, the transform is // done in place; that is, the input array is overwritten with // the transformed values. In this case, it is necessary that // ldx = ldy, and ldx2 = ldy2. // //
        table //
        Real array; dimension 30 + 2 * n1) + (30 + 2 * n2) + (30 + 2 * n3). // // Table of factors and trigonometric functions. If isign = 0, // the routine initializes table (table is output only). If // isign = +1 or -1, the values in table are assumed to be // initialized already by a prior call with isign = 0 (table is // input only). // //
        work //
        Real array; dimension (n1 * n2 * n3). // // Work array. This is a scratch array used for intermediate // calculations. Its address space must be different from that // of the input and output arrays. // //
        // static void ccfft3d(Int isign, Int n1, Int n2, Int n3, Float scale, Complex* x, Int ldx, Int ldx2, Complex* y, Int ldy, Int ldy2, Float* table, Float* work, Int isys); static void zzfft3d(Int isign, Int n1, Int n2, Int n3, Double scale, DComplex* x, Int ldx, Int ldx2, DComplex* y, Int ldy, Int ldy2, Double* table, Double* work, Int isys); // // These are C++ wrapper functions for the 3D real-to-complex and // complex-to-real transform routines in the SGI/Cray Scientific Library // (SCSL). The purpose of these definitions is to overload the functions so // that C++ users can access the functions in SCSL with identical function // names. // // // Currently, the SCSL is available only on SGI machines. // // // scfft3d/dzfft3d computes the three-dimensional Fast Fourier // Transform (FFT) of the real matrix X, and it stores the results in the // complex matrix Y. csfft3d/zdfft3d computes the corresponding // inverse transform. // // In FFT applications, it is customary to use zero-based subscripts; the // formulas are simpler that way. First, the function of SCFFT3D is // described. Suppose the arrays are dimensioned as follows: // // // REAL X(0:ldx-1, 0:ldx2-1, 0:n3-1) // COMPLEX Y(0:ldy-1, 0:ldy2-1, 0:n3-1) // // // scfft3d computes the formula: // // // Y(k1,k2,k3) = // n1-1 n2-1 n3-1 // scale * Sum Sum Sum [X(j1,j2,j3)*w1**(j1*k1)*w2**(j2*k2)*w3**(j3*k3)] // j1=0 j2=0 j3=0 // // for k1 = 0, ..., n1/2, // k2 = 0, ..., n2 - 1, // k3 = 0, ..., n3 - 1, // // where: // w1 = exp(isign*2*pi*i/n1), // w2 = exp(isign*2*pi*i/n2), // w3 = exp(isign*2*pi*i/n3), // i = + sqrt(-1) // pi = 3.14159... // isign = +1 or -1 // // // Different authors use different conventions for which of the // transforms, isign = +1 or isign = -1, is the forward or inverse // transform, and what the scale factor should be in either case. You // can make these routines compute any of the various possible // definitions, however, by choosing the appropriate values for isign and // scale. // // The relevant fact from FFT theory is this: If you take the FFT with // any particular values of isign and scale, the mathematical inverse // function is computed by taking the FFT with -isign and 1/(n1 * n2 * n3 // * scale). In particular, if you use isign = +1 and scale = 1.0 for // the forward FFT, you can compute the inverse FFT by isign = -1 and // // // scale = 1.0/(n1*n2*n3). // // // scfft3d is very similar in function to ccfft3d, but // it takes the real-to-complex transform in the first dimension, followed by // the complex-to-complex transform in the second and third dimensions. // // csfft3d does the reverse. It takes the complex-to-complex FFT // in the third and second dimensions, followed by the complex-to-real FFT in // the first dimension. // // See the scfftm man page for more information about // real-to-complex and complex-to-real FFTs. The three dimensional analog of // the conjugate formula is as follows: // // // Y = conjg Y // k ,k ,k n1 - k , n2 - k , n3 - k // 1 2 3 1 2 3 // // for n1/2 < k <= n1 - 1 // 1 // // 0 <= k <= n2 - 1 // 2 // // 0 <= k <= n3 - 1 // 3 // where the notation conjg(z) represents the complex conjugate of z. // // // Thus, you have to compute only (slightly more than) half out the // output values, namely: // // // Y // k ,k ,k // 1 2 3 // // for 0 <= k <= n1/2 // 1 // // 0 <= k <= n2 - 1 // 2 // // 0 <= k <= n3 - 1 // // //

        Algorithm

        // scfft3d uses a routine similar to scfftm to do // multiple FFTs first on all columns of the input matrix, then uses a routine // similar to ccfftm on all rows of the result, and then on all // planes of that result. See scfftm and ccfftm for // more information about the algorithms used. // // // The following examples are for Origin series only. // // Example 1: Initialize the TABLE array in preparation for doing a // three-dimensional FFT of size 128 by 128 by 128. In this case only // the isign, n1, n2, n3, and table arguments are used; you can use dummy // arguments or zeros for other arguments. // // // REAL TABLE ((15 + 128) + 2(15+128) + 2( 15 + 128)) // CALL SCFFT3D (0, 128, 128, 128, 0.0, DUMMY, 1, 1, DUMMY, 1, 1, // & TABLE, DUMMY, 0) // // // Example 2: X is a real array of size (0:128, 0:128, 0:128). The // first 128 elements of each dimension contain data; for performance // reasons, the extra element forces the leading dimensions to be odd // numbers. Y is a complex array of dimension (0:64, 0:128, 0:128). // Take the three-dimensional FFT of X and store it in Y. Initialize the // TABLE array, as in example 1. // // // REAL X(0:128, 0:128, 0:128) // COMPLEX Y(0:64, 0:128, 0:128) // REAL TABLE ((15+128) + 2(15 + 128) + 2(15 + 128)) // REAL WORK(128*128*128) // ... // CALL SCFFT3D(0, 128, 128, 128, 1.0, X, 129, 129, // & Y, 65, 129, TABLE, WORK, 0) // CALL SCFFT3D(1, 128, 128, 128, 1.0, X, 129, 129, // & Y, 65, 129, TABLE, WORK, 0) // // // Example 3: With X and Y as in example 2, take the inverse FFT of Y // and store it back in X. The scale factor 1/(128**3) is used. Assume // that the TABLE array is initialized already. // // // CALL CSFFT3D(-1, 128, 128, 128, 1.0/128.0**3, Y, 65, 129, // & X, 130, 129, TABLE, WORK, 0) // // // Example 4: Perform the same computation as in example 2, but assume // that the lower bound of each array is 1, rather than 0. No change is // made in the subroutine calls. // // // REAL X(129, 129, 129) // COMPLEX Y(65, 129, 129) // REAL TABLE ((15+128) + 2(15 + 128) + 2(15 + 128)) // REAL WORK(128*128*128) // ... // CALL SCFFT3D(0, 128, 128, 128, 1.0, X, 129, 129, // & Y, 65, 129, TABLE, WORK, 0) // CALL SCFFT3D(1, 128, 128, 128, 1.0, X, 129, 129, // & X, 129, 129, TABLE, WORK, 0) // // // Example 5: Perform the same computation as in example 4, but // equivalence the input and output arrays to save storage space. Assume // that the TABLE array is initialized already. // // // REAL X(130, 129, 129) // COMPLEX Y(65, 129, 129) // EQUIVALENCE (X(1, 1, 1), Y(1, 1, 1)) // ... // CALL SCFFT3D(1, 128, 128, 128, 1.0, X, 130, 129, // & Y, 65, 129, TABLE, WORK, 0) // // // // Input parameters: //
        //
        isign //
        Integer. // Specifies whether to initialize the table array or to do the // forward or inverse Fourier transform, as follows: // // If isign = 0, the routine initializes the table array and // returns. In this case, the only arguments used or checked // are isign, n1, n2, n3, and table. // // If isign = +1 or -1, the value of isign is the sign of the // exponent used in the FFT formula. // //
        n1 //
        Integer. // Transform size in the first dimension. If n1 is not // positive, scfft3d returns without computing a transform. // //
        n2 //
        Integer. // Transform size in the second dimension. If n2 is not // positive, scfft3d returns without computing a transform. // //
        n3 //
        Integer. // Transform size in the third dimension. If n3 is not // positive, scfft3d returns without computing a transform. // //
        scale //
        Scale factor. // scfft3d: real. // dzfft3d: double precision. // csfft3d: real. // zdfft3d: double precision. // Each element of the output array is multiplied by scale // after taking the Fourier transform, as defined previously. // //
        x //
        Array of dimension (0:ldx-1, 0:ldx2-1, 0:n3-1). // scfft3d: real array. // dzfft3d: double precision array. // csfft3d: complex array. // zdfft3d: double complex array. // // Array of values to be transformed. // //
        ldx //
        Integer. // The first dimension of x, as it was declared in the calling // program (the leading dimension of x). // // scfft3d, dzfft3d: ldx >= MAX(n1, 1). // csfft3d, zdfft3d: ldx >= MAX(n1/2 + 1, 1). // //
        ldx2 //
        Integer. // The second dimension of x, as it was declared in the calling // program. ldx2 >= MAX(n2, 1). // //
        ldy //
        Integer. // The first dimension of y, as it was declared in the calling // program; that is, the leading dimension of y. // // scfft3d, dzfft3d: ldy >= MAX(n1/2 + 1, 1). // csfft3d, zdfft3d: ldy >= MAX(n1 + 2, 1). // // In the complex-to-real routine, two extra elements are in // the first dimension (that is, ldy >= n1 + 2, rather than // just ldy >= n1). These elements are needed for intermediate // storage during the computation. On exit, their value is // undefined. // //
        ldy2 //
        Integer. // The second dimension of y, as it was declared in the calling // program. ldy2 >= MAX(n2, 1). // //
        isys //
        Algorithm used; value dependent on hardware system. Currently, no // special options are supported; therefore, you must always specify // an isys argument as constant 0. // // isys = 0 or 1 depending on the amount of workspace the user // can provide to the routine. //
        // Output parameters: //
        //
        y //
        Array of dimension (0:ldy-1, 0:ldy2-1, 0:n3-1). // scfft3d: complex array. // dzfft3d: double complex array. // csfft3d: real array. // zdfft3d: double precision array. // // Output array of transformed values. The output array can be // the same as the input array, in which case, the transform is // done in place; that is, the input array is overwritten with // the transformed values. In this case, it is necessary that // the following equalities hold: // // scfft3d, dzfft3d: ldx = 2 * ldy, and ldx2 = ldy2. // csfft3d, zdfft3d: ldy = 2 * ldx, and ldx2 = ldy2. // //
        table //
        Real array; dimension (15 + n1) + 2(15 + n2) + 2(15 + n3). // // Table of factors and trigonometric functions. // // This array must be initialized by a call to scfft3d or // csfft3d with isign = 0. // // If isign = 0, table is initialized to contain trigonometric // tables needed to compute a three-dimensional FFT of size n1 // by n2 by n3. If isign = +1 or -1, the values in table are // assumed to be initialized already by a prior call with isign // = 0. // //
        work //
        Real array; dimension n1 * n2 * n3. // // Work array. This is a scratch array used for intermediate // calculations. Its address space must be different from that // of the input and output arrays. // //
        // static void scfft3d(Int isign, Int n1, Int n2, Int n3, Float scale, Float* x, Int ldx, Int ldx2, Complex* y, Int ldy, Int ldy2, Float* table, Float* work, Int isys); static void dzfft3d(Int isign, Int n1, Int n2, Int n3, Double scale, Double* x, Int ldx, Int ldx2, DComplex* y, Int ldy, Int ldy2, Double* table, Double* work, Int isys); static void csfft3d(Int isign, Int n1, Int n2, Int n3, Float scale, Complex* x, Int ldx, Int ldx2, Float* y, Int ldy, Int ldy2, Float* table, Float* work, Int isys); static void zdfft3d(Int isign, Int n1, Int n2, Int n3, Double scale, DComplex* x, Int ldx, Int ldx2, Double* y, Int ldy, Int ldy2, Double* table, Double* work, Int isys); // }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/Smooth.h000066400000000000000000000063731476623553700206460ustar00rootroot00000000000000//# Smooth.h: smooth vectors and arrays //# Copyright (C) 2010 by ESO (in the framework of the ALMA collaboration) //# Copyright (C) 1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_SMOOTH_H #define SCIMATH_SMOOTH_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Smooth a Vector or the rows of a 2D Array taking into account // flags which are supplied in a Vector/Array of the same shape. // Modify the flags as necessary to mark channels for which the // smoothing could not be done because needed channels were flagged. // // // // // //
      • Vector //
      • Array // // // self-explanatory // // // Perform smoothing on Vectors or Arrays // // // // Smooth::hanning(outv, // the output // outFlags, // the output mask // yin, // the input // yinFlags, // the input mask // False, // for flagging: good is not true // True); // use the default scheme for producing output flags // // // // This is used, e.g., for visibilities in spectral channel vectors. // // //
      • // template class Smooth { Smooth(){}; public: // Hanning smoothing static void hanning(Vector& out, Vector& outmask, Vector& in, Vector& mask, Bool TrueIsGood, Bool relaxed=True); // as above but calling hanningSmooth for each row of the 2D array static void hanning(Array& out, Array& outmask, Array& in, Array& mask, Bool TrueIsGood, Bool relaxed=True); }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Mathematics/Smooth.tcc000066400000000000000000000114531476623553700211630ustar00rootroot00000000000000//# Smooth.tcc: perform smoothing on vectors and arrays //# Copyright (C) 2010 by ESO (in the framework of the ALMA collaboration) //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_SMOOTH_TCC #define SCIMATH_SMOOTH_TCC // #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template void Smooth::hanning(Vector& out, Vector& outmask, Vector& in, Vector& mask, Bool TrueIsGood, Bool relaxed) { DebugAssert(out.shape().isEqual(in.shape()), AipsError); DebugAssert(outmask.shape().isEqual(mask.shape()), AipsError); Vector< Vector > weights(8); Vector vals(3); vals = 0.0; weights[0] = vals;// FFF vals[0] = 1.0; vals[1] = 0.0; vals[2] = 0.0; weights[1] = vals;// TFF vals[0] = 0.0; vals[1] = 1.0; vals[2] = 0.0; weights[2] = vals;// FTF vals[0] = 1.0/3.0; vals[1] = 2.0/3.0; vals[2] = 0.0; weights[3] = vals;// TTF vals[0] = 0.0; vals[1] = 0.0; vals[2] = 1.0;weights[4] = vals;// FFT vals[0] = 0.5; vals[1] = 0.0; vals[2] = 0.5; weights[5] = vals;// TFT vals[0] = 0.0; vals[1] = 2.0/3.0; vals[2] = 1.0/3.0; weights[6] = vals;// FTT vals[0] = 0.25; vals[1] = 0.5; vals[2] = 0.25; weights[7] = vals;// TTT Vector weighted(8); if (relaxed) { weighted = False; weighted[7] = True; } else { weighted = True; weighted[0] = False; } // make special case for first and last out[0] = in[0]; outmask[0] = mask[0]; uInt nelm1 = in.nelements()-1; uInt m; Vector* w; if(nelm1>0){ m = 2*(mask[0]==TrueIsGood) + 4*(mask[1]==TrueIsGood); w = &(weights[m]); if (weighted[m]) { out[0] = (*w)[1]*in[0] + (*w)[2]*in[1]; outmask[0] = True==TrueIsGood; } else{ if(mask[0]==TrueIsGood){ out[0] = (*w)[1]*in[0] + (*w)[2]*in[1]; } else{ out[0] = in[0]; } outmask[0] = False==TrueIsGood; } m = (mask[nelm1]==TrueIsGood) + 2*(mask[nelm1-1]==TrueIsGood); w = &(weights[m]); if (weighted[m]) { out[nelm1] = (*w)[1]*in[nelm1] + (*w)[0]*in[nelm1-1]; outmask[nelm1] = True==TrueIsGood; } else{ if(mask[nelm1]==TrueIsGood){ out[nelm1] = (*w)[1]*in[nelm1] + (*w)[0]*in[nelm1-1]; } else{ out[nelm1] = in[nelm1]; } outmask[nelm1] = False==TrueIsGood; } } // loop from 1..n-2 for(uInt i=1; i < nelm1; i++){ m = (mask[i-1]==TrueIsGood) + 2*(mask[i]==TrueIsGood) + 4*(mask[i+1]==TrueIsGood); w = &(weights[m]); if (weighted[m]) { out[i] = (*w)[0]*in[i-1] + (*w)[1]*in[i] + (*w)[2]*in[i+1]; outmask[i] = True==TrueIsGood; } else{ if(mask[i]==TrueIsGood){ out[i] = (*w)[0]*in[i-1] + (*w)[1]*in[i] + (*w)[2]*in[i+1]; } else{ out[i] = in[i]; } outmask[i] = False==TrueIsGood; } } } template void Smooth::hanning(Array& out, Array& outmask, Array& in, Array& mask, Bool TrueIsGood, Bool relaxed) { Matrix min(in); Matrix mout(out); Matrix mmask(mask); Matrix moutmask(outmask); for(uInt i=0; i vout(mout.row(i)); Vector voutMask(moutmask.row(i)); Vector vin(min.row(i)); Vector vinMask(mmask.row(i)); Smooth::hanning(vout, voutMask, vin, vinMask, TrueIsGood, relaxed); } } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/SparseDiff.h000066400000000000000000000362011476623553700214140ustar00rootroot00000000000000//# SparseDiff.h: An automatic differentiating class for functions //# Copyright (C) 2007,2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_SPARSEDIFF_H #define SCIMATH_SPARSEDIFF_H //# Includes #include #include #include #include #include // Using using std::pair; namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations template class SparseDiff; // // Class that computes partial derivatives by automatic differentiation. // // // // // // // // //
      • AutoDiff class // // // // Class that computes partial derivatives for some parameters by automatic // differentiation, thus SparseDiff. // // // // Class that computes partial derivatives by automatic differentiation. // It does this by storing the value of a function and the values of its first // derivatives with respect to some of its independent parameters. // When a mathematical // operation is applied to a SparseDiff object, the derivative values of the // resulting new object are computed according to the chain rules // of differentiation. SparseDiff operates like the // AutoDiff class, but only determines the // derivatives with respect to the actual dependent variables. // // Suppose we have a function f(x0,x1,...,xn) and its differential is // // df = (df/dx0)*dx0 + (df/dx1)*dx1 + ... + (df/dxn)*dxn // // We can build a class that has the value of the function, // f(x0,x1,...,xn), and the values of the derivatives, (df/dx0), (df/dx1), // ..., (df/dxn) at (x0,x1,...,xn), as class members. // // Now if we have another function, g(x0,x1,...,xn) and its differential is // dg = (dg/dx0)*dx0 + (dg/dx1)*dx1 + ... + (dg/dxn)*dxn, // since // // d(f+g) = df + dg, // d(f*g) = g*df + f*dg, // d(f/g) = df/g - fdg/g^2, // dsin(f) = cos(f)df, // ..., // // we can calculate // // d(f+g), d(f*g), ..., // based on our information on // // df/dx0, df/dx1, ..., dg/dx0, dg/dx1, ..., dg/dxn. // // All we need to do is to define the operators and derivatives of common // mathematical functions. // // To be able to use the class as an automatic differentiator of a function // object, we need a templated function object, i.e. an object with: //
          //
        • a template T operator()(const T) //
        • or multiple variable input like: // template T operator()(const Vector &) //
        • all dependent variables used in the calculation of the function // value should have been typed with T. //
        // A simple example of such a function object could be: // // template f { // public: // T operator()(const T &x, const T &a, const T &b) { // return a*b*x; } // }; // // Instantiate the following versions: // template class f; // template class f >; // // A call with values will produce the function value: // // cout << f(7.0, 2.0, 3.0) << endl; // // will produce the value at x=7 for a=2; b=3: // 42 // // But a call indicating that we want derivatives to a and b: // cout << f(SparseDiff(7.0), SparseDiff(2.0, 0), // SparseDiff(3.0, 1)) << endl; // // will produce the value at x=7 for a=2; b=3: // // and the partial derivatives wrt a and b at x=7: // (42, [21, 14]) // // The following will calculate the derivate wrt x: // cout << f(SparseDiff(7.0, 0), SparseDiff(2.0), // SparseDiff(3.0)) << endl; // (42,[6]) // // Note that in practice constants may be given as Double constants. // In actual practice, there are a few rules to obey for the structure of // the function object if you want to use the function object and its // derivatives in least squares fitting procedures in the Fitting // module. The major one is to view the function object having 'fixed' and // 'variable' parameters. I.e., rather than viewing the function as // depending on parameters a, b, x (f(a,b,x)), the // function is considered to be f(x; a,b), where a, b // are 'fixed' parameters, and x a variable parameter. // Fixed parameters should be contained in a // FunctionParam container object; // while the variable parameter(s) are given in the function // operator(). See Function class // for details. // // A Gaussian spectral profile would in general have the center frequency, // the width and the amplitude as fixed parameters, and the frequency as // a variable. Given a spectrum, you would solve for the fixed parameters, // given spectrum values. However, in other cases the role of the // parameters could be reversed. An example could be a whole stack of // observed (in the laboratory) spectra at different temperatures at // one frequency. In that case the width would be the variable parameter, // and the frequency one of the fixed (and to be solved for)parameters. // // Since the calculation of the derivatives is done with simple overloading, // the calculation of second (and higher) derivatives is easy. It should be // noted that higher deivatives are inefficient in the current incarnation // (there is no knowledge e.g. about symmetry in the Jacobian). However, // it is a very good way to get the correct answers of the derivatives. In // practice actual production code will be better off with specialization // of the f > implementation. // // The SparseDiff class is the class the user communicates with. // Alias classes (SparseDiffA and // SparseDiffX) exist // to make it possible to have different incarnations of a templated // method (e.g. a generic one and a specialized one). See the // dSparseDiff demo for an example of its use. // // All operators and functions are declared in // SparseDiffMath. The output operator in // SparseDiffIO. // The actual structure of the // data block used by SparseDiff is described in // SparseDiffRep. // // A SparseDiff can be constructed from an AutoDiff. // toAutoDiff(n) can convert it to an AutoDiff. //
        // // // // // First a simple example. // // We have a function of the form f(x,y,z); and want to know the // // value of the function for x=10; y=20; z=30; and for // // the derivatives at those points. // // Specify the values; and indicate the parameter dependence: // SparseDiff x(10.0, 0); // SparseDiff y(20.0, 1); // SparseDiff z(30.0, 2); // // The result will be: // SparseDiff result = x*y + sin(z); // cout << result.value() << endl; // // 199.012 // cout << result.derivatives() << endl; // // [20, 10, 0.154251] // // Note: sin(30) = -0.988; cos(30) = 0.154251; // // // See for an extensive example the demo program dSparseDiff. It is // based on the example given above, and shows also the use of second // derivatives (which is just using SparseDiff > // as template argument). // // // The function, with fixed parameters a,b: // template class f { // public: // T operator()(const T& x) { return a_p*a_p*a_p*b_p*b_p*x; } // void set(const T& a, const T& b) { a_p = a; b_p = b; } // private: // T a_p; // T b_p; // }; // // Call it with different template arguments: // Double a0(2), b0(3), x0(7); // f f0; f0.set(a0, b0); // cout << "Value: " << f0(x0) << endl; // // SparseDiff a1(2,0), b1(3,1), x1(7); // f > f1; f1.set(a1, b1); // cout << "Diff a,b: " << f1(x1) << endl; // // SparseDiff a2(2), b2(3), x2(7,0); // f > f2; f2.set(a2, b2); // cout << "Diff x: " << f2(x2) << endl; // // SparseDiff > a3(SparseDiff(2,0),0), // b3(SparseDiff(3,1),1), x3(SparseDiff(7)); // f > > f3; f3.set(a3, b3); // cout << "Diff2 a,b: " << f3(x3) << endl; // // SparseDiff > a4(SparseDiff(2)), // b4(SparseDiff(3)), // x4(SparseDiff(7,0),0); // f > > f4; f4.set(a4, b4); // cout << "Diff2 x: " << f4(x4) << endl; // // // Result will be: // // Value: 504 // // Diff a,b: (504, [756, 336]) // // Diff x: (504, [72]) // // Diff2 a,b: ((504, [756, 336]), [(756, [756, 504]), (336, [504, 112])]) // // Diff2 x: ((504, [72]), [(72, [0])]) // // // It needed the template instantiations definitions: // template class f; // template class f >; // template class f > >; // // // // // The creation of the class was motivated by least-squares non-linear fits // in cases where each individual condition equation depends only on a // fraction of the fixed parameters (e.g. self-calibration where only pairs // of antennas are present per equation), and hence only a few // partial derivatives of a fitted function are needed. It would be tedious // to create functionals for all partial derivatives of a function. // // // //
      • any class that has the standard mathematical and comparison // operators and functions defined. // // // //
      • Nothing I know of. // template class SparseDiff { public: //# Typedefs typedef T value_type; typedef value_type& reference; typedef const value_type& const_reference; typedef value_type* iterator; typedef const value_type* const_iterator; //# Constructors // Construct a constant with a value of zero. Zero derivatives. SparseDiff(); // Construct a constant with a value of v. Zero derivatives. SparseDiff(const T &v); // A function f(x0,x1,...,xn,...) with a value of v. The // nth derivative is one, and all other derivatives are zero. SparseDiff(const T &v, const uInt n); // A function f(x0,x1,...,xn,...) with a value of v. The // nth derivative is der, and all other derivatives are zero. SparseDiff(const T &v, const uInt n, const T &der); // Construct from an AutoDiff SparseDiff(const AutoDiff &other); // Construct one from another (deep copy) SparseDiff(const SparseDiff &other); // Destructor ~SparseDiff(); // Assignment operator. Assign a constant to variable. SparseDiff &operator=(const T &v); // Assignment operator. Add a gradient to variable. SparseDiff &operator=(const pair &der); // Assignment operator. Assign gradients to variable. SparseDiff &operator=(const vector > &der); // Assign from an Autodiff SparseDiff &operator=(const AutoDiff &other); // Assign one to another (deep copy) SparseDiff &operator=(const SparseDiff &other); // Assignment operators // void operator*=(const SparseDiff &other); void operator/=(const SparseDiff &other); void operator+=(const SparseDiff &other); void operator-=(const SparseDiff &other); void operator*=(const T other) { rep_p->operator*=(other); value() *= other; } void operator/=(const T other) { rep_p->operator/=(other); value() /= other; } void operator+=(const T other) { value() += other; } void operator-=(const T other) { value() -= other; } // // Convert to an AutoDiff of length n AutoDiff toAutoDiff(uInt n) const; // Returns the pointer to the structure of value and derivatives. // SparseDiffRep *theRep() { return rep_p; } const SparseDiffRep *theRep() const { return rep_p; } // // Returns the value of the function // T &value() { return rep_p->val_p; } const T &value() const { return rep_p->val_p; } // // Returns a vector of the derivatives of a SparseDiff // vector > &derivatives() const; void derivatives(vector > &res) const; const vector > &grad() const { return rep_p->grad_p; } vector > &grad() { return rep_p->grad_p; } // // Returns a specific derivative. No check for a valid which. // pair &derivative(uInt which) { return rep_p->grad_p[which]; } const pair &derivative(uInt which) const { return rep_p->grad_p[which]; } // // Return total number of derivatives uInt nDerivatives() const { return rep_p->grad_p.size(); } // Is it a constant, i.e., with zero derivatives? Bool isConstant() const { return rep_p->grad_p.empty(); } // Sort criterium static Bool ltSort(pair &lhs, pair &rhs); // Sort derivative list; cater for doubles and zeroes void sort(); private: //# Data // Value representation SparseDiffRep *rep_p; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Mathematics/SparseDiff.tcc000066400000000000000000000237221476623553700217420ustar00rootroot00000000000000//# SparseDiff.cc: An automatic differentiating class for functions //# Copyright (C) 2007,2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_SPARSEDIFF_TCC #define SCIMATH_SPARSEDIFF_TCC //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors template SparseDiff::SparseDiff() : rep_p(ObjectStack >::stack().get()) {} template SparseDiff::SparseDiff(const T &v) : rep_p(ObjectStack >::stack().get()) { rep_p->val_p = v; } template SparseDiff::SparseDiff(const T &v, const uInt n) : rep_p(ObjectStack >::stack().get()) { rep_p->val_p = v; rep_p->grad_p.push_back(std::make_pair(n, T(1))); } template SparseDiff::SparseDiff(const T &v, const uInt n, const T &der) : rep_p(ObjectStack >::stack().get()) { rep_p->val_p = v; rep_p->grad_p.push_back(std::make_pair(n, der)); } template SparseDiff::SparseDiff(const AutoDiff &other) : rep_p(ObjectStack >::stack().get()) { rep_p->val_p = other.value(); for (uInt i=0; igrad_p.push_back(std::make_pair(i, other.derivative(i))); } template SparseDiff::SparseDiff(const SparseDiff &other) : rep_p(ObjectStack >::stack().get()) { rep_p->val_p = other.rep_p->val_p; rep_p->grad_p = other.rep_p->grad_p; } template SparseDiff::~SparseDiff() { ObjectStack >::stack().put(rep_p); rep_p=0; } template SparseDiff &SparseDiff::operator=(const T &v) { rep_p->val_p = v; return *this; } template SparseDiff &SparseDiff::operator=(const pair &der) { rep_p->grad_p.push_back(der); sort(); return *this; } template SparseDiff &SparseDiff::operator=(const vector > &der) { rep_p->grad_p = der; sort(); return *this; } template SparseDiff &SparseDiff::operator=(const AutoDiff &other) { rep_p->val_p = other.value(); rep_p->grad_p.clear(); for (uInt i=0; igrad_p.push_back(std::make_pair(i, other.derivative(i))); return *this; } template SparseDiff &SparseDiff::operator=(const SparseDiff &other) { if (this != &other) { rep_p->val_p = other.rep_p->val_p; rep_p->grad_p = other.rep_p->grad_p; } return *this; } template void SparseDiff::operator*=(const SparseDiff &other) { T v; if (grad().empty()) { for (typename vector >::const_iterator i=other.grad().begin(); i!=other.grad().end(); ++i) if (value() != T(0)) grad().push_back(std::make_pair(i->first, i->second * value())); } else if (other.grad().empty()) { for (typename vector >::iterator i=grad().begin(); i!=grad().end(); ++i) if (value() != T(0)) i->second *= other.value(); } else { SparseDiffRep *tmp = ObjectStack >::stack().get(); typename vector >::const_iterator j=other.grad().begin(); for (typename vector >::iterator i=grad().begin(); i!=grad().end(); ++i) { if (j==other.grad().end()) { if (other.value() != T(0)) tmp->grad_p.push_back(std::make_pair(i->first, i->second * other.value())); } else if (j->first == i->first) { if ((v = i->second*other.value() + j->second*value()) != T(0)) tmp->grad_p.push_back(std::make_pair(i->first, v)); ++j; } else if (j->first > i->first) { if (other.value() != T(0)) tmp->grad_p.push_back(std::make_pair(i->first, i->second * other.value())); } else { if (value() != T(0)) tmp->grad_p.push_back(std::make_pair(j->first, j->second*value())); ++j; --i; } } if (value() != T(0)) for ( ; j!=other.grad().end(); ++j) tmp->grad_p.push_back(std::make_pair(j->first, j->second*value())); tmp->val_p = value(); ObjectStack >::stack().put(rep_p); rep_p = tmp; } value() *= other.value(); } template void SparseDiff::operator/=(const SparseDiff &other) { T t = other.value()*other.value(); T v; if (grad().empty()) { for (typename vector >::const_iterator i=other.grad().begin(); i!=other.grad().end(); ++i) if (value() != T(0)) grad().push_back(std::make_pair(i->first, -i->second * value()/t)); } else if (other.grad().empty()) { for (typename vector >::iterator i=grad().begin(); i!=grad().end(); ++i) i->second /= other.value(); } else { SparseDiffRep *tmp = ObjectStack >::stack().get(); typename vector >::const_iterator j=other.grad().begin(); for (typename vector >::iterator i=grad().begin(); i!=grad().end(); ++i) { if (j==other.grad().end()) { tmp->grad_p.push_back(std::make_pair(i->first, i->second / other.value())); } else if (j->first == i->first) { if ((v = i->second*other.value() - j->second*value()) != T(0)) tmp->grad_p.push_back(std::make_pair(i->first, v/t)); ++j; } else if (j->first > i->first) { tmp->grad_p.push_back(std::make_pair(i->first, i->second / other.value())); } else { if (value() != T(0)) tmp->grad_p.push_back(std::make_pair(j->first, -j->second*value()/t)); ++j; --i; } } if (value() != T(0)) for ( ; j!=other.grad().end(); ++j) tmp->grad_p.push_back(std::make_pair(j->first, -j->second * value()/t)); tmp->val_p = value(); ObjectStack >::stack().put(rep_p); rep_p = tmp; } value() /= other.value(); } template void SparseDiff::operator+=(const SparseDiff &other) { T v; if (grad().empty()) { for (typename vector >::const_iterator i=other.grad().begin(); i!=other.grad().end(); ++i) grad().push_back(*i); } else if (other.grad().empty()) { } else { SparseDiffRep *tmp = ObjectStack >::stack().get(); typename vector >::const_iterator j=other.grad().begin(); for (typename vector >::iterator i=grad().begin(); i!=grad().end(); ++i) { if (j==other.grad().end()) { tmp->grad_p.push_back(*i); } else if (j->first == i->first) { if ((v = i->second + j->second) != T(0)) tmp->grad_p.push_back(std::make_pair(i->first, v)); ++j; } else if (j->first > i->first) { tmp->grad_p.push_back(*i); } else { tmp->grad_p.push_back(*j); ++j; --i; } } for ( ; j!=other.grad().end(); ++j) tmp->grad_p.push_back(*j); tmp->val_p = value(); ObjectStack >::stack().put(rep_p); rep_p = tmp; } value() += other.value(); } template void SparseDiff::operator-=(const SparseDiff &other) { SparseDiff tmp = other; for (typename vector >::iterator i=tmp.grad().begin(); i!=tmp.grad().end(); ++i) i->second = -i->second; tmp.value() = -tmp.value(); SparseDiff::operator+=(tmp); } template AutoDiff SparseDiff::toAutoDiff(uInt n) const { AutoDiff tmp(n); for (typename vector >::const_iterator i=grad().begin(); i!=grad().end(); ++i) { if (i->first < n) tmp.derivative(i->first) = i->second; } return tmp; } template vector > &SparseDiff::derivatives() const { return rep_p->grad_p; } template void SparseDiff::derivatives(vector > &res) const { res = rep_p->grad_p; } template Bool SparseDiff::ltSort(pair &lhs, pair &rhs) { return (lhs.first < rhs.first); } template void SparseDiff::sort() { std::make_heap(grad().begin(), grad().end(), SparseDiff::ltSort); std::sort_heap(grad().begin(), grad().end(), SparseDiff::ltSort); // Remove empty ones; and add identical ones SparseDiffRep *tmp = ObjectStack >::stack().get(); for (typename vector >::iterator i=grad().begin(); i!=grad().end(); ++i) { if (i != grad().begin()) { if (i->first == (i-1)->first) { i->second += (i-1)->second; i->second = T(0); } } } for (typename vector >::iterator i=grad().begin(); i!=grad().end(); ++i) { if (i->second != T(0)) tmp->grad_p.push_back(*i); } tmp->val_p = value(); ObjectStack >::stack().put(rep_p); rep_p = tmp; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/SparseDiffA.h000066400000000000000000000131031476623553700215110ustar00rootroot00000000000000//# SparseDiff!A.h: An automatic differentiating class for functions //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_SPARSEDIFFA_H #define SCIMATH_SPARSEDIFFA_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Class that computes partial derivatives by automatic differentiation. // // // // // // // // //
      • SparseDiff // // // // Class that computes partial derivatives by automatic differentiation, thus // SparseDiff. // // // // SparseDiffA is an SparseDiff. It is used // to be able to distinguish between two template incarnations; e.g. to // have one or more specializations, in addition to the general template // version. // // // // See for an extensive example the demo program dSparseDiff. It is // based on the example given in the SparseDiff // class, and shows how to have both an automatic and a specific version // of a function object. // // // The function, with fixed parameters a,b: // template class f { // public: // T operator()(const T& x) { return a_p*a_p*a_p*b_p*b_p*x; } // void set(const T& a, const T& b) { a_p = a; b_p = b; } // private: // T a_p; // T b_p; // }; // // The specialized function // template <> class f > { // public: // T operator()(const T& x) { return a_p*a_p*a_p*b_p*b_p*x; } // void set(const T& a, const T& b) { a_p = a; b_p = b; } // private: // T a_p; // T b_p; // }; // // Call it with different template arguments: // SparseDiff a1(2,0), b1(3,1), x1(7); // f > f1; f1.set(a1, b1); // cout << "Diff a,b: " << f1(x1) << endl; // // f > f12; f12.set(a1, b1); // cout << "Same....: " << f12(x1) << endl; // // // Result will be: // // Diff a,b: (504, [756, 336]) // // Same....: (504, [756, 336]) // // // It needed the template instantiations definitions: // template class f >; // // // // // The class was created to enable separate calculations of the same // function. // // // //
      • any class that has the standard mathematical and comparisons // defined // // // //
      • Nothing I know // template class SparseDiffA : public SparseDiff { public: //# Constructors // Construct a constant with a value of zero. Zero derivatives. SparseDiffA() : SparseDiff() {} // Construct a constant with a value of v. Zero derivatives. SparseDiffA(const T &v) : SparseDiff(v) {} // A function f(x0,x1,...,xn,...) with a value of v. // The nth derivative is one, and all others are zero. SparseDiffA(const T &v, const uInt n) : SparseDiff(v, n) {} // A function f(x0,x1,...,xn,...) with a value of v. The // nth derivative is der, and all other derivatives are zero. SparseDiffA(const T &v, const uInt n, const T &der) : SparseDiff(v, n, der) {} // Construct one from another SparseDiffA(const SparseDiff &other) : SparseDiff(other) {} ~SparseDiffA() {} // Assignment operator. Assign a constant to variable. All derivatives // are zero. SparseDiffA &operator=(const T &v) { SparseDiff::operator=(v); return *this; } // Assignment operator. Add a gradient to variable. SparseDiffA &operator=(const pair &der) { SparseDiff::operator=(der); return *this; } // Assignment operator. Assign gradients to variable. SparseDiffA &operator=(const vector > &der) { SparseDiff::operator=(der); return *this; } // Assign one to another (deep copy). SparseDiffA &operator=(const SparseDiff &other) { SparseDiff::operator=(other); return *this; } private: //# Data }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/SparseDiffIO.h000066400000000000000000000043461476623553700216510ustar00rootroot00000000000000//# SparseDiffIO.h: Output for SparseDiff objects //# Copyright (C) 2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_SPARSEDIFFIO_H #define SCIMATH_SPARSEDIFFIO_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations template class SparseDiff; // // Implements all IO operators and functions for SparseDiff. // // // // // // //
      • SparseDiff class // // // // Implements all IO operators and functions for SparseDiff. // // // //
      • Nothing I know of // // template ostream &operator << (ostream &os, const SparseDiff &ad); // } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Mathematics/SparseDiffIO.tcc000066400000000000000000000035041476623553700221660ustar00rootroot00000000000000//# SparseDiffIO.cc: text output for SparseDiff //# Copyright (C) 2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_SPARSEDIFFIO_TCC #define SCIMATH_SPARSEDIFFIO_TCC //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template ostream &operator<<(ostream &os, const SparseDiff &ad) { os << "(" << ad.value(); for (uInt i=0; i #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Implements all mathematical operators and functions for SparseDiff. // // // // // // //
      • SparseDiff class // // // // Implements all mathematical operators and functions for SparseDiff. // // // //
      • nothing I know of // // // Unary arithmetic operators. // template SparseDiff operator+(const SparseDiff &other); template SparseDiff operator-(const SparseDiff &other); // // Arithmetic on two SparseDiff objects, returning a SparseDiff object // template SparseDiff operator+(const SparseDiff &left, const SparseDiff &right); template SparseDiff operator-(const SparseDiff &left, const SparseDiff &right); template SparseDiff operator*(const SparseDiff &left, const SparseDiff &right); template SparseDiff operator/(const SparseDiff &left, const SparseDiff &right); // // Arithmetic on a SparseDiff and a scalar, returning a SparseDiff // template SparseDiff operator+(const SparseDiff &left, const T &right); template SparseDiff operator-(const SparseDiff &left, const T &right); template SparseDiff operator*(const SparseDiff &left, const T &right); template SparseDiff operator/(const SparseDiff &left, const T &right); // // Arithmetic between a scalar and a SparseDiff returning a SparseDiff // template SparseDiff operator+(const T &left, const SparseDiff &right); template SparseDiff operator-(const T &left, const SparseDiff &right); template SparseDiff operator*(const T &left, const SparseDiff &right); template SparseDiff operator/(const T &left, const SparseDiff &right); // // Transcendental functions // template SparseDiff acos(const SparseDiff &ad); template SparseDiff asin(const SparseDiff &ad); template SparseDiff atan(const SparseDiff &ad); template SparseDiff atan2(const SparseDiff &y, const SparseDiff &x); template SparseDiff cos(const SparseDiff &ad); template SparseDiff cosh(const SparseDiff &ad); template SparseDiff exp(const SparseDiff &ad); template SparseDiff log(const SparseDiff &ad); template SparseDiff log10(const SparseDiff &ad); template SparseDiff erf(const SparseDiff &ad); template SparseDiff erfc(const SparseDiff &ad); template SparseDiff pow(const SparseDiff &a, const SparseDiff &b); template SparseDiff pow(const SparseDiff &a, const T &b); template SparseDiff square(const SparseDiff &ad); template SparseDiff cube(const SparseDiff &ad); template SparseDiff sin(const SparseDiff &ad); template SparseDiff sinh(const SparseDiff &ad); template SparseDiff sqrt(const SparseDiff &ad); template SparseDiff tan(const SparseDiff &ad); template SparseDiff tanh(const SparseDiff &ad); template SparseDiff abs(const SparseDiff &ad); // // Floating-point remainder of x/c, with the same sign as x, where c is // a constant. // template SparseDiff fmod(const SparseDiff &x, const T &c); template SparseDiff fmod(const SparseDiff &x, const SparseDiff &c); // // Floor and ceil of values // template SparseDiff floor(const SparseDiff &ad); template SparseDiff ceil(const SparseDiff &ad); // // Comparison operators. Only the values are compared: in the actual // functions, comparisons are used to decide on algorithms. To check // if two SparseDiff values are equal, use comparison for both // value and derivatives. // To check if two SparseDiff values are equal, use the // member method equals() (e.g. for debugging and testing). // // // Compare two SparseDiff's template Bool operator>(const SparseDiff &left, const SparseDiff &right); template Bool operator<(const SparseDiff &left, const SparseDiff &right); template Bool operator>=(const SparseDiff &left, const SparseDiff &right); template Bool operator<=(const SparseDiff &left, const SparseDiff &right); template Bool operator==(const SparseDiff &left, const SparseDiff &right); template Bool operator!=(const SparseDiff &left, const SparseDiff &right); template Bool near(const SparseDiff &left, const SparseDiff &right); template Bool near(const SparseDiff &left, const SparseDiff &right, const Double tol); template Bool allnear(const SparseDiff &left, const SparseDiff &right, const Double tol); template Bool nearAbs(const SparseDiff &left, const SparseDiff &right, const Double tol); template Bool allnearAbs(const SparseDiff &left, const SparseDiff &right, const Double tol); // // Compare a SparseDiff and a constant // template Bool operator>(const SparseDiff &left, const T &right); template Bool operator<(const SparseDiff &left, const T &right); template Bool operator>=(const SparseDiff &left, const T &right); template Bool operator<=(const SparseDiff &left, const T &right); template Bool operator==(const SparseDiff &left, const T &right); template Bool operator!=(const SparseDiff &left, const T &right); template Bool near(const SparseDiff &left, const T &right); template Bool near(const SparseDiff &left, const T &right, const Double tol); template Bool allnear(const SparseDiff &left, const T &right, const Double tol); template Bool nearAbs(const SparseDiff &left, const T &right, const Double tol); template Bool allnearAbs(const SparseDiff &left, const T &right, const Double tol); // // Compare a constant and a SparseDiff // template Bool operator>(const T &left, const SparseDiff &right); template Bool operator<(const T &left, const SparseDiff &right); template Bool operator>=(const T &left, const SparseDiff &right); template Bool operator<=(const T &left, const SparseDiff &right); template Bool operator==(const T &left, const SparseDiff &right); template Bool operator!=(const T &left, const SparseDiff &right); template Bool near(const T &left, const SparseDiff &right, const Double tol); template Bool allnear(const T &left, const SparseDiff &right, const Double tol); template Bool nearAbs(const T &left, const SparseDiff &right, const Double tol); template Bool allnearAbs(const T &left, const SparseDiff &right, const Double tol); // // Test special values // template Bool isNaN(const SparseDiff &val); template Bool isInf(SparseDiff &val); // // Minimum/maximum // template SparseDiff min(const SparseDiff &left, const SparseDiff &right); template SparseDiff max(const SparseDiff &left, const SparseDiff &right); // // } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Mathematics/SparseDiffMath.tcc000066400000000000000000000377671476623553700225720ustar00rootroot00000000000000//# SparseDiffMath.cc: Implements all mathematical functions for SparseDiff. //# Copyright (C) 2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_SPARSEDIFFMATH_TCC #define SCIMATH_SPARSEDIFFMATH_TCC //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Unary arithmetic operators. template SparseDiff operator+(const SparseDiff &other) { SparseDiff tmp(other); return tmp; } template SparseDiff operator-(const SparseDiff &other) { SparseDiff tmp(other); tmp *= T(-1); return tmp; } // Binary arithmetic operators template SparseDiff operator+(const SparseDiff &left, const SparseDiff &right) { SparseDiff tmp(left); tmp += right; return tmp; } template SparseDiff operator-(const SparseDiff &left, const SparseDiff &right) { SparseDiff tmp(left); tmp -= right; return tmp; } template SparseDiff operator*(const SparseDiff &left, const SparseDiff &right) { SparseDiff tmp(left); tmp *= right; return tmp; } template SparseDiff operator/(const SparseDiff &left, const SparseDiff &right) { SparseDiff tmp(left); tmp /= right; return tmp; } template SparseDiff operator+(const SparseDiff &left, const T &right) { SparseDiff tmp(left); tmp += right; return tmp; } template SparseDiff operator-(const SparseDiff &left, const T &right) { SparseDiff tmp(left); tmp -= right; return tmp; } template SparseDiff operator* (const SparseDiff &left, const T &right) { SparseDiff tmp(left); tmp *= right; return tmp; } template SparseDiff operator/(const SparseDiff &left, const T &right) { SparseDiff tmp(left); tmp /= right; return tmp; } template SparseDiff operator+(const T &left, const SparseDiff &right) { SparseDiff tmp(right); tmp += left; return tmp; } template SparseDiff operator-(const T &left, const SparseDiff &right) { SparseDiff tmp(left); tmp -= right; return tmp; } template SparseDiff operator*(const T &left, const SparseDiff &right) { SparseDiff tmp(right); tmp *= left; return tmp; } template SparseDiff operator/(const T &left, const SparseDiff &right) { SparseDiff tmp(left); tmp /= right; return tmp; } template SparseDiff acos(const SparseDiff &ad) { SparseDiff tmp(ad); T tv = tmp.value(); *tmp.theRep() /= T(-sqrt(T(1) - tv*tv)); tmp.value() = acos(tv); return tmp; } template SparseDiff asin(const SparseDiff &ad) { SparseDiff tmp(ad); T tv = tmp.value(); *tmp.theRep() /= T(sqrt(T(1) - tv*tv)); tmp.value() = asin(tv); return tmp; } template SparseDiff atan(const SparseDiff &ad) { SparseDiff tmp(ad); T tv = tmp.value(); *tmp.theRep() /= T(1) + tv*tv; tmp.value() = atan(tv); return tmp; } template SparseDiff atan2(const SparseDiff &y, const SparseDiff &x) { // this gets the derivative right, via the chain rule using the already // defined / and atan functions, but the value may be wrong SparseDiff tmp = atan(y/x); // get the value right tmp.value() = atan2(y.value(), x.value()); return tmp; } template SparseDiff cos(const SparseDiff &ad) { SparseDiff tmp(ad); T tv = tmp.value(); *tmp.theRep() *= T(-sin(tv)); tmp.value() = cos(tv); return tmp; } template SparseDiff cosh(const SparseDiff &ad) { SparseDiff tmp(ad); T tv = tmp.value(); *tmp.theRep() *= T(sinh(tv)); tmp.value() = cosh(tv); return tmp; } template SparseDiff sin(const SparseDiff &ad) { SparseDiff tmp(ad); T tv = tmp.value(); *tmp.theRep() *= T(cos(tv)); tmp.value() = sin(tv); return tmp; } template SparseDiff sinh(const SparseDiff &ad) { SparseDiff tmp(ad); T tv = tmp.value(); *tmp.theRep() *= T(cosh(tv)); tmp.value() = sinh(tv); return tmp; } template SparseDiff exp(const SparseDiff &ad) { SparseDiff tmp(ad); tmp.value() = exp(ad.value()); *tmp.theRep() *= tmp.value(); return tmp; } template SparseDiff log(const SparseDiff &ad) { SparseDiff tmp(ad); T tv = tmp.value(); *tmp.theRep() /= tv; tmp.value() = log(tv); return tmp; } template SparseDiff log10(const SparseDiff &ad) { static const T l10 = T(log(10.0)); SparseDiff tmp(ad); T tv = tmp.value(); *tmp.theRep() /= tv*l10; tmp.value() = log10(tv); return tmp; } template SparseDiff erf(const SparseDiff &ad) { SparseDiff tmp(ad); T tv = tmp.value(); *tmp.theRep() *= T(T(M_2_SQRTPI)*exp(-tv*tv)); tmp.value() = erf(tv); return tmp; } template SparseDiff erfc(const SparseDiff &ad) { SparseDiff tmp(ad); T tv = tmp.value(); *tmp.theRep() *= T(T(-M_2_SQRTPI)*exp(-tv*tv)); tmp.value() = erfc(tv); return tmp; } template SparseDiff pow(const SparseDiff &a, const SparseDiff &b) { if (b.isConstant()) return pow(a, b.value()); T ta = a.value(); T tb = b.value(); T val = pow(ta, tb); T temp2 = tb * pow(ta, tb - T(1)); SparseDiff tmp(b); *tmp.theRep() *= val * T(log(ta)); SparseDiff tmpa(a); tmpa *= temp2; tmp += tmpa; tmp.value() = val; return tmp; } template SparseDiff pow(const SparseDiff &a, const T &b) { SparseDiff tmp(a); T ta = a.value(); *tmp.theRep() *= b*pow(ta, b-T(1)); tmp.value() = pow(ta, b); return tmp; } template SparseDiff square(const SparseDiff &ad) { SparseDiff tmp(ad); tmp.value() = square(tmp.value()); *tmp.theRep() *= T(2)*tmp.value(); return tmp; } template SparseDiff cube(const SparseDiff &ad) { SparseDiff tmp(ad); tmp.value() = cube(tmp.value()); *tmp.theRep() *= T(3)*square(tmp.value()); return tmp; } template SparseDiff sqrt(const SparseDiff &ad) { SparseDiff tmp(ad); tmp.value() = sqrt(tmp.value()); *tmp.theRep() /= T(2)*tmp.value(); return tmp; } template SparseDiff tan(const SparseDiff &ad) { SparseDiff tmp(ad); T tv = tmp.value(); T temp = cos(tv); temp *= temp; *tmp.theRep() /= temp; tmp.value() = tan(tv); return tmp; } template SparseDiff tanh(const SparseDiff &ad) { SparseDiff tmp(ad); T tv = tmp.value(); T temp = cosh(tv); temp *= temp; *tmp.theRep() /= temp; tmp.value() = tanh(tv); return tmp; } template SparseDiff abs(const SparseDiff &ad) { // Here we assume that function F represented by ad is continous and // differentiable in a small enough neighborhood where F is // evaluated. So if ad.value() is positive, F is positive in // the small neighborhood. SparseDiff tmp(ad); if (ad.value() < T(0)) tmp *= T(-1); return tmp; } template SparseDiff fmod(const SparseDiff &x, const T &c) { // Floating-point remainder of x/c, with the same sign as x, where c is // a constant. Since fmod(x,c) = x - ((int)(x/c))*c and d[(int)(z)]/dz = 0, // d(fmod(x,c))/dx = 1. At z = integer, (int)(z) is discontinuous, but // away from the point (int)(z) is well defined and has derivative (=0) // we use the derivative at z = integer+epsilon as the derivative at // z = integer. SparseDiff tmp(x); tmp.value() = fmod(x.value(), c); return tmp; } template SparseDiff fmod(const SparseDiff &x, const SparseDiff &c) { SparseDiff tmp(x); tmp.value() = fmod(x.value(), c.value()); return tmp; } template SparseDiff floor(const SparseDiff &ad) { SparseDiff tmp(floor(ad.value())); return tmp; } template SparseDiff ceil(const SparseDiff &ad) { SparseDiff tmp(ceil(ad.value())); return tmp; } template Bool operator>(const SparseDiff &left, const SparseDiff &right) { return (left.value() > right.value()); } template Bool operator<(const SparseDiff &left, const SparseDiff &right) { return (left.value() < right.value()); } template Bool operator>=(const SparseDiff &left, const SparseDiff &right) { return (left.value() >= right.value()); } template Bool operator<=(const SparseDiff &left, const SparseDiff &right) { return (left.value() <= right.value()); } template Bool operator==(const SparseDiff &left, const SparseDiff &right) { return (left.value() == right.value()); } template Bool operator!=(const SparseDiff &left, const SparseDiff &right) { return (left.value() != right.value()); } // Compare an SparseDiff and a constant template Bool operator>(const SparseDiff &left,const T &right) { return (left.value() > right); } template Bool operator<(const SparseDiff &left,const T &right) { return (left.value() < right); } template Bool operator>=(const SparseDiff &left,const T &right) { return (left.value() >= right); } template Bool operator<=(const SparseDiff &left,const T &right) { return (left.value() <= right); } template Bool operator==(const SparseDiff &left,const T &right) { return (left.value() == right); } template Bool operator!=(const SparseDiff &left,const T &right) { return (left.value() != right); } // Compare a constant and an SparseDiff template Bool operator>(const T &left, const SparseDiff &right) { return (left > right.value()); } template Bool operator<(const T &left, const SparseDiff &right) { return (left < right.value()); } template Bool operator>=(const T &left, const SparseDiff &right) { return (left >= right.value()); } template Bool operator<=(const T &left, const SparseDiff &right) { return (left <= right.value()); } template Bool operator==(const T &left, const SparseDiff &right) { return (left == right.value()); } template Bool operator!=(const T &left, const SparseDiff &right) { return (left != right.value()); } // Near comparisons template Bool near(const SparseDiff &left, const SparseDiff &right) { return (near(left.value(), right.value())); } template Bool near(const T &left, const SparseDiff &right) { return near(left, right.value()); } template Bool near(const SparseDiff &left, const T &right) { return near(left.value(), right); } template Bool near(const SparseDiff &left, const SparseDiff &right, const Double tol) { return near(left.value(), right.value(), tol); } template Bool near(const T &left, const SparseDiff &right, const Double tol) { return near(left, right.value(), tol); } template Bool near(const SparseDiff &left, const T &right, const Double tol) { return near(left.value(), right, tol); } template Bool allnear(const SparseDiff &left, const SparseDiff &right) { return (near(left.value(), right.value())); } template Bool allnear(const T &left, const SparseDiff &right) { return near(left, right.value()); } template Bool allnear(const SparseDiff &left, const T &right) { return near(left.value(), right); } template Bool allnear(const SparseDiff &left, const SparseDiff &right, const Double tol) { return near(left.value(), right.value(), tol); } template Bool allnear(const T &left, const SparseDiff &right, const Double tol) { return near(left, right.value(), tol); } template Bool allnear(const SparseDiff &left, const T &right, const Double tol) { return near(left.value(), right, tol); } template Bool nearAbs(const SparseDiff &left, const SparseDiff &right) { return (nearAbs(left.value(), right.value())); } template Bool nearAbs(const T &left, const SparseDiff &right) { return nearAbs(left, right.value()); } template Bool nearAbs(const SparseDiff &left, const T &right) { return nearAbs(left.value(), right); } template Bool nearAbs(const SparseDiff &left, const SparseDiff &right, const Double tol) { return nearAbs(left.value(), right.value(), tol); } template Bool nearAbs(const T &left, const SparseDiff &right, const Double tol) { return nearAbs(left, right.value(), tol); } template Bool nearAbs(const SparseDiff &left, const T &right, const Double tol) { return nearAbs(left.value(), right, tol); } template Bool allnearAbs(const SparseDiff &left, const SparseDiff &right) { return (nearAbs(left.value(), right.value())); } template Bool allnearAbs(const T &left, const SparseDiff &right) { return nearAbs(left, right.value()); } template Bool allnearAbs(const SparseDiff &left, const T &right) { return nearAbs(left.value(), right); } template Bool allnearAbs(const SparseDiff &left, const SparseDiff &right, const Double tol) { return nearAbs(left.value(), right.value(), tol); } template Bool allnearAbs(const T &left, const SparseDiff &right, const Double tol) { return nearAbs(left, right.value(), tol); } template Bool allnearAbs(const SparseDiff &left, const T &right, const Double tol) { return nearAbs(left.value(), right, tol); } // Test special values template Bool isNaN (const SparseDiff &val) { return isNaN(val.value()); } template Bool isInf(SparseDiff &val) { return isInf(val.value()); } template SparseDiff min(const SparseDiff &left, const SparseDiff &right) { SparseDiff tmp = (left.value() <= right.value()) ? left : right; return tmp; } template SparseDiff max(const SparseDiff &left, const SparseDiff &right) { SparseDiff tmp = (left.value() <= right.value()) ? right : left; return tmp; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/SparseDiffRep.h000066400000000000000000000100351476623553700220600ustar00rootroot00000000000000//# SparseDiffRep.h: Representation of an automatic differential class data //# Copyright (C) 2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_SPARSEDIFFREP_H #define SCIMATH_SPARSEDIFFREP_H //# Includes #include #include #include // Using using std::pair; namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // // Representation of data for the spare automatic differentiation calss. // // // // // // // // //
      • SparseDiff // // // // Class that represents partial derivatives obtained by automatic // differentiation. // // // // Class representing // the data necessary for automatic differentiation. The structure contains a // value, and the derivatives of the value with respect to a number of // dependent variables. // // The actual differentiation and access is done through the // SparseDiff class. // // // See the example in SparseDiff // // // // To separate the data container from the actual calculations. // To be able to create special conatiners; constructors and destructors // (including memory allocation) to speed up processes. // // //
      • any class that has the standard mathematical and comparison // operators defined // // // //
      • Nothing I know of // template class SparseDiffRep { public: //# Typedefs typedef T value_type; typedef value_type& reference; typedef const value_type& const_reference; typedef value_type* iterator; typedef const value_type* const_iterator; //# Constructors // Construct a constant with a value of zero. Zero derivatives. SparseDiffRep(); //# Operators // Assignment operators // SparseDiffRep &operator=(const T &v); SparseDiffRep &operator=(const vector > &grad); SparseDiffRep &operator=(const SparseDiffRep &other); void operator*=(const T other); void operator/=(const T other); void operator+=(const T other); void operator-=(const T other); // //# Member functions // Clear for reuse void clear() { grad_p.clear(); } //# Data // The function value T val_p; // The derivatives vector > grad_p; // Link to indicate its status (1=linked in stack; 2=used in modules) uInt link_p; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Mathematics/SparseDiffRep.tcc000066400000000000000000000046751476623553700224170ustar00rootroot00000000000000//# SparseDiffRep.cc: Representation of an automatic differential class data //# Copyright (C) 2007 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_SPARSEDIFFREP_TCC #define SCIMATH_SPARSEDIFFREP_TCC //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Constructors template SparseDiffRep::SparseDiffRep() : val_p(T(0.0)), grad_p(), link_p(1) {} //# Member functions template SparseDiffRep &SparseDiffRep::operator=(const T &v) { val_p = v; return *this; } template SparseDiffRep &SparseDiffRep:: operator=(const vector > &grad) { grad_p = grad; return *this; } template SparseDiffRep &SparseDiffRep::operator=(const SparseDiffRep &other) { if (this != &other) { val_p = other.val_p; grad_p = other.grad_p; } return *this; } template void SparseDiffRep::operator*=(const T other) { for (typename vector >::iterator i=grad_p.begin(); i!=grad_p.end(); ++i) i->second *= other; } template void SparseDiffRep::operator/=(const T other) { for (typename vector >::iterator i=grad_p.begin(); i!=grad_p.end(); ++i) i->second /= other; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/SparseDiffX.h000066400000000000000000000131301476623553700215400ustar00rootroot00000000000000//# SparseDiff!A.h: An automatic differentiating class for functions //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_SPARSEDIFFX_H #define SCIMATH_SPARSEDIFFX_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations template class Vector; // // Class that computes partial derivatives by automatic differentiation. // // // // // // // // //
      • SparseDiff // // // // Class that computes partial derivatives by automatic differentiation, thus // SparseDiff. // // // // SparseDiffX is an SparseDiff. It is used // to be able to distinguish between two template incarnations; e.g. to // have one or more specializations, in addition to the general template // version. // // // // See for an extensive example the demo program dSparseDiff. It is // based on the example given in the SparseDiff // class, and shows how to have both an automatic and a specific version // of a function object. // // // The function, with fixed parameters a,b: // template class f { // public: // T operator()(const T& x) { return a_p*a_p*a_p*b_p*b_p*x; } // void set(const T& a, const T& b) { a_p = a; b_p = b; } // private: // T a_p; // T b_p; // }; // // The specialized function // template <> class f > { // public: // T operator()(const T& x) { return a_p*a_p*a_p*b_p*b_p*x; } // void set(const T& a, const T& b) { a_p = a; b_p = b; } // private: // T a_p; // T b_p; // }; // // Call it with different template arguments: // SparseDiff a1(2,0), b1(3,1), x1(7); // f > f1; f1.set(a1, b1); // cout << "Diff a,b: " << f1(x1) << endl; // // f > f12; f12.set(a1, b1); // cout << "Same....: " << f12(x1) << endl; // // // Result will be: // // Diff a,b: (504, [756, 336]) // // Same....: (504, [756, 336]) // // // It needed the template instantiations definitions: // template class f >; // // // // // The class was created to enable separate calculations of the same // function. // // // //
      • any class that has the standard mathematical and comparisons // defined // // // //
      • Nothing I know // template class SparseDiffX : public SparseDiff { public: //# Constructors // Construct a constant with a value of zero. Zero derivatives. SparseDiffX() : SparseDiff() {} // Construct a constant with a value of v. Zero derivatives. SparseDiffX(const T &v) : SparseDiff(v) {} // A function f(x0,x1,...,xn,...) with a value of v. // The nth derivative is one, and all others are zero. SparseDiffX(const T &v, const uInt n) : SparseDiff(v, n) {} // A function f(x0,x1,...,xn,...) with a value of v. The // nth derivative is der, and all other derivatives are zero. SparseDiffX(const T &v, const uInt n, const T &der) : SparseDiff(v, n, der) {} // Construct one from another SparseDiffX(const SparseDiff &other) : SparseDiff(other) {} ~SparseDiffX() {} // Assignment operator. Assign a constant to variable. All derivatives // are zero. SparseDiffX &operator=(const T &v) { SparseDiff::operator=(v); return *this; } // Assignment operator. Add a gradient to variable. SparseDiffX &operator=(const pair &der) { SparseDiff::operator=(der); return *this; } // Assignment operator. Assign gradients to variable. SparseDiffX &operator=(const vector > &der) { SparseDiff::operator=(der); return *this; } // Assign one to another (deep copy). SparseDiffX &operator=(const SparseDiff &other) { SparseDiff::operator=(other); return *this; } private: //# Data }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/SquareMatrix.h000066400000000000000000000303221476623553700220110ustar00rootroot00000000000000//# SquareMatrix.h: Fast Square Matrix class with fixed (templated) size //# Copyright (C) 1996,1997,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_SQUAREMATRIX_H #define SCIMATH_SQUAREMATRIX_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# forward declarations template class RigidVector; // // Fast Square Matrix class with fixed (templated) size // // // // // //
      • Complex //
      • Matrix // // // // SquareMatrix is a specialized class for small (<5x5) square matrices. // // // // SquareMatrix provides operations similar to the Matrix class, but it is // much faster for small arrays. One important difference is that operators *= // and * do matrix products for SquareMatrices instead of element by element // multiplication. SquareMatrices also optimize operations internally for // scalar identity matrices (diagonal matrix with all elements equal) and // diagonal matrices. The different types of SquareMatrix are created by // constructors and operator= taking either a scalar, a vector or a full // matrix. // // // // // // create two SquareMatrices // SquareMatrix sm1(3.0); // a scalar identity matrix // Vector vec(2); vec(0)=2.0; vec(1)=3.0; // SquareMatrix sm2(vec); // a diagonal matrix // // multiply the matrices // // Note: A*=B is equivalent to A=A*B where '*' is matrix multiplication // sm1*=sm2; // sm1 now diagonal // // // // // The basic Matrix classes are rather inefficient for small sizes, // new and delete tend to dominate the execution time for computationally // intensive code. The SquareMatrix classes circumvent this by having a // compile-time fixed size c-array internally. The SquareMatrix class have // fixed zero origin and no increments, this allows fast indexing, // copying and math operations. As mentioned in the synopsis, the SquareMatrix // classes also avoid unnecessary operations for simple matrices // (scalar-identity and diagonal). // // // //
      • real() is called for T=Complex/DComplex // // // //
      • No exceptions // // // //
      • when the Sun native compiler improves, some explicit instantiations // can be replaced by their templated equivalent and two constructor // calls with for loops can be moved out of line. //
      • not all operators and math functions available for Matrix are // implemented yet, add on as-needed basis. // template class SquareMatrix { // Friends currently need to be explicit (non templated) type to work. friend class RigidVector; //# friend class SquareMatrix; // for real() // friend class SquareMatrix;// Sun native does not accept this // friend class SquareMatrix; // for directProduct of 2x2 // Global friend function for product of Complex matrix and Float 4-vector friend RigidVector operator*(const SquareMatrix& m, const RigidVector& v); // Global friend function to calculate direct product friend SquareMatrix& directProduct(SquareMatrix& result, const SquareMatrix& left, const SquareMatrix& right); public: // Enum used internally to optimize operations. enum {General, Diagonal, ScalarId}; // Destructor ~SquareMatrix() {} // Default constructor - creates a unity matrix at present, this may not // be what we want (non-intuitive?) SquareMatrix() : type_p(ScalarId) {a_p[0][0]=T(1);} // Create a matrix of a given type, no initialization SquareMatrix(int itype) : type_p(itype) {} // Copy construct a SquareMatrix, a true copy is made. SquareMatrix(const SquareMatrix& m) {operator=(m);} // Construct from c-style matrix (by copying elements). SquareMatrix(const T a[n][n]) {operator=(a);} // Construct from Matrix. SquareMatrix(const Matrix& mat) {operator=(mat);} // Construct from c-style vector, creates a diagonal matrix. SquareMatrix(const T vec[n]){operator=(vec);} // Construct from Vector, creates a diagonal matrix. SquareMatrix(const Vector& vec) {operator=(vec);} // Construct from scalar, creates a scalar-identity matrix SquareMatrix(const T& scalar) : type_p(ScalarId) { a_p[0][0]=scalar; } // Assignment, uses copy semantics. SquareMatrix& operator=(const SquareMatrix& m); // Assign a c-style matrix, creates a general matrix. SquareMatrix& operator=(const T a[n][n]) { type_p=General; const T* pa=&a[0][0]; T* pa_p=&a_p[0][0]; for (Int i=0; i& operator=(const Matrix& m); // Assign a c-style vector, creates a diagonal matrix SquareMatrix& operator=(const T vec[n]) { type_p=Diagonal; for (Int i=0; i& operator=(const Vector& v); // Assign a scalar, creates a scalar-identity matrix SquareMatrix& operator=(T val) { type_p=ScalarId; a_p[0][0]=val; return *this; } // Add two SquareMatrices, element by element. SquareMatrix& operator+=(const SquareMatrix& other); // Matrix product of 'this' SquareMatrix with other, // i.e., A*=B; is equivalent with A=A*B where '*' is matrix multiplication. SquareMatrix& operator*=(const SquareMatrix& other); // Scalar multiplication SquareMatrix& operator*=(Float f); // Indexing, only const indexing is allowed. You cannot change the // matrix via indexing. No bounds checking. T operator()(Int i, Int j) const { switch (type_p) { case ScalarId: return (i==j) ? a_p[0][0] : T(); break; case Diagonal: return (i==j) ? a_p[i][i] : T(); break; } return a_p[i][j]; } // Non const indexing, throws exception if you try to change an element // which would require a type change of the matrix T& operator()(Int i, Int j) { switch (type_p) { case ScalarId: return (i==j) ? a_p[0][0] : throwInvAccess(); break; case Diagonal: return (i==j) ? a_p[i][i] : throwInvAccess(); break; } return a_p[i][j]; } //# The following does not compile with Sun native, replaced by explicit //# global function. //# direct product : dp= this (directproduct) other //# SquareMatrix& //# directProduct(SquareMatrix& dp, //# const SquareMatrix& other) const; // For a SquareMatrix: // set the argument result to the real part of the matrix // (and return result by reference to allow use in // expressions without creating temporary). //# SquareMatrix& real(SquareMatrix& result) const; // For a SquareMatrix: // return the real part of the matrix. //# SquareMatrix real() const { //# SquareMatrix result; //# return real(result); //# } // Conjugate the matrix in place(!). SquareMatrix& conj(); // Tranpose and conjugate the matrix in place(!). SquareMatrix& adjoint(); // Conjugate the matrix, return it in result (and by ref) SquareMatrix& conj(SquareMatrix& result); // Tranpose and conjugate the matrix, return it in result (and by ref) SquareMatrix& adjoint(SquareMatrix& result); // Compute the inverse of the matrix and return it in result (also // returns result by reference). SquareMatrix& inverse(SquareMatrix& result) const; // Return the inverse of the matrix by value. SquareMatrix inverse() const { SquareMatrix result; return inverse(result);} // Assign 'this' to the Matrix result, also return result by reference. Matrix& matrix(Matrix& result) const; // Convert the SquareMatrix to a Matrix. Matrix matrix() const { Matrix result(n,n); return matrix(result);} private: T& throwInvAccess(); T a_p[n][n]; Int type_p; }; //# the following does not compile with Sun native but should... //# expanded by hand for types and sizes needed //#template //#ostream& operator<<(ostream& os, const SquareMatrix& m) { //# return os< inline SquareMatrix operator+(const SquareMatrix& left, //# const SquareMatrix& right) { //# SquareMatrix result(left); //# return result+=right; //#} //#template inline SquareMatrix operator*(const SquareMatrix& left, //# const SquareMatrix& right) //#{ //# SquareMatrix result(left); //# return result*=right; //#} //# //#template inline SquareMatrix directProduct(const SquareMatrix& left, //# const SquareMatrix& right) //#{ //# SquareMatrix result; //# return left.directProduct(result,right); //#} //# //#template inline SquareMatrix& //#directProduct(SquareMatrix& result, //# const SquareMatrix& left, //# const SquareMatrix& right) //#{ //# return left.directProduct(result,right); //#} //#template inline SquareMatrix conj( //# const SquareMatrix& m) { //# SquareMatrix result(m); //# return result.conj(); //#} //# //#template inline SquareMatrix adjoint( //# const SquareMatrix& m) { //# SquareMatrix result(m); //# return result.adjoint(); //#} //# // // Various global math and IO functions. // // // Calculate direct product of two SquareMatrices. SquareMatrix directProduct(const SquareMatrix& left, const SquareMatrix& right); // Return conjugate of SquareMatrix. SquareMatrix conj(const SquareMatrix& m); // Return conjugate of SquareMatrix. SquareMatrix conj(const SquareMatrix& m); // Return adjoint of SquareMatrix. SquareMatrix adjoint(const SquareMatrix& m); // Return adjoint of SquareMatrix. SquareMatrix adjoint(const SquareMatrix& m); // Write SquareMatrix to output, uses Matrix to do the work. ostream& operator<<(ostream& os, const SquareMatrix& m); ostream& operator<<(ostream& os, const SquareMatrix& m); ostream& operator<<(ostream& os, const SquareMatrix& m); ostream& operator<<(ostream& os, const SquareMatrix& m); // } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Mathematics/SquareMatrix.tcc000066400000000000000000000265511476623553700223440ustar00rootroot00000000000000//# SquareMatrix.cc: Fast Square Matrix class with fixed (templated) size //# Copyright (C) 1996,1998,1999,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_SQUAREMATRIX_TCC #define SCIMATH_SQUAREMATRIX_TCC //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template SquareMatrix& SquareMatrix::operator=(const SquareMatrix& m) { type_p=m.type_p; switch (type_p) { case ScalarId: a_p[0][0]=m.a_p[0][0]; break; case Diagonal: { for (Int i=0; i SquareMatrix& SquareMatrix::operator=(const Vector& v) { for (Int i=0; i SquareMatrix& SquareMatrix::operator=(const Matrix& m) { for (Int i=0; i SquareMatrix& SquareMatrix::operator+=(const SquareMatrix& other) { switch (type_p) { case ScalarId: switch (other.type_p) { case ScalarId: { a_p[0][0]+=other.a_p[0][0]; return *this; } case Diagonal: { T tmp=a_p[0][0]; for (Int i=0; i SquareMatrix& SquareMatrix::operator*=(const SquareMatrix& other) { switch (type_p) { case ScalarId: switch (other.type_p) { case ScalarId: { a_p[0][0]*=other.a_p[0][0]; return *this; } case Diagonal: { T tmp=a_p[0][0]; for (Int i=0; i SquareMatrix& SquareMatrix::operator*=(Float f) { switch (type_p) { case ScalarId: a_p[0][0]*=f; break; case Diagonal: { for (Int i=0; i SquareMatrix& SquareMatrix::directProduct(SquareMatrix& dp, const SquareMatrix& other) const { switch (type_p) { case ScalarId: switch (other.type_p) { case ScalarId: { dp.a_p[0][0]=a_p[0][0]*other.a_p[0][0]; dp.type_p=ScalarId; return dp; } case Diagonal: { T tmp=a_p[0][0]; for (Int i=0; i SquareMatrix& SquareMatrix::conj() { switch (type_p) { case ScalarId: { a_p[0][0]=std::conj(a_p[0][0]); return *this; } case Diagonal: { for (Int i=0; i SquareMatrix& SquareMatrix::adjoint() { switch (type_p) { case ScalarId: { a_p[0][0]=std::conj(a_p[0][0]); return *this; } case Diagonal: { for (Int i=0; i SquareMatrix& SquareMatrix::conj(SquareMatrix& result) { result = *this; result.conj(); return result; } template SquareMatrix& SquareMatrix::adjoint(SquareMatrix& result) { result = *this; result.adjoint(); return result; } template SquareMatrix& SquareMatrix::inverse(SquareMatrix& result) const { switch (type_p) { case ScalarId: { result.a_p[0][0]=T(1)/a_p[0][0]; result.type_p=ScalarId; return result; } case Diagonal: { for (Int i=0; i mat=invert(matrix()); if (mat.nelements()==0) { cerr<< "invert of singular matrix attempted:"<< matrix() << endl; result=T(1); } else result=mat; return result; } } } } } template Matrix& SquareMatrix::matrix(Matrix& result) const { result.resize(n,n); switch (type_p) { case ScalarId: { result=T(); for (Int i=0; i T& SquareMatrix::throwInvAccess() { throw(AipsError("SquareMatrix - attempt to change element that is " "not available for this type of matrix")); // following just to make signature ok. return a_p[0][0]; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/SquareMatrix2.cc000066400000000000000000000105621476623553700222350ustar00rootroot00000000000000//# SquareMatrix2.cc: explicit instantiation for SquareMatrix //# Copyright (C) 1996 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN SquareMatrix& directProduct(SquareMatrix& result, const SquareMatrix& left, const SquareMatrix& right) { const Int n=2; // Instantiation for n==2 switch (left.type_p) { case SquareMatrix::ScalarId: switch (right.type_p) { case SquareMatrix::ScalarId: { result.a_p[0][0]=left.a_p[0][0]*right.a_p[0][0]; result.type_p=SquareMatrix::ScalarId; return result; } case SquareMatrix::Diagonal: { Complex tmp=left.a_p[0][0]; for (Int i=0; i::Diagonal; return result; } case SquareMatrix::General: { Complex tmp=left.a_p[0][0]; for (Int i=0; i::General; return result; } } CASACORE_FALLTHROUGH; case SquareMatrix::Diagonal: switch (right.type_p) { case SquareMatrix::ScalarId: { Complex tmp=right.a_p[0][0]; for (Int i=0; i::Diagonal; return result; } case SquareMatrix::Diagonal: { for (Int i=0; i::Diagonal; return result; } case SquareMatrix::General: { for (Int i=0; i::General; return result; } } CASACORE_FALLTHROUGH; case SquareMatrix::General: switch (right.type_p) { case SquareMatrix::ScalarId: { Complex tmp=right.a_p[0][0]; for (Int i=0; i::General; return result; } case SquareMatrix::Diagonal: { for (Int i=0; i::General; return result; } case SquareMatrix::General: { for (Int i=0; i::General; return result; } } } // NOTREACHED return result; } SquareMatrix conj(const SquareMatrix& m) { SquareMatrix result = m; result.conj(); return result; } } //# NAMESPACE CASACORE - END casacore-3.7.1/scimath/Mathematics/StatAcc.h000066400000000000000000000167641476623553700207240ustar00rootroot00000000000000//# StatAcc.h: Statistics Accumulator //# Copyright (C) 1996,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_STATACC_H #define SCIMATH_STATACC_H #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // forward declarations: template class Block; class String; // // //
      • module Arrays //
      • Arrays module // // // // A statistics accumulator // // // // StatAcc stands for `Statistics Accumulator'. // // // //
      • A statistics accumulator accepts (weighted) input values and // calculates simple statistice (min, max, weighted mean, rms etc). // The accepted input types are real, i.e. Int, uInt, Float, Double, // but not Complex. The reason for this is that the < operator of // Complex (needed for min/max) works on the norm in any case, and // the sqrt function (needed for rms) yields an ambiguous result. // // Restriction to real types also allows the internal arithmetic type // to be Double rather than the input type. The latter would give // all kinds of complications with weighting, accuracy and overflow // if the input type would be Int or uInt. // // // The (weighted) values are fed to StatAcc via the member function // `put'. They can be fed individually, or in the form of an // Array. The weights are optional (default = 1) and always have // type Float. // // Asking for a result does not change the internal state. The // type of the returned results is always Fallible. // A result is invalid if no input values with non-zero weight // have been accumulated yet. // // The accumulator // can be re-initialised with the function `reset'. Accumulators // can be added to each other, which is as if their combined values had been // accumulated in the same accumulator. // // Some functions have been provided to display a summary of the // statistics results. One may choose between a one-line format // (with an optional associated header line), and a list. // // // // // StatAcc s; // T is Float, Double, Int etc // Matrix vv(2,5); // a matrix (array) of input values // Matrix wgt(2,5); // an associated matrix of weights // .... fill vv and wgt with values and individual weights ... // s.put(vv,wgt); // accumulate the weighted values // Fallible min = s.getMin(); // return the minimum value // // s.reset(); // re-initialise // s.put(vv); // if wgt omitted, default = 1.0 // if (s.getRms().isValid() { // check validity of rms // ... use it ... // } // // // // // One often needs simple statistics of a series of values, which // may occur by themselves or in arrays at various points in a program. // Sincs it is a pain to have to assign accumulation variables, and to // write statistics evaluation code (including exceptions), // this helper class is provided. // // // // // *************************************************************************** template class StatAcc { public: // constructors and destructor. // StatAcc(); StatAcc(const StatAcc&); ~StatAcc(){;} // // Reset or copy the accumulator attributes. // void reset(); void copy(const StatAcc&); // // Operators for adding and copying accumulators. // StatAcc& operator= (const StatAcc&); StatAcc operator+ (const StatAcc&); StatAcc& operator+= (const StatAcc&); // // Accumulate input value(s) v with weight w. // If weight is omitted, the default=1. // inline void put(const T v); inline void put(const T v, const Float w); void put(const Array& v); void put(const Array& v, const Array& w); void put(const Block& v); void put(const Block& v, const Block& w); // // Get statistics results one at a time. // Count is the nr of values accumulated. // Wtot is the sum of the weights. // Rms is defined w.r.t. the mean, and is the square of Variance. // RmsAbs is the root-mean-square of the absolute input values. // Double getWtot() const; uInt getCount() const; Fallible getMin() const; Fallible getMax() const; Fallible getMean() const; Fallible getRms() const; Fallible getVariance() const; Fallible getRmsAbs() const; // // Print summary of accumulated statistics. // Line is a one-line summary, including the (short) caption. // LineHeader gives a one-line explanation of the numbers. // List uses a separate line for each result (mean, max etc). // void printSummaryList(std::ostream&, const String& caption) const; void printSummaryLine(std::ostream&, const String& caption) const; void printSummaryLineHeader(std::ostream&, const String& caption) const; // private: Double itsWtot; //# Sum of weights Double itsWsum; //# Sum of weighted values Double itsWssum; //# Sum of weighted squares Double itsMin; //# Minimum value Double itsMax; //# Maximum value uInt itsCount; //# Number of samples // Accumulate a single weighted value. void put1(const T, const Float); }; //*************************** inline functions, have to be in StatAcc.h **** // Accumulate a single value: template inline void StatAcc::put(const T v) { put1(v, 1); // default weight = 1 } template inline void StatAcc::put(const T v, const Float w) { put1(v, w); } } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/Mathematics/StatAcc.tcc000066400000000000000000000232401476623553700212310ustar00rootroot00000000000000//# StatAcc.cc: Statistics Accumulator //# Copyright (C) 1996,1999,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_STATACC_TCC #define SCIMATH_STATACC_TCC #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Constructors: template StatAcc::StatAcc() {reset();} template StatAcc::StatAcc(const StatAcc& that) { copy(that); } // Reset (initialise) the accumulator template void StatAcc::reset () { itsWtot = 0; itsWsum = 0; itsWssum = 0; itsMax = 0; itsMin = 0; itsCount = 0; } // Copy the attributes of the given accumulator: template void StatAcc::copy (const StatAcc& that) { itsWtot = that.itsWtot; itsWsum = that.itsWsum; itsWssum = that.itsWssum; itsMax = that.itsMax; itsMin = that.itsMin; itsCount = that.itsCount; } // Accumulate an Array of values (see ArrayMath.cc): template void StatAcc::put(const Array& v) { uInt ntotal = v.nelements(); Bool vDelete; const T* vStorage = v.getStorage(vDelete); const T* vs = vStorage; while (ntotal--) {put1(*vs++,1);} v.freeStorage(vStorage, vDelete); } // Accumulate Array values with individual weights: template void StatAcc::put(const Array& v, const Array& w) { uInt ntotal = v.nelements(); if (ntotal != w.nelements()) { throw(AipsError("StatAcc::put(Array& v, Array& w): v and w have different length")); } Bool vDelete,wDelete; const T* vStorage = v.getStorage(vDelete); const T* vs = vStorage; const Float* wStorage = w.getStorage(wDelete); const Float* ws = wStorage; while (ntotal--) {put1(*vs++,*ws++);} v.freeStorage(vStorage, vDelete); w.freeStorage(wStorage, wDelete); } // Accumulate a Block (simple vector) of values: template void StatAcc::put(const Block& v) { for (uInt i=0; i void StatAcc::put(const Block& v, const Block& w) { uInt ntotal = v.nelements(); if (ntotal != w.nelements()) { throw(AipsError("StatAcc::put(Block& v, Block& w): v and w have different length")); } for (uInt i=0; i void StatAcc::put1(const T v, const Float w) { if (w != 0) { if (itsWtot == 0) { // first time itsMin = v; // minimum value itsMax = v; // maximum value } else { if (v < itsMin) {itsMin = v;} // minimum value if (v > itsMax) {itsMax = v;} // maximum value } if (w == 1) { itsWtot++; // total weight (=count) itsWsum += v; // sum itsWssum += v*v; // sum of squares } else { itsWtot += w; // total weight itsWsum += w*v; // weighted sum itsWssum += w*v*v; // weighetd sum of squares } itsCount++; } } // Get statistics results: template Double StatAcc::getWtot() const // get total weight { return itsWtot; } template uInt StatAcc::getCount() const // get number of samples { return itsCount; } template Fallible StatAcc::getMax() const // get minimum value { if (itsWtot == 0) { return Fallible(); } return Fallible(itsMax); } template Fallible StatAcc::getMin() const // get minimum value { if (itsWtot == 0) { return Fallible(); } return Fallible(itsMin); } template Fallible StatAcc::getMean() const // get mean value { if (itsWtot == 0) { return Fallible(); } return Fallible(itsWsum/itsWtot); } template Fallible StatAcc::getRmsAbs() const // get rmsAbs value { if (itsWtot == 0) { return Fallible(); } return Fallible(sqrt(itsWssum/itsWtot)); } template Fallible StatAcc::getRms() const // get rms w.r.t. the mean { if (getVariance().isValid()) { Double ms = getVariance(); if (ms >= 0) { return Fallible(sqrt(ms)); // valid } else { return Fallible(0); // .....? } } else { return Fallible(); // } } template Fallible StatAcc::getVariance() const // get variance { if (getMean().isValid()) { Double mean = getMean(); return Fallible(itsWssum/itsWtot - mean*mean); } else { return Fallible(); } } // Operator=: this = that template StatAcc& StatAcc::operator= (const StatAcc& that) { copy(that); return *this; } // Accumulators can be added. The result is the same as if the values // accumulated in `that' were accumulated in `this'. // Operator+=: this = this + that template StatAcc& StatAcc::operator+= (const StatAcc& that) { if (that.itsWtot != 0) { if (itsWtot != 0) { if (that.itsMax > itsMax) {itsMax = that.itsMax;} if (that.itsMin < itsMin) {itsMin = that.itsMin;} } else { itsMin = that.itsMin; itsMax = that.itsMax; } itsWtot += that.itsWtot; itsCount += that.itsCount; itsWsum += that.itsWsum; itsWssum += that.itsWssum; } return *this; } // Operator+: new = this + that template StatAcc StatAcc::operator+ (const StatAcc& that) { StatAcc s(*this); // construct a copy of this accumulator return s += that; // add the given accumulator to the copy } // Print one-line summary of the accumulated statistics. template void StatAcc::printSummaryLine (ostream& os, const String& caption) const { ios::fmtflags flags = os.flags(); // save current setting uInt p = 4; // precision os.setf(ios::right,ios::adjustfield); if (itsWtot != 0) { os << setprecision(p) << setw(p+3) << getWtot(); os << setprecision(p) << setw(p+3) << getCount(); os << setprecision(p) << setw(p+3) << getMean(); os << setprecision(p) << setw(p+3) << getRms(); os << setprecision(p) << setw(p+3) << getMin(); os << setprecision(p) << setw(p+3) << getMax(); } else { os << setprecision(p) << setw(p+3) << getWtot(); os << setw(5*(p+3)) << " no values accumulated " ; } os << " : " << caption << endl; // caption os.flags(flags); // restore original setting } // Print header for SummaryLine: template void StatAcc::printSummaryLineHeader (ostream& os, const String& caption) const { ios::fmtflags flags = os.flags(); // save current setting uInt p = 4; // precision // print one-line header os.setf(ios::right,ios::adjustfield); // os << " " << endl; // skip line (?) os << setw(p+3) << "wtot"; os << setw(p+3) << "npts"; os << setw(p+3) << "mean"; os << setw(p+3) << "rms"; os << setw(p+3) << "min"; os << setw(p+3) << "max"; os << " : " << caption << endl; os.flags(flags); // restore original setting } // Print multi-line summary (list) of the accumulated statistics. template void StatAcc::printSummaryList (ostream& os, const String& caption) const { ios::fmtflags flags = os.flags(); // save current setting os << " " << endl; // skip line os << " StatAcc summary for: " << caption << endl; uInt p = 12; // precision os << setprecision(p); os << " Wtot= " << setw(p+3) << getWtot() << endl; os << " Npts= " << setw(p+3) << getCount() << endl; os << " Mean= " << setw(p+3) << getMean() << endl; os << " Min= " << setw(p+3) << getMin() << endl; os << " Max= " << setw(p+3) << getMax() << endl; os << " Rms= " << setw(p+3) << getRms() << endl; os << " Variance=" << setw(p+3) << getVariance() << endl; os << " RmsAbs= " << setw(p+3) << getRmsAbs() << endl; os.flags(flags); // restore original setting } //********************************************************************** } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/VanVleck.cc000066400000000000000000000351071476623553700212410ustar00rootroot00000000000000//# VanVleck.cc: this implements VanVleck. //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // initial values for the static data members Interpolate1D *VanVleck::itsInterp = NULL; uInt VanVleck::itsSize = 65; uInt VanVleck::itsNx = 0; uInt VanVleck::itsNy = 0; Bool VanVleck::itsEquiSpaced = False; Vector VanVleck::itsQx0; Vector VanVleck::itsQx1; Vector VanVleck::itsQy0; Vector VanVleck::itsQy1; Vector VanVleck::itsQx0Qx0; Vector VanVleck::itsQy0Qy0; Matrix VanVleck::itsQx0Qy0; Matrix VanVleck::itsQx1Qy1diffs; Double VanVleck::itsXlev = 0.0; Double VanVleck::itsYlev = 0.0; Double VanVleck::itsXmean = 0.0; Double VanVleck::itsYmean = 0.0; std::mutex VanVleck::theirMutex; #define NEED_UNDERSCORES #if defined(NEED_UNDERSCORES) #define dqags dqags_ #define vvr3 vvr3_ #define vvr9 vvr9_ #define vvr3auto vvr3auto_ #define vvr9auto vvr9auto_ #define vvr3zmean vvr3zmean_ #define vvr9zmean vvr9zmean_ #define vvr3zauto vvr3zauto_ #define vvr9zauto vvr9zauto_ #endif extern "C" { void dqags(Double (*)(Double *), Double*, Double *, Double *, Double *, Double *, Double *, Int *, Int*, Int *, Int *, Int *, Int *, Double *); } extern "C" { Double vvr3(Double*, Double *, Double *, Double *, Double *); } extern "C" { Double vvr9(Double*, Double *, Double *, Double *, Double *); } extern "C" { Double vvr3auto(Double*, Double *, Double *); } extern "C" { Double vvr9auto(Double*, Double *, Double *); } extern "C" { Double vvr3zmean(Double*, Double *, Double *); } extern "C" { Double vvr9zmean(Double*, Double *, Double *); } extern "C" { Double vvr3zauto(Double*, Double *); } extern "C" { Double vvr9zauto(Double*, Double *); } void VanVleck::size(uInt npts) { std::lock_guard lock(theirMutex); if (itsSize != npts) { itsSize = npts; initInterpolator(); } } uInt VanVleck::getsize() { return itsSize; } void VanVleck::setQuantization(const Matrix &qx, const Matrix &qy) { std::lock_guard lock(theirMutex); // should double check that first dimension is 2 uInt nx = qx.ncolumn(); uInt ny = qy.ncolumn(); Bool nxChanged = itsNx != nx; Bool nyChanged = itsNy != ny; if (nxChanged) { itsQx0.resize(nx); itsQx1.resize(nx); itsQx0Qx0.resize(nx); itsNx = nx; } if (nyChanged) { itsQy0.resize(ny); itsQy1.resize(ny); itsQy0Qy0.resize(ny); itsNy = ny; } if (nxChanged || nyChanged) { itsQx0Qy0.resize(nx,ny); itsQx1Qy1diffs.resize(nx,ny); } itsQx0 = qx.row(0); itsQx1 = qx.row(1); itsQy0 = qy.row(0); itsQy1 = qy.row(1); for (uInt i=0;i lock(theirMutex); itsNx = itsNy = n; itsXlev = xlev; itsYlev = ylev; itsXmean = xmean; itsYmean = ymean; itsEquiSpaced = True; initInterpolator(); } return result; } void VanVleck::initInterpolator() { delete itsInterp; itsInterp = 0; Vector rs(itsSize); Vector rhos(itsSize); Double twoN = 2.0*itsSize; Double denom = cos(M_PI/twoN); Int midi = (itsSize-1)/2; rhos[midi] = 0.0; rs[midi] = 0.0; if (!itsEquiSpaced) { if (itsQx0.nelements() == 0) return; for (Int i=1;i<=midi;i++) { // for the rhos, choose the modified Chebyshev points // upper side Int hi = midi+i; rhos[hi] = -cos(Double(2*hi+1)*M_PI/twoN)/denom; rs[hi] = rs[hi-1] + rinc(rhos[hi-1],rhos[hi]); // lower side Int lo = midi-i; rhos[lo] = -cos(Double(2*lo+1)*M_PI/twoN)/denom; rs[lo] = rs[lo+1] + rinc(rhos[lo+1],rhos[lo]); } } else { for (Int i=1;i<=midi;i++) { // for the rhos, choose the modified Chebyshev points // upper side Int hi = midi+i; rhos[hi] = -cos(Double(2*hi+1)*M_PI/twoN)/denom; // lower side Int lo = midi-i; rhos[lo] = -cos(Double(2*lo+1)*M_PI/twoN)/denom; } if (nearAbs(itsXlev, itsYlev)) { // auto-correlation if (nearAbs(itsXmean, 0.0) && nearAbs(itsYmean, 0.0)) { // zero-mean // these are symetric about the mid-point if (itsNx == 3) { for (Int i=1;i<=midi;i++) { Int hi = midi+i; Int lo = midi-i; rs[hi] = vvr3zauto(&itsXlev, &(rhos[hi])); rs[lo] = -rs[hi]; } } else { // it must be 9 for (Int i=1;i<=midi;i++) { Int hi = midi+i; Int lo = midi-i; rs[hi] = vvr9zauto(&itsXlev, &(rhos[hi])); rs[lo] = -rs[hi]; } } } else { if (itsNx == 3) { for (uInt i=0;i fx(rs); ScalarSampledFunctional fy(rhos); itsInterp = new Interpolate1D(fx, fy, True, True); AlwaysAssert(itsInterp, AipsError); itsInterp->setMethod(Interpolate1D::spline); } void VanVleck::getTable(Vector &rs, Vector &rhos) { std::lock_guard lock(theirMutex); rs.resize(itsInterp->getX().nelements()); rs = itsInterp->getX(); rhos.resize(itsInterp->getY().nelements()); rhos = itsInterp->getY(); } Double VanVleck::r(const Double rho) { std::lock_guard lock(theirMutex); return (*itsInterp)(rho); } Bool VanVleck::dcoff(Double &dcoffset, Double &threshold, Int n, Double zerolag, Double bias) { Bool result = True; if (n == 3) { result = dcoff3(dcoffset, threshold, zerolag, bias); } else { dcoffset = 0.0; threshold = thresh(n,zerolag); } return result; } // Only private functions hereafter. They do not need to be locked. double VanVleck::drbydrho(double *rho) { Double s = 0.0; Double thisRho = *rho; Double oneMinusRhoRho = 1.0 - thisRho*thisRho; Double denom = (2.0*M_PI)*sqrt(oneMinusRhoRho); for (uInt i=0;i<(itsNx-1);i++) { for (uInt j=0;j<(itsNy-1);j++) { s+=itsQx1Qy1diffs(i,j) * exp((itsQx0Qx0[i]+thisRho*itsQx0Qy0(i,j)+itsQy0Qy0[j])/oneMinusRhoRho) / denom; } } return s; } Double VanVleck::rinc(Double &rhoi, Double &rhof) { Double work[4096]; Int iwork[1024]; Double result, abserr; Int neval, ier, last; Double epsabs=1.0e-6; Double epsrel=1.0e-6; Int limit=1024; Int lenw = 4*limit; dqags(drbydrho, &rhoi, &rhof, &epsabs, &epsrel, &result, &abserr, &neval, &ier, &limit, &lenw, &last, iwork, work); if (ier != 0) { cout << "Error in dqags : " << ier << endl; } return result; } Double VanVleck::threshNgt3(Int n, Double zerolag) { Double x = 0.0; Bool odd = True; if (n%2 == 0) { x = 1.0; odd = False; } Double tol = 1.0e-8; Double sqrt2 = sqrt(2.0); Double sqrt2dpi = sqrt(2.0/M_PI); Double fp, f; for (Int i=0;i<30;i++) { fp = 0.0; f = zerolag; if (odd) { for (Int k=1;k<=(n-1)/2;k++) { f -= (2*k-1)*::erfc((2*k-1)*x/sqrt2); Double twoKm1 = 2*k-1; fp += sqrt2dpi*twoKm1*twoKm1*exp(-0.5*(twoKm1*x)*(twoKm1*x)); } } else { f -= 1.0; for (Int k=1;k<=(n-2)/2;k++) { f -= 8*k*::erfc(k*x/sqrt2); fp += 8*k*k*sqrt2dpi*exp(-0.5*(k*x)*(k*x)); } } Double deltax = -f/fp; Double signdx = (deltax>=0) ? 1.0 : -1.0; deltax = signdx * min(0.5,abs(deltax)); x += deltax; if (odd) x = max(0.0, x); if (abs(deltax/x) < tol) break; } return x; } Double VanVleck::invErf(Double x) { // these are translations of Mathematic code supplied by Fred Schwab // based upon approximations published by Blair, Edwards, and Johnson. Double absx = abs(x); Double result; if (absx<=0.75) { // from table 10 of Blair et. al. // maximum relative error of 4.47e-8 Double t = x*x-0.75*0.75; Double p1, p2, p3, q1, q2, q3, q4; p1 = -13.0959967422; p2 = 26.785225760; p3 = -9.289057635; q1 = -12.0749426297; q2 = 30.960614529; q3 = -17.149977991; q4 = 1.0; result = x*(p1+t*(p2+t*p3))/(q1+t*(q2+t*(q3+t*q4))); } else if (absx<=0.9375) { // from table 29 of Blair et. al. // maximum relative error of 4.17e-8 Double t = x*x-.9375*.9375; Double p1,p2,p3,p4,q1,q2,q3,q4; p1 = -0.12402565221; p2 = 1.0688059574; p3 = -1.9594556078; p4 = 0.4230581357; q1 = -0.8827697997; q2 = 0.8900743359; q3 = -2.1757031196; q4 = 1.0; result = x*(p1+t*(p2+t*(p3+t*p4)))/(q1+t*(q2+t*(q3+t*q4))); } else if (absx<(1-1e-100)) { // from table 50 of Blair et. al. // maximum relative error of 2.45e-8 Double t = 1.0/sqrt(-log(1.0-absx)); Double p1,p2,p3,p4,p5,p6,q1,q2,q3; p1 = 0.1550470003116; p2 = 1.382719649631; p3 = 0.690969348887; p4 = -1.128081391617; p5 = 0.680544246825; p6 = -0.16444156791; q1 = 0.155024849822; q2 = 1.385228141995; q3 = 1.0; Double signx = (x>=0) ? 1.0 : -1.0; result = signx*(p1/t+p2+t*(p3+t*(p4+t*(p5+t*p6))))/(q1+t*(q2+t*q3)); } else { result = DBL_MAX; if (x<0) { result = -result; } } return result; } Double VanVleck::invErfc(Double x) { Double result; if (x>=2.0) { result = -DBL_MAX; } else if (x>=0.0625) { // just use invErf(1-x) result = invErf(1.0-x); } else if (x>=1e-100) { // From table 50 of Blair et al as well as table 70 Double t = 1.0/sqrt(-log(x)); Double p1,p2,p3,p4,p5,p6; Double q1,q2,q3; p1 = 0.1550470003116; p2 = 1.382719649631; p3 = 0.690969348887; p4 = -1.128081391617; p5 = 0.680544246825; p6 = -0.16444156791; q1 = 0.155024849822; q2 = 1.385228141995; q3 = 1.0; result = (p1/t+p2+t*(p3+t*(p4+t*(p5+t*p6)))) / (q1+t*(q2+t*q3)); } else if (x>0) { // from table 70 of Blair et al // maximum relative error of 2.45e-8 Double t = 1.0/sqrt(-log(x)); Double p1,p2,p3,p4; Double q1,q2,q3; p1 = 0.00980456202915; p2 = 0.363667889171; p3 = 0.97302949837; p4 = -0.5374947401; q1 = 0.00980451277802; q2 = 0.363699971544; q3 = 1.0; result = (p1/t+p2+t*(p3+t*p4))/(q1+t*(q2+t*q3)); } else { result = DBL_MAX; } return result; } Double VanVleck::predictNgt3(Int n, Double threshhold) { Double result = 0.0; if (n%2 == 0) { // even n for (Int k=1;k<=(n-2)/2;k++) { result += ::erfc(k*threshhold/sqrt(2.0)); } result = 1.0 + 8.0*result; } else { // odd n for (Int k=1;k<=(n-1)/2;k++) { result += (2*k-1)*::erfc((2*k-1)*threshhold/sqrt(2.0)); } } return result; } Bool VanVleck::dcoff3(Double &dcoffset, Double &threshold, Double zerolag, Double bias) { // the input data, bias and zerolag, should satisfy the // inequality constraints 0 <= bias < 1 and // sqrt(bias) < zerolag < 2-sqrt(bias) Bool result = True; Double rtbias = sqrt(bias); if (bias < 0.0 || bias >= 1.0 || rtbias >= zerolag || zerolag >= (2.0-rtbias)) { // fall back and return False result = False; dcoffset = 0.0; threshold = threshN3(zerolag); } else { Double rt2 = sqrt(2.0); Double t1 = invErf(1.0+rtbias-zerolag); Double t2 = invErf(-1.0+rtbias+zerolag); dcoffset = (t1+t2)/rt2; threshold = (t1-t2)/rt2; } return result; } } //# NAMESPACE CASACORE - END casacore-3.7.1/scimath/Mathematics/VanVleck.h000066400000000000000000000215151476623553700211010ustar00rootroot00000000000000//# VanVleck.h: Class of static functions to aid with vanVleck corrections. //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_VANVLECK_H #define SCIMATH_VANVLECK_H //#! Includes go here #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // A class of static functions to aid with vanVleck corrections of lag data. // // // // // //
      • Familiarity with the issues involved in turning digitally // sampled lag data from a correlator into spectral data. // // // // This provides the functions necessary to determine the van Vleck correction // for a general n-level by m-level correlator. // // // // This provides the functions necessary to determine the van Vleck correction // for a general n-level by m-level correlator. // // // // // // // The GBT spectrometer provides the measured auto-correlation and // cross-correlation lags. The gbt MeasurementSet filler (gbtmsfiller) // needs to convert those lags to the spectral domain. These functions // allow the filler to calculate the van Vleck correction appropriate // for each measured set of lags. They are of general and hence are // not specific to the GBT spectrometer. // // The functions here are static because of the nature of the underlying // numerical quadrature fortran code used to integrate the // drbyrho function. // // // //
      • //
      • // // // //
      • The inverse error functions may be more generally useful. // It exists here only as a private member function to be // used internally. // class VanVleck { public: // Set the interpolation table size. // Should be an odd number. The default size is 65. static void size(uInt npts); // get the current size. static uInt getsize(); // Set the x and y quantization functions. // Each matrix should have dimensions (2,n) // where n is the number of levels. The first // row (0,...) is the (n-1) threshold levels and // the second row is the n quantizations based // on those thresholds. The thresholds may // include a DC offset. The (0,(n-1)) element is // never used and need not be set. static void setQuantization(const Matrix &qx, const Matrix &qy); // Set the x and y quantization levels for the case // of equi-spaced levels with a possible non-zero // offset. The total number of levels is given by n, // which must be 3 or 9. If n is not 3 or 9, False // will be returned and no quantization will have been // set. For the 3- and 9- level cases a bivarate normal // integral calculation will be used. That is much faster // than the more general numerical integration used // by setQuantization. static Bool setEquiSpaced(Double xlev, Double ylev, Double xmean, Double ymean, Int n); // Get the data used in setting up the interpolation static void getTable(Vector &rs, Vector &rhos); // Given a rho return the corresponding corrected r // Returns 0.0 if no quantization has been set yet. static Double r(const Double rho); // Given a measured zero-lag autocorrelation and number of // levels (n>=3) return the first positive quantizer input // threshold level. This can be used to set the up the // matrix arguments used in setQuantization. static Double thresh(Int n, Double zerolag) { return ( (n>3) ? threshNgt3(n,zerolag) : threshN3(zerolag) ); } // Predict a given zero-lag given n and a threshold. This // is included here to be used as a check against the output // of thresh. static Double predict(Int n, Double threshhold) { return ( (n>3) ? predictNgt3(n,threshhold) : predictN3(threshhold));} // Compute an approximation to the mean signal level (DC offset) // and quantizer threshold setting (both in terms of the r.m.s. // signal input level) given the observed positive bias (the // asymptotic limit of the measured autocorrelation at large // lags) and the zero-lag autocorrelation. // dcoffset is the mean signal level, threshold is the quantizer // setting, n is the number of levels, zerolag is the zero-lag // value and bias is the asymptotic bias. // Currently, this is only available for the n==3 level case, // all other cases set the returned dcoffset to 0 and use thresh() // to set the returned value of threshold. A return value of F // indicates that the zerolag and bias values are inconsistent // and the dcoffset can not be determined. In that case, // the returned dcoffset is 0 and thresh() is used to set // the threshold level. static Bool dcoff(Double &dcoffset, Double &threshold, Int n, Double zerolag, Double bias); private: // the number of points to use in setting up the interpolator static uInt itsSize, itsNx, itsNy; static Bool itsEquiSpaced; static Double itsXlev, itsYlev, itsXmean, itsYmean; // The interpolator static Interpolate1D *itsInterp; // the quantization functions static Vector itsQx0, itsQx1, itsQy0, itsQy1; // Useful combinations of the above - to speed up drbydrho // these are -1/2*(Qx0*Qx0) and -1/2*(Qy0*Qy0) // These are only used for i up to (itsQx0.nelements() and // for j up to (itsQy0.nelements()). static Vector itsQx0Qx0, itsQy0Qy0; // This is Qx0[i]*Qy0[j] static Matrix itsQx0Qy0; // This is (Qx1[i+1]-Qx1[i])*(Qy1[j+1]*Qy1[j]) static Matrix itsQx1Qy1diffs; // The mutex to make the functions thread-safe. static std::mutex theirMutex; // The fortran numerical integration function will call this. // For a given rho and quantization functions, this computes, // via Price's theorem, the value dr/drho of the derivative, // with respect to rho, of the expected value of the correlator // output. static Double drbydrho(Double *rho); // For a given rhoi, rhof, this produces a high-accuracy numerical // approximation to the integral of drbydrho over the range // rhoi to rhof. It calls the standard QUADPACK adaptive Gaussian quadrature // procedure, dqags, to do the numerical integration. static Double rinc(Double &rhoi, Double &rhof); // initialize the interpolator static void initInterpolator(); // compute first threshhold for a given zerolag for n>3 static Double threshNgt3(Int n, Double zerolag); // compute first threshhold for a given zerolag for n==3 static Double threshN3(Double zerolag) { return sqrt(2.0)*invErfc(zerolag);} // inverse err fn - used by invErfc static Double invErf(Double x); // inverse complementary err fn - used by threshN3 static Double invErfc(Double x); // Predict a zero-lag value given the indicated first threshold level // for n>3. static Double predictNgt3(Int n, Double threshhold); // Predict a zero-lag value given the indicated first threshold level // for n=3. static Double predictN3(Double threshhold) { return ::erfc(threshhold/sqrt(2.0));} // implementation of dcoff for the 3-level case static Bool dcoff3(Double &dcoffset, Double &threshold, Double zerolag, Double bias); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/VectorKernel.cc000066400000000000000000000140051476623553700221250ustar00rootroot00000000000000//# VectorKernel.cc: generate moments from an image //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Vector VectorKernel::make(KernelTypes kernelType, Double width, uInt shape, Bool useShapeExactly, Bool peakIsUnity) { LogIO os(LogOrigin("VectorKernel", "make(Double)")); if (shape <=1) { os << "Shape must be > 1" << LogIO::EXCEPTION; } // Vector kernel; uInt nPixels = 0; if (kernelType == GAUSSIAN) { // Gaussian. The volume error is less than 6e-5% for +/- 5 sigma limits // width is FWHM const Double sigma = width / sqrt(Double(8.0) * M_LN2); if (useShapeExactly) { nPixels = shape; } else { nPixels = max(shape,(uInt(5*sigma + 0.5) + 1) * 2); } kernel.resize(nPixels); // const Double refPix = Double(nPixels)/2; Double norm; if (peakIsUnity) { norm = 1.0; } else { norm = 1.0 / (sigma * sqrt(2.0 * M_PI)); } const Gaussian1D gauss(norm, refPix, Double(width)); for (uInt j=0; j VectorKernel::make(KernelTypes kernelType, Float width, uInt shape, Bool useShapeExactly, Bool peakIsUnity) { Double tw = width; Vector tmp = make(kernelType, tw, shape, useShapeExactly, peakIsUnity); Vector kernel(tmp.nelements()); for (uInt i=0; i VectorKernel::toKernelTypes (const String& kernels, const std::regex& delimiter) { const Vector kernelStrings = stringToVector(kernels, delimiter); return VectorKernel::toKernelTypes(kernelStrings); } Vector VectorKernel::toKernelTypes (const Vector& kernels) { const uInt n = kernels.nelements(); Vector kernelTypes(n); for (uInt i=0; i #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class Regex; class String; // // Make a Vector smoothing kernel from a kernel specification // // // // // //
      • Vector // // // Returns a vector from a smoothing kernel specification. // // // This class takes a smoothing kernel type and a width, and // generates a Vector holding that kernel. It can be used // in conjunction with the separable image convolver, // SepImageConvolver // // The kernels can be normalized so that the peak of the // kernel is 1, or 1/area under the kernel. The latter ensures // conservation of integrated pixel value (the usual jargon // is conservation of flux for images) and is the default. // // // // // // // // //
      • // class VectorKernel { public: enum KernelTypes { // Box-car smoothing kernel BOXCAR, // Gaussian smoothing kernel GAUSSIAN, // Hanning smoothing kernel HANNING, NKERNELS}; // Create kernel vector for width in pixels. For Gaussian, width is FWHM, // for Boxcar, width is full width. For Hanning width is ignored. // If useShapeExactly is True, the provided shape is used exactly. // If useShapeExactly is False, // the kernel length will be the max of the provided shape and an // autoestimate (e.g. from +/- 5sigma limits for a Gaussian). // static Vector make(KernelTypes kernelType, Double width, uInt shape, Bool useShapeExactly, Bool peakIsUnity=False); static Vector make(KernelTypes kernelType, Float width, uInt shape, Bool useShapeExactly, Bool peakIsUnity=False); // // Helper function to convert a string containing a list of desired smoothed kernel types // to the correct Vector required for the setSmooth function. // This may be usful if your user interface involves strings rather than integers. // A new value is added to the output vector (which is resized appropriately) if any of the // substrings "boxcar", "gaussian" or "hanning" (actually "box", "gauss", and "hann" // will do) is present. static Vector toKernelTypes (const String& kernels, const std::regex& delimiter); static Vector toKernelTypes (const Vector& kernels); static VectorKernel::KernelTypes toKernelType (const String& kernel); static String fromKernelType (KernelTypes kernelType); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/scimath/Mathematics/test/000077500000000000000000000000001476623553700201725ustar00rootroot00000000000000casacore-3.7.1/scimath/Mathematics/test/CMakeLists.txt000066400000000000000000000010111476623553700227230ustar00rootroot00000000000000set (tests dAutoDiff dSparseDiff tAutoDiff tCombinatorics tConvolver tFFTServer tFFTServer2 tGaussianBeam tGeometry tHistAcc tInterpolateArray1D tMathFunc tMatrixMathLA tMedianSlider tSmooth tSparseDiff tStatAcc tVanVleck tVectorKernel tInterpolate2D ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_scimath) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/scimath/Mathematics/test/dAutoDiff.cc000066400000000000000000000100771476623553700223530ustar00rootroot00000000000000//# dAutoDiff.cc: Demo program for AutoDiff, including 2nd derivative //# Copyright (C) 2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include // Define a simple function a^3*b^3*x template class f { public: T operator()(const T& x) { return a_p*a_p*a_p*b_p*b_p*x; } void set(const T& a, const T& b) { a_p = a; b_p = b; } private: T a_p; T b_p; }; // Specialization template <> class f > { public: AutoDiffA operator()(const AutoDiffA& x) { Vector dr(a_p.nDerivatives()); dr[0] = 3*a_p.value()*a_p.value()*b_p.value()*b_p.value()*x.value(); dr[1] = 2*a_p.value()*a_p.value()*a_p.value()*b_p.value()*x.value(); return AutoDiffA(a_p.value()*a_p.value()*a_p.value()* b_p.value()*b_p.value()*x.value(), dr); } void set(const AutoDiff& a, const AutoDiff& b) { a_p = a; b_p = b; } private: AutoDiffA a_p; AutoDiffA b_p; }; int main() { cout << "Test AutoDiff2" << endl; cout << "----------------------------------------" << endl; // By selecting Double a,b,x; f(x) will calculate value Double a0(2), b0(3), x0(7); f f0; f0.set(a0, b0); cout << "Value: " << f0(x0) << endl; // By selecting AutoDiff a,b, and x; f(x) will calculate value and // partial derivatives wrt a,b AutoDiff a1(2,2,0), b1(3,2,1), x1(7); f > f1; f1.set(a1, b1); cout << "Diff a,b: " << f1(x1) << endl; // No need to use the function object, just calculate the expression: cout << "Same...: " << (pow(a1,3.0)*pow(b1,2.0)*x1) << endl; // Use the specialization: f > f12; f12.set(a1, b1); cout << "Same...: " << f12(x1) << endl; // By selecting AutoDiff x, and a,b; f(x) will calculate value and // partial derivative wrt x AutoDiff a2(2), b2(3), x2(7,1,0); f > f2; f2.set(a2, b2); cout << "Diff x: " << f2(x2) << endl; // By selecting AutoDiff a,b, and x; f(x) will calculate value and // (first and) 2nd order partial derivatives wrt a,b AutoDiff > a3(AutoDiff(2,2,0),2,0), b3(AutoDiff(3,2,1),2,1), x3(AutoDiff(7),2); f > > f3; f3.set(a3, b3); cout << "Diff2 a,b: " << f3(x3) << endl; // By selecting AutoDiff x, and a,b; f(x) will calculate value and // (first and) 2nd order partial derivatives wrt x AutoDiff > a4(AutoDiff(2),1), b4(AutoDiff(3),1), x4(AutoDiff(7,1,0),1,0); f > > f4; f4.set(a4, b4); cout << "Diff2 x: " << f4(x4) << endl; } template class f; template class f >; template class f > >; casacore-3.7.1/scimath/Mathematics/test/dSparseDiff.cc000066400000000000000000000207221476623553700226760ustar00rootroot00000000000000//# dSparseDiff.cc: Demo program for AutoDiff, including 2nd derivative //# Copyright (C) 2007 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include // Define a simple function a^3*b^3*x template class f { public: T operator()(const T& x) { return a_p*a_p*a_p*b_p*b_p*x; } void set(const T& a, const T& b) { a_p = a; b_p = b; } private: T a_p; T b_p; }; // Specialization template <> class f > { public: SparseDiffA operator()(const SparseDiffA& x) { return SparseDiffA(a_p.value()*a_p.value()*a_p.value()* b_p.value()*b_p.value()*x.value(), 0, 3*a_p.value()*a_p.value()*b_p.value()* b_p.value()*x.value()) + SparseDiffA(0, 1, 2*a_p.value()*a_p.value()*a_p.value()* b_p.value()*x.value()) ; } void set(const SparseDiff& a, const SparseDiff& b) { a_p = a; b_p = b; } private: SparseDiffA a_p; SparseDiffA b_p; }; int main() { cout << "Test SparseDiff" << endl; cout << "----------------------------------------" << endl; // By selecting Double a,b,x; f(x) will calculate value Double a0(2), b0(3), x0(7); f f0; f0.set(a0, b0); cout << "Value: " << f0(x0) << endl; // By selecting SparseDiff a,b, and x; f(x) will calculate value and // partial derivatives wrt a,b SparseDiff a1(2,0), b1(3,1), x1(7); f > f1; f1.set(a1, b1); cout << "Diff a,b: " << f1(x1) << endl; { SparseDiff a(3,0), b(5,1), x(7); Double y(11); cout << "a: " << a << endl; cout << "b: " << b << endl; cout << "x: " << x << endl; cout << "y: " << y << endl; } // *= { SparseDiff a(3,0), b(5,1), x(7); Double y(11); a *= a; b *= b; x *= x; y *= y; cout << "a*=a: " << a << endl; cout << "b*=b: " << b << endl; cout << "x*=x: " << x << endl; cout << "y*=y: " << y << endl; } { SparseDiff a(3,0), b(5,1), x(7); a *= b; x *= b; cout << "a*=b: " << a << endl; cout << "x*=b: " << x << endl; } { SparseDiff a(3,0), b(5,1), x(7); a *= x; b *= x; cout << "a*=x: " << a << endl; cout << "b*=x: " << b << endl; } { SparseDiff a(3,0), b(5,1), x(7); Double y(11); a *= y; b *= y; x *= y; cout << "a*=y: " << a << endl; cout << "b*=y: " << b << endl; cout << "x*=y: " << x << endl; } { SparseDiff a(3,0), b(5,1), x(7); b *= a; x *= a; cout << "b*=a: " << b << endl; cout << "x*=a: " << x << endl; } // += { SparseDiff a(3,0), b(5,1), x(7); Double y(11); a += a; b += b; x += x; y += y; cout << "a+=a: " << a << endl; cout << "b+=b: " << b << endl; cout << "x+=x: " << x << endl; cout << "y+=y: " << y << endl; } { SparseDiff a(3,0), b(5,1), x(7); a += b; x += b; cout << "a+=b: " << a << endl; cout << "x+=b: " << x << endl; } { SparseDiff a(3,0), b(5,1), x(7); a += x; b += x; cout << "a+=x: " << a << endl; cout << "b+=x: " << b << endl; } { SparseDiff a(3,0), b(5,1), x(7); Double y(11); a += y; b += y; x += y; cout << "a+=y: " << a << endl; cout << "b+=y: " << b << endl; cout << "x+=y: " << x << endl; } { SparseDiff a(3,0), b(5,1), x(7); b += a; x += a; cout << "b+=a: " << b << endl; cout << "x+=a: " << x << endl; } // -= { SparseDiff a(3,0), b(5,1), x(7); Double y(11); const SparseDiff aref(a); const SparseDiff bref(b); const SparseDiff xref(x); a -= aref; b -= bref; x -= xref; y -= y; cout << "a-=a: " << a << endl; cout << "b-=b: " << b << endl; cout << "x-=x: " << x << endl; cout << "y-=y: " << y << endl; } { SparseDiff a(3,0), b(5,1), x(7); a -= b; x -= b; cout << "a-=b: " << a << endl; cout << "x-=b: " << x << endl; } { SparseDiff a(3,0), b(5,1), x(7); a -= x; b -= x; cout << "a-=x: " << a << endl; cout << "b-=x: " << b << endl; } { SparseDiff a(3,0), b(5,1), x(7); Double y(11); a -= y; b -= y; x -= y; cout << "a-=y: " << a << endl; cout << "b-=y: " << b << endl; cout << "x-=y: " << x << endl; } { SparseDiff a(3,0), b(5,1), x(7); b -= a; x -= a; cout << "b-=a: " << b << endl; cout << "x-=a: " << x << endl; } // /= { SparseDiff a(3,0), b(5,1), x(7); Double y(11); const SparseDiff aref(a); const SparseDiff bref(b); const SparseDiff xref(x); a /= aref; b /= bref; x /= xref; y /= y; cout << "a/=a: " << a << endl; cout << "b/=b: " << b << endl; cout << "x/=x: " << x << endl; cout << "y/=y: " << y << endl; } { SparseDiff a(3,0), b(5,1), x(7); a /= b; x /= b; cout << "a/=b: " << a << endl; cout << "x/=b: " << x << endl; } { SparseDiff a(3,0), b(5,1), x(7); a /= x; b /= x; cout << "a/=x: " << a << endl; cout << "b/=x: " << b << endl; } { SparseDiff a(3,0), b(5,1), x(7); Double y(11); a /= y; b /= y; x /= y; cout << "a/=y: " << a << endl; cout << "b/=y: " << b << endl; cout << "x/=y: " << x << endl; } { SparseDiff a(3,0), b(5,1), x(7); b /= a; x /= a; cout << "b/=a: " << b << endl; cout << "x/=a: " << x << endl; } // Various { SparseDiff a(3,0), b(5,1), x(7); Double y(11); b *= a; cout << "b*=a: " << b << endl; b *= b; cout << "b*=a*=b: " << b << endl; b *= x; cout << "b*=a*=b*=x: " << b << endl; b *= y; cout << "b*=a*=b*=x*=y: " << b << endl; } { SparseDiff a(3,0), b(5,1), x(7); Double y(11); a *= x; b *= y; cout << "a*=x: " << a << endl; cout << "b*=y: " << b << endl; } // No need to use the function object, just calculate the expression: cout << "Same...: " << (pow(a1,3.0)*pow(b1,2.0)*x1) << endl; /* // Use the specialization: f > f12; f12.set(a1, b1); cout << "Same...: " << f12(x1) << endl; // By selecting SparseDiff x, and a,b; f(x) will calculate value and // partial derivative wrt x SparseDiff a2(2), b2(3), x2(7,0); f > f2; f2.set(a2, b2); cout << "Diff x: " << f2(x2) << endl; // By selecting SparseDiff a,b, and x; f(x) will calculate value and // (first and) 2nd order partial derivatives wrt a,b SparseDiff > a3(SparseDiff(2,0),0), b3(SparseDiff(3,1),1), x3(SparseDiff(7)); f > > f3; f3.set(a3, b3); cout << "Diff2 a,b: " << f3(x3) << endl; // By selecting SparseDiff x, and a,b; f(x) will calculate value and // (first and) 2nd order partial derivatives wrt x SparseDiff > a4(SparseDiff(2)), b4(SparseDiff(3)), x4(SparseDiff(7,0),0); f > > f4; f4.set(a4, b4); cout << "Diff2 x: " << f4(x4) << endl; */ } template class f; template class f >; ///template class f > >; casacore-3.7.1/scimath/Mathematics/test/tAutoDiff.cc000066400000000000000000000331501476623553700223700ustar00rootroot00000000000000//# tAutoDiff.cc: test program for AutoDiff //# Copyright (C) 1995,1996,1999,2000,2001,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include // Made some minor changes, so we don't compare floating point values directly, i.e. use == // but rather use nearAbs, as the floating point between chips won't necessarily give the // same answer. wky 23-aug-2004 int main() { uInt nerr = 0; // test the constructors { AutoDiff a; if (a.value() != 0 || (a.derivatives()).nelements() != 0) { cerr << "AutoDiff a; failed a = " << a << endl; nerr++; } AutoDiff b(1.0); if (b.value() != 1.0 || b.derivatives().nelements() != 0) { cerr << "AutoDiff b(1.0); failed b = " << b << endl; nerr++; } Vector g(3); g = 0.0; g(1) = 1.0; AutoDiff x(2.0, 3, 1); if (x.value() != 2.0 || ! allEQ(x.derivatives(),g)) { cerr << "AutoDiff x(2.0, 3, 1); failed x = " << x << endl; nerr++; } AutoDiff y(x); if (y.value() != x.value() || ! allEQ(y.derivatives(),x.derivatives())) { cerr << "AutoDiff y(x); failed y = " << y << " x = " << x << endl; nerr++; } g(0) = 1.0; g(1) = -1.0; g(2) = 0.5; Float val = 5.0; AutoDiff z(val, g); if (z.value() != val || ! allEQ(z.derivatives(),g)) { cerr << "AutoDiff z(val, g); failed z = " << z << " val = " << val << " g = " << g << endl; nerr++; } } // test the assignment operators { AutoDiff x(3.0,1,0); x = 1.0; if (x.value() != 1.0 || x.derivatives().nelements()!=0) { cerr << "assignment to constant failed x : " << x << endl; nerr++; } AutoDiff y(2.0, 3, 1); x = y; if (x.value() != y.value() || ! allEQ(x.derivatives(), y.derivatives())) { cerr << "assignment to other failed x : " << x << " y : " << y << endl; nerr++; } } // test the class member operators { AutoDiff x(3.0,2,0); AutoDiff y(2.0,2,1); AutoDiff z; z = x; z *= y; // verify result if (z.value() != (x.value() * y.value()) || z.derivatives()(0) != y.value() || z.derivatives()(1) != x.value()) { cerr << "*= operator failed" << endl; nerr++; } z = x; z /= y; // verify result if (z.value() != (x.value() / y.value()) || z.derivatives()(0) != (1.0/y.value()) || z.derivatives()(1) != (-x.value()/(y.value()*y.value()))) { cerr << "/= operator failed" << endl; nerr++; } z = x; z += y; // verify result if (z.value() != (x.value() + y.value()) || z.derivatives()(0) != 1 || z.derivatives()(1) != 1) { cerr << "+= operator failed" << endl; nerr++; } z = x; z -= y; // verify result if (z.value() != (x.value() - y.value()) || z.derivatives()(0) != 1 || z.derivatives()(1) != -1) { cerr << "-= operator failed" << endl; nerr++; } } // other class members { AutoDiff x; if (x.nDerivatives() != 0) { cerr << "wrong number of elements, should be 0" << endl; nerr++; } if (!x.isConstant()) { cerr << "x should be const, isConstant reports False" << endl; nerr++; } AutoDiff y(1.0,3,0); if (y.nDerivatives() != 3) { cerr << "resize failed" << endl; nerr++; } Vector grad(3); grad(0) = 1.; grad(1) = 2.; grad(2) = 3.; y = AutoDiff(y.value(), grad);;; if (!allEQ(y.derivatives(),grad)) { cerr << "derivatives assignment failed" << endl; nerr++; } y.value() = 4.0; if (y.value() != 4.0) { cerr << "value assignment failed" << endl; nerr++; } if (y.isConstant()) { cerr << "y should not be const, isConstant reports True" << endl; nerr++; } } // AutoDIffMath tests { AutoDiff x(3.0,1,0); AutoDiff y; y = +x; if (y.value() != x.value() || !allEQ(y.derivatives(), x.derivatives())) { cerr << "operator+(const AutoDiff &) failed" << endl; nerr++; } y = -x; if (y.value() != -x.value() || !allEQ(y.derivatives(), -x.derivatives())) { cerr << "operator-(const AutoDiff &) failed" << endl; nerr++; } y = x + x; if (y.value() != (Float(2.0) * x.value()) || !allEQ(y.derivatives(), Float(2.0) * x.derivatives())) { cerr << "operator+(const AutoDiff &, const AutoDiff &) failed" << endl; nerr++; } y = x - x; if (y.value() != 0.0 || !allEQ(y.derivatives(), Float(0.0))) { cerr << "operator-(const AutoDiff &, const AutoDiff &) failed" << endl; nerr++; } y = x * x; if (y.value() != (x.value() * x.value()) || !allEQ(y.derivatives(), Float(2.0) * x.value() * x.derivatives())) { cerr << "operator*(const AutoDiff &, const AutoDiff &) failed" << endl; nerr++; } y = x / x; if (!near(y.value(),Float(1)) || !allNearAbs(y.derivatives(), Float(0.0),1.0e-5)) { cerr << "operator/(const AutoDiff &, const AutoDiff &) failed" << endl; nerr++; } y = x + Float(1.0); if (y.value() != (x.value() + Float(1.0)) || !allEQ(y.derivatives(), x.derivatives())) { cerr << "operator+(const AutoDiff &,const T&) failed" << endl; nerr++; } y = x - Float(1.0); if (y.value() != (x.value() - Float(1.0)) || !allEQ(y.derivatives(), x.derivatives())) { cerr << "operator-(const AutoDiff &,const T&) failed" << endl; nerr++; } y = x * Float(2.0); if (y.value() != (x.value() * Float(2.0)) || !allEQ(y.derivatives(), x.derivatives()*Float(2.0))) { cerr << "operator*(const AutoDiff &,const T&) failed" << endl; nerr++; } y = x / Float(2.0); if (y.value() != (x.value() / Float(2.0)) || !allEQ(y.derivatives(), x.derivatives()/Float(2.0))) { cerr << "operator/(const AutoDiff &,const T&) failed" << endl; nerr++; } y = Float(1.0) + x; if (y.value() != (x.value() + Float(1.0)) || !allEQ(y.derivatives(), x.derivatives())) { cerr << "operator+(,const T&, const AutoDiff &) failed" << endl; nerr++; } y = Float(1.0) - x; if (y.value() != (Float(1.0) - x.value()) || !allEQ(y.derivatives(), -x.derivatives())) { cerr << "operator-(const T&, const AutoDiff &) failed" << endl; nerr++; } y = Float(2.0) * x; if (y.value() != (x.value() * Float(2.0)) || !allEQ(y.derivatives(), x.derivatives()*Float(2.0))) { cerr << "operator*(const T&, const AutoDiff &) failed" << endl; nerr++; } y = Float(2.0) / x; if (!near(y.value(),Float(2.0) / x.value()) || !allNearAbs(y.derivatives(), -x.derivatives()*Float(2.0)/(x.value()*x.value()),1.0e-5)) { cerr << "operator/(const T&, const AutoDiff &) failed" << endl; nerr++; } // trancendentals x.value() = 0.5; // acos(x) : derivative = -1/sqrt(1-x*x) y = acos(x); if (y.value() != Float(acos(x.value())) || !allEQ(y.derivatives(), -x.derivatives()/Float(sqrt(1.0 - x.value()*x.value())))) { cerr << "acos(const AutoDiff &) failed" << endl; nerr++; } // asin(x) : derivative = 1/sqrt(1-x*x) y = asin(x); if (y.value() != Float(asin(x.value())) || !allEQ(y.derivatives(), x.derivatives()/Float(sqrt(1.0 - x.value()*x.value())))) { cerr << "asin(const AutoDiff &) failed" << endl; nerr++; } // atan(x) : derivative = 1/(1+x*x) y = atan(x); if (!allNearAbs(y.value(), Float(atan(x.value())),1.e-6) || !allNearAbs(y.derivatives(), x.derivatives()/Float(1.0 + x.value()*x.value()),1.e-6)) { cerr << y.value() - Float(atan(x.value())) << endl; cerr << y.derivatives() - x.derivatives()/Float(1.0 + x.value()*x.value()) << endl; cerr << "atan(const AutoDiff &) failed" << endl; nerr++; } // atan2(x, y) : derivative = d(atan(x/y)) // = (1/(1+(x/y)*(x/y))) * (dx/y - x*dy/y**2) AutoDiff w(3.0, 2, 0); AutoDiff z(2.5, 2, 1); y = atan2(w, z); if (y.value() != Float(atan2(w.value(), z.value())) || !allEQ(y.derivatives(), (w.derivatives()/z.value() - w.value()*z.derivatives()/(z.value()*z.value())) / Float(1.0 + w.value()*w.value()/(z.value()*z.value())))) { cerr << "atan2(const AutoDiff &, const AutoDiff &g) failed" << endl; nerr++; } // cos(x) : derivative = -sin(x) y = cos(x); if (!nearAbs(y.value(), Float(cos(x.value())) ) || !allEQ(y.derivatives(),-Float(sin(x.value()))*x.derivatives())) { cerr << "cos(const AutoDiff &) failed" << endl; nerr++; } // cosh(x) : derivative = sinh(x) y = cosh(x); if (y.value() != Float(cosh(x.value())) || !allEQ(y.derivatives(), Float(sinh(x.value()))*x.derivatives())) { cerr << "cosh(const AutoDiff &) failed" << endl; nerr++; } // exp(x) : derivative = exp(x) y = exp(x); if (y.value() != Float(exp(x.value())) || !allEQ(y.derivatives(), x.derivatives() * Float(exp(x.value())))) { cerr << "exp(const AutoDiff &) failed" << endl; nerr++; } // log(x) : derivative = 1/x y = log(x); if (y.value() != Float(log(x.value())) || !allEQ(y.derivatives(), x.derivatives() / x.value())) { cerr << "log(const AutoDiff &) failed" << endl; nerr++; } // log10(x) : derivative = (1/x) / log(10) y = log10(x); if (y.value() != Float(log10(x.value())) || !allEQ(y.derivatives(), x.derivatives() / Float((x.value()*log(10.0))))) { cerr << "log10(const AutoDiff &) failed" << endl; nerr++; } // pow(x,y) : derivative = y*pow(x,y-1)*dx + pow(x,y)*log(x)*dy y = pow(w,z); if (y.value() != Float(pow(w.value(), z.value())) || !allEQ(y.derivatives(), (Float(z.value()*pow(w.value(),z.value()-1))* w.derivatives() + Float(pow(w.value(),z.value())*log(w.value()))* z.derivatives()))) { cerr << "pow(const AutoDiff &, const AutoDiff &) failed" << endl; nerr++; } // pow(x,const) : derivative = const*pow(x,const-1)*dx y = pow((AutoDiff&)x,Float(2.5)); if (y.value() != Float(pow(x.value(),2.5)) || !allEQ(y.derivatives(), Float(2.5*pow(x.value(),1.5))*x.derivatives())) { cerr << "pow(const AutoDiff &, const double &) failed" << endl; nerr++; } // sin(x) : derivative = cos(x) y = sin(x); if (!allNearAbs(y.value(), Float(sin(x.value())) ) || !allEQ(y.derivatives(), Float(cos(x.value()))*x.derivatives())) { cerr << "sin(const AutoDiff &) failed" << endl; nerr++; } // sinh(x) : derivative = cosh(x) y = sinh(x); if (!allNearAbs(y.value(), Float(sinh(x.value()))) || !allEQ(y.derivatives(), Float(cosh(x.value()))*x.derivatives())) { cerr << "sinh(const AutoDiff &) failed" << endl; nerr++; } // sqrt(x) : derivative = 0.5/sqrt(x) y = sqrt(x); if (!allNearAbs(y.value(), Float(sqrt(x.value()))) || !allEQ(y.derivatives(), x.derivatives()*Float(0.5/sqrt(x.value())))) { cerr << "sqrt(const AutoDiff &) failed" << endl; nerr++; } // tan(x) : derivative = sec(x)*sec(x) = 1/(cos(x)*cos(x)) y = tan(x); if (!allNearAbs(y.value(), Float(tan(x.value())) ) || !allNearAbs(y.derivatives(), x.derivatives()/Float(cos(x.value())*cos(x.value())),1.e-6)) { cerr << "tan(const AutoDiff &) failed" << endl; nerr++; } // tanh(x) : derivative = sech(x)*sech(x) = 1/(cosh(x)*cosh(x)) y = tanh(x); if (!allNearAbs(y.value(), Float(tanh(x.value())) ) || !allEQ(y.derivatives(), x.derivatives()/Float(cosh(x.value())*cosh(x.value())))) { cerr << "sinh(const AutoDiff &) failed" << endl; nerr++; } } if (nerr != 0) cout << "There were " << nerr << " errors" << endl; else cout << "ok" << endl; return nerr; } casacore-3.7.1/scimath/Mathematics/test/tCombinatorics.cc000066400000000000000000000044441476623553700234670ustar00rootroot00000000000000//# tMathFunc.cc: This program tests MathFunc objects //# Copyright (C) 1993,1994,1995,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include int main() { try { AlwaysAssert(Combinatorics::factorial(0) == 1, AipsError); AlwaysAssert(Combinatorics::factorial(1) == 1, AipsError); AlwaysAssert(Combinatorics::factorial(5) == 120, AipsError); AlwaysAssert(Combinatorics::factorial(4) == 24, AipsError); AlwaysAssert(Combinatorics::factorial(6) == 720, AipsError); AlwaysAssert(Combinatorics::choose(6,3) == 20, AipsError); AlwaysAssert(Combinatorics::choose(5,3) == 10, AipsError); { // test exception is thrown if k > n Bool res = False; try { Combinatorics::choose(3,5); } catch (std::exception&) { res = True; } AlwaysAssert(res, AipsError); } cout << "ok" << endl; } catch (std::exception& x) { cerr << x.what() << endl; return 1; } return 0; } casacore-3.7.1/scimath/Mathematics/test/tConvolver.cc000066400000000000000000002755261476623553700226630ustar00rootroot00000000000000//# tConvolver.cc: this tests the Convolver class //# Copyright (C) 1996,1997,1998,1999,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include Bool doLinearConv() { Double beamData[] = { 2.7000172105e-25, 9.8635317948e-24, 3.0190166275e-22, 7.7421342681e-21, 1.6634945915e-19, 2.9946487959e-18, 4.5168693657e-17, 5.7081185033e-16, 6.0438070835e-15, 5.3616318199e-14, 3.9851734651e-13, 2.481758939e-12, 1.2949036808e-11, 5.6608499138e-11, 2.0734351736e-10, 6.3630173353e-10, 1.6360705013e-09, 3.5245522056e-09, 6.3616734103e-09, 9.6206465017e-09, 1.2189945942e-08, 1.2940859051e-08, 1.151038731e-08, 8.5778921743e-09, 5.3559525703e-09, 2.8019366827e-09, 1.2281282658e-09, 4.5101875012e-10, 1.3877438088e-10, 3.5776017565e-11, 7.7274167967e-12, 1.3984425597e-12, 2.1204007517e-13 , 7.362798549e-24, 2.5496188929e-22, 7.3972968879e-21, 1.7982067704e-19, 3.6623942745e-18, 6.2496777531e-17, 8.9354691452e-16, 1.0703829994e-14, 1.0743044909e-13, 9.0339850401e-13, 6.3649528356e-12, 3.757297945e-11, 1.8583280748e-10, 7.7007639154e-10, 2.6736826086e-09, 7.7776736163e-09, 1.895640267e-08, 3.8710243899e-08, 6.623101001e-08, 9.4942500084e-08, 1.1403165701e-07, 1.1475035677e-07, 9.6749545264e-08, 6.8345009652e-08, 4.045116242e-08, 2.0059411909e-08, 8.334380297e-09, 2.9012920955e-09, 8.4619750096e-10, 2.0678624091e-10, 4.2338410555e-11, 7.2629064221e-12, 1.043881019e-12 , 1.7142488945e-22, 5.6270084002e-21, 1.5475455831e-19, 3.5659326073e-18, 6.8844614491e-17, 1.1135950628e-15, 1.5092172168e-14, 1.7137238351e-13, 1.6304078313e-12, 1.2996222154e-11, 8.6796098087e-11, 4.8567921906e-10, 2.2770070274e-09, 8.9442311335e-09, 2.9436453275e-08, 8.1169602595e-08, 1.8752831465e-07, 3.6299820749e-07, 5.8871842157e-07, 7.9997084868e-07, 9.1076549324e-07, 8.6876599426e-07, 6.9432843475e-07, 4.6493357786e-07, 2.6084489946e-07, 1.2261305926e-07, 4.8290122123e-08, 1.5934691788e-08, 4.4054928594e-09, 1.0204967982e-09, 1.980565989e-10, 3.2205800526e-11, 4.3877583858e-12 , 3.4077236103e-21, 1.0603099006e-19, 2.7641931753e-18, 6.03762504e-17, 1.1049162573e-15, 1.6941678773e-14, 2.1764347594e-13, 2.3426341162e-12, 2.1126518937e-11, 1.5962965672e-10, 1.0105679626e-09, 5.3602038363e-09, 2.3821185025e-08, 8.8697063916e-08, 2.7670731129e-07, 7.2326224654e-07, 1.5839317484e-06, 2.9063089642e-06, 4.4679895836e-06, 5.7550132624e-06, 6.2107837948e-06, 5.6157773543e-06, 4.2544133976e-06, 2.7004296044e-06, 1.4361226022e-06, 6.3990404442e-07, 2.3889310796e-07, 7.4723331522e-08, 1.9582705235e-08, 4.299887113e-09, 7.9105094697e-10, 1.2193142329e-10, 1.5746770207e-11 , 5.7837483018e-20, 1.7058633512e-18, 4.2154884256e-17, 8.727979425e-16, 1.5140580101e-14, 2.2005867181e-13, 2.6797847795e-12, 2.7341625491e-11, 2.3373031288e-10, 1.6740544506e-09, 1.0045878795e-08, 5.0509349592e-08, 2.1277482176e-07, 7.5099006835e-07, 2.220812803e-06, 5.50244431e-06, 1.1422562238e-05, 1.9867155061e-05, 2.8951673812e-05, 3.534886855e-05, 3.6161200114e-05, 3.0993738619e-05, 2.2257245291e-05, 1.3391580978e-05, 6.7508458415e-06, 2.8513409234e-06, 1.0090335536e-06, 2.9917509892e-07, 7.4320794852e-08, 1.5468991421e-08, 2.697583712e-09, 3.9414410624e-10, 4.8250181628e-11 , 8.3813806733e-19, 2.3432582496e-17, 5.488961963e-16, 1.0772607375e-14, 1.7714145038e-13, 2.4405256462e-12, 2.81714721e-11, 2.7246005452e-10, 2.2077970563e-09, 1.4989314678e-08, 8.5264652228e-08, 4.0636817289e-07, 1.6226908883e-06, 5.428964414e-06, 1.521817012e-05, 3.5741581087e-05, 7.0331341703e-05, 0.00011595467367, 0.00016017470625, 0.00018538023869, 0.00017976220988, 0.00014604839089, 9.9417185993e-05, 5.6700846471e-05, 2.7094622055e-05, 1.08477916e-05, 3.6388605622e-06, 1.0227099665e-06, 2.4082694949e-07, 4.7514166823e-08, 7.854269235e-09, 1.0878081769e-09, 1.2623001255e-10 , 1.0370020887e-17, 2.7482042207e-16, 6.1021867118e-15, 1.1352415319e-13, 1.7695107532e-12, 2.3109132663e-11, 2.528582066e-10, 2.318133463e-09, 1.7805820818e-08, 1.1459134441e-07, 6.1788307448e-07, 2.7914163638e-06, 1.0565966477e-05, 3.350870611e-05, 8.9037050202e-05, 0.00019822047034, 0.0003697356151, 0.00057782873046, 0.00075660849689, 0.00083005789202, 0.00076297478518, 0.00058759358944, 0.00037914779386, 0.00020497698279, 9.2846494226e-05, 3.5236353142e-05, 1.1204226212e-05, 2.9849554721e-06, 6.6628138029e-07, 1.2460689902e-07, 1.9525044692e-08, 2.5633424272e-09, 2.8195892843e-10 , 1.095460233e-16, 2.7519297893e-15, 5.7921556277e-14, 1.0214378171e-12, 1.5091877401e-11, 1.8682792813e-10, 1.9377746074e-09, 1.6839507566e-08, 1.2260896654e-07, 7.4796309946e-07, 3.8229750317e-06, 1.6371477614e-05, 5.8740668464e-05, 0.0001765856432, 0.00044477044139, 0.0009386020829, 0.0016595561756, 0.0024584820494, 0.0030514537357, 0.0031733019277, 0.0027649090625, 0.0020184358582, 0.0012345653959, 0.00063267099904, 0.00027164779021, 9.7723459476e-05, 2.9454919058e-05, 7.4384192885e-06, 1.5738672801e-06, 2.7901003818e-07, 4.1441644782e-08, 5.1572670579e-09, 5.3773124842e-10 , 9.8804434534e-16, 2.3527920673e-14, 4.6941324525e-13, 7.8467553635e-12, 1.0989836124e-10, 1.2896057555e-09, 1.2679044481e-08, 1.0444352938e-07, 7.2084458225e-07, 4.1683711061e-06, 2.019553358e-05, 8.1980273535e-05, 0.00027882237919, 0.00079453276703, 0.001896965201, 0.003794657765, 0.0063599031419, 0.008930850774, 0.010507524945, 0.010357918218, 0.0085547864437, 0.0059198532254, 0.0034322375432, 0.0016672802158, 0.00067858537659, 0.00023140088888, 6.6113607318e-05, 1.5826370145e-05, 3.1742156352e-06, 5.3340340855e-07, 7.5100118124e-08, 8.8591178837e-09, 8.7559853812e-10 , 7.608763981e-15, 1.7174738194e-13, 3.2480797565e-12, 5.1467274886e-11, 6.832812649e-10, 7.6003159322e-09, 7.083196607e-08, 5.5308458968e-07, 3.6184158034e-06, 1.9834025807e-05, 9.1089328635e-05, 0.00035049993312, 0.0011299885809, 0.0030522847082, 0.0069078211673, 0.01309849415, 0.020809743553, 0.027699816972, 0.030892400071, 0.02886630781, 0.022599330172, 0.014823971316, 0.008147017099, 0.0037514341529, 0.0014473070623, 0.0004678304249, 0.00012670148863, 2.8750160709e-05, 5.4659117268e-06, 8.7066121068e-07, 1.1619869866e-07, 1.2993292664e-08, 1.217307144e-09 , 5.0027500588e-14, 1.0704127472e-12, 1.9189228331e-11, 2.8822233489e-10, 3.6271305959e-09, 3.8244081679e-08, 3.3785306641e-07, 2.5006779651e-06, 1.5507876014e-05, 8.0577163317e-05, 0.00035078066867, 0.0012794549111, 0.0039100181311, 0.010011450388, 0.0214773193, 0.038603615016, 0.058135427535, 0.073353089392, 0.077546231449, 0.068685941398, 0.050972923636, 0.031693950295, 0.01651116088, 0.0072068208829, 0.0026355711743, 0.00080755102681, 0.00020731508266, 4.4591928599e-05, 8.0361169239e-06, 1.2133914424e-06, 1.5350433102e-07, 1.6270654157e-08, 1.4449539343e-09 , 2.8084102426e-13, 5.6960144067e-12, 9.6793142945e-11, 1.3781037422e-09, 1.643935299e-08, 1.6430584537e-07, 1.3758943851e-06, 9.6534622571e-06, 5.6747263443e-05, 0.00027949328069, 0.0011533558136, 0.0039876704104, 0.011551556177, 0.028036680073, 0.057013448328, 0.097138792276, 0.13866689801, 0.16585089266, 0.16619867086, 0.13954110444, 0.098161540926, 0.057855609804, 0.028570273891, 0.011820831336, 0.004097759258, 0.001190170995, 0.00028962624492, 5.9051388234e-05, 1.008759773e-05, 1.4438101061e-06, 1.7313968215e-07, 1.739596911e-08, 1.4644174762e-09 , 1.3460855694e-12, 2.5879026352e-11, 4.1685932484e-10, 5.6259481518e-09, 6.3615914314e-08, 6.0270014046e-07, 4.7840999287e-06, 3.1817395211e-05, 0.00017729403044, 0.00082773028407, 0.0032377834432, 0.010611373931, 0.029138045385, 0.067036889493, 0.1292206496, 0.20869635046, 0.28239867091, 0.32016593218, 0.30412513018, 0.24204400182, 0.16139911115, 0.090172208846, 0.042209394276, 0.016554273665, 0.0054397163913, 0.0014976364328, 0.00034546368988, 6.6766995587e-05, 1.0811519132e-05, 1.4668210042e-06, 1.6673658365e-07, 1.5879987103e-08, 1.2671688143e-09 , 5.5085862087e-12, 1.0038819775e-10, 1.5328210923e-09, 1.9609428747e-08, 2.1018544771e-07, 1.8875837213e-06, 1.4202775674e-05, 8.9537563326e-05, 0.00047293581883, 0.0020929765888, 0.0077605196275, 0.024109153077, 0.062753520906, 0.13685443997, 0.2500602603, 0.38282033801, 0.49103248119, 0.52770376205, 0.47515454888, 0.35846304893, 0.22657848895, 0.11999350041, 0.053242884576, 0.01979384385, 0.0061654318124, 0.001609020168, 0.00035182325519, 6.4454354288e-05, 9.893373317e-06, 1.2723365899e-06, 1.3709583868e-07, 1.2376879965e-08, 9.3618546249e-10 , 1.9247109115e-11, 3.3248934161e-10, 4.8123030005e-09, 5.8357095156e-08, 5.9292398191e-07, 5.0474250202e-06, 3.6000030377e-05, 0.00021513119282, 0.0010771300877, 0.0045185321942, 0.015881497413, 0.046768136322, 0.11539142579, 0.238540411, 0.41315698624, 0.59956043959, 0.72898006439, 0.74261391163, 0.63383364677, 0.45326510072, 0.27157768607, 0.13633288443, 0.057341847569, 0.020207272843, 0.0059663485736, 0.0014759581536, 0.00030591737595, 5.3125091654e-05, 7.7296444942e-06, 9.422894891e-07, 9.6244136216e-08, 8.2362294762e-09, 5.9053600898e-10 , 5.7418087646e-11, 9.402172374e-10, 1.289943441e-08, 1.4827892869e-07, 1.4280783489e-06, 1.1523639841e-05, 7.7909782704e-05, 0.00044132626499, 0.002094553085, 0.008328910917, 0.027749154717, 0.077459685504, 0.1811619699, 0.35499551892, 0.5828319788, 0.80173116922, 0.92401474714, 0.89226490259, 0.72189414501, 0.48934811354, 0.27792471647, 0.13225163519, 0.05272782594, 0.017613414675, 0.0049295988865, 0.0011559650302, 0.00022711334168, 3.7385692849e-05, 5.1562419685e-06, 5.9583419443e-07, 5.7687557842e-08, 4.6795438635e-09, 3.1804633926e-10 , 1.4624795819e-10, 2.270055699e-09, 2.9522142952e-08, 3.2167989161e-07, 2.9367281513e-06, 2.246306758e-05, 0.00014395892504, 0.00077298877295, 0.0034775424283, 0.013108015992, 0.041396718472, 0.10953669995, 0.24283881485, 0.45106744766, 0.70198774338, 0.91533988714, 1, 0.91533988714, 0.70198774338, 0.45106744766, 0.24283881485, 0.10953669995, 0.041396718472, 0.013108015992, 0.0034775424283, 0.00077298877295, 0.00014395892504, 2.246306758e-05, 2.9367281513e-06, 3.2167989161e-07, 2.9522142952e-08, 2.270055699e-09, 1.4624795819e-10 , 3.1804633926e-10, 4.6795438635e-09, 5.7687557842e-08, 5.9583419443e-07, 5.1562419685e-06, 3.7385692849e-05, 0.00022711334168, 0.0011559650302, 0.0049295988865, 0.017613414675, 0.05272782594, 0.13225163519, 0.27792471647, 0.48934811354, 0.72189414501, 0.89226490259, 0.92401474714, 0.80173116922, 0.5828319788, 0.35499551892, 0.1811619699, 0.077459685504, 0.027749154717, 0.008328910917, 0.002094553085, 0.00044132626499, 7.7909782704e-05, 1.1523639841e-05, 1.4280783489e-06, 1.4827892869e-07, 1.289943441e-08, 9.402172374e-10, 5.7418087646e-11 , 5.9053600898e-10, 8.2362294762e-09, 9.6244136216e-08, 9.422894891e-07, 7.7296444942e-06, 5.3125091654e-05, 0.00030591737595, 0.0014759581536, 0.0059663485736, 0.020207272843, 0.057341847569, 0.13633288443, 0.27157768607, 0.45326510072, 0.63383364677, 0.74261391163, 0.72898006439, 0.59956043959, 0.41315698624, 0.238540411, 0.11539142579, 0.046768136322, 0.015881497413, 0.0045185321942, 0.0010771300877, 0.00021513119282, 3.6000030377e-05, 5.0474250202e-06, 5.9292398191e-07, 5.8357095156e-08, 4.8123030005e-09, 3.3248934161e-10, 1.9247109115e-11 , 9.3618546249e-10, 1.2376879965e-08, 1.3709583868e-07, 1.2723365899e-06, 9.893373317e-06, 6.4454354288e-05, 0.00035182325519, 0.001609020168, 0.0061654318124, 0.01979384385, 0.053242884576, 0.11999350041, 0.22657848895, 0.35846304893, 0.47515454888, 0.52770376205, 0.49103248119, 0.38282033801, 0.2500602603, 0.13685443997, 0.062753520906, 0.024109153077, 0.0077605196275, 0.0020929765888, 0.00047293581883, 8.9537563326e-05, 1.4202775674e-05, 1.8875837213e-06, 2.1018544771e-07, 1.9609428747e-08, 1.5328210923e-09, 1.0038819775e-10, 5.5085862087e-12 , 1.2671688143e-09, 1.5879987103e-08, 1.6673658365e-07, 1.4668210042e-06, 1.0811519132e-05, 6.6766995587e-05, 0.00034546368988, 0.0014976364328, 0.0054397163913, 0.016554273665, 0.042209394276, 0.090172208846, 0.16139911115, 0.24204400182, 0.30412513018, 0.32016593218, 0.28239867091, 0.20869635046, 0.1292206496, 0.067036889493, 0.029138045385, 0.010611373931, 0.0032377834432, 0.00082773028407, 0.00017729403044, 3.1817395211e-05, 4.7840999287e-06, 6.0270014046e-07, 6.3615914314e-08, 5.6259481518e-09, 4.1685932484e-10, 2.5879026352e-11, 1.3460855694e-12 , 1.4644174762e-09, 1.739596911e-08, 1.7313968215e-07, 1.4438101061e-06, 1.008759773e-05, 5.9051388234e-05, 0.00028962624492, 0.001190170995, 0.004097759258, 0.011820831336, 0.028570273891, 0.057855609804, 0.098161540926, 0.13954110444, 0.16619867086, 0.16585089266, 0.13866689801, 0.097138792276, 0.057013448328, 0.028036680073, 0.011551556177, 0.0039876704104, 0.0011533558136, 0.00027949328069, 5.6747263443e-05, 9.6534622571e-06, 1.3758943851e-06, 1.6430584537e-07, 1.643935299e-08, 1.3781037422e-09, 9.6793142945e-11, 5.6960144067e-12, 2.8084102426e-13 , 1.4449539343e-09, 1.6270654157e-08, 1.5350433102e-07, 1.2133914424e-06, 8.0361169239e-06, 4.4591928599e-05, 0.00020731508266, 0.00080755102681, 0.0026355711743, 0.0072068208829, 0.01651116088, 0.031693950295, 0.050972923636, 0.068685941398, 0.077546231449, 0.073353089392, 0.058135427535, 0.038603615016, 0.0214773193, 0.010011450388, 0.0039100181311, 0.0012794549111, 0.00035078066867, 8.0577163317e-05, 1.5507876014e-05, 2.5006779651e-06, 3.3785306641e-07, 3.8244081679e-08, 3.6271305959e-09, 2.8822233489e-10, 1.9189228331e-11, 1.0704127472e-12, 5.0027500588e-14 , 1.217307144e-09, 1.2993292664e-08, 1.1619869866e-07, 8.7066121068e-07, 5.4659117268e-06, 2.8750160709e-05, 0.00012670148863, 0.0004678304249, 0.0014473070623, 0.0037514341529, 0.008147017099, 0.014823971316, 0.022599330172, 0.02886630781, 0.030892400071, 0.027699816972, 0.020809743553, 0.01309849415, 0.0069078211673, 0.0030522847082, 0.0011299885809, 0.00035049993312, 9.1089328635e-05, 1.9834025807e-05, 3.6184158034e-06, 5.5308458968e-07, 7.083196607e-08, 7.6003159322e-09, 6.832812649e-10, 5.1467274886e-11, 3.2480797565e-12, 1.7174738194e-13, 7.608763981e-15 , 8.7559853812e-10, 8.8591178837e-09, 7.5100118124e-08, 5.3340340855e-07, 3.1742156352e-06, 1.5826370145e-05, 6.6113607318e-05, 0.00023140088888, 0.00067858537659, 0.0016672802158, 0.0034322375432, 0.0059198532254, 0.0085547864437, 0.010357918218, 0.010507524945, 0.008930850774, 0.0063599031419, 0.003794657765, 0.001896965201, 0.00079453276703, 0.00027882237919, 8.1980273535e-05, 2.019553358e-05, 4.1683711061e-06, 7.2084458225e-07, 1.0444352938e-07, 1.2679044481e-08, 1.2896057555e-09, 1.0989836124e-10, 7.8467553635e-12, 4.6941324525e-13, 2.3527920673e-14, 9.8804434534e-16 , 5.3773124842e-10, 5.1572670579e-09, 4.1441644782e-08, 2.7901003818e-07, 1.5738672801e-06, 7.4384192885e-06, 2.9454919058e-05, 9.7723459476e-05, 0.00027164779021, 0.00063267099904, 0.0012345653959, 0.0020184358582, 0.0027649090625, 0.0031733019277, 0.0030514537357, 0.0024584820494, 0.0016595561756, 0.0009386020829, 0.00044477044139, 0.0001765856432, 5.8740668464e-05, 1.6371477614e-05, 3.8229750317e-06, 7.4796309946e-07, 1.2260896654e-07, 1.6839507566e-08, 1.9377746074e-09, 1.8682792813e-10, 1.5091877401e-11, 1.0214378171e-12, 5.7921556277e-14, 2.7519297893e-15, 1.095460233e-16 , 2.8195892843e-10, 2.5633424272e-09, 1.9525044692e-08, 1.2460689902e-07, 6.6628138029e-07, 2.9849554721e-06, 1.1204226212e-05, 3.5236353142e-05, 9.2846494226e-05, 0.00020497698279, 0.00037914779386, 0.00058759358944, 0.00076297478518, 0.00083005789202, 0.00075660849689, 0.00057782873046, 0.0003697356151, 0.00019822047034, 8.9037050202e-05, 3.350870611e-05, 1.0565966477e-05, 2.7914163638e-06, 6.1788307448e-07, 1.1459134441e-07, 1.7805820818e-08, 2.318133463e-09, 2.528582066e-10, 2.3109132663e-11, 1.7695107532e-12, 1.1352415319e-13, 6.1021867118e-15, 2.7482042207e-16, 1.0370020887e-17 , 1.2623001255e-10, 1.0878081769e-09, 7.854269235e-09, 4.7514166823e-08, 2.4082694949e-07, 1.0227099665e-06, 3.6388605622e-06, 1.08477916e-05, 2.7094622055e-05, 5.6700846471e-05, 9.9417185993e-05, 0.00014604839089, 0.00017976220988, 0.00018538023869, 0.00016017470625, 0.00011595467367, 7.0331341703e-05, 3.5741581087e-05, 1.521817012e-05, 5.428964414e-06, 1.6226908883e-06, 4.0636817289e-07, 8.5264652228e-08, 1.4989314678e-08, 2.2077970563e-09, 2.7246005452e-10, 2.81714721e-11, 2.4405256462e-12, 1.7714145038e-13, 1.0772607375e-14, 5.488961963e-16, 2.3432582496e-17, 8.3813806733e-19 , 4.8250181628e-11, 3.9414410624e-10, 2.697583712e-09, 1.5468991421e-08, 7.4320794852e-08, 2.9917509892e-07, 1.0090335536e-06, 2.8513409234e-06, 6.7508458415e-06, 1.3391580978e-05, 2.2257245291e-05, 3.0993738619e-05, 3.6161200114e-05, 3.534886855e-05, 2.8951673812e-05, 1.9867155061e-05, 1.1422562238e-05, 5.50244431e-06, 2.220812803e-06, 7.5099006835e-07, 2.1277482176e-07, 5.0509349592e-08, 1.0045878795e-08, 1.6740544506e-09, 2.3373031288e-10, 2.7341625491e-11, 2.6797847795e-12, 2.2005867181e-13, 1.5140580101e-14, 8.727979425e-16, 4.2154884256e-17, 1.7058633512e-18, 5.7837483018e-20 , 1.5746770207e-11, 1.2193142329e-10, 7.9105094697e-10, 4.299887113e-09, 1.9582705235e-08, 7.4723331522e-08, 2.3889310796e-07, 6.3990404442e-07, 1.4361226022e-06, 2.7004296044e-06, 4.2544133976e-06, 5.6157773543e-06, 6.2107837948e-06, 5.7550132624e-06, 4.4679895836e-06, 2.9063089642e-06, 1.5839317484e-06, 7.2326224654e-07, 2.7670731129e-07, 8.8697063916e-08, 2.3821185025e-08, 5.3602038363e-09, 1.0105679626e-09, 1.5962965672e-10, 2.1126518937e-11, 2.3426341162e-12, 2.1764347594e-13, 1.6941678773e-14, 1.1049162573e-15, 6.03762504e-17, 2.7641931753e-18, 1.0603099006e-19, 3.4077236103e-21 , 4.3877583858e-12, 3.2205800526e-11, 1.980565989e-10, 1.0204967982e-09, 4.4054928594e-09, 1.5934691788e-08, 4.8290122123e-08, 1.2261305926e-07, 2.6084489946e-07, 4.6493357786e-07, 6.9432843475e-07, 8.6876599426e-07, 9.1076549324e-07, 7.9997084868e-07, 5.8871842157e-07, 3.6299820749e-07, 1.8752831465e-07, 8.1169602595e-08, 2.9436453275e-08, 8.9442311335e-09, 2.2770070274e-09, 4.8567921906e-10, 8.6796098087e-11, 1.2996222154e-11, 1.6304078313e-12, 1.7137238351e-13, 1.5092172168e-14, 1.1135950628e-15, 6.8844614491e-17, 3.5659326073e-18, 1.5475455831e-19, 5.6270084002e-21, 1.7142488945e-22 , 1.043881019e-12, 7.2629064221e-12, 4.2338410555e-11, 2.0678624091e-10, 8.4619750096e-10, 2.9012920955e-09, 8.334380297e-09, 2.0059411909e-08, 4.045116242e-08, 6.8345009652e-08, 9.6749545264e-08, 1.1475035677e-07, 1.1403165701e-07, 9.4942500084e-08, 6.623101001e-08, 3.8710243899e-08, 1.895640267e-08, 7.7776736163e-09, 2.6736826086e-09, 7.7007639154e-10, 1.8583280748e-10, 3.757297945e-11, 6.3649528356e-12, 9.0339850401e-13, 1.0743044909e-13, 1.0703829994e-14, 8.9354691452e-16, 6.2496777531e-17, 3.6623942745e-18, 1.7982067704e-19, 7.3972968879e-21, 2.5496188929e-22, 7.362798549e-24 , 2.1204007517e-13, 1.3984425597e-12, 7.7274167967e-12, 3.5776017565e-11, 1.3877438088e-10, 4.5101875012e-10, 1.2281282658e-09, 2.8019366827e-09, 5.3559525703e-09, 8.5778921743e-09, 1.151038731e-08, 1.2940859051e-08, 1.2189945942e-08, 9.6206465017e-09, 6.3616734103e-09, 3.5245522056e-09, 1.6360705013e-09, 6.3630173353e-10, 2.0734351736e-10, 5.6608499138e-11, 1.2949036808e-11, 2.481758939e-12, 3.9851734651e-13, 5.3616318199e-14, 6.0438070835e-15, 5.7081185033e-16, 4.5168693657e-17, 2.9946487959e-18, 1.6634945915e-19, 7.7421342681e-21, 3.0190166275e-22, 9.8635317948e-24, 2.7000172105e-25 }; Double kernelData[] = { 8.8039155317e-17, 6.24999142e-16, 4.0724755416e-15, 2.435668312e-14, 1.3370776925e-13, 6.7371672477e-13, 3.115852628e-12, 1.3226817211e-11, 5.1536528517e-11, 1.8431105253e-10, 6.0502119981e-10, 1.8229211474e-09, 5.0413362374e-09, 1.2796853355e-08, 2.981532532e-08, 6.3761078195e-08, 1.2515641856e-07, 2.2549069456e-07, 3.7289299826e-07, 5.6600407561e-07, 7.8855822494e-07, 1.0083938378e-06, 1.1835978739e-06, 1.275140221e-06, 1.2609335727e-06, 1.144472435e-06, 9.5345058071e-07, 7.2907391768e-07, 5.1171087989e-07, 3.2965331798e-07, 1.9492570402e-07, 1.0579386611e-07, 5.270264225e-08, 2.4098120832e-08, 1.0113783588e-08, 3.896059031e-09, 1.3775781627e-09, 4.4708234337e-10, 1.3317907888e-10, 3.6413785182e-11, 9.1384920464e-12, 2.1050526773e-12, 4.4507285227e-13, 8.6373352318e-14, 1.5385377705e-14 , 3.328093829e-16, 2.350064113e-15, 1.523155991e-14, 9.0612467616e-14, 4.947798898e-13, 2.4797906784e-12, 1.1407623977e-11, 4.8168070227e-11, 1.8668117052e-10, 6.6408289978e-10, 2.1683261853e-09, 6.4983658454e-09, 1.7875782632e-08, 4.5134363802e-08, 1.0459863375e-07, 2.2249817277e-07, 4.3441593789e-07, 7.7851177593e-07, 1.2805753613e-06, 1.9334083845e-06, 2.6793047709e-06, 3.4080062505e-06, 3.9788533286e-06, 4.2637839215e-06, 4.1938587856e-06, 3.7862564568e-06, 3.1375191156e-06, 2.3864001832e-06, 1.6660155779e-06, 1.0675686326e-06, 6.2790070388e-07, 3.3897390495e-07, 1.6796612101e-07, 7.6393284587e-08, 3.1891108421e-08, 1.2219812717e-08, 4.2977306158e-09, 1.3873687754e-09, 4.1107886539e-10, 1.1179883408e-10, 2.7908076891e-11, 6.3944366295e-12, 1.3447870205e-12, 2.5958938775e-13, 4.5993617985e-14 , 1.1891068854e-15, 8.352003205e-15, 5.3844241213e-14, 3.1861416717e-13, 1.7304994243e-12, 8.626971533e-12, 3.9475256397e-11, 1.6579430884e-10, 6.3913946358e-10, 2.2615160855e-09, 7.3449011317e-09, 2.1895267821e-08, 5.9909304184e-08, 1.5045921486e-07, 3.4683378658e-07, 7.338467185e-07, 1.4251776292e-06, 2.5404576718e-06, 4.1565654101e-06, 6.2421840994e-06, 8.6043446572e-06, 1.0886302334e-05, 1.264216462e-05, 1.347541729e-05, 1.3183888768e-05, 1.1839236322e-05, 9.7585025287e-06, 7.3828559835e-06, 5.1267684285e-06, 3.2677069157e-06, 1.9117092052e-06, 1.0265503079e-06, 5.0596344181e-07, 2.2889534534e-07, 9.5046139847e-08, 3.6225348055e-08, 1.2672709993e-08, 4.0691880976e-09, 1.1992874471e-09, 3.2442937226e-10, 8.055563161e-11, 1.8359098963e-11, 3.8404891246e-12, 7.3739988725e-13, 1.2995638907e-13 , 4.0156900293e-15, 2.8055017009e-14, 1.7990425501e-13, 1.0588928822e-12, 5.7206205918e-12, 2.8366982374e-11, 1.2911015312e-10, 5.3937682098e-10, 2.0682342505e-09, 7.2792682992e-09, 2.3515655201e-08, 6.9727697394e-08, 1.8977213756e-07, 4.7406828685e-07, 1.0869947573e-06, 2.2876715775e-06, 4.4191710913e-06, 7.8355033111e-06, 1.2751857867e-05, 1.9048418835e-05, 2.611702439e-05, 3.2867672417e-05, 3.7965884985e-05, 4.0252998588e-05, 3.9172660763e-05, 3.4990225686e-05, 2.8687303711e-05, 2.1588042728e-05, 1.491131934e-05, 9.4536562756e-06, 5.5012528719e-06, 2.9383529636e-06, 1.4405422917e-06, 6.4822671675e-07, 2.6773673767e-07, 1.0150081664e-07, 3.5319160929e-08, 1.1280546275e-08, 3.3069711414e-09, 8.8983687085e-10, 2.1977125675e-10, 4.9820658016e-11, 1.0366414324e-11, 1.9798236401e-12, 3.4706037957e-13 , 1.2817577844e-14, 8.907214159e-14, 5.6814069508e-13, 3.3262093167e-12, 1.7874084157e-11, 8.8160916067e-11, 3.9912345651e-10, 1.6585227636e-09, 6.3257736826e-09, 2.2145462353e-08, 7.1160208392e-08, 2.0987857852e-07, 5.6817276572e-07, 1.4117933915e-06, 3.2198943245e-06, 6.7404948823e-06, 1.2951546523e-05, 2.2841846658e-05, 3.6976151023e-05, 5.494020661e-05, 7.4927105743e-05, 9.3792332336e-05, 0.00010776443378, 0.00011364837701, 0.00011000985251, 9.7741452919e-05, 7.9708617704e-05, 5.9663994762e-05, 4.0991955757e-05, 2.5850269594e-05, 1.496274399e-05, 7.9494429883e-06, 3.876521987e-06, 1.7351086399e-06, 7.1283864145e-07, 2.6880385917e-07, 9.3037982651e-08, 2.9557300607e-08, 8.6188105541e-09, 2.3068069677e-09, 5.6670107496e-10, 1.277840056e-10, 2.644708931e-11, 5.0241269478e-12, 8.7603486748e-13 , 3.8668802318e-14, 2.6728665396e-13, 1.6958053885e-12, 9.8753947728e-12, 5.2785154719e-11, 2.5896915168e-10, 1.1661754895e-09, 4.8201478364e-09, 1.8286749892e-08, 6.3678193385e-08, 2.035289981e-07, 5.970924235e-07, 1.6078166709e-06, 3.9738401938e-06, 9.0149624157e-06, 1.8771475879e-05, 3.5876644688e-05, 6.2936836912e-05, 0.00010133925389, 0.00014977184765, 0.00020317111921, 0.00025297355023, 0.00028911212576, 0.00030327570857, 0.00029200417339, 0.00025805921177, 0.00020932899497, 0.00015585501387, 0.00010650995682, 6.6809734562e-05, 3.8465288526e-05, 2.0327199309e-05, 9.8597847682e-06, 4.3897057367e-06, 1.7938390329e-06, 6.7283912131e-07, 2.3164275831e-07, 7.319898998e-08, 2.123113596e-08, 5.6522462266e-09, 1.3811694011e-09, 3.0977939658e-10, 6.3773146486e-11, 1.2050461323e-11, 2.0900180458e-12 , 1.1026142971e-13, 7.5810158932e-13, 4.784180864e-12, 2.7712030587e-11, 1.4733596287e-10, 7.189986384e-10, 3.2205280664e-09, 1.3240619268e-08, 4.9965095172e-08, 1.7306358302e-07, 5.5020529999e-07, 1.6055474816e-06, 4.3003283281e-06, 1.0572054634e-05, 2.385591506e-05, 4.9409864005e-05, 9.393144137e-05, 0.00016390309611, 0.00026250857627, 0.00038590381155, 0.00052070937818, 0.0006448974018, 0.00073310424341, 0.0007649276522, 0.00073258008342, 0.0006439757999, 0.00051959301345, 0.00038480130024, 0.00026157143293, 0.00016320105351, 9.3462054792e-05, 4.9127993407e-05, 2.3702839826e-05, 1.0496687537e-05, 4.2666188165e-06, 1.5918212739e-06, 5.4511355074e-07, 1.7133940844e-07, 4.9431893245e-08, 1.3089956674e-08, 3.1816118629e-09, 7.0980116229e-10, 1.4534723425e-10, 2.7318374993e-11, 4.7128490346e-12 , 2.9716577506e-13, 2.0322745223e-12, 1.2756989042e-11, 7.3500788789e-11, 3.8870237584e-10, 1.8867691853e-09, 8.4062392602e-09, 3.4376846259e-08, 1.2903525715e-07, 4.4456007231e-07, 1.4058281295e-06, 4.0805107346e-06, 1.0871155609e-05, 2.6583769795e-05, 5.9667519963e-05, 0.00012292456813, 0.00023244418844, 0.00040343948058, 0.00064271502197, 0.00093980587553, 0.001261355821, 0.0015538751613, 0.0017570104683, 0.0018235300668, 0.0017371228896, 0.0015188977122, 0.0012190069538, 0.00089797232067, 0.00060715473955, 0.00037680458627, 0.00021464079327, 0.00011222464673, 5.3857223975e-05, 2.3723554477e-05, 9.5916657301e-06, 3.5594987366e-06, 1.212449888e-06, 3.7906841044e-07, 1.0878051171e-07, 2.865272819e-08, 6.9272054759e-09, 1.5372069173e-09, 3.1309996262e-10, 5.853514895e-11, 1.0044503423e-11 , 7.5697168802e-13, 5.1493076296e-12, 3.2151115104e-11, 1.842572761e-10, 9.6923991055e-10, 4.6797046238e-09, 2.0738880835e-08, 8.4359257357e-08, 3.1496202268e-07, 1.0793536376e-06, 3.3950693705e-06, 9.8019854704e-06, 2.5975239623e-05, 6.3180632424e-05, 0.00014105487207, 0.00028904981446, 0.00054367107805, 0.00093859760091, 0.0014873143518, 0.0021632455755, 0.0028879400343, 0.003538754303, 0.0039800852537, 0.0041087968275, 0.0038932813331, 0.0033860772382, 0.0027030708734, 0.001980607165, 0.0013320432045, 0.00082227750681, 0.00046590543934, 0.00024230216513, 0.00011566354806, 5.0677477702e-05, 2.0380462956e-05, 7.5230082075e-06, 2.5488859592e-06, 7.9266067132e-07, 2.2625798124e-07, 5.9279138043e-08, 1.4255378211e-08, 3.1465547945e-09, 6.3748611945e-10, 1.1854593696e-10, 2.0234067893e-11 , 1.8225184816e-12, 1.2331712908e-11, 7.6586910425e-11, 4.365816042e-10, 2.28432806e-09, 1.0970538256e-08, 4.8358881344e-08, 1.9566212472e-07, 7.2663470974e-07, 2.4768862659e-06, 7.749521501e-06, 2.2254784199e-05, 5.8661400544e-05, 0.00014192533854, 0.00031517163734, 0.00064241443761, 0.0012018833077, 0.0020639018621, 0.0032530871686, 0.0047063310631, 0.0062495437451, 0.0076171704568, 0.0085215643048, 0.0087503362447, 0.0082472506911, 0.0071346648037, 0.0056652366184, 0.0041289748624, 0.0027621404734, 0.0016960129142, 0.00095585512463, 0.0004944644752, 0.00023477789364, 0.00010231978376, 4.0930000978e-05, 1.5028054804e-05, 5.0645999181e-06, 1.5666269064e-06, 4.4480179895e-07, 1.1591701821e-07, 2.7727246277e-08, 6.0876041985e-09, 1.2267750149e-09, 2.2691627743e-10, 3.8525085899e-11 , 4.147366478e-12, 2.7913081568e-11, 1.724333315e-10, 9.7772345775e-10, 5.0884989555e-09, 2.4307746926e-08, 1.0658071403e-07, 4.2893574914e-07, 1.5844726704e-06, 5.3722642406e-06, 1.671897553e-05, 4.775751222e-05, 0.00012521426834, 0.00030133200926, 0.00066560471896, 0.0013494824525, 0.0025112964213, 0.0042895134538, 0.0067250914872, 0.0096776094288, 0.012782554142, 0.01549696736, 0.017244694754, 0.017613448203, 0.016512474045, 0.014208900742, 0.011222465895, 0.0081357164308, 0.0054135583341, 0.0033063553274, 0.0018535151612, 0.00095372350188, 0.00045043081627, 0.00019526015967, 7.7692537161e-05, 2.8374211979e-05, 9.5114974101e-06, 2.9265318062e-06, 8.2648944044e-07, 2.1424045826e-07, 5.0973508081e-08, 1.1131890965e-08, 2.2313733083e-09, 4.1053899169e-10, 6.9329347818e-11 , 8.9203488346e-12, 5.9717210343e-11, 3.6694292049e-10, 2.0695563041e-09, 1.0713585574e-08, 5.0906372451e-08, 2.2201849958e-07, 8.8876282689e-07, 3.2655916584e-06, 1.1013303265e-05, 3.4092074202e-05, 9.6865464002e-05, 0.00025261819246, 0.00060469890013, 0.0013285999885, 0.0026793458965, 0.0049595544115, 0.0084262900054, 0.013140439987, 0.018808895722, 0.024711325765, 0.029799463227, 0.032983828336, 0.033509895205, 0.031248167157, 0.026745835319, 0.021011995152, 0.015151586384, 0.010028325021, 0.0060922689736, 0.0033971052617, 0.00173867587, 0.00081678503193, 0.00035219002166, 0.00013938812481, 5.0635400839e-05, 1.6883514036e-05, 5.1671408983e-06, 1.451502726e-06, 3.7425289179e-07, 8.8571120216e-08, 1.9239760007e-08, 3.8360585819e-09, 7.0202432756e-10, 1.1792239407e-10 , 1.8134277066e-11, 1.207547673e-10, 7.3804967693e-10, 4.1404333295e-09, 2.1320005317e-08, 1.0076472989e-07, 4.3712677211e-07, 1.7405578774e-06, 6.3613310886e-06, 2.1339646992e-05, 6.5706233727e-05, 0.0001856971503, 0.00048170905211, 0.0011469457531, 0.0025065781083, 0.005028042011, 0.0092575587332, 0.01564492844, 0.024267796427, 0.034551527351, 0.045152693987, 0.054160099477, 0.0596287027, 0.060257468373, 0.055891502649, 0.04758399725, 0.037183944136, 0.026670416817, 0.017558347434, 0.010610048659, 0.0058847879991, 0.002995877061, 0.0013998987852, 0.00060041208053, 0.00023636419792, 8.5406958533e-05, 2.8326006941e-05, 8.6229674707e-06, 2.4093940283e-06, 6.1792906081e-07, 1.4546171201e-07, 3.1429575387e-08, 6.2331562134e-09, 1.1346398265e-09, 1.8957757586e-10 , 3.4843967989e-11, 2.307875141e-10, 1.4030641093e-09, 7.8293211914e-09, 4.0100402998e-08, 1.8851793016e-07, 8.1346018987e-07, 3.2218140404e-06, 1.1712335436e-05, 3.9081052819e-05, 0.00011969310435, 0.00033647398232, 0.00086818839191, 0.0020561555866, 0.0044696838595, 0.0089182248339, 0.016332749277, 0.027454914525, 0.042360432446, 0.059990212321, 0.077979497612, 0.093037866056, 0.10188701749, 0.10241366178, 0.094487927854, 0.080015599728, 0.062194619328, 0.044372159988, 0.029056802392, 0.017464857548, 0.0096352240071, 0.0048790862784, 0.0022677462548, 0.0009674540488, 0.00037883155164, 0.0001361573959, 4.4917629566e-05, 1.3601002138e-05, 3.7801230519e-06, 9.6431790553e-07, 2.2579497738e-07, 4.8527599716e-08, 9.5728536209e-09, 1.7333032787e-09, 2.8806185215e-10 , 6.3279874396e-11, 4.1690303987e-10, 2.5210613597e-09, 1.3993019188e-08, 7.1288582149e-08, 3.3335504668e-07, 1.4307829588e-06, 5.6366438912e-06, 2.0382056391e-05, 6.76478885e-05, 0.00020608208433, 0.0005762433284, 0.0014789445559, 0.0034839923028, 0.0075332396664, 0.014950877056, 0.027235239744, 0.045538153499, 0.069887422025, 0.098447062075, 0.12728753686, 0.15105977654, 0.1645475626, 0.16451813281, 0.15097874403, 0.12717378139, 0.098323836923, 0.069775037467, 0.045448645949, 0.027172008529, 0.014910830185, 0.0075103780255, 0.003472175682, 0.0014734007418, 0.00057387852576, 0.00020516278164, 6.7322107498e-05, 2.0276607756e-05, 5.6054877859e-06, 1.4223643348e-06, 3.3127508914e-07, 7.0818316544e-08, 1.3895754769e-08, 2.5026400952e-09, 4.1370917803e-10 , 1.0862049454e-10, 7.1181038841e-10, 4.281498267e-09, 2.3637927171e-08, 1.1978416126e-07, 5.5714878044e-07, 2.3785978556e-06, 9.3207609098e-06, 3.3524433093e-05, 0.00011067543528, 0.00033536730916, 0.0009327600128, 0.0023812172003, 0.0055796611123, 0.012000416405, 0.023689972237, 0.04292518273, 0.071390412748, 0.10898005217, 0.15269824862, 0.19638155401, 0.23181794584, 0.25117307901, 0.24979224801, 0.22801549733, 0.19104231894, 0.14691780508, 0.1037048623, 0.067189827561, 0.039956513792, 0.021809764206, 0.01092684269, 0.0050247875042, 0.0021209029946, 0.00082168012159, 0.00029218997224, 9.5369163319e-05, 2.8571212169e-05, 7.8565217336e-06, 1.9829476514e-06, 4.5937977688e-07, 9.7681478906e-08, 1.9064781753e-08, 3.4153313511e-09, 5.6157928308e-10 , 1.7622441006e-10, 1.1486892548e-09, 6.8725500846e-09, 3.7740893077e-08, 1.9023401876e-07, 8.8012069455e-07, 3.7374588828e-06, 1.4567676772e-05, 5.211748794e-05, 0.00017114206275, 0.00051583402092, 0.0014270629035, 0.0036237256136, 0.0084459288046, 0.01806839928, 0.035478983074, 0.063944384456, 0.10578241199, 0.16062165797, 0.22385896742, 0.28636804223, 0.33624389768, 0.36237969995, 0.35847020149, 0.32547831535, 0.27125060558, 0.20749089122, 0.1456823796, 0.093884699047, 0.055534418672, 0.030151527375, 0.015025748871, 0.0068729482591, 0.0028855544515, 0.0011119749397, 0.00039331510197, 0.00012769291061, 3.8051468437e-05, 1.0407733498e-05, 2.6128918762e-06, 6.0209578123e-07, 1.2734722077e-07, 2.4722488945e-08, 4.4053081183e-09, 7.2050876376e-10 , 2.7022842297e-10, 1.7520631612e-09, 1.0426732366e-08, 5.6954611694e-08, 2.8555302833e-07, 1.3140876263e-06, 5.5506111494e-06, 2.151983972e-05, 7.6580137829e-05, 0.00025013388949, 0.00074990984285, 0.0020635977853, 0.0052122040652, 0.012083613314, 0.02571294643, 0.050221189857, 0.090032987297, 0.14814813435, 0.22375375032, 0.31018736959, 0.39469131827, 0.46096813679, 0.49415573478, 0.48622405529, 0.43912571669, 0.36401635408, 0.27696996927, 0.19343014061, 0.12399250269, 0.072953537107, 0.039398252964, 0.019529322162, 0.0088854003698, 0.0037106222007, 0.0014223156031, 0.00050040875794, 0.00016159747611, 4.7898589401e-05, 1.303139743e-05, 3.2541604469e-06, 7.4587671861e-07, 1.5691844624e-07, 3.0301258391e-08, 5.3706421532e-09, 8.737217061e-10 , 3.916553748e-10, 2.5258406477e-09, 1.4951623939e-08, 8.1236201765e-08, 4.0512836108e-07, 1.8544441218e-06, 7.791405551e-06, 3.0046667234e-05, 0.00010635465878, 0.00034553866135, 0.0010304251919, 0.0028204370756, 0.0070859133266, 0.016340110451, 0.034585449845, 0.067191138864, 0.11981468648, 0.19610469043, 0.29460862279, 0.40624010563, 0.51416176558, 0.59730541706, 0.63690233231, 0.62334567308, 0.55996990204, 0.46172165871, 0.34944227338, 0.24274507165, 0.15477648377, 0.090581484139, 0.04865789786, 0.023990936577, 0.010857267305, 0.0045099714771, 0.0017195164692, 0.00060175370891, 0.00019329106726, 5.6987948483e-05, 1.5421790522e-05, 3.830602509e-06, 8.7332983867e-07, 1.8275478908e-07, 3.5102640794e-08, 6.1885669922e-09, 1.0014288288e-09 , 5.3652027265e-10, 3.4416913763e-09, 2.0264648626e-08, 1.0951769269e-07, 5.4326181953e-07, 2.4735131774e-06, 1.0337095773e-05, 3.965176802e-05, 0.00013960670913, 0.00045115951798, 0.0013382370817, 0.0036434810609, 0.0091049904004, 0.020884402096, 0.043968748301, 0.084966138005, 0.15070493519, 0.24535156786, 0.36663162708, 0.50286406279, 0.63306897879, 0.73152828217, 0.77587360144, 0.7553191781, 0.67491602898, 0.55353963375, 0.41670367122, 0.28792923689, 0.18260966241, 0.10630206764, 0.056798785925, 0.027855848894, 0.012539292686, 0.0051809549332, 0.0019648340531, 0.00068394606933, 0.00021852318605, 6.408457557e-05, 1.7249973098e-05, 4.2619017222e-06, 9.6649262105e-07, 2.0117393262e-07, 3.8434940564e-08, 6.7399827941e-09, 1.0848555387e-09 , 6.9467176456e-10, 4.4324979243e-09, 2.5959549177e-08, 1.3954885958e-07, 6.8855007385e-07, 3.1183383271e-06, 1.2962556866e-05, 4.9458139983e-05, 0.00017320699408, 0.00055676558986, 0.0016427029623, 0.0044486271217, 0.011057885364, 0.025228891522, 0.052832838148, 0.10155215114, 0.17916539311, 0.29013422132, 0.43124428391, 0.58833885193, 0.73673510551, 0.84678846598, 0.8933429718, 0.86504995823, 0.76885396242, 0.62722915411, 0.46966513991, 0.32279747725, 0.20363460481, 0.11791062355, 0.062666252255, 0.030569938943, 0.013687840663, 0.0056254207157, 0.0021220440976, 0.00073474034434, 0.00023350333504, 6.8113338784e-05, 1.8236898541e-05, 4.4817606977e-06, 1.0109434925e-06, 2.0930751532e-07, 3.9776047345e-08, 6.9380612366e-09, 1.1108003406e-09 , 8.5011564455e-10, 5.3954893886e-09, 3.1431493852e-08, 1.6806482961e-07, 8.2483819597e-07, 3.7156980852e-06, 1.5363571947e-05, 5.8307341533e-05, 0.00020311125263, 0.00064941786695, 0.001905875979, 0.0051338705234, 0.012693292461, 0.028806060553, 0.060002960265, 0.11472059041, 0.20132131875, 0.32427841425, 0.47943085432, 0.65059918165, 0.81036531925, 0.92646247149, 0.97219759226, 0.93639904261, 0.82784128189, 0.67175805569, 0.50033217669, 0.34204539657, 0.21462909877, 0.12361560017, 0.065348818898, 0.031708940864, 0.014122298919, 0.0057730991393, 0.0021661657374, 0.00074602721725, 0.00023582899303, 6.8425790232e-05, 1.8223077859e-05, 4.4545577111e-06, 9.9946112186e-07, 2.058289823e-07, 3.8906843969e-08, 6.7503651557e-09, 1.0749994228e-09 , 9.8330776854e-10, 6.2076237484e-09, 3.597005005e-08, 1.9130960993e-07, 9.3392725375e-07, 4.1847333705e-06, 1.7210864826e-05, 6.4970590756e-05, 0.00022511846328, 0.00071595358895, 0.0020899628289, 0.005599798169, 0.013771627098, 0.031086960807, 0.064409606159, 0.1224906072, 0.2138132602, 0.34256762266, 0.50377625227, 0.67999958992, 0.84247964621, 0.95805370808, 1, 0.95805370808, 0.84247964621, 0.67999958992, 0.50377625227, 0.34256762266, 0.2138132602, 0.1224906072, 0.064409606159, 0.031086960807, 0.013771627098, 0.005599798169, 0.0020899628289, 0.00071595358895, 0.00022511846328, 6.4970590756e-05, 1.7210864826e-05, 4.1847333705e-06, 9.3392725375e-07, 1.9130960993e-07, 3.597005005e-08, 6.2076237484e-09, 9.8330776854e-10 , 1.0749994228e-09, 6.7503651557e-09, 3.8906843969e-08, 2.058289823e-07, 9.9946112186e-07, 4.4545577111e-06, 1.8223077859e-05, 6.8425790232e-05, 0.00023582899303, 0.00074602721725, 0.0021661657374, 0.0057730991393, 0.014122298919, 0.031708940864, 0.065348818898, 0.12361560017, 0.21462909877, 0.34204539657, 0.50033217669, 0.67175805569, 0.82784128189, 0.93639904261, 0.97219759226, 0.92646247149, 0.81036531925, 0.65059918165, 0.47943085432, 0.32427841425, 0.20132131875, 0.11472059041, 0.060002960265, 0.028806060553, 0.012693292461, 0.0051338705234, 0.001905875979, 0.00064941786695, 0.00020311125263, 5.8307341533e-05, 1.5363571947e-05, 3.7156980852e-06, 8.2483819597e-07, 1.6806482961e-07, 3.1431493852e-08, 5.3954893886e-09, 8.5011564455e-10 , 1.1108003406e-09, 6.9380612366e-09, 3.9776047345e-08, 2.0930751532e-07, 1.0109434925e-06, 4.4817606977e-06, 1.8236898541e-05, 6.8113338784e-05, 0.00023350333504, 0.00073474034434, 0.0021220440976, 0.0056254207157, 0.013687840663, 0.030569938943, 0.062666252255, 0.11791062355, 0.20363460481, 0.32279747725, 0.46966513991, 0.62722915411, 0.76885396242, 0.86504995823, 0.8933429718, 0.84678846598, 0.73673510551, 0.58833885193, 0.43124428391, 0.29013422132, 0.17916539311, 0.10155215114, 0.052832838148, 0.025228891522, 0.011057885364, 0.0044486271217, 0.0016427029623, 0.00055676558986, 0.00017320699408, 4.9458139983e-05, 1.2962556866e-05, 3.1183383271e-06, 6.8855007385e-07, 1.3954885958e-07, 2.5959549177e-08, 4.4324979243e-09, 6.9467176456e-10 , 1.0848555387e-09, 6.7399827941e-09, 3.8434940564e-08, 2.0117393262e-07, 9.6649262105e-07, 4.2619017222e-06, 1.7249973098e-05, 6.408457557e-05, 0.00021852318605, 0.00068394606933, 0.0019648340531, 0.0051809549332, 0.012539292686, 0.027855848894, 0.056798785925, 0.10630206764, 0.18260966241, 0.28792923689, 0.41670367122, 0.55353963375, 0.67491602898, 0.7553191781, 0.77587360144, 0.73152828217, 0.63306897879, 0.50286406279, 0.36663162708, 0.24535156786, 0.15070493519, 0.084966138005, 0.043968748301, 0.020884402096, 0.0091049904004, 0.0036434810609, 0.0013382370817, 0.00045115951798, 0.00013960670913, 3.965176802e-05, 1.0337095773e-05, 2.4735131774e-06, 5.4326181953e-07, 1.0951769269e-07, 2.0264648626e-08, 3.4416913763e-09, 5.3652027265e-10 , 1.0014288288e-09, 6.1885669922e-09, 3.5102640794e-08, 1.8275478908e-07, 8.7332983867e-07, 3.830602509e-06, 1.5421790522e-05, 5.6987948483e-05, 0.00019329106726, 0.00060175370891, 0.0017195164692, 0.0045099714771, 0.010857267305, 0.023990936577, 0.04865789786, 0.090581484139, 0.15477648377, 0.24274507165, 0.34944227338, 0.46172165871, 0.55996990204, 0.62334567308, 0.63690233231, 0.59730541706, 0.51416176558, 0.40624010563, 0.29460862279, 0.19610469043, 0.11981468648, 0.067191138864, 0.034585449845, 0.016340110451, 0.0070859133266, 0.0028204370756, 0.0010304251919, 0.00034553866135, 0.00010635465878, 3.0046667234e-05, 7.791405551e-06, 1.8544441218e-06, 4.0512836108e-07, 8.1236201765e-08, 1.4951623939e-08, 2.5258406477e-09, 3.916553748e-10 , 8.737217061e-10, 5.3706421532e-09, 3.0301258391e-08, 1.5691844624e-07, 7.4587671861e-07, 3.2541604469e-06, 1.303139743e-05, 4.7898589401e-05, 0.00016159747611, 0.00050040875794, 0.0014223156031, 0.0037106222007, 0.0088854003698, 0.019529322162, 0.039398252964, 0.072953537107, 0.12399250269, 0.19343014061, 0.27696996927, 0.36401635408, 0.43912571669, 0.48622405529, 0.49415573478, 0.46096813679, 0.39469131827, 0.31018736959, 0.22375375032, 0.14814813435, 0.090032987297, 0.050221189857, 0.02571294643, 0.012083613314, 0.0052122040652, 0.0020635977853, 0.00074990984285, 0.00025013388949, 7.6580137829e-05, 2.151983972e-05, 5.5506111494e-06, 1.3140876263e-06, 2.8555302833e-07, 5.6954611694e-08, 1.0426732366e-08, 1.7520631612e-09, 2.7022842297e-10 , 7.2050876376e-10, 4.4053081183e-09, 2.4722488945e-08, 1.2734722077e-07, 6.0209578123e-07, 2.6128918762e-06, 1.0407733498e-05, 3.8051468437e-05, 0.00012769291061, 0.00039331510197, 0.0011119749397, 0.0028855544515, 0.0068729482591, 0.015025748871, 0.030151527375, 0.055534418672, 0.093884699047, 0.1456823796, 0.20749089122, 0.27125060558, 0.32547831535, 0.35847020149, 0.36237969995, 0.33624389768, 0.28636804223, 0.22385896742, 0.16062165797, 0.10578241199, 0.063944384456, 0.035478983074, 0.01806839928, 0.0084459288046, 0.0036237256136, 0.0014270629035, 0.00051583402092, 0.00017114206275, 5.211748794e-05, 1.4567676772e-05, 3.7374588828e-06, 8.8012069455e-07, 1.9023401876e-07, 3.7740893077e-08, 6.8725500846e-09, 1.1486892548e-09, 1.7622441006e-10 , 5.6157928308e-10, 3.4153313511e-09, 1.9064781753e-08, 9.7681478906e-08, 4.5937977688e-07, 1.9829476514e-06, 7.8565217336e-06, 2.8571212169e-05, 9.5369163319e-05, 0.00029218997224, 0.00082168012159, 0.0021209029946, 0.0050247875042, 0.01092684269, 0.021809764206, 0.039956513792, 0.067189827561, 0.1037048623, 0.14691780508, 0.19104231894, 0.22801549733, 0.24979224801, 0.25117307901, 0.23181794584, 0.19638155401, 0.15269824862, 0.10898005217, 0.071390412748, 0.04292518273, 0.023689972237, 0.012000416405, 0.0055796611123, 0.0023812172003, 0.0009327600128, 0.00033536730916, 0.00011067543528, 3.3524433093e-05, 9.3207609098e-06, 2.3785978556e-06, 5.5714878044e-07, 1.1978416126e-07, 2.3637927171e-08, 4.281498267e-09, 7.1181038841e-10, 1.0862049454e-10 , 4.1370917803e-10, 2.5026400952e-09, 1.3895754769e-08, 7.0818316544e-08, 3.3127508914e-07, 1.4223643348e-06, 5.6054877859e-06, 2.0276607756e-05, 6.7322107498e-05, 0.00020516278164, 0.00057387852576, 0.0014734007418, 0.003472175682, 0.0075103780255, 0.014910830185, 0.027172008529, 0.045448645949, 0.069775037467, 0.098323836923, 0.12717378139, 0.15097874403, 0.16451813281, 0.1645475626, 0.15105977654, 0.12728753686, 0.098447062075, 0.069887422025, 0.045538153499, 0.027235239744, 0.014950877056, 0.0075332396664, 0.0034839923028, 0.0014789445559, 0.0005762433284, 0.00020608208433, 6.76478885e-05, 2.0382056391e-05, 5.6366438912e-06, 1.4307829588e-06, 3.3335504668e-07, 7.1288582149e-08, 1.3993019188e-08, 2.5210613597e-09, 4.1690303987e-10, 6.3279874396e-11 , 2.8806185215e-10, 1.7333032787e-09, 9.5728536209e-09, 4.8527599716e-08, 2.2579497738e-07, 9.6431790553e-07, 3.7801230519e-06, 1.3601002138e-05, 4.4917629566e-05, 0.0001361573959, 0.00037883155164, 0.0009674540488, 0.0022677462548, 0.0048790862784, 0.0096352240071, 0.017464857548, 0.029056802392, 0.044372159988, 0.062194619328, 0.080015599728, 0.094487927854, 0.10241366178, 0.10188701749, 0.093037866056, 0.077979497612, 0.059990212321, 0.042360432446, 0.027454914525, 0.016332749277, 0.0089182248339, 0.0044696838595, 0.0020561555866, 0.00086818839191, 0.00033647398232, 0.00011969310435, 3.9081052819e-05, 1.1712335436e-05, 3.2218140404e-06, 8.1346018987e-07, 1.8851793016e-07, 4.0100402998e-08, 7.8293211914e-09, 1.4030641093e-09, 2.307875141e-10, 3.4843967989e-11 , 1.8957757586e-10, 1.1346398265e-09, 6.2331562134e-09, 3.1429575387e-08, 1.4546171201e-07, 6.1792906081e-07, 2.4093940283e-06, 8.6229674707e-06, 2.8326006941e-05, 8.5406958533e-05, 0.00023636419792, 0.00060041208053, 0.0013998987852, 0.002995877061, 0.0058847879991, 0.010610048659, 0.017558347434, 0.026670416817, 0.037183944136, 0.04758399725, 0.055891502649, 0.060257468373, 0.0596287027, 0.054160099477, 0.045152693987, 0.034551527351, 0.024267796427, 0.01564492844, 0.0092575587332, 0.005028042011, 0.0025065781083, 0.0011469457531, 0.00048170905211, 0.0001856971503, 6.5706233727e-05, 2.1339646992e-05, 6.3613310886e-06, 1.7405578774e-06, 4.3712677211e-07, 1.0076472989e-07, 2.1320005317e-08, 4.1404333295e-09, 7.3804967693e-10, 1.207547673e-10, 1.8134277066e-11 , 1.1792239407e-10, 7.0202432756e-10, 3.8360585819e-09, 1.9239760007e-08, 8.8571120216e-08, 3.7425289179e-07, 1.451502726e-06, 5.1671408983e-06, 1.6883514036e-05, 5.0635400839e-05, 0.00013938812481, 0.00035219002166, 0.00081678503193, 0.00173867587, 0.0033971052617, 0.0060922689736, 0.010028325021, 0.015151586384, 0.021011995152, 0.026745835319, 0.031248167157, 0.033509895205, 0.032983828336, 0.029799463227, 0.024711325765, 0.018808895722, 0.013140439987, 0.0084262900054, 0.0049595544115, 0.0026793458965, 0.0013285999885, 0.00060469890013, 0.00025261819246, 9.6865464002e-05, 3.4092074202e-05, 1.1013303265e-05, 3.2655916584e-06, 8.8876282689e-07, 2.2201849958e-07, 5.0906372451e-08, 1.0713585574e-08, 2.0695563041e-09, 3.6694292049e-10, 5.9717210343e-11, 8.9203488346e-12 , 6.9329347818e-11, 4.1053899169e-10, 2.2313733083e-09, 1.1131890965e-08, 5.0973508081e-08, 2.1424045826e-07, 8.2648944044e-07, 2.9265318062e-06, 9.5114974101e-06, 2.8374211979e-05, 7.7692537161e-05, 0.00019526015967, 0.00045043081627, 0.00095372350188, 0.0018535151612, 0.0033063553274, 0.0054135583341, 0.0081357164308, 0.011222465895, 0.014208900742, 0.016512474045, 0.017613448203, 0.017244694754, 0.01549696736, 0.012782554142, 0.0096776094288, 0.0067250914872, 0.0042895134538, 0.0025112964213, 0.0013494824525, 0.00066560471896, 0.00030133200926, 0.00012521426834, 4.775751222e-05, 1.671897553e-05, 5.3722642406e-06, 1.5844726704e-06, 4.2893574914e-07, 1.0658071403e-07, 2.4307746926e-08, 5.0884989555e-09, 9.7772345775e-10, 1.724333315e-10, 2.7913081568e-11, 4.147366478e-12 , 3.8525085899e-11, 2.2691627743e-10, 1.2267750149e-09, 6.0876041985e-09, 2.7727246277e-08, 1.1591701821e-07, 4.4480179895e-07, 1.5666269064e-06, 5.0645999181e-06, 1.5028054804e-05, 4.0930000978e-05, 0.00010231978376, 0.00023477789364, 0.0004944644752, 0.00095585512463, 0.0016960129142, 0.0027621404734, 0.0041289748624, 0.0056652366184, 0.0071346648037, 0.0082472506911, 0.0087503362447, 0.0085215643048, 0.0076171704568, 0.0062495437451, 0.0047063310631, 0.0032530871686, 0.0020639018621, 0.0012018833077, 0.00064241443761, 0.00031517163734, 0.00014192533854, 5.8661400544e-05, 2.2254784199e-05, 7.749521501e-06, 2.4768862659e-06, 7.2663470974e-07, 1.9566212472e-07, 4.8358881344e-08, 1.0970538256e-08, 2.28432806e-09, 4.365816042e-10, 7.6586910425e-11, 1.2331712908e-11, 1.8225184816e-12 , 2.0234067893e-11, 1.1854593696e-10, 6.3748611945e-10, 3.1465547945e-09, 1.4255378211e-08, 5.9279138043e-08, 2.2625798124e-07, 7.9266067132e-07, 2.5488859592e-06, 7.5230082075e-06, 2.0380462956e-05, 5.0677477702e-05, 0.00011566354806, 0.00024230216513, 0.00046590543934, 0.00082227750681, 0.0013320432045, 0.001980607165, 0.0027030708734, 0.0033860772382, 0.0038932813331, 0.0041087968275, 0.0039800852537, 0.003538754303, 0.0028879400343, 0.0021632455755, 0.0014873143518, 0.00093859760091, 0.00054367107805, 0.00028904981446, 0.00014105487207, 6.3180632424e-05, 2.5975239623e-05, 9.8019854704e-06, 3.3950693705e-06, 1.0793536376e-06, 3.1496202268e-07, 8.4359257357e-08, 2.0738880835e-08, 4.6797046238e-09, 9.6923991055e-10, 1.842572761e-10, 3.2151115104e-11, 5.1493076296e-12, 7.5697168802e-13 , 1.0044503423e-11, 5.853514895e-11, 3.1309996262e-10, 1.5372069173e-09, 6.9272054759e-09, 2.865272819e-08, 1.0878051171e-07, 3.7906841044e-07, 1.212449888e-06, 3.5594987366e-06, 9.5916657301e-06, 2.3723554477e-05, 5.3857223975e-05, 0.00011222464673, 0.00021464079327, 0.00037680458627, 0.00060715473955, 0.00089797232067, 0.0012190069538, 0.0015188977122, 0.0017371228896, 0.0018235300668, 0.0017570104683, 0.0015538751613, 0.001261355821, 0.00093980587553, 0.00064271502197, 0.00040343948058, 0.00023244418844, 0.00012292456813, 5.9667519963e-05, 2.6583769795e-05, 1.0871155609e-05, 4.0805107346e-06, 1.4058281295e-06, 4.4456007231e-07, 1.2903525715e-07, 3.4376846259e-08, 8.4062392602e-09, 1.8867691853e-09, 3.8870237584e-10, 7.3500788789e-11, 1.2756989042e-11, 2.0322745223e-12, 2.9716577506e-13 , 4.7128490346e-12, 2.7318374993e-11, 1.4534723425e-10, 7.0980116229e-10, 3.1816118629e-09, 1.3089956674e-08, 4.9431893245e-08, 1.7133940844e-07, 5.4511355074e-07, 1.5918212739e-06, 4.2666188165e-06, 1.0496687537e-05, 2.3702839826e-05, 4.9127993407e-05, 9.3462054792e-05, 0.00016320105351, 0.00026157143293, 0.00038480130024, 0.00051959301345, 0.0006439757999, 0.00073258008342, 0.0007649276522, 0.00073310424341, 0.0006448974018, 0.00052070937818, 0.00038590381155, 0.00026250857627, 0.00016390309611, 9.393144137e-05, 4.9409864005e-05, 2.385591506e-05, 1.0572054634e-05, 4.3003283281e-06, 1.6055474816e-06, 5.5020529999e-07, 1.7306358302e-07, 4.9965095172e-08, 1.3240619268e-08, 3.2205280664e-09, 7.189986384e-10, 1.4733596287e-10, 2.7712030587e-11, 4.784180864e-12, 7.5810158932e-13, 1.1026142971e-13 , 2.0900180458e-12, 1.2050461323e-11, 6.3773146486e-11, 3.0977939658e-10, 1.3811694011e-09, 5.6522462266e-09, 2.123113596e-08, 7.319898998e-08, 2.3164275831e-07, 6.7283912131e-07, 1.7938390329e-06, 4.3897057367e-06, 9.8597847682e-06, 2.0327199309e-05, 3.8465288526e-05, 6.6809734562e-05, 0.00010650995682, 0.00015585501387, 0.00020932899497, 0.00025805921177, 0.00029200417339, 0.00030327570857, 0.00028911212576, 0.00025297355023, 0.00020317111921, 0.00014977184765, 0.00010133925389, 6.2936836912e-05, 3.5876644688e-05, 1.8771475879e-05, 9.0149624157e-06, 3.9738401938e-06, 1.6078166709e-06, 5.970924235e-07, 2.035289981e-07, 6.3678193385e-08, 1.8286749892e-08, 4.8201478364e-09, 1.1661754895e-09, 2.5896915168e-10, 5.2785154719e-11, 9.8753947728e-12, 1.6958053885e-12, 2.6728665396e-13, 3.8668802318e-14 , 8.7603486748e-13, 5.0241269478e-12, 2.644708931e-11, 1.277840056e-10, 5.6670107496e-10, 2.3068069677e-09, 8.6188105541e-09, 2.9557300607e-08, 9.3037982651e-08, 2.6880385917e-07, 7.1283864145e-07, 1.7351086399e-06, 3.876521987e-06, 7.9494429883e-06, 1.496274399e-05, 2.5850269594e-05, 4.0991955757e-05, 5.9663994762e-05, 7.9708617704e-05, 9.7741452919e-05, 0.00011000985251, 0.00011364837701, 0.00010776443378, 9.3792332336e-05, 7.4927105743e-05, 5.494020661e-05, 3.6976151023e-05, 2.2841846658e-05, 1.2951546523e-05, 6.7404948823e-06, 3.2198943245e-06, 1.4117933915e-06, 5.6817276572e-07, 2.0987857852e-07, 7.1160208392e-08, 2.2145462353e-08, 6.3257736826e-09, 1.6585227636e-09, 3.9912345651e-10, 8.8160916067e-11, 1.7874084157e-11, 3.3262093167e-12, 5.6814069508e-13, 8.907214159e-14, 1.2817577844e-14 , 3.4706037957e-13, 1.9798236401e-12, 1.0366414324e-11, 4.9820658016e-11, 2.1977125675e-10, 8.8983687085e-10, 3.3069711414e-09, 1.1280546275e-08, 3.5319160929e-08, 1.0150081664e-07, 2.6773673767e-07, 6.4822671675e-07, 1.4405422917e-06, 2.9383529636e-06, 5.5012528719e-06, 9.4536562756e-06, 1.491131934e-05, 2.1588042728e-05, 2.8687303711e-05, 3.4990225686e-05, 3.9172660763e-05, 4.0252998588e-05, 3.7965884985e-05, 3.2867672417e-05, 2.611702439e-05, 1.9048418835e-05, 1.2751857867e-05, 7.8355033111e-06, 4.4191710913e-06, 2.2876715775e-06, 1.0869947573e-06, 4.7406828685e-07, 1.8977213756e-07, 6.9727697394e-08, 2.3515655201e-08, 7.2792682992e-09, 2.0682342505e-09, 5.3937682098e-10, 1.2911015312e-10, 2.8366982374e-11, 5.7206205918e-12, 1.0588928822e-12, 1.7990425501e-13, 2.8055017009e-14, 4.0156900293e-15 , 1.2995638907e-13, 7.3739988725e-13, 3.8404891246e-12, 1.8359098963e-11, 8.055563161e-11, 3.2442937226e-10, 1.1992874471e-09, 4.0691880976e-09, 1.2672709993e-08, 3.6225348055e-08, 9.5046139847e-08, 2.2889534534e-07, 5.0596344181e-07, 1.0265503079e-06, 1.9117092052e-06, 3.2677069157e-06, 5.1267684285e-06, 7.3828559835e-06, 9.7585025287e-06, 1.1839236322e-05, 1.3183888768e-05, 1.347541729e-05, 1.264216462e-05, 1.0886302334e-05, 8.6043446572e-06, 6.2421840994e-06, 4.1565654101e-06, 2.5404576718e-06, 1.4251776292e-06, 7.338467185e-07, 3.4683378658e-07, 1.5045921486e-07, 5.9909304184e-08, 2.1895267821e-08, 7.3449011317e-09, 2.2615160855e-09, 6.3913946358e-10, 1.6579430884e-10, 3.9475256397e-11, 8.626971533e-12, 1.7304994243e-12, 3.1861416717e-13, 5.3844241213e-14, 8.352003205e-15, 1.1891068854e-15 , 4.5993617985e-14, 2.5958938775e-13, 1.3447870205e-12, 6.3944366295e-12, 2.7908076891e-11, 1.1179883408e-10, 4.1107886539e-10, 1.3873687754e-09, 4.2977306158e-09, 1.2219812717e-08, 3.1891108421e-08, 7.6393284587e-08, 1.6796612101e-07, 3.3897390495e-07, 6.2790070388e-07, 1.0675686326e-06, 1.6660155779e-06, 2.3864001832e-06, 3.1375191156e-06, 3.7862564568e-06, 4.1938587856e-06, 4.2637839215e-06, 3.9788533286e-06, 3.4080062505e-06, 2.6793047709e-06, 1.9334083845e-06, 1.2805753613e-06, 7.7851177593e-07, 4.3441593789e-07, 2.2249817277e-07, 1.0459863375e-07, 4.5134363802e-08, 1.7875782632e-08, 6.4983658454e-09, 2.1683261853e-09, 6.6408289978e-10, 1.8668117052e-10, 4.8168070227e-11, 1.1407623977e-11, 2.4797906784e-12, 4.947798898e-13, 9.0612467616e-14, 1.523155991e-14, 2.350064113e-15, 3.328093829e-16 , 1.5385377705e-14, 8.6373352318e-14, 4.4507285227e-13, 2.1050526773e-12, 9.1384920464e-12, 3.6413785182e-11, 1.3317907888e-10, 4.4708234337e-10, 1.3775781627e-09, 3.896059031e-09, 1.0113783588e-08, 2.4098120832e-08, 5.270264225e-08, 1.0579386611e-07, 1.9492570402e-07, 3.2965331798e-07, 5.1171087989e-07, 7.2907391768e-07, 9.5345058071e-07, 1.144472435e-06, 1.2609335727e-06, 1.275140221e-06, 1.1835978739e-06, 1.0083938378e-06, 7.8855822494e-07, 5.6600407561e-07, 3.7289299826e-07, 2.2549069456e-07, 1.2515641856e-07, 6.3761078195e-08, 2.981532532e-08, 1.2796853355e-08, 5.0413362374e-09, 1.8229211474e-09, 6.0502119981e-10, 1.8431105253e-10, 5.1536528517e-11, 1.3226817211e-11, 3.115852628e-12, 6.7371672477e-13, 1.3370776925e-13, 2.435668312e-14, 4.0724755416e-15, 6.24999142e-16, 8.8039155317e-17 }; Double expectedData[] = { 3.8457113701e-11, 1.5355541691e-10, 5.7920120093e-10, 2.0639292626e-09, 6.9482251076e-09, 2.2098842466e-08, 6.6402420022e-08, 1.8850309303e-07, 5.0556217426e-07, 1.2810126683e-06, 3.06659351e-06, 6.9356002718e-06, 1.4819652283e-05, 2.9917072255e-05, 5.705953493e-05, 0.00010281732805, 0.00017503874276, 0.00028153515523, 0.00042782129724, 0.00061422032944, 0.00083314126713, 0.0010676929909, 0.0012927304956, 0.0014787814262, 0.0015982137736, 0.0016319284126, 0.0015743566474, 0.0014349650576, 0.0012357086523, 0.0010053742396, 0.00077281695595, 0.00056125865138, 0.00038511216981, 0.00024966029251, 0.00015291495955, 8.8488931151e-05, 4.8380080692e-05, 2.4990966756e-05, 1.2196591949e-05, 5.6238406258e-06, 2.4500030907e-06, 1.0084150159e-06, 3.921486349e-07, 1.4407876737e-07, 5.0013227699e-08, 1.1037329674e-10, 4.3723648337e-10, 1.636283973e-09, 5.7851833903e-09, 1.9324204644e-08, 6.0983850658e-08, 1.8182691085e-07, 5.12191586e-07, 1.3631335852e-06, 3.4274945331e-06, 8.1423193202e-06, 1.8274819301e-05, 3.8751829459e-05, 7.7636509936e-05, 0.00014695181449, 0.00026279645591, 0.00044401748663, 0.00070878913587, 0.0010689830016, 0.0015232165496, 0.002050642297, 0.0026082899464, 0.0031344387057, 0.0035587861881, 0.0038175297975, 0.0038690256129, 0.0037047558489, 0.0033516338781, 0.0028647894039, 0.0023134926721, 0.0017651555547, 0.0012724402206, 0.00086662547493, 0.0005576545782, 0.00033903063312, 0.00019473882134, 0.00010568324266, 5.4187582553e-05, 2.6250254403e-05, 1.2014546058e-05, 5.1954275555e-06, 2.1226342248e-06, 8.19348414e-07, 2.9881388688e-07, 1.0296019772e-07, 3.02794963e-10, 1.1903630654e-09, 4.420890157e-09, 1.5511886635e-08, 5.1422412525e-08, 1.6105547757e-07, 4.7657874712e-07, 1.332387126e-06, 3.5193554415e-06, 8.7828022265e-06, 2.0708118508e-05, 4.613027277e-05, 9.7088882143e-05, 0.00019305955886, 0.00036270343014, 0.00064379968217, 0.0010796643898, 0.0017106686218, 0.002560838833, 0.0036219127892, 0.0048398649227, 0.0061103785729, 0.0072885766648, 0.0082140466076, 0.0087460411453, 0.0087984438376, 0.0083625799933, 0.0075095660001, 0.00637132247, 0.0051072201955, 0.0038679413048, 0.002767677649, 0.0018710763746, 0.0011951101522, 0.00072121507978, 0.00041120854454, 0.00022151337315, 0.00011274006279, 5.4212208366e-05, 2.4629546404e-05, 1.0571975754e-05, 4.2874215176e-06, 1.6427676057e-06, 5.9469615434e-07, 2.0339998248e-07, 7.957350819e-10, 3.1048351739e-09, 1.1444954743e-08, 3.9858286322e-08, 1.3114733969e-07, 4.0769806066e-07, 1.1974481467e-06, 3.3228745336e-06, 8.7118519705e-06, 2.1579753679e-05, 5.0503567024e-05, 0.00011167020402, 0.00023328797585, 0.00046045509345, 0.00085866186026, 0.0015128543103, 0.0025183271885, 0.0039606583281, 0.0058852218345, 0.0082622547105, 0.010959093909, 0.013733811596, 0.016261025394, 0.018190524071, 0.019225735632, 0.019198231303, 0.018112566654, 0.0161450472, 0.013596878193, 0.01081881459, 0.0081331867975, 0.0057767347501, 0.0038765440924, 0.0024578063506, 0.0014722827963, 0.00083325026498, 0.00044555398723, 0.00022509500531, 0.00010744149031, 4.8452791782e-05, 2.0644584265e-05, 8.3106288071e-06, 3.1608340267e-06, 1.1358179187e-06, 3.8561390472e-07, 2.0053639124e-09, 7.7665973259e-09, 2.8416967423e-08, 9.8232665366e-08, 3.2082778283e-07, 9.899826851e-07, 2.8861791059e-06, 7.9498539216e-06, 2.0688798831e-05, 5.0868825751e-05, 0.00011817034405, 0.00025936160137, 0.0005378278477, 0.0010537106065, 0.0019504728402, 0.0034111355688, 0.0056363583195, 0.0087991105269, 0.0129783515, 0.018085931707, 0.023812382431, 0.029621389802, 0.034813576865, 0.038657387952, 0.040556177845, 0.040199642788, 0.037646797572, 0.033309994463, 0.027845932452, 0.021993262511, 0.016411876559, 0.011570910099, 0.0077075702749, 0.004850741887, 0.0028842940878, 0.0016203603777, 0.000860051366, 0.00043129841327, 0.00020434876424, 9.1475929472e-05, 3.8688531618e-05, 1.5459595462e-05, 5.8365221785e-06, 2.0818501565e-06, 7.0158747243e-07, 4.8487440521e-09, 1.8640005611e-08, 6.7697858558e-08, 2.3229310615e-07, 7.5307151968e-07, 2.3066208161e-06, 6.6750827858e-06, 1.8250621752e-05, 4.7145393018e-05, 0.0001150645281, 0.00026532872982, 0.0005780530864, 0.0011898488928, 0.0023139637316, 0.0042516934589, 0.0073808777549, 0.012105835228, 0.018759540186, 0.027465676992, 0.03799260899, 0.049653357568, 0.061311059384, 0.07152695224, 0.078839039406, 0.082101980769, 0.080780521987, 0.07509316125, 0.065953042447, 0.054728053018, 0.042906775587, 0.0317820917, 0.022242304933, 0.014706787637, 0.009187483445, 0.0054227056991, 0.0030239612165, 0.0015932250002, 0.00079308314034, 0.00037299329273, 0.00016573872884, 6.9580509897e-05, 2.7598895636e-05, 1.0342749016e-05, 3.6620110163e-06, 1.2250121022e-06, 1.1250109566e-08, 4.2929617418e-08, 1.5476460321e-07, 5.2713222923e-07, 1.6963143646e-06, 5.1574304132e-06, 1.4814978245e-05, 4.0207709676e-05, 0.0001030999237, 0.00024977447074, 0.00057171350086, 0.0012363731025, 0.0025261628908, 0.0048765632302, 0.0088942038345, 0.015326430165, 0.02495259168, 0.038382302788, 0.055781104826, 0.076592088702, 0.099362253058, 0.12178664464, 0.14103230971, 0.15430439331, 0.15950660998, 0.15578290588, 0.14374794333, 0.12532110153, 0.10322561817, 0.080332541202, 0.059065820566, 0.041031906878, 0.026930714842, 0.016699935143, 0.0097841343891, 0.0054159014654, 0.0028324341218, 0.0013995537915, 0.00065337133715, 0.00028818491684, 0.00012009448115, 4.7284126413e-05, 1.7589273874e-05, 6.1818701869e-06, 2.0527163062e-06, 2.5049733328e-08, 9.4882989064e-08, 3.3953920992e-07, 1.1479565683e-06, 3.6669080544e-06, 1.10666183e-05, 3.1555167574e-05, 8.5009362931e-05, 0.00021637334168, 0.00052033311118, 0.0011822243268, 0.0025378096131, 0.0051470561139, 0.0098627832308, 0.017855860971, 0.03054237663, 0.049358912993, 0.075364890264, 0.10872096027, 0.14818294127, 0.19081998789, 0.23216152038, 0.2668685139, 0.28983121577, 0.29739502847, 0.28831217011, 0.26407845609, 0.22853025096, 0.18685088002, 0.14434020721, 0.1053465278, 0.072643013417, 0.047326915555, 0.029131528634, 0.01694178126, 0.0093088404695, 0.0048325107059, 0.0023702315716, 0.0010983719706, 0.00048089337592, 0.0001989247372, 7.7744437427e-05, 2.8707152093e-05, 1.001498633e-05, 3.3010163291e-06, 5.3527463912e-08, 2.012553614e-07, 7.1488531535e-07, 2.3991632159e-06, 7.6071574875e-06, 2.2789007633e-05, 6.4501392013e-05, 0.00017248585329, 0.00043579134738, 0.0010402661093, 0.0023461243083, 0.0049991746581, 0.010064363706, 0.019143223032, 0.034402065713, 0.058411009236, 0.093701302268, 0.14201599806, 0.20336192261, 0.27513303051, 0.35168718839, 0.42472818989, 0.4846255975, 0.52244711636, 0.53213159856, 0.51207843501, 0.46558036061, 0.39993880551, 0.32458852687, 0.24889347461, 0.18031613155, 0.12342307929, 0.079817642807, 0.048768808203, 0.028153092699, 0.015355034623, 0.0079125453174, 0.0038523198515, 0.001772022281, 0.00077011700335, 0.00031621677042, 0.00012267431314, 4.4963762972e-05, 1.5570808233e-05, 5.0944426271e-06, 1.0976899724e-07, 4.0967201853e-07, 1.4444838816e-06, 4.8119831737e-06, 1.5145193628e-05, 4.5036640588e-05, 0.00012653125671, 0.00033586935398, 0.0008423327083, 0.0019958949377, 0.0044681980146, 0.0094507846631, 0.018886176636, 0.035658325987, 0.063609001899, 0.10720546835, 0.17070884476, 0.25682413535, 0.36505337762, 0.49025003282, 0.62204186546, 0.74569687355, 0.84458964649, 0.90379494705, 0.91376557014, 0.87285157116, 0.7877469547, 0.67169759775, 0.54112996552, 0.41187942626, 0.29619610276, 0.20124694957, 0.12918735231, 0.078352239272, 0.044897643953, 0.024307282729, 0.012433402544, 0.0060087522208, 0.0027435906208, 0.0011835729221, 0.00048240454562, 0.00018576689629, 6.7587371814e-05, 2.3232839901e-05, 7.545290725e-06, 2.1602934632e-07, 8.0030469065e-07, 2.8010400297e-06, 9.2622975726e-06, 2.8937272564e-05, 8.5415545103e-05, 0.0002382083169, 0.00062765016697, 0.0015624966861, 0.0036750333641, 0.0081666546416, 0.01714619728, 0.034011999635, 0.063743695846, 0.11287117367, 0.18882938073, 0.29846734575, 0.44572275253, 0.62888829092, 0.83834530707, 1.0558764163, 1.2564463975, 1.4125881717, 1.5004719817, 1.5058473223, 1.4278241341, 1.2791139132, 1.0826409611, 0.86576577301, 0.65411950153, 0.46693293502, 0.31491449026, 0.20066494298, 0.12080671964, 0.068714976243, 0.036927714724, 0.018749694037, 0.0089944923703, 0.0040766163705, 0.0017456763592, 0.00070626597175, 0.00026996869598, 9.7498706263e-05, 3.3267779128e-05, 1.0724711047e-05, 4.0801484345e-07, 1.5003920214e-06, 5.212622924e-06, 1.7109753653e-05, 5.306042988e-05, 0.00015546701245, 0.00043037439772, 0.0011256291392, 0.0027815377193, 0.0064940451628, 0.014324727802, 0.029853700709, 0.058782857635, 0.10935633337, 0.19221085562, 0.31919244989, 0.50080428272, 0.74237644455, 1.0397311531, 1.3758106085, 1.7200337733, 2.0316833383, 2.2673355007, 2.3906515631, 2.3815379306, 2.2415036479, 1.9932516121, 1.6746556728, 1.3293203483, 0.99695259257, 0.70641528539, 0.47291859931, 0.29912549224, 0.17875622804, 0.10092753373, 0.053839232795, 0.027134935031, 0.012921098265, 0.0058131400632, 0.0024709436819, 0.0009923287582, 0.00037652073116, 0.0001349778607, 4.5716786479e-05, 1.4629367583e-05, 7.3955311131e-07, 2.699507118e-06, 9.3094430618e-06, 3.0331870561e-05, 9.3371502533e-05, 0.00027156262863, 0.00074621908056, 0.0019373291774, 0.0047520523414, 0.011012850309, 0.02411342939, 0.049883733052, 0.097498882369, 0.18004499519, 0.3141255019, 0.51780478954, 0.80643553465, 1.1866263114, 1.649677365, 2.1668297064, 2.6890028977, 3.1528149688, 3.4925804793, 3.6554015066, 3.6146352283, 3.3770274746, 2.9808864837, 2.4859764405, 1.9587964406, 1.4582175832, 1.0256426724, 0.68157014456, 0.42792312852, 0.25384095067, 0.14226508725, 0.075331342345, 0.037687193705, 0.017813637613, 0.0079552190282, 0.0033565442258, 0.0013380528939, 0.00050395851037, 0.00017933151902, 6.0291760259e-05, 1.9151193356e-05, 1.2864502234e-06, 4.6611659236e-06, 1.5955906763e-05, 5.1604199208e-05, 0.00015768426034, 0.00045523135712, 0.0012417000133, 0.0031999406678, 0.007791263496, 0.017923160046, 0.038954891287, 0.079992659101, 0.15519545371, 0.28447793114, 0.49267314739, 0.80613899333, 1.2462400105, 1.8202631453, 2.5119291518, 3.2750755298, 4.0343723478, 4.6953866572, 5.1630635413, 5.3639448529, 5.2650423011, 4.8827012428, 4.278181111, 3.5415950884, 2.7699974829, 2.0469185842, 1.429099575, 0.94268183125, 0.58750086225, 0.34593348649, 0.19244978176, 0.10115397757, 0.050233013005, 0.023568731621, 0.010447777362, 0.0043757481063, 0.0017314956198, 0.00064733797137, 0.00022865521775, 7.6308096063e-05, 2.4060046889e-05, 2.1475675441e-06, 7.7238709363e-06, 2.6245187517e-05, 8.4256090432e-05, 0.00025555992797, 0.00073236022527, 0.0019828845874, 0.0050723691004, 0.012259280593, 0.027993670512, 0.060394227512, 0.12310387896, 0.23707664247, 0.43136660743, 0.74155779184, 1.2044373297, 1.8482645641, 2.6796914552, 3.6706769962, 4.750597909, 5.8088625606, 6.7108055342, 7.3248533235, 7.5537731316, 7.3598619465, 6.7751064857, 5.892550451, 4.8420717492, 3.7592383136, 2.7574600108, 1.9109939579, 1.2512674022, 0.77407255927, 0.4524326493, 0.24984285096, 0.13035288241, 0.064256204964, 0.02992610701, 0.013168190255, 0.0054744774087, 0.0021503043146, 0.00079799063076, 0.00027979246203, 9.2685897638e-05, 2.9008644671e-05, 3.4405741146e-06, 1.2283038625e-05, 4.1429364514e-05, 0.00013202250708, 0.00039749125935, 0.0011307009193, 0.0030388453911, 0.0077163192578, 0.01851196398, 0.041960004728, 0.089858524345, 0.18181250821, 0.34755933447, 0.62773288367, 1.0711777109, 1.7269863312, 2.6306132203, 3.7858710852, 5.1477253705, 6.6131084073, 8.0266927617, 9.2046729633, 9.9728850149, 10.208783334, 9.8734262791, 9.0219936144, 7.7889312324, 6.3532208197, 4.8961053972, 3.5649077226, 2.4523731548, 1.5939164027, 0.97878037157, 0.56786576536, 0.31127686722, 0.16120879401, 0.078880804664, 0.036466548017, 0.015927906292, 0.0065729970584, 0.0025627655056, 0.00094404982061, 0.00032856500811, 0.00010804062613, 3.3565161989e-05, 5.2898731346e-06, 1.8745930169e-05, 6.2762071026e-05, 0.00019852950511, 0.00059332524098, 0.0016753330545, 0.0044694099333, 0.011265226957, 0.02682689602, 0.060358943328, 0.12830794555, 0.25769512045, 0.48898953119, 0.87666496671, 1.484938499, 2.3764244297, 3.5931907685, 5.1330709552, 6.9281133601, 8.8347329753, 10.644189447, 12.116368938, 13.030860875, 13.240806935, 12.711491879, 11.529734902, 9.8805892607, 7.9999467571, 6.119727356, 4.4230085422, 3.0202591486, 1.9485491495, 1.1877341575, 0.68401850745, 0.37218355738, 0.19133183431, 0.092930443187, 0.042645135628, 0.018489352704, 0.0075738137876, 0.0029312187328, 0.0010718214585, 0.00037028576396, 0.00012086231653, 3.7271802381e-05, 7.8053046903e-06, 2.7456080798e-05, 9.1246594429e-05, 0.00028650523207, 0.00084994041611, 0.0023822371128, 0.0063084431343, 0.015783383949, 0.037309443647, 0.083325541555, 0.17582403248, 0.35052504015, 0.66023809843, 1.1749593431, 1.9755399774, 3.138264416, 4.7101416569, 6.6791182227, 8.9483923553, 11.326914721, 13.54624633, 15.306187609, 16.340141813, 16.481067438, 15.705637798, 14.140557914, 12.028687397, 9.6674216002, 7.340806994, 5.2664465294, 3.5697046003, 2.286059895, 1.3831957832, 0.790715829, 0.42706888727, 0.21792956724, 0.10506912116, 0.047860227735, 0.020597529666, 0.0083752199738, 0.0032174958704, 0.0011678320565, 0.00040048211612, 0.00012975530544, 3.9719372366e-05, 1.105260734e-05, 3.8592282837e-05, 0.00012731115793, 0.0003967989083, 0.0011684618964, 0.0032508672302, 0.0085452468523, 0.021222215214, 0.049796352981, 0.11039390038, 0.23122422746, 0.45757502901, 0.85552364458, 1.5112713265, 2.5222817324, 3.9772736553, 5.9254055409, 8.3404870249, 11.091887815, 13.93670961, 16.544580468, 18.556325944, 19.663868054, 19.687321625, 18.62280291, 16.643481741, 14.053485407, 11.211523631, 8.4505690109, 6.0179419661, 4.0490278532, 2.5739153479, 1.5458896191, 0.87720970466, 0.47029363259, 0.23821850353, 0.11400466037, 0.051547835664, 0.022021098527, 0.00888808572, 0.0033893637538, 0.0012211492507, 0.00041568049248, 0.00013368716777, 4.0621381714e-05, 1.5019999123e-05, 5.205863455e-05, 0.00017046949683, 0.00052739838468, 0.0015415976718, 0.0042573945252, 0.011108551974, 0.027384935643, 0.063783260668, 0.14035969852, 0.29182255374, 0.57323935667, 1.0638829985, 1.8654880123, 3.0905216015, 4.8373981236, 7.1537310278, 9.9952603, 13.194603244, 16.456568735, 19.392016219, 21.589737841, 22.709757544, 22.56931365, 21.191657797, 18.799757126, 15.7572444, 12.478116322, 9.3359499534, 6.5994649461, 4.4075745253, 2.7811942878, 1.6580732764, 0.9339353987, 0.49701635271, 0.24989941606, 0.11871361324, 0.053281509756, 0.022594005756, 0.0090521274093, 0.0034264845249, 0.0012254271907, 0.00041406313511, 0.00013218577094, 3.986918284e-05, 1.9588688845e-05, 6.7393103929e-05, 0.00021905708276, 0.0006727249561, 0.0019519017605, 0.005350803234, 0.01385864301, 0.033912761226, 0.0784054612, 0.17126562018, 0.3534555343, 0.68919179771, 1.2696561978, 2.2099014131, 3.634129421, 5.6463605582, 8.2885307321, 11.495482987, 15.063213078, 18.648707675, 21.813260631, 24.106444549, 25.170188004, 24.830215193, 23.142764348, 20.379367866, 16.955355909, 13.327964835, 9.8983200502, 6.9454417319, 4.6044629083, 2.8840236162, 1.7067085842, 0.95424668451, 0.50408371921, 0.2515853821, 0.1186339128, 0.052853411071, 0.022247330252, 0.0088475595294, 0.0033243732617, 0.0011801485817, 0.00039582561418, 0.00012543250088, 3.7553489894e-05, 2.451721475e-05, 8.3727578595e-05, 0.00027014586944, 0.00082350587324, 0.0023717848209, 0.006453933459, 0.01659259765, 0.040303707333, 0.092494588952, 0.20055263493, 0.41084795965, 0.79519678696, 1.4541487088, 2.512370895, 4.1010910639, 6.3249311116, 9.2162217059, 12.687929406, 16.503243943, 20.280964753, 23.547707189, 25.831484722, 26.77261771, 26.216398513, 24.25470234, 21.201158564, 17.509105717, 13.661835808, 10.07151686, 7.0148991662, 4.6162434887, 2.8700978981, 1.6859529112, 0.9356962673, 0.49064240174, 0.24307258474, 0.11377519753, 0.050315284471, 0.021022917251, 0.0082990186688, 0.0030952889663, 0.0010907275366, 0.00036313794372, 0.00011422622977, 3.3946389798e-05, 2.9448773015e-05, 9.982791312e-05, 0.00031971997858, 0.00096744499209, 0.0027658144024, 0.0074706846452, 0.019065073055, 0.045968174478, 0.10471689473, 0.22538082713, 0.45830848589, 0.88052065722, 1.598313337, 2.7411009122, 4.4414916761, 6.7994441116, 9.8346450742, 13.439549152, 17.352075733, 21.166979765, 24.395351524, 26.564154162, 27.329118837, 26.564154162, 24.395351524, 21.166979765, 17.352075733, 13.439549152, 9.8346450742, 6.7994441116, 4.4414916761, 2.7411009122, 1.598313337, 0.88052065722, 0.45830848589, 0.22538082713, 0.10471689473, 0.045968174478, 0.019065073055, 0.0074706846452, 0.0027658144024, 0.00096744499209, 0.00031971997858, 9.9827913118e-05, 2.9448773015e-05, 3.3946389798e-05, 0.00011422622978, 0.00036313794373, 0.0010907275366, 0.0030952889663, 0.0082990186688, 0.021022917251, 0.050315284471, 0.11377519753, 0.24307258474, 0.49064240174, 0.9356962673, 1.6859529112, 2.8700978981, 4.6162434887, 7.0148991662, 10.07151686, 13.661835808, 17.509105717, 21.201158564, 24.25470234, 26.216398513, 26.77261771, 25.831484722, 23.547707189, 20.280964753, 16.503243943, 12.687929406, 9.2162217059, 6.3249311116, 4.1010910639, 2.512370895, 1.4541487088, 0.79519678696, 0.41084795965, 0.20055263493, 0.092494588952, 0.040303707333, 0.01659259765, 0.006453933459, 0.0023717848209, 0.00082350587323, 0.00027014586944, 8.3727578592e-05, 2.4517214749e-05, 3.7553489894e-05, 0.00012543250088, 0.00039582561418, 0.0011801485817, 0.0033243732617, 0.0088475595294, 0.022247330252, 0.052853411071, 0.1186339128, 0.2515853821, 0.50408371921, 0.95424668451, 1.7067085842, 2.8840236162, 4.6044629083, 6.9454417319, 9.8983200502, 13.327964835, 16.955355909, 20.379367866, 23.142764348, 24.830215193, 25.170188004, 24.106444549, 21.813260631, 18.648707675, 15.063213078, 11.495482987, 8.2885307321, 5.6463605582, 3.634129421, 2.2099014131, 1.2696561978, 0.68919179771, 0.3534555343, 0.17126562018, 0.0784054612, 0.033912761226, 0.01385864301, 0.005350803234, 0.0019519017605, 0.0006727249561, 0.00021905708276, 6.7393103926e-05, 1.9588688845e-05, 3.986918284e-05, 0.00013218577094, 0.00041406313511, 0.0012254271907, 0.0034264845249, 0.0090521274093, 0.022594005756, 0.053281509756, 0.11871361324, 0.24989941606, 0.49701635271, 0.9339353987, 1.6580732764, 2.7811942878, 4.4075745253, 6.5994649461, 9.3359499534, 12.478116322, 15.7572444, 18.799757126, 21.191657797, 22.56931365, 22.709757544, 21.589737841, 19.392016219, 16.456568735, 13.194603244, 9.9952603, 7.1537310278, 4.8373981236, 3.0905216015, 1.8654880123, 1.0638829985, 0.57323935667, 0.29182255374, 0.14035969852, 0.063783260668, 0.027384935643, 0.011108551974, 0.0042573945252, 0.0015415976718, 0.00052739838468, 0.00017046949683, 5.2058634548e-05, 1.5019999122e-05, 4.0621381714e-05, 0.00013368716777, 0.00041568049248, 0.0012211492507, 0.0033893637538, 0.00888808572, 0.022021098527, 0.051547835664, 0.11400466037, 0.23821850353, 0.47029363259, 0.87720970466, 1.5458896191, 2.5739153479, 4.0490278532, 6.0179419661, 8.4505690109, 11.211523631, 14.053485407, 16.643481741, 18.62280291, 19.687321625, 19.663868054, 18.556325944, 16.544580468, 13.93670961, 11.091887815, 8.3404870249, 5.9254055409, 3.9772736553, 2.5222817324, 1.5112713265, 0.85552364458, 0.45757502901, 0.23122422746, 0.11039390038, 0.049796352981, 0.021222215214, 0.0085452468523, 0.0032508672302, 0.0011684618964, 0.0003967989083, 0.00012731115793, 3.8592282835e-05, 1.105260734e-05, 3.9719372366e-05, 0.00012975530544, 0.00040048211613, 0.0011678320565, 0.0032174958704, 0.0083752199738, 0.020597529666, 0.047860227735, 0.10506912116, 0.21792956724, 0.42706888727, 0.790715829, 1.3831957832, 2.286059895, 3.5697046003, 5.2664465294, 7.340806994, 9.6674216002, 12.028687397, 14.140557914, 15.705637798, 16.481067438, 16.340141813, 15.306187609, 13.54624633, 11.326914721, 8.9483923553, 6.6791182227, 4.7101416569, 3.138264416, 1.9755399774, 1.1749593431, 0.66023809843, 0.35052504015, 0.17582403248, 0.083325541555, 0.037309443647, 0.015783383949, 0.0063084431343, 0.0023822371128, 0.00084994041611, 0.00028650523207, 9.1246594429e-05, 2.7456080796e-05, 7.8053046898e-06, 3.7271802381e-05, 0.00012086231653, 0.00037028576397, 0.0010718214585, 0.0029312187328, 0.0075738137876, 0.018489352704, 0.042645135628, 0.092930443187, 0.19133183431, 0.37218355738, 0.68401850745, 1.1877341575, 1.9485491495, 3.0202591486, 4.4230085422, 6.119727356, 7.9999467571, 9.8805892607, 11.529734902, 12.711491879, 13.240806935, 13.030860875, 12.116368938, 10.644189447, 8.8347329753, 6.9281133601, 5.1330709552, 3.5931907685, 2.3764244297, 1.484938499, 0.87666496671, 0.48898953119, 0.25769512045, 0.12830794555, 0.060358943328, 0.02682689602, 0.011265226957, 0.0044694099333, 0.0016753330545, 0.00059332524098, 0.00019852950511, 6.2762071026e-05, 1.8745930168e-05, 5.2898731341e-06, 3.3565161989e-05, 0.00010804062613, 0.00032856500811, 0.00094404982061, 0.0025627655056, 0.0065729970584, 0.015927906292, 0.036466548017, 0.078880804664, 0.16120879401, 0.31127686722, 0.56786576536, 0.97878037157, 1.5939164027, 2.4523731548, 3.5649077226, 4.8961053972, 6.3532208197, 7.7889312324, 9.0219936144, 9.8734262791, 10.208783334, 9.9728850149, 9.2046729633, 8.0266927617, 6.6131084073, 5.1477253705, 3.7858710852, 2.6306132203, 1.7269863312, 1.0711777109, 0.62773288367, 0.34755933447, 0.18181250821, 0.089858524345, 0.041960004728, 0.01851196398, 0.0077163192578, 0.0030388453911, 0.0011307009193, 0.00039749125935, 0.00013202250708, 4.1429364514e-05, 1.2283038624e-05, 3.4405741141e-06, 2.9008644671e-05, 9.2685897639e-05, 0.00027979246203, 0.00079799063076, 0.0021503043146, 0.0054744774087, 0.013168190255, 0.02992610701, 0.064256204964, 0.13035288241, 0.24984285096, 0.4524326493, 0.77407255927, 1.2512674022, 1.9109939579, 2.7574600108, 3.7592383136, 4.8420717492, 5.892550451, 6.7751064857, 7.3598619465, 7.5537731316, 7.3248533235, 6.7108055342, 5.8088625606, 4.750597909, 3.6706769962, 2.6796914552, 1.8482645641, 1.2044373297, 0.74155779184, 0.43136660743, 0.23707664247, 0.12310387896, 0.060394227512, 0.027993670512, 0.012259280593, 0.0050723691004, 0.0019828845874, 0.00073236022527, 0.00025555992797, 8.4256090432e-05, 2.6245187517e-05, 7.7238709356e-06, 2.1475675436e-06, 2.4060046889e-05, 7.6308096063e-05, 0.00022865521775, 0.00064733797137, 0.0017314956198, 0.0043757481063, 0.010447777362, 0.023568731621, 0.050233013005, 0.10115397757, 0.19244978176, 0.34593348649, 0.58750086225, 0.94268183125, 1.429099575, 2.0469185842, 2.7699974829, 3.5415950884, 4.278181111, 4.8827012428, 5.2650423011, 5.3639448529, 5.1630635413, 4.6953866572, 4.0343723478, 3.2750755298, 2.5119291518, 1.8202631453, 1.2462400105, 0.80613899333, 0.49267314739, 0.28447793114, 0.15519545371, 0.079992659101, 0.038954891287, 0.017923160046, 0.007791263496, 0.0031999406678, 0.0012417000133, 0.00045523135712, 0.00015768426034, 5.1604199208e-05, 1.5955906763e-05, 4.6611659233e-06, 1.2864502229e-06, 1.9151193356e-05, 6.029176026e-05, 0.00017933151902, 0.00050395851037, 0.0013380528939, 0.0033565442258, 0.0079552190282, 0.017813637613, 0.037687193705, 0.075331342345, 0.14226508725, 0.25384095067, 0.42792312852, 0.68157014456, 1.0256426724, 1.4582175832, 1.9587964406, 2.4859764405, 2.9808864837, 3.3770274746, 3.6146352283, 3.6554015066, 3.4925804793, 3.1528149688, 2.6890028977, 2.1668297064, 1.649677365, 1.1866263114, 0.80643553465, 0.51780478954, 0.3141255019, 0.18004499519, 0.097498882369, 0.049883733052, 0.02411342939, 0.011012850309, 0.0047520523414, 0.0019373291774, 0.00074621908056, 0.00027156262863, 9.3371502532e-05, 3.0331870562e-05, 9.3094430614e-06, 2.6995071179e-06, 7.3955311083e-07, 1.4629367583e-05, 4.571678648e-05, 0.0001349778607, 0.00037652073116, 0.0009923287582, 0.0024709436819, 0.0058131400632, 0.012921098265, 0.027134935031, 0.053839232795, 0.10092753373, 0.17875622804, 0.29912549224, 0.47291859931, 0.70641528539, 0.99695259257, 1.3293203483, 1.6746556728, 1.9932516121, 2.2415036479, 2.3815379306, 2.3906515631, 2.2673355007, 2.0316833383, 1.7200337733, 1.3758106085, 1.0397311531, 0.74237644455, 0.50080428272, 0.31919244989, 0.19221085562, 0.10935633337, 0.058782857635, 0.029853700709, 0.014324727802, 0.0064940451628, 0.0027815377193, 0.0011256291392, 0.00043037439772, 0.00015546701245, 5.306042988e-05, 1.7109753653e-05, 5.2126229237e-06, 1.5003920214e-06, 4.0801484294e-07, 1.0724711048e-05, 3.3267779128e-05, 9.7498706262e-05, 0.00026996869598, 0.00070626597175, 0.0017456763592, 0.0040766163705, 0.0089944923703, 0.018749694037, 0.036927714724, 0.068714976243, 0.12080671964, 0.20066494298, 0.31491449026, 0.46693293502, 0.65411950153, 0.86576577301, 1.0826409611, 1.2791139132, 1.4278241341, 1.5058473223, 1.5004719817, 1.4125881717, 1.2564463975, 1.0558764163, 0.83834530707, 0.62888829092, 0.44572275253, 0.29846734575, 0.18882938073, 0.11287117367, 0.063743695846, 0.034011999635, 0.01714619728, 0.0081666546416, 0.0036750333641, 0.0015624966861, 0.00062765016698, 0.0002382083169, 8.5415545102e-05, 2.8937272564e-05, 9.2622975733e-06, 2.8010400293e-06, 8.0030469082e-07, 2.1602934579e-07, 7.5452907253e-06, 2.3232839901e-05, 6.7587371813e-05, 0.00018576689629, 0.00048240454562, 0.0011835729221, 0.0027435906208, 0.0060087522208, 0.012433402544, 0.024307282729, 0.044897643953, 0.078352239272, 0.12918735231, 0.20124694957, 0.29619610276, 0.41187942626, 0.54112996552, 0.67169759775, 0.7877469547, 0.87285157116, 0.91376557014, 0.90379494705, 0.84458964649, 0.74569687355, 0.62204186546, 0.49025003282, 0.36505337762, 0.25682413535, 0.17070884476, 0.10720546835, 0.063609001899, 0.035658325987, 0.018886176636, 0.0094507846631, 0.0044681980146, 0.0019958949377, 0.0008423327083, 0.00033586935398, 0.00012653125671, 4.5036640587e-05, 1.5145193628e-05, 4.8119831744e-06, 1.4444838812e-06, 4.096720188e-07, 1.097689967e-07, 5.0944426274e-06, 1.5570808233e-05, 4.4963762971e-05, 0.00012267431314, 0.00031621677042, 0.00077011700335, 0.001772022281, 0.0038523198515, 0.0079125453174, 0.015355034623, 0.028153092699, 0.048768808203, 0.079817642807, 0.12342307929, 0.18031613155, 0.24889347461, 0.32458852687, 0.39993880551, 0.46558036061, 0.51207843501, 0.53213159856, 0.52244711636, 0.4846255975, 0.42472818989, 0.35168718839, 0.27513303051, 0.20336192261, 0.14201599806, 0.093701302268, 0.058411009236, 0.034402065713, 0.019143223032, 0.010064363706, 0.0049991746581, 0.0023461243083, 0.0010402661093, 0.00043579134738, 0.0001724858533, 6.4501392012e-05, 2.2789007632e-05, 7.6071574872e-06, 2.3991632167e-06, 7.1488531492e-07, 2.0125536173e-07, 5.3527463347e-08, 3.3010163294e-06, 1.001498633e-05, 2.8707152092e-05, 7.7744437428e-05, 0.0001989247372, 0.00048089337592, 0.0010983719706, 0.0023702315716, 0.0048325107059, 0.0093088404695, 0.01694178126, 0.029131528634, 0.047326915555, 0.072643013417, 0.1053465278, 0.14434020721, 0.18685088002, 0.22853025096, 0.26407845609, 0.28831217011, 0.29739502847, 0.28983121577, 0.2668685139, 0.23216152038, 0.19081998789, 0.14818294127, 0.10872096027, 0.075364890264, 0.049358912993, 0.03054237663, 0.017855860971, 0.0098627832308, 0.0051470561139, 0.0025378096131, 0.0011822243268, 0.00052033311118, 0.00021637334168, 8.5009362931e-05, 3.1555167573e-05, 1.10666183e-05, 3.6669080541e-06, 1.1479565691e-06, 3.3953920946e-07, 9.4882989413e-08, 2.5049732752e-08, 2.0527163065e-06, 6.1818701868e-06, 1.7589273874e-05, 4.7284126414e-05, 0.00012009448115, 0.00028818491684, 0.00065337133715, 0.0013995537915, 0.0028324341217, 0.0054159014654, 0.0097841343891, 0.016699935143, 0.026930714842, 0.041031906878, 0.059065820566, 0.080332541202, 0.10322561817, 0.12532110153, 0.14374794333, 0.15578290588, 0.15950660998, 0.15430439331, 0.14103230971, 0.12178664464, 0.099362253058, 0.076592088702, 0.055781104826, 0.038382302788, 0.02495259168, 0.015326430165, 0.0088942038345, 0.0048765632302, 0.0025261628908, 0.0012363731025, 0.00057171350086, 0.00024977447074, 0.0001030999237, 4.0207709676e-05, 1.4814978244e-05, 5.1574304127e-06, 1.6963143643e-06, 5.2713223006e-07, 1.5476460273e-07, 4.2929617753e-08, 1.1250108987e-08, 1.2250121025e-06, 3.6620110162e-06, 1.0342749015e-05, 2.7598895637e-05, 6.9580509897e-05, 0.00016573872883, 0.00037299329273, 0.00079308314034, 0.0015932250002, 0.0030239612165, 0.0054227056991, 0.009187483445, 0.014706787637, 0.022242304933, 0.0317820917, 0.042906775587, 0.054728053018, 0.065953042447, 0.07509316125, 0.080780521987, 0.082101980769, 0.078839039406, 0.07152695224, 0.061311059384, 0.049653357568, 0.03799260899, 0.027465676992, 0.018759540186, 0.012105835228, 0.0073808777549, 0.0042516934589, 0.0023139637316, 0.0011898488928, 0.0005780530864, 0.00026532872982, 0.0001150645281, 4.7145393018e-05, 1.8250621752e-05, 6.6750827849e-06, 2.3066208156e-06, 7.5307151945e-07, 2.3229310701e-07, 6.769785805e-08, 1.8640005905e-08, 4.8487434829e-09, 7.0158747263e-07, 2.0818501564e-06, 5.8365221781e-06, 1.5459595463e-05, 3.8688531618e-05, 9.147592947e-05, 0.00020434876424, 0.00043129841327, 0.000860051366, 0.0016203603777, 0.0028842940878, 0.004850741887, 0.0077075702749, 0.011570910099, 0.016411876559, 0.021993262511, 0.027845932452, 0.033309994463, 0.037646797572, 0.040199642788, 0.040556177845, 0.038657387952, 0.034813576865, 0.029621389802, 0.023812382431, 0.018085931707, 0.0129783515, 0.0087991105269, 0.0056363583195, 0.0034111355688, 0.0019504728402, 0.0010537106065, 0.0005378278477, 0.00025936160137, 0.00011817034405, 5.0868825751e-05, 2.068879883e-05, 7.9498539217e-06, 2.8861791051e-06, 9.8998268472e-07, 3.208277826e-07, 9.8232666208e-08, 2.8416966885e-08, 7.7665975618e-09, 2.0053633694e-09, 3.8561390484e-07, 1.1358179185e-06, 3.1608340264e-06, 8.310628808e-06, 2.0644584265e-05, 4.845279178e-05, 0.00010744149031, 0.00022509500531, 0.00044555398723, 0.00083325026498, 0.0014722827963, 0.0024578063506, 0.0038765440924, 0.0057767347501, 0.0081331867975, 0.01081881459, 0.013596878193, 0.0161450472, 0.018112566654, 0.019198231303, 0.019225735632, 0.018190524071, 0.016261025394, 0.013733811596, 0.010959093909, 0.0082622547105, 0.0058852218345, 0.0039606583281, 0.0025183271885, 0.0015128543103, 0.00085866186026, 0.00046045509345, 0.00023328797586, 0.00011167020402, 5.0503567024e-05, 2.1579753678e-05, 8.7118519703e-06, 3.3228745336e-06, 1.197448146e-06, 4.0769806029e-07, 1.3114733947e-07, 3.9858287123e-08, 1.1444954176e-08, 3.1048353422e-09, 7.9573458579e-10, 2.0339998253e-07, 5.9469615409e-07, 1.6427676055e-06, 4.2874215185e-06, 1.0571975754e-05, 2.4629546403e-05, 5.4212208366e-05, 0.00011274006278, 0.00022151337315, 0.00041120854454, 0.00072121507978, 0.0011951101522, 0.0018710763746, 0.002767677649, 0.0038679413048, 0.0051072201955, 0.00637132247, 0.0075095660001, 0.0083625799933, 0.0087984438376, 0.0087460411453, 0.0082140466076, 0.0072885766648, 0.0061103785729, 0.0048398649227, 0.0036219127892, 0.002560838833, 0.0017106686218, 0.0010796643898, 0.00064379968217, 0.00036270343014, 0.00019305955886, 9.7088882143e-05, 4.6130272771e-05, 2.0708118508e-05, 8.7828022264e-06, 3.5193554413e-06, 1.3323871259e-06, 4.765787465e-07, 1.6105547721e-07, 5.1422412318e-08, 1.5511887363e-08, 4.4208895581e-09, 1.1903631668e-09, 3.0279453771e-10, 1.0296019769e-07, 2.9881388654e-07, 8.1934841385e-07, 2.1226342257e-06, 5.1954275553e-06, 1.2014546057e-05, 2.6250254403e-05, 5.4187582553e-05, 0.00010568324266, 0.00019473882134, 0.00033903063312, 0.0005576545782, 0.00086662547493, 0.0012724402206, 0.0017651555547, 0.0023134926721, 0.0028647894039, 0.0033516338781, 0.0037047558489, 0.0038690256129, 0.0038175297975, 0.0035587861881, 0.0031344387057, 0.0026082899464, 0.002050642297, 0.0015232165496, 0.0010689830016, 0.00070878913586, 0.00044401748663, 0.00026279645591, 0.00014695181449, 7.7636509936e-05, 3.8751829459e-05, 1.8274819301e-05, 8.1423193205e-06, 3.427494533e-06, 1.3631335849e-06, 5.1219158581e-07, 1.818269103e-07, 6.0983850308e-08, 1.9324204448e-08, 5.7851840157e-09, 1.6362833421e-09, 4.3723652767e-10, 1.1037296811e-10, 5.0013227583e-08, 1.4407876694e-07, 3.921486348e-07, 1.0084150167e-06, 2.4500030905e-06, 5.6238406247e-06, 1.2196591949e-05, 2.4990966756e-05, 4.8380080691e-05, 8.8488931151e-05, 0.00015291495955, 0.00024966029251, 0.00038511216981, 0.00056125865138, 0.00077281695595, 0.0010053742396, 0.0012357086523, 0.0014349650576, 0.0015743566474, 0.0016319284126, 0.0015982137736, 0.0014787814262, 0.0012927304956, 0.0010676929909, 0.00083314126713, 0.00061422032944, 0.00042782129724, 0.00028153515523, 0.00017503874276, 0.00010281732804, 5.705953493e-05, 2.9917072255e-05, 1.4819652283e-05, 6.9356002724e-06, 3.0665935102e-06, 1.2810126683e-06, 5.0556217397e-07, 1.8850309274e-07, 6.6402419511e-08, 2.2098842129e-08, 6.9482249317e-09, 2.0639297636e-09, 5.792005369e-10, 1.5355542172e-10, 3.8456906847e-11 }; Matrix beamMatrixIn(33, 33); Double *p; p = beamData; for (uInt i=0; i<33; i++) { for (uInt j=0; j<33; j++) { beamMatrixIn(i,j) = *p++; } } Matrix kernelMatrix(45, 45); Matrix beamMatrixOut; p = kernelData; for (uInt i=0; i<45; i++) { for (uInt j=0; j<45; j++) { kernelMatrix(i,j) = *p++; } } Matrix expectedMatrix(45, 45); p = expectedData; for (uInt i=0; i<45; i++) { for (uInt j=0; j<45; j++) { expectedMatrix(i,j) = *p++; } } Convolver conv(beamMatrixIn, kernelMatrix.shape()); conv.linearConv(beamMatrixOut, kernelMatrix); cout << "*** max " << max(abs(beamMatrixOut - expectedMatrix)) << endl; return max(abs(beamMatrixOut - expectedMatrix)) < 5e-10; } int main() { Bool anyFailures = False; { Bool failed = False; // Test the double precision constructor Array psf(IPosition(1,4)); psf = 0.; psf(IPosition(1,1)) = 0.1; psf(IPosition(1,2)) = 1.; psf(IPosition(1,3)) = 0.5; Convolver conv(psf); // Now test circular Convolution (1 - Dimensional) Vector mod(4); mod = 0; mod(3) = 1; mod(0) = 2; Vector result; conv.circularConv(result, mod); Array expectedResult(IPosition(1,4)); expectedResult(IPosition(1,0)) = 2.5; expectedResult(IPosition(1,1)) = 1.0; expectedResult(IPosition(1,2)) = 0.1; expectedResult(IPosition(1,3)) = 1.2; if (!allNearAbs(expectedResult, result, 1.E-10)) failed = True; if (failed) cout << "Failed"; else cout << "Passed"; cout << " the Circular Convolution in Double Precision Test" << endl; mod.resize(IPosition(1,6)); mod = 0; mod(5) = 1; mod(0) = 2; if (!failed){ result.resize(IPosition(1,0)); conv.circularConv(result, mod); expectedResult.resize(IPosition(1,6)); expectedResult = 0.; expectedResult(IPosition(1,0)) = 2.5; expectedResult(IPosition(1,1)) = 1.0; expectedResult(IPosition(1,4)) = 0.1; expectedResult(IPosition(1,5)) = 1.2; if (!allNearAbs(expectedResult, result, 1.E-10)){ failed = True; cout << "Failed"; } else cout << "Passed"; cout << " the Circular Convolution Resize Test" << endl; } if (failed) anyFailures = True; } { Bool failed = False; // Test the single precision constructor Matrix psf(2,2); psf = 0.; psf(1,1) = 1; psf(0,1) = .5; psf(1,0) = .1; // cout << "Psf:" << psf << endl; Convolver conv(psf); // And test single precision circular convolution Matrix mod(6,6); mod = 0; mod(0,0) = 1; mod(5,5) = 2; mod(2,0) = 3; // cout << "Model:" << mod << endl; Matrix result; conv.circularConv(result, mod); // cout << "Result:" << result << endl; Matrix expectedResult(6,6); expectedResult = mod; expectedResult(5,0) = 0.5; expectedResult(0,5) = 0.1; expectedResult(5,4) = 0.2; expectedResult(4,5) = 1.0; expectedResult(2,5) = 0.3; expectedResult(1,0) = 1.5; if (!allNearAbs(expectedResult, result, 1.E-5)){ failed = True; cout << "Failed"; } else cout << "Passed"; cout << " the Floating Point 2-D Circular Convolution Test" << endl; if (failed) anyFailures = True; } { Bool failed = False; // Test the double precision constructor with supplied image size Array psf(IPosition(1,2)); psf = 0.; psf(IPosition(1,0)) = .5; psf(IPosition(1,1)) = 1.; Convolver conv(psf, IPosition(1,4)); // And test linear convolution Array mod(IPosition(1,4)); mod = 0.; mod(IPosition(1,0)) = 1.; mod(IPosition(1,3)) = 2.; Array result; conv.linearConv(result, mod, False); Array expectedResult(IPosition(1,4)); expectedResult(IPosition(1,0)) = 1.; expectedResult(IPosition(1,1)) = 0.; expectedResult(IPosition(1,2)) = 1.; expectedResult(IPosition(1,3)) = 2.; if (!allNearAbs(expectedResult, result, 1.E-10)){ failed = True; cout << "Failed"; } else cout << "Passed"; cout << " the Linear Convolution in Double Precision Test" << endl; if (!failed){ // see if the convolver can automatically resize if given a bigger image Vector bigMod(8), bigResult; bigMod = 0; bigMod(0) = 1; bigMod(7) = 2; conv.linearConv(bigResult, bigMod, True); Vector expectedBigResult(9); expectedBigResult = 0; expectedBigResult(0) = 0.5; expectedBigResult(1) = 1.0; expectedBigResult(7) = 1.0; expectedBigResult(8) = 2.0; if (!allNearAbs(expectedBigResult, bigResult, 1.E-10)){ failed = True; cout << "Failed"; } else cout << "Passed"; cout << " the array resize test" << endl; } if (!failed){ // Set the psf to something new (different size) psf.resize(IPosition(1,4)); psf(IPosition(1,0)) = .5; psf(IPosition(1,1)) = 1.; psf(IPosition(1,2)) = .3; psf(IPosition(1,3)) = .1; conv.setPsf(psf); result.resize(IPosition(1,0)); conv.linearConv(result, mod); expectedResult.resize(IPosition(1,4)); expectedResult(IPosition(1,0)) = 0.3; expectedResult(IPosition(1,1)) = 1.1; expectedResult(IPosition(1,2)) = 2.0; expectedResult(IPosition(1,3)) = 0.6; if (!allNearAbs(expectedResult, result, 1.E-10)){ failed = True; cout << "Failed"; } else cout << "Passed"; cout << " the new psf test" << endl; } if (!failed){ mod.resize(IPosition(1,2)); mod(IPosition(1,0)) = 0; mod(IPosition(1,1)) = 1; result.resize(IPosition(1,0)); conv.linearConv(result, mod); expectedResult.resize(IPosition(1,2)); expectedResult(IPosition(1,0)) = 1; expectedResult(IPosition(1,1)) = 0.3; if (!allNearAbs(expectedResult, result, 1.E-10)){ failed = True; cout << "Failed"; } else cout << "Passed"; cout << " the small model test" << endl; } if (failed) anyFailures = True; } { Bool failed = False; // Test the linear convolution with Single precision 2-D functions Matrix psf(2,4); psf = 0.; psf(1,2) = 1.; psf(1,3) = .1; Cube mod(2,4,3); Convolver conv(psf, mod.shape()); mod = 0.; mod(1,2,0) = 1.; mod(0,0,1) = 2.; mod(1,3,1) = 4.; for (uInt i = 0; i < 4; i++) for (uInt j = 0; j < 2; j++) mod(j,i,2) = 1.; Cube result; conv.linearConv(result, mod, False); Cube expectedResult(2,4,3); expectedResult = mod; expectedResult(1,3,0) = 0.1; expectedResult(0,1,1) = 0.2; expectedResult(0,0,2) = 1; expectedResult(1,0,2) = 1; expectedResult(0,1,2) = 1.1; expectedResult(1,1,2) = 1.1; expectedResult(0,2,2) = 1.1; expectedResult(1,2,2) = 1.1; expectedResult(0,3,2) = 1.1; expectedResult(1,3,2) = 1.1; if (!allNearAbs(expectedResult, result, 1.E-5)){ failed = True; cout << "Failed"; } else cout << "Passed"; cout << " the Multiple Floating Point 2-D Linear Convolution Test" << endl; if (failed) anyFailures = True; } { Bool failed = False; if (! doLinearConv()) { failed = True; cout << "Failed"; } else { cout << "Passed"; } cout << " the linear convolution test" << endl; if (failed) anyFailures = True; /* Matrix mat1 = doLinearConv(); Matrix mat2 = doLinearConv(); Matrix mat3 = doLinearConv(); Matrix mat4 = doLinearConv(); Matrix mat5 = doLinearConv(); cout << "*** all " << allTrue(mat0 == mat1) << endl; cout << "*** all " << allTrue(mat0 == mat2) << endl; cout << "*** all " << allTrue(mat0 == mat3) << endl; cout << "*** all " << allTrue(mat0 == mat4) << endl; cout << "*** all " << allTrue(mat0 == mat5) << endl; */ } if (anyFailures) { cout << "FAIL" << endl; return 1; } else { cout << "OK" << endl; return 0; } } // Local Variables: // compile-command: "gmake OPTLIB=1 tConvolver" // End: casacore-3.7.1/scimath/Mathematics/test/tFFTServer.cc000066400000000000000000002056331476623553700225040ustar00rootroot00000000000000//# tFFTServer: This program tests the FFTServer and FourierTool classes //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define AlwaysTrue(x, y) \ do { \ ++tests_done; \ AlwaysAssert(x, y); \ } while (0) unsigned tests_done = 0; const Bool debug = False;//True; template void dum(const Array &d) { cout << d.nelements() << " elements, " << d.shape() << " " << d.shape().product() << endl; } template void dump(const Vector &d) { dum(d); for (int i = 0; i < d.shape()(0); i++) { cout << i << ": " << d(i) << endl; } cout << endl; } template void dump(const Matrix &d) { dum(d); for (int i = 0; i < d.shape()(0); i++) { cout << i << ":"; for (int j = 0; j < d.shape()(1); j++) { cout << " " << d(i, j); } cout << endl; } cout << endl; } template void dump(const Cube &d) { dum(d); for (int i = 0; i < d.shape()(0); i++) { cout << i << ":"; for (int j = 0; j < d.shape()(1); j++) { cout << " "; for (int k = 0; k < d.shape()(2); k++) { cout << "," << d(i, j, k); } } cout << endl; } cout << endl; } template void dump(const Array &d) { if (d.shape().nelements() == 1) dump((const Vector)d); else if (d.shape().nelements() == 2) dump((const Matrix)d); else if (d.shape().nelements() == 3) dump((const Cube)d); else dum(d); } template Array shift(const Array &input, const IPosition &input_shape, const IPosition &expected_shape) { // Shift the array by Ni/2 in every dimension (circular boundary), // except along the dimension that is half size in real<->complex pairs Array shifted_input(input.shape()); if (debug ) cout << "shapes: " << input_shape << expected_shape << endl; for (unsigned i = 0; i < input.nelements(); i++) { IPosition here; IPosition shifted; if (input.ndim() == 1) { int N0 = input.shape()(0); int s[1]; here = IPosition(1, i); if (input.shape()(0) < std::max(input_shape(0), expected_shape(0))) { s[0] = i % N0; } else { s[0] = (i + (N0+1)/2) % N0; } shifted = IPosition(1, s[0]); } else if (input.ndim() == 2) { int N[2], n[2]; N[0] = input.shape()(0); N[1] = input.shape()(1); n[0] = i % N[0]; n[1] = i / N[0]; int s[2]; for (int j = 0; j < 2; j++) { if (input.shape()(j) < std::max(input_shape(j), expected_shape(j))) { s[j] = n[j]; } else { s[j] = (n[j] - N[j]/2 + N[j]) % N[j]; } } here = IPosition(2, n[0], n[1]); shifted = IPosition(2, s[0], s[1]); } else if (input.ndim() == 3) { int N[3], n[3], s[3]; N[0] = input.shape()(0); N[1] = input.shape()(1); N[2] = input.shape()(2); n[0] = i % N[0]; n[1] = (i / N[0]) % N[1]; n[2] = i / (N[1]*N[0]); for (int j = 0; j < 3; j++) { if (input.shape()(j) < std::max(input_shape(j), expected_shape(j))) { s[j] = n[j]; } else { s[j] = (n[j] - N[j]/2 + N[j]) % N[j]; } } here = IPosition(3, n[0], n[1], n[2]); shifted = IPosition(3, s[0], s[1], s[2]); } else { AlwaysTrue(input.ndim() == 4, AipsError); // can't handle more than this int N[4], n[4], s[4]; N[0] = input.shape()(0); N[1] = input.shape()(1); N[2] = input.shape()(2); N[3] = input.shape()(3); n[0] = i % N[0]; n[1] = (i / N[0]) % N[1]; n[2] = (i / (N[1]*N[0])) % N[2]; n[3] = i / (N[2]*N[1]*N[0]); for (int j = 0; j < 4; j++) { if (input.shape()(j) < std::max(input_shape(j), expected_shape(j))) { s[j] = n[j]; } else { s[j] = (n[j] - N[j]/2 + N[j]) % N[j]; } } here = IPosition(4, n[0], n[1], n[2], n[3]); shifted = IPosition(4, s[0], s[1], s[2], s[3]); } shifted_input(here) = input(shifted); } return shifted_input; } template class R2C1Deven1 { public: static Vector input() { Vector input(8, 0.0f); input(0) = 1.0f; return input; } static Vector expectedResult() { return Vector(5, Complex(1,0)); } }; template class R2C1Deven2 { public: static Vector input() { Vector input(8, 0.0f); input(0) = 1.0f; input(2) = -1.0f; input(4) = 1.0f; input(6) = -1.0f; return input; } static Vector expectedResult() { Vector expectedResult(5, Complex(0,0)); expectedResult(2) = Complex(4,0); return expectedResult; } }; template class R2C1Deven3 { public: static Vector input() { Vector input(8, 0.0f); input(1) = 1.0f; input(3) = -1.0f; input(5) = 1.0f; input(7) = -1.0f; return input; } static Vector expectedResult() { Vector expectedResult(5, Complex(0,0)); expectedResult(2) = Complex(0, -4); return expectedResult; } }; template class R2C1Deven4 { public: static Vector input() { Vector input(8, 0.0f); input(1) = 1.0f; input(3) = 1.0f; input(5) = 1.0f; input(7) = 1.0f; return input; } static Vector expectedResult() { Vector expectedResult(5, Complex(0,0)); expectedResult(0) = Complex(4, 0); expectedResult(4) = Complex(-4, 0); return expectedResult; } }; template class R2C1Dodd1 { public: static Vector input() { Vector input(9, 0.0f); input(0) = 1.0f; return input; } static Vector expectedResult() { return Vector(5, Complex(1,0)); } }; template class R2C1Dodd2 { public: static Vector input() { return Vector(9, 1.0f); } static Vector expectedResult() { Vector expectedResult(5, Complex(0,0)); expectedResult(0) = Complex(9,0); return expectedResult; } }; template class R2C1Dodd3 { public: static Vector input() { Vector input(9, 1.0f); input(1) = 0.0f; input(3) = 0.0f; input(5) = 0.0f; input(7) = 0.0f; return input; } static Vector expectedResult() { Vector expectedResult(5, Complex(0,0)); expectedResult(0) = Complex(5,0); expectedResult(1) = Complex(0.5, 0.181985117133101); expectedResult(2) = Complex(0.5, 0.419549815588640); expectedResult(3) = Complex(0.5, 0.866025403784437); expectedResult(4) = Complex(0.5, 2.83564090980885); return expectedResult; } }; template class R2C2Deveneven1 { public: static Matrix input() { Matrix input(4, 6); input = 0.0f; input(0, 0) = 1.0f; return input; } static Matrix expectedResult() { return Matrix(3, 6, Complex(1, 0)); } }; template class R2C2Deveneven2 { public: static Matrix input() { return Matrix(4, 6, 1.0f); } static Matrix expectedResult() { Matrix expectedResult(3, 6, Complex(0,0)); expectedResult(0, 0) = Complex(24, 0); return expectedResult; } }; template class R2C2Deveneven3 { public: static Matrix input() { Matrix input(4, 6, 0.0f); input(1,1) = 1.0f; input(1,3) = 1.0f; input(1,5) = 1.0f; return input; } static Matrix expectedResult() { Matrix expectedResult(3, 6, Complex(0,0)); expectedResult(0,0) = expectedResult(2,3) = Complex(3,0); expectedResult(0,3) = expectedResult(2,0) = Complex(-3,0); expectedResult(1,3) = Complex(0,3); expectedResult(1,0) = Complex(0,-3); return expectedResult; } }; template class R2C2Devenodd1 { public: static Matrix input() { Matrix input(4, 5, 0.0f); input(0, 0) = 1.0f; return input; } static Matrix expectedResult() { return Matrix(3, 5, Complex(1, 0)); } }; template class R2C2Devenodd2 { public: static Matrix input() { return Matrix(4, 5, 1.0f); } static Matrix expectedResult() { Matrix expectedResult(3, 5, Complex(0,0)); expectedResult(0,0) = Complex(20,0); return expectedResult; } }; template class R2C2Doddeven1 { public: static Matrix input() { Matrix input(3, 6, 0.0f); input(0, 0) = 1.0f; return input; } static Matrix expectedResult() { return Matrix(2, 6, Complex(1, 0)); } }; template class R2C2Doddeven2 { public: static Matrix input() { return Matrix(3, 6, 1.0f); } static Matrix expectedResult() { Matrix expectedResult(2, 6, Complex(0,0)); expectedResult(0,0) = Complex(18,0); return expectedResult; } }; template class R2C2Doddodd1 { public: static Matrix input() { Matrix input(3, 5, 0.0f); input(0, 0) = 1.0f; return input; } static Matrix expectedResult() { return Matrix(2, 5, Complex(1, 0)); } }; template class R2C2Doddodd2 { public: static Matrix input() { return Matrix(3, 5, 1.0f); } static Matrix expectedResult() { Matrix expectedResult(2, 5, Complex(0,0)); expectedResult(0,0) = Complex(15,0); return expectedResult; } }; template class R2C3Deveneveneven1 { public: static Cube input() { Cube input(4, 6, 8, 0.0f); input(0, 0, 0) = 1.0f; return input; } static Cube expectedResult() { return Cube(3, 6, 8, Complex(1, 0)); } }; template class R2C3Deveneveneven2 { public: static Cube input() { return Cube(4, 6, 8, 1.0f); } static Cube expectedResult() { Cube expectedResult(3, 6, 8, Complex(0,0)); expectedResult(0, 0, 0) = Complex(4*6*8,0); return expectedResult; } }; template class R2C3Doddoddodd1 { public: static Cube input() { Cube input(3,5,7, 0.0f); input(0, 0, 0) = 1.0f; return input; } static Cube expectedResult() { return Cube(2,5,7, Complex(1, 0)); } }; template class R2C3Doddoddodd2 { public: static Cube input() { return Cube(3,5,7, 1.0f); } static Cube expectedResult() { Cube expectedResult(2,5,7, Complex(0,0)); expectedResult(0, 0, 0) = Complex(3*5*7,0); return expectedResult; } static double tolerance() { return 100*FLT_EPSILON; } }; template class R2C4Doddoddoddeven1 { public: static Array input() { Array input(IPosition(4,3,5,7,4), T(0.0)); input(IPosition(4,0)) = 1.0f; return input; } static Array expectedResult() { return Array(IPosition(4,2,5,7,4), S(1, 0)); } }; template class R2C4Doddoddoddeven2 { public: static Array input() { return Array (IPosition(4,3,5,7,4), T(1.0)); } static Array expectedResult() { Array expectedResult(IPosition(4,2,5,7,4), S(0, 0)); expectedResult(IPosition(4,0)) = S(3*5*7*4,0); return expectedResult; } static double tolerance() { return 500*FLT_EPSILON; } }; template class C2R1Deven1 { public: static Vector input() { Vector input(5, Complex(0, 0)); input(0) = Complex(8, 0); return input; } static Vector expectedResult() { return Vector(8, 1.0f); } }; template class C2R1Deven2 { public: static Vector input() { Vector input(5, S(0, 0)); input(0) = S(16.0f, 0.0f); input(2) = S(8.0f, 0.0f); return input; } static Vector expectedResult() { Vector expectedResult(8, T(2.0)); expectedResult(0) = 4.0f; expectedResult(2) = 0.0f; expectedResult(4) = 4.0f; expectedResult(6) = 0.0f; return expectedResult; } }; template class C2R1Deven3 { public: static Vector input() { Vector input(5, S(0, 0)); input(0) = Complex(0.0f, 0.0f); input(2) = Complex(0.0f, 4.0f); return input; } static Vector expectedResult() { Vector expectedResult(8, T(0.0)); expectedResult(1) = -1.0f; expectedResult(3) = 1.0f; expectedResult(5) = -1.0f; expectedResult(7) = 1.0f; return expectedResult; } }; template class C2R1Deven4 { public: static Vector input() { Vector input(5, S(1, 0)); return input; } static Vector expectedResult() { Vector expectedResult(8, T(0.0)); expectedResult(0) = 1.0f; return expectedResult; } }; template class C2R1Deven5 { public: static Vector input() { Vector input(5, S(1, 0)); input(1) = Complex(0,0); input(3) = Complex(0,0); return input; } static Vector expectedResult() { Vector expectedResult(8, T(0.0)); expectedResult(0) = 0.5f; expectedResult(4) = 0.5f; return expectedResult; } }; template class C2R1Dodd1 { public: static Vector input() { Vector input(5, S(0, 0)); input(0) = Complex(9, 0); return input; } static Vector expectedResult() { return Vector(9, T(1.0)); } }; template class C2R1Dodd2 { public: static Vector input() { Vector input(5, S(1, 0)); return input; } static Vector expectedResult() { Vector expectedResult(9, T(0.0)); expectedResult(0) = 1.0f; return expectedResult; } }; template class C2R2Deveneven1 { public: static Matrix input() { Matrix input(3, 6, S(0, 0)); input(0,0) = Complex(4*6,0); return input; } static Matrix expectedResult() { Matrix expectedResult(4, 6, T(1.0)); return expectedResult; } }; template class C2R2Deveneven2 { public: static Matrix input() { Matrix input(3, 6, S(1, 0)); return input; } static Matrix expectedResult() { Matrix expectedResult(4, 6, T(0.0)); expectedResult(0,0) = 1.0f; return expectedResult; } }; template class C2R2Deveneven3 { public: static Matrix input() { Matrix input(3, 6, S(0, 0)); input(0,0) = Complex(24,0); input(2,0) = Complex(-24,0); input(0,1) = Complex(-24,0); input(0,2) = Complex( 24,0); input(0,3) = Complex(-24,0); input(0,4) = Complex( 24,0); input(0,5) = Complex(-24,0); return input; } static Matrix expectedResult() { Matrix expectedResult(4, 6, T(0.0)); expectedResult(0,0) = expectedResult(0,1) = expectedResult(0,2) = expectedResult(0,4) = expectedResult(0,5) = -1.0f; expectedResult(0,3) = 5.0f; expectedResult(1,0) = expectedResult(1,1) = expectedResult(1,2) = expectedResult(1,4) = expectedResult(1,5) = 1.0f; expectedResult(1,3) = 7.0f; expectedResult(2,0) = expectedResult(2,1) = expectedResult(2,2) = expectedResult(2,4) = expectedResult(2,5) = -1.0f; expectedResult(2,3) = 5.0f; expectedResult(3,0) = expectedResult(3,1) = expectedResult(3,2) = expectedResult(3,4) = expectedResult(3,5) = 1.0f; expectedResult(3,3) = 7.0f; return expectedResult; } }; template class C2R2Doddodd1 { public: static Matrix input() { Matrix input(2, 5, S(0, 0)); input(0, 0) = S(3*5, 0); return input; } static Matrix expectedResult() { Matrix expectedResult(3, 5, T(1.0)); return expectedResult; } }; template class C2R2Doddodd2 { public: static Matrix input() { Matrix input(2, 5, S(1, 0)); return input; } static Matrix expectedResult() { Matrix expectedResult(3, 5, T(0.0)); expectedResult(0,0) = 1.0f; return expectedResult; } }; template class C2R2Doddodd3 { public: static Matrix input() { Matrix input(2, 5, S(0, 0)); input(1,0) = Complex(0,45); input(1,1) = Complex(0,45); input(1,2) = Complex(0,45); input(1,3) = Complex(0,45); input(1,4) = Complex(0,45); return input; } static Matrix expectedResult() { Matrix expectedResult(3, 5, T(0.0)); expectedResult(1,0) = -25.9808f; expectedResult(2,0) = 25.9808f; return expectedResult; } }; template class C2R2Devenodd1 { public: static Matrix input() { Matrix input(3, 5, S(0, 0)); input(0,0) = Complex(4*5, 0); return input; } static Matrix expectedResult() { Matrix expectedResult(4, 5, T(1.0)); return expectedResult; } }; template class C2R2Devenodd2 { public: static Matrix input() { Matrix input(3, 5, S(1, 0)); return input; } static Matrix expectedResult() { Matrix expectedResult(4, 5, T(0.0)); expectedResult(0,0) = 1.0f; return expectedResult; } }; template class C2R2Doddeven1 { public: static Matrix input() { Matrix input(2, 6, S(0, 0)); input(0,0) = Complex(3*6, 0); return input; } static Matrix expectedResult() { Matrix expectedResult(3, 6, T(1.0)); return expectedResult; } }; template class C2R2Doddeven2 { public: static Matrix input() { Matrix input(2, 6, S(1, 0)); return input; } static Matrix expectedResult() { Matrix expectedResult(3, 6, T(0.0)); expectedResult(0,0) = 1.0f; return expectedResult; } }; template class C2R3Deveneveneven1 { public: static Cube input() { Cube input(3, 6, 2, S(0, 0)); input(0,0,0) = Complex(4*6*2,0); return input; } static Cube expectedResult() { Cube expectedResult(4, 6, 2, T(1.0)); return expectedResult; } }; template class C2R3Deveneveneven2 { public: static Cube input() { Cube input(3, 6, 2, S(1, 0)); return input; } static Cube expectedResult() { Cube expectedResult(4, 6, 2, T(0.0)); expectedResult(0,0,0) = 1.0f; return expectedResult; } }; template class C2R3Doddoddodd1 { public: static Cube input() { Cube input(2, 5, 7, S(0, 0)); input(0,0,0) = Complex(3*5*7,0); return input; } static Cube expectedResult() { Cube expectedResult(3, 5, 7, T(1.0)); return expectedResult; } }; template class C2R3Doddoddodd2 { public: static Cube input() { Cube input(2, 5, 7, S(1, 0)); return input; } static Cube expectedResult() { Cube expectedResult(3, 5, 7, T(0.0)); expectedResult(0,0,0) = 1.0f; return expectedResult; } }; template class C2R4Doddoddoddeven1 { public: static Array input() { #if PERFORMANCE_TEST // Useful to test how the use of threads effects CPU usage Array input(IPosition(4,20,500,70,20), S(0, 0)); #else Array input(IPosition(4,2,5,7,2), S(0, 0)); #endif input(IPosition(4,0)) = Complex(3*5*7*2,0); return input; } static Array expectedResult() { #if PERFORMANCE_TEST Array expectedResult(IPosition(4, 38, 500, 70, 20)); #else Array expectedResult(IPosition(4,3,5,7,2), T(1.0)); #endif expectedResult = 1.0f; return expectedResult; } }; template class C2R4Doddoddoddeven2 { public: static Array input() { Array input(IPosition(4,2,5,7,2), S(1, 0)); return input; } static Array expectedResult() { Array expectedResult(IPosition(4,3,5,7,2), T(0.0)); expectedResult = 0.0f; expectedResult(IPosition(4,0)) = 1.0f; return expectedResult; } }; template class C2C1Deven1 { public: static Vector input() { Vector input(8, S(0, 0)); input(0) = Complex(1.0f, 0.0f); return input; } static Vector expectedResult() { Vector expectedResult(8, S(1, 0)); return expectedResult; } }; template class C2C1Deven2 { public: static Vector input() { Vector input(8, S(1, 0)); return input; } static Vector expectedResult() { Vector expectedResult(8, S(0, 0)); expectedResult(0) = Complex(8,0); return expectedResult; } }; template class C2C1Deven3 { public: static Vector input() { Vector input(8, S(-1, 0)); input(0) = Complex(1, 0); input(2) = Complex(1, 0); input(4) = Complex(1, 0); input(6) = Complex(1, 0); return input; } static Vector expectedResult() { Vector expectedResult(8, S(0, 0)); expectedResult(4) = Complex(8,0); return expectedResult; } }; template class C2C1Deven4 { public: static Vector input() { Vector input(8, S(0, 0)); input(1) = Complex(1, 0); input(3) = Complex(-1,0); input(5) = Complex(1, 0); input(7) = Complex(-1,0); return input; } static Vector expectedResult() { Vector expectedResult(8, S(0, 0)); expectedResult(2) = Complex(0,-4); expectedResult(6) = Complex(0,4); return expectedResult; } }; template class C2C1Dodd1 { public: static Vector input() { Vector input(7, S(0, 0)); input(0) = Complex(1.0f, 0.0f); return input; } static Vector expectedResult() { Vector expectedResult(7, S(1, 0)); return expectedResult; } }; template class C2C1Dodd2 { public: static Vector input() { Vector input(7, S(1, 0)); return input; } static Vector expectedResult() { Vector expectedResult(7, S(0, 0)); expectedResult(0) = Complex(7,0); return expectedResult; } }; template class C2C2Deveneven1 { public: static Matrix input() { Matrix input(4, 6, S(0, 0)); input(0,0) = Complex(1,0); return input; } static Matrix expectedResult() { Matrix expectedResult(4, 6, S(1, 0)); return expectedResult; } }; template class C2C2Deveneven2 { public: static Matrix input() { Matrix input(4, 6, S(1, 0)); return input; } static Matrix expectedResult() { Matrix expectedResult(4, 6, S(0, 0)); expectedResult(0,0) = Complex(24,0); return expectedResult; } }; template class C2C2Deveneven3 { public: static Matrix input() { Matrix input(4, 6, S(0, 0)); input(1,1) = Complex(1,1); input(1,3) = Complex(1,1); input(1,5) = Complex(1,1); return input; } static Matrix expectedResult() { Matrix expectedResult(4, 6, S(0, 0)); expectedResult(0,0) = expectedResult(2,3) = Complex(3,3); expectedResult(0,3) = expectedResult(2,0) = Complex(-3,-3); expectedResult(1,3) = expectedResult(3,0) = Complex(-3,3); expectedResult(1,0) = expectedResult(3,3) = Complex(3,-3); return expectedResult; } }; template class C2C2Doddodd1 { public: static Matrix input() { Matrix input(3, 5, S(0, 0)); input(0,0) = Complex(1,0); return input; } static Matrix expectedResult() { Matrix expectedResult(3, 5, S(1, 0)); return expectedResult; } }; template class C2C2Doddodd2 { public: static Matrix input() { Matrix input(3, 5, S(1, 0)); return input; } static Matrix expectedResult() { Matrix expectedResult(3, 5, S(0, 0)); expectedResult(0,0) = Complex(15,0); return expectedResult; } }; template class C2C2Devenodd1 { public: static Matrix input() { Matrix input(4, 5, S(0, 0)); input(0,0) = Complex(1,0); return input; } static Matrix expectedResult() { Matrix expectedResult(4, 5, S(1, 0)); return expectedResult; } }; template class C2C2Devenodd2 { public: static Matrix input() { Matrix input(4, 5, S(1, 0)); return input; } static Matrix expectedResult() { Matrix expectedResult(4, 5, S(0, 0)); expectedResult(0,0) = Complex(20,0); return expectedResult; } }; template class C2C2Doddeven1 { public: static Matrix input() { Matrix input(3, 6, Complex(0, 0)); input(0,0) = Complex(1,0); return input; } static Matrix expectedResult() { Matrix expectedResult(3, 6, Complex(1, 0)); return expectedResult; } }; template class C2C2Doddeven2 { public: static Matrix input() { Matrix input(3, 6, Complex(1, 0)); return input; } static Matrix expectedResult() { Matrix expectedResult(3, 6, Complex(0, 0)); expectedResult(0,0) = Complex(18,0); return expectedResult; } }; template class C2C3Doddeveneven1 { public: static Cube input() { Cube input(4, 6, 8, Complex(0, 0)); input(0,0,0) = Complex(1,0); return input; } static Cube expectedResult() { Cube expectedResult(4, 6, 8, Complex(1, 0)); return expectedResult; } }; template class C2C3Doddeveneven2 { public: static Cube input() { Cube input(4, 6, 8, S(1, 0)); return input; } static Cube expectedResult() { Cube expectedResult(4, 6, 8, S(0, 0)); expectedResult(0,0,0) = Complex(4*6*8,0); return expectedResult; } }; template class C2C3Doddoddodd1 { public: static Cube input() { Cube input(3, 5, 7, S(0, 0)); input(0,0,0) = Complex(1,0); return input; } static Cube expectedResult() { Cube expectedResult(3, 5, 7, S(1, 0)); return expectedResult; } }; template class C2C3Doddoddodd2 { public: static Cube input() { Cube input(3, 5, 7, S(1, 0)); return input; } static Cube expectedResult() { Cube expectedResult(3, 5, 7, S(0, 0)); expectedResult(0,0,0) = Complex(3*5*7,0); return expectedResult; } }; template class C2C4Doddoddoddeven1 { public: static Array input() { Array input(IPosition(4,3,5,7,4)); input = Complex(0,0); input(IPosition(4,0)) = Complex(1,0); return input; } static Array expectedResult() { Array result, expectedResult(IPosition(4,3,5,7,4)); expectedResult = Complex(1,0); return expectedResult; } }; template class C2C4Doddoddoddeven2 { public: static Array input() { Array input(IPosition(4,3,5,7,4)); input = Complex(1,0); return input; } static Array expectedResult() { Array result, expectedResult(IPosition(4,3,5,7,4)); expectedResult = Complex(0,0); expectedResult(IPosition(4,0)) = Complex(3*5*7*4,0); return expectedResult; } }; template class Test { public: Test(FFTServer &server, Bool shifted_mode, // use fft() instead of fft0 ? double epsilon, const Array input, const Array expectedResult) { Array result(expectedResult.shape()); int iterations = 1; #if PERFORMANCE_TEST iterations = 10; #endif for (int i = 0; i < iterations; i++) { if (shifted_mode) { server.fft(result, input); } else { server.fft0(result, input); } } if (debug) { cout << "Input:" << endl; dump(input); cout << "Result:" << endl; dump(result); cout << "Expected:" << endl; dump(expectedResult); } AlwaysTrue(result.shape().isEqual(expectedResult.shape()), AipsError); AlwaysTrue(allNearAbs(result, expectedResult, 2*epsilon), AipsError); if (input.shape().nelements() == 1){ if (debug) { cout << "Resize..." << endl;} result.resize(); if (shifted_mode) server.fft(result, input); else server.fft0(result, input); if (debug) { cout << "Input:" << endl; dump(input); cout << "Result:" << endl; dump(result); cout << "Expected:" << endl; dump(expectedResult); } AlwaysTrue(result.shape().isEqual(expectedResult.shape()), AipsError); AlwaysTrue(allNearAbs(result, expectedResult, epsilon), AipsError); int out_size = expectedResult.nelements(); if (out_size % 2 == 0) { server.resize(IPosition(1, out_size), FFTEnums::REALTOCOMPLEX); if (shifted_mode) server.fft(result, input); else server.fft0(result, input); AlwaysTrue(result.shape().isEqual(expectedResult.shape()), AipsError); AlwaysTrue(allNearAbs(result, expectedResult, epsilon), AipsError); server.resize(IPosition(1, out_size-1), FFTEnums::COMPLEX); if (shifted_mode) server.fft(result, input); else server.fft0(result, input); AlwaysTrue(result.shape().isEqual(expectedResult.shape()), AipsError); AlwaysTrue(allNearAbs(result, expectedResult, epsilon), AipsError); } } } }; template class TestFFTShift { public: TestFFTShift() // test the complex->complex fft shift function { FFTServer server; int iterations = 1; Array a(IPosition(2,2,10)); a(IPosition(2,0,0)) = Complex(0.,0.); a(IPosition(2,0,1)) = Complex(1.,0.); a(IPosition(2,0,2)) = Complex(2.,0.); a(IPosition(2,0,3)) = Complex(3.,0.); a(IPosition(2,0,4)) = Complex(4.,0.); a(IPosition(2,0,5)) = Complex(5.,0.); a(IPosition(2,0,6)) = Complex(6.,0.); a(IPosition(2,0,7)) = Complex(7.,0.); a(IPosition(2,0,8)) = Complex(8.,0.); a(IPosition(2,0,9)) = Complex(9.,0.); a(IPosition(2,1,0)) = Complex(0.,0.); a(IPosition(2,1,1)) = Complex(10.,0.); a(IPosition(2,1,2)) = Complex(20.,0.); a(IPosition(2,1,3)) = Complex(30.,0.); a(IPosition(2,1,4)) = Complex(40.,0.); a(IPosition(2,1,5)) = Complex(50.,0.); a(IPosition(2,1,6)) = Complex(60.,0.); a(IPosition(2,1,7)) = Complex(70.,0.); a(IPosition(2,1,8)) = Complex(80.,0.); a(IPosition(2,1,9)) = Complex(90.,0.); Array aflags(IPosition(2,2,10)); aflags(IPosition(2,0,0)) = True; aflags(IPosition(2,0,1)) = True; aflags(IPosition(2,0,2)) = True; aflags(IPosition(2,0,3)) = True; aflags(IPosition(2,0,4)) = False; aflags(IPosition(2,0,5)) = True; aflags(IPosition(2,0,6)) = True; aflags(IPosition(2,0,7)) = False; aflags(IPosition(2,0,8)) = True; aflags(IPosition(2,0,9)) = True; aflags(IPosition(2,1,0)) = True; aflags(IPosition(2,1,1)) = True; aflags(IPosition(2,1,2)) = True; aflags(IPosition(2,1,3)) = True; aflags(IPosition(2,1,4)) = False; aflags(IPosition(2,1,5)) = True; aflags(IPosition(2,1,6)) = True; aflags(IPosition(2,1,7)) = False; aflags(IPosition(2,1,8)) = True; aflags(IPosition(2,1,9)) = True; Array aflags2(IPosition(2,2,10)); aflags2(IPosition(2,0,0)) = True; aflags2(IPosition(2,0,1)) = True; aflags2(IPosition(2,0,2)) = True; aflags2(IPosition(2,0,3)) = True; aflags2(IPosition(2,0,4)) = True; aflags2(IPosition(2,0,5)) = True; aflags2(IPosition(2,0,6)) = False; aflags2(IPosition(2,0,7)) = False; aflags2(IPosition(2,0,8)) = True; aflags2(IPosition(2,0,9)) = True; aflags2(IPosition(2,1,0)) = True; aflags2(IPosition(2,1,1)) = True; aflags2(IPosition(2,1,2)) = True; aflags2(IPosition(2,1,3)) = True; aflags2(IPosition(2,1,4)) = True; aflags2(IPosition(2,1,5)) = True; aflags2(IPosition(2,1,6)) = False; aflags2(IPosition(2,1,7)) = False; aflags2(IPosition(2,1,8)) = True; aflags2(IPosition(2,1,9)) = True; Array b(IPosition(2,10,2)); b(IPosition(2,0,0)) = Complex(0.,0.); b(IPosition(2,1,0)) = Complex(1.,0.); b(IPosition(2,2,0)) = Complex(2.,0.); b(IPosition(2,3,0)) = Complex(3.,0.); b(IPosition(2,4,0)) = Complex(4.,0.); b(IPosition(2,5,0)) = Complex(5.,0.); b(IPosition(2,6,0)) = Complex(6.,0.); b(IPosition(2,7,0)) = Complex(7.,0.); b(IPosition(2,8,0)) = Complex(8.,0.); b(IPosition(2,9,0)) = Complex(9.,0.); b(IPosition(2,0,1)) = Complex(0.,0.); b(IPosition(2,1,1)) = Complex(10.,0.); b(IPosition(2,2,1)) = Complex(20.,0.); b(IPosition(2,3,1)) = Complex(30.,0.); b(IPosition(2,4,1)) = Complex(40.,0.); b(IPosition(2,5,1)) = Complex(50.,0.); b(IPosition(2,6,1)) = Complex(60.,0.); b(IPosition(2,7,1)) = Complex(70.,0.); b(IPosition(2,8,1)) = Complex(80.,0.); b(IPosition(2,9,1)) = Complex(90.,0.); Array expect(IPosition(2,2,10)); expect(IPosition(2,0,0)) = Complex(9.,0.); expect(IPosition(2,0,1)) = Complex(0.,0.); expect(IPosition(2,0,2)) = Complex(1.,0.); expect(IPosition(2,0,3)) = Complex(2.,0.); expect(IPosition(2,0,4)) = Complex(3.,0.); expect(IPosition(2,0,5)) = Complex(4.,0.); expect(IPosition(2,0,6)) = Complex(5.,0.); expect(IPosition(2,0,7)) = Complex(6.,0.); expect(IPosition(2,0,8)) = Complex(7.,0.); expect(IPosition(2,0,9)) = Complex(8.,0.); expect(IPosition(2,1,0)) = Complex(90.,0.); expect(IPosition(2,1,1)) = Complex(0.,0.); expect(IPosition(2,1,2)) = Complex(10.,0.); expect(IPosition(2,1,3)) = Complex(20.,0.); expect(IPosition(2,1,4)) = Complex(30.,0.); expect(IPosition(2,1,5)) = Complex(40.,0.); expect(IPosition(2,1,6)) = Complex(50.,0.); expect(IPosition(2,1,7)) = Complex(60.,0.); expect(IPosition(2,1,8)) = Complex(70.,0.); expect(IPosition(2,1,9)) = Complex(80.,0.); Array expectb(IPosition(2,2,10)); expectb(IPosition(2,0,0)) = Complex(9.,0.); expectb(IPosition(2,0,1)) = Complex(0.,0.); expectb(IPosition(2,0,2)) = Complex(1.,0.); expectb(IPosition(2,0,3)) = Complex(2.,0.); expectb(IPosition(2,0,4)) = Complex(3.,0.); expectb(IPosition(2,0,5)) = Complex(4.,0.); expectb(IPosition(2,0,6)) = Complex(5.,0.); expectb(IPosition(2,0,7)) = Complex(6.,0.); expectb(IPosition(2,0,8)) = Complex(7.,0.); expectb(IPosition(2,0,9)) = Complex(8.,0.); expectb(IPosition(2,1,0)) = Complex(90.,0.); expectb(IPosition(2,1,1)) = Complex(0.,0.); expectb(IPosition(2,1,2)) = Complex(10.,0.); expectb(IPosition(2,1,3)) = Complex(20.,0.); expectb(IPosition(2,1,4)) = Complex(30.,0.); expectb(IPosition(2,1,5)) = Complex(40.,0.); expectb(IPosition(2,1,6)) = Complex(50.,0.); expectb(IPosition(2,1,7)) = Complex(60.,0.); expectb(IPosition(2,1,8)) = Complex(70.,0.); expectb(IPosition(2,1,9)) = Complex(80.,0.); Array expectc(IPosition(2,2,10)); expectc(IPosition(2,0,0)) = Complex(1.,0.); expectc(IPosition(2,0,1)) = Complex(2.,0.); expectc(IPosition(2,0,2)) = Complex(3.,0.); expectc(IPosition(2,0,3)) = Complex(4.,0.); expectc(IPosition(2,0,4)) = Complex(5.,0.); expectc(IPosition(2,0,5)) = Complex(6.,0.); expectc(IPosition(2,0,6)) = Complex(7.,0.); expectc(IPosition(2,0,7)) = Complex(8.,0.); expectc(IPosition(2,0,8)) = Complex(9.,0.); expectc(IPosition(2,0,9)) = Complex(0.,0.); expectc(IPosition(2,1,0)) = Complex(10.,0.); expectc(IPosition(2,1,1)) = Complex(20.,0.); expectc(IPosition(2,1,2)) = Complex(30.,0.); expectc(IPosition(2,1,3)) = Complex(40.,0.); expectc(IPosition(2,1,4)) = Complex(50.,0.); expectc(IPosition(2,1,5)) = Complex(60.,0.); expectc(IPosition(2,1,6)) = Complex(70.,0.); expectc(IPosition(2,1,7)) = Complex(80.,0.); expectc(IPosition(2,1,8)) = Complex(90.,0.); expectc(IPosition(2,1,9)) = Complex(0.,0.); Array expectd(IPosition(2,2,10)); expectd(IPosition(2,0,0)) = Complex(1.,0.); expectd(IPosition(2,0,1)) = Complex(2.,0.); expectd(IPosition(2,0,2)) = Complex(3.,0.); expectd(IPosition(2,0,3)) = Complex(4.,0.); expectd(IPosition(2,0,4)) = Complex(5.,0.); expectd(IPosition(2,0,5)) = Complex(6.,0.); expectd(IPosition(2,0,6)) = Complex(7.,0.); expectd(IPosition(2,0,7)) = Complex(8.,0.); expectd(IPosition(2,0,8)) = Complex(9.,0.); expectd(IPosition(2,0,9)) = Complex(0.,0.); expectd(IPosition(2,1,0)) = Complex(10.,0.); expectd(IPosition(2,1,1)) = Complex(20.,0.); expectd(IPosition(2,1,2)) = Complex(30.,0.); expectd(IPosition(2,1,3)) = Complex(40.,0.); expectd(IPosition(2,1,4)) = Complex(50.,0.); expectd(IPosition(2,1,5)) = Complex(60.,0.); expectd(IPosition(2,1,6)) = Complex(70.,0.); expectd(IPosition(2,1,7)) = Complex(80.,0.); expectd(IPosition(2,1,8)) = Complex(90.,0.); expectd(IPosition(2,1,9)) = Complex(0.,0.); Array expflags(IPosition(2,2,10)); expflags(IPosition(2,0,0)) = False; expflags(IPosition(2,0,1)) = True; expflags(IPosition(2,0,2)) = True; expflags(IPosition(2,0,3)) = True; expflags(IPosition(2,0,4)) = True; expflags(IPosition(2,0,5)) = False; expflags(IPosition(2,0,6)) = True; expflags(IPosition(2,0,7)) = True; expflags(IPosition(2,0,8)) = False; expflags(IPosition(2,0,9)) = True; expflags(IPosition(2,1,0)) = False; expflags(IPosition(2,1,1)) = True; expflags(IPosition(2,1,2)) = True; expflags(IPosition(2,1,3)) = True; expflags(IPosition(2,1,4)) = True; expflags(IPosition(2,1,5)) = False; expflags(IPosition(2,1,6)) = True; expflags(IPosition(2,1,7)) = True; expflags(IPosition(2,1,8)) = False; expflags(IPosition(2,1,9)) = True; Array expflagsb(IPosition(2,2,10)); expflagsb(IPosition(2,0,0)) = False; expflagsb(IPosition(2,0,1)) = True; expflagsb(IPosition(2,0,2)) = True; expflagsb(IPosition(2,0,3)) = True; expflagsb(IPosition(2,0,4)) = True; expflagsb(IPosition(2,0,5)) = True; expflagsb(IPosition(2,0,6)) = True; expflagsb(IPosition(2,0,7)) = True; expflagsb(IPosition(2,0,8)) = True; expflagsb(IPosition(2,0,9)) = True; expflagsb(IPosition(2,1,0)) = False; expflagsb(IPosition(2,1,1)) = True; expflagsb(IPosition(2,1,2)) = True; expflagsb(IPosition(2,1,3)) = True; expflagsb(IPosition(2,1,4)) = True; expflagsb(IPosition(2,1,5)) = True; expflagsb(IPosition(2,1,6)) = True; expflagsb(IPosition(2,1,7)) = True; expflagsb(IPosition(2,1,8)) = True; expflagsb(IPosition(2,1,9)) = True; Array expflagsc(IPosition(2,2,10)); expflagsc(IPosition(2,0,0)) = True; expflagsc(IPosition(2,0,1)) = True; expflagsc(IPosition(2,0,2)) = True; expflagsc(IPosition(2,0,3)) = False; expflagsc(IPosition(2,0,4)) = True; expflagsc(IPosition(2,0,5)) = True; expflagsc(IPosition(2,0,6)) = False; expflagsc(IPosition(2,0,7)) = True; expflagsc(IPosition(2,0,8)) = True; expflagsc(IPosition(2,0,9)) = False; expflagsc(IPosition(2,1,0)) = True; expflagsc(IPosition(2,1,1)) = True; expflagsc(IPosition(2,1,2)) = True; expflagsc(IPosition(2,1,3)) = False; expflagsc(IPosition(2,1,4)) = True; expflagsc(IPosition(2,1,5)) = True; expflagsc(IPosition(2,1,6)) = False; expflagsc(IPosition(2,1,7)) = True; expflagsc(IPosition(2,1,8)) = True; expflagsc(IPosition(2,1,9)) = False; Array expflagsd(IPosition(2,2,10)); expflagsd(IPosition(2,0,0)) = True; expflagsd(IPosition(2,0,1)) = True; expflagsd(IPosition(2,0,2)) = True; expflagsd(IPosition(2,0,3)) = True; expflagsd(IPosition(2,0,4)) = True; expflagsd(IPosition(2,0,5)) = True; expflagsd(IPosition(2,0,6)) = True; expflagsd(IPosition(2,0,7)) = True; expflagsd(IPosition(2,0,8)) = True; expflagsd(IPosition(2,0,9)) = False; expflagsd(IPosition(2,1,0)) = True; expflagsd(IPosition(2,1,1)) = True; expflagsd(IPosition(2,1,2)) = True; expflagsd(IPosition(2,1,3)) = True; expflagsd(IPosition(2,1,4)) = True; expflagsd(IPosition(2,1,5)) = True; expflagsd(IPosition(2,1,6)) = True; expflagsd(IPosition(2,1,7)) = True; expflagsd(IPosition(2,1,8)) = True; expflagsd(IPosition(2,1,9)) = False; Array expflagse(IPosition(2,2,10)); expflagse(IPosition(2,0,0)) = True; expflagse(IPosition(2,0,1)) = True; expflagse(IPosition(2,0,2)) = False; expflagse(IPosition(2,0,3)) = False; expflagse(IPosition(2,0,4)) = True; expflagse(IPosition(2,0,5)) = False; expflagse(IPosition(2,0,6)) = False; expflagse(IPosition(2,0,7)) = True; expflagse(IPosition(2,0,8)) = False; expflagse(IPosition(2,0,9)) = False; expflagse(IPosition(2,1,0)) = True; expflagse(IPosition(2,1,1)) = True; expflagse(IPosition(2,1,2)) = False; expflagse(IPosition(2,1,3)) = False; expflagse(IPosition(2,1,4)) = True; expflagse(IPosition(2,1,5)) = False; expflagse(IPosition(2,1,6)) = False; expflagse(IPosition(2,1,7)) = True; expflagse(IPosition(2,1,8)) = False; expflagse(IPosition(2,1,9)) = False; Array expflagse2(IPosition(2,2,10)); expflagse2(IPosition(2,0,0)) = True; expflagse2(IPosition(2,0,1)) = True; expflagse2(IPosition(2,0,2)) = True; expflagse2(IPosition(2,0,3)) = True; expflagse2(IPosition(2,0,4)) = True; expflagse2(IPosition(2,0,5)) = False; expflagse2(IPosition(2,0,6)) = False; expflagse2(IPosition(2,0,7)) = False; expflagse2(IPosition(2,0,8)) = True; expflagse2(IPosition(2,0,9)) = False; expflagse2(IPosition(2,1,0)) = True; expflagse2(IPosition(2,1,1)) = True; expflagse2(IPosition(2,1,2)) = True; expflagse2(IPosition(2,1,3)) = True; expflagse2(IPosition(2,1,4)) = True; expflagse2(IPosition(2,1,5)) = False; expflagse2(IPosition(2,1,6)) = False; expflagse2(IPosition(2,1,7)) = False; expflagse2(IPosition(2,1,8)) = True; expflagse2(IPosition(2,1,9)) = False; Array ra(IPosition(2,2,10)); ra(IPosition(2,0,0)) = 0.; ra(IPosition(2,0,1)) = 1.; ra(IPosition(2,0,2)) = 2.; ra(IPosition(2,0,3)) = 3.; ra(IPosition(2,0,4)) = 4.; ra(IPosition(2,0,5)) = 5.; ra(IPosition(2,0,6)) = 6.; ra(IPosition(2,0,7)) = 7.; ra(IPosition(2,0,8)) = 8.; ra(IPosition(2,0,9)) = 9.; ra(IPosition(2,1,0)) = 0.; ra(IPosition(2,1,1)) = 10.; ra(IPosition(2,1,2)) = 20.; ra(IPosition(2,1,3)) = 30.; ra(IPosition(2,1,4)) = 40.; ra(IPosition(2,1,5)) = 50.; ra(IPosition(2,1,6)) = 60.; ra(IPosition(2,1,7)) = 70.; ra(IPosition(2,1,8)) = 80.; ra(IPosition(2,1,9)) = 90.; Array rexpect(IPosition(2,2,10)); rexpect(IPosition(2,0,0)) = 9.; rexpect(IPosition(2,0,1)) = 0.; rexpect(IPosition(2,0,2)) = 1.; rexpect(IPosition(2,0,3)) = 2.; rexpect(IPosition(2,0,4)) = 3.; rexpect(IPosition(2,0,5)) = 4.; rexpect(IPosition(2,0,6)) = 5.; rexpect(IPosition(2,0,7)) = 6.; rexpect(IPosition(2,0,8)) = 7.; rexpect(IPosition(2,0,9)) = 8.; rexpect(IPosition(2,1,0)) = 90.; rexpect(IPosition(2,1,1)) = 0.; rexpect(IPosition(2,1,2)) = 10.; rexpect(IPosition(2,1,3)) = 20.; rexpect(IPosition(2,1,4)) = 30.; rexpect(IPosition(2,1,5)) = 40.; rexpect(IPosition(2,1,6)) = 50.; rexpect(IPosition(2,1,7)) = 60.; rexpect(IPosition(2,1,8)) = 70.; rexpect(IPosition(2,1,9)) = 80.; Array rexpectb(IPosition(2,2,10)); rexpectb(IPosition(2,0,0)) = 9.; rexpectb(IPosition(2,0,1)) = 0.; rexpectb(IPosition(2,0,2)) = 1.; rexpectb(IPosition(2,0,3)) = 2.; rexpectb(IPosition(2,0,4)) = 3.; rexpectb(IPosition(2,0,5)) = 4.; rexpectb(IPosition(2,0,6)) = 5.; rexpectb(IPosition(2,0,7)) = 6.; rexpectb(IPosition(2,0,8)) = 7.; rexpectb(IPosition(2,0,9)) = 8.; rexpectb(IPosition(2,1,0)) = 90.; rexpectb(IPosition(2,1,1)) = 0.; rexpectb(IPosition(2,1,2)) = 10.; rexpectb(IPosition(2,1,3)) = 20.; rexpectb(IPosition(2,1,4)) = 30.; rexpectb(IPosition(2,1,5)) = 40.; rexpectb(IPosition(2,1,6)) = 50.; rexpectb(IPosition(2,1,7)) = 60.; rexpectb(IPosition(2,1,8)) = 70.; rexpectb(IPosition(2,1,9)) = 80.; #if PERFORMANCE_TEST iterations = 10; #endif for (int it = 0; it < iterations; it++) { cout << "--- zero shift ----------------------------------------------------" << endl; uInt whichAxis = 1; Double relshift = 0.; Array inVal; inVal.assign(a); server.fftshift(inVal, whichAxis, relshift, False); { for(uInt i=0; i<2; i++){ for(uInt j=0; j<10; j++){ DComplex diff = inVal(IPosition(2,i,j))-a(IPosition(2,i,j)); cout << i << " " << j << " " << inVal(IPosition(2,i,j)) << " " << a(IPosition(2,i,j)) << endl; AlwaysAssert( (diff.real()==0. && diff.imag()==0.), AipsError); } } } cout << "--- right-shift by 1 channel in second axis -----------------------" << endl; whichAxis = 1; relshift = 1./10.; inVal.assign(a); server.fftshift(inVal, whichAxis, relshift, False); { for(uInt i=0; i<2; i++){ for(uInt j=0; j<10; j++){ DComplex diff = inVal(IPosition(2,i,j))-expect(IPosition(2,i,j)); cout << i << " " << j << " " << inVal(IPosition(2,i,j)) << " " << expect(IPosition(2,i,j)) << endl; AlwaysAssert((abs(diff.real())<2E-5) && (abs(diff.imag())<2E-5), AipsError); } } } cout << "--- two consecutive leftshifts by 0.5 channels in second axis -----------------------" << endl; relshift = -0.5/10.; inVal.assign(a); server.fftshift(inVal, whichAxis, relshift, False); server.fftshift(inVal, whichAxis, relshift, False); // two consecutive shifts should shift by 1 channel { for(uInt i=0; i<2; i++){ for(uInt j=0; j<10; j++){ DComplex diff = inVal(IPosition(2,i,j))-expectc(IPosition(2,i,j)); cout << i << " " << j << " " << inVal(IPosition(2,i,j)) << " " << expectc(IPosition(2,i,j)) << endl; AlwaysAssert((abs(diff.real())<2E-5) && (abs(diff.imag())<2E-5), AipsError); } } } cout << "--- rightshift by 1 channel in first axis -----------------------" << endl; whichAxis = 0; relshift = 1./10.; server.fftshift(b, whichAxis, relshift, False); { for(uInt i=0; i<2; i++){ for(uInt j=0; j<10; j++){ DComplex diff = b(IPosition(2,j,i))-expect(IPosition(2,i,j)); cout << i << " " << j << " " << b(IPosition(2,j,i)) << " " << expect(IPosition(2,i,j)) << endl; AlwaysAssert((abs(diff.real())<2E-5) && (abs(diff.imag())<2E-5), AipsError); } } } cout << "--- right-shift by 1 channel in first axis with flags all not set -----------------------" << endl; whichAxis = 1; relshift = 1./10.; Array outVal; Array outFlag; Array inFlags; inFlags.assign(aflags); inFlags = True; server.fftshift(outVal, outFlag, a, inFlags, whichAxis, relshift, True, False); { for(uInt i=0; i<2; i++){ for(uInt j=0; j<10; j++){ DComplex diff = outVal(IPosition(2,i,j))-expect(IPosition(2,i,j)); cout << i << " " << j << " " << outVal(IPosition(2,i,j)) << " " << expect(IPosition(2,i,j)) << endl; cout << "flag " << i << " " << j << " " << outFlag(IPosition(2,i,j)) << " " << expflagsb(IPosition(2,i,j)) << endl; AlwaysAssert((abs(diff.real())<2E-5) && (abs(diff.imag())<2E-5), AipsError); AlwaysAssert(outFlag(IPosition(2,i,j)) == expflagsb(IPosition(2,i,j)), AipsError); } } } cout << "--- right-shift by 1 channel in first axis with flags partially set -----------------------" << endl; whichAxis = 1; relshift = 1./10.; server.fftshift(outVal, outFlag, a, aflags, whichAxis, relshift, True, False); { for(uInt i=0; i<2; i++){ for(uInt j=0; j<10; j++){ DComplex diff = outVal(IPosition(2,i,j))-expectb(IPosition(2,i,j)); cout << i << " " << j << " " << outVal(IPosition(2,i,j)) << " " << expectb(IPosition(2,i,j)) << endl; cout << "flag " << i << " " << j << " " << outFlag(IPosition(2,i,j)) << " " << expflags(IPosition(2,i,j)) << endl; AlwaysAssert((abs(diff.real())<2E-5) && (abs(diff.imag())<2E-5), AipsError); AlwaysAssert(outFlag(IPosition(2,i,j)) == expflags(IPosition(2,i,j)), AipsError); } } } cout << "--- zero shift with flags partially set -----------------------" << endl; whichAxis = 1; relshift = 0.; server.fftshift(outVal, outFlag, a, aflags, whichAxis, relshift, True, False); { for(uInt i=0; i<2; i++){ for(uInt j=0; j<10; j++){ DComplex diff = outVal(IPosition(2,i,j))-a(IPosition(2,i,j)); cout << i << " " << j << " " << outVal(IPosition(2,i,j)) << " " << a(IPosition(2,i,j)) << endl; cout << "flag " << i << " " << j << " " << outFlag(IPosition(2,i,j)) << " " << aflags(IPosition(2,i,j)) << endl; AlwaysAssert((diff.real()==0. && diff.imag()==0.), AipsError); AlwaysAssert(outFlag(IPosition(2,i,j)) == aflags(IPosition(2,i,j)), AipsError); } } } cout << "--- two consecutive left-shifts by 0.5 channels in first axis with flags not set -----------" << endl; whichAxis = 1; relshift = -0.5/10.; inFlags = True; server.fftshift(outVal, outFlag, a, inFlags, whichAxis, relshift, True, False); inVal.assign(outVal); server.fftshift(outVal, outFlag, inVal, inFlags, whichAxis, relshift, True, False); { for(uInt i=0; i<2; i++){ for(uInt j=0; j<10; j++){ DComplex diff = outVal(IPosition(2,i,j))-expectc(IPosition(2,i,j)); cout << i << " " << j << " " << outVal(IPosition(2,i,j)) << " " << expectc(IPosition(2,i,j)) << endl; cout << "flag " << i << " " << j << " " << outFlag(IPosition(2,i,j)) << " " << expflagsd(IPosition(2,i,j)) << endl; AlwaysAssert((abs(diff.real())<2E-5) && (abs(diff.imag())<2E-5), AipsError); AlwaysAssert(outFlag(IPosition(2,i,j)) == expflagsd(IPosition(2,i,j)), AipsError); } } } cout << "--- left-shift by 1 channel in first axis with flags partially set -----------" << endl; whichAxis = 1; relshift = -1./10.; server.fftshift(outVal, outFlag, a, aflags, whichAxis, relshift, True, False); { for(uInt i=0; i<2; i++){ for(uInt j=0; j<10; j++){ DComplex diff = outVal(IPosition(2,i,j))-expectd(IPosition(2,i,j)); cout << i << " " << j << " " << outVal(IPosition(2,i,j)) << " " << expectd(IPosition(2,i,j)) << endl; cout << "flag " << i << " " << j << " " << outFlag(IPosition(2,i,j)) << " " << expflagsc(IPosition(2,i,j)) << endl; AlwaysAssert((abs(diff.real())<2E-5) && (abs(diff.imag())<2E-5), AipsError); AlwaysAssert(outFlag(IPosition(2,i,j)) == expflagsc(IPosition(2,i,j)), AipsError); } } } cout << "--- left-shift by 1.5 channels in first axis with flags partially set -----------" << endl; whichAxis = 1; relshift = -1.5/10.; server.fftshift(outVal, outFlag, a, aflags, whichAxis, relshift, True, False); { for(uInt i=0; i<2; i++){ for(uInt j=0; j<10; j++){ cout << i << " " << j << " " << outVal(IPosition(2,i,j)) << endl; cout << "flag " << i << " " << j << " " << outFlag(IPosition(2,i,j)) << " " << expflagse(IPosition(2,i,j)) << endl; AlwaysAssert(outFlag(IPosition(2,i,j)) == expflagse(IPosition(2,i,j)), AipsError); } } } cout << "--- left-shift by 0.25 channels in first axis with flags partially set -----------" << endl; whichAxis = 1; relshift = -0.25/10.; server.fftshift(outVal, outFlag, a, aflags2, whichAxis, relshift, True, False); { for(uInt i=0; i<2; i++){ for(uInt j=0; j<10; j++){ cout << i << " " << j << " " << outVal(IPosition(2,i,j)) << endl; cout << "flag " << i << " " << j << " " << outFlag(IPosition(2,i,j)) << " " << expflagse2(IPosition(2,i,j)) << endl; AlwaysAssert(outFlag(IPosition(2,i,j)) == expflagse2(IPosition(2,i,j)), AipsError); } } } cout << "--- left-shift by 0.5 channels in first axis with flags partially set -----------" << endl; whichAxis = 1; relshift = -0.5/10.; server.fftshift(outVal, outFlag, a, aflags2, whichAxis, relshift, True, False); { for(uInt i=0; i<2; i++){ for(uInt j=0; j<10; j++){ cout << i << " " << j << " " << outVal(IPosition(2,i,j)) << endl; cout << "flag " << i << " " << j << " " << outFlag(IPosition(2,i,j)) << " " << expflagse2(IPosition(2,i,j)) << endl; AlwaysAssert(outFlag(IPosition(2,i,j)) == expflagse2(IPosition(2,i,j)), AipsError); } } } cout << "--- left-shift by 0.55 channels in first axis with flags partially set -----------" << endl; whichAxis = 1; relshift = -0.55/10.; server.fftshift(outVal, outFlag, a, aflags2, whichAxis, relshift, True, False); { for(uInt i=0; i<2; i++){ for(uInt j=0; j<10; j++){ cout << i << " " << j << " " << outVal(IPosition(2,i,j)) << endl; cout << "flag " << i << " " << j << " " << outFlag(IPosition(2,i,j)) << " " << expflagse2(IPosition(2,i,j)) << endl; AlwaysAssert(outFlag(IPosition(2,i,j)) == expflagse2(IPosition(2,i,j)), AipsError); } } } cout << "--- left-shift by 0.75 channels in first axis with flags partially set -----------" << endl; whichAxis = 1; relshift = -0.75/10.; server.fftshift(outVal, outFlag, a, aflags2, whichAxis, relshift, True, False); { for(uInt i=0; i<2; i++){ for(uInt j=0; j<10; j++){ cout << i << " " << j << " " << outVal(IPosition(2,i,j)) << endl; cout << "flag " << i << " " << j << " " << outFlag(IPosition(2,i,j)) << " " << expflagse2(IPosition(2,i,j)) << endl; AlwaysAssert(outFlag(IPosition(2,i,j)) == expflagse2(IPosition(2,i,j)), AipsError); } } } cout << "--- right-shift by 1 channel in first axis with flags all not set, real data ----------------" << endl; whichAxis = 1; relshift = 1./10.; Array routVal; inFlags.assign(aflags); inFlags = True; server.fftshift(routVal, outFlag, ra, inFlags, whichAxis, relshift, True); { for(uInt i=0; i<2; i++){ for(uInt j=0; j<10; j++){ Double diff = routVal(IPosition(2,i,j))-rexpect(IPosition(2,i,j)); cout << i << " " << j << " " << routVal(IPosition(2,i,j)) << " " << rexpect(IPosition(2,i,j)) << endl; cout << "flag " << i << " " << j << " " << outFlag(IPosition(2,i,j)) << " " << expflagsb(IPosition(2,i,j)) << endl; AlwaysAssert((fabs(diff)<2E-5), AipsError); AlwaysAssert(outFlag(IPosition(2,i,j)) == expflagsb(IPosition(2,i,j)), AipsError); } } } cout << "--- right-shift by 1 channel in first axis with flags partially set, real data ----------------" << endl; whichAxis = 1; relshift = 1./10.; server.fftshift(routVal, outFlag, ra, aflags, whichAxis, relshift, True); { for(uInt i=0; i<2; i++){ for(uInt j=0; j<10; j++){ Double diff = routVal(IPosition(2,i,j))-rexpectb(IPosition(2,i,j)); cout << i << " " << j << " " << routVal(IPosition(2,i,j)) << " " << rexpectb(IPosition(2,i,j)) << endl; cout << "flag " << i << " " << j << " " << outFlag(IPosition(2,i,j)) << " " << expflags(IPosition(2,i,j)) << endl; AlwaysAssert(fabs(diff)<2E-5, AipsError); AlwaysAssert(outFlag(IPosition(2,i,j)) == expflags(IPosition(2,i,j)), AipsError); } } } } // enf for it=0 } }; template class P, class TServer, class SServer> class TestR2C // real->complex and complex->real { public: TestR2C(FFTServer &server, double epsilon = FLT_EPSILON) { Array input = P::input(); Array expectedResult = P::expectedResult(); Test (server, False, epsilon, input, expectedResult); Test (server, True , epsilon, shift(input, input.shape(), expectedResult.shape()), shift(expectedResult, input.shape(), expectedResult.shape())); // Test the non-default constInput = True Bool constInput = True; Array input_before; Array whatever(expectedResult.shape()); input_before = input; server.fft(whatever, input, constInput); AlwaysTrue(allNearAbs(input, input_before, epsilon), AipsError); server.fft0(whatever, input, constInput); AlwaysTrue(allNearAbs(input, input_before, epsilon), AipsError); } }; template class P, class TServer, class SServer> class TestC2C { private: void go(FFTServer &server, Bool shifted, double epsilon1, double epsilon2, const Array &input, const Array &expected) { Test (server, shifted, epsilon1, input, expected); // For complex arrays, excersize in-place transform { Array copy; // Note: copy constructor would give just a reference, not a copy! // Assignement operator creates a true copy! copy = input; if (shifted) { server.fft(copy); } else { server.fft0(copy); } AlwaysTrue(copy.shape().isEqual(expected.shape()), AipsError); AlwaysTrue(allNearAbs(copy, expected, epsilon1), AipsError); // ... and inverse in-place if (debug) { cout << "Input:" << endl; dump(copy); } if (shifted) { server.fft(copy, False); } else { server.fft0(copy, False); } if (debug) { cout << "Result:" << endl; dump(copy); cout << "Expected:" << endl; dump(input); } AlwaysTrue(copy.shape().isEqual(input.shape()), AipsError); AlwaysTrue(allNearAbs(copy, input, 2*epsilon1), AipsError); } // For C2C, excercise inverse transformations too Array p1, p2; if (shifted) { server.fft(p1, expected, False); server.fft(p2, p1, False); } else { server.fft0(p1, expected, False); server.fft0(p2, p1, False); } // Now input should be the forward transform of p2 Test (server, shifted, epsilon2, p2, input); } public: TestC2C(FFTServer &server, double epsilon1 = FLT_EPSILON, double epsilon2 = FLT_EPSILON) { Array input = P::input(); Array expectedResult = P::expectedResult(); go(server, False, epsilon1, epsilon2, input, expectedResult); // repeat using shifted arrays go(server, True, epsilon1, epsilon2, shift(input , input.shape(), expectedResult.shape()), shift(expectedResult, input.shape(), expectedResult.shape())); } }; template void run_tests() { FFTServer server0(IPosition(1,8)); TestR2C t1(server0); TestR2C t2(server0); TestR2C t3(server0); TestR2C t4(server0); TestR2C t5(server0); TestR2C t6(server0); TestR2C t7(server0); TestR2C t8(server0); TestR2C t9(server0); #ifdef HAVE_FFTW3 FFTServer server(IPosition(1,8)); server = server0; // test assignment #else FFTServer server(server0); #endif TestR2C t10(server); TestR2C t11(server); TestR2C t12(server); TestR2C t13(server); TestR2C t14(server); TestR2C t15(server); TestR2C t16(server); TestR2C t17(server); TestR2C t18(server); TestR2C t19(server); TestR2C t20(server, 100*FLT_EPSILON); TestR2C t21(server); TestR2C t22(server, 500*FLT_EPSILON); TestR2C c2r1(server); TestR2C c2r2(server); TestR2C c2r3(server); TestR2C c2r4(server); TestR2C c2r5(server); TestR2C c2r6(server); TestR2C c2r7(server); TestR2C c2r8(server); TestR2C c2r9(server); TestR2C c2r10(server, 4*FLT_EPSILON); TestR2C c2r11(server); TestR2C c2r12(server); TestR2C c2r13(server, 500*FLT_EPSILON); TestR2C c2r14(server); TestR2C c2r15(server); TestR2C c2r16(server); TestR2C c2r17(server); TestR2C c2r18(server); TestR2C c2r19(server); TestR2C c2r20(server); TestR2C c2r21(server); TestR2C c2r22(server); TestR2C c2r23(server); TestC2C c2c1(server); TestC2C c2c2(server); TestC2C c2c3(server); TestC2C c2c4(server); TestC2C c2c5(server, FLT_EPSILON, 5*FLT_EPSILON); TestC2C c2c6(server, 5*FLT_EPSILON, 5*FLT_EPSILON); TestC2C c2c7(server); TestC2C c2c8(server); TestC2C c2c9(server, 5*FLT_EPSILON); TestC2C c2c10(server); TestC2C c2c11(server); TestC2C c2c12(server); TestC2C c2c13(server); TestC2C c2c14(server); TestC2C c2c15(server); TestC2C c2c16(server); TestC2C c2c17(server); TestC2C c2c18(server, FLT_EPSILON, 2*FLT_EPSILON); TestC2C c2c19(server, 100*FLT_EPSILON, 2*FLT_EPSILON); TestC2C c2c20(server, FLT_EPSILON, 2*FLT_EPSILON); TestC2C c2c21(server, 500*FLT_EPSILON, 2*FLT_EPSILON); TestFFTShift (); return; } int main() { tests_done = 0; try { // Test combinations of // - single/double precision // - real->complex, complex->real, complex->complex // - forward / inverse // - 1d, 2d, 3d, 4d data // - even/odd dimensions // - fft() / fft0() // - inplace / copy // - const / non-const input run_tests(); run_tests(); } catch (std::exception& x) { cerr << x.what() << endl; cout << "FAIL" << endl; return 1; } cout << tests_done << " tests OK" << endl; return 0; } casacore-3.7.1/scimath/Mathematics/test/tFFTServer2.cc000066400000000000000000001355571476623553700225750ustar00rootroot00000000000000//# tFFTServer: This program tests the FFTServer and FourierTool classes //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include // #include #include #include #include #include #include #include #include #include #include #include #include int main() { try { FFTServer server(IPosition(1,8)); { // 1-D real->complex FFT's on an even length Vector input(8); input = 0.0f; input(0) = 1.0f; Vector result, expectedResult(5); server.fft0(result, input); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); input(2) = -1.0f; input(4) = 1.0f; input(6) = -1.0f; expectedResult = Complex(0,0); expectedResult(2) = Complex(4,0); server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); input = 0.0f; input(1) = 1.0f; input(3) = -1.0f; input(5) = 1.0f; input(7) = -1.0f; expectedResult = Complex(0,0); expectedResult(2) = Complex(0,-4); server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); input = 0.0f; input(1) = 1.0f; input(3) = 1.0f; input(5) = 1.0f; input(7) = 1.0f; expectedResult = Complex(0,0); expectedResult(0) = Complex(4,0); expectedResult(4) = Complex(-4,0); server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); } { // 1-D real->complex FFT's on an odd length Vector input(9); input = 0.0f; input(0) = 1.0f; Vector result, expectedResult(5); server.fft0(result, input); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); input = 1.0f; expectedResult = Complex(0,0); expectedResult(0) = Complex(9,0); server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); input(1) = 0.0f; input(3) = 0.0f; input(5) = 0.0f; input(7) = 0.0f; expectedResult = Complex(0,0); expectedResult(0) = Complex(5,0); server.fft0(result, input); AlwaysAssert(near(result(0), Complex(5,0), FLT_EPSILON), AipsError); AlwaysAssert(!near(result(4).imag(), 0.0f, FLT_EPSILON), AipsError); } { // 2-D real->complex FFT's on an even/even length Matrix input(4,6); input = 0.0f; input(0,0) = 1.0f; Matrix result, expectedResult(3,6); server.fft0(result, input); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); input = 1.0f; expectedResult = Complex(0,0); expectedResult(0,0) = Complex(24,0); server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); input = 0.0f; input(1,1) = 1.0f; input(1,3) = 1.0f; input(1,5) = 1.0f; expectedResult = Complex(0,0); expectedResult(0,0) = expectedResult(2,3) = Complex(3,0); expectedResult(0,3) = expectedResult(2,0) = Complex(-3,0); expectedResult(1,3) = Complex(0,3); expectedResult(1,0) = Complex(0,-3); server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, 2.0f*FLT_EPSILON), AipsError); } { // 2-D real->complex FFT's on an even/odd length Matrix input(4,5); input = 0.0f; input(0,0) = 1.0f; Matrix result, expectedResult(3,5); server.fft0(result, input); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); input = 1.0f; expectedResult = Complex(0,0); expectedResult(0,0) = Complex(20,0); server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); } { // 2-D real->complex FFT's on an odd/even length Matrix input(3,6); input = 0.0f; input(0,0) = 1.0f; Matrix result, expectedResult(2,6); server.fft0(result, input); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); input = 1.0f; expectedResult = Complex(0,0); expectedResult(0,0) = Complex(18,0); server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); } { // 2-D real->complex FFT's on an odd/odd length Matrix input(3,5); input = 0.0f; input(0,0) = 1.0f; Matrix result, expectedResult(2,5); server.fft0(result, input); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); input = 1.0f; expectedResult = Complex(0,0); expectedResult(0,0) = Complex(15,0); server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); } { // 3-D real->complex FFT's on an even/even/even length Cube input(4,6,8); input = 0.0f; input(0,0,0) = 1.0f; Cube result, expectedResult(3,6,8); server.fft0(result, input); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); input = 1.0f; expectedResult = Complex(0,0); expectedResult(0,0,0) = Complex(4*6*8,0); server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); } { // 3-D real->complex FFT's on an odd/odd/odd length Cube input(3,5,7); input = 0.0f; input(0,0,0) = 1.0f; Cube result, expectedResult(2,5,7); server.fft0(result, input); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); input = 1.0f; expectedResult = Complex(0,0); expectedResult(0,0,0) = Complex(3*5*7,0); server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, 100*FLT_EPSILON), AipsError); } { // 4-D real->complex FFT's on an odd/odd/odd/even length Array input(IPosition(4,3,5,7,4)); input = 0.0f; input(IPosition(4,0)) = 1.0f; Array result, expectedResult(IPosition(4,2,5,7,4)); server.fft0(result, input); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); input = 1.0f; expectedResult = Complex(0,0); expectedResult(IPosition(4,0)) = Complex(3*5*7*4,0); server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, 500*FLT_EPSILON), AipsError); } { // 1-D complex->real FFT's on an even length Vector input(5); input = Complex(0.0f, 0.0f); input(0) = Complex(8.0f, 0.0f); Vector result, expectedResult(8); server.fft0(result, input); expectedResult = 1.0f; AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); input(0) = Complex(0.0f, 0.0f); input(0) = Complex(16.0f, 0.0f); input(2) = Complex(8.0f, 0.0f); expectedResult = 2.0f; expectedResult(0) = 4.0f; expectedResult(2) = 0.0f; expectedResult(4) = 4.0f; expectedResult(6) = 0.0f; server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); input(0) = Complex(0.0f, 0.0f); input(2) = Complex(0.0f, 4.0f); expectedResult = 0.0f; expectedResult(1) = -1.0f; expectedResult(3) = 1.0f; expectedResult(5) = -1.0f; expectedResult(7) = 1.0f; server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); input = Complex(1.0f, 0.0f); expectedResult = 0.0f; expectedResult(0) = 1.0f; server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); input(1) = Complex(0,0); input(3) = Complex(0,0); expectedResult = 0.0f; expectedResult(0) = 0.5f; expectedResult(4) = 0.5f; server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); } { // 1-D complex->real FFT's on an odd length Vector input(5); input = Complex(0,0); input(0) = Complex(9,0); Vector result(9), expectedResult(9); server.fft0(result, input); expectedResult = 1.0f; AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); input = Complex(1,0); expectedResult = 0.0f; expectedResult(0) = 1.0f; server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); result.resize(0); server.fft0(result, input); AlwaysAssert(result.nelements() == 9, AipsError); result.resize(0); server.resize(IPosition(1,8)); server.fft0(result, input); AlwaysAssert(result.nelements() == 8, AipsError); result.resize(0); server.resize(IPosition(1,7)); server.fft0(result, input); AlwaysAssert(result.nelements() == 8, AipsError); result.resize(0); input(4) = Complex(1,1); server.fft0(result, input); AlwaysAssert(result.nelements() == 9, AipsError); } { // 2-D complex->real FFT's on an even/even length Matrix input(3,6); input = Complex(0,0); input(0,0) = Complex(4*6,0); Matrix result(4,6), expectedResult(4,6); server.fft0(result, input); expectedResult =1.0f; AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); input = Complex(1,0); expectedResult = 0.0f; expectedResult(0,0) = 1.0f; server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); input = Complex(0,0); input(0,0) = Complex(24,0); input(2,0) = Complex(-24,0); input(0,1) = Complex(-24,0); input(0,2) = Complex( 24,0); input(0,3) = Complex(-24,0); input(0,4) = Complex( 24,0); input(0,5) = Complex(-24,0); expectedResult = 0.0f; expectedResult(0,0) = expectedResult(0,1) = expectedResult(0,2) = expectedResult(0,4) = expectedResult(0,5) = -1.0f; expectedResult(0,3) = 5.0f; expectedResult(1,0) = expectedResult(1,1) = expectedResult(1,2) = expectedResult(1,4) = expectedResult(1,5) = 1.0f; expectedResult(1,3) = 7.0f; expectedResult(2,0) = expectedResult(2,1) = expectedResult(2,2) = expectedResult(2,4) = expectedResult(2,5) = -1.0f; expectedResult(2,3) = 5.0f; expectedResult(3,0) = expectedResult(3,1) = expectedResult(3,2) = expectedResult(3,4) = expectedResult(3,5) = 1.0f; expectedResult(3,3) = 7.0f; server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, 4.0f*FLT_EPSILON), AipsError); input = 0.0f; input(2,5) = Complex(0,24); result.resize(0,0); server.fft0(result, input); AlwaysAssert(result.shape().isEqual(IPosition(2,5,6)), AipsError); } { // 2-D complex->real FFT's on an odd/odd length Matrix input(2,5); input = Complex(0,0); input(0,0) = Complex(3*5,0); Matrix result(3,5), expectedResult(3,5); server.fft0(result, input); expectedResult =1.0f; AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); input = Complex(1,0); expectedResult = 0.0f; expectedResult(0,0) = 1.0f; server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); input = Complex(0,0); input(1,0) = Complex(0,45); input(1,1) = Complex(0,45); input(1,2) = Complex(0,45); input(1,3) = Complex(0,45); input(1,4) = Complex(0,45); expectedResult = 0.0f; expectedResult(1,0) = -25.9808f; expectedResult(2,0) = 25.9808f; server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, 500*FLT_EPSILON), AipsError); } { // 2-D complex->real FFT's on an even/odd length Matrix input(3,5); input = Complex(0,0); input(0,0) = Complex(4*5,0); Matrix result(4,5), expectedResult(4,5); server.fft0(result, input); expectedResult = 1.0f; AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); input = Complex(1,0); expectedResult = 0.0f; expectedResult(0,0) = 1.0f; server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); } { // 2-D complex->real FFT's on an odd/even length Matrix input(2,6); input = Complex(0,0); input(0,0) = Complex(3*6,0); Matrix result(3,6), expectedResult(3,6); server.fft0(result, input); expectedResult = 1.0f; AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); input = Complex(1,0); expectedResult = 0.0f; expectedResult(0,0) = 1.0f; server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); } { // 3-D complex->real FFT's on an even/even/even length Cube input(3,6,2); input = Complex(0,0); input(0,0,0) = Complex(4*6*2,0); Cube result(4,6,2), expectedResult(4,6,2); server.fft0(result, input); expectedResult = 1.0f; AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); input = Complex(1,0); expectedResult = 0.0f; expectedResult(0,0,0) = 1.0f; server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); } { // 3-D complex->real FFT's on an odd/odd/odd length Cube input(2,5,7); input = Complex(0,0); input(0,0,0) = Complex(3*5*7,0); Cube result(3,5,7), expectedResult(3,5,7); server.fft0(result, input); expectedResult = 1.0f; AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); input = Complex(1,0); expectedResult = 0.0f; expectedResult(0,0,0) = 1.0f; server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); } { // 4-D complex->real FFT's on an odd/odd/odd/even length Array input(IPosition(4,2,5,7,2)); input = Complex(0,0); input(IPosition(4,0)) = Complex(3*5*7*2,0); Array result(IPosition(4,3,5,7,2)); Array expectedResult(IPosition(4,3,5,7,2)); server.fft0(result, input); expectedResult = 1.0f; AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); input = Complex(1,0); expectedResult = 0.0f; expectedResult(IPosition(4,0)) = 1.0f; server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); } { // 1-D complex->complex FFT's on an even length Vector input(8); input = Complex(0, 0); input(0) = Complex(1.0f, 0.0f); Vector result, expectedResult(8); server.fft0(result, input, True); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft0(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, FLT_EPSILON), AipsError); input = Complex(1, 0); expectedResult = Complex(0,0); expectedResult(0) = Complex(8,0); server.fft0(result, input, True); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft0(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, FLT_EPSILON), AipsError); input = Complex(-1, 0); input(0) = Complex(1, 0); input(2) = Complex(1, 0); input(4) = Complex(1, 0); input(6) = Complex(1, 0); expectedResult = Complex(0,0); expectedResult(4) = Complex(8,0); server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft0(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, FLT_EPSILON), AipsError); input = Complex(0, 0); input(1) = Complex(1, 0); input(3) = Complex(-1,0); input(5) = Complex(1, 0); input(7) = Complex(-1,0); expectedResult = Complex(0,0); expectedResult(2) = Complex(0,-4); expectedResult(6) = Complex(0,4); server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft0(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, FLT_EPSILON), AipsError); } { // 1-D complex->complex FFT's on an odd length Vector input(7); input = Complex(0, 0); input(0) = Complex(1.0f, 0.0f); Vector result, expectedResult(7); server.fft0(result, input, True); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft0(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, FLT_EPSILON), AipsError); input = Complex(1, 0); expectedResult = Complex(0,0); expectedResult(0) = Complex(7,0); server.fft0(result, input, True); AlwaysAssert(allNearAbs(result, expectedResult,5*FLT_EPSILON), AipsError); expectedResult = input; server.fft0(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, 5*FLT_EPSILON), AipsError); } { // 2-D complex->complex FFT's on an even/even length Matrix input(4,6); input = Complex(0,0); input(0,0) = Complex(1,0); Matrix result, expectedResult(4,6); server.fft0(result, input); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft0(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, FLT_EPSILON), AipsError); input = Complex(1,0); expectedResult = Complex(0,0); expectedResult(0,0) = Complex(24,0); server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft0(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, FLT_EPSILON), AipsError); input = Complex(0,0); input(1,1) = Complex(1,1); input(1,3) = Complex(1,1); input(1,5) = Complex(1,1); expectedResult = Complex(0,0); expectedResult(0,0) = expectedResult(2,3) = Complex(3,3); expectedResult(0,3) = expectedResult(2,0) = Complex(-3,-3); expectedResult(1,3) = expectedResult(3,0) = Complex(-3,3); expectedResult(1,0) = expectedResult(3,3) = Complex(3,-3); server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, 5*FLT_EPSILON), AipsError); expectedResult = input; server.fft0(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, FLT_EPSILON), AipsError); } { // 2-D complex->complex FFT's on an odd/odd length Matrix input(3,5); input = Complex(0,0); input(0,0) = Complex(1,0); Matrix result, expectedResult(3,5); server.fft0(result, input); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft0(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, FLT_EPSILON), AipsError); input = Complex(1,0); expectedResult = Complex(0,0); expectedResult(0,0) = Complex(15,0); server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft0(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, FLT_EPSILON), AipsError); } { // 2-D complex->complex FFT's on an even/odd length Matrix input(4,5); input = Complex(0,0); input(0,0) = Complex(1,0); Matrix result, expectedResult(4,5); server.fft0(result, input); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft0(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, FLT_EPSILON), AipsError); input = Complex(1,0); expectedResult = Complex(0,0); expectedResult(0,0) = Complex(20,0); server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft0(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, FLT_EPSILON), AipsError); } { // 2-D complex->complex FFT's on an odd/even length Matrix input(3,6); input = Complex(0,0); input(0,0) = Complex(1,0); Matrix result, expectedResult(3,6); server.fft0(result, input); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft0(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, FLT_EPSILON), AipsError); input = Complex(1,0); expectedResult = Complex(0,0); expectedResult(0,0) = Complex(18,0); server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft0(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, FLT_EPSILON), AipsError); } { // 3-D complex->complex FFT's on an even/even/even length Cube input(4,6,8); input = Complex(0,0); input(0,0,0) = Complex(1,0); Cube result, expectedResult(4,6,8); server.fft0(result, input); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft0(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, FLT_EPSILON), AipsError); input = Complex(1,0); expectedResult = Complex(0,0); expectedResult(0,0,0) = Complex(4*6*8,0); server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft0(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, FLT_EPSILON), AipsError); } { // 3-D complex->complex FFT's on an odd/odd/odd length Cube input(3,5,7); input = Complex(0,0); input(0,0,0) = Complex(1,0); Cube result, expectedResult(3,5,7); server.fft0(result, input); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft0(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, 2*FLT_EPSILON), AipsError); input = Complex(1,0); expectedResult = Complex(0,0); expectedResult(0,0,0) = Complex(3*5*7,0); server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, 100*FLT_EPSILON), AipsError); expectedResult = input; server.fft0(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, 2*FLT_EPSILON), AipsError); } { // 4-D complex->complex FFT's on an odd/odd/odd/even length Array input(IPosition(4,3,5,7,4)); input = Complex(0,0); input(IPosition(4,0)) = Complex(1,0); Array result, expectedResult(IPosition(4,3,5,7,4)); server.fft0(result, input); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft0(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, 2*FLT_EPSILON), AipsError); input = Complex(1,0); expectedResult = Complex(0,0); expectedResult(IPosition(4,0)) = Complex(3*5*7*4,0); server.fft0(result, input); AlwaysAssert(allNearAbs(result, expectedResult, 500*FLT_EPSILON), AipsError); expectedResult = input; server.fft0(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, 2*FLT_EPSILON), AipsError); } { // 1-D complex->complex FFT's on an even length (origin at the centre) Vector input(8); input = Complex(0, 0); input(4) = Complex(1.0f, 0.0f); Vector result, expectedResult(8); server.fft(result, input, True); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, FLT_EPSILON), AipsError); input = Complex(1, 0); expectedResult = Complex(0,0); expectedResult(4) = Complex(8,0); server.fft(result, input, True); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, FLT_EPSILON), AipsError); input = Complex(-1, 0); input(0) = Complex(1, 0); input(2) = Complex(1, 0); input(4) = Complex(1, 0); input(6) = Complex(1, 0); expectedResult = Complex(0,0); expectedResult(0) = Complex(8,0); server.fft(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, FLT_EPSILON), AipsError); input = Complex(0, 0); input(1) = Complex(1, 0); input(3) = Complex(-1,0); input(5) = Complex(1, 0); input(7) = Complex(-1,0); expectedResult = Complex(0,0); expectedResult(2) = Complex(0,4); expectedResult(6) = Complex(0,-4); server.fft(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, FLT_EPSILON), AipsError); } { // 1-D complex->complex FFT's on an odd length (origin at the centre) Vector input(7); input = Complex(0, 0); input(3) = Complex(1.0f, 0.0f); Vector result, expectedResult(7); server.fft(result, input, True); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, FLT_EPSILON), AipsError); input = Complex(1, 0); expectedResult = Complex(0,0); expectedResult(3) = Complex(7,0); server.fft(result, input, True); AlwaysAssert(allNearAbs(result, expectedResult, 5*FLT_EPSILON), AipsError); expectedResult = input; server.fft(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, 5*FLT_EPSILON), AipsError); } { // 2-D complex->complex FFT's on an even/even length (origin at centre) Matrix input(4,6); input = Complex(0,0); input(2,3) = Complex(1,0); Matrix result, expectedResult(4,6); server.fft(result, input); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, FLT_EPSILON), AipsError); input = Complex(1,0); expectedResult = Complex(0,0); expectedResult(2,3) = Complex(24,0); server.fft(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, FLT_EPSILON), AipsError); input = Complex(0,0); input(3,1) = Complex(1,1); input(3,3) = Complex(1,1); input(3,5) = Complex(1,1); expectedResult = Complex(0,0); expectedResult(2,3) = expectedResult(2,0) = Complex(3,3); expectedResult(0,0) = expectedResult(0,3) = Complex(-3,-3); expectedResult(1,0) = expectedResult(1,3) = Complex(-3,3); expectedResult(3,3) = expectedResult(3,0) = Complex(3,-3); server.fft(result, input); AlwaysAssert(allNearAbs(result, expectedResult, 5*FLT_EPSILON), AipsError); expectedResult = input; server.fft(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, FLT_EPSILON), AipsError); } { // 2-D complex->complex FFT's on an odd/odd length (origin at centre) Matrix input(3,5); input = Complex(0,0); input(1,2) = Complex(1,0); Matrix result, expectedResult(3,5); server.fft(result, input); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, FLT_EPSILON), AipsError); input = Complex(1,0); expectedResult = Complex(0,0); expectedResult(1,2) = Complex(15,0); server.fft(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, FLT_EPSILON), AipsError); } { // 2-D complex->complex FFT's on an even/odd length (origin at centre) Matrix input(4,5); input = Complex(0,0); input(2,2) = Complex(1,0); Matrix result, expectedResult(4,5); server.fft(result, input); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, FLT_EPSILON), AipsError); input = Complex(1,0); expectedResult = Complex(0,0); expectedResult(2,2) = Complex(20,0); server.fft(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, FLT_EPSILON), AipsError); } { // 2-D complex->complex FFT's on an odd/even length (origin at centre) Matrix input(3,6); input = Complex(0,0); input(1,3) = Complex(1,0); Matrix result, expectedResult(3,6); server.fft(result, input); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, FLT_EPSILON), AipsError); input = Complex(1,0); expectedResult = Complex(0,0); expectedResult(1,3) = Complex(18,0); server.fft(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, FLT_EPSILON), AipsError); } { // 3-D complex->complex FFT's on an even/even/even len (origin at centre) Cube input(4,6,8); input = Complex(0,0); input(2,3,4) = Complex(1,0); Cube result, expectedResult(4,6,8); server.fft(result, input); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, FLT_EPSILON), AipsError); input = Complex(1,0); expectedResult = Complex(0,0); expectedResult(2,3,4) = Complex(4*6*8,0); server.fft(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, FLT_EPSILON), AipsError); } { // 3-D complex->complex FFT's on an odd/odd/odd length (origin at centre) Cube input(3,5,7); input = Complex(0,0); input(1,2,3) = Complex(1,0); Cube result, expectedResult(3,5,7); server.fft(result, input); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, 2*FLT_EPSILON), AipsError); input = Complex(1,0); expectedResult = Complex(0,0); expectedResult(1,2,3) = Complex(3*5*7,0); server.fft(result, input); AlwaysAssert(allNearAbs(result, expectedResult, 100*FLT_EPSILON), AipsError); expectedResult = input; server.fft(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, 2*FLT_EPSILON), AipsError); } { // 4-D complex->complex FFT's on an odd/odd/odd/even len (orig at centre) Array input(IPosition(4,3,5,7,4)); input = Complex(0,0); input(IPosition(4,1,2,3,2)) = Complex(1,0); Array result, expectedResult(IPosition(4,3,5,7,4)); server.fft(result, input); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); expectedResult = input; server.fft(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, 2*FLT_EPSILON), AipsError); input = Complex(1,0); expectedResult = Complex(0,0); expectedResult(IPosition(4,1,2,3,2)) = Complex(3*5*7*4,0); server.fft(result, input); AlwaysAssert(allNearAbs(result, expectedResult, 500*FLT_EPSILON), AipsError); expectedResult = input; server.fft(input, result, False); AlwaysAssert(allNearAbs(input, expectedResult, 2*FLT_EPSILON), AipsError); } { // 1-D real<->complex FFT's on an even length (orig at centre) Vector input(8); input = 0.0f; input(4) = 1.0f; Vector result, expectedResult(5); server.fft(result, input, True); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); Vector reverseTransform; server.fft(reverseTransform, result); AlwaysAssert(allNearAbs(input, reverseTransform, FLT_EPSILON), AipsError); input(2) = -1.0f; input(0) = 1.0f; input(6) = -1.0f; expectedResult = Complex(0,0); expectedResult(2) = Complex(4,0); server.fft(result, input, True); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); server.fft(reverseTransform, result); AlwaysAssert(allNearAbs(input, reverseTransform, FLT_EPSILON), AipsError); input = 0.0f; input(1) = 1.0f; input(3) = -1.0f; input(5) = 1.0f; input(7) = -1.0f; expectedResult = Complex(0,0); expectedResult(2) = Complex(0,-4); server.fft(result, input, True); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); server.fft(reverseTransform, result); AlwaysAssert(allNearAbs(input, reverseTransform, FLT_EPSILON), AipsError); input = 0.0f; input(1) = 1.0f; input(3) = 1.0f; input(5) = 1.0f; input(7) = 1.0f; expectedResult = Complex(0,0); expectedResult(0) = Complex(4,0); expectedResult(4) = Complex(-4,0); server.fft(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); server.fft(reverseTransform, result); AlwaysAssert(allNearAbs(input, reverseTransform, FLT_EPSILON), AipsError); } { // 1-D real<->complex FFT's on an odd length (orig at centre) Vector input(9); input = 0.0f; input(4) = 1.0f; Vector result, expectedResult(5); server.fft(result, input, True); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); Vector reverseTransform; server.fft(reverseTransform, result); AlwaysAssert(allNearAbs(input, reverseTransform, FLT_EPSILON), AipsError); input = 1.0f; expectedResult = Complex(0,0); expectedResult(0) = Complex(9,0); server.fft(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); server.fft(reverseTransform, result); AlwaysAssert(allNearAbs(input, reverseTransform, FLT_EPSILON), AipsError); input(1) = 0.0f; input(3) = 0.0f; input(5) = 0.0f; input(7) = 0.0f; expectedResult = Complex(0,0); expectedResult(0) = Complex(5,0); server.fft(result, input, True); AlwaysAssert(near(result(0), Complex(5,0), FLT_EPSILON), AipsError); AlwaysAssert(!near(result(4).imag(), 0.0f, FLT_EPSILON), AipsError); server.fft(reverseTransform, result); AlwaysAssert(allNearAbs(input, reverseTransform, FLT_EPSILON), AipsError); } { // 2-D real<->complex FFT's on an even/even length (orig at centre) Matrix input(4,6); input = 0.0f; input(2,3) = 1.0f; Matrix result, expectedResult(3,6); server.fft(result, input, True); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); Matrix reverseTransform; server.fft(reverseTransform, result); AlwaysAssert(allNearAbs(input, reverseTransform, FLT_EPSILON), AipsError); input = 1.0f; expectedResult = Complex(0,0); expectedResult(0,3) = Complex(24,0); server.fft(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); server.fft(reverseTransform, result); AlwaysAssert(allNearAbs(input, reverseTransform, FLT_EPSILON), AipsError); input = 0.0f; input(3,4) = 1.0f; input(3,0) = 1.0f; input(3,2) = 1.0f; expectedResult = Complex(0,0); expectedResult(0,3) = expectedResult(2,0) = Complex(3,0); expectedResult(0,0) = expectedResult(2,3) = Complex(-3,0); expectedResult(1,0) = Complex(0,3); expectedResult(1,3) = Complex(0,-3); server.fft(result, input, True); AlwaysAssert(allNearAbs(result, expectedResult, 2.0f*FLT_EPSILON), AipsError); server.fft(reverseTransform, result); AlwaysAssert(allNearAbs(input, reverseTransform, FLT_EPSILON), AipsError); } { // 2-D real<->complex FFT's on an even/odd length (orig at centre) Matrix input(4,5); input = 0.0f; input(2,2) = 1.0f; Matrix result, expectedResult(3,5); server.fft(result, input, True); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); Matrix reverseTransform; server.fft(reverseTransform, result); AlwaysAssert(allNearAbs(input, reverseTransform, FLT_EPSILON), AipsError); input = 1.0f; expectedResult = Complex(0,0); expectedResult(0,2) = Complex(20,0); server.fft(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); server.fft(reverseTransform, result); AlwaysAssert(allNearAbs(input, reverseTransform, FLT_EPSILON), AipsError); } { // 2-D real<->complex FFT's on an odd/even length (orig at centre) Matrix input(3,6); input = 0.0f; input(1,3) = 1.0f; Matrix result, expectedResult(2,6); server.fft(result, input, True); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); Matrix reverseTransform; server.fft(reverseTransform, result); AlwaysAssert(allNearAbs(input, reverseTransform, FLT_EPSILON), AipsError); input = 1.0f; expectedResult = Complex(0,0); expectedResult(0,3) = Complex(18,0); server.fft(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); server.fft(reverseTransform, result); AlwaysAssert(allNearAbs(input, reverseTransform, FLT_EPSILON), AipsError); } { // 2-D real<->complex FFT's on an odd/odd length (orig at centre) Matrix input(3,5); input = 0.0f; input(1,2) = 1.0f; Matrix result, expectedResult(2,5); server.fft(result, input, True); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); Matrix reverseTransform; server.fft(reverseTransform, result); AlwaysAssert(allNearAbs(input, reverseTransform, FLT_EPSILON), AipsError); input = 1.0f; expectedResult = Complex(0,0); expectedResult(0,2) = Complex(15,0); server.fft(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); server.fft(reverseTransform, result); AlwaysAssert(allNearAbs(input, reverseTransform, FLT_EPSILON), AipsError); } { // 3-D real<->complex FFT's on an even/even/even length (orig at centre) Cube input(4,6,8); input = 0.0f; input(2,3,4) = 1.0f; Cube result, expectedResult(3,6,8); server.fft(result, input, True); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); Cube reverseTransform; server.fft(reverseTransform, result); AlwaysAssert(allNearAbs(input, reverseTransform, FLT_EPSILON), AipsError); input = 1.0f; expectedResult = Complex(0,0); expectedResult(0,3,4) = Complex(4*6*8,0); server.fft(result, input); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); server.fft(reverseTransform, result); AlwaysAssert(allNearAbs(input, reverseTransform, FLT_EPSILON), AipsError); } { // 3-D real<->complex FFT's on an odd/odd/odd length (orig at centre) Cube input(3,5,7); input = 0.0f; input(1,2,3) = 1.0f; Cube result, expectedResult(2,5,7); server.fft(result, input, True); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); Cube reverseTransform; server.fft(reverseTransform, result); AlwaysAssert(allNearAbs(input, reverseTransform, FLT_EPSILON), AipsError); input = 1.0f; expectedResult = Complex(0,0); expectedResult(0,2,3) = Complex(3*5*7,0); server.fft(result, input); AlwaysAssert(allNearAbs(result, expectedResult, 100*FLT_EPSILON), AipsError); server.fft(reverseTransform, result); AlwaysAssert(allNearAbs(input, reverseTransform, 5*FLT_EPSILON), AipsError); } { // 4-D real<->complex FFT's on an odd/odd/odd/even len. (orig at centre) Array input(IPosition(4,3,5,7,4)); input = 0.0f; input(IPosition(4,1,2,3,2)) = 1.0f; Array result, expectedResult(IPosition(4,2,5,7,4)); server.fft(result, input, True); expectedResult = Complex(1,0); AlwaysAssert(allNearAbs(result, expectedResult, FLT_EPSILON), AipsError); Array reverseTransform; server.fft(reverseTransform, result); AlwaysAssert(allNearAbs(input, reverseTransform, FLT_EPSILON), AipsError); input = 1.0f; expectedResult = Complex(0,0); expectedResult(IPosition(4,0,2,3,2)) = Complex(3*5*7*4,0); server.fft(result, input); AlwaysAssert(allNearAbs(result, expectedResult, 500*FLT_EPSILON), AipsError); server.fft(reverseTransform, result); AlwaysAssert(allNearAbs(input, reverseTransform, 5*FLT_EPSILON), AipsError); } } catch (std::exception& x) { cerr << x.what() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } // Local Variables: // compile-command: "gmake OPTLIB=1 tFFTServer" // End: casacore-3.7.1/scimath/Mathematics/test/tGaussianBeam.cc000066400000000000000000000167121476623553700232330ustar00rootroot00000000000000//# Copyright (C) 1996,1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include int main() { try { // null beam GaussianBeam null; AlwaysAssert(null.isNull(), AipsError); Quantity qzero(0, "rad"); AlwaysAssert(null.getMajor() == qzero, AipsError); AlwaysAssert(null.getMinor() == qzero, AipsError); AlwaysAssert(null.getPA() == qzero, AipsError); // non null beam constructor Quantity majAx(4, "arcsec"); Quantity minAx(3, "arcsec"); Quantity pa(20, "deg"); GaussianBeam beam(majAx, minAx, pa); AlwaysAssert(beam.getMajor() == majAx, AipsError); AlwaysAssert(beam.getMinor() == minAx, AipsError); AlwaysAssert(beam.getPA() == pa, AipsError); // copy constructor GaussianBeam beam2(beam); AlwaysAssert(beam2 == beam, AipsError); AlwaysAssert(beam2 != null, AipsError); // = operator beam2 = beam; AlwaysAssert(beam2 == beam, AipsError); AlwaysAssert(beam2 != null, AipsError); Bool except = False; try { // bogus units majAx = Quantity(4, "m"); GaussianBeam beam3(majAx, minAx, pa); } catch (std::exception& x) { cout << "Exception thrown as expected: " << x.what() << endl; except = True; } AlwaysAssert(except, AipsError); except = False; try { // major smaller than minor majAx = Quantity(2, "arcsec"); GaussianBeam beam3(majAx, minAx, pa); } catch (std::exception& x) { cout << "Exception thrown as expected: " << x.what() << endl; except = True; } AlwaysAssert(except, AipsError); // getArea majAx = Quantity(1, "arcsec"); minAx = majAx; beam = GaussianBeam(majAx, minAx, pa); AlwaysAssert(beam.getArea("arcsec2") == Quantity(M_PI/4/M_LN2), AipsError); except = False; try { // bogus units beam.getArea("arcsec"); } catch (std::exception& x) { cout << "Exception thrown as expected: " << x.what() << endl; except = True; } AlwaysAssert(except, AipsError); // to/from Record Record rec = beam.toRecord(); beam2 = GaussianBeam::fromRecord(rec); AlwaysAssert(beam == beam2, AipsError); except = False; try { // bogus record rec.define("bogus", 5); beam2 = GaussianBeam::fromRecord(rec); } catch (std::exception& x) { cout << "Exception thrown as expected: " << x.what() << endl; except = True; } AlwaysAssert(except, AipsError); Vector v(3); v[0] = majAx; v[1] = minAx; v[2] = pa; GaussianBeam beam3(v); AlwaysAssert(beam3 == beam, AipsError); v = beam.toVector(); GaussianBeam beam4(v); AlwaysAssert(beam4 == beam3, AipsError); { cout << "Test setPA() and getPA() using unwrapping" << endl; Double u = 60; for (Double d=-660; d<=660; d+=30, u+=30) { if (u > 90) { u -= 180; } beam.setPA(Quantity(d, "deg"), False); AlwaysAssert(beam.getPA(False).getValue("deg") == d, AipsError); AlwaysAssert(beam.getPA(True).getValue("deg") == u, AipsError); beam.setPA(Quantity(d, "deg"), True); AlwaysAssert(beam.getPA(False).getValue("deg") == u, AipsError); } } { cout << "Test NaN and Inf throw exceptions" << endl; static const Quantity inf(doubleInf(), "arcsec"); static const Quantity nan(doubleNaN(), "arcsec"); static const Quantity qok(2, "arcsec"); try { GaussianBeam badBeam(inf, qok, qok); AlwaysAssert(False, AipsError); } catch (const std::exception& x) {} try { GaussianBeam badBeam(nan, qok, qok); AlwaysAssert(False, AipsError); } catch (const std::exception& x) {} try { GaussianBeam badBeam(qok, inf, qok); AlwaysAssert(False, AipsError); } catch (const std::exception& x) {} try { GaussianBeam badBeam(qok, nan, qok); AlwaysAssert(False, AipsError); } catch (const std::exception& x) {} try { GaussianBeam badBeam(qok, qok, inf); AlwaysAssert(False, AipsError); } catch (const std::exception& x) {} try { GaussianBeam badBeam(qok, qok, nan); AlwaysAssert(False, AipsError); } catch (const std::exception& x) {} GaussianBeam bok(qok, qok, qok); try { bok.setMajorMinor(inf, qok); AlwaysAssert(False, AipsError); } catch (const std::exception& x) {} try { bok.setMajorMinor(nan, qok); AlwaysAssert(False, AipsError); } catch (const std::exception& x) {} try { bok.setMajorMinor(qok, inf); AlwaysAssert(False, AipsError); } catch (const std::exception& x) {} try { bok.setMajorMinor(qok, nan); AlwaysAssert(False, AipsError); } catch (const std::exception& x) {} try { bok.setPA(inf); AlwaysAssert(False, AipsError); } catch (const std::exception& x) {} try { bok.setPA(nan); AlwaysAssert(False, AipsError); } catch (const std::exception& x) {} } } catch (const std::exception& x) { cout << x.what() << endl; cout << "FAIL" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/scimath/Mathematics/test/tGeometry.cc000066400000000000000000000070271476623553700224660ustar00rootroot00000000000000//# Copyright (C) 1993,1994,1995,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include int main() { try { { cout << "Text rotate2D()" << endl; Double x = 1; Double y = 0; Quantity theta(30, "deg"); Quantity cumulative(30, "deg"); for (uInt i=0; i<12; i++) { std::pair rotated = Geometry::rotate2D(x, y, theta); x = rotated.first; y = rotated.second; AlwaysAssert(nearAbs(x, cos(cumulative.getValue("rad"))), AipsError); AlwaysAssert(nearAbs(y, sin(cumulative.getValue("rad"))), AipsError); cumulative += theta; } } { cout << "Test doLineSegmentsIntersect()" << endl; AlwaysAssert( ! Geometry::doLineSegmentsIntersect( 1, 1, 3, 3, 3, 1, 5, 5 ), AipsError ); AlwaysAssert( ! Geometry::doLineSegmentsIntersect( 1, 1, 3, 3, 2.1, 2, 6, 2.9 ), AipsError ); AlwaysAssert( Geometry::doLineSegmentsIntersect( 1, 1, 3, 3, 3, 1, 1, 3 ), AipsError ); // common end point => intersect AlwaysAssert( Geometry::doLineSegmentsIntersect( 1, 1, 3, 3, 1, 1, 1, 3 ), AipsError ); // end point of one segment colinear with the endpoints of the other // => intersect AlwaysAssert( Geometry::doLineSegmentsIntersect( 1, 1, 3, 3, 2, 2, 1, 3 ), AipsError ); // overlapping coincident segments => intersect AlwaysAssert( Geometry::doLineSegmentsIntersect( 1, 1, 3, 3, 2, 2, 4, 4 ), AipsError ); // nested coincident segments => intersect AlwaysAssert( Geometry::doLineSegmentsIntersect( 1, 1, 3, 3, 2, 2, 2.5, 2.5 ), AipsError ); // fully coincident segments => intersect AlwaysAssert( Geometry::doLineSegmentsIntersect( 1, 1, 3, 3, 1, 1, 3, 3 ), AipsError ); // parallel segments => no intersection AlwaysAssert( ! Geometry::doLineSegmentsIntersect( 1, 1, 3, 3, 2, 1, 4, 3 ), AipsError ); } cout << "ok" << endl; } catch (const std::exception& x) { cerr << x.what() << endl; return 1; } return 0; } casacore-3.7.1/scimath/Mathematics/test/tHistAcc.cc000066400000000000000000000110521476623553700222020ustar00rootroot00000000000000//# tHistAcc.cc: Test program for class HistAcc //# Copyright (C) 1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include int main() { try { uInt i; // Index uInt nv = 1000; // Nr of input values String captioni(" tHistAcc test for "); String captionf(" tHistAcc test for "); StatAcc si; // Statistics accumulator StatAcc sf; // Statistics accumulator HistAcc hmani(-2,10,1); // Manually defined bins HistAcc hmanf(-2,10,1); // Manually defined bins HistAcc hautof(25); // Fully automatic HistAcc hautoi(25); // Fully automatic HistAcc hsemif(25,2.0); // Semi automatic (width given) Vector vvi(nv,0); // values of required type Vector vvf(nv,0.0); // values of required type Block bvf(nv); // values of required type ACG gen(10,20); // random number generator Normal rnd(& gen, -5.0, 10.0); // Normal distr (mean, variance) cout << " Nr of input values= " << nv << endl; for (i=0; i binsi; Block valsf; hmanf.getHistogram(binsi,valsf); cout << " length of binsi=" << binsi.nelements() << endl; cout << " length of valsf=" << valsf.nelements() << endl; hmanf.reset(); hmanf.printHistogram(cout,"hmanf reset"); hmanf.put(bvf); hmanf.printHistogram(cout,"hmanf put bvf"); cout << " *** end of tHistAcc *** " << endl; } catch (std::exception& x) { cout << x.what() << endl; return 1; // unexpected error } return 0; // exit with success status } casacore-3.7.1/scimath/Mathematics/test/tHistAcc.out000066400000000000000000000073461476623553700224370ustar00rootroot00000000000000 Nr of input values= 1000 Histogram: vvi hmani(-2,10,1) bin value= -2 : n= 81 bin value= -1 : n= 66 bin value= 0 : n= 82 bin value= 1 : n= 12 bin value= 2 : n= 9 bin value= 3 : n= 3 bin value= 4 : n= 4 bin value= 5 : n= 0 bin value= 6 : n= 0 bin value= 7 : n= 0 bin value= 8 : n= 0 bin value= 9 : n= 0 bin value= 10 : n= 0 nTotal=1000 binWidth=1 nSpurious=743 nLow=743(vMin=-15) nHigh=0(vMax=4) mean=-4.543 median= .. rms=3.0676 Histogram: vvf hmanf(-2,10,1) bin value= -2 : n= 81 bin value= -1 : n= 66 bin value= 0 : n= 82 bin value= 1 : n= 12 bin value= 2 : n= 9 bin value= 3 : n= 3 bin value= 4 : n= 4 bin value= 5 : n= 0 bin value= 6 : n= 0 bin value= 7 : n= 0 bin value= 8 : n= 0 bin value= 9 : n= 0 bin value= 10 : n= 0 nTotal=1000 binWidth=1 nSpurious=743 nLow=743(vMin=-15) nHigh=0(vMax=4) mean=-4.543 median= .. rms=3.0676 Histogram: vvf hsemif(25,2) bin value= -12 : n= 9 bin value= -10 : n= 48 bin value= -8 : n= 109 bin value= -6 : n= 219 bin value= -4 : n= 230 bin value= -2 : n= 208 bin value= 0 : n= 148 nTotal=1000 binWidth=2 nSpurious=29 nLow=1(vMin=-15) nHigh=28(vMax=4) mean=-4.543 median=-4 rms=3.0676 Histogram: vvf hautof(25) bin value= -11 : n= 20 bin value= -10 : n= 28 bin value= -9 : n= 43 bin value= -8 : n= 66 bin value= -7 : n= 103 bin value= -6 : n= 116 bin value= -5 : n= 114 bin value= -4 : n= 116 bin value= -3 : n= 127 bin value= -2 : n= 81 bin value= -1 : n= 66 bin value= 0 : n= 82 nTotal=1000 binWidth=1 nSpurious=38 nLow=10(vMin=-15) nHigh=28(vMax=4) mean=-4.543 median=-4 rms=3.0676 wtot npts mean rms min max : remove low-contents bins 257 7-0.6732 1.267 -2 4 : hmani 257 7-0.6732 1.267 -2 4 : hmanf 962 12 -4.652 2.806 -11 0 : hautof Histogram: hautof bin value= -11 : n= 20 bin value= -10 : n= 28 bin value= -9 : n= 43 bin value= -8 : n= 66 bin value= -7 : n= 103 bin value= -6 : n= 116 bin value= -5 : n= 114 bin value= -4 : n= 116 bin value= -3 : n= 127 bin value= -2 : n= 81 bin value= -1 : n= 66 bin value= 0 : n= 82 nTotal=962 binWidth=1 nSpurious=0 nLow=0(vMin=-11) nHigh=0(vMax=0) mean=-4.652 median=-5 rms=2.806 hmanf.getStatistics().getMean(): -0.6732 length of binsi=13 length of valsf=13 Histogram: hmanf reset bin value= -2 : n= 0 bin value= -1 : n= 0 bin value= 0 : n= 0 bin value= 1 : n= 0 bin value= 2 : n= 0 bin value= 3 : n= 0 bin value= 4 : n= 0 bin value= 5 : n= 0 bin value= 6 : n= 0 bin value= 7 : n= 0 bin value= 8 : n= 0 bin value= 9 : n= 0 bin value= 10 : n= 0 nTotal=0 binWidth=1 nSpurious=0 nLow=0(vMin= ..) nHigh=0(vMax= ..) mean= .. median= .. rms= .. Histogram: hmanf put bvf bin value= -2 : n= 81 bin value= -1 : n= 66 bin value= 0 : n= 82 bin value= 1 : n= 12 bin value= 2 : n= 9 bin value= 3 : n= 3 bin value= 4 : n= 4 bin value= 5 : n= 0 bin value= 6 : n= 0 bin value= 7 : n= 0 bin value= 8 : n= 0 bin value= 9 : n= 0 bin value= 10 : n= 0 nTotal=1000 binWidth=1 nSpurious=743 nLow=743(vMin=-15) nHigh=0(vMax=4) mean=-4.543 median= .. rms=3.068 *** end of tHistAcc *** casacore-3.7.1/scimath/Mathematics/test/tInterpolate2D.cc000066400000000000000000000114441476623553700233450ustar00rootroot00000000000000//# Copyright (C) 1996,1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include #include #include #include #include #include #include #include int main() { try { AlwaysAssert(Interpolate2D::stringToMethod("l") == Interpolate2D::LINEAR, AipsError); AlwaysAssert(Interpolate2D::stringToMethod("linear") == Interpolate2D::LINEAR, AipsError); AlwaysAssert(Interpolate2D::stringToMethod("z") == Interpolate2D::LANCZOS, AipsError); AlwaysAssert(Interpolate2D::stringToMethod("lanczos") == Interpolate2D::LANCZOS, AipsError); AlwaysAssert(Interpolate2D::stringToMethod("c") == Interpolate2D::CUBIC, AipsError); AlwaysAssert(Interpolate2D::stringToMethod("cubic") == Interpolate2D::CUBIC, AipsError); // Set up matrix of input values Matrix matt_f(10,10); Matrix matt_d(10,10); for (uInt i=0; i<10; ++i) { for (uInt j=0; j<10; ++j) { matt_f(i,j) = i+j; matt_d(i,j) = i+j; } } // Where to evaluate the interpolation Vector where(2); where(0) = 3.452; where(1) = 6.1; // Test for all implemented methods std::vector methods(4); methods[0] = "linear"; methods[1] = "cubic"; methods[2] = "lanczos"; methods[3] = "nearest"; std::vector results(4); results[0] = 9.552; // Linear results[1] = 9.552; // Cubic results[2] = 9.473654921656; // Lanczos results[3] = 9.; // Nearest Bool ok; for (uInt method=0; method matt_c(10,10); Matrix matt_dc(10,10); std::vector cresults(results.size()); for (uInt i=0; i #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define AlwaysTrue(x, y) \ do { \ ++tests_done; \ AlwaysAssert(x, y); \ } while (0) unsigned tests_done = 0; const Bool debug = False;//True; template class TestLinearInterpolation1 { public: TestLinearInterpolation1() { Array a(IPosition(2,2,1000)); Array aflags(IPosition(2,2,1000)); Array expect(IPosition(2,2,1000)); Array expflags(IPosition(2,2,1000)); Vector ingrid(1000); for(uInt i=0; i<1000; i++){ a(IPosition(2,0,i)) = Complex(1.,1.); a(IPosition(2,1,i)) = Complex(1.,1.); aflags(IPosition(2,0,i)) = False; aflags(IPosition(2,1,i)) = False; expect(IPosition(2,0,i)) = Complex(1.,1.); expect(IPosition(2,1,i)) = Complex(1.,1.); expflags(IPosition(2,0,i)) = False; expflags(IPosition(2,1,i)) = False; ingrid(i) = (T)i; } cout << "--- equidistant output grid ------------------------------------------------------------" << endl; cout << "--- change output grid width from identical to double width in steps of 1/iterations ---" << endl; Double iterations = 1000.; // change output grid width from identical to double width in steps of 1/iterations for (Int it = 0; it < (Int)iterations; it++) { Array yout; Array youtFlags; Vector xout(1000); Vector xin; Array yin; Array yinFlags; Int method = InterpolateArray1D::linear; Bool goodIsTrue=False; Bool extrapolate=False; xin.assign(ingrid); yin.assign(a); yinFlags.assign(aflags); for(uInt i=0; i<1000; i++){ xout(i) = xin(i) + (Double)it * (i+1)/iterations; } InterpolateArray1D::interpolate(yout, // the new visibilities youtFlags, // the new flags xout, // the new channel centers xin, // the old channel centers yin, // the old visibilities yinFlags,// the old flags method, // the interpol method goodIsTrue, // for flagging: good is not true extrapolate // do not extrapolate ); for(uInt i=0; i<2; i++){ for(uInt j=0; j<500; j++){ Double diffr = yout(IPosition(2,i,j)).real() - expect(IPosition(2,i,j)).real(); Double diffi = yout(IPosition(2,i,j)).imag() - expect(IPosition(2,i,j)).imag(); if(debug){ cout << it << " " << i << " " << j << " " << xin(j) << " " << xout(j) << " " << yout(IPosition(2,i,j)) << " " << expect(IPosition(2,i,j)) << endl; cout << it << " flag " << i << " " << j << " " << youtFlags(IPosition(2,i,j)) << " " << expflags(IPosition(2,i,j)) << endl; } AlwaysAssert(fabs(diffr)<1E-8, AipsError); AlwaysAssert(fabs(diffi)<1E-8, AipsError); AlwaysAssert(youtFlags(IPosition(2,i,j)) == expflags(IPosition(2,i,j)), AipsError); } } } // end for it=0 ++tests_done; \ } }; template class TestLinearInterpolation2 { public: TestLinearInterpolation2() { Array a(IPosition(2,2,1000)); Array aflags(IPosition(2,2,1000)); Array expect(IPosition(2,2,1000)); Array expflags(IPosition(2,2,1000)); Vector ingrid(1000); for(uInt i=0; i<1000; i++){ a(IPosition(2,0,i)) = Complex((Float)i,(Float)i); a(IPosition(2,1,i)) = Complex((Float)i,(Float)i); aflags(IPosition(2,0,i)) = False; aflags(IPosition(2,1,i)) = False; expect(IPosition(2,0,i)) = Complex(1.,1.); expect(IPosition(2,1,i)) = Complex(1.,1.); expflags(IPosition(2,0,i)) = False; expflags(IPosition(2,1,i)) = False; ingrid(i) = (T)i; } cout << "--- equidistant output grid, increasing values -----------------------------------------" << endl; cout << "--- change output grid width from identical to double width in steps of 1/iterations ---" << endl; Double iterations = 1000.; // change output grid width from identical to double width in steps of 1/iterations for (Int it = 0; it < (Int)iterations; it++) { Array yout; Array youtFlags; Vector xout(1000); Vector xin; Array yin; Array yinFlags; Int method = InterpolateArray1D::linear; Bool goodIsTrue=False; Bool extrapolate=False; xin.assign(ingrid); yin.assign(a); yinFlags.assign(aflags); for(uInt i=0; i<1000; i++){ xout(i) = xin(i) + (Double)it * (i+1)/iterations; } InterpolateArray1D::interpolate(yout, // the new visibilities youtFlags, // the new flags xout, // the new channel centers xin, // the old channel centers yin, // the old visibilities yinFlags,// the old flags method, // the interpol method goodIsTrue, // for flagging: good is not true extrapolate // do not extrapolate ); for(uInt i=0; i<2; i++){ for(uInt j=0; j<500; j++){ Double diffr = yout(IPosition(2,i,j)).real() - xout(j) * expect(IPosition(2,i,j)).real(); Double diffi = yout(IPosition(2,i,j)).imag() - xout(j) * expect(IPosition(2,i,j)).imag(); if(debug){ cout << it << " " << i << " " << j << " " << xin(j) << " " << xout(j) << " " << yout(IPosition(2,i,j)) << " " << xout(j) * expect(IPosition(2,i,j)) << " diffs: " << diffr << " " << diffi << endl; cout << it << " flag " << i << " " << j << " " << youtFlags(IPosition(2,i,j)) << " " << expflags(IPosition(2,i,j)) << endl; } AlwaysAssert(fabs(diffr)<(xout(j)+1.)*1E-7, AipsError); AlwaysAssert(fabs(diffi)<(xout(j)+1.)*1E-7, AipsError); AlwaysAssert(youtFlags(IPosition(2,i,j)) == expflags(IPosition(2,i,j)), AipsError); } } } // end for it=0 ++tests_done; \ } }; template class TestNearestInterpolation1 { public: TestNearestInterpolation1() { Int N(10); Vector Xa(N,0.0), Xd(N,0.0); Vector Ya(N,0.0), Yd(N,0.0); indgen(Xa); indgen(Ya); for (Int i=0;i x(n,0.0); Vector ya(n,0.0), yd(n,0.0); indgen(x); x/=static_cast(10.0); // tenths Int method = InterpolateArray1D::nearestNeighbour; InterpolateArray1D::interpolate(ya,x,Xa,Ya,method); // Ascending abscissa InterpolateArray1D::interpolate(yd,x,Xd,Yd,method); // Descending abscissa Vector yatest(n,0.0),ydtest(n,0.0); indgen(yatest); yatest/=static_cast(10.0); yatest+=static_cast(0.4999); // ensures middle value goes down (matches InterpolateArray1D) yatest=floor(yatest); indgen(ydtest); ydtest/=static_cast(10.0); ydtest+=static_cast(0.5001); // ensures middle value goes up (matches InterpolateArray1D) ydtest=floor(ydtest); AlwaysAssert(allEQ(ya,yatest),AipsError); AlwaysAssert(allEQ(yd,ydtest),AipsError); ++tests_done; } }; template void run_tests() { TestLinearInterpolation1 (); TestLinearInterpolation2 (); return; } template void run_nearest_tests() { TestNearestInterpolation1 (); return; } int main() { tests_done = 0; try { cout << "Testing 'linear' Float/Complex" << endl; run_tests(); cout << "Testing 'linear' Double/Complex" << endl; run_tests(); cout << "Testing 'nearestNeighbour' Float/Float, ascending/descending" << endl; run_nearest_tests(); cout << "Testing 'nearestNeighbour' Double/Float, ascending/descending" << endl; run_nearest_tests(); } catch (std::exception& x) { cerr << x.what() << endl; cout << "FAIL" << endl; return 1; } cout << tests_done << " tests OK" << endl; return 0; } casacore-3.7.1/scimath/Mathematics/test/tMathFunc.cc000066400000000000000000000212171476623553700223750ustar00rootroot00000000000000//# tMathFunc.cc: This program tests MathFunc objects //# Copyright (C) 1993,1994,1995,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include int main() { try{ cout << "\nMathFunc constants : " << endl; cout << "defcutoff() : " << MathFunc::defcutoff() << endl; cout << "defwidth() : " << MathFunc::defwidth() << endl; cout << "defKBwidth() : " << MathFunc::defKBwidth() << endl; cout << "defKBparm() : " << MathFunc::defKBparm() << endl; cout << "defmodKBparm() : " << MathFunc::defmodKBparm() << endl; cout << "defSphcutoff() : " << MathFunc::defSphcutoff() << endl; cout << "defSincparm() : " << MathFunc::defSincparm() << endl; cout << "defSphparm() : " << MathFunc::defSphparm() << endl; cout << "defExpPower() : " << MathFunc::defExpPower() << endl; cout << "defExpScale() : " << MathFunc::defExpScale() << endl; MathFunc hh(new GaussianConv()); cout << "\ncreate a Gaussian hh by means of call to new GaussianConv\n"; cout << " hh.sup_value() = " << hh.sup_value() << endl; cout << "\nGaussian computation\n"; Float tmp[5] = {1, 0.193867, 0.00141258, 3.86835e-07, 3.9815e-12}; for(int k = 0; k <5; k++) { float result = hh.value((float) k); AlwaysAssertExit(abs(result-tmp[k]) < 1.e-5); cout << result <<"\n"; } cout << " " <<"\n"; MathFunc *p1; MathFunc *p2; p1= MathFunc::newMathFunc(GaussianConv()); cout << "\ncreate a Gaussian pointed to by p1 by call to newMathFunc\n"; cout << " p1->sup_value() = " << p1->sup_value() << endl; int j; for(j = 0; j <5; j++) { float result = p1->value( (float) j); AlwaysAssertExit(abs(result-tmp[j]) < 1.e-5); cout << result <<"\n"; } cout << " " <<"\n"; p2= MathFunc::newMathFunc(Mod_KB_Conv()); cout << "\na Mod Kaiser Bessel pointed to by p2 by call to newMathFunc\n"; cout << " p2->sup_value() = " << p2->sup_value() << endl; cout << "\nMod_KB \n"; Float tmp1[5] = {1,0.315995,0.00662331,0.0268752,0.0072334}; for(j = 0; j <5; j++) { float result = p2->value((float) j); AlwaysAssertExit(abs(result-tmp1[j]) < 1.e-5); cout << result <<"\n"; } cout << " " <<"\n"; MathFunc a(MOD_KB); MathFunc aa(MOD_KB); MathFunc c(GAUSSIAN); MathFunc cc(GAUSSIAN); MathFunc d(SPHEROIDAL); MathFunc dd(SPHEROIDAL); MathFunc ee(GAUSSIAN, 2.0, 1.4); MathFunc ff(GAUSSIAN, 2.1, 1.3); MathFunc gg(MOD_KB, 2.1, 1.3,2.5,3.0); MathFunc *p; p = &aa; aa = a; cc = c; dd = d; cout << " Gaussian ee should have support width 2 " << endl; cout << " ee.sup_value() = " << ee.sup_value() << endl; cout << "\nGaussian computation\n"; Float tmp2[5] = {1,0.243026,0.00348829,2.95718e-06,1.48064e-10}; for(j = 0; j <5; j++) { float result = ee.value((float) j); AlwaysAssertExit(abs(result-tmp2[j]) < 1.e-5); cout << result <<"\n"; } cout << endl << endl; cout << " Gaussian ff should have support width 2.1 " << endl; cout << " ff.value(0.0) = " << ff.value(0.0) << endl; cout << " ff.sup_value() = " << ff.sup_value() << endl; cout << "\nGaussian computation (ff)\n"; Float tmp3[5] = { 1, 0.193867, 0.00141258, 3.86835e-07, 3.9815e-12}; for(j = 0; j <5; j++) { float result = ff.value((float) j); AlwaysAssertExit(abs(result-tmp3[j]) < 1.e-5); cout << result <<"\n"; } cout << endl << endl; cout << " Modified Kaiser Bessel gg should have support width 2.1 " << endl; cout << " gg.value(0.0) = " << gg.value(0.0) << endl; cout << " gg.sup_value() = " << gg.sup_value() << endl; cout << "Modified Kaiser-Bessel computation(gg) \n"; Float tmp4[5] = {1, 0.0807079, 0.142924, 0.0458648, 0.000233285}; for(j = 0; j <5; j++) { float result = gg.value((float) j); AlwaysAssertExit(abs(result-tmp4[j]) < 1.e-5); cout << result <<"\n"; } cout << endl << endl << endl; cout << " aa.value(0.0) = " << aa.value(0.0) << endl; cout << " p->value(0.0) = " << p->value(0.0) << endl; cout << " aa.sup_value() = " << aa.sup_value() << endl; cout << " cc.sup_value() = " << cc.sup_value() << endl; cout << " dd.sup_value() = " << dd.sup_value() << endl; cout << endl; cout << "Modified Kaiser-Bessel computation\n"; Float tmp5[5] = { 1, 0.315995, 0.00662331, 0.0268752, 0.0072334}; int i; for(i = 0; i <5; i++) { float result = aa.value((float) i); AlwaysAssertExit(abs(result-tmp5[i]) < 1.e-5); cout << result <<"\n"; } Float tmp6[5] = { 1, 0.193867, 0.00141258, 3.86835e-07, 3.9815e-12}; cout << "\nGaussian computation\n"; for(i = 0; i <5; i++) { float result = cc.value((float) i); AlwaysAssertExit(abs(result-tmp6[i]) < 1.e-5); cout << result <<"\n"; } Float tmp7[4] = { 1, 0.573245, 0.0826234, 0}; cout << "\nSpheroidal computation\n"; for(i = 0; i <4; i++) { float result = dd.value((float) i); AlwaysAssertExit(abs(result-tmp7[i]) < 1.e-5); cout << result <<"\n"; } cout << endl << "Unary function:" < foo(UNARY); for(i = 0; i <4; i++) { float result = foo.value((float) i); AlwaysAssertExit(abs(result-1.0) < 1.e-5); cout << result <<"\n"; } cout << "\nExponential*Sinc computation\n"; MathFunc expsinc(EXP_SINC, 3.0, 1.2, 3.0, 2.0); cout << " expsinc.sup_value() = " << expsinc.sup_value() << endl; Float tmp8[4] = {1, 0.170902, -0.10605, 0.0468399}; for (i = 0; i<4; i++) { float result = expsinc.value((float) i); AlwaysAssertExit(abs(result-tmp8[i]) < 1.e-5); cout << result << "\n"; } delete p1; delete p2; } catch(std::exception& x) { cout << "Unexpected exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } /* This program should produce output similar to the following: create a Gaussian hh by means of call to new GaussianConv hh.sup_value() = 2 Gaussian computation 1 0.193867 0.00141258 3.86835e-07 3.9815e-12 create a Gaussian pointed to by p1 by call to newMathFunc p1->sup_value() = 2 1 0.193867 0.00141258 3.86835e-07 3.9815e-12 a Mod Kaiser Bessel pointed to by p2 by call to newMathFunc p2->sup_value() = 2 Mod_KB 1 0.315995 0.00662331 0.0268752 0.0072334 Gaussian ee should have support width 2 ee.sup_value() = 2 Gaussian computation 1 0.243026 0.00348829 2.95718e-06 1.48064e-10 Gaussian ff should have support width 2.1 ff.value(0.0) = 1 ff.sup_value() = 2.1 Gaussian computation (ff) 1 0.193867 0.00141258 3.86835e-07 3.9815e-12 Modified Kaiser Bessel gg should have support width 2.1 gg.value(0.0) = 1 gg.sup_value() = 2.1 Modified Kaiser-Bessel computation(gg) 1 0.0807079 0.142924 0.0458648 0.000233285 aa.value(0.0) = 1 p->value(0.0) = 1 aa.sup_value() = 2 cc.sup_value() = 2 dd.sup_value() = 3 Modified Kaiser-Bessel computation 1 0.315995 0.00662331 0.0268752 0.0072334 Gaussian computation 1 0.193867 0.00141258 3.86835e-07 3.9815e-12 Spheroidal computation 1 0.573245 0.0826234 0 Unary function: 1 1 1 1 Exponential*Sinc computation expsinc.sup_value() = 3 1 0.170902 -0.10605 0.0468399 */ casacore-3.7.1/scimath/Mathematics/test/tMatrixMathLA.cc000066400000000000000000000047361476623553700231720ustar00rootroot00000000000000//# tMatrixMathLA.cc: Test functions in MatrixMathLA.h //# Copyright (C) 1995,1996,1999,2001, 2004 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include int main() { Matrix ind(3,3); ind(0,0) = 2; ind(0,1) = 8; ind(0,2) = 6; ind(1,0) = 4; ind(1,1) = 2; ind(1,2) = -2; ind(2,0) = 3; ind(2,1) = -1; ind(2,2) = 1; Matrix outd(3,3); outd(0,0) = 0; outd(0,1) = 7; outd(0,2) = 14; outd(1,0) = 5; outd(1,1) = 8; outd(1,2) = -14; outd(2,0) = 5; outd(2,1) = -13; outd(2,2) = 14; outd /= 70.0; AlwaysAssertExit(allNearAbs(invert(ind), outd, 0.00001)); // Now test the other types - Float/Complex/DComplex Matrix inf(3,3), outf(3,3); convertArray(inf, ind); convertArray(outf, outd); AlwaysAssertExit(allNearAbs(invert(inf), outf, 0.00001)); Matrix inc(3,3), outc(3,3); convertArray(inc, ind); convertArray(outc, outd); AlwaysAssertExit(allNearAbs(invert(inc), outc, 0.00001)); Matrix indc(3,3), outdc(3,3); convertArray(indc, ind); convertArray(outdc, outd); AlwaysAssertExit(allNearAbs(invert(indc), outdc, 0.00001)); cout << "OK" << endl; return 0; } casacore-3.7.1/scimath/Mathematics/test/tMedianSlider.cc000066400000000000000000000123561476623553700232340ustar00rootroot00000000000000//# tMedianSlider.cc: This program tests tMedianSlider objects //# Copyright (C) 2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //#! Includes #include #include #include #include #include #include int main(){ MedianSlider me; cout << "Create a MedianSlider me by means of call to MedianSlider () with default arguments" << endl; int halfwin = 3; MedianSlider m1(halfwin); cout << "Create a MedianSlider m1 by means of call to MedianSlider(int hw)" << endl; cout << "The half window size of m1 is 3, full window size is 7\n"; cout << "add {0.5, 3.5, 1.5, 4.5, 2.5, 5.5} with flags {f,t,f,t,f,t} to m1" << endl; m1.add(0.5); m1.add(3.5, True); m1.add(1.5); m1.add(4.5, True); m1.add(2.5); m1.add(5.5, True); m1.next(); cout << "the function next() add 1 flagged value into m1 window, and make it full " << endl; cout << "The number of non-flagged values in m1 window is " << m1.nval() << endl; cout << "Current median value in m1 window is " << m1.median() << endl; m1.add(6.5); cout << "\nAdd a non-flagged 6.5" << endl; cout << "The number of non-flagged values in m1 window is " << m1.nval() << endl; cout << "Current median value in m1 window is " << m1.median() << endl; m1.add(7.5); cout << "Add a non-flagged 7.5" << endl; cout << "The number of non-flagged values in m1 window is " << m1.nval() << endl; cout << "Current median value in m1 window is " << m1.median() << endl; m1.add(8.5); cout << "Add a non-flagged 8.5" << endl; cout << "The number of non-flagged values in m1 window is " << m1.nval() << endl; cout << "Current median value in m1 window is " << m1.median() << endl; MedianSlider m2(m1); cout << "\nCreate a MedianSlider m2 by means of call to copy constructor\n"; cout << "MedianSlider ( const MedianSlider &other )\n"; cout << "The number of non-flagged values in m2 window is " << m2.nval() << endl; cout << "Current median value in m2 window is " << m2.median() << endl; me = m2; cout << "Assign m2 to me, so me has the number of non-flagged values " << me.nval() << endl; cout << "Current median value in me window is " << me.median() << endl; Vector vl(4); vl(0) = 10.5; vl(1) = 4.5; vl(2) = 5.5; vl(3) = 11.5; cout << "Create Vector vl = {10.5, 4.5, 5.5, 11.5}" << endl; Vector bl(4); bl(0) = False; bl(1) = True; bl(2) = True; bl(3) = False; cout << "Create Vector bl = {False, True, True, False}" << endl; m1.add(vl, bl); cout << "Add vl and bl to m1, old values are pushed out" << endl; cout << "The number of non-flagged values in m1 window is " << m1.nval() << endl; cout << "Current median value in m1 window is " << m1.median() << endl; Bool flag = False; cout << "The value takes 4 step back from end " << m1.prevVal(uInt(4), flag) << endl; cout << "The value at the midpoint " << m1.midpoint(flag) << endl; cout << "The difference between the current median and the value at the window center " << m1.diff(flag) << endl; cout << "The total memory usage for a given half window size is " << m1.objsize(halfwin) << endl; m1.add(); cout << "\nTest add() that add(0, True) to m1 " << endl; cout << "Current median value in m1 window is " << m1.median() << endl; cout << "Now the value at the midpoint is " << m1.midpoint(flag); String boolAsString; if(flag == 0) boolAsString = "False"; else boolAsString = "True"; cout <<" with flag " << boolAsString << " the difference between midpoint and current median " << m1.diff() << endl; // cout << " Number of values in the m1 window " << m1.size() << endl; Vector vl2(7); vl2(0) = 1; vl2(1) = 2; vl2(2) = 3; vl2(3) = 4; vl2(4) = 5; vl2(5) = 6; vl2(6) = 7; cout << "\nCreate Vector vl2 = {1,2,3,4,5,6,7}" << endl; m1.add(vl2); cout << "Add vl2 to m1, old values are pushed out" << endl; cout << "The number of non-flagged values in m1 window is " << m1.nval() << endl; cout << "Current median value in m1 window is " << m1.median() << endl; return 0; } casacore-3.7.1/scimath/Mathematics/test/tSmooth.cc000066400000000000000000000357511476623553700221510ustar00rootroot00000000000000//# tSmooth.cc: this tests the Smooth class //# Copyright (C) 2010 by ESO (in the framework of the ALMA collaboration) //# Copyright (C) 1996,1997,1998,1999,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include int main() { Bool anyFailures = False; { Bool failed = False; // Test with Float Vectors, all flags 0 std::vector vyin; std::vector vyinFlags; Vector myexp; Vector outv; Vector outFlags; uInt vdim = 8; Float myyin[] = {1,3,1,4,2,6,3,8}; Bool myflags1[] = {0,0,0,0,0,0,0,0}; vyin.assign(myyin, myyin+vdim); vyinFlags.assign(myflags1, myflags1+vdim); Vector yin(vyin); Vector yinFlags(vyinFlags); myexp.resize(vdim); myexp[0] = 2./3.*vyin[0] + 1./3.*vyin[1]; myexp[vdim-1] = 1./3.* vyin[vdim-2] + 2./3.*vyin[vdim-1]; for(uInt i=1; i myexpflags(vdim,False); myexpflags[0] = True; myexpflags[7] = True; outv.resize(vdim); outFlags.resize(vdim); Smooth::hanning(outv, // the output outFlags, // the output mask yin, // the input yinFlags, // the input mask False); // for flagging: good is not true if(!allNearAbs(myexp, outv, 1.E-6)){ for(uInt i = 0; i vyin; std::vector vyinFlags; Vector myexp; Vector outv; Vector outFlags; uInt vdim = 8; Float myyin[] = {1,3,1,4,2,6,3,8}; Bool myflags1[] = {1,1,1,1,1,1,1,1}; vyin.assign(myyin, myyin+vdim); vyinFlags.assign(myflags1, myflags1+vdim); Vector yin(vyin); Vector yinFlags(vyinFlags); myexp.resize(vdim); myexp.assign(yin); outv.resize(vdim); outFlags.resize(vdim); Vector myexpflags(yinFlags); Smooth::hanning(outv, // the output outFlags, // the output mask yin, // the input yinFlags, // the input mask False); // for flagging: good is not true if(!allNearAbs(myexp, outv, 1.E-6)){ for(uInt i = 0; i vyin; std::vector vyinFlags; Vector myexp; Vector outv; Vector outFlags; uInt vdim = 8; Float myyin[] = {1,3,1,4,2,6,3,8}; Bool myflags1[] = {1,0,1,1,0,0,0,1}; vyin.assign(myyin, myyin+vdim); vyinFlags.assign(myflags1, myflags1+vdim); Vector yin(vyin); Vector yinFlags(vyinFlags); myexp.resize(vdim); myexp[0] = vyin[0]; myexp[1] = vyin[1]; myexp[2] = vyin[2]; myexp[3] = vyin[3]; myexp[4] = 2./3.*vyin[4] + 1./3.*vyin[5]; myexp[5] = 0.25 * vyin[5-1] + 0.5 * vyin[5] + 0.25 * vyin[5+1]; myexp[6] = 1./3.*vyin[5] + 2./3.*vyin[6]; myexp[7] = vyin[7]; Vector myexpflags(vdim); myexpflags[0] = yinFlags[0] || yinFlags[1]; myexpflags[1] = yinFlags[0] || yinFlags[1] || yinFlags[2]; myexpflags[2] = yinFlags[1] || yinFlags[2] || yinFlags[3]; myexpflags[3] = yinFlags[2] || yinFlags[3] || yinFlags[4]; myexpflags[4] = yinFlags[3] || yinFlags[4] || yinFlags[5]; myexpflags[5] = yinFlags[4] || yinFlags[5] || yinFlags[6]; myexpflags[6] = yinFlags[5] || yinFlags[6] || yinFlags[7]; myexpflags[7] = yinFlags[6] || yinFlags[7]; outv.resize(vdim); outFlags.resize(vdim); Smooth::hanning(outv, // the output outFlags, // the output mask yin, // the input yinFlags, // the input mask False); // for flagging: good is not true if(!allNearAbs(myexp, outv, 1.E-6)){ for(uInt i = 0; i vyin; std::vector vyinFlags; Vector myexp; Vector outv; Vector outFlags; uInt vdim = 8; Complex myyin[] = {Complex(1.,1.), Complex(3.,3.), Complex(1.,1.), Complex(4.,4.), Complex(2.,2.), Complex(6.,6.), Complex(3.,3.), Complex(8.,8.)}; Bool myflags1[] = {0,0,0,0,0,0,0,0}; vyin.assign(myyin, myyin+vdim); vyinFlags.assign(myflags1, myflags1+vdim); Vector yin(vyin); Vector yinFlags(vyinFlags); myexp.resize(vdim); myexp[0] = Complex(2./3.,0.)*vyin[0] + Complex(1./3.,0.)*vyin[1]; myexp[vdim-1] = Complex(2./3.,0.)*vyin[vdim-1] + Complex(1./3.,0.)*vyin[vdim-2]; for(uInt i=1; i myexpflags(vdim,False); myexpflags[0] = True; myexpflags[7] = True; outv.resize(vdim); outFlags.resize(vdim); Smooth::hanning(outv, // the output outFlags, // the output mask yin, // the input yinFlags, // the input mask False); // for flagging: good is not true if(!allNearAbs(myexp, outv, 1.E-6)){ for(uInt i = 0; i vyin; std::vector vyinFlags; Vector myexp; Vector outv; Vector outFlags; uInt vdim = 8; Complex myyin[] = {Complex(1.,1.), Complex(3.,3.), Complex(1.,1.), Complex(4.,4.), Complex(2.,2.), Complex(6.,6.), Complex(3.,3.), Complex(8.,8.)}; Bool myflags1[] = {1,0,1,1,0,0,0,1}; vyin.assign(myyin, myyin+vdim); vyinFlags.assign(myflags1, myflags1+vdim); Vector yin(vyin); Vector yinFlags(vyinFlags); myexp.resize(vdim); myexp[0] = vyin[0]; myexp[1] = vyin[1]; myexp[2] = vyin[2]; myexp[3] = vyin[3]; myexp[4] = Complex(2./3.,0.)*vyin[4] + Complex(1./3.,0.)*vyin[5]; myexp[5] = Complex(0.25,0.) * vyin[5-1] + Complex(0.5,0.) * vyin[5] + Complex(0.25,0) * vyin[5+1]; myexp[6] = Complex(2./3.,0.)*vyin[6] + Complex(1./3.,0.)*vyin[5]; myexp[7] = vyin[7]; Vector myexpflags(vdim); myexpflags[0] = yinFlags[0] || yinFlags[1]; myexpflags[1] = yinFlags[0] || yinFlags[1] || yinFlags[2]; myexpflags[2] = yinFlags[1] || yinFlags[2] || yinFlags[3]; myexpflags[3] = yinFlags[2] || yinFlags[3] || yinFlags[4]; myexpflags[4] = yinFlags[3] || yinFlags[4] || yinFlags[5]; myexpflags[5] = yinFlags[4] || yinFlags[5] || yinFlags[6]; myexpflags[6] = yinFlags[5] || yinFlags[6] || yinFlags[7]; myexpflags[7] = yinFlags[6] || yinFlags[7]; outv.resize(vdim); outFlags.resize(vdim); Smooth::hanning(outv, // the output outFlags, // the output mask yin, // the input yinFlags, // the input mask False); // for flagging: good is not true if(!allNearAbs(myexp, outv, 1.E-6)){ for(uInt i = 0; i myexp; Array outv; Array outFlags; Array yin(adim, myyin); Array yinFlags(adim, myflags); myexp.resize(adim); myexp(IPosition(2,0,0)) = Complex(2./3.,0.)*myyin[2*0] + Complex(1./3.,0.)*myyin[2*1]; myexp(IPosition(2,1,0)) = Complex(2./3.,0.)*myyin[2*0+1] + Complex(1./3.,0.)*myyin[2*1+1]; myexp(IPosition(2,0,7)) = Complex(2./3.,0.)*myyin[2*7] + Complex(1./3.,0.)*myyin[2*6]; myexp(IPosition(2,1,7)) = Complex(2./3.,0.)*myyin[2*7+1] + Complex(1./3.,0.)*myyin[2*6+1]; for(uInt i=1; i myexpflags(adim,False); myexpflags(IPosition(2,0,0)) = True; myexpflags(IPosition(2,1,0)) = True; myexpflags(IPosition(2,0,7)) = True; myexpflags(IPosition(2,1,7)) = True; outv.resize(adim); outFlags.resize(adim); Smooth::hanning(outv, // the output outFlags, // the output mask yin, // the input yinFlags, // the input mask False); // for flagging: good is not true if(!allNearAbs(myexp, outv, 1.E-6)){ for(uInt i = 0; i #include #include #include #include #include #include Bool testDer(const SparseDiff y, const SparseDiff &x, Float f) { return !(x.nDerivatives() != y.nDerivatives() || !nearAbs(y.derivative(0).second, f*x.derivative(0).second, 1e-5)); } int main() { uInt nerr = 0; // test the constructors { SparseDiff a; if (a.value() != 0 || a.nDerivatives() != 0) { cerr << "SparseDiff a; failed a = " << a << endl; nerr++; } SparseDiff b(1.0); if (b.value() != 1.0 || b.nDerivatives() != 0) { cerr << "SparseDiff b(1.0); failed b = " << b << endl; nerr++; } SparseDiff x(2.0, 1); if (x.value() != 2.0 || x.nDerivatives() != 1 || x.derivative(0) != pair(1, 1)) { cerr << "SparseDiff x(2.0, 1); failed x = " << x << endl; nerr++; } SparseDiff y(x); if (y.value() != x.value() || y.nDerivatives() != x.nDerivatives() || x.derivative(0) != y.derivative(0)) { cerr << "SparseDiff y(x); failed y = " << y << " x = " << x << endl; nerr++; } Float val = 5.0; SparseDiff z(val, 2, 73.); if (z.value() != val || z.nDerivatives() != 1 || z.derivative(0) != pair(2, 73.)) { cerr << "SparseDiff z(val, 2, 73.); failed z = " << z << " val = " << val << endl; nerr++; } } // test the assignment operators { SparseDiff x(3.0, 1); x = 14.0; if (x.value() != 14.0 || x.nDerivatives() != 1) { cerr << "assignment of value failed x : " << x << endl; nerr++; } SparseDiff y(2.0, 2); x = y; if (x.value() != y.value() || y.nDerivatives() != x.nDerivatives() || x.derivative(0) != y.derivative(0)) { cerr << "assignment of other failed x : " << x << " y : " << y << endl; nerr++; } pair z(4, 9); x = z; if (x.value() != y.value() || x.nDerivatives() != 2 || x.derivative(1) != z) { cerr << "assignment of added pair failed x : " << x << endl; nerr++; } pair z1(7, 23); vector > z0, z00; z0.push_back(z1); z0.push_back(z); x = z0; if (x.value() != y.value() || x.nDerivatives() != 2 || x.derivative(0) != z || x.derivative(1) !=z1) { cerr << "assignment of vector failed x : " << x << endl; nerr++; } } // test the class member operators { SparseDiff x(3.0, 0); SparseDiff y(2.0, 1); SparseDiff z; z = x; z *= y; // verify result if (z.value() != (x.value() * y.value()) || z.derivative(0).second != y.value() || z.derivative(1).second != x.value()) { cerr << "*= operator failed" << endl; nerr++; } z = x; z /= y; // verify result if (z.value() != (x.value() / y.value()) || z.derivative(0).second != (1.0/y.value()) || z.derivative(1).second != (-x.value()/(y.value()*y.value()))) { cerr << "/= operator failed" << endl; nerr++; } z = x; z += y; // verify result if (z.value() != (x.value() + y.value()) || z.derivative(0).second != 1 || z.derivative(1).second != 1) { cerr << "+= operator failed" << endl; nerr++; } z = x; z -= y; // verify result if (z.value() != (x.value() - y.value()) || z.derivative(0).second != 1 || z.derivative(1).second != -1) { cerr << "-= operator failed" << endl; nerr++; } } // other class members { SparseDiff x; if (x.nDerivatives() != 0) { cerr << "wrong number of elements, should be 0" << endl; nerr++; } if (!x.isConstant()) { cerr << "x should be const, isConstant reports False" << endl; nerr++; } SparseDiff y(1,1); y.value() = 4.0; if (y.value() != 4.0) { cerr << "value assignment failed" << endl; nerr++; } if (y.isConstant()) { cerr << "y should not be const, isConstant reports True" << endl; nerr++; } } // AutoDIffMath tests { SparseDiff x(3.0,0); SparseDiff y; y = +x; if (y.value() != x.value() || y.nDerivatives() != x.nDerivatives() || x.derivative(0) != y.derivative(0)) { cerr << "operator+(const SparseDiff &) failed" << endl; nerr++; } y = -x; if (y.value() != -x.value() || y.nDerivatives() != x.nDerivatives() || -x.derivative(0).second != y.derivative(0).second) { cerr << "operator-(const SparseDiff &) failed" << endl; nerr++; } y = x + x; if (y.value() != (Float(2.0) * x.value()) || y.nDerivatives() != x.nDerivatives() || y.derivative(0).second != Float(2.0) * x.derivative(0).second) { cerr << "operator+(const SparseDiff &, const SparseDiff &) failed" << endl; nerr++; } y = x - x; if (y.value() != 0.0 || !y.isConstant()) { cerr << "operator-(const SparseDiff &, const SparseDiff &) failed" << endl; nerr++; } y = x * x; if (y.value() != (x.value() * x.value()) || y.nDerivatives() != x.nDerivatives() || y.derivative(0).second != Float(2.0) * x.value() * x.derivative(0).second) { cerr << "operator*(const SparseDiff &, const SparseDiff &) failed" << endl; nerr++; } y = x / x; if (!near(y.value(),Float(1)) || !y.isConstant()) { cerr << "operator/(const SparseDiff &, const SparseDiff &) failed" << endl; nerr++; } y = x + Float(1.0); if (y.value() != (x.value() + Float(1.0)) || y.nDerivatives() != x.nDerivatives() || y.derivative(0) != x.derivative(0)) { cerr << "operator+(const SparseDiff &,const T&) failed" << endl; nerr++; } y = x - Float(1.0); if (y.value() != (x.value() - Float(1.0)) || y.nDerivatives() != x.nDerivatives() || y.derivative(0) != x.derivative(0)) { cerr << "operator-(const SparseDiff &,const T&) failed" << endl; nerr++; } y = x * Float(2.0); if (y.value() != (x.value() * Float(2.0)) || y.nDerivatives() != x.nDerivatives() || y.derivative(0).second != x.derivative(0).second*Float(2)) { cerr << "operator*(const SparseDiff &,const T&) failed" << endl; nerr++; } y = x / Float(2.0); if (y.value() != (x.value() / Float(2.0)) || y.nDerivatives() != x.nDerivatives() || y.derivative(0).second != x.derivative(0).second/Float(2)) { cerr << "operator/(const SparseDiff &,const T&) failed" << endl; nerr++; } y = Float(1.0) + x; if (y.value() != (x.value() + Float(1.0)) || y.nDerivatives() != x.nDerivatives() || y.derivative(0) != x.derivative(0)) { cerr << "operator+(,const T&, const SparseDiff &) failed" << endl; nerr++; } y = Float(1.0) - x; if (y.value() != (Float(1.0) - x.value()) || y.nDerivatives() != x.nDerivatives() || y.derivative(0).second != -x.derivative(0).second) { cerr << "operator-(const T&, const SparseDiff &) failed" << endl; nerr++; } y = Float(2.0) * x; if (y.value() != (x.value() * Float(2.0)) || y.nDerivatives() != x.nDerivatives() || y.derivative(0).second != x.derivative(0).second*Float(2)) { cerr << "operator*(const T&, const SparseDiff &) failed" << endl; nerr++; } y = Float(2.0) / x; if (!near(y.value(),Float(2.0) / x.value()) || !testDer(y,x, -Float(2.0)/(x.value()*x.value()))) { cerr << "operator/(const T&, const SparseDiff &) failed" << endl; nerr++; } // transcendentals x.value() = 0.5; // acos(x) : derivative = -1/sqrt(1-x*x) y = acos(x); if (y.value() != Float(acos(x.value())) || !testDer(y,x, -Float(1.)/Float(sqrt(1.0 - x.value()*x.value())))) { cerr << "acos(const SparseDiff &) failed" << endl; nerr++; } // asin(x) : derivative = 1/sqrt(1-x*x) y = asin(x); if (y.value() != Float(asin(x.value())) || !testDer(y,x, Float(1.)/Float(sqrt(1.0 - x.value()*x.value())))) { cerr << "asin(const SparseDiff &) failed" << endl; nerr++; } // atan(x) : derivative = 1/(1+x*x) y = atan(x); if (!allNearAbs(y.value(), Float(atan(x.value())),1.e-6) || !testDer(y,x, Float(1.)/Float(1.0 + x.value()*x.value()))) { cerr << y.value() - Float(atan(x.value())) << endl; cerr << y.derivative(0).second - x.derivative(0).second/Float(1.0 + x.value()*x.value()) << endl; cerr << "atan(const SparseDiff &) failed" << endl; nerr++; } // atan2(x, y) : derivative = d(atan(x/y)) // = (1/(1+(x/y)*(x/y))) * (dx/y - x*dy/y**2) SparseDiff w(3.0, 0); SparseDiff z(2.5, 1); y = atan2(w, z); if (y.value() != Float(atan2(w.value(), z.value())) || !near(y.derivative(0).second, Float(1)/(Float(1)+w*w/z/z).value()* (w/z).derivative(0).second,1e-5) || !near(y.derivative(1).second, Float(1)/(Float(1)+w*w/z/z).value()* (w/z).derivative(1).second,1e-5)) { cerr << "atan2(const SparseDiff &, const SparseDiff &g) failed" << endl; nerr++; } // cos(x) : derivative = -sin(x) y = cos(x); if (!nearAbs(y.value(), Float(cos(x.value())) ) || !testDer(y,x, -Float(sin(x.value())))) { cerr << "cos(const SparseDiff &) failed" << endl; nerr++; } // cosh(x) : derivative = sinh(x) y = cosh(x); if (y.value() != Float(cosh(x.value())) || !testDer(y,x, Float(sinh(x.value())))) { cerr << "cosh(const SparseDiff &) failed" << endl; nerr++; } // exp(x) : derivative = exp(x) y = exp(x); if (y.value() != Float(exp(x.value())) || !testDer(y,x, Float(exp(x.value())))) { cerr << "exp(const SparseDiff &) failed" << endl; nerr++; } // log(x) : derivative = 1/x y = log(x); if (y.value() != Float(log(x.value())) || !testDer(y,x, Float(1.) / x.value())) { cerr << "log(const SparseDiff &) failed" << endl; nerr++; } // log10(x) : derivative = (1/x) / log(10) y = log10(x); if (y.value() != Float(log10(x.value())) || !testDer(y,x, Float(1)/ Float((x.value()*log(10.0))))) { cerr << "log10(const SparseDiff &) failed" << endl; nerr++; } // pow(x,y) : derivative = y*pow(x,y-1)*dx + pow(x,y)*log(x)*dy y = pow(w,z); if (!near(y.value(), Float(pow(w.value(), z.value())), 1E-7) || !near(y.derivative(0).second, (Float(z.value()*pow(w.value(),z.value()-1))* w.derivative(0).second), 1E-7) || !near(y.derivative(1).second, Float(pow(w.value(),z.value())*log(w.value()))* z.derivative(0).second, 1E-7)) { cerr << "pow(const SparseDiff &, const SparseDiff &) failed" << endl; nerr++; } // pow(x,const) : derivative = const*pow(x,const-1)*dx y = pow((SparseDiff&)x,Float(2.5)); if (y.value() != Float(pow(x.value(),2.5)) || !testDer(y,x, Float(2.5*pow(x.value(),1.5)))) { cerr << "pow(const SparseDiff &, const double &) failed" << endl; nerr++; } // sin(x) : derivative = cos(x) y = sin(x); if (!allNearAbs(y.value(), Float(sin(x.value())) ) || !testDer(y,x, Float(cos(x.value())))) { cerr << "sin(const SparseDiff &) failed" << endl; nerr++; } // sinh(x) : derivative = cosh(x) y = sinh(x); if (!allNearAbs(y.value(), Float(sinh(x.value()))) || !testDer(y,x, Float(cosh(x.value())))) { cerr << "sinh(const SparseDiff &) failed" << endl; nerr++; } // sqrt(x) : derivative = 0.5/sqrt(x) y = sqrt(x); if (!allNearAbs(y.value(), Float(sqrt(x.value()))) || !testDer(y,x, Float(0.5/sqrt(x.value())))) { cerr << "sqrt(const SparseDiff &) failed" << endl; nerr++; } // tan(x) : derivative = sec(x)*sec(x) = 1/(cos(x)*cos(x)) y = tan(x); if (!allNearAbs(y.value(), Float(tan(x.value())) ) || !testDer(y,x, Float(1)/Float(cos(x.value())*cos(x.value())))) { cerr << "tan(const SparseDiff &) failed" << endl; nerr++; } // tanh(x) : derivative = sech(x)*sech(x) = 1/(cosh(x)*cosh(x)) y = tanh(x); if (!allNearAbs(y.value(), Float(tanh(x.value())) ) || !testDer(y,x, Float(1)/Float(cosh(x.value())*cosh(x.value())))) { cerr << "sinh(const SparseDiff &) failed" << endl; nerr++; } } if (nerr != 0) cout << "There were " << nerr << " errors" << endl; else cout << "ok" << endl; return nerr; } casacore-3.7.1/scimath/Mathematics/test/tStatAcc.cc000066400000000000000000000112101476623553700222020ustar00rootroot00000000000000//# tStatAcc.cc: Test program for class StatAcc //# Copyright (C) 1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include // #include // #include #include #include int main() { try { Int v1 = -10; Int v2 = 9; Int v3 = 1; uInt nv = 0; // nr ov test-values Int v; for (v=v1; v<=v2; v+=v3) {nv++;} cout << "value-vector vv: nv=" << nv; cout << " v1=" << v1; cout << " v2=" << v2; cout << " v3=" << v3; cout << endl; cout << "weight-vector ww: 0,nv,1" << endl; // For testing other types, change them here: String caption(" tStatAcc test for "); StatAcc s; // accumulator StatAcc s1; Vector vv(nv,0); // values of required type Block bv(nv); // values of required type Vector ww(nv,0.0); // weights are always Float Block bw(nv); // weights are always Float Int i=0; // index for (v=v1; v<=v2; v+=v3) { vv(i) = v; // Array values bv[i] = v; // Block values ww(i) = i; // Array weights bw[i] = i; // Block weights i++; } s.printSummaryLineHeader(cout,caption); s.printSummaryLine(cout,"After initialisation"); s.reset(); s.printSummaryLine(cout,"After reset"); for (i=0; i<20; i++) { s.put(vv(i)); } s.printSummaryLine(cout,"vv(i)"); s.reset(); for (i=0; i<20; i++) { s.put(vv(i),ww(i)); } s.printSummaryLine(cout,"vv(i), weight=ww(i)"); s.reset(); s.put(vv); s.printSummaryLine(cout,"vv"); s.reset(); s.put(bv); s.printSummaryLine(cout,"bv"); s.reset(); s.put(vv,ww); s.printSummaryLine(cout,"vv, weight=ww"); s.put(vv,ww); s.printSummaryLine(cout,"again"); s.reset(); s.put(bv,bw); s.printSummaryLine(cout,"bv, weight=bw"); s.reset(); s.put(vv); s.put(-50); s.printSummaryLine(cout,"s=vv, plus extra -50"); s1 = s; s1.put(100); s1.printSummaryLine(cout,"s1=s, plus extra 100"); s1 += s; s1.printSummaryLine(cout,"s1 += s"); s1 = s1 + s; s1.printSummaryLine(cout,"s1 = s1 + s"); s1 = s + s1; s1.printSummaryLine(cout,"s1 = s + s1"); s.printSummaryLine(cout,"s"); cout << "Test of s.get-functions: " << endl; cout << " s.getWtot: " << s.getWtot() << endl; cout << " s.getCount: " << s.getCount() << endl; cout << " s.getMean: " << s.getMean() << endl; cout << " s.getRms: " << s.getRms() << endl; cout << " s.getVariance:" << s.getVariance() << endl; cout << " s.getRmsAbs: " << s.getRmsAbs() << endl; cout << " s.getMin: " << s.getMin() << endl; cout << " s.getMax: " << s.getMax() << endl; s.printSummaryList(cout,"Test of s.printSummaryList"); } catch (std::exception& x) { cout << x.what() << endl; return 1; // unexpected error } return 0; // exit with success status } casacore-3.7.1/scimath/Mathematics/test/tVanVleck.cc000066400000000000000000000434551476623553700224110ustar00rootroot00000000000000//# tVanVleck.cc: Test program for class VanVleck //# Copyright (C) 2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include Matrix qfn(Int nlevels, Double thresh, Double dcoff) { // works for odd numbers of levels Matrix result(2,nlevels); for (Int i=0;i<(nlevels-1);i++) { result(0,i) = -(nlevels-2)*thresh+2*i*thresh-dcoff; } for (Int i=0;i rs, const Vector rhos, VanVleck &vv) { cout.precision(9); for (uInt i=0;i " << vv.thresh(n, zerolag) << endl; } int main() { try { VanVleck vv; { // Optimal 3-level by 3-level quantization function: // (I.e., the 3-level by 3-level quantization function for // zero-mean input signals with voltage thresholds set // at the optimal values of +/- ~0.612003 sigma.) Matrix qx, qy; // qx = qfn(3,0.61200318096,0.0); // qy = qx; // vv.setQuantization(qx,qy); vv.setEquiSpaced(0.61200318096, 0.61200318096, 0.0, 0.0, 3); Vector rs, rhos; vv.getTable(rs,rhos); showTable(rs,rhos,vv); cout << "Prediction : " << vv.predict(3,0.61200318096) << endl; } { // Like the above, but if the threshold for the x-quantizer // were set non-optimally at 0.6 sigma and there were a d.c. // offset of 0.01 sigma in the x-inputs, and if the y-quantizer // were set non-optimally at 0.7 sigma and there were a d.c. // offset of -.02 sigma in the y-inputs Matrix qx, qy; //qx = qfn(3,.6,.01); //qy = qfn(3,.7,-.02); //vv.setQuantization(qx,qy); vv.setEquiSpaced(.6, .7, .01, -.02, 3); Vector rs, rhos; vv.getTable(rs,rhos); showTable(rs,rhos,vv); } { // Optimal 3-level by 9-level quantization function: // (I.e., no d.c. offsets, and thresholds set optimally for both // the 3-level x-quantizer's input signal and the 9-level // y-quantizer's input signal.) Matrix qx, qy; qx = qfn(3,0.61200318096,0.0); qy = qfn(9,0.26691110435,0.0); //vv.setQuantization(qx,qy); //Vector rs, rhos; //vv.getTable(rs,rhos); //showTable(rs,rhos,vv); } { // Optimal 9-level by 9-level quantization function: // (I.e., the 9-level by 9-level quantization function for // zero-mean input signals with voltage thresholds set // at the optimal values of +/- (2k-1)*0.266911 sigma, k=1,2,3,4.) Matrix qx, qy; //qx = qfn(9,0.26691110435,0.); //qy = qx; //vv.setQuantization(qx,qy); vv.setEquiSpaced(0.26691110435, 0.26691110435, 0.0, 0.0, 9); Vector rs, rhos; vv.getTable(rs,rhos); showTable(rs,rhos,vv); cout << "Prediction : " << vv.predict(9,0.26691110435) << endl; } { // how long does it take to set up the interpolation fn // do it 100 times for the 9x9 optimized case Timer timer; Matrix qx, qy; // qx = qfn(9,0.26691110435,0.); // qy = qx; for (uInt i=0;i<100;i++) { // vv.setQuantization(qx,qy); vv.setEquiSpaced(0.26691110435, 0.26691110435, 0.0, 0.0, 9); } timer.show("Set up 9x9 100 times"); timer.mark(); // divide up -1 to 1 by 80000 segments and get corresponding one Double rho = -1.0; Double incr = 2.0/80001.0; for (uInt i=0;i<80001;i++) { vv.r(rho); rho += incr; } timer.show("After 80000 calls to ()"); // the chebyshev polynomials for size=65 and // corresponding rs from vv interpolator Double twoN = 2.0*65.0; Double denom = cos(M_PI/twoN); for (uInt i=0;i<65;i++) { Double rho = -cos(Double(2*i+1)*M_PI/twoN)/denom; Double r = vv.r(rho); cout << i << " " << r << " " << rho << endl; } } { // how long does it take to set up the interpolation fn // do it 100 times for the 3x3 optimized case Timer timer; Matrix qx, qy; // qx = qfn(3,0.61200318096,0.0); // qy = qx; for (uInt i=0;i<100;i++) { // vv.setQuantization(qx,qy); vv.setEquiSpaced(0.61200318096, 0.61200318096, 0.0, 0.0, 3); } timer.show("Set up 3x3 100 times"); timer.mark(); // divide up -1 to 1 by 80000 segments and get corresponding one Double rho = -1.0; Double incr = 2.0/80001.0; for (uInt i=0;i<80001;i++) { vv.r(rho); rho += incr; } timer.show("After 80000 calls to ()"); } { // some zero lags to check out showThresh(vv,9,0.570756); showThresh(vv,9,0.5700021); showThresh(vv,9,0.5781991); showThresh(vv,9,0.5776786); showThresh(vv,9,0.5750569); showThresh(vv,9,0.5743146); showThresh(vv,9,0.5788578); showThresh(vv,9,0.5784147); showThresh(vv,9,0.5792729); showThresh(vv,9,0.5786775); showThresh(vv,9,0.5791781); showThresh(vv,9,0.5788941); showThresh(vv,9,0.5839967); showThresh(vv,9,0.5834859); showThresh(vv,9,0.5823558); showThresh(vv,9,0.5821444); showThresh(vv,9,0.3521442); showThresh(vv,9,0.3550488); showThresh(vv,9,0.3824283); showThresh(vv,9,0.3856382); showThresh(vv,9,0.3602951); showThresh(vv,9,0.3632926); showThresh(vv,9,0.3799098); showThresh(vv,9,0.3831574); showThresh(vv,9,0.5692192); showThresh(vv,9,0.5680515); showThresh(vv,9,0.5817156); showThresh(vv,9,0.5808259); showThresh(vv,9,0.5764849); showThresh(vv,9,0.5750596); showThresh(vv,9,0.5776771); showThresh(vv,9,0.5765055); showThresh(vv,9,0.4078194); showThresh(vv,9,0.3736775); showThresh(vv,9,0.4172075); showThresh(vv,9,0.3840109); showThresh(vv,9,0.3968355); showThresh(vv,9,0.3628625); showThresh(vv,9,0.4120503); showThresh(vv,9,0.3788624); showThresh(vv,9,0.369375); showThresh(vv,9,0.3359462); showThresh(vv,9,0.4058436); showThresh(vv,9,0.3726806); showThresh(vv,9,0.4196527); showThresh(vv,9,0.3851627); showThresh(vv,9,0.4170341); showThresh(vv,9,0.3837453); showThresh(vv,9,0.334571); showThresh(vv,9,0.3447004); showThresh(vv,9,0.3494235); showThresh(vv,9,0.3602981); showThresh(vv,9,0.3617256); showThresh(vv,9,0.371273); showThresh(vv,9,0.3754921); showThresh(vv,9,0.3855272); showThresh(vv,9,0.3697195); showThresh(vv,9,0.3791553); showThresh(vv,9,0.3827082); showThresh(vv,9,0.3925593); showThresh(vv,9,0.3797117); showThresh(vv,9,0.3890676); showThresh(vv,9,0.3922659); showThresh(vv,9,0.4019398); showThresh(vv,9,0.3185588); showThresh(vv,9,0.3293913); showThresh(vv,9,0.3343834); showThresh(vv,9,0.3460897); showThresh(vv,9,0.3112122); showThresh(vv,9,0.321894); showThresh(vv,9,0.3273709); showThresh(vv,9,0.3390449); showThresh(vv,9,0.3029225); showThresh(vv,9,0.3131599); showThresh(vv,9,0.319591); showThresh(vv,9,0.3309426); showThresh(vv,9,0.2922837); showThresh(vv,9,0.3022471); showThresh(vv,9,0.3095268); showThresh(vv,9,0.3207578); showThresh(vv,9,0.921033); showThresh(vv,9,0.9143527); showThresh(vv,9,0.9207418); showThresh(vv,9,0.9137397); showThresh(vv,9,0.9209988); showThresh(vv,9,0.9143579); showThresh(vv,9,0.9206958); showThresh(vv,9,0.9137287); showThresh(vv,9,0.9190011); showThresh(vv,9,0.9704426); showThresh(vv,9,0.9174222); showThresh(vv,9,0.9698221); showThresh(vv,9,0.7692035); showThresh(vv,9,0.8275466); showThresh(vv,9,0.7629184); showThresh(vv,9,0.8213883); showThresh(vv,9,0.5394604); showThresh(vv,9,0.5656639); showThresh(vv,9,0.544381); showThresh(vv,9,0.5701309); showThresh(vv,9,0.5379332); showThresh(vv,9,0.5654352); showThresh(vv,9,0.5429364); showThresh(vv,9,0.5700172); showThresh(vv,9,0.5435538); showThresh(vv,9,0.5697622); showThresh(vv,9,0.5486179); showThresh(vv,9,0.5743307); showThresh(vv,9,0.5498347); showThresh(vv,9,0.5750396); showThresh(vv,9,0.5545064); showThresh(vv,9,0.5792525); showThresh(vv,9,0.3203699); showThresh(vv,9,0.359142); showThresh(vv,9,0.3347408); showThresh(vv,9,0.3744646); showThresh(vv,9,0.2933097); showThresh(vv,9,0.3306891); showThresh(vv,9,0.3089331); showThresh(vv,9,0.3478108); showThresh(vv,9,0.2961192); showThresh(vv,9,0.3336673); showThresh(vv,9,0.312097); showThresh(vv,9,0.3510777); showThresh(vv,9,0.3096372); showThresh(vv,9,0.3479083); showThresh(vv,9,0.3247232); showThresh(vv,9,0.3641273); showThresh(vv,9,0.5459048); showThresh(vv,9,0.5712026); showThresh(vv,9,0.5507359); showThresh(vv,9,0.5755962); showThresh(vv,9,0.3489026); showThresh(vv,9,0.341387); showThresh(vv,9,0.3715567); showThresh(vv,9,0.3716146); showThresh(vv,9,0.3431585); showThresh(vv,9,0.3354017); showThresh(vv,9,0.366536); showThresh(vv,9,0.3665553); showThresh(vv,9,0.3436162); showThresh(vv,9,0.3362611); showThresh(vv,9,0.3669193); showThresh(vv,9,0.3673983); showThresh(vv,9,0.3447839); showThresh(vv,9,0.3374888); showThresh(vv,9,0.3680037); showThresh(vv,9,0.3685214); showThresh(vv,9,0.359266); showThresh(vv,9,0.353186); showThresh(vv,9,0.3811512); showThresh(vv,9,0.3823101); showThresh(vv,9,0.3599611); showThresh(vv,9,0.3536995); showThresh(vv,9,0.3817651); showThresh(vv,9,0.3827734); showThresh(vv,9,0.3604349); showThresh(vv,9,0.3541578); showThresh(vv,9,0.3822528); showThresh(vv,9,0.3831535); showThresh(vv,9,0.361209); showThresh(vv,9,0.3549063); showThresh(vv,9,0.3829554); showThresh(vv,9,0.3838758); showThresh(vv,9,0.3530525); showThresh(vv,9,0.3403655); showThresh(vv,9,0.3767908); showThresh(vv,9,0.3668419); showThresh(vv,9,0.3532979); showThresh(vv,9,0.3409442); showThresh(vv,9,0.3770015); showThresh(vv,9,0.3674191); showThresh(vv,9,0.3543381); showThresh(vv,9,0.3419594); showThresh(vv,9,0.3779769); showThresh(vv,9,0.3683796); showThresh(vv,9,0.3544584); showThresh(vv,9,0.3417986); showThresh(vv,9,0.378097); showThresh(vv,9,0.368221); showThresh(vv,9,0.3394508); showThresh(vv,9,0.3260299); showThresh(vv,9,0.3647039); showThresh(vv,9,0.3541952); showThresh(vv,9,0.3392384); showThresh(vv,9,0.3259756); showThresh(vv,9,0.3644707); showThresh(vv,9,0.3541369); showThresh(vv,9,0.3420936); showThresh(vv,9,0.3293294); showThresh(vv,9,0.367372); showThresh(vv,9,0.3575394); showThresh(vv,9,0.3458747); showThresh(vv,9,0.3332081); showThresh(vv,9,0.3710668); showThresh(vv,9,0.3613658); showThresh(vv,9,0.2775754); showThresh(vv,9,0.274166); showThresh(vv,9,0.3000241); showThresh(vv,9,0.3013617); showThresh(vv,9,0.2779866); showThresh(vv,9,0.2747351); showThresh(vv,9,0.3003704); showThresh(vv,9,0.3018582); showThresh(vv,9,0.2779422); showThresh(vv,9,0.2748132); showThresh(vv,9,0.3003416); showThresh(vv,9,0.3019316); showThresh(vv,9,0.2782591); showThresh(vv,9,0.2750526); showThresh(vv,9,0.3006591); showThresh(vv,9,0.3021792); showThresh(vv,9,0.2641738); showThresh(vv,9,0.2611372); showThresh(vv,9,0.2877783); showThresh(vv,9,0.289629); showThresh(vv,9,0.2641329); showThresh(vv,9,0.2610094); showThresh(vv,9,0.2877155); showThresh(vv,9,0.289522); showThresh(vv,9,0.2648556); showThresh(vv,9,0.2616547); showThresh(vv,9,0.2884237); showThresh(vv,9,0.2901475); showThresh(vv,9,0.2652715); showThresh(vv,9,0.2619487); showThresh(vv,9,0.2888355); showThresh(vv,9,0.2904285); showThresh(vv,9,0.2822566); showThresh(vv,9,0.2664075); showThresh(vv,9,0.3056722); showThresh(vv,9,0.2893118); showThresh(vv,9,0.2820567); showThresh(vv,9,0.266308); showThresh(vv,9,0.3053422); showThresh(vv,9,0.2891054); showThresh(vv,9,0.2820061); showThresh(vv,9,0.2661623); showThresh(vv,9,0.3052764); showThresh(vv,9,0.2889432); showThresh(vv,9,0.2671696); showThresh(vv,9,0.2508954); showThresh(vv,9,0.2917009); showThresh(vv,9,0.2749783); showThresh(vv,9,0.2669829); showThresh(vv,9,0.2505649); showThresh(vv,9,0.2915318); showThresh(vv,9,0.2745961); showThresh(vv,9,0.2667687); showThresh(vv,9,0.2501054); showThresh(vv,9,0.2913115); showThresh(vv,9,0.2741979); showThresh(vv,9,0.1216316); showThresh(vv,9,0.1131639); showThresh(vv,9,0.1322801); showThresh(vv,9,0.1241305); showThresh(vv,9,0.1217624); showThresh(vv,9,0.1133628); showThresh(vv,9,0.1324191); showThresh(vv,9,0.1243321); showThresh(vv,9,0.1220046); showThresh(vv,9,0.1134972); showThresh(vv,9,0.1326797); showThresh(vv,9,0.1244635); showThresh(vv,9,0.1222211); showThresh(vv,9,0.1136796); showThresh(vv,9,0.1329199); showThresh(vv,9,0.1246549); showThresh(vv,9,0.122408); showThresh(vv,9,0.1138413); showThresh(vv,9,0.1331067); showThresh(vv,9,0.1248384); showThresh(vv,9,0.1225752); showThresh(vv,9,0.1139635); showThresh(vv,9,0.1332604); showThresh(vv,9,0.1249742); showThresh(vv,9,0.1227315); showThresh(vv,9,0.1140941); showThresh(vv,9,0.1334585); showThresh(vv,9,0.1250851); showThresh(vv,9,0.1229062); showThresh(vv,9,0.114181); showThresh(vv,9,0.1335968); showThresh(vv,9,0.125194); showThresh(vv,9,0.1230138); showThresh(vv,9,0.1142716); showThresh(vv,9,0.1337085); showThresh(vv,9,0.125279); showThresh(vv,9,0.1230808); showThresh(vv,9,0.114302); showThresh(vv,9,0.1337986); showThresh(vv,9,0.1253237); showThresh(vv,9,0.1232614); showThresh(vv,9,0.1143428); showThresh(vv,9,0.1339932); showThresh(vv,9,0.1253591); showThresh(vv,9,0.1234251); showThresh(vv,9,0.1144401); showThresh(vv,9,0.1341681); showThresh(vv,9,0.1254652); Double thresh = 0.1254652*16.0; Double result = vv.thresh(9,thresh); cout << thresh << " -> " << result << " -> " << vv.predict(9,result) << endl; thresh = 9.25119; result = vv.thresh(9,thresh); cout << thresh << " -> " << result << " -> " << vv.predict(9,result) << endl; Matrix qx, qy; // qx = qfn(9,result,0.); // qy = qx; // vv.setQuantization(qx,qy); vv.setEquiSpaced(result, result, 0.0, 0.0, 9); Vector rs, rhos; vv.getTable(rs,rhos); showTable(rs,rhos,vv); cout << "vv.r(zerolag) : " << vv.r(thresh) << endl; // test of dcoff Double zerolag = 0.4925; Double bias = 6.7e-4; cout << "vv.dcoff for n==3 and zerolag==" << zerolag << " and bias == " << bias << endl; Double dcoffset, threshold; cout << "return value : " << vv.dcoff(dcoffset, threshold, 3, zerolag, bias) << endl; cout << "dcoffset : " << dcoffset << endl; cout << "threshold: " << threshold << endl; // verify that for n==9 case, thresh == thresh(9,zerolag) // and dcoffset == 0.0 cout << "Test that n==9 returns default value" < #include #include #include #include #include void writeResult( bool failed ){ if ( failed ){ cout << "Failed"< result = VectorKernel::make(VectorKernel::HANNING, 0.0, SHAPE, true, true); if ( static_cast (result.size()) != SHAPE ){ failed = true; cout << "Unexpected result size="< expectedResult(SHAPE); expectedResult[0] = 0.5; expectedResult[1] = 1; expectedResult[2] = 0.5; for ( int i = 0; i < SHAPE; i++ ){ if ( abs( result[i] - expectedResult[i]) > .0000001 ){ cout <<"Result "< result = VectorKernel::make(VectorKernel::HANNING, 0.0, SHAPE, true, false); if ( static_cast (result.size()) != SHAPE ){ failed = true; cout << "Unexpected result size="< expectedResult(SHAPE); expectedResult[0] = 0.25; expectedResult[1] = 0.5; expectedResult[2] = 0.25; for ( int i = 0; i < SHAPE; i++ ){ if ( abs( result[i] - expectedResult[i]) > .0000001 ){ cout <<"Result "< result = VectorKernel::make(VectorKernel::HANNING, 0.0, SHAPE, true, true); if ( static_cast (result.size()) != SHAPE ){ failed = true; cout << "Unexpected result size="< expectedResult(SHAPE); expectedResult[0] = 0.25; expectedResult[1] = 0.75; expectedResult[2] = 1; expectedResult[3] = 0.75; expectedResult[4] = 0.25; for ( int i = 0; i < SHAPE; i++ ){ if ( abs( result[i] - expectedResult[i]) > .0000001 ){ cout <<"Result "< result = VectorKernel::make(VectorKernel::HANNING, 0.0, SHAPE, true, false); if ( static_cast (result.size()) != SHAPE ){ failed = true; cout << "Unexpected result size="< expectedResult(SHAPE); expectedResult[0] = 0.08333333; expectedResult[1] = 0.25; expectedResult[2] = 0.33333333; expectedResult[3] = 0.25; expectedResult[4] = 0.08333333; for ( int i = 0; i < SHAPE; i++ ){ if ( abs( result[i] - expectedResult[i]) > .0000001 ){ cout <<"Result "< #include #include #include #include namespace casacore { // The biweight algorithm is a robust iterative algorithm that computes two // quantities called the "location" and the "scale", which are analogous to // the mean and the standard deviation. Important equations are // // A. How to compute u_i values, which are related to the weights, // w_i = (1 - u_i*u_i) if abs(u_i) < 1, 0 otherwise, using the equation // // u_i = (x_i - c_bi)/(c*s_bi) (1) // // where x_i are the data values, c_bi is the biweight location, c is a // configurable constant, and s_bi is the biweight scale. For the initial // computation of the u_i values, c_bi is set equal to the median of the // distribution and s_bi is set equal to the normalized median of the // absolute deviation about the median (that is the median of the absolute // deviation about the median multiplied by the value of the probit function // at 0.75). // B The location, c_bi, is computed from // // c_bi = sum(x_i * w_i^2)/sum(w_i^2) (2) // // where only values of u_i which satisfy abs(u_i) < 1 (w_i > 0) are used in // the sums. // C. The scale value is computed using // // n * sum((x_i - c_bi)^2 * w_i^4) // s_bi^2 = _______________________________ (3) // p * max(1, p - 1) // // where n is the number of points for the entire distribution (which includes // all the data for which abs(u_i) >= 1) and p is given by // // p = abs(sum((w_i) * (w_i - 4*u_i^2))) // // Again, the sums include only data for which abs(u_i) < 1. // // The algorithm proceeds as follows. // 1. Compute initial u_i values from equation (1), setting c_bi equal to the // median of the distribution and s_bi equal to the normalized median of the // absolute deviation about the median. // 2. Compute the initial value of the scale using the u_i values computed in // step 1. using equation 3. // 3. Recompute u_i values using the most recent previous scale and location // values. // 4. Compute the location using the u_i values from step 3 and equation (2). // 5. Recompute u_i values using the most recent previous scale and location // values. // 6. Compute the new scale value using the the u_i values computed in step 5 // and the value of the location computed in step 4. // 7. Steps 3. - 6. are repeated until convergence occurs or the maximum number // of iterations (a configurable parameter) is reached. The convergence // criterion is given by // // abs(1 - s_bi/s_bi,prev) < 0.03 * sqrt(0.5/(n - 1)) // // where s_bi,prev is the value of the scale computed in the previous // iteration. // // SPECIAL CASE TO FACILITATE SPEED // // In the special case where maxNiter is specified to be negative, the algorithm // proceeds as follows // 1. Compute u_i values using the median for the location and the normalized // median of the absolute deviation about the median as the scale // 2. Compute the location and scale (which can be carried out simultaneously) // using the u_i values computed in step 1. The value of the location is just // the median that is used in equation (3) to compute the scale // // IMPORTANT NOTE REGARDING USER SPECIFIED WEIGHTS // // Although user-specified weights can be supplied, they are effectively ignored // by this algorithm, except for data which have weights of zero, which are // ignored. // This is a derived class of ClassicalStatistics, rather than // ConstrainedRangeStatistics, because if behaves differently from // ConstrainedRangeStatistics and does not need to use any methods in that // class, so making it a specialization of the higher level ClassicalStatistics // seems the better choice. template < class AccumType, class DataIterator, class MaskIterator=const Bool*, class WeightsIterator=DataIterator > class BiweightStatistics : public ClassicalStatistics { public: BiweightStatistics(Int maxNiter=3, Double c=6.0); // copy semantics BiweightStatistics(const BiweightStatistics& other); virtual ~BiweightStatistics(); // copy semantics BiweightStatistics& operator=( const BiweightStatistics& other ); virtual StatisticsData::ALGORITHM algorithm() const; // Clone this instance virtual StatisticsAlgorithm* clone() const; // // these statistics are not supported. The methods, which override // the virtual ancestor versions, throw exceptions. virtual AccumType getMedian( std::shared_ptr knownNpts=nullptr, std::shared_ptr knownMin=nullptr, std::shared_ptr knownMax=nullptr, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt nBins=10000 ); virtual AccumType getMedianAndQuantiles( std::map& quantileToValue, const std::set& quantiles, std::shared_ptr knownNpts=nullptr, std::shared_ptr knownMin=nullptr, std::shared_ptr knownMax=nullptr, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt nBins=10000 ); virtual AccumType getMedianAbsDevMed( std::shared_ptr knownNpts=nullptr, std::shared_ptr knownMin=nullptr, std::shared_ptr knownMax=nullptr, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt nBins=10000 ); virtual std::map getQuantiles( const std::set& quantiles, std::shared_ptr npts=nullptr, std::shared_ptr min=nullptr, std::shared_ptr max=nullptr, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt nBins=10000 ); virtual std::pair getStatisticIndex( StatisticsData::STATS stat ); // // returns the number of iterations performed to // compute the current location and scale values Int getNiter() const; // reset object to initial state. Clears all private fields including data, // accumulators, etc. virtual void reset(); // If c is True, an exception is thrown; this algorithm does not support // computing stats as data are added. virtual void setCalculateAsAdded(Bool c); // Provide guidance to algorithms by specifying a priori which statistics // the caller would like calculated. This algorithm always needs to compute // the location (MEAN) and the scale (STDDEV) so these statistics are always // added to the input set, which is why this method overrides the base class // version. virtual void setStatsToCalculate(std::set& stats); protected: void _computeStats(); virtual StatsData _getStatistics(); private: Double _c{0}; Int _niter{0}, _maxNiter{0}; AccumType _location{0}, _scale{0}; std::pair _range{}; // _npts is the number of points computed using ClassicalStatistics uInt64 _npts{0}; // because the compiler gets confused if these aren't explicitly typed static const AccumType FOUR; static const AccumType FIVE; void _computeLocationAndScaleSums( AccumType& sxw2, AccumType& sw2, AccumType& sx_M2w4, AccumType& ww_4u2, DataIterator dataIter, MaskIterator maskIter, WeightsIterator weightsIter, uInt64 dataCount, const typename StatisticsDataset::ChunkData& chunk ); void _computeLocationSums( AccumType& sxw2, AccumType& sw2, DataIterator dataIter, MaskIterator maskIter, WeightsIterator weightsIter, uInt64 dataCount, const typename StatisticsDataset::ChunkData& chunk ); void _computeScaleSums( AccumType& sx_M2w4, AccumType& ww_4u2, DataIterator dataIter, MaskIterator maskIter, WeightsIterator weightsIter, uInt64 dataCount, const typename StatisticsDataset::ChunkData& chunk ) const; void _doLocationAndScale(); void _doLocation(); void _doScale(); // // sxw2 = sum(x_i*(1 - u_i^2)^2) // sw2 = sum((1-u_i^2)^2) // sx_M2w4 = sum((x_i - _location)^2 * (1 - u_i^2)^4) = sum((x_i - _location)^2 * w_i^4) // ww_4u2 = sum((1 - u_i^2) * (1 - 5*u_i^2)) = sum(w_i * (w_i - 4*u_i^2)) void _locationAndScaleSums( AccumType& sxw2, AccumType& sw2, AccumType& sx_M2w4, AccumType& ww_4u2, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ) const; void _locationAndScaleSums( AccumType& sxw2, AccumType& sw2, AccumType& sx_M2w4, AccumType& ww_4u2, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; void _locationAndScaleSums( AccumType& sxw2, AccumType& sw2, AccumType& sx_M2w4, AccumType& ww_4u2, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; void _locationAndScaleSums( AccumType& sxw2, AccumType& sw2, AccumType& sx_M2w4, AccumType& ww_4u2, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; void _locationAndScaleSums( AccumType& sxw2, AccumType& sw2, AccumType& sx_M2w4, AccumType& ww_4u2, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) const; void _locationAndScaleSums( AccumType& sxw2, AccumType& sw2, AccumType& sx_M2w4, AccumType& ww_4u2, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; void _locationAndScaleSums( AccumType& sxw2, AccumType& sw2, AccumType& sx_M2w4, AccumType& ww_4u2, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; void _locationAndScaleSums( AccumType& sxw2, AccumType& sw2, AccumType& sx_M2w4, AccumType& ww_4u2, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; // // // sxw2 = sum(x_i*(1 - u_i^2)^2) // sw2 = sum((1-u_i^2)^2) void _locationSums( AccumType& sxw2, AccumType& sw2, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ) const; void _locationSums( AccumType& sxw2, AccumType& sw2, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; void _locationSums( AccumType& sxw2, AccumType& sw2, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; void _locationSums( AccumType& sxw2, AccumType& sw2, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; void _locationSums( AccumType& sxw2, AccumType& sw2, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) const; void _locationSums( AccumType& sxw2, AccumType& sw2, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; void _locationSums( AccumType& sxw2, AccumType& sw2, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; void _locationSums( AccumType& sxw2, AccumType& sw2, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; // // // sx_M2w4 = sum((x_i - _location)^2 * (1 - u_i^2)^4) = sum((x_i - _location)^2 * w_i^4) // ww_4u2 = sum((1 - u_i^2) * (1 - 5*u_i^2)) = sum(w_i * (w_i - 4*u_i^2)) void _scaleSums( AccumType& sx_M2w4, AccumType& ww_4u2, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ) const; void _scaleSums( AccumType& sx_M2w4, AccumType& ww_4u2, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; void _scaleSums( AccumType& sx_M2w4, AccumType& ww_4u2, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; void _scaleSums( AccumType& sx_M2w4, AccumType& ww_4u2, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; void _scaleSums( AccumType& sx_M2w4, AccumType& ww_4u2, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) const; void _scaleSums( AccumType& sx_M2w4, AccumType& ww_4u2, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; void _scaleSums( AccumType& sx_M2w4, AccumType& ww_4u2, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; void _scaleSums( AccumType& sx_M2w4, AccumType& ww_4u2, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; // }; } #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif #endif casacore-3.7.1/scimath/StatsFramework/BiweightStatistics.tcc000066400000000000000000001216611476623553700242470ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_BIWEIGHTESTATISTICS_TCC #define SCIMATH_BIWEIGHTESTATISTICS_TCC #include #include #include #include #include #include #include namespace casacore { CASA_STATD const AccumType BiweightStatistics::FOUR = 4; CASA_STATD const AccumType BiweightStatistics::FIVE = 5; CASA_STATD BiweightStatistics::BiweightStatistics(Int maxNiter, Double c) : ClassicalStatistics(), _c(c), _maxNiter(maxNiter) { this->_setUnsupportedStatistics( BiweightStatisticsData::getUnsupportedStats() ); } CASA_STATD BiweightStatistics::BiweightStatistics( const BiweightStatistics& other ) : ClassicalStatistics(other), _c(other._c), _niter(other._niter), _maxNiter(other._maxNiter), _location(other._location), _scale(other._scale), _range(other._range), _npts(other._npts) {} CASA_STATD BiweightStatistics::~BiweightStatistics() {} CASA_STATD BiweightStatistics& BiweightStatistics::operator=( const BiweightStatistics& other ) { if (this != &other) { ClassicalStatistics::operator=(other); _c = other._c; _niter = other._niter; _maxNiter = other._maxNiter; _location = other._location; _scale = other._scale; _range = other._range; _npts = other._npts; } return *this; } CASA_STATD StatisticsData::ALGORITHM BiweightStatistics::algorithm() const { return StatisticsData::BIWEIGHT; } CASA_STATD StatisticsAlgorithm* BiweightStatistics::clone() const { return new BiweightStatistics(*this); } CASA_STATD AccumType BiweightStatistics::getMedian( std::shared_ptr, std::shared_ptr, std::shared_ptr, uInt, Bool, uInt ) { ThrowCc( "The biweight algorithm does not support computation of the median" ); } CASA_STATD AccumType BiweightStatistics::getMedianAndQuantiles( std::map&, const std::set&, std::shared_ptr, std::shared_ptr, std::shared_ptr, uInt, Bool, uInt ) { ThrowCc( "The biweight algorithm does not support computation " "of the median nor quantile values" ); } CASA_STATD AccumType BiweightStatistics::getMedianAbsDevMed( std::shared_ptr, std::shared_ptr, std::shared_ptr, uInt, Bool, uInt ) { ThrowCc( "The biweight algorithm does not support computation " "of the median of the absolute deviation from the median" ); } CASA_STATD Int BiweightStatistics::getNiter() const { return _niter; } CASA_STATD std::map BiweightStatistics::getQuantiles( const std::set&, std::shared_ptr, std::shared_ptr, std::shared_ptr, uInt, Bool, uInt ) { ThrowCc( "The biweight algorithm does not support computation of quantile values" ); } CASA_STATD std::pair BiweightStatistics::getStatisticIndex( StatisticsData::STATS ) { ThrowCc( "The biweight algorithm does not support " "computation of statistics index values" ); } CASA_STATD void BiweightStatistics::setCalculateAsAdded(Bool c) { ThrowIf( c, "BiweightStatistics does not support calculating " "statistics incrementally as data sets are added" ); } CASA_STATD void BiweightStatistics::setStatsToCalculate( std::set& stats ) { // we always must compute the location (MEAN) and scale (STDDEV) stats.insert(StatisticsData::MEAN); stats.insert(StatisticsData::STDDEV); ClassicalStatistics::setStatsToCalculate(stats); } CASA_STATD void BiweightStatistics::reset() { ClassicalStatistics::reset(); _location = 0; _scale = 0; _range = std::pair(); _npts = 0; } CASA_STATD void BiweightStatistics::_computeLocationAndScaleSums( AccumType& sxw2, AccumType& sw2, AccumType& sx_M2w4, AccumType& ww_4u2, DataIterator dataIter, MaskIterator maskIter, WeightsIterator weightsIter, uInt64 dataCount, const typename StatisticsDataset::ChunkData& chunk ) { if (chunk.weights) { this->_getStatsData().weighted = True; if (chunk.mask) { this->_getStatsData().masked = True; if (chunk.ranges) { _locationAndScaleSums( sxw2, sw2, sx_M2w4, ww_4u2, dataIter, weightsIter, dataCount, chunk.dataStride, maskIter, chunk.mask->second, chunk.ranges->first, chunk.ranges->second ); } else { _locationAndScaleSums( sxw2, sw2, sx_M2w4, ww_4u2, dataIter, weightsIter, dataCount, chunk.dataStride, maskIter, chunk.mask->second ); } } else if (chunk.ranges) { _locationAndScaleSums( sxw2, sw2, sx_M2w4, ww_4u2, dataIter, weightsIter, dataCount, chunk.dataStride, chunk.ranges->first, chunk.ranges->second ); } else { // has weights, but no mask nor ranges _locationAndScaleSums( sxw2, sw2, sx_M2w4, ww_4u2, dataIter, weightsIter, dataCount, chunk.dataStride ); } } else if (chunk.mask) { this->_getStatsData().masked = True; // this data set has no weights, but does have a mask if (chunk.ranges) { _locationAndScaleSums( sxw2, sw2, sx_M2w4, ww_4u2, dataIter, dataCount, chunk.dataStride, maskIter, chunk.mask->second, chunk.ranges->first, chunk.ranges->second ); } else { _locationAndScaleSums( sxw2, sw2, sx_M2w4, ww_4u2, dataIter, dataCount, chunk.dataStride, maskIter, chunk.mask->second ); } } else if (chunk.ranges) { // this data set has no weights no mask, but does have a set of ranges // associated with it _locationAndScaleSums( sxw2, sw2, sx_M2w4, ww_4u2, dataIter, dataCount, chunk.dataStride, chunk.ranges->first, chunk.ranges->second ); } else { // simplest case, this data set has no weights, no mask, nor any ranges // associated with it, and its stride is 1. No filtering of the data is // necessary. _locationAndScaleSums( sxw2, sw2, sx_M2w4, ww_4u2, dataIter, dataCount, chunk.dataStride ); } } CASA_STATD void BiweightStatistics::_computeLocationSums( AccumType& sxw2, AccumType& sw2, DataIterator dataIter, MaskIterator maskIter, WeightsIterator weightsIter, uInt64 dataCount, const typename StatisticsDataset::ChunkData& chunk ) { if (chunk.weights) { // no need to put these in atomic or critical blocks because // they always get set to True here this->_getStatsData().weighted = True; if (chunk.mask) { this->_getStatsData().masked = True; if (chunk.ranges) { _locationSums( sxw2, sw2, dataIter, weightsIter, dataCount, chunk.dataStride, maskIter, chunk.mask->second, chunk.ranges->first, chunk.ranges->second ); } else { _locationSums( sxw2, sw2, dataIter, weightsIter, dataCount, chunk.dataStride, maskIter, chunk.mask->second ); } } else if (chunk.ranges) { _locationSums( sxw2, sw2, dataIter, weightsIter, dataCount, chunk.dataStride, chunk.ranges->first, chunk.ranges->second ); } else { // has weights, but no mask nor ranges _locationSums( sxw2, sw2, dataIter, weightsIter, dataCount, chunk.dataStride ); } } else if (chunk.mask) { this->_getStatsData().masked = True; // this data set has no weights, but does have a mask if (chunk.ranges) { _locationSums( sxw2, sw2, dataIter, dataCount, chunk.dataStride, maskIter, chunk.mask->second, chunk.ranges->first, chunk.ranges->second ); } else { _locationSums( sxw2, sw2, dataIter, dataCount, chunk.dataStride, maskIter, chunk.mask->second ); } } else if (chunk.ranges) { // this data set has no weights no mask, but does have a set of ranges // associated with it _locationSums( sxw2, sw2, dataIter, dataCount, chunk.dataStride, chunk.ranges->first, chunk.ranges->second ); } else { // simplest case, this data set has no weights, no mask, nor any ranges // associated with it, and its stride is 1. No filtering of the data is // necessary. _locationSums( sxw2, sw2, dataIter, dataCount, chunk.dataStride ); } } CASA_STATD void BiweightStatistics::_computeScaleSums( AccumType& sx_M2w4, AccumType& ww_4u2, DataIterator dataIter, MaskIterator maskIter, WeightsIterator weightsIter, uInt64 dataCount, const typename StatisticsDataset::ChunkData& chunk ) const { if (chunk.weights) { if (chunk.mask) { if (chunk.ranges) { _scaleSums( sx_M2w4, ww_4u2, dataIter, weightsIter, dataCount, chunk.dataStride, maskIter, chunk.mask->second, chunk.ranges->first, chunk.ranges->second ); } else { _scaleSums( sx_M2w4, ww_4u2, dataIter, weightsIter, dataCount, chunk.dataStride, maskIter, chunk.mask->second ); } } else if (chunk.ranges) { _scaleSums( sx_M2w4, ww_4u2, dataIter, weightsIter, dataCount, chunk.dataStride, chunk.ranges->first, chunk.ranges->second ); } else { // has weights, but no mask nor ranges _scaleSums( sx_M2w4, ww_4u2, dataIter, weightsIter, dataCount, chunk.dataStride ); } } else if (chunk.mask) { // this data set has no weights, but does have a mask if (chunk.ranges) { _scaleSums( sx_M2w4, ww_4u2, dataIter, dataCount, chunk.dataStride, maskIter, chunk.mask->second, chunk.ranges->first, chunk.ranges->second ); } else { _scaleSums( sx_M2w4, ww_4u2, dataIter, dataCount, chunk.dataStride, maskIter, chunk.mask->second ); } } else if (chunk.ranges) { // this data set has no weights no mask, but does have a set of ranges // associated with it _scaleSums( sx_M2w4, ww_4u2, dataIter, dataCount, chunk.dataStride, chunk.ranges->first, chunk.ranges->second ); } else { // simplest case, this data set has no weights, no mask, nor any ranges // associated with it, and its stride is 1. No filtering of the data is // necessary. _scaleSums( sx_M2w4, ww_4u2, dataIter, dataCount, chunk.dataStride ); } } CASA_STATD void BiweightStatistics::_computeStats() { ClassicalStatistics cs(*this); _location = cs.getMedian(); _scale = C::probit_3_4 * cs.getMedianAbsDevMed(); _npts = cs.getNPts(); ThrowIf ( _npts <= 1, "npts is " + String::toString(_npts) + ". There must be at least two " "points to compute the biweight location and scale" ); StatsData& stats = this->_getStatsData(); stats.npts = _npts; AccumType mymin, mymax; cs.getMinMax(mymin, mymax); stats.min.reset(new AccumType(mymin)); stats.max.reset(new AccumType(mymax)); AccumType spread = _c * _scale; _range = std::pair( _location - spread, _location + spread ); if (_maxNiter >= 0) { // initial scale estimation before iteration begins _doScale(); const AccumType epsilon = 0.03*M_SQRT1_2/sqrt(_npts - 1); AccumType prevScale = 0; for (_niter=1; _niter <= _maxNiter; ++_niter) { prevScale = _scale; _doLocation(); // The range must be reset after the location has been computed in // this iteration. note that spread doesn't change after the // _location computation _range = std::pair( _location - spread, _location + spread ); _doScale(); if ( abs(1 - _scale/prevScale) < epsilon || _niter == _maxNiter ) { break; } spread = _c * _scale; _range = std::pair( _location - spread, _location + spread ); } } else { _doLocationAndScale(); _niter = -1; } stats.mean = _location; stats.stddev = _scale; } CASA_STATD void BiweightStatistics::_doLocation() { StatisticsDataset& ds = this->_getDataset(); ds.initIterators(); const uInt nThreadsMax = StatisticsUtilities::nThreadsMax( ds.getDataProvider() ); const uInt dim = ClassicalStatisticsData::CACHE_PADDING*nThreadsMax; std::unique_ptr tsxw2(new AccumType[dim]); std::unique_ptr tsw2(new AccumType[dim]); // initialize the thread-based sums to 0 for (uInt i=0; i dataIter; std::unique_ptr maskIter; std::unique_ptr weightsIter; std::unique_ptr offset; ds.initThreadVars( nBlocks, extra, nthreads, dataIter, maskIter, weightsIter, offset, nThreadsMax ); #ifdef _OPENMP #pragma omp parallel for num_threads(nthreads) #endif for (uInt i=0; i::threadIdx(); uInt64 dataCount = chunk.count - offset[idx8] < blockSize ? extra : blockSize; _computeLocationSums( tsxw2[idx8], tsw2[idx8], dataIter[idx8], maskIter[idx8], weightsIter[idx8], dataCount, chunk ); ds.incrementThreadIters( dataIter[idx8], maskIter[idx8], weightsIter[idx8], offset[idx8], nthreads ); } if (ds.increment(False)) { break; } } AccumType psxw2 = 0; AccumType psw2 = 0; for (uInt i=0; i::_doScale() { StatisticsDataset& ds = this->_getDataset(); ds.initIterators(); const uInt nThreadsMax = StatisticsUtilities::nThreadsMax( ds.getDataProvider() ); const uInt dim = ClassicalStatisticsData::CACHE_PADDING*nThreadsMax; std::unique_ptr tsx_M2w4(new AccumType[dim]); std::unique_ptr tww_4u2(new AccumType[dim]); // initialize the thread-based sums to 0 for (uInt i=0; i dataIter; std::unique_ptr maskIter; std::unique_ptr weightsIter; std::unique_ptr offset; ds.initThreadVars( nBlocks, extra, nthreads, dataIter, maskIter, weightsIter, offset, nThreadsMax ); #ifdef _OPENMP #pragma omp parallel for num_threads(nthreads) #endif for (uInt i=0; i::threadIdx(); uInt64 dataCount = chunk.count - offset[idx8] < blockSize ? extra : blockSize; _computeScaleSums( tsx_M2w4[idx8], tww_4u2[idx8], dataIter[idx8], maskIter[idx8], weightsIter[idx8], dataCount, chunk ); ds.incrementThreadIters( dataIter[idx8], maskIter[idx8], weightsIter[idx8], offset[idx8], nthreads ); } if (ds.increment(False)) { break; } } AccumType psx_M2w4 = 0; AccumType pww_4u2 = 0; for (uInt i=0; i::_doLocationAndScale() { StatisticsDataset& ds = this->_getDataset(); ds.initIterators(); const uInt nThreadsMax = StatisticsUtilities::nThreadsMax( ds.getDataProvider() ); const uInt dim = ClassicalStatisticsData::CACHE_PADDING*nThreadsMax; std::unique_ptr tsxw2(new AccumType[dim]); std::unique_ptr tsw2(new AccumType[dim]); std::unique_ptr tsx_M2w4(new AccumType[dim]); std::unique_ptr tww_4u2(new AccumType[dim]); // initialize the thread-based sums to 0 for (uInt i=0; i dataIter; std::unique_ptr maskIter; std::unique_ptr weightsIter; std::unique_ptr offset; ds.initThreadVars( nBlocks, extra, nthreads, dataIter, maskIter, weightsIter, offset, nThreadsMax ); #ifdef _OPENMP #pragma omp parallel for num_threads(nthreads) #endif for (uInt i=0; i::threadIdx(); uInt64 dataCount = chunk.count - offset[idx8] < blockSize ? extra : blockSize; _computeLocationAndScaleSums( tsxw2[idx8], tsw2[idx8], tsx_M2w4[idx8], tww_4u2[idx8], dataIter[idx8], maskIter[idx8], weightsIter[idx8], dataCount, chunk ); ds.incrementThreadIters( dataIter[idx8], maskIter[idx8], weightsIter[idx8], offset[idx8], nthreads ); } if (ds.increment(False)) { break; } } AccumType psxw2 = 0; AccumType psw2 = 0; AccumType psx_M2w4 = 0; AccumType pww_4u2 = 0; for (uInt i=0; i BiweightStatistics::_getStatistics() { StatsData& stats = this->_getStatsData(); if (stats.npts == 0) { _computeStats(); stats = this->_getStatsData(); } return copy(stats); } // Note we purposefully use > and <, rather than >= and <= to agree with // Amanda Kepley's requirement. #define _locationAndScaleSumsCodeBW \ AccumType x = *datum; \ if (x > _range.first && x < _range.second) { \ AccumType x_M = x - _location; \ AccumType u = x_M/(_c*_scale); \ AccumType w = 1 - u*u; \ AccumType w2 = w * w; \ sxw2 += x * w2; \ sw2 += w2; \ AccumType x_M2 = x_M * x_M; \ AccumType w4 = w2 * w2; \ sx_M2w4 += x_M2 * w4; \ ww_4u2 += w * (FIVE*w - FOUR); \ } CASA_STATD void BiweightStatistics::_locationAndScaleSums( AccumType& sxw2, AccumType& sw2, AccumType& sx_M2w4, AccumType& ww_4u2, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ) const { DataIterator datum = dataBegin; uInt64 count = 0; while (count < nr) { _locationAndScaleSumsCodeBW StatisticsIncrementer::increment( datum, count, dataStride ); } } CASA_STATD void BiweightStatistics::_locationAndScaleSums( AccumType& sxw2, AccumType& sw2, AccumType& sx_M2w4, AccumType& ww_4u2, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { DataIterator datum = dataBegin; uInt64 count = 0; typename DataRanges::const_iterator beginRange = ranges.begin(); typename DataRanges::const_iterator endRange = ranges.end(); while (count < nr) { if ( StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _locationAndScaleSumsCodeBW } StatisticsIncrementer::increment( datum, count, dataStride ); } } CASA_STATD void BiweightStatistics::_locationAndScaleSums( AccumType& sxw2, AccumType& sw2, AccumType& sx_M2w4, AccumType& ww_4u2, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { DataIterator datum = dataBegin; MaskIterator mask = maskBegin; uInt64 count = 0; while (count < nr) { if (*mask) { _locationAndScaleSumsCodeBW } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void BiweightStatistics::_locationAndScaleSums( AccumType& sxw2, AccumType& sw2, AccumType& sx_M2w4, AccumType& ww_4u2, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { DataIterator datum = dataBegin; MaskIterator mask = maskBegin; uInt64 count = 0; typename DataRanges::const_iterator beginRange = ranges.begin(); typename DataRanges::const_iterator endRange = ranges.end(); while (count < nr) { if ( *mask && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _locationAndScaleSumsCodeBW } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void BiweightStatistics::_locationAndScaleSums( AccumType& sxw2, AccumType& sw2, AccumType& sx_M2w4, AccumType& ww_4u2, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; uInt64 count = 0; while (count < nr) { if (*weight > 0) { _locationAndScaleSumsCodeBW } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void BiweightStatistics::_locationAndScaleSums( AccumType& sxw2, AccumType& sw2, AccumType& sx_M2w4, AccumType& ww_4u2, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; uInt64 count = 0; typename DataRanges::const_iterator beginRange = ranges.begin(); typename DataRanges::const_iterator endRange = ranges.end(); while (count < nr) { if ( *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _locationAndScaleSumsCodeBW } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void BiweightStatistics::_locationAndScaleSums( AccumType& sxw2, AccumType& sw2, AccumType& sx_M2w4, AccumType& ww_4u2, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; MaskIterator mask = maskBegin; uInt64 count = 0; typename DataRanges::const_iterator beginRange = ranges.begin(); typename DataRanges::const_iterator endRange = ranges.end(); while (count < nr) { if ( *mask && *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _locationAndScaleSumsCodeBW } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } CASA_STATD void BiweightStatistics::_locationAndScaleSums( AccumType& sxw2, AccumType& sw2, AccumType& sx_M2w4, AccumType& ww_4u2, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; MaskIterator mask = maskBegin; uInt64 count = 0; while (count < nr) { if (*mask && *weight > 0) { _locationAndScaleSumsCodeBW } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } // Note we purposefully use > and <, rather than >= and <= to agree with // Amanda Kepley's requirement. #define _locationSumsCodeBW \ AccumType x = *datum; \ if (x > _range.first && x < _range.second) { \ AccumType x_M = x - _location; \ AccumType u = x_M/(_c*_scale); \ AccumType w = 1 - u*u; \ AccumType w2 = w * w; \ sxw2 += x * w2; \ sw2 += w2; \ } CASA_STATD void BiweightStatistics::_locationSums( AccumType& sxw2, AccumType& sw2, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ) const { DataIterator datum = dataBegin; uInt64 count = 0; while (count < nr) { _locationSumsCodeBW StatisticsIncrementer::increment( datum, count, dataStride ); } } CASA_STATD void BiweightStatistics::_locationSums( AccumType& sxw2, AccumType& sw2, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { DataIterator datum = dataBegin; uInt64 count = 0; typename DataRanges::const_iterator beginRange = ranges.begin(); typename DataRanges::const_iterator endRange = ranges.end(); while (count < nr) { if ( StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _locationSumsCodeBW } StatisticsIncrementer::increment( datum, count, dataStride ); } } CASA_STATD void BiweightStatistics::_locationSums( AccumType& sxw2, AccumType& sw2, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { DataIterator datum = dataBegin; MaskIterator mask = maskBegin; uInt64 count = 0; while (count < nr) { if (*mask) { _locationSumsCodeBW } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void BiweightStatistics::_locationSums( AccumType& sxw2, AccumType& sw2, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { DataIterator datum = dataBegin; MaskIterator mask = maskBegin; uInt64 count = 0; typename DataRanges::const_iterator beginRange = ranges.begin(); typename DataRanges::const_iterator endRange = ranges.end(); while (count < nr) { if ( *mask && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _locationSumsCodeBW } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void BiweightStatistics::_locationSums( AccumType& sxw2, AccumType& sw2, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; uInt64 count = 0; while (count < nr) { if (*weight > 0) { _locationSumsCodeBW } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void BiweightStatistics::_locationSums( AccumType& sxw2, AccumType& sw2, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; uInt64 count = 0; typename DataRanges::const_iterator beginRange = ranges.begin(); typename DataRanges::const_iterator endRange = ranges.end(); while (count < nr) { if ( *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _locationSumsCodeBW } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void BiweightStatistics::_locationSums( AccumType& sxw2, AccumType& sw2, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; MaskIterator mask = maskBegin; uInt64 count = 0; typename DataRanges::const_iterator beginRange = ranges.begin(); typename DataRanges::const_iterator endRange = ranges.end(); while (count < nr) { if ( *mask && *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _locationSumsCodeBW } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } CASA_STATD void BiweightStatistics::_locationSums( AccumType& sxw2, AccumType& sw2, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; MaskIterator mask = maskBegin; uInt64 count = 0; while (count < nr) { if (*mask && *weight > 0) { _locationSumsCodeBW } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } // Note we purposefully use > and <, rather than >= and <= to agree with // Amanda Kepley's requirement. #define _scaleSumsCodeBW \ AccumType x = *datum; \ if (x > _range.first && x < _range.second) { \ AccumType x_M = x - _location; \ AccumType x_M2 = x_M * x_M; \ AccumType u = x_M/(_c*_scale); \ AccumType w = 1 - u*u; \ AccumType w2 = w * w; \ AccumType w4 = w2 * w2; \ sx_M2w4 += x_M2 * w4; \ ww_4u2 += w * (FIVE*w - FOUR); \ } CASA_STATD void BiweightStatistics::_scaleSums( AccumType& sx_M2w4, AccumType& ww_4u2, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ) const { DataIterator datum = dataBegin; uInt64 count = 0; while (count < nr) { _scaleSumsCodeBW StatisticsIncrementer::increment( datum, count, dataStride ); } } CASA_STATD void BiweightStatistics::_scaleSums( AccumType& sx_M2w4, AccumType& ww_4u2, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { DataIterator datum = dataBegin; uInt64 count = 0; typename DataRanges::const_iterator beginRange = ranges.begin(); typename DataRanges::const_iterator endRange = ranges.end(); while (count < nr) { if ( StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _scaleSumsCodeBW } StatisticsIncrementer::increment( datum, count, dataStride ); } } CASA_STATD void BiweightStatistics::_scaleSums( AccumType& sx_M2w4, AccumType& ww_4u2, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { DataIterator datum = dataBegin; MaskIterator mask = maskBegin; uInt64 count = 0; while (count < nr) { if (*mask) { _scaleSumsCodeBW } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void BiweightStatistics::_scaleSums( AccumType& sx_M2w4, AccumType& ww_4u2, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { DataIterator datum = dataBegin; MaskIterator mask = maskBegin; uInt64 count = 0; typename DataRanges::const_iterator beginRange = ranges.begin(); typename DataRanges::const_iterator endRange = ranges.end(); while (count < nr) { if ( *mask && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _scaleSumsCodeBW } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void BiweightStatistics::_scaleSums( AccumType& sx_M2w4, AccumType& ww_4u2, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; uInt64 count = 0; while (count < nr) { if (*weight > 0) { _scaleSumsCodeBW } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void BiweightStatistics::_scaleSums( AccumType& sx_M2w4, AccumType& ww_4u2, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; uInt64 count = 0; typename DataRanges::const_iterator beginRange = ranges.begin(); typename DataRanges::const_iterator endRange = ranges.end(); while (count < nr) { if ( *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _scaleSumsCodeBW } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void BiweightStatistics::_scaleSums( AccumType& sx_M2w4, AccumType& ww_4u2, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; MaskIterator mask = maskBegin; uInt64 count = 0; typename DataRanges::const_iterator beginRange = ranges.begin(); typename DataRanges::const_iterator endRange = ranges.end(); while (count < nr) { if ( *mask && *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _scaleSumsCodeBW } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } CASA_STATD void BiweightStatistics::_scaleSums( AccumType& sx_M2w4, AccumType& ww_4u2, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { DataIterator datum = dataBegin; WeightsIterator weight = weightsBegin; MaskIterator mask = maskBegin; uInt64 count = 0; while (count < nr) { if (*mask && *weight > 0) { _scaleSumsCodeBW } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } } #endif casacore-3.7.1/scimath/StatsFramework/BiweightStatisticsData.cc000066400000000000000000000043061476623553700246510ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include namespace casacore { std::set BiweightStatisticsData::_unsupportedStats = std::set(); std::mutex BiweightStatisticsData::_mutex; std::set BiweightStatisticsData::getUnsupportedStats() { std::lock_guard sc(_mutex); if (_unsupportedStats.empty()) { _unsupportedStats.insert(StatisticsData::RMS); _unsupportedStats.insert(StatisticsData::SUM); _unsupportedStats.insert(StatisticsData::SUMSQ); _unsupportedStats.insert(StatisticsData::SUMWEIGHTS); _unsupportedStats.insert(StatisticsData::VARIANCE); _unsupportedStats.insert(StatisticsData::MEDIAN); _unsupportedStats.insert(StatisticsData::MEDABSDEVMED); _unsupportedStats.insert(StatisticsData::FIRST_QUARTILE); _unsupportedStats.insert(StatisticsData::THIRD_QUARTILE); _unsupportedStats.insert(StatisticsData::INNER_QUARTILE_RANGE); } return _unsupportedStats; } } casacore-3.7.1/scimath/StatsFramework/BiweightStatisticsData.h000066400000000000000000000033251476623553700245130ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_BIWEIGHTSTATISTICSDATA_H #define SCIMATH_BIWEIGHTSTATISTICSDATA_H #include #include #include #include namespace casacore { class BiweightStatisticsData { public: BiweightStatisticsData() = delete; BiweightStatisticsData(const BiweightStatisticsData& other) = delete; static std::set getUnsupportedStats(); private: static std::set _unsupportedStats; static std::mutex _mutex; }; } #endif casacore-3.7.1/scimath/StatsFramework/ChauvenetCriterionStatistics.h000066400000000000000000000104271476623553700257610ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_CHAUVENETCRITERIONSTATISTICS_H #define SCIMATH_CHAUVENETCRITERIONSTATISTICS_H #include #include #include #include namespace casacore { // Class to calculate statistics using the so-called Chauvenet criterion. This // method iteratively calculates statistics by discarding outliers on the basis // of Chauvenet's criterion, until the specified maximum number of iterations is // reached, or the final iteration results in no additional points being // discarded. Alternatively, one can specify a z-score which indicates the // number of standard deviations beyond which to discard points, which is held // fixed while iterating. // // When instantiated, objects of this class use a // ConstrainedRangeQuantileComputer object for computing quantile-like // statistics. See class documentation for StatisticsAlgorithm for details of // QuantileComputer classes. template < class AccumType, class DataIterator, class MaskIterator=const Bool*, class WeightsIterator=DataIterator > class ChauvenetCriterionStatistics : public ConstrainedRangeStatistics { public: ChauvenetCriterionStatistics() = delete; // If zscore is not negative, use that value to discard outliers // beyond zscore standard deviations from the mean, and compute statistics // based on the remaining data. If zscore is negative, use // Chauvenet's Criterion to determine which outliers to discard. // maxIterations is the maximum number of iterations to use // before stopping. If negative, continue iterating until the set zscore or // Chauvenet's criterion is met (ie that there are no remaining outliers). ChauvenetCriterionStatistics(Double zscore=-1, Int maxIterations=0); ChauvenetCriterionStatistics( const ChauvenetCriterionStatistics& other ); virtual ~ChauvenetCriterionStatistics(); // copy semantics ChauvenetCriterionStatistics& operator=( const ChauvenetCriterionStatistics& other ); // Clone this instance virtual StatisticsAlgorithm* clone() const; // get the algorithm that this object uses for computing stats virtual StatisticsData::ALGORITHM algorithm() const { return StatisticsData::CHAUVENETCRITERION; }; // reset object to initial state. Clears all private fields including data, // accumulators, global range. It does not affect the fence factor (_f), // which was set at object construction. virtual void reset(); // This class does not allow statistics to be calculated as datasets are // added, so an exception will be thrown if c is True. void setCalculateAsAdded(Bool c); // get the number of iterations uInt getNiter() const { return _niter; } private: Double _zscore{-1}; Int _maxIterations{0}; Bool _rangeIsSet{False}; uInt _niter{0}; void _setRange(); }; } #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif #endif casacore-3.7.1/scimath/StatsFramework/ChauvenetCriterionStatistics.tcc000066400000000000000000000106261476623553700263040ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_CHAUVENETCRITERIONSTATISTICS_TCC #define SCIMATH_CHAUVENETCRITERIONSTATISTICS_TCC #include #include #include #include namespace casacore { CASA_STATD ChauvenetCriterionStatistics::ChauvenetCriterionStatistics( Double zscore, Int maxIterations ) : ConstrainedRangeStatistics( std::shared_ptr>( new ConstrainedRangeQuantileComputer( &this->_getDataset() ) ) ), _zscore(zscore), _maxIterations(maxIterations) {} CASA_STATD ChauvenetCriterionStatistics::ChauvenetCriterionStatistics( const ChauvenetCriterionStatistics& other ) : ConstrainedRangeStatistics(other), _zscore(other._zscore), _maxIterations(other._maxIterations), _rangeIsSet(other._rangeIsSet), _niter(other._niter) {} CASA_STATD ChauvenetCriterionStatistics::~ChauvenetCriterionStatistics() {} CASA_STATD ChauvenetCriterionStatistics& ChauvenetCriterionStatistics::operator=( const ChauvenetCriterionStatistics& other ) { if (this == &other) { return *this; } ConstrainedRangeStatistics::operator=(other); _zscore = other._zscore; _maxIterations = other._maxIterations; _rangeIsSet = other._rangeIsSet; _niter = other._niter; return *this; } CASA_STATD StatisticsAlgorithm* ChauvenetCriterionStatistics::clone() const { return new ChauvenetCriterionStatistics(*this); } CASA_STATD void ChauvenetCriterionStatistics::reset() { ConstrainedRangeStatistics::reset(); _rangeIsSet = False; _niter = 0; } CASA_STATD void ChauvenetCriterionStatistics::setCalculateAsAdded(Bool c) { ThrowIf( c, "ChauvenetCriterionStatistics does not support calculating " "statistics incrementally as data sets are added" ); } CASA_STATD void ChauvenetCriterionStatistics::_setRange() { if (_rangeIsSet) { return; } uInt maxI = _maxIterations >= 0 ? _maxIterations : 1000; uInt64 prevNpts = 0; StatsData sd; while (_niter <= maxI) { if (_niter == 0) { ClassicalStatistics cs(*this); sd = cs.getStatistics(); } else { sd = this->getStatistics(); if ((uInt64)sd.npts == prevNpts) { break; } } Double zScore = _zscore >= 0 ? _zscore : ZScoreCalculator::getMaxZScore((uInt64)sd.npts); auto range = std::make_shared>( sd.mean - zScore*sd.stddev, sd.mean + zScore*sd.stddev ); ConstrainedRangeStatistics::_setRange(range); // _rangeIsSet is set here to prevent infinite // recursion on next loop iteration _rangeIsSet = True; prevNpts = (uInt64)sd.npts; ++_niter; } --_niter; } } #endif casacore-3.7.1/scimath/StatsFramework/ClassicalQuantileComputer.h000066400000000000000000000474671476623553700252430ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_CLASSICALQUANTILECOMPUTER_H #define SCIMATH_CLASSICALQUANTILECOMPUTER_H #include #include #include #include #include #include #include namespace casacore { // This class is used internally by ClassicalStatistics objects. It should never // be explicitly instantiated by an API developer. See the documentation of // StatisticsAlgorithm for details regarding QuantileComputer classes. template < class AccumType, class DataIterator, class MaskIterator=const Bool*, class WeightsIterator=DataIterator > class ClassicalQuantileComputer : public StatisticsAlgorithmQuantileComputer { using LimitPair = std::pair; using LimitPairVectorIter = typename std::vector::const_iterator; using IndexValueMap = typename std::map; using IndexSet = std::set; public: ClassicalQuantileComputer() = delete; ClassicalQuantileComputer(StatisticsDataset* dataset); // copy semantics ClassicalQuantileComputer(const ClassicalQuantileComputer& other); virtual ~ClassicalQuantileComputer(); // copy semantics ClassicalQuantileComputer& operator=( const ClassicalQuantileComputer& other ); // clone this object by returning a pointer to a copy virtual StatisticsAlgorithmQuantileComputer* clone() const; // Caller is responsible for passing correct values of mynpts, mymin, and // mymax; no checking is done for correctness in this method. virtual AccumType getMedian( uInt64 mynpts, AccumType mymin, AccumType mymax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt nBins ); // get the median of the absolute deviation about the median of the data. virtual AccumType getMedianAbsDevMed( uInt64 mynpts, AccumType mymin, AccumType mymax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt nBins ); // If one needs to compute both the median and QuantileComputer values, it // is better to call getMedianAndQuantiles() rather than getMedian() and // getQuantiles() separately, as the first will scan large data sets fewer // times than calling the separate methods. The return value is the median; // the quantiles are returned in the quantiles map. Values in the // fractions set represent the locations in the CDF and should be // between 0 and 1, exclusive. virtual AccumType getMedianAndQuantiles( std::map& quantiles, const std::set& fractions, uInt64 mynpts, AccumType mymin, AccumType mymax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt nBins ); // Get the specified Quantiles. fractions must be between 0 and // 1, noninclusive. virtual std::map getQuantiles( const std::set& fractions, uInt64 mynpts, AccumType mymin, AccumType mymax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt nBins ); // reset the private fields virtual void reset(); protected: // // Get the counts of data within the specified histogram bins. The number of // arrays within binCounts will be equal to the number of histograms in // hist. Each array within binCounts will have the // same number of elements as the number of bins in its corresponding // histogram in hist. virtual void _findBins( std::vector>& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const std::vector>& hist, const std::vector& maxLimit ) const; virtual void _findBins( std::vector>& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const std::vector>& hist, const std::vector& maxLimit ) const; virtual void _findBins( std::vector>& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const std::vector>& hist, const std::vector& maxLimit ) const; virtual void _findBins( std::vector>& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const std::vector>& hist, const std::vector& maxLimit ) const; virtual void _findBins( std::vector>& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const std::vector>& hist, const std::vector& maxLimit ) const ; virtual void _findBins( std::vector>& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const std::vector>& hist, const std::vector& maxLimit ) const; virtual void _findBins( std::vector>& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const std::vector>& hist, const std::vector& maxLimit ) const; virtual void _findBins( std::vector>& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const std::vector>& hist, const std::vector& maxLimit ) const; // // // populate an unsorted array with valid data. // no weights, no mask, no ranges virtual void _populateArray( std::vector& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ) const; // ranges virtual void _populateArray( std::vector& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _populateArray( std::vector& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; // mask and ranges virtual void _populateArray( std::vector& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; // weights virtual void _populateArray( std::vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) const; // weights and ranges virtual void _populateArray( std::vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; // weights and mask virtual void _populateArray( std::vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; // weights, mask, ranges virtual void _populateArray( std::vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; // // // Create a std::vector of unsorted arrays, one array for each bin defined // by includeLimits. includeLimits should be // non-overlapping and should be given in ascending order (the algorithm // used assumes this). Once the sum of the lengths of all arrays equals // maxCount the method will return with no further processing. // no weights, no mask, no ranges virtual void _populateArrays( std::vector>& arys, uInt64& currentCount, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const IncludeLimits& includeLimits, uInt64 maxCount ) const; // ranges virtual void _populateArrays( std::vector>& arys, uInt64& currentCount, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const IncludeLimits& includeLimits, uInt64 maxCount ) const; virtual void _populateArrays( std::vector>& arys, uInt64& currentCount, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const IncludeLimits& includeLimits, uInt64 maxCount ) const; // mask and ranges virtual void _populateArrays( std::vector>& arys, uInt64& currentCount, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const IncludeLimits& includeLimits, uInt64 maxCount ) const; // weights virtual void _populateArrays( std::vector>& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const IncludeLimits& includeLimits, uInt64 maxCount ) const; // weights and ranges virtual void _populateArrays( std::vector>& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const IncludeLimits& includeLimits, uInt64 maxCount ) const; // weights and mask virtual void _populateArrays( std::vector>& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const IncludeLimits& includeLimits, uInt64 maxCount ) const; // weights, mask, ranges virtual void _populateArrays( std::vector>& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const IncludeLimits& includeLimits, uInt64 maxCount ) const; // // // no weights, no mask, no ranges virtual Bool _populateTestArray( std::vector& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, uInt maxElements ) const; // ranges virtual Bool _populateTestArray( std::vector& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const; // mask virtual Bool _populateTestArray( std::vector& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, uInt maxElements ) const; // mask and ranges virtual Bool _populateTestArray( std::vector& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const; // weights virtual Bool _populateTestArray( std::vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, uInt maxElements ) const; // weights and ranges virtual Bool _populateTestArray( std::vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const; // weights and mask virtual Bool _populateTestArray( std::vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, uInt maxElements ) const; // weights, mask, ranges virtual Bool _populateTestArray( std::vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const; // // get values from sorted array if the array is small enough to be held in // memory. Note that this is the array containing all good data, not data in // just a single bin representing a subset of good data. // Returns True if the data were successfully retrieved. // If True is returned, the values map will contain a map of index to value. // It is the caller's responsibility to check that mynpts is not // 0; no checking is done here. Bool _valuesFromSortedArray( std::map& values, uInt64 mynpts, const std::set& indices, uInt64 maxArraySize, Bool persistSortedArray ); private: Bool _doMedAbsDevMed{False}; // for use in often repeatedly run macros AccumType _myMedian{0}; // tally the number of data points that fall into each bin provided by // hist. Any points that are less than hist.minLimit or greater // than hist.minLimit + hist.nBins*hist.binWidth are not included in the // counts. A data point that falls exactly on a bin boundary is considered // to be in the higher index bin. sameVal will be non-null if all // the good values in the histogram range are the same. In that case, the // value held will be the value of each of those data points. std::vector> _binCounts( std::vector>& sameVal, const std::vector>& hist ); void _computeBins( std::vector>& bins, std::vector>& sameVal, std::vector& allSame, DataIterator dataIter, MaskIterator maskIter, WeightsIterator weightsIter, uInt64 count, const std::vector>& hist, const std::vector& maxLimit, const typename StatisticsDataset::ChunkData& chunk ); void _computeDataArray( std::vector& ary, DataIterator dataIter, MaskIterator maskIter, WeightsIterator weightsIter, uInt64 dataCount, const typename StatisticsDataset::ChunkData& chunk ); void _computeDataArrays( std::vector>& arys, uInt64& currentCount, DataIterator dataIter, MaskIterator maskIter, WeightsIterator weightsIter, uInt64 dataCount, const IncludeLimits& includeLimits, uInt64 maxCount, const typename StatisticsDataset::ChunkData& chunk ); // Create an unsorted array of the complete data set. If // includeLimits is specified, only points within those limits // (including min but excluding max, as per definition of bins), are // included. void _createDataArray(std::vector& array); void _createDataArrays( std::vector>& arrays, const IncludeLimits& includeLimits, uInt64 maxCount ); // extract data from multiple histograms given by hist. // dataIndices represent the indices of the sorted arrays of // values to extract. There should be exactly one set of data indices to // extract for each supplied histogram. The data indices are relative to the // minimum value of the minimum bin in their respective histograms. The // ordering of the maps in the returned std::vector represent the ordering // of histograms in hist. hist should contain // non-overlapping histograms and the histograms should be specified in // ascending order. std::vector _dataFromMultipleBins( const std::vector>& hist, uInt64 maxArraySize, const std::vector& dataIndices, uInt nBins ); std::vector _dataFromSingleBins( const std::vector& binNpts, uInt64 maxArraySize, const IncludeLimits& binLimits, const std::vector& dataIndices, uInt nBins ); // get the values for the specified indices in the sorted array of all good // data IndexValueMap _indicesToValues( uInt64 mynpts, AccumType mymin, AccumType mymax, uInt64 maxArraySize, const IndexSet& dataIndices, Bool persistSortedArray, uInt nBins ); // get the index (for odd npts) or indices (for even npts) of the median of // the sorted array. static IndexSet _medianIndices(uInt64 mynpts); }; } #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif #endif casacore-3.7.1/scimath/StatsFramework/ClassicalQuantileComputer.tcc000066400000000000000000002076101476623553700255510ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_CLASSICALQUANTILECOMPUTER_TCC #define SCIMATH_CLASSICALQUANTILECOMPUTER_TCC #include #include #include #include #include #include namespace casacore { CASA_STATD ClassicalQuantileComputer::ClassicalQuantileComputer( StatisticsDataset* dataset ) : StatisticsAlgorithmQuantileComputer(dataset) {} CASA_STATD ClassicalQuantileComputer::~ClassicalQuantileComputer() {} CASA_STATD ClassicalQuantileComputer::ClassicalQuantileComputer( const ClassicalQuantileComputer& other ) : StatisticsAlgorithmQuantileComputer(other), _doMedAbsDevMed(other._doMedAbsDevMed), _myMedian(other._myMedian) {} CASA_STATD ClassicalQuantileComputer& ClassicalQuantileComputer::operator=( const ClassicalQuantileComputer& other ) { if (this == &other) { return *this; } StatisticsAlgorithmQuantileComputer::operator=(other); _doMedAbsDevMed = other._doMedAbsDevMed; _myMedian = other._myMedian; return *this; } CASA_STATD StatisticsAlgorithmQuantileComputer* ClassicalQuantileComputer::clone() const { return new ClassicalQuantileComputer(*this); } CASA_STATD AccumType ClassicalQuantileComputer::getMedian( uInt64 mynpts, AccumType mymin, AccumType mymax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt nBins ) { auto median = this->_getMedian(); if (! median) { auto indices = _medianIndices(mynpts); auto indexToValue = _indicesToValues( mynpts, mymin, mymax, binningThreshholdSizeBytes/sizeof(AccumType), indices, persistSortedArray, nBins ); median.reset (new AccumType( indexToValue.size() == 1 ? indexToValue[*indices.begin()] : ( indexToValue[*indices.begin()] + indexToValue[*indices.rbegin()] )/AccumType(2) )); this->setMedian(median); } return *median; } CASA_STATD AccumType ClassicalQuantileComputer::getMedianAbsDevMed( uInt64 mynpts, AccumType mymin, AccumType mymax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt nBins ) { auto medAbsDevMed = this->_getMedianAbsDevMedian(); if (! medAbsDevMed) { // This call calculates the median of the data set which is stored internally and // used, but is not necessary to be captured in the return value here. getMedian( mynpts, mymin, mymax, binningThreshholdSizeBytes, persistSortedArray, nBins ); auto indices = _medianIndices(mynpts); // throw the proper switch _doMedAbsDevMed = True; _myMedian = *this->_getMedian(); auto indexToValue = _indicesToValues( mynpts, mymin, mymax, binningThreshholdSizeBytes/sizeof(AccumType), indices, persistSortedArray, nBins ); _doMedAbsDevMed = False; medAbsDevMed.reset( indexToValue.size() == 1 ? new AccumType(indexToValue[*indices.begin()]) : new AccumType( ( indexToValue[*indices.begin()] + indexToValue[*indices.rbegin()] )/AccumType(2) ) ); this->_setMedianAbsDevMedian(medAbsDevMed); } return *medAbsDevMed; } CASA_STATD AccumType ClassicalQuantileComputer::getMedianAndQuantiles( std::map& quantiles, const std::set& fractions, uInt64 mynpts, AccumType mymin, AccumType mymax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt nBins ) { std::set medianIndices; quantiles.clear(); auto median = this->_getMedian(); if (! median) { medianIndices = _medianIndices(mynpts); } auto quantileToIndex = StatisticsData::indicesFromFractions( mynpts, fractions ); auto indices = medianIndices; for_each( quantileToIndex.cbegin(), quantileToIndex.cend(), [&indices](const std::pair& mypair) { indices.insert(mypair.second); }); auto indexToValue = _indicesToValues( mynpts, mymin, mymax, binningThreshholdSizeBytes/sizeof(AccumType), indices, persistSortedArray, nBins ); if (! median) { median.reset( mynpts % 2 == 0 ? new AccumType( ( indexToValue[*medianIndices.begin()] + indexToValue[*medianIndices.rbegin()] )/AccumType(2) ) : new AccumType(indexToValue[*medianIndices.begin()]) ); this->setMedian(median); } for_each( fractions.cbegin(), fractions.cend(), [&quantiles, &indexToValue, &quantileToIndex](Double q) { quantiles[q] = indexToValue[quantileToIndex[q]]; }); return *median; } CASA_STATD std::map ClassicalQuantileComputer::getQuantiles( const std::set& fractions, uInt64 mynpts, AccumType mymin, AccumType mymax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt nBins ) { if (fractions.empty()) { return std::map(); } ThrowIf( *fractions.begin() <= 0 || *fractions.rbegin() >= 1, "Value of all quantiles must be between 0 and 1 (noninclusive)" ); auto quantileToIndex = StatisticsData::indicesFromFractions( mynpts, fractions ); // This seemingly convoluted way of doing things with maps is necessary // because multiple quantiles can map to the same sorted array index, and // multiple array indices can map the same value if the values in the array // are not unique. std::set uniqueIndices; for_each( quantileToIndex.cbegin(), quantileToIndex.cend(), [&uniqueIndices](const std::pair& mypair) { uniqueIndices.insert(mypair.second); }); auto indexToValue = _indicesToValues( mynpts, mymin, mymax, binningThreshholdSizeBytes/sizeof(AccumType), uniqueIndices, persistSortedArray, nBins ); std::map quantileToValue; for_each( quantileToIndex.cbegin(), quantileToIndex.cend(), [&quantileToValue, &indexToValue] (const std::pair& mypair) { quantileToValue[mypair.first] = indexToValue[mypair.second]; }); return quantileToValue; } CASA_STATD void ClassicalQuantileComputer::reset() { StatisticsAlgorithmQuantileComputer::reset(); _doMedAbsDevMed = False; } CASA_STATD std::vector> ClassicalQuantileComputer::_binCounts( std::vector>& sameVal, const std::vector>& hist ) { auto bDesc = hist.cbegin(); auto iDesc = bDesc; auto eDesc = hist.cend(); if (hist.size() > 1) { // initialize only to squash compiler warning auto prevDesc = *bDesc; // sanity check while (iDesc != eDesc) { if (iDesc != bDesc) { ThrowIf ( iDesc->getMinHistLimit() <= prevDesc.getMinHistLimit(), "Logic Error: histograms are not monotonically increasing" ); } prevDesc = *iDesc; ++iDesc; } } std::vector allSame(hist.size(), True); // the elements in the outer vector are histograms. The elements in the // inner vector are the bins in the corresponding histograms. The Int64 // values are the number of data points in those bins std::vector> bins(hist.size()); // initialize all bin counts to 0 iDesc = bDesc; for_each(bins.begin(), bins.end(), [&iDesc](std::vector& hist) { hist = std::vector(iDesc->getNBins(), 0); ++iDesc; }); // sameVal indicates if all values in a histogram // (the vector elements) are the same sameVal = std::vector>(hist.size(), nullptr); // maxLimit are the maximum limits for each histogram. set them here. std::vector maxLimit(hist.size()); iDesc = bDesc; for_each(maxLimit.begin(), maxLimit.end(), [&iDesc](AccumType& myMax) { myMax = iDesc->getMaxHistLimit(); ++iDesc; }); auto* ds = this->_getDataset(); ds->initIterators(); const uInt nThreadsMax = StatisticsUtilities::nThreadsMax( ds->getDataProvider() ); // The std::unique_ptr-s hold references to C arrays of length // ClassicalStatisticsData::CACHE_PADDING*nThreadsMax. // Only every CACHE_PADDING*nth element will be populated std::unique_ptr>[]> tBins( new std::vector>[ ClassicalStatisticsData::CACHE_PADDING*nThreadsMax ] ); std::unique_ptr>[]> tSameVal( new std::vector>[ ClassicalStatisticsData::CACHE_PADDING*nThreadsMax ] ); std::unique_ptr[]> tAllSame( new std::vector[ ClassicalStatisticsData::CACHE_PADDING*nThreadsMax ] ); for (uInt tid=0; tidinitLoopVars(); uInt nBlocks, nthreads; uInt64 extra; std::unique_ptr dataIter; std::unique_ptr maskIter; std::unique_ptr weightsIter; std::unique_ptr offset; ds->initThreadVars( nBlocks, extra, nthreads, dataIter, maskIter, weightsIter, offset, nThreadsMax ); #ifdef _OPENMP #pragma omp parallel for num_threads(nthreads) #endif for (uInt i=0; i::threadIdx(); uInt64 dataCount = (chunk.count - offset[idx8]) < ClassicalStatisticsData::BLOCK_SIZE ? extra : ClassicalStatisticsData::BLOCK_SIZE; _computeBins( tBins[idx8], tSameVal[idx8], tAllSame[idx8], dataIter[idx8], maskIter[idx8], weightsIter[idx8], dataCount, hist, maxLimit, chunk ); ds->incrementThreadIters( dataIter[idx8], maskIter[idx8], weightsIter[idx8], offset[idx8], nthreads ); } if (ds->increment(False)) { break; } } StatisticsUtilities::mergeResults( bins, sameVal, allSame, tBins, tSameVal, tAllSame, nThreadsMax ); return bins; } CASA_STATD void ClassicalQuantileComputer::_computeBins( std::vector& bins, std::vector>& sameVal, std::vector& allSame, DataIterator dataIter, MaskIterator maskIter, WeightsIterator weightsIter, uInt64 count, const std::vector>& hist, const std::vector& maxLimit, const typename StatisticsDataset::ChunkData& chunk ) { if (chunk.weights) { if (chunk.mask) { if (chunk.ranges) { _findBins( bins, sameVal, allSame, dataIter, weightsIter, count, chunk.dataStride, maskIter, chunk.mask->second, chunk.ranges->first, chunk.ranges->second, hist, maxLimit ); } else { _findBins( bins, sameVal, allSame, dataIter, weightsIter, count, chunk.dataStride, maskIter, chunk.mask->second, hist, maxLimit ); } } else if (chunk.ranges) { _findBins( bins, sameVal, allSame, dataIter, weightsIter, count, chunk.dataStride, chunk.ranges->first, chunk.ranges->second, hist, maxLimit ); } else { // has weights, but no mask nor ranges _findBins( bins, sameVal, allSame, dataIter, weightsIter, count, chunk.dataStride, hist, maxLimit ); } } else if (chunk.mask) { // this data set has no weights, but does have a mask if (chunk.ranges) { _findBins( bins, sameVal, allSame, dataIter, count, chunk.dataStride, maskIter, chunk.mask->second, chunk.ranges->first, chunk.ranges->second, hist, maxLimit ); } else { _findBins( bins, sameVal, allSame, dataIter, count, chunk.dataStride, maskIter, chunk.mask->second, hist, maxLimit ); } } else if (chunk.ranges) { // this data set has no weights no mask, but does have a set of ranges // associated with it _findBins( bins, sameVal, allSame, dataIter, count, chunk.dataStride, chunk.ranges->first, chunk.ranges->second, hist, maxLimit ); } else { // simplest case, this data set has no weights, no mask, nor any ranges // associated with it. No filtering of the data is necessary. _findBins( bins, sameVal, allSame, dataIter, count, chunk.dataStride, hist, maxLimit ); } } CASA_STATD void ClassicalQuantileComputer::_createDataArray(DataArray& ary) { auto* ds = this->_getDataset(); ds->initIterators(); const auto nThreadsMax = StatisticsUtilities::nThreadsMax( ds->getDataProvider() ); std::unique_ptr[]> tAry( new std::vector[ ClassicalStatisticsData::CACHE_PADDING*nThreadsMax ] ); while (True) { const auto& chunk = ds->initLoopVars(); uInt nBlocks, nthreads; uInt64 extra; std::unique_ptr dataIter; std::unique_ptr maskIter; std::unique_ptr weightsIter; std::unique_ptr offset; ds->initThreadVars( nBlocks, extra, nthreads, dataIter, maskIter, weightsIter, offset, nThreadsMax ); #ifdef _OPENMP #pragma omp parallel for num_threads(nthreads) #endif for (uInt i=0; i::threadIdx(); uInt64 dataCount = (chunk.count - offset[idx8]) < ClassicalStatisticsData::BLOCK_SIZE ? extra : ClassicalStatisticsData::BLOCK_SIZE; _computeDataArray( tAry[idx8], dataIter[idx8], maskIter[idx8], weightsIter[idx8], dataCount, chunk ); ds->incrementThreadIters( dataIter[idx8], maskIter[idx8], weightsIter[idx8], offset[idx8], nthreads ); } if (ds->increment(False)) { break; } } // merge the per-thread arrays for (uInt tid=0; tid::_computeDataArray( DataArray& ary, DataIterator dataIter, MaskIterator maskIter, WeightsIterator weightsIter, uInt64 dataCount, const typename StatisticsDataset::ChunkData& chunk ) { if (chunk.weights) { if (chunk.mask) { if (chunk.ranges) { _populateArray( ary, dataIter, weightsIter, dataCount, chunk.dataStride, maskIter, chunk.mask->second, chunk.ranges->first, chunk.ranges->second ); } else { _populateArray( ary, dataIter, weightsIter, dataCount, chunk.dataStride, maskIter, chunk.mask->second ); } } else if (chunk.ranges) { _populateArray( ary, dataIter, weightsIter, dataCount, chunk.dataStride, chunk.ranges->first, chunk.ranges->second ); } else { // has weights, but no mask nor ranges _populateArray( ary, dataIter, weightsIter, dataCount, chunk.dataStride ); } } else if (chunk.mask) { // this data set has no weights, but does have a mask if (chunk.ranges) { _populateArray( ary, dataIter, dataCount, chunk.dataStride, maskIter, chunk.mask->second, chunk.ranges->first, chunk.ranges->second ); } else { _populateArray( ary, dataIter, dataCount, chunk.dataStride, maskIter, chunk.mask->second ); } } else if (chunk.ranges) { // this data set has no weights no mask, but does have a set of ranges // associated with it _populateArray( ary, dataIter, dataCount, chunk.dataStride, chunk.ranges->first, chunk.ranges->second ); } else { // simplest case, this data set has no weights, no mask, nor any ranges // associated with it, and its stride is 1. No filtering of the data is // necessary. _populateArray( ary, dataIter, dataCount, chunk.dataStride ); } } CASA_STATD void ClassicalQuantileComputer::_computeDataArrays( std::vector& arys, uInt64& currentCount, DataIterator dataIter, MaskIterator maskIter, WeightsIterator weightsIter, uInt64 dataCount, const IncludeLimits& includeLimits, uInt64 maxCount, const typename StatisticsDataset::ChunkData& chunk ) { if (chunk.weights) { if (chunk.mask) { if (chunk.ranges) { _populateArrays( arys, currentCount, dataIter, weightsIter, dataCount, chunk.dataStride, maskIter, chunk.mask->second, chunk.ranges->first, chunk.ranges->second, includeLimits, maxCount ); } else { _populateArrays( arys, currentCount, dataIter, weightsIter, dataCount, chunk.dataStride, maskIter, chunk.mask->second, includeLimits, maxCount ); } } else if (chunk.ranges) { _populateArrays( arys, currentCount, dataIter, weightsIter, dataCount, chunk.dataStride, chunk.ranges->first, chunk.ranges->second, includeLimits, maxCount ); } else { // has weights, but no mask nor ranges _populateArrays( arys, currentCount, dataIter, weightsIter, dataCount, chunk.dataStride, includeLimits, maxCount ); } } else if (chunk.mask) { // this data set has no weights, but does have a mask if (chunk.ranges) { _populateArrays( arys, currentCount, dataIter, dataCount, chunk.dataStride, maskIter, chunk.mask->second, chunk.ranges->first, chunk.ranges->second, includeLimits, maxCount ); } else { _populateArrays( arys, currentCount, dataIter, dataCount, chunk.dataStride, maskIter, chunk.mask->second, includeLimits, maxCount ); } } else if (chunk.ranges) { // this data set has no weights no mask, but does have a set of ranges // associated with it _populateArrays( arys, currentCount, dataIter, dataCount, chunk.dataStride, chunk.ranges->first, chunk.ranges->second, includeLimits, maxCount ); } else { // simplest case, this data set has no weights, no mask, nor any ranges // associated with it, and its stride is 1. No filtering of the data is // necessary. _populateArrays( arys, currentCount, dataIter, dataCount, chunk.dataStride, includeLimits, maxCount ); } } CASA_STATD void ClassicalQuantileComputer::_createDataArrays( std::vector& arys, const IncludeLimits& includeLimits, uInt64 maxCount ) { std::pair prevLimits; auto first = True; for_each( includeLimits.cbegin(), includeLimits.cend(), [&first, &prevLimits] (const std::pair& limitPair) { if (limitPair.first >= limitPair.second) { ostringstream os; os << "Logic Error: bin limits are nonsensical: " << limitPair; ThrowCc(os.str()); } if (first) { first = False; } else if ( limitPair.first <= prevLimits.first || limitPair.second <= prevLimits.second ) { ostringstream os; os << "Logic Error: bin limits are not in order: " << prevLimits << " , " << limitPair; ThrowCc(os.str()); } prevLimits = limitPair; }); auto* ds = this->_getDataset(); ds->initIterators(); const uInt nThreadsMax = StatisticsUtilities::nThreadsMax( ds->getDataProvider() ); std::unique_ptr>[]> tArys( new std::vector>[ ClassicalStatisticsData::CACHE_PADDING*nThreadsMax ] ); std::unique_ptr tCurrentCount( new uInt64[ ClassicalStatisticsData::CACHE_PADDING*nThreadsMax ] ); for (uInt tid=0; tidinitLoopVars(); uInt nBlocks, nthreads; uInt64 extra; std::unique_ptr dataIter; std::unique_ptr maskIter; std::unique_ptr weightsIter; std::unique_ptr offset; ds->initThreadVars( nBlocks, extra, nthreads, dataIter, maskIter, weightsIter, offset, nThreadsMax ); for (uInt tid=0; tid::threadIdx(); uInt64 dataCount = (chunk.count - offset[idx8]) < ClassicalStatisticsData::BLOCK_SIZE ? extra : ClassicalStatisticsData::BLOCK_SIZE; _computeDataArrays( tArys[idx8], tCurrentCount[idx8], dataIter[idx8], maskIter[idx8], weightsIter[idx8], dataCount, includeLimits, maxCount, chunk ); ds->incrementThreadIters( dataIter[idx8], maskIter[idx8], weightsIter[idx8], offset[idx8], nthreads ); } // currentCount could be updated inside the threaded loop for finer // granularity, but that would require a critical block which // might negatively affect performance. Doing it after the main // loop seems a reasonable trade off between possibly short // circuiting earlier vs performance hits if that does not happen uInt64 prevCount = currentCount; for (uInt tid=0; tidincrement(False)) { break; } } // The accounting issue seems to have been fixed in // CAS-11504, but leave check just in case ThrowIf(currentCount != maxCount, "Accounting error"); // merge the per-thread arrays for (uInt tid=0; tid& ary) { ary.insert(ary.end(), titer->cbegin(), titer->cend()); ++titer; }); } } CASA_STATD std::vector> ClassicalQuantileComputer::_dataFromMultipleBins( const std::vector>& hist, uInt64 maxArraySize, const std::vector& dataIndices, uInt nBins ) { // dataIndices are relative to minimum bin minimum border std::vector> sameVal(hist.size(), nullptr); auto binCounts = _binCounts(sameVal, hist); auto iSameVal = sameVal.cbegin(); auto bCountSet = binCounts.cbegin(); auto iCountSet = bCountSet; auto iDesc = hist.cbegin();; std::map histToIdxValMap; std::vector vnpts; std::vector vlimits; std::vector vindices; std::vector> vNewToOld; // This is necessary for accounting. Map the lower limit of // a single bin to the lower limit of its associated histogram std::map binToHistogramMap; // loop over sets of data indices for_each( dataIndices.cbegin(), dataIndices.cend(), [ &iSameVal, &iDesc, &iCountSet, &vNewToOld, &vnpts, &vlimits, &binToHistogramMap, &vindices, &histToIdxValMap ](const IndexSet& idxSet) { auto iIdx = idxSet.cbegin(); auto eIdx = idxSet.cend(); const auto& maxBinLims = iDesc->getMaxBinLimits(); if (! *iSameVal) { // values in this histogram are not all the same auto iCounts = iCountSet->cbegin(); auto eCounts = iCountSet->cend(); uInt64 dataCount = 0; uInt64 prevDataCount = 0; uInt64 loopCount = 0; // loop over data indices pertaining to a single histogram // this cannot be made into a for_each loop, because iIdx can be // incremented multiple times inside the loop while (iIdx != eIdx) { ThrowIf( iCounts == eCounts, "Logic Error: ran out of bins, accounting error" ); dataCount += *iCounts; if (*iIdx < dataCount) { // datum at index exists in current bin LimitPair histLimits; histLimits.first = loopCount == 0 ? iDesc->getMinHistLimit() : maxBinLims[loopCount - 1]; histLimits.second = maxBinLims[loopCount]; IndexSet newDataIndices; std::map newToOld; while(iIdx != eIdx && *iIdx < dataCount) { // this loop takes into account that multiple // indices could fall in the same bin uInt64 oldIdx = *iIdx; uInt64 newIdx = oldIdx - prevDataCount; newDataIndices.insert(newIdx); newToOld[newIdx] = oldIdx; ++iIdx; } vNewToOld.push_back(newToOld); vnpts.push_back(*iCounts); vlimits.push_back(histLimits); // because multiple single bins can be in the same // histogram, we need to keep track of which bins belong // to which histogram for accounting below binToHistogramMap[histLimits.first] = iDesc->getMinHistLimit(); vindices.push_back(newDataIndices); } prevDataCount = dataCount; ++iCounts; ++loopCount; } } else { // values in this histogram are all the same IndexValueMap mymap; for_each( idxSet.cbegin(), idxSet.cend(), [&mymap, &iSameVal] (uInt64 index) { mymap[index] = *(*iSameVal); }); histToIdxValMap[iDesc->getMinHistLimit()] = mymap; } ++iSameVal; ++iCountSet; ++iDesc; }); if (! vnpts.empty()) { auto dataFromBins = _dataFromSingleBins( vnpts, maxArraySize, vlimits, vindices, nBins ); auto iNewToOld = vNewToOld.cbegin(); auto iVLimits = vlimits.cbegin(); for_each( dataFromBins.cbegin(), dataFromBins.cend(), [&iVLimits, &binToHistogramMap, &iNewToOld, &histToIdxValMap] (const IndexValueMap& idxValMap) { auto myHistKey = binToHistogramMap[iVLimits->first]; IndexValueMap mymap; for_each( idxValMap.cbegin(), idxValMap.cend(), [&iNewToOld, &mymap] (const std::pair& mypair) { auto newIdx = mypair.first; auto oldIdx = iNewToOld->find(newIdx)->second; mymap[oldIdx] = mypair.second; }); histToIdxValMap[myHistKey].insert(mymap.begin(), mymap.end()); ++iNewToOld; ++iVLimits; }); } std::vector ret; for_each( hist.cbegin(), hist.cend(), [&ret, &histToIdxValMap] (const StatsHistogram& myhist) { ret.push_back(histToIdxValMap[myhist.getMinHistLimit()]); }); return ret; } CASA_STATD std::vector> ClassicalQuantileComputer::_dataFromSingleBins( const BinCountArray& binNpts, uInt64 maxArraySize, const std::vector& binLimits, const std::vector& dataIndices, uInt nBins ) { // The uInt64 specification is required or else 0 will be interpreted as a // uInt and there will be overflow issues for totalNpts > (2**32)-1 auto totalPts = std::accumulate(binNpts.begin(), binNpts.end(), uInt64(0)); if (totalPts <= maxArraySize) { // contents of bin is small enough to be sorted in memory, so // get the bin limits and stuff the good points within those limits // in an array and sort it std::vector dataArrays(binLimits.size(), DataArray(0)); _createDataArrays(dataArrays, binLimits, totalPts); auto iNpts = binNpts.cbegin(); for_each( dataArrays.cbegin(), dataArrays.cend(), [&iNpts](const DataArray& ary) { ThrowIf( ary.size() != *iNpts, "Logic Error: data array has " + String::toString(ary.size()) + " elements but it should have " + String::toString(*iNpts) + ". Please file a bug report and include your dataset and " "your inputs" ); ++iNpts; }); iNpts = binNpts.begin(); std::vector ivMaps(binLimits.size()); typename std::vector::iterator iIVMaps = ivMaps.begin(); auto iArrays = dataArrays.begin(); for_each ( dataIndices.cbegin(), dataIndices.cend(), [&iIVMaps, &iNpts, &iArrays](const IndexSet& idxSet) { uInt64 prevIdx = 0; for_each( idxSet.cbegin(), idxSet.cend(), [&iNpts, &iIVMaps, &iArrays, &prevIdx](uInt64 idx) { ThrowIf( idx >= *iNpts, "Logic Error: aryIdx " + String::toString(idx) + " is too " "large. It should be no larger than " + String::toString(*iNpts-1) + ". Please file a defect " + "report and include your dataset and your inputs" ); (*iIVMaps)[idx] = GenSort::kthLargest( &((*iArrays)[prevIdx]), *iNpts - prevIdx, idx - prevIdx ); prevIdx = idx; }); ++iNpts; ++iArrays; ++iIVMaps; }); return ivMaps; } else { // number of points is too large to fit in an array to be sorted, so // rebin those points into smaller bins // we want at least 1000 bins nBins = max(nBins, (uInt)1000); std::vector> hist; for_each( binLimits.cbegin(), binLimits.cend(), [&hist, &nBins](const LimitPair& myLimits) { StatsHistogram histogram( myLimits.first, myLimits.second, nBins ); hist.push_back(histogram); }); try { return _dataFromMultipleBins( hist, maxArraySize, dataIndices, nBins ); } catch (const AipsError& x) { ThrowCc("Binning accounting error"); } } } CASA_STATD std::map ClassicalQuantileComputer::_indicesToValues( uInt64 mynpts, AccumType mymin, AccumType mymax, uInt64 maxArraySize, const IndexSet& indices, Bool persistSortedArray, uInt nBins ) { IndexValueMap indexToValue; if ( _valuesFromSortedArray( indexToValue, mynpts, indices, maxArraySize, persistSortedArray ) ) { return indexToValue; } if (_doMedAbsDevMed) { auto median = this->_getMedian(); ThrowIf(! median, "median is null"); mymax = max(abs(mymax - *median), abs(mymin - *median)); mymin = AccumType(0); } if (mymax == mymin) { // data set values are all the same for_each( indices.cbegin(), indices.cend(), [&indexToValue, mymin](uInt64 idx) { indexToValue[idx] = mymin; }); return indexToValue; } std::vector vindices(1, indices); // Avoiding having exceptions thrown over a wide range of use cases is // surprisingly dependent on the padding factor. 1e-2 seems a reasonable // setting to prevent this. It probably should not be set lower than this, // unless the factor is made dependent on the use case parameters eg, // the mymax - mymin difference. AccumType pad = 1e-2*(mymax - mymin); LimitPair limits(mymin - pad, mymax + pad); std::vector vlimits(1, limits); BinCountArray vmynpts(1, mynpts); return _dataFromSingleBins( vmynpts, maxArraySize, vlimits, vindices, nBins )[0]; } CASA_STATD std::set ClassicalQuantileComputer::_medianIndices( uInt64 mynpts ) { IndexSet indices; if (mynpts % 2 == 0) { indices.insert(mynpts/2 - 1); indices.insert(mynpts/2); } else { indices.insert(mynpts/2); } return indices; } // Tried making this into an inline method, but performance decreased by 20 - 25% when // finding the median and quartiles on a 200 Mpix image. So the #define seems to be // the better choice from a performance standpoint. #define _findBinCode \ AccumType myDatum = _doMedAbsDevMed ? abs((AccumType)*datum - _myMedian) : *datum; \ if (myDatum >= bhist->getMinHistLimit() && myDatum < *maxLimit.rbegin()) { \ /* datum may fall in one of the histograms */ \ iCounts = bCounts; \ iSameVal = bSameVal; \ iAllSame = bAllSame; \ ihist = bhist; \ iMaxLimit = bMaxLimit; \ /* loop over histograms */ \ for (; ihist != ehist; ++ihist, ++iCounts, ++iSameVal, ++iAllSame, ++iMaxLimit) { \ if (myDatum >= ihist->getMinHistLimit() && myDatum < *iMaxLimit) { \ /* datum falls within the current histogram */ \ auto idx = ihist->getIndex(myDatum); \ ++(*iCounts)[idx]; \ if (*iAllSame) { \ if (!*iSameVal) { \ iSameVal->reset (new AccumType(myDatum)); \ } \ else { \ *iAllSame = myDatum == *(*iSameVal); \ if (! *iAllSame) { \ *iSameVal = nullptr; \ } \ } \ } \ /* datum accounted for, so break */ \ break; \ } \ } \ } CASA_STATD void ClassicalQuantileComputer::_findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const std::vector>& hist, const std::vector& maxLimit ) const { auto bCounts = binCounts.begin(); auto iCounts = bCounts; auto bSameVal = sameVal.begin(); auto iSameVal = bSameVal; auto bAllSame = allSame.begin(); auto iAllSame = bAllSame; auto bhist = hist.cbegin(); auto ihist = bhist; auto ehist = hist.cend(); auto bMaxLimit = maxLimit.cbegin(); auto iMaxLimit = bMaxLimit; auto datum = dataBegin; uInt64 count = 0; while (count < nr) { _findBinCode StatisticsIncrementer::increment(datum, count, dataStride); } } CASA_STATD void ClassicalQuantileComputer::_findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const std::vector>& hist, const std::vector& maxLimit ) const { auto bCounts = binCounts.begin(); auto iCounts = bCounts; auto bSameVal = sameVal.begin(); auto iSameVal = bSameVal; auto bAllSame = allSame.begin(); auto iAllSame = bAllSame; auto bhist = hist.cbegin(); auto ihist = bhist; auto ehist = hist.cend(); auto bMaxLimit = maxLimit.cbegin(); auto iMaxLimit = bMaxLimit; auto datum = dataBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _findBinCode } StatisticsIncrementer::increment(datum, count, dataStride); } } CASA_STATD void ClassicalQuantileComputer::_findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const std::vector>& hist, const std::vector& maxLimit ) const { auto bCounts = binCounts.begin(); auto iCounts = bCounts; auto bSameVal = sameVal.begin(); auto iSameVal = bSameVal; auto bAllSame = allSame.begin(); auto iAllSame = bAllSame; auto bhist = hist.cbegin(); auto ihist = bhist; auto ehist = hist.cend(); auto bMaxLimit = maxLimit.cbegin(); auto iMaxLimit = bMaxLimit; auto datum = dataBegin; auto mask = maskBegin; uInt64 count = 0; while (count < nr) { if (*mask) { _findBinCode } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalQuantileComputer::_findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const std::vector>& hist, const std::vector& maxLimit ) const { auto bCounts = binCounts.begin(); auto iCounts = bCounts; auto bSameVal = sameVal.begin(); auto iSameVal = bSameVal; auto bAllSame = allSame.begin(); auto iAllSame = bAllSame; auto bhist = hist.cbegin(); auto ihist = bhist; auto ehist = hist.cend(); auto bMaxLimit = maxLimit.cbegin(); auto iMaxLimit = bMaxLimit; auto datum = dataBegin; auto mask = maskBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *mask && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _findBinCode } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalQuantileComputer::_findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const std::vector>& hist, const std::vector& maxLimit ) const { auto bCounts = binCounts.begin(); auto iCounts = bCounts; auto bSameVal = sameVal.begin(); auto iSameVal = bSameVal; auto bAllSame = allSame.begin(); auto iAllSame = bAllSame; auto bhist = hist.cbegin(); auto ihist = bhist; auto ehist = hist.cend(); auto bMaxLimit = maxLimit.cbegin(); auto iMaxLimit = bMaxLimit; auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; while (count < nr) { if (*weight > 0) { _findBinCode } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ClassicalQuantileComputer::_findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const std::vector>& hist, const std::vector& maxLimit ) const { auto bCounts = binCounts.begin(); auto iCounts = bCounts; auto bSameVal = sameVal.begin(); auto iSameVal = bSameVal; auto bAllSame = allSame.begin(); auto iAllSame = bAllSame; auto bhist = hist.cbegin(); auto ihist = bhist; auto ehist = hist.cend(); auto bMaxLimit = maxLimit.cbegin(); auto iMaxLimit = bMaxLimit; auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _findBinCode } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ClassicalQuantileComputer::_findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const std::vector>& hist, const std::vector& maxLimit ) const { auto bCounts = binCounts.begin(); auto iCounts = bCounts; auto bSameVal = sameVal.begin(); auto iSameVal = bSameVal; auto bAllSame = allSame.begin(); auto iAllSame = bAllSame; auto bhist = hist.cbegin(); auto ihist = bhist; auto ehist = hist.cend(); auto bMaxLimit = maxLimit.cbegin(); auto iMaxLimit = bMaxLimit; auto datum = dataBegin; auto weight = weightsBegin; auto mask = maskBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *mask && *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _findBinCode } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalQuantileComputer::_findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const std::vector>& hist, const std::vector& maxLimit ) const { auto bCounts = binCounts.begin(); auto iCounts = bCounts; auto bSameVal = sameVal.begin(); auto iSameVal = bSameVal; auto bAllSame = allSame.begin(); auto iAllSame = bAllSame; auto bhist = hist.cbegin(); auto ihist = bhist; auto ehist = hist.cend(); auto bMaxLimit = maxLimit.cbegin(); auto iMaxLimit = bMaxLimit; auto datum = dataBegin; auto weight = weightBegin; auto mask = maskBegin; uInt64 count = 0; while (count < nr) { if (*mask && *weight > 0) { _findBinCode } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } // define rather than make a method to ensure this is // called inline to maximize performance #define _populateArrayCode1 \ AccumType myDatum = _doMedAbsDevMed \ ? abs((AccumType)*datum - _myMedian) : *datum; \ ary.push_back(myDatum); CASA_STATD void ClassicalQuantileComputer::_populateArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ) const { uInt64 count = 0; auto datum = dataBegin; while (count < nr) { _populateArrayCode1 StatisticsIncrementer::increment(datum, count, dataStride); } } CASA_STATD void ClassicalQuantileComputer::_populateArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { uInt64 count = 0; auto datum = dataBegin; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _populateArrayCode1 } StatisticsIncrementer::increment(datum, count, dataStride); } } CASA_STATD void ClassicalQuantileComputer::_populateArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { uInt64 count = 0; auto datum = dataBegin; auto mask = maskBegin; while (count < nr) { if (*mask) { _populateArrayCode1 } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalQuantileComputer::_populateArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { uInt64 count = 0; auto datum = dataBegin; auto mask = maskBegin; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *mask && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _populateArrayCode1 } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalQuantileComputer::_populateArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) const { auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; while (count < nr) { if (*weight > 0) { _populateArrayCode1 } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ClassicalQuantileComputer::_populateArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _populateArrayCode1 } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ClassicalQuantileComputer::_populateArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { auto datum = dataBegin; auto weight = weightBegin; auto mask = maskBegin; uInt64 count = 0; while (count < nr) { if (*mask && *weight > 0) { _populateArrayCode1 } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalQuantileComputer::_populateArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { auto datum = dataBegin; auto weight = weightBegin; auto mask = maskBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *mask && *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _populateArrayCode1 } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } // define rather than make a method to ensure this is called inline to maximize // performance. We make use of the fact that bins are in ascending order, so if // datum is less than current bin minimum value, it will not be in any remaining // bins and so we can break out of the loop without having to test each bin. #define _populateArraysCode \ AccumType myDatum = _doMedAbsDevMed \ ? abs((AccumType)*datum - _myMedian) : *datum; \ if ( \ myDatum >= includeLimits.begin()->first \ && myDatum < includeLimits.rbegin()->second \ ) { \ iIncludeLimits = bIncludeLimits; \ iArys = bArys; \ while (iIncludeLimits != eIncludeLimits) { \ if (myDatum < iIncludeLimits->first) { \ break; \ } \ if (myDatum < iIncludeLimits->second) { \ iArys->push_back(myDatum); \ ++currentCount; \ if (currentCount == maxCount) { \ return; \ } \ break; \ } \ ++iIncludeLimits; \ ++iArys; \ } \ } CASA_STATD void ClassicalQuantileComputer::_populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const IncludeLimits &includeLimits, uInt64 maxCount ) const { auto bArys = arys.begin(); auto iArys = bArys; auto bIncludeLimits = includeLimits.cbegin(); auto iIncludeLimits = bIncludeLimits; auto eIncludeLimits = includeLimits.cend(); uInt64 count = 0; auto datum = dataBegin; while (count < nr) { _populateArraysCode StatisticsIncrementer::increment(datum, count, dataStride); } } CASA_STATD void ClassicalQuantileComputer::_populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const IncludeLimits& includeLimits, uInt64 maxCount ) const { auto bArys = arys.begin(); auto iArys = bArys; auto bIncludeLimits = includeLimits.cbegin(); auto iIncludeLimits = bIncludeLimits; auto eIncludeLimits = includeLimits.cend(); uInt64 count = 0; auto datum = dataBegin; auto beginRange = ranges.begin(); auto endRange = ranges.end(); while (count < nr) { if ( StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _populateArraysCode } StatisticsIncrementer::increment(datum, count, dataStride); } } CASA_STATD void ClassicalQuantileComputer::_populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const IncludeLimits& includeLimits, uInt64 maxCount ) const { auto bArys = arys.begin(); auto iArys = bArys; auto bIncludeLimits = includeLimits.cbegin(); auto iIncludeLimits = bIncludeLimits; auto eIncludeLimits = includeLimits.cend(); uInt64 count = 0; auto datum = dataBegin; auto mask = maskBegin; while (count < nr) { if (*mask) { _populateArraysCode } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalQuantileComputer::_populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const IncludeLimits& includeLimits, uInt64 maxCount ) const { auto bArys = arys.begin(); auto iArys = bArys; auto bIncludeLimits = includeLimits.cbegin(); auto iIncludeLimits = bIncludeLimits; auto eIncludeLimits = includeLimits.cend(); uInt64 count = 0; auto datum = dataBegin; auto mask = maskBegin; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *mask && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _populateArraysCode } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalQuantileComputer::_populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const IncludeLimits& includeLimits, uInt64 maxCount ) const { auto bArys = arys.begin(); auto iArys = bArys; auto bIncludeLimits = includeLimits.cbegin(); auto iIncludeLimits = bIncludeLimits; auto eIncludeLimits = includeLimits.cend(); auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; while (count < nr) { if (*weight > 0) { _populateArraysCode } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ClassicalQuantileComputer::_populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const IncludeLimits& includeLimits, uInt64 maxCount ) const { auto bArys = arys.begin(); auto iArys = bArys; auto bIncludeLimits = includeLimits.cbegin(); auto iIncludeLimits = bIncludeLimits; auto eIncludeLimits = includeLimits.cend(); auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _populateArraysCode } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ClassicalQuantileComputer::_populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const IncludeLimits& includeLimits, uInt64 maxCount ) const { auto bArys = arys.begin(); auto iArys = bArys; auto bIncludeLimits = includeLimits.cbegin(); auto iIncludeLimits = bIncludeLimits; auto eIncludeLimits = includeLimits.cend(); auto datum = dataBegin; auto weight = weightBegin; auto mask = maskBegin; uInt64 count = 0; while (count < nr) { if (*mask && *weight > 0) { _populateArraysCode } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalQuantileComputer::_populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const IncludeLimits& includeLimits, uInt64 maxCount ) const { auto bArys = arys.begin(); auto iArys = bArys; auto bIncludeLimits = includeLimits.cbegin(); auto iIncludeLimits = bIncludeLimits; auto eIncludeLimits = includeLimits.cend(); auto datum = dataBegin; auto weight = weightBegin; auto mask = maskBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *mask && *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _populateArraysCode } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } CASA_STATD Bool ClassicalQuantileComputer::_populateTestArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, uInt maxElements ) const { if (ary.size() + nr > maxElements) { return True; } uInt64 count = 0; auto datum = dataBegin; while (count < nr) { ary.push_back( _doMedAbsDevMed ? abs((AccumType)*datum - _myMedian) : *datum ); StatisticsIncrementer::increment( datum, count, dataStride ); } return False; } // define rather than make a method to ensure this is called inline to maximize // performance #define _PopulateTestArrayCode \ ary.push_back( \ _doMedAbsDevMed ? abs((AccumType)*datum - _myMedian) : *datum \ ); \ ++npts; \ if (npts > maxElements) { \ return True; \ } CASA_STATD Bool ClassicalQuantileComputer::_populateTestArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const { uInt64 count = 0; auto npts = ary.size(); auto datum = dataBegin; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _PopulateTestArrayCode } StatisticsIncrementer::increment(datum, count, dataStride); } return False; } CASA_STATD Bool ClassicalQuantileComputer::_populateTestArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, uInt maxElements ) const { uInt64 count = 0; auto datum = dataBegin; auto mask = maskBegin; auto npts = ary.size(); while (count < nr) { if (*mask) { _PopulateTestArrayCode } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } return False; } CASA_STATD Bool ClassicalQuantileComputer::_populateTestArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const { uInt64 count = 0; auto datum = dataBegin; auto mask = maskBegin; auto npts = ary.size(); auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *mask && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _PopulateTestArrayCode } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } return False; } CASA_STATD Bool ClassicalQuantileComputer::_populateTestArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, uInt maxElements ) const { auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; auto npts = ary.size(); while (count < nr) { if (*weight > 0) { _PopulateTestArrayCode } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } return False; } CASA_STATD Bool ClassicalQuantileComputer::_populateTestArray( std::vector& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const { auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); uInt npts = ary.size(); while (count < nr) { if ( *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _PopulateTestArrayCode } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } return False; } CASA_STATD Bool ClassicalQuantileComputer::_populateTestArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, uInt maxElements ) const { auto datum = dataBegin; auto weight = weightBegin; auto mask = maskBegin; uInt64 count = 0; auto npts = ary.size(); while (count < nr) { if (*mask && *weight > 0) { _PopulateTestArrayCode } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } return False; } CASA_STATD Bool ClassicalQuantileComputer::_populateTestArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const { auto datum = dataBegin; auto weight = weightBegin; auto mask = maskBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); uInt npts = ary.size(); while (count < nr) { if ( *mask && *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _PopulateTestArrayCode } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } return False; } CASA_STATD Bool ClassicalQuantileComputer::_valuesFromSortedArray( IndexValueMap& values, uInt64 mynpts, const std::set& indices, uInt64 maxArraySize, Bool persistSortedArray ) { values.clear(); // I need a little wiggle room, the caller can't make the maximum array size // ridiculously small maxArraySize = max(maxArraySize, (uInt64)1000); DataArray myArray; if (_doMedAbsDevMed && ! this->_getSortedArray().empty()) { // make a copy auto pSorted = this->_getSortedArray(); myArray = pSorted; StatisticsUtilities::convertToAbsDevMedArray( myArray, *this->_getMedian() ); } if (! _doMedAbsDevMed) { myArray = this->_getSortedArray(); } if (myArray.empty()) { // object that contains this object is always responsible for passing // correct value of npts, so npts should be set properly here and should // be greater than zero if (mynpts <= maxArraySize) { // npts is smaller than the max array size, so create the array and // sort it in memory _createDataArray(myArray); } else { // data is too large to be sorted in memory return False; } } values = StatisticsUtilities::indicesToValues(myArray, indices); if (! _doMedAbsDevMed) { this->_setSortedArray(persistSortedArray ? myArray : DataArray()); } return True; } } #endif casacore-3.7.1/scimath/StatsFramework/ClassicalStatistics.h000066400000000000000000000537351476623553700240670ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_CLASSICALSTATISTICSS_H #define SCIMATH_CLASSICALSTATISTICSS_H #include #include #include #include #include #include #include #include namespace casacore { // Class to calculate statistics in a "classical" sense, ie using accumulators // with no special filtering beyond optional range filtering etc. // // setCalculateAsAdded() allows one to specify if statistics should be // calculated and updated on upon each call to set/addData(). If False, // statistics will be calculated only when getStatistic(), getStatistics(), or // similar statistics computing methods are called. Setting this value to True // allows the caller to not have to keep all the data accessible at once. Note // however, that all data must be simultaneously accessible if quantile-like // (eg median) calculations are desired. // // Objects of this class are instantiated using a ClassicalQuantileComputer // object for computation of quantile-like statistics. See the documentation // of StatisticsAlgorithm for details relating QuantileComputer classes. template < class AccumType, class DataIterator, class MaskIterator=const Bool*, class WeightsIterator=DataIterator > class ClassicalStatistics : public StatisticsAlgorithm { using ChunkType = typename StatisticsDataset::ChunkData; public: ClassicalStatistics(); // copy semantics ClassicalStatistics(const ClassicalStatistics& cs); virtual ~ClassicalStatistics(); // copy semantics ClassicalStatistics& operator=(const ClassicalStatistics& other); // Clone this instance virtual StatisticsAlgorithm* clone() const; // get the algorithm that this object uses for computing stats virtual StatisticsData::ALGORITHM algorithm() const { return StatisticsData::CLASSICAL; }; // // In the following group of methods, if the size of the composite dataset // is smaller than binningThreshholdSizeBytes, the composite // dataset will be (perhaps partially) sorted and persisted in memory during // the call. In that case, and if persistSortedArray is True, // this sorted array will remain in memory after the call and will be used // on subsequent calls of this method when // binningThreshholdSizeBytes is greater than the size of the // composite dataset. If persistSortedArray is False, the sorted // array will not be stored after this call completes and so any subsequent // calls for which the dataset size is less than // binningThreshholdSizeBytes, the dataset will be sorted from // scratch. Values which are not included due to non-unity strides, are not // included in any specified ranges, are masked, or have associated weights // of zero are not considered as dataset members for quantile computations. // If one has a priori information regarding the number of points (npts) // and/or the minimum and maximum values of the data set, these can be // supplied to improve performance. Note however, that if these values are // not correct, the resulting median and/or quantile values will also not be // correct (although see the following notes regarding max/min). Note that // if this object has already had getStatistics() called, and the min and // max were calculated, there is no need to pass these values in as they // have been stored internally and used (although passing them in shouldn't // hurt anything). If provided, npts, the number of points falling in the // specified ranges which are not masked and have weights > 0, should be // exactly correct. min can be less than the true minimum, and // max can be greater than the True maximum, but for best // performance, these should be as close to the actual min and max as // possible. In order for quantile computations to occur over multiple // datasets, all datasets must be available. This means that if // setCalculateAsAdded() was previously called by passing in a value of // True, these methods will throw an exception as the previous call // indicates that there is no guarantee that all datasets will be available. // If one uses a data provider (by having called setDataProvider()), then // this should not be an issue. // Get the median of the distribution. For a dataset with an odd number of // good points, the median is just the value at index int(N/2) in the // equivalent sorted dataset, where N is the number of points. For a dataset // with an even number of points, the median is the mean of the values at // indices int(N/2)-1 and int(N/2) in the sorted dataset. nBins // is the number of bins, per histogram, to use to bin the data. More // bins decrease the likelihood that multiple passes of the data set will be // necessary, but also increase the amount of memory used. If nBins is set // to less than 1,000, it is automatically increased to 1,000; there should // be no reason to ever set nBins to be this small. virtual AccumType getMedian( std::shared_ptr knownNpts=nullptr, std::shared_ptr knownMin=nullptr, std::shared_ptr knownMax=nullptr, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt nBins=10000 ); // If one needs to compute both the median and quantile values, it is better // to call getMedianAndQuantiles() rather than getMedian() and // getQuantiles() separately, as the first will scan large data sets fewer // times than calling the separate methods. The return value is the median; // the quantiles are returned in the quantiles map. Values in the // fractions set represent the locations in the CDF and should be // between 0 and 1, exclusive. virtual AccumType getMedianAndQuantiles( std::map& quantiles, const std::set& fractions, std::shared_ptr knownNpts=nullptr, std::shared_ptr knownMin=nullptr, std::shared_ptr knownMax=nullptr, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt nBins=10000 ); // get the median of the absolute deviation about the median of the data. virtual AccumType getMedianAbsDevMed( std::shared_ptr knownNpts=nullptr, std::shared_ptr knownMin=nullptr, std::shared_ptr knownMax=nullptr, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt nBins=10000 ); // Get the specified quantiles. fractions must be between 0 and // 1, noninclusive. virtual std::map getQuantiles( const std::set& fractions, std::shared_ptr knownNpts=nullptr, std::shared_ptr knownMin=nullptr, std::shared_ptr knownMax=nullptr, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt nBins=10000 ); // // // scan the dataset(s) that have been added, and find the min and max. This // method may be called even if setStatsToCaclulate has been called and MAX // and MIN has been excluded. If setCalculateAsAdded(True) has previously // been called after this object has been (re)initialized, an exception will // be thrown. The second version also determines npts in the same scan. virtual void getMinMax(AccumType& mymin, AccumType& mymax); virtual void getMinMaxNpts( uInt64& npts, AccumType& mymin, AccumType& mymax ); // // scan the dataset(s) that have been added, and find the number of good // points. This method may be called even if setStatsToCaclulate has been // called and NPTS has been excluded. If setCalculateAsAdded(True) has // previously been called after this object has been (re)initialized, an // exception will be thrown. virtual uInt64 getNPts(); // see base class description virtual std::pair getStatisticIndex( StatisticsData::STATS stat ); // reset object to initial state. Clears all private fields including data, // accumulators, etc. virtual void reset(); // Should statistics be updated with calls to addData or should they only be // calculated upon calls to getStatistics() etc? Beware that calling this // will automatically reinitialize the object, so that it will contain no // references to data et al. after this method has been called. virtual void setCalculateAsAdded(Bool c); // An exception will be thrown if setCalculateAsAdded(True) has been called. virtual void setDataProvider(StatsDataProvider *dataProvider); // Allow derived objects to set the quantile computer object. API developers // shouldn't need to call this, unless they are writing derived classes // of ClassicalStatistics. Purposefully non-virtual. Derived classes should // not implement. void setQuantileComputer( std::shared_ptr> qc ) { _qComputer = qc; } virtual void setStatsToCalculate(std::set& stats); protected: // This constructor should be used by derived objects in order to set // the proper quantile computer object ClassicalStatistics(std::shared_ptr> qc); // // scan through the data set to determine the number of good (unmasked, // weight > 0, within range) points. The first with no mask, no ranges, and // no weights is trivial with npts = nr in this class, but is implemented // here so that derived classes may override it. virtual void _accumNpts( uInt64& npts, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ) const; virtual void _accumNpts( uInt64& npts, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _accumNpts( uInt64& npts, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; virtual void _accumNpts( uInt64& npts, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) const; virtual void _accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; // // inline void _accumulate( StatsData& stats, const AccumType& datum, const LocationType& location ); inline void _accumulate( StatsData& stats, const AccumType& datum, const AccumType& weight, const LocationType& location ); // void _addData(); void _clearStats(); Bool _getDoMaxMin() const { return _doMaxMin; } virtual StatsData _getInitialStats() const; virtual AccumType _getStatistic(StatisticsData::STATS stat); virtual StatsData _getStatistics(); // Retrieve stats structure. Allows derived classes to maintain their own // StatsData structs. virtual StatsData& _getStatsData() { return _statsData; } virtual const StatsData& _getStatsData() const { return _statsData; } // virtual void _minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ) const; virtual void _minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; virtual void _minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) const; virtual void _minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; // // // Sometimes we want the min, max, and npts all in one scan. virtual void _minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ) const; virtual void _minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; virtual void _minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) const; virtual void _minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; // std::shared_ptr> _getQuantileComputer() { return _qComputer; } // // no weights, no mask, no ranges virtual void _unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ); // no weights, no mask virtual void _unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ); virtual void _unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ); virtual void _unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ); // virtual void _updateDataProviderMaxMin( const StatsData& threadStats ); // // has weights, but no mask, no ranges virtual void _weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ); virtual void _weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ); virtual void _weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ); virtual void _weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ); // private: StatsData _statsData; Bool _calculateAsAdded{False}, _doMaxMin{True}, _mustAccumulate{False}; std::shared_ptr> _qComputer{}; void _computeMinMax( std::shared_ptr& mymax, std::shared_ptr& mymin, DataIterator dataIter, MaskIterator maskIter, WeightsIterator weightsIter, uInt64 dataCount, const ChunkType& chunk ); void _computeMinMaxNpts( uInt64& npts, std::shared_ptr& mymax, std::shared_ptr& mymin, DataIterator dataIter, MaskIterator maskIter, WeightsIterator weightsIter, uInt64 dataCount, const ChunkType& chunk ); void _computeNpts( uInt64& npts, DataIterator dataIter, MaskIterator maskIter, WeightsIterator weightsIter, uInt64 dataCount, const ChunkType& chunk ); void _computeStats( StatsData& stats, uInt64& ngood, LocationType& location, DataIterator dataIter, MaskIterator maskIter, WeightsIterator weightsIter, uInt64 count, const ChunkType& chunk ); // scan dataset(s) to find min and max void _doMinMax(AccumType& vmin, AccumType& vmax); uInt64 _doMinMaxNpts(AccumType& vmin, AccumType& vmax); uInt64 _doNpts(); // for quantile computations, if necessary, determines npts, min, max to // send to quantile calculator methods void _doNptsMinMax( uInt64& mynpts, AccumType& mymin, AccumType& mymax, std::shared_ptr knownNpts, std::shared_ptr knownMin, std::shared_ptr knownMax ); }; } #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif #endif casacore-3.7.1/scimath/StatsFramework/ClassicalStatistics.tcc000066400000000000000000001651051476623553700244040ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_CLASSICALSTATISTICS_TCC #define SCIMATH_CLASSICALSTATISTICS_TCC #include #include #include #include #include #include namespace casacore { // min > max indicates that these quantities have not be calculated CASA_STATD ClassicalStatistics::ClassicalStatistics() : StatisticsAlgorithm(), _statsData(initializeStatsData()), _qComputer( new ClassicalQuantileComputer( &this->_getDataset() ) ) { reset(); } CASA_STATD ClassicalStatistics::ClassicalStatistics( std::shared_ptr> qc ) : StatisticsAlgorithm(), _statsData(initializeStatsData()), _qComputer(qc) { reset(); } CASA_STATD ClassicalStatistics::~ClassicalStatistics() {} CASA_STATD ClassicalStatistics::ClassicalStatistics( const ClassicalStatistics& cs ) : StatisticsAlgorithm(cs), _statsData(cs._statsData), _calculateAsAdded(cs._calculateAsAdded), _doMaxMin(cs._doMaxMin), _mustAccumulate(cs._mustAccumulate), _qComputer( (ClassicalQuantileComputer*)(cs._qComputer->clone()) ) { _qComputer->setDataset(&this->_getDataset()); } CASA_STATD ClassicalStatistics& ClassicalStatistics::operator=( const ClassicalStatistics& other ) { if (this == &other) { return *this; } StatisticsAlgorithm::operator=(other); _statsData = copy(_statsData); _calculateAsAdded = other._calculateAsAdded; _doMaxMin = other._doMaxMin; _mustAccumulate = other._mustAccumulate; _qComputer.reset( (ClassicalQuantileComputer*)(other._qComputer->clone()) ); // setting the dataset in the quantile calculator must be done explicitly _qComputer->setDataset(&this->_getDataset()); return *this; } CASA_STATD StatisticsAlgorithm* ClassicalStatistics::clone() const { return new ClassicalStatistics(*this); } CASA_STATD AccumType ClassicalStatistics::getMedian( std::shared_ptr knownNpts, std::shared_ptr knownMin, std::shared_ptr knownMax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt nBins ) { if (_getStatsData().median) { return *_getStatsData().median; } uInt64 mynpts; AccumType mymin, mymax; _doNptsMinMax(mynpts, mymin, mymax, knownNpts, knownMin, knownMax); _getStatsData().median.reset (new AccumType( _qComputer->getMedian( mynpts, mymin, mymax, binningThreshholdSizeBytes, persistSortedArray, nBins ) )); return *_getStatsData().median; } CASA_STATD void ClassicalStatistics::_doNptsMinMax( uInt64& mynpts, AccumType& mymin, AccumType& mymax, std::shared_ptr knownNpts, std::shared_ptr knownMin, std::shared_ptr knownMax ) { if (knownMin && knownMax) { ThrowIf( *knownMax < *knownMin, "Provided max " + String::toString(*knownMax) + " is less than " "provided min " + String::toString(*knownMin) ); mymin = *knownMin; mymax = *knownMax; } if (knownNpts) { mynpts = *knownNpts; ThrowIf(mynpts == 0, "No valid data found"); } if ((! knownMin || ! knownMax) && ! knownNpts) { getMinMaxNpts(mynpts, mymin, mymax); } else if (! knownMin || ! knownMax) { getMinMax(mymin, mymax); } else if (! knownNpts) { mynpts = getNPts(); ThrowIf(mynpts == 0, "No valid data found"); } } CASA_STATD AccumType ClassicalStatistics::getMedianAbsDevMed( std::shared_ptr knownNpts, std::shared_ptr knownMin, std::shared_ptr knownMax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt nBins ) { if (_getStatsData().medAbsDevMed) { return *_getStatsData().medAbsDevMed; } uInt64 mynpts; AccumType mymin, mymax; _doNptsMinMax(mynpts, mymin, mymax, knownNpts, knownMin, knownMax); _getStatsData().medAbsDevMed.reset (new AccumType( _qComputer->getMedianAbsDevMed( mynpts, mymin, mymax, binningThreshholdSizeBytes, persistSortedArray, nBins ) )); return *_getStatsData().medAbsDevMed; } CASA_STATD AccumType ClassicalStatistics::getMedianAndQuantiles( std::map& quantiles, const std::set& fractions, std::shared_ptr knownNpts, std::shared_ptr knownMin, std::shared_ptr knownMax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt nBins ) { uInt64 mynpts; AccumType mymin, mymax; _doNptsMinMax(mynpts, mymin, mymax, knownNpts, knownMin, knownMax); _getStatsData().median.reset (new AccumType( _qComputer->getMedianAndQuantiles( quantiles, fractions, mynpts, mymin, mymax, binningThreshholdSizeBytes, persistSortedArray, nBins ) )); return *_getStatsData().median; } CASA_STATD void ClassicalStatistics::getMinMax( AccumType& mymin, AccumType& mymax ) { if (! _getStatsData().min || ! _getStatsData().max) { ThrowIf( _calculateAsAdded, "Min and max cannot be calculated unless all data are available " "simultaneously. To ensure that will be the case, call " "setCalculateAsAdded(False) on this object" ); _doMinMax(mymin, mymax); _getStatsData().min.reset (new AccumType(mymin)); _getStatsData().max.reset (new AccumType(mymax)); return; } mymin = *_getStatsData().min; mymax = *_getStatsData().max; } CASA_STATD void ClassicalStatistics::getMinMaxNpts( uInt64& npts, AccumType& mymin, AccumType& mymax ) { if (! _getStatsData().min || ! _getStatsData().max) { ThrowIf( _calculateAsAdded, "Min and max cannot be calculated unless all data are available " "simultaneously. To ensure that will be the case, call " "setCalculateAsAdded(False) on this object" ); if (_getStatsData().npts == 0) { ThrowIf( _calculateAsAdded, "npts cannot be calculated unless all data are available " "simultaneously. To ensure that will be the case, call " "setCalculateAsAdded(False) on this object" ); _getStatsData().npts = _doMinMaxNpts(mymin, mymax); _getStatsData().min.reset (new AccumType(mymin)); _getStatsData().max.reset (new AccumType(mymax)); } else { // this will update _getStatsData().min and _getStatsData().max getMinMax(mymin, mymax); } } else if (_getStatsData().npts == 0) { // we don't need to capture the return value as npts is set outside // the block because this call sets _getStatsData().npts getNPts(); } mymin = *_getStatsData().min; mymax = *_getStatsData().max; npts = (uInt64)_getStatsData().npts; } CASA_STATD uInt64 ClassicalStatistics::getNPts() { if (_getStatsData().npts == 0) { ThrowIf( _calculateAsAdded, "npts cannot be calculated unless all data are available " "simultaneously. To ensure that will be the case, call " "setCalculateAsAdded(False) on this object" ); _getStatsData().npts = _doNpts(); } return (uInt64)_getStatsData().npts; } CASA_STATD std::map ClassicalStatistics::getQuantiles( const std::set& fractions, std::shared_ptr knownNpts, std::shared_ptr knownMin, std::shared_ptr knownMax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt nBins ) { ThrowIf( _calculateAsAdded, "Quantiles cannot be calculated unless all data are available " "simultaneously. To ensure that will be the case, call " "setCalculateAsAdded(False) on this object" ); uInt64 mynpts; AccumType mymin, mymax; _doNptsMinMax(mynpts, mymin, mymax, knownNpts, knownMin, knownMax); return _qComputer->getQuantiles( fractions, mynpts, mymin, mymax, binningThreshholdSizeBytes, persistSortedArray, nBins ); } CASA_STATD void ClassicalStatistics::setCalculateAsAdded(Bool c) { const StatisticsDataset& ds = this->_getDataset(); ThrowIf ( ds.getDataProvider() && c, "Logic Error: It is nonsensical to call " + String(__func__) + " method with a True value if one is using a data provider" ); ThrowIf( ds.iDataset() > 0, "Logic Error: " + String(__func__) + " cannot be called after the first dataset has been set" ); _calculateAsAdded = c; } CASA_STATD void ClassicalStatistics::setDataProvider( StatsDataProvider *dataProvider ) { ThrowIf( _calculateAsAdded, "Logic Error: setCalculateAsAdded(True) has previously been called, " "in which case it is nonsensical to use a data provider. Please call " "setCalculateAsAdded(False), and then set the data provider" ); StatisticsAlgorithm::setDataProvider(dataProvider); } CASA_STATD void ClassicalStatistics::setStatsToCalculate( std::set& stats ) { const StatisticsDataset& ds = this->_getDataset(); ThrowIf( _calculateAsAdded && ds.iDataset() > 0, "Cannot set stats to be calculated after setting the first dataset " "when stats are to be calculated as data are added" ); _doMaxMin = stats.empty() || stats.find(StatisticsData::MAX) != stats.end() || stats.find(StatisticsData::MIN) != stats.end(); StatisticsAlgorithm::setStatsToCalculate(stats); } CASA_STATD void ClassicalStatistics::_addData() { _qComputer->_setSortedArray(std::vector()); _getStatsData().median = NULL; _mustAccumulate = True; if (_calculateAsAdded) { // just need to call it, don't need the return value here _getStatistics(); StatisticsAlgorithm::reset(); _qComputer->reset(); } } CASA_STATD void ClassicalStatistics::reset() { _clearStats(); StatisticsAlgorithm::reset(); } CASA_STATD void ClassicalStatistics::_clearStats() { _statsData = initializeStatsData(); this->_getDataset().resetIDataset(); _qComputer->reset(); _mustAccumulate = True; } CASA_STATD LocationType ClassicalStatistics::getStatisticIndex( StatisticsData::STATS stat ) { ThrowIf( ! (stat == StatisticsData::MAX || stat == StatisticsData::MIN), "Index only available for max and min" ); ThrowIf( ! _doMaxMin, "You must specify to calculate the max " "and/or min if you want this index" ); std::set stats = this->_getStatsToCalculate(); ThrowIf( ! stats.empty() && ( ( stat == StatisticsData::MAX && stats.find(StatisticsData::MAX) == stats.end() ) || ( stat == StatisticsData::MIN && stats.find(StatisticsData::MIN) == stats.end() ) ), "You did not request to compute this statistic" ); // this call will calculate maxpos and minpos _getStatistics(); if (stat == StatisticsData::MAX) { return _getStatsData().maxpos; } else if (stat == StatisticsData::MIN) { return _getStatsData().minpos; } else { ThrowCc( "Logic Error: This branch should never be " "executed. Please file a defect report." ); } } CASA_STATD StatsData ClassicalStatistics::_getInitialStats() const { static const auto stats = initializeStatsData(); return stats; } CASA_STATD AccumType ClassicalStatistics::_getStatistic( StatisticsData::STATS stat ) { switch (stat) { case StatisticsData::MEDIAN: return this->getMedian(); case StatisticsData::MEDABSDEVMED: return this->getMedianAbsDevMed(); case StatisticsData::FIRST_QUARTILE: { std::set f; f.insert(0.25); return this->getQuantiles(f)[0.25]; } case StatisticsData::THIRD_QUARTILE: { std::set f; f.insert(0.75); return this->getQuantiles(f)[0.75]; } case StatisticsData::INNER_QUARTILE_RANGE: { std::set f; f.insert(0.25); f.insert(0.75); std::map qs = this->getQuantiles(f); return qs[0.75] - qs[0.25]; } default: AccumType value; auto r = toRecord(_getStatistics()); auto statString = StatisticsData::toString(stat); ThrowIf( ! r.isDefined(statString), "Logic Error: stat " + statString + " is not defined. " "Please file a defect report" ); r.get(statString, value); return value; } } CASA_STATD StatsData ClassicalStatistics::_getStatistics() { StatsData& stats = _getStatsData(); if (! _mustAccumulate) { return copy(stats); } auto& ds = this->_getDataset(); ds.initIterators(); uInt nThreadsMax = StatisticsUtilities::nThreadsMax( ds.getDataProvider() ); std::unique_ptr[]> tStats( new StatsData[ ClassicalStatisticsData::CACHE_PADDING*nThreadsMax ] ); for (uInt i=0; i dataIter; std::unique_ptr maskIter; std::unique_ptr weightsIter; std::unique_ptr offset; ds.initThreadVars( nBlocks, extra, nthreads, dataIter, maskIter, weightsIter, offset, nThreadsMax ); if (chunk.weights) { stats.weighted = True; } if (chunk.mask) { stats.masked = True; } #ifdef _OPENMP #pragma omp parallel for num_threads(nthreads) #endif for (uInt i=0; i::threadIdx(); uInt64 dataCount = (chunk.count - offset[idx8]) < ClassicalStatisticsData::BLOCK_SIZE ? extra : ClassicalStatisticsData::BLOCK_SIZE; LocationType location(ds.iDataset(), offset[idx8]); _computeStats( tStats[idx8], ngood, location, dataIter[idx8], maskIter[idx8], weightsIter[idx8], dataCount, chunk ); ds.incrementThreadIters( dataIter[idx8], maskIter[idx8], weightsIter[idx8], offset[idx8], nthreads ); } for (uInt tid=0; tid> xstats; for (uInt i=0; i& s = tStats[ClassicalStatisticsData::CACHE_PADDING*i]; if (s.minpos.first < 0) { s.min.reset(); } if (s.maxpos.first < 0) { s.max.reset(); } if(s.npts > 0) { xstats.push_back(s); } } if (stats.npts > 0) { // we've accumulated some stats previously so we must // account for that here xstats.push_back(stats); } auto vstats = StatisticsUtilities::combine(xstats); stats.masked = vstats.masked; stats.max = vstats.max; stats.maxpos = vstats.maxpos; stats.mean = vstats.mean; stats.min = vstats.min; stats.minpos = vstats.minpos; stats.npts = vstats.npts; stats.nvariance = vstats.nvariance; stats.rms = vstats.rms; stats.stddev = vstats.stddev; stats.sum = vstats.sum; stats.sumsq = vstats.sumsq; stats.sumweights = vstats.sumweights; stats.variance = vstats.variance; stats.weighted = vstats.weighted; _mustAccumulate = False; return copy(stats); } CASA_STATD void ClassicalStatistics::_computeStats( StatsData& stats, uInt64& ngood, LocationType& location, DataIterator dataIter, MaskIterator maskIter, WeightsIterator weightsIter, uInt64 count, const ChunkType& chunk ) { if (chunk.weights) { stats.weighted = True; if (chunk.mask) { stats.masked = True; if (chunk.ranges) { _weightedStats( stats, location, dataIter, weightsIter, count, chunk.dataStride, maskIter, chunk.mask->second, chunk.ranges->first, chunk.ranges->second ); } else { _weightedStats( stats, location, dataIter, weightsIter, count, chunk.dataStride, maskIter, chunk.mask->second ); } } else if (chunk.ranges) { _weightedStats( stats, location, dataIter, weightsIter, count, chunk.dataStride, chunk.ranges->first, chunk.ranges->second ); } else { // has weights, but no mask nor ranges _weightedStats( stats, location, dataIter, weightsIter, count, chunk.dataStride ); } } else if (chunk.mask) { // this data set has no weights, but does have a mask stats.masked = True; if (chunk.ranges) { _unweightedStats( stats, ngood, location, dataIter, count, chunk.dataStride, maskIter, chunk.mask->second, chunk.ranges->first, chunk.ranges->second ); } else { _unweightedStats( stats, ngood, location, dataIter, count, chunk.dataStride, maskIter, chunk.mask->second ); } } else if (chunk.ranges) { // this data set has no weights no mask, but does have a set of ranges // associated with it _unweightedStats( stats, ngood, location, dataIter, count, chunk.dataStride, chunk.ranges->first, chunk.ranges->second ); } else { // simplest case, this data set has no weights, no mask, nor any ranges // associated with it, and its stride is 1. No filtering of the data is // necessary. _unweightedStats( stats, ngood, location, dataIter, count, chunk.dataStride ); } if (! chunk.weights) { stats.sumweights += ngood; } } CASA_STATD void ClassicalStatistics::_accumNpts( uInt64& npts, const DataIterator&, uInt64 nr, uInt ) const { npts += nr; } CASA_STATD void ClassicalStatistics::_accumNpts( uInt64& npts, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { auto datum = dataBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { ++npts; } StatisticsIncrementer::increment(datum, count, dataStride); } } CASA_STATD void ClassicalStatistics::_accumNpts( uInt64& npts, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { auto datum = dataBegin; auto mask = maskBegin; uInt64 count = 0; while (count < nr) { if (*mask) { ++npts; } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalStatistics::_accumNpts( uInt64& npts, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { auto datum = dataBegin; auto mask = maskBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *mask && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { ++npts; } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalStatistics::_accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) const { auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; while (count < nr) { if (*weight > 0) { ++npts; } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ClassicalStatistics::_accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { ++npts; } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ClassicalStatistics::_accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { auto datum = dataBegin; auto weight = weightsBegin; auto mask = maskBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *mask && *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { ++npts; } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalStatistics::_accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { auto datum = dataBegin; auto weight = weightBegin; auto mask = maskBegin; uInt64 count = 0; while (count < nr) { if (*mask && *weight > 0) { ++npts; } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalStatistics::_accumulate( StatsData& stats, const AccumType& datum, const LocationType& location ) { if (_doMaxMin) { StatisticsUtilities::accumulate( stats.npts, stats.sum, stats.mean, stats.nvariance, stats.sumsq, *stats.min, *stats.max, stats.minpos, stats.maxpos, datum, location ); } else { StatisticsUtilities::accumulate( stats.npts, stats.sum, stats.mean, stats.nvariance, stats.sumsq, datum ); } } CASA_STATD void ClassicalStatistics::_accumulate( StatsData& stats, const AccumType& datum, const AccumType& weight, const LocationType& location ) { if (_doMaxMin) { StatisticsUtilities::waccumulate ( stats.npts, stats.sumweights, stats.sum, stats.mean, stats.nvariance, stats.sumsq, *stats.min, *stats.max, stats.minpos, stats.maxpos, datum, weight, location ); } else { StatisticsUtilities::waccumulate ( stats.npts, stats.sumweights, stats.sum, stats.mean, stats.nvariance, stats.sumsq, datum, weight ); } } CASA_STATD void ClassicalStatistics::_doMinMax( AccumType& datamin, AccumType& datamax ) { StatisticsDataset& ds = this->_getDataset(); ds.initIterators(); const uInt nThreadsMax = StatisticsUtilities::nThreadsMax( ds.getDataProvider() ); std::unique_ptr[]> tmin( new std::shared_ptr[ ClassicalStatisticsData::CACHE_PADDING*nThreadsMax ] ); std::unique_ptr[]> tmax( new std::shared_ptr[ ClassicalStatisticsData::CACHE_PADDING*nThreadsMax ] ); while (True) { const auto& chunk = ds.initLoopVars(); uInt nBlocks, nthreads; uInt64 extra; std::unique_ptr dataIter; std::unique_ptr maskIter; std::unique_ptr weightsIter; std::unique_ptr offset; ds.initThreadVars( nBlocks, extra, nthreads, dataIter, maskIter, weightsIter, offset, nThreadsMax ); #ifdef _OPENMP #pragma omp parallel for num_threads(nthreads) #endif for (uInt i=0; i::threadIdx(); uInt64 dataCount = (chunk.count - offset[idx8]) < ClassicalStatisticsData::BLOCK_SIZE ? extra : ClassicalStatisticsData::BLOCK_SIZE; _computeMinMax( tmax[idx8], tmin[idx8], dataIter[idx8], maskIter[idx8], weightsIter[idx8], dataCount, chunk ); ds.incrementThreadIters( dataIter[idx8], maskIter[idx8], weightsIter[idx8], offset[idx8], nthreads ); } if (ds.increment(False)) { break; } } std::shared_ptr mymax; std::shared_ptr mymin; for (uInt i=0; i *mymax)) { mymax = tmax[idx8]; } } ThrowIf (! mymax || ! mymin, "No valid data found"); datamin = *mymin; datamax = *mymax; } CASA_STATD void ClassicalStatistics::_computeMinMax( std::shared_ptr& mymax, std::shared_ptr& mymin, DataIterator dataIter, MaskIterator maskIter, WeightsIterator weightsIter, uInt64 dataCount, const ChunkType& chunk ) { if (chunk.weights) { if (chunk.mask) { if (chunk.ranges) { _minMax( mymin, mymax, dataIter, weightsIter, dataCount, chunk.dataStride, maskIter, chunk.mask->second, chunk.ranges->first, chunk.ranges->second ); } else { _minMax( mymin, mymax, dataIter, weightsIter, dataCount, chunk.dataStride, maskIter, chunk.mask->second ); } } else if (chunk.ranges) { _minMax( mymin, mymax, dataIter, weightsIter, dataCount, chunk.dataStride, chunk.ranges->first, chunk.ranges->second ); } else { // has weights, but no mask nor ranges _minMax( mymin, mymax, dataIter, weightsIter, dataCount, chunk.dataStride ); } } else if (chunk.mask) { // this data set has no weights, but does have a mask if (chunk.ranges) { _minMax( mymin, mymax, dataIter, dataCount, chunk.dataStride, maskIter, chunk.mask->second, chunk.ranges->first, chunk.ranges->second ); } else { _minMax( mymin, mymax, dataIter, dataCount, chunk.dataStride, maskIter, chunk.mask->second ); } } else if (chunk.ranges) { // this data set has no weights no mask, but does have a set of ranges // associated with it _minMax( mymin, mymax, dataIter, dataCount, chunk.dataStride, chunk.ranges->first, chunk.ranges->second ); } else { // simplest case, this data set has no weights, no mask, nor any ranges // associated with it. No filtering of the data is necessary. _minMax(mymin, mymax, dataIter, dataCount, chunk.dataStride); } } CASA_STATD uInt64 ClassicalStatistics::_doMinMaxNpts( AccumType& datamin, AccumType& datamax ) { StatisticsDataset& ds = this->_getDataset(); ds.initIterators(); const uInt nThreadsMax = StatisticsUtilities::nThreadsMax( ds.getDataProvider() ); std::unique_ptr[]> tmin( new std::shared_ptr[ ClassicalStatisticsData::CACHE_PADDING*nThreadsMax ] ); std::unique_ptr[]> tmax( new std::shared_ptr[ ClassicalStatisticsData::CACHE_PADDING*nThreadsMax ] ); std::unique_ptr npts( new uInt64[ ClassicalStatisticsData::CACHE_PADDING*nThreadsMax ] ); for (uInt tid=0; tid dataIter; std::unique_ptr maskIter; std::unique_ptr weightsIter; std::unique_ptr offset; ds.initThreadVars( nBlocks, extra, nthreads, dataIter, maskIter, weightsIter, offset, nThreadsMax ); #ifdef _OPENMP #pragma omp parallel for num_threads(nthreads) #endif for (uInt i=0; i::threadIdx(); uInt64 dataCount = (chunk.count - offset[idx8]) < ClassicalStatisticsData::BLOCK_SIZE ? extra : ClassicalStatisticsData::BLOCK_SIZE; _computeMinMaxNpts( npts[idx8], tmax[idx8], tmin[idx8], dataIter[idx8], maskIter[idx8], weightsIter[idx8], dataCount, chunk ); ds.incrementThreadIters( dataIter[idx8], maskIter[idx8], weightsIter[idx8], offset[idx8], nthreads ); } if (ds.increment(False)) { break; } } std::shared_ptr mymin, mymax; uInt64 myNpts = 0; for (uInt i=0; i *mymax)) { mymax = tmax[idx8]; } myNpts += npts[idx8]; } ThrowIf (! mymax || ! mymin || myNpts == 0, "No valid data found"); datamin = *mymin; datamax = *mymax; return myNpts; } CASA_STATD void ClassicalStatistics::_computeMinMaxNpts( uInt64& npts, std::shared_ptr& mymax, std::shared_ptr& mymin, DataIterator dataIter, MaskIterator maskIter, WeightsIterator weightsIter, uInt64 dataCount, const ChunkType& chunk ) { if (chunk.weights) { if (chunk.mask) { if (chunk.ranges) { _minMaxNpts( npts, mymin, mymax, dataIter, weightsIter, dataCount, chunk.dataStride, maskIter, chunk.mask->second, chunk.ranges->first, chunk.ranges->second ); } else { _minMaxNpts( npts, mymin, mymax, dataIter, weightsIter, dataCount, chunk.dataStride, maskIter, chunk.mask->second ); } } else if (chunk.ranges) { _minMaxNpts( npts, mymin, mymax, dataIter, weightsIter, dataCount, chunk.dataStride, chunk.ranges->first, chunk.ranges->second ); } else { // has weights, but no mask nor ranges _minMaxNpts( npts, mymin, mymax, dataIter, weightsIter, dataCount, chunk.dataStride ); } } else if (chunk.mask) { // this data set has no weights, but does have a mask if (chunk.ranges) { _minMaxNpts( npts, mymin, mymax, dataIter, dataCount, chunk.dataStride, maskIter, chunk.mask->second, chunk.ranges->first, chunk.ranges->second ); } else { _minMaxNpts( npts, mymin, mymax, dataIter, dataCount, chunk.dataStride, maskIter, chunk.mask->second ); } } else if (chunk.ranges) { // this data set has no weights no mask, but does have a set of ranges // associated with it _minMaxNpts( npts, mymin, mymax, dataIter, dataCount, chunk.dataStride, chunk.ranges->first, chunk.ranges->second ); } else { // simplest case, this data set has no weights, no mask, nor any ranges // associated with it. No filtering of the data is necessary. _minMaxNpts(npts, mymin, mymax, dataIter, dataCount, chunk.dataStride); } } CASA_STATD uInt64 ClassicalStatistics::_doNpts() { auto& ds = this->_getDataset(); ds.initIterators(); const uInt nThreadsMax = StatisticsUtilities::nThreadsMax( ds.getDataProvider() ); std::unique_ptr npts( new uInt64[ClassicalStatisticsData::CACHE_PADDING*nThreadsMax] ); for (uInt tid=0; tid dataIter; std::unique_ptr maskIter; std::unique_ptr weightsIter; std::unique_ptr offset; ds.initThreadVars( nBlocks, extra, nthreads, dataIter, maskIter, weightsIter, offset, nThreadsMax ); #ifdef _OPENMP #pragma omp parallel for num_threads(nthreads) #endif for (uInt i=0; i::threadIdx(); uInt64 dataCount = (chunk.count - offset[idx8]) < ClassicalStatisticsData::BLOCK_SIZE ? extra : ClassicalStatisticsData::BLOCK_SIZE; _computeNpts( npts[idx8], dataIter[idx8], maskIter[idx8], weightsIter[idx8], dataCount, chunk ); ds.incrementThreadIters( dataIter[idx8], maskIter[idx8], weightsIter[idx8], offset[idx8], nthreads ); } if (ds.increment(False)) { break; } } uInt64 myNpts = 0; for (uInt i=0; i::_computeNpts( uInt64& npts, DataIterator dataIter, MaskIterator maskIter, WeightsIterator weightsIter, uInt64 dataCount, const ChunkType& chunk ) { if (chunk.weights) { if (chunk.mask) { if (chunk.ranges) { _accumNpts( npts, dataIter, weightsIter, dataCount, chunk.dataStride, maskIter, chunk.mask->second, chunk.ranges->first, chunk.ranges->second ); } else { _accumNpts( npts, dataIter, weightsIter, dataCount, chunk.dataStride, maskIter, chunk.mask->second ); } } else if (chunk.ranges) { _accumNpts( npts, dataIter, weightsIter, dataCount, chunk.dataStride, chunk.ranges->first, chunk.ranges->second ); } else { // has weights, but no mask nor ranges _accumNpts( npts, dataIter, weightsIter, dataCount, chunk.dataStride ); } } else if (chunk.mask) { // this data set has no weights, but does have a mask if (chunk.ranges) { _accumNpts( npts, dataIter, dataCount, chunk.dataStride, maskIter, chunk.mask->second, chunk.ranges->first, chunk.ranges->second ); } else { _accumNpts( npts, dataIter, dataCount, chunk.dataStride, maskIter, chunk.mask->second ); } } else if (chunk.ranges) { // this data set has no weights no mask, but does have a set of ranges // associated with it _accumNpts( npts, dataIter, dataCount, chunk.dataStride, chunk.ranges->first, chunk.ranges->second ); } else { // simplest case, this data set has no weights, no mask, nor any ranges // associated with it. _accumNpts(npts, dataIter, dataCount, chunk.dataStride); } } #define _minMaxCode \ if (mymin) { \ if (*datum < *mymin) { \ *mymin = *datum; \ } \ else if (*datum > *mymax) { \ *mymax = *datum; \ } \ } \ else { \ mymin.reset (new AccumType(*datum)); \ mymax.reset (new AccumType(*datum)); \ } CASA_STATD void ClassicalStatistics::_minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ) const { auto datum = dataBegin; uInt64 count = 0; while (count < nr) { _minMaxCode StatisticsIncrementer::increment(datum, count, dataStride); } } CASA_STATD void ClassicalStatistics::_minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { auto datum = dataBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _minMaxCode } StatisticsIncrementer::increment(datum, count, dataStride); } } CASA_STATD void ClassicalStatistics::_minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { auto datum = dataBegin; auto mask = maskBegin; uInt64 count = 0; while (count < nr) { if (*mask) { _minMaxCode } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalStatistics::_minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { auto datum = dataBegin; auto mask = maskBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *mask && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _minMaxCode } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalStatistics::_minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) const { auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; while (count < nr) { if (*weight > 0) { _minMaxCode } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ClassicalStatistics::_minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _minMaxCode } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ClassicalStatistics::_minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { auto datum = dataBegin; auto weight = weightsBegin; auto mask = maskBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *mask && *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _minMaxCode } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalStatistics::_minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { auto datum = dataBegin; auto weight = weightBegin; auto mask = maskBegin; uInt64 count = 0; while (count < nr) { if (*mask && *weight > 0) { _minMaxCode } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } // same as _minMaxCode with the addition of npts accumulation #define _minMaxNptsCode \ _minMaxCode \ ++npts; CASA_STATD void ClassicalStatistics::_minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ) const { auto datum = dataBegin; uInt64 count = 0; npts += nr; while (count < nr) { // yes we really want _minMaxCode not _minMaxNptsCode here because // npts is easy addition in this case because of no data filtering _minMaxCode StatisticsIncrementer::increment(datum, count, dataStride); } } CASA_STATD void ClassicalStatistics::_minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { auto datum = dataBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _minMaxNptsCode } StatisticsIncrementer::increment(datum, count, dataStride); } } CASA_STATD void ClassicalStatistics::_minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { auto datum = dataBegin; auto mask = maskBegin; uInt64 count = 0; while (count < nr) { if (*mask) { _minMaxNptsCode } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalStatistics::_minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { auto datum = dataBegin; auto mask = maskBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *mask && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _minMaxNptsCode } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalStatistics::_minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) const { auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; while (count < nr) { if (*weight > 0) { _minMaxNptsCode } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ClassicalStatistics::_minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _minMaxNptsCode } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ClassicalStatistics::_minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { auto datum = dataBegin; auto weight = weightsBegin; MaskIterator mask = maskBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *mask && *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _minMaxNptsCode } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalStatistics::_minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { auto datum = dataBegin; auto weight = weightBegin; auto mask = maskBegin; uInt64 count = 0; while (count < nr) { if (*mask && *weight > 0) { _minMaxNptsCode } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } CASA_STATD void ClassicalStatistics::_updateDataProviderMaxMin( const StatsData& threadStats ) { auto& ds = this->_getDataset(); auto* dataProvider = ds.getDataProvider(); if (! dataProvider) { return; } // if there is a data provider, and the max and/or min updated, we have to // update the data provider after each data set is processed, because the // LatticeStatsDataProvider currently requires that. auto& stats = _getStatsData(); const auto same = &threadStats == &stats; const auto idataset = ds.iDataset(); if ( idataset == threadStats.maxpos.first && (! stats.max || *threadStats.max > *stats.max) ) { if (! same) { // make a copy, do not assign one pointer to another stats.maxpos = threadStats.maxpos; stats.max.reset (new AccumType(*threadStats.max)); } dataProvider->updateMaxPos(stats.maxpos); } if ( idataset == threadStats.minpos.first && (! stats.min || (*threadStats.min) < (*stats.min)) ) { if (! same) { // make a copy, do not assign one pointer to another stats.minpos = threadStats.minpos; stats.min.reset (new AccumType(*threadStats.min)); } dataProvider->updateMinPos(stats.minpos); } } CASA_STATD void ClassicalStatistics::_unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ) { auto datum = dataBegin; uInt64 count = 0; while (count < nr) { _accumulate(stats, *datum, location); StatisticsIncrementer::increment(datum, count, dataStride); location.second += dataStride; } ngood = nr; } CASA_STATD void ClassicalStatistics::_unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) { auto datum = dataBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _accumulate(stats, *datum, location); ++ngood; } StatisticsIncrementer::increment(datum, count, dataStride); location.second += dataStride; } } CASA_STATD void ClassicalStatistics::_unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) { auto datum = dataBegin; auto mask = maskBegin; uInt64 count = 0; while (count < nr) { if (*mask) { _accumulate(stats, *datum, location); ++ngood; } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); location.second += dataStride; } } CASA_STATD void ClassicalStatistics::_unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) { auto datum = dataBegin; auto mask = maskBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *mask && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _accumulate(stats, *datum, location); ++ngood; } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); location.second += dataStride; } } CASA_STATD void ClassicalStatistics::_weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) { auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; while (count < nr) { if (*weight > 0) { _accumulate(stats, *datum, *weight, location); } StatisticsIncrementer::increment( datum, count, weight, dataStride ); location.second += dataStride; } } CASA_STATD void ClassicalStatistics::_weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) { auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _accumulate(stats, *datum, *weight, location); } StatisticsIncrementer::increment( datum, count, weight, dataStride ); location.second += dataStride; } } CASA_STATD void ClassicalStatistics::_weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) { auto datum = dataBegin; auto weight = weightsBegin; auto mask = maskBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *mask && *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _accumulate(stats, *datum, *weight, location); } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); location.second += dataStride; } } CASA_STATD void ClassicalStatistics::_weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) { auto datum = dataBegin; auto weight = weightBegin; auto mask = maskBegin; uInt64 count = 0; while (count < nr) { if (*mask && *weight > 0) { _accumulate(stats, *datum, *weight, location); } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); location.second += dataStride; } } } #endif casacore-3.7.1/scimath/StatsFramework/ClassicalStatisticsData.cc000066400000000000000000000025521476623553700250060ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include namespace casacore { const uInt ClassicalStatisticsData::CACHE_PADDING = 8; const uInt ClassicalStatisticsData::BLOCK_SIZE = 4000; } casacore-3.7.1/scimath/StatsFramework/ClassicalStatisticsData.h000066400000000000000000000031541476623553700246470ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_CLASSICALSTATSDATA_H #define SCIMATH_CLASSICALSTATSDATA_H #include namespace casacore { // Non-templated data related to ClassicalStatistics class class ClassicalStatisticsData { public: ClassicalStatisticsData() = delete; ClassicalStatisticsData(const ClassicalStatisticsData&) = delete; static const uInt CACHE_PADDING; static const uInt BLOCK_SIZE; ~ClassicalStatisticsData() {} }; } #endif casacore-3.7.1/scimath/StatsFramework/ConstrainedRangeQuantileComputer.h000066400000000000000000000331741476623553700265610ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_CONSTRAINEDRANGEQUANTILECOMPUTER_H #define SCIMATH_CONSTRAINEDRANGEQUANTILECOMPUTER_H #include #include namespace casacore { // Basic concrete QuantileComputer class for data constrained to be // in a specified range. Some derived classes of ConstrainedRangeStatistics // use it. It should never be explicitly instantiated by an API developer. // See the class documentation of StatisticsAlgorithm for details on // QuantileComputer classes. template < class AccumType, class DataIterator, class MaskIterator=const Bool*, class WeightsIterator=DataIterator > class ConstrainedRangeQuantileComputer : public ClassicalQuantileComputer { public: ConstrainedRangeQuantileComputer() = delete; ConstrainedRangeQuantileComputer(StatisticsDataset* dataset); // copy semantics ConstrainedRangeQuantileComputer( const ConstrainedRangeQuantileComputer& csq ); virtual ~ConstrainedRangeQuantileComputer(); // copy semantics ConstrainedRangeQuantileComputer& operator=( const ConstrainedRangeQuantileComputer& other ); // clone this object by returning a pointer to a copy virtual StatisticsAlgorithmQuantileComputer* clone() const; // Caller is responsible for passing correct values of mynpts, mymin, and // mymax; no checking is done for correctness in this method. virtual AccumType getMedian( uInt64 mynpts, AccumType mymin, AccumType mymax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt nBins ); // get the median of the absolute deviation about the median of the data. virtual AccumType getMedianAbsDevMed( uInt64 mynpts, AccumType mymin, AccumType mymax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt nBins ); void setRange(const std::pair& r) { _range = r; } protected: // virtual void _findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const std::vector>& binDesc, const std::vector& maxLimit ) const; virtual void _findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const std::vector>& binDesc, const std::vector& maxLimit ) const; virtual void _findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const std::vector>& binDesc, const std::vector& maxLimit ) const; virtual void _findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const std::vector>& binDesc, const std::vector& maxLimit ) const; virtual void _findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const std::vector>& binDesc, const std::vector& maxLimit ) const ; virtual void _findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const std::vector>& binDesc, const std::vector& maxLimit ) const; virtual void _findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const std::vector>& binDesc, const std::vector& maxLimit ) const; virtual void _findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const std::vector>& binDesc, const std::vector& maxLimit ) const; // // // populate an unsorted array with valid data. If includeLimits // is defined, then restrict values that are entered in the array to those // limits (inclusive of the minimum, exclusive of the maximum). // maxCount and currentCount are used only if // includeLimits is defined. In this case, the method will return // when currentCount == maxCount, thus avoiding scanning remaining data // unnecessarily. // no weights, no mask, no ranges virtual void _populateArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ) const; // ranges virtual void _populateArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _populateArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; // mask and ranges virtual void _populateArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; // weights virtual void _populateArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) const; // weights and ranges virtual void _populateArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; // weights and mask virtual void _populateArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; // weights, mask, ranges virtual void _populateArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; // no weights, no mask, no ranges virtual void _populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const IncludeLimits& includeLimits, uInt64 maxCount ) const; // ranges virtual void _populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const IncludeLimits& includeLimits, uInt64 maxCount ) const; virtual void _populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const IncludeLimits& includeLimits, uInt64 maxCount ) const; // mask and ranges virtual void _populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const IncludeLimits& includeLimits, uInt64 maxCount ) const; // weights virtual void _populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const IncludeLimits& includeLimits, uInt64 maxCount ) const; // weights and ranges virtual void _populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const IncludeLimits& includeLimits, uInt64 maxCount ) const; // weights and mask virtual void _populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const IncludeLimits& includeLimits, uInt64 maxCount ) const; // weights, mask, ranges virtual void _populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const IncludeLimits& includeLimits, uInt64 maxCount ) const; // // // no weights, no mask, no ranges virtual Bool _populateTestArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, uInt maxElements ) const; // ranges virtual Bool _populateTestArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const; // mask virtual Bool _populateTestArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, uInt maxElements ) const; // mask and ranges virtual Bool _populateTestArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const; // weights virtual Bool _populateTestArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, uInt maxElements ) const; // weights and ranges virtual Bool _populateTestArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const; // weights and mask virtual Bool _populateTestArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, uInt maxElements ) const; // weights, mask, ranges virtual Bool _populateTestArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const; // private: Bool _doMedAbsDevMed{False}; // for use in macros of often repeatedly run methods AccumType _myMedian{0}; std::pair _range{}; }; } #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif #endif casacore-3.7.1/scimath/StatsFramework/ConstrainedRangeQuantileComputer.tcc000066400000000000000000001045421476623553700271010ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_CONSTRAINEDRANGEQUANTILECOMPUTER_TCC #define SCIMATH_CONSTRAINEDRANGEQUANTILECOMPUTER_TCC #include namespace casacore { CASA_STATD ConstrainedRangeQuantileComputer::ConstrainedRangeQuantileComputer( StatisticsDataset* dataset ) : ClassicalQuantileComputer(dataset) {} CASA_STATD ConstrainedRangeQuantileComputer::ConstrainedRangeQuantileComputer( const ConstrainedRangeQuantileComputer& other ) : ClassicalQuantileComputer(other), _doMedAbsDevMed(other._doMedAbsDevMed), _myMedian(other._myMedian), _range(other._range) {} CASA_STATD ConstrainedRangeQuantileComputer ::~ConstrainedRangeQuantileComputer() {} CASA_STATD ConstrainedRangeQuantileComputer& ConstrainedRangeQuantileComputer::operator=( const ConstrainedRangeQuantileComputer& other ) { if (this == &other) { return *this; } ClassicalQuantileComputer::operator=(other); _doMedAbsDevMed = other._doMedAbsDevMed; _myMedian = other._myMedian; _range = other._range; return *this; } CASA_STATD StatisticsAlgorithmQuantileComputer* ConstrainedRangeQuantileComputer::clone() const { return new ConstrainedRangeQuantileComputer(*this); } CASA_STATD AccumType ConstrainedRangeQuantileComputer::getMedian( uInt64 mynpts, AccumType mymin, AccumType mymax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt nBins ) { auto median = this->_getMedian(); if (! median) { median.reset (new AccumType( ClassicalQuantileComputer::getMedian( mynpts, mymin, mymax, binningThreshholdSizeBytes, persistSortedArray, nBins ) )); this->setMedian(median); } return *median; } CASA_STATD AccumType ConstrainedRangeQuantileComputer::getMedianAbsDevMed( uInt64 mynpts, AccumType mymin, AccumType mymax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt nBins ) { auto medabsdevmed = this->_getMedianAbsDevMedian(); if (! medabsdevmed) { std::shared_ptr median = this->_getMedian(); if (! median) { this->getMedian( mynpts, mymin, mymax, binningThreshholdSizeBytes, persistSortedArray, nBins ); } _doMedAbsDevMed = True; _myMedian = *this->_getMedian(); medabsdevmed.reset( new AccumType( ClassicalQuantileComputer::getMedianAbsDevMed( mynpts, mymin, mymax, binningThreshholdSizeBytes, persistSortedArray, nBins ) ) ); _doMedAbsDevMed = False; } return *medabsdevmed; } #define _findBinCodeCR \ if (*datum >= _range.first && *datum <= _range.second) { \ AccumType myDatum = _doMedAbsDevMed \ ? abs((AccumType)*datum - _myMedian) : *datum; \ if ( \ myDatum >= bBinDesc->getMinHistLimit() \ && myDatum < *maxLimit.rbegin() \ ) { \ iCounts = bCounts; \ iSameVal = bSameVal; \ iAllSame = bAllSame; \ iBinDesc = bBinDesc; \ iMaxLimit = bMaxLimit; \ while (iBinDesc != eBinDesc) { \ if ( \ myDatum >= iBinDesc->getMinHistLimit() \ && myDatum < *iMaxLimit \ ) { \ uInt idx = iBinDesc->getIndex(myDatum); \ ++(*iCounts)[idx]; \ if (*iAllSame) { \ if (! *iSameVal) { \ iSameVal->reset (new AccumType(myDatum)); \ } \ else { \ *iAllSame = myDatum == *(*iSameVal); \ if (! *iAllSame) { \ iSameVal->reset(); \ } \ } \ } \ break; \ } \ ++iCounts; \ ++iSameVal; \ ++iAllSame; \ ++iBinDesc; \ ++iMaxLimit; \ } \ } \ } CASA_STATD void ConstrainedRangeQuantileComputer::_findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const std::vector>& binDesc, const DataArray& maxLimit ) const { auto bCounts = binCounts.begin(); auto iCounts = bCounts; auto bSameVal = sameVal.begin(); auto iSameVal = bSameVal; auto bAllSame = allSame.begin(); auto iAllSame = bAllSame; auto bBinDesc = binDesc.cbegin(); auto iBinDesc = bBinDesc; auto eBinDesc = binDesc.cend(); auto bMaxLimit = maxLimit.cbegin(); auto iMaxLimit = bMaxLimit; auto datum = dataBegin; uInt64 count = 0; while (count < nr) { _findBinCodeCR StatisticsIncrementer::increment(datum, count, dataStride); } } CASA_STATD void ConstrainedRangeQuantileComputer::_findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const std::vector>& binDesc, const DataArray& maxLimit ) const { auto bCounts = binCounts.begin(); auto iCounts = bCounts; auto bSameVal = sameVal.begin(); auto iSameVal = bSameVal; auto bAllSame = allSame.begin(); auto iAllSame = bAllSame; auto bBinDesc = binDesc.cbegin(); auto iBinDesc = bBinDesc; auto eBinDesc = binDesc.cend(); auto bMaxLimit = maxLimit.cbegin(); auto iMaxLimit = bMaxLimit; auto datum = dataBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _findBinCodeCR } StatisticsIncrementer::increment(datum, count, dataStride); } } CASA_STATD void ConstrainedRangeQuantileComputer::_findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const std::vector>& binDesc, const DataArray& maxLimit ) const { auto bCounts = binCounts.begin(); auto iCounts = bCounts; auto bSameVal = sameVal.begin(); auto iSameVal = bSameVal; auto bAllSame = allSame.begin(); auto iAllSame = bAllSame; auto bBinDesc = binDesc.cbegin(); auto iBinDesc = bBinDesc; auto eBinDesc = binDesc.cend(); auto bMaxLimit = maxLimit.cbegin(); auto iMaxLimit = bMaxLimit; auto datum = dataBegin; auto mask = maskBegin; uInt64 count = 0; while (count < nr) { if (*mask) { _findBinCodeCR } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ConstrainedRangeQuantileComputer::_findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const std::vector>& binDesc, const DataArray& maxLimit ) const { auto bCounts = binCounts.begin(); auto iCounts = bCounts; auto bSameVal = sameVal.begin(); auto iSameVal = bSameVal; auto bAllSame = allSame.begin(); auto iAllSame = bAllSame; auto bBinDesc = binDesc.cbegin(); auto iBinDesc = bBinDesc; auto eBinDesc = binDesc.cend(); auto bMaxLimit = maxLimit.cbegin(); auto iMaxLimit = bMaxLimit; auto datum = dataBegin; auto mask = maskBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *mask && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _findBinCodeCR } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ConstrainedRangeQuantileComputer::_findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const std::vector>& binDesc, const DataArray& maxLimit ) const { auto bCounts = binCounts.begin(); auto iCounts = bCounts; auto bSameVal = sameVal.begin(); auto iSameVal = bSameVal; auto bAllSame = allSame.begin(); auto iAllSame = bAllSame; auto bBinDesc = binDesc.cbegin(); auto iBinDesc = bBinDesc; auto eBinDesc = binDesc.cend(); auto bMaxLimit = maxLimit.cbegin(); auto iMaxLimit = bMaxLimit; auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; while (count < nr) { if (*weight > 0) { _findBinCodeCR } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ConstrainedRangeQuantileComputer::_findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const std::vector>& binDesc, const DataArray& maxLimit ) const { auto bCounts = binCounts.begin(); auto iCounts = bCounts; auto bSameVal = sameVal.begin(); auto iSameVal = bSameVal; auto bAllSame = allSame.begin(); auto iAllSame = bAllSame; auto bBinDesc = binDesc.cbegin(); auto iBinDesc = bBinDesc; auto eBinDesc = binDesc.cend(); auto bMaxLimit = maxLimit.cbegin(); auto iMaxLimit = bMaxLimit; auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _findBinCodeCR } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ConstrainedRangeQuantileComputer::_findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const std::vector>& binDesc, const DataArray& maxLimit ) const { auto bCounts = binCounts.begin(); auto iCounts = bCounts; auto bSameVal = sameVal.begin(); auto iSameVal = bSameVal; auto bAllSame = allSame.begin(); auto iAllSame = bAllSame; auto bBinDesc = binDesc.cbegin(); auto iBinDesc = bBinDesc; auto eBinDesc = binDesc.cend(); auto bMaxLimit = maxLimit.cbegin(); auto iMaxLimit = bMaxLimit; auto datum = dataBegin; auto weight = weightsBegin; auto mask = maskBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *mask && *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _findBinCodeCR } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } CASA_STATD void ConstrainedRangeQuantileComputer::_findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const std::vector>& binDesc, const DataArray& maxLimit ) const { auto bCounts = binCounts.begin(); auto iCounts = bCounts; auto bSameVal = sameVal.begin(); auto iSameVal = bSameVal; auto bAllSame = allSame.begin(); auto iAllSame = bAllSame; auto bBinDesc = binDesc.cbegin(); auto iBinDesc = bBinDesc; auto eBinDesc = binDesc.cend(); auto bMaxLimit = maxLimit.cbegin(); auto iMaxLimit = bMaxLimit; auto datum = dataBegin; auto weight = weightsBegin; auto mask = maskBegin; uInt64 count = 0; while (count < nr) { if (*mask && *weight > 0) { _findBinCodeCR } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } // define rather than make a method to ensure this is called inline to maximize // performance #define _populateArrayCodeCR1 \ if (*datum >= _range.first && *datum <= _range.second) { \ AccumType myDatum = _doMedAbsDevMed \ ? abs((AccumType)*datum - _myMedian) : *datum; \ ary.push_back(myDatum); \ } CASA_STATD void ConstrainedRangeQuantileComputer::_populateArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ) const { uInt64 count = 0; auto datum = dataBegin; while (count < nr) { _populateArrayCodeCR1 StatisticsIncrementer::increment(datum, count, dataStride); } } CASA_STATD void ConstrainedRangeQuantileComputer::_populateArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { uInt64 count = 0; auto datum = dataBegin; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _populateArrayCodeCR1 } StatisticsIncrementer::increment(datum, count, dataStride); } } CASA_STATD void ConstrainedRangeQuantileComputer::_populateArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { uInt64 count = 0; auto datum = dataBegin; auto mask = maskBegin; while (count < nr) { if (*mask) { _populateArrayCodeCR1 } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ConstrainedRangeQuantileComputer::_populateArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { uInt64 count = 0; auto datum = dataBegin; auto mask = maskBegin; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *mask && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _populateArrayCodeCR1 } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ConstrainedRangeQuantileComputer::_populateArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) const { auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; while (count < nr) { if (*weight > 0) { _populateArrayCodeCR1 } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ConstrainedRangeQuantileComputer::_populateArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _populateArrayCodeCR1 } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ConstrainedRangeQuantileComputer::_populateArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { auto datum = dataBegin; auto weight = weightsBegin; auto mask = maskBegin; uInt64 count = 0; while (count < nr) { if (*mask && *weight > 0) { _populateArrayCodeCR1 } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } CASA_STATD void ConstrainedRangeQuantileComputer::_populateArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { auto datum = dataBegin; auto weight = weightsBegin; auto mask = maskBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *mask && *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _populateArrayCodeCR1 } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } // define rather than make a method to ensure this is called inline to maximize performance #define _populateArraysCodeCR \ if (*datum >= _range.first && *datum <= _range.second) { \ AccumType myDatum = _doMedAbsDevMed \ ? abs((AccumType)*datum - _myMedian) : *datum; \ if ( \ myDatum >= includeLimits.cbegin()->first \ && myDatum < includeLimits.rbegin()->second \ ) { \ iIncludeLimits = bIncludeLimits; \ iArys = bArys; \ while (iIncludeLimits != eIncludeLimits) { \ if ( \ myDatum >= iIncludeLimits->first \ && myDatum < iIncludeLimits->second \ ) { \ iArys->push_back(myDatum); \ ++currentCount; \ if (currentCount == maxCount) { \ return; \ } \ break; \ } \ ++iIncludeLimits; \ ++iArys; \ } \ } \ } CASA_STATD void ConstrainedRangeQuantileComputer::_populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const IncludeLimits& includeLimits, uInt64 maxCount ) const { auto bArys = arys.begin(); auto iArys = bArys; auto bIncludeLimits = includeLimits.cbegin(); auto iIncludeLimits = bIncludeLimits; auto eIncludeLimits = includeLimits.cend(); uInt64 count = 0; auto datum = dataBegin; while (count < nr) { _populateArraysCodeCR StatisticsIncrementer::increment(datum, count, dataStride); } } CASA_STATD void ConstrainedRangeQuantileComputer::_populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const IncludeLimits& includeLimits, uInt64 maxCount ) const { auto bArys = arys.begin(); auto iArys = bArys; auto bIncludeLimits = includeLimits.cbegin(); auto iIncludeLimits = bIncludeLimits; auto eIncludeLimits = includeLimits.cend(); uInt64 count = 0; auto datum = dataBegin; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _populateArraysCodeCR } StatisticsIncrementer::increment(datum, count, dataStride); } } CASA_STATD void ConstrainedRangeQuantileComputer::_populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const IncludeLimits& includeLimits, uInt64 maxCount ) const { auto bArys = arys.begin(); auto iArys = bArys; auto bIncludeLimits = includeLimits.cbegin(); auto iIncludeLimits = bIncludeLimits; auto eIncludeLimits = includeLimits.cend(); uInt64 count = 0; auto datum = dataBegin; auto mask = maskBegin; while (count < nr) { if (*mask) { _populateArraysCodeCR } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ConstrainedRangeQuantileComputer::_populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const IncludeLimits& includeLimits, uInt64 maxCount ) const { auto bArys = arys.begin(); auto iArys = bArys; auto bIncludeLimits = includeLimits.cbegin(); auto iIncludeLimits = bIncludeLimits; auto eIncludeLimits = includeLimits.cend(); uInt64 count = 0; auto datum = dataBegin; auto mask = maskBegin; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *mask && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _populateArraysCodeCR } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ConstrainedRangeQuantileComputer::_populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const IncludeLimits& includeLimits, uInt64 maxCount ) const { auto bArys = arys.begin(); auto iArys = bArys; auto bIncludeLimits = includeLimits.cbegin(); auto iIncludeLimits = bIncludeLimits; auto eIncludeLimits = includeLimits.cend(); auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; while (count < nr) { if (*weight > 0) { _populateArraysCodeCR } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ConstrainedRangeQuantileComputer::_populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const IncludeLimits& includeLimits, uInt64 maxCount ) const { auto bArys = arys.begin(); auto iArys = bArys; auto bIncludeLimits = includeLimits.cbegin(); auto iIncludeLimits = bIncludeLimits; auto eIncludeLimits = includeLimits.cend(); auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _populateArraysCodeCR } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ConstrainedRangeQuantileComputer::_populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const IncludeLimits& includeLimits, uInt64 maxCount ) const { auto bArys = arys.begin(); auto iArys = bArys; auto bIncludeLimits = includeLimits.cbegin(); auto iIncludeLimits = bIncludeLimits; auto eIncludeLimits = includeLimits.cend(); auto datum = dataBegin; auto weight = weightsBegin; auto mask = maskBegin; uInt64 count = 0; while (count < nr) { if (*mask && *weight > 0) { _populateArraysCodeCR } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } CASA_STATD void ConstrainedRangeQuantileComputer::_populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const IncludeLimits& includeLimits, uInt64 maxCount ) const { auto bArys = arys.begin(); auto iArys = bArys; auto bIncludeLimits = includeLimits.cbegin(); auto iIncludeLimits = bIncludeLimits; auto eIncludeLimits = includeLimits.cend(); auto datum = dataBegin; auto weight = weightsBegin; auto mask = maskBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *mask && *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _populateArraysCodeCR } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } // define rather than make a method to ensure this is called inline to maximize // performance #define _PopulateTestArrayCodeCR \ if (*datum >= _range.first && *datum <= _range.second) { \ ary.push_back( \ _doMedAbsDevMed ? abs((AccumType)*datum - _myMedian) : *datum \ ); \ ++npts; \ if (npts > maxElements) { \ return True; \ } \ } CASA_STATD Bool ConstrainedRangeQuantileComputer::_populateTestArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, uInt maxElements ) const { uInt64 count = 0; auto npts = ary.size(); auto datum = dataBegin; while (count < nr) { _PopulateTestArrayCodeCR StatisticsIncrementer::increment(datum, count, dataStride); } return False; } CASA_STATD Bool ConstrainedRangeQuantileComputer::_populateTestArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const { uInt64 count = 0; auto npts = ary.size(); auto datum = dataBegin; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _PopulateTestArrayCodeCR } StatisticsIncrementer::increment(datum, count, dataStride); } return False; } CASA_STATD Bool ConstrainedRangeQuantileComputer::_populateTestArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, uInt maxElements ) const { uInt64 count = 0; auto datum = dataBegin; auto mask = maskBegin; auto npts = ary.size(); while (count < nr) { if (*mask) { _PopulateTestArrayCodeCR } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } return False; } CASA_STATD Bool ConstrainedRangeQuantileComputer::_populateTestArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const { uInt64 count = 0; auto datum = dataBegin; auto mask = maskBegin; auto npts = ary.size(); auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *mask && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _PopulateTestArrayCodeCR } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } return False; } CASA_STATD Bool ConstrainedRangeQuantileComputer::_populateTestArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, uInt maxElements ) const { auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; auto npts = ary.size(); while (count < nr) { if (*weight > 0) { _PopulateTestArrayCodeCR } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } return False; } CASA_STATD Bool ConstrainedRangeQuantileComputer::_populateTestArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const { auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); auto npts = ary.size(); while (count < nr) { if ( *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _PopulateTestArrayCodeCR } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } return False; } CASA_STATD Bool ConstrainedRangeQuantileComputer::_populateTestArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, uInt maxElements ) const { auto datum = dataBegin; auto weight = weightsBegin; auto mask = maskBegin; uInt64 count = 0; auto npts = ary.size(); while (count < nr) { if (*mask && *weight > 0) { _PopulateTestArrayCodeCR } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } return False; } CASA_STATD Bool ConstrainedRangeQuantileComputer::_populateTestArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const { auto datum = dataBegin; auto weight = weightsBegin; auto mask = maskBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); auto npts = ary.size(); while (count < nr) { if ( *mask && *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _PopulateTestArrayCodeCR } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } return False; } } #endif casacore-3.7.1/scimath/StatsFramework/ConstrainedRangeStatistics.h000066400000000000000000000421171476623553700254070ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_CONSTRAINEDRANGESTATISTICS_H #define SCIMATH_CONSTRAINEDRANGESTATISTICS_H #include #include #include #include #include #include namespace casacore { // Abstract base class for statistics algorithms which are characterized by // a range of good values. The range is usually calculated dynamically based // on the entire distribution. The specifics of such calculations are // delegated to derived classes. template < class AccumType, class DataIterator, class MaskIterator=const Bool*, class WeightsIterator=DataIterator > class ConstrainedRangeStatistics : public ClassicalStatistics { public: ConstrainedRangeStatistics() = delete; virtual ~ConstrainedRangeStatistics(); // // In the following group of methods, if the size of the composite dataset // is smaller than // binningThreshholdSizeBytes, the composite dataset // will be (perhaps partially) sorted and persisted in memory during the // call. In that case, and if persistSortedArray is True, this // sorted array will remain in memory after the call and will be used on // subsequent calls of this method when // binningThreshholdSizeBytes is greater than the size of the // composite dataset. If persistSortedArray is False, the sorted // array will not be stored after this call completes and so any subsequent // calls for which the dataset size is less than // binningThreshholdSizeBytes, the dataset will be sorted from // scratch. Values which are not included due to non-unity strides, are not // included in any specified ranges, are masked, or have associated weights // of zero are not considered as dataset members for quantile computations. // If one has a priori information regarding the number of points (npts) // and/or the minimum and maximum values of the data set, these can be // supplied to improve performance. Note however, that if these values are // not correct, the resulting median and/or quantile values will also not be // correct (although see the following notes regarding max/min). Note that // if this object has already had getStatistics() called, and the min and // max were calculated, there is no need to pass these values in as they // have been stored internally and used (although passing them in shouldn't // hurt anything). If provided, npts, the number of points falling in the // specified ranges which are not masked and have weights > 0, should be // exactly correct. min can be less than the true minimum, and // max can be greater than the True maximum, but for best // performance, these should be as close to the actual min and max as // possible. In order for quantile computations to occur over multiple // datasets, all datasets must be available. This means that if // setCalculateAsAdded() was previously called by passing in a value of // True, these methods will throw an exception as the previous call // indicates that there is no guarantee that all datasets will be available. // If one uses a data provider (by having called setDataProvider()), then // this should not be an issue. // get the median of the distribution. // For a dataset with an odd number of good points, the median is just the // value at index int(N/2) in the equivalent sorted dataset, where N is the // number of points. For a dataset with an even number of points, the median // is the mean of the values at indices int(N/2)-1 and int(N/2) in the // sorted dataset. virtual AccumType getMedian( std::shared_ptr knownNpts=nullptr, std::shared_ptr knownMin=nullptr, std::shared_ptr knownMax=nullptr, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt nBins=10000 ); // get the median of the absolute deviation about the median of the data. virtual AccumType getMedianAbsDevMed( std::shared_ptr knownNpts=nullptr, std::shared_ptr knownMin=nullptr, std::shared_ptr knownMax=nullptr, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt nBins=10000 ); // If one needs to compute both the median and quantile values, it is better // to call getMedianAndQuantiles() rather than getMedian() and // getQuantiles() separately, as the first will scan large data sets fewer // times than calling the seperate methods. The return value is the median; // the quantiles are returned in the quantileToValue map. virtual AccumType getMedianAndQuantiles( std::map& quantileToValue, const std::set& quantiles, std::shared_ptr knownNpts=nullptr, std::shared_ptr knownMin=nullptr, std::shared_ptr knownMax=nullptr, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt nBins=10000 ); // Get the specified quantiles. quantiles must be between 0 and // 1, noninclusive. virtual std::map getQuantiles( const std::set& quantiles, std::shared_ptr knownNpts=nullptr, std::shared_ptr knownMin=nullptr, std::shared_ptr knownMax=NULL, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt nBins=10000 ); // // get the min and max of the data set virtual void getMinMax(AccumType& mymin, AccumType& mymax); // scan the dataset(s) that have been added, and find the number of good // points. This method may be called even if setStatsToCaclulate has been // called and NPTS has been excluded. If setCalculateAsAdded(True) has // previously been called after this object has been (re)initialized, an // exception will be thrown. virtual uInt64 getNPts(); // see base class description virtual LocationType getStatisticIndex(StatisticsData::STATS stat); // reset object to initial state. Clears all private fields including data, // accumulators, global range. virtual void reset(); protected: // Concrete derived classes are responsible for providing an appropriate // QuantileComputer object to the constructor, which is ultimately passed // up the instantiation hierarchy and stored at the StatisticsAlgorithm // level. ConstrainedRangeStatistics( std::shared_ptr> qc ); // copy semantics ConstrainedRangeStatistics( const ConstrainedRangeStatistics& other ); // copy semantics ConstrainedRangeStatistics& operator=( const ConstrainedRangeStatistics& other ); // // scan through the data set to determine the number of good (unmasked, // weight > 0, within range) points. The first with no mask, no ranges, and // no weights is trivial with npts = nr in this class, but is implemented // here so that derived classes may override it. virtual void _accumNpts( uInt64& npts, const DataIterator& dataStart, uInt64 nr, uInt dataStride ) const; virtual void _accumNpts( uInt64& npts, const DataIterator& dataStart, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _accumNpts( uInt64& npts, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; virtual void _accumNpts( uInt64& npts, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) const; virtual void _accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; // virtual AccumType _getStatistic(StatisticsData::STATS stat); virtual StatsData _getStatistics(); // virtual void _minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ) const; virtual void _minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; virtual void _minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) const; virtual void _minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; // // // Sometimes we want the min, max, and npts all in one scan. virtual void _minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ) const; virtual void _minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; virtual void _minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) const; virtual void _minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; // // This method is purposefully non-virtual. Derived classes // should implement the version with no parameters. void _setRange(std::shared_ptr> r); // derived classes need to implement how to set their respective range virtual void _setRange() = 0; // // no weights, no mask, no ranges virtual void _unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ); // no weights, no mask virtual void _unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ); virtual void _unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ); virtual void _unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ); // // // has weights, but no mask, no ranges virtual void _weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ); virtual void _weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ); virtual void _weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ); virtual void _weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ); // private: std::shared_ptr> _range{}; }; } #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif #endif casacore-3.7.1/scimath/StatsFramework/ConstrainedRangeStatistics.tcc000066400000000000000000000701461476623553700257340ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_CONSTRAINEDRANGESTATISTICS_TCC #define SCIMATH_CONSTRAINEDRANGESTATISTICS_TCC #include #include namespace casacore { CASA_STATD ConstrainedRangeStatistics::ConstrainedRangeStatistics( std::shared_ptr> qc ) : ClassicalStatistics(qc) { reset(); } CASA_STATD ConstrainedRangeStatistics::ConstrainedRangeStatistics( const ConstrainedRangeStatistics& other ) : ClassicalStatistics(other), _range(other._range) {} CASA_STATD ConstrainedRangeStatistics::~ConstrainedRangeStatistics() {} CASA_STATD ConstrainedRangeStatistics& ConstrainedRangeStatistics::operator=( const ConstrainedRangeStatistics& other ) { if (this == &other) { return *this; } ClassicalStatistics::operator=(other); _range = other._range; return *this; } CASA_STATD AccumType ConstrainedRangeStatistics::getMedian( std::shared_ptr knownNpts, std::shared_ptr knownMin, std::shared_ptr knownMax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt nBins ) { if (! this->_getStatsData().median) { _setRange(); std::shared_ptr median (new AccumType( ClassicalStatistics::getMedian( knownNpts, knownMin, knownMax, binningThreshholdSizeBytes, persistSortedArray, nBins ) )); this->_getStatsData().median = median; this->_getQuantileComputer()->setMedian(median); } return *this->_getStatsData().median; } CASA_STATD AccumType ConstrainedRangeStatistics::getMedianAbsDevMed( std::shared_ptr knownNpts, std::shared_ptr knownMin, std::shared_ptr knownMax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt nBins ) { _setRange(); return ClassicalStatistics::getMedianAbsDevMed( knownNpts, knownMin, knownMax, binningThreshholdSizeBytes, persistSortedArray, nBins ); } CASA_STATD AccumType ConstrainedRangeStatistics::getMedianAndQuantiles( std::map& quantileToValue, const std::set& quantiles, std::shared_ptr knownNpts, std::shared_ptr knownMin, std::shared_ptr knownMax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt nBins ) { _setRange(); return ClassicalStatistics::getMedianAndQuantiles( quantileToValue, quantiles, knownNpts, knownMin, knownMax, binningThreshholdSizeBytes, persistSortedArray, nBins ); } CASA_STATD void ConstrainedRangeStatistics::getMinMax( AccumType& mymin, AccumType& mymax ) { _setRange(); ClassicalStatistics::getMinMax(mymin, mymax); } CASA_STATD uInt64 ConstrainedRangeStatistics::getNPts() { _setRange(); return ClassicalStatistics::getNPts(); } CASA_STATD std::map ConstrainedRangeStatistics::getQuantiles( const std::set& quantiles, std::shared_ptr knownNpts, std::shared_ptr knownMin, std::shared_ptr knownMax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt nBins ) { _setRange(); return ClassicalStatistics::getQuantiles( quantiles, knownNpts, knownMin, knownMax, binningThreshholdSizeBytes, persistSortedArray, nBins ); } CASA_STATD LocationType ConstrainedRangeStatistics::getStatisticIndex( StatisticsData::STATS stat ) { _setRange(); return ClassicalStatistics::getStatisticIndex(stat); } CASA_STATD void ConstrainedRangeStatistics::reset() { _range = nullptr; ClassicalStatistics::reset(); } CASA_STATD void ConstrainedRangeStatistics::_accumNpts( uInt64& npts, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ) const { auto datum = dataBegin; uInt64 count = 0; while (count < nr) { if (*datum >= _range->first && *datum <= _range->second) { ++npts; } StatisticsIncrementer::increment(datum, count, dataStride); } } CASA_STATD void ConstrainedRangeStatistics::_accumNpts( uInt64& npts, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { auto datum = dataBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *datum >= _range->first && *datum <= _range->second && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { ++npts; } StatisticsIncrementer::increment(datum, count, dataStride); } } CASA_STATD void ConstrainedRangeStatistics::_accumNpts( uInt64& npts, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { auto datum = dataBegin; auto mask = maskBegin; uInt64 count = 0; while (count < nr) { if (*mask && *datum >= _range->first && *datum <= _range->second) { ++npts; } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ConstrainedRangeStatistics::_accumNpts( uInt64& npts, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { auto datum = dataBegin; auto mask = maskBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *mask && *datum >= _range->first && *datum <= _range->second && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { ++npts; } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ConstrainedRangeStatistics::_accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) const { auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; while (count < nr) { if ( *datum >= _range->first && *datum <= _range->second && *weight > 0 ) { ++npts; } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ConstrainedRangeStatistics::_accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *datum >= _range->first && *datum <= _range->second && *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { ++npts; } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ConstrainedRangeStatistics::_accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { auto datum = dataBegin; auto weight = weightsBegin; auto mask = maskBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *mask && *datum >= _range->first && *datum <= _range->second && *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { ++npts; } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } CASA_STATD void ConstrainedRangeStatistics::_accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { auto datum = dataBegin; auto weight = weightsBegin; auto mask = maskBegin; uInt64 count = 0; while (count < nr) { if ( *mask && *datum >= _range->first && *datum <= _range->second && *weight > 0 ) { ++npts; } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } // non-virtual version of method CASA_STATD void ConstrainedRangeStatistics::_setRange( std::shared_ptr> r ) { this->_clearStats(); _range = r; ( (ConstrainedRangeQuantileComputer*)( this->_getQuantileComputer().get() ) )->setRange(*r); } CASA_STATD AccumType ConstrainedRangeStatistics::_getStatistic( StatisticsData::STATS stat ) { _setRange(); return ClassicalStatistics::_getStatistic(stat); } CASA_STATD StatsData ConstrainedRangeStatistics::_getStatistics() { _setRange(); return ClassicalStatistics::_getStatistics(); } #define _minMaxCodeCR \ if (*datum >= _range->first && *datum <= _range->second) { \ _minMaxCode \ } CASA_STATD void ConstrainedRangeStatistics::_minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ) const { auto datum = dataBegin; uInt64 count = 0; while (count < nr) { _minMaxCodeCR StatisticsIncrementer::increment(datum, count, dataStride); } } CASA_STATD void ConstrainedRangeStatistics::_minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { auto datum = dataBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _minMaxCodeCR } StatisticsIncrementer::increment(datum, count, dataStride); } } CASA_STATD void ConstrainedRangeStatistics::_minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { auto datum = dataBegin; auto mask = maskBegin; uInt64 count = 0; while (count < nr) { if (*mask) { _minMaxCodeCR } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ConstrainedRangeStatistics::_minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { auto datum = dataBegin; auto mask = maskBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *mask && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _minMaxCodeCR } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ConstrainedRangeStatistics::_minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) const { auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; while (count < nr) { if (*weight > 0) { _minMaxCodeCR } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ConstrainedRangeStatistics::_minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _minMaxCodeCR } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ConstrainedRangeStatistics::_minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { auto datum = dataBegin; auto weight = weightsBegin; auto mask = maskBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *mask && *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _minMaxCodeCR } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } CASA_STATD void ConstrainedRangeStatistics::_minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { auto datum = dataBegin; auto weight = weightsBegin; auto mask = maskBegin; uInt64 count = 0; while (count < nr) { if (*mask && *weight > 0) { _minMaxCodeCR } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } #define _minMaxNptsCodeCR \ if (*datum >= _range->first && *datum <= _range->second) { \ _minMaxNptsCode \ } CASA_STATD void ConstrainedRangeStatistics::_minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ) const { auto datum = dataBegin; uInt64 count = 0; while (count < nr) { _minMaxNptsCodeCR StatisticsIncrementer::increment( datum, count, dataStride ); } } CASA_STATD void ConstrainedRangeStatistics::_minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { auto datum = dataBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _minMaxNptsCodeCR } StatisticsIncrementer::increment( datum, count, dataStride ); } } CASA_STATD void ConstrainedRangeStatistics::_minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { auto datum = dataBegin; auto mask = maskBegin; uInt64 count = 0; while (count < nr) { if (*mask) { _minMaxNptsCodeCR } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ConstrainedRangeStatistics::_minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { auto datum = dataBegin; auto mask = maskBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *mask && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _minMaxNptsCodeCR } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); } } CASA_STATD void ConstrainedRangeStatistics::_minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) const { auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; while (count < nr) { if (*weight > 0) { _minMaxNptsCodeCR } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ConstrainedRangeStatistics::_minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _minMaxNptsCodeCR } StatisticsIncrementer::increment( datum, count, weight, dataStride ); } } CASA_STATD void ConstrainedRangeStatistics::_minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { auto datum = dataBegin; auto weight = weightsBegin; auto mask = maskBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *mask && *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _minMaxNptsCodeCR } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } CASA_STATD void ConstrainedRangeStatistics::_minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { auto datum = dataBegin; auto weight = weightsBegin; auto mask = maskBegin; uInt64 count = 0; while (count < nr) { if (*mask && *weight > 0) { _minMaxNptsCodeCR } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); } } #define _unweightedStatsCodeCR \ if (*datum >= _range->first && *datum <= _range->second) { \ this->_accumulate(stats, *datum, location); \ ++ngood; \ } CASA_STATD void ConstrainedRangeStatistics::_unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ) { auto datum = dataBegin; uInt64 count = 0; while (count < nr) { _unweightedStatsCodeCR StatisticsIncrementer::increment(datum, count, dataStride); location.second += dataStride; } } CASA_STATD void ConstrainedRangeStatistics::_unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) { auto datum = dataBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _unweightedStatsCodeCR } StatisticsIncrementer::increment(datum, count, dataStride); location.second += dataStride; } } CASA_STATD void ConstrainedRangeStatistics::_unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) { auto datum = dataBegin; auto mask = maskBegin; uInt64 count = 0; while (count < nr) { if (*mask) { _unweightedStatsCodeCR } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); location.second += dataStride; } } CASA_STATD void ConstrainedRangeStatistics::_unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) { auto datum = dataBegin; auto mask = maskBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *mask && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _unweightedStatsCodeCR } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); location.second += dataStride; } } // use #define to ensure code is compiled inline #define _weightedStatsCodeCR \ if (*datum >= _range->first && *datum <= _range->second) { \ this->_accumulate(stats, *datum, *weight, location); \ } CASA_STATD void ConstrainedRangeStatistics::_weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) { auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; while (count < nr) { if (*weight > 0) { _weightedStatsCodeCR } StatisticsIncrementer::increment( datum, count, weight, dataStride ); location.second += dataStride; } } CASA_STATD void ConstrainedRangeStatistics::_weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) { auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _weightedStatsCodeCR } StatisticsIncrementer::increment( datum, count, weight, dataStride ); location.second += dataStride; } } CASA_STATD void ConstrainedRangeStatistics::_weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) { auto datum = dataBegin; auto weight = weightsBegin; auto mask = maskBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *mask && *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _weightedStatsCodeCR } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); location.second += dataStride; } } CASA_STATD void ConstrainedRangeStatistics::_weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) { auto datum = dataBegin; auto weight = weightsBegin; auto mask = maskBegin; uInt64 count = 0; while (count < nr) { if (*mask && *weight > 0) { _weightedStatsCodeCR } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); location.second += dataStride; } } } #endif casacore-3.7.1/scimath/StatsFramework/FitToHalfStatistics.h000066400000000000000000000276141476623553700240060ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_FITTOHALFSTATISTICS_H #define SCIMATH_FITTOHALFSTATISTICS_H #include #include #include namespace casacore { // Class to calculate statistics using the so-called fit to half algorithm. In // this algorithm, a center value is specified, and only points greater or equal // or less or equal this value are included. Furthermore, each of the included // points is reflected about the center value, and these virtual points are // added to the included points and the union of sets of included real points // and virtual points are used for computing statistics. The specified center // point is therefore the mean and median of the resulting distribution, and the // total number of points is exactly twice the number of real data points that // are included. // // This class uses a ConstrainedRangeQuantileComputer object for computing // quantile-like statistics. See class documentation for StatisticsAlgorithm for // details regarding QuantileComputer classes. template < class AccumType, class DataIterator, class MaskIterator=const Bool *, class WeightsIterator=DataIterator > class FitToHalfStatistics : public ConstrainedRangeStatistics { public: const static AccumType TWO; // value is only used if center=CVALUE FitToHalfStatistics( FitToHalfStatisticsData::CENTER center=FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::USE_DATA useData =FitToHalfStatisticsData::LE_CENTER, AccumType value=0 ); // copy semantics FitToHalfStatistics(const FitToHalfStatistics& other); virtual ~FitToHalfStatistics(); // copy semantics FitToHalfStatistics& operator=( const FitToHalfStatistics& other ); // Clone this instance. Caller is responsible for deleting the returned // pointer. virtual StatisticsAlgorithm* clone() const; // get the algorithm that this object uses for computing stats virtual StatisticsData::ALGORITHM algorithm() const { return StatisticsData::FITTOHALF; }; // The median is just the center value, so none of the parameters to this // method are used. AccumType getMedian( std::shared_ptr knownNpts=nullptr, std::shared_ptr knownMin=nullptr, std::shared_ptr knownMax=nullptr, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt nBins=10000 ); // // In the following group of methods, if the size of the composite dataset // is smaller than binningThreshholdSizeBytes, the composite // dataset will be (perhaps partially) sorted and persisted in memory during // the call. In that case, and if persistSortedArray is True, // this sorted array will remain in memory after the call and will be used // on subsequent calls of this method when // binningThreshholdSizeBytes is greater than the size of the // composite dataset. If persistSortedArray is False, the sorted // array will not be stored after this call completes and so any subsequent // calls for which the dataset size is less than // binningThreshholdSizeBytes, the dataset will be sorted from // scratch. Values which are not included due to non-unity strides, are not // included in any specified ranges, are masked, or have associated weights // of zero are not considered as dataset members for quantile computations. // If one has a priori information regarding the number of points (npts) // and/or the minimum and maximum values of the data set, these can be // supplied to improve performance. Note however, that if these values are // not correct, the resulting median and/or quantile values will also not be // correct (although see the following notes regarding max/min). Note that // if this object has already had getStatistics() called, and the min and // max were calculated, there is no need to pass these values in as they // have been stored internally and will be used (although passing them // explicitly shouldn't hurt anything). If provided, npts, the number of // points falling in the specified ranges which are not masked and have // weights > 0, should be correct. min can be less than the true // minimum, and max can be greater than the True maximum, but for // best performance, these should be as close to the actual min and max as // possible (and ideally the actual min/max values of the data set). AccumType getMedianAndQuantiles( std::map& quantiles, const std::set& fractions, std::shared_ptr knownNpts=nullptr, std::shared_ptr knownMin=nullptr, std::shared_ptr knownMax=nullptr, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt nBins=10000 ); // get the median of the absolute deviation about the median of the data. AccumType getMedianAbsDevMed( std::shared_ptr knownNpts=nullptr, std::shared_ptr knownMin=nullptr, std::shared_ptr knownMax=nullptr, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt nBins=10000 ); // Get the specified quantiles. fractions must be between 0 and // 1, noninclusive. std::map getQuantiles( const std::set& fractions, std::shared_ptr knownNpts=nullptr, std::shared_ptr knownMin=nullptr, std::shared_ptr knownMax=nullptr, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt nBins=10000 ); // // scan the dataset(s) that have been added, and find the min and max. // This method may be called even if setStatsToCaclulate has been called and // MAX and MIN has been excluded. virtual void getMinMax(AccumType& mymin, AccumType& mymax); // scan the dataset(s) that have been added, and find the number of good // points. This method may be called even if setStatsToCaclulate has been // called and NPTS has been excluded. If setCalculateAsAdded(True) has // previously been called after this object has been (re)initialized, an // exception will be thrown. uInt64 getNPts(); // reset object to initial state. Clears all private fields including data, // accumulators, global range. It does not affect the center type, center // value, or which "side" to use which were set at construction. virtual void reset(); // This class does not allow statistics to be calculated as datasets are // added, so an exception will be thrown if c is True. void setCalculateAsAdded(Bool c); // Override base class method by requiring mean to be computed in addition // to what is added in stats if the requested center value is CMEAN. virtual void setStatsToCalculate(std::set& stats); protected: virtual StatsData _getInitialStats() const; virtual StatsData _getStatistics(); inline StatsData& _getStatsData() { return _statsData; } inline const StatsData& _getStatsData() const { return _statsData; } // // no weights, no mask, no ranges void _unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ); // no weights, no mask void _unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ); void _unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ); void _unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ); // void _updateDataProviderMaxMin(const StatsData& threadStats); // // has weights, but no mask, no ranges void _weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ); void _weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ); void _weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ); void _weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ); // private: FitToHalfStatisticsData::CENTER _centerType; Bool _useLower; AccumType _centerValue; StatsData _statsData; Bool _doMedAbsDevMed{False}, _rangeIsSet{False}; // these are the max and min for the real portion of the dataset std::shared_ptr _realMax{}, _realMin{}; Bool _isNullSet{False}; // This is used for convenience and performance. It should always // be the same as the _range value used in the base // ConstrainedRangeStatistics object std::shared_ptr> _range; // get the min max of the entire (real + virtual) data set. Only // used for quantile computation void _getMinMax( std::shared_ptr& realMin, std::shared_ptr& realMax, std::shared_ptr knownMin, std::shared_ptr knownMax ); // get the min/max of the real portion only of the dataset void _getRealMinMax(AccumType& realMin, AccumType& realMax); void _setRange(); }; } #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif #endif casacore-3.7.1/scimath/StatsFramework/FitToHalfStatistics.tcc000066400000000000000000000641361476623553700243300ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_FITTOHALFSTATISTICS_TCC #define SCIMATH_FITTOHALFSTATISTICS_TCC #include #include #include namespace casacore { CASA_STATD const AccumType FitToHalfStatistics::TWO = AccumType(2); // min > max indicates that these quantities have not be calculated CASA_STATD FitToHalfStatistics::FitToHalfStatistics( FitToHalfStatisticsData::CENTER centerType, FitToHalfStatisticsData::USE_DATA useData, AccumType centerValue ) : ConstrainedRangeStatistics( std::shared_ptr>( new ConstrainedRangeQuantileComputer( &this->_getDataset()) ) ), _centerType(centerType), _useLower(useData == FitToHalfStatisticsData::LE_CENTER), _centerValue(centerValue), _statsData(initializeStatsData()) { reset(); } CASA_STATD FitToHalfStatistics::FitToHalfStatistics( const FitToHalfStatistics& other ) : ConstrainedRangeStatistics(other), _centerType(other._centerType), _useLower(other._useLower), _centerValue(other._centerValue), _statsData(copy(other._statsData)), _doMedAbsDevMed(other._doMedAbsDevMed), _rangeIsSet(other._rangeIsSet), _realMax(other._realMax ? new AccumType(*other._realMax) : nullptr), _realMin(other._realMin ? new AccumType(*other._realMin) : nullptr), _isNullSet(False), _range(other._range) {} CASA_STATD FitToHalfStatistics::~FitToHalfStatistics() {} CASA_STATD FitToHalfStatistics& FitToHalfStatistics::operator=( const FitToHalfStatistics& other ) { if (this == &other) { return *this; } ConstrainedRangeStatistics::operator=(other); _centerType = other._centerType; _useLower = other._useLower; _centerValue = other._centerValue; _statsData = copy(other._statsData); _doMedAbsDevMed = other._doMedAbsDevMed; _rangeIsSet = other._rangeIsSet; _realMax.reset (other._realMax ? new AccumType(*other._realMax) : nullptr); _realMin.reset (other._realMin ? new AccumType(*other._realMin) : nullptr); _isNullSet = other._isNullSet; _range = other._range; return *this; } CASA_STATD StatisticsAlgorithm* FitToHalfStatistics::clone() const { return new FitToHalfStatistics(*this); } CASA_STATD AccumType FitToHalfStatistics::getMedian( std::shared_ptr , std::shared_ptr , std::shared_ptr , uInt , Bool , uInt ) { auto median = _getStatsData().median; if (! median) { median.reset (new AccumType(_centerValue)); _getStatsData().median = median; this->_getQuantileComputer()->setMedian(median); } return *median; } CASA_STATD AccumType FitToHalfStatistics::getMedianAndQuantiles( std::map& quantileToValue, const std::set& quantiles, std::shared_ptr knownNpts, std::shared_ptr knownMin, std::shared_ptr knownMax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt nBins ) { // The median is trivial, we just need to compute the quantiles quantileToValue = getQuantiles( quantiles, knownNpts, knownMin, knownMax, binningThreshholdSizeBytes, persistSortedArray, nBins ); return getMedian(); } CASA_STATD AccumType FitToHalfStatistics::getMedianAbsDevMed( std::shared_ptr knownNpts, std::shared_ptr knownMin, std::shared_ptr knownMax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt nBins ) { if (! _getStatsData().medAbsDevMed) { _setRange(); ThrowIf( _isNullSet, "No data included using current configuration, " "cannot compute medianabsdevmed" ); // The number of points to hand to the base class is the number of real // data points, or exactly half of the total number of points std::shared_ptr realNPts( new uInt64(knownNpts ? *knownNpts/2 : getNPts()/2) ); std::shared_ptr realMin, realMax; // need to set the median in the quantile computer object here. The // getMedian() call will do that, so we don't need to capture the return // value getMedian(); _getStatsData().medAbsDevMed.reset (new AccumType( ConstrainedRangeStatistics::getMedianAbsDevMed( realNPts, knownMin, knownMax, binningThreshholdSizeBytes, persistSortedArray, nBins ) )); } return *_getStatsData().medAbsDevMed; } CASA_STATD void FitToHalfStatistics::_getMinMax( std::shared_ptr& realMin, std::shared_ptr& realMax, std::shared_ptr knownMin, std::shared_ptr knownMax ) { realMin.reset (new AccumType(_centerValue)); realMax.reset (new AccumType(_centerValue)); AccumType mymin, mymax; if (!knownMin || !knownMax) { getMinMax(mymin, mymax); } else { mymin = *knownMin; mymax = *knownMax; } if (_useLower) { realMin.reset (new AccumType(mymin)); } else { realMax.reset (new AccumType(mymax)); } } CASA_STATD void FitToHalfStatistics::getMinMax( AccumType& mymin, AccumType& mymax ) { // do not do a _realMin/Max existence check in the if condition, because if // _getStatsData().min() and .max are not null, forcing this recalculation // will likely give bogus results because _getStatsData().min/max are used // higher up the inheritence chain and will now be set to the full // (real + virtual) dataset min/max, not the real portion only min/max if ( ! _getStatsData().min || ! _getStatsData().max) { _setRange(); ThrowIf( _isNullSet, "No data included using current configuration, " "cannot compute min and max" ); // This call returns the min and max of the real portion of the dataset ConstrainedRangeStatistics::getMinMax(mymin, mymax); // note that _realMin and _realMax are also computed during the // calculation of accumulated statistics, in // _updateDataProviderMaxMin(). if those have been done previously, this // if block won't be entered so they will not be computed again here _realMin.reset (new AccumType(mymin)); _realMax.reset (new AccumType(mymax)); if (_useLower) { mymax = TWO*_centerValue - mymin; } else { mymin = TWO*_centerValue - mymax; } _getStatsData().min.reset (new AccumType(mymin)); _getStatsData().max.reset (new AccumType(mymax)); } else { mymin = *_getStatsData().min; mymax = *_getStatsData().max; } } CASA_STATD void FitToHalfStatistics::_getRealMinMax( AccumType& realMin, AccumType& realMax ) { // if they exist, just return copies of them if (! _realMin || ! _realMax) { // real portion min/max not yet computed, they should be computed in // getMinMax() AccumType mymin, mymax; getMinMax(mymin, mymax); // should always be OK, but just to be sure, check ThrowIf( ! _realMin || ! _realMax, "Logic Error: _realMin/_realMax not computed as they should have " "been, please file a bug report which includes a pointer to the " "dataset you used and your complete inputs" ); } // return copies realMin = *_realMin; realMax = *_realMax; } CASA_STATD std::map FitToHalfStatistics::getQuantiles( const std::set& fractions, std::shared_ptr knownNpts, std::shared_ptr knownMin, std::shared_ptr knownMax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt nBins ) { ThrowIf( *fractions.begin() <= 0 || *fractions.rbegin() >= 1, "Value of all quantiles must be between 0 and 1 (noninclusive)" ); ThrowIf ( knownNpts && ((*knownNpts % 2) != 0), "knownNpts must be even for this class" ); _setRange(); ThrowIf( _isNullSet, "No data included using current configuration, cannot compute quantiles" ); // fractions that exist in the virtual part of the dataset are determined // from the real fractions reflected about the center point. std::set realPortionFractions; //auto fiter = fractions.cbegin(); //auto fend = fractions.cend(); // map the actual (full dataset) fractions to the real portion fractions std::map actualToReal; Double freal = 0; std::map actual; //for ( ; fiter != fend; ++fiter) { for_each( fractions.cbegin(), fractions.cend(), [this, &actual, &knownNpts, &freal, &realPortionFractions, &actualToReal] (Double q) { if (near(q, 0.5)) { AccumType realMin, realMax; _getRealMinMax(realMin, realMax); actual[q] = _useLower ? realMax : TWO*_centerValue - realMin; } else { auto isVirtualQ = (_useLower && q > 0.5) || (! _useLower && q < 0.5); if (isVirtualQ) { // quantile is in virtual part of data set std::set actualF; actualF.insert(q); uInt64 allNPts = knownNpts ? *knownNpts : getNPts(); auto actualFToI = StatisticsData::indicesFromFractions( allNPts, actualF ); auto actualIdx = actualFToI[q]; auto realIdx = _useLower ? allNPts - (actualIdx + 1) : allNPts/2 - (actualIdx + 1); if (_useLower && (realIdx == allNPts/2 - 1)) { // the actual index is the reflection of the maximum // value of the real portion of the dataset AccumType realMin, realMax; _getRealMinMax(realMin, realMax); actual[q] = TWO*_centerValue - realMax; } else if (! _useLower && realIdx == 0) { // the actual index is the reflection of the minimum // value of the real portion of the dataset AccumType realMin, realMax; _getRealMinMax(realMin, realMax); actual[q] = TWO*_centerValue - realMin; } else { freal = Double(realIdx + 1)/Double(allNPts/2); if (freal == 1) { AccumType mymin, mymax; getMinMax(mymin, mymax); actual[q] = mymin; } else { realPortionFractions.insert(freal); actualToReal[q] = freal; } } } else { // quantile is in the real part of the dataset freal = _useLower ? 2*q : 2*(q - 0.5); realPortionFractions.insert(freal); actualToReal[q] = freal; } } }); if (realPortionFractions.empty()) { return actual; } // if given, knownNpts should be the number of points in the full dataset, // or twice the number in the real portion of the dataset. Points in only // the real portion is what scanning will find, so we need to cut the number // of points in half. This is also true if we have to compute using // getNPts(), so we need our own value to pass in to the call of the base // class' method. std::shared_ptr realNPts( new uInt64(knownNpts ? *knownNpts/2 : getNPts()/2) ); std::shared_ptr realMin, realMax; _getMinMax(realMin, realMax, knownMin, knownMax); auto realPart = ConstrainedRangeStatistics::getQuantiles( realPortionFractions, realNPts, realMin, realMax, binningThreshholdSizeBytes, persistSortedArray, nBins ); // fiter = fractions.begin(); // while (fiter != fend) { for_each( fractions.cbegin(), fractions.cend(), [this, &actual, &actualToReal, &realPart](Double q) { if (actual.find(q) == actual.end()) { Double realF = actualToReal[q]; auto actualValue = realPart[realF]; if ((_useLower && q > 0.5) || (! _useLower && q < 0.5)) { // quantile in virtual part of the data set, reflect // corresponding real value to get actual value actualValue = TWO*_centerValue - actualValue; } actual[q] = actualValue; } // ++fiter; }); return actual; } CASA_STATD uInt64 FitToHalfStatistics::getNPts() { if (_getStatsData().npts == 0) { _setRange(); if (_isNullSet) { return 0; } // guard against subsequent calls multiplying by two _getStatsData().npts = 2*ConstrainedRangeStatistics::getNPts(); } return (uInt64)_getStatsData().npts; } CASA_STATD void FitToHalfStatistics::setCalculateAsAdded(Bool c) { ThrowIf( c, "FitToHalfStatistics does not support calculating statistics " "incrementally as data sets are added" ); } CASA_STATD void FitToHalfStatistics::reset() { _doMedAbsDevMed = False; _statsData = initializeStatsData(); _rangeIsSet = False; _realMax.reset(); _realMin.reset(); ConstrainedRangeStatistics::reset(); } CASA_STATD void FitToHalfStatistics::setStatsToCalculate( std::set& stats ) { if (! stats.empty() && _centerType == FitToHalfStatisticsData::CMEAN) { stats.insert(StatisticsData::MEAN); } ConstrainedRangeStatistics::setStatsToCalculate(stats); } CASA_STATD StatsData FitToHalfStatistics::_getInitialStats() const { StatsData stats = initializeStatsData(); stats.mean = _centerValue; return stats; } CASA_STATD StatsData FitToHalfStatistics::_getStatistics() { ConstrainedRangeStatistics::_getStatistics(); StatsData& stats = _getStatsData(); if (stats.npts == 0) { return copy(stats); } stats.sum = stats.mean * stats.sumweights; if (_useLower) { stats.maxpos.first = -1; stats.maxpos.second = -1; stats.max.reset (new AccumType(TWO*_centerValue - *stats.min)); } else { stats.minpos.first = -1; stats.minpos.second = -1; stats.min.reset (new AccumType(TWO*_centerValue - *stats.max)); } return copy(stats); } CASA_STATD void FitToHalfStatistics::_setRange() { if (_rangeIsSet) { return; } ClassicalStatistics cs(*this); // if FitToHalfStatisticsData::CMEDIAN, the quantile computer object in the // cs object will use the ConstrainedRangeQuantile methods, which is not // what we want here. So we have to explicitly ensure that the cs object // uses a bona-fide ClassicalStatisticsQuantileComputer object for // computation of the median. From a pedantic POV, the dataset used should // be the same (as in the same memory address) as in the cs object, but in // this case a copy will suffice and it does not require making // ClassicalStatistics::_getQuantileComputer() public. std::shared_ptr> qComputer( new ClassicalQuantileComputer(&this->_getDataset()) ); cs.setQuantileComputer(qComputer); if ( _centerType == FitToHalfStatisticsData::CMEAN || _centerType == FitToHalfStatisticsData::CMEDIAN ) { _centerValue = _centerType == FitToHalfStatisticsData::CMEAN ? cs.getStatistic(StatisticsData::MEAN) : cs.getMedian(); } _getStatsData().mean = _centerValue; _getStatsData().median.reset (new AccumType(_centerValue)); this->_getQuantileComputer()->setMedian(_getStatsData().median); AccumType mymin, mymax; cs.getMinMax(mymin, mymax); if (_useLower) { _range.reset (new std::pair(mymin, _centerValue)); _isNullSet = mymin > _centerValue; } else { _range.reset (new std::pair(_centerValue, mymax)); _isNullSet = mymax < _centerValue; } // median must be set after _setRange(_range) call, because the _setRange() // call clears stats (and therefore will clear the median if it is set prior // to that call) ConstrainedRangeStatistics::_setRange(_range); this->_getQuantileComputer()->setMedian(_getStatsData().median); _rangeIsSet = True; } // use a define to ensure code is compiled inline #define _unweightedStatsCodeFH \ if (*datum >= _range->first && *datum <= _range->second) { \ StatisticsUtilities::accumulateSym( \ stats.npts, stats.nvariance, stats.sumsq, *stats.min, *stats.max, \ stats.minpos, stats.maxpos, *datum, location, _centerValue \ ); \ ngood += 2; \ } CASA_STATD void FitToHalfStatistics::_unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ) { auto datum = dataBegin; uInt64 count = 0; while (count < nr) { _unweightedStatsCodeFH StatisticsIncrementer::increment(datum, count, dataStride); location.second += dataStride; } } CASA_STATD void FitToHalfStatistics::_unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) { auto datum = dataBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _unweightedStatsCodeFH } StatisticsIncrementer::increment(datum, count, dataStride); location.second += dataStride; } } CASA_STATD void FitToHalfStatistics::_unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) { auto datum = dataBegin; auto mask = maskBegin; uInt64 count = 0; while (count < nr) { if (*mask) { _unweightedStatsCodeFH } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); location.second += dataStride; } } CASA_STATD void FitToHalfStatistics::_unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) { auto datum = dataBegin; auto mask = maskBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *mask && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _unweightedStatsCodeFH } StatisticsIncrementer::increment( datum, count, mask, dataStride, maskStride ); location.second += dataStride; } } CASA_STATD void FitToHalfStatistics::_updateDataProviderMaxMin( const StatsData& threadStats ) { // _realMin and _realMax are updated here during computation of accumulated // stats, even if there isn't a data provider. It is better to do it here // than in the accumulation methods, as the accumulation methods can be // called (and usually are for CASA) in a multi-threaded context. So, the // updates there would have to be put in omp critical blocks, thus impacting // performance. So this isn't necessarily updating the data provider (ie if // one doesn't exist) but it is necessary to do even if there isn't a data // provider in a method that is always called in a single-thread context. StatsDataProvider *dataProvider = this->_getDataset().getDataProvider(); StatsData& stats = _getStatsData(); const Int64 iDataset = this->_getDataset().iDataset(); if ( iDataset == threadStats.maxpos.first && (!stats.max || *threadStats.max > *stats.max) ) { if (!_realMax || *threadStats.max > *_realMax) { _realMax.reset (new AccumType(*threadStats.max)); if (dataProvider && ! _useLower) { dataProvider->updateMaxPos(threadStats.maxpos); } } } if ( iDataset == threadStats.minpos.first && (!stats.min || (*threadStats.min) < (*stats.min)) ) { if (!_realMin || (*threadStats.min) < *_realMin) { _realMin.reset (new AccumType(*threadStats.min)); if (dataProvider && _useLower) { dataProvider->updateMinPos(threadStats.minpos); } } } } // use #define to ensure code is compiled inline #define _weightedStatsCodeFH \ if (*datum >= _range->first && *datum <= _range->second) { \ StatisticsUtilities::waccumulateSym( \ stats.npts, stats.sumweights, stats.nvariance, \ stats.sumsq, *stats.min, *stats.max, stats.minpos, stats.maxpos, \ *datum, *weight, location, _centerValue \ ); \ } CASA_STATD void FitToHalfStatistics::_weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) { auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; while (count < nr) { if (*weight > 0) { _weightedStatsCodeFH } StatisticsIncrementer::increment( datum, count, weight, dataStride ); location.second += dataStride; } } CASA_STATD void FitToHalfStatistics::_weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) { auto datum = dataBegin; auto weight = weightsBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _weightedStatsCodeFH } StatisticsIncrementer::increment( datum, count, weight, dataStride ); location.second += dataStride; } } CASA_STATD void FitToHalfStatistics::_weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) { auto datum = dataBegin; auto weight = weightsBegin; auto mask = maskBegin; uInt64 count = 0; auto beginRange = ranges.cbegin(); auto endRange = ranges.cend(); while (count < nr) { if ( *mask && *weight > 0 && StatisticsUtilities::includeDatum( *datum, beginRange, endRange, isInclude ) ) { _weightedStatsCodeFH } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); location.second += dataStride; } } CASA_STATD void FitToHalfStatistics::_weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) { auto datum = dataBegin; auto weight = weightsBegin; auto mask = maskBegin; uInt64 count = 0; while (count < nr) { if (*mask && *weight > 0) { _weightedStatsCodeFH } StatisticsIncrementer::increment( datum, count, weight, mask, dataStride, maskStride ); location.second += dataStride; } } } #endif casacore-3.7.1/scimath/StatsFramework/FitToHalfStatisticsData.h000066400000000000000000000032661476623553700245750ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_FITTOHALFSTATISTICSDATA_H #define SCIMATH_FITTOHALFSTATISTICSDATA_H #include namespace casacore { // Various data for FitToHalfStatistics class FitToHalfStatisticsData { public: // choice of center point based on the corresponding statistics from the // entire distribution of data, or simply an arbitrary value enum CENTER { CMEAN, CMEDIAN, CVALUE }; // which section of data to use, greater than or less than the center value enum USE_DATA { LE_CENTER, GE_CENTER }; }; } #endif casacore-3.7.1/scimath/StatsFramework/HingesFencesQuantileComputer.h000066400000000000000000000316311476623553700256700ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_HINGESFENCESQUANTILECOMPUTER_H #define SCIMATH_HINGESFENCESQUANTILECOMPUTER_H #include #include #include namespace casacore { // QuantileComputer used by HingesFencesStatistics for computing quantile-like // statistics. API developers should never explicitly instantiate this class. // See class documentation for StatisticsAlgorithm for details regarding // QuantileComputer classes. template < class AccumType, class DataIterator, class MaskIterator=const Bool*, class WeightsIterator=DataIterator > class HingesFencesQuantileComputer : public ConstrainedRangeQuantileComputer { public: HingesFencesQuantileComputer() = delete; HingesFencesQuantileComputer(StatisticsDataset* dataset); // copy semantics HingesFencesQuantileComputer(const HingesFencesQuantileComputer& other); virtual ~HingesFencesQuantileComputer(); // copy semantics HingesFencesQuantileComputer& operator=( const HingesFencesQuantileComputer& other ); // clone this object by returning a pointer to a copy virtual StatisticsAlgorithmQuantileComputer* clone() const; // reset private fields virtual void reset(); void setHasRange(Bool hr) { _hasRange = hr; } protected: // virtual void _findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const std::vector>& binDesc, const std::vector& maxLimit ) const; virtual void _findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const std::vector>& binDesc, const std::vector& maxLimit ) const; virtual void _findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const std::vector>& binDesc, const std::vector& maxLimit ) const; virtual void _findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const std::vector>& binDesc, const std::vector& maxLimit ) const; virtual void _findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const std::vector>& binDesc, const std::vector& maxLimit ) const ; virtual void _findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const std::vector>& binDesc, const std::vector& maxLimit ) const; virtual void _findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const std::vector>& binDesc, const std::vector& maxLimit ) const; virtual void _findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const std::vector>& binDesc, const std::vector& maxLimit ) const; // // // populate an unsorted array with valid data. If includeLimits // is defined, then restrict values that are entered in the array to those // limits (inclusive of the minimum, exclusive of the maximum). // maxCount and currentCount are used only if // includeLimits is defined. In this case, the method will return // when currentCount == maxCount, thus avoiding scanning remaining data // unnecessarily. // no weights, no mask, no ranges virtual void _populateArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ) const; // ranges virtual void _populateArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _populateArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; // mask and ranges virtual void _populateArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; // weights virtual void _populateArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) const; // weights and ranges virtual void _populateArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; // weights and mask virtual void _populateArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; // weights, mask, ranges virtual void _populateArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; // no weights, no mask, no ranges virtual void _populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const IncludeLimits& includeLimits, uInt64 maxCount ) const; // ranges virtual void _populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const IncludeLimits& includeLimits, uInt64 maxCount ) const; virtual void _populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const IncludeLimits& includeLimits, uInt64 maxCount ) const; // mask and ranges virtual void _populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const IncludeLimits& includeLimits, uInt64 maxCount ) const; // weights virtual void _populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const IncludeLimits& includeLimits, uInt64 maxCount ) const; // weights and ranges virtual void _populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const IncludeLimits& includeLimits, uInt64 maxCount ) const; // weights and mask virtual void _populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const IncludeLimits& includeLimits, uInt64 maxCount ) const; // weights, mask, ranges virtual void _populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const IncludeLimits& includeLimits, uInt64 maxCount ) const; // // // no weights, no mask, no ranges virtual Bool _populateTestArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, uInt maxElements ) const; // ranges virtual Bool _populateTestArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const; // mask virtual Bool _populateTestArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, uInt maxElements ) const; // mask and ranges virtual Bool _populateTestArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const; // weights virtual Bool _populateTestArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, uInt maxElements ) const; // weights and ranges virtual Bool _populateTestArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const; // weights and mask virtual Bool _populateTestArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, uInt maxElements ) const; // weights, mask, ranges virtual Bool _populateTestArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const; // private: Bool _hasRange{False}; }; } #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif #endif casacore-3.7.1/scimath/StatsFramework/HingesFencesQuantileComputer.tcc000066400000000000000000000622741476623553700262210ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_HINGESFENCESQUANTILECOMPUTER_TCC #define SCIMATH_HINGESFENCESQUANTILECOMPUTER_TCC #include namespace casacore { CASA_STATD HingesFencesQuantileComputer::HingesFencesQuantileComputer( StatisticsDataset* dataset ) : ConstrainedRangeQuantileComputer(dataset) {} CASA_STATD HingesFencesQuantileComputer::HingesFencesQuantileComputer( const HingesFencesQuantileComputer& other ) : ConstrainedRangeQuantileComputer(other), _hasRange(other._hasRange) {} CASA_STATD HingesFencesQuantileComputer::~HingesFencesQuantileComputer() {} CASA_STATD HingesFencesQuantileComputer& HingesFencesQuantileComputer::operator=( const HingesFencesQuantileComputer& other ) { if (this == &other) { return *this; } ConstrainedRangeQuantileComputer::operator=(other); _hasRange = other._hasRange; return *this; } CASA_STATD StatisticsAlgorithmQuantileComputer* HingesFencesQuantileComputer::clone() const { return new HingesFencesQuantileComputer(*this); } CASA_STATD void HingesFencesQuantileComputer::reset() { ConstrainedRangeQuantileComputer::reset(); _hasRange = False; } CASA_STATD void HingesFencesQuantileComputer::_findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const std::vector>& binDesc, const std::vector& maxLimit ) const { if (_hasRange) { ConstrainedRangeQuantileComputer::_findBins( binCounts, sameVal, allSame, dataBegin, nr, dataStride, binDesc, maxLimit ); } else { ClassicalQuantileComputer::_findBins( binCounts, sameVal, allSame, dataBegin, nr, dataStride, binDesc, maxLimit ); } } CASA_STATD void HingesFencesQuantileComputer::_findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const std::vector>& binDesc, const std::vector& maxLimit ) const { if (_hasRange) { ConstrainedRangeQuantileComputer::_findBins( binCounts, sameVal, allSame, dataBegin, nr, dataStride, ranges, isInclude, binDesc, maxLimit ); } else { ClassicalQuantileComputer::_findBins( binCounts, sameVal, allSame, dataBegin, nr, dataStride, ranges, isInclude, binDesc, maxLimit ); } } CASA_STATD void HingesFencesQuantileComputer::_findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const std::vector>& binDesc, const std::vector& maxLimit ) const { if (_hasRange) { ConstrainedRangeQuantileComputer::_findBins( binCounts, sameVal, allSame, dataBegin, nr, dataStride, maskBegin, maskStride, binDesc, maxLimit ); } else { ClassicalQuantileComputer::_findBins( binCounts, sameVal, allSame, dataBegin, nr, dataStride, maskBegin, maskStride, binDesc, maxLimit ); } } CASA_STATD void HingesFencesQuantileComputer::_findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const std::vector>& binDesc, const std::vector& maxLimit ) const { if (_hasRange) { ConstrainedRangeQuantileComputer::_findBins( binCounts, sameVal, allSame, dataBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude, binDesc, maxLimit ); } else { ClassicalQuantileComputer::_findBins( binCounts, sameVal, allSame, dataBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude, binDesc, maxLimit ); } } CASA_STATD void HingesFencesQuantileComputer::_findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const std::vector>& binDesc, const std::vector& maxLimit ) const { if (_hasRange) { ConstrainedRangeQuantileComputer::_findBins( binCounts, sameVal, allSame, dataBegin, weightsBegin, nr, dataStride, binDesc, maxLimit ); } else { ClassicalQuantileComputer::_findBins( binCounts, sameVal, allSame, dataBegin, weightsBegin, nr, dataStride, binDesc, maxLimit ); } } CASA_STATD void HingesFencesQuantileComputer::_findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const std::vector>& binDesc, const std::vector& maxLimit ) const { if (_hasRange) { ConstrainedRangeQuantileComputer::_findBins( binCounts, sameVal, allSame, dataBegin, weightsBegin, nr, dataStride, ranges, isInclude, binDesc, maxLimit ); } else { ClassicalQuantileComputer::_findBins( binCounts, sameVal, allSame, dataBegin, weightsBegin, nr, dataStride, ranges, isInclude, binDesc, maxLimit ); } } CASA_STATD void HingesFencesQuantileComputer::_findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const std::vector>& binDesc, const std::vector& maxLimit ) const { if (_hasRange) { ConstrainedRangeQuantileComputer::_findBins( binCounts, sameVal, allSame, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude, binDesc, maxLimit ); } else { ClassicalQuantileComputer::_findBins( binCounts, sameVal, allSame, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude, binDesc, maxLimit ); } } CASA_STATD void HingesFencesQuantileComputer::_findBins( std::vector& binCounts, std::vector>& sameVal, std::vector& allSame, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const std::vector>& binDesc, const std::vector& maxLimit ) const { if (_hasRange) { ConstrainedRangeQuantileComputer::_findBins( binCounts, sameVal, allSame, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, binDesc, maxLimit ); } else { ClassicalQuantileComputer::_findBins( binCounts, sameVal, allSame, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, binDesc, maxLimit ); } } CASA_STATD void HingesFencesQuantileComputer::_populateArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ) const { if (_hasRange) { ConstrainedRangeQuantileComputer::_populateArray( ary, dataBegin, nr, dataStride ); } else { ClassicalQuantileComputer::_populateArray( ary, dataBegin, nr, dataStride ); } } CASA_STATD void HingesFencesQuantileComputer::_populateArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { if (_hasRange) { ConstrainedRangeQuantileComputer::_populateArray( ary, dataBegin, nr, dataStride, ranges, isInclude ); } else { ClassicalQuantileComputer::_populateArray( ary, dataBegin, nr, dataStride, ranges, isInclude ); } } CASA_STATD void HingesFencesQuantileComputer::_populateArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { if (_hasRange) { ConstrainedRangeQuantileComputer::_populateArray( ary, dataBegin, nr, dataStride, maskBegin, maskStride ); } else { ClassicalQuantileComputer::_populateArray( ary, dataBegin, nr, dataStride, maskBegin, maskStride ); } } CASA_STATD void HingesFencesQuantileComputer::_populateArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { if (_hasRange) { ConstrainedRangeQuantileComputer::_populateArray( ary, dataBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude ); } else { ClassicalQuantileComputer::_populateArray( ary, dataBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude ); } } CASA_STATD void HingesFencesQuantileComputer::_populateArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) const { if (_hasRange) { ConstrainedRangeQuantileComputer::_populateArray( ary, dataBegin, weightsBegin, nr, dataStride ); } else { ClassicalQuantileComputer::_populateArray( ary, dataBegin, weightsBegin, nr, dataStride ); } } CASA_STATD void HingesFencesQuantileComputer::_populateArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { if (_hasRange) { ConstrainedRangeQuantileComputer::_populateArray( ary, dataBegin, weightsBegin, nr, dataStride, ranges, isInclude ); } else { ClassicalQuantileComputer::_populateArray( ary, dataBegin, weightsBegin, nr, dataStride, ranges, isInclude ); } } CASA_STATD void HingesFencesQuantileComputer::_populateArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { if (_hasRange) { ConstrainedRangeQuantileComputer::_populateArray( ary, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride ); } else { ClassicalQuantileComputer::_populateArray( ary, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride ); } } CASA_STATD void HingesFencesQuantileComputer::_populateArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { if (_hasRange) { ConstrainedRangeQuantileComputer::_populateArray( ary, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude ); } else { ClassicalQuantileComputer::_populateArray( ary, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude ); } } CASA_STATD void HingesFencesQuantileComputer::_populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const IncludeLimits& includeLimits, uInt64 maxCount ) const { if (_hasRange) { ConstrainedRangeQuantileComputer::_populateArrays( arys, currentCount, dataBegin, nr, dataStride, includeLimits, maxCount ); } else { ClassicalQuantileComputer::_populateArrays( arys, currentCount, dataBegin, nr, dataStride, includeLimits, maxCount ); } } CASA_STATD void HingesFencesQuantileComputer::_populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const IncludeLimits& includeLimits, uInt64 maxCount ) const { if (_hasRange) { ConstrainedRangeQuantileComputer::_populateArrays( arys, currentCount, dataBegin, nr, dataStride, ranges, isInclude, includeLimits, maxCount ); } else { ClassicalQuantileComputer::_populateArrays( arys, currentCount, dataBegin, nr, dataStride, ranges, isInclude, includeLimits, maxCount ); } } CASA_STATD void HingesFencesQuantileComputer::_populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const IncludeLimits& includeLimits, uInt64 maxCount ) const { if (_hasRange) { ConstrainedRangeQuantileComputer::_populateArrays( arys, currentCount, dataBegin, nr, dataStride, maskBegin, maskStride, includeLimits, maxCount ); } else { ClassicalQuantileComputer::_populateArrays( arys, currentCount, dataBegin, nr, dataStride, maskBegin, maskStride, includeLimits, maxCount ); } } CASA_STATD void HingesFencesQuantileComputer::_populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const IncludeLimits& includeLimits, uInt64 maxCount ) const { if (_hasRange) { ConstrainedRangeQuantileComputer::_populateArrays( arys, currentCount, dataBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude, includeLimits, maxCount ); } else { ClassicalQuantileComputer::_populateArrays( arys, currentCount, dataBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude, includeLimits, maxCount ); } } CASA_STATD void HingesFencesQuantileComputer::_populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const IncludeLimits& includeLimits, uInt64 maxCount ) const { if (_hasRange) { ConstrainedRangeQuantileComputer::_populateArrays( arys, currentCount, dataBegin, weightsBegin, nr, dataStride, includeLimits, maxCount ); } else { ClassicalQuantileComputer::_populateArrays( arys, currentCount, dataBegin, weightsBegin, nr, dataStride, includeLimits, maxCount ); } } CASA_STATD void HingesFencesQuantileComputer::_populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, const IncludeLimits& includeLimits, uInt64 maxCount ) const { if (_hasRange) { ConstrainedRangeQuantileComputer::_populateArrays( arys, currentCount, dataBegin, weightsBegin, nr, dataStride, ranges, isInclude, includeLimits, maxCount ); } else { ClassicalQuantileComputer::_populateArrays( arys, currentCount, dataBegin, weightsBegin, nr, dataStride, ranges, isInclude, includeLimits, maxCount ); } } CASA_STATD void HingesFencesQuantileComputer::_populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const IncludeLimits& includeLimits, uInt64 maxCount ) const { if (_hasRange) { ConstrainedRangeQuantileComputer::_populateArrays( arys, currentCount, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, includeLimits, maxCount ); } else { ClassicalQuantileComputer::_populateArrays( arys, currentCount, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, includeLimits, maxCount ); } } CASA_STATD void HingesFencesQuantileComputer::_populateArrays( std::vector& arys, uInt64& currentCount, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, const IncludeLimits& includeLimits, uInt64 maxCount ) const { if (_hasRange) { ConstrainedRangeQuantileComputer::_populateArrays( arys, currentCount, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude, includeLimits, maxCount ); } else { ClassicalQuantileComputer::_populateArrays( arys, currentCount, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude, includeLimits, maxCount ); } } CASA_STATD Bool HingesFencesQuantileComputer::_populateTestArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, uInt maxElements ) const { if (_hasRange) { return ConstrainedRangeQuantileComputer::_populateTestArray( ary, dataBegin, nr, dataStride, maxElements ); } else { return ClassicalQuantileComputer::_populateTestArray( ary, dataBegin, nr, dataStride, maxElements ); } } CASA_STATD Bool HingesFencesQuantileComputer::_populateTestArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const { if (_hasRange) { return ConstrainedRangeQuantileComputer::_populateTestArray( ary, dataBegin, nr, dataStride, ranges, isInclude, maxElements ); } else { return ClassicalQuantileComputer::_populateTestArray( ary, dataBegin, nr, dataStride, ranges, isInclude, maxElements ); } } CASA_STATD Bool HingesFencesQuantileComputer::_populateTestArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, uInt maxElements ) const { if (_hasRange) { return ConstrainedRangeQuantileComputer::_populateTestArray( ary, dataBegin, nr, dataStride, maskBegin, maskStride, maxElements ); } else { return ClassicalQuantileComputer::_populateTestArray( ary, dataBegin, nr, dataStride, maskBegin, maskStride, maxElements ); } } CASA_STATD Bool HingesFencesQuantileComputer::_populateTestArray( DataArray& ary, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const { if (_hasRange) { return ConstrainedRangeQuantileComputer::_populateTestArray( ary, dataBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude, maxElements ); } else { return ClassicalQuantileComputer::_populateTestArray( ary, dataBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude, maxElements ); } } CASA_STATD Bool HingesFencesQuantileComputer::_populateTestArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, uInt maxElements ) const { if (_hasRange) { return ConstrainedRangeQuantileComputer::_populateTestArray( ary, dataBegin, weightsBegin, nr, dataStride, maxElements ); } else { return ClassicalQuantileComputer::_populateTestArray( ary, dataBegin, weightsBegin, nr, dataStride, maxElements ); } } CASA_STATD Bool HingesFencesQuantileComputer::_populateTestArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const { if (_hasRange) { return ConstrainedRangeQuantileComputer::_populateTestArray( ary, dataBegin, weightsBegin, nr, dataStride, ranges, isInclude, maxElements ); } else { return ClassicalQuantileComputer::_populateTestArray( ary, dataBegin, weightsBegin, nr, dataStride, ranges, isInclude, maxElements ); } } CASA_STATD Bool HingesFencesQuantileComputer::_populateTestArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, uInt maxElements ) const { if (_hasRange) { return ConstrainedRangeQuantileComputer::_populateTestArray( ary, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, maxElements ); } else { return ClassicalQuantileComputer::_populateTestArray( ary, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, maxElements ); } } CASA_STATD Bool HingesFencesQuantileComputer::_populateTestArray( DataArray& ary, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude, uInt maxElements ) const { if (_hasRange) { return ConstrainedRangeQuantileComputer::_populateTestArray( ary, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude, maxElements ); } else { return ClassicalQuantileComputer::_populateTestArray( ary, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude, maxElements ); } } } #endif casacore-3.7.1/scimath/StatsFramework/HingesFencesStatistics.h000066400000000000000000000277751476623553700245370ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_HINGESFENCESSTATISTICS_H #define SCIMATH_HINGESFENCESSTATISTICS_H #include #include #include #include #include namespace casacore { // Class to calculate statistics using the so-called hinges and fences // algorithm. In this algorithm, the data on which the statistics are computed // from is limited to the range of values between Q1 - f*D and Q3 + f*D, // inclusive, where D = Q3 - Q1 and Q1 and Q3 are the first and third quartiles, // respectively. // // This class uses a HingesFencesQuantileComputer object for computing quantile- // like statistics. See class documentation for StatisticsAlgorithm for details // regarding QuantileComputer classes. template < class AccumType, class DataIterator, class MaskIterator=const Bool *, class WeightsIterator=DataIterator > class HingesFencesStatistics : public ConstrainedRangeStatistics { public: // If f is negative, the full dataset is used; ie the object has // the same behavior as a ClassicalStatistics object HingesFencesStatistics(Double f=-1.0); // copy semantics HingesFencesStatistics(const HingesFencesStatistics& other); virtual ~HingesFencesStatistics(); // copy semantics HingesFencesStatistics& operator=( const HingesFencesStatistics& other ); // Clone this instance. Caller is responsible for deleting the returned // pointer. virtual StatisticsAlgorithm* clone() const; // get the algorithm that this object uses for computing stats virtual StatisticsData::ALGORITHM algorithm() const { return StatisticsData::HINGESFENCES; }; // reset object to initial state. Clears all private fields including data, // accumulators, global range. It does not affect the fence factor (_f), // which was set at object construction. virtual void reset(); // This class does not allow statistics to be calculated as datasets are // added, so an exception will be thrown if c is True. void setCalculateAsAdded(Bool c); protected: // // scan through the data set to determine the number of good (unmasked, // weight > 0, within range) points. The first with no mask, no ranges, and // no weights is trivial with npts = nr in this class, but is implemented // here so that derived classes may override it. virtual void _accumNpts( uInt64& npts, const DataIterator& dataStart, uInt64 nr, uInt dataStride ) const; virtual void _accumNpts( uInt64& npts, const DataIterator& dataStart, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _accumNpts( uInt64& npts, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; virtual void _accumNpts( uInt64& npts, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) const; virtual void _accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; // // virtual void _minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ) const; virtual void _minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; virtual void _minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) const; virtual void _minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; // // Sometimes we want the min, max, and npts all in one scan. virtual void _minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ) const; virtual void _minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; virtual void _minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) const; virtual void _minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const; virtual void _minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const; // // // no weights, no mask, no ranges virtual void _unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ); // no weights, no mask virtual void _unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ); virtual void _unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ); virtual void _unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ); // // // has weights, but no mask, no ranges virtual void _weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ); virtual void _weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ); virtual void _weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ); virtual void _weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ); // private: // _f defined in inclusion range between Q1 - _f*D and Q3 + _f*D, where // D = Q3 - Q1 and Q1 and Q3 are the first and third quartiles, respectively Double _f; Bool _rangeIsSet{False}, _hasRange{False}; void _setRange(); }; } #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/StatsFramework/HingesFencesStatistics.tcc000066400000000000000000000565041476623553700250510ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_HINGESFENCESSTATISTICS_TCC #define SCIMATH_HINGESFENCESSTATISTICS_TCC #include #include #include #include namespace casacore { // min > max indicates that these quantities have not be calculated CASA_STATD HingesFencesStatistics::HingesFencesStatistics( Double f ) : ConstrainedRangeStatistics( std::make_shared>( &this->_getDataset() ) ), _f(f) { reset(); } CASA_STATD HingesFencesStatistics::HingesFencesStatistics( const HingesFencesStatistics& other ) : ConstrainedRangeStatistics(other), _f(other._f), _rangeIsSet(other._rangeIsSet), _hasRange(other._hasRange) {} CASA_STATD HingesFencesStatistics::~HingesFencesStatistics() {} CASA_STATD HingesFencesStatistics& HingesFencesStatistics::operator=( const HingesFencesStatistics& other ) { if (this == &other) { return *this; } ConstrainedRangeStatistics::operator=(other); _f = other._f; _rangeIsSet = other._rangeIsSet; _hasRange = other._hasRange; return *this; } CASA_STATD StatisticsAlgorithm* HingesFencesStatistics::clone() const { return new HingesFencesStatistics(*this); } CASA_STATD void HingesFencesStatistics::reset() { _rangeIsSet = False; _hasRange = False; ConstrainedRangeStatistics::reset(); } CASA_STATD void HingesFencesStatistics::setCalculateAsAdded( Bool c ) { ThrowIf( c, "HingesFencesStatistics does not support calculating " "statistics incrementally as data sets are added" ); } CASA_STATD void HingesFencesStatistics::_accumNpts( uInt64& npts, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ) const { if (_hasRange) { ConstrainedRangeStatistics::_accumNpts( npts, dataBegin, nr, dataStride ); } else { ClassicalStatistics::_accumNpts( npts, dataBegin, nr, dataStride ); } } CASA_STATD void HingesFencesStatistics::_accumNpts( uInt64& npts, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { if (_hasRange) { ConstrainedRangeStatistics::_accumNpts( npts, dataBegin, nr, dataStride, ranges, isInclude ); } else { ClassicalStatistics::_accumNpts( npts, dataBegin, nr, dataStride, ranges, isInclude ); } } CASA_STATD void HingesFencesStatistics::_accumNpts( uInt64& npts, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { if (_hasRange) { ConstrainedRangeStatistics::_accumNpts( npts, dataBegin, nr, dataStride, maskBegin, maskStride ); } else { ClassicalStatistics::_accumNpts( npts, dataBegin, nr, dataStride, maskBegin, maskStride ); } } CASA_STATD void HingesFencesStatistics::_accumNpts( uInt64& npts, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { if (_hasRange) { ConstrainedRangeStatistics::_accumNpts( npts, dataBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude ); } else { ClassicalStatistics::_accumNpts( npts, dataBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude ); } } CASA_STATD void HingesFencesStatistics::_accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) const { if (_hasRange) { ConstrainedRangeStatistics::_accumNpts( npts, dataBegin,weightsBegin, nr, dataStride ); } else { ClassicalStatistics::_accumNpts( npts, dataBegin,weightsBegin, nr, dataStride ); } } CASA_STATD void HingesFencesStatistics::_accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { if (_hasRange) { ConstrainedRangeStatistics::_accumNpts( npts, dataBegin, weightsBegin, nr, dataStride, ranges, isInclude ); } else { ClassicalStatistics::_accumNpts( npts, dataBegin, weightsBegin, nr, dataStride, ranges, isInclude ); } } CASA_STATD void HingesFencesStatistics::_accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { if (_hasRange) { ConstrainedRangeStatistics::_accumNpts( npts, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude ); } else { ClassicalStatistics::_accumNpts( npts, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude ); } } CASA_STATD void HingesFencesStatistics::_accumNpts( uInt64& npts, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { if (_hasRange) { ConstrainedRangeStatistics::_accumNpts( npts, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride ); } else { ClassicalStatistics::_accumNpts( npts, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride ); } } CASA_STATD void HingesFencesStatistics::_minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ) const { if (_hasRange) { ConstrainedRangeStatistics::_minMax( mymin, mymax, dataBegin, nr, dataStride ); } else { ClassicalStatistics::_minMax( mymin, mymax, dataBegin, nr, dataStride ); } } CASA_STATD void HingesFencesStatistics::_minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { if (_hasRange) { ConstrainedRangeStatistics::_minMax( mymin, mymax, dataBegin, nr, dataStride, ranges, isInclude ); } else { ClassicalStatistics::_minMax( mymin, mymax, dataBegin, nr, dataStride, ranges, isInclude ); } } CASA_STATD void HingesFencesStatistics::_minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { if (_hasRange) { ConstrainedRangeStatistics::_minMax( mymin, mymax, dataBegin, nr, dataStride, maskBegin, maskStride ); } else { ClassicalStatistics::_minMax( mymin, mymax, dataBegin, nr, dataStride, maskBegin, maskStride ); } } CASA_STATD void HingesFencesStatistics::_minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { if (_hasRange) { ConstrainedRangeStatistics::_minMax( mymin, mymax, dataBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude ); } else { ClassicalStatistics::_minMax( mymin, mymax, dataBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude ); } } CASA_STATD void HingesFencesStatistics::_minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) const { if (_hasRange) { ConstrainedRangeStatistics::_minMax( mymin, mymax, dataBegin, weightsBegin, nr, dataStride ); } else { ClassicalStatistics::_minMax( mymin, mymax, dataBegin, weightsBegin, nr, dataStride ); } } CASA_STATD void HingesFencesStatistics::_minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { if (_hasRange) { ConstrainedRangeStatistics::_minMax( mymin, mymax, dataBegin, weightsBegin, nr, dataStride, ranges, isInclude ); } else { ClassicalStatistics::_minMax( mymin, mymax, dataBegin, weightsBegin, nr, dataStride, ranges, isInclude ); } } CASA_STATD void HingesFencesStatistics::_minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { if (_hasRange) { ConstrainedRangeStatistics::_minMax( mymin, mymax, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude ); } else { ClassicalStatistics::_minMax( mymin, mymax, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude ); } } CASA_STATD void HingesFencesStatistics::_minMax( std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { if (_hasRange) { ConstrainedRangeStatistics::_minMax( mymin, mymax, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride ); } else { ClassicalStatistics::_minMax( mymin, mymax, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride ); } } CASA_STATD void HingesFencesStatistics::_minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ) const { if (_hasRange) { ConstrainedRangeStatistics::_minMaxNpts( npts, mymin, mymax, dataBegin, nr, dataStride ); } else { ClassicalStatistics::_minMaxNpts( npts, mymin, mymax, dataBegin, nr, dataStride ); } } CASA_STATD void HingesFencesStatistics::_minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { if (_hasRange) { ConstrainedRangeStatistics::_minMaxNpts( npts, mymin, mymax, dataBegin, nr, dataStride, ranges, isInclude ); } else { ClassicalStatistics::_minMaxNpts( npts, mymin, mymax, dataBegin, nr, dataStride, ranges, isInclude ); } } CASA_STATD void HingesFencesStatistics::_minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { if (_hasRange) { ConstrainedRangeStatistics::_minMaxNpts( npts, mymin, mymax, dataBegin, nr, dataStride, maskBegin, maskStride ); } else { ClassicalStatistics::_minMaxNpts( npts, mymin, mymax, dataBegin, nr, dataStride, maskBegin, maskStride ); } } CASA_STATD void HingesFencesStatistics::_minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { if (_hasRange) { ConstrainedRangeStatistics::_minMaxNpts( npts, mymin, mymax, dataBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude ); } else { ClassicalStatistics::_minMaxNpts( npts, mymin, mymax, dataBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude ); } } CASA_STATD void HingesFencesStatistics::_minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) const { if (_hasRange) { ConstrainedRangeStatistics::_minMaxNpts( npts, mymin, mymax, dataBegin, weightsBegin, nr, dataStride ); } else { ClassicalStatistics::_minMaxNpts( npts, mymin, mymax, dataBegin, weightsBegin, nr, dataStride ); } } CASA_STATD void HingesFencesStatistics::_minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) const { if (_hasRange) { ConstrainedRangeStatistics::_minMaxNpts( npts, mymin, mymax, dataBegin, weightsBegin, nr, dataStride, ranges, isInclude ); } else { ClassicalStatistics::_minMaxNpts( npts, mymin, mymax, dataBegin, weightsBegin, nr, dataStride, ranges, isInclude ); } } CASA_STATD void HingesFencesStatistics::_minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) const { if (_hasRange) { ConstrainedRangeStatistics::_minMaxNpts( npts, mymin, mymax, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude ); } else { ClassicalStatistics::_minMaxNpts( npts, mymin, mymax, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude ); } } CASA_STATD void HingesFencesStatistics::_minMaxNpts( uInt64& npts, std::shared_ptr& mymin, std::shared_ptr& mymax, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) const { if (_hasRange) { ConstrainedRangeStatistics::_minMaxNpts( npts, mymin, mymax, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride ); } else { ClassicalStatistics::_minMaxNpts( npts, mymin, mymax, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride ); } } CASA_STATD void HingesFencesStatistics::_setRange() { if (_rangeIsSet) { return; } _hasRange = _f >= 0; if (_hasRange) { std::set quantiles; quantiles.insert(0.25); quantiles.insert(0.75); ClassicalStatistics cs(*this); std::map quartiles = cs.getQuantiles(quantiles); auto iqr = quartiles[0.75] - quartiles[0.25]; auto range = std::make_shared>( quartiles[0.25] - _f*iqr, quartiles[0.75] + _f*iqr ); ConstrainedRangeStatistics::_setRange(range); } _rangeIsSet = True; ( (HingesFencesQuantileComputer *)( this->_getQuantileComputer().get() ) )->setHasRange(_hasRange); } CASA_STATD void HingesFencesStatistics::_unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, uInt64 nr, uInt dataStride ) { if (_hasRange) { ConstrainedRangeStatistics::_unweightedStats( stats, ngood, location, dataBegin, nr, dataStride ); } else { ClassicalStatistics::_unweightedStats( stats, ngood, location, dataBegin, nr, dataStride ); } } CASA_STATD void HingesFencesStatistics::_unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) { if (_hasRange) { ConstrainedRangeStatistics::_unweightedStats( stats, ngood, location, dataBegin, nr, dataStride, ranges, isInclude ); } else { ClassicalStatistics::_unweightedStats( stats, ngood, location, dataBegin, nr, dataStride, ranges, isInclude ); } } CASA_STATD void HingesFencesStatistics::_unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) { if (_hasRange) { ConstrainedRangeStatistics::_unweightedStats( stats, ngood, location, dataBegin, nr, dataStride, maskBegin, maskStride ); } else { ClassicalStatistics::_unweightedStats( stats, ngood, location, dataBegin, nr, dataStride, maskBegin, maskStride ); } } CASA_STATD void HingesFencesStatistics::_unweightedStats( StatsData& stats, uInt64& ngood, LocationType& location, const DataIterator& dataBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) { if (_hasRange) { ConstrainedRangeStatistics::_unweightedStats( stats, ngood, location, dataBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude ); } else { ClassicalStatistics::_unweightedStats( stats, ngood, location, dataBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude ); } } CASA_STATD void HingesFencesStatistics::_weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride ) { if (_hasRange) { ConstrainedRangeStatistics::_weightedStats( stats, location, dataBegin, weightsBegin, nr, dataStride ); } else { ClassicalStatistics::_weightedStats( stats, location, dataBegin, weightsBegin, nr, dataStride ); } } CASA_STATD void HingesFencesStatistics::_weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const DataRanges& ranges, Bool isInclude ) { if (_hasRange) { ConstrainedRangeStatistics::_weightedStats( stats, location, dataBegin, weightsBegin, nr, dataStride, ranges, isInclude ); } else { ClassicalStatistics::_weightedStats( stats, location, dataBegin, weightsBegin, nr, dataStride, ranges, isInclude ); } } CASA_STATD void HingesFencesStatistics::_weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride, const DataRanges& ranges, Bool isInclude ) { if (_hasRange) { ConstrainedRangeStatistics::_weightedStats( stats, location, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude ); } else { ClassicalStatistics::_weightedStats( stats, location, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride, ranges, isInclude ); } } CASA_STATD void HingesFencesStatistics::_weightedStats( StatsData& stats, LocationType& location, const DataIterator& dataBegin, const WeightsIterator& weightsBegin, uInt64 nr, uInt dataStride, const MaskIterator& maskBegin, uInt maskStride ) { if (_hasRange) { ConstrainedRangeStatistics::_weightedStats( stats, location, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride ); } else { ClassicalStatistics::_weightedStats( stats, location, dataBegin, weightsBegin, nr, dataStride, maskBegin, maskStride ); } } } #endif casacore-3.7.1/scimath/StatsFramework/StatisticsAlgorithm.h000066400000000000000000000427361476623553700241160ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_STATISTICSALGORITHM_H #define SCIMATH_STATISTICSALGORITHM_H #include #include #include #include #include #include #include #include #include #include namespace casacore { // Base class of statistics algorithm class hierarchy. // The default implementation is such that statistics are only calculated when // methods that actually compute statistics are called. Until then, the // iterators which point to the beginning of data sets, masks, etc. are held in // memory. Thus, the caller must keep all data sets available for the statistics // object until these methods are called, and of course, if the actual data // values are changed between adding data and calculating statistics, the // updated values are used when calculating statistics. Derived classes may // override this behavior. // // PRECISION CONSIDERATIONS // Many statistics are computed via accumulators. This can lead to precision // issues, especially for large datasets. For this reason, it is highly // recommended that the data type one uses as the AccumType be of higher // precision, if possible, than the data type pointed to by input iterator. So // for example, if one has a data set of Float values (to which the // InputIterator type points to), then one should use type Double for the // AccumType. In this case, the Float data values will be converted to Doubles // before they are accumulated. // // METHODS OF PROVIDING DATA // Data may be provided in one of two mutually exclusive ways. The first way is // simpler, and that is to use the setData()/addData() methods. Calling // setData() will clear any previous data that was added via these methods or // via a data provider (see below). Calling addData() after having called // setData() will add a data set to the set of data sets on which statistics // will be calculated. In order for this to work correctly, the iterators which // are passed into these methods must still be valid when statistics are // calculated (although note that some derived classes allow certain statistics // to be updated as data sets are added via these methods. See specific classes // for details). // // The second way to provide data is via an object derived from class // StatsDataProvider, in which methods are implemented for retrieving various // information about the data sets to be included. Such an interface is // necessary for data structures which do not easily lend themselves to be // provided via the setData()/addData() methods. For example, in the case of // iterating through a Lattice, a lattice iterator will overwrite the memory // location of the previous chunk of data with the current chunk of data. // Therefore, if one does not wish to load data from the entire lattice into // memory (which is why LatticeIterator was designed to have the behavior it // does), one must use the LatticeStatsDataProvider class, which the statistics // framework will use to iterate through the lattice, only keeping one chunk of // the data of the lattice in memory any given moment. // // STORAGE OF DATA // In order to reduce maintenance costs, the accounting details of the data sets // are maintained in a StatisticsDataset object. This object is held in memory // at the StatisticsAlgorithm level in the _dataset private field of this class // when a derived class is instantiated. A StatisticsDataset object should never // need to be explicitly instantiated by an API developer. // // QUANTILES // A quantile is a value contained in a data set, such that, it has a zero-based // index of ceil(q*n)-1 in the equivalent ordered dataset, where 0 < q < 1 // specifies the fractional location within the ordered dataset and n is the // total number of valid elements. Note that, for a dataset with an odd number // of elements, the median is the same as the quantile value when q = 0.5. // However, there is no such correspondence between the median in a dataset with // an even number of elements, since the median in that case is given by the // mean of the elements of zero-based indices n/2-1 and n/2 in the equivalent // ordered dataset. Thus, in the case of a dataset with an even number of // values, the median may not even exist in the dataset, while a generic // quantile value must exist in the dataset by definition. Note when calculating // quantile values, a dataset that does not fall in specified dataset ranges, // is not included via a stride specification, is masked, or has a weight of // zero, is not considered a member of the dataset for the purposes of quantile // calculations. // // CLASS ORGANIZATION // In general, in the StatsFramework class hierarchy, classes derived from // StatisticsAlgorithm and its descendants contain methods which calculate the // relevant statistics which are computed via accumulation. These classes also // contain the top level methods for computing the quantile-like statistics, for // the convenience of the API developer. Derived classes of StatisticsAlgorithm // normally will have a private field which is an object that contains methods // which compute the various quantile-like statistics. These so-called // QuantileComputer classes have been created to reduce maintainability costs; // because putting all the code into single class files was becoming unwieldy. // The concrete QuantileComputer classes are ultimately derived from // StatisticsAlgorithmQuantileComputer, which is the virtual base class of this // hierarchy. StatisticsAlgorithm objects do not contain a // StatisticsAlgorithmQuantileComputer private field, since StatisticsAlgorithm // is also a virtual base class and hence no actual statistics are computed // within it. The design is such that the only classes an API developer should // over instantiate are the derived classes of StatisticsAlgorithm; the // QuantileComputer classes should never be explicitly instantiated in code // which uses the StatsFramework API. template < class AccumType, class DataIterator, class MaskIterator=const Bool *, class WeightsIterator=DataIterator > class StatisticsAlgorithm { public: virtual ~StatisticsAlgorithm(); // Clone this instance virtual StatisticsAlgorithm* clone() const = 0; // // Add a dataset to an existing set of datasets on which statistics are to // be calculated. nr is the number of points to be considered. If // dataStride is greater than 1, when // nrAccountsForStride=True indicates that the stride has been // taken into account in the value of nr. Otherwise, it has not // so that the actual number of points to include is nr/dataStride if // nr % dataStride == 0 or (int)(nr/dataStride) + 1 otherwise. if one calls // this method after a data provider has been set, an exception will be // thrown. In this case, one should call setData(), rather than addData(), // to indicate that the underlying data provider should be removed. // dataRanges provide the ranges of data to include if // isInclude is True, or ranges of data to exclude if // isInclude is False. If a datum equals the end point of a data // range, it is considered good (included) if isInclude is True, // and it is considered bad (excluded) if isInclude is False. void addData( const DataIterator& first, uInt nr, uInt dataStride=1, Bool nrAccountsForStride=False ); void addData( const DataIterator& first, uInt nr, const DataRanges& dataRanges, Bool isInclude=True, uInt dataStride=1, Bool nrAccountsForStride=False ); void addData( const DataIterator& first, const MaskIterator& maskFirst, uInt nr, uInt dataStride=1, Bool nrAccountsForStride=False, uInt maskStride=1 ); void addData( const DataIterator& first, const MaskIterator& maskFirst, uInt nr, const DataRanges& dataRanges, Bool isInclude=True, uInt dataStride=1, Bool nrAccountsForStride=False, uInt maskStride=1 ); void addData( const DataIterator& first, const WeightsIterator& weightFirst, uInt nr, uInt dataStride=1, Bool nrAccountsForStride=False ); void addData( const DataIterator& first, const WeightsIterator& weightFirst, uInt nr, const DataRanges& dataRanges, Bool isInclude=True, uInt dataStride=1, Bool nrAccountsForStride=False ); void addData( const DataIterator& first, const WeightsIterator& weightFirst, const MaskIterator& maskFirst, uInt nr, uInt dataStride=1, Bool nrAccountsForStride=False, uInt maskStride=1 ); void addData( const DataIterator& first, const WeightsIterator& weightFirst, const MaskIterator& maskFirst, uInt nr, const DataRanges& dataRanges, Bool isInclude=True, uInt dataStride=1, Bool nrAccountsForStride=False, uInt maskStride=1 ); // // get the algorithm that this object uses for computing stats virtual StatisticsData::ALGORITHM algorithm() const = 0; virtual AccumType getMedian( std::shared_ptr knownNpts=nullptr, std::shared_ptr knownMin=nullptr, std::shared_ptr knownMax=nullptr, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt nBins=10000 ) = 0; // The return value is the median; the quantiles are returned in the // quantileToValue map. virtual AccumType getMedianAndQuantiles( std::map& quantileToValue, const std::set& quantiles, std::shared_ptr knownNpts=nullptr, std::shared_ptr knownMin=nullptr, std::shared_ptr knownMax=nullptr, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt nBins=10000 ) = 0; // get the median of the absolute deviation about the median of the data. virtual AccumType getMedianAbsDevMed( std::shared_ptr knownNpts=nullptr, std::shared_ptr knownMin=nullptr, std::shared_ptr knownMax=nullptr, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt nBins=10000 ) = 0; // Purposefully not virtual. Derived classes should not implement. AccumType getQuantile( Double quantile, std::shared_ptr knownNpts=nullptr, std::shared_ptr knownMin=nullptr, std::shared_ptr knownMax=nullptr, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt nBins=10000 ); // get a map of quantiles to values. virtual std::map getQuantiles( const std::set& quantiles, std::shared_ptr npts=nullptr, std::shared_ptr min=nullptr, std::shared_ptr max=nullptr, uInt binningThreshholdSizeBytes=4096*4096, Bool persistSortedArray=False, uInt nBins=10000 ) = 0; // get the value of the specified statistic. Purposefully not virtual. // Derived classes should not implement. AccumType getStatistic(StatisticsData::STATS stat); // certain statistics such as max and min have locations in the dataset // associated with them. This method gets those locations. The first value // in the returned pair is the zero-based dataset number that was set or // added. The second value is the zero-based index in that dataset. A data // stride of greater than one is not accounted for, so the index represents // the actual location in the data set, independent of the dataStride value. virtual LocationType getStatisticIndex(StatisticsData::STATS stat) = 0; // Return statistics. Purposefully not virtual. Derived classes should not // implement. StatsData getStatistics(); // reset this object by clearing data. virtual void reset(); // // setdata() clears any current datasets or data provider and then adds the // specified data set as the first dataset in the (possibly new) set of data // sets for which statistics are to be calculated. See addData() for // parameter meanings. These methods are purposefully not virtual. Derived // classes should not implement. void setData( const DataIterator& first, uInt nr, uInt dataStride=1, Bool nrAccountsForStride=False ); void setData( const DataIterator& first, uInt nr, const DataRanges& dataRanges, Bool isInclude=True, uInt dataStride=1, Bool nrAccountsForStride=False ); void setData( const DataIterator& first, const MaskIterator& maskFirst, uInt nr, uInt dataStride=1, Bool nrAccountsForStride=False, uInt maskStride=1 ); void setData( const DataIterator& first, const MaskIterator& maskFirst, uInt nr, const DataRanges& dataRanges, Bool isInclude=True, uInt dataStride=1, Bool nrAccountsForStride=False, uInt maskStride=1 ); void setData( const DataIterator& first, const WeightsIterator& weightFirst, uInt nr, uInt dataStride=1, Bool nrAccountsForStride=False ); void setData( const DataIterator& first, const WeightsIterator& weightFirst, uInt nr, const DataRanges& dataRanges, Bool isInclude=True, uInt dataStride=1, Bool nrAccountsForStride=False ); void setData( const DataIterator& first, const WeightsIterator& weightFirst, const MaskIterator& maskFirst, uInt nr, uInt dataStride=1, Bool nrAccountsForStride=False, uInt maskStride=1 ); void setData( const DataIterator& first, const WeightsIterator& weightFirst, const MaskIterator& maskFirst, uInt nr, const DataRanges& dataRanges, Bool isInclude=True, uInt dataStride=1, Bool nrAccountsForStride=False, uInt maskStride=1 ); // // instead of setting and adding data "by hand", set the data provider // that will provide all the data sets. Calling this method will clear // any other data sets that have previously been set or added. Method // is virtual to allow derived classes to carry out any necessary // specialized accounting when resetting the data provider. virtual void setDataProvider(StatsDataProvider *dataProvider); // Provide guidance to algorithms by specifying a priori which statistics // the caller would like calculated. virtual void setStatsToCalculate(std::set& stats); protected: StatisticsAlgorithm(); // use copy semantics, except for the data provider which uses reference // semantics StatisticsAlgorithm(const StatisticsAlgorithm& other); // use copy semantics, except for the data provider which uses reference // semantics StatisticsAlgorithm& operator=(const StatisticsAlgorithm& other); // Allows derived classes to do things after data is set or added. // Default implementation does nothing. virtual void _addData() {} // // These methods are purposefully not virtual. Derived classes should // not implement. const StatisticsDataset& _getDataset() const { return _dataset; } StatisticsDataset& _getDataset() { return _dataset; } // virtual AccumType _getStatistic(StatisticsData::STATS stat) = 0; virtual StatsData _getStatistics() = 0; const std::set _getStatsToCalculate() const { return _statsToCalculate; } virtual const std::set& _getUnsupportedStatistics() const { return _unsupportedStats; } // Derived classes should normally call this in their constructors, if // applicable. void _setUnsupportedStatistics( const std::set& stats ) { _unsupportedStats = stats; } private: std::set _statsToCalculate{}, _unsupportedStats{}; StatisticsDataset _dataset{}; Bool _resetDataset{True}; void _resetExceptDataset(); }; } #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif #endif casacore-3.7.1/scimath/StatsFramework/StatisticsAlgorithm.tcc000066400000000000000000000227211476623553700244300ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_STATISTICSALGORITHM_TCC #define SCIMATH_STATISTICSALGORITHM_TCC #include #include namespace casacore { CASA_STATD StatisticsAlgorithm::StatisticsAlgorithm() {} CASA_STATD StatisticsAlgorithm::StatisticsAlgorithm( const StatisticsAlgorithm& other ) : _statsToCalculate(other._statsToCalculate), _unsupportedStats(other._unsupportedStats), _dataset(other._dataset), _resetDataset(other._resetDataset) {} CASA_STATD StatisticsAlgorithm& StatisticsAlgorithm::operator=( const StatisticsAlgorithm& other ) { if (this == &other) { return *this; } _statsToCalculate = other._statsToCalculate; _unsupportedStats = other._unsupportedStats; _dataset = other._dataset; _resetDataset = other._resetDataset; return *this; } CASA_STATD StatisticsAlgorithm::~StatisticsAlgorithm() {} CASA_STATD void StatisticsAlgorithm::addData( const DataIterator& first, uInt nr, uInt dataStride, Bool nrAccountsForStride ) { _dataset.addData(first, nr, dataStride, nrAccountsForStride); _addData(); } CASA_STATD void StatisticsAlgorithm::addData( const DataIterator& first, uInt nr, const DataRanges& dataRanges, Bool isInclude, uInt dataStride, Bool nrAccountsForStride ) { _dataset.addData( first, nr, dataRanges, isInclude, dataStride, nrAccountsForStride ); _addData(); } CASA_STATD void StatisticsAlgorithm::addData( const DataIterator& first, const MaskIterator& maskFirst, uInt nr, uInt dataStride, Bool nrAccountsForStride, uInt maskStride ) { _dataset.addData( first, maskFirst, nr, dataStride, nrAccountsForStride, maskStride ); _addData(); } CASA_STATD void StatisticsAlgorithm::addData( const DataIterator& first, const MaskIterator& maskFirst, uInt nr, const DataRanges& dataRanges, Bool isInclude, uInt dataStride, Bool nrAccountsForStride, uInt maskStride ) { _dataset.addData( first, maskFirst, nr, dataRanges, isInclude, dataStride, nrAccountsForStride, maskStride ); _addData(); } CASA_STATD void StatisticsAlgorithm::addData( const DataIterator& first, const WeightsIterator& weightFirst, uInt nr, uInt dataStride, Bool nrAccountsForStride ) { _dataset.addData(first, weightFirst, nr, dataStride, nrAccountsForStride); _addData(); } CASA_STATD void StatisticsAlgorithm::addData( const DataIterator& first, const WeightsIterator& weightFirst, uInt nr, const DataRanges& dataRanges, Bool isInclude, uInt dataStride, Bool nrAccountsForStride ) { _dataset.addData( first, weightFirst, nr, dataRanges, isInclude, dataStride, nrAccountsForStride ); _addData(); } CASA_STATD void StatisticsAlgorithm::addData( const DataIterator& first, const WeightsIterator& weightFirst, const MaskIterator& maskFirst, uInt nr, uInt dataStride, Bool nrAccountsForStride, uInt maskStride ) { _dataset.addData( first, weightFirst, maskFirst, nr, dataStride, nrAccountsForStride, maskStride ); _addData(); } CASA_STATD void StatisticsAlgorithm::addData( const DataIterator& first, const WeightsIterator& weightFirst, const MaskIterator& maskFirst, uInt nr, const DataRanges& dataRanges, Bool isInclude, uInt dataStride, Bool nrAccountsForStride, uInt maskStride ) { _dataset.addData( first, weightFirst, maskFirst, nr, dataRanges, isInclude, dataStride, nrAccountsForStride, maskStride ); _addData(); } CASA_STATD AccumType StatisticsAlgorithm::getQuantile( Double quantile, std::shared_ptr knownNpts, std::shared_ptr knownMin, std::shared_ptr knownMax, uInt binningThreshholdSizeBytes, Bool persistSortedArray, uInt nBins ) { std::set qs; qs.insert(quantile); return getQuantiles( qs, knownNpts, knownMin, knownMax, binningThreshholdSizeBytes, persistSortedArray, nBins ).begin()->second; } CASA_STATD AccumType StatisticsAlgorithm::getStatistic( StatisticsData::STATS stat ) { ThrowIf( _unsupportedStats.find(stat) != _unsupportedStats.end(), StatisticsData::toString(stat) + " is not a supported statistic for this algorithm" ); ThrowIf( ! _statsToCalculate.empty() && _statsToCalculate.find(stat) == _statsToCalculate.end(), "You did not explicitly request to compute " + StatisticsData::toString(stat) ); return this->_getStatistic(stat); } CASA_STATD StatsData StatisticsAlgorithm::getStatistics() { return this->_getStatistics(); } CASA_STATD void StatisticsAlgorithm::setData( const DataIterator& first, uInt nr, uInt dataStride, Bool nrAccountsForStride ) { _resetExceptDataset(); _dataset.setData(first, nr, dataStride, nrAccountsForStride); _addData(); } CASA_STATD void StatisticsAlgorithm::setData( const DataIterator& first, uInt nr, const DataRanges& dataRanges, Bool isInclude, uInt dataStride, Bool nrAccountsForStride ) { _resetExceptDataset(); _dataset.setData( first, nr, dataRanges, isInclude, dataStride, nrAccountsForStride ); _addData(); } CASA_STATD void StatisticsAlgorithm::setData( const DataIterator& first, const MaskIterator& maskFirst, uInt nr, uInt dataStride, Bool nrAccountsForStride, uInt maskStride ) { _resetExceptDataset(); _dataset.setData( first, maskFirst, nr, dataStride, nrAccountsForStride, maskStride ); _addData(); } CASA_STATD void StatisticsAlgorithm::setData( const DataIterator& first, const MaskIterator& maskFirst, uInt nr, const DataRanges& dataRanges, Bool isInclude, uInt dataStride, Bool nrAccountsForStride, uInt maskStride ) { _resetExceptDataset(); _dataset.setData( first, maskFirst, nr, dataRanges, isInclude, dataStride, nrAccountsForStride, maskStride ); _addData(); } CASA_STATD void StatisticsAlgorithm::setData( const DataIterator& first, const WeightsIterator& weightFirst, uInt nr, uInt dataStride, Bool nrAccountsForStride ) { _resetExceptDataset(); _dataset.setData(first, weightFirst, nr, dataStride, nrAccountsForStride); _addData(); } CASA_STATD void StatisticsAlgorithm::setData( const DataIterator& first, const WeightsIterator& weightFirst, uInt nr, const DataRanges& dataRanges, Bool isInclude, uInt dataStride, Bool nrAccountsForStride ) { _resetExceptDataset(); _dataset.setData( first, weightFirst, nr, dataRanges, isInclude, dataStride, nrAccountsForStride ); _addData(); } CASA_STATD void StatisticsAlgorithm::setData( const DataIterator& first, const WeightsIterator& weightFirst, const MaskIterator& maskFirst, uInt nr, uInt dataStride, Bool nrAccountsForStride, uInt maskStride ) { _resetExceptDataset(); _dataset.setData( first, weightFirst, maskFirst, nr, dataStride, nrAccountsForStride, maskStride ); _addData(); } CASA_STATD void StatisticsAlgorithm::setData( const DataIterator& first, const WeightsIterator& weightFirst, const MaskIterator& maskFirst, uInt nr, const DataRanges& dataRanges, Bool isInclude, uInt dataStride, Bool nrAccountsForStride, uInt maskStride ) { _resetExceptDataset(); _dataset.setData( first, weightFirst, maskFirst, nr, dataRanges, isInclude, dataStride, nrAccountsForStride, maskStride ); _addData(); } CASA_STATD void StatisticsAlgorithm::setStatsToCalculate( std::set& stats ) { _statsToCalculate = stats; } CASA_STATD void StatisticsAlgorithm::setDataProvider( StatsDataProvider *dataProvider ) { _dataset.setDataProvider(dataProvider); _resetExceptDataset(); } CASA_STATD void StatisticsAlgorithm::reset() { if (_resetDataset) { _dataset.reset(); } } CASA_STATD void StatisticsAlgorithm::_resetExceptDataset() { _resetDataset = False; reset(); _resetDataset = True; } } #endif casacore-3.7.1/scimath/StatsFramework/StatisticsAlgorithmFactory.h000066400000000000000000000114371476623553700254400ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_STATSALGORITHMFACTORY_H #define SCIMATH_STATSALGORITHMFACTORY_H #include #include #include #include namespace casacore { // Provides a single interface for creation of stats algorithm objects template < class AccumType, class DataIterator, class MaskIterator=const Bool *, class WeightsIterator=DataIterator > class StatisticsAlgorithmFactory { public: // to make copy() more straight forward to implement template < class AccumType2, class DataIterator2, class MaskIterator2, class WeightsIterator2 > friend class StatisticsAlgorithmFactory; // upon construction, the object is configured to use the classical stats // algorithm StatisticsAlgorithmFactory(); ~StatisticsAlgorithmFactory(); void configureBiweight(Int maxIter=3, Double c=6.0); void configureClassical(); // configure to use fit to half algorithm. void configureFitToHalf( FitToHalfStatisticsData::CENTER centerType =FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::USE_DATA useData =FitToHalfStatisticsData::LE_CENTER, AccumType centerValue=0 ); // configure to use hinges-fences algorithm void configureHingesFences(Double f); // configure to use Chauvenet's criterion void configureChauvenet(Double zscore=-1, Int maxIterations=-1); // copy the data from this object to an object with different template // types. Note that the AccumType of other must be the same as // the AccumType of this object. template void copy( StatisticsAlgorithmFactory< AccumType, DataIterator2, MaskIterator2, WeightsIterator2 >& other ) const; // Create a pointer to an object of a class derived from StatisticsAlgorithm // that reflects the current configuration std::shared_ptr> createStatsAlgorithm() const; StatisticsData::ALGORITHM algorithm() const { return _algorithm; } // Throws an exception if the current configuration is not relevant // to the Biweight algorithm StatisticsAlgorithmFactoryData::BiweightData biweightData() const; // Throws an exception if the current configuration is not relevant // to the Chauvenet/zscore algorithm StatisticsAlgorithmFactoryData::ChauvenetData chauvenetData() const; // Throws an exception if the current configuration is not relevant // to the hinges-fences algorithm Double hingesFencesFactor() const; // Throws an exception if the current configuration is not relevant // to the fit-to-half algorithm StatisticsAlgorithmFactoryData::FitToHalfData fitToHalfData() const; // create a record from the current configuration that can be used // to create another object using the fromRecord() method. Record toRecord() const; // create an object from a record static StatisticsAlgorithmFactory fromRecord(const Record& r); private: StatisticsData::ALGORITHM _algorithm; // hinges-fences f factor Double _hf; StatisticsAlgorithmFactoryData::BiweightData _biweightData; StatisticsAlgorithmFactoryData::FitToHalfData _fitToHalfData; StatisticsAlgorithmFactoryData::ChauvenetData _chauvData; }; } #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif #endif casacore-3.7.1/scimath/StatsFramework/StatisticsAlgorithmFactory.tcc000066400000000000000000000232031476623553700257540ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_STATISTICSALGORITHMFACTORY_TCC #define SCIMATH_STATISTICSALGORITHMFACTORY_TCC #include #include #include #include #include #include namespace casacore { CASA_STATD StatisticsAlgorithmFactory::StatisticsAlgorithmFactory() { configureClassical(); } CASA_STATD StatisticsAlgorithmFactory::~StatisticsAlgorithmFactory() {} CASA_STATD void StatisticsAlgorithmFactory::configureBiweight( Int maxIter, Double c ) { _algorithm = StatisticsData::BIWEIGHT; _biweightData.maxIter = maxIter; _biweightData.c = c; } CASA_STATD void StatisticsAlgorithmFactory::configureClassical() { _algorithm = StatisticsData::CLASSICAL; } CASA_STATD void StatisticsAlgorithmFactory::configureFitToHalf( FitToHalfStatisticsData::CENTER centerType, FitToHalfStatisticsData::USE_DATA useData, AccumType centerValue ) { _algorithm = StatisticsData::FITTOHALF; _fitToHalfData.center = centerType; _fitToHalfData.side = useData; _fitToHalfData.centerValue = centerValue; } CASA_STATD void StatisticsAlgorithmFactory::configureHingesFences(Double f) { _algorithm = StatisticsData::HINGESFENCES; _hf = f; } CASA_STATD void StatisticsAlgorithmFactory::configureChauvenet( Double zscore, Int maxIterations ) { _algorithm = StatisticsData::CHAUVENETCRITERION; _chauvData.zScore = zscore; _chauvData.maxIter= maxIterations; } CASA_STATD template void StatisticsAlgorithmFactory::copy( StatisticsAlgorithmFactory< AccumType, DataIterator2, MaskIterator2, WeightsIterator2 >& other ) const { other._algorithm = _algorithm; other._hf = _hf; other._chauvData = _chauvData; other._fitToHalfData = _fitToHalfData; other._biweightData = _biweightData; } CASA_STATD std::shared_ptr> StatisticsAlgorithmFactory::createStatsAlgorithm() const { switch (_algorithm) { case StatisticsData::BIWEIGHT: return std::make_shared>( _biweightData.maxIter, _biweightData.c ); case StatisticsData::CLASSICAL: return std::make_shared>(); case StatisticsData::HINGESFENCES: { return std::make_shared>(_hf); } case StatisticsData::FITTOHALF: { return std::make_shared>( _fitToHalfData.center, _fitToHalfData.side, _fitToHalfData.centerValue ); } case StatisticsData::CHAUVENETCRITERION: { return std::make_shared>( _chauvData.zScore, _chauvData.maxIter ); } default: ThrowCc( "Logic Error: Unhandled algorithm " + String::toString(_algorithm) ); } } CASA_STATD StatisticsAlgorithmFactoryData::BiweightData StatisticsAlgorithmFactory::biweightData() const { ThrowIf( _algorithm != StatisticsData::BIWEIGHT, "Object is currently not configured to use the biweight algorithm" ); return _biweightData; } CASA_STATD Double StatisticsAlgorithmFactory::hingesFencesFactor() const { ThrowIf( _algorithm != StatisticsData::HINGESFENCES, "Object is currently not configured to use the hinges-fences algorithm" ); return _hf; } CASA_STATD StatisticsAlgorithmFactoryData::FitToHalfData StatisticsAlgorithmFactory::fitToHalfData() const { ThrowIf( _algorithm != StatisticsData::FITTOHALF, "Object is currently not configured to use the fit to half algorithm" ); return _fitToHalfData; } CASA_STATD StatisticsAlgorithmFactoryData::ChauvenetData StatisticsAlgorithmFactory::chauvenetData() const { ThrowIf( _algorithm != StatisticsData::CHAUVENETCRITERION, "Object is currently not configured to use " "the chauvenet/zscore algorithm" ); return _chauvData; } CASA_STATD Record StatisticsAlgorithmFactory::toRecord() const { Record r; r.define("algorithm", _algorithm); switch (_algorithm) { case StatisticsData::BIWEIGHT: r.define("max_iter", _biweightData.maxIter); r.define("c", _biweightData.c); return r; case StatisticsData::CLASSICAL: // nothing else to add return r; case StatisticsData::HINGESFENCES: { r.define("hf", _hf); return r; } case StatisticsData::FITTOHALF: { r.define("center", _fitToHalfData.center); r.define("side", _fitToHalfData.side); if (_fitToHalfData.center == FitToHalfStatisticsData::CVALUE) { r.define("center_value", _fitToHalfData.centerValue); } return r; } case StatisticsData::CHAUVENETCRITERION: { r.define("zscore", _chauvData.zScore); r.define("max_iter", _chauvData.maxIter); return r; } default: ThrowCc( "Logic Error: Unhandled algorithm " + String::toString(_algorithm) ); } } CASA_STATD StatisticsAlgorithmFactory StatisticsAlgorithmFactory::fromRecord(const Record& r) { Int fieldNum = r.fieldNumber("algorithm"); ThrowIf(fieldNum < 0, "field 'algorithm' not defined"); // algorithm can be a string or int DataType dt = r.type(fieldNum); StatisticsData::ALGORITHM algorithm; if (dt == TpString) { String rAlg = r.asString(fieldNum); rAlg.downcase(); if (rAlg.startsWith("b")) { algorithm = StatisticsData::BIWEIGHT; } if (rAlg.startsWith("cl")) { algorithm = StatisticsData::CLASSICAL; } else if (rAlg.startsWith("ch")) { algorithm = StatisticsData::CHAUVENETCRITERION; } else if (rAlg.startsWith("f")) { algorithm = StatisticsData::FITTOHALF; } else if (rAlg.startsWith("h")) { algorithm = StatisticsData::HINGESFENCES; } else { ThrowCc("Unrecognized algorithm " + r.asString(fieldNum)); } } else if (dt == TpInt) { algorithm = (StatisticsData::ALGORITHM)r.asInt(fieldNum); } else { ThrowCc("Unsupported type for field 'algorithm'"); } StatisticsAlgorithmFactory saf; switch (algorithm) { case StatisticsData::BIWEIGHT: { ThrowIf(! r.isDefined("c"), "field 'c' is not defined"); ThrowIf(! r.isDefined("max_iter"), "field 'max_iter' is not defined"); auto c = r.asDouble("c"); auto maxIter = r.asInt("max_iter"); saf.configureBiweight(maxIter, c); return saf; } case StatisticsData::CLASSICAL: return saf; case StatisticsData::HINGESFENCES: { ThrowIf(! r.isDefined("hf"), "field 'hf' is not defined"); saf.configureHingesFences(r.asDouble("hf")); return saf; } case StatisticsData::FITTOHALF: { ThrowIf(! r.isDefined("center"), "field 'center' is not defined"); auto center = (FitToHalfStatisticsData::CENTER)r.asInt("center"); AccumType centerValue = 0; if (center == FitToHalfStatisticsData::CVALUE) { ThrowIf ( ! r.isDefined("center_value"), "field 'center_value' is not defined" ); r.get("center_value", centerValue); } ThrowIf(! r.isDefined("side"), "field 'side' is not defined"); auto side = (FitToHalfStatisticsData::USE_DATA)r.asInt("side"); saf.configureFitToHalf(center, side, centerValue); return saf; } case StatisticsData::CHAUVENETCRITERION: { ThrowIf(! r.isDefined("zscore"), "field 'zscore' is not defined"); ThrowIf(! r.isDefined("max_iter"), "field 'max_iter' is not defined"); auto zscore = r.asDouble("zscore"); auto maxIter = r.asInt("max_iter"); saf.configureChauvenet(zscore, maxIter); return saf; } default: ThrowCc( "Logic Error: Unhandled algorithm " + String::toString(algorithm) ); } } } #endif casacore-3.7.1/scimath/StatsFramework/StatisticsAlgorithmFactoryData.h000066400000000000000000000037531476623553700262340ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_STATSALGORITHMFACTORYDATA_H #define SCIMATH_STATSALGORITHMFACTORYDATA_H #include namespace casacore { // define data structures used by tatisticsAlgorithmFactory class StatisticsAlgorithmFactoryData { public: struct BiweightData { Int maxIter; Double c; }; StatisticsAlgorithmFactoryData() = delete; template struct FitToHalfData { FitToHalfStatisticsData::CENTER center; // fit to half data portion to use FitToHalfStatisticsData::USE_DATA side; // fit to half center value (only relevent if center=CVALUE) AccumType centerValue; }; struct ChauvenetData { Double zScore; Int maxIter; }; ~StatisticsAlgorithmFactoryData() {}; }; } #endif casacore-3.7.1/scimath/StatsFramework/StatisticsAlgorithmQuantileComputer.h000066400000000000000000000117351476623553700273330ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_STATSALGORITHMQUANTILECOMPUTER_H #define SCIMATH_STATSALGORITHMQUANTILECOMPUTER_H #include #include namespace casacore { // This is the virtual base class from which concrete QuantileComputer classes // are derived. The API developer should never explicitly instantiate a // QuantileComputer class; they are used internally by other StatsFramework // classes. See the documentation of StatisticsAlgorithm for more details. template < class AccumType, class DataIterator, class MaskIterator=const Bool *, class WeightsIterator=DataIterator > class StatisticsAlgorithmQuantileComputer { public: StatisticsAlgorithmQuantileComputer() = delete; virtual ~StatisticsAlgorithmQuantileComputer(); // clone this object by returning a pointer to a copy virtual StatisticsAlgorithmQuantileComputer* clone() const = 0; // delete any (partially) sorted array void deleteSortedArray(); // reset this object by clearing data. virtual void reset(); // This must be called upon the copy or assignment of the // associated statistics algorithm object. Otherwise, there is generally // no reason to call it. void setDataset(StatisticsDataset* ds); // FIXME make protected once refactor is complete std::vector& _getSortedArray() { return _sortedArray; } // FIXME make protected once refactor is complete void _setSortedArray(const std::vector& v) { _sortedArray = v; } void setMedian(std::shared_ptr median) { _median = std::move(median); } protected: // ds should be the dataset object held in the StatisticsAlgorithm object. // The QuantileComputer calculator object should never hold its own version // of a dataset object. The algorithm object (caller of this method) is // always responsible for deleting the passed object, usually upon its // destruction. StatisticsAlgorithmQuantileComputer(StatisticsDataset* ds); // use copy semantics. statistics algorithm object's responsibility to set // the _dataset object in the new QuantileComputer calculator object upon a // copy. The underlying _dataset object in the new stats calculator object // should be a reference to the new _dataset object in the copied statistics // algorithm object. StatisticsAlgorithmQuantileComputer( const StatisticsAlgorithmQuantileComputer& other ); // use copy semantics. The _dataset object is not copied. It is the // associated statistics algorithm object's responsibility to set the // _dataset object in the new QuantileComputer calculator object upon an // assignment. The underlying _dataset object in the new stats calculator // object should be a reference to that in the newly assigned statistics // algorithm object. StatisticsAlgorithmQuantileComputer& operator=( const StatisticsAlgorithmQuantileComputer& other ); StatisticsDataset* _getDataset() { return _dataset; } std::shared_ptr _getMedian() const { return _median; } std::shared_ptr _getMedianAbsDevMedian() const { return _medAbsDevMed; } void _setMedianAbsDevMedian(std::shared_ptr medAbsDevMed) { _medAbsDevMed = std::move(medAbsDevMed); } private: std::vector _sortedArray{}; // This pointer references the (non-pointer) object // in the associated non-QuantileComputer computer object, // so this should not be wrapped in a smart pointer. StatisticsDataset* _dataset{nullptr}; std::shared_ptr _median{}, _medAbsDevMed{}; }; } #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif #endif casacore-3.7.1/scimath/StatsFramework/StatisticsAlgorithmQuantileComputer.tcc000066400000000000000000000061061476623553700276510ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_STATSALGORITHMQUANTILECOMPUTER_TCC #define SCIMATH_STATSALGORITHMQUANTILECOMPUTER_TCC #include namespace casacore { CASA_STATD StatisticsAlgorithmQuantileComputer ::StatisticsAlgorithmQuantileComputer(StatisticsDataset* ds) : _dataset(ds) {} CASA_STATD StatisticsAlgorithmQuantileComputer ::~StatisticsAlgorithmQuantileComputer() {} // explicitly set _dataset to NULL, this needs to be // set by the caller upon a copy CASA_STATD StatisticsAlgorithmQuantileComputer ::StatisticsAlgorithmQuantileComputer( const StatisticsAlgorithmQuantileComputer& other ) : _sortedArray(other._sortedArray), _dataset(nullptr), _median(other._median ? new AccumType(*other._median) : nullptr), _medAbsDevMed( other._medAbsDevMed ? new AccumType(*other._medAbsDevMed) : nullptr ) {} CASA_STATD StatisticsAlgorithmQuantileComputer& StatisticsAlgorithmQuantileComputer::operator=( const StatisticsAlgorithmQuantileComputer& other ) { if (this == &other) { return *this; } _sortedArray = other._sortedArray; // explicitly set to NULL, this needs to be // set by the caller upon assignment _dataset = nullptr; _median.reset(other._median ? new AccumType(*other._median) : nullptr); _medAbsDevMed.reset( other._medAbsDevMed ? new AccumType(*other._medAbsDevMed) : nullptr ); return *this; } CASA_STATD void StatisticsAlgorithmQuantileComputer::deleteSortedArray() { _sortedArray.clear(); } CASA_STATD void StatisticsAlgorithmQuantileComputer::reset() { _sortedArray.clear(); _median.reset(); _medAbsDevMed.reset(); } CASA_STATD void StatisticsAlgorithmQuantileComputer::setDataset( StatisticsDataset* ds ) { _dataset = ds; } } #endif casacore-3.7.1/scimath/StatsFramework/StatisticsData.cc000066400000000000000000000055221476623553700231670ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include #include #include #include namespace casacore { String StatisticsData::toString(STATS stat) { switch(stat) { case MAX: return "max"; case MEAN: return "mean"; case MIN: return "min"; case NPTS: return "npts"; case RMS: return "rms"; case STDDEV: return "stddev"; case SUM: return "sum"; case SUMSQ: return "sumsq"; case SUMWEIGHTS: return "sumOfWeights"; case VARIANCE: return "variance"; case MEDIAN: return "median"; case MEDABSDEVMED: return "median of the absolute devation from the median"; case FIRST_QUARTILE: return "first quartile"; case THIRD_QUARTILE: return "third quartile"; case INNER_QUARTILE_RANGE: return "inner quartile range"; default: ThrowCc( "Logic error: Unhandled value in switch statement" + String::toString(stat) ); } } std::map StatisticsData::indicesFromFractions( uInt64 npts, const std::set& fractions ) { std::map fractionToIndexMap; for_each( fractions.cbegin(), fractions.cend(), [&fractionToIndexMap, &npts](Double q) { auto idxWRT1 = q * npts; auto myfloor = floor(idxWRT1); if (near(idxWRT1, myfloor)) { // prevent rounding due to finite machine precision idxWRT1 = myfloor; } fractionToIndexMap[q] = ((uInt64)ceil(idxWRT1) - 1); }); return fractionToIndexMap; } } casacore-3.7.1/scimath/StatsFramework/StatisticsData.h000066400000000000000000000045221476623553700230300ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_STATSISTICSDATA_H #define SCIMATH_STATSISTICSDATA_H #include #include #include #include namespace casacore { class String; // This class defines the enum of supported statistics types in the statistics // framework. class StatisticsData { public: // implemented algorithms enum ALGORITHM { BIWEIGHT, CHAUVENETCRITERION, CLASSICAL, FITTOHALF, HINGESFENCES }; enum STATS { MAX, MEAN, MIN, NPTS, RMS, STDDEV, SUM, SUMSQ, // sum of weights SUMWEIGHTS, VARIANCE, // commonly used quantile-related types MEDIAN, MEDABSDEVMED, FIRST_QUARTILE, THIRD_QUARTILE, // inner quartile range, Q3 - Q1 INNER_QUARTILE_RANGE }; // get the zero-based indices of the specified fractions in a CDF with npts // number of good points. The returned map maps fractions to indices. static std::map indicesFromFractions( uInt64 npts, const std::set& fractions ); static String toString(STATS stat); }; } #endif casacore-3.7.1/scimath/StatsFramework/StatisticsDataset.h000066400000000000000000000256551476623553700235560ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_STATISTICSDATASET_H #define SCIMATH_STATISTICSDATASET_H #include #include namespace casacore { // Representation of a statistics dataset used in statistics framework // calculatations. // // This class is used internally by StatisticsAlgorithm and its derived classes. // There should be no need for an API developer to make direct use of this // class. It encapsulates the data-related portions of StatisticsAlgorithm and // derived classes. To add and set data or to set a data provider, one should // call the relevant methods in StatisticsAlgorithm which have been left // unchanged for the convenience of the API developer. Those methods call the // analogous methods in this class (and the methods in StatisticsAlgorithm also // do necessary bookkeeping for the StatisticsAlgorithm and derived objects). template < class AccumType, class DataIterator, class MaskIterator=const Bool *, class WeightsIterator=DataIterator > class StatisticsDataset { public: // holds information about a data chunk. A data chunk is either an // individual underlying dataset (if no data provider), or a chunk of data // served by the data provider if it exists. struct ChunkData { // start of data DataIterator data; // total number of points uInt64 count; // data stride uInt dataStride; // associated ranges. If nullptr, then there are none. If not, the // second member of the pair indicates if they are include ranges. std::unique_ptr> ranges; // associated mask. If nullptr, then there is no mask. // If there is a mask, the second member is the mask stride. std::unique_ptr> mask; // associated weights. If nullptr, then there are no weights. std::unique_ptr weights; }; StatisticsDataset(); StatisticsDataset(const StatisticsDataset& other); ~StatisticsDataset(); // use copy semantics, except for the data provider which uses reference // semantics StatisticsDataset& operator=( const StatisticsDataset& other ); // // Add a dataset to an existing set of datasets on which statistics are to // be calculated. nr is the number of points to be considered. If // dataStride is greater than 1, when // nrAccountsForStride=True indicates that the stride has been // taken into account in the value of nr. Otherwise, it has not // so that the actual number of points to include is nr/dataStride if // nr % dataStride == 0 or (int)(nr/dataStride) + 1 otherwise. If one calls // this method after a data provider has been set, an exception will be // thrown. In this case, one should call setData(), rather than addData(), // to indicate that the underlying data provider should be removed. // dataRanges provide the ranges of data to include if // isInclude is True, or ranges of data to exclude if // isInclude is False. If a datum equals the end point of a data // range, it is considered good (included) if isInclude is True, // and it is considered bad (excluded) if isInclude is False. void addData( const DataIterator& first, uInt nr, uInt dataStride=1, Bool nrAccountsForStride=False ); void addData( const DataIterator& first, uInt nr, const DataRanges& dataRanges, Bool isInclude=True, uInt dataStride=1, Bool nrAccountsForStride=False ); void addData( const DataIterator& first, const MaskIterator& maskFirst, uInt nr, uInt dataStride=1, Bool nrAccountsForStride=False, uInt maskStride=1 ); void addData( const DataIterator& first, const MaskIterator& maskFirst, uInt nr, const DataRanges& dataRanges, Bool isInclude=True, uInt dataStride=1, Bool nrAccountsForStride=False, uInt maskStride=1 ); void addData( const DataIterator& first, const WeightsIterator& weightFirst, uInt nr, uInt dataStride=1, Bool nrAccountsForStride=False ); void addData( const DataIterator& first, const WeightsIterator& weightFirst, uInt nr, const DataRanges& dataRanges, Bool isInclude=True, uInt dataStride=1, Bool nrAccountsForStride=False ); void addData( const DataIterator& first, const WeightsIterator& weightFirst, const MaskIterator& maskFirst, uInt nr, uInt dataStride=1, Bool nrAccountsForStride=False, uInt maskStride=1 ); void addData( const DataIterator& first, const WeightsIterator& weightFirst, const MaskIterator& maskFirst, uInt nr, const DataRanges& dataRanges, Bool isInclude=True, uInt dataStride=1, Bool nrAccountsForStride=False, uInt maskStride=1 ); // // returns ! dataProvider && _data.empty() Bool empty() const; // get data counts associated with the underlying data sets const std::vector& getCounts() const { return _counts; } StatsDataProvider* getDataProvider() { return _dataProvider; } const StatsDataProvider* getDataProvider() const { return _dataProvider; } Int64 iDataset() const { return _idataset; } Bool increment(Bool includeIDataset); void incrementThreadIters( DataIterator& dataIter, MaskIterator& maskIter, WeightsIterator& weightsIter, uInt64& offset, uInt nthreads ) const; void initIterators(); // used for threaded methods void initLoopVars( uInt64& chunkCount, uInt& chunkStride, Bool& chunkHasRanges, DataRanges& chunkRanges, Bool& chunkIsIncludeRanges, Bool& chunkHasMask, uInt& chunkMaskStride, Bool& chunkHasWeights ); // used for unthreaded methods void initLoopVars( DataIterator& chunkData, uInt64& chunkCount, uInt& chunkStride, Bool& chunkHasRanges, DataRanges& chunkRanges, Bool& chunkIsIncludeRanges, Bool& chunkHasMask, MaskIterator& chunkMask, uInt& chunkMaskStride, Bool& chunkHasWeights, WeightsIterator& chunkWeights ); const ChunkData& initLoopVars(); void initThreadVars( uInt& nBlocks, uInt64& extra, uInt& nthreads, std::unique_ptr& dataIter, std::unique_ptr& maskIter, std::unique_ptr& weightsIter, std::unique_ptr& offset, uInt nThreadsMax ) const; void reset(); void resetIDataset() { _idataset = 0; } // // setdata() clears any current datasets or data provider and then adds the // specified data set as the first dataset in the (possibly new) set of data // sets for which statistics are to be calculated. See addData() for // parameter meanings. void setData( const DataIterator& first, uInt nr, uInt dataStride=1, Bool nrAccountsForStride=False ); void setData( const DataIterator& first, uInt nr, const DataRanges& dataRanges, Bool isInclude=True, uInt dataStride=1, Bool nrAccountsForStride=False ); void setData( const DataIterator& first, const MaskIterator& maskFirst, uInt nr, uInt dataStride=1, Bool nrAccountsForStride=False, uInt maskStride=1 ); void setData( const DataIterator& first, const MaskIterator& maskFirst, uInt nr, const DataRanges& dataRanges, Bool isInclude=True, uInt dataStride=1, Bool nrAccountsForStride=False, uInt maskStride=1 ); void setData( const DataIterator& first, const WeightsIterator& weightFirst, uInt nr, uInt dataStride=1, Bool nrAccountsForStride=False ); void setData( const DataIterator& first, const WeightsIterator& weightFirst, uInt nr, const DataRanges& dataRanges, Bool isInclude=True, uInt dataStride=1, Bool nrAccountsForStride=False ); void setData( const DataIterator& first, const WeightsIterator& weightFirst, const MaskIterator& maskFirst, uInt nr, uInt dataStride=1, Bool nrAccountsForStride=False, uInt maskStride=1 ); void setData( const DataIterator& first, const WeightsIterator& weightFirst, const MaskIterator& maskFirst, uInt nr, const DataRanges& dataRanges, Bool isInclude=True, uInt dataStride=1, Bool nrAccountsForStride=False, uInt maskStride=1 ); // // instead of setting and adding data "by hand", set the data provider that // will provide all the data sets. Calling this method will clear any other // data sets that have previously been set or added. void setDataProvider(StatsDataProvider *dataProvider); private: std::vector _data{}; // maps data to weights. maps are used rather than vectors because only some // (or none) of the data sets in the _data vector may have associated // weights, masks, and/or ranges. std::map _weights{}; // maps data to masks std::map _masks{}; std::vector _counts{}; std::vector _dataStrides{}; std::map _maskStrides{}; std::map _isIncludeRanges{}; std::map _dataRanges{}; StatsDataProvider* _dataProvider{nullptr}; Int64 _idataset{0}; typename std::vector::const_iterator _dend{}, _diter{}; std::vector::const_iterator _citer{}; std::vector::const_iterator _dsiter{}; uInt _dataCount{0}; ChunkData _chunk; void _throwIfDataProviderDefined() const; }; } #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif #endif casacore-3.7.1/scimath/StatsFramework/StatisticsDataset.tcc000066400000000000000000000352151476623553700240710ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_STATISTICSDATASET_TCC #define SCIMATH_STATISTICSDATASET_TCC #include #include namespace casacore { CASA_STATD StatisticsDataset::StatisticsDataset() {} CASA_STATD StatisticsDataset::StatisticsDataset(const StatisticsDataset& other) : _data(other._data), _weights(other._weights), _masks(other._masks), _counts(other._counts), _dataStrides(other._dataStrides), _maskStrides(other._maskStrides), _isIncludeRanges(other._isIncludeRanges), _dataRanges(other._dataRanges), // WARN reference semantics _dataProvider(other._dataProvider), _idataset(0), _dataCount(0), _chunk() {} CASA_STATD StatisticsDataset::~StatisticsDataset() {} CASA_STATD StatisticsDataset& StatisticsDataset::operator=( const StatisticsDataset& other ) { if (this == &other) { return *this; } _data = other._data; _weights = other._weights; _masks = other._masks; _counts = other._counts; _dataStrides = other._dataStrides; _maskStrides = other._maskStrides; _isIncludeRanges = other._isIncludeRanges; _dataRanges = other._dataRanges; // WARN reference semantics _dataProvider = other._dataProvider; _idataset = other._idataset; return *this; } CASA_STATD void StatisticsDataset::addData( const DataIterator& first, uInt nr, uInt dataStride, Bool nrAccountsForStride ) { _throwIfDataProviderDefined(); _data.push_back(first); // internally we store the number of strided points _counts.push_back( nrAccountsForStride ? nr : nr % dataStride == 0 ? nr/dataStride : nr/dataStride + 1 ); _dataStrides.push_back(dataStride); } CASA_STATD void StatisticsDataset::addData( const DataIterator& first, uInt nr, const DataRanges& dataRanges, Bool isInclude, uInt dataStride, Bool nrAccountsForStride ) { _throwIfDataProviderDefined(); for_each( dataRanges.cbegin(), dataRanges.cend(), [](const std::pair& range) { ThrowIf( range.first > range.second, "The first value in a range pair cannot be greater than the second" ); }); auto n = _data.size(); _isIncludeRanges[n] = isInclude; _dataRanges[n] = dataRanges; addData(first, nr, dataStride, nrAccountsForStride); } CASA_STATD void StatisticsDataset::addData( const DataIterator& first, const MaskIterator& maskFirst, uInt nr, uInt dataStride, Bool nrAccountsForStride, uInt maskStride ) { _throwIfDataProviderDefined(); uInt key = _data.size(); _maskStrides[key] = maskStride; _masks[key] = maskFirst; addData(first, nr, dataStride, nrAccountsForStride); } CASA_STATD void StatisticsDataset::addData( const DataIterator& first, const MaskIterator& maskFirst, uInt nr, const DataRanges& dataRanges, Bool isInclude, uInt dataStride, Bool nrAccountsForStride, uInt maskStride ) { _throwIfDataProviderDefined(); uInt key = _data.size(); _maskStrides[key] = maskStride; _masks[key] = maskFirst; addData( first, nr, dataRanges, isInclude, dataStride, nrAccountsForStride ); } CASA_STATD void StatisticsDataset::addData( const DataIterator& first, const WeightsIterator& weightFirst, uInt nr, uInt dataStride, Bool nrAccountsForStride ) { _throwIfDataProviderDefined(); _weights[_data.size()] = weightFirst; addData(first, nr, dataStride, nrAccountsForStride); } CASA_STATD void StatisticsDataset::addData( const DataIterator& first, const WeightsIterator& weightFirst, uInt nr, const DataRanges& dataRanges, Bool isInclude, uInt dataStride, Bool nrAccountsForStride ) { _throwIfDataProviderDefined(); _weights[_data.size()] = weightFirst; addData( first, nr, dataRanges, isInclude, dataStride, nrAccountsForStride ); } CASA_STATD void StatisticsDataset::addData( const DataIterator& first, const WeightsIterator& weightFirst, const MaskIterator& maskFirst, uInt nr, uInt dataStride, Bool nrAccountsForStride, uInt maskStride ) { _throwIfDataProviderDefined(); _weights[_data.size()] = weightFirst; addData( first, maskFirst, nr, dataStride, nrAccountsForStride, maskStride ); } CASA_STATD void StatisticsDataset::addData( const DataIterator& first, const WeightsIterator& weightFirst, const MaskIterator& maskFirst, uInt nr, const DataRanges& dataRanges, Bool isInclude, uInt dataStride, Bool nrAccountsForStride, uInt maskStride ) { _throwIfDataProviderDefined(); _weights[_data.size()] = weightFirst; addData( first, maskFirst, nr, dataRanges, isInclude, dataStride, nrAccountsForStride, maskStride ); } CASA_STATD Bool StatisticsDataset::empty() const { return ! _dataProvider && _data.empty(); } CASA_STATD Bool StatisticsDataset::increment(Bool includeIDataset) { if (includeIDataset) { ++_idataset; } if (_dataProvider) { ++(*_dataProvider); if (_dataProvider->atEnd()) { _dataProvider->finalize(); return True; } } else { ++_diter; if (_diter == _dend) { return True; } ++_citer; ++_dsiter; ++_dataCount; } return False; } CASA_STATD void StatisticsDataset::incrementThreadIters( DataIterator& dataIter, MaskIterator& maskIter, WeightsIterator& weightsIter, uInt64& offset, uInt nthreads ) const { auto increment = nthreads * ClassicalStatisticsData::BLOCK_SIZE * _chunk.dataStride; if (offset+increment >= _chunk.count*_chunk.dataStride) { // necessary because in some cases std::advance will segfault // if advanced past the end of the data structure return; } std::advance(dataIter, increment); if (_chunk.weights) { std::advance(weightsIter, increment); } if (_chunk.mask) { std::advance( maskIter, nthreads*ClassicalStatisticsData::BLOCK_SIZE*_chunk.mask->second ); } offset += increment; } CASA_STATD void StatisticsDataset::initIterators() { ThrowIf(empty(), "No data sets have been added"); if (_dataProvider) { _dataProvider->reset(); } else { _dataCount = 0; _diter = _data.begin(); _dend = _data.end(); _dsiter = _dataStrides.begin(); _citer = _counts.begin(); } _chunk.ranges.reset(); _chunk.mask.reset(); _chunk.weights.reset(); } CASA_STATD const typename StatisticsDataset::ChunkData& StatisticsDataset::initLoopVars() { if (_dataProvider) { _chunk.data = _dataProvider->getData(); _chunk.count = _dataProvider->getCount(); _chunk.dataStride = _dataProvider->getStride(); _chunk.ranges.reset( _dataProvider->hasRanges() ? new std::pair( _dataProvider->getRanges(), _dataProvider->isInclude() ) : nullptr ); _chunk.mask.reset( _dataProvider->hasMask() ? new std::pair( _dataProvider->getMask(), _dataProvider->getMaskStride() ) : nullptr ); _chunk.weights.reset( _dataProvider->hasWeights() ? new WeightsIterator(_dataProvider->getWeights()) : nullptr ); } else { _chunk.data = *_diter; _chunk.count = *_citer; _chunk.dataStride = *_dsiter; auto rangeI = _dataRanges.find(_dataCount); _chunk.ranges.reset( rangeI == _dataRanges.end() ? nullptr : new std::pair( rangeI->second, _isIncludeRanges.find(_dataCount)->second ) ); auto maskI = _masks.find(_dataCount); _chunk.mask.reset( maskI == _masks.end() ? nullptr : new std::pair( maskI->second, _maskStrides.find(_dataCount)->second ) ); _chunk.weights.reset( _weights.find(_dataCount) == _weights.end() ? nullptr : new WeightsIterator(_weights.find(_dataCount)->second) ); } return _chunk; } CASA_STATD void StatisticsDataset::initThreadVars( uInt& nBlocks, uInt64& extra, uInt& nthreads, std::unique_ptr& dataIter, std::unique_ptr& maskIter, std::unique_ptr& weightsIter, std::unique_ptr& offset, uInt nThreadsMax ) const { ThrowIf(nThreadsMax == 0, "Logic error: nThreadsMax should never be 0"); auto n = ClassicalStatisticsData::CACHE_PADDING*nThreadsMax; dataIter.reset(new DataIterator[n]); maskIter.reset(new MaskIterator[n]); weightsIter.reset(new WeightsIterator[n]); offset.reset(new uInt64[n]); nBlocks = _chunk.count/ClassicalStatisticsData::BLOCK_SIZE; extra = _chunk.count % ClassicalStatisticsData::BLOCK_SIZE; if (extra > 0) { ++nBlocks; } ThrowIf(nBlocks == 0, "Logic error: nBlocks should never be 0"); nthreads = std::min(nThreadsMax, nBlocks); ThrowIf(nthreads == 0, "Logic error: nthreads should never be 0"); for (uInt tid=0; tidfirst; std::advance( maskIter[idx8], tid*ClassicalStatisticsData::BLOCK_SIZE*_chunk.mask->second ); } } } CASA_STATD void StatisticsDataset::reset() { _data.clear(); _counts.clear(); _masks.clear(); _weights.clear(); _dataRanges.clear(); _dataStrides.clear(); _maskStrides.clear(); _dataProvider = nullptr; } CASA_STATD void StatisticsDataset::setData( const DataIterator& first, uInt nr, uInt dataStride, Bool nrAccountsForStride ) { reset(); addData(first, nr, dataStride, nrAccountsForStride); } CASA_STATD void StatisticsDataset::setData( const DataIterator& first, uInt nr, const DataRanges& dataRanges, Bool isInclude, uInt dataStride, Bool nrAccountsForStride ) { reset(); addData( first, nr, dataRanges, isInclude, dataStride, nrAccountsForStride ); } CASA_STATD void StatisticsDataset::setData( const DataIterator& first, const MaskIterator& maskFirst, uInt nr, uInt dataStride, Bool nrAccountsForStride, uInt maskStride ) { reset(); addData( first, maskFirst, nr, dataStride, nrAccountsForStride, maskStride ); } CASA_STATD void StatisticsDataset::setData( const DataIterator& first, const MaskIterator& maskFirst, uInt nr, const DataRanges& dataRanges, Bool isInclude, uInt dataStride, Bool nrAccountsForStride, uInt maskStride ) { reset(); addData( first, maskFirst, nr, dataRanges, isInclude, dataStride, nrAccountsForStride, maskStride ); } CASA_STATD void StatisticsDataset::setData( const DataIterator& first, const WeightsIterator& weightFirst, uInt nr, uInt dataStride, Bool nrAccountsForStride ) { reset(); addData(first, weightFirst, nr, dataStride, nrAccountsForStride); } CASA_STATD void StatisticsDataset::setData( const DataIterator& first, const WeightsIterator& weightFirst, uInt nr, const DataRanges& dataRanges, Bool isInclude, uInt dataStride, Bool nrAccountsForStride ) { reset(); addData( first, weightFirst, nr, dataRanges, isInclude, dataStride, nrAccountsForStride ); } CASA_STATD void StatisticsDataset::setData( const DataIterator& first, const WeightsIterator& weightFirst, const MaskIterator& maskFirst, uInt nr, uInt dataStride, Bool nrAccountsForStride, uInt maskStride ) { reset(); addData( first, weightFirst, maskFirst, nr, dataStride, nrAccountsForStride, maskStride ); } CASA_STATD void StatisticsDataset::setData( const DataIterator& first, const WeightsIterator& weightFirst, const MaskIterator& maskFirst, uInt nr, const DataRanges& dataRanges, Bool isInclude, uInt dataStride, Bool nrAccountsForStride, uInt maskStride ) { reset(); addData( first, weightFirst, maskFirst, nr, dataRanges, isInclude, dataStride, nrAccountsForStride, maskStride ); } CASA_STATD void StatisticsDataset::setDataProvider( StatsDataProvider *dataProvider ) { ThrowIf(! dataProvider, "Logic Error: data provider cannot be nullptr"); reset(); _dataProvider = dataProvider; } CASA_STATD void StatisticsDataset::_throwIfDataProviderDefined() const { ThrowIf( _dataProvider, "Logic Error: Cannot add data after a data provider has been set. Call " "setData() to clear the existing data provider and to add this new " "data set" ); } } #endif casacore-3.7.1/scimath/StatsFramework/StatisticsIncrementer.h000066400000000000000000000053741476623553700244400ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_STATISTICSINCREMENTER_H #define SCIMATH_STATISTICSINCREMENTER_H #include namespace casacore { // Utility functions used for incrementing pointers in a data set used by the // stats framework. template < class DataIterator, class MaskIterator=const Bool *, class WeightsIterator=DataIterator > class StatisticsIncrementer { public: StatisticsIncrementer() = delete; ~StatisticsIncrementer() {} // // loopCount is always incremented by one, independent of the // value of dataStride and maskStride inline static void increment( DataIterator& datum, uInt64& loopCount, uInt dataStride ) { std::advance(datum, dataStride); ++loopCount; } inline static void increment( DataIterator& datum, uInt64& loopCount, WeightsIterator& weight, uInt dataStride ) { std::advance(datum, dataStride); std::advance(weight, dataStride); ++loopCount; } inline static void increment( DataIterator& datum, uInt64& loopCount, MaskIterator& mask, uInt dataStride, uInt maskStride ) { std::advance(datum, dataStride); std::advance(mask, maskStride); ++loopCount; } inline static void increment( DataIterator& datum, uInt64& loopCount, WeightsIterator& weight, MaskIterator& mask, uInt dataStride, uInt maskStride ) { std::advance(datum, dataStride); std::advance(weight, dataStride); std::advance(mask, maskStride); ++loopCount; } // }; } #endif casacore-3.7.1/scimath/StatsFramework/StatisticsTypes.h000066400000000000000000000054051476623553700232640ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_STATISTICSTYPES_H #define SCIMATH_STATISTICSTYPES_H #include #include #include #include // because the template signature has become unwieldy #define CASA_STATD template < \ class AccumType, class DataIterator, class MaskIterator, \ class WeightsIterator \ > #define CASA_STATP AccumType, DataIterator, MaskIterator, WeightsIterator #define CASA_STATQ DataIterator, MaskIterator, WeightsIterator namespace casacore { class Record; // Commonly used types in statistics framework. #define DataArray std::vector #define DataRanges std::vector> #define IncludeLimits std::vector> using BinCountArray = std::vector; using LocationType = std::pair; template struct StatsData { Bool masked; std::shared_ptr max; LocationType maxpos; AccumType mean; std::shared_ptr median; std::shared_ptr medAbsDevMed; std::shared_ptr min; LocationType minpos; Double npts; AccumType nvariance; AccumType rms; AccumType stddev; AccumType sum; AccumType sumsq; AccumType sumweights; AccumType variance; Bool weighted; }; template StatsData initializeStatsData(); template StatsData copy(const StatsData& stats); template Record toRecord(const StatsData& stats); } #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/StatsFramework/StatisticsTypes.tcc000066400000000000000000000070361476623553700236100ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_STATISTICSTYPES_TCC #define SCIMATH_STATISTICSTYPES_TCC #include #include #include #include namespace casacore { template StatsData initializeStatsData() { StatsData init = { False, nullptr, std::pair(-1, -1), 0, nullptr, nullptr, nullptr, std::pair(-1, -1), 0, 0, 0, 0, 0, 0, 0, 0, False }; return init; } template StatsData copy(const StatsData& stats) { StatsData mycopy = stats; if (mycopy.max) { mycopy.max.reset (new AccumType(*mycopy.max)); } if (mycopy.median) { mycopy.median.reset (new AccumType(*mycopy.median)); } if (mycopy.medAbsDevMed) { mycopy.medAbsDevMed.reset (new AccumType(*mycopy.medAbsDevMed)); } if (mycopy.min) { mycopy.min.reset (new AccumType(*mycopy.min)); } return mycopy; } template Record toRecord(const StatsData& stats) { Record r; r.define("isMasked", stats.masked); r.define("isWeighted", stats.weighted); if (stats.weighted) { r.define( StatisticsData::toString(StatisticsData::SUMWEIGHTS), stats.sumweights ); } r.define( StatisticsData::toString(StatisticsData::MEAN), stats.mean ); r.define( StatisticsData::toString(StatisticsData::NPTS), stats.npts ); r.define( StatisticsData::toString(StatisticsData::RMS), stats.rms ); r.define( StatisticsData::toString(StatisticsData::STDDEV), stats.stddev ); r.define( StatisticsData::toString(StatisticsData::SUM), stats.sum ); r.define( StatisticsData::toString(StatisticsData::SUMSQ), stats.sumsq ); r.define( StatisticsData::toString(StatisticsData::SUMWEIGHTS), stats.sumweights ); r.define( StatisticsData::toString(StatisticsData::VARIANCE), stats.variance ); if (stats.max) { r.define( StatisticsData::toString(StatisticsData::MAX), *stats.max ); r.define("maxDatasetIndex", stats.maxpos.first); r.define("maxIndex", stats.maxpos.second); } if (stats.min) { r.define( StatisticsData::toString(StatisticsData::MIN), *stats.min ); r.define("minDatasetIndex", stats.minpos.first); r.define("minIndex", stats.minpos.second); } return r; } } #endif casacore-3.7.1/scimath/StatsFramework/StatisticsUtilities.h000066400000000000000000000206571476623553700241410ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_STATISTICSUTILITIES_H #define SCIMATH_STATISTICSUTILITIES_H #include #include #include #include #include #include #include namespace casacore { CASA_STATD class StatsDataProvider; // Various statistics related methods for the statistics framework. template class StatisticsUtilities { public: StatisticsUtilities() = delete; ~StatisticsUtilities() {} // // accumulate values. It is the responsibility of the caller to keep track // of the accumulated values after each call. This class does not since it // has no state. The accumulation derivation for mean and variance can be // found at // www.itl.nist.gov/div898/software/dataplot/refman2/ch2/weighvar.pdf // nvariance is an accumulated value. It is related to the variance via // variance = nvariance/npts or nvariance/(npts-1) depending on your // preferred definition in the non-weighted case and // wvariance = wnvariance/sumofweights or wnvariance/(sumofweights-1) // in the weighted case Its basic definition is // nvariance = sum((x_i - mean)**2), // wnvariance = sum((weight_i*(x_i - mean)**2) // npts is a Double rather than an Int64 because of compilation issues when // T is a Complex inline static void accumulate ( Double& npts, AccumType& sum, AccumType& mean, const AccumType& datum ); // in order to optimize performance, no checking is done for the // weight == 0 case callers should ensure that the weigth is not zero before // calling this method, and shouldn't call this method if the weight is 0. // Expect a segfault because of division by zero if sumweights and weight // are both zero. inline static void waccumulate ( Double& npts, AccumType& sumweights, AccumType& wsum, AccumType& wmean, const AccumType& datum, const AccumType& weight ); inline static void accumulate ( Double& npts, AccumType& sum, AccumType& mean, AccumType& nvariance, AccumType& sumsq, const AccumType& datum ); // wsumsq is the weighted sum of squares, sum(w_i*x_i*x_i) inline static void waccumulate ( Double& npts, AccumType& sumweights, AccumType& wsum, AccumType& wmean, AccumType& wnvariance, AccumType& wsumsq, const AccumType& datum, const AccumType& weight ); // // // The assignment operator of class LocationType should use copy, not // reference, semantics. template inline static void accumulate ( Double& npts, AccumType& sum, AccumType& mean, AccumType& nvariance, AccumType& sumsq, AccumType& datamin, AccumType& datamax, LocationType& minpos, LocationType& maxpos, const AccumType& datum, const LocationType& location ); template inline static void accumulate ( Double& npts, AccumType& sum, AccumType& mean, AccumType& nvariance, AccumType& sumsq, DataType& datamin, DataType& datamax, LocationType& minpos, LocationType& maxpos, const DataType& datum, const LocationType& location ); template inline static void waccumulate ( Double& npts, AccumType& sumofweights, AccumType& sum, AccumType& mean, AccumType& nvariance, AccumType& sumsq, AccumType& datamin, AccumType& datamax, LocationType& minpos, LocationType& maxpos, const AccumType& datum, const AccumType& weight, const LocationType& location ); // // // return True if the max or min was updated, False otherwise. template inline static Bool doMax( AccumType& datamax, LocationType& maxpos, Bool isFirst, const AccumType& datum, const LocationType& location ); template inline static Bool doMin( AccumType& datamin, LocationType& minpos, Bool isFirst, const AccumType& datum, const LocationType& location ); // // // These versions are for symmetric accumulation about a specified center // point. The actual point is accumulated, as is a "virtual" point that is // symmetric about the specified center. Of course, the trivial relationship // that the mean is the specified center is used to simplify things inline static void accumulateSym ( Double& npts, AccumType& nvariance, AccumType& sumsq, const AccumType& datum, const AccumType& center ); // wsumsq is the weighted sum of squares, sum(w_i*x_i*x_i) inline static void waccumulateSym ( Double& npts, AccumType& sumweights, AccumType& wnvariance, AccumType& wsumsq, const AccumType& datum, const AccumType& weight, const AccumType& center ); // maxpos and minpos refer to actual, not // virtually created, data only. template inline static void accumulateSym ( Double& npts, AccumType& nvariance, AccumType& sumsq, AccumType& datamin, AccumType& datamax, LocationType& minpos, LocationType& maxpos, const AccumType& datum, const LocationType& location, const AccumType& center ); template inline static void waccumulateSym ( Double& npts, AccumType& sumofweights, AccumType& nvariance, AccumType& sumsq, AccumType& datamin, AccumType& datamax, LocationType& minpos, LocationType& maxpos, const AccumType& datum, const AccumType& weight, const LocationType& location, const AccumType& center ); // convert in place by taking the absolute value of the difference of the // std::vector and the median inline static void convertToAbsDevMedArray( DataArray& myArray, AccumType median ); // inline static Bool includeDatum( const AccumType& datum, typename DataRanges::const_iterator beginRange, typename DataRanges::const_iterator endRange, Bool isInclude ); // The array can be changed by partially sorting it up to the largest index. // Return a map of index to value in the sorted array. static std::map indicesToValues( std::vector& myArray, const std::set& indices ); static void mergeResults( std::vector& bins, std::vector>& sameVal, std::vector& allSame, const std::unique_ptr[]>& tBins, const std::unique_ptr>[]>& tSameVal, const std::unique_ptr[]>& tAllSame, uInt nThreadsMax ); // use two statistics sets to get the statistics set that would // result in combining the two data sets used to produce the // individual statistics sets. The quantile related stats are // not considered, since it is not in general possible to determine // the resultant quantiles from the information provided; only // the aggregate statistics make sense. static StatsData combine( const std::vector>& stats ); template static uInt nThreadsMax( const StatsDataProvider *const dataProvider ); static uInt threadIdx(); private: const static AccumType TWO; }; } #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/StatsFramework/StatisticsUtilities.tcc000066400000000000000000000333251476623553700244570ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_STATISTICSUTILITIES_TCC #define SCIMATH_STATISTICSUTILITIES_TCC #include #include #include #include #include namespace casacore { template const AccumType StatisticsUtilities::TWO = AccumType(2); // For performance reasons, we ensure code is inlined rather than // calling other functions. The performance // benefits become important for very large datasets #define _NLINEAR \ npts++; \ sum += datum; \ mean += (datum - mean)/npts; #define _WLINEAR \ npts++; \ sumweights += weight; \ wsum += weight*datum; \ wmean += weight/sumweights*(datum - wmean); #define _NQUAD \ sumsq += datum*datum; \ auto prevMean = mean; \ _NLINEAR \ nvariance += (datum - prevMean)*(datum - mean); #define _WQUAD \ wsumsq += weight*datum*datum; \ auto prevMean = wmean; \ _WLINEAR \ wnvariance += weight*(datum - prevMean)*(datum - wmean); #define _MAXMIN \ if (npts == 1) { \ datamax = datum; \ maxpos = location; \ datamin = datum; \ minpos = location; \ } \ else if (datum > datamax) { \ datamax = datum; \ maxpos = location; \ } \ else if (datum < datamin) { \ datamin = datum; \ minpos = location; \ } template void StatisticsUtilities::accumulate ( Double& npts, AccumType& sum, AccumType& mean, const AccumType& datum ) { _NLINEAR } template void StatisticsUtilities::waccumulate ( Double& npts, AccumType& sumweights, AccumType& wsum, AccumType& wmean, const AccumType& datum, const AccumType& weight ) { _WLINEAR } template void StatisticsUtilities::accumulate ( Double& npts, AccumType& sum, AccumType& mean, AccumType& nvariance, AccumType& sumsq, const AccumType& datum ) { _NQUAD } template void StatisticsUtilities::waccumulate ( Double& npts, AccumType& sumweights, AccumType& wsum, AccumType& wmean, AccumType& wnvariance, AccumType& wsumsq, const AccumType& datum, const AccumType& weight ) { _WQUAD } template template void StatisticsUtilities::accumulate ( Double& npts, AccumType& sum, AccumType& mean, AccumType& nvariance, AccumType& sumsq, AccumType& datamin, AccumType& datamax, LocationType& minpos, LocationType& maxpos, const AccumType& datum, const LocationType& location ) { _NQUAD _MAXMIN } template template void StatisticsUtilities::accumulate ( Double& npts, AccumType& sum, AccumType& mean, AccumType& nvariance, AccumType& sumsq, DataType& datamin, DataType& datamax, LocationType& minpos, LocationType& maxpos, const DataType& datum, const LocationType& location ) { _NQUAD _MAXMIN } template template void StatisticsUtilities::waccumulate ( Double& npts, AccumType& sumweights, AccumType& wsum, AccumType& wmean, AccumType& wnvariance, AccumType& wsumsq, AccumType& datamin, AccumType& datamax, LocationType& minpos, LocationType& maxpos, const AccumType& datum, const AccumType& weight, const LocationType& location ) { _WQUAD _MAXMIN } template template Bool StatisticsUtilities::doMax( AccumType& datamax, LocationType& maxpos, Bool isFirst, const AccumType& datum, const LocationType& location ) { if (isFirst || datum > datamax) { datamax = datum; maxpos = location; return True; } return False; } template template Bool StatisticsUtilities::doMin( AccumType& datamin, LocationType& minpos, Bool isFirst, const AccumType& datum, const LocationType& location ) { if (isFirst || datum < datamin) { datamin = datum; minpos = location; return True; } return False; } #define _NQUADSYM \ npts += 2; \ auto reflect = TWO*center - datum; \ sumsq += datum*datum + reflect*reflect; \ auto diff = datum - center; \ nvariance += TWO*diff*diff; #define _WQUADSYM \ npts += 2; \ sumweights += TWO*weight; \ auto reflect = TWO*center - datum; \ wsumsq += weight*(datum*datum + reflect*reflect); \ auto diff = datum - center; \ wnvariance += TWO*weight*diff*diff; #define _MAXMINSYM \ if (npts == 2) { \ datamax = datum; \ maxpos = location; \ datamin = datum; \ minpos = location; \ } \ else if (datum > datamax) { \ datamax = datum; \ maxpos = location; \ } \ else if (datum < datamin) { \ datamin = datum; \ minpos = location; \ } template void StatisticsUtilities::accumulateSym ( Double& npts, AccumType& nvariance, AccumType& sumsq, const AccumType& datum, const AccumType& center ) { _NQUADSYM } template void StatisticsUtilities::waccumulateSym ( Double& npts, AccumType& sumweights, AccumType& wnvariance, AccumType& wsumsq, const AccumType& datum, const AccumType& weight, const AccumType& center ) { _WQUADSYM } template template void StatisticsUtilities::accumulateSym ( Double& npts, AccumType& nvariance, AccumType& sumsq, AccumType& datamin, AccumType& datamax, LocationType& minpos, LocationType& maxpos, const AccumType& datum, const LocationType& location, const AccumType& center ) { _NQUADSYM _MAXMINSYM } template template void StatisticsUtilities::waccumulateSym ( Double& npts, AccumType& sumweights, AccumType& wnvariance, AccumType& wsumsq, AccumType& datamin, AccumType& datamax, LocationType& minpos, LocationType& maxpos, const AccumType& datum, const AccumType& weight, const LocationType& location, const AccumType& center ) { _WQUADSYM _MAXMINSYM } template Bool StatisticsUtilities::includeDatum( const AccumType& datum, typename DataRanges::const_iterator beginRange, typename DataRanges::const_iterator endRange, Bool isInclude ) { // can't use a lambda because the loop can end early via return for (auto iter=beginRange; iter!=endRange; ++iter) { if (datum >= iter->first && datum <= iter->second) { return isInclude; } } return ! isInclude; } template void StatisticsUtilities::convertToAbsDevMedArray( DataArray& myArray, AccumType median ) { for_each(myArray.begin(), myArray.end(), [median](AccumType& datum) { datum = abs(datum - median); }); } template std::map StatisticsUtilities::indicesToValues( DataArray& myArray, const std::set& indices ) { auto arySize = myArray.size(); ThrowIf( *indices.rbegin() >= arySize, "Logic Error: Index " + String::toString(*indices.rbegin()) + " is too " "large. The sorted array has size " + String::toString(arySize) ); std::map indexToValuesMap; uInt64 lastIndex = 0; for_each( indices.cbegin(), indices.cend(), [&myArray, &lastIndex, &arySize](uInt64 index) { GenSort::kthLargest( &myArray[lastIndex], arySize - lastIndex, index - lastIndex ); lastIndex = index; }); for_each( indices.cbegin(), indices.cend(), [&myArray, &indexToValuesMap](uInt64 index) { indexToValuesMap[index] = myArray[index]; }); return indexToValuesMap; } template void StatisticsUtilities::mergeResults( std::vector& bins, std::vector>& sameVal, std::vector& allSame, const std::unique_ptr[]>& tBins, const std::unique_ptr>[]>& tSameVal, const std::unique_ptr[]>& tAllSame, uInt nThreadsMax ) { // merge results from individual threads (tBins, tSameVal, tAllSame) // into single data structures (bins, sameVal, allSame) for (uInt tid=0; tidbegin(), bcArray.begin(), std::plus() ); ++titer; }); //typename std::vector>::iterator siter; //auto send = sameVal.end(); std::vector::iterator aiter = allSame.begin(); auto viter = tSameVal[idx8].cbegin(); auto witer = tAllSame[idx8].cbegin(); for_each( sameVal.begin(), sameVal.end(), [&aiter, &viter, &witer](std::shared_ptr& svalue) { if (! *aiter) { // won't have the same values, do nothing } if (*witer && *aiter) { if ( !*viter || (svalue && *svalue == *(*viter)) ) { // no unflagged values in this chunk or both // have the all the same values, do nothing } else if (!svalue) { svalue.reset(new AccumType(*(*viter))); } else { // both are not null, and they do not have the same values svalue.reset(); *aiter = False; } } else { // *aiter = True, *witer = False, all values are not the same svalue.reset(); *aiter = False; } ++aiter; ++viter; ++witer; }); } } template StatsData StatisticsUtilities::combine( const std::vector>& stats ) { auto n = stats.size(); auto res = n == 1 ? stats[0] : initializeStatsData(); if (n == 0) { // null set return res; } static const AccumType zero = 0; static const AccumType one = 1; if (n > 1) { for_each( stats.cbegin(), stats.cend(), [&res](const StatsData& s) { if (s.max && (!res.max || *(s.max) > *res.max)) { // pointer copy res.max = s.max; res.maxpos = s.maxpos; } if (s.min && (!res.min || *(s.min) < *res.min)) { // pointer copy res.min = s.min; res.minpos = s.minpos; } auto sumweights = s.sumweights + res.sumweights; auto mean = sumweights == zero ? zero : (s.sumweights*s.mean + res.sumweights*res.mean)/sumweights; auto nvariance = zero; if (sumweights > zero) { auto diff1 = s.mean - mean; auto diff2 = res.mean - mean; nvariance = s.nvariance + res.nvariance + s.sumweights*diff1*diff1 + res.sumweights*diff2*diff2; } res.masked = s.masked || res.masked; res.mean = mean; res.npts += s.npts; res.nvariance = nvariance; res.sum += s.sum; res.sumsq += s.sumsq; res.sumweights = sumweights; res.weighted = s.weighted || res.weighted; }); } // In the n = 1 case, the stats which are computed from other stats are // not guaranteed to be in stats[0], so compute and fill them here, also // compute them for the n > 1 case // in any reasonable statistical dataset, sumsq should be zero if // sumweights is 0 res.variance = res.sumweights > one ? res.nvariance/(res.sumweights - one) : 0; res.rms = res.sumweights == zero ? zero : sqrt(res.sumsq/res.sumweights); res.stddev = sqrt(res.variance); return res; } template template uInt StatisticsUtilities::nThreadsMax( const StatsDataProvider *const dataProvider ) { auto nthr = OMP::nMaxThreads(); if (nthr > 1 && dataProvider) { auto n = dataProvider->getNMaxThreads(); if (n > 0) { return n; } } return nthr; } template uInt StatisticsUtilities::threadIdx() { #ifdef _OPENMP uInt tid = omp_get_thread_num(); #else uInt tid = 0; #endif return tid * ClassicalStatisticsData::CACHE_PADDING; } } #endif casacore-3.7.1/scimath/StatsFramework/StatsDataProvider.h000066400000000000000000000117241476623553700235110ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_STATSDATAPROVIDER_H #define SCIMATH_STATSDATAPROVIDER_H #include #include namespace casacore { // Abstract base class which defines interface for providing "datasets" to the // statistics framework in cases where the data structure involved does not // allow for a trivial means of doing so (eg, in the case of a Lattice). template < class AccumType, class DataIterator, class MaskIterator=const Bool *, class WeightsIterator=DataIterator > class StatsDataProvider { public: virtual ~StatsDataProvider(); // increment the data provider to the next dataset, mask, range set, and // weights. virtual void operator++() = 0; // Are there any data sets left to provide? virtual Bool atEnd() const = 0; // Take any actions necessary to finalize the provider. This will be called // when atEnd() returns True. virtual void finalize() = 0; // get the count of elements in the current data set. When implementing this // method, be certain to take stride into account; ie for a data set with // nominally 100 elements that is to have a stride of two, this method // should return 50. virtual uInt64 getCount() = 0; // get an iterator to the first element of the current dataset virtual DataIterator getData() = 0; // Get an iterator to the first element of the mask for the current dataset. // Only called if hasMask() returns True; virtual MaskIterator getMask() = 0; // Get the stride for the current mask. // Only called if hasMask() returns True. virtual uInt getMaskStride() = 0; // If OpenMP is enabled and statistics methods are being called in a // multi-threaded context, get maximum number of threads that should be // used. If zero is returned, the statistics classes will use the maximum // number of threads available to openmp. Returning less than that helps to // decrease overhead used by statistics methods when the maximum number of // threads available to openmp are unnecessary. The base class // implmentation returns 0. virtual uInt getNMaxThreads() const; // Get the associated range(s) of the current dataset. Only called if // hasRanges() returns True; virtual DataRanges getRanges() = 0; // Get the stride for the current data set. virtual uInt getStride() = 0; // Get an iterator to the first weights element of the current dataset. // Only called if hasWeights() returns True; virtual WeightsIterator getWeights() = 0; // Does the current data set have an associated mask? virtual Bool hasMask() const = 0; // Does the current data set have associated range(s)? virtual Bool hasRanges() const = 0; // Does the current data set have associated weights? virtual Bool hasWeights() const = 0; // If the associated data set has ranges, are these include (return True) or // exclude (return False) ranges? virtual Bool isInclude() const = 0; // reset the provider to point to the beginning of the first data set it // manages. virtual void reset() = 0; // // In general, unless you are writing statistics algorithm code, you // shouldn't need to call these methods. // The statistics framework calls these methods when the min and max // posiitons are updated. It passes in the relevant index of the current sub // dataset it is processing. Data providers can use this information to // transform into something more useful, eg an IPosition for lattice data // providers, so that they may be retreived easily after statistics have // been calculated. The default implementations do nothing. virtual void updateMaxPos(const LocationType&) {} virtual void updateMinPos(const LocationType&) {} // protected: StatsDataProvider(); }; } #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/StatsFramework/StatsDataProvider.tcc000066400000000000000000000030341476623553700240260ustar00rootroot00000000000000//# Copyright (C) 2014 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_STATSDATAPROVIDER_TCC #define SCIMATH_STATSDATAPROVIDER_TCC #include namespace casacore { CASA_STATD StatsDataProvider::StatsDataProvider() {} CASA_STATD StatsDataProvider::~StatsDataProvider() {} CASA_STATD uInt StatsDataProvider::getNMaxThreads() const { return 0; } } #endif casacore-3.7.1/scimath/StatsFramework/StatsHistogram.h000066400000000000000000000106571476623553700230660ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_STATSHISTOGRAM_H #define SCIMATH_STATSHISTOGRAM_H #include #include #include #include #include #include namespace casacore { // Represents an unfilled histogram with equal width bins for binning used for // quantile computations. It is necessary to store the min/max values of the bin // limits, because machine precision issues when the bin width is sufficiently // small can cause slightly different results for these when different methods // are used to compute them, leading to accounting errors when the histogram is // filled with data. template class StatsHistogram { public: StatsHistogram() = delete; // Construct a histogram by specifying its minimum and maximum values and // the number of desired bins. No padding of the min/max values is done // internally, so the caller should do that prior to construction if // necessary. StatsHistogram(AccumType minLimit, AccumType maxLimit, uInt nBins); ~StatsHistogram(); // get the binWidth. AccumType getBinWidth() const; // get the index of the bin containing the specified value uInt getIndex(AccumType value) const; // max limit values for all bins const std::vector& getMaxBinLimits() const; // max limit value of entire histogram (ie max limit value of last bin) AccumType getMaxHistLimit() const; // min limit value of entire histogram (ie min limit value of first bin) AccumType getMinHistLimit() const; // get the number of bins uInt getNBins() const; private: AccumType _binWidth{0}, _minHistLimit{0}, _maxHistLimit{0}; uInt _nBins{0}; // maximum values for all bins std::vector _maxBinLimits{}; // This does the obvious conversions. The Complex and DComplex // specializations (implemented below after the close of the class // definition) are used solely to permit compilation. In general, those // versions should never actually be called inline static uInt _getUInt(const AccumType& v) { return (uInt)v; } void _minMaxIdxRange( Int& minIdx, Int& maxIdx, AccumType value, Bool higher ) const; }; // // The Complex and DComplex versions are used solely to permit compilation. In // general, these versions should never actually be called template<> inline uInt StatsHistogram::_getUInt( const casacore::Complex& ) { ThrowCc( "Logic Error: This version for complex " "data types should never be called" ); } template<> inline uInt StatsHistogram::_getUInt( const casacore::DComplex& ) { ThrowCc( "Logic Error: This version for complex " "data types should never be called" ); } // // for use in debugging template ostream &operator<<(ostream &os, const StatsHistogram &hist) { os << "min limit " << hist.getMinHistLimit() << " max limit " << hist.getMaxHistLimit() << " bin width " << hist.getBinWidth() << " nbins " << hist.getNBins(); return os; } } #ifndef CASACORE_NO_AUTO_TEMPLATES #include "StatsHistogram.tcc" #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/scimath/StatsFramework/StatsHistogram.tcc000066400000000000000000000145311476623553700234030ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef SCIMATH_STATSHISTOGRAM_TCC #define SCIMATH_STATSHISTOGRAM_TCC #include #include #include #include namespace casacore { template StatsHistogram::StatsHistogram( AccumType minLimit, AccumType maxLimit, uInt nBins ) : _binWidth(0), _minHistLimit(minLimit), _maxHistLimit(maxLimit), _nBins(nBins), _maxBinLimits(nBins) { ThrowIf (minLimit > maxLimit, "minLimit must be less than maxLimit"); _binWidth = (_maxHistLimit - _minHistLimit)/(AccumType)nBins; // in case of AccumType = Int, this can happen even if max and min are // different. One would hope that AccumType=Int would never be used, but // just in case. The check incurs a negligible performance hit. ThrowIf(_binWidth == AccumType(0), "Histogram bin width is 0"); uInt j = 1; for_each( _maxBinLimits.begin(), _maxBinLimits.end(), [&j, this] (AccumType& val) { val = _minHistLimit + _binWidth * (AccumType)(j); ++j; } ); } template StatsHistogram::~StatsHistogram() {} template AccumType StatsHistogram::getBinWidth() const { return _binWidth; } template uInt StatsHistogram::getIndex(AccumType value) const { // we do not explicitly check if the value is within the histogram, // because the caller has already done that // estimate the index auto idx = _getUInt((value - _minHistLimit)/_binWidth); auto mymin = idx == 0 ? _minHistLimit : _maxBinLimits[idx - 1]; if (value >= mymin && value < _maxBinLimits[idx]) { return idx; } auto higher = value >= _maxBinLimits[idx]; Int testIdx = higher ? idx + 1 : idx - 1; // should never happen, but check just in case... if (higher) { ThrowIf(testIdx >= (Int)_nBins, "testIdx >= nBins"); } else { ThrowIf(testIdx < 0, "testIdx < 0"); } Int minIdx = higher ? idx : testIdx; Int maxIdx = higher ? testIdx : idx; // we must first establish a bin index // range which includes the target value _minMaxIdxRange(minIdx, maxIdx, value, higher); // bin index limits established, so now do binary search to find the // correct bin while (True) { ThrowIf( maxIdx < minIdx, "Logic Error: maxIdx (" + String::toString(maxIdx) + ") < minIdx (" + String::toString(minIdx) + ")" ); // integer division testIdx = (minIdx + maxIdx)/2; if ( value >= _maxBinLimits[testIdx - 1] && value < _maxBinLimits[testIdx] ) { // bin found return testIdx; } // the = part is important, for machine precision issues if the mean // binwidth is very small, since some (but not all) bins for all // intents and purposes may have zero width. See eg CAS-11828 if (value >= _maxBinLimits[testIdx - 1]) { minIdx = testIdx + 1; } else { maxIdx = testIdx - 1; } } ostringstream os; os << std::setprecision(20) << "Logic Error: Unable to locate bin " << "containing value " << value << endl; os << "Histogram spec " << *this << endl; os << "Guessed index " << idx << " with limits " << mymin << ", " << _maxBinLimits[idx] << endl; ThrowCc(os.str()); } template const std::vector& StatsHistogram::getMaxBinLimits() const { return _maxBinLimits; } template AccumType StatsHistogram::getMaxHistLimit() const { return _maxHistLimit; } template AccumType StatsHistogram::getMinHistLimit() const { return _minHistLimit; } template uInt StatsHistogram::getNBins() const { return _nBins; } template void StatsHistogram::_minMaxIdxRange( Int& minIdx, Int& maxIdx, AccumType value, Bool higher ) const { Int mult = 2; while(True) { auto mymin = minIdx == 0 ? _minHistLimit : _maxBinLimits[minIdx - 1]; if (value >= mymin && value < _maxBinLimits[maxIdx]) { // limits established return; } mult *= 2; if (higher) { minIdx = maxIdx + 1; if (minIdx >= (Int)_nBins) { minIdx = _nBins - 1; maxIdx = minIdx; // minIdx can't get any larger, so return return; } maxIdx = minIdx + mult; if (maxIdx >= (Int)_nBins) { maxIdx = _nBins - 1; // maxIdx can't get any larger, so return return; } } else { maxIdx = minIdx - 1; if (maxIdx <= 0) { maxIdx = 0; minIdx = 0; // maxIdx can't get any smaller, so return return; } minIdx = maxIdx - mult; if (minIdx < 0) { minIdx = 0; // minIdx can't get any smaller, so return return; } } } } } #endif casacore-3.7.1/scimath/StatsFramework/ZScoreCalculator.cc000066400000000000000000000076561476623553700234740ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include #include #include #include namespace casacore { std::map ZScoreCalculator::_nptsToMaxZScore; std::mutex ZScoreCalculator::_mutex; Double ZScoreCalculator::getMaxZScore(uInt64 npts) { std::lock_guard lock(_mutex); if (_nptsToMaxZScore.empty()) { // initialize the map _nptsToMaxZScore[0] = 0.5; _nptsToMaxZScore[1] = 1; _nptsToMaxZScore[3] = 1.5; _nptsToMaxZScore[10] = 2; _nptsToMaxZScore[40] = 2.5; _nptsToMaxZScore[185] = 3; _nptsToMaxZScore[1074] = 3.5; _nptsToMaxZScore[7893] = 4; _nptsToMaxZScore[73579] = 4.5; _nptsToMaxZScore[872138] = 5; _nptsToMaxZScore[13165126] = 5.5; _nptsToMaxZScore[253398672] = 6; _nptsToMaxZScore[6225098696ULL] = 6.5; _nptsToMaxZScore[195341107722ULL] = 7; } auto iter = _nptsToMaxZScore.find(npts); if (iter != _nptsToMaxZScore.end()) { return iter->second; } auto lowiter = _nptsToMaxZScore.cbegin(); auto upiter = lowiter; ++upiter; if (npts > _nptsToMaxZScore.rbegin()->first) { auto zscoreMax = _nptsToMaxZScore.rbegin()->second; auto z = zscoreMax + 0.5; while (True) { auto nptsmin = zscoreToNpts(z); if (nptsmin >= npts) { _nptsToMaxZScore[nptsmin] = z; if (nptsmin == npts) { return z; } else { auto increment = _nptsToMaxZScore.size() - 2; advance(lowiter, increment); advance(upiter, increment); break; } } z += 0.5; } } else { // distance must be an Int Int distance(_nptsToMaxZScore.size()/2); while (True) { advance(lowiter, distance); advance(upiter, distance); if (lowiter->first < npts && upiter->first > npts) { break; } distance /= 2; if (distance == 0) { distance = 1; } distance = lowiter->first > npts ? -std::abs(distance) : std::abs(distance); } } auto lz = lowiter->second; auto uz = upiter->second; auto z = (lz + uz)/2; while (True) { auto nptsmin = zscoreToNpts(z); if (_nptsToMaxZScore.size() < 1000000) { _nptsToMaxZScore[nptsmin] = z; } if (nptsmin == npts || near(lz, uz)) { return z; } if (nptsmin > npts) { uz = z; } else { lz = z; } z = (lz + uz)/2; } } } casacore-3.7.1/scimath/StatsFramework/ZScoreCalculator.h000066400000000000000000000042011476623553700233150ustar00rootroot00000000000000//# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef SCIMATH_ZSCORECALCULATOR_H #define SCIMATH_ZSCORECALCULATOR_H #include #include #include #include #include namespace casacore { class Mutex; // This class contains static methods related to z-scores. A z-score is the // number of standard deviations from the mean in a normal distribution. class ZScoreCalculator { public: ZScoreCalculator() = delete; // compute the maximum expected zscore given the number of points // in a sample. static Double getMaxZScore(uInt64 npts); // Get the minimum number of points in a Gaussian distribution, such that // the probability that the maximum of the distribution having the specified // zscore is 0.5. zscore should be non-negative. static inline uInt64 zscoreToNpts(Double zscore) { return (uInt64)(0.5/erfc(zscore/sqrt(2))); } private: static std::map _nptsToMaxZScore; static std::mutex _mutex; }; } #endif casacore-3.7.1/scimath/StatsFramework/test/000077500000000000000000000000001476623553700207075ustar00rootroot00000000000000casacore-3.7.1/scimath/StatsFramework/test/CMakeLists.txt000066400000000000000000000007611476623553700234530ustar00rootroot00000000000000set (tests tBiweightStatistics tChauvenetCriterionStatistics tClassicalStatistics tFitToHalfStatistics tHingesFencesStatistics tStatisticsAlgorithmFactory tStatisticsTypes tStatisticsUtilities tStatsHistogram tZScoreCalculator ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_scimath) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/scimath/StatsFramework/test/tBiweightStatistics.cc000066400000000000000000001723611476623553700252310ustar00rootroot00000000000000//# Copyright (C) 1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include #include #include #include #include #include #include #define COMMA , int main() { try { std::vector v0(5); v0[0] = 2; v0[1] = 1; v0[2] = 1.4; v0[3] = 3; v0[4] = 2.5; std::vector v1(3); v1[0] = 5; v1[1] = 8; v1[2] = 10; Double k[] = {1.4, 1, 2, 3, 2.5}; const Double eps = 1e-11; { BiweightStatistics< Double, std::vector::const_iterator, std::vector::const_iterator > bws(10); bws.setData(v0.begin(), v0.size()); // test unsupported stats through an exception StatisticsData::STATS stat = StatisticsData::MEAN; for (uInt i=0; i<5; ++i) { Bool thrown = False; switch(i) { case 0: stat = StatisticsData::MEDIAN; break; case 1: stat = StatisticsData::RMS; break; case 2: stat = StatisticsData::SUM; break; case 3: stat = StatisticsData::VARIANCE; break; case 4: stat = StatisticsData::FIRST_QUARTILE; break; default: break; } try { bws.getStatistic(stat); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); } StatsData sd = bws.getStatistics(); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 3, AipsError); // isn't set be this algorithm AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); Double expMean = 1.98059737309; AlwaysAssert(near(sd.mean, expMean, eps), AipsError); AlwaysAssert(*sd.min == 1, AipsError); AlwaysAssert(sd.minpos.first == -1, AipsError); AlwaysAssert(sd.minpos.second == -1, AipsError); AlwaysAssert(sd.npts == 5, AipsError); // not computed AlwaysAssert(sd.rms == 0, AipsError); Double expStdev = 0.868802742897; AlwaysAssert(near(sd.stddev, expStdev, eps), AipsError); AlwaysAssert(sd.sum == 0, AipsError); AlwaysAssert(sd.sumsq == 0, AipsError); AlwaysAssert(sd.variance == 0, AipsError); Bool thrown = False; try { bws.getStatisticIndex(StatisticsData::MAX); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); thrown = False; try { bws.getStatisticIndex(StatisticsData::MIN); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); AlwaysAssert(bws.getStatistic( StatisticsData::NPTS) == 5, AipsError ); thrown = False; try { bws.getStatistic(StatisticsData::RMS); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); AlwaysAssert( near( bws.getStatistic(StatisticsData::MEAN), expMean, eps ), AipsError ); AlwaysAssert( near( bws.getStatistic(StatisticsData::STDDEV), expStdev, eps ), AipsError ); AlwaysAssert(bws.getNiter() == 1, AipsError); } { BiweightStatistics< Double, std::vector::const_iterator, std::vector::const_iterator > bws(-1); bws.setData(v0.begin(), v0.size()); StatsData sd = bws.getStatistics(); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 3, AipsError); // isn't set be this algorithm AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); Double expMean = 1.98056452649; AlwaysAssert(near(sd.mean, expMean, eps), AipsError); AlwaysAssert(*sd.min == 1, AipsError); AlwaysAssert(sd.minpos.first == -1, AipsError); AlwaysAssert(sd.minpos.second == -1, AipsError); AlwaysAssert(sd.npts == 5, AipsError); // not computed AlwaysAssert(sd.rms == 0, AipsError); Double expStdev = 0.865625126924; AlwaysAssert(near(sd.stddev, expStdev, eps), AipsError); AlwaysAssert(sd.sum == 0, AipsError); AlwaysAssert(sd.sumsq == 0, AipsError); AlwaysAssert(sd.variance == 0, AipsError); Bool thrown = False; try { bws.getStatisticIndex(StatisticsData::MAX); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); thrown = False; try { bws.getStatisticIndex(StatisticsData::MIN); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); AlwaysAssert(bws.getStatistic( StatisticsData::NPTS) == 5, AipsError ); thrown = False; try { bws.getStatistic(StatisticsData::RMS); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); AlwaysAssert( near( bws.getStatistic(StatisticsData::MEAN), expMean, eps ), AipsError ); AlwaysAssert( near( bws.getStatistic(StatisticsData::STDDEV), expStdev, eps ), AipsError ); AlwaysAssert(bws.getNiter() == -1, AipsError); } { // just another way of specifying the data BiweightStatistics bw1(10); bw1.setData(k, 5); StatsData sd = bw1.getStatistics(); StatisticsData::STATS stat = StatisticsData::MEAN; for (uInt i=0; i<5; ++i) { Bool thrown = False; switch(i) { case 0: stat = StatisticsData::MEDIAN; break; case 1: stat = StatisticsData::RMS; break; case 2: stat = StatisticsData::SUM; break; case 3: stat = StatisticsData::VARIANCE; break; case 4: stat = StatisticsData::FIRST_QUARTILE; break; default: break; } try { bw1.getStatistic(stat); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); } AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 3, AipsError); // isn't set be this algorithm AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); Double expMean = 1.98059737309; AlwaysAssert(near(sd.mean, expMean, eps), AipsError); AlwaysAssert(*sd.min == 1, AipsError); AlwaysAssert(sd.minpos.first == -1, AipsError); AlwaysAssert(sd.minpos.second == -1, AipsError); AlwaysAssert(sd.npts == 5, AipsError); // not computed AlwaysAssert(sd.rms == 0, AipsError); Double expStdev = 0.868802742897; AlwaysAssert(near(sd.stddev, expStdev, eps), AipsError); AlwaysAssert(sd.sum == 0, AipsError); AlwaysAssert(sd.sumsq == 0, AipsError); AlwaysAssert(sd.variance == 0, AipsError); Bool thrown = False; try { bw1.getStatisticIndex(StatisticsData::MAX); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); thrown = False; try { bw1.getStatisticIndex(StatisticsData::MIN); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); AlwaysAssert(bw1.getStatistic( StatisticsData::NPTS) == 5, AipsError ); thrown = False; try { bw1.getStatistic(StatisticsData::RMS); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); AlwaysAssert( near( bw1.getStatistic(StatisticsData::MEAN), expMean, eps ), AipsError ); AlwaysAssert( near( bw1.getStatistic(StatisticsData::STDDEV), expStdev, eps ), AipsError ); AlwaysAssert(bw1.getNiter() == 1, AipsError); } { // two datasets BiweightStatistics< Double, std::vector::const_iterator, std::vector::const_iterator > bw; bw.setData(v0.begin(), v0.size()); bw.addData(v1.begin(), v1.size()); StatsData sd = bw.getStatistics(); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 10, AipsError); // isn't set by this algorithm AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); Double expMean = 3.97415612639; AlwaysAssert(near(sd.mean, expMean, eps), AipsError); AlwaysAssert(*sd.min == 1, AipsError); AlwaysAssert(sd.minpos.first == -1, AipsError); AlwaysAssert(sd.minpos.second == -1, AipsError); AlwaysAssert(sd.npts == 8, AipsError); // not computed AlwaysAssert(sd.rms == 0, AipsError); Double expStdev = 3.43760003872; AlwaysAssert(near(sd.stddev, expStdev, eps), AipsError); AlwaysAssert(sd.sum == 0, AipsError); AlwaysAssert(sd.sumsq == 0, AipsError); AlwaysAssert(sd.variance == 0, AipsError); Bool thrown = False; try { bw.getStatisticIndex(StatisticsData::MAX); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); thrown = False; try { bw.getStatisticIndex(StatisticsData::MIN); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); AlwaysAssert(bw.getStatistic( StatisticsData::NPTS) == 8, AipsError ); thrown = False; try { bw.getStatistic(StatisticsData::RMS); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); AlwaysAssert( near( bw.getStatistic(StatisticsData::MEAN), expMean, eps ), AipsError ); AlwaysAssert( near( bw.getStatistic(StatisticsData::STDDEV), expStdev, eps ), AipsError ); AlwaysAssert(bw.getNiter() == 2, AipsError); // Now reverse the order that the datasets were added. results // should be the same except for min and max dataset locations bw.setData(v1.begin(), v1.size()); bw.addData(v0.begin(), v0.size()); sd = bw.getStatistics(); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 10, AipsError); // isn't set by this algorithm AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); AlwaysAssert(near(sd.mean, expMean, eps), AipsError); AlwaysAssert(*sd.min == 1, AipsError); AlwaysAssert(sd.minpos.first == -1, AipsError); AlwaysAssert(sd.minpos.second == -1, AipsError); AlwaysAssert(sd.npts == 8, AipsError); // not computed AlwaysAssert(sd.rms == 0, AipsError); AlwaysAssert(near(sd.stddev, expStdev, eps), AipsError); AlwaysAssert(sd.sum == 0, AipsError); AlwaysAssert(sd.sumsq == 0, AipsError); AlwaysAssert(sd.variance == 0, AipsError); thrown = False; try { bw.getStatisticIndex(StatisticsData::MAX); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); thrown = False; try { bw.getStatisticIndex(StatisticsData::MIN); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); AlwaysAssert(bw.getStatistic( StatisticsData::NPTS) == 8, AipsError ); thrown = False; try { bw.getStatistic(StatisticsData::RMS); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); AlwaysAssert( near( bw.getStatistic(StatisticsData::MEAN), expMean, eps ), AipsError ); AlwaysAssert( near( bw.getStatistic(StatisticsData::STDDEV), expStdev, eps ), AipsError ); AlwaysAssert(bw.getNiter() == 2, AipsError); } { // Test accumulating as datasets are added. BiweightStatistics< Double, std::vector::const_iterator, std::vector::const_iterator > bw; bw.setCalculateAsAdded(False); bw.setData(v0.begin(), v0.size()); bw.addData(v1.begin(), v1.size()); StatsData sd = bw.getStatistics(); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 10, AipsError); // isn't set by this algorithm AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); Double expMean = 3.97415612639; AlwaysAssert(near(sd.mean, expMean, eps), AipsError); AlwaysAssert(*sd.min == 1, AipsError); AlwaysAssert(sd.minpos.first == -1, AipsError); AlwaysAssert(sd.minpos.second == -1, AipsError); AlwaysAssert(sd.npts == 8, AipsError); // not computed AlwaysAssert(sd.rms == 0, AipsError); Double expStdev = 3.43760003872; AlwaysAssert(near(sd.stddev, expStdev, eps), AipsError); AlwaysAssert(sd.sum == 0, AipsError); AlwaysAssert(sd.sumsq == 0, AipsError); AlwaysAssert(sd.variance == 0, AipsError); AlwaysAssert(bw.getStatistic( StatisticsData::NPTS) == 8, AipsError ); AlwaysAssert( near( bw.getStatistic(StatisticsData::MEAN), expMean, eps ), AipsError ); AlwaysAssert( near( bw.getStatistic(StatisticsData::STDDEV), expStdev, eps ), AipsError ); AlwaysAssert(bw.getNiter() == 2, AipsError); Bool thrown = False; try { bw.setCalculateAsAdded(True); } catch (std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); bw.reset(); thrown = False; try { bw.setCalculateAsAdded(True); } catch (std::exception& x) { thrown = True; } } { // two datasets, stride = 2,1 BiweightStatistics< Double, std::vector::const_iterator, std::vector::const_iterator > bw; bw.setData(v0.begin(), v0.size(), 2); bw.addData(v1.begin(), v1.size()); StatsData sd = bw.getStatistics(); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 10, AipsError); // isn't set by this algorithm AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); Double expMean = 4.74754715912; AlwaysAssert(near(sd.mean, expMean, eps), AipsError); AlwaysAssert(*sd.min == 1.4, AipsError); AlwaysAssert(sd.minpos.first == -1, AipsError); AlwaysAssert(sd.minpos.second == -1, AipsError); AlwaysAssert(sd.npts == 6, AipsError); // not computed AlwaysAssert(sd.rms == 0, AipsError); Double expStdev = 3.77813315235; AlwaysAssert(near(sd.stddev, expStdev, eps), AipsError); AlwaysAssert(sd.sum == 0, AipsError); AlwaysAssert(sd.sumsq == 0, AipsError); AlwaysAssert(sd.variance == 0, AipsError); AlwaysAssert(bw.getStatistic( StatisticsData::NPTS) == 6, AipsError ); AlwaysAssert( near( bw.getStatistic(StatisticsData::MEAN), expMean, eps ), AipsError ); AlwaysAssert( near( bw.getStatistic(StatisticsData::STDDEV), expStdev, eps ), AipsError ); } { // data ranges BiweightStatistics< Double, std::vector::const_iterator, std::vector::const_iterator > bw; std::vector > r0(1); r0[0].first = 5; r0[0].second = -5; Bool thrown = False; try { bw.setData(v0.begin(), 3, r0); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); r0[0].first = 2.4; r0[0].second = 6; std::vector > r1(2); r1[0].first = 9; r1[0].second = 11; r1[1].first = 2; r1[1].second = 7; bw.setData(v0.begin(), v0.size(), r0); bw.addData(v1.begin(), v1.size(), r1, False); StatsData sd = bw.getStatistics(); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 8, AipsError); // isn't set by this algorithm AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); Double expMean = 2.7501751458; AlwaysAssert(near(sd.mean, expMean, eps), AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == -1, AipsError); AlwaysAssert(sd.minpos.second == -1, AipsError); AlwaysAssert(sd.npts == 3, AipsError); // not computed AlwaysAssert(sd.rms == 0, AipsError); Double expStdev = 0.437209840794; AlwaysAssert(near(sd.stddev, expStdev, eps), AipsError); AlwaysAssert(sd.sum == 0, AipsError); AlwaysAssert(sd.sumsq == 0, AipsError); AlwaysAssert(sd.variance == 0, AipsError); AlwaysAssert(bw.getStatistic( StatisticsData::NPTS) == 3, AipsError ); AlwaysAssert( near( bw.getStatistic(StatisticsData::MEAN), expMean, eps ), AipsError ); AlwaysAssert( near( bw.getStatistic(StatisticsData::STDDEV), expStdev, eps ), AipsError ); } { // mask BiweightStatistics< Double, vector::const_iterator, vector::const_iterator > bw; vector m0(v0.size()); m0[0] = False; m0[1] = False; m0[2] = False; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = False; m1[1] = True; m1[2] = False; bw.setData(v0.begin(), m0.begin(), v0.size()); bw.addData(v1.begin(), m1.begin(), v1.size()); StatsData sd = bw.getStatistics(); AlwaysAssert(sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 8, AipsError); // isn't set by this algorithm AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); Double expMean = 2.7501751458; AlwaysAssert(near(sd.mean, expMean, eps), AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == -1, AipsError); AlwaysAssert(sd.minpos.second == -1, AipsError); AlwaysAssert(sd.npts == 3, AipsError); // not computed AlwaysAssert(sd.rms == 0, AipsError); Double expStdev = 0.437209840794; AlwaysAssert(near(sd.stddev, expStdev, eps), AipsError); AlwaysAssert(sd.sum == 0, AipsError); AlwaysAssert(sd.sumsq == 0, AipsError); AlwaysAssert(sd.variance == 0, AipsError); AlwaysAssert(bw.getStatistic( StatisticsData::NPTS) == 3, AipsError ); AlwaysAssert( near( bw.getStatistic(StatisticsData::MEAN), expMean, eps ), AipsError ); AlwaysAssert( near( bw.getStatistic(StatisticsData::STDDEV), expStdev, eps ), AipsError ); // test cloning gives same results std::shared_ptr< BiweightStatistics< Double, std::vector::const_iterator, std::vector::const_iterator > > bw1( dynamic_cast< BiweightStatistics< Double, std::vector::const_iterator, std::vector::const_iterator >* >(bw.clone()) ); sd = bw1->getStatistics(); AlwaysAssert(sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 8, AipsError); // isn't set by this algorithm AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); AlwaysAssert(near(sd.mean, expMean, eps), AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == -1, AipsError); AlwaysAssert(sd.minpos.second == -1, AipsError); AlwaysAssert(sd.npts == 3, AipsError); // not computed AlwaysAssert(sd.rms == 0, AipsError); AlwaysAssert(near(sd.stddev, expStdev, eps), AipsError); AlwaysAssert(sd.sum == 0, AipsError); AlwaysAssert(sd.sumsq == 0, AipsError); AlwaysAssert(sd.variance == 0, AipsError); AlwaysAssert(bw1->getStatistic( StatisticsData::NPTS) == 3, AipsError ); AlwaysAssert( near( bw1->getStatistic(StatisticsData::MEAN), expMean, eps ), AipsError ); AlwaysAssert( near( bw1->getStatistic(StatisticsData::STDDEV), expStdev, eps ), AipsError ); } { // mask and ranges BiweightStatistics< Double, vector::const_iterator, vector::const_iterator > bw; vector m0(v0.size()); m0[0] = False; m0[1] = True; m0[2] = True; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = True; m1[1] = True; m1[2] = False; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 9; bw.setData(v0.begin(), m0.begin(), v0.size(), r0, False); bw.addData(v1.begin(), m1.begin(), v1.size(), r1, True); StatsData sd = bw.getStatistics(); AlwaysAssert(sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 8, AipsError); // isn't set by this algorithm AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); Double expMean = 2.7501751458; AlwaysAssert(near(sd.mean, expMean, eps), AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == -1, AipsError); AlwaysAssert(sd.minpos.second == -1, AipsError); AlwaysAssert(sd.npts == 3, AipsError); // not computed AlwaysAssert(sd.rms == 0, AipsError); Double expStdev = 0.437209840794; AlwaysAssert(near(sd.stddev, expStdev, eps), AipsError); AlwaysAssert(sd.sum == 0, AipsError); AlwaysAssert(sd.sumsq == 0, AipsError); AlwaysAssert(sd.variance == 0, AipsError); AlwaysAssert(bw.getStatistic( StatisticsData::NPTS) == 3, AipsError ); AlwaysAssert( near( bw.getStatistic(StatisticsData::MEAN), expMean, eps ), AipsError ); AlwaysAssert( near( bw.getStatistic(StatisticsData::STDDEV), expStdev, eps ), AipsError ); } { // weights, which don't have any effect for this algorithm, // except for weight = 0 BiweightStatistics< Double, vector::const_iterator, vector::const_iterator > bw; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; bw.setData(v0.begin(), w0.begin(), w0.size()); bw.addData(v1.begin(), w1.begin(), w1.size()); StatsData sd = bw.getStatistics(); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(*sd.max == 10, AipsError); // isn't set by this algorithm AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); Double expMean = 4.31952746181; AlwaysAssert(near(sd.mean, expMean, eps), AipsError); AlwaysAssert(*sd.min == 1, AipsError); AlwaysAssert(sd.minpos.first == -1, AipsError); AlwaysAssert(sd.minpos.second == -1, AipsError); AlwaysAssert(sd.npts == 7, AipsError); // not computed AlwaysAssert(sd.rms == 0, AipsError); Double expStdev = 3.64182681772; AlwaysAssert(near(sd.stddev, expStdev, eps), AipsError); AlwaysAssert(sd.sum == 0, AipsError); AlwaysAssert(sd.sumsq == 0, AipsError); AlwaysAssert(sd.variance == 0, AipsError); AlwaysAssert(bw.getStatistic( StatisticsData::NPTS) == 7, AipsError ); AlwaysAssert( near( bw.getStatistic(StatisticsData::MEAN), expMean, eps ), AipsError ); AlwaysAssert( near( bw.getStatistic(StatisticsData::STDDEV), expStdev, eps ), AipsError ); } { // integer weights BiweightStatistics< Double, vector::const_iterator, vector::const_iterator, vector::const_iterator > bw; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; bw.setData(v0.begin(), w0.begin(), w0.size()); bw.addData(v1.begin(), w1.begin(), w1.size()); StatsData sd = bw.getStatistics(); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(*sd.max == 10, AipsError); // isn't set by this algorithm AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); Double expMean = 4.31952746181; AlwaysAssert(near(sd.mean, expMean, eps), AipsError); AlwaysAssert(*sd.min == 1, AipsError); AlwaysAssert(sd.minpos.first == -1, AipsError); AlwaysAssert(sd.minpos.second == -1, AipsError); AlwaysAssert(sd.npts == 7, AipsError); // not computed AlwaysAssert(sd.rms == 0, AipsError); Double expStdev = 3.64182681772; AlwaysAssert(near(sd.stddev, expStdev, eps), AipsError); AlwaysAssert(sd.sum == 0, AipsError); AlwaysAssert(sd.sumsq == 0, AipsError); AlwaysAssert(sd.variance == 0, AipsError); AlwaysAssert(bw.getStatistic( StatisticsData::NPTS) == 7, AipsError ); AlwaysAssert( near( bw.getStatistic(StatisticsData::MEAN), expMean, eps ), AipsError ); AlwaysAssert( near( bw.getStatistic(StatisticsData::STDDEV), expStdev, eps ), AipsError ); } { // weights and ranges BiweightStatistics< Double, vector::const_iterator, vector::const_iterator > bw; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 9; bw.setData(v0.begin(), w0.begin(), v0.size(), r0, False); bw.addData(v1.begin(), w1.begin(), v1.size(), r1, True); StatsData sd = bw.getStatistics(); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(*sd.max == 8, AipsError); // isn't set by this algorithm AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); Double expMean = 2.7501751458; AlwaysAssert(near(sd.mean, expMean, eps), AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == -1, AipsError); AlwaysAssert(sd.minpos.second == -1, AipsError); AlwaysAssert(sd.npts == 3, AipsError); // not computed AlwaysAssert(sd.rms == 0, AipsError); Double expStdev = 0.437209840794; AlwaysAssert(near(sd.stddev, expStdev, eps), AipsError); AlwaysAssert(sd.sum == 0, AipsError); AlwaysAssert(sd.sumsq == 0, AipsError); AlwaysAssert(sd.variance == 0, AipsError); AlwaysAssert(bw.getStatistic( StatisticsData::NPTS) == 3, AipsError ); AlwaysAssert( near( bw.getStatistic(StatisticsData::MEAN), expMean, eps ), AipsError ); AlwaysAssert( near( bw.getStatistic(StatisticsData::STDDEV), expStdev, eps ), AipsError ); } { // integer weights; ranges BiweightStatistics< Double, vector::const_iterator, vector::const_iterator, vector::const_iterator > bw; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 9; bw.setData(v0.begin(), w0.begin(), v0.size(), r0, False); bw.addData(v1.begin(), w1.begin(), v1.size(), r1, True); StatsData sd = bw.getStatistics(); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(*sd.max == 8, AipsError); // isn't set by this algorithm AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); Double expMean = 2.7501751458; AlwaysAssert(near(sd.mean, expMean, eps), AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == -1, AipsError); AlwaysAssert(sd.minpos.second == -1, AipsError); AlwaysAssert(sd.npts == 3, AipsError); // not computed AlwaysAssert(sd.rms == 0, AipsError); Double expStdev = 0.437209840794; AlwaysAssert(near(sd.stddev, expStdev, eps), AipsError); AlwaysAssert(sd.sum == 0, AipsError); AlwaysAssert(sd.sumsq == 0, AipsError); AlwaysAssert(sd.variance == 0, AipsError); AlwaysAssert(bw.getStatistic( StatisticsData::NPTS) == 3, AipsError ); AlwaysAssert( near( bw.getStatistic(StatisticsData::MEAN), expMean, eps ), AipsError ); AlwaysAssert( near( bw.getStatistic(StatisticsData::STDDEV), expStdev, eps ), AipsError ); } { // weights, ranges, and masks BiweightStatistics< Double, vector::const_iterator, vector::const_iterator > bw; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector m0(v0.size()); m0[0] = True; m0[1] = True; m0[2] = True; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = True; m1[1] = True; m1[2] = False; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 12; bw.setData(v0.begin(), w0.begin(), m0.begin(), v0.size(), r0, False); bw.addData(v1.begin(), w1.begin(), m1.begin(), v1.size(), r1, True); StatsData sd = bw.getStatistics(); AlwaysAssert(sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(*sd.max == 8, AipsError); // isn't set by this algorithm AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); Double expMean = 2.7501751458; AlwaysAssert(near(sd.mean, expMean, eps), AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == -1, AipsError); AlwaysAssert(sd.minpos.second == -1, AipsError); AlwaysAssert(sd.npts == 3, AipsError); // not computed AlwaysAssert(sd.rms == 0, AipsError); Double expStdev = 0.437209840794; AlwaysAssert(near(sd.stddev, expStdev, eps), AipsError); AlwaysAssert(sd.sum == 0, AipsError); AlwaysAssert(sd.sumsq == 0, AipsError); AlwaysAssert(sd.variance == 0, AipsError); AlwaysAssert(bw.getStatistic( StatisticsData::NPTS) == 3, AipsError ); AlwaysAssert( near( bw.getStatistic(StatisticsData::MEAN), expMean, eps ), AipsError ); AlwaysAssert( near( bw.getStatistic(StatisticsData::STDDEV), expStdev, eps ), AipsError ); } { // integer weights; ranges, and masks BiweightStatistics< Double, vector::const_iterator, vector::const_iterator, vector::const_iterator > bw; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector m0(v0.size()); m0[0] = True; m0[1] = True; m0[2] = True; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = True; m1[1] = True; m1[2] = False; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 12; bw.setData(v0.begin(), w0.begin(), m0.begin(), v0.size(), r0, False); bw.addData(v1.begin(), w1.begin(), m1.begin(), v1.size(), r1, True); StatsData sd = bw.getStatistics(); AlwaysAssert(sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(*sd.max == 8, AipsError); // isn't set by this algorithm AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); Double expMean = 2.7501751458; AlwaysAssert(near(sd.mean, expMean, eps), AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == -1, AipsError); AlwaysAssert(sd.minpos.second == -1, AipsError); AlwaysAssert(sd.npts == 3, AipsError); // not computed AlwaysAssert(sd.rms == 0, AipsError); Double expStdev = 0.437209840794; AlwaysAssert(near(sd.stddev, expStdev, eps), AipsError); AlwaysAssert(sd.sum == 0, AipsError); AlwaysAssert(sd.sumsq == 0, AipsError); AlwaysAssert(sd.variance == 0, AipsError); AlwaysAssert(bw.getStatistic( StatisticsData::NPTS) == 3, AipsError ); AlwaysAssert( near( bw.getStatistic(StatisticsData::MEAN), expMean, eps ), AipsError ); AlwaysAssert( near( bw.getStatistic(StatisticsData::STDDEV), expStdev, eps ), AipsError ); } { // weights, masks BiweightStatistics< Double, vector::const_iterator, vector::const_iterator > bw; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector m0(v0.size()); m0[0] = True; m0[1] = False; m0[2] = False; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = False; m1[1] = True; m1[2] = False; bw.setData(v0.begin(), w0.begin(), m0.begin(), v0.size()); bw.addData(v1.begin(), w1.begin(), m1.begin(), v1.size()); StatsData sd = bw.getStatistics(); AlwaysAssert(sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(*sd.max == 8, AipsError); // isn't set by this algorithm AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); Double expMean = 2.7501751458; AlwaysAssert(near(sd.mean, expMean, eps), AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == -1, AipsError); AlwaysAssert(sd.minpos.second == -1, AipsError); AlwaysAssert(sd.npts == 3, AipsError); // not computed AlwaysAssert(sd.rms == 0, AipsError); Double expStdev = 0.437209840794; AlwaysAssert(near(sd.stddev, expStdev, eps), AipsError); AlwaysAssert(sd.sum == 0, AipsError); AlwaysAssert(sd.sumsq == 0, AipsError); AlwaysAssert(sd.variance == 0, AipsError); AlwaysAssert(bw.getStatistic( StatisticsData::NPTS) == 3, AipsError ); AlwaysAssert( near( bw.getStatistic(StatisticsData::MEAN), expMean, eps ), AipsError ); AlwaysAssert( near( bw.getStatistic(StatisticsData::STDDEV), expStdev, eps ), AipsError ); } { // integer weights; masks BiweightStatistics< Double, vector::const_iterator, vector::const_iterator, vector::const_iterator > bw; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector m0(v0.size()); m0[0] = True; m0[1] = False; m0[2] = False; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = False; m1[1] = True; m1[2] = False; bw.setData(v0.begin(), w0.begin(), m0.begin(), v0.size()); bw.addData(v1.begin(), w1.begin(), m1.begin(), v1.size()); StatsData sd = bw.getStatistics(); AlwaysAssert(sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(*sd.max == 8, AipsError); // isn't set by this algorithm AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); Double expMean = 2.7501751458; AlwaysAssert(near(sd.mean, expMean, eps), AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == -1, AipsError); AlwaysAssert(sd.minpos.second == -1, AipsError); AlwaysAssert(sd.npts == 3, AipsError); // not computed AlwaysAssert(sd.rms == 0, AipsError); Double expStdev = 0.437209840794; AlwaysAssert(near(sd.stddev, expStdev, eps), AipsError); AlwaysAssert(sd.sum == 0, AipsError); AlwaysAssert(sd.sumsq == 0, AipsError); AlwaysAssert(sd.variance == 0, AipsError); AlwaysAssert(bw.getStatistic( StatisticsData::NPTS) == 3, AipsError ); AlwaysAssert( near( bw.getStatistic(StatisticsData::MEAN), expMean, eps ), AipsError ); AlwaysAssert( near( bw.getStatistic(StatisticsData::STDDEV), expStdev, eps ), AipsError ); } { // getMinMax(), two datasets BiweightStatistics< Double, vector::const_iterator, vector::const_iterator > bw; bw.setData(v0.begin(), v0.size()); bw.addData(v1.begin(), v1.size()); Double mymin, mymax; bw.getMinMax(mymin, mymax); AlwaysAssert(mymin == 1, AipsError); AlwaysAssert(mymax == 10, AipsError); } { // getMinMax(), two datasets, stride = 2,1 BiweightStatistics< Double, vector::const_iterator, vector::const_iterator > bw; bw.setData(v0.begin(), 3, 2); bw.addData(v1.begin(), v1.size()); Double mymin, mymax; bw.getMinMax(mymin, mymax); AlwaysAssert(mymin == 1.4, AipsError); AlwaysAssert(mymax == 10, AipsError); } { // getMinMax(), data ranges BiweightStatistics< Double, vector::const_iterator, vector::const_iterator > bw; vector > r0(1); r0[0].first = 2.4; r0[0].second = 6; vector > r1(2); r1[0].first = 9; r1[0].second = 11; r1[1].first = 2; r1[1].second = 7; bw.setData(v0.begin(), v0.size(), r0); bw.addData(v1.begin(), v1.size(), r1, False); Double mymin, mymax; bw.getMinMax(mymin, mymax); AlwaysAssert(mymin == 2.5, AipsError); AlwaysAssert(mymax == 8, AipsError); } { // getMinMax(), mask BiweightStatistics< Double, vector::const_iterator, vector::const_iterator > bw; vector m0(v0.size()); m0[0] = False; m0[1] = False; m0[2] = False; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = False; m1[1] = True; m1[2] = False; bw.setData(v0.begin(), m0.begin(), v0.size()); bw.addData(v1.begin(), m1.begin(), v1.size()); Double mymin, mymax; bw.getMinMax(mymin, mymax); AlwaysAssert(mymin == 2.5, AipsError); AlwaysAssert(mymax == 8, AipsError); } { // getMinMax(), mask and ranges BiweightStatistics< Double, vector::const_iterator, vector::const_iterator > bw; vector m0(v0.size()); m0[0] = False; m0[1] = True; m0[2] = True; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = True; m1[1] = True; m1[2] = False; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 9; bw.setData(v0.begin(), m0.begin(), v0.size(), r0, False); bw.addData(v1.begin(), m1.begin(), v1.size(), r1, True); Double mymin, mymax; bw.getMinMax(mymin, mymax); AlwaysAssert(mymin == 2.5, AipsError); AlwaysAssert(mymax == 8, AipsError); } { // getMinMax, weights BiweightStatistics< Double, vector::const_iterator, vector::const_iterator > bw; vector w0(v0.size()); w0[0] = 1; w0[1] = 0; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 0; bw.setData(v0.begin(), w0.begin(), w0.size()); bw.addData(v1.begin(), w1.begin(), w1.size()); Double mymin, mymax; bw.getMinMax(mymin, mymax); AlwaysAssert(mymin == 1.4, AipsError); AlwaysAssert(mymax == 8, AipsError); } { // getMinMax, integer weights BiweightStatistics< Double, vector::const_iterator, vector::const_iterator, vector::const_iterator > bw; vector w0(v0.size()); w0[0] = 1; w0[1] = 0; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 0; bw.setData(v0.begin(), w0.begin(), w0.size()); bw.addData(v1.begin(), w1.begin(), w1.size()); Double mymin, mymax; bw.getMinMax(mymin, mymax); AlwaysAssert(mymin == 1.4, AipsError); AlwaysAssert(mymax == 8, AipsError); } { // getMinMax(), weights and ranges BiweightStatistics< Double, vector::const_iterator, vector::const_iterator > bw; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 9; bw.setData(v0.begin(), w0.begin(), v0.size(), r0, False); bw.addData(v1.begin(), w1.begin(), v1.size(), r1, True); Double mymin, mymax; bw.getMinMax(mymin, mymax); AlwaysAssert(mymin == 2.5, AipsError); AlwaysAssert(mymax == 8, AipsError); } { // getMinMax(), integer weights, and ranges BiweightStatistics< Double, vector::const_iterator, vector::const_iterator, vector::const_iterator > bw; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 9; bw.setData(v0.begin(), w0.begin(), v0.size(), r0, False); bw.addData(v1.begin(), w1.begin(), v1.size(), r1, True); Double mymin, mymax; bw.getMinMax(mymin, mymax); AlwaysAssert(mymin == 2.5, AipsError); AlwaysAssert(mymax == 8, AipsError); } { // getMinMax(), weights, ranges, and masks BiweightStatistics< Double, vector::const_iterator, vector::const_iterator > bw; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector m0(v0.size()); m0[0] = True; m0[1] = True; m0[2] = True; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = True; m1[1] = True; m1[2] = False; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 12; bw.setData(v0.begin(), w0.begin(), m0.begin(), v0.size(), r0, False); bw.addData(v1.begin(), w1.begin(), m1.begin(), v1.size(), r1, True); Double mymin, mymax; bw.getMinMax(mymin, mymax); AlwaysAssert(mymin == 2.5, AipsError); AlwaysAssert(mymax == 8, AipsError); } { // getMinMax(), integer weights, ranges, and masks BiweightStatistics< Double, vector::const_iterator, vector::const_iterator, vector::const_iterator > bw; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector m0(v0.size()); m0[0] = True; m0[1] = True; m0[2] = True; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = True; m1[1] = True; m1[2] = False; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 12; bw.setData(v0.begin(), w0.begin(), m0.begin(), v0.size(), r0, False); bw.addData(v1.begin(), w1.begin(), m1.begin(), v1.size(), r1, True); Double mymin, mymax; bw.getMinMax(mymin, mymax); AlwaysAssert(mymin == 2.5, AipsError); AlwaysAssert(mymax == 8, AipsError); } { // general quantile exceptions BiweightStatistics< Double, vector::const_iterator, vector::const_iterator > bw; bw.setData(v0.begin(), v0.size()); bw.addData(v1.begin(), v1.size()); Bool thrown = False; try { bw.getQuantile(0.1); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); thrown = False; try { bw.getMedian(); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); thrown = False; try { std::map qToV; std::set qs; qs.insert(0.1); bw.getMedianAndQuantiles(qToV, qs); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); try { bw.getMedianAbsDevMed(); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); } { // large array, getMinMax() BiweightStatistics< Double, std::vector::const_iterator, std::vector::const_iterator > bw; std::vector big(1e7); uInt count = 0; std::vector::iterator iter = big.begin(); std::vector::iterator end = big.end(); for (; iter!=end; ++iter, ++count) { *iter = count; } bw.addData(big.begin(), big.size()); Double mymin, mymax; bw.getMinMax(mymin, mymax); AlwaysAssert(mymin == 0, AipsError); AlwaysAssert(mymax == big.size()-1, AipsError); // do it again, but shuffle the elements std::random_device rd; std::mt19937 g(rd()); std::shuffle(big.begin(), big.end(), g); BiweightStatistics< Double, std::vector::const_iterator, std::vector::const_iterator > bw1; bw1.addData(big.begin(), big.size()); bw1.getMinMax(mymin, mymax); AlwaysAssert(mymin == 0, AipsError); AlwaysAssert(mymax == big.size()-1, AipsError); } { // tests for getNPts() uInt n = 6; uInt size[] = {5000, 80000, 6500, 100000, 19256, 7482}; std::vector > data(n); BiweightStatistics< Double, std::vector::const_iterator, std::vector::const_iterator > bw; uInt64 expec = 0; for (uInt i=0; i mask3(size[3]); std::fill(mask3.begin(), mask3.begin()+size[3], False); mask3[1000] = True; mask3[1500] = True; expec -= (size[3] - 2); for (uInt i=0; i #include #include #include #include #include int main() { try { // just check for compilation success, the real tests are in // tImageStatistics2 Double data[] = { -2.61279178e+00, -2.59342551e+00, -2.16943479e+00, -2.13970494e+00, -1.91509378e+00, -1.91133809e+00, -1.84780550e+00, -1.67959487e+00, -1.55754685e+00, -1.49124575e+00, -1.47779667e+00, -1.38040781e+00, -1.37083769e+00, -1.34913635e+00, -1.29416192e+00, -1.10022914e+00, -1.07126451e+00, -1.05194223e+00, -1.03733921e+00, -1.02524054e+00, -9.84085381e-01, -9.46198046e-01, -9.23078358e-01, -9.21401978e-01, -8.76483500e-01, -8.60657215e-01, -8.26754928e-01, -7.59524405e-01, -7.36167967e-01, -6.76235080e-01, -6.72010839e-01, -6.33015037e-01, -5.91541886e-01, -5.87743282e-01, -5.28600693e-01, -5.03111005e-01, -4.84272331e-01, -3.87220532e-01, -3.62094551e-01, -3.12986404e-01, -3.01742464e-01, -2.86407530e-01, -2.77583510e-01, -2.37437248e-01, -2.37364024e-01, -2.35247806e-01, -2.11185545e-01, -1.92734912e-01, -1.87121660e-01, -1.77792773e-01, -1.69995695e-01, -1.45033970e-01, -1.16942599e-01, -6.27262741e-02, -3.45510058e-02, -3.06752156e-02, -1.79617219e-02, -1.14524942e-02, -3.16955987e-03, 7.29589257e-04, 1.24999344e-01, 2.12515876e-01, 2.50957519e-01, 2.79240131e-01, 2.81288683e-01, 3.05763662e-01, 3.11809599e-01, 3.40768367e-01, 3.51874888e-01, 3.91162097e-01, 4.58450705e-01, 4.82642174e-01, 4.96854514e-01, 7.20111370e-01, 7.22756803e-01, 7.25001752e-01, 8.35289240e-01, 8.46509099e-01, 8.93022776e-01, 9.00427580e-01, 9.17734325e-01, 9.18030262e-01, 1.04210591e+00, 1.05506992e+00, 1.09472048e+00, 1.15250385e+00, 1.16275501e+00, 1.21244884e+00, 1.22725236e+00, 1.31463480e+00, 1.33273876e+00, 1.57637489e+00, 1.58221984e+00, 1.65665936e+00, 1.80032420e+00, 1.91410339e+00, 2.02669597e+00, 2.08605909e+00, 2.09777880e+00, 2.21240473e+00, 3.5, 4, 5, 6, 7, 8, 1000000 }; { // zscore=3.5, no iterations ChauvenetCriterionStatistics cs(3.5, 0); cs.setData(data, 107); StatsData sd = cs.getStatistics(); AlwaysAssert(sd.npts == 106, AipsError); AlwaysAssert(*sd.max == 8, AipsError); } { // zscore=3.5, one iteration ChauvenetCriterionStatistics cs(3.5, 1); cs.setData(data, 107); StatsData sd = cs.getStatistics(); AlwaysAssert(sd.npts == 104, AipsError); AlwaysAssert(*sd.max == 6, AipsError); // test cloning gives same results std::shared_ptr< ChauvenetCriterionStatistics< Double, Double*, Bool* > > cs1( dynamic_cast< ChauvenetCriterionStatistics* >(cs.clone()) ); StatsData sd1 = cs1->getStatistics(); AlwaysAssert(sd1.npts == 104, AipsError); AlwaysAssert(*sd1.max == 6, AipsError); } { // zscore=3.5, iterate until converged ChauvenetCriterionStatistics cs(3.5, -1); cs.setData(data, 107); StatsData sd = cs.getStatistics(); AlwaysAssert(sd.npts == 102, AipsError); AlwaysAssert(*sd.max == 4, AipsError); } { // use Chauvenet criterion, no iterations ChauvenetCriterionStatistics cs(-1, 0); cs.setData(data, 107); StatsData sd = cs.getStatistics(); AlwaysAssert(sd.npts == 106, AipsError); AlwaysAssert(*sd.max == 8, AipsError); } { // use Chauvenet criterion, one iteration ChauvenetCriterionStatistics cs(-1, 1); cs.setData(data, 107); StatsData sd = cs.getStatistics(); AlwaysAssert(sd.npts == 103, AipsError); AlwaysAssert(*sd.max == 5, AipsError); } { // use Chauvenet criterion, iterate until converged ChauvenetCriterionStatistics cs(-1, -1); cs.setData(data, 107); StatsData sd = cs.getStatistics(); AlwaysAssert(sd.npts == 100, AipsError); AlwaysAssert(*sd.max == data[99], AipsError); } { // a compile test: change final template parameter to Int* ChauvenetCriterionStatistics cs(-1, -1); cs.setData(data, 107); StatsData sd = cs.getStatistics(); AlwaysAssert(sd.npts == 100, AipsError); AlwaysAssert(*sd.max == data[99], AipsError); } { // illustrate fix of CAS-10103 ChauvenetCriterionStatistics::const_iterator> cs(3); vector v0; v0.push_back(1.0); cs.setData(v0.begin(), v0.size()); StatsData sd = cs.getStatistics(); AlwaysAssert(sd.mean == 1, AipsError); vector v1; v1.push_back(10); v1.push_back(11); cs.setData(v1.begin(), v1.size()); sd = cs.getStatistics(); AlwaysAssert(sd.mean == 10.5, AipsError); } } catch (const std::exception& x) { cout << x.what() << endl; return 1; } return 0; } casacore-3.7.1/scimath/StatsFramework/test/tClassicalStatistics.cc000066400000000000000000002421671476623553700253670ustar00rootroot00000000000000//# Copyright (C) 1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include #include #include #include #include #include #include #define COMMA , int main() { try { std::vector v0(5); v0[0] = 2; v0[1] = 1; v0[2] = 1.5; v0[3] = 3; v0[4] = 2.5; std::vector v1(3); v1[0] = 5; v1[1] = 8; v1[2] = 10; Double k[] = {1.5, 1, 2, 3, 2.5}; { ClassicalStatistics< Double, std::vector::const_iterator, std::vector::const_iterator > cs; cs.setData(v0.begin(), v0.size()); StatsData sd = cs.getStatistics(); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 3, AipsError); AlwaysAssert(sd.maxpos.first == 0, AipsError); AlwaysAssert(sd.maxpos.second, AipsError); AlwaysAssert(sd.mean == 2, AipsError); AlwaysAssert(*sd.min == 1, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 1, AipsError); AlwaysAssert(sd.npts == 5, AipsError); AlwaysAssert(sd.rms == sqrt(22.5/5.0), AipsError); AlwaysAssert(sd.stddev == sqrt(0.625), AipsError); AlwaysAssert(sd.sum == 10, AipsError); AlwaysAssert(sd.sumsq == 22.5, AipsError); AlwaysAssert(sd.variance == 0.625, AipsError); AlwaysAssert( cs.getStatisticIndex(StatisticsData::MAX) == std::pair(0, 3), AipsError ); AlwaysAssert( cs.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 1), AipsError ); AlwaysAssert(cs.getStatistic( StatisticsData::NPTS) == 5, AipsError ); AlwaysAssert(cs.getStatistic( StatisticsData::RMS) == sqrt(22.5/5.0), AipsError ); } { // just another way of specifying the data ClassicalStatistics cs1; cs1.setData(k, 5); StatsData sd = cs1.getStatistics(); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 3, AipsError); AlwaysAssert(sd.maxpos.first == 0, AipsError); AlwaysAssert(sd.maxpos.second, AipsError); AlwaysAssert(sd.mean == 2, AipsError); AlwaysAssert(*sd.min == 1, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 1, AipsError); AlwaysAssert(sd.npts == 5, AipsError); AlwaysAssert(sd.rms == sqrt(22.5/5.0), AipsError); AlwaysAssert(sd.stddev == sqrt(0.625), AipsError); AlwaysAssert(sd.sum == 10, AipsError); AlwaysAssert(sd.sumsq == 22.5, AipsError); AlwaysAssert(sd.variance == 0.625, AipsError); AlwaysAssert( cs1.getStatisticIndex(StatisticsData::MAX) == std::pair(0, 3), AipsError ); AlwaysAssert( cs1.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 1), AipsError ); AlwaysAssert(cs1.getStatistic( StatisticsData::NPTS) == 5, AipsError ); AlwaysAssert(cs1.getStatistic( StatisticsData::RMS) == sqrt(22.5/5.0), AipsError ); } { // two datasets ClassicalStatistics< Double, std::vector::const_iterator, std::vector::const_iterator > cs; cs.setData(v0.begin(), v0.size()); cs.addData(v1.begin(), v1.size()); StatsData sd = cs.getStatistics(); Double variance = (211.5 - 33.0*33.0/8.0)/7.0; AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 10, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 2, AipsError); AlwaysAssert(sd.mean == 33.0/8.0, AipsError); AlwaysAssert(*sd.min == 1, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 1, AipsError); AlwaysAssert(sd.npts == 8, AipsError); AlwaysAssert(sd.rms == sqrt(211.5/8.0), AipsError); AlwaysAssert(near(sd.stddev, sqrt(variance)), AipsError); AlwaysAssert(sd.sum == 33, AipsError); AlwaysAssert(sd.sumsq == 211.5, AipsError); AlwaysAssert(near(sd.variance, variance), AipsError); AlwaysAssert( cs.getStatisticIndex(StatisticsData::MAX) == std::pair(1, 2), AipsError ); AlwaysAssert( cs.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 1), AipsError ); AlwaysAssert(cs.getStatistic( StatisticsData::NPTS) == 8, AipsError ); AlwaysAssert(cs.getStatistic( StatisticsData::RMS) == sqrt(211.5/8.0), AipsError ); // Now reverse the order that the datasets were added. results // should be the same except for min and max dataset locations cs.setData(v1.begin(), v1.size()); cs.addData(v0.begin(), v0.size()); sd = cs.getStatistics(); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 10, AipsError); AlwaysAssert(sd.maxpos.first == 0, AipsError); AlwaysAssert(sd.maxpos.second == 2, AipsError); AlwaysAssert(sd.mean == 33.0/8.0, AipsError); AlwaysAssert(*sd.min == 1, AipsError); AlwaysAssert(sd.minpos.first == 1, AipsError); AlwaysAssert(sd.minpos.second == 1, AipsError); AlwaysAssert(sd.npts == 8, AipsError); AlwaysAssert(sd.rms == sqrt(211.5/8.0), AipsError); AlwaysAssert(near(sd.stddev, sqrt(variance)), AipsError); AlwaysAssert(sd.sum == 33, AipsError); AlwaysAssert(sd.sumsq == 211.5, AipsError); AlwaysAssert(near(sd.variance, variance), AipsError); AlwaysAssert( cs.getStatisticIndex(StatisticsData::MAX) == std::pair(0, 2), AipsError ); AlwaysAssert( cs.getStatisticIndex(StatisticsData::MIN) == std::pair(1, 1), AipsError ); AlwaysAssert(cs.getStatistic( StatisticsData::NPTS) == 8, AipsError ); AlwaysAssert(cs.getStatistic( StatisticsData::RMS) == sqrt(211.5/8.0), AipsError ); } { // Test accumulating as datasets are added. std::vector t0(5); t0[0] = 1.5; t0[1] = 1; t0[2] = 2; t0[3] = 3; t0[4] = 2.5; std::vector t1(3); t1[0] = 5; t1[1] = 8; t1[2] = 10; ClassicalStatistics< Double, std::vector::const_iterator, std::vector::const_iterator > cs; cs.setCalculateAsAdded(False); cs.setData(t0.begin(), t0.size()); std::fill(t0.begin(), t0.begin()+t0.size(), 0); cs.addData(t1.begin(), t1.size()); std::fill(t1.begin(), t1.begin()+t1.size(), 0); StatsData sd = cs.getStatistics(); // not accumulating as added, so everything is zero, // and with multi-threading, the min and max positions // could be anywhere in the datasets since all values // are equal, so no longer test for those AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 0, AipsError); AlwaysAssert(sd.mean == 0, AipsError); AlwaysAssert(*sd.min == 0, AipsError); AlwaysAssert(sd.npts == 8, AipsError); AlwaysAssert(sd.rms == 0, AipsError); AlwaysAssert(sd.stddev == 0, AipsError); AlwaysAssert(sd.sum == 0, AipsError); AlwaysAssert(sd.sumsq == 0, AipsError); AlwaysAssert(sd.variance == 0, AipsError); t0[0] = 1.5; t0[1] = 1; t0[2] = 2; t0[3] = 3; t0[4] = 2.5; t1[0] = 5; t1[1] = 8; t1[2] = 10; Bool exceptionRaised = False; try { cs.setCalculateAsAdded(True); } catch (std::exception& x) { exceptionRaised = True; } AlwaysAssert(exceptionRaised, AipsError); cs.reset(); cs.setCalculateAsAdded(True); cs.setData(t0.begin(), t0.size()); std::fill(t0.begin(), t0.begin()+t0.size(), 0); cs.addData(t1.begin(), t1.size()); std::fill(t1.begin(), t1.begin()+t1.size(), 0); sd = cs.getStatistics(); Double variance = (211.5 - 33.0*33.0/8.0)/7.0; AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 10, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 2, AipsError); AlwaysAssert(sd.mean == 33.0/8.0, AipsError); AlwaysAssert(*sd.min == 1, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 1, AipsError); AlwaysAssert(sd.npts == 8, AipsError); AlwaysAssert(sd.rms == sqrt(211.5/8.0), AipsError); AlwaysAssert(near(sd.stddev, sqrt(variance)), AipsError); AlwaysAssert(sd.sum == 33, AipsError); AlwaysAssert(sd.sumsq == 211.5, AipsError); AlwaysAssert(sd.variance == variance, AipsError); } { // two datasets, stride = 2,1 ClassicalStatistics< Double, std::vector::const_iterator, std::vector::const_iterator > cs; cs.setData(v0.begin(), v0.size(), 2); cs.addData(v1.begin(), v1.size()); StatsData sd = cs.getStatistics(); Double variance = (201.5 - 29.0*29.0/6.0)/5.0; AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 10, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 2, AipsError); AlwaysAssert(sd.mean == 29.0/6.0, AipsError); AlwaysAssert(*sd.min == 1.5, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 2, AipsError); AlwaysAssert(sd.npts == 6, AipsError); AlwaysAssert(sd.rms == sqrt(201.5/6.0), AipsError); AlwaysAssert(near(sd.stddev, sqrt(variance)), AipsError); AlwaysAssert(sd.sum == 29, AipsError); AlwaysAssert(sd.sumsq == 201.5, AipsError); AlwaysAssert(near(sd.variance, variance), AipsError); } { // data ranges ClassicalStatistics< Double, std::vector::const_iterator, std::vector::const_iterator > cs; std::vector > r0(1); r0[0].first = 5; r0[0].second = -5; Bool expectedFail = False; try { cs.setData(v0.begin(), 3, r0); } catch (const std::exception& x) { expectedFail = True; } AlwaysAssert(expectedFail, AipsError); r0[0].first = 2.4; r0[0].second = 6; std::vector > r1(2); r1[0].first = 9; r1[0].second = 11; r1[1].first = 2; r1[1].second = 7; cs.setData(v0.begin(), v0.size(), r0); cs.addData(v1.begin(), v1.size(), r1, False); StatsData sd = cs.getStatistics(); Double variance = (79.25 - 13.5*13.5/3.0)/2.0; AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 8, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 1, AipsError); AlwaysAssert(sd.mean == 13.5/3.0, AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 4, AipsError); AlwaysAssert(sd.npts == 3, AipsError); AlwaysAssert(sd.rms == sqrt(79.25/3.0), AipsError); AlwaysAssert(near(sd.stddev, sqrt(variance)), AipsError); AlwaysAssert(sd.sum == 13.5, AipsError); AlwaysAssert(sd.sumsq == 79.25, AipsError); AlwaysAssert(near(sd.variance, variance), AipsError); } { // mask ClassicalStatistics::const_iterator, vector::const_iterator> cs; vector m0(v0.size()); m0[0] = False; m0[1] = False; m0[2] = False; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = False; m1[1] = True; m1[2] = False; cs.setData(v0.begin(), m0.begin(), v0.size()); cs.addData(v1.begin(), m1.begin(), v1.size()); StatsData sd = cs.getStatistics(); Double variance = (79.25 - 13.5*13.5/3.0)/2.0; AlwaysAssert(sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 8, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 1, AipsError); AlwaysAssert(sd.mean == 13.5/3.0, AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 4, AipsError); AlwaysAssert(sd.npts == 3, AipsError); AlwaysAssert(sd.rms == sqrt(79.25/3.0), AipsError); AlwaysAssert(near(sd.stddev, sqrt(variance)), AipsError); AlwaysAssert(sd.sum == 13.5, AipsError); AlwaysAssert(sd.sumsq == 79.25, AipsError); AlwaysAssert(near(sd.variance, variance), AipsError); // test cloning gives same results std::shared_ptr< ClassicalStatistics< Double, std::vector::const_iterator, std::vector::const_iterator > > cs1( dynamic_cast< ClassicalStatistics< Double, std::vector::const_iterator, std::vector::const_iterator >* >(cs.clone()) ); StatsData sd1 = cs1->getStatistics(); AlwaysAssert(sd1.masked, AipsError); AlwaysAssert(! sd1.weighted, AipsError); AlwaysAssert(*sd1.max == *sd.max, AipsError); AlwaysAssert(sd1.maxpos.first == sd.maxpos.first , AipsError); AlwaysAssert(sd1.maxpos.second == sd.maxpos.second, AipsError); AlwaysAssert(sd1.mean == sd.mean, AipsError); AlwaysAssert(*sd1.min == *sd.min, AipsError); AlwaysAssert(sd1.minpos.first == sd.minpos.first, AipsError); AlwaysAssert(sd1.minpos.second == sd.minpos.second, AipsError); AlwaysAssert(sd1.npts == sd.npts, AipsError); AlwaysAssert(sd1.rms == sd.rms, AipsError); AlwaysAssert(sd1.stddev == sd.stddev, AipsError); AlwaysAssert(sd1.sum == sd.sum, AipsError); AlwaysAssert(sd1.sumsq == sd.sumsq, AipsError); AlwaysAssert(sd1.variance == sd.variance, AipsError); } { // mask and ranges ClassicalStatistics::const_iterator, vector::const_iterator> cs; vector m0(v0.size()); m0[0] = False; m0[1] = True; m0[2] = True; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = True; m1[1] = True; m1[2] = False; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 9; cs.setData(v0.begin(), m0.begin(), v0.size(), r0, False); cs.addData(v1.begin(), m1.begin(), v1.size(), r1, True); StatsData sd = cs.getStatistics(); Double variance = (79.25 - 13.5*13.5/3.0)/2.0; AlwaysAssert(sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 8, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 1, AipsError); AlwaysAssert(sd.mean == 13.5/3.0, AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 4, AipsError); AlwaysAssert(sd.npts == 3, AipsError); AlwaysAssert(sd.rms == sqrt(79.25/3.0), AipsError); AlwaysAssert(near(sd.stddev, sqrt(variance)), AipsError); AlwaysAssert(sd.sum == 13.5, AipsError); AlwaysAssert(sd.sumsq == 79.25, AipsError); AlwaysAssert(near(sd.variance, variance), AipsError); } { // weights ClassicalStatistics::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; cs.setData(v0.begin(), w0.begin(), w0.size()); cs.addData(v1.begin(), w1.begin(), w1.size()); StatsData sd = cs.getStatistics(); Double variance = (529.0 - 82.0*82.0/20.0)/19.0; AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(*sd.max == 10, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 2, AipsError); AlwaysAssert(near(sd.mean, 4.1), AipsError); AlwaysAssert(*sd.min == 1, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 1, AipsError); AlwaysAssert(sd.npts == 7, AipsError); AlwaysAssert(sd.rms == sqrt(529.0/20.0), AipsError); AlwaysAssert(near(sd.stddev, sqrt(variance)), AipsError); AlwaysAssert(sd.sum == 82.0, AipsError); AlwaysAssert(sd.sumweights == 20.0, AipsError); AlwaysAssert(sd.sumsq == 529.0, AipsError); AlwaysAssert(near(sd.variance, variance), AipsError); } { // integer weights ClassicalStatistics::const_iterator, vector::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; cs.setData(v0.begin(), w0.begin(), w0.size()); cs.addData(v1.begin(), w1.begin(), w1.size()); StatsData sd = cs.getStatistics(); Double variance = (529.0 - 82.0*82.0/20.0)/19.0; AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(*sd.max == 10, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 2, AipsError); AlwaysAssert(near(sd.mean, 4.1), AipsError); AlwaysAssert(*sd.min == 1, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 1, AipsError); AlwaysAssert(sd.npts == 7, AipsError); AlwaysAssert(sd.rms == sqrt(529.0/20.0), AipsError); AlwaysAssert(near(sd.stddev, sqrt(variance)), AipsError); AlwaysAssert(sd.sum == 82.0, AipsError); AlwaysAssert(sd.sumweights == 20.0, AipsError); AlwaysAssert(sd.sumsq == 529.0, AipsError); AlwaysAssert(near(sd.variance, variance), AipsError); } { // weights and ranges ClassicalStatistics::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 9; cs.setData(v0.begin(), w0.begin(), v0.size(), r0, False); cs.addData(v1.begin(), w1.begin(), v1.size(), r1, True); StatsData sd = cs.getStatistics(); Double variance = (195.25 - 40.5*40.5/11.0)/10.0; AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(*sd.max == 8, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 1, AipsError); AlwaysAssert(near(sd.mean, 40.5/11.0), AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 4, AipsError); AlwaysAssert(sd.npts == 3, AipsError); AlwaysAssert(sd.rms == sqrt(195.25/11.0), AipsError); AlwaysAssert(near(sd.stddev, sqrt(variance)), AipsError); AlwaysAssert(sd.sum == 40.5, AipsError); AlwaysAssert(sd.sumweights == 11.0, AipsError); AlwaysAssert(sd.sumsq == 195.25, AipsError); AlwaysAssert(near(sd.variance, variance), AipsError); } { // integer weights; ranges ClassicalStatistics::const_iterator, vector::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 9; cs.setData(v0.begin(), w0.begin(), v0.size(), r0, False); cs.addData(v1.begin(), w1.begin(), v1.size(), r1, True); StatsData sd = cs.getStatistics(); Double variance = (195.25 - 40.5*40.5/11.0)/10.0; AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(*sd.max == 8, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 1, AipsError); AlwaysAssert(near(sd.mean, 40.5/11.0), AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 4, AipsError); AlwaysAssert(sd.npts == 3, AipsError); AlwaysAssert(sd.rms == sqrt(195.25/11.0), AipsError); AlwaysAssert(near(sd.stddev, sqrt(variance)), AipsError); AlwaysAssert(sd.sum == 40.5, AipsError); AlwaysAssert(sd.sumweights == 11.0, AipsError); AlwaysAssert(sd.sumsq == 195.25, AipsError); AlwaysAssert(near(sd.variance, variance), AipsError); } { // weights, ranges, and masks ClassicalStatistics::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector m0(v0.size()); m0[0] = True; m0[1] = True; m0[2] = True; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = True; m1[1] = True; m1[2] = False; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 12; cs.setData(v0.begin(), w0.begin(), m0.begin(), v0.size(), r0, False); cs.addData(v1.begin(), w1.begin(), m1.begin(), v1.size(), r1, True); StatsData sd = cs.getStatistics(); Double variance = (195.25 - 40.5*40.5/11.0)/10.0; AlwaysAssert(sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(*sd.max == 8, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 1, AipsError); AlwaysAssert(near(sd.mean, 40.5/11.0), AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 4, AipsError); AlwaysAssert(sd.npts == 3, AipsError); AlwaysAssert(sd.rms == sqrt(195.25/11.0), AipsError); AlwaysAssert(near(sd.stddev, sqrt(variance)), AipsError); AlwaysAssert(sd.sum == 40.5, AipsError); AlwaysAssert(sd.sumweights == 11.0, AipsError); AlwaysAssert(sd.sumsq == 195.25, AipsError); AlwaysAssert(near(sd.variance, variance), AipsError); AlwaysAssert( cs.getStatisticIndex(StatisticsData::MAX) == std::pair(1, 1), AipsError ); AlwaysAssert( cs.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 4), AipsError ); AlwaysAssert(cs.getStatistic( StatisticsData::NPTS) == 3, AipsError ); AlwaysAssert(cs.getStatistic( StatisticsData::RMS) == sqrt(195.25/11.0), AipsError ); } { // integer weights; ranges, and masks ClassicalStatistics::const_iterator, vector::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector m0(v0.size()); m0[0] = True; m0[1] = True; m0[2] = True; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = True; m1[1] = True; m1[2] = False; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 12; cs.setData(v0.begin(), w0.begin(), m0.begin(), v0.size(), r0, False); cs.addData(v1.begin(), w1.begin(), m1.begin(), v1.size(), r1, True); StatsData sd = cs.getStatistics(); Double variance = (195.25 - 40.5*40.5/11.0)/10.0; AlwaysAssert(sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(*sd.max == 8, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 1, AipsError); AlwaysAssert(near(sd.mean, 40.5/11.0), AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 4, AipsError); AlwaysAssert(sd.npts == 3, AipsError); AlwaysAssert(sd.rms == sqrt(195.25/11.0), AipsError); AlwaysAssert(near(sd.stddev, sqrt(variance)), AipsError); AlwaysAssert(sd.sum == 40.5, AipsError); AlwaysAssert(sd.sumweights == 11.0, AipsError); AlwaysAssert(sd.sumsq == 195.25, AipsError); AlwaysAssert(near(sd.variance, variance), AipsError); AlwaysAssert( cs.getStatisticIndex(StatisticsData::MAX) == std::pair(1, 1), AipsError ); AlwaysAssert( cs.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 4), AipsError ); AlwaysAssert(cs.getStatistic( StatisticsData::NPTS) == 3, AipsError ); AlwaysAssert(cs.getStatistic( StatisticsData::RMS) == sqrt(195.25/11.0), AipsError ); } { // weights, masks ClassicalStatistics::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector m0(v0.size()); m0[0] = True; m0[1] = False; m0[2] = False; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = False; m1[1] = True; m1[2] = False; cs.setData(v0.begin(), w0.begin(), m0.begin(), v0.size()); cs.addData(v1.begin(), w1.begin(), m1.begin(), v1.size()); StatsData sd = cs.getStatistics(); Double variance = (195.25 - 40.5*40.5/11.0)/10.0; AlwaysAssert(sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(*sd.max == 8, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 1, AipsError); AlwaysAssert(near(sd.mean, 40.5/11.0), AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 4, AipsError); AlwaysAssert(sd.npts == 3, AipsError); AlwaysAssert(sd.rms == sqrt(195.25/11.0), AipsError); AlwaysAssert(near(sd.stddev, sqrt(variance)), AipsError); AlwaysAssert(sd.sum == 40.5, AipsError); AlwaysAssert(sd.sumweights == 11.0, AipsError); AlwaysAssert(sd.sumsq == 195.25, AipsError); AlwaysAssert(near(sd.variance, variance), AipsError); } { // weights, masks, no max/min (CAS-11859 fix) ClassicalStatistics::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector m0(v0.size()); m0[0] = True; m0[1] = False; m0[2] = False; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = False; m1[1] = True; m1[2] = False; std::set targets; // excludes max/min, so exercises another code branch targets.insert(StatisticsData::VARIANCE); cs.setStatsToCalculate(targets); cs.setData(v0.begin(), w0.begin(), m0.begin(), v0.size()); cs.addData(v1.begin(), w1.begin(), m1.begin(), v1.size()); StatsData sd = cs.getStatistics(); Double variance = (195.25 - 40.5*40.5/11.0)/10.0; AlwaysAssert(sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(near(sd.mean, 40.5/11.0), AipsError); AlwaysAssert(sd.npts == 3, AipsError); AlwaysAssert(sd.rms == sqrt(195.25/11.0), AipsError); AlwaysAssert(near(sd.stddev, sqrt(variance)), AipsError); AlwaysAssert(sd.sum == 40.5, AipsError); AlwaysAssert(sd.sumweights == 11.0, AipsError); AlwaysAssert(sd.sumsq == 195.25, AipsError); AlwaysAssert(near(sd.variance, variance), AipsError); } { // integer weights; masks ClassicalStatistics::const_iterator, vector::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector m0(v0.size()); m0[0] = True; m0[1] = False; m0[2] = False; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = False; m1[1] = True; m1[2] = False; cs.setData(v0.begin(), w0.begin(), m0.begin(), v0.size()); cs.addData(v1.begin(), w1.begin(), m1.begin(), v1.size()); StatsData sd = cs.getStatistics(); Double variance = (195.25 - 40.5*40.5/11.0)/10.0; AlwaysAssert(sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(*sd.max == 8, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 1, AipsError); AlwaysAssert(near(sd.mean, 40.5/11.0), AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 4, AipsError); AlwaysAssert(sd.npts == 3, AipsError); AlwaysAssert(sd.rms == sqrt(195.25/11.0), AipsError); AlwaysAssert(near(sd.stddev, sqrt(variance)), AipsError); AlwaysAssert(sd.sum == 40.5, AipsError); AlwaysAssert(sd.sumweights == 11.0, AipsError); AlwaysAssert(sd.sumsq == 195.25, AipsError); AlwaysAssert(near(sd.variance, variance), AipsError); } { // getMinMax(), two datasets ClassicalStatistics::const_iterator, vector::const_iterator> cs; cs.setData(v0.begin(), v0.size()); cs.addData(v1.begin(), v1.size()); Double mymin, mymax; cs.getMinMax(mymin, mymax); AlwaysAssert(mymin == 1, AipsError); AlwaysAssert(mymax == 10, AipsError); } { // getMinMax(), two datasets, stride = 2,1 ClassicalStatistics::const_iterator, vector::const_iterator> cs; cs.setData(v0.begin(), 3, 2); cs.addData(v1.begin(), v1.size()); Double mymin, mymax; cs.getMinMax(mymin, mymax); AlwaysAssert(mymin == 1.5, AipsError); AlwaysAssert(mymax == 10, AipsError); } { // getMinMax(), data ranges ClassicalStatistics::const_iterator, vector::const_iterator> cs; vector > r0(1); r0[0].first = 2.4; r0[0].second = 6; vector > r1(2); r1[0].first = 9; r1[0].second = 11; r1[1].first = 2; r1[1].second = 7; cs.setData(v0.begin(), v0.size(), r0); cs.addData(v1.begin(), v1.size(), r1, False); Double mymin, mymax; cs.getMinMax(mymin, mymax); AlwaysAssert(mymin == 2.5, AipsError); AlwaysAssert(mymax == 8, AipsError); } { // getMinMax(), mask ClassicalStatistics::const_iterator, vector::const_iterator> cs; vector m0(v0.size()); m0[0] = False; m0[1] = False; m0[2] = False; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = False; m1[1] = True; m1[2] = False; cs.setData(v0.begin(), m0.begin(), v0.size()); cs.addData(v1.begin(), m1.begin(), v1.size()); Double mymin, mymax; cs.getMinMax(mymin, mymax); AlwaysAssert(mymin == 2.5, AipsError); AlwaysAssert(mymax == 8, AipsError); } { // getMinMax(), mask and ranges ClassicalStatistics::const_iterator, vector::const_iterator> cs; vector m0(v0.size()); m0[0] = False; m0[1] = True; m0[2] = True; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = True; m1[1] = True; m1[2] = False; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 9; cs.setData(v0.begin(), m0.begin(), v0.size(), r0, False); cs.addData(v1.begin(), m1.begin(), v1.size(), r1, True); Double mymin, mymax; cs.getMinMax(mymin, mymax); AlwaysAssert(mymin == 2.5, AipsError); AlwaysAssert(mymax == 8, AipsError); } { // getMinMax, weights ClassicalStatistics::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 1; w0[1] = 0; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 0; cs.setData(v0.begin(), w0.begin(), w0.size()); cs.addData(v1.begin(), w1.begin(), w1.size()); Double mymin, mymax; cs.getMinMax(mymin, mymax); AlwaysAssert(mymin == 1.5, AipsError); AlwaysAssert(mymax == 8, AipsError); } { // getMinMax, integer weights ClassicalStatistics::const_iterator, vector::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 1; w0[1] = 0; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 0; cs.setData(v0.begin(), w0.begin(), w0.size()); cs.addData(v1.begin(), w1.begin(), w1.size()); Double mymin, mymax; cs.getMinMax(mymin, mymax); AlwaysAssert(mymin == 1.5, AipsError); AlwaysAssert(mymax == 8, AipsError); } { // getMinMax(), weights and ranges ClassicalStatistics::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 9; cs.setData(v0.begin(), w0.begin(), v0.size(), r0, False); cs.addData(v1.begin(), w1.begin(), v1.size(), r1, True); Double mymin, mymax; cs.getMinMax(mymin, mymax); AlwaysAssert(mymin == 2.5, AipsError); AlwaysAssert(mymax == 8, AipsError); } { // getMinMax(), integer weights, and ranges ClassicalStatistics::const_iterator, vector::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 9; cs.setData(v0.begin(), w0.begin(), v0.size(), r0, False); cs.addData(v1.begin(), w1.begin(), v1.size(), r1, True); Double mymin, mymax; cs.getMinMax(mymin, mymax); AlwaysAssert(mymin == 2.5, AipsError); AlwaysAssert(mymax == 8, AipsError); } { // getMinMax(), weights, ranges, and masks ClassicalStatistics::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector m0(v0.size()); m0[0] = True; m0[1] = True; m0[2] = True; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = True; m1[1] = True; m1[2] = False; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 12; cs.setData(v0.begin(), w0.begin(), m0.begin(), v0.size(), r0, False); cs.addData(v1.begin(), w1.begin(), m1.begin(), v1.size(), r1, True); Double mymin, mymax; cs.getMinMax(mymin, mymax); AlwaysAssert(mymin == 2.5, AipsError); AlwaysAssert(mymax == 8, AipsError); } { // getMinMax(), integer weights, ranges, and masks ClassicalStatistics::const_iterator, vector::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector m0(v0.size()); m0[0] = True; m0[1] = True; m0[2] = True; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = True; m1[1] = True; m1[2] = False; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 12; cs.setData(v0.begin(), w0.begin(), m0.begin(), v0.size(), r0, False); cs.addData(v1.begin(), w1.begin(), m1.begin(), v1.size(), r1, True); Double mymin, mymax; cs.getMinMax(mymin, mymax); AlwaysAssert(mymin == 2.5, AipsError); AlwaysAssert(mymax == 8, AipsError); } { // general quantile exceptions ClassicalStatistics::const_iterator, vector::const_iterator> cs; cs.setData(v0.begin(), v0.size()); cs.addData(v1.begin(), v1.size()); Bool thrown = False; try { cs.getQuantile(0); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); thrown = False; try { cs.getQuantile(1); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); } { // getQuantile(), no weights, no mask, no ranges ClassicalStatistics::const_iterator, vector::const_iterator> cs; cs.setData(v0.begin(), v0.size()); cs.addData(v1.begin(), v1.size()); Double q = cs.getQuantile(0.1); AlwaysAssert(q == 1.0, AipsError); q = cs.getQuantile(0.2); AlwaysAssert(q == 1.5, AipsError); q = cs.getQuantile(0.3); AlwaysAssert(q == 2.0, AipsError); q = cs.getQuantile(0.4); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.5); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.6); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.7); AlwaysAssert(q == 5.0, AipsError); q = cs.getQuantile(0.8); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.9); AlwaysAssert(q == 10.0, AipsError); } { // getQuantile(): two datasets, stride = 2,1 // 1.5, 2, 2.5 5, 8, 10 ClassicalStatistics::const_iterator, vector::const_iterator> cs; cs.setData(v0.begin(), v0.size(), 2); cs.addData(v1.begin(), v1.size()); Double q = cs.getQuantile(0.1); AlwaysAssert(q == 1.5, AipsError); q = cs.getQuantile(0.2); AlwaysAssert(q == 2.0, AipsError); q = cs.getQuantile(0.3); AlwaysAssert(q == 2.0, AipsError); q = cs.getQuantile(0.4); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.5); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.6); AlwaysAssert(q == 5.0, AipsError); q = cs.getQuantile(0.7); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.8); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.9); AlwaysAssert(q == 10.0, AipsError); } { // getQuantile(), ranges ClassicalStatistics::const_iterator, vector::const_iterator> cs; vector > r0(1), r1(2); r0[0].first = 2.4; r0[0].second = 6; r1[0].first = 9; r1[0].second = 11; r1[1].first = 2; r1[1].second = 7; cs.setData(v0.begin(), v0.size(), r0); cs.addData(v1.begin(), v1.size(), r1, False); // 2.5, 3, 8 Double q = cs.getQuantile(0.1); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.2); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.3); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.4); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.5); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.6); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.7); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.8); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.9); AlwaysAssert(q == 8.0, AipsError); } { // getQuantile(): mask ClassicalStatistics::const_iterator, vector::const_iterator> cs; vector m0(v0.size()); m0[0] = False; m0[1] = False; m0[2] = False; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = False; m1[1] = True; m1[2] = False; cs.setData(v0.begin(), m0.begin(), v0.size()); cs.addData(v1.begin(), m1.begin(), v1.size()); // 2.5, 3, 8 Double q = cs.getQuantile(0.1); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.2); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.3); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.4); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.5); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.6); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.7); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.8); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.9); AlwaysAssert(q == 8.0, AipsError); } { //getQuantile(): mask and ranges ClassicalStatistics::const_iterator, vector::const_iterator> cs; vector m0(v0.size()); m0[0] = False; m0[1] = True; m0[2] = True; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = True; m1[1] = True; m1[2] = False; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 9; cs.setData(v0.begin(), m0.begin(), v0.size(), r0, False); cs.addData(v1.begin(), m1.begin(), v1.size(), r1, True); // 2.5, 3, 8 Double q = cs.getQuantile(0.1); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.2); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.3); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.4); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.5); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.6); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.7); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.8); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.9); AlwaysAssert(q == 8.0, AipsError); } { // getQuantile(): weights ClassicalStatistics::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; cs.setData(v0.begin(), w0.begin(), w0.size()); cs.addData(v1.begin(), w1.begin(), w1.size()); // 1, 1.5, 2.5, 3, 5, 8, 10 Double q = cs.getQuantile(0.1); AlwaysAssert(q == 1.0, AipsError); q = cs.getQuantile(0.2); AlwaysAssert(q == 1.5, AipsError); q = cs.getQuantile(0.3); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.4); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.5); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.6); AlwaysAssert(q == 5.0, AipsError); q = cs.getQuantile(0.7); AlwaysAssert(q == 5.0, AipsError); q = cs.getQuantile(0.8); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.9); AlwaysAssert(q == 10.0, AipsError); } { // getQuantile(): integer weights ClassicalStatistics::const_iterator, vector::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; cs.setData(v0.begin(), w0.begin(), w0.size()); cs.addData(v1.begin(), w1.begin(), w1.size()); // 1, 1.5, 2.5, 3, 5, 8, 10 Double q = cs.getQuantile(0.1); AlwaysAssert(q == 1.0, AipsError); q = cs.getQuantile(0.2); AlwaysAssert(q == 1.5, AipsError); q = cs.getQuantile(0.3); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.4); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.5); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.6); AlwaysAssert(q == 5.0, AipsError); q = cs.getQuantile(0.7); AlwaysAssert(q == 5.0, AipsError); q = cs.getQuantile(0.8); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.9); AlwaysAssert(q == 10.0, AipsError); } { // getQuantile(): ranges and weights ClassicalStatistics::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 9; cs.setData(v0.begin(), w0.begin(), v0.size(), r0, False); cs.addData(v1.begin(), w1.begin(), v1.size(), r1, True); // 2.5, 3, 8 Double q = cs.getQuantile(0.1); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.2); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.3); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.4); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.5); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.6); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.7); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.8); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.9); AlwaysAssert(q == 8.0, AipsError); } { // getQuantile(): ranges and integer weights ClassicalStatistics::const_iterator, vector::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 9; cs.setData(v0.begin(), w0.begin(), v0.size(), r0, False); cs.addData(v1.begin(), w1.begin(), v1.size(), r1, True); // 2.5, 3, 8 Double q = cs.getQuantile(0.1); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.2); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.3); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.4); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.5); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.6); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.7); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.8); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.9); AlwaysAssert(q == 8.0, AipsError); } { // getQuantile(): weights and mask ClassicalStatistics::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector m0(v0.size()); m0[0] = True; m0[1] = False; m0[2] = False; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = False; m1[1] = True; m1[2] = False; cs.setData(v0.begin(), w0.begin(), m0.begin(), v0.size()); cs.addData(v1.begin(), w1.begin(), m1.begin(), v1.size()); // 2.5, 3, 8 Double q = cs.getQuantile(0.1); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.2); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.3); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.4); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.5); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.6); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.7); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.8); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.9); AlwaysAssert(q == 8.0, AipsError); } { // getQuantile(): integer weights and mask ClassicalStatistics::const_iterator, vector::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector m0(v0.size()); m0[0] = True; m0[1] = False; m0[2] = False; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = False; m1[1] = True; m1[2] = False; cs.setData(v0.begin(), w0.begin(), m0.begin(), v0.size()); cs.addData(v1.begin(), w1.begin(), m1.begin(), v1.size()); // 2.5, 3, 8 Double q = cs.getQuantile(0.1); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.2); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.3); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.4); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.5); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.6); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.7); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.8); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.9); AlwaysAssert(q == 8.0, AipsError); } { // getQuantile(): weights, mask, ranges ClassicalStatistics::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector m0(v0.size()); m0[0] = True; m0[1] = True; m0[2] = True; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = True; m1[1] = True; m1[2] = False; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 12; cs.setData(v0.begin(), w0.begin(), m0.begin(), v0.size(), r0, False); cs.addData(v1.begin(), w1.begin(), m1.begin(), v1.size(), r1, True); // 2.5, 3, 8 Double q = cs.getQuantile(0.1); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.2); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.3); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.4); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.5); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.6); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.7); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.8); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.9); AlwaysAssert(q == 8.0, AipsError); std::set quantiles; quantiles.insert(0.1); quantiles.insert(0.2); quantiles.insert(0.3); quantiles.insert(0.4); quantiles.insert(0.5); quantiles.insert(0.6); quantiles.insert(0.7); quantiles.insert(0.8); quantiles.insert(0.9); std::map qs = cs.getQuantiles(quantiles); AlwaysAssert(qs[0.1] == 2.5, AipsError); AlwaysAssert(qs[0.2] == 2.5, AipsError); AlwaysAssert(qs[0.3] == 2.5, AipsError); AlwaysAssert(qs[0.4] == 3.0, AipsError); AlwaysAssert(qs[0.5] == 3.0, AipsError); AlwaysAssert(qs[0.6] == 3.0, AipsError); AlwaysAssert(qs[0.7] == 8.0, AipsError); AlwaysAssert(qs[0.8] == 8.0, AipsError); AlwaysAssert(qs[0.9] == 8.0, AipsError); } { // getQuantile(): integer weights, mask, ranges ClassicalStatistics::const_iterator, vector::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector m0(v0.size()); m0[0] = True; m0[1] = True; m0[2] = True; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = True; m1[1] = True; m1[2] = False; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 12; cs.setData(v0.begin(), w0.begin(), m0.begin(), v0.size(), r0, False); cs.addData(v1.begin(), w1.begin(), m1.begin(), v1.size(), r1, True); // 2.5, 3, 8 Double q = cs.getQuantile(0.1); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.2); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.3); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.4); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.5); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.6); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.7); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.8); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.9); AlwaysAssert(q == 8.0, AipsError); std::set quantiles; quantiles.insert(0.1); quantiles.insert(0.2); quantiles.insert(0.3); quantiles.insert(0.4); quantiles.insert(0.5); quantiles.insert(0.6); quantiles.insert(0.7); quantiles.insert(0.8); quantiles.insert(0.9); std::map qs = cs.getQuantiles(quantiles); AlwaysAssert(qs[0.1] == 2.5, AipsError); AlwaysAssert(qs[0.2] == 2.5, AipsError); AlwaysAssert(qs[0.3] == 2.5, AipsError); AlwaysAssert(qs[0.4] == 3.0, AipsError); AlwaysAssert(qs[0.5] == 3.0, AipsError); AlwaysAssert(qs[0.6] == 3.0, AipsError); AlwaysAssert(qs[0.7] == 8.0, AipsError); AlwaysAssert(qs[0.8] == 8.0, AipsError); AlwaysAssert(qs[0.9] == 8.0, AipsError); } { // leave in for compile check ClassicalStatistics::const_iterator, vector::const_iterator> cs; } { // getMedian() ClassicalStatistics::const_iterator, vector::const_iterator> cs; cs.addData(v0.begin(), v0.size()); Double median = cs.getMedian(); AlwaysAssert(median == 2, AipsError); median = cs.getStatistic(StatisticsData::MEDIAN); AlwaysAssert(median == 2, AipsError); cs.reset(); vector m0(v0.size(), True); m0[0] = False; cs.addData(v0.begin(), m0.begin(), v0.size()); median = cs.getMedian(); AlwaysAssert(median == 2, AipsError); } { // getMedianAndQuantiles (even sized data set) std::set quantiles; quantiles.insert(0.1); quantiles.insert(0.2); quantiles.insert(0.3); quantiles.insert(0.4); quantiles.insert(0.5); quantiles.insert(0.6); quantiles.insert(0.7); quantiles.insert(0.8); quantiles.insert(0.9); ClassicalStatistics::const_iterator, vector::const_iterator> cs; cs.setData(v0.begin(), v0.size()); cs.addData(v1.begin(), v1.size()); std::map quantileToValue; Double median = cs.getMedianAndQuantiles(quantileToValue, quantiles); AlwaysAssert(median == 2.75, AipsError); AlwaysAssert(quantileToValue[0.1] == 1.0, AipsError); AlwaysAssert(quantileToValue[0.2] == 1.5, AipsError); AlwaysAssert(quantileToValue[0.3] == 2.0, AipsError); AlwaysAssert(quantileToValue[0.4] == 2.5, AipsError); AlwaysAssert(quantileToValue[0.5] == 2.5, AipsError); AlwaysAssert(quantileToValue[0.6] == 3.0, AipsError); AlwaysAssert(quantileToValue[0.7] == 5.0, AipsError); AlwaysAssert(quantileToValue[0.8] == 8.0, AipsError); AlwaysAssert(quantileToValue[0.9] == 10.0, AipsError); } { // getMedianAndQuantiles (odd sized data set) std::set quantiles; quantiles.insert(0.1); quantiles.insert(0.2); quantiles.insert(0.3); quantiles.insert(0.4); quantiles.insert(0.5); quantiles.insert(0.6); quantiles.insert(0.7); quantiles.insert(0.8); quantiles.insert(0.9); vector > r0(1); r0[0].first = 9; r0[0].second = 11; ClassicalStatistics::const_iterator, vector::const_iterator> cs; cs.setData(v0.begin(), v0.size()); cs.addData(v1.begin(), v1.size(), r0, False); std::map quantileToValue; Double median = cs.getMedianAndQuantiles(quantileToValue, quantiles); AlwaysAssert(median == 2.5, AipsError); AlwaysAssert(quantileToValue[0.1] == 1.0, AipsError); AlwaysAssert(quantileToValue[0.2] == 1.5, AipsError); AlwaysAssert(quantileToValue[0.3] == 2.0, AipsError); AlwaysAssert(quantileToValue[0.4] == 2.0, AipsError); AlwaysAssert(quantileToValue[0.5] == 2.5, AipsError); AlwaysAssert(quantileToValue[0.6] == 3.0, AipsError); AlwaysAssert(quantileToValue[0.7] == 3.0, AipsError); AlwaysAssert(quantileToValue[0.8] == 5.0, AipsError); AlwaysAssert(quantileToValue[0.9] == 8.0, AipsError); } { // getMedianAndQuantiles (even sized data set) ClassicalStatistics::const_iterator, vector::const_iterator> cs; cs.setData(v0.begin(), v0.size()); cs.addData(v1.begin(), v1.size()); Double medabsdevmed = cs.getMedianAbsDevMed(); AlwaysAssert(medabsdevmed == 1.5, AipsError); } { // getMedianAndQuantiles (odd sized data set) vector > r0(1); r0[0].first = 9; r0[0].second = 11; ClassicalStatistics::const_iterator, vector::const_iterator> cs; cs.setData(v0.begin(), v0.size()); cs.addData(v1.begin(), v1.size(), r0, False); Double medabsdevmed = cs.getMedianAbsDevMed(); AlwaysAssert(medabsdevmed == 1.0, AipsError); } uInt npts = (uInt)1e6; vector bigData(npts); vector::iterator iter = bigData.begin(); vector::iterator end = bigData.end(); uInt64 count = 0; while(iter != end) { *iter = count % 2 == 0 ? Double(count) : -Double(count*count); ++iter; ++count; } vector bigMask(npts, True); bigMask[0] = False; { // getMedian() with binning, no ranges, weights, or mask ClassicalStatistics::const_iterator, vector::const_iterator> cs; cs.setData(bigData.begin(), bigData.size()); // enforce a small internal array size so binning algorithm is used Double median = cs.getMedian(NULL, NULL, NULL, 100); AlwaysAssert(median == -0.5, AipsError); } { // getMedian() with mask, but no weights or ranges, using binning ClassicalStatistics::const_iterator, vector::const_iterator> cs; cs.setData(bigData.begin(), bigMask.begin(), bigData.size()); // enforce a small internal array size so binning algorithm is used Double median = cs.getMedian(NULL, NULL, NULL, 100); AlwaysAssert(median == -1, AipsError); } { // getMedianAbsDevMed() with binning, no ranges, weights, or mask ClassicalStatistics::const_iterator, vector::const_iterator> cs; cs.setData(bigData.begin(), bigData.size()); // enforce a small internal array size so binning algorithm is used Double medabsdevmed = cs.getMedianAbsDevMed(NULL, NULL, NULL, 100); AlwaysAssert(medabsdevmed == 998999.5, AipsError); } { // getMedianAbsDevMed() with mask, but no weights or ranges, using binning ClassicalStatistics::const_iterator, vector::const_iterator> cs; cs.setData(bigData.begin(), bigMask.begin(), bigData.size()); // enforce a small internal array size so binning algorithm is used Double medabsdevmed = cs.getMedianAbsDevMed(NULL, NULL, NULL, 100); AlwaysAssert(medabsdevmed == 999001, AipsError); } { // large array with all the same values, getMedianAndQuartile() vector big(100000, 30); ClassicalStatistics::const_iterator, vector::const_iterator> cs; cs.addData(big.begin(), big.size()); std::set quantiles; quantiles.insert(0.25); quantiles.insert(0.75); std::shared_ptr npts; std::shared_ptr mymin, mymax; std::map quantileToValue; Double median = cs.getMedianAndQuantiles( quantileToValue, quantiles, npts, mymin, mymax, 100 ); AlwaysAssert(median == 30, AipsError); AlwaysAssert(quantileToValue[0.25] == 30, AipsError); AlwaysAssert(quantileToValue[0.75] == 30, AipsError); } { // two large array with two unique values, getMedianAndQuartile() ClassicalStatistics::const_iterator, vector::const_iterator> cs; vector big(100000, 30); cs.addData(big.begin(), big.size()); vector big2(50000, -10); cs.addData(big2.begin(), big2.size()); std::set quantiles; quantiles.insert(0.25); quantiles.insert(0.75); std::shared_ptr npts; std::shared_ptr mymin, mymax; std::map quantileToValue; Double median = cs.getMedianAndQuantiles( quantileToValue, quantiles, npts, mymin, mymax, 100 ); AlwaysAssert(median == 30, AipsError); AlwaysAssert(quantileToValue[0.25] == -10, AipsError); AlwaysAssert(quantileToValue[0.75] == 30, AipsError); } { // medium sized randomized array, that can be sorted in memory in one go ClassicalStatistics::const_iterator, vector::const_iterator> cs; vector big(100000); uInt count = 0; vector::iterator iter = big.begin(); vector::iterator end = big.end(); for (; iter!=end; ++iter, ++count) { *iter = count; } std::random_device rd; std::mt19937 g(rd()); std::shuffle(big.begin(), big.end(), g); cs.addData(big.begin(), big.size()); std::set quantiles; quantiles.insert(0.25); quantiles.insert(0.75); std::map quantileToValue; Double median = cs.getMedianAndQuantiles( quantileToValue, quantiles ); AlwaysAssert(median == 49999.5, AipsError); AlwaysAssert(quantileToValue[0.25] == 24999, AipsError); AlwaysAssert(quantileToValue[0.75] == 74999, AipsError); } { // large array, getMinMax() ClassicalStatistics< Double, std::vector::const_iterator, std::vector::const_iterator > cs; std::vector big(1e7); uInt count = 0; std::vector::iterator iter = big.begin(); std::vector::iterator end = big.end(); for (; iter!=end; ++iter, ++count) { *iter = count; } cs.addData(big.begin(), big.size()); Double mymin, mymax; cs.getMinMax(mymin, mymax); AlwaysAssert(mymin == 0, AipsError); AlwaysAssert(mymax == big.size()-1, AipsError); // do it again, but shuffle the elements std::random_device rd; std::mt19937 g(rd()); std::shuffle(big.begin(), big.end(), g); ClassicalStatistics< Double, std::vector::const_iterator, std::vector::const_iterator > cs1; cs1.addData(big.begin(), big.size()); cs1.getMinMax(mymin, mymax); AlwaysAssert(mymin == 0, AipsError); AlwaysAssert(mymax == big.size()-1, AipsError); } { // tests for getNPts() uInt n = 6; uInt size[] = {5000, 80000, 6500, 100000, 19256, 7482}; std::vector > data(n); ClassicalStatistics< Double, std::vector::const_iterator, std::vector::const_iterator > cs; uInt64 expec = 0; for (uInt i=0; i mask3(size[3]); std::fill(mask3.begin(), mask3.begin()+size[3], False); mask3[1000] = True; mask3[1500] = True; expec -= (size[3] - 2); for (uInt i=0; i::const_iterator, vector::const_iterator > cs; vector v {4, 7, 12, 18}; vector w {0.5, 02, 1, 0.9}; cs.setData(v.cbegin(), w.cbegin(), v.size()); auto stats = cs.getStatistics(); cout << "mean " << stats.mean << endl; cout << "variance " << stats.variance << endl; cout << "nvariance " << stats.nvariance << endl; cout << "nvar/n " << stats.nvariance/stats.npts << endl; } } catch (const std::exception& x) { cout << x.what() << endl; return 1; } return 0; } casacore-3.7.1/scimath/StatsFramework/test/tFitToHalfStatistics.cc000066400000000000000000002455341476623553700253120ustar00rootroot00000000000000//# Copyright (C) 1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include #include #include #include #include #include #define COMMA , int main() { try { std::vector v0(5); v0[0] = 2; v0[1] = 1; v0[2] = 1.5; v0[3] = 4; v0[4] = 2.5; std::vector v1(3); v1[0] = 5; v1[1] = 8; v1[2] = 10; { FitToHalfStatistics< Double, std::vector::const_iterator, std::vector::const_iterator > fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); fh.setData(v0.begin(), v0.size()); StatsData sd = fh.getStatistics(); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(near(*sd.max, 3.4), AipsError); AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); AlwaysAssert(sd.mean == 2.2, AipsError); AlwaysAssert(*sd.min == 1, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 1, AipsError); Double npts = 6; Double sumsq = 32.98; Double nvariance = 3.94; AlwaysAssert(sd.npts == npts, AipsError); AlwaysAssert(near(sd.rms, sqrt(sumsq/npts)), AipsError); AlwaysAssert(near(sd.stddev, sqrt(nvariance/(npts - 1))), AipsError); AlwaysAssert(near(sd.sum, 13.2), AipsError); AlwaysAssert(near(sd.sumsq, sumsq), AipsError); AlwaysAssert(near(sd.variance, nvariance/(npts - 1)), AipsError); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MAX) == std::pair(-1, -1), AipsError ); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 1), AipsError ); AlwaysAssert(fh.getStatistic( StatisticsData::NPTS) == npts, AipsError ); AlwaysAssert(fh.getStatistic( StatisticsData::RMS) == sqrt(sumsq/npts), AipsError ); } { // CAS-10760, test that setStatsToCalculate() works correctly FitToHalfStatistics< Double, std::vector::const_iterator, std::vector::const_iterator > fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); fh.setData(v0.begin(), v0.size()); std::set x; x.insert(StatisticsData::VARIANCE); fh.setStatsToCalculate(x); StatsData sd = fh.getStatistics(); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); Double variance = fh.getStatistic(StatisticsData::VARIANCE); Double npts = 6; Double nvariance = 3.94; AlwaysAssert(near(variance, nvariance/(npts - 1)), AipsError); Double mean = fh.getStatistic(StatisticsData::MEAN); AlwaysAssert(near(mean, 13.2/6), AipsError); } { FitToHalfStatistics::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::GE_CENTER ); fh.setData(v0.begin(), v0.size()); StatsData sd = fh.getStatistics(); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(near(*sd.max, 4.0), AipsError); AlwaysAssert(sd.maxpos.first == 0, AipsError); AlwaysAssert(sd.maxpos.second == 3, AipsError); AlwaysAssert(sd.mean == 2.2, AipsError); AlwaysAssert(near(*sd.min, 0.4), AipsError); AlwaysAssert(sd.minpos.first == -1, AipsError); AlwaysAssert(sd.minpos.second == -1, AipsError); Double npts = 4; Double sumsq = 26.02; Double nvariance = 6.66; AlwaysAssert(sd.npts == npts, AipsError); AlwaysAssert(near(sd.rms, sqrt(sumsq/npts)), AipsError); AlwaysAssert(near(sd.stddev, sqrt(nvariance/(npts - 1))), AipsError); AlwaysAssert(near(sd.sum, 8.8), AipsError); AlwaysAssert(near(sd.sumsq, sumsq), AipsError); AlwaysAssert(near(sd.variance, nvariance/(npts - 1)), AipsError); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MAX) == std::pair(0, 3), AipsError ); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MIN) == std::pair(-1, -1), AipsError ); AlwaysAssert(fh.getStatistic( StatisticsData::NPTS) == npts, AipsError ); AlwaysAssert(fh.getStatistic( StatisticsData::RMS) == sqrt(sumsq/npts), AipsError ); } { FitToHalfStatistics::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CMEDIAN, FitToHalfStatisticsData::LE_CENTER ); fh.setData(v0.begin(), v0.size()); StatsData sd = fh.getStatistics(); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(near(*sd.max, 3.0), AipsError); AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); AlwaysAssert(sd.mean == 2, AipsError); AlwaysAssert(near(*sd.min, 1.0), AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 1, AipsError); Double npts = 6; Double sumsq = 26.5; Double nvariance = 2.5; AlwaysAssert(sd.npts == npts, AipsError); AlwaysAssert(near(sd.rms, sqrt(sumsq/npts)), AipsError); AlwaysAssert(near(sd.stddev, sqrt(nvariance/(npts - 1))), AipsError); AlwaysAssert(near(sd.sum, 12.0), AipsError); AlwaysAssert(near(sd.sumsq, sumsq), AipsError); AlwaysAssert(near(sd.variance, nvariance/(npts - 1)), AipsError); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MAX) == std::pair(-1, -1), AipsError ); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 1), AipsError ); AlwaysAssert(fh.getStatistic( StatisticsData::NPTS) == npts, AipsError ); AlwaysAssert(fh.getStatistic( StatisticsData::RMS) == sqrt(sumsq/npts), AipsError ); } { FitToHalfStatistics::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CMEDIAN, FitToHalfStatisticsData::GE_CENTER ); fh.setData(v0.begin(), v0.size()); StatsData sd = fh.getStatistics(); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(near(*sd.max, 4.0), AipsError); AlwaysAssert(sd.maxpos.first == 0, AipsError); AlwaysAssert(sd.maxpos.second == 3, AipsError); AlwaysAssert(sd.mean == 2, AipsError); AlwaysAssert(near(*sd.min, 0.0), AipsError); AlwaysAssert(sd.minpos.first == -1, AipsError); AlwaysAssert(sd.minpos.second == -1, AipsError); Double npts = 6; Double sumsq = 32.5; Double nvariance = 8.5; AlwaysAssert(sd.npts == npts, AipsError); AlwaysAssert(near(sd.rms, sqrt(sumsq/npts)), AipsError); AlwaysAssert(near(sd.stddev, sqrt(nvariance/(npts - 1))), AipsError); AlwaysAssert(near(sd.sum, 12.0), AipsError); AlwaysAssert(near(sd.sumsq, sumsq), AipsError); AlwaysAssert(near(sd.variance, nvariance/(npts - 1)), AipsError); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MAX) == std::pair(0, 3), AipsError ); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MIN) == std::pair(-1, -1), AipsError ); AlwaysAssert(fh.getStatistic( StatisticsData::NPTS) == npts, AipsError ); AlwaysAssert(fh.getStatistic( StatisticsData::RMS) == sqrt(sumsq/npts), AipsError ); } { FitToHalfStatistics::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CVALUE, FitToHalfStatisticsData::LE_CENTER, 3 ); fh.setData(v0.begin(), v0.size()); StatsData sd = fh.getStatistics(); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(near(*sd.max, 5.0), AipsError); AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); AlwaysAssert(sd.mean == 3, AipsError); AlwaysAssert(near(*sd.min, 1.0), AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 1, AipsError); Double npts = 8; Double sumsq = 87; Double nvariance = 15; AlwaysAssert(sd.npts == npts, AipsError); AlwaysAssert(near(sd.rms, sqrt(sumsq/npts)), AipsError); AlwaysAssert(near(sd.stddev, sqrt(nvariance/(npts - 1))), AipsError); AlwaysAssert(near(sd.sum, 24.0), AipsError); AlwaysAssert(near(sd.sumsq, sumsq), AipsError); AlwaysAssert(near(sd.variance, nvariance/(npts - 1)), AipsError); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MAX) == std::pair(-1, -1), AipsError ); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 1), AipsError ); AlwaysAssert(fh.getStatistic( StatisticsData::NPTS) == npts, AipsError ); AlwaysAssert(fh.getStatistic( StatisticsData::RMS) == sqrt(sumsq/npts), AipsError ); } { FitToHalfStatistics::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CVALUE, FitToHalfStatisticsData::GE_CENTER, 2.5 ); fh.setData(v0.begin(), v0.size()); StatsData sd = fh.getStatistics(); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(near(*sd.max, 4.0), AipsError); AlwaysAssert(sd.maxpos.first == 0, AipsError); AlwaysAssert(sd.maxpos.second == 3, AipsError); AlwaysAssert(sd.mean == 2.5, AipsError); AlwaysAssert(near(*sd.min, 1.0), AipsError); AlwaysAssert(sd.minpos.first == -1, AipsError); AlwaysAssert(sd.minpos.second == -1, AipsError); Double npts = 4; Double sumsq = 29.5; Double nvariance = 4.5; AlwaysAssert(sd.npts == npts, AipsError); AlwaysAssert(near(sd.rms, sqrt(sumsq/npts)), AipsError); AlwaysAssert(near(sd.stddev, sqrt(nvariance/(npts - 1))), AipsError); AlwaysAssert(near(sd.sum, 10.0), AipsError); AlwaysAssert(near(sd.sumsq, sumsq), AipsError); AlwaysAssert(near(sd.variance, nvariance/(npts - 1)), AipsError); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MAX) == std::pair(0, 3), AipsError ); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MIN) == std::pair(-1, -1), AipsError ); AlwaysAssert(fh.getStatistic( StatisticsData::NPTS) == npts, AipsError ); AlwaysAssert(fh.getStatistic( StatisticsData::RMS) == sqrt(sumsq/npts), AipsError ); } Double k[] = {1.5, 1, 2, 4, 2.5}; { // just another way of specifying the data FitToHalfStatistics::const_iterator> fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); fh.setData(k, 5); StatsData sd = fh.getStatistics(); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(near(*sd.max, 3.4), AipsError); AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); AlwaysAssert(sd.mean == 2.2, AipsError); AlwaysAssert(*sd.min == 1, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 1, AipsError); Double npts = 6; Double sumsq = 32.98; Double nvariance = 3.94; AlwaysAssert(sd.npts == npts, AipsError); AlwaysAssert(near(sd.rms, sqrt(sumsq/npts)), AipsError); AlwaysAssert(near(sd.stddev, sqrt(nvariance/(npts - 1))), AipsError); AlwaysAssert(near(sd.sum, 13.2), AipsError); AlwaysAssert(near(sd.sumsq, sumsq), AipsError); AlwaysAssert(near(sd.variance, nvariance/(npts - 1)), AipsError); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MAX) == std::pair(-1, -1), AipsError ); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 1), AipsError ); AlwaysAssert(fh.getStatistic( StatisticsData::NPTS) == npts, AipsError ); AlwaysAssert(fh.getStatistic( StatisticsData::RMS) == sqrt(sumsq/npts), AipsError ); } { // two datasets // 2, 1, 1.5, 4, 2.5 // 5, 8, 10 FitToHalfStatistics::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); fh.setData(v0.begin(), v0.size()); fh.addData(v1.begin(), v1.size()); StatsData sd = fh.getStatistics(); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(near(*sd.max, 7.5), AipsError); AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); AlwaysAssert(sd.mean == 4.25, AipsError); AlwaysAssert(*sd.min == 1.0, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 1, AipsError); Double npts = 10; Double sumsq = 233.25; Double nvariance = 52.625; Double mean = 4.25; AlwaysAssert(sd.npts == npts, AipsError); AlwaysAssert(near(sd.rms, sqrt(sumsq/npts)), AipsError); AlwaysAssert(near(sd.stddev, sqrt(nvariance/(npts - 1))), AipsError); AlwaysAssert(near(sd.sum, mean*npts), AipsError); AlwaysAssert(near(sd.sumsq, sumsq), AipsError); AlwaysAssert(near(sd.variance, nvariance/(npts - 1)), AipsError); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MAX) == std::pair(-1, -1), AipsError ); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 1), AipsError ); AlwaysAssert(fh.getStatistic( StatisticsData::NPTS) == npts, AipsError ); AlwaysAssert(fh.getStatistic( StatisticsData::RMS) == sqrt(sumsq/npts), AipsError ); // Now reverse the order that the datasets were added. results // should be the same except for min dataset location fh.setData(v1.begin(), v1.size()); fh.addData(v0.begin(), v0.size()); sd = fh.getStatistics(); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(near(*sd.max, 7.5), AipsError); AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); AlwaysAssert(near(sd.mean, mean), AipsError); AlwaysAssert(*sd.min == 1, AipsError); AlwaysAssert(sd.minpos.first == 1, AipsError); AlwaysAssert(sd.minpos.second == 1, AipsError); AlwaysAssert(sd.npts == npts, AipsError); AlwaysAssert(near(sd.rms, sqrt(sumsq/npts)), AipsError); AlwaysAssert(near(sd.stddev, sqrt(nvariance/(npts - 1))), AipsError); AlwaysAssert(near(sd.sum, mean*npts), AipsError); AlwaysAssert(near(sd.sumsq, sumsq), AipsError); AlwaysAssert(near(sd.variance, nvariance/(npts - 1)), AipsError); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MAX) == std::pair(-1, -1), AipsError ); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MIN) == std::pair(1, 1), AipsError ); AlwaysAssert(fh.getStatistic( StatisticsData::NPTS) == npts, AipsError ); AlwaysAssert( near( fh.getStatistic(StatisticsData::RMS), sqrt(sumsq/npts) ), AipsError ); } { // Verify class does not support computing stats as // datasets are added FitToHalfStatistics::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); Bool exceptionRaised = False; try { fh.setCalculateAsAdded(True); } catch (std::exception& x) { exceptionRaised = True; } AlwaysAssert(exceptionRaised, AipsError); } { // two datasets, stride = 2,1 // 2, 1.5, 2.5 // 5, 8, 10 FitToHalfStatistics::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); fh.setData(v0.begin(), v0.size(), 2); fh.addData(v1.begin(), v1.size()); StatsData sd = fh.getStatistics(); Double npts = 6; Double sumsq = 568.0/3.0; Double nvariance = 295.0/6.0; Double mean = 29.0/6.0; AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(near(*sd.max, 2*mean - 1.5), AipsError); AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); AlwaysAssert(near(sd.mean, mean), AipsError); AlwaysAssert(*sd.min == 1.5, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 2, AipsError); AlwaysAssert(sd.npts == npts, AipsError); AlwaysAssert(near(sd.rms, sqrt(sumsq/npts)), AipsError); AlwaysAssert(near(sd.stddev, sqrt(nvariance/(npts - 1))), AipsError); AlwaysAssert(near(sd.sum, mean*npts), AipsError); AlwaysAssert(near(sd.sumsq, sumsq), AipsError); AlwaysAssert(near(sd.variance, nvariance/(npts - 1)), AipsError); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MAX) == std::pair(-1, -1), AipsError ); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 2), AipsError ); AlwaysAssert(fh.getStatistic( StatisticsData::NPTS) == npts, AipsError ); AlwaysAssert( near( fh.getStatistic(StatisticsData::RMS), sqrt(sumsq/npts) ), AipsError ); } { // data ranges // 4, 2.5 // 8 FitToHalfStatistics::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); vector > r0(1); r0[0].first = 5; r0[0].second = -5; Bool expectedFail = False; try { fh.setData(v0.begin(), 3, r0); } catch (const std::exception& x) { expectedFail = True; } AlwaysAssert(expectedFail, AipsError); r0[0].first = 2.4; r0[0].second = 6; vector > r1(2); r1[0].first = 9; r1[0].second = 11; r1[1].first = 2; r1[1].second = 7; fh.setData(v0.begin(), v0.size(), r0); fh.addData(v1.begin(), v1.size(), r1, False); StatsData sd = fh.getStatistics(); Double npts = 4; Double sumsq = 1903.0/18.0; Double nvariance = 221.0/18.0; Double mean = 14.5/3.0; AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(near(*sd.max, 2*mean - 2.5), AipsError); AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); AlwaysAssert(near(sd.mean, mean), AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 4, AipsError); AlwaysAssert(sd.npts == npts, AipsError); AlwaysAssert(near(sd.rms, sqrt(sumsq/npts)), AipsError); AlwaysAssert(near(sd.stddev, sqrt(nvariance/(npts - 1))), AipsError); AlwaysAssert(near(sd.sum, mean*npts), AipsError); AlwaysAssert(near(sd.sumsq, sumsq), AipsError); AlwaysAssert(near(sd.variance, nvariance/(npts - 1)), AipsError); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MAX) == std::pair(-1, -1), AipsError ); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 4), AipsError ); AlwaysAssert(fh.getStatistic( StatisticsData::NPTS) == npts, AipsError ); AlwaysAssert( near( fh.getStatistic(StatisticsData::RMS), sqrt(sumsq/npts) ), AipsError ); // test cloning gives same results std::shared_ptr< FitToHalfStatistics< Double, std::vector::const_iterator, std::vector::const_iterator > > fh1( dynamic_cast< FitToHalfStatistics< Double, std::vector::const_iterator, std::vector::const_iterator >* >(fh.clone()) ); StatsData sd1 = fh1->getStatistics(); AlwaysAssert(sd1.masked == sd.masked, AipsError); AlwaysAssert(sd1.weighted == sd.weighted, AipsError); AlwaysAssert(*sd1.max == *sd.max, AipsError); AlwaysAssert(sd1.maxpos.first == sd.maxpos.first, AipsError); AlwaysAssert(sd1.maxpos.second == sd.maxpos.second, AipsError); AlwaysAssert(sd1.mean == sd.mean, AipsError); AlwaysAssert(*sd1.min == *sd.min, AipsError); AlwaysAssert(sd1.minpos.first == sd.minpos.first, AipsError); AlwaysAssert(sd1.minpos.second == sd.minpos.second, AipsError); AlwaysAssert(sd1.npts == sd.npts, AipsError); AlwaysAssert(sd1.rms == sd.rms, AipsError); AlwaysAssert(sd1.stddev == sd.stddev, AipsError); AlwaysAssert(sd1.sum == sd.sum, AipsError); AlwaysAssert(sd1.sumsq == sd.sumsq, AipsError); AlwaysAssert(sd1.variance == sd.variance, AipsError); AlwaysAssert( fh1->getStatisticIndex(StatisticsData::MAX) == std::pair(-1, -1), AipsError ); AlwaysAssert( fh1->getStatisticIndex(StatisticsData::MIN) == std::pair(0, 4), AipsError ); AlwaysAssert(fh1->getStatistic( StatisticsData::NPTS) == npts, AipsError ); AlwaysAssert( near( fh1->getStatistic(StatisticsData::RMS), sqrt(sumsq/npts) ), AipsError ); } { // mask // 4, 2.5 // 8 FitToHalfStatistics::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); vector m0(v0.size()); m0[0] = False; m0[1] = False; m0[2] = False; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = False; m1[1] = True; m1[2] = False; fh.setData(v0.begin(), m0.begin(), v0.size()); fh.addData(v1.begin(), m1.begin(), v1.size()); StatsData sd = fh.getStatistics(); Double npts = 4; Double sumsq = 1903.0/18.0; Double nvariance = 221.0/18.0; Double mean = 14.5/3.0; AlwaysAssert(sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(near(*sd.max, 2*mean - 2.5), AipsError); AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); AlwaysAssert(near(sd.mean, mean), AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 4, AipsError); AlwaysAssert(sd.npts == npts, AipsError); AlwaysAssert(near(sd.rms, sqrt(sumsq/npts)), AipsError); AlwaysAssert(near(sd.stddev, sqrt(nvariance/(npts - 1))), AipsError); AlwaysAssert(near(sd.sum, mean*npts), AipsError); AlwaysAssert(near(sd.sumsq, sumsq), AipsError); AlwaysAssert(near(sd.variance, nvariance/(npts - 1)), AipsError); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MAX) == std::pair(-1, -1), AipsError ); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 4), AipsError ); AlwaysAssert(fh.getStatistic( StatisticsData::NPTS) == npts, AipsError ); AlwaysAssert( near( fh.getStatistic(StatisticsData::RMS), sqrt(sumsq/npts) ), AipsError ); } { // mask and ranges // 4, 2.5 // 8 FitToHalfStatistics::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); vector m0(v0.size()); m0[0] = False; m0[1] = True; m0[2] = True; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = True; m1[1] = True; m1[2] = False; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 9; fh.setData(v0.begin(), m0.begin(), v0.size(), r0, False); fh.addData(v1.begin(), m1.begin(), v1.size(), r1, True); StatsData sd = fh.getStatistics(); Double npts = 4; Double sumsq = 1903.0/18.0; Double nvariance = 221.0/18.0; Double mean = 14.5/3.0; AlwaysAssert(sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(near(*sd.max, 2*mean - 2.5), AipsError); AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); AlwaysAssert(near(sd.mean, mean), AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 4, AipsError); AlwaysAssert(sd.npts == npts, AipsError); AlwaysAssert(near(sd.rms, sqrt(sumsq/npts)), AipsError); AlwaysAssert(near(sd.stddev, sqrt(nvariance/(npts - 1))), AipsError); AlwaysAssert(near(sd.sum, mean*npts), AipsError); AlwaysAssert(near(sd.sumsq, sumsq), AipsError); AlwaysAssert(near(sd.variance, nvariance/(npts - 1)), AipsError); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MAX) == std::pair(-1, -1), AipsError ); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 4), AipsError ); AlwaysAssert(fh.getStatistic( StatisticsData::NPTS) == npts, AipsError ); AlwaysAssert( near( fh.getStatistic(StatisticsData::RMS), sqrt(sumsq/npts) ), AipsError ); } { // weights // 1, 1.5, 4, 2.5 // 5, 8, 10 FitToHalfStatistics::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; fh.setData(v0.begin(), w0.begin(), w0.size()); fh.addData(v1.begin(), w1.begin(), w1.size()); StatsData sd = fh.getStatistics(); Double npts = 8; Double sumofweights = 28; Double sumsq = 641.44; Double nvariance = 123.72; Double mean = 4.3; AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(near(*sd.max, 2*mean - 1), AipsError); AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); AlwaysAssert(near(sd.mean, mean), AipsError); AlwaysAssert(*sd.min == 1, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 1, AipsError); AlwaysAssert(sd.npts == npts, AipsError); AlwaysAssert(near(sd.rms, sqrt(sumsq/sumofweights)), AipsError); AlwaysAssert(near(sd.stddev, sqrt(nvariance/(sumofweights - 1))), AipsError); AlwaysAssert(near(sd.sum, mean*sumofweights), AipsError); AlwaysAssert(near(sd.sumsq, sumsq), AipsError); AlwaysAssert(near(sd.variance, nvariance/(sumofweights - 1)), AipsError); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MAX) == std::pair(-1, -1), AipsError ); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 1), AipsError ); AlwaysAssert(fh.getStatistic( StatisticsData::NPTS) == npts, AipsError ); AlwaysAssert( near( fh.getStatistic(StatisticsData::RMS), sqrt(sumsq/sumofweights) ), AipsError ); } { // integer weights FitToHalfStatistics::const_iterator, vector::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; fh.setData(v0.begin(), w0.begin(), w0.size()); fh.addData(v1.begin(), w1.begin(), w1.size()); StatsData sd = fh.getStatistics(); Double npts = 8; Double sumofweights = 28; Double sumsq = 641.44; Double nvariance = 123.72; Double mean = 4.3; AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(near(*sd.max, 2*mean - 1), AipsError); AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); AlwaysAssert(near(sd.mean, mean), AipsError); AlwaysAssert(*sd.min == 1, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 1, AipsError); AlwaysAssert(sd.npts == npts, AipsError); AlwaysAssert(near(sd.rms, sqrt(sumsq/sumofweights)), AipsError); AlwaysAssert(near(sd.stddev, sqrt(nvariance/(sumofweights - 1))), AipsError); AlwaysAssert(near(sd.sum, mean*sumofweights), AipsError); AlwaysAssert(near(sd.sumsq, sumsq), AipsError); AlwaysAssert(near(sd.variance, nvariance/(sumofweights - 1)), AipsError); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MAX) == std::pair(-1, -1), AipsError ); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 1), AipsError ); AlwaysAssert(fh.getStatistic( StatisticsData::NPTS) == npts, AipsError ); AlwaysAssert( near( fh.getStatistic(StatisticsData::RMS), sqrt(sumsq/sumofweights) ), AipsError ); } { // weights and ranges // 4, 2.5 // 8 FitToHalfStatistics::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 9; fh.setData(v0.begin(), w0.begin(), v0.size(), r0, False); fh.addData(v1.begin(), w1.begin(), v1.size(), r1, True); StatsData sd = fh.getStatistics(); Double npts = 4; Double sumofweights = 18; Double sumsq = 154146.0/484.0; Double nvariance = 11568.0/484.0; Double mean = 44.5/11.0; AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(near(*sd.max, 2*mean - 2.5), AipsError); AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); AlwaysAssert(near(sd.mean, mean), AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 4, AipsError); AlwaysAssert(sd.npts == npts, AipsError); AlwaysAssert(near(sd.rms, sqrt(sumsq/sumofweights)), AipsError); AlwaysAssert(near(sd.stddev, sqrt(nvariance/(sumofweights - 1))), AipsError); AlwaysAssert(near(sd.sum, mean*sumofweights), AipsError); AlwaysAssert(near(sd.sumsq, sumsq), AipsError); AlwaysAssert(near(sd.variance, nvariance/(sumofweights - 1)), AipsError); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MAX) == std::pair(-1, -1), AipsError ); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 4), AipsError ); AlwaysAssert(fh.getStatistic( StatisticsData::NPTS) == npts, AipsError ); AlwaysAssert( near( fh.getStatistic(StatisticsData::RMS), sqrt(sumsq/sumofweights) ), AipsError ); } { // integer weights and ranges FitToHalfStatistics::const_iterator, vector::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 9; fh.setData(v0.begin(), w0.begin(), v0.size(), r0, False); fh.addData(v1.begin(), w1.begin(), v1.size(), r1, True); StatsData sd = fh.getStatistics(); Double npts = 4; Double sumofweights = 18; Double sumsq = 154146.0/484.0; Double nvariance = 11568.0/484.0; Double mean = 44.5/11.0; AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(near(*sd.max, 2*mean - 2.5), AipsError); AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); AlwaysAssert(near(sd.mean, mean), AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 4, AipsError); AlwaysAssert(sd.npts == npts, AipsError); AlwaysAssert(near(sd.rms, sqrt(sumsq/sumofweights)), AipsError); AlwaysAssert(near(sd.stddev, sqrt(nvariance/(sumofweights - 1))), AipsError); AlwaysAssert(near(sd.sum, mean*sumofweights), AipsError); AlwaysAssert(near(sd.sumsq, sumsq), AipsError); AlwaysAssert(near(sd.variance, nvariance/(sumofweights - 1)), AipsError); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MAX) == std::pair(-1, -1), AipsError ); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 4), AipsError ); AlwaysAssert(fh.getStatistic( StatisticsData::NPTS) == npts, AipsError ); AlwaysAssert( near( fh.getStatistic(StatisticsData::RMS), sqrt(sumsq/sumofweights) ), AipsError ); } { // weights, ranges, and masks // 4, 2.5 // 8 FitToHalfStatistics::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector m0(v0.size()); m0[0] = True; m0[1] = True; m0[2] = True; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = True; m1[1] = True; m1[2] = False; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 12; fh.setData(v0.begin(), w0.begin(), m0.begin(), v0.size(), r0, False); fh.addData(v1.begin(), w1.begin(), m1.begin(), v1.size(), r1, True); StatsData sd = fh.getStatistics(); Double npts = 4; Double sumofweights = 18; Double sumsq = 154146.0/484.0; Double nvariance = 11568.0/484.0; Double mean = 44.5/11.0; AlwaysAssert(sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(near(*sd.max, 2*mean - 2.5), AipsError); AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); AlwaysAssert(near(sd.mean, mean), AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 4, AipsError); AlwaysAssert(sd.npts == npts, AipsError); AlwaysAssert(near(sd.rms, sqrt(sumsq/sumofweights)), AipsError); AlwaysAssert(near(sd.stddev, sqrt(nvariance/(sumofweights - 1))), AipsError); AlwaysAssert(near(sd.sum, mean*sumofweights), AipsError); AlwaysAssert(near(sd.sumsq, sumsq), AipsError); AlwaysAssert(near(sd.variance, nvariance/(sumofweights - 1)), AipsError); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MAX) == std::pair(-1, -1), AipsError ); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 4), AipsError ); AlwaysAssert(fh.getStatistic( StatisticsData::NPTS) == npts, AipsError ); AlwaysAssert( near( fh.getStatistic(StatisticsData::RMS), sqrt(sumsq/sumofweights) ), AipsError ); } { // integer weights; ranges, and masks FitToHalfStatistics::const_iterator, vector::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector m0(v0.size()); m0[0] = True; m0[1] = True; m0[2] = True; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = True; m1[1] = True; m1[2] = False; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 12; fh.setData(v0.begin(), w0.begin(), m0.begin(), v0.size(), r0, False); fh.addData(v1.begin(), w1.begin(), m1.begin(), v1.size(), r1, True); StatsData sd = fh.getStatistics(); Double npts = 4; Double sumofweights = 18; Double sumsq = 154146.0/484.0; Double nvariance = 11568.0/484.0; Double mean = 44.5/11.0; AlwaysAssert(sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(near(*sd.max, 2*mean - 2.5), AipsError); AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); AlwaysAssert(near(sd.mean, mean), AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 4, AipsError); AlwaysAssert(sd.npts == npts, AipsError); AlwaysAssert(near(sd.rms, sqrt(sumsq/sumofweights)), AipsError); AlwaysAssert(near(sd.stddev, sqrt(nvariance/(sumofweights - 1))), AipsError); AlwaysAssert(near(sd.sum, mean*sumofweights), AipsError); AlwaysAssert(near(sd.sumsq, sumsq), AipsError); AlwaysAssert(near(sd.variance, nvariance/(sumofweights - 1)), AipsError); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MAX) == std::pair(-1, -1), AipsError ); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 4), AipsError ); AlwaysAssert(fh.getStatistic( StatisticsData::NPTS) == npts, AipsError ); AlwaysAssert( near( fh.getStatistic(StatisticsData::RMS), sqrt(sumsq/sumofweights) ), AipsError ); } { // weights, masks FitToHalfStatistics::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector m0(v0.size()); m0[0] = True; m0[1] = False; m0[2] = False; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = False; m1[1] = True; m1[2] = False; fh.setData(v0.begin(), w0.begin(), m0.begin(), v0.size()); fh.addData(v1.begin(), w1.begin(), m1.begin(), v1.size()); StatsData sd = fh.getStatistics(); Double npts = 4; Double sumofweights = 18; Double sumsq = 154146.0/484.0; Double nvariance = 11568.0/484.0; Double mean = 44.5/11.0; AlwaysAssert(sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(near(*sd.max, 2*mean - 2.5), AipsError); AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); AlwaysAssert(near(sd.mean, mean), AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 4, AipsError); AlwaysAssert(sd.npts == npts, AipsError); AlwaysAssert(near(sd.rms, sqrt(sumsq/sumofweights)), AipsError); AlwaysAssert(near(sd.stddev, sqrt(nvariance/(sumofweights - 1))), AipsError); AlwaysAssert(near(sd.sum, mean*sumofweights), AipsError); AlwaysAssert(near(sd.sumsq, sumsq), AipsError); AlwaysAssert(near(sd.variance, nvariance/(sumofweights - 1)), AipsError); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MAX) == std::pair(-1, -1), AipsError ); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 4), AipsError ); AlwaysAssert(fh.getStatistic( StatisticsData::NPTS) == npts, AipsError ); AlwaysAssert( near( fh.getStatistic(StatisticsData::RMS), sqrt(sumsq/sumofweights) ), AipsError ); } { // integer weights, masks FitToHalfStatistics::const_iterator, vector::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector m0(v0.size()); m0[0] = True; m0[1] = False; m0[2] = False; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = False; m1[1] = True; m1[2] = False; fh.setData(v0.begin(), w0.begin(), m0.begin(), v0.size()); fh.addData(v1.begin(), w1.begin(), m1.begin(), v1.size()); StatsData sd = fh.getStatistics(); Double npts = 4; Double sumofweights = 18; Double sumsq = 154146.0/484.0; Double nvariance = 11568.0/484.0; Double mean = 44.5/11.0; AlwaysAssert(sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(near(*sd.max, 2*mean - 2.5), AipsError); AlwaysAssert(sd.maxpos.first == -1, AipsError); AlwaysAssert(sd.maxpos.second == -1, AipsError); AlwaysAssert(near(sd.mean, mean), AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 4, AipsError); AlwaysAssert(sd.npts == npts, AipsError); AlwaysAssert(near(sd.rms, sqrt(sumsq/sumofweights)), AipsError); AlwaysAssert(near(sd.stddev, sqrt(nvariance/(sumofweights - 1))), AipsError); AlwaysAssert(near(sd.sum, mean*sumofweights), AipsError); AlwaysAssert(near(sd.sumsq, sumsq), AipsError); AlwaysAssert(near(sd.variance, nvariance/(sumofweights - 1)), AipsError); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MAX) == std::pair(-1, -1), AipsError ); AlwaysAssert( fh.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 4), AipsError ); AlwaysAssert(fh.getStatistic( StatisticsData::NPTS) == npts, AipsError ); AlwaysAssert( near( fh.getStatistic(StatisticsData::RMS), sqrt(sumsq/sumofweights) ), AipsError ); } { // getMinMax(), two datasets FitToHalfStatistics::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); fh.setData(v0.begin(), v0.size()); fh.addData(v1.begin(), v1.size()); Double mymin, mymax; fh.getMinMax(mymin, mymax); AlwaysAssert(mymin == 1, AipsError); AlwaysAssert(mymax == 7.5, AipsError); fh = FitToHalfStatistics::const_iterator, vector::const_iterator>( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::GE_CENTER ); fh.setData(v0.begin(), v0.size()); fh.addData(v1.begin(), v1.size()); fh.getMinMax(mymin, mymax); AlwaysAssert(mymin == -1.5, AipsError); AlwaysAssert(mymax == 10, AipsError); } { // getMinMax(), two datasets, stride = 2,1 // 2, 1.5, 2.5 // 5, 8, 10 FitToHalfStatistics::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); fh.setData(v0.begin(), v0.size(), 2); fh.addData(v1.begin(), v1.size()); Double mymin, mymax; fh.getMinMax(mymin, mymax); AlwaysAssert(mymin == 1.5, AipsError); AlwaysAssert(near(mymax, 2*29.0/6.0 - 1.5), AipsError); } { // getMaxMin(), data ranges // 2.5, 4 // 8 FitToHalfStatistics::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); vector > r0(1); r0[0].first = 2.4; r0[0].second = 6; vector > r1(2); r1[0].first = 9; r1[0].second = 11; r1[1].first = 2; r1[1].second = 7; fh.setData(v0.begin(), v0.size(), r0); fh.addData(v1.begin(), v1.size(), r1, False); Double mymin, mymax; fh.getMinMax(mymin, mymax); AlwaysAssert(mymin == 2.5, AipsError); AlwaysAssert(near(mymax, 43.0/6.0), AipsError); } { // getMinMax(), mask FitToHalfStatistics::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); vector m0(v0.size()); m0[0] = False; m0[1] = False; m0[2] = False; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = False; m1[1] = True; m1[2] = False; fh.setData(v0.begin(), m0.begin(), v0.size()); fh.addData(v1.begin(), m1.begin(), v1.size()); Double mymin, mymax; fh.getMinMax(mymin, mymax); AlwaysAssert(mymin == 2.5, AipsError); AlwaysAssert(near(mymax, 43.0/6.0), AipsError); } { // getMinMax(), mask and ranges FitToHalfStatistics::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); vector m0(v0.size()); m0[0] = False; m0[1] = True; m0[2] = True; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = True; m1[1] = True; m1[2] = False; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 9; fh.setData(v0.begin(), m0.begin(), v0.size(), r0, False); fh.addData(v1.begin(), m1.begin(), v1.size(), r1, True); Double mymin, mymax; fh.getMinMax(mymin, mymax); AlwaysAssert(mymin == 2.5, AipsError); AlwaysAssert(near(mymax, 43.0/6.0), AipsError); } { // getMinMax, weights // 2, 1.5, 4, 2.5 // 5, 8 FitToHalfStatistics::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); vector w0(v0.size()); w0[0] = 1; w0[1] = 0; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 0; fh.setData(v0.begin(), w0.begin(), w0.size()); fh.addData(v1.begin(), w1.begin(), w1.size()); Double mymin, mymax; fh.getMinMax(mymin, mymax); AlwaysAssert(mymin == 1.5, AipsError); AlwaysAssert(near(mymax, 5.5), AipsError); } { // getMinMax, integer weights FitToHalfStatistics::const_iterator, vector::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); vector w0(v0.size()); w0[0] = 1; w0[1] = 0; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 0; fh.setData(v0.begin(), w0.begin(), w0.size()); fh.addData(v1.begin(), w1.begin(), w1.size()); Double mymin, mymax; fh.getMinMax(mymin, mymax); AlwaysAssert(mymin == 1.5, AipsError); AlwaysAssert(near(mymax, 5.5), AipsError); } { // 4, 2.5 // 8 // getMinMax(), weights and ranges FitToHalfStatistics::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 9; fh.setData(v0.begin(), w0.begin(), v0.size(), r0, False); fh.addData(v1.begin(), w1.begin(), v1.size(), r1, True); Double mymin, mymax; fh.getMinMax(mymin, mymax); AlwaysAssert(mymin == 2.5, AipsError); AlwaysAssert(near(mymax, 123.0/22.0), AipsError); } { // getMinMax(), integer weights and ranges FitToHalfStatistics::const_iterator, vector::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 9; fh.setData(v0.begin(), w0.begin(), v0.size(), r0, False); fh.addData(v1.begin(), w1.begin(), v1.size(), r1, True); Double mymin, mymax; fh.getMinMax(mymin, mymax); AlwaysAssert(mymin == 2.5, AipsError); AlwaysAssert(near(mymax, 123.0/22.0), AipsError); } { // getMinMax(), weights, ranges, and masks FitToHalfStatistics::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector m0(v0.size()); m0[0] = True; m0[1] = True; m0[2] = True; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = True; m1[1] = True; m1[2] = False; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 12; fh.setData(v0.begin(), w0.begin(), m0.begin(), v0.size(), r0, False); fh.addData(v1.begin(), w1.begin(), m1.begin(), v1.size(), r1, True); Double mymin, mymax; fh.getMinMax(mymin, mymax); AlwaysAssert(mymin == 2.5, AipsError); AlwaysAssert(near(mymax, 123.0/22.0), AipsError); } { // getMinMax(), integer weights, ranges, and masks FitToHalfStatistics::const_iterator, vector::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector m0(v0.size()); m0[0] = True; m0[1] = True; m0[2] = True; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = True; m1[1] = True; m1[2] = False; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 12; fh.setData(v0.begin(), w0.begin(), m0.begin(), v0.size(), r0, False); fh.addData(v1.begin(), w1.begin(), m1.begin(), v1.size(), r1, True); Double mymin, mymax; fh.getMinMax(mymin, mymax); AlwaysAssert(mymin == 2.5, AipsError); AlwaysAssert(near(mymax, 123.0/22.0), AipsError); } { // getNPts(), two datasets FitToHalfStatistics::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CVALUE, FitToHalfStatisticsData::LE_CENTER, 1.5 ); fh.setData(v0.begin(), v0.size()); fh.addData(v1.begin(), v1.size()); uInt64 npts = fh.getNPts(); AlwaysAssert(npts == 4, AipsError); // check calling it again works npts = fh.getNPts(); AlwaysAssert(npts == 4, AipsError); // check clearing data and doing it again works fh.setData(v0.begin(), v0.size()); fh.addData(v1.begin(), v1.size()); npts = fh.getNPts(); AlwaysAssert(npts == 4, AipsError); fh = FitToHalfStatistics::const_iterator, vector::const_iterator>( FitToHalfStatisticsData::CVALUE, FitToHalfStatisticsData::GE_CENTER, 1.5 ); fh.setData(v0.begin(), v0.size()); fh.addData(v1.begin(), v1.size()); npts = fh.getNPts(); AlwaysAssert(npts == 14, AipsError); // check calling it again works npts = fh.getNPts(); AlwaysAssert(npts == 14, AipsError); // check clearing data and doing it again works fh.setData(v0.begin(), v0.size()); fh.addData(v1.begin(), v1.size()); npts = fh.getNPts(); AlwaysAssert(npts == 14, AipsError); } { // general quantile exceptions FitToHalfStatistics::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); fh.setData(v0.begin(), v0.size()); fh.addData(v1.begin(), v1.size()); Bool thrown = False; try { fh.getQuantile(0); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); thrown = False; try { fh.getQuantile(1); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); } { // getQuantile(), no weights, no mask, no ranges FitToHalfStatistics< Double, std::vector::const_iterator, std::vector::const_iterator > fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); // mean is 4.25 // real + virtual dataset // 1, 1.5, 2, 2.5, 4, 4.5, 6, 6.5, 7, 7.5 fh.setData(v0.begin(), v0.size()); fh.addData(v1.begin(), v1.size()); Double q = fh.getQuantile(0.1); AlwaysAssert(q == 1, AipsError); q = fh.getQuantile(0.2); AlwaysAssert(q == 1.5, AipsError); q = fh.getQuantile(0.3); AlwaysAssert(q == 2, AipsError); q = fh.getQuantile(0.4); AlwaysAssert(q == 2.5, AipsError); q = fh.getQuantile(0.5); AlwaysAssert(q == 4, AipsError); q = fh.getQuantile(0.6); AlwaysAssert(q == 4.5, AipsError); q = fh.getQuantile(0.7); AlwaysAssert(q == 6, AipsError); q = fh.getQuantile(0.8); AlwaysAssert(q == 6.5, AipsError); q = fh.getQuantile(0.9); AlwaysAssert(q == 7, AipsError); fh = FitToHalfStatistics< Double, std::vector::const_iterator, std::vector::const_iterator >( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::GE_CENTER ); // mean is 4.25 // real + virtual dataset // mean is 4.25 // real + virtual dataset // -1.5, 0.5, 3.5, 5, 8, 10 fh.setData(v0.begin(), v0.size()); fh.addData(v1.begin(), v1.size()); q = fh.getQuantile(0.1); AlwaysAssert(q == -1.5, AipsError); q = fh.getQuantile(0.2); AlwaysAssert(q == 0.5, AipsError); q = fh.getQuantile(0.3); AlwaysAssert(q == 0.5, AipsError); q = fh.getQuantile(0.4); AlwaysAssert(q == 3.5, AipsError); q = fh.getQuantile(0.5); AlwaysAssert(q == 3.5, AipsError); q = fh.getQuantile(0.6); AlwaysAssert(q == 5, AipsError); q = fh.getQuantile(0.7); AlwaysAssert(q == 8, AipsError); q = fh.getQuantile(0.8); AlwaysAssert(q == 8, AipsError); q = fh.getQuantile(0.9); AlwaysAssert(q == 10, AipsError); } { // getQuantile(): two datasets, stride = 2,1 // 1.5, 2, 2.5 5, 8, 10 FitToHalfStatistics::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CVALUE, FitToHalfStatisticsData::GE_CENTER, 2 ); // real + virtual -6, -4, -1, 1.5, 2, 2, 2.5, 5, 8, 10 fh.setData(v0.begin(), v0.size(), 2); fh.addData(v1.begin(), v1.size()); Double q = fh.getQuantile(0.1); AlwaysAssert(q == -6, AipsError); q = fh.getQuantile(0.2); AlwaysAssert(q == -4, AipsError); q = fh.getQuantile(0.3); AlwaysAssert(q == -1, AipsError); q = fh.getQuantile(0.4); AlwaysAssert(q == 1.5, AipsError); q = fh.getQuantile(0.5); AlwaysAssert(q == 2, AipsError); q = fh.getQuantile(0.6); AlwaysAssert(q == 2, AipsError); q = fh.getQuantile(0.7); AlwaysAssert(q == 2.5, AipsError); q = fh.getQuantile(0.8); AlwaysAssert(q == 5, AipsError); q = fh.getQuantile(0.9); AlwaysAssert(q == 8, AipsError); fh = FitToHalfStatistics::const_iterator, vector::const_iterator>( FitToHalfStatisticsData::CVALUE, FitToHalfStatisticsData::LE_CENTER, 2 ); // real + virtual 1.5, 2, 2, 2.5 fh.setData(v0.begin(), v0.size(), 2); fh.addData(v1.begin(), v1.size()); q = fh.getQuantile(0.1); AlwaysAssert(q == 1.5, AipsError); q = fh.getQuantile(0.2); AlwaysAssert(q == 1.5, AipsError); q = fh.getQuantile(0.3); AlwaysAssert(q == 2, AipsError); q = fh.getQuantile(0.4); AlwaysAssert(q == 2, AipsError); q = fh.getQuantile(0.5); AlwaysAssert(q == 2, AipsError); q = fh.getQuantile(0.6); AlwaysAssert(q == 2, AipsError); q = fh.getQuantile(0.7); AlwaysAssert(q == 2, AipsError); q = fh.getQuantile(0.8); AlwaysAssert(q == 2.5, AipsError); q = fh.getQuantile(0.9); AlwaysAssert(q == 2.5, AipsError); } { // leave in for compile check FitToHalfStatistics::const_iterator, vector::const_iterator> fh; } { // getMedian() FitToHalfStatistics::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CVALUE, FitToHalfStatisticsData::LE_CENTER, 4.25 ); fh.addData(v0.begin(), v0.size()); Double median = fh.getMedian(); AlwaysAssert(median == 4.25, AipsError); fh.reset(); vector m0(v0.size(), True); m0[0] = False; fh.addData(v0.begin(), m0.begin(), v0.size()); median = fh.getMedian(); AlwaysAssert(median == 4.25, AipsError); } { // getMedianAndQuantiles std::set quantiles; quantiles.insert(0.1); quantiles.insert(0.2); quantiles.insert(0.3); quantiles.insert(0.4); quantiles.insert(0.5); quantiles.insert(0.6); quantiles.insert(0.7); quantiles.insert(0.8); quantiles.insert(0.9); FitToHalfStatistics::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); fh.setData(v0.begin(), v0.size()); fh.addData(v1.begin(), v1.size()); std::map quantileToValue; Double median = fh.getMedianAndQuantiles(quantileToValue, quantiles); AlwaysAssert(median == 4.25, AipsError); AlwaysAssert(quantileToValue[0.1] == 1, AipsError); AlwaysAssert(quantileToValue[0.2] == 1.5, AipsError); AlwaysAssert(quantileToValue[0.3] == 2, AipsError); AlwaysAssert(quantileToValue[0.4] == 2.5, AipsError); AlwaysAssert(quantileToValue[0.5] == 4, AipsError); AlwaysAssert(quantileToValue[0.6] == 4.5, AipsError); AlwaysAssert(quantileToValue[0.7] == 6, AipsError); AlwaysAssert(quantileToValue[0.8] == 6.5, AipsError); AlwaysAssert(quantileToValue[0.9] == 7, AipsError); fh = FitToHalfStatistics::const_iterator, vector::const_iterator>( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::GE_CENTER ); fh.setData(v0.begin(), v0.size()); fh.addData(v1.begin(), v1.size()); median = fh.getMedianAndQuantiles(quantileToValue, quantiles); AlwaysAssert(median == 4.25, AipsError); //-1.5, 0.5, 3.5, 5, 8, 10 AlwaysAssert(quantileToValue[0.1] == -1.5, AipsError); AlwaysAssert(quantileToValue[0.2] == 0.5, AipsError); AlwaysAssert(quantileToValue[0.3] == 0.5, AipsError); AlwaysAssert(quantileToValue[0.4] == 3.5, AipsError); AlwaysAssert(quantileToValue[0.5] == 3.5, AipsError); AlwaysAssert(quantileToValue[0.6] == 5, AipsError); AlwaysAssert(quantileToValue[0.7] == 8, AipsError); AlwaysAssert(quantileToValue[0.8] == 8, AipsError); AlwaysAssert(quantileToValue[0.9] == 10, AipsError); } { // getMedianAbsDevMed() FitToHalfStatistics::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); fh.setData(v0.begin(), v0.size()); fh.addData(v1.begin(), v1.size()); // 1, 1.5, 2, 2.5, 4, 4.5, 6, 6.5, 7, 7.5 Double medabsdevmed = fh.getMedianAbsDevMed(); AlwaysAssert(medabsdevmed == 2.25, AipsError); } uInt npts = (uInt)1e6; vector bigData(npts); vector::iterator iter = bigData.begin(); vector::iterator end = bigData.end(); { uInt64 count = 0; while(iter != end) { *iter = count % 2 == 0 ? count : (-1)*(Double)(count*count); ++iter; ++count; } } vector bigMask(npts, True); bigMask[0] = False; { FitToHalfStatistics::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CVALUE, FitToHalfStatisticsData::LE_CENTER, 400 ); fh.setData(bigData.begin(), bigData.size()); // getMedian() with binning, no ranges, weights, or mask // The array size should be ignored, because the median is trivial. Double median = fh.getMedian(NULL, NULL, NULL, 100); AlwaysAssert(median == 400, AipsError); fh = FitToHalfStatistics::const_iterator, vector::const_iterator>( FitToHalfStatisticsData::CVALUE, FitToHalfStatisticsData::GE_CENTER, 250 ); fh.setData(bigData.begin(), bigData.size()); median = fh.getMedian(NULL, NULL, NULL, 100); AlwaysAssert(median == 250, AipsError); } { // getMedianAbsDevMed() with binning, no ranges, weights, or mask FitToHalfStatistics::const_iterator, vector::const_iterator> fh( FitToHalfStatisticsData::CVALUE, FitToHalfStatisticsData::LE_CENTER, 400 ); fh.setData(bigData.begin(), bigData.size()); // enforce a small internal array size so binning algorithm is used Double medabsdevmed = fh.getMedianAbsDevMed(NULL, NULL, NULL, 100); AlwaysAssert(medabsdevmed == 249799040801ULL, AipsError); fh = FitToHalfStatistics::const_iterator, vector::const_iterator>( FitToHalfStatisticsData::CVALUE, FitToHalfStatisticsData::GE_CENTER, 250 ); fh.setData(bigData.begin(), bigData.size()); // enforce a small internal array size so binning algorithm is used medabsdevmed = fh.getMedianAbsDevMed(NULL, NULL, NULL, 100); AlwaysAssert(medabsdevmed == 499874, AipsError); } { // large array with all the same values, getMedianAndQuantile() std::vector big(100000, 30); FitToHalfStatistics< Double, std::vector::const_iterator, std::vector::const_iterator > fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); fh.addData(big.begin(), big.size()); std::set quantiles; quantiles.insert(0.25); quantiles.insert(0.75); std::shared_ptr npts; std::shared_ptr mymin, mymax; std::map quantileToValue; Double median = fh.getMedianAndQuantiles( quantileToValue, quantiles, npts, mymin, mymax, 100 ); AlwaysAssert(median == 30, AipsError); AlwaysAssert(quantileToValue[0.25] == 30, AipsError); AlwaysAssert(quantileToValue[0.75] == 30, AipsError); } { // a large array so we test binning Array big(IPosition(1, 100000)); Array::iterator biter = big.begin(); Array::iterator bend = big.end(); uInt count = 0; while (biter != bend) { *biter = count % 2 == 0 ? (Float)count : -(Float)count - 0.5; ++biter; ++count; } FitToHalfStatistics::const_iterator, Array::const_iterator> fh( FitToHalfStatisticsData::CMEAN, FitToHalfStatisticsData::LE_CENTER ); fh.addData(big.begin(), big.size()); std::set quantiles; quantiles.insert(0.25); quantiles.insert(0.75); std::map quantileToValue; std::shared_ptr npts; std::shared_ptr mymin, mymax; Double median = fh.getMedianAndQuantiles( quantileToValue, quantiles, npts, mymin, mymax, 100 ); AlwaysAssert(near(median, -0.75, 1e-12), AipsError); AlwaysAssert(near(quantileToValue[0.25],-50001.5), AipsError); AlwaysAssert(near(quantileToValue[0.75], 49998.0), AipsError); fh = FitToHalfStatistics::const_iterator, Array::const_iterator>( FitToHalfStatisticsData::CVALUE, FitToHalfStatisticsData::GE_CENTER, 4 ); fh.addData(big.begin(), big.size()); quantileToValue.clear(); npts = NULL; mymin = NULL; mymax = NULL; median = fh.getMedianAndQuantiles( quantileToValue, quantiles, npts, mymin, mymax, 100 ); AlwaysAssert(near(median, 4.0), AipsError); AlwaysAssert(near(quantileToValue[0.25], -49994.0), AipsError); AlwaysAssert(near(quantileToValue[0.75], 50000.0), AipsError); } { // CAS-10760 fix for null set equivalent FitToHalfStatistics< Double, std::vector::const_iterator, std::vector::const_iterator > fh( FitToHalfStatisticsData::CVALUE, FitToHalfStatisticsData::LE_CENTER ); fh.addData(v0.begin(), v0.size()); StatsData sd = fh.getStatistics(); AlwaysAssert(sd.npts == 0, AipsError); AlwaysAssert(sd.variance == 0, AipsError); AlwaysAssert(sd.rms == 0, AipsError); AlwaysAssert(sd.sum == 0, AipsError); AlwaysAssert(! sd.max, AipsError); AlwaysAssert(! sd.min, AipsError); } } catch (const std::exception& x) { cout << x.what() << endl; return 1; } return 0; } casacore-3.7.1/scimath/StatsFramework/test/tHingesFencesStatistics.cc000066400000000000000000003601351476623553700260260ustar00rootroot00000000000000//# Copyright (C) 1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include #include #include #include #include #include #define COMMA , int main() { try { // The first group of tests test the default behavior, which is identical to // that of ClassicalStatistics vector v0(5); v0[0] = 2; v0[1] = 1; v0[2] = 1.5; v0[3] = 3; v0[4] = 2.5; vector v1(3); v1[0] = 5; v1[1] = 8; v1[2] = 10; Double k[] = {1.5, 1, 2, 3, 2.5}; { HingesFencesStatistics::const_iterator, vector::const_iterator> cs; cs.setData(v0.begin(), v0.size()); StatsData sd = cs.getStatistics(); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 3, AipsError); AlwaysAssert(sd.maxpos.first == 0, AipsError); AlwaysAssert(sd.maxpos.second == 3, AipsError); AlwaysAssert(sd.mean == 2, AipsError); AlwaysAssert(*sd.min == 1, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 1, AipsError); AlwaysAssert(sd.npts == 5, AipsError); AlwaysAssert(sd.rms == sqrt(22.5/5.0), AipsError); AlwaysAssert(sd.stddev == sqrt(0.625), AipsError); AlwaysAssert(sd.sum == 10, AipsError); AlwaysAssert(sd.sumsq == 22.5, AipsError); AlwaysAssert(sd.variance == 0.625, AipsError); AlwaysAssert( cs.getStatisticIndex(StatisticsData::MAX) == std::pair(0, 3), AipsError ); AlwaysAssert( cs.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 1), AipsError ); AlwaysAssert(cs.getStatistic( StatisticsData::NPTS) == 5, AipsError ); AlwaysAssert(cs.getStatistic( StatisticsData::RMS) == sqrt(22.5/5.0), AipsError ); } { // just another way of specifying the data HingesFencesStatistics cs1; cs1.setData(k, 5); StatsData sd = cs1.getStatistics(); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 3, AipsError); AlwaysAssert(sd.maxpos.first == 0, AipsError); AlwaysAssert(sd.maxpos.second == 3, AipsError); AlwaysAssert(sd.mean == 2, AipsError); AlwaysAssert(*sd.min == 1, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 1, AipsError); AlwaysAssert(sd.npts == 5, AipsError); AlwaysAssert(sd.rms == sqrt(22.5/5.0), AipsError); AlwaysAssert(sd.stddev == sqrt(0.625), AipsError); AlwaysAssert(sd.sum == 10, AipsError); AlwaysAssert(sd.sumsq == 22.5, AipsError); AlwaysAssert(sd.variance == 0.625, AipsError); AlwaysAssert( cs1.getStatisticIndex(StatisticsData::MAX) == std::pair(0, 3), AipsError ); AlwaysAssert( cs1.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 1), AipsError ); AlwaysAssert(cs1.getStatistic( StatisticsData::NPTS) == 5, AipsError ); AlwaysAssert(cs1.getStatistic( StatisticsData::RMS) == sqrt(22.5/5.0), AipsError ); } { // two datasets HingesFencesStatistics::const_iterator, vector::const_iterator> cs; cs.setData(v0.begin(), v0.size()); cs.addData(v1.begin(), v1.size()); StatsData sd = cs.getStatistics(); Double variance = (211.5 - 33.0*33.0/8.0)/7.0; AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 10, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 2, AipsError); AlwaysAssert(sd.mean == 33.0/8.0, AipsError); AlwaysAssert(*sd.min == 1, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 1, AipsError); AlwaysAssert(sd.npts == 8, AipsError); AlwaysAssert(sd.rms == sqrt(211.5/8.0), AipsError); AlwaysAssert(near(sd.stddev, sqrt(variance)), AipsError); AlwaysAssert(sd.sum == 33, AipsError); AlwaysAssert(sd.sumsq == 211.5, AipsError); AlwaysAssert(near(sd.variance, variance), AipsError); AlwaysAssert( cs.getStatisticIndex(StatisticsData::MAX) == std::pair(1, 2), AipsError ); AlwaysAssert( cs.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 1), AipsError ); AlwaysAssert(cs.getStatistic( StatisticsData::NPTS) == 8, AipsError ); AlwaysAssert(cs.getStatistic( StatisticsData::RMS) == sqrt(211.5/8.0), AipsError ); // Now reverse the order that the datasets were added. results // should be the same except for min and max dataset locations cs.setData(v1.begin(), v1.size()); cs.addData(v0.begin(), v0.size()); sd = cs.getStatistics(); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 10, AipsError); AlwaysAssert(sd.maxpos.first == 0, AipsError); AlwaysAssert(sd.maxpos.second == 2, AipsError); AlwaysAssert(sd.mean == 33.0/8.0, AipsError); AlwaysAssert(*sd.min == 1, AipsError); AlwaysAssert(sd.minpos.first == 1, AipsError); AlwaysAssert(sd.minpos.second == 1, AipsError); AlwaysAssert(sd.npts == 8, AipsError); AlwaysAssert(sd.rms == sqrt(211.5/8.0), AipsError); AlwaysAssert(near(sd.stddev, sqrt(variance)), AipsError); AlwaysAssert(sd.sum == 33, AipsError); AlwaysAssert(sd.sumsq == 211.5, AipsError); AlwaysAssert(near(sd.variance, variance), AipsError); AlwaysAssert( cs.getStatisticIndex(StatisticsData::MAX) == std::pair(0, 2), AipsError ); AlwaysAssert( cs.getStatisticIndex(StatisticsData::MIN) == std::pair(1, 1), AipsError ); AlwaysAssert(cs.getStatistic( StatisticsData::NPTS) == 8, AipsError ); AlwaysAssert(cs.getStatistic( StatisticsData::RMS) == sqrt(211.5/8.0), AipsError ); } { // Test accumulating as datasets are added. vector t0(5); t0[0] = 1.5; t0[1] = 1; t0[2] = 2; t0[3] = 3; t0[4] = 2.5; vector t1(3); t1[0] = 5; t1[1] = 8; t1[2] = 10; HingesFencesStatistics::const_iterator, vector::const_iterator> cs; cs.setCalculateAsAdded(False); cs.setData(t0.begin(), t0.size()); std::fill(t0.begin(), t0.begin()+t0.size(), 0); cs.addData(t1.begin(), t1.size()); std::fill(t1.begin(), t1.begin()+t1.size(), 0); StatsData sd = cs.getStatistics(); // not accumulating as added and all values have been set // to zero. With multi-threading, the max and min positions // could be anywhere since all values are equal, so no longer // check those. AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 0, AipsError); AlwaysAssert(sd.mean == 0, AipsError); AlwaysAssert(*sd.min == 0, AipsError); AlwaysAssert(sd.npts == 8, AipsError); AlwaysAssert(sd.rms == 0, AipsError); AlwaysAssert(sd.stddev == 0, AipsError); AlwaysAssert(sd.sum == 0, AipsError); AlwaysAssert(sd.sumsq == 0, AipsError); AlwaysAssert(sd.variance == 0, AipsError); t0[0] = 1.5; t0[1] = 1; t0[2] = 2; t0[3] = 3; t0[4] = 2.5; t1[0] = 5; t1[1] = 8; t1[2] = 10; Bool exceptionRaised = False; try { cs.setCalculateAsAdded(True); } catch (std::exception& x) { exceptionRaised = True; } AlwaysAssert(exceptionRaised, AipsError); } { // two datasets, stride = 2,1 HingesFencesStatistics::const_iterator, vector::const_iterator> hfs; hfs.setData(v0.begin(), v0.size(), 2); hfs.addData(v1.begin(), v1.size()); StatsData sd = hfs.getStatistics(); Double variance = (201.5 - 29.0*29.0/6.0)/5.0; AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 10, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 2, AipsError); AlwaysAssert(sd.mean == 29.0/6.0, AipsError); AlwaysAssert(*sd.min == 1.5, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 2, AipsError); AlwaysAssert(sd.npts == 6, AipsError); AlwaysAssert(sd.rms == sqrt(201.5/6.0), AipsError); AlwaysAssert(near(sd.stddev, sqrt(variance)), AipsError); AlwaysAssert(sd.sum == 29, AipsError); AlwaysAssert(sd.sumsq == 201.5, AipsError); AlwaysAssert(near(sd.variance, variance), AipsError); } { // data ranges HingesFencesStatistics::const_iterator, vector::const_iterator> cs; vector > r0(1); r0[0].first = 5; r0[0].second = -5; Bool expectedFail = False; try { cs.setData(v0.begin(), 3, r0); } catch (const std::exception& x) { expectedFail = True; } AlwaysAssert(expectedFail, AipsError); r0[0].first = 2.4; r0[0].second = 6; vector > r1(2); r1[0].first = 9; r1[0].second = 11; r1[1].first = 2; r1[1].second = 7; cs.setData(v0.begin(), v0.size(), r0); cs.addData(v1.begin(), v1.size(), r1, False); StatsData sd = cs.getStatistics(); Double variance = (79.25 - 13.5*13.5/3.0)/2.0; AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 8, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 1, AipsError); AlwaysAssert(sd.mean == 13.5/3.0, AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 4, AipsError); AlwaysAssert(sd.npts == 3, AipsError); AlwaysAssert(sd.rms == sqrt(79.25/3.0), AipsError); AlwaysAssert(near(sd.stddev, sqrt(variance)), AipsError); AlwaysAssert(sd.sum == 13.5, AipsError); AlwaysAssert(sd.sumsq == 79.25, AipsError); AlwaysAssert(near(sd.variance, variance), AipsError); } { // mask HingesFencesStatistics::const_iterator, vector::const_iterator> cs; vector m0(v0.size()); m0[0] = False; m0[1] = False; m0[2] = False; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = False; m1[1] = True; m1[2] = False; cs.setData(v0.begin(), m0.begin(), v0.size()); cs.addData(v1.begin(), m1.begin(), v1.size()); StatsData sd = cs.getStatistics(); Double variance = (79.25 - 13.5*13.5/3.0)/2.0; AlwaysAssert(sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 8, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 1, AipsError); AlwaysAssert(sd.mean == 13.5/3.0, AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 4, AipsError); AlwaysAssert(sd.npts == 3, AipsError); AlwaysAssert(sd.rms == sqrt(79.25/3.0), AipsError); AlwaysAssert(near(sd.stddev, sqrt(variance)), AipsError); AlwaysAssert(sd.sum == 13.5, AipsError); AlwaysAssert(sd.sumsq == 79.25, AipsError); AlwaysAssert(near(sd.variance, variance), AipsError); } { // mask and ranges HingesFencesStatistics::const_iterator, vector::const_iterator> cs; vector m0(v0.size()); m0[0] = False; m0[1] = True; m0[2] = True; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = True; m1[1] = True; m1[2] = False; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 9; cs.setData(v0.begin(), m0.begin(), v0.size(), r0, False); cs.addData(v1.begin(), m1.begin(), v1.size(), r1, True); StatsData sd = cs.getStatistics(); Double variance = (79.25 - 13.5*13.5/3.0)/2.0; AlwaysAssert(sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 8, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 1, AipsError); AlwaysAssert(sd.mean == 13.5/3.0, AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 4, AipsError); AlwaysAssert(sd.npts == 3, AipsError); AlwaysAssert(sd.rms == sqrt(79.25/3.0), AipsError); AlwaysAssert(near(sd.stddev, sqrt(variance)), AipsError); AlwaysAssert(sd.sum == 13.5, AipsError); AlwaysAssert(sd.sumsq == 79.25, AipsError); AlwaysAssert(near(sd.variance, variance), AipsError); } { // weights HingesFencesStatistics::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; cs.setData(v0.begin(), w0.begin(), w0.size()); cs.addData(v1.begin(), w1.begin(), w1.size()); StatsData sd = cs.getStatistics(); Double variance = (529.0 - 82.0*82.0/20.0)/19.0; AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(*sd.max == 10, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 2, AipsError); AlwaysAssert(near(sd.mean, 4.1), AipsError); AlwaysAssert(*sd.min == 1, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 1, AipsError); AlwaysAssert(sd.npts == 7, AipsError); AlwaysAssert(sd.rms == sqrt(529.0/20.0), AipsError); AlwaysAssert(near(sd.stddev, sqrt(variance)), AipsError); AlwaysAssert(sd.sum == 82.0, AipsError); AlwaysAssert(sd.sumweights == 20.0, AipsError); AlwaysAssert(sd.sumsq == 529.0, AipsError); AlwaysAssert(near(sd.variance, variance), AipsError); } { // integer weights HingesFencesStatistics::const_iterator, vector::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; cs.setData(v0.begin(), w0.begin(), w0.size()); cs.addData(v1.begin(), w1.begin(), w1.size()); StatsData sd = cs.getStatistics(); Double variance = (529.0 - 82.0*82.0/20.0)/19.0; AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(*sd.max == 10, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 2, AipsError); AlwaysAssert(near(sd.mean, 4.1), AipsError); AlwaysAssert(*sd.min == 1, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 1, AipsError); AlwaysAssert(sd.npts == 7, AipsError); AlwaysAssert(sd.rms == sqrt(529.0/20.0), AipsError); AlwaysAssert(near(sd.stddev, sqrt(variance)), AipsError); AlwaysAssert(sd.sum == 82.0, AipsError); AlwaysAssert(sd.sumweights == 20.0, AipsError); AlwaysAssert(sd.sumsq == 529.0, AipsError); AlwaysAssert(near(sd.variance, variance), AipsError); } { // weights and ranges HingesFencesStatistics::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 9; cs.setData(v0.begin(), w0.begin(), v0.size(), r0, False); cs.addData(v1.begin(), w1.begin(), v1.size(), r1, True); StatsData sd = cs.getStatistics(); Double variance = (195.25 - 40.5*40.5/11.0)/10.0; AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(*sd.max == 8, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 1, AipsError); AlwaysAssert(near(sd.mean, 40.5/11.0), AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 4, AipsError); AlwaysAssert(sd.npts == 3, AipsError); AlwaysAssert(sd.rms == sqrt(195.25/11.0), AipsError); AlwaysAssert(near(sd.stddev, sqrt(variance)), AipsError); AlwaysAssert(sd.sum == 40.5, AipsError); AlwaysAssert(sd.sumweights == 11.0, AipsError); AlwaysAssert(sd.sumsq == 195.25, AipsError); AlwaysAssert(near(sd.variance, variance), AipsError); // test cloning gives same results std::shared_ptr< HingesFencesStatistics< Double, std::vector::const_iterator, std::vector::const_iterator > > cs1( dynamic_cast< HingesFencesStatistics< Double, std::vector::const_iterator, std::vector::const_iterator >* >(cs.clone()) ); StatsData sd1 = cs1->getStatistics(); AlwaysAssert(sd1.masked == sd.masked, AipsError); AlwaysAssert(sd1.weighted == sd.weighted, AipsError); AlwaysAssert(*sd1.max == *sd.max, AipsError); AlwaysAssert(sd1.maxpos.first == sd.maxpos.first, AipsError); AlwaysAssert(sd1.maxpos.second == sd.maxpos.second, AipsError); AlwaysAssert(sd1.mean == sd.mean, AipsError); AlwaysAssert(*sd1.min == *sd.min, AipsError); AlwaysAssert(sd1.minpos.first == sd.minpos.first, AipsError); AlwaysAssert(sd1.minpos.second == sd.minpos.second, AipsError); AlwaysAssert(sd1.npts == sd.npts, AipsError); AlwaysAssert(sd1.rms == sd.rms, AipsError); AlwaysAssert(sd1.stddev == sd.stddev, AipsError); AlwaysAssert(sd1.sum == sd.sum, AipsError); AlwaysAssert(sd1.sumweights == sd.sumweights, AipsError); AlwaysAssert(sd1.sumsq == sd.sumsq, AipsError); AlwaysAssert(sd1.variance == sd.variance, AipsError); } { // integer weights and ranges HingesFencesStatistics::const_iterator, vector::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 9; cs.setData(v0.begin(), w0.begin(), v0.size(), r0, False); cs.addData(v1.begin(), w1.begin(), v1.size(), r1, True); StatsData sd = cs.getStatistics(); Double variance = (195.25 - 40.5*40.5/11.0)/10.0; AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(*sd.max == 8, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 1, AipsError); AlwaysAssert(near(sd.mean, 40.5/11.0), AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 4, AipsError); AlwaysAssert(sd.npts == 3, AipsError); AlwaysAssert(sd.rms == sqrt(195.25/11.0), AipsError); AlwaysAssert(near(sd.stddev, sqrt(variance)), AipsError); AlwaysAssert(sd.sum == 40.5, AipsError); AlwaysAssert(sd.sumweights == 11.0, AipsError); AlwaysAssert(sd.sumsq == 195.25, AipsError); AlwaysAssert(near(sd.variance, variance), AipsError); } { // weights, ranges, and masks HingesFencesStatistics::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector m0(v0.size()); m0[0] = True; m0[1] = True; m0[2] = True; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = True; m1[1] = True; m1[2] = False; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 12; cs.setData(v0.begin(), w0.begin(), m0.begin(), v0.size(), r0, False); cs.addData(v1.begin(), w1.begin(), m1.begin(), v1.size(), r1, True); StatsData sd = cs.getStatistics(); Double variance = (195.25 - 40.5*40.5/11.0)/10.0; AlwaysAssert(sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(*sd.max == 8, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 1, AipsError); AlwaysAssert(near(sd.mean, 40.5/11.0), AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 4, AipsError); AlwaysAssert(sd.npts == 3, AipsError); AlwaysAssert(sd.rms == sqrt(195.25/11.0), AipsError); AlwaysAssert(near(sd.stddev, sqrt(variance)), AipsError); AlwaysAssert(sd.sum == 40.5, AipsError); AlwaysAssert(sd.sumweights == 11.0, AipsError); AlwaysAssert(sd.sumsq == 195.25, AipsError); AlwaysAssert(near(sd.variance, variance), AipsError); AlwaysAssert( cs.getStatisticIndex(StatisticsData::MAX) == std::pair(1, 1), AipsError ); AlwaysAssert( cs.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 4), AipsError ); AlwaysAssert(cs.getStatistic( StatisticsData::NPTS) == 3, AipsError ); AlwaysAssert(cs.getStatistic( StatisticsData::RMS) == sqrt(195.25/11.0), AipsError ); } { // integer weights, ranges, and masks HingesFencesStatistics::const_iterator, vector::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector m0(v0.size()); m0[0] = True; m0[1] = True; m0[2] = True; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = True; m1[1] = True; m1[2] = False; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 12; cs.setData(v0.begin(), w0.begin(), m0.begin(), v0.size(), r0, False); cs.addData(v1.begin(), w1.begin(), m1.begin(), v1.size(), r1, True); StatsData sd = cs.getStatistics(); Double variance = (195.25 - 40.5*40.5/11.0)/10.0; AlwaysAssert(sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(*sd.max == 8, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 1, AipsError); AlwaysAssert(near(sd.mean, 40.5/11.0), AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 4, AipsError); AlwaysAssert(sd.npts == 3, AipsError); AlwaysAssert(sd.rms == sqrt(195.25/11.0), AipsError); AlwaysAssert(near(sd.stddev, sqrt(variance)), AipsError); AlwaysAssert(sd.sum == 40.5, AipsError); AlwaysAssert(sd.sumweights == 11.0, AipsError); AlwaysAssert(sd.sumsq == 195.25, AipsError); AlwaysAssert(near(sd.variance, variance), AipsError); AlwaysAssert( cs.getStatisticIndex(StatisticsData::MAX) == std::pair(1, 1), AipsError ); AlwaysAssert( cs.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 4), AipsError ); AlwaysAssert(cs.getStatistic( StatisticsData::NPTS) == 3, AipsError ); AlwaysAssert(cs.getStatistic( StatisticsData::RMS) == sqrt(195.25/11.0), AipsError ); } { // weights, masks HingesFencesStatistics::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector m0(v0.size()); m0[0] = True; m0[1] = False; m0[2] = False; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = False; m1[1] = True; m1[2] = False; cs.setData(v0.begin(), w0.begin(), m0.begin(), v0.size()); cs.addData(v1.begin(), w1.begin(), m1.begin(), v1.size()); StatsData sd = cs.getStatistics(); Double variance = (195.25 - 40.5*40.5/11.0)/10.0; AlwaysAssert(sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(*sd.max == 8, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 1, AipsError); AlwaysAssert(near(sd.mean, 40.5/11.0), AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 4, AipsError); AlwaysAssert(sd.npts == 3, AipsError); AlwaysAssert(sd.rms == sqrt(195.25/11.0), AipsError); AlwaysAssert(near(sd.stddev, sqrt(variance)), AipsError); AlwaysAssert(sd.sum == 40.5, AipsError); AlwaysAssert(sd.sumweights == 11.0, AipsError); AlwaysAssert(sd.sumsq == 195.25, AipsError); AlwaysAssert(near(sd.variance, variance), AipsError); } { // integer weights, masks HingesFencesStatistics::const_iterator, vector::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector m0(v0.size()); m0[0] = True; m0[1] = False; m0[2] = False; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = False; m1[1] = True; m1[2] = False; cs.setData(v0.begin(), w0.begin(), m0.begin(), v0.size()); cs.addData(v1.begin(), w1.begin(), m1.begin(), v1.size()); StatsData sd = cs.getStatistics(); Double variance = (195.25 - 40.5*40.5/11.0)/10.0; AlwaysAssert(sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(*sd.max == 8, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 1, AipsError); AlwaysAssert(near(sd.mean, 40.5/11.0), AipsError); AlwaysAssert(*sd.min == 2.5, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 4, AipsError); AlwaysAssert(sd.npts == 3, AipsError); AlwaysAssert(sd.rms == sqrt(195.25/11.0), AipsError); AlwaysAssert(near(sd.stddev, sqrt(variance)), AipsError); AlwaysAssert(sd.sum == 40.5, AipsError); AlwaysAssert(sd.sumweights == 11.0, AipsError); AlwaysAssert(sd.sumsq == 195.25, AipsError); AlwaysAssert(near(sd.variance, variance), AipsError); } { // getMinMax(), two datasets HingesFencesStatistics::const_iterator, vector::const_iterator> cs; cs.setData(v0.begin(), v0.size()); cs.addData(v1.begin(), v1.size()); Double mymin, mymax; cs.getMinMax(mymin, mymax); AlwaysAssert(mymin == 1, AipsError); AlwaysAssert(mymax == 10, AipsError); } { // getMinMax(), two datasets, stride = 2,1 HingesFencesStatistics::const_iterator, vector::const_iterator> cs; cs.setData(v0.begin(), 3, 2); cs.addData(v1.begin(), v1.size()); Double mymin, mymax; cs.getMinMax(mymin, mymax); AlwaysAssert(mymin == 1.5, AipsError); AlwaysAssert(mymax == 10, AipsError); } { // getMaxMin(), data ranges HingesFencesStatistics::const_iterator, vector::const_iterator> cs; vector > r0(1); r0[0].first = 2.4; r0[0].second = 6; vector > r1(2); r1[0].first = 9; r1[0].second = 11; r1[1].first = 2; r1[1].second = 7; cs.setData(v0.begin(), v0.size(), r0); cs.addData(v1.begin(), v1.size(), r1, False); Double mymin, mymax; cs.getMinMax(mymin, mymax); AlwaysAssert(mymin == 2.5, AipsError); AlwaysAssert(mymax == 8, AipsError); } { // getMinMax(), mask HingesFencesStatistics::const_iterator, vector::const_iterator> cs; vector m0(v0.size()); m0[0] = False; m0[1] = False; m0[2] = False; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = False; m1[1] = True; m1[2] = False; cs.setData(v0.begin(), m0.begin(), v0.size()); cs.addData(v1.begin(), m1.begin(), v1.size()); Double mymin, mymax; cs.getMinMax(mymin, mymax); AlwaysAssert(mymin == 2.5, AipsError); AlwaysAssert(mymax == 8, AipsError); } { // getMinMax(), mask and ranges HingesFencesStatistics::const_iterator, vector::const_iterator> cs; vector m0(v0.size()); m0[0] = False; m0[1] = True; m0[2] = True; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = True; m1[1] = True; m1[2] = False; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 9; cs.setData(v0.begin(), m0.begin(), v0.size(), r0, False); cs.addData(v1.begin(), m1.begin(), v1.size(), r1, True); Double mymin, mymax; cs.getMinMax(mymin, mymax); AlwaysAssert(mymin == 2.5, AipsError); AlwaysAssert(mymax == 8, AipsError); } { // getMinMax, weights HingesFencesStatistics::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 1; w0[1] = 0; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 0; cs.setData(v0.begin(), w0.begin(), w0.size()); cs.addData(v1.begin(), w1.begin(), w1.size()); Double mymin, mymax; cs.getMinMax(mymin, mymax); AlwaysAssert(mymin == 1.5, AipsError); AlwaysAssert(mymax == 8, AipsError); } { // getMinMax, integer weights HingesFencesStatistics::const_iterator, vector::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 1; w0[1] = 0; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 0; cs.setData(v0.begin(), w0.begin(), w0.size()); cs.addData(v1.begin(), w1.begin(), w1.size()); Double mymin, mymax; cs.getMinMax(mymin, mymax); AlwaysAssert(mymin == 1.5, AipsError); AlwaysAssert(mymax == 8, AipsError); } { // getMinMax(), weights and ranges HingesFencesStatistics::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 9; cs.setData(v0.begin(), w0.begin(), v0.size(), r0, False); cs.addData(v1.begin(), w1.begin(), v1.size(), r1, True); Double mymin, mymax; cs.getMinMax(mymin, mymax); AlwaysAssert(mymin == 2.5, AipsError); AlwaysAssert(mymax == 8, AipsError); } { // getMinMax(), integer weights and ranges HingesFencesStatistics::const_iterator, vector::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 9; cs.setData(v0.begin(), w0.begin(), v0.size(), r0, False); cs.addData(v1.begin(), w1.begin(), v1.size(), r1, True); Double mymin, mymax; cs.getMinMax(mymin, mymax); AlwaysAssert(mymin == 2.5, AipsError); AlwaysAssert(mymax == 8, AipsError); } { // getMinMax(), weights, ranges, and masks HingesFencesStatistics::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector m0(v0.size()); m0[0] = True; m0[1] = True; m0[2] = True; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = True; m1[1] = True; m1[2] = False; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 12; cs.setData(v0.begin(), w0.begin(), m0.begin(), v0.size(), r0, False); cs.addData(v1.begin(), w1.begin(), m1.begin(), v1.size(), r1, True); Double mymin, mymax; cs.getMinMax(mymin, mymax); AlwaysAssert(mymin == 2.5, AipsError); AlwaysAssert(mymax == 8, AipsError); } { // getMinMax(), integer weights, ranges, and masks HingesFencesStatistics::const_iterator, vector::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector m0(v0.size()); m0[0] = True; m0[1] = True; m0[2] = True; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = True; m1[1] = True; m1[2] = False; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 12; cs.setData(v0.begin(), w0.begin(), m0.begin(), v0.size(), r0, False); cs.addData(v1.begin(), w1.begin(), m1.begin(), v1.size(), r1, True); Double mymin, mymax; cs.getMinMax(mymin, mymax); AlwaysAssert(mymin == 2.5, AipsError); AlwaysAssert(mymax == 8, AipsError); } { // general quantile exceptions HingesFencesStatistics::const_iterator, vector::const_iterator> cs; cs.setData(v0.begin(), v0.size()); cs.addData(v1.begin(), v1.size()); Bool thrown = False; try { cs.getQuantile(0); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); thrown = False; try { cs.getQuantile(1); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); } { // getQuantile(), no weights, no mask, no ranges HingesFencesStatistics::const_iterator, vector::const_iterator> cs; cs.setData(v0.begin(), v0.size()); cs.addData(v1.begin(), v1.size()); Double q = cs.getQuantile(0.1); AlwaysAssert(q == 1.0, AipsError); q = cs.getQuantile(0.2); AlwaysAssert(q == 1.5, AipsError); q = cs.getQuantile(0.3); AlwaysAssert(q == 2.0, AipsError); q = cs.getQuantile(0.4); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.5); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.6); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.7); AlwaysAssert(q == 5.0, AipsError); q = cs.getQuantile(0.8); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.9); AlwaysAssert(q == 10.0, AipsError); } { // getQuantile(): two datasets, stride = 2,1 // 1.5, 2, 2.5 5, 8, 10 HingesFencesStatistics::const_iterator, vector::const_iterator> hfs; hfs.setData(v0.begin(), v0.size(), 2); hfs.addData(v1.begin(), v1.size()); Double q = hfs.getQuantile(0.1); AlwaysAssert(q == 1.5, AipsError); q = hfs.getQuantile(0.2); AlwaysAssert(q == 2.0, AipsError); q = hfs.getQuantile(0.3); AlwaysAssert(q == 2.0, AipsError); q = hfs.getQuantile(0.4); AlwaysAssert(q == 2.5, AipsError); q = hfs.getQuantile(0.5); AlwaysAssert(q == 2.5, AipsError); q = hfs.getQuantile(0.6); AlwaysAssert(q == 5.0, AipsError); q = hfs.getQuantile(0.7); AlwaysAssert(q == 8.0, AipsError); q = hfs.getQuantile(0.8); AlwaysAssert(q == 8.0, AipsError); q = hfs.getQuantile(0.9); AlwaysAssert(q == 10.0, AipsError); } { // getQuantile(), ranges HingesFencesStatistics::const_iterator, vector::const_iterator> cs; vector > r0(1), r1(2); r0[0].first = 2.4; r0[0].second = 6; r1[0].first = 9; r1[0].second = 11; r1[1].first = 2; r1[1].second = 7; cs.setData(v0.begin(), v0.size(), r0); cs.addData(v1.begin(), v1.size(), r1, False); // 2.5, 3, 8 Double q = cs.getQuantile(0.1); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.2); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.3); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.4); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.5); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.6); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.7); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.8); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.9); AlwaysAssert(q == 8.0, AipsError); } { // getQuantile(): mask HingesFencesStatistics::const_iterator, vector::const_iterator> cs; vector m0(v0.size()); m0[0] = False; m0[1] = False; m0[2] = False; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = False; m1[1] = True; m1[2] = False; cs.setData(v0.begin(), m0.begin(), v0.size()); cs.addData(v1.begin(), m1.begin(), v1.size()); // 2.5, 3, 8 Double q = cs.getQuantile(0.1); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.2); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.3); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.4); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.5); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.6); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.7); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.8); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.9); AlwaysAssert(q == 8.0, AipsError); } { //getQuantile(): mask and ranges HingesFencesStatistics::const_iterator, vector::const_iterator> cs; vector m0(v0.size()); m0[0] = False; m0[1] = True; m0[2] = True; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = True; m1[1] = True; m1[2] = False; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 9; cs.setData(v0.begin(), m0.begin(), v0.size(), r0, False); cs.addData(v1.begin(), m1.begin(), v1.size(), r1, True); // 2.5, 3, 8 Double q = cs.getQuantile(0.1); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.2); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.3); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.4); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.5); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.6); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.7); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.8); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.9); AlwaysAssert(q == 8.0, AipsError); } { // getQuantile(): weights HingesFencesStatistics::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; cs.setData(v0.begin(), w0.begin(), w0.size()); cs.addData(v1.begin(), w1.begin(), w1.size()); // 1, 1.5, 2.5, 3, 5, 8, 10 Double q = cs.getQuantile(0.1); AlwaysAssert(q == 1.0, AipsError); q = cs.getQuantile(0.2); AlwaysAssert(q == 1.5, AipsError); q = cs.getQuantile(0.3); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.4); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.5); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.6); AlwaysAssert(q == 5.0, AipsError); q = cs.getQuantile(0.7); AlwaysAssert(q == 5.0, AipsError); q = cs.getQuantile(0.8); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.9); AlwaysAssert(q == 10.0, AipsError); } { // getQuantile(): integer weights HingesFencesStatistics::const_iterator, vector::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; cs.setData(v0.begin(), w0.begin(), w0.size()); cs.addData(v1.begin(), w1.begin(), w1.size()); // 1, 1.5, 2.5, 3, 5, 8, 10 Double q = cs.getQuantile(0.1); AlwaysAssert(q == 1.0, AipsError); q = cs.getQuantile(0.2); AlwaysAssert(q == 1.5, AipsError); q = cs.getQuantile(0.3); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.4); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.5); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.6); AlwaysAssert(q == 5.0, AipsError); q = cs.getQuantile(0.7); AlwaysAssert(q == 5.0, AipsError); q = cs.getQuantile(0.8); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.9); AlwaysAssert(q == 10.0, AipsError); } { // getQuantile(): ranges and weights HingesFencesStatistics::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 9; cs.setData(v0.begin(), w0.begin(), v0.size(), r0, False); cs.addData(v1.begin(), w1.begin(), v1.size(), r1, True); // 2.5, 3, 8 Double q = cs.getQuantile(0.1); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.2); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.3); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.4); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.5); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.6); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.7); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.8); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.9); AlwaysAssert(q == 8.0, AipsError); } { // getQuantile(): ranges and integer weights HingesFencesStatistics::const_iterator, vector::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 9; cs.setData(v0.begin(), w0.begin(), v0.size(), r0, False); cs.addData(v1.begin(), w1.begin(), v1.size(), r1, True); // 2.5, 3, 8 Double q = cs.getQuantile(0.1); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.2); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.3); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.4); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.5); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.6); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.7); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.8); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.9); AlwaysAssert(q == 8.0, AipsError); } { // getQuantile(): weights and mask HingesFencesStatistics::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector m0(v0.size()); m0[0] = True; m0[1] = False; m0[2] = False; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = False; m1[1] = True; m1[2] = False; cs.setData(v0.begin(), w0.begin(), m0.begin(), v0.size()); cs.addData(v1.begin(), w1.begin(), m1.begin(), v1.size()); // 2.5, 3, 8 Double q = cs.getQuantile(0.1); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.2); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.3); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.4); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.5); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.6); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.7); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.8); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.9); AlwaysAssert(q == 8.0, AipsError); } { // getQuantile(): integer weights and mask HingesFencesStatistics::const_iterator, vector::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector m0(v0.size()); m0[0] = True; m0[1] = False; m0[2] = False; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = False; m1[1] = True; m1[2] = False; cs.setData(v0.begin(), w0.begin(), m0.begin(), v0.size()); cs.addData(v1.begin(), w1.begin(), m1.begin(), v1.size()); // 2.5, 3, 8 Double q = cs.getQuantile(0.1); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.2); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.3); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.4); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.5); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.6); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.7); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.8); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.9); AlwaysAssert(q == 8.0, AipsError); } { // getQuantile(): weights, mask, ranges HingesFencesStatistics::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector m0(v0.size()); m0[0] = True; m0[1] = True; m0[2] = True; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = True; m1[1] = True; m1[2] = False; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 12; cs.setData(v0.begin(), w0.begin(), m0.begin(), v0.size(), r0, False); cs.addData(v1.begin(), w1.begin(), m1.begin(), v1.size(), r1, True); // 2.5, 3, 8 Double q = cs.getQuantile(0.1); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.2); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.3); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.4); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.5); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.6); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.7); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.8); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.9); AlwaysAssert(q == 8.0, AipsError); std::set quantiles; quantiles.insert(0.1); quantiles.insert(0.2); quantiles.insert(0.3); quantiles.insert(0.4); quantiles.insert(0.5); quantiles.insert(0.6); quantiles.insert(0.7); quantiles.insert(0.8); quantiles.insert(0.9); std::map qs = cs.getQuantiles(quantiles); AlwaysAssert(qs[0.1] == 2.5, AipsError); AlwaysAssert(qs[0.2] == 2.5, AipsError); AlwaysAssert(qs[0.3] == 2.5, AipsError); AlwaysAssert(qs[0.4] == 3.0, AipsError); AlwaysAssert(qs[0.5] == 3.0, AipsError); AlwaysAssert(qs[0.6] == 3.0, AipsError); AlwaysAssert(qs[0.7] == 8.0, AipsError); AlwaysAssert(qs[0.8] == 8.0, AipsError); AlwaysAssert(qs[0.9] == 8.0, AipsError); } { // getQuantile(): integer weights, mask, ranges HingesFencesStatistics::const_iterator, vector::const_iterator, vector::const_iterator> cs; vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; vector w1(v1.size()); w1[0] = 1; w1[1] = 2; w1[2] = 3; vector m0(v0.size()); m0[0] = True; m0[1] = True; m0[2] = True; m0[3] = True; m0[4] = True; vector m1(v1.size()); m1[0] = True; m1[1] = True; m1[2] = False; vector > r0(1); r0[0].first = 0.9; r0[0].second = 1.6; vector > r1(1); r1[0].first = 6; r1[0].second = 12; cs.setData(v0.begin(), w0.begin(), m0.begin(), v0.size(), r0, False); cs.addData(v1.begin(), w1.begin(), m1.begin(), v1.size(), r1, True); // 2.5, 3, 8 Double q = cs.getQuantile(0.1); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.2); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.3); AlwaysAssert(q == 2.5, AipsError); q = cs.getQuantile(0.4); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.5); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.6); AlwaysAssert(q == 3.0, AipsError); q = cs.getQuantile(0.7); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.8); AlwaysAssert(q == 8.0, AipsError); q = cs.getQuantile(0.9); AlwaysAssert(q == 8.0, AipsError); std::set quantiles; quantiles.insert(0.1); quantiles.insert(0.2); quantiles.insert(0.3); quantiles.insert(0.4); quantiles.insert(0.5); quantiles.insert(0.6); quantiles.insert(0.7); quantiles.insert(0.8); quantiles.insert(0.9); std::map qs = cs.getQuantiles(quantiles); AlwaysAssert(qs[0.1] == 2.5, AipsError); AlwaysAssert(qs[0.2] == 2.5, AipsError); AlwaysAssert(qs[0.3] == 2.5, AipsError); AlwaysAssert(qs[0.4] == 3.0, AipsError); AlwaysAssert(qs[0.5] == 3.0, AipsError); AlwaysAssert(qs[0.6] == 3.0, AipsError); AlwaysAssert(qs[0.7] == 8.0, AipsError); AlwaysAssert(qs[0.8] == 8.0, AipsError); AlwaysAssert(qs[0.9] == 8.0, AipsError); } { // leave in for compile check HingesFencesStatistics::const_iterator, vector::const_iterator> cs; } { // getMedian() HingesFencesStatistics::const_iterator, vector::const_iterator> cs; cs.addData(v0.begin(), v0.size()); Double median = cs.getMedian(); AlwaysAssert(median == 2, AipsError); cs.reset(); vector m0(v0.size(), True); m0[0] = False; cs.addData(v0.begin(), m0.begin(), v0.size()); median = cs.getMedian(); AlwaysAssert(median == 2, AipsError); } { // getMedianAndQuantiles (even sized data set) std::set quantiles; quantiles.insert(0.1); quantiles.insert(0.2); quantiles.insert(0.3); quantiles.insert(0.4); quantiles.insert(0.5); quantiles.insert(0.6); quantiles.insert(0.7); quantiles.insert(0.8); quantiles.insert(0.9); HingesFencesStatistics::const_iterator, vector::const_iterator> cs; cs.setData(v0.begin(), v0.size()); cs.addData(v1.begin(), v1.size()); std::map quantileToValue; Double median = cs.getMedianAndQuantiles(quantileToValue, quantiles); AlwaysAssert(median == 2.75, AipsError); AlwaysAssert(quantileToValue[0.1] == 1.0, AipsError); AlwaysAssert(quantileToValue[0.2] == 1.5, AipsError); AlwaysAssert(quantileToValue[0.3] == 2.0, AipsError); AlwaysAssert(quantileToValue[0.4] == 2.5, AipsError); AlwaysAssert(quantileToValue[0.5] == 2.5, AipsError); AlwaysAssert(quantileToValue[0.6] == 3.0, AipsError); AlwaysAssert(quantileToValue[0.7] == 5.0, AipsError); AlwaysAssert(quantileToValue[0.8] == 8.0, AipsError); AlwaysAssert(quantileToValue[0.9] == 10.0, AipsError); } { // getMedianAndQuantiles (odd sized data set) std::set quantiles; quantiles.insert(0.1); quantiles.insert(0.2); quantiles.insert(0.3); quantiles.insert(0.4); quantiles.insert(0.5); quantiles.insert(0.6); quantiles.insert(0.7); quantiles.insert(0.8); quantiles.insert(0.9); vector > r0(1); r0[0].first = 9; r0[0].second = 11; HingesFencesStatistics::const_iterator, vector::const_iterator> cs; cs.setData(v0.begin(), v0.size()); cs.addData(v1.begin(), v1.size(), r0, False); std::map quantileToValue; Double median = cs.getMedianAndQuantiles(quantileToValue, quantiles); AlwaysAssert(median == 2.5, AipsError); AlwaysAssert(quantileToValue[0.1] == 1.0, AipsError); AlwaysAssert(quantileToValue[0.2] == 1.5, AipsError); AlwaysAssert(quantileToValue[0.3] == 2.0, AipsError); AlwaysAssert(quantileToValue[0.4] == 2.0, AipsError); AlwaysAssert(quantileToValue[0.5] == 2.5, AipsError); AlwaysAssert(quantileToValue[0.6] == 3.0, AipsError); AlwaysAssert(quantileToValue[0.7] == 3.0, AipsError); AlwaysAssert(quantileToValue[0.8] == 5.0, AipsError); AlwaysAssert(quantileToValue[0.9] == 8.0, AipsError); } { // getMedianAndQuantiles (even sized data set) HingesFencesStatistics::const_iterator, vector::const_iterator> cs; cs.setData(v0.begin(), v0.size()); cs.addData(v1.begin(), v1.size()); Double medabsdevmed = cs.getMedianAbsDevMed(); AlwaysAssert(medabsdevmed == 1.5, AipsError); } { // getMedianAndQuantiles (odd sized data set) vector > r0(1); r0[0].first = 9; r0[0].second = 11; HingesFencesStatistics::const_iterator, vector::const_iterator> cs; cs.setData(v0.begin(), v0.size()); cs.addData(v1.begin(), v1.size(), r0, False); Double medabsdevmed = cs.getMedianAbsDevMed(); AlwaysAssert(medabsdevmed == 1.0, AipsError); } uInt npts = (uInt)1e6; vector bigData(npts); vector::iterator iter = bigData.begin(); vector::iterator end = bigData.end(); uInt64 count = 0; while(iter != end) { *iter = count % 2 == 0 ? (Float)count : -Float(count*count); ++iter; ++count; } vector bigMask(npts, True); bigMask[0] = False; { // getMedian() with binning, no ranges, weights, or mask HingesFencesStatistics::const_iterator, vector::const_iterator> cs; cs.setData(bigData.begin(), bigData.size()); // enforce a small internal array size so binning algorithm is used Double median = cs.getMedian(NULL, NULL, NULL, 100); AlwaysAssert(median == -0.5, AipsError); } { // getMedian() with mask, but no weights or ranges, using binning HingesFencesStatistics::const_iterator, vector::const_iterator> cs; cs.setData(bigData.begin(), bigMask.begin(), bigData.size()); // enforce a small internal array size so binning algorithm is used Double median = cs.getMedian(NULL, NULL, NULL, 100); AlwaysAssert(median == -1, AipsError); } { // getMedianAbsDevMed() with binning, no ranges, weights, or mask HingesFencesStatistics::const_iterator, vector::const_iterator> cs; cs.setData(bigData.begin(), bigData.size()); // enforce a small internal array size so binning algorithm is used Double medabsdevmed = cs.getMedianAbsDevMed(NULL, NULL, NULL, 100); AlwaysAssert(medabsdevmed == 998999.5, AipsError); } { // getMedianAbsDevMed() with mask, but no weights or ranges, using binning HingesFencesStatistics::const_iterator, vector::const_iterator> cs; cs.setData(bigData.begin(), bigMask.begin(), bigData.size()); // enforce a small internal array size so binning algorithm is used Double medabsdevmed = cs.getMedianAbsDevMed(NULL, NULL, NULL, 100); AlwaysAssert(medabsdevmed == 999001, AipsError); } { // large array with all the same values, getMedianAndQuartile() vector big(100000, 0); HingesFencesStatistics::const_iterator, vector::const_iterator> cs; cs.addData(big.begin(), big.size()); std::set quantiles; quantiles.insert(0.25); quantiles.insert(0.75); std::shared_ptr npts; std::shared_ptr mymin, mymax; std::map quantileToValue; Double median = cs.getMedianAndQuantiles( quantileToValue, quantiles, npts, mymin, mymax, 99999 ); AlwaysAssert(median == 0, AipsError); AlwaysAssert(quantileToValue[0.25] == 0, AipsError); AlwaysAssert(quantileToValue[0.75] == 0, AipsError); } // now begin testing the specialized behavior of HingesFencesStatistics v0.resize(12); v0[0] = 5; v0[1] = 2; v0[2] = 6; v0[3] = 10; v0[4] = 7; v0[5] = -1; v0[6] = 15; v0[7] = 11; v0[8] = 6; v0[9] = 20; v0[10] = -3; v0[11] = 14; // for v, the members between Q1 and Q3 inclusive are // 2, 5, 6, 6, 7, 10, 11 { HingesFencesStatistics::const_iterator, vector::const_iterator> hfs(0); hfs.setData(v0.begin(), v0.size()); StatsData sd = hfs.getStatistics(); Double eSum = 47; Double eNpts = 7; Double eSumSq = 371; Double eVar = 9.238095238095239; AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 11, AipsError); AlwaysAssert(sd.maxpos.first == 0, AipsError); AlwaysAssert(sd.maxpos.second == 7, AipsError); AlwaysAssert(sd.mean == eSum/eNpts, AipsError); AlwaysAssert(*sd.min == 2, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 1, AipsError); AlwaysAssert(sd.npts == eNpts, AipsError); AlwaysAssert(sd.rms == sqrt(eSumSq/eNpts), AipsError); AlwaysAssert(near(sd.stddev, sqrt(eVar)), AipsError); AlwaysAssert(sd.sum == eSum, AipsError); AlwaysAssert(sd.sumsq == eSumSq, AipsError); AlwaysAssert(near(sd.variance, eVar), AipsError); AlwaysAssert( hfs.getStatisticIndex(StatisticsData::MAX) == std::pair(0, 7), AipsError ); AlwaysAssert( hfs.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 1), AipsError ); AlwaysAssert(hfs.getStatistic( StatisticsData::NPTS) == eNpts, AipsError ); AlwaysAssert(hfs.getStatistic( StatisticsData::RMS) == sqrt(eSumSq/eNpts), AipsError ); } { // just another way of specifying the data Double kk[] = {5, 2, 6, 10, 7, -1, 15, 11, 6, 20, -3, 14}; HingesFencesStatistics hfs(0); hfs.setData(kk, 12); Double eSum = 47; Double eNpts = 7; Double eSumSq = 371; Double eVar = 9.238095238095239; StatsData sd = hfs.getStatistics(); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 11, AipsError); AlwaysAssert(sd.maxpos.first == 0, AipsError); AlwaysAssert(sd.maxpos.second == 7, AipsError); AlwaysAssert(sd.mean == eSum/eNpts, AipsError); AlwaysAssert(*sd.min == 2, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 1, AipsError); AlwaysAssert(sd.npts == eNpts, AipsError); AlwaysAssert(sd.rms == sqrt(eSumSq/eNpts), AipsError); AlwaysAssert(near(sd.stddev, sqrt(eVar)), AipsError); AlwaysAssert(sd.sum == eSum, AipsError); AlwaysAssert(sd.sumsq == eSumSq, AipsError); AlwaysAssert(near(sd.variance, eVar), AipsError); AlwaysAssert( hfs.getStatisticIndex(StatisticsData::MAX) == std::pair(0, 7), AipsError ); AlwaysAssert( hfs.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 1), AipsError ); AlwaysAssert(hfs.getStatistic( StatisticsData::NPTS) == eNpts, AipsError ); AlwaysAssert(hfs.getStatistic( StatisticsData::RMS) == sqrt(eSumSq/eNpts), AipsError ); } { // two datasets HingesFencesStatistics::const_iterator, vector::const_iterator> hfs(0); hfs.setData(v0.begin(), v0.size()/2); hfs.addData(v0.begin() + v0.size()/2, v0.size() - v0.size()/2); Double eSum = 47; Double eNpts = 7; Double eSumSq = 371; Double eVar = 9.238095238095239; StatsData sd = hfs.getStatistics(); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 11, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 1, AipsError); AlwaysAssert(sd.mean == eSum/eNpts, AipsError); AlwaysAssert(*sd.min == 2, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 1, AipsError); AlwaysAssert(sd.npts == eNpts, AipsError); AlwaysAssert(sd.rms == sqrt(eSumSq/eNpts), AipsError); AlwaysAssert(near(sd.stddev, sqrt(eVar)), AipsError); AlwaysAssert(sd.sum == eSum, AipsError); AlwaysAssert(sd.sumsq == eSumSq, AipsError); AlwaysAssert(near(sd.variance, eVar), AipsError); AlwaysAssert( hfs.getStatisticIndex(StatisticsData::MAX) == std::pair(1, 1), AipsError ); AlwaysAssert( hfs.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 1), AipsError ); AlwaysAssert(hfs.getStatistic( StatisticsData::NPTS) == eNpts, AipsError ); AlwaysAssert(hfs.getStatistic( StatisticsData::RMS) == sqrt(eSumSq/eNpts), AipsError ); // Now reverse the order that the datasets were added. results // should be the same except for min and max dataset locations hfs.setData(v0.begin() + v0.size()/2, v0.size() - v0.size()/2); hfs.addData(v0.begin(), v0.size()/2); sd = hfs.getStatistics(); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 11, AipsError); AlwaysAssert(sd.maxpos.first == 0, AipsError); AlwaysAssert(sd.maxpos.second == 1, AipsError); AlwaysAssert(sd.mean == eSum/eNpts, AipsError); AlwaysAssert(*sd.min == 2, AipsError); AlwaysAssert(sd.minpos.first == 1, AipsError); AlwaysAssert(sd.minpos.second == 1, AipsError); AlwaysAssert(sd.npts == eNpts, AipsError); AlwaysAssert(sd.rms == sqrt(eSumSq/eNpts), AipsError); AlwaysAssert(near(sd.stddev, sqrt(eVar)), AipsError); AlwaysAssert(sd.sum == eSum, AipsError); AlwaysAssert(sd.sumsq == eSumSq, AipsError); AlwaysAssert(near(sd.variance, eVar), AipsError); AlwaysAssert( hfs.getStatisticIndex(StatisticsData::MAX) == std::pair(0, 1), AipsError ); AlwaysAssert( hfs.getStatisticIndex(StatisticsData::MIN) == std::pair(1, 1), AipsError ); AlwaysAssert(hfs.getStatistic( StatisticsData::NPTS) == eNpts, AipsError ); AlwaysAssert(hfs.getStatistic( StatisticsData::RMS) == sqrt(eSumSq/eNpts), AipsError ); } { // verify that datasets cannot be accumulated as added HingesFencesStatistics::const_iterator, vector::const_iterator> hfs(0); Bool except = False; try { hfs.setCalculateAsAdded(True); } catch (const AipsError&) { except = True; } AlwaysAssert(except, AipsError); } { // two datasets, stride = 2,1 // values of the inner quartile in this case are 6, 7, 11, 6, 14 HingesFencesStatistics::const_iterator, vector::const_iterator> hfs(0); hfs.setData(v0.begin(), v0.size()/2, 2); hfs.addData(v0.begin() + v0.size()/2, v0.size() - v0.size()/2); Double eSum = 44; Double eNpts = 5; Double eSumSq = 438; Double eVar = 12.7; StatsData sd = hfs.getStatistics(); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 14, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 5, AipsError); AlwaysAssert(sd.mean == eSum/eNpts, AipsError); AlwaysAssert(*sd.min == 6, AipsError); // the min, 6, occurs twice, at dataset 0, pos 2, and dataset 1, pos 2 AlwaysAssert(sd.minpos.first == 0 || sd.minpos.first == 1, AipsError); AlwaysAssert(sd.minpos.second == 2, AipsError); AlwaysAssert(sd.npts == eNpts, AipsError); AlwaysAssert(sd.rms == sqrt(eSumSq/eNpts), AipsError); AlwaysAssert(sd.stddev == sqrt(eVar), AipsError); AlwaysAssert(sd.sum == eSum, AipsError); AlwaysAssert(sd.sumsq == eSumSq, AipsError); AlwaysAssert(near(sd.variance, eVar), AipsError); AlwaysAssert( hfs.getStatisticIndex(StatisticsData::MAX) == std::pair(1, 5), AipsError ); AlwaysAssert( hfs.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 2) || hfs.getStatisticIndex(StatisticsData::MIN) == std::pair(1, 2), AipsError ); AlwaysAssert(hfs.getStatistic( StatisticsData::NPTS) == eNpts, AipsError ); AlwaysAssert(hfs.getStatistic( StatisticsData::RMS) == sqrt(eSumSq/eNpts), AipsError ); } { // data ranges // 5, 2, 6, 10, 7, -1 // 15, 11, 6, 20, -3, 14 // 5, 6, 15, 20, -3, 14 // 5, 6, 15, 14 HingesFencesStatistics::const_iterator, vector::const_iterator> hfs(0); vector > r0(1); r0[0].first = 5; r0[0].second = -5; Bool expectedFail = False; try { hfs.setData(v0.begin(), v0.size(), r0); } catch (const std::exception& x) { expectedFail = True; } AlwaysAssert(expectedFail, AipsError); r0[0].first = 2.4; r0[0].second = 6; vector > r1(2); r1[0].first = 9; r1[0].second = 11; r1[1].first = 2; r1[1].second = 7; hfs.setData(v0.begin(), v0.size()/2, r0); hfs.addData(v0.begin() + v0.size()/2, v0.size() - v0.size()/2, r1, False); StatsData sd = hfs.getStatistics(); Double eSum = 40; Double eNpts = 4; Double eSumSq = 482; Double eVar = (eSumSq - eSum*eSum/eNpts)/(eNpts - 1); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 15, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 0, AipsError); AlwaysAssert(sd.mean == eSum/eNpts, AipsError); AlwaysAssert(*sd.min == 5, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 0, AipsError); AlwaysAssert(sd.npts == eNpts, AipsError); AlwaysAssert(sd.rms == sqrt(eSumSq/eNpts), AipsError); AlwaysAssert(near(sd.stddev, sqrt(eVar)), AipsError); AlwaysAssert(sd.sum == eSum, AipsError); AlwaysAssert(sd.sumsq == eSumSq, AipsError); AlwaysAssert(near(sd.variance, eVar), AipsError); AlwaysAssert( hfs.getStatisticIndex(StatisticsData::MAX) == std::pair(1, 0), AipsError ); AlwaysAssert( hfs.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 0), AipsError ); AlwaysAssert(hfs.getStatistic( StatisticsData::NPTS) == eNpts, AipsError ); AlwaysAssert(hfs.getStatistic( StatisticsData::RMS) == sqrt(eSumSq/eNpts), AipsError ); } { // mask // 5, 2, 6, 10, 7, -1 // 15, 11, 6, 20, -3, 14 // 10, 7, 15, 11, 14 // 10, 11, 14 HingesFencesStatistics::const_iterator, vector::const_iterator> hfs(0); vector m0(v0.size()); m0[0] = False; m0[1] = False; m0[2] = False; m0[3] = True; m0[4] = True; m0[5] = False; m0[6] = True; m0[7] = True; m0[8] = False; m0[9] = False; m0[10] = False; m0[11] = True; hfs.setData(v0.begin(), m0.begin(), v0.size()/2); hfs.addData(v0.begin() + v0.size()/2, m0.begin() + m0.size()/2, v0.size() - v0.size()/2); StatsData sd = hfs.getStatistics(); Double eSum = 35; Double eNpts = 3; Double eSumSq = 417; Double eVar = (eSumSq - eSum*eSum/eNpts)/(eNpts - 1); AlwaysAssert(sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 14, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 5, AipsError); AlwaysAssert(sd.mean == eSum/eNpts, AipsError); AlwaysAssert(*sd.min == 10, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 3, AipsError); AlwaysAssert(sd.npts == eNpts, AipsError); AlwaysAssert(sd.rms == sqrt(eSumSq/eNpts), AipsError); AlwaysAssert(near(sd.stddev, sqrt(eVar)), AipsError); AlwaysAssert(sd.sum == eSum, AipsError); AlwaysAssert(sd.sumsq == eSumSq, AipsError); AlwaysAssert(near(sd.variance, eVar), AipsError); AlwaysAssert( hfs.getStatisticIndex(StatisticsData::MAX) == std::pair(1, 5), AipsError ); AlwaysAssert( hfs.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 3), AipsError ); AlwaysAssert(hfs.getStatistic( StatisticsData::NPTS) == eNpts, AipsError ); AlwaysAssert(hfs.getStatistic( StatisticsData::RMS) == sqrt(eSumSq/eNpts), AipsError ); } { // mask and ranges // 5, 2, 6, 10, 7, -1 // 15, 11, 6, 20, -3, 14 // 2, 6, 10, 7, -1 // 11, 6, 20, -3, 14 // 2, 6, 10, -1 // 11, 6, 20, 14 // 2, 6, 10, 11, 6 HingesFencesStatistics::const_iterator, vector::const_iterator> hfs(0); vector m0(v0.size()); m0[0] = False; m0[1] = True; m0[2] = True; m0[3] = True; m0[4] = True; m0[5] = True; m0[6] = False; m0[7] = True; m0[8] = True; m0[9] = True; m0[10] = True; m0[11] = True; vector > r0(1); r0[0].first = 7; r0[0].second = 8; vector > r1(1); r1[0].first = 6; r1[0].second = 21; hfs.setData(v0.begin(), m0.begin(), v0.size()/2, r0, False); hfs.addData(v0.begin() + v0.size()/2, m0.begin() + m0.size()/2, v0.size() - v0.size()/2, r1, True); StatsData sd = hfs.getStatistics(); Double eSum = 35; Double eNpts = 5; Double eSumSq = 297; Double eVar = (eSumSq - eSum*eSum/eNpts)/(eNpts - 1); AlwaysAssert(sd.masked, AipsError); AlwaysAssert(! sd.weighted, AipsError); AlwaysAssert(*sd.max == 11, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 1, AipsError); AlwaysAssert(sd.mean == eSum/eNpts, AipsError); AlwaysAssert(*sd.min == 2, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 1, AipsError); AlwaysAssert(sd.npts == eNpts, AipsError); AlwaysAssert(sd.rms == sqrt(eSumSq/eNpts), AipsError); AlwaysAssert(near(sd.stddev, sqrt(eVar)), AipsError); AlwaysAssert(sd.sum == eSum, AipsError); AlwaysAssert(sd.sumsq == eSumSq, AipsError); AlwaysAssert(near(sd.variance, eVar), AipsError); AlwaysAssert( hfs.getStatisticIndex(StatisticsData::MAX) == std::pair(1, 1), AipsError ); AlwaysAssert( hfs.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 1), AipsError ); AlwaysAssert(hfs.getStatistic( StatisticsData::NPTS) == eNpts, AipsError ); AlwaysAssert(hfs.getStatistic( StatisticsData::RMS) == sqrt(eSumSq/eNpts), AipsError ); } { // weights // 5, 2, 6, 10, 7, -1 // 15, 11, 6, 20, -3, 14 // 2, 6, 10, 7, -1 // 11, 6, 20, -3, 14 // 2, 6, 10, 7 // 11, 6 // 4 + 18 + 40 + 35 + 22 + 18 HingesFencesStatistics::const_iterator, vector::const_iterator> hfs(0); vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; w0[5] = 1; w0[6] = 0; w0[7] = 2; w0[8] = 3; w0[9] = 2; w0[10] = 1; w0[11] = 2; hfs.setData(v0.begin(), w0.begin(), v0.size()/2); hfs.addData(v0.begin() + v0.size()/2, w0.begin() + w0.size()/2, v0.size() - v0.size()/2); StatsData sd = hfs.getStatistics(); Double eSum = 137; Double eSumWeights = 19; Double eNpts = 6; Double eSumSq = 1111; Double eVar = (eSumSq - eSum*eSum/eSumWeights)/(eSumWeights - 1); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(*sd.max == 11, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 1, AipsError); AlwaysAssert(sd.mean == eSum/eSumWeights, AipsError); AlwaysAssert(*sd.min == 2, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 1, AipsError); AlwaysAssert(sd.npts == eNpts, AipsError); AlwaysAssert(sd.rms == sqrt(eSumSq/eSumWeights), AipsError); AlwaysAssert(near(sd.stddev, sqrt(eVar)), AipsError); AlwaysAssert(sd.sum == eSum, AipsError); AlwaysAssert(sd.sumsq == eSumSq, AipsError); AlwaysAssert(near(sd.variance, eVar), AipsError); AlwaysAssert( hfs.getStatisticIndex(StatisticsData::MAX) == std::pair(1, 1), AipsError ); AlwaysAssert( hfs.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 1), AipsError ); AlwaysAssert(hfs.getStatistic( StatisticsData::NPTS) == eNpts, AipsError ); AlwaysAssert(hfs.getStatistic( StatisticsData::RMS) == sqrt(eSumSq/eSumWeights), AipsError ); } { // integer weights // 5, 2, 6, 10, 7, -1 // 15, 11, 6, 20, -3, 14 // 2, 6, 10, 7, -1 // 11, 6, 20, -3, 14 // 2, 6, 10, 7 // 11, 6 // 4 + 18 + 40 + 35 + 22 + 18 HingesFencesStatistics::const_iterator, vector::const_iterator, vector::const_iterator> hfs(0); vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; w0[3] = 4; w0[4] = 5; w0[5] = 1; w0[6] = 0; w0[7] = 2; w0[8] = 3; w0[9] = 2; w0[10] = 1; w0[11] = 2; hfs.setData(v0.begin(), w0.begin(), v0.size()/2); hfs.addData(v0.begin() + v0.size()/2, w0.begin() + w0.size()/2, v0.size() - v0.size()/2); StatsData sd = hfs.getStatistics(); Double eSum = 137; Double eSumWeights = 19; Double eNpts = 6; Double eSumSq = 1111; Double eVar = (eSumSq - eSum*eSum/eSumWeights)/(eSumWeights - 1); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(*sd.max == 11, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 1, AipsError); AlwaysAssert(sd.mean == eSum/eSumWeights, AipsError); AlwaysAssert(*sd.min == 2, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 1, AipsError); AlwaysAssert(sd.npts == eNpts, AipsError); AlwaysAssert(sd.rms == sqrt(eSumSq/eSumWeights), AipsError); AlwaysAssert(near(sd.stddev, sqrt(eVar)), AipsError); AlwaysAssert(sd.sum == eSum, AipsError); AlwaysAssert(sd.sumsq == eSumSq, AipsError); AlwaysAssert(near(sd.variance, eVar), AipsError); AlwaysAssert( hfs.getStatisticIndex(StatisticsData::MAX) == std::pair(1, 1), AipsError ); AlwaysAssert( hfs.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 1), AipsError ); AlwaysAssert(hfs.getStatistic( StatisticsData::NPTS) == eNpts, AipsError ); AlwaysAssert(hfs.getStatistic( StatisticsData::RMS) == sqrt(eSumSq/eSumWeights), AipsError ); } { // weights and ranges // 5, 2, 6, 10, 7, -1 // 15, 11, 6, 20, -3, 14 // 2, 6, 10, 7, -1 // 11, 6, 20, -3, 14 // 6, 10, 7, -1 // 11, 6, 14 // 6, 10, 7 // 11, 6 HingesFencesStatistics::const_iterator, vector::const_iterator> hfs(0); vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; // *6 = 18 w0[3] = 4; // *10 = 40 w0[4] = 5; // *7 = 35 w0[5] = 1; w0[6] = 0; w0[7] = 2; // *11 = 22 w0[8] = 3; // *6 = 18 w0[9] = 2; w0[10] = 1; w0[11] = 2; vector > r0(1); r0[0].first = 1; r0[0].second = 2; vector > r1(1); r1[0].first = 0; r1[0].second = 15; hfs.setData(v0.begin(), w0.begin(), v0.size()/2, r0, False); hfs.addData( v0.begin() + v0.size()/2, w0.begin() + w0.size()/2, v0.size() - v0.size()/2, r1, True ); StatsData sd = hfs.getStatistics(); Double eSum = 133; Double eSumWeights = 17; Double eNpts = 5; Double eSumSq = 1103; Double eVar = (eSumSq - eSum*eSum/eSumWeights)/(eSumWeights - 1); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(*sd.max == 11, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 1, AipsError); AlwaysAssert(sd.mean == eSum/eSumWeights, AipsError); AlwaysAssert(*sd.min == 6, AipsError); // the minimum occurs in two places, and with multi-threading, minpos // could be either one AlwaysAssert(sd.minpos.first == 0 || sd.minpos.first == 1, AipsError); AlwaysAssert(sd.minpos.second == 2, AipsError); AlwaysAssert(sd.npts == eNpts, AipsError); AlwaysAssert(sd.rms == sqrt(eSumSq/eSumWeights), AipsError); AlwaysAssert(near(sd.stddev, sqrt(eVar)), AipsError); AlwaysAssert(sd.sum == eSum, AipsError); AlwaysAssert(sd.sumsq == eSumSq, AipsError); AlwaysAssert(near(sd.variance, eVar), AipsError); AlwaysAssert( hfs.getStatisticIndex(StatisticsData::MAX) == std::pair(1, 1), AipsError ); AlwaysAssert( hfs.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 2), AipsError ); AlwaysAssert( hfs.getStatistic(StatisticsData::NPTS) == eNpts, AipsError ); AlwaysAssert(hfs.getStatistic( StatisticsData::RMS) == sqrt(eSumSq/eSumWeights), AipsError ); } { // integer weights and ranges // 5, 2, 6, 10, 7, -1 // 15, 11, 6, 20, -3, 14 // 2, 6, 10, 7, -1 // 11, 6, 20, -3, 14 // 6, 10, 7, -1 // 11, 6, 14 // 6, 10, 7 // 11, 6 HingesFencesStatistics::const_iterator, vector::const_iterator, vector::const_iterator> hfs(0); vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; // *6 = 18 w0[3] = 4; // *10 = 40 w0[4] = 5; // *7 = 35 w0[5] = 1; w0[6] = 0; w0[7] = 2; // *11 = 22 w0[8] = 3; // *6 = 18 w0[9] = 2; w0[10] = 1; w0[11] = 2; vector > r0(1); r0[0].first = 1; r0[0].second = 2; vector > r1(1); r1[0].first = 0; r1[0].second = 15; hfs.setData(v0.begin(), w0.begin(), v0.size()/2, r0, False); hfs.addData( v0.begin() + v0.size()/2, w0.begin() + w0.size()/2, v0.size() - v0.size()/2, r1, True ); StatsData sd = hfs.getStatistics(); Double eSum = 133; Double eSumWeights = 17; Double eNpts = 5; Double eSumSq = 1103; Double eVar = (eSumSq - eSum*eSum/eSumWeights)/(eSumWeights - 1); AlwaysAssert(! sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(*sd.max == 11, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 1, AipsError); AlwaysAssert(sd.mean == eSum/eSumWeights, AipsError); AlwaysAssert(*sd.min == 6, AipsError); // the minimum occurs in two places, and with multi-threading, minpos // could be either one AlwaysAssert(sd.minpos.first == 0 || sd.minpos.first == 1, AipsError); AlwaysAssert(sd.minpos.second == 2, AipsError); AlwaysAssert(sd.npts == eNpts, AipsError); AlwaysAssert(sd.rms == sqrt(eSumSq/eSumWeights), AipsError); AlwaysAssert(near(sd.stddev, sqrt(eVar)), AipsError); AlwaysAssert(sd.sum == eSum, AipsError); AlwaysAssert(sd.sumsq == eSumSq, AipsError); AlwaysAssert(near(sd.variance, eVar), AipsError); AlwaysAssert( hfs.getStatisticIndex(StatisticsData::MAX) == std::pair(1, 1), AipsError ); AlwaysAssert( hfs.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 2), AipsError ); AlwaysAssert( hfs.getStatistic(StatisticsData::NPTS) == eNpts, AipsError ); AlwaysAssert(hfs.getStatistic( StatisticsData::RMS) == sqrt(eSumSq/eSumWeights), AipsError ); } { // weights, ranges, and masks // 5, 2, 6, 10, 7, -1 // 15, 11, 6, 20, -3, 14 // 2, 6, 10, 7, -1 // 11, 6, 20, -3, 14 // 6, 10, 7, -1 // 11, 6, 14 // 6, 10, -1 // 11, 14 // 6, 10 // 11 HingesFencesStatistics::const_iterator, vector::const_iterator> hfs(0); vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; // *6 = 18 w0[3] = 4; // *10 = 40 w0[4] = 5; w0[5] = 1; w0[6] = 0; w0[7] = 2; // *11 = 22 w0[8] = 3; w0[9] = 2; w0[10] = 1; w0[11] = 2; vector > r0(1); r0[0].first = 1; r0[0].second = 2; vector > r1(1); r1[0].first = 0; r1[0].second = 15; vector m0(v0.size(), True); m0[4] = False; m0[8] = False; hfs.setData(v0.begin(), w0.begin(), m0.begin(), v0.size()/2, r0, False); hfs.addData( v0.begin() + v0.size()/2, w0.begin() + w0.size()/2, m0.begin() + m0.size()/2, v0.size() - v0.size()/2, r1, True ); StatsData sd = hfs.getStatistics(); Double eSum = 80; Double eSumWeights = 9; Double eNpts = 3; Double eSumSq = 750; Double eVar = (eSumSq - eSum*eSum/eSumWeights)/(eSumWeights - 1); AlwaysAssert(sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(*sd.max == 11, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 1, AipsError); AlwaysAssert(near(sd.mean, eSum/eSumWeights), AipsError); AlwaysAssert(*sd.min == 6, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 2, AipsError); AlwaysAssert(sd.npts == eNpts, AipsError); AlwaysAssert(sd.rms == sqrt(eSumSq/eSumWeights), AipsError); AlwaysAssert(near(sd.stddev, sqrt(eVar)), AipsError); AlwaysAssert(sd.sum == eSum, AipsError); AlwaysAssert(sd.sumsq == eSumSq, AipsError); AlwaysAssert(near(sd.variance, eVar), AipsError); AlwaysAssert( hfs.getStatisticIndex(StatisticsData::MAX) == std::pair(1, 1), AipsError ); AlwaysAssert( hfs.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 2), AipsError ); AlwaysAssert( hfs.getStatistic(StatisticsData::NPTS) == eNpts, AipsError ); AlwaysAssert(hfs.getStatistic( StatisticsData::RMS) == sqrt(eSumSq/eSumWeights), AipsError ); } { // integer weights, ranges, and masks // 5, 2, 6, 10, 7, -1 // 15, 11, 6, 20, -3, 14 // 2, 6, 10, 7, -1 // 11, 6, 20, -3, 14 // 6, 10, 7, -1 // 11, 6, 14 // 6, 10, -1 // 11, 14 // 6, 10 // 11 HingesFencesStatistics::const_iterator, vector::const_iterator, vector::const_iterator> hfs(0); vector w0(v0.size()); w0[0] = 0; w0[1] = 2; w0[2] = 3; // *6 = 18 w0[3] = 4; // *10 = 40 w0[4] = 5; w0[5] = 1; w0[6] = 0; w0[7] = 2; // *11 = 22 w0[8] = 3; w0[9] = 2; w0[10] = 1; w0[11] = 2; vector > r0(1); r0[0].first = 1; r0[0].second = 2; vector > r1(1); r1[0].first = 0; r1[0].second = 15; vector m0(v0.size(), True); m0[4] = False; m0[8] = False; hfs.setData(v0.begin(), w0.begin(), m0.begin(), v0.size()/2, r0, False); hfs.addData( v0.begin() + v0.size()/2, w0.begin() + w0.size()/2, m0.begin() + m0.size()/2, v0.size() - v0.size()/2, r1, True ); StatsData sd = hfs.getStatistics(); Double eSum = 80; Double eSumWeights = 9; Double eNpts = 3; Double eSumSq = 750; Double eVar = (eSumSq - eSum*eSum/eSumWeights)/(eSumWeights - 1); AlwaysAssert(sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(*sd.max == 11, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 1, AipsError); AlwaysAssert(near(sd.mean, eSum/eSumWeights), AipsError); AlwaysAssert(*sd.min == 6, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 2, AipsError); AlwaysAssert(sd.npts == eNpts, AipsError); AlwaysAssert(sd.rms == sqrt(eSumSq/eSumWeights), AipsError); AlwaysAssert(near(sd.stddev, sqrt(eVar)), AipsError); AlwaysAssert(sd.sum == eSum, AipsError); AlwaysAssert(sd.sumsq == eSumSq, AipsError); AlwaysAssert(near(sd.variance, eVar), AipsError); AlwaysAssert( hfs.getStatisticIndex(StatisticsData::MAX) == std::pair(1, 1), AipsError ); AlwaysAssert( hfs.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 2), AipsError ); AlwaysAssert( hfs.getStatistic(StatisticsData::NPTS) == eNpts, AipsError ); AlwaysAssert(hfs.getStatistic( StatisticsData::RMS) == sqrt(eSumSq/eSumWeights), AipsError ); } { // weights, masks // 5, 2, 6, 10, 7, -1 // 15, 11, 6, 20, -3, 14 // 2, 6, 10, 7, -1 // 11, 6, 20, -3, 14 // 2, 6, 10, -1 // 11, 20, -3, 14 // 2, 6, 10, -1 // 11 HingesFencesStatistics::const_iterator, vector::const_iterator> hfs(0); vector w0(v0.size()); w0[0] = 0; w0[1] = 2; // *2 = 4 w0[2] = 3; // *6 = 18 w0[3] = 4; // *10 = 40 w0[4] = 5; w0[5] = 1; // *-1 = -1 w0[6] = 0; w0[7] = 2; // *11 = 22 w0[8] = 3; w0[9] = 2; w0[10] = 1; w0[11] = 2; vector m0(v0.size(), True); m0[4] = False; m0[8] = False; hfs.setData(v0.begin(), w0.begin(), m0.begin(), v0.size()/2); hfs.addData( v0.begin() + v0.size()/2, w0.begin() + w0.size()/2, m0.begin() + m0.size()/2, v0.size() - v0.size()/2 ); StatsData sd = hfs.getStatistics(); Double eSum = 83; Double eSumWeights = 12; Double eNpts = 5; Double eSumSq = 759; Double eVar = (eSumSq - eSum*eSum/eSumWeights)/(eSumWeights - 1); AlwaysAssert(sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(*sd.max == 11, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 1, AipsError); AlwaysAssert(near(sd.mean, eSum/eSumWeights), AipsError); AlwaysAssert(*sd.min == -1, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 5, AipsError); AlwaysAssert(sd.npts == eNpts, AipsError); AlwaysAssert(sd.rms == sqrt(eSumSq/eSumWeights), AipsError); AlwaysAssert(near(sd.stddev, sqrt(eVar)), AipsError); AlwaysAssert(sd.sum == eSum, AipsError); AlwaysAssert(sd.sumsq == eSumSq, AipsError); AlwaysAssert(near(sd.variance, eVar), AipsError); AlwaysAssert( hfs.getStatisticIndex(StatisticsData::MAX) == std::pair(1, 1), AipsError ); AlwaysAssert( hfs.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 5), AipsError ); AlwaysAssert( hfs.getStatistic(StatisticsData::NPTS) == eNpts, AipsError ); AlwaysAssert(hfs.getStatistic( StatisticsData::RMS) == sqrt(eSumSq/eSumWeights), AipsError ); } { // integer weights, masks // 5, 2, 6, 10, 7, -1 // 15, 11, 6, 20, -3, 14 // 2, 6, 10, 7, -1 // 11, 6, 20, -3, 14 // 2, 6, 10, -1 // 11, 20, -3, 14 // 2, 6, 10, -1 // 11 HingesFencesStatistics::const_iterator, vector::const_iterator, vector::const_iterator> hfs(0); vector w0(v0.size()); w0[0] = 0; w0[1] = 2; // *2 = 4 w0[2] = 3; // *6 = 18 w0[3] = 4; // *10 = 40 w0[4] = 5; w0[5] = 1; // *-1 = -1 w0[6] = 0; w0[7] = 2; // *11 = 22 w0[8] = 3; w0[9] = 2; w0[10] = 1; w0[11] = 2; vector m0(v0.size(), True); m0[4] = False; m0[8] = False; hfs.setData(v0.begin(), w0.begin(), m0.begin(), v0.size()/2); hfs.addData( v0.begin() + v0.size()/2, w0.begin() + w0.size()/2, m0.begin() + m0.size()/2, v0.size() - v0.size()/2 ); StatsData sd = hfs.getStatistics(); Double eSum = 83; Double eSumWeights = 12; Double eNpts = 5; Double eSumSq = 759; Double eVar = (eSumSq - eSum*eSum/eSumWeights)/(eSumWeights - 1); AlwaysAssert(sd.masked, AipsError); AlwaysAssert(sd.weighted, AipsError); AlwaysAssert(*sd.max == 11, AipsError); AlwaysAssert(sd.maxpos.first == 1, AipsError); AlwaysAssert(sd.maxpos.second == 1, AipsError); AlwaysAssert(near(sd.mean, eSum/eSumWeights), AipsError); AlwaysAssert(*sd.min == -1, AipsError); AlwaysAssert(sd.minpos.first == 0, AipsError); AlwaysAssert(sd.minpos.second == 5, AipsError); AlwaysAssert(sd.npts == eNpts, AipsError); AlwaysAssert(sd.rms == sqrt(eSumSq/eSumWeights), AipsError); AlwaysAssert(near(sd.stddev, sqrt(eVar)), AipsError); AlwaysAssert(sd.sum == eSum, AipsError); AlwaysAssert(sd.sumsq == eSumSq, AipsError); AlwaysAssert(near(sd.variance, eVar), AipsError); AlwaysAssert( hfs.getStatisticIndex(StatisticsData::MAX) == std::pair(1, 1), AipsError ); AlwaysAssert( hfs.getStatisticIndex(StatisticsData::MIN) == std::pair(0, 5), AipsError ); AlwaysAssert( hfs.getStatistic(StatisticsData::NPTS) == eNpts, AipsError ); AlwaysAssert(hfs.getStatistic( StatisticsData::RMS) == sqrt(eSumSq/eSumWeights), AipsError ); } { // getMinMax(), two datasets // 5, 2, 6, 10, 7, -1 // 15, 11, 6, 20, -3, 14 // 5, 2, 6, 10, 7 // 11, 6 HingesFencesStatistics::const_iterator, vector::const_iterator> hfs(0); hfs.setData(v0.begin(), v0.size()/2); hfs.addData(v0.begin() + v0.size()/2, v0.size() - v0.size()/2); Double mymin, mymax; hfs.getMinMax(mymin, mymax); AlwaysAssert(mymin == 2, AipsError); AlwaysAssert(mymax == 11, AipsError); } { // general quantile exceptions HingesFencesStatistics::const_iterator, vector::const_iterator> hfs(0); hfs.setData(v0.begin(), v0.size()/2); hfs.addData(v0.begin() + v0.size()/2, v0.size() - v0.size()/2); Bool thrown = False; try { hfs.getQuantile(0); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); thrown = False; try { hfs.getQuantile(1); } catch (const std::exception& x) { thrown = True; } AlwaysAssert(thrown, AipsError); } { // getQuantile(), no weights, no mask, no ranges // 5, 2, 6, 10, 7 // 11, 6 // 2, 5, 6, 6, 7, 10, 11 HingesFencesStatistics::const_iterator, vector::const_iterator> hfs(0); hfs.setData(v0.begin(), v0.size()/2); hfs.addData(v0.begin() + v0.size()/2, v0.size() - v0.size()/2); Double q = hfs.getQuantile(0.1); AlwaysAssert(q == 2, AipsError); q = hfs.getQuantile(0.2); AlwaysAssert(q == 5, AipsError); q = hfs.getQuantile(0.3); AlwaysAssert(q == 6, AipsError); q = hfs.getQuantile(0.4); AlwaysAssert(q == 6, AipsError); q = hfs.getQuantile(0.5); AlwaysAssert(q == 6, AipsError); q = hfs.getQuantile(0.6); AlwaysAssert(q == 7, AipsError); q = hfs.getQuantile(0.7); AlwaysAssert(q == 7, AipsError); q = hfs.getQuantile(0.8); AlwaysAssert(q == 10, AipsError); q = hfs.getQuantile(0.9); AlwaysAssert(q == 11, AipsError); std::set quantiles; quantiles.insert(0.1); quantiles.insert(0.2); quantiles.insert(0.3); quantiles.insert(0.4); quantiles.insert(0.5); quantiles.insert(0.6); quantiles.insert(0.7); quantiles.insert(0.8); quantiles.insert(0.9); std::map qs = hfs.getQuantiles(quantiles); AlwaysAssert(qs[0.1] == 2, AipsError); AlwaysAssert(qs[0.2] == 5, AipsError); AlwaysAssert(qs[0.3] == 6, AipsError); AlwaysAssert(qs[0.4] == 6, AipsError); AlwaysAssert(qs[0.5] == 6, AipsError); AlwaysAssert(qs[0.6] == 7, AipsError); AlwaysAssert(qs[0.7] == 7, AipsError); AlwaysAssert(qs[0.8] == 10, AipsError); AlwaysAssert(qs[0.9] == 11, AipsError); } { // getMedianAbsDevMed() // 5, 2, 6, 10, 7 // 11, 6 // 2, 5, 6, 6, 7, 10, 11 // 4, 1, 0, 0, 1, 4, 5 HingesFencesStatistics::const_iterator, vector::const_iterator> hfs(0); hfs.setData(v0.begin(), v0.size()/2); hfs.addData(v0.begin() + v0.size()/2, v0.size() - v0.size()/2); Double medabsdevmed = hfs.getMedianAbsDevMed(); AlwaysAssert(medabsdevmed == 1, AipsError); } { // getMedian() with binning, no ranges, weights, or mask HingesFencesStatistics::const_iterator, vector::const_iterator> hfs(0); hfs.setData(bigData.begin(), bigData.size()); // enforce a small internal array size so binning algorithm is used Double median = hfs.getMedian(NULL, NULL, NULL, 100); AlwaysAssert(median == -1, AipsError); } { // getMedianAbsDevMed() with binning, no ranges, weights, or mask HingesFencesStatistics::const_iterator, vector::const_iterator> hfs(0); hfs.setData(bigData.begin(), bigData.size()); // enforce a small internal array size so binning algorithm is used Double medabsdevmed = hfs.getMedianAbsDevMed(NULL, NULL, NULL, 100); AlwaysAssert(medabsdevmed == 499295.0, AipsError); } { // large array with all the same values, getMedianAndQuartile() vector big(100000, 0); HingesFencesStatistics::const_iterator, vector::const_iterator> hfs(0); hfs.addData(big.begin(), big.size()); std::set quantiles; quantiles.insert(0.25); quantiles.insert(0.75); std::shared_ptr npts; std::shared_ptr mymin, mymax; std::map quantileToValue; Double median = hfs.getMedianAndQuantiles( quantileToValue, quantiles, npts, mymin, mymax, 50000 ); AlwaysAssert(median == 0, AipsError); AlwaysAssert(quantileToValue[0.25] == 0, AipsError); AlwaysAssert(quantileToValue[0.75] == 0, AipsError); } } catch (const std::exception& x) { cout << x.what() << endl; return 1; } return 0; } casacore-3.7.1/scimath/StatsFramework/test/tStatisticsAlgorithmFactory.cc000066400000000000000000000112511476623553700267330ustar00rootroot00000000000000//# Copyright (C) 1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include #include #include int main() { try { StatisticsAlgorithmFactory saf; AlwaysAssert( saf.createStatsAlgorithm()->algorithm() == StatisticsData::CLASSICAL, AipsError ); saf.configureChauvenet(); AlwaysAssert( saf.createStatsAlgorithm()->algorithm() == StatisticsData::CHAUVENETCRITERION, AipsError ); saf.configureFitToHalf(); AlwaysAssert( saf.createStatsAlgorithm()->algorithm() == StatisticsData::FITTOHALF, AipsError ); saf.configureHingesFences(0.6); AlwaysAssert( saf.createStatsAlgorithm()->algorithm() == StatisticsData::HINGESFENCES, AipsError ); StatisticsAlgorithmFactory saf2; Record r = saf2.toRecord(); saf = StatisticsAlgorithmFactory::fromRecord(r); AlwaysAssert( saf.createStatsAlgorithm()->algorithm() == StatisticsData::CLASSICAL, AipsError ); Double zscore = 4.5; Int maxIter = 20; saf2.configureChauvenet(zscore, maxIter); r = saf2.toRecord(); saf = StatisticsAlgorithmFactory::fromRecord(r); AlwaysAssert( saf.createStatsAlgorithm()->algorithm() == StatisticsData::CHAUVENETCRITERION, AipsError ); StatisticsAlgorithmFactoryData::ChauvenetData cd = saf.chauvenetData(); AlwaysAssert( cd.zScore == zscore, AipsError ); AlwaysAssert( cd.maxIter == maxIter, AipsError ); FitToHalfStatisticsData::CENTER center = FitToHalfStatisticsData::CVALUE; FitToHalfStatisticsData::USE_DATA side = FitToHalfStatisticsData::GE_CENTER; Double centerValue = 5.5; saf2.configureFitToHalf(center, side, centerValue); r = saf2.toRecord(); saf = StatisticsAlgorithmFactory::fromRecord(r); AlwaysAssert( saf.createStatsAlgorithm()->algorithm() == StatisticsData::FITTOHALF, AipsError ); StatisticsAlgorithmFactoryData::FitToHalfData fd = saf.fitToHalfData(); AlwaysAssert( fd.center == center, AipsError ); AlwaysAssert( fd.centerValue == centerValue, AipsError ); AlwaysAssert( fd.side == side, AipsError ); Double hf = 45.2; saf2.configureHingesFences(hf); r = saf2.toRecord(); saf = StatisticsAlgorithmFactory::fromRecord(r); AlwaysAssert( saf.createStatsAlgorithm()->algorithm() == StatisticsData::HINGESFENCES, AipsError ); AlwaysAssert(saf.hingesFencesFactor() == hf, AipsError); maxIter = 22; Double c = 15.2; saf2.configureBiweight(maxIter, c); r = saf2.toRecord(); saf = StatisticsAlgorithmFactory::fromRecord(r); AlwaysAssert( saf.createStatsAlgorithm()->algorithm() == StatisticsData::BIWEIGHT, AipsError ); StatisticsAlgorithmFactoryData::BiweightData bd = saf.biweightData(); AlwaysAssert(bd.maxIter == maxIter, AipsError); AlwaysAssert(bd.c == c, AipsError); } catch (const std::exception& x) { cout << x.what() << endl; return 1; } return 0; } casacore-3.7.1/scimath/StatsFramework/test/tStatisticsTypes.cc000066400000000000000000000117531476623553700245700ustar00rootroot00000000000000//# tStatisticsTypes.cc: Test program for class StatisticsTypes //# Copyright (C) 1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include #include #include int main() { // Unit tests for toRecord(const StatsData& stats) from // StatisticsTypes. try { struct StatsData stats; stats.masked = True; stats.max.reset (new Double(27.3)); stats.maxpos = std::make_pair(2, 55); stats.mean = 22.1; stats.median.reset (new Double(22.8)); stats.medAbsDevMed.reset (new Double(1.3)); stats.min.reset (new Double(18.4)); stats.minpos = std::make_pair(1, 2); stats.npts = 111.0; stats.nvariance = 249.75; stats.rms = 22.15; stats.stddev = 1.5; stats.sum = 2453.1; stats.sumsq = 54463.26; stats.sumweights = 105.8; stats.variance = 2.25; stats.weighted = True; // The following four tests should be done in the given order, as the // sequence of tests incrementally removes some values from the "stats" // structure to test the conversion of optional fields (or lack // thereof). { // Test conversion of fully defined structure. (Note that some // fields in StatsData are never converted by toRecord, and so are // not in this test.) Record rec = toRecord(stats); AlwaysAssert( rec.asBool("isMasked") == stats.masked, AipsError); AlwaysAssert( rec.asBool("isWeighted") == stats.weighted, AipsError); AlwaysAssert( rec.asInt64("maxDatasetIndex") == stats.maxpos.first, AipsError); AlwaysAssert( rec.asInt64("maxIndex") == stats.maxpos.second, AipsError); AlwaysAssert( rec.asInt64("minDatasetIndex") == stats.minpos.first, AipsError); AlwaysAssert( rec.asInt64("minIndex") == stats.minpos.second, AipsError); AlwaysAssert( rec.asDouble(StatisticsData::toString(StatisticsData::SUMWEIGHTS)) == stats.sumweights, AipsError); AlwaysAssert( rec.asDouble(StatisticsData::toString(StatisticsData::MEAN)) == stats.mean, AipsError); AlwaysAssert( rec.asDouble(StatisticsData::toString(StatisticsData::NPTS)) == stats.npts, AipsError); AlwaysAssert( rec.asDouble(StatisticsData::toString(StatisticsData::RMS)) == stats.rms, AipsError); AlwaysAssert( rec.asDouble(StatisticsData::toString(StatisticsData::STDDEV)) == stats.stddev, AipsError); AlwaysAssert( rec.asDouble(StatisticsData::toString(StatisticsData::SUM)) == stats.sum, AipsError); AlwaysAssert( rec.asDouble(StatisticsData::toString(StatisticsData::SUMSQ)) == stats.sumsq, AipsError); AlwaysAssert( rec.asDouble(StatisticsData::toString(StatisticsData::VARIANCE)) == stats.variance, AipsError); AlwaysAssert( rec.asDouble(StatisticsData::toString(StatisticsData::MAX)) == *stats.max, AipsError); AlwaysAssert( rec.asDouble(StatisticsData::toString(StatisticsData::MIN)) == *stats.min, AipsError); } { // "sumweights" should be absent from output record when "weighted" // flag is False. stats.weighted = False; Record rec = toRecord(stats); AlwaysAssert( rec.isDefined(StatisticsData::toString(StatisticsData::SUMWEIGHTS)), AipsError); } { // Index of maximum value should be absent from output record when // "max" value is missing. stats.max = nullptr; Record rec = toRecord(stats); AlwaysAssert(!rec.isDefined("maxDatasetIndex"), AipsError); AlwaysAssert(!rec.isDefined("maxIndex"), AipsError); } { // Index of minimum value should be absent from output record when // "min" value is missing. stats.min = nullptr; Record rec = toRecord(stats); AlwaysAssert(!rec.isDefined("minDatasetIndex"), AipsError); AlwaysAssert(!rec.isDefined("minIndex"), AipsError); } } catch (const std::exception& x) { cout << x.what() << endl; return 1; } return 0; } casacore-3.7.1/scimath/StatsFramework/test/tStatisticsUtilities.cc000066400000000000000000000260661476623553700254420ustar00rootroot00000000000000//# Copyright (C) 1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include #include #include #include #include #include #include #include #define COMMA , int main() { try { vector v(5); v[0] = 1.5; v[1] = 1; v[2] = 2; v[3] = 3; v[4] = 2.5; Double npts = 0; Double sum = 0; Double mean = 0; for (uInt i=0; i<5; i++) { StatisticsUtilities::accumulate ( npts, sum, mean, v[i] ); } AlwaysAssert(npts == 5, AipsError); AlwaysAssert(sum == 10, AipsError); AlwaysAssert(mean == 2, AipsError); npts = 0; sum = 0; mean = 0; Double nvariance = 0; Double sumsq = 0; for (uInt i=0; i<5; i++) { StatisticsUtilities::accumulate ( npts, sum, mean, nvariance, sumsq, v[i] ); } AlwaysAssert(npts == 5, AipsError); AlwaysAssert(sum == 10, AipsError); AlwaysAssert(mean == 2, AipsError); AlwaysAssert(nvariance == 2.5, AipsError); AlwaysAssert(sumsq == 22.5, AipsError); npts = 0; sum = 0; mean = 0; nvariance = 0; sumsq = 0; Double datamin = 0; Double datamax = 0; uInt minpos = 0; uInt maxpos = 0; for (uInt i=0; i<5; i++) { StatisticsUtilities::accumulate ( npts, sum, mean, nvariance, sumsq, datamin, datamax, minpos, maxpos, v[i], i ); } AlwaysAssert(npts == 5, AipsError); AlwaysAssert(sum == 10, AipsError); AlwaysAssert(mean == 2, AipsError); AlwaysAssert(nvariance == 2.5, AipsError); AlwaysAssert(sumsq == 22.5, AipsError); AlwaysAssert(datamin == 1, AipsError); AlwaysAssert(datamax == 3, AipsError); AlwaysAssert(minpos == 1, AipsError); AlwaysAssert(maxpos == 3, AipsError); for (uInt i=0; i<5; i++) { StatisticsUtilities::doMax( datamax, maxpos, i==0, v[i], i ); StatisticsUtilities::doMin( datamin, minpos, i==0, v[i], i ); } AlwaysAssert(datamin == 1, AipsError); AlwaysAssert(datamax == 3, AipsError); AlwaysAssert(minpos == 1, AipsError); AlwaysAssert(maxpos == 3, AipsError); vector w(5); w[0] = 3; w[1] = 2; w[2] = 1; w[3] = 2; w[4] = 1; npts = 0; Double sumweights = 0; Double wsum = 0; Double wmean = 0; for (uInt i=0; i<5; i++) { StatisticsUtilities::waccumulate ( npts, sumweights, wsum, wmean, v[i], w[i] ); } AlwaysAssert(npts == 5, AipsError); AlwaysAssert(sumweights == 9, AipsError); AlwaysAssert(wsum == 17, AipsError); AlwaysAssert(near(wmean, 17.0/9.0), AipsError); npts = 0; sumweights = 0; wsum = 0; wmean = 0; Double wsumsq = 0; Double wnvariance = 0; for (uInt i=0; i<5; i++) { StatisticsUtilities::waccumulate ( npts, sumweights, wsum, wmean, wnvariance, wsumsq, v[i], w[i] ); } AlwaysAssert(npts == 5, AipsError); AlwaysAssert(sumweights == 9, AipsError); AlwaysAssert(wsum == 17, AipsError); AlwaysAssert(near(wmean, 17.0/9.0), AipsError); AlwaysAssert(wsumsq == 37, AipsError); AlwaysAssert(near(wnvariance, wsumsq - sumweights*wmean*wmean) , AipsError); vector::const_iterator vbegin = v.begin(); vector::const_iterator viter = vbegin; vector::const_iterator vend = v.end(); npts = 0; nvariance = 0; sumsq = 0; Double center = 3; while (viter != vend) { StatisticsUtilities::accumulateSym( npts, nvariance, sumsq, *viter, center ); ++viter; } AlwaysAssert(npts == 10, AipsError); AlwaysAssert(sumsq == 105, AipsError); AlwaysAssert(nvariance == 15, AipsError); npts = 0; nvariance = 0; sumsq = 0; sumweights = 0; center = 3; vector::const_iterator wbegin = w.begin(); vector::const_iterator witer = wbegin; viter = vbegin; while (viter != vend) { StatisticsUtilities::waccumulateSym( npts, sumweights, nvariance, sumsq, *viter, *witer, center ); ++viter; ++witer; } AlwaysAssert(npts == 10, AipsError); AlwaysAssert(sumweights == 18, AipsError); AlwaysAssert(nvariance == 32, AipsError); AlwaysAssert(sumsq == 194, AipsError); npts = 0; nvariance = 0; sumsq = 0; center = 3; uInt count = 0; viter = vbegin; while (viter != vend) { StatisticsUtilities::accumulateSym( npts, nvariance, sumsq, datamin, datamax, minpos, maxpos, *viter, count, center ); ++viter; ++count; } AlwaysAssert(npts == 10, AipsError); AlwaysAssert(sumsq == 105, AipsError); AlwaysAssert(nvariance == 15, AipsError); AlwaysAssert(datamin == 1, AipsError); AlwaysAssert(datamax == 3, AipsError); AlwaysAssert(minpos == 1, AipsError); AlwaysAssert(maxpos == 3, AipsError); npts = 0; nvariance = 0; sumsq = 0; sumweights = 0; center = 3; viter = vbegin; witer = wbegin; count = 0; while (viter != vend) { StatisticsUtilities::waccumulateSym( npts, sumweights, nvariance, sumsq, datamin, datamax, minpos, maxpos, *viter, *witer, count, center ); ++viter; ++witer; ++count; } AlwaysAssert(npts == 10, AipsError); AlwaysAssert(sumweights == 18, AipsError); AlwaysAssert(nvariance == 32, AipsError); AlwaysAssert(sumsq == 194, AipsError); AlwaysAssert(datamin == 1, AipsError); AlwaysAssert(datamax == 3, AipsError); AlwaysAssert(minpos == 1, AipsError); AlwaysAssert(maxpos == 3, AipsError); { cout << "Test combine()" << endl; Double d[] = { 0.6, 2.7, 9.6, 5.1, 8.2, 2.3, 4.5, -5.6, 8.7,-3.2, -0.5, 3.2 }; ClassicalStatistics cs; cs.addData(d, 12); StatsData expec = cs.getStatistics(); ClassicalStatistics cs1; cs1.addData(d, 5); StatsData sd1 = cs1.getStatistics(); ClassicalStatistics cs2; cs2.addData(d+5, 7); StatsData sd2 = cs2.getStatistics(); sd2.maxpos.first = 1; sd2.minpos.first = 1; vector > vsd(2); vsd[0] = sd1; vsd[1] = sd2; StatsData got = StatisticsUtilities::combine(vsd); AlwaysAssert(got.npts == expec.npts, AipsError); AlwaysAssert(near(got.mean, expec.mean), AipsError); AlwaysAssert(got.rms == expec.rms, AipsError); AlwaysAssert(near(got.stddev, expec.stddev), AipsError); AlwaysAssert(near(got.sum, expec.sum), AipsError); AlwaysAssert(near(got.sumsq, expec.sumsq), AipsError); AlwaysAssert(near(got.variance, expec.variance), AipsError); AlwaysAssert(*got.max == *expec.max, AipsError); AlwaysAssert(*got.min == *expec.min, AipsError); AlwaysAssert(got.maxpos == std::pair(0, 2), AipsError); AlwaysAssert(got.minpos == std::pair(1, 2), AipsError); ClassicalStatistics cs10; cs10.addData(d, 3); StatsData sd10 = cs10.getStatistics(); ClassicalStatistics cs11; cs11.addData(d+3, 4); StatsData sd11 = cs11.getStatistics(); sd11.maxpos.first = 1; sd11.minpos.first = 1; ClassicalStatistics cs12; cs12.addData(d+7, 5); StatsData sd12 = cs12.getStatistics(); sd12.maxpos.first = 2; sd12.minpos.first = 2; vector > vsd1(3); vsd1[0] = sd10; vsd1[1] = sd11; vsd1[2] = sd12; got = StatisticsUtilities::combine(vsd1); AlwaysAssert(got.npts == expec.npts, AipsError); AlwaysAssert(near(got.mean, expec.mean), AipsError); AlwaysAssert(got.rms == expec.rms, AipsError); AlwaysAssert(near(got.stddev, expec.stddev), AipsError); AlwaysAssert(near(got.sum, expec.sum), AipsError); AlwaysAssert(got.sumsq == expec.sumsq, AipsError); AlwaysAssert(near(got.variance, expec.variance), AipsError); AlwaysAssert(*got.max == *expec.max, AipsError); AlwaysAssert(*got.min == *expec.min, AipsError); AlwaysAssert(got.maxpos == std::pair(0, 2), AipsError); AlwaysAssert(got.minpos == std::pair(2, 0), AipsError); } } catch (const std::exception& x) { cout << x.what() << endl; return 1; } return 0; } casacore-3.7.1/scimath/StatsFramework/test/tStatsHistogram.cc000066400000000000000000000033611476623553700243610ustar00rootroot00000000000000//# Copyright (C) 1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include #include #include #include int main() { try { // CAS-11828 for very small binwidth StatsHistogram sh( -0.0014156261459125514795, -0.0014156261458961459827, 1937361 ); auto idx = sh.getIndex(-0.0014156261458992958069); AlwaysAssert(idx == 1565404, AipsError); } catch (const std::exception& x) { cout << x.what() << endl; return 1; } return 0; } casacore-3.7.1/scimath/StatsFramework/test/tZScoreCalculator.cc000066400000000000000000000034031476623553700246210ustar00rootroot00000000000000//# Copyright (C) 1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #include #include #include int main() { try { for (Double z=0; z<=7; z+=0.5) { cout << z << " " << ZScoreCalculator::zscoreToNpts(z) << endl; } uInt count = 0; uInt64 x = 10; while (count < 15) { cout << "log(npts) " << log10(x) << " zscore " << ZScoreCalculator::getMaxZScore(x) << endl; ++count; x *= 10; } } catch (const std::exception& x) { cout << x.what() << endl; return 1; } return 0; } casacore-3.7.1/scimath/scimath.dox000066400000000000000000000033711476623553700171240ustar00rootroot00000000000000//# scimath.dox: doxygen description of scimath package //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ namespace casacore { // \defgroup scimath scimath package (libcasa_scimath) // // The scimath package contains the basic mathematical modules //
          //
        • Functionals: // n-dimensional functions with automatic differentation. //
        • Fitting: // linear and non-linear fitting to parameters or Functionals. //
        • Mathematics: // FFT, convolution, 2-dim interpolation, random numbers. //
        } casacore-3.7.1/scimath_f/000077500000000000000000000000001476623553700152615ustar00rootroot00000000000000casacore-3.7.1/scimath_f/ADDGRD.f000066400000000000000000000131471476623553700163630ustar00rootroot00000000000000*----------------------------------------------------------------------- * ADDGRD: Add values with weights to an array. *----------------------------------------------------------------------- * * Copyright (C) 1995 * Associated Universities, Inc. Washington DC, USA. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, * MA 02139, USA. * * Correspondence concerning AIPS++ should be addressed as follows: * Internet email: casa-feedback@nrao.edu. * Postal address: AIPS++ Project Office * National Radio Astronomy Observatory * 520 Edgemont Road * Charlottesville, VA 22903-2475 USA * * $Id$ * *----------------------------------------------------------------------- * * ADDGRD contains a set of nearly identical subroutines that are * used by specific instances of the templated class GridTool * when gridding real data. The execution speed of GridTool is * greatly enhanced by these subroutines. Each specific type of * T in GridTool requires a separate version of ADDGRD (the * S type is not used by the real gridding versions of GridTool). * * The following subroutines are found here: * ADGRDF : for Arrays of Float. * ADGRDD : for Arrays of Double * It should be trivial to clone these routines for any other type. * * The following comments apply to all subroutines found here: * * The general form of the subroutine call is (x is replaced by * a character indicating the type: F for Float, D for Double, etc.). * * SUBROUTINE ADGRDx(NVALS, START, INCR, CELLWT, DATA, GRID, WEIGHT) * Given: * NVALS I The number of points to be gridded. * START I The starting location in GRID and WEIGHT. * INCR I The increment to use between successive * locations in GRID and WEIGHT. * CELLWT x A global weight. Applied to all data values. * DATA(2*NVALS) * x Data array (includes data and associated * weights, see note 1 below). * * Given and returned: * GRID(START+INCR*(NVALS-1)) * x The array holding the gridded data. * WEIGHT(START+INCR*(NVALS-1)) * x The array holding the sum of the weights. * * Notes: * 1) DATA consists of 2*NVALS values with the values to be added * to the grid at the odd numbered locations of DATA (1,3,5...) * and the associated weights at the even numbered locations of * DATA (2,4,6...). * * 2) The total weight given to each data point is the global * weight, CELLWT, multiplied by the weight for that data point * (from the associated even number of DATA). A weight less * than zero implies that the associated data value is NOT to be * added to GRID (this is consistent with GridTool usage). * * 3) Each data value is multipled by the total weight and added * to the existing value of GRID. The total weight is added * to the existing value of WEIGHT. The addition of values to * GRID and WEIGHT starts at location START and continues at * each INCR (increment) location after START until all NVALS * values have been used. * * 4) Keep in mind that the first element in a fortran array is * element number 1. *----------------------------------------------------------------------- SUBROUTINE ADGRDF(NVALS, START, INCR, CELLWT, DATA, GRID, WEIGHT) * Add values to an array of Float INTEGER INCR, J, K, NVALS, OFFSET, START REAL CELLWT, DATA(2*NVALS), GRID(START+INCR*(NVALS-1)), TOTWT, * WEIGHT(START+INCR*(NVALS-1)) *----------------------------------------------------------------------- OFFSET = 0 DO 10 J = 2, 2*NVALS, 2 IF (DATA(J).GE.0.0) THEN TOTWT = CELLWT * DATA(J) K = START + OFFSET GRID(K) = GRID(K) + DATA(J-1)*TOTWT WEIGHT(K) = WEIGHT(K) + TOTWT END IF OFFSET = OFFSET + INCR 10 CONTINUE RETURN END *----------------------------------------------------------------------- SUBROUTINE ADGRDD(NVALS, START, INCR, CELLWT, DATA, GRID, WEIGHT) * Add values to an array of Double INTEGER INCR, J, K, NVALS, OFFSET, START DOUBLE PRECISION CELLWT, DATA(2*NVALS), * GRID(START+INCR*(NVALS-1)), TOTWT, * WEIGHT(START+INCR*(NVALS-1)) *----------------------------------------------------------------------- OFFSET = 0 DO 10 J = 2, 2*NVALS, 2 IF (DATA(J).GE.0.0) THEN TOTWT = CELLWT * DATA(J) K = START + OFFSET GRID(K) = GRID(K) + DATA(J-1)*TOTWT WEIGHT(K) = WEIGHT(K) + TOTWT END IF OFFSET = OFFSET + INCR 10 CONTINUE RETURN END casacore-3.7.1/scimath_f/CMakeLists.txt000066400000000000000000000022701476623553700200220ustar00rootroot00000000000000# # CASA scimath_f # set (buildfiles abshis.f absmax.f ADDGRD.f atmroutines.f convolvegridder.f dqags.f fgridft.f fgridsd.f fmosft.f fwproj.f getbig.f grd2d.f grd2dwts.f grdde2d.f grdgauss.f grdjinc1.f grdsf.f hclean.f lawson.f maxabs.f parametricsolver.f phasol.f subcom.f vvroutines.f ) if (BUILD_FFTPACK_DEPRECATED) list (APPEND buildfiles dfftpak.f fftpak.f ) # FFTPACK is getting so old that it needs deprecated behaviour of the compiler. # This is why BUILD_FFTPACK_DEPRECATED is also deprecated in casacore. include(CheckFortranCompilerFlag) check_fortran_compiler_flag(--allow-argument-mismatch FORTRAN_HAS_ALLOW_ARGUMENT_MISMATCH) if (FORTRAN_HAS_ALLOW_ARGUMENT_MISMATCH) set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} --allow-argument-mismatch") endif() endif () add_library (casa_scimath_f ${buildfiles}) target_link_libraries (casa_scimath_f casa_tables ${LAPACK_LIBRARIES} ${BLAS_LIBRARIES} ${CASACORE_ARCH_LIBS}) install ( TARGETS casa_scimath_f LIBRARY DESTINATION lib${LIB_SUFFIX} ARCHIVE DESTINATION lib${LIB_SUFFIX} LIBRARY PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) casacore-3.7.1/scimath_f/abshis.f000066400000000000000000000315571476623553700167140ustar00rootroot00000000000000*----------------------------------------------------------------------- * ABSHIS: calculate an histogram of absolute values. *----------------------------------------------------------------------- * * Copyright (C) 1997,2000 * Associated Universities, Inc. Washington DC, USA. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, * MA 02139, USA. * * Correspondence concerning AIPS++ should be addressed as follows: * Internet email: casa-feedback@nrao.edu. * Postal address: AIPS++ Project Office * National Radio Astronomy Observatory * 520 Edgemont Road * Charlottesville, VA 22903-2475 USA * * $Id$ * *----------------------------------------------------------------------- * * ABSHIS contains a set of functions which produce a histogram of * the absolute values in a data array. It is always assumed that the * array is one dimensional, with a specified number of elements. * Standard AIPS++ Arrays can, unless they have a step increment * defined, be treated as one dimensional for the purposes of finding * the minimum and maximum value. * * These functions also return the limits of the returned histogram, * ie. minimum and maximum absolute values in the data array. * * These functions where written for the ClarkCleanModel class and * hence include the ability to find the minimum and maximum of a * two dimensional array where the second axis is the polarization * axis. This requires that the slowest moving axis is * the polarization axis. * The following subroutines are found here: * ABSHISF : for Arrays of REAL(float) numbers * ABSHIS2F: for Arrays of REAL(float) numbers with * two polarizations (I & V) * ABSHIS4F: for Arrays of REAL(float) numbers with * four polarizations (I, Q, U, V) * It should be trivial to clone these routines for any other data * types, in particular double precision. * * The following comments apply to all subroutines found here: * * The general form of the subroutine call is (x is replaced by * a character indicating the type: F for Float, D for Double, etc.), * and p is a number indicating the number of polarizations that are * required. (if p=1 it is dropped entirely) * * SUBROUTINE ABSHISpx(HIST, MINVAL, MAXVAL, NBINS, ARR, NPIX) * Given: * NPIX I The number of pixels in the data array (ARR) * NBINS I The number of bins in the histogram (HIST) * ARR(NPIX,p) * x Data array. Note that if p.NE.1 then NPIX is * NOT the total size of the ARR array * HIST(NBINS) * I An array to hold the histogram. * * return: * MINVAL x The minimum absolute value in the Array * MAXVAL x The maximum absolute value in the Array * HIST(NBINS) * I The resultant histogram produced from the data. * * Notes: * 1) The histogram needs to be initialised as it will NOT be * initialised to zero in these routines. * * 2) If the data array has all pixels with the same value (like * all zero) then the histogram will have all the pixels in the * topmost bin. * * 3) This routine calculates the minimum and maximum absolute * values and returns them also. * * 4) In the 2 polarization case it is assumed that the two * polarizations are I & V, and that I is in the first half of * the array, and V in the second half. * * 5) In the 4 polarization case it is assumed that the four * polarizations are I, Q, U & V, and that they are in that * order in the array * * 6) The minimum and maximum absolute values returned if p.NE.1 are a * function of all the polarizations at that pixel. For the 4 * polarization case this is the maximum eigenvalue * (=ABS(I+SQRT(Q*Q+U*U+V*V))), and for the * two polarisation case it is ABS(I + ABS(V)) *----------------------------------------------------------------------- SUBROUTINE ABSHISF(HIST, MINVAL, MAXVAL, NBINS, ARR, NPIX) * Calculate a histogram of the absolute values of an array of REAL * numbers. INTEGER NPIX, NBINS INTEGER HIST(0:NBINS-1) REAL MAXVAL, MINVAL, ARR(NPIX) INTEGER N, BIN REAL SAMPLE *----------------------------------------------------------------------- * * Find the minimum and maximum absolute values * MINVAL = ABS(ARR(1)) MAXVAL = MINVAL DO 10 N = 2, NPIX SAMPLE = ABS(ARR(N)) MAXVAL = MAX(MAXVAL, SAMPLE) MINVAL = MIN(MINVAL, SAMPLE) 10 CONTINUE * * Now create the histogram * IF (MINVAL.NE.MAXVAL) THEN SCALE = REAL(NBINS)/(MAXVAL-MINVAL) DO 20 N = 1, NPIX SAMPLE = ABS(ARR(N)) BIN = INT((SAMPLE-MINVAL)*SCALE) IF (BIN.EQ.NBINS) THEN HIST(NBINS-1) = HIST(NBINS-1) + 1 ELSE HIST(BIN) = HIST(BIN) + 1 END IF 20 CONTINUE ELSE HIST(NBINS-1) = NPIX END IF RETURN END *----------------------------------------------------------------------- SUBROUTINE ABSHIS2F(HIST, MINVAL, MAXVAL, NBINS, ARR, NPIX) * Calculate a histogram of the absolute values of an array of REAL * numbers, assuming the array has two polarizations (I & V) INTEGER NPIX, NBINS INTEGER HIST(0:NBINS-1) REAL MAXVAL, MINVAL, ARR(NPIX,2) INTEGER N, BIN REAL SAMPLE, I, V *----------------------------------------------------------------------- * * Find the minimum and maximum absolute values * I = ARR(1,1) V = ARR(1,2) MINVAL = MAX(ABS(I+V), ABS(I-V)) MAXVAL = MINVAL DO 10 N = 2, NPIX I = ARR(N,1) V = ARR(N,2) SAMPLE = MAX(ABS(I+V), ABS(I-V)) MAXVAL = MAX(MAXVAL, SAMPLE) MINVAL = MIN(MINVAL, SAMPLE) 10 CONTINUE * * Now create the histogram * IF (MINVAL.NE.MAXVAL) THEN SCALE = REAL(NBINS)/(MAXVAL-MINVAL) DO 20 N = 1, NPIX I = ARR(N,1) V = ARR(N,2) SAMPLE = MAX(ABS(I+V), ABS(I-V)) BIN = INT((SAMPLE-MINVAL)*SCALE) IF (BIN.EQ.NBINS) THEN HIST(NBINS-1) = HIST(NBINS-1) + 1 ELSE HIST(BIN) = HIST(BIN) + 1 END IF 20 CONTINUE ELSE HIST(NBINS-1) = NPIX END IF RETURN END *----------------------------------------------------------------------- SUBROUTINE ABSHIS4F(HIST, MINVAL, MAXVAL, NBINS, ARR, NPIX) * Calculate a histogram of the absolute values of an array of REAL * numbers, assuming the array has four polarizations (I, Q, U, V) INTEGER NPIX, NBINS INTEGER HIST(0:NBINS-1) REAL MAXVAL, MINVAL, ARR(NPIX,4) INTEGER N, BIN REAL SAMPLE, I, Q, U, V *----------------------------------------------------------------------- * * Find the minimum and maximum absolute values * I = ARR(1,1) Q = ARR(1,2) U = ARR(1,3) V = ARR(1,4) MINVAL = ABS(I+SQRT(Q*Q+U*U+V*V)) MAXVAL = MINVAL DO 10 N = 2, NPIX I = ARR(N,1) Q = ARR(N,2) U = ARR(N,3) V = ARR(N,4) SAMPLE = ABS(I+SQRT(Q*Q+U*U+V*V)) MAXVAL = MAX(MAXVAL, SAMPLE) MINVAL = MIN(MINVAL, SAMPLE) 10 CONTINUE * * Now create the histogram * IF (MINVAL.NE.MAXVAL) THEN SCALE = REAL(NBINS)/(MAXVAL-MINVAL) DO 20 N = 1, NPIX I = ARR(N,1) Q = ARR(N,2) U = ARR(N,3) V = ARR(N,4) SAMPLE = ABS(I+SQRT(Q*Q+U*U+V*V)) BIN = INT((SAMPLE-MINVAL)*SCALE) IF (BIN.EQ.NBINS) THEN HIST(NBINS-1) = HIST(NBINS-1) + 1 ELSE HIST(BIN) = HIST(BIN) + 1 END IF 20 CONTINUE ELSE HIST(NBINS-1) = NPIX END IF RETURN END *----------------------------------------------------------------------- SUBROUTINE ABSHIMF(HIST, MINVAL, MAXVAL, NBINS, ARR, MASK, NPIX) * Calculate a histogram of the absolute values of an array of REAL * numbers weighted by the mask. INTEGER NPIX, NBINS INTEGER HIST(0:NBINS-1) REAL MAXVAL, MINVAL, ARR(NPIX), MASK(NPIX) INTEGER N, BIN REAL SAMPLE *----------------------------------------------------------------------- * * Find the minimum and maximum absolute values * MINVAL = ABS(ARR(1)) * MASK(1) MAXVAL = MINVAL DO 10 N = 2, NPIX SAMPLE = ABS(ARR(N)) * MASK(N) MAXVAL = MAX(MAXVAL, SAMPLE) MINVAL = MIN(MINVAL, SAMPLE) 10 CONTINUE * * Now create the histogram * IF (MINVAL.NE.MAXVAL) THEN SCALE = REAL(NBINS)/(MAXVAL-MINVAL) DO 20 N = 1, NPIX SAMPLE = ABS(ARR(N)) * MASK(N) BIN = INT((SAMPLE-MINVAL)*SCALE) IF (BIN.EQ.NBINS) THEN HIST(NBINS-1) = HIST(NBINS-1) + 1 ELSE HIST(BIN) = HIST(BIN) + 1 END IF 20 CONTINUE ELSE HIST(NBINS-1) = NPIX END IF RETURN END *----------------------------------------------------------------------- SUBROUTINE ABSHIM2F(HIST, MINVAL, MAXVAL, NBINS, ARR, MASK, NPIX) * Calculate a histogram of the absolute values of an array of REAL * numbers, assuming the array has two polarizations (I & V) & * is weighted by a mask. INTEGER NPIX, NBINS INTEGER HIST(0:NBINS-1) REAL MAXVAL, MINVAL, ARR(NPIX,2), MASK(NPIX) INTEGER N, BIN REAL SAMPLE, I, V *----------------------------------------------------------------------- * * Find the minimum and maximum absolute values * I = ARR(1,1) V = ARR(1,2) MINVAL = ABS(I + ABS(V)) * MASK(1) MAXVAL = MINVAL DO 10 N = 2, NPIX I = ARR(N,1) V = ARR(N,2) SAMPLE = ABS(I + ABS(V)) * MASK(N) MAXVAL = MAX(MAXVAL, SAMPLE) MINVAL = MIN(MINVAL, SAMPLE) 10 CONTINUE * * Now create the histogram * IF (MINVAL.NE.MAXVAL) THEN SCALE = REAL(NBINS)/(MAXVAL-MINVAL) DO 20 N = 1, NPIX I = ARR(N,1) V = ARR(N,2) SAMPLE = ABS(I + ABS(V)) * MASK(N) BIN = INT((SAMPLE-MINVAL)*SCALE) IF (BIN.EQ.NBINS) THEN HIST(NBINS-1) = HIST(NBINS-1) + 1 ELSE HIST(BIN) = HIST(BIN) + 1 END IF 20 CONTINUE ELSE HIST(NBINS-1) = NPIX END IF RETURN END *----------------------------------------------------------------------- SUBROUTINE ABSHIM4F(HIST, MINVAL, MAXVAL, NBINS, ARR, MASK, NPIX) * Calculate a histogram of the absolute values of an array of REAL * numbers, assuming the array has four polarizations (I, Q, U, V) & * is weighted by a mask. INTEGER NPIX, NBINS INTEGER HIST(0:NBINS-1) REAL MAXVAL, MINVAL, ARR(NPIX,4), MASK(NPIX) INTEGER N, BIN REAL SAMPLE, I, Q, U, V *----------------------------------------------------------------------- * * Find the minimum and maximum absolute values * I = ARR(1,1) Q = ARR(1,2) U = ARR(1,3) V = ARR(1,4) MINVAL = ABS(I+SQRT(Q*Q+U*U+V*V)) * MASK(1) MAXVAL = MINVAL DO 10 N = 2, NPIX I = ARR(N,1) Q = ARR(N,2) U = ARR(N,3) V = ARR(N,4) SAMPLE = ABS(I+SQRT(Q*Q+U*U+V*V)) * MASK(N) MAXVAL = MAX(MAXVAL, SAMPLE) MINVAL = MIN(MINVAL, SAMPLE) 10 CONTINUE * * Now create the histogram * IF (MINVAL.NE.MAXVAL) THEN SCALE = REAL(NBINS)/(MAXVAL-MINVAL) DO 20 N = 1, NPIX I = ARR(N,1) Q = ARR(N,2) U = ARR(N,3) V = ARR(N,4) SAMPLE = ABS(I+SQRT(Q*Q+U*U+V*V)) * MASK(N) BIN = INT((SAMPLE-MINVAL)*SCALE) IF (BIN.EQ.NBINS) THEN HIST(NBINS-1) = HIST(NBINS-1) + 1 ELSE HIST(BIN) = HIST(BIN) + 1 END IF 20 CONTINUE ELSE HIST(NBINS-1) = NPIX END IF RETURN END casacore-3.7.1/scimath_f/absmax.f000066400000000000000000000170031476623553700167040ustar00rootroot00000000000000*----------------------------------------------------------------------- * ABSMAX: find the element with the largest absolute value *----------------------------------------------------------------------- * * Copyright (C) 1997,2000 * Associated Universities, Inc. Washington DC, USA. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, * MA 02139, USA. * * Correspondence concerning AIPS++ should be addressed as follows: * Internet email: casa-feedback@nrao.edu. * Postal address: AIPS++ Project Office * National Radio Astronomy Observatory * 520 Edgemont Road * Charlottesville, VA 22903-2475 USA * * $Id$ * *----------------------------------------------------------------------- * * ABSMAX contains a set of functions for finding the element with * the largest absolute value in a data array, and the position * (offset) of this element. It is always assumed that the array is * one dimensional, with a specified number of elements. Standard * AIPS++ Arrays can, unless they have a step increment, or are a * sub-array, be treated as one dimensional for the purposes of * finding this element. Global functions defined in IPosition.h can * be used to convert the position returned by these functions into * an IPosition for indexing into the multi-dimensional AIPS++ Array. * * These functions where written for the ClarkCleanModel class and * hence include the ability to search two dimensional arrays. Unlike * related function (MAXABS, HISABS, MINMAX, GETBIG), these functions * require that the polarization axis is the FIRST axis, and not the * last. * The following subroutines are found here: * ABSMAXF : for Arrays of REAL(float) numbers * ABSMAX2F: for Arrays of REAL(float) numbers with * two polarizations (I & V) * ABSMAX4F: for Arrays of REAL(float) numbers with * four polarizations (I, Q, U, V) * It should be trivial to clone these routines for any other data * types, in particular double precision. * * The following comments apply to all subroutines found here: * * The general form of the subroutine call is (x is replaced by * a character indicating the type: F for Float, D for Double, etc.), * and p is a number indicating the number of polarizations that are * required. (if p=1 it is dropped entirely) * * SUBROUTINE ABSMAXpx(MAXELEM, MAXVAL, MAXPOS, ARR, NPIX); * Given: * NPIX I The number of pixels in the Array * ARR(p,NPIX) * x Data array. Note that if p.NE.1 then NPIX is * NOT the total size of the ARR array * * return: * MAXELEM(p) * x The element with the maximum value. * MAXVAL x The absolute value of this element * MAXPOS I The position of the maximum element * * Notes: * 1) It is assumed that the data array contains at least ONE * element (ie. NPIX.GE.1) * * 2) The element returned will in be an array with with p * elements, but its absolute value will always be a REAL number * * 3) The returned positions are ZERO relative. ie. the first * pixel in the array is at position = 0. This is for * convenience for the C/C++ code that will be using these * functions. * * 4) In the 2 polarization case it is assumed that the two * polarizations are I & V, and that I is in the even * locations in the array, and V in the odd locations. * * 5) In the 4 polarization case it is assumed that the four * polarizations are I, Q, U & V, and that they are in that * order in the array * * 6) The maximum absolute values returned if p.NE.1 are a * function of all the polarizations at that pixel. For the 4 * polarization case this is the maximum eigenvalue * (=ABS(I+SQRT(Q*Q+U*U+V*V))), and for the two polarisation * case it is ABS(I + ABS(V)) *----------------------------------------------------------------------- SUBROUTINE ABSMAXF(MAXELEM, MAXVAL, MAXPOS, ARR, NPIX) * Return the array element with the maximum absolute value * (and its position) in an array of REAL numbers INTEGER NPIX, MAXPOS REAL MAXELEM, MAXVAL, ARR(NPIX) INTEGER N REAL SAMPLE, I *----------------------------------------------------------------------- I = ARR(1) MAXELEM = I MAXVAL = ABS(I) MAXPOS = 1 DO 10 N = 2, NPIX I = ARR(N) SAMPLE = ABS(I) IF (SAMPLE.GT.MAXVAL) THEN MAXVAL = SAMPLE MAXELEM = I MAXPOS = N END IF 10 CONTINUE MAXPOS = MAXPOS - 1 RETURN END *----------------------------------------------------------------------- SUBROUTINE ABSMAX2F(MAXELEM, MAXVAL, MAXPOS, ARR, NPIX) * Return the array element with the maximum absolute value * (and its position) in an array of REAL numbers with * 2-Polarizations (I,V) INTEGER NPIX, MAXPOS REAL MAXELEM(2), MAXVAL, ARR(2, NPIX) INTEGER N REAL SAMPLE, I, V *----------------------------------------------------------------------- I = ARR(1,1) V = ARR(2,1) MAXELEM(1) = I MAXELEM(2) = V MAXVAL = MAX(ABS(I+V), ABS(I-V)) MAXPOS = 1 DO 10 N = 2, NPIX I = ARR(1,N) V = ARR(2,N) SAMPLE = MAX(ABS(I+V), ABS(I-V)) IF (SAMPLE.GT.MAXVAL) THEN MAXVAL = SAMPLE MAXELEM(1) = I MAXELEM(2) = V MAXPOS = N END IF 10 CONTINUE MAXPOS = MAXPOS - 1 RETURN END *----------------------------------------------------------------------- SUBROUTINE ABSMAX4F(MAXELEM, MAXVAL, MAXPOS, ARR, NPIX) * Return the array element with the maximum absolute value * (and its position) in an array of REAL numbers with * 4-Polarizations (I,Q,U,V) INTEGER NPIX, MAXPOS REAL MAXELEM(4), MAXVAL, ARR(4, NPIX) INTEGER N REAL SAMPLE, I, Q, U, V *----------------------------------------------------------------------- I = ARR(1,1) Q = ARR(2,1) U = ARR(3,1) V = ARR(4,1) MAXELEM(1) = I MAXELEM(2) = Q MAXELEM(3) = U MAXELEM(4) = V MAXVAL = ABS(I+SQRT(Q*Q+U*U+V*V)) MAXPOS = 1 DO 10 N = 2, NPIX I = ARR(1,N) Q = ARR(2,N) U = ARR(3,N) V = ARR(4,N) SAMPLE = ABS(I+SQRT(Q*Q+U*U+V*V)) IF (SAMPLE.GT.MAXVAL) THEN MAXVAL = SAMPLE MAXELEM(1) = I MAXELEM(2) = Q MAXELEM(3) = U MAXELEM(4) = V MAXPOS = N END IF 10 CONTINUE MAXPOS = MAXPOS - 1 RETURN END casacore-3.7.1/scimath_f/atmroutines.f000066400000000000000000000731601476623553700200110ustar00rootroot00000000000000* Copyright (C) 1999,2001 * Associated Universities, Inc. Washington DC, USA. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, * MA 02139, USA. * * Correspondence concerning AIPS++ should be addressed as follows: * Internet email: aips2-request@@nrao.edu. * Postal address: AIPS++ Project Office * National Radio Astronomy Observatory * 520 Edgemont Road * Charlottesville, VA 22903-2475 USA * * Last processed by NICE on 05-Jul-2001 14:40:00 * Customized for : IEEE, LINUX, UNIX, MOTIF, F77 SUBROUTINE ASE45(P,T,D,HA) C J CERNICHARO C C ATMOSFERA U.S. 1962 MES DE ENERO 45 GRADOS DE LATITUD NORTE C HA ES LA ALTURA A LA QUE SE QUIEREN CALCULAR LA PRESION, C (MILIBARES),LA TEMPERATURA (K) Y LA DENSIDAD (GR/M**3) C HA DEBE ESTAR EN KM C C P === PRESION C T === TEMPERATURA C D === DENSIDAD C REAL P,T,D,HA REAL PR(91),TE(91),DEN(91) INTEGER I0,I1,I2,J REAL X1,X2,X3,Y1,Y2,Y3,A,B,C DATA TE/ $272.150,268.650,265.154,261.654,255.668,249.674,243.686,237.698, $231.710,225.728,219.746,219.159,218.661,218.163,217.665,217.167, $216.670,216.172,215.675,215.178,215.150,215.150,215.150,215.150, $215.150,215.150,215.150,215.150,215.852,216.644,217.437,218.230, $219.022,221.723,224.789,227.855,230.921,233.987,237.049,240.112, $243.175,246.235,249.294,252.354,255.414,258.470,261.527,264.580, $265.650,265.650,265.650,265.650,265.650,264.526,262.560,260.594, $258.628,256.664,254.698,252.736,250.772,248.810,246.848,244.886, $242.926,240.932,238.874,236.818,234.762,232.706,230.650,228.596, $226.543,224.491,222.439,220.387,218.336,216.286,214.236,212.187, $210.150,210.150,210.150,210.150,210.150,210.150,210.150,210.150, $210.150,210.150,210.150/ DATA PR/ $.101800E04,.897340E03,.789746E03,.693761E03,.608132E03,.531322E03, $.462749E03,.401636E03,.347333E03,.299257E03,.256837E03,.219907E03, $.188249E03,.161092E03,.137803E03,.117839E03,.100747E03,.861032E02, $.735727E02,.628431E02,.536674E02,.458314E02,.391458E02,.334355E02, $.285581E02,.243961E02,.208407E02,.178034E02,.152146E02,.130097E02, $.111307E02,.952848E01,.816277E01,.700062E01,.601741E01,.518290E01, $.447305E01,.386792E01,.335147E01,.290933E01,.253005E01,.220437E01, $.192389E01,.168188E01,.147270E01,.129175E01,.113478E01,.998513, $.879632,.774975,.682770,.601612,.530101,.467019,.411144,.361607, $.317729,.278938,.244604,.214337,.187596,.164042,.143293,.125033, $.108996,.949100E-01,.825474E-01,.717187E-01,.622342E-01, $.539367E-01,.466861E-01,.403641E-01,.348523E-01,.3006E-01, $.258872E-01,.223E-01,.191221E-01,.164020E-01,.140484E-01, $.120146E-01,.102596E-01,.875578E-02,.747237E-02,.637812E-02, $.544411E-02,.464687E-02,.396639E-02,.338610E-02,.289071E-02, $.246780E-02,.210710E-02/ DATA DEN/ $.130098E04,.116211E04,.103651E04,.922974E03,.828225E03, $.741093E03,.661404E03,.588557E03,.522172E03,.461856E03, $.407161E03,.349556E03,.299915E03,.257234E03,.220550E03, $.189031E03,.161984E03,.138758E03,.118838E03,.101741E03, $.868975E02,.742096E02,.633844E02,.541483E02,.462409E02, $.395018E02,.337449E02,.288270E02,.245552E02,.209198E02, $.178331E02,.152106E02,.129834E02,.109993E02,.932552E01, $.792416E01,.674806E01,.575870E01,.492532E01,.422101E01, $.362450E01,.311870E01,.268847E01,.232179E01,.200867E01, $.174103E01,.151159E01,.131472E01,.115353E01,.101629E01, $.895370,.788942,.695164,.615041,.545511,.483404,.427977, $.378600,.334562,.295439,.260605,.229681,.202224,.177869, $.156306,.137232,.120385,.105501,.923507E-01,.807449E-01, $.705135E-01,.615126E-01,.535945E-01,.466437E-01,.405427E-01, $.351939E-01,.305104E-01,.264185E-01,.228440E-01,.197255E-01, $.170075E-01,.145146E-01,.123870E-01,.105731E-01,.902476E-02, $.770318E-02,.657513E-02,.561318E-02,.479197E-02,.409090E-02, $.349296E-02/ C C INTERPOLACION PARABOLICA ENTRE TRES PUNTOS C C CALL POLI2 C I1=HA+1 I0=I1-1 I2=I1+1 IF(I0.GT.0)GO TO 10 I1=I1+1 I2=I2+1 I0=I0+1 10 X1=I0-1 X2=I1-1 X3=I2-1 DO 3 J=1,3 GO TO (5,6,7)J 5 Y1=PR(I0) Y2=PR(I1) Y3=PR(I2) CALL POLI2(X1,X2,X3,Y1,Y2,Y3,A,B,C) P=A+B*HA+C*HA**2 GO TO 3 6 Y1=TE(I0) Y2=TE(I1) Y3=TE(I2) CALL POLI2(X1,X2,X3,Y1,Y2,Y3,A,B,C) T=A+B*HA+C*HA*HA GO TO 3 7 Y1=DEN(I0) Y2=DEN(I1) Y3=DEN(I2) CALL POLI2(X1,X2,X3,Y1,Y2,Y3,A,B,C) D=A+B*HA+C*HA**2 3 CONTINUE RETURN END * Last processed by NICE on 05-Jul-2001 14:40:00 * Customized for : IEEE, LINUX, UNIX, MOTIF, F77 SUBROUTINE ASJ45(P,T,D,HA) c J CERNICHARO C C ATMOSFERA STANDARD U.S. 1962 MES DE JULIO 45 GRADOS LATITUD NORTE C HA === ALTURA EN KM DONDE SE DESEA CALCULAR P,T,D C P === PRESION EN MILIBARES C D === DENSIDAD EN GR/M**3 C T === TEMPERATURA EN K C REAL P,T,D,HA REAL PR(91),TE(91),DEN(91) INTEGER I0,I1,I2,J REAL X1,X2,X3,Y1,Y2,Y3,A,B,C * DATA TE/294.15,289.65,285.154,279.156,273.168,267.174,261.186, $254.702,248.215,241.734,235.254,228.773,222.299,215.825, $215.650,215.650,215.650,215.650,216.789,217.982,219.174, $220.367,221.559,222.750,223.942,225.132,226.323,227.513, $229.492,231.573,233.654,235.735,237.814,240.225,242.697, $245.170,247.642,250.115,252.585,255.055,257.525,259.992, $262.460,264.927,267.395,269.860,272.325,274.787,275.650, $275.650,275.650,275.650,275.650,274.245,271.787,269.330, $266.872,264.417,261.960,259.507,257.052,254.600,252.147, $248.931,244.521,240.111,235.701,231.295,226.890,222.484, $218.079,213.678,209.277,204.880,200.484,196.087,191.691, $187.299,182.907,178.515,174.150,174.150,174.150,174.150, $174.150,174.150,174.150,174.150,174.150,174.150,174.150/ DATA PR $/.101350E+04,.902198E+03,.801594E+03,.710433E+03,.628063E+03, $.553615E+03,.486632E+03,.426404E+03,.372351E+03,.324024E+03, $.280904E+03,.242550E+03,.208581E+03,.178571E+03,.152506E+03, $.130245E+03,.111251E+03,.950271E+02,.812140E+02,.694686E+02, $.594726E+02,.509578E+02,.437053E+02,.375159E+02,.322292E+02, $.277141E+02,.238504E+02,.205416E+02,.177123E+02,.152931E+02, $.132216E+02,.114455E+02,.992199E+01,.861267E+01,.748802E+01, $.651947E+01,.568409E+01,.496251E+01,.433891E+01,.379863E+01, $.332989E+01,.292305E+01,.256908E+01,.226070E+01,.199169E+01, $.175697E+01,.155167E+01,.137207E+01,.121431E+01,.107476E+01, $.951246,.842033,.745358,.659666,.583315,.515225,.454565, $.400635,.352641,.310106,.272332,.238893,.209295,.183112, $.159876,.139244,.120964,.104821,.905822E-01,.780538E-01, $.670582E-01,.574425E-01,.490475E-01,.417458E-01,.354072E-01, $.299215E-01,.251894E-01,.211250E-01,.176426E-01,.146698E-01, $.121421E-01,.100283E-01,.828248E-02,.684195E-02,.565195E-02, $.466893E-02,.358689E-02,.318670E-02,.263297E-02,.217545E-02, $.179779E-02/ DATA DEN/ $.119194E04,.107953E04,.975726E03,.884585E03,.799782E03, $.721113E03,.648694E03,.582972E03,.522462E03,.466877E03, $.415928E03,.369346E03,.326869E03,.288235E03,.246363E03, $.210402E03,.179719E03,.153510E03,.130507E03,.111022E03, $.945290E02,.805568E02,.687200E02,.586725E02,.501363E02, $.428845E02,.367119E02,.314533E02,.268872E02,.230062E02, $.197129E02,.169141E02,.145345E02,.124899E02,.107483E02, $.926367E01,.799602E01,.691194E01,.598427E01,.518837E01, $.450452E01,.391664E01,.340998E01,.297272E01,.259482E01, $.226811E01,.198495E01,.173947E01,.153465E01,.135829E01, $.120219E01,.106417E01,.941988,.837960,.747673,.666424,.593377, $.527834,.468961,.416292,.369075,.326876,.289163,.256257, $.227775,.202024,.178786,.157877,.139080,.122217,.107121, $.936510E-01,.816457E-01,.709824E-01,.615248E-01,.531584E-01, $.457778E-01,.392916E-01,.336024E-01,.286279E-01,.242889E-01, $.200605E-01,.165682E-01,.136866E-01,.113061E-01,.933969E-02, $.717528E-02,.637464E-02,.526696E-02,.435175E-02,.359628E-02/ C C INTERPOLACION PARABOLICA ENTRE TRES PUNTOS C C CALL POLI2 C I1=HA+1 I0=I1-1 I2=I1+1 IF(I0.GT.0)GO TO 10 I1=I1+1 I2=I2+1 I0=I0+1 10 X1=I0-1 X2=I1-1 X3=I2-1 DO 3 J=1,3 GO TO (5,6,7)J 5 Y1=PR(I0) Y2=PR(I1) Y3=PR(I2) CALL POLI2(X1,X2,X3,Y1,Y2,Y3,A,B,C) P=A+B*HA+C*HA**2 GO TO 3 6 Y1=TE(I0) Y2=TE(I1) Y3=TE(I2) CALL POLI2(X1,X2,X3,Y1,Y2,Y3,A,B,C) T=A+B*HA+C*HA*HA GO TO 3 7 Y1=DEN(I0) Y2=DEN(I1) Y3=DEN(I2) CALL POLI2(X1,X2,X3,Y1,Y2,Y3,A,B,C) D=A+B*HA+C*HA**2 3 CONTINUE RETURN END * Last processed by NICE on 05-Jul-2001 14:40:00 * Customized for : IEEE, LINUX, UNIX, MOTIF, F77 C+HPUX c$OPTIMIZE LEVEL1 C-HPUX SUBROUTINE ATMATMOSP(T0,P0,H0) C---------------------------------------------------------------------- C Compute an atmospheric model, interpolated between standard atmospheres C of winter and summer (subroutines ase45 and asj 45), to fit with temperature C t0 (k) and pressure p0 (mbar) at altitude h0 (km). C 15 layers are used. C The transmission of the model atmosphere can then be computed by calling C entry point transm. C C input t0 R temperature (K) C po R pressure (mbar) C h0 R altitude (km) C---------------------------------------------------------------------- INTEGER MP PARAMETER (MP=80) REAL T(MP), H(MP), P(MP), R(MP), RR(MP), $PE, TE, DE, H0, T0, P0, PJ, TJ, DJ, APE, ATE, APJ, ATJ, P1, $T1, D1, HEIGHT, R1, WATER, AIRMASS, TAUW, TAUOX, TAUT, TEMI, $FREQ, TAG, TOX, TAGU, TOXI, TATM INTEGER NP, J, IER REAL PATH REAL DPATH, Z, PR_AG REAL*8 N_INDEX, C_SNELL * SAVE T,H,P,R,NP * * average summer and winter model atmospheres according to * given values of temperature and pression CALL ASE45(PE,TE,DE,H0) CALL ASJ45(PJ,TJ,DJ,H0) APE = (P0-PJ)/(PE-PJ) APJ = (P0-PE)/(PJ-PE) ATE = (T0-TJ)/(TE-TJ) ATJ = (T0-TE)/(TJ-TE) * * set layers DO J=1,6 H(J) = .5E5 ENDDO DO J=7,12 H(J) = 2.E5 ENDDO DO J=13,15 H(J) = 15.E5 ENDDO NP = 15 * * Set t,p, and r (H2O for 1mm precipitable content) profiles HEIGHT = H0 P1 = P0 T1 = T0 R1 = .5 DO J = 1, NP HEIGHT = HEIGHT + H(J)/100000. ! in km. P(J) = P1 T(J) = T1 R(J) = R1 CALL ASE45(PE,TE,DE,HEIGHT) CALL ASJ45(PJ,TJ,DJ,HEIGHT) P1 = APE*PE+APJ*PJ T1 = ATE*TE+ATJ*TJ D1 = DE*(1+(P1-PE)/PE-(T1-TE)/TE) R1 = .5*EXP(-.5*(HEIGHT-H0)) IF(HEIGHT.GT.15.) R1 = R1 + D1*2E-6 P(J) = (P(J) + P1)/2. T(J) = (T(J) + T1)/2. R(J) = (R(J) + R1)/2. ENDDO RETURN * ENTRY ATMTRANSM(WATER,AIRMASS,FREQ,TEMI,TATM,TAUOX,TAUW,TAUT $,IER) C---------------------------------------------------------------------- C Compute atmospheric emission and absorption. C C Input: C water R H2O precipitable content(mm) C airmass R Number of air masses C freq R Frequency (GHz) C C Output: C temi R atmosph emission (K) C tatm R mean temperature (K) C tauox R Oxygen optical depth AT ZENITH (nepers) C tauw R Water optical depth AT ZENITH (nepers) C taut R Total optical depth AT ZENITH (nepers) C IER I Error code C---------------------------------------------------------------------- DO J = 1, NP RR(J) = R(J) * WATER ENDDO IER = 0 CALL KVATM(NP,P,T,RR,H,TAUW,TAUOX,FREQ,TEMI,TATM,TAG,TAGU,TOX, $TOXI,0,0,TAUT,AIRMASS,IER) TAUOX = TAUOX / AIRMASS ! RL 14 MAR 86 TAUW = TAUW / AIRMASS ! TAUT = TAUT / AIRMASS ! RETURN * ENTRY ATMPATH(WATER,AIRMASS,FREQ,PATH,IER) C---------------------------------------------------------------------- C integrated optical pathlength of atmosphere C C np .... numero de capas C h .... espesor de las capas (cm) C p .... presion (milibares) C t .... temperatura (k) C rho ... cantidad de vapor de agua (gr/m**3) C---------------------------------------------------------------------- C----------------------------------------------------------------------- * MB: zenith distance angle from airmass (parallel layers): Z = ACOS( 1. / AIRMASS) C_SNELL = -1 PATH = 0. * DO J=1,NP * * partial pressure of water vapor. Rspec = Rgas/M_H2O = 8314/18.02 = 461.4 * Conversion from pascal->mbar 1e-2, g->kg 1e-3: PR_AG = 4.614E-03 * T(J) * R(J) * WATER CALL EXCESS_PATH (FREQ, P(J), PR_AG, T(J), H(J), $ Z, DPATH, C_SNELL, N_INDEX) * IF (J .EQ. 1) DI = Z - ASIN(SIN(Z) / N_INDEX) PATH = PATH + DPATH ENDDO C WRITE(*,*)'FORTRAN ERR=', IER END * Last processed by NICE on 05-Jul-2001 14:40:00 * Customized for : IEEE, LINUX, UNIX, MOTIF, F77 SUBROUTINE EXCESS_PATH (F_GHZ, P_ATM, P_VAP, T, DH, $Z, PATH, C_SNELL, N_INDEX) C-------------------------------------------------------------------------- * * Calculation of the excess path length (path length difference between * vacuum and atmospheric propagation) for a parallel medium of * constant refraction index. * Source for the path length formula: Thompson, Moran, Swenson (1986), * Interferometry and Synthesis in Radio Astronomy, p 407 * * Input: real*4 f_ghz frequency in GHz * p_atm total atmospherical pressure in millibar * p_vap partial pressure of water vapor in millibar * t temperature in kelvin * z Zenith distance angle (radians) * Output: real*8 c_snell : Constant of Snell's law * n_index : refraction index * real*4 path : path length difference in units of dh. * * Author: 25-Jan-1994 Michael Bremer, IRAM C------------------------------------------------------------------------- REAL REFRACT_TOTAL, F_GHZ, P_ATM, P_VAP, T, DH, Z, PATH, REFR REAL*8 SIN_Z, COS_Z, C_SNELL, N_INDEX *---------------------------------------------- * REFR = REFRACT_TOTAL( F_GHZ, P_ATM, P_VAP, T) * * Apply the definition of refractivity to get the refraction index : * N_INDEX = 1.0D+00 + REFR * 1.0D-06 * * c_snell stays constant along the line of sight (Snell's law) and * is calculated if the given value is .lt. 0. This should make life * easier when dealing with multiple layers. * IF (C_SNELL .LT. 0) C_SNELL = SIN( Z ) * N_INDEX SIN_Z = C_SNELL / N_INDEX COS_Z = SQRT(1.D+00 - SIN_Z * SIN_Z) * PATH = REFR * 1.0D-06 * DH / COS_Z * END * FUNCTION REFRACT_TOTAL (F_GHZ, P_ATM, P_VAP, T ) C------------------------------------------------------------------- C C Calculation of the total atmospheric refractivity (dry component and C water vapor), taking into account the dependences from C frequency, pressure, temperature C Source of formulae: Hill and Cliffort (1981), Radio Science 16, pp. 77-82 C and Thompson, Moran, Swenson (1986), C Interferometry and Synthesis in Radio Astronomy, p 407 C C Input: real*4 f_ghz frequency in GHz C p_atm total atmospherical pressure in millibar C p_vap partial pressure of water vapor in millibar C t temperature in kelvin C C Author: 25-Jan-1994 Michael Bremer, IRAM C C------------------------------------------------------------------- REAL REFRACT_TOTAL, F_GHZ, P_ATM, P_VAP, T REAL REF_DRY, REF_VAP, SC, REFRACT_VAPOR * *-------------------------------------------- * * sc = scaling factor for the wavelength dependent part of the wet refraction * (normal conditions 300K, 1013mbar, 80% humidity -> partial pressure of * water vapor 28.2mbar ): * SC = (P_VAP / 28.2) * (300./T)**2 REF_DRY = 77.493 * P_ATM / T REF_VAP = - 12.8 * P_VAP / T + REFRACT_VAPOR( F_GHZ ) * SC REFRACT_TOTAL = REF_DRY + REF_VAP END * FUNCTION REFRACT_VAPOR (F_GHZ) C----------------------------------------------------------------------- C C Function to calculate the refractivity of water vapor 0-480 GHz, under C conditions T=300K, P=1atm, 80% rel. humidity (i.e. partial pressure of C water vapor 28.2 mbar). C C Source: Hill and Clifford (1981), Radio Science 16, curve p. 80 C Method of digitalisation: zoomed copy to a transparency, C points read by cursor. C Approx. errors: F +-1.5 GHz, R +-0.1 C C Author: 24-Jan-1993 Michael Bremer, IRAM C------------------------------------------------------------------------ REAL REFRACT_VAPOR, F_GHZ INTEGER NPOINT, I PARAMETER (NPOINT = 53) REAL FREQ(NPOINT), REFR(NPOINT), U * DATA FREQ / 0.00 , 18.00 , 22.53 , 47.95 , $59.41 , 79.04 , 98.67 , 115.85 , $133.03 , 152.66 , 167.39 , 181.70 , $183.57 , 185.66 , 188.11 , 195.47 , $215.65 , 235.29 , 250.83 , 271.28 , $288.28 , 305.46 , 315.27 , 321.64 , $323.50 , 327.18 , 328.82 , 336.18 , $348.45 , 358.27 , 366.45 , 371.35 , $374.63 , 377.90 , 379.53 , 378.72 , $381.35 , 382.99 , 387.90 , 393.44 , $405.71 , 427.26 , 431.07 , 437.80 , $441.07 , 446.16 , 448.80 , 449.62 , $456.98 , 469.07 , 472.97 , 476.43 , $480.01 / DATA REFR / 115.64 , 115.68 , 115.59 , 115.69 , $115.76 , 115.88 , 116.07 , 116.25 , $116.50 , 116.81 , 117.24 , 118.08 , $117.80 , 116.25 , 116.38 , 117.00 , $117.65 , 118.21 , 118.71 , 119.39 , $120.07 , 120.87 , 121.53 , 122.26 , $122.45 , 121.33 , 121.33 , 122.20 , $123.38 , 124.37 , 125.61 , 126.91 , $128.27 , 129.85 , 125.73 , 121.89 , $119.29 , 119.29 , 121.89 , 123.30 , $125.70 , 129.18 , 129.87 , 132.29 , $132.42 , 134.90 , 129.51 , 125.30 , $129.07 , 133.47 , 134.59 , 134.24 , $134.96 / * ------------------------------- * * negative frequencies are NOT accepted (not even in jest): * IF (F_GHZ .LT. 0) THEN WRITE(6,*) 'E-ATM, Error from refract_vapor: frequency < 0' STOP 'Negative frequency' ENDIF * * Find the frequency interval (i-1,i) of the input frequency: I = 2 * 10 CONTINUE IF (FREQ(I) .GT. F_GHZ) GOTO 20 I = I + 1 * IF (I .LE. NPOINT) GOTO 10 * * Print an error message, if the frequency range has been checked and the * requested frequency lies beyond, and give the last data range value: * PRINT *,'Error from refract_vapor: ',F_GHZ,' outside 0-480 GHz.' * REFRACT_VAPOR = REFR(NPOINT) RETURN * 20 CONTINUE * * Perform linear interpolation between the interval borders: * U = (F_GHZ - FREQ(I-1)) / (FREQ(I) - FREQ(I-1)) REFRACT_VAPOR = REFR(I-1) + (REFR(I) - REFR(I-1)) * U END * Last processed by NICE on 05-Jul-2001 14:40:00 * Customized for : IEEE, LINUX, UNIX, MOTIF, F77 FUNCTION FLIN(V,VL,DV) C---------------------------------------------------------------------- C J Cernicharo, Model atmosphere. C C FORMA CINETICA DEL PERFIL C V... FRECUENCIA C VL.. FRECUENCIA DE LA LINEA C DV.. ANCHURA DE LA LINEA C---------------------------------------------------------------------- REAL FLIN,V,VL,DV,PI,V2 DATA PI/3.141592654/ * FLIN=4.*V*VL*DV/PI V2=V*V FLIN=FLIN/(4.*V2*DV*DV+(VL*VL-V2)**2) END * Last processed by NICE on 05-Jul-2001 14:40:00 * Customized for : IEEE, LINUX, UNIX, MOTIF, F77 FUNCTION FVVW(V,VL,DV) C---------------------------------------------------------------------- C PERFIL DE VAN VLECK & WEISSKOPF C C J CERNICHARO C---------------------------------------------------------------------- REAL FVVW,V,DV,VL,PI,DV2,A1,A2 DATA PI/3.141592654/ * FVVW=DV*V/VL/PI DV2=DV*DV A1=DV2+(V-VL)**2 A2=DV2+(V+VL)**2 FVVW=FVVW*(1./A1+1./A2) END * Last processed by NICE on 05-Jul-2001 14:40:00 * Customized for : IEEE, LINUX, UNIX, MOTIF, F77 FUNCTION KH2O(RHO,T,P,V,IL) C---------------------------------------------------------------------- C COEFICIENTE DE ABSORCION DEL VAPOR DE AGUA ATMOSFERICO C T ... ES LA TEMPERATURA (K) C P ... ES LA PRESION EN MILIBARES C RHO . ES LA CONCENTRACION DE VAPOR DE AGUA EN GR/M**3 C V ... ES LA FRECUENCIA EN GHZ C KH2O . COEFICIENTE DE ABSORCION EN CM-1 C IL=0 PERFIL CINETICO C IL=1 PERFIL DE VAN VLECK & WEISSKPOF C C J.Cernicharo C---------------------------------------------------------------------- REAL RHO,T,P,V INTEGER IL REAL FRE(19),GL(19),FLM(19),EL(19),DV0(19),DVLM(19), $X(19),B1(9),B2(9),B3(9),FDEB(9) REAL KH2O,SUM,TA,TKK,TK,SOM,DV,RD,RD0,TT,GG,PI,FV INTEGER L,J * REAL FVVW,FLIN C C FRE ... FRECUENCIAS DE LAS TRANSICIONES DEL VAPOR DE AGUA (GHZ) C DATA FRE/22.23507985,183.3100906,321.225644,325.152919,380.197372, $390.14,437.346667,439.150812,443.018295,448.001075, $470.888947,474.689127,488.491133,556.936002,620.700807, $752.033227,916.62,970.31,987.94/ C C GL ... DEGENERACION DE LOS NIVELES C DATA GL/3.,1.,3.,1.,3.,1.,1.,3.,3.,3.,1.,1.,1.,3.,3., $1.,1.,1.,1./ C C FLM ... CUADRADO DEL ELEMENTO DE MATRIZ LM C DATA FLM/.057,.102,.089,.091,.123,.068,.088,.0101,.088, $.132,.102,.118,.036,1.5,.122,2.073,.161,.262,.7557/ C C EM ... ENERGIAS EN CM-1 DEL NIVEL SUPERIOR EN LA TRANS L=>M C EL ... " " " " " INFERIOR " " " " C C DATA EM/447.3,142.27,1293.8,326.62,224.84,1538.31,1059.63, C 1 756.76,1059.90,300.37/ DATA EL/446.56,136.16,1283.02,315.78,212.16,1525.31, $1045.03,742.11,1045.11,285.42,742.074,488.135,586.48, $23.794,488.108,70.091,285.217,383.837,37.137/ DATA DV0/2.85,2.68,2.3,3.03,3.19,2.11,1.5,1.94,1.51, $2.47,1.89,2.07,2.58,3.33,2.28,3.13,2.59,2.48,3.09/ DATA DVLM/13.68,14.49,12.04,15.21,15.84,11.42,7.94,10.44, $8.13,14.24,10.56,11.95,14.77,14.66,12.78,13.93,14.06,14.16, $15.20/ C C X ... EXPONENTE DE LA TEMPERATURA C DATA X/.626,.649,.42,.619,.63,.33,.29,.36,.332,.51, $.380,.38,.57,.645,.6,.69,.676,.56,.66/ DATA FDEB/68.052,503.56,504.46,658.34,841.01,859.81,899.38, $903.28,906.21/ DATA B1/1.8E-3,3.5E-3,1.2E-3,4.6E-2,1.2E-3,1.5E-3,9.1E-3, $6.4E-3,1.79E-2/ DATA B2/8.75,6.69,6.69,7.76,8.11,7.99,7.84,8.35,5.04/ DATA B3/2.8E-3,1.27E-3,1.3E-3,3.28E-3,1.7E-3,2.7E-3,3E-3, $2.8E-3,2.04E-3/ DATA PI/3.141592654/,TK/.69503096/ KH2O=1.44*RHO*V/SQRT(T**3) SUM=0. TA=300./T TKK=TK*T DO 1 L=1,19 C IF(V.LE.FRE(L)+200..AND.V.GE.FRE(L)-200.)GO TO 5 C GO TO 1 5 SOM=GL(L)*FLM(L)*EXP(-EL(L)/TKK)*(1.-EXP(-FRE(L)/TKK/29.97925)) DV=DV0(L)*P/1013./((T/300.)**X(L)) DV=DV*(1.+(4.6E-03*RHO*T/P)*(DVLM(L)/DV0(L)-1.)) IF(IL.EQ.0)FV=FLIN(V,FRE(L),DV) IF(IL.EQ.1)FV=FVVW(V,FRE(L),DV) SUM=SUM+SOM*FV 1 CONTINUE KH2O=KH2O*SUM C C TERMINO CORRECTOR EMPIRICO (POSIBLE CONTRIBUCION DE LOS DIMEROS C DE H2O) C KH2O=KH2O+1.08E-11*RHO*V*V*P/1000.*(TA)**2.1 C C RAYAS DEBILES C RD=1.937E-9*V*RHO*T RD0=0. DO 10 J=1,9 C IF(V.LE.FDEB(J)+100..AND.V.GE.FDEB(J)-100.)GO TO 15 C GO TO 10 15 TT=B2(J)*(1.-TA) TT=B1(J)*EXP(TT)*(TA)**3.5 GG=TA**0.6*P*B3(J) IF(IL.EQ.0)FV=FLIN(V,FDEB(J),GG) IF(IL.EQ.1)FV=FVVW(V,FDEB(J),GG) RD0=RD0+TT*FV 10 CONTINUE RD=RD*RD0 KH2O=KH2O+RD RETURN END * Last processed by NICE on 05-Jul-2001 14:40:00 * Customized for : IEEE, LINUX, UNIX, MOTIF, F77 FUNCTION KO2(T,P,V,IL) C---------------------------------------------------------------------- C OPACIDAD DE LA ATMOSFERA DEBIDA AL OXIGENO (O2) C T .... ES LA TEMPERATURA (K) C P .... ES LA PRESION EN MILIBARES C V .... ES LA FRECUENCIA A LA CUAL SE DESEA CALCULAR KO2 C IL ... =0 PERFIL CINETICO C IL ... =1 PERFIL DE VAN VLECK & WEISSKOPF C KO2 .. OPACIDAD EN CM-1 C C ANCHURA DE LAS RAYAS DE REBER... BUENA APROXIMACION EN LAS ALAS C C J.Cernicharo C---------------------------------------------------------------------- REAL KO2,T,P,V INTEGER IL,L,J REAL FMEN(20),FMAS(20),RN(20),FDEB(6),B1(6),B2(6),B3(6) * REAL TA,V2,SUM,E0,DV1,DV,DV2,A1,A2,A3,E,PI,RD,RD0,GG,B,RR REAL FLIN,FVVW C C FMEN ... FRECUENCIAS EN GHZ N- C DATA FMEN/118.750343,62.486255,60.306044,59.164215, $58.323885,57.612488,56.968180,56.363393,55.783819, $55.221372,54.671145,54.1302,53.5959,53.0669,52.5424, $52.0214,51.50302,50.9873,50.4736,49.9618/ C C FMAS ... FRECUENCIAS EN GHZ N+ C DATA FMAS/56.264766,58.446580,59.590978,60.434776, $61.15057,61.800169,62.411223,62.997991,63.56852, $64.127777,64.678914,65.22412,65.764744,66.30206, $66.83677,67.36951,67.90073,68.4308,68.9601,69.4887/ C C N ... NUMERO CUANTICO DE ROTACION C DATA RN/1.,3.,5.,7.,9.,11.,13.,15.,17.,19.,21.,23., $25.,27.,29.,31.,33.,35.,37.,39./ C C RAYAS CON DN=2 C DATA FDEB/368.499,424.7638,487.25,715.3944,773.841,834.147/ DATA B1/6.79E-6,6.43E-05,2.39E-5,9.79E-6,5.71E-5,1.83E-5/ DATA B2/.202,.0112,.0112,.0891,.0798,.0798/ DATA B3/15.6E-4,14.7E-4,14.7E-4,14.4E-4,14E-4,14E-4/ DATA PI/3.141592654/ KO2=1.44E-05*P*V/T/T/T TA=300./T V2=V**2 SUM=0. E0=2.07/T DV1=1.41E-03*P*300./T DV=DV1 IF(DV1.GT.0.0527)DV=DV/3.+0.03513 DV2=DV*DV DO 1 L=1,20 A1=(RN(L)**2+RN(L)+1.)*(2.*RN(L)+1.)/RN(L)/(RN(L)+1.) E=E0*RN(L)*(RN(L)+1.) A1=A1*2.*V*DV/PI/(V2+DV2) A2=RN(L)*(2.*RN(L)+3.)/(RN(L)+1.) IF(IL.EQ.0)A2=A2*FLIN(V,FMAS(L),DV)*FMAS(L) IF(IL.EQ.1)A2=A2*FVVW(V,FMAS(L),DV)*FMAS(L) A3=(RN(L)+1.)*(2.*RN(L)-1.)/RN(L) B=DV IF(L.EQ.1)B=DV1 IF(IL.EQ.0.)A3=A3*FMEN(L)*FLIN(V,FMEN(L),B) IF(IL.EQ.1.)A3=A3*FMEN(L)*FVVW(V,FMEN(L),B) 1 CONTINUE SUM=SUM+(A1+A2+A3)*EXP(-E) KO2=SUM*KO2 C C RAYAS CON DN=2 C RD=P*TA**3*4.193E-07*V RD0=0. DO 10 J=1,6 C IF(V.LE.FDEB(J)+200..AND.V.GE.FDEB(J)-200.)GO TO 15 C GO TO 10 15 RR=B1(J)*EXP(B2(J)*(1.-TA)) GG=B3(J)*P*TA**.9 IF(IL.EQ.0)RR=RR*FLIN(V,FDEB(J),GG) IF(IL.EQ.1)RR=RR*FVVW(V,FDEB(J),GG) RD0=RD0+RR 10 CONTINUE RD=RD*RD0 KO2=KO2+RD RETURN END * Last processed by NICE on 05-Jul-2001 14:40:00 * Customized for : IEEE, LINUX, UNIX, MOTIF, F77 SUBROUTINE KVATM(NP,P,T,RHO,H,AGU,OXI,V,TEMI,TATM,TAG,TAGU,TOX, $TOXI,ILAG,ILOX,KVAT,AMA,IER) C---------------------------------------------------------------------- C opacidad de la atmosfera a la frecuencia 'v' debida al C vapor de agua y al oxigeno. C C np .... numero de capas C h .... espesor de las capas (cm) C p .... presion (milibares) C t .... temperatura (k) C rho ... cantidad de vapor de agua (gr/m**3) C temi .. emisividad total de la atmosfera (k) C kvat .. opacidad total (nepers) C tatm .. temperatura media de la atmosfera (k) C agu ... opacidad debida al vapor de agua (nepers) C oxi ... opacidad debida al oxigeno ( " ) C tag ... emisividad debida al vapor de agua (atmosfera sin oxigeno) C tox ... idem para el oxigeno (sin vapor de agua) C tagu .. temperatura media de la capa de vapor de agua (k) C toxi .. temperatura media de la capa de oxigeno (k) C C J.Cernicharo C---------------------------------------------------------------------- INTEGER NP,ILAG,ILOX,IER REAL AGU,OXI,V,TEMI,TATM,TAG,TAGU,TOX,TOXI,KVAT,AMA REAL H(*),P(*),T(*),RHO(*) * INTEGER J REAL R,PR,TEM,DH,OX,AG,KV REAL KH2O, KO2 * TEMI=0. KV=0. TAG=0. TOX=0. AGU=0. OXI=0. DO 1 J=1,NP R=RHO(J) PR=P(J) TEM=T(J) DH=H(J) AG=KH2O(R,TEM,PR,V,ILAG)*DH*AMA OX=KO2(TEM,PR,V,ILOX)*DH*AMA TAG=TAG+TEM*EXP(-AGU)*(1.-EXP(-AG)) AGU=AGU+AG TOX=TOX+TEM*EXP(-OXI)*(1.-EXP(-OX)) OXI=OXI+OX TEMI=TEMI+TEM*EXP(-KV)*(1.-EXP(-AG-OX)) 1 CONTINUE KV=AGU+OXI KVAT=KV IF ( KV.LE.1.E-10 ) THEN IER = 1 ELSEIF ( OXI.LE.1.E-20) THEN IER = 2 ELSEIF ( AGU.LE.1.E-20 ) THEN IER = 3 ELSE TATM=TEMI/(1.-EXP(-KV)) TAGU=TAG/(1.-EXP(-AGU)) TOXI=TOX/(1.-EXP(-OXI)) IER = 0 ENDIF END * Last processed by NICE on 05-Jul-2001 14:40:00 * Customized for : IEEE, LINUX, UNIX, MOTIF, F77 SUBROUTINE POLI2(X1,X2,X3,Y1,Y2,Y3,A,B,C) C---------------------------------------------------------------------- C ESTA SUBRUTINA CALCULA LOS COEFICIENTES A,B,C DEL POLINOMIO DE C SEGUNDO GRADO A+BX+CX**2, QUE PASA POR LOS PUNTOS (X1,Y1), C (X2,Y2),(X3,Y3) C J.Cernicharo C---------------------------------------------------------------------- REAL X1,X2,X3,Y1,Y2,Y3,A,B,C * C=(Y3-Y2)*(X2-X1)-(Y2-Y1)*(X3-X2) B=(X2-X1)*(X3*X3-X2*X2)-(X2*X2-X1*X1)*(X3-X2) C=C/B B=(Y2-Y1)-C*(X2*X2-X1*X1) B=B/(X2-X1) A=Y1-C*X1*X1-B*X1 END casacore-3.7.1/scimath_f/convolvegridder.f000066400000000000000000000424421476623553700206320ustar00rootroot00000000000000*======================================================================= * Copyright (C) 1999 * Associated Universities, Inc. Washington DC, USA. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, * MA 02139, USA. * * Correspondence concerning AIPS++ should be addressed as follows: * Internet email: casa-feedback@nrao.edu. * Postal address: AIPS++ Project Office * National Radio Astronomy Observatory * 520 Edgemont Road * Charlottesville, VA 22903-2475 USA * * $Id$ *----------------------------------------------------------------------- subroutine cgrd1d (ni, li, grid, value, support, sampling, posi, $ convFunc) implicit none integer ni integer li integer support, sampling complex value, grid(ni), nvalue double precision convFunc(*), norm double precision posi integer i, loci integer offi offi=nint((dble(nint(posi))-posi)*sampling) norm=0.0 do i=-support,support loci=abs(sampling*i+offi) norm=norm+convFunc(loci+1) end do nvalue=nvalue/norm do i=-support,support loci=abs(sampling*i+offi) grid(i+li+1)=grid(i+li+1)+nvalue*convFunc(loci+1) end do return end subroutine cgrd2d (ni, nj, li, lj, grid, value, support, $ sampling, posi, posj, convFunc) implicit none integer ni, nj integer li, lj integer support, sampling complex value, grid(ni, nj), nvalue double precision convFunc(*), norm double precision posi, posj integer i, j, loci, locj integer offi, offj offi=nint((dble(nint(posi))-posi)*sampling) offj=nint((dble(nint(posj))-posj)*sampling) norm=0.0 do j=-support,support locj=abs(sampling*j+offj) do i=-support,support loci=abs(sampling*i+offi) norm=norm+convFunc(loci+1)*convFunc(locj+1) end do end do do j=-support,support locj=abs(sampling*j+offj) nvalue=value*convFunc(locj+1)/norm do i=-support,support loci=abs(sampling*i+offi) grid(i+li+1,j+lj+1)=grid(i+li+1,j+lj+1)+ $ nvalue*convFunc(loci+1) end do end do return end subroutine cgrd3d (ni, nj, nk, li, lj, lk, grid, value, support, $ sampling, posi, posj, posk, convFunc) implicit none integer ni, nj, nk integer li, lj, lk integer support, sampling complex value, grid(ni, nj, nk), nvalue double precision convFunc(*), norm double precision posi, posj, posk integer i, j, k, loci, locj, lock integer offi, offj, offk offi=nint((dble(nint(posi))-posi)*sampling) offj=nint((dble(nint(posj))-posj)*sampling) offk=nint((dble(nint(posk))-posk)*sampling) norm=0.0 do k=-support,support lock=abs(sampling*k+offk) do j=-support,support locj=abs(sampling*j+offj) do i=-support,support loci=abs(sampling*i+offi) norm=norm+convFunc(loci+1)*convFunc(locj+1)* $ convFunc(lock+1) end do end do end do do k=-support,support lock=abs(sampling*k+offk) do j=-support,support locj=abs(sampling*j+offj) nvalue=value*convFunc(locj+1)*convFunc(lock+1)/norm do i=-support,support loci=abs(sampling*i+offi) grid(i+li+1,j+lj+1,k+lk+1)=grid(i+li+1,j+lj+1,k+lk+1)+ $ value*convFunc(loci+1) end do end do end do return end subroutine cdgrd1d (ni, li, grid, value, support, sampling, $ posi, convFunc) implicit none integer ni integer li integer support, sampling complex value, grid(ni) double precision convFunc(*), norm double precision posi integer i, loci integer offi offi=nint((dble(nint(posi))-posi)*sampling) norm=0.0 value=0.0 do i=-support,support loci=abs(sampling*i+offi) value=value+grid(i+li+1)*convFunc(loci+1) norm=norm+convFunc(loci+1) end do value=value/norm return end subroutine cdgrd2d (ni, nj, li, lj, grid, value, support, $ sampling, posi, posj, convFunc) implicit none integer ni, nj integer li, lj integer support, sampling complex value, grid(ni, nj) double precision convFunc(*), norm double precision posi, posj integer i, j, loci, locj integer offi, offj offi=nint((dble(nint(posi))-posi)*sampling) offj=nint((dble(nint(posj))-posj)*sampling) norm=0.0 value=0.0 do j=-support,support locj=abs(sampling*j+offj) do i=-support,support loci=abs(sampling*i+offi) value=value+ $ grid(i+li+1,j+lj+1)*convFunc(loci+1)*convFunc(locj+1) norm=norm+convFunc(loci+1)*convFunc(locj+1) end do end do value=value/norm return end subroutine cdgrd3d (ni, nj, nk, li, lj, lk, grid, value, $ support, sampling, posi, posj, posk, convFunc) implicit none integer ni, nj, nk integer li, lj, lk integer support, sampling complex value, grid(ni, nj, nk) double precision convFunc(*), norm double precision posi, posj, posk integer i, j, k, loci, locj, lock integer offi, offj, offk offi=nint((dble(nint(posi))-posi)*sampling) offj=nint((dble(nint(posj))-posj)*sampling) offk=nint((dble(nint(posk))-posk)*sampling) norm=0.0 value=0.0 do k=-support,support lock=abs(sampling*k+offk) do j=-support,support locj=abs(sampling*j+offj) do i=-support,support loci=abs(sampling*i+offi) norm=norm+convFunc(loci+1)*convFunc(locj+1)* $ convFunc(lock+1) value=value+grid(i+li+1,j+lj+1,k+lk+1)*convFunc(loci+1)* $ convFunc(locj+1)*convFunc(lock+1) end do end do end do value=value/norm return end subroutine dgrd1d (ni, li, grid, value, support, sampling, posi, $ convFunc) implicit none integer ni integer li integer support, sampling double precision value, grid(ni), nvalue double precision convFunc(*), norm double precision posi integer i, loci integer offi offi=nint((dble(nint(posi))-posi)*sampling) norm=0.0 do i=-support,support loci=abs(sampling*i+offi) norm=norm+convFunc(loci+1) end do nvalue=nvalue/norm do i=-support,support loci=abs(sampling*i+offi) grid(i+li+1)=grid(i+li+1)+nvalue*convFunc(loci+1) end do return end subroutine dgrd2d (ni, nj, li, lj, grid, value, support, $ sampling, posi, posj, convFunc) implicit none integer ni, nj integer li, lj integer support, sampling double precision value, grid(ni, nj), nvalue double precision convFunc(*), norm double precision posi, posj integer i, j, loci, locj integer offi, offj offi=nint((dble(nint(posi))-posi)*sampling) offj=nint((dble(nint(posj))-posj)*sampling) norm=0.0 do j=-support,support locj=abs(sampling*j+offj) do i=-support,support loci=abs(sampling*i+offi) norm=norm+convFunc(loci+1)*convFunc(locj+1) end do end do do j=-support,support locj=abs(sampling*j+offj) nvalue=value*convFunc(locj+1)/norm do i=-support,support loci=abs(sampling*i+offi) grid(i+li+1,j+lj+1)=grid(i+li+1,j+lj+1)+ $ nvalue*convFunc(loci+1) end do end do return end subroutine dgrd3d (ni, nj, nk, li, lj, lk, grid, value, support, $ sampling, posi, posj, posk, convFunc) implicit none integer ni, nj, nk integer li, lj, lk integer support, sampling double precision value, grid(ni, nj, nk), nvalue double precision convFunc(*), norm double precision posi, posj, posk integer i, j, k, loci, locj, lock integer offi, offj, offk offi=nint((dble(nint(posi))-posi)*sampling) offj=nint((dble(nint(posj))-posj)*sampling) offk=nint((dble(nint(posk))-posk)*sampling) norm=0.0 do k=-support,support lock=abs(sampling*k+offk) do j=-support,support locj=abs(sampling*j+offj) do i=-support,support loci=abs(sampling*i+offi) norm=norm+convFunc(loci+1)*convFunc(locj+1)* $ convFunc(lock+1) end do end do end do do k=-support,support lock=abs(sampling*k+offk) do j=-support,support locj=abs(sampling*j+offj) nvalue=value*convFunc(locj+1)*convFunc(lock+1)/norm do i=-support,support loci=abs(sampling*i+offi) grid(i+li+1,j+lj+1,k+lk+1)=grid(i+li+1,j+lj+1,k+lk+1)+ $ value*convFunc(loci+1) end do end do end do return end subroutine ddgrd1d (ni, li, grid, value, support, sampling, $ posi, convFunc) implicit none integer ni integer li integer support, sampling double precision value, grid(ni) double precision convFunc(*), norm double precision posi integer i, loci integer offi offi=nint((dble(nint(posi))-posi)*sampling) norm=0.0 value=0.0 do i=-support,support loci=abs(sampling*i+offi) value=value+grid(i+li+1)*convFunc(loci+1) norm=norm+convFunc(loci+1) end do value=value/norm return end subroutine ddgrd2d (ni, nj, li, lj, grid, value, support, $ sampling, posi, posj, convFunc) implicit none integer ni, nj integer li, lj integer support, sampling double precision value, grid(ni, nj) double precision convFunc(*), norm double precision posi, posj integer i, j, loci, locj integer offi, offj offi=nint((dble(nint(posi))-posi)*sampling) offj=nint((dble(nint(posj))-posj)*sampling) norm=0.0 value=0.0 do j=-support,support locj=abs(sampling*j+offj) do i=-support,support loci=abs(sampling*i+offi) value=value+ $ grid(i+li+1,j+lj+1)*convFunc(loci+1)*convFunc(locj+1) norm=norm+convFunc(loci+1)*convFunc(locj+1) end do end do value=value/norm return end subroutine ddgrd3d (ni, nj, nk, li, lj, lk, grid, value, $ support, sampling, posi, posj, posk, convFunc) implicit none integer ni, nj, nk integer li, lj, lk integer support, sampling double precision value, grid(ni, nj, nk) double precision convFunc(*), norm double precision posi, posj, posk integer i, j, k, loci, locj, lock integer offi, offj, offk offi=nint((dble(nint(posi))-posi)*sampling) offj=nint((dble(nint(posj))-posj)*sampling) offk=nint((dble(nint(posk))-posk)*sampling) norm=0.0 value=0.0 do k=-support,support lock=abs(sampling*k+offk) do j=-support,support locj=abs(sampling*j+offj) do i=-support,support loci=abs(sampling*i+offi) norm=norm+convFunc(loci+1)*convFunc(locj+1)* $ convFunc(lock+1) value=value+grid(i+li+1,j+lj+1,k+lk+1)*convFunc(loci+1)* $ convFunc(locj+1)*convFunc(lock+1) end do end do end do value=value/norm return end subroutine fgrd1d (ni, li, grid, value, support, sampling, posi, $ convFunc) implicit none integer ni integer li integer support, sampling real value, grid(ni), nvalue double precision convFunc(*), norm double precision posi integer i, loci integer offi offi=nint((dble(nint(posi))-posi)*sampling) norm=0.0 do i=-support,support loci=abs(sampling*i+offi) norm=norm+convFunc(loci+1) end do nvalue=nvalue/norm do i=-support,support loci=abs(sampling*i+offi) grid(i+li+1)=grid(i+li+1)+nvalue*convFunc(loci+1) end do return end subroutine fgrd2d (ni, nj, li, lj, grid, value, support, $ sampling, posi, posj, convFunc) implicit none integer ni, nj integer li, lj integer support, sampling real value, grid(ni, nj), nvalue double precision convFunc(*), norm double precision posi, posj integer i, j, loci, locj integer offi, offj offi=nint((dble(nint(posi))-posi)*sampling) offj=nint((dble(nint(posj))-posj)*sampling) norm=0.0 do j=-support,support locj=abs(sampling*j+offj) do i=-support,support loci=abs(sampling*i+offi) norm=norm+convFunc(loci+1)*convFunc(locj+1) end do end do nvalue=value/norm do j=-support,support locj=abs(sampling*j+offj) nvalue=value*convFunc(locj+1)/norm do i=-support,support loci=abs(sampling*i+offi) grid(i+li+1,j+lj+1)=grid(i+li+1,j+lj+1)+ $ nvalue*convFunc(loci+1) end do end do return end subroutine fgrd3d (ni, nj, nk, li, lj, lk, grid, value, support, $ sampling, posi, posj, posk, convFunc) implicit none integer ni, nj, nk integer li, lj, lk integer support, sampling real value, grid(ni, nj, nk), nvalue double precision convFunc(*), norm double precision posi, posj, posk integer i, j, k, loci, locj, lock integer offi, offj, offk offi=nint((dble(nint(posi))-posi)*sampling) offj=nint((dble(nint(posj))-posj)*sampling) offk=nint((dble(nint(posk))-posk)*sampling) norm=0.0 do k=-support,support lock=abs(sampling*k+offk) do j=-support,support locj=abs(sampling*j+offj) do i=-support,support loci=abs(sampling*i+offi) norm=norm+convFunc(loci+1)*convFunc(locj+1)* $ convFunc(lock+1) end do end do end do do k=-support,support lock=abs(sampling*k+offk) do j=-support,support locj=abs(sampling*j+offj) nvalue=value*convFunc(locj+1)*convFunc(lock+1)/norm do i=-support,support loci=abs(sampling*i+offi) grid(i+li+1,j+lj+1,k+lk+1)=grid(i+li+1,j+lj+1,k+lk+1)+ $ value*convFunc(loci+1) end do end do end do return end subroutine fdgrd1d (ni, li, grid, value, support, sampling, $ posi, convFunc) implicit none integer ni integer li integer support, sampling real value, grid(ni) double precision convFunc(*), norm double precision posi integer i, loci integer offi offi=nint((dble(nint(posi))-posi)*sampling) norm=0.0 value=0.0 do i=-support,support loci=abs(sampling*i+offi) value=value+grid(i+li+1)*convFunc(loci+1) norm=norm+convFunc(loci+1) end do value=value/norm return end subroutine fdgrd2d (ni, nj, li, lj, grid, value, support, $ sampling, posi, posj, convFunc) implicit none integer ni, nj integer li, lj integer support, sampling real value, grid(ni, nj) double precision convFunc(*), norm double precision posi, posj integer i, j, loci, locj integer offi, offj offi=nint((dble(nint(posi))-posi)*sampling) offj=nint((dble(nint(posj))-posj)*sampling) norm=0.0 value=0.0 do j=-support,support locj=abs(sampling*j+offj) do i=-support,support loci=abs(sampling*i+offi) value=value+ $ grid(i+li+1,j+lj+1)*convFunc(loci+1)*convFunc(locj+1) norm=norm+convFunc(loci+1)*convFunc(locj+1) end do end do value=value/norm return end subroutine fdgrd3d (ni, nj, nk, li, lj, lk, grid, value, $ support, sampling, posi, posj, posk, convFunc) implicit none integer ni, nj, nk integer li, lj, lk integer support, sampling real value, grid(ni, nj, nk) double precision convFunc(*), norm double precision posi, posj, posk integer i, j, k, loci, locj, lock integer offi, offj, offk offi=nint((dble(nint(posi))-posi)*sampling) offj=nint((dble(nint(posj))-posj)*sampling) offk=nint((dble(nint(posk))-posk)*sampling) norm=0.0 value=0.0 do k=-support,support lock=abs(sampling*k+offk) do j=-support,support locj=abs(sampling*j+offj) do i=-support,support loci=abs(sampling*i+offi) norm=norm+convFunc(loci+1)*convFunc(locj+1)* $ convFunc(lock+1) value=value+grid(i+li+1,j+lj+1,k+lk+1)*convFunc(loci+1)* $ convFunc(locj+1)*convFunc(lock+1) end do end do end do value=value/norm return end casacore-3.7.1/scimath_f/dfftpak.f000066400000000000000000002702651476623553700170630ustar00rootroot00000000000000*======================================================================= C Correspondence concerning AIPS++ should be addressed as follows: C Internet email: casa-feedback@nrao.edu. C Postal address: AIPS++ Project Office C National Radio Astronomy Observatory C 520 Edgemont Road C Charlottesville, VA 22903-2475 USA C C $Id$ C C downloaded from http://www.netlib.org/bihar/ on Nov 1997 *----------------------------------------------------------------------- C C * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * C version 3 june 1979 C a package of fortran subprograms for the fast fourier C transform of periodic and other symmetric sequences C paul n swarztrauber C national center for atmospheric research boulder,colorado 80307 C which is sponsored by the national science foundation C modified by P. Bjorstad C C * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * C C 1. drffti initialize drfftf and drfftb C 2. drfftf forward transform of a real periodic sequence C 3. drfftb backward transform of a real coefficient array C C 4. defftf a simplified real periodic forward transform C 5. defftb a simplified real periodic backward transform C C 6. dsinti initialize dsint C 7. dsint sine transform of a real odd sequence C C 8. dcosti initialize dcost C 9. dcost cosine transform of a real even sequence C C 10. dsinqi initialize dsinqf and dsinqb C 11. dsinqf forward sine transform with odd wave numbers C 12. dsinqb unnormalized inverse of dsinqf C C 13. dcosqi initialize dcosqf and dcosqb C 14. dcosqf forward cosine transform with odd wave numbers C 15. dcosqb unnormalized inverse of dcosqf C C 16. dcffti initialize dcfftf and dcfftb C 17. dcfftf forward transform of a complex periodic sequence C 18. dcfftb unnormalized inverse of dcfftf C C Each subroutine is described below. the names used refer to C the double precision version, but the same description C applies for the single precision version. C C **************************************************************** C C subroutine drffti(n,wsave) C C **************************************************************** C C subroutine drffti initializes the array wsave which is used in C both drfftf and drfftb. the prime factorization of n together with C a tabulation of the trigonometric functions are computed and C stored in wsave. C C input parameter C C n the length of the sequence to be transformed. C C output parameter C C wsave a work array which must be dimensioned at least 2*n+15. C the same work array can be used for both drfftf and drfftb C as long as n remains unchanged. different wsave arrays C are required for different values of n. the contents of C wsave must not be changed between calls of drfftf or drfftb. C SUBROUTINE DRFFTI (N,WSAVE) DOUBLE PRECISION WSAVE(*) C IF (N .EQ. 1) RETURN C CALL DRFTI1 (N,WSAVE(N+1),WSAVE(2*N+1)) C RETURN END C SUBROUTINE DRFTI1 (N,WA,IFAC) DOUBLE PRECISION WA(*), ARG, ARGH, ARGLD, FI, TPI INTEGER IFAC(*), NTRYH(4) DATA NTRYH(1), NTRYH(2), NTRYH(3), NTRYH(4) /4, 2, 3, 5/ DATA TPI / 6.2831853071 7958647692 5286766559 00577D0/ C NL = N NF = 0 J = 0 C 101 J = J+1 IF (J.LE.4) NTRY = NTRYH(J) IF (J.GT.4) NTRY = NTRY + 2 104 NQ = NL/NTRY NR = NL-NTRY*NQ IF (NR.NE.0) GO TO 101 C 105 NF = NF+1 IFAC(NF+2) = NTRY NL = NQ IF (NTRY .NE. 2) GO TO 107 IF (NF .EQ. 1) GO TO 107 DO 106 I=2,NF IB = NF-I+2 IFAC(IB+2) = IFAC(IB+1) 106 CONTINUE IFAC(3) = 2 107 IF (NL .NE. 1) GO TO 104 IFAC(1) = N IFAC(2) = NF C ARGH = TPI/DFLOAT(N) IS = 0 NFM1 = NF-1 L1 = 1 IF (NFM1 .EQ. 0) RETURN DO 110 K1=1,NFM1 IP = IFAC(K1+2) LD = 0 L2 = L1*IP IDO = N/L2 IPM = IP-1 DO 109 J=1,IPM LD = LD+L1 I = IS ARGLD = DFLOAT(LD)*ARGH FI = 0.D0 DO 108 II=3,IDO,2 I = I+2 FI = FI+1.D0 ARG = FI*ARGLD WA(I-1) = DCOS(ARG) WA(I) = DSIN(ARG) 108 CONTINUE IS = IS+IDO 109 CONTINUE C L1 = L2 110 CONTINUE C RETURN END C C ****************************************************************** C C subroutine drfftf(n,r,wsave) C C ****************************************************************** C C subroutine drfftf computes the fourier coefficients of a real C perodic sequence (fourier analysis). the transform is defined C below at output parameter r. C C input parameters C C n the length of the array r to be transformed. the method C is most efficient when n is a product of small primes. C n may change so long as different work arrays are provided C C r a real array of length n which contains the sequence C to be transformed C C wsave a work array which must be dimensioned at least 2*n+15. C in the program that calls drfftf. the wsave array must be C initialized by calling subroutine drffti(n,wsave) and a C different wsave array must be used for each different C value of n. this initialization does not have to be C repeated so long as n remains unchanged thus subsequent C transforms can be obtained faster than the first. C the same wsave array can be used by drfftf and drfftb. C C C output parameters C C r r(1) = the sum from i=1 to i=n of r(i) C C if n is even set l =n/2 , if n is odd set l = (n+1)/2 C C then for k = 2,...,l C C r(2*k-2) = the sum from i = 1 to i = n of C C r(i)*cos((k-1)*(i-1)*2*pi/n) C C r(2*k-1) = the sum from i = 1 to i = n of C C -r(i)*sin((k-1)*(i-1)*2*pi/n) C C if n is even C C r(n) = the sum from i = 1 to i = n of C C (-1)**(i-1)*r(i) C C ***** note C this transform is unnormalized since a call of drfftf C followed by a call of drfftb will multiply the input C sequence by n. C C wsave contains results which must not be destroyed between C calls of drfftf or drfftb. C SUBROUTINE DRFFTF (N,R,WSAVE) DOUBLE PRECISION R(*), WSAVE(*) C IF (N .EQ. 1) RETURN C CALL DRFTF1 (N,R,WSAVE,WSAVE(N+1),WSAVE(2*N+1)) C RETURN END C SUBROUTINE DRADF2 (IDO,L1,CC,CH,WA1) DOUBLE PRECISION CC(IDO,L1,2), CH(IDO,2,L1), WA1(*), TI2, TR2 C DO 101 K=1,L1 CH(1,1,K) = CC(1,K,1)+CC(1,K,2) CH(IDO,2,K) = CC(1,K,1)-CC(1,K,2) 101 CONTINUE C C IF (IDO-2) 107,105,102 IF (IDO-2 .LT. 0) GOTO 107 IF (IDO-2 .EQ. 0) GOTO 105 102 IDP2 = IDO+2 DO 104 K=1,L1 DO 103 I=3,IDO,2 IC = IDP2-I TR2 = WA1(I-2)*CC(I-1,K,2)+WA1(I-1)*CC(I,K,2) TI2 = WA1(I-2)*CC(I,K,2)-WA1(I-1)*CC(I-1,K,2) CH(I,1,K) = CC(I,K,1)+TI2 CH(IC,2,K) = TI2-CC(I,K,1) CH(I-1,1,K) = CC(I-1,K,1)+TR2 CH(IC-1,2,K) = CC(I-1,K,1)-TR2 103 CONTINUE 104 CONTINUE C IF (MOD(IDO,2) .EQ. 1) RETURN 105 DO 106 K=1,L1 CH(1,2,K) = -CC(IDO,K,2) CH(IDO,1,K) = CC(IDO,K,1) 106 CONTINUE C 107 RETURN END C SUBROUTINE DRADF3 (IDO,L1,CC,CH,WA1,WA2) DOUBLE PRECISION CC(IDO,L1,3), CH(IDO,3,L1), WA1(*), WA2(*), 1 CI2, CR2, DI2, DI3, DR2, DR3, TAUI, TAUR, TI2, TI3, TR2, TR3 DATA TAUR / -0.5 D0 / DATA TAUI / 0.8660254037 8443864676 3723170752 93618D0/ C DO 101 K=1,L1 CR2 = CC(1,K,2)+CC(1,K,3) CH(1,1,K) = CC(1,K,1)+CR2 CH(1,3,K) = TAUI*(CC(1,K,3)-CC(1,K,2)) CH(IDO,2,K) = CC(1,K,1)+TAUR*CR2 101 CONTINUE C IF (IDO .EQ. 1) RETURN IDP2 = IDO+2 DO 103 K=1,L1 DO 102 I=3,IDO,2 IC = IDP2-I DR2 = WA1(I-2)*CC(I-1,K,2)+WA1(I-1)*CC(I,K,2) DI2 = WA1(I-2)*CC(I,K,2)-WA1(I-1)*CC(I-1,K,2) DR3 = WA2(I-2)*CC(I-1,K,3)+WA2(I-1)*CC(I,K,3) DI3 = WA2(I-2)*CC(I,K,3)-WA2(I-1)*CC(I-1,K,3) CR2 = DR2+DR3 CI2 = DI2+DI3 CH(I-1,1,K) = CC(I-1,K,1)+CR2 CH(I,1,K) = CC(I,K,1)+CI2 TR2 = CC(I-1,K,1)+TAUR*CR2 TI2 = CC(I,K,1)+TAUR*CI2 TR3 = TAUI*(DI2-DI3) TI3 = TAUI*(DR3-DR2) CH(I-1,3,K) = TR2+TR3 CH(IC-1,2,K) = TR2-TR3 CH(I,3,K) = TI2+TI3 CH(IC,2,K) = TI3-TI2 102 CONTINUE 103 CONTINUE C RETURN END C SUBROUTINE DRADF4 (IDO,L1,CC,CH,WA1,WA2,WA3) DOUBLE PRECISION CC(IDO,L1,4), CH(IDO,4,L1), WA1(*), WA2(*), 1 WA3(*), CI2, CI3, CI4, CR2, CR3, CR4, HSQT2, TI1, TI2, TI3, 2 TI4, TR1, TR2, TR3, TR4 DATA HSQT2 / .7071067811 8654752440 0844362104 85 D0 / C DO 101 K=1,L1 TR1 = CC(1,K,2)+CC(1,K,4) TR2 = CC(1,K,1)+CC(1,K,3) CH(1,1,K) = TR1+TR2 CH(IDO,4,K) = TR2-TR1 CH(IDO,2,K) = CC(1,K,1)-CC(1,K,3) CH(1,3,K) = CC(1,K,4)-CC(1,K,2) 101 CONTINUE C C IF (IDO-2) 107,105,102 IF (IDO-2 .LT. 0) GOTO 107 IF (IDO-2 .EQ. 0) GOTO 105 102 IDP2 = IDO+2 DO 104 K=1,L1 DO 103 I=3,IDO,2 IC = IDP2-I CR2 = WA1(I-2)*CC(I-1,K,2)+WA1(I-1)*CC(I,K,2) CI2 = WA1(I-2)*CC(I,K,2)-WA1(I-1)*CC(I-1,K,2) CR3 = WA2(I-2)*CC(I-1,K,3)+WA2(I-1)*CC(I,K,3) CI3 = WA2(I-2)*CC(I,K,3)-WA2(I-1)*CC(I-1,K,3) CR4 = WA3(I-2)*CC(I-1,K,4)+WA3(I-1)*CC(I,K,4) CI4 = WA3(I-2)*CC(I,K,4)-WA3(I-1)*CC(I-1,K,4) TR1 = CR2+CR4 TR4 = CR4-CR2 TI1 = CI2+CI4 TI4 = CI2-CI4 TI2 = CC(I,K,1)+CI3 TI3 = CC(I,K,1)-CI3 TR2 = CC(I-1,K,1)+CR3 TR3 = CC(I-1,K,1)-CR3 CH(I-1,1,K) = TR1+TR2 CH(IC-1,4,K) = TR2-TR1 CH(I,1,K) = TI1+TI2 CH(IC,4,K) = TI1-TI2 CH(I-1,3,K) = TI4+TR3 CH(IC-1,2,K) = TR3-TI4 CH(I,3,K) = TR4+TI3 CH(IC,2,K) = TR4-TI3 103 CONTINUE 104 CONTINUE IF (MOD(IDO,2) .EQ. 1) RETURN 105 CONTINUE C DO 106 K=1,L1 TI1 = -HSQT2*(CC(IDO,K,2)+CC(IDO,K,4)) TR1 = HSQT2*(CC(IDO,K,2)-CC(IDO,K,4)) CH(IDO,1,K) = TR1+CC(IDO,K,1) CH(IDO,3,K) = CC(IDO,K,1)-TR1 CH(1,2,K) = TI1-CC(IDO,K,3) CH(1,4,K) = TI1+CC(IDO,K,3) 106 CONTINUE C 107 RETURN END C SUBROUTINE DRADF5 (IDO,L1,CC,CH,WA1,WA2,WA3,WA4) DOUBLE PRECISION CC(IDO,L1,5), CH(IDO,5,L1), WA1(*), WA2(*), 1 WA3(*), WA4(*), CI2, CI3, CI4, CI5, CR2, CR3, CR4, CR5, DI2, 2 DI3, DI4, DI5, DR2, DR3, DR4, DR5, TI11, TI12, TI2, TI3, TI4, 3 TI5, TR11, TR12, TR2, TR3, TR4, TR5 DATA TR11 / 0.3090169943 7494742410 2293417182 81906D0/ DATA TI11 / 0.9510565162 9515357211 6439333379 38214D0/ DATA TR12 / -0.8090169943 7494742410 2293417182 81906D0/ DATA TI12 / 0.5877852522 9247312916 8705954639 07277D0/ C DO 101 K=1,L1 CR2 = CC(1,K,5)+CC(1,K,2) CI5 = CC(1,K,5)-CC(1,K,2) CR3 = CC(1,K,4)+CC(1,K,3) CI4 = CC(1,K,4)-CC(1,K,3) CH(1,1,K) = CC(1,K,1)+CR2+CR3 CH(IDO,2,K) = CC(1,K,1)+TR11*CR2+TR12*CR3 CH(1,3,K) = TI11*CI5+TI12*CI4 CH(IDO,4,K) = CC(1,K,1)+TR12*CR2+TR11*CR3 CH(1,5,K) = TI12*CI5-TI11*CI4 101 CONTINUE C IF (IDO .EQ. 1) RETURN IDP2 = IDO+2 DO 103 K=1,L1 DO 102 I=3,IDO,2 IC = IDP2-I DR2 = WA1(I-2)*CC(I-1,K,2)+WA1(I-1)*CC(I,K,2) DI2 = WA1(I-2)*CC(I,K,2)-WA1(I-1)*CC(I-1,K,2) DR3 = WA2(I-2)*CC(I-1,K,3)+WA2(I-1)*CC(I,K,3) DI3 = WA2(I-2)*CC(I,K,3)-WA2(I-1)*CC(I-1,K,3) DR4 = WA3(I-2)*CC(I-1,K,4)+WA3(I-1)*CC(I,K,4) DI4 = WA3(I-2)*CC(I,K,4)-WA3(I-1)*CC(I-1,K,4) DR5 = WA4(I-2)*CC(I-1,K,5)+WA4(I-1)*CC(I,K,5) DI5 = WA4(I-2)*CC(I,K,5)-WA4(I-1)*CC(I-1,K,5) CR2 = DR2+DR5 CI5 = DR5-DR2 CR5 = DI2-DI5 CI2 = DI2+DI5 CR3 = DR3+DR4 CI4 = DR4-DR3 CR4 = DI3-DI4 CI3 = DI3+DI4 CH(I-1,1,K) = CC(I-1,K,1)+CR2+CR3 CH(I,1,K) = CC(I,K,1)+CI2+CI3 TR2 = CC(I-1,K,1)+TR11*CR2+TR12*CR3 TI2 = CC(I,K,1)+TR11*CI2+TR12*CI3 TR3 = CC(I-1,K,1)+TR12*CR2+TR11*CR3 TI3 = CC(I,K,1)+TR12*CI2+TR11*CI3 TR5 = TI11*CR5+TI12*CR4 TI5 = TI11*CI5+TI12*CI4 TR4 = TI12*CR5-TI11*CR4 TI4 = TI12*CI5-TI11*CI4 CH(I-1,3,K) = TR2+TR5 CH(IC-1,2,K) = TR2-TR5 CH(I,3,K) = TI2+TI5 CH(IC,2,K) = TI5-TI2 CH(I-1,5,K) = TR3+TR4 CH(IC-1,4,K) = TR3-TR4 CH(I,5,K) = TI3+TI4 CH(IC,4,K) = TI4-TI3 102 CONTINUE 103 CONTINUE C RETURN END C SUBROUTINE DRADFG (IDO,IP,L1,IDL1,CC,C1,C2,CH,CH2,WA) DOUBLE PRECISION CC(IDO,IP,L1), C1(IDO,L1,IP), C2(IDL1,IP), 1 CH(IDO,L1,IP), CH2(IDL1,IP), WA(*), AI1, AI2, AR1, AR1H, AR2, 2 AR2H, ARG, DC2, DCP, DS2, DSP, TPI DATA TPI / 6.2831853071 7958647692 5286766559 00577D0/ C ARG = TPI/DFLOAT(IP) DCP = DCOS(ARG) DSP = DSIN(ARG) IPPH = (IP+1)/2 IPP2 = IP+2 IDP2 = IDO+2 NBD = (IDO-1)/2 IF (IDO .EQ. 1) GO TO 119 DO 101 IK=1,IDL1 CH2(IK,1) = C2(IK,1) 101 CONTINUE DO 103 J=2,IP DO 102 K=1,L1 CH(1,K,J) = C1(1,K,J) 102 CONTINUE 103 CONTINUE C IF (NBD .GT. L1) GO TO 107 IS = -IDO DO 106 J=2,IP IS = IS+IDO IDIJ = IS DO 105 I=3,IDO,2 IDIJ = IDIJ+2 DO 104 K=1,L1 CH(I-1,K,J) = WA(IDIJ-1)*C1(I-1,K,J)+WA(IDIJ)*C1(I,K,J) CH(I,K,J) = WA(IDIJ-1)*C1(I,K,J)-WA(IDIJ)*C1(I-1,K,J) 104 CONTINUE 105 CONTINUE 106 CONTINUE GO TO 111 C 107 IS = -IDO DO 110 J=2,IP IS = IS+IDO DO 109 K=1,L1 IDIJ = IS DO 108 I=3,IDO,2 IDIJ = IDIJ+2 CH(I-1,K,J) = WA(IDIJ-1)*C1(I-1,K,J)+WA(IDIJ)*C1(I,K,J) CH(I,K,J) = WA(IDIJ-1)*C1(I,K,J)-WA(IDIJ)*C1(I-1,K,J) 108 CONTINUE 109 CONTINUE 110 CONTINUE C 111 IF (NBD .LT. L1) GO TO 115 DO 114 J=2,IPPH JC = IPP2-J DO 113 K=1,L1 DO 112 I=3,IDO,2 C1(I-1,K,J) = CH(I-1,K,J)+CH(I-1,K,JC) C1(I-1,K,JC) = CH(I,K,J)-CH(I,K,JC) C1(I,K,J) = CH(I,K,J)+CH(I,K,JC) C1(I,K,JC) = CH(I-1,K,JC)-CH(I-1,K,J) 112 CONTINUE 113 CONTINUE 114 CONTINUE GO TO 121 C 115 DO 118 J=2,IPPH JC = IPP2-J DO 117 I=3,IDO,2 DO 116 K=1,L1 C1(I-1,K,J) = CH(I-1,K,J)+CH(I-1,K,JC) C1(I-1,K,JC) = CH(I,K,J)-CH(I,K,JC) C1(I,K,J) = CH(I,K,J)+CH(I,K,JC) C1(I,K,JC) = CH(I-1,K,JC)-CH(I-1,K,J) 116 CONTINUE 117 CONTINUE 118 CONTINUE GO TO 121 C 119 DO 120 IK=1,IDL1 C2(IK,1) = CH2(IK,1) 120 CONTINUE C 121 DO 123 J=2,IPPH JC = IPP2-J DO 122 K=1,L1 C1(1,K,J) = CH(1,K,J)+CH(1,K,JC) C1(1,K,JC) = CH(1,K,JC)-CH(1,K,J) 122 CONTINUE 123 CONTINUE C AR1 = 1.D0 AI1 = 0.D0 DO 127 L=2,IPPH LC = IPP2-L AR1H = DCP*AR1-DSP*AI1 AI1 = DCP*AI1+DSP*AR1 AR1 = AR1H DO 124 IK=1,IDL1 CH2(IK,L) = C2(IK,1)+AR1*C2(IK,2) CH2(IK,LC) = AI1*C2(IK,IP) 124 CONTINUE DC2 = AR1 DS2 = AI1 AR2 = AR1 AI2 = AI1 DO 126 J=3,IPPH JC = IPP2-J AR2H = DC2*AR2-DS2*AI2 AI2 = DC2*AI2+DS2*AR2 AR2 = AR2H DO 125 IK=1,IDL1 CH2(IK,L) = CH2(IK,L)+AR2*C2(IK,J) CH2(IK,LC) = CH2(IK,LC)+AI2*C2(IK,JC) 125 CONTINUE 126 CONTINUE 127 CONTINUE C DO 129 J=2,IPPH DO 128 IK=1,IDL1 CH2(IK,1) = CH2(IK,1)+C2(IK,J) 128 CONTINUE 129 CONTINUE C IF (IDO .LT. L1) GO TO 132 DO 131 K=1,L1 DO 130 I=1,IDO CC(I,1,K) = CH(I,K,1) 130 CONTINUE 131 CONTINUE GO TO 135 C 132 DO 134 I=1,IDO DO 133 K=1,L1 CC(I,1,K) = CH(I,K,1) 133 CONTINUE 134 CONTINUE C 135 DO 137 J=2,IPPH JC = IPP2-J J2 = J+J DO 136 K=1,L1 CC(IDO,J2-2,K) = CH(1,K,J) CC(1,J2-1,K) = CH(1,K,JC) 136 CONTINUE 137 CONTINUE C IF (IDO .EQ. 1) RETURN IF (NBD .LT. L1) GO TO 141 DO 140 J=2,IPPH JC = IPP2-J J2 = J+J DO 139 K=1,L1 DO 138 I=3,IDO,2 IC = IDP2-I CC(I-1,J2-1,K) = CH(I-1,K,J)+CH(I-1,K,JC) CC(IC-1,J2-2,K) = CH(I-1,K,J)-CH(I-1,K,JC) CC(I,J2-1,K) = CH(I,K,J)+CH(I,K,JC) CC(IC,J2-2,K) = CH(I,K,JC)-CH(I,K,J) 138 CONTINUE 139 CONTINUE 140 CONTINUE RETURN C 141 DO 144 J=2,IPPH JC = IPP2-J J2 = J+J DO 143 I=3,IDO,2 IC = IDP2-I DO 142 K=1,L1 CC(I-1,J2-1,K) = CH(I-1,K,J)+CH(I-1,K,JC) CC(IC-1,J2-2,K) = CH(I-1,K,J)-CH(I-1,K,JC) CC(I,J2-1,K) = CH(I,K,J)+CH(I,K,JC) CC(IC,J2-2,K) = CH(I,K,JC)-CH(I,K,J) 142 CONTINUE 143 CONTINUE 144 CONTINUE C RETURN END C SUBROUTINE DRFTF1 (N,C,CH,WA,IFAC) DOUBLE PRECISION C(*), CH(*), WA(*) INTEGER IFAC(*) C NF = IFAC(2) NA = 1 L2 = N IW = N DO 111 K1=1,NF KH = NF-K1 IP = IFAC(KH+3) L1 = L2/IP IDO = N/L2 IDL1 = IDO*L1 IW = IW-(IP-1)*IDO NA = 1-NA IF (IP .NE. 4) GO TO 102 C IX2 = IW+IDO IX3 = IX2+IDO IF (NA .NE. 0) GO TO 101 CALL DRADF4 (IDO,L1,C,CH,WA(IW),WA(IX2),WA(IX3)) GO TO 110 101 CALL DRADF4 (IDO,L1,CH,C,WA(IW),WA(IX2),WA(IX3)) GO TO 110 C 102 IF (IP .NE. 2) GO TO 104 IF (NA .NE. 0) GO TO 103 CALL DRADF2 (IDO,L1,C,CH,WA(IW)) GO TO 110 103 CALL DRADF2 (IDO,L1,CH,C,WA(IW)) GO TO 110 C 104 IF (IP .NE. 3) GO TO 106 IX2 = IW+IDO IF (NA .NE. 0) GO TO 105 CALL DRADF3 (IDO,L1,C,CH,WA(IW),WA(IX2)) GO TO 110 105 CALL DRADF3 (IDO,L1,CH,C,WA(IW),WA(IX2)) GO TO 110 C 106 IF (IP .NE. 5) GO TO 108 IX2 = IW+IDO IX3 = IX2+IDO IX4 = IX3+IDO IF (NA .NE. 0) GO TO 107 CALL DRADF5 (IDO,L1,C,CH,WA(IW),WA(IX2),WA(IX3),WA(IX4)) GO TO 110 107 CALL DRADF5 (IDO,L1,CH,C,WA(IW),WA(IX2),WA(IX3),WA(IX4)) GO TO 110 C 108 IF (IDO .EQ. 1) NA = 1-NA IF (NA .NE. 0) GO TO 109 CALL DRADFG (IDO,IP,L1,IDL1,C,C,C,CH,CH,WA(IW)) NA = 1 GO TO 110 109 CALL DRADFG (IDO,IP,L1,IDL1,CH,CH,CH,C,C,WA(IW)) NA = 0 C 110 L2 = L1 111 CONTINUE C IF (NA .EQ. 1) RETURN DO 112 I=1,N C(I) = CH(I) 112 CONTINUE C RETURN END C C ****************************************************************** C C subroutine drfftb(n,r,wsave) C C ****************************************************************** C C subroutine drfftb computes the real perodic sequence from its C fourier coefficients (fourier synthesis). the transform is defined C below at output parameter r. C C input parameters C C n the length of the array r to be transformed. the method C is most efficient when n is a product of small primes. C n may change so long as different work arrays are provided C C r a real array of length n which contains the sequence C to be transformed C C wsave a work array which must be dimensioned at least 2*n+15. C in the program that calls drfftb. the wsave array must be C initialized by calling subroutine drffti(n,wsave) and a C different wsave array must be used for each different C value of n. this initialization does not have to be C repeated so long as n remains unchanged thus subsequent C transforms can be obtained faster than the first. C the same wsave array can be used by drfftf and drfftb. C C C output parameters C C r for n even and for i = 1,...,n C C r(i) = r(1)+(-1)**(i-1)*r(n) C C plus the sum from k=2 to k=n/2 of C C 2.*r(2*k-2)*cos((k-1)*(i-1)*2*pi/n) C C -2.*r(2*k-1)*sin((k-1)*(i-1)*2*pi/n) C C for n odd and for i = 1,...,n C C r(i) = r(1) plus the sum from k=2 to k=(n+1)/2 of C C 2.*r(2*k-2)*cos((k-1)*(i-1)*2*pi/n) C C -2.*r(2*k-1)*sin((k-1)*(i-1)*2*pi/n) C C ***** note C this transform is unnormalized since a call of drfftf C followed by a call of drfftb will multiply the input C sequence by n. C C wsave contains results which must not be destroyed between C calls of drfftb or drfftf. C SUBROUTINE DRFFTB (N,R,WSAVE) DOUBLE PRECISION R(*), WSAVE(*) C IF (N .EQ. 1) RETURN C CALL DRFTB1 (N,R,WSAVE,WSAVE(N+1),WSAVE(2*N+1)) C RETURN END C SUBROUTINE DRADB2 (IDO,L1,CC,CH,WA1) DOUBLE PRECISION CC(IDO,2,L1), CH(IDO,L1,2), WA1(*), TI2, TR2 C DO 101 K=1,L1 CH(1,K,1) = CC(1,1,K)+CC(IDO,2,K) CH(1,K,2) = CC(1,1,K)-CC(IDO,2,K) 101 CONTINUE C C IF (IDO-2) 107,105,102 IF (IDO-2 .LT. 0) GOTO 107 IF (IDO-2 .EQ. 0) GOTO 105 102 IDP2 = IDO+2 DO 104 K=1,L1 DO 103 I=3,IDO,2 IC = IDP2-I CH(I-1,K,1) = CC(I-1,1,K)+CC(IC-1,2,K) TR2 = CC(I-1,1,K)-CC(IC-1,2,K) CH(I,K,1) = CC(I,1,K)-CC(IC,2,K) TI2 = CC(I,1,K)+CC(IC,2,K) CH(I-1,K,2) = WA1(I-2)*TR2-WA1(I-1)*TI2 CH(I,K,2) = WA1(I-2)*TI2+WA1(I-1)*TR2 103 CONTINUE 104 CONTINUE C IF (MOD(IDO,2) .EQ. 1) RETURN 105 DO 106 K=1,L1 CH(IDO,K,1) = CC(IDO,1,K)+CC(IDO,1,K) CH(IDO,K,2) = -(CC(1,2,K)+CC(1,2,K)) 106 CONTINUE C 107 RETURN END C SUBROUTINE DRADB3 (IDO,L1,CC,CH,WA1,WA2) DOUBLE PRECISION CC(IDO,3,L1), CH(IDO,L1,3), WA1(*), WA2(*), 1 CI2, CI3, CR2, CR3, DI2, DI3, DR2, DR3, TAUI, TAUR, TI2, TR2 DATA TAUR / -0.5 D0 / DATA TAUI / 0.8660254037 8443864676 3723170752 93618D0/ C DO 101 K=1,L1 TR2 = CC(IDO,2,K)+CC(IDO,2,K) CR2 = CC(1,1,K)+TAUR*TR2 CH(1,K,1) = CC(1,1,K)+TR2 CI3 = TAUI*(CC(1,3,K)+CC(1,3,K)) CH(1,K,2) = CR2-CI3 CH(1,K,3) = CR2+CI3 101 CONTINUE C IF (IDO .EQ. 1) RETURN IDP2 = IDO+2 DO 103 K=1,L1 DO 102 I=3,IDO,2 IC = IDP2-I TR2 = CC(I-1,3,K)+CC(IC-1,2,K) CR2 = CC(I-1,1,K)+TAUR*TR2 CH(I-1,K,1) = CC(I-1,1,K)+TR2 TI2 = CC(I,3,K)-CC(IC,2,K) CI2 = CC(I,1,K)+TAUR*TI2 CH(I,K,1) = CC(I,1,K)+TI2 CR3 = TAUI*(CC(I-1,3,K)-CC(IC-1,2,K)) CI3 = TAUI*(CC(I,3,K)+CC(IC,2,K)) DR2 = CR2-CI3 DR3 = CR2+CI3 DI2 = CI2+CR3 DI3 = CI2-CR3 CH(I-1,K,2) = WA1(I-2)*DR2-WA1(I-1)*DI2 CH(I,K,2) = WA1(I-2)*DI2+WA1(I-1)*DR2 CH(I-1,K,3) = WA2(I-2)*DR3-WA2(I-1)*DI3 CH(I,K,3) = WA2(I-2)*DI3+WA2(I-1)*DR3 102 CONTINUE 103 CONTINUE C RETURN END C SUBROUTINE DRADB4 (IDO,L1,CC,CH,WA1,WA2,WA3) DOUBLE PRECISION CC(IDO,4,L1), CH(IDO,L1,4), WA1(*), WA2(*), 1 WA3(*), CI2, CI3, CI4, CR2, CR3, CR4, SQRT2, TI1, TI2, TI3, * TI4,TR1, TR2, TR3, TR4 DATA SQRT2 / 1.414213562 3730950488 0168872420 970 D0 / C DO 101 K=1,L1 TR1 = CC(1,1,K)-CC(IDO,4,K) TR2 = CC(1,1,K)+CC(IDO,4,K) TR3 = CC(IDO,2,K)+CC(IDO,2,K) TR4 = CC(1,3,K)+CC(1,3,K) CH(1,K,1) = TR2+TR3 CH(1,K,2) = TR1-TR4 CH(1,K,3) = TR2-TR3 CH(1,K,4) = TR1+TR4 101 CONTINUE C C IF (IDO-2) 107,105,102 IF (IDO-2 .LT. 0) GOTO 107 IF (IDO-2 .EQ. 0) GOTO 105 102 IDP2 = IDO+2 DO 104 K=1,L1 DO 103 I=3,IDO,2 IC = IDP2-I TI1 = CC(I,1,K)+CC(IC,4,K) TI2 = CC(I,1,K)-CC(IC,4,K) TI3 = CC(I,3,K)-CC(IC,2,K) TR4 = CC(I,3,K)+CC(IC,2,K) TR1 = CC(I-1,1,K)-CC(IC-1,4,K) TR2 = CC(I-1,1,K)+CC(IC-1,4,K) TI4 = CC(I-1,3,K)-CC(IC-1,2,K) TR3 = CC(I-1,3,K)+CC(IC-1,2,K) CH(I-1,K,1) = TR2+TR3 CR3 = TR2-TR3 CH(I,K,1) = TI2+TI3 CI3 = TI2-TI3 CR2 = TR1-TR4 CR4 = TR1+TR4 CI2 = TI1+TI4 CI4 = TI1-TI4 CH(I-1,K,2) = WA1(I-2)*CR2-WA1(I-1)*CI2 CH(I,K,2) = WA1(I-2)*CI2+WA1(I-1)*CR2 CH(I-1,K,3) = WA2(I-2)*CR3-WA2(I-1)*CI3 CH(I,K,3) = WA2(I-2)*CI3+WA2(I-1)*CR3 CH(I-1,K,4) = WA3(I-2)*CR4-WA3(I-1)*CI4 CH(I,K,4) = WA3(I-2)*CI4+WA3(I-1)*CR4 103 CONTINUE 104 CONTINUE IF (MOD(IDO,2) .EQ. 1) RETURN C 105 CONTINUE DO 106 K=1,L1 TI1 = CC(1,2,K)+CC(1,4,K) TI2 = CC(1,4,K)-CC(1,2,K) TR1 = CC(IDO,1,K)-CC(IDO,3,K) TR2 = CC(IDO,1,K)+CC(IDO,3,K) CH(IDO,K,1) = TR2+TR2 CH(IDO,K,2) = SQRT2*(TR1-TI1) CH(IDO,K,3) = TI2+TI2 CH(IDO,K,4) = -SQRT2*(TR1+TI1) 106 CONTINUE C 107 RETURN END C SUBROUTINE DRADB5 (IDO,L1,CC,CH,WA1,WA2,WA3,WA4) DOUBLE PRECISION CC(IDO,5,L1), CH(IDO,L1,5), WA1(*), WA2(*), 1 WA3(*), WA4(*), CI2, CI3, CI4, CI5, CR2, CR3, CR4, CR5, 2 DI2, DI3, DI4, DI5, DR2, DR3, DR4, DR5, TI11, TI12, TI2, TI3, 3 TI4, TI5, TR11, TR12, TR2, TR3, TR4, TR5 DATA TR11 / 0.3090169943 7494742410 2293417182 81906D0/ DATA TI11 / 0.9510565162 9515357211 6439333379 38214D0/ DATA TR12 / -0.8090169943 7494742410 2293417182 81906D0/ DATA TI12 / 0.5877852522 9247312916 8705954639 07277D0/ C DO 101 K=1,L1 TI5 = CC(1,3,K)+CC(1,3,K) TI4 = CC(1,5,K)+CC(1,5,K) TR2 = CC(IDO,2,K)+CC(IDO,2,K) TR3 = CC(IDO,4,K)+CC(IDO,4,K) CH(1,K,1) = CC(1,1,K)+TR2+TR3 CR2 = CC(1,1,K)+TR11*TR2+TR12*TR3 CR3 = CC(1,1,K)+TR12*TR2+TR11*TR3 CI5 = TI11*TI5+TI12*TI4 CI4 = TI12*TI5-TI11*TI4 CH(1,K,2) = CR2-CI5 CH(1,K,3) = CR3-CI4 CH(1,K,4) = CR3+CI4 CH(1,K,5) = CR2+CI5 101 CONTINUE IF (IDO .EQ. 1) RETURN C IDP2 = IDO+2 DO 103 K=1,L1 DO 102 I=3,IDO,2 IC = IDP2-I TI5 = CC(I,3,K)+CC(IC,2,K) TI2 = CC(I,3,K)-CC(IC,2,K) TI4 = CC(I,5,K)+CC(IC,4,K) TI3 = CC(I,5,K)-CC(IC,4,K) TR5 = CC(I-1,3,K)-CC(IC-1,2,K) TR2 = CC(I-1,3,K)+CC(IC-1,2,K) TR4 = CC(I-1,5,K)-CC(IC-1,4,K) TR3 = CC(I-1,5,K)+CC(IC-1,4,K) CH(I-1,K,1) = CC(I-1,1,K)+TR2+TR3 CH(I,K,1) = CC(I,1,K)+TI2+TI3 CR2 = CC(I-1,1,K)+TR11*TR2+TR12*TR3 CI2 = CC(I,1,K)+TR11*TI2+TR12*TI3 CR3 = CC(I-1,1,K)+TR12*TR2+TR11*TR3 CI3 = CC(I,1,K)+TR12*TI2+TR11*TI3 CR5 = TI11*TR5+TI12*TR4 CI5 = TI11*TI5+TI12*TI4 CR4 = TI12*TR5-TI11*TR4 CI4 = TI12*TI5-TI11*TI4 DR3 = CR3-CI4 DR4 = CR3+CI4 DI3 = CI3+CR4 DI4 = CI3-CR4 DR5 = CR2+CI5 DR2 = CR2-CI5 DI5 = CI2-CR5 DI2 = CI2+CR5 CH(I-1,K,2) = WA1(I-2)*DR2-WA1(I-1)*DI2 CH(I,K,2) = WA1(I-2)*DI2+WA1(I-1)*DR2 CH(I-1,K,3) = WA2(I-2)*DR3-WA2(I-1)*DI3 CH(I,K,3) = WA2(I-2)*DI3+WA2(I-1)*DR3 CH(I-1,K,4) = WA3(I-2)*DR4-WA3(I-1)*DI4 CH(I,K,4) = WA3(I-2)*DI4+WA3(I-1)*DR4 CH(I-1,K,5) = WA4(I-2)*DR5-WA4(I-1)*DI5 CH(I,K,5) = WA4(I-2)*DI5+WA4(I-1)*DR5 102 CONTINUE 103 CONTINUE C RETURN END C SUBROUTINE DRADBG (IDO,IP,L1,IDL1,CC,C1,C2,CH,CH2,WA) DOUBLE PRECISION CC(IDO,IP,L1), C1(IDO,L1,IP), C2(IDL1,IP), 1 CH(IDO,L1,IP), CH2(IDL1,IP), WA(*), AI1, AI2, AR1, AR1H, AR2, 2 AR2H, ARG, DC2, DCP, DS2, DSP, TPI DATA TPI / 6.2831853071 7958647692 5286766559 00577D0/ C ARG = TPI/DFLOAT(IP) DCP = DCOS(ARG) DSP = DSIN(ARG) IDP2 = IDO+2 NBD = (IDO-1)/2 IPP2 = IP+2 IPPH = (IP+1)/2 IF (IDO .LT. L1) GO TO 103 DO 102 K=1,L1 DO 101 I=1,IDO CH(I,K,1) = CC(I,1,K) 101 CONTINUE 102 CONTINUE GO TO 106 C 103 DO 105 I=1,IDO DO 104 K=1,L1 CH(I,K,1) = CC(I,1,K) 104 CONTINUE 105 CONTINUE C 106 DO 108 J=2,IPPH JC = IPP2-J J2 = J+J DO 107 K=1,L1 CH(1,K,J) = CC(IDO,J2-2,K)+CC(IDO,J2-2,K) CH(1,K,JC) = CC(1,J2-1,K)+CC(1,J2-1,K) 107 CONTINUE 108 CONTINUE C IF (IDO .EQ. 1) GO TO 116 IF (NBD .LT. L1) GO TO 112 DO 111 J=2,IPPH JC = IPP2-J DO 110 K=1,L1 DO 109 I=3,IDO,2 IC = IDP2-I CH(I-1,K,J) = CC(I-1,2*J-1,K)+CC(IC-1,2*J-2,K) CH(I-1,K,JC) = CC(I-1,2*J-1,K)-CC(IC-1,2*J-2,K) CH(I,K,J) = CC(I,2*J-1,K)-CC(IC,2*J-2,K) CH(I,K,JC) = CC(I,2*J-1,K)+CC(IC,2*J-2,K) 109 CONTINUE 110 CONTINUE 111 CONTINUE GO TO 116 C 112 DO 115 J=2,IPPH JC = IPP2-J DO 114 I=3,IDO,2 IC = IDP2-I DO 113 K=1,L1 CH(I-1,K,J) = CC(I-1,2*J-1,K)+CC(IC-1,2*J-2,K) CH(I-1,K,JC) = CC(I-1,2*J-1,K)-CC(IC-1,2*J-2,K) CH(I,K,J) = CC(I,2*J-1,K)-CC(IC,2*J-2,K) CH(I,K,JC) = CC(I,2*J-1,K)+CC(IC,2*J-2,K) 113 CONTINUE 114 CONTINUE 115 CONTINUE C 116 AR1 = 1. AI1 = 0. DO 120 L=2,IPPH LC = IPP2-L AR1H = DCP*AR1-DSP*AI1 AI1 = DCP*AI1+DSP*AR1 AR1 = AR1H DO 117 IK=1,IDL1 C2(IK,L) = CH2(IK,1)+AR1*CH2(IK,2) C2(IK,LC) = AI1*CH2(IK,IP) 117 CONTINUE DC2 = AR1 DS2 = AI1 AR2 = AR1 AI2 = AI1 DO 119 J=3,IPPH JC = IPP2-J AR2H = DC2*AR2-DS2*AI2 AI2 = DC2*AI2+DS2*AR2 AR2 = AR2H DO 118 IK=1,IDL1 C2(IK,L) = C2(IK,L)+AR2*CH2(IK,J) C2(IK,LC) = C2(IK,LC)+AI2*CH2(IK,JC) 118 CONTINUE 119 CONTINUE 120 CONTINUE C DO 122 J=2,IPPH DO 121 IK=1,IDL1 CH2(IK,1) = CH2(IK,1)+CH2(IK,J) 121 CONTINUE 122 CONTINUE C DO 124 J=2,IPPH JC = IPP2-J DO 123 K=1,L1 CH(1,K,J) = C1(1,K,J)-C1(1,K,JC) CH(1,K,JC) = C1(1,K,J)+C1(1,K,JC) 123 CONTINUE 124 CONTINUE C IF (IDO .EQ. 1) GO TO 132 IF (NBD .LT. L1) GO TO 128 DO 127 J=2,IPPH JC = IPP2-J DO 126 K=1,L1 DO 125 I=3,IDO,2 CH(I-1,K,J) = C1(I-1,K,J)-C1(I,K,JC) CH(I-1,K,JC) = C1(I-1,K,J)+C1(I,K,JC) CH(I,K,J) = C1(I,K,J)+C1(I-1,K,JC) CH(I,K,JC) = C1(I,K,J)-C1(I-1,K,JC) 125 CONTINUE 126 CONTINUE 127 CONTINUE GO TO 132 C 128 DO 131 J=2,IPPH JC = IPP2-J DO 130 I=3,IDO,2 DO 129 K=1,L1 CH(I-1,K,J) = C1(I-1,K,J)-C1(I,K,JC) CH(I-1,K,JC) = C1(I-1,K,J)+C1(I,K,JC) CH(I,K,J) = C1(I,K,J)+C1(I-1,K,JC) CH(I,K,JC) = C1(I,K,J)-C1(I-1,K,JC) 129 CONTINUE 130 CONTINUE 131 CONTINUE 132 CONTINUE C IF (IDO .EQ. 1) RETURN DO 133 IK=1,IDL1 C2(IK,1) = CH2(IK,1) 133 CONTINUE C DO 135 J=2,IP DO 134 K=1,L1 C1(1,K,J) = CH(1,K,J) 134 CONTINUE 135 CONTINUE C IF (NBD .GT. L1) GO TO 139 IS = -IDO DO 138 J=2,IP IS = IS+IDO IDIJ = IS DO 137 I=3,IDO,2 IDIJ = IDIJ+2 DO 136 K=1,L1 C1(I-1,K,J) = WA(IDIJ-1)*CH(I-1,K,J)-WA(IDIJ)*CH(I,K,J) C1(I,K,J) = WA(IDIJ-1)*CH(I,K,J)+WA(IDIJ)*CH(I-1,K,J) 136 CONTINUE 137 CONTINUE 138 CONTINUE GO TO 143 C 139 IS = -IDO DO 142 J=2,IP IS = IS+IDO DO 141 K=1,L1 IDIJ = IS DO 140 I=3,IDO,2 IDIJ = IDIJ+2 C1(I-1,K,J) = WA(IDIJ-1)*CH(I-1,K,J)-WA(IDIJ)*CH(I,K,J) C1(I,K,J) = WA(IDIJ-1)*CH(I,K,J)+WA(IDIJ)*CH(I-1,K,J) 140 CONTINUE 141 CONTINUE 142 CONTINUE C 143 RETURN END C SUBROUTINE DRFTB1 (N,C,CH,WA,IFAC) DOUBLE PRECISION C(*), CH(*), WA(*) INTEGER IFAC(*) C NF = IFAC(2) NA = 0 L1 = 1 IW = 1 DO 116 K1=1,NF IP = IFAC(K1+2) L2 = IP*L1 IDO = N/L2 IDL1 = IDO*L1 IF (IP .NE. 4) GO TO 103 IX2 = IW+IDO IX3 = IX2+IDO IF (NA .NE. 0) GO TO 101 CALL DRADB4 (IDO,L1,C,CH,WA(IW),WA(IX2),WA(IX3)) GO TO 102 101 CALL DRADB4 (IDO,L1,CH,C,WA(IW),WA(IX2),WA(IX3)) 102 NA = 1-NA GO TO 115 C 103 IF (IP .NE. 2) GO TO 106 IF (NA .NE. 0) GO TO 104 CALL DRADB2 (IDO,L1,C,CH,WA(IW)) GO TO 105 104 CALL DRADB2 (IDO,L1,CH,C,WA(IW)) 105 NA = 1-NA GO TO 115 C 106 IF (IP .NE. 3) GO TO 109 IX2 = IW+IDO IF (NA .NE. 0) GO TO 107 CALL DRADB3 (IDO,L1,C,CH,WA(IW),WA(IX2)) GO TO 108 107 CALL DRADB3 (IDO,L1,CH,C,WA(IW),WA(IX2)) 108 NA = 1-NA GO TO 115 C 109 IF (IP .NE. 5) GO TO 112 IX2 = IW+IDO IX3 = IX2+IDO IX4 = IX3+IDO IF (NA .NE. 0) GO TO 110 CALL DRADB5 (IDO,L1,C,CH,WA(IW),WA(IX2),WA(IX3),WA(IX4)) GO TO 111 110 CALL DRADB5 (IDO,L1,CH,C,WA(IW),WA(IX2),WA(IX3),WA(IX4)) 111 NA = 1-NA GO TO 115 C 112 IF (NA .NE. 0) GO TO 113 CALL DRADBG (IDO,IP,L1,IDL1,C,C,C,CH,CH,WA(IW)) GO TO 114 113 CALL DRADBG (IDO,IP,L1,IDL1,CH,CH,CH,C,C,WA(IW)) 114 IF (IDO .EQ. 1) NA = 1-NA 115 L1 = L2 IW = IW+(IP-1)*IDO 116 CONTINUE C IF (NA .EQ. 0) RETURN DO 117 I=1,N C(I) = CH(I) 117 CONTINUE C RETURN END C C ****************************************************************** C C subroutine defftf(n,r,azero,a,b,wsave) C C ****************************************************************** C C subroutine defftf computes the fourier coefficients of a real C perodic sequence (fourier analysis). the transform is defined C below at output parameters azero,a and b. defftf is a simplified C version of drfftf. it is not as fast as drfftf since scaling C and initialization are computed for each transform. the repeated C initialization can be suppressed by removeing the statment C ( call deffti(n,wsave) ) from both defftf and defftb and inserting C it at the appropriate place in your program. C C input parameters C C n the length of the array r to be transformed. the method C is must efficient when n is the product of small primes. C C r a real array of length n which contains the sequence C to be transformed. r is not destroyed. C C wsave a work array with at least 3*n+15 locations. C C output parameters C C azero the sum from i=1 to i=n of r(i)/n C C a,b for n even b(n/2)=0. and a(n/2) is the sum from i=1 to C i=n of (-1)**(i-1)*r(i)/n C C for n even define kmax=n/2-1 C for n odd define kmax=(n-1)/2 C C then for k=1,...,kmax C C a(k) equals the sum from i=1 to i=n of C C 2./n*r(i)*cos(k*(i-1)*2*pi/n) C C b(k) equals the sum from i=1 to i=n of C C 2./n*r(i)*sin(k*(i-1)*2*pi/n) C SUBROUTINE DEFFTF (N,R,AZERO,A,B,WSAVE) C C VERSION 3 JUNE 1979 C DOUBLE PRECISION R(*), AZERO, A(*), B(*), WSAVE(*), CF, CFM C C IF (N-2) 101,102,103 IF (N-2 .EQ. 0) GOTO 102 IF (N-2 .GT. 0) GOTO 103 101 AZERO = R(1) RETURN C 102 AZERO = .5D0*(R(1)+R(2)) A(1) = .5D0*(R(1)-R(2)) RETURN C C to supress repeated initialization, remove the following statement C ( call deffti(n,wsave) ) from both defftf and defftb and insert it C at the beginning of your program following the definition of n. C 103 CALL DEFFTI (N,WSAVE) C DO 104 I=1,N WSAVE(I) = R(I) 104 CONTINUE C CALL DRFFTF (N,WSAVE,WSAVE(N+1)) C CF = 2.D0/DFLOAT(N) CFM = -CF AZERO = .5D0*CF*WSAVE(1) NS2 = (N+1)/2 NS2M = NS2-1 DO 105 I=1,NS2M A(I) = CF*WSAVE(2*I) B(I) = CFM*WSAVE(2*I+1) 105 CONTINUE IF (MOD(N,2) .EQ. 0) A(NS2) = .5D0*CF*WSAVE(N) C RETURN END C SUBROUTINE DEFFTI (N,WSAVE) DOUBLE PRECISION WSAVE(*) C IF (N .EQ. 1) RETURN C CALL DEFFT1 (N,WSAVE(2*N+1),WSAVE(3*N+1)) C RETURN END C SUBROUTINE DEFFT1 (N,WA,IFAC) DOUBLE PRECISION WA(*), ARG1, ARGH, CH1, CH1H, DCH1, DSH1, SH1, 1 TPI INTEGER IFAC(*), NTRYH(4) DATA NTRYH(1), NTRYH(2), NTRYH(3), NTRYH(4) /4, 2, 3, 5/ DATA TPI / 6.2831853071 7958647692 5286766559 00577D0/ C NL = N NF = 0 J = 0 C 101 J = J+1 IF (J.LE.4) NTRY = NTRYH(J) IF (J.GT.4) NTRY = NTRY + 2 104 NQ = NL/NTRY NR = NL-NTRY*NQ IF (NR.NE.0) GO TO 101 C 105 NF = NF+1 IFAC(NF+2) = NTRY NL = NQ IF (NTRY .NE. 2) GO TO 107 IF (NF .EQ. 1) GO TO 107 DO 106 I=2,NF IB = NF-I+2 IFAC(IB+2) = IFAC(IB+1) 106 CONTINUE IFAC(3) = 2 C 107 IF (NL .NE. 1) GO TO 104 C IFAC(1) = N IFAC(2) = NF ARGH = TPI/DFLOAT(N) IS = 0 NFM1 = NF-1 L1 = 1 IF (NFM1 .EQ. 0) RETURN C DO 111 K1=1,NFM1 IP = IFAC(K1+2) L2 = L1*IP IDO = N/L2 IPM = IP-1 ARG1 = DFLOAT(L1)*ARGH CH1 = 1.D0 SH1 = 0.D0 DCH1 = DCOS(ARG1) DSH1 = DSIN(ARG1) C DO 110 J=1,IPM CH1H = DCH1*CH1-DSH1*SH1 SH1 = DCH1*SH1+DSH1*CH1 CH1 = CH1H I = IS+2 WA(I-1) = CH1 WA(I) = SH1 IF (IDO .LT. 5) GO TO 109 DO 108 II=5,IDO,2 I = I+2 WA(I-1) = CH1*WA(I-3)-SH1*WA(I-2) WA(I) = CH1*WA(I-2)+SH1*WA(I-3) 108 CONTINUE 109 IS = IS+IDO 110 CONTINUE C L1 = L2 111 CONTINUE C RETURN END C C ****************************************************************** C C subroutine defftb(n,r,azero,a,b,wsave) C C ****************************************************************** C C subroutine defftb computes a real perodic sequence from its C fourier coefficients (fourier synthesis). the transform is C defined below at output parameter r. defftb is a simplified C version of drfftb. it is not as fast as drfftb since scaling and C initialization are computed for each transform. the repeated C initialization can be suppressed by removeing the statment C ( call deffti(n,wsave) ) from both defftf and defftb and inserting C ( call deffti(n,wsave) ) from both defftf and defftb and inserting C it at the appropriate place in your program. C C input parameters C C n the length of the output array r. the method is most C efficient when n is the product of small primes. C C azero the constant fourier coefficient C C a,b arrays which contain the remaining fourier coefficients C these arrays are not destroyed. C C the length of these arrays depends on whether n is even or C odd. C C if n is even n/2 locations are required C if n is odd (n-1)/2 locations are required C C wsave a work array with at least 3*n+15 locations. C C C output parameters C C r if n is even define kmax=n/2 C if n is odd define kmax=(n-1)/2 C C then for i=1,...,n C C r(i)=azero plus the sum from k=1 to k=kmax of C C a(k)*cos(k*(i-1)*2*pi/n)+b(k)*sin(k*(i-1)*2*pi/n) C C ********************* complex notation ************************** C C for j=1,...,n C C r(j) equals the sum from k=-kmax to k=kmax of C C c(k)*exp(i*k*(j-1)*2*pi/n) C C where C C c(k) = .5*cmplx(a(k),-b(k)) for k=1,...,kmax C C c(-k) = conjg(c(k)) C C c(0) = azero C C and i=sqrt(-1) C C *************** amplitude - phase notation *********************** C C for i=1,...,n C C r(i) equals azero plus the sum from k=1 to k=kmax of C C alpha(k)*cos(k*(i-1)*2*pi/n+beta(k)) C C where C C alpha(k) = sqrt(a(k)*a(k)+b(k)*b(k)) C C cos(beta(k))=a(k)/alpha(k) C C sin(beta(k))=-b(k)/alpha(k) C C SUBROUTINE DEFFTB (N,R,AZERO,A,B,WSAVE) DOUBLE PRECISION R(*), AZERO, A(*), B(*), WSAVE(*) C C IF (N-2) 101,102,103 IF (N-2 .EQ. 0) GOTO 102 IF (N-2 .GT. 0) GOTO 103 101 R(1) = AZERO RETURN C 102 R(1) = AZERO+A(1) R(2) = AZERO-A(1) RETURN C C to supress repeated initialization, remove the following statement C ( call deffti(n,wsave) ) from both defftf and defftb and insert it C at the beginning of your program following the definition of n. C 103 CALL DEFFTI (N,WSAVE) C NS2 = (N-1)/2 DO 104 I=1,NS2 R(2*I) = .5D0*A(I) R(2*I+1) = -.5D0*B(I) 104 CONTINUE R(1) = AZERO IF (MOD(N,2) .EQ. 0) R(N) = A(NS2+1) C CALL DRFFTB (N,R,WSAVE(N+1)) C RETURN END C C ****************************************************************** C C subroutine dsinti(n,wsave) C C ****************************************************************** C C subroutine dsinti initializes the array wsave which is used in C subroutine dsint. the prime factorization of n together with C a tabulation of the trigonometric functions are computed and C stored in wsave. C C input parameter C C n the length of the sequence to be transformed. the method C is most efficient when n+1 is a product of small primes. C C output parameter C C wsave a work array with at least int(2.5*n+15) locations. C different wsave arrays are required for different values C of n. the contents of wsave must not be changed between C calls of dsint. C SUBROUTINE DSINTI (N,WSAVE) DOUBLE PRECISION WSAVE(*), DT, FK, PI DATA PI / 3.141592653 5897932384 6264338327 950 D0 / C IF (N .LE. 1) RETURN NP1 = N+1 NS2 = N/2 DT = PI/DFLOAT(NP1) FK = 0.D0 DO 101 K=1,NS2 FK = FK+1.D0 WSAVE(K) = 2.D0*DSIN(FK*DT) 101 CONTINUE C CALL DRFFTI (NP1,WSAVE(NS2+1)) C RETURN END C C ****************************************************************** C C subroutine dsint(n,x,wsave) C C ****************************************************************** C C subroutine dsint computes the discrete fourier sine transform C of an odd sequence x(i). the transform is defined below at C output parameter x. C C dsint is the unnormalized inverse of itself since a call of dsint C followed by another call of dsint will multiply the input sequence C x by 2*(n+1). C C the array wsave which is used by subroutine dsint must be C initialized by calling subroutine dsinti(n,wsave). C C input parameters C C n the length of the sequence to be transformed. the method C is most efficient when n+1 is the product of small primes. C C x an array which contains the sequence to be transformed C C ************important************* C C x must be dimensioned at least n+1 C C wsave a work array with dimension at least int(2.5*n+15) C in the program that calls dsint. the wsave array must be C initialized by calling subroutine dsinti(n,wsave) and a C different wsave array must be used for each different C value of n. this initialization does not have to be C repeated so long as n remains unchanged thus subsequent C transforms can be obtained faster than the first. C C output parameters C C x for i=1,...,n C C x(i)= the sum from k=1 to k=n C C 2*x(k)*sin(k*i*pi/(n+1)) C C a call of dsint followed by another call of C dsint will multiply the sequence x by 2*(n+1). C hence dsint is the unnormalized inverse C of itself. C C wsave contains initialization calculations which must not be C destroyed between calls of dsint. C SUBROUTINE DSINT (N,X,WSAVE) DOUBLE PRECISION X(*), WSAVE(*), SQRT3, T1, T2, X1, XH, XIM1 DATA SQRT3 / 1.7320508075 6887729352 7446341505 87237D0/ C C IF (N-2) 101,102,103 IF (N-2 .EQ. 0) GOTO 102 IF (N-2 .GT. 0) GOTO 103 101 X(1) = X(1)+X(1) RETURN C 102 XH = SQRT3*(X(1)+X(2)) X(2) = SQRT3*(X(1)-X(2)) X(1) = XH RETURN C 103 NP1 = N+1 NS2 = N/2 X1 = X(1) X(1) = 0.D0 DO 104 K=1,NS2 KC = NP1-K T1 = X1-X(KC) T2 = WSAVE(K)*(X1+X(KC)) X1 = X(K+1) X(K+1) = T1+T2 X(KC+1) = T2-T1 104 CONTINUE MODN = MOD(N,2) IF (MODN .NE. 0) X(NS2+2) = 4.D0*X1 C CALL DRFFTF (NP1,X,WSAVE(NS2+1)) C X(1) = .5D0*X(1) DO 105 I=3,N,2 XIM1 = X(I-1) X(I-1) = -X(I) X(I) = X(I-2)+XIM1 105 CONTINUE IF (MODN.EQ.0) X(N) = -X(N+1) C RETURN END C C ****************************************************************** C C subroutine dcosti(n,wsave) C C ****************************************************************** C C subroutine dcosti initializes the array wsave which is used in C subroutine dcost. the prime factorization of n together with C a tabulation of the trigonometric functions are computed and C stored in wsave. C C input parameter C C n the length of the sequence to be transformed. the method C is most efficient when n-1 is a product of small primes. C C output parameter C C wsave a work array which must be dimensioned at least 3*n+15. C different wsave arrays are required for different values C of n. the contents of wsave must not be changed between C calls of dcost. C SUBROUTINE DCOSTI (N,WSAVE) DOUBLE PRECISION WSAVE(*), DT, FK, PI DATA PI / 3.141592653 5897932384 6264338327 950 D0 / C IF (N .LE. 3) RETURN C NM1 = N-1 NP1 = N+1 NS2 = N/2 DT = PI/DFLOAT(NM1) FK = 0.D0 DO 101 K=2,NS2 KC = NP1-K FK = FK+1.D0 WSAVE(K) = 2.D0*DSIN(FK*DT) WSAVE(KC) = 2.D0*DCOS(FK*DT) 101 CONTINUE C CALL DRFFTI (NM1,WSAVE(N+1)) C RETURN END C C ****************************************************************** C C subroutine dcost(n,x,wsave) C C ****************************************************************** C C subroutine dcost computes the discrete fourier cosine transform C of an even sequence x(i). the transform is defined below at output C parameter x. C C dcost is the unnormalized inverse of itself since a call of dcost C followed by another call of dcost will multiply the input sequence C x by 2*(n-1). the transform is defined below at output parameter x C C the array wsave which is used by subroutine dcost must be C initialized by calling subroutine dcosti(n,wsave). C C input parameters C C n the length of the sequence x. n must be greater than 1. C the method is most efficient when n-1 is a product of C small primes. C C x an array which contains the sequence to be transformed C C wsave a work array which must be dimensioned at least 3*n+15 C in the program that calls dcost. the wsave array must be C initialized by calling subroutine dcosti(n,wsave) and a C different wsave array must be used for each different C value of n. this initialization does not have to be C repeated so long as n remains unchanged thus subsequent C transforms can be obtained faster than the first. C C output parameters C C x for i=1,...,n C C x(i) = x(1)+(-1)**(i-1)*x(n) C C + the sum from k=2 to k=n-1 C C 2*x(k)*cos((k-1)*(i-1)*pi/(n-1)) C C a call of dcost followed by another call of C dcost will multiply the sequence x by 2*(n-1) C hence dcost is the unnormalized inverse C of itself. C C wsave contains initialization calculations which must not be C destroyed between calls of dcost. C SUBROUTINE DCOST (N,X,WSAVE) DOUBLE PRECISION X(*), WSAVE(*), C1, T1, T2, TX2, X1H, X1P3, XI, 1 XIM2 C NM1 = N-1 NP1 = N+1 NS2 = N/2 C IF (N-2) 106,101,102 IF (N-2 .LT. 0) GOTO 106 IF (N-2 .GT. 0) GOTO 102 101 X1H = X(1)+X(2) X(2) = X(1)-X(2) X(1) = X1H RETURN C 102 IF (N .GT. 3) GO TO 103 X1P3 = X(1)+X(3) TX2 = X(2)+X(2) X(2) = X(1)-X(3) X(1) = X1P3+TX2 X(3) = X1P3-TX2 RETURN C 103 C1 = X(1)-X(N) X(1) = X(1)+X(N) DO 104 K=2,NS2 KC = NP1-K T1 = X(K)+X(KC) T2 = X(K)-X(KC) C1 = C1+WSAVE(KC)*T2 T2 = WSAVE(K)*T2 X(K) = T1-T2 X(KC) = T1+T2 104 CONTINUE MODN = MOD(N,2) IF (MODN .NE. 0) X(NS2+1) = X(NS2+1)+X(NS2+1) C CALL DRFFTF (NM1,X,WSAVE(N+1)) C XIM2 = X(2) X(2) = C1 DO 105 I=4,N,2 XI = X(I) X(I) = X(I-2)-X(I-1) X(I-1) = XIM2 XIM2 = XI 105 CONTINUE IF (MODN .NE. 0) X(N) = XIM2 C 106 RETURN END C C ****************************************************************** C C subroutine dsinqi(n,wsave) C C ****************************************************************** C C subroutine dsinqi initializes the array wsave which is used in C both dsinqf and dsinqb. the prime factorization of n together with C a tabulation of the trigonometric functions are computed and C stored in wsave. C C input parameter C C n the length of the sequence to be transformed. the method C is most efficient when n is a product of small primes. C C output parameter C C wsave a work array which must be dimensioned at least 3*n+15. C the same work array can be used for both dsinqf and dsinqb C as long as n remains unchanged. different wsave arrays C are required for different values of n. the contents of C wsave must not be changed between calls of dsinqf or C dsinqb. C SUBROUTINE DSINQI (N,WSAVE) DOUBLE PRECISION WSAVE(*) C CALL DCOSQI (N,WSAVE) C RETURN END C C ****************************************************************** C C subroutine dsinqf(n,x,wsave) C C ****************************************************************** C C subroutine dsinqf computes the fast fourier transform of quarter C wave data. that is , dsinqf computes the coefficients in a sine C series representation with only odd wave numbers. the transform C is defined below at output parameter x. C C dsinqb is the unnormalized inverse of dsinqf -- a call of dsinqf C followed by a call of dsinqb will multiply the input sequence x C by 4*n. C C the array wsave which is used by subroutine dsinqf must be C initialized by calling subroutine dsinqi(n,wsave). C C C input parameters C C n the length of the array x to be transformed. the method C is most efficient when n is a product of small primes. C C x an array which contains the sequence to be transformed C C wsave a work array which must be dimensioned at least 3*n+15. C in the program that calls dsinqf. the wsave array must be C initialized by calling subroutine dsinqi(n,wsave) and a C different wsave array must be used for each different C value of n. this initialization does not have to be C repeated so long as n remains unchanged thus subsequent C transforms can be obtained faster than the first. C C output parameters C C x for i=1,...,n C C x(i) = (-1)**(i-1)*x(n) C C + the sum from k=1 to k=n-1 of C C 2*x(k)*sin((2*i-1)*k*pi/(2*n)) C C a call of dsinqf followed by a call of C dsinqb will multiply the sequence x by 4*n. C therefore dsinqb is the unnormalized inverse C of dsinqf. C C wsave contains initialization calculations which must not C be destroyed between calls of dsinqf or dsinqb. C SUBROUTINE DSINQF (N,X,WSAVE) DOUBLE PRECISION X(*), WSAVE(*), XHOLD C IF (N .EQ. 1) RETURN C NS2 = N/2 DO 101 K=1,NS2 KC = N-K XHOLD = X(K) X(K) = X(KC+1) X(KC+1) = XHOLD 101 CONTINUE C CALL DCOSQF (N,X,WSAVE) C DO 102 K=2,N,2 X(K) = -X(K) 102 CONTINUE C RETURN END C C ****************************************************************** C C subroutine dsinqb(n,x,wsave) C C ****************************************************************** C C subroutine dsinqb computes the fast fourier transform of quarter C wave data. that is , dsinqb computes a sequence from its C representation in terms of a sine series with odd wave numbers. C the transform is defined below at output parameter x. C C dsinqf is the unnormalized inverse of dsinqb -- a call of dsinqb C followed by a call of dsinqf will multiply the input sequence x C by 4*n. C C the array wsave which is used by subroutine dsinqb must be C initialized by calling subroutine dsinqi(n,wsave). C C C input parameters C C n the length of the array x to be transformed. the method C is most efficient when n is a product of small primes. C C x an array which contains the sequence to be transformed C C wsave a work array which must be dimensioned at least 3*n+15. C in the program that calls dsinqb. the wsave array must be C initialized by calling subroutine dsinqi(n,wsave) and a C different wsave array must be used for each different C value of n. this initialization does not have to be C repeated so long as n remains unchanged thus subsequent C transforms can be obtained faster than the first. C C output parameters C C x for i=1,...,n C C x(i)= the sum from k=1 to k=n of C C 4*x(k)*sin((2k-1)*i*pi/(2*n)) C C a call of dsinqb followed by a call of C dsinqf will multiply the sequence x by 4*n. C therefore dsinqf is the unnormalized inverse C of dsinqb. C C wsave contains initialization calculations which must not C be destroyed between calls of dsinqb or dsinqf. C SUBROUTINE DSINQB (N,X,WSAVE) DOUBLE PRECISION X(*), WSAVE(*), XHOLD C IF (N .GT. 1) GO TO 101 X(1) = 4.D0*X(1) RETURN C 101 NS2 = N/2 DO 102 K=2,N,2 X(K) = -X(K) 102 CONTINUE C CALL DCOSQB (N,X,WSAVE) C DO 103 K=1,NS2 KC = N-K XHOLD = X(K) X(K) = X(KC+1) X(KC+1) = XHOLD 103 CONTINUE C RETURN END C C ****************************************************************** C C subroutine dcosqi(n,wsave) C C ****************************************************************** C C subroutine dcosqi initializes the array wsave which is used in C both dcosqf and dcosqb. the prime factorization of n together with C a tabulation of the trigonometric functions are computed and C stored in wsave. C C input parameter C C n the length of the array to be transformed. the method C is most efficient when n is a product of small primes. C C output parameter C C wsave a work array which must be dimensioned at least 3*n+15. C the same work array can be used for both dcosqf and dcosqb C as long as n remains unchanged. different wsave arrays C are required for different values of n. the contents of C wsave must not be changed between calls of dcosqf or C dcosqb. C SUBROUTINE DCOSQI (N,WSAVE) DOUBLE PRECISION WSAVE(*), DT, FK, PIH DATA PIH / 1.570796326 7948966192 3132169163 975 D0 / C DT = PIH/DFLOAT(N) FK = 0.D0 DO 101 K=1,N FK = FK+1.D0 WSAVE(K) = DCOS(FK*DT) 101 CONTINUE C CALL DRFFTI (N,WSAVE(N+1)) C RETURN END C C ****************************************************************** C C subroutine dcosqf(n,x,wsave) C C ****************************************************************** C C subroutine dcosqf computes the fast fourier transform of quarter C wave data. that is , dcosqf computes the coefficients in a cosine C series representation with only odd wave numbers. the transform C is defined below at output parameter x C C dcosqf is the unnormalized inverse of dcosqb -- a call of dcosqf C followed by a call of dcosqb will multiply the input sequence x C by 4*n. C C the array wsave which is used by subroutine dcosqf must be C initialized by calling subroutine dcosqi(n,wsave). C C C input parameters C C n the length of the array x to be transformed. the method C is most efficient when n is a product of small primes. C C x an array which contains the sequence to be transformed C C wsave a work array which must be dimensioned at least 3*n+15 C in the program that calls dcosqf. the wsave array must be C initialized by calling subroutine dcosqi(n,wsave) and a C different wsave array must be used for each different C value of n. this initialization does not have to be C repeated so long as n remains unchanged thus subsequent C transforms can be obtained faster than the first. C C output parameters C C x for i=1,...,n C C x(i) = x(1) plus the sum from k=2 to k=n of C C 2*x(k)*cos((2*i-1)*(k-1)*pi/(2*n)) C C a call of dcosqf followed by a call of C dcosqb will multiply the sequence x by 4*n. C therefore dcosqb is the unnormalized inverse C of dcosqf. C C wsave contains initialization calculations which must not C be destroyed between calls of dcosqf or dcosqb. C SUBROUTINE DCOSQF (N,X,WSAVE) DOUBLE PRECISION X(*), WSAVE(*), SQRT2, TSQX DATA SQRT2 / 1.414213562 3730950488 0168872420 970 D0 / C C IF (N-2) 102,101,103 IF (N-2 .LT. 0) GOTO 102 IF (N-2 .GT. 0) GOTO 103 101 TSQX = SQRT2*X(2) X(2) = X(1)-TSQX X(1) = X(1)+TSQX 102 RETURN C 103 CALL DCSQF1 (N,X,WSAVE,WSAVE(N+1)) C RETURN END C SUBROUTINE DCSQF1 (N,X,W,XH) DOUBLE PRECISION X(*), W(*), XH(*), XIM1 C NS2 = (N+1)/2 NP2 = N+2 DO 101 K=2,NS2 KC = NP2-K XH(K) = X(K)+X(KC) XH(KC) = X(K)-X(KC) 101 CONTINUE MODN = MOD(N,2) IF (MODN .EQ. 0) XH(NS2+1) = X(NS2+1)+X(NS2+1) C DO 102 K=2,NS2 KC = NP2-K X(K) = W(K-1)*XH(KC)+W(KC-1)*XH(K) X(KC) = W(K-1)*XH(K)-W(KC-1)*XH(KC) 102 CONTINUE IF (MODN .EQ. 0) X(NS2+1) = W(NS2)*XH(NS2+1) C CALL DRFFTF (N,X,XH) C DO 103 I=3,N,2 XIM1 = X(I-1)-X(I) X(I) = X(I-1)+X(I) X(I-1) = XIM1 103 CONTINUE C RETURN END C C ****************************************************************** C C subroutine dcosqb(n,x,wsave) C C ****************************************************************** C C subroutine dcosqb computes the fast fourier transform of quarter C wave data. that is , dcosqb computes a sequence from its C representation in terms of a cosine series with odd wave numbers. C the transform is defined below at output parameter x. C C dcosqb is the unnormalized inverse of dcosqf -- a call of dcosqb C followed by a call of dcosqf will multiply the input sequence x C by 4*n. C C the array wsave which is used by subroutine dcosqb must be C initialized by calling subroutine dcosqi(n,wsave). C C C input parameters C C n the length of the array x to be transformed. the method C is most efficient when n is a product of small primes. C C x an array which contains the sequence to be transformed C C wsave a work array that must be dimensioned at least 3*n+15 C in the program that calls dcosqb. the wsave array must be C initialized by calling subroutine dcosqi(n,wsave) and a C different wsave array must be used for each different C value of n. this initialization does not have to be C repeated so long as n remains unchanged thus subsequent C transforms can be obtained faster than the first. C C output parameters C C x for i=1,...,n C C x(i)= the sum from k=1 to k=n of C C 4*x(k)*cos((2*k-1)*(i-1)*pi/(2*n)) C C a call of dcosqb followed by a call of C dcosqf will multiply the sequence x by 4*n. C therefore dcosqf is the unnormalized inverse C of dcosqb. C C wsave contains initialization calculations which must not C be destroyed between calls of dcosqb or dcosqf. C SUBROUTINE DCOSQB (N,X,WSAVE) DOUBLE PRECISION X(*), WSAVE(*), TSQRT2, X1 DATA TSQRT2 / 2.828427124 7461900976 0337744841 94 D0 / C C IF (N-2) 101,102,103 IF (N-2 .EQ. 0) GOTO 102 IF (N-2 .GT. 0) GOTO 103 101 X(1) = 4.D0*X(1) RETURN C 102 X1 = 4.D0*(X(1)+X(2)) X(2) = TSQRT2*(X(1)-X(2)) X(1) = X1 RETURN C 103 CALL DCSQB1 (N,X,WSAVE,WSAVE(N+1)) C RETURN C END SUBROUTINE DCSQB1 (N,X,W,XH) DOUBLE PRECISION X(*), W(*), XH(*), XIM1 C NS2 = (N+1)/2 NP2 = N+2 DO 101 I=3,N,2 XIM1 = X(I-1)+X(I) X(I) = X(I)-X(I-1) X(I-1) = XIM1 101 CONTINUE X(1) = X(1)+X(1) MODN = MOD(N,2) IF (MODN .EQ. 0) X(N) = X(N)+X(N) C CALL DRFFTB (N,X,XH) C DO 102 K=2,NS2 KC = NP2-K XH(K) = W(K-1)*X(KC)+W(KC-1)*X(K) XH(KC) = W(K-1)*X(K)-W(KC-1)*X(KC) 102 CONTINUE C IF (MODN .EQ. 0) X(NS2+1) = W(NS2)*(X(NS2+1)+X(NS2+1)) DO 103 K=2,NS2 KC = NP2-K X(K) = XH(K)+XH(KC) X(KC) = XH(K)-XH(KC) 103 CONTINUE X(1) = X(1)+X(1) C RETURN END C C ****************************************************************** C C subroutine dcffti(n,wsave) C C ****************************************************************** C C subroutine dcffti initializes the array wsave which is used in C both dcfftf and dcfftb. the prime factorization of n together with C a tabulation of the trigonometric functions are computed and C stored in wsave. C C input parameter C C n the length of the sequence to be transformed C C output parameter C C wsave a work array which must be dimensioned at least 4*n+15 C the same work array can be used for both dcfftf and dcfftb C as long as n remains unchanged. different wsave arrays C are required for different values of n. the contents of C wsave must not be changed between calls of dcfftf or dcfftb. C SUBROUTINE DCFFTI (N,WSAVE) DOUBLE PRECISION WSAVE(*) C IF (N .EQ. 1) RETURN C IW1 = N+N+1 IW2 = IW1+N+N CALL DCFTI1 (N,WSAVE(IW1),WSAVE(IW2)) C RETURN END SUBROUTINE DCFTI1 (N,WA,IFAC) DOUBLE PRECISION WA(*), ARG, ARGH, ARGLD, FI, TPI INTEGER IFAC(*), NTRYH(4) DATA NTRYH(1), NTRYH(2), NTRYH(3), NTRYH(4) /3, 4, 2, 5/ DATA TPI / 6.2831853071 7958647692 5286766559 00577D0/ C NL = N NF = 0 J = 0 C 101 J = J+1 IF (J.LE.4) NTRY = NTRYH(J) IF (J.GT.4) NTRY = NTRY + 2 104 NQ = NL/NTRY NR = NL-NTRY*NQ IF (NR.NE.0) GO TO 101 C 105 NF = NF+1 IFAC(NF+2) = NTRY NL = NQ IF (NTRY .NE. 2) GO TO 107 IF (NF .EQ. 1) GO TO 107 DO 106 I=2,NF IB = NF-I+2 IFAC(IB+2) = IFAC(IB+1) 106 CONTINUE IFAC(3) = 2 C 107 IF (NL .NE. 1) GO TO 104 C IFAC(1) = N IFAC(2) = NF C ARGH = TPI/DFLOAT(N) I = 2 L1 = 1 DO 110 K1=1,NF IP = IFAC(K1+2) LD = 0 L2 = L1*IP IDO = N/L2 IDOT = IDO+IDO+2 IPM = IP-1 C DO 109 J=1,IPM I1 = I WA(I-1) = 1.D0 WA(I) = 0.D0 LD = LD+L1 FI = 0.D0 ARGLD = DFLOAT(LD)*ARGH DO 108 II=4,IDOT,2 I = I+2 FI = FI+1.D0 ARG = FI*ARGLD WA(I-1) = DCOS(ARG) WA(I) = DSIN(ARG) 108 CONTINUE IF (IP .LE. 5) GO TO 109 WA(I1-1) = WA(I-1) WA(I1) = WA(I) 109 CONTINUE C L1 = L2 110 CONTINUE C RETURN END C C ****************************************************************** C C subroutine dcfftf(n,c,wsave) C C ****************************************************************** C C subroutine dcfftf computes the forward complex discrete fourier C transform (the fourier analysis). equivalently , dcfftf computes C the fourier coefficients of a complex periodic sequence. C the transform is defined below at output parameter c. C C the transform is not normalized. to obtain a normalized transform C the output must be divided by n. otherwise a call of dcfftf C followed by a call of dcfftb will multiply the sequence by n. C C the array wsave which is used by subroutine dcfftf must be C initialized by calling subroutine dcffti(n,wsave). C C input parameters C C C n the length of the complex sequence c. the method is C more efficient when n is the product of small primes. n C C c a complex array of length n which contains the sequence C C wsave a real work array which must be dimensioned at least 4n+15 C in the program that calls dcfftf. the wsave array must be C initialized by calling subroutine dcffti(n,wsave) and a C different wsave array must be used for each different C value of n. this initialization does not have to be C repeated so long as n remains unchanged thus subsequent C transforms can be obtained faster than the first. C the same wsave array can be used by dcfftf and dcfftb. C C output parameters C C for j=1,...,n C C c(j)=the sum from k=1,...,n of C C c(k)*exp(-i*j*k*2*pi/n) C C where i=sqrt(-1) C C wsave contains initialization calculations which must not be C destroyed between calls of subroutine dcfftf or dcfftb C SUBROUTINE DCFFTF (N,C,WSAVE) DOUBLE PRECISION C(*), WSAVE(*) C IF (N .EQ. 1) RETURN C IW1 = N+N+1 IW2 = IW1+N+N CALL DCFTF1 (N,C,WSAVE,WSAVE(IW1),WSAVE(IW2)) C RETURN END SUBROUTINE DCFTF1 (N,C,CH,WA,IFAC) DOUBLE PRECISION C(*), CH(*), WA(*) INTEGER IFAC(*) C NF = IFAC(2) NA = 0 L1 = 1 IW = 1 DO 116 K1=1,NF IP = IFAC(K1+2) L2 = IP*L1 IDO = N/L2 IDOT = IDO+IDO IDL1 = IDOT*L1 IF (IP .NE. 4) GO TO 103 IX2 = IW+IDOT IX3 = IX2+IDOT IF (NA .NE. 0) GO TO 101 CALL DPSSF4 (IDOT,L1,C,CH,WA(IW),WA(IX2),WA(IX3)) GO TO 102 101 CALL DPSSF4 (IDOT,L1,CH,C,WA(IW),WA(IX2),WA(IX3)) 102 NA = 1-NA GO TO 115 C 103 IF (IP .NE. 2) GO TO 106 IF (NA .NE. 0) GO TO 104 CALL DPSSF2 (IDOT,L1,C,CH,WA(IW)) GO TO 105 104 CALL DPSSF2 (IDOT,L1,CH,C,WA(IW)) 105 NA = 1-NA GO TO 115 C 106 IF (IP .NE. 3) GO TO 109 IX2 = IW+IDOT IF (NA .NE. 0) GO TO 107 CALL DPSSF3 (IDOT,L1,C,CH,WA(IW),WA(IX2)) GO TO 108 107 CALL DPSSF3 (IDOT,L1,CH,C,WA(IW),WA(IX2)) 108 NA = 1-NA GO TO 115 C 109 IF (IP .NE. 5) GO TO 112 IX2 = IW+IDOT IX3 = IX2+IDOT IX4 = IX3+IDOT IF (NA .NE. 0) GO TO 110 CALL DPSSF5 (IDOT,L1,C,CH,WA(IW),WA(IX2),WA(IX3),WA(IX4)) GO TO 111 110 CALL DPSSF5 (IDOT,L1,CH,C,WA(IW),WA(IX2),WA(IX3),WA(IX4)) 111 NA = 1-NA GO TO 115 C 112 IF (NA .NE. 0) GO TO 113 CALL DPSSF (NAC,IDOT,IP,L1,IDL1,C,C,C,CH,CH,WA(IW)) GO TO 114 113 CALL DPSSF (NAC,IDOT,IP,L1,IDL1,CH,CH,CH,C,C,WA(IW)) 114 IF (NAC .NE. 0) NA = 1-NA C 115 L1 = L2 IW = IW+(IP-1)*IDOT 116 CONTINUE IF (NA .EQ. 0) RETURN C N2 = N+N DO 117 I=1,N2 C(I) = CH(I) 117 CONTINUE C RETURN END SUBROUTINE DPSSF (NAC,IDO,IP,L1,IDL1,CC,C1,C2,CH,CH2,WA) DOUBLE PRECISION CC(IDO,IP,L1), C1(IDO,L1,IP), C2(IDL1,IP), 1 CH(IDO,L1,IP), CH2(IDL1,IP), WA(*), WAI, WAR C IDOT = IDO/2 NT = IP*IDL1 IPP2 = IP+2 IPPH = (IP+1)/2 IDP = IP*IDO C IF (IDO .LT. L1) GO TO 106 DO 103 J=2,IPPH JC = IPP2-J DO 102 K=1,L1 DO 101 I=1,IDO CH(I,K,J) = CC(I,J,K)+CC(I,JC,K) CH(I,K,JC) = CC(I,J,K)-CC(I,JC,K) 101 CONTINUE 102 CONTINUE 103 CONTINUE C DO 105 K=1,L1 DO 104 I=1,IDO CH(I,K,1) = CC(I,1,K) 104 CONTINUE 105 CONTINUE GO TO 112 C 106 DO 109 J=2,IPPH JC = IPP2-J DO 108 I=1,IDO DO 107 K=1,L1 CH(I,K,J) = CC(I,J,K)+CC(I,JC,K) CH(I,K,JC) = CC(I,J,K)-CC(I,JC,K) 107 CONTINUE 108 CONTINUE 109 CONTINUE C DO 111 I=1,IDO DO 110 K=1,L1 CH(I,K,1) = CC(I,1,K) 110 CONTINUE 111 CONTINUE C 112 IDL = 2-IDO INC = 0 DO 116 L=2,IPPH LC = IPP2-L IDL = IDL+IDO DO 113 IK=1,IDL1 C2(IK,L) = CH2(IK,1)+WA(IDL-1)*CH2(IK,2) C2(IK,LC) = -WA(IDL)*CH2(IK,IP) 113 CONTINUE IDLJ = IDL INC = INC+IDO DO 115 J=3,IPPH JC = IPP2-J IDLJ = IDLJ+INC IF (IDLJ .GT. IDP) IDLJ = IDLJ-IDP WAR = WA(IDLJ-1) WAI = WA(IDLJ) DO 114 IK=1,IDL1 C2(IK,L) = C2(IK,L)+WAR*CH2(IK,J) C2(IK,LC) = C2(IK,LC)-WAI*CH2(IK,JC) 114 CONTINUE 115 CONTINUE 116 CONTINUE C DO 118 J=2,IPPH DO 117 IK=1,IDL1 CH2(IK,1) = CH2(IK,1)+CH2(IK,J) 117 CONTINUE 118 CONTINUE C DO 120 J=2,IPPH JC = IPP2-J DO 119 IK=2,IDL1,2 CH2(IK-1,J) = C2(IK-1,J)-C2(IK,JC) CH2(IK-1,JC) = C2(IK-1,J)+C2(IK,JC) CH2(IK,J) = C2(IK,J)+C2(IK-1,JC) CH2(IK,JC) = C2(IK,J)-C2(IK-1,JC) 119 CONTINUE 120 CONTINUE C NAC = 1 IF (IDO .EQ. 2) RETURN NAC = 0 C DO 121 IK=1,IDL1 C2(IK,1) = CH2(IK,1) 121 CONTINUE C DO 123 J=2,IP DO 122 K=1,L1 C1(1,K,J) = CH(1,K,J) C1(2,K,J) = CH(2,K,J) 122 CONTINUE 123 CONTINUE C IF (IDOT .GT. L1) GO TO 127 IDIJ = 0 DO 126 J=2,IP IDIJ = IDIJ+2 DO 125 I=4,IDO,2 IDIJ = IDIJ+2 DO 124 K=1,L1 C1(I-1,K,J) = WA(IDIJ-1)*CH(I-1,K,J)+WA(IDIJ)*CH(I,K,J) C1(I,K,J) = WA(IDIJ-1)*CH(I,K,J)-WA(IDIJ)*CH(I-1,K,J) 124 CONTINUE 125 CONTINUE 126 CONTINUE RETURN C 127 IDJ = 2-IDO DO 130 J=2,IP IDJ = IDJ+IDO DO 129 K=1,L1 IDIJ = IDJ DO 128 I=4,IDO,2 IDIJ = IDIJ+2 C1(I-1,K,J) = WA(IDIJ-1)*CH(I-1,K,J)+WA(IDIJ)*CH(I,K,J) C1(I,K,J) = WA(IDIJ-1)*CH(I,K,J)-WA(IDIJ)*CH(I-1,K,J) 128 CONTINUE 129 CONTINUE 130 CONTINUE C RETURN END SUBROUTINE DPSSF2 (IDO,L1,CC,CH,WA1) DOUBLE PRECISION CC(IDO,2,L1), CH(IDO,L1,2), WA1(*), TI2, TR2 C IF (IDO .GT. 2) GO TO 102 DO 101 K=1,L1 CH(1,K,1) = CC(1,1,K)+CC(1,2,K) CH(1,K,2) = CC(1,1,K)-CC(1,2,K) CH(2,K,1) = CC(2,1,K)+CC(2,2,K) CH(2,K,2) = CC(2,1,K)-CC(2,2,K) 101 CONTINUE RETURN C 102 DO 104 K=1,L1 DO 103 I=2,IDO,2 CH(I-1,K,1) = CC(I-1,1,K)+CC(I-1,2,K) TR2 = CC(I-1,1,K)-CC(I-1,2,K) CH(I,K,1) = CC(I,1,K)+CC(I,2,K) TI2 = CC(I,1,K)-CC(I,2,K) CH(I,K,2) = WA1(I-1)*TI2-WA1(I)*TR2 CH(I-1,K,2) = WA1(I-1)*TR2+WA1(I)*TI2 103 CONTINUE 104 CONTINUE C RETURN END SUBROUTINE DPSSF3 (IDO,L1,CC,CH,WA1,WA2) DOUBLE PRECISION CC(IDO,3,L1), CH(IDO,L1,3), WA1(*), WA2(*), 1 CI2, CI3, CR2, CR3, DI2, DI3, DR2, DR3, TAUI, TAUR, TI2, TR2 DATA TAUR / -0.5 D0 / DATA TAUI / -0.8660254037 8443864676 3723170752 93618D0/ C IF (IDO .NE. 2) GO TO 102 DO 101 K=1,L1 TR2 = CC(1,2,K)+CC(1,3,K) CR2 = CC(1,1,K)+TAUR*TR2 CH(1,K,1) = CC(1,1,K)+TR2 TI2 = CC(2,2,K)+CC(2,3,K) CI2 = CC(2,1,K)+TAUR*TI2 CH(2,K,1) = CC(2,1,K)+TI2 CR3 = TAUI*(CC(1,2,K)-CC(1,3,K)) CI3 = TAUI*(CC(2,2,K)-CC(2,3,K)) CH(1,K,2) = CR2-CI3 CH(1,K,3) = CR2+CI3 CH(2,K,2) = CI2+CR3 CH(2,K,3) = CI2-CR3 101 CONTINUE RETURN C 102 DO 104 K=1,L1 DO 103 I=2,IDO,2 TR2 = CC(I-1,2,K)+CC(I-1,3,K) CR2 = CC(I-1,1,K)+TAUR*TR2 CH(I-1,K,1) = CC(I-1,1,K)+TR2 TI2 = CC(I,2,K)+CC(I,3,K) CI2 = CC(I,1,K)+TAUR*TI2 CH(I,K,1) = CC(I,1,K)+TI2 CR3 = TAUI*(CC(I-1,2,K)-CC(I-1,3,K)) CI3 = TAUI*(CC(I,2,K)-CC(I,3,K)) DR2 = CR2-CI3 DR3 = CR2+CI3 DI2 = CI2+CR3 DI3 = CI2-CR3 CH(I,K,2) = WA1(I-1)*DI2-WA1(I)*DR2 CH(I-1,K,2) = WA1(I-1)*DR2+WA1(I)*DI2 CH(I,K,3) = WA2(I-1)*DI3-WA2(I)*DR3 CH(I-1,K,3) = WA2(I-1)*DR3+WA2(I)*DI3 103 CONTINUE 104 CONTINUE C RETURN END SUBROUTINE DPSSF4 (IDO,L1,CC,CH,WA1,WA2,WA3) DOUBLE PRECISION CC(IDO,4,L1), CH(IDO,L1,4), WA1(*), WA2(*), 1 WA3(*), CI2, CI3, CI4, CR2, CR3, CR4, TI1, TI2, TI3, TI4, 2 TR1, TR2, TR3, TR4 C IF (IDO .NE. 2) GO TO 102 DO 101 K=1,L1 TI1 = CC(2,1,K)-CC(2,3,K) TI2 = CC(2,1,K)+CC(2,3,K) TR4 = CC(2,2,K)-CC(2,4,K) TI3 = CC(2,2,K)+CC(2,4,K) TR1 = CC(1,1,K)-CC(1,3,K) TR2 = CC(1,1,K)+CC(1,3,K) TI4 = CC(1,4,K)-CC(1,2,K) TR3 = CC(1,2,K)+CC(1,4,K) CH(1,K,1) = TR2+TR3 CH(1,K,3) = TR2-TR3 CH(2,K,1) = TI2+TI3 CH(2,K,3) = TI2-TI3 CH(1,K,2) = TR1+TR4 CH(1,K,4) = TR1-TR4 CH(2,K,2) = TI1+TI4 CH(2,K,4) = TI1-TI4 101 CONTINUE RETURN C 102 DO 104 K=1,L1 DO 103 I=2,IDO,2 TI1 = CC(I,1,K)-CC(I,3,K) TI2 = CC(I,1,K)+CC(I,3,K) TI3 = CC(I,2,K)+CC(I,4,K) TR4 = CC(I,2,K)-CC(I,4,K) TR1 = CC(I-1,1,K)-CC(I-1,3,K) TR2 = CC(I-1,1,K)+CC(I-1,3,K) TI4 = CC(I-1,4,K)-CC(I-1,2,K) TR3 = CC(I-1,2,K)+CC(I-1,4,K) CH(I-1,K,1) = TR2+TR3 CR3 = TR2-TR3 CH(I,K,1) = TI2+TI3 CI3 = TI2-TI3 CR2 = TR1+TR4 CR4 = TR1-TR4 CI2 = TI1+TI4 CI4 = TI1-TI4 CH(I-1,K,2) = WA1(I-1)*CR2+WA1(I)*CI2 CH(I,K,2) = WA1(I-1)*CI2-WA1(I)*CR2 CH(I-1,K,3) = WA2(I-1)*CR3+WA2(I)*CI3 CH(I,K,3) = WA2(I-1)*CI3-WA2(I)*CR3 CH(I-1,K,4) = WA3(I-1)*CR4+WA3(I)*CI4 CH(I,K,4) = WA3(I-1)*CI4-WA3(I)*CR4 103 CONTINUE 104 CONTINUE C RETURN END SUBROUTINE DPSSF5 (IDO,L1,CC,CH,WA1,WA2,WA3,WA4) DOUBLE PRECISION CC(IDO,5,L1), CH(IDO,L1,5), WA1(*), WA2(*), 1 WA3(*), WA4(*), CI2, CI3, CI4, CI5, CR2, CR3, CR4, CR5, DI2, 2 DI3, DI4, DI5, DR2, DR3, DR4, DR5, TI11, TI12, TI2, TI3, TI4, 3 TI5, TR11, TR12, TR2, TR3, TR4, TR5 DATA TR11 / 0.3090169943 7494742410 2293417182 81906D0/ DATA TI11 / -0.9510565162 9515357211 6439333379 38214D0/ DATA TR12 / -0.8090169943 7494742410 2293417182 81906D0/ DATA TI12 / -0.5877852522 9247312916 8705954639 07277D0/ C IF (IDO .NE. 2) GO TO 102 DO 101 K=1,L1 TI5 = CC(2,2,K)-CC(2,5,K) TI2 = CC(2,2,K)+CC(2,5,K) TI4 = CC(2,3,K)-CC(2,4,K) TI3 = CC(2,3,K)+CC(2,4,K) TR5 = CC(1,2,K)-CC(1,5,K) TR2 = CC(1,2,K)+CC(1,5,K) TR4 = CC(1,3,K)-CC(1,4,K) TR3 = CC(1,3,K)+CC(1,4,K) CH(1,K,1) = CC(1,1,K)+TR2+TR3 CH(2,K,1) = CC(2,1,K)+TI2+TI3 CR2 = CC(1,1,K)+TR11*TR2+TR12*TR3 CI2 = CC(2,1,K)+TR11*TI2+TR12*TI3 CR3 = CC(1,1,K)+TR12*TR2+TR11*TR3 CI3 = CC(2,1,K)+TR12*TI2+TR11*TI3 CR5 = TI11*TR5+TI12*TR4 CI5 = TI11*TI5+TI12*TI4 CR4 = TI12*TR5-TI11*TR4 CI4 = TI12*TI5-TI11*TI4 CH(1,K,2) = CR2-CI5 CH(1,K,5) = CR2+CI5 CH(2,K,2) = CI2+CR5 CH(2,K,3) = CI3+CR4 CH(1,K,3) = CR3-CI4 CH(1,K,4) = CR3+CI4 CH(2,K,4) = CI3-CR4 CH(2,K,5) = CI2-CR5 101 CONTINUE RETURN C 102 DO 104 K=1,L1 DO 103 I=2,IDO,2 TI5 = CC(I,2,K)-CC(I,5,K) TI2 = CC(I,2,K)+CC(I,5,K) TI4 = CC(I,3,K)-CC(I,4,K) TI3 = CC(I,3,K)+CC(I,4,K) TR5 = CC(I-1,2,K)-CC(I-1,5,K) TR2 = CC(I-1,2,K)+CC(I-1,5,K) TR4 = CC(I-1,3,K)-CC(I-1,4,K) TR3 = CC(I-1,3,K)+CC(I-1,4,K) CH(I-1,K,1) = CC(I-1,1,K)+TR2+TR3 CH(I,K,1) = CC(I,1,K)+TI2+TI3 CR2 = CC(I-1,1,K)+TR11*TR2+TR12*TR3 CI2 = CC(I,1,K)+TR11*TI2+TR12*TI3 CR3 = CC(I-1,1,K)+TR12*TR2+TR11*TR3 CI3 = CC(I,1,K)+TR12*TI2+TR11*TI3 CR5 = TI11*TR5+TI12*TR4 CI5 = TI11*TI5+TI12*TI4 CR4 = TI12*TR5-TI11*TR4 CI4 = TI12*TI5-TI11*TI4 DR3 = CR3-CI4 DR4 = CR3+CI4 DI3 = CI3+CR4 DI4 = CI3-CR4 DR5 = CR2+CI5 DR2 = CR2-CI5 DI5 = CI2-CR5 DI2 = CI2+CR5 CH(I-1,K,2) = WA1(I-1)*DR2+WA1(I)*DI2 CH(I,K,2) = WA1(I-1)*DI2-WA1(I)*DR2 CH(I-1,K,3) = WA2(I-1)*DR3+WA2(I)*DI3 CH(I,K,3) = WA2(I-1)*DI3-WA2(I)*DR3 CH(I-1,K,4) = WA3(I-1)*DR4+WA3(I)*DI4 CH(I,K,4) = WA3(I-1)*DI4-WA3(I)*DR4 CH(I-1,K,5) = WA4(I-1)*DR5+WA4(I)*DI5 CH(I,K,5) = WA4(I-1)*DI5-WA4(I)*DR5 103 CONTINUE 104 CONTINUE C RETURN END C C ****************************************************************** C C subroutine dcfftb(n,c,wsave) C C ****************************************************************** C C subroutine dcfftb computes the backward complex discrete fourier C transform (the fourier synthesis). equivalently , dcfftb computes C a complex periodic sequence from its fourier coefficients. C the transform is defined below at output parameter c. C C a call of dcfftf followed by a call of dcfftb will multiply the C sequence by n. C C the array wsave which is used by subroutine dcfftb must be C initialized by calling subroutine dcffti(n,wsave). C C input parameters C C C n the length of the complex sequence c. the method is C more efficient when n is the product of small primes. C C c a complex array of length n which contains the sequence C C wsave a real work array which must be dimensioned at least 4n+15 C in the program that calls dcfftb. the wsave array must be C initialized by calling subroutine dcffti(n,wsave) and a C different wsave array must be used for each different C value of n. this initialization does not have to be C repeated so long as n remains unchanged thus subsequent C transforms can be obtained faster than the first. C the same wsave array can be used by dcfftf and dcfftb. C C output parameters C C for j=1,...,n C C c(j)=the sum from k=1,...,n of C C c(k)*exp(i*j*k*2*pi/n) C C where i=sqrt(-1) C C wsave contains initialization calculations which must not be C destroyed between calls of subroutine dcfftf or dcfftb C SUBROUTINE DCFFTB (N,C,WSAVE) DOUBLE PRECISION C(*), WSAVE(*) C IF (N .EQ. 1) RETURN C IW1 = N+N+1 IW2 = IW1+N+N CALL DCFTB1 (N,C,WSAVE,WSAVE(IW1),WSAVE(IW2)) C RETURN END SUBROUTINE DCFTB1 (N,C,CH,WA,IFAC) DOUBLE PRECISION C(*), CH(*), WA(*) INTEGER IFAC(*) C NF = IFAC(2) NA = 0 L1 = 1 IW = 1 DO 116 K1=1,NF IP = IFAC(K1+2) L2 = IP*L1 IDO = N/L2 IDOT = IDO+IDO IDL1 = IDOT*L1 IF (IP .NE. 4) GO TO 103 IX2 = IW+IDOT IX3 = IX2+IDOT IF (NA .NE. 0) GO TO 101 CALL DPSSB4 (IDOT,L1,C,CH,WA(IW),WA(IX2),WA(IX3)) GO TO 102 101 CALL DPSSB4 (IDOT,L1,CH,C,WA(IW),WA(IX2),WA(IX3)) 102 NA = 1-NA GO TO 115 C 103 IF (IP .NE. 2) GO TO 106 IF (NA .NE. 0) GO TO 104 CALL DPSSB2 (IDOT,L1,C,CH,WA(IW)) GO TO 105 104 CALL DPSSB2 (IDOT,L1,CH,C,WA(IW)) 105 NA = 1-NA GO TO 115 C 106 IF (IP .NE. 3) GO TO 109 IX2 = IW+IDOT IF (NA .NE. 0) GO TO 107 CALL DPSSB3 (IDOT,L1,C,CH,WA(IW),WA(IX2)) GO TO 108 107 CALL DPSSB3 (IDOT,L1,CH,C,WA(IW),WA(IX2)) 108 NA = 1-NA GO TO 115 C 109 IF (IP .NE. 5) GO TO 112 IX2 = IW+IDOT IX3 = IX2+IDOT IX4 = IX3+IDOT IF (NA .NE. 0) GO TO 110 CALL DPSSB5 (IDOT,L1,C,CH,WA(IW),WA(IX2),WA(IX3),WA(IX4)) GO TO 111 110 CALL DPSSB5 (IDOT,L1,CH,C,WA(IW),WA(IX2),WA(IX3),WA(IX4)) 111 NA = 1-NA GO TO 115 C 112 IF (NA .NE. 0) GO TO 113 CALL DPSSB (NAC,IDOT,IP,L1,IDL1,C,C,C,CH,CH,WA(IW)) GO TO 114 113 CALL DPSSB (NAC,IDOT,IP,L1,IDL1,CH,CH,CH,C,C,WA(IW)) 114 IF (NAC .NE. 0) NA = 1-NA C 115 L1 = L2 IW = IW+(IP-1)*IDOT 116 CONTINUE IF (NA .EQ. 0) RETURN C N2 = N+N DO 117 I=1,N2 C(I) = CH(I) 117 CONTINUE C RETURN END SUBROUTINE DPSSB (NAC,IDO,IP,L1,IDL1,CC,C1,C2,CH,CH2,WA) DOUBLE PRECISION CC(IDO,IP,L1), C1(IDO,L1,IP), C2(IDL1,IP), 1 CH(IDO,L1,IP), CH2(IDL1,IP), WA(*), WAI, WAR C IDOT = IDO/2 NT = IP*IDL1 IPP2 = IP+2 IPPH = (IP+1)/2 IDP = IP*IDO C IF (IDO .LT. L1) GO TO 106 DO 103 J=2,IPPH JC = IPP2-J DO 102 K=1,L1 DO 101 I=1,IDO CH(I,K,J) = CC(I,J,K)+CC(I,JC,K) CH(I,K,JC) = CC(I,J,K)-CC(I,JC,K) 101 CONTINUE 102 CONTINUE 103 CONTINUE C DO 105 K=1,L1 DO 104 I=1,IDO CH(I,K,1) = CC(I,1,K) 104 CONTINUE 105 CONTINUE GO TO 112 C 106 DO 109 J=2,IPPH JC = IPP2-J DO 108 I=1,IDO DO 107 K=1,L1 CH(I,K,J) = CC(I,J,K)+CC(I,JC,K) CH(I,K,JC) = CC(I,J,K)-CC(I,JC,K) 107 CONTINUE 108 CONTINUE 109 CONTINUE C DO 111 I=1,IDO DO 110 K=1,L1 CH(I,K,1) = CC(I,1,K) 110 CONTINUE 111 CONTINUE C 112 IDL = 2-IDO INC = 0 DO 116 L=2,IPPH LC = IPP2-L IDL = IDL+IDO DO 113 IK=1,IDL1 C2(IK,L) = CH2(IK,1)+WA(IDL-1)*CH2(IK,2) C2(IK,LC) = WA(IDL)*CH2(IK,IP) 113 CONTINUE IDLJ = IDL INC = INC+IDO DO 115 J=3,IPPH JC = IPP2-J IDLJ = IDLJ+INC IF (IDLJ .GT. IDP) IDLJ = IDLJ-IDP WAR = WA(IDLJ-1) WAI = WA(IDLJ) DO 114 IK=1,IDL1 C2(IK,L) = C2(IK,L)+WAR*CH2(IK,J) C2(IK,LC) = C2(IK,LC)+WAI*CH2(IK,JC) 114 CONTINUE 115 CONTINUE 116 CONTINUE C DO 118 J=2,IPPH DO 117 IK=1,IDL1 CH2(IK,1) = CH2(IK,1)+CH2(IK,J) 117 CONTINUE 118 CONTINUE C DO 120 J=2,IPPH JC = IPP2-J DO 119 IK=2,IDL1,2 CH2(IK-1,J) = C2(IK-1,J)-C2(IK,JC) CH2(IK-1,JC) = C2(IK-1,J)+C2(IK,JC) CH2(IK,J) = C2(IK,J)+C2(IK-1,JC) CH2(IK,JC) = C2(IK,J)-C2(IK-1,JC) 119 CONTINUE 120 CONTINUE C NAC = 1 IF (IDO .EQ. 2) RETURN NAC = 0 C DO 121 IK=1,IDL1 C2(IK,1) = CH2(IK,1) 121 CONTINUE C DO 123 J=2,IP DO 122 K=1,L1 C1(1,K,J) = CH(1,K,J) C1(2,K,J) = CH(2,K,J) 122 CONTINUE 123 CONTINUE C IF (IDOT .GT. L1) GO TO 127 IDIJ = 0 DO 126 J=2,IP IDIJ = IDIJ+2 DO 125 I=4,IDO,2 IDIJ = IDIJ+2 DO 124 K=1,L1 C1(I-1,K,J) = WA(IDIJ-1)*CH(I-1,K,J)-WA(IDIJ)*CH(I,K,J) C1(I,K,J) = WA(IDIJ-1)*CH(I,K,J)+WA(IDIJ)*CH(I-1,K,J) 124 CONTINUE 125 CONTINUE 126 CONTINUE RETURN C 127 IDJ = 2-IDO DO 130 J=2,IP IDJ = IDJ+IDO DO 129 K=1,L1 IDIJ = IDJ DO 128 I=4,IDO,2 IDIJ = IDIJ+2 C1(I-1,K,J) = WA(IDIJ-1)*CH(I-1,K,J)-WA(IDIJ)*CH(I,K,J) C1(I,K,J) = WA(IDIJ-1)*CH(I,K,J)+WA(IDIJ)*CH(I-1,K,J) 128 CONTINUE 129 CONTINUE 130 CONTINUE C RETURN END SUBROUTINE DPSSB2 (IDO,L1,CC,CH,WA1) DOUBLE PRECISION CC(IDO,2,L1), CH(IDO,L1,2), WA1(*), TI2, TR2 C IF (IDO .GT. 2) GO TO 102 DO 101 K=1,L1 CH(1,K,1) = CC(1,1,K)+CC(1,2,K) CH(1,K,2) = CC(1,1,K)-CC(1,2,K) CH(2,K,1) = CC(2,1,K)+CC(2,2,K) CH(2,K,2) = CC(2,1,K)-CC(2,2,K) 101 CONTINUE RETURN C 102 DO 104 K=1,L1 DO 103 I=2,IDO,2 CH(I-1,K,1) = CC(I-1,1,K)+CC(I-1,2,K) TR2 = CC(I-1,1,K)-CC(I-1,2,K) CH(I,K,1) = CC(I,1,K)+CC(I,2,K) TI2 = CC(I,1,K)-CC(I,2,K) CH(I,K,2) = WA1(I-1)*TI2+WA1(I)*TR2 CH(I-1,K,2) = WA1(I-1)*TR2-WA1(I)*TI2 103 CONTINUE 104 CONTINUE C RETURN END SUBROUTINE DPSSB3 (IDO,L1,CC,CH,WA1,WA2) DOUBLE PRECISION CC(IDO,3,L1), CH(IDO,L1,3), WA1(*), WA2(*), 1 CI2, CI3, CR2, CR3, DI2, DI3, DR2, DR3, TAUI, TAUR, TI2, TR2 DATA TAUR / -0.5 D0 / DATA TAUI / 0.8660254037 8443864676 3723170752 93618D0/ C C ONE HALF SQRT(3) = .866025..... . C IF (IDO .NE. 2) GO TO 102 DO 101 K=1,L1 TR2 = CC(1,2,K)+CC(1,3,K) CR2 = CC(1,1,K)+TAUR*TR2 CH(1,K,1) = CC(1,1,K)+TR2 TI2 = CC(2,2,K)+CC(2,3,K) CI2 = CC(2,1,K)+TAUR*TI2 CH(2,K,1) = CC(2,1,K)+TI2 CR3 = TAUI*(CC(1,2,K)-CC(1,3,K)) CI3 = TAUI*(CC(2,2,K)-CC(2,3,K)) CH(1,K,2) = CR2-CI3 CH(1,K,3) = CR2+CI3 CH(2,K,2) = CI2+CR3 CH(2,K,3) = CI2-CR3 101 CONTINUE RETURN C 102 DO 104 K=1,L1 DO 103 I=2,IDO,2 TR2 = CC(I-1,2,K)+CC(I-1,3,K) CR2 = CC(I-1,1,K)+TAUR*TR2 CH(I-1,K,1) = CC(I-1,1,K)+TR2 TI2 = CC(I,2,K)+CC(I,3,K) CI2 = CC(I,1,K)+TAUR*TI2 CH(I,K,1) = CC(I,1,K)+TI2 CR3 = TAUI*(CC(I-1,2,K)-CC(I-1,3,K)) CI3 = TAUI*(CC(I,2,K)-CC(I,3,K)) DR2 = CR2-CI3 DR3 = CR2+CI3 DI2 = CI2+CR3 DI3 = CI2-CR3 CH(I,K,2) = WA1(I-1)*DI2+WA1(I)*DR2 CH(I-1,K,2) = WA1(I-1)*DR2-WA1(I)*DI2 CH(I,K,3) = WA2(I-1)*DI3+WA2(I)*DR3 CH(I-1,K,3) = WA2(I-1)*DR3-WA2(I)*DI3 103 CONTINUE 104 CONTINUE C RETURN END SUBROUTINE DPSSB4 (IDO,L1,CC,CH,WA1,WA2,WA3) DOUBLE PRECISION CC(IDO,4,L1), CH(IDO,L1,4), WA1(*), WA2(*), 1 WA3(*), CI2, CI3, CI4, CR2, CR3, CR4, TI1, TI2, TI3, TI4, TR1 * ,TR2, TR3, TR4 C IF (IDO .NE. 2) GO TO 102 DO 101 K=1,L1 TI1 = CC(2,1,K)-CC(2,3,K) TI2 = CC(2,1,K)+CC(2,3,K) TR4 = CC(2,4,K)-CC(2,2,K) TI3 = CC(2,2,K)+CC(2,4,K) TR1 = CC(1,1,K)-CC(1,3,K) TR2 = CC(1,1,K)+CC(1,3,K) TI4 = CC(1,2,K)-CC(1,4,K) TR3 = CC(1,2,K)+CC(1,4,K) CH(1,K,1) = TR2+TR3 CH(1,K,3) = TR2-TR3 CH(2,K,1) = TI2+TI3 CH(2,K,3) = TI2-TI3 CH(1,K,2) = TR1+TR4 CH(1,K,4) = TR1-TR4 CH(2,K,2) = TI1+TI4 CH(2,K,4) = TI1-TI4 101 CONTINUE RETURN C 102 DO 104 K=1,L1 DO 103 I=2,IDO,2 TI1 = CC(I,1,K)-CC(I,3,K) TI2 = CC(I,1,K)+CC(I,3,K) TI3 = CC(I,2,K)+CC(I,4,K) TR4 = CC(I,4,K)-CC(I,2,K) TR1 = CC(I-1,1,K)-CC(I-1,3,K) TR2 = CC(I-1,1,K)+CC(I-1,3,K) TI4 = CC(I-1,2,K)-CC(I-1,4,K) TR3 = CC(I-1,2,K)+CC(I-1,4,K) CH(I-1,K,1) = TR2+TR3 CR3 = TR2-TR3 CH(I,K,1) = TI2+TI3 CI3 = TI2-TI3 CR2 = TR1+TR4 CR4 = TR1-TR4 CI2 = TI1+TI4 CI4 = TI1-TI4 CH(I-1,K,2) = WA1(I-1)*CR2-WA1(I)*CI2 CH(I,K,2) = WA1(I-1)*CI2+WA1(I)*CR2 CH(I-1,K,3) = WA2(I-1)*CR3-WA2(I)*CI3 CH(I,K,3) = WA2(I-1)*CI3+WA2(I)*CR3 CH(I-1,K,4) = WA3(I-1)*CR4-WA3(I)*CI4 CH(I,K,4) = WA3(I-1)*CI4+WA3(I)*CR4 103 CONTINUE 104 CONTINUE C RETURN END SUBROUTINE DPSSB5 (IDO,L1,CC,CH,WA1,WA2,WA3,WA4) DOUBLE PRECISION CC(IDO,5,L1), CH(IDO,L1,5), WA1(*), WA2(*), 1 WA3(*), WA4(*), CI2, CI3, CI4, CI5, CR2, CR3, CR4, CR5, 2 DI2, DI3, DI4, DI5, DR2, DR3, DR4, DR5, TI11, TI12, TI2, TI3, 3 TI4, TI5, TR11, TR12, TR2, TR3, TR4, TR5 DATA TR11 / 0.3090169943 7494742410 2293417182 81906D0/ DATA TI11 / 0.9510565162 9515357211 6439333379 38214D0/ DATA TR12 / -0.8090169943 7494742410 2293417182 81906D0/ DATA TI12 / 0.5877852522 9247312916 8705954639 07277D0/ C C SIN(PI/10) = .30901699.... . C COS(PI/10) = .95105651.... . C SIN(PI/5 ) = .58778525.... . C COS(PI/5 ) = .80901699.... . C IF (IDO .NE. 2) GO TO 102 DO 101 K=1,L1 TI5 = CC(2,2,K)-CC(2,5,K) TI2 = CC(2,2,K)+CC(2,5,K) TI4 = CC(2,3,K)-CC(2,4,K) TI3 = CC(2,3,K)+CC(2,4,K) TR5 = CC(1,2,K)-CC(1,5,K) TR2 = CC(1,2,K)+CC(1,5,K) TR4 = CC(1,3,K)-CC(1,4,K) TR3 = CC(1,3,K)+CC(1,4,K) CH(1,K,1) = CC(1,1,K)+TR2+TR3 CH(2,K,1) = CC(2,1,K)+TI2+TI3 CR2 = CC(1,1,K)+TR11*TR2+TR12*TR3 CI2 = CC(2,1,K)+TR11*TI2+TR12*TI3 CR3 = CC(1,1,K)+TR12*TR2+TR11*TR3 CI3 = CC(2,1,K)+TR12*TI2+TR11*TI3 CR5 = TI11*TR5+TI12*TR4 CI5 = TI11*TI5+TI12*TI4 CR4 = TI12*TR5-TI11*TR4 CI4 = TI12*TI5-TI11*TI4 CH(1,K,2) = CR2-CI5 CH(1,K,5) = CR2+CI5 CH(2,K,2) = CI2+CR5 CH(2,K,3) = CI3+CR4 CH(1,K,3) = CR3-CI4 CH(1,K,4) = CR3+CI4 CH(2,K,4) = CI3-CR4 CH(2,K,5) = CI2-CR5 101 CONTINUE RETURN C 102 DO 104 K=1,L1 DO 103 I=2,IDO,2 TI5 = CC(I,2,K)-CC(I,5,K) TI2 = CC(I,2,K)+CC(I,5,K) TI4 = CC(I,3,K)-CC(I,4,K) TI3 = CC(I,3,K)+CC(I,4,K) TR5 = CC(I-1,2,K)-CC(I-1,5,K) TR2 = CC(I-1,2,K)+CC(I-1,5,K) TR4 = CC(I-1,3,K)-CC(I-1,4,K) TR3 = CC(I-1,3,K)+CC(I-1,4,K) CH(I-1,K,1) = CC(I-1,1,K)+TR2+TR3 CH(I,K,1) = CC(I,1,K)+TI2+TI3 CR2 = CC(I-1,1,K)+TR11*TR2+TR12*TR3 CI2 = CC(I,1,K)+TR11*TI2+TR12*TI3 CR3 = CC(I-1,1,K)+TR12*TR2+TR11*TR3 CI3 = CC(I,1,K)+TR12*TI2+TR11*TI3 CR5 = TI11*TR5+TI12*TR4 CI5 = TI11*TI5+TI12*TI4 CR4 = TI12*TR5-TI11*TR4 CI4 = TI12*TI5-TI11*TI4 DR3 = CR3-CI4 DR4 = CR3+CI4 DI3 = CI3+CR4 DI4 = CI3-CR4 DR5 = CR2+CI5 DR2 = CR2-CI5 DI5 = CI2-CR5 DI2 = CI2+CR5 CH(I-1,K,2) = WA1(I-1)*DR2-WA1(I)*DI2 CH(I,K,2) = WA1(I-1)*DI2+WA1(I)*DR2 CH(I-1,K,3) = WA2(I-1)*DR3-WA2(I)*DI3 CH(I,K,3) = WA2(I-1)*DI3+WA2(I)*DR3 CH(I-1,K,4) = WA3(I-1)*DR4-WA3(I)*DI4 CH(I,K,4) = WA3(I-1)*DI4+WA3(I)*DR4 CH(I-1,K,5) = WA4(I-1)*DR5-WA4(I)*DI5 CH(I,K,5) = WA4(I-1)*DI5+WA4(I)*DR5 103 CONTINUE 104 CONTINUE C RETURN END casacore-3.7.1/scimath_f/dqags.f000066400000000000000000001440331476623553700165340ustar00rootroot00000000000000*----------------------------------------------------------------------- * dgags: QUADPACK quadrature routines * * $Id$ * *----------------------------------------------------------------------- * c All the code that follows is standard mathematical software library c code, namely, the relevant QUADPACK code. c c - F. Schwab, Dec. 3, 2001 c************************************************************************* subroutine dqags(f,a,b,epsabs,epsrel,result,abserr,neval,ier, * limit,lenw,last,iwork,work) c***begin prologue dqags c***date written 800101 (yymmdd) c***revision date 830518 (yymmdd) c***category no. h2a1a1 c***keywords automatic integrator, general-purpose, c (end-point) singularities, extrapolation, c globally adaptive c***author piessens,robert,appl. math. & progr. div. - k.u.leuven c de doncker,elise,appl. math. & prog. div. - k.u.leuven c***purpose the routine calculates an approximation result to a given c definite integral i = integral of f over (a,b), c hopefully satisfying following claim for accuracy c abs(i-result).le.max(epsabs,epsrel*abs(i)). c***description c c computation of a definite integral c standard fortran subroutine c double precision version c c c parameters c on entry c f - double precision c function subprogram defining the integrand c function f(x). the actual name for f needs to be c declared e x t e r n a l in the driver program. c c a - double precision c lower limit of integration c c b - double precision c upper limit of integration c c epsabs - double precision c absolute accuracy requested c epsrel - double precision c relative accuracy requested c if epsabs.le.0 c and epsrel.lt.max(50*rel.mach.acc.,0.5d-28), c the routine will end with ier = 6. c c on return c result - double precision c approximation to the integral c c abserr - double precision c estimate of the modulus of the absolute error, c which should equal or exceed abs(i-result) c c neval - integer c number of integrand evaluations c c ier - integer c ier = 0 normal and reliable termination of the c routine. it is assumed that the requested c accuracy has been achieved. c ier.gt.0 abnormal termination of the routine c the estimates for integral and error are c less reliable. it is assumed that the c requested accuracy has not been achieved. c error messages c ier = 1 maximum number of subdivisions allowed c has been achieved. one can allow more sub- c divisions by increasing the value of limit c (and taking the according dimension c adjustments into account. however, if c this yields no improvement it is advised c to analyze the integrand in order to c determine the integration difficulties. if c the position of a local difficulty can be c determined (e.g. singularity, c discontinuity within the interval) one c will probably gain from splitting up the c interval at this point and calling the c integrator on the subranges. if possible, c an appropriate special-purpose integrator c should be used, which is designed for c handling the type of difficulty involved. c = 2 the occurrence of roundoff error is detec- c ted, which prevents the requested c tolerance from being achieved. c the error may be under-estimated. c = 3 extremely bad integrand behaviour c occurs at some points of the integration c interval. c = 4 the algorithm does not converge. c roundoff error is detected in the c extrapolation table. it is presumed that c the requested tolerance cannot be c achieved, and that the returned result is c the best which can be obtained. c = 5 the integral is probably divergent, or c slowly convergent. it must be noted that c divergence can occur with any other value c of ier. c = 6 the input is invalid, because c (epsabs.le.0 and c epsrel.lt.max(50*rel.mach.acc.,0.5d-28) c or limit.lt.1 or lenw.lt.limit*4. c result, abserr, neval, last are set to c zero.except when limit or lenw is invalid, c iwork(1), work(limit*2+1) and c work(limit*3+1) are set to zero, work(1) c is set to a and work(limit+1) to b. c c dimensioning parameters c limit - integer c dimensioning parameter for iwork c limit determines the maximum number of subintervals c in the partition of the given integration interval c (a,b), limit.ge.1. c if limit.lt.1, the routine will end with ier = 6. c c lenw - integer c dimensioning parameter for work c lenw must be at least limit*4. c if lenw.lt.limit*4, the routine will end c with ier = 6. c c last - integer c on return, last equals the number of subintervals c produced in the subdivision process, detemines the c number of significant elements actually in the work c arrays. c c work arrays c iwork - integer c vector of dimension at least limit, the first k c elements of which contain pointers c to the error estimates over the subintervals c such that work(limit*3+iwork(1)),... , c work(limit*3+iwork(k)) form a decreasing c sequence, with k = last if last.le.(limit/2+2), c and k = limit+1-last otherwise c c work - double precision c vector of dimension at least lenw c on return c work(1), ..., work(last) contain the left c end-points of the subintervals in the c partition of (a,b), c work(limit+1), ..., work(limit+last) contain c the right end-points, c work(limit*2+1), ..., work(limit*2+last) contain c the integral approximations over the subintervals, c work(limit*3+1), ..., work(limit*3+last) c contain the error estimates. c c***references (none) c***routines called dqagse,xerror c***end prologue dqags c c double precision a,abserr,b,epsabs,epsrel,f,result,work integer ier,iwork,last,lenw,limit,lvl,l1,l2,l3,neval c dimension iwork(limit),work(lenw) c external f c c check validity of limit and lenw. c c***first executable statement dqags ier = 6 neval = 0 last = 0 result = 0.0d+00 abserr = 0.0d+00 if(limit.lt.1.or.lenw.lt.limit*4) go to 10 c c prepare call for dqagse. c l1 = limit+1 l2 = limit+l1 l3 = limit+l2 c call dqagse(f,a,b,epsabs,epsrel,limit,result,abserr,neval, * ier,work(1),work(l1),work(l2),work(l3),iwork,last) c c call error handler if necessary. c lvl = 0 10 if(ier.eq.6) lvl = 1 c if(ier.ne.0) call xerror(26habnormal return from dqags,26,ier,lvl) if(ier.ne.0)print *,"abnormal return from dqags, ier,lvl=",ier,lvl return end subroutine dqagse(f,a,b,epsabs,epsrel,limit,result,abserr,neval, * ier,alist,blist,rlist,elist,iord,last) c***begin prologue dqagse c***date written 800101 (yymmdd) c***revision date 830518 (yymmdd) c***category no. h2a1a1 c***keywords automatic integrator, general-purpose, c (end point) singularities, extrapolation, c globally adaptive c***author piessens,robert,appl. math. & progr. div. - k.u.leuven c de doncker,elise,appl. math. & progr. div. - k.u.leuven c***purpose the routine calculates an approximation result to a given c definite integral i = integral of f over (a,b), c hopefully satisfying following claim for accuracy c abs(i-result).le.max(epsabs,epsrel*abs(i)). c***description c c computation of a definite integral c standard fortran subroutine c double precision version c c parameters c on entry c f - double precision c function subprogram defining the integrand c function f(x). the actual name for f needs to be c declared e x t e r n a l in the driver program. c c a - double precision c lower limit of integration c c b - double precision c upper limit of integration c c epsabs - double precision c absolute accuracy requested c epsrel - double precision c relative accuracy requested c if epsabs.le.0 c and epsrel.lt.max(50*rel.mach.acc.,0.5d-28), c the routine will end with ier = 6. c c limit - integer c gives an upperbound on the number of subintervals c in the partition of (a,b) c c on return c result - double precision c approximation to the integral c c abserr - double precision c estimate of the modulus of the absolute error, c which should equal or exceed abs(i-result) c c neval - integer c number of integrand evaluations c c ier - integer c ier = 0 normal and reliable termination of the c routine. it is assumed that the requested c accuracy has been achieved. c ier.gt.0 abnormal termination of the routine c the estimates for integral and error are c less reliable. it is assumed that the c requested accuracy has not been achieved. c error messages c = 1 maximum number of subdivisions allowed c has been achieved. one can allow more sub- c divisions by increasing the value of limit c (and taking the according dimension c adjustments into account). however, if c this yields no improvement it is advised c to analyze the integrand in order to c determine the integration difficulties. if c the position of a local difficulty can be c determined (e.g. singularity, c discontinuity within the interval) one c will probably gain from splitting up the c interval at this point and calling the c integrator on the subranges. if possible, c an appropriate special-purpose integrator c should be used, which is designed for c handling the type of difficulty involved. c = 2 the occurrence of roundoff error is detec- c ted, which prevents the requested c tolerance from being achieved. c the error may be under-estimated. c = 3 extremely bad integrand behaviour c occurs at some points of the integration c interval. c = 4 the algorithm does not converge. c roundoff error is detected in the c extrapolation table. c it is presumed that the requested c tolerance cannot be achieved, and that the c returned result is the best which can be c obtained. c = 5 the integral is probably divergent, or c slowly convergent. it must be noted that c divergence can occur with any other value c of ier. c = 6 the input is invalid, because c epsabs.le.0 and c epsrel.lt.max(50*rel.mach.acc.,0.5d-28). c result, abserr, neval, last, rlist(1), c iord(1) and elist(1) are set to zero. c alist(1) and blist(1) are set to a and b c respectively. c c alist - double precision c vector of dimension at least limit, the first c last elements of which are the left end points c of the subintervals in the partition of the c given integration range (a,b) c c blist - double precision c vector of dimension at least limit, the first c last elements of which are the right end points c of the subintervals in the partition of the given c integration range (a,b) c c rlist - double precision c vector of dimension at least limit, the first c last elements of which are the integral c approximations on the subintervals c c elist - double precision c vector of dimension at least limit, the first c last elements of which are the moduli of the c absolute error estimates on the subintervals c c iord - integer c vector of dimension at least limit, the first k c elements of which are pointers to the c error estimates over the subintervals, c such that elist(iord(1)), ..., elist(iord(k)) c form a decreasing sequence, with k = last c if last.le.(limit/2+2), and k = limit+1-last c otherwise c c last - integer c number of subintervals actually produced in the c subdivision process c c***references (none) c***routines called d1mach,dqelg,dqk21,dqpsrt c***end prologue dqagse c double precision a,abseps,abserr,alist,area,area1,area12,area2,a1, * a2,b,blist,b1,b2,correc,dabs,defabs,defab1,defab2,d1mach,dmax1, * dres,elist,epmach,epsabs,epsrel,erlarg,erlast,errbnd,errmax, * error1,error2,erro12,errsum,ertest,f,oflow,resabs,reseps,result, * res3la,rlist,rlist2,small,uflow integer id,ier,ierro,iord,iroff1,iroff2,iroff3,jupbnd,k,ksgn, * ktmin,last,limit,maxerr,neval,nres,nrmax,numrl2 logical extrap,noext c dimension alist(limit),blist(limit),elist(limit),iord(limit), * res3la(3),rlist(limit),rlist2(52) c external f c c the dimension of rlist2 is determined by the value of c limexp in subroutine dqelg (rlist2 should be of dimension c (limexp+2) at least). c c list of major variables c ----------------------- c c alist - list of left end points of all subintervals c considered up to now c blist - list of right end points of all subintervals c considered up to now c rlist(i) - approximation to the integral over c (alist(i),blist(i)) c rlist2 - array of dimension at least limexp+2 containing c the part of the epsilon table which is still c needed for further computations c elist(i) - error estimate applying to rlist(i) c maxerr - pointer to the interval with largest error c estimate c errmax - elist(maxerr) c erlast - error on the interval currently subdivided c (before that subdivision has taken place) c area - sum of the integrals over the subintervals c errsum - sum of the errors over the subintervals c errbnd - requested accuracy max(epsabs,epsrel* c abs(result)) c *****1 - variable for the left interval c *****2 - variable for the right interval c last - index for subdivision c nres - number of calls to the extrapolation routine c numrl2 - number of elements currently in rlist2. if an c appropriate approximation to the compounded c integral has been obtained it is put in c rlist2(numrl2) after numrl2 has been increased c by one. c small - length of the smallest interval considered up c to now, multiplied by 1.5 c erlarg - sum of the errors over the intervals larger c than the smallest interval considered up to now c extrap - logical variable denoting that the routine is c attempting to perform extrapolation i.e. before c subdividing the smallest interval we try to c decrease the value of erlarg. c noext - logical variable denoting that extrapolation c is no longer allowed (true value) c c machine dependent constants c --------------------------- c c epmach is the largest relative spacing. c uflow is the smallest positive magnitude. c oflow is the largest positive magnitude. c c***first executable statement dqagse epmach = d1mach(4) c c test on validity of parameters c ------------------------------ ier = 0 neval = 0 last = 0 result = 0.0d+00 abserr = 0.0d+00 alist(1) = a blist(1) = b rlist(1) = 0.0d+00 elist(1) = 0.0d+00 if(epsabs.le.0.0d+00.and.epsrel.lt.dmax1(0.5d+02*epmach,0.5d-28)) * ier = 6 if(ier.eq.6) go to 999 c c first approximation to the integral c ----------------------------------- c uflow = d1mach(1) oflow = d1mach(2) ierro = 0 call dqk21(f,a,b,result,abserr,defabs,resabs) c c test on accuracy. c dres = dabs(result) errbnd = dmax1(epsabs,epsrel*dres) last = 1 rlist(1) = result elist(1) = abserr iord(1) = 1 if(abserr.le.1.0d+02*epmach*defabs.and.abserr.gt.errbnd) ier = 2 if(limit.eq.1) ier = 1 if(ier.ne.0.or.(abserr.le.errbnd.and.abserr.ne.resabs).or. * abserr.eq.0.0d+00) go to 140 c c initialization c -------------- c rlist2(1) = result errmax = abserr maxerr = 1 area = result errsum = abserr abserr = oflow nrmax = 1 nres = 0 numrl2 = 2 ktmin = 0 extrap = .false. noext = .false. iroff1 = 0 iroff2 = 0 iroff3 = 0 ksgn = -1 if(dres.ge.(0.1d+01-0.5d+02*epmach)*defabs) ksgn = 1 c c main do-loop c ------------ c do 90 last = 2,limit c c bisect the subinterval with the nrmax-th largest error c estimate. c a1 = alist(maxerr) b1 = 0.5d+00*(alist(maxerr)+blist(maxerr)) a2 = b1 b2 = blist(maxerr) erlast = errmax call dqk21(f,a1,b1,area1,error1,resabs,defab1) call dqk21(f,a2,b2,area2,error2,resabs,defab2) c c improve previous approximations to integral c and error and test for accuracy. c area12 = area1+area2 erro12 = error1+error2 errsum = errsum+erro12-errmax area = area+area12-rlist(maxerr) if(defab1.eq.error1.or.defab2.eq.error2) go to 15 if(dabs(rlist(maxerr)-area12).gt.0.1d-04*dabs(area12) * .or.erro12.lt.0.99d+00*errmax) go to 10 if(extrap) iroff2 = iroff2+1 if(.not.extrap) iroff1 = iroff1+1 10 if(last.gt.10.and.erro12.gt.errmax) iroff3 = iroff3+1 15 rlist(maxerr) = area1 rlist(last) = area2 errbnd = dmax1(epsabs,epsrel*dabs(area)) c c test for roundoff error and eventually set error flag. c if(iroff1+iroff2.ge.10.or.iroff3.ge.20) ier = 2 if(iroff2.ge.5) ierro = 3 c c set error flag in the case that the number of subintervals c equals limit. c if(last.eq.limit) ier = 1 c c set error flag in the case of bad integrand behaviour c at a point of the integration range. c if(dmax1(dabs(a1),dabs(b2)).le.(0.1d+01+0.1d+03*epmach)* * (dabs(a2)+0.1d+04*uflow)) ier = 4 c c append the newly-created intervals to the list. c if(error2.gt.error1) go to 20 alist(last) = a2 blist(maxerr) = b1 blist(last) = b2 elist(maxerr) = error1 elist(last) = error2 go to 30 20 alist(maxerr) = a2 alist(last) = a1 blist(last) = b1 rlist(maxerr) = area2 rlist(last) = area1 elist(maxerr) = error2 elist(last) = error1 c c call subroutine dqpsrt to maintain the descending ordering c in the list of error estimates and select the subinterval c with nrmax-th largest error estimate (to be bisected next). c 30 call dqpsrt(limit,last,maxerr,errmax,elist,iord,nrmax) c ***jump out of do-loop if(errsum.le.errbnd) go to 115 c ***jump out of do-loop if(ier.ne.0) go to 100 if(last.eq.2) go to 80 if(noext) go to 90 erlarg = erlarg-erlast if(dabs(b1-a1).gt.small) erlarg = erlarg+erro12 if(extrap) go to 40 c c test whether the interval to be bisected next is the c smallest interval. c if(dabs(blist(maxerr)-alist(maxerr)).gt.small) go to 90 extrap = .true. nrmax = 2 40 if(ierro.eq.3.or.erlarg.le.ertest) go to 60 c c the smallest interval has the largest error. c before bisecting decrease the sum of the errors over the c larger intervals (erlarg) and perform extrapolation. c id = nrmax jupbnd = last if(last.gt.(2+limit/2)) jupbnd = limit+3-last do 50 k = id,jupbnd maxerr = iord(nrmax) errmax = elist(maxerr) c ***jump out of do-loop if(dabs(blist(maxerr)-alist(maxerr)).gt.small) go to 90 nrmax = nrmax+1 50 continue c c perform extrapolation. c 60 numrl2 = numrl2+1 rlist2(numrl2) = area call dqelg(numrl2,rlist2,reseps,abseps,res3la,nres) ktmin = ktmin+1 if(ktmin.gt.5.and.abserr.lt.0.1d-02*errsum) ier = 5 if(abseps.ge.abserr) go to 70 ktmin = 0 abserr = abseps result = reseps correc = erlarg ertest = dmax1(epsabs,epsrel*dabs(reseps)) c ***jump out of do-loop if(abserr.le.ertest) go to 100 c c prepare bisection of the smallest interval. c 70 if(numrl2.eq.1) noext = .true. if(ier.eq.5) go to 100 maxerr = iord(1) errmax = elist(maxerr) nrmax = 1 extrap = .false. small = small*0.5d+00 erlarg = errsum go to 90 80 small = dabs(b-a)*0.375d+00 erlarg = errsum ertest = errbnd rlist2(2) = area 90 continue c c set final result and error estimate. c ------------------------------------ c 100 if(abserr.eq.oflow) go to 115 if(ier+ierro.eq.0) go to 110 if(ierro.eq.3) abserr = abserr+correc if(ier.eq.0) ier = 3 if(result.ne.0.0d+00.and.area.ne.0.0d+00) go to 105 if(abserr.gt.errsum) go to 115 if(area.eq.0.0d+00) go to 130 go to 110 105 if(abserr/dabs(result).gt.errsum/dabs(area)) go to 115 c c test on divergence. c 110 if(ksgn.eq.(-1).and.dmax1(dabs(result),dabs(area)).le. * defabs*0.1d-01) go to 130 if(0.1d-01.gt.(result/area).or.(result/area).gt.0.1d+03 * .or.errsum.gt.dabs(area)) ier = 6 go to 130 c c compute global integral sum. c 115 result = 0.0d+00 do 120 k = 1,last result = result+rlist(k) 120 continue abserr = errsum 130 if(ier.gt.2) ier = ier-1 140 neval = 42*last-21 999 return end subroutine dqelg(n,epstab,result,abserr,res3la,nres) c***begin prologue dqelg c***refer to dqagie,dqagoe,dqagpe,dqagse c***routines called d1mach c***revision date 830518 (yymmdd) c***keywords epsilon algorithm, convergence acceleration, c extrapolation c***author piessens,robert,appl. math. & progr. div. - k.u.leuven c de doncker,elise,appl. math & progr. div. - k.u.leuven c***purpose the routine determines the limit of a given sequence of c approximations, by means of the epsilon algorithm of c p.wynn. an estimate of the absolute error is also given. c the condensed epsilon table is computed. only those c elements needed for the computation of the next diagonal c are preserved. c***description c c epsilon algorithm c standard fortran subroutine c double precision version c c parameters c n - integer c epstab(n) contains the new element in the c first column of the epsilon table. c c epstab - double precision c vector of dimension 52 containing the elements c of the two lower diagonals of the triangular c epsilon table. the elements are numbered c starting at the right-hand corner of the c triangle. c c result - double precision c resulting approximation to the integral c c abserr - double precision c estimate of the absolute error computed from c result and the 3 previous results c c res3la - double precision c vector of dimension 3 containing the last 3 c results c c nres - integer c number of calls to the routine c (should be zero at first call) c c***end prologue dqelg c double precision abserr,dabs,delta1,delta2,delta3,dmax1,d1mach, * epmach,epsinf,epstab,error,err1,err2,err3,e0,e1,e1abs,e2,e3, * oflow,res,result,res3la,ss,tol1,tol2,tol3 integer i,ib,ib2,ie,indx,k1,k2,k3,limexp,n,newelm,nres,num dimension epstab(52),res3la(3) c c list of major variables c ----------------------- c c e0 - the 4 elements on which the computation of a new c e1 element in the epsilon table is based c e2 c e3 e0 c e3 e1 new c e2 c newelm - number of elements to be computed in the new c diagonal c error - error = abs(e1-e0)+abs(e2-e1)+abs(new-e2) c result - the element in the new diagonal with least value c of error c c machine dependent constants c --------------------------- c c epmach is the largest relative spacing. c oflow is the largest positive magnitude. c limexp is the maximum number of elements the epsilon c table can contain. if this number is reached, the upper c diagonal of the epsilon table is deleted. c c***first executable statement dqelg epmach = d1mach(4) oflow = d1mach(2) nres = nres+1 abserr = oflow result = epstab(n) if(n.lt.3) go to 100 limexp = 50 epstab(n+2) = epstab(n) newelm = (n-1)/2 epstab(n) = oflow num = n k1 = n do 40 i = 1,newelm k2 = k1-1 k3 = k1-2 res = epstab(k1+2) e0 = epstab(k3) e1 = epstab(k2) e2 = res e1abs = dabs(e1) delta2 = e2-e1 err2 = dabs(delta2) tol2 = dmax1(dabs(e2),e1abs)*epmach delta3 = e1-e0 err3 = dabs(delta3) tol3 = dmax1(e1abs,dabs(e0))*epmach if(err2.gt.tol2.or.err3.gt.tol3) go to 10 c c if e0, e1 and e2 are equal to within machine c accuracy, convergence is assumed. c result = e2 c abserr = abs(e1-e0)+abs(e2-e1) c result = res abserr = err2+err3 c ***jump out of do-loop go to 100 10 e3 = epstab(k1) epstab(k1) = e1 delta1 = e1-e3 err1 = dabs(delta1) tol1 = dmax1(e1abs,dabs(e3))*epmach c c if two elements are very close to each other, omit c a part of the table by adjusting the value of n c if(err1.le.tol1.or.err2.le.tol2.or.err3.le.tol3) go to 20 ss = 0.1d+01/delta1+0.1d+01/delta2-0.1d+01/delta3 epsinf = dabs(ss*e1) c c test to detect irregular behaviour in the table, and c eventually omit a part of the table adjusting the value c of n. c if(epsinf.gt.0.1d-03) go to 30 20 n = i+i-1 c ***jump out of do-loop go to 50 c c compute a new element and eventually adjust c the value of result. c 30 res = e1+0.1d+01/ss epstab(k1) = res k1 = k1-2 error = err2+dabs(res-e2)+err3 if(error.gt.abserr) go to 40 abserr = error result = res 40 continue c c shift the table. c 50 if(n.eq.limexp) n = 2*(limexp/2)-1 ib = 1 if((num/2)*2.eq.num) ib = 2 ie = newelm+1 do 60 i=1,ie ib2 = ib+2 epstab(ib) = epstab(ib2) ib = ib2 60 continue if(num.eq.n) go to 80 indx = num-n+1 do 70 i = 1,n epstab(i)= epstab(indx) indx = indx+1 70 continue 80 if(nres.ge.4) go to 90 res3la(nres) = result abserr = oflow go to 100 c c compute error estimate c 90 abserr = dabs(result-res3la(3))+dabs(result-res3la(2)) * +dabs(result-res3la(1)) res3la(1) = res3la(2) res3la(2) = res3la(3) res3la(3) = result 100 abserr = dmax1(abserr,0.5d+01*epmach*dabs(result)) return end subroutine dqk21(f,a,b,result,abserr,resabs,resasc) c***begin prologue dqk21 c***date written 800101 (yymmdd) c***revision date 830518 (yymmdd) c***category no. h2a1a2 c***keywords 21-point gauss-kronrod rules c***author piessens,robert,appl. math. & progr. div. - k.u.leuven c de doncker,elise,appl. math. & progr. div. - k.u.leuven c***purpose to compute i = integral of f over (a,b), with error c estimate c j = integral of abs(f) over (a,b) c***description c c integration rules c standard fortran subroutine c double precision version c c parameters c on entry c f - double precision c function subprogram defining the integrand c function f(x). the actual name for f needs to be c declared e x t e r n a l in the driver program. c c a - double precision c lower limit of integration c c b - double precision c upper limit of integration c c on return c result - double precision c approximation to the integral i c result is computed by applying the 21-point c kronrod rule (resk) obtained by optimal addition c of abscissae to the 10-point gauss rule (resg). c c abserr - double precision c estimate of the modulus of the absolute error, c which should not exceed abs(i-result) c c resabs - double precision c approximation to the integral j c c resasc - double precision c approximation to the integral of abs(f-i/(b-a)) c over (a,b) c c***references (none) c***routines called d1mach c***end prologue dqk21 c double precision a,absc,abserr,b,centr,dabs,dhlgth,dmax1,dmin1, * d1mach,epmach,f,fc,fsum,fval1,fval2,fv1,fv2,hlgth,resabs,resasc, * resg,resk,reskh,result,uflow,wg,wgk,xgk integer j,jtw,jtwm1 external f c dimension fv1(10),fv2(10),wg(5),wgk(11),xgk(11) c c the abscissae and weights are given for the interval (-1,1). c because of symmetry only the positive abscissae and their c corresponding weights are given. c c xgk - abscissae of the 21-point kronrod rule c xgk(2), xgk(4), ... abscissae of the 10-point c gauss rule c xgk(1), xgk(3), ... abscissae which are optimally c added to the 10-point gauss rule c c wgk - weights of the 21-point kronrod rule c c wg - weights of the 10-point gauss rule c c c gauss quadrature weights and kronron quadrature abscissae and weights c as evaluated with 80 decimal digit arithmetic by l. w. fullerton, c bell labs, nov. 1981. c data wg ( 1) / 0.0666713443 0868813759 3568809893 332 d0 / data wg ( 2) / 0.1494513491 5058059314 5776339657 697 d0 / data wg ( 3) / 0.2190863625 1598204399 5534934228 163 d0 / data wg ( 4) / 0.2692667193 0999635509 1226921569 469 d0 / data wg ( 5) / 0.2955242247 1475287017 3892994651 338 d0 / c data xgk ( 1) / 0.9956571630 2580808073 5527280689 003 d0 / data xgk ( 2) / 0.9739065285 1717172007 7964012084 452 d0 / data xgk ( 3) / 0.9301574913 5570822600 1207180059 508 d0 / data xgk ( 4) / 0.8650633666 8898451073 2096688423 493 d0 / data xgk ( 5) / 0.7808177265 8641689706 3717578345 042 d0 / data xgk ( 6) / 0.6794095682 9902440623 4327365114 874 d0 / data xgk ( 7) / 0.5627571346 6860468333 9000099272 694 d0 / data xgk ( 8) / 0.4333953941 2924719079 9265943165 784 d0 / data xgk ( 9) / 0.2943928627 0146019813 1126603103 866 d0 / data xgk ( 10) / 0.1488743389 8163121088 4826001129 720 d0 / data xgk ( 11) / 0.0000000000 0000000000 0000000000 000 d0 / c data wgk ( 1) / 0.0116946388 6737187427 8064396062 192 d0 / data wgk ( 2) / 0.0325581623 0796472747 8818972459 390 d0 / data wgk ( 3) / 0.0547558965 7435199603 1381300244 580 d0 / data wgk ( 4) / 0.0750396748 1091995276 7043140916 190 d0 / data wgk ( 5) / 0.0931254545 8369760553 5065465083 366 d0 / data wgk ( 6) / 0.1093871588 0229764189 9210590325 805 d0 / data wgk ( 7) / 0.1234919762 6206585107 7958109831 074 d0 / data wgk ( 8) / 0.1347092173 1147332592 8054001771 707 d0 / data wgk ( 9) / 0.1427759385 7706008079 7094273138 717 d0 / data wgk ( 10) / 0.1477391049 0133849137 4841515972 068 d0 / data wgk ( 11) / 0.1494455540 0291690566 4936468389 821 d0 / c c c list of major variables c ----------------------- c c centr - mid point of the interval c hlgth - half-length of the interval c absc - abscissa c fval* - function value c resg - result of the 10-point gauss formula c resk - result of the 21-point kronrod formula c reskh - approximation to the mean value of f over (a,b), c i.e. to i/(b-a) c c c machine dependent constants c --------------------------- c c epmach is the largest relative spacing. c uflow is the smallest positive magnitude. c c***first executable statement dqk21 epmach = d1mach(4) uflow = d1mach(1) c centr = 0.5d+00*(a+b) hlgth = 0.5d+00*(b-a) dhlgth = dabs(hlgth) c c compute the 21-point kronrod approximation to c the integral, and estimate the absolute error. c resg = 0.0d+00 fc = f(centr) resk = wgk(11)*fc resabs = dabs(resk) do 10 j=1,5 jtw = 2*j absc = hlgth*xgk(jtw) fval1 = f(centr-absc) fval2 = f(centr+absc) fv1(jtw) = fval1 fv2(jtw) = fval2 fsum = fval1+fval2 resg = resg+wg(j)*fsum resk = resk+wgk(jtw)*fsum resabs = resabs+wgk(jtw)*(dabs(fval1)+dabs(fval2)) 10 continue do 15 j = 1,5 jtwm1 = 2*j-1 absc = hlgth*xgk(jtwm1) fval1 = f(centr-absc) fval2 = f(centr+absc) fv1(jtwm1) = fval1 fv2(jtwm1) = fval2 fsum = fval1+fval2 resk = resk+wgk(jtwm1)*fsum resabs = resabs+wgk(jtwm1)*(dabs(fval1)+dabs(fval2)) 15 continue reskh = resk*0.5d+00 resasc = wgk(11)*dabs(fc-reskh) do 20 j=1,10 resasc = resasc+wgk(j)*(dabs(fv1(j)-reskh)+dabs(fv2(j)-reskh)) 20 continue result = resk*hlgth resabs = resabs*dhlgth resasc = resasc*dhlgth abserr = dabs((resk-resg)*hlgth) if(resasc.ne.0.0d+00.and.abserr.ne.0.0d+00) * abserr = resasc*dmin1(0.1d+01,(0.2d+03*abserr/resasc)**1.5d+00) if(resabs.gt.uflow/(0.5d+02*epmach)) abserr = dmax1 * ((epmach*0.5d+02)*resabs,abserr) return end subroutine dqpsrt(limit,last,maxerr,ermax,elist,iord,nrmax) c***begin prologue dqpsrt c***refer to dqage,dqagie,dqagpe,dqawse c***routines called (none) c***revision date 810101 (yymmdd) c***keywords sequential sorting c***author piessens,robert,appl. math. & progr. div. - k.u.leuven c de doncker,elise,appl. math. & progr. div. - k.u.leuven c***purpose this routine maintains the descending ordering in the c list of the local error estimated resulting from the c interval subdivision process. at each call two error c estimates are inserted using the sequential search c method, top-down for the largest error estimate and c bottom-up for the smallest error estimate. c***description c c ordering routine c standard fortran subroutine c double precision version c c parameters (meaning at output) c limit - integer c maximum number of error estimates the list c can contain c c last - integer c number of error estimates currently in the list c c maxerr - integer c maxerr points to the nrmax-th largest error c estimate currently in the list c c ermax - double precision c nrmax-th largest error estimate c ermax = elist(maxerr) c c elist - double precision c vector of dimension last containing c the error estimates c c iord - integer c vector of dimension last, the first k elements c of which contain pointers to the error c estimates, such that c elist(iord(1)),..., elist(iord(k)) c form a decreasing sequence, with c k = last if last.le.(limit/2+2), and c k = limit+1-last otherwise c c nrmax - integer c maxerr = iord(nrmax) c c***end prologue dqpsrt c double precision elist,ermax,errmax,errmin integer i,ibeg,ido,iord,isucc,j,jbnd,jupbn,k,last,limit,maxerr, * nrmax dimension elist(last),iord(last) c c check whether the list contains more than c two error estimates. c c***first executable statement dqpsrt if(last.gt.2) go to 10 iord(1) = 1 iord(2) = 2 go to 90 c c this part of the routine is only executed if, due to a c difficult integrand, subdivision increased the error c estimate. in the normal case the insert procedure should c start after the nrmax-th largest error estimate. c 10 errmax = elist(maxerr) if(nrmax.eq.1) go to 30 ido = nrmax-1 do 20 i = 1,ido isucc = iord(nrmax-1) c ***jump out of do-loop if(errmax.le.elist(isucc)) go to 30 iord(nrmax) = isucc nrmax = nrmax-1 20 continue c c compute the number of elements in the list to be maintained c in descending order. this number depends on the number of c subdivisions still allowed. c 30 jupbn = last if(last.gt.(limit/2+2)) jupbn = limit+3-last errmin = elist(last) c c insert errmax by traversing the list top-down, c starting comparison from the element elist(iord(nrmax+1)). c jbnd = jupbn-1 ibeg = nrmax+1 if(ibeg.gt.jbnd) go to 50 do 40 i=ibeg,jbnd isucc = iord(i) c ***jump out of do-loop if(errmax.ge.elist(isucc)) go to 60 iord(i-1) = isucc 40 continue 50 iord(jbnd) = maxerr iord(jupbn) = last go to 90 c c insert errmin by traversing the list bottom-up. c 60 iord(i-1) = maxerr k = jbnd do 70 j=i,jbnd isucc = iord(k) c ***jump out of do-loop if(errmin.lt.elist(isucc)) go to 80 iord(k+1) = isucc k = k-1 70 continue iord(i) = last go to 90 80 iord(k+1) = last c c set maxerr and ermax. c 90 maxerr = iord(nrmax) ermax = elist(maxerr) return end DOUBLE PRECISION FUNCTION D1MACH(I) INTEGER I C C DOUBLE-PRECISION MACHINE CONSTANTS C D1MACH( 1) = B**(EMIN-1), THE SMALLEST POSITIVE MAGNITUDE. C D1MACH( 2) = B**EMAX*(1 - B**(-T)), THE LARGEST MAGNITUDE. C D1MACH( 3) = B**(-T), THE SMALLEST RELATIVE SPACING. C D1MACH( 4) = B**(1-T), THE LARGEST RELATIVE SPACING. C D1MACH( 5) = LOG10(B) C INTEGER SMALL(2) INTEGER LARGE(2) INTEGER RIGHT(2) INTEGER DIVER(2) INTEGER LOG10(2) INTEGER SC, CRAY1(38), J COMMON /D9MACH/ CRAY1 SAVE SMALL, LARGE, RIGHT, DIVER, LOG10, SC DOUBLE PRECISION DMACH(5) EQUIVALENCE (DMACH(1),SMALL(1)) EQUIVALENCE (DMACH(2),LARGE(1)) EQUIVALENCE (DMACH(3),RIGHT(1)) EQUIVALENCE (DMACH(4),DIVER(1)) EQUIVALENCE (DMACH(5),LOG10(1)) C THIS VERSION ADAPTS AUTOMATICALLY TO MOST CURRENT MACHINES. C R1MACH CAN HANDLE AUTO-DOUBLE COMPILING, BUT THIS VERSION OF C D1MACH DOES NOT, BECAUSE WE DO NOT HAVE QUAD CONSTANTS FOR C MANY MACHINES YET. C TO COMPILE ON OLDER MACHINES, ADD A C IN COLUMN 1 C ON THE NEXT LINE DATA SC/0/ C AND REMOVE THE C FROM COLUMN 1 IN ONE OF THE SECTIONS BELOW. C CONSTANTS FOR EVEN OLDER MACHINES CAN BE OBTAINED BY C mail netlib@research.bell-labs.com C send old1mach from blas C PLEASE SEND CORRECTIONS TO dmg OR ehg@bell-labs.com. C C MACHINE CONSTANTS FOR THE HONEYWELL DPS 8/70 SERIES. C DATA SMALL(1),SMALL(2) / O402400000000, O000000000000 / C DATA LARGE(1),LARGE(2) / O376777777777, O777777777777 / C DATA RIGHT(1),RIGHT(2) / O604400000000, O000000000000 / C DATA DIVER(1),DIVER(2) / O606400000000, O000000000000 / C DATA LOG10(1),LOG10(2) / O776464202324, O117571775714 /, SC/987/ C C MACHINE CONSTANTS FOR PDP-11 FORTRANS SUPPORTING C 32-BIT INTEGERS. C DATA SMALL(1),SMALL(2) / 8388608, 0 / C DATA LARGE(1),LARGE(2) / 2147483647, -1 / C DATA RIGHT(1),RIGHT(2) / 612368384, 0 / C DATA DIVER(1),DIVER(2) / 620756992, 0 / C DATA LOG10(1),LOG10(2) / 1067065498, -2063872008 /, SC/987/ C C MACHINE CONSTANTS FOR THE UNIVAC 1100 SERIES. C DATA SMALL(1),SMALL(2) / O000040000000, O000000000000 / C DATA LARGE(1),LARGE(2) / O377777777777, O777777777777 / C DATA RIGHT(1),RIGHT(2) / O170540000000, O000000000000 / C DATA DIVER(1),DIVER(2) / O170640000000, O000000000000 / C DATA LOG10(1),LOG10(2) / O177746420232, O411757177572 /, SC/987/ C C ON FIRST CALL, IF NO DATA UNCOMMENTED, TEST MACHINE TYPES. IF (SC .NE. 987) THEN DMACH(1) = 1.D13 IF ( SMALL(1) .EQ. 1117925532 * .AND. SMALL(2) .EQ. -448790528) THEN * *** IEEE BIG ENDIAN *** SMALL(1) = 1048576 SMALL(2) = 0 LARGE(1) = 2146435071 LARGE(2) = -1 RIGHT(1) = 1017118720 RIGHT(2) = 0 DIVER(1) = 1018167296 DIVER(2) = 0 LOG10(1) = 1070810131 LOG10(2) = 1352628735 ELSE IF ( SMALL(2) .EQ. 1117925532 * .AND. SMALL(1) .EQ. -448790528) THEN * *** IEEE LITTLE ENDIAN *** SMALL(2) = 1048576 SMALL(1) = 0 LARGE(2) = 2146435071 LARGE(1) = -1 RIGHT(2) = 1017118720 RIGHT(1) = 0 DIVER(2) = 1018167296 DIVER(1) = 0 LOG10(2) = 1070810131 LOG10(1) = 1352628735 ELSE IF ( SMALL(1) .EQ. -2065213935 * .AND. SMALL(2) .EQ. 10752) THEN * *** VAX WITH D_FLOATING *** SMALL(1) = 128 SMALL(2) = 0 LARGE(1) = -32769 LARGE(2) = -1 RIGHT(1) = 9344 RIGHT(2) = 0 DIVER(1) = 9472 DIVER(2) = 0 LOG10(1) = 546979738 LOG10(2) = -805796613 ELSE IF ( SMALL(1) .EQ. 1267827943 * .AND. SMALL(2) .EQ. 704643072) THEN * *** IBM MAINFRAME *** SMALL(1) = 1048576 SMALL(2) = 0 LARGE(1) = 2147483647 LARGE(2) = -1 RIGHT(1) = 856686592 RIGHT(2) = 0 DIVER(1) = 873463808 DIVER(2) = 0 LOG10(1) = 1091781651 LOG10(2) = 1352628735 ELSE IF ( SMALL(1) .EQ. 1120022684 * .AND. SMALL(2) .EQ. -448790528) THEN * *** CONVEX C-1 *** SMALL(1) = 1048576 SMALL(2) = 0 LARGE(1) = 2147483647 LARGE(2) = -1 RIGHT(1) = 1019215872 RIGHT(2) = 0 DIVER(1) = 1020264448 DIVER(2) = 0 LOG10(1) = 1072907283 LOG10(2) = 1352628735 ELSE IF ( SMALL(1) .EQ. 815547074 * .AND. SMALL(2) .EQ. 58688) THEN * *** VAX G-FLOATING *** SMALL(1) = 16 SMALL(2) = 0 LARGE(1) = -32769 LARGE(2) = -1 RIGHT(1) = 15552 RIGHT(2) = 0 DIVER(1) = 15568 DIVER(2) = 0 LOG10(1) = 1142112243 LOG10(2) = 2046775455 ELSE DMACH(2) = 1.D27 + 1 DMACH(3) = 1.D27 LARGE(2) = LARGE(2) - RIGHT(2) IF (LARGE(2) .EQ. 64 .AND. SMALL(2) .EQ. 0) THEN CRAY1(1) = 67291416 DO 10 J = 1, 20 CRAY1(J+1) = CRAY1(J) + CRAY1(J) 10 CONTINUE CRAY1(22) = CRAY1(21) + 321322 DO 20 J = 22, 37 CRAY1(J+1) = CRAY1(J) + CRAY1(J) 20 CONTINUE IF (CRAY1(38) .EQ. SMALL(1)) THEN * *** CRAY *** CALL I1MCRY(SMALL(1), J, 8285, 8388608, 0) SMALL(2) = 0 CALL I1MCRY(LARGE(1), J, 24574, 16777215, 16777215) CALL I1MCRY(LARGE(2), J, 0, 16777215, 16777214) CALL I1MCRY(RIGHT(1), J, 16291, 8388608, 0) RIGHT(2) = 0 CALL I1MCRY(DIVER(1), J, 16292, 8388608, 0) DIVER(2) = 0 CALL I1MCRY(LOG10(1), J, 16383, 10100890, 8715215) CALL I1MCRY(LOG10(2), J, 0, 16226447, 9001388) ELSE WRITE(*,9000) STOP 'All done' END IF ELSE WRITE(*,9000) STOP 'All done' END IF END IF SC = 987 END IF * SANITY CHECK IF (DMACH(4) .GE. 1.0D0) STOP 'All done' IF (I .LT. 1 .OR. I .GT. 5) THEN WRITE(*,*) 'D1MACH(I): I =',I,' is out of bounds.' STOP 'All done' END IF D1MACH = DMACH(I) RETURN 9000 FORMAT(/' Adjust D1MACH by uncommenting data statements'/ *' appropriate for your machine.') * /* Standard C source for D1MACH -- remove the * in column 1 */ *#include *#include *#include *double d1mach_(long *i) *{ * switch(*i){ * case 1: return DBL_MIN; * case 2: return DBL_MAX; * case 3: return DBL_EPSILON/FLT_RADIX; * case 4: return DBL_EPSILON; * case 5: return log10((double)FLT_RADIX); * } * fprintf(stderr, "invalid argument: d1mach(%ld)\n", *i); * exit(1); return 0; /* some compilers demand return values */ *} END SUBROUTINE I1MCRY(A, A1, B, C, D) **** SPECIAL COMPUTATION FOR OLD CRAY MACHINES **** INTEGER A, A1, B, C, D A1 = 16777216*B + C A = 16777216*A1 + D END casacore-3.7.1/scimath_f/fftpak.f000066400000000000000000012103741476623553700167130ustar00rootroot00000000000000*======================================================================= C Correspondence concerning AIPS++ should be addressed as follows: C Internet email: casa-feedback@nrao.edu. C Postal address: AIPS++ Project Office C National Radio Astronomy Observatory C 520 Edgemont Road C Charlottesville, VA 22903-2475 USA C C $Id$ C C downloaded from http://www.netlib.org/fftpack/ on Nov 1997 *----------------------------------------------------------------------- C C * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * C C FFTPACK C C * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * C C version 4 april 1985 C C a package of fortran subprograms for the fast fourier C transform of periodic and other symmetric sequences C C by C C paul n swarztrauber C C national center for atmospheric research boulder,colorado 80307 C C which is sponsored by the national science foundation C C * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * C C C this package consists of programs which perform fast fourier C transforms for both complex and real periodic sequences and C certain other symmetric sequences that are listed below. C C 1. rffti initialize rfftf and rfftb C 2. rfftf forward transform of a real periodic sequence C 3. rfftb backward transform of a real coefficient array C C 4. ezffti initialize ezfftf and ezfftb C 5. ezfftf a simplified real periodic forward transform C 6. ezfftb a simplified real periodic backward transform C C 7. sinti initialize sint C 8. sint sine transform of a real odd sequence C C 9. costi initialize cost C 10. cost cosine transform of a real even sequence C C 11. sinqi initialize sinqf and sinqb C 12. sinqf forward sine transform with odd wave numbers C 13. sinqb unnormalized inverse of sinqf C C 14. cosqi initialize cosqf and cosqb C 15. cosqf forward cosine transform with odd wave numbers C 16. cosqb unnormalized inverse of cosqf C C 17. cffti initialize cfftf and cfftb C 18. cfftf forward transform of a complex periodic sequence C 19. cfftb unnormalized inverse of cfftf C C ****************************************************************** C C subroutine rffti(n,wsave) C C ****************************************************************** C C subroutine rffti initializes the array wsave which is used in C both rfftf and rfftb. the prime factorization of n together with C a tabulation of the trigonometric functions are computed and C stored in wsave. C C input parameter C C n the length of the sequence to be transformed. C C output parameter C C wsave a work array which must be dimensioned at least 2*n+15. C the same work array can be used for both rfftf and rfftb C as long as n remains unchanged. different wsave arrays C are required for different values of n. the contents of C wsave must not be changed between calls of rfftf or rfftb. C SUBROUTINE RFFTI (N,WSAVE) DIMENSION WSAVE(*) IF (N .EQ. 1) RETURN CALL RFFTI1_OLD (N,WSAVE(N+1),WSAVE(2*N+1)) RETURN END C SUBROUTINE RFFTI1_OLD (N,WA,IFAC) DIMENSION WA(*) ,IFAC(*) ,NTRYH(4) DATA NTRYH(1),NTRYH(2),NTRYH(3),NTRYH(4)/4,2,3,5/ NL = N NF = 0 J = 0 101 J = J+1 C IF (J-4) 102,102,103 IF (J-4 .GT. 0) GOTO 103 102 NTRY = NTRYH(J) GO TO 104 103 NTRY = NTRY+2 104 NQ = NL/NTRY NR = NL-NTRY*NQ C IF (NR) 101,105,101 IF (NR .NE. 0) GOTO 101 105 NF = NF+1 IFAC(NF+2) = NTRY NL = NQ IF (NTRY .NE. 2) GO TO 107 IF (NF .EQ. 1) GO TO 107 DO 106 I=2,NF IB = NF-I+2 IFAC(IB+2) = IFAC(IB+1) 106 CONTINUE IFAC(3) = 2 107 IF (NL .NE. 1) GO TO 104 IFAC(1) = N IFAC(2) = NF TPI = 6.28318530717959 ARGH = TPI/FLOAT(N) IS = 0 NFM1 = NF-1 L1 = 1 IF (NFM1 .EQ. 0) RETURN DO 110 K1=1,NFM1 IP = IFAC(K1+2) LD = 0 L2 = L1*IP IDO = N/L2 IPM = IP-1 DO 109 J=1,IPM LD = LD+L1 I = IS ARGLD = FLOAT(LD)*ARGH FI = 0. DO 108 II=3,IDO,2 I = I+2 FI = FI+1. ARG = FI*ARGLD WA(I-1) = COS(ARG) WA(I) = SIN(ARG) 108 CONTINUE IS = IS+IDO 109 CONTINUE L1 = L2 110 CONTINUE RETURN END C C ****************************************************************** C C subroutine rfftf(n,r,wsave) C C ****************************************************************** C C subroutine rfftf computes the fourier coefficients of a real C perodic sequence (fourier analysis). the transform is defined C below at output parameter r. C C input parameters C C n the length of the array r to be transformed. the method C is most efficient when n is a product of small primes. C n may change so long as different work arrays are provided C C r a real array of length n which contains the sequence C to be transformed C C wsave a work array which must be dimensioned at least 2*n+15. C in the program that calls rfftf. the wsave array must be C initialized by calling subroutine rffti(n,wsave) and a C different wsave array must be used for each different C value of n. this initialization does not have to be C repeated so long as n remains unchanged thus subsequent C transforms can be obtained faster than the first. C the same wsave array can be used by rfftf and rfftb. C C C output parameters C C r r(1) = the sum from i=1 to i=n of r(i) C C if n is even set l =n/2 , if n is odd set l = (n+1)/2 C C then for k = 2,...,l C C r(2*k-2) = the sum from i = 1 to i = n of C C r(i)*cos((k-1)*(i-1)*2*pi/n) C C r(2*k-1) = the sum from i = 1 to i = n of C C -r(i)*sin((k-1)*(i-1)*2*pi/n) C C if n is even C C r(n) = the sum from i = 1 to i = n of C C (-1)**(i-1)*r(i) C C ***** note C this transform is unnormalized since a call of rfftf C followed by a call of rfftb will multiply the input C sequence by n. C C wsave contains results which must not be destroyed between C calls of rfftf or rfftb. C SUBROUTINE RFFTF (N,R,WSAVE) DIMENSION R(*) ,WSAVE(*) IF (N .EQ. 1) RETURN CALL RFFTF1_OLD (N,R,WSAVE,WSAVE(N+1),WSAVE(2*N+1)) RETURN END C SUBROUTINE RFFTF1_OLD (N,C,CH,WA,IFAC) DIMENSION CH(*) ,C(*) ,WA(*) ,IFAC(*) NF = IFAC(2) NA = 1 L2 = N IW = N DO 111 K1=1,NF KH = NF-K1 IP = IFAC(KH+3) L1 = L2/IP IDO = N/L2 IDL1 = IDO*L1 IW = IW-(IP-1)*IDO NA = 1-NA IF (IP .NE. 4) GO TO 102 IX2 = IW+IDO IX3 = IX2+IDO IF (NA .NE. 0) GO TO 101 CALL RADF4 (IDO,L1,C,CH,WA(IW),WA(IX2),WA(IX3)) GO TO 110 101 CALL RADF4 (IDO,L1,CH,C,WA(IW),WA(IX2),WA(IX3)) GO TO 110 102 IF (IP .NE. 2) GO TO 104 IF (NA .NE. 0) GO TO 103 CALL RADF2 (IDO,L1,C,CH,WA(IW)) GO TO 110 103 CALL RADF2 (IDO,L1,CH,C,WA(IW)) GO TO 110 104 IF (IP .NE. 3) GO TO 106 IX2 = IW+IDO IF (NA .NE. 0) GO TO 105 CALL RADF3 (IDO,L1,C,CH,WA(IW),WA(IX2)) GO TO 110 105 CALL RADF3 (IDO,L1,CH,C,WA(IW),WA(IX2)) GO TO 110 106 IF (IP .NE. 5) GO TO 108 IX2 = IW+IDO IX3 = IX2+IDO IX4 = IX3+IDO IF (NA .NE. 0) GO TO 107 CALL RADF5 (IDO,L1,C,CH,WA(IW),WA(IX2),WA(IX3),WA(IX4)) GO TO 110 107 CALL RADF5 (IDO,L1,CH,C,WA(IW),WA(IX2),WA(IX3),WA(IX4)) GO TO 110 108 IF (IDO .EQ. 1) NA = 1-NA IF (NA .NE. 0) GO TO 109 CALL RADFG (IDO,IP,L1,IDL1,C,C,C,CH,CH,WA(IW)) NA = 1 GO TO 110 109 CALL RADFG (IDO,IP,L1,IDL1,CH,CH,CH,C,C,WA(IW)) NA = 0 110 L2 = L1 111 CONTINUE IF (NA .EQ. 1) RETURN DO 112 I=1,N C(I) = CH(I) 112 CONTINUE RETURN END C SUBROUTINE RADF2 (IDO,L1,CC,CH,WA1) DIMENSION CH(IDO,2,L1) ,CC(IDO,L1,2) , 1 WA1(*) DO 101 K=1,L1 CH(1,1,K) = CC(1,K,1)+CC(1,K,2) CH(IDO,2,K) = CC(1,K,1)-CC(1,K,2) 101 CONTINUE C IF (IDO-2) 107,105,102 IF (IDO-2 .LT. 0) GOTO 107 IF (IDO-2 .EQ. 0) GOTO 105 102 IDP2 = IDO+2 DO 104 K=1,L1 DO 103 I=3,IDO,2 IC = IDP2-I TR2 = WA1(I-2)*CC(I-1,K,2)+WA1(I-1)*CC(I,K,2) TI2 = WA1(I-2)*CC(I,K,2)-WA1(I-1)*CC(I-1,K,2) CH(I,1,K) = CC(I,K,1)+TI2 CH(IC,2,K) = TI2-CC(I,K,1) CH(I-1,1,K) = CC(I-1,K,1)+TR2 CH(IC-1,2,K) = CC(I-1,K,1)-TR2 103 CONTINUE 104 CONTINUE IF (MOD(IDO,2) .EQ. 1) RETURN 105 DO 106 K=1,L1 CH(1,2,K) = -CC(IDO,K,2) CH(IDO,1,K) = CC(IDO,K,1) 106 CONTINUE 107 RETURN END C SUBROUTINE RADF3 (IDO,L1,CC,CH,WA1,WA2) DIMENSION CH(IDO,3,L1) ,CC(IDO,L1,3) , 1 WA1(*) ,WA2(*) DATA TAUR,TAUI /-.5,.866025403784439/ DO 101 K=1,L1 CR2 = CC(1,K,2)+CC(1,K,3) CH(1,1,K) = CC(1,K,1)+CR2 CH(1,3,K) = TAUI*(CC(1,K,3)-CC(1,K,2)) CH(IDO,2,K) = CC(1,K,1)+TAUR*CR2 101 CONTINUE IF (IDO .EQ. 1) RETURN IDP2 = IDO+2 DO 103 K=1,L1 DO 102 I=3,IDO,2 IC = IDP2-I DR2 = WA1(I-2)*CC(I-1,K,2)+WA1(I-1)*CC(I,K,2) DI2 = WA1(I-2)*CC(I,K,2)-WA1(I-1)*CC(I-1,K,2) DR3 = WA2(I-2)*CC(I-1,K,3)+WA2(I-1)*CC(I,K,3) DI3 = WA2(I-2)*CC(I,K,3)-WA2(I-1)*CC(I-1,K,3) CR2 = DR2+DR3 CI2 = DI2+DI3 CH(I-1,1,K) = CC(I-1,K,1)+CR2 CH(I,1,K) = CC(I,K,1)+CI2 TR2 = CC(I-1,K,1)+TAUR*CR2 TI2 = CC(I,K,1)+TAUR*CI2 TR3 = TAUI*(DI2-DI3) TI3 = TAUI*(DR3-DR2) CH(I-1,3,K) = TR2+TR3 CH(IC-1,2,K) = TR2-TR3 CH(I,3,K) = TI2+TI3 CH(IC,2,K) = TI3-TI2 102 CONTINUE 103 CONTINUE RETURN END C SUBROUTINE RADF4 (IDO,L1,CC,CH,WA1,WA2,WA3) DIMENSION CC(IDO,L1,4) ,CH(IDO,4,L1) , 1 WA1(*) ,WA2(*) ,WA3(*) DATA HSQT2 /.7071067811865475/ DO 101 K=1,L1 TR1 = CC(1,K,2)+CC(1,K,4) TR2 = CC(1,K,1)+CC(1,K,3) CH(1,1,K) = TR1+TR2 CH(IDO,4,K) = TR2-TR1 CH(IDO,2,K) = CC(1,K,1)-CC(1,K,3) CH(1,3,K) = CC(1,K,4)-CC(1,K,2) 101 CONTINUE C IF (IDO-2) 107,105,102 IF (IDO-2 .LT. 0) GOTO 107 IF (IDO-2 .EQ. 0) GOTO 105 102 IDP2 = IDO+2 DO 104 K=1,L1 DO 103 I=3,IDO,2 IC = IDP2-I CR2 = WA1(I-2)*CC(I-1,K,2)+WA1(I-1)*CC(I,K,2) CI2 = WA1(I-2)*CC(I,K,2)-WA1(I-1)*CC(I-1,K,2) CR3 = WA2(I-2)*CC(I-1,K,3)+WA2(I-1)*CC(I,K,3) CI3 = WA2(I-2)*CC(I,K,3)-WA2(I-1)*CC(I-1,K,3) CR4 = WA3(I-2)*CC(I-1,K,4)+WA3(I-1)*CC(I,K,4) CI4 = WA3(I-2)*CC(I,K,4)-WA3(I-1)*CC(I-1,K,4) TR1 = CR2+CR4 TR4 = CR4-CR2 TI1 = CI2+CI4 TI4 = CI2-CI4 TI2 = CC(I,K,1)+CI3 TI3 = CC(I,K,1)-CI3 TR2 = CC(I-1,K,1)+CR3 TR3 = CC(I-1,K,1)-CR3 CH(I-1,1,K) = TR1+TR2 CH(IC-1,4,K) = TR2-TR1 CH(I,1,K) = TI1+TI2 CH(IC,4,K) = TI1-TI2 CH(I-1,3,K) = TI4+TR3 CH(IC-1,2,K) = TR3-TI4 CH(I,3,K) = TR4+TI3 CH(IC,2,K) = TR4-TI3 103 CONTINUE 104 CONTINUE IF (MOD(IDO,2) .EQ. 1) RETURN 105 CONTINUE DO 106 K=1,L1 TI1 = -HSQT2*(CC(IDO,K,2)+CC(IDO,K,4)) TR1 = HSQT2*(CC(IDO,K,2)-CC(IDO,K,4)) CH(IDO,1,K) = TR1+CC(IDO,K,1) CH(IDO,3,K) = CC(IDO,K,1)-TR1 CH(1,2,K) = TI1-CC(IDO,K,3) CH(1,4,K) = TI1+CC(IDO,K,3) 106 CONTINUE 107 RETURN END C SUBROUTINE RADF5 (IDO,L1,CC,CH,WA1,WA2,WA3,WA4) DIMENSION CC(IDO,L1,5) ,CH(IDO,5,L1) , 1 WA1(*) ,WA2(*) ,WA3(*) ,WA4(*) DATA TR11,TI11,TR12,TI12 /.309016994374947,.951056516295154, 1 -.809016994374947,.587785252292473/ DO 101 K=1,L1 CR2 = CC(1,K,5)+CC(1,K,2) CI5 = CC(1,K,5)-CC(1,K,2) CR3 = CC(1,K,4)+CC(1,K,3) CI4 = CC(1,K,4)-CC(1,K,3) CH(1,1,K) = CC(1,K,1)+CR2+CR3 CH(IDO,2,K) = CC(1,K,1)+TR11*CR2+TR12*CR3 CH(1,3,K) = TI11*CI5+TI12*CI4 CH(IDO,4,K) = CC(1,K,1)+TR12*CR2+TR11*CR3 CH(1,5,K) = TI12*CI5-TI11*CI4 101 CONTINUE IF (IDO .EQ. 1) RETURN IDP2 = IDO+2 DO 103 K=1,L1 DO 102 I=3,IDO,2 IC = IDP2-I DR2 = WA1(I-2)*CC(I-1,K,2)+WA1(I-1)*CC(I,K,2) DI2 = WA1(I-2)*CC(I,K,2)-WA1(I-1)*CC(I-1,K,2) DR3 = WA2(I-2)*CC(I-1,K,3)+WA2(I-1)*CC(I,K,3) DI3 = WA2(I-2)*CC(I,K,3)-WA2(I-1)*CC(I-1,K,3) DR4 = WA3(I-2)*CC(I-1,K,4)+WA3(I-1)*CC(I,K,4) DI4 = WA3(I-2)*CC(I,K,4)-WA3(I-1)*CC(I-1,K,4) DR5 = WA4(I-2)*CC(I-1,K,5)+WA4(I-1)*CC(I,K,5) DI5 = WA4(I-2)*CC(I,K,5)-WA4(I-1)*CC(I-1,K,5) CR2 = DR2+DR5 CI5 = DR5-DR2 CR5 = DI2-DI5 CI2 = DI2+DI5 CR3 = DR3+DR4 CI4 = DR4-DR3 CR4 = DI3-DI4 CI3 = DI3+DI4 CH(I-1,1,K) = CC(I-1,K,1)+CR2+CR3 CH(I,1,K) = CC(I,K,1)+CI2+CI3 TR2 = CC(I-1,K,1)+TR11*CR2+TR12*CR3 TI2 = CC(I,K,1)+TR11*CI2+TR12*CI3 TR3 = CC(I-1,K,1)+TR12*CR2+TR11*CR3 TI3 = CC(I,K,1)+TR12*CI2+TR11*CI3 TR5 = TI11*CR5+TI12*CR4 TI5 = TI11*CI5+TI12*CI4 TR4 = TI12*CR5-TI11*CR4 TI4 = TI12*CI5-TI11*CI4 CH(I-1,3,K) = TR2+TR5 CH(IC-1,2,K) = TR2-TR5 CH(I,3,K) = TI2+TI5 CH(IC,2,K) = TI5-TI2 CH(I-1,5,K) = TR3+TR4 CH(IC-1,4,K) = TR3-TR4 CH(I,5,K) = TI3+TI4 CH(IC,4,K) = TI4-TI3 102 CONTINUE 103 CONTINUE RETURN END C SUBROUTINE RADFG (IDO,IP,L1,IDL1,CC,C1,C2,CH,CH2,WA) DIMENSION CH(IDO,L1,IP) ,CC(IDO,IP,L1) , 1 C1(IDO,L1,IP) ,C2(IDL1,IP), 2 CH2(IDL1,IP) ,WA(*) DATA TPI/6.28318530717959/ ARG = TPI/FLOAT(IP) DCP = COS(ARG) DSP = SIN(ARG) IPPH = (IP+1)/2 IPP2 = IP+2 IDP2 = IDO+2 NBD = (IDO-1)/2 IF (IDO .EQ. 1) GO TO 119 DO 101 IK=1,IDL1 CH2(IK,1) = C2(IK,1) 101 CONTINUE DO 103 J=2,IP DO 102 K=1,L1 CH(1,K,J) = C1(1,K,J) 102 CONTINUE 103 CONTINUE IF (NBD .GT. L1) GO TO 107 IS = -IDO DO 106 J=2,IP IS = IS+IDO IDIJ = IS DO 105 I=3,IDO,2 IDIJ = IDIJ+2 DO 104 K=1,L1 CH(I-1,K,J) = WA(IDIJ-1)*C1(I-1,K,J)+WA(IDIJ)*C1(I,K,J) CH(I,K,J) = WA(IDIJ-1)*C1(I,K,J)-WA(IDIJ)*C1(I-1,K,J) 104 CONTINUE 105 CONTINUE 106 CONTINUE GO TO 111 107 IS = -IDO DO 110 J=2,IP IS = IS+IDO DO 109 K=1,L1 IDIJ = IS DO 108 I=3,IDO,2 IDIJ = IDIJ+2 CH(I-1,K,J) = WA(IDIJ-1)*C1(I-1,K,J)+WA(IDIJ)*C1(I,K,J) CH(I,K,J) = WA(IDIJ-1)*C1(I,K,J)-WA(IDIJ)*C1(I-1,K,J) 108 CONTINUE 109 CONTINUE 110 CONTINUE 111 IF (NBD .LT. L1) GO TO 115 DO 114 J=2,IPPH JC = IPP2-J DO 113 K=1,L1 DO 112 I=3,IDO,2 C1(I-1,K,J) = CH(I-1,K,J)+CH(I-1,K,JC) C1(I-1,K,JC) = CH(I,K,J)-CH(I,K,JC) C1(I,K,J) = CH(I,K,J)+CH(I,K,JC) C1(I,K,JC) = CH(I-1,K,JC)-CH(I-1,K,J) 112 CONTINUE 113 CONTINUE 114 CONTINUE GO TO 121 115 DO 118 J=2,IPPH JC = IPP2-J DO 117 I=3,IDO,2 DO 116 K=1,L1 C1(I-1,K,J) = CH(I-1,K,J)+CH(I-1,K,JC) C1(I-1,K,JC) = CH(I,K,J)-CH(I,K,JC) C1(I,K,J) = CH(I,K,J)+CH(I,K,JC) C1(I,K,JC) = CH(I-1,K,JC)-CH(I-1,K,J) 116 CONTINUE 117 CONTINUE 118 CONTINUE GO TO 121 119 DO 120 IK=1,IDL1 C2(IK,1) = CH2(IK,1) 120 CONTINUE 121 DO 123 J=2,IPPH JC = IPP2-J DO 122 K=1,L1 C1(1,K,J) = CH(1,K,J)+CH(1,K,JC) C1(1,K,JC) = CH(1,K,JC)-CH(1,K,J) 122 CONTINUE 123 CONTINUE C AR1 = 1. AI1 = 0. DO 127 L=2,IPPH LC = IPP2-L AR1H = DCP*AR1-DSP*AI1 AI1 = DCP*AI1+DSP*AR1 AR1 = AR1H DO 124 IK=1,IDL1 CH2(IK,L) = C2(IK,1)+AR1*C2(IK,2) CH2(IK,LC) = AI1*C2(IK,IP) 124 CONTINUE DC2 = AR1 DS2 = AI1 AR2 = AR1 AI2 = AI1 DO 126 J=3,IPPH JC = IPP2-J AR2H = DC2*AR2-DS2*AI2 AI2 = DC2*AI2+DS2*AR2 AR2 = AR2H DO 125 IK=1,IDL1 CH2(IK,L) = CH2(IK,L)+AR2*C2(IK,J) CH2(IK,LC) = CH2(IK,LC)+AI2*C2(IK,JC) 125 CONTINUE 126 CONTINUE 127 CONTINUE DO 129 J=2,IPPH DO 128 IK=1,IDL1 CH2(IK,1) = CH2(IK,1)+C2(IK,J) 128 CONTINUE 129 CONTINUE C IF (IDO .LT. L1) GO TO 132 DO 131 K=1,L1 DO 130 I=1,IDO CC(I,1,K) = CH(I,K,1) 130 CONTINUE 131 CONTINUE GO TO 135 132 DO 134 I=1,IDO DO 133 K=1,L1 CC(I,1,K) = CH(I,K,1) 133 CONTINUE 134 CONTINUE 135 DO 137 J=2,IPPH JC = IPP2-J J2 = J+J DO 136 K=1,L1 CC(IDO,J2-2,K) = CH(1,K,J) CC(1,J2-1,K) = CH(1,K,JC) 136 CONTINUE 137 CONTINUE IF (IDO .EQ. 1) RETURN IF (NBD .LT. L1) GO TO 141 DO 140 J=2,IPPH JC = IPP2-J J2 = J+J DO 139 K=1,L1 DO 138 I=3,IDO,2 IC = IDP2-I CC(I-1,J2-1,K) = CH(I-1,K,J)+CH(I-1,K,JC) CC(IC-1,J2-2,K) = CH(I-1,K,J)-CH(I-1,K,JC) CC(I,J2-1,K) = CH(I,K,J)+CH(I,K,JC) CC(IC,J2-2,K) = CH(I,K,JC)-CH(I,K,J) 138 CONTINUE 139 CONTINUE 140 CONTINUE RETURN 141 DO 144 J=2,IPPH JC = IPP2-J J2 = J+J DO 143 I=3,IDO,2 IC = IDP2-I DO 142 K=1,L1 CC(I-1,J2-1,K) = CH(I-1,K,J)+CH(I-1,K,JC) CC(IC-1,J2-2,K) = CH(I-1,K,J)-CH(I-1,K,JC) CC(I,J2-1,K) = CH(I,K,J)+CH(I,K,JC) CC(IC,J2-2,K) = CH(I,K,JC)-CH(I,K,J) 142 CONTINUE 143 CONTINUE 144 CONTINUE RETURN END C C ****************************************************************** C C subroutine rfftb(n,r,wsave) C C ****************************************************************** C C subroutine rfftb computes the real perodic sequence from its C fourier coefficients (fourier synthesis). the transform is defined C below at output parameter r. C C input parameters C C n the length of the array r to be transformed. the method C is most efficient when n is a product of small primes. C n may change so long as different work arrays are provided C C r a real array of length n which contains the sequence C to be transformed C C wsave a work array which must be dimensioned at least 2*n+15. C in the program that calls rfftb. the wsave array must be C initialized by calling subroutine rffti(n,wsave) and a C different wsave array must be used for each different C value of n. this initialization does not have to be C repeated so long as n remains unchanged thus subsequent C transforms can be obtained faster than the first. C the same wsave array can be used by rfftf and rfftb. C C C output parameters C C r for n even and for i = 1,...,n C C r(i) = r(1)+(-1)**(i-1)*r(n) C C plus the sum from k=2 to k=n/2 of C C 2.*r(2*k-2)*cos((k-1)*(i-1)*2*pi/n) C C -2.*r(2*k-1)*sin((k-1)*(i-1)*2*pi/n) C C for n odd and for i = 1,...,n C C r(i) = r(1) plus the sum from k=2 to k=(n+1)/2 of C C 2.*r(2*k-2)*cos((k-1)*(i-1)*2*pi/n) C C -2.*r(2*k-1)*sin((k-1)*(i-1)*2*pi/n) C C ***** note C this transform is unnormalized since a call of rfftf C followed by a call of rfftb will multiply the input C sequence by n. C C wsave contains results which must not be destroyed between C calls of rfftb or rfftf. C SUBROUTINE RFFTB (N,R,WSAVE) DIMENSION R(*) ,WSAVE(*) IF (N .EQ. 1) RETURN CALL RFFTB1_OLD (N,R,WSAVE,WSAVE(N+1),WSAVE(2*N+1)) RETURN END C SUBROUTINE RFFTB1_OLD (N,C,CH,WA,IFAC) DIMENSION CH(*) ,C(*) ,WA(*) ,IFAC(*) NF = IFAC(2) NA = 0 L1 = 1 IW = 1 DO 116 K1=1,NF IP = IFAC(K1+2) L2 = IP*L1 IDO = N/L2 IDL1 = IDO*L1 IF (IP .NE. 4) GO TO 103 IX2 = IW+IDO IX3 = IX2+IDO IF (NA .NE. 0) GO TO 101 CALL RADB4 (IDO,L1,C,CH,WA(IW),WA(IX2),WA(IX3)) GO TO 102 101 CALL RADB4 (IDO,L1,CH,C,WA(IW),WA(IX2),WA(IX3)) 102 NA = 1-NA GO TO 115 103 IF (IP .NE. 2) GO TO 106 IF (NA .NE. 0) GO TO 104 CALL RADB2 (IDO,L1,C,CH,WA(IW)) GO TO 105 104 CALL RADB2 (IDO,L1,CH,C,WA(IW)) 105 NA = 1-NA GO TO 115 106 IF (IP .NE. 3) GO TO 109 IX2 = IW+IDO IF (NA .NE. 0) GO TO 107 CALL RADB3 (IDO,L1,C,CH,WA(IW),WA(IX2)) GO TO 108 107 CALL RADB3 (IDO,L1,CH,C,WA(IW),WA(IX2)) 108 NA = 1-NA GO TO 115 109 IF (IP .NE. 5) GO TO 112 IX2 = IW+IDO IX3 = IX2+IDO IX4 = IX3+IDO IF (NA .NE. 0) GO TO 110 CALL RADB5 (IDO,L1,C,CH,WA(IW),WA(IX2),WA(IX3),WA(IX4)) GO TO 111 110 CALL RADB5 (IDO,L1,CH,C,WA(IW),WA(IX2),WA(IX3),WA(IX4)) 111 NA = 1-NA GO TO 115 112 IF (NA .NE. 0) GO TO 113 CALL RADBG (IDO,IP,L1,IDL1,C,C,C,CH,CH,WA(IW)) GO TO 114 113 CALL RADBG (IDO,IP,L1,IDL1,CH,CH,CH,C,C,WA(IW)) 114 IF (IDO .EQ. 1) NA = 1-NA 115 L1 = L2 IW = IW+(IP-1)*IDO 116 CONTINUE IF (NA .EQ. 0) RETURN DO 117 I=1,N C(I) = CH(I) 117 CONTINUE RETURN END C SUBROUTINE RADB2 (IDO,L1,CC,CH,WA1) DIMENSION CC(IDO,2,L1) ,CH(IDO,L1,2) , 1 WA1(*) DO 101 K=1,L1 CH(1,K,1) = CC(1,1,K)+CC(IDO,2,K) CH(1,K,2) = CC(1,1,K)-CC(IDO,2,K) 101 CONTINUE C IF (IDO-2) 107,105,102 IF (IDO-2 .LT. 0) GOTO 107 IF (IDO-2 .EQ. 0) GOTO 105 102 IDP2 = IDO+2 DO 104 K=1,L1 DO 103 I=3,IDO,2 IC = IDP2-I CH(I-1,K,1) = CC(I-1,1,K)+CC(IC-1,2,K) TR2 = CC(I-1,1,K)-CC(IC-1,2,K) CH(I,K,1) = CC(I,1,K)-CC(IC,2,K) TI2 = CC(I,1,K)+CC(IC,2,K) CH(I-1,K,2) = WA1(I-2)*TR2-WA1(I-1)*TI2 CH(I,K,2) = WA1(I-2)*TI2+WA1(I-1)*TR2 103 CONTINUE 104 CONTINUE IF (MOD(IDO,2) .EQ. 1) RETURN 105 DO 106 K=1,L1 CH(IDO,K,1) = CC(IDO,1,K)+CC(IDO,1,K) CH(IDO,K,2) = -(CC(1,2,K)+CC(1,2,K)) 106 CONTINUE 107 RETURN END C SUBROUTINE RADB3 (IDO,L1,CC,CH,WA1,WA2) DIMENSION CC(IDO,3,L1) ,CH(IDO,L1,3) , 1 WA1(*) ,WA2(*) DATA TAUR,TAUI /-.5,.866025403784439/ DO 101 K=1,L1 TR2 = CC(IDO,2,K)+CC(IDO,2,K) CR2 = CC(1,1,K)+TAUR*TR2 CH(1,K,1) = CC(1,1,K)+TR2 CI3 = TAUI*(CC(1,3,K)+CC(1,3,K)) CH(1,K,2) = CR2-CI3 CH(1,K,3) = CR2+CI3 101 CONTINUE IF (IDO .EQ. 1) RETURN IDP2 = IDO+2 DO 103 K=1,L1 DO 102 I=3,IDO,2 IC = IDP2-I TR2 = CC(I-1,3,K)+CC(IC-1,2,K) CR2 = CC(I-1,1,K)+TAUR*TR2 CH(I-1,K,1) = CC(I-1,1,K)+TR2 TI2 = CC(I,3,K)-CC(IC,2,K) CI2 = CC(I,1,K)+TAUR*TI2 CH(I,K,1) = CC(I,1,K)+TI2 CR3 = TAUI*(CC(I-1,3,K)-CC(IC-1,2,K)) CI3 = TAUI*(CC(I,3,K)+CC(IC,2,K)) DR2 = CR2-CI3 DR3 = CR2+CI3 DI2 = CI2+CR3 DI3 = CI2-CR3 CH(I-1,K,2) = WA1(I-2)*DR2-WA1(I-1)*DI2 CH(I,K,2) = WA1(I-2)*DI2+WA1(I-1)*DR2 CH(I-1,K,3) = WA2(I-2)*DR3-WA2(I-1)*DI3 CH(I,K,3) = WA2(I-2)*DI3+WA2(I-1)*DR3 102 CONTINUE 103 CONTINUE RETURN END C SUBROUTINE RADB4 (IDO,L1,CC,CH,WA1,WA2,WA3) DIMENSION CC(IDO,4,L1) ,CH(IDO,L1,4) , 1 WA1(*) ,WA2(*) ,WA3(*) DATA SQRT2 /1.414213562373095/ DO 101 K=1,L1 TR1 = CC(1,1,K)-CC(IDO,4,K) TR2 = CC(1,1,K)+CC(IDO,4,K) TR3 = CC(IDO,2,K)+CC(IDO,2,K) TR4 = CC(1,3,K)+CC(1,3,K) CH(1,K,1) = TR2+TR3 CH(1,K,2) = TR1-TR4 CH(1,K,3) = TR2-TR3 CH(1,K,4) = TR1+TR4 101 CONTINUE C IF (IDO-2) 107,105,102 IF (IDO-2 .LT. 0) GOTO 107 IF (IDO-2 .EQ. 0) GOTO 105 102 IDP2 = IDO+2 DO 104 K=1,L1 DO 103 I=3,IDO,2 IC = IDP2-I TI1 = CC(I,1,K)+CC(IC,4,K) TI2 = CC(I,1,K)-CC(IC,4,K) TI3 = CC(I,3,K)-CC(IC,2,K) TR4 = CC(I,3,K)+CC(IC,2,K) TR1 = CC(I-1,1,K)-CC(IC-1,4,K) TR2 = CC(I-1,1,K)+CC(IC-1,4,K) TI4 = CC(I-1,3,K)-CC(IC-1,2,K) TR3 = CC(I-1,3,K)+CC(IC-1,2,K) CH(I-1,K,1) = TR2+TR3 CR3 = TR2-TR3 CH(I,K,1) = TI2+TI3 CI3 = TI2-TI3 CR2 = TR1-TR4 CR4 = TR1+TR4 CI2 = TI1+TI4 CI4 = TI1-TI4 CH(I-1,K,2) = WA1(I-2)*CR2-WA1(I-1)*CI2 CH(I,K,2) = WA1(I-2)*CI2+WA1(I-1)*CR2 CH(I-1,K,3) = WA2(I-2)*CR3-WA2(I-1)*CI3 CH(I,K,3) = WA2(I-2)*CI3+WA2(I-1)*CR3 CH(I-1,K,4) = WA3(I-2)*CR4-WA3(I-1)*CI4 CH(I,K,4) = WA3(I-2)*CI4+WA3(I-1)*CR4 103 CONTINUE 104 CONTINUE IF (MOD(IDO,2) .EQ. 1) RETURN 105 CONTINUE DO 106 K=1,L1 TI1 = CC(1,2,K)+CC(1,4,K) TI2 = CC(1,4,K)-CC(1,2,K) TR1 = CC(IDO,1,K)-CC(IDO,3,K) TR2 = CC(IDO,1,K)+CC(IDO,3,K) CH(IDO,K,1) = TR2+TR2 CH(IDO,K,2) = SQRT2*(TR1-TI1) CH(IDO,K,3) = TI2+TI2 CH(IDO,K,4) = -SQRT2*(TR1+TI1) 106 CONTINUE 107 RETURN END C SUBROUTINE RADB5 (IDO,L1,CC,CH,WA1,WA2,WA3,WA4) DIMENSION CC(IDO,5,L1) ,CH(IDO,L1,5) , 1 WA1(*) ,WA2(*) ,WA3(*) ,WA4(*) DATA TR11,TI11,TR12,TI12 /.309016994374947,.951056516295154, 1 -.809016994374947,.587785252292473/ DO 101 K=1,L1 TI5 = CC(1,3,K)+CC(1,3,K) TI4 = CC(1,5,K)+CC(1,5,K) TR2 = CC(IDO,2,K)+CC(IDO,2,K) TR3 = CC(IDO,4,K)+CC(IDO,4,K) CH(1,K,1) = CC(1,1,K)+TR2+TR3 CR2 = CC(1,1,K)+TR11*TR2+TR12*TR3 CR3 = CC(1,1,K)+TR12*TR2+TR11*TR3 CI5 = TI11*TI5+TI12*TI4 CI4 = TI12*TI5-TI11*TI4 CH(1,K,2) = CR2-CI5 CH(1,K,3) = CR3-CI4 CH(1,K,4) = CR3+CI4 CH(1,K,5) = CR2+CI5 101 CONTINUE IF (IDO .EQ. 1) RETURN IDP2 = IDO+2 DO 103 K=1,L1 DO 102 I=3,IDO,2 IC = IDP2-I TI5 = CC(I,3,K)+CC(IC,2,K) TI2 = CC(I,3,K)-CC(IC,2,K) TI4 = CC(I,5,K)+CC(IC,4,K) TI3 = CC(I,5,K)-CC(IC,4,K) TR5 = CC(I-1,3,K)-CC(IC-1,2,K) TR2 = CC(I-1,3,K)+CC(IC-1,2,K) TR4 = CC(I-1,5,K)-CC(IC-1,4,K) TR3 = CC(I-1,5,K)+CC(IC-1,4,K) CH(I-1,K,1) = CC(I-1,1,K)+TR2+TR3 CH(I,K,1) = CC(I,1,K)+TI2+TI3 CR2 = CC(I-1,1,K)+TR11*TR2+TR12*TR3 CI2 = CC(I,1,K)+TR11*TI2+TR12*TI3 CR3 = CC(I-1,1,K)+TR12*TR2+TR11*TR3 CI3 = CC(I,1,K)+TR12*TI2+TR11*TI3 CR5 = TI11*TR5+TI12*TR4 CI5 = TI11*TI5+TI12*TI4 CR4 = TI12*TR5-TI11*TR4 CI4 = TI12*TI5-TI11*TI4 DR3 = CR3-CI4 DR4 = CR3+CI4 DI3 = CI3+CR4 DI4 = CI3-CR4 DR5 = CR2+CI5 DR2 = CR2-CI5 DI5 = CI2-CR5 DI2 = CI2+CR5 CH(I-1,K,2) = WA1(I-2)*DR2-WA1(I-1)*DI2 CH(I,K,2) = WA1(I-2)*DI2+WA1(I-1)*DR2 CH(I-1,K,3) = WA2(I-2)*DR3-WA2(I-1)*DI3 CH(I,K,3) = WA2(I-2)*DI3+WA2(I-1)*DR3 CH(I-1,K,4) = WA3(I-2)*DR4-WA3(I-1)*DI4 CH(I,K,4) = WA3(I-2)*DI4+WA3(I-1)*DR4 CH(I-1,K,5) = WA4(I-2)*DR5-WA4(I-1)*DI5 CH(I,K,5) = WA4(I-2)*DI5+WA4(I-1)*DR5 102 CONTINUE 103 CONTINUE RETURN END C SUBROUTINE RADBG (IDO,IP,L1,IDL1,CC,C1,C2,CH,CH2,WA) DIMENSION CH(IDO,L1,IP) ,CC(IDO,IP,L1) , 1 C1(IDO,L1,IP) ,C2(IDL1,IP), 2 CH2(IDL1,IP) ,WA(*) DATA TPI/6.28318530717959/ ARG = TPI/FLOAT(IP) DCP = COS(ARG) DSP = SIN(ARG) IDP2 = IDO+2 NBD = (IDO-1)/2 IPP2 = IP+2 IPPH = (IP+1)/2 IF (IDO .LT. L1) GO TO 103 DO 102 K=1,L1 DO 101 I=1,IDO CH(I,K,1) = CC(I,1,K) 101 CONTINUE 102 CONTINUE GO TO 106 103 DO 105 I=1,IDO DO 104 K=1,L1 CH(I,K,1) = CC(I,1,K) 104 CONTINUE 105 CONTINUE 106 DO 108 J=2,IPPH JC = IPP2-J J2 = J+J DO 107 K=1,L1 CH(1,K,J) = CC(IDO,J2-2,K)+CC(IDO,J2-2,K) CH(1,K,JC) = CC(1,J2-1,K)+CC(1,J2-1,K) 107 CONTINUE 108 CONTINUE IF (IDO .EQ. 1) GO TO 116 IF (NBD .LT. L1) GO TO 112 DO 111 J=2,IPPH JC = IPP2-J DO 110 K=1,L1 DO 109 I=3,IDO,2 IC = IDP2-I CH(I-1,K,J) = CC(I-1,2*J-1,K)+CC(IC-1,2*J-2,K) CH(I-1,K,JC) = CC(I-1,2*J-1,K)-CC(IC-1,2*J-2,K) CH(I,K,J) = CC(I,2*J-1,K)-CC(IC,2*J-2,K) CH(I,K,JC) = CC(I,2*J-1,K)+CC(IC,2*J-2,K) 109 CONTINUE 110 CONTINUE 111 CONTINUE GO TO 116 112 DO 115 J=2,IPPH JC = IPP2-J DO 114 I=3,IDO,2 IC = IDP2-I DO 113 K=1,L1 CH(I-1,K,J) = CC(I-1,2*J-1,K)+CC(IC-1,2*J-2,K) CH(I-1,K,JC) = CC(I-1,2*J-1,K)-CC(IC-1,2*J-2,K) CH(I,K,J) = CC(I,2*J-1,K)-CC(IC,2*J-2,K) CH(I,K,JC) = CC(I,2*J-1,K)+CC(IC,2*J-2,K) 113 CONTINUE 114 CONTINUE 115 CONTINUE 116 AR1 = 1. AI1 = 0. DO 120 L=2,IPPH LC = IPP2-L AR1H = DCP*AR1-DSP*AI1 AI1 = DCP*AI1+DSP*AR1 AR1 = AR1H DO 117 IK=1,IDL1 C2(IK,L) = CH2(IK,1)+AR1*CH2(IK,2) C2(IK,LC) = AI1*CH2(IK,IP) 117 CONTINUE DC2 = AR1 DS2 = AI1 AR2 = AR1 AI2 = AI1 DO 119 J=3,IPPH JC = IPP2-J AR2H = DC2*AR2-DS2*AI2 AI2 = DC2*AI2+DS2*AR2 AR2 = AR2H DO 118 IK=1,IDL1 C2(IK,L) = C2(IK,L)+AR2*CH2(IK,J) C2(IK,LC) = C2(IK,LC)+AI2*CH2(IK,JC) 118 CONTINUE 119 CONTINUE 120 CONTINUE DO 122 J=2,IPPH DO 121 IK=1,IDL1 CH2(IK,1) = CH2(IK,1)+CH2(IK,J) 121 CONTINUE 122 CONTINUE DO 124 J=2,IPPH JC = IPP2-J DO 123 K=1,L1 CH(1,K,J) = C1(1,K,J)-C1(1,K,JC) CH(1,K,JC) = C1(1,K,J)+C1(1,K,JC) 123 CONTINUE 124 CONTINUE IF (IDO .EQ. 1) GO TO 132 IF (NBD .LT. L1) GO TO 128 DO 127 J=2,IPPH JC = IPP2-J DO 126 K=1,L1 DO 125 I=3,IDO,2 CH(I-1,K,J) = C1(I-1,K,J)-C1(I,K,JC) CH(I-1,K,JC) = C1(I-1,K,J)+C1(I,K,JC) CH(I,K,J) = C1(I,K,J)+C1(I-1,K,JC) CH(I,K,JC) = C1(I,K,J)-C1(I-1,K,JC) 125 CONTINUE 126 CONTINUE 127 CONTINUE GO TO 132 128 DO 131 J=2,IPPH JC = IPP2-J DO 130 I=3,IDO,2 DO 129 K=1,L1 CH(I-1,K,J) = C1(I-1,K,J)-C1(I,K,JC) CH(I-1,K,JC) = C1(I-1,K,J)+C1(I,K,JC) CH(I,K,J) = C1(I,K,J)+C1(I-1,K,JC) CH(I,K,JC) = C1(I,K,J)-C1(I-1,K,JC) 129 CONTINUE 130 CONTINUE 131 CONTINUE 132 CONTINUE IF (IDO .EQ. 1) RETURN DO 133 IK=1,IDL1 C2(IK,1) = CH2(IK,1) 133 CONTINUE DO 135 J=2,IP DO 134 K=1,L1 C1(1,K,J) = CH(1,K,J) 134 CONTINUE 135 CONTINUE IF (NBD .GT. L1) GO TO 139 IS = -IDO DO 138 J=2,IP IS = IS+IDO IDIJ = IS DO 137 I=3,IDO,2 IDIJ = IDIJ+2 DO 136 K=1,L1 C1(I-1,K,J) = WA(IDIJ-1)*CH(I-1,K,J)-WA(IDIJ)*CH(I,K,J) C1(I,K,J) = WA(IDIJ-1)*CH(I,K,J)+WA(IDIJ)*CH(I-1,K,J) 136 CONTINUE 137 CONTINUE 138 CONTINUE GO TO 143 139 IS = -IDO DO 142 J=2,IP IS = IS+IDO DO 141 K=1,L1 IDIJ = IS DO 140 I=3,IDO,2 IDIJ = IDIJ+2 C1(I-1,K,J) = WA(IDIJ-1)*CH(I-1,K,J)-WA(IDIJ)*CH(I,K,J) C1(I,K,J) = WA(IDIJ-1)*CH(I,K,J)+WA(IDIJ)*CH(I-1,K,J) 140 CONTINUE 141 CONTINUE 142 CONTINUE 143 RETURN END C C ****************************************************************** C C subroutine ezffti(n,wsave) C C ****************************************************************** C C subroutine ezffti initializes the array wsave which is used in C both ezfftf and ezfftb. the prime factorization of n together with C a tabulation of the trigonometric functions are computed and C stored in wsave. C C input parameter C C n the length of the sequence to be transformed. C C output parameter C C wsave a work array which must be dimensioned at least 3*n+15. C the same work array can be used for both ezfftf and ezfftb C as long as n remains unchanged. different wsave arrays C are required for different values of n. C SUBROUTINE EZFFTI (N,WSAVE) DIMENSION WSAVE(*) IF (N .EQ. 1) RETURN CALL EZFFT1 (N,WSAVE(2*N+1),WSAVE(3*N+1)) RETURN END C SUBROUTINE EZFFT1 (N,WA,IFAC) DIMENSION WA(*) ,IFAC(*) ,NTRYH(4) DATA NTRYH(1),NTRYH(2),NTRYH(3),NTRYH(4)/4,2,3,5/ 1 ,TPI/6.28318530717959/ NL = N NF = 0 J = 0 101 J = J+1 C IF (J-4) 102,102,103 IF (J-4 .GT. 0) GOTO 103 102 NTRY = NTRYH(J) GO TO 104 103 NTRY = NTRY+2 104 NQ = NL/NTRY NR = NL-NTRY*NQ C IF (NR) 101,105,101 IF (NR .NE. 0) GOTO 101 105 NF = NF+1 IFAC(NF+2) = NTRY NL = NQ IF (NTRY .NE. 2) GO TO 107 IF (NF .EQ. 1) GO TO 107 DO 106 I=2,NF IB = NF-I+2 IFAC(IB+2) = IFAC(IB+1) 106 CONTINUE IFAC(3) = 2 107 IF (NL .NE. 1) GO TO 104 IFAC(1) = N IFAC(2) = NF ARGH = TPI/FLOAT(N) IS = 0 NFM1 = NF-1 L1 = 1 IF (NFM1 .EQ. 0) RETURN DO 111 K1=1,NFM1 IP = IFAC(K1+2) L2 = L1*IP IDO = N/L2 IPM = IP-1 ARG1 = FLOAT(L1)*ARGH CH1 = 1. SH1 = 0. DCH1 = COS(ARG1) DSH1 = SIN(ARG1) DO 110 J=1,IPM CH1H = DCH1*CH1-DSH1*SH1 SH1 = DCH1*SH1+DSH1*CH1 CH1 = CH1H I = IS+2 WA(I-1) = CH1 WA(I) = SH1 IF (IDO .LT. 5) GO TO 109 DO 108 II=5,IDO,2 I = I+2 WA(I-1) = CH1*WA(I-3)-SH1*WA(I-2) WA(I) = CH1*WA(I-2)+SH1*WA(I-3) 108 CONTINUE 109 IS = IS+IDO 110 CONTINUE L1 = L2 111 CONTINUE RETURN END C C ****************************************************************** C C subroutine ezfftf(n,r,azero,a,b,wsave) C C ****************************************************************** C C subroutine ezfftf computes the fourier coefficients of a real C perodic sequence (fourier analysis). the transform is defined C below at output parameters azero,a and b. ezfftf is a simplified C but slower version of rfftf. C C input parameters C C n the length of the array r to be transformed. the method C is must efficient when n is the product of small primes. C C r a real array of length n which contains the sequence C to be transformed. r is not destroyed. C C C wsave a work array which must be dimensioned at least 3*n+15. C in the program that calls ezfftf. the wsave array must be C initialized by calling subroutine ezffti(n,wsave) and a C different wsave array must be used for each different C value of n. this initialization does not have to be C repeated so long as n remains unchanged thus subsequent C transforms can be obtained faster than the first. C the same wsave array can be used by ezfftf and ezfftb. C C output parameters C C azero the sum from i=1 to i=n of r(i)/n C C a,b for n even b(n/2)=0. and a(n/2) is the sum from i=1 to C i=n of (-1)**(i-1)*r(i)/n C C for n even define kmax=n/2-1 C for n odd define kmax=(n-1)/2 C C then for k=1,...,kmax C C a(k) equals the sum from i=1 to i=n of C C 2./n*r(i)*cos(k*(i-1)*2*pi/n) C C b(k) equals the sum from i=1 to i=n of C C 2./n*r(i)*sin(k*(i-1)*2*pi/n) C SUBROUTINE EZFFTF (N,R,AZERO,A,B,WSAVE) C C VERSION 3 JUNE 1979 C DIMENSION R(*) ,A(*) ,B(*) ,WSAVE(*) C IF (N-2) 101,102,103 IF (N-2 .EQ. 0) GOTO 102 IF (N-2 .GT. 0) GOTO 103 101 AZERO = R(1) RETURN 102 AZERO = .5*(R(1)+R(2)) A(1) = .5*(R(1)-R(2)) RETURN 103 DO 104 I=1,N WSAVE(I) = R(I) 104 CONTINUE CALL RFFTF (N,WSAVE,WSAVE(N+1)) CF = 2./FLOAT(N) CFM = -CF AZERO = .5*CF*WSAVE(1) NS2 = (N+1)/2 NS2M = NS2-1 DO 105 I=1,NS2M A(I) = CF*WSAVE(2*I) B(I) = CFM*WSAVE(2*I+1) 105 CONTINUE IF (MOD(N,2) .EQ. 1) RETURN A(NS2) = .5*CF*WSAVE(N) B(NS2) = 0. RETURN END C C ****************************************************************** C C subroutine ezfftb(n,r,azero,a,b,wsave) C C ****************************************************************** C C subroutine ezfftb computes a real perodic sequence from its C fourier coefficients (fourier synthesis). the transform is C defined below at output parameter r. ezfftb is a simplified C but slower version of rfftb. C C input parameters C C n the length of the output array r. the method is most C efficient when n is the product of small primes. C C azero the constant fourier coefficient C C a,b arrays which contain the remaining fourier coefficients C these arrays are not destroyed. C C the length of these arrays depends on whether n is even or C odd. C C if n is even n/2 locations are required C if n is odd (n-1)/2 locations are required C C wsave a work array which must be dimensioned at least 3*n+15. C in the program that calls ezfftb. the wsave array must be C initialized by calling subroutine ezffti(n,wsave) and a C different wsave array must be used for each different C value of n. this initialization does not have to be C repeated so long as n remains unchanged thus subsequent C transforms can be obtained faster than the first. C the same wsave array can be used by ezfftf and ezfftb. C C C output parameters C C r if n is even define kmax=n/2 C if n is odd define kmax=(n-1)/2 C C then for i=1,...,n C C r(i)=azero plus the sum from k=1 to k=kmax of C C a(k)*cos(k*(i-1)*2*pi/n)+b(k)*sin(k*(i-1)*2*pi/n) C C ********************* complex notation ************************** C C for j=1,...,n C C r(j) equals the sum from k=-kmax to k=kmax of C C c(k)*exp(i*k*(j-1)*2*pi/n) C C where C C c(k) = .5*cmplx(a(k),-b(k)) for k=1,...,kmax C C c(-k) = conjg(c(k)) C C c(0) = azero C C and i=sqrt(-1) C C *************** amplitude - phase notation *********************** C C for i=1,...,n C C r(i) equals azero plus the sum from k=1 to k=kmax of C C alpha(k)*cos(k*(i-1)*2*pi/n+beta(k)) C C where C C alpha(k) = sqrt(a(k)*a(k)+b(k)*b(k)) C C cos(beta(k))=a(k)/alpha(k) C C sin(beta(k))=-b(k)/alpha(k) C SUBROUTINE EZFFTB (N,R,AZERO,A,B,WSAVE) DIMENSION R(*) ,A(*) ,B(*) ,WSAVE(*) C IF (N-2) 101,102,103 IF (N-2 .EQ. 0) GOTO 102 IF (N-2 .GT. 0) GOTO 103 101 R(1) = AZERO RETURN 102 R(1) = AZERO+A(1) R(2) = AZERO-A(1) RETURN 103 NS2 = (N-1)/2 DO 104 I=1,NS2 R(2*I) = .5*A(I) R(2*I+1) = -.5*B(I) 104 CONTINUE R(1) = AZERO IF (MOD(N,2) .EQ. 0) R(N) = A(NS2+1) CALL RFFTB (N,R,WSAVE(N+1)) RETURN END C C ****************************************************************** C C subroutine sinti(n,wsave) C C ****************************************************************** C C subroutine sinti initializes the array wsave which is used in C subroutine sint. the prime factorization of n together with C a tabulation of the trigonometric functions are computed and C stored in wsave. C C input parameter C C n the length of the sequence to be transformed. the method C is most efficient when n+1 is a product of small primes. C C output parameter C C wsave a work array with at least int(2.5*n+15) locations. C different wsave arrays are required for different values C of n. the contents of wsave must not be changed between C calls of sint. C SUBROUTINE SINTI (N,WSAVE) DIMENSION WSAVE(*) DATA PI /3.14159265358979/ IF (N .LE. 1) RETURN NS2 = N/2 NP1 = N+1 DT = PI/FLOAT(NP1) DO 101 K=1,NS2 WSAVE(K) = 2.*SIN(K*DT) 101 CONTINUE CALL RFFTI (NP1,WSAVE(NS2+1)) RETURN END C C ****************************************************************** C C subroutine sint(n,x,wsave) C C ****************************************************************** C C subroutine sint computes the discrete fourier sine transform C of an odd sequence x(i). the transform is defined below at C output parameter x. C C sint is the unnormalized inverse of itself since a call of sint C followed by another call of sint will multiply the input sequence C x by 2*(n+1). C C the array wsave which is used by subroutine sint must be C initialized by calling subroutine sinti(n,wsave). C C input parameters C C n the length of the sequence to be transformed. the method C is most efficient when n+1 is the product of small primes. C C x an array which contains the sequence to be transformed C C C wsave a work array with dimension at least int(2.5*n+15) C in the program that calls sint. the wsave array must be C initialized by calling subroutine sinti(n,wsave) and a C different wsave array must be used for each different C value of n. this initialization does not have to be C repeated so long as n remains unchanged thus subsequent C transforms can be obtained faster than the first. C C output parameters C C x for i=1,...,n C C x(i)= the sum from k=1 to k=n C C 2*x(k)*sin(k*i*pi/(n+1)) C C a call of sint followed by another call of C sint will multiply the sequence x by 2*(n+1). C hence sint is the unnormalized inverse C of itself. C C wsave contains initialization calculations which must not be C destroyed between calls of sint. C SUBROUTINE SINT (N,X,WSAVE) DIMENSION X(*) ,WSAVE(*) NP1 = N+1 IW1 = N/2+1 IW2 = IW1+NP1 IW3 = IW2+NP1 CALL SINT1(N,X,WSAVE,WSAVE(IW1),WSAVE(IW2),WSAVE(IW3)) RETURN END C SUBROUTINE SINT1(N,WAR,WAS,XH,X,IFAC) DIMENSION WAR(*),WAS(*),X(*),XH(*),IFAC(*) DATA SQRT3 /1.73205080756888/ DO 100 I=1,N XH(I) = WAR(I) WAR(I) = X(I) 100 CONTINUE C IF (N-2) 101,102,103 IF (N-2 .EQ. 0) GOTO 102 IF (N-2 .GT. 0) GOTO 103 101 XH(1) = XH(1)+XH(1) GO TO 106 102 XHOLD = SQRT3*(XH(1)+XH(2)) XH(2) = SQRT3*(XH(1)-XH(2)) XH(1) = XHOLD GO TO 106 103 NP1 = N+1 NS2 = N/2 X(1) = 0. DO 104 K=1,NS2 KC = NP1-K T1 = XH(K)-XH(KC) T2 = WAS(K)*(XH(K)+XH(KC)) X(K+1) = T1+T2 X(KC+1) = T2-T1 104 CONTINUE MODN = MOD(N,2) IF (MODN .NE. 0) X(NS2+2) = 4.*XH(NS2+1) CALL RFFTF1_OLD (NP1,X,XH,WAR,IFAC) XH(1) = .5*X(1) DO 105 I=3,N,2 XH(I-1) = -X(I) XH(I) = XH(I-2)+X(I-1) 105 CONTINUE IF (MODN .NE. 0) GO TO 106 XH(N) = -X(N+1) 106 DO 107 I=1,N X(I) = WAR(I) WAR(I) = XH(I) 107 CONTINUE RETURN END C C ****************************************************************** C C subroutine costi(n,wsave) C C ****************************************************************** C C subroutine costi initializes the array wsave which is used in C subroutine cost. the prime factorization of n together with C a tabulation of the trigonometric functions are computed and C stored in wsave. C C input parameter C C n the length of the sequence to be transformed. the method C is most efficient when n-1 is a product of small primes. C C output parameter C C wsave a work array which must be dimensioned at least 3*n+15. C different wsave arrays are required for different values C of n. the contents of wsave must not be changed between C calls of cost. C SUBROUTINE COSTI (N,WSAVE) DIMENSION WSAVE(*) DATA PI /3.14159265358979/ IF (N .LE. 3) RETURN NM1 = N-1 NP1 = N+1 NS2 = N/2 DT = PI/FLOAT(NM1) FK = 0. DO 101 K=2,NS2 KC = NP1-K FK = FK+1. WSAVE(K) = 2.*SIN(FK*DT) WSAVE(KC) = 2.*COS(FK*DT) 101 CONTINUE CALL RFFTI (NM1,WSAVE(N+1)) RETURN END C C ****************************************************************** C C subroutine cost(n,x,wsave) C C ****************************************************************** C C subroutine cost computes the discrete fourier cosine transform C of an even sequence x(i). the transform is defined below at output C parameter x. C C cost is the unnormalized inverse of itself since a call of cost C followed by another call of cost will multiply the input sequence C x by 2*(n-1). the transform is defined below at output parameter x C C the array wsave which is used by subroutine cost must be C initialized by calling subroutine costi(n,wsave). C C input parameters C C n the length of the sequence x. n must be greater than 1. C the method is most efficient when n-1 is a product of C small primes. C C x an array which contains the sequence to be transformed C C wsave a work array which must be dimensioned at least 3*n+15 C in the program that calls cost. the wsave array must be C initialized by calling subroutine costi(n,wsave) and a C different wsave array must be used for each different C value of n. this initialization does not have to be C repeated so long as n remains unchanged thus subsequent C transforms can be obtained faster than the first. C C output parameters C C x for i=1,...,n C C x(i) = x(1)+(-1)**(i-1)*x(n) C C + the sum from k=2 to k=n-1 C C 2*x(k)*cos((k-1)*(i-1)*pi/(n-1)) C C a call of cost followed by another call of C cost will multiply the sequence x by 2*(n-1) C hence cost is the unnormalized inverse C of itself. C C wsave contains initialization calculations which must not be C destroyed between calls of cost. C SUBROUTINE COST (N,X,WSAVE) DIMENSION X(*) ,WSAVE(*) NM1 = N-1 NP1 = N+1 NS2 = N/2 C IF (N-2) 106,101,102 IF (N-2 .LT. 0) GOTO 106 IF (N-2 .GT. 0) GOTO 102 101 X1H = X(1)+X(2) X(2) = X(1)-X(2) X(1) = X1H RETURN 102 IF (N .GT. 3) GO TO 103 X1P3 = X(1)+X(3) TX2 = X(2)+X(2) X(2) = X(1)-X(3) X(1) = X1P3+TX2 X(3) = X1P3-TX2 RETURN 103 C1 = X(1)-X(N) X(1) = X(1)+X(N) DO 104 K=2,NS2 KC = NP1-K T1 = X(K)+X(KC) T2 = X(K)-X(KC) C1 = C1+WSAVE(KC)*T2 T2 = WSAVE(K)*T2 X(K) = T1-T2 X(KC) = T1+T2 104 CONTINUE MODN = MOD(N,2) IF (MODN .NE. 0) X(NS2+1) = X(NS2+1)+X(NS2+1) CALL RFFTF (NM1,X,WSAVE(N+1)) XIM2 = X(2) X(2) = C1 DO 105 I=4,N,2 XI = X(I) X(I) = X(I-2)-X(I-1) X(I-1) = XIM2 XIM2 = XI 105 CONTINUE IF (MODN .NE. 0) X(N) = XIM2 106 RETURN END C C ****************************************************************** C C subroutine sinqi(n,wsave) C C ****************************************************************** C C subroutine sinqi initializes the array wsave which is used in C both sinqf and sinqb. the prime factorization of n together with C a tabulation of the trigonometric functions are computed and C stored in wsave. C C input parameter C C n the length of the sequence to be transformed. the method C is most efficient when n is a product of small primes. C C output parameter C C wsave a work array which must be dimensioned at least 3*n+15. C the same work array can be used for both sinqf and sinqb C as long as n remains unchanged. different wsave arrays C are required for different values of n. the contents of C wsave must not be changed between calls of sinqf or sinqb. C SUBROUTINE SINQI (N,WSAVE) DIMENSION WSAVE(*) CALL COSQI (N,WSAVE) RETURN END C C ****************************************************************** C C subroutine sinqf(n,x,wsave) C C ****************************************************************** C C subroutine sinqf computes the fast fourier transform of quarter C wave data. that is , sinqf computes the coefficients in a sine C series representation with only odd wave numbers. the transform C is defined below at output parameter x. C C sinqb is the unnormalized inverse of sinqf since a call of sinqf C followed by a call of sinqb will multiply the input sequence x C by 4*n. C C the array wsave which is used by subroutine sinqf must be C initialized by calling subroutine sinqi(n,wsave). C C C input parameters C C n the length of the array x to be transformed. the method C is most efficient when n is a product of small primes. C C x an array which contains the sequence to be transformed C C wsave a work array which must be dimensioned at least 3*n+15. C in the program that calls sinqf. the wsave array must be C initialized by calling subroutine sinqi(n,wsave) and a C different wsave array must be used for each different C value of n. this initialization does not have to be C repeated so long as n remains unchanged thus subsequent C transforms can be obtained faster than the first. C C output parameters C C x for i=1,...,n C C x(i) = (-1)**(i-1)*x(n) C C + the sum from k=1 to k=n-1 of C C 2*x(k)*sin((2*i-1)*k*pi/(2*n)) C C a call of sinqf followed by a call of C sinqb will multiply the sequence x by 4*n. C therefore sinqb is the unnormalized inverse C of sinqf. C C wsave contains initialization calculations which must not C be destroyed between calls of sinqf or sinqb. C SUBROUTINE SINQF (N,X,WSAVE) DIMENSION X(*) ,WSAVE(*) IF (N .EQ. 1) RETURN NS2 = N/2 DO 101 K=1,NS2 KC = N-K XHOLD = X(K) X(K) = X(KC+1) X(KC+1) = XHOLD 101 CONTINUE CALL COSQF (N,X,WSAVE) DO 102 K=2,N,2 X(K) = -X(K) 102 CONTINUE RETURN END C C ****************************************************************** C C subroutine sinqb(n,x,wsave) C C ****************************************************************** C C subroutine sinqb computes the fast fourier transform of quarter C wave data. that is , sinqb computes a sequence from its C representation in terms of a sine series with odd wave numbers. C the transform is defined below at output parameter x. C C sinqf is the unnormalized inverse of sinqb since a call of sinqb C followed by a call of sinqf will multiply the input sequence x C by 4*n. C C the array wsave which is used by subroutine sinqb must be C initialized by calling subroutine sinqi(n,wsave). C C C input parameters C C n the length of the array x to be transformed. the method C is most efficient when n is a product of small primes. C C x an array which contains the sequence to be transformed C C wsave a work array which must be dimensioned at least 3*n+15. C in the program that calls sinqb. the wsave array must be C initialized by calling subroutine sinqi(n,wsave) and a C different wsave array must be used for each different C value of n. this initialization does not have to be C repeated so long as n remains unchanged thus subsequent C transforms can be obtained faster than the first. C C output parameters C C x for i=1,...,n C C x(i)= the sum from k=1 to k=n of C C 4*x(k)*sin((2k-1)*i*pi/(2*n)) C C a call of sinqb followed by a call of C sinqf will multiply the sequence x by 4*n. C therefore sinqf is the unnormalized inverse C of sinqb. C C wsave contains initialization calculations which must not C be destroyed between calls of sinqb or sinqf. C SUBROUTINE SINQB (N,X,WSAVE) DIMENSION X(*) ,WSAVE(*) IF (N .GT. 1) GO TO 101 X(1) = 4.*X(1) RETURN 101 NS2 = N/2 DO 102 K=2,N,2 X(K) = -X(K) 102 CONTINUE CALL COSQB (N,X,WSAVE) DO 103 K=1,NS2 KC = N-K XHOLD = X(K) X(K) = X(KC+1) X(KC+1) = XHOLD 103 CONTINUE RETURN END C C ****************************************************************** C C subroutine cosqi(n,wsave) C C ****************************************************************** C C subroutine cosqi initializes the array wsave which is used in C both cosqf and cosqb. the prime factorization of n together with C a tabulation of the trigonometric functions are computed and C stored in wsave. C C input parameter C C n the length of the array to be transformed. the method C is most efficient when n is a product of small primes. C C output parameter C C wsave a work array which must be dimensioned at least 3*n+15. C the same work array can be used for both cosqf and cosqb C as long as n remains unchanged. different wsave arrays C are required for different values of n. the contents of C wsave must not be changed between calls of cosqf or cosqb. C SUBROUTINE COSQI (N,WSAVE) DIMENSION WSAVE(*) DATA PIH /1.57079632679491/ DT = PIH/FLOAT(N) FK = 0. DO 101 K=1,N FK = FK+1. WSAVE(K) = COS(FK*DT) 101 CONTINUE CALL RFFTI (N,WSAVE(N+1)) RETURN END C C ****************************************************************** C C subroutine cosqf(n,x,wsave) C C ****************************************************************** C C subroutine cosqf computes the fast fourier transform of quarter C wave data. that is , cosqf computes the coefficients in a cosine C series representation with only odd wave numbers. the transform C is defined below at output parameter x C C cosqf is the unnormalized inverse of cosqb since a call of cosqf C followed by a call of cosqb will multiply the input sequence x C by 4*n. C C the array wsave which is used by subroutine cosqf must be C initialized by calling subroutine cosqi(n,wsave). C C C input parameters C C n the length of the array x to be transformed. the method C is most efficient when n is a product of small primes. C C x an array which contains the sequence to be transformed C C wsave a work array which must be dimensioned at least 3*n+15 C in the program that calls cosqf. the wsave array must be C initialized by calling subroutine cosqi(n,wsave) and a C different wsave array must be used for each different C value of n. this initialization does not have to be C repeated so long as n remains unchanged thus subsequent C transforms can be obtained faster than the first. C C output parameters C C x for i=1,...,n C C x(i) = x(1) plus the sum from k=2 to k=n of C C 2*x(k)*cos((2*i-1)*(k-1)*pi/(2*n)) C C a call of cosqf followed by a call of C cosqb will multiply the sequence x by 4*n. C therefore cosqb is the unnormalized inverse C of cosqf. C C wsave contains initialization calculations which must not C be destroyed between calls of cosqf or cosqb. C SUBROUTINE COSQF (N,X,WSAVE) DIMENSION X(*) ,WSAVE(*) DATA SQRT2 /1.4142135623731/ C IF (N-2) 102,101,103 IF (N-2 .LT. 0) GOTO 102 IF (N-2 .GT. 0) GOTO 103 101 TSQX = SQRT2*X(2) X(2) = X(1)-TSQX X(1) = X(1)+TSQX 102 RETURN 103 CALL COSQF1_OLD (N,X,WSAVE,WSAVE(N+1)) RETURN END C SUBROUTINE COSQF1_OLD (N,X,W,XH) DIMENSION X(*) ,W(*) ,XH(*) NS2 = (N+1)/2 NP2 = N+2 DO 101 K=2,NS2 KC = NP2-K XH(K) = X(K)+X(KC) XH(KC) = X(K)-X(KC) 101 CONTINUE MODN = MOD(N,2) IF (MODN .EQ. 0) XH(NS2+1) = X(NS2+1)+X(NS2+1) DO 102 K=2,NS2 KC = NP2-K X(K) = W(K-1)*XH(KC)+W(KC-1)*XH(K) X(KC) = W(K-1)*XH(K)-W(KC-1)*XH(KC) 102 CONTINUE IF (MODN .EQ. 0) X(NS2+1) = W(NS2)*XH(NS2+1) CALL RFFTF (N,X,XH) DO 103 I=3,N,2 XIM1 = X(I-1)-X(I) X(I) = X(I-1)+X(I) X(I-1) = XIM1 103 CONTINUE RETURN END C C ****************************************************************** C C subroutine cosqb(n,x,wsave) C C ****************************************************************** C C subroutine cosqb computes the fast fourier transform of quarter C wave data. that is , cosqb computes a sequence from its C representation in terms of a cosine series with odd wave numbers. C the transform is defined below at output parameter x. C C cosqb is the unnormalized inverse of cosqf since a call of cosqb C followed by a call of cosqf will multiply the input sequence x C by 4*n. C C the array wsave which is used by subroutine cosqb must be C initialized by calling subroutine cosqi(n,wsave). C C C input parameters C C n the length of the array x to be transformed. the method C is most efficient when n is a product of small primes. C C x an array which contains the sequence to be transformed C C wsave a work array that must be dimensioned at least 3*n+15 C in the program that calls cosqb. the wsave array must be C initialized by calling subroutine cosqi(n,wsave) and a C different wsave array must be used for each different C value of n. this initialization does not have to be C repeated so long as n remains unchanged thus subsequent C transforms can be obtained faster than the first. C C output parameters C C x for i=1,...,n C C x(i)= the sum from k=1 to k=n of C C 4*x(k)*cos((2*k-1)*(i-1)*pi/(2*n)) C C a call of cosqb followed by a call of C cosqf will multiply the sequence x by 4*n. C therefore cosqf is the unnormalized inverse C of cosqb. C C wsave contains initialization calculations which must not C be destroyed between calls of cosqb or cosqf. C SUBROUTINE COSQB (N,X,WSAVE) DIMENSION X(*) ,WSAVE(*) DATA TSQRT2 /2.82842712474619/ C IF (N-2) 101,102,103 IF (N-2 .EQ. 0) GOTO 102 IF (N-2 .GT. 0) GOTO 103 101 X(1) = 4.*X(1) RETURN 102 X1 = 4.*(X(1)+X(2)) X(2) = TSQRT2*(X(1)-X(2)) X(1) = X1 RETURN 103 CALL COSQB1_OLD (N,X,WSAVE,WSAVE(N+1)) RETURN END C SUBROUTINE COSQB1_OLD (N,X,W,XH) DIMENSION X(*) ,W(*) ,XH(*) NS2 = (N+1)/2 NP2 = N+2 DO 101 I=3,N,2 XIM1 = X(I-1)+X(I) X(I) = X(I)-X(I-1) X(I-1) = XIM1 101 CONTINUE X(1) = X(1)+X(1) MODN = MOD(N,2) IF (MODN .EQ. 0) X(N) = X(N)+X(N) CALL RFFTB (N,X,XH) DO 102 K=2,NS2 KC = NP2-K XH(K) = W(K-1)*X(KC)+W(KC-1)*X(K) XH(KC) = W(K-1)*X(K)-W(KC-1)*X(KC) 102 CONTINUE IF (MODN .EQ. 0) X(NS2+1) = W(NS2)*(X(NS2+1)+X(NS2+1)) DO 103 K=2,NS2 KC = NP2-K X(K) = XH(K)+XH(KC) X(KC) = XH(K)-XH(KC) 103 CONTINUE X(1) = X(1)+X(1) RETURN END C C ****************************************************************** C C subroutine cffti(n,wsave) C C ****************************************************************** C C subroutine cffti initializes the array wsave which is used in C both cfftf and cfftb. the prime factorization of n together with C a tabulation of the trigonometric functions are computed and C stored in wsave. C C input parameter C C n the length of the sequence to be transformed C C output parameter C C wsave a work array which must be dimensioned at least 4*n+15 C the same work array can be used for both cfftf and cfftb C as long as n remains unchanged. different wsave arrays C are required for different values of n. the contents of C wsave must not be changed between calls of cfftf or cfftb. C SUBROUTINE CFFTI (N,WSAVE) DIMENSION WSAVE(*) IF (N .EQ. 1) RETURN IW1 = N+N+1 IW2 = IW1+N+N CALL CFFTI1 (N,WSAVE(IW1),WSAVE(IW2)) RETURN END C SUBROUTINE CFFTI1 (N,WA,IFAC) DIMENSION WA(*) ,IFAC(*) ,NTRYH(4) DATA NTRYH(1),NTRYH(2),NTRYH(3),NTRYH(4)/3,4,2,5/ NL = N NF = 0 J = 0 101 J = J+1 C IF (J-4) 102,102,103 IF (J-4 .GT. 0) GOTO 103 102 NTRY = NTRYH(J) GO TO 104 103 NTRY = NTRY+2 104 NQ = NL/NTRY NR = NL-NTRY*NQ C IF (NR) 101,105,101 IF (NR .NE. 0) GOTO 101 105 NF = NF+1 IFAC(NF+2) = NTRY NL = NQ IF (NTRY .NE. 2) GO TO 107 IF (NF .EQ. 1) GO TO 107 DO 106 I=2,NF IB = NF-I+2 IFAC(IB+2) = IFAC(IB+1) 106 CONTINUE IFAC(3) = 2 107 IF (NL .NE. 1) GO TO 104 IFAC(1) = N IFAC(2) = NF TPI = 6.28318530717959 ARGH = TPI/FLOAT(N) I = 2 L1 = 1 DO 110 K1=1,NF IP = IFAC(K1+2) LD = 0 L2 = L1*IP IDO = N/L2 IDOT = IDO+IDO+2 IPM = IP-1 DO 109 J=1,IPM I1 = I WA(I-1) = 1. WA(I) = 0. LD = LD+L1 FI = 0. ARGLD = FLOAT(LD)*ARGH DO 108 II=4,IDOT,2 I = I+2 FI = FI+1. ARG = FI*ARGLD WA(I-1) = COS(ARG) WA(I) = SIN(ARG) 108 CONTINUE IF (IP .LE. 5) GO TO 109 WA(I1-1) = WA(I-1) WA(I1) = WA(I) 109 CONTINUE L1 = L2 110 CONTINUE RETURN END C C ****************************************************************** C C subroutine cfftf(n,c,wsave) C C ****************************************************************** C C subroutine cfftf computes the forward complex discrete fourier C transform (the fourier analysis). equivalently , cfftf computes C the fourier coefficients of a complex periodic sequence. C the transform is defined below at output parameter c. C C the transform is not normalized. to obtain a normalized transform C the output must be divided by n. otherwise a call of cfftf C followed by a call of cfftb will multiply the sequence by n. C C the array wsave which is used by subroutine cfftf must be C initialized by calling subroutine cffti(n,wsave). C C input parameters C C C n the length of the complex sequence c. the method is C more efficient when n is the product of small primes. n C C c a complex array of length n which contains the sequence C C wsave a real work array which must be dimensioned at least 4n+15 C in the program that calls cfftf. the wsave array must be C initialized by calling subroutine cffti(n,wsave) and a C different wsave array must be used for each different C value of n. this initialization does not have to be C repeated so long as n remains unchanged thus subsequent C transforms can be obtained faster than the first. C the same wsave array can be used by cfftf and cfftb. C C output parameters C C c for j=1,...,n C C c(j)=the sum from k=1,...,n of C C c(k)*exp(-i*(j-1)*(k-1)*2*pi/n) C C where i=sqrt(-1) C C wsave contains initialization calculations which must not be C destroyed between calls of subroutine cfftf or cfftb C SUBROUTINE CFFTF (N,C,WSAVE) DIMENSION C(*) ,WSAVE(*) IF (N .EQ. 1) RETURN IW1 = N+N+1 IW2 = IW1+N+N CALL CFFTF1 (N,C,WSAVE,WSAVE(IW1),WSAVE(IW2)) RETURN END C SUBROUTINE CFFTF1 (N,C,CH,WA,IFAC) DIMENSION CH(*) ,C(*) ,WA(*) ,IFAC(*) NF = IFAC(2) NA = 0 L1 = 1 IW = 1 DO 116 K1=1,NF IP = IFAC(K1+2) L2 = IP*L1 IDO = N/L2 IDOT = IDO+IDO IDL1 = IDOT*L1 IF (IP .NE. 4) GO TO 103 IX2 = IW+IDOT IX3 = IX2+IDOT IF (NA .NE. 0) GO TO 101 CALL PASSF4 (IDOT,L1,C,CH,WA(IW),WA(IX2),WA(IX3)) GO TO 102 101 CALL PASSF4 (IDOT,L1,CH,C,WA(IW),WA(IX2),WA(IX3)) 102 NA = 1-NA GO TO 115 103 IF (IP .NE. 2) GO TO 106 IF (NA .NE. 0) GO TO 104 CALL PASSF2 (IDOT,L1,C,CH,WA(IW)) GO TO 105 104 CALL PASSF2 (IDOT,L1,CH,C,WA(IW)) 105 NA = 1-NA GO TO 115 106 IF (IP .NE. 3) GO TO 109 IX2 = IW+IDOT IF (NA .NE. 0) GO TO 107 CALL PASSF3 (IDOT,L1,C,CH,WA(IW),WA(IX2)) GO TO 108 107 CALL PASSF3 (IDOT,L1,CH,C,WA(IW),WA(IX2)) 108 NA = 1-NA GO TO 115 109 IF (IP .NE. 5) GO TO 112 IX2 = IW+IDOT IX3 = IX2+IDOT IX4 = IX3+IDOT IF (NA .NE. 0) GO TO 110 CALL PASSF5 (IDOT,L1,C,CH,WA(IW),WA(IX2),WA(IX3),WA(IX4)) GO TO 111 110 CALL PASSF5 (IDOT,L1,CH,C,WA(IW),WA(IX2),WA(IX3),WA(IX4)) 111 NA = 1-NA GO TO 115 112 IF (NA .NE. 0) GO TO 113 CALL PASSF (NAC,IDOT,IP,L1,IDL1,C,C,C,CH,CH,WA(IW)) GO TO 114 113 CALL PASSF (NAC,IDOT,IP,L1,IDL1,CH,CH,CH,C,C,WA(IW)) 114 IF (NAC .NE. 0) NA = 1-NA 115 L1 = L2 IW = IW+(IP-1)*IDOT 116 CONTINUE IF (NA .EQ. 0) RETURN N2 = N+N DO 117 I=1,N2 C(I) = CH(I) 117 CONTINUE RETURN END C SUBROUTINE PASSF (NAC,IDO,IP,L1,IDL1,CC,C1,C2,CH,CH2,WA) DIMENSION CH(IDO,L1,IP) ,CC(IDO,IP,L1) , 1 C1(IDO,L1,IP) ,WA(*) ,C2(IDL1,IP), 2 CH2(IDL1,IP) IDOT = IDO/2 NT = IP*IDL1 IPP2 = IP+2 IPPH = (IP+1)/2 IDP = IP*IDO C IF (IDO .LT. L1) GO TO 106 DO 103 J=2,IPPH JC = IPP2-J DO 102 K=1,L1 DO 101 I=1,IDO CH(I,K,J) = CC(I,J,K)+CC(I,JC,K) CH(I,K,JC) = CC(I,J,K)-CC(I,JC,K) 101 CONTINUE 102 CONTINUE 103 CONTINUE DO 105 K=1,L1 DO 104 I=1,IDO CH(I,K,1) = CC(I,1,K) 104 CONTINUE 105 CONTINUE GO TO 112 106 DO 109 J=2,IPPH JC = IPP2-J DO 108 I=1,IDO DO 107 K=1,L1 CH(I,K,J) = CC(I,J,K)+CC(I,JC,K) CH(I,K,JC) = CC(I,J,K)-CC(I,JC,K) 107 CONTINUE 108 CONTINUE 109 CONTINUE DO 111 I=1,IDO DO 110 K=1,L1 CH(I,K,1) = CC(I,1,K) 110 CONTINUE 111 CONTINUE 112 IDL = 2-IDO INC = 0 DO 116 L=2,IPPH LC = IPP2-L IDL = IDL+IDO DO 113 IK=1,IDL1 C2(IK,L) = CH2(IK,1)+WA(IDL-1)*CH2(IK,2) C2(IK,LC) = -WA(IDL)*CH2(IK,IP) 113 CONTINUE IDLJ = IDL INC = INC+IDO DO 115 J=3,IPPH JC = IPP2-J IDLJ = IDLJ+INC IF (IDLJ .GT. IDP) IDLJ = IDLJ-IDP WAR = WA(IDLJ-1) WAI = WA(IDLJ) DO 114 IK=1,IDL1 C2(IK,L) = C2(IK,L)+WAR*CH2(IK,J) C2(IK,LC) = C2(IK,LC)-WAI*CH2(IK,JC) 114 CONTINUE 115 CONTINUE 116 CONTINUE DO 118 J=2,IPPH DO 117 IK=1,IDL1 CH2(IK,1) = CH2(IK,1)+CH2(IK,J) 117 CONTINUE 118 CONTINUE DO 120 J=2,IPPH JC = IPP2-J DO 119 IK=2,IDL1,2 CH2(IK-1,J) = C2(IK-1,J)-C2(IK,JC) CH2(IK-1,JC) = C2(IK-1,J)+C2(IK,JC) CH2(IK,J) = C2(IK,J)+C2(IK-1,JC) CH2(IK,JC) = C2(IK,J)-C2(IK-1,JC) 119 CONTINUE 120 CONTINUE NAC = 1 IF (IDO .EQ. 2) RETURN NAC = 0 DO 121 IK=1,IDL1 C2(IK,1) = CH2(IK,1) 121 CONTINUE DO 123 J=2,IP DO 122 K=1,L1 C1(1,K,J) = CH(1,K,J) C1(2,K,J) = CH(2,K,J) 122 CONTINUE 123 CONTINUE IF (IDOT .GT. L1) GO TO 127 IDIJ = 0 DO 126 J=2,IP IDIJ = IDIJ+2 DO 125 I=4,IDO,2 IDIJ = IDIJ+2 DO 124 K=1,L1 C1(I-1,K,J) = WA(IDIJ-1)*CH(I-1,K,J)+WA(IDIJ)*CH(I,K,J) C1(I,K,J) = WA(IDIJ-1)*CH(I,K,J)-WA(IDIJ)*CH(I-1,K,J) 124 CONTINUE 125 CONTINUE 126 CONTINUE RETURN 127 IDJ = 2-IDO DO 130 J=2,IP IDJ = IDJ+IDO DO 129 K=1,L1 IDIJ = IDJ DO 128 I=4,IDO,2 IDIJ = IDIJ+2 C1(I-1,K,J) = WA(IDIJ-1)*CH(I-1,K,J)+WA(IDIJ)*CH(I,K,J) C1(I,K,J) = WA(IDIJ-1)*CH(I,K,J)-WA(IDIJ)*CH(I-1,K,J) 128 CONTINUE 129 CONTINUE 130 CONTINUE RETURN END C SUBROUTINE PASSF2 (IDO,L1,CC,CH,WA1) DIMENSION CC(IDO,2,L1) ,CH(IDO,L1,2) , 1 WA1(*) IF (IDO .GT. 2) GO TO 102 DO 101 K=1,L1 CH(1,K,1) = CC(1,1,K)+CC(1,2,K) CH(1,K,2) = CC(1,1,K)-CC(1,2,K) CH(2,K,1) = CC(2,1,K)+CC(2,2,K) CH(2,K,2) = CC(2,1,K)-CC(2,2,K) 101 CONTINUE RETURN 102 DO 104 K=1,L1 DO 103 I=2,IDO,2 CH(I-1,K,1) = CC(I-1,1,K)+CC(I-1,2,K) TR2 = CC(I-1,1,K)-CC(I-1,2,K) CH(I,K,1) = CC(I,1,K)+CC(I,2,K) TI2 = CC(I,1,K)-CC(I,2,K) CH(I,K,2) = WA1(I-1)*TI2-WA1(I)*TR2 CH(I-1,K,2) = WA1(I-1)*TR2+WA1(I)*TI2 103 CONTINUE 104 CONTINUE RETURN END C SUBROUTINE PASSF3 (IDO,L1,CC,CH,WA1,WA2) DIMENSION CC(IDO,3,L1) ,CH(IDO,L1,3) , 1 WA1(*) ,WA2(*) DATA TAUR,TAUI /-.5,-.866025403784439/ IF (IDO .NE. 2) GO TO 102 DO 101 K=1,L1 TR2 = CC(1,2,K)+CC(1,3,K) CR2 = CC(1,1,K)+TAUR*TR2 CH(1,K,1) = CC(1,1,K)+TR2 TI2 = CC(2,2,K)+CC(2,3,K) CI2 = CC(2,1,K)+TAUR*TI2 CH(2,K,1) = CC(2,1,K)+TI2 CR3 = TAUI*(CC(1,2,K)-CC(1,3,K)) CI3 = TAUI*(CC(2,2,K)-CC(2,3,K)) CH(1,K,2) = CR2-CI3 CH(1,K,3) = CR2+CI3 CH(2,K,2) = CI2+CR3 CH(2,K,3) = CI2-CR3 101 CONTINUE RETURN 102 DO 104 K=1,L1 DO 103 I=2,IDO,2 TR2 = CC(I-1,2,K)+CC(I-1,3,K) CR2 = CC(I-1,1,K)+TAUR*TR2 CH(I-1,K,1) = CC(I-1,1,K)+TR2 TI2 = CC(I,2,K)+CC(I,3,K) CI2 = CC(I,1,K)+TAUR*TI2 CH(I,K,1) = CC(I,1,K)+TI2 CR3 = TAUI*(CC(I-1,2,K)-CC(I-1,3,K)) CI3 = TAUI*(CC(I,2,K)-CC(I,3,K)) DR2 = CR2-CI3 DR3 = CR2+CI3 DI2 = CI2+CR3 DI3 = CI2-CR3 CH(I,K,2) = WA1(I-1)*DI2-WA1(I)*DR2 CH(I-1,K,2) = WA1(I-1)*DR2+WA1(I)*DI2 CH(I,K,3) = WA2(I-1)*DI3-WA2(I)*DR3 CH(I-1,K,3) = WA2(I-1)*DR3+WA2(I)*DI3 103 CONTINUE 104 CONTINUE RETURN END C SUBROUTINE PASSF4 (IDO,L1,CC,CH,WA1,WA2,WA3) DIMENSION CC(IDO,4,L1) ,CH(IDO,L1,4) , 1 WA1(*) ,WA2(*) ,WA3(*) IF (IDO .NE. 2) GO TO 102 DO 101 K=1,L1 TI1 = CC(2,1,K)-CC(2,3,K) TI2 = CC(2,1,K)+CC(2,3,K) TR4 = CC(2,2,K)-CC(2,4,K) TI3 = CC(2,2,K)+CC(2,4,K) TR1 = CC(1,1,K)-CC(1,3,K) TR2 = CC(1,1,K)+CC(1,3,K) TI4 = CC(1,4,K)-CC(1,2,K) TR3 = CC(1,2,K)+CC(1,4,K) CH(1,K,1) = TR2+TR3 CH(1,K,3) = TR2-TR3 CH(2,K,1) = TI2+TI3 CH(2,K,3) = TI2-TI3 CH(1,K,2) = TR1+TR4 CH(1,K,4) = TR1-TR4 CH(2,K,2) = TI1+TI4 CH(2,K,4) = TI1-TI4 101 CONTINUE RETURN 102 DO 104 K=1,L1 DO 103 I=2,IDO,2 TI1 = CC(I,1,K)-CC(I,3,K) TI2 = CC(I,1,K)+CC(I,3,K) TI3 = CC(I,2,K)+CC(I,4,K) TR4 = CC(I,2,K)-CC(I,4,K) TR1 = CC(I-1,1,K)-CC(I-1,3,K) TR2 = CC(I-1,1,K)+CC(I-1,3,K) TI4 = CC(I-1,4,K)-CC(I-1,2,K) TR3 = CC(I-1,2,K)+CC(I-1,4,K) CH(I-1,K,1) = TR2+TR3 CR3 = TR2-TR3 CH(I,K,1) = TI2+TI3 CI3 = TI2-TI3 CR2 = TR1+TR4 CR4 = TR1-TR4 CI2 = TI1+TI4 CI4 = TI1-TI4 CH(I-1,K,2) = WA1(I-1)*CR2+WA1(I)*CI2 CH(I,K,2) = WA1(I-1)*CI2-WA1(I)*CR2 CH(I-1,K,3) = WA2(I-1)*CR3+WA2(I)*CI3 CH(I,K,3) = WA2(I-1)*CI3-WA2(I)*CR3 CH(I-1,K,4) = WA3(I-1)*CR4+WA3(I)*CI4 CH(I,K,4) = WA3(I-1)*CI4-WA3(I)*CR4 103 CONTINUE 104 CONTINUE RETURN END C SUBROUTINE PASSF5 (IDO,L1,CC,CH,WA1,WA2,WA3,WA4) DIMENSION CC(IDO,5,L1) ,CH(IDO,L1,5) , 1 WA1(*) ,WA2(*) ,WA3(*) ,WA4(*) DATA TR11,TI11,TR12,TI12 /.309016994374947,-.951056516295154, 1 -.809016994374947,-.587785252292473/ IF (IDO .NE. 2) GO TO 102 DO 101 K=1,L1 TI5 = CC(2,2,K)-CC(2,5,K) TI2 = CC(2,2,K)+CC(2,5,K) TI4 = CC(2,3,K)-CC(2,4,K) TI3 = CC(2,3,K)+CC(2,4,K) TR5 = CC(1,2,K)-CC(1,5,K) TR2 = CC(1,2,K)+CC(1,5,K) TR4 = CC(1,3,K)-CC(1,4,K) TR3 = CC(1,3,K)+CC(1,4,K) CH(1,K,1) = CC(1,1,K)+TR2+TR3 CH(2,K,1) = CC(2,1,K)+TI2+TI3 CR2 = CC(1,1,K)+TR11*TR2+TR12*TR3 CI2 = CC(2,1,K)+TR11*TI2+TR12*TI3 CR3 = CC(1,1,K)+TR12*TR2+TR11*TR3 CI3 = CC(2,1,K)+TR12*TI2+TR11*TI3 CR5 = TI11*TR5+TI12*TR4 CI5 = TI11*TI5+TI12*TI4 CR4 = TI12*TR5-TI11*TR4 CI4 = TI12*TI5-TI11*TI4 CH(1,K,2) = CR2-CI5 CH(1,K,5) = CR2+CI5 CH(2,K,2) = CI2+CR5 CH(2,K,3) = CI3+CR4 CH(1,K,3) = CR3-CI4 CH(1,K,4) = CR3+CI4 CH(2,K,4) = CI3-CR4 CH(2,K,5) = CI2-CR5 101 CONTINUE RETURN 102 DO 104 K=1,L1 DO 103 I=2,IDO,2 TI5 = CC(I,2,K)-CC(I,5,K) TI2 = CC(I,2,K)+CC(I,5,K) TI4 = CC(I,3,K)-CC(I,4,K) TI3 = CC(I,3,K)+CC(I,4,K) TR5 = CC(I-1,2,K)-CC(I-1,5,K) TR2 = CC(I-1,2,K)+CC(I-1,5,K) TR4 = CC(I-1,3,K)-CC(I-1,4,K) TR3 = CC(I-1,3,K)+CC(I-1,4,K) CH(I-1,K,1) = CC(I-1,1,K)+TR2+TR3 CH(I,K,1) = CC(I,1,K)+TI2+TI3 CR2 = CC(I-1,1,K)+TR11*TR2+TR12*TR3 CI2 = CC(I,1,K)+TR11*TI2+TR12*TI3 CR3 = CC(I-1,1,K)+TR12*TR2+TR11*TR3 CI3 = CC(I,1,K)+TR12*TI2+TR11*TI3 CR5 = TI11*TR5+TI12*TR4 CI5 = TI11*TI5+TI12*TI4 CR4 = TI12*TR5-TI11*TR4 CI4 = TI12*TI5-TI11*TI4 DR3 = CR3-CI4 DR4 = CR3+CI4 DI3 = CI3+CR4 DI4 = CI3-CR4 DR5 = CR2+CI5 DR2 = CR2-CI5 DI5 = CI2-CR5 DI2 = CI2+CR5 CH(I-1,K,2) = WA1(I-1)*DR2+WA1(I)*DI2 CH(I,K,2) = WA1(I-1)*DI2-WA1(I)*DR2 CH(I-1,K,3) = WA2(I-1)*DR3+WA2(I)*DI3 CH(I,K,3) = WA2(I-1)*DI3-WA2(I)*DR3 CH(I-1,K,4) = WA3(I-1)*DR4+WA3(I)*DI4 CH(I,K,4) = WA3(I-1)*DI4-WA3(I)*DR4 CH(I-1,K,5) = WA4(I-1)*DR5+WA4(I)*DI5 CH(I,K,5) = WA4(I-1)*DI5-WA4(I)*DR5 103 CONTINUE 104 CONTINUE RETURN END C C ****************************************************************** C C subroutine cfftb(n,c,wsave) C C ****************************************************************** C C subroutine cfftb computes the backward complex discrete fourier C transform (the fourier synthesis). equivalently , cfftb computes C a complex periodic sequence from its fourier coefficients. C the transform is defined below at output parameter c. C C a call of cfftf followed by a call of cfftb will multiply the C sequence by n. C C the array wsave which is used by subroutine cfftb must be C initialized by calling subroutine cffti(n,wsave). C C input parameters C C C n the length of the complex sequence c. the method is C more efficient when n is the product of small primes. C C c a complex array of length n which contains the sequence C C wsave a real work array which must be dimensioned at least 4n+15 C in the program that calls cfftb. the wsave array must be C initialized by calling subroutine cffti(n,wsave) and a C different wsave array must be used for each different C value of n. this initialization does not have to be C repeated so long as n remains unchanged thus subsequent C transforms can be obtained faster than the first. C the same wsave array can be used by cfftf and cfftb. C C output parameters C C c for j=1,...,n C C c(j)=the sum from k=1,...,n of C C c(k)*exp(i*(j-1)*(k-1)*2*pi/n) C C where i=sqrt(-1) C C wsave contains initialization calculations which must not be C destroyed between calls of subroutine cfftf or cfftb C SUBROUTINE CFFTB (N,C,WSAVE) DIMENSION C(*) ,WSAVE(*) IF (N .EQ. 1) RETURN IW1 = N+N+1 IW2 = IW1+N+N CALL CFFTB1 (N,C,WSAVE,WSAVE(IW1),WSAVE(IW2)) RETURN END C SUBROUTINE CFFTB1 (N,C,CH,WA,IFAC) DIMENSION CH(*) ,C(*) ,WA(*) ,IFAC(*) NF = IFAC(2) NA = 0 L1 = 1 IW = 1 DO 116 K1=1,NF IP = IFAC(K1+2) L2 = IP*L1 IDO = N/L2 IDOT = IDO+IDO IDL1 = IDOT*L1 IF (IP .NE. 4) GO TO 103 IX2 = IW+IDOT IX3 = IX2+IDOT IF (NA .NE. 0) GO TO 101 CALL PASSB4 (IDOT,L1,C,CH,WA(IW),WA(IX2),WA(IX3)) GO TO 102 101 CALL PASSB4 (IDOT,L1,CH,C,WA(IW),WA(IX2),WA(IX3)) 102 NA = 1-NA GO TO 115 103 IF (IP .NE. 2) GO TO 106 IF (NA .NE. 0) GO TO 104 CALL PASSB2 (IDOT,L1,C,CH,WA(IW)) GO TO 105 104 CALL PASSB2 (IDOT,L1,CH,C,WA(IW)) 105 NA = 1-NA GO TO 115 106 IF (IP .NE. 3) GO TO 109 IX2 = IW+IDOT IF (NA .NE. 0) GO TO 107 CALL PASSB3 (IDOT,L1,C,CH,WA(IW),WA(IX2)) GO TO 108 107 CALL PASSB3 (IDOT,L1,CH,C,WA(IW),WA(IX2)) 108 NA = 1-NA GO TO 115 109 IF (IP .NE. 5) GO TO 112 IX2 = IW+IDOT IX3 = IX2+IDOT IX4 = IX3+IDOT IF (NA .NE. 0) GO TO 110 CALL PASSB5 (IDOT,L1,C,CH,WA(IW),WA(IX2),WA(IX3),WA(IX4)) GO TO 111 110 CALL PASSB5 (IDOT,L1,CH,C,WA(IW),WA(IX2),WA(IX3),WA(IX4)) 111 NA = 1-NA GO TO 115 112 IF (NA .NE. 0) GO TO 113 CALL PASSB (NAC,IDOT,IP,L1,IDL1,C,C,C,CH,CH,WA(IW)) GO TO 114 113 CALL PASSB (NAC,IDOT,IP,L1,IDL1,CH,CH,CH,C,C,WA(IW)) 114 IF (NAC .NE. 0) NA = 1-NA 115 L1 = L2 IW = IW+(IP-1)*IDOT 116 CONTINUE IF (NA .EQ. 0) RETURN N2 = N+N DO 117 I=1,N2 C(I) = CH(I) 117 CONTINUE RETURN END C SUBROUTINE PASSB (NAC,IDO,IP,L1,IDL1,CC,C1,C2,CH,CH2,WA) DIMENSION CH(IDO,L1,IP) ,CC(IDO,IP,L1) , 1 C1(IDO,L1,IP) ,WA(*) ,C2(IDL1,IP), 2 CH2(IDL1,IP) IDOT = IDO/2 NT = IP*IDL1 IPP2 = IP+2 IPPH = (IP+1)/2 IDP = IP*IDO C IF (IDO .LT. L1) GO TO 106 DO 103 J=2,IPPH JC = IPP2-J DO 102 K=1,L1 DO 101 I=1,IDO CH(I,K,J) = CC(I,J,K)+CC(I,JC,K) CH(I,K,JC) = CC(I,J,K)-CC(I,JC,K) 101 CONTINUE 102 CONTINUE 103 CONTINUE DO 105 K=1,L1 DO 104 I=1,IDO CH(I,K,1) = CC(I,1,K) 104 CONTINUE 105 CONTINUE GO TO 112 106 DO 109 J=2,IPPH JC = IPP2-J DO 108 I=1,IDO DO 107 K=1,L1 CH(I,K,J) = CC(I,J,K)+CC(I,JC,K) CH(I,K,JC) = CC(I,J,K)-CC(I,JC,K) 107 CONTINUE 108 CONTINUE 109 CONTINUE DO 111 I=1,IDO DO 110 K=1,L1 CH(I,K,1) = CC(I,1,K) 110 CONTINUE 111 CONTINUE 112 IDL = 2-IDO INC = 0 DO 116 L=2,IPPH LC = IPP2-L IDL = IDL+IDO DO 113 IK=1,IDL1 C2(IK,L) = CH2(IK,1)+WA(IDL-1)*CH2(IK,2) C2(IK,LC) = WA(IDL)*CH2(IK,IP) 113 CONTINUE IDLJ = IDL INC = INC+IDO DO 115 J=3,IPPH JC = IPP2-J IDLJ = IDLJ+INC IF (IDLJ .GT. IDP) IDLJ = IDLJ-IDP WAR = WA(IDLJ-1) WAI = WA(IDLJ) DO 114 IK=1,IDL1 C2(IK,L) = C2(IK,L)+WAR*CH2(IK,J) C2(IK,LC) = C2(IK,LC)+WAI*CH2(IK,JC) 114 CONTINUE 115 CONTINUE 116 CONTINUE DO 118 J=2,IPPH DO 117 IK=1,IDL1 CH2(IK,1) = CH2(IK,1)+CH2(IK,J) 117 CONTINUE 118 CONTINUE DO 120 J=2,IPPH JC = IPP2-J DO 119 IK=2,IDL1,2 CH2(IK-1,J) = C2(IK-1,J)-C2(IK,JC) CH2(IK-1,JC) = C2(IK-1,J)+C2(IK,JC) CH2(IK,J) = C2(IK,J)+C2(IK-1,JC) CH2(IK,JC) = C2(IK,J)-C2(IK-1,JC) 119 CONTINUE 120 CONTINUE NAC = 1 IF (IDO .EQ. 2) RETURN NAC = 0 DO 121 IK=1,IDL1 C2(IK,1) = CH2(IK,1) 121 CONTINUE DO 123 J=2,IP DO 122 K=1,L1 C1(1,K,J) = CH(1,K,J) C1(2,K,J) = CH(2,K,J) 122 CONTINUE 123 CONTINUE IF (IDOT .GT. L1) GO TO 127 IDIJ = 0 DO 126 J=2,IP IDIJ = IDIJ+2 DO 125 I=4,IDO,2 IDIJ = IDIJ+2 DO 124 K=1,L1 C1(I-1,K,J) = WA(IDIJ-1)*CH(I-1,K,J)-WA(IDIJ)*CH(I,K,J) C1(I,K,J) = WA(IDIJ-1)*CH(I,K,J)+WA(IDIJ)*CH(I-1,K,J) 124 CONTINUE 125 CONTINUE 126 CONTINUE RETURN 127 IDJ = 2-IDO DO 130 J=2,IP IDJ = IDJ+IDO DO 129 K=1,L1 IDIJ = IDJ DO 128 I=4,IDO,2 IDIJ = IDIJ+2 C1(I-1,K,J) = WA(IDIJ-1)*CH(I-1,K,J)-WA(IDIJ)*CH(I,K,J) C1(I,K,J) = WA(IDIJ-1)*CH(I,K,J)+WA(IDIJ)*CH(I-1,K,J) 128 CONTINUE 129 CONTINUE 130 CONTINUE RETURN END C SUBROUTINE PASSB2 (IDO,L1,CC,CH,WA1) DIMENSION CC(IDO,2,L1) ,CH(IDO,L1,2) , 1 WA1(*) IF (IDO .GT. 2) GO TO 102 DO 101 K=1,L1 CH(1,K,1) = CC(1,1,K)+CC(1,2,K) CH(1,K,2) = CC(1,1,K)-CC(1,2,K) CH(2,K,1) = CC(2,1,K)+CC(2,2,K) CH(2,K,2) = CC(2,1,K)-CC(2,2,K) 101 CONTINUE RETURN 102 DO 104 K=1,L1 DO 103 I=2,IDO,2 CH(I-1,K,1) = CC(I-1,1,K)+CC(I-1,2,K) TR2 = CC(I-1,1,K)-CC(I-1,2,K) CH(I,K,1) = CC(I,1,K)+CC(I,2,K) TI2 = CC(I,1,K)-CC(I,2,K) CH(I,K,2) = WA1(I-1)*TI2+WA1(I)*TR2 CH(I-1,K,2) = WA1(I-1)*TR2-WA1(I)*TI2 103 CONTINUE 104 CONTINUE RETURN END C SUBROUTINE PASSB3 (IDO,L1,CC,CH,WA1,WA2) DIMENSION CC(IDO,3,L1) ,CH(IDO,L1,3) , 1 WA1(*) ,WA2(*) DATA TAUR,TAUI /-.5,.866025403784439/ IF (IDO .NE. 2) GO TO 102 DO 101 K=1,L1 TR2 = CC(1,2,K)+CC(1,3,K) CR2 = CC(1,1,K)+TAUR*TR2 CH(1,K,1) = CC(1,1,K)+TR2 TI2 = CC(2,2,K)+CC(2,3,K) CI2 = CC(2,1,K)+TAUR*TI2 CH(2,K,1) = CC(2,1,K)+TI2 CR3 = TAUI*(CC(1,2,K)-CC(1,3,K)) CI3 = TAUI*(CC(2,2,K)-CC(2,3,K)) CH(1,K,2) = CR2-CI3 CH(1,K,3) = CR2+CI3 CH(2,K,2) = CI2+CR3 CH(2,K,3) = CI2-CR3 101 CONTINUE RETURN 102 DO 104 K=1,L1 DO 103 I=2,IDO,2 TR2 = CC(I-1,2,K)+CC(I-1,3,K) CR2 = CC(I-1,1,K)+TAUR*TR2 CH(I-1,K,1) = CC(I-1,1,K)+TR2 TI2 = CC(I,2,K)+CC(I,3,K) CI2 = CC(I,1,K)+TAUR*TI2 CH(I,K,1) = CC(I,1,K)+TI2 CR3 = TAUI*(CC(I-1,2,K)-CC(I-1,3,K)) CI3 = TAUI*(CC(I,2,K)-CC(I,3,K)) DR2 = CR2-CI3 DR3 = CR2+CI3 DI2 = CI2+CR3 DI3 = CI2-CR3 CH(I,K,2) = WA1(I-1)*DI2+WA1(I)*DR2 CH(I-1,K,2) = WA1(I-1)*DR2-WA1(I)*DI2 CH(I,K,3) = WA2(I-1)*DI3+WA2(I)*DR3 CH(I-1,K,3) = WA2(I-1)*DR3-WA2(I)*DI3 103 CONTINUE 104 CONTINUE RETURN END C SUBROUTINE PASSB4 (IDO,L1,CC,CH,WA1,WA2,WA3) DIMENSION CC(IDO,4,L1) ,CH(IDO,L1,4) , 1 WA1(*) ,WA2(*) ,WA3(*) IF (IDO .NE. 2) GO TO 102 DO 101 K=1,L1 TI1 = CC(2,1,K)-CC(2,3,K) TI2 = CC(2,1,K)+CC(2,3,K) TR4 = CC(2,4,K)-CC(2,2,K) TI3 = CC(2,2,K)+CC(2,4,K) TR1 = CC(1,1,K)-CC(1,3,K) TR2 = CC(1,1,K)+CC(1,3,K) TI4 = CC(1,2,K)-CC(1,4,K) TR3 = CC(1,2,K)+CC(1,4,K) CH(1,K,1) = TR2+TR3 CH(1,K,3) = TR2-TR3 CH(2,K,1) = TI2+TI3 CH(2,K,3) = TI2-TI3 CH(1,K,2) = TR1+TR4 CH(1,K,4) = TR1-TR4 CH(2,K,2) = TI1+TI4 CH(2,K,4) = TI1-TI4 101 CONTINUE RETURN 102 DO 104 K=1,L1 DO 103 I=2,IDO,2 TI1 = CC(I,1,K)-CC(I,3,K) TI2 = CC(I,1,K)+CC(I,3,K) TI3 = CC(I,2,K)+CC(I,4,K) TR4 = CC(I,4,K)-CC(I,2,K) TR1 = CC(I-1,1,K)-CC(I-1,3,K) TR2 = CC(I-1,1,K)+CC(I-1,3,K) TI4 = CC(I-1,2,K)-CC(I-1,4,K) TR3 = CC(I-1,2,K)+CC(I-1,4,K) CH(I-1,K,1) = TR2+TR3 CR3 = TR2-TR3 CH(I,K,1) = TI2+TI3 CI3 = TI2-TI3 CR2 = TR1+TR4 CR4 = TR1-TR4 CI2 = TI1+TI4 CI4 = TI1-TI4 CH(I-1,K,2) = WA1(I-1)*CR2-WA1(I)*CI2 CH(I,K,2) = WA1(I-1)*CI2+WA1(I)*CR2 CH(I-1,K,3) = WA2(I-1)*CR3-WA2(I)*CI3 CH(I,K,3) = WA2(I-1)*CI3+WA2(I)*CR3 CH(I-1,K,4) = WA3(I-1)*CR4-WA3(I)*CI4 CH(I,K,4) = WA3(I-1)*CI4+WA3(I)*CR4 103 CONTINUE 104 CONTINUE RETURN END C SUBROUTINE PASSB5 (IDO,L1,CC,CH,WA1,WA2,WA3,WA4) DIMENSION CC(IDO,5,L1) ,CH(IDO,L1,5) , 1 WA1(*) ,WA2(*) ,WA3(*) ,WA4(*) DATA TR11,TI11,TR12,TI12 /.309016994374947,.951056516295154, 1 -.809016994374947,.587785252292473/ IF (IDO .NE. 2) GO TO 102 DO 101 K=1,L1 TI5 = CC(2,2,K)-CC(2,5,K) TI2 = CC(2,2,K)+CC(2,5,K) TI4 = CC(2,3,K)-CC(2,4,K) TI3 = CC(2,3,K)+CC(2,4,K) TR5 = CC(1,2,K)-CC(1,5,K) TR2 = CC(1,2,K)+CC(1,5,K) TR4 = CC(1,3,K)-CC(1,4,K) TR3 = CC(1,3,K)+CC(1,4,K) CH(1,K,1) = CC(1,1,K)+TR2+TR3 CH(2,K,1) = CC(2,1,K)+TI2+TI3 CR2 = CC(1,1,K)+TR11*TR2+TR12*TR3 CI2 = CC(2,1,K)+TR11*TI2+TR12*TI3 CR3 = CC(1,1,K)+TR12*TR2+TR11*TR3 CI3 = CC(2,1,K)+TR12*TI2+TR11*TI3 CR5 = TI11*TR5+TI12*TR4 CI5 = TI11*TI5+TI12*TI4 CR4 = TI12*TR5-TI11*TR4 CI4 = TI12*TI5-TI11*TI4 CH(1,K,2) = CR2-CI5 CH(1,K,5) = CR2+CI5 CH(2,K,2) = CI2+CR5 CH(2,K,3) = CI3+CR4 CH(1,K,3) = CR3-CI4 CH(1,K,4) = CR3+CI4 CH(2,K,4) = CI3-CR4 CH(2,K,5) = CI2-CR5 101 CONTINUE RETURN 102 DO 104 K=1,L1 DO 103 I=2,IDO,2 TI5 = CC(I,2,K)-CC(I,5,K) TI2 = CC(I,2,K)+CC(I,5,K) TI4 = CC(I,3,K)-CC(I,4,K) TI3 = CC(I,3,K)+CC(I,4,K) TR5 = CC(I-1,2,K)-CC(I-1,5,K) TR2 = CC(I-1,2,K)+CC(I-1,5,K) TR4 = CC(I-1,3,K)-CC(I-1,4,K) TR3 = CC(I-1,3,K)+CC(I-1,4,K) CH(I-1,K,1) = CC(I-1,1,K)+TR2+TR3 CH(I,K,1) = CC(I,1,K)+TI2+TI3 CR2 = CC(I-1,1,K)+TR11*TR2+TR12*TR3 CI2 = CC(I,1,K)+TR11*TI2+TR12*TI3 CR3 = CC(I-1,1,K)+TR12*TR2+TR11*TR3 CI3 = CC(I,1,K)+TR12*TI2+TR11*TI3 CR5 = TI11*TR5+TI12*TR4 CI5 = TI11*TI5+TI12*TI4 CR4 = TI12*TR5-TI11*TR4 CI4 = TI12*TI5-TI11*TI4 DR3 = CR3-CI4 DR4 = CR3+CI4 DI3 = CI3+CR4 DI4 = CI3-CR4 DR5 = CR2+CI5 DR2 = CR2-CI5 DI5 = CI2-CR5 DI2 = CI2+CR5 CH(I-1,K,2) = WA1(I-1)*DR2-WA1(I)*DI2 CH(I,K,2) = WA1(I-1)*DI2+WA1(I)*DR2 CH(I-1,K,3) = WA2(I-1)*DR3-WA2(I)*DI3 CH(I,K,3) = WA2(I-1)*DI3+WA2(I)*DR3 CH(I-1,K,4) = WA3(I-1)*DR4-WA3(I)*DI4 CH(I,K,4) = WA3(I-1)*DI4+WA3(I)*DR4 CH(I-1,K,5) = WA4(I-1)*DR5-WA4(I)*DI5 CH(I,K,5) = WA4(I-1)*DI5+WA4(I)*DR5 103 CONTINUE 104 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE C1F2KB (IDO,L1,NA,CC,IN1,CH,IN2,WA) REAL CC(IN1,L1,IDO,2),CH(IN2,L1,2,IDO),WA(IDO,1,2) C IF (IDO.GT.1 .OR. NA.EQ.1) GO TO 102 DO 101 K=1,L1 CHOLD1 = CC(1,K,1,1)+CC(1,K,1,2) CC(1,K,1,2) = CC(1,K,1,1)-CC(1,K,1,2) CC(1,K,1,1) = CHOLD1 CHOLD2 = CC(2,K,1,1)+CC(2,K,1,2) CC(2,K,1,2) = CC(2,K,1,1)-CC(2,K,1,2) CC(2,K,1,1) = CHOLD2 101 CONTINUE RETURN 102 DO 103 K=1,L1 CH(1,K,1,1) = CC(1,K,1,1)+CC(1,K,1,2) CH(1,K,2,1) = CC(1,K,1,1)-CC(1,K,1,2) CH(2,K,1,1) = CC(2,K,1,1)+CC(2,K,1,2) CH(2,K,2,1) = CC(2,K,1,1)-CC(2,K,1,2) 103 CONTINUE IF(IDO .EQ. 1) RETURN DO 105 I=2,IDO DO 104 K=1,L1 CH(1,K,1,I) = CC(1,K,I,1)+CC(1,K,I,2) TR2 = CC(1,K,I,1)-CC(1,K,I,2) CH(2,K,1,I) = CC(2,K,I,1)+CC(2,K,I,2) TI2 = CC(2,K,I,1)-CC(2,K,I,2) CH(2,K,2,I) = WA(I,1,1)*TI2+WA(I,1,2)*TR2 CH(1,K,2,I) = WA(I,1,1)*TR2-WA(I,1,2)*TI2 104 CONTINUE 105 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE C1F2KF (IDO,L1,NA,CC,IN1,CH,IN2,WA) REAL CC(IN1,L1,IDO,2),CH(IN2,L1,2,IDO),WA(IDO,1,2) C IF (IDO .GT. 1) GO TO 102 SN = 1./REAL(2*L1) IF (NA .EQ. 1) GO TO 106 DO 101 K=1,L1 CHOLD1 = SN*(CC(1,K,1,1)+CC(1,K,1,2)) CC(1,K,1,2) = SN*(CC(1,K,1,1)-CC(1,K,1,2)) CC(1,K,1,1) = CHOLD1 CHOLD2 = SN*(CC(2,K,1,1)+CC(2,K,1,2)) CC(2,K,1,2) = SN*(CC(2,K,1,1)-CC(2,K,1,2)) CC(2,K,1,1) = CHOLD2 101 CONTINUE RETURN 106 DO 107 K=1,L1 CH(1,K,1,1) = SN*(CC(1,K,1,1)+CC(1,K,1,2)) CH(1,K,2,1) = SN*(CC(1,K,1,1)-CC(1,K,1,2)) CH(2,K,1,1) = SN*(CC(2,K,1,1)+CC(2,K,1,2)) CH(2,K,2,1) = SN*(CC(2,K,1,1)-CC(2,K,1,2)) 107 CONTINUE RETURN 102 DO 103 K=1,L1 CH(1,K,1,1) = CC(1,K,1,1)+CC(1,K,1,2) CH(1,K,2,1) = CC(1,K,1,1)-CC(1,K,1,2) CH(2,K,1,1) = CC(2,K,1,1)+CC(2,K,1,2) CH(2,K,2,1) = CC(2,K,1,1)-CC(2,K,1,2) 103 CONTINUE DO 105 I=2,IDO DO 104 K=1,L1 CH(1,K,1,I) = CC(1,K,I,1)+CC(1,K,I,2) TR2 = CC(1,K,I,1)-CC(1,K,I,2) CH(2,K,1,I) = CC(2,K,I,1)+CC(2,K,I,2) TI2 = CC(2,K,I,1)-CC(2,K,I,2) CH(2,K,2,I) = WA(I,1,1)*TI2-WA(I,1,2)*TR2 CH(1,K,2,I) = WA(I,1,1)*TR2+WA(I,1,2)*TI2 104 CONTINUE 105 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE C1F3KB (IDO,L1,NA,CC,IN1,CH,IN2,WA) REAL CC(IN1,L1,IDO,3),CH(IN2,L1,3,IDO),WA(IDO,2,2) DATA TAUR,TAUI /-.5,.866025403784439/ C IF (IDO.GT.1 .OR. NA.EQ.1) GO TO 102 DO 101 K=1,L1 TR2 = CC(1,K,1,2)+CC(1,K,1,3) CR2 = CC(1,K,1,1)+TAUR*TR2 CC(1,K,1,1) = CC(1,K,1,1)+TR2 TI2 = CC(2,K,1,2)+CC(2,K,1,3) CI2 = CC(2,K,1,1)+TAUR*TI2 CC(2,K,1,1) = CC(2,K,1,1)+TI2 CR3 = TAUI*(CC(1,K,1,2)-CC(1,K,1,3)) CI3 = TAUI*(CC(2,K,1,2)-CC(2,K,1,3)) CC(1,K,1,2) = CR2-CI3 CC(1,K,1,3) = CR2+CI3 CC(2,K,1,2) = CI2+CR3 CC(2,K,1,3) = CI2-CR3 101 CONTINUE RETURN 102 DO 103 K=1,L1 TR2 = CC(1,K,1,2)+CC(1,K,1,3) CR2 = CC(1,K,1,1)+TAUR*TR2 CH(1,K,1,1) = CC(1,K,1,1)+TR2 TI2 = CC(2,K,1,2)+CC(2,K,1,3) CI2 = CC(2,K,1,1)+TAUR*TI2 CH(2,K,1,1) = CC(2,K,1,1)+TI2 CR3 = TAUI*(CC(1,K,1,2)-CC(1,K,1,3)) CI3 = TAUI*(CC(2,K,1,2)-CC(2,K,1,3)) CH(1,K,2,1) = CR2-CI3 CH(1,K,3,1) = CR2+CI3 CH(2,K,2,1) = CI2+CR3 CH(2,K,3,1) = CI2-CR3 103 CONTINUE IF (IDO .EQ. 1) RETURN DO 105 I=2,IDO DO 104 K=1,L1 TR2 = CC(1,K,I,2)+CC(1,K,I,3) CR2 = CC(1,K,I,1)+TAUR*TR2 CH(1,K,1,I) = CC(1,K,I,1)+TR2 TI2 = CC(2,K,I,2)+CC(2,K,I,3) CI2 = CC(2,K,I,1)+TAUR*TI2 CH(2,K,1,I) = CC(2,K,I,1)+TI2 CR3 = TAUI*(CC(1,K,I,2)-CC(1,K,I,3)) CI3 = TAUI*(CC(2,K,I,2)-CC(2,K,I,3)) DR2 = CR2-CI3 DR3 = CR2+CI3 DI2 = CI2+CR3 DI3 = CI2-CR3 CH(2,K,2,I) = WA(I,1,1)*DI2+WA(I,1,2)*DR2 CH(1,K,2,I) = WA(I,1,1)*DR2-WA(I,1,2)*DI2 CH(2,K,3,I) = WA(I,2,1)*DI3+WA(I,2,2)*DR3 CH(1,K,3,I) = WA(I,2,1)*DR3-WA(I,2,2)*DI3 104 CONTINUE 105 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE C1F3KF (IDO,L1,NA,CC,IN1,CH,IN2,WA) REAL CC(IN1,L1,IDO,3),CH(IN2,L1,3,IDO),WA(IDO,2,2) DATA TAUR,TAUI /-.5,-.866025403784439/ C IF (IDO .GT. 1) GO TO 102 SN = 1./REAL(3*L1) IF (NA .EQ. 1) GO TO 106 DO 101 K=1,L1 TR2 = CC(1,K,1,2)+CC(1,K,1,3) CR2 = CC(1,K,1,1)+TAUR*TR2 CC(1,K,1,1) = SN*(CC(1,K,1,1)+TR2) TI2 = CC(2,K,1,2)+CC(2,K,1,3) CI2 = CC(2,K,1,1)+TAUR*TI2 CC(2,K,1,1) = SN*(CC(2,K,1,1)+TI2) CR3 = TAUI*(CC(1,K,1,2)-CC(1,K,1,3)) CI3 = TAUI*(CC(2,K,1,2)-CC(2,K,1,3)) CC(1,K,1,2) = SN*(CR2-CI3) CC(1,K,1,3) = SN*(CR2+CI3) CC(2,K,1,2) = SN*(CI2+CR3) CC(2,K,1,3) = SN*(CI2-CR3) 101 CONTINUE RETURN 106 DO 107 K=1,L1 TR2 = CC(1,K,1,2)+CC(1,K,1,3) CR2 = CC(1,K,1,1)+TAUR*TR2 CH(1,K,1,1) = SN*(CC(1,K,1,1)+TR2) TI2 = CC(2,K,1,2)+CC(2,K,1,3) CI2 = CC(2,K,1,1)+TAUR*TI2 CH(2,K,1,1) = SN*(CC(2,K,1,1)+TI2) CR3 = TAUI*(CC(1,K,1,2)-CC(1,K,1,3)) CI3 = TAUI*(CC(2,K,1,2)-CC(2,K,1,3)) CH(1,K,2,1) = SN*(CR2-CI3) CH(1,K,3,1) = SN*(CR2+CI3) CH(2,K,2,1) = SN*(CI2+CR3) CH(2,K,3,1) = SN*(CI2-CR3) 107 CONTINUE RETURN 102 DO 103 K=1,L1 TR2 = CC(1,K,1,2)+CC(1,K,1,3) CR2 = CC(1,K,1,1)+TAUR*TR2 CH(1,K,1,1) = CC(1,K,1,1)+TR2 TI2 = CC(2,K,1,2)+CC(2,K,1,3) CI2 = CC(2,K,1,1)+TAUR*TI2 CH(2,K,1,1) = CC(2,K,1,1)+TI2 CR3 = TAUI*(CC(1,K,1,2)-CC(1,K,1,3)) CI3 = TAUI*(CC(2,K,1,2)-CC(2,K,1,3)) CH(1,K,2,1) = CR2-CI3 CH(1,K,3,1) = CR2+CI3 CH(2,K,2,1) = CI2+CR3 CH(2,K,3,1) = CI2-CR3 103 CONTINUE DO 105 I=2,IDO DO 104 K=1,L1 TR2 = CC(1,K,I,2)+CC(1,K,I,3) CR2 = CC(1,K,I,1)+TAUR*TR2 CH(1,K,1,I) = CC(1,K,I,1)+TR2 TI2 = CC(2,K,I,2)+CC(2,K,I,3) CI2 = CC(2,K,I,1)+TAUR*TI2 CH(2,K,1,I) = CC(2,K,I,1)+TI2 CR3 = TAUI*(CC(1,K,I,2)-CC(1,K,I,3)) CI3 = TAUI*(CC(2,K,I,2)-CC(2,K,I,3)) DR2 = CR2-CI3 DR3 = CR2+CI3 DI2 = CI2+CR3 DI3 = CI2-CR3 CH(2,K,2,I) = WA(I,1,1)*DI2-WA(I,1,2)*DR2 CH(1,K,2,I) = WA(I,1,1)*DR2+WA(I,1,2)*DI2 CH(2,K,3,I) = WA(I,2,1)*DI3-WA(I,2,2)*DR3 CH(1,K,3,I) = WA(I,2,1)*DR3+WA(I,2,2)*DI3 104 CONTINUE 105 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE C1F4KB (IDO,L1,NA,CC,IN1,CH,IN2,WA) REAL CC(IN1,L1,IDO,4),CH(IN2,L1,4,IDO),WA(IDO,3,2) C C FFTPACK 5.1 auxiliary routine C IF (IDO.GT.1 .OR. NA.EQ.1) GO TO 102 DO 101 K=1,L1 TI1 = CC(2,K,1,1)-CC(2,K,1,3) TI2 = CC(2,K,1,1)+CC(2,K,1,3) TR4 = CC(2,K,1,4)-CC(2,K,1,2) TI3 = CC(2,K,1,2)+CC(2,K,1,4) TR1 = CC(1,K,1,1)-CC(1,K,1,3) TR2 = CC(1,K,1,1)+CC(1,K,1,3) TI4 = CC(1,K,1,2)-CC(1,K,1,4) TR3 = CC(1,K,1,2)+CC(1,K,1,4) CC(1,K,1,1) = TR2+TR3 CC(1,K,1,3) = TR2-TR3 CC(2,K,1,1) = TI2+TI3 CC(2,K,1,3) = TI2-TI3 CC(1,K,1,2) = TR1+TR4 CC(1,K,1,4) = TR1-TR4 CC(2,K,1,2) = TI1+TI4 CC(2,K,1,4) = TI1-TI4 101 CONTINUE RETURN 102 DO 103 K=1,L1 TI1 = CC(2,K,1,1)-CC(2,K,1,3) TI2 = CC(2,K,1,1)+CC(2,K,1,3) TR4 = CC(2,K,1,4)-CC(2,K,1,2) TI3 = CC(2,K,1,2)+CC(2,K,1,4) TR1 = CC(1,K,1,1)-CC(1,K,1,3) TR2 = CC(1,K,1,1)+CC(1,K,1,3) TI4 = CC(1,K,1,2)-CC(1,K,1,4) TR3 = CC(1,K,1,2)+CC(1,K,1,4) CH(1,K,1,1) = TR2+TR3 CH(1,K,3,1) = TR2-TR3 CH(2,K,1,1) = TI2+TI3 CH(2,K,3,1) = TI2-TI3 CH(1,K,2,1) = TR1+TR4 CH(1,K,4,1) = TR1-TR4 CH(2,K,2,1) = TI1+TI4 CH(2,K,4,1) = TI1-TI4 103 CONTINUE IF(IDO .EQ. 1) RETURN DO 105 I=2,IDO DO 104 K=1,L1 TI1 = CC(2,K,I,1)-CC(2,K,I,3) TI2 = CC(2,K,I,1)+CC(2,K,I,3) TI3 = CC(2,K,I,2)+CC(2,K,I,4) TR4 = CC(2,K,I,4)-CC(2,K,I,2) TR1 = CC(1,K,I,1)-CC(1,K,I,3) TR2 = CC(1,K,I,1)+CC(1,K,I,3) TI4 = CC(1,K,I,2)-CC(1,K,I,4) TR3 = CC(1,K,I,2)+CC(1,K,I,4) CH(1,K,1,I) = TR2+TR3 CR3 = TR2-TR3 CH(2,K,1,I) = TI2+TI3 CI3 = TI2-TI3 CR2 = TR1+TR4 CR4 = TR1-TR4 CI2 = TI1+TI4 CI4 = TI1-TI4 CH(1,K,2,I) = WA(I,1,1)*CR2-WA(I,1,2)*CI2 CH(2,K,2,I) = WA(I,1,1)*CI2+WA(I,1,2)*CR2 CH(1,K,3,I) = WA(I,2,1)*CR3-WA(I,2,2)*CI3 CH(2,K,3,I) = WA(I,2,1)*CI3+WA(I,2,2)*CR3 CH(1,K,4,I) = WA(I,3,1)*CR4-WA(I,3,2)*CI4 CH(2,K,4,I) = WA(I,3,1)*CI4+WA(I,3,2)*CR4 104 CONTINUE 105 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE C1F4KF (IDO,L1,NA,CC,IN1,CH,IN2,WA) REAL CC(IN1,L1,IDO,4),CH(IN2,L1,4,IDO),WA(IDO,3,2) C C FFTPACK 5.1 auxiliary routine C IF (IDO .GT. 1) GO TO 102 SN = 1./REAL(4*L1) IF (NA .EQ. 1) GO TO 106 DO 101 K=1,L1 TI1 = CC(2,K,1,1)-CC(2,K,1,3) TI2 = CC(2,K,1,1)+CC(2,K,1,3) TR4 = CC(2,K,1,2)-CC(2,K,1,4) TI3 = CC(2,K,1,2)+CC(2,K,1,4) TR1 = CC(1,K,1,1)-CC(1,K,1,3) TR2 = CC(1,K,1,1)+CC(1,K,1,3) TI4 = CC(1,K,1,4)-CC(1,K,1,2) TR3 = CC(1,K,1,2)+CC(1,K,1,4) CC(1,K,1,1) = SN*(TR2+TR3) CC(1,K,1,3) = SN*(TR2-TR3) CC(2,K,1,1) = SN*(TI2+TI3) CC(2,K,1,3) = SN*(TI2-TI3) CC(1,K,1,2) = SN*(TR1+TR4) CC(1,K,1,4) = SN*(TR1-TR4) CC(2,K,1,2) = SN*(TI1+TI4) CC(2,K,1,4) = SN*(TI1-TI4) 101 CONTINUE RETURN 106 DO 107 K=1,L1 TI1 = CC(2,K,1,1)-CC(2,K,1,3) TI2 = CC(2,K,1,1)+CC(2,K,1,3) TR4 = CC(2,K,1,2)-CC(2,K,1,4) TI3 = CC(2,K,1,2)+CC(2,K,1,4) TR1 = CC(1,K,1,1)-CC(1,K,1,3) TR2 = CC(1,K,1,1)+CC(1,K,1,3) TI4 = CC(1,K,1,4)-CC(1,K,1,2) TR3 = CC(1,K,1,2)+CC(1,K,1,4) CH(1,K,1,1) = SN*(TR2+TR3) CH(1,K,3,1) = SN*(TR2-TR3) CH(2,K,1,1) = SN*(TI2+TI3) CH(2,K,3,1) = SN*(TI2-TI3) CH(1,K,2,1) = SN*(TR1+TR4) CH(1,K,4,1) = SN*(TR1-TR4) CH(2,K,2,1) = SN*(TI1+TI4) CH(2,K,4,1) = SN*(TI1-TI4) 107 CONTINUE RETURN 102 DO 103 K=1,L1 TI1 = CC(2,K,1,1)-CC(2,K,1,3) TI2 = CC(2,K,1,1)+CC(2,K,1,3) TR4 = CC(2,K,1,2)-CC(2,K,1,4) TI3 = CC(2,K,1,2)+CC(2,K,1,4) TR1 = CC(1,K,1,1)-CC(1,K,1,3) TR2 = CC(1,K,1,1)+CC(1,K,1,3) TI4 = CC(1,K,1,4)-CC(1,K,1,2) TR3 = CC(1,K,1,2)+CC(1,K,1,4) CH(1,K,1,1) = TR2+TR3 CH(1,K,3,1) = TR2-TR3 CH(2,K,1,1) = TI2+TI3 CH(2,K,3,1) = TI2-TI3 CH(1,K,2,1) = TR1+TR4 CH(1,K,4,1) = TR1-TR4 CH(2,K,2,1) = TI1+TI4 CH(2,K,4,1) = TI1-TI4 103 CONTINUE DO 105 I=2,IDO DO 104 K=1,L1 TI1 = CC(2,K,I,1)-CC(2,K,I,3) TI2 = CC(2,K,I,1)+CC(2,K,I,3) TI3 = CC(2,K,I,2)+CC(2,K,I,4) TR4 = CC(2,K,I,2)-CC(2,K,I,4) TR1 = CC(1,K,I,1)-CC(1,K,I,3) TR2 = CC(1,K,I,1)+CC(1,K,I,3) TI4 = CC(1,K,I,4)-CC(1,K,I,2) TR3 = CC(1,K,I,2)+CC(1,K,I,4) CH(1,K,1,I) = TR2+TR3 CR3 = TR2-TR3 CH(2,K,1,I) = TI2+TI3 CI3 = TI2-TI3 CR2 = TR1+TR4 CR4 = TR1-TR4 CI2 = TI1+TI4 CI4 = TI1-TI4 CH(1,K,2,I) = WA(I,1,1)*CR2+WA(I,1,2)*CI2 CH(2,K,2,I) = WA(I,1,1)*CI2-WA(I,1,2)*CR2 CH(1,K,3,I) = WA(I,2,1)*CR3+WA(I,2,2)*CI3 CH(2,K,3,I) = WA(I,2,1)*CI3-WA(I,2,2)*CR3 CH(1,K,4,I) = WA(I,3,1)*CR4+WA(I,3,2)*CI4 CH(2,K,4,I) = WA(I,3,1)*CI4-WA(I,3,2)*CR4 104 CONTINUE 105 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE C1F5KB (IDO,L1,NA,CC,IN1,CH,IN2,WA) REAL CC(IN1,L1,IDO,5),CH(IN2,L1,5,IDO),WA(IDO,4,2) DATA TR11,TI11,TR12,TI12 /.3090169943749474,.9510565162951536, 1-.8090169943749474,.5877852522924731/ C C FFTPACK 5.1 auxiliary routine C IF (IDO.GT.1 .OR. NA.EQ.1) GO TO 102 DO 101 K=1,L1 TI5 = CC(2,K,1,2)-CC(2,K,1,5) TI2 = CC(2,K,1,2)+CC(2,K,1,5) TI4 = CC(2,K,1,3)-CC(2,K,1,4) TI3 = CC(2,K,1,3)+CC(2,K,1,4) TR5 = CC(1,K,1,2)-CC(1,K,1,5) TR2 = CC(1,K,1,2)+CC(1,K,1,5) TR4 = CC(1,K,1,3)-CC(1,K,1,4) TR3 = CC(1,K,1,3)+CC(1,K,1,4) CHOLD1 = CC(1,K,1,1)+TR2+TR3 CHOLD2 = CC(2,K,1,1)+TI2+TI3 CR2 = CC(1,K,1,1)+TR11*TR2+TR12*TR3 CI2 = CC(2,K,1,1)+TR11*TI2+TR12*TI3 CR3 = CC(1,K,1,1)+TR12*TR2+TR11*TR3 CI3 = CC(2,K,1,1)+TR12*TI2+TR11*TI3 CC(1,K,1,1) = CHOLD1 CC(2,K,1,1) = CHOLD2 CR5 = TI11*TR5+TI12*TR4 CI5 = TI11*TI5+TI12*TI4 CR4 = TI12*TR5-TI11*TR4 CI4 = TI12*TI5-TI11*TI4 CC(1,K,1,2) = CR2-CI5 CC(1,K,1,5) = CR2+CI5 CC(2,K,1,2) = CI2+CR5 CC(2,K,1,3) = CI3+CR4 CC(1,K,1,3) = CR3-CI4 CC(1,K,1,4) = CR3+CI4 CC(2,K,1,4) = CI3-CR4 CC(2,K,1,5) = CI2-CR5 101 CONTINUE RETURN 102 DO 103 K=1,L1 TI5 = CC(2,K,1,2)-CC(2,K,1,5) TI2 = CC(2,K,1,2)+CC(2,K,1,5) TI4 = CC(2,K,1,3)-CC(2,K,1,4) TI3 = CC(2,K,1,3)+CC(2,K,1,4) TR5 = CC(1,K,1,2)-CC(1,K,1,5) TR2 = CC(1,K,1,2)+CC(1,K,1,5) TR4 = CC(1,K,1,3)-CC(1,K,1,4) TR3 = CC(1,K,1,3)+CC(1,K,1,4) CH(1,K,1,1) = CC(1,K,1,1)+TR2+TR3 CH(2,K,1,1) = CC(2,K,1,1)+TI2+TI3 CR2 = CC(1,K,1,1)+TR11*TR2+TR12*TR3 CI2 = CC(2,K,1,1)+TR11*TI2+TR12*TI3 CR3 = CC(1,K,1,1)+TR12*TR2+TR11*TR3 CI3 = CC(2,K,1,1)+TR12*TI2+TR11*TI3 CR5 = TI11*TR5+TI12*TR4 CI5 = TI11*TI5+TI12*TI4 CR4 = TI12*TR5-TI11*TR4 CI4 = TI12*TI5-TI11*TI4 CH(1,K,2,1) = CR2-CI5 CH(1,K,5,1) = CR2+CI5 CH(2,K,2,1) = CI2+CR5 CH(2,K,3,1) = CI3+CR4 CH(1,K,3,1) = CR3-CI4 CH(1,K,4,1) = CR3+CI4 CH(2,K,4,1) = CI3-CR4 CH(2,K,5,1) = CI2-CR5 103 CONTINUE IF(IDO .EQ. 1) RETURN DO 105 I=2,IDO DO 104 K=1,L1 TI5 = CC(2,K,I,2)-CC(2,K,I,5) TI2 = CC(2,K,I,2)+CC(2,K,I,5) TI4 = CC(2,K,I,3)-CC(2,K,I,4) TI3 = CC(2,K,I,3)+CC(2,K,I,4) TR5 = CC(1,K,I,2)-CC(1,K,I,5) TR2 = CC(1,K,I,2)+CC(1,K,I,5) TR4 = CC(1,K,I,3)-CC(1,K,I,4) TR3 = CC(1,K,I,3)+CC(1,K,I,4) CH(1,K,1,I) = CC(1,K,I,1)+TR2+TR3 CH(2,K,1,I) = CC(2,K,I,1)+TI2+TI3 CR2 = CC(1,K,I,1)+TR11*TR2+TR12*TR3 CI2 = CC(2,K,I,1)+TR11*TI2+TR12*TI3 CR3 = CC(1,K,I,1)+TR12*TR2+TR11*TR3 CI3 = CC(2,K,I,1)+TR12*TI2+TR11*TI3 CR5 = TI11*TR5+TI12*TR4 CI5 = TI11*TI5+TI12*TI4 CR4 = TI12*TR5-TI11*TR4 CI4 = TI12*TI5-TI11*TI4 DR3 = CR3-CI4 DR4 = CR3+CI4 DI3 = CI3+CR4 DI4 = CI3-CR4 DR5 = CR2+CI5 DR2 = CR2-CI5 DI5 = CI2-CR5 DI2 = CI2+CR5 CH(1,K,2,I) = WA(I,1,1)*DR2-WA(I,1,2)*DI2 CH(2,K,2,I) = WA(I,1,1)*DI2+WA(I,1,2)*DR2 CH(1,K,3,I) = WA(I,2,1)*DR3-WA(I,2,2)*DI3 CH(2,K,3,I) = WA(I,2,1)*DI3+WA(I,2,2)*DR3 CH(1,K,4,I) = WA(I,3,1)*DR4-WA(I,3,2)*DI4 CH(2,K,4,I) = WA(I,3,1)*DI4+WA(I,3,2)*DR4 CH(1,K,5,I) = WA(I,4,1)*DR5-WA(I,4,2)*DI5 CH(2,K,5,I) = WA(I,4,1)*DI5+WA(I,4,2)*DR5 104 CONTINUE 105 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE C1F5KF (IDO,L1,NA,CC,IN1,CH,IN2,WA) REAL CC(IN1,L1,IDO,5),CH(IN2,L1,5,IDO),WA(IDO,4,2) DATA TR11,TI11,TR12,TI12 /.3090169943749474,-.9510565162951536, 1-.8090169943749474,-.5877852522924731/ C C FFTPACK 5.1 auxiliary routine C IF (IDO .GT. 1) GO TO 102 SN = 1./REAL(5*L1) IF (NA .EQ. 1) GO TO 106 DO 101 K=1,L1 TI5 = CC(2,K,1,2)-CC(2,K,1,5) TI2 = CC(2,K,1,2)+CC(2,K,1,5) TI4 = CC(2,K,1,3)-CC(2,K,1,4) TI3 = CC(2,K,1,3)+CC(2,K,1,4) TR5 = CC(1,K,1,2)-CC(1,K,1,5) TR2 = CC(1,K,1,2)+CC(1,K,1,5) TR4 = CC(1,K,1,3)-CC(1,K,1,4) TR3 = CC(1,K,1,3)+CC(1,K,1,4) CHOLD1 = SN*(CC(1,K,1,1)+TR2+TR3) CHOLD2 = SN*(CC(2,K,1,1)+TI2+TI3) CR2 = CC(1,K,1,1)+TR11*TR2+TR12*TR3 CI2 = CC(2,K,1,1)+TR11*TI2+TR12*TI3 CR3 = CC(1,K,1,1)+TR12*TR2+TR11*TR3 CI3 = CC(2,K,1,1)+TR12*TI2+TR11*TI3 CC(1,K,1,1) = CHOLD1 CC(2,K,1,1) = CHOLD2 CR5 = TI11*TR5+TI12*TR4 CI5 = TI11*TI5+TI12*TI4 CR4 = TI12*TR5-TI11*TR4 CI4 = TI12*TI5-TI11*TI4 CC(1,K,1,2) = SN*(CR2-CI5) CC(1,K,1,5) = SN*(CR2+CI5) CC(2,K,1,2) = SN*(CI2+CR5) CC(2,K,1,3) = SN*(CI3+CR4) CC(1,K,1,3) = SN*(CR3-CI4) CC(1,K,1,4) = SN*(CR3+CI4) CC(2,K,1,4) = SN*(CI3-CR4) CC(2,K,1,5) = SN*(CI2-CR5) 101 CONTINUE RETURN 106 DO 107 K=1,L1 TI5 = CC(2,K,1,2)-CC(2,K,1,5) TI2 = CC(2,K,1,2)+CC(2,K,1,5) TI4 = CC(2,K,1,3)-CC(2,K,1,4) TI3 = CC(2,K,1,3)+CC(2,K,1,4) TR5 = CC(1,K,1,2)-CC(1,K,1,5) TR2 = CC(1,K,1,2)+CC(1,K,1,5) TR4 = CC(1,K,1,3)-CC(1,K,1,4) TR3 = CC(1,K,1,3)+CC(1,K,1,4) CH(1,K,1,1) = SN*(CC(1,K,1,1)+TR2+TR3) CH(2,K,1,1) = SN*(CC(2,K,1,1)+TI2+TI3) CR2 = CC(1,K,1,1)+TR11*TR2+TR12*TR3 CI2 = CC(2,K,1,1)+TR11*TI2+TR12*TI3 CR3 = CC(1,K,1,1)+TR12*TR2+TR11*TR3 CI3 = CC(2,K,1,1)+TR12*TI2+TR11*TI3 CR5 = TI11*TR5+TI12*TR4 CI5 = TI11*TI5+TI12*TI4 CR4 = TI12*TR5-TI11*TR4 CI4 = TI12*TI5-TI11*TI4 CH(1,K,2,1) = SN*(CR2-CI5) CH(1,K,5,1) = SN*(CR2+CI5) CH(2,K,2,1) = SN*(CI2+CR5) CH(2,K,3,1) = SN*(CI3+CR4) CH(1,K,3,1) = SN*(CR3-CI4) CH(1,K,4,1) = SN*(CR3+CI4) CH(2,K,4,1) = SN*(CI3-CR4) CH(2,K,5,1) = SN*(CI2-CR5) 107 CONTINUE RETURN 102 DO 103 K=1,L1 TI5 = CC(2,K,1,2)-CC(2,K,1,5) TI2 = CC(2,K,1,2)+CC(2,K,1,5) TI4 = CC(2,K,1,3)-CC(2,K,1,4) TI3 = CC(2,K,1,3)+CC(2,K,1,4) TR5 = CC(1,K,1,2)-CC(1,K,1,5) TR2 = CC(1,K,1,2)+CC(1,K,1,5) TR4 = CC(1,K,1,3)-CC(1,K,1,4) TR3 = CC(1,K,1,3)+CC(1,K,1,4) CH(1,K,1,1) = CC(1,K,1,1)+TR2+TR3 CH(2,K,1,1) = CC(2,K,1,1)+TI2+TI3 CR2 = CC(1,K,1,1)+TR11*TR2+TR12*TR3 CI2 = CC(2,K,1,1)+TR11*TI2+TR12*TI3 CR3 = CC(1,K,1,1)+TR12*TR2+TR11*TR3 CI3 = CC(2,K,1,1)+TR12*TI2+TR11*TI3 CR5 = TI11*TR5+TI12*TR4 CI5 = TI11*TI5+TI12*TI4 CR4 = TI12*TR5-TI11*TR4 CI4 = TI12*TI5-TI11*TI4 CH(1,K,2,1) = CR2-CI5 CH(1,K,5,1) = CR2+CI5 CH(2,K,2,1) = CI2+CR5 CH(2,K,3,1) = CI3+CR4 CH(1,K,3,1) = CR3-CI4 CH(1,K,4,1) = CR3+CI4 CH(2,K,4,1) = CI3-CR4 CH(2,K,5,1) = CI2-CR5 103 CONTINUE DO 105 I=2,IDO DO 104 K=1,L1 TI5 = CC(2,K,I,2)-CC(2,K,I,5) TI2 = CC(2,K,I,2)+CC(2,K,I,5) TI4 = CC(2,K,I,3)-CC(2,K,I,4) TI3 = CC(2,K,I,3)+CC(2,K,I,4) TR5 = CC(1,K,I,2)-CC(1,K,I,5) TR2 = CC(1,K,I,2)+CC(1,K,I,5) TR4 = CC(1,K,I,3)-CC(1,K,I,4) TR3 = CC(1,K,I,3)+CC(1,K,I,4) CH(1,K,1,I) = CC(1,K,I,1)+TR2+TR3 CH(2,K,1,I) = CC(2,K,I,1)+TI2+TI3 CR2 = CC(1,K,I,1)+TR11*TR2+TR12*TR3 CI2 = CC(2,K,I,1)+TR11*TI2+TR12*TI3 CR3 = CC(1,K,I,1)+TR12*TR2+TR11*TR3 CI3 = CC(2,K,I,1)+TR12*TI2+TR11*TI3 CR5 = TI11*TR5+TI12*TR4 CI5 = TI11*TI5+TI12*TI4 CR4 = TI12*TR5-TI11*TR4 CI4 = TI12*TI5-TI11*TI4 DR3 = CR3-CI4 DR4 = CR3+CI4 DI3 = CI3+CR4 DI4 = CI3-CR4 DR5 = CR2+CI5 DR2 = CR2-CI5 DI5 = CI2-CR5 DI2 = CI2+CR5 CH(1,K,2,I) = WA(I,1,1)*DR2+WA(I,1,2)*DI2 CH(2,K,2,I) = WA(I,1,1)*DI2-WA(I,1,2)*DR2 CH(1,K,3,I) = WA(I,2,1)*DR3+WA(I,2,2)*DI3 CH(2,K,3,I) = WA(I,2,1)*DI3-WA(I,2,2)*DR3 CH(1,K,4,I) = WA(I,3,1)*DR4+WA(I,3,2)*DI4 CH(2,K,4,I) = WA(I,3,1)*DI4-WA(I,3,2)*DR4 CH(1,K,5,I) = WA(I,4,1)*DR5+WA(I,4,2)*DI5 CH(2,K,5,I) = WA(I,4,1)*DI5-WA(I,4,2)*DR5 104 CONTINUE 105 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE C1FGKB (IDO,IP,L1,LID,NA,CC,CC1,IN1, 1 CH,CH1,IN2,WA) REAL CH(IN2,L1,IDO,IP) ,CC(IN1,L1,IP,IDO), 1 CC1(IN1,LID,IP) ,CH1(IN2,LID,IP) , 2 WA(IDO,IP-1,2) C C FFTPACK 5.1 auxiliary routine C IPP2 = IP+2 IPPH = (IP+1)/2 DO 110 KI=1,LID CH1(1,KI,1) = CC1(1,KI,1) CH1(2,KI,1) = CC1(2,KI,1) 110 CONTINUE DO 111 J=2,IPPH JC = IPP2-J DO 112 KI=1,LID CH1(1,KI,J) = CC1(1,KI,J)+CC1(1,KI,JC) CH1(1,KI,JC) = CC1(1,KI,J)-CC1(1,KI,JC) CH1(2,KI,J) = CC1(2,KI,J)+CC1(2,KI,JC) CH1(2,KI,JC) = CC1(2,KI,J)-CC1(2,KI,JC) 112 CONTINUE 111 CONTINUE DO 118 J=2,IPPH DO 117 KI=1,LID CC1(1,KI,1) = CC1(1,KI,1)+CH1(1,KI,J) CC1(2,KI,1) = CC1(2,KI,1)+CH1(2,KI,J) 117 CONTINUE 118 CONTINUE DO 116 L=2,IPPH LC = IPP2-L DO 113 KI=1,LID CC1(1,KI,L) = CH1(1,KI,1)+WA(1,L-1,1)*CH1(1,KI,2) CC1(1,KI,LC) = WA(1,L-1,2)*CH1(1,KI,IP) CC1(2,KI,L) = CH1(2,KI,1)+WA(1,L-1,1)*CH1(2,KI,2) CC1(2,KI,LC) = WA(1,L-1,2)*CH1(2,KI,IP) 113 CONTINUE DO 115 J=3,IPPH JC = IPP2-J IDLJ = MOD((L-1)*(J-1),IP) WAR = WA(1,IDLJ,1) WAI = WA(1,IDLJ,2) DO 114 KI=1,LID CC1(1,KI,L) = CC1(1,KI,L)+WAR*CH1(1,KI,J) CC1(1,KI,LC) = CC1(1,KI,LC)+WAI*CH1(1,KI,JC) CC1(2,KI,L) = CC1(2,KI,L)+WAR*CH1(2,KI,J) CC1(2,KI,LC) = CC1(2,KI,LC)+WAI*CH1(2,KI,JC) 114 CONTINUE 115 CONTINUE 116 CONTINUE IF(IDO.GT.1 .OR. NA.EQ.1) GO TO 136 DO 120 J=2,IPPH JC = IPP2-J DO 119 KI=1,LID CHOLD1 = CC1(1,KI,J)-CC1(2,KI,JC) CHOLD2 = CC1(1,KI,J)+CC1(2,KI,JC) CC1(1,KI,J) = CHOLD1 CC1(2,KI,JC) = CC1(2,KI,J)-CC1(1,KI,JC) CC1(2,KI,J) = CC1(2,KI,J)+CC1(1,KI,JC) CC1(1,KI,JC) = CHOLD2 119 CONTINUE 120 CONTINUE RETURN 136 DO 137 KI=1,LID CH1(1,KI,1) = CC1(1,KI,1) CH1(2,KI,1) = CC1(2,KI,1) 137 CONTINUE DO 135 J=2,IPPH JC = IPP2-J DO 134 KI=1,LID CH1(1,KI,J) = CC1(1,KI,J)-CC1(2,KI,JC) CH1(1,KI,JC) = CC1(1,KI,J)+CC1(2,KI,JC) CH1(2,KI,JC) = CC1(2,KI,J)-CC1(1,KI,JC) CH1(2,KI,J) = CC1(2,KI,J)+CC1(1,KI,JC) 134 CONTINUE 135 CONTINUE IF (IDO .EQ. 1) RETURN DO 131 I=1,IDO DO 130 K=1,L1 CC(1,K,1,I) = CH(1,K,I,1) CC(2,K,1,I) = CH(2,K,I,1) 130 CONTINUE 131 CONTINUE DO 123 J=2,IP DO 122 K=1,L1 CC(1,K,J,1) = CH(1,K,1,J) CC(2,K,J,1) = CH(2,K,1,J) 122 CONTINUE 123 CONTINUE DO 126 J=2,IP DO 125 I=2,IDO DO 124 K=1,L1 CC(1,K,J,I) = WA(I,J-1,1)*CH(1,K,I,J) 1 -WA(I,J-1,2)*CH(2,K,I,J) CC(2,K,J,I) = WA(I,J-1,1)*CH(2,K,I,J) 1 +WA(I,J-1,2)*CH(1,K,I,J) 124 CONTINUE 125 CONTINUE 126 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE C1FGKF (IDO,IP,L1,LID,NA,CC,CC1,IN1, 1 CH,CH1,IN2,WA) REAL CH(IN2,L1,IDO,IP) ,CC(IN1,L1,IP,IDO), 1 CC1(IN1,LID,IP) ,CH1(IN2,LID,IP) , 2 WA(IDO,IP-1,2) C C FFTPACK 5.1 auxiliary routine C IPP2 = IP+2 IPPH = (IP+1)/2 DO 110 KI=1,LID CH1(1,KI,1) = CC1(1,KI,1) CH1(2,KI,1) = CC1(2,KI,1) 110 CONTINUE DO 111 J=2,IPPH JC = IPP2-J DO 112 KI=1,LID CH1(1,KI,J) = CC1(1,KI,J)+CC1(1,KI,JC) CH1(1,KI,JC) = CC1(1,KI,J)-CC1(1,KI,JC) CH1(2,KI,J) = CC1(2,KI,J)+CC1(2,KI,JC) CH1(2,KI,JC) = CC1(2,KI,J)-CC1(2,KI,JC) 112 CONTINUE 111 CONTINUE DO 118 J=2,IPPH DO 117 KI=1,LID CC1(1,KI,1) = CC1(1,KI,1)+CH1(1,KI,J) CC1(2,KI,1) = CC1(2,KI,1)+CH1(2,KI,J) 117 CONTINUE 118 CONTINUE DO 116 L=2,IPPH LC = IPP2-L DO 113 KI=1,LID CC1(1,KI,L) = CH1(1,KI,1)+WA(1,L-1,1)*CH1(1,KI,2) CC1(1,KI,LC) = -WA(1,L-1,2)*CH1(1,KI,IP) CC1(2,KI,L) = CH1(2,KI,1)+WA(1,L-1,1)*CH1(2,KI,2) CC1(2,KI,LC) = -WA(1,L-1,2)*CH1(2,KI,IP) 113 CONTINUE DO 115 J=3,IPPH JC = IPP2-J IDLJ = MOD((L-1)*(J-1),IP) WAR = WA(1,IDLJ,1) WAI = -WA(1,IDLJ,2) DO 114 KI=1,LID CC1(1,KI,L) = CC1(1,KI,L)+WAR*CH1(1,KI,J) CC1(1,KI,LC) = CC1(1,KI,LC)+WAI*CH1(1,KI,JC) CC1(2,KI,L) = CC1(2,KI,L)+WAR*CH1(2,KI,J) CC1(2,KI,LC) = CC1(2,KI,LC)+WAI*CH1(2,KI,JC) 114 CONTINUE 115 CONTINUE 116 CONTINUE IF (IDO .GT. 1) GO TO 136 SN = 1./REAL(IP*L1) IF (NA .EQ. 1) GO TO 146 DO 149 KI=1,LID CC1(1,KI,1) = SN*CC1(1,KI,1) CC1(2,KI,1) = SN*CC1(2,KI,1) 149 CONTINUE DO 120 J=2,IPPH JC = IPP2-J DO 119 KI=1,LID CHOLD1 = SN*(CC1(1,KI,J)-CC1(2,KI,JC)) CHOLD2 = SN*(CC1(1,KI,J)+CC1(2,KI,JC)) CC1(1,KI,J) = CHOLD1 CC1(2,KI,JC) = SN*(CC1(2,KI,J)-CC1(1,KI,JC)) CC1(2,KI,J) = SN*(CC1(2,KI,J)+CC1(1,KI,JC)) CC1(1,KI,JC) = CHOLD2 119 CONTINUE 120 CONTINUE RETURN 146 DO 147 KI=1,LID CH1(1,KI,1) = SN*CC1(1,KI,1) CH1(2,KI,1) = SN*CC1(2,KI,1) 147 CONTINUE DO 145 J=2,IPPH JC = IPP2-J DO 144 KI=1,LID CH1(1,KI,J) = SN*(CC1(1,KI,J)-CC1(2,KI,JC)) CH1(2,KI,J) = SN*(CC1(2,KI,J)+CC1(1,KI,JC)) CH1(1,KI,JC) = SN*(CC1(1,KI,J)+CC1(2,KI,JC)) CH1(2,KI,JC) = SN*(CC1(2,KI,J)-CC1(1,KI,JC)) 144 CONTINUE 145 CONTINUE RETURN 136 DO 137 KI=1,LID CH1(1,KI,1) = CC1(1,KI,1) CH1(2,KI,1) = CC1(2,KI,1) 137 CONTINUE DO 135 J=2,IPPH JC = IPP2-J DO 134 KI=1,LID CH1(1,KI,J) = CC1(1,KI,J)-CC1(2,KI,JC) CH1(2,KI,J) = CC1(2,KI,J)+CC1(1,KI,JC) CH1(1,KI,JC) = CC1(1,KI,J)+CC1(2,KI,JC) CH1(2,KI,JC) = CC1(2,KI,J)-CC1(1,KI,JC) 134 CONTINUE 135 CONTINUE DO 131 I=1,IDO DO 130 K=1,L1 CC(1,K,1,I) = CH(1,K,I,1) CC(2,K,1,I) = CH(2,K,I,1) 130 CONTINUE 131 CONTINUE DO 123 J=2,IP DO 122 K=1,L1 CC(1,K,J,1) = CH(1,K,1,J) CC(2,K,J,1) = CH(2,K,1,J) 122 CONTINUE 123 CONTINUE DO 126 J=2,IP DO 125 I=2,IDO DO 124 K=1,L1 CC(1,K,J,I) = WA(I,J-1,1)*CH(1,K,I,J) 1 +WA(I,J-1,2)*CH(2,K,I,J) CC(2,K,J,I) = WA(I,J-1,1)*CH(2,K,I,J) 1 -WA(I,J-1,2)*CH(1,K,I,J) 124 CONTINUE 125 CONTINUE 126 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE C1FM1B (N,INC,C,CH,WA,FNF,FAC) COMPLEX C(*) REAL CH(*), WA(*), FAC(*) C C FFTPACK 5.1 auxiliary routine C INC2 = INC+INC NF = FNF NA = 0 L1 = 1 IW = 1 DO 125 K1=1,NF IP = FAC(K1) L2 = IP*L1 IDO = N/L2 LID = L1*IDO NBR = 1+NA+2*MIN(IP-2,4) GO TO (52,62,53,63,54,64,55,65,56,66),NBR 52 CALL C1F2KB (IDO,L1,NA,C,INC2,CH,2,WA(IW)) GO TO 120 62 CALL C1F2KB (IDO,L1,NA,CH,2,C,INC2,WA(IW)) GO TO 120 53 CALL C1F3KB (IDO,L1,NA,C,INC2,CH,2,WA(IW)) GO TO 120 63 CALL C1F3KB (IDO,L1,NA,CH,2,C,INC2,WA(IW)) GO TO 120 54 CALL C1F4KB (IDO,L1,NA,C,INC2,CH,2,WA(IW)) GO TO 120 64 CALL C1F4KB (IDO,L1,NA,CH,2,C,INC2,WA(IW)) GO TO 120 55 CALL C1F5KB (IDO,L1,NA,C,INC2,CH,2,WA(IW)) GO TO 120 65 CALL C1F5KB (IDO,L1,NA,CH,2,C,INC2,WA(IW)) GO TO 120 56 CALL C1FGKB (IDO,IP,L1,LID,NA,C,C,INC2,CH,CH,2, 1 WA(IW)) GO TO 120 66 CALL C1FGKB (IDO,IP,L1,LID,NA,CH,CH,2,C,C, 1 INC2,WA(IW)) 120 L1 = L2 IW = IW+(IP-1)*(IDO+IDO) IF(IP .LE. 5) NA = 1-NA 125 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE C1FM1F (N,INC,C,CH,WA,FNF,FAC) COMPLEX C(*) REAL CH(*), WA(*), FAC(*) C C FFTPACK 5.1 auxiliary routine C INC2 = INC+INC NF = FNF NA = 0 L1 = 1 IW = 1 DO 125 K1=1,NF IP = FAC(K1) L2 = IP*L1 IDO = N/L2 LID = L1*IDO NBR = 1+NA+2*MIN(IP-2,4) GO TO (52,62,53,63,54,64,55,65,56,66),NBR 52 CALL C1F2KF (IDO,L1,NA,C,INC2,CH,2,WA(IW)) GO TO 120 62 CALL C1F2KF (IDO,L1,NA,CH,2,C,INC2,WA(IW)) GO TO 120 53 CALL C1F3KF (IDO,L1,NA,C,INC2,CH,2,WA(IW)) GO TO 120 63 CALL C1F3KF (IDO,L1,NA,CH,2,C,INC2,WA(IW)) GO TO 120 54 CALL C1F4KF (IDO,L1,NA,C,INC2,CH,2,WA(IW)) GO TO 120 64 CALL C1F4KF (IDO,L1,NA,CH,2,C,INC2,WA(IW)) GO TO 120 55 CALL C1F5KF (IDO,L1,NA,C,INC2,CH,2,WA(IW)) GO TO 120 65 CALL C1F5KF (IDO,L1,NA,CH,2,C,INC2,WA(IW)) GO TO 120 56 CALL C1FGKF (IDO,IP,L1,LID,NA,C,C,INC2,CH,CH, 1 2,WA(IW)) GO TO 120 66 CALL C1FGKF (IDO,IP,L1,LID,NA,CH,CH,2,C,C, 1 INC2,WA(IW)) 120 L1 = L2 IW = IW+(IP-1)*(IDO+IDO) IF(IP .LE. 5) NA = 1-NA 125 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE CFFT1B (N, INC, C, LENC, WSAVE, LENSAV, 1 WORK, LENWRK, IER) INTEGER N, INC, LENC, LENSAV, LENWRK, IER COMPLEX C(LENC) REAL WSAVE(LENSAV) ,WORK(LENWRK) C IER = 0 C IF (LENC .LT. INC*(N-1) + 1) THEN IER = 1 CALL XERFFT ('CFFT1B ', 4) ELSEIF (LENSAV .LT. 2*N + INT(LOG(REAL(N))/LOG(2.)) + 4) THEN IER = 2 CALL XERFFT ('CFFT1B ', 6) ELSEIF (LENWRK .LT. 2*N) THEN IER = 3 CALL XERFFT ('CFFT1B ', 8) ENDIF C IF (N .EQ. 1) RETURN C IW1 = N+N+1 CALL C1FM1B (N,INC,C,WORK,WSAVE,WSAVE(IW1), 1 WSAVE(IW1+1)) RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE CFFT1F (N, INC, C, LENC, WSAVE, LENSAV, 1 WORK, LENWRK, IER) INTEGER N, INC, LENC, LENSAV, LENWRK, IER COMPLEX C(LENC) REAL WSAVE(LENSAV) ,WORK(LENWRK) C IER = 0 C IF (LENC .LT. INC*(N-1) + 1) THEN IER = 1 CALL XERFFT ('CFFT1F ', 4) ELSEIF (LENSAV .LT. 2*N + INT(LOG(REAL(N))/LOG(2.)) + 4) THEN IER = 2 CALL XERFFT ('CFFT1F ', 6) ELSEIF (LENWRK .LT. 2*N) THEN IER = 3 CALL XERFFT ('CFFT1F ', 8) ENDIF C IF (N .EQ. 1) RETURN C IW1 = N+N+1 CALL C1FM1F (N,INC,C,WORK,WSAVE,WSAVE(IW1), 1 WSAVE(IW1+1)) RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE CFFT1I (N, WSAVE, LENSAV, IER) INTEGER N, LENSAV, IER REAL WSAVE(LENSAV) C IER = 0 C IF (LENSAV .LT. 2*N + INT(LOG(REAL(N))/LOG(2.)) + 4) THEN IER = 2 CALL XERFFT ('CFFTMI ', 3) ENDIF C IF (N .EQ. 1) RETURN C IW1 = N+N+1 CALL MCFTI1 (N,WSAVE,WSAVE(IW1),WSAVE(IW1+1)) C RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE CFFT2B (LDIM, L, M, C, WSAVE, LENSAV, 1 WORK, LENWRK, IER) INTEGER L, M, LDIM, LENSAV, LENWRK, IER COMPLEX C(LDIM,M) REAL WSAVE(LENSAV), WORK(LENWRK) C C Initialize error return C IER = 0 C IF (L .GT. LDIM) THEN IER = 5 CALL XERFFT ('CFFT2B', -2) GO TO 100 ELSEIF (LENSAV .LT. 2*L + INT(LOG(REAL(L))/LOG(2.)) + 1 2*M + INT(LOG(REAL(M))/LOG(2.)) +8) THEN IER = 2 CALL XERFFT ('CFFT2B', 6) GO TO 100 ELSEIF (LENWRK .LT. 2*L*M) THEN IER = 3 CALL XERFFT ('CFFT2B', 8) GO TO 100 ENDIF C C Transform X lines of C array IW = 2*L+INT(LOG(REAL(L))/LOG(2.)) + 3 CALL CFFTMB(L, 1, M, LDIM, C, (L-1) + LDIM*(M-1) +1, 1 WSAVE(IW), 2*M + INT(LOG(REAL(M))/LOG(2.)) + 4, 2 WORK, 2*L*M, IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('CFFT2B',-5) GO TO 100 ENDIF C C Transform Y lines of C array IW = 1 CALL CFFTMB (M, LDIM, L, 1, C, (M-1)*LDIM + L, 1 WSAVE(IW), 2*L + INT(LOG(REAL(L))/LOG(2.)) + 4, 2 WORK, 2*M*L, IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('CFFT2B',-5) ENDIF C 100 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE CFFT2F (LDIM, L, M, C, WSAVE, LENSAV, 1 WORK, LENWRK, IER) INTEGER L, M, LDIM, LENSAV, LENWRK, IER COMPLEX C(LDIM,M) REAL WSAVE(LENSAV), WORK(LENWRK) C C Initialize error return C IER = 0 C IF (L .GT. LDIM) THEN IER = 5 CALL XERFFT ('CFFT2F', -2) GO TO 100 ELSEIF (LENSAV .LT. 2*L + INT(LOG(REAL(L))/LOG(2.)) + 1 2*M + INT(LOG(REAL(M))/LOG(2.)) +8) THEN IER = 2 CALL XERFFT ('CFFT2F', 6) GO TO 100 ELSEIF (LENWRK .LT. 2*L*M) THEN IER = 3 CALL XERFFT ('CFFT2F', 8) GO TO 100 ENDIF C C Transform X lines of C array IW = 2*L+INT(LOG(REAL(L))/LOG(2.)) + 3 CALL CFFTMF(L, 1, M, LDIM, C, (L-1) + LDIM*(M-1) +1, 1 WSAVE(IW), 2*M + INT(LOG(REAL(M))/LOG(2.)) + 4, 2 WORK, 2*L*M, IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('CFFT2F',-5) GO TO 100 ENDIF C C Transform Y lines of C array IW = 1 CALL CFFTMF (M, LDIM, L, 1, C, (M-1)*LDIM + L, 1 WSAVE(IW), 2*L + INT(LOG(REAL(L))/LOG(2.)) + 4, 2 WORK, 2*M*L, IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('CFFT2F',-5) ENDIF C 100 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE CFFT2I (L, M, WSAVE, LENSAV, IER) INTEGER L, M, IER REAL WSAVE(LENSAV) C C Initialize error return C IER = 0 C IF (LENSAV .LT. 2*L + INT(LOG(REAL(L))/LOG(2.)) + 1 2*M + INT(LOG(REAL(M))/LOG(2.)) +8) THEN IER = 2 CALL XERFFT ('CFFT2I', 4) GO TO 100 ENDIF C CALL CFFTMI (L, WSAVE(1), 2*L + INT(LOG(REAL(L))/LOG(2.)) + 4, 1 IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('CFFT2I',-5) GO TO 100 ENDIF CALL CFFTMI (M, WSAVE(2*L+INT(LOG(REAL(L))/LOG(2.)) + 3), 1 2*M + INT(LOG(REAL(M))/LOG(2.)) + 4, IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('CFFT2I',-5) ENDIF C 100 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE CFFTMB (LOT, JUMP, N, INC, C, LENC, WSAVE, LENSAV, 1 WORK, LENWRK, IER) INTEGER LOT, JUMP, N, INC, LENC, LENSAV, LENWRK, IER COMPLEX C(LENC) REAL WSAVE(LENSAV) ,WORK(LENWRK) LOGICAL XERCON C IER = 0 C IF (LENC .LT. (LOT-1)*JUMP + INC*(N-1) + 1) THEN IER = 1 CALL XERFFT ('CFFTMB ', 6) ELSEIF (LENSAV .LT. 2*N + INT(LOG(REAL(N))/LOG(2.)) + 4) THEN IER = 2 CALL XERFFT ('CFFTMB ', 8) ELSEIF (LENWRK .LT. 2*LOT*N) THEN IER = 3 CALL XERFFT ('CFFTMB ', 10) ELSEIF (.NOT. XERCON(INC,JUMP,N,LOT)) THEN IER = 4 CALL XERFFT ('CFFTMB ', -1) ENDIF C IF (N .EQ. 1) RETURN C IW1 = N+N+1 CALL CMFM1B (LOT,JUMP,N,INC,C,WORK,WSAVE,WSAVE(IW1), 1 WSAVE(IW1+1)) RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE CFFTMF (LOT, JUMP, N, INC, C, LENC, WSAVE, LENSAV, 1 WORK, LENWRK, IER) INTEGER LOT, JUMP, N, INC, LENC, LENSAV, LENWRK, IER COMPLEX C(LENC) REAL WSAVE(LENSAV) ,WORK(LENWRK) LOGICAL XERCON C IER = 0 C IF (LENC .LT. (LOT-1)*JUMP + INC*(N-1) + 1) THEN IER = 1 CALL XERFFT ('CFFTMF ', 6) ELSEIF (LENSAV .LT. 2*N + INT(LOG(REAL(N))/LOG(2.)) + 4) THEN IER = 2 CALL XERFFT ('CFFTMF ', 8) ELSEIF (LENWRK .LT. 2*LOT*N) THEN IER = 3 CALL XERFFT ('CFFTMF ', 10) ELSEIF (.NOT. XERCON(INC,JUMP,N,LOT)) THEN IER = 4 CALL XERFFT ('CFFTMF ', -1) ENDIF C IF (N .EQ. 1) RETURN C IW1 = N+N+1 CALL CMFM1F (LOT,JUMP,N,INC,C,WORK,WSAVE,WSAVE(IW1), 1 WSAVE(IW1+1)) RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE CFFTMI (N, WSAVE, LENSAV, IER) INTEGER N, LENSAV, IER REAL WSAVE(LENSAV) C IER = 0 C IF (LENSAV .LT. 2*N + INT(LOG(REAL(N))/LOG(2.)) + 4) THEN IER = 2 CALL XERFFT ('CFFTMI ', 3) ENDIF C IF (N .EQ. 1) RETURN C IW1 = N+N+1 CALL MCFTI1 (N,WSAVE,WSAVE(IW1),WSAVE(IW1+1)) RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE CMF2KB (LOT,IDO,L1,NA,CC,IM1,IN1,CH,IM2,IN2,WA) REAL CC(2,IN1,L1,IDO,2),CH(2,IN2,L1,2,IDO),WA(IDO,1,2) C M1D = (LOT-1)*IM1+1 M2S = 1-IM2 IF (IDO.GT.1 .OR. NA.EQ.1) GO TO 102 DO 101 K=1,L1 DO 101 M1=1,M1D,IM1 CHOLD1 = CC(1,M1,K,1,1)+CC(1,M1,K,1,2) CC(1,M1,K,1,2) = CC(1,M1,K,1,1)-CC(1,M1,K,1,2) CC(1,M1,K,1,1) = CHOLD1 CHOLD2 = CC(2,M1,K,1,1)+CC(2,M1,K,1,2) CC(2,M1,K,1,2) = CC(2,M1,K,1,1)-CC(2,M1,K,1,2) CC(2,M1,K,1,1) = CHOLD2 101 CONTINUE RETURN 102 DO 103 K=1,L1 M2 = M2S DO 103 M1=1,M1D,IM1 M2 = M2+IM2 CH(1,M2,K,1,1) = CC(1,M1,K,1,1)+CC(1,M1,K,1,2) CH(1,M2,K,2,1) = CC(1,M1,K,1,1)-CC(1,M1,K,1,2) CH(2,M2,K,1,1) = CC(2,M1,K,1,1)+CC(2,M1,K,1,2) CH(2,M2,K,2,1) = CC(2,M1,K,1,1)-CC(2,M1,K,1,2) 103 CONTINUE IF(IDO .EQ. 1) RETURN DO 105 I=2,IDO DO 104 K=1,L1 M2 = M2S DO 104 M1=1,M1D,IM1 M2 = M2+IM2 CH(1,M2,K,1,I) = CC(1,M1,K,I,1)+CC(1,M1,K,I,2) TR2 = CC(1,M1,K,I,1)-CC(1,M1,K,I,2) CH(2,M2,K,1,I) = CC(2,M1,K,I,1)+CC(2,M1,K,I,2) TI2 = CC(2,M1,K,I,1)-CC(2,M1,K,I,2) CH(2,M2,K,2,I) = WA(I,1,1)*TI2+WA(I,1,2)*TR2 CH(1,M2,K,2,I) = WA(I,1,1)*TR2-WA(I,1,2)*TI2 104 CONTINUE 105 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE CMF2KF (LOT,IDO,L1,NA,CC,IM1,IN1,CH,IM2,IN2,WA) REAL CC(2,IN1,L1,IDO,2),CH(2,IN2,L1,2,IDO),WA(IDO,1,2) C M1D = (LOT-1)*IM1+1 M2S = 1-IM2 IF (IDO .GT. 1) GO TO 102 SN = 1./REAL(2*L1) IF (NA .EQ. 1) GO TO 106 DO 101 K=1,L1 DO 101 M1=1,M1D,IM1 CHOLD1 = SN*(CC(1,M1,K,1,1)+CC(1,M1,K,1,2)) CC(1,M1,K,1,2) = SN*(CC(1,M1,K,1,1)-CC(1,M1,K,1,2)) CC(1,M1,K,1,1) = CHOLD1 CHOLD2 = SN*(CC(2,M1,K,1,1)+CC(2,M1,K,1,2)) CC(2,M1,K,1,2) = SN*(CC(2,M1,K,1,1)-CC(2,M1,K,1,2)) CC(2,M1,K,1,1) = CHOLD2 101 CONTINUE RETURN 106 DO 107 K=1,L1 M2 = M2S DO 107 M1=1,M1D,IM1 M2 = M2+IM2 CH(1,M2,K,1,1) = SN*(CC(1,M1,K,1,1)+CC(1,M1,K,1,2)) CH(1,M2,K,2,1) = SN*(CC(1,M1,K,1,1)-CC(1,M1,K,1,2)) CH(2,M2,K,1,1) = SN*(CC(2,M1,K,1,1)+CC(2,M1,K,1,2)) CH(2,M2,K,2,1) = SN*(CC(2,M1,K,1,1)-CC(2,M1,K,1,2)) 107 CONTINUE RETURN 102 DO 103 K=1,L1 M2 = M2S DO 103 M1=1,M1D,IM1 M2 = M2+IM2 CH(1,M2,K,1,1) = CC(1,M1,K,1,1)+CC(1,M1,K,1,2) CH(1,M2,K,2,1) = CC(1,M1,K,1,1)-CC(1,M1,K,1,2) CH(2,M2,K,1,1) = CC(2,M1,K,1,1)+CC(2,M1,K,1,2) CH(2,M2,K,2,1) = CC(2,M1,K,1,1)-CC(2,M1,K,1,2) 103 CONTINUE DO 105 I=2,IDO DO 104 K=1,L1 M2 = M2S DO 104 M1=1,M1D,IM1 M2 = M2+IM2 CH(1,M2,K,1,I) = CC(1,M1,K,I,1)+CC(1,M1,K,I,2) TR2 = CC(1,M1,K,I,1)-CC(1,M1,K,I,2) CH(2,M2,K,1,I) = CC(2,M1,K,I,1)+CC(2,M1,K,I,2) TI2 = CC(2,M1,K,I,1)-CC(2,M1,K,I,2) CH(2,M2,K,2,I) = WA(I,1,1)*TI2-WA(I,1,2)*TR2 CH(1,M2,K,2,I) = WA(I,1,1)*TR2+WA(I,1,2)*TI2 104 CONTINUE 105 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE CMF3KB (LOT,IDO,L1,NA,CC,IM1,IN1,CH,IM2,IN2,WA) REAL CC(2,IN1,L1,IDO,3),CH(2,IN2,L1,3,IDO),WA(IDO,2,2) DATA TAUR,TAUI /-.5,.866025403784439/ C M1D = (LOT-1)*IM1+1 M2S = 1-IM2 IF (IDO.GT.1 .OR. NA.EQ.1) GO TO 102 DO 101 K=1,L1 DO 101 M1=1,M1D,IM1 TR2 = CC(1,M1,K,1,2)+CC(1,M1,K,1,3) CR2 = CC(1,M1,K,1,1)+TAUR*TR2 CC(1,M1,K,1,1) = CC(1,M1,K,1,1)+TR2 TI2 = CC(2,M1,K,1,2)+CC(2,M1,K,1,3) CI2 = CC(2,M1,K,1,1)+TAUR*TI2 CC(2,M1,K,1,1) = CC(2,M1,K,1,1)+TI2 CR3 = TAUI*(CC(1,M1,K,1,2)-CC(1,M1,K,1,3)) CI3 = TAUI*(CC(2,M1,K,1,2)-CC(2,M1,K,1,3)) CC(1,M1,K,1,2) = CR2-CI3 CC(1,M1,K,1,3) = CR2+CI3 CC(2,M1,K,1,2) = CI2+CR3 CC(2,M1,K,1,3) = CI2-CR3 101 CONTINUE RETURN 102 DO 103 K=1,L1 M2 = M2S DO 103 M1=1,M1D,IM1 M2 = M2+IM2 TR2 = CC(1,M1,K,1,2)+CC(1,M1,K,1,3) CR2 = CC(1,M1,K,1,1)+TAUR*TR2 CH(1,M2,K,1,1) = CC(1,M1,K,1,1)+TR2 TI2 = CC(2,M1,K,1,2)+CC(2,M1,K,1,3) CI2 = CC(2,M1,K,1,1)+TAUR*TI2 CH(2,M2,K,1,1) = CC(2,M1,K,1,1)+TI2 CR3 = TAUI*(CC(1,M1,K,1,2)-CC(1,M1,K,1,3)) CI3 = TAUI*(CC(2,M1,K,1,2)-CC(2,M1,K,1,3)) CH(1,M2,K,2,1) = CR2-CI3 CH(1,M2,K,3,1) = CR2+CI3 CH(2,M2,K,2,1) = CI2+CR3 CH(2,M2,K,3,1) = CI2-CR3 103 CONTINUE IF (IDO .EQ. 1) RETURN DO 105 I=2,IDO DO 104 K=1,L1 M2 = M2S DO 104 M1=1,M1D,IM1 M2 = M2+IM2 TR2 = CC(1,M1,K,I,2)+CC(1,M1,K,I,3) CR2 = CC(1,M1,K,I,1)+TAUR*TR2 CH(1,M2,K,1,I) = CC(1,M1,K,I,1)+TR2 TI2 = CC(2,M1,K,I,2)+CC(2,M1,K,I,3) CI2 = CC(2,M1,K,I,1)+TAUR*TI2 CH(2,M2,K,1,I) = CC(2,M1,K,I,1)+TI2 CR3 = TAUI*(CC(1,M1,K,I,2)-CC(1,M1,K,I,3)) CI3 = TAUI*(CC(2,M1,K,I,2)-CC(2,M1,K,I,3)) DR2 = CR2-CI3 DR3 = CR2+CI3 DI2 = CI2+CR3 DI3 = CI2-CR3 CH(2,M2,K,2,I) = WA(I,1,1)*DI2+WA(I,1,2)*DR2 CH(1,M2,K,2,I) = WA(I,1,1)*DR2-WA(I,1,2)*DI2 CH(2,M2,K,3,I) = WA(I,2,1)*DI3+WA(I,2,2)*DR3 CH(1,M2,K,3,I) = WA(I,2,1)*DR3-WA(I,2,2)*DI3 104 CONTINUE 105 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE CMF3KF (LOT,IDO,L1,NA,CC,IM1,IN1,CH,IM2,IN2,WA) REAL CC(2,IN1,L1,IDO,3),CH(2,IN2,L1,3,IDO),WA(IDO,2,2) DATA TAUR,TAUI /-.5,-.866025403784439/ C M1D = (LOT-1)*IM1+1 M2S = 1-IM2 IF (IDO .GT. 1) GO TO 102 SN = 1./REAL(3*L1) IF (NA .EQ. 1) GO TO 106 DO 101 K=1,L1 DO 101 M1=1,M1D,IM1 TR2 = CC(1,M1,K,1,2)+CC(1,M1,K,1,3) CR2 = CC(1,M1,K,1,1)+TAUR*TR2 CC(1,M1,K,1,1) = SN*(CC(1,M1,K,1,1)+TR2) TI2 = CC(2,M1,K,1,2)+CC(2,M1,K,1,3) CI2 = CC(2,M1,K,1,1)+TAUR*TI2 CC(2,M1,K,1,1) = SN*(CC(2,M1,K,1,1)+TI2) CR3 = TAUI*(CC(1,M1,K,1,2)-CC(1,M1,K,1,3)) CI3 = TAUI*(CC(2,M1,K,1,2)-CC(2,M1,K,1,3)) CC(1,M1,K,1,2) = SN*(CR2-CI3) CC(1,M1,K,1,3) = SN*(CR2+CI3) CC(2,M1,K,1,2) = SN*(CI2+CR3) CC(2,M1,K,1,3) = SN*(CI2-CR3) 101 CONTINUE RETURN 106 DO 107 K=1,L1 M2 = M2S DO 107 M1=1,M1D,IM1 M2 = M2+IM2 TR2 = CC(1,M1,K,1,2)+CC(1,M1,K,1,3) CR2 = CC(1,M1,K,1,1)+TAUR*TR2 CH(1,M2,K,1,1) = SN*(CC(1,M1,K,1,1)+TR2) TI2 = CC(2,M1,K,1,2)+CC(2,M1,K,1,3) CI2 = CC(2,M1,K,1,1)+TAUR*TI2 CH(2,M2,K,1,1) = SN*(CC(2,M1,K,1,1)+TI2) CR3 = TAUI*(CC(1,M1,K,1,2)-CC(1,M1,K,1,3)) CI3 = TAUI*(CC(2,M1,K,1,2)-CC(2,M1,K,1,3)) CH(1,M2,K,2,1) = SN*(CR2-CI3) CH(1,M2,K,3,1) = SN*(CR2+CI3) CH(2,M2,K,2,1) = SN*(CI2+CR3) CH(2,M2,K,3,1) = SN*(CI2-CR3) 107 CONTINUE RETURN 102 DO 103 K=1,L1 M2 = M2S DO 103 M1=1,M1D,IM1 M2 = M2+IM2 TR2 = CC(1,M1,K,1,2)+CC(1,M1,K,1,3) CR2 = CC(1,M1,K,1,1)+TAUR*TR2 CH(1,M2,K,1,1) = CC(1,M1,K,1,1)+TR2 TI2 = CC(2,M1,K,1,2)+CC(2,M1,K,1,3) CI2 = CC(2,M1,K,1,1)+TAUR*TI2 CH(2,M2,K,1,1) = CC(2,M1,K,1,1)+TI2 CR3 = TAUI*(CC(1,M1,K,1,2)-CC(1,M1,K,1,3)) CI3 = TAUI*(CC(2,M1,K,1,2)-CC(2,M1,K,1,3)) CH(1,M2,K,2,1) = CR2-CI3 CH(1,M2,K,3,1) = CR2+CI3 CH(2,M2,K,2,1) = CI2+CR3 CH(2,M2,K,3,1) = CI2-CR3 103 CONTINUE DO 105 I=2,IDO DO 104 K=1,L1 M2 = M2S DO 104 M1=1,M1D,IM1 M2 = M2+IM2 TR2 = CC(1,M1,K,I,2)+CC(1,M1,K,I,3) CR2 = CC(1,M1,K,I,1)+TAUR*TR2 CH(1,M2,K,1,I) = CC(1,M1,K,I,1)+TR2 TI2 = CC(2,M1,K,I,2)+CC(2,M1,K,I,3) CI2 = CC(2,M1,K,I,1)+TAUR*TI2 CH(2,M2,K,1,I) = CC(2,M1,K,I,1)+TI2 CR3 = TAUI*(CC(1,M1,K,I,2)-CC(1,M1,K,I,3)) CI3 = TAUI*(CC(2,M1,K,I,2)-CC(2,M1,K,I,3)) DR2 = CR2-CI3 DR3 = CR2+CI3 DI2 = CI2+CR3 DI3 = CI2-CR3 CH(2,M2,K,2,I) = WA(I,1,1)*DI2-WA(I,1,2)*DR2 CH(1,M2,K,2,I) = WA(I,1,1)*DR2+WA(I,1,2)*DI2 CH(2,M2,K,3,I) = WA(I,2,1)*DI3-WA(I,2,2)*DR3 CH(1,M2,K,3,I) = WA(I,2,1)*DR3+WA(I,2,2)*DI3 104 CONTINUE 105 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE CMF4KB (LOT,IDO,L1,NA,CC,IM1,IN1,CH,IM2,IN2,WA) REAL CC(2,IN1,L1,IDO,4),CH(2,IN2,L1,4,IDO),WA(IDO,3,2) C C FFTPACK 5.0 auxiliary routine C M1D = (LOT-1)*IM1+1 M2S = 1-IM2 IF (IDO.GT.1 .OR. NA.EQ.1) GO TO 102 DO 101 K=1,L1 DO 101 M1=1,M1D,IM1 TI1 = CC(2,M1,K,1,1)-CC(2,M1,K,1,3) TI2 = CC(2,M1,K,1,1)+CC(2,M1,K,1,3) TR4 = CC(2,M1,K,1,4)-CC(2,M1,K,1,2) TI3 = CC(2,M1,K,1,2)+CC(2,M1,K,1,4) TR1 = CC(1,M1,K,1,1)-CC(1,M1,K,1,3) TR2 = CC(1,M1,K,1,1)+CC(1,M1,K,1,3) TI4 = CC(1,M1,K,1,2)-CC(1,M1,K,1,4) TR3 = CC(1,M1,K,1,2)+CC(1,M1,K,1,4) CC(1,M1,K,1,1) = TR2+TR3 CC(1,M1,K,1,3) = TR2-TR3 CC(2,M1,K,1,1) = TI2+TI3 CC(2,M1,K,1,3) = TI2-TI3 CC(1,M1,K,1,2) = TR1+TR4 CC(1,M1,K,1,4) = TR1-TR4 CC(2,M1,K,1,2) = TI1+TI4 CC(2,M1,K,1,4) = TI1-TI4 101 CONTINUE RETURN 102 DO 103 K=1,L1 M2 = M2S DO 103 M1=1,M1D,IM1 M2 = M2+IM2 TI1 = CC(2,M1,K,1,1)-CC(2,M1,K,1,3) TI2 = CC(2,M1,K,1,1)+CC(2,M1,K,1,3) TR4 = CC(2,M1,K,1,4)-CC(2,M1,K,1,2) TI3 = CC(2,M1,K,1,2)+CC(2,M1,K,1,4) TR1 = CC(1,M1,K,1,1)-CC(1,M1,K,1,3) TR2 = CC(1,M1,K,1,1)+CC(1,M1,K,1,3) TI4 = CC(1,M1,K,1,2)-CC(1,M1,K,1,4) TR3 = CC(1,M1,K,1,2)+CC(1,M1,K,1,4) CH(1,M2,K,1,1) = TR2+TR3 CH(1,M2,K,3,1) = TR2-TR3 CH(2,M2,K,1,1) = TI2+TI3 CH(2,M2,K,3,1) = TI2-TI3 CH(1,M2,K,2,1) = TR1+TR4 CH(1,M2,K,4,1) = TR1-TR4 CH(2,M2,K,2,1) = TI1+TI4 CH(2,M2,K,4,1) = TI1-TI4 103 CONTINUE IF(IDO .EQ. 1) RETURN DO 105 I=2,IDO DO 104 K=1,L1 M2 = M2S DO 104 M1=1,M1D,IM1 M2 = M2+IM2 TI1 = CC(2,M1,K,I,1)-CC(2,M1,K,I,3) TI2 = CC(2,M1,K,I,1)+CC(2,M1,K,I,3) TI3 = CC(2,M1,K,I,2)+CC(2,M1,K,I,4) TR4 = CC(2,M1,K,I,4)-CC(2,M1,K,I,2) TR1 = CC(1,M1,K,I,1)-CC(1,M1,K,I,3) TR2 = CC(1,M1,K,I,1)+CC(1,M1,K,I,3) TI4 = CC(1,M1,K,I,2)-CC(1,M1,K,I,4) TR3 = CC(1,M1,K,I,2)+CC(1,M1,K,I,4) CH(1,M2,K,1,I) = TR2+TR3 CR3 = TR2-TR3 CH(2,M2,K,1,I) = TI2+TI3 CI3 = TI2-TI3 CR2 = TR1+TR4 CR4 = TR1-TR4 CI2 = TI1+TI4 CI4 = TI1-TI4 CH(1,M2,K,2,I) = WA(I,1,1)*CR2-WA(I,1,2)*CI2 CH(2,M2,K,2,I) = WA(I,1,1)*CI2+WA(I,1,2)*CR2 CH(1,M2,K,3,I) = WA(I,2,1)*CR3-WA(I,2,2)*CI3 CH(2,M2,K,3,I) = WA(I,2,1)*CI3+WA(I,2,2)*CR3 CH(1,M2,K,4,I) = WA(I,3,1)*CR4-WA(I,3,2)*CI4 CH(2,M2,K,4,I) = WA(I,3,1)*CI4+WA(I,3,2)*CR4 104 CONTINUE 105 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE CMF4KF (LOT,IDO,L1,NA,CC,IM1,IN1,CH,IM2,IN2,WA) REAL CC(2,IN1,L1,IDO,4),CH(2,IN2,L1,4,IDO),WA(IDO,3,2) C C FFTPACK 5.0 auxiliary routine C M1D = (LOT-1)*IM1+1 M2S = 1-IM2 IF (IDO .GT. 1) GO TO 102 SN = 1./REAL(4*L1) IF (NA .EQ. 1) GO TO 106 DO 101 K=1,L1 DO 101 M1=1,M1D,IM1 TI1 = CC(2,M1,K,1,1)-CC(2,M1,K,1,3) TI2 = CC(2,M1,K,1,1)+CC(2,M1,K,1,3) TR4 = CC(2,M1,K,1,2)-CC(2,M1,K,1,4) TI3 = CC(2,M1,K,1,2)+CC(2,M1,K,1,4) TR1 = CC(1,M1,K,1,1)-CC(1,M1,K,1,3) TR2 = CC(1,M1,K,1,1)+CC(1,M1,K,1,3) TI4 = CC(1,M1,K,1,4)-CC(1,M1,K,1,2) TR3 = CC(1,M1,K,1,2)+CC(1,M1,K,1,4) CC(1,M1,K,1,1) = SN*(TR2+TR3) CC(1,M1,K,1,3) = SN*(TR2-TR3) CC(2,M1,K,1,1) = SN*(TI2+TI3) CC(2,M1,K,1,3) = SN*(TI2-TI3) CC(1,M1,K,1,2) = SN*(TR1+TR4) CC(1,M1,K,1,4) = SN*(TR1-TR4) CC(2,M1,K,1,2) = SN*(TI1+TI4) CC(2,M1,K,1,4) = SN*(TI1-TI4) 101 CONTINUE RETURN 106 DO 107 K=1,L1 M2 = M2S DO 107 M1=1,M1D,IM1 M2 = M2+IM2 TI1 = CC(2,M1,K,1,1)-CC(2,M1,K,1,3) TI2 = CC(2,M1,K,1,1)+CC(2,M1,K,1,3) TR4 = CC(2,M1,K,1,2)-CC(2,M1,K,1,4) TI3 = CC(2,M1,K,1,2)+CC(2,M1,K,1,4) TR1 = CC(1,M1,K,1,1)-CC(1,M1,K,1,3) TR2 = CC(1,M1,K,1,1)+CC(1,M1,K,1,3) TI4 = CC(1,M1,K,1,4)-CC(1,M1,K,1,2) TR3 = CC(1,M1,K,1,2)+CC(1,M1,K,1,4) CH(1,M2,K,1,1) = SN*(TR2+TR3) CH(1,M2,K,3,1) = SN*(TR2-TR3) CH(2,M2,K,1,1) = SN*(TI2+TI3) CH(2,M2,K,3,1) = SN*(TI2-TI3) CH(1,M2,K,2,1) = SN*(TR1+TR4) CH(1,M2,K,4,1) = SN*(TR1-TR4) CH(2,M2,K,2,1) = SN*(TI1+TI4) CH(2,M2,K,4,1) = SN*(TI1-TI4) 107 CONTINUE RETURN 102 DO 103 K=1,L1 M2 = M2S DO 103 M1=1,M1D,IM1 M2 = M2+IM2 TI1 = CC(2,M1,K,1,1)-CC(2,M1,K,1,3) TI2 = CC(2,M1,K,1,1)+CC(2,M1,K,1,3) TR4 = CC(2,M1,K,1,2)-CC(2,M1,K,1,4) TI3 = CC(2,M1,K,1,2)+CC(2,M1,K,1,4) TR1 = CC(1,M1,K,1,1)-CC(1,M1,K,1,3) TR2 = CC(1,M1,K,1,1)+CC(1,M1,K,1,3) TI4 = CC(1,M1,K,1,4)-CC(1,M1,K,1,2) TR3 = CC(1,M1,K,1,2)+CC(1,M1,K,1,4) CH(1,M2,K,1,1) = TR2+TR3 CH(1,M2,K,3,1) = TR2-TR3 CH(2,M2,K,1,1) = TI2+TI3 CH(2,M2,K,3,1) = TI2-TI3 CH(1,M2,K,2,1) = TR1+TR4 CH(1,M2,K,4,1) = TR1-TR4 CH(2,M2,K,2,1) = TI1+TI4 CH(2,M2,K,4,1) = TI1-TI4 103 CONTINUE DO 105 I=2,IDO DO 104 K=1,L1 M2 = M2S DO 104 M1=1,M1D,IM1 M2 = M2+IM2 TI1 = CC(2,M1,K,I,1)-CC(2,M1,K,I,3) TI2 = CC(2,M1,K,I,1)+CC(2,M1,K,I,3) TI3 = CC(2,M1,K,I,2)+CC(2,M1,K,I,4) TR4 = CC(2,M1,K,I,2)-CC(2,M1,K,I,4) TR1 = CC(1,M1,K,I,1)-CC(1,M1,K,I,3) TR2 = CC(1,M1,K,I,1)+CC(1,M1,K,I,3) TI4 = CC(1,M1,K,I,4)-CC(1,M1,K,I,2) TR3 = CC(1,M1,K,I,2)+CC(1,M1,K,I,4) CH(1,M2,K,1,I) = TR2+TR3 CR3 = TR2-TR3 CH(2,M2,K,1,I) = TI2+TI3 CI3 = TI2-TI3 CR2 = TR1+TR4 CR4 = TR1-TR4 CI2 = TI1+TI4 CI4 = TI1-TI4 CH(1,M2,K,2,I) = WA(I,1,1)*CR2+WA(I,1,2)*CI2 CH(2,M2,K,2,I) = WA(I,1,1)*CI2-WA(I,1,2)*CR2 CH(1,M2,K,3,I) = WA(I,2,1)*CR3+WA(I,2,2)*CI3 CH(2,M2,K,3,I) = WA(I,2,1)*CI3-WA(I,2,2)*CR3 CH(1,M2,K,4,I) = WA(I,3,1)*CR4+WA(I,3,2)*CI4 CH(2,M2,K,4,I) = WA(I,3,1)*CI4-WA(I,3,2)*CR4 104 CONTINUE 105 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE CMF5KB (LOT,IDO,L1,NA,CC,IM1,IN1,CH,IM2,IN2,WA) REAL CC(2,IN1,L1,IDO,5),CH(2,IN2,L1,5,IDO),WA(IDO,4,2) DATA TR11,TI11,TR12,TI12 /.3090169943749474,.9510565162951536, 1-.8090169943749474,.5877852522924731/ C C FFTPACK 5.0 auxiliary routine C M1D = (LOT-1)*IM1+1 M2S = 1-IM2 IF (IDO.GT.1 .OR. NA.EQ.1) GO TO 102 DO 101 K=1,L1 DO 101 M1=1,M1D,IM1 TI5 = CC(2,M1,K,1,2)-CC(2,M1,K,1,5) TI2 = CC(2,M1,K,1,2)+CC(2,M1,K,1,5) TI4 = CC(2,M1,K,1,3)-CC(2,M1,K,1,4) TI3 = CC(2,M1,K,1,3)+CC(2,M1,K,1,4) TR5 = CC(1,M1,K,1,2)-CC(1,M1,K,1,5) TR2 = CC(1,M1,K,1,2)+CC(1,M1,K,1,5) TR4 = CC(1,M1,K,1,3)-CC(1,M1,K,1,4) TR3 = CC(1,M1,K,1,3)+CC(1,M1,K,1,4) CHOLD1 = CC(1,M1,K,1,1)+TR2+TR3 CHOLD2 = CC(2,M1,K,1,1)+TI2+TI3 CR2 = CC(1,M1,K,1,1)+TR11*TR2+TR12*TR3 CI2 = CC(2,M1,K,1,1)+TR11*TI2+TR12*TI3 CR3 = CC(1,M1,K,1,1)+TR12*TR2+TR11*TR3 CI3 = CC(2,M1,K,1,1)+TR12*TI2+TR11*TI3 CC(1,M1,K,1,1) = CHOLD1 CC(2,M1,K,1,1) = CHOLD2 CR5 = TI11*TR5+TI12*TR4 CI5 = TI11*TI5+TI12*TI4 CR4 = TI12*TR5-TI11*TR4 CI4 = TI12*TI5-TI11*TI4 CC(1,M1,K,1,2) = CR2-CI5 CC(1,M1,K,1,5) = CR2+CI5 CC(2,M1,K,1,2) = CI2+CR5 CC(2,M1,K,1,3) = CI3+CR4 CC(1,M1,K,1,3) = CR3-CI4 CC(1,M1,K,1,4) = CR3+CI4 CC(2,M1,K,1,4) = CI3-CR4 CC(2,M1,K,1,5) = CI2-CR5 101 CONTINUE RETURN 102 DO 103 K=1,L1 M2 = M2S DO 103 M1=1,M1D,IM1 M2 = M2+IM2 TI5 = CC(2,M1,K,1,2)-CC(2,M1,K,1,5) TI2 = CC(2,M1,K,1,2)+CC(2,M1,K,1,5) TI4 = CC(2,M1,K,1,3)-CC(2,M1,K,1,4) TI3 = CC(2,M1,K,1,3)+CC(2,M1,K,1,4) TR5 = CC(1,M1,K,1,2)-CC(1,M1,K,1,5) TR2 = CC(1,M1,K,1,2)+CC(1,M1,K,1,5) TR4 = CC(1,M1,K,1,3)-CC(1,M1,K,1,4) TR3 = CC(1,M1,K,1,3)+CC(1,M1,K,1,4) CH(1,M2,K,1,1) = CC(1,M1,K,1,1)+TR2+TR3 CH(2,M2,K,1,1) = CC(2,M1,K,1,1)+TI2+TI3 CR2 = CC(1,M1,K,1,1)+TR11*TR2+TR12*TR3 CI2 = CC(2,M1,K,1,1)+TR11*TI2+TR12*TI3 CR3 = CC(1,M1,K,1,1)+TR12*TR2+TR11*TR3 CI3 = CC(2,M1,K,1,1)+TR12*TI2+TR11*TI3 CR5 = TI11*TR5+TI12*TR4 CI5 = TI11*TI5+TI12*TI4 CR4 = TI12*TR5-TI11*TR4 CI4 = TI12*TI5-TI11*TI4 CH(1,M2,K,2,1) = CR2-CI5 CH(1,M2,K,5,1) = CR2+CI5 CH(2,M2,K,2,1) = CI2+CR5 CH(2,M2,K,3,1) = CI3+CR4 CH(1,M2,K,3,1) = CR3-CI4 CH(1,M2,K,4,1) = CR3+CI4 CH(2,M2,K,4,1) = CI3-CR4 CH(2,M2,K,5,1) = CI2-CR5 103 CONTINUE IF(IDO .EQ. 1) RETURN DO 105 I=2,IDO DO 104 K=1,L1 M2 = M2S DO 104 M1=1,M1D,IM1 M2 = M2+IM2 TI5 = CC(2,M1,K,I,2)-CC(2,M1,K,I,5) TI2 = CC(2,M1,K,I,2)+CC(2,M1,K,I,5) TI4 = CC(2,M1,K,I,3)-CC(2,M1,K,I,4) TI3 = CC(2,M1,K,I,3)+CC(2,M1,K,I,4) TR5 = CC(1,M1,K,I,2)-CC(1,M1,K,I,5) TR2 = CC(1,M1,K,I,2)+CC(1,M1,K,I,5) TR4 = CC(1,M1,K,I,3)-CC(1,M1,K,I,4) TR3 = CC(1,M1,K,I,3)+CC(1,M1,K,I,4) CH(1,M2,K,1,I) = CC(1,M1,K,I,1)+TR2+TR3 CH(2,M2,K,1,I) = CC(2,M1,K,I,1)+TI2+TI3 CR2 = CC(1,M1,K,I,1)+TR11*TR2+TR12*TR3 CI2 = CC(2,M1,K,I,1)+TR11*TI2+TR12*TI3 CR3 = CC(1,M1,K,I,1)+TR12*TR2+TR11*TR3 CI3 = CC(2,M1,K,I,1)+TR12*TI2+TR11*TI3 CR5 = TI11*TR5+TI12*TR4 CI5 = TI11*TI5+TI12*TI4 CR4 = TI12*TR5-TI11*TR4 CI4 = TI12*TI5-TI11*TI4 DR3 = CR3-CI4 DR4 = CR3+CI4 DI3 = CI3+CR4 DI4 = CI3-CR4 DR5 = CR2+CI5 DR2 = CR2-CI5 DI5 = CI2-CR5 DI2 = CI2+CR5 CH(1,M2,K,2,I) = WA(I,1,1)*DR2-WA(I,1,2)*DI2 CH(2,M2,K,2,I) = WA(I,1,1)*DI2+WA(I,1,2)*DR2 CH(1,M2,K,3,I) = WA(I,2,1)*DR3-WA(I,2,2)*DI3 CH(2,M2,K,3,I) = WA(I,2,1)*DI3+WA(I,2,2)*DR3 CH(1,M2,K,4,I) = WA(I,3,1)*DR4-WA(I,3,2)*DI4 CH(2,M2,K,4,I) = WA(I,3,1)*DI4+WA(I,3,2)*DR4 CH(1,M2,K,5,I) = WA(I,4,1)*DR5-WA(I,4,2)*DI5 CH(2,M2,K,5,I) = WA(I,4,1)*DI5+WA(I,4,2)*DR5 104 CONTINUE 105 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C SUBROUTINE CMF5KF (LOT,IDO,L1,NA,CC,IM1,IN1,CH,IM2,IN2,WA) REAL CC(2,IN1,L1,IDO,5),CH(2,IN2,L1,5,IDO),WA(IDO,4,2) DATA TR11,TI11,TR12,TI12 /.3090169943749474,-.9510565162951536, 1-.8090169943749474,-.5877852522924731/ C C FFTPACK 5.0 auxiliary routine C M1D = (LOT-1)*IM1+1 M2S = 1-IM2 IF (IDO .GT. 1) GO TO 102 SN = 1./REAL(5*L1) IF (NA .EQ. 1) GO TO 106 DO 101 K=1,L1 DO 101 M1=1,M1D,IM1 TI5 = CC(2,M1,K,1,2)-CC(2,M1,K,1,5) TI2 = CC(2,M1,K,1,2)+CC(2,M1,K,1,5) TI4 = CC(2,M1,K,1,3)-CC(2,M1,K,1,4) TI3 = CC(2,M1,K,1,3)+CC(2,M1,K,1,4) TR5 = CC(1,M1,K,1,2)-CC(1,M1,K,1,5) TR2 = CC(1,M1,K,1,2)+CC(1,M1,K,1,5) TR4 = CC(1,M1,K,1,3)-CC(1,M1,K,1,4) TR3 = CC(1,M1,K,1,3)+CC(1,M1,K,1,4) CHOLD1 = SN*(CC(1,M1,K,1,1)+TR2+TR3) CHOLD2 = SN*(CC(2,M1,K,1,1)+TI2+TI3) CR2 = CC(1,M1,K,1,1)+TR11*TR2+TR12*TR3 CI2 = CC(2,M1,K,1,1)+TR11*TI2+TR12*TI3 CR3 = CC(1,M1,K,1,1)+TR12*TR2+TR11*TR3 CI3 = CC(2,M1,K,1,1)+TR12*TI2+TR11*TI3 CC(1,M1,K,1,1) = CHOLD1 CC(2,M1,K,1,1) = CHOLD2 CR5 = TI11*TR5+TI12*TR4 CI5 = TI11*TI5+TI12*TI4 CR4 = TI12*TR5-TI11*TR4 CI4 = TI12*TI5-TI11*TI4 CC(1,M1,K,1,2) = SN*(CR2-CI5) CC(1,M1,K,1,5) = SN*(CR2+CI5) CC(2,M1,K,1,2) = SN*(CI2+CR5) CC(2,M1,K,1,3) = SN*(CI3+CR4) CC(1,M1,K,1,3) = SN*(CR3-CI4) CC(1,M1,K,1,4) = SN*(CR3+CI4) CC(2,M1,K,1,4) = SN*(CI3-CR4) CC(2,M1,K,1,5) = SN*(CI2-CR5) 101 CONTINUE RETURN 106 DO 107 K=1,L1 M2 = M2S DO 107 M1=1,M1D,IM1 M2 = M2+IM2 TI5 = CC(2,M1,K,1,2)-CC(2,M1,K,1,5) TI2 = CC(2,M1,K,1,2)+CC(2,M1,K,1,5) TI4 = CC(2,M1,K,1,3)-CC(2,M1,K,1,4) TI3 = CC(2,M1,K,1,3)+CC(2,M1,K,1,4) TR5 = CC(1,M1,K,1,2)-CC(1,M1,K,1,5) TR2 = CC(1,M1,K,1,2)+CC(1,M1,K,1,5) TR4 = CC(1,M1,K,1,3)-CC(1,M1,K,1,4) TR3 = CC(1,M1,K,1,3)+CC(1,M1,K,1,4) CH(1,M2,K,1,1) = SN*(CC(1,M1,K,1,1)+TR2+TR3) CH(2,M2,K,1,1) = SN*(CC(2,M1,K,1,1)+TI2+TI3) CR2 = CC(1,M1,K,1,1)+TR11*TR2+TR12*TR3 CI2 = CC(2,M1,K,1,1)+TR11*TI2+TR12*TI3 CR3 = CC(1,M1,K,1,1)+TR12*TR2+TR11*TR3 CI3 = CC(2,M1,K,1,1)+TR12*TI2+TR11*TI3 CR5 = TI11*TR5+TI12*TR4 CI5 = TI11*TI5+TI12*TI4 CR4 = TI12*TR5-TI11*TR4 CI4 = TI12*TI5-TI11*TI4 CH(1,M2,K,2,1) = SN*(CR2-CI5) CH(1,M2,K,5,1) = SN*(CR2+CI5) CH(2,M2,K,2,1) = SN*(CI2+CR5) CH(2,M2,K,3,1) = SN*(CI3+CR4) CH(1,M2,K,3,1) = SN*(CR3-CI4) CH(1,M2,K,4,1) = SN*(CR3+CI4) CH(2,M2,K,4,1) = SN*(CI3-CR4) CH(2,M2,K,5,1) = SN*(CI2-CR5) 107 CONTINUE RETURN 102 DO 103 K=1,L1 M2 = M2S DO 103 M1=1,M1D,IM1 M2 = M2+IM2 TI5 = CC(2,M1,K,1,2)-CC(2,M1,K,1,5) TI2 = CC(2,M1,K,1,2)+CC(2,M1,K,1,5) TI4 = CC(2,M1,K,1,3)-CC(2,M1,K,1,4) TI3 = CC(2,M1,K,1,3)+CC(2,M1,K,1,4) TR5 = CC(1,M1,K,1,2)-CC(1,M1,K,1,5) TR2 = CC(1,M1,K,1,2)+CC(1,M1,K,1,5) TR4 = CC(1,M1,K,1,3)-CC(1,M1,K,1,4) TR3 = CC(1,M1,K,1,3)+CC(1,M1,K,1,4) CH(1,M2,K,1,1) = CC(1,M1,K,1,1)+TR2+TR3 CH(2,M2,K,1,1) = CC(2,M1,K,1,1)+TI2+TI3 CR2 = CC(1,M1,K,1,1)+TR11*TR2+TR12*TR3 CI2 = CC(2,M1,K,1,1)+TR11*TI2+TR12*TI3 CR3 = CC(1,M1,K,1,1)+TR12*TR2+TR11*TR3 CI3 = CC(2,M1,K,1,1)+TR12*TI2+TR11*TI3 CR5 = TI11*TR5+TI12*TR4 CI5 = TI11*TI5+TI12*TI4 CR4 = TI12*TR5-TI11*TR4 CI4 = TI12*TI5-TI11*TI4 CH(1,M2,K,2,1) = CR2-CI5 CH(1,M2,K,5,1) = CR2+CI5 CH(2,M2,K,2,1) = CI2+CR5 CH(2,M2,K,3,1) = CI3+CR4 CH(1,M2,K,3,1) = CR3-CI4 CH(1,M2,K,4,1) = CR3+CI4 CH(2,M2,K,4,1) = CI3-CR4 CH(2,M2,K,5,1) = CI2-CR5 103 CONTINUE DO 105 I=2,IDO DO 104 K=1,L1 M2 = M2S DO 104 M1=1,M1D,IM1 M2 = M2+IM2 TI5 = CC(2,M1,K,I,2)-CC(2,M1,K,I,5) TI2 = CC(2,M1,K,I,2)+CC(2,M1,K,I,5) TI4 = CC(2,M1,K,I,3)-CC(2,M1,K,I,4) TI3 = CC(2,M1,K,I,3)+CC(2,M1,K,I,4) TR5 = CC(1,M1,K,I,2)-CC(1,M1,K,I,5) TR2 = CC(1,M1,K,I,2)+CC(1,M1,K,I,5) TR4 = CC(1,M1,K,I,3)-CC(1,M1,K,I,4) TR3 = CC(1,M1,K,I,3)+CC(1,M1,K,I,4) CH(1,M2,K,1,I) = CC(1,M1,K,I,1)+TR2+TR3 CH(2,M2,K,1,I) = CC(2,M1,K,I,1)+TI2+TI3 CR2 = CC(1,M1,K,I,1)+TR11*TR2+TR12*TR3 CI2 = CC(2,M1,K,I,1)+TR11*TI2+TR12*TI3 CR3 = CC(1,M1,K,I,1)+TR12*TR2+TR11*TR3 CI3 = CC(2,M1,K,I,1)+TR12*TI2+TR11*TI3 CR5 = TI11*TR5+TI12*TR4 CI5 = TI11*TI5+TI12*TI4 CR4 = TI12*TR5-TI11*TR4 CI4 = TI12*TI5-TI11*TI4 DR3 = CR3-CI4 DR4 = CR3+CI4 DI3 = CI3+CR4 DI4 = CI3-CR4 DR5 = CR2+CI5 DR2 = CR2-CI5 DI5 = CI2-CR5 DI2 = CI2+CR5 CH(1,M2,K,2,I) = WA(I,1,1)*DR2+WA(I,1,2)*DI2 CH(2,M2,K,2,I) = WA(I,1,1)*DI2-WA(I,1,2)*DR2 CH(1,M2,K,3,I) = WA(I,2,1)*DR3+WA(I,2,2)*DI3 CH(2,M2,K,3,I) = WA(I,2,1)*DI3-WA(I,2,2)*DR3 CH(1,M2,K,4,I) = WA(I,3,1)*DR4+WA(I,3,2)*DI4 CH(2,M2,K,4,I) = WA(I,3,1)*DI4-WA(I,3,2)*DR4 CH(1,M2,K,5,I) = WA(I,4,1)*DR5+WA(I,4,2)*DI5 CH(2,M2,K,5,I) = WA(I,4,1)*DI5-WA(I,4,2)*DR5 104 CONTINUE 105 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE CMFGKB (LOT,IDO,IP,L1,LID,NA,CC,CC1,IM1,IN1, 1 CH,CH1,IM2,IN2,WA) REAL CH(2,IN2,L1,IDO,IP) ,CC(2,IN1,L1,IP,IDO), 1 CC1(2,IN1,LID,IP) ,CH1(2,IN2,LID,IP) , 2 WA(IDO,IP-1,2) C C FFTPACK 5.0 auxiliary routine C M1D = (LOT-1)*IM1+1 M2S = 1-IM2 IPP2 = IP+2 IPPH = (IP+1)/2 DO 110 KI=1,LID M2 = M2S DO 110 M1=1,M1D,IM1 M2 = M2+IM2 CH1(1,M2,KI,1) = CC1(1,M1,KI,1) CH1(2,M2,KI,1) = CC1(2,M1,KI,1) 110 CONTINUE DO 111 J=2,IPPH JC = IPP2-J DO 112 KI=1,LID M2 = M2S DO 112 M1=1,M1D,IM1 M2 = M2+IM2 CH1(1,M2,KI,J) = CC1(1,M1,KI,J)+CC1(1,M1,KI,JC) CH1(1,M2,KI,JC) = CC1(1,M1,KI,J)-CC1(1,M1,KI,JC) CH1(2,M2,KI,J) = CC1(2,M1,KI,J)+CC1(2,M1,KI,JC) CH1(2,M2,KI,JC) = CC1(2,M1,KI,J)-CC1(2,M1,KI,JC) 112 CONTINUE 111 CONTINUE DO 118 J=2,IPPH DO 117 KI=1,LID M2 = M2S DO 117 M1=1,M1D,IM1 M2 = M2+IM2 CC1(1,M1,KI,1) = CC1(1,M1,KI,1)+CH1(1,M2,KI,J) CC1(2,M1,KI,1) = CC1(2,M1,KI,1)+CH1(2,M2,KI,J) 117 CONTINUE 118 CONTINUE DO 116 L=2,IPPH LC = IPP2-L DO 113 KI=1,LID M2 = M2S DO 113 M1=1,M1D,IM1 M2 = M2+IM2 CC1(1,M1,KI,L) = CH1(1,M2,KI,1)+WA(1,L-1,1)*CH1(1,M2,KI,2) CC1(1,M1,KI,LC) = WA(1,L-1,2)*CH1(1,M2,KI,IP) CC1(2,M1,KI,L) = CH1(2,M2,KI,1)+WA(1,L-1,1)*CH1(2,M2,KI,2) CC1(2,M1,KI,LC) = WA(1,L-1,2)*CH1(2,M2,KI,IP) 113 CONTINUE DO 115 J=3,IPPH JC = IPP2-J IDLJ = MOD((L-1)*(J-1),IP) WAR = WA(1,IDLJ,1) WAI = WA(1,IDLJ,2) DO 114 KI=1,LID M2 = M2S DO 114 M1=1,M1D,IM1 M2 = M2+IM2 CC1(1,M1,KI,L) = CC1(1,M1,KI,L)+WAR*CH1(1,M2,KI,J) CC1(1,M1,KI,LC) = CC1(1,M1,KI,LC)+WAI*CH1(1,M2,KI,JC) CC1(2,M1,KI,L) = CC1(2,M1,KI,L)+WAR*CH1(2,M2,KI,J) CC1(2,M1,KI,LC) = CC1(2,M1,KI,LC)+WAI*CH1(2,M2,KI,JC) 114 CONTINUE 115 CONTINUE 116 CONTINUE IF(IDO.GT.1 .OR. NA.EQ.1) GO TO 136 DO 120 J=2,IPPH JC = IPP2-J DO 119 KI=1,LID DO 119 M1=1,M1D,IM1 CHOLD1 = CC1(1,M1,KI,J)-CC1(2,M1,KI,JC) CHOLD2 = CC1(1,M1,KI,J)+CC1(2,M1,KI,JC) CC1(1,M1,KI,J) = CHOLD1 CC1(2,M1,KI,JC) = CC1(2,M1,KI,J)-CC1(1,M1,KI,JC) CC1(2,M1,KI,J) = CC1(2,M1,KI,J)+CC1(1,M1,KI,JC) CC1(1,M1,KI,JC) = CHOLD2 119 CONTINUE 120 CONTINUE RETURN 136 DO 137 KI=1,LID M2 = M2S DO 137 M1=1,M1D,IM1 M2 = M2+IM2 CH1(1,M2,KI,1) = CC1(1,M1,KI,1) CH1(2,M2,KI,1) = CC1(2,M1,KI,1) 137 CONTINUE DO 135 J=2,IPPH JC = IPP2-J DO 134 KI=1,LID M2 = M2S DO 134 M1=1,M1D,IM1 M2 = M2+IM2 CH1(1,M2,KI,J) = CC1(1,M1,KI,J)-CC1(2,M1,KI,JC) CH1(1,M2,KI,JC) = CC1(1,M1,KI,J)+CC1(2,M1,KI,JC) CH1(2,M2,KI,JC) = CC1(2,M1,KI,J)-CC1(1,M1,KI,JC) CH1(2,M2,KI,J) = CC1(2,M1,KI,J)+CC1(1,M1,KI,JC) 134 CONTINUE 135 CONTINUE IF (IDO .EQ. 1) RETURN DO 131 I=1,IDO DO 130 K=1,L1 M2 = M2S DO 130 M1=1,M1D,IM1 M2 = M2+IM2 CC(1,M1,K,1,I) = CH(1,M2,K,I,1) CC(2,M1,K,1,I) = CH(2,M2,K,I,1) 130 CONTINUE 131 CONTINUE DO 123 J=2,IP DO 122 K=1,L1 M2 = M2S DO 122 M1=1,M1D,IM1 M2 = M2+IM2 CC(1,M1,K,J,1) = CH(1,M2,K,1,J) CC(2,M1,K,J,1) = CH(2,M2,K,1,J) 122 CONTINUE 123 CONTINUE DO 126 J=2,IP DO 125 I=2,IDO DO 124 K=1,L1 M2 = M2S DO 124 M1=1,M1D,IM1 M2 = M2+IM2 CC(1,M1,K,J,I) = WA(I,J-1,1)*CH(1,M2,K,I,J) 1 -WA(I,J-1,2)*CH(2,M2,K,I,J) CC(2,M1,K,J,I) = WA(I,J-1,1)*CH(2,M2,K,I,J) 1 +WA(I,J-1,2)*CH(1,M2,K,I,J) 124 CONTINUE 125 CONTINUE 126 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE CMFGKF (LOT,IDO,IP,L1,LID,NA,CC,CC1,IM1,IN1, 1 CH,CH1,IM2,IN2,WA) REAL CH(2,IN2,L1,IDO,IP) ,CC(2,IN1,L1,IP,IDO), 1 CC1(2,IN1,LID,IP) ,CH1(2,IN2,LID,IP) , 2 WA(IDO,IP-1,2) C C FFTPACK 5.0 auxiliary routine C M1D = (LOT-1)*IM1+1 M2S = 1-IM2 IPP2 = IP+2 IPPH = (IP+1)/2 DO 110 KI=1,LID M2 = M2S DO 110 M1=1,M1D,IM1 M2 = M2+IM2 CH1(1,M2,KI,1) = CC1(1,M1,KI,1) CH1(2,M2,KI,1) = CC1(2,M1,KI,1) 110 CONTINUE DO 111 J=2,IPPH JC = IPP2-J DO 112 KI=1,LID M2 = M2S DO 112 M1=1,M1D,IM1 M2 = M2+IM2 CH1(1,M2,KI,J) = CC1(1,M1,KI,J)+CC1(1,M1,KI,JC) CH1(1,M2,KI,JC) = CC1(1,M1,KI,J)-CC1(1,M1,KI,JC) CH1(2,M2,KI,J) = CC1(2,M1,KI,J)+CC1(2,M1,KI,JC) CH1(2,M2,KI,JC) = CC1(2,M1,KI,J)-CC1(2,M1,KI,JC) 112 CONTINUE 111 CONTINUE DO 118 J=2,IPPH DO 117 KI=1,LID M2 = M2S DO 117 M1=1,M1D,IM1 M2 = M2+IM2 CC1(1,M1,KI,1) = CC1(1,M1,KI,1)+CH1(1,M2,KI,J) CC1(2,M1,KI,1) = CC1(2,M1,KI,1)+CH1(2,M2,KI,J) 117 CONTINUE 118 CONTINUE DO 116 L=2,IPPH LC = IPP2-L DO 113 KI=1,LID M2 = M2S DO 113 M1=1,M1D,IM1 M2 = M2+IM2 CC1(1,M1,KI,L) = CH1(1,M2,KI,1)+WA(1,L-1,1)*CH1(1,M2,KI,2) CC1(1,M1,KI,LC) = -WA(1,L-1,2)*CH1(1,M2,KI,IP) CC1(2,M1,KI,L) = CH1(2,M2,KI,1)+WA(1,L-1,1)*CH1(2,M2,KI,2) CC1(2,M1,KI,LC) = -WA(1,L-1,2)*CH1(2,M2,KI,IP) 113 CONTINUE DO 115 J=3,IPPH JC = IPP2-J IDLJ = MOD((L-1)*(J-1),IP) WAR = WA(1,IDLJ,1) WAI = -WA(1,IDLJ,2) DO 114 KI=1,LID M2 = M2S DO 114 M1=1,M1D,IM1 M2 = M2+IM2 CC1(1,M1,KI,L) = CC1(1,M1,KI,L)+WAR*CH1(1,M2,KI,J) CC1(1,M1,KI,LC) = CC1(1,M1,KI,LC)+WAI*CH1(1,M2,KI,JC) CC1(2,M1,KI,L) = CC1(2,M1,KI,L)+WAR*CH1(2,M2,KI,J) CC1(2,M1,KI,LC) = CC1(2,M1,KI,LC)+WAI*CH1(2,M2,KI,JC) 114 CONTINUE 115 CONTINUE 116 CONTINUE IF (IDO .GT. 1) GO TO 136 SN = 1./REAL(IP*L1) IF (NA .EQ. 1) GO TO 146 DO 149 KI=1,LID M2 = M2S DO 149 M1=1,M1D,IM1 M2 = M2+IM2 CC1(1,M1,KI,1) = SN*CC1(1,M1,KI,1) CC1(2,M1,KI,1) = SN*CC1(2,M1,KI,1) 149 CONTINUE DO 120 J=2,IPPH JC = IPP2-J DO 119 KI=1,LID DO 119 M1=1,M1D,IM1 CHOLD1 = SN*(CC1(1,M1,KI,J)-CC1(2,M1,KI,JC)) CHOLD2 = SN*(CC1(1,M1,KI,J)+CC1(2,M1,KI,JC)) CC1(1,M1,KI,J) = CHOLD1 CC1(2,M1,KI,JC) = SN*(CC1(2,M1,KI,J)-CC1(1,M1,KI,JC)) CC1(2,M1,KI,J) = SN*(CC1(2,M1,KI,J)+CC1(1,M1,KI,JC)) CC1(1,M1,KI,JC) = CHOLD2 119 CONTINUE 120 CONTINUE RETURN 146 DO 147 KI=1,LID M2 = M2S DO 147 M1=1,M1D,IM1 M2 = M2+IM2 CH1(1,M2,KI,1) = SN*CC1(1,M1,KI,1) CH1(2,M2,KI,1) = SN*CC1(2,M1,KI,1) 147 CONTINUE DO 145 J=2,IPPH JC = IPP2-J DO 144 KI=1,LID M2 = M2S DO 144 M1=1,M1D,IM1 M2 = M2+IM2 CH1(1,M2,KI,J) = SN*(CC1(1,M1,KI,J)-CC1(2,M1,KI,JC)) CH1(2,M2,KI,J) = SN*(CC1(2,M1,KI,J)+CC1(1,M1,KI,JC)) CH1(1,M2,KI,JC) = SN*(CC1(1,M1,KI,J)+CC1(2,M1,KI,JC)) CH1(2,M2,KI,JC) = SN*(CC1(2,M1,KI,J)-CC1(1,M1,KI,JC)) 144 CONTINUE 145 CONTINUE RETURN 136 DO 137 KI=1,LID M2 = M2S DO 137 M1=1,M1D,IM1 M2 = M2+IM2 CH1(1,M2,KI,1) = CC1(1,M1,KI,1) CH1(2,M2,KI,1) = CC1(2,M1,KI,1) 137 CONTINUE DO 135 J=2,IPPH JC = IPP2-J DO 134 KI=1,LID M2 = M2S DO 134 M1=1,M1D,IM1 M2 = M2+IM2 CH1(1,M2,KI,J) = CC1(1,M1,KI,J)-CC1(2,M1,KI,JC) CH1(2,M2,KI,J) = CC1(2,M1,KI,J)+CC1(1,M1,KI,JC) CH1(1,M2,KI,JC) = CC1(1,M1,KI,J)+CC1(2,M1,KI,JC) CH1(2,M2,KI,JC) = CC1(2,M1,KI,J)-CC1(1,M1,KI,JC) 134 CONTINUE 135 CONTINUE DO 131 I=1,IDO DO 130 K=1,L1 M2 = M2S DO 130 M1=1,M1D,IM1 M2 = M2+IM2 CC(1,M1,K,1,I) = CH(1,M2,K,I,1) CC(2,M1,K,1,I) = CH(2,M2,K,I,1) 130 CONTINUE 131 CONTINUE DO 123 J=2,IP DO 122 K=1,L1 M2 = M2S DO 122 M1=1,M1D,IM1 M2 = M2+IM2 CC(1,M1,K,J,1) = CH(1,M2,K,1,J) CC(2,M1,K,J,1) = CH(2,M2,K,1,J) 122 CONTINUE 123 CONTINUE DO 126 J=2,IP DO 125 I=2,IDO DO 124 K=1,L1 M2 = M2S DO 124 M1=1,M1D,IM1 M2 = M2+IM2 CC(1,M1,K,J,I) = WA(I,J-1,1)*CH(1,M2,K,I,J) 1 +WA(I,J-1,2)*CH(2,M2,K,I,J) CC(2,M1,K,J,I) = WA(I,J-1,1)*CH(2,M2,K,I,J) 1 -WA(I,J-1,2)*CH(1,M2,K,I,J) 124 CONTINUE 125 CONTINUE 126 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE CMFM1B (LOT,JUMP,N,INC,C,CH,WA,FNF,FAC) COMPLEX C(*) REAL CH(*), WA(*), FAC(*) C C FFTPACK 5.0 auxiliary routine C NF = FNF NA = 0 L1 = 1 IW = 1 DO 125 K1=1,NF IP = FAC(K1) L2 = IP*L1 IDO = N/L2 LID = L1*IDO NBR = 1+NA+2*MIN(IP-2,4) GO TO (52,62,53,63,54,64,55,65,56,66),NBR 52 CALL CMF2KB (LOT,IDO,L1,NA,C,JUMP,INC,CH,1,LOT,WA(IW)) GO TO 120 62 CALL CMF2KB (LOT,IDO,L1,NA,CH,1,LOT,C,JUMP,INC,WA(IW)) GO TO 120 53 CALL CMF3KB (LOT,IDO,L1,NA,C,JUMP,INC,CH,1,LOT,WA(IW)) GO TO 120 63 CALL CMF3KB (LOT,IDO,L1,NA,CH,1,LOT,C,JUMP,INC,WA(IW)) GO TO 120 54 CALL CMF4KB (LOT,IDO,L1,NA,C,JUMP,INC,CH,1,LOT,WA(IW)) GO TO 120 64 CALL CMF4KB (LOT,IDO,L1,NA,CH,1,LOT,C,JUMP,INC,WA(IW)) GO TO 120 55 CALL CMF5KB (LOT,IDO,L1,NA,C,JUMP,INC,CH,1,LOT,WA(IW)) GO TO 120 65 CALL CMF5KB (LOT,IDO,L1,NA,CH,1,LOT,C,JUMP,INC,WA(IW)) GO TO 120 56 CALL CMFGKB (LOT,IDO,IP,L1,LID,NA,C,C,JUMP,INC,CH,CH,1, 1 LOT,WA(IW)) GO TO 120 66 CALL CMFGKB (LOT,IDO,IP,L1,LID,NA,CH,CH,1,LOT,C,C, 1 JUMP,INC,WA(IW)) 120 L1 = L2 IW = IW+(IP-1)*(IDO+IDO) IF(IP .LE. 5) NA = 1-NA 125 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE CMFM1F (LOT,JUMP,N,INC,C,CH,WA,FNF,FAC) COMPLEX C(*) REAL CH(*), WA(*), FAC(*) C C FFTPACK 5.0 auxiliary routine C NF = FNF NA = 0 L1 = 1 IW = 1 DO 125 K1=1,NF IP = FAC(K1) L2 = IP*L1 IDO = N/L2 LID = L1*IDO NBR = 1+NA+2*MIN(IP-2,4) GO TO (52,62,53,63,54,64,55,65,56,66),NBR 52 CALL CMF2KF (LOT,IDO,L1,NA,C,JUMP,INC,CH,1,LOT,WA(IW)) GO TO 120 62 CALL CMF2KF (LOT,IDO,L1,NA,CH,1,LOT,C,JUMP,INC,WA(IW)) GO TO 120 53 CALL CMF3KF (LOT,IDO,L1,NA,C,JUMP,INC,CH,1,LOT,WA(IW)) GO TO 120 63 CALL CMF3KF (LOT,IDO,L1,NA,CH,1,LOT,C,JUMP,INC,WA(IW)) GO TO 120 54 CALL CMF4KF (LOT,IDO,L1,NA,C,JUMP,INC,CH,1,LOT,WA(IW)) GO TO 120 64 CALL CMF4KF (LOT,IDO,L1,NA,CH,1,LOT,C,JUMP,INC,WA(IW)) GO TO 120 55 CALL CMF5KF (LOT,IDO,L1,NA,C,JUMP,INC,CH,1,LOT,WA(IW)) GO TO 120 65 CALL CMF5KF (LOT,IDO,L1,NA,CH,1,LOT,C,JUMP,INC,WA(IW)) GO TO 120 56 CALL CMFGKF (LOT,IDO,IP,L1,LID,NA,C,C,JUMP,INC,CH,CH, 1 1,LOT,WA(IW)) GO TO 120 66 CALL CMFGKF (LOT,IDO,IP,L1,LID,NA,CH,CH,1,LOT,C,C, 1 JUMP,INC,WA(IW)) 120 L1 = L2 IW = IW+(IP-1)*(IDO+IDO) IF(IP .LE. 5) NA = 1-NA 125 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE COSQ1B (N, INC, X, LENX, WSAVE, LENSAV, 1 WORK, LENWRK, IER) INTEGER N, INC, LENX, LENSAV, LENWRK, IER REAL X(INC,*), WSAVE(LENSAV), WORK(LENWRK) C IER = 0 C IF (LENX .LT. INC*(N-1) + 1) THEN IER = 1 CALL XERFFT ('COSQ1B', 6) GO TO 300 ELSEIF (LENSAV .LT. 2*N + INT(LOG(REAL(N))/LOG(2.)) +4) THEN IER = 2 CALL XERFFT ('COSQ1B', 8) GO TO 300 ELSEIF (LENWRK .LT. N) THEN IER = 3 CALL XERFFT ('COSQ1B', 10) GO TO 300 ENDIF C IF (N-2) 300,102,103 102 SSQRT2 = 1./SQRT(2.) X1 = X(1,1)+X(1,2) X(1,2) = SSQRT2*(X(1,1)-X(1,2)) X(1,1) = X1 RETURN 103 CALL COSQB1 (N,INC,X,WSAVE,WORK,IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('COSQ1B',-5) ENDIF C 300 RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE COSQ1F (N, INC, X, LENX, WSAVE, LENSAV, 1 WORK, LENWRK, IER) INTEGER N, INC, LENX, LENSAV, LENWRK, IER REAL X(INC,*), WSAVE(LENSAV), WORK(LENWRK) C IER = 0 C IF (LENX .LT. INC*(N-1) + 1) THEN IER = 1 CALL XERFFT ('COSQ1F', 6) GO TO 300 ELSEIF (LENSAV .LT. 2*N + INT(LOG(REAL(N))/LOG(2.)) +4) THEN IER = 2 CALL XERFFT ('COSQ1F', 8) GO TO 300 ELSEIF (LENWRK .LT. N) THEN IER = 3 CALL XERFFT ('COSQ1F', 10) GO TO 300 ENDIF C IF (N-2) 102,101,103 101 SSQRT2 = 1./SQRT(2.) TSQX = SSQRT2*X(1,2) X(1,2) = .5*X(1,1)-TSQX X(1,1) = .5*X(1,1)+TSQX 102 RETURN 103 CALL COSQF1 (N,INC,X,WSAVE,WORK,IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('COSQ1F',-5) ENDIF C 300 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE COSQ1I (N, WSAVE, LENSAV, IER) INTEGER N, LENSAV, IER REAL WSAVE(LENSAV) C IER = 0 IF (LENSAV .LT. 2*N + INT(LOG(REAL(N))/LOG(2.)) +4) THEN IER = 2 CALL XERFFT ('COSQ1I', 3) GO TO 300 ENDIF C PIH = 2.*ATAN(1.) DT = PIH/FLOAT(N) FK = 0. DO 101 K=1,N FK = FK+1. WSAVE(K) = COS(FK*DT) 101 CONTINUE LNSV = N + INT(LOG(REAL(N))/LOG(2.)) +4 CALL RFFT1I (N, WSAVE(N+1), LNSV, IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('COSQ1I',-5) ENDIF 300 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE COSQB1 (N,INC,X,WSAVE,WORK,IER) DIMENSION X(INC,*) ,WSAVE(*) ,WORK(*) IER = 0 NS2 = (N+1)/2 NP2 = N+2 DO 101 I=3,N,2 XIM1 = X(1,I-1)+X(1,I) X(1,I) = .5*(X(1,I-1)-X(1,I)) X(1,I-1) = .5*XIM1 101 CONTINUE X(1,1) = .5*X(1,1) MODN = MOD(N,2) IF (MODN .NE. 0) GO TO 302 X(1,N) = .5*X(1,N) 302 LENX = INC*(N-1) + 1 LNSV = N + INT(LOG(REAL(N))/LOG(2.)) + 4 LNWK = N C CALL RFFT1B(N,INC,X,LENX,WSAVE(N+1),LNSV,WORK,LNWK,IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('COSQB1',-5) GO TO 400 ENDIF C DO 102 K=2,NS2 KC = NP2-K WORK(K) = WSAVE(K-1)*X(1,KC)+WSAVE(KC-1)*X(1,K) WORK(KC) = WSAVE(K-1)*X(1,K)-WSAVE(KC-1)*X(1,KC) 102 CONTINUE IF (MODN .NE. 0) GO TO 305 X(1,NS2+1) = WSAVE(NS2)*(X(1,NS2+1)+X(1,NS2+1)) 305 DO 103 K=2,NS2 KC = NP2-K X(1,K) = WORK(K)+WORK(KC) X(1,KC) = WORK(K)-WORK(KC) 103 CONTINUE X(1,1) = X(1,1)+X(1,1) 400 RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE COSQF1 (N,INC,X,WSAVE,WORK,IER) DIMENSION X(INC,*) ,WSAVE(*) ,WORK(*) IER = 0 NS2 = (N+1)/2 NP2 = N+2 DO 101 K=2,NS2 KC = NP2-K WORK(K) = X(1,K)+X(1,KC) WORK(KC) = X(1,K)-X(1,KC) 101 CONTINUE MODN = MOD(N,2) IF (MODN .NE. 0) GO TO 301 WORK(NS2+1) = X(1,NS2+1)+X(1,NS2+1) 301 DO 102 K=2,NS2 KC = NP2-K X(1,K) = WSAVE(K-1)*WORK(KC)+WSAVE(KC-1)*WORK(K) X(1,KC) = WSAVE(K-1)*WORK(K) -WSAVE(KC-1)*WORK(KC) 102 CONTINUE IF (MODN .NE. 0) GO TO 303 X(1,NS2+1) = WSAVE(NS2)*WORK(NS2+1) 303 LENX = INC*(N-1) + 1 LNSV = N + INT(LOG(REAL(N))/LOG(2.)) + 4 LNWK = N C CALL RFFT1F(N,INC,X,LENX,WSAVE(N+1),LNSV,WORK,LNWK,IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('COSQF1',-5) GO TO 400 ENDIF C DO 103 I=3,N,2 XIM1 = .5*(X(1,I-1)+X(1,I)) X(1,I) = .5*(X(1,I-1)-X(1,I)) X(1,I-1) = XIM1 103 CONTINUE 400 RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE COSQMB (LOT, JUMP, N, INC, X, LENX, WSAVE, LENSAV, 1 WORK, LENWRK, IER) INTEGER LOT, JUMP, N, INC, LENX, LENSAV, LENWRK, IER REAL X(INC,*), WSAVE(LENSAV), WORK(LENWRK) LOGICAL XERCON C IER = 0 C IF (LENX .LT. (LOT-1)*JUMP + INC*(N-1) + 1) THEN IER = 1 CALL XERFFT ('COSQMB', 6) GO TO 300 ELSEIF (LENSAV .LT. 2*N + INT(LOG(REAL(N))/LOG(2.)) +4) THEN IER = 2 CALL XERFFT ('COSQMB', 8) GO TO 300 ELSEIF (LENWRK .LT. LOT*N) THEN IER = 3 CALL XERFFT ('COSQMB', 10) GO TO 300 ELSEIF (.NOT. XERCON(INC,JUMP,N,LOT)) THEN IER = 4 CALL XERFFT ('COSQMB', -1) GO TO 300 ENDIF C LJ = (LOT-1)*JUMP+1 IF (N-2) 101,102,103 101 DO 201 M=1,LJ,JUMP X(M,1) = X(M,1) 201 CONTINUE RETURN 102 SSQRT2 = 1./SQRT(2.) DO 202 M=1,LJ,JUMP X1 = X(M,1)+X(M,2) X(M,2) = SSQRT2*(X(M,1)-X(M,2)) X(M,1) = X1 202 CONTINUE RETURN 103 CALL MCSQB1 (LOT,JUMP,N,INC,X,WSAVE,WORK,IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('COSQMB',-5) ENDIF C 300 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE COSQMF (LOT, JUMP, N, INC, X, LENX, WSAVE, LENSAV, 1 WORK, LENWRK, IER) INTEGER LOT, JUMP, N, INC, LENX, LENSAV, LENWRK, IER REAL X(INC,*), WSAVE(LENSAV), WORK(LENWRK) LOGICAL XERCON C IER = 0 C IF (LENX .LT. (LOT-1)*JUMP + INC*(N-1) + 1) THEN IER = 1 CALL XERFFT ('COSQMF', 6) GO TO 300 ELSEIF (LENSAV .LT. 2*N + INT(LOG(REAL(N))/LOG(2.)) +4) THEN IER = 2 CALL XERFFT ('COSQMF', 8) GO TO 300 ELSEIF (LENWRK .LT. LOT*N) THEN IER = 3 CALL XERFFT ('COSQMF', 10) GO TO 300 ELSEIF (.NOT. XERCON(INC,JUMP,N,LOT)) THEN IER = 4 CALL XERFFT ('COSQMF', -1) GO TO 300 ENDIF C LJ = (LOT-1)*JUMP+1 IF (N-2) 102,101,103 101 SSQRT2 = 1./SQRT(2.) DO 201 M=1,LJ,JUMP TSQX = SSQRT2*X(M,2) X(M,2) = .5*X(M,1)-TSQX X(M,1) = .5*X(M,1)+TSQX 201 CONTINUE 102 RETURN 103 CALL MCSQF1 (LOT,JUMP,N,INC,X,WSAVE,WORK,IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('COSQMF',-5) ENDIF C 300 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE COSQMI (N, WSAVE, LENSAV, IER) INTEGER N, LENSAV, IER REAL WSAVE(LENSAV) C IER = 0 C IF (LENSAV .LT. 2*N + INT(LOG(REAL(N))/LOG(2.)) +4) THEN IER = 2 CALL XERFFT ('COSQMI', 3) GO TO 300 ENDIF C PIH = 2.*ATAN(1.) DT = PIH/FLOAT(N) FK = 0. DO 101 K=1,N FK = FK+1. WSAVE(K) = COS(FK*DT) 101 CONTINUE LNSV = N + INT(LOG(REAL(N))/LOG(2.)) +4 CALL RFFTMI (N, WSAVE(N+1), LNSV, IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('COSQMI',-5) ENDIF 300 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE COST1B ( N, INC, X, LENX, WSAVE, LENSAV, 1 WORK, LENWRK, IER) INTEGER N, INC, LENX, LENSAV, LENWRK, IER REAL X(INC,*), WSAVE(LENSAV), WORK(LENWRK) C IER = 0 IF (LENX .LT. INC*(N-1) + 1) THEN IER = 1 CALL XERFFT ('COST1B', 6) GO TO 100 ELSEIF (LENSAV .LT. 2*N + INT(LOG(REAL(N))/LOG(2.)) +4) THEN IER = 2 CALL XERFFT ('COST1B', 8) GO TO 100 ELSEIF (LENWRK .LT. N-1) THEN IER = 3 CALL XERFFT ('COST1B', 10) GO TO 100 ENDIF C IF (N .EQ. 1) RETURN C CALL COSTB1 (N,INC,X,WSAVE,WORK,IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('COST1B',-5) ENDIF C 100 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE COST1F ( N, INC, X, LENX, WSAVE, LENSAV, 1 WORK, LENWRK, IER) INTEGER N, INC, LENX, LENSAV, LENWRK, IER REAL X(INC,*), WSAVE(LENSAV), WORK(LENWRK) C IER = 0 IF (LENX .LT. INC*(N-1) + 1) THEN IER = 1 CALL XERFFT ('COST1F', 6) GO TO 100 ELSEIF (LENSAV .LT. 2*N + INT(LOG(REAL(N))/LOG(2.)) +4) THEN IER = 2 CALL XERFFT ('COST1F', 8) GO TO 100 ELSEIF (LENWRK .LT. N-1) THEN IER = 3 CALL XERFFT ('COST1F', 10) GO TO 100 ENDIF C IF (N .EQ. 1) RETURN C CALL COSTF1(N,INC,X,WSAVE,WORK,IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('COST1F',-5) ENDIF C 100 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE COST1I (N, WSAVE, LENSAV, IER) INTEGER N, LENSAV, IER REAL WSAVE(LENSAV) C IER = 0 C IF (LENSAV .LT. 2*N + INT(LOG(REAL(N))/LOG(2.)) +4) THEN IER = 2 CALL XERFFT ('COST1I', 3) GO TO 300 ENDIF C IF (N .LE. 3) RETURN NM1 = N-1 NP1 = N+1 NS2 = N/2 PI = 4.*ATAN(1.) DT = PI/FLOAT(NM1) FK = 0. DO 101 K=2,NS2 KC = NP1-K FK = FK+1. WSAVE(K) = 2.*SIN(FK*DT) WSAVE(KC) = 2.*COS(FK*DT) 101 CONTINUE LNSV = NM1 + INT(LOG(REAL(NM1))/LOG(2.)) +4 CALL RFFT1I (NM1, WSAVE(N+1), LNSV, IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('COST1I',-5) ENDIF 300 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE COSTB1(N,INC,X,WSAVE,WORK,IER) REAL X(INC,*) ,WSAVE(*) DOUBLE PRECISION DSUM IER = 0 NM1 = N-1 NP1 = N+1 NS2 = N/2 IF (N-2) 106,101,102 101 X1H = X(1,1)+X(1,2) X(1,2) = X(1,1)-X(1,2) X(1,1) = X1H RETURN 102 IF (N .GT. 3) GO TO 103 X1P3 = X(1,1)+X(1,3) X2 = X(1,2) X(1,2) = X(1,1)-X(1,3) X(1,1) = X1P3+X2 X(1,3) = X1P3-X2 RETURN 103 X(1,1) = X(1,1)+X(1,1) X(1,N) = X(1,N)+X(1,N) DSUM = X(1,1)-X(1,N) X(1,1) = X(1,1)+X(1,N) DO 104 K=2,NS2 KC = NP1-K T1 = X(1,K)+X(1,KC) T2 = X(1,K)-X(1,KC) DSUM = DSUM+WSAVE(KC)*T2 T2 = WSAVE(K)*T2 X(1,K) = T1-T2 X(1,KC) = T1+T2 104 CONTINUE MODN = MOD(N,2) IF (MODN .EQ. 0) GO TO 124 X(1,NS2+1) = X(1,NS2+1)+X(1,NS2+1) 124 LENX = INC*(NM1-1) + 1 LNSV = NM1 + INT(LOG(REAL(NM1))/LOG(2.)) + 4 LNWK = NM1 C CALL RFFT1F(NM1,INC,X,LENX,WSAVE(N+1),LNSV,WORK, 1 LNWK,IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('COSTB1',-5) RETURN ENDIF C FNM1S2 = FLOAT(NM1)/2. DSUM = .5*DSUM X(1,1) = FNM1S2*X(1,1) IF(MOD(NM1,2) .NE. 0) GO TO 30 X(1,NM1) = X(1,NM1)+X(1,NM1) 30 FNM1S4 = FLOAT(NM1)/4. DO 105 I=3,N,2 XI = FNM1S4*X(1,I) X(1,I) = FNM1S4*X(1,I-1) X(1,I-1) = DSUM DSUM = DSUM+XI 105 CONTINUE IF (MODN .NE. 0) RETURN X(1,N) = DSUM 106 RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE COSTF1(N,INC,X,WSAVE,WORK,IER) REAL X(INC,*) ,WSAVE(*) DOUBLE PRECISION DSUM IER = 0 NM1 = N-1 NP1 = N+1 NS2 = N/2 IF (N-2) 200,101,102 101 X1H = X(1,1)+X(1,2) X(1,2) = .5*(X(1,1)-X(1,2)) X(1,1) = .5*X1H GO TO 200 102 IF (N .GT. 3) GO TO 103 X1P3 = X(1,1)+X(1,3) TX2 = X(1,2)+X(1,2) X(1,2) = .5*(X(1,1)-X(1,3)) X(1,1) = .25*(X1P3+TX2) X(1,3) = .25*(X1P3-TX2) GO TO 200 103 DSUM = X(1,1)-X(1,N) X(1,1) = X(1,1)+X(1,N) DO 104 K=2,NS2 KC = NP1-K T1 = X(1,K)+X(1,KC) T2 = X(1,K)-X(1,KC) DSUM = DSUM+WSAVE(KC)*T2 T2 = WSAVE(K)*T2 X(1,K) = T1-T2 X(1,KC) = T1+T2 104 CONTINUE MODN = MOD(N,2) IF (MODN .EQ. 0) GO TO 124 X(1,NS2+1) = X(1,NS2+1)+X(1,NS2+1) 124 LENX = INC*(NM1-1) + 1 LNSV = NM1 + INT(LOG(REAL(NM1))/LOG(2.)) + 4 LNWK = NM1 C CALL RFFT1F(NM1,INC,X,LENX,WSAVE(N+1),LNSV,WORK, 1 LNWK,IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('COSTF1',-5) GO TO 200 ENDIF C SNM1 = 1./FLOAT(NM1) DSUM = SNM1*DSUM IF(MOD(NM1,2) .NE. 0) GO TO 30 X(1,NM1) = X(1,NM1)+X(1,NM1) 30 DO 105 I=3,N,2 XI = .5*X(1,I) X(1,I) = .5*X(1,I-1) X(1,I-1) = DSUM DSUM = DSUM+XI 105 CONTINUE IF (MODN .NE. 0) GO TO 117 X(1,N) = DSUM 117 X(1,1) = .5*X(1,1) X(1,N) = .5*X(1,N) 200 RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE COSTMB (LOT, JUMP, N, INC, X, LENX, WSAVE, LENSAV, 1 WORK, LENWRK, IER) INTEGER LOT, JUMP, N, INC, LENX, LENSAV, LENWRK, IER REAL X(INC,*), WSAVE(LENSAV), WORK(LENWRK) LOGICAL XERCON C IER = 0 C IF (LENX .LT. (LOT-1)*JUMP + INC*(N-1) + 1) THEN IER = 1 CALL XERFFT ('COSTMB', 6) GO TO 100 ELSEIF (LENSAV .LT. 2*N + INT(LOG(REAL(N))/LOG(2.)) +4) THEN IER = 2 CALL XERFFT ('COSTMB', 8) GO TO 100 ELSEIF (LENWRK .LT. LOT*(N+1)) THEN IER = 3 CALL XERFFT ('COSTMB', 10) GO TO 100 ELSEIF (.NOT. XERCON(INC,JUMP,N,LOT)) THEN IER = 4 CALL XERFFT ('COSTMB', -1) GO TO 100 ENDIF C IW1 = LOT+LOT+1 CALL MCSTB1(LOT,JUMP,N,INC,X,WSAVE,WORK,WORK(IW1),IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('COSTMB',-5) ENDIF C 100 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE COSTMF (LOT, JUMP, N, INC, X, LENX, WSAVE, LENSAV, 1 WORK, LENWRK, IER) INTEGER LOT, JUMP, N, INC, LENX, LENSAV, LENWRK, IER REAL X(INC,*), WSAVE(LENSAV), WORK(LENWRK) LOGICAL XERCON C IER = 0 C IF (LENX .LT. (LOT-1)*JUMP + INC*(N-1) + 1) THEN IER = 1 CALL XERFFT ('COSTMF', 6) GO TO 100 ELSEIF (LENSAV .LT. 2*N + INT(LOG(REAL(N))/LOG(2.)) +4) THEN IER = 2 CALL XERFFT ('COSTMF', 8) GO TO 100 ELSEIF (LENWRK .LT. LOT*(N+1)) THEN IER = 3 CALL XERFFT ('COSTMF', 10) GO TO 100 ELSEIF (.NOT. XERCON(INC,JUMP,N,LOT)) THEN IER = 4 CALL XERFFT ('COSTMF', -1) GO TO 100 ENDIF C IW1 = LOT+LOT+1 CALL MCSTF1(LOT,JUMP,N,INC,X,WSAVE,WORK,WORK(IW1),IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('COSTMF',-5) ENDIF C 100 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE COSTMI (N, WSAVE, LENSAV, IER) INTEGER N, LENSAV, IER REAL WSAVE(LENSAV) C IER = 0 C IF (LENSAV .LT. 2*N + INT(LOG(REAL(N))/LOG(2.)) +4) THEN IER = 2 CALL XERFFT ('COSTMI', 3) GO TO 300 ENDIF C IF (N .LE. 3) RETURN NM1 = N-1 NP1 = N+1 NS2 = N/2 PI = 4.*ATAN(1.) DT = PI/FLOAT(NM1) FK = 0. DO 101 K=2,NS2 KC = NP1-K FK = FK+1. WSAVE(K) = 2.*SIN(FK*DT) WSAVE(KC) = 2.*COS(FK*DT) 101 CONTINUE LNSV = NM1 + INT(LOG(REAL(NM1))/LOG(2.)) +4 CALL RFFTMI (NM1, WSAVE(N+1), LNSV, IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('COSTMI',-5) ENDIF 300 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE FACTOR (N,NF,FAC) REAL FAC(*) INTEGER NTRYH(4) DATA NTRYH(1),NTRYH(2),NTRYH(3),NTRYH(4)/4,2,3,5/ C NL = N NF = 0 J = 0 101 J = J+1 IF (J-4) 102,102,103 102 NTRY = NTRYH(J) GO TO 104 103 NTRY = NTRY+2 104 NQ = NL/NTRY NR = NL-NTRY*NQ IF (NR) 101,105,101 105 NF = NF+1 FAC(NF) = NTRY NL = NQ IF (NL .NE. 1) GO TO 104 RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE MCFTI1 (N,WA,FNF,FAC) REAL WA(*),FAC(*) C CALL FACTOR (N,NF,FAC) FNF = NF IW = 1 L1 = 1 DO 110 K1=1,NF IP = FAC(K1) L2 = L1*IP IDO = N/L2 CALL TABLES (IDO,IP,WA(IW)) IW = IW+(IP-1)*(IDO+IDO) L1 = L2 110 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE MCSQB1 (LOT,JUMP,N,INC,X,WSAVE,WORK,IER) DIMENSION X(INC,*) ,WSAVE(*) ,WORK(LOT,*) IER = 0 LJ = (LOT-1)*JUMP+1 NS2 = (N+1)/2 NP2 = N+2 DO 101 I=3,N,2 DO 201 M=1,LJ,JUMP XIM1 = X(M,I-1)+X(M,I) X(M,I) = .5*(X(M,I-1)-X(M,I)) X(M,I-1) = .5*XIM1 201 CONTINUE 101 CONTINUE DO 301 M=1,LJ,JUMP X(M,1) = .5*X(M,1) 301 CONTINUE MODN = MOD(N,2) IF (MODN .NE. 0) GO TO 302 DO 303 M=1,LJ,JUMP X(M,N) = .5*X(M,N) 303 CONTINUE 302 CONTINUE LENX = (LOT-1)*JUMP + INC*(N-1) + 1 LNSV = N + INT(LOG(REAL(N))/LOG(2.)) + 4 LNWK = LOT*N C CALL RFFTMB(LOT,JUMP,N,INC,X,LENX,WSAVE(N+1),LNSV,WORK,LNWK,IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('MCSQB1',-5) GO TO 400 ENDIF C DO 102 K=2,NS2 KC = NP2-K M1 = 0 DO 202 M=1,LJ,JUMP M1 = M1 + 1 WORK(M1,K) = WSAVE(K-1)*X(M,KC)+WSAVE(KC-1)*X(M,K) WORK(M1,KC) = WSAVE(K-1)*X(M,K)-WSAVE(KC-1)*X(M,KC) 202 CONTINUE 102 CONTINUE IF (MODN .NE. 0) GO TO 305 DO 304 M=1,LJ,JUMP X(M,NS2+1) = WSAVE(NS2)*(X(M,NS2+1)+X(M,NS2+1)) 304 CONTINUE 305 DO 103 K=2,NS2 KC = NP2-K M1 = 0 DO 203 M=1,LJ,JUMP M1 = M1 + 1 X(M,K) = WORK(M1,K)+WORK(M1,KC) X(M,KC) = WORK(M1,K)-WORK(M1,KC) 203 CONTINUE 103 CONTINUE DO 104 M=1,LJ,JUMP X(M,1) = X(M,1)+X(M,1) 104 CONTINUE 400 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE MCSQF1 (LOT,JUMP,N,INC,X,WSAVE,WORK,IER) DIMENSION X(INC,*) ,WSAVE(*) ,WORK(LOT,*) IER = 0 LJ = (LOT-1)*JUMP+1 NS2 = (N+1)/2 NP2 = N+2 DO 101 K=2,NS2 KC = NP2-K M1 = 0 DO 201 M=1,LJ,JUMP M1 = M1 + 1 WORK(M1,K) = X(M,K)+X(M,KC) WORK(M1,KC) = X(M,K)-X(M,KC) 201 CONTINUE 101 CONTINUE MODN = MOD(N,2) IF (MODN .NE. 0) GO TO 301 M1 = 0 DO 202 M=1,LJ,JUMP M1 = M1 + 1 WORK(M1,NS2+1) = X(M,NS2+1)+X(M,NS2+1) 202 CONTINUE 301 DO 102 K=2,NS2 KC = NP2-K M1 = 0 DO 302 M=1,LJ,JUMP M1 = M1 + 1 X(M,K) = WSAVE(K-1)*WORK(M1,KC)+WSAVE(KC-1)*WORK(M1,K) X(M,KC) = WSAVE(K-1)*WORK(M1,K) -WSAVE(KC-1)*WORK(M1,KC) 302 CONTINUE 102 CONTINUE IF (MODN .NE. 0) GO TO 303 M1 = 0 DO 304 M=1,LJ,JUMP M1 = M1 + 1 X(M,NS2+1) = WSAVE(NS2)*WORK(M1,NS2+1) 304 CONTINUE 303 CONTINUE LENX = (LOT-1)*JUMP + INC*(N-1) + 1 LNSV = N + INT(LOG(REAL(N))/LOG(2.)) + 4 LNWK = LOT*N C CALL RFFTMF(LOT,JUMP,N,INC,X,LENX,WSAVE(N+1),LNSV,WORK,LNWK,IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('MCSQF1',-5) GO TO 400 ENDIF C DO 103 I=3,N,2 DO 203 M=1,LJ,JUMP XIM1 = .5*(X(M,I-1)+X(M,I)) X(M,I) = .5*(X(M,I-1)-X(M,I)) X(M,I-1) = XIM1 203 CONTINUE 103 CONTINUE 400 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE MCSTB1(LOT,JUMP,N,INC,X,WSAVE,DSUM,WORK,IER) REAL X(INC,*) ,WSAVE(*) DOUBLE PRECISION DSUM(*) IER = 0 NM1 = N-1 NP1 = N+1 NS2 = N/2 LJ = (LOT-1)*JUMP+1 IF (N-2) 106,101,102 101 DO 111 M=1,LJ,JUMP X1H = X(M,1)+X(M,2) X(M,2) = X(M,1)-X(M,2) X(M,1) = X1H 111 CONTINUE RETURN 102 IF (N .GT. 3) GO TO 103 DO 112 M=1,LJ,JUMP X1P3 = X(M,1)+X(M,3) X2 = X(M,2) X(M,2) = X(M,1)-X(M,3) X(M,1) = X1P3+X2 X(M,3) = X1P3-X2 112 CONTINUE RETURN 103 DO 118 M=1,LJ,JUMP X(M,1) = X(M,1)+X(M,1) X(M,N) = X(M,N)+X(M,N) 118 CONTINUE M1 = 0 DO 113 M=1,LJ,JUMP M1 = M1+1 DSUM(M1) = X(M,1)-X(M,N) X(M,1) = X(M,1)+X(M,N) 113 CONTINUE DO 104 K=2,NS2 M1 = 0 DO 114 M=1,LJ,JUMP M1 = M1+1 KC = NP1-K T1 = X(M,K)+X(M,KC) T2 = X(M,K)-X(M,KC) DSUM(M1) = DSUM(M1)+WSAVE(KC)*T2 T2 = WSAVE(K)*T2 X(M,K) = T1-T2 X(M,KC) = T1+T2 114 CONTINUE 104 CONTINUE MODN = MOD(N,2) IF (MODN .EQ. 0) GO TO 124 DO 123 M=1,LJ,JUMP X(M,NS2+1) = X(M,NS2+1)+X(M,NS2+1) 123 CONTINUE 124 CONTINUE LENX = (LOT-1)*JUMP + INC*(NM1-1) + 1 LNSV = NM1 + INT(LOG(REAL(NM1))/LOG(2.)) + 4 LNWK = LOT*NM1 C CALL RFFTMF(LOT,JUMP,NM1,INC,X,LENX,WSAVE(N+1),LNSV,WORK, 1 LNWK,IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('MCSTB1',-5) GO TO 106 ENDIF C FNM1S2 = FLOAT(NM1)/2. M1 = 0 DO 10 M=1,LJ,JUMP M1 = M1+1 DSUM(M1) = .5*DSUM(M1) X(M,1) = FNM1S2*X(M,1) 10 CONTINUE IF(MOD(NM1,2) .NE. 0) GO TO 30 DO 20 M=1,LJ,JUMP X(M,NM1) = X(M,NM1)+X(M,NM1) 20 CONTINUE 30 FNM1S4 = FLOAT(NM1)/4. DO 105 I=3,N,2 M1 = 0 DO 115 M=1,LJ,JUMP M1 = M1+1 XI = FNM1S4*X(M,I) X(M,I) = FNM1S4*X(M,I-1) X(M,I-1) = DSUM(M1) DSUM(M1) = DSUM(M1)+XI 115 CONTINUE 105 CONTINUE IF (MODN .NE. 0) RETURN M1 = 0 DO 116 M=1,LJ,JUMP M1 = M1+1 X(M,N) = DSUM(M1) 116 CONTINUE 106 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE MCSTF1(LOT,JUMP,N,INC,X,WSAVE,DSUM,WORK,IER) REAL X(INC,*) ,WSAVE(*) DOUBLE PRECISION DSUM(*) IER = 0 NM1 = N-1 NP1 = N+1 NS2 = N/2 LJ = (LOT-1)*JUMP+1 IF (N-2) 200,101,102 101 DO 111 M=1,LJ,JUMP X1H = X(M,1)+X(M,2) X(M,2) = .5*(X(M,1)-X(M,2)) X(M,1) = .5*X1H 111 CONTINUE GO TO 200 102 IF (N .GT. 3) GO TO 103 DO 112 M=1,LJ,JUMP X1P3 = X(M,1)+X(M,3) TX2 = X(M,2)+X(M,2) X(M,2) = .5*(X(M,1)-X(M,3)) X(M,1) = .25*(X1P3+TX2) X(M,3) = .25*(X1P3-TX2) 112 CONTINUE GO TO 200 103 M1 = 0 DO 113 M=1,LJ,JUMP M1 = M1+1 DSUM(M1) = X(M,1)-X(M,N) X(M,1) = X(M,1)+X(M,N) 113 CONTINUE DO 104 K=2,NS2 M1 = 0 DO 114 M=1,LJ,JUMP M1 = M1+1 KC = NP1-K T1 = X(M,K)+X(M,KC) T2 = X(M,K)-X(M,KC) DSUM(M1) = DSUM(M1)+WSAVE(KC)*T2 T2 = WSAVE(K)*T2 X(M,K) = T1-T2 X(M,KC) = T1+T2 114 CONTINUE 104 CONTINUE MODN = MOD(N,2) IF (MODN .EQ. 0) GO TO 124 DO 123 M=1,LJ,JUMP X(M,NS2+1) = X(M,NS2+1)+X(M,NS2+1) 123 CONTINUE 124 CONTINUE LENX = (LOT-1)*JUMP + INC*(NM1-1) + 1 LNSV = NM1 + INT(LOG(REAL(NM1))/LOG(2.)) + 4 LNWK = LOT*NM1 C CALL RFFTMF(LOT,JUMP,NM1,INC,X,LENX,WSAVE(N+1),LNSV,WORK, 1 LNWK,IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('MCSTF1',-5) GO TO 200 ENDIF C SNM1 = 1./FLOAT(NM1) DO 10 M=1,LOT DSUM(M) = SNM1*DSUM(M) 10 CONTINUE IF(MOD(NM1,2) .NE. 0) GO TO 30 DO 20 M=1,LJ,JUMP X(M,NM1) = X(M,NM1)+X(M,NM1) 20 CONTINUE 30 DO 105 I=3,N,2 M1 = 0 DO 115 M=1,LJ,JUMP M1 = M1+1 XI = .5*X(M,I) X(M,I) = .5*X(M,I-1) X(M,I-1) = DSUM(M1) DSUM(M1) = DSUM(M1)+XI 115 CONTINUE 105 CONTINUE IF (MODN .NE. 0) GO TO 117 M1 = 0 DO 116 M=1,LJ,JUMP M1 = M1+1 X(M,N) = DSUM(M1) 116 CONTINUE 117 DO 118 M=1,LJ,JUMP X(M,1) = .5*X(M,1) X(M,N) = .5*X(M,N) 118 CONTINUE C 200 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE MRADB2 (M,IDO,L1,CC,IM1,IN1,CH,IM2,IN2,WA1) REAL CC(IN1,IDO,2,L1), CH(IN2,IDO,L1,2), WA1(IDO) C M1D = (M-1)*IM1+1 M2S = 1-IM2 DO 101 K=1,L1 M2 = M2S DO 1001 M1=1,M1D,IM1 M2 = M2+IM2 CH(M2,1,K,1) = CC(M1,1,1,K)+CC(M1,IDO,2,K) CH(M2,1,K,2) = CC(M1,1,1,K)-CC(M1,IDO,2,K) 1001 CONTINUE 101 CONTINUE IF (IDO-2) 107,105,102 102 IDP2 = IDO+2 DO 104 K=1,L1 DO 103 I=3,IDO,2 IC = IDP2-I M2 = M2S DO 1002 M1=1,M1D,IM1 M2 = M2+IM2 CH(M2,I-1,K,1) = CC(M1,I-1,1,K)+CC(M1,IC-1,2,K) CH(M2,I,K,1) = CC(M1,I,1,K)-CC(M1,IC,2,K) CH(M2,I-1,K,2) = WA1(I-2)*(CC(M1,I-1,1,K)-CC(M1,IC-1,2,K)) 1 -WA1(I-1)*(CC(M1,I,1,K)+CC(M1,IC,2,K)) CH(M2,I,K,2) = WA1(I-2)*(CC(M1,I,1,K)+CC(M1,IC,2,K))+WA1(I-1) 1 *(CC(M1,I-1,1,K)-CC(M1,IC-1,2,K)) 1002 CONTINUE 103 CONTINUE 104 CONTINUE IF (MOD(IDO,2) .EQ. 1) RETURN 105 DO 106 K=1,L1 M2 = M2S DO 1003 M1=1,M1D,IM1 M2 = M2+IM2 CH(M2,IDO,K,1) = CC(M1,IDO,1,K)+CC(M1,IDO,1,K) CH(M2,IDO,K,2) = -(CC(M1,1,2,K)+CC(M1,1,2,K)) 1003 CONTINUE 106 CONTINUE 107 RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE MRADB3 (M,IDO,L1,CC,IM1,IN1,CH,IM2,IN2,WA1,WA2) REAL CC(IN1,IDO,3,L1) ,CH(IN2,IDO,L1,3), 1 WA1(IDO) ,WA2(IDO) C M1D = (M-1)*IM1+1 M2S = 1-IM2 ARG=2.*4.*ATAN(1.0)/3. TAUR=COS(ARG) TAUI=SIN(ARG) DO 101 K=1,L1 M2 = M2S DO 1001 M1=1,M1D,IM1 M2 = M2+IM2 CH(M2,1,K,1) = CC(M1,1,1,K)+2.*CC(M1,IDO,2,K) CH(M2,1,K,2) = CC(M1,1,1,K)+(2.*TAUR)*CC(M1,IDO,2,K) 1 -(2.*TAUI)*CC(M1,1,3,K) CH(M2,1,K,3) = CC(M1,1,1,K)+(2.*TAUR)*CC(M1,IDO,2,K) 1 +2.*TAUI*CC(M1,1,3,K) 1001 CONTINUE 101 CONTINUE IF (IDO .EQ. 1) RETURN IDP2 = IDO+2 DO 103 K=1,L1 DO 102 I=3,IDO,2 IC = IDP2-I M2 = M2S DO 1002 M1=1,M1D,IM1 M2 = M2+IM2 CH(M2,I-1,K,1) = CC(M1,I-1,1,K)+(CC(M1,I-1,3,K)+CC(M1,IC-1,2,K)) CH(M2,I,K,1) = CC(M1,I,1,K)+(CC(M1,I,3,K)-CC(M1,IC,2,K)) CH(M2,I-1,K,2) = WA1(I-2)* 1 ((CC(M1,I-1,1,K)+TAUR*(CC(M1,I-1,3,K)+CC(M1,IC-1,2,K)))- * (TAUI*(CC(M1,I,3,K)+CC(M1,IC,2,K)))) 2 -WA1(I-1)* 3 ((CC(M1,I,1,K)+TAUR*(CC(M1,I,3,K)-CC(M1,IC,2,K)))+ * (TAUI*(CC(M1,I-1,3,K)-CC(M1,IC-1,2,K)))) CH(M2,I,K,2) = WA1(I-2)* 4 ((CC(M1,I,1,K)+TAUR*(CC(M1,I,3,K)-CC(M1,IC,2,K)))+ 8 (TAUI*(CC(M1,I-1,3,K)-CC(M1,IC-1,2,K)))) 5 +WA1(I-1)* 6 ((CC(M1,I-1,1,K)+TAUR*(CC(M1,I-1,3,K)+CC(M1,IC-1,2,K)))- 8 (TAUI*(CC(M1,I,3,K)+CC(M1,IC,2,K)))) CH(M2,I-1,K,3) = WA2(I-2)* 7 ((CC(M1,I-1,1,K)+TAUR*(CC(M1,I-1,3,K)+CC(M1,IC-1,2,K)))+ 8 (TAUI*(CC(M1,I,3,K)+CC(M1,IC,2,K)))) 8 -WA2(I-1)* 9 ((CC(M1,I,1,K)+TAUR*(CC(M1,I,3,K)-CC(M1,IC,2,K)))- 8 (TAUI*(CC(M1,I-1,3,K)-CC(M1,IC-1,2,K)))) CH(M2,I,K,3) = WA2(I-2)* 1 ((CC(M1,I,1,K)+TAUR*(CC(M1,I,3,K)-CC(M1,IC,2,K)))- 8 (TAUI*(CC(M1,I-1,3,K)-CC(M1,IC-1,2,K)))) 2 +WA2(I-1)* 3 ((CC(M1,I-1,1,K)+TAUR*(CC(M1,I-1,3,K)+CC(M1,IC-1,2,K)))+ 8 (TAUI*(CC(M1,I,3,K)+CC(M1,IC,2,K)))) 1002 CONTINUE 102 CONTINUE 103 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE MRADB4 (M,IDO,L1,CC,IM1,IN1,CH,IM2,IN2,WA1,WA2,WA3) REAL CC(IN1,IDO,4,L1) ,CH(IN2,IDO,L1,4) , 1 WA1(IDO) , WA2(IDO) , WA3(IDO) C M1D = (M-1)*IM1+1 M2S = 1-IM2 SQRT2=SQRT(2.) DO 101 K=1,L1 M2 = M2S DO 1001 M1=1,M1D,IM1 M2 = M2+IM2 CH(M2,1,K,3) = (CC(M1,1,1,K)+CC(M1,IDO,4,K)) 1 -(CC(M1,IDO,2,K)+CC(M1,IDO,2,K)) CH(M2,1,K,1) = (CC(M1,1,1,K)+CC(M1,IDO,4,K)) 1 +(CC(M1,IDO,2,K)+CC(M1,IDO,2,K)) CH(M2,1,K,4) = (CC(M1,1,1,K)-CC(M1,IDO,4,K)) 1 +(CC(M1,1,3,K)+CC(M1,1,3,K)) CH(M2,1,K,2) = (CC(M1,1,1,K)-CC(M1,IDO,4,K)) 1 -(CC(M1,1,3,K)+CC(M1,1,3,K)) 1001 CONTINUE 101 CONTINUE IF (IDO-2) 107,105,102 102 IDP2 = IDO+2 DO 104 K=1,L1 DO 103 I=3,IDO,2 IC = IDP2-I M2 = M2S DO 1002 M1=1,M1D,IM1 M2 = M2+IM2 CH(M2,I-1,K,1) = (CC(M1,I-1,1,K)+CC(M1,IC-1,4,K)) 1 +(CC(M1,I-1,3,K)+CC(M1,IC-1,2,K)) CH(M2,I,K,1) = (CC(M1,I,1,K)-CC(M1,IC,4,K)) 1 +(CC(M1,I,3,K)-CC(M1,IC,2,K)) CH(M2,I-1,K,2)=WA1(I-2)*((CC(M1,I-1,1,K)-CC(M1,IC-1,4,K)) 1 -(CC(M1,I,3,K)+CC(M1,IC,2,K)))-WA1(I-1) 1 *((CC(M1,I,1,K)+CC(M1,IC,4,K))+(CC(M1,I-1,3,K)-CC(M1,IC-1,2,K))) CH(M2,I,K,2)=WA1(I-2)*((CC(M1,I,1,K)+CC(M1,IC,4,K)) 1 +(CC(M1,I-1,3,K)-CC(M1,IC-1,2,K)))+WA1(I-1) 1 *((CC(M1,I-1,1,K)-CC(M1,IC-1,4,K))-(CC(M1,I,3,K)+CC(M1,IC,2,K))) CH(M2,I-1,K,3)=WA2(I-2)*((CC(M1,I-1,1,K)+CC(M1,IC-1,4,K)) 1 -(CC(M1,I-1,3,K)+CC(M1,IC-1,2,K)))-WA2(I-1) 1 *((CC(M1,I,1,K)-CC(M1,IC,4,K))-(CC(M1,I,3,K)-CC(M1,IC,2,K))) CH(M2,I,K,3)=WA2(I-2)*((CC(M1,I,1,K)-CC(M1,IC,4,K)) 1 -(CC(M1,I,3,K)-CC(M1,IC,2,K)))+WA2(I-1) 1 *((CC(M1,I-1,1,K)+CC(M1,IC-1,4,K))-(CC(M1,I-1,3,K) 1 +CC(M1,IC-1,2,K))) CH(M2,I-1,K,4)=WA3(I-2)*((CC(M1,I-1,1,K)-CC(M1,IC-1,4,K)) 1 +(CC(M1,I,3,K)+CC(M1,IC,2,K)))-WA3(I-1) 1 *((CC(M1,I,1,K)+CC(M1,IC,4,K))-(CC(M1,I-1,3,K)-CC(M1,IC-1,2,K))) CH(M2,I,K,4)=WA3(I-2)*((CC(M1,I,1,K)+CC(M1,IC,4,K)) 1 -(CC(M1,I-1,3,K)-CC(M1,IC-1,2,K)))+WA3(I-1) 1 *((CC(M1,I-1,1,K)-CC(M1,IC-1,4,K))+(CC(M1,I,3,K)+CC(M1,IC,2,K))) 1002 CONTINUE 103 CONTINUE 104 CONTINUE IF (MOD(IDO,2) .EQ. 1) RETURN 105 CONTINUE DO 106 K=1,L1 M2 = M2S DO 1003 M1=1,M1D,IM1 M2 = M2+IM2 CH(M2,IDO,K,1) = (CC(M1,IDO,1,K)+CC(M1,IDO,3,K)) 1 +(CC(M1,IDO,1,K)+CC(M1,IDO,3,K)) CH(M2,IDO,K,2) = SQRT2*((CC(M1,IDO,1,K)-CC(M1,IDO,3,K)) 1 -(CC(M1,1,2,K)+CC(M1,1,4,K))) CH(M2,IDO,K,3) = (CC(M1,1,4,K)-CC(M1,1,2,K)) 1 +(CC(M1,1,4,K)-CC(M1,1,2,K)) CH(M2,IDO,K,4) = -SQRT2*((CC(M1,IDO,1,K)-CC(M1,IDO,3,K)) 1 +(CC(M1,1,2,K)+CC(M1,1,4,K))) 1003 CONTINUE 106 CONTINUE 107 RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE MRADB5 (M,IDO,L1,CC,IM1,IN1,CH,IM2,IN2, 1 WA1,WA2,WA3,WA4) REAL CC(IN1,IDO,5,L1) ,CH(IN2,IDO,L1,5), 1 WA1(IDO) ,WA2(IDO) ,WA3(IDO) ,WA4(IDO) C M1D = (M-1)*IM1+1 M2S = 1-IM2 ARG=2.*4.*ATAN(1.0)/5. TR11=COS(ARG) TI11=SIN(ARG) TR12=COS(2.*ARG) TI12=SIN(2.*ARG) DO 101 K=1,L1 M2 = M2S DO 1001 M1=1,M1D,IM1 M2 = M2+IM2 CH(M2,1,K,1) = CC(M1,1,1,K)+2.*CC(M1,IDO,2,K)+2.*CC(M1,IDO,4,K) CH(M2,1,K,2) = (CC(M1,1,1,K)+TR11*2.*CC(M1,IDO,2,K) 1 +TR12*2.*CC(M1,IDO,4,K))-(TI11*2.*CC(M1,1,3,K) 1 +TI12*2.*CC(M1,1,5,K)) CH(M2,1,K,3) = (CC(M1,1,1,K)+TR12*2.*CC(M1,IDO,2,K) 1 +TR11*2.*CC(M1,IDO,4,K))-(TI12*2.*CC(M1,1,3,K) 1 -TI11*2.*CC(M1,1,5,K)) CH(M2,1,K,4) = (CC(M1,1,1,K)+TR12*2.*CC(M1,IDO,2,K) 1 +TR11*2.*CC(M1,IDO,4,K))+(TI12*2.*CC(M1,1,3,K) 1 -TI11*2.*CC(M1,1,5,K)) CH(M2,1,K,5) = (CC(M1,1,1,K)+TR11*2.*CC(M1,IDO,2,K) 1 +TR12*2.*CC(M1,IDO,4,K))+(TI11*2.*CC(M1,1,3,K) 1 +TI12*2.*CC(M1,1,5,K)) 1001 CONTINUE 101 CONTINUE IF (IDO .EQ. 1) RETURN IDP2 = IDO+2 DO 103 K=1,L1 DO 102 I=3,IDO,2 IC = IDP2-I M2 = M2S DO 1002 M1=1,M1D,IM1 M2 = M2+IM2 CH(M2,I-1,K,1) = CC(M1,I-1,1,K)+(CC(M1,I-1,3,K)+CC(M1,IC-1,2,K)) 1 +(CC(M1,I-1,5,K)+CC(M1,IC-1,4,K)) CH(M2,I,K,1) = CC(M1,I,1,K)+(CC(M1,I,3,K)-CC(M1,IC,2,K)) 1 +(CC(M1,I,5,K)-CC(M1,IC,4,K)) CH(M2,I-1,K,2) = WA1(I-2)*((CC(M1,I-1,1,K)+TR11* 1 (CC(M1,I-1,3,K)+CC(M1,IC-1,2,K))+TR12 1 *(CC(M1,I-1,5,K)+CC(M1,IC-1,4,K)))-(TI11*(CC(M1,I,3,K) 1 +CC(M1,IC,2,K))+TI12*(CC(M1,I,5,K)+CC(M1,IC,4,K)))) 1 -WA1(I-1)*((CC(M1,I,1,K)+TR11*(CC(M1,I,3,K)-CC(M1,IC,2,K)) 1 +TR12*(CC(M1,I,5,K)-CC(M1,IC,4,K)))+(TI11*(CC(M1,I-1,3,K) 1 -CC(M1,IC-1,2,K))+TI12*(CC(M1,I-1,5,K)-CC(M1,IC-1,4,K)))) CH(M2,I,K,2) = WA1(I-2)*((CC(M1,I,1,K)+TR11*(CC(M1,I,3,K) 1 -CC(M1,IC,2,K))+TR12*(CC(M1,I,5,K)-CC(M1,IC,4,K))) 1 +(TI11*(CC(M1,I-1,3,K)-CC(M1,IC-1,2,K))+TI12 1 *(CC(M1,I-1,5,K)-CC(M1,IC-1,4,K))))+WA1(I-1) 1 *((CC(M1,I-1,1,K)+TR11*(CC(M1,I-1,3,K) 1 +CC(M1,IC-1,2,K))+TR12*(CC(M1,I-1,5,K)+CC(M1,IC-1,4,K))) 1 -(TI11*(CC(M1,I,3,K)+CC(M1,IC,2,K))+TI12 1 *(CC(M1,I,5,K)+CC(M1,IC,4,K)))) CH(M2,I-1,K,3) = WA2(I-2) 1 *((CC(M1,I-1,1,K)+TR12*(CC(M1,I-1,3,K)+CC(M1,IC-1,2,K)) 1 +TR11*(CC(M1,I-1,5,K)+CC(M1,IC-1,4,K)))-(TI12*(CC(M1,I,3,K) 1 +CC(M1,IC,2,K))-TI11*(CC(M1,I,5,K)+CC(M1,IC,4,K)))) 1 -WA2(I-1) 1 *((CC(M1,I,1,K)+TR12*(CC(M1,I,3,K)- 1 CC(M1,IC,2,K))+TR11*(CC(M1,I,5,K)-CC(M1,IC,4,K))) 1 +(TI12*(CC(M1,I-1,3,K)-CC(M1,IC-1,2,K))-TI11 1 *(CC(M1,I-1,5,K)-CC(M1,IC-1,4,K)))) CH(M2,I,K,3) = WA2(I-2) 1 *((CC(M1,I,1,K)+TR12*(CC(M1,I,3,K)- 1 CC(M1,IC,2,K))+TR11*(CC(M1,I,5,K)-CC(M1,IC,4,K))) 1 +(TI12*(CC(M1,I-1,3,K)-CC(M1,IC-1,2,K))-TI11 1 *(CC(M1,I-1,5,K)-CC(M1,IC-1,4,K)))) 1 +WA2(I-1) 1 *((CC(M1,I-1,1,K)+TR12*(CC(M1,I-1,3,K)+CC(M1,IC-1,2,K)) 1 +TR11*(CC(M1,I-1,5,K)+CC(M1,IC-1,4,K)))-(TI12*(CC(M1,I,3,K) 1 +CC(M1,IC,2,K))-TI11*(CC(M1,I,5,K)+CC(M1,IC,4,K)))) CH(M2,I-1,K,4) = WA3(I-2) 1 *((CC(M1,I-1,1,K)+TR12*(CC(M1,I-1,3,K)+CC(M1,IC-1,2,K)) 1 +TR11*(CC(M1,I-1,5,K)+CC(M1,IC-1,4,K)))+(TI12*(CC(M1,I,3,K) 1 +CC(M1,IC,2,K))-TI11*(CC(M1,I,5,K)+CC(M1,IC,4,K)))) 1 -WA3(I-1) 1 *((CC(M1,I,1,K)+TR12*(CC(M1,I,3,K)- 1 CC(M1,IC,2,K))+TR11*(CC(M1,I,5,K)-CC(M1,IC,4,K))) 1 -(TI12*(CC(M1,I-1,3,K)-CC(M1,IC-1,2,K))-TI11 1 *(CC(M1,I-1,5,K)-CC(M1,IC-1,4,K)))) CH(M2,I,K,4) = WA3(I-2) 1 *((CC(M1,I,1,K)+TR12*(CC(M1,I,3,K)- 1 CC(M1,IC,2,K))+TR11*(CC(M1,I,5,K)-CC(M1,IC,4,K))) 1 -(TI12*(CC(M1,I-1,3,K)-CC(M1,IC-1,2,K))-TI11 1 *(CC(M1,I-1,5,K)-CC(M1,IC-1,4,K)))) 1 +WA3(I-1) 1 *((CC(M1,I-1,1,K)+TR12*(CC(M1,I-1,3,K)+CC(M1,IC-1,2,K)) 1 +TR11*(CC(M1,I-1,5,K)+CC(M1,IC-1,4,K)))+(TI12*(CC(M1,I,3,K) 1 +CC(M1,IC,2,K))-TI11*(CC(M1,I,5,K)+CC(M1,IC,4,K)))) CH(M2,I-1,K,5) = WA4(I-2) 1 *((CC(M1,I-1,1,K)+TR11*(CC(M1,I-1,3,K)+CC(M1,IC-1,2,K)) 1 +TR12*(CC(M1,I-1,5,K)+CC(M1,IC-1,4,K)))+(TI11*(CC(M1,I,3,K) 1 +CC(M1,IC,2,K))+TI12*(CC(M1,I,5,K)+CC(M1,IC,4,K)))) 1 -WA4(I-1) 1 *((CC(M1,I,1,K)+TR11*(CC(M1,I,3,K)-CC(M1,IC,2,K)) 1 +TR12*(CC(M1,I,5,K)-CC(M1,IC,4,K)))-(TI11*(CC(M1,I-1,3,K) 1 -CC(M1,IC-1,2,K))+TI12*(CC(M1,I-1,5,K)-CC(M1,IC-1,4,K)))) CH(M2,I,K,5) = WA4(I-2) 1 *((CC(M1,I,1,K)+TR11*(CC(M1,I,3,K)-CC(M1,IC,2,K)) 1 +TR12*(CC(M1,I,5,K)-CC(M1,IC,4,K)))-(TI11*(CC(M1,I-1,3,K) 1 -CC(M1,IC-1,2,K))+TI12*(CC(M1,I-1,5,K)-CC(M1,IC-1,4,K)))) 1 +WA4(I-1) 1 *((CC(M1,I-1,1,K)+TR11*(CC(M1,I-1,3,K)+CC(M1,IC-1,2,K)) 1 +TR12*(CC(M1,I-1,5,K)+CC(M1,IC-1,4,K)))+(TI11*(CC(M1,I,3,K) 1 +CC(M1,IC,2,K))+TI12*(CC(M1,I,5,K)+CC(M1,IC,4,K)))) 1002 CONTINUE 102 CONTINUE 103 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE MRADBG (M,IDO,IP,L1,IDL1,CC,C1,C2,IM1,IN1, 1 CH,CH2,IM2,IN2,WA) REAL CH(IN2,IDO,L1,IP) ,CC(IN1,IDO,IP,L1) , 1 C1(IN1,IDO,L1,IP) ,C2(IN1,IDL1,IP), 2 CH2(IN2,IDL1,IP) ,WA(IDO) C M1D = (M-1)*IM1+1 M2S = 1-IM2 TPI=2.*4.*ATAN(1.0) ARG = TPI/FLOAT(IP) DCP = COS(ARG) DSP = SIN(ARG) IDP2 = IDO+2 NBD = (IDO-1)/2 IPP2 = IP+2 IPPH = (IP+1)/2 IF (IDO .LT. L1) GO TO 103 DO 102 K=1,L1 DO 101 I=1,IDO M2 = M2S DO 1001 M1=1,M1D,IM1 M2 = M2+IM2 CH(M2,I,K,1) = CC(M1,I,1,K) 1001 CONTINUE 101 CONTINUE 102 CONTINUE GO TO 106 103 DO 105 I=1,IDO DO 104 K=1,L1 M2 = M2S DO 1004 M1=1,M1D,IM1 M2 = M2+IM2 CH(M2,I,K,1) = CC(M1,I,1,K) 1004 CONTINUE 104 CONTINUE 105 CONTINUE 106 DO 108 J=2,IPPH JC = IPP2-J J2 = J+J DO 107 K=1,L1 M2 = M2S DO 1007 M1=1,M1D,IM1 M2 = M2+IM2 CH(M2,1,K,J) = CC(M1,IDO,J2-2,K)+CC(M1,IDO,J2-2,K) CH(M2,1,K,JC) = CC(M1,1,J2-1,K)+CC(M1,1,J2-1,K) 1007 CONTINUE 107 CONTINUE 108 CONTINUE IF (IDO .EQ. 1) GO TO 116 IF (NBD .LT. L1) GO TO 112 DO 111 J=2,IPPH JC = IPP2-J DO 110 K=1,L1 DO 109 I=3,IDO,2 IC = IDP2-I M2 = M2S DO 1009 M1=1,M1D,IM1 M2 = M2+IM2 CH(M2,I-1,K,J) = CC(M1,I-1,2*J-1,K)+CC(M1,IC-1,2*J-2,K) CH(M2,I-1,K,JC) = CC(M1,I-1,2*J-1,K)-CC(M1,IC-1,2*J-2,K) CH(M2,I,K,J) = CC(M1,I,2*J-1,K)-CC(M1,IC,2*J-2,K) CH(M2,I,K,JC) = CC(M1,I,2*J-1,K)+CC(M1,IC,2*J-2,K) 1009 CONTINUE 109 CONTINUE 110 CONTINUE 111 CONTINUE GO TO 116 112 DO 115 J=2,IPPH JC = IPP2-J DO 114 I=3,IDO,2 IC = IDP2-I DO 113 K=1,L1 M2 = M2S DO 1013 M1=1,M1D,IM1 M2 = M2+IM2 CH(M2,I-1,K,J) = CC(M1,I-1,2*J-1,K)+CC(M1,IC-1,2*J-2,K) CH(M2,I-1,K,JC) = CC(M1,I-1,2*J-1,K)-CC(M1,IC-1,2*J-2,K) CH(M2,I,K,J) = CC(M1,I,2*J-1,K)-CC(M1,IC,2*J-2,K) CH(M2,I,K,JC) = CC(M1,I,2*J-1,K)+CC(M1,IC,2*J-2,K) 1013 CONTINUE 113 CONTINUE 114 CONTINUE 115 CONTINUE 116 AR1 = 1. AI1 = 0. DO 120 L=2,IPPH LC = IPP2-L AR1H = DCP*AR1-DSP*AI1 AI1 = DCP*AI1+DSP*AR1 AR1 = AR1H DO 117 IK=1,IDL1 M2 = M2S DO 1017 M1=1,M1D,IM1 M2 = M2+IM2 C2(M1,IK,L) = CH2(M2,IK,1)+AR1*CH2(M2,IK,2) C2(M1,IK,LC) = AI1*CH2(M2,IK,IP) 1017 CONTINUE 117 CONTINUE DC2 = AR1 DS2 = AI1 AR2 = AR1 AI2 = AI1 DO 119 J=3,IPPH JC = IPP2-J AR2H = DC2*AR2-DS2*AI2 AI2 = DC2*AI2+DS2*AR2 AR2 = AR2H DO 118 IK=1,IDL1 M2 = M2S DO 1018 M1=1,M1D,IM1 M2 = M2+IM2 C2(M1,IK,L) = C2(M1,IK,L)+AR2*CH2(M2,IK,J) C2(M1,IK,LC) = C2(M1,IK,LC)+AI2*CH2(M2,IK,JC) 1018 CONTINUE 118 CONTINUE 119 CONTINUE 120 CONTINUE DO 122 J=2,IPPH DO 121 IK=1,IDL1 M2 = M2S DO 1021 M1=1,M1D,IM1 M2 = M2+IM2 CH2(M2,IK,1) = CH2(M2,IK,1)+CH2(M2,IK,J) 1021 CONTINUE 121 CONTINUE 122 CONTINUE DO 124 J=2,IPPH JC = IPP2-J DO 123 K=1,L1 M2 = M2S DO 1023 M1=1,M1D,IM1 M2 = M2+IM2 CH(M2,1,K,J) = C1(M1,1,K,J)-C1(M1,1,K,JC) CH(M2,1,K,JC) = C1(M1,1,K,J)+C1(M1,1,K,JC) 1023 CONTINUE 123 CONTINUE 124 CONTINUE IF (IDO .EQ. 1) GO TO 132 IF (NBD .LT. L1) GO TO 128 DO 127 J=2,IPPH JC = IPP2-J DO 126 K=1,L1 DO 125 I=3,IDO,2 M2 = M2S DO 1025 M1=1,M1D,IM1 M2 = M2+IM2 CH(M2,I-1,K,J) = C1(M1,I-1,K,J)-C1(M1,I,K,JC) CH(M2,I-1,K,JC) = C1(M1,I-1,K,J)+C1(M1,I,K,JC) CH(M2,I,K,J) = C1(M1,I,K,J)+C1(M1,I-1,K,JC) CH(M2,I,K,JC) = C1(M1,I,K,J)-C1(M1,I-1,K,JC) 1025 CONTINUE 125 CONTINUE 126 CONTINUE 127 CONTINUE GO TO 132 128 DO 131 J=2,IPPH JC = IPP2-J DO 130 I=3,IDO,2 DO 129 K=1,L1 M2 = M2S DO 1029 M1=1,M1D,IM1 M2 = M2+IM2 CH(M2,I-1,K,J) = C1(M1,I-1,K,J)-C1(M1,I,K,JC) CH(M2,I-1,K,JC) = C1(M1,I-1,K,J)+C1(M1,I,K,JC) CH(M2,I,K,J) = C1(M1,I,K,J)+C1(M1,I-1,K,JC) CH(M2,I,K,JC) = C1(M1,I,K,J)-C1(M1,I-1,K,JC) 1029 CONTINUE 129 CONTINUE 130 CONTINUE 131 CONTINUE 132 CONTINUE IF (IDO .EQ. 1) RETURN DO 133 IK=1,IDL1 M2 = M2S DO 1033 M1=1,M1D,IM1 M2 = M2+IM2 C2(M1,IK,1) = CH2(M2,IK,1) 1033 CONTINUE 133 CONTINUE DO 135 J=2,IP DO 134 K=1,L1 M2 = M2S DO 1034 M1=1,M1D,IM1 M2 = M2+IM2 C1(M1,1,K,J) = CH(M2,1,K,J) 1034 CONTINUE 134 CONTINUE 135 CONTINUE IF (NBD .GT. L1) GO TO 139 IS = -IDO DO 138 J=2,IP IS = IS+IDO IDIJ = IS DO 137 I=3,IDO,2 IDIJ = IDIJ+2 DO 136 K=1,L1 M2 = M2S DO 1036 M1=1,M1D,IM1 M2 = M2+IM2 C1(M1,I-1,K,J) = WA(IDIJ-1)*CH(M2,I-1,K,J)-WA(IDIJ)* 1 CH(M2,I,K,J) C1(M1,I,K,J) = WA(IDIJ-1)*CH(M2,I,K,J)+WA(IDIJ)* 1 CH(M2,I-1,K,J) 1036 CONTINUE 136 CONTINUE 137 CONTINUE 138 CONTINUE GO TO 143 139 IS = -IDO DO 142 J=2,IP IS = IS+IDO DO 141 K=1,L1 IDIJ = IS DO 140 I=3,IDO,2 IDIJ = IDIJ+2 M2 = M2S DO 1040 M1=1,M1D,IM1 M2 = M2+IM2 C1(M1,I-1,K,J) = WA(IDIJ-1)*CH(M2,I-1,K,J)-WA(IDIJ)* 1 CH(M2,I,K,J) C1(M1,I,K,J) = WA(IDIJ-1)*CH(M2,I,K,J)+WA(IDIJ)* 1 CH(M2,I-1,K,J) 1040 CONTINUE 140 CONTINUE 141 CONTINUE 142 CONTINUE 143 RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE MRADF2 (M,IDO,L1,CC,IM1,IN1,CH,IM2,IN2,WA1) REAL CH(IN2,IDO,2,L1) ,CC(IN1,IDO,L1,2) , WA1(IDO) C M1D = (M-1)*IM1+1 M2S = 1-IM2 DO 101 K=1,L1 M2 = M2S DO 1001 M1=1,M1D,IM1 M2 = M2+IM2 CH(M2,1,1,K) = CC(M1,1,K,1)+CC(M1,1,K,2) CH(M2,IDO,2,K) = CC(M1,1,K,1)-CC(M1,1,K,2) 1001 CONTINUE 101 CONTINUE IF (IDO-2) 107,105,102 102 IDP2 = IDO+2 DO 104 K=1,L1 DO 103 I=3,IDO,2 IC = IDP2-I M2 = M2S DO 1003 M1=1,M1D,IM1 M2 = M2+IM2 CH(M2,I,1,K) = CC(M1,I,K,1)+(WA1(I-2)*CC(M1,I,K,2)- 1 WA1(I-1)*CC(M1,I-1,K,2)) CH(M2,IC,2,K) = (WA1(I-2)*CC(M1,I,K,2)-WA1(I-1)* 1 CC(M1,I-1,K,2))-CC(M1,I,K,1) CH(M2,I-1,1,K) = CC(M1,I-1,K,1)+(WA1(I-2)*CC(M1,I-1,K,2)+ 1 WA1(I-1)*CC(M1,I,K,2)) CH(M2,IC-1,2,K) = CC(M1,I-1,K,1)-(WA1(I-2)*CC(M1,I-1,K,2)+ 1 WA1(I-1)*CC(M1,I,K,2)) 1003 CONTINUE 103 CONTINUE 104 CONTINUE IF (MOD(IDO,2) .EQ. 1) RETURN 105 DO 106 K=1,L1 M2 = M2S DO 1006 M1=1,M1D,IM1 M2 = M2+IM2 CH(M2,1,2,K) = -CC(M1,IDO,K,2) CH(M2,IDO,1,K) = CC(M1,IDO,K,1) 1006 CONTINUE 106 CONTINUE 107 RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE MRADF3 (M,IDO,L1,CC,IM1,IN1,CH,IM2,IN2,WA1,WA2) REAL CH(IN2,IDO,3,L1) ,CC(IN1,IDO,L1,3) , 1 WA1(IDO) ,WA2(IDO) C M1D = (M-1)*IM1+1 M2S = 1-IM2 ARG=2.*4.*ATAN(1.0)/3. TAUR=COS(ARG) TAUI=SIN(ARG) DO 101 K=1,L1 M2 = M2S DO 1001 M1=1,M1D,IM1 M2 = M2+IM2 CH(M2,1,1,K) = CC(M1,1,K,1)+(CC(M1,1,K,2)+CC(M1,1,K,3)) CH(M2,1,3,K) = TAUI*(CC(M1,1,K,3)-CC(M1,1,K,2)) CH(M2,IDO,2,K) = CC(M1,1,K,1)+TAUR* 1 (CC(M1,1,K,2)+CC(M1,1,K,3)) 1001 CONTINUE 101 CONTINUE IF (IDO .EQ. 1) RETURN IDP2 = IDO+2 DO 103 K=1,L1 DO 102 I=3,IDO,2 IC = IDP2-I M2 = M2S DO 1002 M1=1,M1D,IM1 M2 = M2+IM2 CH(M2,I-1,1,K) = CC(M1,I-1,K,1)+((WA1(I-2)*CC(M1,I-1,K,2)+ 1 WA1(I-1)*CC(M1,I,K,2))+(WA2(I-2)*CC(M1,I-1,K,3)+WA2(I-1)* 1 CC(M1,I,K,3))) CH(M2,I,1,K) = CC(M1,I,K,1)+((WA1(I-2)*CC(M1,I,K,2)- 1 WA1(I-1)*CC(M1,I-1,K,2))+(WA2(I-2)*CC(M1,I,K,3)-WA2(I-1)* 1 CC(M1,I-1,K,3))) CH(M2,I-1,3,K) = (CC(M1,I-1,K,1)+TAUR*((WA1(I-2)* 1 CC(M1,I-1,K,2)+WA1(I-1)*CC(M1,I,K,2))+(WA2(I-2)* 1 CC(M1,I-1,K,3)+WA2(I-1)*CC(M1,I,K,3))))+(TAUI*((WA1(I-2)* 1 CC(M1,I,K,2)-WA1(I-1)*CC(M1,I-1,K,2))-(WA2(I-2)* 1 CC(M1,I,K,3)-WA2(I-1)*CC(M1,I-1,K,3)))) CH(M2,IC-1,2,K) = (CC(M1,I-1,K,1)+TAUR*((WA1(I-2)* 1 CC(M1,I-1,K,2)+WA1(I-1)*CC(M1,I,K,2))+(WA2(I-2)* 1 CC(M1,I-1,K,3)+WA2(I-1)*CC(M1,I,K,3))))-(TAUI*((WA1(I-2)* 1 CC(M1,I,K,2)-WA1(I-1)*CC(M1,I-1,K,2))-(WA2(I-2)* 1 CC(M1,I,K,3)-WA2(I-1)*CC(M1,I-1,K,3)))) CH(M2,I,3,K) = (CC(M1,I,K,1)+TAUR*((WA1(I-2)*CC(M1,I,K,2)- 1 WA1(I-1)*CC(M1,I-1,K,2))+(WA2(I-2)*CC(M1,I,K,3)-WA2(I-1)* 1 CC(M1,I-1,K,3))))+(TAUI*((WA2(I-2)*CC(M1,I-1,K,3)+WA2(I-1)* 1 CC(M1,I,K,3))-(WA1(I-2)*CC(M1,I-1,K,2)+WA1(I-1)* 1 CC(M1,I,K,2)))) CH(M2,IC,2,K) = (TAUI*((WA2(I-2)*CC(M1,I-1,K,3)+WA2(I-1)* 1 CC(M1,I,K,3))-(WA1(I-2)*CC(M1,I-1,K,2)+WA1(I-1)* 1 CC(M1,I,K,2))))-(CC(M1,I,K,1)+TAUR*((WA1(I-2)*CC(M1,I,K,2)- 1 WA1(I-1)*CC(M1,I-1,K,2))+(WA2(I-2)*CC(M1,I,K,3)-WA2(I-1)* 1 CC(M1,I-1,K,3)))) 1002 CONTINUE 102 CONTINUE 103 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE MRADF4 (M,IDO,L1,CC,IM1,IN1,CH,IM2,IN2,WA1,WA2,WA3) REAL CC(IN1,IDO,L1,4) ,CH(IN2,IDO,4,L1) , 1 WA1(IDO) ,WA2(IDO) ,WA3(IDO) C HSQT2=SQRT(2.)/2. M1D = (M-1)*IM1+1 M2S = 1-IM2 DO 101 K=1,L1 M2 = M2S DO 1001 M1=1,M1D,IM1 M2 = M2+IM2 CH(M2,1,1,K) = (CC(M1,1,K,2)+CC(M1,1,K,4)) 1 +(CC(M1,1,K,1)+CC(M1,1,K,3)) CH(M2,IDO,4,K) = (CC(M1,1,K,1)+CC(M1,1,K,3)) 1 -(CC(M1,1,K,2)+CC(M1,1,K,4)) CH(M2,IDO,2,K) = CC(M1,1,K,1)-CC(M1,1,K,3) CH(M2,1,3,K) = CC(M1,1,K,4)-CC(M1,1,K,2) 1001 CONTINUE 101 CONTINUE IF (IDO-2) 107,105,102 102 IDP2 = IDO+2 DO 104 K=1,L1 DO 103 I=3,IDO,2 IC = IDP2-I M2 = M2S DO 1003 M1=1,M1D,IM1 M2 = M2+IM2 CH(M2,I-1,1,K) = ((WA1(I-2)*CC(M1,I-1,K,2)+WA1(I-1)* 1 CC(M1,I,K,2))+(WA3(I-2)*CC(M1,I-1,K,4)+WA3(I-1)* 1 CC(M1,I,K,4)))+(CC(M1,I-1,K,1)+(WA2(I-2)*CC(M1,I-1,K,3)+ 1 WA2(I-1)*CC(M1,I,K,3))) CH(M2,IC-1,4,K) = (CC(M1,I-1,K,1)+(WA2(I-2)*CC(M1,I-1,K,3)+ 1 WA2(I-1)*CC(M1,I,K,3)))-((WA1(I-2)*CC(M1,I-1,K,2)+ 1 WA1(I-1)*CC(M1,I,K,2))+(WA3(I-2)*CC(M1,I-1,K,4)+ 1 WA3(I-1)*CC(M1,I,K,4))) CH(M2,I,1,K) = ((WA1(I-2)*CC(M1,I,K,2)-WA1(I-1)* 1 CC(M1,I-1,K,2))+(WA3(I-2)*CC(M1,I,K,4)-WA3(I-1)* 1 CC(M1,I-1,K,4)))+(CC(M1,I,K,1)+(WA2(I-2)*CC(M1,I,K,3)- 1 WA2(I-1)*CC(M1,I-1,K,3))) CH(M2,IC,4,K) = ((WA1(I-2)*CC(M1,I,K,2)-WA1(I-1)* 1 CC(M1,I-1,K,2))+(WA3(I-2)*CC(M1,I,K,4)-WA3(I-1)* 1 CC(M1,I-1,K,4)))-(CC(M1,I,K,1)+(WA2(I-2)*CC(M1,I,K,3)- 1 WA2(I-1)*CC(M1,I-1,K,3))) CH(M2,I-1,3,K) = ((WA1(I-2)*CC(M1,I,K,2)-WA1(I-1)* 1 CC(M1,I-1,K,2))-(WA3(I-2)*CC(M1,I,K,4)-WA3(I-1)* 1 CC(M1,I-1,K,4)))+(CC(M1,I-1,K,1)-(WA2(I-2)*CC(M1,I-1,K,3)+ 1 WA2(I-1)*CC(M1,I,K,3))) CH(M2,IC-1,2,K) = (CC(M1,I-1,K,1)-(WA2(I-2)*CC(M1,I-1,K,3)+ 1 WA2(I-1)*CC(M1,I,K,3)))-((WA1(I-2)*CC(M1,I,K,2)-WA1(I-1)* 1 CC(M1,I-1,K,2))-(WA3(I-2)*CC(M1,I,K,4)-WA3(I-1)* 1 CC(M1,I-1,K,4))) CH(M2,I,3,K) = ((WA3(I-2)*CC(M1,I-1,K,4)+WA3(I-1)* 1 CC(M1,I,K,4))-(WA1(I-2)*CC(M1,I-1,K,2)+WA1(I-1)* 1 CC(M1,I,K,2)))+(CC(M1,I,K,1)-(WA2(I-2)*CC(M1,I,K,3)- 1 WA2(I-1)*CC(M1,I-1,K,3))) CH(M2,IC,2,K) = ((WA3(I-2)*CC(M1,I-1,K,4)+WA3(I-1)* 1 CC(M1,I,K,4))-(WA1(I-2)*CC(M1,I-1,K,2)+WA1(I-1)* 1 CC(M1,I,K,2)))-(CC(M1,I,K,1)-(WA2(I-2)*CC(M1,I,K,3)- 1 WA2(I-1)*CC(M1,I-1,K,3))) 1003 CONTINUE 103 CONTINUE 104 CONTINUE IF (MOD(IDO,2) .EQ. 1) RETURN 105 CONTINUE DO 106 K=1,L1 M2 = M2S DO 1006 M1=1,M1D,IM1 M2 = M2+IM2 CH(M2,IDO,1,K) = (HSQT2*(CC(M1,IDO,K,2)-CC(M1,IDO,K,4)))+ 1 CC(M1,IDO,K,1) CH(M2,IDO,3,K) = CC(M1,IDO,K,1)-(HSQT2*(CC(M1,IDO,K,2)- 1 CC(M1,IDO,K,4))) CH(M2,1,2,K) = (-HSQT2*(CC(M1,IDO,K,2)+CC(M1,IDO,K,4)))- 1 CC(M1,IDO,K,3) CH(M2,1,4,K) = (-HSQT2*(CC(M1,IDO,K,2)+CC(M1,IDO,K,4)))+ 1 CC(M1,IDO,K,3) 1006 CONTINUE 106 CONTINUE 107 RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE MRADF5 (M,IDO,L1,CC,IM1,IN1,CH,IM2,IN2, 1 WA1,WA2,WA3,WA4) REAL CC(IN1,IDO,L1,5) ,CH(IN2,IDO,5,L1) , 1 WA1(IDO) ,WA2(IDO) ,WA3(IDO) ,WA4(IDO) C M1D = (M-1)*IM1+1 M2S = 1-IM2 ARG=2.*4.*ATAN(1.0)/5. TR11=COS(ARG) TI11=SIN(ARG) TR12=COS(2.*ARG) TI12=SIN(2.*ARG) DO 101 K=1,L1 M2 = M2S DO 1001 M1=1,M1D,IM1 M2 = M2+IM2 CH(M2,1,1,K) = CC(M1,1,K,1)+(CC(M1,1,K,5)+CC(M1,1,K,2))+ 1 (CC(M1,1,K,4)+CC(M1,1,K,3)) CH(M2,IDO,2,K) = CC(M1,1,K,1)+TR11*(CC(M1,1,K,5)+CC(M1,1,K,2))+ 1 TR12*(CC(M1,1,K,4)+CC(M1,1,K,3)) CH(M2,1,3,K) = TI11*(CC(M1,1,K,5)-CC(M1,1,K,2))+TI12* 1 (CC(M1,1,K,4)-CC(M1,1,K,3)) CH(M2,IDO,4,K) = CC(M1,1,K,1)+TR12*(CC(M1,1,K,5)+CC(M1,1,K,2))+ 1 TR11*(CC(M1,1,K,4)+CC(M1,1,K,3)) CH(M2,1,5,K) = TI12*(CC(M1,1,K,5)-CC(M1,1,K,2))-TI11* 1 (CC(M1,1,K,4)-CC(M1,1,K,3)) 1001 CONTINUE 101 CONTINUE IF (IDO .EQ. 1) RETURN IDP2 = IDO+2 DO 103 K=1,L1 DO 102 I=3,IDO,2 IC = IDP2-I M2 = M2S DO 1002 M1=1,M1D,IM1 M2 = M2+IM2 CH(M2,I-1,1,K) = CC(M1,I-1,K,1)+((WA1(I-2)*CC(M1,I-1,K,2)+ 1 WA1(I-1)*CC(M1,I,K,2))+(WA4(I-2)*CC(M1,I-1,K,5)+WA4(I-1)* 1 CC(M1,I,K,5)))+((WA2(I-2)*CC(M1,I-1,K,3)+WA2(I-1)* 1 CC(M1,I,K,3))+(WA3(I-2)*CC(M1,I-1,K,4)+ 1 WA3(I-1)*CC(M1,I,K,4))) CH(M2,I,1,K) = CC(M1,I,K,1)+((WA1(I-2)*CC(M1,I,K,2)- 1 WA1(I-1)*CC(M1,I-1,K,2))+(WA4(I-2)*CC(M1,I,K,5)-WA4(I-1)* 1 CC(M1,I-1,K,5)))+((WA2(I-2)*CC(M1,I,K,3)-WA2(I-1)* 1 CC(M1,I-1,K,3))+(WA3(I-2)*CC(M1,I,K,4)-WA3(I-1)* 1 CC(M1,I-1,K,4))) CH(M2,I-1,3,K) = CC(M1,I-1,K,1)+TR11* 1 ( WA1(I-2)*CC(M1,I-1,K,2)+WA1(I-1)*CC(M1,I,K,2) 1 +WA4(I-2)*CC(M1,I-1,K,5)+WA4(I-1)*CC(M1,I,K,5))+TR12* 1 ( WA2(I-2)*CC(M1,I-1,K,3)+WA2(I-1)*CC(M1,I,K,3) 1 +WA3(I-2)*CC(M1,I-1,K,4)+WA3(I-1)*CC(M1,I,K,4))+TI11* 1 ( WA1(I-2)*CC(M1,I,K,2)-WA1(I-1)*CC(M1,I-1,K,2) 1 -(WA4(I-2)*CC(M1,I,K,5)-WA4(I-1)*CC(M1,I-1,K,5)))+TI12* 1 ( WA2(I-2)*CC(M1,I,K,3)-WA2(I-1)*CC(M1,I-1,K,3) 1 -(WA3(I-2)*CC(M1,I,K,4)-WA3(I-1)*CC(M1,I-1,K,4))) CH(M2,IC-1,2,K) = CC(M1,I-1,K,1)+TR11* 1 ( WA1(I-2)*CC(M1,I-1,K,2)+WA1(I-1)*CC(M1,I,K,2) 1 +WA4(I-2)*CC(M1,I-1,K,5)+WA4(I-1)*CC(M1,I,K,5))+TR12* 1 ( WA2(I-2)*CC(M1,I-1,K,3)+WA2(I-1)*CC(M1,I,K,3) 1 +WA3(I-2)*CC(M1,I-1,K,4)+WA3(I-1)*CC(M1,I,K,4))-(TI11* 1 ( WA1(I-2)*CC(M1,I,K,2)-WA1(I-1)*CC(M1,I-1,K,2) 1 -(WA4(I-2)*CC(M1,I,K,5)-WA4(I-1)*CC(M1,I-1,K,5)))+TI12* 1 ( WA2(I-2)*CC(M1,I,K,3)-WA2(I-1)*CC(M1,I-1,K,3) 1 -(WA3(I-2)*CC(M1,I,K,4)-WA3(I-1)*CC(M1,I-1,K,4)))) CH(M2,I,3,K) = (CC(M1,I,K,1)+TR11*((WA1(I-2)*CC(M1,I,K,2)- 1 WA1(I-1)*CC(M1,I-1,K,2))+(WA4(I-2)*CC(M1,I,K,5)-WA4(I-1)* 1 CC(M1,I-1,K,5)))+TR12*((WA2(I-2)*CC(M1,I,K,3)-WA2(I-1)* 1 CC(M1,I-1,K,3))+(WA3(I-2)*CC(M1,I,K,4)-WA3(I-1)* 1 CC(M1,I-1,K,4))))+(TI11*((WA4(I-2)*CC(M1,I-1,K,5)+ 1 WA4(I-1)*CC(M1,I,K,5))-(WA1(I-2)*CC(M1,I-1,K,2)+WA1(I-1)* 1 CC(M1,I,K,2)))+TI12*((WA3(I-2)*CC(M1,I-1,K,4)+WA3(I-1)* 1 CC(M1,I,K,4))-(WA2(I-2)*CC(M1,I-1,K,3)+WA2(I-1)* 1 CC(M1,I,K,3)))) CH(M2,IC,2,K) = (TI11*((WA4(I-2)*CC(M1,I-1,K,5)+WA4(I-1)* 1 CC(M1,I,K,5))-(WA1(I-2)*CC(M1,I-1,K,2)+WA1(I-1)* 1 CC(M1,I,K,2)))+TI12*((WA3(I-2)*CC(M1,I-1,K,4)+WA3(I-1)* 1 CC(M1,I,K,4))-(WA2(I-2)*CC(M1,I-1,K,3)+WA2(I-1)* 1 CC(M1,I,K,3))))-(CC(M1,I,K,1)+TR11*((WA1(I-2)*CC(M1,I,K,2)- 1 WA1(I-1)*CC(M1,I-1,K,2))+(WA4(I-2)*CC(M1,I,K,5)-WA4(I-1)* 1 CC(M1,I-1,K,5)))+TR12*((WA2(I-2)*CC(M1,I,K,3)-WA2(I-1)* 1 CC(M1,I-1,K,3))+(WA3(I-2)*CC(M1,I,K,4)-WA3(I-1)* 1 CC(M1,I-1,K,4)))) CH(M2,I-1,5,K) = (CC(M1,I-1,K,1)+TR12*((WA1(I-2)* 1 CC(M1,I-1,K,2)+WA1(I-1)*CC(M1,I,K,2))+(WA4(I-2)* 1 CC(M1,I-1,K,5)+WA4(I-1)*CC(M1,I,K,5)))+TR11*((WA2(I-2)* 1 CC(M1,I-1,K,3)+WA2(I-1)*CC(M1,I,K,3))+(WA3(I-2)* 1 CC(M1,I-1,K,4)+WA3(I-1)*CC(M1,I,K,4))))+(TI12*((WA1(I-2)* 1 CC(M1,I,K,2)-WA1(I-1)*CC(M1,I-1,K,2))-(WA4(I-2)* 1 CC(M1,I,K,5)-WA4(I-1)*CC(M1,I-1,K,5)))-TI11*((WA2(I-2)* 1 CC(M1,I,K,3)-WA2(I-1)*CC(M1,I-1,K,3))-(WA3(I-2)* 1 CC(M1,I,K,4)-WA3(I-1)*CC(M1,I-1,K,4)))) CH(M2,IC-1,4,K) = (CC(M1,I-1,K,1)+TR12*((WA1(I-2)* 1 CC(M1,I-1,K,2)+WA1(I-1)*CC(M1,I,K,2))+(WA4(I-2)* 1 CC(M1,I-1,K,5)+WA4(I-1)*CC(M1,I,K,5)))+TR11*((WA2(I-2)* 1 CC(M1,I-1,K,3)+WA2(I-1)*CC(M1,I,K,3))+(WA3(I-2)* 1 CC(M1,I-1,K,4)+WA3(I-1)*CC(M1,I,K,4))))-(TI12*((WA1(I-2)* 1 CC(M1,I,K,2)-WA1(I-1)*CC(M1,I-1,K,2))-(WA4(I-2)* 1 CC(M1,I,K,5)-WA4(I-1)*CC(M1,I-1,K,5)))-TI11*((WA2(I-2)* 1 CC(M1,I,K,3)-WA2(I-1)*CC(M1,I-1,K,3))-(WA3(I-2)* 1 CC(M1,I,K,4)-WA3(I-1)*CC(M1,I-1,K,4)))) CH(M2,I,5,K) = (CC(M1,I,K,1)+TR12*((WA1(I-2)*CC(M1,I,K,2)- 1 WA1(I-1)*CC(M1,I-1,K,2))+(WA4(I-2)*CC(M1,I,K,5)-WA4(I-1)* 1 CC(M1,I-1,K,5)))+TR11*((WA2(I-2)*CC(M1,I,K,3)-WA2(I-1)* 1 CC(M1,I-1,K,3))+(WA3(I-2)*CC(M1,I,K,4)-WA3(I-1)* 1 CC(M1,I-1,K,4))))+(TI12*((WA4(I-2)*CC(M1,I-1,K,5)+ 1 WA4(I-1)*CC(M1,I,K,5))-(WA1(I-2)*CC(M1,I-1,K,2)+WA1(I-1)* 1 CC(M1,I,K,2)))-TI11*((WA3(I-2)*CC(M1,I-1,K,4)+WA3(I-1)* 1 CC(M1,I,K,4))-(WA2(I-2)*CC(M1,I-1,K,3)+WA2(I-1)* 1 CC(M1,I,K,3)))) CH(M2,IC,4,K) = (TI12*((WA4(I-2)*CC(M1,I-1,K,5)+WA4(I-1)* 1 CC(M1,I,K,5))-(WA1(I-2)*CC(M1,I-1,K,2)+WA1(I-1)* 1 CC(M1,I,K,2)))-TI11*((WA3(I-2)*CC(M1,I-1,K,4)+WA3(I-1)* 1 CC(M1,I,K,4))-(WA2(I-2)*CC(M1,I-1,K,3)+WA2(I-1)* 1 CC(M1,I,K,3))))-(CC(M1,I,K,1)+TR12*((WA1(I-2)*CC(M1,I,K,2)- 1 WA1(I-1)*CC(M1,I-1,K,2))+(WA4(I-2)*CC(M1,I,K,5)-WA4(I-1)* 1 CC(M1,I-1,K,5)))+TR11*((WA2(I-2)*CC(M1,I,K,3)-WA2(I-1)* 1 CC(M1,I-1,K,3))+(WA3(I-2)*CC(M1,I,K,4)-WA3(I-1)* 1 CC(M1,I-1,K,4)))) 1002 CONTINUE 102 CONTINUE 103 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE MRADFG (M,IDO,IP,L1,IDL1,CC,C1,C2,IM1,IN1, 1 CH,CH2,IM2,IN2,WA) REAL CH(IN2,IDO,L1,IP) ,CC(IN1,IDO,IP,L1), 1 C1(IN1,IDO,L1,IP) ,C2(IN1,IDL1,IP), 2 CH2(IN2,IDL1,IP) ,WA(IDO) C M1D = (M-1)*IM1+1 M2S = 1-IM2 TPI=2.*4.*ATAN(1.0) ARG = TPI/FLOAT(IP) DCP = COS(ARG) DSP = SIN(ARG) IPPH = (IP+1)/2 IPP2 = IP+2 IDP2 = IDO+2 NBD = (IDO-1)/2 IF (IDO .EQ. 1) GO TO 119 DO 101 IK=1,IDL1 M2 = M2S DO 1001 M1=1,M1D,IM1 M2 = M2+IM2 CH2(M2,IK,1) = C2(M1,IK,1) 1001 CONTINUE 101 CONTINUE DO 103 J=2,IP DO 102 K=1,L1 M2 = M2S DO 1002 M1=1,M1D,IM1 M2 = M2+IM2 CH(M2,1,K,J) = C1(M1,1,K,J) 1002 CONTINUE 102 CONTINUE 103 CONTINUE IF (NBD .GT. L1) GO TO 107 IS = -IDO DO 106 J=2,IP IS = IS+IDO IDIJ = IS DO 105 I=3,IDO,2 IDIJ = IDIJ+2 DO 104 K=1,L1 M2 = M2S DO 1004 M1=1,M1D,IM1 M2 = M2+IM2 CH(M2,I-1,K,J) = WA(IDIJ-1)*C1(M1,I-1,K,J)+WA(IDIJ) 1 *C1(M1,I,K,J) CH(M2,I,K,J) = WA(IDIJ-1)*C1(M1,I,K,J)-WA(IDIJ) 1 *C1(M1,I-1,K,J) 1004 CONTINUE 104 CONTINUE 105 CONTINUE 106 CONTINUE GO TO 111 107 IS = -IDO DO 110 J=2,IP IS = IS+IDO DO 109 K=1,L1 IDIJ = IS DO 108 I=3,IDO,2 IDIJ = IDIJ+2 M2 = M2S DO 1008 M1=1,M1D,IM1 M2 = M2+IM2 CH(M2,I-1,K,J) = WA(IDIJ-1)*C1(M1,I-1,K,J)+WA(IDIJ) 1 *C1(M1,I,K,J) CH(M2,I,K,J) = WA(IDIJ-1)*C1(M1,I,K,J)-WA(IDIJ) 1 *C1(M1,I-1,K,J) 1008 CONTINUE 108 CONTINUE 109 CONTINUE 110 CONTINUE 111 IF (NBD .LT. L1) GO TO 115 DO 114 J=2,IPPH JC = IPP2-J DO 113 K=1,L1 DO 112 I=3,IDO,2 M2 = M2S DO 1012 M1=1,M1D,IM1 M2 = M2+IM2 C1(M1,I-1,K,J) = CH(M2,I-1,K,J)+CH(M2,I-1,K,JC) C1(M1,I-1,K,JC) = CH(M2,I,K,J)-CH(M2,I,K,JC) C1(M1,I,K,J) = CH(M2,I,K,J)+CH(M2,I,K,JC) C1(M1,I,K,JC) = CH(M2,I-1,K,JC)-CH(M2,I-1,K,J) 1012 CONTINUE 112 CONTINUE 113 CONTINUE 114 CONTINUE GO TO 121 115 DO 118 J=2,IPPH JC = IPP2-J DO 117 I=3,IDO,2 DO 116 K=1,L1 M2 = M2S DO 1016 M1=1,M1D,IM1 M2 = M2+IM2 C1(M1,I-1,K,J) = CH(M2,I-1,K,J)+CH(M2,I-1,K,JC) C1(M1,I-1,K,JC) = CH(M2,I,K,J)-CH(M2,I,K,JC) C1(M1,I,K,J) = CH(M2,I,K,J)+CH(M2,I,K,JC) C1(M1,I,K,JC) = CH(M2,I-1,K,JC)-CH(M2,I-1,K,J) 1016 CONTINUE 116 CONTINUE 117 CONTINUE 118 CONTINUE GO TO 121 119 DO 120 IK=1,IDL1 M2 = M2S DO 1020 M1=1,M1D,IM1 M2 = M2+IM2 C2(M1,IK,1) = CH2(M2,IK,1) 1020 CONTINUE 120 CONTINUE 121 DO 123 J=2,IPPH JC = IPP2-J DO 122 K=1,L1 M2 = M2S DO 1022 M1=1,M1D,IM1 M2 = M2+IM2 C1(M1,1,K,J) = CH(M2,1,K,J)+CH(M2,1,K,JC) C1(M1,1,K,JC) = CH(M2,1,K,JC)-CH(M2,1,K,J) 1022 CONTINUE 122 CONTINUE 123 CONTINUE C AR1 = 1. AI1 = 0. DO 127 L=2,IPPH LC = IPP2-L AR1H = DCP*AR1-DSP*AI1 AI1 = DCP*AI1+DSP*AR1 AR1 = AR1H DO 124 IK=1,IDL1 M2 = M2S DO 1024 M1=1,M1D,IM1 M2 = M2+IM2 CH2(M2,IK,L) = C2(M1,IK,1)+AR1*C2(M1,IK,2) CH2(M2,IK,LC) = AI1*C2(M1,IK,IP) 1024 CONTINUE 124 CONTINUE DC2 = AR1 DS2 = AI1 AR2 = AR1 AI2 = AI1 DO 126 J=3,IPPH JC = IPP2-J AR2H = DC2*AR2-DS2*AI2 AI2 = DC2*AI2+DS2*AR2 AR2 = AR2H DO 125 IK=1,IDL1 M2 = M2S DO 1025 M1=1,M1D,IM1 M2 = M2+IM2 CH2(M2,IK,L) = CH2(M2,IK,L)+AR2*C2(M1,IK,J) CH2(M2,IK,LC) = CH2(M2,IK,LC)+AI2*C2(M1,IK,JC) 1025 CONTINUE 125 CONTINUE 126 CONTINUE 127 CONTINUE DO 129 J=2,IPPH DO 128 IK=1,IDL1 M2 = M2S DO 1028 M1=1,M1D,IM1 M2 = M2+IM2 CH2(M2,IK,1) = CH2(M2,IK,1)+C2(M1,IK,J) 1028 CONTINUE 128 CONTINUE 129 CONTINUE C IF (IDO .LT. L1) GO TO 132 DO 131 K=1,L1 DO 130 I=1,IDO M2 = M2S DO 1030 M1=1,M1D,IM1 M2 = M2+IM2 CC(M1,I,1,K) = CH(M2,I,K,1) 1030 CONTINUE 130 CONTINUE 131 CONTINUE GO TO 135 132 DO 134 I=1,IDO DO 133 K=1,L1 M2 = M2S DO 1033 M1=1,M1D,IM1 M2 = M2+IM2 CC(M1,I,1,K) = CH(M2,I,K,1) 1033 CONTINUE 133 CONTINUE 134 CONTINUE 135 DO 137 J=2,IPPH JC = IPP2-J J2 = J+J DO 136 K=1,L1 M2 = M2S DO 1036 M1=1,M1D,IM1 M2 = M2+IM2 CC(M1,IDO,J2-2,K) = CH(M2,1,K,J) CC(M1,1,J2-1,K) = CH(M2,1,K,JC) 1036 CONTINUE 136 CONTINUE 137 CONTINUE IF (IDO .EQ. 1) RETURN IF (NBD .LT. L1) GO TO 141 DO 140 J=2,IPPH JC = IPP2-J J2 = J+J DO 139 K=1,L1 DO 138 I=3,IDO,2 IC = IDP2-I M2 = M2S DO 1038 M1=1,M1D,IM1 M2 = M2+IM2 CC(M1,I-1,J2-1,K) = CH(M2,I-1,K,J)+CH(M2,I-1,K,JC) CC(M1,IC-1,J2-2,K) = CH(M2,I-1,K,J)-CH(M2,I-1,K,JC) CC(M1,I,J2-1,K) = CH(M2,I,K,J)+CH(M2,I,K,JC) CC(M1,IC,J2-2,K) = CH(M2,I,K,JC)-CH(M2,I,K,J) 1038 CONTINUE 138 CONTINUE 139 CONTINUE 140 CONTINUE RETURN 141 DO 144 J=2,IPPH JC = IPP2-J J2 = J+J DO 143 I=3,IDO,2 IC = IDP2-I DO 142 K=1,L1 M2 = M2S DO 1042 M1=1,M1D,IM1 M2 = M2+IM2 CC(M1,I-1,J2-1,K) = CH(M2,I-1,K,J)+CH(M2,I-1,K,JC) CC(M1,IC-1,J2-2,K) = CH(M2,I-1,K,J)-CH(M2,I-1,K,JC) CC(M1,I,J2-1,K) = CH(M2,I,K,J)+CH(M2,I,K,JC) CC(M1,IC,J2-2,K) = CH(M2,I,K,JC)-CH(M2,I,K,J) 1042 CONTINUE 142 CONTINUE 143 CONTINUE 144 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE MRFTB1 (M,IM,N,IN,C,CH,WA,FAC) REAL CH(M,*), C(IN,*), WA(N) ,FAC(15) C NF = FAC(2) NA = 0 DO 10 K1=1,NF IP = FAC(K1+2) NA = 1-NA IF(IP .LE. 5) GO TO 10 IF(K1 .EQ. NF) GO TO 10 NA = 1-NA 10 CONTINUE HALF = .5 HALFM = -.5 MODN = MOD(N,2) NL = N-2 IF(MODN .NE. 0) NL = N-1 IF (NA .EQ. 0) GO TO 120 M2 = 1-IM DO 117 I=1,M M2 = M2+IM CH(I,1) = C(M2,1) CH(I,N) = C(M2,N) 117 CONTINUE DO 118 J=2,NL,2 M2 = 1-IM DO 118 I=1,M M2 = M2+IM CH(I,J) = HALF*C(M2,J) CH(I,J+1) = HALFM*C(M2,J+1) 118 CONTINUE GO TO 124 120 CONTINUE DO 122 J=2,NL,2 M2 = 1-IM DO 122 I=1,M M2 = M2+IM C(M2,J) = HALF*C(M2,J) C(M2,J+1) = HALFM*C(M2,J+1) 122 CONTINUE 124 L1 = 1 IW = 1 DO 116 K1=1,NF IP = FAC(K1+2) L2 = IP*L1 IDO = N/L2 IDL1 = IDO*L1 IF (IP .NE. 4) GO TO 103 IX2 = IW+IDO IX3 = IX2+IDO IF (NA .NE. 0) GO TO 101 CALL MRADB4 (M,IDO,L1,C,IM,IN,CH,1,M,WA(IW),WA(IX2),WA(IX3)) GO TO 102 101 CALL MRADB4 (M,IDO,L1,CH,1,M,C,IM,IN,WA(IW),WA(IX2),WA(IX3)) 102 NA = 1-NA GO TO 115 103 IF (IP .NE. 2) GO TO 106 IF (NA .NE. 0) GO TO 104 CALL MRADB2 (M,IDO,L1,C,IM,IN,CH,1,M,WA(IW)) GO TO 105 104 CALL MRADB2 (M,IDO,L1,CH,1,M,C,IM,IN,WA(IW)) 105 NA = 1-NA GO TO 115 106 IF (IP .NE. 3) GO TO 109 IX2 = IW+IDO IF (NA .NE. 0) GO TO 107 CALL MRADB3 (M,IDO,L1,C,IM,IN,CH,1,M,WA(IW),WA(IX2)) GO TO 108 107 CALL MRADB3 (M,IDO,L1,CH,1,M,C,IM,IN,WA(IW),WA(IX2)) 108 NA = 1-NA GO TO 115 109 IF (IP .NE. 5) GO TO 112 IX2 = IW+IDO IX3 = IX2+IDO IX4 = IX3+IDO IF (NA .NE. 0) GO TO 110 CALL MRADB5 (M,IDO,L1,C,IM,IN,CH,1,M,WA(IW),WA(IX2), 1 WA(IX3),WA(IX4)) GO TO 111 110 CALL MRADB5 (M,IDO,L1,CH,1,M,C,IM,IN,WA(IW),WA(IX2), 1 WA(IX3),WA(IX4)) 111 NA = 1-NA GO TO 115 112 IF (NA .NE. 0) GO TO 113 CALL MRADBG (M,IDO,IP,L1,IDL1,C,C,C,IM,IN,CH,CH,1, 1 M,WA(IW)) GO TO 114 113 CALL MRADBG (M,IDO,IP,L1,IDL1,CH,CH,CH,1,M,C,C,IM, 1 IN,WA(IW)) 114 IF (IDO .EQ. 1) NA = 1-NA 115 L1 = L2 IW = IW+(IP-1)*IDO 116 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE MRFTF1 (M,IM,N,IN,C,CH,WA,FAC) REAL CH(M,*) ,C(IN,*) ,WA(N) ,FAC(15) C NF = FAC(2) NA = 1 L2 = N IW = N DO 111 K1=1,NF KH = NF-K1 IP = FAC(KH+3) L1 = L2/IP IDO = N/L2 IDL1 = IDO*L1 IW = IW-(IP-1)*IDO NA = 1-NA IF (IP .NE. 4) GO TO 102 IX2 = IW+IDO IX3 = IX2+IDO IF (NA .NE. 0) GO TO 101 CALL MRADF4 (M,IDO,L1,C,IM,IN,CH,1,M,WA(IW),WA(IX2),WA(IX3)) GO TO 110 101 CALL MRADF4 (M,IDO,L1,CH,1,M,C,IM,IN,WA(IW),WA(IX2),WA(IX3)) GO TO 110 102 IF (IP .NE. 2) GO TO 104 IF (NA .NE. 0) GO TO 103 CALL MRADF2 (M,IDO,L1,C,IM,IN,CH,1,M,WA(IW)) GO TO 110 103 CALL MRADF2 (M,IDO,L1,CH,1,M,C,IM,IN,WA(IW)) GO TO 110 104 IF (IP .NE. 3) GO TO 106 IX2 = IW+IDO IF (NA .NE. 0) GO TO 105 CALL MRADF3 (M,IDO,L1,C,IM,IN,CH,1,M,WA(IW),WA(IX2)) GO TO 110 105 CALL MRADF3 (M,IDO,L1,CH,1,M,C,IM,IN,WA(IW),WA(IX2)) GO TO 110 106 IF (IP .NE. 5) GO TO 108 IX2 = IW+IDO IX3 = IX2+IDO IX4 = IX3+IDO IF (NA .NE. 0) GO TO 107 CALL MRADF5(M,IDO,L1,C,IM,IN,CH,1,M,WA(IW),WA(IX2), 1 WA(IX3),WA(IX4)) GO TO 110 107 CALL MRADF5(M,IDO,L1,CH,1,M,C,IM,IN,WA(IW),WA(IX2), 1 WA(IX3),WA(IX4)) GO TO 110 108 IF (IDO .EQ. 1) NA = 1-NA IF (NA .NE. 0) GO TO 109 CALL MRADFG (M,IDO,IP,L1,IDL1,C,C,C,IM,IN,CH,CH,1,M,WA(IW)) NA = 1 GO TO 110 109 CALL MRADFG (M,IDO,IP,L1,IDL1,CH,CH,CH,1,M,C,C,IM,IN,WA(IW)) NA = 0 110 L2 = L1 111 CONTINUE SN = 1./N TSN = 2./N TSNM = -TSN MODN = MOD(N,2) NL = N-2 IF(MODN .NE. 0) NL = N-1 IF (NA .NE. 0) GO TO 120 M2 = 1-IM DO 117 I=1,M M2 = M2+IM C(M2,1) = SN*CH(I,1) 117 CONTINUE DO 118 J=2,NL,2 M2 = 1-IM DO 118 I=1,M M2 = M2+IM C(M2,J) = TSN*CH(I,J) C(M2,J+1) = TSNM*CH(I,J+1) 118 CONTINUE IF(MODN .NE. 0) RETURN M2 = 1-IM DO 119 I=1,M M2 = M2+IM C(M2,N) = SN*CH(I,N) 119 CONTINUE RETURN 120 M2 = 1-IM DO 121 I=1,M M2 = M2+IM C(M2,1) = SN*C(M2,1) 121 CONTINUE DO 122 J=2,NL,2 M2 = 1-IM DO 122 I=1,M M2 = M2+IM C(M2,J) = TSN*C(M2,J) C(M2,J+1) = TSNM*C(M2,J+1) 122 CONTINUE IF(MODN .NE. 0) RETURN M2 = 1-IM DO 123 I=1,M M2 = M2+IM C(M2,N) = SN*C(M2,N) 123 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE MRFTI1 (N,WA,FAC) REAL WA(N) ,FAC(15) INTEGER NTRYH(4) DOUBLE PRECISION TPI,ARGH,ARGLD,ARG DATA NTRYH(1),NTRYH(2),NTRYH(3),NTRYH(4)/4,2,3,5/ C NL = N NF = 0 J = 0 101 J = J+1 IF (J-4) 102,102,103 102 NTRY = NTRYH(J) GO TO 104 103 NTRY = NTRY+2 104 NQ = NL/NTRY NR = NL-NTRY*NQ IF (NR) 101,105,101 105 NF = NF+1 FAC(NF+2) = NTRY NL = NQ IF (NTRY .NE. 2) GO TO 107 IF (NF .EQ. 1) GO TO 107 DO 106 I=2,NF IB = NF-I+2 FAC(IB+2) = FAC(IB+1) 106 CONTINUE FAC(3) = 2 107 IF (NL .NE. 1) GO TO 104 FAC(1) = N FAC(2) = NF TPI = 8.D0*DATAN(1.D0) ARGH = TPI/FLOAT(N) IS = 0 NFM1 = NF-1 L1 = 1 IF (NFM1 .EQ. 0) RETURN DO 110 K1=1,NFM1 IP = FAC(K1+2) LD = 0 L2 = L1*IP IDO = N/L2 IPM = IP-1 DO 109 J=1,IPM LD = LD+L1 I = IS ARGLD = FLOAT(LD)*ARGH FI = 0. DO 108 II=3,IDO,2 I = I+2 FI = FI+1. ARG = FI*ARGLD WA(I-1) = DCOS(ARG) WA(I) = DSIN(ARG) 108 CONTINUE IS = IS+IDO 109 CONTINUE L1 = L2 110 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE MSNTB1(LOT,JUMP,N,INC,X,WSAVE,DSUM,XH,WORK,IER) REAL X(INC,*) ,WSAVE(*) ,XH(LOT,*) DOUBLE PRECISION DSUM(*) IER = 0 LJ = (LOT-1)*JUMP+1 IF (N-2) 200,102,103 102 SRT3S2 = SQRT(3.)/2. DO 112 M=1,LJ,JUMP XHOLD = SRT3S2*(X(M,1)+X(M,2)) X(M,2) = SRT3S2*(X(M,1)-X(M,2)) X(M,1) = XHOLD 112 CONTINUE GO TO 200 103 NP1 = N+1 NS2 = N/2 DO 104 K=1,NS2 KC = NP1-K M1 = 0 DO 114 M=1,LJ,JUMP M1 = M1+1 T1 = X(M,K)-X(M,KC) T2 = WSAVE(K)*(X(M,K)+X(M,KC)) XH(M1,K+1) = T1+T2 XH(M1,KC+1) = T2-T1 114 CONTINUE 104 CONTINUE MODN = MOD(N,2) IF (MODN .EQ. 0) GO TO 124 M1 = 0 DO 123 M=1,LJ,JUMP M1 = M1+1 XH(M1,NS2+2) = 4.*X(M,NS2+1) 123 CONTINUE 124 DO 127 M=1,LOT XH(M,1) = 0. 127 CONTINUE LNXH = LOT-1 + LOT*(NP1-1) + 1 LNSV = NP1 + INT(LOG(REAL(NP1))/LOG(2.)) + 4 LNWK = LOT*NP1 C CALL RFFTMF(LOT,1,NP1,LOT,XH,LNXH,WSAVE(NS2+1),LNSV,WORK, 1 LNWK,IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('MSNTB1',-5) GO TO 200 ENDIF C IF(MOD(NP1,2) .NE. 0) GO TO 30 DO 20 M=1,LOT XH(M,NP1) = XH(M,NP1)+XH(M,NP1) 20 CONTINUE 30 FNP1S4 = FLOAT(NP1)/4. M1 = 0 DO 125 M=1,LJ,JUMP M1 = M1+1 X(M,1) = FNP1S4*XH(M1,1) DSUM(M1) = X(M,1) 125 CONTINUE DO 105 I=3,N,2 M1 = 0 DO 115 M=1,LJ,JUMP M1 = M1+1 X(M,I-1) = FNP1S4*XH(M1,I) DSUM(M1) = DSUM(M1)+FNP1S4*XH(M1,I-1) X(M,I) = DSUM(M1) 115 CONTINUE 105 CONTINUE IF (MODN .NE. 0) GO TO 200 M1 = 0 DO 116 M=1,LJ,JUMP M1 = M1+1 X(M,N) = FNP1S4*XH(M1,N+1) 116 CONTINUE C 200 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE MSNTF1(LOT,JUMP,N,INC,X,WSAVE,DSUM,XH,WORK,IER) REAL X(INC,*) ,WSAVE(*) ,XH(LOT,*) DOUBLE PRECISION DSUM(*) IER = 0 LJ = (LOT-1)*JUMP+1 IF (N-2) 101,102,103 102 SSQRT3 = 1./SQRT(3.) DO 112 M=1,LJ,JUMP XHOLD = SSQRT3*(X(M,1)+X(M,2)) X(M,2) = SSQRT3*(X(M,1)-X(M,2)) X(M,1) = XHOLD 112 CONTINUE 101 GO TO 200 103 NP1 = N+1 NS2 = N/2 DO 104 K=1,NS2 KC = NP1-K M1 = 0 DO 114 M=1,LJ,JUMP M1 = M1 + 1 T1 = X(M,K)-X(M,KC) T2 = WSAVE(K)*(X(M,K)+X(M,KC)) XH(M1,K+1) = T1+T2 XH(M1,KC+1) = T2-T1 114 CONTINUE 104 CONTINUE MODN = MOD(N,2) IF (MODN .EQ. 0) GO TO 124 M1 = 0 DO 123 M=1,LJ,JUMP M1 = M1 + 1 XH(M1,NS2+2) = 4.*X(M,NS2+1) 123 CONTINUE 124 DO 127 M=1,LOT XH(M,1) = 0. 127 CONTINUE LNXH = LOT-1 + LOT*(NP1-1) + 1 LNSV = NP1 + INT(LOG(REAL(NP1))/LOG(2.)) + 4 LNWK = LOT*NP1 C CALL RFFTMF(LOT,1,NP1,LOT,XH,LNXH,WSAVE(NS2+1),LNSV,WORK, 1 LNWK,IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('MSNTF1',-5) GO TO 200 ENDIF C IF(MOD(NP1,2) .NE. 0) GO TO 30 DO 20 M=1,LOT XH(M,NP1) = XH(M,NP1)+XH(M,NP1) 20 CONTINUE 30 SFNP1 = 1./FLOAT(NP1) M1 = 0 DO 125 M=1,LJ,JUMP M1 = M1+1 X(M,1) = .5*XH(M1,1) DSUM(M1) = X(M,1) 125 CONTINUE DO 105 I=3,N,2 M1 = 0 DO 115 M=1,LJ,JUMP M1 = M1+1 X(M,I-1) = .5*XH(M1,I) DSUM(M1) = DSUM(M1)+.5*XH(M1,I-1) X(M,I) = DSUM(M1) 115 CONTINUE 105 CONTINUE IF (MODN .NE. 0) GO TO 200 M1 = 0 DO 116 M=1,LJ,JUMP M1 = M1+1 X(M,N) = .5*XH(M1,N+1) 116 CONTINUE 200 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE R1F2KB (IDO,L1,CC,IN1,CH,IN2,WA1) REAL CC(IN1,IDO,2,L1), CH(IN2,IDO,L1,2), WA1(IDO) C DO 101 K=1,L1 CH(1,1,K,1) = CC(1,1,1,K)+CC(1,IDO,2,K) CH(1,1,K,2) = CC(1,1,1,K)-CC(1,IDO,2,K) 101 CONTINUE IF (IDO-2) 107,105,102 102 IDP2 = IDO+2 DO 104 K=1,L1 DO 103 I=3,IDO,2 IC = IDP2-I CH(1,I-1,K,1) = CC(1,I-1,1,K)+CC(1,IC-1,2,K) CH(1,I,K,1) = CC(1,I,1,K)-CC(1,IC,2,K) CH(1,I-1,K,2) = WA1(I-2)*(CC(1,I-1,1,K)-CC(1,IC-1,2,K)) 1 -WA1(I-1)*(CC(1,I,1,K)+CC(1,IC,2,K)) CH(1,I,K,2) = WA1(I-2)*(CC(1,I,1,K)+CC(1,IC,2,K))+WA1(I-1) 1 *(CC(1,I-1,1,K)-CC(1,IC-1,2,K)) 103 CONTINUE 104 CONTINUE IF (MOD(IDO,2) .EQ. 1) RETURN 105 DO 106 K=1,L1 CH(1,IDO,K,1) = CC(1,IDO,1,K)+CC(1,IDO,1,K) CH(1,IDO,K,2) = -(CC(1,1,2,K)+CC(1,1,2,K)) 106 CONTINUE 107 RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE R1F2KF (IDO,L1,CC,IN1,CH,IN2,WA1) REAL CH(IN2,IDO,2,L1) ,CC(IN1,IDO,L1,2) , WA1(IDO) C DO 101 K=1,L1 CH(1,1,1,K) = CC(1,1,K,1)+CC(1,1,K,2) CH(1,IDO,2,K) = CC(1,1,K,1)-CC(1,1,K,2) 101 CONTINUE IF (IDO-2) 107,105,102 102 IDP2 = IDO+2 DO 104 K=1,L1 DO 103 I=3,IDO,2 IC = IDP2-I CH(1,I,1,K) = CC(1,I,K,1)+(WA1(I-2)*CC(1,I,K,2)- 1 WA1(I-1)*CC(1,I-1,K,2)) CH(1,IC,2,K) = (WA1(I-2)*CC(1,I,K,2)-WA1(I-1)* 1 CC(1,I-1,K,2))-CC(1,I,K,1) CH(1,I-1,1,K) = CC(1,I-1,K,1)+(WA1(I-2)*CC(1,I-1,K,2)+ 1 WA1(I-1)*CC(1,I,K,2)) CH(1,IC-1,2,K) = CC(1,I-1,K,1)-(WA1(I-2)*CC(1,I-1,K,2)+ 1 WA1(I-1)*CC(1,I,K,2)) 103 CONTINUE 104 CONTINUE IF (MOD(IDO,2) .EQ. 1) RETURN 105 DO 106 K=1,L1 CH(1,1,2,K) = -CC(1,IDO,K,2) CH(1,IDO,1,K) = CC(1,IDO,K,1) 106 CONTINUE 107 RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE R1F3KB (IDO,L1,CC,IN1,CH,IN2,WA1,WA2) REAL CC(IN1,IDO,3,L1) ,CH(IN2,IDO,L1,3), 1 WA1(IDO) ,WA2(IDO) C ARG=2.*4.*ATAN(1.0)/3. TAUR=COS(ARG) TAUI=SIN(ARG) DO 101 K=1,L1 CH(1,1,K,1) = CC(1,1,1,K)+2.*CC(1,IDO,2,K) CH(1,1,K,2) = CC(1,1,1,K)+(2.*TAUR)*CC(1,IDO,2,K) 1 -(2.*TAUI)*CC(1,1,3,K) CH(1,1,K,3) = CC(1,1,1,K)+(2.*TAUR)*CC(1,IDO,2,K) 1 +2.*TAUI*CC(1,1,3,K) 101 CONTINUE IF (IDO .EQ. 1) RETURN IDP2 = IDO+2 DO 103 K=1,L1 DO 102 I=3,IDO,2 IC = IDP2-I CH(1,I-1,K,1) = CC(1,I-1,1,K)+(CC(1,I-1,3,K)+CC(1,IC-1,2,K)) CH(1,I,K,1) = CC(1,I,1,K)+(CC(1,I,3,K)-CC(1,IC,2,K)) CH(1,I-1,K,2) = WA1(I-2)* 1 ((CC(1,I-1,1,K)+TAUR*(CC(1,I-1,3,K)+CC(1,IC-1,2,K)))- * (TAUI*(CC(1,I,3,K)+CC(1,IC,2,K)))) 2 -WA1(I-1)* 3 ((CC(1,I,1,K)+TAUR*(CC(1,I,3,K)-CC(1,IC,2,K)))+ * (TAUI*(CC(1,I-1,3,K)-CC(1,IC-1,2,K)))) CH(1,I,K,2) = WA1(I-2)* 4 ((CC(1,I,1,K)+TAUR*(CC(1,I,3,K)-CC(1,IC,2,K)))+ 8 (TAUI*(CC(1,I-1,3,K)-CC(1,IC-1,2,K)))) 5 +WA1(I-1)* 6 ((CC(1,I-1,1,K)+TAUR*(CC(1,I-1,3,K)+CC(1,IC-1,2,K)))- 8 (TAUI*(CC(1,I,3,K)+CC(1,IC,2,K)))) CH(1,I-1,K,3) = WA2(I-2)* 7 ((CC(1,I-1,1,K)+TAUR*(CC(1,I-1,3,K)+CC(1,IC-1,2,K)))+ 8 (TAUI*(CC(1,I,3,K)+CC(1,IC,2,K)))) 8 -WA2(I-1)* 9 ((CC(1,I,1,K)+TAUR*(CC(1,I,3,K)-CC(1,IC,2,K)))- 8 (TAUI*(CC(1,I-1,3,K)-CC(1,IC-1,2,K)))) CH(1,I,K,3) = WA2(I-2)* 1 ((CC(1,I,1,K)+TAUR*(CC(1,I,3,K)-CC(1,IC,2,K)))- 8 (TAUI*(CC(1,I-1,3,K)-CC(1,IC-1,2,K)))) 2 +WA2(I-1)* 3 ((CC(1,I-1,1,K)+TAUR*(CC(1,I-1,3,K)+CC(1,IC-1,2,K)))+ 8 (TAUI*(CC(1,I,3,K)+CC(1,IC,2,K)))) 102 CONTINUE 103 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE R1F3KF (IDO,L1,CC,IN1,CH,IN2,WA1,WA2) REAL CH(IN2,IDO,3,L1) ,CC(IN1,IDO,L1,3) , 1 WA1(IDO) ,WA2(IDO) C ARG=2.*4.*ATAN(1.0)/3. TAUR=COS(ARG) TAUI=SIN(ARG) DO 101 K=1,L1 CH(1,1,1,K) = CC(1,1,K,1)+(CC(1,1,K,2)+CC(1,1,K,3)) CH(1,1,3,K) = TAUI*(CC(1,1,K,3)-CC(1,1,K,2)) CH(1,IDO,2,K) = CC(1,1,K,1)+TAUR* 1 (CC(1,1,K,2)+CC(1,1,K,3)) 101 CONTINUE IF (IDO .EQ. 1) RETURN IDP2 = IDO+2 DO 103 K=1,L1 DO 102 I=3,IDO,2 IC = IDP2-I CH(1,I-1,1,K) = CC(1,I-1,K,1)+((WA1(I-2)*CC(1,I-1,K,2)+ 1 WA1(I-1)*CC(1,I,K,2))+(WA2(I-2)*CC(1,I-1,K,3)+WA2(I-1)* 1 CC(1,I,K,3))) CH(1,I,1,K) = CC(1,I,K,1)+((WA1(I-2)*CC(1,I,K,2)- 1 WA1(I-1)*CC(1,I-1,K,2))+(WA2(I-2)*CC(1,I,K,3)-WA2(I-1)* 1 CC(1,I-1,K,3))) CH(1,I-1,3,K) = (CC(1,I-1,K,1)+TAUR*((WA1(I-2)* 1 CC(1,I-1,K,2)+WA1(I-1)*CC(1,I,K,2))+(WA2(I-2)* 1 CC(1,I-1,K,3)+WA2(I-1)*CC(1,I,K,3))))+(TAUI*((WA1(I-2)* 1 CC(1,I,K,2)-WA1(I-1)*CC(1,I-1,K,2))-(WA2(I-2)* 1 CC(1,I,K,3)-WA2(I-1)*CC(1,I-1,K,3)))) CH(1,IC-1,2,K) = (CC(1,I-1,K,1)+TAUR*((WA1(I-2)* 1 CC(1,I-1,K,2)+WA1(I-1)*CC(1,I,K,2))+(WA2(I-2)* 1 CC(1,I-1,K,3)+WA2(I-1)*CC(1,I,K,3))))-(TAUI*((WA1(I-2)* 1 CC(1,I,K,2)-WA1(I-1)*CC(1,I-1,K,2))-(WA2(I-2)* 1 CC(1,I,K,3)-WA2(I-1)*CC(1,I-1,K,3)))) CH(1,I,3,K) = (CC(1,I,K,1)+TAUR*((WA1(I-2)*CC(1,I,K,2)- 1 WA1(I-1)*CC(1,I-1,K,2))+(WA2(I-2)*CC(1,I,K,3)-WA2(I-1)* 1 CC(1,I-1,K,3))))+(TAUI*((WA2(I-2)*CC(1,I-1,K,3)+WA2(I-1)* 1 CC(1,I,K,3))-(WA1(I-2)*CC(1,I-1,K,2)+WA1(I-1)* 1 CC(1,I,K,2)))) CH(1,IC,2,K) = (TAUI*((WA2(I-2)*CC(1,I-1,K,3)+WA2(I-1)* 1 CC(1,I,K,3))-(WA1(I-2)*CC(1,I-1,K,2)+WA1(I-1)* 1 CC(1,I,K,2))))-(CC(1,I,K,1)+TAUR*((WA1(I-2)*CC(1,I,K,2)- 1 WA1(I-1)*CC(1,I-1,K,2))+(WA2(I-2)*CC(1,I,K,3)-WA2(I-1)* 1 CC(1,I-1,K,3)))) 102 CONTINUE 103 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE R1F4KB (IDO,L1,CC,IN1,CH,IN2,WA1,WA2,WA3) REAL CC(IN1,IDO,4,L1) ,CH(IN2,IDO,L1,4) , 1 WA1(IDO) , WA2(IDO) , WA3(IDO) C SQRT2=SQRT(2.) DO 101 K=1,L1 CH(1,1,K,3) = (CC(1,1,1,K)+CC(1,IDO,4,K)) 1 -(CC(1,IDO,2,K)+CC(1,IDO,2,K)) CH(1,1,K,1) = (CC(1,1,1,K)+CC(1,IDO,4,K)) 1 +(CC(1,IDO,2,K)+CC(1,IDO,2,K)) CH(1,1,K,4) = (CC(1,1,1,K)-CC(1,IDO,4,K)) 1 +(CC(1,1,3,K)+CC(1,1,3,K)) CH(1,1,K,2) = (CC(1,1,1,K)-CC(1,IDO,4,K)) 1 -(CC(1,1,3,K)+CC(1,1,3,K)) 101 CONTINUE IF (IDO-2) 107,105,102 102 IDP2 = IDO+2 DO 104 K=1,L1 DO 103 I=3,IDO,2 IC = IDP2-I CH(1,I-1,K,1) = (CC(1,I-1,1,K)+CC(1,IC-1,4,K)) 1 +(CC(1,I-1,3,K)+CC(1,IC-1,2,K)) CH(1,I,K,1) = (CC(1,I,1,K)-CC(1,IC,4,K)) 1 +(CC(1,I,3,K)-CC(1,IC,2,K)) CH(1,I-1,K,2)=WA1(I-2)*((CC(1,I-1,1,K)-CC(1,IC-1,4,K)) 1 -(CC(1,I,3,K)+CC(1,IC,2,K)))-WA1(I-1) 1 *((CC(1,I,1,K)+CC(1,IC,4,K))+(CC(1,I-1,3,K)-CC(1,IC-1,2,K))) CH(1,I,K,2)=WA1(I-2)*((CC(1,I,1,K)+CC(1,IC,4,K)) 1 +(CC(1,I-1,3,K)-CC(1,IC-1,2,K)))+WA1(I-1) 1 *((CC(1,I-1,1,K)-CC(1,IC-1,4,K))-(CC(1,I,3,K)+CC(1,IC,2,K))) CH(1,I-1,K,3)=WA2(I-2)*((CC(1,I-1,1,K)+CC(1,IC-1,4,K)) 1 -(CC(1,I-1,3,K)+CC(1,IC-1,2,K)))-WA2(I-1) 1 *((CC(1,I,1,K)-CC(1,IC,4,K))-(CC(1,I,3,K)-CC(1,IC,2,K))) CH(1,I,K,3)=WA2(I-2)*((CC(1,I,1,K)-CC(1,IC,4,K)) 1 -(CC(1,I,3,K)-CC(1,IC,2,K)))+WA2(I-1) 1 *((CC(1,I-1,1,K)+CC(1,IC-1,4,K))-(CC(1,I-1,3,K) 1 +CC(1,IC-1,2,K))) CH(1,I-1,K,4)=WA3(I-2)*((CC(1,I-1,1,K)-CC(1,IC-1,4,K)) 1 +(CC(1,I,3,K)+CC(1,IC,2,K)))-WA3(I-1) 1 *((CC(1,I,1,K)+CC(1,IC,4,K))-(CC(1,I-1,3,K)-CC(1,IC-1,2,K))) CH(1,I,K,4)=WA3(I-2)*((CC(1,I,1,K)+CC(1,IC,4,K)) 1 -(CC(1,I-1,3,K)-CC(1,IC-1,2,K)))+WA3(I-1) 1 *((CC(1,I-1,1,K)-CC(1,IC-1,4,K))+(CC(1,I,3,K)+CC(1,IC,2,K))) 103 CONTINUE 104 CONTINUE IF (MOD(IDO,2) .EQ. 1) RETURN 105 CONTINUE DO 106 K=1,L1 CH(1,IDO,K,1) = (CC(1,IDO,1,K)+CC(1,IDO,3,K)) 1 +(CC(1,IDO,1,K)+CC(1,IDO,3,K)) CH(1,IDO,K,2) = SQRT2*((CC(1,IDO,1,K)-CC(1,IDO,3,K)) 1 -(CC(1,1,2,K)+CC(1,1,4,K))) CH(1,IDO,K,3) = (CC(1,1,4,K)-CC(1,1,2,K)) 1 +(CC(1,1,4,K)-CC(1,1,2,K)) CH(1,IDO,K,4) = -SQRT2*((CC(1,IDO,1,K)-CC(1,IDO,3,K)) 1 +(CC(1,1,2,K)+CC(1,1,4,K))) 106 CONTINUE 107 RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE R1F4KF (IDO,L1,CC,IN1,CH,IN2,WA1,WA2,WA3) REAL CC(IN1,IDO,L1,4) ,CH(IN2,IDO,4,L1) , 1 WA1(IDO) ,WA2(IDO) ,WA3(IDO) C HSQT2=SQRT(2.)/2. DO 101 K=1,L1 CH(1,1,1,K) = (CC(1,1,K,2)+CC(1,1,K,4)) 1 +(CC(1,1,K,1)+CC(1,1,K,3)) CH(1,IDO,4,K) = (CC(1,1,K,1)+CC(1,1,K,3)) 1 -(CC(1,1,K,2)+CC(1,1,K,4)) CH(1,IDO,2,K) = CC(1,1,K,1)-CC(1,1,K,3) CH(1,1,3,K) = CC(1,1,K,4)-CC(1,1,K,2) 101 CONTINUE IF (IDO-2) 107,105,102 102 IDP2 = IDO+2 DO 104 K=1,L1 DO 103 I=3,IDO,2 IC = IDP2-I CH(1,I-1,1,K) = ((WA1(I-2)*CC(1,I-1,K,2)+WA1(I-1)* 1 CC(1,I,K,2))+(WA3(I-2)*CC(1,I-1,K,4)+WA3(I-1)* 1 CC(1,I,K,4)))+(CC(1,I-1,K,1)+(WA2(I-2)*CC(1,I-1,K,3)+ 1 WA2(I-1)*CC(1,I,K,3))) CH(1,IC-1,4,K) = (CC(1,I-1,K,1)+(WA2(I-2)*CC(1,I-1,K,3)+ 1 WA2(I-1)*CC(1,I,K,3)))-((WA1(I-2)*CC(1,I-1,K,2)+ 1 WA1(I-1)*CC(1,I,K,2))+(WA3(I-2)*CC(1,I-1,K,4)+ 1 WA3(I-1)*CC(1,I,K,4))) CH(1,I,1,K) = ((WA1(I-2)*CC(1,I,K,2)-WA1(I-1)* 1 CC(1,I-1,K,2))+(WA3(I-2)*CC(1,I,K,4)-WA3(I-1)* 1 CC(1,I-1,K,4)))+(CC(1,I,K,1)+(WA2(I-2)*CC(1,I,K,3)- 1 WA2(I-1)*CC(1,I-1,K,3))) CH(1,IC,4,K) = ((WA1(I-2)*CC(1,I,K,2)-WA1(I-1)* 1 CC(1,I-1,K,2))+(WA3(I-2)*CC(1,I,K,4)-WA3(I-1)* 1 CC(1,I-1,K,4)))-(CC(1,I,K,1)+(WA2(I-2)*CC(1,I,K,3)- 1 WA2(I-1)*CC(1,I-1,K,3))) CH(1,I-1,3,K) = ((WA1(I-2)*CC(1,I,K,2)-WA1(I-1)* 1 CC(1,I-1,K,2))-(WA3(I-2)*CC(1,I,K,4)-WA3(I-1)* 1 CC(1,I-1,K,4)))+(CC(1,I-1,K,1)-(WA2(I-2)*CC(1,I-1,K,3)+ 1 WA2(I-1)*CC(1,I,K,3))) CH(1,IC-1,2,K) = (CC(1,I-1,K,1)-(WA2(I-2)*CC(1,I-1,K,3)+ 1 WA2(I-1)*CC(1,I,K,3)))-((WA1(I-2)*CC(1,I,K,2)-WA1(I-1)* 1 CC(1,I-1,K,2))-(WA3(I-2)*CC(1,I,K,4)-WA3(I-1)* 1 CC(1,I-1,K,4))) CH(1,I,3,K) = ((WA3(I-2)*CC(1,I-1,K,4)+WA3(I-1)* 1 CC(1,I,K,4))-(WA1(I-2)*CC(1,I-1,K,2)+WA1(I-1)* 1 CC(1,I,K,2)))+(CC(1,I,K,1)-(WA2(I-2)*CC(1,I,K,3)- 1 WA2(I-1)*CC(1,I-1,K,3))) CH(1,IC,2,K) = ((WA3(I-2)*CC(1,I-1,K,4)+WA3(I-1)* 1 CC(1,I,K,4))-(WA1(I-2)*CC(1,I-1,K,2)+WA1(I-1)* 1 CC(1,I,K,2)))-(CC(1,I,K,1)-(WA2(I-2)*CC(1,I,K,3)- 1 WA2(I-1)*CC(1,I-1,K,3))) 103 CONTINUE 104 CONTINUE IF (MOD(IDO,2) .EQ. 1) RETURN 105 CONTINUE DO 106 K=1,L1 CH(1,IDO,1,K) = (HSQT2*(CC(1,IDO,K,2)-CC(1,IDO,K,4)))+ 1 CC(1,IDO,K,1) CH(1,IDO,3,K) = CC(1,IDO,K,1)-(HSQT2*(CC(1,IDO,K,2)- 1 CC(1,IDO,K,4))) CH(1,1,2,K) = (-HSQT2*(CC(1,IDO,K,2)+CC(1,IDO,K,4)))- 1 CC(1,IDO,K,3) CH(1,1,4,K) = (-HSQT2*(CC(1,IDO,K,2)+CC(1,IDO,K,4)))+ 1 CC(1,IDO,K,3) 106 CONTINUE 107 RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE R1F5KB (IDO,L1,CC,IN1,CH,IN2, 1 WA1,WA2,WA3,WA4) REAL CC(IN1,IDO,5,L1) ,CH(IN2,IDO,L1,5), 1 WA1(IDO) ,WA2(IDO) ,WA3(IDO) ,WA4(IDO) C ARG=2.*4.*ATAN(1.0)/5. TR11=COS(ARG) TI11=SIN(ARG) TR12=COS(2.*ARG) TI12=SIN(2.*ARG) DO 101 K=1,L1 CH(1,1,K,1) = CC(1,1,1,K)+2.*CC(1,IDO,2,K)+2.*CC(1,IDO,4,K) CH(1,1,K,2) = (CC(1,1,1,K)+TR11*2.*CC(1,IDO,2,K) 1 +TR12*2.*CC(1,IDO,4,K))-(TI11*2.*CC(1,1,3,K) 1 +TI12*2.*CC(1,1,5,K)) CH(1,1,K,3) = (CC(1,1,1,K)+TR12*2.*CC(1,IDO,2,K) 1 +TR11*2.*CC(1,IDO,4,K))-(TI12*2.*CC(1,1,3,K) 1 -TI11*2.*CC(1,1,5,K)) CH(1,1,K,4) = (CC(1,1,1,K)+TR12*2.*CC(1,IDO,2,K) 1 +TR11*2.*CC(1,IDO,4,K))+(TI12*2.*CC(1,1,3,K) 1 -TI11*2.*CC(1,1,5,K)) CH(1,1,K,5) = (CC(1,1,1,K)+TR11*2.*CC(1,IDO,2,K) 1 +TR12*2.*CC(1,IDO,4,K))+(TI11*2.*CC(1,1,3,K) 1 +TI12*2.*CC(1,1,5,K)) 101 CONTINUE IF (IDO .EQ. 1) RETURN IDP2 = IDO+2 DO 103 K=1,L1 DO 102 I=3,IDO,2 IC = IDP2-I CH(1,I-1,K,1) = CC(1,I-1,1,K)+(CC(1,I-1,3,K)+CC(1,IC-1,2,K)) 1 +(CC(1,I-1,5,K)+CC(1,IC-1,4,K)) CH(1,I,K,1) = CC(1,I,1,K)+(CC(1,I,3,K)-CC(1,IC,2,K)) 1 +(CC(1,I,5,K)-CC(1,IC,4,K)) CH(1,I-1,K,2) = WA1(I-2)*((CC(1,I-1,1,K)+TR11* 1 (CC(1,I-1,3,K)+CC(1,IC-1,2,K))+TR12 1 *(CC(1,I-1,5,K)+CC(1,IC-1,4,K)))-(TI11*(CC(1,I,3,K) 1 +CC(1,IC,2,K))+TI12*(CC(1,I,5,K)+CC(1,IC,4,K)))) 1 -WA1(I-1)*((CC(1,I,1,K)+TR11*(CC(1,I,3,K)-CC(1,IC,2,K)) 1 +TR12*(CC(1,I,5,K)-CC(1,IC,4,K)))+(TI11*(CC(1,I-1,3,K) 1 -CC(1,IC-1,2,K))+TI12*(CC(1,I-1,5,K)-CC(1,IC-1,4,K)))) CH(1,I,K,2) = WA1(I-2)*((CC(1,I,1,K)+TR11*(CC(1,I,3,K) 1 -CC(1,IC,2,K))+TR12*(CC(1,I,5,K)-CC(1,IC,4,K))) 1 +(TI11*(CC(1,I-1,3,K)-CC(1,IC-1,2,K))+TI12 1 *(CC(1,I-1,5,K)-CC(1,IC-1,4,K))))+WA1(I-1) 1 *((CC(1,I-1,1,K)+TR11*(CC(1,I-1,3,K) 1 +CC(1,IC-1,2,K))+TR12*(CC(1,I-1,5,K)+CC(1,IC-1,4,K))) 1 -(TI11*(CC(1,I,3,K)+CC(1,IC,2,K))+TI12 1 *(CC(1,I,5,K)+CC(1,IC,4,K)))) CH(1,I-1,K,3) = WA2(I-2) 1 *((CC(1,I-1,1,K)+TR12*(CC(1,I-1,3,K)+CC(1,IC-1,2,K)) 1 +TR11*(CC(1,I-1,5,K)+CC(1,IC-1,4,K)))-(TI12*(CC(1,I,3,K) 1 +CC(1,IC,2,K))-TI11*(CC(1,I,5,K)+CC(1,IC,4,K)))) 1 -WA2(I-1) 1 *((CC(1,I,1,K)+TR12*(CC(1,I,3,K)- 1 CC(1,IC,2,K))+TR11*(CC(1,I,5,K)-CC(1,IC,4,K))) 1 +(TI12*(CC(1,I-1,3,K)-CC(1,IC-1,2,K))-TI11 1 *(CC(1,I-1,5,K)-CC(1,IC-1,4,K)))) CH(1,I,K,3) = WA2(I-2) 1 *((CC(1,I,1,K)+TR12*(CC(1,I,3,K)- 1 CC(1,IC,2,K))+TR11*(CC(1,I,5,K)-CC(1,IC,4,K))) 1 +(TI12*(CC(1,I-1,3,K)-CC(1,IC-1,2,K))-TI11 1 *(CC(1,I-1,5,K)-CC(1,IC-1,4,K)))) 1 +WA2(I-1) 1 *((CC(1,I-1,1,K)+TR12*(CC(1,I-1,3,K)+CC(1,IC-1,2,K)) 1 +TR11*(CC(1,I-1,5,K)+CC(1,IC-1,4,K)))-(TI12*(CC(1,I,3,K) 1 +CC(1,IC,2,K))-TI11*(CC(1,I,5,K)+CC(1,IC,4,K)))) CH(1,I-1,K,4) = WA3(I-2) 1 *((CC(1,I-1,1,K)+TR12*(CC(1,I-1,3,K)+CC(1,IC-1,2,K)) 1 +TR11*(CC(1,I-1,5,K)+CC(1,IC-1,4,K)))+(TI12*(CC(1,I,3,K) 1 +CC(1,IC,2,K))-TI11*(CC(1,I,5,K)+CC(1,IC,4,K)))) 1 -WA3(I-1) 1 *((CC(1,I,1,K)+TR12*(CC(1,I,3,K)- 1 CC(1,IC,2,K))+TR11*(CC(1,I,5,K)-CC(1,IC,4,K))) 1 -(TI12*(CC(1,I-1,3,K)-CC(1,IC-1,2,K))-TI11 1 *(CC(1,I-1,5,K)-CC(1,IC-1,4,K)))) CH(1,I,K,4) = WA3(I-2) 1 *((CC(1,I,1,K)+TR12*(CC(1,I,3,K)- 1 CC(1,IC,2,K))+TR11*(CC(1,I,5,K)-CC(1,IC,4,K))) 1 -(TI12*(CC(1,I-1,3,K)-CC(1,IC-1,2,K))-TI11 1 *(CC(1,I-1,5,K)-CC(1,IC-1,4,K)))) 1 +WA3(I-1) 1 *((CC(1,I-1,1,K)+TR12*(CC(1,I-1,3,K)+CC(1,IC-1,2,K)) 1 +TR11*(CC(1,I-1,5,K)+CC(1,IC-1,4,K)))+(TI12*(CC(1,I,3,K) 1 +CC(1,IC,2,K))-TI11*(CC(1,I,5,K)+CC(1,IC,4,K)))) CH(1,I-1,K,5) = WA4(I-2) 1 *((CC(1,I-1,1,K)+TR11*(CC(1,I-1,3,K)+CC(1,IC-1,2,K)) 1 +TR12*(CC(1,I-1,5,K)+CC(1,IC-1,4,K)))+(TI11*(CC(1,I,3,K) 1 +CC(1,IC,2,K))+TI12*(CC(1,I,5,K)+CC(1,IC,4,K)))) 1 -WA4(I-1) 1 *((CC(1,I,1,K)+TR11*(CC(1,I,3,K)-CC(1,IC,2,K)) 1 +TR12*(CC(1,I,5,K)-CC(1,IC,4,K)))-(TI11*(CC(1,I-1,3,K) 1 -CC(1,IC-1,2,K))+TI12*(CC(1,I-1,5,K)-CC(1,IC-1,4,K)))) CH(1,I,K,5) = WA4(I-2) 1 *((CC(1,I,1,K)+TR11*(CC(1,I,3,K)-CC(1,IC,2,K)) 1 +TR12*(CC(1,I,5,K)-CC(1,IC,4,K)))-(TI11*(CC(1,I-1,3,K) 1 -CC(1,IC-1,2,K))+TI12*(CC(1,I-1,5,K)-CC(1,IC-1,4,K)))) 1 +WA4(I-1) 1 *((CC(1,I-1,1,K)+TR11*(CC(1,I-1,3,K)+CC(1,IC-1,2,K)) 1 +TR12*(CC(1,I-1,5,K)+CC(1,IC-1,4,K)))+(TI11*(CC(1,I,3,K) 1 +CC(1,IC,2,K))+TI12*(CC(1,I,5,K)+CC(1,IC,4,K)))) 102 CONTINUE 103 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE R1F5KF (IDO,L1,CC,IN1,CH,IN2, 1 WA1,WA2,WA3,WA4) REAL CC(IN1,IDO,L1,5) ,CH(IN2,IDO,5,L1) , 1 WA1(IDO) ,WA2(IDO) ,WA3(IDO) ,WA4(IDO) C ARG=2.*4.*ATAN(1.0)/5. TR11=COS(ARG) TI11=SIN(ARG) TR12=COS(2.*ARG) TI12=SIN(2.*ARG) DO 101 K=1,L1 CH(1,1,1,K) = CC(1,1,K,1)+(CC(1,1,K,5)+CC(1,1,K,2))+ 1 (CC(1,1,K,4)+CC(1,1,K,3)) CH(1,IDO,2,K) = CC(1,1,K,1)+TR11*(CC(1,1,K,5)+CC(1,1,K,2))+ 1 TR12*(CC(1,1,K,4)+CC(1,1,K,3)) CH(1,1,3,K) = TI11*(CC(1,1,K,5)-CC(1,1,K,2))+TI12* 1 (CC(1,1,K,4)-CC(1,1,K,3)) CH(1,IDO,4,K) = CC(1,1,K,1)+TR12*(CC(1,1,K,5)+CC(1,1,K,2))+ 1 TR11*(CC(1,1,K,4)+CC(1,1,K,3)) CH(1,1,5,K) = TI12*(CC(1,1,K,5)-CC(1,1,K,2))-TI11* 1 (CC(1,1,K,4)-CC(1,1,K,3)) 101 CONTINUE IF (IDO .EQ. 1) RETURN IDP2 = IDO+2 DO 103 K=1,L1 DO 102 I=3,IDO,2 IC = IDP2-I CH(1,I-1,1,K) = CC(1,I-1,K,1)+((WA1(I-2)*CC(1,I-1,K,2)+ 1 WA1(I-1)*CC(1,I,K,2))+(WA4(I-2)*CC(1,I-1,K,5)+WA4(I-1)* 1 CC(1,I,K,5)))+((WA2(I-2)*CC(1,I-1,K,3)+WA2(I-1)* 1 CC(1,I,K,3))+(WA3(I-2)*CC(1,I-1,K,4)+ 1 WA3(I-1)*CC(1,I,K,4))) CH(1,I,1,K) = CC(1,I,K,1)+((WA1(I-2)*CC(1,I,K,2)- 1 WA1(I-1)*CC(1,I-1,K,2))+(WA4(I-2)*CC(1,I,K,5)-WA4(I-1)* 1 CC(1,I-1,K,5)))+((WA2(I-2)*CC(1,I,K,3)-WA2(I-1)* 1 CC(1,I-1,K,3))+(WA3(I-2)*CC(1,I,K,4)-WA3(I-1)* 1 CC(1,I-1,K,4))) CH(1,I-1,3,K) = CC(1,I-1,K,1)+TR11* 1 ( WA1(I-2)*CC(1,I-1,K,2)+WA1(I-1)*CC(1,I,K,2) 1 +WA4(I-2)*CC(1,I-1,K,5)+WA4(I-1)*CC(1,I,K,5))+TR12* 1 ( WA2(I-2)*CC(1,I-1,K,3)+WA2(I-1)*CC(1,I,K,3) 1 +WA3(I-2)*CC(1,I-1,K,4)+WA3(I-1)*CC(1,I,K,4))+TI11* 1 ( WA1(I-2)*CC(1,I,K,2)-WA1(I-1)*CC(1,I-1,K,2) 1 -(WA4(I-2)*CC(1,I,K,5)-WA4(I-1)*CC(1,I-1,K,5)))+TI12* 1 ( WA2(I-2)*CC(1,I,K,3)-WA2(I-1)*CC(1,I-1,K,3) 1 -(WA3(I-2)*CC(1,I,K,4)-WA3(I-1)*CC(1,I-1,K,4))) CH(1,IC-1,2,K) = CC(1,I-1,K,1)+TR11* 1 ( WA1(I-2)*CC(1,I-1,K,2)+WA1(I-1)*CC(1,I,K,2) 1 +WA4(I-2)*CC(1,I-1,K,5)+WA4(I-1)*CC(1,I,K,5))+TR12* 1 ( WA2(I-2)*CC(1,I-1,K,3)+WA2(I-1)*CC(1,I,K,3) 1 +WA3(I-2)*CC(1,I-1,K,4)+WA3(I-1)*CC(1,I,K,4))-(TI11* 1 ( WA1(I-2)*CC(1,I,K,2)-WA1(I-1)*CC(1,I-1,K,2) 1 -(WA4(I-2)*CC(1,I,K,5)-WA4(I-1)*CC(1,I-1,K,5)))+TI12* 1 ( WA2(I-2)*CC(1,I,K,3)-WA2(I-1)*CC(1,I-1,K,3) 1 -(WA3(I-2)*CC(1,I,K,4)-WA3(I-1)*CC(1,I-1,K,4)))) CH(1,I,3,K) = (CC(1,I,K,1)+TR11*((WA1(I-2)*CC(1,I,K,2)- 1 WA1(I-1)*CC(1,I-1,K,2))+(WA4(I-2)*CC(1,I,K,5)-WA4(I-1)* 1 CC(1,I-1,K,5)))+TR12*((WA2(I-2)*CC(1,I,K,3)-WA2(I-1)* 1 CC(1,I-1,K,3))+(WA3(I-2)*CC(1,I,K,4)-WA3(I-1)* 1 CC(1,I-1,K,4))))+(TI11*((WA4(I-2)*CC(1,I-1,K,5)+ 1 WA4(I-1)*CC(1,I,K,5))-(WA1(I-2)*CC(1,I-1,K,2)+WA1(I-1)* 1 CC(1,I,K,2)))+TI12*((WA3(I-2)*CC(1,I-1,K,4)+WA3(I-1)* 1 CC(1,I,K,4))-(WA2(I-2)*CC(1,I-1,K,3)+WA2(I-1)* 1 CC(1,I,K,3)))) CH(1,IC,2,K) = (TI11*((WA4(I-2)*CC(1,I-1,K,5)+WA4(I-1)* 1 CC(1,I,K,5))-(WA1(I-2)*CC(1,I-1,K,2)+WA1(I-1)* 1 CC(1,I,K,2)))+TI12*((WA3(I-2)*CC(1,I-1,K,4)+WA3(I-1)* 1 CC(1,I,K,4))-(WA2(I-2)*CC(1,I-1,K,3)+WA2(I-1)* 1 CC(1,I,K,3))))-(CC(1,I,K,1)+TR11*((WA1(I-2)*CC(1,I,K,2)- 1 WA1(I-1)*CC(1,I-1,K,2))+(WA4(I-2)*CC(1,I,K,5)-WA4(I-1)* 1 CC(1,I-1,K,5)))+TR12*((WA2(I-2)*CC(1,I,K,3)-WA2(I-1)* 1 CC(1,I-1,K,3))+(WA3(I-2)*CC(1,I,K,4)-WA3(I-1)* 1 CC(1,I-1,K,4)))) CH(1,I-1,5,K) = (CC(1,I-1,K,1)+TR12*((WA1(I-2)* 1 CC(1,I-1,K,2)+WA1(I-1)*CC(1,I,K,2))+(WA4(I-2)* 1 CC(1,I-1,K,5)+WA4(I-1)*CC(1,I,K,5)))+TR11*((WA2(I-2)* 1 CC(1,I-1,K,3)+WA2(I-1)*CC(1,I,K,3))+(WA3(I-2)* 1 CC(1,I-1,K,4)+WA3(I-1)*CC(1,I,K,4))))+(TI12*((WA1(I-2)* 1 CC(1,I,K,2)-WA1(I-1)*CC(1,I-1,K,2))-(WA4(I-2)* 1 CC(1,I,K,5)-WA4(I-1)*CC(1,I-1,K,5)))-TI11*((WA2(I-2)* 1 CC(1,I,K,3)-WA2(I-1)*CC(1,I-1,K,3))-(WA3(I-2)* 1 CC(1,I,K,4)-WA3(I-1)*CC(1,I-1,K,4)))) CH(1,IC-1,4,K) = (CC(1,I-1,K,1)+TR12*((WA1(I-2)* 1 CC(1,I-1,K,2)+WA1(I-1)*CC(1,I,K,2))+(WA4(I-2)* 1 CC(1,I-1,K,5)+WA4(I-1)*CC(1,I,K,5)))+TR11*((WA2(I-2)* 1 CC(1,I-1,K,3)+WA2(I-1)*CC(1,I,K,3))+(WA3(I-2)* 1 CC(1,I-1,K,4)+WA3(I-1)*CC(1,I,K,4))))-(TI12*((WA1(I-2)* 1 CC(1,I,K,2)-WA1(I-1)*CC(1,I-1,K,2))-(WA4(I-2)* 1 CC(1,I,K,5)-WA4(I-1)*CC(1,I-1,K,5)))-TI11*((WA2(I-2)* 1 CC(1,I,K,3)-WA2(I-1)*CC(1,I-1,K,3))-(WA3(I-2)* 1 CC(1,I,K,4)-WA3(I-1)*CC(1,I-1,K,4)))) CH(1,I,5,K) = (CC(1,I,K,1)+TR12*((WA1(I-2)*CC(1,I,K,2)- 1 WA1(I-1)*CC(1,I-1,K,2))+(WA4(I-2)*CC(1,I,K,5)-WA4(I-1)* 1 CC(1,I-1,K,5)))+TR11*((WA2(I-2)*CC(1,I,K,3)-WA2(I-1)* 1 CC(1,I-1,K,3))+(WA3(I-2)*CC(1,I,K,4)-WA3(I-1)* 1 CC(1,I-1,K,4))))+(TI12*((WA4(I-2)*CC(1,I-1,K,5)+ 1 WA4(I-1)*CC(1,I,K,5))-(WA1(I-2)*CC(1,I-1,K,2)+WA1(I-1)* 1 CC(1,I,K,2)))-TI11*((WA3(I-2)*CC(1,I-1,K,4)+WA3(I-1)* 1 CC(1,I,K,4))-(WA2(I-2)*CC(1,I-1,K,3)+WA2(I-1)* 1 CC(1,I,K,3)))) CH(1,IC,4,K) = (TI12*((WA4(I-2)*CC(1,I-1,K,5)+WA4(I-1)* 1 CC(1,I,K,5))-(WA1(I-2)*CC(1,I-1,K,2)+WA1(I-1)* 1 CC(1,I,K,2)))-TI11*((WA3(I-2)*CC(1,I-1,K,4)+WA3(I-1)* 1 CC(1,I,K,4))-(WA2(I-2)*CC(1,I-1,K,3)+WA2(I-1)* 1 CC(1,I,K,3))))-(CC(1,I,K,1)+TR12*((WA1(I-2)*CC(1,I,K,2)- 1 WA1(I-1)*CC(1,I-1,K,2))+(WA4(I-2)*CC(1,I,K,5)-WA4(I-1)* 1 CC(1,I-1,K,5)))+TR11*((WA2(I-2)*CC(1,I,K,3)-WA2(I-1)* 1 CC(1,I-1,K,3))+(WA3(I-2)*CC(1,I,K,4)-WA3(I-1)* 1 CC(1,I-1,K,4)))) 102 CONTINUE 103 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE R1FGKB (IDO,IP,L1,IDL1,CC,C1,C2,IN1, 1 CH,CH2,IN2,WA) REAL CH(IN2,IDO,L1,IP) ,CC(IN1,IDO,IP,L1) , 1 C1(IN1,IDO,L1,IP) ,C2(IN1,IDL1,IP), 2 CH2(IN2,IDL1,IP) ,WA(IDO) C TPI=2.*4.*ATAN(1.0) ARG = TPI/FLOAT(IP) DCP = COS(ARG) DSP = SIN(ARG) IDP2 = IDO+2 NBD = (IDO-1)/2 IPP2 = IP+2 IPPH = (IP+1)/2 IF (IDO .LT. L1) GO TO 103 DO 102 K=1,L1 DO 101 I=1,IDO CH(1,I,K,1) = CC(1,I,1,K) 101 CONTINUE 102 CONTINUE GO TO 106 103 DO 105 I=1,IDO DO 104 K=1,L1 CH(1,I,K,1) = CC(1,I,1,K) 104 CONTINUE 105 CONTINUE 106 DO 108 J=2,IPPH JC = IPP2-J J2 = J+J DO 107 K=1,L1 CH(1,1,K,J) = CC(1,IDO,J2-2,K)+CC(1,IDO,J2-2,K) CH(1,1,K,JC) = CC(1,1,J2-1,K)+CC(1,1,J2-1,K) 1007 CONTINUE 107 CONTINUE 108 CONTINUE IF (IDO .EQ. 1) GO TO 116 IF (NBD .LT. L1) GO TO 112 DO 111 J=2,IPPH JC = IPP2-J DO 110 K=1,L1 DO 109 I=3,IDO,2 IC = IDP2-I CH(1,I-1,K,J) = CC(1,I-1,2*J-1,K)+CC(1,IC-1,2*J-2,K) CH(1,I-1,K,JC) = CC(1,I-1,2*J-1,K)-CC(1,IC-1,2*J-2,K) CH(1,I,K,J) = CC(1,I,2*J-1,K)-CC(1,IC,2*J-2,K) CH(1,I,K,JC) = CC(1,I,2*J-1,K)+CC(1,IC,2*J-2,K) 109 CONTINUE 110 CONTINUE 111 CONTINUE GO TO 116 112 DO 115 J=2,IPPH JC = IPP2-J DO 114 I=3,IDO,2 IC = IDP2-I DO 113 K=1,L1 CH(1,I-1,K,J) = CC(1,I-1,2*J-1,K)+CC(1,IC-1,2*J-2,K) CH(1,I-1,K,JC) = CC(1,I-1,2*J-1,K)-CC(1,IC-1,2*J-2,K) CH(1,I,K,J) = CC(1,I,2*J-1,K)-CC(1,IC,2*J-2,K) CH(1,I,K,JC) = CC(1,I,2*J-1,K)+CC(1,IC,2*J-2,K) 113 CONTINUE 114 CONTINUE 115 CONTINUE 116 AR1 = 1. AI1 = 0. DO 120 L=2,IPPH LC = IPP2-L AR1H = DCP*AR1-DSP*AI1 AI1 = DCP*AI1+DSP*AR1 AR1 = AR1H DO 117 IK=1,IDL1 C2(1,IK,L) = CH2(1,IK,1)+AR1*CH2(1,IK,2) C2(1,IK,LC) = AI1*CH2(1,IK,IP) 117 CONTINUE DC2 = AR1 DS2 = AI1 AR2 = AR1 AI2 = AI1 DO 119 J=3,IPPH JC = IPP2-J AR2H = DC2*AR2-DS2*AI2 AI2 = DC2*AI2+DS2*AR2 AR2 = AR2H DO 118 IK=1,IDL1 C2(1,IK,L) = C2(1,IK,L)+AR2*CH2(1,IK,J) C2(1,IK,LC) = C2(1,IK,LC)+AI2*CH2(1,IK,JC) 118 CONTINUE 119 CONTINUE 120 CONTINUE DO 122 J=2,IPPH DO 121 IK=1,IDL1 CH2(1,IK,1) = CH2(1,IK,1)+CH2(1,IK,J) 121 CONTINUE 122 CONTINUE DO 124 J=2,IPPH JC = IPP2-J DO 123 K=1,L1 CH(1,1,K,J) = C1(1,1,K,J)-C1(1,1,K,JC) CH(1,1,K,JC) = C1(1,1,K,J)+C1(1,1,K,JC) 123 CONTINUE 124 CONTINUE IF (IDO .EQ. 1) GO TO 132 IF (NBD .LT. L1) GO TO 128 DO 127 J=2,IPPH JC = IPP2-J DO 126 K=1,L1 DO 125 I=3,IDO,2 CH(1,I-1,K,J) = C1(1,I-1,K,J)-C1(1,I,K,JC) CH(1,I-1,K,JC) = C1(1,I-1,K,J)+C1(1,I,K,JC) CH(1,I,K,J) = C1(1,I,K,J)+C1(1,I-1,K,JC) CH(1,I,K,JC) = C1(1,I,K,J)-C1(1,I-1,K,JC) 125 CONTINUE 126 CONTINUE 127 CONTINUE GO TO 132 128 DO 131 J=2,IPPH JC = IPP2-J DO 130 I=3,IDO,2 DO 129 K=1,L1 CH(1,I-1,K,J) = C1(1,I-1,K,J)-C1(1,I,K,JC) CH(1,I-1,K,JC) = C1(1,I-1,K,J)+C1(1,I,K,JC) CH(1,I,K,J) = C1(1,I,K,J)+C1(1,I-1,K,JC) CH(1,I,K,JC) = C1(1,I,K,J)-C1(1,I-1,K,JC) 129 CONTINUE 130 CONTINUE 131 CONTINUE 132 CONTINUE IF (IDO .EQ. 1) RETURN DO 133 IK=1,IDL1 C2(1,IK,1) = CH2(1,IK,1) 133 CONTINUE DO 135 J=2,IP DO 134 K=1,L1 C1(1,1,K,J) = CH(1,1,K,J) 134 CONTINUE 135 CONTINUE IF (NBD .GT. L1) GO TO 139 IS = -IDO DO 138 J=2,IP IS = IS+IDO IDIJ = IS DO 137 I=3,IDO,2 IDIJ = IDIJ+2 DO 136 K=1,L1 C1(1,I-1,K,J) = WA(IDIJ-1)*CH(1,I-1,K,J)-WA(IDIJ)* 1 CH(1,I,K,J) C1(1,I,K,J) = WA(IDIJ-1)*CH(1,I,K,J)+WA(IDIJ)* 1 CH(1,I-1,K,J) 136 CONTINUE 137 CONTINUE 138 CONTINUE GO TO 143 139 IS = -IDO DO 142 J=2,IP IS = IS+IDO DO 141 K=1,L1 IDIJ = IS DO 140 I=3,IDO,2 IDIJ = IDIJ+2 C1(1,I-1,K,J) = WA(IDIJ-1)*CH(1,I-1,K,J)-WA(IDIJ)* 1 CH(1,I,K,J) C1(1,I,K,J) = WA(IDIJ-1)*CH(1,I,K,J)+WA(IDIJ)* 1 CH(1,I-1,K,J) 140 CONTINUE 141 CONTINUE 142 CONTINUE 143 RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE R1FGKF (IDO,IP,L1,IDL1,CC,C1,C2,IN1, 1 CH,CH2,IN2,WA) REAL CH(IN2,IDO,L1,IP) ,CC(IN1,IDO,IP,L1), 1 C1(IN1,IDO,L1,IP) ,C2(IN1,IDL1,IP), 2 CH2(IN2,IDL1,IP) ,WA(IDO) C TPI=2.*4.*ATAN(1.0) ARG = TPI/FLOAT(IP) DCP = COS(ARG) DSP = SIN(ARG) IPPH = (IP+1)/2 IPP2 = IP+2 IDP2 = IDO+2 NBD = (IDO-1)/2 IF (IDO .EQ. 1) GO TO 119 DO 101 IK=1,IDL1 CH2(1,IK,1) = C2(1,IK,1) 101 CONTINUE DO 103 J=2,IP DO 102 K=1,L1 CH(1,1,K,J) = C1(1,1,K,J) 102 CONTINUE 103 CONTINUE IF (NBD .GT. L1) GO TO 107 IS = -IDO DO 106 J=2,IP IS = IS+IDO IDIJ = IS DO 105 I=3,IDO,2 IDIJ = IDIJ+2 DO 104 K=1,L1 CH(1,I-1,K,J) = WA(IDIJ-1)*C1(1,I-1,K,J)+WA(IDIJ) 1 *C1(1,I,K,J) CH(1,I,K,J) = WA(IDIJ-1)*C1(1,I,K,J)-WA(IDIJ) 1 *C1(1,I-1,K,J) 104 CONTINUE 105 CONTINUE 106 CONTINUE GO TO 111 107 IS = -IDO DO 110 J=2,IP IS = IS+IDO DO 109 K=1,L1 IDIJ = IS DO 108 I=3,IDO,2 IDIJ = IDIJ+2 CH(1,I-1,K,J) = WA(IDIJ-1)*C1(1,I-1,K,J)+WA(IDIJ) 1 *C1(1,I,K,J) CH(1,I,K,J) = WA(IDIJ-1)*C1(1,I,K,J)-WA(IDIJ) 1 *C1(1,I-1,K,J) 108 CONTINUE 109 CONTINUE 110 CONTINUE 111 IF (NBD .LT. L1) GO TO 115 DO 114 J=2,IPPH JC = IPP2-J DO 113 K=1,L1 DO 112 I=3,IDO,2 C1(1,I-1,K,J) = CH(1,I-1,K,J)+CH(1,I-1,K,JC) C1(1,I-1,K,JC) = CH(1,I,K,J)-CH(1,I,K,JC) C1(1,I,K,J) = CH(1,I,K,J)+CH(1,I,K,JC) C1(1,I,K,JC) = CH(1,I-1,K,JC)-CH(1,I-1,K,J) 112 CONTINUE 113 CONTINUE 114 CONTINUE GO TO 121 115 DO 118 J=2,IPPH JC = IPP2-J DO 117 I=3,IDO,2 DO 116 K=1,L1 C1(1,I-1,K,J) = CH(1,I-1,K,J)+CH(1,I-1,K,JC) C1(1,I-1,K,JC) = CH(1,I,K,J)-CH(1,I,K,JC) C1(1,I,K,J) = CH(1,I,K,J)+CH(1,I,K,JC) C1(1,I,K,JC) = CH(1,I-1,K,JC)-CH(1,I-1,K,J) 116 CONTINUE 117 CONTINUE 118 CONTINUE GO TO 121 119 DO 120 IK=1,IDL1 C2(1,IK,1) = CH2(1,IK,1) 120 CONTINUE 121 DO 123 J=2,IPPH JC = IPP2-J DO 122 K=1,L1 C1(1,1,K,J) = CH(1,1,K,J)+CH(1,1,K,JC) C1(1,1,K,JC) = CH(1,1,K,JC)-CH(1,1,K,J) 122 CONTINUE 123 CONTINUE C AR1 = 1. AI1 = 0. DO 127 L=2,IPPH LC = IPP2-L AR1H = DCP*AR1-DSP*AI1 AI1 = DCP*AI1+DSP*AR1 AR1 = AR1H DO 124 IK=1,IDL1 CH2(1,IK,L) = C2(1,IK,1)+AR1*C2(1,IK,2) CH2(1,IK,LC) = AI1*C2(1,IK,IP) 124 CONTINUE DC2 = AR1 DS2 = AI1 AR2 = AR1 AI2 = AI1 DO 126 J=3,IPPH JC = IPP2-J AR2H = DC2*AR2-DS2*AI2 AI2 = DC2*AI2+DS2*AR2 AR2 = AR2H DO 125 IK=1,IDL1 CH2(1,IK,L) = CH2(1,IK,L)+AR2*C2(1,IK,J) CH2(1,IK,LC) = CH2(1,IK,LC)+AI2*C2(1,IK,JC) 125 CONTINUE 126 CONTINUE 127 CONTINUE DO 129 J=2,IPPH DO 128 IK=1,IDL1 CH2(1,IK,1) = CH2(1,IK,1)+C2(1,IK,J) 128 CONTINUE 129 CONTINUE C IF (IDO .LT. L1) GO TO 132 DO 131 K=1,L1 DO 130 I=1,IDO CC(1,I,1,K) = CH(1,I,K,1) 130 CONTINUE 131 CONTINUE GO TO 135 132 DO 134 I=1,IDO DO 133 K=1,L1 CC(1,I,1,K) = CH(1,I,K,1) 133 CONTINUE 134 CONTINUE 135 DO 137 J=2,IPPH JC = IPP2-J J2 = J+J DO 136 K=1,L1 CC(1,IDO,J2-2,K) = CH(1,1,K,J) CC(1,1,J2-1,K) = CH(1,1,K,JC) 136 CONTINUE 137 CONTINUE IF (IDO .EQ. 1) RETURN IF (NBD .LT. L1) GO TO 141 DO 140 J=2,IPPH JC = IPP2-J J2 = J+J DO 139 K=1,L1 DO 138 I=3,IDO,2 IC = IDP2-I CC(1,I-1,J2-1,K) = CH(1,I-1,K,J)+CH(1,I-1,K,JC) CC(1,IC-1,J2-2,K) = CH(1,I-1,K,J)-CH(1,I-1,K,JC) CC(1,I,J2-1,K) = CH(1,I,K,J)+CH(1,I,K,JC) CC(1,IC,J2-2,K) = CH(1,I,K,JC)-CH(1,I,K,J) 138 CONTINUE 139 CONTINUE 140 CONTINUE RETURN 141 DO 144 J=2,IPPH JC = IPP2-J J2 = J+J DO 143 I=3,IDO,2 IC = IDP2-I DO 142 K=1,L1 CC(1,I-1,J2-1,K) = CH(1,I-1,K,J)+CH(1,I-1,K,JC) CC(1,IC-1,J2-2,K) = CH(1,I-1,K,J)-CH(1,I-1,K,JC) CC(1,I,J2-1,K) = CH(1,I,K,J)+CH(1,I,K,JC) CC(1,IC,J2-2,K) = CH(1,I,K,JC)-CH(1,I,K,J) 142 CONTINUE 143 CONTINUE 144 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C AUTHORS: PAUL N. SWARZTRAUBER AND RICHARD A. VALENT C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC subroutine r2w(ldr,ldw,l,m,r,w) dimension r(ldr,*),w(ldw,*) do j=1,m do i=1,l w(i,j) = r( i,j) end do end do return end CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE RFFT1B ( N, INC, R, LENR, WSAVE, LENSAV, 1 WORK, LENWRK, IER) INTEGER N, INC, LENR, LENSAV, LENWRK, IER REAL R(LENR), WSAVE(LENSAV) ,WORK(LENWRK) C IER = 0 C IF (LENR .LT. INC*(N-1) + 1) THEN IER = 1 CALL XERFFT ('RFFT1B ', 6) ELSEIF (LENSAV .LT. N + INT(LOG(REAL(N))/LOG(2.)) +4) THEN IER = 2 CALL XERFFT ('RFFT1B ', 8) ELSEIF (LENWRK .LT. N) THEN IER = 3 CALL XERFFT ('RFFT1B ', 10) ENDIF C IF (N .EQ. 1) RETURN C CALL RFFTB1 (N,INC,R,WORK,WSAVE,WSAVE(N+1)) RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE RFFT1F ( N, INC, R, LENR, WSAVE, LENSAV, 1 WORK, LENWRK, IER) INTEGER N, INC, LENR, LENSAV, LENWRK, IER REAL R(LENR), WSAVE(LENSAV), WORK(LENWRK) C IER = 0 C IF (LENR .LT. INC*(N-1) + 1) THEN IER = 1 CALL XERFFT ('RFFT1F ', 6) ELSEIF (LENSAV .LT. N + INT(LOG(REAL(N))/LOG(2.)) +4) THEN IER = 2 CALL XERFFT ('RFFT1F ', 8) ELSEIF (LENWRK .LT. N) THEN IER = 3 CALL XERFFT ('RFFT1F ', 10) ENDIF C IF (N .EQ. 1) RETURN C CALL RFFTF1 (N,INC,R,WORK,WSAVE,WSAVE(N+1)) RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE RFFT1I ( N, WSAVE, LENSAV, IER ) INTEGER N, LENSAV, IER REAL WSAVE(LENSAV) C IER = 0 C IF (LENSAV .LT. N + INT(LOG(REAL(N))/LOG(2.)) +4) THEN IER = 2 CALL XERFFT ('RFFT1I ', 3) ENDIF C IF (N .EQ. 1) RETURN C CALL RFFTI1 (N,WSAVE(1),WSAVE(N+1)) RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C AUTHORS: PAUL N. SWARZTRAUBER AND RICHARD A. VALENT C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE RFFT2B (LDIM, L, M, R, WSAVE, LENSAV, WORK, 1 LENWRK, IER) INTEGER LDIM, L, M, LENSAV, LENWRK, IER REAL R(LDIM,M), WSAVE(LENSAV), WORK(LENWRK) INTEGER LDX C C C INITIALIZE IER C IER = 0 C C VERIFY LENSAV C LWSAV = L+INT(LOG(REAL(L))/LOG(2.))+4 MWSAV = 2*M+INT(LOG(REAL(M))/LOG(2.))+4 MMSAV = M+INT(LOG(REAL(M))/LOG(2.))+4 MODL = MOD(L,2) MODM = MOD(M,2) C IF (LENSAV .LT. LWSAV+MWSAV+MMSAV) THEN IER = 2 CALL XERFFT ('RFFT2F', 6) GO TO 100 ENDIF C C VERIFY LENWRK C IF (LENWRK .LT. (L+1)*M) THEN IER = 3 CALL XERFFT ('RFFT2F', 8) GO TO 100 ENDIF C C VERIFY LDIM IS AS BIG AS L C IF (LDIM .LT. L) THEN IER = 5 CALL XERFFT ('RFFT2F', -6) GO TO 100 ENDIF C C TRANSFORM SECOND DIMENSION OF ARRAY C DO J=2,2*((M+1)/2)-1 R(1,J) = R(1,J)+R(1,J) END DO DO J=3,M,2 R(1,J) = -R(1,J) END DO CALL RFFTMB(1,1,M,LDIM,R,M*LDIM, 1 WSAVE(LWSAV+MWSAV+1),MMSAV,WORK,LENWRK,IER1) LDH = INT((L+1)/2) IF(LDH.GT.1) THEN LDW = LDH+LDH C C R AND WORK ARE SWITCHED BECAUSE THE THE FIRST DIMENSION C OF THE INPUT TO COMPLEX CFFTMF MUST BE EVEN. C CALL R2W(LDIM,LDW,L,M,R,WORK) CALL CFFTMB(LDH-1,1,M,LDH,WORK(2),LDH*M, 1 WSAVE(LWSAV+1),MWSAV,R,L*M, IER1) IF(IER1.NE.0) THEN IER=20 CALL XERFFT('RFFT2B',-5) GO TO 100 END IF CALL W2R(LDIM,LDW,L,M,R,WORK) END IF C IF(MODL.EQ.0) THEN DO J=2,2*((M+1)/2)-1 R(L,J) = R(L,J)+R(L,J) END DO DO J=3,M,2 R(L,J) = -R(L,J) END DO CALL RFFTMB(1,1,M,LDIM,R(L,1),M*LDIM, 1 WSAVE(LWSAV+MWSAV+1),MMSAV,WORK,LENWRK,IER1) END IF C C PRINT*, 'BACKWARD TRANSFORM IN THE J DIRECTION' C DO I=1,L C PRINT*, (R(I,J),J=1,M) C END DO C C TRANSFORM FIRST DIMENSION OF ARRAY C LDX = 2*INT((L+1)/2)-1 DO I=2,LDX DO J=1,M R(I,J) = R(I,J)+R(I,J) END DO END DO DO J=1,M DO I=3,LDX,2 R(I,J) = -R(I,J) END DO END DO CALL RFFTMB(M,LDIM,L,1,R,M*LDIM,WSAVE(1), . L+INT(LOG(REAL(L))/LOG(2.))+4,WORK,LENWRK,IER1) C C C PRINT*, 'BACKWARD TRANSFORM IN THE I DIRECTION' C DO I=1,L C PRINT*, (R(I,J),J=1,M) C END DO C IF(IER1.NE.0) THEN IER=20 CALL XERFFT('RFFT2F',-5) GO TO 100 ENDIF C IF(IER1.NE.0) THEN IER=20 CALL XERFFT('RFFT2F',-5) GO TO 100 ENDIF C 100 CONTINUE C RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C AUTHORS: PAUL N. SWARZTRAUBER AND RICHARD A. VALENT C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE RFFT2F (LDIM, L, M, R, WSAVE, LENSAV, WORK, 1 LENWRK, IER) INTEGER LDIM, L, M, LENSAV, LENWRK, IER, IDX, MODL, MODM, 1 IDH, IDW REAL R(LDIM,M), WSAVE(LENSAV), WORK(LENWRK) C C C INITIALIZE IER C IER = 0 C C VERIFY LENSAV C LWSAV = L+INT(LOG(REAL(L))/LOG(2.))+4 MWSAV = 2*M+INT(LOG(REAL(M))/LOG(2.))+4 MMSAV = M+INT(LOG(REAL(M))/LOG(2.))+4 C IF (LENSAV .LT. LWSAV+MWSAV+MMSAV) THEN IER = 2 CALL XERFFT ('RFFT2F', 6) GO TO 100 ENDIF C C VERIFY LENWRK C IF (LENWRK .LT. (L+1)*M) THEN IER = 3 CALL XERFFT ('RFFT2F', 8) GO TO 100 ENDIF C C VERIFY LDIM IS AS BIG AS L C IF (LDIM .LT. L) THEN IER = 5 CALL XERFFT ('RFFT2F', -6) GO TO 100 ENDIF C C TRANSFORM FIRST DIMENSION OF ARRAY C CALL RFFTMF(M,LDIM,L,1,R,M*LDIM,WSAVE(1), . L+INT(LOG(REAL(L))/LOG(2.))+4,WORK,LENWRK,IER1) C IF(IER1.NE.0) THEN IER=20 CALL XERFFT('RFFT2F',-5) GO TO 100 ENDIF C LDX = 2*INT((L+1)/2)-1 DO I=2,LDX DO J=1,M R(I,J) = .5*R(I,J) END DO END DO DO J=1,M DO I=3,LDX,2 R(I,J) = -R(I,J) END DO END DO C C PRINT*, 'FORWARD TRANSFORM IN THE I DIRECTION' C DO I=1,L C PRINT*, (R(I,J),J=1,M) C END DO C C RESHUFFLE TO ADD IN NYQUIST IMAGINARY COMPONENTS C MODL = MOD(L,2) MODM = MOD(M,2) C C TRANSFORM SECOND DIMENSION OF ARRAY C CALL RFFTMF(1,1,M,LDIM,R,M*LDIM, 1 WSAVE(LWSAV+MWSAV+1),MMSAV,WORK,LENWRK,IER1) DO J=2,2*((M+1)/2)-1 R(1,J) = .5*R(1,J) END DO DO J=3,M,2 R(1,J) = -R(1,J) END DO LDH = INT((L+1)/2) IF(LDH.GT.1) THEN LDW = LDH+LDH C C R AND WORK ARE SWITCHED BECAUSE THE THE FIRST DIMENSION C OF THE INPUT TO COMPLEX CFFTMF MUST BE EVEN. C CALL R2W(LDIM,LDW,L,M,R,WORK) CALL CFFTMF(LDH-1,1,M,LDH,WORK(2),LDH*M, 1 WSAVE(LWSAV+1),MWSAV,R,L*M, IER1) IF(IER1.NE.0) THEN IER=20 CALL XERFFT('RFFT2F',-5) GO TO 100 ENDIF CALL W2R(LDIM,LDW,L,M,R,WORK) END IF C IF(MODL.EQ.0) THEN CALL RFFTMF(1,1,M,LDIM,R(L,1),M*LDIM, 1 WSAVE(LWSAV+MWSAV+1),MMSAV,WORK,LENWRK,IER1) DO J=2,2*((M+1)/2)-1 R(L,J) = .5*R(L,J) END DO DO J=3,M,2 R(L,J) = -R(L,J) END DO END IF C C PRINT*, 'FORWARD TRANSFORM IN THE J DIRECTION' C DO I=1,L C PRINT*, (R(I,J),J=1,M) C END DO C IF(IER1.NE.0) THEN IER=20 CALL XERFFT('RFFT2F',-5) GO TO 100 ENDIF C C 100 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C AUTHORS: PAUL N. SWARZTRAUBER AND RICHARD A. VALENT C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE RFFT2I (L, M, WSAVE, LENSAV, IER) INTEGER L, M, LENSAV, IER INTEGER LWSAV,MWSAV,MMSAV REAL WSAVE(LENSAV) C C INITIALIZE IER C IER = 0 C C VERIFY LENSAV C LWSAV = L+INT(LOG(REAL(L))/LOG(2.))+4 MWSAV = 2*M+INT(LOG(REAL(M))/LOG(2.))+4 MMSAV = M+INT(LOG(REAL(M))/LOG(2.))+4 IF (LENSAV .LT. LWSAV+MWSAV+MMSAV) THEN IER = 2 CALL XERFFT ('RFFT2I', 4) GO TO 100 ENDIF C CALL RFFTMI (L, WSAVE(1), LWSAV, IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('RFFT2I',-5) GO TO 100 ENDIF CALL CFFTMI (M, WSAVE(LWSAV+1),MWSAV,IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('RFFT2I',-5) GO TO 100 ENDIF C CALL RFFTMI (M,WSAVE(LWSAV+MWSAV+1),MMSAV, IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('RFFT2I',-5) GO TO 100 END IF C 100 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE RFFTB1 (N,IN,C,CH,WA,FAC) REAL CH(*), C(IN,*), WA(N) ,FAC(15) C NF = FAC(2) NA = 0 DO 10 K1=1,NF IP = FAC(K1+2) NA = 1-NA IF(IP .LE. 5) GO TO 10 IF(K1 .EQ. NF) GO TO 10 NA = 1-NA 10 CONTINUE HALF = .5 HALFM = -.5 MODN = MOD(N,2) NL = N-2 IF(MODN .NE. 0) NL = N-1 IF (NA .EQ. 0) GO TO 120 CH(1) = C(1,1) CH(N) = C(1,N) DO 118 J=2,NL,2 CH(J) = HALF*C(1,J) CH(J+1) = HALFM*C(1,J+1) 118 CONTINUE GO TO 124 120 DO 122 J=2,NL,2 C(1,J) = HALF*C(1,J) C(1,J+1) = HALFM*C(1,J+1) 122 CONTINUE 124 L1 = 1 IW = 1 DO 116 K1=1,NF IP = FAC(K1+2) L2 = IP*L1 IDO = N/L2 IDL1 = IDO*L1 IF (IP .NE. 4) GO TO 103 IX2 = IW+IDO IX3 = IX2+IDO IF (NA .NE. 0) GO TO 101 CALL R1F4KB (IDO,L1,C,IN,CH,1,WA(IW),WA(IX2),WA(IX3)) GO TO 102 101 CALL R1F4KB (IDO,L1,CH,1,C,IN,WA(IW),WA(IX2),WA(IX3)) 102 NA = 1-NA GO TO 115 103 IF (IP .NE. 2) GO TO 106 IF (NA .NE. 0) GO TO 104 CALL R1F2KB (IDO,L1,C,IN,CH,1,WA(IW)) GO TO 105 104 CALL R1F2KB (IDO,L1,CH,1,C,IN,WA(IW)) 105 NA = 1-NA GO TO 115 106 IF (IP .NE. 3) GO TO 109 IX2 = IW+IDO IF (NA .NE. 0) GO TO 107 CALL R1F3KB (IDO,L1,C,IN,CH,1,WA(IW),WA(IX2)) GO TO 108 107 CALL R1F3KB (IDO,L1,CH,1,C,IN,WA(IW),WA(IX2)) 108 NA = 1-NA GO TO 115 109 IF (IP .NE. 5) GO TO 112 IX2 = IW+IDO IX3 = IX2+IDO IX4 = IX3+IDO IF (NA .NE. 0) GO TO 110 CALL R1F5KB (IDO,L1,C,IN,CH,1,WA(IW),WA(IX2), 1 WA(IX3),WA(IX4)) GO TO 111 110 CALL R1F5KB (IDO,L1,CH,1,C,IN,WA(IW),WA(IX2), 1 WA(IX3),WA(IX4)) 111 NA = 1-NA GO TO 115 112 IF (NA .NE. 0) GO TO 113 CALL R1FGKB (IDO,IP,L1,IDL1,C,C,C,IN,CH,CH,1,WA(IW)) GO TO 114 113 CALL R1FGKB (IDO,IP,L1,IDL1,CH,CH,CH,1,C,C,IN,WA(IW)) 114 IF (IDO .EQ. 1) NA = 1-NA 115 L1 = L2 IW = IW+(IP-1)*IDO 116 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE RFFTF1 (N,IN,C,CH,WA,FAC) REAL CH(*) ,C(IN,*) ,WA(N) ,FAC(15) C NF = FAC(2) NA = 1 L2 = N IW = N DO 111 K1=1,NF KH = NF-K1 IP = FAC(KH+3) L1 = L2/IP IDO = N/L2 IDL1 = IDO*L1 IW = IW-(IP-1)*IDO NA = 1-NA IF (IP .NE. 4) GO TO 102 IX2 = IW+IDO IX3 = IX2+IDO IF (NA .NE. 0) GO TO 101 CALL R1F4KF (IDO,L1,C,IN,CH,1,WA(IW),WA(IX2),WA(IX3)) GO TO 110 101 CALL R1F4KF (IDO,L1,CH,1,C,IN,WA(IW),WA(IX2),WA(IX3)) GO TO 110 102 IF (IP .NE. 2) GO TO 104 IF (NA .NE. 0) GO TO 103 CALL R1F2KF (IDO,L1,C,IN,CH,1,WA(IW)) GO TO 110 103 CALL R1F2KF (IDO,L1,CH,1,C,IN,WA(IW)) GO TO 110 104 IF (IP .NE. 3) GO TO 106 IX2 = IW+IDO IF (NA .NE. 0) GO TO 105 CALL R1F3KF (IDO,L1,C,IN,CH,1,WA(IW),WA(IX2)) GO TO 110 105 CALL R1F3KF (IDO,L1,CH,1,C,IN,WA(IW),WA(IX2)) GO TO 110 106 IF (IP .NE. 5) GO TO 108 IX2 = IW+IDO IX3 = IX2+IDO IX4 = IX3+IDO IF (NA .NE. 0) GO TO 107 CALL R1F5KF (IDO,L1,C,IN,CH,1,WA(IW),WA(IX2), 1 WA(IX3),WA(IX4)) GO TO 110 107 CALL R1F5KF (IDO,L1,CH,1,C,IN,WA(IW),WA(IX2), 1 WA(IX3),WA(IX4)) GO TO 110 108 IF (IDO .EQ. 1) NA = 1-NA IF (NA .NE. 0) GO TO 109 CALL R1FGKF (IDO,IP,L1,IDL1,C,C,C,IN,CH,CH,1,WA(IW)) NA = 1 GO TO 110 109 CALL R1FGKF (IDO,IP,L1,IDL1,CH,CH,CH,1,C,C,IN,WA(IW)) NA = 0 110 L2 = L1 111 CONTINUE SN = 1./N TSN = 2./N TSNM = -TSN MODN = MOD(N,2) NL = N-2 IF(MODN .NE. 0) NL = N-1 IF (NA .NE. 0) GO TO 120 C(1,1) = SN*CH(1) DO 118 J=2,NL,2 C(1,J) = TSN*CH(J) C(1,J+1) = TSNM*CH(J+1) 118 CONTINUE IF(MODN .NE. 0) RETURN C(1,N) = SN*CH(N) RETURN 120 C(1,1) = SN*C(1,1) DO 122 J=2,NL,2 C(1,J) = TSN*C(1,J) C(1,J+1) = TSNM*C(1,J+1) 122 CONTINUE IF(MODN .NE. 0) RETURN C(1,N) = SN*C(1,N) RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE RFFTI1 (N,WA,FAC) REAL WA(N) ,FAC(15) INTEGER NTRYH(4) DOUBLE PRECISION TPI,ARGH,ARGLD,ARG DATA NTRYH(1),NTRYH(2),NTRYH(3),NTRYH(4)/4,2,3,5/ C NL = N NF = 0 J = 0 101 J = J+1 IF (J-4) 102,102,103 102 NTRY = NTRYH(J) GO TO 104 103 NTRY = NTRY+2 104 NQ = NL/NTRY NR = NL-NTRY*NQ IF (NR) 101,105,101 105 NF = NF+1 FAC(NF+2) = NTRY NL = NQ IF (NTRY .NE. 2) GO TO 107 IF (NF .EQ. 1) GO TO 107 DO 106 I=2,NF IB = NF-I+2 FAC(IB+2) = FAC(IB+1) 106 CONTINUE FAC(3) = 2 107 IF (NL .NE. 1) GO TO 104 FAC(1) = N FAC(2) = NF TPI = 8.D0*DATAN(1.D0) ARGH = TPI/FLOAT(N) IS = 0 NFM1 = NF-1 L1 = 1 IF (NFM1 .EQ. 0) RETURN DO 110 K1=1,NFM1 IP = FAC(K1+2) LD = 0 L2 = L1*IP IDO = N/L2 IPM = IP-1 DO 109 J=1,IPM LD = LD+L1 I = IS ARGLD = FLOAT(LD)*ARGH FI = 0. DO 108 II=3,IDO,2 I = I+2 FI = FI+1. ARG = FI*ARGLD WA(I-1) = DCOS(ARG) WA(I) = DSIN(ARG) 108 CONTINUE IS = IS+IDO 109 CONTINUE L1 = L2 110 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE RFFTMB (LOT, JUMP, N, INC, R, LENR, WSAVE, LENSAV, 1 WORK, LENWRK, IER) INTEGER LOT, JUMP, N, INC, LENR, LENSAV, LENWRK, IER REAL R(LENR), WSAVE(LENSAV) ,WORK(LENWRK) LOGICAL XERCON C IER = 0 C IF (LENR .LT. (LOT-1)*JUMP + INC*(N-1) + 1) THEN IER = 1 CALL XERFFT ('RFFTMB ', 6) ELSEIF (LENSAV .LT. N + INT(LOG(REAL(N))/LOG(2.)) +4) THEN IER = 2 CALL XERFFT ('RFFTMB ', 8) ELSEIF (LENWRK .LT. LOT*N) THEN IER = 3 CALL XERFFT ('RFFTMB ', 10) ELSEIF (.NOT. XERCON(INC,JUMP,N,LOT)) THEN IER = 4 CALL XERFFT ('RFFTMB ', -1) ENDIF C IF (N .EQ. 1) RETURN C CALL MRFTB1 (LOT,JUMP,N,INC,R,WORK,WSAVE,WSAVE(N+1)) RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE RFFTMF (LOT, JUMP, N, INC, R, LENR, WSAVE, LENSAV, 1 WORK, LENWRK, IER) INTEGER LOT, JUMP, N, INC, LENR, LENSAV, LENWRK, IER REAL R(LENR), WSAVE(LENSAV), WORK(LENWRK) LOGICAL XERCON C IER = 0 C IF (LENR .LT. (LOT-1)*JUMP + INC*(N-1) + 1) THEN IER = 1 CALL XERFFT ('RFFTMF ', 6) ELSEIF (LENSAV .LT. N + INT(LOG(REAL(N))/LOG(2.)) +4) THEN IER = 2 CALL XERFFT ('RFFTMF ', 8) ELSEIF (LENWRK .LT. LOT*N) THEN IER = 3 CALL XERFFT ('RFFTMF ', 10) ELSEIF (.NOT. XERCON(INC,JUMP,N,LOT)) THEN IER = 4 CALL XERFFT ('RFFTMF ', -1) ENDIF C IF (N .EQ. 1) RETURN C CALL MRFTF1 (LOT,JUMP,N,INC,R,WORK,WSAVE,WSAVE(N+1)) RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE RFFTMI (N, WSAVE, LENSAV, IER) INTEGER N, LENSAV, IER REAL WSAVE(LENSAV) C IER = 0 C IF (LENSAV .LT. N + INT(LOG(REAL(N))/LOG(2.)) +4) THEN IER = 2 CALL XERFFT ('RFFTMI ', 3) ENDIF C IF (N .EQ. 1) RETURN C CALL MRFTI1 (N,WSAVE(1),WSAVE(N+1)) RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE SINQ1B ( N, INC, X, LENX, WSAVE, LENSAV, 1 WORK, LENWRK, IER) INTEGER N, INC, LENX, LENSAV, LENWRK, IER REAL X(INC,*), WSAVE(LENSAV), WORK(LENWRK) C IER = 0 C IF (LENX .LT. INC*(N-1) + 1) THEN IER = 1 CALL XERFFT ('SINQ1B', 6) ELSEIF (LENSAV .LT. 2*N + INT(LOG(REAL(N))/LOG(2.)) +4) THEN IER = 2 CALL XERFFT ('SINQ1B', 8) ELSEIF (LENWRK .LT. N) THEN IER = 3 CALL XERFFT ('SINQ1B', 10) ENDIF C IF (N .GT. 1) GO TO 101 C X(1,1) = 4.*X(1,1) line disabled by Dick Valent 08/26/2010 RETURN 101 NS2 = N/2 DO 102 K=2,N,2 X(1,K) = -X(1,K) 102 CONTINUE CALL COSQ1B (N,INC,X,LENX,WSAVE,LENSAV,WORK,LENWRK,IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('SINQ1B',-5) GO TO 300 ENDIF DO 103 K=1,NS2 KC = N-K XHOLD = X(1,K) X(1,K) = X(1,KC+1) X(1,KC+1) = XHOLD 103 CONTINUE 300 RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE SINQ1F ( N, INC, X, LENX, WSAVE, LENSAV, 1 WORK, LENWRK, IER) INTEGER N, INC, LENX, LENSAV, LENWRK, IER REAL X(INC,*), WSAVE(LENSAV), WORK(LENWRK) C IER = 0 C IF (LENX .LT. INC*(N-1) + 1) THEN IER = 1 CALL XERFFT ('SINQ1F', 6) GO TO 300 ELSEIF (LENSAV .LT. 2*N + INT(LOG(REAL(N))/LOG(2.)) +4) THEN IER = 2 CALL XERFFT ('SINQ1F', 8) GO TO 300 ELSEIF (LENWRK .LT. N) THEN IER = 3 CALL XERFFT ('SINQ1F', 10) GO TO 300 ENDIF C IF (N .EQ. 1) RETURN NS2 = N/2 DO 101 K=1,NS2 KC = N-K XHOLD = X(1,K) X(1,K) = X(1,KC+1) X(1,KC+1) = XHOLD 101 CONTINUE CALL COSQ1F (N,INC,X,LENX,WSAVE,LENSAV,WORK,LENWRK,IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('SINQ1F',-5) GO TO 300 ENDIF DO 102 K=2,N,2 X(1,K) = -X(1,K) 102 CONTINUE 300 RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE SINQ1I (N, WSAVE, LENSAV, IER) INTEGER N, LENSAV, IER REAL WSAVE(LENSAV) C IER = 0 C IF (LENSAV .LT. 2*N + INT(LOG(REAL(N))/LOG(2.)) +4) THEN IER = 2 CALL XERFFT ('SINQ1I', 3) GO TO 300 ENDIF C CALL COSQ1I (N, WSAVE, LENSAV, IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('SINQ1I',-5) ENDIF 300 RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE SINQMB (LOT, JUMP, N, INC, X, LENX, WSAVE, LENSAV, 1 WORK, LENWRK, IER) INTEGER LOT, JUMP, N, INC, LENX, LENSAV, LENWRK, IER REAL X(INC,*), WSAVE(LENSAV), WORK(LENWRK) LOGICAL XERCON C IER = 0 C IF (LENX .LT. (LOT-1)*JUMP + INC*(N-1) + 1) THEN IER = 1 CALL XERFFT ('SINQMB', 6) ELSEIF (LENSAV .LT. 2*N + INT(LOG(REAL(N))/LOG(2.)) +4) THEN IER = 2 CALL XERFFT ('SINQMB', 8) ELSEIF (LENWRK .LT. LOT*N) THEN IER = 3 CALL XERFFT ('SINQMB', 10) ELSEIF (.NOT. XERCON(INC,JUMP,N,LOT)) THEN IER = 4 CALL XERFFT ('SINQMB', -1) ENDIF C LJ = (LOT-1)*JUMP+1 IF (N .GT. 1) GO TO 101 DO 201 M=1,LJ,JUMP X(M,1) = 4.*X(M,1) 201 CONTINUE RETURN 101 NS2 = N/2 DO 102 K=2,N,2 DO 202 M=1,LJ,JUMP X(M,K) = -X(M,K) 202 CONTINUE 102 CONTINUE CALL COSQMB (LOT,JUMP,N,INC,X,LENX,WSAVE,LENSAV,WORK,LENWRK,IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('SINQMB',-5) GO TO 300 ENDIF DO 103 K=1,NS2 KC = N-K DO 203 M=1,LJ,JUMP XHOLD = X(M,K) X(M,K) = X(M,KC+1) X(M,KC+1) = XHOLD 203 CONTINUE 103 CONTINUE 300 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE SINQMF (LOT, JUMP, N, INC, X, LENX, WSAVE, LENSAV, 1 WORK, LENWRK, IER) INTEGER LOT, JUMP, N, INC, LENX, LENSAV, LENWRK, IER REAL X(INC,*), WSAVE(LENSAV), WORK(LENWRK) LOGICAL XERCON C IER = 0 C IF (LENX .LT. (LOT-1)*JUMP + INC*(N-1) + 1) THEN IER = 1 CALL XERFFT ('SINQMF', 6) GO TO 300 ELSEIF (LENSAV .LT. 2*N + INT(LOG(REAL(N))/LOG(2.)) +4) THEN IER = 2 CALL XERFFT ('SINQMF', 8) GO TO 300 ELSEIF (LENWRK .LT. LOT*N) THEN IER = 3 CALL XERFFT ('SINQMF', 10) GO TO 300 ELSEIF (.NOT. XERCON(INC,JUMP,N,LOT)) THEN IER = 4 CALL XERFFT ('SINQMF', -1) GO TO 300 ENDIF C IF (N .EQ. 1) RETURN NS2 = N/2 LJ = (LOT-1)*JUMP+1 DO 101 K=1,NS2 KC = N-K DO 201 M=1,LJ,JUMP XHOLD = X(M,K) X(M,K) = X(M,KC+1) X(M,KC+1) = XHOLD 201 CONTINUE 101 CONTINUE CALL COSQMF (LOT,JUMP,N,INC,X,LENX,WSAVE,LENSAV,WORK,LENWRK,IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('SINQMF',-5) GO TO 300 ENDIF DO 102 K=2,N,2 DO 202 M=1,LJ,JUMP X(M,K) = -X(M,K) 202 CONTINUE 102 CONTINUE 300 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE SINQMI (N, WSAVE, LENSAV, IER) INTEGER N, LENSAV, IER REAL WSAVE(LENSAV) C IER = 0 C IF (LENSAV .LT. 2*N + INT(LOG(REAL(N))/LOG(2.)) +4) THEN IER = 2 CALL XERFFT ('SINQMI', 3) GO TO 300 ENDIF C CALL COSQMI (N, WSAVE, LENSAV, IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('SINQMI',-5) ENDIF 300 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE SINT1B ( N, INC, X, LENX, WSAVE, LENSAV, 1 WORK, LENWRK, IER) INTEGER N, INC, LENX, LENSAV, LENWRK, IER REAL X(INC,*), WSAVE(LENSAV), WORK(LENWRK) C IER = 0 C IF (LENX .LT. INC*(N-1) + 1) THEN IER = 1 CALL XERFFT ('SINT1B', 6) GO TO 100 ELSEIF (LENSAV .LT. N/2 + N + INT(LOG(REAL(N))/LOG(2.)) +4) THEN IER = 2 CALL XERFFT ('SINT1B', 8) GO TO 100 ELSEIF (LENWRK .LT. (2*N+2)) THEN IER = 3 CALL XERFFT ('SINT1B', 10) GO TO 100 ENDIF C CALL SINTB1(N,INC,X,WSAVE,WORK,WORK(N+2),IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('SINT1B',-5) ENDIF C 100 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE SINT1F ( N, INC, X, LENX, WSAVE, LENSAV, 1 WORK, LENWRK, IER) INTEGER N, INC, LENX, LENSAV, LENWRK, IER REAL X(INC,*), WSAVE(LENSAV), WORK(LENWRK) C IER = 0 IF (LENX .LT. INC*(N-1) + 1) THEN IER = 1 CALL XERFFT ('SINT1F', 6) GO TO 100 ELSEIF (LENSAV .LT. N/2 + N + INT(LOG(REAL(N))/LOG(2.)) +4) THEN IER = 2 CALL XERFFT ('SINT1F', 8) GO TO 100 ELSEIF (LENWRK .LT. (2*N+2)) THEN IER = 3 CALL XERFFT ('SINT1F', 10) GO TO 100 ENDIF C CALL SINTF1(N,INC,X,WSAVE,WORK,WORK(N+2),IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('SINT1F',-5) ENDIF 100 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE SINT1I (N, WSAVE, LENSAV, IER) INTEGER N, LENSAV, IER REAL WSAVE(LENSAV) C IER = 0 C IF (LENSAV .LT. N/2 + N + INT(LOG(REAL(N))/LOG(2.)) +4) THEN IER = 2 CALL XERFFT ('SINT1I', 3) GO TO 300 ENDIF C PI = 4.*ATAN(1.) IF (N .LE. 1) RETURN NS2 = N/2 NP1 = N+1 DT = PI/FLOAT(NP1) DO 101 K=1,NS2 WSAVE(K) = 2.*SIN(K*DT) 101 CONTINUE LNSV = NP1 + INT(LOG(REAL(NP1))/LOG(2.)) +4 CALL RFFT1I (NP1, WSAVE(NS2+1), LNSV, IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('SINT1I',-5) ENDIF C 300 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE SINTB1(N,INC,X,WSAVE,XH,WORK,IER) REAL X(INC,*) ,WSAVE(*) ,XH(*) DOUBLE PRECISION DSUM IER = 0 IF (N-2) 200,102,103 102 SRT3S2 = SQRT(3.)/2. XHOLD = SRT3S2*(X(1,1)+X(1,2)) X(1,2) = SRT3S2*(X(1,1)-X(1,2)) X(1,1) = XHOLD GO TO 200 103 NP1 = N+1 NS2 = N/2 DO 104 K=1,NS2 KC = NP1-K T1 = X(1,K)-X(1,KC) T2 = WSAVE(K)*(X(1,K)+X(1,KC)) XH(K+1) = T1+T2 XH(KC+1) = T2-T1 104 CONTINUE MODN = MOD(N,2) IF (MODN .EQ. 0) GO TO 124 XH(NS2+2) = 4.*X(1,NS2+1) 124 XH(1) = 0. LNXH = NP1 LNSV = NP1 + INT(LOG(REAL(NP1))/LOG(2.)) + 4 LNWK = NP1 C CALL RFFT1F(NP1,1,XH,LNXH,WSAVE(NS2+1),LNSV,WORK,LNWK,IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('SINTB1',-5) GO TO 200 ENDIF C IF(MOD(NP1,2) .NE. 0) GO TO 30 XH(NP1) = XH(NP1)+XH(NP1) 30 FNP1S4 = FLOAT(NP1)/4. X(1,1) = FNP1S4*XH(1) DSUM = X(1,1) DO 105 I=3,N,2 X(1,I-1) = FNP1S4*XH(I) DSUM = DSUM+FNP1S4*XH(I-1) X(1,I) = DSUM 105 CONTINUE IF (MODN .NE. 0) GO TO 200 X(1,N) = FNP1S4*XH(N+1) C 200 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE SINTF1(N,INC,X,WSAVE,XH,WORK,IER) REAL X(INC,*) ,WSAVE(*) ,XH(*) DOUBLE PRECISION DSUM IER = 0 IF (N-2) 200,102,103 102 SSQRT3 = 1./SQRT(3.) XHOLD = SSQRT3*(X(1,1)+X(1,2)) X(1,2) = SSQRT3*(X(1,1)-X(1,2)) X(1,1) = XHOLD GO TO 200 103 NP1 = N+1 NS2 = N/2 DO 104 K=1,NS2 KC = NP1-K T1 = X(1,K)-X(1,KC) T2 = WSAVE(K)*(X(1,K)+X(1,KC)) XH(K+1) = T1+T2 XH(KC+1) = T2-T1 104 CONTINUE MODN = MOD(N,2) IF (MODN .EQ. 0) GO TO 124 XH(NS2+2) = 4.*X(1,NS2+1) 124 XH(1) = 0. LNXH = NP1 LNSV = NP1 + INT(LOG(REAL(NP1))/LOG(2.)) + 4 LNWK = NP1 C CALL RFFT1F(NP1,1,XH,LNXH,WSAVE(NS2+1),LNSV,WORK, 1 LNWK,IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('SINTF1',-5) GO TO 200 ENDIF C IF(MOD(NP1,2) .NE. 0) GO TO 30 XH(NP1) = XH(NP1)+XH(NP1) 30 SFNP1 = 1./FLOAT(NP1) X(1,1) = .5*XH(1) DSUM = X(1,1) DO 105 I=3,N,2 X(1,I-1) = .5*XH(I) DSUM = DSUM+.5*XH(I-1) X(1,I) = DSUM 105 CONTINUE IF (MODN .NE. 0) GO TO 200 X(1,N) = .5*XH(N+1) 200 RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE SINTMB (LOT, JUMP, N, INC, X, LENX, WSAVE, LENSAV, 1 WORK, LENWRK, IER) INTEGER LOT, JUMP, N, INC, LENX, LENSAV, LENWRK, IER REAL X(INC,*), WSAVE(LENSAV), WORK(LENWRK) LOGICAL XERCON C IER = 0 C IF (LENX .LT. (LOT-1)*JUMP + INC*(N-1) + 1) THEN IER = 1 CALL XERFFT ('SINTMB', 6) GO TO 100 ELSEIF (LENSAV .LT. N/2 + N + INT(LOG(REAL(N))/LOG(2.)) +4) THEN IER = 2 CALL XERFFT ('SINTMB', 8) GO TO 100 ELSEIF (LENWRK .LT. LOT*(2*N+4)) THEN IER = 3 CALL XERFFT ('SINTMB', 10) GO TO 100 ELSEIF (.NOT. XERCON(INC,JUMP,N,LOT)) THEN IER = 4 CALL XERFFT ('SINTMB', -1) GO TO 100 ENDIF C IW1 = LOT+LOT+1 IW2 = IW1+LOT*(N+1) CALL MSNTB1(LOT,JUMP,N,INC,X,WSAVE,WORK,WORK(IW1),WORK(IW2),IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('SINTMB',-5) ENDIF C 100 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE SINTMF (LOT, JUMP, N, INC, X, LENX, WSAVE, LENSAV, 1 WORK, LENWRK, IER) INTEGER LOT, JUMP, N, INC, LENX, LENSAV, LENWRK, IER REAL X(INC,*), WSAVE(LENSAV), WORK(LENWRK) LOGICAL XERCON C IER = 0 C IF (LENX .LT. (LOT-1)*JUMP + INC*(N-1) + 1) THEN IER = 1 CALL XERFFT ('SINTMF', 6) GO TO 100 ELSEIF (LENSAV .LT. N/2 + N + INT(LOG(REAL(N))/LOG(2.)) +4) THEN IER = 2 CALL XERFFT ('SINTMF', 8) GO TO 100 ELSEIF (LENWRK .LT. LOT*(2*N+4)) THEN IER = 3 CALL XERFFT ('SINTMF', 10) GO TO 100 ELSEIF (.NOT. XERCON(INC,JUMP,N,LOT)) THEN IER = 4 CALL XERFFT ('SINTMF', -1) GO TO 100 ENDIF C IW1 = LOT+LOT+1 IW2 = IW1+LOT*(N+1) CALL MSNTF1(LOT,JUMP,N,INC,X,WSAVE,WORK,WORK(IW1),WORK(IW2),IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('SINTMF',-5) ENDIF 100 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE SINTMI (N, WSAVE, LENSAV, IER) INTEGER N, LENSAV, IER REAL WSAVE(LENSAV) C IER = 0 C IF (LENSAV .LT. N/2 + N + INT(LOG(REAL(N))/LOG(2.)) +4) THEN IER = 2 CALL XERFFT ('SINTMI', 3) GO TO 300 ENDIF C PI = 4.*ATAN(1.) IF (N .LE. 1) RETURN NS2 = N/2 NP1 = N+1 DT = PI/FLOAT(NP1) DO 101 K=1,NS2 WSAVE(K) = 2.*SIN(K*DT) 101 CONTINUE LNSV = NP1 + INT(LOG(REAL(NP1))/LOG(2.)) +4 CALL RFFTMI (NP1, WSAVE(NS2+1), LNSV, IER1) IF (IER1 .NE. 0) THEN IER = 20 CALL XERFFT ('SINTMI',-5) ENDIF C 300 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE TABLES (IDO,IP,WA) REAL WA(IDO,IP-1,2) C TPI = 8.*ATAN(1.) ARGZ = TPI/REAL(IP) ARG1 = TPI/REAL(IDO*IP) DO 110 J=2,IP ARG2 = REAL(J-1)*ARG1 DO 100 I=1,IDO ARG3 = REAL(I-1)*ARG2 WA(I,J-1,1) = COS(ARG3) WA(I,J-1,2) = SIN(ARG3) 100 CONTINUE IF (IP .LE. 5) GO TO 110 ARG4 = REAL(J-1)*ARGZ WA(1,J-1,1) = COS(ARG4) WA(1,J-1,2) = SIN(ARG4) 110 CONTINUE RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C AUTHORS: PAUL N. SWARZTRAUBER AND RICHARD A. VALENT C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC subroutine w2r(ldr,ldw,l,m,r,w) dimension r(ldr,*),w(ldw,*) do j=1,m do i=1,l r(i,j) = w( i,j) end do end do return end CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC LOGICAL FUNCTION XERCON (INC,JUMP,N,LOT) INTEGER INC, JUMP, N, LOT INTEGER I, J, JNEW, LCM C C Definition: positive integers INC, JUMP, N and LOT are consistent C ---------- C if I1*INC + J1*JUMP = I2*INC + J2*JUMP for I1,I2 < N and J1,J2 C < LOT implies I1=I2 and J1=J2. C C For multiple FFTs to execute correctly, input parameters INC, C JUMP, N and LOT must be consistent ... otherwise at least one C array element mistakenly is transformed more than once. C C XERCON = .TRUE. if and only if INC, JUMP, N and LOT are C consistent. C C ------------------------------------------------------------------ C C Compute I = greatest common divisor (INC, JUMP) C I = INC J = JUMP 10 CONTINUE IF (J .NE. 0) THEN JNEW = MOD(I,J) I = J J = JNEW GO TO 10 ENDIF C C Compute LCM = least common multiple (INC, JUMP) C LCM = (INC*JUMP)/I C C Check consistency of INC, JUMP, N, LOT C IF (LCM .LE. (N-1)*INC .AND. LCM .LE. (LOT-1)*JUMP) THEN XERCON = .FALSE. ELSE XERCON = .TRUE. ENDIF C RETURN END CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC C C FFTPACK 5.1 C C Authors: Paul N. Swarztrauber and Richard A. Valent C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC SUBROUTINE XERFFT( SRNAME, INFO) C C .. Scalar Arguments .. CHARACTER*6 SRNAME INTEGER INFO C C .. C C Purpose C ======= C C XERFFT is an error handler for library FFTPACK version 5.1 routines. C It is called by an FFTPACK 5.1 routine if an input parameter has an C invalid value. A message is printed and execution stops. C C Installers may consider modifying the STOP statement in order to C call system-specific exception-handling facilities. C C Arguments C ========= C C SRNAME (input) CHARACTER*6 C The name of the routine which called XERFFT. C C INFO (input) INTEGER C When a single invalid parameter in the parameter list of C the calling routine has been detected, INFO is the position C of that parameter. In the case when an illegal combination C of LOT, JUMP, N, and INC has been detected, the calling C subprogram calls XERFFT with INFO = -1. C C ===================================================================== C C .. Executable Statements .. C IF (INFO .GE. 1) THEN WRITE( *, '(A,A,A,I3,A)') ' ** On entry to ', SRNAME, 1 ' parameter number ', INFO, ' had an illegal value' ELSEIF (INFO .EQ. -1) THEN WRITE( *, '(A,A,A,A)') ' ** On entry to ', SRNAME, 1 ' parameters LOT, JUMP, N and INC are inconsistent' ELSEIF (INFO .EQ. -2) THEN WRITE( *, '(A,A,A,A)') ' ** On entry to ', SRNAME, 1 ' parameter L is greater than LDIM' ELSEIF (INFO .EQ. -3) THEN WRITE( *, '(A,A,A,A)') ' ** On entry to ', SRNAME, 1 ' parameter M is greater than MDIM' ELSEIF (INFO .EQ. -5) THEN WRITE( *, '(A,A,A,A)') ' ** Within ', SRNAME, 1 ' input error returned by lower level routine' ELSEIF (INFO .EQ. -6) THEN WRITE( *, '(A,A,A,A)') ' ** On entry to ', SRNAME, 1 ' parameter LDIM is less than 2*(L/2+1)' ENDIF C STOP C C End of XERFFT C END casacore-3.7.1/scimath_f/fgridft.f000066400000000000000000000203231476623553700170550ustar00rootroot00000000000000*======================================================================= * Copyright (C) 1999,2001,2002 * Associated Universities, Inc. Washington DC, USA. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, * MA 02139, USA. * * Correspondence concerning AIPS++ should be addressed as follows: * Internet email: casa-feedback@nrao.edu. * Postal address: AIPS++ Project Office * National Radio Astronomy Observatory * 520 Edgemont Road * Charlottesville, VA 22903-2475 USA * * $Id$ *----------------------------------------------------------------------- C C Grid a number of visibility records C subroutine ggridft (uvw, dphase, values, nvispol, nvischan, $ dopsf, flag, rflag, weight, nrow, rownum, $ scale, offset, grid, nx, ny, npol, nchan, freq, c, $ support, sampling, convFunc, chanmap, polmap, sumwt) implicit none integer nx, ny, npol, nchan, nvispol, nvischan, nrow complex values(nvispol, nvischan, nrow) complex grid(nx, ny, npol, nchan) double precision uvw(3, nrow), freq(nvischan), c, scale(2), $ offset(2) double precision dphase(nrow), uvdist complex phasor integer flag(nvispol, nvischan, nrow) integer rflag(nrow) real weight(nvischan, nrow) double precision sumwt(npol, nchan) integer rownum integer support, sampling integer chanmap(nchan), polmap(npol) integer dopsf complex nvalue double precision convFunc(*) real norm real wt, wtx, wty logical ogridft real pos(2) integer loc(2), off(2), iloc(2) integer rbeg, rend integer ix, iy, ipol, ichan integer apol, achan, irow irow=rownum if(irow.ge.0) then rbeg=irow+1 rend=irow+1 else rbeg=1 rend=nrow end if do irow=rbeg, rend if(rflag(irow).eq.0) then do ichan=1, nvischan achan=chanmap(ichan)+1 if((achan.ge.1).and.(achan.le.nchan).and. C $ (weight(ichan,irow).gt.0.0)) then $ (weight(ichan,irow).ne.0.0)) then call sgridft(uvw(1,irow), dphase(irow), freq(ichan), c, $ scale, offset, sampling, pos, loc, off, phasor) if (ogridft(nx, ny, loc, support)) then do ipol=1, nvispol apol=polmap(ipol)+1 if((flag(ipol,ichan,irow).ne.1).and. $ (apol.ge.1).and.(apol.le.npol)) then C If we are making a PSF then we don't want to phase C rotate but we do want to reproject uvw if(dopsf.eq.1) then nvalue=cmplx(weight(ichan,irow)) else nvalue=weight(ichan,irow)* $ (values(ipol,ichan,irow)*phasor) end if norm=0.0 do iy=-support,support iloc(2)=abs(sampling*iy+off(2))+1 wty=convFunc(iloc(2)) do ix=-support,support iloc(1)=abs(sampling*ix+off(1))+1 wtx=convFunc(iloc(1)) wt=wtx*wty grid(loc(1)+ix,loc(2)+iy,apol,achan)= $ grid(loc(1)+ix,loc(2)+iy,apol,achan)+ $ nvalue*wt norm=norm+wt end do end do sumwt(apol,achan)=sumwt(apol,achan)+ $ weight(ichan,irow)*norm end if end do end if end if end do end if end do return end C C Degrid a number of visibility records C subroutine dgridft (uvw, dphase, values, nvispol, nvischan, $ flag, rflag, $ nrow, rownum, scale, offset, grid, nx, ny, npol, nchan, freq, $ c, support, sampling, convFunc, chanmap, polmap) implicit none integer nx, ny, npol, nchan, nvispol, nvischan, nrow complex values(nvispol, nvischan, nrow) complex grid(nx, ny, npol, nchan) double precision uvw(3, nrow), freq(nvischan), c, scale(2), $ offset(2) double precision dphase(nrow), uvdist complex phasor integer flag(nvispol, nvischan, nrow) integer rflag(nrow) integer rownum integer support, sampling integer chanmap(*), polmap(*) complex nvalue double precision convFunc(*) real norm logical ogridft real pos(2) integer loc(2), off(2), iloc(2) integer rbeg, rend integer ix, iy, ipol, ichan integer apol, achan, irow real wt, wtx, wty irow=rownum if(irow.ge.0) then rbeg=irow+1 rend=irow+1 else rbeg=1 rend=nrow end if do irow=rbeg, rend if(rflag(irow).eq.0) then do ichan=1, nvischan achan=chanmap(ichan)+1 if((achan.ge.1).and.(achan.le.nchan)) then call sgridft(uvw(1,irow), dphase(irow), freq(ichan), c, $ scale, offset, sampling, pos, loc, off, phasor) if (ogridft(nx, ny, loc, support)) then do ipol=1, nvispol apol=polmap(ipol)+1 if((flag(ipol,ichan,irow).ne.1).and. $ (apol.ge.1).and.(apol.le.npol)) then nvalue=0.0 norm=0.0 do iy=-support,support iloc(2)=abs(sampling*iy+off(2))+1 wty=convFunc(iloc(2)) do ix=-support,support iloc(1)=abs(sampling*ix+off(1))+1 wtx=convFunc(iloc(1)) wt=wtx*wty norm=norm+wt nvalue=nvalue+wt* $ grid(loc(1)+ix,loc(2)+iy,apol,achan) end do end do values(ipol,ichan,irow)=(nvalue*conjg(phasor)) $ /norm end if end do end if end if end do end if end do return end C C Calculate gridded coordinates and the phasor needed for C phase rotation. C subroutine sgridft (uvw, dphase, freq, c, scale, offset, sampling, $ pos, loc, off, phasor) implicit none integer sampling integer loc(2), off(2) double precision uvw(3), freq, c, scale(2), offset(2) real pos(2) double precision dphase, phase complex phasor integer idim double precision pi data pi/3.14159265358979323846/ do idim=1,2 pos(idim)=scale(idim)*uvw(idim)*freq/c+(offset(idim)+1.0) loc(idim)=nint(pos(idim)) off(idim)=nint((loc(idim)-pos(idim))*sampling) end do phase=-2.0D0*pi*dphase*freq/c phasor=cmplx(cos(phase), sin(phase)) return end C C Is this on the grid? C logical function ogridft (nx, ny, loc, support) implicit none integer nx, ny, loc(2), support ogridft=(loc(1)-support.ge.1).and.(loc(1)+support.le.nx).and. $ (loc(2)-support.ge.1).and.(loc(2)+support.le.ny) return end casacore-3.7.1/scimath_f/fgridsd.f000066400000000000000000000355201476623553700170570ustar00rootroot00000000000000*======================================================================= * Copyright (C) 1999,2001,2002 * Associated Universities, Inc. Washington DC, USA. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, * MA 02139, USA. * * Correspondence concerning AIPS++ should be addressed as follows: * Internet email: casa-feedback@nrao.edu. * Postal address: AIPS++ Project Office * National Radio Astronomy Observatory * 520 Edgemont Road * Charlottesville, VA 22903-2475 USA * * $Id$ *----------------------------------------------------------------------- C C Grid a number of visibility records: single dish gridding C but with complex images C subroutine ggridsd (xy, values, nvispol, nvischan, $ dowt, flag, rflag, weight, nrow, irow, $ grid, wgrid, nx, ny, npol, nchan, $ support, sampling, convFunc, chanmap, polmap, sumwt) implicit none integer nx, ny, npol, nchan, nvispol, nvischan, nrow complex values(nvispol, nvischan, nrow) complex grid(nx, ny, npol, nchan) real wgrid(nx, ny, npol, nchan) double precision xy(2,nrow) integer flag(nvispol, nvischan, nrow) integer rflag(nrow) real weight(nvischan, nrow) double precision sumwt(npol, nchan) integer irow integer support, sampling integer chanmap(nvischan), polmap(nvispol) integer dowt complex nvalue real convFunc(*) real norm real wt, wtx, wty logical ogridsd real pos(2), rloc(2) integer loc(2), off(2) integer rbeg, rend integer irad((2*support+1)**2) integer ix, iy, ipol, ichan integer apol, achan integer ir integer xloc(2*support+1), yloc(2*support+1) integer ax, ay if(irow.ge.0) then rbeg=irow+1 rend=irow+1 else rbeg=1 rend=nrow end if do irow=rbeg, rend if(rflag(irow).eq.0) then call sgridsd(xy(1,irow), sampling, pos, loc, off) C if (ogridsd(nx, ny, loc, support)) then if (ogridsd(nx, ny, loc, 0)) then ir=1 norm=-(support+1)*sampling+off(1) rloc(2)=-(support+1)*sampling+off(2) do iy=1,2*support+1 rloc(2)=rloc(2)+sampling rloc(1)=norm do ix=1,2*support+1 rloc(1)=rloc(1)+sampling irad(ir)=sqrt(rloc(1)**2+rloc(2)**2)+1 ir=ir+1 end do end do xloc(1)=loc(1)-support do ix=2,2*support+1 xloc(ix)=xloc(ix-1)+1 end do yloc(1)=loc(2)-support do iy=2,2*support+1 yloc(iy)=yloc(iy-1)+1 end do do ichan=1, nvischan achan=chanmap(ichan)+1 if((achan.ge.1).and.(achan.le.nchan).and. $ (weight(ichan,irow).gt.0.0)) then do ipol=1, nvispol apol=polmap(ipol)+1 if((flag(ipol,ichan,irow).ne.1).and. $ (apol.ge.1).and.(apol.le.npol)) then if(dowt.eq.1) then nvalue=cmplx(weight(ichan,irow)) else nvalue=weight(ichan,irow)* $ conjg(values(ipol,ichan,irow)) end if norm=0.0 ir=1 C do iy=-support,support do iy=1,2*support+1 ay=yloc(iy) if ((ay.ge.1).and.(ay.le.ny)) then C do ix=-support,support do ix=1,2*support+1 ax=xloc(ix) if ((ax.ge.1).and.(ax.le.nx)) then ir = (iy-1)*(2*support+1) + ix wt=convFunc(irad(ir)) grid(ax,ay,apol,achan)= $ grid(ax,ay,apol,achan)+ $ nvalue*wt wgrid(ax,ay,apol,achan)= $ wgrid(ax,ay,apol,achan)+ $ weight(ichan,irow)*wt norm=norm+wt end if C ir=ir+1 end do end if end do sumwt(apol,achan)=sumwt(apol,achan)+ $ weight(ichan,irow)*norm end if end do end if end do end if end if end do return end C C Degrid a number of visibility records: single dish gridding C subroutine dgridsd (xy, values, nvispol, nvischan, flag, $ rflag, nrow, irow, grid, nx, ny, npol, nchan, $ support, sampling, convFunc, chanmap, polmap) implicit none integer nx, ny, npol, nchan, nvispol, nvischan, nrow complex values(nvispol, nvischan, nrow) complex grid(nx, ny, npol, nchan) double precision xy(2, nrow) integer flag(nvispol, nvischan, nrow) integer rflag(nrow) integer irow integer support, sampling integer chanmap(*), polmap(*) complex nvalue real convFunc(*) real norm logical ogridsd real pos(2), rloc(2) integer loc(2), off(2) integer rbeg, rend, irad integer ix, iy, ipol, ichan integer apol, achan real wt, wtx, wty integer ax, ay if(irow.ge.0) then rbeg=irow+1 rend=irow+1 else rbeg=1 rend=nrow end if do irow=rbeg, rend if(rflag(irow).eq.0) then call sgridsd(xy(1, irow), sampling, pos, loc, off) C if (ogridsd(nx, ny, loc, support)) then if (ogridsd(nx, ny, loc, 0)) then do ichan=1, nvischan achan=chanmap(ichan)+1 if((achan.ge.1).and.(achan.le.nchan)) then do ipol=1, nvispol apol=polmap(ipol)+1 if((flag(ipol,ichan,irow).ne.1).and. $ (apol.ge.1).and.(apol.le.npol)) then nvalue=0.0 do iy=-support,support rloc(2)=sampling*iy+off(2) ay=loc(2)+iy if ((ay.ge.1).and.(ay.le.ny)) then do ix=-support,support ax=loc(1)+ix if ((ax.ge.1).and.(ax.le.nx)) then rloc(1)=sampling*ix+off(1) irad=sqrt(rloc(1)**2+rloc(2)**2)+1 wt=convFunc(irad) nvalue=nvalue+wt* $ grid(ax,ay,apol,achan) end if end do end if end do values(ipol,ichan,irow)=conjg(nvalue) end if end do end if end do end if endif end do return end C C Calculate gridded coordinates C subroutine sgridsd (xy, sampling, pos, loc, off) implicit none integer sampling integer loc(2), off(2) double precision xy(2) real pos(2) integer idim do idim=1,2 pos(idim)=xy(idim)+1.0 loc(idim)=nint(pos(idim)) off(idim)=nint((loc(idim)-pos(idim))*sampling) end do return end C C Is this on the grid? C logical function ogridsd (nx, ny, loc, support) implicit none integer nx, ny, loc(2), support ogridsd=(loc(1)-support.ge.1).and.(loc(1)+support.le.nx).and. $ (loc(2)-support.ge.1).and.(loc(2)+support.le.ny) return end C C Grid a number of visibility records: single dish gridding C but with complex images including additional process for C min/max clipping C subroutine ggridsd2 (xy, values, nvispol, nvischan, $ dowt, flag, rflag, weight, nrow, irow, $ grid, wgrid, $ npoints, gmin, wmin, cmin, gmax, wmax, cmax, $ nx, ny, npol, nchan, $ support, sampling, convFunc, chanmap, polmap, sumwt) implicit none integer nx, ny, npol, nchan, nvispol, nvischan, nrow complex values(nvispol, nvischan, nrow) complex grid(nx, ny, npol, nchan) real wgrid(nx, ny, npol, nchan) integer npoints(nx, ny, npol) complex gmin(nx, ny, npol, nchan) complex gmax(nx, ny, npol, nchan) real wmin(nx, ny, npol, nchan) real wmax(nx, ny, npol, nchan) real cmax(nx, ny, npol, nchan) real cmin(nx, ny, npol, nchan) double precision xy(2,nrow) integer flag(nvispol, nvischan, nrow) integer rflag(nrow) real weight(nvischan, nrow) double precision sumwt(npol, nchan) integer irow integer support, sampling integer chanmap(nvischan), polmap(nvispol) integer dowt complex nvalue real convFunc(*) real norm real wt, wtx, wty logical ogridsd real pos(2), rloc(2) integer loc(2), off(2) integer rbeg, rend integer irad((2*support+1)**2) integer ix, iy, ipol, ichan integer apol, achan integer ir integer xloc(2*support+1), yloc(2*support+1) integer ax, ay if(irow.ge.0) then rbeg=irow+1 rend=irow+1 else rbeg=1 rend=nrow end if do irow=rbeg, rend if(rflag(irow).eq.0) then call sgridsd(xy(1,irow), sampling, pos, loc, off) C if (ogridsd(nx, ny, loc, support)) then if (ogridsd(nx, ny, loc, 0)) then ir=1 norm=-(support+1)*sampling+off(1) rloc(2)=-(support+1)*sampling+off(2) do iy=1,2*support+1 rloc(2)=rloc(2)+sampling rloc(1)=norm do ix=1,2*support+1 rloc(1)=rloc(1)+sampling irad(ir)=sqrt(rloc(1)**2+rloc(2)**2)+1 ir=ir+1 end do end do xloc(1)=loc(1)-support do ix=2,2*support+1 xloc(ix)=xloc(ix-1)+1 end do yloc(1)=loc(2)-support do iy=2,2*support+1 yloc(iy)=yloc(iy-1)+1 end do do ichan=1, nvischan achan=chanmap(ichan)+1 if((achan.ge.1).and.(achan.le.nchan).and. $ (weight(ichan,irow).gt.0.0)) then do ipol=1, nvispol apol=polmap(ipol)+1 if((flag(ipol,ichan,irow).ne.1).and. $ (apol.ge.1).and.(apol.le.npol)) then if(dowt.eq.1) then nvalue=cmplx(weight(ichan,irow)) else nvalue=weight(ichan,irow)* $ conjg(values(ipol,ichan,irow)) end if norm=0.0 ir=1 C do iy=-support,support do iy=1,2*support+1 ay=yloc(iy) if ((ay.ge.1).and.(ay.le.ny)) then C do ix=-support,support do ix=1,2*support+1 ax=xloc(ix) if ((ax.ge.1).and.(ax.le.nx)) then ir = (iy-1)*(2*support+1) + ix wt=convFunc(irad(ir)) grid(ax,ay,apol,achan)= $ grid(ax,ay,apol,achan)+ $ nvalue*wt wgrid(ax,ay,apol,achan)= $ wgrid(ax,ay,apol,achan)+ $ weight(ichan,irow)*wt norm=norm+wt C ir=ir+1 C------------------------------------------------------------------- C update variables for clipping C------------------------------------------------------------------- if (wt .gt. 0.0) then if (ichan .eq. 1) then npoints(ax,ay,apol)= $ npoints(ax,ay,apol)+1 end if if (real(values(ipol,ichan,irow)) .lt. $ real(gmin(ax,ay,apol,achan))) then gmin(ax,ay,apol,achan)= $ conjg(values(ipol,ichan,irow)) wmin(ax,ay,apol,achan)= $ weight(ichan,irow) cmin(ax,ay,apol,achan)=wt end if if (real(values(ipol,ichan,irow)) .gt. $ real(gmax(ax,ay,apol,achan))) then gmax(ax,ay,apol,achan)= $ conjg(values(ipol,ichan,irow)) wmax(ax,ay,apol,achan)= $ weight(ichan,irow) cmax(ax,ay,apol,achan)=wt end if end if C------------------------------------------------------------------- end if end do end if end do sumwt(apol,achan)=sumwt(apol,achan)+ $ weight(ichan,irow)*norm end if end do end if end do end if end if end do return end casacore-3.7.1/scimath_f/fmosft.f000066400000000000000000000332761476623553700167410ustar00rootroot00000000000000*======================================================================= * Copyright (C) 1999,2001,2002 * Associated Universities, Inc. Washington DC, USA. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, * MA 02139, USA. * * Correspondence concerning AIPS++ should be addressed as follows: * Internet email: casa-feedback@nrao.edu. * Postal address: AIPS++ Project Office * National Radio Astronomy Observatory * 520 Edgemont Road * Charlottesville, VA 22903-2475 USA * * $Id$ *----------------------------------------------------------------------- C C Grid a number of visibility records C subroutine gmosft (uvw, dphase, values, nvispol, nvischan, $ dopsf, flag, rflag, weight, nrow, rownum, $ scale, offset, grid, nx, ny, npol, nchan, freq, c, $ support, convsize, sampling, convfunc, $ chanmap, polmap, $ sumwt, weightgrid, convweight, doweightgrid, convplanemap, $ nconvplane) implicit none integer nx, ny, npol, nchan, nvispol, nvischan, nrow complex values(nvispol, nvischan, nrow) complex grid(nx, ny, npol, nchan) double precision uvw(3, nrow), freq(nvischan), c, scale(3), $ offset(3) double precision dphase(nrow), uvdist double precision xlast, ylast complex phasor integer flag(nvispol, nvischan, nrow) integer rflag(nrow) real weight(nvischan, nrow), phase double precision sumwt(npol, nchan) integer rownum integer support integer chanmap(nchan), polmap(npol) integer dopsf complex weightgrid(nx, ny, npol, nchan) integer doweightgrid complex nvalue complex nweight integer convsize, sampling integer nconvplane integer convplanemap(nrow) complex convfunc(convsize, convsize, nconvplane), cwt, crot complex convweight(convsize, convsize, nconvplane) complex shiftx(-support:support), shifty(-support:support) complex sconv(-support:support, -support:support, nconvplane) complex sconv2(-support:support, -support:support, nconvplane) real sumsconv real sumsconv2 real ratioofbeam real norm real wt logical omosft, doshift real pos(3) integer loc(3), off(3), iloc(3) integer rbeg, rend integer ix, iy, iz, ipol, ichan integer apol, achan, aconvplane, irow double precision pi data pi/3.14159265358979323846/ irow=rownum sumsconv=0 sumsconv2=0 ratioofbeam=1.0 do ix=-support,support shiftx(ix)=1.0 shifty(ix)=1.0 end do do iz=1, nconvplane do iy=-support,support iloc(2)=iy+convsize/2+1 do ix=-support,support iloc(1)=ix+convsize/2+1 sconv(ix,iy,iz)=(convfunc(iloc(1), iloc(2),iz)) sconv2(ix,iy,iz)=convweight(iloc(1), iloc(2),iz) end do end do end do doshift=.FALSE. if(irow.ge.0) then rbeg=irow+1 rend=irow+1 else rbeg=1 rend=nrow end if xlast=0.0 ylast=0.0 do irow=rbeg, rend aconvplane=convplanemap(irow)+1 if(rflag(irow).eq.0) then do ichan=1, nvischan achan=chanmap(ichan)+1 if((achan.ge.1).and.(achan.le.nchan).and. $ (weight(ichan,irow).gt.0.0)) then call smosft(uvw(1,irow), dphase(irow), freq(ichan), c, $ scale, offset, sampling, pos, loc, off, phasor) if (omosft(nx, ny, loc, support)) then do ipol=1, nvispol apol=polmap(ipol)+1 if((flag(ipol,ichan,irow).ne.1).and. $ (apol.ge.1).and.(apol.le.npol)) then C If we are making a PSF then we don't want to phase C rotate but we do want to reproject uvw if(dopsf.eq.1) then nvalue=cmplx(weight(ichan,irow)) else nvalue=weight(ichan,irow)* $ (values(ipol,ichan,irow)*phasor) end if if(doweightgrid .gt. 0) then nweight=cmplx(weight(ichan,irow)) end if C norm will be the value we would get for the peak C at the phase center. We will want to normalize C the final image by this term. norm=0.0 if(sampling.eq.1) then do iy=-support,support do ix=-support,support grid(loc(1)+ix, $ loc(2)+iy,apol,achan)= $ grid(loc(1)+ix, $ loc(2)+iy,apol,achan)+ $ nvalue*sconv(ix,iy, aconvplane) if(doweightgrid .gt. 0) then iloc(1)=nx/2+1+ix iloc(2)=ny/2+1+iy weightgrid(iloc(1),iloc(2), $ apol,achan)= weightgrid( $ iloc(1),iloc(2),apol,achan) $ + nweight*sconv2(ix,iy,aconvplane) end if end do end do else do ix=-support,support iloc(1)=convsize/2+1+ix*sampling $ +off(1) if(doshift) then cwt=conjg(convfunc(iloc(1), $ iloc(2),aconvplane))* $ shiftx(ix)*shifty(iy) sumsconv=sumsconv+real(cwt) else cwt=conjg(convfunc(iloc(1), $ iloc(2),aconvplane)) sumsconv=sumsconv+real(cwt) end if grid(loc(1)+ix, $ loc(2)+iy,apol,achan)= $ grid(loc(1)+ix, $ loc(2)+iy,apol,achan)+ $ nvalue*cwt norm=norm+real(cwt) end do end if sumwt(apol, achan)= sumwt(apol,achan)+ $ weight(ichan,irow) end if end do end if end if end do end if end do return end C C Degrid a number of visibility records C subroutine dmosft (uvw, dphase, values, nvispol, nvischan, $ flag, rflag, $ nrow, rownum, scale, offset, grid, nx, ny, npol, nchan, freq, $ c, support, convsize, sampling, convfunc, $ chanmap, polmap, convplanemap, nconvplane) implicit none integer nx, ny, npol, nchan, nvispol, nvischan, nrow integer nconvplane complex values(nvispol, nvischan, nrow) complex grid(nx, ny, npol, nchan) double precision uvw(3, nrow), freq(nvischan), c, scale(2), $ offset(2) double precision dphase(nrow), uvdist double precision xlast, ylast complex phasor integer flag(nvispol, nvischan, nrow) integer rflag(nrow) integer rownum integer support integer chanmap(nchan), polmap(npol) integer convplanemap(nrow) complex nvalue integer convsize, sampling complex convfunc(convsize, convsize, nconvplane), cwt, crot complex shiftx(-support:support), shifty(-support:support) complex sconv(-support:support, -support:support, nconvplane) real sconv2(-support:support, -support:support, nconvplane) real sumsconv2 real norm, phase logical omosft, doshift real pos(2) integer loc(2), off(2), iloc(2) integer rbeg, rend integer ix, iy, iz, ipol, ichan integer apol, achan, aconvplane, irow real wt, wtx, wty double precision pi data pi/3.14159265358979323846/ irow=rownum do ix=-support,support shiftx(ix)=1.0 shifty(ix)=1.0 end do sumsconv2=0.0 do iz=1, nconvplane do iy=-support,support iloc(2)=iy+convsize/2+1 do ix=-support,support iloc(1)=ix+convsize/2+1 sconv(ix,iy,iz)=conjg(convfunc(iloc(1), iloc(2),iz)) C sconv2(ix,iy)=abs(sconv(ix,iy)) C sumsconv2=sumsconv2+sconv2(ix,iy) end do end do end do doshift=.FALSE. if(irow.ge.0) then rbeg=irow+1 rend=irow+1 else rbeg=1 rend=nrow end if C xlast=0.0 ylast=0.0 do irow=rbeg, rend aconvplane=convplanemap(irow)+1 if(rflag(irow).eq.0) then do ichan=1, nvischan achan=chanmap(ichan)+1 if((achan.ge.1).and.(achan.le.nchan)) then call smosft(uvw(1,irow), dphase(irow), freq(ichan), c, $ scale, offset, sampling, pos, loc, off, phasor) if (omosft(nx, ny, loc, support)) then do ipol=1, nvispol apol=polmap(ipol)+1 if((flag(ipol,ichan,irow).ne.1).and. $ (apol.ge.1).and.(apol.le.npol)) then nvalue=0.0 norm=0.0 if(sampling.eq.1) then do iy=-support,support do ix=-support,support nvalue=nvalue+(sconv(ix,iy, $ aconvplane))* $ grid(loc(1)+ix,loc(2)+iy, $ apol,achan) end do end do else do iy=-support,support iloc(2)=convsize/2+1+sampling*iy+off(2) do ix=-support,support iloc(1)=convsize/2+1+ix*sampling $ +off(1) if(doshift) then cwt=conjg(convfunc(iloc(1), $ iloc(2), aconvplane))*shiftx(ix)* $ shifty(iy) else cwt=conjg(convfunc(iloc(1), $ iloc(2),aconvplane)) end if nvalue=nvalue+conjg(cwt)* $ grid(loc(1)+ix,loc(2)+iy, $ apol,achan) end do end do end if values(ipol,ichan,irow)=nvalue*conjg( $ phasor) end if end do end if end if end do end if end do return end C C Calculate gridded coordinates and the phasor needed for C phase rotation. C subroutine smosft (uvw, dphase, freq, c, scale, offset, $ sampling, pos, loc, off, phasor) implicit none integer loc(2), off(2), sampling double precision uvw(3), freq, c, scale(2), offset(2) real pos(2) double precision dphase, phase complex phasor integer idim double precision pi data pi/3.14159265358979323846/ if(sampling.gt.1) then do idim=1,2 pos(idim)=scale(idim)*uvw(idim)*freq/c+ $ (offset(idim)+1.0) loc(idim)=nint(pos(idim)) off(idim)=nint((loc(idim)-pos(idim))*sampling) end do else do idim=1,2 pos(idim)=scale(idim)*uvw(idim)*freq/c+ $ (offset(idim)+1.0) loc(idim)=nint(pos(idim)) off(idim)=0 end do end if phase=-2.0D0*pi*dphase*freq/c phasor=cmplx(cos(phase), sin(phase)) return end logical function omosft (nx, ny, loc, support) implicit none integer nx, ny, nw, loc(2), support omosft=(loc(1)-support.ge.1).and.(loc(1)+support.le.nx).and. $ (loc(2)-support.ge.1).and.(loc(2)+support.le.ny) return end casacore-3.7.1/scimath_f/fwproj.f000066400000000000000000000233361476623553700167460ustar00rootroot00000000000000*======================================================================= * Copyright (C) 1999,2001,2002 * Associated Universities, Inc. Washington DC, USA. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, * MA 02139, USA. * * Correspondence concerning AIPS++ should be addressed as follows: * Internet email: casa-feedback@nrao.edu. * Postal address: AIPS++ Project Office * National Radio Astronomy Observatory * 520 Edgemont Road * Charlottesville, VA 22903-2475 USA * * $Id$ *----------------------------------------------------------------------- C C Grid a number of visibility records C subroutine gwproj (uvw, dphase, values, nvispol, nvischan, $ dopsf, flag, rflag, weight, nrow, rownum, $ scale, offset, grid, nx, ny, npol, nchan, freq, c, $ support, convsize, sampling, wconvsize, convfunc, $ chanmap, polmap, $ sumwt) implicit none integer nx, ny, npol, nchan, nvispol, nvischan, nrow complex values(nvispol, nvischan, nrow) complex grid(nx, ny, npol, nchan) double precision uvw(3, nrow), freq(nvischan), c, scale(3), $ offset(3) double precision dphase(nrow), uvdist complex phasor integer flag(nvispol, nvischan, nrow) integer rflag(nrow) real weight(nvischan, nrow), phase double precision sumwt(npol, nchan) integer rownum integer support(*), rsupport integer chanmap(nchan), polmap(npol) integer dopsf complex nvalue integer convsize, sampling, wconvsize complex convfunc(convsize/2-1, convsize/2-1, wconvsize), $ cwt real norm real wt logical owproj real pos(3) integer loc(3), off(3), iloc(3) integer rbeg, rend integer ix, iy, ipol, ichan integer apol, achan, irow double precision pi data pi/3.14159265358979323846/ irow=rownum if(irow.ge.0) then rbeg=irow+1 rend=irow+1 else rbeg=1 rend=nrow end if do irow=rbeg, rend if(rflag(irow).eq.0) then do ichan=1, nvischan achan=chanmap(ichan)+1 if((achan.ge.1).and.(achan.le.nchan).and. $ (weight(ichan,irow).gt.0.0)) then call swproj(uvw(1,irow), dphase(irow), freq(ichan), c, $ scale, offset, sampling, pos, loc, off, phasor) iloc(3)=max(1, min(wconvsize, loc(3))) rsupport=support(iloc(3)) if (owproj(nx, ny, wconvsize, loc, rsupport)) then do ipol=1, nvispol apol=polmap(ipol)+1 if((flag(ipol,ichan,irow).ne.1).and. $ (apol.ge.1).and.(apol.le.npol)) then C If we are making a PSF then we don't want to phase C rotate but we do want to reproject uvw if(dopsf.eq.1) then nvalue=cmplx(weight(ichan,irow)) else nvalue=weight(ichan,irow)* $ (values(ipol,ichan,irow)*phasor) end if C norm will be the value we would get for the peak C at the phase center. We will want to normalize C the final image by this term. norm=0.0 do iy=-rsupport,rsupport iloc(2)=1+abs(iy*sampling+off(2)) do ix=-rsupport,rsupport iloc(1)=1+abs(ix*sampling+off(1)) if(uvw(3,irow).gt.0.0) then cwt=conjg(convfunc(iloc(1), $ iloc(2), iloc(3))) else cwt=convfunc(iloc(1), $ iloc(2), iloc(3)) end if grid(loc(1)+ix, $ loc(2)+iy,apol,achan)= $ grid(loc(1)+ix, $ loc(2)+iy,apol,achan)+ $ nvalue*cwt norm=norm+real(cwt) end do end do sumwt(apol,achan)=sumwt(apol,achan)+ $ weight(ichan,irow)*norm end if end do else C write(*,*) uvw(3,irow), pos(1), pos(2), pos(3), C $ loc(1), loc(2), loc(3) end if end if end do end if end do return end C C Degrid a number of visibility records C subroutine dwproj (uvw, dphase, values, nvispol, nvischan, $ flag, rflag, $ nrow, rownum, scale, offset, grid, nx, ny, npol, nchan, freq, $ c, support, convsize, sampling, wconvsize, convfunc, $ chanmap, polmap) implicit none integer nx, ny, npol, nchan, nvispol, nvischan, nrow complex values(nvispol, nvischan, nrow) complex grid(nx, ny, npol, nchan) double precision uvw(3, nrow), freq(nvischan), c, scale(3), $ offset(3) double precision dphase(nrow), uvdist complex phasor integer flag(nvispol, nvischan, nrow) integer rflag(nrow) integer rownum integer support(*), rsupport integer chanmap(*), polmap(*) complex nvalue integer convsize, wconvsize, sampling complex convfunc(convsize/2-1, convsize/2-1, wconvsize), $ cwt real norm, phase logical owproj real pos(3) integer loc(3), off(3), iloc(3) integer rbeg, rend integer ix, iy, ipol, ichan integer apol, achan, irow real wt, wtx, wty double precision pi data pi/3.14159265358979323846/ irow=rownum if(irow.ge.0) then rbeg=irow+1 rend=irow+1 else rbeg=1 rend=nrow end if C do irow=rbeg, rend if(rflag(irow).eq.0) then do ichan=1, nvischan achan=chanmap(ichan)+1 if((achan.ge.1).and.(achan.le.nchan)) then call swproj(uvw(1,irow), dphase(irow), freq(ichan), c, $ scale, offset, sampling, pos, loc, off, phasor) iloc(3)=max(1, min(wconvsize, loc(3))) rsupport=support(iloc(3)) if (owproj(nx, ny, wconvsize, loc, rsupport)) then do ipol=1, nvispol apol=polmap(ipol)+1 if((flag(ipol,ichan,irow).ne.1).and. $ (apol.ge.1).and.(apol.le.npol)) then nvalue=0.0 do iy=-rsupport,rsupport iloc(2)=1+abs(iy*sampling+off(2)) do ix=-rsupport,rsupport iloc(1)=1+abs(ix*sampling+off(1)) if(uvw(3,irow).gt.0.0) then cwt=conjg(convfunc(iloc(1), $ iloc(2), iloc(3))) else cwt=convfunc(iloc(1), $ iloc(2), iloc(3)) end if nvalue=nvalue+conjg(cwt)* $ grid(loc(1)+ix,loc(2)+iy,apol,achan) end do end do values(ipol,ichan,irow)=nvalue*conjg(phasor) end if end do end if end if end do end if end do return end C C Calculate gridded coordinates and the phasor needed for C phase rotation. C subroutine swproj (uvw, dphase, freq, c, scale, offset, $ sampling, pos, loc, off, phasor) implicit none integer loc(3), off(3), sampling double precision uvw(3), freq, c, scale(3), offset(3) real pos(3) double precision dphase, phase complex phasor integer idim double precision pi data pi/3.14159265358979323846/ C pos(3)=(scale(3)*uvw(3)*freq/c)*(scale(3)*uvw(3)*freq/c) C $ +offset(3)+1.0; C pos(3)=(scale(3)*uvw(3)*freq/c)+offset(3)+1.0; pos(3)=sqrt(abs(scale(3)*uvw(3)*freq/c))+offset(3)+1.0 loc(3)=nint(pos(3)) off(3)=0 do idim=1,2 pos(idim)=scale(idim)*uvw(idim)*freq/c+ $ (offset(idim)+1.0) loc(idim)=nint(pos(idim)) off(idim)=nint((loc(idim)-pos(idim))*sampling) end do phase=-2.0D0*pi*dphase*freq/c phasor=cmplx(cos(phase), sin(phase)) return end logical function owproj (nx, ny, nw, loc, support) implicit none integer nx, ny, nw, loc(3), support owproj=(support.gt.0).and. $ (loc(1)-support.ge.1).and.(loc(1)+support.le.nx).and. $ (loc(2)-support.ge.1).and.(loc(2)+support.le.ny).and. $ (loc(3).ge.1).and.(loc(3).le.nw) return end casacore-3.7.1/scimath_f/getbig.f000066400000000000000000000300121476623553700166650ustar00rootroot00000000000000*----------------------------------------------------------------------- * GETBIG: find the pixels with the biggest absolute value *----------------------------------------------------------------------- * * Copyright (C) 1997,2000 * Associated Universities, Inc. Washington DC, USA. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, * MA 02139, USA. * * Correspondence concerning AIPS++ should be addressed as follows: * Internet email: casa-feedback@nrao.edu. * Postal address: AIPS++ Project Office * National Radio Astronomy Observatory * 520 Edgemont Road * Charlottesville, VA 22903-2475 USA * * $Id$ * *----------------------------------------------------------------------- * * GETBIG contains a set of functions which extract a list of * components (defined as a pixel with specified amplitude and * position) from a specified array. Only components with an absolute * value greater than a specified fluxlimit are extracted. * * This specialised function is used by the Clark clean algorithm * (in the ClarkCleanModel class), and contains variants for * data with 1, 2 or 4 polarisations. * * It is assumed that the array from with the components to be * extracted has two spatial dimensions and that the third axis is * the polarization axis. * * The position of all the components in the list is zero relative, * for the convienience of the C/C++ functions which call this one. * * If there are too many components to extract this function will * only extract enough components to fill the specified list. But it * will continue scanning the data array and return how many * components there are in total that meet the criteria. This would * allow the calling routine to resize the list to the required size * and call the this function again. * * If there are fewer components in the list then the number of * components extracted will be returned. * * The following subroutines are found here: * GETBIGF : for Arrays of REAL numbers with * one polarisation (I) * GETBIG2F: for Arrays of REAL numbers with * two polarisations (I & V) * GETBIG4F: for Arrays of REAL numbers with * four polarizations (I, Q, U, V) * It should be trivial to clone these routines for any other data * types, in particular double precision. * * The following comments apply to all subroutines found here: * * The general form of the subroutine call is (x is replaced by * a character indicating the type: F for Float, D for Double, etc.), * and p is a number indicating the number of polarizations that are * required. (if p=1 it is dropped entirely) * * SUBROUTINE GETBIGpx(PIXVAL, PIXPOS, MAXPIX, FLUXLIM, ARR, NX, NY) * Given: * FLUXLIM x Extract only pixels with an * absolute value greater then this fluxlimit * MAXPIX I The maximum number of pixels to extract * NX, NY I Size of the array to extract the componets from * ARR(NX,NY,p) * x The array wich the components are extracted from * * return: * PIXVAL(p,NPIX) * x The amplitudes of the pixels extracted * PIXPOS(2,NPIX) * I The positions of the pixels extracted * MAXPIX I The number of pixels which meet the * fluxlimit criteria * * Notes: * * 1) In the 2 and 4 polarization case a transpose is effectively * done as the input arrat as polarization as the slowest moving * axis but the returned pixel values have polarization as the * fastest moving axis. This is because the the length of the * returned list is not known beforehand, wheras the number of * polarizations is, and it ensures that the returned pixel * value list is contigious in memory, and that different * polarizations are near each other, which should improve the * number of hits in the processor data cache. * * 2) The minimum and maximum absolute values used if p.NE.1 is a * function of all the polarizations at that pixel. For the 4 * polarization case this is the maximum eigenvalue * (=ABS(I+SQRT(Q*Q+U*U+V*V))), and for the * two polarisation case it is ABS(I + ABS(V)) * * 3) The returned value of MAXPIX is positive if not all the * components could be extracted and negative if there where * not enough to fill the list *----------------------------------------------------------------------- SUBROUTINE GETBIGF(PIXVAL, PIXPOS, MAXPIX, FLUXLIM, ARR, NX, NY) * Returns the value and position of all pixels which have an * absolute value greater than a specified fluxlimit. INTEGER NX, NY, MAXPIX, PIXPOS(2, MAXPIX) REAL FLUXLIM, PIXVAL(MAXPIX), ARR(0:NX-1, 0:NY-1) INTEGER NPIX, IX, IY REAL I *----------------------------------------------------------------------- NPIX = 0 DO 10 IX = 0, NX-1 DO 20, IY = 0, NY-1 I = ARR(IX, IY) IF (ABS(I).GE.FLUXLIM) THEN NPIX = NPIX + 1 IF (NPIX.LE.MAXPIX) THEN PIXVAL(NPIX) = I PIXPOS(1, NPIX) = IX PIXPOS(2, NPIX) = IY END IF END IF 20 CONTINUE 10 CONTINUE MAXPIX = NPIX - MAXPIX RETURN END *----------------------------------------------------------------------- SUBROUTINE GETBIG2F(PIXVAL, PIXPOS, MAXPIX, FLUXLIM, ARR, NX, NY) * Returns the value and position of all pixels which have an * absolute value greater than a specified fluxlimit in an array * with two polarizations (I & V) INTEGER NX, NY, MAXPIX, PIXPOS(2, MAXPIX) REAL FLUXLIM, PIXVAL(2, MAXPIX), ARR(0:NX-1, 0:NY-1, 2) INTEGER NPIX, IX, IY REAL I, V *----------------------------------------------------------------------- NPIX = 0 DO 10 IX = 0, NX-1 DO 20, IY = 0, NY-1 I = ARR(IX, IY, 1) V = ARR(IX, IY, 2) IF ( (MAX(ABS(I+V), ABS(I-V))) .GE.FLUXLIM) THEN NPIX = NPIX + 1 IF (NPIX.LE.MAXPIX) THEN PIXVAL(1, NPIX) = I PIXVAL(2, NPIX) = V PIXPOS(1, NPIX) = IX PIXPOS(2, NPIX) = IY END IF END IF 20 CONTINUE 10 CONTINUE MAXPIX = NPIX - MAXPIX RETURN END *----------------------------------------------------------------------- SUBROUTINE GETBIG4F(PIXVAL, PIXPOS, MAXPIX, FLUXLIM, ARR, NX, NY) * Returns the value and position of all pixels which have an * absolute value greater than a specified fluxlimit in an array * with four polarizations (I, Q, U & V) INTEGER NX, NY, MAXPIX, PIXPOS(2, MAXPIX) REAL FLUXLIM, PIXVAL(4, MAXPIX), ARR(0:NX-1, 0:NY-1, 4) INTEGER NPIX, IX, IY REAL I, Q, U, V *----------------------------------------------------------------------- NPIX = 0 DO 10 IX = 0, NX-1 DO 20, IY = 0, NY-1 I = ARR(IX, IY, 1) Q = ARR(IX, IY, 2) U = ARR(IX, IY, 3) V = ARR(IX, IY, 4) IF (ABS(I+SQRT(Q*Q+U*U+V*V)).GE.FLUXLIM) THEN NPIX = NPIX + 1 IF (NPIX.LE.MAXPIX) THEN PIXVAL(1, NPIX) = I PIXVAL(2, NPIX) = Q PIXVAL(3, NPIX) = U PIXVAL(4, NPIX) = V PIXPOS(1, NPIX) = IX PIXPOS(2, NPIX) = IY END IF END IF 20 CONTINUE 10 CONTINUE MAXPIX = NPIX - MAXPIX RETURN END *----------------------------------------------------------------------- SUBROUTINE GETBIMF(PIXVAL, PIXPOS, MAXPIX, FLUXLIM, * ARR, MASK, NX, NY) * Returns the value and position of all pixels which have an * absolute value greater than a specified fluxlimit after weighting * the pixels by a mask. INTEGER NX, NY, MAXPIX, PIXPOS(2, MAXPIX) REAL FLUXLIM, PIXVAL(MAXPIX) REAL ARR(0:NX-1, 0:NY-1), MASK(0:NX-1, 0:NY-1) INTEGER NPIX, IX, IY REAL I *----------------------------------------------------------------------- NPIX = 0 DO 10 IX = 0, NX-1 DO 20, IY = 0, NY-1 I = ARR(IX, IY) * MASK(IX, IY) IF (ABS(I).GE.FLUXLIM) THEN NPIX = NPIX + 1 IF (NPIX.LE.MAXPIX) THEN PIXVAL(NPIX) = I PIXPOS(1, NPIX) = IX PIXPOS(2, NPIX) = IY END IF END IF 20 CONTINUE 10 CONTINUE MAXPIX = NPIX - MAXPIX RETURN END *----------------------------------------------------------------------- SUBROUTINE GETBIM2F(PIXVAL, PIXPOS, MAXPIX, FLUXLIM, * ARR, MASK, NX, NY) * Returns the value and position of all pixels which have an * absolute value greater than a specified fluxlimit in an array with * two polarizations (I & V) after weighting the pixels by a mask. INTEGER NX, NY, MAXPIX, PIXPOS(2, MAXPIX) REAL FLUXLIM, PIXVAL(2, MAXPIX) REAL ARR(0:NX-1, 0:NY-1, 2), MASK(0:NX-1, 0:NY-1) INTEGER NPIX, IX, IY REAL I, V, M *----------------------------------------------------------------------- NPIX = 0 DO 10 IX = 0, NX-1 DO 20, IY = 0, NY-1 M = MASK(IX, IY) I = ARR(IX, IY, 1) * M V = ARR(IX, IY, 2) * M IF (ABS(I + ABS(V)).GE.FLUXLIM) THEN NPIX = NPIX + 1 IF (NPIX.LE.MAXPIX) THEN PIXVAL(1, NPIX) = I PIXVAL(2, NPIX) = V PIXPOS(1, NPIX) = IX PIXPOS(2, NPIX) = IY END IF END IF 20 CONTINUE 10 CONTINUE MAXPIX = NPIX - MAXPIX RETURN END *----------------------------------------------------------------------- SUBROUTINE GETBIM4F(PIXVAL, PIXPOS, MAXPIX, FLUXLIM, * ARR, MASK, NX, NY) * Returns the value and position of all pixels which have an * absolute value greater than a specified fluxlimit in an array with * four polarizations (I, Q, U & V) after weighting the pixels by a * mask. INTEGER NX, NY, MAXPIX, PIXPOS(2, MAXPIX) REAL FLUXLIM, PIXVAL(4, MAXPIX) REAL ARR(0:NX-1, 0:NY-1, 4), MASK(0:NX-1, 0:NY-1) INTEGER NPIX, IX, IY REAL I, Q, U, V, M *----------------------------------------------------------------------- NPIX = 0 DO 10 IX = 0, NX-1 DO 20, IY = 0, NY-1 M = MASK(IX, IY) I = ARR(IX, IY, 1) * M Q = ARR(IX, IY, 2) * M U = ARR(IX, IY, 3) * M V = ARR(IX, IY, 4) * M IF (ABS(I+SQRT(Q*Q+U*U+V*V)).GE.FLUXLIM) THEN NPIX = NPIX + 1 IF (NPIX.LE.MAXPIX) THEN PIXVAL(1, NPIX) = I PIXVAL(2, NPIX) = Q PIXVAL(3, NPIX) = U PIXVAL(4, NPIX) = V PIXPOS(1, NPIX) = IX PIXPOS(2, NPIX) = IY END IF END IF 20 CONTINUE 10 CONTINUE MAXPIX = NPIX - MAXPIX RETURN END casacore-3.7.1/scimath_f/grd2d.f000066400000000000000000000146671476623553700164500ustar00rootroot00000000000000*======================================================================= * Copyright (C) 1999 * Associated Universities, Inc. Washington DC, USA. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, * MA 02139, USA. * * Correspondence concerning AIPS++ should be addressed as follows: * Internet email: casa-feedback@nrao.edu. * Postal address: AIPS++ Project Office * National Radio Astronomy Observatory * 520 Edgemont Road * Charlottesville, VA 22903-2475 USA * * $Id$ *----------------------------------------------------------------------- C C National Radio Astronomy Observatory, Socorro, NM 87801 C Software Development Environment (SDE) C++ C $Id$ C SUBROUTINE GRD2D (VIS, WT, UVW, NVIS, SCALE, OFFSET, 1 ORIGIN, GVIS, PSF, NAXIS1, NAXIS2, GRIDFNU, GRIDFNV, 1 SUPP, OSAMP, SHIFT, SUMWT) C CD Grid two dimensional complex data. C The scaling factors should be set so that USCALE*U + UOFFSET converts C to grid cells centered at 0. This is then shifted to UORIGIN. Thus, C for example, UOFFSET, VOFFSET should nearly always be zero, while C UORIGIN = 1, VORIGIN = NV/2. C The phase center of the data can be adjusted as it is gridded using C the shift matrix. C C VIS CMPLX(*) input Non-gridded data C WT REAL(*) input Weights C UVW REAL(3,*) input Coordinates of data C NVIS INT input Number to be gridded C SCALE REAL(2) input Scaling factor to get to pixels C OFFSET REAL(2) input Offset to get to pixels C ORIGIN INT(2) input Origin of u axis C GVIS CMPLX(*) output Gridded data C PSF LOG input TRUE for PSF C NAXIS INT(2) input Size of gridded plane C GRIDFNU REAL(*) input Gridding function C GRIDFNV REAL(*) input Gridding function C SUPP INT(2) input Support of gridding function C SAMPU INT(2) input Over-sampling factor C SHIFT REAL(3,3) input Shift matrix C SUMWT REAL output Sum of weights C C Audit trail: C First version. This has been tailored to match the requirements C of AIPS++ FFTTool. C Also, expects the convolution functions to be un-normalized C (conv. functions generated in SDE are normalized to unit area). C S.Bhatnagar 27 Aug. 1996 C C------------------------------------------------------------------------ C C INTEGER NVIS, NAXIS1, NAXIS2, SUPP(2), OSAMP(2) INTEGER ORIGIN(2) COMPLEX VIS(*), GVIS(NAXIS1, NAXIS2) LOGICAL PSF REAL WT(*) REAL UVW(3,*), SCALE(2), OFFSET(2) REAL GRIDFNU(*),GRIDFNV(*) DOUBLE PRECISION SHIFT(3,*) REAL SUMWT C CHARACTER*(*) ROUTINE DOUBLE PRECISION TWOPI PARAMETER (ROUTINE = 'GRD2D', TWOPI=3.14159265358979323844*2) C LOGICAL DOSHIFT INTEGER IVIS, CFOFFSET INTEGER OFFU, UGRID, DELU, DELUI, UCEN INTEGER OFFV, VGRID, DELV, DELVI, VCEN COMPLEX FVIS DOUBLE PRECISION UG, VG, WG DOUBLE PRECISION PHASE, ULOCAL, VLOCAL, WLOCAL DOUBLE PRECISION UCELL, VCELL, UVWT, LSUMWT C========================================================================== C CFOFFSET = (SUPP(1)+1)*OSAMP(1) + 1 C C Is there a shift? C DOSHIFT = (SHIFT(1,1).NE.1.0D0).OR.(SHIFT(2,2).NE.1.0D0).OR. 1 (SHIFT(3,3).NE.1.0D0) C C Start of loop elements to be gridded C LSUMWT = 0.0 DO 10 IVIS = 1, NVIS IF (WT(IVIS).LE.0.0) GO TO 10 C C Shift to new phase center if required. C UG = - DBLE(UVW(1,IVIS)) VG = - DBLE(UVW(2,IVIS)) WG = - DBLE(UVW(3,IVIS)) IF (DOSHIFT) THEN ULOCAL = SHIFT(1,1) * UG + SHIFT(2,1) * VG + 1 SHIFT(3,1) * WG VLOCAL = SHIFT(1,2) * UG + SHIFT(2,2) * VG + 1 SHIFT(3,2) * WG WLOCAL = SHIFT(1,3) * UG + SHIFT(2,3) * VG + 1 SHIFT(3,3) * WG PHASE = TWOPI * (WLOCAL - WG) FVIS = VIS(IVIS) * CMPLX(COS(PHASE), -SIN(PHASE)) UCELL = SCALE(1) * ULOCAL + OFFSET(1) VCELL = SCALE(2) * VLOCAL + OFFSET(2) ELSE FVIS = VIS(IVIS) UCELL = SCALE(1) * UG + OFFSET(1) VCELL = SCALE(2) * VG + OFFSET(2) END IF C C Accumulate sum of weights C SUMWT = SUMWT + WT(IVIS) C C Find offsets within convolution function and center point of C gridded point. At his point we offset the grid by SUPP(1) so C that we don't have to worry about edge effects on the v-axis. C DELUI = NINT(OSAMP(1)*(FLOAT(NINT(UCELL))-UCELL)) + CFOFFSET DELVI = NINT(OSAMP(2)*(FLOAT(NINT(VCELL))-VCELL)) + CFOFFSET UCEN = NINT(UCELL) C + SUPP(1) VCEN = NINT(VCELL) if (UCEN+SUPP(1) .GT. NAXIS1) goto 10 if (UCEN-SUPP(1) .LT. 1) goto 10 if (VCEN+SUPP(2) .GT. NAXIS2) goto 10 if (VCEN-SUPP(2) .LT. 1) goto 10 C C There is no problem with running into an axis so just plunge right C in. C ******************************************************************** C This loop produces different answers on the sparc and on the C IBM6000. UCEN, VCEN, DELUI, DELVI are the same as are GRIDFNU, GRIDFNV C The difference is that one pixel (?) is misplaced by two pixels in C v. I don't know which is correct. This same error also afflicts C GRDH23D. TJC March 3 1991 C DO 140 OFFV = - SUPP(2), SUPP(2) DELV = DELVI + OSAMP(2)*OFFV VGRID = VCEN + OFFV DO 150 OFFU = - SUPP(1), SUPP(1) DELU = DELUI + OSAMP(1)*OFFU UGRID = UCEN + OFFU UVWT = WT(IVIS) * GRIDFNU(DELU) * GRIDFNV(DELV) GVIS(UGRID,VGRID) = GVIS(UGRID,VGRID) + UVWT * FVIS LSUMWT = LSUMWT + UVWT 150 CONTINUE 140 CONTINUE C C ******************************************************************** C 10 CONTINUE C 999 CONTINUE END casacore-3.7.1/scimath_f/grd2dwts.f000066400000000000000000000150211476623553700171670ustar00rootroot00000000000000*======================================================================= * Copyright (C) 1999 * Associated Universities, Inc. Washington DC, USA. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, * MA 02139, USA. * * Correspondence concerning AIPS++ should be addressed as follows: * Internet email: casa-feedback@nrao.edu. * Postal address: AIPS++ Project Office * National Radio Astronomy Observatory * 520 Edgemont Road * Charlottesville, VA 22903-2475 USA * * $Id$ *----------------------------------------------------------------------- C C National Radio Astronomy Observatory, Socorro, NM 87801 C Software Development Environment (SDE) C++ C $Id$ C SUBROUTINE GRD2DWTS (VIS, WT, UVW, NVIS, SCALE, OFFSET, 1 ORIGIN, GVIS, PSF, NAXIS1, NAXIS2, GRIDFNU, GRIDFNV, 1 SUPP, OSAMP, SHIFT, GWTS) C CD Grid two dimensional complex data. Also fills in the grid of CD weights. C The scaling factors should be set so that USCALE*U + UOFFSET converts C to grid cells centered at 0. This is then shifted to UORIGIN. Thus, C for example, UOFFSET, VOFFSET should nearly always be zero, while C UORIGIN = 1, VORIGIN = NV/2. C The phase center of the data can be adjusted as it is gridded using C the shift matrix. C C VIS CMPLX(*) input Non-gridded data C WT REAL(*) input Weights C UVW REAL(3,*) input Coordinates of data C NVIS INT input Number to be gridded C SCALE REAL(2) input Scaling factor to get to pixels C OFFSET REAL(2) input Offset to get to pixels C ORIGIN INT(2) input Origin of u axis C GVIS CMPLX(*) output Gridded data C PSF LOG input TRUE for PSF C NAXIS INT(2) input Size of gridded plane C GRIDFNU REAL(*) input Gridding function C GRIDFNV REAL(*) input Gridding function C SUPP INT(2) input Support of gridding function C SAMPU INT(2) input Over-sampling factor C SHIFT REAL(3,3) input Shift matrix C GWTS REAL(*) output Sum of weights C C Audit trail: C First version. This has been tailored to match the requirements C of AIPS++ FFTTool. C Also, expects the convolution functions to be un-normalized C (conv. functions generated in SDE are normalized to unit area). C S.Bhatnagar 27 Aug. 1996 C C------------------------------------------------------------------------ C C INTEGER NVIS, NAXIS1, NAXIS2, SUPP(2), OSAMP(2) INTEGER ORIGIN(2) COMPLEX VIS(*), GVIS(NAXIS1, NAXIS2) LOGICAL PSF REAL WT(*) REAL UVW(3,*), SCALE(2), OFFSET(2) REAL GRIDFNU(*),GRIDFNV(*) DOUBLE PRECISION SHIFT(3,*) REAL GWTS(NAXIS1, NAXIS2) C CHARACTER*(*) ROUTINE DOUBLE PRECISION TWOPI PARAMETER (ROUTINE = 'GRD2DWTS', TWOPI=2*3.14159265358979323844) C LOGICAL DOSHIFT INTEGER IVIS, CFOFFSET, NOFFGRD INTEGER OFFU, UGRID, DELU, DELUI, UCEN INTEGER OFFV, VGRID, DELV, DELVI, VCEN COMPLEX FVIS DOUBLE PRECISION UG, VG, WG DOUBLE PRECISION PHASE, ULOCAL, VLOCAL, WLOCAL DOUBLE PRECISION UCELL, VCELL, UVWT, LSUMWT C========================================================================== C NOFFGRD = 0 CFOFFSET = (SUPP(1)+1)*OSAMP(1) + 1 C C Is there a shift? C DOSHIFT = (SHIFT(1,1).NE.1.0D0).OR.(SHIFT(2,2).NE.1.0D0).OR. 1 (SHIFT(3,3).NE.1.0D0) C C Start of loop elements to be gridded C LSUMWT = 0.0 DO 20 IVIS = 1, NVIS IF (WT(IVIS).LE.0.0) GO TO 20 C C Shift to new phase center if required. C IF (DOSHIFT) THEN UG = - DBLE(UVW(1,IVIS)) VG = - DBLE(UVW(2,IVIS)) WG = - DBLE(UVW(3,IVIS)) ULOCAL = SHIFT(1,1) * UG + SHIFT(2,1) * VG + 1 SHIFT(3,1) * WG VLOCAL = SHIFT(1,2) * UG + SHIFT(2,2) * VG + 1 SHIFT(3,2) * WG WLOCAL = SHIFT(1,3) * UG + SHIFT(2,3) * VG + 1 SHIFT(3,3) * WG PHASE = TWOPI * (WLOCAL - WG) FVIS = VIS(IVIS) * CMPLX(COS(PHASE), -SIN(PHASE)) UCELL = SCALE(1) * ULOCAL + OFFSET(1) VCELL = SCALE(2) * VLOCAL + OFFSET(2) ELSE FVIS = VIS(IVIS) UCELL = - SCALE(1) * DBLE(UVW(1,IVIS)) + OFFSET(1) VCELL = - SCALE(2) * DBLE(UVW(2,IVIS)) + OFFSET(2) END IF C UCEN = NINT(UCELL) VCEN = NINT(VCELL) if (UCEN+SUPP(1) .GT. NAXIS1) goto 10 if (UCEN-SUPP(1) .LT. 1) goto 10 if (VCEN+SUPP(2) .GT. NAXIS2) goto 10 if (VCEN-SUPP(2) .LT. 1) goto 10 DELUI = NINT(OSAMP(1)*(FLOAT(NINT(UCELL))-UCELL)) + CFOFFSET DELVI = NINT(OSAMP(2)*(FLOAT(NINT(VCELL))-VCELL)) + CFOFFSET C C There is no problem with running into an axis so just plunge right C in. C ******************************************************************** C This loop produces different answers on the sparc and on the C IBM6000. UCEN, VCEN, DELUI, DELVI are the same as are GRIDFNU, GRIDFNV C The difference is that one pixel (?) is misplaced by two pixels in C v. I don't know which is correct. This same error also afflicts C GRDH23D. TJC March 3 1991 C DO 140 OFFV = - SUPP(2), SUPP(2) DELV = DELVI + OSAMP(2)*OFFV VGRID = VCEN + OFFV DO 150 OFFU = - SUPP(1), SUPP(1) DELU = DELUI + OSAMP(1)*OFFU UGRID = UCEN + OFFU UVWT = WT(IVIS) * GRIDFNU(DELU) * GRIDFNV(DELV) GVIS(UGRID,VGRID) = GVIS(UGRID,VGRID)+UVWT*FVIS GWTS(UGRID,VGRID) = GWTS(UGRID,VGRID)+UVWT LSUMWT = LSUMWT + UVWT 150 CONTINUE 140 CONTINUE GOTO 20 C C ******************************************************************** C 10 CONTINUE NOFFGRD = NOFFGRD + 1 20 CONTINUE C IF(NOFFGRD.GT.0) THEN WRITE(*,*) 'GRD2DWTS: ', NOFFGRD, ' points fell off the grid' ENDIF C 999 CONTINUE END casacore-3.7.1/scimath_f/grdde2d.f000066400000000000000000000140201476623553700167400ustar00rootroot00000000000000*======================================================================= * Copyright (C) 1999 * Associated Universities, Inc. Washington DC, USA. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, * MA 02139, USA. * * Correspondence concerning AIPS++ should be addressed as follows: * Internet email: casa-feedback@nrao.edu. * Postal address: AIPS++ Project Office * National Radio Astronomy Observatory * 520 Edgemont Road * Charlottesville, VA 22903-2475 USA * * $Id$ *----------------------------------------------------------------------- C C National Radio Astronomy Observatory, Socorro, NM 87801 C Software Development Environment (SDE) C++ C $Id$ C SUBROUTINE GRDDE2D (VIS, WT, UVW, NVIS, SCALE, OFFSET, 1 ORIGIN, GVIS, NAXIS1, NAXIS2, INTFNU, INTFNV, SUPP, OSAMP, $ SHIFT) C CD De-Grid two dimensional complex data. There must be no points within the C support size of the edge of the grid. C The scaling factors should be set so that SCALE(1)*U + OFFSET(1) converts C to grid cells centered at 0. This is then shifted to UORIGIN. Thus, C for example, OFFSET(1), OFFSET(2) should nearly always be zero, while C UORIGIN = 1, ORIGIN(2) = NV/2. C C VIS CMPLX(*) Output Un-gridded data C WT REAL(*) Output Weights C UVW REAL(*) input Coordinates of data C NVIS INT input Number to be gridded C SCALE REAL(2) input Scaling factor to get to pixels C OFFSET REAL(2) input Offset to get to pixels C ORIGIN INT(2) input Origin of u axis C GVIS CMPLX(*) input Gridded data C NAxis INT(2,2) input Size of gridded plane C INTFNU REAL(*) input Interpolation function C INTFNV REAL(*) input Interpolation function C SUPP INT(2,2) input Support of interpolation function C OSAMP INT(2,2) input Over-sampling factor C SHIFT REAL(3,3) input Shift matrix C C Audit trail: C C First version. This has been tailored to match the requirements C of AIPS++ FFTTool. C Also, expects the convolution functions to be un-normalized C (conv. functions generated in SDE are normalized to unit area). C S.Bhatnagar Sept. 4 1996 C C------------------------------------------------------------------------ C C INTEGER NVIS, NAXIS1, NAXIS2, SUPP(2), OSAMP(2) INTEGER ORIGIN(2) COMPLEX VIS(*), GVIS(NAXIS1,NAXIS2) REAL WT(*) REAL UVW(3,*), SCALE(2), OFFSET(2), INTFNU(*) REAL INTFNV(*) DOUBLE PRECISION SHIFT(3,3) C CHARACTER*(*) ROUTINE DOUBLE PRECISION TWOPI, STOR PARAMETER (ROUTINE = 'GRDDE2D', TWOPI=2*3.14159265358979323844, $ STOR=TWOPI/(360.0*3600.0)) C LOGICAL DOSHIFT INTEGER IVIS, NBAD, CFOFFSET INTEGER OFFU, UINT, UCEN, DELU, DELUI INTEGER OFFV, VINT, VCEN, DELV, DELVI COMPLEX ROT REAL UCELL, VCELL, UVWT, SUMWT DOUBLE PRECISION UG, VG, WG DOUBLE PRECISION PHASE, ULOCAL, VLOCAL, WLOCAL C C========================================================================== C CFOFFSET = (SUPP(1)+1)*OSAMP(1) + 1 C NBAD = 0 C C Is there a shift? C DOSHIFT = (SHIFT(1,1).NE.1.0D0).OR.(SHIFT(2,2).NE.1.0D0).OR. 1 (SHIFT(3,3).NE.1.0D0) C C Start of loop elements to be de-gridded C DO 10 IVIS = 1, NVIS VIS(IVIS)=CMPLX(0.0,0.0) IF (WT(IVIS).LE.0.0) GO TO 10 SUMWT = 0.0 WT(IVIS) = - WT(IVIS) UG = - DBLE(UVW(1,IVIS)) VG = - DBLE(UVW(2,IVIS)) WG = - DBLE(UVW(3,IVIS)) if (DOSHIFT) then ULOCAL = SHIFT(1,1) * UG + SHIFT(2,1) * VG + 1 SHIFT(3,1) * WG VLOCAL = SHIFT(1,2) * UG + SHIFT(2,2) * VG + 1 SHIFT(3,2) * WG WLOCAL = SHIFT(1,3) * UG + SHIFT(2,3) * VG + 1 SHIFT(3,3) * WG PHASE = TWOPI * (WLOCAL - WG) ROT = CMPLX(COS(PHASE), SIN(PHASE)) UCELL = SCALE(1) * ULOCAL + OFFSET(1) VCELL = SCALE(2) * VLOCAL + OFFSET(2) else UCELL = SCALE(1) * UG + OFFSET(1) VCELL = SCALE(2) * VG + OFFSET(2) endif C IF ((NINT(UCELL)+SUPP(1)).GT.NAXIS1) GO TO 10 IF ((NINT(UCELL)-SUPP(1)).LT.1) GO TO 10 IF ((NINT(VCELL)+SUPP(2)).GT.NAXIS2) GO TO 10 IF ((NINT(VCELL)-SUPP(2)).LT.1) GO TO 10 C DELUI = NINT((OSAMP(1)*(FLOAT(NINT(UCELL))-UCELL))) DELVI = NINT((OSAMP(2)*(FLOAT(NINT(VCELL))-VCELL))) UCEN = NINT(UCELL) - ORIGIN(1) VCEN = NINT(VCELL) - ORIGIN(2) C C No danger of axis problems: simple loop C DO 140 OFFV = - SUPP(2), SUPP(2) DELV = DELVI + OSAMP(2)*OFFV + CFOFFSET VINT = ORIGIN(2) + VCEN + OFFV DO 150 OFFU = - SUPP(1), SUPP(1) DELU = DELUI + OSAMP(1)*OFFU + CFOFFSET UINT = ORIGIN(1) + UCEN + OFFU UVWT = INTFNU(DELU) * INTFNV(DELV) VIS(IVIS) = VIS(IVIS) + GVIS(UINT, VINT)* UVWT SUMWT = SUMWT + UVWT 150 CONTINUE 140 CONTINUE C C Were there any good data for this point? C IF (SUMWT.GT.0.0) THEN VIS(IVIS) = VIS(IVIS)/SUMWT WT(IVIS) = ABS(WT(IVIS)) ELSE NBAD = NBAD + 1 VIS(IVIS) = 0.0 END IF C C Now finally phase rotate C if (DOSHIFT) VIS(IVIS) = VIS(IVIS) * ROT C 10 CONTINUE C 999 CONTINUE END casacore-3.7.1/scimath_f/grdgauss.f000066400000000000000000000035471476623553700172600ustar00rootroot00000000000000*======================================================================= * Copyright (C) 1999 * Associated Universities, Inc. Washington DC, USA. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, * MA 02139, USA. * * Correspondence concerning AIPS++ should be addressed as follows: * Internet email: casa-feedback@nrao.edu. * Postal address: AIPS++ Project Office * National Radio Astronomy Observatory * 520 Edgemont Road * Charlottesville, VA 22903-2475 USA * * $Id: grdsf.f 17791 2004-08-25 02:28:46Z cvsmgr $ *----------------------------------------------------------------------- SUBROUTINE GRDGAUSS (HWHM, VAL, OUT) C C Gaussian function with a radius of half-maximum, HWHM, which C is written as, C C OUT = exp( -ln(2.0)*(VAL/RHM)**2 ) C C------------------------------------------------------------------------ C DOUBLE PRECISION HWHM, VAL, OUT C DOUBLE PRECISION :: LN2 = 0.69314718055994529D0 C======================================================================= OUT = DEXP( - LN2 * (VAL/HWHM) * (VAL/HWHM) ) END casacore-3.7.1/scimath_f/grdjinc1.f000066400000000000000000000073741476623553700171440ustar00rootroot00000000000000*======================================================================= * Copyright (C) 1999 * Associated Universities, Inc. Washington DC, USA. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, * MA 02139, USA. * * Correspondence concerning AIPS++ should be addressed as follows: * Internet email: casa-feedback@nrao.edu. * Postal address: AIPS++ Project Office * National Radio Astronomy Observatory * 520 Edgemont Road * Charlottesville, VA 22903-2475 USA * * $Id: grdsf.f 17791 2004-08-25 02:28:46Z cvsmgr $ *----------------------------------------------------------------------- SUBROUTINE GRDJINC1 (C, VAL, NORM, OUT) C C The jinc function for the first order Bessel function, which is C written as, C C OUT = J_1( PI * VAL / C ) / ( PI * VAL / C ) C C where J_1 is the Bessel function of the first kind with order of C 1, which will be calculated by the subroutine BESSELJ1 defined C below. C C The name, jinc, may be an analogue of sinc function. C C If the parameter NORM is 1, then returned value will be normalized C such that OUT[0] = 1.0. Otherwise, the value will not be scaled C (In this case, OUT[0] = 0.5). C C------------------------------------------------------------------------ C DOUBLE PRECISION C, VAL INTEGER NORM DOUBLE PRECISION OUT C DOUBLE PRECISION X PARAMETER (PI = 3.1415926535897931D0) C======================================================================= IF (VAL .EQ. 0.0) THEN OUT = 0.5D0 ELSE X = PI * VAL / C CALL BESSELJ1(X, OUT) OUT = OUT / X ENDIF IF (NORM .EQ. 1) THEN OUT = OUT / 0.5D0 ENDIF END C C Bessel function of the first kind with order of 1, J_1(x) C Approximate formula is taken from Numerical Recipe C SUBROUTINE BESSELJ1( VAL, OUT ) DOUBLE PRECISION VAL, OUT C DOUBLE PRECISION AX, X, Y, Z, ANS1, ANS2 C======================================================================= OUT = 0.0 AX = DABS(VAL) IF (AX .LT. 8.0D0) THEN Y = VAL * VAL ANS1 = VAL * (72362614232.0D0 + Y * (-7895059235.0D0 $ + Y * (242396853.1D0 + Y * (-2972611.439D0 $ + Y * (15704.48260D0 + Y * (-30.16036606D0)))))) ANS2 = 144725228442.0D0 + Y * (2300535178.0D0 $ + Y * (18583304.74D0 + Y * (99447.43394D0 $ + Y * (376.9991397D0 + Y * 1.0D0)))) OUT = ANS1 / ANS2 ELSE Z = 8.0D0 / AX Y = Z * Z X = AX - 2.356194491D0 ANS1 = 1.0D0 + Y * (0.183105D-2 + Y * (-0.3516396496D-4 $ + Y * (0.2457520174D-5 + Y * (-0.240337019D-6)))) ANS2 = 0.04687499995D0 + Y * (-0.2002690873D-3 $ + Y * (0.8449199096D-5 + Y * (-0.88228987D-6 $ + Y * (0.105787412D-6)))) OUT = DSQRT(0.636619772D0 / AX) $ * (DCOS(X) * ANS1 - Z * DSIN(X) * ANS2) IF (VAL .LT. 0.0D0) THEN OUT = - OUT ENDIF ENDIF END casacore-3.7.1/scimath_f/grdsf.f000066400000000000000000000060361476623553700165420ustar00rootroot00000000000000*======================================================================= * Copyright (C) 1999 * Associated Universities, Inc. Washington DC, USA. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, * MA 02139, USA. * * Correspondence concerning AIPS++ should be addressed as follows: * Internet email: casa-feedback@nrao.edu. * Postal address: AIPS++ Project Office * National Radio Astronomy Observatory * 520 Edgemont Road * Charlottesville, VA 22903-2475 USA * * $Id$ *----------------------------------------------------------------------- SUBROUTINE GRDSF (NU, VAL) C CD Find Spheroidal function with M = 6, alpha = 1 using the rational C approximations discussed by Fred Schwab in 'Indirect Imaging'. C This routine was checked against Fred's SPHFN routine, and agreed C to about the 7th significant digit. C The gridding function is (1-NU**2)*GRDSF(NU) where NU is the distance C to the edge. The grid correction function is just 1/GRDSF(NU) where NU C is now the distance to the edge of the image. C C------------------------------------------------------------------------ C DOUBLE PRECISION NU, VAL C DOUBLE PRECISION TOP, BOT, DELNUSQ, NUEND INTEGER K, PART INTEGER NP, NQ PARAMETER (NP = 4) PARAMETER (NQ = 2) DOUBLE PRECISION P(0:NP,2), Q(0:NQ,2) DATA P /8.203343D-2, -3.644705D-1, 6.278660D-1, 1 -5.335581D-1, 2.312756D-1, 2 4.028559D-3, -3.697768D-2, 1.021332D-1, 3 -1.201436D-1, 6.412774D-2/ DATA Q /1.0000000D0, 8.212018D-1, 2.078043D-1, 1 1.0000000D0, 9.599102D-1, 2.918724D-1/ C======================================================================= VAL = 0.0 C IF ((NU.GE.0.0).AND.(NU.LT.0.75)) THEN PART = 1 NUEND = 0.75D0 ELSEIF ((NU.GE.0.75).AND.(NU.LE.1.00)) THEN PART = 2 NUEND = 1.00D0 ELSE VAL = 0.0 GO TO 999 END IF C TOP = P(0,PART) DELNUSQ = NU**2 - NUEND**2 DO 10 K = 1, NP TOP = TOP + P(K,PART) * DELNUSQ ** K 10 CONTINUE BOT = Q(0,PART) DO 20 K = 1, NQ BOT = BOT + Q(K,PART) * DELNUSQ ** K 20 CONTINUE IF (BOT.NE.0.0) THEN VAL = TOP/BOT ELSE VAL = 0.0 END IF C 999 CONTINUE END casacore-3.7.1/scimath_f/hclean.f000066400000000000000000000147231476623553700166710ustar00rootroot00000000000000*======================================================================= * Copyright (C) 1999,2000,2003 * Associated Universities, Inc. Washington DC, USA. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, * MA 02139, USA. * * Correspondence concerning AIPS++ should be addressed as follows: * Internet email: casa-feedback@nrao.edu. * Postal address: AIPS++ Project Office * National Radio Astronomy Observatory * 520 Edgemont Road * Charlottesville, VA 22903-2475 USA * * $Id$ *----------------------------------------------------------------------- cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc c c limagestep real(nx,ny,npol) input dirty image; output residual image c domask integer domask == 0 ==> no mask is present c (formerly a logical, but that was trouble) c lmask real(nx,ny) input mask image c nx,ny,npol integer shape of images cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc subroutine maximg(limagestep, domask, lmask, nx, ny, npol, fmin, $ fmax) implicit none integer nx, ny, npol, domask integer xbeg, xend, ybeg, yend real limagestep(nx, ny, npol) real lmask(nx, ny) real fmin, fmax real wpeak integer pol, ix, iy integer x1, x2, y1, y2 c Now find peak in residual image fmin=1e20 fmax=-1e20 do pol=1,npol do iy=1,ny do ix=1,nx if((domask.eq.0).or.(lmask(ix,iy).GT.0.5)) then wpeak = limageStep(ix,iy,pol) if(wpeak.GT.fmax) then fmax=wpeak end if if(wpeak.LT.fmin) then fmin=wpeak end if end if end do end do end do return end cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc c c limage real(nx,ny,npol) output model image (clean components image) c limagestep real(nx,ny,npol) input dirty image; output residual image c lpsf real(nx,ny) input point spread function c domask integer domask == 0 ==> no mask is present c (formerly a logical, but that was trouble) c lmask real(nx,ny) input mask image c nx,ny,npol integer shape of images c xbeg,xend integer shape of box to clean - this could be larger c ybeg,yend than the inner quarter, but the subtraction c will be incorrect then for points outside the c inner quarter c niter integer maximum allowed iterations c siter integer starting iteration number c iter integer last iteration number we get to in this algorithm c gain real clean loop gain c thres real flux cleaning threshold c cspeedup real if > 0, thres = thres_o * exp( iter/cspeedup ) c msgput pointer to function which outputs the status message c stopnow pointer to function which determines if it is stopping time c cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc subroutine hclean(limage, limagestep, lpsf, domask, lmask, nx, ny, $ npol, xbeg, xend, ybeg, yend, niter, siter, iter, gain, $ thres, cspeedup, msgput, stopnow) implicit none integer nx, ny, npol, xbeg, xend, ybeg, yend integer niter, siter, iter, yes real limage(nx, ny, npol) real limagestep(nx, ny, npol) integer domask real lpsf(nx, ny), lmask(nx, ny) real gain, thres, cspeedup external msgput external stopnow real cthres real maxval, pv, val integer px, py, pol, ix, iy, cycle, i, maxiter integer x1, x2, y1, y2 maxiter = siter yes=0 do pol=1,npol do iter=siter, niter maxval=0.0 px=1 py=1 do iy=ybeg,yend do ix=xbeg,xend if((domask.eq.0).or.(lmask(ix,iy).GT.0.5)) then val = abs(limagestep(ix,iy,pol)) if(val.GT.maxval) then px=ix py=iy maxval=val end if end if end do end do c maxval=limageStep(px,py,pol) if (cspeedup .gt. 0.0) then cthres = thres * 2.0**(real(iter-siter)/cspeedup ) else cthres = thres endif if((yes.EQ.1).OR.(abs(maxval).LT.cthres)) then goto 200 endif c // Output ten lines of information if run to the end cycle=max(1,(niter-siter)/10) if((iter.EQ.siter).OR.(mod(iter,cycle)).EQ.1) then call msgput(npol, pol, iter, px, py, maxval) call stopnow(yes) endif if(yes.EQ.1) then goto 200 endif x1 = max( 1, px - nx/2 ) y1 = max( 1, py - ny/2 ) x2 = min( nx, px + nx/2 -1 ) y2 = min( ny, py + ny/2 -1 ) pv=gain*maxval limage(px,py,pol)=limage(px,py,pol)+pv do iy=y1,y2 do ix=x1,x2 limageStep(ix,iy,pol)=limageStep(ix,iy,pol) $ -pv * lpsf(nx/2+ix-px+1,ny/2+iy-py+1) end do end do end do 200 continue if(iter.gt.siter) then call msgput(npol, pol, -iter, px, py, maxval) end if maxiter=max(iter, maxiter) end do 100 continue iter=maxiter if (iter .gt. niter) then iter = niter endif return end casacore-3.7.1/scimath_f/lawson.f000066400000000000000000001124251476623553700167400ustar00rootroot00000000000000*======================================================================= * $Id$ *----------------------------------------------------------------------- C C The original version of this code was developed by C Charles L. Lawson and Richard J. Hanson at Jet Propulsion Laboratory C 1973 JUN 15, and published in the book C "SOLVING LEAST SQUARES PROBLEMS", Prentice-Hall, 1974. C C----------------------------------------------------------------------- C SUBROUTINE HFTI (A,MDA,M,N,B,MDB,NB,TAU,KRANK,RNORM,H,G,IP) C C.L.LAWSON & R.J.HANSON, JET PROPULSION LABORATORY, 1973 JUN 12 C TO APPEAR IN 'SOLVING LEAST SQUARES PROBLEMS', PRENTICE-HALL 1974 C SOLVE LEAST SQUARES PROBLEM USING ALGORITHM HFTI. C DIMENSION A(MDA,N),B(MDB,NB),H(N),G(N),RNORM(NB) INTEGER IP(N) DOUBLE PRECISION SM,DZERO DATA SZERO/ 0./, DZERO / 0.D0 / DATA FACTOR / .001 / C K = 0 LDIAG = MIN0(M,N) IF (LDIAG.LE.0) GOTO 270 DO J=1,LDIAG IF (J.EQ.1) GOTO 20 C C UPDATE SQUARED COLUMN LENGTHS AND FIND LMAX C LMAX = J DO 10 L = J,N H(L) = H(L) - A(J-1,L)**2 IF (H(L).GT.H(LMAX)) LMAX = L 10 CONTINUE C IF (DIFF(HMAX+FACTOR*H(LMAX),HMAX)) 20,20,50 IF (DIFF(HMAX+FACTOR*H(LMAX),HMAX) .GT. 0) GOTO 50 C C COMPUTE SQUARED COLUMN LENGTHS AND FIND LMAX C 20 LMAX = J DO L = J,N H(L) = SZERO DO I = J,M H(L) = H(L) + A(I,L)**2 END DO IF (H(L).GT.H(LMAX)) LMAX = L END DO HMAX = H(LMAX) C C LMAX HAS BEEN DETERMINED C C DO COLUMN INTERCHANGES IF NEEDED. C 50 CONTINUE IP(J) = LMAX IF (IP(J).EQ.J) GOTO 70 DO I = 1,M TMP = A(I,J) A(I,J) = A(I,LMAX) A(I,LMAX) = TMP END DO H(LMAX) = H(J) C C COMPUTE THE J-TH TRANSFORMATION AND APPLY IT TO A AND B. C 70 CALL H12 (1,J,J+1,M,A(1,J),1,H(J),A(1,J+1),1,MDA,N-J) CALL H12 (2,J,J+1,M,A(1,J),1,H(J),B,1,MDB,NB) END DO C C DETERMINE THE PSEUDORANK, K, USING THE TOLERANCE, TAU. C DO J = 1,LDIAG IF (ABS(A(J,J)).LE.TAU) GOTO 100 END DO K = LDIAG GOTO 110 100 K = J - 1 110 KP1 = K + 1 C C COMPUTE THE NORMS OF THE RESIDUAL VECTORS. C IF (NB.LE.0) GOTO 140 DO JB = 1,NB TMP = SZERO IF (KP1.GT.M) GOTO 130 DO I = KP1,M TMP = TMP + B(I,JB)**2 END DO 130 RNORM(JB) = SQRT(TMP) END DO 140 CONTINUE C SPECIAL FOR PSEUDORANK = 0 IF (K.GT.0) GOTO 160 IF (NB.LE.0) GOTO 270 DO JB = 1,NB DO I = 1,N B(I,JB) = SZERO END DO END DO GOTO 270 C C IF THE PSEUDORANK IS LESS THAN N COMPUTE HOUSHOLDER C DECOMPOSITION OF FIRST K ROWS. C 160 IF (K.EQ.N) GOTO 180 DO II = 1,K I = KP1 - II CALL H12 (1,I,KP1,N,A(I,1),MDA,G(I),A,MDA,1,I-1) END DO 180 CONTINUE C C IF (NB.LE.0) GOTO 270 DO 260 JB = 1,NB C C SOLVE THE K BY K TRIANGULAR SYSTEM C DO L = 1,K SM = DZERO I = KP1 - L IF (I.EQ.K) GOTO 200 IP1 = I + 1 DO J = IP1,K SM = SM + A(I,J)*DBLE(B(J,JB)) END DO 200 SM1 = SM B(I,JB) = (B(I,JB)-SM1)/A(I,I) END DO C IF (K.EQ.N) GOTO 240 DO J = KP1,N B(J,JB) = SZERO END DO DO I = 1,K CALL H12 (2,I,KP1,N,A(I,1),MDA,G(I),B(1,JB),1,MDB,1) END DO C C RE-ORDER THE SOLUTION VECTOR TO COMPENSATE FOR THE C COLUMN INTERCHANGES. C C COMPLETE COMPUTATION OF SOLUTION VECTOR C 240 DO 250 JJ = 1,LDIAG J = LDIAG + 1 - JJ IF (IP(J).EQ.J) GOTO 250 L = IP(J) TMP = B(L,JB) B(L,JB) = B(J,JB) B(J,JB) = TMP 250 CONTINUE 260 CONTINUE C C THE SOLUTION VECTORS, X, ARE NOW C IN THE FIRST N ROWS OF THE ARRAY B(.). C 270 KRANK = K RETURN END C SUBROUTINE LDP (G,MDG,M,N,H,X,XNORM,W,INDEX,MODE) C C.L.LAWSON AND R.J.HANSON, JET PROPULSION LABORATORY, 1974 MAR 1 C TO APPEAR IN @SOLVING LEAST SQUARES PROBLEMS@, PRENTICE-HALL, 1974 C C ********** LEAST DISTANCE PROGRAMMING ********** C INTEGER INDEX(1) DIMENSION G(MDG,1), H(1), X(1), W(1) ZERO=0. ONE=1. IF (N.LE.0) GO TO 120 DO J=1,N X(J)=ZERO END DO XNORM=ZERO IF (M.LE.0) GO TO 110 C C THE DECLARED DIMENSION OF W() MUST BE AT LEAST (N+1)*(M+2)+2*M. C C FIRST (N+1)*M LOCS OF W() = MATRIX E FOR PROBLEM NNLS. C NEXT N+1 LOCS OF W() = VECTOR F FOR PROBLEM NNLS. C NEXT N+1 LOCS OF W() = VECTOR Z FOR PROBLEM NNLS. C NEXT M LOCS OF W() = VECTOR Y FOR PROBLEM NNLS. C NEXT M LOCS OF W() = VECTOR WDUAL FOR PROBLEM NNLS. C COPY G**T INTO FIRST N ROWS AND M COLUMNS OF E. C COPY H**T INTO ROW N+1 OF E. C IW=0 DO J=1,M DO I=1,N IW=IW+1 W(IW)=G(J,I) END DO IW=IW+1 W(IW)=H(J) END DO IF=IW+1 C STORE N ZEROS FOLLOWED BY A ONE INTO F. DO I=1,N IW=IW+1 W(IW)=ZERO END DO W(IW+1)=ONE C NP1=N+1 IZ=IW+2 IY=IZ+NP1 IWDUAL=IY+M C CALL NNLS (W,NP1,NP1,M,W(IF),W(IY),RNORM,W(IWDUAL),W(IZ), $ INDEX,3*NP1,MODE) c print *, rnorm, mode c write (20,*) (W(IY+I-1), I=1,N+1) C USE THE FOLLOWING RETURN IF UNSUCCESSFUL IN NNLS. IF (MODE.NE.1) RETURN C IF (RNORM) 130, 130, 50 IF (RNORM .LE. 0) GOTO 130 50 FAC=ONE IW=IY-1 DO I=1,M IW=IW+1 C HERE WE ARE USING THE SOLUTION VECTOR Y. FAC=FAC-H(I)*W(IW) END DO C C IF (DIFF(ONE+FAC,ONE)) 130,130,70 IF (DIFF(ONE+FAC,ONE) .LE. 0) GOTO 130 70 FAC=ONE/FAC DO J=1,N IW=IY-1 DO I=1,M IW=IW+1 C HERE WE ARE USING THE SOLUTION VECTOR Y. X(J)=X(J)+G(I,J)*W(IW) END DO X(J)=X(J)*FAC END DO DO J=1,N XNORM=XNORM+X(J)**2 END DO XNORM=SQRT(XNORM) C SUCCESSFUL RETURN. 110 MODE=1 c do i = 1, m c acc = 0.0 c do k = 1, n c acc = acc + g(i,k) * x(k) c end do c write (21, *) i, acc, h(i) c end do RETURN C ERROR RETURN. N .LE. 0. 120 MODE=2 RETURN C RETURNING WITH CONSTRAINTS NOT COMPATIBLE. 130 MODE=4 RETURN END C C C SUBROUTINE NNLS (A,MDA,M,N,B,X,RNORM,W,ZZ,INDEX,ITMAX,MODE) C C.L.LAWSON AND R.J.HANSON, JET PROPULSION LABORATORY, 1973 JUNE 15 C TO APPEAR IN @SOLVING LEAST SQUARES PROBLEMS@, PRENTICE-HALL, 1974 C C ********** NONNEGATIVE LEAST SQUARES ********** C C GIVEN AN M BY N MATRIX, A, AND AN M-VECTOR, B, COMPUTE AN C N-VECTOR, X, WHICH SOLVES THE LEAST SQUARES PROBLEM C C A * X = B SUBJECT TO X .GE. 0 C C A(),MDA,M,N MDA IS THE FIRST DIMENSIONING PARAMETER FOR THE C ARRAY, A(). ON ENTRY A() CONTAINS THE M BY N C MATRIX, A. ON EXIT A() CONTAINS C THE PRODUCT MATRIX, Q*A , WHERE Q IS AN C M BY M ORTHOGONAL MATRIX GENERATED IMPLICITLY BY C THIS SUBROUTINE. C B() ON ENTRY B() CONTAINS THE M-VECTOR, B. ON EXIT B() CON- C TAINS Q*B. C X() ON ENTRY X() NEED NOT BE INITIALIZED. ON EXIT X() WILL C CONTAIN THE SOLUTION VECTOR. C RNORM ON EXIT RNORM CONTAINS THE EUCLIDEAN NORM OF THE C RESIDUAL VECTOR. C W() AN N-ARRAY OF WORKING SPACE. ON EXIT W() WILL CONTAIN C THE DUAL SOLUTION VECTOR. W WILL SATISFY W(I) = 0. C FOR ALL I IN SET P AND W(I) .LE. 0. FOR ALL I IN SET Z C ZZ() AN M-ARRAY OF WORKING SPACE. C INDEX() AN INTEGER WORKING ARRAY OF LENGTH AT LEAST N. C ON EXIT THE CONTENTS OF THIS ARRAY DEFINE THE SETS C P AND Z AS FOLLOWS.. C C INDEX(1) THRU INDEX(NSETP) = SET P. C INDEX(IZ1) THRU INDEX(IZ2) = SET Z. C IZ1 = NSETP + 1 = NPP1 C IZ2 = N C ITMAX MAXIMUM NUMBER OF ITERATIONS (WAS HARDED CODED AT 3*N IN C ORIGINAL LAWSON ROUTINE) C MODE THIS IS A SUCCESS-FAILURE FLAG WITH THE FOLLOWING C MEANINGS. C 1 THE SOLUTION HAS BEEN COMPUTED SUCCESSFULLY. C 2 THE DIMENSIONS OF THE PROBLEM ARE BAD. C EITHER M .LE. 0 OR N .LE. 0. C 3 ITERATION COUNT EXCEEDED. MORE THAN ITMAX ITERATIONS. C SUBROUTINE NNLS (A,MDA,M,N,B,X,RNORM,W,ZZ,INDEX,ITMAX,MODE) DIMENSION A(MDA,N), B(1), X(1), W(1), ZZ(1) INTEGER INDEX(1) REAL A, B, X, RNORM, W, ZZ, ZERO, ONE, TWO, FACTOR, SM, $ WMAX, ASAVE, UP, DUMMY(1), UNORM, ZTEST, ALPHA, T, CC, SS DOUBLE PRECISION DSVCMP, DSVTIM INTEGER MDA, M, N, MODE, ITER, I, J, L, IZ1, IZ2, NSETP, NPP1, $ IZ, ITMAX, IZMAX, JZ, NEXT, IP, II, JJ REAL DIFF ZERO=0. ONE=1. TWO=2. FACTOR=0.01 DSVCMP=1.D0 DSVTIM=1.D0 C MODE=1 IF (M.GT.0.AND.N.GT.0) GO TO 10 MODE=2 RETURN 10 ITER=0 C C INITIALIZE THE ARRAYS INDEX() AND X(). C DO I=1,N X(I)=ZERO INDEX(I)=I END DO C IZ2=N IZ1=1 NSETP=0 NPP1=1 C ****** MAIN LOOP BEGINS HERE ****** 30 CONTINUE C QUIT IF ALL COEFFICIENTS ARE ALREADY IN THE SOLUTION. C OR IF M COLS OF A HAVE BEEN TRIANGULARIZED. C IF (IZ1.GT.IZ2.OR.NSETP.GE.M) GO TO 350 C C COMPUTE COMPONENTS OF THE DUAL (NEGATIVE GRADIENT) VECTOR W(). C DO IZ=IZ1,IZ2 J=INDEX(IZ) SM=ZERO DO L=NPP1,M SM=SM+A(L,J)*B(L) END DO W(J)=SM END DO C FIND LARGEST POSITIVE W(J). 60 WMAX=ZERO DO 70 IZ=IZ1,IZ2 J=INDEX(IZ) IF (W(J).LE.WMAX) GO TO 70 WMAX=W(J) IZMAX=IZ 70 CONTINUE C C IF WMAX .LE. 0. GO TO TERMINATION. C THIS INDICATES SATISFACTION OF THE KUHN-TUCKER CONDITIONS. C C IF (WMAX) 350,350,80 IF (WMAX .LE. 0) GOTO 350 80 IZ=IZMAX J=INDEX(IZ) C C THE SIGN OF W(J) IS OK FOR J TO BE MOVED TO SET P. C BEGIN THE TRANSFORMATION AND CHECK NEW DIAGONAL ELEMENT TO AVOID C NEAR LINEAR DEPENDENCE. C ASAVE=A(NPP1,J) CALL H12 (1,NPP1,NPP1+1,M,A(1,J),1,UP,DUMMY,1,1,0) UNORM=ZERO IF (NSETP.EQ.0) GO TO 100 DO L=1,NSETP UNORM=UNORM+A(L,J)**2 END DO 100 UNORM=SQRT(UNORM) C IF (DIFF(UNORM+ABS(A(NPP1,J))*FACTOR,UNORM)) 130,130,110 IF (DIFF(UNORM+ABS(A(NPP1,J))*FACTOR,UNORM) .LE. 0) GOTO 130 C C COL J IS SUFFICIENTLY INDEPENDENT. COPY B INTO ZZ, UPDATE ZZ AND C > SOLVE FOR ZTEST ( = PROPOSED NEW VALUE FOR X(J) ). C 110 DO L=1,M ZZ(L)=B(L) END DO CALL H12 (2,NPP1,NPP1+1,M,A(1,J),1,UP,ZZ,1,1,1) ZTEST=ZZ(NPP1)/A(NPP1,J) C C SEE IF ZTEST IS POSITIVE C REJECT J AS A CANDIDATE TO BE MOVED FROM SET Z TO SET P. C RESTORE A(NPP1,J), SET W(J)=0., AND LOOP BACK TO TEST DUAL C C IF (ZTEST) 130,130,140 IF (ZTEST .GT. 0) GOTO 140 C C COEFFS AGAIN. C 130 A(NPP1,J)=ASAVE W(J)=ZERO GO TO 60 C C THE INDEX J=INDEX(IZ) HAS BEEN SELECTED TO BE MOVED FROM C SET Z TO SET P. UPDATE B, UPDATE INDICES, APPLY HOUSEHOLDER C TRANSFORMATIONS TO COLS IN NEW SET Z, ZERO SUBDIAGONAL ELTS IN C COL J, SET W(J)=0. C 140 DO L=1,M B(L)=ZZ(L) END DO C INDEX(IZ)=INDEX(IZ1) INDEX(IZ1)=J IZ1=IZ1+1 NSETP=NPP1 NPP1=NPP1+1 C IF (IZ1.GT.IZ2) GO TO 170 DO JZ=IZ1,IZ2 JJ=INDEX(JZ) CALL H12 (2,NSETP,NPP1,M,A(1,J),1,UP,A(1,JJ),1,MDA,1) END DO 170 CONTINUE C IF (NSETP.EQ.M) GO TO 190 DO L=NPP1,M A(L,J)=ZERO END DO 190 CONTINUE C W(J)=ZERO C SOLVE THE TRIANGULAR SYSTEM. C STORE THE SOLUTION TEMPORARILY IN ZZ(). C ASSIGN 200 TO NEXT NEXT = 1 GO TO 400 200 CONTINUE C C ****** SECONDARY LOOP BEGINS HERE ****** C C ITERATION COUNTER. C 210 ITER=ITER+1 IF (ITER.LE.ITMAX) GO TO 220 MODE=3 ITER = ITER - 1 GO TO 350 220 CONTINUE C C SEE IF ALL NEW CONSTRAINED COEFFS ARE FEASIBLE. C IF NOT COMPUTE ALPHA. C ALPHA=TWO DO 240 IP=1,NSETP L=INDEX(IP) C IF (ZZ(IP)) 230,230,240 IF (ZZ(IP) .GT. 0) GOTO 240 C 230 T=-X(L)/(ZZ(IP)-X(L)) IF (ALPHA.LE.T) GO TO 240 ALPHA=T JJ=IP 240 CONTINUE C C IF ALL NEW CONSTRAINED COEFFS ARE FEASIBLE THEN ALPHA WILL C STILL = 2. IF SO EXIT FROM SECONDARY LOOP TO MAIN LOOP. C IF (ALPHA.EQ.TWO) GO TO 330 C C OTHERWISE USE ALPHA WHICH WILL BE BETWEEN 0. AND 1. TO C INTERPOLATE BETWEEN THE OLD X AND THE NEW ZZ. C DO IP=1,NSETP L=INDEX(IP) X(L)=X(L)+ALPHA*(ZZ(IP)-X(L)) END DO C C MODIFY A AND B AND THE INDEX ARRAYS TO MOVE COEFFICIENT I C FROM SET P TO SET Z. C I=INDEX(JJ) 260 X(I)=ZERO C IF (JJ.EQ.NSETP) GO TO 290 JJ=JJ+1 DO J=JJ,NSETP II=INDEX(J) INDEX(J-1)=II CALL G1 (A(J-1,II),A(J,II),CC,SS,A(J-1,II)) A(J,II)=ZERO DO L=1,N IF (L.NE.II) CALL G2 (CC,SS,A(J-1,L),A(J,L)) END DO CALL G2 (CC,SS,B(J-1),B(J)) END DO 290 NPP1=NSETP NSETP=NSETP-1 IZ1=IZ1-1 INDEX(IZ1)=I C C SEE IF THE REMAINING COEFFS IN SET P ARE FEASIBLE. THEY SHOULD C BE BECAUSE OF THE WAY ALPHA WAS DETERMINED. C IF ANY ARE INFEASIBLE IT IS DUE TO ROUND-OFF ERROR. ANY C THAT ARE NONPOSITIVE WILL BE SET TO ZERO C AND MOVED FROM SET P TO SET Z. C DO 300 JJ=1,NSETP I=INDEX(JJ) C IF (X(I)) 260,260,300 IF (X(I) .LE. 0) GOTO 260 300 CONTINUE C C COPY B( ) INTO ZZ( ). THEN SOLVE AGAIN AND LOOP BACK. C DO I=1,M ZZ(I)=B(I) END DO C ASSIGN 320 TO NEXT NEXT = 2 GO TO 400 320 CONTINUE GO TO 210 C ****** END OF SECONDARY LOOP ****** C 330 DO IP=1,NSETP I=INDEX(IP) X(I)=ZZ(IP) END DO C ALL NEW COEFFS ARE POSITIVE. LOOP BACK TO BEGINNING. GO TO 30 C C ****** END OF MAIN LOOP ****** C C COME TO HERE FOR TERMINATION. C COMPUTE THE NORM OF THE FINAL RESIDUAL VECTOR. C 350 SM=ZERO IF (NPP1.GT.M) GO TO 370 DO I=NPP1,M SM=SM+B(I)**2 END DO GO TO 390 370 DO J=1,N W(J)=ZERO END DO 390 RNORM=SQRT(SM) RETURN C C THE FOLLOWING BLOCK OF CODE IS USED AS AN INTERNAL SUBROUTINE C TO SOLVE THE TRIANGULAR SYSTEM, PUTTING THE SOLUTION IN ZZ(). C 400 DO L=1,NSETP IP=NSETP+1-L IF (L.EQ.1) GO TO 420 DO II=1,IP ZZ(II)=ZZ(II)-A(II,JJ)*ZZ(IP+1) END DO 420 JJ=INDEX(IP) ZZ(IP)=ZZ(IP)/A(IP,JJ) END DO C GO TO NEXT, (200,320) GOTO (200, 320), NEXT 440 FORMAT (35H0 NNLS QUITTING ON ITERATION COUNT.) END C C SUBROUTINE H12 (MODE,LPIVOT,L1,M,U,IUE,UP,C,ICE,ICV,NCV) C C.L.LAWSON AND R.J.HANSON, JET PROPULSION LABORATORY, 1973 JUN 12 C TO APPEAR IN @SOLVING LEAST SQUARES PROBLEMS@, PRENTICE-HALL, 1974 C C CONSTRUCTION AND/OR APPLICATION OF A SINGLE C HOUSEHOLDER TRANSFORMATION.. Q = I + U*(U**T)/B C C MODE = 1 OR 2 TO SELECT ALGORITHM H1 OR H2 . C LPIVOT IS THE INDEX OF THE PIVOT ELEMENT. C L1,M IF L1 .LE. M THE TRANSFORMATION WILL BE CONSTRUCTED TO C ZERO ELEMENTS INDEXED FROM L1 THROUGH M. IF L1 GT. M C THE SUBROUTINE DOES AN IDENTITY TRANSFORMATION. C U(),IUE,UP ON ENTRY TO H1 U() CONTAINS THE PIVOT VECTOR. C IUE IS THE STORAGE INCREMENT BETWEEN ELEMENTS. C ON EXIT FROM H1 U() AND UP C CONTAIN QUANTITIES DEFINING THE VECTOR U OF THE C HOUSEHOLDER TRANSFORMATION. ON ENTRY TO H2 U() C AND UP SHOULD CONTAIN QUANTITIES PREVIOUSLY COMPUTED C BY H1. THESE WILL NOT BE MODIFIED BY H2. C C() ON ENTRY TO H1 OR H2 C() CONTAINS A MATRIX WHICH WILL BE C REGARDED AS A SET OF VECTORS TO WHICH THE HOUSEHOLDER C TRANSFORMATION IS TO BE APPLIED. ON EXIT C() CONTAINS THE C SET OF TRANSFORMED VECTORS. C ICE STORAGE INCREMENT BETWEEN ELEMENTS OF VECTORS IN C(). C ICV STORAGE INCREMENT BETWEEN VECTORS IN C(). C NCV NUMBER OF VECTORS IN C() TO BE TRANSFORMED. IF NCV .LE. 0 C NO OPERATIONS WILL BE DONE ON C(). C SUBROUTINE H12 (MODE,LPIVOT,L1,M,U,IUE,UP,C,ICE,ICV,NCV) DIMENSION U(IUE,1), C(1) DOUBLE PRECISION SM,B ONE=1. C IF (0.GE.LPIVOT.OR.LPIVOT.GE.L1.OR.L1.GT.M) RETURN CL=ABS(U(1,LPIVOT)) IF (MODE.EQ.2) GO TO 60 C ****** CONSTRUCT THE TRANSFORMATION. ****** DO J=L1,M CL=AMAX1(ABS(U(1,J)),CL) END DO C IF (CL) 130,130,20 IF (CL .LE. 0) GOTO 130 20 CLINV=ONE/CL SM=(DBLE(U(1,LPIVOT))*CLINV)**2 DO J=L1,M SM=SM+(DBLE(U(1,J))*CLINV)**2 END DO C CONVERT DBLE. PREC. SM TO SNGL. PREC. SM1 SM1=SM CL=CL*SQRT(SM1) C IF (U(1,LPIVOT)) 50,50,40 IF (U(1,LPIVOT) .LE. 0) GOTO 50 40 CL=-CL 50 UP=U(1,LPIVOT)-CL U(1,LPIVOT)=CL GO TO 70 C ****** APPLY THE TRANSFORMATION I+U*(U**T)/B TO C. ****** C C 60 IF (CL) 130,130,70 60 IF (CL .LE. 0) GOTO 130 70 IF (NCV.LE.0) RETURN B=DBLE(UP)*U(1,LPIVOT) C B MUST BE NONPOSITIVE HERE. IF B = 0., RETURN. C C IF (B) 80,130,130 IF (B .GE. 0) GOTO 130 80 B=ONE/B I2=1-ICV+ICE*(LPIVOT-1) INCR=ICE*(L1-LPIVOT) DO 120 J=1,NCV I2=I2+ICV I3=I2+INCR I4=I3 SM=C(I2)*DBLE(UP) DO I=L1,M SM=SM+C(I3)*DBLE(U(1,I)) I3=I3+ICE END DO C IF (SM) 100,120,100 IF (SM .EQ. 0) GOTO 120 100 SM=SM*B C(I2)=C(I2)+SM*DBLE(UP) DO I=L1,M C(I4)=C(I4)+SM*DBLE(U(1,I)) I4=I4+ICE END DO 120 CONTINUE 130 RETURN END C SUBROUTINE G1 (A,B,COS,SIN,SIG) C C.L.LAWSON AND R.J.HANSON, JET PROPULSION LABORATORY, 1973 JUN 12 C TO APPEAR IN @SOLVING LEAST SQUARES PROBLEMS@, PRENTICE-HALL, 1974 C C C COMPUTE ORTHOGONAL ROTATION MATRIX.. C COMPUTE.. MATRIX (C, S) SO THAT (C, S)(A) = (SQRT(A**2+B**2)) C (-S,C) (-S,C)(B) ( 0 ) C COMPUTE SIG = SQRT(A**2+B**2) C SIG IS COMPUTED LAST TO ALLOW FOR THE POSSIBILITY THAT C SIG MAY BE IN THE SAME LOCATION AS A OR B . C ZERO=0. ONE=1. IF (ABS(A).LE.ABS(B)) GO TO 10 XR=B/A YR=SQRT(ONE+XR**2) COS=SIGN(ONE/YR,A) SIN=COS*XR SIG=ABS(A)*YR RETURN C 10 IF (B) 20,30,20 10 IF (B .EQ. 0) GOTO 30 20 XR=A/B YR=SQRT(ONE+XR**2) SIN=SIGN(ONE/YR,B) COS=SIN*XR SIG=ABS(B)*YR RETURN 30 SIG=ZERO COS=ZERO SIN=ONE RETURN END C SUBROUTINE G2 (COS,SIN,X,Y) C C.L.LAWSON AND R.J.HANSON, JET PROPULSION LABORATORY, 1972 DEC 15 C TO APPEAR IN @SOLVING LEAST SQUARES PROBLEMS@, PRENTICE-HALL, 1974 C APPLY THE ROTATION COMPUTED BY G1 TO (X,Y). XR=COS*X+SIN*Y Y=-SIN*X+COS*Y X=XR RETURN END C FUNCTION DIFF(X,Y) C C.L.LAWSON AND R.J.HANSON, JET PROPULSION LABORATORY, 1973 JUNE 7 C TO APPEAR IN @SOLVING LEAST SQUARES PROBLEMS@, PRENTICE-HALL, 1974 DIFF=X-Y RETURN END C C Double precision versions C SUBROUTINE DLDP (G,MDG,M,N,H,X,XNORM,W,INDEX,MODE) C C.L.LAWSON AND R.J.HANSON, JET PROPULSION LABORATORY, 1974 MAR 1 C TO APPEAR IN @SOLVING LEAST SQUARES PROBLEMS@, PRENTICE-HALL, 1974 C C ********** LEAST DISTANCE PROGRAMMING ********** C implicit double precision (a-h,o-z) INTEGER INDEX(1) DIMENSION G(MDG,1), H(1), X(1), W(1) ZERO=0.D0 ONE=1.D0 IF (N.LE.0) GO TO 120 DO J=1,N X(J)=ZERO END DO XNORM=ZERO IF (M.LE.0) GO TO 110 C C THE DECLARED DIMENSION OF W() MUST BE AT LEAST (N+1)*(M+2)+2*M. C C FIRST (N+1)*M LOCS OF W() = MATRIX E FOR PROBLEM NNLS. C NEXT N+1 LOCS OF W() = VECTOR F FOR PROBLEM NNLS. C NEXT N+1 LOCS OF W() = VECTOR Z FOR PROBLEM NNLS. C NEXT M LOCS OF W() = VECTOR Y FOR PROBLEM NNLS. C NEXT M LOCS OF W() = VECTOR WDUAL FOR PROBLEM NNLS. C COPY G**T INTO FIRST N ROWS AND M COLUMNS OF E. C COPY H**T INTO ROW N+1 OF E. C IW=0 DO J=1,M DO I=1,N IW=IW+1 W(IW)=G(J,I) END DO IW=IW+1 W(IW)=H(J) END DO IF=IW+1 C STORE N ZEROS FOLLOWED BY A ONE INTO F. DO I=1,N IW=IW+1 W(IW)=ZERO END DO W(IW+1)=ONE C NP1=N+1 IZ=IW+2 IY=IZ+NP1 IWDUAL=IY+M C CALL DNNLS (W,NP1,NP1,M,W(IF),W(IY),RNORM,W(IWDUAL),W(IZ), $ INDEX, 3*NP1, MODE) c print *, rnorm, mode c write (20,*) (W(IY+I-1), I=1,N+1) C USE THE FOLLOWING RETURN IF UNSUCCESSFUL IN NNLS. IF (MODE.NE.1) RETURN C IF (RNORM) 130,130,50 IF (RNORM .LE. 0) GOTO 130 50 FAC=ONE IW=IY-1 DO I=1,M IW=IW+1 C HERE WE ARE USING THE SOLUTION VECTOR Y. FAC=FAC-H(I)*W(IW) END DO C C IF (DDIFF(ONE+FAC,ONE)) 130,130,70 IF (DDIFF(ONE+FAC,ONE) .LE. 0) GOTO 130 70 FAC=ONE/FAC DO J=1,N IW=IY-1 DO I=1,M IW=IW+1 C HERE WE ARE USING THE SOLUTION VECTOR Y. X(J)=X(J)+G(I,J)*W(IW) END DO X(J)=X(J)*FAC END DO DO J=1,N XNORM=XNORM+X(J)**2 END DO XNORM=SQRT(XNORM) C SUCCESSFUL RETURN. 110 MODE=1 c do i = 1, m c acc = 0.0 c do k = 1, n c acc = acc + g(i,k) * x(k) c end do c write (21, *) i, acc, h(i) c end do RETURN C ERROR RETURN. N .LE. 0. 120 MODE=2 RETURN C RETURNING WITH CONSTRAINTS NOT COMPATIBLE. 130 MODE=4 RETURN END C C C SUBROUTINE DNNLS (A,MDA,M,N,B,X,RNORM,W,ZZ,INDEX,ITMAX,MODE) C C.L.LAWSON AND R.J.HANSON, JET PROPULSION LABORATORY, 1973 JUNE 15 C TO APPEAR IN @SOLVING LEAST SQUARES PROBLEMS@, PRENTICE-HALL, 1974 C C ********** NONNEGATIVE LEAST SQUARES ********** C C GIVEN AN M BY N MATRIX, A, AND AN M-VECTOR, B, COMPUTE AN C N-VECTOR, X, WHICH SOLVES THE LEAST SQUARES PROBLEM C C A * X = B SUBJECT TO X .GE. 0 C C A(),MDA,M,N MDA IS THE FIRST DIMENSIONING PARAMETER FOR THE C ARRAY, A(). ON ENTRY A() CONTAINS THE M BY N C MATRIX, A. ON EXIT A() CONTAINS C THE PRODUCT MATRIX, Q*A , WHERE Q IS AN C M BY M ORTHOGONAL MATRIX GENERATED IMPLICITLY BY C THIS SUBROUTINE. C B() ON ENTRY B() CONTAINS THE M-VECTOR, B. ON EXIT B() CON- C TAINS Q*B. C X() ON ENTRY X() NEED NOT BE INITIALIZED. ON EXIT X() WILL C CONTAIN THE SOLUTION VECTOR. C RNORM ON EXIT RNORM CONTAINS THE EUCLIDEAN NORM OF THE C RESIDUAL VECTOR. C W() AN N-ARRAY OF WORKING SPACE. ON EXIT W() WILL CONTAIN C THE DUAL SOLUTION VECTOR. W WILL SATISFY W(I) = 0. C FOR ALL I IN SET P AND W(I) .LE. 0. FOR ALL I IN SET Z C ZZ() AN M-ARRAY OF WORKING SPACE. C INDEX() AN INTEGER WORKING ARRAY OF LENGTH AT LEAST N. C ON EXIT THE CONTENTS OF THIS ARRAY DEFINE THE SETS C P AND Z AS FOLLOWS.. C C INDEX(1) THRU INDEX(NSETP) = SET P. C INDEX(IZ1) THRU INDEX(IZ2) = SET Z. C IZ1 = NSETP + 1 = NPP1 C IZ2 = N C ITMAX MAXIMUM NUMBER OF ITERATIONS (WAS HARDED CODED AT 3*N IN C ORIGINAL LAWSON ROUTINE) C MODE THIS IS A SUCCESS-FAILURE FLAG WITH THE FOLLOWING C MEANINGS. C 1 THE SOLUTION HAS BEEN COMPUTED SUCCESSFULLY. C 2 THE DIMENSIONS OF THE PROBLEM ARE BAD. C EITHER M .LE. 0 OR N .LE. 0. C 3 ITERATION COUNT EXCEEDED. MORE THAN ITMAX ITERATIONS. C SUBROUTINE DNNLS (A,MDA,M,N,B,X,RNORM,W,ZZ,INDEX,ITMAX,MODE) implicit double precision (a-h,o-z) DIMENSION A(MDA,N), B(1), X(1), W(1), ZZ(1) DOUBLE PRECISION DUMMY(1) INTEGER INDEX(1) ZERO=0.D0 ONE=1.D0 TWO=2.D0 FACTOR=0.01D0 C MODE=1 IF (M.GT.0.AND.N.GT.0) GO TO 10 MODE=2 RETURN 10 ITER=0 C C INITIALIZE THE ARRAYS INDEX() AND X(). C DO I=1,N X(I)=ZERO INDEX(I)=I END DO C IZ2=N IZ1=1 NSETP=0 NPP1=1 C ****** MAIN LOOP BEGINS HERE ****** 30 CONTINUE C QUIT IF ALL COEFFICIENTS ARE ALREADY IN THE SOLUTION. C OR IF M COLS OF A HAVE BEEN TRIANGULARIZED. C IF (IZ1.GT.IZ2.OR.NSETP.GE.M) GO TO 350 C C COMPUTE COMPONENTS OF THE DUAL (NEGATIVE GRADIENT) VECTOR W(). C DO IZ=IZ1,IZ2 J=INDEX(IZ) SM=ZERO DO L=NPP1,M SM=SM+A(L,J)*B(L) END DO W(J)=SM END DO C FIND LARGEST POSITIVE W(J). 60 WMAX=ZERO DO 70 IZ=IZ1,IZ2 J=INDEX(IZ) IF (W(J).LE.WMAX) GO TO 70 WMAX=W(J) IZMAX=IZ 70 CONTINUE C C IF WMAX .LE. 0. GO TO TERMINATION. C THIS INDICATES SATISFACTION OF THE KUHN-TUCKER CONDITIONS. C C IF (WMAX) 350,350,80 IF (WMAX .LE. 0) GOTO 350 80 IZ=IZMAX J=INDEX(IZ) C C THE SIGN OF W(J) IS OK FOR J TO BE MOVED TO SET P. C BEGIN THE TRANSFORMATION AND CHECK NEW DIAGONAL ELEMENT TO AVOID C NEAR LINEAR DEPENDENCE. C ASAVE=A(NPP1,J) CALL DH12 (1,NPP1,NPP1+1,M,A(1,J),1,UP,DUMMY,1,1,0) UNORM=ZERO IF (NSETP.EQ.0) GO TO 100 DO L=1,NSETP UNORM=UNORM+A(L,J)**2 END DO 100 UNORM=SQRT(UNORM) C IF (DDIFF(UNORM+ABS(A(NPP1,J))*FACTOR,UNORM)) 130,130,110 IF (DDIFF(UNORM+ABS(A(NPP1,J))*FACTOR,UNORM) .LE. 0) GOTO 130 C C COL J IS SUFFICIENTLY INDEPENDENT. COPY B INTO ZZ, UPDATE ZZ AND C > SOLVE FOR ZTEST ( = PROPOSED NEW VALUE FOR X(J) ). C 110 DO L=1,M ZZ(L)=B(L) END DO CALL DH12 (2,NPP1,NPP1+1,M,A(1,J),1,UP,ZZ,1,1,1) ZTEST=ZZ(NPP1)/A(NPP1,J) C C SEE IF ZTEST IS POSITIVE C REJECT J AS A CANDIDATE TO BE MOVED FROM SET Z TO SET P. C RESTORE A(NPP1,J), SET W(J)=0., AND LOOP BACK TO TEST DUAL C C IF (ZTEST) 130,130,140 IF (ZTEST .GT. 0) GOTO 140 C C COEFFS AGAIN. C 130 A(NPP1,J)=ASAVE W(J)=ZERO GO TO 60 C C THE INDEX J=INDEX(IZ) HAS BEEN SELECTED TO BE MOVED FROM C SET Z TO SET P. UPDATE B, UPDATE INDICES, APPLY HOUSEHOLDER C TRANSFORMATIONS TO COLS IN NEW SET Z, ZERO SUBDIAGONAL ELTS IN C COL J, SET W(J)=0. C 140 DO L=1,M B(L)=ZZ(L) END DO C INDEX(IZ)=INDEX(IZ1) INDEX(IZ1)=J IZ1=IZ1+1 NSETP=NPP1 NPP1=NPP1+1 C IF (IZ1.GT.IZ2) GO TO 170 DO JZ=IZ1,IZ2 JJ=INDEX(JZ) CALL DH12 (2,NSETP,NPP1,M,A(1,J),1,UP,A(1,JJ),1,MDA,1) END DO 170 CONTINUE C IF (NSETP.EQ.M) GO TO 190 DO L=NPP1,M A(L,J)=ZERO END DO 190 CONTINUE C W(J)=ZERO C SOLVE THE TRIANGULAR SYSTEM. C STORE THE SOLUTION TEMPORARILY IN ZZ(). C ASSIGN 200 TO NEXT NEXT = 1 GO TO 400 200 CONTINUE C C ****** SECONDARY LOOP BEGINS HERE ****** C C ITERATION COUNTER. C 210 ITER=ITER+1 IF (ITER.LE.ITMAX) GO TO 220 MODE=3 C Return number of iterations actually completed ITER = ITER - 1 GO TO 350 220 CONTINUE C C SEE IF ALL NEW CONSTRAINED COEFFS ARE FEASIBLE. C IF NOT COMPUTE ALPHA. C ALPHA=TWO DO 240 IP=1,NSETP L=INDEX(IP) C IF (ZZ(IP)) 230,230,240 IF (ZZ(IP) .GT. 0) GOTO 240 C 230 T=-X(L)/(ZZ(IP)-X(L)) IF (ALPHA.LE.T) GO TO 240 ALPHA=T JJ=IP 240 CONTINUE C C IF ALL NEW CONSTRAINED COEFFS ARE FEASIBLE THEN ALPHA WILL C STILL = 2. IF SO EXIT FROM SECONDARY LOOP TO MAIN LOOP. C IF (ALPHA.EQ.TWO) GO TO 330 C C OTHERWISE USE ALPHA WHICH WILL BE BETWEEN 0. AND 1. TO C INTERPOLATE BETWEEN THE OLD X AND THE NEW ZZ. C DO IP=1,NSETP L=INDEX(IP) X(L)=X(L)+ALPHA*(ZZ(IP)-X(L)) END DO C C MODIFY A AND B AND THE INDEX ARRAYS TO MOVE COEFFICIENT I C FROM SET P TO SET Z. C I=INDEX(JJ) 260 X(I)=ZERO C IF (JJ.EQ.NSETP) GO TO 290 JJ=JJ+1 DO J=JJ,NSETP II=INDEX(J) INDEX(J-1)=II CALL DG1 (A(J-1,II),A(J,II),CC,SS,A(J-1,II)) A(J,II)=ZERO DO 270 L=1,N IF (L.NE.II) CALL DG2 (CC,SS,A(J-1,L),A(J,L)) 270 CONTINUE CALL DG2 (CC,SS,B(J-1),B(J)) END DO 290 NPP1=NSETP NSETP=NSETP-1 IZ1=IZ1-1 INDEX(IZ1)=I C C SEE IF THE REMAINING COEFFS IN SET P ARE FEASIBLE. THEY SHOULD C BE BECAUSE OF THE WAY ALPHA WAS DETERMINED. C IF ANY ARE INFEASIBLE IT IS DUE TO ROUND-OFF ERROR. ANY C THAT ARE NONPOSITIVE WILL BE SET TO ZERO C AND MOVED FROM SET P TO SET Z. C DO 300 JJ=1,NSETP I=INDEX(JJ) C IF (X(I)) 260,260,300 IF (X(I) .LE. 0) GOTO 260 300 CONTINUE C C COPY B( ) INTO ZZ( ). THEN SOLVE AGAIN AND LOOP BACK. C DO I=1,M ZZ(I)=B(I) END DO C ASSIGN 320 TO NEXT NEXT = 2 GO TO 400 320 CONTINUE GO TO 210 C ****** END OF SECONDARY LOOP ****** C 330 DO IP=1,NSETP I=INDEX(IP) X(I)=ZZ(IP) END DO C ALL NEW COEFFS ARE POSITIVE. LOOP BACK TO BEGINNING. GO TO 30 C C ****** END OF MAIN LOOP ****** C C COME TO HERE FOR TERMINATION. C COMPUTE THE NORM OF THE FINAL RESIDUAL VECTOR. C 350 SM=ZERO IF (NPP1.GT.M) GO TO 370 DO I=NPP1,M SM=SM+B(I)**2 END DO GO TO 390 370 DO J=1,N W(J)=ZERO END DO 390 RNORM=SQRT(SM) RETURN C C THE FOLLOWING BLOCK OF CODE IS USED AS AN INTERNAL SUBROUTINE C TO SOLVE THE TRIANGULAR SYSTEM, PUTTING THE SOLUTION IN ZZ(). C 400 DO L=1,NSETP IP=NSETP+1-L IF (L.EQ.1) GO TO 420 DO II=1,IP ZZ(II)=ZZ(II)-A(II,JJ)*ZZ(IP+1) END DO 420 JJ=INDEX(IP) ZZ(IP)=ZZ(IP)/A(IP,JJ) END DO C GO TO NEXT, (200,320) GOTO (200, 320), NEXT 440 FORMAT (35H0 NNLS QUITTING ON ITERATION COUNT.) END C C SUBROUTINE DH12 (MODE,LPIVOT,L1,M,U,IUE,UP,C,ICE,ICV,NCV) C C.L.LAWSON AND R.J.HANSON, JET PROPULSION LABORATORY, 1973 JUN 12 C TO APPEAR IN @SOLVING LEAST SQUARES PROBLEMS@, PRENTICE-HALL, 1974 C C CONSTRUCTION AND/OR APPLICATION OF A SINGLE C HOUSEHOLDER TRANSFORMATION.. Q = I + U*(U**T)/B C C MODE = 1 OR 2 TO SELECT ALGORITHM H1 OR H2 . C LPIVOT IS THE INDEX OF THE PIVOT ELEMENT. C L1,M IF L1 .LE. M THE TRANSFORMATION WILL BE CONSTRUCTED TO C ZERO ELEMENTS INDEXED FROM L1 THROUGH M. IF L1 GT. M C THE SUBROUTINE DOES AN IDENTITY TRANSFORMATION. C U(),IUE,UP ON ENTRY TO H1 U() CONTAINS THE PIVOT VECTOR. C IUE IS THE STORAGE INCREMENT BETWEEN ELEMENTS. C ON EXIT FROM H1 U() AND UP C CONTAIN QUANTITIES DEFINING THE VECTOR U OF THE C HOUSEHOLDER TRANSFORMATION. ON ENTRY TO H2 U() C AND UP SHOULD CONTAIN QUANTITIES PREVIOUSLY COMPUTED C BY H1. THESE WILL NOT BE MODIFIED BY H2. C C() ON ENTRY TO H1 OR H2 C() CONTAINS A MATRIX WHICH WILL BE C REGARDED AS A SET OF VECTORS TO WHICH THE HOUSEHOLDER C TRANSFORMATION IS TO BE APPLIED. ON EXIT C() CONTAINS THE C SET OF TRANSFORMED VECTORS. C ICE STORAGE INCREMENT BETWEEN ELEMENTS OF VECTORS IN C(). C ICV STORAGE INCREMENT BETWEEN VECTORS IN C(). C NCV NUMBER OF VECTORS IN C() TO BE TRANSFORMED. IF NCV .LE. 0 C NO OPERATIONS WILL BE DONE ON C(). C SUBROUTINE DH12 (MODE,LPIVOT,L1,M,U,IUE,UP,C,ICE,ICV,NCV) implicit double precision (a-h,o-z) DIMENSION U(IUE,1), C(1) DOUBLE PRECISION SM,B ONE=1.D0 C IF (0.GE.LPIVOT.OR.LPIVOT.GE.L1.OR.L1.GT.M) RETURN CL=ABS(U(1,LPIVOT)) IF (MODE.EQ.2) GO TO 60 C ****** CONSTRUCT THE TRANSFORMATION. ****** DO J=L1,M CL=DMAX1(ABS(U(1,J)),CL) END DO C IF (CL) 130,130,20 IF (CL .LE. 0) GOTO 130 20 CLINV=ONE/CL SM=(DBLE(U(1,LPIVOT))*CLINV)**2 DO J=L1,M SM=SM+(DBLE(U(1,J))*CLINV)**2 END DO C CONVERT DBLE. PREC. SM TO SNGL. PREC. SM1 SM1=SM CL=CL*SQRT(SM1) C IF (U(1,LPIVOT)) 50,50,40 IF (U(1,LPIVOT) .LE. 0) GOTO 50 40 CL=-CL 50 UP=U(1,LPIVOT)-CL U(1,LPIVOT)=CL GO TO 70 C ****** APPLY THE TRANSFORMATION I+U*(U**T)/B TO C. ****** C C 60 IF (CL) 130,130,70 60 IF (CL .LE. 0) GOTO 130 70 IF (NCV.LE.0) RETURN B=DBLE(UP)*U(1,LPIVOT) C B MUST BE NONPOSITIVE HERE. IF B = 0., RETURN. C C IF (B) 80,130,130 IF (B .GE. 0) GOTO 130 80 B=ONE/B I2=1-ICV+ICE*(LPIVOT-1) INCR=ICE*(L1-LPIVOT) DO 120 J=1,NCV I2=I2+ICV I3=I2+INCR I4=I3 SM=C(I2)*DBLE(UP) DO I=L1,M SM=SM+C(I3)*DBLE(U(1,I)) I3=I3+ICE END DO C IF (SM) 100,120,100 IF (SM .EQ. 0) GOTO 120 100 SM=SM*B C(I2)=C(I2)+SM*DBLE(UP) DO I=L1,M C(I4)=C(I4)+SM*DBLE(U(1,I)) I4=I4+ICE END DO 120 CONTINUE 130 RETURN END C SUBROUTINE DG1 (A,B,COS,SIN,SIG) implicit double precision (a-h,o-z) C C.L.LAWSON AND R.J.HANSON, JET PROPULSION LABORATORY, 1973 JUN 12 C TO APPEAR IN @SOLVING LEAST SQUARES PROBLEMS@, PRENTICE-HALL, 1974 C C C COMPUTE ORTHOGONAL ROTATION MATRIX.. C COMPUTE.. MATRIX (C, S) SO THAT (C, S)(A) = (SQRT(A**2+B**2)) C (-S,C) (-S,C)(B) ( 0 ) C COMPUTE SIG = SQRT(A**2+B**2) C SIG IS COMPUTED LAST TO ALLOW FOR THE POSSIBILITY THAT C SIG MAY BE IN THE SAME LOCATION AS A OR B . C ZERO=0.D0 ONE=1.D0 IF (ABS(A).LE.ABS(B)) GO TO 10 XR=B/A YR=SQRT(ONE+XR**2) COS=SIGN(ONE/YR,A) SIN=COS*XR SIG=ABS(A)*YR RETURN C 10 IF (B) 20,30,20 10 IF (B .EQ. 0) GOTO 30 20 XR=A/B YR=SQRT(ONE+XR**2) SIN=SIGN(ONE/YR,B) COS=SIN*XR SIG=ABS(B)*YR RETURN 30 SIG=ZERO COS=ZERO SIN=ONE RETURN END C SUBROUTINE DG2 (COS,SIN,X,Y) implicit double precision (a-h,o-z) C C.L.LAWSON AND R.J.HANSON, JET PROPULSION LABORATORY, 1972 DEC 15 C TO APPEAR IN @SOLVING LEAST SQUARES PROBLEMS@, PRENTICE-HALL, 1974 C APPLY THE ROTATION COMPUTED BY G1 TO (X,Y). XR=COS*X+SIN*Y Y=-SIN*X+COS*Y X=XR RETURN END C FUNCTION DDIFF(X,Y) implicit double precision (a-h,o-z) C C.L.LAWSON AND R.J.HANSON, JET PROPULSION LABORATORY, 1973 JUNE 7 C TO APPEAR IN @SOLVING LEAST SQUARES PROBLEMS@, PRENTICE-HALL, 1974 DDIFF=X-Y RETURN END casacore-3.7.1/scimath_f/maxabs.f000066400000000000000000000235201476623553700167050ustar00rootroot00000000000000*----------------------------------------------------------------------- * MAXABS: find the maximum absolute value in an array *----------------------------------------------------------------------- * * Copyright (C) 1997,1998,2000 * Associated Universities, Inc. Washington DC, USA. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, * MA 02139, USA. * * Correspondence concerning AIPS++ should be addressed as follows: * Internet email: casa-feedback@nrao.edu. * Postal address: AIPS++ Project Office * National Radio Astronomy Observatory * 520 Edgemont Road * Charlottesville, VA 22903-2475 USA * * $Id$ * *----------------------------------------------------------------------- * * MAXABS contains a set of functions for finding the * maximum absolute value in a data array. It is always assumed that * the array is one dimensional, with a specified number of elements. * Standard AIPS++ Arrays can, unless they have a step increment * defined, or are a subarray, be treated as one dimensional for the * purposes of finding the maximum value. * * These functions where written for the ClarkCleanModel class and * hence include the ability to find the maximum absolute * value of a two dimensional array where the second axis is the * polarization axis. This requires that the slowest moving axis is * the polarization axis. * * There are also a set of functions for for finding the absolute * maximum value on pixels which are weighted by a mask. A mask is a * REAL array with the same number of pixels as the data array. Each * pixel is multiplied by the mask before its magnitude is compared * with the current maximum. If the mask is zero for a pixel then * its value can never be returned as the maximum. ie. the pixel is * masked. If the mask is one then then the mask has no effect on the * pixel. Intermediate values (between zero and one) can be used to * achieve a soft masking, where pixels may have their value * returned if there value is large enough. The returned value is * always the absolute value multiplied by the mask. * * The following subroutines are found here: * MAXABSF : for Arrays of REAL(float) numbers * MAXABS2F: for Arrays of REAL(float) numbers with * two polarizations (I & V) * MAXABS4F: for Arrays of REAL(float) numbers with * four polarizations (I, Q, U, V) * MAXABMF : for Arrays of REAL(float) numbers and masking * MAXABM2F: for Arrays of REAL(float) numbers with * two polarizations (I & V) and masking * MAXABM4F: for Arrays of REAL(float) numbers with * four polarizations (I, Q, U, V) and masking * It should be trivial to clone these routines for any other data * types, in particular double precision. * * The following comments apply to all subroutines found here: * * For the first three subroutines the general form of the subroutine * call is (x is replaced by a character indicating the type: F for * Float, D for Double, etc.), and p is a number indicating the * number of polarizations that are required. (if p=1 it is dropped * entirely) * * SUBROUTINE MAXABSpx(MAXVAL, ARR, NPIX) * Given: * NPIX I The number of pixels in the Array * ARR(NPIX,p) * x Data array. Note that if p.NE.1 then NPIX is * NOT the total size of the ARR array * * return: * MAXVAL x The maximum absolute value in the Array * * Notes: * 1) It is assumed that the data array contains at least ONE * element (ie. NPIX.GE.1) * * 2) In the 2 polarization case it is assumed that the two * polarizations are I & V, and that I is in the first half of * the array, and V in the second half. * * 3) In the 4 polarization case it is assumed that the four * polarizations are I, Q, U & V, and that they are in that * order in the array * * 4) The minimum and maximum absolute values returned if p.NE.1 are a * function of all the polarizations at that pixel. For the 4 * polarization case this is the maximum eigenvalue * (=ABS(I+SQRT(Q*Q+U*U+V*V))), and for the two polarisation * case it is MAX(ABS(I+V), ABS(I-V)) * * For the last three subroutines the general form of the subroutine * call is (x is replaced by a character indicating the type: F for * Float, D for Double, etc.), and p is a number indicating the * number of polarizations that are required. (if p=1 it is dropped * entirely) * * SUBROUTINE MAXABMpx(MAXVAL, ARR, MASK, NPIX) * Given: * NPIX I The number of pixels in the Array * ARR(NPIX,p) * x Data array. Note that if p.NE.1 then NPIX is * NOT the total size of the ARR array * MASK(NPIX) * x MASK array. * * return: * MAXVAL x The maximum absolute value in the Array * multiplied by the mask value * * Notes: * 1) See all the notes (1-4) for the ABSMAXpx routines * * 2) The mask array is a different size (ie. number of bytes) * to the data array if p.NE.1 *----------------------------------------------------------------------- SUBROUTINE MAXABSF(MAXVAL, ARR, NPIX) * Find the maximum absolute value in an array of REAL numbers INTEGER NPIX REAL MAXVAL, ARR(NPIX) INTEGER N REAL I *----------------------------------------------------------------------- I = ARR(1) MAXVAL = ABS(I) DO 10 N = 2, NPIX I = ARR(N) MAXVAL = MAX(MAXVAL, ABS(I)) 10 CONTINUE RETURN END *----------------------------------------------------------------------- SUBROUTINE MAXABS2F(MAXVAL, ARR, NPIX) * Find the maximum absolute value in an array of REAL numbers with * 2-Polarizations (I,V) INTEGER NPIX REAL MAXVAL, ARR(NPIX, 2) REAL THISVAL INTEGER N REAL I, V *----------------------------------------------------------------------- I = ARR(1,1) V = ARR(1,2) MAXVAL = MAX(ABS(I+V), ABS(I-V)) DO 10 N = 2, NPIX I = ARR(N,1) V = ARR(N,2) THISVAL = MAX(ABS(I+V), ABS(I-V)) MAXVAL = MAX(MAXVAL, THISVAL) 10 CONTINUE RETURN END *----------------------------------------------------------------------- SUBROUTINE MAXABS4F(MAXVAL, ARR, NPIX) * Find the maximum absolute value in an array of REAL numbers * with 4-Polarizations (I,Q,U,V) INTEGER NPIX REAL MAXVAL, ARR(NPIX, 4) INTEGER N REAL I, Q, U, V *----------------------------------------------------------------------- I = ARR(1,1) Q = ARR(1,2) U = ARR(1,3) V = ARR(1,4) MAXVAL = ABS(I+SQRT(Q*Q+U*U+V*V)) DO 10 N = 2, NPIX I = ARR(N,1) Q = ARR(N,2) U = ARR(N,3) V = ARR(N,4) MAXVAL = MAX(MAXVAL, ABS(I+SQRT(Q*Q+U*U+V*V))) 10 CONTINUE RETURN END *----------------------------------------------------------------------- SUBROUTINE MAXABMF(MAXVAL, ARR, MASK, NPIX) * Find the maximum absolute value in an array of REAL numbers * weighted by a mask. INTEGER NPIX REAL MAXVAL, ARR(NPIX), MASK(NPIX) INTEGER N REAL I *----------------------------------------------------------------------- I = ARR(1) MAXVAL = MASK(1) * ABS(I) DO 10 N = 2, NPIX I = ARR(N) MAXVAL = MAX(MAXVAL, MASK(N) * ABS(I)) 10 CONTINUE RETURN END *----------------------------------------------------------------------- SUBROUTINE MAXABM2F(MAXVAL, ARR, MASK, NPIX) * Find the maximum absolute value in an array of REAL numbers with * 2-Polarizations (I,V) weighted by a mask. INTEGER NPIX REAL MAXVAL, ARR(NPIX, 2), MASK(NPIX) INTEGER N REAL I, V, THISVAL *----------------------------------------------------------------------- I = ARR(1,1) V = ARR(1,2) MAXVAL = MASK(1) * MAX(ABS(I+V), ABS(I-V)) DO 10 N = 1, NPIX I = ARR(N,1) V = ARR(N,2) THISVAL = MASK(N) * MAX(ABS(I+V), ABS(I-V)) MAXVAL = MAX(MAXVAL, THISVAL) 10 CONTINUE RETURN END *----------------------------------------------------------------------- SUBROUTINE MAXABM4F(MAXVAL, ARR, MASK, NPIX) * Find the maximum absolute value in an array of REAL numbers * with 4-Polarizations (I,Q,U,V) weighted by a mask. INTEGER NPIX REAL MAXVAL, ARR(NPIX, 4), MASK(NPIX) INTEGER N REAL I, Q, U, V *----------------------------------------------------------------------- I = ARR(1,1) Q = ARR(1,2) U = ARR(1,3) V = ARR(1,4) MAXVAL = MASK(1) * ABS(I+SQRT(Q*Q+U*U+V*V)) DO 10 N = 2, NPIX I = ARR(N,1) Q = ARR(N,2) U = ARR(N,3) V = ARR(N,4) MAXVAL = MAX(MAXVAL, MASK(N) * ABS(I+SQRT(Q*Q+U*U+V*V))) 10 CONTINUE RETURN END casacore-3.7.1/scimath_f/parametricsolver.f000066400000000000000000001373461476623553700210300ustar00rootroot00000000000000*======================================================================= * Copyright (C) 1999,2001,2002,2003 * Associated Universities, Inc. Washington DC, USA. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, * MA 02139, USA. * * Correspondence concerning AIPS++ should be addressed as follows: * Internet email: casa-feedback@nrao.edu. * Postal address: AIPS++ Project Office * National Radio Astronomy Observatory * 520 Edgemont Road * Charlottesville, VA 22903-2475 USA * * $Id$ *------------------------------------------------------------------------- C C Routines for polynomial and spline fitting, using the global C baseline data to get antenna based coefficients C Written by R.Lucas for IRAM gildas. C SUBROUTINE POLYANT(IY, M, NBAS, IANT, JANT, IREF, $ KPLUS1, NANT, X, Y, W, $ WK1, WK2, WK3, SS, C) C------------------------------------------------------------------------ C polyant computes a weighted least-squares polynimial approximation C to the antenna amplitudes or phases c for an arbitrary set of baseline data points. c parameters: C iy I Input 1 for log(amplitude), 2 for phase c m I Input the number of data points for each baseline c nbas I Input the number of baselines c iant(nbas) I Input start antenna for each baseline c jant(nbas) I Input end antenna for each baseline c iref i Input Reference antenna for phases c kplus1 I Input degree of polynomials + 1 c nant I Input the number of antennas c x(m) R8 Input the data abscissae c y(m,nbas) R8 Input the data values c w(m,mbas) R8 Input weights c wk1(kplus1) R8 Output work space c wk2(kplus1**2*nant**2) c R8 Output work space c wk3(kplus1*nant) c R8 Output work space c ss(nbas) R8 Output rms of fit for each baseline c c(nant,kplus1) R8 Output the polynomial coefficients c C------------------------------------------------------------------------ * Dummy INTEGER M, KPLUS1, NANT, NBAS, IANT(NBAS), JANT(NBAS), IY, IREF REAL*8 C(NANT,KPLUS1), W(M,NBAS), $WK2(KPLUS1*NANT,KPLUS1*NANT), WK3(KPLUS1*NANT), $WK1(KPLUS1), X(M), Y(M,NBAS), SS(NBAS), NORM, TOL LOGICAL ERROR * PARAMETER (TOL=1D-14) c------------------------------------------------------- * PI is defined with more digits than necessary to avoid losing * the last few bits in the decimal to binary conversion REAL*8 PI PARAMETER (PI=3.14159265358979323846D0) REAL*4 PIS PARAMETER (PIS=3.141592653) * Relative precision of REAL*4 REAL*4 EPSR4 PARAMETER (EPSR4=1E-7) REAL*4 MAXR4 PARAMETER (MAXR4=1E38) * Maximum acceptable integer INTEGER MAX_INTEG PARAMETER (MAX_INTEG=2147483647) C------------------------------------------------- * Local INTEGER ZANT, I, IA, IB, NANTM1, J, $JA, IC, IL, KN, KP, K, L, ITER, INFO REAL*8 WI, XI, WN, WW, WWW, TEST, YI, XCAP, X1, XN, D, WSS(4000) C------------------------------------------------------------------------ C Code c c Check that the weights are positive. c ERROR=.FALSE. DO I=1,M DO IB = 1, NBAS IF (W(I,IB).LT.0.0D0) THEN C CALL MESSAGE(6,4,'POLYANT','Weights not positive') write(*,*)'POLYANT: Weights not positive' ERROR = .TRUE. RETURN ENDIF ENDDO ENDDO X1 = X(1) XN = X(M) D = XN-X1 c * Amplitude case is simple... c IF (IY.EQ.1) THEN DO I=1,NANT*KPLUS1 DO L=1,NANT*KPLUS1 WK2(L,I) = 0.0D0 ENDDO WK3(I) = 0.0D0 ENDDO DO I=1, M XI = X(I) XCAP = ((XI-X1)-(XN-XI))/D * compute the chebychev polynomials at point xi. CALL CHEB (KPLUS1, XCAP, WK1, ERROR) IF (ERROR) GOTO 999 * c store the upper-triangular part of the normal equations in wk2 c and the right-hand side in wk3. c DO K=1, KPLUS1 WN = WK1(K) IL = (K-1)*NANT DO KP = 1, KPLUS1 WW = WN*WK1(KP) IC = (KP-1)*NANT DO IB=1,NBAS WI = W(I,IB) IF (WI.GT.0) THEN IA = IANT(IB) JA = JANT(IB) WWW = WI*WW WK2(IL+IA,IC+IA) = WK2(IL+IA,IC+IA)+WWW WK2(IL+IA,IC+JA) = WK2(IL+IA,IC+JA)+WWW WK2(IL+JA,IC+IA) = WK2(IL+JA,IC+IA)+WWW WK2(IL+JA,IC+JA) = WK2(IL+JA,IC+JA)+WWW ENDIF ENDDO ENDDO DO IB=1, NBAS IA = IANT(IB) JA = JANT(IB) WI = W(I,IB)*WN*Y(I,IB) * wi = w(i,ib)*wn*xcap*(ja+ia) !**! WK3(IL+IA) = WK3(IL+IA) + WI WK3(IL+JA) = WK3(IL+JA) + WI ENDDO ENDDO ENDDO C C Solve the system of normal equations by first computing the Cholesky c factorization * CALL MTH_DPOTRF ('POLYANT', $ 'U',KPLUS1*NANT,WK2,KPLUS1*NANT,INFO) IF (ERROR) GOTO 999 CALL MTH_DPOTRS ('POLYANT', $ 'U',KPLUS1*NANT,1,WK2,KPLUS1*NANT, $ WK3,KPLUS1*NANT,INFO) IF (ERROR) GOTO 999 DO J=1,KPLUS1 DO I=1,NANT C(I,J) = WK3(I+(J-1)*NANT) ENDDO ENDDO * * Phase is more complicated ... ELSEIF (IY.EQ.2) THEN NANTM1 = NANT-1 DO I=1,NANT DO J=1,KPLUS1 C(I,J) = 0.0D0 ENDDO ENDDO * * start iterating NORM = 1E10 ITER = 0 DO WHILE (NORM.GT.TOL .AND. ITER.LT.100) ITER = ITER + 1 DO I=1,NANTM1*KPLUS1 DO L=1,NANTM1*KPLUS1 WK2(L,I) = 0.0D0 ENDDO WK3(I) = 0.0D0 ENDDO DO I=1, M XI = X(I) XCAP = ((XI-X1)-(XN-XI))/D * compute the chebychev polynomials at point xi. CALL CHEB (KPLUS1, XCAP, WK1, ERROR) IF (ERROR) GOTO 999 * DO K=1, KPLUS1 WN = WK1(K) IL = (K-1)*NANTM1 DO KP = 1, KPLUS1 WW = WN*WK1(KP) IC = (KP-1)*NANTM1 DO IB=1,NBAS WI = W(I,IB) IF (WI.GT.0) THEN IA = ZANT(IANT(IB),IREF) JA = ZANT(JANT(IB),IREF) WWW = WI*WW IF (IA.NE.0) THEN WK2(IL+IA,IC+IA) = WK2(IL+IA,IC+IA)+WWW ENDIF IF (JA.NE.0) THEN WK2(IL+JA,IC+JA) = WK2(IL+JA,IC+JA)+WWW ENDIF IF (IA.NE.0 .AND. JA.NE.0) THEN WK2(IL+JA,IC+IA) = WK2(IL+JA,IC+IA)-WWW WK2(IL+IA,IC+JA) = WK2(IL+IA,IC+JA)-WWW ENDIF ENDIF ENDDO ENDDO DO IB=1, NBAS IF (W(I,IB).GT.0) THEN YI = Y(I,IB) IA = IANT(IB) JA = JANT(IB) DO KP=1, KPLUS1 YI = YI+(C(IA,KP)-C(JA,KP))*WK1(KP) ENDDO * yi = xcap*(ja-ia) !**! ELSE YI = 0 ENDIF * if (norm.lt.1e9) then YI = SIN(YI) * endif IA = ZANT(IANT(IB),IREF) JA = ZANT(JANT(IB),IREF) WI = W(I,IB)*WN*YI IF (IA.NE.0) THEN WK3(IA+IL) = WK3(IA+IL) - WI ENDIF IF (JA.NE.0) THEN WK3(JA+IL) = WK3(JA+IL) + WI ENDIF ENDDO ENDDO ENDDO C Solve the system of normal equations by first computing the Cholesky c factorization CALL MTH_DPOTRF('POLYANT', $ 'U',KPLUS1*NANTM1,WK2,KPLUS1*NANT,INFO) IF (ERROR) GOTO 999 CALL MTH_DPOTRS ('POLYANT', $ 'U',KPLUS1*NANTM1,1,WK2,KPLUS1*NANT, $ WK3,KPLUS1*NANTM1,INFO) IF (ERROR) GOTO 999 * * Add the result to c: NORM = 0 DO J=1,KPLUS1 DO IA=1,NANT I = ZANT(IA,IREF) IF (I.NE.0) THEN WW = WK3(I+(J-1)*NANTM1) C(IA,J) = C(IA,J)+WW NORM = NORM+WW**2 ENDIF ENDDO ENDDO ENDDO ENDIF c c loop over data points to get rms DO IB=1, NBAS SS(IB) = 0 WSS(IB) = 0 ENDDO DO I = 1, M XI = X(I) XCAP = ((XI-X1)-(XN-XI))/D CALL CHEB (KPLUS1, XCAP, WK1, ERROR) IF (ERROR) GOTO 999 DO IB=1,NBAS IF (W(I,IB).GT.0) THEN IA = IANT(IB) JA = JANT(IB) TEST = 0 DO KN = 1, KPLUS1 WN = WK1(KN) IF (IY.EQ.1) THEN TEST = TEST+(C(IA,KN)+C(JA,KN))*WN ELSE TEST = TEST+(-C(IA,KN)+C(JA,KN))*WN ENDIF ENDDO TEST = Y(I,IB)-TEST IF (IY.EQ.2) TEST = MOD(TEST+11*PI,2*PI)-PI SS(IB) = SS(IB)+W(I,IB)*TEST**2 WSS(IB) = WSS(IB)+W(I,IB) ENDIF ENDDO ENDDO DO IB=1, NBAS IF (WSS(IB).NE.0) THEN SS(IB) = SQRT(SS(IB)/WSS(IB)) ELSE SS(IB) = 0 ENDIF ENDDO RETURN 999 ERROR = .TRUE. RETURN END * SUBROUTINE CHEB(NPLUS1, XCAP, P, ERROR) C C Compute nplus1 Che+bishev Polynomials at x = xcap C LOGICAL ERROR INTEGER NPLUS1 REAL*8 XCAP, P(NPLUS1) * REAL*8 BK, BKP1, BKP2, DK, ETA, FACTOR INTEGER N, K * c c eta is the smallest positive number such that c the computed value of 1.0 + eta exceeds unity. c with NAG, ETA = X02AAF(1.0D0) DATA ETA/1.110223024625156D-16/ * ERROR=.FALSE. IF (NPLUS1.LT.1) THEN WRITE(*,*) 'F-CHEB, nplus1.lt.1' ERROR = .TRUE. RETURN ENDIF IF (DABS(XCAP).GT.1.0D0+4.0D0*ETA) THEN WRITE(*,*) 'F-CHEB, abs(xcap).gt.1' ENDIF P(1) = 0.5D0 IF (NPLUS1.LE.1) RETURN N = NPLUS1 - 1 K = N + 2 IF (XCAP.LT.-0.5D0) THEN c c Gentleman*s modified recurrence. FACTOR = 2.0D0*(1.0D0+XCAP) DK = -1.0D0 BK = 0.0D0 DO K=1,N DK = - DK + FACTOR*BK BK = DK - BK P(K+1) = - DK + 0.5D0*FACTOR*BK ENDDO ELSEIF (XCAP.LE.0.5D0) THEN c c Clenshaw*s original recurrence. c FACTOR = 2.0D0*XCAP BKP1 = 0.0D0 BKP2 = -1.0D0 DO K=1,N BK = - BKP2 + FACTOR*BKP1 P(K+1) = - BKP1 + 0.5D0*FACTOR*BK BKP2 = BKP1 BKP1 = BK ENDDO ELSE c c Reinsch*s modified recurrence. c FACTOR = 2.0D0*(1.0D0-XCAP) DK = 1.0D0 BK = 0.0D0 DO K=1,N DK = DK - FACTOR*BK BK = BK + DK P(K+1) = DK - 0.5D0*FACTOR*BK ENDDO ENDIF P(1) = 0.5D0 RETURN END * Linear Algebra: use LAPACK routines * SUBROUTINE MTH_DPOTRF (NAME, UPLO, N, A, LDA, INFO) CHARACTER*(*) NAME, UPLO INTEGER INFO, LDA, N REAL*8 A( LDA, * ) LOGICAL ERROR * * Purpose * ======= * * DPOTRF computes the Cholesky factorization of a real symmetric * positive definite matrix A. * * The factorization has the form * A = U**T * U, if UPLO = 'U', or * A = L * L**T, if UPLO = 'L', * where U is an upper triangular matrix and L is lower triangular. * * This is the block version of the algorithm, calling Level 3 BLAS. * * Arguments * ========= * * UPLO (input) CHARACTER*1 * = 'U': Upper triangle of A is stored; * = 'L': Lower triangle of A is stored. * * N (input) INTEGER * The order of the matrix A. N >= 0. * * A (input/output) DOUBLE PRECISION array, dimension (LDA,N) * On entry, the symmetric matrix A. If UPLO = 'U', the leading * N-by-N upper triangular part of A contains the upper * triangular part of the matrix A, and the strictly lower * triangular part of A is not referenced. If UPLO = 'L', the * leading N-by-N lower triangular part of A contains the lower * triangular part of the matrix A, and the strictly upper * triangular part of A is not referenced. * * On exit, if INFO = 0, the factor U or L from the Cholesky * factorization A = U**T*U or A = L*L**T. * * LDA (input) INTEGER * The leading dimension of the array A. LDA >= max(1,N). * * INFO (output) INTEGER * = 0: successful exit * < 0: if INFO = -i, the i-th argument had an illegal value * > 0: if INFO = i, the leading minor of order i is not * positive definite, and the factorization could not be * completed. * * * Call LAPACK routine CALL DPOTRF (UPLO, N, A, LDA, INFO ) C CALL MTH_FAIL(NAME,'MTH_DPOTRF',INFO,ERROR) END SUBROUTINE MTH_DPOTRS (NAME, $UPLO, N, NRHS, A, LDA, B, LDB, INFO ) CHARACTER*(*) UPLO, NAME INTEGER INFO, LDA, LDB, N, NRHS REAL*8 A( LDA, * ), B( LDB, * ) LOGICAL ERROR * * Purpose * ======= * * DPOTRS solves a system of linear equations A*X = B with a symmetric * positive definite matrix A using the Cholesky factorization * A = U**T*U or A = L*L**T computed by DPOTRF. * * Arguments * ========= * * UPLO (input) CHARACTER*1 * = 'U': Upper triangle of A is stored; * = 'L': Lower triangle of A is stored. * * N (input) INTEGER * The order of the matrix A. N >= 0. * * NRHS (input) INTEGER * The number of right hand sides, i.e., the number of columns * of the matrix B. NRHS >= 0. * * A (input) DOUBLE PRECISION array, dimension (LDA,N) * The triangular factor U or L from the Cholesky factorization * A = U**T*U or A = L*L**T, as computed by DPOTRF. * * LDA (input) INTEGER * The leading dimension of the array A. LDA >= max(1,N). * * B (input/output) DOUBLE PRECISION array, dimension (LDB,NRHS) * On entry, the right hand side matrix B. * On exit, the solution matrix X. * * LDB (input) INTEGER * The leading dimension of the array B. LDB >= max(1,N). * * INFO (output) INTEGER * = 0: successful exit * < 0: if INFO = -i, the i-th argument had an illegal value * * Call LAPACK routine CALL DPOTRS (UPLO, N, NRHS, A, LDA, B, LDB, INFO ) C CALL MTH_FAIL(NAME,'MTH_DPOTRF',INFO,ERROR) END FUNCTION ZANT(I,R) INTEGER I, R, ZANT IF (I.EQ.R) THEN ZANT = 0 ELSEIF (I.GT.R) THEN ZANT = I-1 ELSE ZANT = I ENDIF RETURN END SUBROUTINE SPLINANT(IY, M, NBAS, IANT, JANT, IREF, $NCAP7, NANT, X, Y, W, $K, WK1, WK2, WK3, SS, C) C------------------------------------------------------------------------ C splinant computes a weighted least-squares approximation c to an arbitrary set of data points, either amplitude or phase. c with knots prescribed by the user. c parameters: C iy I Input 1 for log(amplitude), 2 for phase c m I Input the number of data points for each baseline c nbas I Input the number of baselines c iant(nbas) I Input start antenna for each baseline c jant(nbas) I Input end antenna for each baseline c iref i Input Reference antenna for phases c ncap7 I Input number of knots for splines (see e02baf) c nant I Input the number of antennas c X(m) R8 Input the data abscissae c Y(m,nbas) R8 Input the data values c W(m,mbas) R8 Input weights c k(ncap7) R8 Input knots for the splines (inners + 4 at each end) c wk1(4,m) R8 Output wk space c wk2(4*nant,ncap7*nant) c R8 Output work space c wk3(ncap7*nant) c R8 Output work space c ss(nbas) R8 Output rms of fit for each baseline c c(nant,ncap7) R8 Output the spline coefficients (ncap3 values) c C------------------------------------------------------------------------ * Dummy INTEGER M, NCAP7, NANT, NBAS, IANT(NBAS), JANT(NBAS), IY, IREF REAL*8 C(NANT,NCAP7), K(NCAP7), W(M,NBAS), WK1(4,M), $WK2(4*NANT,NCAP7*NANT), WK3(NCAP7*NANT), $X(M), Y(M,NBAS), SS(NBAS), $NORM, TOL LOGICAL ERROR PARAMETER (TOL=1E-14) INTEGER ZANT c------------------------------------------------------- REAL*8 PI PARAMETER (PI=3.14159265358979323846D0) REAL*4 PIS PARAMETER (PIS=3.141592653) * Relative precision of REAL*4 REAL*4 EPSR4 PARAMETER (EPSR4=1E-7) REAL*4 MAXR4 PARAMETER (MAXR4=1E38) * Maximum acceptable integer INTEGER MAX_INTEG PARAMETER (MAX_INTEG=2147483647) C------------------------------------------------- * Local INTEGER I, IA, IB, JJ, NANTM1, ITER, JA, IBD, ICOL, NBD, $KN, KP, J, JOLD, L, NCAP3, NCAP, NCAPM1 REAL*8 D4, D5, D6, D7, D8, D9, E2, E3, E4, E5, K1, K2, K3, $K4, K5, K6, N1, N2, N3, WI, XI, WN, WW, WWW, TEST, YI C------------------------------------------------------------------------ C Code c ERROR=.FALSE. CALL SPLINE_CHECK(M, NCAP7, X, K, WK1, ERROR) IF (ERROR) RETURN c c Check that the weights are (strictly) positive. c DO I=1,M DO IB = 1, NBAS IF (W(I,IB).LT.0.0D0) THEN WRITE(*,*)'SPLINANT','Weights not positive' ERROR = .TRUE. RETURN ENDIF ENDDO ENDDO NCAP = NCAP7 - 7 NCAPM1 = NCAP - 1 NCAP3 = NCAP + 3 NBD = 4*NANT NANTM1 = NANT-1 c c First loop on data abscissae to compute the spline values in wk1 c J = 0 JOLD = 0 DO I = 1, M c c for the data point (x(i), y(i,ib)) determine an interval c k(j + 3) .le. x .lt. k(j + 4) containing x(i). (in the c case j + 4 .eq. ncap the second equality is relaxed to c include equality). c XI = X(I) DO WHILE (XI.GE.K(J+4) .AND. J.LE.NCAPM1) J = J + 1 ENDDO IF (J.NE.JOLD) THEN c c set certain constants relating to the interval c k(j + 3) .le. x .le. k(j + 4) c (i.e. the jth non vanishing interval) c K1 = K(J+1) K2 = K(J+2) K3 = K(J+3) K4 = K(J+4) K5 = K(J+5) K6 = K(J+6) D4 = 1.0D0/(K4-K1) D5 = 1.0D0/(K5-K2) D6 = 1.0D0/(K6-K3) D7 = 1.0D0/(K4-K2) D8 = 1.0D0/(K5-K3) D9 = 1.0D0/(K4-K3) JOLD = J ENDIF c c compute and store in wk1(l,i) (l = 1, 2, 3, 4) the values c of the four normalized cubic b-splines which are non-zero at c x=x(i), i.e. the splines of indexes j, j+1, j+2, j+3. c E5 = K5 - XI E4 = K4 - XI E3 = XI - K3 E2 = XI - K2 N1 = E4*D9*D7 N2 = E3*D9*D8 N3 = E3*N2*D6 N2 = (E2*N1+E5*N2)*D5 N1 = E4*N1*D4 WK1(4,I) = E3*N3 WK1(3,I) = E2*N2 + (K6-XI)*N3 WK1(2,I) = (XI-K1)*N1 + E5*N2 WK1(1,I) = E4*N1 ENDDO c * Amplitude case is simple... c IF (IY.EQ.1) THEN NBD = 4*NANT DO I=1,NANT*NCAP3 DO L=1,NBD WK2(L,I) = 0.0D0 ENDDO WK3(I) = 0.0D0 ENDDO J = 0 DO I=1, M XI = X(I) DO WHILE (XI.GE.K(J+4) .AND. J.LE.NCAPM1) J = J + 1 ENDDO * c store the upper-triangular part of the normal equations in wk2 * * write(*,*) i,j,(wk1(kn,i),kn=1,4) DO KN=0, 3 WN = WK1(KN+1,I) DO KP = KN, 3 ICOL = (J+KP-1)*NANT IBD = NBD-(KP-KN)*NANT WW = WN*WK1(KP+1,I) DO IB=1,NBAS WI = W(I,IB) IF (WI.GT.0) THEN IA = IANT(IB) JA = JANT(IB) WWW = WI*WW WK2(IBD,ICOL+IA) = WK2(IBD,ICOL+IA)+WWW WK2(IBD,ICOL+JA) = WK2(IBD,ICOL+JA)+WWW WK2(IBD+IA-JA,ICOL+JA) = $ WK2(IBD+IA-JA,ICOL+JA) + WWW IF (KP.GT.KN) THEN WK2(IBD+JA-IA,ICOL+IA) = $ WK2(IBD+JA-IA,ICOL+IA) + WWW ENDIF ENDIF ENDDO ENDDO DO IB=1, NBAS IA = IANT(IB) JA = JANT(IB) WI = W(I,IB)*WN*Y(I,IB) JJ = (J+KN-1)*NANT WK3(IA+JJ) = WK3(IA+JJ) + WI WK3(JA+JJ) = WK3(JA+JJ) + WI ENDDO ENDDO ENDDO C C Solve the system of normal equations by first computing the Cholesky c factorization CALL MTH_DPBTRF ('SPLINANT', $ 'U',NCAP3*NANT,NBD-1,WK2,4*NANT,ERROR) IF (ERROR) RETURN CALL MTH_DPBTRS ('SPLINANT', $ 'U',NCAP3*NANT,NBD-1,1,WK2,4*NANT, $ WK3,NCAP3*NANT,ERROR) IF (ERROR) RETURN DO J=1,NCAP3 DO I=1,NANT C(I,J) = WK3(I+(J-1)*NANT) ENDDO ENDDO * Phase is more complicated ... ELSEIF (IY.EQ.2) THEN NBD = 4*NANTM1 DO I=1,NANT DO J=1,NCAP3 C(I,J) = 0.0D0 ENDDO ENDDO * start iterating NORM = 1E10 ITER = 0 DO WHILE (NORM.GT.TOL .AND. ITER.LT.100) ITER = ITER+1 DO I=1,NANTM1*NCAP3 DO L=1,NBD WK2(L,I) = 0.0D0 ENDDO WK3(I) = 0.0D0 ENDDO J = 0 I = 0 XI = X(1) DO I=1, M XI = X(I) DO WHILE (XI.GE.K(J+4) .AND. J.LE.NCAPM1) J = J + 1 ENDDO * c store the upper-triangular part of the normal equations in wk2 * DO KN=0, 3 WN = WK1(KN+1,I) DO KP = KN, 3 ICOL = (J+KP-1)*NANTM1 IBD = NBD-(KP-KN)*NANTM1 WW = WN*WK1(KP+1,I) DO IB=1,NBAS WI = W(I,IB) IF (WI.GT.0) THEN WWW = WI*WW IA = ZANT(IANT(IB),IREF) JA = ZANT(JANT(IB),IREF) IF (IA.NE.0) THEN WK2(IBD,ICOL+IA) = WK2(IBD,ICOL+IA)+WWW ENDIF IF (JA.NE.0) THEN WK2(IBD,ICOL+JA) = WK2(IBD,ICOL+JA)+WWW ENDIF IF (IA.NE.0 .AND. JA.NE.0) THEN WK2(IBD+IA-JA,ICOL+JA) = $ WK2(IBD+IA-JA,ICOL+JA) - WWW IF (KP.GT.KN) THEN WK2(IBD+JA-IA,ICOL+IA) = $ WK2(IBD+JA-IA,ICOL+IA) - WWW ENDIF ENDIF ENDIF ENDDO ENDDO ENDDO DO IB=1, NBAS IF (W(I,IB).GT.0) THEN IA = IANT(IB) JA = JANT(IB) YI = Y(I,IB) IF (J.LE.NCAP3) THEN DO KN = 0, 3 WN = WK1(KN+1,I) IF (J+KN.LE.NCAP3) THEN YI = YI+C(IA,J+KN)*WN YI = YI-C(JA,J+KN)*WN ENDIF ENDDO ELSE YI = 0 ENDIF IF (NORM.LT.1E9) THEN YI = SIN(YI) ENDIF IA = ZANT(IANT(IB),IREF) JA = ZANT(JANT(IB),IREF) DO KN = 0, 3 WN = WK1(KN+1,I) WI = W(I,IB)*WN*YI JJ = (J+KN-1)*NANTM1 IF (IA.NE.0) THEN WK3(IA+JJ) = WK3(IA+JJ) - WI ENDIF IF (JA.NE.0) THEN WK3(JA+JJ) = WK3(JA+JJ) + WI ENDIF ENDDO ENDIF ENDDO ENDDO C C Solve the system of normal equations by first computing the Cholesky c factorization CALL MTH_DPBTRF ('SPLINANT', $ 'U',NCAP3*NANTM1,NBD-1,WK2,4*NANT,ERROR) IF (ERROR) RETURN CALL MTH_DPBTRS ('SPLINANT', $ 'U',NCAP3*NANTM1,NBD-1,1,WK2,4*NANT, $ WK3,NCAP3*NANT,ERROR) IF (ERROR) RETURN * * Add the result to c: NORM = 0 DO J=1,NCAP3 DO IA=1,NANT I = ZANT(IA,IREF) IF (I.NE.0) THEN WW = WK3(I+(J-1)*NANTM1) C(IA,J) = C(IA,J)+WW NORM = NORM+WW**2 ENDIF ENDDO ENDDO ENDDO ENDIF c c loop over data points to get rms DO I=1, NBAS SS(I) = 0 WK2(I,1) = 0 ENDDO J = 0 DO I = 1, M XI = X(I) DO WHILE (XI.GE.K(J+4) .AND. J.LE.NCAPM1) J = J + 1 ENDDO DO IB=1,NBAS IF (W(I,IB).GT.0) THEN IA = IANT(IB) JA = JANT(IB) TEST = 0 DO KN = 0, 3 WN = WK1(KN+1,I) IF (IY.EQ.1) THEN TEST = TEST+(C(IA,J+KN)+C(JA,J+KN))*WN ELSE TEST = TEST+(-C(IA,J+KN)+C(JA,J+KN))*WN ENDIF ENDDO TEST = Y(I,IB)-TEST TEST = MOD(TEST+11*PI,2*PI)-PI SS(IB) = SS(IB)+W(I,IB)*TEST**2 WK2(IB,1) = WK2(IB,1)+W(I,IB) ENDIF ENDDO ENDDO DO IB=1, NBAS IF (WK2(IB,1).GT.0) THEN SS(IB) = SQRT(SS(IB)/WK2(IB,1)) ELSE SS(IB) = 0 ENDIF ENDDO * RETURN END * SUBROUTINE SPLINE_CHECK(M, NCAP7, X, K, WK, ERROR) C------------------------------------------------------------------------ C splinant computes a weighted least-squares approximation c to an arbitrary set of data points by a cubic spline c with knots prescribed by the user. C------------------------------------------------------------------------ * Dummy INTEGER M, NCAP7 REAL*8 K(NCAP7), WK(M), X(M) LOGICAL ERROR * Local INTEGER I, J, L, NCAP3, NCAP, NCAPM1, R REAL*8 K0, K4 C------------------------------------------------------------------------ C Code c c check that the values of m and ncap7 are reasonable IF (NCAP7.LT.8 .OR. M.LT.NCAP7-4) GO TO 991 NCAP = NCAP7 - 7 NCAPM1 = NCAP - 1 NCAP3 = NCAP + 3 c c In order to define the full b-spline basis, augment the c prescribed interior knots by knots of multiplicity four c at each end of the data range. c DO J=1,4 I = NCAP3 + J K(J) = X(1) K(I) = X(M) ENDDO c c test the validity of the data. c c check that the knots are ordered and are interior c to the data interval. c IF (K(5).LE.X(1) .OR. K(NCAP3).GE.X(M)) THEN WRITE(*,*)'SPLINE_CHECK',' Knots outside range' ERROR = .TRUE. RETURN ELSE DO J=4,NCAP3 IF (K(J).GT.K(J+1)) THEN WRITE(*,*)'SPLINE_CHECK','Knots non increasing' ERROR = .TRUE. RETURN ENDIF ENDDO ENDIF c c check that the data abscissae are ordered, then form the c array wk from the array x. the array wk contains c the set of distinct data abscissae. c WK(1) = X(1) J = 2 DO I=2,M IF (X(I).LT.WK(J-1)) THEN write(*,*)'SPLINE_CHECK', $ 'Data abscissae not ordered' ERROR = .TRUE. RETURN ELSEIF (X(I).GT.WK(J-1)) THEN WK(J) = X(I) J = J + 1 ENDIF ENDDO R = J - 1 c c check that there are sufficient distinct data abscissae for c the prescribed number of knots. c IF (R.LT.NCAP3) GOTO 991 c c check the first s and the last s Schoenberg-Whitney c conditions ( s = min(ncap - 1, 4) ). c DO J=1,4 IF (J.GE.NCAP) RETURN I = NCAP3 - J + 1 L = R - J + 1 IF (WK(J).GE.K(J+4) .OR. K(I).GE.WK(L)) GOTO 991 ENDDO c c check all the remaining schoenberg-whitney conditions. c IF (NCAP.GT.5) THEN R = R - 4 I = 3 DO J= 5, NCAPM1 K0 = K(J+4) K4 = K(J) DO WHILE (WK(I).LE.K4) I = I + 1 ENDDO IF (I.GT.R .OR. WK(I).GE.K0) GOTO 991 ENDDO ENDIF RETURN 991 WRITE(*,*)'SPLINE_CHECK', $'Too many knots' WRITE(*,*) 'abscissae: ',(WK(I),I=1,R) WRITE(*,*) 'knots: ',(K(I),I=1,NCAP7) ERROR = .TRUE. RETURN END SUBROUTINE MTH_DPBTRF (NAME, UPLO, N, KD, AB, LDAB, ERROR) CHARACTER*(*) UPLO, NAME INTEGER INFO, KD, LDAB, N REAL*8 AB( LDAB, * ) LOGICAL ERROR * * Purpose * ======= * * DPBTRF computes the Cholesky factorization of a real symmetric * positive definite band matrix A. * * The factorization has the form * A = U**T * U, if UPLO = 'U', or * A = L * L**T, if UPLO = 'L', * where U is an upper triangular matrix and L is lower triangular. * * Arguments * ========= * * UPLO (input) CHARACTER*1 * = 'U': Upper triangle of A is stored; * = 'L': Lower triangle of A is stored. * * N (input) INTEGER * The order of the matrix A. N >= 0. * * KD (input) INTEGER * The number of superdiagonals of the matrix A if UPLO = 'U', * or the number of subdiagonals if UPLO = 'L'. KD >= 0. * * AB (input/output) DOUBLE PRECISION array, dimension (LDAB,N) * On entry, the upper or lower triangle of the symmetric band * matrix A, stored in the first KD+1 rows of the array. The * j-th column of A is stored in the j-th column of the array AB * as follows: * if UPLO = 'U', AB(kd+1+i-j,j) = A(i,j) for max(1,j-kd)<=i<=j; * if UPLO = 'L', AB(1+i-j,j) = A(i,j) for j<=i<=min(n,j+kd). * * On exit, if INFO = 0, the triangular factor U or L from the * Cholesky factorization A = U**T*U or A = L*L**T of the band * matrix A, in the same storage format as A. * * LDAB (input) INTEGER * The leading dimension of the array AB. LDAB >= KD+1. * * INFO (output) INTEGER * = 0: successful exit * < 0: if INFO = -i, the i-th argument had an illegal value * > 0: if INFO = i, the leading minor of order i is not * positive definite, and the factorization could not be * completed. * * Further Details * =============== * * The band storage scheme is illustrated by the following example, when * N = 6, KD = 2, and UPLO = 'U': * * On entry: On exit: * * * * a13 a24 a35 a46 * * u13 u24 u35 u46 * * a12 a23 a34 a45 a56 * u12 u23 u34 u45 u56 * a11 a22 a33 a44 a55 a66 u11 u22 u33 u44 u55 u66 * * Similarly, if UPLO = 'L' the format of A is as follows: * * On entry: On exit: * * a11 a22 a33 a44 a55 a66 l11 l22 l33 l44 l55 l66 * a21 a32 a43 a54 a65 * l21 l32 l43 l54 l65 * * a31 a42 a53 a64 * * l31 l42 l53 l64 * * * * Array elements marked * are not used by the routine. * * Call LAPACK routine CALL DPBTRF (UPLO, N, KD, AB, LDAB, INFO ) if(INFO .lt. 0) then write(*,*) 'DPBTRF NOT SUCCESSFUL; INFO', INFO endif if(INFO .gt. 0) then write(*,*) 'DPBTRF ;problem leading minor ', INFO endif C CALL MTH_FAIL(NAME,'MTH_DPBTRF',INFO,ERROR) END SUBROUTINE MTH_DPBTRS (NAME, $UPLO, N, KD, NRHS, AB, LDAB, B, LDB, ERROR) CHARACTER*(*) UPLO, NAME INTEGER INFO, KD, LDAB, LDB, N, NRHS REAL*8 AB( LDAB, * ), B( LDB, * ) LOGICAL ERROR * * Purpose * ======= * * DPBTRS solves a system of linear equations A*X = B with a symmetric * positive definite band matrix A using the Cholesky factorization * A = U**T*U or A = L*L**T computed by DPBTRF. * * Arguments * ========= * * UPLO (input) CHARACTER*1 * = 'U': Upper triangular factor stored in AB; * = 'L': Lower triangular factor stored in AB. * * N (input) INTEGER * The order of the matrix A. N >= 0. * * KD (input) INTEGER * The number of superdiagonals of the matrix A if UPLO = 'U', * or the number of subdiagonals if UPLO = 'L'. KD >= 0. * * NRHS (input) INTEGER * The number of right hand sides, i.e., the number of columns * of the matrix B. NRHS >= 0. * * AB (input) DOUBLE PRECISION array, dimension (LDAB,N) * The triangular factor U or L from the Cholesky factorization * A = U**T*U or A = L*L**T of the band matrix A, stored in the * first KD+1 rows of the array. The j-th column of U or L is * stored in the j-th column of the array AB as follows: * if UPLO ='U', AB(kd+1+i-j,j) = U(i,j) for max(1,j-kd)<=i<=j; * if UPLO ='L', AB(1+i-j,j) = L(i,j) for j<=i<=min(n,j+kd). * * LDAB (input) INTEGER * The leading dimension of the array AB. LDAB >= KD+1. * * B (input/output) DOUBLE PRECISION array, dimension (LDB,NRHS) * On entry, the right hand side matrix B. * On exit, the solution matrix X. * * LDB (input) INTEGER * The leading dimension of the array B. LDB >= max(1,N). * * INFO (output) INTEGER * = 0: successful exit * < 0: if INFO = -i, the i-th argument had an illegal value * * Call LAPACK routine CALL DPBTRS (UPLO, N, KD, NRHS, AB, LDAB, B, LDB, INFO ) C CALL MTH_FAIL(NAME,'MTH_DPBTRS',INFO,ERROR) if (INFO .lt. 0) then write(*,*) 'DPBTRS NOT SUCCESSFUL; INFO', INFO endif END SUBROUTINE GETBSPL(NCAP7, K, C, X, S, IFAIL) * * Evaluates a cubic spline from its B-spline representation. * * Uses DE BOOR*s method of convex combinations. * INTEGER NCAP7, IFAIL, J, J1, L REAL*8 K(NCAP7), C(NCAP7), X, S, C1, C2, C3, E2, E3, E4, $E5,K1, K2, K3, K4, K5, K6 * * Check enough data IF (NCAP7.LT.8) THEN IFAIL = 2 RETURN ENDIF * Check in boundary IF (X.LT.K(4) .OR. X.GT.K(NCAP7-3)) THEN IFAIL = 1 S = 0.0D0 RETURN ENDIF * * Determine J such that K(J + 3) .LE. X .LE. K(J + 4). J1 = 0 J = NCAP7 - 7 DO WHILE (J-J1.GT.1) L = (J1+J)/2 IF (X.GE.K(L+4)) THEN J1 = L ELSE J = L ENDIF ENDDO * * Use the method of convex combinations to compute S(X). K1 = K(J+1) K2 = K(J+2) K3 = K(J+3) K4 = K(J+4) K5 = K(J+5) K6 = K(J+6) E2 = X - K2 E3 = X - K3 E4 = K4 - X E5 = K5 - X C2 = C(J+1) C3 = C(J+2) C1 = ((X-K1)*C2+E4*C(J))/(K4-K1) C2 = (E2*C3+E5*C2)/(K5-K2) C3 = (E3*C(J+3)+(K6-X)*C3)/(K6-K3) C1 = (E2*C2+E4*C1)/(K4-K2) C2 = (E3*C3+E5*C2)/(K5-K3) S = (E3*C2+E4*C1)/(K4-K3) IFAIL = 0 END SUBROUTINE AMPLIANT(ANT1,ANT2,NANT,NBAS,BD,WBD,AD,WAD,ERROR, $WK2,WK3) INTEGER ERROR INTEGER NANT, NBAS INTEGER ANT1(NBAS), ANT2(NBAS) REAL*8 BD(NBAS), WBD(NBAS), AD(NANT), WAD(NANT), WB, YI, WW REAL*8 WK2(NANT, NANT), WK3(NBAS) INTEGER IB, IA, JA, NANTM1 NANTM1=NANT-1 * DO IA=1, NANT DO JA=1, NANT WK2(IA,JA) = 0 ENDDO WK3(IA) = 0 AD(IA) = 0 ENDDO DO IB = 1, NBAS WB = WBD(IB) IF (WB.GT.0) THEN IA = ANT1(IB) JA = ANT2(IB) YI = BD(IB) - (AD(JA)+AD(IA)) WK3(IA) = WK3(IA) + WB*YI WK3(JA) = WK3(JA) + WB*YI WK2(IA,IA) = WK2(IA,IA) + WB WK2(JA,JA) = WK2(JA,JA) + WB WK2(IA,JA) = WK2(IA,JA) + WB WK2(JA,IA) = WK2(JA,IA) + WB ENDIF ENDDO CALL MTH_DPOTRF ('AMPLI_ANT','U',NANT,WK2,NANT,ERROR) IF (ERROR.GT.0 .OR. ERROR.LT.0)THEN WRITE(*,*)'AMPLIANT: DPOTRF RETURNS ', ERROR ENDIF CALL MTH_DPOTRS ('AMPLI_ANT', $'U',NANT,1,WK2,NANT,WK3,NANT,ERROR) IF (ERROR.GT.0 .OR. ERROR.LT.0) THEN WRITE(*,*)'AMPLIANT: DPOTRF RETURNS ', ERROR ENDIF * Add the result to ad: DO IA=1,NANT WW = WK3(IA) AD(IA) = AD(IA) + WW ENDDO RETURN END SUBROUTINE PHASEANT(ANT1,ANT2,NANT,NBAS,BD,WBD,AD,WAD,ERROR, WK2, $ WK3) INTEGER ERROR INTEGER NANT, NBAS INTEGER ANT1(NBAS), ANT2(NBAS) REAL*8 BD(NBAS), WBD(NBAS), AD(NANT), WAD(NANT), WB, YI, WW REAL*8 WK2(NANT, NANT), WK3(NBAS), NORM INTEGER IB, IA, IR, NANTM1, JA, I, ZANT * NORM = 1E10 NANTM1 = NANT - 1 IR = 1 DO IA=1, NANT AD(IA) = 0 ENDDO DO WHILE (NORM.GT.1E-10) DO IA=1, NANT DO JA=1, NANT WK2(IA,JA) = 0 ENDDO WK3(IA) = 0 ENDDO DO IB = 1, NBAS WB = WBD(IB) IF (WB.GT.0) THEN IA = ANT1(IB) JA = ANT2(IB) YI = SIN(BD(IB) - (AD(JA)-AD(IA))) IA = ZANT(IA,IR) JA = ZANT(JA,IR) IF (IA.NE.0) THEN WK2(IA,IA) = WK2(IA,IA) + WB WK3(IA) = WK3(IA) - WB*YI ENDIF IF (JA.NE.0) THEN WK2(JA,JA) = WK2(JA,JA) + WB WK3(JA) = WK3(JA) + WB*YI ENDIF IF (IA.NE.0 .AND. JA.NE.0) THEN WK2(IA,JA) = WK2(IA,JA) - WB WK2(JA,IA) = WK2(JA,IA) - WB ENDIF ENDIF ENDDO CALL MTH_DPOTRF ('PHASE_ANT','U',NANTM1,WK2,NANT,ERROR) IF (ERROR .NE. 0) THEN WRITE(*,*) 'PHASEANT ERROR IN DPOTRF ',ERROR ENDIF CALL MTH_DPOTRS ('PHASE_ANT', $ 'U',NANTM1,1,WK2,NANT,WK3,NANTM1,ERROR) IF (ERROR .NE. 0) THEN WRITE(*,*) 'PHASEANT ERROR IN DPOTRS ',ERROR ENDIF * Add the result to ad: NORM = 0 DO IA=1,NANT I = ZANT(IA,IR) IF (I.NE.0) THEN WW = WK3(I) AD(IA) = AD(IA) + WW NORM = NORM + WW**2 ENDIF ENDDO ENDDO RETURN END SUBROUTINE ANTGAIN (Z,W, IANT, JANT, ZANT,WANT, NANT, NBAS, $ REF_ANT) C------------------------------------------------------------------------ C CLIC C Derive antenna "gains" from baseline visibilities C Arguments: C Z(NBAS) COMPLEX Visibility C W(NBAS) REAL Weight C IANT(NBAS) INTEGER antenna1 for baseline C JANT(NBAS) INTEGER antenna2 for baseline C ZANT(NANT) COMPLEX Complex antenna gain C WANT(NANT) REAL Weight C NANT INTEGER number of antennas C NBAS INTEGER number of baselines C REF_ANT INTEGER reference antenna C------------------------------------------------------------------------ * Dummy variables: REAL*4 W(2016), WANT(64) COMPLEX Z(2016), ZANT(64), CMPL2 INTEGER REF_ANT, IANT(2016), JANT(2016) * Local variables: REAL*4 PHA(64), AMP(64), AA, FAZ, WA, ADD, AJI, AKI, AJK, $PHA0(64), C(2016) INTEGER IB, IA, J_I, K_I, J_K, JA, KA, BASE, IREF, ITRY, I LOGICAL RETRO, REFOK PARAMETER (RETRO=.FALSE.) *------------------------------------------------------------------------ * Code: * * Solve for phases, using retroprojection algorithm: * REFOK = .FALSE. IREF = REF_ANT ITRY = 1 DO WHILE (.NOT.REFOK .AND. ITRY.LE.NANT) DO IA=1, NANT IF (IA.LT.IREF) THEN IB = BASE(IA,IREF) REFOK = REFOK .OR. W(IB).GT.0 ELSEIF (IA.GT.IREF) THEN IB = BASE(IREF,IA) REFOK = REFOK .OR. W(IB).GT.0 ENDIF ENDDO IF (.NOT.REFOK) THEN ITRY = ITRY+1 IREF = MOD(IREF,NANT)+1 ENDIF ENDDO IF (.NOT.REFOK) THEN DO I=1, NANT PHA(I) = 0 ENDDO ELSE * * PHA0(IREF) = 0. DO IA=1, NANT IF (IA.LT.IREF) THEN IB = BASE(IA,IREF) IF (W(IB).GT.0) PHA0(IA) = -FAZ(Z(IB)) ELSEIF (IA.GT.IREF) THEN IB = BASE(IREF,IA) IF (W(IB).GT.0) PHA0(IA) = FAZ(Z(IB)) ENDIF ENDDO DO IA = 1, NANT ADD = 0 DO JA = 1, NANT IF (JA.LT.IA) THEN IB = BASE(JA,IA) IF (W(IB).GT.0) THEN ADD = ADD + FAZ(Z(IB)) - PHA0(IA) + PHA0(JA) ENDIF ELSEIF (JA.GT.IA) THEN IB = BASE(IA,JA) IF (W(IB).GT.0) THEN ADD = ADD - FAZ(Z(IB)) - PHA0(IA) + PHA0(JA) ENDIF ENDIF ENDDO ADD = MOD(ADD+31D0*PI,2D0*PI)-PI PHA(IA) = PHA0(IA)+ADD/NANT ENDDO ENDIF * * solve for amplitudes, using retroprojection algorithm: c (to be checked again) * note: * to compute this way the weights must be provided by antenna, * not by baseline. (RL 2002-02-27) IF (RETRO) THEN DO IA = 1, NANT WANT(IA) = 0 AMP(IA) = 0 DO IB=1, NBAS IF (W(IB).GT.0) THEN IF (IANT(IB).EQ.IA .OR. JANT(IB).EQ.IA) THEN AMP(IA) = AMP(IA)+LOG(ABS(Z(IB)))*WA WANT(IA) = WANT(IA)+WA ELSE AMP(IA) = AMP(IA)-LOG(ABS(Z(IB)))*WA/(NANT-2) WANT(IA) = WANT(IA)-WA/(NANT-2) ENDIF ENDIF ENDDO IF (WANT(IA).GT.0) THEN AMP(IA) = AMP(IA)/WANT(IA) ELSE AMP(IA) = 0. ENDIF IF (R_NANT.GT.1) THEN AMP(IA) = AMP(IA)/(NANT-1) WANT(IA) = WANT(IA)/(NANT-1) ENDIF ZANT(IA) = EXP(CMPLX(AMP(IA),PHA(IA))) c if (want(ia).gt.0) WANT(IA) = EXP(WANT(IA)) ENDDO ELSE * * Solve for amplitudes DO IA = 1, NANT WANT(IA) = 0 AMP(IA) = 0 IF (NANT.GT.2) THEN DO JA = 1, NANT IF (JA.NE.IA .AND. JA.LT.NANT) THEN DO KA = JA+1, NANT IF (KA.NE.IA) THEN J_I = BASE(MIN(JA,IA),MAX(JA,IA)) K_I = BASE(MIN(KA,IA),MAX(KA,IA)) J_K = BASE(JA,KA) IF (Z(J_K).NE.0 .AND. Z(J_I).NE.0 .AND. $ Z(K_I).NE.0 .AND. W(J_K).NE.0 .AND. $ W(J_I).NE.0 .AND. W(K_I).NE.0) THEN AJI = ABS(Z(J_I)) AKI = ABS(Z(K_I)) AJK = ABS(Z(J_K)) IF (AJI.LT.1E15 .AND. AKI.LT.1E15 $ .AND. AJK.LT.1E15) THEN AA = AJI*AKI/AJK WA = 1./W(J_I)/ABS(Z(J_I))**2 $ +1./W(K_I)/ABS(Z(K_I))**2 $ +1./W(J_K)/ABS(Z(J_K))**2 WA = 1/AA**2/WA AMP(IA) = AMP(IA) + AA*WA WANT(IA) = WANT(IA) + WA ENDIF ENDIF ENDIF ENDDO ENDIF ENDDO IF (WANT(IA).NE.0) AMP(IA) = AMP(IA) / WANT(IA) ENDIF * * if previous algorithm did not work, take first valid baseline * containing IA. This will work if there is only one operational baseline ... IF (WANT(IA).LE.0) THEN DO IB=1, NBAS IF (W(IB).GT.0) THEN AMP(IA) = ABS(Z(IB)) WANT(IA) = W(IB) ENDIF ENDDO ENDIF ZANT(IA) = AMP(IA) * EXP(CMPLX(0.,PHA(IA))) c c ZANT(IA) =CMPL2(AMP(IA), PHA(IA)) c if (amp(ia).GT.BLANK4-D_BLANK4) want(ia) = 0 ENDDO ENDIF RETURN END FUNCTION BASE(I,J) C---------------------------------------------------------------------- C Returns the number of baseline I,J (not oriented) C---------------------------------------------------------------------- * Global variables: * Dummy variables: INTEGER BASE,I,J * Local variables: *------------------------------------------------------------------------ * Code: IF (I.LT.J) THEN BASE = (J-1)*(J-2)/2 + I ELSE BASE = (I-1)*(I-2)/2 + J ENDIF END FUNCTION FAZ(Z) C------------------------------------------------------------------------ C Compute the phase of a complex number Z C------------------------------------------------------------------------ * Dummy variables: COMPLEX Z REAL FAZ COMPLEX BLANKC PARAMETER (BLANKC=(1.23456E34,1.23456E34)) REAL*4 BLANK4 PARAMETER (BLANK4=1.23456E34) *------------------------------------------------------------------------ * Code: IF (Z.NE.0 .AND. Z.NE.BLANKC) THEN FAZ = ATAN2(AIMAG(Z),REAL(Z)) ELSE FAZ = BLANK4 ENDIF RETURN END casacore-3.7.1/scimath_f/phasol.f000066400000000000000000000161321476623553700167210ustar00rootroot00000000000000*======================================================================= * Copyright (C) 1999,2001 * Associated Universities, Inc. Washington DC, USA. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, * MA 02139, USA. * * Correspondence concerning AIPS++ should be addressed as follows: * Internet email: casa-feedback@nrao.edu. * Postal address: AIPS++ Project Office * National Radio Astronomy Observatory * 520 Edgemont Road * Charlottesville, VA 22903-2475 USA * * $Id$ *----------------------------------------------------------------------- c c This code has been extracted from MIRIAD's selfcal.for routine, c originally written by Bob Sault. Also note that the BIMA and ATNF c versions of MIRIAD have slightly different versions of selfcal.for c (but not these two subroutines) and derivate programs, such as c BIMA's mselfcal.for and gmake.for c subroutine phasol(Nblines,NAnts,Sum,SumVM,b1,b2,Gain,Convrg) c implicit none logical Convrg integer Nblines,Nants integer b1(NBlines),b2(NBlines) complex SumVM(Nblines),Gain(NAnts),Sum(NAnts) c c Solve for the phase corrections which minimise the error. This uses c a nonlinear Jacobi iteration, as suggested by Fred Schwab in "Adaptive c calibration of radio interferomter data", SPIE Vol. 231, 1980 c International Optical Computing Conference (pp 18-24). The damping c heuristics are copied from AIPS ASCAL. c c Input: c NBlines Number of baselines. c Nants Number of antennae. c b1,b2 This gives the antennae pair for each baseline. c SumVM Sum of Model*conjg(Vis) c Scratch: c Sum c Output: c Convrg If .true., then the algorithm converged. c Gain The antenna gain solution. c------------------------------------------------------------------------ integer MaxIter real Epsi,Epsi2 parameter(MaxIter=100,Epsi=1.e-8,Epsi2=1.e-4) c real Factor,Change, absSum complex Temp integer Niter,i, count c c Initialise. c do i=1,NAnts Gain(i) = (1.,0.) Sum(i) = (0.,0.) enddo c Factor = 0.8 if(Nants.le.6)Factor = 0.5 c c Iterate. c Convrg = .false. Niter = 0 do while(.not.Convrg.and.Niter.lt.MaxIter) Niter = Niter + 1 c c Sum the contributions over the baselines. Note that the following c loop has a dependency. c do i=1,nblines Sum(b1(i)) = Sum(b1(i)) + Gain(b2(i)) * SumVM(i) Sum(b2(i)) = Sum(b2(i)) + Gain(b1(i)) * conjg(SumVM(i)) enddo c c Update the gains. The following will be flagged as a short loop c on the Cray, if we assume we have fewer than 32 antennae. Hopefully c this will vectorise. For "typical" cases, the absolute value function c in the next loop takes up about 30-40% of the run time of this routine c on a VAX. c Change = 0 count = 0 c#maxloop 32 do i=1,nants absSum = abs(Sum(i)) if (absSum.gt.0.0) then Temp = ( Sum(i)/absSum ) Temp = Gain(i) + Factor * ( Temp - Gain(i) ) Temp = Temp/abs(Temp) Change = Change + real(Gain(i)-Temp)**2 * + aimag(Gain(i)-Temp)**2 Gain(i) = Temp count = count + 1 endif Sum(i) = (0.,0.) enddo Convrg = Change/count .lt. Epsi enddo Convrg = Change/count .lt. Epsi2 end c************************************************************************ subroutine amphasol(NBlines,NAnts,Sum,Sum2,SumVM,SumVV, * b1,b2,gain,convrg) c implicit none logical Convrg integer NBlines,NAnts integer B1(NBlines),B2(NBlines) complex SumVM(NBlines),Gain(NAnts),Sum(NAnts) real SumVV(NBlines),Sum2(NAnts) c c Solve for the amplitudes and phase corrections which minimise c error. Algorithm is again Schwab's Jacobi iteration. The damping c heuristics are copied from AIPS ASCAL or dreamed up by me (as was the c initial gain estimate). c c Input: c NBlines Number of baselines. c Nants Number of antennae. c b1,b2 This gives the antennae pair for each baseline. c SumVM Sum of Model*conjg(Vis), for each baseline. c SumVV Sum of Vis*conjg(Vis), for each baseline. c Scratch: c Sum c Output: c Convrg If .true., then the algorithm converged. c Gain The antenna gain solution. c c------------------------------------------------------------------------ integer maxiter real Epsi,Epsi2 parameter(maxiter=100,Epsi=1.e-8,Epsi2=1e-4) integer i,Niter real Factor,Change,SumRVV,SumWt,SumRVM complex Temp real Factors(11) data Factors/0.5,0.75,8*0.9,0.5/ c c Calculate initial phase gain estimates. c call phasol(NBlines,Nants,Sum,SumVM,b1,b2,gain,convrg) if(.not.convrg)return c c Get an initial approximation of the gain solution. This finds a single c real gain which minimises the error. This helps stablise the algorithm c when the gain solution is very different from 1 (e.g. when we are c calculating a priori calibration factors). c SumRVM = 0 SumRVV = 0 do i=1,NBlines SumRVM = SumRVM + conjg(gain(b1(i)))*gain(b2(i))*SumVM(i) SumRVV = SumRVV + SumVV(i) enddo Factor = sqrt(abs(SumRVM / SumRVV)) c c Ready for the amplitude/phase solution. c do i=1,NAnts Gain(i) = Factor * Gain(i) Sum(i) = 0 Sum2(i) = 0. enddo c c Iterate. c Convrg = .false. Niter = 0 do while(.not.Convrg.and.Niter.lt.MaxIter) Niter = Niter + 1 c c Set the damping constant. I do not think this is really necessary. c AIPS does it. c if(Nants.le.6)then Factor = 0.5 else Factor = Factors(min(11,Niter)) endif c c Sum the contributions over the baselines. Note that the following c loop has a dependency. c do i=1,nblines Sum(b1(i)) = Sum(b1(i)) + Gain(b2(i)) * SumVM(i) Sum(b2(i)) = Sum(b2(i)) + Gain(b1(i)) * conjg(SumVM(i)) Sum2(b1(i)) = Sum2(b1(i)) + * (real(Gain(b2(i)))**2 + aimag(Gain(b2(i)))**2) * SumVV(i) Sum2(b2(i)) = Sum2(b2(i)) + * (real(Gain(b1(i)))**2 + aimag(Gain(b1(i)))**2) * SumVV(i) enddo c c Update the gains. The following should be flagged as a short loop c on the Cray, if we assume we have fewer than 32 antennae. Hopefully c this will vectorise. c Change = 0 SumWt = 0 c#maxloop 32 do i=1,nants if (Sum2(i).gt.0) then Temp = Sum(i)/Sum2(i) - Gain(i) Gain(i) = Gain(i) + Factor * Temp Change = Change + (real(Temp)**2 + aimag(Temp)**2) SumWt = SumWt + (real(Gain(i))**2 + aimag(Gain(i))**2) endif Sum(i) = (0.,0.) Sum2(i) = 0. enddo Convrg = Change/SumWt .lt. Epsi enddo Convrg = Change/SumWt .lt. Epsi2 end casacore-3.7.1/scimath_f/subcom.f000066400000000000000000000174601476623553700167300ustar00rootroot00000000000000*----------------------------------------------------------------------- * SUBCOM: Subtract a component from a list of pixels *----------------------------------------------------------------------- * * Copyright (C) 1997 * Associated Universities, Inc. Washington DC, USA. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, * MA 02139, USA. * * Correspondence concerning AIPS++ should be addressed as follows: * Internet email: casa-feedback@nrao.edu. * Postal address: AIPS++ Project Office * National Radio Astronomy Observatory * 520 Edgemont Road * Charlottesville, VA 22903-2475 USA * * $Id$ * *----------------------------------------------------------------------- * * SUBCOM contains a set of functions for subtracting a component * (defined as a pixel with specified amplitude and position), from * a list of components after convolving the component with a two * dimensional point spread function. * * This is very specialised function that is at the core of the * Clark clean algorithm and is used by the ClarkCleanModel class. * * It contains a number of functions for subtracting components with * 1, 2 or 4 polarizations. While there is no intrinsic difference * between these three functions separate functions where written to * avoid an otherwise small inner loop over all the polarizations. * I hope this is more efficient. * * The following subroutines are found here: * SUBCOMF : for one polarisation and REAL arrays * SUBCOM2F: for two polarisations (I & V) and REAL arrays * SUBCOM4F: for four polarisations (I, Q, U & V) and REAL arrays * * It should be trivial to clone these routines for any other data * types, in particular double precision. * * The following comments apply to all subroutines found here: * * The general form of the subroutine call is (x is replaced by * a character indicating the type: F for Float, D for Double, etc.), * and p is a number indicating the number of polarizations that are * required. (if p=1 it is dropped entirely) * * SUBROUTINE SUBCOMpx(PIXVAL, PIXPOS, NPIX, MAXPIX, MAXPOS, PSF, NX, NY) * * Given: * NPIX I The number of components in the list * PIXVAL(p,NPIX) * x The amplitude of each component in the list * PIXPOS(2,NPIX) * I The position of each component in the list * MAXVAL(p) * x The amplitude of the maximum pixel * MAXPOS(2) * I The position of the maximum pixel * NX, NY I The size of the two dimensional point spread function * PSF(NX,NY) * x Data array. Note that if p.NE.1 then NPIX is * NOT the total size of the ARR array * * return: * PIXVAL(p,NPIX) * x The amplitude of each component in the list * after the maximum has been subtracted * * Notes: * 1) It is assumed that the "origin" of the psf is at (NX,NY)/2 + 1 * At this point the psf should have a value of one. For a * 128 by 128 psf this will be at 65, 65 and for a 5 by 5 psf * it will be at 3, 3 (where the first element is at 1,1). This * is relatively simple to change. * * 2) A series of nested IF's is used rather than a single * IF (expr1).AND.(expr2).AND ... for effeciency * It means that the as soon as one expression returns false * the component will bypassed. * *----------------------------------------------------------------------- SUBROUTINE SUBCOMF(PIXVAL, PIXPOS, NPIX, MAXPIX, MAXPOS, * PSF, NX, NY) * Subtract a component (after convolving with the psf) from a list * of pixels in an array of REAL numbers INTEGER NPIX, NX, NY, PIXPOS(2, NPIX), MAXPOS(2) REAL MAXPIX, PIXVAL(NPIX), PSF(NX, NY) INTEGER N, POSX, POSY, IX, IY REAL PSFVAL *----------------------------------------------------------------------- POSX = NX/2 + 1 - MAXPOS(1) POSY = NY/2 + 1 - MAXPOS(2) DO 10 N = 1, NPIX IX = PIXPOS(1, N) + POSX IF (IX.GE.1) THEN IF (IX.LE.NX) THEN IY = PIXPOS(2, N) + POSY IF (IY.GE.1) THEN IF (IY.LE.NY) THEN PSFVAL = PSF(IX,IY) PIXVAL(N) = PIXVAL(N) - MAXPIX*PSFVAL END IF END IF END IF END IF 10 CONTINUE RETURN END *----------------------------------------------------------------------- SUBROUTINE SUBCOM2F(PIXVAL, PIXPOS, NPIX, MAXPIX, MAXPOS, * PSF, NX, NY) * Subtract a component (after convolving with the psf) from a list * of pixels in an array of REAL numbers with 2-Polarizations (I & V) INTEGER NPIX, NX, NY, PIXPOS(2, NPIX), MAXPOS(2) REAL MAXPIX(2), PIXVAL(2, NPIX), PSF(NX, NY) INTEGER N, POSX, POSY, IX, IY REAL PSFVAL, IMAX, VMAX *----------------------------------------------------------------------- POSX = NX/2 + 1 - MAXPOS(1) POSY = NY/2 + 1 - MAXPOS(2) IMAX = MAXPIX(1) VMAX = MAXPIX(2) DO 10 N = 1, NPIX IX = PIXPOS(1, N) + POSX IF (IX.GE.1) THEN IF (IX.LE.NX) THEN IY = PIXPOS(2, N) + POSY IF (IY.GE.1) THEN IF (IY.LE.NY) THEN PSFVAL = PSF(IX,IY) PIXVAL(1, N) = PIXVAL(1, N) - IMAX*PSFVAL PIXVAL(2, N) = PIXVAL(2, N) - VMAX*PSFVAL END IF END IF END IF END IF 10 CONTINUE RETURN END *----------------------------------------------------------------------- SUBROUTINE SUBCOM4F(PIXVAL, PIXPOS, NPIX, MAXPIX, MAXPOS, * PSF, NX, NY) * Subtract a component (after convolving with the psf) from a list * of pixels in an array of REAL numbers with 4-Polarizations * (I, Q, U & V) INTEGER NPIX, NX, NY, PIXPOS(2, NPIX), MAXPOS(2) REAL MAXPIX(4), PIXVAL(4, NPIX), PSF(NX, NY) INTEGER N, POSX, POSY, IX, IY REAL PSFVAL, IMAX, QMAX, UMAX, VMAX *----------------------------------------------------------------------- POSX = NX/2 + 1 - MAXPOS(1) POSY = NY/2 + 1 - MAXPOS(2) IMAX = MAXPIX(1) QMAX = MAXPIX(2) UMAX = MAXPIX(3) VMAX = MAXPIX(4) DO 10 N = 1, NPIX IX = PIXPOS(1, N) + POSX IF (IX.GE.1) THEN IF (IX.LE.NX) THEN IY = PIXPOS(2, N) + POSY IF (IY.GE.1) THEN IF (IY.LE.NY) THEN PSFVAL = PSF(IX,IY) PIXVAL(1, N) = PIXVAL(1, N) - IMAX*PSFVAL PIXVAL(2, N) = PIXVAL(2, N) - QMAX*PSFVAL PIXVAL(3, N) = PIXVAL(3, N) - UMAX*PSFVAL PIXVAL(4, N) = PIXVAL(4, N) - VMAX*PSFVAL END IF END IF END IF END IF 10 CONTINUE RETURN END casacore-3.7.1/scimath_f/vvroutines.f000066400000000000000000000504001476623553700176530ustar00rootroot00000000000000*----------------------------------------------------------------------- * vvroutines.f : Collection of fortran routines used by VanVleck class. *----------------------------------------------------------------------- * * Copyright (C) 2002 * Associated Universities, Inc. Washington DC, USA. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, * MA 02139, USA. * * Correspondence concerning AIPS++ should be addressed as follows: * Internet email: casa-feedback@nrao.edu. * Postal address: AIPS++ Project Office * National Radio Astronomy Observatory * 520 Edgemont Road * Charlottesville, VA 22903-2475 USA * * $Id$ * *----------------------------------------------------------------------- * * vvroutines contains a collection of subroutines used by the VanVleck * class for the special-case of equi-spaced quantizer input voltage * thresholds and equi-spaced output levels for the 3-level x 3-level * and 9-level x 9-level sampling. These are taken from Appendix B. * of Fred Schwab's memo on the subject (eventually this will be a * GBT memo - when it is, the correct citation should be inserted here). * * The following subroutines are found here: * vvr3 : for the 3x3 level Van Vleck curve * vv3auto : for the 3x3 auto-correlation case (x==y) * vv3zmean: for the 3x3 zero-mean case (mux=muy=0.0) * vv3zauto: for the 3x3 zero-mean, auto correlation case * vvr9 : for the 9x9 level Van Vleck curve * vv9auto : for the 9x9 auto-correlation case (x==y) * vv9zmean: for the 9x9 zero-mean case (mux=muy=0.0) * vv9zauto: for the 9x9 zero-mean, auto correlation case * BVND : bivariate normal integrals * PHID : used by BVND *----------------------------------------------------------------------- * vvr3 : the Van Vleck curve for the 3x3 level case given * mux, muy : the mean input levels * v1x, v1y : the first positive input thresholds * rho : the observed correlation * mux, muy, v1x, v1y are in units of the respective r.m.s. * input levels. *----------------------------------------------------------------------- double precision function vvr3(mux,muy,v1x,v1y,rho) double precision mux,muy,v1x,v1y,rho,L,h,k,r,bvnd,rt2 L(h,k,r)=bvnd(h,k,r) rt2=sqrt(2d0) vvr3=.5d0*(erf((-mux+v1x)/rt2)-erf((mux+v1x)/rt2)+ & erf((-muy+v1y)/rt2)-erf((muy+v1y)/rt2))+ & L(-mux-v1x,-muy-v1y,rho)+L(-mux-v1x,-muy+v1y,rho)+ & L(-mux+v1x,-muy-v1y,rho)+L(-mux+v1x,-muy+v1y,rho)-1d0 return end *----------------------------------------------------------------------- * vvr3auto : the Van Vleck curve for the 3x3 level case given * mux : the mean input level (x and y are the same here) * v1x : the first positive input threshold * rho : the observed correlation * mux, v1x, are in units of the r.m.s.input levels. *----------------------------------------------------------------------- double precision function vvr3auto(mux,v1x,rho) double precision mux,v1x,rho,L,h,k,r,bvnd,rt2 L(h,k,r)=bvnd(h,k,r) rt2=sqrt(2d0) vvr3auto=erf((-mux+v1x)/rt2)-erf((mux+v1x)/rt2)+ & L(-mux-v1x,-mux-v1x,rho)+ & 2d0 * L(-mux-v1x,-mux+v1x,rho)+ & L(-mux+v1x,-mux+v1x,rho)- & 1d0 return end *----------------------------------------------------------------------- * vvr3zmean : the Van Vleck curve for the 3x3 level case given * v1x, v1y : the first positive input thresholds (zero mean) * rho : the observed correlation * v1x, v1y are in units of the respective r.m.s. input levels. *----------------------------------------------------------------------- double precision function vvr3zmean(v1x,v1y,rho) double precision v1x,v1y,rho,L,h,k,r,bvnd L(h,k,r)=bvnd(h,k,r) vvr3zmean=L(-v1x,-v1y,rho)+L(-v1x,v1y,rho)+ & L(v1x,-v1y,rho)+L(v1x,v1y,rho)-1d0 return end *----------------------------------------------------------------------- * vvr3zauto : the Van Vleck curve for the 3x3 level case given * v : the first positive input threshold (zero mean, x==y) * rho : the observed correlation * v is in units of the r.m.s. input levels. *----------------------------------------------------------------------- double precision function vvr3zauto(v,rho) double precision v,rho,L,h,k,r,bvnd L(h,k,r)=bvnd(h,k,r) vvr3zauto=L(-v,-v,rho)+L(-v,v,rho)+ & L(v,-v,rho)+L(v,v,rho)-1d0 return end *----------------------------------------------------------------------- * vvr9 : the Van Vleck curve for the 9x9 level case given * mux, muy : the mean input levels * v1x, v1y : the first positive input thresholds * rho : the observed correlation * mux, muy, v1x, v1y are in units of the respective r.m.s. * input levels. *----------------------------------------------------------------------- double precision function vvr9(mux,muy,v1x,v1y,rho) double precision mux,muy,v1x,v1y,rho,L,h,k,r,bvnd,rt2 L(h,k,r)=bvnd(h,k,r) rt2=sqrt(2d0) vvr9=-16d0+2d0*(-erf((mux-7*v1x)/rt2)-erf((mux-5*v1x)/rt2)- & erf((mux-3*v1x)/rt2)+erf((-mux+v1x)/rt2)- & erf((mux+v1x)/rt2)-erf((mux+3*v1x)/rt2)- & erf((mux+5*v1x)/rt2)-erf((mux+7*v1x)/rt2)- & erf((muy-7*v1y)/rt2)-erf((muy-5*v1y)/rt2)- & erf((muy-3*v1y)/rt2)+erf((-muy+v1y)/rt2)- & erf((muy+v1y)/rt2)-erf((muy+3*v1y)/rt2)- & erf((muy+5*v1y)/rt2)-erf((muy+7*v1y)/rt2))+ & L(-mux-7*v1x,-muy-7*v1y,rho)+L(-mux-7*v1x,-muy-5*v1y,rho)+ & L(-mux-7*v1x,-muy-3*v1y,rho)+L(-mux-7*v1x,-muy-v1y,rho)+ & L(-mux-7*v1x,-muy+v1y,rho)+L(-mux-7*v1x,-muy+3*v1y,rho)+ & L(-mux-7*v1x,-muy+5*v1y,rho)+L(-mux-7*v1x,-muy+7*v1y,rho)+ & L(-mux-5*v1x,-muy-7*v1y,rho)+L(-mux-5*v1x,-muy-5*v1y,rho)+ & L(-mux-5*v1x,-muy-3*v1y,rho)+L(-mux-5*v1x,-muy-v1y,rho)+ & L(-mux-5*v1x,-muy+v1y,rho)+L(-mux-5*v1x,-muy+3*v1y,rho)+ & L(-mux-5*v1x,-muy+5*v1y,rho)+L(-mux-5*v1x,-muy+7*v1y,rho)+ & L(-mux-3*v1x,-muy-7*v1y,rho)+L(-mux-3*v1x,-muy-5*v1y,rho)+ & L(-mux-3*v1x,-muy-3*v1y,rho)+L(-mux-3*v1x,-muy-v1y,rho)+ & L(-mux-3*v1x,-muy+v1y,rho)+L(-mux-3*v1x,-muy+3*v1y,rho)+ & L(-mux-3*v1x,-muy+5*v1y,rho)+L(-mux-3*v1x,-muy+7*v1y,rho)+ & L(-mux-v1x,-muy-7*v1y,rho)+L(-mux-v1x,-muy-5*v1y,rho)+ & L(-mux-v1x,-muy-3*v1y,rho)+L(-mux-v1x,-muy-v1y,rho)+ & L(-mux-v1x,-muy+v1y,rho)+L(-mux-v1x,-muy+3*v1y,rho)+ & L(-mux-v1x,-muy+5*v1y,rho)+L(-mux-v1x,-muy+7*v1y,rho)+ & L(-mux+v1x,-muy-7*v1y,rho)+L(-mux+v1x,-muy-5*v1y,rho)+ & L(-mux+v1x,-muy-3*v1y,rho)+L(-mux+v1x,-muy-v1y,rho)+ & L(-mux+v1x,-muy+v1y,rho)+L(-mux+v1x,-muy+3*v1y,rho)+ & L(-mux+v1x,-muy+5*v1y,rho)+L(-mux+v1x,-muy+7*v1y,rho)+ & L(-mux+3*v1x,-muy-7*v1y,rho)+L(-mux+3*v1x,-muy-5*v1y,rho)+ & L(-mux+3*v1x,-muy-3*v1y,rho)+L(-mux+3*v1x,-muy-v1y,rho)+ & L(-mux+3*v1x,-muy+v1y,rho)+L(-mux+3*v1x,-muy+3*v1y,rho)+ & L(-mux+3*v1x,-muy+5*v1y,rho)+L(-mux+3*v1x,-muy+7*v1y,rho)+ & L(-mux+5*v1x,-muy-7*v1y,rho)+L(-mux+5*v1x,-muy-5*v1y,rho)+ & L(-mux+5*v1x,-muy-3*v1y,rho)+L(-mux+5*v1x,-muy-v1y,rho)+ & L(-mux+5*v1x,-muy+v1y,rho)+L(-mux+5*v1x,-muy+3*v1y,rho)+ & L(-mux+5*v1x,-muy+5*v1y,rho)+L(-mux+5*v1x,-muy+7*v1y,rho)+ & L(-mux+7*v1x,-muy-7*v1y,rho)+L(-mux+7*v1x,-muy-5*v1y,rho)+ & L(-mux+7*v1x,-muy-3*v1y,rho)+L(-mux+7*v1x,-muy-v1y,rho)+ & L(-mux+7*v1x,-muy+v1y,rho)+L(-mux+7*v1x,-muy+3*v1y,rho)+ & L(-mux+7*v1x,-muy+5*v1y,rho)+L(-mux+7*v1x,-muy+7*v1y,rho) return end *----------------------------------------------------------------------- * vvr9auto : the Van Vleck curve for the 9x9 level case given * mux : the mean input levels (x and y are the same here) * v1x : the first positive input threshold * rho : the observed correlation * mux, v1x, are in units of the respective r.m.s. * input levels. *----------------------------------------------------------------------- double precision function vvr9auto(mux,v1x,rho) double precision mux,v1x,rho,L,h,k,r,bvnd,rt2 L(h,k,r)=bvnd(h,k,r) rt2=sqrt(2d0) vvr9auto=-16d0+4d0*(-erf((mux-7*v1x)/rt2)-erf((mux-5*v1x)/rt2) & -erf((mux-3*v1x)/rt2)+erf((-mux+v1x)/rt2)-erf((mux+v1x)/rt2) & -erf((mux+3*v1x)/rt2)-erf((mux+5*v1x)/rt2) & -erf((mux+7*v1x)/rt2))+ & L(-mux-7*v1x,-mux-7*v1x,rho)+L(-mux-5*v1x,-mux-5*v1x,rho)+ & L(-mux-3*v1x,-mux-3*v1x,rho)+L(-mux-v1x,-mux-v1x,rho)+ & L(-mux+v1x,-mux+v1x,rho)+L(-mux+3*v1x,-mux+3*v1x,rho)+ & L(-mux+5*v1x,-mux+5*v1x,rho)+L(-mux+7*v1x,-mux+7*v1x,rho)+ & 2d0*(L(-mux-7*v1x,-mux-5*v1x,rho)+L(-mux-7*v1x,-mux-3*v1x,rho)+ & L(-mux-7*v1x,-mux-v1x,rho)+L(-mux-7*v1x,-mux+v1x,rho)+ & L(-mux-7*v1x,-mux+3*v1x,rho)+L(-mux-7*v1x,-mux+5*v1x,rho)+ & L(-mux-7*v1x,-mux+7*v1x,rho)+L(-mux-5*v1x,-mux-3*v1x,rho)+ & L(-mux-5*v1x,-mux-v1x,rho)+L(-mux-5*v1x,-mux+v1x,rho)+ & L(-mux-5*v1x,-mux+3*v1x,rho)+L(-mux-5*v1x,-mux+5*v1x,rho)+ & L(-mux-5*v1x,-mux+7*v1x,rho)+L(-mux-3*v1x,-mux-v1x,rho)+ & L(-mux-3*v1x,-mux+v1x,rho)+L(-mux-3*v1x,-mux+3*v1x,rho)+ & L(-mux-3*v1x,-mux+5*v1x,rho)+L(-mux-3*v1x,-mux+7*v1x,rho)+ & L(-mux-v1x,-mux+v1x,rho)+L(-mux-v1x,-mux+3*v1x,rho)+ & L(-mux-v1x,-mux+5*v1x,rho)+L(-mux-v1x,-mux+7*v1x,rho)+ & L(-mux+v1x,-mux+3*v1x,rho)+L(-mux+v1x,-mux+5*v1x,rho)+ & L(-mux+v1x,-mux+7*v1x,rho)+L(-mux+3*v1x,-mux+5*v1x,rho)+ & L(-mux+3*v1x,-mux+7*v1x,rho)+L(-mux+7*v1x,-mux+5*v1x,rho)) return end *----------------------------------------------------------------------- * vvr9zmean : the Van Vleck curve for the 9x9 level case given * v1x, v1y : the first positive input thresholds (zero mean) * rho : the observed correlation * v1x, v1y are in units of the respective r.m.s. * input levels. *----------------------------------------------------------------------- double precision function vvr9zmean(v1x,v1y,rho) double precision v1x,v1y,rho,L,h,k,r,bvnd,rt2 L(h,k,r)=bvnd(h,k,r) rt2=sqrt(2d0) vvr9zmean=-12d0 - erfc(v1x/rt2)/2.0 + & L(-7*v1x,-7*v1y,rho)+L(-7*v1x,-5*v1y,rho)+ & L(-7*v1x,-3*v1y,rho)+L(-7*v1x,-v1y,rho)+ & L(-7*v1x,v1y,rho)+L(-7*v1x,3*v1y,rho)+ & L(-7*v1x,5*v1y,rho)+L(-7*v1x,7*v1y,rho)+ & L(-5*v1x,-7*v1y,rho)+L(-5*v1x,-5*v1y,rho)+ & L(-5*v1x,-3*v1y,rho)+L(-5*v1x,-v1y,rho)+ & L(-5*v1x,v1y,rho)+L(-5*v1x,3*v1y,rho)+ & L(-5*v1x,5*v1y,rho)+L(-5*v1x,7*v1y,rho)+ & L(-3*v1x,-7*v1y,rho)+L(-3*v1x,-5*v1y,rho)+ & L(-3*v1x,-3*v1y,rho)+L(-3*v1x,-v1y,rho)+ & L(-3*v1x,v1y,rho)+L(-3*v1x,3*v1y,rho)+ & L(-3*v1x,5*v1y,rho)+L(-3*v1x,7*v1y,rho)- & L(v1x,-7*v1y,-rho)+L(v1x,-7*v1y,rho)- & L(v1x,-5*v1y,-rho)+L(v1x,-5*v1y,rho)- & L(v1x,-3*v1y,-rho)+L(v1x,-3*v1y,rho)+ & L(v1x,-v1y,rho)-L(v1x,v1y,-rho)+ & 2*L(v1x,v1y,rho)-L(v1x,3*v1y,-rho)+ & L(v1x,3*v1y,rho)-L(v1x,5*v1y,-rho)+ & L(v1x,5*v1y,rho)-L(v1x,7*v1y,-rho)+ & L(v1x,7*v1y,rho)+L(3*v1x,-7*v1y,rho)+ & L(3*v1x,-5*v1y,rho)+L(3*v1x,-3*v1y,rho)+ & L(3*v1x,-v1y,rho)+L(3*v1x,v1y,rho)+ & L(3*v1x,3*v1y,rho)+L(3*v1x,5*v1y,rho)+ & L(3*v1x,7*v1y,rho)+L(5*v1x,-7*v1y,rho)+ & L(5*v1x,-5*v1y,rho)+L(5*v1x,-3*v1y,rho)+ & L(5*v1x,-v1y,rho)+L(5*v1x,v1y,rho)+ & L(5*v1x,3*v1y,rho)+L(5*v1x,5*v1y,rho)+ & L(5*v1x,7*v1y,rho)+L(7*v1x,-7*v1y,rho)+ & L(7*v1x,-5*v1y,rho)+L(7*v1x,-3*v1y,rho)+ & L(7*v1x,-v1y,rho)+L(7*v1x,v1y,rho)+ & L(7*v1x,3*v1y,rho)+L(7*v1x,5*v1y,rho)+ & L(7*v1x,7*v1y,rho) return end *----------------------------------------------------------------------- * vvr9zauto : the Van Vleck curve for the 9x9 level case given * v : the first positive input threshold (x==y, zero mean) * rho : the observed correlation * v is in units of the r.m.s. * input levels. *----------------------------------------------------------------------- double precision function vvr9zauto(v,rho) double precision v,rho,L,h,k,r,bvnd,rt2 L(h,k,r)=bvnd(h,k,r) rt2=sqrt(2d0) vvr9zauto=-15d0+erfc((3*v)/rt2)+erfc((5*v)/rt2)+erfc((7*v)/rt2) + & L(-7*v,-7*v,rho)+L(-5*v,-5*v,rho)+L(-3*v,-3*v,rho)+ & L(3*v,3*v,rho)+L(5*v,5*v,rho)+L(7*v,7*v,rho)+ & 2*(L(-7*v,-5*v,rho)+L(-7*v,-3*v,rho)+ & L(-7*v,-v,rho)+L(-7*v,v,rho)+L(-7*v,3*v,rho)+ & L(-7*v,5*v,rho)+L(-7*v,7*v,rho)+ & L(-5*v,-3*v,rho)+L(-5*v,-v,rho)+L(-5*v,v,rho)+ & L(-5*v,3*v,rho)+L(-5*v,5*v,rho)+L(-5*v,7*v,rho)+ & L(-3*v,-1*v,rho)+L(-3*v,v,rho)+ & L(-3*v,3*v,rho)+L(-3*v,5*v,rho)+L(-3*v,7*v,rho)- & L(v,v,-rho)+L(v,v,rho)-L(v,3*v,-rho)+ & L(v,3*v,rho)-L(v,5*v,-rho)+L(v,5*v,rho)- & L(v,7*v,-rho)+L(v,7*v,rho)+ & L(3*v,5*v,rho)+L(3*v,7*v,rho)+ & L(5*v,7*v,rho)) return end *----------------------------------------------------------------------- * BVND was written by Alan Genz of the Department of Mathematics * at Washington State University. See the Schwab memo for details. *----------------------------------------------------------------------- DOUBLE PRECISION FUNCTION BVND( DH, DK, R ) * * A function for computing bivariate normal probabilities. * * Alan Genz * Department of Mathematics * Washington State University * Pullman, WA 99164-3113 * Email : alangenzwsu.edu * * This function is based on the method described by * Drezner, Z and G.O. Wesolowsky, (1989), * On the computation of the bivariate normal inegral, * Journal of Statist. Comput. Simul. 35, pp. 101-107, * with major modifications for double precision, and for |R| close to 1. * * BVND - calculate the probability that X is larger than DH and Y is * larger than DK. * * Parameters * * DH DOUBLE PRECISION, integration limit * DK DOUBLE PRECISION, integration limit * R DOUBLE PRECISION, correlation coefficient * DOUBLE PRECISION DH, DK, R, ZERO, TWOPI INTEGER I, IS, LG, NG PARAMETER ( ZERO = 0, TWOPI = 6.283185307179586D0 ) DOUBLE PRECISION X(10,3), W(10,3), AS, A, B, C, D, RS, XS, BVN DOUBLE PRECISION PHID, SN, ASR, H, K, BS, HS, HK * Gauss Legendre Points and Weights, N = 6 DATA ( W(I,1), X(I,1), I = 1,3) / & 0.1713244923791705D+00,-0.9324695142031522D+00, & 0.3607615730481384D+00,-0.6612093864662647D+00, & 0.4679139345726904D+00,-0.2386191860831970D+00/ * Gauss Legendre Points and Weights, N = 12 DATA ( W(I,2), X(I,2), I = 1,6) / & 0.4717533638651177D-01,-0.9815606342467191D+00, & 0.1069393259953183D+00,-0.9041172563704750D+00, & 0.1600783285433464D+00,-0.7699026741943050D+00, & 0.2031674267230659D+00,-0.5873179542866171D+00, & 0.2334925365383547D+00,-0.3678314989981802D+00, & 0.2491470458134029D+00,-0.1252334085114692D+00/ * Gauss Legendre Points and Weights, N = 20 DATA ( W(I,3), X(I,3), I = 1, 10 ) / & 0.1761400713915212D-01,-0.9931285991850949D+00, & 0.4060142980038694D-01,-0.9639719272779138D+00, & 0.6267204833410906D-01,-0.9122344282513259D+00, & 0.8327674157670475D-01,-0.8391169718222188D+00, & 0.1019301198172404D+00,-0.7463319064601508D+00, & 0.1181945319615184D+00,-0.6360536807265150D+00, & 0.1316886384491766D+00,-0.5108670019508271D+00, & 0.1420961093183821D+00,-0.3737060887154196D+00, & 0.1491729864726037D+00,-0.2277858511416451D+00, & 0.1527533871307259D+00,-0.7652652113349733D-01/ SAVE X, W IF ( ABS(R) .LT. 0.3D0 ) THEN NG = 1 LG = 3 ELSE IF ( ABS(R) .LT. 0.75D0 ) THEN NG = 2 LG = 6 ELSE NG = 3 LG = 10 ENDIF H = DH K = DK HK = H*K BVN = 0 IF ( ABS(R) .LT. 0.925D0 ) THEN HS = ( H*H + K*K )/2 ASR = ASIN(R) DO I = 1, LG DO IS = -1, 1, 2 SN = SIN( ASR*( IS*X(I,NG) + 1 )/2 ) BVN = BVN + W(I,NG)*EXP( ( SN*HK - HS )/( 1 - SN*SN ) ) END DO END DO BVN = BVN*ASR/( 2*TWOPI ) + PHID(-H)*PHID(-K) ELSE IF ( R .LT. 0 ) THEN K = -K HK = -HK ENDIF IF ( ABS(R) .LT. 1 ) THEN AS = ( 1 - R )*( 1 + R ) A = SQRT(AS) BS = ( H - K )**2 C = ( 4 - HK )/8 D = ( 12 - HK )/16 ASR = -( BS/AS + HK )/2 IF ( ASR .GT. -100 ) BVN = A*EXP(ASR) & *( 1 - C*( BS - AS )*( 1 - D*BS/5 )/3 + C*D*AS*AS/5 ) IF ( -HK .LT. 100 ) THEN B = SQRT(BS) BVN = BVN - EXP( -HK/2 )*SQRT(TWOPI)*PHID(-B/A)*B & *( 1 - C*BS*( 1 - D*BS/5 )/3 ) ENDIF A = A/2 DO I = 1, LG DO IS = -1, 1, 2 XS = ( A*( IS*X(I,NG) + 1 ) )**2 RS = SQRT( 1 - XS ) ASR = -( BS/XS + HK )/2 IF ( ASR .GT. -100 ) THEN BVN = BVN + A*W(I,NG)*EXP( ASR ) & *( EXP( -HK*( 1 - RS )/( 2*( 1 + RS ) ) )/RS & - ( 1 + C*XS*( 1 + D*XS ) ) ) END IF END DO END DO BVN = -BVN/TWOPI ENDIF IF ( R .GT. 0 ) BVN = BVN + PHID( -MAX( H, K ) ) IF ( R .LT. 0 ) BVN = -BVN + MAX( ZERO, PHID(-H) - PHID(-K) ) ENDIF BVND = BVN END *----------------------------------------------------------------------- * PHID is used by BVND *----------------------------------------------------------------------- DOUBLE PRECISION FUNCTION PHID(Z) * * Normal distribution probabilities accurate to 1.e-15. * Z = no. of standard deviations from the mean. * * Based upon algorithm 5666 for the error function, from: * Hart, J.F. et al, 'Computer Approximations', Wiley 1968 * * Programmer: Alan Miller * * Latest revision - 30 March 1986 * DOUBLE PRECISION P0, P1, P2, P3, P4, P5, P6, & Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7, & Z, P, EXPNTL, CUTOFF, ROOTPI, ZABS PARAMETER( & P0 = 220.20 68679 12376 1D0, & P1 = 221.21 35961 69931 1D0, & P2 = 112.07 92914 97870 9D0, & P3 = 33.912 86607 83830 0D0, & P4 = 6.3739 62203 53165 0D0, & P5 = .70038 30644 43688 1D0, & P6 = .035262 49659 98910 9D0 ) PARAMETER( & Q0 = 440.41 37358 24752 2D0, & Q1 = 793.82 65125 19948 4D0, & Q2 = 637.33 36333 78831 1D0, & Q3 = 296.56 42487 79673 7D0, & Q4 = 86.780 73220 29460 8D0, & Q5 = 16.064 17757 92069 5D0, & Q6 = 1.7556 67163 18264 2D0, & Q7 = .088388 34764 83184 4D0 ) PARAMETER( ROOTPI = 2.5066 28274 63100 1D0 ) PARAMETER( CUTOFF = 7.0710 67811 86547 5D0 ) * ZABS = ABS(Z) * * |Z| > 37 * IF ( ZABS .GT. 37 ) THEN P = 0 ELSE * * |Z| <= 37 * EXPNTL = EXP( -ZABS**2/2 ) * * |Z| < CUTOFF = 10/SQRT(2) * IF ( ZABS .LT. CUTOFF ) THEN P = EXPNTL*( ( ( ( ( ( P6*ZABS + P5 )*ZABS + P4 )*ZABS & + P3 )*ZABS + P2 )*ZABS + P1 )*ZABS + P0 ) & /( ( ( ( ( ( ( Q7*ZABS + Q6 )*ZABS + Q5 )*ZABS & + Q4 )*ZABS + Q3 )*ZABS + Q2 )*ZABS + Q1 )*ZABS + Q0 ) * * |Z| >= CUTOFF. * ELSE P = EXPNTL/( ZABS + 1/( ZABS + 2/( ZABS + 3/( ZABS & + 4/( ZABS + 0.65D0 ) ) ) ) )/ROOTPI END IF END IF IF ( Z .GT. 0 ) P = 1 - P PHID = P END casacore-3.7.1/tables/000077500000000000000000000000001476623553700145765ustar00rootroot00000000000000casacore-3.7.1/tables/AlternateMans/000077500000000000000000000000001476623553700173345ustar00rootroot00000000000000casacore-3.7.1/tables/AlternateMans/AntennaPairFile.h000066400000000000000000000202621476623553700225070ustar00rootroot00000000000000#ifndef CASACORE_ANTENNA_PAIR_FILE_H_ #define CASACORE_ANTENNA_PAIR_FILE_H_ #include "BufferedColumnarFile.h" #include #include #include #include #include namespace casacore { /** * This class is able to store the combination of ANTENNA1 and ANTENNA2 * columns in a compressed manner. It does this by assuming that the * pair of antenna indices are repeated, i.e. every timestep in the measurement * set follows the same sequence of baselines. By only writing the repeated * sequence once, the storage required is reduce to that of only one timestep. * * The class checks to see if the writing satisfies the required constraints, * and if they do not an exception is thrown. */ class AntennaPairFile { public: AntennaPairFile() noexcept = default; AntennaPairFile(AntennaPairFile&& source) noexcept : file_(std::move(source.file_)), rows_in_pattern_(source.rows_in_pattern_), data_(source.data_) { source.rows_in_pattern_ = 0; source.data_.clear(); } ~AntennaPairFile() noexcept { Close(); } AntennaPairFile& operator=(AntennaPairFile&& rhs) { Close(); file_ = std::move(rhs.file_); rows_in_pattern_ = rhs.rows_in_pattern_; data_ = rhs.data_; rhs.rows_in_pattern_ = 0; rhs.data_.clear(); return *this; } /** * Create a new antenna-pair file on disk with the given filename. */ static AntennaPairFile CreateNew(const std::string& filename) { return AntennaPairFile(filename); } /** * Open an already existing antenna-pair file from disk with the given * filename. */ static AntennaPairFile OpenExisting(const std::string& filename) { return AntennaPairFile(filename, true); } void WriteAntenna1(uint64_t row, int32_t antenna1) { WriteAntenna<0>(row, antenna1); } void WriteAntenna2(uint64_t row, int32_t antenna2) { WriteAntenna<1>(row, antenna2); } void WritePair(uint64_t row, int32_t antenna1, int32_t antenna2) { WriteAntenna<0>(row, antenna1); WriteAntenna<1>(row, antenna2); } int32_t ReadAntenna1(uint64_t row) { return ReadAntenna<0>(row); } int32_t ReadAntenna2(uint64_t row) { return ReadAntenna<1>(row); } void Close() { if (file_.IsOpen()) { if (rows_in_pattern_ == 0) { WriteHeader(); WriteData(); } file_.Close(); rows_in_pattern_ = 0; data_.clear(); } } const std::string& Filename() const { return file_.Filename(); } /** * The number of rows that form one repeating pattern. Function is added * for testing purposes. */ uint64_t NRowsInPattern() const { return rows_in_pattern_; } private: /** * Create a new file on disk. */ AntennaPairFile(const std::string& filename) : file_(BufferedColumnarFile::CreateNew(filename, kHeaderSize, sizeof(int32_t) * 2)) {} /** * Open an existing file from disk. The last parameter is a dummy * parameter to distinguish it from the creating constructor. */ AntennaPairFile(const std::string& filename, bool /*open existing*/) : file_(BufferedColumnarFile::OpenExisting(filename, kHeaderSize)) { ReadHeader(); ReadData(); } static bool HasUnsetAntenna(const std::array& pair) { return pair[0] == kUnsetAntenna || pair[1] == kUnsetAntenna; } /** * @tparam AntennaNumber Number of the antenna inside the pair: zero or one. */ template void WriteAntenna(uint64_t row, int32_t antenna) { static_assert(AntennaNumber == 0 || AntennaNumber == 1); if (rows_in_pattern_ == 0) { const bool has_unfinished_row = !data_.empty() && HasUnsetAntenna(data_.back()); if (has_unfinished_row) { if (row >= data_.size()) throw std::runtime_error( "Incorrect writing order for AntennaPairFile (in unfinished " "pair, row=" + std::to_string(row) + ")"); const bool is_rewrite = row < data_.size() - 1 || data_.back()[AntennaNumber] != kUnsetAntenna; if (is_rewrite) { if (data_[row][AntennaNumber] != antenna) throw std::runtime_error( "Antenna " + std::to_string(AntennaNumber) + " value in row " + std::to_string(row) + " is rewritten with a different value"); } else { data_.back()[AntennaNumber] = antenna; if (data_.back() == data_.front() && data_.size() > 1) { // This row is the same as the first row, so this row is the first // row of the next repeated pattern, and we have discovered the size // of the pattern. data_.pop_back(); rows_in_pattern_ = data_.size(); WriteHeader(); WriteData(); } } } else { if (row > data_.size()) throw std::runtime_error( "Incorrect writing order for AntennaPairFile (in new pair, row=" + std::to_string(row) + ")"); if (row == data_.size()) { if constexpr (AntennaNumber == 0) data_.emplace_back(std::array{antenna, kUnsetAntenna}); else data_.emplace_back(std::array{kUnsetAntenna, antenna}); } else { // This is a rewrite of an already written value if (data_[row][AntennaNumber] != antenna) throw std::runtime_error( "Antenna " + std::to_string(AntennaNumber) + " value in row " + std::to_string(row) + " is rewritten with a different value"); } } } else { const std::array& pair = data_[row % rows_in_pattern_]; if (pair[AntennaNumber] != antenna) throw std::runtime_error( "Error writing to AntennaPairFile, row " + std::to_string(row) + ": the antenna pairs do not follow a consistent pattern"); } } template int32_t ReadAntenna(uint64_t row) { static_assert(AntennaNumber == 0 || AntennaNumber == 1); int32_t antenna; if (rows_in_pattern_ == 0) { if (row >= data_.size()) throw std::runtime_error( "Invalid read of antenna pair: requested row is beyond the number " "of written rows, and writing of antenna pattern not finished"); antenna = data_[row][AntennaNumber]; if (antenna == kUnsetAntenna) throw std::runtime_error( "Trying to read antenna value that has not been written yet"); return antenna; } else { return data_[row % rows_in_pattern_][AntennaNumber]; } } void ReadHeader() { unsigned char data[kHeaderSize]; file_.ReadHeader(data); if (!std::equal(data, data + 8, kMagicHeaderTag)) { throw std::runtime_error( "The Antenna-pair columnar file header does not have the expected " "tag for antenna columns: the measurement set may be damaged"); } rows_in_pattern_ = reinterpret_cast(data[8]); } void WriteHeader() { unsigned char data[kHeaderSize]; std::copy_n(kMagicHeaderTag, 8, data); reinterpret_cast(data[8]) = rows_in_pattern_; file_.WriteHeader(data); } void ReadData() { data_.resize(file_.NRows()); for (uint64_t row = 0; row != data_.size(); ++row) { file_.Read(row, 0, data_[row].data(), 2); } } void WriteData() { // Note that rows_in_pattern_ might still be zero if the pattern has not // been finished. This would happen if MS is partially written, reopened and // written further. This is rather unlikely to happen, but it is easy to // support. for (uint64_t row = 0; row != data_.size(); ++row) { file_.Write(row, 0, data_[row].data(), 2); } } /** * The header: * char[8] "AntPair\0" * uint64_t rows_per_block */ constexpr static size_t kHeaderSize = 16; constexpr static const char kMagicHeaderTag[8] = "AntPair"; constexpr static int32_t kUnsetAntenna = std::numeric_limits::min(); BufferedColumnarFile file_; /** * This value remains zero until the repeating pattern was found. */ uint64_t rows_in_pattern_ = 0; std::vector> data_; }; } // namespace casacore #endif casacore-3.7.1/tables/AlternateMans/AntennaPairStMan.cc000066400000000000000000000036001476623553700230050ustar00rootroot00000000000000 #include "AntennaPairStMan.h" #include "AntennaPairStManColumn.h" namespace casacore { AntennaPairStMan::AntennaPairStMan(const String &, const Record &) : DataManager() { } AntennaPairStMan::AntennaPairStMan(const AntennaPairStMan &source) : DataManager(), name_(source.name_) {} AntennaPairStMan::~AntennaPairStMan() noexcept = default; void AntennaPairStMan::create64(rownr_t /*nRow*/) { file_ = AntennaPairFile::CreateNew(fileName()); } rownr_t AntennaPairStMan::open64(rownr_t n_row, AipsIO &) { file_ = AntennaPairFile::OpenExisting(fileName()); return n_row; } DataManagerColumn *AntennaPairStMan::makeScalarColumn( const String &name, int dataType, const String & /*dataTypeID*/) { if (dataType == TpInt) { if(name == "ANTENNA1") { columns_[0] = std::make_unique(file_, false); return columns_[0].get(); } if(name == "ANTENNA2") { columns_[1] = std::make_unique(file_, true); return columns_[1].get(); } else { throw std::runtime_error("Trying to create a column with AntennaPairStMan that is named '" + name + "': only ANTENNA1 or ANTENNA2 is supported"); } } else { throw std::runtime_error( "Trying to create an ANTENNA column (" + name + ") with wrong type"); } } void AntennaPairStMan::deleteManager() { unlink(fileName().c_str()); } void AntennaPairStMan::addRow64(rownr_t) { } void AntennaPairStMan::removeRow64(rownr_t) { throw std::runtime_error( "Can't remove rows from a AntennaPairStMan"); } void AntennaPairStMan::removeColumn(DataManagerColumn *column) { if(columns_[0].get() == column) { columns_[0].reset(); } else if(columns_[1].get() == column) { columns_[1].reset(); } else { throw std::runtime_error( "Trying to remove column that was not part of the storage manager"); } } } // namespace casacore casacore-3.7.1/tables/AlternateMans/AntennaPairStMan.h000066400000000000000000000051301476623553700226470ustar00rootroot00000000000000#ifndef CASACORE_ANTENNA_PAIR_STORAGE_MANAGER_H_ #define CASACORE_ANTENNA_PAIR_STORAGE_MANAGER_H_ #include #include #include "AntennaPairFile.h" #include #include namespace casacore { class AntennaPairStManColumn; /** * A storage manager that saves the antenna columns with (lossless) compression. */ class AntennaPairStMan final : public DataManager { public: AntennaPairStMan(const String &, const Record &); /** * The columns are not copied: the new manager will be empty. */ AntennaPairStMan(const AntennaPairStMan &source); ~AntennaPairStMan() noexcept; AntennaPairStMan &operator=(const AntennaPairStMan &source) = delete; DataManager *clone() const final { return new AntennaPairStMan(*this); } static DataManager *makeObject(const String &name, const Record &spec) { return new AntennaPairStMan(name, spec); } String dataManagerType() const final { return "AntennaPairStMan"; } Record dataManagerSpec() const final { return Record(); } casacore::Bool canAddColumn() const final { return true; } private: Bool flush(AipsIO &, Bool) final { return false; } void create64(rownr_t nRow) final; rownr_t open64(rownr_t nRow, AipsIO &) final; DataManagerColumn *makeScalarColumn(const String &name, int dataType, const String &dataTypeID) final; DataManagerColumn *makeDirArrColumn(const String &, int, const String &) final { throw std::runtime_error( "makeDirArrColumn() called on a AntennaPairStMan. AntennaPairStMan can " "only make scalar columns"); } DataManagerColumn *makeIndArrColumn(const String &, int, const String &) final { throw std::runtime_error( "makeIndArrColumn() called on a AntennaPairStMan. AntennaPairStMan can " "only make scalar columns"); } rownr_t resync64(rownr_t nRow) final { return nRow; } void deleteManager() final; // Reopen the storage manager files for read/write. void reopenRW() final {} // Add rows to the storage manager. void addRow64(rownr_t nrrow) final; // Delete a row from all columns. void removeRow64(rownr_t row_nr) final; // Do the final addition of a column. void addColumn(DataManagerColumn *) final {} // Remove a column from the data file. void removeColumn(DataManagerColumn *) final; std::string name_; std::array, 2> columns_; AntennaPairFile file_; }; } // namespace casacore #endif casacore-3.7.1/tables/AlternateMans/AntennaPairStManColumn.h000066400000000000000000000031241476623553700240260ustar00rootroot00000000000000#ifndef CASACORE_STOKES_I_ST_MAN_COLUMN_H_ #define CASACORE_STOKES_I_ST_MAN_COLUMN_H_ #include #include #include #include #include "AntennaPairFile.h" #include namespace casacore { class AntennaPairStManColumn final : public StManColumn { public: /** * Constructor, to be overloaded by subclass. * @param parent The parent stman to which this column belongs. * @param dtype The column's type as defined by Casacore. */ explicit AntennaPairStManColumn(AntennaPairFile &file, bool is_antenna_2) : StManColumn(DataType::TpInt), file_(file), is_antenna_2_(is_antenna_2) {} /** * Whether this column is writable * @returns @c true */ Bool isWritable() const final { return true; } void getInt(rownr_t row, Int *dataPtr) final { if (is_antenna_2_) *dataPtr = file_.ReadAntenna2(row); else *dataPtr = file_.ReadAntenna1(row); } /** * Write values into a particular row. * @param rowNr The row number to write the values to. * @param dataPtr The data pointer. */ void putInt(rownr_t row, const Int *dataPtr) final { if (is_antenna_2_) file_.WriteAntenna2(row, *dataPtr); else file_.WriteAntenna1(row, *dataPtr); } private: AntennaPairStManColumn(const AntennaPairStManColumn &source) = delete; void operator=(const AntennaPairStManColumn &source) = delete; AntennaPairFile &file_; bool is_antenna_2_; }; } // namespace casacore #endif casacore-3.7.1/tables/AlternateMans/BitPacking.h000066400000000000000000000021031476623553700215140ustar00rootroot00000000000000#ifndef CASACORE_TABLES_BITPACKING_H_ #define CASACORE_TABLES_BITPACKING_H_ #include inline void PackBoolArray(unsigned char* packed_buffer, const bool* input, size_t n) { const size_t limit = n / 8; const bool* end = input + n; for (size_t i = 0; i != limit; ++i) { *packed_buffer = 0; for (size_t b = 0; b != 8; ++b) { *packed_buffer |= (*input) << b; ++input; } ++packed_buffer; } size_t b = 0; if (input != end) { *packed_buffer = 0; do { *packed_buffer |= (*input) << b; ++input; ++b; } while (input != end); } } inline void UnpackBoolArray(bool* output, const unsigned char* packed_input, size_t n) { bool* end = output + n; const size_t limit = n / 8; for (size_t i = 0; i != limit; i++) { for (size_t b = 0; b != 8; ++b) { *output = (*packed_input >> b) & 0x1; ++output; } ++packed_input; } size_t b = 0; while (output != end) { *output = (*packed_input >> b) & 0x1; ++output; ++b; } } #endif casacore-3.7.1/tables/AlternateMans/BufferedColumnarFile.h000066400000000000000000000275721476623553700235450ustar00rootroot00000000000000#ifndef CASACORE_BUFFERED_COLUMNAR_FILE_H_ #define CASACORE_BUFFERED_COLUMNAR_FILE_H_ #include #include #include #include #include #include "BitPacking.h" #include "RowBasedFile.h" namespace casacore { /** * Class that provides binary table I/O. It is rather low-level, and requires * the user to keep track of the columns and their datatype. It uses a simple * binary format where the columns are interleaved into the output file. * Booleans are written with bit-packing. * * This class limits calls to the read/write functions by caching a block of * data around the last accessed row. The size of the buffer is templated to * make testing of the caching easier. Production code can normally use the * alias BufferedColumnarFile, which uses a reasonable buffer size. * * This class aims to be as fast as possible for large data files that are read * from or written to consecutively. It does not try to optimize random access, * partial data access and access is not transactional. * * The class uses exceptions to handle any I/O errors. */ template class VarBufferedColumnarFile : private RowBasedFile { public: using RowBasedFile::AddRows; using RowBasedFile::DeleteRow; using RowBasedFile::Filename; using RowBasedFile::IsOpen; using RowBasedFile::NRows; using RowBasedFile::ReadHeader; using RowBasedFile::Stride; using RowBasedFile::WriteHeader; VarBufferedColumnarFile() noexcept = default; VarBufferedColumnarFile(const VarBufferedColumnarFile& rhs) = delete; VarBufferedColumnarFile(VarBufferedColumnarFile&& rhs) noexcept : packed_buffer_(std::move(rhs.packed_buffer_)), block_changed_(rhs.block_changed_), active_block_(rhs.active_block_), rows_per_block_(rhs.rows_per_block_), block_buffer_(std::move(rhs.block_buffer_)) { rhs.block_changed_ = false; rhs.active_block_ = 0; rhs.rows_per_block_ = 0; } ~VarBufferedColumnarFile() noexcept { if (IsOpen()) { if (block_changed_) { const uint64_t start_row = active_block_ * rows_per_block_; const size_t n_rows_to_write = std::min(rows_per_block_, std::max(NRows(), start_row) - start_row); Seek(start_row * Stride() + DataLocation(), SEEK_SET); WriteData(block_buffer_.data(), n_rows_to_write * Stride()); block_changed_ = false; } } } VarBufferedColumnarFile& operator=(VarBufferedColumnarFile&& rhs) { Close(); RowBasedFile::operator=(std::move(rhs)); std::swap(packed_buffer_, rhs.packed_buffer_); std::swap(block_changed_, rhs.block_changed_); std::swap(active_block_, rhs.active_block_); std::swap(rows_per_block_, rhs.rows_per_block_); std::swap(block_buffer_, rhs.block_buffer_); return *this; } /** * Close the file. After closing, all calls to I/O functions * cause undefined behaviour, untill the class is assigned to * a new instance. */ void Close() { if (IsOpen()) { if (block_changed_) { try { WriteActiveBlock(); } catch (...) { // Even if writing fails, the file should still be closed to // prevent the file to remain open after destruction. RowBasedFile::Close(); throw; } } RowBasedFile::Close(); } } /** * Create a new file on disk. If the file exists, it is overwritten. * @param header Optional header. * @param stride The number of bytes in one row (total over all columns). */ static VarBufferedColumnarFile CreateNew(const std::string& filename, uint64_t header_size, uint64_t stride) { return VarBufferedColumnarFile(filename, header_size, stride); } /** * Open an existing file from disk. If the file does not exists, an * exception is thrown. * @param header Optional header. * @param stride The number of bytes in one row (total over all columns). */ static VarBufferedColumnarFile OpenExisting(const std::string& filename, size_t header_size) { return VarBufferedColumnarFile(filename, header_size); } /** * Read one cell containing an array of floats. If the row was not written * yet, zeros are returned. * @param row The cell's row index. * @param column_offset The position of this column counted from the start * of the row, in bytes. * @param data Buffer in which the data will be stored. * @param n Size of the column in number of elements (NOT in bytes!). */ void Read(uint64_t row, uint64_t column_offset, float* data, uint64_t n) { ReadImplementation(row, column_offset, data, n); } /** * Read array of doubles. See float version for documentation. */ void Read(uint64_t row, uint64_t column_offset, double* data, uint64_t n) { ReadImplementation(row, column_offset, data, n); } /** * Read array of int32_t. See float version for documentation. */ void Read(uint64_t row, uint64_t column_offset, int32_t* data, uint64_t n) { ReadImplementation(row, column_offset, data, n); } /** * Read array of complex floats. See float version for documentation. */ void Read(uint64_t row, uint64_t column_offset, std::complex* data, uint64_t n) { ReadImplementation(row, column_offset, data, n); } /** * Read an array of bools. See float version for documentation. Booleans are * stored with bit-packing. */ void Read(uint64_t row, uint64_t column_offset, bool* data, uint64_t n) { const size_t byte_size = (n + 7) / 8; assert(column_offset + byte_size <= Stride()); ActivateBlock(row); if (row >= NRows()) { std::fill_n(data, n, false); } else { Seek(row * Stride() + column_offset + DataLocation(), SEEK_SET); ReadData(packed_buffer_.data(), byte_size); UnpackBoolArray(data, packed_buffer_.data(), n); } } /** * Write one cell containing an array of floats. If the row is past the end of * the file, the file is enlarged (making NRows() = row + 1). */ void Write(uint64_t row, uint64_t column_offset, const float* data, uint64_t n) { WriteImplementation(row, column_offset, data, n); } /** * Write an array of doubles. See float version for documentation. */ void Write(uint64_t row, uint64_t column_offset, const double* data, uint64_t n) { WriteImplementation(row, column_offset, data, n); } /** * Write an array of int32_t. See float version for documentation. */ void Write(uint64_t row, uint64_t column_offset, const int32_t* data, uint64_t n) { WriteImplementation(row, column_offset, data, n); } /** * Write an array of complex floats. See float version for documentation. */ void Write(uint64_t row, uint64_t column_offset, const std::complex* data, uint64_t n) { WriteImplementation(row, column_offset, data, n); } /** * Write an array of complex doubles. See float version for documentation. */ void Write(uint64_t row, uint64_t column_offset, const std::complex* data, uint64_t n) { WriteImplementation(row, column_offset, data, n); } /** * Write an array of bools. Bools are stored with bit-packing. See float * version for documentation. */ void Write(uint64_t row, uint64_t column_offset, const bool* data, uint64_t n) { const size_t byte_size = (n + 7) / 8; assert(column_offset + byte_size <= Stride()); ActivateBlock(row); PackBoolArray(packed_buffer_.data(), data, n); Seek(row * Stride() + column_offset + DataLocation(), SEEK_SET); WriteData(packed_buffer_.data(), byte_size); SetNRows(std::max(row + 1, NRows())); } /** * Set the number of bytes per row for this file. This changes the format * of the file, and because of this the file is emptied. */ void SetStride(uint64_t new_stride) { RowBasedFile::SetStride(new_stride); packed_buffer_.resize(new_stride); active_block_ = std::numeric_limits::max(); rows_per_block_ = new_stride == 0 ? 0 : std::max(1, BufferSize / new_stride); block_buffer_.resize(rows_per_block_ * new_stride); block_changed_ = false; } private: // Create or overwrite a new columnar file on disk VarBufferedColumnarFile(const std::string& filename, uint64_t header_size, uint64_t stride) : RowBasedFile(filename, header_size, stride), packed_buffer_(stride), rows_per_block_(stride == 0 ? 0 : std::max(1, BufferSize / stride)), block_buffer_(rows_per_block_ * stride) {} // Open an existing columnar file VarBufferedColumnarFile(const std::string& filename, size_t header_size) : RowBasedFile(filename, header_size) { if (Stride() != 0) { packed_buffer_.resize(Stride()); rows_per_block_ = std::max(1, BufferSize / Stride()); block_buffer_.resize(rows_per_block_ * Stride()); } active_block_ = std::numeric_limits::max(); } void ActivateBlock(uint64_t row) { const uint64_t block = row / rows_per_block_; if (active_block_ != block) { if (block_changed_) { WriteActiveBlock(); } const uint64_t start_row = block * rows_per_block_; const size_t n_rows_to_read = std::min(rows_per_block_, std::max(NRows(), start_row) - start_row); Seek(start_row * Stride() + DataLocation(), SEEK_SET); ReadData(block_buffer_.data(), n_rows_to_read * Stride()); // Fill the remainder of block_buffer_ with zeroes. Doing it here makes // the code robust and avoids the need for inserting zeroes when adding // rows out-of-order, e.g., when adding row 5 while NRows() is 2." std::fill(block_buffer_.begin() + n_rows_to_read * Stride(), block_buffer_.end(), 0); active_block_ = block; } } template void ReadImplementation(uint64_t row, uint64_t column_offset, ValueType* data, uint64_t n) { assert(column_offset + n * sizeof(ValueType) <= Stride()); if (row >= NRows()) { std::fill_n(data, n, ValueType()); } else { ActivateBlock(row); const uint64_t block_row = active_block_ * rows_per_block_; const unsigned char* position = block_buffer_.data() + (row - block_row) * Stride() + column_offset; std::copy_n(position, n * sizeof(ValueType), reinterpret_cast(data)); } } template void WriteImplementation(uint64_t row, uint64_t column_offset, const ValueType* data, uint64_t n) { assert(column_offset + n * sizeof(ValueType) <= Stride()); ActivateBlock(row); const uint64_t block_row = active_block_ * rows_per_block_; unsigned char* position = block_buffer_.data() + (row - block_row) * Stride() + column_offset; std::copy_n(reinterpret_cast(data), n * sizeof(ValueType), position); SetNRows(std::max(row + 1, NRows())); block_changed_ = true; } void WriteActiveBlock() { const uint64_t start_row = active_block_ * rows_per_block_; const size_t n_rows_to_write = std::min(rows_per_block_, std::max(NRows(), start_row) - start_row); Seek(start_row * Stride() + DataLocation(), SEEK_SET); WriteData(block_buffer_.data(), n_rows_to_write * Stride()); block_changed_ = false; } /** * This buffer is used temporarily for (un)packing booleans. Storing it as a * member avoids memory allocations. */ std::vector packed_buffer_; bool block_changed_ = false; uint64_t active_block_ = 0; uint64_t rows_per_block_ = 0; std::vector block_buffer_; }; using BufferedColumnarFile = VarBufferedColumnarFile<100 * 1024>; } // namespace casacore #endif casacore-3.7.1/tables/AlternateMans/RowBasedFile.h000066400000000000000000000274101476623553700220170ustar00rootroot00000000000000#ifndef CASACORE_ROW_BASED_FILE_H_ #define CASACORE_ROW_BASED_FILE_H_ #include #include #include #include #include #include namespace casacore { class RowBasedFile { public: RowBasedFile() = default; RowBasedFile(const RowBasedFile& rhs) = delete; RowBasedFile(RowBasedFile&& rhs) noexcept : file_(rhs.file_), private_header_size_(rhs.private_header_size_), n_rows_(rhs.n_rows_), need_truncate_(rhs.need_truncate_), stride_(rhs.stride_), data_location_(rhs.data_location_), filename_(rhs.filename_) { rhs.file_ = -1; rhs.private_header_size_ = kWriterPrivateHeaderSize; rhs.n_rows_ = 0; rhs.need_truncate_ = false; rhs.stride_ = 0; rhs.data_location_ = kWriterPrivateHeaderSize; rhs.filename_ = ""; } /** * Create or overwrite a new columnar file on disk */ RowBasedFile(const std::string& filename, uint64_t header_size, uint64_t stride) : stride_(stride), filename_(filename) { file_ = open(filename.c_str(), O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (file_ < 0) throw std::runtime_error("I/O error: could not create new file '" + filename + "'"); data_location_ = header_size + kWriterPrivateHeaderSize; WritePrivateHeader(); } /** * Open an existing columnar file */ RowBasedFile(const std::string& filename, size_t header_size) : filename_(filename) { file_ = open(filename.c_str(), O_RDWR); if (file_ < 0) { file_ = open(filename.c_str(), O_RDONLY); if (file_ < 0) throw std::runtime_error("I/O error: could not open file '" + filename + "'"); } uint32_t magic_tag; ReadData(reinterpret_cast(&magic_tag), sizeof(uint32_t)); if (magic_tag != kMagicFileTag) { throw std::runtime_error( "Could not read file " + filename + ": file does not obey the Casacore row-based file format: either the " "file is damaged, or this is not a Casacore row-based file"); } uint32_t file_version; ReadData(reinterpret_cast(&file_version), sizeof(uint32_t)); const uint32_t major_version = (file_version & 0xFF00) >> 8; constexpr uint32_t kWriterMajorVersion = (kFileVersion & 0xFF00) >> 8; if (major_version > kWriterMajorVersion) { throw std::runtime_error("The file " + filename + " requires a reader of at least major version " + std::to_string(major_version) + ". This reader is for major version " + std::to_string(kWriterMajorVersion) + "."); } // Combine reading of private header size, stride and user header size in // one read call. std::array rest_of_private_header; ReadData(rest_of_private_header.data(), rest_of_private_header.size()); private_header_size_ = reinterpret_cast(rest_of_private_header.data()[0]); stride_ = reinterpret_cast( rest_of_private_header.data()[sizeof(uint32_t)]); size_t file_user_header_size = reinterpret_cast( rest_of_private_header.data()[sizeof(uint32_t) + sizeof(uint64_t)]); if (file_user_header_size != header_size) { throw std::runtime_error("Error reading file " + filename + ": inconsistent size of private header"); } data_location_ = header_size + private_header_size_; const uint64_t pos = lseek(file_, 0, SEEK_END); n_rows_ = stride_ == 0 ? 0 : (pos - data_location_) / stride_; } ~RowBasedFile() noexcept { try { Close(); } catch (...) { } } RowBasedFile& operator=(RowBasedFile&& rhs) { Close(); std::swap(file_, rhs.file_); std::swap(private_header_size_, rhs.private_header_size_); std::swap(n_rows_, rhs.n_rows_); std::swap(need_truncate_, rhs.need_truncate_); std::swap(stride_, rhs.stride_); std::swap(data_location_, rhs.data_location_); std::swap(filename_, rhs.filename_); return *this; } /** * Close the file. After closing, all calls to I/O functions * cause undefined behaviour, until the class is assigned to * a new instance. */ void Close() { if (IsOpen()) { if (need_truncate_) { try { need_truncate_ = false; Truncate(NRows()); } catch (...) { // Truncate failed, still try to close the file to prevent a dangling // open file, before throwing the exception. CloseWithoutTruncate(); throw; } } CloseWithoutTruncate(); } } void Truncate(uint64_t n_rows) { const int result = ftruncate(file_, n_rows_ * stride_ + data_location_); if (result < 0) { throw std::runtime_error( "I/O error: could not truncate file '" + filename_ + "' to have " + std::to_string(n_rows) + " rows: " + ErrorString()); } } void Seek(off_t pos, int seek_direction) { const off_t result = lseek(file_, pos, seek_direction); if (result < 0) throw std::runtime_error("I/O error: could not seek through file '" + filename_ + "'"); } void ReadData(unsigned char* data, uint64_t size) { const int result = ::read(file_, data, size); if (result < 0) throw std::runtime_error("I/O error: could not read from file '" + filename_ + "'"); } void WriteData(const unsigned char* data, uint64_t size) { const int result = write(file_, data, size); if (result < 0) throw std::runtime_error("I/O error: could not write to file '" + filename_ + "'"); } bool IsOpen() const { return file_ >= 0; } /** * Offset of the first row in the file. When using Seek() to move to * a row, this number should be added to the offset. */ uint64_t DataLocation() const { return data_location_; } const std::string& Filename() const { return filename_; } /** * Number of bytes reserved for an optional header. */ uint64_t HeaderSize() const { return data_location_ - private_header_size_; } /** * Write an optional extra header to the file. When creating the file, * the requested space is saved to store this header. * @param data An array equal to the size of the header given * in the @ref CreateNew() and @ref OpenExisting() calls. */ void WriteHeader(const unsigned char* data) { Seek(private_header_size_, SEEK_SET); WriteData(data, data_location_ - private_header_size_); } /** * Read an optional extra header to the file. @see WriteHeader(). */ void ReadHeader(unsigned char* data) { Seek(private_header_size_, SEEK_SET); ReadData(data, data_location_ - private_header_size_); } /** * Total number of rows stored in this file. */ uint64_t NRows() const { return n_rows_; } void SetNRows(uint64_t new_n_rows) { need_truncate_ = true; n_rows_ = new_n_rows; } /** * Total number of bytes in one row. This value is also stored in the file, * and is read from the file in @ref OpenExisting(). */ uint64_t Stride() const { return stride_; } /** * Set the number of bytes per row for this file. This changes the format * of the file, and because of this the file is emptied. */ void SetStride(uint64_t new_stride) { const uint64_t header_size = HeaderSize(); Truncate(0); n_rows_ = 0; stride_ = new_stride; Seek(0, SEEK_SET); data_location_ = header_size + kWriterPrivateHeaderSize; WritePrivateHeader(); } /** * Adds a given number of rows to the back of the file. */ void AddRows(uint64_t n_rows) { SetNRows(NRows() + n_rows); } /** * Deletes the last row. */ void DeleteRow() { if (NRows() > 0) { SetNRows(NRows() - 1); } } private: void CloseWithoutTruncate() { int result = close(file_); file_ = -1; private_header_size_ = kWriterPrivateHeaderSize; n_rows_ = 0; stride_ = 0; data_location_ = kWriterPrivateHeaderSize; filename_ = ""; if (result < 0) throw std::runtime_error("Could not close file " + filename_); } void WritePrivateHeader() { // Collect entire private header in one write call std::array private_header_buffer; reinterpret_cast(private_header_buffer[0]) = kMagicFileTag; reinterpret_cast(private_header_buffer[4]) = kFileVersion; reinterpret_cast(private_header_buffer[8]) = kWriterPrivateHeaderSize; reinterpret_cast(private_header_buffer[12]) = stride_; reinterpret_cast(private_header_buffer[20]) = HeaderSize(); WriteData(private_header_buffer.data(), private_header_buffer.size()); } /** * The size of the private header that the writer creates for the current file * format. This number is written into the file. Whenever a file is read, the * number written into the file should be used to skip over the header. That * way, members can be added to the header that can still be read by older * readers. The header consists of: * - u32: "Crbf" (magic file tag) * - u32: file version * - u32: private header size * - u64: stride * - u64: full header size (private header + user header) */ inline constexpr static uint32_t kWriterPrivateHeaderSize = 3 * sizeof(uint32_t) + 2 * sizeof(uint64_t); /** * First four bytes of a file. This spells out "Crbf" when stored as a little * endian number, which stands for "Casacore Row-based file". Files without * this magic number in the first four bytes are rejected. * * This also makes sure that a file written on a little endian machine is * rejected by a big endian machine, and vice versa. Because big endian * machines are extremely rare for astronomical processing, no effort is made * to make it interchangable at this point of time. The official format is * declared to use little endian numbers. */ inline constexpr static uint32_t kMagicFileTag = 0x66627243; /** * Version of this file, in format 0xaabb, where aa is the major version and * bb is the minor version. The major version is checked against the version * of the reader: if the file has a higher major version than the reader, the * file is rejected. The minor version is not checked, thus an increase in * minor version indicates a change in file format that is still readable by * older readers. */ inline constexpr static uint32_t kFileVersion = 0x0100; static std::string ErrorStringHelper(int result_value, char* buffer) { if (result_value == 0) return buffer; else return "Unknown error"; } static std::string ErrorStringHelper(char* returned_buffer, char* /*supplied_buffer*/) { return std::string(returned_buffer); } static std::string ErrorString() { char errstr[128]; // This is a small trick to allow both versions of strerror_r: by using // function overloading, the right behaviour is picked. return ErrorStringHelper(strerror_r(errno, errstr, 128), errstr); } // The "C" file API is used because we need to use (f)truncate, which is not // available from the C++ fstream API. int file_ = -1; uint32_t private_header_size_ = kWriterPrivateHeaderSize; uint64_t n_rows_ = 0; bool need_truncate_ = false; uint64_t stride_ = 0; /** * This variable is also used to set/calculate the header size, using the * relation: data_location_ = private_header_size_ + header_size. */ uint64_t data_location_ = kWriterPrivateHeaderSize; std::string filename_; }; } // namespace casacore #endif casacore-3.7.1/tables/AlternateMans/SimpleColumnarFile.h000066400000000000000000000134151476623553700232430ustar00rootroot00000000000000#ifndef CASACORE_COLUMNAR_FILE_H_ #define CASACORE_COLUMNAR_FILE_H_ #include #include #include #include #include #include "BitPacking.h" #include "RowBasedFile.h" namespace casacore { /** * Class that provides binary table I/O. It is similar to * @ref BufferedColumnarFile, but is a simple implementation to demonstrate the * interface and to test the base class @ref RowBasedFile. For documentation, * see @ref BufferedColumnarFile. * * This class writes the data in a cell directly to file when @ref Write() is * called, and always reads it back when @ref Read() is called. */ class SimpleColumnarFile : private RowBasedFile { public: using RowBasedFile::AddRows; using RowBasedFile::Close; using RowBasedFile::DeleteRow; using RowBasedFile::Filename; using RowBasedFile::IsOpen; using RowBasedFile::NRows; using RowBasedFile::ReadHeader; using RowBasedFile::Stride; using RowBasedFile::WriteHeader; SimpleColumnarFile() noexcept = default; SimpleColumnarFile(const SimpleColumnarFile& rhs) = delete; SimpleColumnarFile(SimpleColumnarFile&& rhs) noexcept : packed_buffer_(std::move(rhs.packed_buffer_)) {} ~SimpleColumnarFile() noexcept = default; SimpleColumnarFile& operator=(SimpleColumnarFile&& rhs) { RowBasedFile::operator=(std::move(rhs)); std::swap(packed_buffer_, rhs.packed_buffer_); return *this; } static SimpleColumnarFile CreateNew(const std::string& filename, uint64_t header_size, uint64_t stride) { return SimpleColumnarFile(filename, header_size, stride); } static SimpleColumnarFile OpenExisting(const std::string& filename, size_t header_size) { return SimpleColumnarFile(filename, header_size); } void Read(uint64_t row, uint64_t column_offset, std::complex* data, uint64_t n) { ReadImplementation(row, column_offset, data, n); } void Read(uint64_t row, uint64_t column_offset, float* data, uint64_t n) { ReadImplementation(row, column_offset, data, n); } void Read(uint64_t row, uint64_t column_offset, double* data, uint64_t n) { ReadImplementation(row, column_offset, data, n); } void Read(uint64_t row, uint64_t column_offset, int32_t* data, uint64_t n) { ReadImplementation(row, column_offset, data, n); } void Read(uint64_t row, uint64_t column_offset, bool* data, uint64_t n) { const size_t byte_size = (n + 7) / 8; assert(column_offset + byte_size <= Stride()); if (row >= NRows()) { std::fill_n(data, n, false); } else { Seek(row * Stride() + column_offset + DataLocation(), SEEK_SET); ReadData(packed_buffer_.data(), byte_size); UnpackBoolArray(data, packed_buffer_.data(), n); } } void Write(uint64_t row, uint64_t column_offset, const std::complex* data, uint64_t n) { WriteImplementation(row, column_offset, data, n); } void Write(uint64_t row, uint64_t column_offset, const std::complex* data, uint64_t n) { WriteImplementation(row, column_offset, data, n); } void Write(uint64_t row, uint64_t column_offset, const float* data, uint64_t n) { WriteImplementation(row, column_offset, data, n); } void Write(uint64_t row, uint64_t column_offset, const double* data, uint64_t n) { WriteImplementation(row, column_offset, data, n); } void Write(uint64_t row, uint64_t column_offset, const int32_t* data, uint64_t n) { WriteImplementation(row, column_offset, data, n); } void Write(uint64_t row, uint64_t column_offset, const bool* data, uint64_t n) { const size_t byte_size = (n + 7) / 8; assert(column_offset + byte_size <= Stride()); PackBoolArray(packed_buffer_.data(), data, n); Seek(row * Stride() + column_offset + DataLocation(), SEEK_SET); WriteData(packed_buffer_.data(), byte_size); SetNRows(std::max(row + 1, NRows())); } /** * Set the number of bytes per row for this file. This changes the format * of the file, and because of this the file is emptied. */ void SetStride(uint64_t new_stride) { RowBasedFile::SetStride(new_stride); packed_buffer_.resize((new_stride + 7) / 8); } private: // Create or overwrite a new columnar file on disk SimpleColumnarFile(const std::string& filename, uint64_t header_size, uint64_t stride) : RowBasedFile(filename, header_size, stride), packed_buffer_((stride + 7) / 8) {} // Open an existing columnar file SimpleColumnarFile(const std::string& filename, size_t header_size) : RowBasedFile(filename, header_size) { packed_buffer_.resize((Stride() + 7) / 8); } template void ReadImplementation(uint64_t row, uint64_t column_offset, ValueType* data, uint64_t n) { assert(column_offset + n * sizeof(ValueType) <= Stride()); if (row >= NRows()) { std::fill_n(data, n, ValueType()); } else { Seek(row * Stride() + column_offset + DataLocation(), SEEK_SET); ReadData(reinterpret_cast(data), n * sizeof(ValueType)); } } template void WriteImplementation(uint64_t row, uint64_t column_offset, const ValueType* data, uint64_t n) { assert(column_offset + n * sizeof(ValueType) <= Stride()); Seek(row * Stride() + column_offset + DataLocation(), SEEK_SET); WriteData(reinterpret_cast(data), n * sizeof(ValueType)); SetNRows(std::max(row + 1, NRows())); } /** * This buffer is used temporarily for (un)packing booleans. Storing it as a * member avoids memory allocations. */ std::vector packed_buffer_; }; } // namespace casacore #endif casacore-3.7.1/tables/AlternateMans/StokesIStMan.cc000066400000000000000000000105211476623553700221660ustar00rootroot00000000000000#include "StokesIStMan.h" #include "StokesIStManColumn.h" void register_stokesistman() { casacore::StokesIStMan::registerClass(); } namespace casacore { namespace { /** * Create an object with given name and spec. * This methods gets registered in the StokesIStMan "constructor" map. * The caller has to delete the object. */ static casacore::DataManager *Make(const casacore::String &name, const casacore::Record &spec) { return new StokesIStMan(name, spec); } } StokesIStMan::StokesIStMan(const casacore::String &/*name*/, const casacore::Record &/*spec*/) : DataManager() { } StokesIStMan::StokesIStMan(const StokesIStMan &source) : DataManager(), name_(source.name_) {} StokesIStMan::~StokesIStMan() noexcept = default; casacore::Record StokesIStMan::dataManagerSpec() const { return casacore::Record(); } void StokesIStMan::registerClass() { DataManager::registerCtor("StokesIStMan", Make); } void StokesIStMan::create64(casacore::rownr_t nRow) { file_ = BufferedColumnarFile::CreateNew(fileName(), kHeaderSize, CalculateAndUpdateStride()); unsigned char data[kHeaderSize]; std::copy_n(kMagicHeaderTag, 8, data); file_.WriteHeader(data); file_.AddRows(nRow); } casacore::rownr_t StokesIStMan::open64(casacore::rownr_t /*n_row*/, casacore::AipsIO &) { file_ = BufferedColumnarFile::OpenExisting(fileName(), kHeaderSize); unsigned char data[kHeaderSize]; file_.ReadHeader(data); if (!std::equal(data, data + 8, kMagicHeaderTag)) { throw std::runtime_error( "The Stokes I columnar file header does not have the expected tag for Stokes I " "columns: the measurement set may be damaged"); } return file_.NRows(); } casacore::DataManagerColumn *StokesIStMan::makeScalarColumn( const casacore::String & /*name*/, int dataType, const casacore::String &dataTypeID) { std::ostringstream s; s << "Can not create scalar columns with StokesIStMan! (requested datatype: '" << dataTypeID << "' (" << dataType << ")"; throw std::runtime_error(s.str()); } casacore::DataManagerColumn *StokesIStMan::makeDirArrColumn( const casacore::String & /*name*/, int dataType, const casacore::String & /*dataTypeID*/) { if (dataType == casacore::TpFloat || dataType == casacore::TpComplex || dataType == casacore::TpBool) return columns_.emplace_back(std::make_unique(*this, file_, static_cast(dataType))).get(); else throw std::runtime_error( "Trying to create a Stokes I column with wrong type"); } casacore::DataManagerColumn *StokesIStMan::makeIndArrColumn( const casacore::String & /*name*/, int /*dataType*/, const casacore::String & /*dataTypeID*/) { throw std::runtime_error( "makeIndArrColumn() called on StokesIStMan. StokesIStMan can only create " "direct columns!\nUse casacore::ColumnDesc::Direct as option in the " "column desc constructor"); } casacore::rownr_t StokesIStMan::resync64(casacore::rownr_t nRow) { return nRow; } void StokesIStMan::deleteManager() { unlink(fileName().c_str()); } void StokesIStMan::prepare() {} void StokesIStMan::reopenRW() {} void StokesIStMan::addRow64(casacore::rownr_t nrrow) { file_.AddRows(nrrow); } void StokesIStMan::removeRow64(casacore::rownr_t rowNr) { if (rowNr != file_.NRows() - 1) throw std::runtime_error( "Trying to remove a row in the middle of the file: " "the StokesIStMan does not support this"); file_.DeleteRow(); } void StokesIStMan::addColumn(casacore::DataManagerColumn * /*column*/) { throw std::runtime_error( "Can't add generic columns to StokesIStMan"); } void StokesIStMan::removeColumn(casacore::DataManagerColumn *column) { for (std::vector>::iterator i = columns_.begin(); i != columns_.end(); ++i) { if (i->get() == column) { columns_.erase(i); file_.SetStride(CalculateAndUpdateStride()); return; } } throw std::runtime_error( "Trying to remove column that was not part of the storage manager"); } uint64_t StokesIStMan::CalculateAndUpdateStride() { uint64_t offset = 0; for (std::unique_ptr& column : columns_) { column->setOffset(offset); offset += column->getStoredSizeInBytes(); } return offset; } } // namespace casacore casacore-3.7.1/tables/AlternateMans/StokesIStMan.h000066400000000000000000000120031476623553700220250ustar00rootroot00000000000000#ifndef CASACORE_STOKES_I_STORAGE_MANAGER_H_ #define CASACORE_STOKES_I_STORAGE_MANAGER_H_ #include #include #include "BufferedColumnarFile.h" #ifndef DOXYGEN_SHOULD_SKIP_THIS extern "C" { #endif void register_stokesistman(); #ifndef DOXYGEN_SHOULD_SKIP_THIS } #endif namespace casacore { class StokesIStManColumn; /** * The Stokes I storage manager behaves like a full set of (4) polarizations but * only stores the Stokes I value on disk. */ class StokesIStMan final : public casacore::DataManager { public: /** * This constructor is called by Casacore when it needs to create a * StokesIStMan. When Casacore loads an StokesIStMan for an existing MS, the * "spec" parameter will be empty, thus the class should initialize its * properties by reading them from the file. The @p spec is used to make a new * storage manager with specs similar to another one. * @param name Name of this storage manager. * @param spec Specs to initialize this class with. */ StokesIStMan(const casacore::String &name, const casacore::Record &spec); /** * Copy constructor that initializes a storage manager with similar specs. * The columns are not copied: the new manager will be empty. */ StokesIStMan(const StokesIStMan &source); ~StokesIStMan() noexcept; /** * This StokesIStMan takes the settings of the source (but not the * columns and/or data). */ StokesIStMan &operator=(const StokesIStMan &source) = delete; casacore::DataManager *clone() const final { return new StokesIStMan(*this); } /** * Create an object with given name and spec. * This methods gets registered in the DataManager "constructor" map. * The caller has to delete the object. New class will be * initialized via @ref StokesIStMan(const casacore::String& name, const * casacore::Record& spec). * @returns A StokesIStMan with given specs. */ static casacore::DataManager *makeObject(const casacore::String &name, const casacore::Record &spec) { return new StokesIStMan(name, spec); } casacore::String dataManagerType() const final { return "StokesIStMan"; } casacore::Record dataManagerSpec() const final; casacore::Bool canAddRow() const final { return true; } casacore::Bool canRemoveRow() const final { return true; } casacore::Bool canAddColumn() const final { return true; } casacore::Bool canRemoveColumn() const final { return true; } /** * This function makes the StokesIStMan known to Casacore. */ static void registerClass(); protected: private: friend class StokesIStManColumn; uint64_t CalculateAndUpdateStride(); casacore::Bool flush(casacore::AipsIO &, [[maybe_unused]] casacore::Bool doFsync) final { return false; } // Let the storage manager create files as needed for a new table. // This allows a column with an indirect array to create its file. void create64(casacore::rownr_t nRow) final; // Open the storage manager file for an existing table. // Return the number of rows in the data file. casacore::rownr_t open64(casacore::rownr_t nRow, casacore::AipsIO &) final; // Create a column in the storage manager on behalf of a table column. // The caller will NOT delete the newly created object. // Create a scalar column. casacore::DataManagerColumn *makeScalarColumn( const casacore::String &name, int dataType, const casacore::String &dataTypeID) final; // Create a direct array column. casacore::DataManagerColumn *makeDirArrColumn( const casacore::String &name, int dataType, const casacore::String &dataTypeID) final; // Create an indirect array column. casacore::DataManagerColumn *makeIndArrColumn( const casacore::String &name, int dataType, const casacore::String &dataTypeID) final; casacore::rownr_t resync64(casacore::rownr_t nRow) final; void deleteManager() final; // Prepare the columns, let the data manager initialize itself further. // Prepare is called after create/open has been called for all // columns. In this way one can be sure that referenced columns // are read back and partly initialized. void prepare() final; // Reopen the storage manager files for read/write. void reopenRW() final; // Add rows to the storage manager. void addRow64(casacore::rownr_t nrrow) final; // Delete a row from all columns. void removeRow64(casacore::rownr_t row_nr) final; // Do the final addition of a column. void addColumn(casacore::DataManagerColumn *) final; // Remove a column from the data file. void removeColumn(casacore::DataManagerColumn *) final; std::string name_; // The item-type needs to be a pointer, because casacore::StManColumn // does not have move construct/assignment. std::vector> columns_; BufferedColumnarFile file_; constexpr static size_t kHeaderSize = 8; constexpr static const char kMagicHeaderTag[8] = "StkIcol"; }; } // namespace casacore #endif casacore-3.7.1/tables/AlternateMans/StokesIStManColumn.h000066400000000000000000000162221476623553700232120ustar00rootroot00000000000000#ifndef CASACORE_STOKES_I_ST_MAN_COLUMN_H_ #define CASACORE_STOKES_I_ST_MAN_COLUMN_H_ #include #include #include #include "BufferedColumnarFile.h" #include namespace casacore { class StokesIStMan; /** * Expands @p n values from single Stokes I values * to have 4 values, in place. This implies the array * should have place to store n*4 values. */ template void ExpandFromStokesI(T *data, size_t n) { for (size_t i = 0; i != n; ++i) { const size_t index = n - i - 1; const T value = data[index]; data[index * 4] = value; data[index * 4 + 1] = T(); data[index * 4 + 2] = T(); data[index * 4 + 3] = value; } } template <> void ExpandFromStokesI(bool *data, size_t n) { for (size_t i = 0; i != n; ++i) { const size_t index = n - i - 1; const bool value = data[index]; data[index * 4] = value; data[index * 4 + 1] = value; data[index * 4 + 2] = value; data[index * 4 + 3] = value; } } /** * Calculates for every set of 4 input values the Stokes-I values by * doing out = 0.5 * (in_pp + in_qq), where pp/qq can be xx/yy or * ll/rr. If pq or qp is non-zero, an exception is thrown. * * If type T is bool, then out = in_pp || in_qq. */ template inline T *TransformToStokesI(const T *input, char *buffer, size_t n) { for (size_t i = 0; i != n; ++i) { // Placement new is used, because the lifetime of type T needs // to be started. new (&buffer[i * sizeof(T)]) T((input[i * 4] + input[i * 4 + 3]) * T(0.5)); if (input[i * 4 + 1] != T(0.0) || input[i * 4 + 2] != T(0.0)) throw std::runtime_error( "Stokes-I stman cannot store data for which the 2nd and 3rd " "correlation are non-zero"); // While we could also check whether pp == qq, this is a bit more // complicated because of rounding inaccuracies. The above check should // catch the most crucial misuse of the stman, so a pp == qq check is not // performed. } return reinterpret_cast(buffer); } template <> inline bool *TransformToStokesI(const bool *input, char *buffer, size_t n) { for (size_t i = 0; i != n; ++i) { const bool a = input[i * 4]; const bool b = input[i * 4 + 1]; const bool c = input[i * 4 + 2]; const bool d = input[i * 4 + 3]; if (a != b || a != c || a != d) throw std::runtime_error( "Stokes-I stman cannot store boolean data for which not all " "correlations are equal"); new (&buffer[i]) bool(a); } return reinterpret_cast(buffer); } /** * Base class for columns of the StokesIStMan. * @author André Offringa */ class StokesIStManColumn final : public casacore::StManColumn { public: /** * Constructor, to be overloaded by subclass. * @param parent The parent stman to which this column belongs. * @param dtype The column's type as defined by Casacore. */ explicit StokesIStManColumn(StokesIStMan &parent, BufferedColumnarFile &file, casacore::DataType dtype) : casacore::StManColumn(dtype), parent_(parent), file_(file) {} /** * Whether this column is writable * @returns @c true */ casacore::Bool isWritable() const final { return true; } /** Set the dimensions of values in this column. */ void setShapeColumn(const casacore::IPosition &shape) final { if (shape.size() != 2) { throw std::runtime_error("StokesIStMan is used for a column with " + std::to_string(shape.size()) + " dimensions, but it can only be used for " "columns with exactly 2 dimensions"); } shape_ = shape; updateStride(); } /** Get the dimensions of the values in a particular row. * @param rownr The row to get the shape for. */ casacore::IPosition shape(casacore::uInt) final { return shape_; } casacore::IPosition shape(casacore::rownr_t) final { return shape_; } const casacore::IPosition &shape() const { return shape_; } /** * Read the values for a particular row. * @param rowNr The row number to get the values for. * @param dataPtr The array of values. */ void getArrayComplexV(casacore::uInt rowNr, casacore::Array *dataPtr) final { getArrayGeneric(rowNr, dataPtr); } void getArrayfloatV(casacore::uInt rowNr, casacore::Array *dataPtr) final { getArrayGeneric(rowNr, dataPtr); } void getArrayBoolV(casacore::uInt rowNr, casacore::Array *dataPtr) final { getArrayGeneric(rowNr, dataPtr); } /** * Write values into a particular row. * @param rowNr The row number to write the values to. * @param dataPtr The data pointer. */ void putArrayComplexV( casacore::uInt rowNr, const casacore::Array *dataPtr) final { putArrayGeneric(rowNr, dataPtr); } void putArrayDComplexV( casacore::uInt rowNr, const casacore::Array *dataPtr) final { putArrayGeneric(rowNr, dataPtr); } void putArraydoubleV(casacore::uInt rowNr, const casacore::Array *dataPtr) final { putArrayGeneric(rowNr, dataPtr); } void putArrayfloatV(casacore::uInt rowNr, const casacore::Array *dataPtr) final { putArrayGeneric(rowNr, dataPtr); } void putArrayBoolV(casacore::uInt rowNr, const casacore::Array *dataPtr) final { putArrayGeneric(rowNr, dataPtr); } void setOffset(uint64_t column_offset) { column_offset_ = column_offset; } uint64_t getStoredSizeInBytes() const { if (dtype() == casacore::TpBool) { return (shape_[1] + 7) / 8; } else { const uint64_t type_size = SizeOfType(dtype()); return shape_[1] * type_size; } } private: StokesIStManColumn(const StokesIStManColumn &source) = delete; void operator=(const StokesIStManColumn &source) = delete; template void getArrayGeneric(casacore::uInt rowNr, casacore::Array *dataPtr) { bool ownership; T *storage = dataPtr->getStorage(ownership); const size_t n_values = shape_[1]; file_.Read(rowNr, column_offset_, storage, n_values); ExpandFromStokesI(storage, n_values); dataPtr->putStorage(storage, ownership); } template void putArrayGeneric(casacore::uInt rowNr, const casacore::Array *dataPtr) { bool ownership; const T *storage = dataPtr->getStorage(ownership); const size_t n_values = shape_[1]; buffer_.resize(n_values * sizeof(T)); T *buffer = TransformToStokesI(storage, buffer_.data(), n_values); file_.Write(rowNr, column_offset_, buffer, n_values); dataPtr->freeStorage(storage, ownership); } void updateStride(); StokesIStMan &parent_; BufferedColumnarFile &file_; IPosition shape_; uint64_t column_offset_; std::vector buffer_; }; } // namespace casacore #include "StokesIStMan.h" namespace casacore { inline void StokesIStManColumn::updateStride() { parent_.CalculateAndUpdateStride(); } } // namespace casacore #endif casacore-3.7.1/tables/AlternateMans/UvwFile.h000066400000000000000000000355401476623553700210750ustar00rootroot00000000000000#ifndef CASACORE_UVW_FILE_H_ #define CASACORE_UVW_FILE_H_ #include "BufferedColumnarFile.h" #include #include #include #include #include #include namespace casacore { /** * Stores values of a UVW column in a compressed way. Instead of storing a UVW * value per baseline, it stores one per antenna. It uses the fact that: * * baseline_uvw = antenna2_uvw - antenna1_uvw. * * By taking the first written antenna as reference antenna and storing the * relative UVW distance of the other antennas towards the reference antenna, * the baseline uvws can be reconstructed. * * A small downside is that this requires the measurement set to be "reasonably" * ordered: every timestep should have the same baselines in the same order. * Also, it requires the baselines to be ordered such that a relation to the * reference antenna can be made. A measurement set that is ordered either by * antenna1 or antenna2 for which no baselines have been removed satisfies this. * Removing an entire antenna is fine. * * Example of acceptable antenna lists: * - [0,0] ; [0,1] ; [0,2] ; [1,1] ; [1,2] ; [2,2] * Ordered by antenna1, no missing baselines, no missing antennas. * - [0,1] ; [0,2] ; [1,2] * Ordered by antenna1, only auto-correlations are missing, no missing * antennas. * - [0,0] ; [1,0] ; [2,0] ; [1,1] ; [2,1] ; [2,2] * Ordered by antenna2, only auto-correlations are missing, no missing * antennas. * - [1,3] ; [1,4] ; [3,4] * Ordered by antenna1, misses antenna 0 and 2. * * An example of a baseline ordering that is not accepted: * - [1, 2] ; [3, 4] * The class will use 1 as reference antenna and calculate antenna 2 relative * to that. However, it can't store 3 or 4 relative to 1. */ class UvwFile { public: UvwFile() noexcept = default; UvwFile(UvwFile&& source) noexcept : file_(std::move(source.file_)), n_rows_(source.n_rows_), rows_per_block_(source.rows_per_block_), active_block_(source.active_block_), reference_antenna_(source.reference_antenna_), start_antenna_2_(source.start_antenna_2_), n_antennas_(source.n_antennas_), block_uvws_(std::move(source.block_uvws_)), block_is_changed_(source.block_is_changed_) { source.n_rows_ = 0; source.rows_per_block_ = 0; source.active_block_ = 0; source.reference_antenna_ = 0; source.start_antenna_2_ = 0; source.n_antennas_ = 0; source.block_uvws_.clear(); source.block_is_changed_ = false; } ~UvwFile() noexcept { Close(); } UvwFile& operator=(UvwFile&& rhs) { Close(); file_ = std::move(rhs.file_); n_rows_ = rhs.n_rows_; rows_per_block_ = rhs.rows_per_block_; active_block_ = rhs.active_block_; reference_antenna_ = rhs.reference_antenna_; start_antenna_2_ = rhs.start_antenna_2_; n_antennas_ = rhs.n_antennas_; block_uvws_ = std::move(rhs.block_uvws_); block_is_changed_ = rhs.block_is_changed_; rhs.n_rows_ = 0; rhs.rows_per_block_ = 0; rhs.active_block_ = 0; rhs.reference_antenna_ = 0; rhs.start_antenna_2_ = 0; rhs.n_antennas_ = 0; rhs.block_uvws_.clear(); rhs.block_is_changed_ = false; return *this; } /** * Create a new UVW file on disk with the given filename. */ static UvwFile CreateNew(const std::string& filename) { return UvwFile(filename); } /** * Open an already existing UVW file from disk with the given filename. */ static UvwFile OpenExisting(const std::string& filename) { return UvwFile(filename, true); } /** * Write a single row to the column. This has to be done in a reasonable * order; see the class description. * @param row denotes the row in the Casacore measurement set to be read. */ void WriteUvw(uint64_t row, size_t antenna1, size_t antenna2, const double* uvw) { assert(file_.IsOpen()); if (row > n_rows_) { throw std::runtime_error( "Uvw data must be written in order (writing row " + std::to_string(row) + ", after writing " + std::to_string(n_rows_) + " rows)"); } // The row/block is zero when there's not yet a full block written. if (rows_per_block_ == 0) { if (row == 0) { reference_antenna_ = antenna1; start_antenna_2_ = antenna2; n_antennas_ = std::max(antenna1, antenna2) + 1; block_uvws_.resize(n_antennas_, kUnsetPosition); block_uvws_[reference_antenna_] = {0.0, 0.0, 0.0}; } else if (antenna1 == reference_antenna_ && antenna2 == start_antenna_2_) { // This baseline is the first baseline of a new block, so the block size // can be determined rows_per_block_ = n_rows_; n_antennas_ = block_uvws_.size(); WriteHeader(); ActivateBlock(1); } else { n_antennas_ = std::max({antenna1 + 1, antenna2 + 1, n_antennas_}); } } else { const uint64_t block = row / rows_per_block_; ActivateBlock(block); } if (antenna1 != antenna2) { if (antenna1 == reference_antenna_) { // baseline = a2 - a1 with a1 = 0 const std::array ant2_uvw{uvw[0], uvw[1], uvw[2]}; StoreOrCheck(antenna2, ant2_uvw); } else if (antenna2 == reference_antenna_) { // baseline = a2 - a1 with a2 = 0 const std::array ant1_uvw{-uvw[0], -uvw[1], -uvw[2]}; StoreOrCheck(antenna1, ant1_uvw); } else if (IsSet(antenna1)) { // baseline = a2 - a1. Given a1: // a2 = baseline + a1 const std::array ant1_uvw = block_uvws_[antenna1]; const std::array ant2_uvw{ uvw[0] + ant1_uvw[0], uvw[1] + ant1_uvw[1], uvw[2] + ant1_uvw[2]}; StoreOrCheck(antenna2, ant2_uvw); } else if (IsSet(antenna2)) { // baseline = a2 - a1. Given a2: // a1 = a2 - baseline const std::array ant2_uvw = block_uvws_[antenna2]; const std::array ant1_uvw{ ant2_uvw[0] - uvw[0], ant2_uvw[1] - uvw[1], ant2_uvw[2] - uvw[2]}; StoreOrCheck(antenna1, ant1_uvw); } else { throw std::runtime_error( "Baselines are written in a non-ordered way: they need to be " "ordered either by antenna 1 or by antenna 2"); } } n_rows_ = std::max(n_rows_, row + 1); } /** * Read a single row. This may be done in random order, but is most efficient * when reading a file contiguously. * @param row denotes the row in the Casacore measurement set to be read. */ void ReadUvw(uint64_t row, size_t antenna1, size_t antenna2, double* uvw) { assert(file_.IsOpen()); if (row >= n_rows_ || antenna1 >= n_antennas_ || antenna2 >= n_antennas_) { throw std::runtime_error( "Invalid read for Uvw data: row " + std::to_string(row) + ", baseline (" + std::to_string(antenna1) + ", " + std::to_string(antenna2) + ") was requested. File has only " + std::to_string(n_rows_) + " rows."); } if (rows_per_block_ != 0) { const uint64_t block = row / rows_per_block_; ActivateBlock(block); } uvw[0] = block_uvws_[antenna2][0] - block_uvws_[antenna1][0]; uvw[1] = block_uvws_[antenna2][1] - block_uvws_[antenna1][1]; uvw[2] = block_uvws_[antenna2][2] - block_uvws_[antenna1][2]; } void Close() { if (file_.IsOpen()) { if (rows_per_block_ == 0) { rows_per_block_ = n_rows_; n_antennas_ = block_uvws_.size(); WriteHeader(); } if (block_is_changed_) { WriteActiveBlock(); } file_.Close(); } } uint64_t NRows() const { return n_rows_; } std::string Filename() const { return file_.Filename(); } private: /** * Create a new file on disk. */ UvwFile(const std::string& filename) : file_(BufferedColumnarFile::CreateNew(filename, kHeaderSize, sizeof(double) * 3)) {} /** * Open an existing file from disk. The last parameter is a dummy * parameter to distinguish it from the creating constructor. */ UvwFile(const std::string& filename, bool /*open existing*/) : file_(BufferedColumnarFile::OpenExisting(filename, kHeaderSize)) { ReadHeader(); active_block_ = std::numeric_limits::max(); if (n_antennas_ > 1) { if (file_.NRows() % (n_antennas_ - 1) != 0) { throw std::runtime_error( "Uvw file has an incorrect number of rows (" + std::to_string(file_.NRows()) + ", expecting multiple of " + std::to_string(n_antennas_ - 1) + "): file corrupted?"); } const uint64_t n_blocks = file_.NRows() / (n_antennas_ - 1); n_rows_ = n_blocks * rows_per_block_; } if (n_rows_ > 0 && n_antennas_ <= reference_antenna_) throw std::runtime_error( "Invalid combination of values for n_antenna and reference antenna " "in file: file damaged?"); // Setting the size of block_uvws_ here, saves an extra size check in // ActivateBlock(). block_uvws_.assign(n_antennas_, kUnsetPosition); } /** * If this block does not have a value for the specified antenna, store * the uvw value for it. If it does have a value, the value is checked. This * check has a relatively high tolerance (1e-5, fractionally) because it is * only there to catch "significant" errors caused by e.g. writing in the * wrong order. */ void StoreOrCheck(size_t antenna, const std::array& antenna_uvw) { if (IsSet(antenna)) { if (!AreNear(block_uvws_[antenna], antenna_uvw)) { std::ostringstream msg; msg << "Inconsistent UVW value written for antenna " << antenna << ": old value is " << UvwAsString(block_uvws_[antenna]) << ", new value is " << UvwAsString(antenna_uvw) << "."; throw std::runtime_error(msg.str()); } } else { if (block_uvws_.size() <= antenna) block_uvws_.resize(antenna + 1, kUnsetPosition); block_uvws_[antenna] = antenna_uvw; block_is_changed_ = true; } } void ActivateBlock(size_t block) { if (block != active_block_) { if (block_is_changed_) { WriteActiveBlock(); } active_block_ = block; ReadActiveBlock(); } } void ReadActiveBlock() { const uint64_t block_start_row = (n_antennas_ - 1) * active_block_; if (block_start_row < file_.NRows()) { block_uvws_.clear(); for (size_t antenna = 0; antenna != n_antennas_; ++antenna) { if (antenna != reference_antenna_) { const uint64_t row = antenna < reference_antenna_ ? block_start_row + antenna : block_start_row + antenna - 1; std::array& uvw = block_uvws_.emplace_back(); file_.Read(row, 0, uvw.data(), 3); } else { block_uvws_.emplace_back(std::array{0.0, 0.0, 0.0}); } } } else { std::fill(block_uvws_.begin(), block_uvws_.end(), kUnsetPosition); block_uvws_[reference_antenna_] = {0.0, 0.0, 0.0}; } } void WriteActiveBlock() { if (block_uvws_.size() != n_antennas_) throw std::runtime_error("Trying to write an incomplete UVW block"); const uint64_t block_start_row = (n_antennas_ - 1) * active_block_; for (size_t antenna = 0; antenna != n_antennas_; ++antenna) { if (antenna != reference_antenna_) { const uint64_t row = antenna < reference_antenna_ ? block_start_row + antenna : block_start_row + antenna - 1; file_.Write(row, 0, block_uvws_[antenna].data(), 3); } } block_is_changed_ = false; } void ReadHeader() { unsigned char data[kHeaderSize]; file_.ReadHeader(data); if (!std::equal(data, data + 8, kMagicHeaderTag)) { throw std::runtime_error( "The UVW columnar file header not have the expected tag for UVW " "columns: the measurement set may be damaged"); } rows_per_block_ = reinterpret_cast(data[8]); reference_antenna_ = reinterpret_cast(data[16]); n_antennas_ = reinterpret_cast(data[24]); } void WriteHeader() { unsigned char data[kHeaderSize]; std::copy_n(kMagicHeaderTag, 8, data); reinterpret_cast(data[8]) = rows_per_block_; reinterpret_cast(data[16]) = reference_antenna_; reinterpret_cast(data[24]) = n_antennas_; file_.WriteHeader(data); } bool IsSet(size_t antenna) const { return block_uvws_.size() > antenna && block_uvws_[antenna] != kUnsetPosition; } static bool AreNear(std::array a, std::array b) { return AreNear(a[0], b[0]) && AreNear(a[1], b[1]) && AreNear(a[2], b[2]); } static bool AreNear(double a, double b) { const double magnitude = std::max({1e-5, std::fabs(a), std::fabs(b)}); return (std::fabs(a - b) / magnitude) < 1e-5; } static std::string UvwAsString(const std::array& uvw) { std::ostringstream str; str << "[" << uvw[0] << ", " << uvw[1] << ", " << uvw[2] << "]"; return str.str(); } /** * The header: * char[8] "Uvw-col\0" (=kMagicHeaderTag) * uint64_t rows_per_block * uint64_t reference_antenna * uint64_t n_antenna */ constexpr static size_t kHeaderSize = 32; constexpr static const char kMagicHeaderTag[8] = "Uvw-col"; constexpr static std::array kUnsetPosition = { std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max()}; BufferedColumnarFile file_; /** * Number of rows in the Uvw column. It is increased when writing a new * Uvw row using @ref WriteUvw(). This concept of a "row" is different * from a row in the BufferedColumnarFile (i.e., @c file_ ), as for each * block (see below), only the uvw per antenna are stored, and these form * the rows in that file. */ uint64_t n_rows_ = 0; /** * A "block" is a contiguous number of baselines that together form * one timestep. This is the same as saying they form one (triangular) * correlation matrix. It can have auto-correlation but this is not * required. A block may have missing antennas, but has some constraints * on the ordering; see the class description. Per block, the UVW values * are calculated per antenna. This value specifies the number of table * rows in one block, i.e. the number of set elements in the correlation * matrix. */ uint64_t rows_per_block_ = 0; uint64_t active_block_ = 0; size_t reference_antenna_ = 0; // This value is used to determine the first baseline in the data, which is // the baseline (reference_antenna_, start_antenna_2_). size_t start_antenna_2_ = 0; size_t n_antennas_ = 0; // UVW for each antenna in the block std::vector> block_uvws_; bool block_is_changed_ = false; }; } // namespace casacore #endif casacore-3.7.1/tables/AlternateMans/UvwStMan.cc000066400000000000000000000027501476623553700213730ustar00rootroot00000000000000 #include "UvwStMan.h" #include "UvwStManColumn.h" namespace casacore { UvwStMan::UvwStMan(const String &, const Record &) : DataManager() { } UvwStMan::UvwStMan(const UvwStMan &source) : DataManager(), name_(source.name_) {} UvwStMan::~UvwStMan() noexcept = default; void UvwStMan::create64(rownr_t /*nRow*/) { file_ = UvwFile::CreateNew(fileName()); } rownr_t UvwStMan::open64(rownr_t /*n_row*/, AipsIO &) { file_ = UvwFile::OpenExisting(fileName()); return file_.NRows(); } DataManagerColumn *UvwStMan::makeDirArrColumn( const String & /*name*/, int dataType, const String & /*dataTypeID*/) { if (dataType == TpDouble) { column_ = std::make_unique(file_); return column_.get(); } else { throw std::runtime_error( "Trying to create a Uvw column with wrong type"); } } void UvwStMan::deleteManager() { unlink(fileName().c_str()); } void UvwStMan::prepare() { if(column_) column_->Prepare(table()); } void UvwStMan::addRow64(rownr_t) { } void UvwStMan::removeRow64(rownr_t) { throw std::runtime_error( "Can't remove rows from a UvwStMan"); } void UvwStMan::addColumn(DataManagerColumn*) { throw std::runtime_error( "Can't add generic columns to UvwStMan"); } void UvwStMan::removeColumn(DataManagerColumn *column) { if(column_.get() == column) { column_.reset(); } throw std::runtime_error( "Trying to remove column that was not part of the storage manager"); } } // namespace casacore casacore-3.7.1/tables/AlternateMans/UvwStMan.h000066400000000000000000000054111476623553700212320ustar00rootroot00000000000000#ifndef CASACORE_UVW_STORAGE_MANAGER_H_ #define CASACORE_UVW_STORAGE_MANAGER_H_ #include #include #include "UvwFile.h" #include #include namespace casacore { class UvwStManColumn; /** * A storage manager that saves the UVW with (lossless) compression. * Because this storage manager needs to know the ANTENNA1 and * ANTENNA2 values, the UVW values can only be written after writing * the antenna values for a row. * @see UvwFile for how the compression works and the requirements. */ class UvwStMan final : public DataManager { public: UvwStMan(const String &, const Record &); /** * The columns are not copied: the new manager will be empty. */ UvwStMan(const UvwStMan &source); ~UvwStMan() noexcept; UvwStMan &operator=(const UvwStMan &source) = delete; DataManager *clone() const final { return new UvwStMan(*this); } static DataManager *makeObject(const String &name, const Record &spec) { return new UvwStMan(name, spec); } String dataManagerType() const final { return "UvwStMan"; } Record dataManagerSpec() const final { return Record(); } private: Bool flush(AipsIO &, Bool) final { return false; } void create64(rownr_t nRow) final; rownr_t open64(rownr_t nRow, AipsIO &) final; DataManagerColumn *makeScalarColumn(const String &, int, const String &) final { throw std::runtime_error( "makeScalarColumn() called on a UvwStMan. UvwStMan can only make array " "columns"); } DataManagerColumn *makeDirArrColumn(const String &name, int dataType, const String &dataTypeID) final; DataManagerColumn *makeIndArrColumn(const String &, int, const String &) final { throw std::runtime_error( "makeIndArrColumn() called on a UvwStMan. UvwStMan can only make " "direct columns"); } rownr_t resync64(rownr_t nRow) final { return nRow; } void deleteManager() final; // Prepare is called after create/open has been called for all // columns. In this way one can be sure that referenced columns // are read back and partly initialized. void prepare() final; // Reopen the storage manager files for read/write. void reopenRW() final {} // Add rows to the storage manager. void addRow64(rownr_t nrrow) final; // Delete a row from all columns. void removeRow64(rownr_t row_nr) final; // Do the final addition of a column. void addColumn(DataManagerColumn *) final; // Remove a column from the data file. void removeColumn(DataManagerColumn *) final; std::string name_; std::unique_ptr column_; UvwFile file_; }; } // namespace casacore #endif casacore-3.7.1/tables/AlternateMans/UvwStManColumn.h000066400000000000000000000044201476623553700224070ustar00rootroot00000000000000#ifndef CASACORE_STOKES_I_ST_MAN_COLUMN_H_ #define CASACORE_STOKES_I_ST_MAN_COLUMN_H_ #include #include #include #include #include "UvwFile.h" #include namespace casacore { class UvwStManColumn final : public StManColumn { public: explicit UvwStManColumn(UvwFile &file) : StManColumn(DataType::TpDouble), file_(file) {} Bool isWritable() const final { return true; } /** Set the dimensions of values in this column. */ void setShapeColumn(const IPosition &shape) final { if (shape.size() != 1 || shape[0] != 3) { throw std::runtime_error( "UvwStMan can only be used for array columns with 1 dimension of " "size 3"); } } IPosition shape(uInt) final { return IPosition{3}; } IPosition shape(rownr_t) final { return IPosition{3}; } void getArrayV(rownr_t row, ArrayBase &dataPtr) final { Array &array = static_cast &>(dataPtr); bool ownership; double *storage = array.getStorage(ownership); const int antenna1 = antenna1_column_(row); const int antenna2 = antenna2_column_(row); file_.ReadUvw(row, antenna1, antenna2, storage); array.putStorage(storage, ownership); } /** * Write values into a particular row. * @param row The row number to write the values to. * @param dataPtr The data pointer. */ void putArrayV(rownr_t row, const ArrayBase &dataPtr) final { const Array &array = static_cast &>(dataPtr); bool ownership; const double *storage = array.getStorage(ownership); const int antenna1 = antenna1_column_(row); const int antenna2 = antenna2_column_(row); file_.WriteUvw(row, antenna1, antenna2, storage); array.freeStorage(storage, ownership); } void Prepare(Table &table) { antenna1_column_ = ScalarColumn(table, "ANTENNA1"); antenna2_column_ = ScalarColumn(table, "ANTENNA2"); } private: UvwStManColumn(const UvwStManColumn &source) = delete; void operator=(const UvwStManColumn &source) = delete; UvwFile &file_; ScalarColumn antenna1_column_; ScalarColumn antenna2_column_; }; } // namespace casacore #endif casacore-3.7.1/tables/AlternateMans/test/000077500000000000000000000000001476623553700203135ustar00rootroot00000000000000casacore-3.7.1/tables/AlternateMans/test/CMakeLists.txt000066400000000000000000000011041476623553700230470ustar00rootroot00000000000000set (testfiles tAntennaPairFile.cc tBitPacking.cc tColumnarFile.cc tStokesIStMan.cc tUvwFile.cc ) find_package(Boost COMPONENTS filesystem system unit_test_framework) if(Boost_FOUND) include_directories(${Boost_INCLUDE_DIR}) add_executable (altmantest ${testfiles}) add_pch_support(altmantest) target_link_libraries(altmantest casa_tables ${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) add_test (altmantest ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./altmantest) add_dependencies(check altmantest) endif(Boost_FOUND) casacore-3.7.1/tables/AlternateMans/test/tAntennaPairFile.cc000066400000000000000000000141711476623553700240120ustar00rootroot00000000000000#include #include using casacore::AntennaPairFile; namespace { const std::string kFilename = "antenna_pair_file_test.tmp"; } // namespace BOOST_AUTO_TEST_SUITE(antenna_pair_file) BOOST_AUTO_TEST_CASE(empty_file) { AntennaPairFile file; BOOST_CHECK_EQUAL(file.Filename(), ""); BOOST_CHECK_EQUAL(file.NRowsInPattern(), 0); file = AntennaPairFile::CreateNew(kFilename); BOOST_CHECK_EQUAL(file.Filename(), kFilename); BOOST_CHECK_EQUAL(file.NRowsInPattern(), 0); file.Close(); file = AntennaPairFile::OpenExisting(kFilename); BOOST_CHECK_EQUAL(file.Filename(), kFilename); BOOST_CHECK_EQUAL(file.NRowsInPattern(), 0); file.Close(); unlink(kFilename.c_str()); } BOOST_AUTO_TEST_CASE(conisistency_checking) { AntennaPairFile file = AntennaPairFile::CreateNew(kFilename); BOOST_CHECK_THROW(file.ReadAntenna1(0), std::runtime_error); BOOST_CHECK_THROW(file.ReadAntenna2(0), std::runtime_error); // If we don't start writing at the start, an exception should be thrown BOOST_CHECK_THROW(file.WriteAntenna1(2, 0), std::runtime_error); BOOST_CHECK_THROW(file.ReadAntenna1(0), std::runtime_error); BOOST_CHECK_THROW(file.ReadAntenna2(0), std::runtime_error); BOOST_CHECK_THROW(file.ReadAntenna1(2), std::runtime_error); BOOST_CHECK_THROW(file.ReadAntenna2(2), std::runtime_error); file.WriteAntenna1(0, 3); BOOST_CHECK_EQUAL(file.ReadAntenna1(0), 3); BOOST_CHECK_THROW(file.ReadAntenna2(0), std::runtime_error); // Rewriting a value with the same value should be allowed file.WriteAntenna1(0, 3); BOOST_CHECK_EQUAL(file.ReadAntenna1(0), 3); BOOST_CHECK_THROW(file.ReadAntenna2(0), std::runtime_error); // ...but not with a different value BOOST_CHECK_THROW(file.WriteAntenna1(0, 4), std::runtime_error); BOOST_CHECK_EQUAL(file.ReadAntenna1(0), 3); BOOST_CHECK_THROW(file.ReadAntenna2(0), std::runtime_error); // Writing the next row without finishing this one is not allowed // (this is because the pattern can not be discovered without both // values written -- one should not e.g. write first all ANTENNA1 // values, because then all values would have to be buffered). BOOST_CHECK_THROW(file.WriteAntenna1(1, 3), std::runtime_error); BOOST_CHECK_THROW(file.ReadAntenna1(1), std::runtime_error); BOOST_CHECK_THROW(file.ReadAntenna2(1), std::runtime_error); file.WriteAntenna2(0, 0); // This time write antenna 2 for the next row first file.WriteAntenna2(1, 1); BOOST_CHECK_EQUAL(file.ReadAntenna2(1), 1); BOOST_CHECK_THROW(file.ReadAntenna1(1), std::runtime_error); // Test rewriting antenna 2 file.WriteAntenna2(1, 1); BOOST_CHECK_EQUAL(file.ReadAntenna2(1), 1); BOOST_CHECK_THROW(file.ReadAntenna1(1), std::runtime_error); BOOST_CHECK_THROW(file.WriteAntenna2(1, 0), std::runtime_error); BOOST_CHECK_EQUAL(file.ReadAntenna2(1), 1); BOOST_CHECK_THROW(file.ReadAntenna1(1), std::runtime_error); // Writing next row is not allowed until antenna 1 is written BOOST_CHECK_THROW(file.WriteAntenna2(2, 2), std::runtime_error); BOOST_CHECK_THROW(file.ReadAntenna2(2), std::runtime_error); BOOST_CHECK_THROW(file.ReadAntenna1(2), std::runtime_error); file.WriteAntenna1(1, 3); // Check all values written so far BOOST_CHECK_EQUAL(file.ReadAntenna1(0), 3); BOOST_CHECK_EQUAL(file.ReadAntenna2(0), 0); BOOST_CHECK_EQUAL(file.ReadAntenna1(1), 3); BOOST_CHECK_EQUAL(file.ReadAntenna2(1), 1); // Finish the pattern by starting a new pattern with the same first row file.WriteAntenna2(2, 0); BOOST_CHECK_EQUAL(file.ReadAntenna2(2), 0); file.WriteAntenna1(2, 3); BOOST_CHECK_EQUAL(file.ReadAntenna1(2), 3); BOOST_CHECK_EQUAL(file.NRowsInPattern(), 2); // Writing further patterns should be allowed (as long as they are consistent) file.WritePair(3, 3, 1); file.WritePair(4, 3, 0); BOOST_CHECK_EQUAL(file.NRowsInPattern(), 2); // Writing inconsistent patterns should throw BOOST_CHECK_THROW(file.WriteAntenna1(5, 1), std::runtime_error); // should be 3 BOOST_CHECK_THROW(file.WriteAntenna2(5, 3), std::runtime_error); // should be 1 // Reading any further rows should return the same pattern BOOST_CHECK_EQUAL(file.ReadAntenna1(6), 3); BOOST_CHECK_EQUAL(file.ReadAntenna2(6), 0); BOOST_CHECK_EQUAL(file.ReadAntenna1(7), 3); BOOST_CHECK_EQUAL(file.ReadAntenna2(7), 1); file.Close(); unlink(kFilename.c_str()); } BOOST_AUTO_TEST_CASE(write_and_read) { // Define pirate language using Arr = std::array; const std::array data = { Arr{ 0, 1 }, Arr{ 0, 2 }, Arr{ 0, 3 }, Arr{ 0, 5 }, Arr{ 1, 2 }, Arr{ 1, 3 }, Arr{ 1, 5 }, Arr{ 2, 3 }, Arr{ 2, 5 }, Arr{ 3, 5 } }; { AntennaPairFile file = AntennaPairFile::CreateNew(kFilename); // Only write 4 lines, to see if we can continue an unfinished file for(size_t i=0; i!=4; ++i) { file.WritePair(i, data[i][0], data[i][1]); } } { AntennaPairFile file = AntennaPairFile::OpenExisting(kFilename); for(size_t i=0; i!=4; ++i) { BOOST_CHECK_EQUAL(file.ReadAntenna1(i), data[i][0]); BOOST_CHECK_EQUAL(file.ReadAntenna2(i), data[i][1]); } BOOST_CHECK_THROW(file.ReadAntenna1(4), std::runtime_error); BOOST_CHECK_THROW(file.ReadAntenna2(4), std::runtime_error); } { AntennaPairFile file = AntennaPairFile::OpenExisting(kFilename); uint64_t row = 0; const size_t repeat_count = 3; for(size_t repeat = 0; repeat != repeat_count; ++repeat) { for(const Arr& row_data : data) { file.WritePair(row, row_data[0], row_data[1]); ++row; } } BOOST_CHECK_EQUAL(file.NRowsInPattern(), data.size()); } { AntennaPairFile file = AntennaPairFile::OpenExisting(kFilename); BOOST_CHECK_EQUAL(file.NRowsInPattern(), data.size()); uint64_t row = 0; const size_t repeat_count = 4; for(size_t repeat = 0; repeat != repeat_count; ++repeat) { for(const Arr& row_data : data) { BOOST_CHECK_EQUAL(file.ReadAntenna1(row), row_data[0]); BOOST_CHECK_EQUAL(file.ReadAntenna2(row), row_data[1]); ++row; } } } unlink(kFilename.c_str()); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/tables/AlternateMans/test/tBitPacking.cc000066400000000000000000000016341476623553700230250ustar00rootroot00000000000000#include #include BOOST_AUTO_TEST_SUITE(bit_packing) BOOST_AUTO_TEST_CASE(pack_and_unpack) { BOOST_CHECK_NO_THROW(PackBoolArray(nullptr, nullptr, 0)); BOOST_CHECK_NO_THROW(UnpackBoolArray(nullptr, nullptr, 0)); const std::array input_a={true, true, false, true, false, false, true, false, true, false, true, false}; std::array packed_a={0xFF, 0xFF}; PackBoolArray(packed_a.data(), input_a.data(), input_a.size()); BOOST_CHECK_EQUAL(packed_a[0], 0b01001011); BOOST_CHECK_EQUAL(packed_a[1], 0b00000101); std::array unpacked_a; std::fill(unpacked_a.begin(), unpacked_a.end(), false); UnpackBoolArray(unpacked_a.data(), packed_a.data(), input_a.size()); BOOST_CHECK_EQUAL_COLLECTIONS(unpacked_a.begin(), unpacked_a.end(), input_a.begin(), input_a.end()); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/tables/AlternateMans/test/tColumnarFile.cc000066400000000000000000000234751476623553700234010ustar00rootroot00000000000000#define BOOST_TEST_MODULE alternate_mans #define BOOST_TEST_DYN_LINK // The support of std::filesystem in gcc13 was not sufficient to use std::filesystem::permissions(), so // boost is used. #include #include #include #include using casacore::VarBufferedColumnarFile; using casacore::SimpleColumnarFile; template void TestEmptyConstructor() { ColumnarFile file_a; BOOST_CHECK_EQUAL(file_a.NRows(), 0); BOOST_CHECK_EQUAL(file_a.Filename(), ""); BOOST_CHECK(!file_a.IsOpen()); const std::string filename = "columnar_file_test.tmp"; ColumnarFile file_b = ColumnarFile::CreateNew(filename, 0, 0); file_b = std::move(file_a); BOOST_CHECK_EQUAL(file_b.NRows(), 0); BOOST_CHECK_EQUAL(file_b.Filename(), ""); BOOST_CHECK(!file_b.IsOpen()); unlink(filename.c_str()); } template void TestCreateAndOpen() { constexpr size_t kStrideA = 10; const std::string filename = "columnar_file_test.tmp"; { ColumnarFile file = ColumnarFile::CreateNew(filename, 0, kStrideA); BOOST_CHECK_EQUAL(file.Stride(), kStrideA); BOOST_CHECK_EQUAL(file.NRows(), 0); BOOST_CHECK_EQUAL(file.Filename(), filename); BOOST_CHECK(file.IsOpen()); } BOOST_CHECK_THROW(ColumnarFile::OpenExisting("This-is-not-an-existing-filename.nope", 0), std::runtime_error); // See if re-creating works constexpr size_t kStrideB = 20; ColumnarFile file = ColumnarFile::CreateNew(filename, 0, kStrideB); BOOST_CHECK_EQUAL(file.Stride(), kStrideB); BOOST_CHECK_EQUAL(file.NRows(), 0); BOOST_CHECK_EQUAL(file.Filename(), filename); BOOST_CHECK(file.IsOpen()); file.AddRows(43); BOOST_CHECK_EQUAL(file.NRows(), 43); file.DeleteRow(); BOOST_CHECK_EQUAL(file.NRows(), 42); BOOST_CHECK(file.IsOpen()); // Close by assigning to empty file = ColumnarFile(); file = ColumnarFile::OpenExisting(filename, 0); BOOST_CHECK_EQUAL(file.Stride(), kStrideB); BOOST_CHECK_EQUAL(file.NRows(), 42); BOOST_CHECK_EQUAL(file.Filename(), filename); BOOST_CHECK(file.IsOpen()); file.AddRows(8); BOOST_CHECK_EQUAL(file.NRows(), 50); file.Close(); BOOST_CHECK(!file.IsOpen()); // Check if update worked file = ColumnarFile::OpenExisting(filename, 0); BOOST_CHECK_EQUAL(file.Stride(), kStrideB); BOOST_CHECK_EQUAL(file.NRows(), 50); file.Close(); unlink(filename.c_str()); } template void TestReadAndWrite() { // Test a file with two columns: one is 14 complex floats, the other 3. std::array, 14> data_a; std::array, 3> data_b; constexpr size_t kStride = sizeof(data_a) + sizeof(data_b); const std::string filename = "columnar_file_test.tmp"; const std::array header = {1, 9, 8, 2}; ColumnarFile file = ColumnarFile::CreateNew(filename, header.size(), kStride); file.WriteHeader(header.data()); BOOST_CHECK_EQUAL(file.Stride(), 17*8); // Generate random data for(size_t i=0; i!=data_a.size(); ++i) data_a[i] = std::complex(i+3, i*2); for(size_t i=0; i!=data_b.size(); ++i) data_b[i] = std::complex(i*3+41, i*-2.0); file.Write(2, 0, data_a.data(), data_a.size()); file.Write(2, sizeof(data_a), data_b.data(), data_b.size()); BOOST_CHECK_EQUAL(file.NRows(), 3); std::array header_buffer; std::fill(header_buffer.begin(), header_buffer.end(), 0); file.ReadHeader(header_buffer.data()); BOOST_CHECK_EQUAL_COLLECTIONS(header_buffer.begin(), header_buffer.end(), header.begin(), header.end()); std::array, data_a.size()> read_buffer; file.Read(2, 0, read_buffer.data(), data_a.size()); BOOST_CHECK_EQUAL_COLLECTIONS(read_buffer.begin(), read_buffer.end(), data_a.begin(), data_a.end()); file.Read(2, sizeof(data_a), read_buffer.data(), data_b.size()); BOOST_CHECK_EQUAL_COLLECTIONS(read_buffer.begin(), read_buffer.begin() + data_b.size(), data_b.begin(), data_b.end()); file.Read(1, 0, read_buffer.data(), data_a.size()); BOOST_CHECK(std::all_of(read_buffer.begin(), read_buffer.end(), [](std::complex v) {return v == 0.0f;} )); file.Close(); file = ColumnarFile::OpenExisting(filename, 4); std::fill(header_buffer.begin(), header_buffer.end(), 0); file.ReadHeader(header_buffer.data()); BOOST_CHECK_EQUAL_COLLECTIONS(header_buffer.begin(), header_buffer.end(), header.begin(), header.end()); file.Read(0, 0, read_buffer.data(), data_a.size()); BOOST_CHECK(std::all_of(read_buffer.begin(), read_buffer.end(), [](std::complex v) {return v == 0.0f;} )); file.Read(2, 0, read_buffer.data(), data_a.size()); BOOST_CHECK_EQUAL_COLLECTIONS(read_buffer.begin(), read_buffer.end(), data_a.begin(), data_a.end()); file.Read(2, sizeof(data_a), read_buffer.data(), data_b.size()); BOOST_CHECK_EQUAL_COLLECTIONS(read_buffer.begin(), read_buffer.begin() + data_b.size(), data_b.begin(), data_b.end()); std::fill(read_buffer.begin(), read_buffer.end(), 0.0f); file.Write(2, sizeof(data_a), read_buffer.data(), data_b.size()); file.Read(2, 0, read_buffer.data(), data_a.size()); BOOST_CHECK_EQUAL_COLLECTIONS(read_buffer.begin(), read_buffer.end(), data_a.begin(), data_a.end()); std::fill(read_buffer.begin(), read_buffer.end(), 1.0f); file.Read(2, sizeof(data_a), read_buffer.data(), data_b.size()); BOOST_CHECK(std::all_of(read_buffer.begin(), read_buffer.begin() + data_b.size(), [](std::complex v) {return v == 0.0f;} )); BOOST_CHECK(std::all_of(read_buffer.begin() + data_b.size(), read_buffer.end(), [](std::complex v) {return v == 1.0f;} )); unlink(filename.c_str()); } template void TestReadOnlyOpen() { using boost::filesystem::permissions; using boost::filesystem::perms; constexpr size_t kColumnOffset = 6; const std::array kRowData{1, 9, 8, 2}; constexpr size_t kStride = kColumnOffset + kRowData.size() * sizeof(int32_t); constexpr size_t kHeader = 33; const std::string kFilename = "columnar_file_test_ro.tmp"; // If an earlier test failed, there might still be an RO file with this // name on disk; make sure to remove it, otherwise CreateNew() fails. if(boost::filesystem::exists(kFilename)) { permissions(kFilename, perms::add_perms | perms::owner_write|perms::others_write|perms::group_write); unlink(kFilename.c_str()); } // Write a simple test file { ColumnarFile file = ColumnarFile::CreateNew(kFilename, kHeader, kStride); file.AddRows(37); file.Write(3, kColumnOffset, kRowData.data(), kRowData.size()); file.Close(); } permissions(kFilename, perms::remove_perms|perms::owner_write|perms::others_write|perms::group_write); // Check if we can read the RO file ColumnarFile file = ColumnarFile::OpenExisting(kFilename, kHeader); BOOST_CHECK_EQUAL(file.Stride(), kStride); BOOST_CHECK_EQUAL(file.NRows(), 37); std::array data{0, 0, 0, 0}; file.Read(3, kColumnOffset, data.data(), data.size()); BOOST_CHECK_EQUAL_COLLECTIONS(kRowData.begin(), kRowData.end(), data.begin(), data.end()); file.Close(); // Overwriting an RO file should report an error BOOST_CHECK_THROW(ColumnarFile::CreateNew(kFilename, kHeader, kStride), std::runtime_error); // Updating an RO file should report an error. The error might not be throwed before calling close, // because the write actions might be buffered. const auto Update = [kFilename]()->void { ColumnarFile file = ColumnarFile::OpenExisting(kFilename, kHeader); std::array data{1, 2, 3, 4}; file.Write(3, kColumnOffset, data.data(), data.size()); file.Close(); }; BOOST_CHECK_THROW(Update(), std::runtime_error); permissions(kFilename, perms::add_perms | perms::owner_write|perms::others_write|perms::group_write); unlink(kFilename.c_str()); } BOOST_AUTO_TEST_SUITE(simple_columnar_file) BOOST_AUTO_TEST_CASE(empty_constructor) { TestEmptyConstructor(); } BOOST_AUTO_TEST_CASE(create_and_open_file) { TestCreateAndOpen(); } BOOST_AUTO_TEST_CASE(read_and_write) { TestReadAndWrite(); } BOOST_AUTO_TEST_CASE(read_only) { TestReadOnlyOpen(); } BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE(buffered_columnar_file) BOOST_AUTO_TEST_CASE(empty_constructor) { TestEmptyConstructor>(); TestEmptyConstructor>(); } BOOST_AUTO_TEST_CASE(create_and_open_file) { TestCreateAndOpen>(); TestCreateAndOpen>(); } BOOST_AUTO_TEST_CASE(read_and_write) { TestReadAndWrite>(); TestReadAndWrite>(); } BOOST_AUTO_TEST_CASE(read_only) { TestReadOnlyOpen>(); TestReadOnlyOpen>(); } BOOST_AUTO_TEST_CASE(buffered_file_edge_case) { constexpr size_t kColumnSize = sizeof(float) * 2; constexpr size_t kStride = kColumnSize * 2; const std::string filename = "columnar_file_test.tmp"; casacore::VarBufferedColumnarFile file = casacore::VarBufferedColumnarFile::CreateNew(filename, 0, kStride); const float values[2] = { 3, 4 }; // Polute the buffer with some values file.Write(2, 0, values, 2); file.Write(3, 0, values, 2); file.Write(2, kColumnSize, values, 2); file.Write(3, kColumnSize, values, 2); // At this point, block 1 (rows 2-3) is activated. file.Write(5, 0, values, 2); // Now, block 2 (rows 4-5) is activated float result[2]; file.Read(5, kColumnSize, result, 2); BOOST_CHECK_EQUAL(result[0], 0); BOOST_CHECK_EQUAL(result[1], 0); file.Read(5, 0, result, 2); BOOST_CHECK_EQUAL(result[0], 3); BOOST_CHECK_EQUAL(result[1], 4); file.Close(); unlink(filename.c_str()); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/tables/AlternateMans/test/tStokesIStMan.cc000066400000000000000000000044461476623553700233420ustar00rootroot00000000000000#include #include BOOST_AUTO_TEST_SUITE(stokes_i_st_man_column) BOOST_AUTO_TEST_CASE(expand_from_stokes_i) { casacore::ExpandFromStokesI(nullptr, 0); double test_data_a[4] = {1.0, -1.0, -1.0, -1.0}; casacore::ExpandFromStokesI(test_data_a, 1); BOOST_CHECK_EQUAL(test_data_a[0], 1.0); BOOST_CHECK_EQUAL(test_data_a[1], 0.0); BOOST_CHECK_EQUAL(test_data_a[2], 0.0); BOOST_CHECK_EQUAL(test_data_a[3], 1.0); const std::complex u = {8.8, 8.8}; std::complex test_data_b[12] = {{-3.7, 2.0}, {5.2, 0.0}, {std::numeric_limits::quiet_NaN(), -2.1}, u,u,u,u,u,u,u,u,u}; casacore::ExpandFromStokesI(test_data_b, 3); const std::complex reference_b[8] = { {-3.7, 2.0}, {0.0}, {0.0}, {-3.7, 2.0}, // 1 {5.2, 0.0}, {0.0}, {0.0}, {5.2, 0.0} // 2 }; BOOST_CHECK_EQUAL_COLLECTIONS(test_data_b, test_data_b+8, reference_b, reference_b+8); BOOST_CHECK(std::isnan(test_data_b[8].real())); BOOST_CHECK_EQUAL(test_data_b[8].imag(), -2.1f); BOOST_CHECK_EQUAL(test_data_b[9], 0.0f); BOOST_CHECK_EQUAL(test_data_b[10], 0.0f); BOOST_CHECK(std::isnan(test_data_b[11].real())); BOOST_CHECK_EQUAL(test_data_b[11].imag(), -2.1f); } BOOST_AUTO_TEST_CASE(transform_to_stokes_i) { casacore::TransformToStokesI(nullptr, nullptr, 0); constexpr size_t data_a_size = 12; const bool test_data_a[data_a_size] = {false, false, false, false, true, true, true, true, false, false, false, false}; char buffer_a[data_a_size * sizeof(bool)]; const bool* result_a = casacore::TransformToStokesI(test_data_a, buffer_a, data_a_size/4); BOOST_CHECK(!result_a[0]); BOOST_CHECK(result_a[1]); BOOST_CHECK(!result_a[2]); constexpr size_t data_b_size = 16; const double test_data_b[data_b_size] = {3.0, 0.0, 0.0, 3.0, 20.0, 0.0, 0.0, 24.0, 0.0, 0.0, 0.0, 1000.0, std::numeric_limits::quiet_NaN(), 0.0, 0.0, 0.0}; char buffer_b[data_b_size * sizeof(double)]; const double* result_b = casacore::TransformToStokesI(test_data_b, buffer_b, data_b_size/4); BOOST_CHECK_CLOSE_FRACTION(result_b[0], 3.0, 1e-11); BOOST_CHECK_CLOSE_FRACTION(result_b[1], 22.0, 1e-11); BOOST_CHECK_CLOSE_FRACTION(result_b[2], 500.0, 1e-11); BOOST_CHECK(std::isnan(result_b[3])); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/tables/AlternateMans/test/tUvwFile.cc000066400000000000000000000140711476623553700223720ustar00rootroot00000000000000#include #include using casacore::UvwFile; namespace { const std::string kFilename = "uvw_file_test.tmp"; const std::array kAnts[3] ={ { 7, 13, 17 }, { 19, 23, 29 }, { 5, 3, 31 } }; constexpr size_t kNBaselines = 6; const std::array kBaselines[kNBaselines] = { {0, 0}, {0, 1}, {0, 2}, {1, 1}, {1, 2}, {2, 2} }; const std::array kBaselinesSwapped[kNBaselines] = { {0, 0}, {1, 0}, {2, 0}, {1, 1}, {2, 1}, {2, 2} }; const std::array kUvwPerBaseline[kNBaselines] = { {0.0, 0.0, 0.0}, {kAnts[1][0] - kAnts[0][0], kAnts[1][1] - kAnts[0][1], kAnts[1][2] - kAnts[0][2] }, {kAnts[2][0] - kAnts[0][0], kAnts[2][1] - kAnts[0][1], kAnts[2][2] - kAnts[0][2] }, {0.0, 0.0, 0.0}, {kAnts[2][0] - kAnts[1][0], kAnts[2][1] - kAnts[1][1], kAnts[2][2] - kAnts[1][2] }, {0.0, 0.0, 0.0} }; const std::array kBaselinesMissingAntenna[kNBaselines] = { {1, 1}, {1, 3}, {1, 4}, {3, 3}, {3, 4}, {4, 4} }; } BOOST_AUTO_TEST_SUITE(uvw_file) BOOST_AUTO_TEST_CASE(create_empty) { UvwFile file_a = UvwFile::CreateNew(kFilename); BOOST_CHECK_EQUAL(file_a.NRows(), 0); BOOST_CHECK_EQUAL(file_a.Filename(), kFilename); file_a.Close(); UvwFile file_b = UvwFile::CreateNew(kFilename); file_b = std::move(file_a); BOOST_CHECK_EQUAL(file_b.NRows(), 0); BOOST_CHECK_EQUAL(file_b.Filename(), ""); file_b.Close(); unlink(kFilename.c_str()); } BOOST_AUTO_TEST_CASE(open_empty) { UvwFile file = UvwFile::CreateNew(kFilename); BOOST_CHECK_EQUAL(file.NRows(), 0); BOOST_CHECK_EQUAL(file.Filename(), kFilename); file.Close(); file = UvwFile::OpenExisting(kFilename); BOOST_CHECK_EQUAL(file.NRows(), 0); BOOST_CHECK_EQUAL(file.Filename(), kFilename); file.Close(); unlink(kFilename.c_str()); } void WriteTwoTimesteps(UvwFile& file, const std::array* baselines, bool with_autos) { uint64_t row = 0; for(uint64_t i=0; i!=kNBaselines; ++i) { if(with_autos || baselines[i][0] != baselines[i][1]) { const std::array& uvw = kUvwPerBaseline[i]; file.WriteUvw(row, baselines[i][0], baselines[i][1], uvw.data()); BOOST_CHECK_EQUAL(file.NRows(), row+1); ++row; } } for(uint64_t i=0; i!=kNBaselines; ++i) { if(with_autos || baselines[i][0] != baselines[i][1]) { std::array uvw = kUvwPerBaseline[i]; uvw[0] *= 2.0; uvw[1] *= 3.0; uvw[2] *= 4.0; file.WriteUvw(row, baselines[i][0], baselines[i][1], uvw.data()); BOOST_CHECK_EQUAL(file.NRows(), row+1); ++row; } } } void ReadTwoTimesteps(UvwFile& file, const std::array* baselines, bool with_autos) { // Read the second timestep first to see if seeking works ok. uint64_t row = with_autos ? kNBaselines : 3; for(uint64_t i=0; i!=kNBaselines; ++i) { if(with_autos || baselines[i][0] != baselines[i][1]) { std::array uvw; file.ReadUvw(row, baselines[i][0], baselines[i][1], uvw.data()); std::array expected_uvw = kUvwPerBaseline[i]; expected_uvw[0] *= 2.0; expected_uvw[1] *= 3.0; expected_uvw[2] *= 4.0; for(size_t z=0; z!=3; ++z) BOOST_CHECK_CLOSE_FRACTION(expected_uvw[z], uvw[z], 1e-6); ++row; } } row = 0; for(uint64_t i=0; i!=kNBaselines; ++i) { if(with_autos || baselines[i][0] != baselines[i][1]) { std::array uvw; file.ReadUvw(row, baselines[i][0], baselines[i][1], uvw.data()); for(size_t z=0; z!=3; ++z) BOOST_CHECK_CLOSE_FRACTION(kUvwPerBaseline[i][z], uvw[z], 1e-6); ++row; } } } BOOST_AUTO_TEST_CASE(write_and_direct_read) { UvwFile file = UvwFile::CreateNew(kFilename); WriteTwoTimesteps(file, kBaselines, true); ReadTwoTimesteps(file, kBaselines, true); file.Close(); unlink(kFilename.c_str()); } BOOST_AUTO_TEST_CASE(write_and_read_after_reopen) { { UvwFile file = UvwFile::CreateNew(kFilename); WriteTwoTimesteps(file, kBaselines, true); } UvwFile file = UvwFile::OpenExisting(kFilename); ReadTwoTimesteps(file, kBaselines, true); file.Close(); unlink(kFilename.c_str()); } BOOST_AUTO_TEST_CASE(without_autocorrelations) { UvwFile file = UvwFile::CreateNew(kFilename); WriteTwoTimesteps(file, kBaselines, false); ReadTwoTimesteps(file, kBaselines, false); file.Close(); unlink(kFilename.c_str()); } BOOST_AUTO_TEST_CASE(missing_antenna) { UvwFile file = UvwFile::CreateNew(kFilename); WriteTwoTimesteps(file, kBaselinesMissingAntenna, true); ReadTwoTimesteps(file, kBaselinesMissingAntenna, true); file.Close(); file = UvwFile::CreateNew(kFilename); WriteTwoTimesteps(file, kBaselinesMissingAntenna, false); ReadTwoTimesteps(file, kBaselinesMissingAntenna, false); file.Close(); unlink(kFilename.c_str()); } BOOST_AUTO_TEST_CASE(invalid_baselines) { UvwFile file = UvwFile::CreateNew(kFilename); file.WriteUvw(0, 0, 1, kUvwPerBaseline[0].data()); BOOST_CHECK_THROW( file.WriteUvw(1, 2, 3, kUvwPerBaseline[1].data()), std::runtime_error); file.Close(); unlink(kFilename.c_str()); } BOOST_AUTO_TEST_CASE(non_standard_ordering) { UvwFile file = UvwFile::CreateNew(kFilename); WriteTwoTimesteps(file, kBaselinesSwapped, true); ReadTwoTimesteps(file, kBaselinesSwapped, true); file.Close(); file = UvwFile::CreateNew(kFilename); WriteTwoTimesteps(file, kBaselinesSwapped, false); ReadTwoTimesteps(file, kBaselinesSwapped, false); file.Close(); unlink(kFilename.c_str()); } BOOST_AUTO_TEST_CASE(same_positions) { UvwFile file = UvwFile::CreateNew(kFilename); const std::array zero_uvw = {0.0, 0.0, 0.0}; for(uint64_t i=0; i!=kNBaselines; ++i) { file.WriteUvw(i, kBaselines[i][0], kBaselines[i][1], zero_uvw.data()); } for(uint64_t i=0; i!=kNBaselines; ++i) { std::array uvw = {1.0, 1.0, 1.0}; file.ReadUvw(i, kBaselines[i][0], kBaselines[i][1], uvw.data()); for(size_t z=0; z!=3; ++z) BOOST_CHECK_LT(std::fabs(uvw[z]), 1e-6); } file.Close(); unlink(kFilename.c_str()); } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/tables/CMakeLists.txt000066400000000000000000000273211476623553700173430ustar00rootroot00000000000000# # CASA Tables # set ( parser_inputs RecordGram TableGram ) # Note that in the future -Dapi.pure might need be added to the else branch, # while %pure_parser should be removed from the .yy file. foreach (src ${parser_inputs}) if (BISON_VERSION VERSION_LESS 3.0) BISON_TARGET (${src} TaQL/${src}.yy ${CMAKE_CURRENT_BINARY_DIR}/${src}.ycc COMPILE_FLAGS "-y -p ${src}") else() BISON_TARGET (${src} TaQL/${src}.yy ${CMAKE_CURRENT_BINARY_DIR}/${src}.ycc COMPILE_FLAGS "-y -Dapi.prefix={${src}} --warnings=no-yacc") endif() FLEX_TARGET (${src} TaQL/${src}.ll ${CMAKE_CURRENT_BINARY_DIR}/${src}.lcc COMPILE_FLAGS "-P${src}") endforeach (src) include_directories (${CMAKE_CURRENT_BINARY_DIR}) # Add SOVERSION definition for files needing it. foreach (src DataMan/DataManager.cc TaQL/UDFBase.cc) set_source_files_properties(${src} PROPERTIES COMPILE_FLAGS -DSOVERSION=${LIB_SOVERSION}) endforeach (src) if (ADIOS2_FOUND) set(ADIOS2_SOURCES DataMan/Adios2StMan.cc DataMan/Adios2StManColumn.cc) set(ADIOS2_HEADERS DataMan/Adios2StMan.h DataMan/Adios2StManColumn.h) endif (ADIOS2_FOUND) if(BUILD_DYSCO) add_subdirectory(Dysco) else(BUILD_DYSCO) set(DYSCOSTMAN_SOURCES) set(DYSCOSTMAN_LIBRARIES) endif(BUILD_DYSCO) add_library (casa_tables Tables/ArrayColumnBase.cc Tables/ArrayColumn_tmpl.cc Tables/ArrColData.cc Tables/ArrColDesc.cc Tables/ArrColDesc_tmpl.cc Tables/BaseColDesc.cc Tables/BaseColumn.cc Tables/BaseTabIter.cc Tables/BaseTable.cc Tables/ColDescSet.cc Tables/ColumnCache.cc Tables/ColumnDesc.cc Tables/ColumnSet.cc Tables/ColumnsIndex.cc Tables/ColumnsIndexArray.cc Tables/ConcatColumn.cc Tables/ConcatRows.cc Tables/ConcatTable.cc Tables/ExternalLockSync.cc Tables/MemoryTable.cc Tables/NullTable.cc Tables/PlainColumn.cc Tables/PlainTable.cc Tables/ReadAsciiTable.cc Tables/RefColumn.cc Tables/RefRows.cc Tables/RefTable.cc Tables/RowCopier.cc Tables/RowNumbers.cc Tables/ScaColDesc_tmpl.cc Tables/ScalarColumn_tmpl.cc Tables/ScaRecordColData.cc Tables/ScaRecordColDesc.cc Tables/SetupNewTab.cc Tables/StorageOption.cc Tables/SubTabDesc.cc Tables/TabPath.cc Tables/Table.cc Tables/TableAttr.cc Tables/TableCache.cc Tables/TableColumn.cc Tables/TableCopy.cc Tables/TableDesc.cc Tables/TableError.cc Tables/TableIndexProxy.cc Tables/TableInfo.cc Tables/TableIter.cc Tables/TableIterProxy.cc Tables/TableKeyword.cc Tables/TableLock.cc Tables/TableLockData.cc Tables/TableLocker.cc Tables/TableProxy.cc Tables/TableRecord.cc Tables/TableRecordRep.cc Tables/TableRow.cc Tables/TableRowProxy.cc Tables/TableSyncData.cc Tables/TableTrace.cc Tables/TableUtil.cc AlternateMans/AntennaPairStMan.cc AlternateMans/StokesIStMan.cc AlternateMans/UvwStMan.cc DataMan/BitFlagsEngine.cc DataMan/CompressComplex.cc DataMan/CompressFloat.cc DataMan/DataManAccessor.cc DataMan/DataManError.cc DataMan/DataManInfo.cc DataMan/DataManager.cc DataMan/DataManagerColumn.cc DataMan/ForwardCol.cc DataMan/ForwardColRow.cc DataMan/ISMBase.cc DataMan/ISMBucket.cc DataMan/ISMColumn.cc DataMan/ISMIndColumn.cc DataMan/ISMIndex.cc DataMan/IncrStManAccessor.cc DataMan/IncrementalStMan.cc DataMan/MSMBase.cc DataMan/MSMColumn.cc DataMan/MSMDirColumn.cc DataMan/MSMIndColumn.cc DataMan/MemoryStMan.cc DataMan/SSMBase.cc DataMan/SSMColumn.cc DataMan/SSMDirColumn.cc DataMan/SSMIndColumn.cc DataMan/SSMIndStringColumn.cc DataMan/SSMIndex.cc DataMan/SSMStringHandler.cc DataMan/StArrAipsIO.cc DataMan/StArrayFile.cc DataMan/StIndArrAIO.cc DataMan/StIndArray.cc DataMan/StManAipsIO.cc DataMan/StManColumn.cc DataMan/StManColumnBase.cc DataMan/StandardStMan.cc DataMan/StandardStManAccessor.cc DataMan/TSMColumn.cc DataMan/TSMCoordColumn.cc DataMan/TSMCube.cc DataMan/TSMCubeBuff.cc DataMan/TSMCubeMMap.cc DataMan/TSMDataColumn.cc DataMan/TSMFile.cc DataMan/TSMIdColumn.cc DataMan/TSMOption.cc DataMan/TSMShape.cc DataMan/TiledCellStMan.cc DataMan/TiledColumnStMan.cc DataMan/TiledDataStMan.cc DataMan/TiledDataStManAccessor.cc DataMan/TiledFileAccess.cc DataMan/TiledFileHelper.cc DataMan/TiledShapeStMan.cc DataMan/TiledStMan.cc DataMan/TiledStManAccessor.cc DataMan/VirtArrCol.cc DataMan/VirtScaCol.cc DataMan/VirtColEng.cc DataMan/VirtualTaQLColumn.cc TaQL/ExprAggrNode.cc TaQL/ExprAggrNodeArray.cc TaQL/ExprConeNode.cc TaQL/ExprDerNode.cc TaQL/ExprDerNodeArray.cc TaQL/ExprFuncNode.cc TaQL/ExprFuncNodeArray.cc TaQL/ExprGroup.cc TaQL/ExprGroupAggrFunc.cc TaQL/ExprGroupAggrFuncArray.cc TaQL/ExprLogicNode.cc TaQL/ExprLogicNodeArray.cc TaQL/ExprMathNode.cc TaQL/ExprMathNodeArray.cc TaQL/ExprNode.cc TaQL/ExprNodeArray.cc TaQL/ExprNodeRecord.cc TaQL/ExprNodeRep.cc TaQL/ExprNodeSet.cc TaQL/ExprNodeSetElem.cc TaQL/ExprNodeSetOpt.cc TaQL/ExprNodeUtil.cc TaQL/ExprRange.cc TaQL/ExprUDFNode.cc TaQL/ExprUDFNodeArray.cc TaQL/ExprUnitNode.cc TaQL/MArrayBase.cc TaQL/RecordExpr.cc TaQL/RecordGram.cc TaQL/TaQLJoin.cc TaQL/TaQLNode.cc TaQL/TaQLNodeDer.cc TaQL/TaQLNodeHandler.cc TaQL/TaQLNodeRep.cc TaQL/TaQLNodeVisitor.cc TaQL/TaQLResult.cc TaQL/TaQLShow.cc TaQL/TaQLStyle.cc TaQL/TableExprData.cc TaQL/TableExprId.cc TaQL/TableGram.cc TaQL/TableParse.cc TaQL/TableParseFunc.cc TaQL/TableParseGroupby.cc TaQL/TableParseJoin.cc TaQL/TableParseProject.cc TaQL/TableParseQuery.cc TaQL/TableParseSortKey.cc TaQL/TableParseTableList.cc TaQL/TableParseUpdate.cc TaQL/TableParseUtil.cc TaQL/UDFBase.cc LogTables/TableLogSink.cc LogTables/NewFile.cc LogTables/LoggerHolder.cc LogTables/LogFilterTaql.cc LogTables/LogFilterExpr.cc ${BISON_RecordGram_OUTPUTS} ${FLEX_RecordGram_OUTPUTS} ${BISON_TableGram_OUTPUTS} ${FLEX_TableGram_OUTPUTS} ${ADIOS2_SOURCES} ${DYSCOSTMAN_SOURCES} ) set(top_level_headers DataMan.h LogTables.h Tables.h TaQL.h ) init_pch_support(casa_tables ${top_level_headers}) target_link_libraries (casa_tables casa_casa ${CASACORE_ARCH_LIBS} ${DYSCOSTMAN_LIBRARIES} ${CASACORE_ADIOS_LIBRARY} ${CASACORE_MPI_LIBRARY}) add_subdirectory (apps) install (TARGETS casa_tables LIBRARY DESTINATION lib${LIB_SUFFIX} ARCHIVE DESTINATION lib${LIB_SUFFIX} LIBRARY PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) install (FILES Tables/ArrColData.h Tables/ArrColDesc.h Tables/ArrColDesc.tcc Tables/ArrayColumn.h Tables/ArrayColumn.tcc Tables/ArrayColumnBase.h Tables/ArrayColumnFunc.h Tables/BaseColDesc.h Tables/BaseColumn.h Tables/BaseTabIter.h Tables/BaseTable.h Tables/ColDescSet.h Tables/ColumnCache.h Tables/ColumnDesc.h Tables/ColumnSet.h Tables/ColumnsIndex.h Tables/ColumnsIndexArray.h Tables/ConcatColumn.h Tables/ConcatRows.h Tables/ConcatScalarColumn.h Tables/ConcatScalarColumn.tcc Tables/ConcatTable.h Tables/ExternalLockSync.h Tables/MemoryTable.h Tables/NullTable.h Tables/PlainColumn.h Tables/PlainTable.h Tables/ReadAsciiTable.h Tables/RefColumn.h Tables/RefRows.h Tables/RefTable.h Tables/RowCopier.h Tables/RowNumbers.h Tables/ScaColData.h Tables/ScaColData.tcc Tables/ScaColDesc.h Tables/ScaColDesc.tcc Tables/ScaRecordColData.h Tables/ScaRecordColDesc.h Tables/ScalarColumn.h Tables/ScalarColumn.tcc Tables/SetupNewTab.h Tables/StorageOption.h Tables/SubTabDesc.h Tables/TVec.h Tables/TVec.tcc Tables/TVecLogic.h Tables/TVecLogic.tcc Tables/TVecMath.h Tables/TVecMath.tcc Tables/TVecScaCol.h Tables/TVecScaCol.tcc Tables/TVecTemp.h Tables/TVecTemp.tcc Tables/TabPath.h Tables/TabVecLogic.h Tables/TabVecLogic.tcc Tables/TabVecMath.h Tables/TabVecMath.tcc Tables/Table.h Tables/TableAttr.h Tables/TableCache.h Tables/TableColumn.h Tables/TableCopy.h Tables/TableCopy.tcc Tables/TableDesc.h Tables/TableError.h Tables/TableIndexProxy.h Tables/TableInfo.h Tables/TableIter.h Tables/TableIterProxy.h Tables/TableKeyword.h Tables/TableLock.h Tables/TableLockData.h Tables/TableLocker.h Tables/TableProxy.h Tables/TableRecord.h Tables/TableRecordRep.h Tables/TableRow.h Tables/TableRowProxy.h Tables/TableSyncData.h Tables/TableTrace.h Tables/TableUtil.h Tables/TableVector.h Tables/TableVector.tcc DESTINATION include/casacore/tables/Tables ) install (FILES AlternateMans/AntennaPairFile.h AlternateMans/AntennaPairStMan.h AlternateMans/AntennaPairStManColumn.h AlternateMans/BitPacking.h AlternateMans/BufferedColumnarFile.h AlternateMans/RowBasedFile.h AlternateMans/SimpleColumnarFile.h AlternateMans/StokesIStMan.h AlternateMans/StokesIStManColumn.h AlternateMans/UvwFile.h DataMan/BaseMappedArrayEngine.h DataMan/BaseMappedArrayEngine.tcc DataMan/BitFlagsEngine.h DataMan/BitFlagsEngine.tcc DataMan/CompressComplex.h DataMan/CompressFloat.h DataMan/DataManAccessor.h DataMan/DataManError.h DataMan/DataManInfo.h DataMan/DataManager.h DataMan/DataManagerColumn.h DataMan/ForwardCol.h DataMan/ForwardColRow.h DataMan/ISMBase.h DataMan/ISMBucket.h DataMan/ISMColumn.h DataMan/ISMIndColumn.h DataMan/ISMIndex.h DataMan/IncrStManAccessor.h DataMan/IncrementalStMan.h DataMan/MSMBase.h DataMan/MSMColumn.h DataMan/MSMDirColumn.h DataMan/MSMIndColumn.h DataMan/MappedArrayEngine.h DataMan/MappedArrayEngine.tcc DataMan/MemoryStMan.h DataMan/RetypedArrayEngine.h DataMan/RetypedArrayEngine.tcc DataMan/RetypedArraySetGet.h DataMan/RetypedArraySetGet.tcc DataMan/SSMBase.h DataMan/SSMColumn.h DataMan/SSMDirColumn.h DataMan/SSMIndColumn.h DataMan/SSMIndStringColumn.h DataMan/SSMIndex.h DataMan/SSMStringHandler.h DataMan/ScaledArrayEngine.h DataMan/ScaledArrayEngine.tcc DataMan/ScaledComplexData.h DataMan/ScaledComplexData.tcc DataMan/StArrAipsIO.h DataMan/StArrayFile.h DataMan/StIndArrAIO.h DataMan/StIndArray.h DataMan/StManAipsIO.h DataMan/StManColumn.h DataMan/StManColumnBase.h DataMan/StandardStMan.h DataMan/StandardStManAccessor.h DataMan/TSMColumn.h DataMan/TSMCoordColumn.h DataMan/TSMCube.h DataMan/TSMCubeBuff.h DataMan/TSMCubeMMap.h DataMan/TSMDataColumn.h DataMan/TSMFile.h DataMan/TSMIdColumn.h DataMan/TSMOption.h DataMan/TSMShape.h DataMan/TiledCellStMan.h DataMan/TiledColumnStMan.h DataMan/TiledDataStMan.h DataMan/TiledDataStManAccessor.h DataMan/TiledFileAccess.h DataMan/TiledFileHelper.h DataMan/TiledShapeStMan.h DataMan/TiledStMan.h DataMan/TiledStManAccessor.h DataMan/VACEngine.h DataMan/VACEngine.tcc DataMan/VSCEngine.h DataMan/VSCEngine.tcc DataMan/VirtArrCol.h DataMan/VirtArrCol.tcc DataMan/VirtColEng.h DataMan/VirtScaCol.h DataMan/VirtScaCol.tcc DataMan/VirtualTaQLColumn.h ${ADIOS2_HEADERS} DESTINATION include/casacore/tables/DataMan ) install (FILES TaQL/ExprAggrNode.h TaQL/ExprAggrNodeArray.h TaQL/ExprConeNode.h TaQL/ExprDerNode.h TaQL/ExprDerNodeArray.h TaQL/ExprFuncNode.h TaQL/ExprFuncNodeArray.h TaQL/ExprGroup.h TaQL/ExprGroupAggrFunc.h TaQL/ExprGroupAggrFuncArray.h TaQL/ExprLogicNode.h TaQL/ExprLogicNodeArray.h TaQL/ExprMathNode.h TaQL/ExprMathNodeArray.h TaQL/ExprNode.h TaQL/ExprNodeArray.h TaQL/ExprNodeRecord.h TaQL/ExprNodeRep.h TaQL/ExprNodeSet.h TaQL/ExprNodeSetElem.h TaQL/ExprNodeSetOpt.h TaQL/ExprNodeUtil.h TaQL/ExprRange.h TaQL/ExprUDFNode.h TaQL/ExprUDFNodeArray.h TaQL/ExprUnitNode.h TaQL/MArrayBase.h TaQL/MArrayLogical.h TaQL/MArrayMathBase.h TaQL/MArrayMath.h TaQL/MArrayUtil.h TaQL/MArray.h TaQL/RecordExpr.h TaQL/RecordGram.h TaQL/TaQLJoin.h TaQL/TaQLNode.h TaQL/TaQLNodeDer.h TaQL/TaQLNodeHandler.h TaQL/TaQLNodeRep.h TaQL/TaQLNodeResult.h TaQL/TaQLNodeVisitor.h TaQL/TaQLResult.h TaQL/TaQLShow.h TaQL/TaQLStyle.h TaQL/TableExprData.h TaQL/TableExprId.h TaQL/TableExprIdAggr.h TaQL/TableGram.h TaQL/TableParse.h TaQL/TableParseFunc.h TaQL/TableParseGroupby.h TaQL/TableParseJoin.h TaQL/TableParseProject.h TaQL/TableParseQuery.h TaQL/TableParseSortKey.h TaQL/TableParseTableList.h TaQL/TableParseUpdate.h TaQL/TableParseUtil.h TaQL/UDFBase.h DESTINATION include/casacore/tables/TaQL ) install (FILES LogTables/LogFilterTaql.h LogTables/LoggerHolder.h LogTables/TableLogSink.h LogTables/LogFilterExpr.h LogTables/NewFile.h DESTINATION include/casacore/tables/LogTables ) install (FILES ${top_level_headers} DESTINATION include/casacore/tables ) foreach (casa_module Tables AlternateMans DataMan TaQL LogTables) add_subdirectory (${casa_module}/test ${EXCL_ALL}) endforeach (casa_module) casacore-3.7.1/tables/DataMan.h000066400000000000000000000066251476623553700162650ustar00rootroot00000000000000//# DataMan.h: The DataMan module - Casacore table data managers //# Copyright (C) 1994-2010 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_DATAMAN_H #define TABLES_DATAMAN_H //# Includes //# storage managers #include #include #include #include #include #include #include #include #include #include #include #include //# virtual column engines #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // // DataManagers are the physical representation of table data. // // // // // //
      • Tables module // // // DataMan is the abbreviation of data managers. // // // Tables are the fundamental storage mechanism for Casacore. // Tables themselves are a logical organization of the data. // Table data are physically stored (or calculated on the fly) // using data managers. //
        Casacore ships with several data managers, but it is possible // to write a specific data manager that can be loaded dynamically // from a shared library. //
        See the Tables module // for more information. //
        // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/000077500000000000000000000000001476623553700161035ustar00rootroot00000000000000casacore-3.7.1/tables/DataMan/Adios2StMan.cc000066400000000000000000000442451476623553700205070ustar00rootroot00000000000000//# Adios2StMan.cc: Base class of the ADIOS2 Storage Manager //# Copyright (C) 2018 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusettes Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include "Adios2StManImpl.h" #include "Adios2StManColumn.h" #include #include namespace casacore { // Static objects in impl class #ifdef HAVE_MPI MPI_Comm Adios2StMan::impl::itsMpiComm = MPI_COMM_WORLD; #endif constexpr const char *Adios2StMan::impl::SPEC_FIELD_XML_FILE; constexpr const char *Adios2StMan::impl::SPEC_FIELD_ENGINE_TYPE; constexpr const char *Adios2StMan::impl::SPEC_FIELD_ENGINE_PARAMS; constexpr const char *Adios2StMan::impl::SPEC_FIELD_TRANSPORT_PARAMS; constexpr const char *Adios2StMan::impl::SPEC_FIELD_OPERATOR_PARAMS; // // Adios2StMan implementation in terms of the impl class // #ifdef HAVE_MPI Adios2StMan::Adios2StMan(MPI_Comm mpiComm, std::string engineType, std::map engineParams, std::vector> transportParams, std::vector> operatorParams ) : Adios2StMan(mpiComm, move(engineType), move(engineParams), move(transportParams), move(operatorParams), {}) { } Adios2StMan::Adios2StMan(MPI_Comm mpiComm, std::string configFile, from_config_t) : Adios2StMan(mpiComm, {}, {}, {}, {}, move(configFile)) { } Adios2StMan::Adios2StMan(MPI_Comm mpiComm, std::string engineType, std::map engineParams, std::vector> transportParams, std::vector> operatorParams, std::string configFile) : DataManager(), pimpl(std::unique_ptr(new impl( *this, &mpiComm, move(engineType), move(engineParams), move(transportParams), move(operatorParams), move(configFile)))) { } #endif Adios2StMan::Adios2StMan(std::string engineType, std::map engineParams, std::vector> transportParams, std::vector> operatorParams ) : Adios2StMan(move(engineType), move(engineParams), move(transportParams), move(operatorParams), {}) { } Adios2StMan::Adios2StMan(std::string configFile, from_config_t) : Adios2StMan({}, {}, {}, {}, move(configFile)) { } Adios2StMan::Adios2StMan(std::string engineType, std::map engineParams, std::vector> transportParams, std::vector> operatorParams, std::string configFile) : DataManager(), pimpl(std::unique_ptr(new impl( *this, nullptr, move(engineType), move(engineParams), move(transportParams), move(operatorParams), move(configFile)))) { } Adios2StMan::~Adios2StMan() = default; DataManager *Adios2StMan::clone() const { return pimpl->clone(); } String Adios2StMan::dataManagerType() const { return pimpl->dataManagerType(); } String Adios2StMan::dataManagerName() const { return pimpl->dataManagerName(); } void Adios2StMan::create64(rownr_t aNrRows) { pimpl->create64(aNrRows); } rownr_t Adios2StMan::open64(rownr_t aRowNr, AipsIO &ios) { return pimpl->open64(aRowNr, ios); } rownr_t Adios2StMan::resync64(rownr_t aRowNr) { return pimpl->resync64(aRowNr); } Bool Adios2StMan::flush(AipsIO &ios, Bool doFsync) { return pimpl->flush(ios, doFsync); } DataManagerColumn *Adios2StMan::makeScalarColumn( const String &aName, int aDataType, const String &aDataTypeID) { return pimpl->makeScalarColumn(aName, aDataType, aDataTypeID); } DataManagerColumn *Adios2StMan::makeDirArrColumn( const String &aName, int aDataType, const String &aDataTypeID) { return pimpl->makeDirArrColumn(aName, aDataType, aDataTypeID); } DataManagerColumn *Adios2StMan::makeIndArrColumn( const String &aName, int aDataType, const String &aDataTypeID) { return pimpl->makeIndArrColumn(aName, aDataType, aDataTypeID); } void Adios2StMan::deleteManager() { pimpl->deleteManager(); } void Adios2StMan::addRow64(rownr_t aNrRows) { return pimpl->addRow64(aNrRows); } DataManager *Adios2StMan::makeObject( const String &aDataManType, const Record &spec) { return impl::makeObject(aDataManType, spec); } Record Adios2StMan::dataManagerSpec() const { return pimpl->dataManagerSpec(); } rownr_t Adios2StMan::getNrRows() { return pimpl->getNrRows(); } // // impl class implementation using ADIOS2 // #ifdef HAVE_MPI void Adios2StMan::impl::checkMPI() const { int mpi_finalized; MPI_Finalized(&mpi_finalized); if(mpi_finalized) { throw(std::runtime_error("MPI has been finalized when initializing Adios2StMan")); } else { int mpi_initialized; MPI_Initialized(&mpi_initialized); if(!mpi_initialized) { #ifdef USE_THREADS int provided; MPI_Init_thread(0,0,MPI_THREAD_MULTIPLE, &provided); if(provided != MPI_THREAD_MULTIPLE) { throw(std::runtime_error( "Casacore is built with thread and MPI enabled, \ but the MPI installation does not support threads")); } #else MPI_Init(0,0); #endif } } } #endif // HAVE_MPI Adios2StMan::impl::impl( Adios2StMan &parent, void *mpiComm, std::string engineType, std::map engineParams, std::vector> transportParams, std::vector> operatorParams, std::string configFile) : parent(parent), itsAdiosEngineType(std::move(engineType)), itsAdiosEngineParams(std::move(engineParams)), itsAdiosTransportParamsVec(std::move(transportParams)), itsAdiosOperatorParamsVec(std::move(operatorParams)), itsAdiosConfigFile(std::move(configFile)) { auto configureWithFile = !itsAdiosConfigFile.empty(); #ifdef HAVE_MPI if (mpiComm) { itsMpiComm = *reinterpret_cast(mpiComm); checkMPI(); if (configureWithFile) { itsAdios = std::make_shared(itsAdiosConfigFile, itsMpiComm); } else { itsAdios = std::make_shared(itsMpiComm); } } else #endif // HAVE_MPI { // Using an explicit check here instead of an assert avoids a warning // in release builds due to mpiComm being unused if (mpiComm != nullptr) { throw std::invalid_argument("mpiComm should be null"); } if (configureWithFile) { itsAdios = std::make_shared(itsAdiosConfigFile); } else { itsAdios = std::make_shared(); } } itsAdiosIO = std::make_shared(itsAdios->DeclareIO("Adios2StMan")); if (!configureWithFile) { configureAdios(); } } void Adios2StMan::impl::configureAdios() { if (itsAdiosEngineType.empty() == false) { itsAdiosIO->SetEngine(itsAdiosEngineType); } if (itsAdiosEngineParams.empty() == false) { itsAdiosIO->SetParameters(itsAdiosEngineParams); } // transport is valid only when it has a valid ADIOS2 transport name // invalid transport name may cause an exception for (const auto ¶m: itsAdiosTransportParamsVec) { auto itName = param.find("Name"); if(itName==param.end()) continue; itsAdiosIO->AddTransport(itName->second, param); } // auto param intended as it should not modify itsAdiosOperatorParamsVec for(auto param : itsAdiosOperatorParamsVec) { std::string var,op; auto itVar = param.find("Variable"); if(itVar==param.end()) continue; else var = itVar->second; auto itOp = param.find("Operator"); if(itOp==param.end()) continue; else op=itOp->second; param.erase(itVar); param.erase(itOp); itsAdiosIO->AddOperation(var, op, param); } } Adios2StMan::impl::~impl() { if (itsAdiosEngine) { itsAdiosEngine->EndStep(); itsAdiosEngine->Close(); } for (uInt i = 0; i < ncolumn(); ++i) { delete itsColumnPtrBlk[i]; } } static adios2::Params to_adios2_params(const Record &record) { adios2::Params params; for (Int i = 0; i != Int(record.size()); i++) { params[record.name(i)] = record.asString(i); } return params; } static Record to_record(const adios2::Params ¶ms) { Record record; for (auto &kv : params) { record.define(kv.first, kv.second); } return record; } DataManager *Adios2StMan::impl::makeObject(const String &/*aDataManType*/, const Record &spec) { std::string configFile; std::string engine; adios2::Params engine_params; std::vector transport_params; std::vector operator_params; if (spec.isDefined(SPEC_FIELD_XML_FILE)) { configFile = spec.asString(SPEC_FIELD_XML_FILE); } if (spec.isDefined(SPEC_FIELD_ENGINE_TYPE)) { engine = spec.asString(SPEC_FIELD_ENGINE_TYPE); } if (spec.isDefined(SPEC_FIELD_ENGINE_PARAMS)) { engine_params = to_adios2_params(spec.asRecord(SPEC_FIELD_ENGINE_PARAMS)); } if (spec.isDefined(SPEC_FIELD_TRANSPORT_PARAMS)) { auto &record = spec.asRecord(SPEC_FIELD_TRANSPORT_PARAMS); for (Int i = 0; i != Int(record.size()); i++) { auto name = record.name(i); auto params = to_adios2_params(record.asRecord(i)); params["Name"] = name; transport_params.emplace_back(std::move(params)); } } if (spec.isDefined(SPEC_FIELD_OPERATOR_PARAMS)) { auto &record = spec.asRecord(SPEC_FIELD_OPERATOR_PARAMS); for (Int i = 0; i != Int(record.size()); i++) { auto variable = record.name(i); auto params = to_adios2_params(record.asRecord(i)); params["Variable"] = variable; operator_params.emplace_back(std::move(params)); } } return new Adios2StMan( #ifdef HAVE_MPI itsMpiComm, #endif engine, engine_params, transport_params, operator_params, configFile); } Record Adios2StMan::impl::dataManagerSpec() const { Record record; if (!itsAdiosConfigFile.empty()) { record.define(SPEC_FIELD_XML_FILE, itsAdiosConfigFile); } if (!itsAdiosEngineType.empty()) { record.define(SPEC_FIELD_ENGINE_TYPE, itsAdiosEngineType); } if (!itsAdiosEngineParams.empty()) { record.defineRecord(SPEC_FIELD_ENGINE_PARAMS, to_record(itsAdiosEngineParams)); } if (!itsAdiosTransportParamsVec.empty()) { Record transport_params_record; for (const auto ¶ms : itsAdiosTransportParamsVec) { auto itName = params.find("Name"); if (itName == params.end()) { continue; } transport_params_record.defineRecord(itName->second, to_record(params)); } record.defineRecord(SPEC_FIELD_TRANSPORT_PARAMS, transport_params_record); } if (!itsAdiosOperatorParamsVec.empty()) { Record operator_params_record; for (const auto ¶ms : itsAdiosOperatorParamsVec) { auto itVar = params.find("Variable"); if (itVar == params.end()) { continue; } auto itOper = params.find("Operator"); if (itOper == params.end()) { continue; } operator_params_record.defineRecord(itVar->second, to_record(params)); } record.defineRecord(SPEC_FIELD_OPERATOR_PARAMS, operator_params_record); } return record; } DataManager *Adios2StMan::impl::clone() const { return new Adios2StMan( #ifdef HAVE_MPI itsMpiComm, #endif itsAdiosEngineType, itsAdiosEngineParams, itsAdiosTransportParamsVec, itsAdiosOperatorParamsVec, itsAdiosConfigFile ); } String Adios2StMan::impl::dataManagerType() const { return DATA_MANAGER_TYPE; } void Adios2StMan::impl::addRow64(rownr_t aNrRows) { itsRows += aNrRows; } void Adios2StMan::impl::create64(rownr_t aNrRows) { itsRows = aNrRows; itsAdiosEngine = std::make_shared( itsAdiosIO->Open(fileName() + ".bp", adios2::Mode::Write)); itsAdiosEngine->BeginStep(); for (uInt i = 0; i < ncolumn(); ++i) { itsColumnPtrBlk[i]->create(itsAdiosEngine, 'w'); } } rownr_t Adios2StMan::impl::open64(rownr_t aNrRows, AipsIO &ios) { itsRows = aNrRows; itsAdiosEngine = std::make_shared( itsAdiosIO->Open(fileName() + ".bp", adios2::Mode::Read)); itsAdiosEngine->BeginStep(); for (uInt i = 0; i < ncolumn(); ++i) { itsColumnPtrBlk[i]->create(itsAdiosEngine, 'r'); } ios.getstart(DATA_MANAGER_TYPE); ios >> itsDataManName; { // see comment on flush() int dummy; ios >> dummy; } ios.getend(); itsRows = aNrRows; return itsRows; } void Adios2StMan::impl::deleteManager() {} DataManagerColumn *Adios2StMan::impl::makeScalarColumn(const String &name, int aDataType, const String &dataTypeId) { return makeColumnCommon(name, aDataType, dataTypeId); } DataManagerColumn *Adios2StMan::impl::makeDirArrColumn(const String &name, int aDataType, const String &dataTypeId) { return makeColumnCommon(name, aDataType, dataTypeId); } DataManagerColumn *Adios2StMan::impl::makeIndArrColumn(const String &name, int aDataType, const String &dataTypeId) { return makeColumnCommon(name, aDataType, dataTypeId); } DataManagerColumn *Adios2StMan::impl::makeColumnCommon(const String &name, int aDataType, const String &/*dataTypeId*/) { if (ncolumn() >= itsColumnPtrBlk.nelements()) { itsColumnPtrBlk.resize(itsColumnPtrBlk.nelements() + 32); } Adios2StManColumn *aColumn; switch (aDataType) { case TpBool: case TpArrayBool: aColumn = new Adios2StManColumnT(this, aDataType, name, itsAdiosIO); break; case TpChar: case TpArrayChar: aColumn = new Adios2StManColumnT(this, aDataType, name, itsAdiosIO); break; case TpUChar: case TpArrayUChar: aColumn = new Adios2StManColumnT(this, aDataType, name, itsAdiosIO); break; case TpShort: case TpArrayShort: aColumn = new Adios2StManColumnT(this, aDataType, name, itsAdiosIO); break; case TpUShort: case TpArrayUShort: aColumn = new Adios2StManColumnT(this, aDataType, name, itsAdiosIO); break; case TpInt: case TpArrayInt: aColumn = new Adios2StManColumnT(this, aDataType, name, itsAdiosIO); break; case TpUInt: case TpArrayUInt: aColumn = new Adios2StManColumnT(this, aDataType, name, itsAdiosIO); break; case TpInt64: case TpArrayInt64: aColumn = new Adios2StManColumnT(this, aDataType, name, itsAdiosIO); break; case TpFloat: case TpArrayFloat: aColumn = new Adios2StManColumnT(this, aDataType, name, itsAdiosIO); break; case TpDouble: case TpArrayDouble: aColumn = new Adios2StManColumnT(this, aDataType, name, itsAdiosIO); break; case TpComplex: case TpArrayComplex: aColumn = new Adios2StManColumnT>(this, aDataType, name, itsAdiosIO); break; case TpDComplex: case TpArrayDComplex: aColumn = new Adios2StManColumnT>(this, aDataType, name, itsAdiosIO); break; case TpString: case TpArrayString: aColumn = new Adios2StManColumnString(this, aDataType, name, itsAdiosIO); break; default: throw (DataManInvDT (name)); } itsColumnPtrBlk[ncolumn()] = aColumn; return aColumn; } rownr_t Adios2StMan::impl::getNrRows() { return itsRows; } rownr_t Adios2StMan::impl::resync64(rownr_t /*aNrRows*/) { return itsRows; } Bool Adios2StMan::impl::flush(AipsIO &ios, Bool /*doFsync*/) { ios.putstart(DATA_MANAGER_TYPE, 2); ios << itsDataManName; // Here we used to write itsStManColumnType (int), but that was an otherwise // unused member, so we are writing a dummy 0 instead to preserve backwards // compatibility ios << 0; ios.putend(); return true; } String Adios2StMan::impl::dataManagerName() const { return itsDataManName; } void register_adios2stman() { DataManager::registerCtor("Adios2StMan", Adios2StMan::makeObject); } } casacore-3.7.1/tables/DataMan/Adios2StMan.h000066400000000000000000000110341476623553700203370ustar00rootroot00000000000000//# Adios2StMan.h: Base class of the ADIOS2 Storage Manager //# Copyright (C) 2018 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef ADIOS2STMAN_H #define ADIOS2STMAN_H #include #include #include #include #ifdef HAVE_MPI #include #endif #include #include #include namespace casacore { class Adios2StMan : public DataManager { friend class Adios2StManColumn; template friend class Adios2StManColumnT; public: // tag for differentiating string-based constructors struct from_config_t {}; constexpr static from_config_t from_config {}; #ifdef HAVE_MPI Adios2StMan( MPI_Comm mpiComm, std::string engineType = {}, std::map engineParams = {}, std::vector> transportParams = {}, std::vector> operatorParams = {}); Adios2StMan(MPI_Comm mpiComm, std::string configFile, from_config_t); #endif // HAVE_MPI Adios2StMan( std::string engineType = {}, std::map engineParams = {}, std::vector> transportParams = {}, std::vector> operatorParams = {}); Adios2StMan(std::string configFile, from_config_t); virtual ~Adios2StMan(); virtual DataManager *clone() const; virtual String dataManagerType() const; virtual String dataManagerName() const; virtual void create64(rownr_t aNrRows); virtual rownr_t open64(rownr_t aRowNr, AipsIO &ios); virtual rownr_t resync64(rownr_t aRowNr); virtual Bool flush(AipsIO &, Bool doFsync); virtual DataManagerColumn *makeScalarColumn(const String &aName, int aDataType, const String &aDataTypeID); virtual DataManagerColumn *makeDirArrColumn(const String &aName, int aDataType, const String &aDataTypeID); virtual DataManagerColumn *makeIndArrColumn(const String &aName, int aDataType, const String &aDataTypeID); virtual void deleteManager(); virtual void addRow64(rownr_t aNrRows); static DataManager *makeObject(const String &aDataManType, const Record &spec); Record dataManagerSpec() const; rownr_t getNrRows(); private: class impl; std::unique_ptr pimpl; #ifdef HAVE_MPI Adios2StMan( MPI_Comm mpiComm, std::string engineType, std::map engineParams, std::vector> transportParams, std::vector> operatorParams, std::string configFile); #endif // HAVE_MPI Adios2StMan( std::string engineType, std::map engineParams, std::vector> transportParams, std::vector> operatorParams, std::string configFile); }; // end of class Adios2StMan extern "C" void register_adios2stman(); } // end of namespace casa #endif casacore-3.7.1/tables/DataMan/Adios2StManColumn.cc000066400000000000000000000313671476623553700216660ustar00rootroot00000000000000//# Adios2StManColumn.cc: The Column of the ADIOS2 Storage Manager //# Copyright (C) 2018 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include "Adios2StManColumn.h" namespace casacore { Adios2StManColumn::Adios2StManColumn( Adios2StMan::impl *aParent, int aDataType, String aColName, std::shared_ptr aAdiosIO) :StManColumnBase(aDataType), itsStManPtr(aParent), itsColumnName(aColName), itsAdiosIO(aAdiosIO) { } String Adios2StManColumn::getColumnName() { return itsColumnName; } void Adios2StManColumn::setShapeColumn(const IPosition &aShape) { isShapeFixed = true; itsCasaShape = aShape; itsAdiosShape.resize(aShape.size() + 1); itsAdiosStart.resize(aShape.size() + 1); itsAdiosCount.resize(aShape.size() + 1); for (size_t i = 0; i < aShape.size(); ++i) { itsAdiosShape[i + 1] = aShape[aShape.size() - i - 1]; itsAdiosCount[i + 1] = aShape[aShape.size() - i - 1]; itsAdiosStart[i + 1] = 0; } } IPosition Adios2StManColumn::shape(rownr_t aRowNr) { if(isShapeFixed) { return itsCasaShape; } else { auto shape = itsCasaShapes.find(aRowNr); if(shape != itsCasaShapes.end()) { return shape->second; } else { return IPosition(); } } } Bool Adios2StManColumn::canChangeShape() const { return !isShapeFixed; } void Adios2StManColumn::setShape (rownr_t aRowNr, const IPosition& aShape) { itsCasaShapes[aRowNr] = aShape; } void Adios2StManColumn::scalarToSelection(rownr_t rownr) { itsAdiosStart[0] = rownr; itsAdiosCount[0] = 1; } void Adios2StManColumn::scalarColumnVToSelection() { itsAdiosStart[0] = 0; itsAdiosCount[0] = itsStManPtr->getNrRows(); } void Adios2StManColumn::arrayVToSelection(rownr_t rownr) { if(isShapeFixed) { itsAdiosStart[0] = rownr; itsAdiosCount[0] = 1; for (size_t i = 1; i < itsAdiosShape.size(); ++i) { itsAdiosStart[i] = 0; itsAdiosCount[i] = itsAdiosShape[i]; } } else { auto casaShape = itsCasaShapes.find(rownr); if(casaShape != itsCasaShapes.end()) { itsAdiosShape.resize(casaShape->second.size() + 1); itsAdiosStart.resize(casaShape->second.size() + 1); itsAdiosCount.resize(casaShape->second.size() + 1); itsAdiosStart[0] = rownr; itsAdiosCount[0] = 1; for (size_t i = 0; i < casaShape->second.size(); ++i) { itsAdiosShape[i + 1] = casaShape->second[casaShape->second.size() - i - 1]; itsAdiosCount[i + 1] = casaShape->second[casaShape->second.size() - i - 1]; itsAdiosStart[i + 1] = 0; } } else { cerr << "Shape of Row " << rownr << " has not been set" << endl; } } } void Adios2StManColumn::arrayColumnVToSelection() { itsAdiosStart[0] = 0; itsAdiosCount[0] = itsStManPtr->getNrRows(); for (size_t i = 1; i < itsAdiosShape.size(); ++i) { itsAdiosStart[i] = 0; itsAdiosCount[i] = itsAdiosShape[i]; } } void Adios2StManColumn::scalarColumnCellsVToSelection(const RefRows &rows) { RefRowsSliceIter iter(rows); auto row_start = iter.sliceStart(); auto row_end = iter.sliceEnd(); iter.next(); if (!iter.pastEnd()) { throw std::runtime_error("Adios2StManColumn::scalarColumnCellsVToSelection supports single slices"); } itsAdiosStart[0] = row_start; itsAdiosCount[0] = row_end - row_start + 1; for (size_t i = 1; i < itsAdiosShape.size(); ++i) { itsAdiosStart[i] = 0; itsAdiosCount[i] = itsAdiosShape[i]; } } void Adios2StManColumn::sliceVToSelection(rownr_t rownr, const Slicer &ns) { columnSliceCellsVToSelection(rownr, 1, ns); } void Adios2StManColumn::columnSliceVToSelection(const Slicer &ns) { columnSliceCellsVToSelection(0, itsStManPtr->getNrRows(), ns); } void Adios2StManColumn::columnSliceCellsVToSelection(const RefRows &rows, const Slicer &ns) { RefRowsSliceIter iter(rows); auto row_start = iter.sliceStart(); auto row_end = iter.sliceEnd(); iter.next(); if (!iter.pastEnd()) { throw std::runtime_error("Adios2StManColumn::columnSliceCellsVToSelection supports single slices"); } columnSliceCellsVToSelection(row_start, row_end - row_start + 1, ns); } void Adios2StManColumn::columnSliceCellsVToSelection(rownr_t row_start, rownr_t row_count, const Slicer &ns) { itsAdiosStart[0] = row_start; itsAdiosCount[0] = row_count; for (size_t i = 1; i < itsAdiosShape.size(); ++i) { itsAdiosStart[i] = ns.start()(ns.ndim() - i); itsAdiosCount[i] = ns.length()(ns.ndim() - i); } } void Adios2StManColumn::putArrayV(rownr_t rownr, const ArrayBase& data) { arrayVToSelection(rownr); toAdios(&data); } void Adios2StManColumn::getArrayV(rownr_t rownr, ArrayBase& data) { arrayVToSelection(rownr); fromAdios(&data); } void Adios2StManColumn::putScalar(rownr_t rownr, const void *dataPtr) { scalarToSelection(rownr); toAdios(dataPtr); } void Adios2StManColumn::getScalar(rownr_t rownr, void *data) { scalarToSelection(rownr); fromAdios(data); } void Adios2StManColumn::putScalarColumnV(const ArrayBase &data) { scalarColumnVToSelection(); toAdios(&data); } void Adios2StManColumn::getScalarColumnV(ArrayBase &data) { scalarColumnVToSelection(); fromAdios(&data); } void Adios2StManColumn::getScalarColumnCellsV(const RefRows &rownrs, ArrayBase& data) { scalarColumnCellsVToSelection(rownrs); fromAdios(&data); } void Adios2StManColumn::putScalarColumnCellsV(const RefRows &rownrs, const ArrayBase& data) { scalarColumnCellsVToSelection(rownrs); toAdios(&data); } void Adios2StManColumn::putArrayColumnCellsV (const RefRows& rownrs, const ArrayBase& data) { if(rownrs.isSliced()) { rownrs.convert(); } Bool deleteIt; const void *dataPtr = data.getVStorage(deleteIt); itsAdiosCount[0] = 1; for (size_t i = 1; i < itsAdiosShape.size(); ++i) { itsAdiosStart[i] = 0; itsAdiosCount[i] = itsAdiosShape[i]; } for(uInt i = 0; i < rownrs.rowVector().size(); ++i) { itsAdiosStart[0] = rownrs.rowVector()[i]; toAdios(dataPtr, i * itsCasaShape.nelements()); } data.freeVStorage(dataPtr, deleteIt); } void Adios2StManColumn::getArrayColumnCellsV (const RefRows& rownrs, ArrayBase &data) { if(rownrs.isSliced()) { rownrs.convert(); } Bool deleteIt; void *dataPtr = data.getVStorage(deleteIt); itsAdiosCount[0] = 1; for (size_t i = 1; i < itsAdiosShape.size(); ++i) { itsAdiosStart[i] = 0; itsAdiosCount[i] = itsAdiosShape[i]; } for(uInt i = 0; i < rownrs.rowVector().size(); ++i) { itsAdiosStart[0] = rownrs.rowVector()[i]; fromAdios(dataPtr, i * itsCasaShape.nelements()); } data.putVStorage(dataPtr, deleteIt); } void Adios2StManColumn::getSliceV(rownr_t aRowNr, const Slicer &ns, ArrayBase& data) { sliceVToSelection(aRowNr, ns); fromAdios(&data); } void Adios2StManColumn::putSliceV(rownr_t aRowNr, const Slicer &ns, const ArrayBase& data) { sliceVToSelection(aRowNr, ns); toAdios(&data); } void Adios2StManColumn::getArrayColumnV(ArrayBase& data) { arrayColumnVToSelection(); fromAdios(&data); } void Adios2StManColumn::putArrayColumnV(const ArrayBase& data) { arrayColumnVToSelection(); toAdios(&data); } void Adios2StManColumn::putColumnSliceV(const Slicer &ns, const ArrayBase& data) { columnSliceVToSelection(ns); toAdios(&data); } void Adios2StManColumn::getColumnSliceV(const Slicer &ns, ArrayBase& data) { columnSliceVToSelection(ns); fromAdios(&data); } void Adios2StManColumn::getColumnSliceCellsV(const RefRows& rownrs, const Slicer& slicer, ArrayBase& data) { columnSliceCellsVToSelection(rownrs, slicer); fromAdios(&data); } void Adios2StManColumn::putColumnSliceCellsV(const RefRows& rownrs, const Slicer& slicer, const ArrayBase& data) { columnSliceCellsVToSelection(rownrs, slicer); toAdios(&data); } #define DEFINE_GETPUT(T) \ void Adios2StManColumn::put ## T(rownr_t rownr, const T *dataPtr) \ { \ putScalar(rownr, dataPtr); \ } \ \ void Adios2StManColumn::get ## T(rownr_t rownr, T *dataPtr) \ { \ getScalar(rownr, dataPtr); \ } DEFINE_GETPUT(Bool) DEFINE_GETPUT(uChar) DEFINE_GETPUT(Short) DEFINE_GETPUT(uShort) DEFINE_GETPUT(Int) DEFINE_GETPUT(uInt) DEFINE_GETPUT(float) DEFINE_GETPUT(double) DEFINE_GETPUT(Complex) DEFINE_GETPUT(DComplex) DEFINE_GETPUT(Int64) #undef DEFINE_GETPUT // string template<> void Adios2StManColumnT::create(std::shared_ptr aAdiosEngine, char /*aOpenMode*/) { itsAdiosEngine = aAdiosEngine; } void Adios2StManColumn::putString(rownr_t rownr, const String *dataPtr) { std::string variableName = static_cast(itsColumnName) + std::to_string(rownr); adios2::Variable v = itsAdiosIO->InquireVariable(variableName); if (!v) { v = itsAdiosIO->DefineVariable(variableName); } itsAdiosEngine->Put(v, reinterpret_cast(dataPtr), adios2::Mode::Sync); } void Adios2StManColumn::getString(rownr_t rownr, String *dataPtr) { std::string variableName = static_cast(itsColumnName) + std::to_string(rownr); adios2::Variable v = itsAdiosIO->InquireVariable(variableName); if (v) { itsAdiosEngine->Get(v, reinterpret_cast(dataPtr), adios2::Mode::Sync); } } void Adios2StManColumnString::putArrayV(rownr_t rownr, const ArrayBase& data) { String combined; Bool deleteIt; auto *arrayPtr = reinterpret_cast *>(&data); const String *dataPtr = arrayPtr->getStorage(deleteIt); for(auto &i : *arrayPtr) { combined = combined + i + itsStringArrayBarrier; } arrayPtr->freeStorage(dataPtr, deleteIt); putString(rownr, &combined); } void Adios2StManColumnString::getArrayV(rownr_t rownr, ArrayBase& data) { String combined; getString(rownr, &combined); Bool deleteIt; auto *arrayPtr = reinterpret_cast *>(&data); String *dataPtr = arrayPtr->getStorage(deleteIt); size_t pos = 0; for(auto &i : *arrayPtr) { size_t found = combined.find(itsStringArrayBarrier, pos); if(found != std::string::npos) { i = combined.substr(pos, found - pos); pos = found + itsStringArrayBarrier.length(); } } arrayPtr->putStorage(dataPtr, deleteIt); } void Adios2StManColumnString::getSliceV(rownr_t /*aRowNr*/, const Slicer &/*ns*/, ArrayBase &/*data*/) { throw std::runtime_error("Not implemented yet"); } void Adios2StManColumnString::putSliceV(rownr_t /*aRowNr*/, const Slicer &/*ns*/, const ArrayBase &/*data*/) { throw std::runtime_error("Not implemented yet"); } void Adios2StManColumnString::getColumnSliceV(const Slicer &/*ns*/, ArrayBase &/*data*/) { throw std::runtime_error("Not implemented yet"); } void Adios2StManColumnString::putColumnSliceV(const Slicer &/*ns*/, const ArrayBase &/*data*/) { throw std::runtime_error("Not implemented yet"); } void Adios2StManColumnString::getColumnSliceCellsV(const RefRows& /*rownrs*/, const Slicer& /*slicer*/, ArrayBase& /*data*/) { throw std::runtime_error("Not implemented yet"); } void Adios2StManColumnString::putColumnSliceCellsV(const RefRows& /*rownrs*/, const Slicer& /*slicer*/, const ArrayBase& /*data*/) { throw std::runtime_error("Not implemented yet"); } } // namespace casacore casacore-3.7.1/tables/DataMan/Adios2StManColumn.h000066400000000000000000000224611476623553700215230ustar00rootroot00000000000000//# Adios2StManColumn.h: A Column in the ADIOS2 Storage Manager //# Copyright (C) 2018 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef ADIOS2STMANCOLUMN_H #define ADIOS2STMANCOLUMN_H #include #include #include #include #include "Adios2StManImpl.h" namespace casacore { class Adios2StManColumn : public StManColumnBase { public: Adios2StManColumn(Adios2StMan::impl *aParent, int aDataType, String aColName, std::shared_ptr aAdiosIO); virtual void create(std::shared_ptr aAdiosEngine, char aOpenMode) = 0; virtual void setShapeColumn(const IPosition &aShape) override; virtual IPosition shape(rownr_t aRowNr) override; Bool canChangeShape() const override; void setShape (rownr_t aRowNr, const IPosition& aShape) override; int getDataTypeSize(); int getDataType(); String getColumnName(); protected: // scalar get/put virtual void getBool(rownr_t aRowNr, Bool *aDataPtr) override; virtual void getuChar(rownr_t aRowNr, uChar *aDataPtr) override; virtual void getShort(rownr_t aRowNr, Short *aDataPtr) override; virtual void getuShort(rownr_t aRowNr, uShort *aDataPtr) override; virtual void getInt(rownr_t aRowNr, Int *aDataPtr) override; virtual void getuInt(rownr_t aRowNr, uInt *aDataPtr) override; virtual void getInt64(rownr_t aRowNr, Int64 *aDataPtr) override; virtual void getfloat(rownr_t aRowNr, Float *aDataPtr) override; virtual void getdouble(rownr_t aRowNr, Double *aDataPtr) override; virtual void getComplex(rownr_t aRowNr, Complex *aDataPtr) override; virtual void getDComplex(rownr_t aRowNr, DComplex *aDataPtr) override; virtual void getString(rownr_t aRowNr, String *aDataPtr) override; virtual void putBool(rownr_t aRowNr, const Bool *aDataPtr) override; virtual void putuChar(rownr_t aRowNr, const uChar *aDataPtr) override; virtual void putShort(rownr_t aRowNr, const Short *aDataPtr) override; virtual void putuShort(rownr_t aRowNr, const uShort *aDataPtr) override; virtual void putInt(rownr_t aRowNr, const Int *aDataPtr) override; virtual void putuInt(rownr_t aRowNr, const uInt *aDataPtr) override; virtual void putInt64(rownr_t aRowNr, const Int64 *aDataPtr) override; virtual void putfloat(rownr_t aRowNr, const Float *aDataPtr) override; virtual void putdouble(rownr_t aRowNr, const Double *aDataPtr) override; virtual void putComplex(rownr_t aRowNr, const Complex *aDataPtr) override; virtual void putDComplex(rownr_t aRowNr, const DComplex *aDataPtr) override; virtual void putString(rownr_t aRowNr, const String *aDataPtr) override; // The rest of the get and put functions virtual void getScalarColumnV (ArrayBase& dataPtr) override; virtual void putScalarColumnV (const ArrayBase& dataPtr) override; virtual void getScalarColumnCellsV (const RefRows& rownrs, ArrayBase& dataPtr) override; virtual void putScalarColumnCellsV (const RefRows& rownrs, const ArrayBase& dataPtr) override; virtual void getArrayV (rownr_t rownr, ArrayBase& dataPtr) override; virtual void putArrayV (rownr_t rownr, const ArrayBase& data) override; virtual void getArrayColumnV (ArrayBase& data) override; virtual void putArrayColumnV (const ArrayBase& data) override; virtual void getArrayColumnCellsV (const RefRows& rownrs, ArrayBase& data) override; virtual void putArrayColumnCellsV (const RefRows& rownrs, const ArrayBase& data) override; virtual void getSliceV (rownr_t rownr, const Slicer& slicer, ArrayBase& data) override; virtual void putSliceV (rownr_t rownr, const Slicer& slicer, const ArrayBase& data) override; virtual void getColumnSliceV (const Slicer& slicer, ArrayBase& data) override; virtual void putColumnSliceV (const Slicer& slicer, const ArrayBase& data) override; virtual void getColumnSliceCellsV (const RefRows& rownrs, const Slicer& slicer, ArrayBase& data) override; virtual void putColumnSliceCellsV (const RefRows& rownrs, const Slicer& slicer, const ArrayBase& data) override; private: void putScalar(rownr_t rownr, const void *dataPtr); void getScalar(rownr_t rownr, void *dataPtr); virtual void toAdios(const ArrayBase *arrayPtr) = 0; virtual void fromAdios(ArrayBase *arrayPtr) = 0; virtual void toAdios(const void *dataPtr, std::size_t offset=0) = 0; virtual void fromAdios(void *dataPtr, std::size_t offset=0) = 0; protected: void scalarToSelection(rownr_t rownr); void scalarColumnVToSelection(); void scalarColumnCellsVToSelection(const RefRows &rownrs); void arrayVToSelection(rownr_t rownr); void arrayColumnVToSelection(); void sliceVToSelection(rownr_t rownr, const Slicer &ns); void columnSliceVToSelection(const Slicer &ns); void columnSliceCellsVToSelection(const RefRows &rows, const Slicer &ns); void columnSliceCellsVToSelection(rownr_t row_start, rownr_t row_end, const Slicer &ns); Adios2StMan::impl *itsStManPtr; String itsColumnName; IPosition itsCasaShape; std::unordered_map itsCasaShapes; Bool isShapeFixed = false; std::shared_ptr itsAdiosIO; std::shared_ptr itsAdiosEngine; std::string itsAdiosDataType; adios2::Dims itsAdiosShape = {std::numeric_limits::max()}; adios2::Dims itsAdiosStart = {0}; adios2::Dims itsAdiosCount = {1}; }; // class Adios2StManColumn template class Adios2StManColumnT : public Adios2StManColumn { public: using Adios2StManColumn::Adios2StManColumn; void create(std::shared_ptr aAdiosEngine, char aOpenMode) { itsAdiosEngine = aAdiosEngine; itsAdiosVariable = itsAdiosIO->InquireVariable(itsColumnName); if (!itsAdiosVariable && aOpenMode == 'w') { itsAdiosVariable = itsAdiosIO->DefineVariable( itsColumnName, itsAdiosShape, itsAdiosStart, itsAdiosCount); } } private: adios2::Variable itsAdiosVariable; void toAdios(const void *data, std::size_t offset) { const T *tData = static_cast(data); if(!isShapeFixed) itsAdiosVariable.SetShape(itsAdiosShape); itsAdiosVariable.SetSelection({itsAdiosStart, itsAdiosCount}); itsAdiosEngine->Put(itsAdiosVariable, tData + offset, adios2::Mode::Sync); } void fromAdios(void *data, std::size_t offset) { T *tData = static_cast(data); itsAdiosVariable.SetSelection({itsAdiosStart, itsAdiosCount}); itsAdiosEngine->Get(itsAdiosVariable, tData + offset, adios2::Mode::Sync); } void toAdios(const ArrayBase *arrayPtr) { Bool deleteIt; const void *data = arrayPtr->getVStorage(deleteIt); toAdios(data, 0); arrayPtr->freeVStorage (data, deleteIt); } void fromAdios(ArrayBase *arrayPtr) { Bool deleteIt; void *data = arrayPtr->getVStorage(deleteIt); fromAdios(data, 0); arrayPtr->putVStorage(data, deleteIt); } }; // class Adios2StManColumnT class Adios2StManColumnString : public Adios2StManColumnT { public: using Adios2StManColumnT::Adios2StManColumnT; protected: void putArrayV(rownr_t rownr, const ArrayBase& data); void getArrayV(rownr_t rownr, ArrayBase& data); void getSliceV(rownr_t /*aRowNr*/, const Slicer &/*ns*/, ArrayBase &/*data*/); void putSliceV(rownr_t /*aRowNr*/, const Slicer &/*ns*/, const ArrayBase &/*data*/); void getColumnSliceV(const Slicer &/*ns*/, ArrayBase &/*data*/); void putColumnSliceV(const Slicer &/*ns*/, const ArrayBase &/*data*/); void getColumnSliceCellsV(const RefRows& /*rownrs*/, const Slicer& /*slicer*/, ArrayBase& /*data*/); void putColumnSliceCellsV(const RefRows& /*rownrs*/, const Slicer& /*slicer*/, const ArrayBase& /*data*/); private: const String itsStringArrayBarrier = "ADIOS2BARRIER"; }; // class Adios2StManColumnString } // namespace casacore #endif // ADIOS2STMANCOLUMN_H casacore-3.7.1/tables/DataMan/Adios2StManImpl.h000066400000000000000000000117751476623553700211750ustar00rootroot00000000000000//# Adios2StManImpl.h: Implementation class definition of the ADIOS2 Storage Manager // //# ICRAR - International Centre for Radio Astronomy Research //# (c) UWA - The University of Western Australia, 2018 //# Copyright by UWA (in the framework of the ICRAR) //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusettes Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# #ifndef ADIOS2STMANIMPL_H #define ADIOS2STMANIMPL_H #include #include "Adios2StMan.h" namespace casacore { class Adios2StManColumn; class Adios2StMan::impl { public: impl(Adios2StMan &parent, void *mpiComm, std::string engineType, std::map engineParams, std::vector> transportParams, std::vector> operatorParams, std::string configFile); ~impl(); #ifdef HAVE_MPI void checkMPI() const; #endif DataManager *clone() const; String dataManagerType() const; String dataManagerName() const; void create64(rownr_t aNrRows); rownr_t open64(rownr_t aRowNr, AipsIO &ios); rownr_t resync64(rownr_t aRowNr); Bool flush(AipsIO &ios, Bool doFsync); DataManagerColumn *makeColumnCommon(const String &aName, int aDataType, const String &aDataTypeID); DataManagerColumn *makeScalarColumn(const String &aName, int aDataType, const String &aDataTypeID); DataManagerColumn *makeDirArrColumn(const String &aName, int aDataType, const String &aDataTypeID); DataManagerColumn *makeIndArrColumn(const String &aName, int aDataType, const String &aDataTypeID); void deleteManager(); void addRow64(rownr_t aNrRows); static DataManager *makeObject(const String &aDataManType, const Record &spec); Record dataManagerSpec() const; rownr_t getNrRows(); private: Adios2StMan &parent; String itsDataManName = "Adios2StMan"; rownr_t itsRows {0}; PtrBlock itsColumnPtrBlk; std::shared_ptr itsAdios; std::shared_ptr itsAdiosIO; std::shared_ptr itsAdiosEngine; #ifdef HAVE_MPI // MPI communicator to be used by all instances of this storage manager static MPI_Comm itsMpiComm; #endif // The ADIOS2 Engine type std::string itsAdiosEngineType; // Parameters for the ADIOS2 engine adios2::Params itsAdiosEngineParams; // Parameters for the ADIOS2 transports std::vector itsAdiosTransportParamsVec; // Parameters for the ADIOS2 operators (compressors) std::vector itsAdiosOperatorParamsVec; // The ADIOS2 XML configuration file std::string itsAdiosConfigFile; // The type of this storage manager static constexpr const char *DATA_MANAGER_TYPE = "Adios2StMan"; // The name of the specification field for the ADIOS2 XML configuration file static constexpr const char *SPEC_FIELD_XML_FILE = "XMLFILE"; // The name of the specification field for the ADIOS2 engine type static constexpr const char *SPEC_FIELD_ENGINE_TYPE = "ENGINETYPE"; // The name of the specification field for the ADIOS2 engine parameters static constexpr const char *SPEC_FIELD_ENGINE_PARAMS = "ENGINEPARAMS"; // The name of the specification field for the ADIOS2 transport parameters static constexpr const char *SPEC_FIELD_TRANSPORT_PARAMS = "TRANSPORTPARAMS"; // The name of the specification field for the ADIOS2 operator parameters static constexpr const char *SPEC_FIELD_OPERATOR_PARAMS = "OPERATORPARAMS"; void configureAdios(); uInt ncolumn() const { return parent.ncolumn(); } String fileName() const { return parent.fileName(); } }; } // namespace casacore #endif // ADIOS2STMANIMPL_H casacore-3.7.1/tables/DataMan/BaseMappedArrayEngine.h000066400000000000000000000466021476623553700224120ustar00rootroot00000000000000//# BaseMappedArrayEngine.h: Abstract virtual column engine for virtual->stored mapping //# Copyright (C) 1995,1996,1997,1999,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_BASEMAPPEDARRAYENGINE_H #define TABLES_BASEMAPPEDARRAYENGINE_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class ArrayColumn; class TableColumn; // // Templated virtual column engine for a table array of any type. // // // // // //# Classes you should understand before using this one. //
      • VirtualColumnEngine //
      • VirtualArrayColumn // // // BaseMappedArrayEngine contains for the 1-1 mapping of a virtual // column to a stored column (both containing arrays). // // // BaseMappedArrayEngine is an abstract base class for virtual column engines // which map data from the arrays in the virtual column to // the arrays in the stored column. Note the the stored column does not need // to be stored; it can be another virtual column, but usually it will be a // stored column. // Examples of classes using this base class are // ScaledArrayEngine and // RetypedArrayEngine. // // The virtual column has to be bound to the virtual column engine used // for it. The stored column will usually be bound to a storage manager, // but any other suitable data manager is possible. E.g. it is // possible to use MappedArrayEngine // to map a StokesVector to a float column, which in its turn uses // ScaledArrayEngine to store it as integers. // Note that the names of the virtual and stored column have to be different, // otherwise the table system cannot distinguish them. // // This base class does several tasks for the derived classes. // The main one is to keep and handle the information about the virtual // and stored column. The name of the stored column is written as a keyword // in the virtual column. In this way the stored column is known when // a table is read back. It also creates (RO)ArrayColumn // objects to access the stored column. The function roColumn gives // read access, while rwColumn gives write access. // // An engine object should be used for one column only, because the stored // column name is part of the engine. If it would be used for more than // one column, they would all share the same stored column. // When the engine is bound to a column, it is checked if the name // of that column matches the given virtual column name. // // The engine can be used for a column containing any kind of array // (thus direct or indirect, fixed or variable shaped)) as long as the // virtual array can be stored in the stored array. Thus a fixed shaped // virtual can use a variable shaped stored, but not vice versa. // A fixed shape indirect virtual can use a stored with direct arrays. // // The DataManager framework contains various virtual functions. // This class implements several, but not all of them. Furthermore // some implementations may not be optimal or correct for derived classes. // Hereafter follows a list of functions which may need implementation // in derived classes. The classes mentioned in the examples below show // implementations of these functions. //
          //
        • // The following (virtual) functions have to be implemented: //
          //
          // ~... (the destructor) // //
          // DataManager* clone() const; // //
          // String dataManagerType() const; // //
          // static void registerClass(); // //
          // static DataManager* makeObject (const String& dataManagerType); // //
          // void getArray (rownr_t rownr, Array& data); // //
          // void putArray (rownr_t rownr, const Array& data); // // (only if the virtual column is writable). //
          //
        • // For efficiency reasons it could be better to implement the following // functions: //
          //
          // void getSlice (rownr_t rownr, const Slicer& slicer, Array& data); // //
          // void putSlice (rownr_t rownr, const Slicer& slicer, // const Array& data); // //
          // void getArrayColumn (Array& data); // //
          // void putArrayColumn (const Array& data); // //
          // void getColumnSlice (const Slicer& slicer, Array& data); // //
          // void putColumnSlice (const Slicer& slicer, const Array& data); // //
          //
        • // The following functions have to be implemented when the shapes // of the virtual and stored arrays are not the same. //
          //
          // void setShapeColumn (const IPosition& shape); // //
          // void setShape (rownr_t rownr, const IPosition& shape); // //
          // uInt ndim (rownr_t rownr); // //
          // IPosition shape (rownr_t rownr); // //
          //
        • // The following functions deal with the initialization and persistence // of engine specific variables. When the class has variables of its // own, these functions may need to be implemented. Implementations of // create and prepare have to call the similar functions in this base class. //
          //
          // void close (AipsIO& ios); // //
          // void create64 (rownr_t nrrow); // //
          // void open64 (rownr_t nrrow, AipsIO& ios); // //
          // void prepare(); // //
          //
        • // The following functions do not need to be declared and implemented // in derived classes unless it is a very special case. //
          //
          // String dataManagerName() const; // //
          // Bool canAddRow() const; // //
          // Bool canRemoveRow() const; // //
          // void addRow64 (rownr_t nrrow); // //
          // void removeRow64 (rownr_t rownr); // //
          // DataManagerColumn* makeDirArrColumn (const String& columnName, // int dataType, // const String& dataTypeId); // //
          // DataManagerColumn* makeIndArrColumn (const String& columnName, // int dataType, // const String& dataTypeId); // //
          // Bool isWritable() const; // //
          // Bool isShapeDefined (rownr_t rownr); // //
          //
        //
        // // The derived classes // ScaledArrayEngine and // RetypedArrayEngine // are two examples of how to derive a class from this base class. // Note that ScaledArrayEngine does not need to implement functions // dealing with shapes, because it can use them from this base class. // On the other hand they need to be implemented in RetypedArrayEngine. // // // This base class implements several functions making the implementation // of derived classes simpler. Many details are implemented here, so often // only the basic mapping functions (get, put) need to be implemented // in a derived class. // // //
      • default constructor //
      • copy constructor //
      • assignment operator //
      • static String dataTypeId(); // unique name of the class // // //
      • Default constructor //
      • Copy constructor //
      • Assignment operator // template class BaseMappedArrayEngine : public VirtualColumnEngine, public VirtualArrayColumn { public: // Get the virtual column name. const String& virtualName() const; // Get the stored column name. const String& storedName() const; // The column is writable if the underlying stored column is writable. virtual Bool isWritable() const; protected: // Construct an engine to convert the virtual column to the stored column. // StoredColumnName is the name of the column where the converted // data will be put and must have data type StoredType. // The virtual column using this engine must have data type VirtualType. // By default the virtual column is assumed to be writable. // Use setWritable to unset it. BaseMappedArrayEngine (const String& virtualColumnName, const String& storedColumnName); // Destructor is mandatory. ~BaseMappedArrayEngine(); // The default constructor is required for reconstruction of the // engine when a table is read back. BaseMappedArrayEngine(); // Copy constructor is used by copy constructor of derived classes. BaseMappedArrayEngine (const BaseMappedArrayEngine&); // Assignment is not needed and therefore forbidden BaseMappedArrayEngine& operator= (const BaseMappedArrayEngine&) = delete; // Set if the column is writable or not. void setWritable (Bool isWritable); // Set the virtual and stored column name. void setNames (const String& virtualName, const String& storedName); // Give access to the stored column. // This can be used by the derived classes to get/put data. inline ArrayColumn& column(); // Create the column object for the array column in this engine. // It will check if the given column name matches the virtual // column name. This assures that the engine is bound to the // correct column. virtual DataManagerColumn* makeIndArrColumn (const String& columnName, int dataType, const String& dataTypeId); // Initialize the object for a new table. // It defines a virtual column keyword telling the stored column name. // Initially the table has the given number of rows. // A derived class can have its own create function, but that should // always call this create function. virtual void create64 (rownr_t initialNrrow); // Preparing consists of setting the writable switch and // adding the initial number of rows in case of create. // It reads the stored column name from the virtual column keywords. // A derived class can have its own prepare function, but that should // always call this prepare function. virtual void prepare(); // Do the 2 stages of the prepare (define columns and adding rows). // void prepare1(); void prepare2(); // // Rows are added to the end of the table. // If the virtual column has FixedShape arrays and the stored not, // the shape in each stored row will be set. // This assures that the arrays are properly defined in each row, // so putSlice can be used without problems. //
        The second version is used by prepare2, because in case a column is // added to an already existing table, table.nrow() gives the existing // number of columns instead of 0. // virtual void addRow64 (rownr_t nrrow); virtual void addRowInit (rownr_t startRow, rownr_t nrrow); // // Set the shape of the FixedShape arrays in the column. // This function only gets called if the column has FixedShape arrays. // The shape gets saved and used to set the shape of the arrays // in the stored in case the stored has non-FixedShape arrays. // This implementation assumes the shape of virtual and stored arrays // are the same. If not, it has to be overidden in a derived class. virtual void setShapeColumn (const IPosition& shape); // Define the shape of the array in the given row. // It will define the shape of the (underlying) array. // This implementation assumes the shape of virtual and stored arrays // are the same. If not, it has to be overidden in a derived class. virtual void setShape (rownr_t rownr, const IPosition& shape); // Test if the (underlying) array is defined in the given row. virtual Bool isShapeDefined (rownr_t rownr); // Get the dimensionality of the (underlying) array in the given row. // This implementation assumes the dimensionality of virtual and // stored arrays are the same. If not, it has to be overidden in a // derived class. virtual uInt ndim (rownr_t rownr); // Get the shape of the (underlying) array in the given row. // This implementation assumes the shape of virtual and stored arrays // are the same. If not, it has to be overidden in a derived class. virtual IPosition shape (rownr_t rownr); // The data manager can handle changing the shape of an existing array // when the underlying stored column can do it. virtual Bool canChangeShape() const; // Make a table column object for the given column. // This has to be used in the create function, otherwise it could not // create a TableColumn object to store data in the column keywords. TableColumn makeTableColumn (const String& columnName); // Get an array in the given row. // This will scale and offset from the underlying array. virtual void getArray (rownr_t rownr, Array& array); // Put an array in the given row. // This will scale and offset to the underlying array. virtual void putArray (rownr_t rownr, const Array& array); // Get a section of the array in the given row. // This will scale and offset from the underlying array. virtual void getSlice (rownr_t rownr, const Slicer& slicer, Array& array); // Put into a section of the array in the given row. // This will scale and offset to the underlying array. virtual void putSlice (rownr_t rownr, const Slicer& slicer, const Array& array); // Get an entire column. // This will scale and offset from the underlying array. virtual void getArrayColumn (Array& array); // Put an entire column. // This will scale and offset to the underlying array. virtual void putArrayColumn (const Array& array); // Get some array values in the column. // This will scale and offset from the underlying array. virtual void getArrayColumnCells (const RefRows& rownrs, Array& data); // Put some array values in the column. // This will scale and offset to the underlying array. virtual void putArrayColumnCells (const RefRows& rownrs, const Array& data); // Get a section of all arrays in the column. // This will scale and offset from the underlying array. void getColumnSlice (const Slicer& slicer, Array& array); // Put a section of all arrays in the column. // This will scale and offset to the underlying array. void putColumnSlice (const Slicer& slicer, const Array& array); // Get a section of some arrays in the column. // This will scale and offset from the underlying array. virtual void getColumnSliceCells (const RefRows& rownrs, const Slicer& slicer, Array& data); // Put into a section of some arrays in the column. // This will scale and offset to the underlying array. virtual void putColumnSliceCells (const RefRows& rownrs, const Slicer& slicer, const Array& data); // Map the virtual shape to the stored shape. // By default is returns the virtual shape. virtual IPosition getStoredShape (rownr_t rownr, const IPosition& virtualShape); // Map the slicer for a virtual shape to a stored shape. // By default it returns the virtual input slicer. virtual Slicer getStoredSlicer (const Slicer& virtualSlicer) const; // Map StoredType array to VirtualType array. // This is meant when reading an array from the stored column. // The default implementation throws an exception. virtual void mapOnGet (Array& array, const Array& stored); // Map Bool array to bit flags array. // This is meant when writing an array into the stored column. // The default implementation throws an exception. virtual void mapOnPut (const Array& array, Array& stored); private: //# Now define the data members. String virtualName_p; //# virtual column name String storedName_p; //# stored column name Bool isWritable_p; //# is virtual column writable? Bool tempWritable_p; //# True = create phase, so column //# is temporarily writable //# False = asks stored column rownr_t initialNrrow_p; //# initial #rows in case of create Bool arrayIsFixed_p; //# True = virtual is FixedShape array IPosition shapeFixed_p; //# shape in case FixedShape array ArrayColumn* column_p; //# the stored column }; template inline const String& BaseMappedArrayEngine::virtualName() const { return virtualName_p; } template inline const String& BaseMappedArrayEngine::storedName() const { return storedName_p; } template inline void BaseMappedArrayEngine::setNames (const String& virtualName, const String& storedName) { virtualName_p = virtualName; storedName_p = storedName; } template inline void BaseMappedArrayEngine::setWritable (Bool isWritable) { isWritable_p = isWritable; } template inline ArrayColumn& BaseMappedArrayEngine::column() { return *column_p; } } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/tables/DataMan/BaseMappedArrayEngine.tcc000066400000000000000000000333271476623553700227340ustar00rootroot00000000000000//# BaseMappedArrayEngine.cc: Abstract virtual column engine for virtual->stored mapping //# Copyright (C) 1995,1996,2001,2002 //# Associated Universitie Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_BASEMAPPEDARRAYENGINE_TCC #define TABLES_BASEMAPPEDARRAYENGINE_TCC //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template BaseMappedArrayEngine::BaseMappedArrayEngine () : virtualName_p (""), storedName_p (""), isWritable_p (True), tempWritable_p (False), initialNrrow_p (0), arrayIsFixed_p (False), column_p (0) {} template BaseMappedArrayEngine::BaseMappedArrayEngine (const String& virtualColumnName, const String& storedColumnName) : virtualName_p (virtualColumnName), storedName_p (storedColumnName), isWritable_p (True), tempWritable_p (False), initialNrrow_p (0), arrayIsFixed_p (False), column_p (0) {} template BaseMappedArrayEngine::BaseMappedArrayEngine (const BaseMappedArrayEngine& that) : VirtualColumnEngine(), VirtualArrayColumn(), virtualName_p (that.virtualName_p), storedName_p (that.storedName_p), isWritable_p (that.isWritable_p), tempWritable_p (False), initialNrrow_p (0), arrayIsFixed_p (False), column_p (0) {} template BaseMappedArrayEngine::~BaseMappedArrayEngine() { delete column_p; } // The function prepare is called upon initialization of the virtual column. // The initialization order of the columns is undetermined, which means // that this function isWritable can be called before the column has been // initialized. // For example, suppose column A uses column B and A gets initialized // before B. Then A will call B's isWritable(), while B has not been // initialized yet. // This all means that isWritable must take care of the case // where the writable_p flag is not set yet. template Bool BaseMappedArrayEngine::isWritable() const { if (tempWritable_p) { return True; } return isWritable_p && table().isColumnWritable (storedName_p); } // Create the column object for the array column in this engine. // This merely checks if the virtual column name matches. template DataManagerColumn* BaseMappedArrayEngine::makeIndArrColumn (const String& columnName, int, const String&) { //# Check if the column name matches the virtual column name. //# The virtual name is only filled in case of creating a new table. //# In case the table is read back, makeIndArrColumn is called //# before prepare, thus before the virtual name can be read back. if (virtualName_p.empty()) { virtualName_p = columnName; } else if (columnName != virtualName_p) { throw (DataManInvOper ("BaseMappedArrayEngine with virtual column " + virtualName_p + " bound to column " + columnName + "; should be the same")); } return this; } template TableColumn BaseMappedArrayEngine::makeTableColumn (const String& columnName) { tempWritable_p = True; TableColumn thisCol (table(), columnName); tempWritable_p = False; return thisCol; } template void BaseMappedArrayEngine::create64 (rownr_t initialNrrow) { //# Define the stored name as a column keyword in the virtual. makeTableColumn (virtualName_p).rwKeywordSet().define ("_BaseMappedArrayEngine_Name", storedName_p); initialNrrow_p = initialNrrow; } template void BaseMappedArrayEngine::prepare() { prepare1(); prepare2(); } template void BaseMappedArrayEngine::prepare1() { //# Get the name of the stored column from the keywords in the //# virtual column. tempWritable_p = True; TableColumn thisCol (table(), virtualName_p); storedName_p = thisCol.keywordSet().asString ("_BaseMappedArrayEngine_Name"); //# Determine if the stored column is writable. //# Allocate an object to get from the stored column. //# Allocate one to put if the column is writable. column_p = new ArrayColumn (table(), storedName_p); tempWritable_p = False; //# It is not permitted to have a FixedShape stored and non-FixedShape //# virtual column. if ((! arrayIsFixed_p) && ((column_p->columnDesc().options() & ColumnDesc::FixedShape) == ColumnDesc::FixedShape)) { throw (DataManInvOper ("BaseMappedArrayEngine: virtual column " + virtualName_p + " is FixedShape, but stored " + storedName_p + " is not")); } } template void BaseMappedArrayEngine::prepare2() { //# Add the initial number of rows (thus only done after create). //# This will set the shape of the stored arrays when needed. if (initialNrrow_p > 0) { addRowInit (0, initialNrrow_p); } } //# Add nrrow rows to the end of the table. //# Set the shape if virtual is FixedShape and stored is non-FixedShape. template void BaseMappedArrayEngine::addRow64 (rownr_t nrrow) { addRowInit (table().nrow(), nrrow); } template void BaseMappedArrayEngine::addRowInit (rownr_t startRow, rownr_t nrrow) { if (arrayIsFixed_p && ((column_p->columnDesc().options() & ColumnDesc::FixedShape) != ColumnDesc::FixedShape)) { for (rownr_t i=0; isetShape (startRow++, shapeFixed_p); } } } //# This function is called in case the virtual column has FixedShape arrays. //# If the stored has non-FixedShape arrays this shape will be set for the //# array in each row of the stored (by function addRow). template void BaseMappedArrayEngine::setShapeColumn (const IPosition& shape) { shapeFixed_p = shape; arrayIsFixed_p = True; } template void BaseMappedArrayEngine::setShape (rownr_t rownr, const IPosition& shape) { column_p->setShape (rownr, shape); } template Bool BaseMappedArrayEngine::isShapeDefined (rownr_t rownr) { return column_p->isDefined (rownr); } template uInt BaseMappedArrayEngine::ndim (rownr_t rownr) { return column_p->ndim (rownr); } template IPosition BaseMappedArrayEngine::shape (rownr_t rownr) { return column_p->shape (rownr); } template Bool BaseMappedArrayEngine::canChangeShape() const { return (column_p == 0 ? False : column_p->canChangeShape()); } template void BaseMappedArrayEngine::getArray (rownr_t rownr, Array& array) { Array target(getStoredShape(0, array.shape())); column().baseGet (rownr, target); mapOnGet (array, target); } template void BaseMappedArrayEngine::putArray (rownr_t rownr, const Array& array) { Array target(getStoredShape(0, array.shape())); mapOnPut (array, target); column().basePut (rownr, target); } template void BaseMappedArrayEngine::getSlice (rownr_t rownr, const Slicer& slicer, Array& array) { Array target(getStoredShape(rownr, array.shape())); column().getSlice (rownr, getStoredSlicer(slicer), target); mapOnGet (array, target); } template void BaseMappedArrayEngine::putSlice (rownr_t rownr, const Slicer& slicer, const Array& array) { Array target(getStoredShape(rownr, array.shape())); mapOnPut (array, target); column().putSlice (rownr, getStoredSlicer(slicer), target); } template void BaseMappedArrayEngine::getArrayColumn (Array& array) { Array target(getStoredShape(0, array.shape())); column().getColumn (target); mapOnGet (array, target); } template void BaseMappedArrayEngine::putArrayColumn (const Array& array) { Array target(getStoredShape(0, array.shape())); mapOnPut (array, target); column().putColumn (target); } template void BaseMappedArrayEngine::getArrayColumnCells (const RefRows& rownrs, Array& array) { Array target(getStoredShape(0, array.shape())); column().getColumnCells (rownrs, target); mapOnGet (array, target); } template void BaseMappedArrayEngine::putArrayColumnCells (const RefRows& rownrs, const Array& array) { Array target(getStoredShape(0, array.shape())); mapOnPut (array, target); column().putColumnCells (rownrs, target); } template void BaseMappedArrayEngine::getColumnSlice (const Slicer& slicer, Array& array) { Array target(getStoredShape(0, array.shape())); column().getColumn (getStoredSlicer(slicer), target); mapOnGet (array, target); } template void BaseMappedArrayEngine::putColumnSlice (const Slicer& slicer, const Array& array) { Array target(getStoredShape(0, array.shape())); mapOnPut (array, target); column().putColumn (getStoredSlicer(slicer), target); } template void BaseMappedArrayEngine::getColumnSliceCells (const RefRows& rownrs, const Slicer& slicer, Array& array) { Array target(getStoredShape(0, array.shape())); column().getColumnCells (rownrs, getStoredSlicer(slicer), target); mapOnGet (array, target); } template void BaseMappedArrayEngine::putColumnSliceCells (const RefRows& rownrs, const Slicer& slicer, const Array& array) { Array target(getStoredShape(0, array.shape())); mapOnPut (array, target); column().putColumnCells (rownrs, getStoredSlicer(slicer), target); } template IPosition BaseMappedArrayEngine::getStoredShape (rownr_t, const IPosition& virtualShape) { return virtualShape; } template Slicer BaseMappedArrayEngine::getStoredSlicer (const Slicer& virtualSlicer) const { return virtualSlicer; } template void BaseMappedArrayEngine::mapOnGet (Array&, const Array&) { throw DataManInvOper("BaseMappedArrayEngine::mapOnGet not implemented " "for column " + virtualName()); } template void BaseMappedArrayEngine::mapOnPut (const Array&, Array&) { throw DataManInvOper("BaseMappedArrayEngine::mapOnPut not implemented " "for column " + virtualName()); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/BitFlagsEngine.cc000066400000000000000000000060161476623553700212360ustar00rootroot00000000000000//# BitFlagsEngine.cc: Implementation of helper class. //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN BFEngineMask::BFEngineMask (uInt mask) : itsMask (mask) {} BFEngineMask::BFEngineMask (const Array& keys, uInt defaultMask) : itsMaskKeys (keys), itsMask (defaultMask) {} void BFEngineMask::fromRecord (const RecordInterface& spec, const TableColumn& column, const String& prefix) { String keyName = prefix + "Mask"; if (spec.isDefined (keyName)) { itsMask = spec.asuInt (keyName); } keyName += "Keys"; if (spec.isDefined (keyName)) { itsMaskKeys = spec.asArrayString (keyName); makeMask (column); } } void BFEngineMask::toRecord (RecordInterface& spec, const String& prefix) const { spec.define (prefix + "Mask", itsMask); spec.define (prefix + "MaskKeys", itsMaskKeys); } void BFEngineMask::makeMask (const TableColumn& column) { if (! itsMaskKeys.empty()) { if (column.keywordSet().isDefined("FLAGSETS")) { const RecordInterface& rec = column.keywordSet().asRecord("FLAGSETS"); uInt mask = 0; Array::const_iterator iterEnd = itsMaskKeys.end(); for (Array::const_iterator iter=itsMaskKeys.begin(); iter!=iterEnd; ++iter) { if (rec.isDefined(*iter)) { mask = mask | rec.asuInt (*iter); } } itsMask = mask; } } } } casacore-3.7.1/tables/DataMan/BitFlagsEngine.h000066400000000000000000000355571476623553700211140ustar00rootroot00000000000000//# BitFlagsEngine.h: Templated virtual column engine to map bit flags to a Bool //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_BITFLAGSENGINE_H #define TABLES_BITFLAGSENGINE_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Non-templated Helper class to handle the mask. // class BFEngineMask { public: // Form the mask as given. explicit BFEngineMask (uInt mask=0xffffffff); // Form the mask from the given keywords defining the bits. BFEngineMask (const Array& keys, uInt defaultMask); // Make the mask from the given keywords defining the bits. void makeMask (const TableColumn& column); // Form the read mask from the specification. // If keywords are given, the mask is formed from them. void fromRecord (const RecordInterface& spec, const TableColumn& column, const String& prefix); // Store the info in a Record. void toRecord (RecordInterface& spec, const String& prefix) const; // Get the mask. uInt getMask() const { return itsMask; } // Get the mask keywords. const Array& getKeys() const { return itsMaskKeys; } private: Array itsMaskKeys; uInt itsMask; }; // // Templated virtual column engine to map bit flags to a Bool. // // // // // //# Classes you should understand before using this one. //
      • VirtualColumnEngine //
      • VirtualArrayColumn // // // BitFlagsEngine is a virtual column engine which maps an integer column // containing flag bits to a Bool column. It can be used in a MeasurementSet // to have multiple flag categories, yet use all existing software that // deals with the Bool FLAG column. // // The engine support read as well as write access. // For both cases a mask can be defined telling which bits have to be taken // into account. For example, when writing to the Bool FLAG column, the data // in the bitflags column twill be or-ed with the bits as defined in the // writemask. Similary when reading FLAG, only the bits of the readmask are // taken into account. // // The masks can be defined in two ways: //
          //
        • The mask can be given directly as an integer value. // The default write mask is 1 (thus only bit 0), while the default // read mask is all bits. //
        • Symbolic names for mask bits can be defined as keywords in the // flagbits column. They define the bit value, not the bit number. // It makes it possible to combine bits in a keyword. // The keywords are stored in a subrecord of keyword FLAGSETS. // Example of keyword and their values could be: //
          RFI=1, CAL=2, CLIP=4, OTHER=8, RFICAL=3 //
          Note that in this example RFICAL is defined such that it // contains RFI and CAL. //
        // A mask can be set at construction time, but it can be changed at runtime // using the setProperties function. // The masks are kept in special keywords (which are different from the // keywords defining the flag bits), so it is possible to change a mask // by changing those keywords before opening a table. However, that is // not recommended. // // BitFlagsEngine is known to the table system for data types uChar, Short, // and Int. //
        // // The FLAG_CATEGORY defined the Measurement does not work because adding // an extra flag means resizing the entire array which is slow. // This class makes it possible to use an integer column to store flags // and map it directly to a Bool column. // // // // // Create the table description and 2 columns with indirect arrays in it. // // The Int column will be stored, while the Bool will be used as virtual. // TableDesc tableDesc ("", TableDesc::Scratch); // tableDesc.addColumn (ArrayColumnDesc ("BitBlags")); // tableDesc.addColumn (ArrayColumnDesc ("FLAG")); // // // Create a new table using the table description. // SetupNewTable newtab (tableDesc, "tab.data", Table::New); // // // Create the engine and bind the FLAG column to it. // BitFlagsEngine flagsEngine("FLAG", "BitFlags"); // newtab.bindColumn ("FLAG", flagsEngine); // // Create the table. // Table table (newtab); // // // Store a 3-D array (with dim. 2,3,4) into each row of the column. // // The shape of each array in the column is implicitly set by the put // // function. This will also set the shape of the underlying Int array. // ArrayColumn data (table, "virtualArray"); // Array someArray(IPosition(4,2,3,4)); // someArray = True; // for (rownr_t i=0, i<10; i++) { // table will have 10 rows // table.addRow(); // data.put (i, someArray) // } // // The underlying integer array will be stored according to the writemask // which defaults to 1. // // //
      • only suited for built-in integer data types // template class BitFlagsEngine : public BaseMappedArrayEngine { //# Make members of parent class known. public: using BaseMappedArrayEngine::virtualName; protected: using BaseMappedArrayEngine::storedName; using BaseMappedArrayEngine::table; using BaseMappedArrayEngine::column; using BaseMappedArrayEngine::setNames; public: // Construct an engine to map integer arrays in a column to Bool arrays. // StoredColumnName is the name of the column where the integer // data will be put and must have data type StoredType. // The virtual column using this engine must have data type Bool. //
        A mask can be given that specifies which bits to use in the mapping // from StoredType to Bool. Similarly a mask can be given defining which // bits to set when mapping from Bool to StoredType. BitFlagsEngine (const String& virtualColumnName, const String& storedColumnName, StoredType readMask=StoredType(0xffffffff), StoredType writeMask=1); // Construct an engine to map integer arrays in a column to Bool arrays. // StoredColumnName is the name of the column where the scaled // data will be put and must have data type StoredType. // The virtual column using this engine must have data type Bool. //
        A mask can be given that specifies which bits to use in the mapping // from StoredType to Bool. Similarly a mask can be given defining which // bits to set when mapping from Bool to StoredType. // The masks are given using the values of keywords in the stored column. // Each keyword should be an integer defining one or more bits and can be // seen as a symbolic name. The keyword values are or-ed to form the mask. // The keywords are stored in a subrecord of keyword FLAGSETS. BitFlagsEngine (const String& virtualColumnName, const String& storedColumnName, const Array& readMaskKeys, const Array& writeMaskKeys); // Construct from a record specification as created by dataManagerSpec(). BitFlagsEngine (const Record& spec); // Destructor is mandatory. ~BitFlagsEngine(); // Assignment is not needed and therefore forbidden BitFlagsEngine& operator= (const BitFlagsEngine&) = delete; // Return the type name of the engine (i.e. its class name). virtual String dataManagerType() const; // Get the name given to the engine (is the virtual column name). virtual String dataManagerName() const; // Record a record containing data manager specifications. virtual Record dataManagerSpec() const; // Get data manager properties that can be modified. // These are ReadMask, WriteMask, ReadMaskKeys, and WriteMaskKeys. // It is a subset of the data manager specification. virtual Record getProperties() const; // Modify data manager properties. // These are ReadMask, WriteMask, ReadMaskKeys, and/or WriteMaskKeys. // Mask keys should be given as an array of strings giving the keyword // names defining mask bits (similar to the constructor). Mask keys are // only used if not empty. virtual void setProperties (const Record& spec); // Return the name of the class. // This includes the names of the template arguments. static String className(); // Register the class name and the static makeObject "constructor". // This will make the engine known to the table system. // The automatically invoked registration function in DataManReg.cc // contains BitFlagsEngine. // Any other instantiation of this class must be registered "manually" // (or added to DataManReg.cc). static void registerClass(); private: // Copy constructor is only used by clone(). // (so it is made private). BitFlagsEngine (const BitFlagsEngine&); // Clone the engine object. DataManager* clone() const; // Initialize the object for a new table. // It defines the keywords containing the engine parameters. void create64 (rownr_t initialNrrow); // Preparing consists of setting the writable switch and // adding the initial number of rows in case of create. // Furthermore it reads the keywords containing the engine parameters. void prepare(); // Get an array in the given row. // This will scale and offset from the underlying array. void getArray (rownr_t rownr, Array& array); // Put an array in the given row. // This will scale and offset to the underlying array. void putArray (rownr_t rownr, const Array& array); // Get a section of the array in the given row. // This will scale and offset from the underlying array. void getSlice (rownr_t rownr, const Slicer& slicer, Array& array); // Put into a section of the array in the given row. // This will scale and offset to the underlying array. void putSlice (rownr_t rownr, const Slicer& slicer, const Array& array); // Get an entire column. // This will scale and offset from the underlying array. void getArrayColumn (Array& array); // Put an entire column. // This will scale and offset to the underlying array. void putArrayColumn (const Array& array); // Get some array values in the column. // This will scale and offset from the underlying array. virtual void getArrayColumnCells (const RefRows& rownrs, Array& data); // Put some array values in the column. // This will scale and offset to the underlying array. virtual void putArrayColumnCells (const RefRows& rownrs, const Array& data); // Get a section of all arrays in the column. // This will scale and offset from the underlying array. void getColumnSlice (const Slicer& slicer, Array& array); // Put a section of all arrays in the column. // This will scale and offset to the underlying array. void putColumnSlice (const Slicer& slicer, const Array& array); // Get a section of some arrays in the column. // This will scale and offset from the underlying array. virtual void getColumnSliceCells (const RefRows& rownrs, const Slicer& slicer, Array& data); // Put into a section of some arrays in the column. // This will scale and offset to the underlying array. virtual void putColumnSliceCells (const RefRows& rownrs, const Slicer& slicer, const Array& data); // Map bit flags array to Bool array. // This is meant when reading an array from the stored column. void mapOnGet (Array& array, const Array& stored); // Map Bool array to bit flags array. // This is meant when writing an array into the stored column. void mapOnPut (const Array& array, Array& stored); // Functor to and an array and mask and convert to Bool. struct FlagsToBool { explicit FlagsToBool(StoredType readMask) : itsMask(readMask) {} Bool operator() (StoredType value) const { return (value & itsMask) != 0; } private: StoredType itsMask; }; // Functor to convert Bools to flags using a mask. // By default only bit 0 is set. // Flag bits not affected are kept. struct BoolToFlags { explicit BoolToFlags(StoredType writeMask) : itsMask(writeMask) {} StoredType operator() (Bool flag, StoredType value) const { return (flag ? value&itsMask : value); } private: StoredType itsMask; }; public: // Define the "constructor" to construct this engine when a // table is read back. // This "constructor" has to be registered by the user of the engine. // If the engine is commonly used, its registration can be added // to the registerAllCtor function in DataManReg.cc. // That function gets automatically invoked by the table system. static DataManager* makeObject (const String& dataManagerType, const Record& spec); private: BFEngineMask itsBFEReadMask; BFEngineMask itsBFEWriteMask; StoredType itsReadMask; StoredType itsWriteMask; Bool itsIsNew; //# True = new table }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/tables/DataMan/BitFlagsEngine.tcc000066400000000000000000000242721476623553700214260ustar00rootroot00000000000000//# BitFlagsEngine.tcc: Templated virtual column engine to map bit flags to a Bool //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_BITFLAGSENGINE_TCC #define TABLES_BITFLAGSENGINE_TCC //# Includes #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template BitFlagsEngine::BitFlagsEngine (const String& virtualColumnName, const String& storedColumnName, T readMask, T writeMask) : BaseMappedArrayEngine (virtualColumnName, storedColumnName), itsBFEReadMask (readMask), itsBFEWriteMask (writeMask), itsReadMask (readMask), itsWriteMask (writeMask), itsIsNew (False) {} template BitFlagsEngine::BitFlagsEngine (const String& virtualColumnName, const String& storedColumnName, const Array& readMaskKeys, const Array& writeMaskKeys) : BaseMappedArrayEngine (virtualColumnName, storedColumnName), itsBFEReadMask (readMaskKeys, 0xffffffff), itsBFEWriteMask (writeMaskKeys, 1), itsReadMask (0xffffffff), itsWriteMask (1), itsIsNew (False) {} template BitFlagsEngine::BitFlagsEngine (const Record& spec) : BaseMappedArrayEngine(), itsIsNew (False) { if (spec.isDefined("SOURCENAME") && spec.isDefined("TARGETNAME")) { setNames (spec.asString("SOURCENAME"), spec.asString("TARGETNAME")); setProperties (spec); } } template BitFlagsEngine::BitFlagsEngine (const BitFlagsEngine& that) : BaseMappedArrayEngine (that), itsBFEReadMask (that.itsBFEReadMask), itsBFEWriteMask (that.itsBFEWriteMask), itsReadMask (that.itsReadMask), itsWriteMask (that.itsWriteMask), itsIsNew (that.itsIsNew) {} template BitFlagsEngine::~BitFlagsEngine() {} //# Clone the engine object. template DataManager* BitFlagsEngine::clone() const { DataManager* dmPtr = new BitFlagsEngine (*this); return dmPtr; } //# Return the type name of the engine (i.e. its class name). template String BitFlagsEngine::dataManagerType() const { return className(); } //# Return the class name. //# Get the data type names using class ValType. template String BitFlagsEngine::className() { return "BitFlagsEngine<" + valDataTypeId (static_cast(0)); } template String BitFlagsEngine::dataManagerName() const { return virtualName(); } template Record BitFlagsEngine::dataManagerSpec() const { Record spec = getProperties(); spec.define ("SOURCENAME", virtualName()); spec.define ("TARGETNAME", storedName()); return spec; } template Record BitFlagsEngine::getProperties() const { Record spec; itsBFEReadMask.toRecord (spec, "Read"); itsBFEWriteMask.toRecord (spec, "Write"); return spec; } template void BitFlagsEngine::setProperties (const Record& spec) { itsBFEReadMask.fromRecord (spec, column(), "Read"); itsBFEWriteMask.fromRecord (spec, column(), "Write"); itsReadMask = T(itsBFEReadMask.getMask()); itsWriteMask = T(itsBFEWriteMask.getMask()); } template DataManager* BitFlagsEngine::makeObject (const String&, const Record& spec) { DataManager* dmPtr = new BitFlagsEngine(spec); return dmPtr; } template void BitFlagsEngine::registerClass() { DataManager::registerCtor (className(), makeObject); } template void BitFlagsEngine::create64 (rownr_t initialNrrow) { BaseMappedArrayEngine::create64 (initialNrrow); itsIsNew = True; } template void BitFlagsEngine::prepare() { BaseMappedArrayEngine::prepare(); // If a new table, derive the mask here. // This cannot be done in create, because the other column may not // be created yet. if (itsIsNew) { itsBFEReadMask.makeMask (column()); itsBFEWriteMask.makeMask(column()); // Store the various parameters as keywords in this column. TableColumn thisCol (table(), virtualName()); itsBFEReadMask.toRecord (thisCol.rwKeywordSet(), "_BitFlagsEngine_Read"); itsBFEWriteMask.toRecord(thisCol.rwKeywordSet(), "_BitFlagsEngine_Write"); } else { // Existing table, get masks from the keywords. TableColumn thisCol (table(), virtualName()); itsBFEReadMask.fromRecord (thisCol.keywordSet(), column(), "_BitFlagsEngine_Read"); itsBFEWriteMask.fromRecord(thisCol.keywordSet(), column(), "_BitFlagsEngine_Write"); } itsReadMask = T(itsBFEReadMask.getMask()); itsWriteMask = T(itsBFEWriteMask.getMask()); } template void BitFlagsEngine::getArray (rownr_t rownr, Array& array) { Array target(array.shape()); column().get (rownr, target); mapOnGet (array, target); } template void BitFlagsEngine::putArray (rownr_t rownr, const Array& array) { Array target(array.shape()); mapOnPut (array, target); column().put (rownr, target); } template void BitFlagsEngine::getSlice (rownr_t rownr, const Slicer& slicer, Array& array) { Array target(array.shape()); column().getSlice (rownr, slicer, target); mapOnGet (array, target); } template void BitFlagsEngine::putSlice (rownr_t rownr, const Slicer& slicer, const Array& array) { Array target(array.shape()); mapOnPut (array, target); column().putSlice (rownr, slicer, target); } template void BitFlagsEngine::getArrayColumn (Array& array) { Array target(array.shape()); column().getColumn (target); mapOnGet (array, target); } template void BitFlagsEngine::putArrayColumn (const Array& array) { Array target(array.shape()); mapOnPut (array, target); column().putColumn (target); } template void BitFlagsEngine::getArrayColumnCells (const RefRows& rownrs, Array& array) { Array target(array.shape()); column().getColumnCells (rownrs, target); mapOnGet (array, target); } template void BitFlagsEngine::putArrayColumnCells (const RefRows& rownrs, const Array& array) { Array target(array.shape()); mapOnPut (array, target); column().putColumnCells (rownrs, target); } template void BitFlagsEngine::getColumnSlice (const Slicer& slicer, Array& array) { Array target(array.shape()); column().getColumn (slicer, target); mapOnGet (array, target); } template void BitFlagsEngine::putColumnSlice (const Slicer& slicer, const Array& array) { Array target(array.shape()); mapOnPut (array, target); column().putColumn (slicer, target); } template void BitFlagsEngine::getColumnSliceCells (const RefRows& rownrs, const Slicer& slicer, Array& array) { Array target(array.shape()); column().getColumnCells (rownrs, slicer, target); mapOnGet (array, target); } template void BitFlagsEngine::putColumnSliceCells (const RefRows& rownrs, const Slicer& slicer, const Array& array) { Array target(array.shape()); mapOnPut (array, target); column().putColumnCells (rownrs, slicer, target); } template void BitFlagsEngine::mapOnGet (Array& array, const Array& stored) { arrayTransform (stored, array, FlagsToBool(itsReadMask)); } template void BitFlagsEngine::mapOnPut (const Array& array, Array& stored) { arrayTransformInPlace (stored, array, BoolToFlags(itsWriteMask)); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/CompressComplex.cc000066400000000000000000000575331476623553700215520ustar00rootroot00000000000000//# CompressComplex.cc: Virtual column engine to scale a table Complex array //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN CompressComplex::CompressComplex (const String& virtualColumnName, const String& storedColumnName, Float scale, Float offset) : BaseMappedArrayEngine (virtualColumnName, storedColumnName), scale_p (scale), offset_p (offset), fixed_p (True), autoScale_p (False), scaleColumn_p (0), offsetColumn_p (0) {} CompressComplex::CompressComplex (const String& virtualColumnName, const String& storedColumnName, const String& scaleColumnName, const String& offsetColumnName, Bool autoScale) : BaseMappedArrayEngine (virtualColumnName, storedColumnName), scaleName_p (scaleColumnName), offsetName_p (offsetColumnName), scale_p (0.0), offset_p (0.0), fixed_p (False), autoScale_p (autoScale), scaleColumn_p (0), offsetColumn_p (0) {} CompressComplex::CompressComplex (const Record& spec) : BaseMappedArrayEngine (), scale_p (1.0), offset_p (0.0), fixed_p (True), autoScale_p (False), scaleColumn_p (0), offsetColumn_p (0) { if (spec.isDefined("SOURCENAME") && spec.isDefined("TARGETNAME")) { setNames (spec.asString("SOURCENAME"), spec.asString("TARGETNAME")); if (spec.isDefined("SCALE") && spec.isDefined("OFFSET")) { spec.get ("SCALE", scale_p); spec.get ("OFFSET", offset_p); } else { spec.get ("SCALENAME", scaleName_p); spec.get ("OFFSETNAME", offsetName_p); fixed_p = False; } if (spec.isDefined("AUTOSCALE")) { spec.get ("AUTOSCALE", autoScale_p); } } } CompressComplex::CompressComplex (const CompressComplex& that) : BaseMappedArrayEngine (that), scaleName_p (that.scaleName_p), offsetName_p (that.offsetName_p), scale_p (that.scale_p), offset_p (that.offset_p), fixed_p (that.fixed_p), autoScale_p (that.autoScale_p), scaleColumn_p (0), offsetColumn_p (0) {} CompressComplex::~CompressComplex() { delete scaleColumn_p; delete offsetColumn_p; } //# Clone the engine object. DataManager* CompressComplex::clone() const { return new CompressComplex (*this); } //# Return the type name of the engine (i.e. its class name). String CompressComplex::dataManagerType() const { return className(); } //# Return the class name. //# Get the data type names using class ValType. String CompressComplex::className() { return "CompressComplex"; } String CompressComplex::dataManagerName() const { return virtualName(); } Record CompressComplex::dataManagerSpec() const { Record spec; spec.define ("SOURCENAME", virtualName()); spec.define ("TARGETNAME", storedName()); if (fixed_p) { spec.define ("SCALE", scale_p); spec.define ("OFFSET", offset_p); } else { spec.define ("SCALENAME", scaleName_p); spec.define ("OFFSETNAME", offsetName_p); } spec.define ("AUTOSCALE", autoScale_p); return spec; } DataManager* CompressComplex::makeObject (const String&, const Record& spec) { return new CompressComplex(spec); } void CompressComplex::registerClass() { DataManager::registerCtor (className(), makeObject); } void CompressComplex::create64 (rownr_t initialNrrow) { BaseMappedArrayEngine::create64 (initialNrrow); // Store the various parameters as keywords in this column. TableColumn thisCol (table(), virtualName()); thisCol.rwKeywordSet().define ("_CompressComplex_Scale", scale_p); thisCol.rwKeywordSet().define ("_CompressComplex_Offset", offset_p); thisCol.rwKeywordSet().define ("_CompressComplex_ScaleName", scaleName_p); thisCol.rwKeywordSet().define ("_CompressComplex_OffsetName", offsetName_p); thisCol.rwKeywordSet().define ("_CompressComplex_Fixed", fixed_p); thisCol.rwKeywordSet().define ("_CompressComplex_AutoScale", autoScale_p); thisCol.rwKeywordSet().define ("_CompressComplex_Type", "CompressComplex"); } void CompressComplex::prepare() { BaseMappedArrayEngine::prepare1(); TableColumn thisCol (table(), virtualName()); thisCol.keywordSet().get ("_CompressComplex_Scale", scale_p); thisCol.keywordSet().get ("_CompressComplex_Offset", offset_p); thisCol.keywordSet().get ("_CompressComplex_ScaleName", scaleName_p); thisCol.keywordSet().get ("_CompressComplex_OffsetName", offsetName_p); thisCol.keywordSet().get ("_CompressComplex_Fixed", fixed_p); thisCol.keywordSet().get ("_CompressComplex_AutoScale", autoScale_p); //# Allocate column objects to get scale and offset. if (! fixed_p) { scaleColumn_p = new ScalarColumn (table(), scaleName_p); offsetColumn_p = new ScalarColumn (table(), offsetName_p); } // Do this at the end, because it might call addRow. BaseMappedArrayEngine::prepare2(); } void CompressComplex::reopenRW() { } void CompressComplex::addRowInit (rownr_t startRow, rownr_t nrrow) { BaseMappedArrayEngine::addRowInit (startRow, nrrow); if (autoScale_p) { for (rownr_t i=0; iput (startRow++, 0.); } } } // Find minimum and maximum. void CompressComplex::findMinMax (Float& minVal, Float& maxVal, const Array& array) const { setNaN (minVal); setNaN (maxVal); Bool deleteIt; const Complex* data = array.getStorage (deleteIt); const Int64 nr = array.nelements(); Bool firstTime = True; for (Int64 i=0; i maxVal) { maxVal = tmp; } tmp = data[i].imag(); if (tmp < minVal) { minVal = tmp; } else if (tmp > maxVal) { maxVal = tmp; } } } array.freeStorage (data, deleteIt); } // Find minimum and maximum. void CompressComplex::makeScaleOffset (Float& scale, Float& offset, Float minVal, Float maxVal) const { if (isNaN (minVal)) { scale = 0; offset = 0; } else { if (minVal == maxVal) { scale = 1; } else { scale = (maxVal - minVal) / 65534; } offset = (maxVal + minVal) / 2; } } // Scale/offset an array for get. void CompressComplex::scaleOnGet (Float scale, Float offset, Array& array, const Array& target) { Bool deleteIn, deleteOut; Complex* out = array.getStorage (deleteOut); const Int* in = target.getStorage (deleteIn); const Int64 nr = array.nelements(); for (Int64 i=0; i= 32768) { r += 1; im -= 65536; } out[i] = Complex (r * scale + offset, im * scale + offset); } } target.freeStorage (in, deleteIn); array.putStorage (out, deleteOut); } // Scale/offset an array for put. void CompressComplex::scaleOnPut (Float scale, Float offset, const Array& array, Array& target) { Bool deleteIn, deleteOut; const Complex* in = array.getStorage (deleteIn); Int* out = target.getStorage (deleteOut); const Int64 nr = array.nelements(); for (Int64 i=0; i 32767) { s = 32767; } else { s = short(f); } } Int r = int(s) * 65536; tmp = (in[i].imag() - offset) / scale; if (tmp < 0) { float f = ceil(tmp - 0.5); if (f < -32767) { s = -32767; } else { s = short(f); } } else { float f = floor(tmp + 0.5); if (f > 32767) { s = 32767; } else { s = short(f); } } out[i] = r + s; } } array.freeStorage (in, deleteIn); target.putStorage (out, deleteOut); } void CompressComplex::scaleColumnOnGet (Array& array, const Array& target) { if (fixed_p) { scaleOnGet (scale_p, offset_p, array, target); }else{ ArrayIterator arrayIter (array, array.ndim() - 1); ReadOnlyArrayIterator targetIter (target, target.ndim() - 1); rownr_t rownr = 0; while (! arrayIter.pastEnd()) { scaleOnGet (getScale(rownr), getOffset(rownr), arrayIter.array(), targetIter.array()); rownr++; arrayIter.next(); targetIter.next(); } } } void CompressComplex::scaleColumnOnPut (const Array& array, Array& target) { if (fixed_p) { scaleOnPut (scale_p, offset_p, array, target); }else{ ReadOnlyArrayIterator arrayIter (array, array.ndim() - 1); ArrayIterator targetIter (target, target.ndim() - 1); rownr_t rownr = 0; while (! arrayIter.pastEnd()) { scaleOnPut (getScale(rownr), getOffset(rownr), arrayIter.array(), targetIter.array()); rownr++; arrayIter.next(); targetIter.next(); } } } void CompressComplex::getArray (rownr_t rownr, Array& array) { if (! array.shape().isEqual (buffer_p.shape())) { buffer_p.resize (array.shape()); } column().baseGet (rownr, buffer_p); scaleOnGet (getScale(rownr), getOffset(rownr), array, buffer_p); } void CompressComplex::putArray (rownr_t rownr, const Array& array) { if (! array.shape().isEqual (buffer_p.shape())) { buffer_p.resize (array.shape()); } if (! autoScale_p) { scaleOnPut (getScale(rownr), getOffset(rownr), array, buffer_p); } else { Float minVal, maxVal; findMinMax (minVal, maxVal, array); Float scale, offset; makeScaleOffset (scale, offset, minVal, maxVal); scaleColumn_p->put (rownr, scale); offsetColumn_p->put (rownr, offset); scaleOnPut (scale, offset, array, buffer_p); } column().basePut (rownr, buffer_p); } void CompressComplex::getSlice (rownr_t rownr, const Slicer& slicer, Array& array) { if (! array.shape().isEqual (buffer_p.shape())) { buffer_p.resize (array.shape()); } column().getSlice (rownr, slicer, buffer_p); scaleOnGet (getScale(rownr), getOffset(rownr), array, buffer_p); } void CompressComplex::putPart (rownr_t rownr, const Slicer& slicer, const Array& array, Float scale, Float offset) { if (! array.shape().isEqual (buffer_p.shape())) { buffer_p.resize (array.shape()); } scaleOnPut (scale, offset, array, buffer_p); column().putSlice (rownr, slicer, buffer_p); } void CompressComplex::putFullPart (rownr_t rownr, const Slicer& slicer, Array& fullArray, const Array& partArray, Float minVal, Float maxVal) { Array subarr = fullArray(slicer.start(), slicer.end(), slicer.stride()); subarr = partArray; Float scale, offset; makeScaleOffset (scale, offset, minVal, maxVal); scaleColumn_p->put (rownr, scale); offsetColumn_p->put (rownr, offset); if (! fullArray.shape().isEqual (buffer_p.shape())) { buffer_p.resize (fullArray.shape()); } scaleOnPut (scale, offset, fullArray, buffer_p); column().basePut (rownr, buffer_p); } void CompressComplex::putSlice (rownr_t rownr, const Slicer& slicer, const Array& array) { // If the slice is the entire array, write it as such. IPosition shp = shape(rownr); if (shp.isEqual (array.shape())) { CompressComplex::putArray (rownr, array); } else { // Get current scale and offset. // If no autoscaling, write the part immediately. Float scale = getScale(rownr); Float offset = getOffset(rownr); if (! autoScale_p) { putPart (rownr, slicer, array, scale, offset); } else { // Determine min/max of new slice. // scale==0 means that no array data was written yet. // In that case initialize array to NaN if the slice has valid data. Float minValArr, maxValArr; findMinMax (minValArr, maxValArr, array); if (scale == 0) { if (! isNaN(minValArr)) { Array arr(shp); Complex val; setNaN (val); arr = val; putFullPart (rownr, slicer, arr, array, minValArr, maxValArr); } } else { // Valid data in row. // Writing the part will do if no valid data in it or if // its min/max is within the current min/max. // Otherwise we have to rescale using new min/max. Float maxValRow = offset + scale*65534/2; Float minValRow = offset - scale*65534/2; if (isNaN(minValArr) || (minValArr >= minValRow && maxValArr <= maxValRow)) { putPart (rownr, slicer, array, scale, offset); } else { Array arr(shp); CompressComplex::getArray (rownr, arr); putFullPart (rownr, slicer, arr, array, min(minValRow, minValArr), max(maxValRow, maxValArr)); } } } } } void CompressComplex::getArrayColumn (Array& array) { Array target(array.shape()); column().getColumn (target); scaleColumnOnGet (array, target); } void CompressComplex::putArrayColumn (const Array& array) { Array target(array.shape()); if (! autoScale_p) { scaleColumnOnPut (array, target); column().putColumn (target); } else { ReadOnlyArrayIterator iter(array, array.ndim()-1); rownr_t nrrow = table().nrow(); for (rownr_t rownr=0; rownr& array) { ArrayIterator arrIter(array, array.ndim()-1); RefRowsSliceIter rowsIter(rownrs); while (! rowsIter.pastEnd()) { rownr_t rownr = rowsIter.sliceStart(); rownr_t end = rowsIter.sliceEnd(); rownr_t incr = rowsIter.sliceIncr(); while (rownr <= end) { CompressComplex::getArray (rownr, arrIter.array()); arrIter.next(); rownr += incr; } rowsIter++; } } void CompressComplex::putArrayColumnCells (const RefRows& rownrs, const Array& array) { ReadOnlyArrayIterator arrIter(array, array.ndim()-1); RefRowsSliceIter rowsIter(rownrs); while (! rowsIter.pastEnd()) { rownr_t rownr = rowsIter.sliceStart(); rownr_t end = rowsIter.sliceEnd(); rownr_t incr = rowsIter.sliceIncr(); while (rownr <= end) { CompressComplex::putArray (rownr, arrIter.array()); arrIter.next(); rownr += incr; } rowsIter++; } } void CompressComplex::getColumnSlice (const Slicer& slicer, Array& array) { Array target(array.shape()); column().getColumn (slicer, target); scaleColumnOnGet (array, target); } void CompressComplex::putColumnSlice (const Slicer& slicer, const Array& array) { Array target(array.shape()); if (! autoScale_p) { scaleColumnOnPut (array, target); column().putColumn (slicer, target); } else { ReadOnlyArrayIterator iter(array, array.ndim()-1); rownr_t nrrow = table().nrow(); for (rownr_t rownr=0; rownr& array) { ArrayIterator arrIter(array, array.ndim()-1); RefRowsSliceIter rowsIter(rownrs); while (! rowsIter.pastEnd()) { rownr_t rownr = rowsIter.sliceStart(); rownr_t end = rowsIter.sliceEnd(); rownr_t incr = rowsIter.sliceIncr(); while (rownr <= end) { CompressComplex::getSlice (rownr, slicer, arrIter.array()); arrIter.next(); rownr += incr; } rowsIter++; } } void CompressComplex::putColumnSliceCells (const RefRows& rownrs, const Slicer& slicer, const Array& array) { ReadOnlyArrayIterator arrIter(array, array.ndim()-1); RefRowsSliceIter rowsIter(rownrs); while (! rowsIter.pastEnd()) { rownr_t rownr = rowsIter.sliceStart(); rownr_t end = rowsIter.sliceEnd(); rownr_t incr = rowsIter.sliceIncr(); while (rownr <= end) { CompressComplex::putSlice (rownr, slicer, arrIter.array()); arrIter.next(); rownr += incr; } rowsIter++; } } CompressComplexSD::CompressComplexSD (const String& virtualColumnName, const String& storedColumnName, Float scale, Float offset) : CompressComplex (virtualColumnName, storedColumnName, scale, offset) {} CompressComplexSD::CompressComplexSD (const String& virtualColumnName, const String& storedColumnName, const String& scaleColumnName, const String& offsetColumnName, Bool autoScale) : CompressComplex (virtualColumnName, storedColumnName, scaleColumnName, offsetColumnName, autoScale) {} CompressComplexSD::CompressComplexSD (const Record& spec) : CompressComplex (spec) { if (spec.isDefined("SOURCENAME") && spec.isDefined("TARGETNAME")) { setNames (spec.asString("SOURCENAME"), spec.asString("TARGETNAME")); if (spec.isDefined("SCALE") && spec.isDefined("OFFSET")) { spec.get ("SCALE", scale_p); spec.get ("OFFSET", offset_p); } else { spec.get ("SCALENAME", scaleName_p); spec.get ("OFFSETNAME", offsetName_p); fixed_p = False; } if (spec.isDefined("AUTOSCALE")) { spec.get ("AUTOSCALE", autoScale_p); } } } CompressComplexSD::CompressComplexSD (const CompressComplexSD& that) : CompressComplex (that) {} CompressComplexSD::~CompressComplexSD() {} //# Clone the engine object. DataManager* CompressComplexSD::clone() const { return new CompressComplexSD (*this); } //# Return the type name of the engine (i.e. its class name). String CompressComplexSD::dataManagerType() const { return className(); } //# Return the class name. //# Get the data type names using class ValType. String CompressComplexSD::className() { return "CompressComplexSD"; } DataManager* CompressComplexSD::makeObject (const String&, const Record& spec) { return new CompressComplexSD(spec); } void CompressComplexSD::registerClass() { DataManager::registerCtor (className(), makeObject); } void CompressComplexSD::create64 (rownr_t initialNrrow) { CompressComplex::create64 (initialNrrow); // Set the type. TableColumn thisCol (table(), virtualName()); thisCol.rwKeywordSet().define ("_CompressComplex_Type", "CompressComplexSD"); } // Find minimum and maximum. void CompressComplexSD::findMinMax (Float& minVal, Float& maxVal, const Array& array) const { setNaN (minVal); setNaN (maxVal); Bool deleteIt; const Complex* data = array.getStorage (deleteIt); const Int64 nr = array.nelements(); Bool firstTime = True; for (Int64 i=0; i maxVal) { maxVal = tmp; } tmp = data[i].imag(); if (tmp != 0) { if (tmp < minVal) { minVal = tmp; } else if (tmp > maxVal) { maxVal = tmp; } } } } array.freeStorage (data, deleteIt); } // Scale/offset an array for get. void CompressComplexSD::scaleOnGet (Float scale, Float offset, Array& array, const Array& target) { Float fullScale = scale/32768; Float imagScale = scale*2; Bool deleteIn, deleteOut; Complex* out = array.getStorage (deleteOut); const Int* in = target.getStorage (deleteIn); const Int64 nr = array.nelements(); for (Int64 i=0; i>= 1; out[i] = Complex (inval*fullScale + offset, 0); } else { Int r = inval / 65536; if (r == -32768) { setNaN (out[i]); } else { Int im = inval - r*65536; if (im < -32768) { r -= 1; im += 65536; } else if (im >= 32768) { r += 1; im -= 65536; } im >>= 1; out[i] = Complex (r * scale + offset, im * imagScale + offset); } } } target.freeStorage (in, deleteIn); array.putStorage (out, deleteOut); } // Scale/offset an array for put. void CompressComplexSD::scaleOnPut (Float scale, Float offset, const Array& array, Array& target) { Float fullScale = scale/32768; Float imagScale = scale*2; Bool deleteIn, deleteOut; const Complex* in = array.getStorage (deleteIn); Int* out = target.getStorage (deleteOut); const Int64 nr = array.nelements(); for (Int64 i=0; i(std::max(f, -32768.*32768)); } else { double f = floor(tmp + 0.5); s = static_cast(std::min(f, 32768.*32768-1)); } // Shift 1 bit to left and make last bit 0 indicating that imag==0. out[i] = s<<1; } else { // There is an imaginary part, so scale both parts. Short s; Float tmp = (in[i].real() - offset) / scale; if (tmp < 0) { float f = ceil(tmp - 0.5); if (f < -32767) { s = -32767; } else { s = short(f); } } else { float f = floor(tmp + 0.5); if (f > 32767) { s = 32767; } else { s = short(f); } } Int r = int(s) * 65536; // Scale imaginary with 1 bit less. tmp = (in[i].imag() - offset) / imagScale; if (tmp < 0) { float f = ceil(tmp - 0.5); if (f < -16384) { s = -16384; } else { s = short(f); } } else { float f = floor(tmp + 0.5); if (f > 16383) { s = 16383; } else { s = short(f); } } // Shift 1 bit to left; last bit is 1 indicating that imag!=0. s <<= 1; out[i] = r + s + 1; } } array.freeStorage (in, deleteIn); target.putStorage (out, deleteOut); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/CompressComplex.h000066400000000000000000000514061476623553700214050ustar00rootroot00000000000000//# CompressComplex.h: Virtual column engine to scale a table Complex array //# Copyright (C) 2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_COMPRESSCOMPLEX_H #define TABLES_COMPRESSCOMPLEX_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Virtual column engine to scale a table Complex array // // // // // //# Classes you should understand before using this one. //
      • VirtualColumnEngine //
      • VirtualArrayColumn // // // CompressComplex is a virtual column engine which scales an array // of one type to another type to save disk storage. // This resembles the classic AIPS compress method which scales the // data from Complex to int. // The scale factor and offset values can be given in two ways: //
          //
        • As a fixed values which is used for all arrays in the column. // These values have to be given when constructing of the engine. //
        • As the name of a column. In this way each array in the // column has its own scale and offset value. // By default it uses auto-scaling (see below). // Otherwise the scale and offset value in a row must be put // before the array is put and should not be changed anymore. //
        // Auto-scaling means that the engine will determine the scale // and offset value itself when an array (or a slice) is put. // It does it by mapping the values in the array to the range [-32767,32767]. // At each put the scale/offset values are changed as needed. // Note that with auto-scaling putSlice can be somewhat // slower, because the entire array might need to be rescaled. // // As in FITS the scale and offset values are used as: //
        True_value = Stored_value * scale + offset; // // An engine object should be used for one column only, because the stored // column name is part of the engine. If it would be used for more than // one column, they would all share the same stored column. // When the engine is bound to a column, it is checked if the name // of that column matches the given virtual column name. // // The engine can be used for a column containing any kind of array // (thus direct or indirect, fixed or variable shaped)) as long as the // virtual array can be stored in the stored array. Thus a fixed shaped // virtual can use a variable shaped stored, but not vice versa. // A fixed shape indirect virtual can use a stored with direct arrays. // // This class can also serve as an example of how to implement // a virtual column engine. //
        // // This class allows to store data in a smaller representation. // It is needed to resemble the classic AIPS compress option. // // Because the engine can serve only one column, it was possible to // combine the engine and the column functionality in one class. // // // // // Create the table description and 2 columns with indirect arrays in it. // // The Int column will be stored, while the double will be // // used as virtual. // TableDesc tableDesc ("", TableDesc::Scratch); // tableDesc.addColumn (ArrayColumnDesc ("storedArray")); // tableDesc.addColumn (ArrayColumnDesc ("virtualArray")); // tableDesc.addColumn (ScalarColumnDesc ("scale")); // tableDesc.addColumn (ScalarColumnDesc ("offset")); // // // Create a new table using the table description. // SetupNewTable newtab (tableDesc, "tab.data", Table::New); // // // Create the array scaling engine (with auto-scale) // // and bind it to the Complex column. // CompressComplex scalingEngine("virtualArray", "storedArray", // "scale", "offset"); // newtab.bindColumn ("virtualArray", scalingEngine); // // Create the table. // Table table (newtab); // // // Store a 3-D array (with dim. 2,3,4) into each row of the column. // // The shape of each array in the column is implicitly set by the put // // function. This will also set the shape of the underlying Int array. // ArrayColumn data (table, "virtualArray"); // Array someArray(IPosition(4,2,3,4)); // someArray = 0; // for (rownr_t i=0, i<10; i++) { // table will have 10 rows // table.addRow(); // data.put (i, someArray) // } // // class CompressComplex : public BaseMappedArrayEngine { public: // Construct an engine to scale all arrays in a column with // the given offset and scale factor. // StoredColumnName is the name of the column where the scaled // data will be put and must have data type Int. // The virtual column using this engine must have data type Complex. CompressComplex (const String& virtualColumnName, const String& storedColumnName, Float scale, Float offset = 0); // Construct an engine to scale the arrays in a column. // The scale and offset values are taken from a column with // the given names. In that way each array has its own scale factor // and offset value. // An exception is thrown if these columns do not exist. // VirtualColumnName is the name of the virtual column and is used to // check if the engine gets bound to the correct column. // StoredColumnName is the name of the column where the scaled // data will be put and must have data type Int. // The virtual column using this engine must have data type Complex. CompressComplex (const String& virtualColumnName, const String& storedColumnName, const String& scaleColumnName, const String& offsetColumnName, Bool autoScale = True); // Construct from a record specification as created by getmanagerSpec(). CompressComplex (const Record& spec); // Destructor is mandatory. ~CompressComplex(); // Assignment is not needed and therefore forbidden CompressComplex& operator= (const CompressComplex&) = delete; // Return the type name of the engine (i.e. its class name). virtual String dataManagerType() const; // Get the name given to the engine (is the virtual column name). virtual String dataManagerName() const; // Record a record containing data manager specifications. virtual Record dataManagerSpec() const; // Return the name of the class. // This includes the names of the template arguments. static String className(); // Register the class name and the static makeObject "constructor". // This will make the engine known to the table system. static void registerClass(); protected: // Copy constructor is only used by clone() and derived class. // (so it is made protected). CompressComplex (const CompressComplex&); private: // Clone the engine object. virtual DataManager* clone() const; protected: // Initialize the object for a new table. // It defines the keywords containing the engine parameters. virtual void create64 (rownr_t initialNrrow); private: // Preparing consists of setting the writable switch and // adding the initial number of rows in case of create. // Furthermore it reads the keywords containing the engine parameters. virtual void prepare(); // Reopen the engine for read/write access. // It makes the column writable if the underlying column is writable. virtual void reopenRW(); // Add rows to the table. // If auto-scaling, it initializes the scale column with 0 // to indicate that no data has been processed yet. virtual void addRowInit (rownr_t startRow, rownr_t nrrow); // Get an array in the given row. // This will scale and offset from the underlying array. virtual void getArray (rownr_t rownr, Array& array); // Put an array in the given row. // This will scale and offset to the underlying array. virtual void putArray (rownr_t rownr, const Array& array); // Get a section of the array in the given row. // This will scale and offset from the underlying array. virtual void getSlice (rownr_t rownr, const Slicer& slicer, Array& array); // Put into a section of the array in the given row. // This will scale and offset to the underlying array. virtual void putSlice (rownr_t rownr, const Slicer& slicer, const Array& array); // Get an entire column. // This will scale and offset from the underlying array. virtual void getArrayColumn (Array& array); // Put an entire column. // This will scale and offset to the underlying array. virtual void putArrayColumn (const Array& array); // Get some array values in the column. // This will scale and offset from the underlying array. virtual void getArrayColumnCells (const RefRows& rownrs, Array& data); // Put some array values in the column. // This will scale and offset to the underlying array. virtual void putArrayColumnCells (const RefRows& rownrs, const Array& data); // Get a section of all arrays in the column. // This will scale and offset from the underlying array. virtual void getColumnSlice (const Slicer& slicer, Array& array); // Put a section of all arrays in the column. // This will scale and offset to the underlying array. virtual void putColumnSlice (const Slicer& slicer, const Array& array); // Get a section of some arrays in the column. // This will scale and offset from the underlying array. virtual void getColumnSliceCells (const RefRows& rownrs, const Slicer& slicer, Array& data); // Put into a section of some arrays in the column. // This will scale and offset to the underlying array. virtual void putColumnSliceCells (const RefRows& rownrs, const Slicer& slicer, const Array& data); // Scale and/or offset target to array. // This is meant when reading an array from the stored column. // It optimizes for scale=1 and/or offset=0. virtual void scaleOnGet (Float scale, Float offset, Array& array, const Array& target); // Scale and/or offset array to target. // This is meant when writing an array into the stored column. // It optimizes for scale=1 and/or offset=0. virtual void scaleOnPut (Float scale, Float offset, const Array& array, Array& target); // Scale and/or offset target to array for the entire column. // When the scale and offset are fixed, it will do the entire array. // Otherwise it iterates through the array and applies the scale // and offset per row. void scaleColumnOnGet (Array& array, const Array& target); // Scale and/or offset array to target for the entire column. // When the scale and offset are fixed, it will do the entire array. // Otherwise it iterates through the array and applies the scale // and offset per row. void scaleColumnOnPut (const Array& array, Array& target); protected: //# Now define the data members. String scaleName_p; //# name of scale column String offsetName_p; //# name of offset column Float scale_p; //# fixed scale factor Float offset_p; //# fixed offset value Bool fixed_p; //# scale/offset is fixed Bool autoScale_p; //# determine scale/offset automatically ScalarColumn* scaleColumn_p; //# column with scale value ScalarColumn* offsetColumn_p; //# column with offset value Array buffer_p; //# buffer to avoid Array constructions //# (makes multi-threading harder) // Get the scale value for this row. Float getScale (rownr_t rownr); // Get the offset value for this row. Float getOffset (rownr_t rownr); // Find minimum and maximum from the array data. // NaN and infinite values are ignored. If no values are finite, // minimum and maximum are set to NaN. virtual void findMinMax (Float& minVal, Float& maxVal, const Array& array) const; // Make scale and offset from the minimum and maximum of the array data. // If minVal is NaN, scale is set to 0. void makeScaleOffset (Float& scale, Float& offset, Float minVal, Float maxVal) const; // Put a part of an array in a row using given scale/offset values. void putPart (rownr_t rownr, const Slicer& slicer, const Array& array, Float scale, Float offset); // Fill the array part into the full array and put it using the // given min/max values. void putFullPart (rownr_t rownr, const Slicer& slicer, Array& fullArray, const Array& partArray, Float minVal, Float maxVal); public: // Define the "constructor" to construct this engine when a // table is read back. // This "constructor" has to be registered by the user of the engine. // If the engine is commonly used, its registration can be added // to the registerAllCtor function in DataManager.cc. // That function gets automatically invoked by the table system. static DataManager* makeObject (const String& dataManagerType, const Record& spec); }; // // Virtual column engine to scale a table Complex array for Single Dish data // // // // // //# Classes you should understand before using this one. //
      • CompressComplex // // // CompressComplexSD is similar to CompressComplex, but compresses // in a slighty different way optimized for single dish data. // Usually the imaginary part of single dish data is 0, so the scaling // is optimized for it. //
        If the imaginary part is 0, the real part is scaled with 15 bits // extra to get a higher precision. The least significant bit is set to 0 // indicating the imag==0. //
        If the imaginary part is not 0, the real part is scaled normally. // The imaginary part is scaled with 1 bit less. The least significant bit // is set to 1 indicating that imag!=0. //
        // // This class is created on top of CompressComplex to cope with SD data // in a better way. Using CompressComplex often makes the imag part non-zero // if it is scaled as 0. // // // // // Create the table description and 2 columns with indirect arrays in it. // // The Int column will be stored, while the double will be // // used as virtual. // TableDesc tableDesc ("", TableDesc::Scratch); // tableDesc.addColumn (ArrayColumnDesc ("storedArray")); // tableDesc.addColumn (ArrayColumnDesc ("virtualArray")); // tableDesc.addColumn (ScalarColumnDesc ("scale")); // tableDesc.addColumn (ScalarColumnDesc ("offset")); // // // Create a new table using the table description. // SetupNewTable newtab (tableDesc, "tab.data", Table::New); // // // Create the array scaling engine (with auto-scale) // // and bind it to the Complex column. // CompressComplexSD scalingEngine("virtualArray", "storedArray", // "scale", "offset"); // newtab.bindColumn ("virtualArray", scalingEngine); // // Create the table. // Table table (newtab); // // // Store a 3-D array (with dim. 2,3,4) into each row of the column. // // The shape of each array in the column is implicitly set by the put // // function. This will also set the shape of the underlying Int array. // ArrayColumn data (table, "virtualArray"); // Array someArray(IPosition(4,2,3,4)); // someArray = 0; // for (rownr_t i=0, i<10; i++) { // table will have 10 rows // table.addRow(); // data.put (i, someArray) // } // // class CompressComplexSD : public CompressComplex { public: // Construct an engine to scale all arrays in a column with // the given offset and scale factor. // StoredColumnName is the name of the column where the scaled // data will be put and must have data type Int. // The virtual column using this engine must have data type Complex. CompressComplexSD (const String& virtualColumnName, const String& storedColumnName, Float scale, Float offset = 0); // Construct an engine to scale the arrays in a column. // The scale and offset values are taken from a column with // the given names. In that way each array has its own scale factor // and offset value. // An exception is thrown if these columns do not exist. // VirtualColumnName is the name of the virtual column and is used to // check if the engine gets bound to the correct column. // StoredColumnName is the name of the column where the scaled // data will be put and must have data type Int. // The virtual column using this engine must have data type Complex. CompressComplexSD (const String& virtualColumnName, const String& storedColumnName, const String& scaleColumnName, const String& offsetColumnName, Bool autoScale = True); // Construct from a record specification as created by getmanagerSpec(). CompressComplexSD (const Record& spec); // Destructor is mandatory. ~CompressComplexSD(); // Assignment is not needed and therefore forbidden CompressComplexSD& operator= (const CompressComplexSD&) = delete; // Return the type name of the engine (i.e. its class name). virtual String dataManagerType() const; // Return the name of the class. // This includes the names of the template arguments. static String className(); // Register the class name and the static makeObject "constructor". // This will make the engine known to the table system. static void registerClass(); private: // Copy constructor is only used by clone(). // (so it is made private). CompressComplexSD (const CompressComplexSD&); // Clone the engine object. virtual DataManager* clone() const; // Initialize the object for a new table. // It defines the keywords containing the engine parameters. virtual void create64 (rownr_t initialNrrow); // Scale and/or offset target to array. // This is meant when reading an array from the stored column. // It optimizes for scale=1 and/or offset=0. virtual void scaleOnGet (Float scale, Float offset, Array& array, const Array& target); // Scale and/or offset array to target. // This is meant when writing an array into the stored column. // It optimizes for scale=1 and/or offset=0. virtual void scaleOnPut (Float scale, Float offset, const Array& array, Array& target); // Find minimum and maximum from the array data. // NaN and infinite values and zero imaginary parts are ignored. // If no values are finite, minimum and maximum are set to NaN. virtual void findMinMax (Float& minVal, Float& maxVal, const Array& array) const; public: // Define the "constructor" to construct this engine when a // table is read back. // This "constructor" has to be registered by the user of the engine. // If the engine is commonly used, its registration can be added // to the registerAllCtor function in DataManager.cc. // That function gets automatically invoked by the table system. static DataManager* makeObject (const String& dataManagerType, const Record& spec); }; inline Float CompressComplex::getScale (rownr_t rownr) { return (fixed_p ? scale_p : (*scaleColumn_p)(rownr)); } inline Float CompressComplex::getOffset (rownr_t rownr) { return (fixed_p ? offset_p : (*offsetColumn_p)(rownr)); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/CompressFloat.cc000066400000000000000000000420461476623553700212010ustar00rootroot00000000000000//# CompressFloat.cc: Virtual column engine to scale a table float array //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN CompressFloat::CompressFloat (const String& virtualColumnName, const String& storedColumnName, Float scale, Float offset) : BaseMappedArrayEngine (virtualColumnName, storedColumnName), scale_p (scale), offset_p (offset), fixed_p (True), autoScale_p (False), scaleColumn_p (0), offsetColumn_p (0) {} CompressFloat::CompressFloat (const String& virtualColumnName, const String& storedColumnName, const String& scaleColumnName, const String& offsetColumnName, Bool autoScale) : BaseMappedArrayEngine (virtualColumnName, storedColumnName), scaleName_p (scaleColumnName), offsetName_p (offsetColumnName), scale_p (0.0), offset_p (0.0), fixed_p (False), autoScale_p (autoScale), scaleColumn_p (0), offsetColumn_p (0) {} CompressFloat::CompressFloat (const Record& spec) : BaseMappedArrayEngine (), scale_p (1.0), offset_p (0.0), fixed_p (True), autoScale_p (False), scaleColumn_p (0), offsetColumn_p (0) { if (spec.isDefined("SOURCENAME") && spec.isDefined("TARGETNAME")) { setNames (spec.asString("SOURCENAME"), spec.asString("TARGETNAME")); if (spec.isDefined("SCALE") && spec.isDefined("OFFSET")) { spec.get ("SCALE", scale_p); spec.get ("OFFSET", offset_p); } else { spec.get ("SCALENAME", scaleName_p); spec.get ("OFFSETNAME", offsetName_p); fixed_p = False; } if (spec.isDefined("AUTOSCALE")) { spec.get ("AUTOSCALE", autoScale_p); } } } CompressFloat::CompressFloat (const CompressFloat& that) : BaseMappedArrayEngine (that), scaleName_p (that.scaleName_p), offsetName_p (that.offsetName_p), scale_p (that.scale_p), offset_p (that.offset_p), fixed_p (that.fixed_p), autoScale_p (that.autoScale_p), scaleColumn_p (0), offsetColumn_p (0) {} CompressFloat::~CompressFloat() { delete scaleColumn_p; delete offsetColumn_p; } //# Clone the engine object. DataManager* CompressFloat::clone() const { return new CompressFloat (*this); } //# Return the type name of the engine (i.e. its class name). String CompressFloat::dataManagerType() const { return className(); } //# Return the class name. //# Get the data type names using class ValType. String CompressFloat::className() { return "CompressFloat"; } String CompressFloat::dataManagerName() const { return virtualName(); } Record CompressFloat::dataManagerSpec() const { Record spec; spec.define ("SOURCENAME", virtualName()); spec.define ("TARGETNAME", storedName()); if (fixed_p) { spec.define ("SCALE", scale_p); spec.define ("OFFSET", offset_p); } else { spec.define ("SCALENAME", scaleName_p); spec.define ("OFFSETNAME", offsetName_p); } spec.define ("AUTOSCALE", autoScale_p); return spec; } DataManager* CompressFloat::makeObject (const String&, const Record& spec) { return new CompressFloat(spec); } void CompressFloat::registerClass() { DataManager::registerCtor (className(), makeObject); } void CompressFloat::create64 (rownr_t initialNrrow) { BaseMappedArrayEngine::create64 (initialNrrow); // Store the various parameters as keywords in this column. TableColumn thisCol (table(), virtualName()); thisCol.rwKeywordSet().define ("_CompressFloat_Scale", scale_p); thisCol.rwKeywordSet().define ("_CompressFloat_Offset", offset_p); thisCol.rwKeywordSet().define ("_CompressFloat_ScaleName", scaleName_p); thisCol.rwKeywordSet().define ("_CompressFloat_OffsetName", offsetName_p); thisCol.rwKeywordSet().define ("_CompressFloat_Fixed", fixed_p); thisCol.rwKeywordSet().define ("_CompressFloat_AutoScale", autoScale_p); } void CompressFloat::prepare() { BaseMappedArrayEngine::prepare1(); TableColumn thisCol (table(), virtualName()); thisCol.keywordSet().get ("_CompressFloat_Scale", scale_p); thisCol.keywordSet().get ("_CompressFloat_Offset", offset_p); thisCol.keywordSet().get ("_CompressFloat_ScaleName", scaleName_p); thisCol.keywordSet().get ("_CompressFloat_OffsetName", offsetName_p); thisCol.keywordSet().get ("_CompressFloat_Fixed", fixed_p); thisCol.keywordSet().get ("_CompressFloat_AutoScale", autoScale_p); //# Allocate column objects to get scale and offset. if (! fixed_p) { scaleColumn_p = new ScalarColumn (table(), scaleName_p); offsetColumn_p = new ScalarColumn (table(), offsetName_p); } // Do this at the end, because it might call addRow. BaseMappedArrayEngine::prepare2(); } void CompressFloat::reopenRW() {} void CompressFloat::addRowInit (rownr_t startRow, rownr_t nrrow) { BaseMappedArrayEngine::addRowInit (startRow, nrrow); if (autoScale_p) { for (rownr_t i=0; iput (startRow++, 0.); } } } // Find minimum and maximum. void CompressFloat::findMinMax (Float& minVal, Float& maxVal, const Array& array) const { setNaN (minVal); setNaN (maxVal); Bool deleteIt; const Float* data = array.getStorage (deleteIt); const Int64 nr = array.nelements(); Bool firstTime = True; for (Int64 i=0; i maxVal) { maxVal = data[i]; } } } } array.freeStorage (data, deleteIt); } // Find minimum and maximum. void CompressFloat::makeScaleOffset (Float& scale, Float& offset, Float minVal, Float maxVal) const { if (isNaN (minVal)) { scale = 0; offset = 0; } else { if (minVal == maxVal) { scale = 1; } else { scale = (maxVal - minVal) / 65534; } offset = (maxVal + minVal) / 2; } } // Scale/offset an array for get. void CompressFloat::scaleOnGet (Float scale, Float offset, Array& array, const Array& target) { Bool deleteIn, deleteOut; Float* out = array.getStorage (deleteOut); const Short* in = target.getStorage (deleteIn); const Int64 nr = array.nelements(); for (Int64 i=0; i& array, Array& target) { Bool deleteIn, deleteOut; const Float* in = array.getStorage (deleteIn); Short* out = target.getStorage (deleteOut); const Int64 nr = array.nelements(); for (Int64 i=0; i& array, const Array& target) { if (fixed_p) { scaleOnGet (scale_p, offset_p, array, target); }else{ ArrayIterator arrayIter (array, array.ndim() - 1); ReadOnlyArrayIterator targetIter (target, target.ndim() - 1); rownr_t rownr = 0; while (! arrayIter.pastEnd()) { scaleOnGet (getScale(rownr), getOffset(rownr), arrayIter.array(), targetIter.array()); rownr++; arrayIter.next(); targetIter.next(); } } } void CompressFloat::scaleColumnOnPut (const Array& array, Array& target) { if (fixed_p) { scaleOnPut (scale_p, offset_p, array, target); }else{ ReadOnlyArrayIterator arrayIter (array, array.ndim() - 1); ArrayIterator targetIter (target, target.ndim() - 1); rownr_t rownr = 0; while (! arrayIter.pastEnd()) { scaleOnPut (getScale(rownr), getOffset(rownr), arrayIter.array(), targetIter.array()); rownr++; arrayIter.next(); targetIter.next(); } } } void CompressFloat::getArray (rownr_t rownr, Array& array) { if (! array.shape().isEqual (buffer_p.shape())) { buffer_p.resize (array.shape()); } column().baseGet (rownr, buffer_p); scaleOnGet (getScale(rownr), getOffset(rownr), array, buffer_p); } void CompressFloat::putArray (rownr_t rownr, const Array& array) { if (! array.shape().isEqual (buffer_p.shape())) { buffer_p.resize (array.shape()); } if (! autoScale_p) { scaleOnPut (getScale(rownr), getOffset(rownr), array, buffer_p); } else { Float minVal, maxVal; findMinMax (minVal, maxVal, array); Float scale, offset; makeScaleOffset (scale, offset, minVal, maxVal); scaleColumn_p->put (rownr, scale); offsetColumn_p->put (rownr, offset); scaleOnPut (scale, offset, array, buffer_p); } column().basePut (rownr, buffer_p); } void CompressFloat::getSlice (rownr_t rownr, const Slicer& slicer, Array& array) { if (! array.shape().isEqual (buffer_p.shape())) { buffer_p.resize (array.shape()); } column().getSlice (rownr, slicer, buffer_p); scaleOnGet (getScale(rownr), getOffset(rownr), array, buffer_p); } void CompressFloat::putPart (rownr_t rownr, const Slicer& slicer, const Array& array, Float scale, Float offset) { if (! array.shape().isEqual (buffer_p.shape())) { buffer_p.resize (array.shape()); } scaleOnPut (scale, offset, array, buffer_p); column().putSlice (rownr, slicer, buffer_p); } void CompressFloat::putFullPart (rownr_t rownr, const Slicer& slicer, Array& fullArray, const Array& partArray, Float minVal, Float maxVal) { Array subarr = fullArray(slicer.start(), slicer.end(), slicer.stride()); subarr = partArray; Float scale, offset; makeScaleOffset (scale, offset, minVal, maxVal); scaleColumn_p->put (rownr, scale); offsetColumn_p->put (rownr, offset); if (! fullArray.shape().isEqual (buffer_p.shape())) { buffer_p.resize (fullArray.shape()); } scaleOnPut (scale, offset, fullArray, buffer_p); column().basePut (rownr, buffer_p); } void CompressFloat::putSlice (rownr_t rownr, const Slicer& slicer, const Array& array) { // If the slice is the entire array, write it as such. IPosition shp = shape(rownr); if (shp.isEqual (array.shape())) { CompressFloat::putArray (rownr, array); } else { // Get current scale and offset. // If no autoscaling, write the part immediately. Float scale = getScale(rownr); Float offset = getOffset(rownr); if (! autoScale_p) { putPart (rownr, slicer, array, scale, offset); } else { // Determine min/max of new slice. // scale==0 means that no array data was written yet. // In that case initialize array to NaN if the slice has valid data. Float minValArr, maxValArr; findMinMax (minValArr, maxValArr, array); if (scale == 0) { if (! isNaN(minValArr)) { Array arr(shp); Float val; setNaN (val); arr = val; putFullPart (rownr, slicer, arr, array, minValArr, maxValArr); } } else { // Valid data in row. // Writing the part will do if no valid data in it or if // its min/max is within the current min/max. // Otherwise we have to rescale using new min/max. Float maxValRow = offset + scale*65534/2; Float minValRow = offset - scale*65534/2; if (isNaN(minValArr) || (minValArr >= minValRow && maxValArr <= maxValRow)) { putPart (rownr, slicer, array, scale, offset); } else { Array arr(shp); CompressFloat::getArray (rownr, arr); putFullPart (rownr, slicer, arr, array, min(minValRow, minValArr), max(maxValRow, maxValArr)); } } } } } void CompressFloat::getArrayColumn (Array& array) { Array target(array.shape()); column().getColumn (target); scaleColumnOnGet (array, target); } void CompressFloat::putArrayColumn (const Array& array) { Array target(array.shape()); if (! autoScale_p) { scaleColumnOnPut (array, target); column().putColumn (target); } else { ReadOnlyArrayIterator iter(array, array.ndim()-1); rownr_t nrrow = table().nrow(); for (rownr_t rownr=0; rownr& array) { ArrayIterator arrIter(array, array.ndim()-1); RefRowsSliceIter rowsIter(rownrs); while (! rowsIter.pastEnd()) { rownr_t rownr = rowsIter.sliceStart(); rownr_t end = rowsIter.sliceEnd(); rownr_t incr = rowsIter.sliceIncr(); while (rownr <= end) { CompressFloat::getArray (rownr, arrIter.array()); arrIter.next(); rownr += incr; } rowsIter++; } } void CompressFloat::putArrayColumnCells (const RefRows& rownrs, const Array& array) { ReadOnlyArrayIterator arrIter(array, array.ndim()-1); RefRowsSliceIter rowsIter(rownrs); while (! rowsIter.pastEnd()) { rownr_t rownr = rowsIter.sliceStart(); rownr_t end = rowsIter.sliceEnd(); rownr_t incr = rowsIter.sliceIncr(); while (rownr <= end) { CompressFloat::putArray (rownr, arrIter.array()); arrIter.next(); rownr += incr; } rowsIter++; } } void CompressFloat::getColumnSlice (const Slicer& slicer, Array& array) { Array target(array.shape()); column().getColumn (slicer, target); scaleColumnOnGet (array, target); } void CompressFloat::putColumnSlice (const Slicer& slicer, const Array& array) { Array target(array.shape()); if (! autoScale_p) { scaleColumnOnPut (array, target); column().putColumn (slicer, target); } else { ReadOnlyArrayIterator iter(array, array.ndim()-1); rownr_t nrrow = table().nrow(); for (rownr_t rownr=0; rownr& array) { ArrayIterator arrIter(array, array.ndim()-1); RefRowsSliceIter rowsIter(rownrs); while (! rowsIter.pastEnd()) { rownr_t rownr = rowsIter.sliceStart(); rownr_t end = rowsIter.sliceEnd(); rownr_t incr = rowsIter.sliceIncr(); while (rownr <= end) { CompressFloat::getSlice (rownr, slicer, arrIter.array()); arrIter.next(); rownr += incr; } rowsIter++; } } void CompressFloat::putColumnSliceCells (const RefRows& rownrs, const Slicer& slicer, const Array& array) { ReadOnlyArrayIterator arrIter(array, array.ndim()-1); RefRowsSliceIter rowsIter(rownrs); while (! rowsIter.pastEnd()) { rownr_t rownr = rowsIter.sliceStart(); rownr_t end = rowsIter.sliceEnd(); rownr_t incr = rowsIter.sliceIncr(); while (rownr <= end) { CompressFloat::putSlice (rownr, slicer, arrIter.array()); arrIter.next(); rownr += incr; } rowsIter++; } } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/CompressFloat.h000066400000000000000000000346221476623553700210440ustar00rootroot00000000000000//# CompressFloat.h: Virtual column engine to scale a table float array //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_COMPRESSFLOAT_H #define TABLES_COMPRESSFLOAT_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Virtual column engine to scale a table float array // // // // // //# Classes you should understand before using this one. //
      • VirtualColumnEngine //
      • VirtualArrayColumn // // // CompressFloat is a virtual column engine which scales an array // of one type to another type to save disk storage. // This resembles the classic AIPS compress method which scales the // data from float to short. // The scale factor and offset values can be given in two ways: //
          //
        • As a fixed values which is used for all arrays in the column. // These values have to be given when constructing of the engine. //
        • As the name of a column. In this way each array in the // column has its own scale and offset value. // By default it uses auto-scaling (see below). // Otherwise the scale and offset value in a row must be put // before the array is put and should not be changed anymore. //
        // Auto-scaling means that the engine will determine the scale // and offset value itself when an array (or a slice) is put. // It does it by mapping the values in the array to the range [-32767,32767]. // At each put the scale/offset values are changed as needed. // Note that with auto-scaling putSlice can be somewhat // slower, because the entire array might need to be rescaled. // // As in FITS the scale and offset values are used as: //
        True_value = Stored_value * scale + offset; // // An engine object should be used for one column only, because the stored // column name is part of the engine. If it would be used for more than // one column, they would all share the same stored column. // When the engine is bound to a column, it is checked if the name // of that column matches the given virtual column name. // // The engine can be used for a column containing any kind of array // (thus direct or indirect, fixed or variable shaped)) as long as the // virtual array can be stored in the stored array. Thus a fixed shaped // virtual can use a variable shaped stored, but not vice versa. // A fixed shape indirect virtual can use a stored with direct arrays. // // This class can also serve as an example of how to implement // a virtual column engine. //
        // // This class allows to store data in a smaller representation. // It is needed to resemble the classic AIPS compress option. // // Because the engine can serve only one column, it was possible to // combine the engine and the column functionality in one class. // // // // // Create the table description and 2 columns with indirect arrays in it. // // The Int column will be stored, while the double will be // // used as virtual. // TableDesc tableDesc ("", TableDesc::Scratch); // tableDesc.addColumn (ArrayColumnDesc ("storedArray")); // tableDesc.addColumn (ArrayColumnDesc ("virtualArray")); // tableDesc.addColumn (ScalarColumnDesc ("scale")); // tableDesc.addColumn (ScalarColumnDesc ("offset")); // // // Create a new table using the table description. // SetupNewTable newtab (tableDesc, "tab.data", Table::New); // // // Create the array scaling engine (with auto-scale) // // and bind it to the float column. // CompressFloat scalingEngine("virtualArray", "storedArray", // "scale", "offset"); // newtab.bindColumn ("virtualArray", scalingEngine); // // Create the table. // Table table (newtab); // // // Store a 3-D array (with dim. 2,3,4) into each row of the column. // // The shape of each array in the column is implicitly set by the put // // function. This will also set the shape of the underlying Int array. // ArrayColumn data (table, "virtualArray"); // Array someArray(IPosition(4,2,3,4)); // someArray = 0; // for (rownr_t i=0, i<10; i++) { // table will have 10 rows // table.addRow(); // data.put (i, someArray) // } // // class CompressFloat : public BaseMappedArrayEngine { public: // Construct an engine to scale all arrays in a column with // the given offset and scale factor. // StoredColumnName is the name of the column where the scaled // data will be put and must have data type Short. // The virtual column using this engine must have data type Float. CompressFloat (const String& virtualColumnName, const String& storedColumnName, Float scale, Float offset = 0); // Construct an engine to scale the arrays in a column. // The scale and offset values are taken from a column with // the given names. In that way each array has its own scale factor // and offset value. // An exception is thrown if these columns do not exist. // VirtualColumnName is the name of the virtual column and is used to // check if the engine gets bound to the correct column. // StoredColumnName is the name of the column where the scaled // data will be put and must have data type Short. // The virtual column using this engine must have data type Float. CompressFloat (const String& virtualColumnName, const String& storedColumnName, const String& scaleColumnName, const String& offsetColumnName, Bool autoScale = True); // Construct from a record specification as created by getmanagerSpec(). CompressFloat (const Record& spec); // Destructor is mandatory. ~CompressFloat(); // Assignment is not needed and therefore forbidden CompressFloat& operator= (const CompressFloat&) = delete; // Return the type name of the engine (i.e. its class name). virtual String dataManagerType() const; // Get the name given to the engine (is the virtual column name). virtual String dataManagerName() const; // Record a record containing data manager specifications. virtual Record dataManagerSpec() const; // Return the name of the class. // This includes the names of the template arguments. static String className(); // Register the class name and the static makeObject "constructor". // This will make the engine known to the table system. static void registerClass(); private: // Copy constructor is only used by clone(). // (so it is made private). CompressFloat (const CompressFloat&); // Clone the engine object. virtual DataManager* clone() const; // Initialize the object for a new table. // It defines the keywords containing the engine parameters. virtual void create64 (rownr_t initialNrrow); // Preparing consists of setting the writable switch and // adding the initial number of rows in case of create. // Furthermore it reads the keywords containing the engine parameters. virtual void prepare(); // Reopen the engine for read/write access. // It makes the column writable if the underlying column is writable. virtual void reopenRW(); // Add rows to the table. // If auto-scaling, it initializes the scale column with 0 // to indicate that no data has been processed yet. virtual void addRowInit (rownr_t startRow, rownr_t nrrow); // Get an array in the given row. // This will scale and offset from the underlying array. virtual void getArray (rownr_t rownr, Array& array); // Put an array in the given row. // This will scale and offset to the underlying array. virtual void putArray (rownr_t rownr, const Array& array); // Get a section of the array in the given row. // This will scale and offset from the underlying array. virtual void getSlice (rownr_t rownr, const Slicer& slicer, Array& array); // Put into a section of the array in the given row. // This will scale and offset to the underlying array. virtual void putSlice (rownr_t rownr, const Slicer& slicer, const Array& array); // Get an entire column. // This will scale and offset from the underlying array. virtual void getArrayColumn (Array& array); // Put an entire column. // This will scale and offset to the underlying array. virtual void putArrayColumn (const Array& array); // Get some array values in the column. // This will scale and offset from the underlying array. virtual void getArrayColumnCells (const RefRows& rownrs, Array& data); // Put some array values in the column. // This will scale and offset to the underlying array. virtual void putArrayColumnCells (const RefRows& rownrs, const Array& data); // Get a section of all arrays in the column. // This will scale and offset from the underlying array. virtual void getColumnSlice (const Slicer& slicer, Array& array); // Put a section of all arrays in the column. // This will scale and offset to the underlying array. virtual void putColumnSlice (const Slicer& slicer, const Array& array); // Get a section of some arrays in the column. // This will scale and offset from the underlying array. virtual void getColumnSliceCells (const RefRows& rownrs, const Slicer& slicer, Array& data); // Put into a section of some arrays in the column. // This will scale and offset to the underlying array. virtual void putColumnSliceCells (const RefRows& rownrs, const Slicer& slicer, const Array& data); // Scale and/or offset target to array. // This is meant when reading an array from the stored column. // It optimizes for scale=1 and/or offset=0. void scaleOnGet (Float scale, Float offset, Array& array, const Array& target); // Scale and/or offset array to target. // This is meant when writing an array into the stored column. // It optimizes for scale=1 and/or offset=0. void scaleOnPut (Float scale, Float offset, const Array& array, Array& target); // Scale and/or offset target to array for the entire column. // When the scale and offset are fixed, it will do the entire array. // Otherwise it iterates through the array and applies the scale // and offset per row. void scaleColumnOnGet (Array& array, const Array& target); // Scale and/or offset array to target for the entire column. // When the scale and offset are fixed, it will do the entire array. // Otherwise it iterates through the array and applies the scale // and offset per row. void scaleColumnOnPut (const Array& array, Array& target); //# Now define the data members. String scaleName_p; //# name of scale column String offsetName_p; //# name of offset column Float scale_p; //# fixed scale factor Float offset_p; //# fixed offset value Bool fixed_p; //# scale/offset is fixed Bool autoScale_p; //# determine scale/offset automatically ScalarColumn* scaleColumn_p; //# column with scale value ScalarColumn* offsetColumn_p; //# column with offset value Array buffer_p; //# buffer to avoid Array constructions // Get the scale value for this row. Float getScale (rownr_t rownr); // Get the offset value for this row. Float getOffset (rownr_t rownr); // Find minimum and maximum from the array data. // NaN and infinite values are ignored. If no values are finite, // minimum and maximum are set to NaN. void findMinMax (Float& minVal, Float& maxVal, const Array& array) const; // Make scale and offset from the minimum and maximum of the array data. // If minVal is NaN, scale is set to 0. void makeScaleOffset (Float& scale, Float& offset, Float minVal, Float maxVal) const; // Put a part of an array in a row using given scale/offset values. void putPart (rownr_t rownr, const Slicer& slicer, const Array& array, Float scale, Float offset); // Fill the array part into the full array and put it using the // given min/max values. void putFullPart (rownr_t rownr, const Slicer& slicer, Array& fullArray, const Array& partArray, Float minVal, Float maxVal); public: // Define the "constructor" to construct this engine when a // table is read back. // This "constructor" has to be registered by the user of the engine. // If the engine is commonly used, its registration can be added // to the registerAllCtor function in DataManager.cc. // That function gets automatically invoked by the table system. static DataManager* makeObject (const String& dataManagerType, const Record& spec); }; inline Float CompressFloat::getScale (rownr_t rownr) { return (fixed_p ? scale_p : (*scaleColumn_p)(rownr)); } inline Float CompressFloat::getOffset (rownr_t rownr) { return (fixed_p ? offset_p : (*offsetColumn_p)(rownr)); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/DataManAccessor.cc000066400000000000000000000047061476623553700214110ustar00rootroot00000000000000//# DataManAccessor.cc: Base class for the Data Manager Accessor classes //# Copyright (C) 1996 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN RODataManAccessor::RODataManAccessor (const Table& table, const String& name, Bool byColumn) : itsDataManager(0) { itsDataManager = table.findDataManager (name, byColumn); } RODataManAccessor::~RODataManAccessor() {} void RODataManAccessor::setProperties (const Record& prop) const { if (itsDataManager) { itsDataManager->setProperties (prop); } else { throw DataManError ("setProperties cannot be used on a default " "empty RODataManAccessor object"); } } Record RODataManAccessor::getProperties() const { if (itsDataManager) { return itsDataManager->getProperties(); } throw DataManError ("getProperties cannot be used on a default " "empty RODataManAccessor object"); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/DataManAccessor.h000066400000000000000000000101061476623553700212420ustar00rootroot00000000000000//# DataManAccessor.h: Base class for the Data Manager Accessor classes //# Copyright (C) 1996,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_DATAMANACCESSOR_H #define TABLES_DATAMANACCESSOR_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Base class for the Data Manager Accessor classes. // // // // // //# Classes you should understand before using this one. //
      • DataManager // // // The Table system has one or more data managers underneath. // Once a table is constructed, these data managers are invisible // and there is no way to get access to them. // However, sometimes limited access to them is needed (e.g. to // set the size of an internal cache). //

        // This class should be used as the base class for specialized // Data Manager Accessor classes (e.g. class // // ROIncrementalStManAccessor. // This base class provides the functionality to get the // DataManager object for a given column. // // // This base class makes it possible that every derived class can get the // data manager, because RODataManAccessor is a friend of class Table. // Otherwise all accessor classes needed to be friend of Table. // //# //# class RODataManAccessor { public: // Construct an empty object. RODataManAccessor() : itsDataManager(0) {} // Construct the accessor object for a data manager in the table. // An exception is thrown if the name of the data manager or column is // unknown. RODataManAccessor (const Table& table, const String& name, Bool byColumn); virtual ~RODataManAccessor(); // Set data manager properties using the fields in the record. // Each data manager has its specific set of properties. void setProperties (const Record&) const; // Get data manager properties as a record. Record getProperties() const; // Get the data manager type. String dataManagerType() const { return itsDataManager->dataManagerType(); } // Get the data manager name. String dataManagerName() const { return itsDataManager->dataManagerName(); } // Get the data manager sequence nr. uInt dataManagerSeqNr() const { return itsDataManager->sequenceNr(); } // Show IO statistics. void showCacheStatistics (ostream& os) const { itsDataManager->showCacheStatistics (os); } protected: // Get the data manager for the given data manager or column name. DataManager* baseDataManager() const { return itsDataManager; } private: DataManager* itsDataManager; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/DataManError.cc000066400000000000000000000055151476623553700207370ustar00rootroot00000000000000//# DataManError.cc: Storage manager error classes //# Copyright (C) 1994,1995,1996,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Implementation of DataManager error classes. DataManError::DataManError () : AipsError("Table DataManager error") { ; } DataManError::DataManError (const String& str) : AipsError("Table DataManager error: " + str) { ; } DataManError::~DataManError () noexcept { ; } DataManInternalError::DataManInternalError (const String& str) : DataManError("Internal error: " + str) { ; } DataManInternalError::~DataManInternalError () noexcept { ; } DataManUnknownCtor::DataManUnknownCtor (const String& msg) : DataManError(msg) {} DataManUnknownCtor::~DataManUnknownCtor () noexcept {} DataManInvDT::DataManInvDT () : DataManError ("Invalid data type") { ; } DataManInvDT::DataManInvDT (const String& name) : DataManError ("Invalid data type when accessing column " + name) { ; } DataManInvDT::~DataManInvDT () noexcept { ; } DataManInvOper::DataManInvOper () : DataManError ("Invalid operation") { ; } DataManInvOper::DataManInvOper (const String& s) : DataManError ("Invalid operation: " + s) { ; } DataManInvOper::~DataManInvOper () noexcept { ; } DataManUnknownVirtualColumn::DataManUnknownVirtualColumn (const String& columnName, const String& engineName) : DataManError ("column " + columnName + " is unknown to virtual column engine " + engineName) { ; } DataManUnknownVirtualColumn::~DataManUnknownVirtualColumn () noexcept { ; } TSMError::TSMError (const String& s) : DataManError ("TiledStMan: " + s) { ; } TSMError::~TSMError () noexcept { ; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/DataManError.h000066400000000000000000000137641476623553700206060ustar00rootroot00000000000000//# DataManError.h: Data manager error classes //# Copyright (C) 1994,1995,1996,1999,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_DATAMANERROR_H #define TABLES_DATAMANERROR_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# This header file defines the error classes used in the //# data manager classes. //

        // Base error class for table data manager // // // // // // This is the generic data manager exception; catching this one means // catching all DataMan* exceptions. // Note that you have to catch AipsError to catch all possible exceptions. // class DataManError : public AipsError { public: // The default constructor generates the message "Table DataManager error". DataManError (); // Construct with given message. DataManError (const String& message); ~DataManError () noexcept; }; // // Internal table data manager error // // // // // // Internal data manager error (should never be thrown). // If this is thrown, something is terribly wrong. // class DataManInternalError : public DataManError { public: // Add given message to string "Internal Table DataManager error: ". DataManInternalError (const String& message); ~DataManInternalError () noexcept; }; // // Table DataManager error; invalid data manager // // // // // // A data manager is unknown (i.e. not registered in DataManReg.cc). // This means that the data manager object cannot be recreated. // class DataManUnknownCtor : public DataManError { public: // This constructor generates a message that a data manager // with the given name is unknown (i.e. not registered). DataManUnknownCtor (const String& columnName); ~DataManUnknownCtor () noexcept; }; // // Table DataManager error; invalid data type // // // // // // Invalid data type used in the data manager. // The data manager found an unknown data type when doing a get or put. // In principle this error should never occur. // class DataManInvDT : public DataManError { public: // The default constructor generates a generic "invalid data type" message. DataManInvDT (); // Put the name of the offending column in the "invalid data type" message. DataManInvDT (const String& columnName); ~DataManInvDT () noexcept; }; // // Table DataManager error; invalid operation // // // // // // Invalid operation on a data manager. // A request was done that the data manager could not handle. // In principle the table system should already test on such operations // and it should not bother the data manager with invalid requests. // However, the data manager still tests them for safety. // class DataManInvOper : public DataManError { public: // The default constructor generates a generic "invalid operation" message. DataManInvOper (); // Add given message to string "Invalid DataMan operation: ". DataManInvOper (const String& message); ~DataManInvOper () noexcept; }; // // Table DataManager error; unknown virtual column // // // // // // A column is unknown to the virtual column engine. // This error is caused by binding a column to a virtual column engine // which does not know the column name or data type. // class DataManUnknownVirtualColumn : public DataManError { public: // Issue a message containing the column name. DataManUnknownVirtualColumn (const String& columnName, const String& engineName); ~DataManUnknownVirtualColumn () noexcept; }; // // Table DataManager error; error in TiledStMan // // // // // // An error was made when using the TiledStMan. // The TiledStMan is quite complex, so it is easy to make mistakes. // The TiledStMan and related // classes should be studied carefully. // class TSMError : public DataManError { public: // Issue the message prefixed by "TiledStMan: ". TSMError (const String& message); ~TSMError () noexcept; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/DataManInfo.cc000066400000000000000000000431441476623553700205410ustar00rootroot00000000000000//# DataManInfo.cc: Class with static functions to manipulate a datamanager info record //# Copyright (C) 2001,2002,2003,2009 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN void DataManInfo::removeHypercolumns (TableDesc& tabDesc) { tabDesc.adjustHypercolumns (std::map()); } void DataManInfo::adjustDesc (TableDesc& tdesc, const Record& dminfo) { // Find out the columns and data manager groups of the fields. std::map dmTypeMap; std::map dmGroupMap; for (uInt i=0; i cols = sub.asArrayString ("COLUMNS"); for (uInt j=0; j::iterator iter1 = dmTypeMap.find (name); if (iter1 != dmTypeMap.end()) { String v = iter1->second; if (! v.empty()) { cdesc.dataManagerType() = v; } } std::map::iterator iter2 = dmGroupMap.find (name); if (iter2 != dmTypeMap.end()) { String v = iter2->second; if (! v.empty()) { cdesc.dataManagerGroup() = v; } } } // Remove hypercolumn definitions which are different from // data manager group in the column descriptions. Vector hcNames = tdesc.hypercolumnNames(); for (uInt i=0; i dataNames, coordNames, idNames; tdesc.hypercolumnDesc (hcNames[i], dataNames, coordNames, idNames); Bool same = True; for (uInt j=0; j dataNames, coordNames, idNames; // Keep track of hypercolumns to be changed. Vector hcChange; uInt nrhc = 0; // Loop through all hypercolumn descriptions. Vector hcNames = tabDesc.hypercolumnNames(); for (uInt i=0; i 0) { // The hypercolumn definition contains ID columns, so it // has to be changed later in the TableDesc. hcChange.resize (nrhc+1, True); hcChange(nrhc++) = hcNames(i); // Keep the dminfo columns which are not an ID column. Vector colNames = rec.asArrayString("COLUMNS"); Vector colsout(colNames.nelements()); uInt nrout = 0; for (uInt k=0; k 0) { tabDesc.removeIDhypercolumns (hcChange); } } Record DataManInfo::adjustStMan (const Record& dminfo, const String& dmType, Bool replaceMSM) { Record newdm; for (uInt j=0; jisStorageManager() && !(dmptr->canAddRow() || dmptr->isRegular())) || (replaceMSM && exType == "MemoryStMan")) { // A non-writable storage manager; use given storage manager instead. rec.define ("TYPE", dmType); rec.define ("NAME", exName); } delete dmptr; newdm.defineRecord (j, rec); } return newdm; } void DataManInfo::mergeInfo (Record& dminfo1, const Record& dminfo2) { // See for each new data manager what to do. for (uInt i2=0; i2 cols; if (dm.isDefined("COLUMNS")) { cols.reference (dm.asArrayString("COLUMNS")); } if (! cols.empty()) { // Iterate over all dm-s to find the ones containing columns of the new dm. for (uInt i=0; i cols2(dm2.asArrayString("COLUMNS")); if (! cols2.empty()) { std::vector colsnew; // Keep columns not equal to a column in the new dm. for (auto col2 : cols2) { if (std::find (cols.begin(), cols.end(), col2) == cols.end()) { colsnew.push_back (col2); } } if (i != dmindex) { // dm is not the new one, so update the COLUMNS in it (if changed). if (colsnew.size() != cols2.size()) { dm2.define ("COLUMNS", Vector(colsnew)); dminfo.defineRecord (i, dm2); } } else { // dm is the new one. So add its columns and redefine them. colsnew.insert (colsnew.end(), cols.begin(), cols.end()); dm.define ("COLUMNS", Vector(colsnew)); } } } } } } Record DataManInfo::finalizeMerge (const TableDesc& desc, const Record& dminfo) { // Make a map of the data managers in the dminfo record, so possible // specifications can be used. // Also make a map of column to dminfo index. std::map, uInt> dmMap; std::map colMap; for (uInt i=0; i cols(dm.asArrayString("COLUMNS")); for (auto col : cols) { colMap[col] = i; } } } } // Find out which columns share the same data manager by making a map // of data manager type/name to columns in the Table Description. std::map, std::vector> descMap; for (uInt i=0; isecond); if (dm.isDefined("TYPE")) { type = dm.asString("TYPE"); } if (dm.isDefined("NAME")) { name = dm.asString("NAME"); } } if (type.empty()) { type = "StandardStMan"; } // Add the column to the vector. descMap[std::make_pair(type, name)].push_back (cd.name()); } // Create a dminfo entry for each column set found above. // Use dm parameters if found. Record newdm; for (auto desc : descMap) { String type = desc.first.first; String name = desc.first.second; Record dm; // Try to find this type/name in the dminfo map to copy its specs. // Define the columns. auto iter = dmMap.find (std::make_pair(type, name)); if (iter != dmMap.end()) { dm = dminfo.subRecord(iter->second); } dm.define ("COLUMNS", Vector(desc.second)); // Use the first column name one for a dm entry without a name. if (name.empty()) { name = desc.second[0]; } // Ensure the name is unique. dm.define ("TYPE", type); dm.define ("NAME", uniqueName (newdm, name)); // Add the the overall dminfo record. newdm.defineRecord (newdm.size(), dm); } return newdm; } void DataManInfo::makeUniqueNames (Record& dminfo) { // Ensure that data manager names are unique by adding a suffix if needed. // First set empty names to the name of the first column. for (uInt i=0; i cols(dm.asArrayString("COLUMNS")); if (! cols.empty()) { name = cols[0]; } } dm.define ("NAME", name); } } // Now make the names unique if needed. // The first instance is kept as is, others get suffix _1, _2, etc. std::set firstNames; for (uInt i=0; i cols(subinfo.asArrayString("COLUMNS")); if (! cols.empty()) { String name = subinfo.asString(i); for (uInt j=0; j DataManInfo::removeDminfoColumns (Record& dminfo, const Vector& columns, const String& keepType) { Record newdm; // Find the given columns and remove them. // Keep track which columns are removed. Vector remCols(columns.size()); uInt ncols = 0; for (uInt j=0; j dmcols (rec.asArrayString("COLUMNS")); uInt ndmcol = dmcols.size(); const String& dmtype = rec.asString ("TYPE"); if (keepType.empty() || dmtype.substr(0,keepType.size()) != keepType) { // This dmtype does not need to be kept, so columns can be removed. for (uInt i=0; i 0) { if (ndmcol != dmcols.size()) { dmcols.resize (ndmcol, True); rec.define ("COLUMNS", dmcols); } newdm.defineRecord (j, rec); } } dminfo = newdm; remCols.resize (ncols, True); return remCols; } void DataManInfo::setTiledStMan (Record& dminfo, const Vector& columns, const String& dmType, const String& dmName, const IPosition& defaultTileShape) { // Remove the columns. Vector remCols (removeDminfoColumns (dminfo, columns, "Tiled")); // Add removed columns with a TiledStMan. if (remCols.size() > 0) { Record dm; dm.define("TYPE", dmType); dm.define("NAME", dmName); dm.define ("COLUMNS", remCols); Record spec; spec.define("DEFAULTTILESHAPE", defaultTileShape.asVector()); dm.defineRecord ("SPEC", spec); dminfo.defineRecord (dminfo.nfields(), dm); } } void DataManInfo::showDataManStats (const Table& tab, ostream& os) { Record dmInfo = tab.dataManagerInfo(); // Loop through all data managers. // Not all of them might have a name, so use the first column in // each of them to construct the Accessor object. for (uInt i=0; i #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations. class TableDesc; class Table; class Record; // // Class with static functions to manipulate a datamanager record. // // // // // //# Classes you should understand before using this one. //
      • Table // // // DataManInfo is a class to manipulate a datamanager info record and/or table // description. A datamanager info record tells how the columns are stored. // It is a Record containing the following fields. If omitted, a default is used. //
          //
        • TYPE: data manager type (default StandardStMan) //
        • NAME: unique data manager name //
        • COLUMNS: string vector containing columns stored with this data manager //
        • SPEC: subrecord containing data manager specific parameters //
        // Often an existing table description and datamanager info record are used to // construct a new table, but it might be necessary to change it somewhat. //
          //
        • Remove hypercolumn definitions from a table description. They are // not needed anymore and can be a burden. //
        • Replace non-writable storage managers (like LofarStMan) by a // writable one. //
        • Replace the deprecated TiledDataStMan by TiledShapeStMan. //
        • Merge two datamanager info records. //
        //
        //# //# class DataManInfo { public: // Remove hypercolumn definitions from the table description. static void removeHypercolumns (TableDesc& tabDesc); // Replace TiledDataStMan by TiledShapeStMan in the DataManagerInfo record. // Since TiledShapeStMan does not support ID columns, they are // adjusted as well in tabDesc and dminfo. static void adjustTSM (TableDesc& tabDesc, Record& dminfo); // Replace non-writable storage managers by the given storage manager // (usually StandardStMan or IncrementalStMan). // It is possible to specify the new data manager type to use. // This is needed for special storage managers like LofarStMan. // If replaceMSM is set, MemoryStMan is also replaced. static Record adjustStMan (const Record& dminfo, const String& dmType, Bool replaceMSM = True); // Ensure all data manager names in dminfo are unique by // adding a unique suffix as needed (using function uniqueName). // Empty names are set to the name of the first column (DM if no columns). static void makeUniqueNames (Record& dminfo); // Return a unique data manager name by testing if the name already // exist in of the the dm-s in the dminfo record. // If so, a suffix _i is added where i makes the name unique. // The excludeDM-th dm is excluded, so comparing to itself can be avoided. static String uniqueName (const Record& dminfo, const String& name, Int excludeDM=-1); // Merge the second DataManagerInfo record into the first one. // If the same column occurs in both records, the second one is used. // Columns having the same data manager name are combined in one data manager. // If the second one has no name, it is considered to be equal to the first // data manager of that type. static void mergeInfo (Record&, const Record&); // Finalize the merge by merging the dminfo record with the table description // to create the final dminfo record. // The final dminfo record gets all columns in the TableDesc object. // The given dminfo object is leading in determining a column's data manager. // If not present, the data manager type given in the TableDesc is used. // If empty, StandardStMan is used. static Record finalizeMerge (const TableDesc&, const Record& dminfo); // Adapt data manager names in dminfo if already used in the table. static void adaptNames (Record& dminfo, const Table&); // Set the data managers of the given column(s) to the given tiled storage // manager (normally TiledShapeStMan or TiledColumnStMan). // The columns are combined in a single storage manager, so the function // has to be called multiple times if, say, one per column is needed. // The columns already having a tiled storage manager are not changed. static void setTiledStMan (Record& dminfo, const Vector& columns, const String& dmType, const String& dmName, const IPosition& defaultTileShape); // Remove the given columns from the dminfo record and return a vector // containing the names of the columns actually removed. // The columns having a data manager matching keepType are not // removed. Matching means that the beginning of the data manager name // has to match, so "Tiled" matches all tiled storagemanagers. static Vector removeDminfoColumns (Record& dminfo, const Vector& columns, const String& keepType= String()); // Adjust the data manager types and groups and the // hypercolumn definitions to the actual data manager info. static void adjustDesc (TableDesc& tabDesc, const Record& dminfo); // Show the Table IO statistics. static void showDataManStats (const Table&, ostream&); private: // Merge the column info of data manager definitions. // It is used by mergeInfo to merge the new dm definitions into // the existing one defined in dminfo. It is called for each new // dm, whose name/type already exists as the dmindex-th record in dminfo. // It does two things: //
          //
        • Columns mentioned in newdm are removed from dm definitions in dminfo. //
        • Columns in the dmindex-th dminfo record are merged into newdm, // so mergeInfo can redefine that dm in the overall dminfo. //
        static void mergeColumns (Record& dminfo, uInt dmindex, Record& newdm); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/DataManager.cc000066400000000000000000000370311476623553700205620ustar00rootroot00000000000000//# DataManager.cc: Abstract base class for a data manager //# Copyright (C) 1994,1995,1996,1997,1998,1999,2001,2002,2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // for snprintf #ifdef HAVE_ADIOS2 #include #endif #ifdef HAVE_DYSCO #include #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN DataManager::DataManager() : nrcol_p (0), seqnr_p (0), asBigEndian_p (False), tsmOption_p (TSMOption::Buffer, 0, 0), clone_p (0) { table_p = new Table; } DataManager::~DataManager() { delete table_p; } String DataManager::dataManagerName() const { return String(); } void DataManager::dataManagerInfo (Record& info) const { info.define ("SEQNR", seqnr_p); info.defineRecord ("SPEC", dataManagerSpec()); } Record DataManager::dataManagerSpec() const { return Record(); } Record DataManager::getProperties() const { return Record(); } void DataManager::setProperties (const Record&) {} Bool DataManager::isStorageManager() const { return True; } void DataManager::create64 (rownr_t nrrow) { AlwaysAssert (nrrow <= std::numeric_limits::max(), AipsError); create (uInt(nrrow)); } rownr_t DataManager::open64 (rownr_t nrrow, AipsIO& ios) { return open1 (uInt(nrrow), ios); } rownr_t DataManager::resync64 (rownr_t nrrow) { AlwaysAssert (nrrow < std::numeric_limits::max(), AipsError); return resync1 (uInt(nrrow)); } uInt DataManager::open1 (uInt nrrow, AipsIO& ios) { open (nrrow, ios); return nrrow; } uInt DataManager::resync1 (uInt nrrow) { resync (nrrow); return nrrow; } void DataManager::create (uInt) { { throw DataManInvOper ("DataManager::create not implemented for " "data manager type " + dataManagerType()); } } void DataManager::open (uInt, AipsIO&) { { throw DataManInvOper ("DataManager::open not implemented for " "data manager type " + dataManagerType()); } } void DataManager::resync (uInt) { { throw DataManInvOper ("DataManager::resync not implemented for " "data manager type " + dataManagerType()); } } void DataManager::reopenRW() {} void DataManager::setMaximumCacheSize (uInt) {} void DataManager::showCacheStatistics (ostream&) const {} void DataManager::setTsmOption (const TSMOption& tsmOption) { AlwaysAssert (!multiFile_p, AipsError); tsmOption_p = tsmOption; } void DataManager::setMultiFile (const std::shared_ptr& mfile) { multiFile_p = mfile; // Only caching can be used with a MultiFile. if (multiFile_p) { tsmOption_p = TSMOption(TSMOption::Cache, 0, tsmOption_p.maxCacheSizeMB()); } } //# Create a column object for a scalar. //# Check its data type. DataManagerColumn* DataManager::createScalarColumn (const String& columnName, int dataType, const String& dataTypeId) { DataManagerColumn* colPtr = makeScalarColumn (columnName, dataType, dataTypeId); colPtr->setColumnName (columnName); checkDataType (colPtr, columnName, dataType, dataTypeId); colPtr->setIsFixedShape (True); nrcol_p++; return colPtr; } //# Creating a column object for an indirect array. //# Check its data type. DataManagerColumn* DataManager::createIndArrColumn (const String& columnName, int dataType, const String& dataTypeId) { DataManagerColumn* colPtr = makeIndArrColumn (columnName, dataType, dataTypeId); colPtr->setColumnName (columnName); checkDataType (colPtr, columnName, dataType, dataTypeId); nrcol_p++; return colPtr; } //# Creating a column object for a direct array. //# Check its data type. DataManagerColumn* DataManager::createDirArrColumn (const String& columnName, int dataType, const String& dataTypeId) { DataManagerColumn* colPtr = makeDirArrColumn (columnName, dataType, dataTypeId); colPtr->setColumnName (columnName); checkDataType (colPtr, columnName, dataType, dataTypeId); nrcol_p++; return colPtr; } void DataManager::checkDataType (const DataManagerColumn* colPtr, const String& columnName, int dataType, const String& dataTypeId) const { if (dataType != colPtr->dataType()) { throw (DataManInvDT ("Column " + columnName + " has data type " + String::toString(colPtr->dataTypeId()) + "; expected " + String::toString(dataTypeId))); } if (dataType == TpOther) { if (dataTypeId != colPtr->dataTypeId()) { throw (DataManInvDT ("Column " + columnName + " has data type ID " + colPtr->dataTypeId() + "; expected " + dataTypeId)); } } } void DataManager::throwDataTypeOther (const String& columnName, int dataType) const { if (dataType == TpOther) { throw (DataManInvOper ("Data manager " + dataManagerType() + " does not support data type TpOther" " (in column " + columnName + ")")); } } Bool DataManager::hasMultiFileSupport() const { return False; } Bool DataManager::canReallocateColumns() const { return False; } DataManagerColumn* DataManager::reallocateColumn (DataManagerColumn* column) { return column; } //# Compose the keyword name from the given name appended with the //# sequence number to make the name unique. String DataManager::keywordName (const String& keyword) const { char strc[8]; snprintf (strc, sizeof(strc), "_%i", seqnr_p); return keyword + strc; } //# Compose the file name from the table name followed by the //# sequence number to make the name unique. String DataManager::fileName() const { char strc[8]; snprintf (strc, sizeof(strc), ".f%i", seqnr_p); return table_p->tableName() + "/table" + strc; } ByteIO::OpenOption DataManager::fileOption() const { return PlainTable::toAipsIOFoption (table_p->tableOption()); } Bool DataManager::isRegular() const { return True; } void DataManager::linkToTable (Table& tab) { *table_p = tab; } //# Default prepare does nothing. void DataManager::prepare() {} Bool DataManager::canAddRow() const { return False; } Bool DataManager::canRemoveRow() const { return False; } Bool DataManager::canAddColumn() const { return False; } Bool DataManager::canRemoveColumn() const { return False; } Bool DataManager::canRenameColumn() const { return True; } void DataManager::addRow64 (rownr_t nrrow) { AlwaysAssert (nrrow < std::numeric_limits::max(), AipsError); addRow (uInt(nrrow)); } void DataManager::removeRow64 (rownr_t rownr) { AlwaysAssert (rownr < std::numeric_limits::max(), AipsError); removeRow (uInt(rownr)); } void DataManager::addRow (uInt) { throw DataManInvOper ("DataManager::addRow not allowed for " "data manager type " + dataManagerType()); } void DataManager::removeRow (uInt) { throw DataManInvOper ("DataManager::removeRow not allowed for " "data manager type " + dataManagerType()); } void DataManager::addColumn (DataManagerColumn*) { throw DataManInvOper ("DataManager::addColumn not allowed for " "data manager type " + dataManagerType()); } void DataManager::removeColumn (DataManagerColumn*) { throw DataManInvOper ("DataManager::removeColumn not allowed for " "data manager type " + dataManagerType()); } //# Initialize the static map of "constructors". // Use a recursive mutex, because loading from a shared library can cause // a nested lock. std::map DataManager::theirRegisterMap(initRegisterMap()); std::recursive_mutex DataManager::theirMutex; // Define the nr of rows fitting in an Int which is used by the data // managers. Test programs can set it to a lower value to test storing // 64-bit rownrs without the need of having very large tables. rownr_t DataManager::MAXROWNR32 (2147483647); //# Register a mapping. void DataManager::registerCtor (const String& type, DataManagerCtor func) { std::lock_guard lock(theirMutex); theirRegisterMap.insert (std::make_pair(type, func)); } //# Test if the data manager is registered. Bool DataManager::isRegistered (const String& type) { std::lock_guard lock(theirMutex); return theirRegisterMap.find(type) != theirRegisterMap.end(); } //# Get a data manager constructor. //# Return default function if the data manager is undefined //# after having tried to load it from a shared library. DataManagerCtor DataManager::getCtor (const String& type) { std::lock_guard lock(theirMutex); std::map::const_iterator iter = theirRegisterMap.find (type); if (iter != theirRegisterMap.end()) { return iter->second; } // Try to load the data manager from a dynamic library with that name // (in lowercase without possible template extension). // A < denotes a template name which is discarded. // A dot can be used to have a specific library name (so multiple // data managers can use the same library). String libname(type); libname.downcase(); string::size_type pos = libname.find_first_of (".<"); if (pos != string::npos) { libname = libname.substr (0, pos); } // Try to load and initialize the dynamic library. DynLib dl(libname, string("libcasa_"), CASACORE_STRINGIFY(SOVERSION), "register_"+libname, False); if (dl.getHandle()) { // See if registered now. iter = theirRegisterMap.find (type); if (iter != theirRegisterMap.end()) { return iter->second; } } return unknownDataManager; } //# The default "ctor" function for unknown data manager type names. DataManager* DataManager::unknownDataManager (const String& type, const Record&) { throw DataManUnknownCtor ("Data Manager class " + type + " is not registered\n" " Check (DY)LD_LIBRARY_PATH matches the" " libraries used during the build of " + type); return nullptr; } //# Register all mappings of the names of classes derived from //# DataManager to a static function calling the default constructor. //# The class name is the name as returned by the function dataManagerType. // No locking since private and only called by ctor of static member init. std::map DataManager::initRegisterMap() { std::map regMap; theirRegisterMap.insert (std::make_pair("StManAipsIO", StManAipsIO::makeObject)); theirRegisterMap.insert (std::make_pair("StandardStMan", StandardStMan::makeObject)); theirRegisterMap.insert (std::make_pair("IncrementalStMan", IncrementalStMan::makeObject)); theirRegisterMap.insert (std::make_pair("TiledDataStMan", TiledDataStMan::makeObject)); theirRegisterMap.insert (std::make_pair("TiledCellStMan", TiledCellStMan::makeObject)); theirRegisterMap.insert (std::make_pair("TiledColumnStMan", TiledColumnStMan::makeObject)); theirRegisterMap.insert (std::make_pair("TiledShapeStMan", TiledShapeStMan::makeObject)); theirRegisterMap.insert (std::make_pair("MemoryStMan", MemoryStMan::makeObject)); theirRegisterMap.insert (std::make_pair("AntennaPairStMan", AntennaPairStMan::makeObject)); theirRegisterMap.insert (std::make_pair("StokesIStMan", StokesIStMan::makeObject)); theirRegisterMap.insert (std::make_pair("UvwStMan", UvwStMan::makeObject)); #ifdef HAVE_ADIOS2 theirRegisterMap.insert (std::make_pair("Adios2StMan", Adios2StMan::makeObject)); #endif #ifdef HAVE_DYSCO theirRegisterMap.insert (std::make_pair("DyscoStMan", dyscostman::DyscoStMan::makeObject)); #endif theirRegisterMap.insert (std::make_pair(CompressFloat::className(), CompressFloat::makeObject)); theirRegisterMap.insert (std::make_pair(CompressComplex::className(), CompressComplex::makeObject)); theirRegisterMap.insert (std::make_pair(CompressComplexSD::className(), CompressComplexSD::makeObject)); theirRegisterMap.insert (std::make_pair(MappedArrayEngine::className(), MappedArrayEngine::makeObject)); theirRegisterMap.insert (std::make_pair(ForwardColumnEngine::className(), ForwardColumnEngine::makeObject)); theirRegisterMap.insert (std::make_pair(VirtualTaQLColumn::className(), VirtualTaQLColumn::makeObject)); theirRegisterMap.insert (std::make_pair(BitFlagsEngine::className(), BitFlagsEngine::makeObject)); theirRegisterMap.insert (std::make_pair(BitFlagsEngine::className(), BitFlagsEngine::makeObject)); theirRegisterMap.insert (std::make_pair(BitFlagsEngine::className(), BitFlagsEngine::makeObject)); return regMap; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/DataManager.h000066400000000000000000000543731476623553700204340ustar00rootroot00000000000000//# DataManager.h: Abstract base class for a data manager //# Copyright (C) 1994,1995,1996,1997,1998,1999,2001,2002,2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_DATAMANAGER_H #define TABLES_DATAMANAGER_H //# Includes #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class DataManager; class SetupNewTable; class Table; class MultiFileBase; class Record; class AipsIO; // // Define the type of the static construction function. // // // // // // Class names of data managers and pointers to their associated constructor // function are registered in a static map to be able to create the correct // data manager object from a string giving the type name of the data manager. // DataManagerCtor is the type of the constructor functions. // // typedef DataManager* (*DataManagerCtor) (const String& dataManagerType, const Record& spec); // // // Abstract base class for a data manager // // // // // //# Classes you should understand before using this one. // // // DataManager is the abstract base class for all kind of table data managers. //
        The DataManager class structure is shown in this // UML diagram. // There are currently 2 classes of data managers: //
          //
        • Storage managers handling the storage of data. These classes // have to be derived from DataManager. // StManAipsIO is an example of a storage manager. //
        • Virtual column engines handling the on-the-fly calculation // of data, which are not stored as such. The base class for // these is VirtualColumnEngine (which is derived from DataManager), // from which all virtual columns engine must be derived from. //
        // DataManager contains some common data and defines several virtual // functions, which usually have to be implemented in the derived classes. // It also contains some helper functions for the derived classes // (like fileName(). // // The actual handling of a column by the data manager is defined in // the abstract base class // DataManagerColumn. // Each data manager must // have an associated class (derived from DataManagerColumn) to // handle the columns. // // There is a protocol defined how a data manager is created and // initialized. For a new table it is: //
          //
        • // The user creates data managers and binds them to columns. For example: // // SetupNewTable newtab("name.data", Table::New); // set up new table // StManAipsIO stman; // define storage manager // newtab.bindColumn ("column1", stman); // bind column to st.man. // newtab.bindColumn ("column2", stman); // bind column to st.man. // Table tab(newtab); // actually create table // // When the given data manager object is used for the first time in a bind // function, a copy of the object is made using the clone function. // Thus in the above example column1 and column2 share the same data // manager; only at the first bind the stman object is cloned. // Columns not explicitly bound to a data manager get implicitly bound // to the default data manager (as defined in the column description) // by the Table constructor (as used in line 5). //
        • // After binding the unbound columns, the PlainTable constructor sets up // the data managers. For each column it asks the data manager to // construct a DataManagerColumn object (in fact, an object of a class // derived from DataManagerColumn). This is done by the functions // createScalarColumn, createIndArrColumn and createDirArrColumn. // For each data manager the create function is called. This allows // them to initialize themselves and/or to call an initialization // function in their column objects. // This is, for instance, used by the storage managers to create files. // Thereafter the prepare function is called to allow the data managers // to do further initialization possibly requiring information from // other columns. //
        • // When the table gets written (by the PlainTable destructor), // the flush function is called for each data manager. This allows // the data manager or their column objects to write or flush their data. // The table system takes care of storing the information required // to reconstruct the data managers. It uses the function dataManagerType // to store the (unique) type name of the data manager class. //
        • // Finally each data manager object gets deleted. Their destructors // must delete their column objects (if any and if needed). //
        // For an existing table the procedure is slightly different: //
          //
        • // The statement //
          Table tab("name.data"); // will create a table object for an existing table. This has the effect // that the given table file will be read to reconstruct the Table object // and the data managers. //
        • // The stored data manager class names are used to reconstruct // the data managers. This uses the static registration map, which // maps the class name to a static class constructor function (usually // called makeObject). This requires that the type name and constructor // for each possible data manager are registered before the table // is opened. The DataManager function registerMainCtor (implemented // in DataManager.cc) is called before a table is opened, so registration // of data managers should, in principle, be done there. //
          However, for unknown data managers it is tried to load a shared // library whose name is the lowercase version of the data manager without a // possible template argument (e.g. bitflagsengine for // data manager BitFlagsEngine). // It can be preceeded by lib or libcasa_ and followed by .so or .dylib. // The shared library has to have a function with a name like // register_bitflagsengine that must register the data manager(s). // The function must be declared as extern "C", otherwise its // name gets mangled. //
        • // Each table column is bound to the correct data manager. The sequence // number stored in the table file is used for that purpose. //
        • // The DataManager createXXXColumn functions are called for each table // column to let the data manager construct a data manager column object. //
        • // For each data manager the open function is called to allow it and // its column objects to read back the information stored in the // flush function. // Thereafter the prepare function is called for each data manager // to allow it to initialize some variables. // The reason that open and prepare are separated is that in order to // initialize variables it may be required to use other columns. // So it may be needed that all columns are read back before they // get initialized. //
        • // Similar to a new table the flush functions gets called when the // table gets written. Destruction is also the same as sketched // for new tables. //
        //
        // // An abstract base class is needed to support data managers and // virtual column engines in the table system in a transparant way. // // //# A List of bugs, limitations, extensions or planned refinements. //
      • Handle unregistered data managers in a better way. // Instead of throwing an exception a subprocess could be // started which represents the data manager. // class DataManager { friend class SetupNewTable; friend class ColumnSet; public: // Default constructor. DataManager(); virtual ~DataManager(); // The copy constructor cannot be used for this base class. // The clone function should be used instead. DataManager (const DataManager&) = delete; // Assignment cannot be used for this base class. DataManager& operator= (const DataManager&) = delete; // Make a clone of the derived object. virtual DataManager* clone() const = 0; // Return the name of the data manager. This is the name of this // instantiation of the data manager, thus not its type name. // By default it returns an empty string. virtual String dataManagerName() const; // Return the type name of the data manager (in fact its class name). // It has to be a unique name, thus if the class is templated // the template parameter has to be part of the name. // This is used by the open/flush mechanism to be able to reconstruct // the correct data manager. virtual String dataManagerType() const = 0; // Add SEQNR and SPEC (the DataManagerSpec subrecord) to the info. void dataManagerInfo (Record& info) const; // Return a record containing data manager specifications. // The default implementation returns an empty record. virtual Record dataManagerSpec() const; // Get data manager properties that can be modified. // It is a subset of the data manager specification. // The default implementation returns an empty record. virtual Record getProperties() const; // Modify data manager properties given in record fields. Only the // properties as returned by getProperties are used, others are ignored. // The default implementation does nothing. virtual void setProperties (const Record& spec); // Is the data manager a storage manager? // The default is yes. virtual Bool isStorageManager() const; // Tell if the data manager wants to reallocate the data manager // column objects. // This is used by the tiling storage manager. // By default it returns False. virtual Bool canReallocateColumns() const; // Reallocate the column object if it is part of this data manager. // It returns a pointer to the new column object. // This function is used by the tiling storage manager. // By default it does nothing and returns the input pointer. virtual DataManagerColumn* reallocateColumn (DataManagerColumn* column); // Get the (unique) sequence nr of this data manager. uInt sequenceNr() const { return seqnr_p; } // Get the nr of columns in this data manager (can be zero). uInt ncolumn() const { return nrcol_p; } // Have the data to be stored in big or little endian canonical format? Bool asBigEndian() const { return asBigEndian_p; } // Get the TSM option. const TSMOption& tsmOption() const { return tsmOption_p; } // Get the MultiFile pointer (can be 0). std::shared_ptr multiFile() { return multiFile_p; } // Compose a keyword name from the given keyword appended with the // sequence number (e.g. key_0). // This makes the keyword name unique if multiple data managers // are used with the same type. String keywordName (const String& keyword) const; // Compose a unique filename from the table name and sequence number. String fileName() const; // Get the AipsIO option of the underlying file. ByteIO::OpenOption fileOption() const; // Is this a regular storage manager? // It is regular if it allows addition of rows and writing data in them. //
        The default implementation returns True. virtual Bool isRegular() const; // Get the table this object is associated with. Table& table() const { return *table_p; } // Reopen the data manager for read/write access. // By default it is assumed that a reopen for read/write does // not have to do anything. virtual void reopenRW(); // Does the data manager allow to add rows? (default no) virtual Bool canAddRow() const; // Does the data manager allow to delete rows? (default no) virtual Bool canRemoveRow() const; // Does the data manager allow to add columns? (default no) virtual Bool canAddColumn() const; // Does the data manager allow to delete columns? (default no) virtual Bool canRemoveColumn() const; // Does the data manager allow to rename columns? (default yes) virtual Bool canRenameColumn() const; // Set the maximum cache size (in bytes) to be used by a storage manager. // The default implementation does nothing. virtual void setMaximumCacheSize (uInt nMiB); // Show the data manager's IO statistics. By default it does nothing. virtual void showCacheStatistics (std::ostream&) const; // Create a column in the data manager on behalf of a table column. // It calls makeXColumn and checks the data type. // // Create a scalar column. // The dataTypeId argument is gives the id (i.e. name) // of the data type of the column. It is only used for virtual // columns of a non-standard data type to be able to check if // the correctness of the column data type. //
        Storage managers only handle standard data types and // can readily ignore this argument. DataManagerColumn* createScalarColumn (const String& columnName, int dataType, const String& dataTypeId); // Create a direct array column. DataManagerColumn* createDirArrColumn (const String& columnName, int dataType, const String& dataTypeId); // Create an indirect array column. DataManagerColumn* createIndArrColumn (const String& columnName, int dataType, const String& dataTypeId); //
        // The data manager will be deleted (because all its columns are // requested to be deleted). // So clean up the things needed (e.g. delete files). virtual void deleteManager() = 0; protected: // Decrement number of columns (in case a column is deleted). void decrementNcolumn() { nrcol_p--; } // Tell the data manager if big or little endian format is needed. void setEndian (Bool bigEndian) { asBigEndian_p = bigEndian; } // Tell the data manager which TSM option to use. void setTsmOption (const TSMOption& tsmOption); // Tell the data manager that MultiFile can be used. // Because MultiFile cannot be used with mmapped files, it sets // the TSMOption accordingly. void setMultiFile (const std::shared_ptr& mfile); // Does the data manager support use of MultiFile? // A derived class has to return True if it can use the MultiFile. // The default implementation returns False. virtual Bool hasMultiFileSupport() const; // Throw an exception in case data type is TpOther, because the // storage managers (and maybe other data managers) do not support // such columns. void throwDataTypeOther (const String& columnName, int dataType) const; private: uInt nrcol_p; //# #columns in this st.man. uInt seqnr_p; //# Unique nr of this st.man. in a Table Bool asBigEndian_p; //# store data in big or little endian TSMOption tsmOption_p; std::shared_ptr multiFile_p; //# Possible MultiFile to use Table* table_p; //# Table this data manager belongs to mutable DataManager* clone_p; //# Pointer to clone (used by SetupNewTab) // Create a column in the data manager on behalf of a table column. //# Should be private, but has to be public because friend //# declaration gave internal CFront error. // // Create a scalar column. virtual DataManagerColumn* makeScalarColumn (const String& columnName, int dataType, const String& dataTypeId) = 0; // Create a direct array column. virtual DataManagerColumn* makeDirArrColumn (const String& columnName, int dataType, const String& dataTypeId) = 0; // Create an indirect array column. virtual DataManagerColumn* makeIndArrColumn (const String& columnName, int dataType, const String& dataTypeId) = 0; // // Check if the data type of the created data manager column is correct. void checkDataType (const DataManagerColumn* colPtr, const String& columnName, int dataType, const String& dataTypeId) const; // Add rows to all columns. //
        The default implementation calls the uInt version. virtual void addRow64 (rownr_t nrrow); // Delete a row from all columns. //
        The default implementation calls the uInt version. virtual void removeRow64 (rownr_t rownr); // Add a column. // The default implementation throws a "not possible" exception. virtual void addColumn (DataManagerColumn*); // Delete a column. // The default implementation throws a "not possible" exception. virtual void removeColumn (DataManagerColumn*); // Set the sequence number of this data manager. void setSeqnr (uInt nr) { seqnr_p = nr; } // Link the data manager to the Table object. void linkToTable (Table& tab); // Flush and optionally fsync the data. // The AipsIO stream represents the main table file and can be // used by virtual column engines to store SMALL amounts of data. // It returns a True status if it had to flush (i.e. if data have changed). virtual Bool flush (AipsIO& ios, Bool fsync) = 0; // Let the data manager initialize itself for a new table. //
        The default implementation calls the uInt version. virtual void create64 (rownr_t nrrow); // Let the data manager initialize itself for an existing table. // The AipsIO stream represents the main table file and must be // used by virtual column engines to retrieve the data stored // in the flush function. //
        The data manager returns 0 or the nr of rows it thinks there are. // This is particularly useful for data managers like LofarStMan whose // data are written outside the table system, thus for which no rows // have been added. //
        The default implementation calls the uInt version of open and open1. virtual rownr_t open64 (rownr_t nrrow, AipsIO& ios); // Resync the data by rereading cached data from the file. // This is called when a lock is acquired on the file and it appears // that data in this data manager has been changed by another process. //
        The data manager returns 0 or the number of rows it thinks there are. // This is particularly useful for data managers like LofarStMan whose // data are written outside the table system, thus for which no rows // have been added. //
        The default implementation calls the uInt version of resync and // resync1. virtual rownr_t resync64 (rownr_t nrrow); // Let the data manager initialize itself further. // Prepare is called after create/open has been called for all // columns. In this way one can be sure that referenced columns // are read back and partly initialized. // The default implementation does nothing. virtual void prepare(); // Backward compatibility function using uInt instead of rownr_t. // The default implementations throw an exception. // virtual void addRow (uInt nrrow); virtual void removeRow (uInt rownr); virtual void create (uInt nrrow); virtual void open (uInt nrrow, AipsIO& ios); virtual uInt open1 (uInt nrrow, AipsIO& ios); virtual void resync (uInt nrrow); virtual uInt resync1 (uInt nrrow); // // Declare the mapping of the data manager type name to a static // "makeObject" function. static std::map theirRegisterMap; static std::recursive_mutex theirMutex; public: // Has the object already been cloned? DataManager* getClone() const { return clone_p; } // Set the pointer to the clone. void setClone (DataManager* clone) const { clone_p = clone; } // Register a mapping of a data manager type to its static construction // function. It is fully thread-safe. static void registerCtor (const String& type, DataManagerCtor func); // Get the "constructor" of a data manager (thread-safe). static DataManagerCtor getCtor (const String& dataManagerType); // Test if a data manager is registered (thread-safe). static Bool isRegistered (const String& dataManagerType); // Serve as default function for theirRegisterMap, which catches all // unknown data manager types. // //
      • TableUnknownDataManager // static DataManager* unknownDataManager (const String& dataManagerType, const Record& spec); // Define the highest row number that can be represented as signed 32-bit. // In principle it is the maximum uInt number, but for test purposes it // can be reset (to a lower number). static rownr_t MAXROWNR32; //# set to 2147483647 private: // Register the main data managers. static std::map initRegisterMap(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/DataManagerColumn.cc000066400000000000000000000515061476623553700217430ustar00rootroot00000000000000//# DataManagerColumn.cc: Abstract base class for a data manager column //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN DataManagerColumn::~DataManagerColumn() {} void DataManagerColumn::setMaxLength (uInt) {} void DataManagerColumn::setShapeColumn (const IPosition&) { throw DataManInvOper ("setShapeColumn only allowed for FixedShape arrays" " in column " + columnName()); } void DataManagerColumn::setShape (rownr_t, const IPosition&) { throw DataManInvOper("setShape only allowed for non-FixedShape arrays" " in column " + columnName()); } void DataManagerColumn::setShapeTiled (rownr_t rownr, const IPosition& shape, const IPosition&) { setShape (rownr, shape); } // By default the shape is defined (for scalars). Bool DataManagerColumn::isShapeDefined (rownr_t) { return True; } // The default implementation of ndim is to use the shape. uInt DataManagerColumn::ndim (rownr_t rownr) { return shape(rownr).nelements(); } // The shape of the array in the given row. IPosition DataManagerColumn::shape (rownr_t) { return IPosition(0); } // The tile shape of the array in the given row. IPosition DataManagerColumn::tileShape (rownr_t) { return IPosition(0); } Bool DataManagerColumn::canChangeShape() const { return False; } String DataManagerColumn::dataTypeId() const { return String(); } Bool DataManagerColumn::isWritable() const { return True; } void DataManagerColumn::throwGet() const { throw (DataManInvOper ("DataManagerColumn::get not allowed in column " + columnName())); } void DataManagerColumn::throwPut() const { throw (DataManInvOper ("DataManagerColumn::put not allowed in column " + columnName())); } void DataManagerColumn::getBool (rownr_t, Bool*) { throwGet(); } void DataManagerColumn::getuChar (rownr_t, uChar*) { throwGet(); } void DataManagerColumn::getShort (rownr_t, Short*) { throwGet(); } void DataManagerColumn::getuShort (rownr_t, uShort*) { throwGet(); } void DataManagerColumn::getInt (rownr_t, Int*) { throwGet(); } void DataManagerColumn::getuInt (rownr_t, uInt*) { throwGet(); } void DataManagerColumn::getInt64 (rownr_t, Int64*) { throwGet(); } void DataManagerColumn::getfloat (rownr_t, float*) { throwGet(); } void DataManagerColumn::getdouble (rownr_t, double*) { throwGet(); } void DataManagerColumn::getComplex (rownr_t, Complex*) { throwGet(); } void DataManagerColumn::getDComplex (rownr_t, DComplex*) { throwGet(); } void DataManagerColumn::getString (rownr_t, String*) { throwGet(); } void DataManagerColumn::putBool (rownr_t, const Bool*) { throwPut(); } void DataManagerColumn::putuChar (rownr_t, const uChar*) { throwPut(); } void DataManagerColumn::putShort (rownr_t, const Short*) { throwPut(); } void DataManagerColumn::putuShort (rownr_t, const uShort*) { throwPut(); } void DataManagerColumn::putInt (rownr_t, const Int*) { throwPut(); } void DataManagerColumn::putuInt (rownr_t, const uInt*) { throwPut(); } void DataManagerColumn::putInt64 (rownr_t, const Int64*) { throwPut(); } void DataManagerColumn::putfloat (rownr_t, const float*) { throwPut(); } void DataManagerColumn::putdouble (rownr_t, const double*) { throwPut(); } void DataManagerColumn::putComplex (rownr_t, const Complex*) { throwPut(); } void DataManagerColumn::putDComplex (rownr_t, const DComplex*) { throwPut(); } void DataManagerColumn::putString (rownr_t, const String*) { throwPut(); } void DataManagerColumn::getOther (rownr_t, void*) { throw (DataManInvOper ("DataManagerColumn::getOther not allowed" " in column " + columnName())); } void DataManagerColumn::putOther (rownr_t, const void*) { throw (DataManInvOper ("DataManagerColumn::putOther not allowed" " in column " + columnName())); } // Define a macro to get or put a scalar column. // It gets the value for row i which might fill the ColumnCache. // If the cache gets filled, use it to get next values in a faster way. #define DATAMANAGERCOLUMN_GETCOL(T) \ { \ Vector& vec = static_cast&>(arr); \ rownr_t nr = vec.nelements(); \ rownr_t rownr = 0; \ while (rownr < nr) { \ aips_name2(get,T) (rownr, &vec[rownr]); \ rownr++; \ if (rownr <= colCache_p.end() && rownr > colCache_p.start()) { \ rownr_t last = std::min(nr-1, colCache_p.end()); \ rownr_t inx = (rownr - colCache_p.start()) * colCache_p.incr(); \ const T* cptr = static_cast(colCache_p.dataPtr()) + inx; \ for (rownr_t j=rownr; j<=last; ++j) { \ vec[rownr++] = *cptr; \ cptr += colCache_p.incr(); \ } \ } \ } \ } #define DATAMANAGERCOLUMN_PUTCOL(T) \ { \ const Vector& vec = static_cast&>(arr); \ rownr_t nr = vec.nelements(); \ for (rownr_t rownr=0; rownr& vec = static_cast&>(arr); \ if (rownrs.isSliced()) { \ RefRowsSliceIter iter(rownrs); \ rownr_t i=0; \ while (! iter.pastEnd()) { \ rownr_t rownr = iter.sliceStart(); \ rownr_t end = iter.sliceEnd(); \ rownr_t incr = iter.sliceIncr(); \ while (rownr <= end) { \ if (rownr < colCache_p.start() || rownr > colCache_p.end()) { \ aips_name2(get,T) (rownr, &(vec[i])); \ i++; \ rownr += incr; \ } else { \ rownr_t inx = (rownr - colCache_p.start()) * colCache_p.incr(); \ const T* cptr = static_cast(colCache_p.dataPtr()) + inx; \ rownr_t endrow = std::min (end, colCache_p.end()); \ while (rownr <= endrow) { \ vec[i++] = *cptr; \ rownr += incr; \ cptr += incr * colCache_p.incr(); \ } \ } \ } \ iter++; \ } \ } else { \ const Vector& rowvec = rownrs.rowVector(); \ rownr_t nr = rowvec.nelements(); \ if (nr > 0) { \ Bool delR; \ const rownr_t* rows = rowvec.getStorage (delR); \ const T* cptr = static_cast(colCache_p.dataPtr()); \ rownr_t strow = colCache_p.start(); \ rownr_t endrow = colCache_p.end(); \ for (rownr_t i=0; i= strow && rownr <= endrow) { \ vec[i] = cptr[(rownr-strow)*colCache_p.incr()]; \ } else { \ aips_name2(get,T) (rownr, &(vec[i])); \ cptr = static_cast(colCache_p.dataPtr()); \ strow = colCache_p.start(); \ endrow = colCache_p.end(); \ } \ } \ rowvec.freeStorage (rows, delR); \ } \ } \ } #define DATAMANAGERCOLUMN_PUTCELLS(T) \ { \ const Vector& vec = static_cast&>(arr); \ RefRowsSliceIter iter(rownrs); \ rownr_t i=0; \ while (! iter.pastEnd()) { \ rownr_t rownr = iter.sliceStart(); \ rownr_t end = iter.sliceEnd(); \ rownr_t incr = iter.sliceIncr(); \ while (rownr <= end) { \ aips_name2(put,T) (rownr, &(vec[i])); \ i++; \ rownr += incr; \ } \ iter++; \ } \ } void DataManagerColumn::getScalarColumnV (ArrayBase& arr) { getScalarColumnBase (arr); } void DataManagerColumn::putScalarColumnV (const ArrayBase& arr) { putScalarColumnBase (arr); } void DataManagerColumn::getScalarColumnCellsV (const RefRows& rows, ArrayBase& arr) { getScalarColumnCellsBase (rows, arr); } void DataManagerColumn::putScalarColumnCellsV (const RefRows& rows, const ArrayBase& arr) { putScalarColumnCellsBase (rows, arr); } void DataManagerColumn::getArrayV (rownr_t, ArrayBase&) { throw DataManError("getArrayV not implemented" " for column " + columnName()); } void DataManagerColumn::putArrayV (rownr_t, const ArrayBase&) { throw DataManError("putArrayV not implemented" " for column " + columnName()); } void DataManagerColumn::getArrayColumnV (ArrayBase& arr) { getArrayColumnBase (arr); } void DataManagerColumn::putArrayColumnV (const ArrayBase& arr) { putArrayColumnBase (arr); } void DataManagerColumn::getArrayColumnCellsV (const RefRows& rows, ArrayBase& arr) { getArrayColumnCellsBase (rows, arr); } void DataManagerColumn::putArrayColumnCellsV (const RefRows& rows, const ArrayBase& arr) { putArrayColumnCellsBase (rows, arr); } void DataManagerColumn::getSliceV (rownr_t rownr, const Slicer& slicer, ArrayBase& arr) { getSliceBase (rownr, slicer, arr); } void DataManagerColumn::putSliceV (rownr_t rownr, const Slicer& slicer, const ArrayBase& arr) { putSliceBase (rownr, slicer, arr); } void DataManagerColumn::getColumnSliceV (const Slicer& slicer, ArrayBase& arr) { getColumnSliceBase (slicer, arr); } void DataManagerColumn::putColumnSliceV (const Slicer& slicer, const ArrayBase& arr) { putColumnSliceBase (slicer, arr); } void DataManagerColumn::getColumnSliceCellsV (const RefRows& rows, const Slicer& slicer, ArrayBase& arr) { getColumnSliceCellsBase (rows, slicer, arr); } void DataManagerColumn::putColumnSliceCellsV (const RefRows& rows, const Slicer& slicer, const ArrayBase& arr) { putColumnSliceCellsBase (rows, slicer, arr); } void DataManagerColumn::getScalarColumnBase (ArrayBase& arr) { switch (dataType()) { case TpBool: DATAMANAGERCOLUMN_GETCOL(Bool) break; case TpUChar: DATAMANAGERCOLUMN_GETCOL(uChar) break; case TpShort: DATAMANAGERCOLUMN_GETCOL(Short) break; case TpUShort: DATAMANAGERCOLUMN_GETCOL(uShort) break; case TpInt: DATAMANAGERCOLUMN_GETCOL(Int) break; case TpUInt: DATAMANAGERCOLUMN_GETCOL(uInt) break; case TpInt64: DATAMANAGERCOLUMN_GETCOL(Int64) break; case TpFloat: DATAMANAGERCOLUMN_GETCOL(float) break; case TpDouble: DATAMANAGERCOLUMN_GETCOL(double) break; case TpComplex: DATAMANAGERCOLUMN_GETCOL(Complex) break; case TpDComplex: DATAMANAGERCOLUMN_GETCOL(DComplex) break; case TpString: DATAMANAGERCOLUMN_GETCOL(String) break; default: throw (DataManInvOper("DataManagerColumn::getScalarColumnV not allowed" " for column " + columnName())); } } void DataManagerColumn::putScalarColumnBase (const ArrayBase& arr) { switch (dataType()) { case TpBool: DATAMANAGERCOLUMN_PUTCOL(Bool) break; case TpUChar: DATAMANAGERCOLUMN_PUTCOL(uChar) break; case TpShort: DATAMANAGERCOLUMN_PUTCOL(Short) break; case TpUShort: DATAMANAGERCOLUMN_PUTCOL(uShort) break; case TpInt: DATAMANAGERCOLUMN_PUTCOL(Int) break; case TpUInt: DATAMANAGERCOLUMN_PUTCOL(uInt) break; case TpInt64: DATAMANAGERCOLUMN_PUTCOL(Int64) break; case TpFloat: DATAMANAGERCOLUMN_PUTCOL(float) break; case TpDouble: DATAMANAGERCOLUMN_PUTCOL(double) break; case TpComplex: DATAMANAGERCOLUMN_PUTCOL(Complex) break; case TpDComplex: DATAMANAGERCOLUMN_PUTCOL(DComplex) break; case TpString: DATAMANAGERCOLUMN_PUTCOL(String) break; default: throw (DataManInvOper("DataManagerColumn::putScalarColumnV not allowed" " in column " + columnName())); } } void DataManagerColumn::getScalarColumnCellsBase (const RefRows& rownrs, ArrayBase& arr) { switch (dataType()) { case TpBool: DATAMANAGERCOLUMN_GETCELLS(Bool) break; case TpUChar: DATAMANAGERCOLUMN_GETCELLS(uChar) break; case TpShort: DATAMANAGERCOLUMN_GETCELLS(Short) break; case TpUShort: DATAMANAGERCOLUMN_GETCELLS(uShort) break; case TpInt: DATAMANAGERCOLUMN_GETCELLS(Int) break; case TpUInt: DATAMANAGERCOLUMN_GETCELLS(uInt) break; case TpInt64: DATAMANAGERCOLUMN_GETCELLS(Int64) break; case TpFloat: DATAMANAGERCOLUMN_GETCELLS(float) break; case TpDouble: DATAMANAGERCOLUMN_GETCELLS(double) break; case TpComplex: DATAMANAGERCOLUMN_GETCELLS(Complex) break; case TpDComplex: DATAMANAGERCOLUMN_GETCELLS(DComplex) break; case TpString: DATAMANAGERCOLUMN_GETCELLS(String) break; default: throw (DataManInvOper("DataManagerColumn::getScalarColumnCellsV not allowed" " in column " + columnName())); } } void DataManagerColumn::putScalarColumnCellsBase (const RefRows& rownrs, const ArrayBase& arr) { switch (dataType()) { case TpBool: DATAMANAGERCOLUMN_PUTCELLS(Bool) break; case TpUChar: DATAMANAGERCOLUMN_PUTCELLS(uChar) break; case TpShort: DATAMANAGERCOLUMN_PUTCELLS(Short) break; case TpUShort: DATAMANAGERCOLUMN_PUTCELLS(uShort) break; case TpInt: DATAMANAGERCOLUMN_PUTCELLS(Int) break; case TpUInt: DATAMANAGERCOLUMN_PUTCELLS(uInt) break; case TpInt64: DATAMANAGERCOLUMN_PUTCELLS(Int64) break; case TpFloat: DATAMANAGERCOLUMN_PUTCELLS(float) break; case TpDouble: DATAMANAGERCOLUMN_PUTCELLS(double) break; case TpComplex: DATAMANAGERCOLUMN_PUTCELLS(Complex) break; case TpDComplex: DATAMANAGERCOLUMN_PUTCELLS(DComplex) break; case TpString: DATAMANAGERCOLUMN_PUTCELLS(String) break; default: throw (DataManInvOper("DataManagerColumn::putScalarColumnCellsV not allowed" " in column " + columnName())); } } void DataManagerColumn::getArrayColumnBase (ArrayBase& arr) { const IPosition& shp = arr.shape(); rownr_t nr = shp[shp.size() - 1]; std::unique_ptr iter = arr.makeIterator (shp.size()-1); for (rownr_t row=0; rowgetArray()); iter->next(); } } void DataManagerColumn::putArrayColumnBase (const ArrayBase& arr) { const IPosition& shp = arr.shape(); rownr_t nr = shp[shp.size() - 1]; std::unique_ptr iter = arr.makeIterator (shp.size()-1); for (rownr_t row=0; rowgetArray()); iter->next(); } } void DataManagerColumn::getArrayColumnCellsBase (const RefRows& rows, ArrayBase& arr) { std::unique_ptr iter = arr.makeIterator (arr.ndim()-1); RefRowsSliceIter rowsIter(rows); while (! rowsIter.pastEnd()) { for (rownr_t row=rowsIter.sliceStart(); row<=rowsIter.sliceEnd(); row+=rowsIter.sliceIncr()) { DebugAssert (! iter->pastEnd(), AipsError); getArrayV (row, iter->getArray()); iter->next(); } rowsIter.next(); } DebugAssert (iter->pastEnd(), AipsError); } void DataManagerColumn::putArrayColumnCellsBase (const RefRows& rows, const ArrayBase& arr) { std::unique_ptr iter = arr.makeIterator (arr.ndim()-1); RefRowsSliceIter rowsIter(rows); while (! rowsIter.pastEnd()) { for (rownr_t row=rowsIter.sliceStart(); row<=rowsIter.sliceEnd(); row+=rowsIter.sliceIncr()) { DebugAssert (! iter->pastEnd(), AipsError); putArrayV (row, iter->getArray()); iter->next(); } rowsIter.next(); } DebugAssert (iter->pastEnd(), AipsError); } void DataManagerColumn::getSliceArr (rownr_t row, const Slicer& section, std::shared_ptr& fullArr, ArrayBase& arr) { IPosition shp = shape(row); if (shp.isEqual (arr.shape())) { getArrayV (row, arr); } else { if (! shp.isEqual (fullArr->shape())) { fullArr->resize (shp); } getArrayV (row, *fullArr); arr.assignBase (*(fullArr->getSection (section))); } } void DataManagerColumn::putSliceArr (rownr_t row, const Slicer& section, std::shared_ptr& fullArr, const ArrayBase& arr) { IPosition shp = shape(row); if (shp.isEqual (arr.shape())) { putArrayV (row, arr); } else { if (! shp.isEqual (fullArr->shape())) { fullArr->resize (shp); } getArrayV (row, *fullArr); (fullArr->getSection(section))->assignBase (arr); putArrayV (row, *fullArr); } } void DataManagerColumn::getSliceBase (rownr_t row, const Slicer& section, ArrayBase& arr) { std::shared_ptr fullArr(arr.makeArray()); getSliceArr (row, section, fullArr, arr); } void DataManagerColumn::putSliceBase (rownr_t row, const Slicer& section, const ArrayBase& arr) { std::shared_ptr fullArr(arr.makeArray()); putSliceArr (row, section, fullArr, arr); } void DataManagerColumn::getColumnSliceBase (const Slicer& section, ArrayBase& arr) { const IPosition& shp = arr.shape(); rownr_t nr = shp[shp.size() - 1]; std::shared_ptr fullArr(arr.makeArray()); std::unique_ptr iter = arr.makeIterator (shp.size()-1); for (rownr_t row=0; rowgetArray()); iter->next(); } } void DataManagerColumn::putColumnSliceBase (const Slicer& section, const ArrayBase& arr) { const IPosition& shp = arr.shape(); rownr_t nr = shp[shp.size() - 1]; std::shared_ptr fullArr(arr.makeArray()); std::unique_ptr iter = arr.makeIterator (shp.size()-1); for (rownr_t row=0; rowgetArray()); iter->next(); } } void DataManagerColumn::getColumnSliceCellsBase (const RefRows& rows, const Slicer& section, ArrayBase& arr) { std::shared_ptr fullArr(arr.makeArray()); std::unique_ptr iter = arr.makeIterator (arr.ndim()-1); RefRowsSliceIter rowsIter(rows); while (! rowsIter.pastEnd()) { for (rownr_t row=rowsIter.sliceStart(); row<=rowsIter.sliceEnd(); row+=rowsIter.sliceIncr()) { DebugAssert (! iter->pastEnd(), AipsError); getSliceArr (row, section, fullArr, iter->getArray()); iter->next(); } rowsIter.next(); } DebugAssert (iter->pastEnd(), AipsError); } void DataManagerColumn::putColumnSliceCellsBase (const RefRows& rows, const Slicer& section, const ArrayBase& arr) { std::shared_ptr fullArr(arr.makeArray()); std::unique_ptr iter = arr.makeIterator (arr.ndim()-1); RefRowsSliceIter rowsIter(rows); while (! rowsIter.pastEnd()) { for (rownr_t row=rowsIter.sliceStart(); row<=rowsIter.sliceEnd(); row+=rowsIter.sliceIncr()) { DebugAssert (! iter->pastEnd(), AipsError); putSliceArr (row, section, fullArr, iter->getArray()); iter->next(); } rowsIter.next(); } DebugAssert (iter->pastEnd(), AipsError); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/DataManagerColumn.h000066400000000000000000000547551476623553700216160ustar00rootroot00000000000000//# DataManagerColumn.h: Abstract base class for a data manager column //# Copyright (C) 1994,1995,1996,1997,1998,1999,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_DATAMANAGERCOLUMN_H #define TABLES_DATAMANAGERCOLUMN_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class IPosition; class Slicer; class RefRows; class ArrayBase; // // Abstract base class for a column in a data manager // // // // // //# Classes you should understand before using this one. //
      • DataManager // // // DataManagerColumn handles a column for a data manager. // // // DataManagerColumn is the abstract base class to handle a column in // a data manager. Each data manager class must have one or more associated // classes derived from DataManagerColumn to handle the columns. // For example, storage manager StManAipsIO has columns classes // StManColumnAipsIO, StManColumnArrayAipsIO and StManColumnIndArrayAipsIO // to handle scalars, direct arrays and indirect arrays, resp.. // However, using multiple inheritance it is possible that the derived // DataManager and DataManagerColumn classes are the same. This is used // in class ScaledArrayEngine which represents both the data manager // and its column class. It can do that, because the virtual column engine // ScaledArrayEngine // can handle only one column. // // In the synopsis of class DataManager it is described how the (derived) // DataManagerColumn objects gets created and deleted. // // DataManagerColumn defines various virtual functions to get or put (slices) // of data in a column. These functions are called by the table column // classes ScalarColumnData and ArrayColumnData. // It does not define functions create, open, flush and prepare like // those defined in DataManager. It is left to the derived classes to // define those as needed and to interact properly with their // data manager object. // // The get/put interface has changed per 1-Sep-2017. // The old interface for ArrayColumn::getArray worked as follows: //
          //
        1. ArrayColumn calls (virtual) BaseColumn::get passing the array as a void*. // void* is used to support the derived RefColumn class which is not templated. //
        2. BaseColumn::get calls (virtual) DataManagerColumn::getArrayV. // This function can be implemented by a derived storage manager or // virtual column engine class. //
        3. Storage managers derive from StManColumn. Its getArrayV function calls // the appropriate getArrayXXV function where XX is the data type (e.g. Int). // These getArrayXXV functions are implemented in the storage managers. //
        4. Virtual column engines derive from the templated VirtArrCol class which // implements getArrayV by calling a templated virtual getArray function. //
        // The old interface for a function such as getArrayColumn works more or less // the same. However, this function does not need to be implemented by a data manager. // ArrayColumn will first ask the data manager if it supports getting an entire // array column. If not, ArrayColumn will call getArray for each row. // Functions such as getSlice, etc. work similarly. // // A new interface has been developed which should result in a smaller code base // and simpler classes. The new interface could be developed thanks to same // Array enhancements making it possible to use quite some Array functionality // in non-templated classes. // The new interface works differently in a number of points: //
          //
        • Arrays are passed as ArrayBase* instead of void* making it possible to // get shapes, etc. in a non-templated way. //
        • ArrayColumn does not ask anymore if a data manager supports getArrayColumn. // Instead, the default implementation in DataManagerColumn::getArrayColumnV // will call getArrayV repetitively. //
        • The StManColumn interface is not really necessary anymore. //
        // // However, some plug-in data managers exist outside the Casacore repository // (e.g., LofarStMan and AdiosStMan). It should be possible to build and use them // for some time with the old and new interface. To make this possible the new // interface has to be backward compatible for some time. This is achieved by: //
          //
        • StManColumn is maintained (but getArrayV takes ArrayBase&, not void*). // It calls getArrayXXV(void*) depending on the data type. // A storage manager can implemented getArrayV itself bypassing StManColumn. //
        • Functions such as getArrayColumn are a bit more complicated. // StManColumn::getArrayColumnV calls getArrayColumnXXV, which calls // DataManager::getArrayColumnAB doing the getArrayV per row. // A derived class can have getArrayColumnXXV implemented. //
        //
        // // An abstract base class is needed to support multiple data // managers in the table system // // //# A List of bugs, limitations, extensions or planned refinements. // class DataManagerColumn { public: // Create a column. DataManagerColumn() : isFixedShape_p(False) {} // Frees up the storage. virtual ~DataManagerColumn(); // The copy constructor cannot be used for this base class. DataManagerColumn (const DataManagerColumn&) = delete; // Assignment cannot be used for this base class. DataManagerColumn& operator= (const DataManagerColumn&) = delete; // Set the isFixedShape flag. void setIsFixedShape (Bool isFixedShape) { isFixedShape_p = isFixedShape; } // Is this a fixed shape column? Bool isFixedShape() const { return isFixedShape_p; } // Get the data type of the column as defined in DataType.h. virtual int dataType() const = 0; // Get the data type id of the column for dataType==TpOther. // The default implementation returns an emptry string. // This function is required for virtual column engines handling // non-standard data types. It is used to check the data type. virtual String dataTypeId() const; // Test if data can be put into this column. // This does not test if the data file is writable, only if // it is in principle allowed to store data into the column. // (It may not be allowed for virtual columns). // The default is True. virtual Bool isWritable() const; // Set the maximum length of the value (can be used for strings). // By default the maximum length is ignored. virtual void setMaxLength (uInt maxLength); // Set the shape of all (fixed-shaped) arrays in the column. // Effectively it is the same as setShapeColumn, but it also sets // the isFixedShape_p flag. void setFixedShapeColumn (const IPosition& shape) { setShapeColumn (shape); isFixedShape_p = True; } // Set the shape of an (variable-shaped) array in the given row. // By default it throws a "not possible" exception. virtual void setShape (rownr_t rownr, const IPosition& shape); // Set the shape and tile shape of an (variable-shaped) array // in the given row. // By default it ignores the tile shape (thus only sets the shape). virtual void setShapeTiled (rownr_t rownr, const IPosition& shape, const IPosition& tileShape); // Is the value shape defined in the given row? // By default it returns True. virtual Bool isShapeDefined (rownr_t rownr); // Get the dimensionality of the item in the given row. // By default it returns shape(rownr).nelements(). virtual uInt ndim (rownr_t rownr); // Get the shape of the item in the given row. // By default it returns a zero-length IPosition (for a scalar value). virtual IPosition shape (rownr_t rownr); // Get the tile shape of the item in the given row. // By default it returns a zero-length IPosition. virtual IPosition tileShape (rownr_t rownr); // Can the data manager handle chaging the shape of an existing array? // Default is no. virtual Bool canChangeShape() const; // Get access to the ColumnCache object. // ColumnCache& columnCache() { return colCache_p; } const ColumnCache* columnCachePtr() const { return &colCache_p; } // // Get the scalar value in the given row. // These functions are non-virtual and are converted to their // virtual getXX equivalent to achieve that a derived templated class // (such as VirtualScalarColumn) does not have to declare and implement // all these functions. // The compiler complains about hiding virtual functions if you do not // declare all virtual functions with the same name in a derived class. // void get (rownr_t rownr, Bool* dataPtr) { getBool (rownr, dataPtr); } void get (rownr_t rownr, uChar* dataPtr) { getuChar (rownr, dataPtr); } void get (rownr_t rownr, Short* dataPtr) { getShort (rownr, dataPtr); } void get (rownr_t rownr, uShort* dataPtr) { getuShort (rownr, dataPtr); } void get (rownr_t rownr, Int* dataPtr) { getInt (rownr, dataPtr); } void get (rownr_t rownr, uInt* dataPtr) { getuInt (rownr, dataPtr); } void get (rownr_t rownr, Int64* dataPtr) { getInt64 (rownr, dataPtr); } void get (rownr_t rownr, float* dataPtr) { getfloat (rownr, dataPtr); } void get (rownr_t rownr, double* dataPtr) { getdouble (rownr, dataPtr); } void get (rownr_t rownr, Complex* dataPtr) { getComplex (rownr, dataPtr); } void get (rownr_t rownr, DComplex* dataPtr) { getDComplex (rownr, dataPtr); } void get (rownr_t rownr, String* dataPtr) { getString (rownr, dataPtr); } // This function is the get for all non-standard data types. void get (rownr_t rownr, void* dataPtr) { getOther (rownr, dataPtr); } // // Put the scalar value into the given row. // These functions are non-virtual and are converted to their // virtual putXX equivalent to achieve that a derived templated class // (such as VirtualScalarColumn) does not have to declare and implement // all these functions. // The compiler complains about hiding virtual functions if you do not // declare all virtual functions with the same name in a derived class. // void put (rownr_t rownr, const Bool* dataPtr) { putBool (rownr, dataPtr); } void put (rownr_t rownr, const uChar* dataPtr) { putuChar (rownr, dataPtr); } void put (rownr_t rownr, const Short* dataPtr) { putShort (rownr, dataPtr); } void put (rownr_t rownr, const uShort* dataPtr) { putuShort (rownr, dataPtr); } void put (rownr_t rownr, const Int* dataPtr) { putInt (rownr, dataPtr); } void put (rownr_t rownr, const uInt* dataPtr) { putuInt (rownr, dataPtr); } void put (rownr_t rownr, const Int64* dataPtr) { putInt64 (rownr, dataPtr); } void put (rownr_t rownr, const float* dataPtr) { putfloat (rownr, dataPtr); } void put (rownr_t rownr, const double* dataPtr) { putdouble (rownr, dataPtr); } void put (rownr_t rownr, const Complex* dataPtr) { putComplex (rownr, dataPtr); } void put (rownr_t rownr, const DComplex* dataPtr) { putDComplex (rownr, dataPtr); } void put (rownr_t rownr, const String* dataPtr) { putString (rownr, dataPtr); } // This function is the put for all non-standard data types. void put (rownr_t rownr, const void* dataPtr) { putOther (rownr, dataPtr); } // // Get all scalar values in the column. // The vector given in data has to have the correct length // (which is guaranteed by the ScalarColumn getColumn function). // The default implementation does a getXX per row. virtual void getScalarColumnV (ArrayBase& dataPtr); // Put all scalar values in the column. // The vector given in data has to have the correct length // (which is guaranteed by the ScalarColumn putColumn function). // The default implementation does a putXX per row. virtual void putScalarColumnV (const ArrayBase& dataPtr); // Get some scalar values in the column. // The vector given in data has to have the correct length // (which is guaranteed by the ScalarColumn getColumn function). // The default implementation does a getXX per row. virtual void getScalarColumnCellsV (const RefRows& rownrs, ArrayBase& dataPtr); // Put some scalar values in the column. // The vector given in data has to have the correct length // (which is guaranteed by the ScalarColumn getColumn function). // The default implementation does a putXX per row. virtual void putScalarColumnCellsV (const RefRows& rownrs, const ArrayBase& dataPtr); // Get the array value in the given row. // The array given in data has to have the correct shape // (which is guaranteed by the ArrayColumn get function). // The default implementation throws an "invalid operation" exception. virtual void getArrayV (rownr_t rownr, ArrayBase& dataPtr); // Put the array value into the given row. // The array given in data has to have the correct shape // (which is guaranteed by the ArrayColumn put function). // The default implementation throws an "invalid operation" exception. virtual void putArrayV (rownr_t rownr, const ArrayBase& data); // Get all array values in the column. // The array given in data has to have the correct shape // (which is guaranteed by the ArrayColumn getColumn function). // The default implementation does a getArrayV per row. virtual void getArrayColumnV (ArrayBase& data); // Put all array values in the column. // The array given in data has to have the correct shape // (which is guaranteed by the ArrayColumn putColumn function). // The default implementation does a putArrayV per row. virtual void putArrayColumnV (const ArrayBase& data); // Get some array values in the column. // The array given in data has to have the correct shape // (which is guaranteed by the ArrayColumn getColumn function). // The default implementation does a getArrayV per row. virtual void getArrayColumnCellsV (const RefRows& rownrs, ArrayBase& data); // Put some array values in the column. // The array given in data has to have the correct shape // (which is guaranteed by the ArrayColumn getColumn function). // The default implementation does a putArrayV per row. virtual void putArrayColumnCellsV (const RefRows& rownrs, const ArrayBase& data); // Get a section of the array in the given row. // The array given in data has to have the correct shape // (which is guaranteed by the ArrayColumn getSlice function). // The default implementation does getArrayV and takes the slice. virtual void getSliceV (rownr_t rownr, const Slicer& slicer, ArrayBase& data); // Put into a section of the array in the given row. // The array given in data has to have the correct shape // (which is guaranteed by the ArrayColumn putSlice function). // The default implementation does get/putArrayV and puts the slice. virtual void putSliceV (rownr_t rownr, const Slicer& slicer, const ArrayBase& data); // Get a section of all arrays in the column. // The array given in data has to have the correct shape // (which is guaranteed by the ArrayColumn getColumn function). // The default implementation does a getSliceV per row. virtual void getColumnSliceV (const Slicer& slicer, ArrayBase& data); // Put into a section of all arrays in the column. // The array given in data has to have the correct shape // (which is guaranteed by the ArrayColumn putColumn function). // The default implementation does a putSliceV per row. virtual void putColumnSliceV (const Slicer& slicer, const ArrayBase& data); // Get a section of some arrays in the column. // The array given in data has to have the correct shape // (which is guaranteed by the ArrayColumn getColumn function). // The default implementation does a getSliceV per row. virtual void getColumnSliceCellsV (const RefRows& rownrs, const Slicer& slicer, ArrayBase& data); // Put into a section of some arrays in the column. // The array given in data has to have the correct shape // (which is guaranteed by the ArrayColumn putColumn function). // The default implementation does a putSliceV per row. virtual void putColumnSliceCellsV (const RefRows& rownrs, const Slicer& slicer, const ArrayBase& data); // Throw an "invalid operation" exception for the default // implementation of get. void throwGet() const; // Throw an "invalid operation" exception for the default // implementation of put. void throwPut() const; // Set the column name. void setColumnName (const String& colName) { colName_p = colName; } // Get rhe column name. const String& columnName() const { return colName_p; } protected: // Get the scalar value in the given row. // The default implementation throws an "invalid operation" exception. // virtual void getBool (rownr_t rownr, Bool* dataPtr); virtual void getuChar (rownr_t rownr, uChar* dataPtr); virtual void getShort (rownr_t rownr, Short* dataPtr); virtual void getuShort (rownr_t rownr, uShort* dataPtr); virtual void getInt (rownr_t rownr, Int* dataPtr); virtual void getuInt (rownr_t rownr, uInt* dataPtr); virtual void getInt64 (rownr_t rownr, Int64* dataPtr); virtual void getfloat (rownr_t rownr, float* dataPtr); virtual void getdouble (rownr_t rownr, double* dataPtr); virtual void getComplex (rownr_t rownr, Complex* dataPtr); virtual void getDComplex (rownr_t rownr, DComplex* dataPtr); virtual void getString (rownr_t rownr, String* dataPtr); // This function is the get for all non-standard data types. virtual void getOther (rownr_t rownr, void* dataPtr); // // Put the scalar value into the given row. // The default implementation throws an "invalid operation" exception. // virtual void putBool (rownr_t rownr, const Bool* dataPtr); virtual void putuChar (rownr_t rownr, const uChar* dataPtr); virtual void putShort (rownr_t rownr, const Short* dataPtr); virtual void putuShort (rownr_t rownr, const uShort* dataPtr); virtual void putInt (rownr_t rownr, const Int* dataPtr); virtual void putuInt (rownr_t rownr, const uInt* dataPtr); virtual void putInt64 (rownr_t rownr, const Int64* dataPtr); virtual void putfloat (rownr_t rownr, const float* dataPtr); virtual void putdouble (rownr_t rownr, const double* dataPtr); virtual void putComplex (rownr_t rownr, const Complex* dataPtr); virtual void putDComplex (rownr_t rownr, const DComplex* dataPtr); virtual void putString (rownr_t rownr, const String* dataPtr); // This function is the put for all non-standard data types. virtual void putOther (rownr_t rownr, const void* dataPtr); // // The default implementations of get and put functions. // void getScalarColumnBase (ArrayBase& dataPtr); void putScalarColumnBase (const ArrayBase& dataPtr); void getScalarColumnCellsBase (const RefRows& rownrs, ArrayBase& dataPtr); void putScalarColumnCellsBase (const RefRows& rownrs, const ArrayBase& dataPtr); void getArrayColumnBase (ArrayBase& data); void putArrayColumnBase (const ArrayBase& data); void getArrayColumnCellsBase (const RefRows& rownrs, ArrayBase& data); void putArrayColumnCellsBase (const RefRows& rownrs, const ArrayBase& data); void getSliceBase (rownr_t rownr, const Slicer& slicer, ArrayBase& data); void putSliceBase (rownr_t rownr, const Slicer& slicer, const ArrayBase& data); void getColumnSliceBase (const Slicer& slicer, ArrayBase& data); void putColumnSliceBase (const Slicer& slicer, const ArrayBase& data); void getColumnSliceCellsBase (const RefRows& rownrs, const Slicer& slicer, ArrayBase& data); void putColumnSliceCellsBase (const RefRows& rownrs, const Slicer& slicer, const ArrayBase& data); // private: // Set the shape of all (fixed-shaped) arrays in the column. // By default it throws a "not possible" exception. virtual void setShapeColumn (const IPosition& shape); // Get a slice from the array in the given row. // It reads the full array in the possibly reshaped ArrayBase object. void getSliceArr (rownr_t row, const Slicer& section, std::shared_ptr& fullArr, ArrayBase& arr); // Put a slice into the array in the given row. // It reads and writes the full array in the possibly reshaped ArrayBase // object. void putSliceArr (rownr_t row, const Slicer& section, std::shared_ptr& fullArr, const ArrayBase& arr); //# Data members Bool isFixedShape_p; String colName_p; ColumnCache colCache_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/ForwardCol.cc000066400000000000000000000345431476623553700204650ustar00rootroot00000000000000//# ForwardCol.cc: Virtual Column Engine forwarding to another column //# Copyright (C) 1995,1996,1997,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ForwardColumnEngine::ForwardColumnEngine (const String& dataManagerName, const Record& spec) : refColumns_p (0), dataManName_p(dataManagerName) { if (spec.isDefined ("FORWARDTABLE")) { refTable_p = Table(spec.asString ("FORWARDTABLE")); } } ForwardColumnEngine::ForwardColumnEngine (const Table& referencedTable, const String& dataManagerName) : refColumns_p (0), refTable_p (referencedTable), dataManName_p(dataManagerName) {} ForwardColumnEngine::ForwardColumnEngine (const Table& referencedTable) : refColumns_p (0), refTable_p (referencedTable) {} ForwardColumnEngine::~ForwardColumnEngine() { for (uInt i=0; ifillTableName (table(), refTable_p); refColumns_p[i]->prepare (table()); return; } } throw DataManInternalError ("ForwardColumnEngine::addColumn on column " + colp->columnName()); } void ForwardColumnEngine::removeColumn (DataManagerColumn* colp) { for (uInt i=0; icolumnName()); } void ForwardColumnEngine::addForwardColumn (ForwardColumn* colp) { uInt nr = refColumns_p.nelements(); refColumns_p.resize(nr + 1); refColumns_p[nr] = colp; } DataManagerColumn* ForwardColumnEngine::makeScalarColumn (const String& name, int dataType, const String& dataTypeId) { ForwardColumn* colp = new ForwardColumn (this, name, dataType, dataTypeId, refTable_p); addForwardColumn (colp); return colp; } DataManagerColumn* ForwardColumnEngine::makeIndArrColumn (const String& name, int dataType, const String& dataTypeId) { return makeScalarColumn (name, dataType, dataTypeId); } void ForwardColumnEngine::create64 (rownr_t) { baseCreate(); } void ForwardColumnEngine::baseCreate() { // The table is new. // Define a keyword telling the data manager name. table().rwKeywordSet().define (keywordName ("_ForwardColumn_Name"), dataManName_p); // Define a keyword in all columns telling the original table. for (uInt i=0; ifillTableName (table(), refTable_p); } } void ForwardColumnEngine::prepare() { basePrepare(); } void ForwardColumnEngine::basePrepare() { // Get the data manager name (if defined). const TableRecord& keySet = table().keywordSet(); String keyword = keywordName ("_ForwardColumn_Name"); if (keySet.isDefined (keyword)) { dataManName_p = keySet.asString (keyword); } // Attach all forwarding columns. for (uInt i=0; iprepare (table()); } } void ForwardColumnEngine::reopenRW() { for (uInt i=0; isetRW(); } } DataManager* ForwardColumnEngine::makeObject (const String& dataManagerName, const Record& spec) { DataManager* dmPtr = new ForwardColumnEngine (dataManagerName, spec); return dmPtr; } void ForwardColumnEngine::registerClass() { DataManager::registerCtor (className(), makeObject); } String ForwardColumnEngine::dataManagerName() const { return dataManName_p; } String ForwardColumnEngine::dataManagerType() const { return className(); } String ForwardColumnEngine::className() { return "ForwardColumnEngine"; } Record ForwardColumnEngine::dataManagerSpec() const { Record spec; spec.define ("FORWARDTABLE", refTable_p.tableName()); return spec; } ForwardColumn::ForwardColumn (ForwardColumnEngine* enginePtr, const String& name, int dataType, const String& dataTypeId, const Table& refTable) : enginePtr_p (enginePtr), colName_p (name), dataType_p (dataType), dataTypeId_p(dataTypeId), colPtr_p (0) { if (!refTable.isNull()) { refCol_p.attach (refTable, name); } } ForwardColumn::~ForwardColumn() {} int ForwardColumn::dataType() const { return dataType_p; } String ForwardColumn::dataTypeId() const { return dataTypeId_p; } void ForwardColumn::fillTableName (const Table& thisTable, const Table& refTable) { // Set the column (temporarily) to writable, so a TableColumn // object can be created for adding the keyword. // Prepare will set the writable switch correctly. writable_p = True; // When the table (in which this virtual column is used) is new, // the name of the outermost non-forwarding table will be stored // as a column keyword. // It is possible that there is a chain of forwarding engines and // in this way forwarding is kept to a minimum. TableColumn thisCol (thisTable, colName_p); // Get the name of the original table. // If the referenced table column is a ForwardColumnEngine itself, it // will contain the special keyword containing the name of the // original table. Otherwise the referenced table is the original // table itself. String name; if (refCol_p.keywordSet().isDefined ("_ForwardColumn_TableName")) { name = refCol_p.keywordSet().asString("_ForwardColumn_TableName"); }else{ name = refTable.tableName(); } // Make relative to parent table. name = Path::stripDirectory (name, thisTable.tableName()); // Define the keyword containing the original table name. thisCol.rwKeywordSet().define ("_ForwardColumn_TableName" + enginePtr_p->suffix(), name); } void ForwardColumn::prepare (const Table& thisTable) { basePrepare (thisTable, True); } void ForwardColumn::basePrepare (const Table& thisTable, Bool writable) { TableColumn thisCol (thisTable, colName_p); // Open the original table as stored in the special keyword. // Open it for read/write if this table is writable and if the // original table file is writable. String name = thisCol.keywordSet().asString ("_ForwardColumn_TableName" + enginePtr_p->suffix()); name = Path::addDirectory (name, thisTable.tableName()); writable_p = writable; if (writable_p) { if (!(thisTable.isWritable() && Table::isWritable(name))) { writable_p = False; } } if (writable_p) { origTable_p = Table (name, Table::Update); }else{ origTable_p = Table (name); } TableColumn origCol (origTable_p, colName_p); // Check if the column descriptions match. // If so, get the pointer to the original column. if (origCol.columnDesc() != thisCol.columnDesc()) { throw (DataManInvOper ("ForwardColumn::prepare: ColumnDesc of " + colName_p + " mismatches original")); } colPtr_p = origCol.baseColPtr(); if (writable_p) { writable_p = origTable_p.isColumnWritable (colName_p); } // Set the RefTable in the engine (in case not set yet). enginePtr_p->setRefTable (origTable_p); } Bool ForwardColumn::isWritable() const { return writable_p; } void ForwardColumn::setRW() { // Set the column to writable if the underlying table is writable // and the referenced column in it is writable. // First test if the table is already writable, which may often be // the case because the same table is used by multiple columns. if (! origTable_p.isWritable()) { if (Table::isWritable (origTable_p.tableName())) { origTable_p.reopenRW(); } } if (origTable_p.isColumnWritable (colName_p)) { writable_p = True; } } void ForwardColumn::setShapeColumn (const IPosition& shape) { if (!refCol_p.isNull()) { if (shape != refCol_p.shapeColumn()) { throw (DataManInvOper ("ForwardColumn::setShapeColumn: shape of column " + colName_p + " mismatches forwarded column")); } } } void ForwardColumn::setShape (rownr_t rownr, const IPosition& shape) { colPtr_p->setShape (rownr, shape); } uInt ForwardColumn::ndim (rownr_t rownr) { return colPtr_p->ndim (rownr); } IPosition ForwardColumn::shape(rownr_t rownr) { return colPtr_p->shape (rownr); } Bool ForwardColumn::isShapeDefined (rownr_t rownr) { return colPtr_p->isDefined (rownr); } Bool ForwardColumn::canChangeShape() const { return (colPtr_p == 0 ? False : colPtr_p->canChangeShape()); } void ForwardColumn::getArrayV (rownr_t rownr, ArrayBase& dataPtr) { colPtr_p->getArray (rownr, dataPtr); } void ForwardColumn::getSliceV (rownr_t rownr, const Slicer& ns, ArrayBase& dataPtr) { colPtr_p->getSlice (rownr, ns, dataPtr); } void ForwardColumn::getScalarColumnV (ArrayBase& dataPtr) { colPtr_p->getScalarColumn (dataPtr); } void ForwardColumn::getArrayColumnV (ArrayBase& dataPtr) { colPtr_p->getArrayColumn (dataPtr); } void ForwardColumn::getScalarColumnCellsV (const RefRows& rownrs, ArrayBase& dataPtr) { colPtr_p->getScalarColumnCells (rownrs, dataPtr); } void ForwardColumn::getArrayColumnCellsV (const RefRows& rownrs, ArrayBase& dataPtr) { colPtr_p->getArrayColumnCells (rownrs, dataPtr); } void ForwardColumn::getColumnSliceV (const Slicer& ns, ArrayBase& dataPtr) { colPtr_p->getColumnSlice (ns, dataPtr); } void ForwardColumn::getColumnSliceCellsV (const RefRows& rownrs, const Slicer& ns, ArrayBase& dataPtr) { colPtr_p->getColumnSliceCells (rownrs, ns, dataPtr); } void ForwardColumn::putArrayV (rownr_t rownr, const ArrayBase& dataPtr) { colPtr_p->putArray (rownr, dataPtr); } void ForwardColumn::putSliceV (rownr_t rownr, const Slicer& ns, const ArrayBase& dataPtr) { colPtr_p->putSlice (rownr, ns, dataPtr); } void ForwardColumn::putScalarColumnV (const ArrayBase& dataPtr) { colPtr_p->putScalarColumn (dataPtr); } void ForwardColumn::putArrayColumnV (const ArrayBase& dataPtr) { colPtr_p->putArrayColumn (dataPtr); } void ForwardColumn::putScalarColumnCellsV (const RefRows& rownrs, const ArrayBase& dataPtr) { colPtr_p->putScalarColumnCells (rownrs, dataPtr); } void ForwardColumn::putArrayColumnCellsV (const RefRows& rownrs, const ArrayBase& dataPtr) { colPtr_p->putArrayColumnCells (rownrs, dataPtr); } void ForwardColumn::putColumnSliceV (const Slicer& ns, const ArrayBase& dataPtr) { colPtr_p->putColumnSlice (ns, dataPtr); } void ForwardColumn::putColumnSliceCellsV (const RefRows& rownrs, const Slicer& ns, const ArrayBase& dataPtr) { colPtr_p->putColumnSliceCells (rownrs, ns, dataPtr); } #define FORWARDCOLUMN_GETPUT(T,NM) \ void ForwardColumn::aips_name2(get,NM) (rownr_t rownr, T* dataPtr) \ { colPtr_p->get (rownr, dataPtr); } \ void ForwardColumn::aips_name2(put,NM) (rownr_t rownr, const T* dataPtr) \ { colPtr_p->put (rownr, dataPtr); } FORWARDCOLUMN_GETPUT(Bool,Bool) FORWARDCOLUMN_GETPUT(uChar,uChar) FORWARDCOLUMN_GETPUT(Short,Short) FORWARDCOLUMN_GETPUT(uShort,uShort) FORWARDCOLUMN_GETPUT(Int,Int) FORWARDCOLUMN_GETPUT(uInt,uInt) FORWARDCOLUMN_GETPUT(Int64,Int64) FORWARDCOLUMN_GETPUT(float,float) FORWARDCOLUMN_GETPUT(double,double) FORWARDCOLUMN_GETPUT(Complex,Complex) FORWARDCOLUMN_GETPUT(DComplex,DComplex) FORWARDCOLUMN_GETPUT(String,String) FORWARDCOLUMN_GETPUT(void,Other) } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/ForwardCol.h000066400000000000000000000540401476623553700203210ustar00rootroot00000000000000//# ForwardCol.h: Virtual Column Engine to forward to other columns //# Copyright (C) 1995,1996,1997,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_FORWARDCOL_H #define TABLES_FORWARDCOL_H //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class ForwardColumnEngine; class BaseColumn; // // Virtual column forwarding to another column // // // // // //# Classes you should understand before using this one. //
      • ForwardColumnEngine //
      • DataManagerColumn // // // ForwardColumn represents a virtual column which forwards the // gets and puts to a column with the same name in another table. // It is, in fact, a reference to the other column. // The name of the other table is stored as a keyword in the // forwarding column. When there is a forwarding chain (i.e. // forwarding to a forwarding column), the name of the last // table in the chain is stored in the keyword. In this way, the // length of the chain is kept to a minimum. Otherwise a very long // chain could occur, which would slow things down. // // Addition and deletion of rows is allowed, but the functions addRow and // removeRow do not do anything at all. They are implemented to override // the default "throw exception" implementation. Because the engine // allows this, it can be used in a table supporting addition and removal // of rows. // // An object of this class is created (and deleted) by // ForwardColumnEngine // which creates a ForwardColumn object for each column being forwarded. // // // This class will be used by the calibration software. // Most columns in a measurement table will be forwarded, while // a few (i.e. the data themselves) will be calculated by a dedicated // calibration engine. // class ForwardColumn : public DataManagerColumn { public: // Construct it for the given column. ForwardColumn (ForwardColumnEngine* enginePtr, const String& columnName, int dataType, const String& dataTypeId, const Table& referencedTable); // Destructor is mandatory. virtual ~ForwardColumn(); // Copy constructor is not needed and therefore forbidden. ForwardColumn (const ForwardColumn&) = delete; // Assignment is not needed and therefore forbidden. ForwardColumn& operator= (const ForwardColumn&) = delete; // Define the special keyword containing the name of the original table. // If the column in the referenced table contains that special keyword, // it is in its turn a forwarding column. In that case the special // keyword value will be copied over to shortcut the forwarding chain. // The suffix is appended to the keyword name when defining it. // This makes this function usable for derived classes. void fillTableName (const Table& thisTable, const Table& referencedTable); // Initialize the object. // This means binding the column to the column with the same name // in the original table. // It checks if the description of both columns is the same. // It also determines if the column is writable. virtual void prepare (const Table& thisTable); // Set the column to writable if its underlying table is writable. void setRW(); protected: // Do the preparation of the base class column object. void basePrepare (const Table& thisTable, Bool writable); BaseColumn* colPtr() const { return colPtr_p; } private: // Create a SetupNewTable object with the given name and option // and with the description from the given table. // The SetupNewTable object will use a single ForwardColumn // engine which forwards all columns to the given table. // Later the SetupNewTable::bind functions can be used to bind one // or more columns to another data manager. static SetupNewTable setupNewTable (const Table& table, const String& tableName, Table::TableOption option); // This data manager may be able to handle changing array shapes. Bool canChangeShape() const; // Get the data type of the column as defined in DataType.h. int dataType() const; // Get the data type id of the column for dataType==TpOther. // This function is required for virtual column engines handling // non-standard data types. It is used to check the data type. String dataTypeId() const; // Test if data can be put into this column. Bool isWritable() const; // Set the shape of an direct array. // This only checks if the shape matches the referenced column. void setShapeColumn (const IPosition& shape); // Set the shape of an (indirect) array in the given row. void setShape (rownr_t rownr, const IPosition& shape); // Is the value shape defined in the given row? Bool isShapeDefined (rownr_t rownr); // Get the dimensionality of the item in the given row. uInt ndim (rownr_t rownr); // Get the shape of the item in the given row. IPosition shape (rownr_t rownr); // Get the scalar value with a standard data type in the given row. // virtual void getBool (rownr_t rownr, Bool* dataPtr); virtual void getuChar (rownr_t rownr, uChar* dataPtr); virtual void getShort (rownr_t rownr, Short* dataPtr); virtual void getuShort (rownr_t rownr, uShort* dataPtr); virtual void getInt (rownr_t rownr, Int* dataPtr); virtual void getuInt (rownr_t rownr, uInt* dataPtr); virtual void getInt64 (rownr_t rownr, Int64* dataPtr); virtual void getfloat (rownr_t rownr, float* dataPtr); virtual void getdouble (rownr_t rownr, double* dataPtr); virtual void getComplex (rownr_t rownr, Complex* dataPtr); virtual void getDComplex (rownr_t rownr, DComplex* dataPtr); virtual void getString (rownr_t rownr, String* dataPtr); // // Get the scalar value with a non-standard data type in the given row. virtual void getOther (rownr_t rownr, void* dataPtr); // Put the scalar value with a standard data type into the given row. // virtual void putBool (rownr_t rownr, const Bool* dataPtr); virtual void putuChar (rownr_t rownr, const uChar* dataPtr); virtual void putShort (rownr_t rownr, const Short* dataPtr); virtual void putuShort (rownr_t rownr, const uShort* dataPtr); virtual void putInt (rownr_t rownr, const Int* dataPtr); virtual void putuInt (rownr_t rownr, const uInt* dataPtr); virtual void putInt64 (rownr_t rownr, const Int64* dataPtr); virtual void putfloat (rownr_t rownr, const float* dataPtr); virtual void putdouble (rownr_t rownr, const double* dataPtr); virtual void putComplex (rownr_t rownr, const Complex* dataPtr); virtual void putDComplex (rownr_t rownr, const DComplex* dataPtr); virtual void putString (rownr_t rownr, const String* dataPtr); // // Put the scalar value with a non-standard data type into the given row. virtual void putOther (rownr_t rownr, const void* dataPtr); // Get all scalar values in the column. // The argument dataPtr is in fact a Vector&, but a ArrayBase& // is needed to be generic. // The vector pointed to by dataPtr has to have the correct length // (which is guaranteed by the ScalarColumn getColumn function). void getScalarColumnV (ArrayBase& dataPtr); // Put all scalar values in the column. // The argument dataPtr is in fact a const Vector&, but a const ArrayBase& // is needed to be generic. // The vector pointed to by dataPtr has to have the correct length // (which is guaranteed by the ScalarColumn putColumn function). void putScalarColumnV (const ArrayBase& dataPtr); // Get some scalar values in the column. // The argument dataPtr is in fact a Vector&, but a ArrayBase& // is needed to be generic. // The vector pointed to by dataPtr has to have the correct length // (which is guaranteed by the ScalarColumn getColumn function). virtual void getScalarColumnCellsV (const RefRows& rownrs, ArrayBase& dataPtr); // Put some scalar values in the column. // The argument dataPtr is in fact a const Vector&, but a const ArrayBase& // is needed to be generic. // The vector pointed to by dataPtr has to have the correct length // (which is guaranteed by the ScalarColumn getColumn function). virtual void putScalarColumnCellsV (const RefRows& rownrs, const ArrayBase& dataPtr); // Get the array value in the given row. // The argument dataPtr is in fact a Array&, but a ArrayBase& // is needed to be generic. // The array pointed to by dataPtr has to have the correct shape // (which is guaranteed by the ArrayColumn get function). void getArrayV (rownr_t rownr, ArrayBase& dataPtr); // Put the array value into the given row. // The argument dataPtr is in fact a const Array&, but a const ArrayBase& // is needed to be generic. // The array pointed to by dataPtr has to have the correct shape // (which is guaranteed by the ArrayColumn put function). void putArrayV (rownr_t rownr, const ArrayBase& dataPtr); // Get a section of the array in the given row. // The argument dataPtr is in fact a Array&, but a ArrayBase& // is needed to be generic. // The array pointed to by dataPtr has to have the correct shape // (which is guaranteed by the ArrayColumn getSlice function). void getSliceV (rownr_t rownr, const Slicer& slicer, ArrayBase& dataPtr); // Put into a section of the array in the given row. // The argument dataPtr is in fact a const Array&, but a const ArrayBase& // is needed to be generic. // The array pointed to by dataPtr has to have the correct shape // (which is guaranteed by the ArrayColumn putSlice function). void putSliceV (rownr_t rownr, const Slicer& slicer, const ArrayBase& dataPtr); // Get all scalar values in the column. // The argument dataPtr is in fact a Vector&, but a ArrayBase& // is needed to be generic. // The vector pointed to by dataPtr has to have the correct length // (which is guaranteed by the ScalarColumn getColumn function). void getArrayColumnV (ArrayBase& dataPtr); // Put all scalar values in the column. // The argument dataPtr is in fact a const Vector&, but a const ArrayBase& // is needed to be generic. // The vector pointed to by dataPtr has to have the correct length // (which is guaranteed by the ScalarColumn putColumn function). void putArrayColumnV (const ArrayBase& dataPtr); // Get some array values in the column. // The argument dataPtr is in fact an Array&, but a ArrayBase& // is needed to be generic. // The vector pointed to by dataPtr has to have the correct length // (which is guaranteed by the ArrayColumn getColumn function). virtual void getArrayColumnCellsV (const RefRows& rownrs, ArrayBase& dataPtr); // Put some array values in the column. // The argument dataPtr is in fact an const Array&, but a const ArrayBase& // is needed to be generic. // The vector pointed to by dataPtr has to have the correct length // (which is guaranteed by the ArrayColumn getColumn function). virtual void putArrayColumnCellsV (const RefRows& rownrs, const ArrayBase& dataPtr); // Get a section of all arrays in the column. // The argument dataPtr is in fact a Array&, but a ArrayBase& // is needed to be generic. // The array pointed to by dataPtr has to have the correct shape // (which is guaranteed by the ArrayColumn getColumn function). void getColumnSliceV (const Slicer& slicer, ArrayBase& dataPtr); // Put a section into all arrays in the column. // The argument dataPtr is in fact a const Array&, but a const ArrayBase& // is needed to be generic. // The array pointed to by dataPtr has to have the correct shape // (which is guaranteed by the ArrayColumn putColumn function). void putColumnSliceV (const Slicer& slicer, const ArrayBase& dataPtr); // Get a section of some arrays in the column. // The argument dataPtr is in fact an Array&, but a ArrayBase& // is needed to be generic. // The array pointed to by dataPtr has to have the correct shape // (which is guaranteed by the ArrayColumn getColumn function). virtual void getColumnSliceCellsV (const RefRows& rownrs, const Slicer& slicer, ArrayBase& dataPtr); // Put into a section of some arrays in the column. // The argument dataPtr is in fact a const Array&, but a const ArrayBase& // is needed to be generic. // The array pointed to by dataPtr has to have the correct shape // (which is guaranteed by the ArrayColumn putColumn function). virtual void putColumnSliceCellsV (const RefRows& rownrs, const Slicer& slicer, const ArrayBase& dataPtr); //# Now define the data members. ForwardColumnEngine* enginePtr_p; //# pointer to parent engine String colName_p; //# The name of the column int dataType_p; //# data type of the column String dataTypeId_p; //# data type Id of the column TableColumn refCol_p; //# Column in referenced table //# This is only filled in when //# a new table is created. Bool writable_p; //# True = column is writable Table origTable_p; //# The original table for this column BaseColumn* colPtr_p; //# pointer to column in original table }; // // Virtual column engine forwarding to other columns // // // // // //# Classes you should understand before using this one. //
      • VirtualColumnEngine // // // ForwardColumnEngine is a data manager which forwards // the gets and puts of columns to columns with the same names in // another table. // It is, in fact, a reference to the other table columns. // The engine consists of a set of // ForwardColumn // objects, which handle the actual gets and puts. // // // This class will be used by the calibration software. // Most columns in a measurement table will be forwarded // (thus bound to a ForwardColumnEngine object), while // a few (i.e. the data themselves) will be calculated by a dedicated // calibration engine. // // // // // The original table. // Table tab("someTable"); // // Create another table with the same description. // SetupNewTable newtab("tForwardCol1.data", tab.tableDesc(), Table::New); // // Create an engine which forwards to the original table. // // Bind all columns in the new table to the forwarding engine. // ForwardColumnEngine fce(tab); // newtab.bindAll (fce); // // Create the new table. // // Every get and put on this table is forwarded to the original table. // // NB. Puts cannot be done here, because the original table was // // opened as readonly. // // Of course, some columns could have been bound to another // // data manager (storage manager, calibration engine, ...). // Table forwTab(newtab); // // class ForwardColumnEngine : public VirtualColumnEngine { public: // The default constructor is required for reconstruction of the // engine when a table is read back. ForwardColumnEngine (const String& dataManagerName, const Record& spec); // Create the engine. // The columns using this engine will reference the given table. // The data manager gets the given name. ForwardColumnEngine (const Table& referencedTable, const String& dataManagerName); // Create the engine. // The columns using this engine will reference the given table. // The data manager has no name. ForwardColumnEngine (const Table& referencedTable); // Destructor is mandatory. ~ForwardColumnEngine(); // The copy constructor is forbidden. ForwardColumnEngine (const ForwardColumnEngine&) = delete; // Assignment is forbidden. ForwardColumnEngine& operator= (const ForwardColumnEngine&) = delete; // Clone the engine object. DataManager* clone() const; // Return the name of the data manager. This is the name of this // instantiation of the data manager, thus not its type name. String dataManagerName() const; // Return the type of the engine (i.e. its class name ForwardColumnEngine). String dataManagerType() const; // Record a record containing data manager specifications. virtual Record dataManagerSpec() const; // Get the suffix to be used for names. const String& suffix() const; // Return the name of the class. static String className(); // Register the class name and the static makeObject "constructor". // This will make the engine known to the table system. static void registerClass(); protected: // Set the suffix. void setSuffix (const String& suffix); // Add a ForwardColumn object to the block. void addForwardColumn (ForwardColumn* colp); // Get access to the refTable_p data member. const Table& refTable() const { return refTable_p; } // Do the creation (i.e. initialization) of the engine. void baseCreate(); // Do the preparation of the engine by preparing all columns. void basePrepare(); private: // This data manager allows to add rows. Bool canAddRow() const; // This data manager allows to delete rows. Bool canRemoveRow() const; // Add rows to all columns. // This is not doing anything (but needed to override the default). void addRow64 (rownr_t nrrow); // Delete a row from all columns. // This is not doing anything (but needed to override the default). void removeRow64 (rownr_t rownr); // This data manager allows to add columns. Bool canAddColumn() const; // This data manager allows to delete columns. Bool canRemoveColumn() const; // Add a column. void addColumn (DataManagerColumn*); // Delete a column. void removeColumn (DataManagerColumn*); // Create the column object for the scalar column in this engine. DataManagerColumn* makeScalarColumn (const String& columnName, int dataType, const String& dataTypeId); // Create the column object for the indirect array column in this engine. DataManagerColumn* makeIndArrColumn (const String& columnName, int dataType, const String& dataTypeId); // Initialize the object for a new table. // It defines the column keywords containing the name of the // original table, which can be the parent of the referenced table. void create64 (rownr_t initialNrrow); // Initialize the engine. // It gets the name of the original table(s) from the column keywords, // opens those tables and attaches the ForwardColumn objects to the // columns in those tables. void prepare(); // Reopen the engine for read/write access. // It makes all its columns writable if their underlying table is writable. void reopenRW(); // Define the various engine column objects. PtrBlock refColumns_p; // The referenced table. // For newly created tables this is filled in by the constructor. // For existing tables this is filled in by the first ForwardColumn // object being constructed. Table refTable_p; // The name of the data manager. String dataManName_p; // The suffix to be used in names. String suffix_p; public: // Set RefTable_p if not set yet. // This is done by ForwardColumn to cover the case for existing // tables where the default constructor of ForwardColumnEngine // is used and refTable_p is not filled in. void setRefTable (const Table& refTable); // Define the "constructor" to construct this engine when a // table is read back. // This "constructor" has to be registered by the user of the engine. // If the engine is commonly used, its registration can be added // into the registerAllCtor function in DataManReg.cc. // This function gets automatically invoked by the table system. static DataManager* makeObject (const String& dataManagerType, const Record& spec); }; inline const String& ForwardColumnEngine::suffix() const { return suffix_p; } inline void ForwardColumnEngine::setSuffix (const String& suffix) { suffix_p = suffix; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/ForwardColRow.cc000066400000000000000000000175631476623553700211600ustar00rootroot00000000000000//# ForwardColRow.cc: Virtual Column Engine forwarding to another row/column //# Copyright (C) 1995,1996,1997,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ForwardColumnIndexedRowEngine::ForwardColumnIndexedRowEngine (const String& dataManagerName, const Record& spec) : ForwardColumnEngine (dataManagerName, spec), lastRow_p (-1) { setSuffix ("_Row"); if (spec.isDefined("COLUMNNAME")) { spec.get ("COLUMNNAME", rowColumnName_p); } } ForwardColumnIndexedRowEngine::ForwardColumnIndexedRowEngine (const Table& referencedTable, const String& rowColumnName, const String& dataManagerName) : ForwardColumnEngine (referencedTable, dataManagerName), rowColumnName_p (rowColumnName), lastRow_p (-1) { setSuffix ("_Row"); } ForwardColumnIndexedRowEngine::ForwardColumnIndexedRowEngine (const Table& referencedTable, const String& rowColumnName) : ForwardColumnEngine (referencedTable, ""), rowColumnName_p (rowColumnName), lastRow_p (-1) { setSuffix ("_Row"); } ForwardColumnIndexedRowEngine::~ForwardColumnIndexedRowEngine() {} // Clone the engine object. DataManager* ForwardColumnIndexedRowEngine::clone() const { DataManager* dmPtr = new ForwardColumnIndexedRowEngine (refTable(), rowColumnName_p, dataManagerName()); return dmPtr; } DataManagerColumn* ForwardColumnIndexedRowEngine::makeScalarColumn (const String& name, int dataType, const String& dataTypeId) { ForwardColumnIndexedRow* colp = new ForwardColumnIndexedRow (this, name, dataType, dataTypeId, refTable()); addForwardColumn (colp); return colp; } DataManagerColumn* ForwardColumnIndexedRowEngine::makeIndArrColumn (const String& name, int dataType, const String& dataTypeId) { return makeScalarColumn (name, dataType, dataTypeId); } void ForwardColumnIndexedRowEngine::create64 (rownr_t) { // The table is new. baseCreate(); // Define a keyword in the table telling the column containing the // row numbers in the original table. table().rwKeywordSet().define (keywordName ("_ForwardColumn_RowName"), rowColumnName_p); } void ForwardColumnIndexedRowEngine::prepare() { basePrepare(); // Create the ScalarColumn object for the rownr column. // Get its column name from the special table keyword defined in create. const String& rowName = table().keywordSet().asString (keywordName ("_ForwardColumn_RowName")); rowColumn_p.attach (table(), rowName); } void ForwardColumnIndexedRowEngine::reopenRW() {} DataManager* ForwardColumnIndexedRowEngine::makeObject (const String& dataManagerName, const Record& spec) { DataManager* dmPtr = new ForwardColumnIndexedRowEngine (dataManagerName, spec); return dmPtr; } void ForwardColumnIndexedRowEngine::registerClass() { DataManager::registerCtor (className(), makeObject); } String ForwardColumnIndexedRowEngine::dataManagerType() const { return className(); } String ForwardColumnIndexedRowEngine::className() { return "ForwardColumnIndexedRowEngine"; } Record ForwardColumnIndexedRowEngine::dataManagerSpec() const { Record spec = ForwardColumnEngine::dataManagerSpec(); spec.define ("COLUMNNAME", rowColumnName_p); return spec; } ForwardColumnIndexedRow::ForwardColumnIndexedRow (ForwardColumnIndexedRowEngine* enginePtr, const String& name, int dataType, const String& dataTypeId, const Table& refTable) : ForwardColumn (enginePtr, name, dataType, dataTypeId, refTable), enginePtr_p (enginePtr) {} ForwardColumnIndexedRow::~ForwardColumnIndexedRow() {} void ForwardColumnIndexedRow::prepare (const Table& thisTable) { basePrepare (thisTable, False); } void ForwardColumnIndexedRow::setShape (rownr_t, const IPosition&) { throw (DataManInvOper ("setShape not supported by data manager ForwardColumnIndexedRow")); } uInt ForwardColumnIndexedRow::ndim (rownr_t rownr) { return colPtr()->ndim (convertRownr(rownr)); } IPosition ForwardColumnIndexedRow::shape(rownr_t rownr) { return colPtr()->shape (convertRownr(rownr)); } Bool ForwardColumnIndexedRow::isShapeDefined (rownr_t rownr) { return colPtr()->isDefined (convertRownr(rownr)); } Bool ForwardColumnIndexedRow::canChangeShape() const { return False; // put is not supported } void ForwardColumnIndexedRow::getArrayV (rownr_t rownr, ArrayBase& dataPtr) { colPtr()->getArray (convertRownr(rownr), dataPtr); } void ForwardColumnIndexedRow::getSliceV (rownr_t rownr, const Slicer& ns, ArrayBase& dataPtr) { colPtr()->getSlice (convertRownr(rownr), ns, dataPtr); } void ForwardColumnIndexedRow::putArrayV (rownr_t, const ArrayBase&) { throw (DataManInvOper ("putArray not supported by data manager ForwardColumnIndexedRow")); } void ForwardColumnIndexedRow::putSliceV (rownr_t, const Slicer&, const ArrayBase&) { throw (DataManInvOper ("putSlice not supported by data manager ForwardColumnIndexedRow")); } #define FORWARDCOLUMNINDEXEDROW_GETPUT(T,NM) \ void ForwardColumnIndexedRow::aips_name2(get,NM) (rownr_t rownr, T* dataPtr) \ { colPtr()->get (convertRownr(rownr), dataPtr); } \ void ForwardColumnIndexedRow::aips_name2(put,NM) (rownr_t, const T*) \ { \ throw (DataManInvOper \ ("put not supported by data manager ForwardColumnIndexedRow")); \ } FORWARDCOLUMNINDEXEDROW_GETPUT(Bool,Bool) FORWARDCOLUMNINDEXEDROW_GETPUT(uChar,uChar) FORWARDCOLUMNINDEXEDROW_GETPUT(Short,Short) FORWARDCOLUMNINDEXEDROW_GETPUT(uShort,uShort) FORWARDCOLUMNINDEXEDROW_GETPUT(Int,Int) FORWARDCOLUMNINDEXEDROW_GETPUT(uInt,uInt) FORWARDCOLUMNINDEXEDROW_GETPUT(Int64,Int64) FORWARDCOLUMNINDEXEDROW_GETPUT(float,float) FORWARDCOLUMNINDEXEDROW_GETPUT(double,double) FORWARDCOLUMNINDEXEDROW_GETPUT(Complex,Complex) FORWARDCOLUMNINDEXEDROW_GETPUT(DComplex,DComplex) FORWARDCOLUMNINDEXEDROW_GETPUT(String,String) FORWARDCOLUMNINDEXEDROW_GETPUT(void,Other) } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/ForwardColRow.h000066400000000000000000000373141476623553700210160ustar00rootroot00000000000000//# ForwardColRow.h: Virtual Column Engine to forward to other rows/columns //# Copyright (C) 1995,1996,1997,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_FORWARDCOLROW_H #define TABLES_FORWARDCOLROW_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class ForwardColumnIndexedRowEngine; // // Virtual column forwarding to another row/column // // // // // //# Classes you should understand before using this one. //
      • ForwardColumnIndexedRowEngine //
      • ForwardColumn // // // ForwardColumnIndexedRow handles the forwarding of the gets and puts // for an individual row/column on behalf of the virtual column engine // ForwardColumnIndexedRowEngine. It forwards them to a row/column in // another table. The row forwarding is done using a special column // containing row numbers indexing the referenced table. // // // ForwardColumnIndexedRow represents a virtual column which forwards the // gets and puts to a column with the same name in another table. // It is, in fact, a reference to the other column. // The row numbers in the column are mapped to row numbers in the referenced // column using a special column containing the mapping. // The name of the other table is stored as a keyword in the // forwarding column. When the referenced column is in its turn a // ForwardColumn (note: not a ForwardColumnIndexedRow), the table // mentioned in there will be used. In this way, the length of the // forwarding chain is kept to a minimum. // // An object of this class is created (and deleted) by the virtual column // engine // // ForwardColumnIndexedRowEngine // which creates a ForwardColumnIndexedRow object for each column being // forwarded. // class ForwardColumnIndexedRow : public ForwardColumn { public: // Construct it for the given column. ForwardColumnIndexedRow (ForwardColumnIndexedRowEngine* enginePtr, const String& columnName, int dataType, const String& dataTypeId, const Table& referencedTable); // Destructor is mandatory. ~ForwardColumnIndexedRow(); // Copy constructor is not needed and therefore forbidden. ForwardColumnIndexedRow (const ForwardColumnIndexedRow&) = delete; // Assignment is not needed and therefore forbidden. ForwardColumnIndexedRow& operator= (const ForwardColumnIndexedRow&) = delete; // Initialize the object. // This means binding the column to the column with the same name // in the original table. // It checks if the description of both columns is the same. void prepare (const Table& thisTable); private: // This data manager cannot handle changing array shapes. Bool canChangeShape() const; // Set the shape of an (indirect) array in the given row. // This throws an exception, because putting is not supported. void setShape (rownr_t rownr, const IPosition& shape); // Is the value shape defined in the given row? Bool isShapeDefined (rownr_t rownr); // Get the dimensionality of the item in the given row. uInt ndim (rownr_t rownr); // Get the shape of the item in the given row. IPosition shape (rownr_t rownr); // Get the scalar value with a standard data type in the given row. // virtual void getBool (rownr_t rownr, Bool* dataPtr); virtual void getuChar (rownr_t rownr, uChar* dataPtr); virtual void getShort (rownr_t rownr, Short* dataPtr); virtual void getuShort (rownr_t rownr, uShort* dataPtr); virtual void getInt (rownr_t rownr, Int* dataPtr); virtual void getuInt (rownr_t rownr, uInt* dataPtr); virtual void getInt64 (rownr_t rownr, Int64* dataPtr); virtual void getfloat (rownr_t rownr, float* dataPtr); virtual void getdouble (rownr_t rownr, double* dataPtr); virtual void getComplex (rownr_t rownr, Complex* dataPtr); virtual void getDComplex (rownr_t rownr, DComplex* dataPtr); virtual void getString (rownr_t rownr, String* dataPtr); // // Get the scalar value with a non-standard data type in the given row. virtual void getOther (rownr_t rownr, void* dataPtr); // Put the scalar value with a standard data type into the given row. // This throws an exception, because putting is not supported. // virtual void putBool (rownr_t rownr, const Bool* dataPtr); virtual void putuChar (rownr_t rownr, const uChar* dataPtr); virtual void putShort (rownr_t rownr, const Short* dataPtr); virtual void putuShort (rownr_t rownr, const uShort* dataPtr); virtual void putInt (rownr_t rownr, const Int* dataPtr); virtual void putuInt (rownr_t rownr, const uInt* dataPtr); virtual void putInt64 (rownr_t rownr, const Int64* dataPtr); virtual void putfloat (rownr_t rownr, const float* dataPtr); virtual void putdouble (rownr_t rownr, const double* dataPtr); virtual void putComplex (rownr_t rownr, const Complex* dataPtr); virtual void putDComplex (rownr_t rownr, const DComplex* dataPtr); virtual void putString (rownr_t rownr, const String* dataPtr); // // Put the scalar value with a non-standard data type into the given row. // This throws an exception, because putting is not supported. virtual void putOther (rownr_t rownr, const void* dataPtr); // Get the array value in the given row. // The argument dataPtr is in fact a Array&, but a ArrayBase& // is needed to be generic. // The array pointed to by dataPtr has to have the correct shape // (which is guaranteed by the ArrayColumn get function). void getArrayV (rownr_t rownr, ArrayBase& dataPtr); // Put the array value into the given row. // This throws an exception, because putting is not supported. void putArrayV (rownr_t rownr, const ArrayBase& dataPtr); // Get a section of the array in the given row. // The argument dataPtr is in fact a Array&, but a ArrayBase& // is needed to be generic. // The array pointed to by dataPtr has to have the correct shape // (which is guaranteed by the ArrayColumn getSlice function). void getSliceV (rownr_t rownr, const Slicer& slicer, ArrayBase& dataPtr); // Put into a section of the array in the given row. // This throws an exception, because putting is not supported. void putSliceV (rownr_t rownr, const Slicer& slicer, const ArrayBase& dataPtr); // Convert the rownr to the rownr in the underlying table. rownr_t convertRownr (rownr_t rownr); //# Now define the data members. ForwardColumnIndexedRowEngine* enginePtr_p; //# pointer to parent engine }; // // Virtual column engine forwarding to other columns/rows. // // // // // //# Classes you should understand before using this one. //
      • VirtualColumnEngine // // // ForwardColumnIndexedRowEngine is a virtual column engine which // forwards the gets and puts of columns to corresponding columns // in another table. Furthermore it maps the row number by indexing // the row number in the referenced table. // // // ForwardColumnIndexedRowEngine is a data manager which forwards // the gets and puts of columns to columns with the same names in // another table. In that sense it is the same as the virtual column engine // // ForwardColumnEngine. // However, it also forwards the row number. That is, it uses a column // containing row numbers to index the correct row in the referenced table. // The name of this column and the name of the referenced table have to // be given when constructing the engine. // // For example:
        // Table TABA contains columns A, B and C and consists of N rows. // Table TABF uses ForwardColumnIndexedRowEngine to forward its columns // A, B and C to the corresponding columns in TABA. Furthermore it // contains a column ROW containing row numbers in TABA. This column is // the mapping of row numbers in TABF to rows in TABA. E.g. if ROW has // the value 25 in row 10, row 10 of TABF is forwarded to row 25 in TABA. // // Actually, puts are not possible. When multiple rows map to the same row // in the referenced table, putting a value in one row would also change // the value in another row referencing the same underlying row. This // could result in unexpected behaviour. // // The engine consists of a set of // // ForwardColumnIndexedRow // objects, which handle the actual gets. //
        // // In some ways it overlaps the functionality of the storage manager // StManMirAIO. They both allow to have the same value used by multiple // rows. However, StManMirAIO only allows that for consecutive rows, // while this engine allows it for any row. On the other side, // StManMirAIO is faster. // // // // // The original table. // Table tab("someTable"); // // Create another table with the same description. // SetupNewTable newtab("tForwardColRow.data", tab.tableDesc(), Table::New); // // Create an engine which forwards to the original table and uses // // column rowColumn to get the row number in the referenced table. // // Bind all columns in the new table to the forwarding engine. // ForwardColumnIndexedRowEngine fce(tab, "rowColumn"); // newtab.bindAll (fce); // // Create the new table. // // Every get and put on this table is forwarded to the original table. // // NB. Puts cannot be done here, because the original table was // // opened as readonly. // // Of course, some columns could have been bound to another // // data manager (storage manager, calibration engine, ...). // Table forwTab(newtab); // // class ForwardColumnIndexedRowEngine : public ForwardColumnEngine { public: // The default constructor is required for reconstruction of the // engine when a table is read back. ForwardColumnIndexedRowEngine (const String& dataManagerName, const Record& spec); // Create the engine. // The columns using this engine will reference the given table. // The column with the given name contains the row number mapping, // i.e. a row number in a get or put is converted to a row number // in the referenced table using the value in this column. // The data manager gets the given name. ForwardColumnIndexedRowEngine (const Table& referencedTable, const String& rowColumnName, const String& dataManagerName); // Create the engine. // The columns using this engine will reference the given table. // The column with the given name contains the row number mapping, // i.e. a row number in a get or put is converted to a row number // in the referenced table using the value in this column. // The data manager has no name. ForwardColumnIndexedRowEngine (const Table& referencedTable, const String& rowColumnName); // Destructor is mandatory. ~ForwardColumnIndexedRowEngine(); // The copy constructor is forbidden. ForwardColumnIndexedRowEngine (const ForwardColumnIndexedRowEngine&) = delete; // Assignment is forbidden. ForwardColumnIndexedRowEngine& operator= (const ForwardColumnIndexedRowEngine&) = delete; // Clone the engine object. DataManager* clone() const; // Return the type name of the engine // (i.e. its class name ForwardColumnIndexedRowEngine). String dataManagerType() const; // Record a record containing data manager specifications. virtual Record dataManagerSpec() const; // Return the name of the class. static String className(); // Register the class name and the static makeObject "constructor". // This will make the engine known to the table system. static void registerClass(); private: // Create the column object for the scalar column in this engine. DataManagerColumn* makeScalarColumn (const String& columnName, int dataType, const String& dataTypeId); // Create the column object for the indirect array column in this engine. DataManagerColumn* makeIndArrColumn (const String& columnName, int dataType, const String& dataTypeId); // Initialize the object for a new table. // It defines the column keywords containing the name of the // original table, which can be the parent of the referenced table. // It also defines a keyword containing the row column name. void create64 (rownr_t initialNrrow); // Initialize the engine. // It gets the name of the original table(s) from the column keywords, // opens those tables and attaches the ForwardColumnIndexedRow objects // to the columns in those tables. void prepare(); // Reopen the engine for read/write access. // This cannot be done, so all columns remain readonly. // The function is needed to override the behaviour of its base class. void reopenRW(); // Define the column with the row numbers (must have data type uInt). String rowColumnName_p; ScalarColumn rowColumn_p; // Define the various engine column objects. PtrBlock refColumns_p; // Cache of last row used to get row number. Int64 lastRow_p; rownr_t rowNumber_p; public: // Define the "constructor" to construct this engine when a // table is read back. // This "constructor" has to be registered by the user of the engine. // If the engine is commonly used, its registration can be added // into the registerAllCtor function in DataManReg.cc. // This function gets automatically invoked by the table system. static DataManager* makeObject (const String& dataManagerName, const Record& spec); // Convert the rownr to the rownr in the underlying table. rownr_t convertRownr (rownr_t rownr); }; inline rownr_t ForwardColumnIndexedRowEngine::convertRownr (rownr_t rownr) { if (Int64(rownr) != lastRow_p) { rowNumber_p = rowColumn_p(rownr); lastRow_p = rownr; } return rowNumber_p; } inline rownr_t ForwardColumnIndexedRow::convertRownr (rownr_t rownr) { return enginePtr_p->convertRownr (rownr); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/ISMBase.cc000066400000000000000000000525771476623553700176550ustar00rootroot00000000000000//# ISMBase.cc: Base class of the Incremental Storage Manager //# Copyright (C) 1996,1997,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ISMBase::ISMBase (uInt bucketSize, Bool checkBucketSize, uInt cacheSize) : DataManager (), /// dataManName_p ("ISM0"), version_p (3), iosfile_p (0), uniqnr_p (0), cache_p (0), file_p (0), index_p (0), persCacheSize_p (cacheSize), cacheSize_p (0), nbucketInit_p (1), nFreeBucket_p (0), firstFree_p (-1), bucketSize_p (bucketSize), checkBucketSize_p (checkBucketSize), dataChanged_p (False), tempBuffer_p (0) {} ISMBase::ISMBase (const String& dataManagerName, uInt bucketSize, Bool checkBucketSize, uInt cacheSize) : DataManager (), dataManName_p (dataManagerName), version_p (3), iosfile_p (0), uniqnr_p (0), cache_p (0), file_p (0), index_p (0), persCacheSize_p (cacheSize), cacheSize_p (0), nbucketInit_p (1), nFreeBucket_p (0), firstFree_p (-1), bucketSize_p (bucketSize), checkBucketSize_p (checkBucketSize), dataChanged_p (False), tempBuffer_p (0) {} ISMBase::ISMBase (const String& dataManagerName, const Record& spec) : DataManager (), dataManName_p (dataManagerName), version_p (3), iosfile_p (0), uniqnr_p (0), cache_p (0), file_p (0), index_p (0), persCacheSize_p (1), cacheSize_p (0), nbucketInit_p (1), nFreeBucket_p (0), firstFree_p (-1), bucketSize_p (32768), checkBucketSize_p (False), dataChanged_p (False), tempBuffer_p (0) { if (spec.isDefined ("BUCKETSIZE")) { bucketSize_p = spec.asInt ("BUCKETSIZE"); } if (spec.isDefined ("CHECKBUCKETSIZE")) { checkBucketSize_p = spec.asBool ("CHECKBUCKETSIZE"); } if (spec.isDefined ("PERSCACHESIZE")) { persCacheSize_p = spec.asuInt ("PERSCACHESIZE"); } } ISMBase::ISMBase (const ISMBase& that) : DataManager (), dataManName_p (that.dataManName_p), version_p (that.version_p), iosfile_p (0), uniqnr_p (0), cache_p (0), file_p (0), index_p (0), persCacheSize_p (that.persCacheSize_p), cacheSize_p (that.cacheSize_p), nbucketInit_p (1), nFreeBucket_p (0), firstFree_p (-1), bucketSize_p (that.bucketSize_p), checkBucketSize_p (that.checkBucketSize_p), dataChanged_p (False), tempBuffer_p (0) {} ISMBase::~ISMBase() { for (uInt i=0; i(this)->getCache(); Record rec; rec.define ("MaxCacheSize", Int(cacheSize_p)); return rec; } void ISMBase::setProperties (const Record& rec) { if (rec.isDefined("MaxCacheSize")) { setCacheSize (rec.asInt("MaxCacheSize"), False); } } void ISMBase::clearCache() { if (cache_p != 0) { cache_p->clear(); } } void ISMBase::showCacheStatistics (ostream& os) const { if (cache_p != 0) { os << ">>> IncrementalStMan cache statistics:" << endl; cache_p->showStatistics (os); os << "<<<" << endl; } } void ISMBase::showIndexStatistics (ostream& os) { if (index_p != 0) { index_p->show (os); } } void ISMBase::showBucketLayout (ostream& os) { uInt cursor=0; rownr_t bstrow=0; rownr_t bnrow; uInt bucketNr; while (getIndex().nextBucketNr (cursor, bstrow, bnrow, bucketNr)) { os << " bucket strow=" << bstrow << " bucketnr=" << bucketNr << endl; ((ISMBucket*) (getCache().getBucket (bucketNr)))->show (os); } } DataManagerColumn* ISMBase::makeScalarColumn (const String&, int dataType, const String&) { //# Extend colSet_p block if needed. if (ncolumn() >= colSet_p.nelements()) { colSet_p.resize (colSet_p.nelements() + 32); } ISMColumn* colp = new ISMColumn (this, dataType, ncolumn()); colSet_p[ncolumn()] = colp; return colp; } DataManagerColumn* ISMBase::makeDirArrColumn (const String& name, int dataType, const String& dataTypeId) { return makeScalarColumn (name, dataType, dataTypeId); } DataManagerColumn* ISMBase::makeIndArrColumn (const String&, int dataType, const String&) { //# Extend colSet_p block if needed. if (ncolumn() >= colSet_p.nelements()) { colSet_p.resize (colSet_p.nelements() + 32); } ISMColumn* colp = new ISMIndColumn (this, dataType, ncolumn()); colSet_p[ncolumn()] = colp; return colp; } DataManager* ISMBase::makeObject (const String& group, const Record& spec) { // This function is called when reading a table back. // Construct it with the default bucket size and cache size. return new ISMBase (group, spec); } void ISMBase::setCacheSize (uInt cacheSize, Bool canExceedNrBuckets) { cacheSize_p = cacheSize; // Limit the cache size if needed. if (!canExceedNrBuckets && cacheSize_p > getCache().nBucket()) { cacheSize_p = cache_p->nBucket(); } if (cache_p != 0) { cache_p->resize (cacheSize_p); } } void ISMBase::makeCache() { if (cache_p == 0) { makeIndex(); // Set cache size to persistent cache size if not set explicitly yet. if (cacheSize_p == 0) { cacheSize_p = persCacheSize_p; } cache_p = new BucketCache (file_p, 512, bucketSize_p, nbucketInit_p, cacheSize_p, this, ISMBucket::readCallBack, ISMBucket::writeCallBack, ISMBucket::initCallBack, ISMBucket::deleteCallBack); cache_p->resync (nbucketInit_p, nFreeBucket_p, firstFree_p); // Allocate a buffer for temporary storage by all ISM classes. if (tempBuffer_p == 0) { tempBuffer_p = new char [bucketSize_p]; } } } void ISMBase::makeIndex() { if (index_p != 0) { return; } index_p = new ISMIndex(); file_p->open(); readIndex(); } void ISMBase::readIndex() { file_p->seek (0); // Use the file given by the BucketFile object. std::shared_ptr fio = file_p->makeFilebufIO (1024); std::shared_ptr tio; // It is stored in canonical or local format. if (asBigEndian()) { tio.reset (new CanonicalIO (fio)); }else{ tio.reset (new LECanonicalIO (fio)); } AipsIO os (tio); uInt version = os.getstart ("IncrementalStMan"); //# ISMBase.cc version 11.1 contained a little error. //# It used the version of putstart("IncrementalStMan") instead of //# the version from putstart("ISM"). //# The incorrect one used "IncrementalStMan",3 and "ISM",1. //# The fixed one uses "IncrementalStMan",2 and "ISM",3. //# This newest one uses "IncrementalStMan",4 and "ISM",3. //# It was fixed immediately after a weekly inhale, but unfortunately //# the TMS system in Westerbork used that version for a while //# without applying the fix. //# Therefore this hack (together with one in function open) is needed //# to make these MSs accessible. if (version == 3) { version_p = 3; } Bool bigEndian = True; if (version >= 5) { os >> bigEndian; } if (bigEndian != asBigEndian()) { throw DataManError("Endian flag in ISM mismatches the table flag"); } os >> bucketSize_p; os >> nbucketInit_p; os >> persCacheSize_p; os >> uniqnr_p; if (version > 1) { os >> nFreeBucket_p; os >> firstFree_p; } os.getend(); Int64 off = nbucketInit_p; os.setpos (512 + off * bucketSize_p); index_p->get (os); os.close(); } void ISMBase::writeIndex() { if (index_p == 0) { return; } uInt nbuckets = getCache().nBucket(); // Write a few items at the beginning of the file. file_p->seek (0); // Use the file given by the BucketFile object. std::shared_ptr fio = file_p->makeFilebufIO (1024); std::shared_ptr tio; // Store it in canonical or local format. if (asBigEndian()) { tio.reset (new CanonicalIO (fio)); }else{ tio.reset (new LECanonicalIO (fio)); } AipsIO os (tio); // The endian switch is a new feature. So only put it if little endian // is used. In that way older software can read newer tables. if (asBigEndian()) { os.putstart ("IncrementalStMan", 4); } else { os.putstart ("IncrementalStMan", 5); os << asBigEndian(); } os << bucketSize_p; os << nbuckets; os << persCacheSize_p; os << uniqnr_p; os << getCache().nFreeBucket(); os << getCache().firstFreeBucket(); os.putend(); // Write the index itself at the very end of the file. Int64 off = nbuckets; os.setpos (512 + off * bucketSize_p); index_p->put (os); os.close(); } ISMBucket* ISMBase::getBucket (rownr_t rownr, rownr_t& bucketStartRow, rownr_t& bucketNrrow) { uInt bucketNr = getIndex().getBucketNr (rownr, bucketStartRow, bucketNrrow); return (ISMBucket*) (getCache().getBucket (bucketNr)); } ISMBucket* ISMBase::nextBucket (uInt& cursor, rownr_t& bucketStartRow, rownr_t& bucketNrrow) { uInt bucketNr; if (getIndex().nextBucketNr (cursor, bucketStartRow, bucketNrrow, bucketNr)) { return (ISMBucket*) (getCache().getBucket (bucketNr)); } return 0; } void ISMBase::setBucketDirty() { cache_p->setDirty(); dataChanged_p = True; } void ISMBase::addBucket (rownr_t rownr, ISMBucket* bucket) { // Add the bucket to the cache and the index. // It's the last bucket in the cache. uInt bucketNr = getCache().addBucket ((char*)bucket); getIndex().addBucketNr (rownr, bucketNr); } //# The storage manager can add rows. Bool ISMBase::canAddRow() const { return True; } //# The storage manager can delete rows. Bool ISMBase::canRemoveRow() const { return True; } //# The storage manager cannot add columns (not yet). Bool ISMBase::canAddColumn() const { return False; } //# The storage manager cannot delete columns (not yet). Bool ISMBase::canRemoveColumn() const { return False; } void ISMBase::addRow64 (rownr_t nrrow) { getIndex().addRow (nrrow); uInt nrcol = ncolumn(); for (uInt i=0; iaddRow (nrrow_p + nrrow, nrrow_p); } nrrow_p += nrrow; dataChanged_p = True; } void ISMBase::removeRow64 (rownr_t rownr) { // Get the bucket and interval to which the row belongs. uInt i; rownr_t bucketStartRow; rownr_t bucketNrrow; ISMBucket* bucket = getBucket (rownr, bucketStartRow, bucketNrrow); uInt bucketRownr = rownr - bucketStartRow; // Remove that row from the bucket for all columns. uInt nrcol = ncolumn(); for (i=0; iremove (bucketRownr, bucket, bucketNrrow, nrrow_p-1); } // Remove the row from the index. Int emptyBucket = getIndex().removeRow (rownr); nrrow_p--; // When no more rows left, recreate index and cache. if (nrrow_p == 0) { recreate(); }else{ // Remove the bucket if it is empty now. if (emptyBucket >= 0) { getCache().getBucket (emptyBucket); getCache().removeBucket(); } } dataChanged_p = True; } // Note that the column has already been added by makeXXColumn. // This function is merely for initializing the added column. void ISMBase::addColumn (DataManagerColumn* colp) { // AddColumn is not possible yet. throw (DataManInvOper ("IncrementalStMan::addColumn not possible yet")); for (uInt i=0; idoCreate ((ISMBucket*)(getCache().getBucket (0))); dataChanged_p = True; return; } } throw (DataManInternalError ("ISMBase::addColumn")); } void ISMBase::removeColumn (DataManagerColumn* colp) { // RemoveColumn is not possible yet. throw (DataManInvOper ("IncrementalStMan::removeColumn not possible yet")); for (uInt i=0; idoCreate ((ISMBucket*)(getCache().getBucket (0))); } setBucketDirty(); } Bool ISMBase::hasMultiFileSupport() const { return True; } Bool ISMBase::flush (AipsIO& ios, Bool fsync) { //# Let the column objects flush themselves (if needed). //# Check if anything has changed. Bool changed = False; uInt nrcol = ncolumn(); for (uInt i=0; iflush (nrrow_p, fsync)) { changed = True; } } if (cache_p != 0) { cache_p->flush(); } if (dataChanged_p) { writeIndex(); if (fsync) { file_p->fsync(); } changed = True; dataChanged_p = False; } ios.putstart ("ISM", version_p); ios << dataManName_p; ios.putend(); return changed; } rownr_t ISMBase::resync64 (rownr_t nrrow) { nrrow_p = nrrow; if (index_p != 0) { readIndex(); } if (cache_p != 0) { cache_p->resync (nbucketInit_p, nFreeBucket_p, firstFree_p); } uInt nrcol = ncolumn(); for (uInt i=0; iresync (nrrow_p); } if (iosfile_p != 0) { iosfile_p->resync(); } return nrrow_p; } void ISMBase::create64 (rownr_t nrrow) { init(); recreate(); nrrow_p = 0; addRow64 (nrrow); } rownr_t ISMBase::open64 (rownr_t tabNrrow, AipsIO& ios) { nrrow_p = tabNrrow; // Do not check the bucketsize for an existing table. checkBucketSize_p = False; version_p = ios.getstart ("ISM"); ios >> dataManName_p; ios.getend(); init(); file_p = new BucketFile (fileName(), table().isWritable(), 0, False, multiFile()); //# Westerbork MSs have a problem, because TMS used for a while //# the erroneous version of ISMBase.cc. //# So if we have an old ISM version, do a makeIndex to get //# the version from the index. That was 3. if (version_p == 1) { makeIndex(); } //# Let the column objects initialize themselves (if needed). uInt nrcol = ncolumn(); for (uInt i=0; igetFile (nrrow_p); } return nrrow_p; } StManArrayFile* ISMBase::openArrayFile (ByteIO::OpenOption opt) { if (iosfile_p == 0) { iosfile_p = new StManArrayFile (fileName() + 'i', opt, 1, asBigEndian(), 0, multiFile()); } return iosfile_p; } void ISMBase::reopenRW() { file_p->setRW(); uInt nrcol = ncolumn(); for (uInt i=0; ireopenRW(); } } void ISMBase::deleteManager() { delete iosfile_p; iosfile_p = 0; // Clear cache without flushing. if (cache_p != 0) { cache_p->clear (0, False); } if (file_p != 0) { file_p->remove(); delete file_p; file_p = 0; } } void ISMBase::init() { // Determine the size of a uInt in external format. uIntSize_p = ValType::getCanonicalSize (TpUInt, asBigEndian()); rownrSize_p = sizeof(rownr_t); // Get the total length for all columns. // Use 32 for each variable length element. // On top of that each variable length element requires uIntSize_p bytes // and uIntSize_p for all elements together (representing total length // and length per element). uInt fixedSize = 0; uInt varSize = 0; uInt nrcol = ncolumn(); uInt headerSize = uIntSize_p * (nrcol + 1); // needed per column for (uInt i=0; igetFixedLength(); fixedSize += 2 * uIntSize_p; // indices per column if (leng == 0) { uInt nr = colSet_p[i]->nelements(); fixedSize += uIntSize_p * (nr + 1); // length values varSize += 32 * nr; }else{ fixedSize += leng; } } if (checkBucketSize_p && bucketSize_p > 0) { // The bucket size is defined. Check if at least 2 // fixed-length items for each row fit in it. // When the bucket is smaller than 32768 bytes, check // if can hold at least 10 rows (since it makes no sense to // have very small buckets). if (bucketSize_p < headerSize + 2*fixedSize) { throw (DataManError ("IncrementalStMan: bucket too small " "to hold 2 rows")); }else if (bucketSize_p < 32768) { if (bucketSize_p < headerSize + 10*fixedSize) { throw (DataManError ("IncrementalStMan: bucket < 32768 and " "too small to hold 10 rows")); } } } if (bucketSize_p == 0) { // Calculate the bucket size. // Try to fit 100 rows (with a minimum of 32768 bytes). // If that results in a very large size (> 327680) try to fit // 10 rows. If that still results in a large size, use 327680 // but at least 2 rows have to fit in it. bucketSize_p = headerSize + 100 * (fixedSize + varSize); if (bucketSize_p < 32768) { bucketSize_p = 32768; } else if (bucketSize_p > 327680) { bucketSize_p = headerSize + 10 * (fixedSize + varSize); if (bucketSize_p > 327680) { bucketSize_p = headerSize + 2 * (fixedSize + varSize); if (bucketSize_p < 327680) { bucketSize_p = 327680; } } } } // Only 28 bits can be used for the offset, so the bucketsize should // not be larger. if (bucketSize_p >= 1<<28) { throw DataManError("IncrementalStMan: bucketSize exceeds 28 bits" " (>= 268435456)"); } } Bool ISMBase::checkBucketLayout (uInt& offendingCursor, rownr_t& offendingBucketStartRow, uInt& offendingBucketNrow, uInt& offendingBucketNr, uInt& offendingCol, uInt& offendingIndex, rownr_t& offendingRow, rownr_t& offendingPrevRow) { Bool ok = False; uInt cursor = 0; rownr_t bucketStartRow = 0; rownr_t bucketNrow = 0; uInt bucketNr = 0; while (getIndex().nextBucketNr(cursor, bucketStartRow, bucketNrow, bucketNr)) { ok = ((ISMBucket*) (getCache().getBucket(bucketNr)))->check(offendingCol, offendingIndex, offendingRow, offendingPrevRow); if (not ok) { offendingCursor = cursor; offendingBucketStartRow = bucketStartRow; offendingBucketNrow = bucketNrow; offendingBucketNr = bucketNr; return False; } } return True; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/ISMBase.h000066400000000000000000000350611476623553700175040ustar00rootroot00000000000000//# ISMBase.h: Base class of the Incremental Storage Manager //# Copyright (C) 1996,1997,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_ISMBASE_H #define TABLES_ISMBASE_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations class BucketCache; class BucketFile; class ISMBucket; class ISMIndex; class ISMColumn; class StManArrayFile; // // Base class of the Incremental Storage Manager // // // // // //# Classes you should understand before using this one. //
      • IncrementalStMan //
      • ISMColumn // // // ISMBase is the base class of the Incremental Storage Manager. // // // The behaviour of this class is described in // IncrementalStMan. // // The public interface of ISMBase is quite large, because the other // internal ISM classes need these functions. To have a class with a // minimal interface for the normal user, class IncrementalStMan // is derived from it. //
        IncrementalStMan needs an isA- instead of hasA-relation to be // able to bind columns to it in class // SetupNewTable. //
        // //# A List of bugs, limitations, extensions or planned refinements. //
      • Removed AipsIO argument from open and close. // class ISMBase: public DataManager { public: // Create an incremental storage manager without a name. // The bucket size has to be given in bytes and the cache size in buckets. // The bucket size is checked or calculated (if 0) as described in // IncrementalStMan.h. explicit ISMBase (uInt bucketSize = 0, Bool checkBucketSize = True, uInt cacheSize = 1); // Create an incremental storage manager with the given name. // The bucket size has to be given in bytes and the cache size in buckets. // The bucket size is checked or calculated (if 0) as described in // IncrementalStMan.h. ISMBase (const String& dataManagerName, uInt bucketSize, Bool checkBucketSize, uInt cacheSize); // Create an incremental storage manager with the given name. // The specifications are in the record (as created by dataManagerSpec). ISMBase (const String& aDataManName, const Record& spec); ~ISMBase(); // Assignment cannot be used. ISMBase& operator= (const ISMBase& that) = delete; // Clone this object. // It does not clone ISMColumn objects possibly used. // The caller has to delete the newly created object. virtual DataManager* clone() const; // Get the type name of the data manager (i.e. IncrementalStMan). virtual String dataManagerType() const; // Get the name given to the storage manager (in the constructor). virtual String dataManagerName() const; // Record a record containing data manager specifications. virtual Record dataManagerSpec() const; // Get data manager properties that can be modified. // It is only ActualCacheSize (the actual cache size in buckets). // It is a subset of the data manager specification. virtual Record getProperties() const; // Modify data manager properties. // Only MaxCacheSize can be used. It is similar to function setCacheSize // with canExceedNrBuckets=False. virtual void setProperties (const Record& spec); // Get the version of the class. uInt version() const; // Set the cache size (in buckets). // If canExceedNrBuckets=True, the given cache size can be // larger than the nr of buckets in the file. In this way the cache can // be made large enough for a future file extnsion. // Otherwise, it is limited to the actual number of buckets. This is useful // if one wants the entire file to be cached. void setCacheSize (uInt cacheSize, Bool canExceedNrBuckets); // Get the current cache size (in buckets). uInt cacheSize() const; // Clear the cache used by this storage manager. // It will flush the cache as needed and remove all buckets from it. void clearCache(); // Show the statistics of all caches used. virtual void showCacheStatistics (ostream& os) const; // Show the index statistics. void showIndexStatistics (ostream& os); // Show the layout of the buckets void showBucketLayout (ostream& os); // Get the bucket size (in bytes). uInt bucketSize() const; // Get the size of a uInt in external format (can be canonical or local). uInt uIntSize() const; // Get the size of a rownr in external format (can be canonical or local). uInt rownrSize() const; // Get the bucket containing the given row. // Also return the first and last row of that bucket. // The bucket object is created and deleted by the caching mechanism. ISMBucket* getBucket (rownr_t rownr, rownr_t& bucketStartRow, rownr_t& bucketNrrow); // Get the next bucket. // cursor=0 indicates the start of the iteration. // The first bucket returned is the bucket containing the rownr // given in bucketStartRow. // After each iteration BucketStartRow and bucketNrrow are set. // A 0 is returned when no more buckets. // The bucket object is created and deleted by the caching mechanism. ISMBucket* nextBucket (uInt& cursor, rownr_t& bucketStartRow, rownr_t& bucketNrrow); // Get access to the temporary buffer. char* tempBuffer() const; // Get a unique column number for the column // (it is only unique for this storage manager). // This is used by ISMColumnIndArr to create a unique file name. uInt uniqueNr(); // Get the number of rows in this storage manager. rownr_t nrow() const; // Can the storage manager add rows? (yes) virtual Bool canAddRow() const; // Can the storage manager delete rows? (yes) virtual Bool canRemoveRow() const; // Can the storage manager add columns? (not yet) virtual Bool canAddColumn() const; // Can the storage manager delete columns? (not yet) virtual Bool canRemoveColumn() const; // Make the object from the type name string. // This function gets registered in the DataManager "constructor" map. // The caller has to delete the object. static DataManager* makeObject (const String& dataManagerType, const Record& spec); // Get access to the given column. ISMColumn& getColumn (uInt colnr); // Add a bucket to the storage manager (i.e. to the cache). // The pointer is taken over. void addBucket (rownr_t rownr, ISMBucket* bucket); // Make the current bucket in the cache dirty (i.e. something has been // changed in it and it needs to be written when removed from the cache). // (used by ISMColumn::putValue). void setBucketDirty(); // Open (if needed) the file for indirect arrays with the given mode. // Return a pointer to the object. StManArrayFile* openArrayFile (ByteIO::OpenOption opt); // Check that there are no repeated rowIds in the buckets comprising this ISM. Bool checkBucketLayout (uInt& offendingCursor, rownr_t& offendingBucketStartRow, uInt& offendingBucketNrow, uInt& offendingBucketNr, uInt& offendingCol, uInt& ffendingIndex, rownr_t& offendingRow, rownr_t& offendingPrevRow); private: // Copy constructor (only meant for clone function). ISMBase (const ISMBase& that); // (Re)create the index, file, and cache object. void recreate(); // The data manager supports use of MultiFile. virtual Bool hasMultiFileSupport() const; // Flush and optionally fsync the data. // It returns a True status if it had to flush (i.e. if data have changed). virtual Bool flush (AipsIO&, Bool fsync); // Let the storage manager create files as needed for a new table. // This allows a column with an indirect array to create its file. virtual void create64 (rownr_t nrrow); // Open the storage manager file for an existing table, read in // the data, and let the ISMColumn objects read their data. virtual rownr_t open64 (rownr_t nrrow, AipsIO&); // Resync the storage manager with the new file contents. // This is done by clearing the cache. virtual rownr_t resync64 (rownr_t nrrow); // Reopen the storage manager files for read/write. virtual void reopenRW(); // The data manager will be deleted (because all its columns are // requested to be deleted). // So clean up the things needed (e.g. delete files). virtual void deleteManager(); // Let the storage manager initialize itself. // It is used by create and open. void init(); // Add rows to the storage manager. // Per column it extends the interval for which the last value written // is valid. virtual void addRow64 (rownr_t nrrow); // Delete a row from all columns. virtual void removeRow64 (rownr_t rownr); // Do the final addition of a column. // The DataManagerColumn object has already been created // (by the makeXXColumn function) and added to // colSet_p. However, it still has to be added to the // data files, which is done by this function. It uses the // pointer to find the correct column in the colSet_p. virtual void addColumn (DataManagerColumn*); // Remove a column from the data file and the colSet_p. // The DataManagerColumn object gets deleted.. virtual void removeColumn (DataManagerColumn*); // Create a column in the storage manager on behalf of a table column. // The caller has to delete the newly created object. // // Create a scalar column. virtual DataManagerColumn* makeScalarColumn (const String& name, int dataType, const String& dataTypeID); // Create a direct array column. virtual DataManagerColumn* makeDirArrColumn (const String& name, int dataType, const String& dataTypeID); // Create an indirect array column. virtual DataManagerColumn* makeIndArrColumn (const String& name, int dataType, const String& dataTypeID); // // Get the cache object. // This will construct the cache object if not present yet. // The cache object will be deleted by the destructor. BucketCache& getCache(); // Get the index object. // This will construct the index object if not present yet. // The index object will be deleted by the destructor. ISMIndex& getIndex(); // Construct the cache object (if not constructed yet). void makeCache(); // Construct the index object (if not constructed yet) and read it. void makeIndex(); // Read the index (at the end of the file). void readIndex(); // Write the index (at the end of the file). void writeIndex(); //# Declare member variables. // Name of data manager. String dataManName_p; // The version of the class. uInt version_p; // The file containing the indirect arrays. StManArrayFile* iosfile_p; // Unique nr for column in this storage manager. uInt uniqnr_p; // The number of rows in the columns. rownr_t nrrow_p; // The assembly of all columns. PtrBlock colSet_p; // The cache with the ISM buckets. BucketCache* cache_p; // The file containing all data. BucketFile* file_p; // The ISM bucket index. ISMIndex* index_p; // The persistent cache size. uInt persCacheSize_p; // The actual cache size. uInt cacheSize_p; // The initial number of buckets in the cache. uInt nbucketInit_p; // The nr of free buckets. uInt nFreeBucket_p; // The first free bucket. Int firstFree_p; // The bucket size. uInt bucketSize_p; // Check a positive bucketsize? Bool checkBucketSize_p; // Has the data changed since the last flush? Bool dataChanged_p; // The size of a uInt in external format (local or canonical). uInt uIntSize_p; // The size of a rownr in external format (local or canonical). uInt rownrSize_p; // A temporary read/write buffer (also for other classes). char* tempBuffer_p; }; inline uInt ISMBase::version() const { return version_p; } inline uInt ISMBase::cacheSize() const { return cacheSize_p; } inline uInt ISMBase::uniqueNr() { return uniqnr_p++; } inline rownr_t ISMBase::nrow() const { return nrrow_p; } inline uInt ISMBase::bucketSize() const { return bucketSize_p; } inline uInt ISMBase::uIntSize() const { return uIntSize_p; } inline uInt ISMBase::rownrSize() const { return rownrSize_p; } inline char* ISMBase::tempBuffer() const { return tempBuffer_p; } inline BucketCache& ISMBase::getCache() { if (cache_p == 0) { makeCache(); } return *cache_p; } inline ISMIndex& ISMBase::getIndex() { if (index_p == 0) { makeIndex(); } return *index_p; } inline ISMColumn& ISMBase::getColumn (uInt colnr) { return *(colSet_p[colnr]); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/ISMBucket.cc000066400000000000000000000530411476623553700202030ustar00rootroot00000000000000//# ISMBucket.cc: A bucket in the Incremental Storage Manager //# Copyright (C) 1996,1997,1999,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ISMBucket::ISMBucket (ISMBase* parent, const char* bucketStorage) : stmanPtr_p (parent), uIntSize_p (parent->uIntSize()), rownrSize_p(parent->rownrSize()), dataLeng_p (0), indexLeng_p(0), rowIndex_p (parent->ncolumn(), static_cast*>(0)), offIndex_p (parent->ncolumn(), static_cast*>(0)), indexUsed_p(parent->ncolumn(), (uInt)0) { uInt nrcol = stmanPtr_p->ncolumn(); for (uInt i=0; i; offIndex_p[i] = new Block; } // Get the initial index length. // This consists of the offset at the beginning of the bucket // and #entries for each column. indexLeng_p = uIntSize_p + nrcol * uIntSize_p; // Allocate a buffer for the data. data_p = new char[stmanPtr_p->bucketSize()]; AlwaysAssert (data_p != 0, AipsError); // Read the row index for all columns (for an existing bucket). if (bucketStorage != 0) { read (bucketStorage); } } ISMBucket::~ISMBucket() { uInt nrcol = stmanPtr_p->ncolumn(); for (uInt i=0; incolumn(); for (uInt i=0; iresize (nused); offIndex_p[i]->resize (nused); for (uInt j=0; j& rowIndex = *(rowIndex_p[colnr]); Bool found; uInt inx = binarySearchBrackets (found, rowIndex, rownr, indexUsed_p[colnr]); uInt index = inx; // If no exact match, start of interval is previous index. if (!found) { inx--; } offset = (*(offIndex_p[colnr]))[inx]; start = rowIndex[inx]; // End of interval is start of next interval, but it is the last // row in the bucket if it is the last interval. inx++; if (inx == indexUsed_p[colnr]) { end = bucketNrrow; }else{ end = rowIndex[inx]; } end--; return index; } Bool ISMBucket::canReplaceData (uInt newLeng, uInt oldLeng) const { if (dataLeng_p + newLeng - oldLeng + indexLeng_p <= stmanPtr_p->bucketSize()) { return True; } return False; } void ISMBucket::replaceData (uInt& offset, const char* data, uInt newLeng, uInt oldLeng) { #ifdef AIPS_TRACE cout << " replace at offset "<& rowIndex = *(rowIndex_p[colnr]); Block& offIndex = *(offIndex_p[colnr]); uInt nrused = indexUsed_p[colnr]; DebugAssert ((index == 0 || rowIndex[index-1] < rownr) && (index <= nrused) && (index == nrused || rowIndex[index] >= rownr), AipsError); // Extend blocks if needed. if (offIndex.nelements() <= nrused) { rowIndex.resize (nrused + 32); offIndex.resize (nrused + 32); } // Increment row if the same row is being added. if (index < nrused && rownr == rowIndex[index]) { rowIndex[index]++; } // Shift to the right. for (uInt i=nrused; i>index; i--) { rowIndex[i] = rowIndex[i-1]; offIndex[i] = offIndex[i-1]; } // Insert the new row number. indexLeng_p += uIntSize_p + rownrSize_p; indexUsed_p[colnr]++; rowIndex[index] = rownr; offIndex[index] = insertData (data, leng); } uInt ISMBucket::getLength (uInt fixedLength, const char* data) const { if (fixedLength != 0) { return fixedLength; } // Get the data item length if it is variable. uInt leng; Conversion::ValueFunction* readuInt = ISMColumn::getReaduInt (stmanPtr_p->asBigEndian()); readuInt (&leng, data, 1); return leng; } void ISMBucket::shiftLeft (uInt index, uInt nr, Block& rowIndex, Block& offIndex, uInt& nused, uInt leng) { #ifdef AIPS_TRACE cout<<" shift left "< index + nr) { objmove (&rowIndex[index], &rowIndex[index+nr], nused - index - nr); objmove (&offIndex[index], &offIndex[index+nr], nused - index - nr); } indexLeng_p -= nr * (uIntSize_p + rownrSize_p); nused -= nr; } void ISMBucket::removeData (uInt offset, uInt leng) { // Get the data item length if it is variable. leng = getLength (leng, data_p + offset); // Remove the data and decrease the length. dataLeng_p -= leng; #ifdef AIPS_TRACE cout<<" removed " < offset) { memmove (data_p + offset, data_p + offset + leng, dataLeng_p - offset); // Decrement the offset of all other items following this one. uInt nrcol = offIndex_p.nelements(); for (uInt i=0; i& offIndex = *(offIndex_p[i]); for (uInt j=0; j offset) { offIndex[j] -= leng; } } } } } uInt ISMBucket::insertData (const char* data, uInt leng) { AlwaysAssert (dataLeng_p + leng + indexLeng_p <= stmanPtr_p->bucketSize(), AipsError); memcpy (data_p + dataLeng_p, data, leng); uInt offset = dataLeng_p; dataLeng_p += leng; #ifdef AIPS_TRACE cout<<" inserted "<write (bucketStorage); } void ISMBucket::deleteCallBack (void*, char* bucket) { delete (ISMBucket*)bucket; } char* ISMBucket::initCallBack (void* owner) { ISMBucket* bucket = new ISMBucket ((ISMBase*)owner, 0); AlwaysAssert (bucket != 0, AipsError); return (char*)bucket; } void ISMBucket::write (char* bucketStorage) const { uInt nrcol = stmanPtr_p->ncolumn(); Conversion::ValueFunction* writeuInt = ISMColumn::getWriteuInt (stmanPtr_p->asBigEndian()); Conversion::ValueFunction* writeRownr = ISMColumn::getWriteRownr (stmanPtr_p->asBigEndian()); // See if all rownrs fit in 32 bits. // This will often be the case and makes it possible to use an older // Casacore version. Bool use32 = True; for (uInt i=0; i 0 && (*rowIndex_p[i])[nr-1] > DataManager::MAXROWNR32) { use32 = False; break; } } // The index will be written just after the data. // Set high bit if 64 bit row numbers are used. uInt offset = dataLeng_p + uIntSize_p; uInt woffset = offset; if (!use32) { woffset |= 0x80000000; } writeuInt (bucketStorage, &woffset, 1); // Copy the data. memcpy (bucketStorage + uIntSize_p, data_p, dataLeng_p); // Write the index. for (uInt i=0; istorage(), nr); } offset += writeuInt (bucketStorage+offset, offIndex_p[i]->storage(), nr); } // Do an extra validity check. AlwaysAssert (offset <= stmanPtr_p->bucketSize(), AipsError); } void ISMBucket::read (const char* bucketStorage) { uInt nrcol = stmanPtr_p->ncolumn(); Conversion::ValueFunction* readuInt = ISMColumn::getReaduInt (stmanPtr_p->asBigEndian()); Conversion::ValueFunction* readRownr = ISMColumn::getReadRownr (stmanPtr_p->asBigEndian()); // Get the offset of the index. uInt offset; readuInt (&offset, bucketStorage, 1); indexLeng_p = uIntSize_p; // The high 4 bits (currently 1 bit is used) give the type/version. // If set, the rownrs are written as 64 bits. // If unset, it is 32 bit which is also the old Casacore behaviour making // it backward compatible. uInt type = offset & 0xf0000000; offset &= 0x0fffffff; // See if old version, thus rownrs use 32 bits. Bool use32 = (type == 0); // Copy the data, which are just before the index. dataLeng_p = offset - uIntSize_p; memcpy (data_p, bucketStorage + uIntSize_p, dataLeng_p); // Read the index. // Calculate length of index always with full rownr length. uInt rownr32; for (uInt i=0; iresize (nr); offIndex_p[i]->resize (nr); if (use32) { for (uInt j=0; jstorage(), bucketStorage+offset, nr); } offset += readuInt (offIndex_p[i]->storage(), bucketStorage+offset, nr); indexLeng_p += nr * (uIntSize_p + rownrSize_p); } } Bool ISMBucket::simpleSplit (ISMBucket* left, ISMBucket* right, Block& duplicated, rownr_t& splitRownr, rownr_t rownr) { // Determine the last rownr in the bucket. rownr_t lastRow = 0; uInt nrcol = stmanPtr_p->ncolumn(); for (uInt i=0; i lastRow) { lastRow = row; } } // Don't do a simple split if the row is not the last row in the bucket. if (rownr < lastRow) { return False; } // The last values of the bucket are the starting values of the // right one, so copy them. // The left bucket is this bucket. // Remove the last value from the left if the rownr is in the bucket. left->copy (*this); for (uInt i=0; ishiftLeft (index, 1, left->rowIndex(i), left->offIndex(i), left->indexUsed(i), stmanPtr_p->getColumn(i).getFixedLength()); duplicated[i] = False; } } splitRownr = rownr; #ifdef AIPS_TRACE cout << "Simple split "; cout << "Original" << endl; show (cout); cout << "Left" << endl; left->show (cout); cout << "Right" << endl; right->show (cout); #endif return True; } rownr_t ISMBucket::split (ISMBucket*& left, ISMBucket*& right, Block& duplicated, rownr_t bucketStartRow, rownr_t bucketNrrow, uInt colnr, rownr_t rownr, uInt lengToAdd) { AlwaysAssert (bucketNrrow > 1, AipsError); uInt nrcol = stmanPtr_p->ncolumn(); duplicated.resize (nrcol); left = new ISMBucket (stmanPtr_p, 0); right = new ISMBucket (stmanPtr_p, 0); rownr_t splitRownr; // Try a simple split if the current bucket is the last one. // (Then we usually add to the end of the file). if (bucketStartRow + bucketNrrow >= stmanPtr_p->nrow()) { if (simpleSplit (left, right, duplicated, splitRownr, rownr)) { return splitRownr; } } // Count the number of values in all columns. uInt nr = 0; for (uInt i=0; i rows(nr + 1); rows[0] = rownr; // new item nr = 1; for (uInt i=0; i::sort (rows, rows.nelements(), Sort::Ascending, Sort::NoDuplicates); // If the bucket contains values of only one row, a simple split // can be done (and should succeed). if (nruniq == 1) { Bool split = simpleSplit (left, right, duplicated, splitRownr, rownr); AlwaysAssert (split, AipsError); return splitRownr; } // Now get the length of all data items in the rows. // Also determine the index of the row to be added. Matrix itemLeng(nrcol, nruniq); itemLeng = 0; Block cursor(nrcol, uInt(0)); uInt index = 0; for (uInt j=0; jgetColumn(i).getFixedLength(), data_p + (*offIndex_p[i])[cursor[i]]); itemLeng(i,j) = 2*uIntSize_p + leng; cursor[i]++; } } if (rownr == rows[j]) { index = j; } } // Insert the length of the new item. // If it is a new item, add the index length too. if (itemLeng(colnr, index) == 0) { itemLeng(colnr, index) = lengToAdd + 2*uIntSize_p; }else{ itemLeng(colnr, index) += lengToAdd; } // Now determine the length of all items in each row. // Determine the cumulative and total size. Block size(nrcol, uInt(0)); Block rowLeng(nruniq, uInt(0)); Block cumLeng(nruniq); uInt totLeng = 0; for (uInt j=0; j toCursor(nrcol, 1); index++; while (index < nruniq) { rownr_t row = rows[index]; for (uInt i=0; ishow (cout); cout << "Right" << endl; right->show (cout); #endif return splitRownr; } uInt ISMBucket::getSplit (uInt totLeng, const Block& rowLeng, const Block& cumLeng) { // If there are only 2 elements, we can only split in the middle. uInt nr = rowLeng.nelements(); if (nr <= 2) { return 1; } // Determine the index where left and right have about the same size. // totLeng = length of all values. This includes the starting values. // rowLeng = length of all values in a row. This gives the length // of the starting values if the bucket starts at that row. // cumLeng = length of all values till the row with index i. // cumLeng[0] = length of the starting values in first row. // If i is the index where the bucket is split, then: // length of left bucket = cumLeng[i-1]. // length of right bucket = rowLeng[i] + totleng - cumLeng[i] // Loop until left size exceeds right size or until we get at the // rightmost index. uInt i=1; uInt diff = 0; while (cumLeng[i-1] < rowLeng[i] + totLeng - cumLeng[i] && i 0) { if (cumLeng[i-1] + cumLeng[i] - rowLeng[i] - totLeng > diff) { i--; } } return i; } uInt ISMBucket::copyData (ISMBucket& other, uInt colnr, rownr_t toRownr, uInt fromIndex, uInt toIndex) const { // Determine the length of the data item. // If variable, read it from the data. char* data = data_p + (*offIndex_p[colnr])[fromIndex]; uInt leng = getLength (stmanPtr_p->getColumn(colnr).getFixedLength(), data); other.addData (colnr, toRownr, toIndex, data, leng); return leng; } void ISMBucket::show (ostream& os) const { uInt nrcol = stmanPtr_p->ncolumn(); for (uInt i=0; incolumn(); for (uInt col_i=0; col_i #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations class ISMBase; // // A bucket in the Incremental Storage Manager // // // // // //# Classes you should understand before using this one. //
      • IncrementalStMan //
      • BucketCache // // // ISMBucket represents a bucket in the Incremental Storage Manager. // // // The Incremental Storage Manager uses a // BucketCache object to read/write/cache the buckets // containing the data. An ISMBucket object is the // internal representation of the contents of a bucket. ISMBucket // contains static callback functions which are called by // BucketCache when reading/writing a bucket. These callback // functions do the mapping of bucket data to ISMBucket object // and vice-versa. //

        // A bucket contains the values of several rows // of all columns bound to this Incremental Storage Manager. // A bucket is split into a data part and an index part. // Each part has an arbitrary length but together they do not exceed // the fixed bucket length. //

        // The beginning of the data part contains the values of all columns // bound. The remainder of the data part contains the values of // the rows/columns with a changed value. //
        // The index part contains an index per column. Each index contains the // row number and an offset for a row with a stored value. The row numbers // are relative to the beginning of the bucket, so the bucket has // no knowledge about the absolute row numbers. In this way deletion of // rows is much simpler. //

        // The contents of a bucket looks like: // // ------------------------------------------------------------------- // | index offset | data part | index part | free | // ------------------------------------------------------------------- // 0 4 4+length(data part) // <--------------------------bucketsize-----------------------------> // // The data part contains all data value belonging to the bucket. // The index part contains for each column the following data: // // ----------------------------------------------------------------------- // | #values stored | row numbers of values | offset in data part of | // | for column i | stored for column i | values stored for column i | // ----------------------------------------------------------------------- // 0 4 4+4*nrval // // Note that the row numbers in the bucket start at 0, thus are relative // to the beginning of the bucket. The main index kept in // ISMIndex knows the starting row of // each bucket. In this way bucket splitting and especially row removal // is much easier. //

        // The bucket can be stored in canonical or local (i.e. native) data format. // When a bucket is read into memory, its data are read, converted, and // stored in the ISMBucket object. When flushed, the contents are // written. ISMBucket takes care that the values stored in its object // do not exceed the size of the bucket. When full, the user can call // a function to split it into a left and right bucket. When the new // value has to be written at the end, the split merely consist of // creating a new bucket. In any case, care is taken that a row is // not split. Thus a row is always entirely contained in one bucket. //

        // Class ISMColumn does the actual // writing of data in a bucket and uses the relevant ISMBucket functions. // // ISMBucket encapsulates the data of a bucket. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class ISMBucket { public: // Create a bucket with the given parent. // When bucketStorage is non-zero, reconstruct the // object from it. // It keeps the pointer to its parent (but does not own it). ISMBucket (ISMBase* parent, const char* bucketStorage); ~ISMBucket(); // Forbid copy constructor. ISMBucket (const ISMBucket&) = delete; // Forbid assignment. ISMBucket& operator= (const ISMBucket&) = delete; // Get the row-interval for given column and row. // It sets the start and end of the interval to which the row belongs // and the offset of its current value. // It returns the index where the row number can be put in the // bucket index. uInt getInterval (uInt colnr, rownr_t rownr, rownr_t bucketNrrow, rownr_t& start, rownr_t& end, uInt& offset) const; // Is the bucket large enough to add a value? Bool canAddData (uInt leng) const; // Add the data to the data part. // It updates the bucket index at the given index. // An exception is thrown if the bucket is too small. void addData (uInt colnr, rownr_t rownr, uInt index, const char* data, uInt leng); // Is the bucket large enough to replace a value? Bool canReplaceData (uInt newLeng, uInt oldLeng) const; // Replace a data item. // When its length is variable (indicated by fixedLength=0), the old // value will be removed and the new one appended at the end. // An exception is thrown if the bucket is too small. void replaceData (uInt& offset, const char* data, uInt newLeng, uInt fixedLength); // Get a pointer to the data for the given offset. const char* get (uInt offset) const; // Get the length of the data value. // It is fixedLength when non-zero, // otherwise read it from the data value. uInt getLength (uInt fixedLength, const char* data) const; // Get access to the offset of the data for given column and row. // It allows to change it (used for example by replaceData). uInt& getOffset (uInt colnr, rownr_t rownr); // Get access to the index information for the given column. // This is used by ISMColumn when putting the data. // // Return the row numbers with a stored value. Block& rowIndex (uInt colnr); // Return the offsets of the values stored in the data part. Block& offIndex (uInt colnr); // Return the number of values stored. uInt& indexUsed (uInt colnr); // // Split the bucket in the middle. // It returns the row number where the bucket was split and the // new left and right bucket. The caller is responsible for // deleting the newly created buckets. // When possible a simple split is done. //
        // The starting values in the right bucket may be copies of the // values in the left bucket. The duplicated Block contains a switch // per column indicating if the value is copied. rownr_t split (ISMBucket*& left, ISMBucket*& right, Block& duplicated, rownr_t bucketStartRow, rownr_t bucketNrrow, uInt colnr, rownr_t rownr, uInt lengToAdd); // Determine whether a simple split is possible. If so, do it. // This is possible if the new row is at the end of the last bucket, // which will often be the case. //
        A simple split means adding a new bucket for the new row. // If the old bucket already contains values for that row, those // values are moved to the new bucket. //
        This fuction is only called by split, which created the // left and right bucket. Bool simpleSplit (ISMBucket* left, ISMBucket* right, Block& duplicated, rownr_t& splitRownr, rownr_t rownr); // Return the index where the bucket should be split to get // two parts with almost identical length. uInt getSplit (uInt totLeng, const Block& rowLeng, const Block& cumLeng); // Remove nr items from data and index part by shifting // to the left. The rowIndex, offIndex, and // nused get updated. The caller is responsible for // removing data when needed (e.g. ISMIndColumn removes // the indirect arrays from its file). void shiftLeft (uInt index, uInt nr, Block& rowIndex, Block& offIndex, uInt& nused, uInt leng); // Copy the contents of that bucket to this bucket. // This is used after a split operation. void copy (const ISMBucket& that); // Callback function when BucketCache reads a bucket. // It creates an ISMBucket object and converts the raw bucketStorage // to that object. // It returns the pointer to ISMBucket object which gets part of the cache. // The object gets deleted by the deleteCallBack function. static char* readCallBack (void* owner, const char* bucketStorage); // Callback function when BucketCache writes a bucket. // It converts the ISMBucket bucket object to the raw bucketStorage. static void writeCallBack (void* owner, char* bucketStorage, const char* bucket); // Callback function when BucketCache adds a new bucket to the data file. // This function creates an empty ISMBucket object. // It returns the pointer to ISMBucket object which gets part of the cache. // The object gets deleted by the deleteCallBack function. static char* initCallBack (void* owner); // Callback function when BucketCache removes a bucket from the cache. // This function dletes the ISMBucket bucket object. static void deleteCallBack (void*, char* bucket); // Show the layout of the bucket. void show (ostream& os) const; // Check that there are no repeated rowIds in the bucket Bool check (uInt& offendingCol, uInt& offendingIndex, rownr_t& offendingRow, rownr_t& offendingPrevRow) const; private: // Remove a data item with the given length. // If the length is zero, its variable length is read first. void removeData (uInt offset, uInt leng); // Insert a data value by appending it to the end. // It returns the offset of the data value. uInt insertData (const char* data, uInt leng); // Copy a data item from this bucket to the other bucket. uInt copyData (ISMBucket& other, uInt colnr, rownr_t toRownr, uInt fromIndex, uInt toIndex) const; // Read the data from the storage into this bucket. void read (const char* bucketStorage); // Write the bucket into the storage. void write (char* bucketStorage) const; //# Declare member variables. // Pointer to the parent storage manager. ISMBase* stmanPtr_p; // The size (in bytes) of an uInt and rownr_t (used in index, etc.). uInt uIntSize_p; uInt rownrSize_p; // The size (in bytes) of the data. uInt dataLeng_p; // The size (in bytes) of the index. uInt indexLeng_p; // The row index per column; each index contains the row number // of each value stored in the bucket (for that column). PtrBlock*> rowIndex_p; // The offset index per column; each index contains the offset (in bytes) // of each value stored in the bucket (for that column). PtrBlock*> offIndex_p; // Nr of used elements in each index; i.e. the number of stored values // per column. Block indexUsed_p; // The data space (in external (e.g. canonical) format). char* data_p; }; inline const char* ISMBucket::get (uInt offset) const { return data_p + offset; } inline Block& ISMBucket::rowIndex (uInt colnr) { return *(rowIndex_p[colnr]); } inline Block& ISMBucket::offIndex (uInt colnr) { return *(offIndex_p[colnr]); } inline uInt& ISMBucket::indexUsed (uInt colnr) { return indexUsed_p[colnr]; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/ISMColumn.cc000066400000000000000000001115311476623553700202220ustar00rootroot00000000000000//# ISMColumn.cc: The Column of the Incremental Storage Manager //# Copyright (C) 1996,1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ISMColumn::ISMColumn (ISMBase* parent, int dataType, uInt colnr) : StManColumnBase(dataType), stmanPtr_p (parent), fixedLength_p (0), colnr_p (colnr), nrelem_p (1), startRow_p (1), endRow_p (0), lastValue_p (0), lastRowPut_p (0) { //# The increment in the column cache is always 0, //# because multiple rows refer to the same value. columnCache().setIncrement (0); } ISMColumn::~ISMColumn() { clear(); } void ISMColumn::clear() { switch (dataType()) { case TpBool: delete [] (Bool*)lastValue_p; break; case TpUChar: delete [] (uChar*)lastValue_p; break; case TpShort: delete [] (Short*)lastValue_p; break; case TpUShort: delete [] (uShort*)lastValue_p; break; case TpInt: delete [] (Int*)lastValue_p; break; case TpUInt: delete [] (uInt*)lastValue_p; break; case TpInt64: delete [] (Int64*)lastValue_p; break; case TpFloat: delete [] (float*)lastValue_p; break; case TpDouble: delete [] (double*)lastValue_p; break; case TpComplex: delete [] (Complex*)lastValue_p; break; case TpDComplex: delete [] (DComplex*)lastValue_p; break; case TpString: delete [] (String*)lastValue_p; break; default: AlwaysAssert (0, AipsError); } lastValue_p = 0; } void ISMColumn::setShapeColumn (const IPosition& shape) { nrelem_p = shape.product(); shape_p = shape; } uInt ISMColumn::ndim (rownr_t) { return shape_p.nelements(); } IPosition ISMColumn::shape (rownr_t) { return shape_p; } void ISMColumn::addRow (rownr_t, rownr_t) { //# Nothing to do. } void ISMColumn::remove (rownr_t bucketRownr, ISMBucket* bucket, rownr_t bucketNrrow, rownr_t newNrrow) { uInt inx, offset; rownr_t stint, endint; // Get the index where to remove the value. // If the rownr is not the start of the interval, index is one further. inx = bucket->getInterval (colnr_p, bucketRownr, bucketNrrow, stint, endint, offset); #ifdef AIPS_TRACE cout << "remove column " << colnr_p << ", row " << bucketRownr << " (nrelem="<& rowIndex = bucket->rowIndex (colnr_p); Block& offIndex = bucket->offIndex (colnr_p); uInt& nused = bucket->indexUsed (colnr_p); // Invalidate the last value read. columnCache().invalidate(); startRow_p = 1; endRow_p = 0; // We have to change the bucket, so let the cache set the dirty flag // for this bucket. stmanPtr_p->setBucketDirty(); // If the row is single, remove the value by shifting left one value. if (stint == endint) { handleRemove (bucketRownr, bucket->get (offset)); bucket->shiftLeft (inx, 1, rowIndex, offIndex, nused, fixedLength_p); //# We can also test if previous and next value are equal and //# shift left one more. However, that is not implemented. } else { // If not single, start of this interval does not change. // The index has to be incremented if row is at start of interval. if (bucketRownr == stint) { inx++; } } // Decrement the row number for all following rows. for (uInt i=inx; i newNrrow) { lastRowPut_p = newNrrow+1; } } void ISMColumn::getBool (rownr_t rownr, Bool* value) { getValue (rownr, lastValue_p, True); *value = *(Bool*)lastValue_p; } void ISMColumn::getuChar (rownr_t rownr, uChar* value) { getValue (rownr, lastValue_p, True); *value = *(uChar*)lastValue_p; } void ISMColumn::getShort (rownr_t rownr, Short* value) { getValue (rownr, lastValue_p, True); *value = *(Short*)lastValue_p; } void ISMColumn::getuShort (rownr_t rownr, uShort* value) { getValue (rownr, lastValue_p, True); *value = *(uShort*)lastValue_p; } void ISMColumn::getInt (rownr_t rownr, Int* value) { getValue (rownr, lastValue_p, True); *value = *(Int*)lastValue_p; } void ISMColumn::getuInt (rownr_t rownr, uInt* value) { getValue (rownr, lastValue_p, True); *value = *(uInt*)lastValue_p; } void ISMColumn::getInt64 (rownr_t rownr, Int64* value) { getValue (rownr, lastValue_p, True); *value = *(Int64*)lastValue_p; } void ISMColumn::getfloat (rownr_t rownr, float* value) { getValue (rownr, lastValue_p, True); *value = *(float*)lastValue_p; } void ISMColumn::getdouble (rownr_t rownr, double* value) { getValue (rownr, lastValue_p, True); *value = *(double*)lastValue_p; } void ISMColumn::getComplex (rownr_t rownr, Complex* value) { getValue (rownr, lastValue_p, True); *value = *(Complex*)lastValue_p; } void ISMColumn::getDComplex (rownr_t rownr, DComplex* value) { getValue (rownr, lastValue_p, True); *value = *(DComplex*)lastValue_p; } void ISMColumn::getString (rownr_t rownr, String* value) { getValue (rownr, lastValue_p, True); *value = *(String*)lastValue_p; } void ISMColumn::getScalarColumnV (ArrayBase& dataPtr) { switch (dtype()) { case TpBool: getScaCol (static_cast&>(dataPtr)); break; case TpUChar: getScaCol (static_cast&>(dataPtr)); break; case TpShort: getScaCol (static_cast&>(dataPtr)); break; case TpUShort: getScaCol (static_cast&>(dataPtr)); break; case TpInt: getScaCol (static_cast&>(dataPtr)); break; case TpUInt: getScaCol (static_cast&>(dataPtr)); break; case TpInt64: getScaCol (static_cast&>(dataPtr)); break; case TpFloat: getScaCol (static_cast&>(dataPtr)); break; case TpDouble: getScaCol (static_cast&>(dataPtr)); break; case TpComplex: getScaCol (static_cast&>(dataPtr)); break; case TpDComplex: getScaCol (static_cast&>(dataPtr)); break; case TpString: getScaCol (static_cast&>(dataPtr)); break; default: AlwaysAssert (0, AipsError); } } void ISMColumn::getScalarColumnCellsV (const RefRows& rows, ArrayBase& dataPtr) { switch (dtype()) { case TpBool: getScaColCells (rows, static_cast&>(dataPtr)); break; case TpUChar: getScaColCells (rows, static_cast&>(dataPtr)); break; case TpShort: getScaColCells (rows, static_cast&>(dataPtr)); break; case TpUShort: getScaColCells (rows, static_cast&>(dataPtr)); break; case TpInt: getScaColCells (rows, static_cast&>(dataPtr)); break; case TpUInt: getScaColCells (rows, static_cast&>(dataPtr)); break; case TpInt64: getScaColCells (rows, static_cast&>(dataPtr)); break; case TpFloat: getScaColCells (rows, static_cast&>(dataPtr)); break; case TpDouble: getScaColCells (rows, static_cast&>(dataPtr)); break; case TpComplex: getScaColCells (rows, static_cast&>(dataPtr)); break; case TpDComplex: getScaColCells (rows, static_cast&>(dataPtr)); break; case TpString: getScaColCells (rows, static_cast&>(dataPtr)); break; default: AlwaysAssert (0, AipsError); } } #define ISMCOLUMN_GET(T) \ void ISMColumn::getScaCol (Vector& dataPtr) \ { \ rownr_t nrrow = dataPtr.nelements(); \ rownr_t rownr = 0; \ while (rownr < nrrow) { \ aips_name2(get,T) (rownr, &(dataPtr(rownr))); \ for (rownr++; rownr<=endRow_p; ++rownr) { \ dataPtr(rownr) = *(T*)lastValue_p; \ } \ } \ } \ void ISMColumn::getScaColCells (const RefRows& rownrs, \ Vector& values) \ { \ Bool delV; \ T* value = values.getStorage (delV); \ T* valptr = value; \ if (rownrs.isSliced()) { \ RefRowsSliceIter iter(rownrs); \ while (! iter.pastEnd()) { \ rownr_t rownr = iter.sliceStart(); \ rownr_t end = iter.sliceEnd(); \ rownr_t incr = iter.sliceIncr(); \ while (rownr <= end) { \ if (isLastValueInvalid (rownr)) { \ aips_name2(get,T) (rownr, valptr); \ valptr++; \ rownr += incr; \ } \ const T* cacheValue = (const T*)(lastValue_p); \ rownr_t endrow = min (end, endRow_p); \ while (rownr <= endrow) { \ *valptr++ = *cacheValue; \ rownr += incr; \ } \ } \ iter++; \ } \ } else { \ const Vector& rowvec = rownrs.rowVector(); \ rownr_t nr = rowvec.nelements(); \ if (nr > 0) { \ Bool delR; \ const rownr_t* rows = rowvec.getStorage (delR); \ if (isLastValueInvalid (rows[0])) { \ aips_name2(get,T) (0, &(value[0])); \ } \ const T* cacheValue = (const T*)(lastValue_p); \ rownr_t strow = startRow_p; \ rownr_t endrow = endRow_p; \ for (rownr_t i=0; i= strow && rownr <= endrow) { \ value[i] = *cacheValue; \ } else { \ aips_name2(get,T) (rownr, &(value[i])); \ cacheValue = (const T*)(lastValue_p); \ strow = startRow_p; \ endrow = endRow_p; \ } \ } \ rowvec.freeStorage (rows, delR); \ } \ } \ values.putStorage (value, delV); \ } ISMCOLUMN_GET(Bool) ISMCOLUMN_GET(uChar) ISMCOLUMN_GET(Short) ISMCOLUMN_GET(uShort) ISMCOLUMN_GET(Int) ISMCOLUMN_GET(uInt) ISMCOLUMN_GET(Int64) ISMCOLUMN_GET(float) ISMCOLUMN_GET(double) ISMCOLUMN_GET(Complex) ISMCOLUMN_GET(DComplex) ISMCOLUMN_GET(String) void ISMColumn::getValue (rownr_t rownr, void* value, Bool setCache) { if (rownr < startRow_p || rownr > endRow_p) { // Get the bucket with its row number boundaries. rownr_t bucketStartRow; rownr_t bucketNrrow; ISMBucket* bucket = stmanPtr_p->getBucket (rownr, bucketStartRow, bucketNrrow); // Get the interval in the bucket with its rownr boundaries. rownr -= bucketStartRow; uInt offset; rownr_t stint, endint; bucket->getInterval (colnr_p, rownr, bucketNrrow, stint, endint, offset); // Get the value. // Set the start and end rownr for which this value is valid. readFunc_p (value, bucket->get (offset), nrcopy_p); startRow_p = bucketStartRow + stint; endRow_p = bucketStartRow + endint; } if (setCache) { columnCache().set (startRow_p, endRow_p, lastValue_p); } } void ISMColumn::putBool (rownr_t rownr, const Bool* value) { putValue (rownr, value); } void ISMColumn::putuChar (rownr_t rownr, const uChar* value) { putValue (rownr, value); } void ISMColumn::putShort (rownr_t rownr, const Short* value) { putValue (rownr, value); } void ISMColumn::putuShort (rownr_t rownr, const uShort* value) { putValue (rownr, value); } void ISMColumn::putInt (rownr_t rownr, const Int* value) { putValue (rownr, value); } void ISMColumn::putuInt (rownr_t rownr, const uInt* value) { putValue (rownr, value); } void ISMColumn::putInt64 (rownr_t rownr, const Int64* value) { putValue (rownr, value); } void ISMColumn::putfloat (rownr_t rownr, const float* value) { putValue (rownr, value); } void ISMColumn::putdouble (rownr_t rownr, const double* value) { putValue (rownr, value); } void ISMColumn::putComplex (rownr_t rownr, const Complex* value) { putValue (rownr, value); } void ISMColumn::putDComplex (rownr_t rownr, const DComplex* value) { putValue (rownr, value); } void ISMColumn::putString (rownr_t rownr, const String* value) { putValue (rownr, value); } void ISMColumn::putScalarColumnV (const ArrayBase& dataPtr) { switch (dtype()) { case TpBool: putScaCol (static_cast&>(dataPtr)); break; case TpUChar: putScaCol (static_cast&>(dataPtr)); break; case TpShort: putScaCol (static_cast&>(dataPtr)); break; case TpUShort: putScaCol (static_cast&>(dataPtr)); break; case TpInt: putScaCol (static_cast&>(dataPtr)); break; case TpUInt: putScaCol (static_cast&>(dataPtr)); break; case TpInt64: putScaCol (static_cast&>(dataPtr)); break; case TpFloat: putScaCol (static_cast&>(dataPtr)); break; case TpDouble: putScaCol (static_cast&>(dataPtr)); break; case TpComplex: putScaCol (static_cast&>(dataPtr)); break; case TpDComplex: putScaCol (static_cast&>(dataPtr)); break; case TpString: putScaCol (static_cast&>(dataPtr)); break; default: AlwaysAssert (0, AipsError); } } void ISMColumn::putScaCol (const Vector& dataPtr) { rownr_t nrrow = dataPtr.nelements(); for (rownr_t i=0; i& dataPtr) { rownr_t nrrow = dataPtr.nelements(); for (rownr_t i=0; i& dataPtr) { rownr_t nrrow = dataPtr.nelements(); for (rownr_t i=0; i& dataPtr) { rownr_t nrrow = dataPtr.nelements(); for (rownr_t i=0; i& dataPtr) { rownr_t nrrow = dataPtr.nelements(); for (rownr_t i=0; i& dataPtr) { rownr_t nrrow = dataPtr.nelements(); for (rownr_t i=0; i& dataPtr) { rownr_t nrrow = dataPtr.nelements(); for (rownr_t i=0; i& dataPtr) { rownr_t nrrow = dataPtr.nelements(); for (rownr_t i=0; i& dataPtr) { rownr_t nrrow = dataPtr.nelements(); for (rownr_t i=0; i& dataPtr) { rownr_t nrrow = dataPtr.nelements(); for (rownr_t i=0; i& dataPtr) { rownr_t nrrow = dataPtr.nelements(); for (rownr_t i=0; i& dataPtr) { rownr_t nrrow = dataPtr.nelements(); for (rownr_t i=0; i (shape_p, (String*)lastValue_p, SHARE)); } else { Bool deleteIt; void* vptr = value.getVStorage(deleteIt); memcpy (vptr, lastValue_p, nrelem_p * typeSize_p); value.putVStorage (vptr, deleteIt); } } void ISMColumn::putArrayV (rownr_t rownr, const ArrayBase& value) { Bool deleteIt; const void* data = value.getVStorage (deleteIt); putValue (rownr, data); value.freeVStorage (data, deleteIt); } void ISMColumn::putValue (rownr_t rownr, const void* value) { // Get the bucket and interval to which the row belongs. rownr_t bucketStartRow; rownr_t bucketNrrow; ISMBucket* bucket = stmanPtr_p->getBucket (rownr, bucketStartRow, bucketNrrow); rownr_t bucketRownr = rownr - bucketStartRow; rownr_t stint, endint; uInt inx, offset; // Get the index where to add/replace the new value. // Note: offset gives the offset of the current value, which is often NOT // the same as offIndex[inx] (usually it is offIndex[inx-1]). inx = bucket->getInterval (colnr_p, bucketRownr, bucketNrrow, stint, endint, offset); #ifdef AIPS_TRACE cout << "put column " << colnr_p << ", row " << rownr << " (nrelem="<& rowIndex = bucket->rowIndex (colnr_p); Block& offIndex = bucket->offIndex (colnr_p); uInt& nused = bucket->indexUsed (colnr_p); // Determine if the new row is after the last row ever put for this column. Bool afterLastRowPut = False; if (rownr >= lastRowPut_p) { #ifdef AIPS_TRACE cout << " after last row " << lastRowPut_p<get (offset), nrcopy_p); if (compareValue (value, lastValue_p)) { #ifdef AIPS_TRACE cout << " equal value" << endl; #endif return; } // We have to write the value, so let the cache set the dirty flag // for this bucket. stmanPtr_p->setBucketDirty(); // Get the temporary buffer from the storage manager. uInt lenData; char* buffer = stmanPtr_p->tempBuffer(); // Test if the value equals the previous one if at the start of // the interval (or equals next value if at the end of the interval). // Take care if row is first (or last) row in bucket. // The index of the next value is inx+1 if the row is the starting // row of the interval. Bool equalPrev = False; Bool equalNext = False; uInt nextInx = inx; if (bucketRownr == stint) { nextInx++; if (bucketRownr > 0) { readFunc_p (lastValue_p, bucket->get (offIndex[inx-1]), nrcopy_p); if (compareValue (value, lastValue_p)) { equalPrev = True; } } } if (bucketRownr == endint) { if (bucketRownr < bucketNrrow-1) { readFunc_p (lastValue_p, bucket->get (offIndex[nextInx]), nrcopy_p); if (compareValue (value, lastValue_p)) { equalNext = True; } } } // If the row is higher than the last row ever put for this column, // the value is valid for all rows from this row on. // If it is the first row in the bucket, replace the value. // If the new value equals the previous one, combine them. // Otherwise add it to this bucket. // In any case put the value in all further buckets. if (afterLastRowPut) { lenData = writeFunc_p (buffer, value, nrcopy_p); if (bucketRownr == 0) { replaceData (bucket, bucketStartRow, bucketNrrow, bucketRownr, offIndex[inx], buffer, lenData); }else{ if (equalPrev) { #ifdef AIPS_TRACE cout << " equal prev"; #endif bucket->shiftLeft (inx, 1, rowIndex, offIndex, nused, fixedLength_p); }else{ addData (bucket, bucketStartRow, bucketNrrow, bucketRownr, inx, buffer, lenData, True); } } putFromRow (rownr, buffer, lenData); return; } // If the new value matches previous and next, we can contract the // values by removing 2 of them. if (equalPrev && equalNext) { #ifdef AIPS_TRACE cout << " equal prev and next"<shiftLeft (inx, 2, rowIndex, offIndex, nused, fixedLength_p); return; } // Determine if the value is the only one in the interval. // If the row to change is the first of the interval, increment // the interval start. Bool single = (stint==endint); //# if (!single && bucketRownr == stint) { //# rowIndex[inx]++; //# } // If matching the previous, combine with previous interval // (which is already done by incrementing the rowIndex above). // If it was a single value, contract the intervals. if (equalPrev) { #ifdef AIPS_TRACE cout << " equal prev"; #endif if (single) { #ifdef AIPS_TRACE cout << " and single"; #endif bucket->shiftLeft (inx, 1, rowIndex, offIndex, nused, fixedLength_p); }else{ rowIndex[inx]++; } #ifdef AIPS_TRACE cout << endl; #endif return; } // If equal to next value, act similarly as above. if (equalNext) { #ifdef AIPS_TRACE cout << " equal next"; #endif if (single) { #ifdef AIPS_TRACE cout << " and single"; #endif bucket->shiftLeft (inx, 1, rowIndex, offIndex, nused, fixedLength_p); } rowIndex[inx]--; #ifdef AIPS_TRACE cout << endl; #endif return; } // We have to add or replace the new data value. // If the value is single, simply replace it. // This will also update the offset value if needed. if (single) { lenData = writeFunc_p (buffer, value, nrcopy_p); replaceData (bucket, bucketStartRow, bucketNrrow, bucketRownr, offIndex[inx], buffer, lenData); return; } // Add the data item. // If the new value is in the middle of the interval, the // original value has to be duplicated. Give a derived class // the opportunity to handle the copy. // Do this before inserting the new value. Otherwise the new // value may get at the end of the bucket and fill up the bucket. // Thereafter inserting the duplicate results in a split and the new // value may get promoted to the new bucket iso. the original. if (bucketRownr > stint && bucketRownr < endint) { lenData = writeFunc_p (buffer, lastValue_p, nrcopy_p); addData (bucket, bucketStartRow, bucketNrrow, bucketRownr+1, inx, buffer, lenData); handleCopy (rownr, buffer); putValue (rownr, value); }else{ lenData = writeFunc_p (buffer, value, nrcopy_p); addData (bucket, bucketStartRow, bucketNrrow, bucketRownr, inx, buffer, lenData); } } void ISMColumn::putFromRow (rownr_t rownr, const char* data, uInt lenData) { // Skip the first bucket, because that is the one containing the // row just written. // Note that the previous write may have resulted in a bucket split. // So the previously calculated end of the bucket may not be right // anymore. Therefore we start at the given row and skip that bucket. #ifdef AIPS_TRACE cout << " putFromRow"; #endif ISMBucket* bucket; rownr_t bucketNrrow; uInt cursor = 0; stmanPtr_p->nextBucket (cursor, rownr, bucketNrrow); // Loop through all buckets from the given row on. // Replace the starting value in them. while ((bucket = stmanPtr_p->nextBucket (cursor, rownr, bucketNrrow)) != nullptr) { #ifdef AIPS_TRACE cout << "," << rownr; #endif // Set the dirty flag for this bucket. stmanPtr_p->setBucketDirty(); replaceData (bucket, rownr, bucketNrrow, 0, bucket->getOffset (colnr_p, 0), data, lenData); // The value has been duplicated; give a derived class the // opportunity to handle it. handleCopy (rownr, data); } #ifdef AIPS_TRACE cout << endl; #endif } void ISMColumn::putData (ISMBucket* bucket, rownr_t bucketStartRow, rownr_t bucketNrrow, rownr_t bucketRownr, const char* data, uInt lenData, Bool afterLastRow, Bool canSplit) { // Determine the index. uInt inx, dum3; rownr_t start, end; inx = bucket->getInterval (colnr_p, bucketRownr, 0, start, end, dum3); if ((afterLastRow && bucketRownr == 0) || start == end) { Block& offIndex = bucket->offIndex (colnr_p); replaceData (bucket, bucketStartRow, bucketNrrow, bucketRownr, offIndex[inx], data, lenData, canSplit); }else{ addData (bucket, bucketStartRow, bucketNrrow, bucketRownr, inx, data, lenData, afterLastRow, canSplit); } } void ISMColumn::replaceData (ISMBucket* bucket, rownr_t bucketStartRow, rownr_t bucketNrrow, rownr_t bucketRownr, uInt& offset, const char* data, uInt lenData, Bool canSplit) { // Replacing a value means removing the old value. // So give the opportunity to handle a removal before the // actual replace is done. // If the new value fits in the bucket, it can simply be replaced. uInt oldLeng = bucket->getLength (fixedLength_p, bucket->get (offset)); if (bucket->canReplaceData (lenData, oldLeng)) { handleRemove (bucketRownr, bucket->get (offset)); bucket->replaceData (offset, data, lenData, oldLeng); return; } // The bucket is too small, so split it in the middle (if allowed). AlwaysAssert (canSplit, AipsError); ISMBucket* left; ISMBucket* right; Block duplicated; rownr_t splitRownr = bucket->split (left, right, duplicated, bucketStartRow, bucketNrrow, colnr_p, bucketRownr, lenData - oldLeng); #ifdef AIPS_TRACE cout << " replace split at rownr "<copy (*left); delete left; // Replace the data in the correct part. if (bucketRownr >= splitRownr) { bucket = right; bucketRownr -= splitRownr; } uInt& offs = bucket->getOffset (colnr_p, bucketRownr); handleRemove (bucketRownr, bucket->get (offs)); bucket->replaceData (offs, data, lenData, oldLeng); // Add the right bucket to the index. stmanPtr_p->addBucket (splitRownr + bucketStartRow, right); } Bool ISMColumn::addData (ISMBucket* bucket, rownr_t bucketStartRow, rownr_t bucketNrrow, rownr_t bucketRownr, uInt inx, const char* data, uInt lenData, Bool afterLastRow, Bool canSplit) { // If the value fits in the bucket, it can simply be added. if (bucket->canAddData (lenData)) { bucket->addData (colnr_p, bucketRownr, inx, data, lenData); return False; } // The bucket is too small, so split it in the middle (if allowed). AlwaysAssert (canSplit, AipsError); ISMBucket* left; ISMBucket* right; Block duplicated; rownr_t splitRownr = bucket->split (left, right, duplicated, bucketStartRow, bucketNrrow, colnr_p, bucketRownr, lenData); #ifdef AIPS_TRACE cout << " add split at rownr "<copy (*left); delete left; // Add the data to the correct part. rownr_t startRow = bucketStartRow; rownr_t nrrow = splitRownr; if (bucketRownr >= splitRownr) { bucket = right; bucketRownr -= splitRownr; startRow += splitRownr; nrrow = bucketNrrow - splitRownr; } // The next put cannot split anymore. putData (bucket, startRow, nrrow, bucketRownr, data, lenData, afterLastRow, False); // Add the right bucket to the index. stmanPtr_p->addBucket (splitRownr + bucketStartRow, right); return True; } #ifdef AIPS_TRACE void ISMColumn::handleCopy (rownr_t rownr, const char*) { cout << " handleCopy for row " << rownr << ", column " << colnr_p << endl; #else void ISMColumn::handleCopy (rownr_t, const char*) { #endif } #ifdef AIPS_TRACE void ISMColumn::handleRemove (rownr_t rownr, const char*) { cout << " handleRemove for row " << rownr << ", column " << colnr_p << endl; #else void ISMColumn::handleRemove (rownr_t, const char*) { #endif } void ISMColumn::handleSplit (ISMBucket& bucket, const Block& duplicated) { // Loop through all columns. // If the split duplicated a value, handle the copied value. uInt nrcol = stmanPtr_p->ncolumn(); for (uInt i=0; igetColumn(i).handleCopy (0, bucket.get (offset)); } } } Bool ISMColumn::compareValue (const void* val1, const void* val2) const { const char* v1 = (const char*)val1; const char* v2 = (const char*)val2; for (uInt i=0; iasBigEndian(); nrcopy_p = nrelem_p; if (dt == TpString) { fixedLength_p = 0; } else if (dt == TpBool) { fixedLength_p = (nrelem_p + 7) / 8; }else{ fixedLength_p = ValType::getCanonicalSize (dt, asBigEndian); uInt nrel; ValType::getCanonicalFunc (dt, readFunc_p, writeFunc_p, nrel, asBigEndian); nrcopy_p *= nrel; fixedLength_p *= nrelem_p; } switch (dt) { case TpBool: { readFunc_p = &Conversion::bitToBool; writeFunc_p = &Conversion::boolToBit; compareFunc_p = ObjCompare::compare; lastValue_p = new Bool [nrelem_p]; Bool undef = False; objset ((Bool*)lastValue_p, undef, nrelem_p); } break; case TpUChar: { compareFunc_p = ObjCompare::compare; lastValue_p = new uChar [nrelem_p]; uChar undef = 0; objset ((uChar*)lastValue_p, undef, nrelem_p); } break; case TpShort: { compareFunc_p = ObjCompare::compare; lastValue_p = new Short [nrelem_p]; Short undef = 0; objset ((Short*)lastValue_p, undef, nrelem_p); } break; case TpUShort: { compareFunc_p = ObjCompare::compare; lastValue_p = new uShort [nrelem_p]; uShort undef = 0; objset ((uShort*)lastValue_p, undef, nrelem_p); } break; case TpInt: { compareFunc_p = ObjCompare::compare; lastValue_p = new Int [nrelem_p]; Int undef = 0; objset ((Int*)lastValue_p, undef, nrelem_p); } break; case TpUInt: { compareFunc_p = ObjCompare::compare; lastValue_p = new uInt [nrelem_p]; uInt undef = 0; objset ((uInt*)lastValue_p, undef, nrelem_p); } break; case TpInt64: { compareFunc_p = ObjCompare::compare; lastValue_p = new Int64 [nrelem_p]; Int64 undef = 0; objset ((Int64*)lastValue_p, undef, nrelem_p); } break; case TpFloat: { compareFunc_p = ObjCompare::compare; lastValue_p = new float [nrelem_p]; float undef = 0; objset ((float*)lastValue_p, undef, nrelem_p); } break; case TpDouble: { compareFunc_p = ObjCompare::compare; lastValue_p = new double [nrelem_p]; double undef = 0; objset ((double*)lastValue_p, undef, nrelem_p); } break; case TpComplex: { compareFunc_p = ObjCompare::compare; lastValue_p = new Complex [nrelem_p]; Complex undef; objset ((Complex*)lastValue_p, undef, nrelem_p); } break; case TpDComplex: { compareFunc_p = ObjCompare::compare; lastValue_p = new DComplex [nrelem_p]; DComplex undef; objset ((DComplex*)lastValue_p, undef, nrelem_p); } break; case TpString: { if (asBigEndian) { readFunc_p = readStringBE; writeFunc_p = writeStringBE; }else{ readFunc_p = readStringLE; writeFunc_p = writeStringLE; } compareFunc_p = ObjCompare::compare; lastValue_p = new String [nrelem_p]; String undef; objset ((String*)lastValue_p, undef, nrelem_p); } break; default: AlwaysAssert (0, AipsError); } AlwaysAssert (lastValue_p != 0, AipsError); } void ISMColumn::doCreate (ISMBucket* bucket) { init(); char* buffer = stmanPtr_p->tempBuffer(); uInt leng = writeFunc_p (buffer, lastValue_p, nrcopy_p); bucket->addData (colnr_p, 0, 0, buffer, leng); } void ISMColumn::getFile (rownr_t nrrow) { init(); lastRowPut_p = nrrow; } Bool ISMColumn::flush (rownr_t, Bool) { return False; } void ISMColumn::resync (rownr_t nrrow) { // Invalidate the last value read. columnCache().invalidate(); startRow_p = 1; endRow_p = 0; lastRowPut_p = nrrow; } void ISMColumn::reopenRW() {} Conversion::ValueFunction* ISMColumn::getReaduInt (Bool asBigEndian) { if (asBigEndian) { return CanonicalConversion::getToLocal (static_cast(0)); } return LECanonicalConversion::getToLocal (static_cast(0)); } Conversion::ValueFunction* ISMColumn::getReadRownr (Bool asBigEndian) { if (asBigEndian) { return CanonicalConversion::getToLocal (static_cast(0)); } return LECanonicalConversion::getToLocal (static_cast(0)); } Conversion::ValueFunction* ISMColumn::getWriteuInt (Bool asBigEndian) { if (asBigEndian) { return CanonicalConversion::getFromLocal (static_cast(0)); } return LECanonicalConversion::getFromLocal (static_cast(0)); } Conversion::ValueFunction* ISMColumn::getWriteRownr (Bool asBigEndian) { if (asBigEndian) { return CanonicalConversion::getFromLocal (static_cast(0)); } return LECanonicalConversion::getFromLocal (static_cast(0)); } size_t ISMColumn::fromString (void* out, const void* in, size_t n, Conversion::ValueFunction* writeLeng) { // The first entry represents the length of the entire object. // If there is only one string, it is the length of the string. // If there are multiple strings, it is the length of all strings // (including the lengths of their lengths). // Note that the length of itself is also included. char* buf = (char*)out; uInt leng = 0; uInt strleng; if (n > 1) { leng = writeLeng (buf, &leng, 1); } for (size_t i=0; i 1) { leng += readLeng (&strleng, buf + leng, 1); } String& str = static_cast(out)[i]; str.resize (strleng); // resize storage which adds trailing 0 char* var = &(str[0]); // get actual string memcpy (var, buf + leng, strleng); #ifdef USE_OLD_STRING var[strleng] = '\0'; #endif leng += strleng; } return leng; } size_t ISMColumn::writeStringBE (void* out, const void* in, size_t n) { return fromString (out, in, n, CanonicalConversion::fromLocalUInt); } size_t ISMColumn::readStringBE (void* out, const void* in, size_t n) { return toString (out, in, n, CanonicalConversion::toLocalUInt); } size_t ISMColumn::writeStringLE (void* out, const void* in, size_t n) { return fromString (out, in, n, LECanonicalConversion::fromLocalUInt); } size_t ISMColumn::readStringLE (void* out, const void* in, size_t n) { return toString (out, in, n, LECanonicalConversion::toLocalUInt); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/ISMColumn.h000066400000000000000000000373241476623553700200730ustar00rootroot00000000000000//# ISMColumn.h: A Column in the Incremental Storage Manager //# Copyright (C) 1996,1997,1998,1999,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_ISMCOLUMN_H #define TABLES_ISMCOLUMN_H //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations class ISMBucket; //

        // A Column in the Incremental Storage Manager. // // // // // //# Classes you should understand before using this one. //
      • ISMBase // // // ISMColumn represents a Column in the Incremental Storage Manager. // // // ISMColumn handles the access to a column containing scalars or direct // arrays of the various data types. It uses class // ISMBucket to get and put the data into the correct bucket. // When the value does not fit in the bucket, the bucket is split // and the new bucket is added to the storage manager. //

        // The object maintains a variable indicating the last row ever put. // This is used to decide if a put of a value is valid until the // end of the table or for that one row only. In this way it does not // make any difference if rows are added before or after a value is put //
        // A value put before or at the last row ever put will only affect that // one row. The rows before and after it keep their original value. If // needed that value is copied. //

        // To optimize (especially sequential) access to the column, ISMColumn // maintains the last value gotten and the rows for which it is valid. // In this way a get does not need to access the data in the bucket. //

        // ISMColumn use the static conversion functions in the // Conversion framework to // get/put the data in external format (be it canonical or local). // Most data types are fixed length, but some are variable length // (e.g. String). In external format variable length data is preceeded // by its total length (which includes the length itself). This makes // it possible to get the length of a data value without having to // interpret it, which is easy when (re)moving a value. For this reason // ISMColumn contains its own conversion functions for Strings. //

        // ISMColumn also acts as the base class for more specialized ISM // column classes (i.e. ISMIndColumn // for indirect columns). // In this way ISMBase can hold a // block of ISMColumn* for any column. Furthermore // ISMColumn contains the hooks to allow a derived class // to use other ISMColumn functions (e.g. there are "action" functions // for a derived class to react on the duplication or removal of // a data value (e.g. due to a bucket split). // // // ISMColumn encapsulates all operations on an ISM Column. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class ISMColumn : public StManColumnBase { public: // Create a ISMColumn object with the given parent. // It initializes the various variables. // It keeps the pointer to its parent (but does not own it). ISMColumn (ISMBase* parent, int dataType, uInt colnr); ~ISMColumn(); // Forbid copy constructor. ISMColumn (const ISMColumn&) = delete; // Forbid assignment. ISMColumn& operator= (const ISMColumn&) = delete; // Set the shape of an array in the column. virtual void setShapeColumn (const IPosition& shape); // Get the dimensionality of the item in the given row. // This is the same for all rows. virtual uInt ndim (rownr_t rownr); // Get the shape of the array in the given row. // This is the same for all rows. virtual IPosition shape (rownr_t rownr); // Let the column object initialize itself for a newly created table. // This is meant for a derived class. virtual void doCreate (ISMBucket*); // Let the column object initialize itself for an existing table. virtual void getFile (rownr_t nrrow); // Flush and optionally fsync the data. // This is meant for a derived class. virtual Bool flush (rownr_t nrrow, Bool fsync); // Resync the storage manager with the new file contents. // It resets the last rownr put. void resync (rownr_t nrrow); // Let the column reopen its data files for read/write access. virtual void reopenRW(); // Get a scalar value in the given row. // virtual void getBool (rownr_t rownr, Bool* dataPtr); virtual void getuChar (rownr_t rownr, uChar* dataPtr); virtual void getShort (rownr_t rownr, Short* dataPtr); virtual void getuShort (rownr_t rownr, uShort* dataPtr); virtual void getInt (rownr_t rownr, Int* dataPtr); virtual void getuInt (rownr_t rownr, uInt* dataPtr); virtual void getInt64 (rownr_t rownr, Int64* dataPtr); virtual void getfloat (rownr_t rownr, float* dataPtr); virtual void getdouble (rownr_t rownr, double* dataPtr); virtual void getComplex (rownr_t rownr, Complex* dataPtr); virtual void getDComplex (rownr_t rownr, DComplex* dataPtr); virtual void getString (rownr_t rownr, String* dataPtr); // // Put a scalar value in the given row. // virtual void putBool (rownr_t rownr, const Bool* dataPtr); virtual void putuChar (rownr_t rownr, const uChar* dataPtr); virtual void putShort (rownr_t rownr, const Short* dataPtr); virtual void putuShort (rownr_t rownr, const uShort* dataPtr); virtual void putInt (rownr_t rownr, const Int* dataPtr); virtual void putuInt (rownr_t rownr, const uInt* dataPtr); virtual void putInt64 (rownr_t rownr, const Int64* dataPtr); virtual void putfloat (rownr_t rownr, const float* dataPtr); virtual void putdouble (rownr_t rownr, const double* dataPtr); virtual void putComplex (rownr_t rownr, const Complex* dataPtr); virtual void putDComplex (rownr_t rownr, const DComplex* dataPtr); virtual void putString (rownr_t rownr, const String* dataPtr); // // Get the scalar values in the entire column. // The buffer pointed to by dataPtr has to have the correct length. // (which is guaranteed by the ScalarColumn getColumn function). virtual void getScalarColumnV (ArrayBase& dataPtr); // Put the scalar values into the entire column. // The buffer pointed to by dataPtr has to have the correct length. // (which is guaranteed by the ScalarColumn putColumn function). virtual void putScalarColumnV (const ArrayBase& dataPtr); // Get the scalar values in some cells of the column. // The buffer pointed to by dataPtr has to have the correct length. // (which is guaranteed by the ScalarColumn getColumnCells function). virtual void getScalarColumnCellsV (const RefRows& rownrs, ArrayBase& dataPtr); // Get an array value in the given row. virtual void getArrayV (rownr_t rownr, ArrayBase& dataPtr); // Put an array value in the given row. virtual void putArrayV (rownr_t rownr, const ArrayBase& dataPtr); // Add (newNrrow-oldNrrow) rows to the column and initialize // the new rows when needed. virtual void addRow (rownr_t newNrrow, rownr_t oldNrrow); // Remove the given row in the bucket from the column. void remove (rownr_t bucketRownr, ISMBucket* bucket, rownr_t bucketNrrow, rownr_t newNrrow); // Get the function needed to read/write a uInt and rownr from/to // external format. This is used by other classes to read the length // of a variable data value. // static Conversion::ValueFunction* getReaduInt (Bool asCanonical); static Conversion::ValueFunction* getReadRownr (Bool asCanonical); static Conversion::ValueFunction* getWriteuInt (Bool asCanonical); static Conversion::ValueFunction* getWriteRownr (Bool asCanonical); // // Give a derived class the opportunity to react on the duplication // of a value. It is used by ISMIndColumn. virtual void handleCopy (rownr_t rownr, const char* value); // Give a derived class the opportunity to react on the removal // of a value. It is used by ISMIndColumn. virtual void handleRemove (rownr_t rownr, const char* value); // Get the fixed length of the data value in a cell of this column // (0 = variable length). uInt getFixedLength() const; // Get the nr of elements in this data value. uInt nelements() const; protected: // Test if the last value is invalid for this row. Bool isLastValueInvalid (rownr_t rownr); // Get the value for this row. // Set the cache if the flag is set. void getValue (rownr_t rownr, void* value, Bool setCache); // Put the value for this row. void putValue (rownr_t rownr, const void* value); //# Declare member variables. // Pointer to the parent storage manager. ISMBase* stmanPtr_p; // Length of column cell value in storage format (0 = variable length). // If 0, the value is always preceeded by a uInt giving the length. uInt fixedLength_p; // Column sequence number of this column. uInt colnr_p; // The shape of the column. IPosition shape_p; // Number of elements in a value for this column. uInt nrelem_p; // Number of values to be copied. // Normally this is nrelem_p, but for complex types it is 2*nrelem_p. // When local format is used, it is the number of bytes. uInt nrcopy_p; // Cache for interval for which last value read is valid. // The last value is valid for startRow_p till endRow_p (inclusive). rownr_t startRow_p; rownr_t endRow_p; void* lastValue_p; // The last row for which a value has been put. rownr_t lastRowPut_p; // The size of the data type in local format. uInt typeSize_p; // Pointer to a convert function for writing. Conversion::ValueFunction* writeFunc_p; // Pointer to a convert function for reading. Conversion::ValueFunction* readFunc_p; // Pointer to a compare function. ObjCompareFunc* compareFunc_p; private: // Initialize part of the object. // It is used by doCreate and getFile. void init(); // Clear the object (used by destructor and init). void clear(); // Put the value in all buckets from the given row on. void putFromRow (rownr_t rownr, const char* data, uInt lenData); // Put a data value into the bucket. // When it is at the first row of the bucket, it replaces the value. // Otherwise it is added. void putData (ISMBucket* bucket, rownr_t bucketStartRow, rownr_t bucketNrrow, rownr_t bucketRownr, const char* data, uInt lenData, Bool afterLastRow, Bool canSplit); // Replace a value at the given offset in the bucket. // If the bucket is too small, it will be split (if allowed). void replaceData (ISMBucket* bucket, rownr_t bucketStartRow, rownr_t bucketNrrow, rownr_t bucketRownr, uInt& offset, const char* data, uInt lenData, Bool canSplit = True); // Add a value at the given index in the bucket. // If the bucket is too small, it will be split (if allowed). Bool addData (ISMBucket* bucket, rownr_t bucketStartRow, rownr_t bucketNrrow, rownr_t bucketRownr, uInt inx, const char* data, uInt lenData, Bool afterLastRow = False, Bool canSplit = True); // Handle the duplicated values after a bucket split. void handleSplit (ISMBucket& bucket, const Block& duplicated); // Compare the values. virtual Bool compareValue (const void* val1, const void* val2) const; // Handle a String in copying to/from external format. // static size_t fromString (void* out, const void* in, size_t n, Conversion::ValueFunction* writeLeng); static size_t toString (void* out, const void* in, size_t n, Conversion::ValueFunction* readLeng); static size_t writeStringBE (void* out, const void* in, size_t n); static size_t readStringBE (void* out, const void* in, size_t n); static size_t writeStringLE (void* out, const void* in, size_t n); static size_t readStringLE (void* out, const void* in, size_t n); // void getScaCol (Vector&); void getScaCol (Vector&); void getScaCol (Vector&); void getScaCol (Vector&); void getScaCol (Vector&); void getScaCol (Vector&); void getScaCol (Vector&); void getScaCol (Vector&); void getScaCol (Vector&); void getScaCol (Vector&); void getScaCol (Vector&); void getScaCol (Vector&); void getScaColCells (const RefRows&, Vector&); void getScaColCells (const RefRows&, Vector&); void getScaColCells (const RefRows&, Vector&); void getScaColCells (const RefRows&, Vector&); void getScaColCells (const RefRows&, Vector&); void getScaColCells (const RefRows&, Vector&); void getScaColCells (const RefRows&, Vector&); void getScaColCells (const RefRows&, Vector&); void getScaColCells (const RefRows&, Vector&); void getScaColCells (const RefRows&, Vector&); void getScaColCells (const RefRows&, Vector&); void getScaColCells (const RefRows&, Vector&); void putScaCol (const Vector&); void putScaCol (const Vector&); void putScaCol (const Vector&); void putScaCol (const Vector&); void putScaCol (const Vector&); void putScaCol (const Vector&); void putScaCol (const Vector&); void putScaCol (const Vector&); void putScaCol (const Vector&); void putScaCol (const Vector&); void putScaCol (const Vector&); void putScaCol (const Vector&); }; inline Bool ISMColumn::isLastValueInvalid (rownr_t rownr) { return rownr < startRow_p || rownr > endRow_p; } inline uInt ISMColumn::getFixedLength() const { return fixedLength_p; } inline uInt ISMColumn::nelements() const { return nrelem_p; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/ISMIndColumn.cc000066400000000000000000000235171476623553700206630ustar00rootroot00000000000000//# ISMIndColumn.cc: Column of Incremental storage manager for indirect arrays //# Copyright (C) 1996,1997,1998,1999,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include // for snprintf namespace casacore { //# NAMESPACE CASACORE - BEGIN ISMIndColumn::ISMIndColumn (ISMBase* smptr, int dataType, uInt colnr) : ISMColumn (smptr, dataType, colnr), seqnr_p (smptr->uniqueNr()), shapeIsFixed_p(False), iosfile_p (0), indArray_p (0) {} ISMIndColumn::~ISMIndColumn() { clear(); } void ISMIndColumn::clear() { delete (Int64*)lastValue_p; lastValue_p = 0; if (stmanPtr_p->version() < 3) { delete iosfile_p; } iosfile_p = 0; } //# Create the array file (for a new column). //# Compose the file name from the mother file name extended with //# the unique column sequence nr. void ISMIndColumn::doCreate (ISMBucket* bucket) { // Initialize and create new file. init (ByteIO::New); // Insert a dummy zero offset as the first value. lastRowPut_p = 0; *(Int64*)lastValue_p = 0; char* buffer = stmanPtr_p->tempBuffer(); uInt leng = writeFunc_p (buffer, lastValue_p, 1); bucket->addData (colnr_p, 0, 0, buffer, leng); } void ISMIndColumn::getFile (rownr_t nrrow) { // Initialize and open existing file. init (stmanPtr_p->fileOption()); lastRowPut_p = nrrow; } Bool ISMIndColumn::flush (rownr_t, Bool fsync) { return iosfile_p->flush (fsync); } void ISMIndColumn::resync (rownr_t nrrow) { ISMColumn::resync (nrrow); if (stmanPtr_p->version() < 3) { iosfile_p->resync(); } } void ISMIndColumn::reopenRW() { iosfile_p->reopenRW(); } void ISMIndColumn::addRow (rownr_t, rownr_t oldNrrow) { // If the shape is fixed and if the first row is added, define // an array to have an array for all rows. // Later rows get the value of a previous row, so we don't have to // do anything for them. if (oldNrrow == 0 && shapeIsFixed_p) { putShape (0, fixedShape_p); } } void ISMIndColumn::setShapeColumn (const IPosition& shape) { fixedShape_p = shape; shapeIsFixed_p = True; } void ISMIndColumn::setShape (rownr_t rownr, const IPosition& shape) { StIndArray* ptr = getArrayPtr (rownr); if (ptr != 0) { ptr->getShape (*iosfile_p); } if (ptr == 0 || !shape.isEqual (ptr->shape())) { putShape (rownr, shape); } } //# Get the shape for the array (if any) in the given row. //# Read shape if not read yet. StIndArray* ISMIndColumn::getArrayPtr (rownr_t rownr) { if (isLastValueInvalid (rownr)) { getValue (rownr, lastValue_p, False); Int64 offset = *(Int64*)lastValue_p; if (offset != 0) { indArray_p = StIndArray (offset); foundArray_p = True; }else{ foundArray_p = False; } } if (foundArray_p) { return &indArray_p; } return 0; } //# Get the shape for the array (if any) in the given row. //# Read shape if not read yet. StIndArray* ISMIndColumn::getShape (rownr_t rownr) { StIndArray* ptr = getArrayPtr (rownr); if (ptr == 0) { throw DataManInvOper ("ISM: no array in row " + String::toString(rownr) + " in column " + columnName() + " of " + stmanPtr_p->fileName()); } ptr->getShape (*iosfile_p); return ptr; } //# Set the shape for the array in the given row for a put operation. StIndArray* ISMIndColumn::putShape (rownr_t rownr, const IPosition& shape) { //# Insert an entry for this row and set its shape. //# Nothing will be done if it is already defined. return putArrayPtr (rownr, shape, False); // StIndArray* ptr = putArrayPtr (rownr, shape, False); // ptr->setShape (*iosfile_p, dataType(), shape); // return ptr; } //# Set the shape for the array (if any) in the given row for a sliced //# put operation. StIndArray* ISMIndColumn::putShapeSliced (rownr_t rownr) { //# Get the shape of this row and define it again. //# Defining is necessary, because the shape gotten may be valid for //# row 10-20, while the put is only for row 15. In that case row 15 //# has to be inserted. StIndArray* ptr = getShape (rownr); return putArrayPtr (rownr, ptr->shape(), True); } Bool ISMIndColumn::isShapeDefined (rownr_t rownr) { return (getArrayPtr(rownr) == 0 ? False : True); } uInt ISMIndColumn::ndim (rownr_t rownr) { return getShape(rownr)->shape().nelements(); } IPosition ISMIndColumn::shape (rownr_t rownr) { return getShape(rownr)->shape(); } Bool ISMIndColumn::canChangeShape() const { return (shapeIsFixed_p ? False : True); } StIndArray* ISMIndColumn::putArrayPtr (rownr_t rownr, const IPosition& shape, Bool copyData) { // Start with getting the array pointer. This gives the range // for which this array is valid. StIndArray* ptr = getArrayPtr (rownr); // When the shape is put for the last row ever put, don't do it // when the shape is equal. This is needed to get correct behaviour // when an array is put. Putting an array calls setShape and then // putXXXV resulting in two calls to putShape. if (ptr != 0 && rownr+1 == lastRowPut_p) { ptr->getShape (*iosfile_p); if (shape.isEqual (ptr->shape())) { return ptr; } } // When the interval contains a single row, we can do a simple replace // if the value is not shared. if (ptr != 0 && startRow_p == endRow_p) { if (ptr->refCount (*iosfile_p) <= 1) { // The value is not shared, so we can replace it. ptr->setShape (*iosfile_p, dataType(), shape); Int64 offset = ptr->fileOffset(); putValue (rownr, &offset); return ptr; } } // Make a new IndArray object. StIndArray tmp(0); tmp.setShape (*iosfile_p, dataType(), shape); if (copyData) { tmp.copyData (*iosfile_p, dataType(), *ptr); } indArray_p = tmp; Int64 offset = indArray_p.fileOffset(); putValue (rownr, &offset); return &indArray_p; } void ISMIndColumn::getArrayV (rownr_t rownr, ArrayBase& arr) { getShape(rownr)->getArrayV (*iosfile_p, arr, dtype()); } void ISMIndColumn::putArrayV (rownr_t rownr, const ArrayBase& arr) { putShape(rownr, arr.shape())->putArrayV (*iosfile_p, arr, dtype()); } void ISMIndColumn::getSliceV (rownr_t rownr, const Slicer& ns, ArrayBase& arr) { getShape(rownr)->getSliceV (*iosfile_p, ns, arr, dtype()); } void ISMIndColumn::putSliceV (rownr_t rownr, const Slicer& ns, const ArrayBase& arr) { putShapeSliced(rownr)->putSliceV (*iosfile_p, ns, arr, dtype()); } Bool ISMIndColumn::compareValue (const void*, const void*) const { return False; } void ISMIndColumn::init (ByteIO::OpenOption fileOption) { clear(); DebugAssert (nrelem_p==1, AipsError); Bool asBigEndian = stmanPtr_p->asBigEndian(); if (asBigEndian) { readFunc_p = CanonicalConversion::getToLocal (static_cast(0)); writeFunc_p = CanonicalConversion::getFromLocal (static_cast(0)); fixedLength_p = CanonicalConversion::canonicalSize (static_cast(0)); nrcopy_p = 1; }else{ readFunc_p = LECanonicalConversion::getToLocal (static_cast(0)); writeFunc_p = LECanonicalConversion::getFromLocal (static_cast(0)); fixedLength_p = LECanonicalConversion::canonicalSize (static_cast(0)); nrcopy_p = 1; } lastValue_p = new Int64; //# Open or create the type 1 file to hold the arrays in the column. //# For newer versions one file is maintained by the parent //# for all indirect columns. if (stmanPtr_p->version() >= 3) { iosfile_p = stmanPtr_p->openArrayFile (fileOption); } else { char strc[8]; snprintf (strc, sizeof(strc), "i%i", seqnr_p); iosfile_p = new StManArrayFile (stmanPtr_p->fileName() + strc, fileOption, 1, asBigEndian); } } void ISMIndColumn::handleCopy (rownr_t, const char* value) { Int64 offset; readFunc_p (&offset, value, nrcopy_p); if (offset != 0) { StIndArray tmp (offset); tmp.incrementRefCount (*iosfile_p); } } void ISMIndColumn::handleRemove (rownr_t, const char* value) { Int64 offset; readFunc_p (&offset, value, nrcopy_p); if (offset != 0) { StIndArray tmp (offset); tmp.decrementRefCount (*iosfile_p); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/ISMIndColumn.h000066400000000000000000000211631476623553700205200ustar00rootroot00000000000000//# ISMIndColumn.h: A column in Incremental storage manager for indirect arrays //# Copyright (C) 1996,1997,1998,1999,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_ISMINDCOLUMN_H #define TABLES_ISMINDCOLUMN_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class StManArrayFile; class AipsIO; //

        // A column of Incremental storage manager for indirect arrays. // // // // // //# Classes you should understand before using this one. //
      • ISMColumn //
      • StIndArray // // // ISMIndColumn represents a Column in the Incremental Storage Manager // containing INDirect arrays. // // // ISMIndColumn is the implementation of an // ISMColumn class // to handle indirect arrays. The arrays (shape and data) are stored in // a separate file using class StIndArray. // The file offset of the beginning of the array in stored in the // ISM using the standard ISMColumn functions. //

        // ISMIndColumn contains functions which are called when ISMColumn // duplicates or removes a value. In that way the array can also be // duplicated or removed in the StIndArray file by incrementing or // decrementing the reference count manitained in the file. //

        // Unlike ISMColumn it is not tested if a value put is equal to // the value in the previous or next row, because it is too time-consuming // to do so (although this behaviour could be changed in the future). // Instead the user should not put equal values to prevent storing // equal values. //

        // Note that an indirect array can have a fixed shape. In that case // adding a row results in reserving space for the array in the StIndArray // file, so for each row an array is present. // On the other hand adding a row does nothing for variable shaped arrays. // So when no data is put or shape is set, a row may contain no array at all. // In that case the function isShapeDefined returns False for // that row. // // //# A List of bugs, limitations, extensions or planned refinements. //

      • Maybe TpArrayInt, etc. should be used instead of TpInt. // class ISMIndColumn : public ISMColumn { public: // Create a column of the given data type. // It keeps the pointer to its parent (but does not own it). ISMIndColumn (ISMBase* parent, int dataType, uInt colnr); // Frees up the storage. virtual ~ISMIndColumn(); // Forbid copy constructor. ISMIndColumn (const ISMIndColumn&) = delete; // Forbid assignment. ISMIndColumn& operator= (const ISMIndColumn&) = delete; // Add (newNrrow-oldNrrow) rows to the column. virtual void addRow (rownr_t newNrrow, rownr_t oldNrrow); // Set the (fixed) shape of the arrays in the entire column. virtual void setShapeColumn (const IPosition& shape); // Get the dimensionality of the item in the given row. virtual uInt ndim (rownr_t rownr); // Set the shape of the array in the given row and allocate the array // in the file. virtual void setShape (rownr_t rownr, const IPosition& shape); // Is the shape defined (i.e. is there an array) in this row? virtual Bool isShapeDefined (rownr_t rownr); // Get the shape of the array in the given row. virtual IPosition shape (rownr_t rownr); // This storage manager can handle changing array shapes. virtual Bool canChangeShape() const; // Get an array value in the given row. // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the ArrayColumn get function). virtual void getArrayV (rownr_t rownr, ArrayBase&); // Put an array value into the given row. // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the ArrayColumn put function). virtual void putArrayV (rownr_t rownr, const ArrayBase&); // Get a section of the array in the given row. // The array has to have the correct length // (which is guaranteed by the ArrayColumn getSlice function). virtual void getSliceV (rownr_t rownr, const Slicer&, ArrayBase&); // Put into a section of the array in the given row. // The array has to have the correct length // (which is guaranteed by the ArrayColumn putSlice function). virtual void putSliceV (rownr_t rownr, const Slicer&, const ArrayBase&); // Let the column object create its array file. virtual void doCreate (ISMBucket* bucket); // Let the column object open an existing file. virtual void getFile (rownr_t nrrow); // Flush and optionally fsync the data. virtual Bool flush (rownr_t nrrow, Bool fsync); // Resync the storage manager with the new file contents. virtual void resync (rownr_t nrrow); // Let the column reopen its data files for read/write access. virtual void reopenRW(); // Handle the duplication of a value; i.e. increment its reference count. virtual void handleCopy (rownr_t rownr, const char* value); // Handle the removal of a value; i.e. decrement its reference count. virtual void handleRemove (rownr_t rownr, const char* value); private: // Initialize part of the object and open/create the file. // It is used by doCreate and getFile. void init (ByteIO::OpenOption fileOption); // Clear the object (used by destructor and init). void clear(); // Compare the values to check if a value to be put matches the // value in the previous or next row. // It always return False, because comparing large arrays is // too expensive (it could be changed in the future). virtual Bool compareValue (const void* val1, const void* val2) const; // Read the shape at the given row. // This will cache the information in the StIndArray // object for that row. StIndArray* getShape (rownr_t rownr); // Put the shape for an array being put. // When there are multiple rows in the interval, it will // split the interval. StIndArray* putShape (rownr_t rownr, const IPosition& shape); // Put the shape for an array of which a slice is being put. // It gets the shape for the given row. // When there are multiple rows in the interval, it will // split the interval and copy the data. StIndArray* putShapeSliced (rownr_t rownr); // Return a pointer to the array in the given row (for a get). StIndArray* getArrayPtr (rownr_t rownr); // When needed, create an array in the given row with the given shape. // When the array is created, its data are copied when the flag is set. StIndArray* putArrayPtr (rownr_t rownr, const IPosition& shape, Bool copyData); // The (unique) sequence number of the column. uInt seqnr_p; // The shape of all arrays in case it is fixed. IPosition fixedShape_p; // Switch indicating if the shape is fixed. Bool shapeIsFixed_p; // The file containing the arrays. StManArrayFile* iosfile_p; // The indirect array object. StIndArray indArray_p; // The indirect array exists for the row interval last accessed. Bool foundArray_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/ISMIndex.cc000066400000000000000000000126771476623553700200470ustar00rootroot00000000000000//# ISMIndex.cc: The Index of the Incremental Storage Manager //# Copyright (C) 1996,1997,1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ISMIndex::ISMIndex() : nused_p (1), rows_p (2, 0), bucketNr_p (1, 0) {} ISMIndex::~ISMIndex() {} void ISMIndex::get (AipsIO& os) { uInt version = os.getstart ("ISMIndex"); os >> nused_p; if (version > 1) { // stored as 64-bit getBlock (os, rows_p); } else { // stored as 32-bit Block rows; getBlock (os, rows); rows_p.resize (rows.size()); std::copy (rows.begin(), rows.end(), rows_p.begin()); } getBlock (os, bucketNr_p); os.getend(); } void ISMIndex::put (AipsIO& os) { // If the last rownr fits in 32-bit, write as version 1 (thus 32-bit). uInt version = (rows_p[nused_p] > DataManager::MAXROWNR32 ? 2 : 1); os.putstart ("ISMIndex", version); os << nused_p; if (version > 1) { putBlock (os, rows_p, nused_p + 1); } else { Block rows(nused_p +1); std::copy (rows_p.begin(), rows_p.begin() + nused_p + 1, rows.begin()); putBlock (os, rows, nused_p + 1); } putBlock (os, bucketNr_p, nused_p); os.putend(); } void ISMIndex::addBucketNr (rownr_t rownr, uInt bucketNr) { if (nused_p >= bucketNr_p.nelements()) { rows_p.resize (nused_p + 64 + 1); bucketNr_p.resize (nused_p + 64); } Bool found; uInt index = binarySearchBrackets (found, rows_p, rownr, nused_p); AlwaysAssert (!found, AipsError); objmove (&rows_p[index+1], &rows_p[index], nused_p + 1 - index); if (nused_p > index) { objmove (&bucketNr_p[index+1], &bucketNr_p[index], nused_p - index); } rows_p[index] = rownr; bucketNr_p[index] = bucketNr; nused_p++; } void ISMIndex::addRow (rownr_t nrrow) { rows_p[nused_p] += nrrow; } Int ISMIndex::removeRow (rownr_t rownr) { // Decrement the row number for all intervals after the row // to be removed. uInt index = getIndex (rownr); for (uInt i=index+1; i<=nused_p; i++) { rows_p[i]--; } // Remove the entire bucket when no row is left. Int emptyBucket = -1; if (rows_p[index] == rows_p[index+1]) { emptyBucket = bucketNr_p[index]; if (nused_p > index+1) { objmove (&rows_p[index+1], &rows_p[index+2], nused_p - index - 1); objmove (&bucketNr_p[index], &bucketNr_p[index+1], nused_p - index - 1); } rows_p[nused_p] = 0; // There should always be one interval. if (nused_p > 1) { nused_p--; } } return emptyBucket; } uInt ISMIndex::getIndex (rownr_t rownr) const { // If no exact match, the interval starts at the previous index. Bool found; uInt index = binarySearchBrackets (found, rows_p, rownr, (uInt)nused_p+1); if (!found) { index--; } AlwaysAssert (index <= nused_p, AipsError); return index; } uInt ISMIndex::getBucketNr (rownr_t rownr, rownr_t& bucketStartRow, rownr_t& bucketNrrow) const { uInt index = getIndex (rownr); bucketStartRow = rows_p[index]; bucketNrrow = rows_p[index+1] - bucketStartRow; return bucketNr_p[index]; } Bool ISMIndex::nextBucketNr (uInt& cursor, rownr_t& bucketStartRow, rownr_t& bucketNrrow, uInt& bucketNr) const { // When first time, get the index of the bucket containing the row. // End the iteration when the first row is past the end. if (cursor == 0) { if (bucketStartRow >= rows_p[nused_p]) { return False; } cursor = getIndex (bucketStartRow); }else{ // Not the first time. // End the iteration when no more buckets. if (cursor >= nused_p) { return False; } } bucketStartRow = rows_p[cursor]; bucketNrrow = rows_p[cursor+1] - bucketStartRow; bucketNr = bucketNr_p[cursor++]; return True; } void ISMIndex::show (ostream& os) const { os << "ISMIndex " << nused_p << " strow:bucket"; for (uInt i=0; i #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations class ISMBase; class AipsIO; // // The Index of the Incremental Storage Manager // // // // // //# Classes you should understand before using this one. //
      • ISMBase // // // ISMIndex represents the index in the Incremental Storage Manager. // // // ISMIndex maintains an index of all buckets in an ISM (Incremental Storage // Manager). The index consists of the starting row number and the // bucket number of each bucket in the BucketCache object of the ISM. // When the ISM is opened, the entire index is read in and kept in memory. // When the ISM is closed or flushed, the index is written back after // all buckets in the file. A little header at the beginning of the file // indicates the starting offset of the index. // // // ISMIndex encapsulates all operations on the ISM index. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class ISMIndex { public: ISMIndex(); // The destructor closes the file (if opened). ~ISMIndex(); // Forbid copy constructor. ISMIndex (const ISMIndex&) = delete; // Forbid assignment. ISMIndex& operator= (const ISMIndex&) = delete; // Add a row. void addRow (rownr_t nrrow); // Remove a row from the index. // If the result of this is that the entire bucket gets empty, // that bucketnr is returned. Otherwise -1 is returned. Int removeRow (rownr_t rownr); // Get the bucket number for the given row. // Also return the start row of the bucket and the number of rows in it. uInt getBucketNr (rownr_t rownr, rownr_t& bucketStartRow, rownr_t& bucketNrrow) const; // Read the bucket index from the AipsIO object. void get (AipsIO& os); // Write the bucket index into the AipsIO object. void put (AipsIO& os); // Add a bucket number to the index. // Argument rownr gives the starting row of the bucket. // It is used to add the bucket number at the correct place // (such that the row numbers are kept in ascending order). void addBucketNr (rownr_t rownr, uInt bucketNr); // Get the number of the next bucket from the index and return // it in bucketNr. The starting row of that bucket and // the number of rows in the bucket are also returned. // Return status False indicates that no more buckets are available. //
        The start of the iteration is indicated by cursor=0. // The first bucket returned is the bucket containing the rownr // given in bucketStartRow (thus set bucketStartRow // to 0 if you want to start at the first bucket). //
        The next iterations return the next bucket number and fill // the starting row and number of rows. Bool nextBucketNr (uInt& cursor, rownr_t& bucketStartRow, rownr_t& bucketNrrow, uInt& bucketNr) const; // Show the index. void show (std::ostream&) const; private: // Get the index of the bucket containing the given row. uInt getIndex (rownr_t rownr) const; //# Declare member variables. // Number of entries used. uInt nused_p; // Rownr index (i.e. row rows_p[i] starts in bucketNr_p[i]). Block rows_p; // Corresponding bucket number. Block bucketNr_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/IncrStManAccessor.cc000066400000000000000000000104111476623553700217300ustar00rootroot00000000000000//# IncrStManAccessor.cc: Gives access to some IncrementalStMan functions //# Copyright (C) 1996 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ROIncrementalStManAccessor::ROIncrementalStManAccessor (const Table& table, const String& name, Bool byColumn) : RODataManAccessor (table, name, byColumn), dataManPtr_p (0) { dataManPtr_p = dynamic_cast(baseDataManager()); if (dataManPtr_p == 0) { throw (DataManError ("ROIncrementalStManAccessor " + name + " constructed for data manager type " + baseDataManager()->dataManagerType() + "; expected IncrementalStMan")); } } ROIncrementalStManAccessor::~ROIncrementalStManAccessor() {} ROIncrementalStManAccessor::ROIncrementalStManAccessor (const ROIncrementalStManAccessor& that) : RODataManAccessor(that), dataManPtr_p (that.dataManPtr_p) {} ROIncrementalStManAccessor& ROIncrementalStManAccessor::operator= (const ROIncrementalStManAccessor& that) { dataManPtr_p = that.dataManPtr_p; return *this; } void ROIncrementalStManAccessor::setCacheSize (uInt size, Bool canExceedNrBuckets) { dataManPtr_p->setCacheSize (size, canExceedNrBuckets); } uInt ROIncrementalStManAccessor::cacheSize() const { return dataManPtr_p->cacheSize(); } void ROIncrementalStManAccessor::clearCache() { dataManPtr_p->clearCache(); } void ROIncrementalStManAccessor::showIndexStatistics (ostream& os) const { dataManPtr_p->showIndexStatistics (os); } void ROIncrementalStManAccessor::showBucketLayout (ostream& os) const { dataManPtr_p->showBucketLayout (os); } Bool ROIncrementalStManAccessor::checkBucketLayout (uInt& offendingCursor, rownr_t& offendingBucketStartRow, uInt& offendingBucketNrow, uInt& offendingBucketNr, uInt& offendingCol, uInt& offendingIndex, rownr_t& offendingRow, rownr_t& offendingPrevRow) const { Bool ok; ok = dataManPtr_p->checkBucketLayout (offendingCursor, offendingBucketStartRow, offendingBucketNrow, offendingBucketNr, offendingCol, offendingIndex, offendingRow, offendingPrevRow); return ok; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/IncrStManAccessor.h000066400000000000000000000136561476623553700216100ustar00rootroot00000000000000//# IncrStManAccessor.h: Gives access to some IncrementalStMan functions //# Copyright (C) 1996,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_INCRSTMANACCESSOR_H #define TABLES_INCRSTMANACCESSOR_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class ISMBase; class DataManager; class Table; class String; // // Give access to some IncrementalStMan functions // // // // // //# Classes you should understand before using this one. //
      • IncrementalStMan // // // The Table system has one or more storage managers underneath. // One of these possible storage managers is the // IncrementalStMan. // This storage manager uses a cache of buckets. The default // cache size is defined when the IncrementalStMan object was // constructed at the time the table was created. //

        // Sometimes it can be useful to change the cache size. E.g. when // the table is accessed in a random way, the hit rate may drop // when the cache is too small. The class ROIncrStManAccessor makes // it possible to change the cache size in a temporary way. //
        // Furthermore this class makes it possible to show the cache size // and to show the cache statistics. // // In principle a pointer to IncrementalStMan could be used. // However, that would give access to all public functions. // Furthermore it could not distinguish between read/write and readonly // tables. // // // This example shows how to set the cache size for // the incremental storage manager with the name "ISMExample". The cache // size is not persistent, i.e. when the same table is reopened // at a later time, this cache size is not remembered. // // // Open a table. // Table table("someName.data"); // // Set the cache size of its incremental storage manager ISMExample // // to 5 buckets. // ROIncrementalStManAccessor accessor(table, "ISMExample"); // accessor.setCacheSize (5); // // //# //# class ROIncrementalStManAccessor : public RODataManAccessor { public: // Construct the object for a data manager in the table given the name // of the data manager or the column. // An exception is thrown if the data manager type is not the incremental // storage manager. ROIncrementalStManAccessor (const Table& table, const String& name, Bool byColumn=False); virtual ~ROIncrementalStManAccessor(); // Copy constructor (reference semantics). ROIncrementalStManAccessor (const ROIncrementalStManAccessor& that); // Assignment (reference semantics). ROIncrementalStManAccessor& operator= (const ROIncrementalStManAccessor& that); // Set the cache size (in buckets) to be used by the // storage manager. // The cache size given in this way is not persistent. // Only the cache size given to the constructors of the incremental // storage managers, is persistent. // If canExceedNrBuckets=True, the given cache size can be // larger than the nr of buckets in the file. In this way the cache can // be made large enough for a future file extnsion. // Otherwise, it is limited to the actual number of buckets. This is useful // if one wants the entire file to be cached. void setCacheSize (uInt aSize, Bool canExceedNrBuckets=True); // Get the cache size (in buckets). uInt cacheSize() const; // Clear the caches used by the hypercubes in this storage manager. // It will flush the caches as needed and remove all buckets from them // resulting in a possibly large drop in memory used. void clearCache(); // Show the index used by this storage manager. void showIndexStatistics (ostream& os) const; // Show the layout of the buckets used by this storage manager. void showBucketLayout (ostream& os) const; // Check that there are no repeated rowIds in the buckets comprising this ISM Bool checkBucketLayout (uInt& offendingCursor, rownr_t& offendingBucketStartRow, uInt& offendingBucketNrow, uInt& offendingBucketNr, uInt& offendingCol, uInt& offendingIndex, rownr_t& offendingRow, rownr_t& offendingPrevRow) const; private: //# Declare the data members. ISMBase* dataManPtr_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/IncrementalStMan.cc000066400000000000000000000033561476623553700216250ustar00rootroot00000000000000//# IncrementalStMan.cc: The Incremental Storage Manager //# Copyright (C) 1996 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include namespace casacore { //# NAMESPACE CASACORE - BEGIN IncrementalStMan::IncrementalStMan (uInt bucketSize, Bool checkBucketSize, uInt cacheSize) : ISMBase (bucketSize, checkBucketSize, cacheSize) {} IncrementalStMan::IncrementalStMan (const String& dataManagerName, uInt bucketSize, Bool checkBucketSize, uInt cacheSize) : ISMBase (dataManagerName, bucketSize, checkBucketSize, cacheSize) {} IncrementalStMan::~IncrementalStMan() {} } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/IncrementalStMan.h000066400000000000000000000233371476623553700214700ustar00rootroot00000000000000//# IncrementalStMan.h: The Incremental Storage Manager //# Copyright (C) 1996,1997,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_INCREMENTALSTMAN_H #define TABLES_INCREMENTALSTMAN_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //

        // The Incremental Storage Manager // // // // // //# Classes you should understand before using this one. //
      • The Table Data Managers concept as described in module file // Tables.h //
      • // ROIncrementalStManAccessor // for a discussion of the cache size // // // IncrementalStMan is the data manager storing values in an incremental way // (similar to an incremental backup). A value is only stored when it // differs from the previous value. // // // IncrementalStMan stores the data in a way that a value is only stored // when it is different from the value in the previous row. This storage // manager is very well suited for columns with slowly changing values, // because the resulting file can be much smaller. It is not suited at // all for columns with continuously changing data. //

        // In general it can be advantageous to use this storage manager when // a value changes at most every 4 rows (although it depends on the length // of the data values themselves). The following simple example // shows the approximate savings that can be achieved when storing a column // with double values changing every CH rows. // // #rows CH normal length ISM length compress ratio // 50000 5 4000000 1606000 2.5 // 50000 50 4000000 164000 24.5 // 50000 500 4000000 32800 122 // // There is a special test program nISMBucket in the Tables module // doing a simple, but usually adequate, simulation of the amount of // storage needed for a scenario. //

        // IncrementalStMan stores the values (and associated indices) in // fixed-length buckets. A BucketCache // object is used to read/write // the buckets. The default cache size is 1 bucket (which is fine for // sequential access), but for random access it can make sense to // increase the size of the cache. This can be done using // the class // ROIncrementalStManAccessor. //

        // The IncrementalStMan can hold values of any standard data type (thus // from Bool to String). It can handle scalars, direct and indirect // arrays. It can support an arbitrary number of columns. The values in // each of them can vary at its own speed. //
        // A bucket contains the values of several consecutive rows. // At the beginning of a bucket the values of the starting row of all // columns for this storage manager are repeated. In this way the value // of a cell can always be found in the bucket and no references // to previous buckets are needed. //
        A bucket should be big enough to hold all starting values and // a reasonable number of other values. As a rule of thumb it should be // big enough to hold at least 100 values of each column. In general the // default bucket size will do. Only in special cases (e.g. when storing // large variable length strings) the bucket size should be set explicitly. // Giving a zero bucket size means that a suitale default bucket size // will be calculated. //
        // When a table is filled sequentially each bucket can be filled as // much as possible. When writing in a random way, buckets can contain // some unused space, because a bucket in the middle of the file // has to be split when a new value has to be put in it. //

        // Each column in the IncrementalStMan has the following properties to // achieve the "store-different-values-only" behaviour. //

          //
        • When a row is not explicitly put, it has the same value as the // previous row. // The first row gets the standard undefined values when not put. // The order of put's and addRow's is not important. //
          E.g. when a table has N rows and row N and the following M rows // have the same value, the following schematic code has the same effect: //
          add 1 row; put value in row N; add M rows; //
          add M+1 rows; put value in row N; //
        • When putting a scalar or direct array, it is tested if it matches // the previous row. If so, it is not stored again. // This test is not done for indirect arrays, because those can // be (very) big and it would be too time-consuming. So the only // way to save space for indirect arrays is by not putting them // as explained in the previous item. //
        • For indirect arrays the buckets contain a pointer only. The // arrays themselves are stored in a separate file. //
        • When a value of an existing row is updated, only that one row is // updated. The next row(s) keep their value, even if it was // shared with the row being updated. //
          For scalars and direct arrays it will be tested if the // new value matches the value in the previous and/or next row. // If so, those rows will be combined to save storage. //
        • The IncrementalStMan is optimized for sequential access to a table. //
          - A bucket is accessed only once, because a bucket contains // consecutive rows. //
          - For each column a copy is kept of the last value read. // So the value for the next rows (with that same value) // is immediately available. //
          For random access the performance can be improved by setting // the cache size using class // // ROIncrementalStManAccessor. //
        // // This class contains many public functions which are only used // by other ISM classes. The only useful function for the user is the // constructor. // // // IncrementalStMan can save a lot of storage space. // Unlike the old StManMirAIO it stores the values directly in the // file to save on memory usage. // // // This example shows how to create a table and how to attach // the storage manager to some columns. // // SetupNewTable newtab("name.data", tableDesc, Table::New); // IncrementalStMan stman; // define storage manager // newtab.bindColumn ("column1", stman); // bind column to st.man. // newtab.bindColumn ("column2", stman); // bind column to st.man. // Table tab(newtab); // actually create table // // //# //# A List of bugs, limitations, extensions or planned refinements. //# class IncrementalStMan : public ISMBase { public: // Create an incremental storage manager with the given name. // If no name is used, it is set to an empty string. // The name can be used to construct a // ROIncrementalStManAccessor // object (e.g. to set the cache size). //
        // The bucket size has to be given in bytes and the cache size in buckets. // Bucket size 0 means that the storage manager will set the bucket // size such that it can contain about 100 rows // (with a minimum size of 32768 bytes). However, if that results // in a very large bucket size (>327680) it'll make it smaller. // Note it uses 32 bytes for the size of variable length strings, // so this heuristic may fail when a column contains large strings. // When checkBucketSize is set and Bucket size > 0 // the storage manager throws an exception // when the size is too small to hold the values of at least 2 rows. // For this check it uses 0 for the length of variable length strings. // explicit IncrementalStMan (uInt bucketSize = 0, Bool checkBucketSize = True, uInt cacheSize = 1); explicit IncrementalStMan (const String& dataManagerName, uInt bucketSize = 0, Bool checkBucketSize = True, uInt cacheSize = 1); // ~IncrementalStMan(); // Copy constructor cannot be used. IncrementalStMan (const IncrementalStMan&) = delete; // Assignment cannot be used. IncrementalStMan& operator= (const IncrementalStMan&) = delete; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/MSMBase.cc000066400000000000000000000166061476623553700176520ustar00rootroot00000000000000//# MSMBase.cc: Base class for storage manager for tables using memory //# Copyright (C) 2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MSMBase::MSMBase() : DataManager (), nrrow_p (0), nrrowCreate_p (0), colSet_p (0), hasPut_p (False) {} MSMBase::MSMBase (const String& storageManagerName) : DataManager (), stmanName_p (storageManagerName), nrrow_p (0), nrrowCreate_p (0), colSet_p (0), hasPut_p (False) {} MSMBase::MSMBase (const String& storageManagerName, const Record&) : DataManager (), stmanName_p (storageManagerName), nrrow_p (0), nrrowCreate_p (0), colSet_p (0), hasPut_p (False) {} MSMBase::~MSMBase() { for (uInt i=0; i= colSet_p.nelements()) { colSet_p.resize (colSet_p.nelements() + 32); } MSMColumn* colp = new MSMColumn (this, dataType, False); colSet_p[ncolumn()] = colp; return colp; } DataManagerColumn* MSMBase::makeDirArrColumn (const String& columnName, int dataType, const String&) { //# Check if data type is not TpOther. throwDataTypeOther (columnName, dataType); //# Extend colSet_p block if needed. if (ncolumn() >= colSet_p.nelements()) { colSet_p.resize (colSet_p.nelements() + 32); } MSMColumn* colp = new MSMDirColumn (this, dataType); colSet_p[ncolumn()] = colp; return colp; } DataManagerColumn* MSMBase::makeIndArrColumn (const String& columnName, int dataType, const String&) { //# Check if data type is not TpOther. throwDataTypeOther (columnName, dataType); //# Extend colSet_p block if needed. if (ncolumn() >= colSet_p.nelements()) { colSet_p.resize (colSet_p.nelements() + 32); } MSMColumn* colp = new MSMIndColumn (this, dataType); colSet_p[ncolumn()] = colp; return colp; } Bool MSMBase::canReallocateColumns() const { return True; } DataManagerColumn* MSMBase::reallocateColumn (DataManagerColumn* column) { // Replace an indirect column by a direct one if its shape is fixed. for (uInt i=0; iisFixedShape()) { MSMIndColumn* col = dynamic_cast(ptr); if (col != 0) { // Turn a fixed shaped indirect array into a direct array. MSMDirColumn* newcol = new MSMDirColumn (this, col->dataType()); newcol->setShapeColumn (col->columnShape()); delete col; colSet_p[i] = newcol; column = newcol; } } } } return column; } void MSMBase::prepare() { // Create the rows if needed. if (nrrowCreate_p > 0) { AlwaysAssert (nrrow_p == 0, AipsError); addRow64 (nrrowCreate_p); nrrowCreate_p = 0; } } // Note that the column has already been added by makeXXColumn. // This function is merely for initializing the added column. void MSMBase::addColumn (DataManagerColumn* colp) { for (uInt i=0; idoCreate (nrrow_p); setHasPut(); return; } } throw DataManInternalError ("MSMBase::addColumn, column " + colp->columnName()); } void MSMBase::removeColumn (DataManagerColumn* colp) { for (uInt i=0; icolumnName() + " does not exist"); } void MSMBase::addRow64 (rownr_t nr) { //# Add the number of rows to each column. for (uInt i=0; iaddRow (nrrow_p+nr, nrrow_p); } nrrow_p += nr; setHasPut(); } void MSMBase::removeRow64 (rownr_t rownr) { for (uInt i=0; iremove (rownr); } nrrow_p--; setHasPut(); } Bool MSMBase::flush (AipsIO&, Bool) { return False; } void MSMBase::create64 (rownr_t nrrow) { //# Do not add the required nr of rows yet. // It is done later in reallocateColumn to avoid that all row data // have to be deleted and allocated again if an IndArrColumn is turned // into a DirArrColumn. nrrowCreate_p = nrrow; } rownr_t MSMBase::open64 (rownr_t tabNrrow, AipsIO&) { nrrow_p = tabNrrow; //# Create the required nr of rows and initialize them. for (uInt i=0; idoCreate (tabNrrow); } return nrrow_p; } rownr_t MSMBase::resync64 (rownr_t nrrow) { // Add or remove rows if it has changed. // Note that removing decreases the row number, so the same row number // is always used. if (nrrow > nrrow_p) { addRow64 (nrrow-nrrow_p); } else { rownr_t nr=nrrow_p-nrrow; for (rownr_t i=0; i #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MSMColumn; // // Base class for memory-based table storage manager class // // // // // //# Classes you should understand before using this one. //
      • DataManager // // // MSMBase is the base class for MemoryStMan. // // // See class MemoryStMan for // the description. // class MSMBase : public DataManager { public: // Create a memory storage manager. // Its name will be blank. MSMBase(); // Create a memory storage manager with the given name. // Its name can be used later in e.g. Table::addColumn to // add a column to this storage manager. //
        Note that the 2nd constructor is needed for table creation // from a record specification. // MSMBase (const String& storageManagerName); MSMBase (const String& storageManagerName, const Record&); // virtual ~MSMBase(); // Clone this object. // It does not clone MSMColumn objects possibly used. virtual DataManager* clone() const; // Get the type name of the data manager (i.e. MemoryStMan). virtual String dataManagerType() const; // Get the name given to this storage manager. virtual String dataManagerName() const; // Set the hasPut_p flag. In this way the StManAipsIOColumn objects // can indicate that data have been put. void setHasPut() { hasPut_p = True; } // Get the nr of rows in this storage manager. rownr_t nrow() const { return nrrow_p; } // Does the storage manager allow to add rows? (yes) virtual Bool canAddRow() const; // Does the storage manager allow to delete rows? (yes) virtual Bool canRemoveRow() const; // Does the storage manager allow to add columns? (yes) virtual Bool canAddColumn() const; // Does the storage manager allow to delete columns? (yes) virtual Bool canRemoveColumn() const; // Make the object from the string. // This function gets registered in the DataManager "constructor" map. static DataManager* makeObject (const String& dataManagerType, const Record& spec); private: // Flush and optionally fsync the data. // It does not done anything and always returns a False status. virtual Bool flush (AipsIO&, Bool fsync); // Let the storage manager create the nr of rows needed. virtual void create64 (rownr_t nrrow); // Open the storage manager file for an existing table. // It fills the rows with 0 values. virtual rownr_t open64 (rownr_t nrrow, AipsIO&); // Let the data manager initialize itself further. // It creates nr of rows (given to create) if needed. // Note this is done after reallocateColumn. virtual void prepare(); // Resync the storage manager with the new file contents. // It adds or removes rows as needed. // It cannot know which rows are deleted, so it always deletes // the last rows. virtual rownr_t resync64 (rownr_t nrrow); // The data manager will be deleted (because all its columns are // requested to be deleted). // It does not have to do anything. virtual void deleteManager(); // Add rows to all columns. virtual void addRow64 (rownr_t nrrow); // Delete a row from all columns. virtual void removeRow64 (rownr_t rownr); // Create a column in the storage manager on behalf of a table column. // // Create a scalar column. virtual DataManagerColumn* makeScalarColumn (const String& name, int dataType, const String& dataTypeID); // Create a direct array column. virtual DataManagerColumn* makeDirArrColumn (const String& name, int dataType, const String& dataTypeID); // Create an indirect array column. virtual DataManagerColumn* makeIndArrColumn (const String& name, int dataType, const String& dataTypeID); // // The MemoryStMan wants to do reallocateColumn. virtual Bool canReallocateColumns() const; // Reallocate the column object if it is part of this data manager. // It returns a pointer to the new column object. // It is used to replace an MSMIndColumn object for indirect array with // a fixed shape by an MSMDirColumn object. virtual DataManagerColumn* reallocateColumn (DataManagerColumn* column); // Add a column. virtual void addColumn (DataManagerColumn*); // Delete a column. virtual void removeColumn (DataManagerColumn*); protected: // Name given by user to this storage manager. String stmanName_p; // The number of rows in the columns. rownr_t nrrow_p; // The number of rows in create(). rownr_t nrrowCreate_p; // The assembly of all columns. PtrBlock colSet_p; // Has anything been put since the last flush? Bool hasPut_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/MSMColumn.cc000066400000000000000000000450201476623553700202250ustar00rootroot00000000000000//# MSMColumn.cc: A column in the MemoryStMan //# Copyright (C) 2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include // for memcpy namespace casacore { //# NAMESPACE CASACORE - BEGIN #define EXTBLSZ 32 MSMColumn::MSMColumn (MSMBase* smptr, int dataType, Bool byPtr) : StManColumnBase(dataType), stmanPtr_p (smptr), byPtr_p (byPtr), nralloc_p (0), nrext_p (0), data_p (EXTBLSZ,static_cast(0)), ncum_p (EXTBLSZ,(rownr_t)0) {} MSMColumn::~MSMColumn() { deleteAll(); } void MSMColumn::doCreate (rownr_t nrrow) { addRow (nrrow, 0); initData (data_p[1], nrrow); } void MSMColumn::addRow (rownr_t nrnew, rownr_t) { //# Extend the column sizes if needed. if (nrnew > nralloc_p) { rownr_t n = nralloc_p + 4096; if (n < nrnew) { n = nrnew; } resize (n); } } void MSMColumn::resize (rownr_t nr) { //# Extend internal blocks if needed. if (nrext_p+1 >= data_p.nelements()) { //#cout << "resize internal blocks " << nrext_p << endl; data_p.resize(nrext_p + 1+EXTBLSZ); ncum_p.resize(nrext_p + 1+EXTBLSZ); } //# Allocate another block of the correct data type. data_p[nrext_p+1] = allocData (nr-nralloc_p, byPtr_p); //#cout << "allocated new block " << nr-nralloc_p << endl; nrext_p++; ncum_p[nrext_p] = nr; nralloc_p = nr; return; } uInt MSMColumn::findExt (rownr_t index, Bool setCache) { //# Use a binary search to get the block containing the index. Int st = 0; Int ent= nrext_p; Int i = 0; while (st<=ent) { i = (st+ent)/2; if (index < ncum_p[i]) { ent = i-1; }else{ if (index > ncum_p[i]) { i++; st = i; }else{ ent = -1; // found i++; } } } if (i > Int(nrext_p)) { throw (indexError(index, "MSMColumn::findExt - " "rownr " + String::toString(index) + " in column " + columnName() + " out of range")); } if (setCache) { columnCache().set (ncum_p[i-1], ncum_p[i]-1, data_p[i]); } return i; } void MSMColumn::getScalarColumnV (ArrayBase& vec) { rownr_t nrow = stmanPtr_p->nrow(); // Get a pointer to the destination data. // It assures the data are contiguous. Bool deleteIt; void* ptr = vec.getVStorage (deleteIt); // About all data types can be simply copied. // Only String has to be handled specifically. if (dtype() == TpString) { String* to = static_cast(ptr); for (uInt i=1; i<=nrext_p; ++i) { const String* from = static_cast(data_p[i]); rownr_t nr = min(nrow, ncum_p[i]) - ncum_p[i-1]; for (rownr_t j=0; j(ptr); for (uInt i=1; i<=nrext_p; ++i) { const char* from = static_cast(data_p[i]); rownr_t nr = min(nrow, ncum_p[i]) - ncum_p[i-1]; memcpy (to, from, nr * elemSize()); to += nr * elemSize(); } } // This frees the storage if needed. vec.putVStorage (ptr, deleteIt); } void MSMColumn::putScalarColumnV (const ArrayBase& vec) { rownr_t nrow = stmanPtr_p->nrow(); // Get a pointer to the destination data. // It assures the data are contiguous. Bool deleteIt; const void* ptr = vec.getVStorage (deleteIt); // About all data types can be simply copied. // Only String has to be handled specifically. if (dtype() == TpString) { const String* from = static_cast(ptr); for (uInt i=1; i<=nrext_p; ++i) { String* to = static_cast(data_p[i]); rownr_t nr = min(nrow, ncum_p[i]) - ncum_p[i-1]; for (rownr_t j=0; j(ptr); for (uInt i=1; i<=nrext_p; ++i) { char* to = static_cast(data_p[i]); rownr_t nr = min(nrow, ncum_p[i]) - ncum_p[i-1]; memcpy (to, from, nr * elemSize()); from += nr * elemSize(); } } // This frees the storage if needed. vec.freeVStorage (ptr, deleteIt); } void MSMColumn::getBool (rownr_t rownr, Bool* value) { // Note that the ColumnCache references the appropriate data array in data_p. const ColumnCache& cache = columnCache(); if (rownr < cache.start() || rownr > cache.end()) { findExt (rownr, True); } rownr_t inx = rownr - cache.start(); *value = static_cast(cache.dataPtr())[inx]; } void MSMColumn::putBool (rownr_t rownr, const Bool* value) { // Note that the ColumnCache references the appropriate data array in data_p. ColumnCache& cache = columnCache(); if (rownr < cache.start() || rownr > cache.end()) { findExt (rownr, True); } rownr_t inx = rownr - cache.start(); const_cast(static_cast(cache.dataPtr()))[inx] = *value; stmanPtr_p->setHasPut(); } void MSMColumn::getuChar (rownr_t rownr, uChar* value) { // Note that the ColumnCache references the appropriate data array in data_p. const ColumnCache& cache = columnCache(); if (rownr < cache.start() || rownr > cache.end()) { findExt (rownr, True); } rownr_t inx = rownr - cache.start(); *value = static_cast(cache.dataPtr())[inx]; } void MSMColumn::putuChar (rownr_t rownr, const uChar* value) { // Note that the ColumnCache references the appropriate data array in data_p. ColumnCache& cache = columnCache(); if (rownr < cache.start() || rownr > cache.end()) { findExt (rownr, True); } rownr_t inx = rownr - cache.start(); const_cast(static_cast(cache.dataPtr()))[inx] = *value; stmanPtr_p->setHasPut(); } void MSMColumn::getShort (rownr_t rownr, Short* value) { // Note that the ColumnCache references the appropriate data array in data_p. const ColumnCache& cache = columnCache(); if (rownr < cache.start() || rownr > cache.end()) { findExt (rownr, True); } rownr_t inx = rownr - cache.start(); *value = static_cast(cache.dataPtr())[inx]; } void MSMColumn::putShort (rownr_t rownr, const Short* value) { // Note that the ColumnCache references the appropriate data array in data_p. ColumnCache& cache = columnCache(); if (rownr < cache.start() || rownr > cache.end()) { findExt (rownr, True); } rownr_t inx = rownr - cache.start(); const_cast(static_cast(cache.dataPtr()))[inx] = *value; stmanPtr_p->setHasPut(); } void MSMColumn::getuShort (rownr_t rownr, uShort* value) { // Note that the ColumnCache references the appropriate data array in data_p. const ColumnCache& cache = columnCache(); if (rownr < cache.start() || rownr > cache.end()) { findExt (rownr, True); } rownr_t inx = rownr - cache.start(); *value = static_cast(cache.dataPtr())[inx]; } void MSMColumn::putuShort (rownr_t rownr, const uShort* value) { // Note that the ColumnCache references the appropriate data array in data_p. ColumnCache& cache = columnCache(); if (rownr < cache.start() || rownr > cache.end()) { findExt (rownr, True); } rownr_t inx = rownr - cache.start(); const_cast(static_cast(cache.dataPtr()))[inx] = *value; stmanPtr_p->setHasPut(); } void MSMColumn::getInt (rownr_t rownr, Int* value) { // Note that the ColumnCache references the appropriate data array in data_p. const ColumnCache& cache = columnCache(); if (rownr < cache.start() || rownr > cache.end()) { findExt (rownr, True); } rownr_t inx = rownr - cache.start(); *value = static_cast(cache.dataPtr())[inx]; } void MSMColumn::putInt (rownr_t rownr, const Int* value) { // Note that the ColumnCache references the appropriate data array in data_p. ColumnCache& cache = columnCache(); if (rownr < cache.start() || rownr > cache.end()) { findExt (rownr, True); } rownr_t inx = rownr - cache.start(); const_cast(static_cast(cache.dataPtr()))[inx] = *value; stmanPtr_p->setHasPut(); } void MSMColumn::getuInt (rownr_t rownr, uInt* value) { // Note that the ColumnCache references the appropriate data array in data_p. const ColumnCache& cache = columnCache(); if (rownr < cache.start() || rownr > cache.end()) { findExt (rownr, True); } rownr_t inx = rownr - cache.start(); *value = static_cast(cache.dataPtr())[inx]; } void MSMColumn::putuInt (rownr_t rownr, const uInt* value) { // Note that the ColumnCache references the appropriate data array in data_p. ColumnCache& cache = columnCache(); if (rownr < cache.start() || rownr > cache.end()) { findExt (rownr, True); } rownr_t inx = rownr - cache.start(); const_cast(static_cast(cache.dataPtr()))[inx] = *value; stmanPtr_p->setHasPut(); } void MSMColumn::getInt64 (rownr_t rownr, Int64* value) { // Note that the ColumnCache references the appropriate data array in data_p. const ColumnCache& cache = columnCache(); if (rownr < cache.start() || rownr > cache.end()) { findExt (rownr, True); } rownr_t inx = rownr - cache.start(); *value = static_cast(cache.dataPtr())[inx]; } void MSMColumn::putInt64 (rownr_t rownr, const Int64* value) { // Note that the ColumnCache references the appropriate data array in data_p. ColumnCache& cache = columnCache(); if (rownr < cache.start() || rownr > cache.end()) { findExt (rownr, True); } rownr_t inx = rownr - cache.start(); const_cast(static_cast(cache.dataPtr()))[inx] = *value; stmanPtr_p->setHasPut(); } void MSMColumn::getfloat (rownr_t rownr, float* value) { // Note that the ColumnCache references the appropriate data array in data_p. const ColumnCache& cache = columnCache(); if (rownr < cache.start() || rownr > cache.end()) { findExt (rownr, True); } rownr_t inx = rownr - cache.start(); *value = static_cast(cache.dataPtr())[inx]; } void MSMColumn::putfloat (rownr_t rownr, const float* value) { // Note that the ColumnCache references the appropriate data array in data_p. ColumnCache& cache = columnCache(); if (rownr < cache.start() || rownr > cache.end()) { findExt (rownr, True); } rownr_t inx = rownr - cache.start(); const_cast(static_cast(cache.dataPtr()))[inx] = *value; stmanPtr_p->setHasPut(); } void MSMColumn::getdouble (rownr_t rownr, double* value) { // Note that the ColumnCache references the appropriate data array in data_p. const ColumnCache& cache = columnCache(); if (rownr < cache.start() || rownr > cache.end()) { findExt (rownr, True); } rownr_t inx = rownr - cache.start(); *value = static_cast(cache.dataPtr())[inx]; } void MSMColumn::putdouble (rownr_t rownr, const double* value) { // Note that the ColumnCache references the appropriate data array in data_p. ColumnCache& cache = columnCache(); if (rownr < cache.start() || rownr > cache.end()) { findExt (rownr, True); } rownr_t inx = rownr - cache.start(); const_cast(static_cast(cache.dataPtr()))[inx] = *value; stmanPtr_p->setHasPut(); } void MSMColumn::getComplex (rownr_t rownr, Complex* value) { // Note that the ColumnCache references the appropriate data array in data_p. const ColumnCache& cache = columnCache(); if (rownr < cache.start() || rownr > cache.end()) { findExt (rownr, True); } rownr_t inx = rownr - cache.start(); *value = static_cast(cache.dataPtr())[inx]; } void MSMColumn::putComplex (rownr_t rownr, const Complex* value) { // Note that the ColumnCache references the appropriate data array in data_p. ColumnCache& cache = columnCache(); if (rownr < cache.start() || rownr > cache.end()) { findExt (rownr, True); } rownr_t inx = rownr - cache.start(); const_cast(static_cast(cache.dataPtr()))[inx] = *value; stmanPtr_p->setHasPut(); } void MSMColumn::getDComplex (rownr_t rownr, DComplex* value) { // Note that the ColumnCache references the appropriate data array in data_p. const ColumnCache& cache = columnCache(); if (rownr < cache.start() || rownr > cache.end()) { findExt (rownr, True); } rownr_t inx = rownr - cache.start(); *value = static_cast(cache.dataPtr())[inx]; } void MSMColumn::putDComplex (rownr_t rownr, const DComplex* value) { // Note that the ColumnCache references the appropriate data array in data_p. ColumnCache& cache = columnCache(); if (rownr < cache.start() || rownr > cache.end()) { findExt (rownr, True); } rownr_t inx = rownr - cache.start(); const_cast(static_cast(cache.dataPtr()))[inx] = *value; stmanPtr_p->setHasPut(); } void MSMColumn::getString (rownr_t rownr, String* value) { // Note that the ColumnCache references the appropriate data array in data_p. const ColumnCache& cache = columnCache(); if (rownr < cache.start() || rownr > cache.end()) { findExt (rownr, True); } rownr_t inx = rownr - cache.start(); *value = static_cast(cache.dataPtr())[inx]; } void MSMColumn::putString (rownr_t rownr, const String* value) { // Note that the ColumnCache references the appropriate data array in data_p. ColumnCache& cache = columnCache(); if (rownr < cache.start() || rownr > cache.end()) { findExt (rownr, True); } rownr_t inx = rownr - cache.start(); const_cast(static_cast(cache.dataPtr()))[inx] = *value; stmanPtr_p->setHasPut(); } void MSMColumn::remove (rownr_t index) { //# Find the extension. uInt extnr = findExt(index, False); rownr_t nrval = ncum_p[extnr] - ncum_p[extnr-1]; void* datap = data_p[extnr]; //# If the extension contains only this element, remove the extension. if (nrval == 1) { deleteData (datap, byPtr_p); for (uInt i=extnr; i= nr of extensions. //# Their first elements must be zero. if (data_p.nelements() == 0 || data_p.nelements() < nrext_p) return False; if (data_p.nelements() != ncum_p.nelements()) return False; if (data_p[0] != 0 || ncum_p[0] != 0) return False; //# If no points, there should be no extensions (and vice versa). if ((nralloc_p == 0) != (nrext_p == 0)) return False; //# If no extensions, first length must also be zero. if (nrext_p == 0 && ncum_p[1] != 0) return False; //# All extension pointers must be filled in. //# The ncum_p array must be increasing. for (uInt i=1; i<=nrext_p; i++) { if (data_p[i] == 0 || ncum_p[i] <= ncum_p[i-1]) return False; } return True; } void MSMColumn::deleteAll() { for (uInt i=1; i<=nrext_p; i++) { deleteData (data_p[i], byPtr_p); } nralloc_p = 0; nrext_p = 0; ncum_p[1] = 0; } void MSMColumn::deleteData (void* datap, Bool byPtr) { if (byPtr) { delete [] static_cast(datap); } else if (dtype() == TpString) { delete [] static_cast(datap); } else { delete [] static_cast(datap); } datap = 0; } void* MSMColumn::allocData (rownr_t nrval, Bool byPtr) { void* datap = 0; if (byPtr) { datap = new void*[nrval]; memset (datap, 0, nrval*sizeof(void*)); } else if (dtype() == TpString) { datap = new String[nrval]; } else { datap = new char[nrval * elemSize()]; } return datap; } void MSMColumn::removeData (void* dp, rownr_t inx, rownr_t nrvalAfter) { if (inx >= nrvalAfter) { return; } if (byPtr_p) { objmove (static_cast(dp) + inx, static_cast(dp) + inx+1, nrvalAfter-inx); } else if (dtype() == TpString) { objmove (static_cast(dp) + inx, static_cast(dp) + inx+1, nrvalAfter-inx); } else { memmove (static_cast(dp) + inx*elemSize(), static_cast(dp) + (inx+1)*elemSize(), (nrvalAfter-inx)*elemSize()); } } void MSMColumn::initData (void* datap, rownr_t nrval) { // Pointers are already initialized by allocData. if (!byPtr_p) { if (dtype() == TpBool) { objset (static_cast(datap), True, nrval); } else if (dtype() != TpString) { memset (datap, 0, nrval*elemSize()); } } } void* MSMColumn::getArrayPtr (rownr_t rownr) { uInt extnr = findExt(rownr, False); return (static_cast(data_p[extnr])) [rownr-ncum_p[extnr-1]]; } void MSMColumn::putArrayPtr (rownr_t rownr, void* ptr) { uInt extnr = findExt(rownr, False); (static_cast(data_p[extnr])) [rownr-ncum_p[extnr-1]] = ptr; stmanPtr_p->setHasPut(); } void MSMColumn::putFile (rownr_t, AipsIO&) {} void MSMColumn::getFile (rownr_t, AipsIO&) {} void MSMColumn::reopenRW() {} } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/MSMColumn.h000066400000000000000000000214561476623553700200760ustar00rootroot00000000000000//# MSMColumn.h: A column in the MemoryStMan //# Copyright (C) 2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_MSMCOLUMN_H #define TABLES_MSMCOLUMN_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations class MSMBase; // // Column in the Memory table storage manager class // // // // // //# Classes you should understand before using this one. //
      • StManColumn //
      • MemoryStMan // // // MSMColumn handles a column for the memory-based storage manager. // // // MSMColumn is used by MemoryStMan to handle the access to // the data in a table column. // It is an storage manager based in memory. Thus the data is lost // when the table is closed. // On reopen it will be initialized to the default column value. // It fully supports addition and removal of rows. // // MSMColumn serves 2 purposes: //
          //
        1. It handles a column containing scalar values. //
        2. It serves as a base class for MSMDirColumn and MSMIndColumn // These classes handle arrays and use MSMColumn to hold a pointer // to the array in each row. //
        // // MSMColumn does not hold a column as a consecutive array, // because extending the column (i.e. adding rows) proved be too // expensive due to the repeated copying involved when creating a table // (this method was used by the first version of the table system). // Instead it has a number of data blocks (extensions) indexed to by a // super block. Accessing a row means finding the appropriate extension // via a binary search. Because there is only 1 extension when a table is // read back, the overhead in finding a row is small. //
        // // MSMColumn handles the standard data types. The class // is not templated, but a switch statement is used instead. // Templates would cause too many instantiations. // // //# A List of bugs, limitations, extensions or planned refinements. //
      • StManAipsIO should use this class // class MSMColumn: public StManColumnBase { public: // Create a column of the given type. // It will maintain a pointer to its parent storage manager. MSMColumn (MSMBase* smptr, int dataType, Bool byPtr); // Frees up the storage. virtual ~MSMColumn(); // Forbid copy constructor. MSMColumn (const MSMColumn&) = delete; // Forbid assignment. MSMColumn& operator= (const MSMColumn&) = delete; // Get a scalar value in the given row. // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the Scalar/ArrayColumn get function). // virtual void getBool (rownr_t rownr, Bool* dataPtr); virtual void getuChar (rownr_t rownr, uChar* dataPtr); virtual void getShort (rownr_t rownr, Short* dataPtr); virtual void getuShort (rownr_t rownr, uShort* dataPtr); virtual void getInt (rownr_t rownr, Int* dataPtr); virtual void getuInt (rownr_t rownr, uInt* dataPtr); virtual void getInt64 (rownr_t rownr, Int64* dataPtr); virtual void getfloat (rownr_t rownr, float* dataPtr); virtual void getdouble (rownr_t rownr, double* dataPtr); virtual void getComplex (rownr_t rownr, Complex* dataPtr); virtual void getDComplex (rownr_t rownr, DComplex* dataPtr); virtual void getString (rownr_t rownr, String* dataPtr); // // Put a scalar value into the given row. // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the Scalar/ArrayColumn put function). // virtual void putBool (rownr_t rownr, const Bool* dataPtr); virtual void putuChar (rownr_t rownr, const uChar* dataPtr); virtual void putShort (rownr_t rownr, const Short* dataPtr); virtual void putuShort (rownr_t rownr, const uShort* dataPtr); virtual void putInt (rownr_t rownr, const Int* dataPtr); virtual void putuInt (rownr_t rownr, const uInt* dataPtr); virtual void putInt64 (rownr_t rownr, const Int64* dataPtr); virtual void putfloat (rownr_t rownr, const float* dataPtr); virtual void putdouble (rownr_t rownr, const double* dataPtr); virtual void putComplex (rownr_t rownr, const Complex* dataPtr); virtual void putDComplex (rownr_t rownr, const DComplex* dataPtr); virtual void putString (rownr_t rownr, const String* dataPtr); // // Get all scalar values in the column. // The vector given in data has to have the correct length // (which is guaranteed by the ScalarColumn getColumn function). virtual void getScalarColumnV (ArrayBase& data); // Put all scalar values in the column. // The vector given in data has to have the correct length // (which is guaranteed by the ScalarColumn putColumn function). virtual void putScalarColumnV (const ArrayBase& data); // Add (newNrrow-oldNrrow) rows to the column. virtual void addRow (rownr_t newNrrow, rownr_t oldNrrow); // Resize the data blocks. // This adds an extension when needed. void resize (rownr_t nrval); // Remove the given row. // If no rows remain in the extension, the extension is also removed. virtual void remove (rownr_t rownr); // Create the number of rows in a new table. // This is used when a table gets created or opened. virtual void doCreate (rownr_t nrrow); // Make it possible to write the column data. // It is only used by derived classes. virtual void putFile (rownr_t nrval, AipsIO&); // Make it possible to read the column data. // It is only used by derived classes. virtual void getFile (rownr_t nrval, AipsIO&); // Reopen the storage manager files for read/write. virtual void reopenRW(); // Check if the class invariants still hold. virtual Bool ok() const; protected: MSMBase* stmanPtr_p; // The data is indirectly accessed via a pointer (for the derived classes). Bool byPtr_p; // The number of allocated rows in the column. rownr_t nralloc_p; // The nr of extensions in use. uInt nrext_p; // The assembly of all extensions (actually Block). Block data_p; // The cumulative nr of rows in all extensions. Block ncum_p; // Find the extension in which the row number is. // If the flag is true, it also sets the columnCache object. uInt findExt (rownr_t rownr, Bool setCache); // Allocate an extension with the data type of the column. void* allocData (rownr_t nrval, Bool byPtr); // Delete all extensions. // Possible underlying data (as used by StManArrayColumnMemory) // will not be deleted and should have been deleted beforehand. void deleteAll(); // Delete an extension. void deleteData (void* datap, Bool byPtr); // Remove an entry (i.e. a row) from an extension at the given index. // It will do this by shifting the rest (nrvalAfter elements) // one position to the left. void removeData (void* datap, rownr_t inx, rownr_t nrvalAfter); // Initialize the data (after an open). virtual void initData (void* datap, rownr_t nrval); // Get the pointer for the given row. // This is for the derived classes like StManArrayColumnMemory. void* getArrayPtr (rownr_t rownr); // Put the pointer for the given row. // This is for the derived classes like StManArrayColumnMemory. void putArrayPtr (rownr_t rownr, void* dataPtr); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/MSMDirColumn.cc000066400000000000000000000152031476623553700206640ustar00rootroot00000000000000//# MSMDirColumn.cc: Memory storage manager for fixed shape table arrays //# Copyright (C) 2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include // for memcpy namespace casacore { //# NAMESPACE CASACORE - BEGIN MSMDirColumn::MSMDirColumn (MSMBase* smptr, int dataType) : MSMColumn (smptr, dataType, True), nrelem_p (0) {} MSMDirColumn::~MSMDirColumn() { rownr_t nr = stmanPtr_p->nrow(); for (rownr_t i=0; i(data), static_cast(getArrayPtr (rownr)), nrelem_p); } else { memcpy (static_cast(data), static_cast(getArrayPtr (rownr)), elemSize() * nrelem_p); } arr.putVStorage (data, deleteIt); } void MSMDirColumn::putArrayV (rownr_t rownr, const ArrayBase& arr) { DebugAssert (shape_p.isEqual (arr.shape()), AipsError); Bool deleteIt; const void* data = arr.getVStorage (deleteIt); if (dtype() == TpString) { objcopy (static_cast(getArrayPtr (rownr)), static_cast(data), nrelem_p); } else { memcpy (static_cast(getArrayPtr (rownr)), static_cast(data), elemSize() * nrelem_p); } arr.freeVStorage (data, deleteIt); stmanPtr_p->setHasPut(); } void MSMDirColumn::getSliceV (rownr_t rownr, const Slicer& slicer, ArrayBase& arr) { switch (dtype()) { case TpBool: doGetSlice (rownr, slicer, static_cast&>(arr)); break; case TpUChar: doGetSlice (rownr, slicer, static_cast&>(arr)); break; case TpShort: doGetSlice (rownr, slicer, static_cast&>(arr)); break; case TpUShort: doGetSlice (rownr, slicer, static_cast&>(arr)); break; case TpInt: doGetSlice (rownr, slicer, static_cast&>(arr)); break; case TpUInt: doGetSlice (rownr, slicer, static_cast&>(arr)); break; case TpInt64: doGetSlice (rownr, slicer, static_cast&>(arr)); break; case TpFloat: doGetSlice (rownr, slicer, static_cast&>(arr)); break; case TpDouble: doGetSlice (rownr, slicer, static_cast&>(arr)); break; case TpComplex: doGetSlice (rownr, slicer, static_cast&>(arr)); break; case TpDComplex: doGetSlice (rownr, slicer, static_cast&>(arr)); break; case TpString: doGetSlice (rownr, slicer, static_cast&>(arr)); break; default: throw (DataManInvDT ("MSMDirColumn::getSlice")); } } void MSMDirColumn::putSliceV (rownr_t rownr, const Slicer& slicer, const ArrayBase& arr) { switch (dtype()) { case TpBool: doPutSlice (rownr, slicer, static_cast&>(arr)); break; case TpUChar: doPutSlice (rownr, slicer, static_cast&>(arr)); break; case TpShort: doPutSlice (rownr, slicer, static_cast&>(arr)); break; case TpUShort: doPutSlice (rownr, slicer, static_cast&>(arr)); break; case TpInt: doPutSlice (rownr, slicer, static_cast&>(arr)); break; case TpUInt: doPutSlice (rownr, slicer, static_cast&>(arr)); break; case TpInt64: doPutSlice (rownr, slicer, static_cast&>(arr)); break; case TpFloat: doPutSlice (rownr, slicer, static_cast&>(arr)); break; case TpDouble: doPutSlice (rownr, slicer, static_cast&>(arr)); break; case TpComplex: doPutSlice (rownr, slicer, static_cast&>(arr)); break; case TpDComplex: doPutSlice (rownr, slicer, static_cast&>(arr)); break; case TpString: doPutSlice (rownr, slicer, static_cast&>(arr)); break; default: throw (DataManInvDT ("MSMDirColumn::getSlice")); } stmanPtr_p->setHasPut(); } void MSMDirColumn::remove (rownr_t rownr) { deleteArray (rownr); MSMColumn::remove (rownr); } void MSMDirColumn::deleteArray (rownr_t rownr) { void* datap = getArrayPtr (rownr); deleteData (datap, False); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/MSMDirColumn.h000066400000000000000000000113061476623553700205260ustar00rootroot00000000000000//# MSMDirColumn.h: Memory storage manager for fixed shape table arrays //# Copyright (C) 2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_MSMDIRCOLUMN_H #define TABLES_MSMDIRCOLUMN_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Memory storage manager for table arrays // // // // // //# Classes you should understand before using this one. //
      • MSMBase //
      • MSMColumn // // // MSMDirColumn handles arrays in a table column. // It only keeps them in memory, so they are not persistent. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class MSMDirColumn: public MSMColumn { public: // Create a column of the given type. MSMDirColumn (MSMBase* smptr, int dataType); // Frees up the storage. virtual ~MSMDirColumn(); // Forbid copy constructor. MSMDirColumn (const MSMDirColumn&) = delete; // Forbid assignment. MSMDirColumn& operator= (const MSMDirColumn&) = delete; // Set the (fixed) shape of the arrays in the entire column. virtual void setShapeColumn (const IPosition& shape); // Add (newNrrow-oldNrrow) rows to the column. // Allocate the data arrays in these rows if the shape is fixed. virtual void addRow (rownr_t newNrrow, rownr_t oldNrrow); // Get the dimensionality of the item in the given row. // 0 is returned if there is no array. virtual uInt ndim (rownr_t rownr); // Get the shape of the array in the given row. // An zero-length IPosition is returned if there is no array. virtual IPosition shape (rownr_t rownr); // Get an array value in the given row. // The buffer given by arr has to have the correct length // (which is guaranteed by the ArrayColumn get function). virtual void getArrayV (rownr_t rownr, ArrayBase& arr); // Put an array value into the given row. // The buffer given by arr has to have the correct length // (which is guaranteed by the ArrayColumn put function). virtual void putArrayV (rownr_t rownr, const ArrayBase& arr); // Get a section of the array in the given row. // The buffer given by arr has to have the correct length // (which is guaranteed by the ArrayColumn getSlice function). virtual void getSliceV (rownr_t rownr, const Slicer&, ArrayBase& arr); // Put into a section of the array in the given row. // The buffer given by arr has to have the correct length // (which is guaranteed by the ArrayColumn putSlice function). virtual void putSliceV (rownr_t rownr, const Slicer&, const ArrayBase& arr); // Remove the value in the given row. void remove (rownr_t rownr); // Let the column create its arrays. void doCreate (rownr_t nrrow); private: template inline void doGetSlice (rownr_t rownr, const Slicer& slicer, Array& data) { Array arr(shape_p, static_cast(getArrayPtr (rownr)), SHARE); data = arr(slicer); } template inline void doPutSlice (rownr_t rownr, const Slicer& slicer, const Array& data) { Array arr(shape_p, static_cast(getArrayPtr (rownr)), SHARE); arr(slicer) = data; } // Delete the array in the given row. void deleteArray (rownr_t rownr); // The shape of the array. IPosition shape_p; // The nr of elements in the array. rownr_t nrelem_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/MSMIndColumn.cc000066400000000000000000000226431476623553700206660ustar00rootroot00000000000000//# MSMIndColumn.cc: Memory storage manager for variable shaped table arrays //# Copyright (C) 2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include // for memcpy namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Define a macro which gets the pointer for the given row //# and casts it to the block. #define MSMINDCOLUMN_GETDATA(rownr) \ (static_cast(getArrayPtr(rownr))) MSMIndColumn::MSMIndColumn (MSMBase* smptr, int dataType) : MSMColumn (smptr, dataType, True) {} //# Delete all objects created. MSMIndColumn::~MSMIndColumn() { rownr_t nr = stmanPtr_p->nrow(); for (rownr_t i=0; ishape().isEqual (shape)) { return; } delete ptr; } // Create the array. ptr = new Data (shape, dataType(), elemSize()); putArrayPtr (rownr, ptr); } //# Get the shape for the array (if any) in the given row. //# Read shape if not read yet. MSMIndColumn::Data* MSMIndColumn::getShape (rownr_t rownr) { void* ptr = getArrayPtr(rownr); if (ptr == 0) { throw (DataManInvOper ("MSM: no array in row " + String::toString(rownr) + " in column " + columnName() + " of " + stmanPtr_p->fileName())); } return static_cast(ptr); } Bool MSMIndColumn::isShapeDefined (rownr_t rownr) { return (getArrayPtr(rownr) == 0 ? False : True); } uInt MSMIndColumn::ndim (rownr_t rownr) { return getShape(rownr)->shape().nelements(); } IPosition MSMIndColumn::shape (rownr_t rownr) { return getShape(rownr)->shape(); } Bool MSMIndColumn::canChangeShape() const { return True; } void MSMIndColumn::getArrayV (rownr_t rownr, ArrayBase& arr) { Data* data = getShape(rownr); //# also checks if row contains data DebugAssert (data->shape().isEqual (arr.shape()), AipsError); Bool deleteIt; void* arrData = arr.getVStorage (deleteIt); if (dtype() == TpString) { objcopy (static_cast(arrData), static_cast(data->data()), arr.size()); } else { memcpy (static_cast(arrData), static_cast(data->data()), elemSize() * arr.size()); } arr.putVStorage (arrData, deleteIt); } void MSMIndColumn::putArrayV (rownr_t rownr, const ArrayBase& arr) { Data* data = getShape(rownr); //# also checks if row contains data DebugAssert (shape(rownr).isEqual (arr.shape()), AipsError); Bool deleteIt; const void* arrData = arr.getVStorage (deleteIt); if (dtype() == TpString) { objcopy (static_cast(data->data()), static_cast(arrData), arr.size()); } else { memcpy (static_cast(data->data()), static_cast(arrData), elemSize() * arr.size()); } arr.freeVStorage (arrData, deleteIt); stmanPtr_p->setHasPut(); } void MSMIndColumn::getSliceV (rownr_t rownr, const Slicer& ns, ArrayBase& arr) { Data* data = getShape(rownr); //# also checks if row contains data const IPosition& shp = data->shape(); IPosition blc, trc, inc; ns.inferShapeFromSource (shp, blc, trc, inc); switch (dtype()) { case TpBool: arr.assignBase (Array(shp, static_cast(data->data()), SHARE) (blc, trc, inc), False); break; case TpUChar: arr.assignBase (Array(shp, static_cast(data->data()), SHARE) (blc, trc, inc), False); break; case TpShort: arr.assignBase (Array(shp, static_cast(data->data()), SHARE) (blc, trc, inc), False); break; case TpUShort: arr.assignBase (Array(shp, static_cast(data->data()), SHARE) (blc, trc, inc), False); break; case TpInt: arr.assignBase (Array(shp, static_cast(data->data()), SHARE) (blc, trc, inc), False); break; case TpUInt: arr.assignBase (Array(shp, static_cast(data->data()), SHARE) (blc, trc, inc), False); break; case TpInt64: arr.assignBase (Array(shp, static_cast(data->data()), SHARE) (blc, trc, inc), False); break; case TpFloat: arr.assignBase (Array(shp, static_cast(data->data()), SHARE) (blc, trc, inc), False); break; case TpDouble: arr.assignBase (Array(shp, static_cast(data->data()), SHARE) (blc, trc, inc), False); break; case TpComplex: arr.assignBase (Array(shp, static_cast(data->data()), SHARE) (blc, trc, inc), False); break; case TpDComplex: arr.assignBase (Array(shp, static_cast(data->data()), SHARE) (blc, trc, inc), False); break; case TpString: arr.assignBase (Array(shp, static_cast(data->data()), SHARE) (blc, trc, inc), False); break; default: throw DataManInvDT ("MSMIndColumn::getSliceV"); } } void MSMIndColumn::putSliceV (rownr_t rownr, const Slicer& ns, const ArrayBase& arr) { Data* data = MSMINDCOLUMN_GETDATA(rownr); const IPosition& shp = data->shape(); IPosition blc, trc, inc; ns.inferShapeFromSource (shp, blc, trc, inc); switch (dtype()) { case TpBool: Array(shp, static_cast(data->data()), SHARE) (blc, trc, inc).assignBase (arr, False); break; case TpUChar: Array(shp, static_cast(data->data()), SHARE) (blc, trc, inc).assignBase (arr, False); break; case TpShort: Array(shp, static_cast(data->data()), SHARE) (blc, trc, inc).assignBase (arr, False); break; case TpUShort: Array(shp, static_cast(data->data()), SHARE) (blc, trc, inc).assignBase (arr, False); break; case TpInt: Array(shp, static_cast(data->data()), SHARE) (blc, trc, inc).assignBase (arr, False); break; case TpUInt: Array(shp, static_cast(data->data()), SHARE) (blc, trc, inc).assignBase (arr, False); break; case TpInt64: Array(shp, static_cast(data->data()), SHARE) (blc, trc, inc).assignBase (arr, False); break; case TpFloat: Array(shp, static_cast(data->data()), SHARE) (blc, trc, inc).assignBase (arr, False); break; case TpDouble: Array(shp, static_cast(data->data()), SHARE) (blc, trc, inc).assignBase (arr, False); break; case TpComplex: Array(shp, static_cast(data->data()), SHARE) (blc, trc, inc).assignBase (arr, False); break; case TpDComplex: Array(shp, static_cast(data->data()), SHARE) (blc, trc, inc).assignBase (arr, False); break; case TpString: Array(shp, static_cast(data->data()), SHARE) (blc, trc, inc).assignBase (arr, False); break; default: throw DataManInvDT ("MSMIndColumn::putSliceV"); } stmanPtr_p->setHasPut(); } void MSMIndColumn::remove (rownr_t rownr) { deleteArray (rownr); MSMColumn::remove (rownr); } void MSMIndColumn::deleteArray (rownr_t rownr) { // Remove the array for this row (if there). delete MSMINDCOLUMN_GETDATA(rownr); } MSMIndColumn::Data::Data (const IPosition& shape, int dtype, int elemSize) : shape_p (shape), data_p (nullptr), data_is_string(dtype == TpString) { Int64 nelem = shape.product(); if (data_is_string) { data_p = new String[nelem]; } else { data_p = new char[nelem * elemSize]; } } MSMIndColumn::Data::~Data() { if (data_is_string) { delete [] static_cast(data_p); } else { delete [] static_cast(data_p); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/MSMIndColumn.h000066400000000000000000000132001476623553700205150ustar00rootroot00000000000000//# MSMIndColumn.h: Memory storage manager for variable shaped table arrays //# Copyright (C) 2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_MSMINDCOLUMN_H #define TABLES_MSMINDCOLUMN_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Mmeory storage manager for variable shaped table arrays // // // // // //# Classes you should understand before using this one. //
      • MSMBase //
      • MSMColumn // // // StManColumnArrayAipsIO handles indirect arrays in a table column. // // An array (or section of an array) is only read when needed. // It, however, caches the array shape using the helper class // StIndArray. Pointers to these objects // are maintained using the standard StManColumnAipsIO facilities. // When the column gets written, the offsets in the StManArrayFile file // get written. Those will be read back when the column is read in. // // When a row gets deleted or when the array gets bigger, the file space // is lost. This storage manager is a simple one and no attempts // are done to make it smart. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class MSMIndColumn : public MSMColumn { public: // Create a column of the given type. MSMIndColumn (MSMBase*, int dataType); // Frees up the storage. ~MSMIndColumn(); // Forbid copy constructor. MSMIndColumn (const MSMIndColumn&) = delete; // Forbid assignment. MSMIndColumn& operator= (const MSMIndColumn&) = delete; // Set the (fixed) shape of the arrays in the entire column. void setShapeColumn (const IPosition& shape); // Get the column shape. const IPosition& columnShape() const { return fixedShape_p; } // Set the shape of the array in the given row and allocate the array // in the file. void setShape (rownr_t rownr, const IPosition& shape); // Is the shape defined (i.e. is there an array) in this row? Bool isShapeDefined (rownr_t rownr); // Get the dimensionality of the item in the given row. // 0 is returned if there is no array. uInt ndim (rownr_t rownr); // Get the shape of the array in the given row. // An zero-length IPosition is returned if there is no array. IPosition shape (rownr_t rownr); // This storage manager can handle changing array shapes. Bool canChangeShape() const; // Get an array value in the given row. // The buffer given by arr has to have the correct length // (which is guaranteed by the ArrayColumn get function). void getArrayV (rownr_t rownr, ArrayBase& arr); // Put an array value into the given row. // The buffer given by arr has to have the correct length // (which is guaranteed by the ArrayColumn put function). void putArrayV (rownr_t rownr, const ArrayBase& arr); // Get a section of the array in the given row. // The buffer given by arr has to have the correct length // (which is guaranteed by the ArrayColumn getSlice function). void getSliceV (rownr_t rownr, const Slicer&, ArrayBase& arr); // Put into a section of the array in the given row. // The buffer given by arr has to have the correct length // (which is guaranteed by the ArrayColumn putSlice function). void putSliceV (rownr_t rownr, const Slicer&, const ArrayBase& arr); // Remove the value in the given row. // This will result in lost file space. void remove (rownr_t rownr); private: class Data { public: Data (const IPosition& shape, int dtype, int elemSize); ~Data(); Data (const Data&) = delete; Data& operator= (const Data&) = delete; const IPosition& shape() const {return shape_p;} void* data() {return data_p;} private: IPosition shape_p; void* data_p; bool data_is_string; }; // The shape of all arrays in case it is fixed. IPosition fixedShape_p; // The size at the start of the data (for the IPosition). uInt startSize_p; // Delete the array in the given row. void deleteArray (rownr_t rownr); // Read the shape at the given row. // It throws an exception if undefined. Data* getShape (rownr_t rownr); // Get a pointer to the data array. void* getDataPtr (rownr_t rownr) { return (char*)(getShape(rownr)) + startSize_p; } }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/MappedArrayEngine.h000066400000000000000000000174571476623553700216250ustar00rootroot00000000000000//# MappedArrayEngine.h: Templated virtual column engine to map a table array //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_MAPPEDARRAYENGINE_H #define TABLES_MAPPEDARRAYENGINE_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Templated virtual column engine to map the data type of a table array // // // // // //# Classes you should understand before using this one. //
      • VirtualColumnEngine //
      • VirtualArrayColumn // // // MappedArrayEngine is a virtual column engine which maps an array // of one type to another type (without any scaling). // // An engine object should be used for one column only, because the stored // column name is part of the engine. If it would be used for more than // one column, they would all share the same stored column. // When the engine is bound to a column, it is checked if the name // of that column matches the given virtual column name. // // The engine can be used for a column containing any kind of array // (thus direct or indirect, fixed or variable shaped)) as long as the // virtual array can be stored in the stored array. Thus a fixed shaped // virtual can use a variable shaped stored, but not vice versa. // A fixed shape indirect virtual can use a stored with direct arrays. // // // For precision it is sometimes needed to store the visibility data in a // MeasurementSet in double precision. To be able to use other applications // on such data, it is needed to map them to single precision. // // Because the engine can serve only one column, it was possible to // combine the engine and the column functionality in one class. // This has been achieved using multiple inheritance. // The advantage of this is that only one templated class is used, // so less template instantiations are needed. // // // // // Create the table description and 2 columns with indirect arrays in it. // // The Int column will be stored, while the double will be // // used as virtual. // TableDesc tableDesc ("", TableDesc::Scratch); // tableDesc.addColumn (ArrayColumnDesc ("storedArray")); // tableDesc.addColumn (ArrayColumnDesc ("virtualArray")); // // // Create a new table using the table description. // SetupNewTable newtab (tableDesc, "tab.data", Table::New); // // // Create the array mapping engine to map from double to Int // // and bind it to the double column. // // Create the table. // MappedArrayEngine mappingEngine("virtualArray", // "storedArray", 10); // newtab.bindColumn ("virtualArray", mappingEngine); // Table table (newtab); // // // Store a 3-D array (with dim. 2,3,4) into each row of the column. // // The shape of each array in the column is implicitly set by the put // // function. This will also set the shape of the underlying Int array. // ArrayColumn data (table, "virtualArray"); // Array someArray(IPosition(4,2,3,4)); // someArray = 0; // for (rownr_t i=0, i<10; i++) { // table will have 10 rows // table.addRow(); // data.put (i, someArray) // } // // // //
      • only suited for built-in numerics data types // // //
      • only suited for built-in numerics data types // template class MappedArrayEngine : public BaseMappedArrayEngine { //# Make members of parent class known. public: using BaseMappedArrayEngine::virtualName; protected: using BaseMappedArrayEngine::storedName; using BaseMappedArrayEngine::table; using BaseMappedArrayEngine::column; using BaseMappedArrayEngine::setNames; public: // Construct an engine to map all arrays in a column. // StoredColumnName is the name of the column where the mapped // data will be put and must have data type StoredType. // The virtual column using this engine must have data type VirtualType. MappedArrayEngine (const String& virtualColumnName, const String& storedColumnName); // Construct from a record specification as created by dataManagerSpec(). MappedArrayEngine (const Record& spec); // Destructor is mandatory. ~MappedArrayEngine(); // Assignment is not needed and therefore forbidden. MappedArrayEngine& operator= (const MappedArrayEngine&) = delete; // Return the type name of the engine (i.e. its class name). virtual String dataManagerType() const; // Get the name given to the engine (is the virtual column name). virtual String dataManagerName() const; // Record a record containing data manager specifications. virtual Record dataManagerSpec() const; // Return the name of the class. // This includes the names of the template arguments. static String className(); // Register the class name and the static makeObject "constructor". // This will make the engine known to the table system. // The automatically invoked registration function in DataManReg.cc // contains MappedArrayEngine. // Any other instantiation of this class must be registered "manually" // (or added to DataManReg.cc). static void registerClass(); private: // Copy constructor is only used by clone(). // (so it is made private). MappedArrayEngine (const MappedArrayEngine&); // Clone the engine object. DataManager* clone() const; // Copy the stored array to the virtual array. virtual void mapOnGet (Array& array, const Array& stored); // Copy the virtual array to the stored array. virtual void mapOnPut (const Array& array, Array& stored); public: // Define the "constructor" to construct this engine when a // table is read back. // This "constructor" has to be registered by the user of the engine. // If the engine is commonly used, its registration can be added // to the registerAllCtor function in DataManReg.cc. // That function gets automatically invoked by the table system. static DataManager* makeObject (const String& dataManagerType, const Record& spec); }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/tables/DataMan/MappedArrayEngine.tcc000066400000000000000000000100061476623553700221260ustar00rootroot00000000000000//# MappedArrayEngine.cc: Templated virtual column engine to map a table array //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_MAPPEDARRAYENGINE_TCC #define TABLES_MAPPEDARRAYENGINE_TCC //# Includes #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template MappedArrayEngine::MappedArrayEngine (const String& virtualColumnName, const String& storedColumnName) : BaseMappedArrayEngine (virtualColumnName, storedColumnName) {} template MappedArrayEngine::MappedArrayEngine (const Record& spec) : BaseMappedArrayEngine () { if (spec.isDefined("SOURCENAME") && spec.isDefined("TARGETNAME")) { setNames (spec.asString("SOURCENAME"), spec.asString("TARGETNAME")); } } template MappedArrayEngine::MappedArrayEngine (const MappedArrayEngine& that) : BaseMappedArrayEngine (that) {} template MappedArrayEngine::~MappedArrayEngine() {} //# Clone the engine object. template DataManager* MappedArrayEngine::clone() const { DataManager* dmPtr = new MappedArrayEngine (*this); return dmPtr; } //# Return the type name of the engine (i.e. its class name). template String MappedArrayEngine::dataManagerType() const { return className(); } //# Return the class name. //# Get the data type names using class ValType. template String MappedArrayEngine::className() { return "MappedArrayEngine<" + valDataTypeId (static_cast(0)) + "," + valDataTypeId (static_cast(0)) + ">"; } template String MappedArrayEngine::dataManagerName() const { return virtualName(); } template Record MappedArrayEngine::dataManagerSpec() const { Record spec; spec.define ("SOURCENAME", virtualName()); spec.define ("TARGETNAME", storedName()); return spec; } template DataManager* MappedArrayEngine::makeObject (const String&, const Record& spec) { DataManager* dmPtr = new MappedArrayEngine(spec); return dmPtr; } template void MappedArrayEngine::registerClass() { DataManager::registerCtor (className(), makeObject); } template void MappedArrayEngine::mapOnGet (Array& array, const Array& target) { convertArray (array, target); } template void MappedArrayEngine::mapOnPut (const Array& array, Array& target) { convertArray (target, array); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/MemoryStMan.cc000066400000000000000000000030541476623553700206270ustar00rootroot00000000000000//# MemoryStMan.cc: Storage manager for columns held in meory //# Copyright (C) 2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MemoryStMan::MemoryStMan() : MSMBase() {} MemoryStMan::MemoryStMan (const String& storageManagerName) : MSMBase (storageManagerName) {} MemoryStMan::~MemoryStMan() {} } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/MemoryStMan.h000066400000000000000000000064461476623553700205010ustar00rootroot00000000000000//# MemoryStMan.h: Storage manager for tables using memory //# Copyright (C) 2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_MEMORYSTMAN_H #define TABLES_MEMORYSTMAN_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Memory-based table storage manager class // // // // // //# Classes you should understand before using this one. //
      • DataManager //
      • MSMColumn // // // MemoryStMan is a table storage manager based in memory. // It holds all data in the columns in memory and deletes them // when the table gets closed. // It contains pointers to the underlying MSMColumn objects, // which do the actual data handling. // // The Memory storage manager does fully support addition and removal // of rows and columns. // // The primary use of this storage manager is for a memory-based table, // but it can also be used for temporary columns in disk-based tables. // When reopening a disk-based table, possible columns stored with // MemoryStMan will be initialized to 0. // An important issue is synchronizing tables containing MemoryStMan // storage managers in case of concurrent access. Because its data are // not stored on disk, there is no way to synchronize the data if another // process changed data or added or deleted rows. If the number or rows // has changed, rows will be added or deleted as needed. Row deletion // will be done at the end of the table. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class MemoryStMan: public MSMBase { public: // Create an Memory storage manager. // Its name will be blank. MemoryStMan(); // Create an Memory storage manager with the given name. // Its name can be used later in e.g. Table::addColumn to // add a column to this storage manager. MemoryStMan (const String& storageManagerName); ~MemoryStMan(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/RetypedArrayEngine.h000066400000000000000000000660121476623553700220220ustar00rootroot00000000000000//# RetypedArrayEngine.h: Virtual column engine to retype and reshape arrays //# Copyright (C) 1995,1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_RETYPEDARRAYENGINE_H #define TABLES_RETYPEDARRAYENGINE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // Virtual column engine to retype and reshape arrays. // // // // // //# Classes you should understand before using this one. //
      • BaseMappedArrayEngine // // // RetypedArrayEngine maps a virtual column containing arrays of objects // to a stored column containing arrays of data of another type. Usually // the dimensionality of the arrays get smaller during this mapping process. // The engine makes it possible to store an array of any type in a table. //
        // For example, a column with 2D arrays of StokesVector's can be mapped to // a column with 3D arrays of floats (of which the first axes has, say, // length 4). Another example is mapping a 2D array of StokesMatrix's // to a 4D array of floats. //

        // The mapping process has to be done by a (static) set and get // function in the VirtualType class. When a RetypedArrayEngine object is // constructed, it is possible to pass information in a TableRecord. This // TableRecord is indirectly passed to the set/get functions. This is done by // means of the function newCopyInfo, which can preprocess the information // in the TableRecord and store it in another object. That object is passed to // the set and get functions. At the end a function deleteCopyInfo is called // to delete the object. Of course, it is not needed to allocate such // an object; newCopyInfo can return a null pointer. // Because the variables have to be generic and because of // limitations in the CFront compiler, several variables have to be // passed as void* and need to be casted in the set/get functions. // // // The virtual column data type class has to contain several functions. // The example shows how they can be implemented. //

        //
        static String dataTypeId(); //
        has to give the (unique) name of the class. //
        static IPosition shape(); //
        This has to return the full shape of the elements in the virtual. // E.g. StokesVector will return [4]. StokesMatrix will return [4,4]. //
        static void* newCopyInfo (const TableRecord& record, // const IPosition& virtualElementShape); //
        This function has to setup the set/get functions by preprocessing the // information contained in the TableRecord and storing it in a so-called // "copyInfo" object. A pointer to that object has to be returned, which // is kept by the engine and passed to the set/get functions. // The "copyInfo" class can be a nested class in the VirtualType // (as shown in the StokesVector example), but it can also // be an independent class. //
        // The supplied TableRecord is the TableRecord given when // constructing the engine. // When no TableRecord was given, it will be empty. // The supplied shape is the shape of a virtual element as given to // the constructor of the engine. This can be a full or partial shape. // E.g. for a StokesVector it will usually be [4], but it can also, // say, [1] if only U is used. // The function could check if the information in the TableRecord // and the shape match. //
        // Of course, a VirtualType may not need any extra information. // Therefore it is possible to return a null "copyInfo" pointer. //
        static void deleteCopyInfo (void* copyInfo); //
        This function has to delete the "copyInfo" object allocated // by newCopyInfo. To do so, it needs to cast the pointer to the // correct type. //
        static void set (void* copyInfo, void* out, // const Array& in, // const IPosition& virtualElementShape); //
        This function is called when an Array is read. // It has to convert the StoredType array to the VirtualType array. // In principle, there are two different cases (which can be deduced // from the given shape): //
          //
        1. The stored information is complete. For example: suppose the // VirtualType is a StokesVector object (containing I, Q, U and V), // When the stored array contains 4 values per StokesVector, // it is complete. //
          // In this case the entire virtual array can be directly copied from // the stored array when the VirtualType object contains no // virtual functions and the data are directly contained in it. // The function //
          // // // retypedArrayEngineSet (Array& out, // const Array& in); //
          // can be used for this purpose. //
        2. When in the example above the stored array contains less // than 4 values per StokesVector, the stored information // is incomplete. In this case the set function has to // fill the data in one way or another. The information // in the "copyInfo" object can assist in it. //
          // Each VirtualType element has to be set individually, so // a loop through the array is required. To assist in this, // the loop has been implemented in the function //
          // // // retypedArrayEngineSet (Array& out, // const Array& in, // const void* extraArgument); // //
          It calls the VirtualType function // // void setElem (const StoredType* data, const IPosition& shape, // const void* extraArgument); // // for each VirtualType element. This set function has to // fill the VirtualType object from the data. It can use the // shape and the extraArgument to know how it should do it. //
          // Note that the 3-argument function retypedArrayEngineSet is // only a convenience function. For optimal performance it may // be needed to handcode the loop instead of using this function. //
        // Note that the given virtual element shape does // not need to match the shape given to the constructor of the engine. // It is possible that the user sets the shape of the stored array // before putting the virtual array. In that case the system uses the // relevant part of the stored array shape as the virtual element shape. // // If the out argument is declared (as it should be) as // Array& out, // the CFront compiler complains about unknown size of // VirtualType when instantiating Array. // Therefore it has to be declared as void* and the set function // needs to cast it to Array*. // //
        static void get (void* copyInfo, Array& out, // const void* in, // const IPosition& virtualElementShape); //
        This function is similar to the set function described above, but // is called when an Array is written. // It has to convert the VirtualType array to the StoredType array. //
        // //
        E.g.: A StokesVector has 4 float elements. // // // Construct the column object for the Stokes column. // ArrayColumn stokesColumn (table, "StokesVirtualColumn"); // // Put an array of StokesVector's with shape 512,512. // // This will implicitly set the shape of the underlying // // data column to 4,512,512. // // This put is very quick (it can copy all data in one go). // Array stokesData (IPosition(2,512,512)); // stokesColumn.put (rownr, stokesData); // // // Get the column object for the Data column. // // Set its shape explicitly to 1,512,512, // ArrayColumn dataColumn (table, "DataColumn"); // dataColumn.setShape (rownr, IPosition(3,1,512,512)); // // Now a put of the data results in calling the StokesVector::getElem // // function for each element with an IPosition(1,1); i.e. the // // data array needs only one value for each StokesVector. // stokesColumn.put (rownr, stokesData); // // // When reading a table back, the engine has to be registered. // Otherwise it will be unknown to the table system. // Similarly, the appropriate ArrayColumnDesc object has to be registered. // This can be done as follows: //
        //    RetypedArrayEngine::registerClass();
        //    ArrayColumnDesc tmp(ColumnDesc::registerMap);
        // 
        // When they are not registered, the open of the table will fail // telling which class could not be found. //
        // // This class allows one to store arrays of arbitrary objects in a table. // It also allows it to be done it in a very efficient way. //

        // The class had to be doubly templated. There were 2 reasons: //

          //
        1. The typedef trick described on page 321 in Barton/Nackman // did not work with the CFront-based ObjectCenter compiler. //
        2. It was needed to allow derivation from BaseMappedArrayEngine. //
        //

        // Originally it was the idea to have a mandatory nested CopyInfo class in the // VirtualType class and use syntax like VirtualType::CopyInfo to access // functions in it and to keep a pointer to such an object. Alas, the // CFront compiler could not handle this. //

        // Because the engine can serve only one column, it was possible to // combine the engine and the column functionality in one class. // This has been achieved using multiple inheritance. // The advantage of this is that only one templated class is used, // so fewer template instantiations are needed. // // // The following example shows how a StokesVector could be implemented. // It doesn't check whether the mask is correct. // Two more examples are contained in the demo/test program // // dRetypedArrayEngine.h and its // // .cc file. Their second example (class RetypedArrayEx2) is similar to // the StokesVector example below, but contains more extensive checking. // // //# Forward Declarations // template class Array; // template class Vector; // // class StokesVector // { // public: // StokesVector(): I_p(0), Q_p(0), U_p(0), V_p(0) {} // StokesVector(double i, double q, double u, double v) // : I_p(i), Q_p(q), U_p(u), V_p(v) {} // StokesVector(const StokesVector& that): I_p(that.I_p), Q_p(that.Q_p), // U_p(that.U_p), V_p(that.V_p) {} // StokesVector& operator= (const StokesVector& that) // { I_p=that.I_p; Q_p=that.Q_p; U_p=that.U_p; V_p=that.V_p; // return *this; } // // static String dataTypeId() // { return "StokesVector"; } // // // A StokesVector is 1-dim and contains 4 elements. // static IPosition shape() // { return IPosition (1,4); } // // // Preprocess possible information in the TableRecord. // static void* newCopyInfo (const TableRecord& record, // const IPosition& shape) // { return new CopyInfo(record, shape); } // // // Delete the object containing preprocessed information. // static void* deleteSetDet (void* copyInfo) // { delete (CopyInfo*)copyInfo; } // // // Convert a StoredType array to a VirtualType array. // // Do this in a CopyInfo function to use its preprocessed information. // static void set (void* copyInfo, void* out, // const Array& in, const IPosition& shape) // { ((CopyInfo*)copyInfo)->set (out, in, shape); } // // // Convert a VirtualType array to a StoredType array. // // Do this in a CopyInfo function to use its preprocessed information. // static void get (void* copyInfo, Array& out, // const void* in, const IPosition& shape) // { ((CopyInfo*)copyInfo)->get (out, in, shape); } // // // This nested class is used to hold preprocessed information. It // // holds a mask extracted from the TableRecord supplied to the engine. // // One can imagine that it could also extract a flag telling // // whether the stored data is stored as I,Q,U,V or as XX,YY,XY,YX // // (although such a conversion would probably be better handled // // by a separate virtual column engine). // class CopyInfo { // public: // // The constructor extracts the mask from the record. // void CopyInfo (const TableRecord& record) // { // RORecordFieldRef > field (record, 0); // mask_p = new Vector; // *mask_p = *field; // } // // The set function fills the StokesVector. // // It uses the general functions for that purpose. // void set (void* vout, const Array& in, // const IPosition& shape) // { // Array& out = *(Array*)vout; // if (shape.nelements() == 1 && shape(0) == 4) { // // All values available, copy in one go. // // This can be done because a StokesVector object // // only contains 4 double values (and no virtual // // function table). // retypedArrayEngineSet (out, in); // }else{ // // Only some values available. Fill each // // StokesVector object using the shape and mask. // // The set function below is called for each object. // retypedArrayEngineSet (out, in, shape, (void*)mask_p); // } // } // // get is the opposite of set. // void get (Array& out, const void* vin, // const IPosition& shape) // { // const Array& in = // *(const Array*)vin; // if (shape.nelements() == 1 && shape(0) == 4) { // retypedArrayEngineGet (out, in); // }else{ // retypedArrayEngineGet (out, in, shape, (void*)mask_p); // } // private: // Vector* mask_p; // }; // // // Set values of StokesVector using the mask. // // The shape is not used here. // void setElem (const double* data, const IPosition&, const void* maskPtr) // { // const Vector& mask = *(const Vector*)maskPtr; // I_p = Q_p = U_p = V_p = 0; // if (mask(0)) { // I_p = *data++; // } // if (mask(1)) { // Q_p = *data++; // } // if (mask(2)) { // U_p = *data++; // } // if (mask(3)) { // V_p = *data; // } // } // // Get values of StokesVector using the mask (opposite of setElem). // void getElem (double* data, const IPosition&, const void* maskPtr); // private: // double I_p, Q_p, U_p, V_p; // }; // // main() { // // First register the virtual column engine. // RetypedArrayEngine::registerClass(); // // Add ArrayColumnDesc to column type map. // ArrayColumnDesc tmp(ColumnDesc::registerMap); // // // Build the table description. // TableDesc td("", "1", TableDesc::Scratch); // td.addColumn (ArrayColumnDesc ("Data")); // td.addColumn (ArrayColumnDesc ("Stokes")); // // // Now create a new table from the description. // SetupNewTable newtab("tRetypedArrayEngine_tmp.data", td, Table::New); // // Create the virtual column engine with the stored columns Data. // RetypedArrayEngine engine ("Stokes", "Data"); // newtab.bindColumn ("Stokes", engine); // Table tab(newtab, 50); // // // Fill the table via the virtual columns. // ArrayColumn stokesColumn (tab, "Stokes"); // Vector vec(10); // rownr_t i; // for (i=0; i // // Due to instantiation problems with the CFront-based ObjectCenter compiler // (and probably other CFront-based compilers as well) the Array and // Vector have to be forward declared. Array.h and Vector.h should // NOT be included in this StokesVector.h, thus the implementations // should not be inlined (they are too large anyway), but put in a // separate .cc file where Array.h and Vector.h can be included. // //

        // Another compiler problem is that the variable mask_p is not // automatically converted to a void*, so an explicit cast has to be done. // // //

      • default constructor //
      • copy constructor //
      • assignment operator //
      • static String dataTypeId(); //
      • static IPosition shape(); //
      • static void* newCopyInfo (const TableRecord& record, const IPosition& virtualElementShape); //
      • static void deleteCopyInfo (void* copyInfo); //
      • static void set (void* copyInfo, void* out, // const Array& in, // const IPosition& shape); //
      • static void get (void* copyInfo, Array& out, // const void* in, const IPosition& shape); //
      • void setElem (const StoredType* data, const IPosition& shape, // const void* extraArgument); //
        when global function retypedArrayEngineSet is used. //
      • void getElem (StoredType* data, const IPosition& shape, // const void* extraArgument) const; //
        when global function retypedArrayEngineGet is used. // // //
      • Default constructor //
      • Copy constructor //
      • Assignment operator // //# //# template class RetypedArrayEngine : public BaseMappedArrayEngine { //# Make members of parent class known. public: using BaseMappedArrayEngine::virtualName; protected: using BaseMappedArrayEngine::storedName; using BaseMappedArrayEngine::table; using BaseMappedArrayEngine::column; using BaseMappedArrayEngine::setNames; public: // Construct an engine to map a virtual column containing arrays with // an arbitrary data type to arrays in a stored column. // StoredColumnName is the name of the column where the converted // data will be put and must have data type StoredType. // The virtual column using this engine must have data type VirtualType. RetypedArrayEngine (const String& virtualColumnName, const String& storedColumnName); // Construct an engine to map a virtual column containing arrays with // an arbitrary data type to arrays in a stored column. // StoredColumnName is the name of the column where the converted // data will be put and must have data type StoredType. // The virtual column using this engine must have data type VirtualType. // The shape and record provided is handed to the newCopyInfo function // in the VirtualType class. It can be used to determine how an element // has to be handled when the stored data is incomplete. RetypedArrayEngine (const String& virtualColumnName, const String& storedColumnName, const IPosition& virtualElementShape, const TableRecord& extraInformation); // Construct from a record specification as created by getmanagerSpec(). RetypedArrayEngine (const Record& spec); // Destructor is mandatory. ~RetypedArrayEngine(); // Assignment is not needed and therefore forbidden. RetypedArrayEngine& operator= (const RetypedArrayEngine&) = delete; // Return the type name of the engine (i.e. its class name). virtual String dataManagerType() const; // Get the name given to the engine (is the virtual column name). virtual String dataManagerName() const; // Record a record containing data manager specifications. virtual Record dataManagerSpec() const; // Return the name of the class. // This includes the names of the template arguments. static String className(); // Register the class name and the static makeObject "constructor". // This will make the engine known to the table system. // The automatically invoked registration function in DataManReg.cc // contains RetypedArrayEngine. // Any other instantiation of this class must be registered "manually" // (or added to DataManReg.cc). static void registerClass(); private: // Copy constructor is only used by clone(). // (so it is made private). RetypedArrayEngine (const RetypedArrayEngine&); // Clone the engine object. DataManager* clone() const; // Initialize the object for a new table. // It defines the keywords containing the engine parameters. void create64 (rownr_t initialNrrow); // Preparing consists of setting the writable switch and // adding the initial number of rows in case of create. // Furthermore it reads the keywords containing the engine parameters // and allocates a CopyInfo object for the VirtualType. void prepare(); // Set the shape of the FixedShape arrays in the column. // This function only gets called if the column has FixedShape arrays. // The shape gets saved and used to set the shape of the arrays // in the stored in case the stored has non-FixedShape arrays. void setShapeColumn (const IPosition& shape); // Define the shape of the array in the given row. // When the shape of the (underlying) stored array has already been // defined, it checks whether its latter dimensions match the given // virtual shape. When matching, nothing will be done. // When mismatching or when the stored shape has not been defined // yet, the stored shape will be defined from the virtual shape and // the virtual element shape. // E.g. in case of a StokesVector a virtual shape of (512,512) // results in a stored shape of (4,512,512). void setShape (rownr_t rownr, const IPosition& shape); // Get the dimensionality of the array in the given row. uInt ndim (rownr_t rownr); // Get the shape of the array in the given row. // This is done by stripping the first dimension(s) from the shape // of the underlying stored array. IPosition shape (rownr_t rownr); // Check if the shapes of virtual and stored match. // Determine the shape of the virtual elements in the stored. IPosition checkShape (const Array& source, const Array& target); // Map the virtual shape to the stored shape. // By default is returns the virtual shape. virtual IPosition getStoredShape (rownr_t rownr, const IPosition& virtualShape); // Convert the Slicer for a virtual to a Slicer for the stored. virtual Slicer getStoredSlicer (const Slicer& virtualSlicer) const; // Copy the stored array to the virtual array. // It tries to optimize as much as possible. virtual void mapOnGet (Array& array, const Array& stored); // Copy the virtual array to the stored array. // It tries to optimize as much as possible. virtual void mapOnPut (const Array& array, Array& stored); //# Now define the data members. IPosition shape_p; //# shape of a virtual element in the stored IPosition virtualFixedShape_p; //# The shape in case virtual has FixedShape Bool isVirtualFixedShape_p; TableRecord record_p; //# VirtualType::CopyInfo* copyInfo_p; //# object used to set/get arrays void* copyInfo_p; //# CFront compiler does not accept above public: //*display 4 // Define the "constructor" to construct this engine when a // table is read back. // This "constructor" has to be registered by the user of the engine. // If the engine is commonly used, its registration can be added // to the registerAllCtor function in DataManReg.cc. // That function gets automatically invoked by the table system. static DataManager* makeObject (const String& dataManagerType, const Record& spec); }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/tables/DataMan/RetypedArrayEngine.tcc000066400000000000000000000250131476623553700223400ustar00rootroot00000000000000//# RetypedArrayEngine.cc: Virtual column engine to retype and reshape arrays //# Copyright (C) 1995,1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_RETYPEDARRAYENGINE_TCC #define TABLES_RETYPEDARRAYENGINE_TCC //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template RetypedArrayEngine::RetypedArrayEngine (const String& virtualColumnName, const String& storedColumnName) : BaseMappedArrayEngine (virtualColumnName, storedColumnName), shape_p (S::shape()), isVirtualFixedShape_p (False), copyInfo_p (0) {} template RetypedArrayEngine::RetypedArrayEngine (const String& virtualColumnName, const String& storedColumnName, const IPosition& virtualShape, const TableRecord& extraInformation) : BaseMappedArrayEngine (virtualColumnName, storedColumnName), shape_p (virtualShape), isVirtualFixedShape_p (False), record_p (extraInformation), copyInfo_p (0) {} template RetypedArrayEngine::RetypedArrayEngine (const Record& spec) : BaseMappedArrayEngine (), isVirtualFixedShape_p (False), copyInfo_p (0) { if (spec.isDefined("SOURCENAME") && spec.isDefined("TARGETNAME")) { setNames (spec.asString("SOURCENAME"), spec.asString("TARGETNAME")); if (spec.isDefined("SHAPE")) { Vector shp; spec.get ("SHAPE", shp); shape_p.fill (shp.size(), shp.begin()); } if (spec.isDefined("RECORD")) { record_p = spec.asRecord ("RECORD"); } } } template RetypedArrayEngine::RetypedArrayEngine (const RetypedArrayEngine& that) : BaseMappedArrayEngine (that), shape_p (that.shape_p), isVirtualFixedShape_p (False), record_p (that.record_p), copyInfo_p (0) {} template RetypedArrayEngine::~RetypedArrayEngine() { S::deleteCopyInfo (copyInfo_p); } //# Clone the engine object. template DataManager* RetypedArrayEngine::clone() const { DataManager* dmPtr = new RetypedArrayEngine (*this); return dmPtr; } //# Return the type name of the engine (i.e. its class name). template String RetypedArrayEngine::dataManagerType() const { return className(); } //# Return the class name. //# Get the data type names using class ValType. template String RetypedArrayEngine::className() { return "RetypedArrayEngine<" + valDataTypeId(static_cast(0)) + "," + valDataTypeId(static_cast(0)) + ">"; } template String RetypedArrayEngine::dataManagerName() const { return virtualName(); } template Record RetypedArrayEngine::dataManagerSpec() const { Record spec; spec.define ("SOURCENAME", virtualName()); spec.define ("TARGETNAME", storedName()); spec.define ("SHAPE", shape_p.asVector()); if (record_p.nfields() > 0) { spec.defineRecord ("RECORD", record_p); } return spec; } template DataManager* RetypedArrayEngine::makeObject (const String&, const Record& spec) { DataManager* dmPtr = new RetypedArrayEngine(spec); return dmPtr; } template void RetypedArrayEngine::registerClass() { DataManager::registerCtor (className(), makeObject); } template void RetypedArrayEngine::prepare() { // Get the various parameters from keywords in this column. TableColumn thisCol (table(), virtualName()); Vector vec (thisCol.keywordSet().toArrayInt64("_RetypedArrayEngine_Shape")); shape_p.fill (vec.size(), vec.begin()); record_p = thisCol.keywordSet().subRecord ("_RetypedArrayEngine_Record"); // Set the column shape in the base class (when needed). // This has to be dome before prepare in the base class is called. if (isVirtualFixedShape_p) { BaseMappedArrayEngine::setShapeColumn (shape_p.concatenate (virtualFixedShape_p)); } BaseMappedArrayEngine::prepare(); // Allocate and initialize a CopyInfo object for the virtual. copyInfo_p = S::newCopyInfo (record_p, shape_p); } template void RetypedArrayEngine::create64 (rownr_t initialNrrow) { BaseMappedArrayEngine::create64 (initialNrrow); // Store the various parameters as keywords in this column. TableColumn thisCol (this->makeTableColumn (virtualName())); thisCol.rwKeywordSet().define ("_RetypedArrayEngine_Shape", shape_p.asVector()); thisCol.rwKeywordSet().defineRecord ("_RetypedArrayEngine_Record", record_p); } //# This function is called in case the virtual column has FixedShape arrays. //# Because the shape of the VirtualType is not known yet (it is read //# in prepare), the base class setShapeColumn is done in prepare(). template void RetypedArrayEngine::setShapeColumn (const IPosition& shape) { virtualFixedShape_p = shape; isVirtualFixedShape_p = True; } template void RetypedArrayEngine::setShape (rownr_t rownr, const IPosition& shape) { //# Do not define the shape in the stored column when it has //# already been defined and matches the virtual shape. if (column().isDefined (rownr)) { IPosition storedShape = column().shape (rownr); IPosition virtualShape = storedShape.getLast (shape.nelements()); if (shape.isEqual (virtualShape)) { return; } } //# Set the stored shape to the default element shape plus virtual shape. column().setShape (rownr, shape_p.concatenate (shape)); } template uInt RetypedArrayEngine::ndim (rownr_t rownr) { return column().ndim (rownr) - shape_p.nelements(); } template IPosition RetypedArrayEngine::shape (rownr_t rownr) { // The virtual shape is the stored shape minus the first dimensions. IPosition storedShape = column().shape (rownr); return storedShape.getLast (storedShape.nelements() - shape_p.nelements()); } template IPosition RetypedArrayEngine::getStoredShape (rownr_t rownr, const IPosition& virtualShape) { //# Determine the element shape. //# If the stored is defined, take it from there. IPosition elemShape(shape_p); if (rownr < table().nrow() && column().isDefined (rownr)) { elemShape = (column().shape(rownr)).getFirst (elemShape.nelements()); } //# The stored shape is element shape plus virtual shape. return elemShape.concatenate (virtualShape); } template Slicer RetypedArrayEngine::getStoredSlicer (const Slicer& virtualSlicer) const { //# Determine the element dimensionality. //# Make the Slicer such that all values of the element are used. uInt ndim = shape_p.nelements(); return Slicer (IPosition(ndim,0).concatenate (virtualSlicer.start()), IPosition(ndim,Slicer::MimicSource). concatenate (virtualSlicer.end()), IPosition(ndim,1).concatenate (virtualSlicer.stride()), Slicer::endIsLast); } template IPosition RetypedArrayEngine::checkShape (const Array& source, const Array& target) { IPosition tShape = target.shape(); IPosition sShape = source.shape(); //# Check if the dimensionalities match. //# Source + element shape must match stored shape. if (tShape.nelements() != shape_p.nelements() + sShape.nelements()) { throw (DataManInvOper ("RetypedArrayEngine: stored/virtual" " dimensionalities are not appropriate")); } //# Determine and check the shape of the virtual elements in the target //# which are formed by the first axes in the stored. //# Their shape cannot be greater than the real virtual element shape. IPosition elemShape (shape_p.nelements()); uInt i; //used later for (i=0; i shape_p(i)) { throw (DataManInvOper ("RetypedArrayEngine: stored shape > virtual")); } elemShape(i) = tShape(i); } //# Check if remaining sizes in stored shape match virtual shape. for (uInt j=0; j void RetypedArrayEngine::mapOnGet (Array& array, const Array& target) { IPosition elemShape = checkShape (array, target); S::set (copyInfo_p, &array, target, elemShape); } // Copy an array for put. template void RetypedArrayEngine::mapOnPut (const Array& array, Array& target) { IPosition elemShape = checkShape (array, target); S::get (copyInfo_p, target, &array, elemShape); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/RetypedArraySetGet.h000066400000000000000000000131521476623553700220050ustar00rootroot00000000000000//# RetypedArraySetGet.h: Helper functions for users of RetypedArrayEngine //# Copyright (C) 1994,1995,1996,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_RETYPEDARRAYSETGET_H #define TABLES_RETYPEDARRAYSETGET_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class IPosition; // // Helper functions for users of RetypedArrayEngine // // // // // //# Classes you should understand before using this one. //
      • RetypedArrayEngine // // // The functions in here can be used in the implementation of the // CopyInfo class inside a SourceType class used by a RetypedArrayEngine. // // // The example in RetypedArrayEngine shows how these functions can be used. // // // These functions make the implementation of the set and get // functions in the SourceType objects of the RetypedArrayEngine easier. // They are not part of the RetypedArrayEngine.h file to avoid // the inclusion of that (heavy) file in a SourceType. // // // Copy the entire target array to the source array. // It will check if the shapes and sizes match. //
        // This very efficient copy function can only be called by the static set // function in the SourceType when the TargetType array can directly be // copied to the SourceType array. //
        See // RetypedArrayEngine for // more information. template void retypedArrayEngineSet (Array& out, const Array& in); // Copy the entire source array to the target array. // It will check if the shapes and sizes match. //
        // This very efficient copy function can only be called by the static set // function in the SourceType when the TargetType array can directly be // copied to the SourceType array. //
        See // RetypedArrayEngine for // more information. template void retypedArrayEngineGet (Array& out, const Array& in); // Fill an array with SourceType objects from the target array. // This is called when the target is incomplete. // The shape and extra argument can help to set the correct // elements in the source. //
        // It loops through all elements in the SourceType array and // calls the SourceType function // It calls the SourceType function // // void setElem (const TargetType* data, const IPosition& shape, // const void* extraArgument); // // for each element. // // This retypedArrayEngineSet function is only a convenience function. // For optimal performance it may be needed to handcode the loop instead // of using this function. // //
        See // RetypedArrayEngine for // more information. template void retypedArrayEngineSet (Array& out, const Array& in, const IPosition& shape, const void* extraArgument); // Fill an array with TargetType objects from the source array. // This is called when the target is incomplete. // The shape and extra argument can help to get the correct // elements from the source. //
        // It loops through all elements in the SourceType array and // calls the SourceType function // // void getElem (TargetType* data, const IPosition& shape, // const void* extraArgument); // // for each element. // // This retypedArrayEngineGet function is only a convenience function. // For optimal performance it may be needed to handcode the loop instead // of using this function. // //
        See // RetypedArrayEngine for // more information. template void retypedArrayEngineGet (Array& out, const Array& in, const IPosition& shape, const void* extraArgument); //
        } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/tables/DataMan/RetypedArraySetGet.tcc000066400000000000000000000104331476623553700223260ustar00rootroot00000000000000//# RetypedArraySetGet.cc: Helper functions for users of RetypedArrayEngine //# Copyright (C) 1994,1995,1996 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_RETYPEDARRAYSETGET_TCC #define TABLES_RETYPEDARRAYSETGET_TCC //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Copy the entire target array to the source array. // It will check if the shapes and sizes match. template void retypedArrayEngineSet (Array& out, const Array& in) { Bool deleteIn, deleteOut; SourceType* dataOut = out.getStorage (deleteOut); const TargetType* dataIn = in.getStorage (deleteIn); objcopy ((TargetType*)dataOut, dataIn, in.nelements()); in.freeStorage (dataIn, deleteIn); out.putStorage (dataOut, deleteOut); } // Copy the entire source array to the target array. // It will check if the shapes and sizes match. template void retypedArrayEngineGet (Array& out, const Array& in) { Bool deleteIn, deleteOut; TargetType* dataOut = out.getStorage (deleteOut); const SourceType* dataIn = in.getStorage (deleteIn); objcopy (dataOut, (const TargetType*)dataIn, out.nelements()); in.freeStorage (dataIn, deleteIn); out.putStorage (dataOut, deleteOut); } // Fill an array with SourceType objects from the target array. // This is called when the target is incomplete. // The shape and extra argument can help to fill the source. template void retypedArrayEngineSet (Array& out, const Array& in, const IPosition& shape, const void* extraArgument) { Bool deleteIn, deleteOut; SourceType* dataOut = out.getStorage (deleteOut); const TargetType* dataIn = in.getStorage (deleteIn); // Set element by element. Int64 n = shape.product(); SourceType* op = dataOut; const TargetType* ip = dataIn; const TargetType* last = ip + in.nelements(); while (ip < last) { op++->setElem (ip, shape, extraArgument); ip += n; } in.freeStorage (dataIn, deleteIn); out.putStorage (dataOut, deleteOut); } // Fill an array with TargetType objects from the source array. // This is called when the target is incomplete. // The shape and extra argument can help to extract the correct // elements from the source. template void retypedArrayEngineGet (Array& out, const Array& in, const IPosition& shape, const void* extraArgument) { Bool deleteIn, deleteOut; TargetType* dataOut = out.getStorage (deleteOut); const SourceType* dataIn = in.getStorage (deleteIn); // Set element by element. Int64 n = shape.product(); TargetType* op = dataOut; const SourceType* ip = dataIn; const SourceType* last = ip + in.nelements(); while (ip < last) { ip++->getElem (op, shape, extraArgument); op += n; } in.freeStorage (dataIn, deleteIn); out.putStorage (dataOut, deleteOut); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/SSMBase.cc000066400000000000000000001016521476623553700176540ustar00rootroot00000000000000//# SSMBase.cc: Base class of the Standard Storage Manager //# Copyright (C) 2000,2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusettes Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN SSMBase::SSMBase (Int aBucketSize, uInt aCacheSize) : DataManager (), itsDataManName ("SSM"), itsIosFile (0), itsNrRows (0), itsCache (0), itsFile (0), itsStringHandler (0), itsPersCacheSize (std::max(aCacheSize,uInt(2))), itsCacheSize (0), itsNrBuckets (0), itsNrIdxBuckets (0), itsFirstIdxBucket (-1), itsIdxBucketOffset (0), itsLastStringBucket (-1), itsIndexLength (0), itsFreeBucketsNr (0), itsFirstFreeBucket (-1), itsBucketSize (0), itsBucketRows (0), isDataChanged (False) { if (aBucketSize < 0) { itsBucketRows = -aBucketSize; } else if (aBucketSize == 0) { itsBucketRows = 32; } else { itsBucketSize = aBucketSize; } } SSMBase::SSMBase (const String& aDataManName, Int aBucketSize, uInt aCacheSize) : DataManager (), itsDataManName (aDataManName), itsIosFile (0), itsNrRows (0), itsCache (0), itsFile (0), itsStringHandler (0), itsPersCacheSize (std::max(aCacheSize,uInt(2))), itsCacheSize (0), itsNrBuckets (0), itsNrIdxBuckets (0), itsFirstIdxBucket (-1), itsIdxBucketOffset (0), itsLastStringBucket (-1), itsIndexLength (0), itsFreeBucketsNr (0), itsFirstFreeBucket (-1), itsBucketSize (0), itsBucketRows (0), isDataChanged (False) { if (aBucketSize < 0) { itsBucketRows = -aBucketSize; } else if (aBucketSize == 0) { itsBucketRows = 32; } else { itsBucketSize = aBucketSize; } } SSMBase::SSMBase (const String& aDataManName, const Record& spec) : DataManager (), itsDataManName (aDataManName), itsIosFile (0), itsNrRows (0), itsCache (0), itsFile (0), itsStringHandler (0), itsPersCacheSize (2), itsCacheSize (0), itsNrBuckets (0), itsNrIdxBuckets (0), itsFirstIdxBucket (-1), itsIdxBucketOffset (0), itsLastStringBucket (-1), itsIndexLength (0), itsFreeBucketsNr (0), itsFirstFreeBucket (-1), itsBucketSize (0), itsBucketRows (0), isDataChanged (False) { // Get nr of rows per bucket if defined. if (spec.isDefined ("BUCKETROWS")) { itsBucketRows = spec.asInt ("BUCKETROWS"); } // If no bucketrows, get bucketsize if defined. // Otherwise set bucketrows to default value. if (itsBucketRows == 0) { if (spec.isDefined ("BUCKETSIZE")) { itsBucketSize = spec.asInt ("BUCKETSIZE"); } if (itsBucketSize == 0) { itsBucketRows = 32; } } if (spec.isDefined ("PERSCACHESIZE")) { itsPersCacheSize = max(2, spec.asInt ("PERSCACHESIZE")); } } SSMBase::SSMBase (const SSMBase& that) : DataManager (), itsDataManName (that.itsDataManName), itsIosFile (0), itsNrRows (0), itsCache (0), itsFile (0), itsStringHandler (0), itsPersCacheSize (that.itsPersCacheSize), itsCacheSize (0), itsNrBuckets (0), itsNrIdxBuckets (0), itsFirstIdxBucket (-1), itsIdxBucketOffset (0), itsLastStringBucket (-1), itsIndexLength (0), itsFreeBucketsNr (0), itsFirstFreeBucket (-1), itsBucketSize (that.itsBucketSize), itsBucketRows (that.itsBucketRows), isDataChanged (False) {} SSMBase::~SSMBase() { for (uInt i=0; i(this)->getCache(); Record rec; rec.define ("MaxCacheSize", Int(itsCacheSize)); return rec; } void SSMBase::setProperties (const Record& rec) { if (rec.isDefined("MaxCacheSize")) { setCacheSize (rec.asInt("MaxCacheSize"), False); } } void SSMBase::clearCache() { if (itsCache != 0) { itsStringHandler->flush(); itsCache->clear(); } } void SSMBase::showBaseStatistics (ostream& anOs) const { anOs << "StandardStMan Base statistics:" << endl; anOs << "Nr of columns : " << ncolumn() << endl ; anOs << "Nr of rows in the columns : " << itsNrRows << endl; for (uInt i=0;ishowStatistics (anOs); anOs << endl; } } void SSMBase::showIndexStatistics (ostream & anOs) const { uInt aNrIdx=itsPtrIndex.nelements(); for (uInt i=0; i < aNrIdx; i++) { anOs << "StandardStMan index: " << i << " statistics:" << endl; itsPtrIndex[i]->showStatistics (anOs); anOs << endl; } } DataManagerColumn* SSMBase::makeScalarColumn (const String&, int aDataType, const String&) { //# Extend itsPtrColumn block if needed. if (ncolumn() >= itsPtrColumn.nelements()) { itsPtrColumn.resize (itsPtrColumn.nelements() + 32); } SSMColumn* aColumn = new SSMColumn (this, aDataType, ncolumn()); itsPtrColumn[ncolumn()] = aColumn; return aColumn; } DataManagerColumn* SSMBase::makeDirArrColumn (const String&, int aDataType, const String&) { //# Extend itsPtrColumn block if needed. if (ncolumn() >= itsPtrColumn.nelements()) { itsPtrColumn.resize (itsPtrColumn.nelements() + 32); } SSMColumn* aColumn = new SSMDirColumn (this, aDataType, ncolumn()); itsPtrColumn[ncolumn()] = aColumn; return aColumn; } DataManagerColumn* SSMBase::makeIndArrColumn (const String&, int aDataType, const String&) { //# Extend itsPtrColumn block if needed. if (ncolumn() >= itsPtrColumn.nelements()) { itsPtrColumn.resize (itsPtrColumn.nelements() + 32); } SSMColumn* aColumn; if (aDataType == TpString) { aColumn = new SSMIndStringColumn (this, aDataType, ncolumn()); } else { aColumn = new SSMIndColumn (this, aDataType, ncolumn()); } itsPtrColumn[ncolumn()] = aColumn; return aColumn; } DataManager* SSMBase::makeObject (const String& group, const Record& spec) { // This function is called when reading a table back. // Construct it with the default bucket size and cache size. return new SSMBase (group, spec); } void SSMBase::setCacheSize (uInt aCacheSize, Bool canExceedNrBuckets) { itsCacheSize = max(aCacheSize,2u); // Limit the cache size if needed. if (!canExceedNrBuckets && itsCacheSize > getCache().nBucket()) { itsCacheSize = itsCache->nBucket(); } if (itsCache != 0) { itsCache->resize (itsCacheSize); } } void SSMBase::makeCache() { if (itsCache == 0) { Bool forceFill= False; if (itsPtrIndex.nelements() == 0) { itsFile->open(); readHeader(); forceFill=True; } // Set cache size to persistent cache size if not set explicitly yet. if (itsCacheSize == 0) { itsCacheSize = itsPersCacheSize; } itsCache = new BucketCache (itsFile, 512, itsBucketSize, itsNrBuckets, itsCacheSize, this, SSMBase::readCallBack, SSMBase::writeCallBack, SSMBase::initCallBack, SSMBase::deleteCallBack); itsCache->resync (itsNrBuckets, itsFreeBucketsNr, itsFirstFreeBucket); if (forceFill) { readIndexBuckets(); } } } uInt SSMBase::getRowsPerBucket(uInt aColumn) const { return itsPtrIndex[itsColIndexMap[aColumn]]->getRowsPerBucket(); } uInt SSMBase::getNewBucket() { char* aBucketPtr = new char[itsBucketSize]; memset (aBucketPtr,0,itsBucketSize); // Get a new bucket number from bucketcache return getCache().addBucket(aBucketPtr); } void SSMBase::readHeader() { // Set at start of file. itsFile->seek(0); // Use the file given by the BucketFile object // Use a buffer size (512) equal to start of buckets in the file, // so the IO buffers in the different objects do not overlap. std::shared_ptr aFio = itsFile->makeFilebufIO (512); // It is stored in big or little endian canonical format. std::shared_ptr aTio; if (asBigEndian()) { aTio.reset (new CanonicalIO (aFio)); } else { aTio.reset (new LECanonicalIO (aFio)); } AipsIO anOs (aTio); uInt version = anOs.getstart("StandardStMan"); itsBucketRows = 0; itsIdxBucketOffset = 0; Bool bigEndian = True; if (version >= 3) { anOs >> bigEndian; } if (bigEndian != asBigEndian()) { throw DataManError("Endian flag in SSM mismatches the table flag"); } anOs >> itsBucketSize; // Size of the bucket anOs >> itsNrBuckets; // Initial Nr of Buckets anOs >> itsPersCacheSize; // Size of Persistent cache anOs >> itsFreeBucketsNr; // Nr of Free Buckets anOs >> itsFirstFreeBucket; // First Free Bucket nr anOs >> itsNrIdxBuckets; // Nr of Buckets needed 4 Index anOs >> itsFirstIdxBucket; // First indexBucket Number if (version >= 2) { anOs >> itsIdxBucketOffset; } anOs >> itsLastStringBucket; // Last StringBucket in use anOs >> itsIndexLength; // length of index uInt nrinx; anOs >> nrinx; // Nr of indices if (itsStringHandler == 0) { itsStringHandler = new SSMStringHandler(this); itsStringHandler->init(); } itsStringHandler->setLastStringBucket(itsLastStringBucket); anOs.getend(); anOs.close(); for (uInt i=0; i aMio; auto aMemBuf = std::make_shared(itsIndexLength); uInt aCLength = 2*CanonicalConversion::canonicalSize (&itsFirstIdxBucket); getCache(); // It is stored in big or little endian canonical format. if (asBigEndian()) { aMio.reset (new CanonicalIO (aMemBuf)); } else { aMio.reset (new LECanonicalIO (aMemBuf)); } AipsIO anMOs (aMio); Int aBucket = itsFirstIdxBucket; Int idxBucketSize = itsBucketSize - aCLength; Int aNr = itsIndexLength; char* aBucketPtr; for (uInt j=0; j< itsNrIdxBuckets; j++) { aBucketPtr = getBucket(aBucket); // First aCLength/2 bytes should be identical to next aCLength/2 bytes. // If not it might be an indicator that something went wrong // This can be used in the future Int aCheckNr; CanonicalConversion::toLocal (aCheckNr,aBucketPtr); CanonicalConversion::toLocal (aBucket,aBucketPtr+aCLength/2); if (aCheckNr != aBucket) { // Not used for now } // If offset is given, index fits in this single bucket from offset on. if (itsIdxBucketOffset > 0) { AlwaysAssert (itsIdxBucketOffset+itsIndexLength <= itsBucketSize && itsNrIdxBuckets == 1, AipsError); aMemBuf->write (aNr, aBucketPtr+itsIdxBucketOffset); } else if (aNr < idxBucketSize) { aMemBuf->write (aNr, aBucketPtr+aCLength); } else { aMemBuf->write (idxBucketSize, aBucketPtr+aCLength); } aNr-=idxBucketSize; } aMemBuf->seek(0); uInt aNrIdx=itsPtrIndex.nelements(); for (uInt i=0; i < aNrIdx; i++) { itsPtrIndex[i] = new SSMIndex(this); itsPtrIndex[i]->get(anMOs); } anMOs.close(); } void SSMBase::writeIndex() { std::shared_ptr aTio; std::shared_ptr aMio; auto aMemBuf = std::make_shared(); // Use the file given by the BucketFile object.. // Use a buffer size (512) equal to start of buckets in the file, // so the IO buffers in the different objects do not overlap. std::shared_ptr aFio = itsFile->makeFilebufIO (512); uInt aCLength = 2*CanonicalConversion::canonicalSize(&itsFirstIdxBucket); // Store it in big or little endian canonical format. if (asBigEndian()) { aMio.reset (new CanonicalIO (aMemBuf)); aTio.reset (new CanonicalIO (aFio)); } else { aMio.reset (new LECanonicalIO (aMemBuf)); aTio.reset (new LECanonicalIO (aFio)); } AipsIO anMOs (aMio); uInt aNrIdx = itsPtrIndex.nelements(); for (uInt i=0;iput(anMOs); } anMOs.close(); // Write total Mio in buckets. // Leave space for next bucket nr. const uChar* aMemPtr = aMemBuf->getBuffer(); uInt idxLength = aMemBuf->length(); uInt idxBucketSize = itsBucketSize-aCLength; uInt aNrBuckets = idxLength / idxBucketSize; uInt aRestSize = idxLength % idxBucketSize; if (aRestSize != 0) { aNrBuckets++; } else { aRestSize = idxBucketSize; } // If index is currently written in a single half of a bucket, // see if this fits in the other half. if (itsIdxBucketOffset > 0 && idxLength <= idxBucketSize/2) { if (itsIdxBucketOffset == aCLength) { itsIdxBucketOffset += idxBucketSize/2; } else { itsIdxBucketOffset = aCLength; } char* aBucketPtr = getBucket (itsFirstIdxBucket); memcpy (aBucketPtr+itsIdxBucketOffset, aMemPtr, idxLength); setBucketDirty(); } else { // One or more new buckets are needed to store the index. Int aNewBucket = -1; Int anOldBucket = -1; for (uInt i=aNrBuckets; i>0; i--) { aNewBucket = getNewBucket(); char* aBucketPtr = getBucket(aNewBucket); // Writing is done from the end to be able to fill in immediately // the nr of the next bucket (held in anOldBucket). CanonicalConversion::fromLocal (aBucketPtr, anOldBucket); CanonicalConversion::fromLocal (aBucketPtr+aCLength/2, anOldBucket); // Write rest of index as far as it fits. memcpy (aBucketPtr+aCLength, aMemPtr+((i-1)*idxBucketSize), aRestSize); setBucketDirty(); aRestSize = idxBucketSize; anOldBucket = aNewBucket; } // New Index is written, give old indexbuckets free, and save firstBucketNr Int aBucket = itsFirstIdxBucket; while (aBucket != -1) { char* aBucketPtr = getBucket(aBucket); CanonicalConversion::toLocal (aBucket, aBucketPtr+aCLength/2); itsCache->removeBucket(); } itsFirstIdxBucket = aNewBucket; // If the index fits in half a bucket, we might be able to use the other // half when writing the index the next time. // Set the index offset variable accordingly. if (idxLength <= idxBucketSize/2) { itsIdxBucketOffset = aCLength; } else { itsIdxBucketOffset = 0; } } itsNrIdxBuckets = aNrBuckets; AlwaysAssert ( itsStringHandler != 0, AipsError); itsLastStringBucket = itsStringHandler->lastStringBucket(); itsStringHandler->flush(); itsCache->flush(); aNrBuckets = getCache().nBucket(); itsFile->seek (0); AipsIO anOs (aTio); // Write a few items at the beginning of the file AipsIO anOs (aTio); // The endian switch is a new feature. So only put it if little endian // is used. In that way older software can read newer tables. if (asBigEndian()) { anOs.putstart("StandardStMan", 2); } else { anOs.putstart("StandardStMan", 3); anOs << asBigEndian(); } anOs << itsBucketSize; // Size of the bucket anOs << aNrBuckets; // Present number of buckets anOs << itsPersCacheSize; // Size of Persistent cache anOs << getCache().nFreeBucket(); // Nr of Free Buckets anOs << getCache().firstFreeBucket(); // First Free Bucket nr anOs << itsNrIdxBuckets; // Nr buckets needed for index anOs << itsFirstIdxBucket; // First Index bucket number anOs << itsIdxBucketOffset; // Offset of bucket if fitting anOs << itsLastStringBucket; // Last String bucket in use anOs << idxLength; // length of index anOs << uInt(itsPtrIndex.nelements());// Nr of indices anOs.putend(); anOs.close(); aFio->flush(); // Synchronize to make sure it gets written to disk. // This is needed for NFS-files under Linux (to resolve defect 2752). itsFile->fsync(); } void SSMBase::setBucketDirty() { itsCache->setDirty(); isDataChanged = True; } //# The storage manager can add rows. Bool SSMBase::canAddRow() const { return True; } //# The storage manager can delete rows. Bool SSMBase::canRemoveRow() const { return True; } //# The storage manager cannot add columns (not yet). Bool SSMBase::canAddColumn() const { return True; } //# The storage manager cannot delete columns (not yet). Bool SSMBase::canRemoveColumn() const { return True; } void SSMBase::addRow64 (rownr_t aNrRows) { //make sure cache is available and filled (I need itsPtrIndex) getCache(); uInt aNrIdx = itsPtrIndex.nelements(); for (uInt i=0; i< aNrIdx; i++) { itsPtrIndex[i]->addRow(aNrRows); } uInt aNrCol = ncolumn(); for (uInt j=0; j< aNrCol; j++) { itsPtrColumn[j]->addRow(itsNrRows+aNrRows,itsNrRows,False); } itsNrRows+=aNrRows; isDataChanged = True; } void SSMBase::removeRow64 (rownr_t aRowNr) { uInt aNrCol = ncolumn(); for (uInt j=0; j< aNrCol; j++) { itsPtrColumn[j]->deleteRow(aRowNr); } uInt aNrIdx=itsPtrIndex.nelements(); for (uInt i=0; i< aNrIdx; i++) { Int anEmptyBucket=itsPtrIndex[i]->deleteRow(aRowNr); // remove bucket if empty; if (anEmptyBucket >= 0) { removeBucket(anEmptyBucket); } } itsNrRows--; if (itsNrRows == 0) { for (uInt i=0; iremoveBucket(); } itsFirstIdxBucket = -1; itsIdxBucketOffset = 0; itsNrIdxBuckets = 0; create64(itsNrRows); // recreate(); } isDataChanged = True; } void SSMBase::addColumn (DataManagerColumn* aColumn) { // Be sure cache is filled getCache(); SSMColumn* aSSMC = dynamic_cast (aColumn); AlwaysAssert ( aSSMC != 0, AipsError); aSSMC->doCreate(0); Int aSearchLength = aSSMC->getExternalSizeBits(); Int anOffset=-1; Int aBestFit=-1; uInt saveIndex=0; Int saveOffset=-1; // Try if there is freespace available where this column can fit (best fit) // For now we assume that a best fit will be : // 1) exact fit // 2) any fit for (uInt i=0; igetFree(anOffset,aSearchLength); if (aFoundFit == 0) { // Perfect Fit Found aBestFit = aSearchLength; saveIndex=i; saveOffset=anOffset; } else if (aFoundFit > 0) { if (aFoundFit < aBestFit || aBestFit == -1) { aBestFit = aFoundFit; saveIndex=i; saveOffset=anOffset; } } } // If fit found use this space, else make new column uInt nCol = aSSMC->getColNr(); itsColumnOffset.resize(ncolumn(),True); itsColIndexMap.resize(ncolumn(),True); if (aBestFit != -1) { itsPtrIndex[saveIndex]->addColumn(saveOffset,aSearchLength); itsColIndexMap[nCol]=saveIndex; itsColumnOffset[nCol]=saveOffset; } else { // calculate rowsperbucket for new index AlwaysAssert (aSearchLength != 0, AipsError); uInt rowsPerBucket = itsBucketSize*8 / aSearchLength; if (rowsPerBucket < 1) { // The BucketSize is too small to contain data. throw DataManError ("StandardStMan::addColumn bucketsize too small" " for adding column " + aColumn->columnName()); } uInt nrIdx=itsPtrIndex.nelements(); itsPtrIndex.resize(nrIdx+1,True); itsPtrIndex[nrIdx] = new SSMIndex(this,rowsPerBucket); uInt aSize =(rowsPerBucket*aSSMC->getExternalSizeBits() + 7) / 8; itsPtrIndex[nrIdx]->setNrColumns(1,aSize); itsPtrIndex[nrIdx]->addRow(itsNrRows); itsColIndexMap[nCol]=nrIdx; itsColumnOffset[nCol]=0; } aSSMC->addRow(itsNrRows,0,aBestFit != -1); isDataChanged = True; } void SSMBase::removeBucket (uInt aBucketNr) { getCache().getBucket(aBucketNr); getCache().removeBucket(); } char* SSMBase::getBucket (uInt aBucketNr) { return itsCache->getBucket(aBucketNr); } void SSMBase::removeColumn (DataManagerColumn* aColumn) { getCache(); SSMColumn* aSSMC = dynamic_cast (aColumn); AlwaysAssert ( aSSMC != 0, AipsError); uInt aNrCol = ncolumn(); uInt aColNr = aSSMC->getColNr(); Bool isFound=False; for (uInt i=0; igetColNr() == aColNr) { isFound=True; itsPtrColumn[i]->removeColumn(); // free up space Int aNrColumns = itsPtrIndex[itsColIndexMap[i]]->removeColumn (itsColumnOffset[i], itsPtrColumn[i]->getExternalSizeBits()); // if no columns left,buckets can be released if (aNrColumns == 0) { Vector aBucketList=itsPtrIndex[itsColIndexMap[i]]->getBuckets(); for (uInt k=0; k then i should be 1 less. for (uInt k=0; k itsColIndexMap[i]) { itsColIndexMap[k] = itsColIndexMap[k]-1; } } } delete itsPtrColumn[i]; for (uInt j=i;jsetColNr(itsPtrColumn[j]->getColNr()-1); // move the offsets of the remaining columns to the left also itsColumnOffset[j] = itsColumnOffset[j+1]; itsColIndexMap[j] = itsColIndexMap[j+1]; itsPtrColumn[j] = itsPtrColumn[j+1]; } decrementNcolumn(); isDataChanged = True; } } } char* SSMBase::readCallBack (void* anOwner, const char* aBucketStorage) { uInt aSize = static_cast(anOwner)->getBucketSize(); char* aBucket = new char [aSize]; memcpy (aBucket, aBucketStorage, aSize); return aBucket; } void SSMBase::writeCallBack (void* anOwner, char* aBucketStorage, const char* aBucket) { uInt aSize = static_cast(anOwner)->getBucketSize(); memcpy (aBucketStorage, aBucket, aSize); } void SSMBase::deleteCallBack (void*, char* aBucket) { delete [] aBucket; } char* SSMBase::initCallBack (void* anOwner) { uInt aSize = static_cast(anOwner)->getBucketSize(); char* aBucket = new char [aSize]; memset (aBucket,0,aSize); return aBucket; } char* SSMBase::find(rownr_t aRowNr, uInt aColNr, rownr_t& aStartRow, rownr_t& anEndRow, const String& colName) { // Make sure that cache is available and filled. getCache(); SSMIndex* anIndexPtr = itsPtrIndex[itsColIndexMap[aColNr]]; uInt aBucketNr; anIndexPtr->find(aRowNr,aBucketNr,aStartRow,anEndRow, colName); char* aPtr = getBucket(aBucketNr); return aPtr + itsColumnOffset[aColNr]; } void SSMBase::recreate() { delete itsCache; itsCache = 0; delete itsFile; itsFile = 0; delete itsIosFile; itsIosFile = 0; delete itsStringHandler; itsStringHandler = 0; itsNrBuckets = 0; itsFirstIdxBucket = -1; itsFreeBucketsNr = 0; itsFirstFreeBucket = -1; itsFile = new BucketFile (fileName(), 0, False, multiFile()); makeCache(); // Let the Index recreate itself when needed uInt aNrIdx=itsPtrIndex.nelements(); for (uInt i=0; irecreate(); } itsStringHandler = new SSMStringHandler(this); itsStringHandler->init(); // Let the column objects create something (if needed) uInt aNrCol = ncolumn(); for (uInt i=0; idoCreate(itsNrRows); } isDataChanged = True; } Bool SSMBase::hasMultiFileSupport() const { return True; } Bool SSMBase::flush (AipsIO& ios, Bool doFsync) { //# Check if anything has changed. Bool changed = False; if (itsStringHandler) { itsStringHandler->flush(); } if (itsCache) { itsCache->flush(); } if (isDataChanged) { writeIndex(); if (doFsync) { itsFile->fsync(); } changed = True; isDataChanged = False; } if (itsIosFile) { itsIosFile->flush(doFsync); } ios.putstart ("SSM", 2); ios << itsDataManName; putBlock (ios, itsColumnOffset, itsColumnOffset.nelements()); putBlock (ios, itsColIndexMap, itsColIndexMap.nelements()); ios.putend(); return changed; } rownr_t SSMBase::resync64 (rownr_t aNrRows) { itsNrRows = aNrRows; if (itsPtrIndex.nelements() != 0) { readHeader(); } if (itsCache != 0) { itsCache->resync (itsNrBuckets, itsFreeBucketsNr, itsFirstFreeBucket); } if (itsPtrIndex.nelements() != 0) { readIndexBuckets(); } if (itsStringHandler != 0) { itsStringHandler->resync(); } uInt aNrCol = ncolumn(); if (itsIosFile != 0) { itsIosFile->resync(); } for (uInt i=0; iresync (itsNrRows); } return itsNrRows; } void SSMBase::create64 (rownr_t aNrRows) { init(); recreate(); itsNrRows = 0; addRow64 (aNrRows); } rownr_t SSMBase::open64 (rownr_t aRowNr, AipsIO& ios) { itsNrRows = aRowNr; ios.getstart ("SSM"); ios >> itsDataManName; getBlock (ios,itsColumnOffset); getBlock (ios,itsColIndexMap); ios.getend(); itsFile = new BucketFile (fileName(), table().isWritable(), 0, False, multiFile()); AlwaysAssert (itsFile != 0, AipsError); // Let the column object initialize themselves (if needed) uInt aNrCol = ncolumn(); for (uInt i=0; igetFile(itsNrRows); } return itsNrRows; } StManArrayFile* SSMBase::openArrayFile (ByteIO::OpenOption anOpt) { if (itsIosFile == 0) { itsIosFile = new StManArrayFile (fileName() + 'i', anOpt, 0, asBigEndian(), 0, multiFile()); } return itsIosFile; } void SSMBase::reopenRW() { if (itsFile != 0) { itsFile->setRW(); } if (itsIosFile != 0) { itsIosFile->reopenRW(); } } void SSMBase::deleteManager() { delete itsIosFile; itsIosFile = 0; // Clear cache without flushing. if (itsCache != 0) { itsCache->clear (0, False); } if (itsFile != 0) { itsFile->remove(); delete itsFile; itsFile = 0; } } void SSMBase::init() { // Size the blocks as needed. uInt nrCol = ncolumn(); itsColumnOffset.resize (nrCol, True); itsColIndexMap.resize (nrCol, True); itsColIndexMap = 0; // Set the bucket size and get nr of rows per bucket. // If an advised nr of rows per bucket was given and the actual // nr is smaller, adjust it to fill up the last bucket. uInt rowsPerBucket = setBucketSize(); if (itsBucketRows > 0 && itsBucketRows > rowsPerBucket) { uInt nbuckets = (itsBucketRows + rowsPerBucket - 1) / rowsPerBucket; itsBucketRows = (itsBucketRows + nbuckets - 1) / nbuckets; rowsPerBucket = setBucketSize(); } // Determine the offset of each column. // Note that the data of a column are consecutive per bucket. uInt aTotalSize = 0; for (uInt i=0; igetExternalSizeBits() + 7) / 8; } // All columns are in the same bucket list, thus only one SSMIndex needed. itsPtrIndex.resize (1, True); itsPtrIndex[0] = new SSMIndex(this, rowsPerBucket); itsPtrIndex[0]->setNrColumns (nrCol, aTotalSize); } uInt SSMBase::setBucketSize() { // Find nr of columns and possibly advised nr of rows per bucket. uInt nrCol = ncolumn(); uInt advBucketRows = itsBucketRows; // Finding the nr of rows fitting in the bucket is a bit hard, because // Bool values are stored as bits. Therefore we have to iterate. // First find the nr of full bytes needed (ignoring possible remainders). uInt aTotalSize = 0; for (uInt i=0; igetExternalSizeBytes(); } // Get first guess for nr of rows per bucket. if (itsBucketSize < 128) { itsBucketSize = 128; } uInt rowsPerBucket = advBucketRows; if (advBucketRows == 0) { if (itsBucketSize < 128) { itsBucketSize = 128; } rowsPerBucket = itsBucketSize/aTotalSize; } // Now refine it by determining how big bucket is when using one more row. while (True) { uInt aThisSize = 0; uInt aNextSize = 0; for (uInt i=0; igetExternalSizeBits() + 7) / 8; aNextSize += ((rowsPerBucket+1) * itsPtrColumn[i]->getExternalSizeBits() + 7) / 8; } // If advised #rows/bucket given, get bucket size. if (advBucketRows > 0) { itsBucketSize = min (32768u, max(128u, aThisSize)); if (itsBucketSize == aThisSize) { break; } // Exceeding minimum or maximum, so calculate #rows/bucket. rowsPerBucket = itsBucketSize/aTotalSize; advBucketRows = 0; } else { // Stop if one more row does not fit. if (aNextSize > itsBucketSize) { break; } rowsPerBucket++; } } if (rowsPerBucket < 1) { // The bucket size is too small to contain all columns, so adjust it. itsBucketSize = aTotalSize; rowsPerBucket = 1; } AlwaysAssert (itsBucketSize >= 128, AipsError); return rowsPerBucket; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/SSMBase.h000066400000000000000000000411461476623553700175170ustar00rootroot00000000000000//# SSMBase.h: Base class of the Standard Storage Manager //# Copyright (C) 2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_SSMBASE_H #define TABLES_SSMBASE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations class BucketCache; class BucketFile; class StManArrayFile; class SSMIndex; class SSMColumn; class SSMStringHandler; // // Base class of the Standard Storage Manager // // // // // //# Classes you should understand before using this one. //
      • StandardStMan //
      • SSMColumn // // // SSMBase is the base class of the Standard Storage Manager. // // // The global principles of this class are described in // StandardStMan. //

        // The Standard Storage Manager divides the data file in equally sized // chunks called buckets. There are 3 types of buckets: //

          //
        • Data buckets containing the fixed length data (scalars and // direct arrays of data type Int, Float, Bool, etc.). // For variable shaped data (strings and indirect arrays) they // contain references to the actual data position in the // string buckets or in an external file. //
        • String buckets containing strings and array of strings. //
        • Index buckets containing the index info for the data buckets. //
        // Bucket access is handled by class // BucketCache. // It also keeps a list of free buckets. A bucket is freed when it is // not needed anymore (e.g. all data from it are deleted). //

        // Data buckets form the main part of the SSM. The data can be viewed as // a few streams of buckets, where each stream contains the data of // a given number of columns. Each stream has an // SSMIndex object describing the // number of rows stored in each data bucket of the stream. // The SSM starts with a single bucket stream (holding all columns), // but when columns are added, new bucket streams might be created. //

        // For example, we have an SSM with a bucket size of 100 bytes. // There are 5 Int columns (A,B,C,D,E) each taking 4 bytes per row. // Column A, B, C, and D are stored in bucket stream 1, while column // E is stored in bucket stream 2. So in stream 1 each bucket can hold // 6 rows, while in stream 2 each bucket can hold 25 rows. // For a 100 row table it will result in 17+4 data buckets. //

        // A few classes collaborate to make it work: //

          //
        • Each bucket stream has an SSMIndex // object to map row number to bucket number. // Note that in principle each bucket in a stream contains the same // number of rows. However, when a row is deleted it is removed // from its bucket shifting the remainder to the left. Data in the // next buckets is not shifted, so that bucket has now one row less. //
        • For each column SSMBase knows to which bucket stream it belongs // and at which offset the column starts in a bucket. // Note that column data in a bucket are adjacent, which is done // to make it easier to use the // ColumnCache object in SSMColumn // and to be able to efficiently store Bool values as bits. //
        • Each column has an SSMColumn // object knowing how many bits each data cell takes in a bucket. // The SSMColumn objects handle all access to data in the columns // (using SSMBase and SSMIndex). //
        //

        // String buckets are used by class // SSMStringHandler to // store scalar strings and fixed and variable shaped arrays of strings. // The bucketnr, offset, and length of such string (arrays) are stored // in the data buckets. //
        // Indirect arrays of other data types are also stored indirectly // and their offset is stored in the data buckets. Such arrays are // handled by class StIndArray // which uses an extra file to store the arrays. //

        // Index buckets are used by SSMBase to make the SSMIndex data persistent. // It uses alternately 2 sets of index buckets. In that way there is // always an index availanle in case the system crashes. // If possible 2 halfs of a single bucket are used alternately, otherwise // separate buckets are used. // // // The public interface of SSMBase is quite large, because the other // internal SSM classes need these functions. To have a class with a // minimal interface for the normal user, class StandardStMan // is derived from it. //
        StandardStMan needs an isA- instead of hasA-relation to be // able to bind columns to it in class // SetupNewTable. //
        // //# A List of bugs, limitations, extensions or planned refinements. //

      • Remove AipsIO argument from open and close. //
      • When only 1 bucket in use addcolumn can check if there's enough // room to fit the new column (so rearange the bucket) in the free // row space. // class SSMBase: public DataManager { public: // Create a Standard storage manager with default name SSM. explicit SSMBase (Int aBucketSize=0, uInt aCacheSize=1); // Create a Standard storage manager with the given name. explicit SSMBase (const String& aDataManName, Int aBucketSize=0, uInt aCacheSize=1); // Create a Standard storage manager with the given name. // The specifications are part of the record (as created by dataManagerSpec). SSMBase (const String& aDataManName, const Record& spec); ~SSMBase(); // Clone this object. // It does not clone SSMColumn objects possibly used. // The caller has to delete the newly created object. virtual DataManager* clone() const; // Get the type name of the data manager (i.e. StandardStMan). virtual String dataManagerType() const; // Get the name given to the storage manager (in the constructor). virtual String dataManagerName() const; // Record a record containing data manager specifications. virtual Record dataManagerSpec() const; // Get data manager properties that can be modified. // It is only ActualCacheSize (the actual cache size in buckets). // It is a subset of the data manager specification. virtual Record getProperties() const; // Modify data manager properties. // Only MaxCacheSize can be used. It is similar to function setCacheSize // with canExceedNrBuckets=False. virtual void setProperties (const Record& spec); // Get the version of the class. uInt getVersion() const; // Set the cache size (in buckets). // If canExceedNrBuckets=True, the given cache size can be // larger than the nr of buckets in the file. In this way the cache can // be made large enough for a future file extension. // Otherwise, it is limited to the actual number of buckets. This is useful // if one wants the entire file to be cached. void setCacheSize (uInt aCacheSize, Bool canExceedNrBuckets=True); // Get the current cache size (in buckets). uInt getCacheSize() const; // Clear the cache used by this storage manager. // It will flush the cache as needed and remove all buckets from it. void clearCache(); // Show the statistics of all caches used. virtual void showCacheStatistics (ostream& anOs) const; // Show statistics of all indices used. void showIndexStatistics (ostream & anOs) const; // Show statistics of the Base offsets/index etc. void showBaseStatistics (ostream & anOs) const; // Get the bucket size. uInt getBucketSize() const; // Get the number of rows in this storage manager. rownr_t getNRow() const; // The storage manager can add rows. virtual Bool canAddRow() const; // The storage manager can delete rows. virtual Bool canRemoveRow() const; // The storage manager can add columns. virtual Bool canAddColumn() const; // The storage manager can delete columns. virtual Bool canRemoveColumn() const; // Make the object from the type name string. // This function gets registered in the DataManager "constructor" map. // The caller has to delete the object. static DataManager* makeObject (const String& aDataManType, const Record& spec); // Get access to the given column. SSMColumn& getColumn (uInt aColNr); // Get access to the given Index. SSMIndex& getIndex (uInt anIdxNr); // Make the current bucket in the cache dirty (i.e. something has been // changed in it and it needs to be written when removed from the cache). // (used by SSMColumn::putValue). void setBucketDirty(); // Open (if needed) the file for indirect arrays with the given mode. // Return a pointer to the object. StManArrayFile* openArrayFile (ByteIO::OpenOption anOpt); // Find the bucket containing the column and row and return the pointer // to the beginning of the column data in that bucket. // It also fills in the start and end row for the column data. char* find (rownr_t aRowNr, uInt aColNr, rownr_t& aStartRow, rownr_t& anEndRow, const String& colName); // Add a new bucket and get its bucket number. uInt getNewBucket(); // Read the bucket (if needed) and return the pointer to it. char* getBucket (uInt aBucketNr); // Remove a bucket from the bucket cache. void removeBucket (uInt aBucketNr); // Get rows per bucket for the given column. uInt getRowsPerBucket (uInt aColumn) const; // Return a pointer to the (one and only) StringHandler object. SSMStringHandler* getStringHandler(); // // Callbacks for BucketCache access. static char* readCallBack (void* anOwner, const char* aBucketStorage); static void writeCallBack (void* anOwner, char* aBucketStorage, const char* aBucket); static void deleteCallBack (void*, char* aBucket); static char* initCallBack (void* anOwner); // private: // Copy constructor (only meant for clone function). SSMBase (const SSMBase& that); // Assignment cannot be used. SSMBase& operator= (const SSMBase& that); // (Re)create the index, file, and cache object. // It is used when all rows are deleted from the table. void recreate(); // The data manager supports use of MultiFile. virtual Bool hasMultiFileSupport() const; // Flush and optionally fsync the data. // It returns a True status if it had to flush (i.e. if data have changed). virtual Bool flush (AipsIO&, Bool doFsync); // Let the storage manager create files as needed for a new table. // This allows a column with an indirect array to create its file. virtual void create64 (rownr_t aNrRows); // Open the storage manager file for an existing table, read in // the data, and let the SSMColumn objects read their data. virtual rownr_t open64 (rownr_t aRowNr, AipsIO&); // Resync the storage manager with the new file contents. // This is done by clearing the cache. virtual rownr_t resync64 (rownr_t aRowNr); // Reopen the storage manager files for read/write. virtual void reopenRW(); // The data manager will be deleted (because all its columns are // requested to be deleted). // So clean up the things needed (e.g. delete files). virtual void deleteManager(); // Let the storage manager initialize itself (upon creation). // It determines the bucket size and fills the index. void init(); // Determine and set the bucket size. // It returns the number of rows per bucket. uInt setBucketSize(); // Get the number of indices in use. uInt getNrIndices() const; // Add rows to the storage manager. // Per column it extends number of rows. virtual void addRow64 (rownr_t aNrRows); // Delete a row from all columns. virtual void removeRow64 (rownr_t aRowNr); // Do the final addition of a column. virtual void addColumn (DataManagerColumn*); // Remove a column from the data file. virtual void removeColumn (DataManagerColumn*); // Create a column in the storage manager on behalf of a table column. // The caller has to delete the newly created object. // // Create a scalar column. virtual DataManagerColumn* makeScalarColumn (const String& aName, int aDataType, const String& aDataTypeID); // Create a direct array column. virtual DataManagerColumn* makeDirArrColumn (const String& aName, int aDataType, const String& aDataTypeID); // Create an indirect array column. virtual DataManagerColumn* makeIndArrColumn (const String& aName, int aDataType, const String& aDataTypeID); // // Get the cache object. // This will construct the cache object if not present yet. // The cache object will be deleted by the destructor. BucketCache& getCache(); // Construct the cache object (if not constructed yet). void makeCache(); // Read the header. void readHeader(); // Read the index from its buckets. void readIndexBuckets(); // Write the header and the indices. void writeIndex(); //# Declare member variables. // Name of data manager. String itsDataManName; // The file containing the indirect arrays. StManArrayFile* itsIosFile; // The number of rows in the columns. rownr_t itsNrRows; // Column offset Block itsColumnOffset; // Row Index ID containing all the columns in a bucket Block itsColIndexMap; // Will contain all indices PtrBlock itsPtrIndex; // The cache with the SSM buckets. BucketCache* itsCache; // The file containing all data. BucketFile* itsFile; // String handler class SSMStringHandler* itsStringHandler; // The persistent cache size. uInt itsPersCacheSize; // The actual cache size. uInt itsCacheSize; // The initial number of buckets in the cache. uInt itsNrBuckets; // Nr of buckets needed for index. uInt itsNrIdxBuckets; // Number of the first index bucket Int itsFirstIdxBucket; // Offset of index in first bucket. // If >0, the index fits in a single bucket. uInt itsIdxBucketOffset; // Number of the first String Bucket Int itsLastStringBucket; // length of index memoryblock uInt itsIndexLength; // The nr of free buckets. uInt itsFreeBucketsNr; // The first free bucket. Int itsFirstFreeBucket; // The bucket size. uInt itsBucketSize; uInt itsBucketRows; // The assembly of all columns. PtrBlock itsPtrColumn; // Has the data changed since the last flush? Bool isDataChanged; }; inline uInt SSMBase::getNrIndices() const { return itsPtrIndex.nelements(); } inline uInt SSMBase::getCacheSize() const { return itsCacheSize; } inline rownr_t SSMBase::getNRow() const { return itsNrRows; } inline uInt SSMBase::getBucketSize() const { return itsBucketSize; } inline BucketCache& SSMBase::getCache() { if (itsCache == 0) { makeCache(); } return *itsCache; } inline SSMColumn& SSMBase::getColumn (uInt aColNr) { return *(itsPtrColumn[aColNr]); } inline SSMIndex& SSMBase::getIndex (uInt anIdxNr) { return *(itsPtrIndex[anIdxNr]); } inline SSMStringHandler* SSMBase::getStringHandler() { return itsStringHandler; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/SSMColumn.cc000066400000000000000000000450571476623553700202450ustar00rootroot00000000000000//# SSMColumn.cc: The Column of the Standard Storage Manager //# Copyright (C) 2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN SSMColumn::SSMColumn (SSMBase* aParent, int aDataType, uInt aColNr) : StManColumnBase(aDataType), itsSSMPtr (aParent), itsExternalSizeBytes(0), itsExternalSizeBits (0), itsColNr (aColNr), itsMaxLen (0), itsNrElem (1), itsNrCopy (0), itsData (0) { init(); } SSMColumn::~SSMColumn() { delete [] static_cast(itsData); } void SSMColumn::setShapeColumn (const IPosition& aShape) { itsNrElem = aShape.product(); itsShape = aShape; init(); } void SSMColumn::setMaxLength (uInt maxLength) { itsMaxLen = maxLength; init(); } uInt SSMColumn::ndim (rownr_t) { return itsShape.nelements(); } IPosition SSMColumn::shape (rownr_t) { return itsShape; } void SSMColumn::doCreate(rownr_t) { } void SSMColumn::getFile(rownr_t) { } void SSMColumn::addRow (rownr_t aNewNrRows, rownr_t, Bool doInit) { if (doInit && dataType() == TpString) { rownr_t aRowNr=0; rownr_t aNrRows=aNewNrRows; while (aNrRows > 0) { rownr_t aStartRow; rownr_t anEndRow; char* aValPtr; aValPtr = itsSSMPtr->find (aRowNr, itsColNr, aStartRow, anEndRow, columnName()); aRowNr = anEndRow+1; rownr_t aNr = anEndRow-aStartRow+1; aNrRows -= aNr; memset(aValPtr, 0, aNr * itsExternalSizeBytes); itsSSMPtr->setBucketDirty(); } } } void SSMColumn::deleteRow(rownr_t aRowNr) { char* aValue; rownr_t aSRow; rownr_t anERow; int aDT = dataType(); if (aDT == TpString && itsMaxLen == 0) { Int buf[3]; getRowValue(buf, aRowNr); if (buf[2] > 8 ) { itsSSMPtr->getStringHandler()->remove(buf[0], buf[1], buf[2]); aValue = itsSSMPtr->find (aRowNr, itsColNr, aSRow, anERow, columnName()); shiftRows(aValue,aRowNr,aSRow,anERow); itsSSMPtr->setBucketDirty(); return; } } aValue = itsSSMPtr->find (aRowNr, itsColNr, aSRow, anERow, columnName()); // For bools be sure that cache is actual Bool isBool = (aDT == TpBool); if (isBool && aRowNr < anERow) { Bool aVal; getBool(aRowNr,&aVal); } // first check if aRowNr is in cache, if not, fill cache // In both cases remove row from cache rownr_t aStartRow = columnCache().start(); rownr_t anEndRow = columnCache().end(); // Remove from cache if needed if (aRowNr >= aStartRow && aRowNr <= anEndRow) { // remove the row in itsData if not last if (aRowNr < anEndRow) { char* aToPtr = getDataPtr() + (aRowNr-aStartRow) * itsLocalSize; char* aFromPtr = getDataPtr() + (aRowNr+1-aStartRow) * itsLocalSize; // decrement anEndrow uInt64 aLength = (anEndRow - aRowNr) * itsLocalSize; memmove(aToPtr,aFromPtr,aLength); } // Fill cache again with actual itsData. if (aStartRow == anEndRow) { columnCache().invalidate(); } else { columnCache().set (aStartRow, anEndRow-1, getDataPtr()); } } if (aRowNr < anERow) { // remove from bucket // first check if type is a bool if (isBool) { itsWriteFunc (aValue,itsData, (anERow-aSRow) * itsNrCopy); } else { shiftRows(aValue,aRowNr,aSRow,anERow); } itsSSMPtr->setBucketDirty(); } } void SSMColumn::shiftRows(char* aValue, rownr_t aRowNr, rownr_t aSRow, rownr_t anERow) { // Shift from aRrowNr on 1 to the left. char* aToPtr = aValue + (aRowNr-aSRow) * itsExternalSizeBytes; char* aFromPtr = aToPtr + itsExternalSizeBytes; uInt64 aLength = (anERow - aRowNr) * itsExternalSizeBytes; memmove(aToPtr,aFromPtr,aLength); // Clear last entry (so a putString on a new row finds zeroes). memset (aToPtr + aLength, 0, itsExternalSizeBytes); } void SSMColumn::getBool (rownr_t aRowNr, Bool* aValue) { getValue(aRowNr); *aValue = static_cast(itsData)[aRowNr-columnCache().start()]; } void SSMColumn::getuChar (rownr_t aRowNr, uChar* aValue) { getValue(aRowNr); *aValue = static_cast(itsData)[aRowNr-columnCache().start()]; } void SSMColumn::getShort (rownr_t aRowNr, Short* aValue) { getValue(aRowNr); *aValue = static_cast(itsData)[aRowNr-columnCache().start()]; } void SSMColumn::getuShort (rownr_t aRowNr, uShort* aValue) { getValue(aRowNr); *aValue = static_cast(itsData)[aRowNr-columnCache().start()]; } void SSMColumn::getInt (rownr_t aRowNr, Int* aValue) { getValue(aRowNr); *aValue = static_cast(itsData)[aRowNr-columnCache().start()]; } void SSMColumn::getuInt (rownr_t aRowNr, uInt* aValue) { getValue(aRowNr); *aValue = static_cast(itsData)[aRowNr-columnCache().start()]; } void SSMColumn::getInt64 (rownr_t aRowNr, Int64* aValue) { getValue(aRowNr); *aValue = static_cast(itsData)[aRowNr-columnCache().start()]; } void SSMColumn::getfloat (rownr_t aRowNr, float* aValue) { getValue(aRowNr); *aValue = static_cast(itsData)[aRowNr-columnCache().start()]; } void SSMColumn::getdouble (rownr_t aRowNr, double* aValue) { getValue(aRowNr); *aValue = static_cast(itsData)[aRowNr-columnCache().start()]; } void SSMColumn::getComplex (rownr_t aRowNr, Complex* aValue) { getValue(aRowNr); *aValue = static_cast(itsData)[aRowNr-columnCache().start()]; } void SSMColumn::getDComplex (rownr_t aRowNr,DComplex* aValue) { getValue(aRowNr); *aValue = static_cast(itsData)[aRowNr-columnCache().start()]; } void SSMColumn::getString (rownr_t aRowNr, String* aValue) { if (itsMaxLen > 0) { // Allocate the maximum number of characters needed // The +1 is to correct for the incorrect use of the chars() function // Should be changed to use real Char* aValue->alloc(itsMaxLen+1); char* sp = const_cast(aValue->chars()); rownr_t aStartRow; rownr_t anEndRow; char* buf = itsSSMPtr->find (aRowNr, itsColNr, aStartRow, anEndRow, columnName()); itsReadFunc (sp, buf+(aRowNr-aStartRow)*itsExternalSizeBytes, itsNrCopy); // Append a trailing zero (in case needed). // Note that if shorter, the string already contains a trailing zero. // Set the string to its actual length. sp[itsMaxLen] = '\0'; uInt len = 0; while (*sp++ != '\0') { len++; } aValue->alloc(len); } else { // The string is probably stored indirectly in a string bucket. // Get bucketnr, offset, and length. Int buf[3]; char* strbuf = getRowValue(buf, aRowNr); // if length <= 8 chars the string can be found in de data bucket // instead of the string bucket. if (buf[2] <= 8) { aValue->resize (buf[2]); // resize storage which adds trailing 0 char* sp = &((*aValue)[0]); // get actual string memcpy (sp, strbuf, buf[2]); #ifdef USE_OLD_STRING sp[buf[2]] = '\0'; #endif } else { itsSSMPtr->getStringHandler()->get(*aValue, buf[0], buf[1], buf[2]); } } } Char* SSMColumn::getRowValue(Int* data, rownr_t aRowNr) { rownr_t aStartRow; rownr_t anEndRow; char* aValue; aValue = itsSSMPtr->find (aRowNr, itsColNr, aStartRow, anEndRow, columnName()); itsReadFunc (data, aValue+(aRowNr-aStartRow)*itsExternalSizeBytes, itsNrCopy); return aValue+(aRowNr-aStartRow)*itsExternalSizeBytes; } void SSMColumn::getValue(rownr_t aRowNr) { if (aRowNr < columnCache().start() || aRowNr > columnCache().end()) { rownr_t aStartRow; rownr_t anEndRow; char* aValue; aValue = itsSSMPtr->find (aRowNr, itsColNr, aStartRow, anEndRow, columnName()); itsReadFunc (getDataPtr(), aValue, (anEndRow-aStartRow+1) * itsNrCopy); columnCache().set (aStartRow, anEndRow, getDataPtr()); } } void SSMColumn::putBool (rownr_t aRowNr, const Bool* aValue) { rownr_t aStartRow; rownr_t anEndRow; char* aDummy; aDummy = itsSSMPtr->find (aRowNr, itsColNr, aStartRow, anEndRow, columnName()); uInt64 anOff = aRowNr-aStartRow; Conversion::boolToBit(aDummy+(anOff/8), aValue,anOff%8,1); itsSSMPtr->setBucketDirty(); if (aRowNr >= columnCache().start() && aRowNr <= columnCache().end()) { getDataPtr()[aRowNr-columnCache().start()] = *aValue; } } void SSMColumn::putuChar (rownr_t aRowNr, const uChar* aValue) { putValue(aRowNr,aValue); if (aRowNr >= columnCache().start() && aRowNr <= columnCache().end()) { static_cast(itsData)[aRowNr-columnCache().start()] = *aValue; } } void SSMColumn::putShort (rownr_t aRowNr, const Short* aValue) { putValue(aRowNr,aValue); if (aRowNr >= columnCache().start() && aRowNr <= columnCache().end()) { static_cast(itsData)[aRowNr-columnCache().start()] = *aValue; } } void SSMColumn::putuShort (rownr_t aRowNr, const uShort* aValue) { putValue(aRowNr,aValue); if (aRowNr >= columnCache().start() && aRowNr <= columnCache().end()) { static_cast(itsData)[aRowNr-columnCache().start()] = *aValue; } } void SSMColumn::putInt (rownr_t aRowNr, const Int* aValue) { putValue(aRowNr,aValue); if (aRowNr >= columnCache().start() && aRowNr <= columnCache().end()) { static_cast(itsData)[aRowNr-columnCache().start()] = *aValue; } } void SSMColumn::putuInt (rownr_t aRowNr, const uInt* aValue) { putValue(aRowNr,aValue); if (aRowNr >= columnCache().start() && aRowNr <= columnCache().end()) { static_cast(itsData)[aRowNr-columnCache().start()] = *aValue; } } void SSMColumn::putInt64 (rownr_t aRowNr, const Int64* aValue) { putValue(aRowNr,aValue); if (aRowNr >= columnCache().start() && aRowNr <= columnCache().end()) { static_cast(itsData)[aRowNr-columnCache().start()] = *aValue; } } void SSMColumn::putfloat (rownr_t aRowNr, const float* aValue) { putValue(aRowNr,aValue); if (aRowNr >= columnCache().start() && aRowNr <= columnCache().end()) { static_cast(itsData)[aRowNr-columnCache().start()] = *aValue; } } void SSMColumn::putdouble (rownr_t aRowNr, const double* aValue) { putValue(aRowNr,aValue); if (aRowNr >= columnCache().start() && aRowNr <= columnCache().end()) { static_cast(itsData)[aRowNr-columnCache().start()] = *aValue; } } void SSMColumn::putComplex (rownr_t aRowNr, const Complex* aValue) { putValue(aRowNr,aValue); if (aRowNr >= columnCache().start() && aRowNr <= columnCache().end()) { static_cast(itsData)[aRowNr-columnCache().start()] = *aValue; } } void SSMColumn::putDComplex (rownr_t aRowNr, const DComplex* aValue) { putValue(aRowNr,aValue); if (aRowNr >= columnCache().start() && aRowNr <= columnCache().end()) { static_cast(itsData)[aRowNr-columnCache().start()] = *aValue; } } void SSMColumn::putString (rownr_t aRowNr, const String* aValue) { // Fixed length strings are written directly. if (itsMaxLen > 0) { rownr_t aStartRow; rownr_t anEndRow; char* aDummy = itsSSMPtr->find (aRowNr, itsColNr, aStartRow, anEndRow, columnName()); itsWriteFunc (aDummy+(aRowNr-aStartRow)*itsExternalSizeBytes, aValue->chars(), min(itsMaxLen, aValue->length()+1)); itsSSMPtr->setBucketDirty(); } else { Int buf[3]; // Try to find out if this value was filled before, in that case we use // an overwrite. getRowValue(buf, aRowNr); // if String <= 8 chars it is written into the data bucket // instead of the string bucket. if (aValue->length() <= 8) { // if string was written before, but longer then 8 chars it has to // be removed from the stringbucket if (buf[2] > 8 ) { itsSSMPtr->getStringHandler()->remove(buf[0], buf[1], buf[2]); } buf[2] = aValue->length(); putValueShortString (aRowNr, buf, *aValue); } else { // Maybe it was there earlier, but smaller. if (buf[2] <= 8) { buf[0] = 0; buf[1] = 0; buf[2] = 0; } itsSSMPtr->getStringHandler()->put (buf[0], buf[1], buf[2], *aValue); putValue (aRowNr, buf); } } } void SSMColumn::putValue(rownr_t aRowNr, const void* aValue) { rownr_t aStartRow; rownr_t anEndRow; char* aDummy = itsSSMPtr->find (aRowNr, itsColNr, aStartRow, anEndRow, columnName()); itsWriteFunc (aDummy+(aRowNr-aStartRow)*itsExternalSizeBytes, aValue, itsNrCopy); itsSSMPtr->setBucketDirty(); } void SSMColumn::putValueShortString(rownr_t aRowNr, const void* aValue, const String& string) { rownr_t aStartRow; rownr_t anEndRow; char* aDummy; aDummy = itsSSMPtr->find (aRowNr, itsColNr, aStartRow, anEndRow, columnName()); itsWriteFunc (aDummy+(aRowNr-aStartRow)*itsExternalSizeBytes, aValue, itsNrCopy); memcpy (aDummy+(aRowNr-aStartRow)*itsExternalSizeBytes, string.chars(), string.length()); itsSSMPtr->setBucketDirty(); } void SSMColumn::getScalarColumnV (ArrayBase& aDataPtr) { if (dtype() == TpString) { Vector& vec = static_cast&>(aDataPtr); for (uInt64 i=0; i(anArray); rownr_t aRowNr=0; rownr_t rowsToDo = aNrRows; while (rowsToDo > 0) { rownr_t aStartRow; rownr_t anEndRow; char* aValue; aValue = itsSSMPtr->find (aRowNr, itsColNr, aStartRow, anEndRow, columnName()); aRowNr = anEndRow+1; rownr_t aNr = anEndRow-aStartRow+1; rowsToDo -= aNr; itsReadFunc (aDataPtr, aValue, aNr * itsNrCopy); aDataPtr += aNr * itsLocalSize; } } void SSMColumn::putScalarColumnV (const ArrayBase& aDataPtr) { if (dtype() == TpString) { const Vector& vec = static_cast&>(aDataPtr); for (uInt64 i=0 ; i(anArray); rownr_t aRowNr=0; rownr_t rowsToDo=aNrRows; while (rowsToDo > 0) { rownr_t aStartRow; rownr_t anEndRow; char* aValPtr; aValPtr = itsSSMPtr->find (aRowNr, itsColNr, aStartRow, anEndRow, columnName()); aRowNr = anEndRow+1; rownr_t aNr = anEndRow-aStartRow+1; rowsToDo -= aNr; itsWriteFunc (aValPtr, aDataPtr, aNr * itsNrCopy); aDataPtr += aNr * itsLocalSize; itsSSMPtr->setBucketDirty(); } // Be sure cache will be emptied columnCache().invalidate(); } void SSMColumn::removeColumn() { if (dataType() == TpString && itsMaxLen == 0) { Int buf[3]; for (rownr_t i=0; igetNRow(); i++) { getRowValue(buf, i); if (buf[2] > 8 ) { itsSSMPtr->getStringHandler()->remove(buf[0], buf[1], buf[2]); } } } } void SSMColumn::init() { DataType aDT = static_cast(dataType()); itsLocalSize = ValType::getTypeSize(aDT); Bool asBigEndian = itsSSMPtr->asBigEndian(); itsNrCopy = itsNrElem; if (aDT == TpString) { // Fixed length strings are written directly. if (itsMaxLen > 0) { itsNrCopy = itsMaxLen; itsLocalSize = itsNrCopy; itsExternalSizeBytes = itsNrCopy; itsReadFunc = itsWriteFunc = Conversion::valueCopy; } else { // Variable length strings are written indirectly. // They have 3 Ints (bucketnr, offset, length) telling where // the strings are. itsNrCopy=1; itsLocalSize = ValType::getTypeSize(TpInt); itsExternalSizeBytes = ValType::getCanonicalSize (TpInt, asBigEndian); uInt aNRel; ValType::getCanonicalFunc (TpInt, itsReadFunc, itsWriteFunc, aNRel, asBigEndian); itsNrCopy *= aNRel; itsExternalSizeBytes *= 3; itsLocalSize *= 3; itsNrCopy *= 3; } itsExternalSizeBits = 8*itsExternalSizeBytes; } else if (aDT == TpBool) { itsExternalSizeBytes = (itsNrElem + 7) / 8; itsExternalSizeBits = itsNrElem; itsReadFunc = &Conversion::bitToBool; itsWriteFunc = &Conversion::boolToBit; } else { itsExternalSizeBytes = ValType::getCanonicalSize (aDT, asBigEndian); uInt aNRel; ValType::getCanonicalFunc (aDT, itsReadFunc, itsWriteFunc, aNRel, asBigEndian); itsNrCopy *= aNRel; itsExternalSizeBytes *= itsNrElem; itsLocalSize *= itsNrElem; itsExternalSizeBits = 8*itsExternalSizeBytes; } } void SSMColumn::resync (rownr_t) { // Invalidate the last value read. columnCache().invalidate(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/SSMColumn.h000066400000000000000000000260321476623553700200770ustar00rootroot00000000000000//# SSMColumn.h: A Column in the Standard Storage Manager //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_SSMCOLUMN_H #define TABLES_SSMCOLUMN_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // // A Column in the Standard Storage Manager. // // // // // //# Classes you should understand before using this one. //
      • SSMBase //
      • SSMStringHandler // // // SSMColumn represents a Column in the Standard Storage Manager. // // // SSMColumn is the base class for access to a column stored with // the Standard Storage manager. It provides some basic functionality // for the derived classes handling direct and indirect arrays. //

        // The main task of SSMColumn is handling the access to a column // containing scalars of the various data types. The data is stored // in buckets. The classes SSMBase // and SSMIndex keep track in which data // bucket a given row is stored and at which offset the column starts. // Using that information SSMColumn can access its data easily. //

        // Almost all data types have a fixed length and can be handled easily. // However, strings are a special case. //
        If the string is fixed length (which means it has a maximum length), // the string is stored directly in the data bucket. If the string is // shorter than the maximum length, its length is indicated by a // trailing 0. //
        Variable strings are in principle stored in a special string bucket. // The data bucket contains 3 integers telling the bucketnr, offset, and // length of the string. However, it the string is short enough (ie. <= // 8 characters), the string is stored directly in data bucket using // the space for bucketnr and offset. //

        // The class maintains a cache of the data in the bucket last read. // This cache is used by the higher level table classes to get faster // read access to the data. // The cache is not used for strings, because they are stored differently. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class SSMColumn : public StManColumnBase { public: // Create a SSMColumn object with the given parent. // It initializes the various variables. // It keeps the pointer to its parent (but does not own it). SSMColumn (SSMBase* aParent, int aDataType, uInt aColNr); virtual ~SSMColumn(); // Forbid copy constructor. SSMColumn (const SSMColumn&) = delete; // Forbid assignment. SSMColumn& operator= (const SSMColumn&) = delete; // Set the shape of an array in the column. // It is only called (right after the constructor) if the array has // a fixed shape. virtual void setShapeColumn (const IPosition& aShape); // Set the maximum length of a 'fixed length' string. // It is only called (right after the constructor) if the string has // a fixed length. virtual void setMaxLength (uInt maxLength); // Get the dimensionality of the item in the given row. virtual uInt ndim (rownr_t aRowNr); // Get the shape of the array in the given row. virtual IPosition shape (rownr_t aRowNr); // Let the object initialize itself for a newly created table. // It is meant for a derived class. virtual void doCreate (rownr_t aNrRows); // Let the column object initialize itself for an existing table virtual void getFile (rownr_t aNrRows); // Resync the storage manager with the new file contents. // It resets the last rownr put. void resync (rownr_t aNrRow); // Get the scalar value in the given row. // virtual void getBool (rownr_t aRowNr, Bool* aDataPtr); virtual void getuChar (rownr_t aRowNr, uChar* aDataPtr); virtual void getShort (rownr_t aRowNr, Short* aDataPtr); virtual void getuShort (rownr_t aRowNr, uShort* aDataPtr); virtual void getInt (rownr_t aRowNr, Int* aDataPtr); virtual void getuInt (rownr_t aRowNr, uInt* aDataPtr); virtual void getInt64 (rownr_t aRowNr, Int64* aDataPtr); virtual void getfloat (rownr_t aRowNr, float* aDataPtr); virtual void getdouble (rownr_t aRowNr, double* aDataPtr); virtual void getComplex (rownr_t aRowNr, Complex* aDataPtr); virtual void getDComplex (rownr_t aRowNr, DComplex* aDataPtr); virtual void getString (rownr_t aRowNr, String* aDataPtr); // // Put the scalar value in the given row. // It updates the cache if the row is contained in the cache. // virtual void putBool (rownr_t aRowNr, const Bool* aDataPtr); virtual void putuChar (rownr_t aRowNr, const uChar* aDataPtr); virtual void putShort (rownr_t aRowNr, const Short* aDataPtr); virtual void putuShort (rownr_t aRowNr, const uShort* aDataPtr); virtual void putInt (rownr_t aRowNr, const Int* aDataPtr); virtual void putuInt (rownr_t aRowNr, const uInt* aDataPtr); virtual void putInt64 (rownr_t aRowNr, const Int64* aDataPtr); virtual void putfloat (rownr_t aRowNr, const float* aDataPtr); virtual void putdouble (rownr_t aRowNr, const double* aDataPtr); virtual void putComplex (rownr_t aRowNr, const Complex* aDataPtr); virtual void putDComplex (rownr_t aRowNr, const DComplex* aDataPtr); virtual void putString (rownr_t aRowNr, const String* aDataPtr); // // Get the scalar values of the entire column. virtual void getScalarColumnV (ArrayBase& aDataPtr); // Put the scalar values of the entire column. // It invalidates the cache. virtual void putScalarColumnV (const ArrayBase& aDataPtr); // Add (NewNrRows-OldNrRows) rows to the Column and initialize // the new rows when needed. virtual void addRow (rownr_t aNewNrRows, rownr_t anOldNrRows, Bool doInit); // Remove the given row from the data bucket and possibly string bucket. // If needed, it also removes it from the cache. virtual void deleteRow (rownr_t aRowNr); // Get the size of the dataType in bytes!! uInt getExternalSizeBytes() const; // Get the size of the dataType in bits!! uInt getExternalSizeBits() const; // get the sequence number of this column. uInt getColNr(); // set the sequence number of this column. void setColNr (uInt aColNr); // If something special has to be done before removing the Column, // as is the case with Strings, it can be done here. void removeColumn(); protected: // Shift the rows in the bucket one to the left when removing the given row. void shiftRows (char* aValue, rownr_t rowNr, rownr_t startRow, rownr_t endRow); // Fill the cache with data of the bucket containing the given row. void getValue (rownr_t aRowNr); // Get the bucketnr, offset, and length of a variable length string. // data must have 3 Ints to hold the values. // It returns a pointer to the data in the bucket, which can be used // for the case that the data bucket contains the (short) string. Char* getRowValue (Int* data, rownr_t aRowNr); // Put the given value for the row into the correct data bucket. void putValue (rownr_t aRowNr, const void* aValue); // Put the given string for the row into the correct data bucket. // The argument aValue> must be 3 Ints (for bucketnr, offset, // and length). Only the length is actually used. void putValueShortString (rownr_t aRowNr, const void* aValue, const String& string); // Get the values for the entire column. // The data from all buckets is copied to the array. void getColumnValue (void* anArray, rownr_t aNrRows); // Put the values from the array in the entire column. // Each data bucket is filled with the the appropriate part of the array. void putColumnValue (const void* anArray, rownr_t aNrRows); // Pointer to the parent storage manager. SSMBase* itsSSMPtr; // Length of column cell value in storage format (0 = variable length). uInt itsExternalSizeBytes; uInt itsExternalSizeBits; // Column sequence number of this column. uInt itsColNr; // The shape of the column. IPosition itsShape; // The maximum length of a 'fixed length' string. uInt itsMaxLen; // Number of elements in a value for this column. uInt itsNrElem; // Number of values to be copied. // Normally this is itsNrElem, but for complex types it is 2*itsNrElem. // When local format is used, it is the number of bytes. uInt itsNrCopy; // The sizeof the datatype in local format uInt itsLocalSize; // The data in local format. void* itsData; // Pointer to a convert function for writing. Conversion::ValueFunction* itsWriteFunc; // Pointer to a convert function for reading. Conversion::ValueFunction* itsReadFunc; private: // Initialize part of the object. // It determines the nr of elements, the function to use to convert // from local to file format, etc.. void init(); // Get the pointer to the cache. It is created if not done yet. char* getDataPtr(); }; inline uInt SSMColumn::getExternalSizeBytes() const { return itsExternalSizeBytes; } inline uInt SSMColumn::getExternalSizeBits() const { return itsExternalSizeBits; } inline char* SSMColumn::getDataPtr() { if (itsData == 0) { itsData = new char[itsSSMPtr->getRowsPerBucket(itsColNr) * itsLocalSize]; } return static_cast(itsData); } inline uInt SSMColumn::getColNr() { return itsColNr; } inline void SSMColumn::setColNr (uInt aColNr) { itsColNr = aColNr; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/SSMDirColumn.cc000066400000000000000000000116241476623553700206750ustar00rootroot00000000000000//# SSMDirColumn.cc: a Direct Array Column of the Standard Storage Manager //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN SSMDirColumn::SSMDirColumn (SSMBase* aParent, int aDataType, uInt aColNr) : SSMColumn (aParent,aDataType,aColNr) { } SSMDirColumn::~SSMDirColumn() { } void SSMDirColumn::setMaxLength (uInt) {} void SSMDirColumn::deleteRow(rownr_t aRowNr) { char* aValue; rownr_t aSRow; rownr_t anERow; aValue = itsSSMPtr->find (aRowNr, itsColNr, aSRow, anERow, columnName()); if (aRowNr < anERow) { // remove from bucket // first check if type is a bool if (dataType() == TpBool) { uInt64 anOffr = (aRowNr-aSRow+1) * itsNrCopy; uInt64 anOfto = (aRowNr-aSRow) * itsNrCopy; uInt64 nr = (anERow-aRowNr) * itsNrCopy; Block tmp(nr); Conversion::bitToBool (tmp.storage(), aValue + anOffr/8, anOffr%8, nr); Conversion::boolToBit (aValue + anOfto/8, tmp.storage(), anOfto%8, nr); } else { // remove from bucket shiftRows(aValue,aRowNr,aSRow,anERow); } itsSSMPtr->setBucketDirty(); } } void SSMDirColumn::getArrayV (rownr_t aRowNr, ArrayBase& aDataPtr) { Bool deleteIt; if (dtype() == TpBool) { // Bools need to be converted from bits. rownr_t aStartRow; rownr_t anEndRow; char* aValue; Array& arr = static_cast&>(aDataPtr); Bool* data = arr.getStorage (deleteIt); aValue = itsSSMPtr->find (aRowNr, itsColNr, aStartRow, anEndRow, columnName()); uInt64 anOff = (aRowNr-aStartRow) * itsNrCopy; Conversion::bitToBool(data, aValue+ anOff/8, anOff%8, itsNrCopy); arr.putStorage (data, deleteIt); } else if (dtype() == TpString) { // Strings are stored indirectly. Int buf[3]; getRowValue(buf, aRowNr); Array& arr = static_cast&>(aDataPtr); itsSSMPtr->getStringHandler()->get(arr, buf[0], buf[1], buf[2], False); } else { // Other types can be handled directly. void* data = aDataPtr.getVStorage (deleteIt); getValue (aRowNr, data); aDataPtr.putVStorage (data, deleteIt); } } void SSMDirColumn::getValue(rownr_t aRowNr, void* data) { rownr_t aStartRow; rownr_t anEndRow; char* aValue; aValue = itsSSMPtr->find (aRowNr, itsColNr, aStartRow, anEndRow, columnName()); itsReadFunc (data, aValue+(aRowNr-aStartRow)*itsExternalSizeBytes, itsNrCopy); } void SSMDirColumn::putArrayV (rownr_t aRowNr, const ArrayBase& aDataPtr) { Bool deleteIt; if (dtype() == TpBool) { // Bools need to be converted from bits. rownr_t aStartRow; rownr_t anEndRow; char* aValue; aValue = itsSSMPtr->find (aRowNr, itsColNr, aStartRow, anEndRow, columnName()); uInt64 anOff = (aRowNr-aStartRow) * itsNrCopy; const Array& arr = static_cast&>(aDataPtr); const Bool* data = arr.getStorage (deleteIt); Conversion::boolToBit (aValue + anOff/8, data, anOff%8, itsNrCopy); arr.freeStorage (data, deleteIt); } else if (dtype() == TpString) { // Strings are stored indirectly. Int buf[3]; getRowValue(buf, aRowNr); const Array& arr = static_cast&>(aDataPtr); itsSSMPtr->getStringHandler()->put(buf[0], buf[1], buf[2], arr, False); putValue(aRowNr, buf); } else { // Other types can be handled directly. const void* data = aDataPtr.getVStorage (deleteIt); putValue (aRowNr, data); aDataPtr.freeVStorage (data, deleteIt); } itsSSMPtr->setBucketDirty(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/SSMDirColumn.h000066400000000000000000000073471476623553700205460ustar00rootroot00000000000000//# SSMDirColumn.h: A Column for Direct Arrays in the Standard Storage Manager //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_SSMDIRCOLUMN_H #define TABLES_SSMDIRCOLUMN_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations //

        // A Direct Array Column in the Standard Storage Manager. // // // // // //# Classes you should understand before using this one. //
      • SSMBase //
      • SSMColumn //
      • SSMStringHandler // // // SSMDirColumn represents a Direct Array Column in the // Standard Storage Manager. // // // SSMDirColumn handles the access to a column containing direct // arrays of the various data types. //
        // It is derived from SSMColumn // and uses most of its functions. The only thing done differently // in this class is that it maintains no cache. // Furthermore fixed length strings are not handled specially. // All string arrays are stored in the special string buckets. //
        //# //# A List of bugs, limitations, extensions or planned refinements. //# class SSMDirColumn : public SSMColumn { public: // Create a SSMDirColumn object with the given parent. // It initializes the various variables. // It keeps the pointer to its parent (but does not own it). SSMDirColumn (SSMBase* aParent, int aDataType, uInt aColNr); virtual ~SSMDirColumn(); // Forbid copy constructor. SSMDirColumn (const SSMDirColumn&) = delete; // Forbid assignment. SSMDirColumn& operator= (const SSMDirColumn&) = delete; // An array of 'fixed length' strings is not handled specially, // thus this function is ignored. // It is needed to override the bahviour of the base class. virtual void setMaxLength (uInt maxLength); // Get an array value in the given row. virtual void getArrayV (rownr_t rownr, ArrayBase& dataPtr); // Put an array value in the given row. virtual void putArrayV (rownr_t rownr, const ArrayBase& dataPtr); // Remove the given row from the data bucket and possibly string bucket. virtual void deleteRow (rownr_t aRowNr); protected: // Read the array data for the given row into the data buffer. void getValue (rownr_t aRowNr, void* data); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/SSMIndColumn.cc000066400000000000000000000157561476623553700207030ustar00rootroot00000000000000//# SSMIndColumn.cc: Column of Standard storage manager for indirect arrays //# Copyright (C) 2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN SSMIndColumn::SSMIndColumn (SSMBase* aParent, int aDataType, uInt aColNr) : SSMColumn (aParent, aDataType, aColNr), isShapeFixed (False), itsIosFile (0), itsIndArray (0) { init(); } SSMIndColumn::~SSMIndColumn() {} void SSMIndColumn::setMaxLength (uInt) {} void SSMIndColumn::doCreate (rownr_t aNrRows) { // Initialize and create new file. itsIosFile = itsSSMPtr->openArrayFile (ByteIO::New); addRow(aNrRows,0,False); } void SSMIndColumn::getFile (rownr_t) { // Initialize and open existing file. itsIosFile = itsSSMPtr->openArrayFile (itsSSMPtr->fileOption()); } void SSMIndColumn::addRow (rownr_t aNewNrRows, rownr_t anOldNrRows, Bool doInit) { // init the buckets to zero of needed if (doInit) { rownr_t aRowNr=0; rownr_t aNrRows=aNewNrRows; while (aNrRows > 0) { rownr_t aStartRow; rownr_t anEndRow; char* aValPtr; aValPtr = itsSSMPtr->find (aRowNr, itsColNr, aStartRow, anEndRow, columnName()); aRowNr = anEndRow+1; rownr_t aNr = anEndRow-aStartRow+1; aNrRows -= aNr; memset(aValPtr, 0, aNr * itsExternalSizeBytes); itsSSMPtr->setBucketDirty(); } } // If the shape is fixed and if the first row is added, define // an array to have an array for all rows. // Later rows get the value of a previous row, so we don't have to // do anything for them. if (isShapeFixed) { for (; anOldNrRowsgetShape (*itsIosFile); } // put the new shape (if changed) // when changed put the file offset if (itsIndArray.setShape (*itsIosFile, dataType(), aShape)) { Int64 anOffset = itsIndArray.fileOffset(); putValue (aRowNr, &anOffset); } } StIndArray* SSMIndColumn::getArrayPtr (rownr_t aRowNr) { Int64 anOffset; rownr_t aStartRow; rownr_t anEndRow; char* aValue; aValue = itsSSMPtr->find (aRowNr, itsColNr, aStartRow, anEndRow, columnName()); itsReadFunc (&anOffset, aValue+(aRowNr-aStartRow)*itsExternalSizeBytes, itsNrCopy); if (anOffset != 0) { itsIndArray = StIndArray (anOffset); return &itsIndArray; }else{ return 0; } } //# Get the shape for the array (if any) in the given row. //# Read shape if not read yet. StIndArray* SSMIndColumn::getShape (rownr_t aRowNr) { StIndArray* aPtr = getArrayPtr (aRowNr); if (aPtr == 0) { throw DataManInvOper ("SSMIndColumn::getShape: no array in row "+ String::toString(aRowNr) + " in column " + columnName() + " of table " + itsSSMPtr->table().tableName()); } aPtr->getShape (*itsIosFile); return aPtr; } Bool SSMIndColumn::isShapeDefined (rownr_t aRowNr) { return (getArrayPtr(aRowNr) == 0 ? False : True); } uInt SSMIndColumn::ndim (rownr_t aRowNr) { return getShape(aRowNr)->shape().nelements(); } IPosition SSMIndColumn::shape (rownr_t aRowNr) { return getShape(aRowNr)->shape(); } Bool SSMIndColumn::canChangeShape() const { return (isShapeFixed ? False : True); } void SSMIndColumn::deleteRow(rownr_t aRowNr) { char* aValue; rownr_t aSRow; rownr_t anERow; aValue = itsSSMPtr->find (aRowNr, itsColNr, aSRow, anERow, columnName()); if (aRowNr < anERow) { // remove from bucket shiftRows(aValue,aRowNr,aSRow,anERow); itsSSMPtr->setBucketDirty(); } } void SSMIndColumn::getArrayV (rownr_t aRowNr, ArrayBase& arr) { getShape(aRowNr)->getArrayV (*itsIosFile, arr, dtype()); } void SSMIndColumn::putArrayV (rownr_t aRowNr, const ArrayBase& arr) { getShape(aRowNr)->putArrayV (*itsIosFile, arr, dtype()); } void SSMIndColumn::getSliceV (rownr_t aRowNr, const Slicer& ns, ArrayBase& arr) { getShape(aRowNr)->getSliceV (*itsIosFile, ns, arr, dtype()); } void SSMIndColumn::putSliceV (rownr_t aRowNr, const Slicer& ns, const ArrayBase& arr) { getShape(aRowNr)->putSliceV (*itsIosFile, ns, arr, dtype()); } void SSMIndColumn::init() { DebugAssert (itsNrElem==1, AipsError); if (itsSSMPtr->asBigEndian()) { itsReadFunc = CanonicalConversion::getToLocal(static_cast(0)); itsWriteFunc = CanonicalConversion::getFromLocal(static_cast(0)); itsExternalSizeBytes = CanonicalConversion::canonicalSize(static_cast(0)); }else{ itsReadFunc = LECanonicalConversion::getToLocal(static_cast(0)); itsWriteFunc = LECanonicalConversion::getFromLocal(static_cast(0)); itsExternalSizeBytes = LECanonicalConversion::canonicalSize(static_cast(0)); } itsNrCopy = 1; itsExternalSizeBits = 8*itsExternalSizeBytes; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/SSMIndColumn.h000066400000000000000000000156021476623553700205330ustar00rootroot00000000000000//# SSMIndColumn.h: A column in Standard storage manager for indirect arrays //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_SSMINDCOLUMN_H #define TABLES_SSMINDCOLUMN_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class StManArrayFile; class AipsIO; // // A column of Standard storage manager for indirect arrays. // // // // // //# Classes you should understand before using this one. //
      • SSMColumn //
      • StIndArray // // // SSMIndColumn represents a Column in the Standard Storage Manager // containing Indirect arrays. // // // SSMIndColumn is the implementation of an // SSMColumn class // to handle indirect arrays. The arrays (shape and data) are stored in // a separate file using class StIndArray. // The file offset of the beginning of the array in stored in the // appropriate data bucket using the standard SSMColumn functions. //

        // Note that an indirect array can have a fixed shape. In that case // adding a row results in reserving space for the array in the StIndArray // file, so for each row an array is present. // On the other hand adding a row does nothing for variable shaped arrays. // So when no data is put or shape is set, a row may contain no array at all. // In that case the function isShapeDefined returns False for // that row. //

        // Indirect arrays containing strings are not handled by this class, but // by SSMIndStringColumn. // That class stores those string arrays in the special string buckets // instead of using StIndArray. The reason is that the string buckets // are more disk space efficient when string arrays are frequently updated. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class SSMIndColumn : public SSMColumn { public: // Create a column of the given data type. // It keeps the pointer to its parent (but does not own it). SSMIndColumn (SSMBase* aParent, int aDataType, uInt aColNr); // Frees up the storage. ~SSMIndColumn(); // Forbid copy constructor. SSMIndColumn (const SSMIndColumn&) = delete; // Forbid assignment. SSMIndColumn& operator= (const SSMIndColumn&) = delete; // An array of 'fixed length' strings is not handled specially, // thus this function is ignored. // It is needed to override the bahviour of the base class. virtual void setMaxLength (uInt maxLength); // Add (newNrrow-oldNrrow) rows to the column. virtual void addRow (rownr_t aNewNrRows, rownr_t anOldNrRows, Bool doInit); // Set the (fixed) shape of the arrays in the entire column. virtual void setShapeColumn (const IPosition& aShape); // Get the dimensionality of the item in the given row. virtual uInt ndim (rownr_t aRowNr); // Set the shape of the array in the given row and allocate the array // in the file. void setShape (rownr_t aRowNr, const IPosition& aShape); // Is the shape defined (i.e. is there an array) in this row? virtual Bool isShapeDefined (rownr_t aRowNr); // Get the shape of the array in the given row. virtual IPosition shape (rownr_t aRowNr); // This storage manager can handle changing array shapes. Bool canChangeShape() const; // Get an array value in the given row. // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the ArrayColumn get function). virtual void getArrayV (rownr_t aRowNr, ArrayBase& aDataPtr); // Put an array value into the given row. // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the ArrayColumn put function). virtual void putArrayV (rownr_t aRowNr, const ArrayBase& aDataPtr); // Get a section of the array in the given row. // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the ArrayColumn getSlice function). virtual void getSliceV (rownr_t aRowNr, const Slicer&, ArrayBase& aDataPtr); // Put into a section of the array in the given row. // The buffer pointed to by aDataPtr has to have the correct length // (which is guaranteed by the ArrayColumn putSlice function). virtual void putSliceV (rownr_t aRowNr, const Slicer&, const ArrayBase& aDataPtr); // Let the column object create its array file. virtual void doCreate (rownr_t aNrRows); // Open an existing file. virtual void getFile (rownr_t aNrRows); // Remove the given row from the data bucket and possibly string bucket. virtual void deleteRow(rownr_t aRowNr); private: // Initialize part of the object and open/create the file. // It is used by doCreate and getFile. void init(); // Read the shape at the given row. // This will cache the information in the StIndArray // object for that row. StIndArray* getShape (rownr_t aRowNr); // Return a pointer to the array in the given row (for a get). StIndArray* getArrayPtr (rownr_t aRowNr); //# The shape off all arrays in case it is fixed IPosition itsFixedShape; //# Switch indicating if the shape is fixed. Bool isShapeFixed; //# The file containing the arrays. StManArrayFile* itsIosFile; //# The indirect array object. StIndArray itsIndArray; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/SSMIndStringColumn.cc000066400000000000000000000104511476623553700220550ustar00rootroot00000000000000//# SSMIndStringColumn.cc: a indirect String Array Column of the //# Standard Storage Manager //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN SSMIndStringColumn::SSMIndStringColumn (SSMBase* aParent, int aDataType, uInt aColNr): SSMDirColumn (aParent,aDataType,aColNr) { } SSMIndStringColumn::~SSMIndStringColumn() { } void SSMIndStringColumn::setShape (rownr_t aRowNr, const IPosition& aShape) { DebugAssert(itsShape.nelements() == 0,AipsError); Int buf[3]; // Try to find out if this value was filled before, in that case we use // an overwrite. getRowValue(buf, aRowNr); itsSSMPtr->getStringHandler()->putShape(buf[0], buf[1], buf[2], aShape); putValue(aRowNr, buf); } IPosition SSMIndStringColumn::shape (rownr_t aRowNr) { if (itsShape.nelements() != 0) { return itsShape; } IPosition aShape; Int buf[3]; getRowValue(buf, aRowNr); if (buf[2] > 0) { itsSSMPtr->getStringHandler()->getShape(aShape, buf[0], buf[1], buf[2]); } else { throw DataManInvOper ("SSMIndStringColumn::getShape: no array in row "+ String::toString(aRowNr) + " in column " + columnName() + " of table " + itsSSMPtr->table().tableName()); } return aShape; } Bool SSMIndStringColumn::canChangeShape() const { return itsShape.nelements() ==0; } Bool SSMIndStringColumn::isShapeDefined (rownr_t aRowNr) { if (itsShape.nelements() != 0) { return True; } else { Int buf[3]; getRowValue(buf, aRowNr); return buf[2] != 0; } } uInt SSMIndStringColumn::ndim (rownr_t aRowNr) { return shape(aRowNr).nelements(); } void SSMIndStringColumn::getArrayV (rownr_t aRowNr, ArrayBase& aDataPtr) { if (itsShape.nelements() != 0) { SSMDirColumn::getArrayV (aRowNr,aDataPtr); } else { Int buf[3]; getRowValue(buf, aRowNr); if ( buf[2] == 0 ) { throw DataManInvOper ("SSMIndStringColumn::getArrayStringV: no array in row " + String::toString(aRowNr) + " in column " + columnName() + " of table " + itsSSMPtr->table().tableName()); } itsSSMPtr->getStringHandler()->get(static_cast&>(aDataPtr), buf[0], buf[1], buf[2], True); } } void SSMIndStringColumn::putArrayV (rownr_t aRowNr, const ArrayBase& aDataPtr) { if (itsShape.nelements() != 0) { SSMDirColumn::putArrayV (aRowNr, aDataPtr); } else { Int buf[3]; // Try to find out if this value was filled before, in that case we use // an overwrite. getRowValue(buf, aRowNr); itsSSMPtr->getStringHandler()->put(buf[0], buf[1], buf[2], static_cast&>(aDataPtr), True); putValue(aRowNr, buf); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/SSMIndStringColumn.h000066400000000000000000000103621476623553700217200ustar00rootroot00000000000000//# SSMIndStringColumn.h: An Indirect String Array Column in the SSM //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_SSMINDSTRINGCOLUMN_H #define TABLES_SSMINDSTRINGCOLUMN_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations //

        // An Indirect String Array Column in the Standard Storage Manager. // // // // // //# Classes you should understand before using this one. //
      • SSMBase //
      • SSMColumn //
      • SSMStringHandler // // // SSMIndStringColumn represents an Indirect String Array Column in the // Standard Storage Manager. // // // SSMIndStringColumn handles indirect variable shaped string arrays. // Note that indirect fixed shape string arrays are handled by // SSMDirColumn. //

        // All string array access is handled by class // SSMStringHandler, so // SSMIndStringColumn is merely an interface to this class. // The only thing it does is accessing the bucketnr, offset, and length // in the data bucket. // // // The reason that indirect string arrays are handled here instead of // in SSMIndColumn is that the string // buckets are more disk space efficient when string arrays are frequently // updated. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class SSMIndStringColumn : public SSMDirColumn { public: // Create a SSMIndStringColumn object with the given parent. // It initializes the various variables. // It keeps the pointer to its parent (but does not own it). SSMIndStringColumn (SSMBase* aParent, int aDataType, uInt aColNr); virtual ~SSMIndStringColumn(); // Forbid copy constructor. SSMIndStringColumn (const SSMIndStringColumn&) = delete; // Forbid assignment. SSMIndStringColumn& operator= (const SSMIndStringColumn&) = delete; // Get an array value in the given row. // An exception is thrown if no array is defined in this row. virtual void getArrayV (rownr_t rownr, ArrayBase& dataPtr); // Put an array value in the given row. virtual void putArrayV (rownr_t rownr, const ArrayBase& dataPtr); // Set the shape of the array in the given row. void setShape (rownr_t aRowNr, const IPosition& aShape); // Get the shape of the array in the given row. virtual IPosition shape (rownr_t aRowNr); // This storage manager can handle changing array shapes. Bool canChangeShape() const; // Is the shape defined (i.e. is there an array) in this row? virtual Bool isShapeDefined (rownr_t aRowNr); // Get the dimensionality of the item in the given row. virtual uInt ndim (rownr_t aRowNr); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/SSMIndex.cc000066400000000000000000000231051476623553700200450ustar00rootroot00000000000000//# SSMIndex.cc: The bucket index for a group of columns in the SSM //# Copyright (C) 2000,2001,2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN SSMIndex::SSMIndex (SSMBase* aSSMPtr, uInt rowsPerBucket) : itsSSMPtr (aSSMPtr), itsNUsed (0), itsRowsPerBucket (rowsPerBucket), itsNrColumns (0) { } SSMIndex::~SSMIndex() { } void SSMIndex::get (AipsIO& anOs) { uInt version = anOs.getstart("SSMIndex"); anOs >> itsNUsed; anOs >> itsRowsPerBucket; anOs >> itsNrColumns; anOs >> itsFreeSpace; if (version == 1) { Block tmp; getBlock (anOs, tmp); itsLastRow.resize (tmp.size()); for (size_t i=0; i 0 && itsLastRow[itsNUsed-1] > DataManager::MAXROWNR32) { version = 2; } anOs.putstart("SSMIndex", version); anOs << itsNUsed; anOs << itsRowsPerBucket; anOs << itsNrColumns; anOs << itsFreeSpace; if (version == 1) { Block tmp(itsNUsed); for (uInt i=0; i 0 ) { for (uInt i=0; igetBucketSize()-aSizeUsed; if (nfree > 0) { itsFreeSpace.insert (std::make_pair(aSizeUsed, nfree)); } } void SSMIndex::addRow (rownr_t aNrRows) { rownr_t lastRow=0; if (aNrRows == 0 ) { return; } if (itsNUsed > 0 ) { lastRow = itsLastRow[itsNUsed-1]+1; rownr_t usedLast = lastRow; if (itsNUsed > 1) { usedLast -= itsLastRow[itsNUsed-2]+1; } uInt64 fitLast = itsRowsPerBucket-usedLast; uInt64 toAdd = std::min(fitLast, aNrRows); itsLastRow[itsNUsed-1] += toAdd; aNrRows -= toAdd; lastRow += toAdd; } if (aNrRows == 0) { return; } // add extra row. If nrrows > itsLastRow.nelements reserve extra rows // which is cheaper. // Take in account that NrRows can be bigger then itsRowsPerBucket ! uInt aNr = (aNrRows+itsRowsPerBucket-1) / itsRowsPerBucket; uInt aNewNr = itsNUsed+aNr; uInt aOldNr = itsLastRow.nelements(); if (aNewNr > aOldNr) { uInt newSize = aOldNr*2; if (aNewNr < newSize) { aNewNr = newSize; } itsLastRow.resize (aNewNr); itsBucketNumber.resize(aNewNr); } // first time bucket is made and filled, last bucket was filled, so if // still rowsLeft, there must be a new entry/bucket while (aNrRows > 0) { itsBucketNumber[itsNUsed] =itsSSMPtr->getNewBucket(); uInt toAdd = std::min (aNrRows, rownr_t(itsRowsPerBucket)); lastRow += toAdd; aNrRows -= toAdd; itsLastRow[itsNUsed] = lastRow-1; itsNUsed += 1; } } Int SSMIndex::deleteRow (rownr_t aRowNr) { // Decrement the rowNrs of all the intervals after the row to be removed uInt anIndex = getIndex(aRowNr, String()); Bool isEmpty=False; for (uInt i = anIndex ; i< itsNUsed; i++) { if (itsLastRow[i] > 0) { itsLastRow[i]--; } else { isEmpty = True; } } // If this bucket is empty, add to free list and remove from itsLastRow // and itsBucketNumber. Int anEmptyBucket = -1; Int last = -1; if (anIndex > 0) { last = itsLastRow[anIndex-1]; } if (static_cast(itsLastRow[anIndex]) == last || isEmpty) { anEmptyBucket = itsBucketNumber[anIndex]; if (anIndex+1 < itsNUsed) { objmove (&itsLastRow[anIndex], &itsLastRow[anIndex+1], itsNUsed-anIndex-1); objmove (&itsBucketNumber[anIndex], &itsBucketNumber[anIndex+1], itsNUsed-anIndex-1); } itsNUsed--; itsLastRow[itsNUsed]=0; itsBucketNumber[itsNUsed]=0; } return anEmptyBucket; } void SSMIndex::recreate() { itsNUsed=0; } uInt SSMIndex::getIndex (rownr_t aRowNumber, const String& colName) const { Bool isFound; uInt anIndex = binarySearchBrackets( isFound, itsLastRow, aRowNumber, itsNUsed ); if (anIndex >= itsNUsed) { throw TableError ("SSMIndex::getIndex - access to non-existing row " + String::toString(aRowNumber) + " in column " + colName + " of table " + itsSSMPtr->table().tableName()); } return anIndex; } Int SSMIndex::removeColumn (Int anOffset, uInt nbits) { // set freespace (total in bytes). uInt aLength = (itsRowsPerBucket * nbits + 7) / 8; itsFreeSpace.insert (std::make_pair(anOffset,aLength)); itsNrColumns--; AlwaysAssert (itsNrColumns > -1, AipsError); // See if space can be combined // That is possible if two or more entries are adjacent. std::map::iterator next = itsFreeSpace.begin(); std::map::iterator curr = next; next++; while (next != itsFreeSpace.end()) { Int aK = curr->first; Int aV = curr->second; if (aK+aV == next->first) { // Adjacent; combine current with next by adding its free space. curr->second += next->second; // Remove next such that the iterator is still valid. // Thus first increment, then erase. std::map::iterator nextsav = next; ++next; itsFreeSpace.erase (nextsav); } else { curr = next; ++next; } } return (itsNrColumns); } Vector SSMIndex::getBuckets() const { Vector aBucketList(itsNUsed); for (uInt i=0; i< itsNUsed; i++) { aBucketList(i) = itsBucketNumber[i]; } return aBucketList; } Int SSMIndex::getFree (Int& anOffset, uInt nbits) const { Int aLength = (itsRowsPerBucket * nbits + 7) / 8; // returnvalue: -1 : No fit // 0 : Best fit at anOffset // other : Nr of bytes left after fit Int bestLength = -1; // try to find if there's a place to (best) fit data with a given length for (const auto& x : itsFreeSpace) { Int aV = x.second; if (aV == aLength) { anOffset = x.first; //Perfect fit found, don't need to continue return(0); } if (aV >= aLength && (aV < bestLength || bestLength == -1)) { bestLength = aV; anOffset = x.first; } } if (bestLength == -1) { return (bestLength); } else { return (bestLength-aLength); } } void SSMIndex::addColumn (Int anOffset, uInt nbits) { Int aLength = (itsRowsPerBucket * nbits + 7) / 8; Int aV = itsFreeSpace.at(anOffset); itsNrColumns++; itsFreeSpace.erase(anOffset); if (aLength != aV) { DebugAssert (aLength < aV, AipsError); // more space then needed, remap extra space Int anO = anOffset + aLength; aV -= aLength; itsFreeSpace.insert (std::make_pair(anO, aV)); } } void SSMIndex::find (rownr_t aRowNumber, uInt& aBucketNr, rownr_t& aStartRow, rownr_t& anEndRow, const String& colName) const { uInt anIndex = getIndex(aRowNumber, colName); aBucketNr = itsBucketNumber[anIndex]; anEndRow = itsLastRow[anIndex]; aStartRow = 0; if (anIndex > 0) { aStartRow = itsLastRow[anIndex-1]+1; } } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/SSMIndex.h000066400000000000000000000137251476623553700177160ustar00rootroot00000000000000//# SSMIndex.h: The bucket index for a group of columns in the SSM //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_SSMINDEX_H #define TABLES_SSMINDEX_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class SSMBase; //

        // The bucket index for a group of columns in the Standard Storage Manager. // // // // // //# Classes you should understand before using this one. //
      • SSMBase // // // SSMIndex represent the bucket index in the Standard Storage Manager. // // // In SSMBase it is described that an index // is used to map row number to data bucket in a bucket stream. // This class implements this index. It serves 2 purposes: //
          //
        1. It keeps a block of row numbers giving the last row number // stored in each data bucket. //
          Note that each bucket does not need to contain the same number // of rows, because rows might be deleted from it. // When all rows are deleted from a bucket, the bucket is removed // from the index and added to the free bucket list. //
        2. When a column is deleted, the bucket will have a hole. // SSMIndex maintains a map to know the size and offset of each hole. // Adjacent holes are combined. // When a new column is added SSMBase // will scan the SSMIndex objects to find the hole fitting best. //
        //
        // //# A List of bugs, limitations, extensions or planned refinements. //
      • recreate should recreate the itsLastRow && itsBucketNr as well // (moving them to the front and rearrange the freespace as one // concatenated block) // class SSMIndex { public: // Create the object with the given number of rows per bucket. // Note that the default is needed to create the object for existing // tables. explicit SSMIndex (SSMBase* aPtrSSM, uInt rowsPerBucket=0); ~SSMIndex(); // Read the bucket index from the AipsIO object. void get (AipsIO& anOs); // Write the bucket index into the AipsIO object. void put (AipsIO& anOs) const; // Recreate the object in case all rows are deleted from the table. void recreate(); // Return all the bucketnrs used in this index. Vector getBuckets() const; // Return the nr of buckets used. uInt getNrBuckets() const; // Set nr of columns use this index. void setNrColumns (Int aNrColumns, uInt aSizeUsed); // Add some rows. void addRow (rownr_t aNrRows); // Show Statistics of index. void showStatistics (ostream& anOs) const; // A column is removed. // Set the free space at offset for a field with the given nr of bits. // It returns the nr of columns still used in this index. Int removeColumn (Int anOffset, uInt nbits); // Try to find free space for a field with a given length (best fit). // -1 is returned if no fit is found. // Otherwise it returns the nr of bytes left unused. Int getFree (Int& anOffset, uInt nbits) const; // reuse the space at offset for a field with the given nr of bits. // This is used when column has been added to this bucket. void addColumn (Int anOffset, uInt nbits); // Delete the given row. // It returns the bucket nr if it gets empty, otherwise -1. Int deleteRow (rownr_t aRowNumber); // Get the number of rows that fits in ach bucket. uInt getRowsPerBucket() const; // Find the bucket containing the given row. // An exception is thrown if not found. // It also sets the first and last row number fitting in that bucket. void find (rownr_t aRowNumber, uInt& aBucketNr, rownr_t& aStartRow, rownr_t& anEndRow, const String& colName) const; private: // Get the index of the bucket containing the given row. uInt getIndex (rownr_t aRowNr, const String& colName) const; //# Pointer to specific Storage Manager. SSMBase* itsSSMPtr; //# Nr of entries used in blocks. uInt itsNUsed; //# Last row nr indexed together with itsBucketNumber Block itsLastRow; //# Bucketnumbers indexed together with itsLastRow. //# So itsLastRow[0] contains the last rownumber of the bucket //# in itsBucketNumber[0] Block itsBucketNumber; //# Map that contains length/offset pairs for free size (size in bytes). std::map itsFreeSpace; //# How many rows fit in a bucket? uInt itsRowsPerBucket; //# Nr of columns using this index. Int itsNrColumns; }; inline uInt SSMIndex::getRowsPerBucket() const { return itsRowsPerBucket; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/SSMStringHandler.cc000066400000000000000000000366721476623553700215570ustar00rootroot00000000000000//# SSMStringHandler.cc: Store Strings in the Standard Storage Manager //# Copyright (C) 2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN SSMStringHandler::SSMStringHandler (SSMBase* aBase): itsSSMPtr (aBase), itsCurrentBucket (-1), itsLength (0), itsNDeleted (0), itsUsedLength (0), itsNextBucket (-1), itsData (0), itsIntBuf (0), isChanged (False), itsLastBucket (-1) { } SSMStringHandler::~SSMStringHandler() { delete [] itsData; delete [] itsIntBuf; } void SSMStringHandler::init() { delete [] itsData; itsData = 0; delete [] itsIntBuf; itsIntBuf = 0; itsIntSize = CanonicalConversion::canonicalSize(&itsUsedLength); itsStart = 4*itsIntSize; itsLength = itsSSMPtr->getBucketSize()-itsStart; itsData = new char[itsLength]; itsIntBuf = new char[itsIntSize]; memset(itsData,0,itsLength); memset(itsIntBuf,0,itsIntSize); } void SSMStringHandler::replace(Int bucketNr, Int offset, Int length, const String& string) { // Check if current bucket is wanted bucket, else get wanted bucket. if (bucketNr != itsCurrentBucket) { getBucket(bucketNr); } replaceData(offset,string.length(),string.chars()); // remove additional space left (if any) if (length - string.length() > 0) { remove (itsCurrentBucket, offset, length-string.length()); } } void SSMStringHandler::replace(Int bucketNr, Int offset, Int length, Int totalLength, const Array& string, Bool handleShape) { const IPosition& aShape = string.shape(); // Check if current bucket is wanted bucket, else get wanted bucket. if (bucketNr != itsCurrentBucket) { getBucket(bucketNr); } Bool deleteIt; const String *aString = string.getStorage(deleteIt); if (handleShape) { CanonicalConversion::fromLocal (itsIntBuf, (uInt) aShape.nelements()); replaceData (offset,itsIntSize, itsIntBuf); for (uInt i=0; i< aShape.nelements();i++) { CanonicalConversion::fromLocal (itsIntBuf, Int(aShape(i))); replaceData (offset,itsIntSize, itsIntBuf); } CanonicalConversion::fromLocal (itsIntBuf,1); replaceData (offset,itsIntSize, itsIntBuf); } for (uInt i=0;i size_t // CanonicalConversion::fromLocal (itsIntBuf, uInt(aString[i].length())); replaceData (offset,itsIntSize, itsIntBuf); replaceData (offset,aString[i].length(), aString[i].chars()); } string.freeStorage(aString,deleteIt); // remove additional space left (if any) if (length - totalLength > 0) { remove (itsCurrentBucket, offset, length-totalLength); } } void SSMStringHandler::replace(Int bucketNr, Int offset, Int length, Int totalLength, const IPosition& aShape) { // Check if current bucket is wanted bucket, else get wanted bucket. if (bucketNr != itsCurrentBucket) { getBucket(bucketNr); } CanonicalConversion::fromLocal (itsIntBuf, uInt(aShape.nelements())); replaceData (offset,itsIntSize, itsIntBuf); for (uInt i=0; i< aShape.nelements();i++) { CanonicalConversion::fromLocal (itsIntBuf, Int(aShape(i))); replaceData (offset,itsIntSize, itsIntBuf); } // write 'empty' flag CanonicalConversion::fromLocal (itsIntBuf,0); replaceData (offset,itsIntSize, itsIntBuf); // remove additional space left (if any) if (length - totalLength > 0) { remove (itsCurrentBucket, offset, length-totalLength); } } void SSMStringHandler::replaceData (Int& offset,Int length, const Char* data) { while (length > 0) { Int nCopy = length; if (length > itsLength-offset) { nCopy = itsLength-offset; } length -= nCopy; memcpy (itsData+offset, data, nCopy); data += nCopy; offset += nCopy; isChanged=True; if (length > 0) { offset = 0; getBucket (itsNextBucket); } } } void SSMStringHandler::put (Int& bucketNr, Int& offset, Int& length, const String& string) { if (length > 0) { if (static_cast(string.length()) > length || string.length() == 0) { remove (bucketNr, offset, length); bucketNr = 0; offset = 0; length = 0; } } if (string.length() == 0) { return; } if (length > 0) { if (itsCurrentBucket != bucketNr) { getBucket(bucketNr); } replace(bucketNr, offset, length, string); length = string.length(); return; } if (itsLastBucket == -1) { getNewBucket(False); } else if (itsCurrentBucket != itsLastBucket) { getBucket(itsLastBucket); } // if Bucket available but string doesn't fit and space < 50 get // a new bucket anyway. if (static_cast(string.length()) > itsLength-itsUsedLength && itsLength-itsUsedLength < 50 ) { getNewBucket(False); } offset = itsUsedLength; bucketNr = itsCurrentBucket; length = string.length(); putData (length, string.chars()); } void SSMStringHandler::put (Int& bucketNr, Int& offset, Int& length, const Array& string, Bool handleShape) { const IPosition& aShape = string.shape(); Int totalLength=0; Bool deleteIt; const String *aString = string.getStorage(deleteIt); for (uInt i=0;i 0) { if (totalLength > length || totalLength == 0) { remove (bucketNr, offset, length); bucketNr = 0; offset = 0; length = 0; } } if (totalLength == 0) { string.freeStorage(aString,deleteIt); return; } if (length > 0) { if (itsCurrentBucket != bucketNr) { getBucket(bucketNr); } replace(bucketNr, offset, length, totalLength,string,handleShape); length = totalLength; string.freeStorage(aString,deleteIt); return; } if (itsLastBucket == -1) { getNewBucket(False); } else if (itsCurrentBucket != itsLastBucket) { getBucket(itsLastBucket); } // if Bucket available but string doesn't fit and space < 50 get // a new bucket anyway. if (totalLength > itsLength-itsUsedLength && itsLength-itsUsedLength < 50 ) { getNewBucket(False); } bucketNr=itsCurrentBucket; offset= itsUsedLength; length= totalLength; if (handleShape) { CanonicalConversion::fromLocal (itsIntBuf, uInt(aShape.nelements())); putData (itsIntSize, itsIntBuf); for (uInt i=0; i< string.ndim();i++) { CanonicalConversion::fromLocal (itsIntBuf, Int(aShape(i))); putData (itsIntSize, itsIntBuf); } CanonicalConversion::fromLocal (itsIntBuf,1); putData (itsIntSize, itsIntBuf); } for (uInt i=0; i< string.nelements();i++) { // // Made it a uInt so the SGI compiler could figure out which overloaded // function to use, since it seemed confused by string::size_t -> size_t // CanonicalConversion::fromLocal (itsIntBuf, uInt(aString[i].length())); putData (itsIntSize, itsIntBuf); putData (aString[i].length(), aString[i].chars()); } string.freeStorage(aString,deleteIt); } void SSMStringHandler::putData (Int length, const Char* data) { while (length > 0) { Int toDo = length; if (toDo > itsLength-itsUsedLength ) { toDo = itsLength-itsUsedLength; } length-=toDo; memcpy (itsData+itsUsedLength, data, toDo); data += toDo; itsNDeleted -= toDo; itsUsedLength += toDo; isChanged=True; if (length > 0) { getNewBucket(True); } } } void SSMStringHandler::getData (Int length, Char* data,Int& offset) { while (length > 0) { Int nCopy = itsUsedLength-offset; if (length < nCopy) { nCopy = length; } length -= nCopy; memcpy (data,itsData+offset, nCopy); data += nCopy; offset += nCopy; if (length > 0) { getBucket(itsNextBucket); offset=0; } } } void SSMStringHandler::remove (Int bucketNr, Int offset, Int length) { if (itsCurrentBucket != bucketNr) { getBucket(bucketNr); } Int n = itsLength-offset; if (length < n) { n = length; } itsNDeleted+=n; // If space was at the end of the bucket, reset used length. if (offset+n == itsUsedLength) { itsUsedLength = offset; } isChanged = True; if (itsNDeleted == itsLength) { itsSSMPtr->removeBucket(itsCurrentBucket); if (itsCurrentBucket == itsLastBucket) { itsLastBucket=-1; } itsCurrentBucket=-1; isChanged = False; } // Check if continuation in next bucket length -= n; if (length > 0) { Int next=itsNextBucket; // We are deleting this concatenated string itsNextBucket=-1; offset=0; remove(next,offset,length); } } void SSMStringHandler::get (String& string, Int bucket, Int offset, Int length) { if (itsCurrentBucket != static_cast(bucket)) { getBucket(bucket); } string.resize (length); // resize storage which adds trailing 0 Char* data = &(string[0]); // get actual string getData(length,data,offset); // terminate string for old strings #ifdef USE_OLD_STRING data[length] = '\0'; #endif } void SSMStringHandler::get (Array& string, Int bucket, Int offset, Int length, Bool handleShape) { IPosition aShape; uInt aFilledFlag=0; String emptyString; if (length >0) { if (itsCurrentBucket != static_cast(bucket)) { getBucket(bucket); } aFilledFlag=1; if (handleShape) { getShape(aShape,bucket,offset,length); DebugAssert (aShape.isEqual (string.shape()),AipsError); // get the 'filled' flag getData (itsIntSize, itsIntBuf,offset); CanonicalConversion::toLocal(aFilledFlag,itsIntBuf); } } Bool deleteIt; String* aString = string.getStorage(deleteIt); for (uInt i=0; i< string.nelements();i++) { if (aFilledFlag == 0) { aString[i] = emptyString; } else { // get length of next string Beware, offset resetting will be done in // getdata, so you don't need to do it here again... getData (itsIntSize, itsIntBuf,offset); Int aL=0; CanonicalConversion::toLocal(aL,itsIntBuf); aString[i].resize (aL); // resize storage which adds trailing 0 Char* aS = &(aString[i][0]); // get actual string // get next string. Beware, offset resetting will be done in // getdata, so you don't need to do it here again... getData (aL, aS, offset); // terminate string #ifdef USE_OLD_STRING aS[aL] = '\0'; #endif } } string.putStorage(aString,deleteIt); } void SSMStringHandler::putShape (Int& bucketNr, Int& offset, Int& length, const IPosition& aShape) { Int totalLength=0; if (itsLastBucket == -1) { getNewBucket(False); } else if (itsCurrentBucket != itsLastBucket) { getBucket(itsLastBucket); } totalLength = (aShape.nelements()+2)*ValType::getCanonicalSize (TpInt); if (length > 0) { if (totalLength > length || totalLength == 0) { remove (bucketNr, offset, length); bucketNr = 0; offset = 0; length = 0; } } if (length > 0) { if (itsCurrentBucket != bucketNr) { getBucket(bucketNr); } replace(bucketNr, offset, length, totalLength,aShape); length = totalLength; return; } if (itsLastBucket == -1) { getNewBucket(False); } else if (itsCurrentBucket != itsLastBucket) { getBucket(itsLastBucket); } // if Bucket available but shape doesn't fit and space < 50 get // a new bucket anyway. if (totalLength > itsLength-itsUsedLength && itsLength-itsUsedLength < 50 ) { getNewBucket(False); } bucketNr=itsCurrentBucket; offset= itsUsedLength; length= totalLength; CanonicalConversion::fromLocal (itsIntBuf, uInt(aShape.nelements())); putData (itsIntSize, itsIntBuf); for (uInt i=0; i< aShape.nelements();i++) { CanonicalConversion::fromLocal (itsIntBuf, Int(aShape(i))); putData (itsIntSize, itsIntBuf); } // write 'empty' flag CanonicalConversion::fromLocal (itsIntBuf,0); putData (itsIntSize, itsIntBuf); } void SSMStringHandler::getShape (IPosition& aShape, Int bucket, Int& offset, Int) { if (itsCurrentBucket != static_cast(bucket)) { getBucket(bucket); } getData (itsIntSize, itsIntBuf,offset); Int nDim=0; CanonicalConversion::toLocal(nDim,itsIntBuf); aShape.resize(nDim); Int tmp; for (Int i=0; i< nDim; i++) { getData (itsIntSize, itsIntBuf,offset); CanonicalConversion::toLocal(tmp, itsIntBuf); aShape(i) = tmp; } } void SSMStringHandler::flush() { if (isChanged) { AlwaysAssert (itsCurrentBucket != -1, AipsError); //save old bucket Char* aPtr = itsSSMPtr->getBucket(itsCurrentBucket); CanonicalConversion::fromLocal (aPtr+itsIntSize, itsUsedLength); CanonicalConversion::fromLocal (aPtr+itsIntSize*2, itsNDeleted); CanonicalConversion::fromLocal (aPtr+itsIntSize*3, itsNextBucket); memcpy (aPtr+itsStart, itsData, itsLength); itsSSMPtr->setBucketDirty(); isChanged = False; } } void SSMStringHandler::getBucket (uInt bucketNr,Bool isNew) { // check if itsCurrentBuffer is in use, if so save this one first flush(); itsCurrentBucket = bucketNr; if (! isNew) { char* aPtr = itsSSMPtr->getBucket(itsCurrentBucket); memcpy (itsData, aPtr+itsStart, itsLength); CanonicalConversion::toLocal (itsUsedLength, aPtr+itsIntSize); CanonicalConversion::toLocal (itsNDeleted, aPtr+2*itsIntSize); CanonicalConversion::toLocal (itsNextBucket, aPtr+3*itsIntSize); } } void SSMStringHandler::getNewBucket(Bool doConcat) { Int bucketNr = itsSSMPtr->getNewBucket(); if (doConcat) { itsNextBucket = bucketNr; // save nextbucket isChanged=True; } getBucket(bucketNr,True); // zero dataspace itsUsedLength = 0; itsNDeleted = itsLength; itsNextBucket=-1; itsLastBucket=itsCurrentBucket; } void SSMStringHandler::resync() { AlwaysAssert (!isChanged,AipsError); itsCurrentBucket = -1; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/SSMStringHandler.h000066400000000000000000000244321476623553700214100ustar00rootroot00000000000000//# SSMStringHandler.h: Store strings in the Standard Storage Manager //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_SSMSTRINGHANDLER_H #define TABLES_SSMSTRINGHANDLER_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations. class SSMBase; // // Store strings in the Standard Storage Manager. // // // // // //# Classes you should understand before using this one. //
      • SSMBase // // // SSMStringHandler handles strings for the Standard Storage Manager. // // // Variable length strings cannot be stored in the data bucket. // Only short (<8 characters) strings can be stored directly. // Class SSMStringhandler is used by the SSM to store strings in // so-called string buckets. // A string bucket has the following layout: //
          //
        • The first Int is reserved to be used for the free bucket list. //
        • itsUsedLength tells how many bytes have been used. // Thus it tells the next free byte in the string part. // In principle it always increases. Only if data are removed // from the last part of the string part, it is decreased, thus // the deleted part can be reused again. //
        • itsNDeleted tells how many bytes of the string part // are deleted (i.e. not used). Initially it is the length of the // string part of the bucket (i.e. bucketsize minus 4 Ints). // When a string is stored, its length is subtracted from itsNDeleted. // When a string is removed, its length is added again. // When the string part is deleted, the bucket is added to the // free bucket list. //
        • itsNextBucket tells the next bucket if the last // entry in the bucket is continued in another bucket. // Normally this field is -1 (meaning not continued), but long // strings or string arrays might be continued in another bucket // (and continued from there again). //
        • The string part is a sequence of bytes containing the string // data. When a value is to be stored, it will replace the current // value if the new value is not longer. Otherwise the current // value (if any) is deleted and the new value is appended to // the end of the string part in the last bucket used. //

          // For a scalar string only its characters are stored. Its length // (and bucketnr and offset in string bucket) are stored in the data // bucket. //
          // A fixed length array is stored as an array of bytes. That byte // array contains length-value pairs for each element of the array. // The total length (and bucketnr and offset) are stored in the data // bucket. //
          // A variable length array is stored as the shape, a flag, optionally // followed by the string array as length-value pairs (as above). // The shape consists of the nr of dimensions followed by the // length of each dimension. The flag indicates if a string array // is actually stored. It is not if only the shape of the array // is set, but no data put yet. //

        // SSMStringHandler keeps a copy of the current bucket in use to reduce // the number of accesses to the bucket cache. //

        // It also keeps the bucket number of the last bucket where data were // added to. It tells which bucket to use when new data has to be stored. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class SSMStringHandler { public: // Default constructor initializes last string bucket to -1. SSMStringHandler (SSMBase* aBase); ~SSMStringHandler(); // Forbid copy constructor. SSMStringHandler (const SSMStringHandler&) = delete; // Forbid assignment. SSMStringHandler& operator= (const SSMStringHandler&) = delete; // Set or get last string bucketnr. // Setting is needed when an existing table is opened. // void setLastStringBucket (Int lastStringBucket); Int lastStringBucket() const; // // Put a single string or an array of strings into a bucket. // If its length does not exceed the given length, it reuses // the currently used space (given by bucketnr and offset). // Otherwise it adds the data to the last string bucket. // It fills the offset and bucketnr where the data are stored and the // length occupied in the buckets. // An array of strings is flattened first (a la SSMColumn::writeString). //
        // If handleShape is True (for variable shaped arrays), the // shape will be put first. // void put (Int& bucketNr, Int& offset, Int& length, const String& string); void put (Int& bucketNr, Int& offset, Int& length, const Array& string, Bool handleShape); // // Put a single string or an array of strings into a bucket. // If its length does not exceed the given length, it reuses // the currently used space (given by bucketnr and offset). // Otherwise it adds the data to the last string bucket. // It fills the offset and bucketnr where stored and the // length occupied in the buckets. void putShape (Int& bucketNr, Int& offset, Int& length, const IPosition& aShape); // Get the shape in the given bucket and offset. // It sets the offset to the data right after the shape. // The IPosition object is resized as needed. void getShape (IPosition& aShape, Int bucket, Int& offset, Int length); // Remove data with the given length from a bucket. // If the data are continued in next bucket(s), they will be // removed there as well. void remove (Int bucketNr, Int offset, Int length); // Get a string or an array of strings. // The array must have the correct shape. // handleShape will be True for variable shaped arrays // indicating that the data are preceeded by the shape. // void get (String& string, Int bucket, Int offset, Int length); void get (Array& string, Int bucket, Int offset, Int length, Bool handleShape); // // Flush the currently used string bucket. void flush(); // Initialize the StringHandler void init(); // Resynchronize (after a table lock was acquired). // It clears the itsCurrentBucket variable to assure that buckets // are reread. void resync(); private: // Get the given bucket and make it current. // It first writes the current bucket if it has changed. //
        // If isNew is True the bucket is new, // so the Ints at its beginning do not have to be interpreted. void getBucket (uInt bucketNr, Bool isNew=False); // Get a new bucket and make it current. // If doConcat is True, the new bucket is a continuation, // so itsNextBucket in the currently used bucket is filled // with the new bucket number. void getNewBucket (Bool doConcat); // Put the data with the given length at the end of the current bucket. // If they do not fit, they are continued in a new bucket. void putData (Int length, const Char* data); // Get the data with the given length from the curent bucket at the // given offset. If sets the offset to the byte after the data read. // Continuation buckets are followed (and made current). void getData (Int length, Char* data, Int& offset); // Replace the current data with the new data. // It is used by put after having assured that the // new length does not exceed the current one. // It follows continuation buckets as needed. // void replace (Int bucketNr, Int offset, Int length, const String& string); void replace (Int bucketNr, Int offset, Int length, Int totalLength, const IPosition& aShape); void replace (Int bucketNr, Int offset, Int length, Int totalLength, const Array& string, Bool handleShape); void replaceData (Int& offset,Int length, const Char* data); // SSMBase* itsSSMPtr; // Pointer to SSMBase stucture Int itsCurrentBucket; // bucketnr of current string bucket (-1 is none) Int itsLength; // length of bucket in use (only the string part) Int itsNDeleted; // #bytes deleted from the string part of the bucket Int itsUsedLength; // #bytes used from the string part of the bucket Int itsNextBucket; // next bucket for long strings char* itsData; // bucket string data char* itsIntBuf; // buffer for initialisation params Bool isChanged; // has current bucket been changed? uInt itsIntSize; // size of integers in this system Int itsLastBucket; // last string bucket used uInt itsStart; // Start position of actual data in bucket }; inline void SSMStringHandler::setLastStringBucket (Int lastStringBucket) { itsLastBucket = lastStringBucket; } inline Int SSMStringHandler::lastStringBucket() const { return itsLastBucket; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/ScaledArrayEngine.h000066400000000000000000000322651476623553700216040ustar00rootroot00000000000000//# ScaledArrayEngine.h: Templated virtual column engine to scale a table array //# Copyright (C) 1994,1995,1996,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_SCALEDARRAYENGINE_H #define TABLES_SCALEDARRAYENGINE_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class ScalarColumn; //

        // Templated virtual column engine to scale a table array // // // // // //# Classes you should understand before using this one. //
      • VirtualColumnEngine //
      • VirtualArrayColumn // // // ScaledArrayEngine is a virtual column engine which scales an array // of one type to another type to save disk storage. // This resembles the classic AIPS compress method which scales the // data from float to short. // The scale factor and offset value can be given in two ways: //
          //
        • As a fixed value which is used for all arrays in the column. //
        • As the name of a column. In this way each array in a // column can have its own scale and offset value. // The scale and offset value in a row must be put before // the array is put and should not be changed anymore. //
        // It is also possible to have a variable scale factor with a fixed offset // value. // As in FITS the scale and offset values are used as: //
        True_value = Stored_value * scale + offset; // // An engine object should be used for one column only, because the stored // column name is part of the engine. If it would be used for more than // one column, they would all share the same stored column. // When the engine is bound to a column, it is checked if the name // of that column matches the given virtual column name. // // The engine can be used for a column containing any kind of array // (thus direct or indirect, fixed or variable shaped)) as long as the // virtual array can be stored in the stored array. Thus a fixed shaped // virtual can use a variable shaped stored, but not vice versa. // A fixed shape indirect virtual can use a stored with direct arrays. // // This class can also serve as an example of how to implement // a virtual column engine. //
        // // This class allows to store data in a smaller representation. // It is needed to resemble the classic AIPS compress option. // It adds the scale and offset value on a per row basis. // // Because the engine can serve only one column, it was possible to // combine the engine and the column functionality in one class. // This has been achieved using multiple inheritance. // The advantage of this is that only one templated class is used, // so less template instantiations are needed. // // // // // Create the table description and 2 columns with indirect arrays in it. // // The Int column will be stored, while the double will be // // used as virtual. // TableDesc tableDesc ("", TableDesc::Scratch); // tableDesc.addColumn (ArrayColumnDesc ("storedArray")); // tableDesc.addColumn (ArrayColumnDesc ("virtualArray")); // // // Create a new table using the table description. // SetupNewTable newtab (tableDesc, "tab.data", Table::New); // // // Create the array scaling engine to scale from double to Int // // and bind it to the double column. // // Create the table. // ScaledArrayEngine scalingEngine("virtualArray", // "storedArray", 10); // newtab.bindColumn ("virtualArray", scalingEngine); // Table table (newtab); // // // Store a 3-D array (with dim. 2,3,4) into each row of the column. // // The shape of each array in the column is implicitly set by the put // // function. This will also set the shape of the underlying Int array. // ArrayColumn data (table, "virtualArray"); // Array someArray(IPosition(4,2,3,4)); // someArray = 0; // for (rownr_t i=0, i<10; i++) { // table will have 10 rows // table.addRow(); // data.put (i, someArray) // } // // // //
      • only suited for built-in numerics data types // // //
      • only suited for built-in numerics data types // template class ScaledArrayEngine : public BaseMappedArrayEngine { //# Make members of parent class known. public: using BaseMappedArrayEngine::virtualName; protected: using BaseMappedArrayEngine::storedName; using BaseMappedArrayEngine::table; using BaseMappedArrayEngine::column; using BaseMappedArrayEngine::setNames; public: // Construct an engine to scale all arrays in a column with // the given offset and scale factor. // StoredColumnName is the name of the column where the scaled // data will be put and must have data type StoredType. // The virtual column using this engine must have data type VirtualType. ScaledArrayEngine (const String& virtualColumnName, const String& storedColumnName, VirtualType scale, VirtualType offset = 0); // Construct an engine to scale the arrays in a column. // The scale and offset values are taken from a column with // the given names. In that way each array has its own scale factor // and offset value. // An exception is thrown if these columns do not exist. // VirtualColumnName is the name of the virtual column and is used to // check if the engine gets bound to the correct column. // StoredColumnName is the name of the column where the scaled // data will be put and must have data type StoredType. // The virtual column using this engine must have data type VirtualType. // ScaledArrayEngine (const String& virtualColumnName, const String& storedColumnName, const String& scaleColumnName, VirtualType offset = 0); ScaledArrayEngine (const String& virtualColumnName, const String& storedColumnName, const String& scaleColumnName, const String& offsetColumnName); // // Construct from a record specification as created by getmanagerSpec(). ScaledArrayEngine (const Record& spec); // Destructor is mandatory. ~ScaledArrayEngine(); // Assignment is not needed and therefore forbidden. ScaledArrayEngine& operator= (const ScaledArrayEngine&) = delete; // Return the type name of the engine (i.e. its class name). virtual String dataManagerType() const; // Get the name given to the engine (is the virtual column name). virtual String dataManagerName() const; // Record a record containing data manager specifications. virtual Record dataManagerSpec() const; // Return the name of the class. // This includes the names of the template arguments. static String className(); // Register the class name and the static makeObject "constructor". // This will make the engine known to the table system. // The automatically invoked registration function in DataManReg.cc // contains ScaledArrayEngine. // Any other instantiation of this class must be registered "manually" // (or added to DataManReg.cc). static void registerClass(); private: // Copy constructor is only used by clone(). // (so it is made private). ScaledArrayEngine (const ScaledArrayEngine&); // Clone the engine object. DataManager* clone() const; // Initialize the object for a new table. // It defines the keywords containing the engine parameters. void create64 (rownr_t initialNrrow); // Preparing consists of setting the writable switch and // adding the initial number of rows in case of create. // Furthermore it reads the keywords containing the engine parameters. void prepare(); // Get an array in the given row. // This will scale and offset from the underlying array. void getArray (rownr_t rownr, Array& array); // Put an array in the given row. // This will scale and offset to the underlying array. void putArray (rownr_t rownr, const Array& array); // Get a section of the array in the given row. // This will scale and offset from the underlying array. void getSlice (rownr_t rownr, const Slicer& slicer, Array& array); // Put into a section of the array in the given row. // This will scale and offset to the underlying array. void putSlice (rownr_t rownr, const Slicer& slicer, const Array& array); // Get an entire column. // This will scale and offset from the underlying array. void getArrayColumn (Array& array); // Put an entire column. // This will scale and offset to the underlying array. void putArrayColumn (const Array& array); // Get a section of all arrays in the column. // This will scale and offset from the underlying array. void getColumnSlice (const Slicer& slicer, Array& array); // Put a section of all arrays in the column. // This will scale and offset to the underlying array. void putColumnSlice (const Slicer& slicer, const Array& array); // Scale and/or offset stored to array. // This is meant when reading an array from the stored column. // It optimizes for scale=1 and/or offset=0. void scaleOnGet (VirtualType scale, VirtualType offset, Array& array, const Array& stored); // Scale and/or offset array to stored. // This is meant when writing an array into the stored column. // It optimizes for scale=1 and/or offset=0. void scaleOnPut (VirtualType scale, VirtualType offset, const Array& array, Array& stored); // Scale and/or offset stored to array for the entire column. // When the scale and offset are fixed, it will do the entire array. // Otherwise it iterates through the array and applies the scale // and offset per row. void scaleColumnOnGet (Array& array, const Array& stored); // Scale and/or offset array to stored for the entire column. // When the scale and offset are fixed, it will do the entire array. // Otherwise it iterates through the array and applies the scale // and offset per row. void scaleColumnOnPut (const Array& array, Array& stored); //# Now define the data members. String scaleName_p; //# name of scale column String offsetName_p; //# name of offset column VirtualType scale_p; //# scale factor VirtualType offset_p; //# offset value Bool fixedScale_p; //# scale is a fixed column Bool fixedOffset_p; //# scale is a fixed column ScalarColumn* scaleColumn_p; //# column with scale value ScalarColumn* offsetColumn_p; //# column with offset value // Get the scale value for this row. VirtualType getScale (rownr_t rownr); // Get the offset value for this row. VirtualType getOffset (rownr_t rownr); public: //*display 4 // Define the "constructor" to construct this engine when a // table is read back. // This "constructor" has to be registered by the user of the engine. // If the engine is commonly used, its registration can be added // to the registerAllCtor function in DataManReg.cc. // That function gets automatically invoked by the table system. static DataManager* makeObject (const String& dataManagerType, const Record& spec); }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/tables/DataMan/ScaledArrayEngine.tcc000066400000000000000000000311141476623553700221160ustar00rootroot00000000000000//# ScaledArrayEngine.cc: Templated virtual column engine to scale a table array //# Copyright (C) 1994,1995,1996,1997,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_SCALEDARRAYENGINE_TCC #define TABLES_SCALEDARRAYENGINE_TCC //# Includes #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template ScaledArrayEngine::ScaledArrayEngine (const String& virtualColumnName, const String& storedColumnName, S scale, S offset) : BaseMappedArrayEngine (virtualColumnName, storedColumnName), scale_p (scale), offset_p (offset), fixedScale_p (True), fixedOffset_p (True), scaleColumn_p (0), offsetColumn_p(0) {} template ScaledArrayEngine::ScaledArrayEngine (const String& virtualColumnName, const String& storedColumnName, const String& scaleColumnName, S offset) : BaseMappedArrayEngine (virtualColumnName, storedColumnName), scaleName_p (scaleColumnName), scale_p (0.0), offset_p (offset), fixedScale_p (False), fixedOffset_p (True), scaleColumn_p (0), offsetColumn_p(0) {} template ScaledArrayEngine::ScaledArrayEngine (const String& virtualColumnName, const String& storedColumnName, const String& scaleColumnName, const String& offsetColumnName) : BaseMappedArrayEngine (virtualColumnName, storedColumnName), scaleName_p (scaleColumnName), offsetName_p (offsetColumnName), scale_p (0.0), offset_p (0.0), fixedScale_p (False), fixedOffset_p (False), scaleColumn_p (0), offsetColumn_p(0) {} template ScaledArrayEngine::ScaledArrayEngine (const Record& spec) : BaseMappedArrayEngine (), scale_p (1.0), offset_p (0.0), fixedScale_p (True), fixedOffset_p (True), scaleColumn_p (0), offsetColumn_p(0) { if (spec.isDefined("SOURCENAME") && spec.isDefined("TARGETNAME")) { setNames (spec.asString("SOURCENAME"), spec.asString("TARGETNAME")); if (spec.isDefined("SCALE")) { spec.get ("SCALE", scale_p); } else { spec.get ("SCALENAME", scaleName_p); fixedScale_p = False; } if (spec.isDefined("OFFSET")) { spec.get ("OFFSET", offset_p); } else { spec.get ("OFFSETNAME", offsetName_p); fixedOffset_p = False; } } } template ScaledArrayEngine::ScaledArrayEngine (const ScaledArrayEngine& that) : BaseMappedArrayEngine (that), scaleName_p (that.scaleName_p), offsetName_p (that.offsetName_p), scale_p (that.scale_p), offset_p (that.offset_p), fixedScale_p (that.fixedScale_p), fixedOffset_p (that.fixedOffset_p), scaleColumn_p (0), offsetColumn_p(0) {} template ScaledArrayEngine::~ScaledArrayEngine() { delete scaleColumn_p; delete offsetColumn_p; } //# Clone the engine object. template DataManager* ScaledArrayEngine::clone() const { DataManager* dmPtr = new ScaledArrayEngine (*this); return dmPtr; } //# Return the type name of the engine (i.e. its class name). template String ScaledArrayEngine::dataManagerType() const { return className(); } //# Return the class name. //# Get the data type names using class ValType. template String ScaledArrayEngine::className() { return "ScaledArrayEngine<" + valDataTypeId (static_cast(0)) + "," + valDataTypeId (static_cast(0)) + ">"; } template String ScaledArrayEngine::dataManagerName() const { return virtualName(); } template Record ScaledArrayEngine::dataManagerSpec() const { Record spec; spec.define ("SOURCENAME", virtualName()); spec.define ("TARGETNAME", storedName()); if (fixedScale_p) { spec.define ("SCALE", scale_p); } else { spec.define ("SCALENAME", scaleName_p); } if (fixedOffset_p) { spec.define ("SCALE", scale_p); } else { spec.define ("SCALENAME", scaleName_p); } return spec; } template DataManager* ScaledArrayEngine::makeObject (const String&, const Record& spec) { DataManager* dmPtr = new ScaledArrayEngine(spec); return dmPtr; } template void ScaledArrayEngine::registerClass() { DataManager::registerCtor (className(), makeObject); } template void ScaledArrayEngine::create64 (rownr_t initialNrrow) { BaseMappedArrayEngine::create64 (initialNrrow); // Store the various parameters as keywords in this column. TableColumn thisCol (table(), virtualName()); thisCol.rwKeywordSet().define ("_ScaledArrayEngine_Scale", scale_p); thisCol.rwKeywordSet().define ("_ScaledArrayEngine_Offset", offset_p); thisCol.rwKeywordSet().define ("_ScaledArrayEngine_ScaleName", scaleName_p); thisCol.rwKeywordSet().define ("_ScaledArrayEngine_OffsetName", offsetName_p); thisCol.rwKeywordSet().define ("_ScaledArrayEngine_FixedScale", fixedScale_p); thisCol.rwKeywordSet().define ("_ScaledArrayEngine_FixedOffset", fixedOffset_p); } template void ScaledArrayEngine::prepare() { BaseMappedArrayEngine::prepare(); TableColumn thisCol (table(), virtualName()); thisCol.keywordSet().get ("_ScaledArrayEngine_Scale", scale_p); thisCol.keywordSet().get ("_ScaledArrayEngine_Offset", offset_p); thisCol.keywordSet().get ("_ScaledArrayEngine_ScaleName", scaleName_p); thisCol.keywordSet().get ("_ScaledArrayEngine_OffsetName", offsetName_p); thisCol.keywordSet().get ("_ScaledArrayEngine_FixedScale", fixedScale_p); thisCol.keywordSet().get ("_ScaledArrayEngine_FixedOffset", fixedOffset_p); //# Allocate column objects to get scale and offset. if (! fixedScale_p) { scaleColumn_p = new ScalarColumn (table(), scaleName_p); } if (! fixedOffset_p) { offsetColumn_p = new ScalarColumn (table(), offsetName_p); } } template S ScaledArrayEngine::getScale (rownr_t rownr) { if (fixedScale_p) { return scale_p; } return (*scaleColumn_p)(rownr); } template S ScaledArrayEngine::getOffset (rownr_t rownr) { if (fixedOffset_p) { return offset_p; } return (*offsetColumn_p)(rownr); } // Scale/offset an array for get. template void ScaledArrayEngine::scaleOnGet (S scale, S offset, Array& array, const Array& target) { Bool deleteIn, deleteOut; S* out = array.getStorage (deleteOut); S* op = out; const T* in = target.getStorage (deleteIn); const T* ip = in; const T* last = ip + array.nelements(); if (offset == 0) { if (scale == 1) { while (ip < last) { *op++ = *ip++; } }else{ while (ip < last) { *op++ = *ip++ * scale; } } }else{ if (scale == 1) { while (ip < last) { *op++ = *ip++ + offset; } }else{ while (ip < last) { *op++ = *ip++ * scale + offset; } } } target.freeStorage (in, deleteIn); array.putStorage (out, deleteOut); } // Scale/offset an array for put. template void ScaledArrayEngine::scaleOnPut (S scale, S offset, const Array& array, Array& target) { Bool deleteIn, deleteOut; const S* in = array.getStorage (deleteIn); const S* ip = in; T* out = target.getStorage (deleteOut); T* op = out; const T* last = op + array.nelements(); if (offset == 0) { if (scale == 1) { while (op < last) { *op++ = T(*ip++); } }else{ while (op < last) { *op++ = T(*ip++ / scale); } } }else{ if (scale == 1) { while (op < last) { *op++ = T(*ip++ - offset); } }else{ while (op < last) { *op++ = T((*ip++ - offset) / scale); } } } array.freeStorage (in, deleteIn); target.putStorage (out, deleteOut); } template void ScaledArrayEngine::scaleColumnOnGet (Array& array, const Array& target) { if (fixedScale_p && fixedOffset_p) { scaleOnGet (scale_p, offset_p, array, target); }else{ ArrayIterator arrayIter (array, array.ndim() - 1); ReadOnlyArrayIterator targetIter (target, target.ndim() - 1); rownr_t rownr = 0; while (! arrayIter.pastEnd()) { scaleOnGet (getScale(rownr), getOffset(rownr), arrayIter.array(), targetIter.array()); rownr++; arrayIter.next(); targetIter.next(); } } } template void ScaledArrayEngine::scaleColumnOnPut (const Array& array, Array& target) { if (fixedScale_p && fixedOffset_p) { scaleOnPut (scale_p, offset_p, array, target); }else{ ReadOnlyArrayIterator arrayIter (array, array.ndim() - 1); ArrayIterator targetIter (target, target.ndim() - 1); rownr_t rownr = 0; while (! arrayIter.pastEnd()) { scaleOnPut (getScale(rownr), getOffset(rownr), arrayIter.array(), targetIter.array()); rownr++; arrayIter.next(); targetIter.next(); } } } template void ScaledArrayEngine::getArray (rownr_t rownr, Array& array) { Array target(array.shape()); column().get (rownr, target); scaleOnGet (getScale(rownr), getOffset(rownr), array, target); } template void ScaledArrayEngine::putArray (rownr_t rownr, const Array& array) { Array target(array.shape()); scaleOnPut (getScale(rownr), getOffset(rownr), array, target); column().put (rownr, target); } template void ScaledArrayEngine::getSlice (rownr_t rownr, const Slicer& slicer, Array& array) { Array target(array.shape()); column().getSlice (rownr, slicer, target); scaleOnGet (getScale(rownr), getOffset(rownr), array, target); } template void ScaledArrayEngine::putSlice (rownr_t rownr, const Slicer& slicer, const Array& array) { Array target(array.shape()); scaleOnPut (getScale(rownr), getOffset(rownr), array, target); column().putSlice (rownr, slicer, target); } template void ScaledArrayEngine::getArrayColumn (Array& array) { Array target(array.shape()); column().getColumn (target); scaleColumnOnGet (array, target); } template void ScaledArrayEngine::putArrayColumn (const Array& array) { Array target(array.shape()); scaleColumnOnPut (array, target); column().putColumn (target); } template void ScaledArrayEngine::getColumnSlice (const Slicer& slicer, Array& array) { Array target(array.shape()); column().getColumn (slicer, target); scaleColumnOnGet (array, target); } template void ScaledArrayEngine::putColumnSlice (const Slicer& slicer, const Array& array) { Array target(array.shape()); scaleColumnOnPut (array, target); column().putColumn (slicer, target); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/ScaledComplexData.h000066400000000000000000000411721476623553700215760ustar00rootroot00000000000000//# ScaledComplexData.h: Templated virtual column engine to scale a complex table array //# Copyright (C) 1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_SCALEDCOMPLEXDATA_H #define TABLES_SCALEDCOMPLEXDATA_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class ScalarColumn; // // Templated virtual column engine to scale a complex table array // // // // // //# Classes you should understand before using this one. //
      • VirtualColumnEngine //
      • VirtualArrayColumn // // // ScaledComplexData is a virtual column engine which scales an array // of a complex type to 2 values of another type (to save disk storage). // For example, ScaledComplexData resembles the // classic AIPS compress method which scales the data from float to short. // The (complex) scale factor and offset value can be given in two ways: //
          //
        • As a fixed value which is used for all arrays in the column. //
        • As the name of a column. In this way each array in a // column can have its own scale and offset value. // The scale and offset value in a row must be put before // the array is put and should not be changed anymore. //
        // It is also possible to have a variable scale factor with a fixed offset // value. // As in FITS the scale and offset values are used as: //
        True_value = Stored_value * scale + offset; // // An engine object should be used for one column only, because the stored // column name is part of the engine. If it would be used for more than // one column, they would all share the same stored column. // When the engine is bound to a column, it is checked if the name // of that column matches the given virtual column name. // // The engine can be used for a column containing any kind of array // (thus direct or indirect, fixed or variable shaped)) as long as the // virtual array can be stored in the stored array. Thus a fixed shaped // virtual can use a variable shaped stored, but not vice versa. // A fixed shape indirect virtual can use a stored with direct arrays. // // This class can also serve as an example of how to implement // a virtual column engine. //
        // // This class allows to store data in a smaller representation. // It is needed to resemble the classic AIPS compress option. // It adds the scale and offset value on a per row basis. // // Because the engine can serve only one column, it was possible to // combine the engine and the column functionality in one class. // This has been achieved using multiple inheritance. // The advantage of this is that only one templated class is used, // so less template instantiations are needed. // // Class ScaledArrayEngine could not be used, because complex integer // types are not supported in the tabe system. // // // // // Create the table description and 2 columns with indirect arrays in it. // // The Int column will be stored, while the double will be // // used as virtual. // TableDesc tableDesc ("", TableDesc::Scratch); // tableDesc.addColumn (ArrayColumnDesc ("storedArray")); // tableDesc.addColumn (ArrayColumnDesc ("virtualArray")); // // // Create a new table using the table description. // SetupNewTable newtab (tableDesc, "tab.data", Table::New); // // // Create the array scaling engine to scale from double to Int // // and bind it to the double column. // // Create the table. // ScaledComplexData scalingEngine("virtualArray", // "storedArray", 10); // newtab.bindColumn ("virtualArray", scalingEngine); // Table table (newtab); // // // Store a 2-D array (with dim. 3,4) into each row of the column. // // The shape of each array in the column is implicitly set by the put // // function. This will also set the shape of the underlying Int array // // (as a 3-D array with shape 2,3,4). // ArrayColumn data (table, "virtualArray"); // Array someArray(IPosition(2,3,4)); // someArray = 0; // for (rownr_t i=0, i<10; i++) { // table will have 10 rows // table.addRow(); // data.put (i, someArray) // } // // // //
      • only complex data types // // //
      • only built-in numerics data types // template class ScaledComplexData : public BaseMappedArrayEngine { //# Make members of parent class known. public: using BaseMappedArrayEngine::virtualName; protected: using BaseMappedArrayEngine::storedName; using BaseMappedArrayEngine::table; using BaseMappedArrayEngine::column; using BaseMappedArrayEngine::setNames; public: // Construct an engine to scale all arrays in a column with // the given offset and scale factor. // StoredColumnName is the name of the column where the scaled // data will be put and must have data type StoredType. // The virtual column using this engine must have data type VirtualType. ScaledComplexData (const String& virtualColumnName, const String& storedColumnName, VirtualType scale, VirtualType offset = 0); // Construct an engine to scale the arrays in a column. // The scale and offset values are taken from a column with // the given names. In that way each array has its own scale factor // and offset value. // An exception is thrown if these columns do not exist. // VirtualColumnName is the name of the virtual column and is used to // check if the engine gets bound to the correct column. // StoredColumnName is the name of the column where the scaled // data will be put and must have data type StoredType. // The virtual column using this engine must have data type VirtualType. // ScaledComplexData (const String& virtualColumnName, const String& storedColumnName, const String& scaleColumnName, VirtualType offset = 0); ScaledComplexData (const String& virtualColumnName, const String& storedColumnName, const String& scaleColumnName, const String& offsetColumnName); // // Construct from a record specification as created by getmanagerSpec(). ScaledComplexData (const Record& spec); // Destructor is mandatory. ~ScaledComplexData(); // Assignment is not needed and therefore forbidden. ScaledComplexData& operator= (const ScaledComplexData&) = delete; // Return the type name of the engine (i.e. its class name). String dataManagerType() const; // Record a record containing data manager specifications. virtual Record dataManagerSpec() const; // Return the name of the class. // This includes the names of the template arguments. static String className(); // Register the class name and the static makeObject "constructor". // This will make the engine known to the table system. // The automatically invoked registration function in DataManReg.cc // contains ScaledComplexData. // Any other instantiation of this class must be registered "manually" // (or added to DataManReg.cc). static void registerClass(); private: // The default constructor is required for reconstruction of the // engine when a table is read back. ScaledComplexData(); // Copy constructor is only used by clone(). // (so it is made private). ScaledComplexData (const ScaledComplexData&); // Clone the engine object. virtual DataManager* clone() const; // Initialize the object for a new table. // It defines the keywords containing the engine parameters. virtual void create64 (rownr_t initialNrrow); // Preparing consists of setting the writable switch and // adding the initial number of rows in case of create. // Furthermore it reads the keywords containing the engine parameters. virtual void prepare(); // Set the shape of the FixedShape arrays in the column. // This function only gets called if the column has FixedShape arrays. // The shape gets saved and used to set the shape of the arrays // in the stored in case the stored has non-FixedShape arrays. virtual void setShapeColumn (const IPosition& shape); // Define the shape of the array in the given row. // When the shape of the (underlying) stored array has already been // defined, it checks whether its latter dimensions match the given // virtual shape. When matching, nothing will be done. // When mismatching or when the stored shape has not been defined // yet, the stored shape will be defined from the virtual shape and // the virtual element shape. // E.g. in case of a StokesVector a virtual shape of (512,512) // results in a stored shape of (4,512,512). virtual void setShape (rownr_t rownr, const IPosition& shape); // Get the dimensionality of the array in the given row. virtual uInt ndim (rownr_t rownr); // Get the shape of the array in the given row. // This is done by stripping the first dimension from the shape // of the underlying stored array. virtual IPosition shape (rownr_t rownr); // Get an array in the given row. // This will scale and offset from the underlying array. virtual void getArray (rownr_t rownr, Array& array); // Put an array in the given row. // This will scale and offset to the underlying array. virtual void putArray (rownr_t rownr, const Array& array); // Get a section of the array in the given row. // This will scale and offset from the underlying array. virtual void getSlice (rownr_t rownr, const Slicer& slicer, Array& array); // Put into a section of the array in the given row. // This will scale and offset to the underlying array. virtual void putSlice (rownr_t rownr, const Slicer& slicer, const Array& array); // Get an entire column. // This will scale and offset from the underlying array. virtual void getArrayColumn (Array& array); // Put an entire column. // This will scale and offset to the underlying array. virtual void putArrayColumn (const Array& array); // Get some array values in the column. // This will scale and offset from the underlying array. virtual void getArrayColumnCells (const RefRows& rownrs, Array& data); // Put some array values in the column. // This will scale and offset to the underlying array. virtual void putArrayColumnCells (const RefRows& rownrs, const Array& data); // Get a section of all arrays in the column. // This will scale and offset from the underlying array. virtual void getColumnSlice (const Slicer& slicer, Array& array); // Put a section of all arrays in the column. // This will scale and offset to the underlying array. virtual void putColumnSlice (const Slicer& slicer, const Array& array); // Get a section of some arrays in the column. // This will scale and offset from the underlying array. virtual void getColumnSliceCells (const RefRows& rownrs, const Slicer& slicer, Array& data); // Put into a section of some arrays in the column. // This will scale and offset to the underlying array. virtual void putColumnSliceCells (const RefRows& rownrs, const Slicer& slicer, const Array& data); // Scale and/or offset stored to array. // This is meant when reading an array from the stored column. // It optimizes for scale=1 and/or offset=0. void scaleOnGet (VirtualType scale, VirtualType offset, Array& array, const Array& stored); // Scale and/or offset array to stored. // This is meant when writing an array into the stored column. // It optimizes for scale=1 and/or offset=0. void scaleOnPut (VirtualType scale, VirtualType offset, const Array& array, Array& stored); // Scale and/or offset stored to array for the entire column. // When the scale and offset are fixed, it will do the entire array. // Otherwise it iterates through the array and applies the scale // and offset per row. void scaleColumnOnGet (Array& array, const Array& stored); // Scale and/or offset array to stored for the entire column. // When the scale and offset are fixed, it will do the entire array. // Otherwise it iterates through the array and applies the scale // and offset per row. void scaleColumnOnPut (const Array& array, Array& stored); // Scale and/or offset stored to array for some cells in the column. // When the scale and offset are fixed, it will do the entire array. // Otherwise it iterates through the array and applies the scale // and offset per row. void scaleCellsOnGet (Array& array, const Array& stored, const RefRows& rownrs); // Scale and/or offset array to stored for some cells in the column. // When the scale and offset are fixed, it will do the entire array. // Otherwise it iterates through the array and applies the scale // and offset per row. void scaleCellsOnPut (const Array& array, Array& stored, const RefRows& rownrs); // Determine the shape of an array in the stored column. IPosition storedShape (const IPosition& virtualShape) const { return IPosition(1,2).concatenate (virtualShape); } // Convert the Slicer for a virtual to a Slicer for the stored. Slicer storedSlicer (const Slicer& virtualSlicer) const; //# Now define the data members. String scaleName_p; //# name of scale column String offsetName_p; //# name of offset column VirtualType scale_p; //# scale factor VirtualType offset_p; //# offset value Bool fixedScale_p; //# scale is a fixed column Bool fixedOffset_p; //# offset is a fixed column ScalarColumn* scaleColumn_p; //# column with scale value ScalarColumn* offsetColumn_p; //# column with offset value // Get the scale value for this row. VirtualType getScale (rownr_t rownr); // Get the offset value for this row. VirtualType getOffset (rownr_t rownr); public: // Define the "constructor" to construct this engine when a // table is read back. // This "constructor" has to be registered by the user of the engine. // If the engine is commonly used, its registration can be added // to the registerAllCtor function in DataManReg.cc. // That function gets automatically invoked by the table system. static DataManager* makeObject (const String& dataManagerType, const Record& spec); }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/tables/DataMan/ScaledComplexData.tcc000066400000000000000000000431371476623553700221230ustar00rootroot00000000000000//# ScaledComplexData.cc: Templated virtual column engine to scale a complex table array //# Copyright (C) 1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_SCALEDCOMPLEXDATA_TCC #define TABLES_SCALEDCOMPLEXDATA_TCC //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template ScaledComplexData::ScaledComplexData (const String& virtualColumnName, const String& storedColumnName, S scale, S offset) : BaseMappedArrayEngine (virtualColumnName, storedColumnName), scale_p (scale), offset_p (offset), fixedScale_p (True), fixedOffset_p (True), scaleColumn_p (0), offsetColumn_p(0) {} template ScaledComplexData::ScaledComplexData (const String& virtualColumnName, const String& storedColumnName, const String& scaleColumnName, S offset) : BaseMappedArrayEngine (virtualColumnName, storedColumnName), scaleName_p (scaleColumnName), scale_p (S(0.0, 0.0)), offset_p (offset), fixedScale_p (False), fixedOffset_p (True), scaleColumn_p (0), offsetColumn_p(0) {} template ScaledComplexData::ScaledComplexData (const String& virtualColumnName, const String& storedColumnName, const String& scaleColumnName, const String& offsetColumnName) : BaseMappedArrayEngine (virtualColumnName, storedColumnName), scaleName_p (scaleColumnName), offsetName_p (offsetColumnName), scale_p (S(0.0, 0.0)), offset_p (S(0.0, 0.0)), fixedScale_p (False), fixedOffset_p (False), scaleColumn_p (0), offsetColumn_p(0) {} template ScaledComplexData::ScaledComplexData (const ScaledComplexData& that) : BaseMappedArrayEngine (that), scaleName_p (that.scaleName_p), offsetName_p (that.offsetName_p), scale_p (that.scale_p), offset_p (that.offset_p), fixedScale_p (that.fixedScale_p), fixedOffset_p (that.fixedOffset_p), scaleColumn_p (0), offsetColumn_p(0) {} template ScaledComplexData::ScaledComplexData (const Record& spec) : BaseMappedArrayEngine (), scale_p (S(1.0, 1.0)), offset_p (S(0.0, 0.0)), fixedScale_p (True), fixedOffset_p (True), scaleColumn_p (0), offsetColumn_p(0) { if (spec.isDefined("SOURCENAME") && spec.isDefined("TARGETNAME")) { setNames (spec.asString("SOURCENAME"), spec.asString("TARGETNAME")); if (spec.isDefined("SCALE")) { spec.get ("SCALE", scale_p); } else { spec.get ("SCALENAME", scaleName_p); fixedScale_p = False; } if (spec.isDefined("OFFSET")) { spec.get ("OFFSET", offset_p); } else { spec.get ("OFFSETNAME", offsetName_p); fixedOffset_p = False; } } } template ScaledComplexData::~ScaledComplexData() { delete scaleColumn_p; delete offsetColumn_p; } //# Clone the engine object. template DataManager* ScaledComplexData::clone() const { DataManager* dmPtr = new ScaledComplexData (*this); return dmPtr; } //# Return the type name of the engine (i.e. its class name). template String ScaledComplexData::dataManagerType() const { return className(); } //# Return the class name. //# Get the data type names using class ValType. template String ScaledComplexData::className() { return "ScaledComplexData<" + valDataTypeId ((S*)0) + "," + valDataTypeId ((T*)0) + ">"; } template Record ScaledComplexData::dataManagerSpec() const { Record spec; spec.define ("SOURCENAME", virtualName()); spec.define ("TARGETNAME", storedName()); if (fixedScale_p) { spec.define ("SCALE", scale_p); } else { spec.define ("SCALENAME", scaleName_p); } if (fixedOffset_p) { spec.define ("OFFSET", offset_p); } else { spec.define ("OFFSETNAME", offsetName_p); } return spec; } template DataManager* ScaledComplexData::makeObject (const String&, const Record& spec) { return new ScaledComplexData (spec); } template void ScaledComplexData::registerClass() { DataManager::registerCtor (className(), makeObject); } template void ScaledComplexData::create64 (rownr_t initialNrrow) { BaseMappedArrayEngine::create64 (initialNrrow); // Store the various parameters as keywords in this column. TableColumn thisCol (table(), virtualName()); thisCol.rwKeywordSet().define ("_ScaledComplexData_Scale", scale_p); thisCol.rwKeywordSet().define ("_ScaledComplexData_Offset", offset_p); thisCol.rwKeywordSet().define ("_ScaledComplexData_ScaleName", scaleName_p); thisCol.rwKeywordSet().define ("_ScaledComplexData_OffsetName", offsetName_p); thisCol.rwKeywordSet().define ("_ScaledComplexData_FixedScale", fixedScale_p); thisCol.rwKeywordSet().define ("_ScaledComplexData_FixedOffset", fixedOffset_p); } template void ScaledComplexData::prepare() { BaseMappedArrayEngine::prepare(); TableColumn thisCol (table(), virtualName()); thisCol.keywordSet().get ("_ScaledComplexData_Scale", scale_p); thisCol.keywordSet().get ("_ScaledComplexData_Offset", offset_p); thisCol.keywordSet().get ("_ScaledComplexData_ScaleName", scaleName_p); thisCol.keywordSet().get ("_ScaledComplexData_OffsetName", offsetName_p); thisCol.keywordSet().get ("_ScaledComplexData_FixedScale", fixedScale_p); thisCol.keywordSet().get ("_ScaledComplexData_FixedOffset", fixedOffset_p); //# Allocate column objects to get scale and offset. if (! fixedScale_p) { scaleColumn_p = new ScalarColumn (table(), scaleName_p); } if (! fixedOffset_p) { offsetColumn_p = new ScalarColumn (table(), offsetName_p); } } //# This function is called in case the virtual column has FixedShape arrays. template void ScaledComplexData::setShapeColumn (const IPosition& shape) { BaseMappedArrayEngine::setShapeColumn (storedShape(shape)); } template void ScaledComplexData::setShape (rownr_t rownr, const IPosition& shape) { BaseMappedArrayEngine::setShape (rownr, storedShape(shape)); } template uInt ScaledComplexData::ndim (rownr_t rownr) { return column().ndim (rownr) - 1; } template IPosition ScaledComplexData::shape (rownr_t rownr) { // The virtual shape is the stored shape minus the first dimensions. IPosition storedShape = column().shape (rownr); return storedShape.getLast (storedShape.nelements() - 1); } template S ScaledComplexData::getScale (rownr_t rownr) { if (fixedScale_p) { return scale_p; } return (*scaleColumn_p)(rownr); } template S ScaledComplexData::getOffset (rownr_t rownr) { if (fixedOffset_p) { return offset_p; } return (*offsetColumn_p)(rownr); } // Scale/offset an array for get. template void ScaledComplexData::scaleOnGet (S scale, S offset, Array& array, const Array& target) { Bool deleteIn, deleteOut; S* out = array.getStorage (deleteOut); S* op = out; const T* in = target.getStorage (deleteIn); const T* ip = in; const T* last = ip + target.nelements(); if (offset == S(0.0, 0.0)) { if (scale == S(1.0, 1.0)) { while (ip < last) { /// op->real() = *ip++; /// op->imag() = *ip++; *op = S(*ip, ip[1]); ip++; ip++; op++; } }else{ while (ip < last) { /// op->real() = *ip++ * scale.real(); /// op->imag() = *ip++ * scale.imag(); *op = S(*ip * scale.real(), ip[1] * scale.imag()); ip++; ip++; op++; } } }else{ if (scale == S(1.0, 1.0)) { while (ip < last) { /// op->real() = *ip++ + offset.real(); /// op->imag() = *ip++ + offset.imag(); *op = S(*ip + offset.real(), ip[1] + offset.imag()); ip++; ip++; op++; } }else{ while (ip < last) { /// op->real() = *ip++ * scale.real() + offset.real(); /// op->imag() = *ip++ * scale.imag() + offset.imag(); *op = S(*ip * scale.real() + offset.real(), ip[1] * scale.imag() + offset.imag()); ip++; ip++; op++; } } } target.freeStorage (in, deleteIn); array.putStorage (out, deleteOut); } // Scale/offset an array for put. template void ScaledComplexData::scaleOnPut (S scale, S offset, const Array& array, Array& target) { Bool deleteIn, deleteOut; const S* in = array.getStorage (deleteIn); const S* ip = in; T* out = target.getStorage (deleteOut); T* op = out; const T* last = op + target.nelements(); if (offset == S(0.0, 0.0)) { if (scale == S(1.0, 1.0)) { while (op < last) { *op++ = T(ip->real()); *op++ = T(ip->imag()); ip++; } }else{ while (op < last) { *op++ = T(ip->real() / scale.real()); *op++ = T(ip->imag() / scale.imag()); ip++; } } }else{ if (scale == S(1.0, 1.0)) { while (op < last) { *op++ = T(ip->real() - offset.real()); *op++ = T(ip->imag() - offset.imag()); ip++; } }else{ while (op < last) { *op++ = T((ip->real() - offset.real()) / scale.real()); *op++ = T((ip->imag() - offset.imag()) / scale.imag()); ip++; } } } array.freeStorage (in, deleteIn); target.putStorage (out, deleteOut); } template void ScaledComplexData::scaleColumnOnGet (Array& array, const Array& target) { if (fixedScale_p && fixedOffset_p) { scaleOnGet (scale_p, offset_p, array, target); }else{ ArrayIterator arrayIter (array, array.ndim() - 1); ReadOnlyArrayIterator targetIter (target, target.ndim() - 1); rownr_t rownr = 0; while (! arrayIter.pastEnd()) { scaleOnGet (getScale(rownr), getOffset(rownr), arrayIter.array(), targetIter.array()); rownr++; arrayIter.next(); targetIter.next(); } } } template void ScaledComplexData::scaleColumnOnPut (const Array& array, Array& target) { if (fixedScale_p && fixedOffset_p) { scaleOnPut (scale_p, offset_p, array, target); }else{ ReadOnlyArrayIterator arrayIter (array, array.ndim() - 1); ArrayIterator targetIter (target, target.ndim() - 1); rownr_t rownr = 0; while (! arrayIter.pastEnd()) { scaleOnPut (getScale(rownr), getOffset(rownr), arrayIter.array(), targetIter.array()); rownr++; arrayIter.next(); targetIter.next(); } } } template void ScaledComplexData::scaleCellsOnGet (Array& array, const Array& target, const RefRows& rownrs) { if (fixedScale_p && fixedOffset_p) { scaleOnGet (scale_p, offset_p, array, target); }else{ ArrayIterator arrayIter (array, array.ndim() - 1); ReadOnlyArrayIterator targetIter (target, target.ndim() - 1); RefRowsSliceIter rowiter(rownrs); while (! rowiter.pastEnd()) { rownr_t rownr = rowiter.sliceStart(); rownr_t end = rowiter.sliceEnd(); rownr_t incr = rowiter.sliceIncr(); // Iterate through the row numbers in the slice. while (rownr <= end) { scaleOnGet (getScale(rownr), getOffset(rownr), arrayIter.array(), targetIter.array()); rownr += incr; arrayIter.next(); targetIter.next(); } // Go to next slice. rowiter++; } } } template void ScaledComplexData::scaleCellsOnPut (const Array& array, Array& target, const RefRows& rownrs) { if (fixedScale_p && fixedOffset_p) { scaleOnPut (scale_p, offset_p, array, target); }else{ ReadOnlyArrayIterator arrayIter (array, array.ndim() - 1); ArrayIterator targetIter (target, target.ndim() - 1); RefRowsSliceIter rowiter(rownrs); while (! rowiter.pastEnd()) { rownr_t rownr = rowiter.sliceStart(); rownr_t end = rowiter.sliceEnd(); rownr_t incr = rowiter.sliceIncr(); // Iterate through the row numbers in the slice. while (rownr <= end) { scaleOnPut (getScale(rownr), getOffset(rownr), arrayIter.array(), targetIter.array()); rownr += incr; arrayIter.next(); targetIter.next(); } // Go to next slice. rowiter++; } } } template void ScaledComplexData::getArray (rownr_t rownr, Array& array) { Array target(storedShape(array.shape())); column().get (rownr, target); scaleOnGet (getScale(rownr), getOffset(rownr), array, target); } template void ScaledComplexData::putArray (rownr_t rownr, const Array& array) { Array target(storedShape(array.shape())); scaleOnPut (getScale(rownr), getOffset(rownr), array, target); column().put (rownr, target); } template void ScaledComplexData::getSlice (rownr_t rownr, const Slicer& slicer, Array& array) { Array target(storedShape(array.shape())); column().getSlice (rownr, storedSlicer(slicer), target); scaleOnGet (getScale(rownr), getOffset(rownr), array, target); } template void ScaledComplexData::putSlice (rownr_t rownr, const Slicer& slicer, const Array& array) { Array target(storedShape(array.shape())); scaleOnPut (getScale(rownr), getOffset(rownr), array, target); column().putSlice (rownr, storedSlicer(slicer), target); } template void ScaledComplexData::getArrayColumn (Array& array) { Array target(storedShape(array.shape())); column().getColumn (target); scaleColumnOnGet (array, target); } template void ScaledComplexData::putArrayColumn (const Array& array) { Array target(storedShape(array.shape())); scaleColumnOnPut (array, target); column().putColumn (target); } template void ScaledComplexData::getArrayColumnCells (const RefRows& rownrs, Array& array) { Array target(storedShape(array.shape())); column().getColumnCells (rownrs, target); scaleCellsOnGet (array, target, rownrs); } template void ScaledComplexData::putArrayColumnCells (const RefRows& rownrs, const Array& array) { Array target(storedShape(array.shape())); scaleCellsOnPut (array, target, rownrs); column().putColumnCells (rownrs, target); } template void ScaledComplexData::getColumnSlice (const Slicer& slicer, Array& array) { Array target(storedShape(array.shape())); column().getColumn (storedSlicer(slicer), target); scaleColumnOnGet (array, target); } template void ScaledComplexData::putColumnSlice (const Slicer& slicer, const Array& array) { Array target(storedShape(array.shape())); scaleColumnOnPut (array, target); column().putColumn (storedSlicer(slicer), target); } template void ScaledComplexData::getColumnSliceCells (const RefRows& rownrs, const Slicer& slicer, Array& array) { Array target(storedShape(array.shape())); column().getColumnCells (rownrs, storedSlicer(slicer), target); scaleCellsOnGet (array, target, rownrs); } template void ScaledComplexData::putColumnSliceCells (const RefRows& rownrs, const Slicer& slicer, const Array& array) { Array target(storedShape(array.shape())); scaleCellsOnPut (array, target, rownrs); column().putColumnCells (rownrs, storedSlicer(slicer), target); } template Slicer ScaledComplexData::storedSlicer (const Slicer& virtualSlicer) const { return Slicer (IPosition(1,0).concatenate (virtualSlicer.start()), IPosition(1,1).concatenate (virtualSlicer.end()), IPosition(1,1).concatenate (virtualSlicer.stride()), Slicer::endIsLast); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/StArrAipsIO.cc000066400000000000000000000166351476623553700205250ustar00rootroot00000000000000//# StArrAipsIO.cc: Read/write a table column of arrays array using AipsIO //# Copyright (C) 1994,1995,1996,1997,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include // for memcpy namespace casacore { //# NAMESPACE CASACORE - BEGIN StManColumnArrayAipsIO::StManColumnArrayAipsIO (StManAipsIO* smptr, int dataType) : StManColumnAipsIO (smptr, dataType, True), nrelem_p (0) {} StManColumnArrayAipsIO::~StManColumnArrayAipsIO() { uInt nr = stmanPtr_p->nrow(); for (uInt i=0; i(data), static_cast(getArrayPtr (rownr)), nrelem_p); } else { memcpy (static_cast(data), static_cast(getArrayPtr (rownr)), elemSize() * nrelem_p); } arr.putVStorage (data, deleteIt); } void StManColumnArrayAipsIO::putArrayV (rownr_t rownr, const ArrayBase& arr) { DebugAssert (shape_p.isEqual (arr.shape()), AipsError); Bool deleteIt; const void* data = arr.getVStorage (deleteIt); if (dtype() == TpString) { objcopy (static_cast(getArrayPtr (rownr)), static_cast(data), nrelem_p); } else { memcpy (static_cast(getArrayPtr (rownr)), static_cast(data), elemSize() * nrelem_p); } arr.freeVStorage (data, deleteIt); stmanPtr_p->setHasPut(); } void StManColumnArrayAipsIO::remove (rownr_t rownr) { deleteArray (rownr); MSMColumn::remove (rownr); } void StManColumnArrayAipsIO::deleteArray (rownr_t rownr) { void* datap = getArrayPtr (rownr); deleteData (datap, False); } //# Write all data into AipsIO. void StManColumnArrayAipsIO::putFile (rownr_t nrval, AipsIO& ios) { // Version 2 does not write dtype, shape and nelem anymore. // They are automatically set on reconstruction of the storage manager. ios.putstart ("StManColumnArrayAipsIO", 2); // class version 2 StManColumnAipsIO::putFile (nrval, ios); ios.putend(); } //# Read all data from AipsIO. void StManColumnArrayAipsIO::getFile (rownr_t nrval, AipsIO& ios) { uInt version = ios.getstart ("StManColumnArrayAipsIO"); if (version == 1) { IPosition shape; uInt n; ios >> n; // data type ios >> shape; ios >> n; // nelem } StManColumnAipsIO::getFile (nrval, ios); ios.getend(); } #define STMANCOLUMNARRAYAIPSIO_PUTDATA(T) \ { \ T** dpa = (T**)dp; \ while (nrval--) { \ ios.put (nrelem_p, *dpa, False); \ dpa++; \ } \ } void StManColumnArrayAipsIO::putData (void* dp, uInt nrval, AipsIO& ios) { ios << nrval * nrelem_p; switch (dtype()) { case TpBool: STMANCOLUMNARRAYAIPSIO_PUTDATA(Bool) break; case TpUChar: STMANCOLUMNARRAYAIPSIO_PUTDATA(uChar) break; case TpShort: STMANCOLUMNARRAYAIPSIO_PUTDATA(Short) break; case TpUShort: STMANCOLUMNARRAYAIPSIO_PUTDATA(uShort) break; case TpInt: STMANCOLUMNARRAYAIPSIO_PUTDATA(Int) break; case TpUInt: STMANCOLUMNARRAYAIPSIO_PUTDATA(uInt) break; case TpInt64: STMANCOLUMNARRAYAIPSIO_PUTDATA(Int64) break; case TpFloat: STMANCOLUMNARRAYAIPSIO_PUTDATA(float) break; case TpDouble: STMANCOLUMNARRAYAIPSIO_PUTDATA(double) break; case TpComplex: STMANCOLUMNARRAYAIPSIO_PUTDATA(Complex) break; case TpDComplex: STMANCOLUMNARRAYAIPSIO_PUTDATA(DComplex) break; case TpString: STMANCOLUMNARRAYAIPSIO_PUTDATA(String) break; default: throw DataManInvDT("StArrAipsIO::putData"); } } #define STMANCOLUMNARRAYAIPSIO_GETDATA(T) \ { \ uInt nr; \ T** dparr = (T**)dp + inx; \ T* dpd; \ while (nrval--) { \ dpd = (T*) allocData (nrelem_p, False); \ *dparr++ = dpd; \ if (version == 1) { \ ios >> nr; \ } \ ios.get (nrelem_p, dpd); \ } \ } void StManColumnArrayAipsIO::getData (void* dp, uInt inx, uInt nrval, AipsIO& ios, uInt version) { uInt nr; if (version > 1) { ios >> nr; } switch (dtype()) { case TpBool: STMANCOLUMNARRAYAIPSIO_GETDATA(Bool) break; case TpUChar: STMANCOLUMNARRAYAIPSIO_GETDATA(uChar) break; case TpShort: STMANCOLUMNARRAYAIPSIO_GETDATA(Short) break; case TpUShort: STMANCOLUMNARRAYAIPSIO_GETDATA(uShort) break; case TpInt: STMANCOLUMNARRAYAIPSIO_GETDATA(Int) break; case TpUInt: STMANCOLUMNARRAYAIPSIO_GETDATA(uInt) break; case TpInt64: STMANCOLUMNARRAYAIPSIO_GETDATA(Int64) break; case TpFloat: STMANCOLUMNARRAYAIPSIO_GETDATA(float) break; case TpDouble: STMANCOLUMNARRAYAIPSIO_GETDATA(double) break; case TpComplex: STMANCOLUMNARRAYAIPSIO_GETDATA(Complex) break; case TpDComplex: STMANCOLUMNARRAYAIPSIO_GETDATA(DComplex) break; case TpString: STMANCOLUMNARRAYAIPSIO_GETDATA(String) break; default: throw DataManInvDT("StArrAipsIO::getData"); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/StArrAipsIO.h000066400000000000000000000133571476623553700203650ustar00rootroot00000000000000//# StArrAipsIO.h: AipsIO storage manager for direct table arrays //# Copyright (C) 1994,1995,1996,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_STARRAIPSIO_H #define TABLES_STARRAIPSIO_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class AipsIO; // // AipsIO storage manager for direct table arrays // // // // // //# Classes you should understand before using this one. //
      • StManAipsIO //
      • StManColumnAipsIO // // // StManColumnArrayAipsIO handles the access to a direct array in a table // column using the AipsIO storage manager. // // // StManColumnArrayAipsIO holds the direct arrays in memory and writes // them into the AipsIO file when the table gets closed. // It fully supports addition and removal of rows. // When a row is added to the table, the direct array gets allocated. // This is possible, because the shape of direct arrays is known. // // The class is derived from StManColumnAipsIO which is used to hold // a pointer to the array. // // // StManColumnArrayAipsIO handles the standard data types. The class // is not templated, but a switch statement is used instead. // Templates would cause too many instantiations. // // //# A List of bugs, limitations, extensions or planned refinements. //
      • Maybe TpArrayInt, etc. should be used instead of TpInt. //
      • get/putSlice use too many array operations. // To solve this requires an array constructor taking a // pointer to the data (which is foreseen in the new Array classes). // class StManColumnArrayAipsIO : public StManColumnAipsIO { public: // Create a column of the given data type. StManColumnArrayAipsIO (StManAipsIO*, int dataType); // Frees up the storage. virtual ~StManColumnArrayAipsIO(); // Forbid copy constructor. StManColumnArrayAipsIO (const StManColumnArrayAipsIO&) = delete; // Forbid assignment. StManColumnArrayAipsIO& operator= (const StManColumnArrayAipsIO&) = delete; // Set the (fixed) shape of the arrays in the entire column. virtual void setShapeColumn (const IPosition& shape); // Add (newNrrow-oldNrrow) rows to the column. // Allocate the data arrays in these rows if the shape is fixed. virtual void addRow (rownr_t newNrrow, rownr_t oldNrrow); // Get the dimensionality of the item in the given row. // 0 is returned if there is no array. virtual uInt ndim (rownr_t rownr); // Get the shape of the array in the given row. // An zero-length IPosition is returned if there is no array. virtual IPosition shape (rownr_t rownr); // Get an array value in the given row. // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the ArrayColumn get function). virtual void getArrayV (rownr_t rownr, ArrayBase& dataPtr); // Put an array value into the given row. // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the ArrayColumn put function). virtual void putArrayV (rownr_t rownr, const ArrayBase& dataPtr); // Remove the value in the given row. virtual void remove (rownr_t rownr); // Let the column create its arrays. virtual void doCreate (rownr_t nrrow); // Write the data into AipsIO. // This will call StManColumnAipsIO::putFile which will in its turn // call putData in this class for each of its chunks of data. virtual void putFile (rownr_t nrval, AipsIO&); // Read the data from AipsIO. // This will call StManColumnAipsIO::getFile which will in its turn // call getData in this class for each of its chunks of data. virtual void getFile (rownr_t nrval, AipsIO&); private: // The shape of the array. IPosition shape_p; // The nr of elements in the array. uInt nrelem_p; // Delete the array at the given index. void deleteArray (rownr_t index); // Put the data of a data block. // datap is an array of nrval pointers to arrays. virtual void putData (void* datap, uInt nrval, AipsIO&); // Get data arrays into a data block at the given index. // datap is an array of pointers to arrays. nrval arrays will // be allocated and read starting at datap[index]. virtual void getData (void* datap, uInt index, uInt nrval, AipsIO&, uInt version); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/StArrayFile.cc000066400000000000000000000341721476623553700206060ustar00rootroot00000000000000//# StArrayFile.cc: Read/write array in external format for a storage manager //# Copyright (C) 1994,1995,1996,1997,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN StManArrayFile::StManArrayFile (const String& fname, ByteIO::OpenOption fop, uInt version, Bool bigEndian, uInt bufferSize, const std::shared_ptr& mfile) : leng_p (16), version_p (version), hasPut_p (False) { // The maximum version is 1. if (version_p > 1) { version_p = 1; } if (bufferSize == 0) { bufferSize = 65536; } //# Open file name as input and/or output; throw exception if it fails. if (mfile) { file_p.reset (new MFFileIO (mfile, fname, fop)); } else { file_p.reset (new RegularFileIO (RegularFile(fname), fop, bufferSize)); } if (bigEndian) { iofil_p.reset (new CanonicalIO (file_p)); }else{ iofil_p.reset (new LECanonicalIO (file_p)); } AlwaysAssert (iofil_p != 0, AipsError); swput_p = iofil_p->isWritable(); //# Get the version and length for an existing file. //# Otherwise set put-flag. resync(); sizeChar_p = ValType::getCanonicalSize (TpChar, bigEndian); sizeuChar_p = ValType::getCanonicalSize (TpUChar, bigEndian); sizeShort_p = ValType::getCanonicalSize (TpShort, bigEndian); sizeuShort_p = ValType::getCanonicalSize (TpUShort, bigEndian); sizeInt_p = ValType::getCanonicalSize (TpInt, bigEndian); sizeuInt_p = ValType::getCanonicalSize (TpUInt, bigEndian); sizeInt64_p = ValType::getCanonicalSize (TpInt64, bigEndian); sizeFloat_p = ValType::getCanonicalSize (TpFloat, bigEndian); sizeDouble_p = ValType::getCanonicalSize (TpDouble, bigEndian); } //# Close the file. //# Delete it if required. StManArrayFile::~StManArrayFile () { //# Write the version and file length at the beginning. flush (False); } Bool StManArrayFile::flush (Bool) { if (hasPut_p) { setpos (0); put (version_p); iofil_p->write (1, &leng_p); hasPut_p = False; file_p->flush(); setpos (leng_p); return True; } return False; } // Resync the file (i.e. clear possible cache information). void StManArrayFile::resync() { file_p->resync(); if (iofil_p->seek (0, ByteIO::End) > 0) { setpos (0); get (version_p); iofil_p->read (1, &leng_p); }else{ setpos (0); put (version_p); iofil_p->write (1, &leng_p); //# Put a 0 to fill up the buffer and make valgrind happy. put (Int(0)); hasPut_p = True; } } void StManArrayFile::setpos (Int64 pos) { Int64 newpos = iofil_p->seek (pos); if (newpos != pos) { throw (DataManError ("StManArrayFile::setpos failed in file " + file_p->fileName())); } } void StManArrayFile::put (Int64 fileOff, Int64 arrayOff, uInt64 nr, const Float* data) { setpos (fileOff + arrayOff*sizeFloat_p); iofil_p->write (nr, data); hasPut_p = True; } uInt StManArrayFile::putShape (const IPosition& shape, Int64& offset, const Float*) { return putRes (shape, offset, sizeFloat_p); } void StManArrayFile::get (Int64 fileOff, Int64 arrayOff, uInt64 nr, Float* data) { setpos (fileOff + arrayOff*sizeFloat_p); iofil_p->read (nr, data); } void StManArrayFile::copyArrayFloat (Int64 to, Int64 from, uInt64 nr) { copyData (to, from, nr*sizeFloat_p); } //# Put a vector at the given offset. #define STMANARRAYFILE_PUTGET(T,SIZEDTYPE) \ void StManArrayFile::put (Int64 fileOff, Int64 arrayOff, uInt64 nr, \ const T* data) \ { \ setpos (fileOff + arrayOff*SIZEDTYPE); \ iofil_p->write (nr, data); \ hasPut_p = True; \ } \ uInt StManArrayFile::putShape (const IPosition& shape, Int64& offset, \ const T*) \ { return putRes (shape, offset, SIZEDTYPE); } \ void StManArrayFile::get (Int64 fileOff, Int64 arrayOff, uInt64 nr, T* data) \ { \ setpos (fileOff + arrayOff*SIZEDTYPE); \ iofil_p->read (nr, data); \ } \ void StManArrayFile::aips_name2(copyArray,T) (Int64 to, Int64 from, uInt64 nr)\ { copyData (to, from, nr*SIZEDTYPE); } STMANARRAYFILE_PUTGET(Char, sizeChar_p) STMANARRAYFILE_PUTGET(uChar, sizeuChar_p) STMANARRAYFILE_PUTGET(Short, sizeShort_p) STMANARRAYFILE_PUTGET(uShort, sizeuShort_p) STMANARRAYFILE_PUTGET(Int, sizeInt_p) STMANARRAYFILE_PUTGET(uInt, sizeuInt_p) STMANARRAYFILE_PUTGET(Int64, sizeInt64_p) STMANARRAYFILE_PUTGET(uInt64, sizeuInt64_p) //#//STMANARRAYFILE_PUTGET(Float, sizeFloat_p) STMANARRAYFILE_PUTGET(Double, sizeDouble_p) //#//STMANARRAYFILE_PUTGET(long double, sizeLDouble_p) //# Handle it for Bool. void StManArrayFile::put (Int64 fileOff, Int64 arrayOff, uInt64 nr, const Bool* data) { //# Bools are stored as bits, thus a bit more complex. uInt64 start = arrayOff / 8; uInt stbit = arrayOff - 8 * start; uInt64 end = (arrayOff + nr) / 8; uInt endbit = arrayOff + nr - 8 * end; if (endbit != 0) { end++; } //# Allocate a buffer of the required length. //# Read first and/or last byte when partially accessed. uChar* buf = new uChar[end - start]; if (endbit != 0) { setpos (fileOff + end - 1); iofil_p->read (1, buf+end-start-1); } //# Avoid reading same byte twice if all bits are in 1 byte. if (stbit != 0 && (endbit == 0 || start < end-1)) { setpos (fileOff + start); iofil_p->read (1, buf); } Conversion::boolToBit (buf, data, stbit, nr); setpos (fileOff + start); iofil_p->write (end - start, buf); hasPut_p = True; delete [] buf; } uInt StManArrayFile::putShape (const IPosition& shape, Int64& offset, const Bool*) { return putRes (shape, offset, 0.125); } void StManArrayFile::get (Int64 fileOff, Int64 arrayOff, uInt64 nr, Bool* data) { //# Bools are stored as bits, thus a bit more complex. uInt64 start = arrayOff / 8; uInt stbit = arrayOff - 8 * start; uInt64 end = (arrayOff + nr) / 8; uInt endbit = arrayOff + nr - 8 * end; if (endbit != 0) { end++; } //# Allocate a buffer of the required length. uChar* buf = new uChar[end - start]; setpos (fileOff + start); iofil_p->read (end - start, buf); Conversion::bitToBool (data, buf, stbit, nr); delete [] buf; } void StManArrayFile::copyArrayBool (Int64 to, Int64 from, uInt64 nr) { copyData (to, from, (nr+7)/8); } //# Handle it for Complex and String. //# A Complex consists of 2 float values. //# For a string its file offset gets stored (as a uInt), while the //# string itself will be put at the end of the file. uInt StManArrayFile::putShape (const IPosition& shape, Int64& offset, const Complex*) { return putRes (shape, offset, 2*sizeFloat_p); } uInt StManArrayFile::putShape (const IPosition& shape, Int64& offset, const DComplex*) { return putRes (shape, offset, 2*sizeDouble_p); } uInt StManArrayFile::putShape (const IPosition& shape, Int64& offset, const String*) { uInt n = putRes (shape, offset, sizeuInt_p); uInt64 nr = shape.product(); Block data(nr, 0u); put (offset+n, 0, nr, data.storage()); return n; } //# Put a complex vector at the given file offset. void StManArrayFile::put (Int64 fileOff, Int64 arrayOff, uInt64 nr, const Complex* data) { setpos (fileOff + arrayOff*2*sizeFloat_p); iofil_p->write (2*nr, (const Float*)data); hasPut_p = True; } void StManArrayFile::put (Int64 fileOff, Int64 arrayOff, uInt64 nr, const DComplex* data) { setpos (fileOff + arrayOff*2*sizeDouble_p); iofil_p->write (2*nr, (const Double*)data); hasPut_p = True; } //# Put a string at the given file offset. void StManArrayFile::put (Int64 fileOff, Int64 arrayOff, uInt64 nr, const String* data) { //# Get file offset for string offset array. //# Allocate a buffer to hold 4096 string offsets. Int64 offs = fileOff + arrayOff*sizeuInt_p; uInt buf[4096]; uInt64 n; while (nr > 0) { n = (nr < 4096 ? nr : 4096); setpos (leng_p); // position at end of file for (uInt64 i=0; ilength())); // write string length leng_p += iofil_p->write (data->length(), data->chars()); data++; } //# Write the offsets. setpos (offs); offs += iofil_p->write (n, buf); hasPut_p = True; nr -= n; } } //# Get a complex vector at the given file offset. void StManArrayFile::get (Int64 fileOff, Int64 arrayOff, uInt64 nr, Complex* data) { setpos (fileOff + arrayOff*2*sizeFloat_p); iofil_p->read (2*nr, (Float*)data); } void StManArrayFile::get (Int64 fileOff, Int64 arrayOff, uInt64 nr, DComplex* data) { setpos (fileOff + arrayOff*2*sizeDouble_p); iofil_p->read (2*nr, (Double*)data); } //# Get a string at the given file offset. void StManArrayFile::get (Int64 fileOff, Int64 arrayOff, uInt64 nr, String* data) { //# Get file offset for string offset array. //# Allocate a buffer to hold 4096 string offsets. Int64 offs = fileOff + arrayOff*sizeuInt_p; uInt buf[4096]; uInt64 n; uInt l; while (nr > 0) { n = (nr < 4096 ? nr : 4096); setpos (offs); offs += iofil_p->read (n, buf); for (uInt64 i=0; iresize (l); // resize storage which adds trailing 0 char* ptr = &((*data)[0]); // get actual string iofil_p->read (data->length(), ptr); #ifdef USE_OLD_STRING ptr[l] = '\0'; #endif } data++; } nr -= n; } } void StManArrayFile::copyArrayComplex (Int64 to, Int64 from, uInt64 nr) { copyData (to, from, nr*2*sizeFloat_p); } void StManArrayFile::copyArrayDComplex (Int64 to, Int64 from, uInt64 nr) { copyData (to, from, nr*2*sizeDouble_p); } void StManArrayFile::copyArrayString (Int64 to, Int64 from, uInt64 nr) { String data[4096]; uInt64 ndone = 0; for (uInt64 n=0; nr>0; nr-=n) { n = (nr < 4096 ? nr : 4096); get (from, ndone, n, data); put (to, ndone, n, data); ndone += n; } } //# Copy the data of the given length from one file offset to another. void StManArrayFile::copyData (Int64 to, Int64 from, uInt64 length) { uChar buffer[32768]; for (uInt64 n=0; length>0; length-=n) { n = (length < 32768 ? length : 32768); setpos (from); from += iofil_p->read (n, buffer); setpos (to); to += iofil_p->write (n, buffer); hasPut_p = True; } } //# Write shape and reserve file space for the array by //# increasing the length. //# Take care it is at 8 byte boundary. //# Write something in the last byte to make sure the file is extended. uInt StManArrayFile::putRes (const IPosition& shape, Int64& offset, float lenElem) { leng_p = 8 * ((leng_p+7) / 8); offset = leng_p; uInt n = 0; setpos (leng_p); // Put reference count in higher versions only. if (version_p > 0) { n += put (uInt(1)); } n += put (uInt(shape.nelements())); for (uInt i=0; iwrite (1, &c); hasPut_p = True; return n; } //# Get the shape at the given file offset //# and returns its length in the file. uInt StManArrayFile::getShape (Int64 fileOff, IPosition& shape) { setpos (fileOff); uInt n=0; if (version_p > 0) { uInt refCount; n = get (refCount); } uInt nr; n += get (nr); shape.resize (nr); Int tmp; for (uInt i=0; i #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class MultiFileBase; class IPosition; // // Read/write array in external format for a storage manager // // // // // //# Classes you should understand before using this one. //
      • ToLocal //
      • FromLocal // // // StManArrayFile is a class used by table storage managers // to store indirect arrays in a file. // // // StManArrayFile is for use by the table storage manager, in particular // to read/write indirectly stored arrays. // Instead of holding the data in memory, they are written directly // into a file. It also allows to access a part of an array, which // is needed for the table system to access an array section. // It does not use a cache of its own, but it is relying on the // underlying system routines to cache and buffer adequately. // // This class could in principle also be used for other array purposes, // for example, to implement a paged array class for really huge arrays. // // An StManArrayFile object is connected to one file. It is possible // to hold multiple arrays in the file, each with its own shape. // An array is stored as its shape followed by the actual data // (all in little or big endian format). An array of strings is written as // an array of offsets pointing to the actual strings. // When a string gets a new value, the new value is written at the // end of the file and the file space with the old value is lost. // // Currently only the basic types are supported, but arbitrary types // could also be supported by writing/reading an element in the normal // way into the AipsIO buffer. It would only require that AipsIO // would contain a function to get its buffers and to restart them. // // // // void writeArray (const Array& array) // { // // Construct object and update file StArray.dat. // StManArrayFile arrayFile("StArray.dat, ByteIO::New); // // Reserve space for an array with the given shape and data type. // // This writes the shape at the end of the file and reserves // // space the hold the entire Bool array. // // It fills in the file offset where the shape is stored // // and returns the length of the shape in the file. // Int64 offset; // uInt shapeLength = arrayFile.putShape (array.shape(), offset, static_cast(0)); // // Now put the actual array. // // This has to be put at the returned file offset plus the length // // of the shape in the file. // Bool deleteIt; // const Bool* dataPtr = array.getStorage (deleteIt); // arrayFile.put (offset+shapeLength, 0, array.nelements(), dataPtr); // array.freeStorage (dataPtr, deleteIt); // } // // // // The AipsIO class was not suitable for indirect table arrays, // because it uses memory to hold the data. Furthermore it is // not possible to access part of the data in AipsIO. // // //
      • implement long double //
      • support arbitrary types //
      • when rewriting a string value, use the current file // space if it fits // class StManArrayFile { public: // Construct the object and attach it to the give file. // The OpenOption determines how the file is opened // (e.g. ByteIO::New for a new file). // The buffersize is used to allocate a buffer of a proper size // for the underlying filebuf object (see iostream package). // A bufferSize 0 means using the default size (currently 65536). StManArrayFile (const String& name, ByteIO::OpenOption, uInt version=0, Bool bigEndian=True, uInt bufferSize=0, const std::shared_ptr& = std::shared_ptr()); // Close the possibly opened file. ~StManArrayFile(); // Flush and optionally fsync the data. // It returns True when any data was written since the last flush. Bool flush (Bool fsync); // Reopen the file for read/write access. void reopenRW(); // Resync the file (i.e. clear possible cache information). void resync(); // Return the current file length (merely a debug tool). Int64 length() { return leng_p; } // Put the array shape and store its file offset into the offset argument. // Reserve file space for the associated array. // The length of the shape part in the file is returned. // The file offset plus the shape length is the starting offset of the // actual array data (which can be used by get and put). // Space is reserved to store the reference count. // uInt putShape (const IPosition& shape, Int64& fileOffset, const Bool* dummy); uInt putShape (const IPosition& shape, Int64& fileOffset, const Char* dummy); uInt putShape (const IPosition& shape, Int64& fileOffset, const uChar* dummy); uInt putShape (const IPosition& shape, Int64& fileOffset, const Short* dummy); uInt putShape (const IPosition& shape, Int64& fileOffset, const uShort* dummy); uInt putShape (const IPosition& shape, Int64& fileOffset, const Int* dummy); uInt putShape (const IPosition& shape, Int64& fileOffset, const uInt* dummy); uInt putShape (const IPosition& shape, Int64& fileOffset, const Int64* dummy); uInt putShape (const IPosition& shape, Int64& fileOffset, const uInt64* dummy); uInt putShape (const IPosition& shape, Int64& fileOffset, const Float* dummy); uInt putShape (const IPosition& shape, Int64& fileOffset, const Double* dummy); uInt putShape (const IPosition& shape, Int64& fileOffset, const Complex* dummy); uInt putShape (const IPosition& shape, Int64& fileOffset, const DComplex* dummy); uInt putShape (const IPosition& shape, Int64& fileOffset, const String* dummy); // // Get the reference count. uInt getRefCount (Int64 offset); // Put the reference count. // An exception is thrown if a value other than 1 is put for version 0. void putRefCount (uInt refCount, Int64 offset); // Put nr elements at the given file offset and array offset. // The file offset of the first array element is the file offset // of the shape plus the length of the shape in the file. // The array offset is counted in number of elements. It can be // used to put only a (contiguous) section of the array. // void put (Int64 fileOffset, Int64 arrayOffset, uInt64 nr, const Bool*); void put (Int64 fileOffset, Int64 arrayOffset, uInt64 nr, const Char*); void put (Int64 fileOffset, Int64 arrayOffset, uInt64 nr, const uChar*); void put (Int64 fileOffset, Int64 arrayOffset, uInt64 nr, const Short*); void put (Int64 fileOffset, Int64 arrayOffset, uInt64 nr, const uShort*); void put (Int64 fileOffset, Int64 arrayOffset, uInt64 nr, const Int*); void put (Int64 fileOffset, Int64 arrayOffset, uInt64 nr, const uInt*); void put (Int64 fileOffset, Int64 arrayOffset, uInt64 nr, const Int64*); void put (Int64 fileOffset, Int64 arrayOffset, uInt64 nr, const uInt64*); void put (Int64 fileOffset, Int64 arrayOffset, uInt64 nr, const Float*); void put (Int64 fileOffset, Int64 arrayOffset, uInt64 nr, const Double*); //#// void put (Int64 fileOffset, Int64 arrayOffset, uInt64 nr, const long double*); void put (Int64 fileOffset, Int64 arrayOffset, uInt64 nr, const Complex*); void put (Int64 fileOffset, Int64 arrayOffset, uInt64 nr, const DComplex*); void put (Int64 fileOffset, Int64 arrayOffset, uInt64 nr, const String*); // // Get the shape at the given file offset. // It will reshape the IPosition vector when needed. // It returns the length of the shape in the file. uInt getShape (Int64 fileOffset, IPosition& shape); // Get nr elements at the given file offset and array offset. // The file offset of the first array element is the file offset // of the shape plus the length of the shape in the file. // The array offset is counted in number of elements. It can be // used to get only a (contiguous) section of the array. // void get (Int64 fileOffset, Int64 arrayOffset, uInt64 nr, Bool*); void get (Int64 fileOffset, Int64 arrayOffset, uInt64 nr, Char*); void get (Int64 fileOffset, Int64 arrayOffset, uInt64 nr, uChar*); void get (Int64 fileOffset, Int64 arrayOffset, uInt64 nr, Short*); void get (Int64 fileOffset, Int64 arrayOffset, uInt64 nr, uShort*); void get (Int64 fileOffset, Int64 arrayOffset, uInt64 nr, Int*); void get (Int64 fileOffset, Int64 arrayOffset, uInt64 nr, uInt*); void get (Int64 fileOffset, Int64 arrayOffset, uInt64 nr, Int64*); void get (Int64 fileOffset, Int64 arrayOffset, uInt64 nr, uInt64*); void get (Int64 fileOffset, Int64 arrayOffset, uInt64 nr, Float*); void get (Int64 fileOffset, Int64 arrayOffset, uInt64 nr, Double*); //#// void get (Int64 fileOffset, Int64 arrayOffset, uInt64 nr, long double*); void get (Int64 fileOffset, Int64 arrayOffset, uInt64 nr, Complex*); void get (Int64 fileOffset, Int64 arrayOffset, uInt64 nr, DComplex*); void get (Int64 fileOffset, Int64 arrayOffset, uInt64 nr, String*); // // Copy the array with nr elements from one file offset // to another. // void copyArrayBool (Int64 to, Int64 from, uInt64 nr); void copyArrayChar (Int64 to, Int64 from, uInt64 nr); void copyArrayuChar (Int64 to, Int64 from, uInt64 nr); void copyArrayShort (Int64 to, Int64 from, uInt64 nr); void copyArrayuShort (Int64 to, Int64 from, uInt64 nr); void copyArrayInt (Int64 to, Int64 from, uInt64 nr); void copyArrayuInt (Int64 to, Int64 from, uInt64 nr); void copyArrayInt64 (Int64 to, Int64 from, uInt64 nr); void copyArrayuInt64 (Int64 to, Int64 from, uInt64 nr); void copyArrayFloat (Int64 to, Int64 from, uInt64 nr); void copyArrayDouble (Int64 to, Int64 from, uInt64 nr); //#// void copyArrayLDouble (Int64 to, Int64 from, uInt64 nr); void copyArrayComplex (Int64 to, Int64 from, uInt64 nr); void copyArrayDComplex (Int64 to, Int64 from, uInt64 nr); void copyArrayString (Int64 to, Int64 from, uInt64 nr); // private: std::shared_ptr file_p; //# File object std::shared_ptr iofil_p; //# IO object Int64 leng_p; //# File length uInt version_p; //# Version of StArrayFile file Bool swput_p; //# True = put is possible Bool hasPut_p; //# True = put since last flush uInt sizeChar_p; uInt sizeuChar_p; uInt sizeShort_p; uInt sizeuShort_p; uInt sizeInt_p; uInt sizeuInt_p; uInt sizeInt64_p; uInt sizeuInt64_p; uInt sizeFloat_p; uInt sizeDouble_p; // Put a single value at the current file offset. // It returns the length of the value in the file. // uInt put (const Int&); uInt put (const uInt&); // // Put the array shape at the end of the file and reserve // space for nr elements (each lenElem bytes long). // It fills the file offset of the shape. // It returns the length of the shape in the file. uInt putRes (const IPosition& shape, Int64& fileOffset, float lenElem); // Get a single value at the current file offset. // It returns the length of the value in the file. // uInt get (Int&); uInt get (uInt&); // // Copy data with the given length from one file offset to another. void copyData (Int64 to, Int64 from, uInt64 length); // Position the file on the given offset. void setpos (Int64 offset); }; inline void StManArrayFile::reopenRW() { file_p->reopenRW(); } inline uInt StManArrayFile::put (const Int& value) { hasPut_p = True; return iofil_p->write (1, &value); } inline uInt StManArrayFile::put (const uInt& value) { hasPut_p = True; return iofil_p->write (1, &value); } inline uInt StManArrayFile::get (Int& value) { return iofil_p->read (1, &value); } inline uInt StManArrayFile::get (uInt& value) { return iofil_p->read (1, &value); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/StIndArrAIO.cc000066400000000000000000000173571476623553700204460ustar00rootroot00000000000000//# StIndArrAIO.cc: Read/write a table column of arrays array using AipsIO //# Copyright (C) 1994,1995,1996,1997,1999,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include //# for snprintf namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Define a macro which gets the pointer for the given row //# and casts it to the block. #define STMANINDGETBLOCK(rownr) \ ((StIndArray*)(getArrayPtr(rownr))) StManColumnIndArrayAipsIO::StManColumnIndArrayAipsIO (StManAipsIO* smptr, int dataType) : StManColumnAipsIO (smptr, dataType, True), staioPtr_p (smptr), seqnr_p (smptr->uniqueNr()), shapeIsFixed_p(False), version_p (2), iosfile_p (0) {} //# Delete all objects created. StManColumnIndArrayAipsIO::~StManColumnIndArrayAipsIO() { rownr_t nr = stmanPtr_p->nrow(); for (rownr_t i=0; i 1) { iosfile_p = staioPtr_p->openArrayFile (opt); } else { //# Open/create the file holding the arrays in the column. if (iosfile_p == 0) { char strc[8]; snprintf (strc, sizeof(strc), "i%i", seqnr_p); iosfile_p = new StManArrayFile (stmanPtr_p->fileName() + strc, opt); } else { iosfile_p->resync(); } } } void StManColumnIndArrayAipsIO::reopenRW() { iosfile_p->reopenRW(); } void StManColumnIndArrayAipsIO::setShapeColumn (const IPosition& shape) { fixedShape_p = shape; shapeIsFixed_p = True; } void StManColumnIndArrayAipsIO::addRow (rownr_t nrnew, rownr_t nrold) { //# Extend data blocks if needed. StManColumnAipsIO::addRow (nrnew, nrold); //# Allocate the data arrays if fixed shape. if (shapeIsFixed_p) { for (; nroldsetShape (*iosfile_p, dtype(), shape)) { putArrayPtr (rownr, ptr); } } //# Get the shape for the array (if any) in the given row. //# Read shape if not read yet. StIndArray* StManColumnIndArrayAipsIO::getShape (rownr_t rownr) { StIndArray* ptr = STMANINDGETBLOCK(rownr); if (ptr == 0) { throw (DataManInvOper ("ASM: no array in row " + String::toString(rownr) + " of " + stmanPtr_p->fileName())); } ptr->getShape (*iosfile_p); return ptr; } Bool StManColumnIndArrayAipsIO::isShapeDefined (rownr_t rownr) { return (STMANINDGETBLOCK(rownr) == 0 ? False : True); } uInt StManColumnIndArrayAipsIO::ndim (rownr_t rownr) { return getShape(rownr)->shape().nelements(); } IPosition StManColumnIndArrayAipsIO::shape (rownr_t rownr) { return getShape(rownr)->shape(); } Bool StManColumnIndArrayAipsIO::canChangeShape() const { return (shapeIsFixed_p ? False : True); } void StManColumnIndArrayAipsIO::getArrayV (rownr_t rownr, ArrayBase& arr) { StIndArray* sia = getShape (rownr); sia->getArrayV (*iosfile_p, arr, dtype()); } void StManColumnIndArrayAipsIO::putArrayV (rownr_t rownr, const ArrayBase& arr) { StIndArray* sia = getShape (rownr); sia->putArrayV (*iosfile_p, arr, dtype()); stmanPtr_p->setHasPut(); } void StManColumnIndArrayAipsIO::getSliceV (rownr_t rownr, const Slicer& ns, ArrayBase& arr) { StIndArray* sia = getShape (rownr); sia->getSliceV (*iosfile_p, ns, arr, dtype()); } void StManColumnIndArrayAipsIO::putSliceV (rownr_t rownr, const Slicer& ns, const ArrayBase& arr) { StIndArray* sia = getShape (rownr); sia->putSliceV (*iosfile_p, ns, arr, dtype()); stmanPtr_p->setHasPut(); } void StManColumnIndArrayAipsIO::remove (rownr_t rownr) { deleteArray (rownr); StManColumnAipsIO::remove (rownr); } Bool StManColumnIndArrayAipsIO::ok() const { return StManColumnAipsIO::ok(); } void StManColumnIndArrayAipsIO::deleteArray (rownr_t rownr) { delete STMANINDGETBLOCK(rownr); } //# Write all data into AipsIO. void StManColumnIndArrayAipsIO::putFile (rownr_t nrval, AipsIO& ios) { ios.putstart ("StManColumnIndArrayAipsIO", version_p); ios << dtype(); // for backward compatibility ios << seqnr_p; StManColumnAipsIO::putFile (nrval, ios); ios.putend(); iosfile_p->flush (False); } void StManColumnIndArrayAipsIO::putData (void* dp, uInt nrval, AipsIO& ios) { StIndArray** dpa = (StIndArray**)dp; while (nrval--) { if (*dpa == 0) { ios << (uInt)0; }else{ Int64 off = (*dpa)->fileOffset(); if (off <= 2u*1024u*1024u*1024u) { ios << uInt(off); }else{ ios << 2u*1024u*1024u*1024u + 1u; ios << off; } } dpa++; } } //# Read all data from AipsIO. void StManColumnIndArrayAipsIO::getFile (rownr_t nrval, AipsIO& ios) { int dtype; version_p = ios.getstart ("StManColumnIndArrayAipsIO"); ios >> dtype; // for backward compatibility ios >> seqnr_p; openFile (stmanPtr_p->fileOption()); // open the array file StManColumnAipsIO::getFile (nrval, ios); ios.getend(); } void StManColumnIndArrayAipsIO::getData (void* dp, uInt inx, uInt nrval, AipsIO& ios, uInt) { Int64 offset; uInt off; StIndArray** dpa = (StIndArray**)dp; dpa += inx; while (nrval--) { ios >> off; if (off == 2u*1024u*1024u*1024u + 1u) { ios >> offset; }else{ offset = off; } if (offset == 0) { *dpa = 0; }else{ *dpa = new StIndArray (offset); } dpa++; } } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/StIndArrAIO.h000066400000000000000000000201531476623553700202740ustar00rootroot00000000000000//# StIndArrAIO.h: AipsIO storage manager for indirect table arrays //# Copyright (C) 1994,1995,1996,1997,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_STINDARRAIO_H #define TABLES_STINDARRAIO_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class AipsIO; class StManArrayFile; class StIndArray; // // AipsIO storage manager for indirect table arrays // // // // // //# Classes you should understand before using this one. //
      • StManAipsIO //
      • StManColumnAipsIO //
      • StIndArray // // // StManColumnIndArrayAipsIO handles the access to an indirect array // in a table column using the AipsIO storage manager. // // // StManColumnArrayAipsIO handles indirect arrays in a table column. // An StManArrayFile object is used to read and write the arrays // from/into a file in a simple way. So this column has a file of its own // to store the actual data in. It uses the (unique) column sequence // number to make the file name unique. // // An array (or section of an array) is only read when needed. // It, however, caches the array shape using the helper class // StIndArray. Pointers to these objects // are maintained using the standard StManColumnAipsIO facilities. // When the column gets written, the offsets in the StManArrayFile file // get written. Those will be read back when the column is read in. // // When a row gets deleted or when the array gets bigger, the file space // is lost. This storage manager is a simple one and no attempts // are done to make it smart. // // // StManColumnIndArrayAipsIO handles the standard data types. The class // is not templated, but a switch statement is used instead. // Templates would cause too many instantiations. // // //# A List of bugs, limitations, extensions or planned refinements. // class StManColumnIndArrayAipsIO : public StManColumnAipsIO { public: // Create a column of the given type. // The StManArrayFile object is not allocated here but by doCreate. StManColumnIndArrayAipsIO (StManAipsIO*, int dataType); // Frees up the storage and delete the StManArrayFile object. virtual ~StManColumnIndArrayAipsIO(); // Forbid copy constructor. StManColumnIndArrayAipsIO (const StManColumnIndArrayAipsIO&) = delete; // Forbid assignment. StManColumnIndArrayAipsIO& operator= (const StManColumnIndArrayAipsIO&) = delete; // Set the (fixed) shape of the arrays in the entire column. virtual void setShapeColumn (const IPosition& shape); // Add (newNrrow-oldNrrow) rows to the column. // Allocate the data arrays in these rows if the shape is fixed. virtual void addRow (rownr_t newNrrow, rownr_t oldNrrow); // Set the shape of the array in the given row and allocate the array // in the file. virtual void setShape (rownr_t rownr, const IPosition& shape); // Is the shape defined (i.e. is there an array) in this row? virtual Bool isShapeDefined (rownr_t rownr); // Get the dimensionality of the item in the given row. // 0 is returned if there is no array. virtual uInt ndim (rownr_t rownr); // Get the shape of the array in the given row. // An zero-length IPosition is returned if there is no array. virtual IPosition shape (rownr_t rownr); // This storage manager can handle changing array shapes // for non-FixedShape columns. virtual Bool canChangeShape() const; // Get an array value in the given row. // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the ArrayColumn get function). virtual void getArrayV (rownr_t rownr, ArrayBase& dataPtr); // Put an array value into the given row. // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the ArrayColumn put function). virtual void putArrayV (rownr_t rownr, const ArrayBase& dataPtr); // Get a section of the array in the given row. // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the ArrayColumn getSlice function). virtual void getSliceV (rownr_t rownr, const Slicer&, ArrayBase& dataPtr); // Put into a section of the array in the given row. // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the ArrayColumn putSlice function). virtual void putSliceV (rownr_t rownr, const Slicer&, const ArrayBase& dataPtr); // Remove the value in the given row. // This will result in lost file space. virtual void remove (rownr_t rownr); // Let the column create its array file. virtual void doCreate (rownr_t nrrow); // Write the data into AipsIO. // This will call StManColumnAipsIO::putFile which will in its turn // call putData in this class for each of its chunks of data. virtual void putFile (rownr_t nrval, AipsIO&); // Read the data from AipsIO. // This will call StManColumnAipsIO::getFile which will in its turn // call getData in this class for each of its chunks of data. virtual void getFile (rownr_t nrval, AipsIO&); // Reopen the storage manager files for read/write. virtual void reopenRW(); // Check if the class invariants still hold. Bool ok() const; private: // The storage manager. StManAipsIO* staioPtr_p; // The (unique) sequence number of the column. uInt seqnr_p; // The shape of all arrays in case it is fixed. IPosition fixedShape_p; // Switch indicating if the shape is fixed. Bool shapeIsFixed_p; // The version of the object retrieved from a file. // Versions < 2 use a StManArrayFile of their own. // Newer versions share the one in StManAipsIO. uInt version_p; // The file containing the indirect arrays. StManArrayFile* iosfile_p; // Open the file with the given mode. void openFile (ByteIO::OpenOption opt); // Delete the array in the given row. void deleteArray (rownr_t rownr); // Read the shape at the given row. // This will cache the information in the StIndArray // object for that row. StIndArray* getShape (rownr_t rownr); // Put the data of a data block. // datap is an array of nrval pointers to StIndArray. // Only the file offsets get written. void putData (void* datap, uInt nrval, AipsIO&); // Get file offsets to the arrays into a data block at the given index. // datap is an array of pointers to StIndArray. // nrval blocks will be allocated and read starting at datap[index]. // The actual shape and array data will be read when needed. void getData (void* datap, uInt index, uInt nrval, AipsIO&, uInt version); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/StIndArray.cc000066400000000000000000000472731476623553700204470ustar00rootroot00000000000000//# StIndArray.cc: Read/write indirect arrays //# Copyright (C) 1994,1995,1996,1997,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN StIndArray::StIndArray (Int64 fileOffset) : fileOffset_p (fileOffset), arrOffset_p (0) {} StIndArray::StIndArray (const StIndArray& that) : fileOffset_p (that.fileOffset_p), arrOffset_p (that.arrOffset_p), shape_p (that.shape_p) {} StIndArray::~StIndArray() {} StIndArray& StIndArray::operator= (const StIndArray& that) { if (this != &that) { fileOffset_p = that.fileOffset_p; arrOffset_p = that.arrOffset_p; shape_p.resize (that.shape_p.nelements()); shape_p = that.shape_p; } return *this; } void StIndArray::getShape (StManArrayFile& ios) { if (arrOffset_p == 0) { arrOffset_p = ios.getShape (fileOffset_p, shape_p); } } uInt StIndArray::refCount (StManArrayFile& ios) { return ios.getRefCount (fileOffset_p); } void StIndArray::incrementRefCount (StManArrayFile& ios) { uInt refCount = ios.getRefCount (fileOffset_p); refCount++; ios.putRefCount (refCount, fileOffset_p); #ifdef AIPS_TRACE cout << " incr refcount to " << refCount << " at "<(0)); break; case TpUChar: arrOffset_p = ios.putShape (shape_p, fileOffset_p, static_cast(0)); break; case TpShort: arrOffset_p = ios.putShape (shape_p, fileOffset_p, static_cast(0)); break; case TpUShort: arrOffset_p = ios.putShape (shape_p, fileOffset_p, static_cast(0)); break; case TpInt: arrOffset_p = ios.putShape (shape_p, fileOffset_p, static_cast(0)); break; case TpUInt: arrOffset_p = ios.putShape (shape_p, fileOffset_p, static_cast(0)); break; case TpInt64: arrOffset_p = ios.putShape (shape_p, fileOffset_p, static_cast(0)); break; case TpFloat: arrOffset_p = ios.putShape (shape_p, fileOffset_p, static_cast(0)); break; case TpDouble: arrOffset_p = ios.putShape (shape_p, fileOffset_p, static_cast(0)); break; case TpComplex: arrOffset_p = ios.putShape (shape_p, fileOffset_p, static_cast(0)); break; case TpDComplex: arrOffset_p = ios.putShape (shape_p, fileOffset_p, static_cast(0)); break; case TpString: arrOffset_p = ios.putShape (shape_p, fileOffset_p, static_cast(0)); break; } return True; } void StIndArray::copyData (StManArrayFile& ios, int dataType, const StIndArray& other) { // Check if both shape are equal. if (! shape_p.isEqual (other.shape_p)) { throw (DataManInternalError ("StManIndArray::copyData shapes not conforming")); } switch (dataType) { case TpBool: ios.copyArrayBool (fileOffset_p + arrOffset_p, other.fileOffset_p + other.arrOffset_p, shape_p.product()); break; case TpUChar: ios.copyArrayuChar (fileOffset_p + arrOffset_p, other.fileOffset_p + other.arrOffset_p, shape_p.product()); break; case TpShort: ios.copyArrayShort (fileOffset_p + arrOffset_p, other.fileOffset_p + other.arrOffset_p, shape_p.product()); break; case TpUShort: ios.copyArrayuShort (fileOffset_p + arrOffset_p, other.fileOffset_p + other.arrOffset_p, shape_p.product()); break; case TpInt: ios.copyArrayInt (fileOffset_p + arrOffset_p, other.fileOffset_p + other.arrOffset_p, shape_p.product()); break; case TpUInt: ios.copyArrayuInt (fileOffset_p + arrOffset_p, other.fileOffset_p + other.arrOffset_p, shape_p.product()); break; case TpInt64: ios.copyArrayInt64 (fileOffset_p + arrOffset_p, other.fileOffset_p + other.arrOffset_p, shape_p.product()); break; case TpFloat: ios.copyArrayFloat (fileOffset_p + arrOffset_p, other.fileOffset_p + other.arrOffset_p, shape_p.product()); break; case TpDouble: ios.copyArrayDouble (fileOffset_p + arrOffset_p, other.fileOffset_p + other.arrOffset_p, shape_p.product()); break; case TpComplex: ios.copyArrayComplex (fileOffset_p + arrOffset_p, other.fileOffset_p + other.arrOffset_p, shape_p.product()); break; case TpDComplex: ios.copyArrayDComplex (fileOffset_p + arrOffset_p, other.fileOffset_p + other.arrOffset_p, shape_p.product()); break; case TpString: ios.copyArrayString (fileOffset_p + arrOffset_p, other.fileOffset_p + other.arrOffset_p, shape_p.product()); break; default: throw DataManInternalError("Unhandled data type in StIndArray::copyData"); } } void StIndArray::checkShape (const IPosition& userArrayShape, const IPosition& tableArrayShape) const { if (! userArrayShape.isEqual (tableArrayShape)) { throw (DataManInternalError ("StManIndArray::get/put shapes not conforming")); } } void StIndArray::getArrayV (StManArrayFile& ios, ArrayBase& arr, DataType dtype) { checkShape (arr.shape(), shape_p); Bool deleteIt; void* value = arr.getVStorage (deleteIt); switch (dtype) { case TpBool: ios.get (fileOffset_p + arrOffset_p, 0, shape_p.product(), static_cast(value)); break; case TpUChar: ios.get (fileOffset_p + arrOffset_p, 0, shape_p.product(), static_cast(value)); break; case TpShort: ios.get (fileOffset_p + arrOffset_p, 0, shape_p.product(), static_cast(value)); break; case TpUShort: ios.get (fileOffset_p + arrOffset_p, 0, shape_p.product(), static_cast(value)); break; case TpInt: ios.get (fileOffset_p + arrOffset_p, 0, shape_p.product(), static_cast(value)); break; case TpUInt: ios.get (fileOffset_p + arrOffset_p, 0, shape_p.product(), static_cast(value)); break; case TpInt64: ios.get (fileOffset_p + arrOffset_p, 0, shape_p.product(), static_cast(value)); break; case TpFloat: ios.get (fileOffset_p + arrOffset_p, 0, shape_p.product(), static_cast(value)); break; case TpDouble: ios.get (fileOffset_p + arrOffset_p, 0, shape_p.product(), static_cast(value)); break; case TpComplex: ios.get (fileOffset_p + arrOffset_p, 0, shape_p.product(), static_cast(value)); break; case TpDComplex: ios.get (fileOffset_p + arrOffset_p, 0, shape_p.product(), static_cast(value)); break; case TpString: ios.get (fileOffset_p + arrOffset_p, 0, shape_p.product(), static_cast(value)); break; default: const void* cvalue = value; arr.freeVStorage (cvalue, deleteIt); throw DataManInternalError("Unhandled data type in StIndArray::getArrayV"); break; } arr.putVStorage (value, deleteIt); } void StIndArray::putArrayV (StManArrayFile& ios, const ArrayBase& arr, DataType dtype) { checkShape (arr.shape(), shape_p); Bool deleteIt; const void* value = arr.getVStorage (deleteIt); switch (dtype) { case TpBool: ios.put (fileOffset_p + arrOffset_p, 0, shape_p.product(), static_cast(value)); break; case TpUChar: ios.put (fileOffset_p + arrOffset_p, 0, shape_p.product(), static_cast(value)); break; case TpShort: ios.put (fileOffset_p + arrOffset_p, 0, shape_p.product(), static_cast(value)); break; case TpUShort: ios.put (fileOffset_p + arrOffset_p, 0, shape_p.product(), static_cast(value)); break; case TpInt: ios.put (fileOffset_p + arrOffset_p, 0, shape_p.product(), static_cast(value)); break; case TpUInt: ios.put (fileOffset_p + arrOffset_p, 0, shape_p.product(), static_cast(value)); break; case TpInt64: ios.put (fileOffset_p + arrOffset_p, 0, shape_p.product(), static_cast(value)); break; case TpFloat: ios.put (fileOffset_p + arrOffset_p, 0, shape_p.product(), static_cast(value)); break; case TpDouble: ios.put (fileOffset_p + arrOffset_p, 0, shape_p.product(), static_cast(value)); break; case TpComplex: ios.put (fileOffset_p + arrOffset_p, 0, shape_p.product(), static_cast(value)); break; case TpDComplex: ios.put (fileOffset_p + arrOffset_p, 0, shape_p.product(), static_cast(value)); break; case TpString: ios.put (fileOffset_p + arrOffset_p, 0, shape_p.product(), static_cast(value)); break; default: arr.freeVStorage (value, deleteIt); throw DataManInternalError("Unhandled data type in StIndArray::putArrayV"); break; } arr.freeVStorage (value, deleteIt); } void StIndArray::getSliceV (StManArrayFile& ios, const Slicer& ns, ArrayBase& arr, DataType dtype) { Bool deleteIt; void* value = arr.getVStorage (deleteIt); switch (dtype) { case TpBool: getSliceData (ios, ns, value, arr.shape(), &StIndArray::getVecBoolV); break; case TpUChar: getSliceData (ios, ns, value, arr.shape(), &StIndArray::getVecuCharV); break; case TpShort: getSliceData (ios, ns, value, arr.shape(), &StIndArray::getVecShortV); break; case TpUShort: getSliceData (ios, ns, value, arr.shape(), &StIndArray::getVecuShortV); break; case TpInt: getSliceData (ios, ns, value, arr.shape(), &StIndArray::getVecIntV); break; case TpUInt: getSliceData (ios, ns, value, arr.shape(), &StIndArray::getVecuIntV); break; case TpInt64: getSliceData (ios, ns, value, arr.shape(), &StIndArray::getVecInt64V); break; case TpFloat: getSliceData (ios, ns, value, arr.shape(), &StIndArray::getVecfloatV); break; case TpDouble: getSliceData (ios, ns, value, arr.shape(), &StIndArray::getVecdoubleV); break; case TpComplex: getSliceData (ios, ns, value, arr.shape(), &StIndArray::getVecComplexV); break; case TpDComplex: getSliceData (ios, ns, value, arr.shape(), &StIndArray::getVecDComplexV); break; case TpString: getSliceData (ios, ns, value, arr.shape(), &StIndArray::getVecStringV); break; default: const void* cvalue = value; arr.freeVStorage (cvalue, deleteIt); throw DataManInternalError("Unhandled data type in StIndArray::getSliceV"); break; } arr.putVStorage (value, deleteIt); } void StIndArray::putSliceV (StManArrayFile& ios, const Slicer& ns, const ArrayBase& arr, DataType dtype) { Bool deleteIt; const void* value = arr.getVStorage (deleteIt); switch (dtype) { case TpBool: putSliceData (ios, ns, value, arr.shape(), &StIndArray::putVecBoolV); break; case TpUChar: putSliceData (ios, ns, value, arr.shape(), &StIndArray::putVecuCharV); break; case TpShort: putSliceData (ios, ns, value, arr.shape(), &StIndArray::putVecShortV); break; case TpUShort: putSliceData (ios, ns, value, arr.shape(), &StIndArray::putVecuShortV); break; case TpInt: putSliceData (ios, ns, value, arr.shape(), &StIndArray::putVecIntV); break; case TpUInt: putSliceData (ios, ns, value, arr.shape(), &StIndArray::putVecuIntV); break; case TpInt64: putSliceData (ios, ns, value, arr.shape(), &StIndArray::putVecInt64V); break; case TpFloat: putSliceData (ios, ns, value, arr.shape(), &StIndArray::putVecfloatV); break; case TpDouble: putSliceData (ios, ns, value, arr.shape(), &StIndArray::putVecdoubleV); break; case TpComplex: putSliceData (ios, ns, value, arr.shape(), &StIndArray::putVecComplexV); break; case TpDComplex: putSliceData (ios, ns, value, arr.shape(), &StIndArray::putVecDComplexV); break; case TpString: putSliceData (ios, ns, value, arr.shape(), &StIndArray::putVecStringV); break; default: arr.freeVStorage (value, deleteIt); throw DataManInternalError("Unhandled data type in StIndArray::putSliceV"); break; } arr.freeVStorage (value, deleteIt); } void StIndArray::getVecfloatV (StManArrayFile& ios, Int64 fileOffset, uInt64 start, uInt64 leng, uInt64 inc, uInt64 valInx, void* value) { float* valp = (float*)value + valInx; if (inc == 1) { ios.get (fileOffset, start, leng, valp); }else{ while (leng-- > 0) { ios.get (fileOffset, start, 1, valp++); start += inc; } } } void StIndArray::getSliceData (StManArrayFile& ios, const Slicer& ns, void* value, const IPosition& userShape, void (*getVec) (StManArrayFile&, Int64, uInt64, uInt64, uInt64, uInt64, void*)) { //# Check if the shape of the slice and user array match. uInt ndim = ns.ndim(); IPosition blc(ndim), trc(ndim), inc(ndim), shape(ndim); shape = ns.inferShapeFromSource (shape_p, blc,trc,inc); checkShape (userShape, shape); //# We'll get a vector at the time; get its length. //# Get the offset of the array in the file. //# If the array is 1-dim, we can just use the vector get. uInt64 leng = shape(0); Int64 fileOffset = fileOffset_p + arrOffset_p; if (ndim == 1) { getVec (ios, fileOffset, blc(0), leng, inc(0), 0, value); }else{ //# Loop through the slice a vector at a time. ArrayPositionIterator iter(shape, 1); IPosition pos(ndim); uInt64 count=0; while (! iter.pastEnd()) { //# Get the iterator position in the slice and transform //# that to the file-offset for the corresponding part in //# the table array. pos = iter.pos(); uInt64 offset = 0; for (uInt i=ndim-1; i>0; i--) { offset += blc(i) + pos(i) * inc(i); offset *= shape_p(i-1); } offset += blc(0); getVec (ios, fileOffset, offset, leng, inc(0), count, value); count += leng; iter.next(); } } } void StIndArray::putVecfloatV (StManArrayFile& ios, Int64 fileOffset, uInt64 start, uInt64 leng, uInt64 inc, uInt64 valInx, const void* value) { float* valp = (float*)value + valInx; if (inc == 1) { ios.put (fileOffset, start, leng, valp); }else{ while (leng-- > 0) { ios.put (fileOffset, start, 1, valp++); start += inc; } } } //# putSliceData works similar to getSliceData. void StIndArray::putSliceData (StManArrayFile& ios, const Slicer& ns, const void* value, const IPosition& userShape, void (*putVec) (StManArrayFile&, Int64, uInt64, uInt64, uInt64, uInt64, const void*)) { uInt ndim = ns.ndim(); IPosition blc(ndim), trc(ndim), inc(ndim), shape(ndim); shape = ns.inferShapeFromSource (shape_p, blc,trc,inc); checkShape (userShape, shape); uInt64 leng = shape(0); Int64 fileOffset = fileOffset_p + arrOffset_p; if (ndim == 1) { putVec (ios, fileOffset, blc(0), leng, inc(0), 0, value); }else{ ArrayPositionIterator iter(shape, 1); IPosition pos(ndim); uInt64 count=0; while (! iter.pastEnd()) { pos = iter.pos(); uInt64 offset = 0; for (uInt i=ndim-1; i>0; i--) { offset += blc(i) + pos(i) * inc(i); offset *= shape_p(i-1); } offset += blc(0); putVec (ios, fileOffset, offset, leng, inc(0), count, value); count += leng; iter.next(); } } } #define STINDARRAY_GETPUT(T,NM) \ void StIndArray::aips_name2(getVec,NM) \ (StManArrayFile& ios, Int64 fileOffset, \ uInt64 start, uInt64 leng, uInt64 inc, uInt64 valInx, \ void* value) \ { \ T* valp = (T*)value + valInx; \ if (inc == 1) { \ ios.get (fileOffset, start, leng, valp); \ }else{ \ while (leng-- > 0) { \ ios.get (fileOffset, start, 1, valp++); \ start += inc; \ } \ } \ } \ void StIndArray::aips_name2(putVec,NM) \ (StManArrayFile& ios, Int64 fileOffset, \ uInt64 start, uInt64 leng, uInt64 inc, uInt64 valInx, \ const void* value) \ { \ T* valp = (T*)value + valInx; \ if (inc == 1) { \ ios.put (fileOffset, start, leng, valp); \ }else{ \ while (leng-- > 0) { \ ios.put (fileOffset, start, 1, valp++); \ start += inc; \ } \ } \ } STINDARRAY_GETPUT(Bool,BoolV) STINDARRAY_GETPUT(uChar,uCharV) STINDARRAY_GETPUT(Short,ShortV) STINDARRAY_GETPUT(uShort,uShortV) STINDARRAY_GETPUT(Int,IntV) STINDARRAY_GETPUT(uInt,uIntV) STINDARRAY_GETPUT(Int64,Int64V) //#//STINDARRAY_GETPUT(float,floatV) // done explicitly for debug purposes STINDARRAY_GETPUT(double,doubleV) STINDARRAY_GETPUT(Complex,ComplexV) STINDARRAY_GETPUT(DComplex,DComplexV) STINDARRAY_GETPUT(String,StringV) } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/StIndArray.h000066400000000000000000000342261476623553700203030ustar00rootroot00000000000000//# StIndArray.h: Read/write indirect arrays //# Copyright (C) 1994,1995,1996,1997,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_STINDARRAY_H #define TABLES_STINDARRAY_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class Slicer; class ArrayBase; // // Read/write indirect arrays // // // // // //# Classes you should understand before using this one. //
      • StManArrayFile // // // StIndArray stores indirect arrays on behalf of a storage manager. // // // StIndArray is a helper class for accessing indirect table arrays. // It is the interface between a storage manager like StManAipsIO // (in particular its indirect array column class // // StManColumnIndArrayAipsIO) // and the data storage class // StManArrayFile // which represents the file holding the shapes and data of the arrays. // This file holds the data in canonical format. // // StIndArray holds information about an array in the file. //
          //
        1. Offset of the array in the file. This points to the array shape. // This is stored by storage managers and serves as the mapping between // row number and array. //
        2. Array data offset, i.e. the length of the shape in the file. // Because the data is stored in canonical format, the length of the // shape in the file is not directly known but has to be supplied this way. //
        3. The actual shape of the array //
        // The storage manager creates an StIndArray object for each row. // When an array is accessed for the first time, // the array data offset and the shape will be filled in by StIndArray. // In this way it serves as a cache for the array shape. // // StIndArray implements all necessary functions to get/put an array or // an array slice from/into file supplied by the given StManArrayFile object. // The StManArrayFile object itself has to be created by the storage manager // and given to the StIndArray functions. //
        // // This helper class makes it possible to share equal functionality // between various storage managers handling indirect arrays. // At the moment it is used by the StManAipsIO, IncrementalStMan, and // StandardStMan storage managers, but it is not limited to them. It can // equally well be used by any other storage manager storing (indirect) arrays // via an StManArrayFile object. // // // Note that the following example is not really useful. // StIndArray is an internal class and should not be used by a casual user. // The example may however give a bit of insight. // // Array array(...); // // Create an StManArrayFile object to hold the arrays. // StManArrayFile stmanFile ("some.name", ByteIO::New); // // Create a still empty StIndArray object for an array. // StIndArray arrayRef(0); // // Define the shape and allocate a Float array. // // Put the array data. // arrayRef.setShape (stmanFile, TpFloat, array.shape()); // arrayRef.putArrayfloatV (stmanFile, &array); // // Get the file offset of the array (for later use). // Int64 offset = arrayRef.fileOffset(); // // Create an StIndArray object to read the array back. // // Of course, the same object could have been used for that purpose, // // but this shows how to create one for an existing file. // StIndArray arrayRef2(offset); // arrayRef2.getShape (stmanFile); // read shape // Array array2(arrayRef2.shape()); // create with correct size // arrayRef2.getArrayfloatV (stmanFile, &array2); // // // //# A List of bugs, limitations, extensions or planned refinements. //
      • Reuse file storage when an array gets reshaped. // This could be done if the array does not grow. // It also requires a change in StManArrayFile. //
      • Be smarter when accessing slices by not accessing a vector // at a time, but by determining and accessing the largest // possible consecutive area. // class StIndArray { public: // Construct the object with the given file offset. // A zero file offset means that no array has been defined yet. // That may be filled in later by setShape. StIndArray (Int64 fileOffset); // Copy constructor. StIndArray (const StIndArray&); // Assignment. StIndArray& operator= (const StIndArray&); ~StIndArray(); // Get the shape. const IPosition& shape() const {return shape_p;} // Get the file offset. Int64 fileOffset() const {return fileOffset_p;} // Set the shape and allocate the array in the file. // This will define the array and fill in the file offset. // If the shape is already defined and does not change, // nothing is done and a False value is returned. // If the shape changes, the old file space is lost. Bool setShape (StManArrayFile&, int dataType, const IPosition& shape); // Read the shape if not read yet. void getShape (StManArrayFile& ios); // Get the reference count. uInt refCount (StManArrayFile& ios); // Increment the reference count. void incrementRefCount (StManArrayFile& ios); // Decrement the reference count. void decrementRefCount (StManArrayFile& ios); // Copy the data from another array. // An exception if thrown if the shapes do not match. void copyData (StManArrayFile& ios, int dataType, const StIndArray& other); // Get an array value from the file at the offset held in this object. // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the ArrayColumn get function). void getArrayV (StManArrayFile& ios, ArrayBase& arr, DataType dtype); // Put an array value into the file at the offset held in this object. // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the ArrayColumn put function). void putArrayV (StManArrayFile& ios, const ArrayBase& arr, DataType dtype); // Get a section of the array from the file at the offset held in // this object. // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the ArrayColumn getSlice function). void getSliceV (StManArrayFile&, const Slicer&, ArrayBase& dataPtr, DataType dtype); // Put a section of the array into the file at the offset held in // this object. // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the ArrayColumn putSlice function). void putSliceV (StManArrayFile&, const Slicer&, const ArrayBase& dataPtr, DataType dtype); private: Int64 fileOffset_p; //# offset of shape in StManArrayFile uInt arrOffset_p; //# extra offset to the array //# 0 = arrOffset and shape not known yet IPosition shape_p; //# shape of the array // Get sliced data, i.e. get a section of an array. // This function is used by getSliceXXXV to have common functionality // in one function. It calls the given getVec function for each // chunk of data. In this way the bulk of type-independent code // is concentrated in getSliceData resulting in small // type-dependent functions. void getSliceData (StManArrayFile&, const Slicer& ns, void* value, const IPosition& userArrayShape, void (*getVec) (StManArrayFile&, Int64, uInt64, uInt64, uInt64, uInt64, void* dataPtr)); // Get a (type-dependent) vector part of a slice. // This function is called for each chunk by putSliceData. // static void getVecBoolV (StManArrayFile&, Int64 fileOffset, uInt64 arrayStart, uInt64 length, uInt64 increment, uInt64 valueIndex, void* value); static void getVecuCharV (StManArrayFile&, Int64 fileOffset, uInt64 arrayStart, uInt64 length, uInt64 increment, uInt64 valueIndex, void* value); static void getVecShortV (StManArrayFile&, Int64 fileOffset, uInt64 arrayStart, uInt64 length, uInt64 increment, uInt64 valueIndex, void* value); static void getVecuShortV (StManArrayFile&, Int64 fileOffset, uInt64 arrayStart, uInt64 length, uInt64 increment, uInt64 valueIndex, void* value); static void getVecIntV (StManArrayFile&, Int64 fileOffset, uInt64 arrayStart, uInt64 length, uInt64 increment, uInt64 valueIndex, void* value); static void getVecuIntV (StManArrayFile&, Int64 fileOffset, uInt64 arrayStart, uInt64 length, uInt64 increment, uInt64 valueIndex, void* value); static void getVecInt64V (StManArrayFile&, Int64 fileOffset, uInt64 arrayStart, uInt64 length, uInt64 increment, uInt64 valueIndex, void* value); static void getVecfloatV (StManArrayFile&, Int64 fileOffset, uInt64 arrayStart, uInt64 length, uInt64 increment, uInt64 valueIndex, void* value); static void getVecdoubleV (StManArrayFile&, Int64 fileOffset, uInt64 arrayStart, uInt64 length, uInt64 increment, uInt64 valueIndex, void* value); static void getVecComplexV (StManArrayFile&, Int64 fileOffset, uInt64 arrayStart, uInt64 length, uInt64 increment, uInt64 valueIndex, void* value); static void getVecDComplexV (StManArrayFile&, Int64 fileOffset, uInt64 arrayStart, uInt64 length, uInt64 increment, uInt64 valueIndex, void* value); static void getVecStringV (StManArrayFile&, Int64 fileOffset, uInt64 arrayStart, uInt64 length, uInt64 increment, uInt64 valueIndex, void* value); // // Put sliced data, i.e. put a section of an array. // This function is used by putSlice to have common functionality // in one function. It calls the given in putVec function for // chunk of data. In this way the bulk of type-independent code // is concentrated in putSliceData resulting in small // type-dependent functions. void putSliceData (StManArrayFile&, const Slicer& ns, const void* value, const IPosition& userArrayShape, void (*putVec) (StManArrayFile&, Int64, uInt64, uInt64, uInt64, uInt64, const void* dataPtr)); // Put a (type-dependent) vector part of a slice. // This function is called for each chunk by putSliceData. // static void putVecBoolV (StManArrayFile&, Int64 fileOffset, uInt64 arrayStart, uInt64 length, uInt64 increment, uInt64 valueIndex, const void* value); static void putVecuCharV (StManArrayFile&, Int64 fileOffset, uInt64 arrayStart, uInt64 length, uInt64 increment, uInt64 valueIndex, const void* value); static void putVecShortV (StManArrayFile&, Int64 fileOffset, uInt64 arrayStart, uInt64 length, uInt64 increment, uInt64 valueIndex, const void* value); static void putVecuShortV (StManArrayFile&, Int64 fileOffset, uInt64 arrayStart, uInt64 length, uInt64 increment, uInt64 valueIndex, const void* value); static void putVecIntV (StManArrayFile&, Int64 fileOffset, uInt64 arrayStart, uInt64 length, uInt64 increment, uInt64 valueIndex, const void* value); static void putVecuIntV (StManArrayFile&, Int64 fileOffset, uInt64 arrayStart, uInt64 length, uInt64 increment, uInt64 valueIndex, const void* value); static void putVecInt64V (StManArrayFile&, Int64 fileOffset, uInt64 arrayStart, uInt64 length, uInt64 increment, uInt64 valueIndex, const void* value); static void putVecfloatV (StManArrayFile&, Int64 fileOffset, uInt64 arrayStart, uInt64 length, uInt64 increment, uInt64 valueIndex, const void* value); static void putVecdoubleV (StManArrayFile&, Int64 fileOffset, uInt64 arrayStart, uInt64 length, uInt64 increment, uInt64 valueIndex, const void* value); static void putVecComplexV (StManArrayFile&, Int64 fileOffset, uInt64 arrayStart, uInt64 length, uInt64 increment, uInt64 valueIndex, const void* value); static void putVecDComplexV (StManArrayFile&, Int64 fileOffset, uInt64 arrayStart, uInt64 length, uInt64 increment, uInt64 valueIndex, const void* value); static void putVecStringV (StManArrayFile&, Int64 fileOffset, uInt64 arrayStart, uInt64 length, uInt64 increment, uInt64 valueIndex, const void* value); // // Throw an exception if the shape of the given array and the table // array (slice) are not equal. void checkShape (const IPosition& userArrayShape, const IPosition& tableArrayShape) const; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/StManAipsIO.cc000066400000000000000000000251641476623553700205110ustar00rootroot00000000000000//# StManAipsIO.cc: Storage manager for tables using AipsIO //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN #define EXTBLSZ 32 StManColumnAipsIO::StManColumnAipsIO (StManAipsIO* smptr, int dataType, Bool byPtr) : MSMColumn(smptr, dataType, byPtr) {} StManColumnAipsIO::~StManColumnAipsIO() {} void StManColumnAipsIO::initData (void*, rownr_t) {} //# Write all data into AipsIO. void StManColumnAipsIO::putFile (rownr_t nrval, AipsIO& ios) { ios.putstart ("StManColumnAipsIO", 2); // class version 2 ios << uInt(nrval); uInt nr; for (uInt i=1; i<=nrext_p; i++) { nr = ncum_p[i] - ncum_p[i-1]; if (nr > nrval) { nr = nrval; } if (nr > 0) { ios << nr; putData (data_p[i], nr, ios); nrval -= nr; } } ios.putend(); } void StManColumnAipsIO::putData (void* dp, uInt nrval, AipsIO& ios) { switch (dtype()) { case TpBool: ios.put (nrval, (Bool*)dp); break; case TpUChar: ios.put (nrval, (uChar*)dp); break; case TpShort: ios.put (nrval, (Short*)dp); break; case TpUShort: ios.put (nrval, (uShort*)dp); break; case TpInt: ios.put (nrval, (Int*)dp); break; case TpUInt: ios.put (nrval, (uInt*)dp); break; case TpInt64: ios.put (nrval, (Int64*)dp); break; case TpFloat: ios.put (nrval, (float*)dp); break; case TpDouble: ios.put (nrval, (double*)dp); break; case TpComplex: ios.put (nrval, (Complex*)dp); break; case TpDComplex: ios.put (nrval, (DComplex*)dp); break; case TpString: ios.put (nrval, (String*)dp); break; default: throw DataManInvDT("StManAipsIO::putData"); } } //# Read all data from AipsIO. void StManColumnAipsIO::getFile (rownr_t nrval, AipsIO& ios) { uInt version = ios.getstart ("StManColumnAipsIO"); uInt nr; //# Get and check nr of values. ios >> nr; if (nr != nrval) { throw (DataManInternalError ("StManColumnAipsIO::getFile: mismatch in #values")); } deleteAll(); if (nrval > 0) { resize (nrval); void* datap = data_p[1]; uInt nrd=0; while (nrd < nrval) { ios >> nr; if (nr == 0) { nr = nrval - nrd; } if (nr+nrd > nrval) { throw (DataManInternalError ("StManColumnAipsIO::getFile")); } getData (datap, nrd, nr, ios, version); nrd += nr; } } ios.getend(); columnCache().invalidate(); } void StManColumnAipsIO::getData (void* datap, uInt inx, uInt nrval, AipsIO& ios, uInt) { uInt nr; ios >> nr; switch (dtype()) { case TpBool: ios.get (nrval, (Bool*)datap + inx); break; case TpUChar: ios.get (nrval, (uChar*)datap + inx); break; case TpShort: ios.get (nrval, (Short*)datap + inx); break; case TpUShort: ios.get (nrval, (uShort*)datap + inx); break; case TpInt: ios.get (nrval, (Int*)datap + inx); break; case TpUInt: ios.get (nrval, (uInt*)datap + inx); break; case TpInt64: ios.get (nrval, (Int64*)datap + inx); break; case TpFloat: ios.get (nrval, (float*)datap + inx); break; case TpDouble: ios.get (nrval, (double*)datap + inx); break; case TpComplex: ios.get (nrval, (Complex*)datap + inx); break; case TpDComplex: ios.get (nrval, (DComplex*)datap + inx); break; case TpString: ios.get (nrval, (String*)datap + inx); break; default: throw DataManInvDT("StManAipsIO::getData"); } } StManAipsIO::StManAipsIO () : MSMBase(), uniqnr_p (0), iosfile_p (0) {} StManAipsIO::StManAipsIO (const String& storageManagerName) : MSMBase (storageManagerName), uniqnr_p (0), iosfile_p (0) {} StManAipsIO::StManAipsIO (const String& storageManagerName, const Record& rec) : MSMBase (storageManagerName, rec), uniqnr_p (0), iosfile_p (0) {} StManAipsIO::~StManAipsIO() { delete iosfile_p; } DataManager* StManAipsIO::clone() const { StManAipsIO* smp = new StManAipsIO (stmanName_p); return smp; } DataManager* StManAipsIO::makeObject (const String& storageManagerName, const Record& spec) { StManAipsIO* smp = new StManAipsIO (storageManagerName, spec); return smp; } String StManAipsIO::dataManagerType() const { return "StManAipsIO"; } DataManagerColumn* StManAipsIO::makeScalarColumn (const String& columnName, int dataType, const String&) { //# Check if data type is not TpOther. throwDataTypeOther (columnName, dataType); //# Extend colSet_p block if needed. if (ncolumn() >= colSet_p.nelements()) { colSet_p.resize (colSet_p.nelements() + 32); } StManColumnAipsIO* colp = new StManColumnAipsIO (this, dataType, False); colSet_p[ncolumn()] = colp; return colp; } DataManagerColumn* StManAipsIO::makeDirArrColumn (const String& columnName, int dataType, const String&) { //# Check if data type is not TpOther. throwDataTypeOther (columnName, dataType); //# Extend colSet_p block if needed. if (ncolumn() >= colSet_p.nelements()) { colSet_p.resize (colSet_p.nelements() + 32); } StManColumnAipsIO* colp = new StManColumnArrayAipsIO (this, dataType); colSet_p[ncolumn()] = colp; return colp; } DataManagerColumn* StManAipsIO::makeIndArrColumn (const String& columnName, int dataType, const String&) { //# Check if data type is not TpOther. throwDataTypeOther (columnName, dataType); //# Extend colSet_p block if needed. if (ncolumn() >= colSet_p.nelements()) { colSet_p.resize (colSet_p.nelements() + 32); } StManColumnAipsIO* colp = new StManColumnIndArrayAipsIO (this, dataType); colSet_p[ncolumn()] = colp; return colp; } Bool StManAipsIO::flush (AipsIO&, Bool) { //# Do not write if nothing has been put. if (! hasPut_p) { return False; } uInt i; AipsIO ios(fileName(), ByteIO::New); ios.putstart ("StManAipsIO", 2); // version 2 //# Write the number of rows and columns and the column types. //# This is only done to check it when reading back. //# Note that an AipsIO object cannot exceed 4 GB, so nrrow_p always //# fits in 32 bits. ios << stmanName_p; // this is added in version 2 ios << sequenceNr(); ios << uniqnr_p; ios << uInt(nrrow_p); ios << ncolumn(); for (i=0; idataType(); } for (i=0; iputFile (nrrow_p, ios); } ios.putend(); hasPut_p = False; return True; } void StManAipsIO::create64 (rownr_t nrrow) { nrrow_p = nrrow; //# Let the column create something if needed. for (uInt i=0; idoCreate (nrrow); } setHasPut(); } rownr_t StManAipsIO::open64 (rownr_t tabNrrow, AipsIO&) { return resync64 (tabNrrow); } rownr_t StManAipsIO::resync64 (rownr_t nrrow) { if (iosfile_p != 0) { iosfile_p->resync(); } AipsIO ios(fileName()); uInt version = ios.getstart ("StManAipsIO"); //# Get and check the number of rows and columns and the column types. uInt i, nrr, nrc, snr; int dt; if (version > 1) { ios >> stmanName_p; } ios >> snr; ios >> uniqnr_p; ios >> nrr; ios >> nrc; if (snr != sequenceNr() || nrc != ncolumn()) { throw (DataManInternalError ("StManAipsIO::open: mismatch in seqnr,#col")); } if (nrrow != nrr) { #if defined(TABLEREPAIR) cerr << "StManAipsIO::open: mismatch in #row (expected " << nrrow << ", found " << nrr << ")" << endl; cerr << "Remainder will be added or discarded" << endl; setHasPut(); #else throw (DataManInternalError ("StManAipsIO::open: mismatch in #row; expected " + String::toString(nrrow) + ", found " + String::toString(nrr))); #endif } for (i=0; i> dt; if (dt != colSet_p[i]->dataType()) { throw (DataManInternalError ("StManAipsIO::open: mismatch in data type")); } } //# Now read in all the columns. for (i=0; igetFile (nrr, ios); //# The following can only be executed in case of TABLEREPAIR. //# Add rows if storage manager has fewer rows than table. //# Remove rows if storage manager has more rows than table. if (nrrow > nrr) { colSet_p[i]->addRow (nrrow, nrr); } else if (nrrow < nrr) { for (uInt r=nrrow; rremove (nrrow); } } } nrrow_p = nrrow; ios.getend(); return nrrow_p; } StManArrayFile* StManAipsIO::openArrayFile (ByteIO::OpenOption opt) { if (iosfile_p == 0) { iosfile_p = new StManArrayFile (fileName() + 'i', opt); } return iosfile_p; } void StManAipsIO::reopenRW() { for (uInt i=0; ireopenRW(); } } void StManAipsIO::deleteManager() { delete iosfile_p; iosfile_p = 0; DOos::remove (fileName() + 'i', False, False); DOos::remove (fileName(), False, False); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/StManAipsIO.h000066400000000000000000000233011476623553700203420ustar00rootroot00000000000000//# StManAipsIO.h: Storage manager for tables using AipsIO //# Copyright (C) 1994,1995,1996,1997,1998,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_STMANAIPSIO_H #define TABLES_STMANAIPSIO_H //# Includes #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward clarations class AipsIO; class StManAipsIO; class StManArrayFile; // // AipsIO table column storage manager class // // // // // //# Classes you should understand before using this one. //
      • DataManagerColumn // // // StManColumnAipsIO handles a column for an AipsIO storage manager. // // // StManColumnAipsIO is used by StManAipsIO to handle the access to // the data in a table column. // It is an storage manager based on AipsIO. The entire column is // kept in memory and only written when the storage manager closes. // When the storage manager gets opened, the entire column gets // read back. // It fully supports addition and removal of rows. // // StManColumnAipsIO serves 2 purposes: //
          //
        1. It handles a column containing scalar values. //
        2. It serves as a base class for StManArrayColumnAipsIO and // StManIndArrayColumnAipsIO. These classes handle arrays and // use StManColumnAipsIO to hold a pointer to the array in each row. //
        // // StManColumnAipsIO does not hold a column as a consecutive array, // because extending the column (i.e. adding rows) proofed be too // expensive due to the repeated copying involved when creating a table // (this method was used by the old table system). // Instead it has a number of data blocks (extensions) indexed to by a // super block. Accessing a row means finding the appropriate extension // via a binary search. Because there is only 1 extension when a table is // read back, the overhead in finding a row is small. //
        // // StManColumnAipsIO handles the standard data types. The class // is not templated, but a switch statement is used instead. // Templates would cause too many instantiations. // // //# A List of bugs, limitations, extensions or planned refinements. // class StManColumnAipsIO : public MSMColumn { public: // Create a column of the given type. // It will maintain a pointer to its parent storage manager. StManColumnAipsIO (StManAipsIO* stMan, int dataType, Bool byPtr); // Frees up the storage. virtual ~StManColumnAipsIO(); // Forbid copy constructor. StManColumnAipsIO (const StManColumnAipsIO&) = delete; // Forbid assignment. StManColumnAipsIO& operator= (const StManColumnAipsIO&) = delete; // Write the column data into AipsIO. // It will successively write all extensions using putData. virtual void putFile (rownr_t nrval, AipsIO&); // Read the column data from AipsIO. // One extension gets allocated to hold all rows in the column. virtual void getFile (rownr_t nrval, AipsIO&); protected: // initData does not do anything (only used in MSMColumn). virtual void initData (void* datap, rownr_t nrval); // Put the data (nrval elements) in an extension (starting at datap) // into AipsIO. virtual void putData (void* datap, uInt nrval, AipsIO&); // Get data (nrval elements) into an extension (starting at datap // plus the given index). virtual void getData (void* datap, uInt index, uInt nrval, AipsIO&, uInt version); }; // // AipsIO table storage manager class // // // // // //# Classes you should understand before using this one. //
      • DataManager //
      • StManColumnAipsIO // // // StManAipsIO is the storage manager using AipsIO. // // // StManAipsIO is a table storage manager based on AipsIO. // It holds the data in the columns in memory and writes them to // a file when the table gets closed. Only the data of indirect arrays // are directly read/written from/to a file. // It contains pointers to the underlying StManColumnAipsIO objects, // which do the actual data handling. // // The AipsIO storage manager does fully support addition and removal // of rows and columns. // // All data, except indirect columns, for this storage manager are kept // in one file. The file name is the table name appended with // .N_AipsIO, where N is the (unique) storage manager sequence number. // Each column containing indirect arrays is stored in a separate file // using class StManIndArrayColumnAipsIO. The name of such a file is // the storage manager file name appended with _cM, where M is a unique // column sequence number acquired using function uniqueNr(). // // //# A List of bugs, limitations, extensions or planned refinements. // class StManAipsIO : public MSMBase { public: // Create an AipsIO storage manager. // Its name will be blank. StManAipsIO(); // Create an AipsIO storage manager with the given name. // Its name can be used later in e.g. Table::addColumn to // add a column to this storage manager. //
        Note that the 2nd constructor is needed for table creation // from a record specification. // StManAipsIO (const String& storageManagerName); StManAipsIO (const String& storageManagerName, const Record&); // virtual ~StManAipsIO(); // Forbid copy constructor. StManAipsIO (const StManAipsIO&) = delete; // Forbid assignment. StManAipsIO& operator= (const StManAipsIO&) = delete; // Clone this object. // It does not clone StManAipsIOColumn objects possibly used. virtual DataManager* clone() const; // Get the type name of the data manager (i.e. StManAipsIO). virtual String dataManagerType() const; // Get a unique column number for the column // (it is only unique for this storage manager). // This is used by StManIndArrayColumnAipsIO to create a unique file name. uInt uniqueNr() { return uniqnr_p++; } // Make the object from the string. // This function gets registered in the DataManager "constructor" map. static DataManager* makeObject (const String& dataManagerType, const Record& spec); // Open (if needed) the file for indirect arrays with the given mode. // Return a pointer to the object. StManArrayFile* openArrayFile (ByteIO::OpenOption opt); private: // Flush and optionally fsync the data. // It returns a True status if it had to flush (i.e. if data have changed). virtual Bool flush (AipsIO&, Bool fsync); // Let the storage manager create files as needed for a new table. // This allows a column with an indirect array to create its file. virtual void create64 (rownr_t nrrow); // Open the storage manager file for an existing table and read in // the data and let the StManColumnAipsIO objects read their data. virtual rownr_t open64 (rownr_t nrrow, AipsIO&); // Resync the storage manager with the new file contents. // This is done by clearing the cache. virtual rownr_t resync64 (rownr_t nrrow); // Reopen the storage manager files for read/write. virtual void reopenRW(); // The data manager will be deleted (because all its columns are // requested to be deleted). // So clean up the things needed (e.g. delete files). virtual void deleteManager(); // Create a column in the storage manager on behalf of a table column. // // Create a scalar column. DataManagerColumn* makeScalarColumn (const String& name, int dataType, const String& dataTypeID); // Create a direct array column. DataManagerColumn* makeDirArrColumn (const String& name, int dataType, const String& dataTypeID); // Create an indirect array column. DataManagerColumn* makeIndArrColumn (const String& name, int dataType, const String& dataTypeID); // // Unique nr for column in this storage manager. uInt uniqnr_p; // The file containing the indirect arrays. StManArrayFile* iosfile_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/StManColumn.cc000066400000000000000000000754741476623553700206330ustar00rootroot00000000000000//# StManColumn.cc: Base storage manager column class //# Copyright (C) 1994,1995,1996,1998,1999,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN StManColumn::~StManColumn() {} // Map to backward compatibility functions. void StManColumn::setShape (rownr_t rownr, const IPosition& shape) { setShape (uInt(rownr), shape); } void StManColumn::setShapeTiled (rownr_t rownr, const IPosition& shape, const IPosition& tileShape) { setShapeTiled (uInt(rownr), shape, tileShape); } Bool StManColumn::isShapeDefined (rownr_t rownr) { return isShapeDefined (uInt(rownr)); } uInt StManColumn::ndim (rownr_t rownr) { return ndim (uInt(rownr)); } IPosition StManColumn::shape (rownr_t rownr) { return shape (uInt(rownr)); } IPosition StManColumn::tileShape (rownr_t rownr) { return tileShape(uInt(rownr)); } void StManColumn::setShape (uInt, const IPosition&) { throw DataManInvOper("setShape only allowed for non-FixedShape arrays" " in column " + columnName()); } void StManColumn::setShapeTiled (uInt rownr, const IPosition& shape, const IPosition&) { setShape (rownr, shape); } // By default the shape is defined (for scalars). Bool StManColumn::isShapeDefined (uInt) { return True; } // The default implementation of ndim is to use the shape. uInt StManColumn::ndim (uInt rownr) { return shape(rownr).nelements(); } // The shape of the array in the given row. IPosition StManColumn::shape (uInt) { return IPosition(0); } // The tile shape of the array in the given row. IPosition StManColumn::tileShape (uInt) { return IPosition(0); } // The following takes care of backward compatibility for external storage managers. // It maps the get/putXX functions taking rownr_t to the old get/putXXV taking uInt. // As before the default get/putXXV implementations throw a 'not implemented' exception. #define STMANCOLUMN_GETPUT_SCALAR(T,NM) \ void StManColumn::aips_name2(get,T) (rownr_t rownr, T* dataPtr) \ { aips_name2(get,NM) (rownr, dataPtr); } \ void StManColumn::aips_name2(put,T) (rownr_t rownr, const T* dataPtr) \ { aips_name2(put,NM) (rownr, dataPtr); } \ void StManColumn::aips_name2(get,NM) (uInt, T*) \ { throwInvalidOp(CASACORE_STRINGIFY(aips_name2(get,NM))); } \ void StManColumn::aips_name2(put,NM) (uInt, const T*) \ { throwInvalidOp(CASACORE_STRINGIFY(aips_name2(put,NM))); } \ STMANCOLUMN_GETPUT_SCALAR(Bool,BoolV) STMANCOLUMN_GETPUT_SCALAR(uChar,uCharV) STMANCOLUMN_GETPUT_SCALAR(Short,ShortV) STMANCOLUMN_GETPUT_SCALAR(uShort,uShortV) STMANCOLUMN_GETPUT_SCALAR(Int,IntV) STMANCOLUMN_GETPUT_SCALAR(uInt,uIntV) STMANCOLUMN_GETPUT_SCALAR(float,floatV) STMANCOLUMN_GETPUT_SCALAR(double,doubleV) STMANCOLUMN_GETPUT_SCALAR(Complex,ComplexV) STMANCOLUMN_GETPUT_SCALAR(DComplex,DComplexV) STMANCOLUMN_GETPUT_SCALAR(String,StringV) //# Call the correct getScalarColumnX function depending on the data type. void StManColumn::getScalarColumnV (ArrayBase& dataPtr) { switch (dtype()) { case TpBool: getScalarColumnBoolV (static_cast*>(&dataPtr)); break; case TpUChar: getScalarColumnuCharV (static_cast*>(&dataPtr)); break; case TpShort: getScalarColumnShortV (static_cast*>(&dataPtr)); break; case TpUShort: getScalarColumnuShortV (static_cast*>(&dataPtr)); break; case TpInt: getScalarColumnIntV (static_cast*>(&dataPtr)); break; case TpUInt: getScalarColumnuIntV (static_cast*>(&dataPtr)); break; case TpInt64: getScalarColumnInt64V (static_cast*>(&dataPtr)); break; case TpFloat: getScalarColumnfloatV (static_cast*>(&dataPtr)); break; case TpDouble: getScalarColumndoubleV (static_cast*>(&dataPtr)); break; case TpComplex: getScalarColumnComplexV (static_cast*>(&dataPtr)); break; case TpDComplex: getScalarColumnDComplexV (static_cast*>(&dataPtr)); break; case TpString: getScalarColumnStringV (static_cast*>(&dataPtr)); break; default: throw (DataManInvDT ("StManColumn::getScalarColumn")); } } //# Call the correct putScalarColumnX function depending on the data type. void StManColumn::putScalarColumnV (const ArrayBase& dataPtr) { switch (dtype()) { case TpBool: putScalarColumnBoolV (static_cast*>(&dataPtr)); break; case TpUChar: putScalarColumnuCharV (static_cast*>(&dataPtr)); break; case TpShort: putScalarColumnShortV (static_cast*>(&dataPtr)); break; case TpUShort: putScalarColumnuShortV (static_cast*>(&dataPtr)); break; case TpInt: putScalarColumnIntV (static_cast*>(&dataPtr)); break; case TpUInt: putScalarColumnuIntV (static_cast*>(&dataPtr)); break; case TpInt64: putScalarColumnInt64V (static_cast*>(&dataPtr)); break; case TpFloat: putScalarColumnfloatV (static_cast*>(&dataPtr)); break; case TpDouble: putScalarColumndoubleV (static_cast*>(&dataPtr)); break; case TpComplex: putScalarColumnComplexV (static_cast*>(&dataPtr)); break; case TpDComplex: putScalarColumnDComplexV (static_cast*>(&dataPtr)); break; case TpString: putScalarColumnStringV (static_cast*>(&dataPtr)); break; default: throw (DataManInvDT ("StManColumn::putScalarColumn")); } } //# Call the correct getScalarColumnCellsX function depending on the data type. void StManColumn::getScalarColumnCellsV (const RefRows& rownrs, ArrayBase& dataPtr) { switch (dtype()) { case TpBool: getScalarColumnCellsBoolV (rownrs, static_cast*>(&dataPtr)); break; case TpUChar: getScalarColumnCellsuCharV (rownrs, static_cast*>(&dataPtr)); break; case TpShort: getScalarColumnCellsShortV (rownrs, static_cast*>(&dataPtr)); break; case TpUShort: getScalarColumnCellsuShortV (rownrs, static_cast*>(&dataPtr)); break; case TpInt: getScalarColumnCellsIntV (rownrs, static_cast*>(&dataPtr)); break; case TpUInt: getScalarColumnCellsuIntV (rownrs, static_cast*>(&dataPtr)); break; case TpInt64: getScalarColumnCellsInt64V (rownrs, static_cast*>(&dataPtr)); break; case TpFloat: getScalarColumnCellsfloatV (rownrs, static_cast*>(&dataPtr)); break; case TpDouble: getScalarColumnCellsdoubleV (rownrs, static_cast*>(&dataPtr)); break; case TpComplex: getScalarColumnCellsComplexV (rownrs, static_cast*>(&dataPtr)); break; case TpDComplex: getScalarColumnCellsDComplexV (rownrs, static_cast*>(&dataPtr)); break; case TpString: getScalarColumnCellsStringV (rownrs, static_cast*>(&dataPtr)); break; default: throw (DataManInvDT ("StManColumn::getScalarColumnCells")); } } //# Call the correct putScalarColumnCellsX function depending on the data type. void StManColumn::putScalarColumnCellsV (const RefRows& rownrs, const ArrayBase& dataPtr) { switch (dtype()) { case TpBool: putScalarColumnCellsBoolV (rownrs, static_cast*>(&dataPtr)); break; case TpUChar: putScalarColumnCellsuCharV (rownrs, static_cast*>(&dataPtr)); break; case TpShort: putScalarColumnCellsShortV (rownrs, static_cast*>(&dataPtr)); break; case TpUShort: putScalarColumnCellsuShortV (rownrs, static_cast*>(&dataPtr)); break; case TpInt: putScalarColumnCellsIntV (rownrs, static_cast*>(&dataPtr)); break; case TpUInt: putScalarColumnCellsuIntV (rownrs, static_cast*>(&dataPtr)); break; case TpInt64: putScalarColumnCellsInt64V (rownrs, static_cast*>(&dataPtr)); break; case TpFloat: putScalarColumnCellsfloatV (rownrs, static_cast*>(&dataPtr)); break; case TpDouble: putScalarColumnCellsdoubleV (rownrs, static_cast*>(&dataPtr)); break; case TpComplex: putScalarColumnCellsComplexV (rownrs, static_cast*>(&dataPtr)); break; case TpDComplex: putScalarColumnCellsDComplexV (rownrs, static_cast*>(&dataPtr)); break; case TpString: putScalarColumnCellsStringV (rownrs, static_cast*>(&dataPtr)); break; default: throw (DataManInvDT ("StManColumn::putScalarColumnCells")); } } //# Call the correct getArrayX function depending on the data type. void StManColumn::getArrayV (rownr_t rownr, ArrayBase& dataPtr) { switch (dtype()) { case TpBool: getArrayBoolV (rownr, static_cast*>(&dataPtr)); break; case TpUChar: getArrayuCharV (rownr, static_cast*>(&dataPtr)); break; case TpShort: getArrayShortV (rownr, static_cast*>(&dataPtr)); break; case TpUShort: getArrayuShortV (rownr, static_cast*>(&dataPtr)); break; case TpInt: getArrayIntV (rownr, static_cast*>(&dataPtr)); break; case TpUInt: getArrayuIntV (rownr, static_cast*>(&dataPtr)); break; case TpInt64: getArrayInt64V (rownr, static_cast*>(&dataPtr)); break; case TpFloat: getArrayfloatV (rownr, static_cast*>(&dataPtr)); break; case TpDouble: getArraydoubleV (rownr, static_cast*>(&dataPtr)); break; case TpComplex: getArrayComplexV (rownr, static_cast*>(&dataPtr)); break; case TpDComplex: getArrayDComplexV (rownr, static_cast*>(&dataPtr)); break; case TpString: getArrayStringV (rownr, static_cast*>(&dataPtr)); break; default: throw (DataManInvDT ("StManColumn::getArray")); } } //# Call the correct putArrayX function depending on the data type. void StManColumn::putArrayV (rownr_t rownr, const ArrayBase& dataPtr) { switch (dtype()) { case TpBool: putArrayBoolV (rownr, static_cast*>(&dataPtr)); break; case TpUChar: putArrayuCharV (rownr, static_cast*>(&dataPtr)); break; case TpShort: putArrayShortV (rownr, static_cast*>(&dataPtr)); break; case TpUShort: putArrayuShortV (rownr, static_cast*>(&dataPtr)); break; case TpInt: putArrayIntV (rownr, static_cast*>(&dataPtr)); break; case TpUInt: putArrayuIntV (rownr, static_cast*>(&dataPtr)); break; case TpInt64: putArrayInt64V (rownr, static_cast*>(&dataPtr)); break; case TpFloat: putArrayfloatV (rownr, static_cast*>(&dataPtr)); break; case TpDouble: putArraydoubleV (rownr, static_cast*>(&dataPtr)); break; case TpComplex: putArrayComplexV (rownr, static_cast*>(&dataPtr)); break; case TpDComplex: putArrayDComplexV (rownr, static_cast*>(&dataPtr)); break; case TpString: putArrayStringV (rownr, static_cast*>(&dataPtr)); break; default: throw (DataManInvDT ("StManColumn::putArray")); } } //# Call the correct getColumnSliceX function depending on the data type. void StManColumn::getSliceV (rownr_t rownr, const Slicer& ns, ArrayBase& dataPtr) { switch (dtype()) { case TpBool: getSliceBoolV (rownr, ns, static_cast*>(&dataPtr)); break; case TpUChar: getSliceuCharV (rownr, ns, static_cast*>(&dataPtr)); break; case TpShort: getSliceShortV (rownr, ns, static_cast*>(&dataPtr)); break; case TpUShort: getSliceuShortV (rownr, ns, static_cast*>(&dataPtr)); break; case TpInt: getSliceIntV (rownr, ns, static_cast*>(&dataPtr)); break; case TpUInt: getSliceuIntV (rownr, ns, static_cast*>(&dataPtr)); break; case TpInt64: getSliceInt64V (rownr, ns, static_cast*>(&dataPtr)); break; case TpFloat: getSlicefloatV (rownr, ns, static_cast*>(&dataPtr)); break; case TpDouble: getSlicedoubleV (rownr, ns, static_cast*>(&dataPtr)); break; case TpComplex: getSliceComplexV (rownr, ns, static_cast*>(&dataPtr)); break; case TpDComplex: getSliceDComplexV (rownr, ns, static_cast*>(&dataPtr)); break; case TpString: getSliceStringV (rownr, ns, static_cast*>(&dataPtr)); break; default: throw (DataManInvDT ("StManColumn::getSlice")); } } //# Call the correct putSliceX function depending on the data type. void StManColumn::putSliceV (rownr_t rownr, const Slicer& ns, const ArrayBase& dataPtr) { switch (dtype()) { case TpBool: putSliceBoolV (rownr, ns, static_cast*>(&dataPtr)); break; case TpUChar: putSliceuCharV (rownr, ns, static_cast*>(&dataPtr)); break; case TpShort: putSliceShortV (rownr, ns, static_cast*>(&dataPtr)); break; case TpUShort: putSliceuShortV (rownr, ns, static_cast*>(&dataPtr)); break; case TpInt: putSliceIntV (rownr, ns, static_cast*>(&dataPtr)); break; case TpUInt: putSliceuIntV (rownr, ns, static_cast*>(&dataPtr)); break; case TpInt64: putSliceInt64V (rownr, ns, static_cast*>(&dataPtr)); break; case TpFloat: putSlicefloatV (rownr, ns, static_cast*>(&dataPtr)); break; case TpDouble: putSlicedoubleV (rownr, ns, static_cast*>(&dataPtr)); break; case TpComplex: putSliceComplexV (rownr, ns, static_cast*>(&dataPtr)); break; case TpDComplex: putSliceDComplexV (rownr, ns, static_cast*>(&dataPtr)); break; case TpString: putSliceStringV (rownr, ns, static_cast*>(&dataPtr)); break; default: throw (DataManInvDT ("StManColumn::putSlice")); } } //# Call the correct getArrayColumnX function depending on the data type. void StManColumn::getArrayColumnV (ArrayBase& dataPtr) { switch (dtype()) { case TpBool: getArrayColumnBoolV (static_cast*>(&dataPtr)); break; case TpUChar: getArrayColumnuCharV (static_cast*>(&dataPtr)); break; case TpShort: getArrayColumnShortV (static_cast*>(&dataPtr)); break; case TpUShort: getArrayColumnuShortV (static_cast*>(&dataPtr)); break; case TpInt: getArrayColumnIntV (static_cast*>(&dataPtr)); break; case TpUInt: getArrayColumnuIntV (static_cast*>(&dataPtr)); break; case TpInt64: getArrayColumnInt64V (static_cast*>(&dataPtr)); break; case TpFloat: getArrayColumnfloatV (static_cast*>(&dataPtr)); break; case TpDouble: getArrayColumndoubleV (static_cast*>(&dataPtr)); break; case TpComplex: getArrayColumnComplexV (static_cast*>(&dataPtr)); break; case TpDComplex: getArrayColumnDComplexV (static_cast*>(&dataPtr)); break; case TpString: getArrayColumnStringV (static_cast*>(&dataPtr)); break; default: throw (DataManInvDT ("StManColumn::getArrayColumn")); } } //# Call the correct putArrayColumnX function depending on the data type. void StManColumn::putArrayColumnV (const ArrayBase& dataPtr) { switch (dtype()) { case TpBool: putArrayColumnBoolV (static_cast*>(&dataPtr)); break; case TpUChar: putArrayColumnuCharV (static_cast*>(&dataPtr)); break; case TpShort: putArrayColumnShortV (static_cast*>(&dataPtr)); break; case TpUShort: putArrayColumnuShortV (static_cast*>(&dataPtr)); break; case TpInt: putArrayColumnIntV (static_cast*>(&dataPtr)); break; case TpUInt: putArrayColumnuIntV (static_cast*>(&dataPtr)); break; case TpInt64: putArrayColumnInt64V (static_cast*>(&dataPtr)); break; case TpFloat: putArrayColumnfloatV (static_cast*>(&dataPtr)); break; case TpDouble: putArrayColumndoubleV (static_cast*>(&dataPtr)); break; case TpComplex: putArrayColumnComplexV (static_cast*>(&dataPtr)); break; case TpDComplex: putArrayColumnDComplexV (static_cast*>(&dataPtr)); break; case TpString: putArrayColumnStringV (static_cast*>(&dataPtr)); break; default: throw (DataManInvDT ("StManColumn::putArrayColumn")); } } //# Call the correct getArrayColumnCellsX function depending on the data type. void StManColumn::getArrayColumnCellsV (const RefRows& rownrs, ArrayBase& dataPtr) { switch (dtype()) { case TpBool: getArrayColumnCellsBoolV (rownrs, static_cast*>(&dataPtr)); break; case TpUChar: getArrayColumnCellsuCharV (rownrs, static_cast*>(&dataPtr)); break; case TpShort: getArrayColumnCellsShortV (rownrs, static_cast*>(&dataPtr)); break; case TpUShort: getArrayColumnCellsuShortV (rownrs, static_cast*>(&dataPtr)); break; case TpInt: getArrayColumnCellsIntV (rownrs, static_cast*>(&dataPtr)); break; case TpUInt: getArrayColumnCellsuIntV (rownrs, static_cast*>(&dataPtr)); break; case TpInt64: getArrayColumnCellsInt64V (rownrs, static_cast*>(&dataPtr)); break; case TpFloat: getArrayColumnCellsfloatV (rownrs, static_cast*>(&dataPtr)); break; case TpDouble: getArrayColumnCellsdoubleV (rownrs, static_cast*>(&dataPtr)); break; case TpComplex: getArrayColumnCellsComplexV (rownrs, static_cast*>(&dataPtr)); break; case TpDComplex: getArrayColumnCellsDComplexV (rownrs, static_cast*>(&dataPtr)); break; case TpString: getArrayColumnCellsStringV (rownrs, static_cast*>(&dataPtr)); break; default: throw (DataManInvDT ("StManColumn::getArrayColumnCells")); } } //# Call the correct putArrayColumnCellsX function depending on the data type. void StManColumn::putArrayColumnCellsV (const RefRows& rownrs, const ArrayBase& dataPtr) { switch (dtype()) { case TpBool: putArrayColumnCellsBoolV (rownrs, static_cast*>(&dataPtr)); break; case TpUChar: putArrayColumnCellsuCharV (rownrs, static_cast*>(&dataPtr)); break; case TpShort: putArrayColumnCellsShortV (rownrs, static_cast*>(&dataPtr)); break; case TpUShort: putArrayColumnCellsuShortV (rownrs, static_cast*>(&dataPtr)); break; case TpInt: putArrayColumnCellsIntV (rownrs, static_cast*>(&dataPtr)); break; case TpUInt: putArrayColumnCellsuIntV (rownrs, static_cast*>(&dataPtr)); break; case TpInt64: putArrayColumnCellsInt64V (rownrs, static_cast*>(&dataPtr)); break; case TpFloat: putArrayColumnCellsfloatV (rownrs, static_cast*>(&dataPtr)); break; case TpDouble: putArrayColumnCellsdoubleV (rownrs, static_cast*>(&dataPtr)); break; case TpComplex: putArrayColumnCellsComplexV (rownrs, static_cast*>(&dataPtr)); break; case TpDComplex: putArrayColumnCellsDComplexV (rownrs, static_cast*>(&dataPtr)); break; case TpString: putArrayColumnCellsStringV (rownrs, static_cast*>(&dataPtr)); break; default: throw (DataManInvDT ("StManColumn::putArrayColumnCells")); } } //# Call the correct getColumnSliceX function depending on the data type. void StManColumn::getColumnSliceV (const Slicer& ns, ArrayBase& dataPtr) { switch (dtype()) { case TpBool: getColumnSliceBoolV (ns, static_cast*>(&dataPtr)); break; case TpUChar: getColumnSliceuCharV (ns, static_cast*>(&dataPtr)); break; case TpShort: getColumnSliceShortV (ns, static_cast*>(&dataPtr)); break; case TpUShort: getColumnSliceuShortV (ns, static_cast*>(&dataPtr)); break; case TpInt: getColumnSliceIntV (ns, static_cast*>(&dataPtr)); break; case TpUInt: getColumnSliceuIntV (ns, static_cast*>(&dataPtr)); break; case TpInt64: getColumnSliceInt64V (ns, static_cast*>(&dataPtr)); break; case TpFloat: getColumnSlicefloatV (ns, static_cast*>(&dataPtr)); break; case TpDouble: getColumnSlicedoubleV (ns, static_cast*>(&dataPtr)); break; case TpComplex: getColumnSliceComplexV (ns, static_cast*>(&dataPtr)); break; case TpDComplex: getColumnSliceDComplexV (ns, static_cast*>(&dataPtr)); break; case TpString: getColumnSliceStringV (ns, static_cast*>(&dataPtr)); break; default: throw (DataManInvDT ("StManColumn::getColumnSlice")); } } //# Call the correct putColumnSliceX function depending on the data type. void StManColumn::putColumnSliceV (const Slicer& ns, const ArrayBase& dataPtr) { switch (dtype()) { case TpBool: putColumnSliceBoolV (ns, static_cast*>(&dataPtr)); break; case TpUChar: putColumnSliceuCharV (ns, static_cast*>(&dataPtr)); break; case TpShort: putColumnSliceShortV (ns, static_cast*>(&dataPtr)); break; case TpUShort: putColumnSliceuShortV (ns, static_cast*>(&dataPtr)); break; case TpInt: putColumnSliceIntV (ns, static_cast*>(&dataPtr)); break; case TpUInt: putColumnSliceuIntV (ns, static_cast*>(&dataPtr)); break; case TpInt64: putColumnSliceInt64V (ns, static_cast*>(&dataPtr)); break; case TpFloat: putColumnSlicefloatV (ns, static_cast*>(&dataPtr)); break; case TpDouble: putColumnSlicedoubleV (ns, static_cast*>(&dataPtr)); break; case TpComplex: putColumnSliceComplexV (ns, static_cast*>(&dataPtr)); break; case TpDComplex: putColumnSliceDComplexV (ns, static_cast*>(&dataPtr)); break; case TpString: putColumnSliceStringV (ns, static_cast*>(&dataPtr)); break; default: throw (DataManInvDT ("StManColumn::putColumnSlice")); } } //# Call the correct getColumnSliceCellsX function depending on the data type. void StManColumn::getColumnSliceCellsV (const RefRows& rownrs, const Slicer& ns, ArrayBase& dataPtr) { switch (dtype()) { case TpBool: getColumnSliceCellsBoolV (rownrs, ns, static_cast*>(&dataPtr)); break; case TpUChar: getColumnSliceCellsuCharV (rownrs, ns, static_cast*>(&dataPtr)); break; case TpShort: getColumnSliceCellsShortV (rownrs, ns, static_cast*>(&dataPtr)); break; case TpUShort: getColumnSliceCellsuShortV (rownrs, ns, static_cast*>(&dataPtr)); break; case TpInt: getColumnSliceCellsIntV (rownrs, ns, static_cast*>(&dataPtr)); break; case TpUInt: getColumnSliceCellsuIntV (rownrs, ns, static_cast*>(&dataPtr)); break; case TpInt64: getColumnSliceCellsInt64V (rownrs, ns, static_cast*>(&dataPtr)); break; case TpFloat: getColumnSliceCellsfloatV (rownrs, ns, static_cast*>(&dataPtr)); break; case TpDouble: getColumnSliceCellsdoubleV (rownrs, ns, static_cast*>(&dataPtr)); break; case TpComplex: getColumnSliceCellsComplexV (rownrs, ns, static_cast*>(&dataPtr)); break; case TpDComplex: getColumnSliceCellsDComplexV (rownrs, ns, static_cast*>(&dataPtr)); break; case TpString: getColumnSliceCellsStringV (rownrs, ns, static_cast*>(&dataPtr)); break; default: throw (DataManInvDT ("StManColumn::getColumnSliceCells")); } } //# Call the correct putColumnSliceCellsX function depending on the data type. void StManColumn::putColumnSliceCellsV (const RefRows& rownrs, const Slicer& ns, const ArrayBase& dataPtr) { switch (dtype()) { case TpBool: putColumnSliceCellsBoolV (rownrs, ns, static_cast*>(&dataPtr)); break; case TpUChar: putColumnSliceCellsuCharV (rownrs, ns, static_cast*>(&dataPtr)); break; case TpShort: putColumnSliceCellsShortV (rownrs, ns, static_cast*>(&dataPtr)); break; case TpUShort: putColumnSliceCellsuShortV (rownrs, ns, static_cast*>(&dataPtr)); break; case TpInt: putColumnSliceCellsIntV (rownrs, ns, static_cast*>(&dataPtr)); break; case TpUInt: putColumnSliceCellsuIntV (rownrs, ns, static_cast*>(&dataPtr)); break; case TpInt64: putColumnSliceCellsInt64V (rownrs, ns, static_cast*>(&dataPtr)); break; case TpFloat: putColumnSliceCellsfloatV (rownrs, ns, static_cast*>(&dataPtr)); break; case TpDouble: putColumnSliceCellsdoubleV (rownrs, ns, static_cast*>(&dataPtr)); break; case TpComplex: putColumnSliceCellsComplexV (rownrs, ns, static_cast*>(&dataPtr)); break; case TpDComplex: putColumnSliceCellsDComplexV (rownrs, ns, static_cast*>(&dataPtr)); break; case TpString: putColumnSliceCellsStringV (rownrs, ns, static_cast*>(&dataPtr)); break; default: throw (DataManInvDT ("StManColumn::putColumnSliceCells")); } } void StManColumn::throwInvalidOp(const String &op) const { throw (DataManInvOper ("StManColumn::" + op + " not possible" " for column " + columnName())); } //# The default implementations use the DataManagerColumn counterparts. #define STMANCOLUMN_GETPUT(T,NM) \ void StManColumn::aips_name2(getScalarColumn,NM) (Vector* dataPtr) \ { getScalarColumnBase (*dataPtr); } \ void StManColumn::aips_name2(putScalarColumn,NM) (const Vector* dataPtr) \ { putScalarColumnBase (*dataPtr); } \ void StManColumn::aips_name2(getArray,NM) (uInt, Array*) \ { throwInvalidOp("getArray" #NM); } \ void StManColumn::aips_name2(putArray,NM) (uInt, const Array*) \ { throwInvalidOp("putArray" #NM); } \ void StManColumn::aips_name2(getSlice,NM) (uInt rownr, const Slicer& slicer, \ Array* arr) \ { getSliceBase (rownr, slicer, *arr); } \ void StManColumn::aips_name2(putSlice,NM) (uInt rownr, const Slicer& slicer, \ const Array* arr) \ { putSliceBase (rownr, slicer, *arr); } \ void StManColumn::aips_name2(getArrayColumn,NM) (Array* arr) \ { getArrayColumnBase (*arr); } \ void StManColumn::aips_name2(putArrayColumn,NM) (const Array* arr) \ { putArrayColumnBase (*arr); } \ void StManColumn::aips_name2(getColumnSlice,NM) (const Slicer& slicer, \ Array* arr) \ { getColumnSliceBase (slicer, *arr); } \ void StManColumn::aips_name2(putColumnSlice,NM) (const Slicer& slicer, \ const Array* arr) \ { putColumnSliceBase (slicer, *arr); } \ void StManColumn::aips_name2(getScalarColumnCells,NM) \ (const RefRows& rownrs, \ Vector* values) \ { getScalarColumnCellsBase (rownrs, *values); } \ void StManColumn::aips_name2(putScalarColumnCells,NM) \ (const RefRows& rownrs, \ const Vector* values) \ { putScalarColumnCellsBase (rownrs, *values); } \ void StManColumn::aips_name2(getArrayColumnCells,NM) \ (const RefRows& rownrs, \ Array* values) \ { getArrayColumnCellsBase (rownrs, *values); } \ void StManColumn::aips_name2(putArrayColumnCells,NM) \ (const RefRows& rownrs, \ const Array* values) \ { putArrayColumnCellsBase (rownrs, *values); } \ void StManColumn::aips_name2(getColumnSliceCells,NM) \ (const RefRows& rownrs, \ const Slicer& ns, \ Array* values) \ { getColumnSliceCellsBase (rownrs, ns, *values); } \ void StManColumn::aips_name2(putColumnSliceCells,NM) \ (const RefRows& rownrs, \ const Slicer& ns, \ const Array* values) \ { putColumnSliceCellsBase (rownrs, ns, *values); } \ STMANCOLUMN_GETPUT(Bool,BoolV) STMANCOLUMN_GETPUT(uChar,uCharV) STMANCOLUMN_GETPUT(Short,ShortV) STMANCOLUMN_GETPUT(uShort,uShortV) STMANCOLUMN_GETPUT(Int,IntV) STMANCOLUMN_GETPUT(uInt,uIntV) STMANCOLUMN_GETPUT(Int64,Int64V) STMANCOLUMN_GETPUT(float,floatV) STMANCOLUMN_GETPUT(double,doubleV) STMANCOLUMN_GETPUT(Complex,ComplexV) STMANCOLUMN_GETPUT(DComplex,DComplexV) STMANCOLUMN_GETPUT(String,StringV) /* Vector value = *values; \ const ColumnCache* cachePtr = columnCachePtr(); \ uInt nr = rownrs.nelements(); \ Timer timer; \ for (uInt i=0; ioffset(rownr); \ if (off >= 0) { \ value(i) = ((T*)(cachePtr->dataPtr()))[off]; \ } else { \ aips_name2(get,NM) (rownr, &(value(i))); \ } \ } \ timer.show("a"); \ */ } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/StManColumn.h000066400000000000000000001127631476623553700204660ustar00rootroot00000000000000//# StManColumn.h: Base storage manager column class //# Copyright (C) 1994,1995,1996,1998,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_STMANCOLUMN_H #define TABLES_STMANCOLUMN_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Base table column storage manager class // // // // // //# Classes you should understand before using this one. //
      • DataManagerColumn // // // StManColumn handles a column for a storage manager. // // // StManColumn is the old storage manager base class which has been replaced // by StManColumnBase. // However, the class still exists for backward compatibility for external storage // managers (such as LofarStMan) that do not derive from StManColumnBase yet. // It also maps the new get/put functions taking a rownr_t to the old // functions taking a uInt rownr. // // // Provide backward compatibility for external storage managers. // // //# A List of bugs, limitations, extensions or planned refinements. // class StManColumn : public StManColumnBase { public: // Default constructor. StManColumn (int dataType) : StManColumnBase (dataType) {} virtual ~StManColumn(); // The object cannot be copied. StManColumn (const StManColumn&) = delete; // The object cannot be assigned to. StManColumn& operator= (const StManColumn&) = delete; // Set the shape of an (variable-shaped) array in the given row. // By default it throws a "not possible" exception. virtual void setShape (rownr_t rownr, const IPosition& shape); virtual void setShape (uInt rownr, const IPosition& shape); // Set the shape and tile shape of an (variable-shaped) array // in the given row. // By default it ignores the tile shape (thus only sets the shape). virtual void setShapeTiled (rownr_t rownr, const IPosition& shape, const IPosition& tileShape); virtual void setShapeTiled (uInt rownr, const IPosition& shape, const IPosition& tileShape); // Is the value shape defined in the given row? // By default it returns True. virtual Bool isShapeDefined (rownr_t rownr); virtual Bool isShapeDefined (uInt rownr); // Get the dimensionality of the item in the given row. // By default it returns shape(rownr).nelements(). virtual uInt ndim (rownr_t rownr); virtual uInt ndim (uInt rownr); // Get the shape of the item in the given row. // By default it returns a zero-length IPosition (for a scalar value). virtual IPosition shape (rownr_t rownr); virtual IPosition shape (uInt rownr); // Get the tile shape of the item in the given row. // By default it returns a zero-length IPosition. virtual IPosition tileShape (rownr_t rownr); virtual IPosition tileShape (uInt rownr); // Get the scalar value in the given row. // virtual void getBool (rownr_t rownr, Bool* dataPtr); virtual void getuChar (rownr_t rownr, uChar* dataPtr); virtual void getShort (rownr_t rownr, Short* dataPtr); virtual void getuShort (rownr_t rownr, uShort* dataPtr); virtual void getInt (rownr_t rownr, Int* dataPtr); virtual void getuInt (rownr_t rownr, uInt* dataPtr); virtual void getfloat (rownr_t rownr, float* dataPtr); virtual void getdouble (rownr_t rownr, double* dataPtr); virtual void getComplex (rownr_t rownr, Complex* dataPtr); virtual void getDComplex (rownr_t rownr, DComplex* dataPtr); virtual void getString (rownr_t rownr, String* dataPtr); // // Put the scalar value in the given row. // virtual void putBool (rownr_t rownr, const Bool* dataPtr); virtual void putuChar (rownr_t rownr, const uChar* dataPtr); virtual void putShort (rownr_t rownr, const Short* dataPtr); virtual void putuShort (rownr_t rownr, const uShort* dataPtr); virtual void putInt (rownr_t rownr, const Int* dataPtr); virtual void putuInt (rownr_t rownr, const uInt* dataPtr); virtual void putfloat (rownr_t rownr, const float* dataPtr); virtual void putdouble (rownr_t rownr, const double* dataPtr); virtual void putComplex (rownr_t rownr, const Complex* dataPtr); virtual void putDComplex (rownr_t rownr, const DComplex* dataPtr); virtual void putString (rownr_t rownr, const String* dataPtr); // // Get all scalar values in the column. // The argument dataPtr is in fact a Vector&, but an ArrayBase& // is needed to be generic. // The vector pointed to by dataPtr has to have the correct length // (which is guaranteed by the ScalarColumn getColumn function). // The default implementation calls the appropriate getScalarColumnXXV. virtual void getScalarColumnV (ArrayBase& dataPtr); // Put all scalar values in the column. // The argument dataPtr is in fact a const Vector&, but a const ArrayBase& // is needed to be generic. // The vector pointed to by dataPtr has to have the correct length // (which is guaranteed by the ScalarColumn putColumn function). // The default implementation calls the appropriate putScalarColumnXXV. virtual void putScalarColumnV (const ArrayBase& dataPtr); // Get some scalar values in the column. // The argument dataPtr is in fact a Vector&, but an ArrayBase& // is needed to be generic. // The vector pointed to by dataPtr has to have the correct length // (which is guaranteed by the ScalarColumn getColumn function). // The default implementation calls the appropriate getScalarColumnCellsXXV. virtual void getScalarColumnCellsV (const RefRows& rownrs, ArrayBase& dataPtr); // Put some scalar values in the column. // The argument dataPtr is in fact a const Vector&, but a const ArrayBase& // is needed to be generic. // The vector pointed to by dataPtr has to have the correct length // (which is guaranteed by the ScalarColumn getColumn function). // The default implementation calls the appropriate putScalarColumnCellsXXV. virtual void putScalarColumnCellsV (const RefRows& rownrs, const ArrayBase& dataPtr); // Get the array value in the given row. // The argument dataPtr is in fact an Array&, but a ArrayBase& // is needed to be generic. // The array pointed to by dataPtr has to have the correct shape // (which is guaranteed by the ArrayColumn get function). // The default implementation calls the appropriate getArrayXXV. virtual void getArrayV (rownr_t rownr, ArrayBase& dataPtr); // Put the array value into the given row. // The argument dataPtr is in fact a const Array&, but a const ArrayBase& // is needed to be generic. // The array pointed to by dataPtr has to have the correct shape // (which is guaranteed by the ArrayColumn put function). // The default implementation calls the appropriate putArrayXXV. virtual void putArrayV (rownr_t rownr, const ArrayBase& dataPtr); // Get all array values in the column. // The argument dataPtr is in fact an Array&, but a ArrayBase& // is needed to be generic. // The vector pointed to by dataPtr has to have the correct length // (which is guaranteed by the ArrayColumn getColumn function). // The default implementation calls the appropriate getArrayColumnXXV. virtual void getArrayColumnV (ArrayBase& dataPtr); // Put all array values in the column. // The argument dataPtr is in fact a const Array&, but a const ArrayBase& // is needed to be generic. // The vector pointed to by dataPtr has to have the correct length // (which is guaranteed by the ArrayColumn putColumn function). // The default implementation calls the appropriate putArrayColumnXXV. virtual void putArrayColumnV (const ArrayBase& dataPtr); // Get some array values in the column. // The argument dataPtr is in fact an Array&, but a ArrayBase& // is needed to be generic. // The vector pointed to by dataPtr has to have the correct length // (which is guaranteed by the ArrayColumn getColumn function). // The default implementation calls the appropriate getArrayColumnCellsXXV. virtual void getArrayColumnCellsV (const RefRows& rownrs, ArrayBase& dataPtr); // Put some array values in the column. // The argument dataPtr is in fact an const Array&, but a const ArrayBase& // is needed to be generic. // The vector pointed to by dataPtr has to have the correct length // (which is guaranteed by the ArrayColumn getColumn function). // The default implementation calls the appropriate putArrayColumnCellsXXV. virtual void putArrayColumnCellsV (const RefRows& rownrs, const ArrayBase& dataPtr); // Get a section of the array in the given row. // The argument dataPtr is in fact an Array&, but a ArrayBase& // is needed to be generic. // The array pointed to by dataPtr has to have the correct shape // (which is guaranteed by the ArrayColumn getSlice function). // The default implementation calls the appropriate getSliceXXV. virtual void getSliceV (rownr_t rownr, const Slicer& slicer, ArrayBase& dataPtr); // Put into a section of the array in the given row. // The argument dataPtr is in fact a const Array&, but a const ArrayBase& // is needed to be generic. // The array pointed to by dataPtr has to have the correct shape // (which is guaranteed by the ArrayColumn putSlice function). // The default implementation calls the appropriate putSliceXXV. virtual void putSliceV (rownr_t rownr, const Slicer& slicer, const ArrayBase& dataPtr); // Get a section of all arrays in the column. // The argument dataPtr is in fact an Array&, but a ArrayBase& // is needed to be generic. // The array pointed to by dataPtr has to have the correct shape // (which is guaranteed by the ArrayColumn getColumn function). // The default implementation calls the appropriate getColumnSliceXXV. virtual void getColumnSliceV (const Slicer& slicer, ArrayBase& dataPtr); // Put into a section of all arrays in the column. // The argument dataPtr is in fact a const Array&, but a const ArrayBase& // is needed to be generic. // The array pointed to by dataPtr has to have the correct shape // (which is guaranteed by the ArrayColumn putColumn function). // The default implementation calls the appropriate putColumnSliceXXV. virtual void putColumnSliceV (const Slicer& slicer, const ArrayBase& dataPtr); // Get a section of some arrays in the column. // The argument dataPtr is in fact an Array&, but a ArrayBase& // is needed to be generic. // The array pointed to by dataPtr has to have the correct shape // (which is guaranteed by the ArrayColumn getColumn function). // The default implementation calls the appropriate getColumnSliceCellsXXV. virtual void getColumnSliceCellsV (const RefRows& rownrs, const Slicer& slicer, ArrayBase& dataPtr); // Put into a section of some arrays in the column. // The argument dataPtr is in fact a const Array&, but a const ArrayBase& // is needed to be generic. // The array pointed to by dataPtr has to have the correct shape // (which is guaranteed by the ArrayColumn putColumn function). // The default implementation calls the appropriate putColumnSliceCellsXXV. virtual void putColumnSliceCellsV (const RefRows& rownrs, const Slicer& slicer, const ArrayBase& dataPtr); private: // Throw an "invalid operation" exception for the default // implementation of getArray. void throwInvalidOp(const String &op) const; protected: // Get the scalar value in the given row. // virtual void getBoolV (uInt rownr, Bool* dataPtr); virtual void getuCharV (uInt rownr, uChar* dataPtr); virtual void getShortV (uInt rownr, Short* dataPtr); virtual void getuShortV (uInt rownr, uShort* dataPtr); virtual void getIntV (uInt rownr, Int* dataPtr); virtual void getuIntV (uInt rownr, uInt* dataPtr); virtual void getfloatV (uInt rownr, float* dataPtr); virtual void getdoubleV (uInt rownr, double* dataPtr); virtual void getComplexV (uInt rownr, Complex* dataPtr); virtual void getDComplexV (uInt rownr, DComplex* dataPtr); virtual void getStringV (uInt rownr, String* dataPtr); // // Put the scalar value in the given row. // virtual void putBoolV (uInt rownr, const Bool* dataPtr); virtual void putuCharV (uInt rownr, const uChar* dataPtr); virtual void putShortV (uInt rownr, const Short* dataPtr); virtual void putuShortV (uInt rownr, const uShort* dataPtr); virtual void putIntV (uInt rownr, const Int* dataPtr); virtual void putuIntV (uInt rownr, const uInt* dataPtr); virtual void putfloatV (uInt rownr, const float* dataPtr); virtual void putdoubleV (uInt rownr, const double* dataPtr); virtual void putComplexV (uInt rownr, const Complex* dataPtr); virtual void putDComplexV (uInt rownr, const DComplex* dataPtr); virtual void putStringV (uInt rownr, const String* dataPtr); // // Get the scalar values in the entire column. // The buffer pointed to by dataPtr has to have the correct length. // (which is guaranteed by the ScalarColumn getColumn function). // The default implementations calls DataManagerColumn::getScalarColumnBase. // virtual void getScalarColumnBoolV (Vector* dataPtr); virtual void getScalarColumnuCharV (Vector* dataPtr); virtual void getScalarColumnShortV (Vector* dataPtr); virtual void getScalarColumnuShortV (Vector* dataPtr); virtual void getScalarColumnIntV (Vector* dataPtr); virtual void getScalarColumnuIntV (Vector* dataPtr); virtual void getScalarColumnInt64V (Vector* dataPtr); virtual void getScalarColumnfloatV (Vector* dataPtr); virtual void getScalarColumndoubleV (Vector* dataPtr); virtual void getScalarColumnComplexV (Vector* dataPtr); virtual void getScalarColumnDComplexV (Vector* dataPtr); virtual void getScalarColumnStringV (Vector* dataPtr); // // Put the scalar values into the entire column. // The buffer pointed to by dataPtr has to have the correct length. // (which is guaranteed by the ScalarColumn putColumn function). // The default implementations calls DataManagerColumn::putScalarColumnBase. // virtual void putScalarColumnBoolV (const Vector* dataPtr); virtual void putScalarColumnuCharV (const Vector* dataPtr); virtual void putScalarColumnShortV (const Vector* dataPtr); virtual void putScalarColumnuShortV (const Vector* dataPtr); virtual void putScalarColumnIntV (const Vector* dataPtr); virtual void putScalarColumnuIntV (const Vector* dataPtr); virtual void putScalarColumnInt64V (const Vector* dataPtr); virtual void putScalarColumnfloatV (const Vector* dataPtr); virtual void putScalarColumndoubleV (const Vector* dataPtr); virtual void putScalarColumnComplexV (const Vector* dataPtr); virtual void putScalarColumnDComplexV (const Vector* dataPtr); virtual void putScalarColumnStringV (const Vector* dataPtr); // // Get the scalar values in some cells of the column. // The buffer pointed to by dataPtr has to have the correct length. // (which is guaranteed by the ScalarColumn getColumnCells function). // The default implementations call DataManagerColumn::getScalarColumnCellsBase. // virtual void getScalarColumnCellsBoolV (const RefRows& rownrs, Vector* dataPtr); virtual void getScalarColumnCellsuCharV (const RefRows& rownrs, Vector* dataPtr); virtual void getScalarColumnCellsShortV (const RefRows& rownrs, Vector* dataPtr); virtual void getScalarColumnCellsuShortV (const RefRows& rownrs, Vector* dataPtr); virtual void getScalarColumnCellsIntV (const RefRows& rownrs, Vector* dataPtr); virtual void getScalarColumnCellsuIntV (const RefRows& rownrs, Vector* dataPtr); virtual void getScalarColumnCellsInt64V (const RefRows& rownrs, Vector* dataPtr); virtual void getScalarColumnCellsfloatV (const RefRows& rownrs, Vector* dataPtr); virtual void getScalarColumnCellsdoubleV (const RefRows& rownrs, Vector* dataPtr); virtual void getScalarColumnCellsComplexV (const RefRows& rownrs, Vector* dataPtr); virtual void getScalarColumnCellsDComplexV (const RefRows& rownrs, Vector* dataPtr); virtual void getScalarColumnCellsStringV (const RefRows& rownrs, Vector* dataPtr); // // Put the scalar values into some cells of the column. // The buffer pointed to by dataPtr has to have the correct length. // (which is guaranteed by the ScalarColumn putColumnCells function). // The default implementations call DataManagerColumn::putScalarColumnCellsBase. // virtual void putScalarColumnCellsBoolV (const RefRows& rownrs, const Vector* dataPtr); virtual void putScalarColumnCellsuCharV (const RefRows& rownrs, const Vector* dataPtr); virtual void putScalarColumnCellsShortV (const RefRows& rownrs, const Vector* dataPtr); virtual void putScalarColumnCellsuShortV (const RefRows& rownrs, const Vector* dataPtr); virtual void putScalarColumnCellsIntV (const RefRows& rownrs, const Vector* dataPtr); virtual void putScalarColumnCellsuIntV (const RefRows& rownrs, const Vector* dataPtr); virtual void putScalarColumnCellsInt64V (const RefRows& rownrs, const Vector* dataPtr); virtual void putScalarColumnCellsfloatV (const RefRows& rownrs, const Vector* dataPtr); virtual void putScalarColumnCellsdoubleV (const RefRows& rownrs, const Vector* dataPtr); virtual void putScalarColumnCellsComplexV (const RefRows& rownrs, const Vector* dataPtr); virtual void putScalarColumnCellsDComplexV (const RefRows& rownrs, const Vector* dataPtr); virtual void putScalarColumnCellsStringV (const RefRows& rownrs, const Vector* dataPtr); // // Get the array value in the given row. // The array pointed to by dataPtr has to have the correct length // (which is guaranteed by the ArrayColumn get function). // The default implementations throw an exception. // virtual void getArrayBoolV (uInt rownr, Array* dataPtr); virtual void getArrayuCharV (uInt rownr, Array* dataPtr); virtual void getArrayShortV (uInt rownr, Array* dataPtr); virtual void getArrayuShortV (uInt rownr, Array* dataPtr); virtual void getArrayIntV (uInt rownr, Array* dataPtr); virtual void getArrayuIntV (uInt rownr, Array* dataPtr); virtual void getArrayInt64V (uInt rownr, Array* dataPtr); virtual void getArrayfloatV (uInt rownr, Array* dataPtr); virtual void getArraydoubleV (uInt rownr, Array* dataPtr); virtual void getArrayComplexV (uInt rownr, Array* dataPtr); virtual void getArrayDComplexV (uInt rownr, Array* dataPtr); virtual void getArrayStringV (uInt rownr, Array* dataPtr); // // Put the array value into the given row. // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the ArrayColumn put function). // The default implementations throw an exception. // virtual void putArrayBoolV (uInt rownr, const Array* dataPtr); virtual void putArrayuCharV (uInt rownr, const Array* dataPtr); virtual void putArrayShortV (uInt rownr, const Array* dataPtr); virtual void putArrayuShortV (uInt rownr, const Array* dataPtr); virtual void putArrayIntV (uInt rownr, const Array* dataPtr); virtual void putArrayuIntV (uInt rownr, const Array* dataPtr); virtual void putArrayInt64V (uInt rownr, const Array* dataPtr); virtual void putArrayfloatV (uInt rownr, const Array* dataPtr); virtual void putArraydoubleV (uInt rownr, const Array* dataPtr); virtual void putArrayComplexV (uInt rownr, const Array* dataPtr); virtual void putArrayDComplexV (uInt rownr, const Array* dataPtr); virtual void putArrayStringV (uInt rownr, const Array* dataPtr); // // Get the array values in the entire column. // The buffer pointed to by dataPtr has to have the correct length. // (which is guaranteed by the ArrayColumn getColumn function). // The default implementations call DataManagerColumn::getArrayColumnBase. // virtual void getArrayColumnBoolV (Array* dataPtr); virtual void getArrayColumnuCharV (Array* dataPtr); virtual void getArrayColumnShortV (Array* dataPtr); virtual void getArrayColumnuShortV (Array* dataPtr); virtual void getArrayColumnIntV (Array* dataPtr); virtual void getArrayColumnuIntV (Array* dataPtr); virtual void getArrayColumnInt64V (Array* dataPtr); virtual void getArrayColumnfloatV (Array* dataPtr); virtual void getArrayColumndoubleV (Array* dataPtr); virtual void getArrayColumnComplexV (Array* dataPtr); virtual void getArrayColumnDComplexV (Array* dataPtr); virtual void getArrayColumnStringV (Array* dataPtr); // // Put the array values into the entire column. // The buffer pointed to by dataPtr has to have the correct length. // (which is guaranteed by the ArrayColumn putColumn function). // The default implementations call DataManagerColumn::putArrayColumnBase. // virtual void putArrayColumnBoolV (const Array* dataPtr); virtual void putArrayColumnuCharV (const Array* dataPtr); virtual void putArrayColumnShortV (const Array* dataPtr); virtual void putArrayColumnuShortV (const Array* dataPtr); virtual void putArrayColumnIntV (const Array* dataPtr); virtual void putArrayColumnuIntV (const Array* dataPtr); virtual void putArrayColumnInt64V (const Array* dataPtr); virtual void putArrayColumnfloatV (const Array* dataPtr); virtual void putArrayColumndoubleV (const Array* dataPtr); virtual void putArrayColumnComplexV (const Array* dataPtr); virtual void putArrayColumnDComplexV (const Array* dataPtr); virtual void putArrayColumnStringV (const Array* dataPtr); // // Get the array values in some cells of the column. // The buffer pointed to by dataPtr has to have the correct length. // (which is guaranteed by the ArrayColumn getColumnCells function). // The default implementations call DataManagerColumn::getArrayColumnCellsBase. // virtual void getArrayColumnCellsBoolV (const RefRows& rownrs, Array* dataPtr); virtual void getArrayColumnCellsuCharV (const RefRows& rownrs, Array* dataPtr); virtual void getArrayColumnCellsShortV (const RefRows& rownrs, Array* dataPtr); virtual void getArrayColumnCellsuShortV (const RefRows& rownrs, Array* dataPtr); virtual void getArrayColumnCellsIntV (const RefRows& rownrs, Array* dataPtr); virtual void getArrayColumnCellsuIntV (const RefRows& rownrs, Array* dataPtr); virtual void getArrayColumnCellsInt64V (const RefRows& rownrs, Array* dataPtr); virtual void getArrayColumnCellsfloatV (const RefRows& rownrs, Array* dataPtr); virtual void getArrayColumnCellsdoubleV (const RefRows& rownrs, Array* dataPtr); virtual void getArrayColumnCellsComplexV (const RefRows& rownrs, Array* dataPtr); virtual void getArrayColumnCellsDComplexV (const RefRows& rownrs, Array* dataPtr); virtual void getArrayColumnCellsStringV (const RefRows& rownrs, Array* dataPtr); // // Put the array values into some cells of the column. // The buffer pointed to by dataPtr has to have the correct length. // (which is guaranteed by the ArrayColumn putColumnCells function). // The default implementations call DataManagerColumn::putArrayColumnCellsBase. // virtual void putArrayColumnCellsBoolV (const RefRows& rownrs, const Array* dataPtr); virtual void putArrayColumnCellsuCharV (const RefRows& rownrs, const Array* dataPtr); virtual void putArrayColumnCellsShortV (const RefRows& rownrs, const Array* dataPtr); virtual void putArrayColumnCellsuShortV (const RefRows& rownrs, const Array* dataPtr); virtual void putArrayColumnCellsIntV (const RefRows& rownrs, const Array* dataPtr); virtual void putArrayColumnCellsuIntV (const RefRows& rownrs, const Array* dataPtr); virtual void putArrayColumnCellsInt64V (const RefRows& rownrs, const Array* dataPtr); virtual void putArrayColumnCellsfloatV (const RefRows& rownrs, const Array* dataPtr); virtual void putArrayColumnCellsdoubleV (const RefRows& rownrs, const Array* dataPtr); virtual void putArrayColumnCellsComplexV (const RefRows& rownrs, const Array* dataPtr); virtual void putArrayColumnCellsDComplexV (const RefRows& rownrs, const Array* dataPtr); virtual void putArrayColumnCellsStringV (const RefRows& rownrs, const Array* dataPtr); // // Get the array value in the given row. // The array pointed to by dataPtr has to have the correct length // (which is guaranteed by the ArrayColumn getSlice function). // The default implementations call DataManagerColumn::getSliceBase. // virtual void getSliceBoolV (uInt rownr, const Slicer& ns, Array* dataPtr); virtual void getSliceuCharV (uInt rownr, const Slicer& ns, Array* dataPtr); virtual void getSliceShortV (uInt rownr, const Slicer& ns, Array* dataPtr); virtual void getSliceuShortV (uInt rownr, const Slicer& ns, Array* dataPtr); virtual void getSliceIntV (uInt rownr, const Slicer& ns, Array* dataPtr); virtual void getSliceuIntV (uInt rownr, const Slicer& ns, Array* dataPtr); virtual void getSliceInt64V (uInt rownr, const Slicer& ns, Array* dataPtr); virtual void getSlicefloatV (uInt rownr, const Slicer& ns, Array* dataPtr); virtual void getSlicedoubleV (uInt rownr, const Slicer& ns, Array* dataPtr); virtual void getSliceComplexV (uInt rownr, const Slicer& ns, Array* dataPtr); virtual void getSliceDComplexV (uInt rownr, const Slicer& ns, Array* dataPtr); virtual void getSliceStringV (uInt rownr, const Slicer& ns, Array* dataPtr); // // Put the array value into the given row. // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the ArrayColumn putSlice function). // The default implementations call DataManagerColumn::putSliceBase. // virtual void putSliceBoolV (uInt rownr, const Slicer& ns, const Array* dataPtr); virtual void putSliceuCharV (uInt rownr, const Slicer& ns, const Array* dataPtr); virtual void putSliceShortV (uInt rownr, const Slicer& ns, const Array* dataPtr); virtual void putSliceuShortV (uInt rownr, const Slicer& ns, const Array* dataPtr); virtual void putSliceIntV (uInt rownr, const Slicer& ns, const Array* dataPtr); virtual void putSliceuIntV (uInt rownr, const Slicer& ns, const Array* dataPtr); virtual void putSliceInt64V (uInt rownr, const Slicer& ns, const Array* dataPtr); virtual void putSlicefloatV (uInt rownr, const Slicer& ns, const Array* dataPtr); virtual void putSlicedoubleV (uInt rownr, const Slicer& ns, const Array* dataPtr); virtual void putSliceComplexV (uInt rownr, const Slicer& ns, const Array* dataPtr); virtual void putSliceDComplexV (uInt rownr, const Slicer& ns, const Array* dataPtr); virtual void putSliceStringV (uInt rownr, const Slicer& ns, const Array* dataPtr); // // Get the array values in the entire column. // The buffer pointed to by dataPtr has to have the correct length. // (which is guaranteed by the ArrayColumn getColumn function). // The default implementations call DataManagerColumn::getColumnSliceBase. // virtual void getColumnSliceBoolV (const Slicer& ns, Array* dataPtr); virtual void getColumnSliceuCharV (const Slicer& ns, Array* dataPtr); virtual void getColumnSliceShortV (const Slicer& ns, Array* dataPtr); virtual void getColumnSliceuShortV (const Slicer& ns, Array* dataPtr); virtual void getColumnSliceIntV (const Slicer& ns, Array* dataPtr); virtual void getColumnSliceuIntV (const Slicer& ns, Array* dataPtr); virtual void getColumnSliceInt64V (const Slicer& ns, Array* dataPtr); virtual void getColumnSlicefloatV (const Slicer& ns, Array* dataPtr); virtual void getColumnSlicedoubleV (const Slicer& ns, Array* dataPtr); virtual void getColumnSliceComplexV (const Slicer& ns, Array* dataPtr); virtual void getColumnSliceDComplexV (const Slicer& ns, Array* dataPtr); virtual void getColumnSliceStringV (const Slicer& ns, Array* dataPtr); // // Put the array values into the entire column. // The buffer pointed to by dataPtr has to have the correct length. // (which is guaranteed by the ArrayColumn putColumn function). // The default implementations call DataManagerColumn::putColumnSliceBase. // virtual void putColumnSliceBoolV (const Slicer& ns, const Array* dataPtr); virtual void putColumnSliceuCharV (const Slicer& ns, const Array* dataPtr); virtual void putColumnSliceShortV (const Slicer& ns, const Array* dataPtr); virtual void putColumnSliceuShortV (const Slicer& ns, const Array* dataPtr); virtual void putColumnSliceIntV (const Slicer& ns, const Array* dataPtr); virtual void putColumnSliceuIntV (const Slicer& ns, const Array* dataPtr); virtual void putColumnSliceInt64V (const Slicer& ns, const Array* dataPtr); virtual void putColumnSlicefloatV (const Slicer& ns, const Array* dataPtr); virtual void putColumnSlicedoubleV (const Slicer& ns, const Array* dataPtr); virtual void putColumnSliceComplexV (const Slicer& ns, const Array* dataPtr); virtual void putColumnSliceDComplexV (const Slicer& ns, const Array* dataPtr); virtual void putColumnSliceStringV (const Slicer& ns, const Array* dataPtr); // // Get the array values in some cells of the column. // The buffer pointed to by dataPtr has to have the correct length. // (which is guaranteed by the ArrayColumn getColumnCells function). // The default implementations call DataManagerColumn::getColumnSliceCellsBase. // virtual void getColumnSliceCellsBoolV (const RefRows& rownrs, const Slicer& ns, Array* dataPtr); virtual void getColumnSliceCellsuCharV (const RefRows& rownrs, const Slicer& ns, Array* dataPtr); virtual void getColumnSliceCellsShortV (const RefRows& rownrs, const Slicer& ns, Array* dataPtr); virtual void getColumnSliceCellsuShortV (const RefRows& rownrs, const Slicer& ns, Array* dataPtr); virtual void getColumnSliceCellsIntV (const RefRows& rownrs, const Slicer& ns, Array* dataPtr); virtual void getColumnSliceCellsuIntV (const RefRows& rownrs, const Slicer& ns, Array* dataPtr); virtual void getColumnSliceCellsInt64V (const RefRows& rownrs, const Slicer& ns, Array* dataPtr); virtual void getColumnSliceCellsfloatV (const RefRows& rownrs, const Slicer& ns, Array* dataPtr); virtual void getColumnSliceCellsdoubleV (const RefRows& rownrs, const Slicer& ns, Array* dataPtr); virtual void getColumnSliceCellsComplexV (const RefRows& rownrs, const Slicer& ns, Array* dataPtr); virtual void getColumnSliceCellsDComplexV (const RefRows& rownrs, const Slicer& ns, Array* dataPtr); virtual void getColumnSliceCellsStringV (const RefRows& rownrs, const Slicer& ns, Array* dataPtr); // // Put the array values into some cells of the column. // The buffer pointed to by dataPtr has to have the correct length. // (which is guaranteed by the ArrayColumn putColumnSlice function). // The default implementations call DataManagerColumn::putColumnSliceCellsBase. // virtual void putColumnSliceCellsBoolV (const RefRows& rownrs, const Slicer& ns, const Array* dataPtr); virtual void putColumnSliceCellsuCharV (const RefRows& rownrs, const Slicer& ns, const Array* dataPtr); virtual void putColumnSliceCellsShortV (const RefRows& rownrs, const Slicer& ns, const Array* dataPtr); virtual void putColumnSliceCellsuShortV (const RefRows& rownrs, const Slicer& ns, const Array* dataPtr); virtual void putColumnSliceCellsIntV (const RefRows& rownrs, const Slicer& ns, const Array* dataPtr); virtual void putColumnSliceCellsuIntV (const RefRows& rownrs, const Slicer& ns, const Array* dataPtr); virtual void putColumnSliceCellsInt64V (const RefRows& rownrs, const Slicer& ns, const Array* dataPtr); virtual void putColumnSliceCellsfloatV (const RefRows& rownrs, const Slicer& ns, const Array* dataPtr); virtual void putColumnSliceCellsdoubleV (const RefRows& rownrs, const Slicer& ns, const Array* dataPtr); virtual void putColumnSliceCellsComplexV (const RefRows& rownrs, const Slicer& ns, const Array* dataPtr); virtual void putColumnSliceCellsDComplexV (const RefRows& rownrs, const Slicer& ns, const Array* dataPtr); virtual void putColumnSliceCellsStringV (const RefRows& rownrs, const Slicer& ns, const Array* dataPtr); // }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/StManColumnBase.cc000066400000000000000000000046001476623553700214050ustar00rootroot00000000000000//# StManColumnBase.cc: Base storage manager column class //# Copyright (C) 2019 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN StManColumnBase::StManColumnBase (int dataType) : dtype_p (static_cast(dataType)) { elemSize_p = ValType::getTypeSize (dtype_p); } StManColumnBase::~StManColumnBase() {} int StManColumnBase::dataType() const { return dtype_p; } Bool StManColumnBase::isNativeDataType (int dtype) { switch (dtype) { case TpBool: case TpUChar: case TpShort: case TpUShort: case TpInt: case TpUInt: case TpInt64: case TpFloat: case TpDouble: case TpComplex: case TpDComplex: case TpString: case TpArrayBool: case TpArrayUChar: case TpArrayShort: case TpArrayUShort: case TpArrayInt: case TpArrayUInt: case TpArrayInt64: case TpArrayFloat: case TpArrayDouble: case TpArrayComplex: case TpArrayDComplex: case TpArrayString: return True; } return False; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/StManColumnBase.h000066400000000000000000000063671476623553700212630ustar00rootroot00000000000000//# StManColumnBase.h: Base storage manager column class //# Copyright (C) 2019 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_STMANCOLUMNBASE_H #define TABLES_STMANCOLUMNBASE_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Base table column storage manager class // // // // // //# Classes you should understand before using this one. //
      • DataManagerColumn // // // StManColumnBase is the base class for a storage manager. // // // StManColumnBase is the abstract base class to handle a column in all // kind of storage managers. It is derived from DataManagerColumn // and implements a few (virtual) functions handling the column's // data type for derived storage manager column classes. // // //# A List of bugs, limitations, extensions or planned refinements. // class StManColumnBase : public DataManagerColumn { public: // Default constructor. StManColumnBase (int dataType); ~StManColumnBase(); // The object cannot be copied. StManColumnBase (const StManColumnBase&) = delete; // The object cannot be assigned to. StManColumnBase& operator= (const StManColumnBase&) = delete; // Test if the given data type is supported by storage managers. // It is used by the function Table::isNativeDataType. static Bool isNativeDataType (int dtype); // Return the data type of the column. // virtual int dataType() const; DataType dtype() const { return dtype_p; } // // Return the size of an element of the column's data type. Int elemSize() const { return elemSize_p; } private: // The data type of the column. DataType dtype_p; // The size of an element of this data type. Int elemSize_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/StandardStMan.cc000066400000000000000000000032061476623553700211160ustar00rootroot00000000000000//# StandardStMan.cc: The Standard Storage Manager //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include namespace casacore { //# NAMESPACE CASACORE - BEGIN StandardStMan::StandardStMan (Int bucketSize, uInt cacheSize) : SSMBase (bucketSize, cacheSize) {} StandardStMan::StandardStMan (const String& dataManagerName, Int bucketSize, uInt cacheSize) : SSMBase (dataManagerName, bucketSize, cacheSize) {} StandardStMan::~StandardStMan() {} } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/StandardStMan.h000066400000000000000000000172421476623553700207650ustar00rootroot00000000000000//# StandardStMan.h: The Standard Storage Manager //# Copyright (C) 2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_STANDARDSTMAN_H #define TABLES_STANDARDSTMAN_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // The Standard Storage Manager // // // // // //# Classes you should understand before using this one. //
      • The Table Data Managers concept as described in module file // Tables.h //
      • // ROStandardStManAccessor // for a discussion of the cache size // // // StandardStMan is the data manager which stores the data in a // standard way. I.e. it does not use special techniques like // other storage managers do. // // // StandardStMan is meant as the storage manager to be used standardly. // Other storage managers like // IncrementalStMan and the // TiledStMan derivatives should // only be used when appropriate. //
        // Like the other storage managers StandardStMan uses // bucket-based access to its data. // where a bucket contains the number of columns and rows that fit best. // Variable length strings are stored in separate buckets because they do // not fit in the fixed bucket layout used for the other columns. // Only fixed length strings and strings <= 8 characters are stored directly. // Note that, in fact, fixed length string means maximum length strings. // It can be set using the setMaxLength function in // class ColumnDesc or // class BaseColumnDesc. //

        // The file size is at least the size of a bucket, even if only the table // contains only a few rows, thus uses only a fraction of a bucket. // The default bucketsize is 32 rows. This means that if it is known // in advance that the table will contain many more rows, it might make // sense to construct the StandardStMan with a larger bucketsize. //

        // StandardStMan is a robust storage manager. Care has been taken // that its index cannot be corrupted in case of exceptions like // device full or crash. //

        // StandardStMan supports the following functionality: //

          //
        1. Removal of rows. This leaves some empty space in a bucket. // An empty bucket will be reused. //
        2. Addition of rows. This is always done in the last bucket // and a new bucket is added when needed. //
        3. Removal of a column. This also leaves empty space, which will // be reused when a newly added column fits in it. //
        4. Addition of a column. If available, empty column space is used. // Otherwise it creates as many new buckets as needed. //
        // All direct data (scalars and direct arrays) is stored in the main file. // Indirect arrays (except strings) are stored in a second file. // Indirect string arrays are also stored in the main file, because in // that way frequently rewriting indirect strings arrays wastes far // less space. //

        // As said above all string arrays and variable length scalar strings // are stored in separate string buckets. // // // StManAipsIO is the standard storage manager used so far. // Its major drawback is that it is memory based which makes it // not usable for large tables. Furthermore it is not a very robust // storage manager. When a system crashes, tables might get corrupted. //
        // These drawbacks have been adressed in this new StandardStman. // It uses a bucket-based access scheme and makes sure that its // indices are stored in a way that they can hardly get corrupted. //
        // // The following example shows how to create a table and how to attach // the storage manager to some columns. // // SetupNewTable newtab("name.data", tableDesc, Table::New); // StandardStMan stman; // define storage manager // newtab.bindColumn ("column1", stman); // bind column to st.man. // newtab.bindColumn ("column2", stman); // bind column to st.man. // Table tab(newtab); // actually create table // // // The following example shows how to create a StandardStMan storage // manager for a table with 16 rows. By giving the (expected) nr of rows // to the storage manager, it can optimize its bucket size. // // SetupNewTable newtab("name.data", tableDesc, Table::New); // StandardStMan stman(-16); // newtab.bindAll ("column1", stman); // bind all columns to st.man. // Table tab(newtab); // actually create table // // //# //# A List of bugs, limitations, extensions or planned refinements. //# class StandardStMan : public SSMBase { public: // Create a Standard storage manager with the given name. // If no name is used, it is set to "SSM" // The name can be used to construct a // ROStandardStManAccessor // object (e.g. to set the cache size). //
        // The cache size has to be given in buckets. //
        // The bucket size can be given in 2 ways: //
        - A positive number gives the bucket size in bytes. // The number of rows per bucket will be calculated from it. //
        - A negative number gives the number of rows per bucket. // The bucket size in bytes will be calculated from it. // Note that in this way the maximum bucketsize is 32768 (minimum is 128). //
        - The default 0 means that 32 rows will be stored in a bucket. //
        Note that the default is only suitable for small tables. // In general it makes sense to give the expected number of table rows. // In that way the buckets will be small enough for small tables // and not too small for large tables. // explicit StandardStMan (Int bucketSize = 0, uInt cacheSize = 1); explicit StandardStMan (const String& dataManagerName, Int bucketSize = 0, uInt cacheSize = 1); // ~StandardStMan(); // Copy constructor cannot be used. StandardStMan (const StandardStMan&) = delete; // Assignment cannot be used. StandardStMan& operator= (const StandardStMan&) = delete; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/StandardStManAccessor.cc000066400000000000000000000062131476623553700226020ustar00rootroot00000000000000//# StandardStManAccessor.cc: Gives access to some StandardStMan functions //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ROStandardStManAccessor::ROStandardStManAccessor (const Table& table, const String& name, Bool byColumn) : RODataManAccessor (table, name, byColumn), itsSSMPtr (0) { itsSSMPtr = dynamic_cast(baseDataManager()); if (itsSSMPtr == 0) { throw (DataManError ("ROStandardStManAccessor " + name + " constructed for data manager type " + baseDataManager()->dataManagerType() + "; expected StandardStMan")); } } ROStandardStManAccessor::~ROStandardStManAccessor() {} ROStandardStManAccessor::ROStandardStManAccessor (const ROStandardStManAccessor& that) : RODataManAccessor(that), itsSSMPtr (that.itsSSMPtr) {} ROStandardStManAccessor& ROStandardStManAccessor::operator= (const ROStandardStManAccessor& that) { itsSSMPtr = that.itsSSMPtr; return *this; } void ROStandardStManAccessor::setCacheSize (uInt aSize, Bool canExceedNrBuckets) { itsSSMPtr->setCacheSize (aSize, canExceedNrBuckets); } uInt ROStandardStManAccessor::getCacheSize() const { return itsSSMPtr->getCacheSize(); } void ROStandardStManAccessor::clearCache() { itsSSMPtr->clearCache(); } void ROStandardStManAccessor::showBaseStatistics (ostream& anOs) const { itsSSMPtr->showBaseStatistics (anOs); } void ROStandardStManAccessor::showIndexStatistics (ostream& anOs) const { itsSSMPtr->showIndexStatistics (anOs); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/StandardStManAccessor.h000066400000000000000000000126471476623553700224540ustar00rootroot00000000000000//# StandardStManAccessor.h: Gives access to some StandardStMan functions //# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_STANDARDSTMANACCESSOR_H #define TABLES_STANDARDSTMANACCESSOR_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class SSMBase; class DataManager; class Table; class String; //

        // Give access to some StandardStMan functions // // // // // //# Classes you should understand before using this one. //
      • StandardStMan // // // The Table system has one or more storage managers underneath. // One of these possible storage managers is the // StandardStMan. // This storage manager uses a cache of buckets. The default // cache size is defined when the StandardStMan object was // constructed at the time the table was created. //

        // Sometimes it can be useful to change the cache size. E.g. when // the table is accessed in a random way, the hit rate may drop // when the cache is too small. The class ROStandardStManAccessor makes // it possible to change the cache size in a temporary way. //
        // It is also possible to get the cache size. //

        // Furthermore it is possible to show some statistics (about the cache // and the internals of SSM classes). // // // In principle a pointer to StandardStMan could be used. // However, that would give access to all public functions. // Furthermore it could not distinguish between read/write and readonly // tables. // // // This example shows how to set the cache size for // the standard storage manager with the name "SSMExample". The cache // size is not persistent, i.e. when the same table is reopened // at a later time, this cache size is not remembered. // // // Open a table. // Table table("someName.data"); // // Set the cache size of its standard storage manager SSMExample // // to 5 buckets. // ROStandardStManAccessor accessor(table, "SSMExample"); // accessor.setCacheSize (5); // // //# //# class ROStandardStManAccessor : public RODataManAccessor { public: // Construct the object for a data manager in the table given the name // of the data manager or the column. // An exception is thrown if the data manager type is not the incremental // storage manager. ROStandardStManAccessor (const Table& table, const String& name, Bool byColumn=False); virtual ~ROStandardStManAccessor(); // Copy constructor (reference semantics). ROStandardStManAccessor (const ROStandardStManAccessor& that); // Assignment (reference semantics). ROStandardStManAccessor& operator= (const ROStandardStManAccessor& that); // Set the cache size (in buckets) to be used by the // storage manager. // The cache size given in this way is not persistent. // Only the cache size given to the constructors of the Standard // storage managers, is persistent. // If canExceedNrBuckets=True, the given cache size can be // larger than the nr of buckets in the file. In this way the cache can // be made large enough for a future file extension. // Otherwise, it is limited to the actual number of buckets. This is useful // if one wants the entire file to be cached. void setCacheSize (uInt aSize, Bool canExceedNrBuckets=True); // Get the cache size (in buckets). uInt getCacheSize() const; // Clear the cache used by this storage manager. // It will flush the cache as needed and remove all buckets from it // resulting in a drop in memory used. void clearCache(); // Show the statistics for the base class. void showBaseStatistics (ostream& anOs) const; // Show the statistics for each index used by this storage manager. void showIndexStatistics (ostream& anOs) const; private: //# Declare the data members. SSMBase* itsSSMPtr; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/TSMColumn.cc000066400000000000000000000055101476623553700202340ustar00rootroot00000000000000//# TSMColumn.cc: Tiled Hypercube Storage Manager for table columns //# Copyright (C) 1995,1996,1997 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TSMColumn::TSMColumn (TiledStMan* stman, int dataType, const String& columnName) : StManColumnBase (dataType), stmanPtr_p (stman), dtype_p (dataType), name_p (columnName), colPtr_p (0) {} TSMColumn::TSMColumn (const TSMColumn& that) : StManColumnBase (that.dtype_p), stmanPtr_p (that.stmanPtr_p), dtype_p (that.dtype_p), name_p (that.name_p), columnShape_p (that.columnShape_p), colPtr_p (0) {} TSMColumn::~TSMColumn() { delete colPtr_p; } int TSMColumn::dataType() const { return dtype_p; } void TSMColumn::setShapeColumn (const IPosition& shape) { columnShape_p = shape; } TSMDataColumn* TSMColumn::makeDataColumn() { TSMDataColumn* colPtr = new TSMDataColumn (*this); colPtr_p = colPtr; return colPtr; } TSMCoordColumn* TSMColumn::makeCoordColumn (uInt axesNumber) { TSMCoordColumn* colPtr = new TSMCoordColumn (*this, axesNumber); colPtr_p = colPtr; return colPtr; } TSMIdColumn* TSMColumn::makeIdColumn() { TSMIdColumn* colPtr = new TSMIdColumn (*this); colPtr_p = colPtr; return colPtr; } TSMColumn* TSMColumn::unlink() { TSMColumn* ptr = colPtr_p; colPtr_p = 0; // do not delete linked object in destructor return ptr; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/TSMColumn.h000066400000000000000000000125531476623553700201030ustar00rootroot00000000000000//# TSMColumn.h: A column in the Tiled Storage Manager //# Copyright (C) 1995,1996,1997,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TSMCOLUMN_H #define TABLES_TSMCOLUMN_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TiledStMan; class TSMDataColumn; class TSMCoordColumn; class TSMIdColumn; //

        // A column in the Tiled Storage Manager // // // // // //# Classes you should understand before using this one. //
      • StManColumn //
      • TiledStMan // // // TSMColumn handles a column for the Tiled Storage Manager. // // // TSMColumn serves 2 purposes: //
          //
        1. It is the initial object for all columns in TiledStMan. //
        2. It serves as a base class for the specialized TiledStMan // column classes dealing with data, coordinates and id values. //
        // The protocol used for creating the derived // TSMDataColumn, // TSMCoordColumn, and // TSMIdColumn // objects is somewhat complicated. It works as follows: //
        // When the table is set up, a TSMColumn object gets created for all // columns in a TiledStMan storage manager. The TiledStMan initialization // function lets each TSMColumn object create its specialized TSMXXColumn // object (using make{Coord,Id,Data}Column). At the end of the setup process // the TSMColumn objects are deleted and the DataManagerColumn pointers // in the BaseColumn objects get replaced by those to the specialized // objects. In that way no needless virtual function calls are done. //
        // // TSMColumn is needed for the initial DataManagerColumn setup process. // It is also useful as a base class for all TiledStMan column objects. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class TSMColumn : public StManColumnBase { public: // Create a column of the given type. // It will maintain a pointer to its parent storage manager. TSMColumn (TiledStMan* stman, int dataType, const String& columnName); // Frees up the storage. virtual ~TSMColumn(); // Forbid assignment. TSMColumn& operator= (const TSMColumn&) = delete; // Get the name of the column. const String& columnName() const; // Return the data type of the column. virtual int dataType() const; // Set the fixed shape of the column. void setShapeColumn (const IPosition& shape); // Get the fixed shape of the column. const IPosition& shapeColumn() const; // Make a TSM data column object. // Add the pixel length to the total data pixel length. TSMDataColumn* makeDataColumn(); // Make a TSM coordinate column object. TSMCoordColumn* makeCoordColumn (uInt axesNumber); // Make a TSM id column object. TSMIdColumn* makeIdColumn(); // Unlink the underlying column. // It clears the pointer and returns its original value. // This is used to get a pointer directly to the underlying TSMXXColumn // object in the BaseColumn classes. In that way only 1 instead // of 2 virtual function calls are needed for a get or put. TSMColumn* unlink(); protected: // The storage manager. TiledStMan* stmanPtr_p; // The data type of the data (as defined in DataType.h). int dtype_p; // The name of the column. String name_p; // The fixed shape of the column. IPosition columnShape_p; // The specialized column object (i.e. data, coordinate or id). TSMColumn* colPtr_p; // The copy constructor can only be used to copy a derived class. TSMColumn (const TSMColumn& that); }; inline const String& TSMColumn::columnName() const { return name_p; } inline const IPosition& TSMColumn::shapeColumn() const { return columnShape_p; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/TSMCoordColumn.cc000066400000000000000000000221271476623553700212260ustar00rootroot00000000000000//# TSMCoordColumn.cc: Tiled Hypercube Storage Manager for id columns //# Copyright (C) 1995,1996,1997,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TSMCoordColumn::TSMCoordColumn (const TSMColumn& column, uInt axisNr) : TSMColumn (column), axisNr_p (axisNr) {} TSMCoordColumn::~TSMCoordColumn() {} void TSMCoordColumn::setShape (rownr_t rownr, const IPosition& shape) { if (shape.nelements() != 1) { throw (TSMError ("setShape of coordinate column " + columnName() + " is not 1-dim")); } TSMCube* hypercube = stmanPtr_p->getHypercube (rownr); IPosition cubeShape = hypercube->cubeShape(); if (cubeShape.nelements() != 0) { if (shape(0) != cubeShape(axisNr_p)) { throw (TSMError ("setShape of coordinate column " + columnName() + " does not match shape of hypercube")); } return; } const Record& rec = hypercube->valueRecord(); if (rec.isDefined (columnName())) { if (shape != rec.shape (columnName())) { throw (TSMError ("setShape of coordinate column " + columnName() + " has already been done")); } return; } stmanPtr_p->setDataChanged(); Record& record = hypercube->rwValueRecord(); switch (dataType()) { case TpInt: case TpArrayInt: record.define (columnName(), Array(shape)); break; case TpUInt: case TpArrayUInt: record.define (columnName(), Array(shape)); break; case TpFloat: case TpArrayFloat: record.define (columnName(), Array(shape)); break; case TpDouble: case TpArrayDouble: record.define (columnName(), Array(shape)); break; case TpComplex: case TpArrayComplex: record.define (columnName(), Array(shape)); break; case TpDComplex: case TpArrayDComplex: record.define (columnName(), Array(shape)); break; default: throw (DataManInvDT ("Unknown data type for coordinate column " + columnName())); } } Bool TSMCoordColumn::isShapeDefined (rownr_t rownr) { //# The shape is defined when the shape is fixed, when //# a hypercube has been defined for this row or when the //# coordinate values have already been defined. if (shapeColumn().nelements() != 0) { return True; // FixedShape } TSMCube* hypercube = stmanPtr_p->getHypercube (rownr); if (hypercube->valueRecord().isDefined (columnName())) { return True; // already defined } return (hypercube->cubeShape().nelements() != 0); } IPosition TSMCoordColumn::shape (rownr_t rownr) { //# Return the shape when it is fixed. if (shapeColumn().nelements() != 0) { return shapeColumn(); } //# Return coordinate shape if defined. TSMCube* hypercube = stmanPtr_p->getHypercube (rownr); const IPosition& cubeShape = hypercube->cubeShape(); if (cubeShape.nelements() != 0) { return IPosition (1, cubeShape(axisNr_p)); } if (! hypercube->valueRecord().isDefined (columnName())) { throw (DataManInvOper ("TSMCoord: no array in row " + String::toString(rownr) + " of coordinate column " + columnName())); } return hypercube->valueRecord().shape (columnName()); } void TSMCoordColumn::getfloat (rownr_t rownr, float* dataPtr) { // Get the hypercube the row is in. // It also gives the position of the row in the hypercube. IPosition position; TSMCube* hypercube = stmanPtr_p->getHypercube (rownr, position); // Get vector of coordinate values. // Since a reference is used, it has to be treated as an Array. // Get the correct value. RORecordFieldPtr > field (hypercube->valueRecord(), columnName()); *dataPtr = (*field) (IPosition (1, position(axisNr_p))); } void TSMCoordColumn::putfloat (rownr_t rownr, const float* dataPtr) { // Get the hypercube the row is in. // It also gives the position of the row in the hypercube. IPosition position; TSMCube* hypercube = stmanPtr_p->getHypercube (rownr, position); // Get vector of coordinate values. // Since a reference is used, it has to be treated as an Array. // Update the correct value. RecordFieldPtr > field (hypercube->rwValueRecord(), columnName()); (*field) (IPosition (1, position(axisNr_p))) = *dataPtr; stmanPtr_p->setDataChanged(); } void TSMCoordColumn::getArrayV (rownr_t rownr, ArrayBase& dataPtr) { // Get the hypercube the row is in. // It also gives the position of the row in the hypercube. IPosition position; TSMCube* hypercube = stmanPtr_p->getHypercube (rownr, position); switch (dtype()) { case TpInt: hypercube->valueRecord().toArray (columnName(), static_cast&>(dataPtr)); break; case TpUInt: hypercube->valueRecord().toArray (columnName(), static_cast&>(dataPtr)); break; case TpFloat: hypercube->valueRecord().toArray (columnName(), static_cast&>(dataPtr)); break; case TpDouble: hypercube->valueRecord().toArray (columnName(), static_cast&>(dataPtr)); break; case TpComplex: hypercube->valueRecord().toArray (columnName(), static_cast&>(dataPtr)); break; case TpDComplex: hypercube->valueRecord().toArray (columnName(), static_cast&>(dataPtr)); break; default: throw DataManInvDT ("TSMCoordColiumn::getArrayV"); } } void TSMCoordColumn::putArrayV (rownr_t rownr, const ArrayBase& dataPtr) { // Get the hypercube the row is in. // It also gives the position of the row in the hypercube. IPosition position; TSMCube* hypercube = stmanPtr_p->getHypercube (rownr, position); switch (dtype()) { case TpInt: hypercube->rwValueRecord().define (columnName(), static_cast&>(dataPtr)); break; case TpUInt: hypercube->rwValueRecord().define (columnName(), static_cast&>(dataPtr)); break; case TpFloat: hypercube->rwValueRecord().define (columnName(), static_cast&>(dataPtr)); break; case TpDouble: hypercube->rwValueRecord().define (columnName(), static_cast&>(dataPtr)); break; case TpComplex: hypercube->rwValueRecord().define (columnName(), static_cast&>(dataPtr)); break; case TpDComplex: hypercube->rwValueRecord().define (columnName(), static_cast&>(dataPtr)); break; default: throw DataManInvDT ("TSMCoordColiumn::putArrayV"); } stmanPtr_p->setDataChanged(); } #define TSMCOORDCOLUMN_GETPUT(T) \ void TSMCoordColumn::aips_name2(get,T) (rownr_t rownr, T* dataPtr) \ { \ IPosition position; \ TSMCube* hypercube = stmanPtr_p->getHypercube (rownr, position); \ RORecordFieldPtr > field (hypercube->valueRecord(),columnName());\ *dataPtr = (*field) (IPosition (1, position(axisNr_p))); \ } \ void TSMCoordColumn::aips_name2(put,T) (rownr_t rownr, const T* dataPtr) \ { \ IPosition position; \ TSMCube* hypercube = stmanPtr_p->getHypercube (rownr, position); \ RecordFieldPtr > field (hypercube->rwValueRecord(),columnName()); \ (*field) (IPosition (1, position(axisNr_p))) = *dataPtr; \ stmanPtr_p->setDataChanged(); \ } TSMCOORDCOLUMN_GETPUT(Int) TSMCOORDCOLUMN_GETPUT(uInt) TSMCOORDCOLUMN_GETPUT(Int64) //#TSMCOORDCOLUMN_GETPUT(float) TSMCOORDCOLUMN_GETPUT(double) TSMCOORDCOLUMN_GETPUT(Complex) TSMCOORDCOLUMN_GETPUT(DComplex) } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/TSMCoordColumn.h000066400000000000000000000136121476623553700210670ustar00rootroot00000000000000//# TSMCoordColumn.h: A coordinate column in Tiled Storage Manager //# Copyright (C) 1995,1996,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TSMCOORDCOLUMN_H #define TABLES_TSMCOORDCOLUMN_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations // // A coordinate column in Tiled Storage Manager // // // // // //# Classes you should understand before using this one. //
      • TSMColumn //
      • TSMCube //
      • Record // // // TSMCoordColumn handles a coordinate column for a Tiled // Storage Manager. // // // TSMCoordColumn is used by // TiledStMan // to handle the access to // a table column containing coordinates of a tiled hypercube axis. // There are 2 types of coordinates (as described at // // TableDesc::defineHypercolumn): //
          //
        1. As a vector. These are the coordinates of the arrays held // in the data cells. They are accessed via the get/putArray // functions. Their shapes are dependent on the hypercube shape, // so it is checked if they match. //
        2. As a scalar. These are the coordinates of the extra axes // defined in the hypercube. They are accessed via the get/put // functions. //
        // The coordinates are held in a TSMCube object. The row number // determines which TSMCube object has to be accessed. //

        // The creation of a TSMCoordColumn object is done by a TSMColumn object. // This process is described in more detail in the class // TSMColumn. // // // Handling coordinate columns in the Tiled Storage Manager is // different from other columns. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class TSMCoordColumn : public TSMColumn { public: // Create a coordinate column from the given column. TSMCoordColumn (const TSMColumn& column, uInt axisNr); // Frees up the storage. virtual ~TSMCoordColumn(); // Forbid copy constructor. TSMCoordColumn (const TSMCoordColumn&) = delete; // Forbid assignment. TSMCoordColumn& operator= (const TSMCoordColumn&) = delete; // Set the shape of the coordinate vector in the given row. virtual void setShape (rownr_t rownr, const IPosition& shape); // Is the value shape defined in the given row? virtual Bool isShapeDefined (rownr_t rownr); // Get the shape of the item in the given row. virtual IPosition shape (rownr_t rownr); // Get a scalar value in the given row. // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the Scalar/ArrayColumn get function). // virtual void getInt (rownr_t rownr, Int* dataPtr); virtual void getuInt (rownr_t rownr, uInt* dataPtr); virtual void getInt64 (rownr_t rownr, Int64* dataPtr); virtual void getfloat (rownr_t rownr, float* dataPtr); virtual void getdouble (rownr_t rownr, double* dataPtr); virtual void getComplex (rownr_t rownr, Complex* dataPtr); virtual void getDComplex (rownr_t rownr, DComplex* dataPtr); // // Put a scalar value into the given row. // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the Scalar/ArrayColumn put function). // virtual void putInt (rownr_t rownr, const Int* dataPtr); virtual void putuInt (rownr_t rownr, const uInt* dataPtr); virtual void putInt64 (rownr_t rownr, const Int64* dataPtr); virtual void putfloat (rownr_t rownr, const float* dataPtr); virtual void putdouble (rownr_t rownr, const double* dataPtr); virtual void putComplex (rownr_t rownr, const Complex* dataPtr); virtual void putDComplex (rownr_t rownr, const DComplex* dataPtr); // // Get the array value in the given row. // The array pointed to by dataPtr has to have the correct length // (which is guaranteed by the ArrayColumn get function). virtual void getArrayV (rownr_t rownr, ArrayBase& dataPtr); // Put the array value into the given row. // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the ArrayColumn put function). virtual void putArrayV (rownr_t rownr, const ArrayBase& dataPtr); private: // The axis number of the coordinate. uInt axisNr_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/TSMCube.cc000066400000000000000000001441661476623553700176700ustar00rootroot00000000000000//# TSMCube.cc: Tiled Hypercube Storage Manager for tables //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // for memcpy #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // memcpy with constant argument is inlined #define TSM_COPY(a, b, n) case n: memcpy(a, b, n); break static void TSMCube_MoveData(char * a, char * b, int n) { switch (n) { case 0: break; TSM_COPY(a, b, 1); TSM_COPY(a, b, 2); TSM_COPY(a, b, 3); TSM_COPY(a, b, 4); TSM_COPY(a, b, 5); TSM_COPY(a, b, 6); TSM_COPY(a, b, 7); TSM_COPY(a, b, 8); TSM_COPY(a, b, 12); TSM_COPY(a, b, 16); TSM_COPY(a, b, 20); TSM_COPY(a, b, 24); TSM_COPY(a, b, 28); TSM_COPY(a, b, 32); default: memcpy(a, b, n); } } #undef TSM_COPY TSMCube::TSMCube (TiledStMan* stman, TSMFile* file, const IPosition& cubeShape, const IPosition& tileShape, const Record& values, Int64 fileOffset, Bool useDerived) : cachedTile_p (0), stmanPtr_p (stman), useDerived_p (useDerived), values_p (values), extensible_p (False), nrdim_p (0), nrTiles_p (0), tileSize_p (0), filePtr_p (file), fileOffset_p (0), cache_p (0), userSetCache_p (False), lastColAccess_p(NoAccess) { if (fileOffset < 0) { // TiledCellStMan uses an empty shape; setShape is called later. if (! cubeShape.empty()) { // A shape is given, so set it. extensible_p = cubeShape(cubeShape.nelements()-1) == 0; setShape (cubeShape, tileShape); } } else { // Meant for TiledFileAccess. nrdim_p = cubeShape.nelements(); cubeShape_p = cubeShape; tileShape_p = tileShape; fileOffset_p = fileOffset; setup(); } } TSMCube::TSMCube (TiledStMan* stman, AipsIO& ios, Bool useDerived) : cachedTile_p (0), stmanPtr_p (stman), useDerived_p (useDerived), filePtr_p (0), cache_p (0), userSetCache_p (False), lastColAccess_p(NoAccess) { Int fileSeqnr = getObject (ios); if (fileSeqnr >= 0) { filePtr_p = stmanPtr_p->getFile (fileSeqnr); } // Calculate the various variables. setup(); } TSMCube::~TSMCube() { delete cache_p; delete [] cachedTile_p; } void TSMCube::clearCache (Bool doFlush) { if (doFlush) { flushCache(); } if (cache_p != 0) { cache_p->clear (0, False); } } void TSMCube::emptyCache() { if (cache_p != 0) { cache_p->resize (0); } userSetCache_p = False; lastColAccess_p = NoAccess; } void TSMCube::showCacheStatistics (ostream& os) const { if (cache_p != 0) { os << ">>> TSMCube cache statistics:" << endl; os << "cubeShape: " << cubeShape_p << endl; os << "tileShape: " << tileShape_p << endl; os << "maxCacheSz:" << stmanPtr_p->maximumCacheSize() << " MiB" << endl; cache_p->showStatistics (os); os << "<<<" << endl; } } uInt TSMCube::coordinateSize (const String& coordinateName) const { if (! values_p.isDefined (coordinateName)) { return 0; } IPosition shape (values_p.shape (coordinateName)); if (shape.nelements() == 0) { return 0; } return shape(0); } IPosition TSMCube::cellShape() const { uInt nr = stmanPtr_p->nrCoordVector(); if (nr < cubeShape_p.nelements()) { return cubeShape_p.getFirst (nr); } return cubeShape_p; } IPosition TSMCube::adjustTileShape (const IPosition& cubeShape, const IPosition& tileShape) const { // Make this function independent of the length of tileShape, // so it can be shorter or longer than the cube shape. // The returned tile shape always has the length of the cube shape. uInt nrdim = cubeShape.nelements(); // Make length of tile shape equal to length of cube shape. // Fill with 0 (meaning undefined tile axes). IPosition tileShp (nrdim, 0); IPosition cubeUnk (nrdim); uInt nrunk = 0; uInt length = 1; for (uInt i=0; i cubeShape(i) && cubeShape(i) != 0) { tileShp(i) = cubeShape(i); } length *= tileShp(i); } } cubeUnk.resize (nrunk); // Calculate a default for the unknown tile axes. // Use the remainder of the 32768 for it. if (nrunk > 0) { Float rem = 32768. / length; Int leng = max(1, Int(rem + 0.5)); IPosition tileUnk = TiledStMan::makeTileShape (cubeUnk, 0.5, leng); length *= tileUnk.product(); uInt j = 0; for (uInt i=0; icheckCubeShape (this, cubeShape); // If the shape is redefined, the cache may already exist. // So delete it first. deleteCache(); fileOffset_p = filePtr_p->length(); nrdim_p = cubeShape.nelements(); // Resize the tile section member variables used in accessSection() resizeTileSections(); cubeShape_p = cubeShape; tileShape_p = adjustTileShape (cubeShape, tileShape); // Calculate the various variables. setup(); // If used directly, create the cache. // It has to be done here, otherwise the file does not get extended if // no explicit put is done. if (!useDerived_p) { makeCache(); } // Tell TSMFile that the file gets extended. filePtr_p->extend (nrTiles_p * bucketSize_p); // Initialize the coordinate columns (as far as needed). stmanPtr_p->initCoordinates (this); // Set flag if writing. stmanPtr_p->setDataChanged(); } void TSMCube::putObject (AipsIO& ios) { flushCache(); // If the offset is small enough, write it as an old style file, // so older software can still read it. Bool vers1 = (fileOffset_p < 2u*1024u*1024u*1024u); if (vers1) { ios << 1; // version 1 } else { ios << 2; // version 2 } ios << values_p; ios << extensible_p; ios << nrdim_p; ios << cubeShape_p; ios << tileShape_p; Int seqnr = -1; if (filePtr_p != 0) { seqnr = filePtr_p->sequenceNumber(); } ios << seqnr; if (vers1) { ios << uInt(fileOffset_p); } else { ios << fileOffset_p; } } Int TSMCube::getObject (AipsIO& ios) { uInt version; Int fileSeqnr; ios >> version; ios >> values_p; ios >> extensible_p; ios >> nrdim_p; ios >> cubeShape_p; ios >> tileShape_p; ios >> fileSeqnr; if (version == 1) { uInt offs; ios >> offs; fileOffset_p = offs; } else { ios >> fileOffset_p; } return fileSeqnr; } void TSMCube::resync (AipsIO& ios) { getObject (ios); setupNrTiles(); resyncCache(); } void TSMCube::setup() { // Determine the nr of tiles in all but the last dimension. // This is needed when extending the last dimension. // Also determine the nr of tiles needed (total and per dimension). setupNrTiles(); expandedTileShape_p = TSMShape (tileShape_p); expandedTilesPerDim_p = TSMShape (tilesPerDim_p); // Determine the bucket size for the cache. // Also determine the start offset for each data column // and the length of the tile if converted to local format. tileSize_p = tileShape_p.product(); bucketSize_p = stmanPtr_p->getLengthOffset (tileSize_p, externalOffset_p, localOffset_p, localTileLength_p); // Resize IPosition member variables used in accessSection() resizeTileSections(); } void TSMCube::setupNrTiles() { // Determine the nr of tiles in all but the last dimension. // This is needed when extending the last dimension. // Also determine the nr of tiles needed (total and per dimension). tilesPerDim_p.resize (nrdim_p); nrTiles_p = 1; for (uInt i=0; ibucketFile(), fileOffset_p, bucketSize_p, nrTiles_p, 1, this, readCallBack, writeCallBack, initCallBack, deleteCallBack); } } void TSMCube::flushCache() { if (cache_p != 0) { cache_p->flush(); } } void TSMCube::resyncCache() { if (cache_p != 0) { cache_p->resync (nrTiles_p, 0, -1); } } void TSMCube::deleteCache() { delete cache_p; cache_p = 0; } Bool TSMCube::isExtensible() const { return extensible_p; } void TSMCube::extend (uInt64 nr, const Record& coordValues, const TSMColumn* lastCoordColumn) { if (!extensible_p) { throw TSMError ("Hypercube in TSM " + stmanPtr_p->dataManagerName() + " is not extensible"); } // Make the cache here, otherwise nrTiles_p is too high. makeCache(); uInt lastDim = nrdim_p - 1; uInt nrold = nrTiles_p; cubeShape_p(lastDim) += nr; tilesPerDim_p(lastDim) = (cubeShape_p(lastDim) + tileShape_p(lastDim) - 1) / tileShape_p(lastDim); nrTiles_p = nrTilesSubCube_p * tilesPerDim_p(lastDim); getCache()->extend (nrTiles_p - nrold); filePtr_p->extend ((nrTiles_p - nrold) * bucketSize_p); // Update the last coordinate (if there). if (lastCoordColumn != 0) { extendCoordinates (coordValues, lastCoordColumn->columnName(), cubeShape_p(lastDim)); } } void TSMCube::extendCoordinates (const Record& coordValues, const String& name, uInt length) { //# Determine if the coordinate field is already defined. Bool defined = values_p.isDefined (name); //# Determine the extension length of the coordinate vector. //# This is the given length (which is the entire cube axis) //# minus already defined coordinate length. uInt vectorLength = length; if (defined) { vectorLength -= coordinateSize (name); } //# Exit if no extend. if (vectorLength == 0) { return; } //# Determine the start and end of the new coordinate values. //# If they are not defined, start will be > end. IPosition start(1, length); IPosition end(1, length-1); if (coordValues.isDefined (name)) { IPosition shape = coordValues.shape (name); if (shape.nelements() > 0) { start(0) -= shape(0); } } //# Now insert the new coordinate values. //# Note that the nr of new coordinate values can be less than //# the coordinate vector extension length. //# The algorithm is as follows: //# - Define coordinate values if not defined yet. //# - Extend the coordinate vector with default values. //# - Insert the new coordinate values. switch (stmanPtr_p->coordinateDataType (name)) { case TpBool: case TpArrayBool: { if (!defined) { values_p.define (name, Array()); } RecordFieldPtr > field (values_p, name); Array& array = *field; if (vectorLength > 0) { Vector vector(vectorLength); vector = False; Array newArray (concatenateArray (array, vector)); array.reference (newArray); } if (start(0) < Int(length)) { array(start, end) = coordValues.toArrayBool (name); } } break; case TpInt: case TpArrayInt: { if (!defined) { values_p.define (name, Array()); } RecordFieldPtr > field (values_p, name); Array& array = *field; if (vectorLength > 0) { Vector vector(vectorLength); vector = 0; Array newArray (concatenateArray (array, vector)); array.reference (newArray); } if (start(0) < Int(length)) { array(start, end) = coordValues.toArrayInt (name); } } break; case TpUInt: case TpArrayUInt: { if (!defined) { values_p.define (name, Array()); } RecordFieldPtr > field (values_p, name); Array& array = *field; if (vectorLength > 0) { Vector vector(vectorLength); vector = 0; Array newArray (concatenateArray (array, vector)); array.reference (newArray); } if (start(0) < Int(length)) { array(start, end) = coordValues.toArrayuInt (name); } } break; case TpFloat: case TpArrayFloat: { if (!defined) { values_p.define (name, Array()); } RecordFieldPtr > field (values_p, name); Array& array = *field; if (vectorLength > 0) { Vector vector(vectorLength); vector = 0; Array newArray (concatenateArray (array, vector)); array.reference (newArray); } if (start(0) < Int(length)) { array(start, end) = coordValues.toArrayFloat (name); } } break; case TpDouble: case TpArrayDouble: { if (!defined) { values_p.define (name, Array()); } RecordFieldPtr > field (values_p, name); Array& array = *field; if (vectorLength > 0) { Vector vector(vectorLength); vector = 0; Array newArray (concatenateArray (array, vector)); array.reference (newArray); } if (start(0) < Int(length)) { array(start, end) = coordValues.toArrayDouble (name); } } break; case TpComplex: case TpArrayComplex: { if (!defined) { values_p.define (name, Array()); } RecordFieldPtr > field (values_p, name); Array& array = *field; if (vectorLength > 0) { Vector vector(vectorLength); vector = Complex(0); Array newArray (concatenateArray (array, vector)); array.reference (newArray); } if (start(0) < Int(length)) { array(start, end) = coordValues.toArrayComplex (name); } } break; case TpDComplex: case TpArrayDComplex: { if (!defined) { values_p.define (name, Array()); } RecordFieldPtr > field (values_p, name); Array& array = *field; if (vectorLength > 0) { Vector vector(vectorLength); vector = DComplex(0); Array newArray (concatenateArray (array, vector)); array.reference (newArray); } if (start(0) < Int(length)) { array(start, end) = coordValues.toArrayDComplex (name); } } break; case TpString: case TpArrayString: { if (!defined) { values_p.define (name, Array()); } RecordFieldPtr > field (values_p, name); Array& array = *field; if (vectorLength > 0) { Vector vector(vectorLength); vector = ""; Array newArray (concatenateArray (array, vector)); array.reference (newArray); } if (start(0) < Int(length)) { array(start, end) = coordValues.toArrayString (name); } } break; default: throw DataManInvDT ("extendCoordinates in TSM " + stmanPtr_p->dataManagerName()); } } Bool TSMCube::matches (const PtrBlock& idColSet, const Record& idValues) { for (uInt i=0; icolumnName(); switch (values_p.dataType (name)) { case TpBool: if (idValues.asBool (name) != values_p.asBool (name)) { return False; } break; case TpString: if (idValues.asString (name) != values_p.asString (name)) { return False; } break; case TpComplex: case TpDComplex: { const DComplex& idVal = idValues.asDComplex (name); const DComplex& val = values_p.asDComplex (name); if (idVal != val) { return False; } } break; default: if (idValues.asdouble (name) != values_p.asdouble (name)) { return False; } break; } } return True; } char* TSMCube::readCallBack (void* owner, const char* external) { return ((TSMCube*)owner)->readTile (external); } char* TSMCube::readTile (const char* external) { char* local = 0; if (cachedTile_p != 0){ local = cachedTile_p; cachedTile_p = 0; } else { local = new char[localTileLength_p]; } stmanPtr_p->readTile (local, localOffset_p, external, externalOffset_p, tileSize_p); return local; } void TSMCube::writeCallBack (void* owner, char* external, const char* local) { ((TSMCube*)owner)->writeTile (external, local); } void TSMCube::writeTile (char* external, const char* local) { stmanPtr_p->writeTile (external, externalOffset_p, local, localOffset_p, tileSize_p); } void TSMCube::deleteCallBack (void* owner, char* buffer) { TSMCube * tsmCube = ((TSMCube*)owner); if (tsmCube->cachedTile_p == 0){ tsmCube->cachedTile_p = buffer; } else { delete [] buffer; } } char* TSMCube::initCallBack (void* owner) { uInt64 size = ((TSMCube*)owner)->localTileLength(); char* buffer = new char[size]; memset(buffer, 0, size); return buffer; } uInt TSMCube::cacheSize() const { if (cache_p == 0) { return 0; } return cache_p->cacheSize(); } uInt TSMCube::validateCacheSize (uInt cacheSize) const { return validateCacheSize (cacheSize, stmanPtr_p->maximumCacheSize(), bucketSize_p); } uInt TSMCube::validateCacheSize (uInt cacheSize, uInt maxSizeMiB, uInt bucketSize) { // An overdraft of 10% is allowed. uInt maxnb = std::max(1u, uInt(1024. * 1024. * maxSizeMiB / bucketSize)); if (maxSizeMiB > 0 && cacheSize > maxnb) { if (10 * cacheSize > 11 * maxnb) { return maxnb; } } return cacheSize; } void TSMCube::setCacheSize (uInt cacheSize, Bool forceSmaller, Bool userSet) { // Resize the cache in the expectation that this access is // the first of a bunch of accesses at the same tiles. // However, don't let the cache exceed the maximum, // unless it is only 10% more. BucketCache* cachePtr = getCache(); cacheSize = validateCacheSize (cacheSize); if (forceSmaller || cacheSize > cachePtr->cacheSize()) { cachePtr->resize (cacheSize); } //// cout << "cachesize=" << cacheSize << endl; userSetCache_p = userSet; } // Set the cache size for the given slice and access path. void TSMCube::setCacheSize (const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath, Bool forceSmaller, Bool userSet) { uInt cacheSize = calcCacheSize (sliceShape, windowStart, windowLength, axisPath); // If not userset, do not cache if more than 25% of the memory is needed. if (!userSet) { uInt maxSize = uInt(HostInfo::memoryTotal(True) * 1024.*0.25 / bucketSize_p); if (cacheSize > maxSize) { cacheSize = 1; } } setCacheSize (cacheSize, forceSmaller, userSet); } // Calculate the cache size for the given slice and access path. uInt TSMCube::calcCacheSize (const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath) const { return calcCacheSize (cubeShape_p, tileShape_p, extensible_p, sliceShape, windowStart, windowLength, axisPath, stmanPtr_p->maximumCacheSize(), bucketSize_p); } uInt TSMCube::calcCacheSize (const IPosition& cubeShape, const IPosition& tileShape, Bool extensible, const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath, uInt maxCacheSize, uInt bucketSize) { uInt nrdim = cubeShape.nelements(); if (sliceShape.nelements() > nrdim || windowStart.nelements() > nrdim || windowLength.nelements() > nrdim || axisPath.nelements() > nrdim) { throw TSMError ("calcCacheSize: invalid arguments"); } uInt i; // The unspecified sliceShape dimensions are 1. IPosition slice(nrdim, 1); for (i=0; i 0) { slice(i) = sliceShape(i); } } // The unspecified window start dimensions are 0. IPosition start(nrdim, 0); for (i=0; i 0) { nrd--; // Caching is needed if lower dimensions are reused because // a higher dimension loops through a tile. // So skip dimensions until a reused dimension is found. uInt nr = nrd; while (nr > 0 && reused(nr) == 0) { nr--; } // The cache needs to contain the tiles needed for the entire window // of the remaining axes. // If a tile is reused, we also need to take into account // the number of tiles needed for the slice. uInt64 cacheSize = 1; for (i=0; i 0) { for (i=nr+1; i<=nrd; i++) { cacheSize *= sliceTiles(i); } } if (cacheSize == validateCacheSize (cacheSize, maxCacheSize, bucketSize)) { return cacheSize; } nrd = nr; } return 1; } void TSMCube::resizeTileSections() { // Resize to dimension nrdim_p if (nrTileSection_p.nelements() != nrdim_p) { nrTileSection_p.resize(nrdim_p); startTile_p.resize(nrdim_p); endTile_p.resize(nrdim_p); startPixelInFirstTile_p.resize(nrdim_p); endPixelInFirstTile_p.resize(nrdim_p); endPixelInLastTile_p.resize(nrdim_p); } return; } void TSMCube::accessSection (const IPosition& start, const IPosition& end, char* section, uInt colnr, uInt localPixelSize, uInt, Bool writeFlag) { // Set flag if writing. if (writeFlag) { stmanPtr_p->setDataChanged(); } // Prepare for the iteration through the necessary tiles. uInt i, j; // Initialize the various variables and determine the number of // tiles needed (which will determine the cache size). // Also determine if the slice happens to be an entire slice // or if it is a line (this cases occur quite often and can be // handled in a more optimal way). Bool oneEntireTile = True; uInt lineIndex = 0; uInt nOneLong = 0; for (i=0; igetBucket (tileNr); // If writing, set cache slot to dirty. if (writeFlag) { memcpy (dataArray+pixelOffset, section, tileSize_p * localPixelSize); cachePtr->setDirty(); }else{ memcpy (section, dataArray+pixelOffset, tileSize_p * localPixelSize); } return; } // If the section is a line, call a specialized function. // Note that a single pixel is also handled as a line. if (nOneLong >= nrdim_p - 1) { accessLine (section, pixelOffset, localPixelSize, writeFlag, cachePtr, startTile_p, endTile_p(lineIndex), startPixelInFirstTile_p, endPixelInLastTile_p(lineIndex), lineIndex); return; } // At this point we start looping through all tiles. // startPixel and endPixel will contain the first and last pixels // needed in the current tile. // tilePos contains the position of the current tile. IPosition startSection (start); // start of section in cube IPosition sectionShape (end - start + 1); // section shape TSMShape expandedSectionShape (sectionShape); IPosition startPixel (startPixelInFirstTile_p); IPosition endPixel (endPixelInFirstTile_p); IPosition tilePos (startTile_p); IPosition tileIncr = expandedTilesPerDim_p.offsetIncrement (nrTileSection_p); IPosition dataLength(nrdim_p); IPosition dataPos (nrdim_p); IPosition sectionPos(nrdim_p); uInt dataOffset; size_t sectionOffset; uInt tileNr = expandedTilesPerDim_p.offset (tilePos); while (True) { // cout << "tilePos=" << tilePos << endl; // cout << "tileNr=" << tileNr << endl; // cout << "start=" << startPixel << endl; // cout << "end=" << endPixel << endl; // Get the tile from the cache. // Set it to dirty if we are writing. char* dataArray = cachePtr->getBucket (tileNr); if (writeFlag) { cachePtr->setDirty(); } // At this point we start looping through all pixels in the tile. // We do a vector at a time. // Calculate the start and end pixel in the tile. // Initialize the pixel position in the data and section. for (i=0; i // // //# Classes you should understand before using this one. //

      • TiledStMan //
      • ROTiledStManAccessor // for a discussion of the maximum cache size //
      • TSMFile //
      • BucketCache // // // TSMCube represents a hypercube in the Tiled Storage Manager. // // // TSMCube defines a tiled hypercube. The data is stored in a TSMFile // object and accessed using a BucketCache object. The hypercube can // be extensible in its last dimension to support tables with a size // which is not known in advance. //
        // Normally hypercubes share the same TSMFile object, but extensible // hypercubes have their own TSMFile object (to be extensible). // If the hypercolumn has multiple data columns, their cells share the same // tiles. Per tile data column A appears first, thereafter B, etc.. //
        // The data in the cache is held in external format and is converted // when accessed. The alternative would be to hold it in the cache in // local format and convert it when read/written from the file. It was // felt that the latter approach would generate more needless conversions. //

        // The possible id and coordinate values are stored in a Record // object. They are written in the main hypercube AipsIO file. //

        // TSMCube uses the maximum cache size set for a Tiled Storage manager. // The description of class // ROTiledStManAccessor // contains a discussion about the effect of setting the maximum cache size. // // // TSMCube encapsulates all operations on a hypercube. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class TSMCube { public: // Define the possible access types for TSMDataColumn. enum AccessType { NoAccess, CellAccess, SliceAccess, ColumnAccess, ColumnSliceAccess }; // Construct the hypercube using the given file with the given shape. // The record contains the id and possible coordinate values. //
        If the cubeshape is empty, the hypercube is still undefined and // can be added later with setShape. That is only used by TiledCellStMan. //
        The fileOffset argument is meant for class TiledFileAccess. TSMCube (TiledStMan* stman, TSMFile* file, const IPosition& cubeShape, const IPosition& tileShape, const Record& values, Int64 fileOffset, Bool useDerived = False); // Reconstruct the hypercube by reading its data from the AipsIO stream. // It will link itself to the correct TSMFile. The TSMFile objects // must have been reconstructed in advance. TSMCube (TiledStMan* stman, AipsIO& ios, Bool useDerived = False); virtual ~TSMCube(); // Forbid copy constructor. TSMCube (const TSMCube&) = delete; // Forbid assignment. TSMCube& operator= (const TSMCube&) = delete; // Flush the data in the cache. virtual void flushCache(); // Clear the cache, so data will be reread. // If wanted, the data is flushed before the cache is cleared. void clearCache (Bool doFlush = True); // Empty the cache. // It will flush the cache as needed and remove all buckets from it // resulting in a possibly large drop in memory used. // It'll also clear the userSetCache_p flag. void emptyCache(); // Show the cache statistics. virtual void showCacheStatistics (ostream& os) const; // Put the data of the object into the AipsIO stream. void putObject (AipsIO& ios); // Get the data of the object from the AipsIO stream. // It returns the data manager sequence number, which is -1 if // no file is attached to the cube (for cells without a value). Int getObject (AipsIO& ios); // Resync the object with the data file. // It reads the object, and adjusts the cache. virtual void resync (AipsIO& ios); // Is the hypercube extensible? Bool isExtensible() const; // Get the bucket size (bytes). // It is the length of a tile in external format. uInt bucketSize() const; // Get the length of a tile (in bytes) in local format. uInt localTileLength() const; // Set the hypercube shape. // This is only possible if the shape was not defined yet. virtual void setShape (const IPosition& cubeShape, const IPosition& tileShape); // Get the shape of the hypercube. const IPosition& cubeShape() const; // Get the shape of the tiles. const IPosition& tileShape() const; // Get the shape of the data cells in the cube. IPosition cellShape() const; // Get the size of a coordinate (i.e. the number of values in it). // If not defined, it returns zero. uInt coordinateSize (const String& coordinateName) const; // Get the record containing the id and coordinate values. // It is used by TSMIdColumn and TSMCoordColumn. // const Record& valueRecord() const; Record& rwValueRecord(); // // Test if the id values match. Bool matches (const PtrBlock& idColSet, const Record& idValues); // Extend the last dimension of the cube with the given number. // The record can contain the coordinates of the elements added. virtual void extend (uInt64 nr, const Record& coordValues, const TSMColumn* lastCoordColumn); // Extend the coordinates vector for the given coordinate // to the given length with the given coordValues. // It will be initialized to zero if no coordValues are given. // If the coordinate vector does not exist yet, it will be created. void extendCoordinates (const Record& coordValues, const String& coordName, uInt length); // Read or write a section in the cube. // It is assumed that the section buffer is long enough. virtual void accessSection (const IPosition& start, const IPosition& end, char* section, uInt colnr, uInt localPixelSize, uInt externalPixelSize, Bool writeFlag); // Read or write a section in a strided way. // It is assumed that the section buffer is long enough. virtual void accessStrided (const IPosition& start, const IPosition& end, const IPosition& stride, char* section, uInt colnr, uInt localPixelSize, uInt externalPixelSize, Bool writeFlag); // Get the current cache size (in buckets). uInt cacheSize() const; // Calculate the cache size (in buckets) for the given slice // and access path. // uInt calcCacheSize (const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath) const; static uInt calcCacheSize (const IPosition& cubeShape, const IPosition& tileShape, Bool extensible, const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath, uInt maxCacheSizeMiB, uInt bucketSize); // // Set the cache size for the given slice and access path. virtual void setCacheSize (const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath, Bool forceSmaller, Bool userSet); // Resize the cache object. // If forceSmaller is False, the cache will only be resized when it grows. // If the given size exceeds the maximum size with more // than 10%, the maximum size will be used. // The cacheSize has to be given in buckets. //
        The flag userSet inidicates if the cache size is set by // the user (by an Accessor object) or automatically (by TSMDataColumn). virtual void setCacheSize (uInt cacheSize, Bool forceSmaller, Bool userSet); // Validate the cache size (in buckets). // This means it will return the given cache size (in buckets) if // smaller than the maximum cache size (given in MiB). // Otherwise the maximum is returned. // uInt validateCacheSize (uInt cacheSize) const; static uInt validateCacheSize (uInt cacheSize, uInt maxSizeMiB, uInt bucketSize); // // Determine if the user set the cache size (using setCacheSize). Bool userSetCache() const; // Functions for TSMDataColumn to keep track of the last type of // access to a hypercube. It uses it to determine if the cache // has to be reset. // AccessType getLastColAccess() const; const IPosition& getLastColSlice() const; void setLastColAccess (AccessType type); void setLastColSlice (const IPosition& slice); // protected: // Initialize the various variables. // void setup(); void setupNrTiles(); // // Adjust the tile shape to the hypercube shape. // A size of 0 gets set to 1. // A tile size > cube size gets set to the cube size. IPosition adjustTileShape (const IPosition& cubeShape, const IPosition& tileShape) const; // Resize the IPosition member variables used in accessSection() // if nrdim_p changes value. void resizeTileSections(); private: // Get the cache object. // This will construct the cache object if not present yet. BucketCache* getCache(); // Construct the cache object (if not constructed yet). virtual void makeCache(); // Resync the cache object. virtual void resyncCache(); // Delete the cache object. virtual void deleteCache(); // Access a line in a more optimized way. void accessLine (char* section, uInt pixelOffset, uInt localPixelSize, Bool writeFlag, BucketCache* cachePtr, const IPosition& startTile, uInt endTile, const IPosition& startPixelInFirstTile, uInt endPixelInLastTile, uInt lineIndex); // Define the callback functions for the BucketCache. // static char* readCallBack (void* owner, const char* external); static void writeCallBack (void* owner, char* external, const char* local); static char* initCallBack (void* owner); static void deleteCallBack (void* owner, char* buffer); // // Define the functions doing the actual read and write of the // data in the tile and converting it to/from local format. // char* readTile (const char* external); void writeTile (char* external, const char* local); // protected: //# Declare member variables. char * cachedTile_p; // optimization to hold one tile chunk // Pointer to the parent storage manager. TiledStMan* stmanPtr_p; // Is the class used directly or only by a derived class only? Bool useDerived_p; // The values of the possible id and coordinate columns. Record values_p; // Is the hypercube extensible? Bool extensible_p; // Dimensionality of the hypercube. uInt nrdim_p; // Number of tiles in the hypercube. uInt nrTiles_p; // The shape of the hypercube. IPosition cubeShape_p; // The shape of the tiles in the hypercube. IPosition tileShape_p; // The number of tiles in each hypercube dimension. IPosition tilesPerDim_p; // Precomputed tileShape information. TSMShape expandedTileShape_p; // Precomputed tilesPerDim information. TSMShape expandedTilesPerDim_p; // Number of tiles in all but last dimension (used when extending). uInt nrTilesSubCube_p; // The tilesize in bytes. uInt tileSize_p; // Pointer to the TSMFile object holding the data. TSMFile* filePtr_p; // Offset in the TSMFile object where the data of this hypercube starts. Int64 fileOffset_p; // Offset for each data column in a tile (in external format). Block externalOffset_p; // Offset for each data column in a tile (in local format). Block localOffset_p; // The bucket size in bytes (is equal to tile size in bytes). uInt bucketSize_p; // The tile size in bytes in local format. uInt localTileLength_p; // The bucket cache. BucketCache* cache_p; // Did the user set the cache size? Bool userSetCache_p; // Was the last column access to a cell, slice, or column? AccessType lastColAccess_p; // The slice shape of the last column access to a slice. IPosition lastColSlice_p; // IPosition variables used in accessSection(); declared here // as member variables to avoid significant construction and // desctruction overhead if they are local to accessSection() // #tiles needed for the section IPosition nrTileSection_p; // First tile needed IPosition startTile_p; // Last tile needed IPosition endTile_p; // First pixel in first tile IPosition startPixelInFirstTile_p; // Last pixel in first tile IPosition endPixelInFirstTile_p; // Last pixel in last tile IPosition endPixelInLastTile_p; }; inline BucketCache* TSMCube::getCache() { if (cache_p == 0) { makeCache(); } return cache_p; } inline uInt TSMCube::bucketSize() const { return bucketSize_p; } inline uInt TSMCube::localTileLength() const { return localTileLength_p; } inline const IPosition& TSMCube::cubeShape() const { return cubeShape_p; } inline const IPosition& TSMCube::tileShape() const { return tileShape_p; } inline const Record& TSMCube::valueRecord() const { return values_p; } inline Record& TSMCube::rwValueRecord() { return values_p; } inline Bool TSMCube::userSetCache() const { return userSetCache_p; } inline TSMCube::AccessType TSMCube::getLastColAccess() const { return lastColAccess_p; } inline const IPosition& TSMCube::getLastColSlice() const { return lastColSlice_p; } inline void TSMCube::setLastColAccess (TSMCube::AccessType type) { lastColAccess_p = type; } inline void TSMCube::setLastColSlice (const IPosition& slice) { lastColSlice_p.resize (slice.nelements()); lastColSlice_p = slice; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/TSMCubeBuff.cc000066400000000000000000000427061476623553700204700ustar00rootroot00000000000000//# TSMCubeBuff.cc: Tiled Hypercube Storage Manager for tables using buff //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // for memcpy #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TSMCubeBuff::TSMCubeBuff (TiledStMan* stman, TSMFile* file, const IPosition& cubeShape, const IPosition& tileShape, const Record& values, Int64 fileOffset) : TSMCube (stman, file, cubeShape, tileShape, values, fileOffset, True) { // Note that the TSMCube constructor calls setShape. // However, because it is in the constructor TSMCube's setShape is called. // Hence we have to make the cache here. if (fileOffset < 0 && nrTiles_p > 0) { makeCache(); } } TSMCubeBuff::TSMCubeBuff (TiledStMan* stman, AipsIO& ios) : TSMCube (stman, ios, True) {} void TSMCubeBuff::showCacheStatistics (ostream& os) const { if (cache_p) { os << ">>> No TSMCube cache statistics (uses buffered IO)" << endl; os << "<<<" << endl; } } void TSMCubeBuff::makeCache() { // If there is no cache, make one. if (!cache_p) { cache_p.reset (new BucketBuffered (filePtr_p->bucketFile(), fileOffset_p, bucketSize_p, nrTiles_p)); } } void TSMCubeBuff::flushCache() { if (cache_p) { cache_p->flush(); } } void TSMCubeBuff::resyncCache() { if (cache_p) { cache_p->resync (nrTiles_p); } } void TSMCubeBuff::deleteCache() { cache_p.reset(); } void TSMCubeBuff::setShape (const IPosition& cubeShape, const IPosition& tileShape) { TSMCube::setShape (cubeShape, tileShape); makeCache(); } void TSMCubeBuff::extend (uInt64 nr, const Record& coordValues, const TSMColumn* lastCoordColumn) { if (!extensible_p) { throw (TSMError ("Hypercube is not extensible")); } // Make the cache here, otherwise nrTiles_p is too high. makeCache(); uInt lastDim = nrdim_p - 1; uInt nrold = nrTiles_p; cubeShape_p(lastDim) += nr; tilesPerDim_p(lastDim) = (cubeShape_p(lastDim) + tileShape_p(lastDim) - 1) / tileShape_p(lastDim); nrTiles_p = nrTilesSubCube_p * tilesPerDim_p(lastDim); // Extend the cache which extends the file and remaps. // Note that extending TSMFile only means updating its length. getCache()->extend (nrTiles_p - nrold); filePtr_p->extend ((nrTiles_p - nrold) * bucketSize_p); // Update the last coordinate (if there). if (lastCoordColumn != 0) { extendCoordinates (coordValues, lastCoordColumn->columnName(), cubeShape_p(lastDim)); } } void TSMCubeBuff::setCacheSize (uInt, Bool, Bool) {} void TSMCubeBuff::setCacheSize (const IPosition&, const IPosition&, const IPosition&, const IPosition&, Bool, Bool) {} void TSMCubeBuff::accessSection (const IPosition& start, const IPosition& end, char* section, uInt colnr, uInt localPixelSize, uInt externalPixelSize, Bool writeFlag) { // A tile can contain more than one data column. // Get the offset of the column's data array in the tile. uInt tileOffset = externalOffset_p[colnr]; // Get convert function and nr of elements per value to convert. const TSMDataColumn* dataColumn = stmanPtr_p->getDataColumn(colnr); Conversion::ValueFunction* convertFunc = dataColumn->getConvertFunction (writeFlag); uInt nrConvElem = dataColumn->getNrConvert(); // A Bool column is stored as bits and has to be treated differently. uInt dataPixelSize = externalPixelSize; Bool useBool = False; if (dataPixelSize == 0) { useBool = True; dataPixelSize = 1; } // Set flag if writing. if (writeFlag) { stmanPtr_p->setDataChanged(); } // Initialize the various variables and determine the number of // tiles needed (which will determine the cache size). // Also determine if the slice happens to be an entire tile // or if it is a line (these cases occur quite often and can be // handled in a faster way). Bool oneEntireTile = True; for (uInt i=0; igetBuffer(), section, tileSize_p*nrConvElem); cachePtr->write (tileNr, 0, bucketSize_p); } else { cachePtr->read (tileNr, 0, bucketSize_p); convertFunc (section, cachePtr->getBuffer(), tileSize_p*nrConvElem); } return; } // At this point we start looping through all tiles. // startPixel and endPixel will contain the first and last pixels // needed in the current tile. // tilePos contains the position of the current tile. IPosition startSection (start); // start of section in cube IPosition sectionShape (end - start + 1); // section shape TSMShape expandedSectionShape (sectionShape); IPosition startPixel (startPixelInFirstTile_p); IPosition endPixel (endPixelInFirstTile_p); IPosition tilePos (startTile_p); IPosition tileIncr = expandedTilesPerDim_p.offsetIncrement (nrTileSection_p); IPosition dataLength(nrdim_p); IPosition dataPos (nrdim_p); IPosition sectionPos(nrdim_p); uInt dataOffset; size_t sectionOffset; uInt tileNr = expandedTilesPerDim_p.offset (tilePos); // Loop over all tiles. while (True) { // cout << "tilePos=" << tilePos << endl; // cout << "tileNr=" << tileNr << endl; // cout << "start=" << startPixel << endl; // cout << "end=" << endPixel << endl; // At this point we start looping through all pixels in the tile. // We do a vector at a time. // Calculate the start and end pixel in the tile. // Initialize the pixel position in the data and section. // Note that for Bools it counts external in bits. for (uInt i=0; i 0 || nrvalData < 8) { cachePtr->read (tileNr, offset, 1); } if (nBytes > 1 && (stBit+nrvalData) % 8 != 0) { cachePtr->read (tileNr, offset+nBytes-1, 1, nBytes-1); } for (uInt n = 0; n < nData; n += nSec) { Conversion::boolToBit(cachePtr->getBuffer() + (stBit + n)/8, section+sectionOffset, (stBit + n) % 8, nrvalSec); sectionOffset += localSize; for (uInt j = secDim; j < dataDim; j++) { sectionOffset += sectionIncr(j); if (++dataPos(j) <= endPixel(j)) { break; } dataPos(j) = startPixel(j); } } cachePtr->write (tileNr, offset, nBytes); } else { cachePtr->read (tileNr, offset, nBytes); for (uInt n = 0; n < nData; n += nSec) { Conversion::bitToBool(section+sectionOffset, cachePtr->getBuffer() + (stBit + n)/8, (stBit + n) % 8, nrvalSec); sectionOffset += localSize; for (uInt j = secDim; j < dataDim; j++) { sectionOffset += sectionIncr(j); if (++dataPos(j) <= endPixel(j)) { break; } dataPos(j) = startPixel(j); } } } dataOffset += dataSize; uInt j; for (j=dataDim; jgetBuffer() + n*dataPixelSize, section+sectionOffset, nrvalSec); sectionOffset += localSize; for (uInt j = secDim; j < dataDim; j++) { sectionOffset += sectionIncr(j); if (++dataPos(j) <= endPixel(j)) { break; } dataPos(j) = startPixel(j); } } cachePtr->write (tileNr, dataOffset, dataSize); } else { cachePtr->read (tileNr, dataOffset, dataSize); for (uInt n = 0; n < nData; n += nSec) { convertFunc (section+sectionOffset, cachePtr->getBuffer() + n*dataPixelSize, nrvalSec); sectionOffset += localSize; for (uInt j = secDim; j < dataDim; j++) { sectionOffset += sectionIncr(j); if (++dataPos(j) <= endPixel(j)) { break; } dataPos(j) = startPixel(j); } } } dataOffset += dataSize; uInt j; for (j=dataDim; j fullArr(fullShape); Array partArr = fullArr(fst, fend, incr); Array sectArr(sectShape, section, SHARE); // Read the data of the full array. // Thereafter copy the part needed. accessSection (start, end, fullArr.data(), colnr, localPixelSize, externalPixelSize, False); if (writeFlag) { partArr = sectArr; accessSection (start, end, fullArr.data(), colnr, localPixelSize, externalPixelSize, True); } else { sectArr = partArr; } } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/TSMCubeBuff.h000066400000000000000000000164661476623553700203360ustar00rootroot00000000000000//# TSMCubeBuff.h: Tiled hypercube in a table //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TSMCUBEBUFF_H #define TABLES_TSMCUBEBUFF_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations class BucketBuffered; //

        // Tiled hypercube in a table // // // // // //# Classes you should understand before using this one. //
      • TiledStMan //
      • ROTiledStManAccessor // for a discussion of the maximum cache size //
      • TSMFile //
      • BucketBuffered // // // TSMCubeBuff represents a hypercube in the Tiled Storage Manager. // // // TSMCubeBuff defines a tiled hypercube. The data is stored in a TSMFile // object and accessed using a BucketBuffered object. The hypercube can // be extensible in its last dimension to support tables with a size // which is not known in advance. //
        // Normally hypercubes share the same TSMFile object, but extensible // hypercubes have their own TSMFile object (to be extensible). // If the hypercolumn has multiple data columns, their cells share the same // tiles. Per tile data column A appears first, thereafter B, etc.. //
        // The data in the cache is held in external format and is converted // when accessed. The alternative would be to hold it in the cache in // local format and convert it when read/written from the file. It was // felt that the latter approach would generate more needless conversions. //

        // The possible id and coordinate values are stored in a Record // object. They are written in the main hypercube AipsIO file. //

        // TSMCubeBuff uses the maximum cache size set for a Tiled Storage manager. // The description of class // ROTiledStManAccessor // contains a discussion about the effect of setting the maximum cache size. // // // TSMCubeBuff encapsulates all operations on a hypercube. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class TSMCubeBuff: public TSMCube { public: // Construct the hypercube using the given file with the given shape. // The record contains the id and possible coordinate values. //
        If the cubeshape is empty, the hypercube is still undefined and // can be added later with setShape. That is only used by TiledCellStMan. //
        The fileOffset argument is meant for class TiledFileAccess. TSMCubeBuff (TiledStMan* stman, TSMFile* file, const IPosition& cubeShape, const IPosition& tileShape, const Record& values, Int64 fileOffset); // Reconstruct the hypercube by reading its data from the AipsIO stream. // It will link itself to the correct TSMFile. The TSMFile objects // must have been reconstructed in advance. TSMCubeBuff (TiledStMan* stman, AipsIO& ios); ~TSMCubeBuff() override = default; // Forbid copy constructor. TSMCubeBuff (const TSMCubeBuff&) = delete; // Forbid assignment. TSMCubeBuff& operator= (const TSMCubeBuff&) = delete; // Flush the data in the cache. virtual void flushCache() override; // Show the cache statistics. void showCacheStatistics (ostream& os) const override; // Set the hypercube shape. // This is only possible if the shape was not defined yet. void setShape (const IPosition& cubeShape, const IPosition& tileShape) override; // Extend the last dimension of the cube with the given number. // The record can contain the coordinates of the elements added. void extend (uInt64 nr, const Record& coordValues, const TSMColumn* lastCoordColumn) override; // Read or write a section in the cube. // It is assumed that the section buffer is long enough. void accessSection (const IPosition& start, const IPosition& end, char* section, uInt colnr, uInt localPixelSize, uInt externalPixelSize, Bool writeFlag) override; // Read or write a section in a strided way. // It is assumed that the section buffer is long enough. void accessStrided (const IPosition& start, const IPosition& end, const IPosition& stride, char* section, uInt colnr, uInt localPixelSize, uInt externalPixelSize, Bool writeFlag) override; // Set the cache size for the given slice and access path. void setCacheSize (const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath, Bool forceSmaller, Bool userSet) override; // Resize the cache object. // If forceSmaller is False, the cache will only be resized when it grows. // If the given size exceeds the maximum size with more // than 10%, the maximum size will be used. // The cacheSize has to be given in buckets. //
        The flag userSet inidicates if the cache size is set by // the user (by an Accessor object) or automatically (by TSMDataColumn). void setCacheSize (uInt cacheSize, Bool forceSmaller, Bool userSet) override; private: // Get the cache object. // This will construct the cache object if not present yet. BucketBuffered* getCache(); // Construct the cache object (if not constructed yet). void makeCache() override; // Resync the cache object. void resyncCache() override; // Delete the cache object. void deleteCache() override; //# Declare member variables. // The bucket cache. std::unique_ptr cache_p; }; inline BucketBuffered* TSMCubeBuff::getCache() { if (!cache_p) { makeCache(); } return cache_p.get(); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/TSMCubeMMap.cc000066400000000000000000000372451476623553700204420ustar00rootroot00000000000000//# TSMCubeMMap.cc: Tiled Hypercube Storage Manager for tables using mmap //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // for memcpy #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TSMCubeMMap::TSMCubeMMap (TiledStMan* stman, TSMFile* file, const IPosition& cubeShape, const IPosition& tileShape, const Record& values, Int64 fileOffset) : TSMCube (stman, file, cubeShape, tileShape, values, fileOffset, True), cache_p (0) { // Note that the TSMCube constructor can call setShape. // However, because it is in the constructor TSMCube's setShape is called. // Hence we have to make the cache here. if (fileOffset < 0 && nrTiles_p > 0) { makeCache(); } } TSMCubeMMap::TSMCubeMMap (TiledStMan* stman, AipsIO& ios) : TSMCube (stman, ios, True), cache_p (0) {} TSMCubeMMap::~TSMCubeMMap() { delete cache_p; } void TSMCubeMMap::showCacheStatistics (ostream& os) const { if (cache_p != 0) { os << ">>> No TSMCube cache statistics (uses mmap)" << endl; os << "<<<" << endl; } } void TSMCubeMMap::makeCache() { // If there is no cache, make one. if (cache_p == 0) { cache_p = new BucketMapped (filePtr_p->bucketFile(), fileOffset_p, bucketSize_p, nrTiles_p); } } void TSMCubeMMap::flushCache() { if (cache_p != 0) { cache_p->flush(); } } void TSMCubeMMap::resyncCache() { if (cache_p != 0) { cache_p->resync (nrTiles_p); } } void TSMCubeMMap::deleteCache() { delete cache_p; cache_p = 0; } void TSMCubeMMap::setShape (const IPosition& cubeShape, const IPosition& tileShape) { TSMCube::setShape (cubeShape, tileShape); makeCache(); } void TSMCubeMMap::extend (uInt64 nr, const Record& coordValues, const TSMColumn* lastCoordColumn) { if (!extensible_p) { throw TSMError ("Hypercube in TSM " + stmanPtr_p->dataManagerName() + " is not extensible"); } // Make the cache here, otherwise nrTiles_p is too high. makeCache(); uInt lastDim = nrdim_p - 1; uInt nrold = nrTiles_p; cubeShape_p(lastDim) += nr; tilesPerDim_p(lastDim) = (cubeShape_p(lastDim) + tileShape_p(lastDim) - 1) / tileShape_p(lastDim); nrTiles_p = nrTilesSubCube_p * tilesPerDim_p(lastDim); // Extend the cache which extends the file and remaps. // Note that extending TSMFile only means updating its length. getCache()->extend (nrTiles_p - nrold); filePtr_p->extend ((nrTiles_p - nrold) * bucketSize_p); // Update the last coordinate (if there). if (lastCoordColumn != 0) { extendCoordinates (coordValues, lastCoordColumn->columnName(), cubeShape_p(lastDim)); } } void TSMCubeMMap::setCacheSize (uInt, Bool, Bool) {} void TSMCubeMMap::setCacheSize (const IPosition&, const IPosition&, const IPosition&, const IPosition&, Bool, Bool) {} void TSMCubeMMap::accessSection (const IPosition& start, const IPosition& end, char* section, uInt colnr, uInt localPixelSize, uInt externalPixelSize, Bool writeFlag) { // A tile can contain more than one data column. // Get the offset of the column's data array in the tile. uInt tileOffset = externalOffset_p[colnr]; // Get convert function and nr of elements per value to convert. const TSMDataColumn* dataColumn = stmanPtr_p->getDataColumn(colnr); Conversion::ValueFunction* convertFunc = dataColumn->getConvertFunction (writeFlag); uInt nrConvElem = dataColumn->getNrConvert(); // Conversion (or memcpy) is necessary if different byte order or if // not aligned properly. // If not needed, it is possible to use assignment because for smaller arrays // it is faster than memcpy. // Not done yet; might be in future. /// Bool mustConvert = dataColumn->isConversionNeeded(); /// if (!mustConvert) { /// if (tileOffset % sizeof(int) != 0 /// || tileSize_p % sizeof(int) != 0 /// || localPixelSize % sizeof(int) != 0) { /// mustConvert = True; /// } /// } // A Bool column is stored as bits and has to be treated differently. uInt dataPixelSize = externalPixelSize; Bool useBool = False; if (dataPixelSize == 0) { useBool = True; dataPixelSize = 1; } // Set flag if writing. if (writeFlag) { stmanPtr_p->setDataChanged(); } // Initialize the various variables and determine the number of // tiles needed (which will determine the cache size). // Also determine if the slice happens to be an entire tile // or if it is a line (these cases occur quite often and can be // handled in a faster way). Bool oneEntireTile = True; for (uInt i=0; igetrwBucket (tileNr); convertFunc (dataArray+tileOffset, section, tileSize_p*nrConvElem); }else{ const char* dataArray = cachePtr->getBucket (tileNr); convertFunc (const_cast(section), dataArray+tileOffset, tileSize_p*nrConvElem); } return; } // At this point we start looping through all tiles. // startPixel and endPixel will contain the first and last pixels // needed in the current tile. // tilePos contains the position of the current tile. IPosition startSection (start); // start of section in cube IPosition sectionShape (end - start + 1); // section shape TSMShape expandedSectionShape (sectionShape); IPosition startPixel (startPixelInFirstTile_p); IPosition endPixel (endPixelInFirstTile_p); IPosition tilePos (startTile_p); IPosition tileIncr = expandedTilesPerDim_p.offsetIncrement (nrTileSection_p); IPosition dataLength(nrdim_p); IPosition dataPos (nrdim_p); IPosition sectionPos(nrdim_p); uInt dataOffset; size_t sectionOffset; uInt tileNr = expandedTilesPerDim_p.offset (tilePos); // Loop over all tiles. while (True) { // cout << "tilePos=" << tilePos << endl; // cout << "tileNr=" << tileNr << endl; // cout << "start=" << startPixel << endl; // cout << "end=" << endPixel << endl; // At this point we start looping through all pixels in the tile. // We do a vector at a time. // Calculate the start and end pixel in the tile. // Initialize the pixel position in the data and section. // Note that for Bools it counts external in bits. for (uInt i=0; igetrwBucket (tileNr); } else { dataArray = const_cast(cachePtr->getBucket (tileNr)); } // Determine the byte to start with. dataArray += tileOffset + dataOffset/8; // Determine the bit to start with. dataOffset %= 8; while (True) { if (writeFlag) { Conversion::boolToBit (dataArray, section+sectionOffset, dataOffset, nrval); }else{ Conversion::bitToBool (section+sectionOffset, dataArray, dataOffset, nrval); } dataOffset += dataSize; sectionOffset += localSize; uInt j; for (j=secDim; jgetrwBucket (tileNr); } else { dataArray = const_cast(cachePtr->getBucket (tileNr)); } // Determine the byte to start with. dataArray += tileOffset; dataOffset *= dataPixelSize; while (True) { if (writeFlag) { convertFunc (dataArray+dataOffset, section+sectionOffset, nrval); }else{ convertFunc (section+sectionOffset, dataArray+dataOffset, nrval); } dataOffset += dataSize; sectionOffset += localSize; uInt j; for (j=secDim; j fullArr(fullShape); Array partArr = fullArr(fst, fend, incr); Array sectArr(sectShape, section, SHARE); // Read the data of the full array. // Thereafter copy the part needed. accessSection (start, end, fullArr.data(), colnr, localPixelSize, externalPixelSize, False); if (writeFlag) { partArr = sectArr; accessSection (start, end, fullArr.data(), colnr, localPixelSize, externalPixelSize, True); } else { sectArr = partArr; } } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/TSMCubeMMap.h000066400000000000000000000165611476623553700203020ustar00rootroot00000000000000//# TSMCubeMMap.h: Tiled hypercube in a table //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TSMCUBEMMAP_H #define TABLES_TSMCUBEMMAP_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations class BucketMapped; //

        // Tiled hypercube in a table // // // // // //# Classes you should understand before using this one. //
      • TiledStMan //
      • ROTiledStManAccessor // for a discussion of the maximum cache size //
      • TSMFile //
      • BucketMapped // // // TSMCubeMMap represents a hypercube in the Tiled Storage Manager. // // // TSMCubeMMap defines a tiled hypercube. The data is stored in a TSMFile // object and accessed using a BucketMapped object. The hypercube can // be extensible in its last dimension to support tables with a size // which is not known in advance. //
        // Normally hypercubes share the same TSMFile object, but extensible // hypercubes have their own TSMFile object (to be extensible). // If the hypercolumn has multiple data columns, their cells share the same // tiles. Per tile data column A appears first, thereafter B, etc.. //
        // The data in the cache is held in external format and is converted // when accessed. The alternative would be to hold it in the cache in // local format and convert it when read/written from the file. It was // felt that the latter approach would generate more needless conversions. //

        // The possible id and coordinate values are stored in a Record // object. They are written in the main hypercube AipsIO file. //

        // TSMCubeMMap uses the maximum cache size set for a Tiled Storage manager. // The description of class // ROTiledStManAccessor // contains a discussion about the effect of setting the maximum cache size. // // // TSMCubeMMap encapsulates all operations on a hypercube. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class TSMCubeMMap: public TSMCube { public: // Construct the hypercube using the given file with the given shape. // The record contains the id and possible coordinate values. //
        If the cubeshape is empty, the hypercube is still undefined and // can be added later with setShape. That is only used by TiledCellStMan. //
        The fileOffset argument is meant for class TiledFileAccess. TSMCubeMMap (TiledStMan* stman, TSMFile* file, const IPosition& cubeShape, const IPosition& tileShape, const Record& values, Int64 fileOffset); // Reconstruct the hypercube by reading its data from the AipsIO stream. // It will link itself to the correct TSMFile. The TSMFile objects // must have been reconstructed in advance. TSMCubeMMap (TiledStMan* stman, AipsIO& ios); virtual ~TSMCubeMMap(); // Forbid copy constructor. TSMCubeMMap (const TSMCubeMMap&) = delete; // Forbid assignment. TSMCubeMMap& operator= (const TSMCubeMMap&) = delete; // Flush the data in the cache. virtual void flushCache(); // Show the cache statistics. virtual void showCacheStatistics (ostream& os) const; // Set the hypercube shape. // This is only possible if the shape was not defined yet. virtual void setShape (const IPosition& cubeShape, const IPosition& tileShape); // Extend the last dimension of the cube with the given number. // The record can contain the coordinates of the elements added. virtual void extend (uInt64 nr, const Record& coordValues, const TSMColumn* lastCoordColumn); // Read or write a section in the cube. // It is assumed that the section buffer is long enough. virtual void accessSection (const IPosition& start, const IPosition& end, char* section, uInt colnr, uInt localPixelSize, uInt externalPixelSize, Bool writeFlag); // Read or write a section in a strided way. // It is assumed that the section buffer is long enough. virtual void accessStrided (const IPosition& start, const IPosition& end, const IPosition& stride, char* section, uInt colnr, uInt localPixelSize, uInt externalPixelSize, Bool writeFlag); // Set the cache size for the given slice and access path. virtual void setCacheSize (const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath, Bool forceSmaller, Bool userSet); // Resize the cache object. // If forceSmaller is False, the cache will only be resized when it grows. // If the given size exceeds the maximum size with more // than 10%, the maximum size will be used. // The cacheSize has to be given in buckets. //
        The flag userSet inidicates if the cache size is set by // the user (by an Accessor object) or automatically (by TSMDataColumn). virtual void setCacheSize (uInt cacheSize, Bool forceSmaller, Bool userSet); private: // Get the cache object. // This will construct the cache object if not present yet. BucketMapped* getCache(); // Construct the cache object (if not constructed yet). virtual void makeCache(); // Resync the cache object. virtual void resyncCache(); // Delete the cache object. virtual void deleteCache(); //# Declare member variables. // The bucket cache. BucketMapped* cache_p; }; inline BucketMapped* TSMCubeMMap::getCache() { if (cache_p == 0) { makeCache(); } return cache_p; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/TSMDataColumn.cc000066400000000000000000000550431476623553700210340ustar00rootroot00000000000000//# TSMDataColumn.cc: Tiled Hypercube Storage Manager for data columns //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TSMDataColumn::TSMDataColumn (const TSMColumn& column) : TSMColumn (column) { DataType dt = DataType(dataType()); localPixelSize_p = ValType::getTypeSize (dt); convPixelSize_p = 1; if (dt == TpBool) { readFunc_p = &Conversion::bitToBool; writeFunc_p = &Conversion::boolToBit; tilePixelSize_p = 0; mustConvert_p = True; }else{ Bool asBigEndian = stmanPtr_p->asBigEndian(); ValType::getCanonicalFunc (dt, readFunc_p, writeFunc_p, convPixelSize_p, asBigEndian); tilePixelSize_p = ValType::getCanonicalSize (dt, asBigEndian); mustConvert_p = localPixelSize_p > 1 && asBigEndian != HostInfo::bigEndian(); } } TSMDataColumn::~TSMDataColumn() {} uInt64 TSMDataColumn::dataLength (uInt64 nrPixels) const { // For Bools a byte can hold 8 pixels. if (tilePixelSize_p == 0) { return (nrPixels + 7) / 8; } return tilePixelSize_p * nrPixels; } Bool TSMDataColumn::canChangeShape() const { // This storage manager can handle changing array shapes // for non-FixedShape columns. if (shapeColumn().nelements() != 0) { return False; } return stmanPtr_p->canChangeShape(); } void TSMDataColumn::setShape (rownr_t rownr, const IPosition& shape) { setShapeTiled (rownr, shape, stmanPtr_p->defaultTileShape()); } void TSMDataColumn::setShapeTiled (rownr_t rownr, const IPosition& shape, const IPosition& tileShape) { TSMCube* hypercube = stmanPtr_p->getHypercube (rownr); IPosition cubeShape = hypercube->cubeShape(); if (cubeShape.nelements() == 0) { stmanPtr_p->setShape (rownr, hypercube, shape, tileShape); }else{ Bool eq = True; for (uInt i=0; isetShape (rownr, hypercube, shape, tileShape); hypercube->setLastColAccess (TSMCube::NoAccess); } } } Bool TSMDataColumn::isShapeDefined (rownr_t rownr) { //# The shape is defined when the shape is fixed or when //# a hypercube has been defined for this row. if (shapeColumn().nelements() != 0) { return True; // FixedShape } TSMCube* hypercube = stmanPtr_p->getHypercube (rownr); return (hypercube->cubeShape().nelements() != 0); } IPosition TSMDataColumn::shape (rownr_t rownr) { //# Return the shape when it is fixed. if (shapeColumn().nelements() != 0) { return shapeColumn(); } //# When no shape is defined, throw exception. TSMCube* hypercube = stmanPtr_p->getHypercube (rownr); IPosition cubeShape = hypercube->cubeShape(); if (cubeShape.nelements() == 0) { throw (DataManInvOper ("TSM: no array in row " + String::toString(rownr) + " of column " + columnName() + " in "+ stmanPtr_p->fileName())); } IPosition shape (stmanPtr_p->nrCoordVector()); for (uInt i=0; igetHypercube (rownr); return hypercube->tileShape(); } void TSMDataColumn::accessCell (rownr_t rownr, const void* dataPtr, Bool writeFlag) { // Get the hypercube the row is in. // It also gives the position of the row in the hypercube. IPosition end; TSMCube* hypercube = stmanPtr_p->getHypercube (rownr, end); IPosition start (end); for (uInt i=0; inrCoordVector(); i++) { start(i) = 0; end(i)--; } // Size the cache if the user has not done it and if the // last access was not to a cell. if (hypercube->getLastColAccess() != TSMCube::CellAccess) { if (! stmanPtr_p->userSetCache (rownr)) { hypercube->setCacheSize (1 + end - start, IPosition(), IPosition(), IPosition(), True, False); hypercube->setLastColAccess (TSMCube::CellAccess); } } hypercube->accessSection (start, end, (char*)dataPtr, colnr_p, localPixelSize_p, tilePixelSize_p, writeFlag); } void TSMDataColumn::accessCellSlice (rownr_t rownr, const Slicer& ns, const void* dataPtr, Bool writeFlag) { IPosition end; TSMCube* hypercube = stmanPtr_p->getHypercube (rownr, end); IPosition endcp (end); IPosition start (end); IPosition stride (end.nelements(), 1); IPosition blc, trc, inc; IPosition slice = ns.inferShapeFromSource (shape(rownr), blc, trc, inc); // Set the correct start and end of the slice in the hypercube. // A slice of the data cell is accessed, thus copy the Slicer // info for the vector coordinates. for (uInt i=0; inrCoordVector(); i++) { start(i) = blc(i); end(i) = trc(i); stride(i) = inc(i); } // Size the cache if the user has not done it // and if the access type or slice shape differs. if (hypercube->getLastColAccess() != TSMCube::SliceAccess || ! slice.isEqual (hypercube->getLastColSlice())) { if (! stmanPtr_p->userSetCache (rownr)) { // The main access path is assumed to be along the full slice // dimensions. uInt naxis = 0; IPosition axisPath (end.nelements()); for (uInt i=0; inrCoordVector(); i++) { if (blc(i) == 0 && trc(i) == endcp(i)) { axisPath(naxis++) = i; } } axisPath.resize (naxis); hypercube->setCacheSize (1 + end - start, IPosition(), IPosition(), axisPath, True, False); hypercube->setLastColAccess (TSMCube::SliceAccess); hypercube->setLastColSlice (slice); } } hypercube->accessStrided (start, end, stride, (char*)dataPtr, colnr_p, localPixelSize_p, tilePixelSize_p, writeFlag); } void TSMDataColumn::accessColumn (const void* dataPtr, Bool writeFlag) { // Get the single hypercube and the shape of the hypercube. TSMCube* hypercube = stmanPtr_p->singleHypercube(); IPosition end (hypercube->cubeShape()); end -= 1; IPosition start (end.nelements(), 0); // Size the cache if the user has not done it. if (! stmanPtr_p->userSetCache (0)) { hypercube->setCacheSize (end + 1, IPosition(), IPosition(), IPosition(), True, False); hypercube->setLastColAccess (TSMCube::ColumnAccess); } hypercube->accessSection (start, end, (char*)dataPtr, colnr_p, localPixelSize_p, tilePixelSize_p, writeFlag); } void TSMDataColumn::accessColumnSlice (const Slicer& ns, const void* dataPtr, Bool writeFlag) { // Get the single hypercube and the shape of the hypercube. TSMCube* hypercube = stmanPtr_p->singleHypercube(); IPosition end (hypercube->cubeShape()); end -= 1; IPosition endcp (end); IPosition start (end.nelements(), 0); IPosition stride (end.nelements(), 1); IPosition blc, trc, inc; IPosition slice = ns.inferShapeFromSource (shape(0), blc, trc, inc); // Set the correct start and end of the slice in the hypercube. // The entire column is accessed, thus all scalar coordinates. // A slice of each data cell is accessed, thus copy the Slicer // info for the vector coordinates. for (uInt i=0; inrCoordVector(); i++) { start(i) = blc(i); end(i) = trc(i); stride(i) = inc(i); } // Size the cache if the user has not done it // and if the access type or slice shape differs. if (hypercube->getLastColAccess() != TSMCube::ColumnSliceAccess || ! slice.isEqual (hypercube->getLastColSlice())) { if (! stmanPtr_p->userSetCache (0)) { // The main access path is assumed to be along the full slice // dimensions. uInt naxis = 0; IPosition axisPath (end.nelements()); for (uInt i=0; inrCoordVector(); i++) { if (blc(i) == 0 && trc(i) == endcp(i)) { axisPath(naxis++) = i; } } // The further access path is along the trailing axes. for (uInt i=stmanPtr_p->nrCoordVector(); isetCacheSize (1 + end - start, IPosition(), IPosition(), axisPath, True, False); hypercube->setLastColAccess (TSMCube::ColumnSliceAccess); hypercube->setLastColSlice (slice); } } hypercube->accessStrided (start, end, stride, (char*)dataPtr, colnr_p, localPixelSize_p, tilePixelSize_p, writeFlag); } void TSMDataColumn::accessColumnCells (const RefRows& rownrs, const IPosition& arrShape, const void* dataPtr, Bool writeFlag) { char* data = (char*)(dataPtr); uInt lastAxis = arrShape.nelements() - 1; IPosition cellShape = arrShape.getFirst (lastAxis); uInt64 chunkSize = arrShape.product() / arrShape(lastAxis) * localPixelSize_p; uInt64 nrinc = 0; Int64 lastRowPos = 0; TSMCube* lastCube = 0; IPosition rowpos; IPosition start(lastAxis+1); IPosition end(lastAxis+1); IPosition incr(lastAxis+1); // Step through all rownr intervals and all rownrs in each interval. RefRowsSliceIter iter(rownrs); while (! iter.pastEnd()) { rownr_t rownr = iter.sliceStart(); rownr_t erow = iter.sliceEnd(); rownr_t irow = iter.sliceIncr(); while (rownr <= erow) { // Get the hypercube and the position of the row in it. // A read has to be done if we have another hypercube // or if the rownr is not higher. TSMCube* hypercube = stmanPtr_p->getHypercube (rownr, rowpos); Int64 hcRowPos = rowpos(lastAxis); Bool doIt = False; if (hypercube != lastCube || hcRowPos <= lastRowPos) { doIt = True; } else { // The same hypercube. Check if the stride is the same. // The first time the stride has to be determined. if (nrinc == 0) { incr(lastAxis) = hcRowPos - end(lastAxis); } else { if (hcRowPos - end(lastAxis) != incr(lastAxis)) { doIt = True; } } } if (doIt) { AlwaysAssert (hypercube, AipsError); if (lastCube != 0) { accessFullCells (lastCube, data, writeFlag, start, end, incr); data += (nrinc+1) * chunkSize; } else { for (uInt i=0; icubeShape().getFirst (lastAxis); if (! cellShape.isEqual (hcShape)) { throw DataManError("getArrayColumnCells shape mismatch in column " + columnName()); } } } else { end(lastAxis) = hcRowPos; nrinc++; } lastRowPos = hcRowPos; rownr += irow; } iter++; } if (lastCube != 0) { accessFullCells (lastCube, data, writeFlag, start, end, incr); } } void TSMDataColumn::accessColumnSliceCells (const RefRows& rownrs, const Slicer& ns, const IPosition& arrShape, const void* dataPtr, Bool writeFlag) { char* data = (char*)(dataPtr); uInt lastAxis = arrShape.nelements() - 1; uInt64 chunkSize = arrShape.product() / arrShape(lastAxis) * localPixelSize_p; uInt64 nrinc = 0; Int64 lastRowPos = 0; TSMCube* lastCube = 0; IPosition rowpos; IPosition start(lastAxis+1); IPosition end(lastAxis+1); IPosition incr(lastAxis+1); // Step through all rownr intervals and all rownrs in each interval. RefRowsSliceIter iter(rownrs); while (! iter.pastEnd()) { rownr_t rownr = iter.sliceStart(); rownr_t erow = iter.sliceEnd(); rownr_t irow = iter.sliceIncr(); while (rownr <= erow) { // Get the hypercube and the position of the row in it. // A read has to be done if we have another hypercube // or if the rownr is not higher. TSMCube* hypercube = stmanPtr_p->getHypercube (rownr, rowpos); Int64 hcRowPos = rowpos(lastAxis); Bool doIt = False; if (hypercube != lastCube || hcRowPos <= lastRowPos) { doIt = True; } else { // The same hypercube. Check if the stride is the same. // The first time the stride has to be determined. if (nrinc == 0) { incr(lastAxis) = hcRowPos - end(lastAxis); } else { if (hcRowPos - end(lastAxis) != incr(lastAxis)) { doIt = True; } } } if (doIt) { if (lastCube != 0) { accessSlicedCells (lastCube, data, writeFlag, start, end, incr); data += (nrinc+1) * chunkSize; } else { IPosition blc, trc, inc; ns.inferShapeFromSource (shape(rownr), blc, trc, inc); for (uInt i=0; iuserSetCache (0)) { if (hypercube->getLastColAccess() != TSMCube::ColumnAccess) { hypercube->setCacheSize (hypercube->cubeShape(), IPosition(), IPosition(), IPosition(), True, False); hypercube->setLastColAccess (TSMCube::ColumnAccess); } } hypercube->accessStrided (start, end, incr, dataPtr, colnr_p, localPixelSize_p, tilePixelSize_p, writeFlag); } void TSMDataColumn::accessSlicedCells (TSMCube* hypercube, char* dataPtr, Bool writeFlag, const IPosition& start, const IPosition& end, const IPosition& incr) { // cout << "accessSlicedCells " << start << end << incr << endl; // Size the cache if the user has not done it. if (! stmanPtr_p->userSetCache (0)) { // The main access path is assumed to be along the full slice // dimensions. uInt naxis = 0; IPosition axisPath (end.nelements()); IPosition sliceShp = hypercube->cubeShape(); for (uInt i=0; inrCoordVector(); i++) { if (start(i) == 0 && end(i) == sliceShp(i)-1) { axisPath(naxis++) = i; } sliceShp(i) = 1 + end(i) - start(i); } // Set only if a different slice is accessed. if (hypercube->getLastColAccess() != TSMCube::ColumnSliceAccess || ! sliceShp.isEqual (hypercube->getLastColSlice())) { // The further access path is along the trailing axes. for (uInt i=stmanPtr_p->nrCoordVector(); isetCacheSize (sliceShp, IPosition(), IPosition(), axisPath, True, False); hypercube->setLastColAccess (TSMCube::ColumnSliceAccess); hypercube->setLastColSlice (sliceShp); } } hypercube->accessStrided (start, end, incr, dataPtr, colnr_p, localPixelSize_p, tilePixelSize_p, writeFlag); } void TSMDataColumn::getfloat (rownr_t rownr, float* dataPtr) { accessCell (rownr, dataPtr, False); } void TSMDataColumn::putfloat (rownr_t rownr, const float* dataPtr) { accessCell (rownr, dataPtr, True); } void TSMDataColumn::getArrayV (rownr_t rownr, ArrayBase& dataPtr) { Bool deleteIt; void* data = dataPtr.getVStorage (deleteIt); accessCell (rownr, data, False); dataPtr.putVStorage (data, deleteIt); } void TSMDataColumn::putArrayV (rownr_t rownr, const ArrayBase& dataPtr) { Bool deleteIt; const void* data = dataPtr.getVStorage (deleteIt); accessCell (rownr, data, True); dataPtr.freeVStorage (data, deleteIt); } void TSMDataColumn::getSliceV (rownr_t rownr, const Slicer& ns, ArrayBase& dataPtr) { Bool deleteIt; void* data = dataPtr.getVStorage (deleteIt); accessCellSlice (rownr, ns, data, False); dataPtr.putVStorage (data, deleteIt); } void TSMDataColumn::putSliceV (rownr_t rownr, const Slicer& ns, const ArrayBase& dataPtr) { Bool deleteIt; const void* data = dataPtr.getVStorage (deleteIt); accessCellSlice (rownr, ns, data, True); dataPtr.freeVStorage (data, deleteIt); } void TSMDataColumn::getArrayColumnV (ArrayBase& dataPtr) { if (! stmanPtr_p->canAccessColumn()) { getArrayColumnBase (dataPtr); } else { Bool deleteIt; void* data = dataPtr.getVStorage (deleteIt); accessColumn (data, False); dataPtr.putVStorage (data, deleteIt); } } void TSMDataColumn::putArrayColumnV (const ArrayBase& dataPtr) { if (! stmanPtr_p->canAccessColumn()) { putArrayColumnBase (dataPtr); } else { Bool deleteIt; const void* data = dataPtr.getVStorage (deleteIt); accessColumn (data, True); dataPtr.freeVStorage (data, deleteIt); } } void TSMDataColumn::getColumnSliceV (const Slicer& ns, ArrayBase& dataPtr) { if (! stmanPtr_p->canAccessColumn()) { getColumnSliceBase (ns, dataPtr); } else { Bool deleteIt; void* data = dataPtr.getVStorage (deleteIt); accessColumnSlice (ns, data, False); dataPtr.putVStorage (data, deleteIt); } } void TSMDataColumn::putColumnSliceV (const Slicer& ns, const ArrayBase& dataPtr) { if (! stmanPtr_p->canAccessColumn()) { putColumnSliceBase (ns, dataPtr); } else { Bool deleteIt; const void* data = dataPtr.getVStorage (deleteIt); accessColumnSlice (ns, data, True); dataPtr.freeVStorage (data, deleteIt); } } void TSMDataColumn::getArrayColumnCellsV (const RefRows& rownrs, ArrayBase& dataPtr) { // Only use optimized accessColumnCells for hypercubes where the rows // are mapped to a single axis. if (dataPtr.ndim() == stmanPtr_p->nrCoordVector() + 1) { Bool deleteIt; void* data = dataPtr.getVStorage (deleteIt); accessColumnCells (rownrs, dataPtr.shape(), data, False); dataPtr.putVStorage (data, deleteIt); } else { getArrayColumnCellsBase (rownrs, dataPtr); } } void TSMDataColumn::putArrayColumnCellsV (const RefRows& rownrs, const ArrayBase& dataPtr) { // Only use optimized accessColumnCells for hypercubes where the rows // are mapped to a single axis. if (dataPtr.ndim() == stmanPtr_p->nrCoordVector() + 1) { Bool deleteIt; const void* data = dataPtr.getVStorage (deleteIt); accessColumnCells (rownrs, dataPtr.shape(), data, True); dataPtr.freeVStorage (data, deleteIt); } else { putArrayColumnCellsBase (rownrs, dataPtr); } } void TSMDataColumn::getColumnSliceCellsV (const RefRows& rownrs, const Slicer& ns, ArrayBase& dataPtr) { // Only use optimized accessColumnSliceCells for hypercubes where the rows // are mapped to a single axis. if (dataPtr.ndim() == stmanPtr_p->nrCoordVector() + 1) { Bool deleteIt; void* data = dataPtr.getVStorage (deleteIt); accessColumnSliceCells (rownrs, ns, dataPtr.shape(), data, False); dataPtr.putVStorage (data, deleteIt); } else { getColumnSliceCellsBase (rownrs, ns, dataPtr); } } void TSMDataColumn::putColumnSliceCellsV (const RefRows& rownrs, const Slicer& ns, const ArrayBase& dataPtr) { // Only use optimized accessColumnSliceCells for hypercubes where the rows // are mapped to a single axis. if (dataPtr.ndim() == stmanPtr_p->nrCoordVector() + 1) { Bool deleteIt; const void* data = dataPtr.getVStorage (deleteIt); accessColumnSliceCells (rownrs, ns, dataPtr.shape(), data, True); dataPtr.freeVStorage (data, deleteIt); } else { putColumnSliceCellsBase (rownrs, ns, dataPtr); } } #define TSMDATACOLUMN_GETPUT(T) \ void TSMDataColumn::aips_name2(get,T) (rownr_t rownr, T* dataPtr) \ { \ accessCell (rownr, dataPtr, False); \ } \ void TSMDataColumn::aips_name2(put,T) (rownr_t rownr, const T* dataPtr) \ { \ accessCell (rownr, dataPtr, True); \ } TSMDATACOLUMN_GETPUT(Bool) TSMDATACOLUMN_GETPUT(uChar) TSMDATACOLUMN_GETPUT(Short) TSMDATACOLUMN_GETPUT(uShort) TSMDATACOLUMN_GETPUT(Int) TSMDATACOLUMN_GETPUT(uInt) TSMDATACOLUMN_GETPUT(Int64) //#TSMDATACOLUMN_GETPUT(float) TSMDATACOLUMN_GETPUT(double) TSMDATACOLUMN_GETPUT(Complex) TSMDATACOLUMN_GETPUT(DComplex) } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/TSMDataColumn.h000066400000000000000000000336241476623553700206770ustar00rootroot00000000000000//# TSMDataColumn.h: A data column in Tiled Storage Manager //# Copyright (C) 1995,1996,1997,1999,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TSMDATACOLUMN_H #define TABLES_TSMDATACOLUMN_H //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class Slicer; //

        // A data column in Tiled Storage Manager. // // // // // //# Classes you should understand before using this one. //
      • TSMColumn //
      • TSMCube // // // TSMDataColumn handles a data column for a Tiled // Storage Manager. // // // TSMDataColumn is used by // TiledStMan // to handle the access to // a table column containing data of a tiled hypercube axis. // The data in a cell can be a scalar or an array (depending on its // definition in the table column description). // The shapes of the coordinates and the data are related. Therefore // the function setShape checks if the data shape matches the coordinates // shape. //

        // The data are held in a TSMCube object. The row number // determines which TSMCube object has to be accessed. //

        // The creation of a TSMDataColumn object is done by a TSMColumn object. // This process is described in more detail in the class // TSMColumn. // // // Handling data columns in the Tiled Storage Manager is // different from other columns. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class TSMDataColumn : public TSMColumn { public: // Create a data column from the given column. TSMDataColumn (const TSMColumn& column); // Frees up the storage. virtual ~TSMDataColumn(); // Forbid copy constructor. TSMDataColumn (const TSMDataColumn&) = delete; // Forbid assignment. TSMDataColumn& operator= (const TSMDataColumn&) = delete; // Return the size of a pixel in the tile in external format. uInt tilePixelSize() const; // Return the size of a pixel in the tile in local format. uInt localPixelSize() const; // Determine the length to store the given number of pixels. uInt64 dataLength (uInt64 nrPixels) const; // Set column sequence number. void setColumnNumber (uInt colnr); // Changing array shapes for non-FixedShape columns when the // parent tiled storage manager can handle it. Bool canChangeShape() const; // Set the shape of the data array in the given row. // It will check if it matches already defined data and coordinates shapes. // It will define undefined data and coordinates shapes. void setShape (rownr_t rownr, const IPosition& shape); // Set the shape and tile shape of the array in the given row. // It will check if it matches already defined data and coordinates shapes. // It will define undefined data and coordinates shapes. // The tile shape is adjusted to the array shape (size 0 gets set to 1; // size > cubesize gets set to the cubesize). void setShapeTiled (rownr_t rownr, const IPosition& shape, const IPosition& tileShape); // Is the value shape defined in the given row? Bool isShapeDefined (rownr_t rownr); // Get the shape of the item in the given row. IPosition shape (rownr_t rownr); // Get the tile shape of the item in the given row. IPosition tileShape (rownr_t rownr); // Get a scalar value in the given row. // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the Scalar/ArrayColumn get function). // virtual void getBool (rownr_t rownr, Bool* dataPtr); virtual void getuChar (rownr_t rownr, uChar* dataPtr); virtual void getShort (rownr_t rownr, Short* dataPtr); virtual void getuShort (rownr_t rownr, uShort* dataPtr); virtual void getInt (rownr_t rownr, Int* dataPtr); virtual void getuInt (rownr_t rownr, uInt* dataPtr); virtual void getInt64 (rownr_t rownr, Int64* dataPtr); virtual void getfloat (rownr_t rownr, float* dataPtr); virtual void getdouble (rownr_t rownr, double* dataPtr); virtual void getComplex (rownr_t rownr, Complex* dataPtr); virtual void getDComplex (rownr_t rownr, DComplex* dataPtr); // // Put a scalar value into the given row. // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the Scalar/ArrayColumn put function). // virtual void putBool (rownr_t rownr, const Bool* dataPtr); virtual void putuChar (rownr_t rownr, const uChar* dataPtr); virtual void putShort (rownr_t rownr, const Short* dataPtr); virtual void putuShort (rownr_t rownr, const uShort* dataPtr); virtual void putInt (rownr_t rownr, const Int* dataPtr); virtual void putuInt (rownr_t rownr, const uInt* dataPtr); virtual void putInt64 (rownr_t rownr, const Int64* dataPtr); virtual void putfloat (rownr_t rownr, const float* dataPtr); virtual void putdouble (rownr_t rownr, const double* dataPtr); virtual void putComplex (rownr_t rownr, const Complex* dataPtr); virtual void putDComplex (rownr_t rownr, const DComplex* dataPtr); // // Get the array value in the given row. // The array given in data has to have the correct shape // (which is guaranteed by the ArrayColumn get function). virtual void getArrayV (rownr_t rownr, ArrayBase& data); // Put the array value into the given row. // The array given in data has to have the correct shape // (which is guaranteed by the ArrayColumn put function). virtual void putArrayV (rownr_t rownr, const ArrayBase& data); // Get into a section of the array in the given row. // The array given in data has to have the correct shape // (which is guaranteed by the ArrayColumn putSlice function). virtual void getSliceV (rownr_t rownr, const Slicer& slicer, ArrayBase& dataPtr); // Put into a section of the array in the given row. // The array given in data has to have the correct shape // (which is guaranteed by the ArrayColumn putSlice function). virtual void putSliceV (rownr_t rownr, const Slicer& slicer, const ArrayBase& data); // Get all array values in the column. // The array given in data has to have the correct shape // (which is guaranteed by the ArrayColumn getColumn function). virtual void getArrayColumnV (ArrayBase& arr); // Put all array values in the column. // The array given in data has to have the correct shape // (which is guaranteed by the ArrayColumn getColumn function). virtual void putArrayColumnV (const ArrayBase& arr); // Get the array values in some cells of the column. // The array given in data has to have the correct shape // (which is guaranteed by the ArrayColumn getColumnCells function). virtual void getArrayColumnCellsV (const RefRows& rownrs, ArrayBase& data); // Put the array values into some cells of the column. // The array given in data has to have the correct shape // (which is guaranteed by the ArrayColumn getColumn function). virtual void putArrayColumnCellsV (const RefRows& rownrs, const ArrayBase& dataPtr); // Get a section of all arrays in the column. // The array given in data has to have the correct shape // (which is guaranteed by the ArrayColumn getColumn function). virtual void getColumnSliceV (const Slicer& slicer, ArrayBase& arr); // Put a section into all array values in the column. // The array given in data has to have the correct shape // (which is guaranteed by the ArrayColumn getColumn function). virtual void putColumnSliceV (const Slicer& slicer, const ArrayBase& data); // Get a section from some cells of the column. // The array given in data has to have the correct shape // (which is guaranteed by the ArrayColumn getColumnCells function). virtual void getColumnSliceCellsV (const RefRows& rownrs, const Slicer& ns, ArrayBase& data); // Put into a section of some cells of the column. // The array given in data has to have the correct shape // (which is guaranteed by the ArrayColumn putColumnSlice function). virtual void putColumnSliceCellsV (const RefRows& rownrs, const Slicer& ns, const ArrayBase& data); // Read the data of the column from a tile. // (I.e. convert from external to local format). void readTile (void* to, const void* from, uInt nrPixels); // Write the data of the column into a tile. // (I.e. convert from local to external format). void writeTile (void* to, const void* from, uInt nrPixels); // Get the function to convert from external to local format // (or vice-versa if writeFlag=True). Conversion::ValueFunction* getConvertFunction (Bool writeFlag) const { return writeFlag ? writeFunc_p : readFunc_p; } // Get nr of elements in a value to convert (usually 1, but 2 for Complex). size_t getNrConvert() const { return convPixelSize_p; } // Does a conversion (byte swap) needs to be done? Bool isConversionNeeded() const { return mustConvert_p; } private: // The (canonical) size of a pixel in a tile. uInt tilePixelSize_p; // The local size of a pixel. uInt localPixelSize_p; // The multiplication factor for a conversion operation. // This is the pixel size when a memcpy can be used, otherwise it is 1. uInt convPixelSize_p; // Is a conversion necessary? Bool mustConvert_p; // The column sequence number. uInt colnr_p; // The conversion function needed when reading. Conversion::ValueFunction* readFunc_p; // The conversion function needed when writing. Conversion::ValueFunction* writeFunc_p; // Read or write a data cell in the cube. // A cell can contain a scalar or an array (depending on the // column definition). void accessCell (rownr_t rownr, const void* dataPtr, Bool writeFlag); // Read or write a slice of a data cell in the cube. void accessCellSlice (rownr_t rownr, const Slicer& ns, const void* dataPtr, Bool writeFlag); // Read or write an entire column. // This can only be done if one hypercube is used. void accessColumn (const void* dataPtr, Bool writeFlag); // Read or write a slice from the entire column. // This can only be done if one hypercube is used. void accessColumnSlice (const Slicer& ns, const void* dataPtr, Bool writeFlag); // Read or write some cells in a column. // It tries to optimize by looking for regular row strides. void accessColumnCells (const RefRows& rownrs, const IPosition& shape, const void* dataPtr, Bool writeFlag); // Read or write some cells in a column. // It tries to optimize by looking for regular row strides. void accessColumnSliceCells (const RefRows& rownrs, const Slicer& ns, const IPosition& shape, const void* dataPtr, Bool writeFlag); // Read or write the full cells given by start,end,incr. void accessFullCells (TSMCube* hypercube, char* dataPtr, Bool writeFlag, const IPosition& start, const IPosition& end, const IPosition& incr); // Read or write the sliced cells given by start,end,incr. void accessSlicedCells (TSMCube* hypercube, char* dataPtr, Bool writeFlag, const IPosition& start, const IPosition& end, const IPosition& incr); }; inline uInt TSMDataColumn::tilePixelSize() const { return tilePixelSize_p; } inline uInt TSMDataColumn::localPixelSize() const { return localPixelSize_p; } inline void TSMDataColumn::setColumnNumber (uInt colnr) { colnr_p = colnr; } inline void TSMDataColumn::readTile (void* to, const void* from, uInt nrPixels) { readFunc_p (to, from, nrPixels * convPixelSize_p); } inline void TSMDataColumn::writeTile (void* to, const void* from, uInt nrPixels) { writeFunc_p (to, from, nrPixels * convPixelSize_p); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/TSMFile.cc000066400000000000000000000101721476623553700176560ustar00rootroot00000000000000//# TSMFile.cc: Tiled Hypercube Storage Manager for tables //# Copyright (C) 1995,1996,1997,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include // for snprintf namespace casacore { //# NAMESPACE CASACORE - BEGIN TSMFile::TSMFile (const TiledStMan* stman, uInt fileSequenceNr, const TSMOption& tsmOpt, const std::shared_ptr& mfile) : fileSeqnr_p (fileSequenceNr), file_p (0), length_p (0) { // Create the file. char strc[8]; snprintf (strc, sizeof(strc), "_TSM%i", fileSeqnr_p); String fileName = stman->fileName() + strc; Bool mapOpt = tsmOpt.option() == TSMOption::MMap; uInt bufSize = 0; if (tsmOpt.option() == TSMOption::Buffer) { bufSize = tsmOpt.bufferSize(); } file_p = new BucketFile (fileName, bufSize, mapOpt, mfile); } TSMFile::TSMFile (const String& fileName, Bool writable, const TSMOption& tsmOpt, const std::shared_ptr& mfile) : fileSeqnr_p (0), file_p (0), length_p (0) { // Create the file. Bool mapOpt = tsmOpt.option() == TSMOption::MMap; uInt bufSize = 0; if (tsmOpt.option() == TSMOption::Buffer) { bufSize = tsmOpt.bufferSize(); } file_p = new BucketFile (fileName, writable, bufSize, mapOpt, mfile); } TSMFile::TSMFile (const TiledStMan* stman, AipsIO& ios, uInt seqnr, const TSMOption& tsmOpt, const std::shared_ptr& mfile) : file_p (0) { getObject (ios); if (seqnr != fileSeqnr_p) { throw DataManInternalError ("TSMFile::TSMFile " + stman->dataManagerName()); } char strc[8]; snprintf (strc, sizeof(strc), "_TSM%i", fileSeqnr_p); String fileName = stman->fileName() + strc; Bool mapOpt = tsmOpt.option() == TSMOption::MMap; uInt bufSize = 0; if (tsmOpt.option() == TSMOption::Buffer) { bufSize = tsmOpt.bufferSize(); } file_p = new BucketFile (fileName, stman->table().isWritable(), bufSize, mapOpt, mfile); } TSMFile::~TSMFile() { delete file_p; } void TSMFile::putObject (AipsIO& ios) const { // Take care of forward compatibility (for small enough files). uInt version = (length_p < 2u*1024u*1024u*1024u ? 1 : 2); ios << version; ios << fileSeqnr_p; if (version == 1) { uInt len = length_p; ios << len; } else { ios << length_p; } } void TSMFile::getObject (AipsIO& ios) { uInt version; ios >> version; ios >> fileSeqnr_p; if (version == 1) { uInt len; ios >> len; length_p = len; } else { ios >> length_p; } } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/TSMFile.h000066400000000000000000000111711476623553700175200ustar00rootroot00000000000000//# TSMFile.h: File object for Tiled Storage Manager //# Copyright (C) 1995,1996,1997,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TSMFILE_H #define TABLES_TSMFILE_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TSMOption; class TiledStMan; class MultiFileBase; class AipsIO; //

        // File object for Tiled Storage Manager. // // // // // //# Classes you should understand before using this one. //
      • TiledStMan // // // TSMFile represents a data file for the Tiled Storage Manager. // // // A TSMFile object represents a data file. Currently it is meant // for the TiledStMan classes, but it can easily be turned into // a more general storage manager file class. //
        // Creation of a TSMFile object does not open the file. // An explicit open call has to be given before the file can be used. //

        // Underneath it uses a BucketFile to access the file. // In this way the IO details are well encapsulated. // // // Encapsulate the Tiled Storage Manager file details. // //# //# class TSMFile { public: // Create a TSMFile object (with corresponding file). // The sequence number gets part of the file name. TSMFile (const TiledStMan* stMan, uInt fileSequenceNr, const TSMOption&, const std::shared_ptr& = std::shared_ptr()); // Create a TSMFile object for the given existing file. TSMFile (const String& fileName, Bool writable, const TSMOption&, const std::shared_ptr& = std::shared_ptr()); // Read the object back. // The file is not opened until the first access, // thus until the file descriptor is asked for the first time. // It checks if the sequence number matches the expected one. TSMFile (const TiledStMan* stMan, AipsIO& ios, uInt seqnr, const TSMOption&, const std::shared_ptr& = std::shared_ptr()); // The destructor closes the file. ~TSMFile(); // Forbid copy constructor. TSMFile (const TSMFile&) = delete; // Forbid assignment. TSMFile& operator= (const TSMFile&) = delete; // Write the object. void putObject (AipsIO& ios) const; // Get the object. void getObject (AipsIO& ios); // Open the file if not open yet. void open(); // Return the BucketFile object (to be used in the BucketCache). BucketFile* bucketFile(); // Return the logical file length. Int64 length() const; // Return the file sequence number. uInt sequenceNumber() const; // Increment the logical file length. void extend (Int64 increment); private: // The file sequence number. uInt fileSeqnr_p; // The file object. BucketFile* file_p; // The (logical) length of the file. Int64 length_p; }; inline Int64 TSMFile::length() const { return length_p; } inline uInt TSMFile::sequenceNumber() const { return fileSeqnr_p; } inline void TSMFile::extend (Int64 increment) { length_p += increment; } inline BucketFile* TSMFile::bucketFile() { return file_p; } inline void TSMFile::open() { file_p->open(); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/TSMIdColumn.cc000066400000000000000000000060611476623553700205130ustar00rootroot00000000000000//# TSMIdColumn.cc: Tiled Hypercube Storage Manager for id columns //# Copyright (C) 1995,1996,1997,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TSMIdColumn::TSMIdColumn (const TSMColumn& column) : TSMColumn (column) {} TSMIdColumn::~TSMIdColumn() {} void TSMIdColumn::getfloat (rownr_t rownr, float* dataPtr) { // Get the hypercube the row is in. TSMCube* hypercube = stmanPtr_p->getHypercube (rownr); hypercube->valueRecord().get (columnName(), *dataPtr); } void TSMIdColumn::putfloat (rownr_t rownr, const float* dataPtr) { float value; TSMIdColumn::getfloat (rownr, &value); if (value != *dataPtr) { throw TSMError ("TSMIdColumn::put: new value mismatches existing " "in id column " + columnName()); } } #define TSMIDCOLUMN_GETPUT(T) \ void TSMIdColumn::aips_name2(get,T) (rownr_t rownr, T* dataPtr) \ { \ TSMCube* hypercube = stmanPtr_p->getHypercube (rownr); \ hypercube->valueRecord().get (columnName(), *dataPtr); \ } \ void TSMIdColumn::aips_name2(put,T) (rownr_t rownr, const T* dataPtr) \ { \ T value; \ TSMIdColumn::aips_name2(get,T) (rownr, &value); \ if (value != *dataPtr) { \ throw TSMError ("TSMIdColumn::put: new value mismatches existing" \ " in id column " + columnName()); \ } \ } TSMIDCOLUMN_GETPUT(Bool) TSMIDCOLUMN_GETPUT(Int) TSMIDCOLUMN_GETPUT(uInt) TSMIDCOLUMN_GETPUT(Int64) //#TSMIDCOLUMN_GETPUT(float) TSMIDCOLUMN_GETPUT(double) TSMIDCOLUMN_GETPUT(Complex) TSMIDCOLUMN_GETPUT(DComplex) TSMIDCOLUMN_GETPUT(String) } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/TSMIdColumn.h000066400000000000000000000121341476623553700203530ustar00rootroot00000000000000//# TSMIdColumn.h: An id column in Tiled Storage Manager //# Copyright (C) 1995,1996,1997,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TSMIDCOLUMN_H #define TABLES_TSMIDCOLUMN_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations //

        // An id column in Tiled Storage Manager. // // // // // //# Classes you should understand before using this one. //
      • TSMColumn //
      • TSMCube //
      • Record // // // TSMIdColumn handles an id column for a Tiled // Storage Manager. // // // TSMIdColumn is used by // TiledStMan // to handle the access to // a table column containing an id value of a tiled hypercube. // Explicitly putting an id value is not possible. The only way to // define the value is by specifying it when adding a hypercube // in TiledDataStMan. //

        // The id values are held in a TSMCube object. The row number // determines which TSMCube object has to be accessed. //

        // The creation of a TSMIdColumn object is done by a TSMColumn object. // This process is described in more detail in the class // TSMColumn. // // // Handling coordinate columns in the Tiled Storage Manager is // different from other columns. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class TSMIdColumn : public TSMColumn { public: // Create an id column from the given column. TSMIdColumn (const TSMColumn& column); // Frees up the storage. virtual ~TSMIdColumn(); // Forbid copy constructor. TSMIdColumn (const TSMIdColumn&) = delete; // Forbid assignment. TSMIdColumn& operator= (const TSMIdColumn&) = delete; // Get a scalar value in the given row. // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the ScalarColumn get function). // virtual void getBool (rownr_t rownr, Bool* dataPtr); virtual void getInt (rownr_t rownr, Int* dataPtr); virtual void getuInt (rownr_t rownr, uInt* dataPtr); virtual void getInt64 (rownr_t rownr, Int64* dataPtr); virtual void getfloat (rownr_t rownr, float* dataPtr); virtual void getdouble (rownr_t rownr, double* dataPtr); virtual void getComplex (rownr_t rownr, Complex* dataPtr); virtual void getDComplex (rownr_t rownr, DComplex* dataPtr); virtual void getString (rownr_t rownr, String* dataPtr); // // Put a scalar value in the given row. // The buffer pointed to by dataPtr has to have the correct length // (which is guaranteed by the ScalarColumn get function). // The value to be put must match the value which has already // been inserted by the TiledStMan::addHypercube function. // The put function is only there to be fully orthogonal. // virtual void putBool (rownr_t rownr, const Bool* dataPtr); virtual void putInt (rownr_t rownr, const Int* dataPtr); virtual void putuInt (rownr_t rownr, const uInt* dataPtr); virtual void putInt64 (rownr_t rownr, const Int64* dataPtr); virtual void putfloat (rownr_t rownr, const float* dataPtr); virtual void putdouble (rownr_t rownr, const double* dataPtr); virtual void putComplex (rownr_t rownr, const Complex* dataPtr); virtual void putDComplex (rownr_t rownr, const DComplex* dataPtr); virtual void putString (rownr_t rownr, const String* dataPtr); // }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/TSMOption.cc000066400000000000000000000056771476623553700202650ustar00rootroot00000000000000//# TSMOption.cc: Options for the Tiled Storage Manager Access //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have receied a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TSMOption::TSMOption (TSMOption::Option option, Int bufferSize, Int maxCacheSizeMB) : itsOption (option), itsBufferSize (bufferSize), itsMaxCacheSize (maxCacheSizeMB) {} void TSMOption::fillOption (Bool newTable) { // Get variables from aipsrc if needed. if (itsOption == TSMOption::Aipsrc) { String opt; AipsrcValue::find (opt, "table.tsm.option", "cache"); opt.downcase(); if (opt == "map" || opt == "mmap") { itsOption = TSMOption::MMap; } else if (opt == "cache") { itsOption = TSMOption::Cache; /// } else if (opt == "buffer") { /// itsOption = TSMOption::Buffer; } else if (opt == "default32") { itsOption = (newTable ? TSMOption::Cache : TSMOption::MMap); } else { itsOption = TSMOption::Default; } } // Default buffer size is 4096. if (itsBufferSize <= -2) { AipsrcValue::find (itsBufferSize, "table.tsm.buffersize", 0); } if (itsBufferSize <= 0) { itsBufferSize = 4096; } // Default is -1. if (itsMaxCacheSize <= -2) { AipsrcValue::find (itsMaxCacheSize, "table.tsm.maxcachesizemb", -1); } // Default is to use the old caching behaviour // Abandoned default to use mmap for existing files on 64 bit systems. if (itsOption == TSMOption::Default) { itsOption = TSMOption::Cache; ///#ifdef AIPS_64B /// if (!newTable) { /// itsOption = TSMOption::MMap; /// } ///#endif } } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/TSMOption.h000066400000000000000000000140011476623553700201040ustar00rootroot00000000000000//# TSMOption.h: Options for the Tiled Storage Manager Access //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have receied a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TSMOPTION_H #define TABLES_TSMOPTION_H //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //

        // Options for the Tiled Storage Manager Access // // // // // //# Classes you should understand before using this one. //
      • TiledStMan // // // This class can be used to define how the Tiled Storage Manager accesses // its data. There are three ways: //
          //
        1. Using a cache of its own. The cache size is derived using the hinted // access pattern. The cache can be (too) large when using large tables // with bad access patterns. // A maximum cache size can be defined to overcome this problem, but // that may result in poor caching behaviour. // Until January 2010 this was the only way to access the data. //
        2. Use memory-mapped IO (mmap); the operating system take care of caching. // On 32-bit systems mmap cannot be used for larger tables due to the // 4 GB address space limit. // When creating or extending files, mmap can be disadvantageous because // extending the file requires remapping it. //
        3. Use buffered IO; the kernel's file cache should avoid unnecessary IO. // Its performance is less than mmap, but it works well on 32-bit systems. // The buffer size to be used can be defined. //
        // // The constructor of the class can be used to define the options or // to read options from the aipsrc file. //
          //
        • TSMOption::Cache // Use unbuffered file IO with internal TSM caching. This is the old // behaviour. // The maximum cache size can be given as a constructor argument. //
        • TSMOption::MMap // Use memory-mapped IO. //
        • TSMOption::Buffer // Use buffered file IO without. // The buffer size can be given as a constructor argument. //
        • TSMOption::Default // Use default. This is MMap for existing files on 64-bit systems, // otherwise Buffer. //
        • TSMOption::Aipsrc // Use the option as defined in the aipsrc file. //
        // The aipsrc variables are: //
          //
        • table.tsm.option gives the option as the case-insensitive // string value: //
            //
          • cache means TSMCache. //
          • mmap (or map) means TSMMap. //
          • mmapold (or mapold) means TSMMap for existing // tables and TSMDefault for new tables. //
          • buffer means TSMBuffer. //
          • default means TSMDefault. //
          // It defaults to value default. // Note that mmapold is almost the same as default. // Only on 32-bit systems it is different. //
        • table.tsm.maxcachesizemb gives the maximum cache size in // MibiByte for option TSMOption::Cache. A value -1 means // that the system determines the maximum. A value 0 means unlimited. // It defaults to -1. // Note it can always be overridden using class ROTiledStManAccessor. //
        • table.tsm.buffersize gives the buffer size for option // TSMOption::Buffer. A value <=0 means use the default 4096. // It defaults to 0. //
        //
        class TSMOption { public: // Define the possible options how the TiledStMan accesses its data. enum Option { // Use unbuffered file IO with internal TSM caching. Cache, // Use buffered file IO without internal TSM caching. Buffer, // Use memory-mapped IO. MMap, // Use default. Default, // Use as defined in the aipsrc file. Aipsrc }; // Create an option object. // The parameter values are described in the synopsis. // A size value -2 means reading that size from the aipsrc file. // The buffer size has to be given in bytes. // The maximum cache size has to be given in MibiBytes (1024*1024 bytes). TSMOption (Option option=Aipsrc, Int bufferSize=-2, Int maxCacheSizeMB=-2); // Fill the option in case Aipsrc or Default was given. // It is done as explained in the synopsis. void fillOption (Bool newFile); // Get the option. Option option() const { return itsOption; } // Get the buffer size. Int bufferSize() const { return itsBufferSize; } // Get the maximum cache size (in MibiByte). -1 means undefined. Int maxCacheSizeMB() const { return itsMaxCacheSize; } private: Option itsOption; Int itsBufferSize; Int itsMaxCacheSize; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/TSMShape.cc000066400000000000000000000104511476623553700200370ustar00rootroot00000000000000//# TSMShape.cc: A vector of integers, used to index into arrays. //# Copyright (C) 1994,1995,1996,1997 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TSMShape::TSMShape() : data_p (), size_p (0) {} TSMShape::TSMShape (const IPosition& shape) : data_p (shape.nelements()), size_p (shape.nelements()) { if (size_p > 0) { data_p(0) = 1; for (uInt i=1; i 0) { for (uInt i=size_p-1; i>0; i--) { pos(i) = offset / data_p(i); offset -= pos(i) * data_p(i); } pos(0) = offset; } return pos; } IPosition TSMShape::position (size_t offset, const IPosition& origin) const { if (size_p != origin.nelements()) { throw (ArrayConformanceError( "TSMShape::position - shapes do not conform")); } IPosition pos(size_p); if (size_p > 0) { for (uInt i=size_p-1; i>0; i--) { pos(i) = offset / data_p(i); offset -= pos(i) * data_p(i); pos(i) += origin(i); } pos(0) = offset + origin(0); } return pos; } IPosition TSMShape::offsetIncrement (const IPosition& subShape) const { if (size_p != subShape.nelements()) { throw (ArrayConformanceError( "TSMShape::offsetIncrement - shapes do not conform")); } IPosition incr(size_p,1); for (uInt i=1; i #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // Expanded IPosition for shapes. // // // // // //# Classes you should understand before using this one. //
      • IPosition // // // TSMShape handles the shapes for the Tiled Storage Manager. // // // TSMShape is an extension of class // IPosition // to handle shapes. // It contains some precalculated values to speed up the calculation // of an array offset from an array index (and vice-versa). // // // The Tiled Hypercube Storage Manager is heavily using array shapes // and determining offsets from array indices. This class makes these // calculations more efficient. // // //
      • Integrate in a class like LatticeLayout. // class TSMShape { public: // A zero-length TSMShape. TSMShape(); // Construct from a shape and precalculate some values. TSMShape (const IPosition& shape); // Copy constructor (copy semantics). TSMShape (const TSMShape& that); // Assignment (copy semantics). // "this" and "that" must either be conformant (same size) // or "this" must be 0-length, in which case it will // resize itself to be the same length as "that". TSMShape& operator= (const TSMShape& that); ~TSMShape(); // Index into the TSMShape. Indices are zero-based. If the preprocessor // symbol AIPS_ARRAY_INDEX_CHECK is defined, "index" will be // checked to ensure it is not out of bounds. If this check fails, an // AipsError will be thrown. Int operator() (uInt index) const; // The number of elements in this TSMShape. Since TSMShape // objects use zero-based indexing, the maximum available index is // nelements() - 1. uInt nelements() const; // conform returns true if nelements() == other.nelements(). Bool conform (const TSMShape& other) const; // Calculate the offset for a given position. // size_t offset (const IPosition& position) const; size_t offset (const IPosition& position, const IPosition& origin) const; // // Calculate the position for a given offset. // IPosition position (size_t offset) const; IPosition position (size_t offset, const IPosition& origin) const; // // Calculate the increments when stepping through an array in // a linear way. This can be used to update the array offset // without recalculating it after each step. // For example: // // template // Array someFunc (const Array& array, // const IPosition& subArrayShape, // const IPosition& subArrayStart) const // { // TSMShape TSM (array.shape()); // IPosition offsetIncr = TSM.offsetIncrement (subArrayShape); // Array subArray(subArrayShape); // Bool deleteMain; // const T* mainData = array.getStorage (deleteMain); // mainData += TSM.offset (subArrayStart) // Bool deleteSub; // T* subData = subArray.getStorage (deleteSub); // for (uInt i=0; i // IPosition offsetIncrement (const IPosition& subShape) const; IPosition offsetIncrement (const IPosition& subShape, const IPosition& stride) const; // private: IPosition data_p; uInt size_p; //# Not necessary, but done for speedup }; inline uInt TSMShape::nelements() const { return size_p; } inline Int TSMShape::operator()(uInt index) const { return data_p(index); } inline Bool TSMShape::conform (const TSMShape& other) const { return data_p.conform (other.data_p); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/TiledCellStMan.cc000066400000000000000000000154541476623553700212270ustar00rootroot00000000000000//# TiledCellStMan.cc: Storage manager for tables using tiled hypercubes //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TiledCellStMan::TiledCellStMan () : TiledStMan () {} TiledCellStMan::TiledCellStMan (const String& hypercolumnName, const IPosition& defaultTileShape, uInt64 maximumCacheSize) : TiledStMan (hypercolumnName, maximumCacheSize), defaultTileShape_p (defaultTileShape) {} TiledCellStMan::TiledCellStMan (const String& hypercolumnName, const Record& spec) : TiledStMan (hypercolumnName, 0) { if (spec.isDefined ("DEFAULTTILESHAPE")) { defaultTileShape_p = IPosition (spec.toArrayInt ("DEFAULTTILESHAPE")); } if (spec.isDefined ("MAXIMUMCACHESIZE")) { setPersMaxCacheSize (spec.asInt64 ("MAXIMUMCACHESIZE")); } } TiledCellStMan::~TiledCellStMan() {} DataManager* TiledCellStMan::clone() const { TiledCellStMan* smp = new TiledCellStMan (hypercolumnName_p, defaultTileShape_p, maximumCacheSize()); return smp; } DataManager* TiledCellStMan::makeObject (const String& group, const Record& spec) { TiledCellStMan* smp = new TiledCellStMan (group, spec); return smp; } String TiledCellStMan::dataManagerType() const { return "TiledCellStMan"; } IPosition TiledCellStMan::defaultTileShape() const { return defaultTileShape_p; } Bool TiledCellStMan::canChangeShape() const { return True; } void TiledCellStMan::setShape (rownr_t, TSMCube* hypercube, const IPosition& shape, const IPosition& tileShape) { hypercube->setShape (shape, tileShape); } void TiledCellStMan::setupCheck (const TableDesc& tableDesc, const Vector& dataNames) const { // The data columns should only contain arrays matching the // dimensionality of the hypercolumn. for (uInt i=0; i 0) { throw TSMError("ID columns cannot be used with TiledCellStMan"); } } void TiledCellStMan::create64 (rownr_t nrrow) { // Set up the various things. setup(0); // Create the one and single TSMFile object. createFile (0); // Add the rows for the given number of rows. addRow64 (nrrow); } Bool TiledCellStMan::flush (AipsIO&, Bool fsync) { // Flush the caches. // Exit if nothing has changed. if (! flushCaches (fsync)) { return False; } // Create the header file and write data in it. // A zero pointer is returned when nothing has changed, thus nothing // has to be written. AipsIO* headerFile = headerFileCreate(); if (headerFile == 0) { return False; } headerFile->putstart ("TiledCellStMan", 1); *headerFile << defaultTileShape_p; // Let the base class write its data. headerFilePut (*headerFile, nrrow_p); headerFile->putend(); headerFileClose (headerFile); return True; } void TiledCellStMan::readHeader (rownr_t tabNrrow, Bool firstTime) { // Open the header file and read data from it. AipsIO* headerFile = headerFileOpen(); headerFile->getstart ("TiledCellStMan"); *headerFile >> defaultTileShape_p; // Let the base class read and initialize its data. headerFileGet (*headerFile, tabNrrow, firstTime, 0); headerFile->getend(); headerFileClose (headerFile); } void TiledCellStMan::addRow64 (rownr_t nrow) { // Resize block when needed. uInt64 size = cubeSet_p.nelements(); if (size < nrrow_p + nrow) { size += 32; if (size < nrrow_p + nrow) { size = nrrow_p + nrow; } cubeSet_p.resize (size); for (uInt64 i=nrrow_p; i 0) { hypercube->setShape (fixedCellShape_p, defaultTileShape_p); } } nrrow_p += nrow; setDataChanged(); } TSMCube* TiledCellStMan::getHypercube (rownr_t rownr) { // Check if the row number is correct. if (rownr >= nrrow_p) { throw (TSMError ("getHypercube: rownr is too high")); } return cubeSet_p[rownr]; } TSMCube* TiledCellStMan::getHypercube (rownr_t rownr, IPosition& position) { // Check if the row number is correct. if (rownr >= nrrow_p) { throw (TSMError ("getHypercube: rownr is too high")); } TSMCube* hypercube = cubeSet_p[rownr]; position.resize (0); position = hypercube->cubeShape(); return hypercube; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/TiledCellStMan.h000066400000000000000000000225101476623553700210600ustar00rootroot00000000000000//# TiledCellStMan.h: Tiled Cell Storage Manager //# Copyright (C) 1995,1996,1997,1998,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TILEDCELLSTMAN_H #define TABLES_TILEDCELLSTMAN_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // Tiled Cell Storage Manager. // // // // // //# Classes you should understand before using this one. //
      • TiledStMan //
      • TSMCube //
      • ROTiledStManAccessor // for a discussion of the maximum cache size // // // TiledCellStMan is the Tiled Storage Manager storing // each cell as a separate hypercube. // // // TiledCellStMan is a derivation from TiledStMan, the abstract // tiled storage manager class. A description of the basics // of tiled storage managers is given in the // Tables module description. //

        // TiledCellStMan allows the user to create a tiled hypercube for // each data cell in an automatic way. It is meant to be used for // storing regularly shaped data like images (where the table contains // a possibly differently shaped image in each row). //

        // The TiledCellStMan has the following (extra) properties: //

          //
        • Addition of a row results in the addition of a hypercube in // which the data cells in that row will be stored. Thus each row // of the hypercolumn is stored in its own hypercube. // Note that a hypercolumn has a given dimensionality, so each // data cell in the hypercolumn has to match that dimensionality. //
        • Although there are multiple hypercubes, an id value is not needed. // The row number serves as the id value. //
        • Coordinates for the hypercubes can be defined and (of course) // their shapes have to match the hypercube shape. // Their values have to be put explicitly (so it is not possible // to define them via an addHypercube call like in // TiledDataStMan). //
        • It is possible to define a (default) tile shape in the // TiledCellStMan constructor. When setting the shape of the // array in a row (using // ArrayColumn::setShape), it is possible to override // that default for the hypercube in this particular row. //
        //
        // // This tiled storage manager does not require any special action // (like calling add/extendHypercube) when used with a column // containing variable shaped arrays. // // // // // Define the table description and the columns in it. // TableDesc td ("", "1", TableDesc::Scratch); // td.addColumn (ArrayColumnDesc ("RA", 1)); // td.addColumn (ArrayColumnDesc ("Dec", 1)); // td.addColumn (ArrayColumnDesc ("Velocity", 1)); // td.addColumn (ArrayColumnDesc ("Image", 3)); // // Define the 3-dim hypercolumn with its data and coordinate columns. // // Note that its dimensionality must match the dimensionality // // of the data cells. // td.defineHypercolumn ("TSMExample", // 3, // stringToVector ("Image"), // stringToVector ("RA,Dec,Velocity")); // // Now create a new table from the description. // SetupNewTable newtab("tTiledCellStMan_tmp.data", td, Table::New); // // Create a TiledCellStMan storage manager for the hypercolumn // // and bind the columns to it. // TiledCellStMan sm1 ("TSMExample"); // newtab.bindAll (sm1); // // Create the table. // Table table(newtab); // // Define the values for the coordinates of the hypercube. // Vector raValues(512); // Vector DecValues(512); // Vector VelocityValues(64); // indgen (raValues); // indgen (decValues, float(100)); // indgen (velocityValues, float(200)); // ArrayColumn ra (table, "RA"); // ArrayColumn dec (table, "Dec"); // ArrayColumn velocity (table, "Velocity"); // ArrayColumn image (table, "Image"); // Cube imageValues(IPosition(3,512,512,64)); // indgen (imageValues); // // Write some data into the data columns. // for (uInt i=0; i<4; i++) { // table.addRow(); // image.put (i, imageValues); // ra.put (i, raValues); // dec.put (i, decValues); // velocity.put (i, velocityValues); // } // // //# //# A List of bugs, limitations, extensions or planned refinements. //# class TiledCellStMan : public TiledStMan { public: // Create a TiledDataStMan storage manager for the hypercolumn // with the given name. The columns used should have the FixedShape // attribute set. // The hypercolumn name is also the name of the storage manager. // The given tile shape will be used as the default for the hypercube // in each cell. Per cell it can be redefined via ArrayColumn::setShape. // The given maximum cache size (default is unlimited) is persistent, // thus will be reused when the table is read back. Note that the class // ROTiledStManAccessor // allows one to overwrite the maximum cache size temporarily. // Its description contains a discussion about the effects of // setting a maximum cache. //
        The constructor taking a Record expects fields in the record with // the name of the arguments in uppercase. If not defined, their // default value is used. // TiledCellStMan (const String& hypercolumnName, const IPosition& defaultTileShape, uInt64 maximumCacheSize = 0); TiledCellStMan (const String& hypercolumnName, const Record& spec); // ~TiledCellStMan(); // Forbid copy constructor. TiledCellStMan (const TiledCellStMan&) = delete; // Forbid assignment. TiledCellStMan& operator= (const TiledCellStMan&) = delete; // Clone this object. // It does not clone TSMColumn objects possibly used. DataManager* clone() const; // Get the type name of the data manager (i.e. TiledCellStMan). String dataManagerType() const; // This tiled storage manager can handle changing array shapes. Bool canChangeShape() const; // Set the shape and tile shape of the hypercube. virtual void setShape (rownr_t rownr, TSMCube* hypercube, const IPosition& shape, const IPosition& tileShape); // Make the object from the type name string. // This function gets registered in the DataManager "constructor" map. static DataManager* makeObject (const String& dataManagerType, const Record& spec); private: // Create a TiledCellStMan. // This constructor is private, because it should only be used // by makeObject. TiledCellStMan(); // Get the default tile shape. virtual IPosition defaultTileShape() const; // Add rows to the storage manager. void addRow64 (rownr_t nrrow); // Get the hypercube in which the given row is stored. virtual TSMCube* getHypercube (rownr_t rownr); // Get the hypercube in which the given row is stored. // It also returns the position of the row in that hypercube. virtual TSMCube* getHypercube (rownr_t rownr, IPosition& position); // Check if the hypercolumn definition fits this storage manager. virtual void setupCheck (const TableDesc& tableDesc, const Vector& dataNames) const; // Flush and optionally fsync the data. // It returns a True status if it had to flush (i.e. if data have changed). virtual Bool flush (AipsIO&, Bool fsync); // Let the storage manager create files as needed for a new table. // This allows a column with an indirect array to create its file. virtual void create64 (rownr_t nrrow); // Read the header info. virtual void readHeader (rownr_t nrrow, Bool firstTime); //# Declare the data members. IPosition defaultTileShape_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/TiledColumnStMan.cc000066400000000000000000000163021476623553700215760ustar00rootroot00000000000000//# TiledColumnStMan.cc: Storage manager for tables using tiled hypercubes //# Copyright (C) 1995,1996,1997,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Allocate an empty record to avoid reconstructing it over and over //# again when addRow is called many times. static Record emptyRecord; TiledColumnStMan::TiledColumnStMan () : TiledStMan () {} TiledColumnStMan::TiledColumnStMan (const String& hypercolumnName, const IPosition& tileShape, uInt64 maximumCacheSize) : TiledStMan (hypercolumnName, maximumCacheSize), tileShape_p (tileShape) {} TiledColumnStMan::TiledColumnStMan (const String& hypercolumnName, const Record& spec) : TiledStMan (hypercolumnName, 0) { if (spec.isDefined ("DEFAULTTILESHAPE")) { tileShape_p = IPosition (spec.toArrayInt ("DEFAULTTILESHAPE")); } if (spec.isDefined ("MAXIMUMCACHESIZE")) { setPersMaxCacheSize (spec.asInt64 ("MAXIMUMCACHESIZE")); } } TiledColumnStMan::~TiledColumnStMan() {} DataManager* TiledColumnStMan::clone() const { TiledColumnStMan* smp = new TiledColumnStMan (hypercolumnName_p, tileShape_p, maximumCacheSize()); return smp; } DataManager* TiledColumnStMan::makeObject (const String& group, const Record& spec) { TiledColumnStMan* smp = new TiledColumnStMan (group, spec); return smp; } String TiledColumnStMan::dataManagerType() const { return "TiledColumnStMan"; } Bool TiledColumnStMan::canAccessColumn () const { return True; } void TiledColumnStMan::create64 (rownr_t nrrow) { // Set up the various things. setup(1); // Create the one and single TSMFile object. createFile (0); // Create the hypercube object. // Its shape is the cell shape plus an extensible last dimension. // Check if the hypercube dimensionality is one extra. if (nrdim_p != fixedCellShape_p.nelements() + 1) { throw (TSMError ("TiledColumnStMan: hypercube dimensionality " "has to be 1 + cell dimensionality")); } IPosition cubeShape (fixedCellShape_p); cubeShape.resize (nrdim_p); cubeShape(nrdim_p - 1) = 0; cubeSet_p.resize (1); cubeSet_p[0] = makeTSMCube (fileSet_p[0], cubeShape, tileShape_p, emptyRecord); // Add the rows for the given number of rows. addRow64 (nrrow); } Bool TiledColumnStMan::flush (AipsIO&, Bool fsync) { // Flush the caches. // Exit if nothing has changed. if (! flushCaches (fsync)) { return False; } // Create the header file and write data in it. AipsIO* headerFile = headerFileCreate(); headerFile->putstart ("TiledColumnStMan", 1); *headerFile << tileShape_p; // Let the base class write its data; there is only one TSMCube to write. headerFilePut (*headerFile, 1); headerFile->putend(); headerFileClose (headerFile); return True; } void TiledColumnStMan::readHeader (rownr_t tabNrrow, Bool firstTime) { // Open the header file and read data from it. AipsIO* headerFile = headerFileOpen(); headerFile->getstart ("TiledColumnStMan"); *headerFile >> tileShape_p; // Let the base class read and initialize its data. headerFileGet (*headerFile, tabNrrow, firstTime, 1); headerFile->getend(); headerFileClose (headerFile); } void TiledColumnStMan::setupCheck (const TableDesc& tableDesc, const Vector& dataNames) const { // The data columns may only contain arrays with the correct // dimensionality, which should be one less than the hypercube // dimensionality. Int ndim = nrdim_p - 1; for (uInt i=0; i 0) { throw TSMError("ID columns cannot be used with TiledColumnStMan"); } } IPosition TiledColumnStMan::defaultTileShape() const { return tileShape_p; } void TiledColumnStMan::addRow64 (rownr_t nrow) { cubeSet_p[0]->extend (nrow, emptyRecord, coordColSet_p[nrdim_p - 1]); nrrow_p += nrow; setDataChanged(); } TSMCube* TiledColumnStMan::getHypercube (rownr_t rownr) { // Check if the row number is correct. if (rownr >= nrrow_p) { throw (TSMError ("getHypercube: rownr is too high")); } return cubeSet_p[0]; } TSMCube* TiledColumnStMan::getHypercube (rownr_t rownr, IPosition& position) { // Check if the row number is correct. if (rownr >= nrrow_p) { throw (TSMError ("getHypercube: rownr is too high")); } // The rownr is the position in the hypercube. position.resize (0); position = cubeSet_p[0]->cubeShape(); position(nrdim_p-1) = rownr; return cubeSet_p[0]; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/TiledColumnStMan.h000066400000000000000000000216351476623553700214450ustar00rootroot00000000000000//# TiledColumnStMan.h: Tiled Column Storage Manager //# Copyright (C) 1995,1996,1997,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TILEDCOLUMNSTMAN_H #define TABLES_TILEDCOLUMNSTMAN_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // Tiled Column Storage Manager. // // // // // //# Classes you should understand before using this one. //
      • TiledStMan //
      • TSMCube //
      • ROTiledStManAccessor // for a discussion of the maximum cache size // // // TiledColumnStMan is the Tiled Storage Manager storing // an entire column as one hypercube. // // // TiledColumnStMan is a derivation from TiledStMan, the abstract // tiled storage manager class. A description of the basics // of tiled storage managers is given in the // Tables module description. //

        // TiledColumnStMan allows the user to create a tiled hypercube for // an entire data column and extend it in an automatic way. // It is meant to be used for fixed shaped data which have to // be accessed in various directions. //

        // The TiledColumnStMan has the following (extra) properties: //

          //
        • Addition of a row results in the extension of the hypercube. // The data cells in all rows have to have the same shape. Therefore // the columns stored by a TiledColumnStMan storage manager // have to be fixed shaped (i.e. FixedShape attribute set in their // column descriptions). //
        • Coordinates for the hypercubes can be defined and (of course) // their shapes have to match the hypercube shape. // Their values have to be put explicitly (so it is not possible // to define them via an extendHypercube call like in // TiledDataStMan). //
        • The tile shape of the hypercube has to be defined by means // of the TiledColumnStMan constructor. //
        //
        // // This tiled storage manager does not require any special action // (like calling add/extendHypercube) when used with a column // containing equally shaped arrays. // // // // // Define the table description and the columns in it. // TableDesc td ("", "1", TableDesc::Scratch); // td.addColumn (ArrayColumnDesc ("RA", 1)); // td.addColumn (ArrayColumnDesc ("Dec", 1)); // td.addColumn (ScalarColumnDesc ("Velocity")); // td.addColumn (ArrayColumnDesc ("Image", 2)); // // Define the 3-dim hypercolumn with its data and coordinate columns. // // Note that its dimensionality must be one higher than the dimensionality // // of the data cells. // td.defineHypercolumn ("TSMExample", // 3, // stringToVector ("Image"), // stringToVector ("RA,Dec,Velocity")); // // Now create a new table from the description. // SetupNewTable newtab("tTiledColumnStMan_tmp.data", td, Table::New); // // Create a TiledColumnStMan storage manager for the hypercolumn // // and bind the columns to it. // // The tile shape has to be specified for the storage manager. // TiledColumnStMan sm1 ("TSMExample", IPosition(3,16,32,32)); // newtab.bindAll (sm1); // // Create the table. // Table table(newtab); // // Define the values for the coordinates of the hypercube. // Vector raValues(512); // Vector DecValues(512); // indgen (raValues); // indgen (decValues, float(100)); // ArrayColumn ra (table, "RA"); // ArrayColumn dec (table, "Dec"); // ScalarColumn velocity (table, "Velocity"); // ArrayColumn image (table, "Image"); // Cube imageValues(IPosition(2,512,512)); // indgen (imageValues); // // Write some data into the data columns. // for (uInt i=0; i<64; i++) { // table.addRow(); // image.put (i, imageValues); // // The RA and Dec have to be put only once, because they // // are the same for each row. // if (i == 0) { // ra.put (i, raValues); // dec.put (i, decValues); // } // velocity.put (i, float(i)); // } // // //# //# A List of bugs, limitations, extensions or planned refinements. //# class TiledColumnStMan : public TiledStMan { public: // Create a TiledDataStMan storage manager for the hypercolumn // with the given name. The columns used should have the FixedShape // attribute set. // The hypercolumn name is also the name of the storage manager. // The given tile shape will be used. // The given maximum cache size in bytes (default is unlimited) is // persistent, thus will be reused when the table is read back. // Note that the class // ROTiledStManAccessor // allows one to overwrite the maximum cache size temporarily. // Its description contains a discussion about the effects of // setting a maximum cache. //
        The constructor taking a Record expects fields in the record with // the name of the arguments in uppercase. If not defined, their // default value is used. // TiledColumnStMan (const String& hypercolumnName, const IPosition& tileShape, uInt64 maximumCacheSize = 0); TiledColumnStMan (const String& hypercolumnName, const Record& spec); // ~TiledColumnStMan(); // Forbid copy constructor. TiledColumnStMan (const TiledColumnStMan&) = delete; // Forbid assignment. TiledColumnStMan& operator= (const TiledColumnStMan&) = delete; // Clone this object. // It does not clone TSMColumn objects possibly used. virtual DataManager* clone() const; // TiledColumnStMan can always access a column. virtual Bool canAccessColumn() const; // Get the type name of the data manager (i.e. TiledColumnStMan). virtual String dataManagerType() const; // Make the object from the type name string. // This function gets registered in the DataManager "constructor" map. static DataManager* makeObject (const String& dataManagerType, const Record& spec); private: // Create a TiledColumnStMan. // This constructor is private, because it should only be used // by makeObject. TiledColumnStMan(); // Get the (default) tile shape. virtual IPosition defaultTileShape() const; // Add rows to the storage manager. // This will extend the hypercube. void addRow64 (rownr_t nrrow); // Get the hypercube in which the given row is stored. virtual TSMCube* getHypercube (rownr_t rownr); // Get the hypercube in which the given row is stored. // It also returns the position of the row in that hypercube. virtual TSMCube* getHypercube (rownr_t rownr, IPosition& position); // Check if the hypercolumn definition fits this storage manager. virtual void setupCheck (const TableDesc& tableDesc, const Vector& dataNames) const; // Flush and optionally fsync the data. // It returns a True status if it had to flush (i.e. if data have changed). virtual Bool flush (AipsIO&, Bool fsync); // Let the storage manager create files as needed for a new table. // This allows a column with an indirect array to create its file. virtual void create64 (rownr_t nrrow); // Read the header info. virtual void readHeader (rownr_t nrrow, Bool firstTime); //# Declare data members. IPosition tileShape_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/TiledDataStMan.cc000066400000000000000000000204641476623553700212160ustar00rootroot00000000000000//# TiledDataStMan.cc: Storage manager for tables using tiled hypercubes //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TiledDataStMan::TiledDataStMan () : TiledStMan (), nrrowLast_p (0) {} TiledDataStMan::TiledDataStMan (const String& hypercolumnName, uInt64 maximumCacheSize) : TiledStMan (hypercolumnName, maximumCacheSize), nrrowLast_p (0) {} TiledDataStMan::TiledDataStMan (const String& hypercolumnName, const Record& spec) : TiledStMan (hypercolumnName, 0), nrrowLast_p (0) { if (spec.isDefined ("MAXIMUMCACHESIZE")) { setPersMaxCacheSize (spec.asInt64 ("MAXIMUMCACHESIZE")); } } TiledDataStMan::~TiledDataStMan() {} DataManager* TiledDataStMan::clone() const { TiledDataStMan* smp = new TiledDataStMan (hypercolumnName_p, maximumCacheSize()); return smp; } DataManager* TiledDataStMan::makeObject (const String& group, const Record& spec) { TiledDataStMan* smp = new TiledDataStMan (group, spec); return smp; } String TiledDataStMan::dataManagerType() const { return "TiledDataStMan"; } void TiledDataStMan::create64 (rownr_t nrrow) { // Set up the various things. setup(-1); // Add the rows for the given number of rows. addRow64 (nrrow); } Bool TiledDataStMan::flush (AipsIO&, Bool fsync) { // Flush the caches. // Exit if nothing has changed. if (! flushCaches (fsync)) { return False; } // Create the header file and write data in it. AipsIO* headerFile = headerFileCreate(); headerFile->putstart ("TiledDataStMan", 2); // Let the base class write its data. headerFilePut (*headerFile, cubeSet_p.nelements()); // Write the data from this object. *headerFile << nrrowLast_p; *headerFile << rowMap_p << cubeMap_p << posMap_p; headerFile->putend(); headerFileClose (headerFile); return True; } void TiledDataStMan::readHeader (rownr_t tabNrrow, Bool firstTime) { // Open the header file and read data from it. AipsIO* headerFile = headerFileOpen(); uInt version = headerFile->getstart ("TiledDataStMan"); // Let the base class read and initialize its data. uInt hdrVersion = headerFileGet (*headerFile, tabNrrow, firstTime, -1); // Read the data for this object. // Version 1 was not incremented at the change to rownr_t, but the // parent class TiledStMan was. So test that version as well. if (version == 1 && hdrVersion < 3) { uInt nrow; *headerFile >> nrow; nrrowLast_p = nrow; } else { *headerFile >> nrrowLast_p; } if (version == 1) { uInt nused; *headerFile >> nused; std::vector rowMap; *headerFile >> rowMap; rowMap_p.insert (rowMap_p.end(), rowMap.begin(), rowMap.end()); *headerFile >> cubeMap_p >> posMap_p; } else { *headerFile >> rowMap_p >> cubeMap_p >> posMap_p; } headerFile->getend(); headerFileClose (headerFile); } void TiledDataStMan::addRow64 (rownr_t nrow) { nrrow_p += nrow; setDataChanged(); } void TiledDataStMan::checkNrrow (const IPosition& cubeShape, uInt64 incrInLastDim) const { rownr_t nrrow = addedNrrow (cubeShape, incrInLastDim); if (nrrowLast_p + nrrow > nrrow_p) { throw (TSMError ("Insufficient #rows in table for add/extendHypercube")); } } void TiledDataStMan::addHypercube (const IPosition& cubeShape, const IPosition& tileShape, const Record& values) { // Check if the number of rows involved fits in the table. checkNrrow (cubeShape, cubeShape(nrdim_p - 1)); // Check the hypercube definition and create the hypercube. checkAddHypercube (cubeShape, values); TSMCube* hypercube = makeHypercube (cubeShape, tileShape, values); uInt ncube = cubeSet_p.nelements(); cubeSet_p.resize (ncube + 1); cubeSet_p[ncube] = hypercube; // Update the row map with the number of pixels in last dimension. updateRowMap (ncube, cubeShape(nrdim_p-1)); } void TiledDataStMan::extendHypercube (uInt64 incrInLastDim, const Record& values) { // Check if id values are correctly given. // Get the hypercube using the id values. checkValues (idColSet_p, values); Int cubeNr = getCubeIndex (values); if (cubeNr < 0) { throw (TSMError ("extendHypercube with unknown id values")); } // Check if the number of rows involved fits in the table. checkNrrow (cubeSet_p[cubeNr]->cubeShape(), incrInLastDim); // Check if values for the last coordinate are given correctly. PtrBlock lastCoord (1, coordColSet_p[nrdim_p-1]); IPosition lastDim (1, incrInLastDim); checkCoordinates (lastCoord, lastDim, values); cubeSet_p[cubeNr]->extend (incrInLastDim, values, lastCoord[0]); updateRowMap (cubeNr, incrInLastDim); setDataChanged(); } void TiledDataStMan::updateRowMap (uInt cubeNr, uInt64 incrInLastDim) { if (incrInLastDim == 0) { return; } // Determine the numbers of rows added via the extension. const IPosition& shape = cubeSet_p[cubeNr]->cubeShape(); rowMap_p.push_back (nrrowLast_p); cubeMap_p.push_back (cubeNr); posMap_p.push_back (shape(nrdim_p-1) - incrInLastDim); // Now update the last row number used with the // number of rows this extension represents. rownr_t nr = addedNrrow (cubeSet_p[cubeNr]->cubeShape(), incrInLastDim); nrrowLast_p += nr; } TSMCube* TiledDataStMan::getHypercube (rownr_t rownr) { IPosition pos; return TiledDataStMan::getHypercube (rownr, pos); } TSMCube* TiledDataStMan::getHypercube (rownr_t rownr, IPosition& position) { // Check if the row number is correct. if (rownr >= nrrowLast_p ) { throw (TSMError ("getHypercube: rownr is too high")); } // Find the closest row number in the map (equal or less). Bool found; uInt index = binarySearchBrackets (found, rowMap_p, rownr, rowMap_p.size()); if (!found) { index--; } // Get the hypercube and the rownr relative to the start // of the hypercube chunk the requested row is in. TSMCube* hypercube = cubeSet_p[cubeMap_p[index]]; rownr_t rowDiff = rownr - rowMap_p[index]; // Transform the relative rownr into a hypercube position. // When the hypercube has axes with vector coordinates, those // axes are part of the data in the cell (thus only scalar // coordinates have to be taken into account). const IPosition& shape = hypercube->cubeShape(); position.resize (0); position = shape; for (uInt i=nrCoordVector_p; i #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // Tiled Data Storage Manager. // // // // // //# Classes you should understand before using this one. //
      • TiledStMan //
      • TSMCube //
      • ROTiledStManAccessor // for a discussion of the maximum cache size //
      • Record // // // TiledDataStMan is the Tiled Storage Manager for general // data arrays. // // // TiledDataStMan is a derivation from TiledStMan, the abstract // tiled storage manager class. A description of the basics // of tiled storage managers is given in the // Tables module description. //

        // TiledDataStMan allows the user explicit control over the // definition and extension of hypercubes by means of the accessor // class TiledDataStManAccessor. // The user can determine which row should be put in which hypercube, // so it is possible to put row 0-9 in hypercube A, row 10-29 in B, // row 30-39 in A again, etc.. This makes it possible to use a tiled // storage manager for a data column containing data with // different shapes (e.g. line and continuum data). Actually, // this storage manager is developed for irregularly shaped // UV-data, but can be used for any purpose. //
        // Each extensible hypercube uses a file of its own. This means that there // shouldn't be too many of them, otherwise the number of files may // get too high. //

        // The TiledDataStMan has the following (extra) properties: //

          //
        • When multiple hypercubes are used, one or more id columns have // to be used to differentiate between them. The id values must // be defined when the hypercube gets added; they cannot be put // explicitly. //
        • A hypercube can be extensible in its last dimension by setting // its last dimension to zero. In that case extendHypercube can // be used to extend the hypercube when needed. // All fixed sized hypercubes are stored in one file, while there // is one file per extensible hypercube. //
        • The table must be large enough to accommodate the addition // or extension of a hypercube. This means that a sufficient // number of rows must be added to the table before a hypercube // can be added or extended. It is the responsibility of the user // to "synchronize" addition of rows and hypercubes. //
        • It is possible to define coordinates for the hypercube axes // in several ways: //
            //
          • Use the TiledDataStMan storage manager to hold their values // and define the coordinates when adding or extending the // hypercube. This is the preferred way. //
          • As above, but use explicit puts to write their values. // This has to be used when coordinates are defined after // the hypercube has been added or extended. // Note that several rows may share the same value, so // overwriting a value may affect multiple rows. //
          • Use another storage manager to hold their values. // This is useful when their values depend on other axes, // because that cannot be handled by TiledDataStMan. //
          // Note that it is possible to store one coordinate column with // TiledDataStMan and another with another storage manager. //
        //
        // // This tiled storage manager allows one to create and extend hypercubes // as needed. One has complete control over which row is stored in which // hypercube. // // // The following example shows how to create a TiledDataStMan tiled // storage manager using the hypercolumn as defined in the table description. // Furthermore it shows how to use TiledDataStManAccessor // to add a hypercube, while defining its tile shape, coordinates, // and id-value. // The example shows that reading the data back does not require any knowledge // of the data manager. It's exactly the same if another data manager was used. //
        // The table created contains the equally shaped data columns "Data" and // "Weight". // Each cell in those columns contains a 2D array with shape [12,20]. The // coordinates of those arrays are "Pol" and "Freq". // The tiled storage manager superimposes two more axes ("Baseline"and "Time") // on the data resulting in a 4D hypercube with shape [12,20,30,42]. // The table contains 42*30 rows (which has to be equal to the number of // elements in the superimposed axes). //
        // The tile shape of the hypercube is (arbitrarily) set to [4,5,6,7]. // Of course, any tile shape could be chosen. This tile shape results // in a tile size of 6720 bytes (4*5*6*7 *(4+4) bytes), which is not // that large (32768 as tile size is very reasonable). The number of tiles // is integral in each dimension, so no space is wasted. // Finally it makes access along the various axes about equally efficient. //
        // Although in this example only one hypercube is added, multiple hypercubes // are possible, because an id column has been defined. // // The example uses the global Array function indgen to fill the data // and coordinate arrays with arbitrary values. // // Note that the description of class // ROTiledStManAccessor // contains a discussion about the effect of setting the maximum cache size. // // // // Define the table description and the columns in it. // TableDesc td ("", "1", TableDesc::Scratch); // td.addColumn (ScalarColumnDesc ("Time")); // td.addColumn (ScalarColumnDesc ("Baseline")); // td.addColumn (ArrayColumnDesc ("Pol", 1)); // td.addColumn (ArrayColumnDesc ("Freq", 1)); // td.addColumn (ScalarColumnDesc ("Id")); // td.addColumn (ArrayColumnDesc ("Data", 2)); // td.addColumn (ArrayColumnDesc ("Weight", 2)); // // Define the 4-dim hypercolumn with its data, coordinate and id columns. // td.defineHypercolumn ("TSMExample", // 4, // stringToVector ("Data,Weight"), // stringToVector ("Pol,Freq,Baseline,Time"), // stringToVector ("Id")); // // // Now create a new table from the description. // SetupNewTable newtab("tTiledDataStMan_tmp.data", td, Table::New); // // Create a TiledDataStMan storage manager for the hypercolumn // // and bind the columns to it. // TiledDataStMan sm1 ("TSMExample"); // newtab.bindAll (sm1); // // Create the table with 42*30 rows. // Table table(newtab, 42*30); // // Create the accessor to be able to add a hypercube to this // // storage manager. // TiledDataStManAccessor accessor(table, "TSMExample"); // // Define the values for the coordinates of the hypercube // // and put them into the record. // Vector timeValues(42); // Vector baselineValues(30); // Vector freqValues(20); // Vector polValues(12); // indgen (timeValues); // indgen (baselineValues, float(100)); // indgen (freqValues, float(200)); // indgen (polValues, float(300)); // Record hyperDef; // hyperDef.define ("Time", timeValues); // hyperDef.define ("Baseline", baselineValues); // hyperDef.define ("Freq", freqValues); // hyperDef.define ("Pol", polValues); // // Define the id value as well. // hyperDef.define ("Id", ""); // // Now add the hypercube with the given shape, tile shape, // // and coordinate and id values. // accessor.addHypercube (IPosition(4,12,20,30,42), // IPosition(4,4,5,6,7), hyperDef); // ArrayColumn data (table, "Data"); // ArrayColumn weight (table, "Weight"); // Matrix array(IPosition(2,12,20)); // indgen (array); // // Write some data into the data columns. // for (uInt i=0; i<30*42; i++) { // data.put (i, array); // weight.put (i, array+float(100)); // array += float(200); // } // // Prepare for reading the data back. // // Note that time and baseline are in fact scalar columns. They are // // superimposed dimensions on the hypercube. // ScalarColumn time (table, "Time"); // ScalarColumn baseline (table, "Baseline"); // ArrayColumn freq (table, "Freq"); // ArrayColumn pol (table, "Pol"); // ScalarColumn id (table, "Id"); // float fValue; // String sValue; // for (rownr_t i=0; i // Note that in this example an id column was not necessary, because // there is only one hypercube. //

        // The following example is more advanced. Two (extensible) hypercubes // are used for line and continuum data. Writing such a data set // could be done as shown. Reading it back is the same as above. //
        // In this example the data columns contain line and continuum data. // So there are two types of data, each with their own shape and // stored in their own (extensible) hypercube. Note that the last // dimension of the hypercube shape is set to zero (to make extensible), // but the last tile shape dimension has been filled in, // because the exact tile shape must be known. //
        // Before each put of the data the appropriate hypercube is extended. // Also the time has to be put, which is done (as an example) in // two different ways (using an explicit put and using the extendHypercube). // // // // Defining TableDesc and storage manager is same as in first example. // // Create the table. // Table table(newtab); // // Create the accessor to be able to add the hypercubes to this // // storage manager. // TiledDataStManAccessor accessor(table, "TSMExample"); // // Fill the coordinate values. // // Note that the time axis of the hypercube will have length 0 to // // make it extensible. Therefore the time coordinate can only be // // filled in when the hypercube is extended. // Vector baselineValues(30); // Vector freqValuesCont(1); // Vector freqValuesLine(20); // Vector polValues(4); // indgen (baselineValues, float(100)); // indgen (freqValuesLine, float(200)); // indgen (freqValuesCont, float(200)); // indgen (polValues, float(300)); // Record hyperDefLine; // hyperDefLine.define ("Baseline", baselineValues); // hyperDefLine.define ("Pol", polValues); // // Make similar record for line data. // // Fill the correct id and frequency values for each type. // // Add the 2 hypercubes. // Record hyperDefCont (hyperDefLine); // hyperDefLine.define ("Id", "L"); // hyperDefLine.define ("Freq", freqValuesLine); // hyperDefCont.define ("Id", "C"); // hyperDefCont.define ("Freq", freqValuesCont); // // Add the hypercubes. // // Define their last dimension as zero to make them extensible. // accessor.addHypercube (IPosition(4,4,20,30,0), // IPosition(4,4,5,6,7), hyperDefLine); // accessor.addHypercube (IPosition(4,4,1,30,0), // IPosition(4,4,1,6,7), hyperDefCont); // ScalarColumn time (table, "Time"); // ScalarColumn baseline (table, "Baseline"); // ArrayColumn freq (table, "Freq"); // ArrayColumn pol (table, "Pol"); // ArrayColumn data (table, "Data"); // ArrayColumn weight (table, "Weight"); // Matrix arrayLine(IPosition(2,4,20)); // Matrix arrayCont(IPosition(2,4,1)); // indgen (arrayLine); // indgen (arrayCont); // // Write some data into the data columns. // // Alternately line and continuum is written. // // Each hypercube requires 30 rows to be added (i.e. nr of baselines). // // The last dimension of each hypercube is extended with 1. // rownr_t rownr = 0; // for (uInt i=0; i<42; i++) { // if (i%2 == 0) { // table.addRow (30); // accessor.extendHypercube (1, hyperDefLine); // time.put (rownr, float(i)); // for (uInt j=0; j<30; j++) { // data.put (rownr, arrayLine); // weight.put (rownr, arrayLine); // rownr++; // } // }else{ // table.addRow (30); // Vector timeValue(1); // timeValue(0) = float(i); // hyperDefCont.define ("Time", timeValue); // accessor.extendHypercube (1, hyperDefCont); // time.put (rownr, float(i)); // for (uInt j=0; j<30; j++) { // data.put (rownr, arrayCont); // weight.put (rownr, arrayCont); // rownr++; // } // } // } // // Note that in this example the time is defined in 2 different ways. // The first one by an explicit put, the second one as a record in // the extendHypercube call. The second way if the preferred one, // although it requires a bit more coding. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class TiledDataStMan : public TiledStMan { friend class TiledDataStManAccessor; public: // Create a TiledDataStMan storage manager for the hypercolumn // with the given name. // The hypercolumn name is also the name of the storage manager. // The given maximum cache size (default is unlimited) is persistent, // thus will be reused when the table is read back. Note that the class // ROTiledStManAccessor // allows one to overwrite the maximum cache size temporarily. //
        The constructor taking a Record expects fields in the record with // the name of the arguments in uppercase. If not defined, their // default value is used. // TiledDataStMan (const String& hypercolumnName, uInt64 maximumCacheSize = 0); TiledDataStMan (const String& hypercolumnName, const Record& spec); // ~TiledDataStMan(); // Forbid copy constructor. TiledDataStMan (const TiledDataStMan&) = delete; // Forbid assignment. TiledDataStMan& operator= (const TiledDataStMan&) = delete; // Clone this object. // It does not clone TSMColumn objects possibly used. DataManager* clone() const; // Get the type name of the data manager (i.e. TiledDataStMan). String dataManagerType() const; // Make the object from the type name string. // This function gets registered in the DataManager "constructor" map. static DataManager* makeObject (const String& dataManagerType, const Record& spec); private: // Create a TiledDataStMan. // This constructor is private, because it should only be used // by makeObject. TiledDataStMan(); // Add rows to the storage manager. // This will only increase the number of rows. When a hypercube is // added or extended, it will be checked whether the number of rows // is sufficient. void addRow64 (rownr_t nrrow); // Add a hypercube. // The number of rows in the table must be large enough to // accommodate this hypercube. // The possible id values must be given in the record, while // coordinate values are optional. The field names in the record // should match the coordinate and id column names. // The last dimension in the cube shape can be zero, indicating that // the hypercube is extensible. void addHypercube (const IPosition& cubeShape, const IPosition& tileShape, const Record& values); // Extend the hypercube with the given number of elements in // the last dimension. // The record should contain the id values (to get the correct // hypercube) and optionally coordinate values for the elements added. void extendHypercube (uInt64 incrInLastDim, const Record& values); // Get the hypercube in which the given row is stored. virtual TSMCube* getHypercube (rownr_t rownr); // Get the hypercube in which the given row is stored. // It also returns the position of the row in that hypercube. virtual TSMCube* getHypercube (rownr_t rownr, IPosition& position); // Flush and optionally fsync the data. // It returns a True status if it had to flush (i.e. if data have changed). virtual Bool flush (AipsIO&, Bool fsync); // Let the storage manager create files as needed for a new table. // This allows a column with an indirect array to create its file. virtual void create64 (rownr_t nrrow); // Read the header info. virtual void readHeader (rownr_t nrrow, Bool firstTime); // Update the map of row numbers to cube number plus offset. void updateRowMap (uInt cubeNr, uInt64 incrInLastDim); // Check if the table is large enough to hold this // hypercube extension. void checkNrrow (const IPosition& cubeShape, uInt64 incrInLastDim) const; //# Declare the data members. // The map of row number to cube and position in cube. std::vector rowMap_p; std::vector cubeMap_p; std::vector posMap_p; // The row number since the last hypercube extension. rownr_t nrrowLast_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/TiledDataStManAccessor.cc000066400000000000000000000062531476623553700227010ustar00rootroot00000000000000//# TiledDataStManAccessor.cc: Gives access to some TiledDataStMan functions //# Copyright (C) 1994,1995,1996,1997 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TiledDataStManAccessor::TiledDataStManAccessor (const Table& table, const String& dataManagerName) : ROTiledStManAccessor (table, dataManagerName), tiledDataManPtr_p (0) { DataManager* dmptr = getDataManager(); TiledDataStMan dataMan; if (dmptr->dataManagerType() != dataMan.dataManagerType()) { throw (DataManError ("Data manager " + dataManagerName + " has type " + dmptr->dataManagerType() + "; expected " + dataMan.dataManagerType())); } if (! table.isWritable()) { throw (DataManError ("TiledDataStManAccessor: table is not writable")); } // The types match, so it is now safe to cast. tiledDataManPtr_p = (TiledDataStMan*)dmptr; } TiledDataStManAccessor::TiledDataStManAccessor () { // dummy constructor } TiledDataStManAccessor::~TiledDataStManAccessor() {} TiledDataStManAccessor::TiledDataStManAccessor (const TiledDataStManAccessor& that) : ROTiledStManAccessor (that), tiledDataManPtr_p (that.tiledDataManPtr_p) {} TiledDataStManAccessor& TiledDataStManAccessor::operator= (const TiledDataStManAccessor& that) { ROTiledStManAccessor::operator= (that); tiledDataManPtr_p = that.tiledDataManPtr_p; return *this; } void TiledDataStManAccessor::addHypercube (const IPosition& cubeShape, const IPosition& tileShape, const Record& values) { tiledDataManPtr_p->addHypercube (cubeShape, tileShape, values); } void TiledDataStManAccessor::extendHypercube (uInt incrInLastDim, const Record& values) { tiledDataManPtr_p->extendHypercube (incrInLastDim, values); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/TiledDataStManAccessor.h000066400000000000000000000130541476623553700225400ustar00rootroot00000000000000//# TiledDataStManAccessor.h: Gives access to some TiledDataStMan functions //# Copyright (C) 1994,1995,1996,1997,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TILEDDATASTMANACCESSOR_H #define TABLES_TILEDDATASTMANACCESSOR_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TiledDataStMan; class Table; class String; class IPosition; class Record; //

        // Give access to some TiledDataStMan functions // // // // // //# Classes you should understand before using this one. //
      • ROTiledStManAccessor //
      • TiledDataStMan // // // The Table system has one or more storage managers underneath. // These storage managers are invisible and there is no way to // get access to them. // However, the TiledDataStMan // storage manager is quite specific. It has a few functions which // need to be called by the user, in particular the functions // defining and extending a hypercube. //

        // The class TiledDataStManAccessor gives the user the means to // access a TiledDataStMan object and to call the functions mentioned above. // It can only be constructed for a table opened for read/write (because // the functions in it need write access). // // // In principle a pointer to TiledDataStMan could be used. // However, that would give access to all public functions. // Furthermore it could not distinguish between read/write and readonly // tables. // // // // // Open a table for write. // Table table ("someName", Table::Update); // // Get access to the tiled data storage manager with name UVdata. // TiledDataStManAccessor accessor (Table, "UVdata"); // // Add a hypercube to it (requires addition of rows). // // Define the values of the ID and coordinate columns. // // (The coordinate vectors have to be filled in one way or another). // Vector timeVector(70); // Vector baselineVector(60); // Vector freqVector(50); // Vector polVector(4); // Record values; // values.define ("ID", 4); // ID=4 // values.define ("time", timeVector); // values.define ("baseline", baselineVector); // values.define ("freq", freqVector); // values.define ("pol", polVector); // table.addRow (4200); // accessor.addHypercube (IPosition(4,4,50,60,70), // cube shape // IPosition(4,4,5,6,7), // tile shape // values); // id/coord values // // // //# A List of bugs, limitations, extensions or planned refinements. //

      • A base class RO_Tiled(Data)StManAccessor may once be needed // for access to a tiled data storage manager in a readonly table // class TiledDataStManAccessor : public ROTiledStManAccessor { public: // Construct the object for the data manager in the table. // An exception is thrown if the data manager type does not // match the type of this TiledDataStManAccessor object. // Also an exception is thrown if the table is not open for read/write. TiledDataStManAccessor (const Table& table, const String& dataManagerName); TiledDataStManAccessor (); ~TiledDataStManAccessor(); // Copy constructor (reference semantics). TiledDataStManAccessor (const TiledDataStManAccessor& that); // Assignment (reference semantics). TiledDataStManAccessor& operator= (const TiledDataStManAccessor& that); // Add a hypercube. // The possible coordinate- and id-values have to be given in the // record (where the field names should be equal to the // coordinate and id column names). void addHypercube (const IPosition& cubeShape, const IPosition& tileShape, const Record& values); // Extend the hypercube with the given number of elements in // the last dimension. // The record should contain the id values (to get the correct // hypercube) and coordinate values for the elements added. void extendHypercube (uInt incrInLastDim, const Record& values); private: //# Declare the data members. TiledDataStMan* tiledDataManPtr_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/TiledFileAccess.cc000066400000000000000000000417611476623553700214060ustar00rootroot00000000000000//# TiledFileAccess.cc: Tiled access to an array in a file //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TiledFileAccess::TiledFileAccess (const String& fileName, Int64 fileOffset, const IPosition& shape, const IPosition& tileShape, DataType dataType, const TSMOption& tsmOpt, Bool writable) : itsCube (0), itsTSM (0), itsWritable (writable), itsDataType (dataType) { itsLocalPixelSize = ValType::getTypeSize (dataType); itsTSM = new TiledFileHelper (fileName, shape, dataType, tsmOpt, writable, HostInfo::bigEndian()); itsCube = itsTSM->makeTSMCube (itsTSM->file(), shape, tileShape, Record(), fileOffset); } TiledFileAccess::TiledFileAccess (const String& fileName, Int64 fileOffset, const IPosition& shape, const IPosition& tileShape, DataType dataType, const TSMOption& tsmOpt, Bool writable, Bool bigEndian) : itsCube (0), itsTSM (0), itsWritable (writable), itsDataType (dataType) { itsLocalPixelSize = ValType::getTypeSize (dataType); itsTSM = new TiledFileHelper (fileName, shape, dataType, tsmOpt, writable, bigEndian); itsCube = itsTSM->makeTSMCube (itsTSM->file(), shape, tileShape, Record(), fileOffset); } TiledFileAccess::~TiledFileAccess() { delete itsCube; delete itsTSM; } Array TiledFileAccess::getBool (const Slicer& section) { Array arr; get (arr, section); return arr; } Array TiledFileAccess::getUChar (const Slicer& section) { Array arr; get (arr, section); return arr; } Array TiledFileAccess::getShort (const Slicer& section) { Array arr; get (arr, section); return arr; } Array TiledFileAccess::getInt (const Slicer& section) { Array arr; get (arr, section); return arr; } Array TiledFileAccess::getFloat (const Slicer& section) { Array arr; get (arr, section); return arr; } Array TiledFileAccess::getDouble (const Slicer& section) { Array arr; get (arr, section); return arr; } Array TiledFileAccess::getComplex (const Slicer& section) { Array arr; get (arr, section); return arr; } Array TiledFileAccess::getDComplex (const Slicer& section) { Array arr; get (arr, section); return arr; } void TiledFileAccess::get (Array& buffer, const Slicer& section) { AlwaysAssert (itsDataType == TpBool, AipsError); IPosition start, end, stride; IPosition shp = section.inferShapeFromSource (itsCube->cubeShape(), start, end, stride); buffer.resize (shp); Bool deleteIt; Bool* dataPtr = buffer.getStorage (deleteIt); itsCube->accessStrided (start, end, stride, (char*)dataPtr, 0, itsLocalPixelSize, itsLocalPixelSize, False); buffer.putStorage (dataPtr, deleteIt); } void TiledFileAccess::get (Array& buffer, const Slicer& section) { AlwaysAssert (itsDataType == TpUChar, AipsError); IPosition start, end, stride; IPosition shp = section.inferShapeFromSource (itsCube->cubeShape(), start, end, stride); buffer.resize (shp); Bool deleteIt; uChar* dataPtr = buffer.getStorage (deleteIt); itsCube->accessStrided (start, end, stride, (char*)dataPtr, 0, itsLocalPixelSize, itsLocalPixelSize, False); buffer.putStorage (dataPtr, deleteIt); } void TiledFileAccess::get (Array& buffer, const Slicer& section) { AlwaysAssert (itsDataType == TpShort, AipsError); IPosition start, end, stride; IPosition shp = section.inferShapeFromSource (itsCube->cubeShape(), start, end, stride); buffer.resize (shp); Bool deleteIt; Short* dataPtr = buffer.getStorage (deleteIt); itsCube->accessStrided (start, end, stride, (char*)dataPtr, 0, itsLocalPixelSize, itsLocalPixelSize, False); buffer.putStorage (dataPtr, deleteIt); } void TiledFileAccess::get (Array& buffer, const Slicer& section) { AlwaysAssert (itsDataType == TpInt, AipsError); IPosition start, end, stride; IPosition shp = section.inferShapeFromSource (itsCube->cubeShape(), start, end, stride); buffer.resize (shp); Bool deleteIt; Int* dataPtr = buffer.getStorage (deleteIt); itsCube->accessStrided (start, end, stride, (char*)dataPtr, 0, itsLocalPixelSize, itsLocalPixelSize, False); buffer.putStorage (dataPtr, deleteIt); } void TiledFileAccess::get (Array& buffer, const Slicer& section) { AlwaysAssert (itsDataType == TpFloat, AipsError); IPosition start, end, stride; IPosition shp = section.inferShapeFromSource (itsCube->cubeShape(), start, end, stride); buffer.resize (shp); Bool deleteIt; Float* dataPtr = buffer.getStorage (deleteIt); itsCube->accessStrided (start, end, stride, (char*)dataPtr, 0, itsLocalPixelSize, itsLocalPixelSize, False); buffer.putStorage (dataPtr, deleteIt); } void TiledFileAccess::get (Array& buffer, const Slicer& section) { AlwaysAssert (itsDataType == TpDouble, AipsError); IPosition start, end, stride; IPosition shp = section.inferShapeFromSource (itsCube->cubeShape(), start, end, stride); buffer.resize (shp); Bool deleteIt; Double* dataPtr = buffer.getStorage (deleteIt); itsCube->accessStrided (start, end, stride, (char*)dataPtr, 0, itsLocalPixelSize, itsLocalPixelSize, False); buffer.putStorage (dataPtr, deleteIt); } void TiledFileAccess::get (Array& buffer, const Slicer& section) { AlwaysAssert (itsDataType == TpComplex, AipsError); IPosition start, end, stride; IPosition shp = section.inferShapeFromSource (itsCube->cubeShape(), start, end, stride); buffer.resize (shp); Bool deleteIt; Complex* dataPtr = buffer.getStorage (deleteIt); itsCube->accessStrided (start, end, stride, (char*)dataPtr, 0, itsLocalPixelSize, itsLocalPixelSize, False); buffer.putStorage (dataPtr, deleteIt); } void TiledFileAccess::get (Array& buffer, const Slicer& section) { AlwaysAssert (itsDataType == TpDComplex, AipsError); IPosition start, end, stride; IPosition shp = section.inferShapeFromSource (itsCube->cubeShape(), start, end, stride); buffer.resize (shp); Bool deleteIt; DComplex* dataPtr = buffer.getStorage (deleteIt); itsCube->accessStrided (start, end, stride, (char*)dataPtr, 0, itsLocalPixelSize, itsLocalPixelSize, False); buffer.putStorage (dataPtr, deleteIt); } Array TiledFileAccess::getFloat (const Slicer& section, Float scale, Float offset, uChar deleteValue, Bool examineForDeleteValues) { Array arr; get (arr, section, scale, offset, deleteValue, examineForDeleteValues); return arr; } Array TiledFileAccess::getFloat (const Slicer& section, Float scale, Float offset, Short deleteValue, Bool examineForDeleteValues) { Array arr; get (arr, section, scale, offset, deleteValue, examineForDeleteValues); return arr; } Array TiledFileAccess::getFloat (const Slicer& section, Float scale, Float offset, Int deleteValue, Bool examineForDeleteValues) { Array arr; get (arr, section, scale, offset, deleteValue, examineForDeleteValues); return arr; } void TiledFileAccess::get (Array& buffer, const Slicer& section, Float scale, Float offset, uChar deleteValue, Bool examineForDeleteValues) { Array arr = getUChar (section); buffer.resize (arr.shape()); Bool deleteArr, deleteBuf; const uChar* arrPtr = arr.getStorage (deleteArr); Float* bufPtr = buffer.getStorage (deleteBuf); uInt64 n = arr.nelements(); if (examineForDeleteValues) { for (uInt64 i=0; i& buffer, const Slicer& section, Float scale, Float offset, Short deleteValue, Bool examineForDeleteValues) { Array arr = getShort (section); buffer.resize (arr.shape()); Bool deleteArr, deleteBuf; const Short* arrPtr = arr.getStorage (deleteArr); Float* bufPtr = buffer.getStorage (deleteBuf); uInt64 n = arr.nelements(); if (examineForDeleteValues) { for (uInt64 i=0; i& buffer, const Slicer& section, Float scale, Float offset, Int deleteValue, Bool examineForDeleteValues) { Array arr = getInt (section); buffer.resize (arr.shape()); Bool deleteArr, deleteBuf; const Int* arrPtr = arr.getStorage (deleteArr); Float* bufPtr = buffer.getStorage (deleteBuf); uInt64 n = arr.nelements(); if (examineForDeleteValues) { for (uInt64 i=0; i& buffer, const Slicer& section) { AlwaysAssert (isWritable(), AipsError); AlwaysAssert (itsDataType == TpBool, AipsError); IPosition start, end, stride; IPosition shp = section.inferShapeFromSource (itsCube->cubeShape(), start, end, stride); AlwaysAssert (shp.isEqual (buffer.shape()), AipsError); Bool deleteIt; const Bool* dataPtr = buffer.getStorage (deleteIt); itsCube->accessStrided (start, end, stride, (char*)dataPtr, 0, itsLocalPixelSize, itsLocalPixelSize, True); buffer.freeStorage (dataPtr, deleteIt); } void TiledFileAccess::put (const Array& buffer, const Slicer& section) { AlwaysAssert (isWritable(), AipsError); AlwaysAssert (itsDataType == TpShort, AipsError); IPosition start, end, stride; IPosition shp = section.inferShapeFromSource (itsCube->cubeShape(), start, end, stride); AlwaysAssert (shp.isEqual (buffer.shape()), AipsError); Bool deleteIt; const uChar* dataPtr = buffer.getStorage (deleteIt); itsCube->accessStrided (start, end, stride, (char*)dataPtr, 0, itsLocalPixelSize, itsLocalPixelSize, True); buffer.freeStorage (dataPtr, deleteIt); } void TiledFileAccess::put (const Array& buffer, const Slicer& section) { AlwaysAssert (isWritable(), AipsError); AlwaysAssert (itsDataType == TpShort, AipsError); IPosition start, end, stride; IPosition shp = section.inferShapeFromSource (itsCube->cubeShape(), start, end, stride); AlwaysAssert (shp.isEqual (buffer.shape()), AipsError); Bool deleteIt; const Short* dataPtr = buffer.getStorage (deleteIt); itsCube->accessStrided (start, end, stride, (char*)dataPtr, 0, itsLocalPixelSize, itsLocalPixelSize, True); buffer.freeStorage (dataPtr, deleteIt); } void TiledFileAccess::put (const Array& buffer, const Slicer& section) { AlwaysAssert (isWritable(), AipsError); AlwaysAssert (itsDataType == TpInt, AipsError); IPosition start, end, stride; IPosition shp = section.inferShapeFromSource (itsCube->cubeShape(), start, end, stride); AlwaysAssert (shp.isEqual (buffer.shape()), AipsError); Bool deleteIt; const Int* dataPtr = buffer.getStorage (deleteIt); itsCube->accessStrided (start, end, stride, (char*)dataPtr, 0, itsLocalPixelSize, itsLocalPixelSize, True); buffer.freeStorage (dataPtr, deleteIt); } void TiledFileAccess::put (const Array& buffer, const Slicer& section) { AlwaysAssert (isWritable(), AipsError); AlwaysAssert (itsDataType == TpFloat, AipsError); IPosition start, end, stride; IPosition shp = section.inferShapeFromSource (itsCube->cubeShape(), start, end, stride); AlwaysAssert (shp.isEqual (buffer.shape()), AipsError); Bool deleteIt; const Float* dataPtr = buffer.getStorage (deleteIt); itsCube->accessStrided (start, end, stride, (char*)dataPtr, 0, itsLocalPixelSize, itsLocalPixelSize, True); buffer.freeStorage (dataPtr, deleteIt); } void TiledFileAccess::put (const Array& buffer, const Slicer& section) { AlwaysAssert (isWritable(), AipsError); AlwaysAssert (itsDataType == TpDouble, AipsError); IPosition start, end, stride; IPosition shp = section.inferShapeFromSource (itsCube->cubeShape(), start, end, stride); AlwaysAssert (shp.isEqual (buffer.shape()), AipsError); Bool deleteIt; const Double* dataPtr = buffer.getStorage (deleteIt); itsCube->accessStrided (start, end, stride, (char*)dataPtr, 0, itsLocalPixelSize, itsLocalPixelSize, True); buffer.freeStorage (dataPtr, deleteIt); } void TiledFileAccess::put (const Array& buffer, const Slicer& section) { AlwaysAssert (isWritable(), AipsError); AlwaysAssert (itsDataType == TpComplex, AipsError); IPosition start, end, stride; IPosition shp = section.inferShapeFromSource (itsCube->cubeShape(), start, end, stride); AlwaysAssert (shp.isEqual (buffer.shape()), AipsError); Bool deleteIt; const Complex* dataPtr = buffer.getStorage (deleteIt); itsCube->accessStrided (start, end, stride, (char*)dataPtr, 0, itsLocalPixelSize, itsLocalPixelSize, True); buffer.freeStorage (dataPtr, deleteIt); } void TiledFileAccess::put (const Array& buffer, const Slicer& section) { AlwaysAssert (isWritable(), AipsError); AlwaysAssert (itsDataType == TpDComplex, AipsError); IPosition start, end, stride; IPosition shp = section.inferShapeFromSource (itsCube->cubeShape(), start, end, stride); AlwaysAssert (shp.isEqual (buffer.shape()), AipsError); Bool deleteIt; const DComplex* dataPtr = buffer.getStorage (deleteIt); itsCube->accessStrided (start, end, stride, (char*)dataPtr, 0, itsLocalPixelSize, itsLocalPixelSize, True); buffer.freeStorage (dataPtr, deleteIt); } void TiledFileAccess::setMaximumCacheSize (uInt64 nbytes) { itsTSM->setMaximumCacheSize (nbytes); } uInt64 TiledFileAccess::maximumCacheSize() const { return itsTSM->maximumCacheSize(); } IPosition TiledFileAccess::makeTileShape (const IPosition& arrayShape, uInt nrPixelsPerTile) { Float nrPixels = nrPixelsPerTile; uInt ndim = arrayShape.nelements(); IPosition tileShape (ndim, 1); for (uInt i=0; i 0, AipsError); } break; } } return tileShape; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/TiledFileAccess.h000066400000000000000000000220011476623553700212320ustar00rootroot00000000000000//# TiledFileAccess.h: Tiled access to an array in a file //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TILEDFILEACCESS_H #define TABLES_TILEDFILEACCESS_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TiledFileHelper; class Slicer; // // Tiled access to an array in a file. // // // // // //# Classes you should understand before using this one. //
      • Description of Tiled Storage Manager in module file // Tables.h //
      • ROTiledStManAccessor // for a discussion of the maximum cache size // // // TiledFileAccess is a class that makes it possible to access // an arbitrary array in a file using the tiled storage manager classes. // It can handle arrays of any type supported by the tiled storage // managers. The array can be in big or little endian canonical format. //

        // See ROTiledStManAccessor // for a more detailed discussion. // // // This class makes it possible to access an image in a FITS file. // // // // // Define the object which also opens the file. // // The (float) array starts at offset 2880. // TiledFileAccess tfa ("fits.file", 2880, IPosition(2,512,512), // IPosition(2,512,1), TpFloat); // // Get all the data. // Array data = tfa.getFloat (Slicer(IPosition(2,0,0), tfa.shape())); // // //# //# A List of bugs, limitations, extensions or planned refinements. //# class TiledFileAccess { public: // Create a TiledFileAccess object. // The data is assumed to be in local canonical format // (thus big endian on e.g. SUN and little endian on e.g. PC). // The TSMOption determines how the file is accessed. TiledFileAccess (const String& fileName, Int64 fileOffset, const IPosition& shape, const IPosition& tileShape, DataType dataType, const TSMOption& = TSMOption(), Bool writable=False); // Create a TiledFileAccess object. // The endian format of the data is explicitly given. TiledFileAccess (const String& fileName, Int64 fileOffset, const IPosition& shape, const IPosition& tileShape, DataType dataType, const TSMOption&, Bool writable, Bool bigEndian); ~TiledFileAccess(); // Forbid copy constructor. TiledFileAccess (const TiledFileAccess&) = delete; // Forbid assignment. TiledFileAccess& operator= (const TiledFileAccess&) = delete; // Is the file writable? Bool isWritable() const { return itsWritable; } DataType dataType() const { return itsDataType; } // Get part of the array. // The Array object is resized if needed. // Array getBool (const Slicer& section); Array getUChar (const Slicer& section); Array getShort (const Slicer& section); Array getInt (const Slicer& section); Array getFloat (const Slicer& section); Array getDouble (const Slicer& section); Array getComplex (const Slicer& section); Array getDComplex (const Slicer& section); void get (Array&, const Slicer& section); void get (Array&, const Slicer& section); void get (Array&, const Slicer& section); void get (Array&, const Slicer& section); void get (Array&, const Slicer& section); void get (Array&, const Slicer& section); void get (Array&, const Slicer& section); void get (Array&, const Slicer& section); // // Get the array and scale/offset the data using the given values. // It is meant for FITS, so for now they can only be used for TpUChar, TpShort // or TpInt TiledFileAccess objects. // A deleteValue is set to a NaN without being scaled. // Array getFloat (const Slicer& section, Float scale, Float offset, uChar deleteValue, Bool examineForDeleteValues=True); Array getFloat (const Slicer& section, Float scale, Float offset, Short deleteValue, Bool examineForDeleteValues=True); Array getFloat (const Slicer& section, Float scale, Float offset, Int deleteValue, Bool examineForDeleteValues=True); void get (Array&, const Slicer& section, Float scale, Float offset, uChar deleteValue, Bool examineForDeleteValues=True); void get (Array&, const Slicer& section, Float scale, Float offset, Short deleteValue, Bool examineForDeleteValues=True); void get (Array&, const Slicer& section, Float scale, Float offset, Int deleteValue, Bool examineForDeleteValues=True); // // Put part of the array. // void put (const Array&, const Slicer& section); void put (const Array&, const Slicer& section); void put (const Array&, const Slicer& section); void put (const Array&, const Slicer& section); void put (const Array&, const Slicer& section); void put (const Array&, const Slicer& section); void put (const Array&, const Slicer& section); void put (const Array&, const Slicer& section); // // Flush the cache. void flush() { itsCube->flushCache(); } // Empty the cache. // It will flush the cache as needed and remove all buckets from it // resulting in a possibly large drop in memory used. // It'll also clear the userSetCache_p flag. void clearCache() { itsCube->emptyCache(); } // Show the cache statistics. void showCacheStatistics (ostream& os) const { itsCube->showCacheStatistics (os); } // Get the shape of the array. const IPosition& shape() const { return itsCube->cubeShape(); } // Get the shape of the tiles. const IPosition& tileShape() const { return itsCube->tileShape(); } // Set the maximum cache size (in bytes). // 0 means no maximum. void setMaximumCacheSize (uInt64 nbytes); // Get the maximum cache size (in bytes). uInt64 maximumCacheSize() const; // Get the current cache size (in buckets). uInt cacheSize() const { return itsCube->cacheSize(); } // Set the cache size using the given access pattern. // void setCacheSize (const IPosition& sliceShape, const IPosition& axisPath, Bool forceSmaller=True) { itsCube->setCacheSize (sliceShape, IPosition(), IPosition(), axisPath, forceSmaller, True); } void setCacheSize (const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath, Bool forceSmaller=True) { itsCube->setCacheSize (sliceShape, windowStart, windowLength, axisPath, forceSmaller, True); } // // Set the cache size for accessing the data. // When the give cache size exceeds the maximum cache size with more // than 10%, the maximum cache size is used instead. //
        When forceSmaller is False, the cache is not resized when the // new size is smaller. void setCacheSize (uInt nbuckets, Bool forceSmaller=True) { itsCube->setCacheSize (nbuckets, forceSmaller, True); } // Make a tile shape from the array shape to fit as closely as possible // the number of pixels in the tile. static IPosition makeTileShape (const IPosition& arrayShape, uInt nrPixelsPerTile = 32768); private: TSMCube* itsCube; TiledFileHelper* itsTSM; uInt itsLocalPixelSize; Bool itsWritable; DataType itsDataType; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/TiledFileHelper.cc000066400000000000000000000103721476623553700214160ustar00rootroot00000000000000//# TiledFileHelper.cc: Helper class for tiled access to an array in a file //# Copyright (C) 2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TiledFileHelper::TiledFileHelper (const String& fileName, const IPosition& shape, DataType dtype, const TSMOption& tsmOption, Bool writable, Bool bigEndian) : TiledStMan ("TiledFileHelper", std::max(0, tsmOption.maxCacheSizeMB()) * 1024*1024) { // TSM is used on an existing file. So set optional default accordingly. TSMOption tsmOpt(tsmOption); tsmOpt.fillOption (False); // Set info in parent TiledStMan object. setEndian (bigEndian); setTsmOption (tsmOpt); switch (dtype) { case TpBool: itsDesc.addColumn (ArrayColumnDesc ("DATA", shape, ColumnDesc::FixedShape)); break; case TpUChar: itsDesc.addColumn (ArrayColumnDesc ("DATA", shape, ColumnDesc::FixedShape)); break; case TpShort: itsDesc.addColumn (ArrayColumnDesc ("DATA", shape, ColumnDesc::FixedShape)); break; case TpInt: itsDesc.addColumn (ArrayColumnDesc ("DATA", shape, ColumnDesc::FixedShape)); break; case TpFloat: itsDesc.addColumn (ArrayColumnDesc ("DATA", shape, ColumnDesc::FixedShape)); break; case TpDouble: itsDesc.addColumn (ArrayColumnDesc ("DATA", shape, ColumnDesc::FixedShape)); break; case TpComplex: itsDesc.addColumn (ArrayColumnDesc ("DATA", shape, ColumnDesc::FixedShape)); break; case TpDComplex: itsDesc.addColumn (ArrayColumnDesc ("DATA", shape, ColumnDesc::FixedShape)); break; default: throw TableError ("TiledFileHelper: invalid data type"); } createDirArrColumn ("DATA", dtype, ""); TiledStMan::setup(0); fileSet_p[0] = new TSMFile (fileName, writable, tsmOpt); } TiledFileHelper::~TiledFileHelper() {} const TableDesc& TiledFileHelper::getDesc() const { return itsDesc; } String TiledFileHelper::dataManagerType() const { return "TiledFileHelper"; } DataManager* TiledFileHelper::clone() const { throw AipsError ("TileFileHelper::clone - not implemented"); } Bool TiledFileHelper::flush (AipsIO&, Bool) { throw AipsError ("TileFileHelper::flush - not implemented"); return False; } void TiledFileHelper::create64 (rownr_t) { throw AipsError ("TileFileHelper::create64 - not implemented"); } TSMCube* TiledFileHelper::getHypercube (rownr_t) { throw AipsError ("TileFileHelper::getHypercube - not implemented"); return 0; } TSMCube* TiledFileHelper::getHypercube (rownr_t, IPosition&) { throw AipsError ("TileFileHelper:getHypercube: - not implemented"); return 0; } void TiledFileHelper::readHeader (rownr_t, Bool) { throw AipsError ("TileFileHelper::readHeader - not implemented"); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/TiledFileHelper.h000066400000000000000000000067741476623553700212730ustar00rootroot00000000000000//# TiledFileHelper.h: Helper class for tiled access to an array in a file //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TILEDFILEHELPER_H #define TABLES_TILEDFILEHELPER_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //

        // Helper class for tiled access to an array in a file. // // // // // //# Classes you should understand before using this one. //
      • Description of Tiled Storage Manager in module file // Tables.h // // // TiledFileHelper is a helper class for class // TiledFileAccess. // It sets up a table description containing one array column // to make it possible to use the // tiled storage manager // to access an array in an arbitrary file. // // // This class was created to be able to read an image in a FITS file. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class TiledFileHelper : public TiledStMan { public: // Create a TiledFileHelper object. // Tell if the data is stored in big or little endian canonical format. TiledFileHelper (const String& fileName, const IPosition& shape, DataType dtype, const TSMOption&, Bool writable, Bool bigEndian); ~TiledFileHelper(); // Forbid copy constructor. TiledFileHelper (const TiledFileHelper&) = delete; // Forbid assignment. TiledFileHelper& operator= (const TiledFileHelper&) = delete; virtual const TableDesc& getDesc() const; TSMFile* file() { return fileSet_p[0]; } // Return the class name. virtual String dataManagerType() const; // These functions are pure virtual, but not needed here. // They throw an exception. // virtual DataManager* clone() const; virtual Bool flush (AipsIO&, Bool); virtual void create64 (rownr_t); virtual TSMCube* getHypercube (rownr_t); virtual TSMCube* getHypercube (rownr_t, IPosition&); virtual void readHeader (rownr_t, Bool); // private: TableDesc itsDesc; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/TiledShapeStMan.cc000066400000000000000000000410651476623553700214050ustar00rootroot00000000000000//# TiledShapeStMan.cc: Tiled Data Storage Manager using the shape as id //# Copyright (C) 1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Allocate an empty record to avoid reconstructing it over and over //# again when addRow is called many times. static Record emptyRecord; TiledShapeStMan::TiledShapeStMan() : TiledStMan (), nrUsedRowMap_p (0), lastHC_p (-1) {} TiledShapeStMan::TiledShapeStMan (const String& hypercolumnName, const IPosition& defaultTileShape, uInt64 maximumCacheSize) : TiledStMan (hypercolumnName, maximumCacheSize), defaultTileShape_p (defaultTileShape), nrUsedRowMap_p (0), lastHC_p (-1) {} TiledShapeStMan::TiledShapeStMan (const String& hypercolumnName, const Record& spec) : TiledStMan (hypercolumnName, 0), nrUsedRowMap_p (0), lastHC_p (-1) { if (spec.isDefined ("DEFAULTTILESHAPE")) { defaultTileShape_p = IPosition (spec.toArrayInt ("DEFAULTTILESHAPE")); } if (spec.isDefined ("MAXIMUMCACHESIZE")) { setPersMaxCacheSize (spec.asInt64 ("MAXIMUMCACHESIZE")); } } TiledShapeStMan::~TiledShapeStMan() {} DataManager* TiledShapeStMan::clone() const { TiledShapeStMan* smp = new TiledShapeStMan (hypercolumnName_p, defaultTileShape_p, maximumCacheSize()); return smp; } DataManager* TiledShapeStMan::makeObject (const String& group, const Record& spec) { TiledShapeStMan* smp = new TiledShapeStMan (group, spec); return smp; } String TiledShapeStMan::dataManagerType() const { return "TiledShapeStMan"; } Record TiledShapeStMan::dataManagerSpec() const { Record rec = TiledStMan::dataManagerSpec(); rec.define ("IndexSize", nrUsedRowMap_p); return rec; } IPosition TiledShapeStMan::defaultTileShape() const { return defaultTileShape_p; } Bool TiledShapeStMan::canAccessColumn() const { // The entire column can be accessed if all rows are in the same hypercube, // thus if there is 1 row map entry and the last value is #rows. return (nrUsedRowMap_p == 1 && rowMap_p[0] == nrrow_p-1); } TSMCube* TiledShapeStMan::singleHypercube() { if (nrUsedRowMap_p != 1 || rowMap_p[0] != nrrow_p-1) { throw (TSMError ("TiledShapeStMan: function on hypercolumn " + hypercolumnName_p + " cannot be done " "when it is using multiple hypercubes")); } return cubeSet_p[1]; } void TiledShapeStMan::setShape (rownr_t rownr, TSMCube*, const IPosition& shape, const IPosition& tileShape) { IPosition cubeShape = shape; uInt64 n = shape.nelements(); cubeShape.resize (n+1); cubeShape(n) = 0; // hypercube is extensible // Find a hypercube with given shape. Int index = findHypercube (cubeShape); // Extend hypercube when found. // Otherwise create a new one. if (index >= 0) { extendHypercube (rownr, index); }else{ addHypercube (rownr, cubeShape, tileShape); } // Clear the value record in the first (dummy) cube, since it may // contain coordinates defined before the shape was defined. cubeSet_p[0]->rwValueRecord() = emptyRecord; } Int TiledShapeStMan::findHypercube (const IPosition& shape) { // A hypercube matches when its shape matches. // Its last axis is excluded, because it represents the rows. uInt64 n = cubeSet_p.nelements(); for (uInt64 i=1; icubeShape(), size_t(nrdim_p-1))) { return i; } } return -1; } void TiledShapeStMan::setupCheck (const TableDesc& tableDesc, const Vector& dataNames) const { // The data columns may only contain arrays with the correct // dimensionality, which should be one less than the hypercube // dimensionality. Int ndim = nrdim_p - 1; for (uInt i=0; i 0) { throw (TSMError ("Dimensionality of column " + dataNames(i) + " should be one less than hypercolumn" " definition when used in TiledShapeStMan")); } } // There shouldn't be ID columns. if (idColSet_p.nelements() > 0) { throw TSMError("ID columns cannot be used with TiledShapeStMan"); } } void TiledShapeStMan::create64 (rownr_t nrrow) { // Set up the various things. setup(1); // Create a cubeset (with no file attached) for undefined cells. cubeSet_p.resize (1); cubeSet_p[0] = new TSMCube (this, 0, IPosition(), IPosition(), Record(), -1); // Add the rows for the given number of rows. addRow64 (nrrow); } Bool TiledShapeStMan::flush (AipsIO&, Bool fsync) { // Flush the caches. // Exit if nothing has changed. if (! flushCaches (fsync)) { return False; } // Create the header file and write data in it. AipsIO* headerFile = headerFileCreate(); headerFile->putstart ("TiledShapeStMan", 1); // Let the base class write its data. headerFilePut (*headerFile, cubeSet_p.nelements()); // Write the data from this object. *headerFile << defaultTileShape_p; *headerFile << nrUsedRowMap_p; putBlock (*headerFile, rowMap_p, Int(nrUsedRowMap_p)); putBlock (*headerFile, cubeMap_p, Int(nrUsedRowMap_p)); putBlock (*headerFile, posMap_p, Int(nrUsedRowMap_p)); headerFile->putend(); headerFileClose (headerFile); return True; } void TiledShapeStMan::readHeader (rownr_t tabNrrow, Bool firstTime) { // Open the header file and read data from it. AipsIO* headerFile = headerFileOpen(); headerFile->getstart ("TiledShapeStMan"); // Let the base class read and initialize its data. headerFileGet (*headerFile, tabNrrow, firstTime, 1); // Read the data for this object. *headerFile >> defaultTileShape_p; *headerFile >> nrUsedRowMap_p; getBlock (*headerFile, rowMap_p); getBlock (*headerFile, cubeMap_p); getBlock (*headerFile, posMap_p); headerFile->getend(); headerFileClose (headerFile); } void TiledShapeStMan::addRow64 (rownr_t nrow) { rownr_t oldnrrow = nrrow_p; nrrow_p += nrow; if (fixedCellShape_p.nelements() > 0) { for (rownr_t i=oldnrrow; ivalueRecord()); uInt ncube = cubeSet_p.nelements(); cubeSet_p.resize (ncube + 1); cubeSet_p[ncube] = hypercube; // Extend the hypercube. extendHypercube (rownr, ncube); } void TiledShapeStMan::extendHypercube (rownr_t rownr, uInt cubeNr) { TSMCube* hypercube = cubeSet_p[cubeNr]; uInt64 pos = hypercube->cubeShape()(nrdim_p-1); hypercube->extend (1, emptyRecord, coordColSet_p[nrdim_p - 1]); updateRowMap (cubeNr, pos, rownr); setDataChanged(); } void TiledShapeStMan::updateRowMap (uInt cubeNr, uInt pos, rownr_t rownr) { // Check if the row number is correct. if (rownr >= nrrow_p) { throw (TSMError ("TiledShapeStMan::updateRowMap: rownr is too high")); } // Determine the next row used and check (in debug mode) if it is right. rownr_t nextRow = 0; if (nrUsedRowMap_p > 0) { nextRow = 1 + rowMap_p[nrUsedRowMap_p-1]; } DebugAssert (nextRow <= nrrow_p, AipsError); // The row can be past the end of the rowMap. // In that case it is a new row which will be added. // If needed, intermediate zero references will also be added for // the new rows which do not have a shape yet. if (rownr >= nextRow) { if (cubeNr == 0) { return; // not really a new reference } // If the maps need to be extended, an extra entry is needed // if intermediate rows are needed. uInt nrext = 2; if (rownr == nextRow) { nrext = 1; // If this row is consecutive to the previous one, // only the maps need to be updated. if (nrUsedRowMap_p > 0) { uInt i = nrUsedRowMap_p-1; if (cubeNr == cubeMap_p[i] && pos == 1+posMap_p[i]) { rowMap_p[i]++; posMap_p[i]++; return; } } } // A new entry has to be inserted. // Extend the maps when needed. if (nrUsedRowMap_p + nrext > rowMap_p.nelements()) { uInt nrnew = rowMap_p.nelements() + 64; rowMap_p.resize (nrnew); cubeMap_p.resize (nrnew); posMap_p.resize (nrnew); } if (rownr > nextRow) { rowMap_p[nrUsedRowMap_p] = rownr-1; cubeMap_p[nrUsedRowMap_p] = 0; posMap_p[nrUsedRowMap_p] = 0; nrUsedRowMap_p++; } rowMap_p[nrUsedRowMap_p] = rownr; cubeMap_p[nrUsedRowMap_p] = cubeNr; posMap_p[nrUsedRowMap_p] = pos; nrUsedRowMap_p++; return; } // Some explanation about the maps. // rowMap gives the last row number for which the cubeMap applies // and for which the positions in the cube are consecutive. // Thus rowMap gives row intervals for which cubeMap and posMap apply. // cubeMap gives the index of the cube (cubenr 0 means no value). // posMap gives the position of the row in rowMap in the cube. // Previous rows are in the previous positions. // E.g. rowMap 5 10 15 // cubeMap 1 2 1 // posMap 5 4 10 // means: row 0-5 are in pos 0-5 of cube 1 // row 6-10 are in pos 0-4 of cube 2 // row 11-15 are in pos 6-10 of cube 1 // The row is not past the end. // Find the closest row number in the map // (returns index of entry equal or less to given one). Bool found; uInt index = binarySearchBrackets (found, rowMap_p, rownr, nrUsedRowMap_p); // Exit immediately if the cube and pos did not change. rownr_t diffRow = rowMap_p[index] - rownr; if (cubeNr == cubeMap_p[index] && pos == posMap_p[index] - diffRow) { return; } // Determine if the new entry is at the beginning or end of a row interval. // If so, determine if it matches previous or next entry. // To match, the cube has to be the same and the position has to // be consecutive. Bool atB = (rownr == 0 || (index > 0 && rownr-1 == rowMap_p[index-1])); Bool atE = found; Bool eqP = False; Bool eqN = False; if (atE && index+1 < nrUsedRowMap_p) { uInt fpos = posMap_p[index+1] - (rowMap_p[index+1] - rowMap_p[index]); eqN = (cubeNr == cubeMap_p[index+1] && pos == fpos); } if (atB && index > 0) { eqP = (cubeNr == cubeMap_p[index-1] && pos == 1+posMap_p[index-1]); } if (atB && atE) { // We have a single entry, so update the maps directly. cubeMap_p[index] = cubeNr; posMap_p[index] = pos; // If it equals previous and/or next, combine maps by moving // the entries to the left. uInt nm = 0; if (eqN) { nm += 1; } if (eqP) { nm += 1; index -= 1; } if (nm > 0) { uInt nr = nrUsedRowMap_p - (index+nm); if (nr > 0) { objmove (&(rowMap_p[index]), &(rowMap_p[index+nm]), nr); objmove (&(cubeMap_p[index]), &(cubeMap_p[index+nm]), nr); objmove (&(posMap_p[index]), &(posMap_p[index+nm]), nr); } nrUsedRowMap_p -= nm; } return; } // Not a single entry, so we may need to do more work. // If equal previous or next, only the maps need to be updated. if (eqP) { rowMap_p[index-1]++; posMap_p[index-1]++; return; } if (eqN) { rowMap_p[index]--; posMap_p[index]--; return; } // It is getting more and more complicated. // A new entry has to be inserted (or 2 if in the middle). // So shift to the right (after extending the maps when needed). uInt nm = (atB || atE ? 1 : 2); if (nrUsedRowMap_p + nm > rowMap_p.nelements()) { uInt nrnew = rowMap_p.nelements() + 64; rowMap_p.resize (nrnew); cubeMap_p.resize (nrnew); posMap_p.resize (nrnew); } uInt nr = nrUsedRowMap_p - index; if (nr > 0) { objmove (&(rowMap_p[index+nm]), &(rowMap_p[index]), nr); objmove (&(cubeMap_p[index+nm]), &(cubeMap_p[index]), nr); objmove (&(posMap_p[index+nm]), &(posMap_p[index]), nr); } nrUsedRowMap_p += nm; if (!atB) { if (atE) { rowMap_p[index]--; posMap_p[index]--; } else { posMap_p[index] -= diffRow+1; rowMap_p[index] = rownr-1; } index++; } rowMap_p[index] = rownr; cubeMap_p[index] = cubeNr; posMap_p[index] = pos; } TSMCube* TiledShapeStMan::getHypercube (rownr_t rownr) { if (rownr >= nrrow_p) { throw (TSMError ("getHypercube: rownr is too high")); } // Get the hypercube. if (nrUsedRowMap_p == 0 || rownr > rowMap_p[nrUsedRowMap_p-1]) { return cubeSet_p[0]; } // Test if the row number is in the most recently used interval. // See description in function updateRowMap (about line 340) // how intervals are defined. if (lastHC_p < 0 || rownr > rowMap_p[lastHC_p] || (lastHC_p > 0 && rownr <= rowMap_p[lastHC_p-1])) { Bool found; lastHC_p = binarySearchBrackets (found, rowMap_p, rownr, nrUsedRowMap_p); } return cubeSet_p[cubeMap_p[lastHC_p]]; } TSMCube* TiledShapeStMan::getHypercube (rownr_t rownr, IPosition& position) { if (rownr >= nrrow_p) { throw (TSMError ("getHypercube: rownr is too high")); } // Get the hypercube. if (nrUsedRowMap_p == 0 || rownr > rowMap_p[nrUsedRowMap_p-1]) { TSMCube* hypercube = cubeSet_p[0]; const IPosition& shp = hypercube->cubeShape(); if (position.nelements() != shp.nelements()) { position.resize (shp.nelements()); } position = shp; return hypercube; } // Test if the row number is in the most recently used interval. // See description in function updateRowMap (about line 340) // how intervals are defined. if (lastHC_p < 0 || rownr > rowMap_p[lastHC_p] || (lastHC_p > 0 && rownr <= rowMap_p[lastHC_p-1])) { Bool found; lastHC_p = binarySearchBrackets (found, rowMap_p, rownr, nrUsedRowMap_p); } TSMCube* hypercube = cubeSet_p[cubeMap_p[lastHC_p]]; const IPosition& shp = hypercube->cubeShape(); if (position.nelements() != shp.nelements()) { position.resize (shp.nelements()); } position = shp; // Add the starting position of the hypercube chunk the row is in. if (position.nelements() > 0) { position(nrdim_p - 1) = posMap_p[lastHC_p] - (rowMap_p[lastHC_p] - rownr); } return hypercube; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/TiledShapeStMan.h000066400000000000000000000316071476623553700212500ustar00rootroot00000000000000//# TiledShapeStMan.h: Tiled Data Storage Manager using the shape as id //# Copyright (C) 1998,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TILEDSHAPESTMAN_H #define TABLES_TILEDSHAPESTMAN_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // Tiled Data Storage Manager using the shape as id. // // // // // //# Classes you should understand before using this one. //
      • TiledStMan //
      • TSMCube //
      • ROTiledStManAccessor // for a discussion of the maximum cache size // // // TiledShapeStMan is the Tiled Storage Manager where the shape is used as id // to support variable shaped arrays. // // // TiledShapeStMan is a derivation from TiledStMan, the abstract // tiled storage manager class. A description of the basics // of tiled storage managers is given in the // Tables module description. //

        // TiledShapeStMan creates a hypercube for each different shape of // the data arrays. For example, if a table contains line and continuum // data of an observation, it results in 2 hypercubes. // TiledShapeStMan does it all automatically, so it is much easier to use // than class TiledDataStMan. //
        TiledShapeStMan is meant for columns with not too many different // shapes, otherwise looking for a matching hypercube may take too long. // When many different shapes are used, class // TiledCellStMan // should be used instead. // // TiledShapeStMan has the following (extra) properties: //

          //
        • It can only handle columns containing arrays, thus not scalars. //
        • Addition of a row sets the appropriate data arrays // in that row temporarily to an empty hypercube. // However, if the data arrays have a fixed shape, the // shape is known and the hypercube can be generated immediately. // Note that for a fixed shape column, one can as well use class // TiledColumnStMan. //
        • When the shape of the data array in a row is set for the // first time, it is known which hypercube should be used or // if a new hypercube has to be created. //
          Note that is is not possible to change the shape of an array. // If that is needed, TiledCellStMan should be used instead. //
          Note that a hypercolumn has a given dimensionality, so each // data cell in the hypercolumn has to match that dimensionality. //
        • Although there are multiple hypercubes, an id value is not needed. // The shape serves as the id value. //
        • Coordinates for the hypercubes can be defined and (of course) // their shapes have to match the hypercube shape. // Their values have to be put explicitly (so it is not possible // to define them via an addHypercube call like in // TiledDataStMan). // It is possible to put the coordinate values before or after // the shape of the data array in that row is defined. //
        • It is possible to define a (default) tile shape in the // TiledShapeStMan constructor. When setting the shape of the // array in a row (using // ArrayColumn::setShape), it is possible to override // that default for the hypercube in this particular row. // However, since the tile shape is only used when creating // a hypercube, using an overriding tile shape makes only // sense when a given array shape is used for the first time. // Note that the dimensionality of the hypercube is one higher // than the dimensionality of the data arrays (since the hypercube // contains multiple rows). It means that the number of values in // tile shape can be one more than the number of axes in the data // array. The last tile shape value defaults to 1; the other // tile shape values have to be defined. //
        //
        // // TiledDataStMan proved to be very powerful, but also a bit cumbersome // to use because a few special functions need to be called. // TiledShapeStMan alleviates that problem. // // // // // Define the table description and the columns in it. // TableDesc td ("", "1", TableDesc::Scratch); // td.addColumn (ArrayColumnDesc ("RA", 1)); // td.addColumn (ArrayColumnDesc ("Dec", 1)); // td.addColumn (ScalarColumnDesc ("Velocity")); // td.addColumn (ArrayColumnDesc ("Image", 2)); // // Define the 3-dim hypercolumn with its data and coordinate columns. // // Note that its dimensionality must be one higher than the dimensionality // // of the data cells. // td.defineHypercolumn ("TSMExample", // 3, // stringToVector ("Image"), // stringToVector ("RA,Dec,Velocity")); // // Now create a new table from the description. // SetupNewTable newtab("tTiledShapeStMan_tmp.data", td, Table::New); // // Create a TiledShapeStMan storage manager for the hypercolumn // // and bind the columns to it. // // The (default) tile shape has to be specified for the storage manager. // TiledShapeStMan sm1 ("TSMExample", IPosition(3,16,32,32)); // newtab.bindAll (sm1); // // Create the table. // Table table(newtab); // // Define the values for the coordinates of the hypercube. // Vector raValues(512); // Vector DecValues(512); // indgen (raValues); // indgen (decValues, float(100)); // ArrayColumn ra (table, "RA"); // ArrayColumn dec (table, "Dec"); // ScalarColumn velocity (table, "Velocity"); // ArrayColumn image (table, "Image"); // Cube imageValues(IPosition(2,512,512)); // indgen (imageValues); // // Write some data into the data columns. // for (uInt i=0; i<64; i++) { // table.addRow(); // image.put (i, imageValues); // ra.put (i, raValues); // dec.put (i, decValues); // velocity.put (i, float(i)); // } // // Note that in this example the same shape is used for each row, // but it could have been different. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class TiledShapeStMan : public TiledStMan { public: // Create a TiledShapeStMan storage manager for the hypercolumn // with the given name. // The hypercolumn name is also the name of the storage manager. // The given maximum cache size (default is unlimited) is persistent, // thus will be reused when the table is read back. Note that the class // ROTiledStManAccessor // allows one to overwrite the maximum cache size temporarily. //
        The constructor taking a Record expects fields in the record with // the name of the arguments in uppercase. If not defined, their // default value is used. // TiledShapeStMan (const String& hypercolumnName, const IPosition& defaultTileShape, uInt64 maximumCacheSize = 0); TiledShapeStMan (const String& hypercolumnName, const Record& spec); // ~TiledShapeStMan(); // Forbid copy constructor. TiledShapeStMan (const TiledShapeStMan&) = delete; // Forbid assignment. TiledShapeStMan& operator= (const TiledShapeStMan&) = delete; // Clone this object. // It does not clone TSMColumn objects possibly used. virtual DataManager* clone() const; // Get the type name of the data manager (i.e. TiledShapeStMan). virtual String dataManagerType() const; // Return a record containing data manager specifications and info. virtual Record dataManagerSpec() const; // TiledShapeStMan can access a column if there are 2 hypercubes // and the first one is empty. virtual Bool canAccessColumn() const; // Test if only one hypercube is used by this storage manager. // If not, throw an exception. Otherwise return the hypercube. virtual TSMCube* singleHypercube(); // Set the shape and tile shape of the given hypercube. // It is used when the first row in a new hypercube is written. // If needed it adds a dimension to the shape, which reflects the // row dimension. The tile shape in that dimension is by default 1. virtual void setShape (rownr_t rownr, TSMCube* hypercube, const IPosition& shape, const IPosition& tileShape); // Make the object from the type name string. // This function gets registered in the DataManager "constructor" map. static DataManager* makeObject (const String& dataManagerType, const Record& spec); private: // Create a TiledShapeStMan. // This constructor is private, because it should only be used // by makeObject. TiledShapeStMan(); // Get the default tile shape. virtual IPosition defaultTileShape() const; // Add rows to the storage manager. void addRow64 (rownr_t nrrow); // Find the hypercube for the given shape. // It returns -1 when not found. Int findHypercube (const IPosition& shape); // Add a hypercube. // The number of rows in the table must be large enough to // accommodate this hypercube. // The possible id values must be given in the record, while // coordinate values are optional. The field names in the record // should match the coordinate and id column names. // The last dimension in the cube shape can be zero, indicating that // the hypercube is extensible. void addHypercube (rownr_t rownr, const IPosition& cubeShape, const IPosition& tileShape); // Extend the hypercube with the given number of elements in // the last dimension. // The record should contain the id values (to get the correct // hypercube) and optionally coordinate values for the elements added. void extendHypercube (rownr_t rownr, uInt cubeNr); // Get the hypercube in which the given row is stored. virtual TSMCube* getHypercube (rownr_t rownr); // Get the hypercube in which the given row is stored. // It also returns the position of the row in that hypercube. virtual TSMCube* getHypercube (rownr_t rownr, IPosition& position); // Check if the hypercolumn definition fits this storage manager. virtual void setupCheck (const TableDesc& tableDesc, const Vector& dataNames) const; // Flush and optionally fsync the data. // It returns a True status if it had to flush (i.e. if data have changed). virtual Bool flush (AipsIO&, Bool fsync); // Let the storage manager create files as needed for a new table. // This allows a column with an indirect array to create its file. virtual void create64 (rownr_t nrrow); // Read the header info. virtual void readHeader (rownr_t nrrow, Bool firstTime); // Update the map of row numbers to cube number plus offset. void updateRowMap (uInt cubeNr, uInt pos, rownr_t rownr); // Extend the map of row numbers to cube number plus offset // will new empty entries. void extendRowMap (rownr_t nrow); //# Declare the data members. // The default tile shape. IPosition defaultTileShape_p; // The map of row number to cube and position in cube. Block rowMap_p; Block cubeMap_p; Block posMap_p; // The nr of elements used in the map blocks. uInt nrUsedRowMap_p; // The last hypercube found. Int lastHC_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/TiledStMan.cc000066400000000000000000001103521476623553700204200ustar00rootroot00000000000000//# TiledStMan.cc: Storage manager for tables using tiled hypercubes //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TiledStMan::TiledStMan () : DataManager (), nrrow_p (0), fileSet_p (1, static_cast(0)), persMaxCacheSize_p(0), maxCacheSize_p (0), nrdim_p (0), nrCoordVector_p (0), dataChanged_p (False) {} TiledStMan::TiledStMan (const String& hypercolumnName, uInt maximumCacheSize) : DataManager (), hypercolumnName_p (hypercolumnName), nrrow_p (0), fileSet_p (1, static_cast(0)), persMaxCacheSize_p(maximumCacheSize), maxCacheSize_p (maximumCacheSize), nrdim_p (0), nrCoordVector_p (0), dataChanged_p (False) {} TiledStMan::~TiledStMan() { uInt i; for (i=0; i weight(hypercubeShape.nelements()); weight = double(1); Vector tol(hypercubeShape.nelements()); tol = tolerance; return makeTileShape (hypercubeShape, weight, tol, nrPixelsPerTile); } IPosition TiledStMan::makeTileShape (const IPosition& hypercubeShape, const Vector& weight, const Vector& tolerance, uInt64 nrPixelsPerTile) { uInt nrdim = hypercubeShape.nelements(); if (weight.nelements() != nrdim || tolerance.nelements() != nrdim) { throw (TSMError ("makeTileShape: nelements mismatch")); } double nrLeft = nrPixelsPerTile; Vector tmpShape(nrdim); IPosition tileShape(nrdim, 0); uInt i; // Iterate until the tile shape is set nicely. // This is needed to prevent tile shape dimensions from underflow // or overflow. while (True) { double prod = 1; uInt n = 0; for (i=0; i 1) { diff = hypercubeShape(i) / diff; } if (maxIndex < 0 || diff < maxDiff) { maxDiff = diff; maxIndex = i; } } } // If there is no underflow/overflow we can copy the dimensions // and exit. if (maxDiff >= 1) { for (i=0; i maxShape(i)) { Int64 sav = minShape(i); minShape(i) = maxShape(i); maxShape(i) = sav; } if (minShape(i) < 1) { minShape(i) = 1; } if (maxShape(i) > hypercubeShape(i)) { maxShape(i) = hypercubeShape(i); } cubeSpace *= hypercubeShape(i); } // Find the shapes on each axis that will be tried. Block nval(nrdim, uInt64(0)); PtrBlock*> values(nrdim); for (i=0; i (maxShape(i) - minShape(i) + 1); // First find exactly fitting shapes. for (Int64 j=minShape(i); j<=maxShape(i); j++) { if (hypercubeShape(i) % j == 0) { (*values[i])[nval[i]] = j; nval[i]++; } } // If none available, use all possible shapes within half the range. if (nval[i] == 0) { for (Int64 j=(tileShape(i)+minShape(i))/2; j<=(tileShape(i)+maxShape(i))/2; j++) { (*values[i])[nval[i]] = j; nval[i]++; } } } // Now calculate the cost for all the possibilities. // Take the one with the lowest cost. Block ndone (nrdim, uInt64(0)); IPosition tshape (nrdim); for (i=0; icubeShape().nelements() > 0) { Record srec; srec.define ("CubeShape", cubeSet_p[i]->cubeShape().asVector()); srec.define ("TileShape", cubeSet_p[i]->tileShape().asVector()); srec.define ("CellShape", cubeSet_p[i]->cellShape().asVector()); srec.define ("BucketSize", Int(cubeSet_p[i]->bucketSize())); srec.defineRecord ("ID", cubeSet_p[i]->valueRecord()); subrec.defineRecord (nrrec++, srec); } } rec.defineRecord ("HYPERCUBES", subrec); rec.define ("SEQNR", sequenceNr()); return rec; } Record TiledStMan::getProperties() const { Record rec; rec.define ("MaxCacheSize", Int(maxCacheSize_p)); return rec; } void TiledStMan::setProperties (const Record& rec) { if (rec.isDefined("MaxCacheSize")) { setMaximumCacheSize (rec.asInt("MaxCacheSize")); } } void TiledStMan::setShape (rownr_t, TSMCube*, const IPosition&, const IPosition&) { throw (TSMError ("setShape is not possible for TSM " + hypercolumnName_p)); } void TiledStMan::reopenRW() { for (uInt i=0; ibucketFile()->setRW(); } } } void TiledStMan::deleteManager() { for (uInt i=0; iclearCache (False); } } for (uInt i=0; ibucketFile()->remove(); } } // Remove the header file. /// removeFile(); DOos::remove (fileName(), False, False); } void TiledStMan::setMaximumCacheSize (uInt nMiB) { maxCacheSize_p = nMiB; } Bool TiledStMan::canChangeShape() const { return False; } Bool TiledStMan::canAccessColumn() const { return (nhypercubes() == 1); } Bool TiledStMan::hasMultiFileSupport() const { return True; } //# Does the storage manager allow to add rows? (yes) Bool TiledStMan::canAddRow() const { return True; } TSMCube* TiledStMan::makeTSMCube (TSMFile* file, const IPosition& cubeShape, const IPosition& tileShape, const Record& values, Int64 fileOffset) { TSMCube* hypercube; if (tsmOption().option() == TSMOption::MMap) { //cout << "mmapping TSM1" << endl; AlwaysAssert (file->bucketFile()->isMapped(), AipsError); hypercube = new TSMCubeMMap (this, file, cubeShape, tileShape, values, fileOffset); } else if (tsmOption().option() == TSMOption::Buffer) { //cout << "buffered TSM1" << endl; AlwaysAssert (file->bucketFile()->isBuffered(), AipsError); hypercube = new TSMCubeBuff (this, file, cubeShape, tileShape, values, fileOffset); } else { //cout << "caching TSM1" << endl; AlwaysAssert (file->bucketFile()->isCached(), AipsError); hypercube = new TSMCube (this, file, cubeShape, tileShape, values, fileOffset); } return hypercube; } TSMCube* TiledStMan::getTSMCube (uInt hypercube) { if (hypercube >= nhypercubes() || cubeSet_p[hypercube] == 0) { throw (AipsError ("TiledStMan::getTSMCube - hypercube nr " + String::toString(hypercube) + " does not exist in " + hypercolumnName_p)); } return cubeSet_p[hypercube]; } const IPosition& TiledStMan::hypercubeShape (rownr_t rownr) const { return getHypercube(rownr)->cubeShape(); } const IPosition& TiledStMan::tileShape (rownr_t rownr) const { return getHypercube(rownr)->tileShape(); } uInt64 TiledStMan::bucketSize (rownr_t rownr) const { return getHypercube(rownr)->bucketSize(); } uInt TiledStMan::cacheSize (rownr_t rownr) const { return getHypercube(rownr)->cacheSize(); } uInt TiledStMan::calcCacheSize (rownr_t rownr, const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath) const { // Calculate the cache size for the given hypercube. return getHypercube(rownr)->calcCacheSize (sliceShape, windowStart, windowLength, axisPath); } void TiledStMan::setCacheSize (rownr_t rownr, const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath, Bool forceSmaller) { // Set the cache size for the given hypercube. getHypercube(rownr)->setCacheSize (sliceShape, windowStart, windowLength, axisPath, forceSmaller, True); } void TiledStMan::setCacheSize (rownr_t rownr, uInt nbuckets, Bool forceSmaller) { // Set the cache size (in buckets) for the given hypercube. TSMCube* hypercube = getHypercube(rownr); hypercube->setCacheSize (nbuckets, forceSmaller, True); } void TiledStMan::setHypercubeCacheSize (uInt hypercube, uInt nbuckets, Bool forceSmaller) { // Set the cache size (in buckets) for the given hypercube. TSMCube* tsmCube = getTSMCube (hypercube); tsmCube->setCacheSize (nbuckets, forceSmaller, True); } Bool TiledStMan::userSetCache (rownr_t rownr) const { return getHypercube(rownr)->userSetCache(); } void TiledStMan::emptyCaches() { for (uInt i=0; iemptyCache(); } } } void TiledStMan::showCacheStatistics (ostream& os) const { for (uInt i=0; ishowCacheStatistics (os); } } } TSMCube* TiledStMan::singleHypercube() { if (cubeSet_p.nelements() != 1 || cubeSet_p[0] == 0) { throw (TSMError ("TiledStMan: function on hypercolumn " + hypercolumnName_p + " cannot be done " "when it is using multiple hypercubes")); } return cubeSet_p[0]; } uInt64 TiledStMan::getLengthOffset (uInt64 nrPixels, Block& dataOffset, Block& localOffset, uInt& localTileLength) const { localTileLength = 0; uInt64 length = 0; uInt nrcol = dataCols_p.nelements(); dataOffset.resize (nrcol); localOffset.resize (nrcol); for (uInt i=0; idataLength (nrPixels); localTileLength += nrPixels * dataCols_p[i]->localPixelSize(); } return length; } void TiledStMan::readTile (char* local, const Block& localOffset, const char* external, const Block& externalOffset, uInt nrPixels) { uInt nr = dataCols_p.nelements(); for (uInt i=0; ireadTile (local + localOffset[i], external + externalOffset[i], nrPixels); } } void TiledStMan::writeTile (char* external, const Block& externalOffset, const char* local, const Block& localOffset, uInt nrPixels) { uInt nr = dataCols_p.nelements(); for (uInt i=0; iwriteTile (external + externalOffset[i], local + localOffset[i], nrPixels); } } DataManagerColumn* TiledStMan::makeScalarColumn (const String& columnName, int dataType, const String& dataTypeId) { return makeIndArrColumn (columnName, dataType, dataTypeId); } DataManagerColumn* TiledStMan::makeDirArrColumn (const String& columnName, int dataType, const String& dataTypeId) { return makeIndArrColumn (columnName, dataType, dataTypeId); } DataManagerColumn* TiledStMan::makeIndArrColumn (const String& columnName, int dataType, const String&) { //# Check if data type is not TpOther. throwDataTypeOther (columnName, dataType); //# Extend colSet_p block if needed. if (ncolumn() >= colSet_p.nelements()) { colSet_p.resize (colSet_p.nelements() + 32); } TSMColumn* colp = new TSMColumn (this, dataType, columnName); colSet_p[ncolumn()] = colp; return colp; } int TiledStMan::coordinateDataType (const String& columnName) const { for (uInt i=0; icolumnName()) { return coordColSet_p[i]->dataType(); } } } throw (TSMError ("coordinateDataType: column " + columnName + " is unknown")); return 0; } // Get the proper array data type. int TiledStMan::arrayDataType (int dataType) const { switch (dataType) { case TpBool: return TpArrayBool; case TpChar: return TpArrayChar; case TpUChar: return TpArrayUChar; case TpShort: return TpArrayShort; case TpUShort: return TpArrayUShort; case TpInt: return TpArrayInt; case TpUInt: return TpArrayUInt; case TpInt64: return TpArrayInt64; case TpFloat: return TpArrayFloat; case TpDouble: return TpArrayDouble; case TpComplex: return TpArrayComplex; case TpDComplex: return TpArrayDComplex; case TpString: return TpArrayString; } return dataType; } IPosition TiledStMan::defaultTileShape() const { return IPosition(); } Bool TiledStMan::canReallocateColumns() const { return True; } DataManagerColumn* TiledStMan::reallocateColumn (DataManagerColumn* column) { for (uInt i=0; iunlink(); delete ptr; return colSet_p[i]; } } // The column is not part of this storage manager, so return column itself. return column; } void TiledStMan::setup (Int extraNdim) { uInt i; // Get the description of the hypercolumn. Vector dataNames; Vector coordNames; Vector idNames; const TableDesc& tableDesc = getDesc(); if (extraNdim < 0 || tableDesc.isHypercolumn (hypercolumnName_p)) { // If defined as a hypercolumn get the columns in it. nrdim_p = tableDesc.hypercolumnDesc (hypercolumnName_p, dataNames, coordNames, idNames); // Determine the number of vector coordinates. // This is the dimensionality of the cells. nrCoordVector_p = tableDesc.columnDesc(dataNames(0)).ndim(); } else { // No hypercolumn definition; assume all columns are data columns. Int ndim = 0; dataNames.resize (ncolumn()); for (uInt i=0; icolumnName(); Int nd = tableDesc.columnDesc(dataNames(i)).ndim(); if (nd > 0) { if (ndim == 0) { ndim = nd; } else if (nd != ndim) { throw TSMError ("TiledStMan: dimensionality of column " + dataNames(i) + " mismatches other columns"); } } } if (ndim == 0) { throw TSMError ("TiledStMan: unknown dimensionality for column " + dataNames(0)); } nrCoordVector_p = ndim; nrdim_p = ndim + extraNdim; } // Check if the required columns are bound // and get the pointers to those columns. dataCols_p.resize (dataNames.nelements()); dataColSet_p.resize (dataNames.nelements()); coordColSet_p.resize (nrdim_p); idColSet_p.resize (idNames.nelements()); uInt nrDataBound = getBindings (dataNames, dataColSet_p, True); uInt nrCoordBound = getBindings (coordNames, coordColSet_p, False); uInt nrIdBound = getBindings (idNames, idColSet_p, True); // Check if no non-TiledStMan columns are bound. if (nrDataBound + nrCoordBound + nrIdBound != ncolumn()) { throw (TSMError ("non-TiledStMan columns bound in " + hypercolumnName_p)); } // Let the derived class do some more checks. setupCheck (tableDesc, dataNames); // Find the first fixed shape data column. // Check if FixedShape column shapes of data and coordinate columns match. for (i=0; ishapeColumn(); if (fixedCellShape_p.nelements() > 0) { break; } } checkShapeColumn (fixedCellShape_p); // Construct the various TSMColumn objects. for (i=0; imakeCoordColumn (i); } } for (i=0; imakeIdColumn(); } uInt nrd = dataColSet_p.nelements(); PtrBlock dataColSet(nrd); for (i=0; imakeDataColumn(); } // Organize the pixel offset in the data columns in descending // order of external pixel length. // The sort is stable, so equal lengths will always occur in // the same order. // In that way we are sure that their data are aligned in a tile // (which may be needed for TSMCube::accessLine). Block lengths(nrd); for (i=0; itilePixelSize(); } Vector inx; GenSortIndirect::sort (inx, lengths, nrd, Sort::Descending); // Rearrange the objects and set their column number. // In this way function setLengths will behave correctly. for (i=0; isetColumnNumber (i); dataColSet_p[i] = dataCols_p[i]; } } const TableDesc& TiledStMan::getDesc() const { return table().tableDesc(); } void TiledStMan::setupCheck (const TableDesc&, const Vector&) const {} void TiledStMan::checkCubeShape (const TSMCube* hypercube, const IPosition& cubeShape) const { // Check if the dimensionalities are correct. if (cubeShape.nelements() != nrdim_p) { throw (TSMError ("addHypercube dimensionality mismatch in " + hypercolumnName_p)); } // Check if all dimensions are > 0. // Only the last one in shape can be 0 (meaning extensible). for (uInt i=0; ishapeColumn(); for (uInt j=0; jcolumnName())); } } } for (i=0; ishapeColumn(); if (shapeColumn.nelements() > 0) { if (shape(i) != shapeColumn(0)) { throw (TSMError ("Mismatch in fixed shape of coordinate column " + coordColSet_p[i]->columnName())); } } } } } void TiledStMan::checkCoordinatesShapes (const TSMCube* hypercube, const IPosition& cubeShape) const { //# Check for all coordinates if their length (if defined) //# matches the hypercube shape. for (uInt i=0; icoordinateSize (coordColSet_p[i]->columnName()); if (size != 0 && size != cubeShape(i)) { throw (TSMError ("Mismatch in shape of coordinate column " + coordColSet_p[i]->columnName())); } } } } void TiledStMan::initCoordinates (TSMCube* hypercube) { for (uInt i=0; iextendCoordinates (Record(), coordColSet_p[i]->columnName(), hypercube->cubeShape()(i)); dataChanged_p = True; } } } uInt TiledStMan::getBindings (const Vector& columnNames, PtrBlock& colSet, Bool mustExist) const { colSet = static_cast(0); uInt nrfound = 0; uInt j; Bool found = False; for (uInt i=0; icolumnName()) { colSet[i] = colSet_p[j]; found = True; nrfound++; break; } } if (!found && mustExist) { throw (TSMError ("TiledStMan column " + columnNames(i) + " is not bound")); } } return nrfound; } void TiledStMan::checkAddHypercube (const IPosition& cubeShape, const Record& values) const { //# Check if the cube shape is correct. checkCubeShape (0, cubeShape); // Check whether all id and coordinate values are given correctly. checkValues (idColSet_p, values); checkCoordinates (coordColSet_p, cubeShape, values); // Check whether no double id values are given. if (getCubeIndex (values) >= 0) { throw (TSMError ("addHypercube with already existing id values in " + hypercolumnName_p)); } } TSMCube* TiledStMan::makeHypercube (const IPosition& cubeShape, const IPosition& tileShape, const Record& values) { dataChanged_p = True; // Pick a TSMFile object for the hypercube. // Non-extensible cubes share the first file; others get their own file. uInt filenr = 0; if (cubeShape(nrdim_p - 1) == 0) { filenr = fileSet_p.nelements(); fileSet_p.resize (filenr + 1); fileSet_p[filenr] = 0; } // Create the file when needed. if (fileSet_p[filenr] == 0) { createFile (filenr); } // Create a TSMCube object. // Its data will be written at the end of the file. return makeTSMCube (fileSet_p[filenr], cubeShape, tileShape, values); } void TiledStMan::createFile (uInt index) { TSMFile* file = new TSMFile (this, index, tsmOption(), multiFile()); fileSet_p[index] = file; } Int TiledStMan::getCubeIndex (const Record& idValues) const { // When there are no id columns, return the one and single hypercube // (or -1 if no one created yet). if (idColSet_p.nelements() == 0) { if (cubeSet_p.nelements() == 0) { return -1; } return 0; } // Look if a hypercube matches the id values. for (uInt i=0; imatches (idColSet_p, idValues)) { return i; } } } return -1; } void TiledStMan::checkValues (const PtrBlock& colSet, const Record& values) const { // Check if all values are given and if their data types match. for (uInt i=0; icolumnName(); if (! values.isDefined (name)) { throw (TSMError ("No value given for column " + name)); } if (values.dataType(name) != colSet[i]->dataType()) { throw (TSMError ("Data type mismatch for column " + name)); } } } } void TiledStMan::checkCoordinates (const PtrBlock& coordColSet, const IPosition& cubeShape, const Record& values) const { // Check if the coordinates data types and shapes are correct, // i.e. if the coordinates shapes match the hypercube shape. for (uInt i=0; icolumnName(); if (values.isDefined (name)) { int dataType = arrayDataType (coordColSet[i]->dataType()); if (values.dataType(name) != dataType) { throw (TSMError ("Data type mismatch for coordinate " + name)); } IPosition shape = values.shape (name); if (shape.nelements() != 1) { throw (TSMError ("Values of coordinate " + name + " do not form a vector")); } if (shape(0) != cubeShape(i)) { throw (TSMError ("Shape mismatch for coordinate " + name)); } } } } } rownr_t TiledStMan::addedNrrow (const IPosition& shape, uInt incrInLastDim) const { rownr_t nrrowAdded = 1; for (uInt i=nrCoordVector_p; iflushCache(); } } if (fsync) { for (i=0; ibucketFile()->fsync(); } } } return True; } AipsIO* TiledStMan::headerFileCreate() { return new AipsIO (fileName(), ByteIO::New, 16384, multiFile()); } AipsIO* TiledStMan::headerFileOpen() { return new AipsIO (fileName(), ByteIO::Old, 16384, multiFile()); } void TiledStMan::headerFilePut (AipsIO& headerFile, uInt64 nrCube) { // The endian switch is a new feature. So only put it if little endian // is used. In that way older software can read newer tables. // Similarly, use older version if number of rows less than maxUint. Bool useNewVersion = False; if (nrrow_p > MAXROWNR32 || persMaxCacheSize_p != uInt(persMaxCacheSize_p)) { headerFile.putstart ("TiledStMan", 3); headerFile << asBigEndian(); useNewVersion = True; } else if (asBigEndian()) { headerFile.putstart ("TiledStMan", 1); } else { headerFile.putstart ("TiledStMan", 2); headerFile << asBigEndian(); } //# Write StMan sequence number, the number of rows and columns, //# and the column data types. //# This is only done to check it when reading back. headerFile << sequenceNr(); if (useNewVersion) { headerFile << nrrow_p; } else { headerFile << uInt(nrrow_p); } headerFile << ncolumn(); for (uInt i=0; idataType(); } headerFile << hypercolumnName_p; if (useNewVersion) { headerFile << persMaxCacheSize_p; } else { headerFile << uInt(persMaxCacheSize_p); } headerFile << nrdim_p; // nrfile and nrcube can never exceed nrrow, // so it's safe to use uInt for old version. if (useNewVersion) { headerFile << uInt64(fileSet_p.nelements()); } else { headerFile << uInt(fileSet_p.nelements()); } for (uInt64 i=0; iputObject (headerFile); } } if (useNewVersion) { headerFile << nrCube; } else { headerFile << uInt(nrCube); } for (uInt64 i=0; iputObject (headerFile); } headerFile.putend(); } uInt TiledStMan::headerFileGet (AipsIO& headerFile, rownr_t tabNrrow, Bool firstTime, Int extraNdim) { nrrow_p = tabNrrow; uInt version = headerFile.getstart ("TiledStMan"); Bool bigEndian = True; if (version >= 2) { headerFile >> bigEndian; } if (bigEndian != asBigEndian()) { throw DataManError("Endian flag in TSM mismatches the table flag"); } //# Get and check the number of rows and columns and the column types. rownr_t nrrow; uInt nrcol, seqnr; int dtype; headerFile >> seqnr; if (version >= 3) { headerFile >> nrrow; } else { uInt nrrowOld; headerFile >> nrrowOld; nrrow = nrrowOld; } headerFile >> nrcol; if (seqnr != sequenceNr() || nrcol != ncolumn()) { //# Temporary hack to fix a corrupted table. //#if (sequenceNr() != 7) { throw (DataManInternalError ("TiledStMan::headerFileGet: mismatch in seqnr,#col")); //#} } if (nrrow != nrrow_p) { #if defined(TABLEREPAIR) cerr << "TiledStMan::headerFileGet: mismatch in #row (expected " << nrrow_p << ", found " << nrrow << ")" << endl; dataChanged_p = True; #else throw (DataManInternalError ("TiledStMan::headerFileGet: mismatch in #row; expected " + String::toString(nrrow_p) + ", found " + String::toString(nrrow))); #endif } for (uInt i=0; i> dtype; if (dtype != colSet_p[i]->dataType()) { throw (DataManInternalError ("TiledStMan::headerFileGet: mismatch in data type")); } } headerFile >> hypercolumnName_p; if (version >= 3) { headerFile >> persMaxCacheSize_p; } else { uInt tmp; headerFile >> tmp; persMaxCacheSize_p = tmp; } maxCacheSize_p = persMaxCacheSize_p; if (firstTime) { // Setup the various things (i.e. initialize other variables). setup (extraNdim); } uInt nrdim; headerFile >> nrdim; if (nrdim != nrdim_p) { throw (DataManInternalError ("TiledStMan::headerFileGet: mismatch in nrdim")); } uInt64 nrFile; Bool flag; if (version >= 3) { headerFile >> nrFile; } else { uInt nrFile32; headerFile >> nrFile32; nrFile = nrFile32; } uInt64 nrFileOld = fileSet_p.nelements(); fileSet_p.resize (nrFile); for (uInt64 i=nrFileOld; i> flag; if (flag) { if (fileSet_p[i] == 0) { fileSet_p[i] = new TSMFile (this, headerFile, i, tsmOption(), multiFile()); }else{ fileSet_p[i]->getObject (headerFile); } }else{ delete fileSet_p[i]; fileSet_p[i] = 0; } } uInt64 nrCube; if (version >= 3) { headerFile >> nrCube; } else { uInt nrCube32; headerFile >> nrCube32; nrCube = nrCube32; } uInt64 nrCubeOld = cubeSet_p.nelements(); cubeSet_p.resize (nrCube); for (uInt64 i=nrCubeOld; iresync (headerFile); } } headerFile.getend(); //# The following can only be executed in case of TABLEREPAIR. if (nrrow < nrrow_p) { cubeSet_p[0]->extend (nrrow_p-nrrow, Record(), coordColSet_p[nrdim_p - 1]); } return version; } void TiledStMan::headerFileClose (AipsIO* headerFile) { delete headerFile; } TSMFile* TiledStMan::getFile (uInt sequenceNumber) { //# Do internal check to see if TSMFile really exists. if (sequenceNumber >= fileSet_p.nelements() || fileSet_p[sequenceNumber] == 0) { throw (DataManInternalError ("TiledStMan::getFile in " + hypercolumnName_p)); } return fileSet_p[sequenceNumber]; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/TiledStMan.h000066400000000000000000000535021476623553700202650ustar00rootroot00000000000000//# TiledStMan.h: Base class for Tiled Storage Managers //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TILEDSTMAN_H #define TABLES_TILEDSTMAN_H //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TSMColumn; class TSMDataColumn; class TSMCube; class TSMFile; class TableDesc; class Record; // // Base class for Tiled Storage Manager classes // // // // // //# Classes you should understand before using this one. //
      • Description of Tiled Storage Manager in module file // Tables.h //
      • DataManager //
      • TSMColumn //
      • ROTiledStManAccessor // for a discussion of the maximum cache size // // // TiledStMan is the base class for Tiled Storage Managers. // A tiled storage manager is capable of storing a hypercolumn // (as defined by // TableDesc::defineHypercolumn) // in one or more hypercubes. //
        It is not necessary to define a hypercolumn. If not defined, // it is assumed that all columns bound to this storage manager are // data columns. At least one of the columns must have a fixed // dimensionality and is used to determine the hypercube dimnensionality. //
        The general concept of these storage managers is explained in the // Tables module description. //

        // TiledStMan contains all common functions for the different tiled // storage managers. In particular, it contains functions // to check if the definitions of the shapes of hypercubes, coordinates, and // data cells are consistent. // It also contains various data members and functions to make them // persistent by writing them into an AipsIO stream. // // // This base class contains the common functionality of all // tiled storage managers. The base class is still abstract. // Only concrete tiled storage managers derived from it can // be instantiated. //

        // Tiled storage managers make access to array data possible with // more or less the same efficiency for access along different axes. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class TiledStMan : public DataManager { public: // Create a TiledStMan. TiledStMan(); // Create a TiledStMan storage manager. // The given maximum cache size (in MibiByte) is persistent, // thus will be reused when the table is read back. Note that the class // ROTiledStManAccessor // allows one to overwrite the maximum cache size temporarily. // Its description contains a discussion about the effects of // setting a maximum cache. TiledStMan (const String& hypercolumnName, uInt maximumCacheSizeMiB); virtual ~TiledStMan(); // Forbid copy constructor. TiledStMan (const TiledStMan&) = delete; // Forbid assignment. TiledStMan& operator= (const TiledStMan&) = delete; // Get the name given to the storage manager. // This is the name of the hypercolumn. virtual String dataManagerName() const; void setDataManagerName (const String& newHypercolumnName); // Return a record containing data manager specifications. virtual Record dataManagerSpec() const; // Get data manager properties that can be modified. // It is only MaxCacheSize (the maximum cache size in MibiByte). // It is a subset of the data manager specification. virtual Record getProperties() const; // Modify data manager properties. // Only MaxCacheSize can be used. It is similar to function setCacheSize // with canExceedNrBuckets=False. virtual void setProperties (const Record& spec); // Set the flag to "data has changed since last flush". void setDataChanged(); // Derive the tile shape from the hypercube shape for the given // number of pixels per tile. It is tried to get the same number // of tiles for each dimension. // When a weight vector is given, the number of tiles for a dimension // is proportional to the weight. //
        After the initial guess it tries to optimize it by trying // to waste as little space as possible, while trying to keep as close // to the initial guess. The given tolerance (possibly per axis) // gives the minimum and maximum possible length of a tile axis // (minimum = initial_guess*tolerance; maximum = initial_guess/tolerance). // The heuristic is such that a tile axis length dividing the cube length // exactly is always favoured. // The test program tTiledStMan can be used to see how // the algorithm works out for a given tile size and cube shape. // static IPosition makeTileShape (const IPosition& hypercubeShape, Double tolerance = 0.5, uInt64 maxNrPixelsPerTile = 4*1024*1024); static IPosition makeTileShape (const IPosition& hypercubeShape, const Vector& weight, const Vector& tolerance, uInt64 maxNrPixelsPerTile = 4*1024*1024); // // Set the maximum cache size (in MiB) in a non-persistent way. virtual void setMaximumCacheSize (uInt nMiB); // Get the current maximum cache size (in MiB (MibiByte)). uInt maximumCacheSize() const; // Get the current cache size (in buckets) for the hypercube in // the given row. uInt cacheSize (rownr_t rownr) const; // Get the hypercube shape of the data in the given row. const IPosition& hypercubeShape (rownr_t rownr) const; // Get the tile shape of the data in the given row. const IPosition& tileShape (rownr_t rownr) const; // Get the bucket size (in bytes) of the hypercube in the given row. uInt64 bucketSize (rownr_t rownr) const; // Can the tiled storage manager handle changing array shapes? // The default is no (but TiledCellStMan can). virtual Bool canChangeShape() const; // Can the tiled storage manager access an entire column. // TiledColumnStMan can always do that. // The others might be able to do it (for this time). // The default implementation returns True if there is only 1 hypercube. virtual Bool canAccessColumn() const; // The data manager supports use of MultiFile. virtual Bool hasMultiFileSupport() const; // Calculate the cache size (in buckets) for accessing the hypercube // containing the given row. It takes the maximum cache size into // account (allowing an overdraft of 10%). // It uses the given axisPath (i.e. traversal order) to determine // the optimum size. A window can be specified to indicate that only // the given subset of the hypercube will be accessed. //
        // The length of the slice and window arguments and axisPath // must be less or equal to the dimensionality of the hypercube. // The non-specified windowStart parts default to 0. // The non-specified windowLength parts default to // the hypercube shape. // The non-specified sliceShape parts default to 1. //
        // Axispath = [2,0,1] indicates that the z-axis changes most rapidly, // thereafter x and y. An axis can occur only once in the axisPath. // The non-specified axisPath parts get the natural order. // E.g. in the previous example axisPath=[2] defines the same path. //
        When forceSmaller is False, the cache is not resized when the // new size is smaller. //
        A flag is set indicating that the TSMDataColumn // access functions do not need to size the cache. uInt calcCacheSize (rownr_t rownr, const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath) const; // Set the cache size using the calcCacheSize // function mentioned above. void setCacheSize (rownr_t rownr, const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath, Bool forceSmaller); // Set the cache size for accessing the hypercube containing the given row. // When the give cache size exceeds the maximum cache size with more // than 10%, the maximum cache size is used instead. //
        When forceSmaller is False, the cache is not resized when the // new size is smaller. //
        A flag is set indicating that the TSMDataColumn // access functions do not need to size the cache. void setCacheSize (rownr_t rownr, uInt nbuckets, Bool forceSmaller); // Sets the cache size using the hypercube instead of the row number. // Useful for iterating over all hypercubes. void setHypercubeCacheSize (uInt hypercube, uInt nbuckets, Bool forceSmaller); // Determine if the user set the cache size (using setCacheSize). Bool userSetCache (rownr_t rownr) const; // Empty the caches used by the hypercubes in this storage manager. // It will flush the caches as needed and remove all buckets from them // resulting in a possibly large drop in memory used. // It also clears the userSetCache flag. void emptyCaches(); // Show the statistics of all caches used. void showCacheStatistics (ostream& os) const; // Get the length of the data for the given number of pixels. // This can be used to calculate the length of a tile. uInt64 getLengthOffset (uInt64 nrPixels, Block& dataOffset, Block& localOffset, uInt& localTileLength) const; // Get the number of coordinate vectors. uInt nrCoordVector() const; // Get the nr of rows in this storage manager. rownr_t nrow() const; // Does the storage manager allow to add rows? (yes) Bool canAddRow() const; // Get the default tile shape. // By default it returns a zero-length IPosition. virtual IPosition defaultTileShape() const; // Return the number of hypercubes. uInt nhypercubes() const; // Test if only one hypercube is used by this storage manager. // If not, throw an exception. Otherwise return the hypercube. virtual TSMCube* singleHypercube(); // Get the given hypercube. // const TSMCube* getTSMCube (uInt hypercube) const; TSMCube* getTSMCube (uInt hypercube); // // Get the hypercube in which the given row is stored. // const TSMCube* getHypercube (rownr_t rownr) const; virtual TSMCube* getHypercube (rownr_t rownr) = 0; // // Get the hypercube in which the given row is stored. // It also returns the position of the row in that hypercube. virtual TSMCube* getHypercube (rownr_t rownr, IPosition& position) = 0; // Make the correct TSMCube type (depending on tsmOption()). TSMCube* makeTSMCube (TSMFile* file, const IPosition& cubeShape, const IPosition& tileShape, const Record& values, Int64 fileOffset=-1); // Read a tile and convert the data to local format. void readTile (char* local, const Block& localOffset, const char* external, const Block& externalOffset, uInt nrpixels); // Write a tile after converting the data to external format. void writeTile (char* external, const Block& externalOffset, const char* local, const Block& localOffset, uInt nrpixels); // Get the TSMFile object with the given sequence number. TSMFile* getFile (uInt sequenceNumber); // Open the storage manager for an existing table. virtual rownr_t open64 (rownr_t nrrow, AipsIO&); // Resync the storage manager with the new file contents. virtual rownr_t resync64 (rownr_t nrrow); // Reopen all files used in this storage manager for read/write access. virtual void reopenRW(); // The data manager will be deleted (because all its columns are // requested to be deleted). // So clean up the things needed (e.g. delete files). virtual void deleteManager(); // Create a column in the storage manager on behalf of a table column. // // Create a scalar column. DataManagerColumn* makeScalarColumn (const String& name, int dataType, const String& dataTypeID); // Create a direct array column. DataManagerColumn* makeDirArrColumn (const String& name, int dataType, const String& dataTypeID); // Create an indirect array column. DataManagerColumn* makeIndArrColumn (const String& name, int dataType, const String& dataTypeID); // // The TiledStMan wants to do reallocateColumn. Bool canReallocateColumns() const; // Reallocate the column object if it is part of this data manager. // It returns a pointer to the new column object. // It is used to remove the indirection of the TSMColumn objects // resulting in only one iso. two virtual column calls to get the data. DataManagerColumn* reallocateColumn (DataManagerColumn* column); // Set the shape and tile shape of a hypercube. // By default it throws an "impossible" exception. virtual void setShape (rownr_t rownr, TSMCube* hypercube, const IPosition& shape, const IPosition& tileShape); // Check the shape to be set for a hypercube. // It checks if it matches predefined (fixed shape) columns // and the shape of already defined coordinate columns. void checkCubeShape (const TSMCube* hypercube, const IPosition& cubeShape) const; // Get the data type of the coordinate column with the given name. // An exception is thrown when the column is unknown. int coordinateDataType (const String& columnName) const; // Initialize the new coordinates for the given cube. void initCoordinates (TSMCube* hypercube); // Get pointer to data column object. const TSMDataColumn* getDataColumn (uInt colnr) const { return dataCols_p[colnr]; } protected: // Set the persistent maximum cache size (in MiB). void setPersMaxCacheSize (uInt nMiB); // Get the bindings of the columns with the given names. // If bound, the pointer to the TSMColumn object is stored in the block. // If mustExist is True, an exception is thrown if the column // is not bound. // It returns the number of bound columns. uInt getBindings (const Vector& columnNames, PtrBlock& colSet, Bool mustExist) const; // Function setup calls this function to allow the derived class // to check specific information. In case of errors, an exception // should be thrown. // By default it does nothing. virtual void setupCheck (const TableDesc& tableDesc, const Vector& dataNames) const; // Get the table description needed for the hypercolumn description. virtual const TableDesc& getDesc() const; // Check if values are given in the record for all columns in // the block. Also check if the data types are correct. // An exception is thrown if something is incorrect. void checkValues (const PtrBlock& colSet, const Record& values) const; // Check if the coordinate values are correct. // This calls checkValues and checks if their shapes match the // hypercube shape. // An exception is thrown if invalid. void checkCoordinates (const PtrBlock& coordColSet, const IPosition& cubeShape, const Record& values) const; // Check if the shapes of FixedShape data and coordinate columns match. // An exception is thrown if not. void checkShapeColumn (const IPosition& shape) const; // Check if the cube shape matches that of defined coordinates. void checkCoordinatesShapes (const TSMCube* hypercube, const IPosition& cubeShape) const; // Check if the hypercube to be added is correctly defined. void checkAddHypercube (const IPosition& cubeShape, const Record& values) const; // Make a new TSMCube object. TSMCube* makeHypercube (const IPosition& cubeShape, const IPosition& tileShape, const Record& values); // Get the index of the hypercube with the given id-values. // If not found, -1 is returned. Int getCubeIndex (const Record& idValues) const; // Determine how many rows need to be added for an extension // (in the last dimension) of a hypercube with the given shape. rownr_t addedNrrow (const IPosition& shape, uInt incrInLastDim) const; // Flush the caches of all hypercubes. // If data have put and fsync is set, fsync all files. Bool flushCaches (Bool fsync); // Let a derived class read the header info. // This is used by the open and resync function. virtual void readHeader (rownr_t nrrow, Bool firstTime) = 0; // Create the TSM header file. // It creates an AipsIO object for it. AipsIO* headerFileCreate(); // Open the TSM header file. // It creates an AipsIO object for it. AipsIO* headerFileOpen(); // Write the data into the header file. // The given number of TSMCube objects have to be written. void headerFilePut (AipsIO& headerFile, uInt64 nrCube); // Read the data from the header file. // When done for the first time, setup() is called to initialize // the various variables (using the extraNdim variable). // It returns the version of the AipsIO object in the header. uInt headerFileGet (AipsIO& headerFile, rownr_t tabNrrow, Bool firstTime, Int extraNdim); // Close the header file. // It deletes the AipsIO object. void headerFileClose (AipsIO* headerFile); // Set up the TiledStMan variables from the table description. // The argument specifies the number of extra dimensions for the // hypercube compared to the data array (usually 0 or 1). // It is only used if no hypercolumn definition exists. // -1 means that the hypercolumn definition has to be present. void setup (Int extraNdim=-1); // Create a TSMFile object and store its pointer at the given index // in the block. void createFile (uInt index); // Convert the scalar data type to an array data type. // This function is temporary and can disappear when the ColumnDesc // classes use type TpArray*. int arrayDataType (int dataType) const; //# Declare all data members. // The name of the hypercolumn. String hypercolumnName_p; // The number of rows in the columns. rownr_t nrrow_p; // The assembly of all columns. PtrBlock colSet_p; // The assembly of all data columns. PtrBlock dataCols_p; PtrBlock dataColSet_p; // The assembly of all id columns. PtrBlock idColSet_p; // The assembly of all coordinate columns. PtrBlock coordColSet_p; // The assembly of all TSMFile objects. // The first file is for all non-extensible cubes, while the others // are for one file per extensible cube. PtrBlock fileSet_p; // The assembly of all TSMCube objects. PtrBlock cubeSet_p; // The persistent maximum cache size (in MiB) for a hypercube. uInt persMaxCacheSize_p; // The actual maximum cache size for a hypercube (in MiB). uInt maxCacheSize_p; // The dimensionality of the hypercolumn. uInt nrdim_p; // The number of vector coordinates. uInt nrCoordVector_p; // The fixed cell shape. IPosition fixedCellShape_p; // Has any data changed since the last flush? Bool dataChanged_p; }; inline uInt TiledStMan::maximumCacheSize() const { return maxCacheSize_p; } inline uInt TiledStMan::nrCoordVector() const { return nrCoordVector_p; } inline rownr_t TiledStMan::nrow() const { return nrrow_p; } inline uInt TiledStMan::nhypercubes() const { return cubeSet_p.nelements(); } inline void TiledStMan::setDataChanged() { dataChanged_p = True; } inline const TSMCube* TiledStMan::getTSMCube (uInt hypercube) const { return const_cast(this)->getTSMCube (hypercube); } inline const TSMCube* TiledStMan::getHypercube (rownr_t rownr) const { return const_cast(this)->getHypercube (rownr); } inline void TiledStMan::setPersMaxCacheSize (uInt nMiB) { persMaxCacheSize_p = nMiB; maxCacheSize_p = nMiB; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/TiledStManAccessor.cc000066400000000000000000000140241476623553700221020ustar00rootroot00000000000000//# TiledStManAccessor.cc: Gives access to some TiledStMan functions //# Copyright (C) 1994,1995,1996,1997,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ROTiledStManAccessor::ROTiledStManAccessor (const Table& table, const String& name, Bool byColumn) : RODataManAccessor (table, name, byColumn), dataManPtr_p (0) { dataManPtr_p = dynamic_cast(baseDataManager()); if (dataManPtr_p == 0) { throw (DataManError ("ROTiledStManAccessor " + name + " constructed for data manager type " + baseDataManager()->dataManagerType() + "; expected Tiled*StMan")); } } ROTiledStManAccessor::ROTiledStManAccessor() {} ROTiledStManAccessor::~ROTiledStManAccessor() {} ROTiledStManAccessor::ROTiledStManAccessor (const ROTiledStManAccessor& that) : RODataManAccessor(that), dataManPtr_p (that.dataManPtr_p) {} ROTiledStManAccessor& ROTiledStManAccessor::operator= (const ROTiledStManAccessor& that) { if (this != &that) { RODataManAccessor::operator= (that); dataManPtr_p = that.dataManPtr_p; } return *this; } DataManager* ROTiledStManAccessor::getDataManager() const { return dataManPtr_p; } void ROTiledStManAccessor::setMaximumCacheSize (uInt size) { dataManPtr_p->setMaximumCacheSize (size); } uInt ROTiledStManAccessor::maximumCacheSize() const { return dataManPtr_p->maximumCacheSize(); } uInt ROTiledStManAccessor::cacheSize (rownr_t rownr) const { return dataManPtr_p->cacheSize (rownr); } const IPosition& ROTiledStManAccessor::hypercubeShape (rownr_t rownr) const { return dataManPtr_p->hypercubeShape (rownr); } const IPosition& ROTiledStManAccessor::tileShape (rownr_t rownr) const { return dataManPtr_p->tileShape (rownr); } uInt ROTiledStManAccessor::bucketSize (rownr_t rownr) const { return dataManPtr_p->bucketSize (rownr); } const Record& ROTiledStManAccessor::valueRecord (rownr_t rownr) const { return dataManPtr_p->getHypercube(rownr)->valueRecord(); } uInt ROTiledStManAccessor::nhypercubes() const { return dataManPtr_p->nhypercubes(); } uInt ROTiledStManAccessor::getCacheSize (uInt hypercube) const { return dataManPtr_p->getTSMCube(hypercube)->cacheSize(); } const IPosition& ROTiledStManAccessor::getHypercubeShape (uInt hypercube) const { return dataManPtr_p->getTSMCube(hypercube)->cubeShape(); } const IPosition& ROTiledStManAccessor::getTileShape (uInt hypercube) const { return dataManPtr_p->getTSMCube(hypercube)->tileShape(); } uInt ROTiledStManAccessor::getBucketSize (uInt hypercube) const { return dataManPtr_p->getTSMCube(hypercube)->bucketSize(); } const Record& ROTiledStManAccessor::getValueRecord (uInt hypercube) const { return dataManPtr_p->getTSMCube(hypercube)->valueRecord(); } uInt ROTiledStManAccessor::calcCacheSize (rownr_t rownr, const IPosition& sliceShape, const IPosition& axisPath) const { return dataManPtr_p->calcCacheSize (rownr, sliceShape, IPosition(), IPosition(), axisPath); } uInt ROTiledStManAccessor::calcCacheSize (rownr_t rownr, const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath) const { return dataManPtr_p->calcCacheSize (rownr, sliceShape, windowStart, windowLength, axisPath); } void ROTiledStManAccessor::setCacheSize (rownr_t rownr, const IPosition& sliceShape, const IPosition& axisPath, Bool forceSmaller) { dataManPtr_p->setCacheSize (rownr, sliceShape, IPosition(), IPosition(), axisPath, forceSmaller); } void ROTiledStManAccessor::setCacheSize (rownr_t rownr, const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath, Bool forceSmaller) { dataManPtr_p->setCacheSize (rownr, sliceShape, windowStart, windowLength, axisPath, forceSmaller); } void ROTiledStManAccessor::setCacheSize (rownr_t rownr, uInt nbuckets, Bool forceSmaller) { dataManPtr_p->setCacheSize (rownr, nbuckets, forceSmaller); } void ROTiledStManAccessor::setHypercubeCacheSize (uInt hypercube, uInt nbuckets, Bool forceSmaller) { // Allow the cache to be sized only if the hypercube is not empty. if (getBucketSize(hypercube) > 0){ static_cast (dataManPtr_p)->setHypercubeCacheSize (hypercube, nbuckets, forceSmaller); } } void ROTiledStManAccessor::clearCaches() { dataManPtr_p->emptyCaches(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/TiledStManAccessor.h000066400000000000000000000275771476623553700217650ustar00rootroot00000000000000//# TiledStManAccessor.h: Gives access to some TiledStMan functions //# Copyright (C) 1994,1995,1996,1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TILEDSTMANACCESSOR_H #define TABLES_TILEDSTMANACCESSOR_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TiledStMan; class DataManager; class Table; class IPosition; class String; class Record; //

        // Give access to some TiledStMan functions // // // // // //# Classes you should understand before using this one. //
      • TiledStMan // // // The Table system has one or more storage managers underneath. // These storage managers are invisible and there is no way to // get access to them. // However, the TiledStMan-type // storage managers are quite specific. // This class ROTiledStManAccessor gives the user the means to // access a TiledStMan-type object and to control it in some way. //

        // The actions that can be performed deal with the caches used in // a tiled storage manager. Per hypercube a cache is used to keep as many // tiles in memory as needed for efficient access to the data. // The cache size needed is calculated automatically. However, // it may be possible that a cache uses too much memory. Therefore // a maximum cache size can be specified, which can be done in 2 ways: //

          //
        1. To the constructor of a tiled storage manager. This is // persistent and acts as the default maximum cache size. //
        2. Using the function setMaximumCacheSize in this accessor class. // This is not persistent and acts as a temporary overwrite // of the default maximum cache size. //
        // It is recommended to set the maximum cache size only when the // tiled storage manager may use too much memory. Setting a // maximum could have the effect that the optimal number of tiles // does not fit in memory leading to excessive read/write activity. //
        For example:
        // A hypercube has shape [12,20,30,42] and tile shape [4,5,6,7]. // The hypercube contains doubles, so the tilesize is 6720 bytes. // The number of tiles per dimension is [3,4,5,6] resulting in 360 tiles. // Iterating through that hypercube requires that some tiles are kept in // memory to avoid too many read operations. When iterating like // // for (uInt i3=0; i3<42; i3++) // for (uInt i2=0; i2<30; i2++) // for (uInt i1=0; i1<20; i1++) // for (uInt i0=0; i0<12; i0++) // do something with data[i0,i1,i2,i3] // // it is clear that it is best to have a cache which can contain at least // 3*4*5 tiles. In that way each tile is read only once resulting in // 360 reads. //
        When the cache can hold 3*4 tiles, the first tiles of the 3rd // dimension have been flushed out when the second step in the 4th dimension // gets executed. So the tiles have to be reread for each step in the 4th // dimension, resulting in 3*4*5*42 = 2520 reads. //
        When the cache can hold only one tile, the situation is dramatic. // A tile has to be read for every 4 pixels, resulting in 75600 reads. //

        // Apart from setting the maximum cache size, one can also clear the // caches. This can be useful to free memory when an iteration through the // data in the tiled storage manager has been done completely. Clearing // the caches also clears their statistics (see below). //

        // Showing the statistics of the caches used by a tiled storage // manager is possible. Per cache it shows the number of tiles accessed and // the number of tiles actually read, written, or initialized. The hit ratio // gives a good idea of the cache behaviour. //

        // Note that the maximum cache size is not an absolute maximum. // When the optimal number of tiles do not fit, it is tried if they fit // when using an overdrawn of maximum 10%. If so, it uses that overdrawn. // If not, it uses the maximum cache size. //

        // A few functions exist to get information about a hypercube. // The 'get' functions get the information for the given hypercube, // while similar functions without the 'get' prefix do the same for the // given row. // // // In principle a pointer to TiledStMan could be used. // However, that would give access to all public functions. // Furthermore it could not distinguish between read/write and readonly // tables. // // // This example shows how to set the maximum cache size for // the tiled storage manager with the name "TSMExample". The cache // size is not persistent, i.e. when the same table is reopened // at a later time, this cache size is not remembered. // // // Open a table. // Table table("someName.data"); // // Set the maximum cache size of its tiled hypercube storage // // manager TSMExample to 0.5 MiB. // ROTiledStManAccessor accessor(table, "TSMExample"); // accessor.setMaximumCacheSize (512*1024); // // //# //# class ROTiledStManAccessor : public RODataManAccessor { public: // Default constructor should be used with care. // The resulting object cannot be used for any other operation // until a 'true' ROTiledStManAccessor object is assigned to it. ROTiledStManAccessor (); // Construct the object for a data manager in the table given the name // of the data manager or the column. // An exception is thrown if the data manager type is not any tiled // storage manager. ROTiledStManAccessor (const Table& table, const String& name, Bool byColumn=False); virtual ~ROTiledStManAccessor(); // Copy constructor (reference semantics). ROTiledStManAccessor (const ROTiledStManAccessor& that); // Assignment (reference semantics). ROTiledStManAccessor& operator= (const ROTiledStManAccessor& that); // Set the maximum cache size (in MibiByte) to be used by a hypercube // in the storage manager. Note that each hypercube has its own cache. // 0 means unlimited. // The initial maximum cache size is unlimited. // The maximum cache size given in this way is not persistent. // Only the maximum cache size given to the constructors of the tiled // storage managers, is persistent. void setMaximumCacheSize (uInt nMiB); // Get the maximum cache size (in MiB). uInt maximumCacheSize() const; // Get the current cache size (in buckets) for the hypercube in // the given row. uInt cacheSize (rownr_t rownr) const; // Get the hypercube shape of the data in the given row. const IPosition& hypercubeShape (rownr_t rownr) const; // Get the tile shape of the data in the given row. const IPosition& tileShape (rownr_t rownr) const; // Get the bucket size (in bytes) of the hypercube in the given row. uInt bucketSize (rownr_t rownr) const; // Get coordinate and id values of the hypercube in the given row. const Record& valueRecord (rownr_t rownr) const; // Return the number of hypercubes. uInt nhypercubes() const; // Get the current cache size (in buckets) for the given hypercube. uInt getCacheSize (uInt hypercube) const; // Get the shape of the given hypercube. const IPosition& getHypercubeShape (uInt hypercube) const; // Get the tile shape of the given hypercube. const IPosition& getTileShape (uInt hypercube) const; // Get the bucket size (in bytes) of the given hypercube. uInt getBucketSize (uInt hypercube) const; // Get coordinate and id values of the given hypercube. const Record& getValueRecord (uInt hypercube) const; // Calculate the cache size (in buckets) for accessing the hypercube // containing the given row. It takes the maximum cache size into // account (allowing an overdraft of 10%). // It uses the given axisPath (i.e. traversal order) to determine // the optimum size. A window can be specified to indicate that only // the given subset of the hypercube will be accessed. The window // defaults to the entire hypercube. //
        // The length of the slice and window arguments and axisPath // must be less or equal to the dimensionality of the hypercube. // The non-specified windowStart parts default to 0. // The non-specified windowLength parts default to // the hypercube shape. // The non-specified sliceShape parts default to 1. //
        // Axispath = [2,0,1] indicates that the z-axis changes most rapidly, // thereafter x and y. An axis can occur only once in the axisPath. // The non-specified axisPath parts get the natural order. // E.g. in the previous example axisPath=[2] defines the same path. // uInt calcCacheSize (rownr_t rownr, const IPosition& sliceShape, const IPosition& axisPath) const; uInt calcCacheSize (rownr_t rownr, const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath) const; // // Set the cache size using the corresponding calcCacheSize // function mentioned above. //
        When forceSmaller is False, the cache is not resized when the // new size is smaller. // void setCacheSize (rownr_t rownr, const IPosition& sliceShape, const IPosition& axisPath, Bool forceSmaller = True); void setCacheSize (rownr_t rownr, const IPosition& sliceShape, const IPosition& windowStart, const IPosition& windowLength, const IPosition& axisPath, Bool forceSmaller = True); // // Set the cache size for accessing the hypercube containing the given row. // When the give cache size exceeds the maximum cache size with more // than 10%, the maximum cache size is used instead. //
        When forceSmaller is False, the cache is not resized when the // new size is smaller. void setCacheSize (rownr_t rownr, uInt nbuckets, Bool forceSmaller = True); // This version allows setting the tile cache for a particular hypercube. This // is useful when iterating over the hypercubes in an StMan. void setHypercubeCacheSize (uInt hypercube, uInt nbuckets, Bool forceSmaller = True); // Clear the caches used by the hypercubes in this storage manager. // It will flush the caches as needed and remove all buckets from them // resulting in a possibly large drop in memory used. void clearCaches(); protected: // Get the data manager. DataManager* getDataManager() const; private: //# Declare the data members. TiledStMan* dataManPtr_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/VACEngine.h000066400000000000000000000303741476623553700200220ustar00rootroot00000000000000//# VACEngine.h: Base virtual column for an array column with any type //# Copyright (C) 1994,1995,1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_VACENGINE_H #define TABLES_VACENGINE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //

        // Base virtual column for an array column with any type // // // // // //# Classes you should understand before using this one. //
      • VirtualColumnEngine //
      • VirtualArrayColumn // // // VACEngine stands for Virtual Array Column Engine, i.e. a class // handling a virtual table column containing array values. // // // VACEngine is a base virtual column engine to handle a column // with an arbitrary type. // Data of columns with standard data types can directly be stored // in a Table using a storage manager, but data of column with non-standard // types have to be stored in another way. // The way to do this is to split the object with the non-standard // type into its individual elements, which are subsequently put into the // appropriate columns. // // A virtual column engine has to be implemented for each non-standard // data type, which has to be stored in a table. This engine has to get // and put the individual parts the object. // VACEngine is the base class for such engines, so the actual // engine quite simple to implement. The example shows the implementation // of an engine AVACEngine handling a data type A. // // In principle the name of the engine class is free, but it is strongly // recommended to use the name VACEngine, where VAC // stands for Virtual Array Column (e.g. AVACEngine for class A). // In this way the default data manager name supplied by the class and by // class ArrayColumnDesc can be used. // // // This example shows the implementation of an engine class AVACEngine, // which stores the data of a class A. // The data objects A are stored in a column called the source column. // The user has to associate two target columns with it. The engine stores // the data parts x and y in the target columns. // The names of the target columns are stored as keywords in the source // column. In this way the engine can reconstruct itself when the table // is read back. // // In the example all AVACEngine functions are shown inline, but they // should be implemented out-of-line in a separate .cc file. // // //# AVACEngine.h: Example virtual column engine to handle data type A // // #if !defined(AIPS_AVACENGINE_H) // #define AIPS_AVACENGINE_H // // //# Includes // #include // #include // // // Define the class A. // class A // { // public: // A(): x_p(0), y_p(0) {} // A(Int x, float y) : x_p(x), y_p(y) {} // A(const A& that): x_p(that.x_p), y_p(that.y_p) {} // static String dataTypeId() // { return "A"; } // Int x() const // { return x_p; } // float y() const // { return y_p; } // Int& x() // { return x_p; } // float& y() // { return y_p; } // int operator== (const A& that) const // { return x_p==that.x_p && y_p==that.y_p; } // int operator< (const A& that) const // { return x_p // { // public: // // // The default constructor is required for reconstruction of the // // engine when a table is read back. // AVACEngine() = default; // // // Construct the engine for the given source column and storing // // the result in the given target columns for the data members // // x and y of class A. // AVACEngine (const String& sourceColumnName, // const String& xTargetColumnName, // const String& yTargetColumnname) // : VACEngine (sourceColumnName), // xTargetName_p (xTargetColumnName), // yTargetName_p (yTargetColumnName) // {} // // // Destructor is only needed if something has to be destructed. // ~AVACEngine() = override // {} // // // Assignment is not needed and therefore forbidden. // AVACEngine& operator= (const AVACEngine&) = delete; // // // Clone the object. // virtual DataManager* clone() const // { // DataManager* dmPtr = new AVACEngine (sourceColumnName(), // xTargetName_p, yTargetName_p); // return dmPtr; // } // // // Store the target column names in the source column keywords. // virtual void create (rownr_t) // { // TableColumn src (table(), sourceColumnName()); // src.keywordSet().keysString()("_xTargetName") = xTargetName_p; // src.keywordSet().keysString()("_yTargetName") = yTargetName_p; // } // // // Prepare the engine by allocating column objects // // for the target columns. // virtual void prepare() // { // TableColumn src (table(), sourceColumnName()); // xTargetName_p = src.keywordSet().asString ("_xTargetName"); // yTargetName_p = src.keywordSet().asString ("_yTargetName"); // rocolx.attach (table(), xTargetName_p); // rocoly.attach (table(), yTargetName_p); // if (table().isWritable()) { // colx.attach (table(), xTargetName_p); // coly.attach (table(), yTargetName_p); // } // } // // // Get the data from a row. // virtual void get (rownr_t rownr, A& value) // { // rocolx.get (rownr, value.x()); // rocoly.get (rownr, value.y()); // } // // // Put the data in a row. // virtual void put (rownr_t rownr, const A& value) // { // colx.put (rownr, value.x()); // coly.put (rownr, value.y()); // } // // // Register the class name and the static makeObject "constructor". // // This will make the engine known to the table system. // static void registerClass() // { // DataManager::registerCtor ("AVACEngine", makeObject); // } // // private: // // Copy constructor is only used by clone(). // // (so it is made private). // AVACEngine (const AVACEngine&) // : VACEngine (that), // xTargetName_p (that.xTargetName_p), // yTargetName_p (that.yTargetName_p) // {} // // // // The target column names. // String xTargetName_p; // String yTargetName_p; // // Objects for the target columns. // ArrayColumn colx; // used by put // ArrayColumn rocolx; // used by get // ArrayColumn coly; // used by put // ArrayColumn rocoly; // used by get // // public: // // Define the "constructor" to construct this engine when a // // table is read back. // // This "constructor" has to be registered by the user of the engine. // // Function registerClass() is doing that. // static DataManager* makeObject (const String& dataManagerType) // { // DataManager* dmPtr = new AVACEngine(); // return dmPtr; // } // }; // // #endif // // // User code using this engine to create a new table could look like: // // // Register the engine. // // This is not needed if the engine is registered as part // // of the general DataManager::registerAllCtor function. // AVACEngine::registerClass(); // // Create the table description. // TableDesc td; // td.addColumn (ArrayColumnDesc("source")); // td.addColumn (ArrayColumnDesc("xTarget")); // td.addColumn (ArrayColumnDesc("yTarget")); // SetupNewTable setup ("table.name", td, Table::New); // // Define the engine for column "source". // AVACEngine engine ("source", "xTarget", "yTarget"); // Table tab (setup, 10); // // Put data into column "source". // ArrayColumn col (tab, "source"); // for (uInt i=0; i<10; i++) { // col.put (i, someA); // writes indirectly xTarget and yTarget // } // // // // // This class makes it easier for the user to implement the engine. // It supplies several default functions. // // //
      • Default constructor T(); //
      • Copy constructor T(const T&); //
      • Assignment operator T& operator= (const T&); //
      • comparison operator int operator== (const T&) const; //
      • comparison operator int operator< (const T&) const; //
      • identification static String dataTypeId(); // This should return the (unique) name of the class, thus // when T is templated in its turn, the name should contain the // template argument name. // template class VACEngine : public VirtualColumnEngine, public VirtualArrayColumn { //# Make members of parent class known. public: using VirtualArrayColumn::dataTypeId; public: // The default constructor is required for reconstruction of the // engine when a table is read back. // It is also used to construct an engine, which does not check // the source column name. VACEngine() = default; // Construct an engine to handle a column with an arbitrary data type. // Later it will check if the source column name is correct. VACEngine (const String& sourceColumnName); // Destructor. virtual ~VACEngine() = default; // Assignment is not needed and therefore forbidden. VACEngine& operator= (const VACEngine&) = delete; // Return the data manager type name. // This defaults to the data type ID followed by VACEngine // (meaning Virtual Array Column Engine). String dataManagerType() const; // Get the name of the source column. const String& sourceColumnName() const { return sourceName_p; } protected: // Copy constructor is only used by clone(). // (so it is made protected). VACEngine (const VACEngine&); private: // The column is in principle writable. // This does not mean it is actually writable, because that // depends on the fact if the table is writable. Bool isWritable() const; // Create the column object for the array column in this engine. // It will check if the given column name matches the source // column name. This assures that the engine is bound to the // correct column. DataManagerColumn* makeDirArrColumn (const String& columnName, int dataType, const String& dataTypeID); DataManagerColumn* makeIndArrColumn (const String& columnName, int dataType, const String& dataTypeID); //# Now define the data members. String sourceName_p; //# source column name }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/tables/DataMan/VACEngine.tcc000066400000000000000000000051121476623553700203340ustar00rootroot00000000000000//# VACEngine.cc: Base virtual column for an array column with any type //# Copyright (C) 1994,1995,1996 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_VACENGINE_TCC #define TABLES_VACENGINE_TCC //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template VACEngine::VACEngine (const String& sourceColumnName) : sourceName_p (sourceColumnName) {} template VACEngine::VACEngine (const VACEngine& that) : VirtualColumnEngine(), VirtualArrayColumn(), sourceName_p (that.sourceName_p) {} template String VACEngine::dataManagerType() const { return dataTypeId() + "VACEngine"; } // The column is in principle writable. template Bool VACEngine::isWritable() const { return True; } template DataManagerColumn* VACEngine::makeDirArrColumn (const String& columnName, int dataType, const String& dataTypeId) { return makeIndArrColumn (columnName, dataType, dataTypeId); } template DataManagerColumn* VACEngine::makeIndArrColumn (const String& columnName, int, const String&) { if (sourceName_p.empty()) { sourceName_p = columnName; }else{ if (columnName != sourceName_p) { throw (DataManInvOper ("VACEngine with source column " + sourceName_p + " bound to column " + columnName + "; must be the same")); } } return this; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/VSCEngine.h000066400000000000000000000301401476623553700200330ustar00rootroot00000000000000//# VSCEngine.h: Base virtual column for a scalar column with any type //# Copyright (C) 1994,1995,1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_VSCENGINE_H #define TABLES_VSCENGINE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Base virtual column for a scalar column with any type // // // // // //# Classes you should understand before using this one. //
      • VirtualColumnEngine //
      • VirtualScalarColumn // // // VSCEngine stands for Virtual Scalar Column Engine, i.e. a class // handling a virtual table column containing scalar values. // // // VSCEngine is a base virtual column engine to handle a column // with an arbitrary type. // Data of columns with standard data types can directly be stored // in a Table using a storage manager, but data of column with non-standard // types have to be stored in another way. // The way to do this is to split the object with the non-standard // type into its individual elements, which are subsequently put into the // appropriate columns. // // A virtual column engine has to be implemented for each non-standard // data type, which has to be stored in a table. This engine has to get // and put the individual parts the object. // VSCEngine is the base class for such engines, so the actual // engine quite simple to implement. The example shows the implementation // of an engine AVSCEngine handling a data type A. // // In principle the name of the engine class is free, but it is strongly // recommended to use the name VSCEngine, where VSC // stands for Virtual Scalar Column (e.g. AVSCEngine for class A). // In this way the default data manager name supplied by the class and by // class ScalarColumnDesc can be used. // // // This example shows the implementation of an engine class AVSCEngine, // which stores the data of a class A. // The data objects A are stored in a column called the source column. // The user has to associate two target columns with it. The engine stores // the data parts x and y in the target columns. // The names of the target columns are stored as keywords in the source // column. In this way the engine can reconstruct itself when the table // is read back. // // In the example all AVSCEngine functions are shown inline, but they // should be implemented out-of-line in a separate .cc file. // // //# AVSCEngine.h: Example virtual column engine to handle data type A // // #if !defined(AIPS_AVSCENGINE_H) // #define AIPS_AVSCENGINE_H // // //# Includes // #include // #include // // // Define the class A. // class A // { // public: // A(): x_p(0), y_p(0) {} // A(Int x, float y) : x_p(x), y_p(y) {} // A(const A& that): x_p(that.x_p), y_p(that.y_p) {} // static String dataTypeId() // { return "A"; } // Int x() const // { return x_p; } // float y() const // { return y_p; } // Int& x() // { return x_p; } // float& y() // { return y_p; } // int operator== (const A& that) const // { return x_p==that.x_p && y_p==that.y_p; } // int operator< (const A& that) const // { return x_p // { // public: // // // The default constructor is required for reconstruction of the // // engine when a table is read back. // AVSCEngine() // {} // // // Construct the engine for the given source column and storing // // the result in the given target columns for the data members // // x and y of class A. // AVSCEngine (const String& sourceColumnName, // const String& xTargetColumnName, // const String& yTargetColumnname) // : VSCEngine (sourceColumnName), // xTargetName_p (xTargetColumnName), // yTargetName_p (yTargetColumnName) // {} // // // Destructor is mandatory. // virtual ~AVSCEngine() // {} // // // Assignment is not needed and therefore forbidden. // AVSCEngine& operator= (const AVSCEngine&) = delete; // // // Clone the object. // virtual DataManager* clone() const // { // DataManager* dmPtr = new AVSCEngine (sourceColumnName(), // xTargetName_p, yTargetName_p); // return dmPtr; // } // // // Store the target column names in the source column keywords. // virtual void create (rownr_t) // { // TableColumn src (table(), sourceColumnName()); // src.keywordSet().keysString()("_xTargetName") = xTargetName_p; // src.keywordSet().keysString()("_yTargetName") = yTargetName_p; // } // // // Prepare the engine by allocating column objects // // for the target columns. // virtual void prepare() // { // TableColumn src (table(), sourceColumnName()); // xTargetName_p = src.keywordSet().asString ("_xTargetName"); // yTargetName_p = src.keywordSet().asString ("_yTargetName"); // rocolx.attach (table(), xTargetName_p); // rocoly.attach (table(), yTargetName_p); // if (table().isWritable()) { // colx.attach (table(), xTargetName_p); // coly.attach (table(), yTargetName_p); // } // } // // // Get the data from a row. // virtual void get (rownr_t rownr, A& value) // { // rocolx.get (rownr, value.x()); // rocoly.get (rownr, value.y()); // } // // // Put the data in a row. // virtual void put (rownr_t rownr, const A& value) // { // colx.put (rownr, value.x()); // coly.put (rownr, value.y()); // } // // // Register the class name and the static makeObject "constructor". // // This will make the engine known to the table system. // static void registerClass() // { // DataManager::registerCtor ("AVSCEngine", makeObject); // } // // private: // // Copy constructor is only used by clone(). // // (so it is made private). // AVSCEngine (const AVSCEngine&) // : VSCEngine (that), // xTargetName_p (that.xTargetName_p), // yTargetName_p (that.yTargetName_p) // {} // // // // The target column names. // String xTargetName_p; // String yTargetName_p; // // Objects for the target columns. // ScalarColumn colx; // used by put // ScalarColumn rocolx; // used by get // ScalarColumn coly; // used by put // ScalarColumn rocoly; // used by get // // public: // // Define the "constructor" to construct this engine when a // // table is read back. // // This "constructor" has to be registered by the user of the engine. // // Function registerClass() is doing that. // static DataManager* makeObject (const String& dataManagerType) // { // DataManager* dmPtr = new AVSCEngine(); // return dmPtr; // } // }; // // #endif // // // User code using this engine to create a new table could look like: // // // Register the engine. // // This is not needed if the engine is registered as part // // of the general DataManager::registerAllCtor function. // AVSCEngine::registerClass(); // // Create the table description. // TableDesc td; // td.addColumn (ScalarColumnDesc("source")); // td.addColumn (ScalarColumnDesc("xTarget")); // td.addColumn (ScalarColumnDesc("yTarget")); // SetupNewTable setup ("table.name", td, Table::New); // // Define the engine for column "source". // AVSCEngine engine ("source", "xTarget", "yTarget"); // Table tab (setup, 10); // // Put data into column "source". // ScalarColumn col (tab, "source"); // for (uInt i=0; i<10; i++) { // col.put (i, someA); // writes indirectly xTarget and yTarget // } // // // // // This class makes it easier for the user to implement the engine. // It supplies several default functions. // // //
      • Default constructor T(); //
      • Copy constructor T(const T&); //
      • Assignment operator T& operator= (const T&); //
      • comparison operator int operator== (const T&) const; //
      • comparison operator int operator< (const T&) const; //
      • identification static String dataTypeId(); // This should return the (unique) name of the class, thus // when T is templated in its turn, the name should contain the // template argument name. // template class VSCEngine : public VirtualColumnEngine, public VirtualScalarColumn { //# Make members of parent class known. public: using VirtualScalarColumn::dataTypeId; public: // The default constructor is required for reconstruction of the // engine when a table is read back. // It is also used to construct an engine, which does not check // the source column name. VSCEngine(); // Construct an engine to handle a column with an arbitrary data type. // Later it will check if the source column name is correct. VSCEngine (const String& sourceColumnName); // Destructor is mandatory. ~VSCEngine(); // Assignment is not needed and therefore forbidden. VSCEngine& operator= (const VSCEngine&) = delete; // Return the data manager type name. // This defaults to the data type ID followed by VSCEngine // (meaning Virtual Scalar Column Engine). String dataManagerType() const; // Get the name of the source column. const String& sourceColumnName() const { return sourceName_p; } protected: // Copy constructor is only used by clone(). // (so it is made protected). VSCEngine (const VSCEngine&); private: // The column is in principle writable. // This does not mean it is actually writable, because that // depends on the fact if the table is writable. Bool isWritable() const; // Create the column object for the scalar column in this engine. // It will check if the given column name matches the source // column name. This assures that the engine is bound to the // correct column. DataManagerColumn* makeScalarColumn (const String& columnName, int dataType, const String& dataTypeID); //# Now define the data members. String sourceName_p; //# source column name }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/tables/DataMan/VSCEngine.tcc000066400000000000000000000047541476623553700203710ustar00rootroot00000000000000//# VSCEngine.cc: Base virtual column for a scalar column with any type //# Copyright (C) 1994,1995,1996 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_VSCENGINE_TCC #define TABLES_VSCENGINE_TCC //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template VSCEngine::VSCEngine () : sourceName_p ("") {} template VSCEngine::VSCEngine (const String& sourceColumnName) : sourceName_p (sourceColumnName) {} template VSCEngine::VSCEngine (const VSCEngine& that) : VirtualColumnEngine(), VirtualScalarColumn(), sourceName_p (that.sourceName_p) {} template VSCEngine::~VSCEngine() {} template String VSCEngine::dataManagerType() const { return dataTypeId() + "VSCEngine"; } // The column is in principle writable. template Bool VSCEngine::isWritable() const { return True; } template DataManagerColumn* VSCEngine::makeScalarColumn (const String& columnName, int, const String&) { if (sourceName_p.empty()) { sourceName_p = columnName; }else{ if (columnName != sourceName_p) { throw (DataManInvOper ("VSCEngine with source column " + sourceName_p + " bound to column " + columnName + "; must be the same")); } } return this; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/VirtArrCol.cc000066400000000000000000000065471476623553700204550ustar00rootroot00000000000000//# VirtArrCol.cc: Base virtual column data manager class //# Copyright (C) 1994,1995,1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN VirtualArrayColumnBase::~VirtualArrayColumnBase() {} Bool VirtualArrayColumnBase::isWritable() const { return False; } void VirtualArrayColumnBase::getScalarColumnV (ArrayBase&) { throw DataManInvOper ("VirtualArrayColumn::getScalarColumnV not possible" " for column " + columnName()); } void VirtualArrayColumnBase::putScalarColumnV (const ArrayBase&) { throw DataManInvOper ("VirtualArrayColumn::putScalarColumnV not possible" " for column " + columnName()); } void VirtualArrayColumnBase::getScalarColumnCellsV (const RefRows&, ArrayBase&) { throw DataManInvOper ("VirtualArrayColumn::getScalarColumnCellsV not possible" " for column " + columnName()); } void VirtualArrayColumnBase::putScalarColumnCellsV (const RefRows&, const ArrayBase&) { throw DataManInvOper ("VirtualArrayColumn::putScalarColumnCellsV not possible" " for column " + columnName()); } //# The default implementations of the shape functions throw //# an exception. void VirtualArrayColumnBase::setShapeColumn (const IPosition&) { throw DataManInvOper ("VirtualArrayColumn::setShapeColumn not possible" " for column " + columnName()); } void VirtualArrayColumnBase::setShape (rownr_t, const IPosition&) { throw DataManInvOper ("VirtualArrayColumn::setShape not possible" " for column " + columnName()); } Bool VirtualArrayColumnBase::isShapeDefined (rownr_t) { throw DataManInvOper ("VirtualArrayColumn::isShapeDefined not possible" " for column " + columnName()); return False; } IPosition VirtualArrayColumnBase::shape (rownr_t) { throw DataManInvOper ("VirtualArrayColumn::shape not possible" " for column " + columnName()); return IPosition(0); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/VirtArrCol.h000066400000000000000000000323211476623553700203040ustar00rootroot00000000000000//# VirtArrCol.h: Templated base class for virtual array column //# Copyright (C) 1994,1995,1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_VIRTARRCOL_H #define TABLES_VIRTARRCOL_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class Slicer; // // Templated base class for virtual array column // // // // // //# Classes you should understand before using this one. //
      • DataManagerColumn //
      • VirtualColumnEngine // // // VirtualArrayColumn handles a virtual column containing an array. // // // VirtualArrayColumn is the abstract base class to handle an array column // for a virtual column engine (both direct and indirect arrays). // It is derived from DataManagerColumn and reimplements some // virtual functions to make life easier for the derived classes. // It does the following: //
          //
        • // It implements the dataType function, so it is not needed to implement // that in derived classes. //
        • // It has a default implementation of False for function isWritable. // Thus by default virtual scalar columns are not writable, which will // often be the case. Only if a virtual scalar column can be writable, // it has to be implemented in the derived class. //
        • // It has a default implementation for the functions dealing with // the array shapes. By default they throw an "invalid operation" // exception, so it is needed to implement them in the derived class. //
        • // In DataManagerColumn the functions get/putArrayV and get/putSliceV // are defined, which have an ArrayBase& data argument. This is necessary // to handle arbitrary data types in the non-templated base class // DataManagerColumn. // In this templated VirtualArrayColumn class, virtual functions // get/putArray, get/putSlice, etc. have been defined. They cast // the ArrayBase& data argument to Array&, so in a derived class no care // has to be taken for that cast. // Furthermore a default implementation of the get/putSlice has been made. // They get/put the entire array (using get/putArray) and access the // required slice. // By default the get/putArray functions thrown an "invalid operation" // exception, so they have to be implemented in the derived class. //
        • // Similarly the functions get/putArrayColumnV and get/putColumnSliceV // have been templated to get/putArrayColumn and get/putColumnSlice. // The default implementation of these latter functions handle a // column by looping through its individual cells. //
        • // Similarly the functions get/putArrayColumnCellsV and // get/putColumnSliceCells have been templated to // get/putArrayColumnCells and get/putColumnSliceCells. // However, their implementations throw an exception. // However, it makes it possible that a derived class // (like ScaledComplexData) // can implement these functions. //
        // An example of a virtual array column class is ScaledComplexData. Note that // this class is (indirectly) multiple derived from VirtualColumnEngine and // VirtualArrayColumn, so it combines the functionality of DataManager // and DataManagerColumn. // This is possible, because one ScaledComplexData engine can handle only one // column. //
        // // This class reimplements some virtual functions implemented by // DataManagerColumn and types the data argument. In that way they are // easier to implement in derived classes. Furthermore they allow // default implementations. // // //
      • default constructor //
      • copy constructor //
      • assignment operator //
      • static String dataTypeId(); // unique name of the class // // //# A List of bugs, limitations, extensions or planned refinements. // class VirtualArrayColumnBase : public DataManagerColumn { public: // Create a column. VirtualArrayColumnBase() {} virtual ~VirtualArrayColumnBase(); // By default no data can be put in a virtual column. virtual Bool isWritable() const; protected: // Set the shape of all arrays in the column. // It is only called if the column contains direct arrays. // By default it throws a "not possible" exception. virtual void setShapeColumn (const IPosition& shape); // Set the shape of an array in the given row. // It is only called if the column contains indirect arrays. // By default it throws a "not possible" exception. virtual void setShape (rownr_t rownr, const IPosition& shape); // Is the value shape defined in the given row? // By default it throws a "not possible" exception. virtual Bool isShapeDefined (rownr_t rownr); // Get the shape of the item in the given row. // By default it throws a "not possible" exception. virtual IPosition shape (rownr_t rownr); // The scalar access functions throw an exception. // virtual void getScalarColumnV (ArrayBase& dataPtr); virtual void putScalarColumnV (const ArrayBase& dataPtr); virtual void getScalarColumnCellsV (const RefRows& rownrs, ArrayBase& dataPtr); virtual void putScalarColumnCellsV (const RefRows& rownrs, const ArrayBase& dataPtr); // }; template class VirtualArrayColumn : public VirtualArrayColumnBase { public: // Create a column. VirtualArrayColumn() {} virtual ~VirtualArrayColumn(); // The object cannot be copied. VirtualArrayColumn (const VirtualArrayColumn&) = delete; // The object cannot be assigned to. VirtualArrayColumn& operator= (const VirtualArrayColumn&) = delete; // Return the data type of the column. virtual int dataType() const; // Return the data type Id of the column. virtual String dataTypeId() const; protected: // Get the array value in the given row. // The data array has to have the correct shape // (which is guaranteed by the ArrayColumn::get function). virtual void getArray (rownr_t rownr, Array& data) = 0; // Put the array value into the given row. // The data array has to have the correct shape // (which is guaranteed by the ArrayColumn::put function). // By default it throws a "not possible" exception. virtual void putArray (rownr_t rownr, const Array& data); // Get a section of the array in the given row. // The data array has to have the correct shape // (which is guaranteed by the ArrayColumn::getSlice function). // The default implementation gets the slice by getting the full // array first. virtual void getSlice (rownr_t rownr, const Slicer& slicer, Array& data); // Put into a section of the array in the given row. // The data array has to have the correct shape // (which is guaranteed by the ArrayColumn::putSlice function). // The default implementation gets the slice by accessing the full // array. virtual void putSlice (rownr_t rownr, const Slicer& slicer, const Array& data); // Get an entire column. // The data array has to have the correct shape // (which is guaranteed by the ArrayColum::getColumn function). // The default implementation gets the column row by row. virtual void getArrayColumn (Array& data); // Put an entire column. // The data array has to have the correct shape // (which is guaranteed by the ArrayColumn::putColumn function). // The default implementation puts the column row by row. virtual void putArrayColumn (const Array& data); // Get some array values in the column. // The data array has to have the correct length // (which is guaranteed by the ArrayColumn::getColumn function). // By default it throws a "not possible" exception. virtual void getArrayColumnCells (const RefRows& rownrs, Array& data); // Put some array values in the column. // The data array has to have the correct length // (which is guaranteed by the ArrayColumn::putColumn function). // By default it throws a "not possible" exception. virtual void putArrayColumnCells (const RefRows& rownrs, const Array& data); // Get a section of all arrays in the column. // The data array has to have the correct shape // (which is guaranteed by the ArrayColumn::getColumn function). // The default implementation gets the column row by row. virtual void getColumnSlice (const Slicer& slicer, Array& data); // Put a section of all arrays in the column. // The data array has to have the correct shape // (which is guaranteed by the ArrayColumn putColumn function). // The default implementation puts the column row by row. virtual void putColumnSlice (const Slicer& slicer, const Array& data); // Get a section of some arrays in the column. // The data array has to have the correct shape // (which is guaranteed by the ArrayColumn::getColumn function). // By default it throws a "not possible" exception. virtual void getColumnSliceCells (const RefRows& rownrs, const Slicer& slicer, Array& data); // Put into a section of some arrays in the column. // The data array has to have the correct shape // (which is guaranteed by the ArrayColumn::putColumn function). // By default it throws a "not possible" exception. virtual void putColumnSliceCells (const RefRows& rownrs, const Slicer& slicer, const Array& data); private: // Implement the virtual functions defined in DataManagerColumn. // Get the array value in the given row. void getArrayV (rownr_t rownr, ArrayBase& dataPtr); // Implement the virtual functions defined in DataManagerColumn. // Put the array value into the given row. void putArrayV (rownr_t rownr, const ArrayBase& dataPtr); // Implement the virtual functions defined in DataManagerColumn. // Get some array values in the column. void getArrayColumnCellsV (const RefRows& rownrs, ArrayBase& dataPtr); // Implement the virtual functions defined in DataManagerColumn. // Put some array values in the column. void putArrayColumnCellsV (const RefRows& rownrs, const ArrayBase& dataPtr); // Implement the virtual functions defined in DataManagerColumn. // Get a section of the array in the given row. void getSliceV (rownr_t rownr, const Slicer& slicer, ArrayBase& dataPtr); // Implement the virtual functions defined in DataManagerColumn. // Put into a section of the array in the given row. void putSliceV (rownr_t rownr, const Slicer& slicer, const ArrayBase& dataPtr); // Implement the virtual functions defined in DataManagerColumn. // Get an entire column. void getArrayColumnV (ArrayBase& dataPtr); // Implement the virtual functions defined in DataManagerColumn. // Put an entire column. void putArrayColumnV (const ArrayBase& dataPtr); // Implement the virtual functions defined in DataManagerColumn. // Get a section of all arrays in the column. void getColumnSliceV (const Slicer& slicer, ArrayBase& dataPtr); // Implement the virtual functions defined in DataManagerColumn. // Put into section of all arrays in the column. void putColumnSliceV (const Slicer& slicer, const ArrayBase& dataPtr); // Implement the virtual functions defined in DataManagerColumn. // Get a section of some arrays in the column. virtual void getColumnSliceCellsV (const RefRows& rownrs, const Slicer& slicer, ArrayBase& dataPtr); // Implement the virtual functions defined in DataManagerColumn. // Put into a section of some arrays in the column. virtual void putColumnSliceCellsV (const RefRows& rownrs, const Slicer& slicer, const ArrayBase& dataPtr); }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/tables/DataMan/VirtArrCol.tcc000066400000000000000000000147011476623553700206300ustar00rootroot00000000000000//# VirtArrCol.tcc: Base virtual column data manager class //# Copyright (C) 1994,1995,1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_VIRTARRCOL_TCC #define TABLES_VIRTARRCOL_TCC //# Includes #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template VirtualArrayColumn::~VirtualArrayColumn() {} template int VirtualArrayColumn::dataType() const { return ValType::getType (static_cast(0)); } template String VirtualArrayColumn::dataTypeId() const { return valDataTypeId (static_cast(0)); } template void VirtualArrayColumn::getArrayV (rownr_t rownr, ArrayBase& array) { getArray (rownr, static_cast&>(array)); } template void VirtualArrayColumn::putArrayV (rownr_t rownr, const ArrayBase& array) { putArray (rownr, static_cast&>(array)); } template void VirtualArrayColumn::getSliceV (rownr_t rownr, const Slicer& slicer, ArrayBase& array) { getSlice (rownr, slicer, static_cast&>(array)); } template void VirtualArrayColumn::putSliceV (rownr_t rownr, const Slicer& slicer, const ArrayBase& array) { putSlice (rownr, slicer, static_cast&>(array)); } template void VirtualArrayColumn::getArrayColumnV (ArrayBase& array) { getArrayColumn (static_cast&>(array)); } template void VirtualArrayColumn::putArrayColumnV (const ArrayBase& array) { putArrayColumn (static_cast&>(array)); } template void VirtualArrayColumn::getArrayColumnCellsV (const RefRows& rownrs, ArrayBase& array) { getArrayColumnCells (rownrs, static_cast&>(array)); } template void VirtualArrayColumn::putArrayColumnCellsV (const RefRows& rownrs, const ArrayBase& array) { putArrayColumnCells (rownrs, static_cast&>(array)); } template void VirtualArrayColumn::getColumnSliceV (const Slicer& slicer, ArrayBase& array) { getColumnSlice (slicer, static_cast&>(array)); } template void VirtualArrayColumn::putColumnSliceV (const Slicer& slicer, const ArrayBase& array) { putColumnSlice (slicer, static_cast&>(array)); } template void VirtualArrayColumn::getColumnSliceCellsV (const RefRows& rownrs, const Slicer& slicer, ArrayBase& array) { getColumnSliceCells (rownrs, slicer, static_cast&>(array)); } template void VirtualArrayColumn::putColumnSliceCellsV (const RefRows& rownrs, const Slicer& slicer, const ArrayBase& array) { putColumnSliceCells (rownrs, slicer, static_cast&>(array)); } //# The default implementations of get/putSlice get the entire array //# and access the required slice. template void VirtualArrayColumn::getSlice (rownr_t rownr, const Slicer& slicer, Array& arraySlice) { getSliceBase (rownr, slicer, arraySlice); } template void VirtualArrayColumn::putSlice (rownr_t rownr, const Slicer& slicer, const Array& arraySlice) { putSliceBase (rownr, slicer, arraySlice); } //# The default implementations of get/putArrayColumn handle the //# array in each individual row. template void VirtualArrayColumn::getArrayColumn (Array& array) { getArrayColumnBase (array); } template void VirtualArrayColumn::putArrayColumn (const Array& array) { putArrayColumnBase (array); } //# The default implementations of get/putColumnSlice take a slice //# for each individual row. template void VirtualArrayColumn::getColumnSlice (const Slicer& slicer, Array& array) { getColumnSliceBase (slicer, array); } template void VirtualArrayColumn::putColumnSlice (const Slicer& slicer, const Array& array) { putColumnSliceBase (slicer, array); } //# The default implementations of the Cells functions throw an exception. template void VirtualArrayColumn::getArrayColumnCells (const RefRows& rownrs, Array& value) { getArrayColumnCellsBase (rownrs, value); } template void VirtualArrayColumn::putArrayColumnCells (const RefRows& rownrs, const Array& value) { putArrayColumnCellsBase (rownrs, value); } template void VirtualArrayColumn::getColumnSliceCells (const RefRows& rownrs, const Slicer& ns, Array& value) { getColumnSliceCellsBase (rownrs, ns, value); } template void VirtualArrayColumn::putColumnSliceCells (const RefRows& rownrs, const Slicer& ns, const Array& value) { putColumnSliceCellsBase (rownrs, ns, value); } //# The default implementation of the put function throws //# an exception. template void VirtualArrayColumn::putArray (rownr_t, const Array&) { throw DataManInvOper ("VirtualArrayColumn::putArray not possible" " for column " + columnName()); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/VirtColEng.cc000066400000000000000000000060321476623553700204270ustar00rootroot00000000000000//# VirtColEng.cc: Abstract base class for virtual column handling //# Copyright (C) 1994,1995,1996,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN VirtualColumnEngine::~VirtualColumnEngine() {} Bool VirtualColumnEngine::isStorageManager() const { return False; } Bool VirtualColumnEngine::canAddRow() const { return True; } void VirtualColumnEngine::addRow64 (rownr_t) {} Bool VirtualColumnEngine::canRemoveRow() const { return True; } void VirtualColumnEngine::removeRow64 (rownr_t) {} //# Create, open, prepare and close do nothing unless implemented in the // derived class. Bool VirtualColumnEngine::flush (AipsIO&, Bool) { return False; } void VirtualColumnEngine::create64 (rownr_t) {} rownr_t VirtualColumnEngine::open64 (rownr_t nrow, AipsIO&) { return nrow; } rownr_t VirtualColumnEngine::resync64 (rownr_t nrow) { return nrow; } void VirtualColumnEngine::prepare() {} void VirtualColumnEngine::deleteManager() {} DataManagerColumn* VirtualColumnEngine::makeScalarColumn (const String& columnName, int, const String&) { throw (DataManUnknownVirtualColumn (columnName, dataManagerType())); return 0; } DataManagerColumn* VirtualColumnEngine::makeIndArrColumn (const String& columnName, int, const String&) { throw (DataManUnknownVirtualColumn (columnName, dataManagerType())); return 0; } //# Creating a direct array is by default the same as creating //# an indirect array. DataManagerColumn* VirtualColumnEngine::makeDirArrColumn (const String& columnName, int dataType, const String& dataTypeId) { return makeIndArrColumn (columnName, dataType, dataTypeId); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/VirtColEng.h000066400000000000000000000217401476623553700202740ustar00rootroot00000000000000//# VirtColEng.h: Abstract base class for virtual column handling //# Copyright (C) 1994,1995,1996,1997,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_VIRTCOLENG_H #define TABLES_VIRTCOLENG_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // Abstract base class for virtual column handling // // // // // //# Classes you should understand before using this one. //
      • DataManager //
      • Table // // // VirtualColumnEngine is the abstract data manager class for specialized // classes (engines) handling a group of virtual columns. // // // VirtualColumnEngine is the data manager for classes handling // a group of virtual columns in tables. It is an abstract base class // for the specialized virtual column engines. // Each virtual column as such is represented by a class which has // to be derived from the abstract base classes VirtualScalarColumn // or VirtualArrayColumn. The engine has to create the various // column objects via the functions makeXXXColumn. // // Initialization of the virtual column engine is done by the // functions create (for new tables), open (for existing tables) and prepare. // The engine can be flushed by the function flush, which allows to // write some data. The function open can read these data back. // VirtualColumnEngine is closely related with the table system. // // A number of (pure) virtual functions have been defined. The pure // virtual functions must be implemented in the derived class. // The non-pure virtual functions have a default implementation throwing // a "not possible" exception. They need to be implemented if they // are used for this engine (e.g. makeIndArrColumn does not need to // be implemented if the engine does not handle arrays). // Furthermore the pure virtual function dataManagerType (defined in // DataManager.h) has to be implemented. This should return the name // of the data manager, which is usually its class name. This name // has to be unique; so if the engine is templated, the template // parameter has to be part of the data manager name. // // The engine has to be registered before it can be used by the table system. // This means that a special makeObject function has to be made // known to the table system, which allows the table system to // reconstruct the engine using its name. // // An example of a virtual column engine can be found in dVirtColEng.{h,cc} // in the test directory of the Tables module. // Another exanple is class ScaledComplexData. // // // It is nice if a table column can be expressed as a function // of other columns (maybe even in other tables). A virtual column // provides this functionality in a very flexible way. // A specialized class can calculate the data of a virtual column, // but a common base class is required to interface it to the // table system. // // //# A List of bugs, limitations, extensions or planned refinements. // class VirtualColumnEngine : public DataManager { public: // Create the object. VirtualColumnEngine() {}; virtual ~VirtualColumnEngine(); // The copy constructor cannot be used for this base class. // The clone function should be used instead. VirtualColumnEngine (const VirtualColumnEngine&) = delete; // Assignment cannot be used for this base class. VirtualColumnEngine& operator= (const VirtualColumnEngine&) = delete; private: // The data manager is not a storage manager? virtual Bool isStorageManager() const; // Does the data manager allow to add rows? (default no) virtual Bool canAddRow() const; // Does the data manager allow to delete rows? (default no) virtual Bool canRemoveRow() const; // Add rows to all columns. // The default implementation does nothing. virtual void addRow64 (rownr_t nrrow); // Delete a row from all columns. // The default implementation does nothing. virtual void removeRow64 (rownr_t rownr); // Flush the data in the engine object. // If the object contains persistent data, this is the place to write them. // This can be done in two ways: //
          //
        • // They can be written in the main table file (using the AipsIO argument). // This should preferably be used if the object contains only little data. //
        • // They can be written in a file of its own. A unique filename // can be acquired using DataManager::fileName(). // This way is preferred when the object contains a lot of data. // Possibly this file could already be created in function create // and only be flushed and closed in this function. This allows // getting and putting of data as needed. //
        // Another way of storing information is by storing it as a keyword // in the table. In this case it is important to know that close // is called AFTER the keywords are written. Thus, in this way the // information has to be stored and read back in create, open and/or // prepare. // It returns a True status if it had to flush (i.e. if data have changed). //
        The default implementation does nothing and returns False. virtual Bool flush (AipsIO&, Bool fsync); // Resync the storage manager with the new file contents. // This is done by clearing the cache. // The default implementation does nothing. virtual rownr_t resync64 (rownr_t nrrow); // Initialize the object for a new table containing initially nrrow rows. // It can be used to initialize variables (possibly using data // from other columns in the table). // The default implementation does nothing. virtual void create64 (rownr_t initialNrrow); // Initialize the object for an existing table containing nrrow rows. // It can be used to read values back (written by close) and/or // to initialize variables (possibly using data from other columns // in the table). // The default implementation does nothing. virtual rownr_t open64 (rownr_t nrrow, AipsIO& mainTableFile); // Let the data manager initialize itself further. // Prepare is called after create/open has been called for all // columns. In this way one can be sure that referenced columns // are read back and partly initialized. // The default implementation does nothing. virtual void prepare(); // The data manager will be deleted (because all its columns are // requested to be deleted). // So clean up the things needed (e.g. delete files). // By default it assumes that nothing has to be done. virtual void deleteManager(); // Make a column object in the engine on behalf of a table column. // This column object class is derived from VirtualScalarColumn // or VirtualArrayColumn. It handles the gets and puts of data. // // Create a scalar column. // The default implementation throws an exception that it cannot // do it for this column. virtual DataManagerColumn* makeScalarColumn (const String& columnName, int dataType, const String& dataTypeId); // Create a direct array column. // The default implementation calls makeIndArrColumn // (when reading the user sees no difference between direct and indirect). virtual DataManagerColumn* makeDirArrColumn (const String& columnName, int dataType, const String& dataTypeId); // Create an indirect array column. // The default implementation throws an exception that it cannot // do it for this column. virtual DataManagerColumn* makeIndArrColumn (const String& columnName, int dataType, const String& dataTypeId); // }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/VirtScaCol.cc000066400000000000000000000104541476623553700204270ustar00rootroot00000000000000//# VirtScaColBase.cc: Base virtual column data manager class //# Copyright (C) 1994,1995,1996,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN VirtualScalarColumnBase::~VirtualScalarColumnBase() {} Bool VirtualScalarColumnBase::isWritable() const { return False; } void VirtualScalarColumnBase::getArrayV (rownr_t, ArrayBase&) { throw DataManInvOper ("VirtualScalarColumn::getArrayV not possible" " for column " + columnName()); } void VirtualScalarColumnBase::putArrayV (rownr_t, const ArrayBase&) { throw DataManInvOper ("VirtualScalarColumn::putArrayV not possible" " for column " + columnName()); } void VirtualScalarColumnBase::getArrayColumnV (ArrayBase&) { throw DataManInvOper ("VirtualScalarColumn::getArrayColumnV not possible" " for column " + columnName()); } void VirtualScalarColumnBase::putArrayColumnV (const ArrayBase&) { throw DataManInvOper ("VirtualScalarColumn::putArrayColumnV not possible" " for column " + columnName()); } void VirtualScalarColumnBase::getArrayColumnCellsV (const RefRows&, ArrayBase&) { throw DataManInvOper ("VirtualScalarColumn::getArrayColumnCellsV not possible" " for column " + columnName()); } void VirtualScalarColumnBase::putArrayColumnCellsV (const RefRows&, const ArrayBase&) { throw DataManInvOper ("VirtualScalarColumn::putArrayColumnCellsV not possible" " for column " + columnName()); } void VirtualScalarColumnBase::getSliceV (rownr_t, const Slicer&, ArrayBase&) { throw DataManInvOper ("VirtualScalarColumn::getSliceV not possible" " for column " + columnName()); } void VirtualScalarColumnBase::putSliceV (rownr_t, const Slicer&, const ArrayBase&) { throw DataManInvOper ("VirtualScalarColumn::putSliceV not possible" " for column " + columnName()); } void VirtualScalarColumnBase::getColumnSliceV (const Slicer&, ArrayBase&) { throw DataManInvOper ("VirtualScalarColumn::getColumnSliceV not possible" " for column " + columnName()); } void VirtualScalarColumnBase::putColumnSliceV (const Slicer&, const ArrayBase&) { throw DataManInvOper ("VirtualScalarColumn::putColumnSliceV not possible" " for column " + columnName()); } void VirtualScalarColumnBase::getColumnSliceCellsV (const RefRows&, const Slicer&, ArrayBase&) { throw DataManInvOper ("VirtualScalarColumn::getColumnSliceCellsV not possible" " for column " + columnName()); } void VirtualScalarColumnBase::putColumnSliceCellsV (const RefRows&, const Slicer&, const ArrayBase&) { throw DataManInvOper ("VirtualScalarColumn::putColumnSliceCellsV not possible" " for column " + columnName()); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/VirtScaCol.h000066400000000000000000000250121476623553700202650ustar00rootroot00000000000000//# VirtScaCol.h: Templated base class for virtual scalar column //# Copyright (C) 1994,1995,1996,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_VIRTSCACOL_H #define TABLES_VIRTSCACOL_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Templated base class for virtual scalar column // // // // // //# Classes you should understand before using this one. //
      • DataManagerColumn //
      • VirtualColumnEngine // // // VirtualScalarColumn handles a virtual column containing a scalar. // // // VirtualScalarColumn is the abstract base class to handle a scalar column // for a virtual column engine. // It is derived from DataManagerColumn and reimplements some // virtual functions to make life easier for the derived classes. // It does the following: //
          //
        • // It implements the dataType function, so it is not needed to implement // that in derived classes. //
        • // It has a default implementation of False for function isWritable. // Thus by default virtual scalar columns are not writable, which will // often be the case. Only if a virtual scalar column can be writable, // it has to be implemented in the derived class. //
        • // Declare a get/put function with the template parameter as its argument. // The virtual functions get/putBoolV, etc. (defined in DataManagerColumn) // are by default implemented using this (templated) get/put function. // This allows for the default implementation of get/putBlock and // makes life easier for the implementor of a derived class. // However, the disadvantage of this is an extra virtual function call. // (E.g. for a Bool value the first one is getBoolV and the second // one get(T&), where T is Bool). If efficiency is really necessary, // getBoolV, etc. should also be implemented in the derived class. //
        • // In DataManagerColumn the functions get/putBlockV and get/putColumnV // are defined, which have a void* data argument. This is necessary // to handle arbitrary data types in the non-templated base class // DataManagerColumn. // In this templated VirtualScalarColumn class, virtual functions // get/putBlock and get/putColumn have been defined. They cast // the void* data argument to T&, so in a derived class no care has // to be taken for that cast. // Furthermore a default implementation of them has been made. //
            //
          • getBlock gets one value using function get. //
          • putBlock puts one value at the time using function put. //
          • getColumn uses function getBlock. //
          • putColumn uses function putBlock. //
          // If efficiency is an issue, these functions should be implemented // in the derived class. //
        //
        // // This class reimplements some virtual functions implemented by // DataManagerColumn and types the data argument. In that way they are // easier to implement in derived classes. Furthermore they allow // default implementations. // // //
      • default constructor //
      • copy constructor //
      • assignment operator //
      • static String dataTypeId(); // unique name of the class // // //# A List of bugs, limitations, extensions or planned refinements. // class VirtualScalarColumnBase : public DataManagerColumn { public: // Create a column. VirtualScalarColumnBase() {} virtual ~VirtualScalarColumnBase(); // By default no data can be put in a virtual column. virtual Bool isWritable() const; protected: // The array access functions throw an exception. // virtual void getArrayV (rownr_t rownr, ArrayBase& dataPtr); virtual void putArrayV (rownr_t rownr, const ArrayBase& data); virtual void getArrayColumnV (ArrayBase& data); virtual void putArrayColumnV (const ArrayBase& data); virtual void getArrayColumnCellsV (const RefRows& rownrs, ArrayBase& data); virtual void putArrayColumnCellsV (const RefRows& rownrs, const ArrayBase& data); virtual void getSliceV (rownr_t rownr, const Slicer& slicer, ArrayBase& data); virtual void putSliceV (rownr_t rownr, const Slicer& slicer, const ArrayBase& data); virtual void getColumnSliceV (const Slicer& slicer, ArrayBase& data); virtual void putColumnSliceV (const Slicer& slicer, const ArrayBase& data); virtual void getColumnSliceCellsV (const RefRows& rownrs, const Slicer& slicer, ArrayBase& data); virtual void putColumnSliceCellsV (const RefRows& rownrs, const Slicer& slicer, const ArrayBase& data); // }; template class VirtualScalarColumn : public VirtualScalarColumnBase { public: // Create a column. VirtualScalarColumn() {;} // Frees up the storage. virtual ~VirtualScalarColumn(); // The object cannot be copied. VirtualScalarColumn (const VirtualScalarColumn&) = delete; // The object cannot be assigned to. VirtualScalarColumn& operator= (const VirtualScalarColumn&) = delete; // Return the data type of the column. virtual int dataType() const; // Return the data type Id of the column. virtual String dataTypeId() const; // Let a derived class get the scalar value in the given row. virtual void get (rownr_t rownr, T& data) = 0; // Let a derived class put the scalar value into the given row. // The default implementation throws an exception. virtual void put (rownr_t rownr, const T& data); private: // Implement the virtual functions defined in DataManagerColumn. // Get the scalar value in the given row. // virtual void getBool (rownr_t rownr, Bool* dataPtr); virtual void getuChar (rownr_t rownr, uChar* dataPtr); virtual void getShort (rownr_t rownr, Short* dataPtr); virtual void getuShort (rownr_t rownr, uShort* dataPtr); virtual void getInt (rownr_t rownr, Int* dataPtr); virtual void getuInt (rownr_t rownr, uInt* dataPtr); virtual void getInt64 (rownr_t rownr, Int64* dataPtr); virtual void getfloat (rownr_t rownr, float* dataPtr); virtual void getdouble (rownr_t rownr, double* dataPtr); virtual void getComplex (rownr_t rownr, Complex* dataPtr); virtual void getDComplex (rownr_t rownr, DComplex* dataPtr); virtual void getString (rownr_t rownr, String* dataPtr); // This function is the get for all non-standard data types. virtual void getOther (rownr_t rownr, void* dataPtr); // // Implement the virtual functions defined in DataManagerColumn. // Put the scalar value into the given row. // virtual void putBool (rownr_t rownr, const Bool* dataPtr); virtual void putuChar (rownr_t rownr, const uChar* dataPtr); virtual void putShort (rownr_t rownr, const Short* dataPtr); virtual void putuShort (rownr_t rownr, const uShort* dataPtr); virtual void putInt (rownr_t rownr, const Int* dataPtr); virtual void putuInt (rownr_t rownr, const uInt* dataPtr); virtual void putInt64 (rownr_t rownr, const Int64* dataPtr); virtual void putfloat (rownr_t rownr, const float* dataPtr); virtual void putdouble (rownr_t rownr, const double* dataPtr); virtual void putComplex (rownr_t rownr, const Complex* dataPtr); virtual void putDComplex (rownr_t rownr, const DComplex* dataPtr); virtual void putString (rownr_t rownr, const String* dataPtr); // This function is the put for all non-standard data types. virtual void putOther (rownr_t rownr, const void* dataPtr); // // Get all scalar values in the column. // The default implementation loops over the rows. virtual void getScalarColumnV (ArrayBase& dataPtr); // Put all scalar values in the column. // The default implementation loops over the rows. virtual void putScalarColumnV (const ArrayBase& dataPtr); // Get some scalar values in the column. // The default implementation loops over the rows. virtual void getScalarColumnCellsV (const RefRows& rownrs, ArrayBase& dataPtr); // Put some scalar values in the column. // The default implementation loops over the rows. virtual void putScalarColumnCellsV (const RefRows& rownrs, const ArrayBase& dataPtr); }; // // Global functions to get or put data of a virtual column // // // // template inline void getVirtualScalar (VirtualScalarColumn* col, uInt rownr, T* dataPtr) { col->get (rownr, *dataPtr); } inline void getVirtualScalar (DataManagerColumn* col, uInt, void*) { col->throwGet(); } template inline void putVirtualScalar (VirtualScalarColumn* col, uInt rownr, const T* dataPtr) { col->put (rownr, *dataPtr); } inline void putVirtualScalar (DataManagerColumn* col, uInt, const void*) { col->throwPut(); } // } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/tables/DataMan/VirtScaCol.tcc000066400000000000000000000115601476623553700206120ustar00rootroot00000000000000//# VirtScaCol.tcc: Base virtual column data manager class //# Copyright (C) 1994,1995,1996,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_VIRTSCACOL_TCC #define TABLES_VIRTSCACOL_TCC //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template VirtualScalarColumn::~VirtualScalarColumn() {} template int VirtualScalarColumn::dataType() const { return ValType::getType (static_cast(0)); } template String VirtualScalarColumn::dataTypeId() const { return valDataTypeId (static_cast(0)); } //# Implement the get/put functions via a macro. //# In principle they are not possible. //# The implementation is done using global functions defined in the .h fie. #define VIRTUALSCALARCOLUMN_GETPUT(TP) \ template \ void VirtualScalarColumn::aips_name2(get,TP) (rownr_t rownr, TP* dataPtr) \ { getVirtualScalar (this, rownr, dataPtr); } \ template \ void VirtualScalarColumn::aips_name2(put,TP) (rownr_t rownr, \ const TP* dataPtr) \ { putVirtualScalar (this, rownr, dataPtr); } VIRTUALSCALARCOLUMN_GETPUT(Bool) VIRTUALSCALARCOLUMN_GETPUT(uChar) VIRTUALSCALARCOLUMN_GETPUT(Short) VIRTUALSCALARCOLUMN_GETPUT(uShort) VIRTUALSCALARCOLUMN_GETPUT(Int) VIRTUALSCALARCOLUMN_GETPUT(uInt) VIRTUALSCALARCOLUMN_GETPUT(Int64) VIRTUALSCALARCOLUMN_GETPUT(float) VIRTUALSCALARCOLUMN_GETPUT(double) VIRTUALSCALARCOLUMN_GETPUT(Complex) VIRTUALSCALARCOLUMN_GETPUT(DComplex) VIRTUALSCALARCOLUMN_GETPUT(String) template void VirtualScalarColumn::getOther (rownr_t rownr, void* dataPtr) { get (rownr, *static_cast(dataPtr)); } template void VirtualScalarColumn::putOther (rownr_t rownr, const void* dataPtr) { put (rownr, *static_cast(dataPtr)); } //# Now implement the default implementations of the column access functions. template void VirtualScalarColumn::getScalarColumnV (ArrayBase& dataPtr) { Vector& vec = static_cast&>(dataPtr); rownr_t nr = vec.nelements(); for (rownr_t rownr=0; rownr void VirtualScalarColumn::putScalarColumnV (const ArrayBase& dataPtr) { const Vector& vec = static_cast&>(dataPtr); rownr_t nr = vec.nelements(); for (rownr_t rownr=0; rownr void VirtualScalarColumn::getScalarColumnCellsV (const RefRows& rownrs, ArrayBase& dataPtr) { Vector& vec = static_cast&>(dataPtr); RefRowsSliceIter iter(rownrs); rownr_t i=0; while (! iter.pastEnd()) { rownr_t rownr = iter.sliceStart(); rownr_t end = iter.sliceEnd(); rownr_t incr = iter.sliceIncr(); while (rownr <= end) { get (rownr, vec[i]); rownr += incr; i++; } iter++; } } template void VirtualScalarColumn::putScalarColumnCellsV (const RefRows& rownrs, const ArrayBase& dataPtr) { const Vector& vec = static_cast&>(dataPtr); RefRowsSliceIter iter(rownrs); rownr_t i=0; while (! iter.pastEnd()) { rownr_t rownr = iter.sliceStart(); rownr_t end = iter.sliceEnd(); rownr_t incr = iter.sliceIncr(); while (rownr <= end) { put (rownr, vec[i]); rownr += incr; i++; } iter++; } } //# The default implementation of put throws an exception. template void VirtualScalarColumn::put (rownr_t, const T&) { throwPut(); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/DataMan/VirtualTaQLColumn.cc000066400000000000000000000373621476623553700217530ustar00rootroot00000000000000//# VirtualTaQLColumn.h: Virtual column engine based on TaQL //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN VirtualTaQLColumn::VirtualTaQLColumn (const String& expr, const String& style) : itsDataType (TpOther), itsIsArray (False), itsIsConst (False), itsTempWritable (False), itsExpr (expr), itsStyle (style), itsNode (0), itsCurArray (0), itsCurRow (-1) {} VirtualTaQLColumn::VirtualTaQLColumn (const Record& spec) : itsDataType (TpOther), itsIsArray (False), itsIsConst (False), itsTempWritable (False), itsNode (0), itsCurArray (0), itsCurRow (-1) { if (spec.isDefined ("TAQLCALCEXPR")) { itsExpr = spec.asString ("TAQLCALCEXPR"); } if (spec.isDefined ("TAQLSTYLE")) { itsStyle = spec.asString ("TAQLSTYLE"); } } VirtualTaQLColumn::~VirtualTaQLColumn() { delete itsCurArray; delete itsNode; } void VirtualTaQLColumn::makeCurArray() { delete itsCurArray; itsCurArray = 0; switch (itsDataType) { case TpBool: itsCurArray = new Array(); break; case TpUChar: itsCurArray = new Array(); break; case TpShort: itsCurArray = new Array(); break; case TpUShort: itsCurArray = new Array(); break; case TpInt: itsCurArray = new Array(); break; case TpUInt: itsCurArray = new Array(); break; case TpInt64: itsCurArray = new Array(); break; case TpFloat: itsCurArray = new Array(); break; case TpDouble: itsCurArray = new Array(); break; case TpComplex: itsCurArray = new Array(); break; case TpDComplex: itsCurArray = new Array(); break; case TpString: itsCurArray = new Array(); break; default: throw DataManError ("VirtualTaQLColumn::makeCurArray - unknown data type"); } } DataManager* VirtualTaQLColumn::clone() const { DataManager* dmPtr = new VirtualTaQLColumn (itsExpr, itsStyle); return dmPtr; } DataManagerColumn* VirtualTaQLColumn::makeScalarColumn (const String& name, int dataType, const String&) { AlwaysAssert (dataType!=TpOther, AipsError); itsDataType = dataType; itsIsArray = False; itsColumnName = name; return this; } DataManagerColumn* VirtualTaQLColumn::makeIndArrColumn (const String& name, int dataType, const String&) { AlwaysAssert (dataType!=TpOther, AipsError); itsDataType = dataType; itsIsArray = True; itsColumnName = name; return this; } void VirtualTaQLColumn::create64 (rownr_t) { // Define a keyword in the column telling the expression. // The table has to be writable for this operation only; otherwise it is not writable. itsTempWritable = True; TableColumn tabcol (table(), itsColumnName); itsTempWritable = False; tabcol.rwKeywordSet().define ("_VirtualTaQLEngine_CalcExpr", itsExpr); tabcol.rwKeywordSet().define ("_VirtualTaQLEngine_Style", itsStyle); } void VirtualTaQLColumn::prepare() { // Get the expression. TableColumn tabcol (table(), itsColumnName); itsExpr = tabcol.keywordSet().asString ("_VirtualTaQLEngine_CalcExpr"); if (tabcol.keywordSet().isDefined ("_VirtualTaQLEngine_Style")) { itsStyle = tabcol.keywordSet().asString ("_VirtualTaQLEngine_Style"); } // Compile the expression. String cmd; if (! itsStyle.empty()) { cmd = "using style " + itsStyle; } TaQLResult res = tableCommand (cmd + " calc from $1 calc " + itsExpr, table()); itsNode = new TableExprNode(res.node()); // Check if the expression type matches the column type. if (itsNode->isScalar() == itsIsArray) { throw DataManError ("VirtualTaQLColumn: " "expression and " + itsColumnName + " column type mismatch (not both scalar or array)"); } // Check if the data types match. int exptype = itsDataType; switch (itsDataType) { case TpComplex: exptype = TpDComplex; break; case TpUChar: case TpShort: case TpUShort: case TpInt: case TpUInt: exptype = TpInt64; break; case TpFloat: exptype = TpDouble; break; default: break; } if (itsNode->dataType() != exptype) { throw DataManError ("VirtualTaQLColumn: " "expression and column " + itsColumnName + " data type mismatch"); } // Create the correct array type. makeCurArray(); // If a constant expression, get the constant value and set cache for scalars. itsIsConst = itsNode->getRep()->isConstant(); if (itsIsConst) { if (itsIsArray) { getResult(0); } else { fillColumnCache(); } } } DataManager* VirtualTaQLColumn::makeObject (const String&, const Record& spec) { DataManager* dmPtr = new VirtualTaQLColumn (spec); return dmPtr; } void VirtualTaQLColumn::registerClass() { DataManager::registerCtor (className(), makeObject); } String VirtualTaQLColumn::className() { return "VirtualTaQLColumn"; } String VirtualTaQLColumn::dataManagerType() const { return className(); } Record VirtualTaQLColumn::dataManagerSpec() const { Record spec; spec.define ("TAQLCALCEXPR", itsExpr); return spec; } void VirtualTaQLColumn::setShapeColumn (const IPosition& aShape) { itsShape = aShape; } void VirtualTaQLColumn::setMaxLength (uInt maxLength) { itsMaxLen = maxLength; } int VirtualTaQLColumn::dataType() const { return itsDataType; } Bool VirtualTaQLColumn::isWritable() const { // This is always False except temporarily in function create64 to define a keyword. return itsTempWritable; } uInt VirtualTaQLColumn::ndim (rownr_t rownr) { return shape(rownr).nelements(); } IPosition VirtualTaQLColumn::shape (rownr_t rownr) { if (!itsIsArray) { return IPosition(); } // See if the expression has a fixed shape. IPosition shp = itsNode->getNodeRep()->shape(); if (shp.nelements() > 0) { return shp; } // Value is already available if constant or if current row. if (!itsIsConst && rownr != itsCurRow) { getResult (rownr); itsCurRow = rownr; } return itsCurArray->shape(); } Bool VirtualTaQLColumn::isShapeDefined (rownr_t) { return True; } void VirtualTaQLColumn::getBool (rownr_t rownr, Bool* dataPtr) { *dataPtr = itsNode->getBool (rownr); } void VirtualTaQLColumn::getuChar (rownr_t rownr, uChar* dataPtr) { *dataPtr = uChar(itsNode->getInt (rownr)); } void VirtualTaQLColumn::getShort (rownr_t rownr, Short* dataPtr) { *dataPtr = Short(itsNode->getInt (rownr)); } void VirtualTaQLColumn::getuShort (rownr_t rownr, uShort* dataPtr) { *dataPtr = uShort(itsNode->getInt (rownr)); } void VirtualTaQLColumn::getInt (rownr_t rownr, Int* dataPtr) { *dataPtr = Int(itsNode->getInt (rownr)); } void VirtualTaQLColumn::getuInt (rownr_t rownr, uInt* dataPtr) { *dataPtr = uInt(itsNode->getInt (rownr)); } void VirtualTaQLColumn::getInt64 (rownr_t rownr, Int64* dataPtr) { *dataPtr = itsNode->getInt (rownr); } void VirtualTaQLColumn::getfloat (rownr_t rownr, float* dataPtr) { *dataPtr = Float(itsNode->getDouble (rownr)); } void VirtualTaQLColumn::getdouble (rownr_t rownr, double* dataPtr) { *dataPtr = itsNode->getDouble (rownr); } void VirtualTaQLColumn::getComplex (rownr_t rownr, Complex* dataPtr) { *dataPtr = Complex(itsNode->getDComplex (rownr)); } void VirtualTaQLColumn::getDComplex (rownr_t rownr, DComplex* dataPtr) { *dataPtr = itsNode->getDComplex (rownr); } void VirtualTaQLColumn::getString (rownr_t rownr, String* dataPtr) { *dataPtr = itsNode->getString (rownr); if (itsMaxLen > 0 && dataPtr->size() > itsMaxLen) { *dataPtr = dataPtr->substr (0, itsMaxLen); } } void VirtualTaQLColumn::getArrayV (rownr_t rownr, ArrayBase& arr) { // Usually getShape is called before getArray. // To avoid double calculation of the same value, the result is cached // by getShape in itsCurArray (by getResult). // Value is also available if constant. if (!itsIsConst && rownr != itsCurRow) { getResult (rownr); itsCurRow = rownr; } arr.assignBase (*itsCurArray); } void VirtualTaQLColumn::getResult (rownr_t rownr) { switch (itsDataType) { case TpBool: { Array arr = itsNode->getArrayBool (rownr); Array& out = *static_cast*>(itsCurArray); out.reference (arr); break; } case TpUChar: { Array arr = itsNode->getArrayInt (rownr); Array& out = *static_cast*>(itsCurArray); out.resize (arr.shape()); convertArray (out, arr); break; } case TpShort: { Array arr = itsNode->getArrayInt (rownr); Array& out = *static_cast*>(itsCurArray); out.resize (arr.shape()); convertArray (out, arr); break; } case TpUShort: { Array arr = itsNode->getArrayInt (rownr); Array& out = *static_cast*>(itsCurArray); out.resize (arr.shape()); convertArray (out, arr); break; } case TpInt: { Array arr = itsNode->getArrayInt (rownr); Array& out = *static_cast*>(itsCurArray); out.resize (arr.shape()); convertArray (out, arr); break; } case TpUInt: { Array arr = itsNode->getArrayInt (rownr); Array& out = *static_cast*>(itsCurArray); out.resize (arr.shape()); convertArray (out, arr); break; } case TpInt64: { Array arr = itsNode->getArrayInt (rownr); Array& out = *static_cast*>(itsCurArray); out.reference (arr); break; } case TpFloat: { Array arr = itsNode->getArrayDouble (rownr); Array& out = *static_cast*>(itsCurArray); out.resize (arr.shape()); convertArray (out, arr); break; } case TpDouble: { Array arr = itsNode->getArrayDouble (rownr); Array& out = *static_cast*>(itsCurArray); out.reference (arr); break; } case TpComplex: { Array arr = itsNode->getArrayDComplex (rownr); Array& out = *static_cast*>(itsCurArray); out.resize (arr.shape()); convertArray (out, arr); break; } case TpDComplex: { Array arr = itsNode->getArrayDComplex (rownr); Array& out = *static_cast*>(itsCurArray); out.reference (arr); break; } case TpString: { Array arr = itsNode->getArrayString (rownr); Array& out = *static_cast*>(itsCurArray); out.reference (arr); break; } default: throw DataManError ("VirtualTaQLColumn::getResult - unknown data type"); } if (! itsShape.empty() && ! itsShape.isEqual(itsCurArray->shape())) { throw DataManError ("VirtualTaQLColumn::getResult - shape of result mismatches fixed " "shape of column " + columnName()); } } void VirtualTaQLColumn::getScalarColumnV (ArrayBase& arr) { if (itsIsConst) { // Constant value, so fill the array with the same value. fillArray (arr); } else { getScalarColumnBase (arr); } } void VirtualTaQLColumn::getScalarColumnCellsV (const RefRows& rownrs, ArrayBase& arr) { if (itsIsConst) { // Constant value, so fill the array with the same value. fillArray (arr); } else { getScalarColumnCellsBase (rownrs, arr); } } void VirtualTaQLColumn::fillColumnCache() { columnCache().setIncrement (0); switch (itsDataType) { case TpBool: getBool (0, &itsBool); columnCache().set (0, table().nrow()-1, &itsBool); break; case TpUChar: getuChar (0, &itsuChar); columnCache().set (0, table().nrow()-1, &itsuChar); break; case TpShort: getShort (0, &itsShort); columnCache().set (0, table().nrow()-1, &itsShort); break; case TpUShort: getuShort (0, &itsuShort); columnCache().set (0, table().nrow()-1, &itsuShort); break; case TpInt: getInt (0, &itsInt); columnCache().set (0, table().nrow()-1, &itsInt); break; case TpUInt: getuInt (0, &itsuInt); columnCache().set (0, table().nrow()-1, &itsuInt); break; case TpInt64: getInt64 (0, &itsInt64); columnCache().set (0, table().nrow()-1, &itsInt64); break; case TpFloat: getfloat (0, &itsFloat); columnCache().set (0, table().nrow()-1, &itsFloat); break; case TpDouble: getdouble (0, &itsDouble); columnCache().set (0, table().nrow()-1, &itsDouble); break; case TpComplex: getComplex (0, &itsComplex); columnCache().set (0, table().nrow()-1, &itsComplex); break; case TpDComplex: getDComplex (0, &itsDComplex); columnCache().set (0, table().nrow()-1, &itsDComplex); break; case TpString: getString (0, &itsString); columnCache().set (0, table().nrow()-1, &itsString); break; default: throw DataManInvDT(itsColumnName); } } void VirtualTaQLColumn::fillArray (ArrayBase& arr) { Bool deleteIt; void* ptr = arr.getVStorage (deleteIt); switch (itsDataType) { case TpBool: objset (static_cast(ptr), itsBool, arr.size()); break; case TpUChar: objset (static_cast(ptr), itsuChar, arr.size()); break; case TpShort: objset (static_cast(ptr), itsShort, arr.size()); break; case TpUShort: objset (static_cast(ptr), itsuShort, arr.size()); break; case TpInt: objset (static_cast(ptr), itsInt, arr.size()); break; case TpUInt: objset (static_cast(ptr), itsuInt, arr.size()); break; case TpInt64: objset (static_cast(ptr), itsInt64, arr.size()); break; case TpFloat: objset (static_cast(ptr), itsFloat, arr.size()); break; case TpDouble: objset (static_cast(ptr), itsDouble, arr.size()); break; case TpComplex: objset (static_cast(ptr), itsComplex, arr.size()); break; case TpDComplex: objset (static_cast(ptr), itsDComplex, arr.size()); break; case TpString: objset (static_cast(ptr), itsString, arr.size()); break; default: throw DataManInvDT(itsColumnName); } arr.putVStorage (ptr, deleteIt); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/DataMan/VirtualTaQLColumn.h000066400000000000000000000227441476623553700216130ustar00rootroot00000000000000//# VirtualTaQLColumn.h: Virtual column engine based on TaQL //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_VIRTUALTAQLCOLUMN_H #define TABLES_VIRTUALTAQLCOLUMN_H //# Includes #include #include #include #include namespace casacore { //# Forward Declarations class TableExprNode; // // Virtual scalar column using TaQL // // // //# Classes you should understand before using this one. //
      • VirtualScalarColumn // // // // VirtualTaQLColumn is a virtual column engine to define the contents of a // column as a TaQL CALC expression in which possibly other columns are used. // It is (of course) only possible to get data from the column; puts cannot // be done. See note 199 for a description of TaQL. // The TaQL style can be specified (such as 0- or 1-based indexing). //
        // The expression result can be a scalar or array of the basic TaQL data types. // The column data type has to be conformant with that TaQL type, thus a // column of any integer type has to be used for an integer TaQL result. //
        // Constant expressions are precalculated and cached making the retrieval of // e.g. the full column much faster (factor 4). //
        // A possible use for a virtual TaQL column is a column in a MeasurementSet // containing a constant value. It could also be used for on-the-fly calculation // of J2000 UVW-values or HADEC using an expression such as "derivedmscal.newuvw()" // One has to be careful with deleting columns. If in an // existing table a TaQL expression uses a deleted column, the expression // cannot be parsed anymore and the table cannot be opened anymore. // In the future the Table System will be made more forgiving. // //
        // // // The following example creates a table with a few columns. // One column is virtual and has a random value if Col3 is true. // Otherwise it has value 0. // // // Create the table description. // TableDesc td; // td.addColumn (ScalarColumnDesc("Col1")); // td.addColumn (ScalarColumnDesc("Col2")); // td.addColumn (ScalarColumnDesc("Col3")); // td.addColumn (ScalarColumnDesc("ColVirt")); // // // Now create a new table from the description. // SetupNewTable newTab("tmtest", td, Table::New); // // Define the expression of the virtual column and bind the column to it. // // The other columns are by default bound to StandardStMan. // VirtualTaQLColumn engine("iif(Col3,rand(),0)"); // newTab.bindColumn("ColVirt", engine); // Table tab(newTab); // // class VirtualTaQLColumn : public VirtualColumnEngine, public DataManagerColumn { public: // Construct it with the given TaQL expression. VirtualTaQLColumn (const String& expr, const String& style=String()); // Construct it with the given specification. VirtualTaQLColumn (const Record& spec); // Destructor is mandatory. virtual ~VirtualTaQLColumn(); // Copy is not needed and therefore forbidden. VirtualTaQLColumn (const VirtualTaQLColumn&) = delete; // Assignment is not needed and therefore forbidden. VirtualTaQLColumn& operator= (const VirtualTaQLColumn&) = delete; // Clone the engine object. virtual DataManager* clone() const; // Get the data manager specification. virtual Record dataManagerSpec() const; // Return the type name of the engine. // (i.e. its class name VirtualTaQLColumn). virtual String dataManagerType() const; // Return the name of the class. static String className(); // Register the class name and the static makeObject "constructor". // This will make the engine known to the table system. static void registerClass(); // Define the "constructor" to construct this engine when a // table is read back. // This "constructor" has to be registered by the user of the engine. // If the engine is commonly used, its registration can be added // into the registerAllCtor function in DataManReg.cc. // This function gets automatically invoked by the table system. static DataManager* makeObject (const String& dataManagerName, const Record& spec); // Return the TaQL expression used. const String& expression() const { return itsExpr; } // Set the shape of an array in the column. // It is only called (right after the constructor) if the array has // a fixed shape. virtual void setShapeColumn (const IPosition& aShape); // Set the maximum length of a 'fixed length' string. // It is only called (right after the constructor) if the string has // a fixed length. virtual void setMaxLength (uInt maxLength); // Functions to return column info. // virtual int dataType() const; virtual Bool isWritable() const; virtual uInt ndim (rownr_t rownr); virtual IPosition shape (rownr_t rownr); virtual Bool isShapeDefined (rownr_t rownr); // private: // Create the column object for the scalar column in this engine. virtual DataManagerColumn* makeScalarColumn (const String& columnName, int dataType, const String&); // Create the column object for the indirect array column in this engine. virtual DataManagerColumn* makeIndArrColumn (const String& columnName, int dataType, const String& dataTypeId); // Let the engine initialize the object for a new table. // It defines a column keyword holding the expression. virtual void create64 (rownr_t); // Prepare compiles the expression. virtual void prepare(); // Get the scalar value in the given row. // virtual void getBool (rownr_t rownr, Bool* dataPtr); virtual void getuChar (rownr_t rownr, uChar* dataPtr); virtual void getShort (rownr_t rownr, Short* dataPtr); virtual void getuShort (rownr_t rownr, uShort* dataPtr); virtual void getInt (rownr_t rownr, Int* dataPtr); virtual void getuInt (rownr_t rownr, uInt* dataPtr); virtual void getInt64 (rownr_t rownr, Int64* dataPtr); virtual void getfloat (rownr_t rownr, float* dataPtr); virtual void getdouble (rownr_t rownr, double* dataPtr); virtual void getComplex (rownr_t rownr, Complex* dataPtr); virtual void getDComplex (rownr_t rownr, DComplex* dataPtr); virtual void getString (rownr_t rownr, String* dataPtr); // // Get the array value in the given row. // The array given by arr has to have the correct shape // (which is guaranteed by the ArrayColumn get function). virtual void getArrayV (rownr_t rownr, ArrayBase& arr); // Get the array result into itsCurArray. void getResult (rownr_t rownr); // Make the result cache. void makeCurArray(); // Get functions implemented by means of their DataManagerColumn::getXXBase // counterparts, but optimized for constant expressions. // virtual void getScalarColumnV (ArrayBase& arr); virtual void getScalarColumnCellsV (const RefRows& rownrs, ArrayBase& arr); // // Fill the ColumnCache object with a constant scalar value. void fillColumnCache(); // Fill an array with a constant scalar value. void fillArray (ArrayBase& data); //# Now define the data members. int itsDataType; Bool itsIsArray; Bool itsIsConst; //# Constant expression? Bool itsTempWritable; String itsColumnName; String itsExpr; //# TaQL expression String itsStyle; //# TaQL style TableExprNode* itsNode; //# compiled TaQL expression IPosition itsShape; //# The shape of the column. uInt itsMaxLen; //# The maximum length of a 'fixed length' string. union { Bool itsBool; //# Constant scalar values uChar itsuChar; Short itsShort; uShort itsuShort; Int itsInt; uInt itsuInt; Int64 itsInt64; Float itsFloat; Double itsDouble; }; Complex itsComplex; DComplex itsDComplex; String itsString; ArrayBase* itsCurArray; //# array value (constant or in itsCurRow) rownr_t itsCurRow; //# row of current array value }; } //end namespace casacore #endif casacore-3.7.1/tables/DataMan/test/000077500000000000000000000000001476623553700170625ustar00rootroot00000000000000casacore-3.7.1/tables/DataMan/test/CMakeLists.txt000066400000000000000000000017371476623553700216320ustar00rootroot00000000000000set (tests dRetypedArrayEngine dVSCEngine dVirtColEng nISMBucket tBitFlagsEngine tCompressComplex tCompressFloat tDataManInfo tExternalStMan tExternalStManNew tForwardCol tForwardColRow tIncrementalStMan tMappedArrayEngine tMemoryStMan tScaledArrayEngine tScaledComplexData tSSMAddRemove tSSMStringHandler tStandardStMan tStArrayFile tStMan tStMan1 tStManAll tTiledBool tTiledCellStM_1 tTiledCellStMan tTiledColumnStMan tTiledDataStM_1 tTiledDataStMan tTiledEmpty tTiledFileAccess tTiledShapeStM_1 tTiledShapeStMan tTiledStMan tTSMShape tVirtColEng tVirtualTaQLColumn tVACEngine tVSCEngine ) if (USE_ADIOS2) list(APPEND tests tAdios2StMan) endif() # Some test sources include a test .h file. include_directories ( . ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_tables) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/tables/DataMan/test/dRetypedArrayEngine.cc000066400000000000000000000312471476623553700233050ustar00rootroot00000000000000//# dRetypedArrayEngine.cc: Test program for class RetypedArrayEngine //# Copyright (C) 1995,1996,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes //#include #include "dRetypedArrayEngine.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void* RetypedArrayEx1::newCopyInfo (const TableRecord&, const IPosition&) { return 0; } void RetypedArrayEx1::deleteCopyInfo (void*) {} void RetypedArrayEx1::set (void*, void* vout, const Array& in, const IPosition& shape) { Array& out = *(Array*)vout; if (shape.nelements() == 1 && shape(0) == 2) { retypedArrayEngineSet (out, in); }else{ throw (DataManError ("RetypedArrayEx1::set")); } } void RetypedArrayEx1::get (void*, Array& out, const void* vin, const IPosition& shape) { const Array& in = *(const Array*)vin; if (shape.nelements() == 1 && shape(0) == 2) { retypedArrayEngineGet (out, in); }else{ throw (DataManError ("RetypedArrayEx1::get")); } } void* RetypedArrayEx2::newCopyInfo (const TableRecord& record, const IPosition& shape) { return new CopyInfo (record, shape); } void RetypedArrayEx2::deleteCopyInfo (void* copyInfo) { delete (CopyInfo*)copyInfo; } RetypedArrayEx2::CopyInfo::CopyInfo (const TableRecord& record, const IPosition& shape) : mask_p (new Vector), nrTrue_p (0) { Int fieldnr = record.description().fieldNumber ("mask"); if (fieldnr >= 0) { RORecordFieldPtr > field (record, fieldnr); *mask_p = *field; AlwaysAssert (mask_p->nelements() == 4, DataManError); } for (uInt i=0; inelements(); i++) { if ((*mask_p)(i)) { nrTrue_p++; } } // The shape must be 1-dimensionsal. AlwaysAssert (shape.nelements() == 1, DataManError); // When a mask is given, it must match the shape. if (nrTrue_p > 0) { AlwaysAssert (shape(0) == Int(nrTrue_p), DataManError); } } RetypedArrayEx2::CopyInfo::~CopyInfo() { delete mask_p; } void RetypedArrayEx2::CopyInfo::set (void* vout, const Array& in, const IPosition& shape) { Array& out = *(Array*)vout; AlwaysAssert (shape.nelements() == 1, DataManError); if (shape(0) == 4) { retypedArrayEngineSet (out, in); }else{ AlwaysAssert (shape(0) == Int(nrTrue_p), DataManError); retypedArrayEngineSet (out, in, shape, (void*)mask_p); } } void RetypedArrayEx2::CopyInfo::get (Array& out, const void* vin, const IPosition& shape) { const Array& in = *(const Array*)vin; AlwaysAssert (shape.nelements() == 1, DataManError); if (shape(0) == 4) { retypedArrayEngineGet (out, in); }else{ retypedArrayEngineGet (out, in, shape, (void*)mask_p); } } void RetypedArrayEx2::setElem (const DComplex* data, const IPosition&, const void* maskPtr) { const Vector& mask = *(const Vector*)maskPtr; if (mask(0)) { I_p = *data++; }else{ I_p = 0; } if (mask(1)) { Q_p = *data++; }else{ Q_p = 0; } if (mask(2)) { U_p = *data++; }else{ U_p = 0; } if (mask(3)) { V_p = *data; }else{ V_p = 0; } } void RetypedArrayEx2::getElem (DComplex* data, const IPosition&, const void* maskPtr) const { const Vector& mask = *(const Vector*)maskPtr; if (mask(0)) { *data++ = I_p; } if (mask(1)) { *data++ = Q_p; } if (mask(2)) { *data++ = U_p; } if (mask(3)) { *data = V_p; } } // Test program for class RetypedArrayEngine // This program tests the virtual column engine RetypedArrayEngine. // It is using the example classes RetypedArrayEx* for that purpose. // The results are written to stdout. The script executing this program, // compares the results with the reference output file. void a(Bool doExcp); void b(); void c(); int main (int argc, const char*[]) { try { a( (argc<2)); b(); c(); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } // First build a description. void a (Bool doExcp) { // First register the virtual column engine. RetypedArrayEngine::registerClass(); // Add ArrayColumnDesc to column type map. ArrayColumnDesc("x").registerClass(); // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.comment() = "A test of class TableDesc"; td.addColumn (ArrayColumnDesc ("Data", IPosition(2,2,10), ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc ("colA", "", RetypedArrayEngine::className(), "", IPosition(1,10), ColumnDesc::FixedShape)); // Now create a new table from the description. SetupNewTable newtab("dRetypedArrayEngine_tmp.data", td, Table::New); // Create the virtual column engine with the target columns Data. RetypedArrayEngine engine ("colA", "Data"); newtab.bindColumn ("colA", engine); Table tab(newtab, 50); // Fill the table via the virtual columns. ArrayColumn colA (tab, "colA"); Vector vec(10); uInt i; for (i=0; i colD (tab, "Data"); ArrayColumn colA(tab, "colA"); Matrix valD; Vector valA, valA1; Matrix resD(2,10); Vector resA(10); Slice slice(1,5,2); for (rownr_t i=0; i matA = colA.getColumn(); for (rownr_t i=0; i::registerClass(); // Add ArrayColumnDesc to column type map. ArrayColumnDesc("x").registerClass(); // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.addColumn (ArrayColumnDesc ("Data")); td.addColumn (ArrayColumnDesc ("Stokes")); { // Now create a new table from the description. SetupNewTable newtab("dRetypedArrayEngine_tmp.data", td, Table::New); // Create the virtual column engine with the target columns Data. RetypedArrayEngine engine ("Stokes", "Data"); newtab.bindColumn ("Stokes", engine); Table tab(newtab, 50); // Fill the table via the virtual columns. ArrayColumn stokesColumn (tab, "Stokes"); Vector vec(10); uInt i; for (i=0; i colD (tab, "Data"); ArrayColumn colA(tab, "Stokes"); Matrix valD; Vector valA; Matrix resD(4,10); Vector resA(10); for (rownr_t i=0; i > field (record, 0); // Only the I and Q value are used, so the shape is [2]. Vector mask(4); mask = False; mask(0) = True; mask(1) = True; *field = mask; RetypedArrayEngine engine ("Stokes", "Data", IPosition(1,2), record); newtab.bindColumn ("Stokes", engine); Table tab(newtab, 50); // Fill the table via the virtual columns. ArrayColumn stokesColumn (tab, "Stokes"); Vector vec(10); uInt i; for (i=0; i colD (tab, "Data"); ArrayColumn colA(tab, "Stokes"); Matrix valD; Vector valA; Matrix resD(2,10); Vector resA(10); for (rownr_t i=0; i #include #include #include #include #include //# Forward Declarations namespace casacore { class TableRecord; } // // Example virtual column engines to handle an arbitrary data type. // // // // //# Classes you should understand before using this one. //
      • RetypedArrayEngine // // // This class is an example of a class that can be used as the source // type in the virtual engine column // RetypedArrayEngine. // The target type is in this case a float (because that is the type // of the data in this class). So the actual engine to be used is // RetypedArrayEngine. // class RetypedArrayEx1 { public: RetypedArrayEx1(): x_p(0), y_p(0) {} RetypedArrayEx1(float x, float y) : x_p(x), y_p(y) {} static String dataTypeId() { return "RetypedArrayEx1"; } static IPosition shape() { return IPosition (1,2); } static void* newCopyInfo (const TableRecord&, const IPosition&); static void deleteCopyInfo (void*); // Alas, the CFront compiler complains about unknown size of // RetypedArrayEx1 when instantiating Array. // Therefore we have to declare it as a void*. //# static void set (void* copyInfo, Array& out, static void set (void* copyInfo, void* out, const casacore::Array& in, const casacore::IPosition& shape); static void get (void* copyInfo, casacore::Array& out, const void* in, const casacore::IPosition& shape); float x() const { return x_p; } float y() const { return y_p; } int operator== (const RetypedArrayEx1& that) const { return x_p==that.x_p && y_p==that.y_p; } int operator< (const RetypedArrayEx1& that) const { return x_p& in, const IPosition& sourceElementShape); void get (Array& out, const void* in, const IPosition& sourceElementShape); private: Vector* mask_p; uInt nrTrue_p; }; static void set (void* copyInfo, void* out, const Array& in, const IPosition& shape) { ((CopyInfo*)copyInfo)->set (out, in, shape); } static void get (void* copyInfo, Array& out, const void* in, const IPosition& shape) { ((CopyInfo*)copyInfo)->get (out, in, shape); } void setElem (const DComplex* data, const IPosition& shape, const void* mask); void getElem (DComplex* data, const IPosition& shape, const void* mask) const; private: DComplex I_p, Q_p, U_p, V_p; }; #endif casacore-3.7.1/tables/DataMan/test/dRetypedArrayEngine.out000066400000000000000000000010341476623553700235160ustar00rootroot00000000000000get row 0 get row 1 get row 2 get row 3 get row 4 get row 5 get row 6 get row 7 get row 8 get row 9 get row 10 get row 11 get row 12 get row 13 get row 14 get row 15 get row 16 get row 17 get row 18 get row 19 get row 20 get row 21 get row 22 get row 23 get row 24 get row 25 get row 26 get row 27 get row 28 get row 29 get row 30 get row 31 get row 32 get row 33 get row 34 get row 35 get row 36 get row 37 get row 38 get row 39 get row 40 get row 41 get row 42 get row 43 get row 44 get row 45 get row 46 get row 47 get row 48 get row 49 casacore-3.7.1/tables/DataMan/test/dVACEngine.cc000066400000000000000000000115331476623553700212770ustar00rootroot00000000000000//# dVACEngine.cc: Example virtual column engine to handle data type A //# Copyright (C) 1994,1995,1996,1997,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include "dVACEngine.h" #include #include #include #include VACExampleVACEngine::VACExampleVACEngine() {} VACExampleVACEngine::VACExampleVACEngine (const String& sourceColumnName, const String& xTargetColumnName, const String& yTargetColumnName, const String& zTargetColumnName) : VACEngine (sourceColumnName), xTargetName_p (xTargetColumnName), yTargetName_p (yTargetColumnName), zTargetName_p (zTargetColumnName) {} VACExampleVACEngine::VACExampleVACEngine (const VACExampleVACEngine& that) : VACEngine (that), xTargetName_p (that.xTargetName_p), yTargetName_p (that.yTargetName_p), zTargetName_p (that.zTargetName_p) {} VACExampleVACEngine::~VACExampleVACEngine() {} DataManager* VACExampleVACEngine::clone() const { DataManager* dmPtr = new VACExampleVACEngine (sourceColumnName(), xTargetName_p, yTargetName_p, zTargetName_p); if (dmPtr == 0) { throw (AllocError ("VACExampleVACEngine::clone()", 1)); } return dmPtr; } // Store the target column names in the keywords of this column. void VACExampleVACEngine::create64 (rownr_t) { TableColumn src (table(), sourceColumnName()); src.rwKeywordSet().define ("_xTargetName", xTargetName_p); src.rwKeywordSet().define ("_yTargetName", yTargetName_p); src.rwKeywordSet().define ("_zTargetName", zTargetName_p); } // Prepare the engine by allocating column objects // for the used columns. // Get their names from the keywords of this column. void VACExampleVACEngine::prepare() { TableColumn src (table(), sourceColumnName()); xTargetName_p = src.keywordSet().asString ("_xTargetName"); yTargetName_p = src.keywordSet().asString ("_yTargetName"); zTargetName_p = src.keywordSet().asString ("_zTargetName"); colx.attach (table(), xTargetName_p); coly.attach (table(), yTargetName_p); colz.attach (table(), zTargetName_p); } void VACExampleVACEngine::setShape (rownr_t rownr, const IPosition& shape) { colx.setShape (rownr, shape); coly.setShape (rownr, shape); colz.setShape (rownr, shape); } Bool VACExampleVACEngine::isShapeDefined (rownr_t rownr) { return colx.isDefined (rownr); } IPosition VACExampleVACEngine::shape (rownr_t rownr) { return colx.shape (rownr); } void VACExampleVACEngine::getArray (rownr_t rownr, Array& value) { Array x; Array y; Array z; colx.get (rownr, x); coly.get (rownr, y); colz.get (rownr, z); Array::iterator iter = value.begin(); for (size_t i=0; ix() = x.data()[i]; iter->y() = y.data()[i]; iter->z() = z.data()[i]; ++iter; } } void VACExampleVACEngine::putArray (rownr_t rownr, const Array& value) { Array x(value.shape()); Array y(value.shape()); Array z(value.shape()); Array::const_iterator iter = value.begin(); for (size_t i=0; ix(); y.data()[i] = iter->y(); z.data()[i] = iter->z(); ++iter; } colx.put (rownr, x); coly.put (rownr, y); colz.put (rownr, z); } DataManager* VACExampleVACEngine::makeObject (const String&, const Record&) { DataManager* dmPtr = new VACExampleVACEngine(); if (dmPtr == 0) { throw (AllocError ("VACExampleVACEngine::makeObject()", 1)); } return dmPtr; } void VACExampleVACEngine::registerClass() { DataManager::registerCtor ("VACExampleVACEngine", makeObject); } casacore-3.7.1/tables/DataMan/test/dVACEngine.h000066400000000000000000000126201476623553700211370ustar00rootroot00000000000000//# dVACEngine.h: Example virtual column engine to handle data type A //# Copyright (C) 1994,1995,1996,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_DVACENGINE_H #define TABLES_DVACENGINE_H //# Includes #include #include #include class VACExample { public: VACExample(): x_p(0), y_p(0) {} VACExample(Int x, float y, const String& z) : x_p(x), y_p(y), z_p(z) {} static String dataTypeId() { return "VACExample"; } Int x() const { return x_p; } float y() const { return y_p; } const String& z() const { return z_p; } Int& x() { return x_p; } float& y() { return y_p; } String& z() { return z_p; } int operator== (const VACExample& that) const { return x_p==that.x_p && y_p==that.y_p && z_p==that.z_p; } int operator< (const VACExample& that) const { return x_p // Example virtual column engine to handle data type VACExample // // // // //# Classes you should understand before using this one. //
      • VirtualColumnEngine //
      • VirtualArrayColumn // // // VACExampleVACEngine is an example showing how to use the templated // class VACEngine to handle table data with an arbitrary type. // Data of columns with the standard data types can directly be // stored using a storage manager, but data of column with non-standard // types have to be stored in another way. // // The normal way of doing this is to split the object of the non-standard // type into its elementary types. // This eample uses the class VACExample as the data type to be handled. // It consists of 2 data fields, which will transparently be stored in // 2 separate columns. // class VACExampleVACEngine : public VACEngine { public: // The default constructor is required for reconstruction of the // engine when a table is read back. // It is also used to construct an engine, which does not check // the source column name. VACExampleVACEngine(); // Construct the engine for the given source column and storing // the result in the given target columns for the data members // x and y of class VACExample. VACExampleVACEngine (const String& sourceColumnName, const String& xTargetColumnName, const String& yTargetColumnname, const String& zTargetColumnname); // Destructor is mandatory. virtual ~VACExampleVACEngine(); // Assignment is not needed and therefore forbidden. VACExampleVACEngine& operator= (const VACExampleVACEngine&) = delete; // Clone the object. DataManager* clone() const; // Store the target column names in the source column keywords. virtual void create64 (rownr_t); // Prepare the engine by allocating column objects // for the target columns. virtual void prepare(); virtual void setShape (rownr_t rownr, const IPosition& shape); virtual Bool isShapeDefined (rownr_t rownr); virtual IPosition shape (rownr_t rownr); // Get the data from a row. virtual void getArray (rownr_t rownr, Array& value); // Put the data in a row. virtual void putArray (rownr_t rownr, const Array& value); // Register the class name and the static makeObject "constructor". // This will make the engine known to the table system. static void registerClass(); private: // Copy constructor is only used by clone(). // (so it is made private). VACExampleVACEngine (const VACExampleVACEngine&); // The target column names. String xTargetName_p; String yTargetName_p; String zTargetName_p; // Objects for the target columns. ArrayColumn colx; ArrayColumn coly; ArrayColumn colz; public: //*display 4 // Define the "constructor" to construct this engine when a // table is read back. // This "constructor" has to be registered by the user of the engine. static DataManager* makeObject (const String& dataManagerName, const Record& spec); }; #endif casacore-3.7.1/tables/DataMan/test/dVSCEngine.cc000066400000000000000000000142221476623553700213170ustar00rootroot00000000000000//# dVSCEngine.cc: Example virtual column engine to handle data type A //# Copyright (C) 1994,1995,1996,1997,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Define a main program to allow compalition and linking by the make system. //# The variable is set in tVSCEngine.cc to skip it. #if !defined(DVSCENGINE_MAIN) int main() { return 0; } #endif //# Includes #include "dVSCEngine.h" #include #include #include #include VSCExampleVSCEngine::VSCExampleVSCEngine() {} VSCExampleVSCEngine::VSCExampleVSCEngine (const String& sourceColumnName, const String& xTargetColumnName, const String& yTargetColumnName, const String& zTargetColumnName) : VSCEngine (sourceColumnName), xTargetName_p (xTargetColumnName), yTargetName_p (yTargetColumnName), zTargetName_p (zTargetColumnName) {} VSCExampleVSCEngine::VSCExampleVSCEngine (const VSCExampleVSCEngine& that) : VSCEngine (that), xTargetName_p (that.xTargetName_p), yTargetName_p (that.yTargetName_p), zTargetName_p (that.zTargetName_p) {} VSCExampleVSCEngine::~VSCExampleVSCEngine() {} DataManager* VSCExampleVSCEngine::clone() const { DataManager* dmPtr = new VSCExampleVSCEngine (sourceColumnName(), xTargetName_p, yTargetName_p, zTargetName_p); if (dmPtr == 0) { throw (AllocError ("VSCExampleVSCEngine::clone()", 1)); } return dmPtr; } // Store the target column names in the keywords of this column. void VSCExampleVSCEngine::create64 (rownr_t) { TableColumn src (table(), sourceColumnName()); src.rwKeywordSet().define ("_xTargetName", xTargetName_p); src.rwKeywordSet().define ("_yTargetName", yTargetName_p); src.rwKeywordSet().define ("_zTargetName", zTargetName_p); } // Prepare the engine by allocating column objects // for the used columns. // Get their names from the keywords of this column. void VSCExampleVSCEngine::prepare() { TableColumn src (table(), sourceColumnName()); xTargetName_p = src.keywordSet().asString ("_xTargetName"); yTargetName_p = src.keywordSet().asString ("_yTargetName"); zTargetName_p = src.keywordSet().asString ("_zTargetName"); colx.attach (table(), xTargetName_p); coly.attach (table(), yTargetName_p); colz.attach (table(), zTargetName_p); } void VSCExampleVSCEngine::get (rownr_t rownr, VSCExample& value) { colx.get (rownr, value.x()); coly.get (rownr, value.y()); colz.get (rownr, value.z()); } void VSCExampleVSCEngine::put (rownr_t rownr, const VSCExample& value) { colx.put (rownr, value.x()); coly.put (rownr, value.y()); colz.put (rownr, value.z()); } DataManager* VSCExampleVSCEngine::makeObject (const String&, const Record&) { DataManager* dmPtr = new VSCExampleVSCEngine(); if (dmPtr == 0) { throw (AllocError ("VSCExampleVSCEngine::makeObject()", 1)); } return dmPtr; } void VSCExampleVSCEngine::registerClass() { DataManager::registerCtor ("VSCExampleVSCEngine", makeObject); } #ifdef AIPS_NO_TEMPLATE_SRC // Instantiate the templates here and not by means of the templates file. // This is needed in case -f_no-implicit-templates is not used. // In that case weak symbols are also created for Vector, etc. // Thereafter the linker wants to eliminate double defined weak symbols, // and also takes the dRetypedArrayEngine symbols into account. // That is fine when linking dRetypedArrayEngine, but gives undefined // linkonce symbols for other test programs which might use Vector or so. #include #include #include #include #include #include #include #include #include #include #include namespace casacore { template class Array; template class MaskedArray; template class Vector; template class Block; template class ScalarColumnData; template class ScalarColumnDesc; template class ScalarColumn; template class VSCEngine; template class VirtualScalarColumn; template class ObjCompare; template void objcopy(VSCExample *, VSCExample const *, uInt); template void objcopy(VSCExample *, VSCExample const *, uInt, uInt, uInt); template void objset(VSCExample *, VSCExample, uInt); template void objset(VSCExample *, VSCExample, uInt, uInt); template void objmove(VSCExample *, VSCExample const *, uInt); template class std::shared_ptr>; template class PtrRep>; template String valDataTypeId(VSCExample const *); } #endif casacore-3.7.1/tables/DataMan/test/dVSCEngine.h000066400000000000000000000122551476623553700211650ustar00rootroot00000000000000//# dVSCEngine.h: Example virtual column engine to handle data type A //# Copyright (C) 1994,1995,1996,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_DVSCENGINE_H #define TABLES_DVSCENGINE_H //# Includes #include #include #include class VSCExample { public: VSCExample(): x_p(0), y_p(0) {} VSCExample(Int x, float y, const String& z) : x_p(x), y_p(y), z_p(z) {} static String dataTypeId() { return "VSCExample"; } Int x() const { return x_p; } float y() const { return y_p; } const String& z() const { return z_p; } Int& x() { return x_p; } float& y() { return y_p; } String& z() { return z_p; } int operator== (const VSCExample& that) const { return x_p==that.x_p && y_p==that.y_p && z_p==that.z_p; } int operator< (const VSCExample& that) const { return x_p // Example virtual column engine to handle data type VSCExample // // // // //# Classes you should understand before using this one. //
      • VirtualColumnEngine //
      • VirtualScalarColumn // // // ExampleVSCEngine is an example showing how to use the templated // class VSCEngine to handle table data with an arbitrary type. // Data of columns with the standard data types can directly be // stored using a storage manager, but data of column with non-standard // types have to be stored in another way. // // The normal way of doing this is to split the object of the non-standard // type into its elementary types. // This eample uses the class VSCExample as the data type to be handled. // It consists of 2 data fields, which will transparently be stored in // 2 separate columns. // class VSCExampleVSCEngine : public VSCEngine { public: // The default constructor is required for reconstruction of the // engine when a table is read back. // It is also used to construct an engine, which does not check // the source column name. VSCExampleVSCEngine(); // Construct the engine for the given source column and storing // the result in the given target columns for the data members // x and y of class VSCExample. VSCExampleVSCEngine (const String& sourceColumnName, const String& xTargetColumnName, const String& yTargetColumnname, const String& zTargetColumnname); // Destructor is mandatory. ~VSCExampleVSCEngine(); // Assignment is not needed and therefore forbidden. VSCExampleVSCEngine& operator= (const VSCExampleVSCEngine&) = delete; // Clone the object. DataManager* clone() const; // Store the target column names in the source column keywords. void create64 (rownr_t); // Prepare the engine by allocating column objects // for the target columns. void prepare(); // Get the data from a row. void get (rownr_t rownr, VSCExample& value); // Put the data in a row. void put (rownr_t rownr, const VSCExample& value); // Register the class name and the static makeObject "constructor". // This will make the engine known to the table system. static void registerClass(); private: // Copy constructor is only used by clone(). // (so it is made private). VSCExampleVSCEngine (const VSCExampleVSCEngine&); // The target column names. String xTargetName_p; String yTargetName_p; String zTargetName_p; // Objects for the target columns. ScalarColumn colx; ScalarColumn coly; ScalarColumn colz; public: //*display 4 // Define the "constructor" to construct this engine when a // table is read back. // This "constructor" has to be registered by the user of the engine. static DataManager* makeObject (const String& dataManagerName, const Record& spec); }; #endif casacore-3.7.1/tables/DataMan/test/dVirtColEng.cc000066400000000000000000000210761476623553700215570ustar00rootroot00000000000000//# dVirtColEng.cc: Demo of a virtual column engine //# Copyright (C) 1994,1995,1996,1997,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Define a main program to allow compalition and linking by the make system. //# The variable is set in tVirtColEng.cc to skip it. #if !defined(DVIRTCOLENG_MAIN) int main() { return 0; } #endif //# Includes #include "dVirtColEng.h" #include #include #include #include #include #include #include #include #include DummyVirtualEngine::DummyVirtualEngine() : data1_p (this, 1.0), data2_p (this, 1.0) {} DummyVirtualEngine::DummyVirtualEngine (double scale1, double scale2) : data1_p (this, scale1), data2_p (this, scale2) {} DummyVirtualEngine::~DummyVirtualEngine() {} // Clone the engine object. DataManager* DummyVirtualEngine::clone() const { DataManager* dmPtr = new DummyVirtualEngine (data1_p.scale(), data2_p.scale()); if (dmPtr == 0) { throw (AllocError ("DummyVirtualEngine::clone()", 1)); } return dmPtr; } DataManagerColumn* DummyVirtualEngine::makeScalarColumn (const String&, int, const String&) { return &data1_p; } DataManagerColumn* DummyVirtualEngine::makeIndArrColumn (const String&, int, const String&) { return &data2_p; } Bool DummyVirtualEngine::flush (AipsIO& ios, Bool) { data1_p.flush (ios); data2_p.flush (ios); return True; } void DummyVirtualEngine::create64 (rownr_t) {} rownr_t DummyVirtualEngine::open64 (rownr_t nrrow, AipsIO& ios) { data1_p.open (ios); data2_p.open (ios); return nrrow; } void DummyVirtualEngine::prepare () { data1_p.prepare (table()); data2_p.prepare (table()); } DataManager* DummyVirtualEngine::makeObject (const String&, const Record&) { DataManager* dmPtr = new DummyVirtualEngine(); if (dmPtr == 0) { throw (AllocError ("DummyVirtualEngine::makeObject()", 1)); } return dmPtr; } void DummyVirtualEngine::registerClass() { DataManager::registerCtor ("DummyVirtualEngine", DummyVirtualEngine::makeObject); } String DummyVirtualEngine::dataManagerType() const { return "DummyVirtualEngine"; } DummyVirtualScalar::DummyVirtualScalar (DummyVirtualEngine* dve, double scale) : enginePtr_p(dve), scale_p (scale), writable_p (0), column_p (0) {} //# This copy constructor should only be called by VirtualColumnEngine::clone, //# when writable_p and the column_p variables are not filled yet. //# Check if that is indeed the case. DummyVirtualScalar::DummyVirtualScalar (const DummyVirtualScalar& that) : VirtualScalarColumn(), enginePtr_p(that.enginePtr_p), scale_p (that.scale_p), writable_p (0), column_p (0) { if (that.writable_p || that.column_p) { throw (DataManInternalError ("DummyVirtualScalar copy ctor")); } } DummyVirtualScalar::~DummyVirtualScalar() { delete column_p; } void DummyVirtualScalar::prepare (const Table& table) { //# Determine if the column is writable. writable_p = (table.isColumnWritable ("DATA1") ? 1 : -1); column_p = new ScalarColumn (table, "DATA1"); } void DummyVirtualScalar::open (AipsIO& ios) { ios.getstart ("DummyVirtualScalar"); ios >> scale_p; ios.getend(); } void DummyVirtualScalar::flush (AipsIO& ios) { ios.putstart ("DummyVirtualScalar", 1); // class version 1 ios << scale_p; ios.putend(); } // The function create is called upon initialization of the virtual column. // The initialization order of the columns is undetermined, which means // that this function isWritable can be called before the column has been // initialized. // For example, suppose column A uses column B and A get initialized // before B. Then A will call B's isWritable(), while B has not been // initialized yet. // This all means that isWritable must take care of the case // where the writable_p flag is not set yet. Bool DummyVirtualScalar::isWritable() const { if (writable_p == 0) { return enginePtr_p->table().isColumnWritable ("DATA1"); } return (writable_p > 0 ? True : False); } void DummyVirtualScalar::get (rownr_t rownr, double& data) { data = scale_p * (*column_p)(rownr); } void DummyVirtualScalar::getdoubleV (rownr_t rownr, double* dataPtr) { *dataPtr = scale_p * (*column_p)(rownr); } void DummyVirtualScalar::put (rownr_t rownr, const double& data) { column_p->put (rownr, Int(data / scale_p)); } void DummyVirtualScalar::putdoubleV (rownr_t rownr, const double* dataPtr) { column_p->put (rownr, Int(*dataPtr / scale_p)); } DummyVirtualArray::DummyVirtualArray (DummyVirtualEngine* dve, double scale) : enginePtr_p(dve), scale_p (scale), writable_p (0), column_p (0) {} //# This copy constructor should only be called by VirtualColumnEngine::clone, //# when writable_p and the column_p variables are not filled yet. //# Check if that is indeed the case. DummyVirtualArray::DummyVirtualArray (const DummyVirtualArray& that) : VirtualArrayColumn(), enginePtr_p(that.enginePtr_p), scale_p (that.scale_p), writable_p (0), column_p (0) { if (that.writable_p || that.column_p) { throw (DataManInternalError ("DummyVirtualArray copy ctor")); } } DummyVirtualArray::~DummyVirtualArray() { delete column_p; } void DummyVirtualArray::prepare (const Table& table) { //# Determine if the column is writable. writable_p = (table.isColumnWritable ("DATA2") ? 1 : -1); column_p = new ArrayColumn (table, "DATA2"); } void DummyVirtualArray::open (AipsIO& ios) { ios.getstart ("DummyVirtualArray"); ios >> scale_p; ios.getend(); } void DummyVirtualArray::flush (AipsIO& ios) { ios.putstart ("DummyVirtualArray", 1); // class version 1 ios << scale_p; ios.putend(); } Bool DummyVirtualArray::isWritable() const { if (writable_p == 0) { return enginePtr_p->table().isColumnWritable ("DATA2"); } return (writable_p > 0 ? True : False); } void DummyVirtualArray::setShape (rownr_t rownr, const IPosition& shape) { column_p->setShape (rownr, shape); } Bool DummyVirtualArray::isShapeDefined (rownr_t rownr) { return column_p->isDefined (rownr); } uInt DummyVirtualArray::ndim (rownr_t rownr) { return column_p->ndim (rownr); } IPosition DummyVirtualArray::shape (rownr_t rownr) { return column_p->shape (rownr); } void DummyVirtualArray::getArray (rownr_t rownr, Array& array) { Array intern(array.shape()); column_p->get (rownr, intern); Bool deleteIn, deleteOut; double* out = array.getStorage (deleteOut); double* op = out; const Int* in = intern.getStorage (deleteIn); const Int* ip = in; const Int* last = ip + array.nelements(); while (ip < last) { *op++ = *ip++ * scale_p; } intern.freeStorage (in, deleteIn); array.putStorage (out, deleteOut); } void DummyVirtualArray::putArray (rownr_t rownr, const Array& array) { Array intern(array.shape()); Bool deleteIn, deleteOut; const double* in = array.getStorage (deleteIn); const double* ip = in; Int* out = intern.getStorage (deleteOut); Int* op = out; const Int* last = op + array.nelements(); while (op < last) { *op++ = Int (*ip++ / scale_p + 0.5); } array.freeStorage (in, deleteIn); intern.putStorage (out, deleteOut); column_p->put (rownr, intern); } casacore-3.7.1/tables/DataMan/test/dVirtColEng.h000066400000000000000000000306621476623553700214220ustar00rootroot00000000000000//# dVirtColEng.h: Demo of a virtual column engine //# Copyright (C) 1994,1995,1996,1997,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_DVIRTCOLENG_H #define TABLES_DVIRTCOLENG_H //# Includes #include #include #include #include //# Forward Declarations class DummyVirtualEngine; namespace casacore { template class ScalarColumn; template class ArrayColumn; } // // Demo of a virtual scalar column // // // //# Classes you should understand before using this one. // // VirtualColumnEngine // VirtualScalarColumn // VirtualArrayColumn // // // // // DummyVirtualScalar is an example of how to implement a virtual // column class handling a scalar. // This class scales the data in table column "DATA1" from Int to // double and back using a scale factor given at construction time. // This class is used by DummyVirtualEngine which is the engine for // handling this scalar column and another column. // // // // This class is an example for writers of real virtual column classes. // It is tested by tVirtColEng.cc. // class DummyVirtualScalar : public VirtualScalarColumn { public: // Construct it with the given scale factor. DummyVirtualScalar (DummyVirtualEngine*, double scale); // The copy constructor (copy semantics) is needed for // DummyVirtualEngine::clone(). DummyVirtualScalar (const DummyVirtualScalar&); // Destructor is mandatory. ~DummyVirtualScalar(); // Assignment is not needed and therefore forbidden. DummyVirtualScalar& operator= (const DummyVirtualScalar&) = delete; // Return the scale factor used. double scale() const { return scale_p; } // Let the engine initialize the object for a new table. // It constructs the ScalarColumn object. void prepare (const Table& theTable); // Let the engine initialize the object for an existing table. // It reads the scale factor and constructs the ScalarColumn object. void open (AipsIO& ios); // Let the engine flush the object. // It writes the scale factor. void flush (AipsIO& ios); private: // The column may be writable, so we must override the default // implementation in the base class VirtualScalarColumn. Bool isWritable() const; // Get a value. //+grp void get (rownr_t rownr, double& data); // We also implement the getdoubleV, because that saves a // virtual function call. void getdoubleV (rownr_t rownr, double* dataPtr); //-grp // Put a value. //+grp void put (rownr_t rownr, const double& data); // We also implement the putdoubleV, because that saves a // virtual function call. void putdoubleV (rownr_t rownr, const double* dataPtr); //-grp //# We could also define the get/putBlockDoubleV functions, but //# that is not required. The default implementation gets //# one value. Possible optimization can be done by //# implementing it here. //# The same is true for get/putColumn. //# Now define the data members. DummyVirtualEngine* enginePtr_p; // pointer to engine object double scale_p; // scale factor Int writable_p; // 1 = column is writable // -1 = column is not writable // 0 = not known yet ScalarColumn* column_p; // the unscaled table column }; // // Dummy example of a virtual array column // // // //# Classes you should understand before using this one. // // VirtualColumnEngine // VirtualScalarColumn // VirtualArrayColumn // // // // // DummyVirtualArray is an example of how to implement a virtual // column class handling a array. // This class scales the data in table column "DATA1" from Int to // double and back using a scale factor given at construction time. // This class is used by DummyVirtualEngine which is the engine for // handling this array column and another column. // // // // This class is an example for writers of real virtual column classes. // class DummyVirtualArray : public VirtualArrayColumn { public: // Construct it with the given scale factor. DummyVirtualArray (DummyVirtualEngine*, double scale); // The copy constructor (copy semantics) is needed for // DummyVirtualEngine::clone(). DummyVirtualArray (const DummyVirtualArray&); // Destructor is mandatory. ~DummyVirtualArray(); // Assignment is not needed and therefore forbidden. DummyVirtualArray& operator= (const DummyVirtualArray&) = delete; // Return the scale factor used. double scale() const { return scale_p; } // Let the engine initialize the object for an existing table. // It reads the scale factor. void open (AipsIO& ios); // Let the engine initialize the object for a new table. // It constructs the ArrayColumn object and sets the writable switch. void prepare (const Table& theTable); // Let the engine flush the object. // It writes the scale factor. void flush (AipsIO& ios); private: // The column is writable, so we must override the default // implementation in the base class VirtualColumn. Bool isWritable() const; // Define the shape of the array. // This will define the shape of the underlying Int array. void setShape (rownr_t rownr, const IPosition& shape); // Test if the (underlying) array is defined. Bool isShapeDefined (rownr_t rownr); // Get the dimensionality of the (underlying) array. uInt ndim (rownr_t rownr); // Get the shape of the (underlying) array. IPosition shape (rownr_t rownr); // Get an array. void getArray (rownr_t rownr, Array& array); // Put an array. void putArray (rownr_t rownr, const Array& array); //# We could also define the get/putSlice functions, but //# that is not required. Possible optimization can be done by //# implementing it here. //# Now define the data members. DummyVirtualEngine* enginePtr_p; // pointer to engine object double scale_p; // scale factor Int writable_p; // 1 = column is writable // -1 = column is not writable // 0 = not known yet ArrayColumn* column_p; // the unscaled table column (for put) }; // // Dummy example of a virtual column engine // // // //# Classes you should understand before using this one. // // VirtualColumnEngine // VirtualScalarColumn // VirtualArrayColumn // // // // // DummyVirtualEngine is an example of how a virtual column engine // can be implemented. // This class scales the data in table column "DATA1" and "DATA2" from // Int to double and back using scale factors given at construction time. // It uses the classes DummyScalarColumn and DummyArrayColumn to handle // the specific columns. // // The scale factors will be stored in the table, so they can be // read back when the table is used again. // The static function registerClass registers the name of this class and // its makeObject function. It is important to call registerClass before // a table using this virtual column engine is used, otherwise the // engine is unknown to the table system. // // // // This class is an example for writers of real virtual column classes. // class DummyVirtualEngine : public VirtualColumnEngine { public: // The default constructor is required for reconstruction of the // engine when a table is read back. // It uses scale factors 1. The correct values will be set by open. DummyVirtualEngine(); // Create the object with the given scale factors. DummyVirtualEngine (double scaleData1, double scaleData2); // Destructor is mandatory. ~DummyVirtualEngine(); // The data manager system will use the clone function to make // a copy of the object. It does not need a copy constructor. DummyVirtualEngine (const DummyVirtualEngine&) = delete; // The data manager system will use the clone function to make // a copy of the object. It does not need the assignment operator. DummyVirtualEngine& operator= (const DummyVirtualEngine&) = delete; // Clone the engine object. DataManager* clone() const; // Return the type name of the engine. // (i.e. its class name DummyVirtualEngine). String dataManagerType() const; // Register the class name and the static makeObject "constructor". // This will make the engine known to the table system. static void registerClass(); //# In this example it is not needed to implement the functions //# canAddRow/Column and canDeleteRow/Column, because the //# default DataManager implementation of no suffices. //# Usually you do not need to implement them. private: //# In this example it is not needed to implement the functions //# addRow/Column and deleteRow/Column, because the //# default DataManager implementation of no suffices. //# Usually you do not need to implement them. // Create the column object for the scalar column in this engine. DataManagerColumn* makeScalarColumn (const String& columnName, int dataType, const String&); // Create the column object for the indirect array column in this engine. DataManagerColumn* makeIndArrColumn (const String& columnName, int dataType, const String& dataTypeId); // Flush the engine. // It will write a few things into the AipsIO object. Bool flush (AipsIO& ios, Bool fsync); // Initialize the object for a new table. // Intially the table has the given number of rows. // This function does not have to do anything; the real initialization // is done by prepare. void create64 (rownr_t initialNrrow); // Initialize the object for an existing table with the given number // of rows. // It will read back the data written by close. rownr_t open64 (rownr_t nrrow, AipsIO& ios); // Initialize the engine. void prepare(); // Define the various engine column objects. DummyVirtualScalar data1_p; DummyVirtualArray data2_p; public: //*display 4 // Define the "constructor" to construct this engine when a // table is read back. // This "constructor" has to be registered by the user of the engine. // If the engine is commonly used, its registration can be added // into the registerAllCtor function in DataManReg.cc. // This function gets automatically invoked by the table system. static DataManager* makeObject (const String& dataManagerName, const Record& spec); }; #endif casacore-3.7.1/tables/DataMan/test/nISMBucket.cc000066400000000000000000000160341476623553700213410ustar00rootroot00000000000000//# nISMBucket.cc :Simulation program for bucket behaviour of IncrementalStman //# Copyright (C) 1996,1999,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include // // Simulation program for bucket behaviour of IncrementalStman. // // // nISMBucket shows how much disk space is needed when using the // IncrementalStMan for a given scenario. It also shows the gain // compared to storing each data value separately. //

        // The programs asks for the bucket size and total number of rows. // Then it asks in a loop for the column(s), value length and how // often a value varies. // int main (int argc, const char* argv[]) { if (argc < 2) { cout << "This program calculates the number of buckets needed" << endl; cout << "for a scenario with the IncrementalStMan" << endl; cout << "Start as:" << endl; cout << " nISMBucket " << endl; cout << "bucketSize==0 means it will be calculated using the" < nrcol, leng, same; istringstream istr1(argv[1]); istr1 >> bucketSize; if (argc < 3) { cerr << "#rows: "; cin >> nrow; }else{ istringstream istr2(argv[2]); istr2 >> nrow; } nrent = 0; totNrcol = 0; cerr <<"You can now specify the distribution of the values" << endl; cerr <<"by giving #columns, value length, and #rows with same value"<> n; if (n == 0) { break; } nrent++; nrcol.resize (nrent); leng.resize (nrent); same.resize (nrent); nrcol[nrent-1] = n; totNrcol += n; cerr << "value length: "; cin >> leng[nrent-1]; cerr << "#rows with same value: "; cin >> same[nrent-1]; if (n == 0 || leng[nrent-1] == 0 || same[nrent-1] == 0) { cerr << "One or more zero values were given" << endl; return 1; } } if (nrent < 1) { cerr << "No columns given" << endl; return 1; } // Sort the "same" array in ascending order. Vector index; genSort (index, same); uInt lowest = same[index(0)]; // Calculate the bucket size if not specified. // This piece of code is similar to that in ISMBase.cc. uInt headerSize = 4 * (totNrcol + 1); // needed per column uInt fixedSize = 0; for (i=0; i 0) { // The bucket size is defined. Check if at least 2 // fixed-length items for each row fit in it. // When the bucket is smaller than 32768 bytes, check // if can hold at least 10 rows (since it makes no sense to // have very small buckets). if (bucketSize < headerSize + 2*fixedSize) { cout << "bucket too small to hold 2 rows" << endl; }else if (bucketSize < 32768) { if (bucketSize < headerSize + 10*fixedSize) { cout << "bucket < 32768 and too small to hold 10 rows" << endl; } } } if (bucketSize == 0) { // Calculate the bucket size. // Try to fit 100 rows (with a minimum of 32768 bytes). // If that results in a very large size (> 327680) try to fit // 10 rows. If that still results in a large size, use 327680 // but at least 2 rows have to fit in it. bucketSize = headerSize + 100 * fixedSize; if (bucketSize < 32768) { bucketSize = 32768; } else if (bucketSize > 327680) { bucketSize = headerSize + 10 * fixedSize; if (bucketSize > 327680) { bucketSize = headerSize + 2 * fixedSize; if (bucketSize < 327680) { bucketSize = 327680; } } } } // Check if #rows divide. // Now determine how many buckets are needed for this stuff. // First determine initial length (i.e. values of all columns). // The column index takes 4 initial bytes and 4 bytes per column. // Per value the index takes 8 bytes. uInt initleng = 0; uInt colindex = 4; uInt normalleng = 0; Block sameleng (nrent, 0u); Block times (nrent); for (i=0; i nrow/lowest) { break; } for (i=0; i &str, uInt row){ size_t s=0; for(auto &i : str){ i = "string for Row " + std::to_string(row) + " Element " + std::to_string(s); ++s; } } template void VerifyArrayColumn(Table &table, std::string column, uInt rows, IPosition array_pos) { ArrayColumn array_column(table, column); for(uInt i=0; i arr_read = array_column.get(i); Array arr_gen(array_pos); GenData(arr_gen, i); AlwaysAssertExit (arr_read.nelements() == arr_gen.nelements()); for(size_t j=0; j void VerifyScalarColumn(Table &table, std::string column, uInt rows) { ScalarColumn scalar_column(table, column); for(uInt i=0; i("scalar_Bool")); td.addColumn (ScalarColumnDesc("scalar_uChar")); td.addColumn (ScalarColumnDesc("scalar_Short")); td.addColumn (ScalarColumnDesc("scalar_uShort")); td.addColumn (ScalarColumnDesc("scalar_Int")); td.addColumn (ScalarColumnDesc("scalar_uInt")); td.addColumn (ScalarColumnDesc("scalar_Float")); td.addColumn (ScalarColumnDesc("scalar_Double")); td.addColumn (ScalarColumnDesc("scalar_Complex")); td.addColumn (ScalarColumnDesc("scalar_DComplex")); td.addColumn (ScalarColumnDesc("scalar_String")); td.addColumn (ArrayColumnDesc("array_Bool", array_pos, ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc("array_uChar", array_pos, ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc("array_Short", array_pos, ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc("array_uShort", array_pos, ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc("array_Int", array_pos, ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc("array_uInt", array_pos, ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc("array_Float", array_pos, ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc("array_Double", array_pos, ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc("array_Complex", array_pos, ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc("array_DComplex", array_pos, ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc("array_String", array_pos, ColumnDesc::FixedShape)); SetupNewTable newtab(filename, td, Table::New); #ifdef HAVE_MPI Adios2StMan stman(MPI_COMM_WORLD); newtab.bindAll(stman); Table tab(MPI_COMM_WORLD, newtab, rows); #else Adios2StMan stman; newtab.bindAll(stman); Table tab(newtab, rows); #endif // HAVE_MPI ScalarColumn scalar_Bool (tab, "scalar_Bool"); ScalarColumn scalar_uChar (tab, "scalar_uChar"); ScalarColumn scalar_Short (tab, "scalar_Short"); ScalarColumn scalar_uShort (tab, "scalar_uShort"); ScalarColumn scalar_Int (tab, "scalar_Int"); ScalarColumn scalar_uInt (tab, "scalar_uInt"); ScalarColumn scalar_Float (tab, "scalar_Float"); ScalarColumn scalar_Double (tab, "scalar_Double"); ScalarColumn scalar_Complex (tab, "scalar_Complex"); ScalarColumn scalar_DComplex (tab, "scalar_DComplex"); ScalarColumn scalar_String (tab, "scalar_String"); ArrayColumn array_Bool (tab, "array_Bool"); ArrayColumn array_uChar (tab, "array_uChar"); ArrayColumn array_Short (tab, "array_Short"); ArrayColumn array_uShort (tab, "array_uShort"); ArrayColumn array_Int (tab, "array_Int"); ArrayColumn array_uInt (tab, "array_uInt"); ArrayColumn array_Float (tab, "array_Float"); ArrayColumn array_Double (tab, "array_Double"); ArrayColumn array_Complex (tab, "array_Complex"); ArrayColumn array_DComplex (tab, "array_DComplex"); ArrayColumn array_String (tab, "array_String"); Array arr_Bool(array_pos); Array arr_Char(array_pos); Array arr_uChar(array_pos); Array arr_Short(array_pos); Array arr_uShort(array_pos); Array arr_Int(array_pos); Array arr_uInt(array_pos); Array arr_Float(array_pos); Array arr_Double(array_pos); Array arr_Complex(array_pos); Array arr_DComplex(array_pos); Array arr_String(array_pos); Bool sca_Bool; uChar sca_uChar; Short sca_Short; uShort sca_uShort; Int sca_Int; uInt sca_uInt; Float sca_Float; Double sca_Double; Complex sca_Complex; DComplex sca_DComplex; String sca_String; for(uInt i=0; i(casa_table, "scalar_Bool", rows); VerifyScalarColumn(casa_table, "scalar_uChar", rows); VerifyScalarColumn(casa_table, "scalar_Short", rows); VerifyScalarColumn(casa_table, "scalar_uShort", rows); VerifyScalarColumn(casa_table, "scalar_Int", rows); VerifyScalarColumn(casa_table, "scalar_uInt", rows); VerifyScalarColumn(casa_table, "scalar_Float", rows); VerifyScalarColumn(casa_table, "scalar_Double", rows); VerifyScalarColumn(casa_table, "scalar_Complex", rows); VerifyScalarColumn(casa_table, "scalar_DComplex", rows); VerifyScalarColumn(casa_table, "scalar_String", rows); } void doReadArray(std::string filename, uInt rows, IPosition array_pos){ Table casa_table(filename); VerifyArrayColumn(casa_table, "array_Bool", rows, array_pos); VerifyArrayColumn(casa_table, "array_uChar", rows, array_pos); VerifyArrayColumn(casa_table, "array_Short", rows, array_pos); VerifyArrayColumn(casa_table, "array_uShort", rows, array_pos); VerifyArrayColumn(casa_table, "array_Int", rows, array_pos); VerifyArrayColumn(casa_table, "array_uInt", rows, array_pos); VerifyArrayColumn(casa_table, "array_Float", rows, array_pos); VerifyArrayColumn(casa_table, "array_Double", rows, array_pos); VerifyArrayColumn(casa_table, "array_Complex", rows, array_pos); VerifyArrayColumn(casa_table, "array_DComplex", rows, array_pos); VerifyArrayColumn(casa_table, "array_String", rows, array_pos); } void doCopyTable(std::string inTable, std::string outTable, std::string column) { Table tab(inTable); TableDesc td("", "1", TableDesc::Scratch); SetupNewTable newtab(outTable, td, Table::New); Table duptab(newtab); duptab.addRow(tab.nrow()); Adios2StMan a2stman; duptab.addColumn(tab.tableDesc().columnDesc(column), a2stman); TableCopy::copyColumnData(tab, column, duptab, column, false); } void doReadCopiedTable(std::string filename, std::string column, uInt rows, IPosition array_pos) { Table tab(filename); VerifyArrayColumn(tab, column, rows, array_pos); } int main(int argc, char **argv){ #ifdef HAVE_MPI MPI_Init(&argc,&argv); #endif uInt rows = 100; IPosition array_pos = IPosition(2,5,6); doWriteDefault("default.table", rows, array_pos); doReadScalar("default.table", rows); doReadArray("default.table", rows, array_pos); doCopyTable("default.table", "duplicated.table", "array_Complex"); doReadCopiedTable("duplicated.table", "array_Complex", rows, array_pos); #ifdef HAVE_MPI MPI_Finalize(); #endif } casacore-3.7.1/tables/DataMan/test/tBitFlagsEngine.cc000066400000000000000000000227471476623553700224120ustar00rootroot00000000000000//# tBitFlagsEngine.cc: Test program for class BitFlagsEngine //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //

        Test program for class BitFlagsEngine // This program tests the virtual column engine BitFlagsEngine. // The results are written to stdout. The script executing this program, // compares the results with the reference output file. // Build a description. void createTable() { // First register the virtual column engine. BitFlagsEngine::registerClass(); BitFlagsEngine::registerClass(); BitFlagsEngine::registerClass(); // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.addColumn (ArrayColumnDesc ("virtualcol1")); td.addColumn (ArrayColumnDesc ("storedcol1")); td.addColumn (ArrayColumnDesc ("virtualcol2")); td.addColumn (ArrayColumnDesc ("storedcol2")); td.addColumn (ArrayColumnDesc ("virtualcol3", "", IPosition(3,2,3,4), ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc ("storedcol3", "", IPosition(2,3,4), ColumnDesc::Direct)); // Define keywords telling the bitmask. ColumnDesc& cdesc = td.rwColumnDesc("storedcol1"); Record brec; brec.define ("bit01", 3); brec.define ("bit12", 6); brec.define ("bit23", 12); cdesc.rwKeywordSet().defineRecord ("FLAGSETS", brec); // Now create a new table from the description. SetupNewTable newtab("tBitFlagsEngine_tmp.data", td, Table::New); // Create the virtual column engines and bind the columns to them. // Use keywords to define the mask. Vector writeMask(2); writeMask[0] = "bit01"; writeMask[1] = "bit12"; BitFlagsEngine engine1("virtualcol1", "storedcol1", Vector(1,"bit23"), writeMask); BitFlagsEngine engine2("virtualcol2", "storedcol2"); BitFlagsEngine engine3("virtualcol3", "storedcol3"); newtab.bindColumn ("virtualcol1", engine1); newtab.bindColumn ("virtualcol2", engine2); newtab.bindColumn ("virtualcol3", engine3); Table tab(newtab, 10); // Fill the table via the flag columns. ArrayColumn storedcol1 (tab, "storedcol1"); ArrayColumn storedcol2 (tab, "storedcol2"); ArrayColumn storedcol3 (tab, "storedcol3"); Matrix arri(IPosition(2,3,4)); Matrix arrs(IPosition(2,3,4)); Matrix arrc(IPosition(2,3,4)); for (Int j=0; j<10; j++) { Int i=0; for (uInt i2=0; i2<4; i2++) { for (uInt i1=0; i1<3; i1++) { arri(i1,i2) = (j + i)%2; arrs(i1,i2) = (j + i)%5; arrc(i1,i2) = (j + i)%4; ++i; } } storedcol1.put (j, arri); storedcol2.put (j, arrs); storedcol3.put (j, arrc); arri += 840; arrs += Short(210); arrc += uChar(1); } } void readTable() { // Read back the table. Table tab("tBitFlagsEngine_tmp.data"); ArrayColumn storedcol1 (tab, "storedcol1"); ArrayColumn storedcol2 (tab, "storedcol2"); ArrayColumn storedcol3 (tab, "storedcol3"); ArrayColumn virtualcol1 (tab, "virtualcol1"); ArrayColumn virtualcol2 (tab, "virtualcol2"); ArrayColumn virtualcol3 (tab, "virtualcol3"); Matrix arrd1(IPosition(2,3,4)); Matrix arrd2(IPosition(2,3,4)); Matrix arrd3(IPosition(2,3,4)); Matrix arrd3slice(arrd3(Slice(0,1,2),Slice(0,2,2))); Matrix arri(IPosition(2,3,4)); Matrix arrs(IPosition(2,3,4)); Matrix arrc(IPosition(2,3,4)); Slice tmp; Slicer nslice (tmp, tmp, Slicer::endIsLength); Slicer nslice2(Slice(0,1,2), Slice(0,2,2), Slicer::endIsLength); for (uInt j=0; j<10; j++) { { Int i=0; for (uInt i2=0; i2<4; i2++) { for (uInt i1=0; i1<3; i1++) { arri(i1,i2) = (j + i)%2; arrs(i1,i2) = (j + i)%5; arrc(i1,i2) = (j + i)%4; arrd1(i1,i2) = arri(i1,i2) & 12; arrd2(i1,i2) = arrs(i1,i2); arrd3(i1,i2) = arrc(i1,i2); ++i; } } } Array ai; Array as; Array ac; Array arrbool; Array arrboolslice; cout << "get row " << j << endl; storedcol1.get (j, ai); if (!allEQ (ai, arri)) { cout << "error in storedcol1 in row " << j << endl; cout << ai << endl; cout << arri << endl; } virtualcol1.get (j, arrbool); if (!allEQ (arrbool, arrd1)) { cout << "error in virtualcol1 in row " << j << endl; cout << arrbool << endl; cout << arrd1 << endl; } storedcol2.get (j, as); if (!allEQ (as, arrs)) { cout << "error in storedcol2 in row " << j << endl; cout << as << endl; cout << arrs << endl; } virtualcol2.get (j, arrbool); if (!allEQ (arrbool, arrd2)) { cout << "error in virtualcol2 in row " << j << endl; cout << arrbool << endl; cout << arrd2 << endl; } storedcol3.get (j, ac); if (!allEQ (ac, arrc)) { cout << "error in storedcol3 in row " << j << endl; cout << ac << endl; cout << arrc << endl; } virtualcol3.get (j, arrbool); if (!allEQ (arrbool, arrd3)) { cout << "error in virtualcol3 in row " << j << endl; cout << arrbool << endl; cout << arrd3 << endl; } virtualcol3.getSlice (j, nslice, arrbool); if (!allEQ (arrbool, arrd3)) { cout << "error in virtualcol3 (entire slice) in row " << j << endl; cout << arrbool << endl; cout << arrd3 << endl; } virtualcol3.getSlice (j, nslice2, arrboolslice); if (!allEQ (arrboolslice, arrd3slice)) { cout << "error in virtualcol3 (partial slice) in row " << j << endl; cout << arrboolslice << endl; cout << arrd3slice << endl; } } // Now test getting the columns. { Cube arrd2(IPosition(3,3,4,10)); Slicer nslice2(Slice(0,2,1), Slice(1,2,2), Slicer::endIsLength); for (uInt j=0; j<10; j++) { Int i = 0; for (uInt i2=0; i2<4; i2++) { for (uInt i1=0; i1<3; i1++) { Int val = (j + i)%5; arrd2(i1,i2,j) = val; ++i; } } } { Cube arrvald = virtualcol2.getColumn(); if (!allEQ (arrvald, arrd2)) { cout << "error in virtualcol2 getcolumn " << endl; cout << arrvald << endl; cout << arrd2 << endl; } } { Cube arrvald = virtualcol2.getColumnRange(Slice(1,4,2)); if (!allEQ (arrvald, arrd2(Slice(0,3,1),Slice(0,4,1),Slice(1,4,2)))) { cout << "error in virtualcol2 getcolumnrange " << endl; cout << arrvald << endl; cout << arrd2 << endl; } } { Cube arrvald = virtualcol2.getColumn(nslice2); if (!allEQ (arrvald, arrd2(Slice(0,2,1),Slice(1,2,2),Slice(0,10,1)))) { cout << "error in virtualcol2 getcolumnslice " << endl; cout << arrvald << endl; cout << arrd2 << endl; } } { Cube arrvald = virtualcol2.getColumnRange(Slice(1,4,2), nslice2); if (!allEQ (arrvald, arrd2(Slice(0,2,1),Slice(1,2,2),Slice(1,4,2)))) { cout << "error in virtualcol2 getcolumnrangeslice " << endl; cout << arrvald << endl; cout << arrd2 << endl; } } } } int main () { // return 0; try { createTable(); readTable(); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } casacore-3.7.1/tables/DataMan/test/tBitFlagsEngine.out000066400000000000000000000001441476623553700226170ustar00rootroot00000000000000get row 0 get row 1 get row 2 get row 3 get row 4 get row 5 get row 6 get row 7 get row 8 get row 9 casacore-3.7.1/tables/DataMan/test/tCompressComplex.cc000066400000000000000000000411001476623553700226740ustar00rootroot00000000000000//# tCompressComplex.cc: Test program for class CompressComplex //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Test program for class CompressComplex // This program tests the virtual column engine CompressComplex. // The results are written to stdout. The script executing this program, // compares the results with the reference output file. // First build a description. void writeData (Bool isSD, Bool autoScale) { // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.comment() = "A test of class TableDesc"; td.addColumn (ArrayColumnDesc ("target1", 3)); td.addColumn (ArrayColumnDesc ("source1")); td.addColumn (ArrayColumnDesc ("source2","", IPosition(3,2,3,4), ColumnDesc::Direct)); td.addColumn (ScalarColumnDesc ("scale1")); td.addColumn (ScalarColumnDesc ("offset1")); td.defineHypercolumn ("tileddata", 4, stringToVector("target1")); // Now create a new table from the description. SetupNewTable newtab("tCompressComplex_tmp.data", td, Table::New); // Create the virtual column engine with the scale factors // and bind the columns to them. if (isSD) { CompressComplexSD engine1("source1", "target1", "scale1", "offset1", autoScale); newtab.bindColumn ("source1", engine1); } else { CompressComplex engine1("source1", "target1", "scale1", "offset1", autoScale); newtab.bindColumn ("source1", engine1); } TiledShapeStMan tsm("tileddata", IPosition(4,2,3,4,4)); newtab.bindColumn ("target1", tsm); Table tab(newtab, 10); // Fill the table via the virtual columns. ArrayColumn source1 (tab, "source1"); ArrayColumn source2 (tab, "source2"); ScalarColumn scale1 (tab, "scale1"); ScalarColumn offset1 (tab,"offset1"); Cube arrf(IPosition(3,2,3,4)); uInt i; i=16; for (uInt i2=0; i2<4; i2++) { for (uInt i1=0; i1<3; i1++) { arrf(0,i1,i2) = Complex(i,i+36); i += 12; arrf(1,i1,i2) = Complex(i,0); i += 12; } } for (i=0; i<10; i++) { if (!autoScale) { scale1.put (i, 2.); offset1.put (i, 20.); } if (i != 5) { source1.put (i, arrf); } source2.put (i, arrf); arrf += Complex(12*arrf.nelements(), 12*arrf.nelements()); } // Write the 5th row in Slices. arrf -= Complex(5*12*arrf.nelements(), 5*12*arrf.nelements()); source1.setShape (5, arrf.shape()); for (i=0; i<3; i++) { source1.putSlice (5, Slicer(IPosition(3,0,i,0), IPosition(3,2,1,4)), arrf(IPosition(3,0,i,0), IPosition(3,1,i,3))); } { tab.flush(); ROTiledStManAccessor acc(tab, "tileddata"); acc.showCacheStatistics (cout); } //# Do an erroneous thing. //# However, this fails to run on Linux (so outcommented). /// SetupNewTable newtab2("tCompressComplex_tmp.dat2", td, Table::Scratch); /// newtab2.bindColumn ("source2", engine1); /// try { /// Table tab2(newtab2, 10); // bound to incorrect column /// } catch (std::exception x) { /// cout << x.what() << endl; /// } } Bool checkData (Bool autoScale) { Bool ok = True; // Read back the table. Table tab("tCompressComplex_tmp.data"); ArrayColumn source1 (tab, "source1"); ArrayColumn source2 (tab, "source2"); ArrayColumn target1 (tab, "target1"); ScalarColumn scale1 (tab, "scale1"); ScalarColumn offset1 (tab,"offset1"); Cube arri1(IPosition(3,2,3,4)); Cube arrvali(IPosition(3,2,3,4)); Cube arrf1(IPosition(3,2,3,4)); Cube arrvalf(IPosition(3,2,3,4)); RefRows refrows(1,9,2); Slicer slicer(IPosition(3,0,1,0), IPosition(3,2,2,2), IPosition(3,1,1,2)); Array arrCol1 (source1.getColumn()); Array arrColSlice1 (source1.getColumn(slicer)); Array arrCells1 (source1.getColumnCells(refrows)); Array arrCellsSlice1 (source1.getColumnCells(refrows,slicer)); Array arrCol2 (source2.getColumn()); Array arrColSlice2 (source2.getColumn(slicer)); Array arrCells2 (source2.getColumnCells(refrows)); Array arrCellsSlice2 (source2.getColumnCells(refrows,slicer)); Slicer slicercol(IPosition(4,0,1,0,0), IPosition(4,2,2,2,10), IPosition(4,1,1,2,1)); Slicer slicercells(IPosition(4,0,0,0,1), IPosition(4,2,3,4,5), IPosition(4,1,1,1,2)); Slicer slicercsl (IPosition(4,0,1,0,1), IPosition(4,2,2,2,5), IPosition(4,1,1,2,2)); AlwaysAssertExit (allEQ(arrColSlice1, arrCol1(slicercol))); AlwaysAssertExit (allEQ(arrCells1, arrCol1(slicercells))); AlwaysAssertExit (allEQ(arrCellsSlice1, arrCol1(slicercsl))); AlwaysAssertExit (allEQ(arrColSlice2, arrCol2(slicercol))); AlwaysAssertExit (allEQ(arrCells2, arrCol2(slicercells))); AlwaysAssertExit (allEQ(arrCellsSlice2, arrCol2(slicercsl))); uInt i=0; for (uInt i2=0; i2<4; i2++) { for (uInt i1=0; i1<3; i1++) { arrf1(0,i1,i2) = Complex (16 + 12*i, 16 + 12*i + 36); arri1(0,i1,i2) = 65536 * (6*i - 2) + (6*i - 2 + 18); i++; arrf1(1,i1,i2) = Complex (16 + 12*i, 0); arri1(1,i1,i2) = 65536 * (6*i - 2) + -10; i++; } } ArrayIterator iter1(arrCol1, 3); ArrayIterator iter2(arrCol2, 3); for (i=0; i<10; i++) { cout << "get row " << i << endl; source1.get (i, arrvalf); if (!allNear (arrvalf, arrf1, 1e-4)) { cout << "error in source1 in row " << i << endl; cout << "Read: " << arrvalf << endl; cout << "Expected: " << arrf1 << endl; ok = False; } AlwaysAssertExit (allEQ(arrvalf, iter1.array())); AlwaysAssertExit (allEQ(arrvalf(slicer), source1.getSlice(i, slicer))); if (!autoScale) { target1.get (i, arrvali); if (!allEQ (arrvali, arri1)) { cout << "error in target1 in row " << i << endl; cout << "Read: " << arrvali << endl; cout << "Expected: " << arri1 << endl; ok = False; } } else { Float offs = offset1(i); Float so = (arrvalf(0,2,3).imag() + arrvalf(1,0,0).imag()) / 2; if (!near(offs, so)) { cout << "error in offset1 in row " << i << endl; cout << "Read: " << offs << endl; cout << "Expected: " << so << endl; ok = False; } Float scale = scale1(i); so = (arrvalf(0,2,3).imag() - arrvalf(1,0,0).imag()) / 65534; if (!near(scale, so)) { cout << "error in scale1 in row " << i << endl; cout << "Read: " << scale << endl; cout << "Expected: " << so << endl; ok = False; } } source2.get (i, arrvalf); if (!allEQ (arrvalf, arrf1)) { cout << "error in source2 in row " << i << endl; cout << "Read: " << arrvalf << endl; cout << "Expected: " << arrf1 << endl; ok = False; } AlwaysAssertExit (allEQ(arrvalf, iter2.array())); AlwaysAssertExit (allEQ(arrvalf(slicer), source2.getSlice(i, slicer))); arrf1 += Complex(12*arrf1.nelements(), 12*arrf1.nelements()); arri1 += Int(65536 * 6*arri1.nelements() + 6*arri1.nelements()); iter1.next(); iter2.next(); } return ok; } Bool checkDataSD (bool autoScale) { Bool ok = True; // Read back the table. Table tab("tCompressComplex_tmp.data"); ArrayColumn source1 (tab, "source1"); ArrayColumn source2 (tab, "source2"); ArrayColumn target1 (tab, "target1"); ScalarColumn scale1 (tab, "scale1"); ScalarColumn offset1 (tab,"offset1"); Cube arri1(IPosition(3,2,3,4)); Cube arrvali(IPosition(3,2,3,4)); Cube arrf1(IPosition(3,2,3,4)); Cube arrvalf(IPosition(3,2,3,4)); uInt i=0; for (uInt i2=0; i2<4; i2++) { for (uInt i1=0; i1<3; i1++) { arrf1(0,i1,i2) = Complex (16 + 12*i, 16 + 12*i + 36); arri1(0,i1,i2) = 65536 * (6*i - 2) + 2*(3*i - 1 + 9) + 1; i++; arrf1(1,i1,i2) = Complex (16 + 12*i, 0); arri1(1,i1,i2) = 2 * 32768 * (6*i - 2); i++; } } cout << "get SD row 0" << endl; source1.get (0, arrvalf); if (!allNear (arrvalf, arrf1, 1e-4)) { cout << "error in source1 in row 0" << endl; cout << "Read: " << arrvalf << endl; cout << "Expected: " << arrf1 << endl; ok = False; } if (!autoScale) { target1.get (0, arrvali); if (!allEQ (arrvali, arri1)) { cout << "error in target1 in row 0" << endl; cout << "Read: " << arrvali << endl; cout << "Expected: " << arri1 << endl; ok = False; } } else { Float offs = offset1(0); Float so = (arrvalf(0,2,3).imag() + arrvalf(0,0,0).real()) / 2; if (!near(offs, so, 1e-4)) { cout << "error in offset1 in row 0" << endl; cout << "Read: " << offs << endl; cout << "Expected: " << so << endl; ok = False; } Float scale = scale1(0); so = (arrvalf(0,2,3).imag() - arrvalf(0,0,0).real()) / 65534; if (!near(scale, so, 1e-4)) { cout << "error in scale1 in row 0" << endl; cout << "Read: " << scale << endl; cout << "Expected: " << so << endl; ok = False; } } i = 1; for (uInt i2=0; i2<4; i2++) { for (uInt i1=0; i1<3; i1++) { arri1(1,i1,i2) = 65536 * (6*i - 2) + -10 + 1; i+=2; } } for (i=1; i<10; i++) { arrf1 += Complex(12*arrf1.nelements(), 12*arrf1.nelements()); arri1 += Int(65536 * 6*arri1.nelements() + 6*arri1.nelements()); cout << "get SD row " << i << endl; source1.get (i, arrvalf); if (!allNear (arrvalf, arrf1, 1e-4)) { cout << "error in source1 in row " << i << endl; cout << "Read: " << arrvalf << endl; cout << "Expected: " << arrf1 << endl; ok = False; } if (!autoScale) { target1.get (i, arrvali); if (!allEQ (arrvali, arri1)) { cout << "error in target1 in row " << i << endl; cout << "Read: " << arrvali << endl; cout << "Expected: " << arri1 << endl; ok = False; } } else { Float offs = offset1(i); Float so = (arrvalf(0,2,3).imag() + arrvalf(1,0,0).imag()) / 2; if (!near(offs, so, 1e-4)) { cout << "error in offset1 in row " << i << endl; cout << "Read: " << offs << endl; cout << "Expected: " << so << endl; ok = False; } Float scale = scale1(i); so = (arrvalf(0,2,3).imag() - arrvalf(1,0,0).imag()) / 65534; if (!near(scale, so, 1e-4)) { cout << "error in scale1 in row " << i << endl; cout << "Read: " << scale << endl; cout << "Expected: " << so << endl; ok = False; } } source2.get (i, arrvalf); if (!allEQ (arrvalf, arrf1)) { cout << "error in source2 in row " << i << endl; cout << "Read: " << arrvalf << endl; cout << "Expected: " << arrf1 << endl; ok = False; } } return ok; } void testSpeed() { { // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.comment() = "A test of class TableDesc"; td.addColumn (ArrayColumnDesc ("target1", IPosition(3,2,3,4), ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc ("source1", IPosition(3,2,3,4), ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc ("source2","", IPosition(3,2,3,4), ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc ("target3", IPosition(3,2,3,4), ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc ("source3", IPosition(3,2,3,4), ColumnDesc::Direct)); td.addColumn (ScalarColumnDesc ("scale1")); td.addColumn (ScalarColumnDesc ("offset1")); // Now create a new table from the description. SetupNewTable newtab("tCompressComplex_tmp.data", td, Table::New); // Create the virtual column engine with the scale factors // and bind the columns to them. CompressComplex engine1("source1", "target1", "scale1", "offset1"); CompressComplex engine3("source3", "target3", 2.0, 4.0); newtab.bindColumn ("source1", engine1); newtab.bindColumn ("source3", engine3); Table tab(newtab, 10000); // Fill the table via the virtual columns. ArrayColumn source1 (tab, "source1"); ArrayColumn source2 (tab, "source2"); ScalarColumn scale1 (tab,"scale1"); ScalarColumn offset1 (tab,"offset1"); Cube arrf(IPosition(3,2,3,4)); uInt i; i=20; for (uInt i2=0; i2<4; i2++) { for (uInt i1=0; i1<3; i1++) { arrf(0,i1,i2) = Complex(i,i+36); i += 6; arrf(1,i1,i2) = Complex(i,0); i += 6; } } for (i=0; i<10; i++) { scale1.put (i, 2.); offset1.put (i, 4.); source1.put (i, arrf); source2.put (i, arrf); arrf += Complex(6*arrf.nelements(), 6*arrf.nelements()); } } { { // Time reading back column source1. Table tab("tCompressComplex_tmp.data"); ArrayColumn source (tab, "source1"); Cube arrvalf(IPosition(3,2,3,4)); Timer timer; uInt nrow = tab.nrow(); for (uInt i=0; i source (tab, "source2"); Cube arrvalf(IPosition(3,2,3,4)); Timer timer; uInt nrow = tab.nrow(); for (uInt i=0; i source (tab, "source3"); Cube arrvalf(IPosition(3,2,3,4)); Timer timer; uInt nrow = tab.nrow(); for (uInt i=0; i source (tab, "source1"); Timer timer; source.getColumn(); timer.show(); } { // Time reading back column source2. Table tab("tCompressComplex_tmp.data"); ArrayColumn source (tab, "source2"); Timer timer; source.getColumn(); timer.show(); } { // Time reading back column source3. Table tab("tCompressComplex_tmp.data"); ArrayColumn source (tab, "source3"); Timer timer; source.getColumn(); timer.show(); } } } int main () { Int sts = 0; try { writeData (False, False); if (!checkData (False)) sts=1; writeData (False, True); if (!checkData (True)) sts=1; writeData (True, False); if (!checkDataSD (False)) sts=1; writeData (True, True); if (!checkDataSD (True)) sts=1; testSpeed(); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return sts; } casacore-3.7.1/tables/DataMan/test/tCompressFloat.cc000066400000000000000000000265201476623553700223430ustar00rootroot00000000000000//# tCompressFloat.cc: Test program for class CompressFloat //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Test program for class CompressFloat // This program tests the virtual column engine CompressFloat. // The results are written to stdout. The script executing this program, // compares the results with the reference output file. // First build a description. void writeData (Bool autoScale) { // First register the virtual column engine. CompressFloat::registerClass(); // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.comment() = "A test of class TableDesc"; td.addColumn (ArrayColumnDesc ("target1")); td.addColumn (ArrayColumnDesc ("source1")); td.addColumn (ArrayColumnDesc ("source2","", IPosition(3,2,3,4), ColumnDesc::Direct)); td.addColumn (ScalarColumnDesc ("scale1")); td.addColumn (ScalarColumnDesc ("offset1")); // Now create a new table from the description. SetupNewTable newtab("tCompressFloat_tmp.data", td, Table::New); // Create the virtual column engine with the scale factors // and bind the columns to them. CompressFloat engine1("source1", "target1", "scale1", "offset1", autoScale); newtab.bindColumn ("source1", engine1); Table tab(newtab, 10); // Fill the table via the virtual columns. ArrayColumn source1 (tab, "source1"); ArrayColumn source2 (tab, "source2"); ScalarColumn scale1 (tab, "scale1"); ScalarColumn offset1 (tab,"offset1"); Cube arrf(IPosition(3,2,3,4)); uInt i; i=2; for (uInt i2=0; i2<4; i2++) { for (uInt i1=0; i1<3; i1++) { for (uInt i0=0; i0<2; i0++) { arrf(i0,i1,i2) = i; i += 6; } } } for (i=0; i<10; i++) { if (!autoScale) { scale1.put (i, 2.); offset1.put (i, 4.); } if (i != 5) { source1.put (i, arrf); } source2.put (i, arrf); arrf += (Float)(6*arrf.nelements()); } // Write the 5th row in Slices. arrf -= (Float)(5*6*arrf.nelements()); source1.setShape (5, arrf.shape()); for (i=0; i<3; i++) { source1.putSlice (5, Slicer(IPosition(3,0,i,0), IPosition(3,2,1,4)), arrf(IPosition(3,0,i,0), IPosition(3,1,i,3))); } //# Do an erroneous thing. //# However, this fails to run on Linux (so outcommented). /// SetupNewTable newtab2("tCompressFloat_tmp.dat2", td, Table::Scratch); /// newtab2.bindColumn ("source2", engine1); /// try { /// Table tab2(newtab2, 10); // bound to incorrect column /// } catch (std::exception x) { /// cout << x.what() << endl; /// } } Bool checkData (Bool autoScale) { Bool ok = True; // Read back the table. Table tab("tCompressFloat_tmp.data"); ArrayColumn source1 (tab, "source1"); ArrayColumn source2 (tab, "source2"); ArrayColumn target1 (tab, "target1"); Cube arri1(IPosition(3,2,3,4)); Cube arrvali(IPosition(3,2,3,4)); Cube arrf1(IPosition(3,2,3,4)); Cube arrvalf(IPosition(3,2,3,4)); RefRows refrows(1,9,2); Slicer slicer(IPosition(3,0,1,0), IPosition(3,2,2,2), IPosition(3,1,1,2)); Array arrCol1 (source1.getColumn()); Array arrColSlice1 (source1.getColumn(slicer)); Array arrCells1 (source1.getColumnCells(refrows)); Array arrCellsSlice1 (source1.getColumnCells(refrows,slicer)); Array arrCol2 (source2.getColumn()); Array arrColSlice2 (source2.getColumn(slicer)); Array arrCells2 (source2.getColumnCells(refrows)); Array arrCellsSlice2 (source2.getColumnCells(refrows,slicer)); Slicer slicercol(IPosition(4,0,1,0,0), IPosition(4,2,2,2,10), IPosition(4,1,1,2,1)); Slicer slicercells(IPosition(4,0,0,0,1), IPosition(4,2,3,4,5), IPosition(4,1,1,1,2)); Slicer slicercsl (IPosition(4,0,1,0,1), IPosition(4,2,2,2,5), IPosition(4,1,1,2,2)); AlwaysAssertExit (allEQ(arrColSlice1, arrCol1(slicercol))); AlwaysAssertExit (allEQ(arrCells1, arrCol1(slicercells))); AlwaysAssertExit (allEQ(arrCellsSlice1, arrCol1(slicercsl))); AlwaysAssertExit (allEQ(arrColSlice2, arrCol2(slicercol))); AlwaysAssertExit (allEQ(arrCells2, arrCol2(slicercells))); AlwaysAssertExit (allEQ(arrCellsSlice2, arrCol2(slicercsl))); uInt i=0; for (uInt i2=0; i2<4; i2++) { for (uInt i1=0; i1<3; i1++) { for (uInt i0=0; i0<2; i0++) { arrf1(i0,i1,i2) = 2 + 6*i; arri1(i0,i1,i2) = 3*i - 1; i++; } } } ArrayIterator iter1(arrCol1, 3); ArrayIterator iter2(arrCol2, 3); for (i=0; i<10; i++) { cout << "get row " << i << endl; source1.get (i, arrvalf); if (!allNear (arrvalf, arrf1, 1e-4)) { cout << "error in source1 in row " << i << endl; cout << "Read: " << arrvalf << endl; cout << "Expected: " << arrf1 << endl; ok = False; } AlwaysAssertExit (allEQ(arrvalf, iter1.array())); AlwaysAssertExit (allEQ(arrvalf(slicer), source1.getSlice(i, slicer))); if (!autoScale) { target1.get (i, arrvali); if (!allEQ (arrvali, arri1)) { cout << "error in target1 in row " << i << endl; cout << "Read: " << arrvali << endl; cout << "Expected: " << arri1 << endl; ok = False; } } source2.get (i, arrvalf); if (!allEQ (arrvalf, arrf1)) { cout << "error in source2 in row " << i << endl; cout << "Read: " << arrvalf << endl; cout << "Expected: " << arrf1 << endl; ok = False; } AlwaysAssertExit (allEQ(arrvalf, iter2.array())); AlwaysAssertExit (allEQ(arrvalf(slicer), source2.getSlice(i, slicer))); arrf1 += (Float)(6*arrf1.nelements()); arri1 += (Short)(3*arri1.nelements()); iter1.next(); iter2.next(); } return ok; } void testSpeed() { { // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.comment() = "A test of class TableDesc"; td.addColumn (ArrayColumnDesc ("target1", IPosition(3,2,3,4), ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc ("source1", IPosition(3,2,3,4), ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc ("source2","", IPosition(3,2,3,4), ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc ("target3", IPosition(3,2,3,4), ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc ("source3", IPosition(3,2,3,4), ColumnDesc::Direct)); td.addColumn (ScalarColumnDesc ("scale1")); td.addColumn (ScalarColumnDesc ("offset1")); // Now create a new table from the description. SetupNewTable newtab("tCompressFloat_tmp.data", td, Table::New); // Create the virtual column engine with the scale factors // and bind the columns to them. CompressFloat engine1("source1", "target1", "scale1", "offset1", False); CompressFloat engine3("source3", "target3", 2.0, 4.0); newtab.bindColumn ("source1", engine1); newtab.bindColumn ("source3", engine3); Table tab(newtab, 10000); // Fill the table via the virtual columns. ArrayColumn source1 (tab, "source1"); ArrayColumn source2 (tab, "source2"); ScalarColumn scale1 (tab,"scale1"); ScalarColumn offset1 (tab,"offset1"); Cube arrf(IPosition(3,2,3,4)); uInt i; i=2; for (uInt i2=0; i2<4; i2++) { for (uInt i1=0; i1<3; i1++) { for (uInt i0=0; i0<2; i0++) { arrf(i0,i1,i2) = i; i += 6; } } } for (i=0; i<10; i++) { scale1.put (i, 2.); offset1.put (i, 4.); source1.put (i, arrf); source2.put (i, arrf); arrf += (Float)(6*arrf.nelements()); } } { { // Time reading back column source1. Table tab("tCompressFloat_tmp.data"); ArrayColumn source (tab, "source1"); Cube arrvalf(IPosition(3,2,3,4)); Timer timer; uInt nrow = tab.nrow(); for (uInt i=0; i source (tab, "source2"); Cube arrvalf(IPosition(3,2,3,4)); Timer timer; uInt nrow = tab.nrow(); for (uInt i=0; i source (tab, "source3"); Cube arrvalf(IPosition(3,2,3,4)); Timer timer; uInt nrow = tab.nrow(); for (uInt i=0; i source (tab, "source1"); Timer timer; source.getColumn(); timer.show(); } { // Time reading back column source2. Table tab("tCompressFloat_tmp.data"); ArrayColumn source (tab, "source2"); Timer timer; source.getColumn(); timer.show(); } { // Time reading back column source3. Table tab("tCompressFloat_tmp.data"); ArrayColumn source (tab, "source3"); Timer timer; source.getColumn(); timer.show(); } } } int main () { Int sts=0; try { writeData (False); if (! checkData (False)) sts=1; writeData (True); if (! checkData (True)) sts=1; testSpeed(); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return sts; } casacore-3.7.1/tables/DataMan/test/tDataManInfo.cc000066400000000000000000000251671476623553700217110ustar00rootroot00000000000000//# tDataManInfo.cc: Test program for class DataManInfo //# Copyright (C) 2021 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include using namespace casacore; using namespace std; // // Test program for class DataManInfo // // Test modifying the dminfo record. void testDM() { TableDesc td; td.addColumn(ScalarColumnDesc("col1")); td.addColumn(ScalarColumnDesc("col2")); td.addColumn(ScalarColumnDesc("col3")); td.addColumn(ArrayColumnDesc("col4")); // Now create a new table from the description. SetupNewTable aNewTab("tTableCopy_tmp.dm", td, Table::New); Table tabl(aNewTab); Record dminfo = tabl.dataManagerInfo(); cout << dminfo; Vector remCols1 = DataManInfo::removeDminfoColumns (dminfo, Vector(1, "col1"), "Standard"); cout << dminfo << remCols1 << endl; Vector remCols2 = DataManInfo::removeDminfoColumns (dminfo, Vector(1, "col1")); cout << dminfo << remCols2 << endl; DataManInfo::setTiledStMan (dminfo, Vector(1, "col3"), "TiledShapeStMan", "TSMData", IPosition(3,3,4,5)); cout << dminfo << endl; } void mergeTestEmpty() { Record rec; DataManInfo::mergeInfo (rec, Record()); AlwaysAssertExit (rec.nfields() == 0); } void mergeTestNonEmpty() { Record dminfo; Record sub; sub.define("TYPE", "abc"); sub.define ("COLUMNS", Vector(1, "col1")); dminfo.defineRecord (0, sub); Record rec; DataManInfo::mergeInfo (rec, dminfo); AlwaysAssertExit (rec.nfields() == 1); const Record& dm1 = rec.subRecord(0); AlwaysAssertExit (dm1.asString("TYPE") =="abc"); AlwaysAssertExit (! dm1.isDefined("NAME")); AlwaysAssertExit (dm1.asArrayString("COLUMNS").size() == 1); AlwaysAssertExit (dm1.asArrayString("COLUMNS").data()[0] == "col1"); } void finalizeTestEmpty() { Record rec = DataManInfo::finalizeMerge (TableDesc(), Record()); AlwaysAssertExit (rec.nfields() == 0); } void finalizeTestNonEmpty() { Record dminfo; Record sub; sub.define("TYPE", "abc"); sub.define("NAME", String()); sub.define ("COLUMNS", Vector(1, "col1")); dminfo.defineRecord (0, sub); { Record rec = DataManInfo::finalizeMerge (TableDesc(), dminfo); AlwaysAssertExit (rec.nfields() == 0); } TableDesc desc; desc.addColumn (ScalarColumnDesc("col1")); Record rec; DataManInfo::mergeInfo (rec, dminfo); rec = DataManInfo::finalizeMerge (desc, dminfo); AlwaysAssertExit (rec.nfields() == 1); const Record& dm1 = rec.subRecord(0); AlwaysAssertExit (dm1.nfields() == 3); AlwaysAssertExit (dm1.asString("TYPE") =="abc"); AlwaysAssertExit (dm1.asString("NAME") == "col1"); AlwaysAssertExit (dm1.asArrayString("COLUMNS").size() == 1); AlwaysAssertExit (dm1.asArrayString("COLUMNS").data()[0] == "col1"); } void largeTest() { // Create a table description with various data manager types and names. TableDesc desc; desc.addColumn (ScalarColumnDesc("col1")); desc.addColumn (ScalarColumnDesc("col2", "", "dmtype1", "dmname1")); desc.addColumn (ScalarColumnDesc("col3", "", "dmtype2", "")); desc.addColumn (ScalarColumnDesc("col4", "", "dmtype1", "dmname2")); desc.addColumn (ScalarColumnDesc("col5", "", "", "dmname3")); desc.addColumn (ScalarColumnDesc("col6")); desc.addColumn (ScalarColumnDesc("col7")); desc.addColumn (ScalarColumnDesc("col8")); Record dminfoRes; { // Create a dminfo object for a few columns. Record dminfo; Record dm1; dm1.define ("TYPE", "dmtype1"); dm1.define ("NAME", "dmname1a"); dm1.define ("COLUMNS", Vector(1, "col2")); dminfo.defineRecord (0, dm1); DataManInfo::mergeInfo (dminfoRes, dminfo); AlwaysAssertExit (dminfoRes.nfields() == 1); AlwaysAssertExit (dminfoRes.subRecord(0).nfields() == 3); AlwaysAssertExit (dminfoRes.subRecord(0).asString("TYPE") == "dmtype1"); AlwaysAssertExit (dminfoRes.subRecord(0).asString("NAME") == "dmname1a"); AlwaysAssertExit (dminfoRes.subRecord(0).asArrayString("COLUMNS").size() == 1); AlwaysAssertExit (dminfoRes.subRecord(0).asArrayString("COLUMNS").data()[0] == "col2"); } // Merge another dminfo. { Record dminfo; Record dm1; dm1.define ("TYPE", "dmtype1"); dm1.define ("NAME", "dmname2"); Record spec1; spec1.define ("BSZ", 10); dm1.defineRecord ("SPEC", spec1); dminfo.defineRecord (0, dm1); Record dm2; dm2.define ("TYPE", "dmtype2"); dm2.define ("NAME", "dmname2a"); dm2.define ("COLUMNS", Vector({"col6", "col7"})); dminfo.defineRecord (1, dm2); DataManInfo::mergeInfo (dminfoRes, dminfo); AlwaysAssertExit (dminfoRes.nfields() == 3); AlwaysAssertExit (dminfoRes.subRecord(0).nfields() == 3); AlwaysAssertExit (dminfoRes.subRecord(1).nfields() == 3); AlwaysAssertExit (dminfoRes.subRecord(2).nfields() == 3); AlwaysAssertExit (dminfoRes.subRecord(0).asString("TYPE") == "dmtype1"); AlwaysAssertExit (dminfoRes.subRecord(1).asString("TYPE") == "dmtype1"); AlwaysAssertExit (dminfoRes.subRecord(2).asString("TYPE") == "dmtype2"); AlwaysAssertExit (dminfoRes.subRecord(0).asString("NAME") == "dmname1a"); AlwaysAssertExit (dminfoRes.subRecord(1).asString("NAME") == "dmname2"); AlwaysAssertExit (dminfoRes.subRecord(2).asString("NAME") == "dmname2a"); AlwaysAssertExit (dminfoRes.subRecord(0).asArrayString("COLUMNS").size() == 1); AlwaysAssertExit (dminfoRes.subRecord(0).asArrayString("COLUMNS").data()[0] == "col2"); AlwaysAssertExit (dminfoRes.subRecord(2).asArrayString("COLUMNS").size() == 2); AlwaysAssertExit (dminfoRes.subRecord(2).asArrayString("COLUMNS").data()[0] == "col6"); AlwaysAssertExit (dminfoRes.subRecord(2).asArrayString("COLUMNS").data()[1] == "col7"); AlwaysAssertExit (dminfoRes.subRecord(1).subRecord("SPEC").nfields() == 1); AlwaysAssertExit (dminfoRes.subRecord(1).subRecord("SPEC").asInt("BSZ") == 10); } // Merge another dminfo. { Record dminfo; Record dm1; dm1.define ("TYPE", "dmtype1"); dm1.define ("NAME", "dmname1a"); dm1.define ("COLUMNS", Vector({"col6"})); dminfo.defineRecord (0, dm1); DataManInfo::mergeInfo (dminfoRes, dminfo); AlwaysAssertExit (dminfoRes.nfields() == 3); AlwaysAssertExit (dminfoRes.subRecord(0).nfields() == 3); AlwaysAssertExit (dminfoRes.subRecord(1).nfields() == 3); AlwaysAssertExit (dminfoRes.subRecord(2).nfields() == 3); AlwaysAssertExit (dminfoRes.subRecord(0).asString("TYPE") == "dmtype1"); AlwaysAssertExit (dminfoRes.subRecord(1).asString("TYPE") == "dmtype1"); AlwaysAssertExit (dminfoRes.subRecord(2).asString("TYPE") == "dmtype2"); AlwaysAssertExit (dminfoRes.subRecord(0).asString("NAME") == "dmname1a"); AlwaysAssertExit (dminfoRes.subRecord(1).asString("NAME") == "dmname2"); AlwaysAssertExit (dminfoRes.subRecord(2).asString("NAME") == "dmname2a"); AlwaysAssertExit (dminfoRes.subRecord(0).asArrayString("COLUMNS").size() == 2); AlwaysAssertExit (dminfoRes.subRecord(0).asArrayString("COLUMNS").data()[0] == "col2"); AlwaysAssertExit (dminfoRes.subRecord(0).asArrayString("COLUMNS").data()[1] == "col6"); AlwaysAssertExit (dminfoRes.subRecord(2).asArrayString("COLUMNS").size() == 1); AlwaysAssertExit (dminfoRes.subRecord(2).asArrayString("COLUMNS").data()[0] == "col7"); AlwaysAssertExit (dminfoRes.subRecord(1).subRecord("SPEC").nfields() == 1); AlwaysAssertExit (dminfoRes.subRecord(1).subRecord("SPEC").asInt("BSZ") == 10); } Record dminfoFinal = DataManInfo::finalizeMerge (desc, dminfoRes); cout << dminfoFinal; } void testUniqueName() { Record dminfo; Record dm; dminfo.defineRecord (0, dm); // no NAME, becomes DM dm.define ("NAME", "nma"); dminfo.defineRecord (1, dm); dminfo.defineRecord (2, dm); // nma becomes nma_2 dm.define ("NAME", "nma_1"); dminfo.defineRecord (3, dm); dm.define ("NAME", ""); dminfo.defineRecord (4, dm); // becomes DM_1 dminfo.defineRecord (5, dm); // becomes DM_2 dminfo.defineRecord (6, dm); // becomes DM_3 dminfo.defineRecord (7, dm); // becomes DM_4 dm.define ("COLUMNS", Vector(1, "col1")); dminfo.defineRecord (8, dm); // becomes col1 AlwaysAssertExit (DataManInfo::uniqueName(dminfo, "nma") == "nma_2"); AlwaysAssertExit (DataManInfo::uniqueName(dminfo, "nmb") == "nmb"); DataManInfo::makeUniqueNames (dminfo); AlwaysAssertExit (dminfo.subRecord(0).asString("NAME") == "DM"); AlwaysAssertExit (dminfo.subRecord(1).asString("NAME") == "nma"); AlwaysAssertExit (dminfo.subRecord(2).asString("NAME") == "nma_2"); AlwaysAssertExit (dminfo.subRecord(3).asString("NAME") == "nma_1"); AlwaysAssertExit (dminfo.subRecord(4).asString("NAME") == "DM_1"); AlwaysAssertExit (dminfo.subRecord(5).asString("NAME") == "DM_2"); AlwaysAssertExit (dminfo.subRecord(6).asString("NAME") == "DM_3"); AlwaysAssertExit (dminfo.subRecord(7).asString("NAME") == "DM_4"); AlwaysAssertExit (dminfo.subRecord(8).asString("NAME") == "col1"); } int main() { try { testDM(); mergeTestEmpty(); mergeTestNonEmpty(); finalizeTestEmpty(); finalizeTestNonEmpty(); largeTest(); testUniqueName(); } catch (const std::exception& x) { cout << "Caught exception: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/tables/DataMan/test/tDataManInfo.out000066400000000000000000000041531476623553700221230ustar00rootroot00000000000000 *1: { TYPE: String "StandardStMan" NAME: String "StandardStMan" SEQNR: uInt 0 SPEC: { MaxCacheSize: Int 2 BUCKETSIZE: Int 640 PERSCACHESIZE: Int 2 IndexLength: Int 0 } COLUMNS: String array with shape [4] [col1, col2, col3, col4] } *1: { TYPE: String "StandardStMan" NAME: String "StandardStMan" SEQNR: uInt 0 SPEC: { MaxCacheSize: Int 2 BUCKETSIZE: Int 640 PERSCACHESIZE: Int 2 IndexLength: Int 0 } COLUMNS: String array with shape [4] [col1, col2, col3, col4] } [] *1: { TYPE: String "StandardStMan" NAME: String "StandardStMan" SEQNR: uInt 0 SPEC: { MaxCacheSize: Int 2 BUCKETSIZE: Int 640 PERSCACHESIZE: Int 2 IndexLength: Int 0 } COLUMNS: String array with shape [3] [col2, col3, col4] } [col1] *1: { TYPE: String "StandardStMan" NAME: String "StandardStMan" SEQNR: uInt 0 SPEC: { MaxCacheSize: Int 2 BUCKETSIZE: Int 640 PERSCACHESIZE: Int 2 IndexLength: Int 0 } COLUMNS: String array with shape [2] [col2, col4] } *2: { TYPE: String "TiledShapeStMan" NAME: String "TSMData" COLUMNS: String array with shape [1] [col3] SPEC: { DEFAULTTILESHAPE: Int array with shape [3] [3, 4, 5] } } *1: { COLUMNS: String array with shape [2] [col1, col8] TYPE: String "StandardStMan" NAME: String "StandardStMan" } *2: { COLUMNS: String array with shape [1] [col5] TYPE: String "StandardStMan" NAME: String "dmname3" } *3: { TYPE: String "dmtype1" NAME: String "dmname1a" COLUMNS: String array with shape [2] [col2, col6] } *4: { TYPE: String "dmtype1" NAME: String "dmname2" SPEC: { BSZ: Int 10 } COLUMNS: String array with shape [1] [col4] } *5: { TYPE: String "dmtype2" NAME: String "dmname2a" COLUMNS: String array with shape [1] [col7] } *6: { COLUMNS: String array with shape [1] [col3] TYPE: String "dmtype2" NAME: String "dmtype2" } casacore-3.7.1/tables/DataMan/test/tExternalStMan.cc000066400000000000000000000717731476623553700223210ustar00rootroot00000000000000//# tExternalStMan.cc: Test program for an external stman with old interface //# Copyright (C) 2019 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // This test is a very simplified clone of ASTRON's LofarStMan. // using the old DataManager interface. // It tests if the old DataManager interface works properly. // // The results are written to stdout. The script executing this program, // compares the results with the reference output file. //# Includes #include #include #include #include #include #include #include #include #include namespace casacore { // Define the constants as used in the Column classes. const uInt ntime=10; const uInt nant=3; const uInt npol=4; const uInt nchan=8; //# Forward Declarations. class LofarColumn; class LofarStMan : public DataManager { public: // Create a Lofar storage manager with the given name. // If no name is used, it is set to "LofarStMan" explicit LofarStMan (const String& dataManagerName = "LofarStMan"); // Create a Lofar storage manager with the given name. // The specifications are part of the record (as created by dataManagerSpec). LofarStMan (const String& dataManagerName, const Record& spec); ~LofarStMan(); // Clone this object. virtual DataManager* clone() const override; // Get the type name of the data manager (i.e. LofarStMan). virtual String dataManagerType() const override; // Get the name given to the storage manager (in the constructor). virtual String dataManagerName() const override; // Record a record containing data manager specifications. virtual Record dataManagerSpec() const override; // The storage manager is not a regular one. virtual Bool isRegular() const override; // The storage manager cannot add rows. virtual Bool canAddRow() const override; // The storage manager cannot delete rows. virtual Bool canRemoveRow() const override; // The storage manager can add columns, which does not really do something. virtual Bool canAddColumn() const override; // Columns can be removed, but it does not do anything at all. virtual Bool canRemoveColumn() const override; // Make the object from the type name string. // This function gets registered in the DataManager "constructor" map. // The caller has to delete the object. static DataManager* makeObject (const String& aDataManType, const Record& spec); // Register the class name and the static makeObject "constructor". // This will make the engine known to the table system. static void registerClass(); // Get the nr of rows. uInt getNRow() const { return ntime * nant * nant; } private: // Copy constructor cannot be used. LofarStMan (const LofarStMan& that); // Assignment cannot be used. LofarStMan& operator= (const LofarStMan& that); // Flush and optionally fsync the data. // It does nothing, and returns False. virtual Bool flush (AipsIO&, Bool doFsync) override; // Let the storage manager create files as needed for a new table. // This allows a column with an indirect array to create its file. virtual void create (uInt nrrow) override; // Open the storage manager file for an existing table. // Return the number of rows in the data file. // virtual void open (uInt nrrow, AipsIO&) override; //# should never be called virtual uInt open1 (uInt nrrow, AipsIO&) override; // // Prepare the columns. virtual void prepare() override; // Resync the storage manager with the new file contents. // It does nothing. // virtual void resync (uInt nrrow) override; //# should never be called virtual uInt resync1 (uInt nrrow) override; // // Reopen the storage manager files for read/write. // It does nothing. virtual void reopenRW() override; // The data manager will be deleted (because all its columns are // requested to be deleted). // So clean up the things needed (e.g. delete files). virtual void deleteManager() override; // Add rows to the storage manager. // It cannot do it, so throws an exception. virtual void addRow (uInt nrrow) override; // Delete a row from all columns. // It cannot do it, so throws an exception. virtual void removeRow (uInt rowNr) override; // Do the final addition of a column. // It won't do anything. virtual void addColumn (DataManagerColumn*) override; // Remove a column from the data file. // It won't do anything. virtual void removeColumn (DataManagerColumn*) override; // Create a column in the storage manager on behalf of a table column. // The caller has to delete the newly created object. // // Create a scalar column. virtual DataManagerColumn* makeScalarColumn (const String& aName, int aDataType, const String& aDataTypeID) override; // Create a direct array column. virtual DataManagerColumn* makeDirArrColumn (const String& aName, int aDataType, const String& aDataTypeID) override; // Create an indirect array column. virtual DataManagerColumn* makeIndArrColumn (const String& aName, int aDataType, const String& aDataTypeID) override; // //# Declare member variables. // Name of data manager. String itsDataManName; // The column objects. vector itsColumns; }; class LofarColumn : public StManColumn { public: explicit LofarColumn (LofarStMan* parent, int dtype) : StManColumn (dtype), itsParent (parent) {} virtual ~LofarColumn(); // Most columns are not writable (only DATA is writable). virtual Bool isWritable() const override; // Set column shape of fixed shape columns; it does nothing. virtual void setShapeColumn (const IPosition& shape) override; // Prepare the column. By default it does nothing. virtual void prepareCol(); protected: LofarStMan* itsParent; }; // ANTENNA1 column in the LOFAR Storage Manager. // class Ant1Column : public LofarColumn { public: explicit Ant1Column (LofarStMan* parent, int dtype) : LofarColumn(parent, dtype) {} virtual ~Ant1Column(); virtual void getIntV (uInt rowNr, Int* dataPtr) override; }; // ANTENNA2 column in the LOFAR Storage Manager. // class Ant2Column : public LofarColumn { public: explicit Ant2Column (LofarStMan* parent, int dtype) : LofarColumn(parent, dtype) {} virtual ~Ant2Column(); virtual void getIntV (uInt rowNr, Int* dataPtr) override; }; // TIME and TIME_CENTROID column in the LOFAR Storage Manager. // class TimeColumn : public LofarColumn { public: explicit TimeColumn (LofarStMan* parent, int dtype) : LofarColumn(parent, dtype) {} virtual ~TimeColumn(); virtual void getdoubleV (uInt rowNr, Double* dataPtr) override; }; // INTERVAL and EXPOSURE column in the LOFAR Storage Manager. // class IntervalColumn : public LofarColumn { public: explicit IntervalColumn (LofarStMan* parent, int dtype) : LofarColumn(parent, dtype) {} virtual ~IntervalColumn(); virtual void getdoubleV (uInt rowNr, Double* dataPtr) override; }; // All columns in the LOFAR Storage Manager with value 0. // class ZeroColumn : public LofarColumn { public: explicit ZeroColumn (LofarStMan* parent, int dtype) : LofarColumn(parent, dtype) {} virtual ~ZeroColumn(); virtual void getIntV (uInt rowNr, Int* dataPtr) override; private: Int itsValue; }; // All columns in the LOFAR Storage Manager with value False. // class FalseColumn : public LofarColumn { public: explicit FalseColumn (LofarStMan* parent, int dtype) : LofarColumn(parent, dtype) {} virtual ~FalseColumn(); virtual void getBoolV (uInt rowNr, Bool* dataPtr) override; private: Bool itsValue; }; // UVW column in the LOFAR Storage Manager. // class UvwColumn : public LofarColumn { public: explicit UvwColumn (LofarStMan* parent, int dtype) : LofarColumn(parent, dtype) {} virtual ~UvwColumn(); using LofarColumn::shape; virtual IPosition shape (uInt rownr) override; virtual void getArraydoubleV (uInt rowNr, Array* dataPtr) override; }; // DATA column in the LOFAR Storage Manager. // class DataColumn : public LofarColumn { public: explicit DataColumn (LofarStMan* parent, int dtype) : LofarColumn(parent, dtype) {} virtual ~DataColumn(); virtual Bool isWritable() const override; using LofarColumn::shape; virtual IPosition shape (uInt rownr) override; virtual void getArrayComplexV (uInt rowNr, Array* dataPtr) override; virtual void putArrayComplexV (uInt rowNr, const Array* dataPtr) override; }; // FLAG column in the LOFAR Storage Manager. // class FlagColumn : public LofarColumn { public: explicit FlagColumn (LofarStMan* parent, int dtype) : LofarColumn(parent, dtype) {} virtual ~FlagColumn(); using LofarColumn::shape; virtual IPosition shape (uInt rownr) override; virtual void getArrayBoolV (uInt rowNr, Array* dataPtr) override; }; // WEIGHT column in the LOFAR Storage Manager. // class WeightColumn : public LofarColumn { public: explicit WeightColumn (LofarStMan* parent, int dtype) : LofarColumn(parent, dtype) {} virtual ~WeightColumn(); using LofarColumn::shape; virtual IPosition shape (uInt rownr) override; virtual void getArrayfloatV (uInt rowNr, Array* dataPtr) override; }; // SIGMA column in the LOFAR Storage Manager. // class SigmaColumn : public LofarColumn { public: explicit SigmaColumn (LofarStMan* parent, int dtype) : LofarColumn(parent, dtype) {} virtual ~SigmaColumn(); using LofarColumn::shape; virtual IPosition shape (uInt rownr) override; virtual void getArrayfloatV (uInt rowNr, Array* dataPtr) override; }; // WEIGHT_SPECTRUM column in the LOFAR Storage Manager. // class WSpectrumColumn : public LofarColumn { public: explicit WSpectrumColumn (LofarStMan* parent, int dtype) : LofarColumn(parent, dtype) {} virtual ~WSpectrumColumn(); using LofarColumn::shape; virtual IPosition shape (uInt rownr) override; virtual void getArrayfloatV (uInt rowNr, Array* dataPtr) override; }; // FLAG_CATEGORY column in the LOFAR Storage Manager. // class FlagCatColumn : public LofarColumn { public: explicit FlagCatColumn (LofarStMan* parent, int dtype) : LofarColumn(parent, dtype) {} virtual ~FlagCatColumn(); using LofarColumn::isShapeDefined; virtual Bool isShapeDefined (uInt rownr) override; using LofarColumn::shape; virtual IPosition shape (uInt rownr) override; }; LofarColumn::~LofarColumn() {} Bool LofarColumn::isWritable() const { return False; } void LofarColumn::setShapeColumn (const IPosition&) {} void LofarColumn::prepareCol() {} Ant1Column::~Ant1Column() {} void Ant1Column::getIntV (uInt rownr, Int* dataPtr) { // Use 3 antennae (baselines 0-0, 0-1, 0-2, 1-0, 1-1, 1-2, 2-0, 2-1, 2-2). *dataPtr = (rownr%(nant*nant)) / nant; } Ant2Column::~Ant2Column() {} void Ant2Column::getIntV (uInt rownr, Int* dataPtr) { // Use 3 antennae (baselines 0-0, 0-1, 0-2, 1-0, 1-1, 1-2, 2-0, 2-1, 2-2). *dataPtr = rownr%nant; } TimeColumn::~TimeColumn() {} void TimeColumn::getdoubleV (uInt rownr, Double* dataPtr) { *dataPtr = 1 + 2 * (rownr/(nant*nant)); } IntervalColumn::~IntervalColumn() {} void IntervalColumn::getdoubleV (uInt, Double* dataPtr) { *dataPtr = 2; } ZeroColumn::~ZeroColumn() {} void ZeroColumn::getIntV (uInt, Int* dataPtr) { itsValue = 0; columnCache().setIncrement (0); if (itsParent->getNRow() > 0) { columnCache().set (0, itsParent->getNRow()-1, &itsValue); } *dataPtr = 0; } FalseColumn::~FalseColumn() {} void FalseColumn::getBoolV (uInt, Bool* dataPtr) { itsValue = False; columnCache().setIncrement (0); if (itsParent->getNRow() > 0) { columnCache().set (0, itsParent->getNRow()-1, &itsValue); } *dataPtr = 0; } UvwColumn::~UvwColumn() {} IPosition UvwColumn::shape (uInt) { return IPosition(1,3); } void UvwColumn::getArraydoubleV (uInt rownr, Array* dataPtr) { (*dataPtr)[0] = rownr * 0.1; (*dataPtr)[1] = rownr * 0.1 + 0.03; (*dataPtr)[2] = rownr * 0.1 + 0.06; } DataColumn::~DataColumn() {} Bool DataColumn::isWritable() const { return True; } IPosition DataColumn::shape (uInt) { return IPosition(2, npol, nchan); } void DataColumn::getArrayComplexV (uInt rownr, Array* dataPtr) { indgen (*dataPtr, Complex(rownr, rownr+0.5)); } void DataColumn::putArrayComplexV (uInt rownr, const Array* dataPtr) { cout << "Ignored DataColumn::putArrayComplexV " << dataPtr->shape() << " for row " << rownr << endl; } FlagColumn::~FlagColumn() {} IPosition FlagColumn::shape (uInt) { return IPosition(2, npol, nchan); } void FlagColumn::getArrayBoolV (uInt rownr, Array* dataPtr) { *dataPtr = False; (*dataPtr)(IPosition(2,rownr%npol, rownr%nchan)) = True; } WeightColumn::~WeightColumn() {} IPosition WeightColumn::shape (uInt) { return IPosition(1, 4); } void WeightColumn::getArrayfloatV (uInt, Array* dataPtr) { *dataPtr = float(1); } SigmaColumn::~SigmaColumn() {} IPosition SigmaColumn::shape (uInt) { return IPosition(1, 4); } void SigmaColumn::getArrayfloatV (uInt, Array* dataPtr) { *dataPtr = float(1); } WSpectrumColumn::~WSpectrumColumn() {} IPosition WSpectrumColumn::shape (uInt) { return IPosition(2, npol, nchan); } void WSpectrumColumn::getArrayfloatV (uInt rownr, Array* dataPtr) { *dataPtr = float(rownr); } FlagCatColumn::~FlagCatColumn() {} Bool FlagCatColumn::isShapeDefined (uInt) { return False; } IPosition FlagCatColumn::shape (uInt) { throw DataManError ("LofarStMan: no data in column FLAG_CATEGORY"); } LofarStMan::LofarStMan (const String& dataManName) : DataManager (), itsDataManName (dataManName) {} LofarStMan::LofarStMan (const String& dataManName, const Record&) : DataManager (), itsDataManName (dataManName) {} LofarStMan::LofarStMan (const LofarStMan& that) : DataManager (), itsDataManName (that.itsDataManName) {} LofarStMan::~LofarStMan() { for (uInt i=0; i #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace casacore; void createTable() { // Build the table description. // Add all mandatory columns of the MS main table. TableDesc td("", "1", TableDesc::Scratch); td.comment() = "A test of class Table"; td.addColumn (ScalarColumnDesc("TIME")); td.addColumn (ScalarColumnDesc("ANTENNA1")); td.addColumn (ScalarColumnDesc("ANTENNA2")); td.addColumn (ScalarColumnDesc("FEED1")); td.addColumn (ScalarColumnDesc("FEED2")); td.addColumn (ScalarColumnDesc("DATA_DESC_ID")); td.addColumn (ScalarColumnDesc("PROCESSOR_ID")); td.addColumn (ScalarColumnDesc("FIELD_ID")); td.addColumn (ScalarColumnDesc("ARRAY_ID")); td.addColumn (ScalarColumnDesc("OBSERVATION_ID")); td.addColumn (ScalarColumnDesc("STATE_ID")); td.addColumn (ScalarColumnDesc("SCAN_NUMBER")); td.addColumn (ScalarColumnDesc("INTERVAL")); td.addColumn (ScalarColumnDesc("EXPOSURE")); td.addColumn (ScalarColumnDesc("TIME_CENTROID")); td.addColumn (ScalarColumnDesc("FLAG_ROW")); td.addColumn (ArrayColumnDesc("UVW",IPosition(1,3), ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc("DATA")); td.addColumn (ArrayColumnDesc("SIGMA")); td.addColumn (ArrayColumnDesc("WEIGHT")); td.addColumn (ArrayColumnDesc("WEIGHT_SPECTRUM")); td.addColumn (ArrayColumnDesc("FLAG")); td.addColumn (ArrayColumnDesc("FLAG_CATEGORY")); // Now create a new table from the description. SetupNewTable newtab("tLofarStMan_tmp.data", td, Table::New); // Create the storage manager and bind all columns to it. LofarStMan sm1; newtab.bindAll (sm1); // Finally create the table. The destructor writes it. Table tab(newtab); } // maxWeight tells maximum weight before it wraps // (when nbytesPerSample is small). void readTable() { // Open the table and check if #rows is as expected. Table tab("tLofarStMan_tmp.data"); uInt nrow = tab.nrow(); uInt nbasel = nant*nant; AlwaysAssertExit (ntime*nbasel == nrow); AlwaysAssertExit (!tab.canAddRow()); AlwaysAssertExit (!tab.canRemoveRow()); AlwaysAssertExit (tab.canRemoveColumn(Vector(1, "DATA"))); // Create objects for all mandatory MS columns. ArrayColumn dataCol(tab, "DATA"); ArrayColumn weightCol(tab, "WEIGHT"); ArrayColumn wspecCol(tab, "WEIGHT_SPECTRUM"); ArrayColumn sigmaCol(tab, "SIGMA"); ArrayColumn uvwCol(tab, "UVW"); ArrayColumn flagCol(tab, "FLAG"); ArrayColumn flagcatCol(tab, "FLAG_CATEGORY"); ScalarColumn timeCol(tab, "TIME"); ScalarColumn centCol(tab, "TIME_CENTROID"); ScalarColumn intvCol(tab, "INTERVAL"); ScalarColumn expoCol(tab, "EXPOSURE"); ScalarColumn ant1Col(tab, "ANTENNA1"); ScalarColumn ant2Col(tab, "ANTENNA2"); ScalarColumn feed1Col(tab, "FEED1"); ScalarColumn feed2Col(tab, "FEED2"); ScalarColumn ddidCol(tab, "DATA_DESC_ID"); ScalarColumn pridCol(tab, "PROCESSOR_ID"); ScalarColumn fldidCol(tab, "FIELD_ID"); ScalarColumn arridCol(tab, "ARRAY_ID"); ScalarColumn obsidCol(tab, "OBSERVATION_ID"); ScalarColumn stidCol(tab, "STATE_ID"); ScalarColumn scnrCol(tab, "SCAN_NUMBER"); ScalarColumn flagrowCol(tab, "FLAG_ROW"); // Create and initialize expected data and weight. Array dataExp(IPosition(2,npol,nchan)); indgen (dataExp, Complex(0, 0.5)); Array weightExp(IPosition(2,1,nchan), 0.f); // Loop through all rows in the table and check the data. uInt row=0; for (uInt i=0; i weights = wspecCol(row); AlwaysAssertExit (weights.shape() == IPosition(2,npol,nchan)); Array flagExp (weights.shape(), False); flagExp(IPosition(2,row%npol, row%nchan)) = True; AlwaysAssertExit (allEQ (flagCol(row), flagExp)); // Check ANTENNA1 and ANTENNA2 AlwaysAssertExit (ant1Col(row) == Int(j)); AlwaysAssertExit (ant2Col(row) == Int(k)); dataExp += Complex(1, 1); weightExp += Float(1); ++row; } } } // Check values in TIME column. const double interval = 2; Vector times = timeCol.getColumn(); AlwaysAssertExit (times.size() == nrow); row=0; double startTime = 1; for (uInt i=0; i uvwExp(IPosition(1,3)); indgen (uvwExp, 0., 0.03); for (uInt i=0; i wg = wspecCol.getColumnCells (rownrs); Array wgs = wspecCol.getColumnCells (rownrs, slicer); cout << wspecCol(0).shape() << ' ' << wg.shape() << ' ' << wgs.shape() << endl; } void updateTable() { // Open the table for write. Table tab("tLofarStMan_tmp.data", Table::Update); // Create object for DATA column. ArrayColumn dataCol(tab, "DATA"); // Check we can write the column, but not change the shape. AlwaysAssertExit (tab.isColumnWritable ("DATA")); AlwaysAssertExit (!dataCol.canChangeShape()); // Create and initialize data. Array data(IPosition(2,npol,nchan)); // Write the data (which only writes a message). dataCol.put (0, data); } void copyTable() { Table tab("tLofarStMan_tmp.data"); // Deep copy the table. tab.deepCopy ("tLofarStMan_tmp.datcp", Table::New, true); } int main() { try { // Register LofarStMan to be able to read it back. LofarStMan::registerClass(); // Create the table. createTable(); readTable(); // Update the table and check again. updateTable(); readTable(); // Check the copying the table works well. copyTable(); } catch (AipsError& x) { cout << "Caught an exception: " << x.getMesg() << endl; return 1; } return 0; // exit with success status } casacore-3.7.1/tables/DataMan/test/tExternalStManNew.cc000066400000000000000000000710521476623553700227610ustar00rootroot00000000000000//# tExternalStManNew.cc: Test program for an external stman with new interface //# Copyright (C) 2019 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // This test is a very simplified clone of ASTRON's LofarStMan // using the new DataManager interface. // It can be compared with tExternalStMan.cc to show the differences // between the old and new DataManager interface. // // The results are written to stdout. The script executing this program, // compares the results with the reference output file. //# Includes #include #include #include #include #include #include #include #include #include namespace casacore { // Define the constants as used in the Column classes. const uInt ntime=10; const uInt nant=3; const uInt npol=4; const uInt nchan=8; //# Forward Declarations. class LofarColumn; class LofarStMan : public DataManager { public: // Create a Lofar storage manager with the given name. // If no name is used, it is set to "LofarStMan" explicit LofarStMan (const String& dataManagerName = "LofarStMan"); // Create a Lofar storage manager with the given name. // The specifications are part of the record (as created by dataManagerSpec). LofarStMan (const String& dataManagerName, const Record& spec); ~LofarStMan(); // Assignment cannot be used. LofarStMan& operator= (const LofarStMan&) = delete; // Clone this object. virtual DataManager* clone() const; // Get the type name of the data manager (i.e. LofarStMan). virtual String dataManagerType() const; // Get the name given to the storage manager (in the constructor). virtual String dataManagerName() const; // Record a record containing data manager specifications. virtual Record dataManagerSpec() const; // The storage manager is not a regular one. virtual Bool isRegular() const; // The storage manager cannot add rows. virtual Bool canAddRow() const; // The storage manager cannot delete rows. virtual Bool canRemoveRow() const; // The storage manager can add columns, which does not really do something. virtual Bool canAddColumn() const; // Columns can be removed, but it does not do anything at all. virtual Bool canRemoveColumn() const; // Make the object from the type name string. // This function gets registered in the DataManager "constructor" map. // The caller has to delete the object. static DataManager* makeObject (const String& aDataManType, const Record& spec); // Register the class name and the static makeObject "constructor". // This will make the engine known to the table system. static void registerClass(); // Get the nr of rows. rownr_t getNRow() const { return ntime * nant * nant; } private: // Copy constructor can only be used by clone. LofarStMan (const LofarStMan&); // Flush and optionally fsync the data. // It does nothing, and returns False. virtual Bool flush (AipsIO&, Bool doFsync); // Let the storage manager create files as needed for a new table. // This allows a column with an indirect array to create its file. virtual void create64 (rownr_t nrrow); // Open the storage manager file for an existing table. // Return the number of rows in the data file. virtual rownr_t open64 (rownr_t nrrow, AipsIO&); // Prepare the columns. virtual void prepare(); // Resync the storage manager with the new file contents. // It does nothing. virtual rownr_t resync64 (rownr_t nrrow); // Reopen the storage manager files for read/write. // It does nothing. virtual void reopenRW(); // The data manager will be deleted (because all its columns are // requested to be deleted). // So clean up the things needed (e.g. delete files). virtual void deleteManager(); // Add rows to the storage manager. // It cannot do it, so throws an exception. virtual void addRow64 (rownr_t nrrow); // Delete a row from all columns. // It cannot do it, so throws an exception. virtual void removeRow64 (rownr_t rowNr); // Do the final addition of a column. // It won't do anything. virtual void addColumn (DataManagerColumn*); // Remove a column from the data file. // It won't do anything. virtual void removeColumn (DataManagerColumn*); // Create a column in the storage manager on behalf of a table column. // The caller has to delete the newly created object. // // Create a scalar column. virtual DataManagerColumn* makeScalarColumn (const String& aName, int aDataType, const String& aDataTypeID); // Create a direct array column. virtual DataManagerColumn* makeDirArrColumn (const String& aName, int aDataType, const String& aDataTypeID); // Create an indirect array column. virtual DataManagerColumn* makeIndArrColumn (const String& aName, int aDataType, const String& aDataTypeID); // //# Declare member variables. // Name of data manager. String itsDataManName; // The column objects. vector itsColumns; }; class LofarColumn : public StManColumnBase { public: explicit LofarColumn (LofarStMan* parent, int dtype) : StManColumnBase (dtype), itsParent (parent) {} virtual ~LofarColumn(); // Most columns are not writable (only DATA is writable). virtual Bool isWritable() const; // Set column shape of fixed shape columns; it does nothing. virtual void setShapeColumn (const IPosition& shape); // Prepare the column. By default it does nothing. virtual void prepareCol(); protected: LofarStMan* itsParent; }; // ANTENNA1 column in the LOFAR Storage Manager. // class Ant1Column : public LofarColumn { public: explicit Ant1Column (LofarStMan* parent, int dtype) : LofarColumn(parent, dtype) {} virtual ~Ant1Column(); virtual void getInt (rownr_t rowNr, Int* dataPtr); }; // ANTENNA2 column in the LOFAR Storage Manager. // class Ant2Column : public LofarColumn { public: explicit Ant2Column (LofarStMan* parent, int dtype) : LofarColumn(parent, dtype) {} virtual ~Ant2Column(); virtual void getInt (rownr_t rowNr, Int* dataPtr); }; // TIME and TIME_CENTROID column in the LOFAR Storage Manager. // class TimeColumn : public LofarColumn { public: explicit TimeColumn (LofarStMan* parent, int dtype) : LofarColumn(parent, dtype) {} virtual ~TimeColumn(); virtual void getdouble (rownr_t rowNr, Double* dataPtr); }; // INTERVAL and EXPOSURE column in the LOFAR Storage Manager. // class IntervalColumn : public LofarColumn { public: explicit IntervalColumn (LofarStMan* parent, int dtype) : LofarColumn(parent, dtype) {} virtual ~IntervalColumn(); virtual void getdouble (rownr_t rowNr, Double* dataPtr); }; // All columns in the LOFAR Storage Manager with value 0. // class ZeroColumn : public LofarColumn { public: explicit ZeroColumn (LofarStMan* parent, int dtype) : LofarColumn(parent, dtype) {} virtual ~ZeroColumn(); virtual void getInt (rownr_t rowNr, Int* dataPtr); private: Int itsValue; }; // All columns in the LOFAR Storage Manager with value False. // class FalseColumn : public LofarColumn { public: explicit FalseColumn (LofarStMan* parent, int dtype) : LofarColumn(parent, dtype) {} virtual ~FalseColumn(); virtual void getBool (rownr_t rowNr, Bool* dataPtr); private: Bool itsValue; }; // UVW column in the LOFAR Storage Manager. // class UvwColumn : public LofarColumn { public: explicit UvwColumn (LofarStMan* parent, int dtype) : LofarColumn(parent, dtype) {} virtual ~UvwColumn(); virtual IPosition shape (rownr_t rownr); virtual void getArrayV (rownr_t rowNr, ArrayBase& dataPtr); }; // DATA column in the LOFAR Storage Manager. // class DataColumn : public LofarColumn { public: explicit DataColumn (LofarStMan* parent, int dtype) : LofarColumn(parent, dtype) {} virtual ~DataColumn(); virtual Bool isWritable() const; virtual IPosition shape (rownr_t rownr); virtual void getArrayV (rownr_t rowNr, ArrayBase& dataPtr); virtual void putArrayV (rownr_t rowNr, const ArrayBase& dataPtr); }; // FLAG column in the LOFAR Storage Manager. // class FlagColumn : public LofarColumn { public: explicit FlagColumn (LofarStMan* parent, int dtype) : LofarColumn(parent, dtype) {} virtual ~FlagColumn(); virtual IPosition shape (rownr_t rownr); virtual void getArrayV (rownr_t rowNr, ArrayBase& dataPtr); }; // WEIGHT column in the LOFAR Storage Manager. // class WeightColumn : public LofarColumn { public: explicit WeightColumn (LofarStMan* parent, int dtype) : LofarColumn(parent, dtype) {} virtual ~WeightColumn(); virtual IPosition shape (rownr_t rownr); virtual void getArrayV (rownr_t rowNr, ArrayBase& dataPtr); }; // SIGMA column in the LOFAR Storage Manager. // class SigmaColumn : public LofarColumn { public: explicit SigmaColumn (LofarStMan* parent, int dtype) : LofarColumn(parent, dtype) {} virtual ~SigmaColumn(); virtual IPosition shape (rownr_t rownr); virtual void getArrayV (rownr_t rowNr, ArrayBase& dataPtr); }; // WEIGHT_SPECTRUM column in the LOFAR Storage Manager. // class WSpectrumColumn : public LofarColumn { public: explicit WSpectrumColumn (LofarStMan* parent, int dtype) : LofarColumn(parent, dtype) {} virtual ~WSpectrumColumn(); virtual IPosition shape (rownr_t rownr); virtual void getArrayV (rownr_t rowNr, ArrayBase& dataPtr); }; // FLAG_CATEGORY column in the LOFAR Storage Manager. // class FlagCatColumn : public LofarColumn { public: explicit FlagCatColumn (LofarStMan* parent, int dtype) : LofarColumn(parent, dtype) {} virtual ~FlagCatColumn(); virtual Bool isShapeDefined (rownr_t rownr); virtual IPosition shape (rownr_t rownr); }; LofarColumn::~LofarColumn() {} Bool LofarColumn::isWritable() const { return False; } void LofarColumn::setShapeColumn (const IPosition&) {} void LofarColumn::prepareCol() {} Ant1Column::~Ant1Column() {} void Ant1Column::getInt (rownr_t rownr, Int* dataPtr) { // Use 3 antennae (baselines 0-0, 0-1, 0-2, 1-0, 1-1, 1-2, 2-0, 2-1, 2-2). *dataPtr = (rownr%(nant*nant)) / nant; } Ant2Column::~Ant2Column() {} void Ant2Column::getInt (rownr_t rownr, Int* dataPtr) { // Use 3 antennae (baselines 0-0, 0-1, 0-2, 1-0, 1-1, 1-2, 2-0, 2-1, 2-2). *dataPtr = rownr%nant; } TimeColumn::~TimeColumn() {} void TimeColumn::getdouble (rownr_t rownr, Double* dataPtr) { *dataPtr = 1 + 2 * (rownr/(nant*nant)); } IntervalColumn::~IntervalColumn() {} void IntervalColumn::getdouble (rownr_t, Double* dataPtr) { *dataPtr = 2; } ZeroColumn::~ZeroColumn() {} void ZeroColumn::getInt (rownr_t, Int* dataPtr) { itsValue = 0; columnCache().setIncrement (0); if (itsParent->getNRow() > 0) { columnCache().set (0, itsParent->getNRow()-1, &itsValue); } *dataPtr = 0; } FalseColumn::~FalseColumn() {} void FalseColumn::getBool (rownr_t, Bool* dataPtr) { itsValue = False; columnCache().setIncrement (0); if (itsParent->getNRow() > 0) { columnCache().set (0, itsParent->getNRow()-1, &itsValue); } *dataPtr = 0; } UvwColumn::~UvwColumn() {} IPosition UvwColumn::shape (rownr_t) { return IPosition(1,3); } void UvwColumn::getArrayV (rownr_t rownr, ArrayBase& dataPtr) { DebugAssert (dtype() == TpDouble, AipsError); Bool deleteIt; void* ptr = dataPtr.getVStorage (deleteIt); double* p = static_cast(ptr); p[0] = rownr * 0.1; p[1] = rownr * 0.1 + 0.03; p[2] = rownr * 0.1 + 0.06; dataPtr.putVStorage (ptr, deleteIt); } DataColumn::~DataColumn() {} Bool DataColumn::isWritable() const { return True; } IPosition DataColumn::shape (rownr_t) { return IPosition(2, npol, nchan); } void DataColumn::getArrayV (rownr_t rownr, ArrayBase& dataPtr) { DebugAssert (dtype() == TpComplex, AipsError); Array& arr = static_cast&>(dataPtr); indgen (arr, Complex(rownr, rownr+0.5)); } void DataColumn::putArrayV (rownr_t rownr, const ArrayBase& dataPtr) { DebugAssert (dtype() == TpComplex, AipsError); cout << "Ignored DataColumn::putArrayComplexV " << dataPtr.shape() << " for row " << rownr << endl; } FlagColumn::~FlagColumn() {} IPosition FlagColumn::shape (rownr_t) { return IPosition(2, npol, nchan); } void FlagColumn::getArrayV (rownr_t rownr, ArrayBase& dataPtr) { DebugAssert (dtype() == TpBool, AipsError); Array& arr = static_cast&>(dataPtr); arr = False; arr(IPosition(2,rownr%npol, rownr%nchan)) = True; } WeightColumn::~WeightColumn() {} IPosition WeightColumn::shape (rownr_t) { return IPosition(1, 4); } void WeightColumn::getArrayV (rownr_t, ArrayBase& dataPtr) { DebugAssert (dtype() == TpFloat, AipsError); Array& arr = static_cast&>(dataPtr); arr = float(1); } SigmaColumn::~SigmaColumn() {} IPosition SigmaColumn::shape (rownr_t) { return IPosition(1, 4); } void SigmaColumn::getArrayV (rownr_t, ArrayBase& dataPtr) { DebugAssert (dtype() == TpFloat, AipsError); Array& arr = static_cast&>(dataPtr); arr = float(1); } WSpectrumColumn::~WSpectrumColumn() {} IPosition WSpectrumColumn::shape (rownr_t) { return IPosition(2, npol, nchan); } void WSpectrumColumn::getArrayV (rownr_t rownr, ArrayBase& dataPtr) { DebugAssert (dtype() == TpFloat, AipsError); Array& arr = static_cast&>(dataPtr); arr = float(rownr); } FlagCatColumn::~FlagCatColumn() {} Bool FlagCatColumn::isShapeDefined (rownr_t) { return False; } IPosition FlagCatColumn::shape (rownr_t) { throw DataManError ("LofarStMan: no data in column FLAG_CATEGORY"); } LofarStMan::LofarStMan (const String& dataManName) : DataManager (), itsDataManName (dataManName) {} LofarStMan::LofarStMan (const String& dataManName, const Record&) : DataManager (), itsDataManName (dataManName) {} LofarStMan::LofarStMan (const LofarStMan& that) : DataManager (), itsDataManName (that.itsDataManName) {} LofarStMan::~LofarStMan() { for (uInt i=0; i #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace casacore; void createTable() { // Build the table description. // Add all mandatory columns of the MS main table. TableDesc td("", "1", TableDesc::Scratch); td.comment() = "A test of class Table"; td.addColumn (ScalarColumnDesc("TIME")); td.addColumn (ScalarColumnDesc("ANTENNA1")); td.addColumn (ScalarColumnDesc("ANTENNA2")); td.addColumn (ScalarColumnDesc("FEED1")); td.addColumn (ScalarColumnDesc("FEED2")); td.addColumn (ScalarColumnDesc("DATA_DESC_ID")); td.addColumn (ScalarColumnDesc("PROCESSOR_ID")); td.addColumn (ScalarColumnDesc("FIELD_ID")); td.addColumn (ScalarColumnDesc("ARRAY_ID")); td.addColumn (ScalarColumnDesc("OBSERVATION_ID")); td.addColumn (ScalarColumnDesc("STATE_ID")); td.addColumn (ScalarColumnDesc("SCAN_NUMBER")); td.addColumn (ScalarColumnDesc("INTERVAL")); td.addColumn (ScalarColumnDesc("EXPOSURE")); td.addColumn (ScalarColumnDesc("TIME_CENTROID")); td.addColumn (ScalarColumnDesc("FLAG_ROW")); td.addColumn (ArrayColumnDesc("UVW",IPosition(1,3), ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc("DATA")); td.addColumn (ArrayColumnDesc("SIGMA")); td.addColumn (ArrayColumnDesc("WEIGHT")); td.addColumn (ArrayColumnDesc("WEIGHT_SPECTRUM")); td.addColumn (ArrayColumnDesc("FLAG")); td.addColumn (ArrayColumnDesc("FLAG_CATEGORY")); // Now create a new table from the description. SetupNewTable newtab("tLofarStMan_tmp.data", td, Table::New); // Create the storage manager and bind all columns to it. LofarStMan sm1; newtab.bindAll (sm1); // Finally create the table. The destructor writes it. Table tab(newtab); } // maxWeight tells maximum weight before it wraps // (when nbytesPerSample is small). void readTable() { // Open the table and check if #rows is as expected. Table tab("tLofarStMan_tmp.data"); rownr_t nrow = tab.nrow(); uInt nbasel = nant*nant; AlwaysAssertExit (ntime*nbasel == nrow); AlwaysAssertExit (!tab.canAddRow()); AlwaysAssertExit (!tab.canRemoveRow()); AlwaysAssertExit (tab.canRemoveColumn(Vector(1, "DATA"))); // Create objects for all mandatory MS columns. ArrayColumn dataCol(tab, "DATA"); ArrayColumn weightCol(tab, "WEIGHT"); ArrayColumn wspecCol(tab, "WEIGHT_SPECTRUM"); ArrayColumn sigmaCol(tab, "SIGMA"); ArrayColumn uvwCol(tab, "UVW"); ArrayColumn flagCol(tab, "FLAG"); ArrayColumn flagcatCol(tab, "FLAG_CATEGORY"); ScalarColumn timeCol(tab, "TIME"); ScalarColumn centCol(tab, "TIME_CENTROID"); ScalarColumn intvCol(tab, "INTERVAL"); ScalarColumn expoCol(tab, "EXPOSURE"); ScalarColumn ant1Col(tab, "ANTENNA1"); ScalarColumn ant2Col(tab, "ANTENNA2"); ScalarColumn feed1Col(tab, "FEED1"); ScalarColumn feed2Col(tab, "FEED2"); ScalarColumn ddidCol(tab, "DATA_DESC_ID"); ScalarColumn pridCol(tab, "PROCESSOR_ID"); ScalarColumn fldidCol(tab, "FIELD_ID"); ScalarColumn arridCol(tab, "ARRAY_ID"); ScalarColumn obsidCol(tab, "OBSERVATION_ID"); ScalarColumn stidCol(tab, "STATE_ID"); ScalarColumn scnrCol(tab, "SCAN_NUMBER"); ScalarColumn flagrowCol(tab, "FLAG_ROW"); // Create and initialize expected data and weight. Array dataExp(IPosition(2,npol,nchan)); indgen (dataExp, Complex(0, 0.5)); Array weightExp(IPosition(2,1,nchan), 0.0f); // Loop through all rows in the table and check the data. rownr_t row=0; for (uInt i=0; i weights = wspecCol(row); AlwaysAssertExit (weights.shape() == IPosition(2,npol,nchan)); Array flagExp (weights.shape(), False); flagExp(IPosition(2,row%npol, row%nchan)) = True; AlwaysAssertExit (allEQ (flagCol(row), flagExp)); // Check ANTENNA1 and ANTENNA2 AlwaysAssertExit (ant1Col(row) == Int(j)); AlwaysAssertExit (ant2Col(row) == Int(k)); dataExp += Complex(1, 1); weightExp += Float(1); ++row; } } } // Check values in TIME column. const double interval = 2; Vector times = timeCol.getColumn(); AlwaysAssertExit (times.size() == nrow); row=0; double startTime = 1; for (uInt i=0; i uvwExp(IPosition(1,3)); indgen (uvwExp, 0., 0.03); for (rownr_t i=0; i wg = wspecCol.getColumnCells (rownrs); Array wgs = wspecCol.getColumnCells (rownrs, slicer); cout << wspecCol(0).shape() << ' ' << wg.shape() << ' ' << wgs.shape() << endl; } void updateTable() { // Open the table for write. Table tab("tLofarStMan_tmp.data", Table::Update); // Create object for DATA column. ArrayColumn dataCol(tab, "DATA"); // Check we can write the column, but not change the shape. AlwaysAssertExit (tab.isColumnWritable ("DATA")); AlwaysAssertExit (!dataCol.canChangeShape()); // Create and initialize data. Array data(IPosition(2,npol,nchan)); // Write the data (which only writes a message). dataCol.put (0, data); } void copyTable() { Table tab("tLofarStMan_tmp.data"); // Deep copy the table. tab.deepCopy ("tLofarStMan_tmp.datcp", Table::New, true); } int main() { try { // Register LofarStMan to be able to read it back. LofarStMan::registerClass(); // Create the table. createTable(); readTable(); // Update the table and check again. updateTable(); readTable(); // Check the copying the table works well. copyTable(); } catch (AipsError& x) { cout << "Caught an exception: " << x.getMesg() << endl; return 1; } return 0; // exit with success status } casacore-3.7.1/tables/DataMan/test/tForwardCol.cc000066400000000000000000000246211476623553700216240ustar00rootroot00000000000000//# tForwardCol.cc: Test program for class ForwardColumn //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Test program for class ForwardColumn // This program tests the virtual column engine ForwardCol. // The results are written to stdout. The script executing this program, // compares the results with the reference output file. TableDesc makeDesc(); void a (const TableDesc&); void b (const TableDesc&); void c (const TableDesc&); void check(const String& tableName, Int abOffset, Int acOffset); int main () { try { TableDesc td = makeDesc(); a (td); check("tForwardCol_tmp.data0", 0, 0); check("tForwardCol_tmp.sort0", 0, 0); b (td); check("tForwardCol_tmp.data1", 0, 0); c (td); check("tForwardCol_tmp.data0", 0, 10); check("tForwardCol_tmp.data1", 0, 10); check("tForwardCol_tmp.data2", 10, 10); check("tForwardCol_tmp.data3", 10, 10); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } // First build a description. TableDesc makeDesc() { // First register the virtual column engine. ForwardColumnEngine::registerClass(); // Build the table description. TableDesc td("tTableDesc","1",TableDesc::Scratch); td.comment() = "A test of class ForwardColumn"; td.addColumn (ScalarColumnDesc("ab","Comment for column ab")); td.addColumn (ScalarColumnDesc("ac")); td.addColumn (ScalarColumnDesc("ad","comment for ad")); td.addColumn (ScalarColumnDesc("ae")); td.addColumn (ScalarColumnDesc("af")); td.addColumn (ScalarColumnDesc("ag")); td.addColumn (ArrayColumnDesc("arr1",3,ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc("arr2",0)); td.addColumn (ArrayColumnDesc("arr3",0,ColumnDesc::Direct)); return td; } void a (const TableDesc& td) { // Now create a new table from the description. SetupNewTable newtab("tForwardCol_tmp.data0", td, Table::New); newtab.setShapeColumn("arr1",IPosition(3,2,3,4)); newtab.setShapeColumn("arr3",IPosition(3,2,3,4)); Table tab(newtab, 10); ScalarColumn ab1(tab,"ab"); ScalarColumn ab2(tab,"ab"); ScalarColumn ac (tab,"ac"); ScalarColumn ad(tab,"ad"); ScalarColumn ae(tab,"ae"); ScalarColumn af(tab,"af"); TableColumn ag1(tab,"ag"); ScalarColumn ag(tab,"ag"); ArrayColumn arr1(tab,"arr1"); ArrayColumn arr2(tab,"arr2"); ArrayColumn arr3(tab,"arr3"); Cube arrf(IPosition(3,2,3,4)); uInt i; char str[8]; indgen (arrf); for (i=0; i<10; i++) { ab1.put (i, i); ac.put (i, i+1); ad.put (i, i+2); ae.put (i, i+3); snprintf (str, sizeof(str), "V%i", i); af.put (i, str); arr1.put(i,arrf); arr2.put(i,arrf); arr3.put(i,arrf); arrf += (float)(arrf.nelements()); } ag1.putColumn (ad); // Sort the table. Table tabsort = tab.sort ("ac"); tabsort.rename ("tForwardCol_tmp.sort0", Table::New); } void b (const TableDesc& td) { Table tabsort ("tForwardCol_tmp.sort0", Table::Update); // Now use the description to make a table, which will use the // forwarding engine. SetupNewTable newtab1("tForwardCol_tmp.data1", td, Table::New); newtab1.setShapeColumn("arr1",IPosition(3,2,3,4)); newtab1.setShapeColumn("arr3",IPosition(3,2,3,4)); ForwardColumnEngine fce(tabsort); newtab1.bindAll (fce); Table forwTab(newtab1, 10); } void c (const TableDesc& tdin) { Table tab("tForwardCol_tmp.data1", Table::Update); // Now use the description to make a table, which will use the // forwarding engine again. TableDesc td = tdin; td.removeColumn ("ac"); SetupNewTable newtab1("tForwardCol_tmp.data2", td, Table::New); newtab1.setShapeColumn("arr1",IPosition(3,2,3,4)); newtab1.setShapeColumn("arr3",IPosition(3,2,3,4)); ForwardColumnEngine fce(tab, "ForwardEngine1"); newtab1.bindAll (fce); StManAipsIO sm; newtab1.bindColumn("ab", sm); Table forwTab(newtab1, 10); // Update the values. // Column ac will be updated in the original table. ScalarColumn ab1(forwTab,"ab"); uInt i; for (i=0; i<10; i++) { ab1.put (i, i+10); } forwTab.addColumn (ScalarColumnDesc("ac"), "ForwardEngine1", True); ScalarColumn ac (forwTab,"ac"); for (i=0; i<10; i++) { ac.put (i, i+11); } // Now use the description to make a table, which will use the // forwarding engine again. SetupNewTable newtab2("tForwardCol_tmp.data3", tdin, Table::New); newtab2.setShapeColumn("arr1",IPosition(3,2,3,4)); newtab2.setShapeColumn("arr3",IPosition(3,2,3,4)); ForwardColumnEngine fce2(forwTab); newtab2.bindAll (fce2); Table forwTab2(newtab2, 10); } void check(const String& tableName, Int abOffset, Int acOffset) { cout << "Checking table " << tableName << endl; // Read back the table and check the data. Table tab(tableName); ScalarColumn ab2(tab,"ab"); ScalarColumn ac (tab,"ac"); ScalarColumn ad(tab,"ad"); ScalarColumn ae(tab,"ae"); ScalarColumn af(tab,"af"); ScalarColumn ag(tab,"ag"); ArrayColumn arr1(tab,"arr1"); ArrayColumn arr2(tab,"arr2"); ArrayColumn arr3(tab,"arr3"); Int i; Int abval, acval; uInt adval; float aeval; String afval; DComplex agval; char str[8]; Cube arrf(IPosition(3,2,3,4)); Cube arrval(IPosition(3,2,3,4)); Cube arrvalslice(arrval(Slice(0,1),Slice(0,1,2),Slice(0,2,2))); Slice tmp; Slicer nslice (tmp, tmp, tmp, Slicer::endIsLength); Slicer nslice2(Slice(0,1), Slice(0,1,2), Slice(0,2,2), Slicer::endIsLength); indgen (arrf); for (i=0; i<10; i++) { cout << "get scalar row " << i << endl; ab2.get (i, abval); ac.get (i, acval); ad.get (i, adval); ae.get (i, aeval); af.get (i, afval); ag.get (i, agval); snprintf (str, sizeof(str), "V%i", i); if (abval != i+abOffset || acval != i+1+acOffset || Int(adval) != i+2 || aeval != i+3 || afval != str || agval != DComplex(i+2)) { cout << "error in row " << i << ": " << abval << ", " << acval << ", " << adval << ", " << aeval << ", " << afval << ", " << agval << endl; } arr1.get (i, arrval); if (!allEQ (arrval, arrf)) { cout << "error in arr1 in row " << i << endl; } arr2.get (i, arrval); if (!allEQ (arrval, arrf)) { cout << "error in arr2 in row " << i << endl; } cout << "get array row " << i << endl; arr3.get (i, arrval); if (!allEQ (arrval, arrf)) { cout << "error in arr3 in row " << i << endl; } arr2.getSlice (i, nslice, arrval); if (!allEQ (arrval, arrf)) { cout << "error in arr2 (entire slice) in row " << i << endl; } arr2.getSlice (i, nslice2, arrvalslice); if (!allEQ (arrval, arrf)) { cout << "error in arr2 (partial slice) in row " << i << endl; } arrf += (float)(arrf.nelements()); } Vector abvec = ab2.getColumn(); cout << tab.nrow() << " " << abvec.nelements() << endl; for (i=0; i<10; i++) { if (abvec(i) != i+abOffset) { cout << "error in getColumn " << i << ": " << abvec(i) << endl; } } Array arr1a = arr1.getColumn(); if (arr1a.ndim() != 4) { cout << "arr1a not 4-dim" << endl; } i=0; uInt j0,j1,j2,j3; for (j3=0; j3<10; j3++) for (j2=0; j2<4; j2++) for (j1=0; j1<3; j1++) for (j0=0; j0<2; j0++) { if (arr1a(IPosition(4,j0,j1,j2,j3)) != i++) { cout <<"arr1a error at " < arr1b = arr1.getColumn(nslice); if (arr1b.ndim() != 4) { cout << "arr1b not 4-dim" << endl; } i=0; for (j3=0; j3<10; j3++) for (j2=0; j2<4; j2++) for (j1=0; j1<3; j1++) for (j0=0; j0<2; j0++) { if (arr1b(IPosition(4,j0,j1,j2,j3)) != i++) { cout <<"arr1b error at " < ab (iter.table(), "ab"); if (ab(0) != 9-i+abOffset) { cout << "Invalid value " << ab(0) << " in TableIterator " << i << endl; } iter.next(); i++; } } casacore-3.7.1/tables/DataMan/test/tForwardCol.out000066400000000000000000000050631476623553700220450ustar00rootroot00000000000000Checking table tForwardCol_tmp.data0 get scalar row 0 get array row 0 get scalar row 1 get array row 1 get scalar row 2 get array row 2 get scalar row 3 get array row 3 get scalar row 4 get array row 4 get scalar row 5 get array row 5 get scalar row 6 get array row 6 get scalar row 7 get array row 7 get scalar row 8 get array row 8 get scalar row 9 get array row 9 10 10 Checking table tForwardCol_tmp.sort0 get scalar row 0 get array row 0 get scalar row 1 get array row 1 get scalar row 2 get array row 2 get scalar row 3 get array row 3 get scalar row 4 get array row 4 get scalar row 5 get array row 5 get scalar row 6 get array row 6 get scalar row 7 get array row 7 get scalar row 8 get array row 8 get scalar row 9 get array row 9 10 10 Checking table tForwardCol_tmp.data1 get scalar row 0 get array row 0 get scalar row 1 get array row 1 get scalar row 2 get array row 2 get scalar row 3 get array row 3 get scalar row 4 get array row 4 get scalar row 5 get array row 5 get scalar row 6 get array row 6 get scalar row 7 get array row 7 get scalar row 8 get array row 8 get scalar row 9 get array row 9 10 10 Checking table tForwardCol_tmp.data0 get scalar row 0 get array row 0 get scalar row 1 get array row 1 get scalar row 2 get array row 2 get scalar row 3 get array row 3 get scalar row 4 get array row 4 get scalar row 5 get array row 5 get scalar row 6 get array row 6 get scalar row 7 get array row 7 get scalar row 8 get array row 8 get scalar row 9 get array row 9 10 10 Checking table tForwardCol_tmp.data1 get scalar row 0 get array row 0 get scalar row 1 get array row 1 get scalar row 2 get array row 2 get scalar row 3 get array row 3 get scalar row 4 get array row 4 get scalar row 5 get array row 5 get scalar row 6 get array row 6 get scalar row 7 get array row 7 get scalar row 8 get array row 8 get scalar row 9 get array row 9 10 10 Checking table tForwardCol_tmp.data2 get scalar row 0 get array row 0 get scalar row 1 get array row 1 get scalar row 2 get array row 2 get scalar row 3 get array row 3 get scalar row 4 get array row 4 get scalar row 5 get array row 5 get scalar row 6 get array row 6 get scalar row 7 get array row 7 get scalar row 8 get array row 8 get scalar row 9 get array row 9 10 10 Checking table tForwardCol_tmp.data3 get scalar row 0 get array row 0 get scalar row 1 get array row 1 get scalar row 2 get array row 2 get scalar row 3 get array row 3 get scalar row 4 get array row 4 get scalar row 5 get array row 5 get scalar row 6 get array row 6 get scalar row 7 get array row 7 get scalar row 8 get array row 8 get scalar row 9 get array row 9 10 10 casacore-3.7.1/tables/DataMan/test/tForwardColRow.cc000066400000000000000000000231261476623553700223130ustar00rootroot00000000000000//# tForwardColRow.cc: Test program for class ForwardColumn //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Test program for class ForwardColumnIndexedRow // This program tests the virtual column engine ForwardColumnIndexedRowEngine. // The results are written to stdout. The script executing this program, // compares the results with the reference output file. TableDesc makeDesc(); void a (const TableDesc&); void c (const TableDesc&); void check(const String& tableName, Int abOffset, Int acOffset); int main () { try { TableDesc td = makeDesc(); a (td); check("tForwardColRow_tmp.data0", 0, 0); check("tForwardColRow_tmp.data1", 0, 0); c (td); check("tForwardColRow_tmp.data0", 0, 0); check("tForwardColRow_tmp.data1", 0, 0); check("tForwardColRow_tmp.data2", 0, 0); check("tForwardColRow_tmp.data3", 0, 0); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } // First build a description. TableDesc makeDesc() { // First register the virtual column engines. ForwardColumnEngine::registerClass(); ForwardColumnIndexedRowEngine::registerClass(); // Build the table description. TableDesc td("tTableDesc", "1", TableDesc::Scratch); td.comment() = "A test of class ForwardColumn"; td.addColumn (ScalarColumnDesc("row")); td.addColumn (ScalarColumnDesc("ab","Comment for column ab")); td.addColumn (ScalarColumnDesc("ac")); td.addColumn (ScalarColumnDesc("ad","comment for ad")); td.addColumn (ScalarColumnDesc("ae")); td.addColumn (ScalarColumnDesc("af")); td.addColumn (ScalarColumnDesc("ag")); td.addColumn (ArrayColumnDesc("arr1",3,ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc("arr2",0)); td.addColumn (ArrayColumnDesc("arr3",0,ColumnDesc::Direct)); return td; } void a (const TableDesc& td) { // Now create a new table from the description. SetupNewTable newtab("tForwardColRow_tmp.data0", td, Table::New); newtab.setShapeColumn("arr1",IPosition(3,2,3,4)); newtab.setShapeColumn("arr3",IPosition(3,2,3,4)); Table tab(newtab, 10); ScalarColumn ab1(tab,"ab"); ScalarColumn ab2(tab,"ab"); ScalarColumn ac (tab,"ac"); ScalarColumn ad(tab,"ad"); ScalarColumn ae(tab,"ae"); ScalarColumn af(tab,"af"); TableColumn ag1(tab,"ag"); ScalarColumn ag(tab,"ag"); ArrayColumn arr1(tab,"arr1"); ArrayColumn arr2(tab,"arr2"); ArrayColumn arr3(tab,"arr3"); Cube arrf(IPosition(3,2,3,4)); uInt i; char str[8]; indgen (arrf); for (i=0; i<10; i++) { ab1.put (i, i); ac.put (i, i+1); ad.put (i, i+2); ae.put (i, i+3); snprintf (str, sizeof(str), "V%i", i); af.put (i, str); arr1.put(i,arrf); arr2.put(i,arrf); arr3.put(i,arrf); arrf += (float)(arrf.nelements()); } ag1.putColumn (ad); // Now use the description to make a table, which will use the // forwarding engine. SetupNewTable newtab1("tForwardColRow_tmp.data1", td, Table::New); newtab1.setShapeColumn("arr1",IPosition(3,2,3,4)); newtab1.setShapeColumn("arr3",IPosition(3,2,3,4)); ForwardColumnEngine fce(tab); newtab1.bindAll (fce); Table forwTab(newtab1, 10); } void c (const TableDesc& tdin) { Table tab("tForwardColRow_tmp.data1", Table::Update); // Now use the description to make a table, which will use the // forwarding engine again. TableDesc td = tdin; td.removeColumn ("ac"); SetupNewTable newtab1("tForwardColRow_tmp.data2", td, Table::New); newtab1.setShapeColumn("arr1",IPosition(3,2,3,4)); newtab1.setShapeColumn("arr3",IPosition(3,2,3,4)); ForwardColumnIndexedRowEngine fce(tab, "row", "ForwardEngineRow1"); newtab1.bindAll (fce); StManAipsIO sm; newtab1.bindColumn("row", sm); Table forwTab(newtab1, 20); cout << " canAddRow=" << forwTab.canAddRow(); cout << " canRemoveRow=" << forwTab.canRemoveRow(); cout << endl; ScalarColumn rowCol (forwTab, "row"); uInt i; for (i=0; i<20; i++) { rowCol.put (i, i%10); } forwTab.addColumn (ScalarColumnDesc("ac"), "ForwardEngineRow1", True); // Now use the description to make a table, which will use the // forwarding engine again. SetupNewTable newtab2("tForwardColRow_tmp.data3", tdin, Table::New); newtab2.setShapeColumn("arr1",IPosition(3,2,3,4)); newtab2.setShapeColumn("arr3",IPosition(3,2,3,4)); ForwardColumnIndexedRowEngine fce2(forwTab, "row"); newtab2.bindAll (fce2); IncrementalStMan sm2; newtab2.bindColumn("row", sm2); Table forwTab2(newtab2); ScalarColumn rowCol2 (forwTab2, "row"); for (i=0; i<40; i++) { forwTab2.addRow(); rowCol2.put (i, i%20); } } void check(const String& tableName, Int abOffset, Int acOffset) { cout << "Checking table " << tableName << endl; // Read back the table and check the data. Table tab(tableName); uInt ntimes = tab.nrow() / 10; ScalarColumn ab2(tab,"ab"); ScalarColumn ac (tab,"ac"); ScalarColumn ad(tab,"ad"); ScalarColumn ae(tab,"ae"); ScalarColumn af(tab,"af"); ScalarColumn ag(tab,"ag"); ArrayColumn arr1(tab,"arr1"); ArrayColumn arr2(tab,"arr2"); ArrayColumn arr3(tab,"arr3"); uInt i, j; Int abval, acval; uInt adval; float aeval; String afval; DComplex agval; char str[8]; Cube arrf(IPosition(3,2,3,4)); Cube arrval(IPosition(3,2,3,4)); Cube arrvalslice(arrval(Slice(0,1),Slice(0,1,2),Slice(0,2,2))); Slice tmp; Slicer nslice (tmp, tmp, tmp, Slicer::endIsLength); Slicer nslice2(Slice(0,1), Slice(0,1,2), Slice(0,2,2), Slicer::endIsLength); for (j=0; j #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Test program for the IncrementalStMan storage manager // // This program tests the IncrementalStMan storage manager, especially the // get and put functions. // The results are written to stdout. The script executing this program, // compares the results with the reference output file. void a (uInt bucketSize, uInt mode); void b (const Vector& removedRows); void c(); void d(); void e (uInt nrrow); void f(); void testWithLocking(); int main (int argc, const char* argv[]) { /// DataManager::MAXROWNR32 = 0; uInt nr = 1000; if (argc > 1) { istringstream istr(argv[1]); istr >> nr; } try { a (nr, 0); c(); a (0, 1); e (20); d(); e (10); a (0, 2); e (20); a (nr, 0); f(); testWithLocking(); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } void init (Cube& arrf, Vector& arrdc, Cube& arrb) { // Hey it's a bug in the SGI compiler that's // why the static_cast indgen (static_cast< Cube &>(arrf)); arrdc(0) = DComplex(1.2, 3.4); arrdc(1) = DComplex(-2.3, 5.6); IPosition shape(arrb.shape()); uInt n = 0; for (Int i=0; i("ac")); td.addColumn (ScalarColumnDesc("ad")); td.addColumn (ScalarColumnDesc("ae")); td.addColumn (ScalarColumnDesc("af")); td.addColumn (ArrayColumnDesc("arr1",3,ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc("arr2",0)); td.addColumn (ArrayColumnDesc("arr3",0,ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc("arr4",0,ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc("arr5",0,ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc("arr6",0,ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc("arr7",0,ColumnDesc::FixedShape)); // Now create a new table from the description. SetupNewTable newtab("tIncrementalStMan_tmp.data", td, Table::New); // Create a storage manager for it. IncrementalStMan sm1 ("ISM", bucketSize, False); newtab.bindAll (sm1); newtab.setShapeColumn("arr1",IPosition(3,2,3,4)); newtab.setShapeColumn("arr3",IPosition(3,2,3,4)); newtab.setShapeColumn("arr4",IPosition(1,8)); newtab.setShapeColumn("arr5",IPosition(1,2)); newtab.setShapeColumn("arr6",IPosition(3,5,7,11)); newtab.setShapeColumn("arr7",IPosition(3,5,7,11)); tab = Table (newtab, 10); } else { tab = Table ("tIncrementalStMan_tmp.data", Table::Update); } ScalarColumn ac(tab,"ac"); ScalarColumn ad(tab,"ad"); ScalarColumn ae(tab,"ae"); ScalarColumn af(tab,"af"); ArrayColumn arr1(tab,"arr1"); ArrayColumn arr2(tab,"arr2"); ArrayColumn arr3(tab,"arr3"); ArrayColumn arr4(tab,"arr4"); ArrayColumn arr5(tab, "arr5"); ArrayColumn arr6(tab, "arr6"); ArrayColumn arr7(tab, "arr7"); Cube arrf(IPosition(3,2,3,4)); Vector arrdc(2); Cube arrb(IPosition(3,5,7,11)); init (arrf, arrdc, arrb); uInt i; for (i=0; i<10; i++) { if (mode < 2) { if (mode == 1) { tab.addRow(); } if (i%2 == 1) { ac.put (i, Complex(i+1)); } ad.put (i, i); ae.put (i, 10); arr1.put (i, arrf); if (i%4 == 0) { arr2.put (i, arrf); arr3.put (i, arrf); } arr5.put (i, arrdc); arr6.put (i, arrb); if (i == 0) { arr7.put (i, arrb); } } arrf += (float)(arrf.nelements()); arrdc += DComplex(2, 3); } if (mode == 2) { Vector vecae = ae.getColumn(); vecae -= float(1); ae.putColumn (vecae); } String str("abc"); for (i=0; i<10; i++) { tab.addRow(); if (i%2 == 1) { ac.put (i+10, Complex(i+10+1)); } ad.put (i+10, i+10); ae.put (i+10, i/3); if (i%3 == 0) str += 'a'; af.put (i+10, str); if (i%3 != 0) { arr1.put (i, arrf); arr1.put (i+10, arrf); } if (i%3 == 1) { arr2.put (i, arrf); arr2.put (i+10, arrf); arr3.put (i, arrf); arr3.put (i+10, arrf); } arrf += (float)(arrf.nelements()); } for (i=0; i<19; i++) { cout << ae(i) << ", "; } cout << ae(19) << endl; Vector vecae = ae.getColumn(); for (i=0; i vecae2 = ae.getColumn(); for (i=0; i vstr(8); vstr(0) = "z"; for (i=1; i ac(tab, "ac"); ScalarColumn ad(tab, "ad"); ScalarColumn ae(tab, "ae"); ScalarColumn af(tab, "af"); ArrayColumn arr1(tab,"arr1"); ArrayColumn arr2(tab,"arr2"); ArrayColumn arr3(tab,"arr3"); ArrayColumn arr5(tab,"arr5"); ArrayColumn arr6(tab,"arr6"); ArrayColumn arr7(tab,"arr7"); cout << "#Rows " << tab.nrow() << endl; uInt i; if (tab.nrow() == 20) { for (i=0; i<19; i++) { cout << ac(i) << ", "; } cout << ac(19) << endl; for (i=0; i<19; i++) { cout << ad(i) << ", "; } cout << ad(19) << endl; for (i=0; i<19; i++) { cout << ae(i) << ", "; } cout << ae(19) << endl; for (i=0; i<19; i++) { cout << af(i) << ", "; } cout << af(19) << endl; } Cube arrf(2,3,4); Vector arrdc(2); Cube arrb(5,7,11); init (arrf, arrdc, arrb); // Check if all values match. uInt rownr = 0; for (i=0; i<19; i++) { if (!removedRows(i)) { if (i>0 && ac(rownr) != Complex(acvalues[i])) cout << i << "," << rownr << " ac-mismatch: " << ac(rownr) << endl; if (ad(rownr) != advalues[i]) cout << i << "," << rownr << " ad-mismatch: " << ad(rownr) << endl; if (ae(rownr) != aevalues[i]) cout << i << "," << rownr << " ae-mismatch: " << ae(rownr) << endl; if (Int(af(rownr).length()) != afvalues[i]) cout << i << "," << rownr << " af-mismatch: " << af(rownr) << endl; if (!allEQ (arr1(rownr), arrf + 24*arr1Start[i])) cout << i << "," << rownr << " arr1-mismatch: " << arr1(rownr) << endl; if (!allEQ (arr2(rownr), arrf + 24*arr2Start[i])) cout << i << "," << rownr << " arr2-mismatch: " << arr2(rownr) << endl; if (!allEQ(arr3(rownr), arrf + 24*arr3Start[i])) cout << i << "," << rownr << " arr3-mismatch: " << arr3(rownr) << endl; uInt j = min(9U,i); if (!allEQ(arr5(rownr), arrdc + DComplex(2*j, 3*j))) cout << i << "," << rownr << " arr5-mismatch: " << arr5(rownr) << endl; if (!allEQ(arr6(rownr), arrb)) cout << i << "," << rownr << " arr6-mismatch: " << arr6(rownr) << endl; if (i == 19) { arrb(0,0,0) = True; } if (!allEQ(arr7(rownr), arrb)) cout << i << "," << rownr << " arr7-mismatch: " << arr7(rownr) << endl; rownr++; } } arrb(0,0,0) = False; if (tab.nrow() == 20) { cout << arr1(19) << endl; cout << arr2(19) << endl; cout << arr3(19) << endl; IPosition bshape = arrb.shape(); Cube arrb1 (bshape); for (i=0; i<19; i++) { for (Int j=0; j result (arrb1(IPosition(3,j,0,0), IPosition(3,j,bshape(1)-1,bshape(2)-1))); arr6.getSlice (i, Slicer(IPosition(3,j,0,0), IPosition(3,1,bshape(1),bshape(2))), result); } if (!allEQ(arrb1, arrb)) cout << i << " arr6-slice-mismatch: " << arrb1 << ", "; arrb1.set (True); } for (i=0; i<19; i++) { if (i == 19) { arrb(0,0,0) = True; } for (Int j=0; j result (arrb1(IPosition(3,j,0,0), IPosition(3,j,bshape(1)-1,bshape(2)-1))); arr7.getSlice (i, Slicer(IPosition(3,j,0,0), IPosition(3,1,bshape(1),bshape(2))), result); } if (!allEQ(arrb1, arrb)) cout << i << " arr7-slice-mismatch: " << arrb1 << ", "; arrb1.set (True); } } if (tab.nrow() % 5 == 0) { accessor.showCacheStatistics (cout); } accessor.clearCache(); } void c() { uInt i; Vector removedRows(20); removedRows.set (False); b (removedRows); // Remove several rows. // Open the table as read/write for that purpose. Table rwtab ("tIncrementalStMan_tmp.data", Table::Update); rwtab.removeRow (17); AlwaysAssertExit (rwtab.nrow() == 19); removedRows(17) = True; b (removedRows); rwtab.removeRow (18); AlwaysAssertExit (rwtab.nrow() == 18); removedRows(19) = True; b (removedRows); rwtab.removeRow (10); AlwaysAssertExit (rwtab.nrow() == 17); removedRows(10) = True; b (removedRows); rwtab.removeRow (0); AlwaysAssertExit (rwtab.nrow() == 16); removedRows(0) = True; b (removedRows); rwtab.removeRow (10); AlwaysAssertExit (rwtab.nrow() == 15); removedRows(12) = True; // row 10 was old row 12 b (removedRows); // Remove several rows. Vector rows(5); for (i=0; i<5; i++) { rows(i)=i+2; removedRows(i+3) = True; } rwtab.removeRow (rows); AlwaysAssertExit (rwtab.nrow() == 10); b (removedRows); // Remove all remaining rows. for (i=0; i<10; i++) { rwtab.removeRow (0); AlwaysAssertExit (rwtab.nrow() == 9-i); for (uInt j=0; j<20; j++) { if (!removedRows(j)) { removedRows(j) = True; break; } } b (removedRows); } } void d() { uInt i; // Remove the last 10 rows. // Open the table as read/write for that purpose. Table rwtab ("tIncrementalStMan_tmp.data", Table::Update); Vector rows(10); for (i=0; i<10; i++) { rows(i)=i+10; } rwtab.removeRow (rows); AlwaysAssertExit (rwtab.nrow() == 10); } void e (uInt nrrow) { uInt i; Vector removedRows(20); removedRows.set (True); for (i=0; i arr1(rwtab,"arr1"); ArrayColumn arr2(rwtab,"arr2"); ArrayColumn arr7(rwtab, "arr7"); Vector vecb(10); Vector vecf(10); indgen (vecf); //# Try to change some arrays (which cannot be done). try { arr1.put (0, vecf); } catch (std::exception& x) { cout << x.what() << endl; // shape cannot change } try { arr7.put (0, vecb); } catch (std::exception& x) { cout << x.what() << endl; // shape cannot change } Vector removedRows(20); removedRows.set (False); b (removedRows); //# Change an array which can be changed. //# Check value, change it back and check all values. Array arrrow0 = arr2(0); Array arrrow12 = arr2(12); arr2.put (0, vecf); arr2.put (12, vecf); AlwaysAssertExit (allEQ (arr2(0), vecf)); AlwaysAssertExit (allEQ (arr2(12), vecf)); arr2.put (0, arrrow0); arr2.put (12, arrrow12); b (removedRows); } // This function tests issue 970. void testWithLocking() { // Create a table of 10**6 rows where each 10000-th row is written. { uInt nrow = 1000000; uInt time_rows = 10000; TableDesc td; td.addColumn(ScalarColumnDesc("TIME")); SetupNewTable newtab("tIn.tab", td, Table::New); IncrementalStMan ism; newtab.bindAll (ism); Table tab(newtab, nrow); ScalarColumn col(tab, "TIME"); uInt row=0; while (row < nrow) { uInt n = min(time_rows, nrow-row); Vector vec(n); indgen (vec, Int(row)); col.put (row, Int(row)); row += n; } } // Read back the table and use UserLocking to make a lock // which invalidates the cache. // Do it twice with a size less and greater than 10000. for (uInt time_rows=3333; time_rows<=33333; time_rows+=30000) { Table tab("tIn.tab", TableLock::UserLocking); uInt nrow = tab.nrow(); uInt row = 0; while (row < nrow) { uInt n = min(time_rows, nrow-row); tab.lock(); ScalarColumn col(tab, "TIME"); Vector vec = col.getColumnRange (Slicer(IPosition(1,row), IPosition(1,n))); // Check the contents. for (uInt i=0; i>> IncrementalStMan cache statistics: cacheSize: 2 (*1000) #buckets: 12 (< #reads + #writes!) #reads: 84 #accesses: 236 hit-rate: 64.4068% <<< #Rows 19 #Rows 18 #Rows 17 #Rows 16 #Rows 15 >>> IncrementalStMan cache statistics: cacheSize: 2 (*1000) #buckets: 12 (< #reads + #writes!) #deleted: 1 #reads: 12 #writes: 1 #accesses: 125 hit-rate: 90.4% <<< #Rows 10 >>> IncrementalStMan cache statistics: cacheSize: 2 (*1000) #buckets: 12 #deleted: 5 #reads: 11 #writes: 1 #accesses: 89 hit-rate: 87.6404% <<< #Rows 9 #Rows 8 #Rows 7 #Rows 6 #Rows 5 >>> IncrementalStMan cache statistics: cacheSize: 2 (*1000) #buckets: 12 #deleted: 9 #reads: 4 #accesses: 38 hit-rate: 89.4737% <<< #Rows 4 #Rows 3 #Rows 2 #Rows 1 #Rows 0 >>> IncrementalStMan cache statistics: cacheSize: 2 (*1000) #buckets: 1 #reads: 1 #accesses: 11 hit-rate: 90.9091% <<< 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] #Rows 20 (0,0), (2,0), (2,0), (4,0), (4,0), (6,0), (6,0), (8,0), (8,0), (10,0), (10,0), (12,0), (12,0), (14,0), (14,0), (16,0), (16,0), (18,0), (18,0), (20,0) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4 , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][432, 433] [0, 1, 0][434, 435] [0, 2, 0][436, 437] [0, 0, 1][438, 439] [0, 1, 1][440, 441] [0, 2, 1][442, 443] [0, 0, 2][444, 445] [0, 1, 2][446, 447] [0, 2, 2][448, 449] [0, 0, 3][450, 451] [0, 1, 3][452, 453] [0, 2, 3][454, 455] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] >>> IncrementalStMan cache statistics: cacheSize: 2 (*1000) #buckets: 12 (< #reads + #writes!) #reads: 84 #accesses: 236 hit-rate: 64.4068% <<< #Rows 10 >>> IncrementalStMan cache statistics: cacheSize: 2 (*1000) #buckets: 12 #deleted: 4 #reads: 8 #accesses: 87 hit-rate: 90.8046% <<< 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] #Rows 20 (0,0), (2,0), (2,0), (4,0), (4,0), (6,0), (6,0), (8,0), (8,0), (10,0), (10,0), (12,0), (12,0), (14,0), (14,0), (16,0), (16,0), (18,0), (18,0), (20,0) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4 , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][432, 433] [0, 1, 0][434, 435] [0, 2, 0][436, 437] [0, 0, 1][438, 439] [0, 1, 1][440, 441] [0, 2, 1][442, 443] [0, 0, 2][444, 445] [0, 1, 2][446, 447] [0, 2, 2][448, 449] [0, 0, 3][450, 451] [0, 1, 3][452, 453] [0, 2, 3][454, 455] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] >>> IncrementalStMan cache statistics: cacheSize: 2 (*1000) #buckets: 12 (< #reads + #writes!) #deleted: 1 #reads: 77 #accesses: 222 hit-rate: 65.3153% <<< 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] ArrayColumn::put for row 0 in column arr1: Table array conformance error (shape=[10], expected [2, 3, 4]) ArrayColumn::put for row 0 in column arr7: Table array conformance error (shape=[10], expected [5, 7, 11]) #Rows 20 (0,0), (2,0), (2,0), (4,0), (4,0), (6,0), (6,0), (8,0), (8,0), (10,0), (10,0), (12,0), (12,0), (14,0), (14,0), (16,0), (16,0), (18,0), (18,0), (20,0) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4 , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][432, 433] [0, 1, 0][434, 435] [0, 2, 0][436, 437] [0, 0, 1][438, 439] [0, 1, 1][440, 441] [0, 2, 1][442, 443] [0, 0, 2][444, 445] [0, 1, 2][446, 447] [0, 2, 2][448, 449] [0, 0, 3][450, 451] [0, 1, 3][452, 453] [0, 2, 3][454, 455] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] >>> IncrementalStMan cache statistics: cacheSize: 2 (*1000) #buckets: 12 (< #reads + #writes!) #reads: 84 #accesses: 236 hit-rate: 64.4068% <<< #Rows 20 (0,0), (2,0), (2,0), (4,0), (4,0), (6,0), (6,0), (8,0), (8,0), (10,0), (10,0), (12,0), (12,0), (14,0), (14,0), (16,0), (16,0), (18,0), (18,0), (20,0) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4 , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][432, 433] [0, 1, 0][434, 435] [0, 2, 0][436, 437] [0, 0, 1][438, 439] [0, 1, 1][440, 441] [0, 2, 1][442, 443] [0, 0, 2][444, 445] [0, 1, 2][446, 447] [0, 2, 2][448, 449] [0, 0, 3][450, 451] [0, 1, 3][452, 453] [0, 2, 3][454, 455] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] >>> IncrementalStMan cache statistics: cacheSize: 2 (*1000) #buckets: 12 (< #reads + #writes!) #reads: 85 #writes: 2 #accesses: 257 hit-rate: 66.9261% <<< 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] #Rows 20 (0,0), (2,0), (2,0), (4,0), (4,0), (6,0), (6,0), (8,0), (8,0), (10,0), (10,0), (12,0), (12,0), (14,0), (14,0), (16,0), (16,0), (18,0), (18,0), (20,0) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4 , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][432, 433] [0, 1, 0][434, 435] [0, 2, 0][436, 437] [0, 0, 1][438, 439] [0, 1, 1][440, 441] [0, 2, 1][442, 443] [0, 0, 2][444, 445] [0, 1, 2][446, 447] [0, 2, 2][448, 449] [0, 0, 3][450, 451] [0, 1, 3][452, 453] [0, 2, 3][454, 455] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] >>> IncrementalStMan cache statistics: cacheSize: 2 (*2000) #buckets: 4 (< #reads + #writes!) #reads: 28 #accesses: 158 hit-rate: 82.2785% <<< #Rows 19 #Rows 18 #Rows 17 #Rows 16 #Rows 15 >>> IncrementalStMan cache statistics: cacheSize: 2 (*2000) #buckets: 4 (< #reads + #writes!) #reads: 5 #writes: 1 #accesses: 93 hit-rate: 94.6237% <<< #Rows 10 >>> IncrementalStMan cache statistics: cacheSize: 2 (*2000) #buckets: 4 #deleted: 1 #reads: 4 #accesses: 68 hit-rate: 94.1176% <<< #Rows 9 #Rows 8 #Rows 7 #Rows 6 #Rows 5 >>> IncrementalStMan cache statistics: cacheSize: 2 (*2000) #buckets: 4 #deleted: 2 #reads: 2 #accesses: 32 hit-rate: 93.75% <<< #Rows 4 #Rows 3 #Rows 2 #Rows 1 #Rows 0 >>> IncrementalStMan cache statistics: cacheSize: 2 (*2000) #buckets: 1 #reads: 1 #accesses: 11 hit-rate: 90.9091% <<< 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] #Rows 20 (0,0), (2,0), (2,0), (4,0), (4,0), (6,0), (6,0), (8,0), (8,0), (10,0), (10,0), (12,0), (12,0), (14,0), (14,0), (16,0), (16,0), (18,0), (18,0), (20,0) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4 , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][432, 433] [0, 1, 0][434, 435] [0, 2, 0][436, 437] [0, 0, 1][438, 439] [0, 1, 1][440, 441] [0, 2, 1][442, 443] [0, 0, 2][444, 445] [0, 1, 2][446, 447] [0, 2, 2][448, 449] [0, 0, 3][450, 451] [0, 1, 3][452, 453] [0, 2, 3][454, 455] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] >>> IncrementalStMan cache statistics: cacheSize: 2 (*2000) #buckets: 4 (< #reads + #writes!) #reads: 28 #accesses: 158 hit-rate: 82.2785% <<< #Rows 10 >>> IncrementalStMan cache statistics: cacheSize: 2 (*2000) #buckets: 4 #deleted: 1 #reads: 3 #accesses: 64 hit-rate: 95.3125% <<< 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] #Rows 20 (0,0), (2,0), (2,0), (4,0), (4,0), (6,0), (6,0), (8,0), (8,0), (10,0), (10,0), (12,0), (12,0), (14,0), (14,0), (16,0), (16,0), (18,0), (18,0), (20,0) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4 , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][432, 433] [0, 1, 0][434, 435] [0, 2, 0][436, 437] [0, 0, 1][438, 439] [0, 1, 1][440, 441] [0, 2, 1][442, 443] [0, 0, 2][444, 445] [0, 1, 2][446, 447] [0, 2, 2][448, 449] [0, 0, 3][450, 451] [0, 1, 3][452, 453] [0, 2, 3][454, 455] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] >>> IncrementalStMan cache statistics: cacheSize: 2 (*2000) #buckets: 4 (< #reads + #writes!) #reads: 28 #accesses: 156 hit-rate: 82.0513% <<< 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] ArrayColumn::put for row 0 in column arr1: Table array conformance error (shape=[10], expected [2, 3, 4]) ArrayColumn::put for row 0 in column arr7: Table array conformance error (shape=[10], expected [5, 7, 11]) #Rows 20 (0,0), (2,0), (2,0), (4,0), (4,0), (6,0), (6,0), (8,0), (8,0), (10,0), (10,0), (12,0), (12,0), (14,0), (14,0), (16,0), (16,0), (18,0), (18,0), (20,0) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4 , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][432, 433] [0, 1, 0][434, 435] [0, 2, 0][436, 437] [0, 0, 1][438, 439] [0, 1, 1][440, 441] [0, 2, 1][442, 443] [0, 0, 2][444, 445] [0, 1, 2][446, 447] [0, 2, 2][448, 449] [0, 0, 3][450, 451] [0, 1, 3][452, 453] [0, 2, 3][454, 455] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] >>> IncrementalStMan cache statistics: cacheSize: 2 (*2000) #buckets: 4 (< #reads + #writes!) #reads: 28 #accesses: 158 hit-rate: 82.2785% <<< #Rows 20 (0,0), (2,0), (2,0), (4,0), (4,0), (6,0), (6,0), (8,0), (8,0), (10,0), (10,0), (12,0), (12,0), (14,0), (14,0), (16,0), (16,0), (18,0), (18,0), (20,0) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4 , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][432, 433] [0, 1, 0][434, 435] [0, 2, 0][436, 437] [0, 0, 1][438, 439] [0, 1, 1][440, 441] [0, 2, 1][442, 443] [0, 0, 2][444, 445] [0, 1, 2][446, 447] [0, 2, 2][448, 449] [0, 0, 3][450, 451] [0, 1, 3][452, 453] [0, 2, 3][454, 455] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] >>> IncrementalStMan cache statistics: cacheSize: 2 (*2000) #buckets: 4 (< #reads + #writes!) #reads: 29 #writes: 2 #accesses: 181 hit-rate: 83.9779% <<< 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] #Rows 20 (0,0), (2,0), (2,0), (4,0), (4,0), (6,0), (6,0), (8,0), (8,0), (10,0), (10,0), (12,0), (12,0), (14,0), (14,0), (16,0), (16,0), (18,0), (18,0), (20,0) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4 , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][432, 433] [0, 1, 0][434, 435] [0, 2, 0][436, 437] [0, 0, 1][438, 439] [0, 1, 1][440, 441] [0, 2, 1][442, 443] [0, 0, 2][444, 445] [0, 1, 2][446, 447] [0, 2, 2][448, 449] [0, 0, 3][450, 451] [0, 1, 3][452, 453] [0, 2, 3][454, 455] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] >>> IncrementalStMan cache statistics: cacheSize: 2 (*3000) #buckets: 3 (< #reads + #writes!) #reads: 21 #accesses: 150 hit-rate: 86% <<< #Rows 19 #Rows 18 #Rows 17 #Rows 16 #Rows 15 >>> IncrementalStMan cache statistics: cacheSize: 2 (*3000) #buckets: 3 #reads: 3 #accesses: 89 hit-rate: 96.6292% <<< #Rows 10 >>> IncrementalStMan cache statistics: cacheSize: 2 (*3000) #buckets: 3 (< #reads + #writes!) #reads: 3 #writes: 1 #accesses: 67 hit-rate: 95.5224% <<< #Rows 9 #Rows 8 #Rows 7 #Rows 6 #Rows 5 >>> IncrementalStMan cache statistics: cacheSize: 2 (*3000) #buckets: 3 #deleted: 1 #reads: 2 #accesses: 32 hit-rate: 93.75% <<< #Rows 4 #Rows 3 #Rows 2 #Rows 1 #Rows 0 >>> IncrementalStMan cache statistics: cacheSize: 2 (*3000) #buckets: 1 #reads: 1 #accesses: 11 hit-rate: 90.9091% <<< 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] #Rows 20 (0,0), (2,0), (2,0), (4,0), (4,0), (6,0), (6,0), (8,0), (8,0), (10,0), (10,0), (12,0), (12,0), (14,0), (14,0), (16,0), (16,0), (18,0), (18,0), (20,0) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4 , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][432, 433] [0, 1, 0][434, 435] [0, 2, 0][436, 437] [0, 0, 1][438, 439] [0, 1, 1][440, 441] [0, 2, 1][442, 443] [0, 0, 2][444, 445] [0, 1, 2][446, 447] [0, 2, 2][448, 449] [0, 0, 3][450, 451] [0, 1, 3][452, 453] [0, 2, 3][454, 455] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] >>> IncrementalStMan cache statistics: cacheSize: 2 (*3000) #buckets: 3 (< #reads + #writes!) #reads: 21 #accesses: 150 hit-rate: 86% <<< #Rows 10 >>> IncrementalStMan cache statistics: cacheSize: 2 (*3000) #buckets: 3 #deleted: 1 #reads: 2 #accesses: 60 hit-rate: 96.6667% <<< 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] #Rows 20 (0,0), (2,0), (2,0), (4,0), (4,0), (6,0), (6,0), (8,0), (8,0), (10,0), (10,0), (12,0), (12,0), (14,0), (14,0), (16,0), (16,0), (18,0), (18,0), (20,0) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4 , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][432, 433] [0, 1, 0][434, 435] [0, 2, 0][436, 437] [0, 0, 1][438, 439] [0, 1, 1][440, 441] [0, 2, 1][442, 443] [0, 0, 2][444, 445] [0, 1, 2][446, 447] [0, 2, 2][448, 449] [0, 0, 3][450, 451] [0, 1, 3][452, 453] [0, 2, 3][454, 455] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] >>> IncrementalStMan cache statistics: cacheSize: 2 (*3000) #buckets: 3 #deleted: 1 #reads: 2 #accesses: 139 hit-rate: 98.5611% <<< 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] ArrayColumn::put for row 0 in column arr1: Table array conformance error (shape=[10], expected [2, 3, 4]) ArrayColumn::put for row 0 in column arr7: Table array conformance error (shape=[10], expected [5, 7, 11]) #Rows 20 (0,0), (2,0), (2,0), (4,0), (4,0), (6,0), (6,0), (8,0), (8,0), (10,0), (10,0), (12,0), (12,0), (14,0), (14,0), (16,0), (16,0), (18,0), (18,0), (20,0) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4 , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][432, 433] [0, 1, 0][434, 435] [0, 2, 0][436, 437] [0, 0, 1][438, 439] [0, 1, 1][440, 441] [0, 2, 1][442, 443] [0, 0, 2][444, 445] [0, 1, 2][446, 447] [0, 2, 2][448, 449] [0, 0, 3][450, 451] [0, 1, 3][452, 453] [0, 2, 3][454, 455] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] >>> IncrementalStMan cache statistics: cacheSize: 2 (*3000) #buckets: 3 (< #reads + #writes!) #reads: 21 #accesses: 150 hit-rate: 86% <<< #Rows 20 (0,0), (2,0), (2,0), (4,0), (4,0), (6,0), (6,0), (8,0), (8,0), (10,0), (10,0), (12,0), (12,0), (14,0), (14,0), (16,0), (16,0), (18,0), (18,0), (20,0) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4 , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][432, 433] [0, 1, 0][434, 435] [0, 2, 0][436, 437] [0, 0, 1][438, 439] [0, 1, 1][440, 441] [0, 2, 1][442, 443] [0, 0, 2][444, 445] [0, 1, 2][446, 447] [0, 2, 2][448, 449] [0, 0, 3][450, 451] [0, 1, 3][452, 453] [0, 2, 3][454, 455] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] >>> IncrementalStMan cache statistics: cacheSize: 2 (*3000) #buckets: 3 (< #reads + #writes!) #reads: 21 #writes: 2 #accesses: 173 hit-rate: 87.8613% <<< 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] #Rows 20 (0,0), (2,0), (2,0), (4,0), (4,0), (6,0), (6,0), (8,0), (8,0), (10,0), (10,0), (12,0), (12,0), (14,0), (14,0), (16,0), (16,0), (18,0), (18,0), (20,0) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4 , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][432, 433] [0, 1, 0][434, 435] [0, 2, 0][436, 437] [0, 0, 1][438, 439] [0, 1, 1][440, 441] [0, 2, 1][442, 443] [0, 0, 2][444, 445] [0, 1, 2][446, 447] [0, 2, 2][448, 449] [0, 0, 3][450, 451] [0, 1, 3][452, 453] [0, 2, 3][454, 455] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] >>> IncrementalStMan cache statistics: cacheSize: 2 (*4000) #buckets: 2 #reads: 2 #accesses: 137 hit-rate: 98.5401% <<< #Rows 19 #Rows 18 #Rows 17 #Rows 16 #Rows 15 >>> IncrementalStMan cache statistics: cacheSize: 2 (*4000) #buckets: 2 #reads: 2 #accesses: 81 hit-rate: 97.5309% <<< #Rows 10 >>> IncrementalStMan cache statistics: cacheSize: 2 (*4000) #buckets: 2 #reads: 2 #accesses: 61 hit-rate: 96.7213% <<< #Rows 9 #Rows 8 #Rows 7 #Rows 6 #Rows 5 >>> IncrementalStMan cache statistics: cacheSize: 2 (*4000) #buckets: 2 #deleted: 1 #reads: 1 #accesses: 26 hit-rate: 96.1538% <<< #Rows 4 #Rows 3 #Rows 2 #Rows 1 #Rows 0 >>> IncrementalStMan cache statistics: cacheSize: 2 (*4000) #buckets: 1 #reads: 1 #accesses: 11 hit-rate: 90.9091% <<< 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] #Rows 20 (0,0), (2,0), (2,0), (4,0), (4,0), (6,0), (6,0), (8,0), (8,0), (10,0), (10,0), (12,0), (12,0), (14,0), (14,0), (16,0), (16,0), (18,0), (18,0), (20,0) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4 , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][432, 433] [0, 1, 0][434, 435] [0, 2, 0][436, 437] [0, 0, 1][438, 439] [0, 1, 1][440, 441] [0, 2, 1][442, 443] [0, 0, 2][444, 445] [0, 1, 2][446, 447] [0, 2, 2][448, 449] [0, 0, 3][450, 451] [0, 1, 3][452, 453] [0, 2, 3][454, 455] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] >>> IncrementalStMan cache statistics: cacheSize: 2 (*4000) #buckets: 2 #reads: 2 #accesses: 137 hit-rate: 98.5401% <<< #Rows 10 >>> IncrementalStMan cache statistics: cacheSize: 2 (*4000) #buckets: 2 #reads: 2 #accesses: 58 hit-rate: 96.5517% <<< 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] #Rows 20 (0,0), (2,0), (2,0), (4,0), (4,0), (6,0), (6,0), (8,0), (8,0), (10,0), (10,0), (12,0), (12,0), (14,0), (14,0), (16,0), (16,0), (18,0), (18,0), (20,0) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4 , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][432, 433] [0, 1, 0][434, 435] [0, 2, 0][436, 437] [0, 0, 1][438, 439] [0, 1, 1][440, 441] [0, 2, 1][442, 443] [0, 0, 2][444, 445] [0, 1, 2][446, 447] [0, 2, 2][448, 449] [0, 0, 3][450, 451] [0, 1, 3][452, 453] [0, 2, 3][454, 455] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] >>> IncrementalStMan cache statistics: cacheSize: 2 (*4000) #buckets: 2 #reads: 2 #accesses: 137 hit-rate: 98.5401% <<< 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] ArrayColumn::put for row 0 in column arr1: Table array conformance error (shape=[10], expected [2, 3, 4]) ArrayColumn::put for row 0 in column arr7: Table array conformance error (shape=[10], expected [5, 7, 11]) #Rows 20 (0,0), (2,0), (2,0), (4,0), (4,0), (6,0), (6,0), (8,0), (8,0), (10,0), (10,0), (12,0), (12,0), (14,0), (14,0), (16,0), (16,0), (18,0), (18,0), (20,0) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4 , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][432, 433] [0, 1, 0][434, 435] [0, 2, 0][436, 437] [0, 0, 1][438, 439] [0, 1, 1][440, 441] [0, 2, 1][442, 443] [0, 0, 2][444, 445] [0, 1, 2][446, 447] [0, 2, 2][448, 449] [0, 0, 3][450, 451] [0, 1, 3][452, 453] [0, 2, 3][454, 455] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] >>> IncrementalStMan cache statistics: cacheSize: 2 (*4000) #buckets: 2 #reads: 2 #accesses: 137 hit-rate: 98.5401% <<< #Rows 20 (0,0), (2,0), (2,0), (4,0), (4,0), (6,0), (6,0), (8,0), (8,0), (10,0), (10,0), (12,0), (12,0), (14,0), (14,0), (16,0), (16,0), (18,0), (18,0), (20,0) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4 , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][432, 433] [0, 1, 0][434, 435] [0, 2, 0][436, 437] [0, 0, 1][438, 439] [0, 1, 1][440, 441] [0, 2, 1][442, 443] [0, 0, 2][444, 445] [0, 1, 2][446, 447] [0, 2, 2][448, 449] [0, 0, 3][450, 451] [0, 1, 3][452, 453] [0, 2, 3][454, 455] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] >>> IncrementalStMan cache statistics: cacheSize: 2 (*4000) #buckets: 2 #reads: 2 #accesses: 160 hit-rate: 98.75% <<< 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] #Rows 20 (0,0), (2,0), (2,0), (4,0), (4,0), (6,0), (6,0), (8,0), (8,0), (10,0), (10,0), (12,0), (12,0), (14,0), (14,0), (16,0), (16,0), (18,0), (18,0), (20,0) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4 , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][432, 433] [0, 1, 0][434, 435] [0, 2, 0][436, 437] [0, 0, 1][438, 439] [0, 1, 1][440, 441] [0, 2, 1][442, 443] [0, 0, 2][444, 445] [0, 1, 2][446, 447] [0, 2, 2][448, 449] [0, 0, 3][450, 451] [0, 1, 3][452, 453] [0, 2, 3][454, 455] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] >>> IncrementalStMan cache statistics: cacheSize: 2 (*5000) #buckets: 1 #reads: 1 #accesses: 125 hit-rate: 99.2% <<< #Rows 19 #Rows 18 #Rows 17 #Rows 16 #Rows 15 >>> IncrementalStMan cache statistics: cacheSize: 2 (*5000) #buckets: 1 #reads: 1 #accesses: 76 hit-rate: 98.6842% <<< #Rows 10 >>> IncrementalStMan cache statistics: cacheSize: 2 (*5000) #buckets: 1 #reads: 1 #accesses: 57 hit-rate: 98.2456% <<< #Rows 9 #Rows 8 #Rows 7 #Rows 6 #Rows 5 >>> IncrementalStMan cache statistics: cacheSize: 2 (*5000) #buckets: 1 #reads: 1 #accesses: 26 hit-rate: 96.1538% <<< #Rows 4 #Rows 3 #Rows 2 #Rows 1 #Rows 0 >>> IncrementalStMan cache statistics: cacheSize: 2 (*5000) #buckets: 1 #reads: 1 #accesses: 11 hit-rate: 90.9091% <<< 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] #Rows 20 (0,0), (2,0), (2,0), (4,0), (4,0), (6,0), (6,0), (8,0), (8,0), (10,0), (10,0), (12,0), (12,0), (14,0), (14,0), (16,0), (16,0), (18,0), (18,0), (20,0) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4 , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][432, 433] [0, 1, 0][434, 435] [0, 2, 0][436, 437] [0, 0, 1][438, 439] [0, 1, 1][440, 441] [0, 2, 1][442, 443] [0, 0, 2][444, 445] [0, 1, 2][446, 447] [0, 2, 2][448, 449] [0, 0, 3][450, 451] [0, 1, 3][452, 453] [0, 2, 3][454, 455] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] >>> IncrementalStMan cache statistics: cacheSize: 2 (*5000) #buckets: 1 #reads: 1 #accesses: 125 hit-rate: 99.2% <<< #Rows 10 >>> IncrementalStMan cache statistics: cacheSize: 2 (*5000) #buckets: 1 #reads: 1 #accesses: 53 hit-rate: 98.1132% <<< 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] #Rows 20 (0,0), (2,0), (2,0), (4,0), (4,0), (6,0), (6,0), (8,0), (8,0), (10,0), (10,0), (12,0), (12,0), (14,0), (14,0), (16,0), (16,0), (18,0), (18,0), (20,0) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4 , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][432, 433] [0, 1, 0][434, 435] [0, 2, 0][436, 437] [0, 0, 1][438, 439] [0, 1, 1][440, 441] [0, 2, 1][442, 443] [0, 0, 2][444, 445] [0, 1, 2][446, 447] [0, 2, 2][448, 449] [0, 0, 3][450, 451] [0, 1, 3][452, 453] [0, 2, 3][454, 455] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] >>> IncrementalStMan cache statistics: cacheSize: 2 (*5000) #buckets: 1 #reads: 1 #accesses: 125 hit-rate: 99.2% <<< 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] ArrayColumn::put for row 0 in column arr1: Table array conformance error (shape=[10], expected [2, 3, 4]) ArrayColumn::put for row 0 in column arr7: Table array conformance error (shape=[10], expected [5, 7, 11]) #Rows 20 (0,0), (2,0), (2,0), (4,0), (4,0), (6,0), (6,0), (8,0), (8,0), (10,0), (10,0), (12,0), (12,0), (14,0), (14,0), (16,0), (16,0), (18,0), (18,0), (20,0) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4 , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][432, 433] [0, 1, 0][434, 435] [0, 2, 0][436, 437] [0, 0, 1][438, 439] [0, 1, 1][440, 441] [0, 2, 1][442, 443] [0, 0, 2][444, 445] [0, 1, 2][446, 447] [0, 2, 2][448, 449] [0, 0, 3][450, 451] [0, 1, 3][452, 453] [0, 2, 3][454, 455] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] >>> IncrementalStMan cache statistics: cacheSize: 2 (*5000) #buckets: 1 #reads: 1 #accesses: 125 hit-rate: 99.2% <<< #Rows 20 (0,0), (2,0), (2,0), (4,0), (4,0), (6,0), (6,0), (8,0), (8,0), (10,0), (10,0), (12,0), (12,0), (14,0), (14,0), (16,0), (16,0), (18,0), (18,0), (20,0) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4 , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][432, 433] [0, 1, 0][434, 435] [0, 2, 0][436, 437] [0, 0, 1][438, 439] [0, 1, 1][440, 441] [0, 2, 1][442, 443] [0, 0, 2][444, 445] [0, 1, 2][446, 447] [0, 2, 2][448, 449] [0, 0, 3][450, 451] [0, 1, 3][452, 453] [0, 2, 3][454, 455] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] >>> IncrementalStMan cache statistics: cacheSize: 2 (*5000) #buckets: 1 #reads: 1 #accesses: 146 hit-rate: 99.3151% <<< 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] #Rows 20 (0,0), (2,0), (2,0), (4,0), (4,0), (6,0), (6,0), (8,0), (8,0), (10,0), (10,0), (12,0), (12,0), (14,0), (14,0), (16,0), (16,0), (18,0), (18,0), (20,0) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4 , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][432, 433] [0, 1, 0][434, 435] [0, 2, 0][436, 437] [0, 0, 1][438, 439] [0, 1, 1][440, 441] [0, 2, 1][442, 443] [0, 0, 2][444, 445] [0, 1, 2][446, 447] [0, 2, 2][448, 449] [0, 0, 3][450, 451] [0, 1, 3][452, 453] [0, 2, 3][454, 455] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] >>> IncrementalStMan cache statistics: cacheSize: 2 (*163008) #buckets: 1 #reads: 1 #accesses: 125 hit-rate: 99.2% <<< #Rows 19 #Rows 18 #Rows 17 #Rows 16 #Rows 15 >>> IncrementalStMan cache statistics: cacheSize: 2 (*163008) #buckets: 1 #reads: 1 #accesses: 76 hit-rate: 98.6842% <<< #Rows 10 >>> IncrementalStMan cache statistics: cacheSize: 2 (*163008) #buckets: 1 #reads: 1 #accesses: 57 hit-rate: 98.2456% <<< #Rows 9 #Rows 8 #Rows 7 #Rows 6 #Rows 5 >>> IncrementalStMan cache statistics: cacheSize: 2 (*163008) #buckets: 1 #reads: 1 #accesses: 26 hit-rate: 96.1538% <<< #Rows 4 #Rows 3 #Rows 2 #Rows 1 #Rows 0 >>> IncrementalStMan cache statistics: cacheSize: 2 (*163008) #buckets: 1 #reads: 1 #accesses: 11 hit-rate: 90.9091% <<< 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] #Rows 20 (0,0), (2,0), (2,0), (4,0), (4,0), (6,0), (6,0), (8,0), (8,0), (10,0), (10,0), (12,0), (12,0), (14,0), (14,0), (16,0), (16,0), (18,0), (18,0), (20,0) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4 , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][432, 433] [0, 1, 0][434, 435] [0, 2, 0][436, 437] [0, 0, 1][438, 439] [0, 1, 1][440, 441] [0, 2, 1][442, 443] [0, 0, 2][444, 445] [0, 1, 2][446, 447] [0, 2, 2][448, 449] [0, 0, 3][450, 451] [0, 1, 3][452, 453] [0, 2, 3][454, 455] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] >>> IncrementalStMan cache statistics: cacheSize: 2 (*163008) #buckets: 1 #reads: 1 #accesses: 125 hit-rate: 99.2% <<< #Rows 10 >>> IncrementalStMan cache statistics: cacheSize: 2 (*163008) #buckets: 1 #reads: 1 #accesses: 53 hit-rate: 98.1132% <<< 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] #Rows 20 (0,0), (2,0), (2,0), (4,0), (4,0), (6,0), (6,0), (8,0), (8,0), (10,0), (10,0), (12,0), (12,0), (14,0), (14,0), (16,0), (16,0), (18,0), (18,0), (20,0) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4 , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][432, 433] [0, 1, 0][434, 435] [0, 2, 0][436, 437] [0, 0, 1][438, 439] [0, 1, 1][440, 441] [0, 2, 1][442, 443] [0, 0, 2][444, 445] [0, 1, 2][446, 447] [0, 2, 2][448, 449] [0, 0, 3][450, 451] [0, 1, 3][452, 453] [0, 2, 3][454, 455] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] >>> IncrementalStMan cache statistics: cacheSize: 2 (*163008) #buckets: 1 #reads: 1 #accesses: 125 hit-rate: 99.2% <<< 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z] arr4 = Axis Lengths: [1, 20] (NB: Matrix in Row/Column order) [z, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a] ArrayColumn::put for row 0 in column arr1: Table array conformance error (shape=[10], expected [2, 3, 4]) ArrayColumn::put for row 0 in column arr7: Table array conformance error (shape=[10], expected [5, 7, 11]) #Rows 20 (0,0), (2,0), (2,0), (4,0), (4,0), (6,0), (6,0), (8,0), (8,0), (10,0), (10,0), (12,0), (12,0), (14,0), (14,0), (16,0), (16,0), (18,0), (18,0), (20,0) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4 , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][432, 433] [0, 1, 0][434, 435] [0, 2, 0][436, 437] [0, 0, 1][438, 439] [0, 1, 1][440, 441] [0, 2, 1][442, 443] [0, 0, 2][444, 445] [0, 1, 2][446, 447] [0, 2, 2][448, 449] [0, 0, 3][450, 451] [0, 1, 3][452, 453] [0, 2, 3][454, 455] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] >>> IncrementalStMan cache statistics: cacheSize: 2 (*163008) #buckets: 1 #reads: 1 #accesses: 125 hit-rate: 99.2% <<< #Rows 20 (0,0), (2,0), (2,0), (4,0), (4,0), (6,0), (6,0), (8,0), (8,0), (10,0), (10,0), (12,0), (12,0), (14,0), (14,0), (16,0), (16,0), (18,0), (18,0), (20,0) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4 , , , , , , , , , , abca, abca, abca, abcaa, abcaa, abcaa, abcaaa, abcaaa, abcaaa, abcaaaa Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][432, 433] [0, 1, 0][434, 435] [0, 2, 0][436, 437] [0, 0, 1][438, 439] [0, 1, 1][440, 441] [0, 2, 1][442, 443] [0, 0, 2][444, 445] [0, 1, 2][446, 447] [0, 2, 2][448, 449] [0, 0, 3][450, 451] [0, 1, 3][452, 453] [0, 2, 3][454, 455] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][408, 409] [0, 1, 0][410, 411] [0, 2, 0][412, 413] [0, 0, 1][414, 415] [0, 1, 1][416, 417] [0, 2, 1][418, 419] [0, 0, 2][420, 421] [0, 1, 2][422, 423] [0, 2, 2][424, 425] [0, 0, 3][426, 427] [0, 1, 3][428, 429] [0, 2, 3][430, 431] >>> IncrementalStMan cache statistics: cacheSize: 2 (*163008) #buckets: 1 #reads: 1 #accesses: 146 hit-rate: 99.3151% <<< casacore-3.7.1/tables/DataMan/test/tIncrementalStMan.run000066400000000000000000000007521476623553700232040ustar00rootroot00000000000000#!/bin/sh #----------------------------------------------------------------------------- # Script to test tIncrementalStMan by running it in various ways. #============================================================================= $casa_checktool ./tIncrementalStMan 1000 $casa_checktool ./tIncrementalStMan 2000 $casa_checktool ./tIncrementalStMan 3000 $casa_checktool ./tIncrementalStMan 4000 $casa_checktool ./tIncrementalStMan 5000 $casa_checktool ./tIncrementalStMan 0 casacore-3.7.1/tables/DataMan/test/tMappedArrayEngine.cc000066400000000000000000000167651476623553700231270ustar00rootroot00000000000000//# tMappedArrayEngine.cc: Test program for class MappedArrayEngine //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Test program for class MappedArrayEngine // This program tests the virtual column engine MappedArrayEngine. // The results are written to stdout. The script executing this program, // compares the results with the reference output file. void a(); void b(); int main () { try { a(); b(); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } // First build a description. void a() { // First register the virtual column engine. MappedArrayEngine::registerClass(); // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.comment() = "A test of class TableDesc"; td.addColumn (ArrayColumnDesc ("target1")); td.addColumn (ArrayColumnDesc ("source1")); td.addColumn (ArrayColumnDesc ("target2")); td.addColumn (ArrayColumnDesc ("source2","", IPosition(3,2,3,4), ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc ("target3", "", IPosition(3,2,3,4), ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc ("source3", "", IPosition(3,2,3,4), ColumnDesc::Direct)); // Now create a new table from the description. SetupNewTable newtab("tMappedArrayEngine_tmp.data", td, Table::New); // Create the virtual column engine with the scale factors // and bind the columns to them. MappedArrayEngine engine1("source1", "target1"); MappedArrayEngine engine2("source2", "target2"); MappedArrayEngine engine3("source3", "target3"); newtab.bindColumn ("source1", engine1); newtab.bindColumn ("source2", engine2); newtab.bindColumn ("source3", engine3); Table tab(newtab, 10); // Fill the table via the virtual columns. ArrayColumn source1 (tab, "source1"); ArrayColumn source2 (tab, "source2"); ArrayColumn source3 (tab, "source3"); Cube arrd(IPosition(3,2,3,4)); Cube arrf(IPosition(3,2,3,4)); uInt i; i=2; for (uInt i2=0; i2<4; i2++) for (uInt i1=0; i1<3; i1++) for (uInt i0=0; i0<2; i0++) { arrd(i0,i1,i2) = Complex(i,i+1); arrf(i0,i1,i2) = i; i += 6; } for (i=0; i<10; i++) { source1.put (i, arrd); source2.put (i, arrf); source3.put (i, arrd + Complex(4,5)); arrd += Complex(6*arrd.nelements(), 6*arrd.nelements()); arrf += (float)(6*arrf.nelements()); } //# Do an erroneous thing. SetupNewTable newtab2("tMappedArrayEngine_tmp.dat2", td, Table::Scratch); newtab2.bindColumn ("source2", engine1); /// try { /// Table tab2(newtab2, 10); // bound to incorrect column /// } catch (std::exception x) { /// cout << x.what() << endl; /// } } void b() { // Read back the table. Table tab("tMappedArrayEngine_tmp.data"); ArrayColumn source1 (tab, "source1"); ArrayColumn source2 (tab, "source2"); ArrayColumn source3 (tab, "source3"); ArrayColumn target1 (tab, "target1"); ArrayColumn target2 (tab, "target2"); ArrayColumn target3 (tab, "target3"); Cube arri1(IPosition(3,2,3,4)); Cube arri3(IPosition(3,2,3,4)); Cube arrvali(IPosition(3,2,3,4)); Cube arrc2(IPosition(3,2,3,4)); Cube arrvalc(IPosition(3,2,3,4)); Cube arrd1(IPosition(3,2,3,4)); Cube arrd3(IPosition(3,2,3,4)); Cube arrvald(IPosition(3,2,3,4)); Cube arrf2(IPosition(3,2,3,4)); Cube arrvalf(IPosition(3,2,3,4)); Cube arrvalslice(arrvald(Slice(0,1),Slice(0,1,2),Slice(0,2,2))); Slice tmp; Slicer nslice (tmp, tmp, tmp, Slicer::endIsLength); Slicer nslice2(Slice(0,1), Slice(0,1,2), Slice(0,2,2), Slicer::endIsLength); uInt i=2; for (uInt i2=0; i2<4; i2++) for (uInt i1=0; i1<3; i1++) for (uInt i0=0; i0<2; i0++) { arrd1(i0,i1,i2) = Complex(i, i+1); arrf2(i0,i1,i2) = i; arrd3(i0,i1,i2) = Complex(i+4, i+6); arri1(i0,i1,i2) = DComplex(i, i+1); arrc2(i0,i1,i2) = i; arri3(i0,i1,i2) = DComplex(i+4, i+6); i+=6; } for (i=0; i<10; i++) { cout << "get row " << i << endl; source1.get (i, arrvald); if (!allEQ (arrvald, arrd1)) { cout << "error in source1 in row " << i << endl; cout << arrvald << arrd1; } target1.get (i, arrvali); if (!allEQ (arrvali, arri1)) { cout << "error in target1 in row " << i << endl; } source2.get (i, arrvalf); if (!allEQ (arrvalf, arrf2)) { cout << "error in source2 in row " << i << endl; } target2.get (i, arrvalc); if (!allEQ (arrvalc, arrc2)) { cout << "error in target2 in row " << i << endl; } source3.get (i, arrvald); if (!allEQ (arrvald, arrd3)) { cout << "error in source3 in row " << i << endl; } target3.get (i, arrvali); if (!allEQ (arrvali, arri3)) { cout << "error in target3 in row " << i << endl; } source1.getSlice (i, nslice, arrvald); if (!allEQ (arrvald, arrd1)) { cout << "error in source1 (entire slice) in row " << i << endl; } source1.getSlice (i, nslice2, arrvalslice); if (!allEQ (arrvald, arrd1)) { cout << "error in source1 (partial slice) in row " << i << endl; } arrd1 += Complex(6*arrd1.nelements(), 6*arrd1.nelements()); arrf2 += (float)(6*arrf2.nelements()); arrd3 += Complex(6*arrd3.nelements(), 6*arrd3.nelements()); arri1 += DComplex(6*arri1.nelements(), 6*arri1.nelements()); arrc2 += (uShort)(6*arrc2.nelements()); arri3 += DComplex(6*arri3.nelements(), 6*arri3.nelements()); } } casacore-3.7.1/tables/DataMan/test/tMappedArrayEngine.out000066400000000000000000000001441476623553700233310ustar00rootroot00000000000000get row 0 get row 1 get row 2 get row 3 get row 4 get row 5 get row 6 get row 7 get row 8 get row 9 casacore-3.7.1/tables/DataMan/test/tMemoryStMan.cc000066400000000000000000000426131476623553700217760ustar00rootroot00000000000000//# tMemoryStMan.cc: Test program for the MemoryStMan storage manager //# Copyright (C) 2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Test program for the MemoryStMan storage manager // // This program tests the MemoryStMan storage manager, especially the // get and put functions. // The results are written to stdout. The script executing this program, // compares the results with the reference output file. // Have a global area to keep the data to be inserted when // the table is reopened. Record theData; // (re) open and write a few rows // aMode == 0 open new // aMode == 1 reopen existing void init (uInt aMode); // reopen table, and throw away a row void deleteRow(const uInt aRow); // reopen table, and throw away a few rows void deleteRows(const Vector& aNrRows); // delete a Column void deleteColumn(const String& aColumn); // delete a column && put it back again void deleteAndRestore(); // add aColumn void addColumn(DataType aDataType); //add a few direct arrays void addDirectArrays(); //add an indirect string array void addIndStringArray(); //add an indirect array void addIndArray(); // show table info void info(const Table aTable); void saveData(const Table aTable); void restoreData(const Table aTable); // put/putColumn cache test void putColumnTest(); int main () { try { init (0); init (1); deleteRow (0); deleteRow (17); // delete and restore Column 1 (should use perfect fit) deleteAndRestore(); // putColumnTest(); // delete middle Column deleteColumn ("Col-2"); // add a Bool Column Should fit in freed space addColumn (TpBool); // add a DComplex Column Should use new index && space addColumn (TpDComplex); // delete first Column deleteColumn ("Col-1"); // delete last Column deleteColumn ("Col-3"); addDirectArrays (); addIndStringArray(); addIndArray (); Vector aNrRows(3); for (uInt i=0; i< 3; i++) { aNrRows(i) = i+3; } deleteRows (aNrRows); deleteColumn ("Col-7"); addColumn(TpString); // remove all remaining rows to check freebucket performance Vector aNewNrRows(15); for (uInt i=0; i< 15; i++) { aNewNrRows(i) = i; } deleteRows (aNewNrRows); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } void initArrays(Cube& arrf, Vector& arrdc, Cube& arrb) { // The static_cast is a workaround for an SGI compiler bug indgen (static_cast< Cube &>(arrf)); arrdc(0) = DComplex(1.2, 3.4); arrdc(1) = DComplex(-2.3, 5.6); IPosition shape(arrb.shape()); uInt n = 0; for (Int i=0; i ad(aTable,cdesc.name()); cout << ad.getColumn() << endl; } else { ScalarColumn aa(aTable,cdesc.name()); cout << aa.getColumn() << endl; } break; case TpBool: if (cdesc.isArray()) { ArrayColumn ad(aTable,cdesc.name()); cout << ad.getColumn() << endl; } else { ScalarColumn ab(aTable,cdesc.name()); cout << ab.getColumn() << endl; } break; case TpDComplex: if (cdesc.isArray()) { ArrayColumn ad(aTable,cdesc.name()); cout << ad.getColumn() << endl; } else { ScalarColumn ac(aTable,cdesc.name()); cout << ac.getColumn() << endl; } break; case TpFloat: if (cdesc.isArray()) { ArrayColumn ad(aTable,cdesc.name()); cout << ad.getColumn() << endl; } else { ScalarColumn ad(aTable,cdesc.name()); cout << ad.getColumn() << endl; } break; case TpString: if (cdesc.isArray()) { ArrayColumn ad(aTable,cdesc.name()); cout << ad.getColumn() << endl; } else { ScalarColumn ad(aTable,cdesc.name()); cout << ad.getColumn() << endl; } break; default: cout << "Sorry, datatype not implemented yet." << endl; } } } void saveData( const Table aTable) { theData = Record(); TableDesc tdesc = aTable.tableDesc(); for (uInt i=0; i col(aTable, cdesc.name()); theData.define (cdesc.name(), col.getColumn()); } else { ScalarColumn col(aTable, cdesc.name()); theData.define (cdesc.name(), col.getColumn()); } } else if (cdesc.dataType() == TpBool) { if (cdesc.isArray()) { ArrayColumn col(aTable, cdesc.name()); theData.define (cdesc.name(), col.getColumn()); } else { ScalarColumn col(aTable, cdesc.name()); theData.define (cdesc.name(), col.getColumn()); } } else if (cdesc.dataType() == TpDComplex) { if (cdesc.isArray()) { ArrayColumn col(aTable, cdesc.name()); theData.define (cdesc.name(), col.getColumn()); } else { ScalarColumn col(aTable, cdesc.name()); theData.define (cdesc.name(), col.getColumn()); } } else if (cdesc.dataType() == TpFloat) { if (cdesc.isArray()) { ArrayColumn col(aTable, cdesc.name()); theData.define (cdesc.name(), col.getColumn()); } else { ScalarColumn col(aTable, cdesc.name()); theData.define (cdesc.name(), col.getColumn()); } } else if (cdesc.dataType() == TpString) { if (cdesc.isArray()) { ArrayColumn col(aTable, cdesc.name()); theData.define (cdesc.name(), col.getColumn()); } else { ScalarColumn col(aTable, cdesc.name()); theData.define (cdesc.name(), col.getColumn()); } } else { cout << "Sorry, datatype not implemented yet." << endl; } } } void restoreData(const Table aTable) { for (uInt i=0; i& arr = theData.asArrayInt(i); if (arr.ndim() > 1) { ArrayColumn col(aTable, name); col.putColumn (arr); } else { ScalarColumn col(aTable, name); col.putColumn (arr); } break; } case TpArrayBool: { const Array& arr = theData.asArrayBool(i); if (arr.ndim() > 1) { ArrayColumn col(aTable, name); col.putColumn (arr); } else { ScalarColumn col(aTable, name); col.putColumn (arr); } break; } case TpArrayDComplex: { const Array& arr = theData.asArrayDComplex(i); if (arr.ndim() > 1) { ArrayColumn col(aTable, name); col.putColumn (arr); } else { ScalarColumn col(aTable, name); col.putColumn (arr); } break; } case TpArrayFloat: { const Array& arr = theData.asArrayFloat(i); if (arr.ndim() > 1) { ArrayColumn col(aTable, name); col.putColumn (arr); } else { ScalarColumn col(aTable, name); col.putColumn (arr); } break; } case TpArrayString: { const Array& arr = theData.asArrayString(i); if (arr.ndim() > 1) { ArrayColumn col(aTable, name); col.putColumn (arr); } else { ScalarColumn col(aTable, name); col.putColumn (arr); } break; } default: cout << "Sorry, datatype not implemented yet." << endl; } } } // First build a description. void init (uInt aMode) { Table aTable; if (aMode == 0) { DataManager::registerCtor ("MemoryStMan", MemoryStMan::makeObject); // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.comment() = "A test of class TableDesc"; td.addColumn (ScalarColumnDesc("Col-1")); td.addColumn (ScalarColumnDesc("Col-2")); td.addColumn (ScalarColumnDesc("Col-3")); // Now create a new table from the description. SetupNewTable aNewTab("tMemoryStMan_tmp.data", td, Table::New); // Create a storage manager for it. MemoryStMan aSm1 ("MSM"); aNewTab.bindAll (aSm1); aTable = Table (aNewTab, 10); } else { aTable = Table("tMemoryStMan_tmp.data", Table::Update); restoreData(aTable); } ScalarColumn aa(aTable,"Col-1"); ScalarColumn ab(aTable,"Col-2"); ScalarColumn ac(aTable,"Col-3"); // fill columns with data uInt i; uInt j = 0; for (i=0; i<10; i++) { if (aMode == 1) { aTable.addRow(); j=10; } DComplex a(i+j,(i+j)*2); aa.put(i+j,a); ab.put(i+j,i+j); Bool b; if ((i+j)%2 == 0) { b=True; } else { b=False; } ac.put(i+j,b); } if (aMode == 0) { cout << "after creation" << endl; } else { cout << "after reopening and adding 10 rows" << endl; } // Print column data info(aTable); saveData(aTable); } void deleteRow(const uInt aRow) { Table aTable = Table("tMemoryStMan_tmp.data", Table::Update); restoreData(aTable); ScalarColumn aa(aTable,"Col-1"); // Make sure ColumnCache is filled. aa(0); aTable.removeRow(aRow); cout << "after removing row: " << aRow << endl; // Print column data info(aTable); saveData(aTable); } void deleteRows(const Vector& aNrRows) { Table aTable = Table("tMemoryStMan_tmp.data", Table::Update); restoreData(aTable); ScalarColumn ae(aTable,"Col-5"); // Make sure ColumnCache is filled. ae(0); aTable.removeRow(aNrRows); cout << "after removing several rows at once: " << endl; // Print column data info(aTable); saveData(aTable); } void deleteColumn(const String& aColumn) { Table aTable = Table("tMemoryStMan_tmp.data", Table::Update); restoreData(aTable); cout << "Try to remove Column:" << aColumn << endl; aTable.removeColumn(aColumn); cout << "After removing Column: " << aColumn << endl; // Print column data info(aTable); saveData(aTable); } void deleteAndRestore() { Table aTable = Table("tMemoryStMan_tmp.data", Table::Update); restoreData(aTable); ScalarColumn ac(aTable,"Col-1"); cout << "Try to remove Column 1 after saving the contents" << endl; Vector save = ac.getColumn(); aTable.removeColumn("Col-1"); cout << "After removing Column 1" << endl; // Print column data info(aTable); cout << "Try to restore Column 1" << endl; aTable.addColumn(ScalarColumnDesc("Col-1")); if (aTable.tableDesc().isColumn("Col-1")) { ac.attach(aTable,"Col-1"); } // refill again ac.putColumn(save); // Print column data cout << "After restoring Column1:" << endl; info(aTable); saveData(aTable); } void addColumn(DataType aDataType) { Table aTable = Table("tMemoryStMan_tmp.data", Table::Update); restoreData(aTable); ScalarColumn ad; ScalarColumn ae; ScalarColumn aj; switch (aDataType) { case TpBool: cout << "Try to add Column: Col-4 and fill it. "<< endl << "It Should be using space just freed up" << endl; aTable.addColumn(ScalarColumnDesc("Col-4")); if (aTable.tableDesc().isColumn("Col-4")) { ad.attach(aTable,"Col-4"); } // fill new column with data uInt i; Bool b; for (i=0; i("Col-5")); if (aTable.tableDesc().isColumn("Col-5")) { ae.attach(aTable,"Col-5"); } // fill new column with data for (uInt i=0; i("Col-10")); if (aTable.tableDesc().isColumn("Col-10")) { aj.attach(aTable,"Col-10"); } String aString("String-1"); // fill new column with data for (uInt i=0; i af; ArrayColumn ag; ArrayColumn ah; cout << "Trying to add a few Direct Array Columns." << endl; aTable.addColumn(ArrayColumnDesc("Col-6", IPosition(3,2,3,1), ColumnDesc::Direct)); aTable.addColumn(ArrayColumnDesc("Col-7", IPosition(1,2), ColumnDesc::Direct)); aTable.addColumn(ArrayColumnDesc("Col-8", IPosition(3,5,7,1), ColumnDesc::Direct)); Cube arrf(IPosition(3,2,3,1)); Vector arrdc(2); Cube arrb(IPosition(3,5,7,1)); initArrays (arrf, arrdc, arrb); if (aTable.tableDesc().isColumn("Col-6")) { af.attach(aTable,"Col-6"); } if (aTable.tableDesc().isColumn("Col-7")) { ag.attach(aTable,"Col-7"); } if (aTable.tableDesc().isColumn("Col-8")) { ah.attach(aTable,"Col-8"); } for (uInt i=0; i ai; cout << "Trying to add an indirect String Array Column." << endl; aTable.addColumn(ArrayColumnDesc("Col-9")); Vector arrs(5); arrs(0)="Start-1"; arrs(1)="Start-2"; arrs(2)="Start-3"; arrs(3)="Start-4"; arrs(4)="Start-5"; if (aTable.tableDesc().isColumn("Col-9")) { ai.attach(aTable,"Col-9"); } for (uInt i=0; i ak; cout << "Trying to add an indirect Array Column." << endl; aTable.addColumn(ArrayColumnDesc("Col-11")); Vector arrs(5); arrs(0)=1; arrs(1)=2; arrs(2)=3; arrs(3)=4; arrs(4)=5; if (aTable.tableDesc().isColumn("Col-11")) { ak.attach(aTable,"Col-11"); } for (uInt i=0; i ab(aTable,"Col-2"); // put value 3 in rownr 5 ab.put(5,3); AlwaysAssertExit(ab(5) == 3); // put value 4 in column ab.putColumn(ab.getColumn() + 1); AlwaysAssertExit (ab(5) == 4); saveData(aTable); } casacore-3.7.1/tables/DataMan/test/tMemoryStMan.out000066400000000000000000001354711476623553700222250ustar00rootroot00000000000000after creation Col-1: DComplex [(0,0), (1,2), (2,4), (3,6), (4,8), (5,10), (6,12), (7,14), (8,16), (9,18)] Col-2: Int [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Col-3: Bool [1, 0, 1, 0, 1, 0, 1, 0, 1, 0] after reopening and adding 10 rows Col-1: DComplex [(0,0), (1,2), (2,4), (3,6), (4,8), (5,10), (6,12), (7,14), (8,16), (9,18), (10,20), (11,22), (12,24), (13,26), (14,28), (15,30), (16,32), (17,34), (18,36), (19,38)] Col-2: Int [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] Col-3: Bool [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0] after removing row: 0 Col-1: DComplex [(1,2), (2,4), (3,6), (4,8), (5,10), (6,12), (7,14), (8,16), (9,18), (10,20), (11,22), (12,24), (13,26), (14,28), (15,30), (16,32), (17,34), (18,36), (19,38)] Col-2: Int [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] Col-3: Bool [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0] after removing row: 17 Col-1: DComplex [(1,2), (2,4), (3,6), (4,8), (5,10), (6,12), (7,14), (8,16), (9,18), (10,20), (11,22), (12,24), (13,26), (14,28), (15,30), (16,32), (17,34), (19,38)] Col-2: Int [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 19] Col-3: Bool [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0] Try to remove Column 1 after saving the contents After removing Column 1 Col-2: Int [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 19] Col-3: Bool [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0] Try to restore Column 1 After restoring Column1: Col-2: Int [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 19] Col-3: Bool [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0] Col-1: DComplex [(1,2), (2,4), (3,6), (4,8), (5,10), (6,12), (7,14), (8,16), (9,18), (10,20), (11,22), (12,24), (13,26), (14,28), (15,30), (16,32), (17,34), (19,38)] Try to remove Column:Col-2 After removing Column: Col-2 Col-3: Bool [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0] Col-1: DComplex [(1,2), (2,4), (3,6), (4,8), (5,10), (6,12), (7,14), (8,16), (9,18), (10,20), (11,22), (12,24), (13,26), (14,28), (15,30), (16,32), (17,34), (19,38)] Try to add Column: Col-4 and fill it. It Should be using space just freed up Col-3: Bool [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0] Col-1: DComplex [(1,2), (2,4), (3,6), (4,8), (5,10), (6,12), (7,14), (8,16), (9,18), (10,20), (11,22), (12,24), (13,26), (14,28), (15,30), (16,32), (17,34), (19,38)] Col-4: Bool [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] Try to add Column: Col-5 and fill it. It should make a new index because there's not enough free space Col-3: Bool [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0] Col-1: DComplex [(1,2), (2,4), (3,6), (4,8), (5,10), (6,12), (7,14), (8,16), (9,18), (10,20), (11,22), (12,24), (13,26), (14,28), (15,30), (16,32), (17,34), (19,38)] Col-4: Bool [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] Col-5: DComplex [(18,0), (17,1), (16,2), (15,3), (14,4), (13,5), (12,6), (11,7), (10,8), (9,9), (8,10), (7,11), (6,12), (5,13), (4,14), (3,15), (2,16), (1,17)] Try to remove Column:Col-1 After removing Column: Col-1 Col-3: Bool [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0] Col-4: Bool [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] Col-5: DComplex [(18,0), (17,1), (16,2), (15,3), (14,4), (13,5), (12,6), (11,7), (10,8), (9,9), (8,10), (7,11), (6,12), (5,13), (4,14), (3,15), (2,16), (1,17)] Try to remove Column:Col-3 After removing Column: Col-3 Col-4: Bool [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] Col-5: DComplex [(18,0), (17,1), (16,2), (15,3), (14,4), (13,5), (12,6), (11,7), (10,8), (9,9), (8,10), (7,11), (6,12), (5,13), (4,14), (3,15), (2,16), (1,17)] Trying to add a few Direct Array Columns. Col-4: Bool [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] Col-5: DComplex [(18,0), (17,1), (16,2), (15,3), (14,4), (13,5), (12,6), (11,7), (10,8), (9,9), (8,10), (7,11), (6,12), (5,13), (4,14), (3,15), (2,16), (1,17)] Col-6: float Ndim=4 Axis Lengths: [2, 3, 1, 18] [0, 0, 0, 0][0, 1] [0, 1, 0, 0][2, 3] [0, 2, 0, 0][4, 5] [0, 0, 0, 1][6, 7] [0, 1, 0, 1][8, 9] [0, 2, 0, 1][10, 11] [0, 0, 0, 2][12, 13] [0, 1, 0, 2][14, 15] [0, 2, 0, 2][16, 17] [0, 0, 0, 3][18, 19] [0, 1, 0, 3][20, 21] [0, 2, 0, 3][22, 23] [0, 0, 0, 4][24, 25] [0, 1, 0, 4][26, 27] [0, 2, 0, 4][28, 29] [0, 0, 0, 5][30, 31] [0, 1, 0, 5][32, 33] [0, 2, 0, 5][34, 35] [0, 0, 0, 6][36, 37] [0, 1, 0, 6][38, 39] [0, 2, 0, 6][40, 41] [0, 0, 0, 7][42, 43] [0, 1, 0, 7][44, 45] [0, 2, 0, 7][46, 47] [0, 0, 0, 8][48, 49] [0, 1, 0, 8][50, 51] [0, 2, 0, 8][52, 53] [0, 0, 0, 9][54, 55] [0, 1, 0, 9][56, 57] [0, 2, 0, 9][58, 59] [0, 0, 0, 10][60, 61] [0, 1, 0, 10][62, 63] [0, 2, 0, 10][64, 65] [0, 0, 0, 11][66, 67] [0, 1, 0, 11][68, 69] [0, 2, 0, 11][70, 71] [0, 0, 0, 12][72, 73] [0, 1, 0, 12][74, 75] [0, 2, 0, 12][76, 77] [0, 0, 0, 13][78, 79] [0, 1, 0, 13][80, 81] [0, 2, 0, 13][82, 83] [0, 0, 0, 14][84, 85] [0, 1, 0, 14][86, 87] [0, 2, 0, 14][88, 89] [0, 0, 0, 15][90, 91] [0, 1, 0, 15][92, 93] [0, 2, 0, 15][94, 95] [0, 0, 0, 16][96, 97] [0, 1, 0, 16][98, 99] [0, 2, 0, 16][100, 101] [0, 0, 0, 17][102, 103] [0, 1, 0, 17][104, 105] [0, 2, 0, 17][106, 107] Col-7: DComplex Axis Lengths: [2, 18] (NB: Matrix in Row/Column order) [(1.2,3.4), (3.2,6.4), (5.2,9.4), (7.2,12.4), (9.2,15.4), (11.2,18.4), (13.2,21.4), (15.2,24.4), (17.2,27.4), (19.2,30.4), (21.2,33.4), (23.2,36.4), (25.2,39.4), (27.2,42.4), (29.2,45.4), (31.2,48.4), (33.2,51.4), (35.2,54.4) (-2.3,5.6), (-0.3,8.6), (1.7,11.6), (3.7,14.6), (5.7,17.6), (7.7,20.6), (9.7,23.6), (11.7,26.6), (13.7,29.6), (15.7,32.6), (17.7,35.6), (19.7,38.6), (21.7,41.6), (23.7,44.6), (25.7,47.6), (27.7,50.6), (29.7,53.6), (31.7,56.6)] Col-8: Bool Ndim=4 Axis Lengths: [5, 7, 1, 18] [0, 0, 0, 0][0, 0, 1, 0, 0] [0, 1, 0, 0][1, 0, 0, 1, 0] [0, 2, 0, 0][0, 1, 0, 0, 1] [0, 3, 0, 0][0, 0, 1, 0, 0] [0, 4, 0, 0][1, 0, 0, 1, 0] [0, 5, 0, 0][0, 1, 0, 0, 1] [0, 6, 0, 0][0, 0, 1, 0, 0] [0, 0, 0, 1][0, 0, 1, 0, 0] [0, 1, 0, 1][1, 0, 0, 1, 0] [0, 2, 0, 1][0, 1, 0, 0, 1] [0, 3, 0, 1][0, 0, 1, 0, 0] [0, 4, 0, 1][1, 0, 0, 1, 0] [0, 5, 0, 1][0, 1, 0, 0, 1] [0, 6, 0, 1][0, 0, 1, 0, 0] [0, 0, 0, 2][0, 0, 1, 0, 0] [0, 1, 0, 2][1, 0, 0, 1, 0] [0, 2, 0, 2][0, 1, 0, 0, 1] [0, 3, 0, 2][0, 0, 1, 0, 0] [0, 4, 0, 2][1, 0, 0, 1, 0] [0, 5, 0, 2][0, 1, 0, 0, 1] [0, 6, 0, 2][0, 0, 1, 0, 0] [0, 0, 0, 3][0, 0, 1, 0, 0] [0, 1, 0, 3][1, 0, 0, 1, 0] [0, 2, 0, 3][0, 1, 0, 0, 1] [0, 3, 0, 3][0, 0, 1, 0, 0] [0, 4, 0, 3][1, 0, 0, 1, 0] [0, 5, 0, 3][0, 1, 0, 0, 1] [0, 6, 0, 3][0, 0, 1, 0, 0] [0, 0, 0, 4][0, 0, 1, 0, 0] [0, 1, 0, 4][1, 0, 0, 1, 0] [0, 2, 0, 4][0, 1, 0, 0, 1] [0, 3, 0, 4][0, 0, 1, 0, 0] [0, 4, 0, 4][1, 0, 0, 1, 0] [0, 5, 0, 4][0, 1, 0, 0, 1] [0, 6, 0, 4][0, 0, 1, 0, 0] [0, 0, 0, 5][0, 0, 1, 0, 0] [0, 1, 0, 5][1, 0, 0, 1, 0] [0, 2, 0, 5][0, 1, 0, 0, 1] [0, 3, 0, 5][0, 0, 1, 0, 0] [0, 4, 0, 5][1, 0, 0, 1, 0] [0, 5, 0, 5][0, 1, 0, 0, 1] [0, 6, 0, 5][0, 0, 1, 0, 0] [0, 0, 0, 6][0, 0, 1, 0, 0] [0, 1, 0, 6][1, 0, 0, 1, 0] [0, 2, 0, 6][0, 1, 0, 0, 1] [0, 3, 0, 6][0, 0, 1, 0, 0] [0, 4, 0, 6][1, 0, 0, 1, 0] [0, 5, 0, 6][0, 1, 0, 0, 1] [0, 6, 0, 6][0, 0, 1, 0, 0] [0, 0, 0, 7][0, 0, 1, 0, 0] [0, 1, 0, 7][1, 0, 0, 1, 0] [0, 2, 0, 7][0, 1, 0, 0, 1] [0, 3, 0, 7][0, 0, 1, 0, 0] [0, 4, 0, 7][1, 0, 0, 1, 0] [0, 5, 0, 7][0, 1, 0, 0, 1] [0, 6, 0, 7][0, 0, 1, 0, 0] [0, 0, 0, 8][0, 0, 1, 0, 0] [0, 1, 0, 8][1, 0, 0, 1, 0] [0, 2, 0, 8][0, 1, 0, 0, 1] [0, 3, 0, 8][0, 0, 1, 0, 0] [0, 4, 0, 8][1, 0, 0, 1, 0] [0, 5, 0, 8][0, 1, 0, 0, 1] [0, 6, 0, 8][0, 0, 1, 0, 0] [0, 0, 0, 9][0, 0, 1, 0, 0] [0, 1, 0, 9][1, 0, 0, 1, 0] [0, 2, 0, 9][0, 1, 0, 0, 1] [0, 3, 0, 9][0, 0, 1, 0, 0] [0, 4, 0, 9][1, 0, 0, 1, 0] [0, 5, 0, 9][0, 1, 0, 0, 1] [0, 6, 0, 9][0, 0, 1, 0, 0] [0, 0, 0, 10][0, 0, 1, 0, 0] [0, 1, 0, 10][1, 0, 0, 1, 0] [0, 2, 0, 10][0, 1, 0, 0, 1] [0, 3, 0, 10][0, 0, 1, 0, 0] [0, 4, 0, 10][1, 0, 0, 1, 0] [0, 5, 0, 10][0, 1, 0, 0, 1] [0, 6, 0, 10][0, 0, 1, 0, 0] [0, 0, 0, 11][0, 0, 1, 0, 0] [0, 1, 0, 11][1, 0, 0, 1, 0] [0, 2, 0, 11][0, 1, 0, 0, 1] [0, 3, 0, 11][0, 0, 1, 0, 0] [0, 4, 0, 11][1, 0, 0, 1, 0] [0, 5, 0, 11][0, 1, 0, 0, 1] [0, 6, 0, 11][0, 0, 1, 0, 0] [0, 0, 0, 12][0, 0, 1, 0, 0] [0, 1, 0, 12][1, 0, 0, 1, 0] [0, 2, 0, 12][0, 1, 0, 0, 1] [0, 3, 0, 12][0, 0, 1, 0, 0] [0, 4, 0, 12][1, 0, 0, 1, 0] [0, 5, 0, 12][0, 1, 0, 0, 1] [0, 6, 0, 12][0, 0, 1, 0, 0] [0, 0, 0, 13][0, 0, 1, 0, 0] [0, 1, 0, 13][1, 0, 0, 1, 0] [0, 2, 0, 13][0, 1, 0, 0, 1] [0, 3, 0, 13][0, 0, 1, 0, 0] [0, 4, 0, 13][1, 0, 0, 1, 0] [0, 5, 0, 13][0, 1, 0, 0, 1] [0, 6, 0, 13][0, 0, 1, 0, 0] [0, 0, 0, 14][0, 0, 1, 0, 0] [0, 1, 0, 14][1, 0, 0, 1, 0] [0, 2, 0, 14][0, 1, 0, 0, 1] [0, 3, 0, 14][0, 0, 1, 0, 0] [0, 4, 0, 14][1, 0, 0, 1, 0] [0, 5, 0, 14][0, 1, 0, 0, 1] [0, 6, 0, 14][0, 0, 1, 0, 0] [0, 0, 0, 15][0, 0, 1, 0, 0] [0, 1, 0, 15][1, 0, 0, 1, 0] [0, 2, 0, 15][0, 1, 0, 0, 1] [0, 3, 0, 15][0, 0, 1, 0, 0] [0, 4, 0, 15][1, 0, 0, 1, 0] [0, 5, 0, 15][0, 1, 0, 0, 1] [0, 6, 0, 15][0, 0, 1, 0, 0] [0, 0, 0, 16][0, 0, 1, 0, 0] [0, 1, 0, 16][1, 0, 0, 1, 0] [0, 2, 0, 16][0, 1, 0, 0, 1] [0, 3, 0, 16][0, 0, 1, 0, 0] [0, 4, 0, 16][1, 0, 0, 1, 0] [0, 5, 0, 16][0, 1, 0, 0, 1] [0, 6, 0, 16][0, 0, 1, 0, 0] [0, 0, 0, 17][0, 0, 1, 0, 0] [0, 1, 0, 17][1, 0, 0, 1, 0] [0, 2, 0, 17][0, 1, 0, 0, 1] [0, 3, 0, 17][0, 0, 1, 0, 0] [0, 4, 0, 17][1, 0, 0, 1, 0] [0, 5, 0, 17][0, 1, 0, 0, 1] [0, 6, 0, 17][0, 0, 1, 0, 0] Trying to add an indirect String Array Column. Col-4: Bool [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] Col-5: DComplex [(18,0), (17,1), (16,2), (15,3), (14,4), (13,5), (12,6), (11,7), (10,8), (9,9), (8,10), (7,11), (6,12), (5,13), (4,14), (3,15), (2,16), (1,17)] Col-6: float Ndim=4 Axis Lengths: [2, 3, 1, 18] [0, 0, 0, 0][0, 1] [0, 1, 0, 0][2, 3] [0, 2, 0, 0][4, 5] [0, 0, 0, 1][6, 7] [0, 1, 0, 1][8, 9] [0, 2, 0, 1][10, 11] [0, 0, 0, 2][12, 13] [0, 1, 0, 2][14, 15] [0, 2, 0, 2][16, 17] [0, 0, 0, 3][18, 19] [0, 1, 0, 3][20, 21] [0, 2, 0, 3][22, 23] [0, 0, 0, 4][24, 25] [0, 1, 0, 4][26, 27] [0, 2, 0, 4][28, 29] [0, 0, 0, 5][30, 31] [0, 1, 0, 5][32, 33] [0, 2, 0, 5][34, 35] [0, 0, 0, 6][36, 37] [0, 1, 0, 6][38, 39] [0, 2, 0, 6][40, 41] [0, 0, 0, 7][42, 43] [0, 1, 0, 7][44, 45] [0, 2, 0, 7][46, 47] [0, 0, 0, 8][48, 49] [0, 1, 0, 8][50, 51] [0, 2, 0, 8][52, 53] [0, 0, 0, 9][54, 55] [0, 1, 0, 9][56, 57] [0, 2, 0, 9][58, 59] [0, 0, 0, 10][60, 61] [0, 1, 0, 10][62, 63] [0, 2, 0, 10][64, 65] [0, 0, 0, 11][66, 67] [0, 1, 0, 11][68, 69] [0, 2, 0, 11][70, 71] [0, 0, 0, 12][72, 73] [0, 1, 0, 12][74, 75] [0, 2, 0, 12][76, 77] [0, 0, 0, 13][78, 79] [0, 1, 0, 13][80, 81] [0, 2, 0, 13][82, 83] [0, 0, 0, 14][84, 85] [0, 1, 0, 14][86, 87] [0, 2, 0, 14][88, 89] [0, 0, 0, 15][90, 91] [0, 1, 0, 15][92, 93] [0, 2, 0, 15][94, 95] [0, 0, 0, 16][96, 97] [0, 1, 0, 16][98, 99] [0, 2, 0, 16][100, 101] [0, 0, 0, 17][102, 103] [0, 1, 0, 17][104, 105] [0, 2, 0, 17][106, 107] Col-7: DComplex Axis Lengths: [2, 18] (NB: Matrix in Row/Column order) [(1.2,3.4), (3.2,6.4), (5.2,9.4), (7.2,12.4), (9.2,15.4), (11.2,18.4), (13.2,21.4), (15.2,24.4), (17.2,27.4), (19.2,30.4), (21.2,33.4), (23.2,36.4), (25.2,39.4), (27.2,42.4), (29.2,45.4), (31.2,48.4), (33.2,51.4), (35.2,54.4) (-2.3,5.6), (-0.3,8.6), (1.7,11.6), (3.7,14.6), (5.7,17.6), (7.7,20.6), (9.7,23.6), (11.7,26.6), (13.7,29.6), (15.7,32.6), (17.7,35.6), (19.7,38.6), (21.7,41.6), (23.7,44.6), (25.7,47.6), (27.7,50.6), (29.7,53.6), (31.7,56.6)] Col-8: Bool Ndim=4 Axis Lengths: [5, 7, 1, 18] [0, 0, 0, 0][0, 0, 1, 0, 0] [0, 1, 0, 0][1, 0, 0, 1, 0] [0, 2, 0, 0][0, 1, 0, 0, 1] [0, 3, 0, 0][0, 0, 1, 0, 0] [0, 4, 0, 0][1, 0, 0, 1, 0] [0, 5, 0, 0][0, 1, 0, 0, 1] [0, 6, 0, 0][0, 0, 1, 0, 0] [0, 0, 0, 1][0, 0, 1, 0, 0] [0, 1, 0, 1][1, 0, 0, 1, 0] [0, 2, 0, 1][0, 1, 0, 0, 1] [0, 3, 0, 1][0, 0, 1, 0, 0] [0, 4, 0, 1][1, 0, 0, 1, 0] [0, 5, 0, 1][0, 1, 0, 0, 1] [0, 6, 0, 1][0, 0, 1, 0, 0] [0, 0, 0, 2][0, 0, 1, 0, 0] [0, 1, 0, 2][1, 0, 0, 1, 0] [0, 2, 0, 2][0, 1, 0, 0, 1] [0, 3, 0, 2][0, 0, 1, 0, 0] [0, 4, 0, 2][1, 0, 0, 1, 0] [0, 5, 0, 2][0, 1, 0, 0, 1] [0, 6, 0, 2][0, 0, 1, 0, 0] [0, 0, 0, 3][0, 0, 1, 0, 0] [0, 1, 0, 3][1, 0, 0, 1, 0] [0, 2, 0, 3][0, 1, 0, 0, 1] [0, 3, 0, 3][0, 0, 1, 0, 0] [0, 4, 0, 3][1, 0, 0, 1, 0] [0, 5, 0, 3][0, 1, 0, 0, 1] [0, 6, 0, 3][0, 0, 1, 0, 0] [0, 0, 0, 4][0, 0, 1, 0, 0] [0, 1, 0, 4][1, 0, 0, 1, 0] [0, 2, 0, 4][0, 1, 0, 0, 1] [0, 3, 0, 4][0, 0, 1, 0, 0] [0, 4, 0, 4][1, 0, 0, 1, 0] [0, 5, 0, 4][0, 1, 0, 0, 1] [0, 6, 0, 4][0, 0, 1, 0, 0] [0, 0, 0, 5][0, 0, 1, 0, 0] [0, 1, 0, 5][1, 0, 0, 1, 0] [0, 2, 0, 5][0, 1, 0, 0, 1] [0, 3, 0, 5][0, 0, 1, 0, 0] [0, 4, 0, 5][1, 0, 0, 1, 0] [0, 5, 0, 5][0, 1, 0, 0, 1] [0, 6, 0, 5][0, 0, 1, 0, 0] [0, 0, 0, 6][0, 0, 1, 0, 0] [0, 1, 0, 6][1, 0, 0, 1, 0] [0, 2, 0, 6][0, 1, 0, 0, 1] [0, 3, 0, 6][0, 0, 1, 0, 0] [0, 4, 0, 6][1, 0, 0, 1, 0] [0, 5, 0, 6][0, 1, 0, 0, 1] [0, 6, 0, 6][0, 0, 1, 0, 0] [0, 0, 0, 7][0, 0, 1, 0, 0] [0, 1, 0, 7][1, 0, 0, 1, 0] [0, 2, 0, 7][0, 1, 0, 0, 1] [0, 3, 0, 7][0, 0, 1, 0, 0] [0, 4, 0, 7][1, 0, 0, 1, 0] [0, 5, 0, 7][0, 1, 0, 0, 1] [0, 6, 0, 7][0, 0, 1, 0, 0] [0, 0, 0, 8][0, 0, 1, 0, 0] [0, 1, 0, 8][1, 0, 0, 1, 0] [0, 2, 0, 8][0, 1, 0, 0, 1] [0, 3, 0, 8][0, 0, 1, 0, 0] [0, 4, 0, 8][1, 0, 0, 1, 0] [0, 5, 0, 8][0, 1, 0, 0, 1] [0, 6, 0, 8][0, 0, 1, 0, 0] [0, 0, 0, 9][0, 0, 1, 0, 0] [0, 1, 0, 9][1, 0, 0, 1, 0] [0, 2, 0, 9][0, 1, 0, 0, 1] [0, 3, 0, 9][0, 0, 1, 0, 0] [0, 4, 0, 9][1, 0, 0, 1, 0] [0, 5, 0, 9][0, 1, 0, 0, 1] [0, 6, 0, 9][0, 0, 1, 0, 0] [0, 0, 0, 10][0, 0, 1, 0, 0] [0, 1, 0, 10][1, 0, 0, 1, 0] [0, 2, 0, 10][0, 1, 0, 0, 1] [0, 3, 0, 10][0, 0, 1, 0, 0] [0, 4, 0, 10][1, 0, 0, 1, 0] [0, 5, 0, 10][0, 1, 0, 0, 1] [0, 6, 0, 10][0, 0, 1, 0, 0] [0, 0, 0, 11][0, 0, 1, 0, 0] [0, 1, 0, 11][1, 0, 0, 1, 0] [0, 2, 0, 11][0, 1, 0, 0, 1] [0, 3, 0, 11][0, 0, 1, 0, 0] [0, 4, 0, 11][1, 0, 0, 1, 0] [0, 5, 0, 11][0, 1, 0, 0, 1] [0, 6, 0, 11][0, 0, 1, 0, 0] [0, 0, 0, 12][0, 0, 1, 0, 0] [0, 1, 0, 12][1, 0, 0, 1, 0] [0, 2, 0, 12][0, 1, 0, 0, 1] [0, 3, 0, 12][0, 0, 1, 0, 0] [0, 4, 0, 12][1, 0, 0, 1, 0] [0, 5, 0, 12][0, 1, 0, 0, 1] [0, 6, 0, 12][0, 0, 1, 0, 0] [0, 0, 0, 13][0, 0, 1, 0, 0] [0, 1, 0, 13][1, 0, 0, 1, 0] [0, 2, 0, 13][0, 1, 0, 0, 1] [0, 3, 0, 13][0, 0, 1, 0, 0] [0, 4, 0, 13][1, 0, 0, 1, 0] [0, 5, 0, 13][0, 1, 0, 0, 1] [0, 6, 0, 13][0, 0, 1, 0, 0] [0, 0, 0, 14][0, 0, 1, 0, 0] [0, 1, 0, 14][1, 0, 0, 1, 0] [0, 2, 0, 14][0, 1, 0, 0, 1] [0, 3, 0, 14][0, 0, 1, 0, 0] [0, 4, 0, 14][1, 0, 0, 1, 0] [0, 5, 0, 14][0, 1, 0, 0, 1] [0, 6, 0, 14][0, 0, 1, 0, 0] [0, 0, 0, 15][0, 0, 1, 0, 0] [0, 1, 0, 15][1, 0, 0, 1, 0] [0, 2, 0, 15][0, 1, 0, 0, 1] [0, 3, 0, 15][0, 0, 1, 0, 0] [0, 4, 0, 15][1, 0, 0, 1, 0] [0, 5, 0, 15][0, 1, 0, 0, 1] [0, 6, 0, 15][0, 0, 1, 0, 0] [0, 0, 0, 16][0, 0, 1, 0, 0] [0, 1, 0, 16][1, 0, 0, 1, 0] [0, 2, 0, 16][0, 1, 0, 0, 1] [0, 3, 0, 16][0, 0, 1, 0, 0] [0, 4, 0, 16][1, 0, 0, 1, 0] [0, 5, 0, 16][0, 1, 0, 0, 1] [0, 6, 0, 16][0, 0, 1, 0, 0] [0, 0, 0, 17][0, 0, 1, 0, 0] [0, 1, 0, 17][1, 0, 0, 1, 0] [0, 2, 0, 17][0, 1, 0, 0, 1] [0, 3, 0, 17][0, 0, 1, 0, 0] [0, 4, 0, 17][1, 0, 0, 1, 0] [0, 5, 0, 17][0, 1, 0, 0, 1] [0, 6, 0, 17][0, 0, 1, 0, 0] Col-9: String Axis Lengths: [5, 18] (NB: Matrix in Row/Column order) [Start-1, Start-1 0, Start-1 0 1, Start-1 0 1 2, Start-1 0 1 2 3, Start-1 0 1 2 3 4, Start-1 0 1 2 3 4 5, Start-1 0 1 2 3 4 5 6, Start-1 0 1 2 3 4 5 6 7, Start-1 0 1 2 3 4 5 6 7 8, Start-1 0 1 2 3 4 5 6 7 8 9, Start-1 0 1 2 3 4 5 6 7 8 9 10, Start-1 0 1 2 3 4 5 6 7 8 9 10 11, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-2, Start-2 0, Start-2 0 1, Start-2 0 1 2, Start-2 0 1 2 3, Start-2 0 1 2 3 4, Start-2 0 1 2 3 4 5, Start-2 0 1 2 3 4 5 6, Start-2 0 1 2 3 4 5 6 7, Start-2 0 1 2 3 4 5 6 7 8, Start-2 0 1 2 3 4 5 6 7 8 9, Start-2 0 1 2 3 4 5 6 7 8 9 10, Start-2 0 1 2 3 4 5 6 7 8 9 10 11, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-3, Start-3 0, Start-3 0 1, Start-3 0 1 2, Start-3 0 1 2 3, Start-3 0 1 2 3 4, Start-3 0 1 2 3 4 5, Start-3 0 1 2 3 4 5 6, Start-3 0 1 2 3 4 5 6 7, Start-3 0 1 2 3 4 5 6 7 8, Start-3 0 1 2 3 4 5 6 7 8 9, Start-3 0 1 2 3 4 5 6 7 8 9 10, Start-3 0 1 2 3 4 5 6 7 8 9 10 11, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-4, Start-4 0, Start-4 0 1, Start-4 0 1 2, Start-4 0 1 2 3, Start-4 0 1 2 3 4, Start-4 0 1 2 3 4 5, Start-4 0 1 2 3 4 5 6, Start-4 0 1 2 3 4 5 6 7, Start-4 0 1 2 3 4 5 6 7 8, Start-4 0 1 2 3 4 5 6 7 8 9, Start-4 0 1 2 3 4 5 6 7 8 9 10, Start-4 0 1 2 3 4 5 6 7 8 9 10 11, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-5, Start-5 0, Start-5 0 1, Start-5 0 1 2, Start-5 0 1 2 3, Start-5 0 1 2 3 4, Start-5 0 1 2 3 4 5, Start-5 0 1 2 3 4 5 6, Start-5 0 1 2 3 4 5 6 7, Start-5 0 1 2 3 4 5 6 7 8, Start-5 0 1 2 3 4 5 6 7 8 9, Start-5 0 1 2 3 4 5 6 7 8 9 10, Start-5 0 1 2 3 4 5 6 7 8 9 10 11, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16] Trying to add an indirect Array Column. Col-4: Bool [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] Col-5: DComplex [(18,0), (17,1), (16,2), (15,3), (14,4), (13,5), (12,6), (11,7), (10,8), (9,9), (8,10), (7,11), (6,12), (5,13), (4,14), (3,15), (2,16), (1,17)] Col-6: float Ndim=4 Axis Lengths: [2, 3, 1, 18] [0, 0, 0, 0][0, 1] [0, 1, 0, 0][2, 3] [0, 2, 0, 0][4, 5] [0, 0, 0, 1][6, 7] [0, 1, 0, 1][8, 9] [0, 2, 0, 1][10, 11] [0, 0, 0, 2][12, 13] [0, 1, 0, 2][14, 15] [0, 2, 0, 2][16, 17] [0, 0, 0, 3][18, 19] [0, 1, 0, 3][20, 21] [0, 2, 0, 3][22, 23] [0, 0, 0, 4][24, 25] [0, 1, 0, 4][26, 27] [0, 2, 0, 4][28, 29] [0, 0, 0, 5][30, 31] [0, 1, 0, 5][32, 33] [0, 2, 0, 5][34, 35] [0, 0, 0, 6][36, 37] [0, 1, 0, 6][38, 39] [0, 2, 0, 6][40, 41] [0, 0, 0, 7][42, 43] [0, 1, 0, 7][44, 45] [0, 2, 0, 7][46, 47] [0, 0, 0, 8][48, 49] [0, 1, 0, 8][50, 51] [0, 2, 0, 8][52, 53] [0, 0, 0, 9][54, 55] [0, 1, 0, 9][56, 57] [0, 2, 0, 9][58, 59] [0, 0, 0, 10][60, 61] [0, 1, 0, 10][62, 63] [0, 2, 0, 10][64, 65] [0, 0, 0, 11][66, 67] [0, 1, 0, 11][68, 69] [0, 2, 0, 11][70, 71] [0, 0, 0, 12][72, 73] [0, 1, 0, 12][74, 75] [0, 2, 0, 12][76, 77] [0, 0, 0, 13][78, 79] [0, 1, 0, 13][80, 81] [0, 2, 0, 13][82, 83] [0, 0, 0, 14][84, 85] [0, 1, 0, 14][86, 87] [0, 2, 0, 14][88, 89] [0, 0, 0, 15][90, 91] [0, 1, 0, 15][92, 93] [0, 2, 0, 15][94, 95] [0, 0, 0, 16][96, 97] [0, 1, 0, 16][98, 99] [0, 2, 0, 16][100, 101] [0, 0, 0, 17][102, 103] [0, 1, 0, 17][104, 105] [0, 2, 0, 17][106, 107] Col-7: DComplex Axis Lengths: [2, 18] (NB: Matrix in Row/Column order) [(1.2,3.4), (3.2,6.4), (5.2,9.4), (7.2,12.4), (9.2,15.4), (11.2,18.4), (13.2,21.4), (15.2,24.4), (17.2,27.4), (19.2,30.4), (21.2,33.4), (23.2,36.4), (25.2,39.4), (27.2,42.4), (29.2,45.4), (31.2,48.4), (33.2,51.4), (35.2,54.4) (-2.3,5.6), (-0.3,8.6), (1.7,11.6), (3.7,14.6), (5.7,17.6), (7.7,20.6), (9.7,23.6), (11.7,26.6), (13.7,29.6), (15.7,32.6), (17.7,35.6), (19.7,38.6), (21.7,41.6), (23.7,44.6), (25.7,47.6), (27.7,50.6), (29.7,53.6), (31.7,56.6)] Col-8: Bool Ndim=4 Axis Lengths: [5, 7, 1, 18] [0, 0, 0, 0][0, 0, 1, 0, 0] [0, 1, 0, 0][1, 0, 0, 1, 0] [0, 2, 0, 0][0, 1, 0, 0, 1] [0, 3, 0, 0][0, 0, 1, 0, 0] [0, 4, 0, 0][1, 0, 0, 1, 0] [0, 5, 0, 0][0, 1, 0, 0, 1] [0, 6, 0, 0][0, 0, 1, 0, 0] [0, 0, 0, 1][0, 0, 1, 0, 0] [0, 1, 0, 1][1, 0, 0, 1, 0] [0, 2, 0, 1][0, 1, 0, 0, 1] [0, 3, 0, 1][0, 0, 1, 0, 0] [0, 4, 0, 1][1, 0, 0, 1, 0] [0, 5, 0, 1][0, 1, 0, 0, 1] [0, 6, 0, 1][0, 0, 1, 0, 0] [0, 0, 0, 2][0, 0, 1, 0, 0] [0, 1, 0, 2][1, 0, 0, 1, 0] [0, 2, 0, 2][0, 1, 0, 0, 1] [0, 3, 0, 2][0, 0, 1, 0, 0] [0, 4, 0, 2][1, 0, 0, 1, 0] [0, 5, 0, 2][0, 1, 0, 0, 1] [0, 6, 0, 2][0, 0, 1, 0, 0] [0, 0, 0, 3][0, 0, 1, 0, 0] [0, 1, 0, 3][1, 0, 0, 1, 0] [0, 2, 0, 3][0, 1, 0, 0, 1] [0, 3, 0, 3][0, 0, 1, 0, 0] [0, 4, 0, 3][1, 0, 0, 1, 0] [0, 5, 0, 3][0, 1, 0, 0, 1] [0, 6, 0, 3][0, 0, 1, 0, 0] [0, 0, 0, 4][0, 0, 1, 0, 0] [0, 1, 0, 4][1, 0, 0, 1, 0] [0, 2, 0, 4][0, 1, 0, 0, 1] [0, 3, 0, 4][0, 0, 1, 0, 0] [0, 4, 0, 4][1, 0, 0, 1, 0] [0, 5, 0, 4][0, 1, 0, 0, 1] [0, 6, 0, 4][0, 0, 1, 0, 0] [0, 0, 0, 5][0, 0, 1, 0, 0] [0, 1, 0, 5][1, 0, 0, 1, 0] [0, 2, 0, 5][0, 1, 0, 0, 1] [0, 3, 0, 5][0, 0, 1, 0, 0] [0, 4, 0, 5][1, 0, 0, 1, 0] [0, 5, 0, 5][0, 1, 0, 0, 1] [0, 6, 0, 5][0, 0, 1, 0, 0] [0, 0, 0, 6][0, 0, 1, 0, 0] [0, 1, 0, 6][1, 0, 0, 1, 0] [0, 2, 0, 6][0, 1, 0, 0, 1] [0, 3, 0, 6][0, 0, 1, 0, 0] [0, 4, 0, 6][1, 0, 0, 1, 0] [0, 5, 0, 6][0, 1, 0, 0, 1] [0, 6, 0, 6][0, 0, 1, 0, 0] [0, 0, 0, 7][0, 0, 1, 0, 0] [0, 1, 0, 7][1, 0, 0, 1, 0] [0, 2, 0, 7][0, 1, 0, 0, 1] [0, 3, 0, 7][0, 0, 1, 0, 0] [0, 4, 0, 7][1, 0, 0, 1, 0] [0, 5, 0, 7][0, 1, 0, 0, 1] [0, 6, 0, 7][0, 0, 1, 0, 0] [0, 0, 0, 8][0, 0, 1, 0, 0] [0, 1, 0, 8][1, 0, 0, 1, 0] [0, 2, 0, 8][0, 1, 0, 0, 1] [0, 3, 0, 8][0, 0, 1, 0, 0] [0, 4, 0, 8][1, 0, 0, 1, 0] [0, 5, 0, 8][0, 1, 0, 0, 1] [0, 6, 0, 8][0, 0, 1, 0, 0] [0, 0, 0, 9][0, 0, 1, 0, 0] [0, 1, 0, 9][1, 0, 0, 1, 0] [0, 2, 0, 9][0, 1, 0, 0, 1] [0, 3, 0, 9][0, 0, 1, 0, 0] [0, 4, 0, 9][1, 0, 0, 1, 0] [0, 5, 0, 9][0, 1, 0, 0, 1] [0, 6, 0, 9][0, 0, 1, 0, 0] [0, 0, 0, 10][0, 0, 1, 0, 0] [0, 1, 0, 10][1, 0, 0, 1, 0] [0, 2, 0, 10][0, 1, 0, 0, 1] [0, 3, 0, 10][0, 0, 1, 0, 0] [0, 4, 0, 10][1, 0, 0, 1, 0] [0, 5, 0, 10][0, 1, 0, 0, 1] [0, 6, 0, 10][0, 0, 1, 0, 0] [0, 0, 0, 11][0, 0, 1, 0, 0] [0, 1, 0, 11][1, 0, 0, 1, 0] [0, 2, 0, 11][0, 1, 0, 0, 1] [0, 3, 0, 11][0, 0, 1, 0, 0] [0, 4, 0, 11][1, 0, 0, 1, 0] [0, 5, 0, 11][0, 1, 0, 0, 1] [0, 6, 0, 11][0, 0, 1, 0, 0] [0, 0, 0, 12][0, 0, 1, 0, 0] [0, 1, 0, 12][1, 0, 0, 1, 0] [0, 2, 0, 12][0, 1, 0, 0, 1] [0, 3, 0, 12][0, 0, 1, 0, 0] [0, 4, 0, 12][1, 0, 0, 1, 0] [0, 5, 0, 12][0, 1, 0, 0, 1] [0, 6, 0, 12][0, 0, 1, 0, 0] [0, 0, 0, 13][0, 0, 1, 0, 0] [0, 1, 0, 13][1, 0, 0, 1, 0] [0, 2, 0, 13][0, 1, 0, 0, 1] [0, 3, 0, 13][0, 0, 1, 0, 0] [0, 4, 0, 13][1, 0, 0, 1, 0] [0, 5, 0, 13][0, 1, 0, 0, 1] [0, 6, 0, 13][0, 0, 1, 0, 0] [0, 0, 0, 14][0, 0, 1, 0, 0] [0, 1, 0, 14][1, 0, 0, 1, 0] [0, 2, 0, 14][0, 1, 0, 0, 1] [0, 3, 0, 14][0, 0, 1, 0, 0] [0, 4, 0, 14][1, 0, 0, 1, 0] [0, 5, 0, 14][0, 1, 0, 0, 1] [0, 6, 0, 14][0, 0, 1, 0, 0] [0, 0, 0, 15][0, 0, 1, 0, 0] [0, 1, 0, 15][1, 0, 0, 1, 0] [0, 2, 0, 15][0, 1, 0, 0, 1] [0, 3, 0, 15][0, 0, 1, 0, 0] [0, 4, 0, 15][1, 0, 0, 1, 0] [0, 5, 0, 15][0, 1, 0, 0, 1] [0, 6, 0, 15][0, 0, 1, 0, 0] [0, 0, 0, 16][0, 0, 1, 0, 0] [0, 1, 0, 16][1, 0, 0, 1, 0] [0, 2, 0, 16][0, 1, 0, 0, 1] [0, 3, 0, 16][0, 0, 1, 0, 0] [0, 4, 0, 16][1, 0, 0, 1, 0] [0, 5, 0, 16][0, 1, 0, 0, 1] [0, 6, 0, 16][0, 0, 1, 0, 0] [0, 0, 0, 17][0, 0, 1, 0, 0] [0, 1, 0, 17][1, 0, 0, 1, 0] [0, 2, 0, 17][0, 1, 0, 0, 1] [0, 3, 0, 17][0, 0, 1, 0, 0] [0, 4, 0, 17][1, 0, 0, 1, 0] [0, 5, 0, 17][0, 1, 0, 0, 1] [0, 6, 0, 17][0, 0, 1, 0, 0] Col-9: String Axis Lengths: [5, 18] (NB: Matrix in Row/Column order) [Start-1, Start-1 0, Start-1 0 1, Start-1 0 1 2, Start-1 0 1 2 3, Start-1 0 1 2 3 4, Start-1 0 1 2 3 4 5, Start-1 0 1 2 3 4 5 6, Start-1 0 1 2 3 4 5 6 7, Start-1 0 1 2 3 4 5 6 7 8, Start-1 0 1 2 3 4 5 6 7 8 9, Start-1 0 1 2 3 4 5 6 7 8 9 10, Start-1 0 1 2 3 4 5 6 7 8 9 10 11, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-2, Start-2 0, Start-2 0 1, Start-2 0 1 2, Start-2 0 1 2 3, Start-2 0 1 2 3 4, Start-2 0 1 2 3 4 5, Start-2 0 1 2 3 4 5 6, Start-2 0 1 2 3 4 5 6 7, Start-2 0 1 2 3 4 5 6 7 8, Start-2 0 1 2 3 4 5 6 7 8 9, Start-2 0 1 2 3 4 5 6 7 8 9 10, Start-2 0 1 2 3 4 5 6 7 8 9 10 11, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-3, Start-3 0, Start-3 0 1, Start-3 0 1 2, Start-3 0 1 2 3, Start-3 0 1 2 3 4, Start-3 0 1 2 3 4 5, Start-3 0 1 2 3 4 5 6, Start-3 0 1 2 3 4 5 6 7, Start-3 0 1 2 3 4 5 6 7 8, Start-3 0 1 2 3 4 5 6 7 8 9, Start-3 0 1 2 3 4 5 6 7 8 9 10, Start-3 0 1 2 3 4 5 6 7 8 9 10 11, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-4, Start-4 0, Start-4 0 1, Start-4 0 1 2, Start-4 0 1 2 3, Start-4 0 1 2 3 4, Start-4 0 1 2 3 4 5, Start-4 0 1 2 3 4 5 6, Start-4 0 1 2 3 4 5 6 7, Start-4 0 1 2 3 4 5 6 7 8, Start-4 0 1 2 3 4 5 6 7 8 9, Start-4 0 1 2 3 4 5 6 7 8 9 10, Start-4 0 1 2 3 4 5 6 7 8 9 10 11, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-5, Start-5 0, Start-5 0 1, Start-5 0 1 2, Start-5 0 1 2 3, Start-5 0 1 2 3 4, Start-5 0 1 2 3 4 5, Start-5 0 1 2 3 4 5 6, Start-5 0 1 2 3 4 5 6 7, Start-5 0 1 2 3 4 5 6 7 8, Start-5 0 1 2 3 4 5 6 7 8 9, Start-5 0 1 2 3 4 5 6 7 8 9 10, Start-5 0 1 2 3 4 5 6 7 8 9 10 11, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16] Col-11: Int Axis Lengths: [5, 18] (NB: Matrix in Row/Column order) [1, 1, 2, 4, 7, 11, 16, 22, 29, 37, 46, 56, 67, 79, 92, 106, 121, 137 2, 2, 3, 5, 8, 12, 17, 23, 30, 38, 47, 57, 68, 80, 93, 107, 122, 138 3, 3, 4, 6, 9, 13, 18, 24, 31, 39, 48, 58, 69, 81, 94, 108, 123, 139 4, 4, 5, 7, 10, 14, 19, 25, 32, 40, 49, 59, 70, 82, 95, 109, 124, 140 5, 5, 6, 8, 11, 15, 20, 26, 33, 41, 50, 60, 71, 83, 96, 110, 125, 141] after removing several rows at once: Col-4: Bool [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] Col-5: DComplex [(18,0), (17,1), (16,2), (12,6), (11,7), (10,8), (9,9), (8,10), (7,11), (6,12), (5,13), (4,14), (3,15), (2,16), (1,17)] Col-6: float Ndim=4 Axis Lengths: [2, 3, 1, 15] [0, 0, 0, 0][0, 1] [0, 1, 0, 0][2, 3] [0, 2, 0, 0][4, 5] [0, 0, 0, 1][6, 7] [0, 1, 0, 1][8, 9] [0, 2, 0, 1][10, 11] [0, 0, 0, 2][12, 13] [0, 1, 0, 2][14, 15] [0, 2, 0, 2][16, 17] [0, 0, 0, 3][36, 37] [0, 1, 0, 3][38, 39] [0, 2, 0, 3][40, 41] [0, 0, 0, 4][42, 43] [0, 1, 0, 4][44, 45] [0, 2, 0, 4][46, 47] [0, 0, 0, 5][48, 49] [0, 1, 0, 5][50, 51] [0, 2, 0, 5][52, 53] [0, 0, 0, 6][54, 55] [0, 1, 0, 6][56, 57] [0, 2, 0, 6][58, 59] [0, 0, 0, 7][60, 61] [0, 1, 0, 7][62, 63] [0, 2, 0, 7][64, 65] [0, 0, 0, 8][66, 67] [0, 1, 0, 8][68, 69] [0, 2, 0, 8][70, 71] [0, 0, 0, 9][72, 73] [0, 1, 0, 9][74, 75] [0, 2, 0, 9][76, 77] [0, 0, 0, 10][78, 79] [0, 1, 0, 10][80, 81] [0, 2, 0, 10][82, 83] [0, 0, 0, 11][84, 85] [0, 1, 0, 11][86, 87] [0, 2, 0, 11][88, 89] [0, 0, 0, 12][90, 91] [0, 1, 0, 12][92, 93] [0, 2, 0, 12][94, 95] [0, 0, 0, 13][96, 97] [0, 1, 0, 13][98, 99] [0, 2, 0, 13][100, 101] [0, 0, 0, 14][102, 103] [0, 1, 0, 14][104, 105] [0, 2, 0, 14][106, 107] Col-7: DComplex Axis Lengths: [2, 15] (NB: Matrix in Row/Column order) [(1.2,3.4), (3.2,6.4), (5.2,9.4), (13.2,21.4), (15.2,24.4), (17.2,27.4), (19.2,30.4), (21.2,33.4), (23.2,36.4), (25.2,39.4), (27.2,42.4), (29.2,45.4), (31.2,48.4), (33.2,51.4), (35.2,54.4) (-2.3,5.6), (-0.3,8.6), (1.7,11.6), (9.7,23.6), (11.7,26.6), (13.7,29.6), (15.7,32.6), (17.7,35.6), (19.7,38.6), (21.7,41.6), (23.7,44.6), (25.7,47.6), (27.7,50.6), (29.7,53.6), (31.7,56.6)] Col-8: Bool Ndim=4 Axis Lengths: [5, 7, 1, 15] [0, 0, 0, 0][0, 0, 1, 0, 0] [0, 1, 0, 0][1, 0, 0, 1, 0] [0, 2, 0, 0][0, 1, 0, 0, 1] [0, 3, 0, 0][0, 0, 1, 0, 0] [0, 4, 0, 0][1, 0, 0, 1, 0] [0, 5, 0, 0][0, 1, 0, 0, 1] [0, 6, 0, 0][0, 0, 1, 0, 0] [0, 0, 0, 1][0, 0, 1, 0, 0] [0, 1, 0, 1][1, 0, 0, 1, 0] [0, 2, 0, 1][0, 1, 0, 0, 1] [0, 3, 0, 1][0, 0, 1, 0, 0] [0, 4, 0, 1][1, 0, 0, 1, 0] [0, 5, 0, 1][0, 1, 0, 0, 1] [0, 6, 0, 1][0, 0, 1, 0, 0] [0, 0, 0, 2][0, 0, 1, 0, 0] [0, 1, 0, 2][1, 0, 0, 1, 0] [0, 2, 0, 2][0, 1, 0, 0, 1] [0, 3, 0, 2][0, 0, 1, 0, 0] [0, 4, 0, 2][1, 0, 0, 1, 0] [0, 5, 0, 2][0, 1, 0, 0, 1] [0, 6, 0, 2][0, 0, 1, 0, 0] [0, 0, 0, 3][0, 0, 1, 0, 0] [0, 1, 0, 3][1, 0, 0, 1, 0] [0, 2, 0, 3][0, 1, 0, 0, 1] [0, 3, 0, 3][0, 0, 1, 0, 0] [0, 4, 0, 3][1, 0, 0, 1, 0] [0, 5, 0, 3][0, 1, 0, 0, 1] [0, 6, 0, 3][0, 0, 1, 0, 0] [0, 0, 0, 4][0, 0, 1, 0, 0] [0, 1, 0, 4][1, 0, 0, 1, 0] [0, 2, 0, 4][0, 1, 0, 0, 1] [0, 3, 0, 4][0, 0, 1, 0, 0] [0, 4, 0, 4][1, 0, 0, 1, 0] [0, 5, 0, 4][0, 1, 0, 0, 1] [0, 6, 0, 4][0, 0, 1, 0, 0] [0, 0, 0, 5][0, 0, 1, 0, 0] [0, 1, 0, 5][1, 0, 0, 1, 0] [0, 2, 0, 5][0, 1, 0, 0, 1] [0, 3, 0, 5][0, 0, 1, 0, 0] [0, 4, 0, 5][1, 0, 0, 1, 0] [0, 5, 0, 5][0, 1, 0, 0, 1] [0, 6, 0, 5][0, 0, 1, 0, 0] [0, 0, 0, 6][0, 0, 1, 0, 0] [0, 1, 0, 6][1, 0, 0, 1, 0] [0, 2, 0, 6][0, 1, 0, 0, 1] [0, 3, 0, 6][0, 0, 1, 0, 0] [0, 4, 0, 6][1, 0, 0, 1, 0] [0, 5, 0, 6][0, 1, 0, 0, 1] [0, 6, 0, 6][0, 0, 1, 0, 0] [0, 0, 0, 7][0, 0, 1, 0, 0] [0, 1, 0, 7][1, 0, 0, 1, 0] [0, 2, 0, 7][0, 1, 0, 0, 1] [0, 3, 0, 7][0, 0, 1, 0, 0] [0, 4, 0, 7][1, 0, 0, 1, 0] [0, 5, 0, 7][0, 1, 0, 0, 1] [0, 6, 0, 7][0, 0, 1, 0, 0] [0, 0, 0, 8][0, 0, 1, 0, 0] [0, 1, 0, 8][1, 0, 0, 1, 0] [0, 2, 0, 8][0, 1, 0, 0, 1] [0, 3, 0, 8][0, 0, 1, 0, 0] [0, 4, 0, 8][1, 0, 0, 1, 0] [0, 5, 0, 8][0, 1, 0, 0, 1] [0, 6, 0, 8][0, 0, 1, 0, 0] [0, 0, 0, 9][0, 0, 1, 0, 0] [0, 1, 0, 9][1, 0, 0, 1, 0] [0, 2, 0, 9][0, 1, 0, 0, 1] [0, 3, 0, 9][0, 0, 1, 0, 0] [0, 4, 0, 9][1, 0, 0, 1, 0] [0, 5, 0, 9][0, 1, 0, 0, 1] [0, 6, 0, 9][0, 0, 1, 0, 0] [0, 0, 0, 10][0, 0, 1, 0, 0] [0, 1, 0, 10][1, 0, 0, 1, 0] [0, 2, 0, 10][0, 1, 0, 0, 1] [0, 3, 0, 10][0, 0, 1, 0, 0] [0, 4, 0, 10][1, 0, 0, 1, 0] [0, 5, 0, 10][0, 1, 0, 0, 1] [0, 6, 0, 10][0, 0, 1, 0, 0] [0, 0, 0, 11][0, 0, 1, 0, 0] [0, 1, 0, 11][1, 0, 0, 1, 0] [0, 2, 0, 11][0, 1, 0, 0, 1] [0, 3, 0, 11][0, 0, 1, 0, 0] [0, 4, 0, 11][1, 0, 0, 1, 0] [0, 5, 0, 11][0, 1, 0, 0, 1] [0, 6, 0, 11][0, 0, 1, 0, 0] [0, 0, 0, 12][0, 0, 1, 0, 0] [0, 1, 0, 12][1, 0, 0, 1, 0] [0, 2, 0, 12][0, 1, 0, 0, 1] [0, 3, 0, 12][0, 0, 1, 0, 0] [0, 4, 0, 12][1, 0, 0, 1, 0] [0, 5, 0, 12][0, 1, 0, 0, 1] [0, 6, 0, 12][0, 0, 1, 0, 0] [0, 0, 0, 13][0, 0, 1, 0, 0] [0, 1, 0, 13][1, 0, 0, 1, 0] [0, 2, 0, 13][0, 1, 0, 0, 1] [0, 3, 0, 13][0, 0, 1, 0, 0] [0, 4, 0, 13][1, 0, 0, 1, 0] [0, 5, 0, 13][0, 1, 0, 0, 1] [0, 6, 0, 13][0, 0, 1, 0, 0] [0, 0, 0, 14][0, 0, 1, 0, 0] [0, 1, 0, 14][1, 0, 0, 1, 0] [0, 2, 0, 14][0, 1, 0, 0, 1] [0, 3, 0, 14][0, 0, 1, 0, 0] [0, 4, 0, 14][1, 0, 0, 1, 0] [0, 5, 0, 14][0, 1, 0, 0, 1] [0, 6, 0, 14][0, 0, 1, 0, 0] Col-9: String Axis Lengths: [5, 15] (NB: Matrix in Row/Column order) [Start-1, Start-1 0, Start-1 0 1, Start-1 0 1 2 3 4 5, Start-1 0 1 2 3 4 5 6, Start-1 0 1 2 3 4 5 6 7, Start-1 0 1 2 3 4 5 6 7 8, Start-1 0 1 2 3 4 5 6 7 8 9, Start-1 0 1 2 3 4 5 6 7 8 9 10, Start-1 0 1 2 3 4 5 6 7 8 9 10 11, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-2, Start-2 0, Start-2 0 1, Start-2 0 1 2 3 4 5, Start-2 0 1 2 3 4 5 6, Start-2 0 1 2 3 4 5 6 7, Start-2 0 1 2 3 4 5 6 7 8, Start-2 0 1 2 3 4 5 6 7 8 9, Start-2 0 1 2 3 4 5 6 7 8 9 10, Start-2 0 1 2 3 4 5 6 7 8 9 10 11, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-3, Start-3 0, Start-3 0 1, Start-3 0 1 2 3 4 5, Start-3 0 1 2 3 4 5 6, Start-3 0 1 2 3 4 5 6 7, Start-3 0 1 2 3 4 5 6 7 8, Start-3 0 1 2 3 4 5 6 7 8 9, Start-3 0 1 2 3 4 5 6 7 8 9 10, Start-3 0 1 2 3 4 5 6 7 8 9 10 11, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-4, Start-4 0, Start-4 0 1, Start-4 0 1 2 3 4 5, Start-4 0 1 2 3 4 5 6, Start-4 0 1 2 3 4 5 6 7, Start-4 0 1 2 3 4 5 6 7 8, Start-4 0 1 2 3 4 5 6 7 8 9, Start-4 0 1 2 3 4 5 6 7 8 9 10, Start-4 0 1 2 3 4 5 6 7 8 9 10 11, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-5, Start-5 0, Start-5 0 1, Start-5 0 1 2 3 4 5, Start-5 0 1 2 3 4 5 6, Start-5 0 1 2 3 4 5 6 7, Start-5 0 1 2 3 4 5 6 7 8, Start-5 0 1 2 3 4 5 6 7 8 9, Start-5 0 1 2 3 4 5 6 7 8 9 10, Start-5 0 1 2 3 4 5 6 7 8 9 10 11, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16] Col-11: Int Axis Lengths: [5, 15] (NB: Matrix in Row/Column order) [1, 1, 2, 16, 22, 29, 37, 46, 56, 67, 79, 92, 106, 121, 137 2, 2, 3, 17, 23, 30, 38, 47, 57, 68, 80, 93, 107, 122, 138 3, 3, 4, 18, 24, 31, 39, 48, 58, 69, 81, 94, 108, 123, 139 4, 4, 5, 19, 25, 32, 40, 49, 59, 70, 82, 95, 109, 124, 140 5, 5, 6, 20, 26, 33, 41, 50, 60, 71, 83, 96, 110, 125, 141] Try to remove Column:Col-7 After removing Column: Col-7 Col-4: Bool [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] Col-5: DComplex [(18,0), (17,1), (16,2), (12,6), (11,7), (10,8), (9,9), (8,10), (7,11), (6,12), (5,13), (4,14), (3,15), (2,16), (1,17)] Col-6: float Ndim=4 Axis Lengths: [2, 3, 1, 15] [0, 0, 0, 0][0, 1] [0, 1, 0, 0][2, 3] [0, 2, 0, 0][4, 5] [0, 0, 0, 1][6, 7] [0, 1, 0, 1][8, 9] [0, 2, 0, 1][10, 11] [0, 0, 0, 2][12, 13] [0, 1, 0, 2][14, 15] [0, 2, 0, 2][16, 17] [0, 0, 0, 3][36, 37] [0, 1, 0, 3][38, 39] [0, 2, 0, 3][40, 41] [0, 0, 0, 4][42, 43] [0, 1, 0, 4][44, 45] [0, 2, 0, 4][46, 47] [0, 0, 0, 5][48, 49] [0, 1, 0, 5][50, 51] [0, 2, 0, 5][52, 53] [0, 0, 0, 6][54, 55] [0, 1, 0, 6][56, 57] [0, 2, 0, 6][58, 59] [0, 0, 0, 7][60, 61] [0, 1, 0, 7][62, 63] [0, 2, 0, 7][64, 65] [0, 0, 0, 8][66, 67] [0, 1, 0, 8][68, 69] [0, 2, 0, 8][70, 71] [0, 0, 0, 9][72, 73] [0, 1, 0, 9][74, 75] [0, 2, 0, 9][76, 77] [0, 0, 0, 10][78, 79] [0, 1, 0, 10][80, 81] [0, 2, 0, 10][82, 83] [0, 0, 0, 11][84, 85] [0, 1, 0, 11][86, 87] [0, 2, 0, 11][88, 89] [0, 0, 0, 12][90, 91] [0, 1, 0, 12][92, 93] [0, 2, 0, 12][94, 95] [0, 0, 0, 13][96, 97] [0, 1, 0, 13][98, 99] [0, 2, 0, 13][100, 101] [0, 0, 0, 14][102, 103] [0, 1, 0, 14][104, 105] [0, 2, 0, 14][106, 107] Col-8: Bool Ndim=4 Axis Lengths: [5, 7, 1, 15] [0, 0, 0, 0][0, 0, 1, 0, 0] [0, 1, 0, 0][1, 0, 0, 1, 0] [0, 2, 0, 0][0, 1, 0, 0, 1] [0, 3, 0, 0][0, 0, 1, 0, 0] [0, 4, 0, 0][1, 0, 0, 1, 0] [0, 5, 0, 0][0, 1, 0, 0, 1] [0, 6, 0, 0][0, 0, 1, 0, 0] [0, 0, 0, 1][0, 0, 1, 0, 0] [0, 1, 0, 1][1, 0, 0, 1, 0] [0, 2, 0, 1][0, 1, 0, 0, 1] [0, 3, 0, 1][0, 0, 1, 0, 0] [0, 4, 0, 1][1, 0, 0, 1, 0] [0, 5, 0, 1][0, 1, 0, 0, 1] [0, 6, 0, 1][0, 0, 1, 0, 0] [0, 0, 0, 2][0, 0, 1, 0, 0] [0, 1, 0, 2][1, 0, 0, 1, 0] [0, 2, 0, 2][0, 1, 0, 0, 1] [0, 3, 0, 2][0, 0, 1, 0, 0] [0, 4, 0, 2][1, 0, 0, 1, 0] [0, 5, 0, 2][0, 1, 0, 0, 1] [0, 6, 0, 2][0, 0, 1, 0, 0] [0, 0, 0, 3][0, 0, 1, 0, 0] [0, 1, 0, 3][1, 0, 0, 1, 0] [0, 2, 0, 3][0, 1, 0, 0, 1] [0, 3, 0, 3][0, 0, 1, 0, 0] [0, 4, 0, 3][1, 0, 0, 1, 0] [0, 5, 0, 3][0, 1, 0, 0, 1] [0, 6, 0, 3][0, 0, 1, 0, 0] [0, 0, 0, 4][0, 0, 1, 0, 0] [0, 1, 0, 4][1, 0, 0, 1, 0] [0, 2, 0, 4][0, 1, 0, 0, 1] [0, 3, 0, 4][0, 0, 1, 0, 0] [0, 4, 0, 4][1, 0, 0, 1, 0] [0, 5, 0, 4][0, 1, 0, 0, 1] [0, 6, 0, 4][0, 0, 1, 0, 0] [0, 0, 0, 5][0, 0, 1, 0, 0] [0, 1, 0, 5][1, 0, 0, 1, 0] [0, 2, 0, 5][0, 1, 0, 0, 1] [0, 3, 0, 5][0, 0, 1, 0, 0] [0, 4, 0, 5][1, 0, 0, 1, 0] [0, 5, 0, 5][0, 1, 0, 0, 1] [0, 6, 0, 5][0, 0, 1, 0, 0] [0, 0, 0, 6][0, 0, 1, 0, 0] [0, 1, 0, 6][1, 0, 0, 1, 0] [0, 2, 0, 6][0, 1, 0, 0, 1] [0, 3, 0, 6][0, 0, 1, 0, 0] [0, 4, 0, 6][1, 0, 0, 1, 0] [0, 5, 0, 6][0, 1, 0, 0, 1] [0, 6, 0, 6][0, 0, 1, 0, 0] [0, 0, 0, 7][0, 0, 1, 0, 0] [0, 1, 0, 7][1, 0, 0, 1, 0] [0, 2, 0, 7][0, 1, 0, 0, 1] [0, 3, 0, 7][0, 0, 1, 0, 0] [0, 4, 0, 7][1, 0, 0, 1, 0] [0, 5, 0, 7][0, 1, 0, 0, 1] [0, 6, 0, 7][0, 0, 1, 0, 0] [0, 0, 0, 8][0, 0, 1, 0, 0] [0, 1, 0, 8][1, 0, 0, 1, 0] [0, 2, 0, 8][0, 1, 0, 0, 1] [0, 3, 0, 8][0, 0, 1, 0, 0] [0, 4, 0, 8][1, 0, 0, 1, 0] [0, 5, 0, 8][0, 1, 0, 0, 1] [0, 6, 0, 8][0, 0, 1, 0, 0] [0, 0, 0, 9][0, 0, 1, 0, 0] [0, 1, 0, 9][1, 0, 0, 1, 0] [0, 2, 0, 9][0, 1, 0, 0, 1] [0, 3, 0, 9][0, 0, 1, 0, 0] [0, 4, 0, 9][1, 0, 0, 1, 0] [0, 5, 0, 9][0, 1, 0, 0, 1] [0, 6, 0, 9][0, 0, 1, 0, 0] [0, 0, 0, 10][0, 0, 1, 0, 0] [0, 1, 0, 10][1, 0, 0, 1, 0] [0, 2, 0, 10][0, 1, 0, 0, 1] [0, 3, 0, 10][0, 0, 1, 0, 0] [0, 4, 0, 10][1, 0, 0, 1, 0] [0, 5, 0, 10][0, 1, 0, 0, 1] [0, 6, 0, 10][0, 0, 1, 0, 0] [0, 0, 0, 11][0, 0, 1, 0, 0] [0, 1, 0, 11][1, 0, 0, 1, 0] [0, 2, 0, 11][0, 1, 0, 0, 1] [0, 3, 0, 11][0, 0, 1, 0, 0] [0, 4, 0, 11][1, 0, 0, 1, 0] [0, 5, 0, 11][0, 1, 0, 0, 1] [0, 6, 0, 11][0, 0, 1, 0, 0] [0, 0, 0, 12][0, 0, 1, 0, 0] [0, 1, 0, 12][1, 0, 0, 1, 0] [0, 2, 0, 12][0, 1, 0, 0, 1] [0, 3, 0, 12][0, 0, 1, 0, 0] [0, 4, 0, 12][1, 0, 0, 1, 0] [0, 5, 0, 12][0, 1, 0, 0, 1] [0, 6, 0, 12][0, 0, 1, 0, 0] [0, 0, 0, 13][0, 0, 1, 0, 0] [0, 1, 0, 13][1, 0, 0, 1, 0] [0, 2, 0, 13][0, 1, 0, 0, 1] [0, 3, 0, 13][0, 0, 1, 0, 0] [0, 4, 0, 13][1, 0, 0, 1, 0] [0, 5, 0, 13][0, 1, 0, 0, 1] [0, 6, 0, 13][0, 0, 1, 0, 0] [0, 0, 0, 14][0, 0, 1, 0, 0] [0, 1, 0, 14][1, 0, 0, 1, 0] [0, 2, 0, 14][0, 1, 0, 0, 1] [0, 3, 0, 14][0, 0, 1, 0, 0] [0, 4, 0, 14][1, 0, 0, 1, 0] [0, 5, 0, 14][0, 1, 0, 0, 1] [0, 6, 0, 14][0, 0, 1, 0, 0] Col-9: String Axis Lengths: [5, 15] (NB: Matrix in Row/Column order) [Start-1, Start-1 0, Start-1 0 1, Start-1 0 1 2 3 4 5, Start-1 0 1 2 3 4 5 6, Start-1 0 1 2 3 4 5 6 7, Start-1 0 1 2 3 4 5 6 7 8, Start-1 0 1 2 3 4 5 6 7 8 9, Start-1 0 1 2 3 4 5 6 7 8 9 10, Start-1 0 1 2 3 4 5 6 7 8 9 10 11, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-2, Start-2 0, Start-2 0 1, Start-2 0 1 2 3 4 5, Start-2 0 1 2 3 4 5 6, Start-2 0 1 2 3 4 5 6 7, Start-2 0 1 2 3 4 5 6 7 8, Start-2 0 1 2 3 4 5 6 7 8 9, Start-2 0 1 2 3 4 5 6 7 8 9 10, Start-2 0 1 2 3 4 5 6 7 8 9 10 11, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-3, Start-3 0, Start-3 0 1, Start-3 0 1 2 3 4 5, Start-3 0 1 2 3 4 5 6, Start-3 0 1 2 3 4 5 6 7, Start-3 0 1 2 3 4 5 6 7 8, Start-3 0 1 2 3 4 5 6 7 8 9, Start-3 0 1 2 3 4 5 6 7 8 9 10, Start-3 0 1 2 3 4 5 6 7 8 9 10 11, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-4, Start-4 0, Start-4 0 1, Start-4 0 1 2 3 4 5, Start-4 0 1 2 3 4 5 6, Start-4 0 1 2 3 4 5 6 7, Start-4 0 1 2 3 4 5 6 7 8, Start-4 0 1 2 3 4 5 6 7 8 9, Start-4 0 1 2 3 4 5 6 7 8 9 10, Start-4 0 1 2 3 4 5 6 7 8 9 10 11, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-5, Start-5 0, Start-5 0 1, Start-5 0 1 2 3 4 5, Start-5 0 1 2 3 4 5 6, Start-5 0 1 2 3 4 5 6 7, Start-5 0 1 2 3 4 5 6 7 8, Start-5 0 1 2 3 4 5 6 7 8 9, Start-5 0 1 2 3 4 5 6 7 8 9 10, Start-5 0 1 2 3 4 5 6 7 8 9 10 11, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16] Col-11: Int Axis Lengths: [5, 15] (NB: Matrix in Row/Column order) [1, 1, 2, 16, 22, 29, 37, 46, 56, 67, 79, 92, 106, 121, 137 2, 2, 3, 17, 23, 30, 38, 47, 57, 68, 80, 93, 107, 122, 138 3, 3, 4, 18, 24, 31, 39, 48, 58, 69, 81, 94, 108, 123, 139 4, 4, 5, 19, 25, 32, 40, 49, 59, 70, 82, 95, 109, 124, 140 5, 5, 6, 20, 26, 33, 41, 50, 60, 71, 83, 96, 110, 125, 141] Try to add a string Column: Col-10 and fill it. Col-4: Bool [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] Col-5: DComplex [(18,0), (17,1), (16,2), (12,6), (11,7), (10,8), (9,9), (8,10), (7,11), (6,12), (5,13), (4,14), (3,15), (2,16), (1,17)] Col-6: float Ndim=4 Axis Lengths: [2, 3, 1, 15] [0, 0, 0, 0][0, 1] [0, 1, 0, 0][2, 3] [0, 2, 0, 0][4, 5] [0, 0, 0, 1][6, 7] [0, 1, 0, 1][8, 9] [0, 2, 0, 1][10, 11] [0, 0, 0, 2][12, 13] [0, 1, 0, 2][14, 15] [0, 2, 0, 2][16, 17] [0, 0, 0, 3][36, 37] [0, 1, 0, 3][38, 39] [0, 2, 0, 3][40, 41] [0, 0, 0, 4][42, 43] [0, 1, 0, 4][44, 45] [0, 2, 0, 4][46, 47] [0, 0, 0, 5][48, 49] [0, 1, 0, 5][50, 51] [0, 2, 0, 5][52, 53] [0, 0, 0, 6][54, 55] [0, 1, 0, 6][56, 57] [0, 2, 0, 6][58, 59] [0, 0, 0, 7][60, 61] [0, 1, 0, 7][62, 63] [0, 2, 0, 7][64, 65] [0, 0, 0, 8][66, 67] [0, 1, 0, 8][68, 69] [0, 2, 0, 8][70, 71] [0, 0, 0, 9][72, 73] [0, 1, 0, 9][74, 75] [0, 2, 0, 9][76, 77] [0, 0, 0, 10][78, 79] [0, 1, 0, 10][80, 81] [0, 2, 0, 10][82, 83] [0, 0, 0, 11][84, 85] [0, 1, 0, 11][86, 87] [0, 2, 0, 11][88, 89] [0, 0, 0, 12][90, 91] [0, 1, 0, 12][92, 93] [0, 2, 0, 12][94, 95] [0, 0, 0, 13][96, 97] [0, 1, 0, 13][98, 99] [0, 2, 0, 13][100, 101] [0, 0, 0, 14][102, 103] [0, 1, 0, 14][104, 105] [0, 2, 0, 14][106, 107] Col-8: Bool Ndim=4 Axis Lengths: [5, 7, 1, 15] [0, 0, 0, 0][0, 0, 1, 0, 0] [0, 1, 0, 0][1, 0, 0, 1, 0] [0, 2, 0, 0][0, 1, 0, 0, 1] [0, 3, 0, 0][0, 0, 1, 0, 0] [0, 4, 0, 0][1, 0, 0, 1, 0] [0, 5, 0, 0][0, 1, 0, 0, 1] [0, 6, 0, 0][0, 0, 1, 0, 0] [0, 0, 0, 1][0, 0, 1, 0, 0] [0, 1, 0, 1][1, 0, 0, 1, 0] [0, 2, 0, 1][0, 1, 0, 0, 1] [0, 3, 0, 1][0, 0, 1, 0, 0] [0, 4, 0, 1][1, 0, 0, 1, 0] [0, 5, 0, 1][0, 1, 0, 0, 1] [0, 6, 0, 1][0, 0, 1, 0, 0] [0, 0, 0, 2][0, 0, 1, 0, 0] [0, 1, 0, 2][1, 0, 0, 1, 0] [0, 2, 0, 2][0, 1, 0, 0, 1] [0, 3, 0, 2][0, 0, 1, 0, 0] [0, 4, 0, 2][1, 0, 0, 1, 0] [0, 5, 0, 2][0, 1, 0, 0, 1] [0, 6, 0, 2][0, 0, 1, 0, 0] [0, 0, 0, 3][0, 0, 1, 0, 0] [0, 1, 0, 3][1, 0, 0, 1, 0] [0, 2, 0, 3][0, 1, 0, 0, 1] [0, 3, 0, 3][0, 0, 1, 0, 0] [0, 4, 0, 3][1, 0, 0, 1, 0] [0, 5, 0, 3][0, 1, 0, 0, 1] [0, 6, 0, 3][0, 0, 1, 0, 0] [0, 0, 0, 4][0, 0, 1, 0, 0] [0, 1, 0, 4][1, 0, 0, 1, 0] [0, 2, 0, 4][0, 1, 0, 0, 1] [0, 3, 0, 4][0, 0, 1, 0, 0] [0, 4, 0, 4][1, 0, 0, 1, 0] [0, 5, 0, 4][0, 1, 0, 0, 1] [0, 6, 0, 4][0, 0, 1, 0, 0] [0, 0, 0, 5][0, 0, 1, 0, 0] [0, 1, 0, 5][1, 0, 0, 1, 0] [0, 2, 0, 5][0, 1, 0, 0, 1] [0, 3, 0, 5][0, 0, 1, 0, 0] [0, 4, 0, 5][1, 0, 0, 1, 0] [0, 5, 0, 5][0, 1, 0, 0, 1] [0, 6, 0, 5][0, 0, 1, 0, 0] [0, 0, 0, 6][0, 0, 1, 0, 0] [0, 1, 0, 6][1, 0, 0, 1, 0] [0, 2, 0, 6][0, 1, 0, 0, 1] [0, 3, 0, 6][0, 0, 1, 0, 0] [0, 4, 0, 6][1, 0, 0, 1, 0] [0, 5, 0, 6][0, 1, 0, 0, 1] [0, 6, 0, 6][0, 0, 1, 0, 0] [0, 0, 0, 7][0, 0, 1, 0, 0] [0, 1, 0, 7][1, 0, 0, 1, 0] [0, 2, 0, 7][0, 1, 0, 0, 1] [0, 3, 0, 7][0, 0, 1, 0, 0] [0, 4, 0, 7][1, 0, 0, 1, 0] [0, 5, 0, 7][0, 1, 0, 0, 1] [0, 6, 0, 7][0, 0, 1, 0, 0] [0, 0, 0, 8][0, 0, 1, 0, 0] [0, 1, 0, 8][1, 0, 0, 1, 0] [0, 2, 0, 8][0, 1, 0, 0, 1] [0, 3, 0, 8][0, 0, 1, 0, 0] [0, 4, 0, 8][1, 0, 0, 1, 0] [0, 5, 0, 8][0, 1, 0, 0, 1] [0, 6, 0, 8][0, 0, 1, 0, 0] [0, 0, 0, 9][0, 0, 1, 0, 0] [0, 1, 0, 9][1, 0, 0, 1, 0] [0, 2, 0, 9][0, 1, 0, 0, 1] [0, 3, 0, 9][0, 0, 1, 0, 0] [0, 4, 0, 9][1, 0, 0, 1, 0] [0, 5, 0, 9][0, 1, 0, 0, 1] [0, 6, 0, 9][0, 0, 1, 0, 0] [0, 0, 0, 10][0, 0, 1, 0, 0] [0, 1, 0, 10][1, 0, 0, 1, 0] [0, 2, 0, 10][0, 1, 0, 0, 1] [0, 3, 0, 10][0, 0, 1, 0, 0] [0, 4, 0, 10][1, 0, 0, 1, 0] [0, 5, 0, 10][0, 1, 0, 0, 1] [0, 6, 0, 10][0, 0, 1, 0, 0] [0, 0, 0, 11][0, 0, 1, 0, 0] [0, 1, 0, 11][1, 0, 0, 1, 0] [0, 2, 0, 11][0, 1, 0, 0, 1] [0, 3, 0, 11][0, 0, 1, 0, 0] [0, 4, 0, 11][1, 0, 0, 1, 0] [0, 5, 0, 11][0, 1, 0, 0, 1] [0, 6, 0, 11][0, 0, 1, 0, 0] [0, 0, 0, 12][0, 0, 1, 0, 0] [0, 1, 0, 12][1, 0, 0, 1, 0] [0, 2, 0, 12][0, 1, 0, 0, 1] [0, 3, 0, 12][0, 0, 1, 0, 0] [0, 4, 0, 12][1, 0, 0, 1, 0] [0, 5, 0, 12][0, 1, 0, 0, 1] [0, 6, 0, 12][0, 0, 1, 0, 0] [0, 0, 0, 13][0, 0, 1, 0, 0] [0, 1, 0, 13][1, 0, 0, 1, 0] [0, 2, 0, 13][0, 1, 0, 0, 1] [0, 3, 0, 13][0, 0, 1, 0, 0] [0, 4, 0, 13][1, 0, 0, 1, 0] [0, 5, 0, 13][0, 1, 0, 0, 1] [0, 6, 0, 13][0, 0, 1, 0, 0] [0, 0, 0, 14][0, 0, 1, 0, 0] [0, 1, 0, 14][1, 0, 0, 1, 0] [0, 2, 0, 14][0, 1, 0, 0, 1] [0, 3, 0, 14][0, 0, 1, 0, 0] [0, 4, 0, 14][1, 0, 0, 1, 0] [0, 5, 0, 14][0, 1, 0, 0, 1] [0, 6, 0, 14][0, 0, 1, 0, 0] Col-9: String Axis Lengths: [5, 15] (NB: Matrix in Row/Column order) [Start-1, Start-1 0, Start-1 0 1, Start-1 0 1 2 3 4 5, Start-1 0 1 2 3 4 5 6, Start-1 0 1 2 3 4 5 6 7, Start-1 0 1 2 3 4 5 6 7 8, Start-1 0 1 2 3 4 5 6 7 8 9, Start-1 0 1 2 3 4 5 6 7 8 9 10, Start-1 0 1 2 3 4 5 6 7 8 9 10 11, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-2, Start-2 0, Start-2 0 1, Start-2 0 1 2 3 4 5, Start-2 0 1 2 3 4 5 6, Start-2 0 1 2 3 4 5 6 7, Start-2 0 1 2 3 4 5 6 7 8, Start-2 0 1 2 3 4 5 6 7 8 9, Start-2 0 1 2 3 4 5 6 7 8 9 10, Start-2 0 1 2 3 4 5 6 7 8 9 10 11, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-3, Start-3 0, Start-3 0 1, Start-3 0 1 2 3 4 5, Start-3 0 1 2 3 4 5 6, Start-3 0 1 2 3 4 5 6 7, Start-3 0 1 2 3 4 5 6 7 8, Start-3 0 1 2 3 4 5 6 7 8 9, Start-3 0 1 2 3 4 5 6 7 8 9 10, Start-3 0 1 2 3 4 5 6 7 8 9 10 11, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-4, Start-4 0, Start-4 0 1, Start-4 0 1 2 3 4 5, Start-4 0 1 2 3 4 5 6, Start-4 0 1 2 3 4 5 6 7, Start-4 0 1 2 3 4 5 6 7 8, Start-4 0 1 2 3 4 5 6 7 8 9, Start-4 0 1 2 3 4 5 6 7 8 9 10, Start-4 0 1 2 3 4 5 6 7 8 9 10 11, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-5, Start-5 0, Start-5 0 1, Start-5 0 1 2 3 4 5, Start-5 0 1 2 3 4 5 6, Start-5 0 1 2 3 4 5 6 7, Start-5 0 1 2 3 4 5 6 7 8, Start-5 0 1 2 3 4 5 6 7 8 9, Start-5 0 1 2 3 4 5 6 7 8 9 10, Start-5 0 1 2 3 4 5 6 7 8 9 10 11, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16] Col-11: Int Axis Lengths: [5, 15] (NB: Matrix in Row/Column order) [1, 1, 2, 16, 22, 29, 37, 46, 56, 67, 79, 92, 106, 121, 137 2, 2, 3, 17, 23, 30, 38, 47, 57, 68, 80, 93, 107, 122, 138 3, 3, 4, 18, 24, 31, 39, 48, 58, 69, 81, 94, 108, 123, 139 4, 4, 5, 19, 25, 32, 40, 49, 59, 70, 82, 95, 109, 124, 140 5, 5, 6, 20, 26, 33, 41, 50, 60, 71, 83, 96, 110, 125, 141] Col-10: String [String-1, String-1 0, String-1 0 1, String-1 0 1 2, String-1 0 1 2 3, String-1 0 1 2 3 4, String-1 0 1 2 3 4 5, String-1 0 1 2 3 4 5 6, String-1 0 1 2 3 4 5 6 7, String-1 0 1 2 3 4 5 6 7 8, String-1 0 1 2 3 4 5 6 7 8 9, String-1 0 1 2 3 4 5 6 7 8 9 10, String-1 0 1 2 3 4 5 6 7 8 9 10 11, String-1 0 1 2 3 4 5 6 7 8 9 10 11 12, String-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13] after removing several rows at once: Col-4: Bool [] Col-5: DComplex [] Col-6: float [] Col-8: Bool [] Col-9: String [] Col-11: Int [] Col-10: String [] casacore-3.7.1/tables/DataMan/test/tSSMAddRemove.cc000066400000000000000000000044741476623553700220170ustar00rootroot00000000000000//# tSSMAddRemove.cc: Test program when adding/removing rows //# Copyright (C) 2012 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include using namespace casacore; // This test program was created due to a problem in BucketCache found // on 31-Aug-2012. When a bucket was added and removed before being flushed, // only the free list (4 bytes) was written instead of the entire bucket. // When opening the file again, it found that the nr of buckets in the file // was one short and tried to write it, which failed if opened as readonly. // // The table used is tarred in tSSMAddRemove.in and unpacked by the .run file. void addRemove() { Table tab ("tSSMAddRemove_tmp.tab", Table::Update); cout << "nrow=" << tab.nrow() << endl; tab.addRow (14); Vector rows(14); indgen (rows, rownr_t(28)); tab.removeRow (rows); } int main() { try { addRemove(); Table tab ("tSSMAddRemove_tmp.tab"); ScalarColumn idcol(tab, "SOURCE_ID"); idcol(27); } catch (std::exception& x) { cout << x.what() << endl; return 1; } return 0; } casacore-3.7.1/tables/DataMan/test/tSSMAddRemove.in000066400000000000000000000046441476623553700220370ustar00rootroot00000000000000‹4óSí\mlGÞ½;»¶» Ò@—BKi\ç>í˜6µÏö%¹Ö÷‘[;mªJ—õÞÚÞàÛ]v÷œ¸RÁ?ZŠÄŸ"”¤9RT !ç"âG/" )JRh©D#UDSê”@LKfvßµçÎçóÙ=;N™GZ?ïî|ìû13;3{k“çcáL&%eÕ1)mfµVSÜÎT^¯·=â,n³ÙëÚ à|þ@{°Íð¶8¯/ .T]5J#g˜‚ŽTÉÈ’&) çCÙ††ÊÔvÌò³düÑ1*µf³*÷@þh Š¿Ïï ¶Å¿=à÷1œ·*w_ÿçñašE$Öôã¨#Á…Ž­ÞdR6N¡+õÖI¯dˆ•î²ë¬ )ITõ :eÑñ\“}¥¨ »ÜBMÀ÷„u]ïQGsYg{4£æPMéõ½ÑT¤§?šˆ£“Í½².‰¦¬*܃Rëp+— ·p½‘ž¯·¢ÄFÞ”Œ gx3&(¥.Ôa/A½_ÄuG“ª!ãêà";§$»€UÏ•´ ë÷æÅÌeÙ4°e Þ帢. óÑø®¶®¬Óǰe–ÓåM]V†CÜ„òøpëBfŽ–6¥cAS<渆[Ò:¸¯;% §V\2N(°S÷£N)…µ1™J$#©t,¡mLêª&é\V…:«NæHi$Jú€]n8ñyk¹9G ŠÌvƒôØf^FpYT19 Ȱ±'ÜíN…±ÃÒ»S‰$º¸-žË"·©Cܰ®æ4t;nHÕ9T—<¨ VWÑrº¦Re}¤†)IJ:7KÈóí²½2k—§'ÑAü¯I¢,Œr∠ ¢)é²aÊ¢m3Ôœ.J-œÕ黑ƚ`³FªzE¦­c ±¦µòºh¼?’ÚîCòNNICö!{L9+Yq:4"‹#œ9"œ!™8ICÆg%”ÕàÐEAsÈB©âAâ‡«Öæª¤Í³³í}IÍ#Žáæ±-ŽÜ2×8Áà†å1Iá29+¿:h`÷b%µÒŠzp}| –î‹Æ#<öí\Ï5PÃ7uÔ2FeEªLùUè¦ÅÊó‰TO$íµNì È™5ªígø$š5¤Â}é'£ñÞÄ“¶Ú_ŽöZ½ÎîoŽ×ÉJF=„û_N[+æ $žþ¨Õ ‹ÉM•sYƒHå³$쥭»í¡ÊÇœLi >Ñü§FÒTq§ ô÷átÒ›Õ%|æ>_sü³ZSCE ‚3Æ SÊ¢±í j«• VøÀ±›KyÝQ2¸Fã,W·#g þŒ¬@„ë´9«<ÑþÔ.Ç•„·)áûÓ»R‘½‘xÏ~tå³}hpçtÉ0¹!]úVNRÄñê´et[¹€ÎÎÓ÷<‹þÊ+ÇzÒÝž>>õÄRYËïç÷EðîK¼Õe‘“FUQ6Ç9ÁD’tTùfh•ÄÒ¿FcÙRI,ÝYki£¬@0›Ðº ­œTÑ¢ ešÄgÇÞf«[öë‚C°‚œÅr]…±\îôÕ|“±wrÇÎÓR»$³ž(»(ÆÇ¼UxåEK®FYB%gYG^#×CyaŽO^+˜,$Q2¡ôœ¯à>0*ЉxÏF 'Ì ’熂˅­k6éU{£Žù:Ü<sPPœPÓ÷7¡t¡=>&Ðß…5Gr=vâÆnëq¥N±…*£øÔ ÜþÿP•6àËïÿ¼þyïÚ½Á Ýÿ_ À°‚µyc 3îž`ìdzõh‡•3Œ×üm¦ðyEqçàLíÏ]zvf:ÿË|sö+[Ïæ?êÚݵõÚsùS÷mÙ4þýSùë{OMlßôfþܵ©›7êùë©æÜ_ÈåÏ]ÿèý±»G燮^zþùsÿy!ýð+èü”½‘?7ó‹wîûISþÚC­ÿŒèBþ‡7>ó³GéúëñïÏŸûøÖëÏ¿ünþ:[ü-TþÆWwOä/hÓï]™9Ñy¡þäðÅ–tNŸNìnÿã–Î+cSO¿ðw^m»É|øÎ:¯Ü»©óé›û;ßõËõ;:gÞO?õbK:ÿzÝÛÏ\LžÈŸÍŸ¸°ïµïÝyöÅ8Öyåw¿_̾ÛÝ~(îlÜZeÜn{) ñì¸|Zy1ûæ4Ù”ðû?æ._{0ð°7°ÉuþŽÖ€`e©µäAB 9c˾ý^ßlY‰ÈcÏkb0l X÷òBÛB^¼—Q7"k¾_[;cíŽ ºlXyrº ä »ìšÐS]\ÏÊZ]ØU>ÒÁúÝc/“kb™ÌØËäâ%òRÊ5•«ªbøj×Ñk~ä߈íj‰O`ÞûÛ³ƒ˜•‹éŸc>*ÿkóiñ&Ìo>qù»˜ÿòÆÛ;¿\s¡áâÉÖ¦p}“?½ÿÞ#˜Ï¼õÞyÌ¿~-7‰ø¸x¸ö©Õ¾ß}ê,9rÀ'ÏÏ@8à=Àðà3À³Ùµxð`˜Ç¹¦€ß°Ù ·s?œ> < üài›=÷w1•ôb@/ôbœù%èÅ€^N3bA/ôbA/ôbA/ôr^.ÐË5üðiàË6»á]¢â↸¸'€!.nˆ‹ââá€÷0•ò3N~ˆ#qdÎCYð þbƒ¿Xð þr6f]à/øËþrMƒ¿\à/7øËÝ þrƒ¿Üà/÷e›=à/øËSIûw‚¹ä „̲—»9IÈY#ä B~‰' yŠÏòyB¾LÈÓs2KèÏú³„þ,¡?KèÏú³„þ,¡?KèÏú³„þ¬£ÿ2€Ç‰Æ)˜¯ÕôüÎ:§            XƒpÞKÁï ðoHëx>U2Þ›qÖucïáâý› ¼œÕF¥„ž‘t)4¦pí‡÷ðïù ~9…- %PPPPPPPPPPPPPPPP¬ Êÿ#Wåå¿ÿñú}¡yßÿøý^úýÏê¿-²w¥º£'3憹óh>šæ£ù–šb ¡Üó_V†ÔjÜc‘ç¿uRôü¶èó5Ð?®IÜN®Ï :bÃíÖ‰bõP®ÿÃ[ÊOŒÅúÈšßÿé÷ÿ«‡ðþçæ1Æüåž»àžƒßû;ÿµ¦äë{:É¡                        X+øÍ(ë½xcasacore-3.7.1/tables/DataMan/test/tSSMAddRemove.run000077500000000000000000000001411476623553700222240ustar00rootroot00000000000000#!/bin/sh rm -rf tSSMAddRemove_tmp.tab tar zxf tSSMAddRemove.in $casa_checktool ./tSSMAddRemove casacore-3.7.1/tables/DataMan/test/tSSMStringHandler.cc000066400000000000000000000277131476623553700227160ustar00rootroot00000000000000//# tSSMStringHandler.cc: Test program for the StringHandler part of the //# StandardStMan storage manager //# Copyright (C) 2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Test program for the SSMStringHandler part of the // StandardStMan storage manager // // This program tests the StandardStMan storage manager, especially the // SSMStringHandler part. // The results are written to stdout. The script executing this program, // compares the results with the reference output file. // open and write a few rows // aMode == 0 open new void init (uInt aBucketSize,uInt aMode); // reopen table, and throw away a row void deleteRow(const uInt aRow); // reopen table, and throw away a few rows void deleteRows(const Vector& aNrRows); // delete a column void deleteColumn(const String aColumn); // add a direct ArrayColumn void addDirArrayColumn(); // add an indirect array void addIndArrayColumn(); // add small column (<=8) void addSmallColumn(); // create empty column (only setShape, no puts) void addEmptyColumn(); // replace a few string with same or smaller length void replaceStrings(); // show table info void info(const Table aTable); int main (int argc, const char* argv[]) { uInt aNr = 500; if (argc > 1) { istringstream anIstr(argv[1]); anIstr >> aNr; } try { init (aNr,0); init (aNr,1); addDirArrayColumn (); addIndArrayColumn (); replaceStrings (); deleteRow (2); deleteRow (40); Vector aNrRows(35); for (uInt i=0; i< 35; i++) { aNrRows(i) = i+3; } deleteRows (aNrRows); deleteColumn ("Col-2"); addSmallColumn (); deleteColumn ("Col-1"); addEmptyColumn (); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } void info( const Table aTable) { for (uInt i=0; i< aTable.tableDesc().ncolumn(); i++) { cout << aTable.tableDesc().columnNames()(i) << ": " << aTable.tableDesc().columnDesc(i).dataType() << endl; if (aTable.tableDesc().columnDesc(i).dataType() == TpString) { String aColName=aTable.tableDesc().columnNames()(i); if (aColName == "Col-1") { ScalarColumn ad(aTable,aColName); cout << ad.getColumn() << endl; } else if (aColName == "Col-2" || aColName == "Col-3" || aColName == "Col-4" || aColName == "Col-5") { ArrayColumn ab(aTable,aColName); cout << ab.getColumn() << endl; } else { cout << "Sorry, String not implemented yet for this case." << endl; } } else { cout << "Sorry, datatype not implemented for this test yet." << endl; } } } // First build a description. void init (uInt aBucketSize, uInt aMode) { Table aTable; if (aMode == 0) { DataManager::registerCtor ("StandardStMan", StandardStMan::makeObject); // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.comment() = "A test of StringHandler"; td.addColumn (ScalarColumnDesc("Col-1")); // Now create a new table from the description. SetupNewTable aNewTab("tSSMStringHandler_tmp.data", td, Table::New); // Create a storage manager for it. StandardStMan aSm1 ("SSM", aBucketSize); aNewTab.bindAll (aSm1); aTable = Table (aNewTab, 10); } else { aTable = Table("tSSMStringHandler_tmp.data", Table::Update); } ScalarColumn aa(aTable,"Col-1"); uInt start=0; String aString("String-1"); if (aMode == 1){ aString="String-2"; aTable.addRow(40); start=10; } // fill new column with data for (uInt i=start; i ab; cout << "Trying to add a Direct Array Column of String." << endl; aTable.addColumn(ArrayColumnDesc("Col-2", IPosition(1,5), ColumnDesc::Direct)); if (aTable.tableDesc().isColumn("Col-2")) { ab.attach(aTable,"Col-2"); } Vector arrs(5); arrs(0)="Array-1"; arrs(1)="Array-2"; arrs(2)="Array-3"; arrs(3)="Array-4"; arrs(4)="Array-5"; for (uInt i=0; i ac; cout << "Trying to add an Indirect Array Column of String." << endl; aTable.addColumn(ArrayColumnDesc("Col-3")); if (aTable.tableDesc().isColumn("Col-3")) { ac.attach(aTable,"Col-3"); } Vector arrs(5); arrs(0)="IndArr1"; arrs(1)="IndArr2"; arrs(2)="IndArr3"; arrs(3)="IndArr4"; arrs(4)="IndArr5"; for (uInt i=0; i ae; cout << "Trying to add a small fixed shape Column of String." << endl; aTable.addColumn(ArrayColumnDesc("Col-4", IPosition(1,5), ColumnDesc::FixedShape)); if (aTable.tableDesc().isColumn("Col-4")) { ae.attach(aTable,"Col-4"); } String aS("SFS"); Vector arrs(5); arrs(0)="SFS1"; arrs(1)="SFS2"; arrs(2)="SFS3"; arrs(3)="SFS4"; arrs(4)="SFS5"; for (uInt i=0; i af; cout << "Trying to add a Column without filling it." << endl; aTable.addColumn(ArrayColumnDesc("Col-5")); if (aTable.tableDesc().isColumn("Col-5")) { af.attach(aTable,"Col-5"); } for (uInt i=0; i& aNrRows) { Table aTable = Table("tSSMStringHandler_tmp.data", Table::Update); cout << "Try to remove a few rows at the same time." << endl; aTable.removeRow(aNrRows); cout << "after removing several rows at once: " << endl; ROStandardStManAccessor anA(aTable,"SSM"); anA.showBaseStatistics(cout); anA.showIndexStatistics(cout); cout << endl << endl; // Print column data info(aTable); } void deleteColumn(const String aColumn) { Table aTable = Table("tSSMStringHandler_tmp.data", Table::Update); cout << "Try to remove Column:" << aColumn << endl; aTable.removeColumn(aColumn); cout << "After removing Column: " << aColumn << endl; ROStandardStManAccessor anA(aTable,"SSM"); anA.showBaseStatistics(cout); anA.showIndexStatistics(cout); cout << endl << endl; // Print column data info(aTable); } void replaceStrings() { Table aTable = Table("tSSMStringHandler_tmp.data", Table::Update); ScalarColumn aa(aTable,"Col-1"); ArrayColumn ab(aTable,"Col-2"); ArrayColumn ac(aTable,"Col-3"); cout << "Try to change some datain Column 1" << endl; String aString="Much Bigger I Believe"; for (uInt i=0; i arrd(5); arrd(0)="A bigger Direct Array"; arrd(1)="A bigger Direct Array"; arrd(2)="A bigger Direct Array"; arrd(3)="A bigger Direct Array"; arrd(4)="A bigger Direct Array"; for (uInt i=0; i<5; i++) { ab.put(i,arrd); for (uInt j=0; j< 5;j++) { arrd(j) += "-"+String::toString(i); } } arrd(0)="Small"; arrd(1)="Small"; arrd(2)="Small"; arrd(3)="Small"; arrd(4)="Small"; for (uInt i=6; i<15; i++) { ab.put(i,arrd); for (uInt j=0; j< 5;j++) { arrd(j) += "-"+String::toString(i); } } cout << "Try to change some datain Column 3" << endl; Vector arri(5); arri(0)="A bigger Indirect Array"; arri(1)="A bigger Indirect Array"; arri(2)="A bigger Indirect Array"; arri(3)="A bigger Indirect Array"; arri(4)="A bigger Indirect Array"; for (uInt i=0; i<5; i++) { ac.put(i,arri); for (uInt j=0; j< 5;j++) { arri(j) += "-"+String::toString(i); } } arri(0)="Small"; arri(1)="Small"; arri(2)="Small"; arri(3)="Small"; arri(4)="Small"; for (uInt i=6; i<15; i++) { ac.put(i,arri); for (uInt j=0; j< 5;j++) { arri(j) += "-"+String::toString(i); } } cout << "After Changing the data" << endl; ROStandardStManAccessor anA(aTable,"SSM"); anA.showBaseStatistics(cout); anA.showIndexStatistics(cout); cout << endl << endl; // Print column data info(aTable); } casacore-3.7.1/tables/DataMan/test/tSSMStringHandler.out000066400000000000000000007252511476623553700231420ustar00rootroot00000000000000after creation StandardStMan Base statistics: Nr of columns : 1 Nr of rows in the columns : 10 ColIndex[0] : 0 ColOffset[0] : 0 CacheSize : 2 Size of buckets : 500 Total buckets : 0 Total Index buckets : 0 1st Index bucket : -1 Index bucket offset : 0 last String bucket used : -1 Total free buckets : 0 1st free bucket : -1 StandardStMan index: 0 statistics: Index statistics: Entries used : 1 Rows Per bucket : 41 Nr of Columns : 1 BucketNr[0] : 0 - LastRow[0] : 9 Freespace entries: 1 Offset[0]: 492 - nrBytes[0]: 8 Col-1: String [String-1, String-1 0, String-1 0 1, String-1 0 1 2, String-1 0 1 2 3, String-1 0 1 2 3 4, String-1 0 1 2 3 4 5, String-1 0 1 2 3 4 5 6, String-1 0 1 2 3 4 5 6 7, String-1 0 1 2 3 4 5 6 7 8] after reopening and adding 40 rows StandardStMan Base statistics: Nr of columns : 1 Nr of rows in the columns : 50 ColIndex[0] : 0 ColOffset[0] : 0 CacheSize : 2 Size of buckets : 500 Total buckets : 3 Total Index buckets : 1 1st Index bucket : 2 Index bucket offset : 8 last String bucket used : 1 Total free buckets : 0 1st free bucket : -1 StandardStMan index: 0 statistics: Index statistics: Entries used : 2 Rows Per bucket : 41 Nr of Columns : 1 BucketNr[0] : 0 - LastRow[0] : 40 BucketNr[1] : 3 - LastRow[1] : 49 Freespace entries: 1 Offset[0]: 492 - nrBytes[0]: 8 Col-1: String [String-1, String-1 0, String-1 0 1, String-1 0 1 2, String-1 0 1 2 3, String-1 0 1 2 3 4, String-1 0 1 2 3 4 5, String-1 0 1 2 3 4 5 6, String-1 0 1 2 3 4 5 6 7, String-1 0 1 2 3 4 5 6 7 8, String-2, String-2 10, String-2 10 11, String-2 10 11 12, String-2 10 11 12 13, String-2 10 11 12 13 14, String-2 10 11 12 13 14 15, String-2 10 11 12 13 14 15 16, String-2 10 11 12 13 14 15 16 17, String-2 10 11 12 13 14 15 16 17 18, String-2 10 11 12 13 14 15 16 17 18 19, String-2 10 11 12 13 14 15 16 17 18 19 20, String-2 10 11 12 13 14 15 16 17 18 19 20 21, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22 23, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33, String-2 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, String-2 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, String-2 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, String-2 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, String-2 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, String-2 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, String-2 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, String-2 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, String-2 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, String-2 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, String-2 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, String-2 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, String-2 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, String-2 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, String-2 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] Trying to add a Direct Array Column of String. StandardStMan Base statistics: Nr of columns : 2 Nr of rows in the columns : 50 ColIndex[0] : 0 ColOffset[0] : 0 ColIndex[1] : 1 ColOffset[1] : 0 CacheSize : 2 Size of buckets : 500 Total buckets : 10 Total Index buckets : 1 1st Index bucket : 2 Index bucket offset : 254 last String bucket used : 9 Total free buckets : 0 1st free bucket : -1 StandardStMan index: 0 statistics: Index statistics: Entries used : 2 Rows Per bucket : 41 Nr of Columns : 1 BucketNr[0] : 0 - LastRow[0] : 40 BucketNr[1] : 3 - LastRow[1] : 49 Freespace entries: 1 Offset[0]: 492 - nrBytes[0]: 8 StandardStMan index: 1 statistics: Index statistics: Entries used : 2 Rows Per bucket : 41 Nr of Columns : 1 BucketNr[0] : 10 - LastRow[0] : 40 BucketNr[1] : 11 - LastRow[1] : 49 Freespace entries: 1 Offset[0]: 492 - nrBytes[0]: 8 Col-1: String [String-1, String-1 0, String-1 0 1, String-1 0 1 2, String-1 0 1 2 3, String-1 0 1 2 3 4, String-1 0 1 2 3 4 5, String-1 0 1 2 3 4 5 6, String-1 0 1 2 3 4 5 6 7, String-1 0 1 2 3 4 5 6 7 8, String-2, String-2 10, String-2 10 11, String-2 10 11 12, String-2 10 11 12 13, String-2 10 11 12 13 14, String-2 10 11 12 13 14 15, String-2 10 11 12 13 14 15 16, String-2 10 11 12 13 14 15 16 17, String-2 10 11 12 13 14 15 16 17 18, String-2 10 11 12 13 14 15 16 17 18 19, String-2 10 11 12 13 14 15 16 17 18 19 20, String-2 10 11 12 13 14 15 16 17 18 19 20 21, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22 23, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33, String-2 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, String-2 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, String-2 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, String-2 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, String-2 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, String-2 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, String-2 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, String-2 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, String-2 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, String-2 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, String-2 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, String-2 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, String-2 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, String-2 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, String-2 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] Col-2: String Axis Lengths: [5, 50] (NB: Matrix in Row/Column order) [Array-1, Array-1-0, Array-1-0-1, Array-1-0-1-2, Array-1-0-1-2-3, Array-1-0-1-2-3-4, Array-1-0-1-2-3-4-5, Array-1-0-1-2-3-4-5-6, Array-1-0-1-2-3-4-5-6-7, Array-1-0-1-2-3-4-5-6-7-8, Array-1-0-1-2-3-4-5-6-7-8-9, Array-1-0-1-2-3-4-5-6-7-8-9-10, Array-1-0-1-2-3-4-5-6-7-8-9-10-11, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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 Array-2, Array-2-0, Array-2-0-1, Array-2-0-1-2, Array-2-0-1-2-3, Array-2-0-1-2-3-4, Array-2-0-1-2-3-4-5, Array-2-0-1-2-3-4-5-6, Array-2-0-1-2-3-4-5-6-7, Array-2-0-1-2-3-4-5-6-7-8, Array-2-0-1-2-3-4-5-6-7-8-9, Array-2-0-1-2-3-4-5-6-7-8-9-10, Array-2-0-1-2-3-4-5-6-7-8-9-10-11, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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 Array-3, Array-3-0, Array-3-0-1, Array-3-0-1-2, Array-3-0-1-2-3, Array-3-0-1-2-3-4, Array-3-0-1-2-3-4-5, Array-3-0-1-2-3-4-5-6, Array-3-0-1-2-3-4-5-6-7, Array-3-0-1-2-3-4-5-6-7-8, Array-3-0-1-2-3-4-5-6-7-8-9, Array-3-0-1-2-3-4-5-6-7-8-9-10, Array-3-0-1-2-3-4-5-6-7-8-9-10-11, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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 Array-4, Array-4-0, Array-4-0-1, Array-4-0-1-2, Array-4-0-1-2-3, Array-4-0-1-2-3-4, Array-4-0-1-2-3-4-5, Array-4-0-1-2-3-4-5-6, Array-4-0-1-2-3-4-5-6-7, Array-4-0-1-2-3-4-5-6-7-8, Array-4-0-1-2-3-4-5-6-7-8-9, Array-4-0-1-2-3-4-5-6-7-8-9-10, Array-4-0-1-2-3-4-5-6-7-8-9-10-11, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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 Array-5, Array-5-0, Array-5-0-1, Array-5-0-1-2, Array-5-0-1-2-3, Array-5-0-1-2-3-4, Array-5-0-1-2-3-4-5, Array-5-0-1-2-3-4-5-6, Array-5-0-1-2-3-4-5-6-7, Array-5-0-1-2-3-4-5-6-7-8, Array-5-0-1-2-3-4-5-6-7-8-9, Array-5-0-1-2-3-4-5-6-7-8-9-10, Array-5-0-1-2-3-4-5-6-7-8-9-10-11, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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] Trying to add an Indirect Array Column of String. StandardStMan Base statistics: Nr of columns : 3 Nr of rows in the columns : 50 ColIndex[0] : 0 ColOffset[0] : 0 ColIndex[1] : 1 ColOffset[1] : 0 ColIndex[2] : 2 ColOffset[2] : 0 CacheSize : 2 Size of buckets : 500 Total buckets : 52 Total Index buckets : 1 1st Index bucket : 51 Index bucket offset : 0 last String bucket used : 50 Total free buckets : 1 1st free bucket : 2 StandardStMan index: 0 statistics: Index statistics: Entries used : 2 Rows Per bucket : 41 Nr of Columns : 1 BucketNr[0] : 0 - LastRow[0] : 40 BucketNr[1] : 3 - LastRow[1] : 49 Freespace entries: 1 Offset[0]: 492 - nrBytes[0]: 8 StandardStMan index: 1 statistics: Index statistics: Entries used : 2 Rows Per bucket : 41 Nr of Columns : 1 BucketNr[0] : 10 - LastRow[0] : 40 BucketNr[1] : 11 - LastRow[1] : 49 Freespace entries: 1 Offset[0]: 492 - nrBytes[0]: 8 StandardStMan index: 2 statistics: Index statistics: Entries used : 2 Rows Per bucket : 41 Nr of Columns : 1 BucketNr[0] : 2 - LastRow[0] : 40 BucketNr[1] : 52 - LastRow[1] : 49 Freespace entries: 1 Offset[0]: 492 - nrBytes[0]: 8 Col-1: String [String-1, String-1 0, String-1 0 1, String-1 0 1 2, String-1 0 1 2 3, String-1 0 1 2 3 4, String-1 0 1 2 3 4 5, String-1 0 1 2 3 4 5 6, String-1 0 1 2 3 4 5 6 7, String-1 0 1 2 3 4 5 6 7 8, String-2, String-2 10, String-2 10 11, String-2 10 11 12, String-2 10 11 12 13, String-2 10 11 12 13 14, String-2 10 11 12 13 14 15, String-2 10 11 12 13 14 15 16, String-2 10 11 12 13 14 15 16 17, String-2 10 11 12 13 14 15 16 17 18, String-2 10 11 12 13 14 15 16 17 18 19, String-2 10 11 12 13 14 15 16 17 18 19 20, String-2 10 11 12 13 14 15 16 17 18 19 20 21, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22 23, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32, String-2 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33, String-2 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, String-2 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, String-2 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, String-2 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, String-2 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, String-2 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, String-2 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, String-2 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, String-2 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, String-2 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, String-2 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, String-2 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, String-2 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, String-2 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, String-2 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] Col-2: String Axis Lengths: [5, 50] (NB: Matrix in Row/Column order) [Array-1, Array-1-0, Array-1-0-1, Array-1-0-1-2, Array-1-0-1-2-3, Array-1-0-1-2-3-4, Array-1-0-1-2-3-4-5, Array-1-0-1-2-3-4-5-6, Array-1-0-1-2-3-4-5-6-7, Array-1-0-1-2-3-4-5-6-7-8, Array-1-0-1-2-3-4-5-6-7-8-9, Array-1-0-1-2-3-4-5-6-7-8-9-10, Array-1-0-1-2-3-4-5-6-7-8-9-10-11, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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 Array-2, Array-2-0, Array-2-0-1, Array-2-0-1-2, Array-2-0-1-2-3, Array-2-0-1-2-3-4, Array-2-0-1-2-3-4-5, Array-2-0-1-2-3-4-5-6, Array-2-0-1-2-3-4-5-6-7, Array-2-0-1-2-3-4-5-6-7-8, Array-2-0-1-2-3-4-5-6-7-8-9, Array-2-0-1-2-3-4-5-6-7-8-9-10, Array-2-0-1-2-3-4-5-6-7-8-9-10-11, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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 Array-3, Array-3-0, Array-3-0-1, Array-3-0-1-2, Array-3-0-1-2-3, Array-3-0-1-2-3-4, Array-3-0-1-2-3-4-5, Array-3-0-1-2-3-4-5-6, Array-3-0-1-2-3-4-5-6-7, Array-3-0-1-2-3-4-5-6-7-8, Array-3-0-1-2-3-4-5-6-7-8-9, Array-3-0-1-2-3-4-5-6-7-8-9-10, Array-3-0-1-2-3-4-5-6-7-8-9-10-11, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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 Array-4, Array-4-0, Array-4-0-1, Array-4-0-1-2, Array-4-0-1-2-3, Array-4-0-1-2-3-4, Array-4-0-1-2-3-4-5, Array-4-0-1-2-3-4-5-6, Array-4-0-1-2-3-4-5-6-7, Array-4-0-1-2-3-4-5-6-7-8, Array-4-0-1-2-3-4-5-6-7-8-9, Array-4-0-1-2-3-4-5-6-7-8-9-10, Array-4-0-1-2-3-4-5-6-7-8-9-10-11, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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 Array-5, Array-5-0, Array-5-0-1, Array-5-0-1-2, Array-5-0-1-2-3, Array-5-0-1-2-3-4, Array-5-0-1-2-3-4-5, Array-5-0-1-2-3-4-5-6, Array-5-0-1-2-3-4-5-6-7, Array-5-0-1-2-3-4-5-6-7-8, Array-5-0-1-2-3-4-5-6-7-8-9, Array-5-0-1-2-3-4-5-6-7-8-9-10, Array-5-0-1-2-3-4-5-6-7-8-9-10-11, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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] Col-3: String Axis Lengths: [5, 50] (NB: Matrix in Row/Column order) [IndArr1, IndArr1-0, IndArr1-0-1, IndArr1-0-1-2, IndArr1-0-1-2-3, IndArr1-0-1-2-3-4, IndArr1-0-1-2-3-4-5, IndArr1-0-1-2-3-4-5-6, IndArr1-0-1-2-3-4-5-6-7, IndArr1-0-1-2-3-4-5-6-7-8, IndArr1-0-1-2-3-4-5-6-7-8-9, IndArr1-0-1-2-3-4-5-6-7-8-9-10, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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 IndArr2, IndArr2-0, IndArr2-0-1, IndArr2-0-1-2, IndArr2-0-1-2-3, IndArr2-0-1-2-3-4, IndArr2-0-1-2-3-4-5, IndArr2-0-1-2-3-4-5-6, IndArr2-0-1-2-3-4-5-6-7, IndArr2-0-1-2-3-4-5-6-7-8, IndArr2-0-1-2-3-4-5-6-7-8-9, IndArr2-0-1-2-3-4-5-6-7-8-9-10, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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 IndArr3, IndArr3-0, IndArr3-0-1, IndArr3-0-1-2, IndArr3-0-1-2-3, IndArr3-0-1-2-3-4, IndArr3-0-1-2-3-4-5, IndArr3-0-1-2-3-4-5-6, IndArr3-0-1-2-3-4-5-6-7, IndArr3-0-1-2-3-4-5-6-7-8, IndArr3-0-1-2-3-4-5-6-7-8-9, IndArr3-0-1-2-3-4-5-6-7-8-9-10, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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 IndArr4, IndArr4-0, IndArr4-0-1, IndArr4-0-1-2, IndArr4-0-1-2-3, IndArr4-0-1-2-3-4, IndArr4-0-1-2-3-4-5, IndArr4-0-1-2-3-4-5-6, IndArr4-0-1-2-3-4-5-6-7, IndArr4-0-1-2-3-4-5-6-7-8, IndArr4-0-1-2-3-4-5-6-7-8-9, IndArr4-0-1-2-3-4-5-6-7-8-9-10, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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 IndArr5, IndArr5-0, IndArr5-0-1, IndArr5-0-1-2, IndArr5-0-1-2-3, IndArr5-0-1-2-3-4, IndArr5-0-1-2-3-4-5, IndArr5-0-1-2-3-4-5-6, IndArr5-0-1-2-3-4-5-6-7, IndArr5-0-1-2-3-4-5-6-7-8, IndArr5-0-1-2-3-4-5-6-7-8-9, IndArr5-0-1-2-3-4-5-6-7-8-9-10, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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] Try to change some datain Column 1 Try to change some datain Column 2 Try to change some datain Column 3 After Changing the data StandardStMan Base statistics: Nr of columns : 3 Nr of rows in the columns : 50 ColIndex[0] : 0 ColOffset[0] : 0 ColIndex[1] : 1 ColOffset[1] : 0 ColIndex[2] : 2 ColOffset[2] : 0 CacheSize : 2 Size of buckets : 500 Total buckets : 94 Total Index buckets : 1 1st Index bucket : 93 Index bucket offset : 0 last String bucket used : 92 Total free buckets : 1 1st free bucket : 51 StandardStMan index: 0 statistics: Index statistics: Entries used : 2 Rows Per bucket : 41 Nr of Columns : 1 BucketNr[0] : 0 - LastRow[0] : 40 BucketNr[1] : 3 - LastRow[1] : 49 Freespace entries: 1 Offset[0]: 492 - nrBytes[0]: 8 StandardStMan index: 1 statistics: Index statistics: Entries used : 2 Rows Per bucket : 41 Nr of Columns : 1 BucketNr[0] : 10 - LastRow[0] : 40 BucketNr[1] : 11 - LastRow[1] : 49 Freespace entries: 1 Offset[0]: 492 - nrBytes[0]: 8 StandardStMan index: 2 statistics: Index statistics: Entries used : 2 Rows Per bucket : 41 Nr of Columns : 1 BucketNr[0] : 2 - LastRow[0] : 40 BucketNr[1] : 52 - LastRow[1] : 49 Freespace entries: 1 Offset[0]: 492 - nrBytes[0]: 8 Col-1: String [Much Bigger I Believe, Much Bigger I Believe 50, Much Bigger I Believe 50 49, Much Bigger I Believe 50 49 48, Much Bigger I Believe 50 49 48 47, Much Bigger I Believe 50 49 48 47 46, Much Bigger I Believe 50 49 48 47 46 45, Much Bigger I Believe 50 49 48 47 46 45 44, Much Bigger I Believe 50 49 48 47 46 45 44 43, Much Bigger I Believe 50 49 48 47 46 45 44 43 42, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38 37, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 31, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 31 30, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27, Small, Small 25, Small 25 24, Small 25 24 23, Small 25 24 23 22, Small 25 24 23 22 21, Small 25 24 23 22 21 20, Small 25 24 23 22 21 20 19, Small 25 24 23 22 21 20 19 18, Small 25 24 23 22 21 20 19 18 17, Small 25 24 23 22 21 20 19 18 17 16, Small 25 24 23 22 21 20 19 18 17 16 15, Small 25 24 23 22 21 20 19 18 17 16 15 14, Small 25 24 23 22 21 20 19 18 17 16 15 14 13, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2] Col-2: String Axis Lengths: [5, 50] (NB: Matrix in Row/Column order) [A bigger Direct Array, A bigger Direct Array-0, A bigger Direct Array-0-1, A bigger Direct Array-0-1-2, A bigger Direct Array-0-1-2-3, Array-1-0-1-2-3-4, Small, Small-6, Small-6-7, Small-6-7-8, Small-6-7-8-9, Small-6-7-8-9-10, Small-6-7-8-9-10-11, Small-6-7-8-9-10-11-12, Small-6-7-8-9-10-11-12-13, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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 A bigger Direct Array, A bigger Direct Array-0, A bigger Direct Array-0-1, A bigger Direct Array-0-1-2, A bigger Direct Array-0-1-2-3, Array-2-0-1-2-3-4, Small, Small-6, Small-6-7, Small-6-7-8, Small-6-7-8-9, Small-6-7-8-9-10, Small-6-7-8-9-10-11, Small-6-7-8-9-10-11-12, Small-6-7-8-9-10-11-12-13, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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 A bigger Direct Array, A bigger Direct Array-0, A bigger Direct Array-0-1, A bigger Direct Array-0-1-2, A bigger Direct Array-0-1-2-3, Array-3-0-1-2-3-4, Small, Small-6, Small-6-7, Small-6-7-8, Small-6-7-8-9, Small-6-7-8-9-10, Small-6-7-8-9-10-11, Small-6-7-8-9-10-11-12, Small-6-7-8-9-10-11-12-13, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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 A bigger Direct Array, A bigger Direct Array-0, A bigger Direct Array-0-1, A bigger Direct Array-0-1-2, A bigger Direct Array-0-1-2-3, Array-4-0-1-2-3-4, Small, Small-6, Small-6-7, Small-6-7-8, Small-6-7-8-9, Small-6-7-8-9-10, Small-6-7-8-9-10-11, Small-6-7-8-9-10-11-12, Small-6-7-8-9-10-11-12-13, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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 A bigger Direct Array, A bigger Direct Array-0, A bigger Direct Array-0-1, A bigger Direct Array-0-1-2, A bigger Direct Array-0-1-2-3, Array-5-0-1-2-3-4, Small, Small-6, Small-6-7, Small-6-7-8, Small-6-7-8-9, Small-6-7-8-9-10, Small-6-7-8-9-10-11, Small-6-7-8-9-10-11-12, Small-6-7-8-9-10-11-12-13, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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] Col-3: String Axis Lengths: [5, 50] (NB: Matrix in Row/Column order) [A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1, A bigger Indirect Array-0-1-2, A bigger Indirect Array-0-1-2-3, IndArr1-0-1-2-3-4, Small, Small-6, Small-6-7, Small-6-7-8, Small-6-7-8-9, Small-6-7-8-9-10, Small-6-7-8-9-10-11, Small-6-7-8-9-10-11-12, Small-6-7-8-9-10-11-12-13, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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 A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1, A bigger Indirect Array-0-1-2, A bigger Indirect Array-0-1-2-3, IndArr2-0-1-2-3-4, Small, Small-6, Small-6-7, Small-6-7-8, Small-6-7-8-9, Small-6-7-8-9-10, Small-6-7-8-9-10-11, Small-6-7-8-9-10-11-12, Small-6-7-8-9-10-11-12-13, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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 A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1, A bigger Indirect Array-0-1-2, A bigger Indirect Array-0-1-2-3, IndArr3-0-1-2-3-4, Small, Small-6, Small-6-7, Small-6-7-8, Small-6-7-8-9, Small-6-7-8-9-10, Small-6-7-8-9-10-11, Small-6-7-8-9-10-11-12, Small-6-7-8-9-10-11-12-13, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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 A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1, A bigger Indirect Array-0-1-2, A bigger Indirect Array-0-1-2-3, IndArr4-0-1-2-3-4, Small, Small-6, Small-6-7, Small-6-7-8, Small-6-7-8-9, Small-6-7-8-9-10, Small-6-7-8-9-10-11, Small-6-7-8-9-10-11-12, Small-6-7-8-9-10-11-12-13, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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 A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1, A bigger Indirect Array-0-1-2, A bigger Indirect Array-0-1-2-3, IndArr5-0-1-2-3-4, Small, Small-6, Small-6-7, Small-6-7-8, Small-6-7-8-9, Small-6-7-8-9-10, Small-6-7-8-9-10-11, Small-6-7-8-9-10-11-12, Small-6-7-8-9-10-11-12-13, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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] after removing row: 2 StandardStMan Base statistics: Nr of columns : 3 Nr of rows in the columns : 49 ColIndex[0] : 0 ColOffset[0] : 0 ColIndex[1] : 1 ColOffset[1] : 0 ColIndex[2] : 2 ColOffset[2] : 0 CacheSize : 2 Size of buckets : 500 Total buckets : 100 Total Index buckets : 1 1st Index bucket : 99 Index bucket offset : 0 last String bucket used : 98 Total free buckets : 1 1st free bucket : 93 StandardStMan index: 0 statistics: Index statistics: Entries used : 2 Rows Per bucket : 41 Nr of Columns : 1 BucketNr[0] : 0 - LastRow[0] : 39 BucketNr[1] : 3 - LastRow[1] : 48 Freespace entries: 1 Offset[0]: 492 - nrBytes[0]: 8 StandardStMan index: 1 statistics: Index statistics: Entries used : 2 Rows Per bucket : 41 Nr of Columns : 1 BucketNr[0] : 10 - LastRow[0] : 39 BucketNr[1] : 11 - LastRow[1] : 48 Freespace entries: 1 Offset[0]: 492 - nrBytes[0]: 8 StandardStMan index: 2 statistics: Index statistics: Entries used : 2 Rows Per bucket : 41 Nr of Columns : 1 BucketNr[0] : 2 - LastRow[0] : 39 BucketNr[1] : 52 - LastRow[1] : 48 Freespace entries: 1 Offset[0]: 492 - nrBytes[0]: 8 Col-1: String [Much Bigger I Believe, Much Bigger I Believe 50, Much Bigger I Believe 50 49 48, Much Bigger I Believe 50 49 48 47, Much Bigger I Believe 50 49 48 47 46, Much Bigger I Believe 50 49 48 47 46 45, Much Bigger I Believe 50 49 48 47 46 45 44, Much Bigger I Believe 50 49 48 47 46 45 44 43, Much Bigger I Believe 50 49 48 47 46 45 44 43 42, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38 37, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 31, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 31 30, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27, Small, Small 25, Small 25 24, Small 25 24 23, Small 25 24 23 22, Small 25 24 23 22 21, Small 25 24 23 22 21 20, Small 25 24 23 22 21 20 19, Small 25 24 23 22 21 20 19 18, Small 25 24 23 22 21 20 19 18 17, Small 25 24 23 22 21 20 19 18 17 16, Small 25 24 23 22 21 20 19 18 17 16 15, Small 25 24 23 22 21 20 19 18 17 16 15 14, Small 25 24 23 22 21 20 19 18 17 16 15 14 13, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2] Col-2: String Axis Lengths: [5, 49] (NB: Matrix in Row/Column order) [A bigger Direct Array, A bigger Direct Array-0, A bigger Direct Array-0-1-2, A bigger Direct Array-0-1-2-3, Array-1-0-1-2-3-4, Small, Small-6, Small-6-7, Small-6-7-8, Small-6-7-8-9, Small-6-7-8-9-10, Small-6-7-8-9-10-11, Small-6-7-8-9-10-11-12, Small-6-7-8-9-10-11-12-13, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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 A bigger Direct Array, A bigger Direct Array-0, A bigger Direct Array-0-1-2, A bigger Direct Array-0-1-2-3, Array-2-0-1-2-3-4, Small, Small-6, Small-6-7, Small-6-7-8, Small-6-7-8-9, Small-6-7-8-9-10, Small-6-7-8-9-10-11, Small-6-7-8-9-10-11-12, Small-6-7-8-9-10-11-12-13, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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 A bigger Direct Array, A bigger Direct Array-0, A bigger Direct Array-0-1-2, A bigger Direct Array-0-1-2-3, Array-3-0-1-2-3-4, Small, Small-6, Small-6-7, Small-6-7-8, Small-6-7-8-9, Small-6-7-8-9-10, Small-6-7-8-9-10-11, Small-6-7-8-9-10-11-12, Small-6-7-8-9-10-11-12-13, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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 A bigger Direct Array, A bigger Direct Array-0, A bigger Direct Array-0-1-2, A bigger Direct Array-0-1-2-3, Array-4-0-1-2-3-4, Small, Small-6, Small-6-7, Small-6-7-8, Small-6-7-8-9, Small-6-7-8-9-10, Small-6-7-8-9-10-11, Small-6-7-8-9-10-11-12, Small-6-7-8-9-10-11-12-13, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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 A bigger Direct Array, A bigger Direct Array-0, A bigger Direct Array-0-1-2, A bigger Direct Array-0-1-2-3, Array-5-0-1-2-3-4, Small, Small-6, Small-6-7, Small-6-7-8, Small-6-7-8-9, Small-6-7-8-9-10, Small-6-7-8-9-10-11, Small-6-7-8-9-10-11-12, Small-6-7-8-9-10-11-12-13, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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] Col-3: String Axis Lengths: [5, 49] (NB: Matrix in Row/Column order) [A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, A bigger Indirect Array-0-1-2-3, IndArr1-0-1-2-3-4, Small, Small-6, Small-6-7, Small-6-7-8, Small-6-7-8-9, Small-6-7-8-9-10, Small-6-7-8-9-10-11, Small-6-7-8-9-10-11-12, Small-6-7-8-9-10-11-12-13, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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 A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, A bigger Indirect Array-0-1-2-3, IndArr2-0-1-2-3-4, Small, Small-6, Small-6-7, Small-6-7-8, Small-6-7-8-9, Small-6-7-8-9-10, Small-6-7-8-9-10-11, Small-6-7-8-9-10-11-12, Small-6-7-8-9-10-11-12-13, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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 A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, A bigger Indirect Array-0-1-2-3, IndArr3-0-1-2-3-4, Small, Small-6, Small-6-7, Small-6-7-8, Small-6-7-8-9, Small-6-7-8-9-10, Small-6-7-8-9-10-11, Small-6-7-8-9-10-11-12, Small-6-7-8-9-10-11-12-13, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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 A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, A bigger Indirect Array-0-1-2-3, IndArr4-0-1-2-3-4, Small, Small-6, Small-6-7, Small-6-7-8, Small-6-7-8-9, Small-6-7-8-9-10, Small-6-7-8-9-10-11, Small-6-7-8-9-10-11-12, Small-6-7-8-9-10-11-12-13, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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 A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, A bigger Indirect Array-0-1-2-3, IndArr5-0-1-2-3-4, Small, Small-6, Small-6-7, Small-6-7-8, Small-6-7-8-9, Small-6-7-8-9-10, Small-6-7-8-9-10-11, Small-6-7-8-9-10-11-12, Small-6-7-8-9-10-11-12-13, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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] after removing row: 40 StandardStMan Base statistics: Nr of columns : 3 Nr of rows in the columns : 48 ColIndex[0] : 0 ColOffset[0] : 0 ColIndex[1] : 1 ColOffset[1] : 0 ColIndex[2] : 2 ColOffset[2] : 0 CacheSize : 2 Size of buckets : 500 Total buckets : 100 Total Index buckets : 1 1st Index bucket : 93 Index bucket offset : 0 last String bucket used : 98 Total free buckets : 1 1st free bucket : 99 StandardStMan index: 0 statistics: Index statistics: Entries used : 2 Rows Per bucket : 41 Nr of Columns : 1 BucketNr[0] : 0 - LastRow[0] : 39 BucketNr[1] : 3 - LastRow[1] : 47 Freespace entries: 1 Offset[0]: 492 - nrBytes[0]: 8 StandardStMan index: 1 statistics: Index statistics: Entries used : 2 Rows Per bucket : 41 Nr of Columns : 1 BucketNr[0] : 10 - LastRow[0] : 39 BucketNr[1] : 11 - LastRow[1] : 47 Freespace entries: 1 Offset[0]: 492 - nrBytes[0]: 8 StandardStMan index: 2 statistics: Index statistics: Entries used : 2 Rows Per bucket : 41 Nr of Columns : 1 BucketNr[0] : 2 - LastRow[0] : 39 BucketNr[1] : 52 - LastRow[1] : 47 Freespace entries: 1 Offset[0]: 492 - nrBytes[0]: 8 Col-1: String [Much Bigger I Believe, Much Bigger I Believe 50, Much Bigger I Believe 50 49 48, Much Bigger I Believe 50 49 48 47, Much Bigger I Believe 50 49 48 47 46, Much Bigger I Believe 50 49 48 47 46 45, Much Bigger I Believe 50 49 48 47 46 45 44, Much Bigger I Believe 50 49 48 47 46 45 44 43, Much Bigger I Believe 50 49 48 47 46 45 44 43 42, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38 37, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 31, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 31 30, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28, Much Bigger I Believe 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27, Small, Small 25, Small 25 24, Small 25 24 23, Small 25 24 23 22, Small 25 24 23 22 21, Small 25 24 23 22 21 20, Small 25 24 23 22 21 20 19, Small 25 24 23 22 21 20 19 18, Small 25 24 23 22 21 20 19 18 17, Small 25 24 23 22 21 20 19 18 17 16, Small 25 24 23 22 21 20 19 18 17 16 15, Small 25 24 23 22 21 20 19 18 17 16 15 14, Small 25 24 23 22 21 20 19 18 17 16 15 14 13, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2] Col-2: String Axis Lengths: [5, 48] (NB: Matrix in Row/Column order) [A bigger Direct Array, A bigger Direct Array-0, A bigger Direct Array-0-1-2, A bigger Direct Array-0-1-2-3, Array-1-0-1-2-3-4, Small, Small-6, Small-6-7, Small-6-7-8, Small-6-7-8-9, Small-6-7-8-9-10, Small-6-7-8-9-10-11, Small-6-7-8-9-10-11-12, Small-6-7-8-9-10-11-12-13, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, Array-1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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 A bigger Direct Array, A bigger Direct Array-0, A bigger Direct Array-0-1-2, A bigger Direct Array-0-1-2-3, Array-2-0-1-2-3-4, Small, Small-6, Small-6-7, Small-6-7-8, Small-6-7-8-9, Small-6-7-8-9-10, Small-6-7-8-9-10-11, Small-6-7-8-9-10-11-12, Small-6-7-8-9-10-11-12-13, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, Array-2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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 A bigger Direct Array, A bigger Direct Array-0, A bigger Direct Array-0-1-2, A bigger Direct Array-0-1-2-3, Array-3-0-1-2-3-4, Small, Small-6, Small-6-7, Small-6-7-8, Small-6-7-8-9, Small-6-7-8-9-10, Small-6-7-8-9-10-11, Small-6-7-8-9-10-11-12, Small-6-7-8-9-10-11-12-13, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, Array-3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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 A bigger Direct Array, A bigger Direct Array-0, A bigger Direct Array-0-1-2, A bigger Direct Array-0-1-2-3, Array-4-0-1-2-3-4, Small, Small-6, Small-6-7, Small-6-7-8, Small-6-7-8-9, Small-6-7-8-9-10, Small-6-7-8-9-10-11, Small-6-7-8-9-10-11-12, Small-6-7-8-9-10-11-12-13, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, Array-4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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 A bigger Direct Array, A bigger Direct Array-0, A bigger Direct Array-0-1-2, A bigger Direct Array-0-1-2-3, Array-5-0-1-2-3-4, Small, Small-6, Small-6-7, Small-6-7-8, Small-6-7-8-9, Small-6-7-8-9-10, Small-6-7-8-9-10-11, Small-6-7-8-9-10-11-12, Small-6-7-8-9-10-11-12-13, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, Array-5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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] Col-3: String Axis Lengths: [5, 48] (NB: Matrix in Row/Column order) [A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, A bigger Indirect Array-0-1-2-3, IndArr1-0-1-2-3-4, Small, Small-6, Small-6-7, Small-6-7-8, Small-6-7-8-9, Small-6-7-8-9-10, Small-6-7-8-9-10-11, Small-6-7-8-9-10-11-12, Small-6-7-8-9-10-11-12-13, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, IndArr1-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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 A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, A bigger Indirect Array-0-1-2-3, IndArr2-0-1-2-3-4, Small, Small-6, Small-6-7, Small-6-7-8, Small-6-7-8-9, Small-6-7-8-9-10, Small-6-7-8-9-10-11, Small-6-7-8-9-10-11-12, Small-6-7-8-9-10-11-12-13, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, IndArr2-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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 A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, A bigger Indirect Array-0-1-2-3, IndArr3-0-1-2-3-4, Small, Small-6, Small-6-7, Small-6-7-8, Small-6-7-8-9, Small-6-7-8-9-10, Small-6-7-8-9-10-11, Small-6-7-8-9-10-11-12, Small-6-7-8-9-10-11-12-13, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, IndArr3-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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 A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, A bigger Indirect Array-0-1-2-3, IndArr4-0-1-2-3-4, Small, Small-6, Small-6-7, Small-6-7-8, Small-6-7-8-9, Small-6-7-8-9-10, Small-6-7-8-9-10-11, Small-6-7-8-9-10-11-12, Small-6-7-8-9-10-11-12-13, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, IndArr4-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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 A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, A bigger Indirect Array-0-1-2-3, IndArr5-0-1-2-3-4, Small, Small-6, Small-6-7, Small-6-7-8, Small-6-7-8-9, Small-6-7-8-9-10, Small-6-7-8-9-10-11, Small-6-7-8-9-10-11-12, Small-6-7-8-9-10-11-12-13, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23, IndArr5-0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-19-20-21-22-23-24, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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] Try to remove a few rows at the same time. after removing several rows at once: StandardStMan Base statistics: Nr of columns : 3 Nr of rows in the columns : 13 ColIndex[0] : 0 ColOffset[0] : 0 ColIndex[1] : 1 ColOffset[1] : 0 ColIndex[2] : 2 ColOffset[2] : 0 CacheSize : 2 Size of buckets : 500 Total buckets : 100 Total Index buckets : 1 1st Index bucket : 99 Index bucket offset : 0 last String bucket used : 98 Total free buckets : 1 1st free bucket : 93 StandardStMan index: 0 statistics: Index statistics: Entries used : 2 Rows Per bucket : 41 Nr of Columns : 1 BucketNr[0] : 0 - LastRow[0] : 4 BucketNr[1] : 3 - LastRow[1] : 12 Freespace entries: 1 Offset[0]: 492 - nrBytes[0]: 8 StandardStMan index: 1 statistics: Index statistics: Entries used : 2 Rows Per bucket : 41 Nr of Columns : 1 BucketNr[0] : 10 - LastRow[0] : 4 BucketNr[1] : 11 - LastRow[1] : 12 Freespace entries: 1 Offset[0]: 492 - nrBytes[0]: 8 StandardStMan index: 2 statistics: Index statistics: Entries used : 2 Rows Per bucket : 41 Nr of Columns : 1 BucketNr[0] : 2 - LastRow[0] : 4 BucketNr[1] : 52 - LastRow[1] : 12 Freespace entries: 1 Offset[0]: 492 - nrBytes[0]: 8 Col-1: String [Much Bigger I Believe, Much Bigger I Believe 50, Much Bigger I Believe 50 49 48, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2] Col-2: String Axis Lengths: [5, 13] (NB: Matrix in Row/Column order) [A bigger Direct Array, A bigger Direct Array-0, A bigger Direct Array-0-1-2, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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, Array-1-0-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 A bigger Direct Array, A bigger Direct Array-0, A bigger Direct Array-0-1-2, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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, Array-2-0-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 A bigger Direct Array, A bigger Direct Array-0, A bigger Direct Array-0-1-2, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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, Array-3-0-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 A bigger Direct Array, A bigger Direct Array-0, A bigger Direct Array-0-1-2, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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, Array-4-0-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 A bigger Direct Array, A bigger Direct Array-0, A bigger Direct Array-0-1-2, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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, Array-5-0-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] Col-3: String Axis Lengths: [5, 13] (NB: Matrix in Row/Column order) [A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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 A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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 A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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 A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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 A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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] Try to remove Column:Col-2 After removing Column: Col-2 StandardStMan Base statistics: Nr of columns : 2 Nr of rows in the columns : 13 ColIndex[0] : 0 ColOffset[0] : 0 ColIndex[1] : 1 ColOffset[1] : 0 CacheSize : 2 Size of buckets : 500 Total buckets : 100 Total Index buckets : 1 1st Index bucket : 51 Index bucket offset : 0 last String bucket used : 98 Total free buckets : 5 1st free bucket : 99 StandardStMan index: 0 statistics: Index statistics: Entries used : 2 Rows Per bucket : 41 Nr of Columns : 1 BucketNr[0] : 0 - LastRow[0] : 4 BucketNr[1] : 3 - LastRow[1] : 12 Freespace entries: 1 Offset[0]: 492 - nrBytes[0]: 8 StandardStMan index: 1 statistics: Index statistics: Entries used : 2 Rows Per bucket : 41 Nr of Columns : 1 BucketNr[0] : 2 - LastRow[0] : 4 BucketNr[1] : 52 - LastRow[1] : 12 Freespace entries: 1 Offset[0]: 492 - nrBytes[0]: 8 Col-1: String [Much Bigger I Believe, Much Bigger I Believe 50, Much Bigger I Believe 50 49 48, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2] Col-3: String Axis Lengths: [5, 13] (NB: Matrix in Row/Column order) [A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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 A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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 A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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 A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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 A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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] Trying to add a small fixed shape Column of String. StandardStMan Base statistics: Nr of columns : 3 Nr of rows in the columns : 13 ColIndex[0] : 0 ColOffset[0] : 0 ColIndex[1] : 1 ColOffset[1] : 0 ColIndex[2] : 2 ColOffset[2] : 0 CacheSize : 2 Size of buckets : 500 Total buckets : 100 Total Index buckets : 1 1st Index bucket : 11 Index bucket offset : 0 last String bucket used : 98 Total free buckets : 21 1st free bucket : 51 StandardStMan index: 0 statistics: Index statistics: Entries used : 2 Rows Per bucket : 41 Nr of Columns : 1 BucketNr[0] : 0 - LastRow[0] : 4 BucketNr[1] : 3 - LastRow[1] : 12 Freespace entries: 1 Offset[0]: 492 - nrBytes[0]: 8 StandardStMan index: 1 statistics: Index statistics: Entries used : 2 Rows Per bucket : 41 Nr of Columns : 1 BucketNr[0] : 2 - LastRow[0] : 4 BucketNr[1] : 52 - LastRow[1] : 12 Freespace entries: 1 Offset[0]: 492 - nrBytes[0]: 8 StandardStMan index: 2 statistics: Index statistics: Entries used : 1 Rows Per bucket : 41 Nr of Columns : 1 BucketNr[0] : 51 - LastRow[0] : 12 Freespace entries: 1 Offset[0]: 492 - nrBytes[0]: 8 Col-1: String [Much Bigger I Believe, Much Bigger I Believe 50, Much Bigger I Believe 50 49 48, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3, Small 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2] Col-3: String Axis Lengths: [5, 13] (NB: Matrix in Row/Column order) [A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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 A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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 A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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 A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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 A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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] Col-4: String Axis Lengths: [5, 13] (NB: Matrix in Row/Column order) [SFS1, SFS0-0, SFS0-1, SFS0-2, SFS0-3, SFS0-4, SFS0-5, SFS0-6, SFS0-7, SFS0-8, SFS0-9, SFS0-10, SFS0-11 SFS2, SFS1-0, SFS1-1, SFS1-2, SFS1-3, SFS1-4, SFS1-5, SFS1-6, SFS1-7, SFS1-8, SFS1-9, SFS1-10, SFS1-11 SFS3, SFS2-0, SFS2-1, SFS2-2, SFS2-3, SFS2-4, SFS2-5, SFS2-6, SFS2-7, SFS2-8, SFS2-9, SFS2-10, SFS2-11 SFS4, SFS3-0, SFS3-1, SFS3-2, SFS3-3, SFS3-4, SFS3-5, SFS3-6, SFS3-7, SFS3-8, SFS3-9, SFS3-10, SFS3-11 SFS5, SFS4-0, SFS4-1, SFS4-2, SFS4-3, SFS4-4, SFS4-5, SFS4-6, SFS4-7, SFS4-8, SFS4-9, SFS4-10, SFS4-11] Try to remove Column:Col-1 After removing Column: Col-1 StandardStMan Base statistics: Nr of columns : 2 Nr of rows in the columns : 13 ColIndex[0] : 0 ColOffset[0] : 0 ColIndex[1] : 1 ColOffset[1] : 0 CacheSize : 2 Size of buckets : 500 Total buckets : 100 Total Index buckets : 1 1st Index bucket : 50 Index bucket offset : 0 last String bucket used : 10 Total free buckets : 19 1st free bucket : 11 StandardStMan index: 0 statistics: Index statistics: Entries used : 2 Rows Per bucket : 41 Nr of Columns : 1 BucketNr[0] : 2 - LastRow[0] : 4 BucketNr[1] : 52 - LastRow[1] : 12 Freespace entries: 1 Offset[0]: 492 - nrBytes[0]: 8 StandardStMan index: 1 statistics: Index statistics: Entries used : 1 Rows Per bucket : 41 Nr of Columns : 1 BucketNr[0] : 51 - LastRow[0] : 12 Freespace entries: 1 Offset[0]: 492 - nrBytes[0]: 8 Col-3: String Axis Lengths: [5, 13] (NB: Matrix in Row/Column order) [A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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 A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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 A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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 A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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 A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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] Col-4: String Axis Lengths: [5, 13] (NB: Matrix in Row/Column order) [SFS1, SFS0-0, SFS0-1, SFS0-2, SFS0-3, SFS0-4, SFS0-5, SFS0-6, SFS0-7, SFS0-8, SFS0-9, SFS0-10, SFS0-11 SFS2, SFS1-0, SFS1-1, SFS1-2, SFS1-3, SFS1-4, SFS1-5, SFS1-6, SFS1-7, SFS1-8, SFS1-9, SFS1-10, SFS1-11 SFS3, SFS2-0, SFS2-1, SFS2-2, SFS2-3, SFS2-4, SFS2-5, SFS2-6, SFS2-7, SFS2-8, SFS2-9, SFS2-10, SFS2-11 SFS4, SFS3-0, SFS3-1, SFS3-2, SFS3-3, SFS3-4, SFS3-5, SFS3-6, SFS3-7, SFS3-8, SFS3-9, SFS3-10, SFS3-11 SFS5, SFS4-0, SFS4-1, SFS4-2, SFS4-3, SFS4-4, SFS4-5, SFS4-6, SFS4-7, SFS4-8, SFS4-9, SFS4-10, SFS4-11] Trying to add a Column without filling it. StandardStMan Base statistics: Nr of columns : 3 Nr of rows in the columns : 13 ColIndex[0] : 0 ColOffset[0] : 0 ColIndex[1] : 1 ColOffset[1] : 0 ColIndex[2] : 2 ColOffset[2] : 0 CacheSize : 2 Size of buckets : 500 Total buckets : 100 Total Index buckets : 1 1st Index bucket : 3 Index bucket offset : 0 last String bucket used : 10 Total free buckets : 24 1st free bucket : 50 StandardStMan index: 0 statistics: Index statistics: Entries used : 2 Rows Per bucket : 41 Nr of Columns : 1 BucketNr[0] : 2 - LastRow[0] : 4 BucketNr[1] : 52 - LastRow[1] : 12 Freespace entries: 1 Offset[0]: 492 - nrBytes[0]: 8 StandardStMan index: 1 statistics: Index statistics: Entries used : 1 Rows Per bucket : 41 Nr of Columns : 1 BucketNr[0] : 51 - LastRow[0] : 12 Freespace entries: 1 Offset[0]: 492 - nrBytes[0]: 8 StandardStMan index: 2 statistics: Index statistics: Entries used : 1 Rows Per bucket : 41 Nr of Columns : 1 BucketNr[0] : 50 - LastRow[0] : 12 Freespace entries: 1 Offset[0]: 492 - nrBytes[0]: 8 Col-3: String Axis Lengths: [5, 13] (NB: Matrix in Row/Column order) [A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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, IndArr1-0-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 A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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, IndArr2-0-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 A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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, IndArr3-0-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 A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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, IndArr4-0-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 A bigger Indirect Array, A bigger Indirect Array-0, A bigger Indirect Array-0-1-2, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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, IndArr5-0-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] Col-4: String Axis Lengths: [5, 13] (NB: Matrix in Row/Column order) [SFS1, SFS0-0, SFS0-1, SFS0-2, SFS0-3, SFS0-4, SFS0-5, SFS0-6, SFS0-7, SFS0-8, SFS0-9, SFS0-10, SFS0-11 SFS2, SFS1-0, SFS1-1, SFS1-2, SFS1-3, SFS1-4, SFS1-5, SFS1-6, SFS1-7, SFS1-8, SFS1-9, SFS1-10, SFS1-11 SFS3, SFS2-0, SFS2-1, SFS2-2, SFS2-3, SFS2-4, SFS2-5, SFS2-6, SFS2-7, SFS2-8, SFS2-9, SFS2-10, SFS2-11 SFS4, SFS3-0, SFS3-1, SFS3-2, SFS3-3, SFS3-4, SFS3-5, SFS3-6, SFS3-7, SFS3-8, SFS3-9, SFS3-10, SFS3-11 SFS5, SFS4-0, SFS4-1, SFS4-2, SFS4-3, SFS4-4, SFS4-5, SFS4-6, SFS4-7, SFS4-8, SFS4-9, SFS4-10, SFS4-11] Col-5: String Ndim=3 Axis Lengths: [2, 2, 13] [0, 0, 0][, ] [0, 1, 0][, ] [0, 0, 1][, ] [0, 1, 1][, ] [0, 0, 2][, ] [0, 1, 2][, ] [0, 0, 3][, ] [0, 1, 3][, ] [0, 0, 4][, ] [0, 1, 4][, ] [0, 0, 5][, ] [0, 1, 5][, ] [0, 0, 6][, ] [0, 1, 6][, ] [0, 0, 7][, ] [0, 1, 7][, ] [0, 0, 8][, ] [0, 1, 8][, ] [0, 0, 9][, ] [0, 1, 9][, ] [0, 0, 10][, ] [0, 1, 10][, ] [0, 0, 11][, ] [0, 1, 11][, ] [0, 0, 12][, ] [0, 1, 12][, ] casacore-3.7.1/tables/DataMan/test/tScaledArrayEngine.cc000066400000000000000000000170021476623553700230750ustar00rootroot00000000000000//# tScaledArrayEngine.cc: Test program for class ScaledArrayEngine //# Copyright (C) 1994,1995,1996,1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Test program for class ScaledArrayEngine // This program tests the virtual column engine ScaledArrayEngine. // The results are written to stdout. The script executing this program, // compares the results with the reference output file. void a(); void b(); int main () { try { a(); b(); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } // First build a description. void a() { // First register the virtual column engine. ScaledArrayEngine::registerClass(); ScaledArrayEngine::registerClass(); // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.comment() = "A test of class TableDesc"; td.addColumn (ArrayColumnDesc ("target1")); td.addColumn (ArrayColumnDesc ("source1")); td.addColumn (ArrayColumnDesc ("target2")); td.addColumn (ArrayColumnDesc ("source2","", IPosition(3,2,3,4), ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc ("target3", "", IPosition(3,2,3,4), ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc ("source3", "", IPosition(3,2,3,4), ColumnDesc::Direct)); td.addColumn (ScalarColumnDesc ("scale3")); // Now create a new table from the description. SetupNewTable newtab("tScaledArrayEngine_tmp.data", td, Table::New); // Create the virtual column engine with the scale factors // and bind the columns to them. ScaledArrayEngine engine1("source1", "target1", 2.0, 4.0); ScaledArrayEngine engine2("source2", "target2", 6.0, 2.0); ScaledArrayEngine engine3("source3", "target3", "scale3"); newtab.bindColumn ("source1", engine1); newtab.bindColumn ("source2", engine2); newtab.bindColumn ("source3", engine3); Table tab(newtab, 10); // Fill the table via the virtual columns. ArrayColumn source1 (tab, "source1"); ArrayColumn source2 (tab, "source2"); ArrayColumn source3 (tab, "source3"); ScalarColumn scale3 (tab,"scale3"); Cube arrd(IPosition(3,2,3,4)); Cube arrf(IPosition(3,2,3,4)); uInt i; i=2; for (uInt i2=0; i2<4; i2++) for (uInt i1=0; i1<3; i1++) for (uInt i0=0; i0<2; i0++) { arrd(i0,i1,i2) = i; arrf(i0,i1,i2) = i; i += 6; } for (i=0; i<10; i++) { scale3.put (i, 1 + i%3); source1.put (i, arrd); source2.put (i, arrf); source3.put (i, arrd + (double)4); arrd += (double)(6*arrd.nelements()); arrf += (float)(6*arrd.nelements()); } //# Do an erroneous thing. SetupNewTable newtab2("tScaledArrayEngine_tmp.dat2", td, Table::Scratch); newtab2.bindColumn ("source2", engine1); /// try { /// Table tab2(newtab2, 10); // bound to incorrect column /// } catch (std::exception x) { /// cout << x.what() << endl; /// } } void b() { // Read back the table. Table tab("tScaledArrayEngine_tmp.data"); ArrayColumn source1 (tab, "source1"); ArrayColumn source2 (tab, "source2"); ArrayColumn source3 (tab, "source3"); ArrayColumn target1 (tab, "target1"); ArrayColumn target2 (tab, "target2"); ArrayColumn target3 (tab, "target3"); Cube arri1(IPosition(3,2,3,4)); Cube arri3(IPosition(3,2,3,4)); Cube arrvali(IPosition(3,2,3,4)); Cube arrc2(IPosition(3,2,3,4)); Cube arrvalc(IPosition(3,2,3,4)); Cube arrd1(IPosition(3,2,3,4)); Cube arrd3(IPosition(3,2,3,4)); Cube arrvald(IPosition(3,2,3,4)); Cube arrf2(IPosition(3,2,3,4)); Cube arrvalf(IPosition(3,2,3,4)); Cube arrvalslice(arrvald(Slice(0,1),Slice(0,1,2),Slice(0,2,2))); Slice tmp; Slicer nslice (tmp, tmp, tmp, Slicer::endIsLength); Slicer nslice2(Slice(0,1), Slice(0,1,2), Slice(0,2,2), Slicer::endIsLength); uInt i=0; for (uInt i2=0; i2<4; i2++) for (uInt i1=0; i1<3; i1++) for (uInt i0=0; i0<2; i0++) { arrd1(i0,i1,i2) = 2 + 6*i; arrf2(i0,i1,i2) = 2 + 6*i; arrd3(i0,i1,i2) = 6 + 6*i; arri1(i0,i1,i2) = 3*i - 1; arrc2(i0,i1,i2) = i; arri3(i0,i1,i2) = 6 + 6*i; i++; } for (i=0; i<10; i++) { cout << "get row " << i << endl; source1.get (i, arrvald); if (!allEQ (arrvald, arrd1)) { cout << "error in source1 in row " << i << endl; } target1.get (i, arrvali); if (!allEQ (arrvali, arri1)) { cout << "error in target1 in row " << i << endl; } source2.get (i, arrvalf); if (!allEQ (arrvalf, arrf2)) { cout << "error in source2 in row " << i << endl; } target2.get (i, arrvalc); if (!allEQ (arrvalc, arrc2)) { cout << "error in target2 in row " << i << endl; } source3.get (i, arrvald); if (!allEQ (arrvald, arrd3)) { cout << "error in source3 in row " << i << endl; } target3.get (i, arrvali); if (!allEQ (arrvali, arri3/(Int)(1+i%3))) { cout << "error in target3 in row " << i << endl; } source1.getSlice (i, nslice, arrvald); if (!allEQ (arrvald, arrd1)) { cout << "error in source1 (entire slice) in row " << i << endl; } source1.getSlice (i, nslice2, arrvalslice); if (!allEQ (arrvald, arrd1)) { cout << "error in source1 (partial slice) in row " << i << endl; } arrd1 += (double)(6*arrd1.nelements()); arrf2 += (float)(6*arrf2.nelements()); arrd3 += (double)(6*arrd3.nelements()); arri1 += (Int)(3*arri1.nelements()); arrc2 += (uChar)(arrc2.nelements()); arri3 += (Int)(6*arri3.nelements()); } } casacore-3.7.1/tables/DataMan/test/tScaledArrayEngine.out000066400000000000000000000001441476623553700233160ustar00rootroot00000000000000get row 0 get row 1 get row 2 get row 3 get row 4 get row 5 get row 6 get row 7 get row 8 get row 9 casacore-3.7.1/tables/DataMan/test/tScaledComplexData.cc000066400000000000000000000236741476623553700231060ustar00rootroot00000000000000//# tScaledComplexData.cc: Test program for class ScaledComplexData //# Copyright (C) 1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Test program for class ScaledComplexData // This program tests the virtual column engine ScaledComplexData. // The results are written to stdout. The script executing this program, // compares the results with the reference output file. void a(); void b(); int main () { try { a(); b(); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } // First build a description. void a() { // First register the virtual column engine. ScaledComplexData::registerClass(); ScaledComplexData::registerClass(); // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.addColumn (ArrayColumnDesc ("target1")); td.addColumn (ArrayColumnDesc ("source1")); td.addColumn (ArrayColumnDesc ("target2")); td.addColumn (ArrayColumnDesc ("source2","", IPosition(2,3,4), ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc ("target3", "", IPosition(3,2,3,4), ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc ("source3", "", IPosition(2,3,4), ColumnDesc::Direct)); td.addColumn (ScalarColumnDesc ("scale3")); // Now create a new table from the description. SetupNewTable newtab("tScaledComplexData_tmp.data", td, Table::New); // Create the virtual column engine with the scale factors // and bind the columns to them. ScaledComplexData engine1("source1", "target1", DComplex(2.0, 3.0), DComplex(4.0, 5.0)); ScaledComplexData engine2("source2", "target2", Complex(6.0, 7.0), Complex(2.0, 3.0)); ScaledComplexData engine3("source3", "target3", "scale3"); newtab.bindColumn ("source1", engine1); newtab.bindColumn ("source2", engine2); newtab.bindColumn ("source3", engine3); Table tab(newtab, 10); // Fill the table via the virtual columns. ArrayColumn source1 (tab, "source1"); ArrayColumn source2 (tab, "source2"); ArrayColumn source3 (tab, "source3"); ScalarColumn scale3 (tab,"scale3"); Matrix arrd(IPosition(2,3,4)); Matrix arrf(IPosition(2,3,4)); Int i=0; for (uInt i2=0; i2<4; i2++) { for (uInt i1=0; i1<3; i1++) { arrd(i1,i2) = DComplex(4+i, 2+i); arrf(i1,i2) = Complex(8+i, 3+i); i += 420; } } for (i=0; i<10; i++) { scale3.put (i, Complex(1+i%3, 1+i%4)); source1.put (i, arrd); source2.put (i, arrf); source3.put (i, arrd + DComplex(2, 22)); arrd += DComplex(840, 840); arrf += Complex(840, 840); } //# Do an erroneous thing. //# 2005-02-15 GvD: this test does not work correctly, because the //# exception calls the BaseTable dtor. In there the table directory //# gets removed, but there is still an open file which NFS renames //# to .nfs... So the remove throws an exception resulting in an abort. //# So all open files should be closed first (in one way or another). SetupNewTable newtab2("tScaledComplexData_tmp.dat2", td, Table::Scratch); newtab2.bindColumn ("source2", engine1); try { Table tab2(newtab2, 10); // bound to incorrect column } catch (std::exception& x) { cout << x.what() << endl; } } void b() { // Read back the table. Table tab("tScaledComplexData_tmp.data"); ArrayColumn source1 (tab, "source1"); ArrayColumn source2 (tab, "source2"); ArrayColumn source3 (tab, "source3"); ArrayColumn target1 (tab, "target1"); ArrayColumn target2 (tab, "target2"); ArrayColumn target3 (tab, "target3"); Cube arri1(IPosition(3,2,3,4)); Cube arri3(IPosition(3,2,3,4)); Cube arrvali(IPosition(3,2,3,4)); Cube arrc2(IPosition(3,2,3,4)); Cube arrvalc(IPosition(3,2,3,4)); Matrix arrd1(IPosition(2,3,4)); Matrix arrd3(IPosition(2,3,4)); Matrix arrvald(IPosition(2,3,4)); Matrix arrf2(IPosition(2,3,4)); Matrix arrvalf(IPosition(2,3,4)); Matrix arrvalslice(arrvald(Slice(0,1,2),Slice(0,2,2))); Slice tmp; Slicer nslice (tmp, tmp, Slicer::endIsLength); Slicer nslice2(Slice(0,1,2), Slice(0,2,2), Slicer::endIsLength); for (uInt j=0; j<10; j++) { { Int sc1 = 1+j%3; Int sc2 = 1+j%4; Int i=0; for (uInt i2=0; i2<4; i2++) { for (uInt i1=0; i1<3; i1++) { Int val = j*840 + i; arrd1(i1,i2) = DComplex(val+4, val+2); arrf2(i1,i2) = Complex(val+8, val+3); arrd3(i1,i2) = arrd1(i1,i2) + DComplex(2,22); arri1(0,i1,i2) = (val+4 - 4)/2; arri1(1,i1,i2) = (val+2 - 5)/3; arrc2(0,i1,i2) = (val+8 - 2)/6; arrc2(1,i1,i2) = (val+3 - 3)/7; arri3(0,i1,i2) = (val+4+2)/sc1; arri3(1,i1,i2) = (val+2+22)/sc2; i += 420; } } } cout << "get row " << j << endl; source1.get (j, arrvald); if (!allEQ (arrvald, arrd1)) { cout << "error in source1 in row " << j << endl; cout << arrvald << endl; cout << arrd1 << endl; } target1.get (j, arrvali); if (!allEQ (arrvali, arri1)) { cout << "error in target1 in row " << j << endl; cout << arrvali << endl; cout << arri1 << endl; } source2.get (j, arrvalf); if (!allEQ (arrvalf, arrf2)) { cout << "error in source2 in row " << j << endl; cout << arrvalf << endl; cout << arrf2 << endl; } target2.get (j, arrvalc); if (!allEQ (arrvalc, arrc2)) { cout << "error in target2 in row " << j << endl; cout << arrvalc << endl; cout << arrc2 << endl; } source3.get (j, arrvald); if (!allEQ (arrvald, arrd3)) { cout << "error in source3 in row " << j << endl; cout << arrvald << endl; cout << arrd3 << endl; } target3.get (j, arrvali); if (!allEQ (arrvali, arri3)) { cout << "error in target3 in row " << j << endl; cout << arrvali << endl; cout << arri3 << endl; } source1.getSlice (j, nslice, arrvald); if (!allEQ (arrvald, arrd1)) { cout << "error in source1 (entire slice) in row " << j << endl; cout << arrvald << endl; cout << arrd1 << endl; } source1.getSlice (j, nslice2, arrvalslice); if (!allEQ (arrvald, arrd1)) { cout << "error in source1 (partial slice) in row " << j << endl; cout << arrvald << endl; cout << arrd1 << endl; } } // Now test getting the columns. { Cube arrd1(IPosition(3,3,4,10)); Slicer nslice2(Slice(0,2,1), Slice(1,2,2), Slicer::endIsLength); for (uInt j=0; j<10; j++) { Int i = 0; for (uInt i2=0; i2<4; i2++) { for (uInt i1=0; i1<3; i1++) { Int val = j*840 + i; arrd1(i1,i2,j) = DComplex(val+4, val+2); i += 420; } } } { Cube arrvald = source1.getColumn(); if (!allEQ (arrvald, arrd1)) { cout << "error in source1 getcolumn " << endl; cout << arrvald << endl; cout << arrd1 << endl; } } { Cube arrvald = source1.getColumnRange(Slice(1,4,2)); if (!allEQ (arrvald, arrd1(Slice(0,3,1),Slice(0,4,1),Slice(1,4,2)))) { cout << "error in source1 getcolumnrange " << endl; cout << arrvald << endl; cout << arrd1 << endl; } } { Cube arrvald = source1.getColumn(nslice2); if (!allEQ (arrvald, arrd1(Slice(0,2,1),Slice(1,2,2),Slice(0,10,1)))) { cout << "error in source1 getcolumnslice " << endl; cout << arrvald << endl; cout << arrd1 << endl; } } { Cube arrvald = source1.getColumnRange(Slice(1,4,2), nslice2); if (!allEQ (arrvald, arrd1(Slice(0,2,1),Slice(1,2,2),Slice(1,4,2)))) { cout << "error in source1 getcolumnrangeslice " << endl; cout << arrvald << endl; cout << arrd1 << endl; } } } } casacore-3.7.1/tables/DataMan/test/tStArrayFile.cc000066400000000000000000000237541476623553700217550ustar00rootroot00000000000000//# tStArrayFile.cc: Test program for the StManArrayFile class //# Copyright (C) 1994,1995,1996,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include // for snprintf #include // Test program for the StManArrayFile class // This program tests the class StManArrayFile. // This class is meant to store indirect table arrays, but could // in principle also be used for other array purposes. void a (Bool, uInt, Int64&, Int64&, Int64&, Int64&); void b (Bool, Int64, Int64, Int64, Int64, Int64&, Int64&, Int64&, Int64&); void c (Bool, Int64, Int64, Int64, Int64); int main (int argc, const char* argv[]) { uInt stVersion = 0; uInt endVersion = 1; if (argc > 1) { istringstream istr(argv[1]); istr >> stVersion; endVersion = stVersion; if (argc > 2) { istringstream istr(argv[2]); istr >> endVersion; } } try { for (uInt i=stVersion; i<=endVersion; i++) { Int64 off1, off2, off3, off4, offc1, offc2, offc3, offc4; cout << "test of StArrayFile with version " << i << " in canonical format " << endl; a (True, i, off1, off2, off3, off4); b (True, off1, off2, off3, off4, offc1, offc2, offc3, offc4); c (True, off1, off2, off3, off4); c (True, offc1, offc2, offc3, offc4); cout << "test of StArrayFile with version " << i << " in local format " << endl; a (False, i, off1, off2, off3, off4); b (False, off1, off2, off3, off4, offc1, offc2, offc3, offc4); c (False, off1, off2, off3, off4); c (False, offc1, offc2, offc3, offc4); } } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } // Write some arrays (in chunks). void a (Bool canonical, uInt version, Int64& off1, Int64& off2, Int64& off3, Int64& off4) { uInt l1,l2,l3,l4; Bool bbuf[10000]; Int ibuf[10000]; Complex cbuf[10000]; String sbuf[10000]; char str[16]; for (uInt i=0; i<10000; i++) { if (i%3 == 0) { bbuf[i] = True; }else{ bbuf[i] = False; } ibuf[i] = i; cbuf[i] = Complex(i+1,i+2); snprintf (str, sizeof(str), "str %d", i); sbuf[i] = str; } StManArrayFile io("tStArrayFile_tmp.data", ByteIO::New, version, canonical); cout << "Length=" << io.length() << endl; l1 = io.putShape (IPosition(2,100,100), off1, static_cast(0)); cout << l1 << " " << off1 << endl; cout << "Length=" << io.length() << endl; //# Note that because the data is not written here (but a bit later), //# valgrind gives an uninitialized error when the buffer gets written. l3 = io.putShape (IPosition(2,2000,5), off3, static_cast(0)); cout << l3 << " " << off3 << endl; cout << "Length=" << io.length() << endl; if (version > 0) { io.putRefCount (5, off3); } io.put (off3+l3, 0, 3000, sbuf); cout << "Length=" << io.length() << endl; l2 = io.putShape (IPosition(1,10000), off2, static_cast(0)); cout << l2 << " " << off2 << endl; cout << "Length=" << io.length() << endl; io.put (off3+l3, 3000, 1024, sbuf+3000); cout << "Length=" << io.length() << endl; io.put (off2+l2, 0, 10000, cbuf); io.put (off1+l1, 0, 10000, ibuf); cout << "Length=" << io.length() << endl; io.put (off3+l3, 4024, 5976, sbuf+4024); cout << "Length=" << io.length() << endl; l4 = io.putShape (IPosition(2,1000,10), off4, static_cast(0)); cout << l4 << " " << off4 << endl; cout << "Length=" << io.length() << endl; io.put (off4+l4, 0, 10000, bbuf); } // Read back and update and copy some arrays. void b (Bool canonical, Int64 off1, Int64 off2, Int64 off3, Int64 off4, Int64& offc1, Int64& offc2, Int64& offc3, Int64& offc4) { StManArrayFile io("tStArrayFile_tmp.data", ByteIO::Update, 0, canonical); cout << "Length=" << io.length() << endl; IPosition shp, shp1, shp2, shp3, shp4; Int64 offs; uInt nref; Bool bbuf[10000]; Int ibuf[10000]; Complex cbuf[10000]; String sbuf[10000], sbufo[10000]; char str[16]; Int i; for (i=0; i<10000; i++) { snprintf (str, sizeof(str), "str %d", i); sbuf[i] = str; } uInt l1 = io.getShape (off1, shp); nref = io.getRefCount (off1); cout << l1 << " " << shp << " " << nref <(0)); cout << "copy to " << lc2 << " " << offc2 << endl; io.copyArrayComplex (offc2+lc2, off2+l2, shp2.product()); uInt lc3 = io.putShape (shp3, offc3, static_cast(0)); cout << "copy to " << lc3 << " " << offc3 << endl; io.copyArrayString (offc3+lc3, off3+l3, shp3.product()); uInt lc4 = io.putShape (shp4, offc4, static_cast(0)); cout << "copy to " << lc4 << " " << offc4 << endl; io.copyArrayBool (offc4+lc4, off4+l4, shp4.product()); } // Read back. void c (Bool canonical, Int64 off1, Int64 off2, Int64 off3, Int64 off4) { StManArrayFile io("tStArrayFile_tmp.data", ByteIO::Old, 0, canonical); cout << "Length=" << io.length() << endl; uInt nref; IPosition shp; Bool bbuf[10000]; Int ibuf[10000]; Complex cbuf[10000]; String sbuf[10000], sbufo[10000]; char str[16]; uInt i; for (i=0; i<10000; i++) { snprintf (str, sizeof(str), "str %d", i); sbuf[i] = str; } uInt l1 = io.getShape (off1, shp); nref = io.getRefCount (off1); cout << l1 << " " << shp << " " << nref << endl; uInt l2 = io.getShape (off2, shp); nref = io.getRefCount (off2); cout << l2 << " " << shp << " " << nref << endl; uInt l3 = io.getShape (off3, shp); nref = io.getRefCount (off3); cout << l3 << " " << shp << " " << nref << endl; uInt l4 = io.getShape (off4, shp); nref = io.getRefCount (off4); cout << l4 << " " << shp << " " << nref << endl; io.get (off4+l4, 0, 10000, bbuf); io.get (off3+l3, 0, 10000, sbufo); io.get (off1+l1, 0, 10000, ibuf); io.get (off2+l2, 0, 10000, cbuf); Int j; for (i=0; i<10000; i++) { j = i; if (i>0 && i<21) j = i-1; if (sbufo[i] != sbuf[j]) { cout << "Mismatch " << i << ": " << sbuf[j] << " " << sbufo[j] << endl; } } for (i=0; i<10000; i++) { Bool b = (i%3 == 0 ? True : False); j = i; if (i>=1 && i<21) { j = i-1; b = (j%3 == 0 ? True : False); } if (i == 23) { b = True; } if (i>=34 && i<38) { b = ((i-34)%3 == 0 ? True : False); } if (ibuf[i] != j || cbuf[i] != Complex(j+1,j+2) || bbuf[i] != b) cout << "mismatch in row " << i << ":" << ibuf[i] << " " << cbuf[i] << " " << bbuf[i] << endl; } } casacore-3.7.1/tables/DataMan/test/tStArrayFile.out000066400000000000000000000045121476623553700221660ustar00rootroot00000000000000test of StArrayFile with version 0 in canonical format Length=16 12 16 Length=40028 12 40032 Length=80044 Length=114934 8 114936 Length=194944 Length=207232 Length=207232 Length=278944 12 278944 Length=280206 Length=280206 12 [100, 100] 1 8 [10000] 1 12 [2000, 5] 1 12 [1000, 10] 1 Length=280206 12 280208 Length=280420 Length=280420 Length=280610 Length=280610 Length=280610 copy to 12 280616 copy to 8 320632 copy to 12 400640 copy to 12 559544 Length=560806 12 [100, 100] 1 8 [10000] 1 12 [2000, 5] 1 12 [1000, 10] 1 Length=560806 12 [100, 100] 1 8 [10000] 1 12 [2000, 5] 1 12 [1000, 10] 1 test of StArrayFile with version 0 in local format Length=16 12 16 Length=40028 12 40032 Length=80044 Length=114934 8 114936 Length=194944 Length=207232 Length=207232 Length=278944 12 278944 Length=280206 Length=280206 12 [100, 100] 1 8 [10000] 1 12 [2000, 5] 1 12 [1000, 10] 1 Length=280206 12 280208 Length=280420 Length=280420 Length=280610 Length=280610 Length=280610 copy to 12 280616 copy to 8 320632 copy to 12 400640 copy to 12 559544 Length=560806 12 [100, 100] 1 8 [10000] 1 12 [2000, 5] 1 12 [1000, 10] 1 Length=560806 12 [100, 100] 1 8 [10000] 1 12 [2000, 5] 1 12 [1000, 10] 1 test of StArrayFile with version 1 in canonical format Length=16 16 16 Length=40032 16 40032 Length=80048 Length=114938 12 114944 Length=194956 Length=207244 Length=207244 Length=278956 16 278960 Length=280226 Length=280226 16 [100, 100] 1 12 [10000] 1 16 [2000, 5] 5 16 [1000, 10] 1 Length=280226 16 280232 Length=280448 Length=280448 Length=280638 Length=280638 Length=280638 copy to 16 280640 copy to 12 320656 copy to 16 400672 copy to 16 559584 Length=560850 16 [100, 100] 1 12 [10000] 1 16 [2000, 5] 5 16 [1000, 10] 1 Length=560850 16 [100, 100] 1 12 [10000] 1 16 [2000, 5] 1 16 [1000, 10] 1 test of StArrayFile with version 1 in local format Length=16 16 16 Length=40032 16 40032 Length=80048 Length=114938 12 114944 Length=194956 Length=207244 Length=207244 Length=278956 16 278960 Length=280226 Length=280226 16 [100, 100] 1 12 [10000] 1 16 [2000, 5] 5 16 [1000, 10] 1 Length=280226 16 280232 Length=280448 Length=280448 Length=280638 Length=280638 Length=280638 copy to 16 280640 copy to 12 320656 copy to 16 400672 copy to 16 559584 Length=560850 16 [100, 100] 1 12 [10000] 1 16 [2000, 5] 5 16 [1000, 10] 1 Length=560850 16 [100, 100] 1 12 [10000] 1 16 [2000, 5] 1 16 [1000, 10] 1 casacore-3.7.1/tables/DataMan/test/tStMan.cc000066400000000000000000000644231476623553700206100ustar00rootroot00000000000000//# tStMan.cc: Test program for the various storage managers //# Copyright (C) 2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Test program for the various storage managers. // // Create and fill a new table. void newtab (uInt nrrow, const DataManager& stman) { // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.addColumn (ScalarColumnDesc("str1")); ScalarColumnDesc dstr2("str2"); dstr2.setMaxLength (20); td.addColumn (dstr2); td.addColumn (ArrayColumnDesc("stra1",IPosition(2,2,3), ColumnDesc::Direct)); ArrayColumnDesc dstra2("stra2",IPosition(2,2,3), ColumnDesc::Direct); dstra2.setMaxLength (20); td.addColumn (dstra2); td.addColumn (ArrayColumnDesc("stra3", -1, ColumnDesc::FixedShape)); ArrayColumnDesc dstra4("stra4"); dstra4.setMaxLength (20); td.addColumn (dstra4); td.addColumn (ScalarColumnDesc("b1")); td.addColumn (ArrayColumnDesc("ba1",IPosition(2,2,3), ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc("ba2", -1, ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc("ba3")); td.addColumn (ScalarColumnDesc("f1")); td.addColumn (ArrayColumnDesc("fa1",IPosition(2,2,3), ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc("fa2", -1, ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc("fa3")); td.addColumn (ScalarColumnDesc("dc1")); td.addColumn (ArrayColumnDesc("dca1",IPosition(2,2,3), ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc("dca2", -1, ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc("dca3")); // Now create a new table from the description. SetupNewTable newtab("tStMan_tmp.data", td, Table::New); // Create a storage manager for it. newtab.bindAll (stman); newtab.setShapeColumn("stra3",IPosition(2,2,3)); newtab.setShapeColumn("ba2",IPosition(2,2,3)); newtab.setShapeColumn("fa2",IPosition(2,2,3)); newtab.setShapeColumn("dca2",IPosition(2,2,3)); Table tab(newtab, nrrow); Array emptyArray(IPosition(2,2,3)); Array boolArray(IPosition(2,2,3), False); ScalarColumn str1 (tab, "str1"); ScalarColumn str2 (tab, "str2"); ArrayColumn stra1 (tab, "stra1"); ArrayColumn stra2 (tab, "stra2"); ArrayColumn stra3 (tab, "stra3"); ArrayColumn stra4 (tab, "stra4"); ScalarColumn b1 (tab, "b1"); ArrayColumn ba1 (tab, "ba1"); ArrayColumn ba2 (tab, "ba2"); ArrayColumn ba3 (tab, "ba3"); ScalarColumn f1 (tab, "f1"); ArrayColumn fa1 (tab, "fa1"); ArrayColumn fa2 (tab, "fa2"); ArrayColumn fa3 (tab, "fa3"); ScalarColumn dc1 (tab, "dc1"); ArrayColumn dca1 (tab, "dca1"); ArrayColumn dca2 (tab, "dca2"); ArrayColumn dca3 (tab, "dca3"); for (uInt i=0; i()); dca3.put (nrrow-1, Array(IPosition(2,2,0))); AlwaysAssertExit ( fa3.isDefined(nrrow-1)); AlwaysAssertExit ( dca3.isDefined(nrrow-1)); AlwaysAssertExit (! fa3.hasContent(nrrow-1)); AlwaysAssertExit (! dca3.hasContent(nrrow-1)); } void checktab1() { Table tab("tStMan_tmp.data"); Array emptyArray(IPosition(2,2,3)); ScalarColumn str1 (tab, "str1"); ScalarColumn str2 (tab, "str2"); ArrayColumn stra1 (tab, "stra1"); ArrayColumn stra2 (tab, "stra2"); ArrayColumn stra3 (tab, "stra3"); ArrayColumn stra4 (tab, "stra4"); ScalarColumn b1 (tab, "b1"); ArrayColumn ba1 (tab, "ba1"); ArrayColumn ba2 (tab, "ba2"); ArrayColumn ba3 (tab, "ba3"); ScalarColumn f1 (tab, "f1"); ArrayColumn fa1 (tab, "fa1"); ArrayColumn fa2 (tab, "fa2"); ArrayColumn fa3 (tab, "fa3"); ScalarColumn dc1 (tab, "dc1"); ArrayColumn dca1 (tab, "dca1"); ArrayColumn dca2 (tab, "dca2"); ArrayColumn dca3 (tab, "dca3"); uInt nrrow = tab.nrow(); for (uInt i=0; i emptyArray(IPosition(2,2,3)); Array filledArray(IPosition(2,2,3)); filledArray(IPosition(2,0,0)) = prefix + "str_00_"; filledArray(IPosition(2,0,1)) = "str_01_"; filledArray(IPosition(2,0,2)) = "str_02_"; filledArray(IPosition(2,1,0)) = "str_10_"; filledArray(IPosition(2,1,1)) = "str_11_"; filledArray(IPosition(2,1,2)) = "str_12_"; Array arrf(IPosition(2,2,3)); indgen (arrf); Array arrd(IPosition(2,4,3)); indgen (arrd); Array arrdc = RealToComplex (arrd); Array arrb = (fmod(arrf,float(4)) == float(0)); ScalarColumn str1 (tab, "str1"); ScalarColumn str2 (tab, "str2"); ArrayColumn stra1 (tab, "stra1"); ArrayColumn stra2 (tab, "stra2"); ArrayColumn stra3 (tab, "stra3"); ArrayColumn stra4 (tab, "stra4"); ScalarColumn b1 (tab, "b1"); ArrayColumn ba1 (tab, "ba1"); ArrayColumn ba2 (tab, "ba2"); ArrayColumn ba3 (tab, "ba3"); ScalarColumn f1 (tab, "f1"); ArrayColumn fa1 (tab, "fa1"); ArrayColumn fa2 (tab, "fa2"); ArrayColumn fa3 (tab, "fa3"); ScalarColumn dc1 (tab, "dc1"); ArrayColumn dca1 (tab, "dca1"); ArrayColumn dca2 (tab, "dca2"); ArrayColumn dca3 (tab, "dca3"); uInt nrrow = tab.nrow(); char buf[8]; { String s1(prefix + "str1_"); String s2(prefix + "str2_"); for (uInt i=0; i 20) { s2 = prefix + "str2_"; } AlwaysAssertExit (str1(i) == s1); AlwaysAssertExit (str2(i) == s2); AlwaysAssertExit (allEQ (stra1(i), String("a1_")+filledArray+String(buf))); AlwaysAssertExit (allEQ (stra1.getSlice (i, Slicer(IPosition(2,1,0), IPosition(2,1,2))), (String("a1_")+filledArray+String(buf)) (IPosition(2,1,0), IPosition(2,1,1)))); AlwaysAssertExit (allEQ (stra2(i), String("a2_")+filledArray+String(buf))); AlwaysAssertExit (allEQ (stra2.getSlice (i, Slicer(IPosition(2,1,0), IPosition(2,1,2))), (String("a2_")+filledArray+String(buf)) (IPosition(2,1,0), IPosition(2,1,1)))); AlwaysAssertExit (allEQ (stra3(i), String("a3_")+filledArray+String(buf))); AlwaysAssertExit (allEQ (stra3.getSlice (i, Slicer(IPosition(2,1,0), IPosition(2,1,2))), (String("a3_")+filledArray+String(buf)) (IPosition(2,1,0), IPosition(2,1,1)))); if (i%2 == 0) { AlwaysAssertExit (allEQ (stra4(i), String("a4_")+filledArray+String(buf))); AlwaysAssertExit (allEQ (stra4.getSlice (i, Slicer(IPosition(2,1,0), IPosition(2,1,2))), (String("a4_")+filledArray+String(buf)) (IPosition(2,1,0), IPosition(2,1,1)))); } else { AlwaysAssertExit (allEQ (stra4(i), emptyArray)); AlwaysAssertExit (allEQ (stra4.getSlice (i, Slicer(IPosition(2,1,0), IPosition(2,1,2))), emptyArray (IPosition(2,1,0), IPosition(2,1,1)))); } AlwaysAssertExit (b1(i) == (i%2==0)); AlwaysAssertExit (allEQ (ba1(i), arrb)); AlwaysAssertExit (allEQ (ba1.getSlice (i, Slicer(IPosition(2,1,0), IPosition(2,1,2))), arrb(IPosition(2,1,0), IPosition(2,1,1)))); AlwaysAssertExit (allEQ (ba2(i), !arrb)); AlwaysAssertExit (allEQ (ba2.getSlice (i, Slicer(IPosition(2,1,0), IPosition(2,1,2))), (!arrb)(IPosition(2,1,0), IPosition(2,1,1)))); AlwaysAssertExit (allEQ (ba3(i), arrb)); AlwaysAssertExit (allEQ (ba3.getSlice (i, Slicer(IPosition(2,1,0), IPosition(2,1,2))), arrb(IPosition(2,1,0), IPosition(2,1,1)))); AlwaysAssertExit (f1(i) == i+1); AlwaysAssertExit (allEQ (fa1(i), arrf)); AlwaysAssertExit (allEQ (fa1.getSlice (i, Slicer(IPosition(2,1,0), IPosition(2,1,2))), arrf(IPosition(2,1,0), IPosition(2,1,1)))); AlwaysAssertExit (allEQ (fa2(i), arrf+float(1))); AlwaysAssertExit (allEQ (fa2.getSlice (i, Slicer(IPosition(2,1,0), IPosition(2,1,2))), (arrf+float(1)) (IPosition(2,1,0), IPosition(2,1,1)))); AlwaysAssertExit (allEQ (fa3(i), arrf+float(2))); AlwaysAssertExit (allEQ (fa3.getSlice (i, Slicer(IPosition(2,1,0), IPosition(2,1,2))), (arrf+float(2)) (IPosition(2,1,0), IPosition(2,1,1)))); AlwaysAssertExit (dc1(i) == DComplex(i+1,i+2)); AlwaysAssertExit (allEQ (dca1(i), arrdc)); AlwaysAssertExit (allEQ (dca1.getSlice (i, Slicer(IPosition(2,1,0), IPosition(2,1,2))), arrdc(IPosition(2,1,0), IPosition(2,1,1)))); AlwaysAssertExit (allEQ (dca2(i), arrdc+DComplex(1,2))); AlwaysAssertExit (allEQ (dca2.getSlice (i, Slicer(IPosition(2,1,0), IPosition(2,1,2))), (arrdc+DComplex(1,2)) (IPosition(2,1,0), IPosition(2,1,1)))); AlwaysAssertExit (allEQ (dca3(i), arrdc+DComplex(3,4))); AlwaysAssertExit (allEQ (dca3.getSlice (i, Slicer(IPosition(2,1,0), IPosition(2,1,2))), (arrdc+DComplex(3,4)) (IPosition(2,1,0), IPosition(2,1,1)))); arrb = !arrb; arrf += float(10); arrdc += DComplex(10,20); } } { String s1(prefix + "str1_"); String s2(prefix + "str2_"); indgen (arrf); arrdc = RealToComplex (arrd); arrb = (fmod(arrf,float(4)) == float(0)); Vector vec1 = str1.getColumn(); Vector vec2 = str2.getColumn(); Array arr1 = stra1.getColumn().reform(IPosition(2,2,3*nrrow)); Array arr2 = stra2.getColumn().reform(IPosition(2,2,3*nrrow)); Array arr3 = stra3.getColumn().reform(IPosition(2,2,3*nrrow)); Array arr4 = stra4.getColumn().reform(IPosition(2,2,3*nrrow)); Vector bvec1 = b1.getColumn(); Array barr1 = ba1.getColumn().reform(IPosition(2,2,3*nrrow)); Array barr2 = ba2.getColumn().reform(IPosition(2,2,3*nrrow)); Array barr3 = ba3.getColumn().reform(IPosition(2,2,3*nrrow)); Vector fvec1 = f1.getColumn(); Array farr1 = fa1.getColumn().reform(IPosition(2,2,3*nrrow)); Array farr2 = fa2.getColumn().reform(IPosition(2,2,3*nrrow)); Array farr3 = fa3.getColumn().reform(IPosition(2,2,3*nrrow)); Vector dcvec1 = dc1.getColumn(); Array dcarr1 = dca1.getColumn().reform(IPosition(2,2,3*nrrow)); Array dcarr2 = dca2.getColumn().reform(IPosition(2,2,3*nrrow)); Array dcarr3 = dca3.getColumn().reform(IPosition(2,2,3*nrrow)); for (uInt i=0; i 20) { s2 = prefix + "str2_"; } AlwaysAssertExit (vec1(i) == s1); AlwaysAssertExit (vec2(i) == s2); AlwaysAssertExit (allEQ (arr1(IPosition(2,0,3*i), IPosition(2,1,3*i+2)), String("a1_")+filledArray+String(buf))); AlwaysAssertExit (allEQ (arr2(IPosition(2,0,3*i), IPosition(2,1,3*i+2)), String("a2_")+filledArray+String(buf))); AlwaysAssertExit (allEQ (arr3(IPosition(2,0,3*i), IPosition(2,1,3*i+2)), String("a3_")+filledArray+String(buf))); if (i%2 == 0) { AlwaysAssertExit (allEQ (arr4(IPosition(2,0,3*i),IPosition(2,1,3*i+2)), String("a4_")+filledArray+String(buf))); } else { AlwaysAssertExit (allEQ (arr4(IPosition(2,0,3*i),IPosition(2,1,3*i+2)), emptyArray)); } AlwaysAssertExit (bvec1(i) == (i%2==0)); AlwaysAssertExit (allEQ (barr1(IPosition(2,0,3*i), IPosition(2,1,3*i+2)), arrb)); AlwaysAssertExit (allEQ (barr2(IPosition(2,0,3*i), IPosition(2,1,3*i+2)), !arrb)); AlwaysAssertExit (allEQ (barr3(IPosition(2,0,3*i), IPosition(2,1,3*i+2)), arrb)); AlwaysAssertExit (fvec1(i) == i+1); AlwaysAssertExit (allEQ (farr1(IPosition(2,0,3*i), IPosition(2,1,3*i+2)), arrf)); AlwaysAssertExit (allEQ (farr2(IPosition(2,0,3*i), IPosition(2,1,3*i+2)), arrf+float(1))); AlwaysAssertExit (allEQ (farr3(IPosition(2,0,3*i), IPosition(2,1,3*i+2)), arrf+float(2))); AlwaysAssertExit (dcvec1(i) == DComplex(i+1,i+2)); AlwaysAssertExit (allEQ(dcarr1(IPosition(2,0,3*i), IPosition(2,1,3*i+2)), arrdc)); AlwaysAssertExit (allEQ(dcarr2(IPosition(2,0,3*i), IPosition(2,1,3*i+2)), arrdc+DComplex(1,2))); AlwaysAssertExit (allEQ(dcarr3(IPosition(2,0,3*i), IPosition(2,1,3*i+2)), arrdc+DComplex(3,4))); arrb = !arrb; arrf += float(10); arrdc += DComplex(10,20); } } { String s1(prefix + "str1_"); String s2(prefix + "str2_"); indgen (arrf); arrdc = RealToComplex (arrd); arrb = (fmod(arrf,float(4)) == float(0)); Array arr1 = stra1.getColumn(Slicer(IPosition(2,1,0), IPosition(2,1,2))). reform(IPosition(2,1,2*nrrow)); Array arr2 = stra2.getColumn(Slicer(IPosition(2,1,0), IPosition(2,1,2))). reform(IPosition(2,1,2*nrrow)); Array arr3 = stra3.getColumn(Slicer(IPosition(2,1,0), IPosition(2,1,2))). reform(IPosition(2,1,2*nrrow)); Array arr4 = stra4.getColumn(Slicer(IPosition(2,1,0), IPosition(2,1,2))). reform(IPosition(2,1,2*nrrow)); Array barr1 = ba1.getColumn(Slicer(IPosition(2,1,0), IPosition(2,1,2))). reform(IPosition(2,1,2*nrrow)); Array barr2 = ba2.getColumn(Slicer(IPosition(2,1,0), IPosition(2,1,2))). reform(IPosition(2,1,2*nrrow)); Array barr3 = ba3.getColumn(Slicer(IPosition(2,1,0), IPosition(2,1,2))). reform(IPosition(2,1,2*nrrow)); Array farr1 = fa1.getColumn(Slicer(IPosition(2,1,0), IPosition(2,1,2))). reform(IPosition(2,1,2*nrrow)); Array farr2 = fa2.getColumn(Slicer(IPosition(2,1,0), IPosition(2,1,2))). reform(IPosition(2,1,2*nrrow)); Array farr3 = fa3.getColumn(Slicer(IPosition(2,1,0), IPosition(2,1,2))). reform(IPosition(2,1,2*nrrow)); Array dcarr1 = dca1.getColumn(Slicer(IPosition(2,1,0), IPosition(2,1,2))). reform(IPosition(2,1,2*nrrow)); Array dcarr2 = dca2.getColumn(Slicer(IPosition(2,1,0), IPosition(2,1,2))). reform(IPosition(2,1,2*nrrow)); Array dcarr3 = dca3.getColumn(Slicer(IPosition(2,1,0), IPosition(2,1,2))). reform(IPosition(2,1,2*nrrow)); for (uInt i=0; i 20) { s2 = prefix + "str2_"; } AlwaysAssertExit (allEQ (arr1(IPosition(2,0,2*i), IPosition(2,0,2*i+1)), (String("a1_")+filledArray+String(buf)) (IPosition(2,1,0), IPosition(2,1,1)))); AlwaysAssertExit (allEQ (arr2(IPosition(2,0,2*i), IPosition(2,0,2*i+1)), (String("a2_")+filledArray+String(buf)) (IPosition(2,1,0), IPosition(2,1,1)))); AlwaysAssertExit (allEQ (arr3(IPosition(2,0,2*i), IPosition(2,0,2*i+1)), (String("a3_")+filledArray+String(buf)) (IPosition(2,1,0), IPosition(2,1,1)))); if (i%2 == 0) { AlwaysAssertExit (allEQ (arr4(IPosition(2,0,2*i),IPosition(2,0,2*i+1)), (String("a4_")+filledArray+String(buf)) (IPosition(2,1,0), IPosition(2,1,1)))); } else { AlwaysAssertExit (allEQ (arr4(IPosition(2,0,2*i),IPosition(2,0,2*i+1)), emptyArray (IPosition(2,1,0), IPosition(2,1,1)))); } AlwaysAssertExit (allEQ (barr1(IPosition(2,0,2*i), IPosition(2,0,2*i+1)), arrb(IPosition(2,1,0), IPosition(2,1,1)))); AlwaysAssertExit (allEQ (barr2(IPosition(2,0,2*i), IPosition(2,0,2*i+1)), (!arrb)(IPosition(2,1,0), IPosition(2,1,1)))); AlwaysAssertExit (allEQ (barr3(IPosition(2,0,2*i), IPosition(2,0,2*i+1)), arrb(IPosition(2,1,0), IPosition(2,1,1)))); AlwaysAssertExit (allEQ (farr1(IPosition(2,0,2*i), IPosition(2,0,2*i+1)), arrf(IPosition(2,1,0), IPosition(2,1,1)))); AlwaysAssertExit (allEQ (farr2(IPosition(2,0,2*i), IPosition(2,0,2*i+1)), (arrf+float(1)) (IPosition(2,1,0), IPosition(2,1,1)))); AlwaysAssertExit (allEQ (farr3(IPosition(2,0,2*i), IPosition(2,0,2*i+1)), (arrf+float(2)) (IPosition(2,1,0), IPosition(2,1,1)))); AlwaysAssertExit (allEQ(dcarr1(IPosition(2,0,2*i), IPosition(2,0,2*i+1)), arrdc(IPosition(2,1,0), IPosition(2,1,1)))); AlwaysAssertExit (allEQ(dcarr2(IPosition(2,0,2*i), IPosition(2,0,2*i+1)), (arrdc+DComplex(1,2)) (IPosition(2,1,0), IPosition(2,1,1)))); AlwaysAssertExit (allEQ(dcarr3(IPosition(2,0,2*i), IPosition(2,0,2*i+1)), (arrdc+DComplex(3,4)) (IPosition(2,1,0), IPosition(2,1,1)))); arrb = !arrb; arrf += float(10); arrdc += DComplex(10,20); } } } void extab (const String& prefix) { Table tab("tStMan_tmp.data", Table::Update); Array filledArray(IPosition(2,2,3)); filledArray(IPosition(2,0,0)) = prefix + "str_00_"; filledArray(IPosition(2,0,1)) = "str_01_"; filledArray(IPosition(2,0,2)) = "str_02_"; filledArray(IPosition(2,1,0)) = "str_10_"; filledArray(IPosition(2,1,1)) = "str_11_"; filledArray(IPosition(2,1,2)) = "str_12_"; Array arrf(IPosition(2,2,3)); indgen (arrf); Array arrd(IPosition(2,4,3)); indgen (arrd); Array arrdc = RealToComplex (arrd); Array arrb = (fmod(arrf,float(4)) == float(0)); ScalarColumn str1 (tab, "str1"); ScalarColumn str2 (tab, "str2"); ArrayColumn stra1 (tab, "stra1"); ArrayColumn stra2 (tab, "stra2"); ArrayColumn stra3 (tab, "stra3"); ArrayColumn stra4 (tab, "stra4"); ScalarColumn b1 (tab, "b1"); ArrayColumn ba1 (tab, "ba1"); ArrayColumn ba2 (tab, "ba2"); ArrayColumn ba3 (tab, "ba3"); ScalarColumn f1 (tab, "f1"); ArrayColumn fa1 (tab, "fa1"); ArrayColumn fa2 (tab, "fa2"); ArrayColumn fa3 (tab, "fa3"); ScalarColumn dc1 (tab, "dc1"); ArrayColumn dca1 (tab, "dca1"); ArrayColumn dca2 (tab, "dca2"); ArrayColumn dca3 (tab, "dca3"); char buf[8]; String s1(prefix + "str1_"); String s2(prefix + "str2_"); uInt nrrow = tab.nrow(); for (uInt i=0; i 20) { s2 = prefix + "str2_"; } str1.put (i, s1); str2.put (i, s2); stra1.put (i, String("a1_")+filledArray+String(buf)); stra2.put (i, String("a2_")+filledArray+String(buf)); stra3.put (i, String("a3_")+filledArray+String(buf)); if (i%2 == 0) { stra4.put (i, String("a4_")+filledArray+String(buf)); } else { stra4.setShape (i, filledArray.shape()); } b1.put(i, i%2==0); ba1.put (i, arrb); ba2.put (i, !arrb); ba3.put (i, arrb); f1.put(i, i+1); fa1.put (i, arrf); fa2.put (i, arrf+float(1)); fa3.put (i, arrf+float(2)); dc1.put(i, DComplex(i+1,i+2)); dca1.put (i, arrdc); dca2.put (i, arrdc+DComplex(1,2)); dca3.put (i, arrdc+DComplex(3,4)); arrb = !arrb; arrf += float(10); arrdc += DComplex(10,20); } checktab(prefix); } void doTest (uInt nrrow, const DataManager& stman) { newtab (nrrow, stman); checktab1(); for (uInt i=0; i<4; i++) { extab (""); checktab (""); } extab ("p"); checktab ("p"); } int main (int argc, const char* argv[]) { uInt nrrow = 10; uInt bucketSize = 500; if (argc > 1) { istringstream istr(argv[1]); istr >> nrrow; } if (argc > 2) { istringstream istr(argv[2]); istr >> bucketSize; } try { StManAipsIO st1; doTest (nrrow, st1); StandardStMan st2(max(bucketSize,500u)); doTest (nrrow, st2); IncrementalStMan st3(max(bucketSize,1000u), False); doTest (nrrow, st3); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } casacore-3.7.1/tables/DataMan/test/tStMan1.cc000066400000000000000000000127431476623553700206670ustar00rootroot00000000000000//# tStMan1.cc: Test program for performance of the various storage managers //# Copyright (C) 2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Test program for performance of the various storage managers. // // Read the table back. void readtab() { Timer timer; { Table tab("tStMan1_tmp.data"); uInt nrrow = tab.nrow(); timer.show ("table open "); ScalarColumn int1 (tab, "int1"); for (uInt i=0; i vec = int1.getColumn(); timer.show ("table get column "); for (uInt i=0; i("int1")); Timer timer; { // Now create a new table from the description. // Use copy constructor to test if it works fine. // (newtab and newtabcp have the same underlying object). SetupNewTable newtab("tStMan1_tmp.data", td, Table::New); // Create a storage manager for it. newtab.bindAll (stman); Table tab(newtab, nrrow); timer.show ("table rows creation "); ScalarColumn int1 (tab, "int1"); for (uInt i=0; i int1 (tab, "int1"); for (uInt i=0; i int1 (tab, "int1"); for (uInt i=0; i0 && i%flushnr == 0) { tab.flush(); } } timer.show ("table put/fl non-add"); } timer.show ("total + destructor "); } void doTest (uInt nrrow, const DataManager& stman, uInt flushnr) { newtab (nrrow, stman, flushnr); readtab(); } int main (int argc, const char* argv[]) { uInt nrrow = 100000; uInt bucketSize = 32768; uInt flushnr = 1000; if (argc > 1) { istringstream istr(argv[1]); istr >> nrrow; } if (argc > 2) { istringstream istr(argv[2]); istr >> bucketSize; } if (argc > 3) { istringstream istr(argv[3]); istr >> flushnr; } try { cout << "StManAipsIO" << endl; StManAipsIO st1; doTest (nrrow, st1, flushnr); cout << endl << "StandardStMan" << endl; StandardStMan st2(max(bucketSize,100u)); doTest (nrrow, st2, flushnr); cout << endl << "IncrementalStMan" << endl; IncrementalStMan st3(max(bucketSize,1000u), False); doTest (nrrow, st3, flushnr); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } casacore-3.7.1/tables/DataMan/test/tStManAll.cc000066400000000000000000001013031476623553700212260ustar00rootroot00000000000000//# tStManAll.cc: Test program for the various storage managers //# Copyright (C) 2018 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Test program for the various storage managers. // It tests if all storage managers work well for all data types and for all // possible get and put functions (defined in ArrayColumn). // It also tests it for ForwardColumn, VirtualTaQLColumn, ConcatTable and RefTable. // // Define a macro to execute a function for all column types. #define ExecFunc(funcName, tab, prefix) \ funcName (tab, prefix+"b", BoolArrays); \ funcName (tab, prefix+"uc", uCharArrays); \ funcName (tab, prefix+"s", ShortArrays); \ funcName (tab, prefix+"us", uShortArrays); \ funcName (tab, prefix+"i", IntArrays); \ funcName (tab, prefix+"ui", uIntArrays); \ funcName (tab, prefix+"i64", Int64Arrays); \ funcName (tab, prefix+"f", FloatArrays); \ funcName (tab, prefix+"d", DoubleArrays); \ funcName (tab, prefix+"cx", ComplexArrays); \ funcName (tab, prefix+"dcx", DComplexArrays); \ funcName (tab, prefix+"sf", FStringArrays); \ funcName (tab, prefix+"sv", VStringArrays); #define updateTable(funcName) \ { \ Table tab(table); \ if (! keepTable) { \ tab = Table("tStMan_tmp.data", Table::Update); \ } \ ExecFunc(funcName, tab, String()); \ } \ checktab (table); // Define globally all data arrays (one array per column; outer axis is row). // They get changed in the same way as the table data are changed. IPosition arrShapes[4]; Array BoolArrays[4]; Array uCharArrays[4]; Array ShortArrays[4]; Array uShortArrays[4]; Array IntArrays[4]; Array uIntArrays[4]; Array Int64Arrays[4]; Array FloatArrays[4]; Array DoubleArrays[4]; Array ComplexArrays[4]; Array DComplexArrays[4]; Array FStringArrays[4]; Array VStringArrays[4]; // Create an array and fill it using start value and increment. // Note that a Bool andf String array are specialized below. template Array makeArray (const IPosition& shape, T value, T incr) { Array arr(shape); indgen(arr, value, incr); return arr; } template<> Array makeArray (const IPosition& shape, Bool, Bool) { Array arr(shape); for (uInt i=0; i Array makeArray (const IPosition& shape, String value, String) { Array arr(shape); for (uInt i=0; i void incrArray (Array& arr, int incr, Bool) { arr += T(incr); } template<> void incrArray (Array& arr, int, Bool incr) { // Shift one to the left and put new value at the end. if (arr.size() > 0) { Bool deleteIt; Bool* p = arr.getStorage (deleteIt); for (uInt i=0; i void incrArray (Array& arr, int incr, Bool) { arr += String::toString(abs(incr)%10); } // Create all global arrays using the globally defined array shapes. void createArrays (uInt nrow) { for (int i=0; i<4; ++i) { IPosition shape = arrShapes[i].concatenate(IPosition(1,nrow)); BoolArrays[i].reference (makeArray(shape, True, True)); uCharArrays[i].reference (makeArray(shape, 0, 1)); ShortArrays[i].reference (makeArray(shape, -32768, 10)); uShortArrays[i].reference (makeArray(shape, 0, 10)); IntArrays[i].reference (makeArray(shape, -32768*65536, 100000)); uIntArrays[i].reference (makeArray(shape, 0, 100000)); Int64Arrays[i].reference (makeArray(shape, -6553600000L, 100000001)); FloatArrays[i].reference (makeArray(shape, -10.5, 1)); DoubleArrays[i].reference (makeArray(shape, -100.3, 22)); ComplexArrays[i].reference (makeArray(shape, Complex(-10.5,20), Complex(1,3.1))); DComplexArrays[i].reference (makeArray(shape, DComplex(-100.3,-5), DComplex(2,1.7))); FStringArrays[i].reference (makeArray(shape, String("maxstr"), String())); VStringArrays[i].reference (makeArray(shape, String(), String("varstr"))); } } // Add 4 columns for the given data type to the table description. // These are: Scalar, Direct Array, Indirect FixedShape Array, Indirect Array. template void addColDesc (TableDesc& td, const String& name, Bool addVirtual, uInt maxLength=0) { // Give the scalars the group name 'scalar'. ScalarColumnDesc s1(name+"s1", String(), String(), "scalar"); if (maxLength > 0) s1.setMaxLength (maxLength); td.addColumn (s1); ArrayColumnDesc a1(name+"a1", arrShapes[1], ColumnDesc::Direct); if (maxLength > 0) a1.setMaxLength (maxLength); td.addColumn (a1); ArrayColumnDesc a2(name+"a2", 2, ColumnDesc::FixedShape); if (maxLength > 0) a2.setMaxLength (maxLength); td.addColumn (a2); ArrayColumnDesc a3(name+"a3"); if (maxLength > 0) a3.setMaxLength (maxLength); td.addColumn (a3); if (addVirtual) { td.addColumn (ScalarColumnDesc("vt_" +name+"s1")); td.addColumn (ArrayColumnDesc("vt_"+name+"a1")); td.addColumn (ArrayColumnDesc("vt_"+name+"a2")); td.addColumn (ArrayColumnDesc("vt_"+name+"a3")); } } // Check if the columns contain defined values and content. // For TiledStMan arrays are always defined. // Optionally check the value (used for strings). template void checkDefined (Table& tab, const String& name, Bool tiled, const IPosition& shape=IPosition(), Bool checkValue=False) { ScalarColumn s1(tab, name+"s1"); ArrayColumn a1(tab, name+"a1"); ArrayColumn a2(tab, name+"a2"); ArrayColumn a3(tab, name+"a3"); for (uInt i=0; i(shape))); } } } template Bool testEQ (T v1, T v2) { return v1 == v2; } template<> Bool testEQ (Float v1, Float v2) { return near (v1, v2); } template<> Bool testEQ (Double v1, Double v2) { return near (v1, v2); } template Bool testEQ (std::complex v1, std::complex v2) { return near (v1, v2); } template Bool testEQ (Array v1, Array v2) { return allEQ (v1, v2); } template<> Bool testEQ (Array v1, Array v2) { return allNear (v1, v2, 1e-5); } template<> Bool testEQ (Array v1, Array v2) { return allNear (v1, v2, 1e-5); } template Bool testEQ (Array> v1, Array> v2) { return allNear (v1, v2, 1e-5); } // Write per row the data into scalar and array columns of a data type. template void writeRows (Table& tab, const String& name, const Array* values) { ScalarColumn s1(tab, name+"s1"); ArrayColumn a1(tab, name+"a1"); ArrayColumn a2(tab, name+"a2"); ArrayColumn a3(tab, name+"a3"); for (uInt i=0; i void checkRows (Table& tab, const String& name, const Array* values) { ScalarColumn s1(tab, name+"s1"); ArrayColumn a1(tab, name+"a1"); ArrayColumn a2(tab, name+"a2"); ArrayColumn a3(tab, name+"a3"); for (uInt i=0; i void writeColumns (Table& tab, const String& name, Array* values) { ScalarColumn s1(tab, name+"s1"); ArrayColumn a1(tab, name+"a1"); ArrayColumn a2(tab, name+"a2"); ArrayColumn a3(tab, name+"a3"); incrArray (values[0], -1, True); incrArray (values[1], 3, False); incrArray (values[2], -1, True); incrArray (values[3], 10, False); s1.putColumn (values[0]); a1.putColumn (values[1]); a2.putColumn (values[2]); a3.putColumn (values[3]); } // Check the full column data in scalar and array columns of a data type. template void checkColumns (Table& tab, const String& name, const Array* values) { ScalarColumn s1(tab, name+"s1"); ArrayColumn a1(tab, name+"a1"); ArrayColumn a2(tab, name+"a2"); ArrayColumn a3(tab, name+"a3"); AlwaysAssertExit (testEQ (s1.getColumn(), values[0])); AlwaysAssertExit (testEQ (a1.getColumn(), values[1])); AlwaysAssertExit (testEQ (a2.getColumn(), values[2])); AlwaysAssertExit (testEQ (a3.getColumn(), values[3])); } // Write a column range data into scalar and array columns of a data type. // The array values are updated. template void writeRange (Table& tab, const String& name, Array* values) { ScalarColumn s1(tab, name+"s1"); ArrayColumn a1(tab, name+"a1"); ArrayColumn a2(tab, name+"a2"); ArrayColumn a3(tab, name+"a3"); uInt start = 1; uInt end = tab.nrow() - 2; uInt incr = 3; RefRows rows(start, end, incr); Slicer rowSlicer(IPosition(1,start), IPosition(1,end), IPosition(1,incr), Slicer::endIsLast); IPosition arrend1(values[1].shape() - 1); arrend1[2] = end; Slicer slicer1(IPosition(3,0,0,start), arrend1, IPosition(3,1,1,incr), Slicer::endIsLast); IPosition arrend2(values[2].shape() - 1); arrend2[2] = end; Slicer slicer2(IPosition(3,0,0,start), arrend2, IPosition(3,1,1,incr), Slicer::endIsLast); IPosition arrend3(values[3].shape() - 1); arrend3[2] = end; Slicer slicer3(IPosition(3,0,0,start), arrend3, IPosition(3,1,1,incr), Slicer::endIsLast); Array arr0(values[0](rowSlicer)); Array arr1(values[1](slicer1)); Array arr2(values[2](slicer2)); Array arr3(values[3](slicer3)); incrArray (arr0, 2, False); incrArray (arr1, 2, True); incrArray (arr2, 3, False); incrArray (arr3, 1, True); s1.putColumnRange (rowSlicer, arr0); a1.putColumnCells (rows, arr1); a2.putColumnCells (rows, arr2); a3.putColumnCells (rows, arr3); } // Check a column range data in scalar and array columns of a data type. template void checkRange (Table& tab, const String& name, const Array* values) { ScalarColumn s1(tab, name+"s1"); ArrayColumn a1(tab, name+"a1"); ArrayColumn a2(tab, name+"a2"); ArrayColumn a3(tab, name+"a3"); uInt start = 1; uInt end = tab.nrow() - 2; uInt incr = 3; RefRows rows(start, end, incr); Slicer rowSlicer(IPosition(1,start), IPosition(1,end), IPosition(1,incr), Slicer::endIsLast); IPosition arrend1(values[1].shape() - 1); arrend1[2] = end; Slicer slicer1(IPosition(3,0,0,start), arrend1, IPosition(3,1,1,incr), Slicer::endIsLast); IPosition arrend2(values[2].shape() - 1); arrend2[2] = end; Slicer slicer2(IPosition(3,0,0,start), arrend2, IPosition(3,1,1,incr), Slicer::endIsLast); IPosition arrend3(values[3].shape() - 1); arrend3[2] = end; Slicer slicer3(IPosition(3,0,0,start), arrend3, IPosition(3,1,1,incr), Slicer::endIsLast); AlwaysAssertExit (testEQ (s1.getColumnRange(rowSlicer), values[0](rowSlicer))); AlwaysAssertExit (testEQ (a1.getColumnCells(rows), values[1](slicer1))); AlwaysAssertExit (testEQ (a2.getColumnCells(rows), values[2](slicer2))); AlwaysAssertExit (testEQ (a3.getColumnCells(rows), values[3](slicer3))); } // Write per row a data slice into array columns of a data type. template void writeRowSlice (Table& tab, const String& name, Array* values) { ArrayColumn a1(tab, name+"a1"); ArrayColumn a2(tab, name+"a2"); ArrayColumn a3(tab, name+"a3"); Slicer slicer1(IPosition(2,0), (values[1].shape()-2).getFirst(2), IPosition(2,2,3), Slicer::endIsLast); Slicer slicer2(IPosition(2,0), (values[2].shape()-2).getFirst(2), IPosition(2,1,2), Slicer::endIsLast); Slicer slicer3(IPosition(2,0), (values[3].shape()-2).getFirst(2), IPosition(2,1,1), Slicer::endIsLast); for (uInt i=0; i arr1(values[1][i](slicer1)); Array arr2(values[2][i](slicer2)); Array arr3(values[3][i](slicer3)); incrArray (arr1, 1, True); incrArray (arr2, 11, False); incrArray (arr3, 1, True); a1.putSlice (i, slicer1, arr1); a2.putSlice (i, slicer2, arr2); a3.putSlice (i, slicer3, arr3); } } // Check per row a data slice in scalar and array columns of a data type. template void checkRowSlice (Table& tab, const String& name, const Array* values) { ScalarColumn s1(tab, name+"s1"); ArrayColumn a1(tab, name+"a1"); ArrayColumn a2(tab, name+"a2"); ArrayColumn a3(tab, name+"a3"); Slicer slicer1(IPosition(2,0), (values[1].shape()-2).getFirst(2), IPosition(2,2,3), Slicer::endIsLast); Slicer slicer2(IPosition(2,0), (values[2].shape()-2).getFirst(2), IPosition(2,1,2), Slicer::endIsLast); Slicer slicer3(IPosition(2,0), (values[3].shape()-2).getFirst(2), IPosition(2,1,1), Slicer::endIsLast); for (uInt i=0; i void writeColumnSlice (Table& tab, const String& name, Array* values) { ArrayColumn a1(tab, name+"a1"); ArrayColumn a2(tab, name+"a2"); ArrayColumn a3(tab, name+"a3"); Slicer slicer1(IPosition(2,0), (values[1].shape()-1).getFirst(2), IPosition(2,2,3), Slicer::endIsLast); Slicer slicer2(IPosition(2,0), (values[2].shape()-1).getFirst(2), IPosition(2,1,2), Slicer::endIsLast); Slicer slicer3(IPosition(2,0), (values[3].shape()-1).getFirst(2), IPosition(2,1,1), Slicer::endIsLast); Slicer aslicer1(IPosition(3,0), values[1].shape()-1, IPosition(3,2,3,1), Slicer::endIsLast); Slicer aslicer2(IPosition(3,0), values[2].shape()-1, IPosition(3,1,2,1), Slicer::endIsLast); Slicer aslicer3(IPosition(3,0), values[3].shape()-1, IPosition(3,1,1,1), Slicer::endIsLast); Array arr1(values[1](aslicer1)); Array arr2(values[2](aslicer2)); Array arr3(values[3](aslicer3)); incrArray (arr1, -2, True); incrArray (arr2, 5, False); incrArray (arr3, 3, True); a1.putColumn (slicer1, arr1); a2.putColumn (slicer2, arr2); a3.putColumn (slicer3, arr3); } // Check a column data slice in array columns of a data type. template void checkColumnSlice (Table& tab, const String& name, const Array* values) { ArrayColumn a1(tab, name+"a1"); ArrayColumn a2(tab, name+"a2"); ArrayColumn a3(tab, name+"a3"); Slicer slicer1(IPosition(2,0), (values[1].shape()-1).getFirst(2), IPosition(2,2,3), Slicer::endIsLast); Slicer slicer2(IPosition(2,0), (values[2].shape()-1).getFirst(2), IPosition(2,1,2), Slicer::endIsLast); Slicer slicer3(IPosition(2,0), (values[3].shape()-1).getFirst(2), IPosition(2,1,1), Slicer::endIsLast); Slicer aslicer1(IPosition(3,0), values[1].shape()-1, IPosition(3,2,3,1), Slicer::endIsLast); Slicer aslicer2(IPosition(3,0), values[2].shape()-1, IPosition(3,1,2,1), Slicer::endIsLast); Slicer aslicer3(IPosition(3,0), values[3].shape()-1, IPosition(3,1,1,1), Slicer::endIsLast); AlwaysAssertExit (testEQ (a1.getColumn(slicer1), values[1](aslicer1))); AlwaysAssertExit (testEQ (a2.getColumn(slicer2), values[2](aslicer2))); AlwaysAssertExit (testEQ (a3.getColumn(slicer3), values[3](aslicer3))); } // Write a data slice range into array columns of a data type. template void writeRangeSlice (Table& tab, const String& name, const Array* values) { ArrayColumn a1(tab, name+"a1"); ArrayColumn a2(tab, name+"a2"); ArrayColumn a3(tab, name+"a3"); uInt start = 1; uInt end = tab.nrow() - 1; uInt incr = 2; RefRows rows(start, end, incr); Slicer slicer1(IPosition(2,1), (values[1].shape()-1).getFirst(2), IPosition(2,1,2), Slicer::endIsLast); Slicer slicer2(IPosition(2,2), (values[2].shape()-2).getFirst(2), IPosition(2,2,3), Slicer::endIsLast); Slicer slicer3(IPosition(2,0), (values[3].shape()-3).getFirst(2), IPosition(2,1,1), Slicer::endIsLast); IPosition arrend1(values[1].shape() - 1); arrend1[2] = end; IPosition arrend2(values[2].shape() - 2); arrend2[2] = end; IPosition arrend3(values[3].shape() - 3); arrend3[2] = end; Slicer aslicer1(IPosition(3,1,1,start), arrend1, IPosition(3,1,2,incr), Slicer::endIsLast); Slicer aslicer2(IPosition(3,2,2,start), arrend2, IPosition(3,2,3,incr), Slicer::endIsLast); Slicer aslicer3(IPosition(3,0,0,start), arrend3, IPosition(3,1,1,incr), Slicer::endIsLast); Array arr1(values[1](aslicer1)); Array arr2(values[2](aslicer2)); Array arr3(values[3](aslicer3)); incrArray (arr1, 3, True); incrArray (arr2, -11, False); incrArray (arr3, -4, True); a1.getColumnCells(rows, slicer1, arr1); a2.getColumnCells(rows, slicer2, arr2); a3.getColumnCells(rows, slicer3, arr3); } // Check a data slice range in array columns of a data type. template void checkRangeSlice (Table& tab, const String& name, const Array* values) { ArrayColumn a1(tab, name+"a1"); ArrayColumn a2(tab, name+"a2"); ArrayColumn a3(tab, name+"a3"); uInt start = 1; uInt end = tab.nrow() - 1; uInt incr = 2; RefRows rows(start, end, incr); Slicer slicer1(IPosition(2,1), (values[1].shape()-1).getFirst(2), IPosition(2,1,2), Slicer::endIsLast); Slicer slicer2(IPosition(2,2), (values[2].shape()-2).getFirst(2), IPosition(2,2,3), Slicer::endIsLast); Slicer slicer3(IPosition(2,0), (values[3].shape()-3).getFirst(2), IPosition(2,1,1), Slicer::endIsLast); IPosition arrend1(values[1].shape() - 1); arrend1[2] = end; IPosition arrend2(values[2].shape() - 2); arrend2[2] = end; IPosition arrend3(values[3].shape() - 3); arrend3[2] = end; Slicer aslicer1(IPosition(3,1,1,start), arrend1, IPosition(3,1,2,incr), Slicer::endIsLast); Slicer aslicer2(IPosition(3,2,2,start), arrend2, IPosition(3,2,3,incr), Slicer::endIsLast); Slicer aslicer3(IPosition(3,0,0,start), arrend3, IPosition(3,1,1,incr), Slicer::endIsLast); AlwaysAssertExit (testEQ (a1.getColumnCells(rows, slicer1), values[1](aslicer1))); AlwaysAssertExit (testEQ (a2.getColumnCells(rows, slicer2), values[2](aslicer2))); AlwaysAssertExit (testEQ (a3.getColumnCells(rows, slicer3), values[3](aslicer3))); } // Do a check in all kind of ways to test all get variants. template void checkAll (Table& tab, const String& name, const Array* values) { checkRows (tab, name, values); checkColumns (tab, name, values); checkRange (tab, name, values); checkRowSlice (tab, name, values); checkColumnSlice (tab, name, values); checkRangeSlice (tab, name, values); } // Bind a VirtualTaQLColumn. void bindVirtual (SetupNewTable& newtab, const String& name) { newtab.bindColumn ("vt_"+name+"s1", VirtualTaQLColumn(name+"s1")); newtab.bindColumn ("vt_"+name+"a1", VirtualTaQLColumn(name+"a1")); newtab.bindColumn ("vt_"+name+"a2", VirtualTaQLColumn(name+"a2")); newtab.bindColumn ("vt_"+name+"a3", VirtualTaQLColumn(name+"a3")); } // Create a new table and fill a few cells with an empty array. // An empty table name defaults to tStMan_tmp.data. Table maketab (uInt nrrow, const DataManager& stman, Bool tiled, const String& tabName=String(), Bool addVirtual=False) { // Build the table description. TableDesc td("", "1", TableDesc::Scratch); addColDesc (td, "b", addVirtual); addColDesc (td, "uc", addVirtual); addColDesc (td, "s", addVirtual); addColDesc (td, "us", addVirtual); addColDesc (td, "i", addVirtual); addColDesc (td, "ui", addVirtual); addColDesc (td, "i64", addVirtual); addColDesc (td, "f", addVirtual); addColDesc (td, "d", addVirtual); addColDesc (td, "cx", addVirtual); addColDesc (td, "dcx", addVirtual); addColDesc (td, "sv", addVirtual); // variable length string addColDesc (td, "sf", addVirtual, 40); // fixed length string // Now create a new table from the description. String tname(tabName.empty() ? "tStMan_tmp.data" : tabName); SetupNewTable newtab(tname, td, Table::New); // Bind all columns to the storage manager and set the fixed shapes. newtab.bindAll (stman); // The tiled storage manager does not support scalars nor strings, // so use StandardStMan for them. if (tiled) { StandardStMan ssm(1000); newtab.bindGroup ("scalar", ssm, True); newtab.bindColumn ("sva1", ssm); newtab.bindColumn ("sva2", ssm); newtab.bindColumn ("sva3", ssm); newtab.bindColumn ("sfa1", ssm); newtab.bindColumn ("sfa2", ssm); newtab.bindColumn ("sfa3", ssm); } if (addVirtual) { bindVirtual (newtab, "b"); bindVirtual (newtab, "uc"); bindVirtual (newtab, "s"); bindVirtual (newtab, "us"); bindVirtual (newtab, "i"); bindVirtual (newtab, "ui"); bindVirtual (newtab, "i64"); bindVirtual (newtab, "f"); bindVirtual (newtab, "d"); bindVirtual (newtab, "cx"); bindVirtual (newtab, "dcx"); bindVirtual (newtab, "sv"); bindVirtual (newtab, "sf"); } newtab.setShapeColumn("ba2", arrShapes[2]); newtab.setShapeColumn("uca2", arrShapes[2]); newtab.setShapeColumn("sa2", arrShapes[2]); newtab.setShapeColumn("usa2", arrShapes[2]); newtab.setShapeColumn("ia2", arrShapes[2]); newtab.setShapeColumn("uia2", arrShapes[2]); newtab.setShapeColumn("i64a2", arrShapes[2]); newtab.setShapeColumn("fa2", arrShapes[2]); newtab.setShapeColumn("da2", arrShapes[2]); newtab.setShapeColumn("cxa2", arrShapes[2]); newtab.setShapeColumn("dcxa2", arrShapes[2]); newtab.setShapeColumn("sva2", arrShapes[2]); newtab.setShapeColumn("sfa2", arrShapes[2]); Table tab(newtab, nrrow); // Check the columns for defined and content. checkDefined (tab, "b", tiled); checkDefined (tab, "uc", tiled); checkDefined (tab, "s", tiled); checkDefined (tab, "us", tiled); checkDefined (tab, "i", tiled); checkDefined (tab, "ui", tiled); checkDefined (tab, "i64", tiled); checkDefined (tab, "f", tiled); checkDefined (tab, "d", tiled); checkDefined (tab, "cx", tiled); checkDefined (tab, "dcx", tiled); checkDefined (tab, "sv", False, arrShapes[1], True); checkDefined (tab, "sf", False, arrShapes[1], True); // Write Bool arrays to avoid valgrind errors in Conversion::boolToBit. ScalarColumn bs1(tab, "bs1"); ArrayColumn ba1(tab, "ba1"); ArrayColumn ba2(tab, "ba2"); Array boolArray1(arrShapes[1], False); Array boolArray2(arrShapes[2], False); for (uInt i=0; i fa3(tab, "fa3"); ArrayColumn dca3(tab, "dcxa3"); fa3.put (nrrow-1, Array()); dca3.put (nrrow-1, Array(IPosition(2,2,0))); AlwaysAssertExit ( fa3.isDefined(nrrow-1)); AlwaysAssertExit ( dca3.isDefined(nrrow-1)); AlwaysAssertExit (! fa3.hasContent(nrrow-1)); AlwaysAssertExit (! dca3.hasContent(nrrow-1)); } return tab; } // Reopen the table and check the contents again. // This has to be done right after creating the table in function maketab. void checknewtab (const Table& table, uInt nrrow, Bool tiled) { Table tab(table); if (tab.isNull()) { tab = Table("tStMan_tmp.data"); } AlwaysAssertExit (tab.nrow() == nrrow); // Make a subset of all except the last row, because the float and DComplex // column contain an empty array in the last row. Vector rows(nrrow-1); indgen(rows); Table subtab(tab(rows)); // Check the columns for defined and content. checkDefined (tab, "b", tiled); checkDefined (tab, "uc", tiled); checkDefined (tab, "s", tiled); checkDefined (tab, "us", tiled); checkDefined (tab, "i", tiled); checkDefined (tab, "ui", tiled); checkDefined (tab, "i64", tiled); checkDefined (subtab, "f", tiled); checkDefined (tab, "d", tiled); checkDefined (tab, "cx", tiled); checkDefined (subtab, "dcx", tiled); checkDefined (tab, "sv", False, arrShapes[1], True); checkDefined (tab, "sf", False, arrShapes[1], True); // Check the last row where an empty array has been put. ArrayColumn fa3(tab, "fa3"); ArrayColumn dca3(tab, "dcxa3"); AlwaysAssertExit (fa3.isDefined(nrrow-1)); AlwaysAssertExit (dca3.isDefined(nrrow-1)); AlwaysAssertExit (fa3.hasContent(nrrow-1) == tiled); AlwaysAssertExit (dca3.hasContent(nrrow-1) == tiled); } void checktab (const Table& table) { Table tab(table); if (tab.isNull()) { tab = Table("tStMan_tmp.data"); } ExecFunc(checkAll, tab, String()); } void doTest (uInt nrrow, const DataManager& stman, Bool keepTable, Bool tiled, const Table& refTab = Table()) { // Create the table (if not given). Table table(refTab); if (table.isNull()) { table = maketab (nrrow, stman, tiled); } if (! keepTable) { table = Table(); // will not be done for MemoryStMan } // Check for defined and content. checknewtab (table, nrrow, tiled); // Write the data row by row and check the result. cout << " writeRows .." << endl; updateTable (writeRows); // Update the data by writing full columns. cout << " writeColumns .." << endl; updateTable (writeColumns); // Update data row range. cout << " writeRange .." << endl; updateTable (writeRange); // Update data slices row by row. cout << " writeRowSlice .." << endl; updateTable (writeRowSlice); // Update data column slices. cout << " writeColumnSlice .." << endl; updateTable (writeColumnSlice); // Update data slice ranges. cout << " writeRangeSlice .." << endl; updateTable (writeRangeSlice); } void testTaQLColumns() { } int main (int argc, const char* argv[]) { uInt nrrow = 10; uInt bucketSize = 1000; if (argc > 1) { istringstream istr(argv[1]); istr >> nrrow; } if (argc > 2) { istringstream istr(argv[2]); istr >> bucketSize; } try { // Define the shapes of the array columns; the first one is a scalar column. arrShapes[0] = IPosition(); arrShapes[1] = IPosition(2,3,4); arrShapes[2] = IPosition(2,5,7); arrShapes[3] = IPosition(2,11,23); createArrays (nrrow); cout << "Testing StManAipsIO ..." << endl; StManAipsIO st1; doTest (nrrow, st1, False, False); cout << "Testing StandardStMan ..." << endl; StandardStMan st2(max(bucketSize,1000u)); doTest (nrrow, st2, False, False); cout << "Testing IncrementalStMan ..." << endl; IncrementalStMan st3(max(bucketSize,5000u), False); doTest (nrrow, st3, False, False); cout << "Testing MemoryStMan ..." << endl; MemoryStMan st4; doTest (nrrow, st4, True, False); cout << "Testing TiledShapeStMan ..." << endl; // Need to be the same shape (because FixedShape columns are part of it). arrShapes[1] = IPosition(2,11,23); arrShapes[2] = IPosition(2,11,23); arrShapes[3] = IPosition(2,11,23); createArrays (nrrow); TiledShapeStMan st5("tiled", IPosition(3,20,20,20)); doTest (nrrow, st5, False, True); { cout << "Testing ForwardColumnEngine ..." << endl; // Test ForwardColumn. StandardStMan stman(2000); Table tab = maketab (nrrow, stman, False, "tStMan_tmp.datafc"); ForwardColumnEngine dataman(tab, "forwardcolumn"); doTest (nrrow, dataman, True, False); } { cout << "Testing RefTable ..." << endl; StandardStMan stman(2000); Table tab = maketab (nrrow, stman, False); Vector rows(tab.nrow()); indgen (rows); // Note that the second argument does not matter. doTest (tab.nrow(), stman, True, False, tab(rows)); // Test if underlying table is correct. ExecFunc(checkRows, tab, String()); } { cout << "Testing ConcatTable ..." << endl; StandardStMan stman(2000); Table tab = maketab (nrrow, stman, False); Table ctab(Block
      • (1, tab)); // Note that the second argument does not matter. doTest (tab.nrow(), stman, True, False, ctab); // Test if underlying table is correct. ExecFunc(checkRows, tab, String()); } { cout << "Testing VirtualTaQLColumn ..." << endl; StandardStMan stman(2000); Bool keepTable = False; Table table = maketab (nrrow, stman, keepTable, String(), True); updateTable (writeRows); ExecFunc(checkAll, table, String("vt_")); } } catch (const std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } casacore-3.7.1/tables/DataMan/test/tStandardStMan.cc000066400000000000000000000437271476623553700222750ustar00rootroot00000000000000//# tStandardStMan.cc: Test program for the StandardStMan storage manager //# Copyright (C) 2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Test program for the StandardStMan storage manager // // This program tests the StandardStMan storage manager, especially the // get and put functions. // The results are written to stdout. The script executing this program, // compares the results with the reference output file. // (re) open and write a few rows // aMode == 0 open new // aMode == 1 reopen existing void init (uInt aBucketSize,uInt aMode); // reopen table, and throw away a row void deleteRow(const uInt aRow); // reopen table, and throw away a few rows void deleteRows(const Vector& aNrRows); // delete a Column void deleteColumn(const String aColumn); // delete a column && put it back again void deleteAndRestore(); // add aColumn void addColumn(DataType aDataType); //add a few direct arrays void addDirectArrays(); //add an indirect string array void addIndStringArray(); //add an indirect array void addIndArray(); // show table info void info(const Table aTable); // put/putColumn cache test void putColumnTest(); // Test writing and updating an indirect array. void testInd() { cout << endl << "testInd ..." << endl; String tabName = "tStandardStMan_tmp.tabind"; Array arr1(IPosition(2,3,4)); Array arr2(IPosition(2,3,5)); indgen(arr1); indgen(arr2); { TableDesc td; td.addColumn (ArrayColumnDesc("col1")); SetupNewTable newt(tabName, td, Table::New); Table tab(newt, 2); ArrayColumn col(tab, "col1"); col.put (0, arr1); col.put (1, arr2); AlwaysAssertExit (allEQ (col(0), arr1)); AlwaysAssertExit (allEQ (col(1), arr2)); } File file(tabName + "/table.f0i"); cout << "size " << file.size() << endl; // Write differently sized arrays. for (int i=0; i<4; ++i) { Table tab(tabName, Table::Update); ArrayColumn col(tab, "col1"); col.put (i%2, arr2); col.put ((i+1)%2, arr1); tab.flush(); AlwaysAssertExit (allEQ (col((i+1)%2), arr1)); AlwaysAssertExit (allEQ (col(i%2), arr2)); cout << "size " << file.size() << endl; } } void testInd2() { cout << endl << "testInd2 ..." << endl; String tabName = "tStandardStMan_tmp.tabind2"; String colName = "Test"; TableRecord rec; rec.define("j","x"); { // set up a new table TableDesc td; td.addColumn(ScalarRecordColumnDesc(colName)); SetupNewTable newtab(tabName, td, Table::New); Table tab(newtab, TableLock::AutoLocking, 1, false); ScalarColumn recCol; recCol.attach(tab, colName); recCol.put(0, rec); TableRecord rec1; recCol.get(0, rec1); cout << "nrow " << tab.nrow() << endl; cout << "rec1 " << rec1; } File file(tabName + "/table.f0i"); cout << "size " << file.size() << endl; for (uInt i=0; i<4; ++i) { // overwrite the record with the identical record multiple times. Table tab(tabName, TableLock(TableLock::AutoLocking), Table::Update); ScalarColumn recCol; recCol.attach(tab, colName); recCol.put(0, rec); TableRecord rec1; recCol.get(0, rec1); tab.flush(); cout << "nrow " << tab.nrow() << endl; cout << "rec1 " << rec1; cout << "size " << file.size() << endl; } } int main (int argc, const char* argv[]) { ///DataManager::MAXROWNR32 = 0; uInt aNr = 250; if (argc > 1) { istringstream anIstr(argv[1]); anIstr >> aNr; } try { init (aNr,0); init (aNr,1); deleteRow (0); deleteRow (17); // delete and restore Column 1 (should use perfect fit) deleteAndRestore(); // putColumnTest(); // delete middle Column deleteColumn ("Col-2"); // add a Bool Column Should fit in freed space addColumn (TpBool); // add a DComplex Column Should use new index && space addColumn (TpDComplex); // delete first Column deleteColumn ("Col-1"); // delete last Column deleteColumn ("Col-3"); addDirectArrays (); addIndStringArray(); addIndArray (); Vector aNrRows(3); for (uInt i=0; i< 3; i++) { aNrRows(i) = i+3; } deleteRows (aNrRows); deleteColumn ("Col-7"); addColumn(TpString); // remove all remaining rows to check freebucket performance Vector aNewNrRows(15); for (uInt i=0; i< 15; i++) { aNewNrRows(i) = i; } deleteRows (aNewNrRows); // Test if writing an indirect array multiple times does not // increase the file size. testInd(); testInd2(); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } void initArrays(Cube& arrf, Vector& arrdc, Cube& arrb) { // The static_cast is a workaround for an SGI compiler bug indgen (static_cast< Cube &>(arrf)); arrdc(0) = DComplex(1.2, 3.4); arrdc(1) = DComplex(-2.3, 5.6); IPosition shape(arrb.shape()); uInt n = 0; for (Int i=0; i ad(aTable,aTable.tableDesc().columnNames()(i)); cout << ad.getColumn() << endl; } else { ScalarColumn aa(aTable,aTable.tableDesc().columnNames()(i)); cout << aa.getColumn() << endl; } } else if (aTable.tableDesc().columnDesc(i).dataType() == TpBool) { if (aTable.tableDesc().columnNames()(i) == "Col-8") { ArrayColumn ad(aTable,aTable.tableDesc().columnNames()(i)); cout << ad.getColumn() << endl; } else { ScalarColumn ab(aTable,aTable.tableDesc().columnNames()(i)); cout << ab.getColumn() << endl; } } else if (aTable.tableDesc().columnDesc(i).dataType() == TpDComplex) { if (aTable.tableDesc().columnNames()(i) == "Col-7") { ArrayColumn ad(aTable,aTable.tableDesc().columnNames()(i)); cout << ad.getColumn() << endl; } else { ScalarColumn ac(aTable,aTable.tableDesc().columnNames()(i)); cout << ac.getColumn() << endl; } } else if (aTable.tableDesc().columnDesc(i).dataType() == TpFloat) { if (aTable.tableDesc().columnNames()(i) == "Col-6") { ArrayColumn ad(aTable,aTable.tableDesc().columnNames()(i)); cout << ad.getColumn() << endl; } else { ScalarColumn ad(aTable,aTable.tableDesc().columnNames()(i)); cout << ad.getColumn() << endl; } } else if (aTable.tableDesc().columnDesc(i).dataType() == TpString) { if (aTable.tableDesc().columnNames()(i) == "Col-9") { ArrayColumn ad(aTable,aTable.tableDesc().columnNames()(i)); cout << ad.getColumn() << endl; } else { ScalarColumn ad(aTable,aTable.tableDesc().columnNames()(i)); cout << ad.getColumn() << endl; } } else { cout << "Sorry, datatype not implemented yet." << endl; } } } // First build a description. void init (uInt aBucketSize, uInt aMode) { Table aTable; if (aMode == 0) { DataManager::registerCtor ("StandardStMan", StandardStMan::makeObject); // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.comment() = "A test of class TableDesc"; td.addColumn (ScalarColumnDesc("Col-1")); td.addColumn (ScalarColumnDesc("Col-2")); td.addColumn (ScalarColumnDesc("Col-3")); // Now create a new table from the description. SetupNewTable aNewTab("tStandardStMan_tmp.data", td, Table::New); // Create a storage manager for it. StandardStMan aSm1 ("SSM", aBucketSize); aNewTab.bindAll (aSm1); aTable = Table (aNewTab, 10); } else { aTable = Table("tStandardStMan_tmp.data", Table::Update); } ScalarColumn aa(aTable,"Col-1"); ScalarColumn ab(aTable,"Col-2"); ScalarColumn ac(aTable,"Col-3"); // fill columns with data uInt i; uInt j = 0; for (i=0; i<10; i++) { if (aMode == 1) { aTable.addRow(); j=10; } DComplex a(i+j,(i+j)*2); aa.put(i+j,a); ab.put(i+j,i+j); Bool b; if ((i+j)%2 == 0) { b=True; } else { b=False; } ac.put(i+j,b); } if (aMode == 0) { cout << "after creation" << endl; } else { cout << "after reopening and adding 10 rows" << endl; } ROStandardStManAccessor anA(aTable,"SSM"); anA.showBaseStatistics(cout); anA.showIndexStatistics(cout); cout << endl << endl; // Print column data info(aTable); } void deleteRow(const uInt aRow) { Table aTable = Table("tStandardStMan_tmp.data", Table::Update); ScalarColumn aa(aTable,"Col-1"); // Make sure ColumnCache is filled. aa(0); aTable.removeRow(aRow); cout << "after removing row: " << aRow << endl; ROStandardStManAccessor anA(aTable,"SSM"); anA.showBaseStatistics(cout); anA.showIndexStatistics(cout); cout << endl << endl; // Print column data info(aTable); } void deleteRows(const Vector& aNrRows) { Table aTable = Table("tStandardStMan_tmp.data", Table::Update); ScalarColumn ae(aTable,"Col-5"); // Make sure ColumnCache is filled. ae(0); aTable.removeRow(aNrRows); cout << "after removing several rows at once: " << endl; ROStandardStManAccessor anA(aTable,"SSM"); anA.showBaseStatistics(cout); anA.showIndexStatistics(cout); cout << endl << endl; // Print column data info(aTable); } void deleteColumn(const String aColumn) { Table aTable = Table("tStandardStMan_tmp.data", Table::Update); cout << "Try to remove Column:" << aColumn << endl; aTable.removeColumn(aColumn); cout << "After removing Column: " << aColumn << endl; ROStandardStManAccessor anA(aTable,"SSM"); anA.showBaseStatistics(cout); anA.showIndexStatistics(cout); cout << endl << endl; // Print column data info(aTable); } void deleteAndRestore() { Table aTable = Table("tStandardStMan_tmp.data", Table::Update); ScalarColumn ac(aTable,"Col-1"); cout << "Try to remove Column 1 after saving the contents" << endl; Vector save = ac.getColumn(); aTable.removeColumn("Col-1"); cout << "After removing Column 1" << endl; ROStandardStManAccessor anA(aTable,"SSM"); anA.showBaseStatistics(cout); anA.showIndexStatistics(cout); cout << endl << endl; // Print column data info(aTable); cout << "Try to restore Column 1" << endl; aTable.addColumn(ScalarColumnDesc("Col-1")); if (aTable.tableDesc().isColumn("Col-1")) { ac.attach(aTable,"Col-1"); } // refill again ac.putColumn(save); cout << "After restoring Column1:" << endl; anA.showBaseStatistics(cout); anA.showIndexStatistics(cout); cout << endl << endl; // Print column data info(aTable); } void addColumn(DataType aDataType) { Table aTable = Table("tStandardStMan_tmp.data", Table::Update); ScalarColumn ad; ScalarColumn ae; ScalarColumn aj; switch (aDataType) { case TpBool: cout << "Try to add Column: Col-4 and fill it. "<< endl << "It Should be using space just freed up" << endl; aTable.addColumn(ScalarColumnDesc("Col-4")); if (aTable.tableDesc().isColumn("Col-4")) { ad.attach(aTable,"Col-4"); } // fill new column with data uInt i; Bool b; for (i=0; i("Col-5")); if (aTable.tableDesc().isColumn("Col-5")) { ae.attach(aTable,"Col-5"); } // fill new column with data for (uInt i=0; i("Col-10")); if (aTable.tableDesc().isColumn("Col-10")) { aj.attach(aTable,"Col-10"); } String aString("String-1"); // fill new column with data for (uInt i=0; i af; ArrayColumn ag; ArrayColumn ah; cout << "Trying to add a few Direct Array Columns." << endl; aTable.addColumn(ArrayColumnDesc("Col-6", IPosition(3,2,3,1), ColumnDesc::Direct)); aTable.addColumn(ArrayColumnDesc("Col-7", IPosition(1,2), ColumnDesc::Direct)); aTable.addColumn(ArrayColumnDesc("Col-8", IPosition(3,5,7,1), ColumnDesc::Direct)); Cube arrf(IPosition(3,2,3,1)); Vector arrdc(2); Cube arrb(IPosition(3,5,7,1)); initArrays (arrf, arrdc, arrb); if (aTable.tableDesc().isColumn("Col-6")) { af.attach(aTable,"Col-6"); } if (aTable.tableDesc().isColumn("Col-7")) { ag.attach(aTable,"Col-7"); } if (aTable.tableDesc().isColumn("Col-8")) { ah.attach(aTable,"Col-8"); } for (uInt i=0; i ai; cout << "Trying to add an indirect String Array Column." << endl; aTable.addColumn(ArrayColumnDesc("Col-9")); Vector arrs(5); arrs(0)="Start-1"; arrs(1)="Start-2"; arrs(2)="Start-3"; arrs(3)="Start-4"; arrs(4)="Start-5"; if (aTable.tableDesc().isColumn("Col-9")) { ai.attach(aTable,"Col-9"); } for (uInt i=0; i ak; cout << "Trying to add an indirect Array Column." << endl; aTable.addColumn(ArrayColumnDesc("Col-11")); Vector arrs(5); arrs(0)=1; arrs(1)=2; arrs(2)=3; arrs(3)=4; arrs(4)=5; if (aTable.tableDesc().isColumn("Col-11")) { ak.attach(aTable,"Col-11"); } for (uInt i=0; i ab(aTable,"Col-2"); // put value 3 in rownr 5 ab.put(5,3); AlwaysAssertExit(ab(5) == 3); // put value 4 in column ab.putColumn(ab.getColumn() + 1); AlwaysAssertExit (ab(5) == 4); } casacore-3.7.1/tables/DataMan/test/tStandardStMan.out000066400000000000000000002111501476623553700225020ustar00rootroot00000000000000after creation StandardStMan Base statistics: Nr of columns : 3 Nr of rows in the columns : 10 ColIndex[0] : 0 ColOffset[0] : 0 ColIndex[1] : 0 ColOffset[1] : 192 ColIndex[2] : 0 ColOffset[2] : 240 CacheSize : 2 Size of buckets : 250 Total buckets : 0 Total Index buckets : 0 1st Index bucket : -1 Index bucket offset : 0 last String bucket used : -1 Total free buckets : 0 1st free bucket : -1 StandardStMan index: 0 statistics: Index statistics: Entries used : 1 Rows Per bucket : 12 Nr of Columns : 3 BucketNr[0] : 0 - LastRow[0] : 9 Freespace entries: 1 Offset[0]: 242 - nrBytes[0]: 8 Col-1: DComplex [(0,0), (1,2), (2,4), (3,6), (4,8), (5,10), (6,12), (7,14), (8,16), (9,18)] Col-2: Int [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Col-3: Bool [1, 0, 1, 0, 1, 0, 1, 0, 1, 0] after reopening and adding 10 rows StandardStMan Base statistics: Nr of columns : 3 Nr of rows in the columns : 20 ColIndex[0] : 0 ColOffset[0] : 0 ColIndex[1] : 0 ColOffset[1] : 192 ColIndex[2] : 0 ColOffset[2] : 240 CacheSize : 2 Size of buckets : 250 Total buckets : 2 Total Index buckets : 1 1st Index bucket : 1 Index bucket offset : 0 last String bucket used : -1 Total free buckets : 0 1st free bucket : -1 StandardStMan index: 0 statistics: Index statistics: Entries used : 2 Rows Per bucket : 12 Nr of Columns : 3 BucketNr[0] : 0 - LastRow[0] : 11 BucketNr[1] : 2 - LastRow[1] : 19 Freespace entries: 1 Offset[0]: 242 - nrBytes[0]: 8 Col-1: DComplex [(0,0), (1,2), (2,4), (3,6), (4,8), (5,10), (6,12), (7,14), (8,16), (9,18), (10,20), (11,22), (12,24), (13,26), (14,28), (15,30), (16,32), (17,34), (18,36), (19,38)] Col-2: Int [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] Col-3: Bool [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0] after removing row: 0 StandardStMan Base statistics: Nr of columns : 3 Nr of rows in the columns : 19 ColIndex[0] : 0 ColOffset[0] : 0 ColIndex[1] : 0 ColOffset[1] : 192 ColIndex[2] : 0 ColOffset[2] : 240 CacheSize : 2 Size of buckets : 250 Total buckets : 4 Total Index buckets : 1 1st Index bucket : 3 Index bucket offset : 0 last String bucket used : -1 Total free buckets : 1 1st free bucket : 1 StandardStMan index: 0 statistics: Index statistics: Entries used : 2 Rows Per bucket : 12 Nr of Columns : 3 BucketNr[0] : 0 - LastRow[0] : 10 BucketNr[1] : 2 - LastRow[1] : 18 Freespace entries: 1 Offset[0]: 242 - nrBytes[0]: 8 Col-1: DComplex [(1,2), (2,4), (3,6), (4,8), (5,10), (6,12), (7,14), (8,16), (9,18), (10,20), (11,22), (12,24), (13,26), (14,28), (15,30), (16,32), (17,34), (18,36), (19,38)] Col-2: Int [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] Col-3: Bool [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0] after removing row: 17 StandardStMan Base statistics: Nr of columns : 3 Nr of rows in the columns : 18 ColIndex[0] : 0 ColOffset[0] : 0 ColIndex[1] : 0 ColOffset[1] : 192 ColIndex[2] : 0 ColOffset[2] : 240 CacheSize : 2 Size of buckets : 250 Total buckets : 4 Total Index buckets : 1 1st Index bucket : 1 Index bucket offset : 0 last String bucket used : -1 Total free buckets : 1 1st free bucket : 3 StandardStMan index: 0 statistics: Index statistics: Entries used : 2 Rows Per bucket : 12 Nr of Columns : 3 BucketNr[0] : 0 - LastRow[0] : 10 BucketNr[1] : 2 - LastRow[1] : 17 Freespace entries: 1 Offset[0]: 242 - nrBytes[0]: 8 Col-1: DComplex [(1,2), (2,4), (3,6), (4,8), (5,10), (6,12), (7,14), (8,16), (9,18), (10,20), (11,22), (12,24), (13,26), (14,28), (15,30), (16,32), (17,34), (19,38)] Col-2: Int [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 19] Col-3: Bool [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0] Try to remove Column 1 after saving the contents After removing Column 1 StandardStMan Base statistics: Nr of columns : 2 Nr of rows in the columns : 18 ColIndex[0] : 0 ColOffset[0] : 192 ColIndex[1] : 0 ColOffset[1] : 240 CacheSize : 2 Size of buckets : 250 Total buckets : 4 Total Index buckets : 1 1st Index bucket : 3 Index bucket offset : 0 last String bucket used : -1 Total free buckets : 1 1st free bucket : 1 StandardStMan index: 0 statistics: Index statistics: Entries used : 2 Rows Per bucket : 12 Nr of Columns : 2 BucketNr[0] : 0 - LastRow[0] : 10 BucketNr[1] : 2 - LastRow[1] : 17 Freespace entries: 2 Offset[0]: 0 - nrBytes[0]: 192 Offset[1]: 242 - nrBytes[1]: 8 Col-2: Int [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 19] Col-3: Bool [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0] Try to restore Column 1 After restoring Column1: StandardStMan Base statistics: Nr of columns : 3 Nr of rows in the columns : 18 ColIndex[0] : 0 ColOffset[0] : 192 ColIndex[1] : 0 ColOffset[1] : 240 ColIndex[2] : 0 ColOffset[2] : 0 CacheSize : 2 Size of buckets : 250 Total buckets : 4 Total Index buckets : 1 1st Index bucket : 3 Index bucket offset : 0 last String bucket used : -1 Total free buckets : 1 1st free bucket : 1 StandardStMan index: 0 statistics: Index statistics: Entries used : 2 Rows Per bucket : 12 Nr of Columns : 3 BucketNr[0] : 0 - LastRow[0] : 10 BucketNr[1] : 2 - LastRow[1] : 17 Freespace entries: 1 Offset[0]: 242 - nrBytes[0]: 8 Col-2: Int [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 19] Col-3: Bool [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0] Col-1: DComplex [(1,2), (2,4), (3,6), (4,8), (5,10), (6,12), (7,14), (8,16), (9,18), (10,20), (11,22), (12,24), (13,26), (14,28), (15,30), (16,32), (17,34), (19,38)] Try to remove Column:Col-2 After removing Column: Col-2 StandardStMan Base statistics: Nr of columns : 2 Nr of rows in the columns : 18 ColIndex[0] : 0 ColOffset[0] : 240 ColIndex[1] : 0 ColOffset[1] : 0 CacheSize : 2 Size of buckets : 250 Total buckets : 4 Total Index buckets : 1 1st Index bucket : 3 Index bucket offset : 0 last String bucket used : -1 Total free buckets : 1 1st free bucket : 1 StandardStMan index: 0 statistics: Index statistics: Entries used : 2 Rows Per bucket : 12 Nr of Columns : 2 BucketNr[0] : 0 - LastRow[0] : 10 BucketNr[1] : 2 - LastRow[1] : 17 Freespace entries: 2 Offset[0]: 192 - nrBytes[0]: 48 Offset[1]: 242 - nrBytes[1]: 8 Col-3: Bool [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0] Col-1: DComplex [(1,2), (2,4), (3,6), (4,8), (5,10), (6,12), (7,14), (8,16), (9,18), (10,20), (11,22), (12,24), (13,26), (14,28), (15,30), (16,32), (17,34), (19,38)] Try to add Column: Col-4 and fill it. It Should be using space just freed up StandardStMan Base statistics: Nr of columns : 3 Nr of rows in the columns : 18 ColIndex[0] : 0 ColOffset[0] : 240 ColIndex[1] : 0 ColOffset[1] : 0 ColIndex[2] : 0 ColOffset[2] : 242 CacheSize : 2 Size of buckets : 250 Total buckets : 4 Total Index buckets : 1 1st Index bucket : 1 Index bucket offset : 0 last String bucket used : -1 Total free buckets : 1 1st free bucket : 3 StandardStMan index: 0 statistics: Index statistics: Entries used : 2 Rows Per bucket : 12 Nr of Columns : 3 BucketNr[0] : 0 - LastRow[0] : 10 BucketNr[1] : 2 - LastRow[1] : 17 Freespace entries: 2 Offset[0]: 192 - nrBytes[0]: 48 Offset[1]: 244 - nrBytes[1]: 6 Col-3: Bool [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0] Col-1: DComplex [(1,2), (2,4), (3,6), (4,8), (5,10), (6,12), (7,14), (8,16), (9,18), (10,20), (11,22), (12,24), (13,26), (14,28), (15,30), (16,32), (17,34), (19,38)] Col-4: Bool [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] Try to add Column: Col-5 and fill it. It should make a new index because there's not enough free space StandardStMan Base statistics: Nr of columns : 4 Nr of rows in the columns : 18 ColIndex[0] : 0 ColOffset[0] : 240 ColIndex[1] : 0 ColOffset[1] : 0 ColIndex[2] : 0 ColOffset[2] : 242 ColIndex[3] : 1 ColOffset[3] : 0 CacheSize : 2 Size of buckets : 250 Total buckets : 4 Total Index buckets : 1 1st Index bucket : 3 Index bucket offset : 0 last String bucket used : -1 Total free buckets : 1 1st free bucket : 1 StandardStMan index: 0 statistics: Index statistics: Entries used : 2 Rows Per bucket : 12 Nr of Columns : 3 BucketNr[0] : 0 - LastRow[0] : 10 BucketNr[1] : 2 - LastRow[1] : 17 Freespace entries: 2 Offset[0]: 192 - nrBytes[0]: 48 Offset[1]: 244 - nrBytes[1]: 6 StandardStMan index: 1 statistics: Index statistics: Entries used : 2 Rows Per bucket : 15 Nr of Columns : 1 BucketNr[0] : 1 - LastRow[0] : 14 BucketNr[1] : 4 - LastRow[1] : 17 Freespace entries: 1 Offset[0]: 240 - nrBytes[0]: 10 Col-3: Bool [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0] Col-1: DComplex [(1,2), (2,4), (3,6), (4,8), (5,10), (6,12), (7,14), (8,16), (9,18), (10,20), (11,22), (12,24), (13,26), (14,28), (15,30), (16,32), (17,34), (19,38)] Col-4: Bool [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] Col-5: DComplex [(18,0), (17,1), (16,2), (15,3), (14,4), (13,5), (12,6), (11,7), (10,8), (9,9), (8,10), (7,11), (6,12), (5,13), (4,14), (3,15), (2,16), (1,17)] Try to remove Column:Col-1 After removing Column: Col-1 StandardStMan Base statistics: Nr of columns : 3 Nr of rows in the columns : 18 ColIndex[0] : 0 ColOffset[0] : 240 ColIndex[1] : 0 ColOffset[1] : 242 ColIndex[2] : 1 ColOffset[2] : 0 CacheSize : 2 Size of buckets : 250 Total buckets : 7 Total Index buckets : 2 1st Index bucket : 6 Index bucket offset : 0 last String bucket used : -1 Total free buckets : 1 1st free bucket : 3 StandardStMan index: 0 statistics: Index statistics: Entries used : 2 Rows Per bucket : 12 Nr of Columns : 2 BucketNr[0] : 0 - LastRow[0] : 10 BucketNr[1] : 2 - LastRow[1] : 17 Freespace entries: 2 Offset[0]: 0 - nrBytes[0]: 240 Offset[1]: 244 - nrBytes[1]: 6 StandardStMan index: 1 statistics: Index statistics: Entries used : 2 Rows Per bucket : 15 Nr of Columns : 1 BucketNr[0] : 1 - LastRow[0] : 14 BucketNr[1] : 4 - LastRow[1] : 17 Freespace entries: 1 Offset[0]: 240 - nrBytes[0]: 10 Col-3: Bool [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0] Col-4: Bool [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] Col-5: DComplex [(18,0), (17,1), (16,2), (15,3), (14,4), (13,5), (12,6), (11,7), (10,8), (9,9), (8,10), (7,11), (6,12), (5,13), (4,14), (3,15), (2,16), (1,17)] Try to remove Column:Col-3 After removing Column: Col-3 StandardStMan Base statistics: Nr of columns : 2 Nr of rows in the columns : 18 ColIndex[0] : 0 ColOffset[0] : 242 ColIndex[1] : 1 ColOffset[1] : 0 CacheSize : 2 Size of buckets : 250 Total buckets : 8 Total Index buckets : 2 1st Index bucket : 7 Index bucket offset : 0 last String bucket used : -1 Total free buckets : 2 1st free bucket : 5 StandardStMan index: 0 statistics: Index statistics: Entries used : 2 Rows Per bucket : 12 Nr of Columns : 1 BucketNr[0] : 0 - LastRow[0] : 10 BucketNr[1] : 2 - LastRow[1] : 17 Freespace entries: 2 Offset[0]: 0 - nrBytes[0]: 242 Offset[1]: 244 - nrBytes[1]: 6 StandardStMan index: 1 statistics: Index statistics: Entries used : 2 Rows Per bucket : 15 Nr of Columns : 1 BucketNr[0] : 1 - LastRow[0] : 14 BucketNr[1] : 4 - LastRow[1] : 17 Freespace entries: 1 Offset[0]: 240 - nrBytes[0]: 10 Col-4: Bool [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] Col-5: DComplex [(18,0), (17,1), (16,2), (15,3), (14,4), (13,5), (12,6), (11,7), (10,8), (9,9), (8,10), (7,11), (6,12), (5,13), (4,14), (3,15), (2,16), (1,17)] Trying to add a few Direct Array Columns. StandardStMan Base statistics: Nr of columns : 5 Nr of rows in the columns : 18 ColIndex[0] : 0 ColOffset[0] : 242 ColIndex[1] : 1 ColOffset[1] : 0 ColIndex[2] : 2 ColOffset[2] : 0 ColIndex[3] : 3 ColOffset[3] : 0 ColIndex[4] : 0 ColOffset[4] : 0 CacheSize : 2 Size of buckets : 250 Total buckets : 8 Total Index buckets : 2 1st Index bucket : 6 Index bucket offset : 0 last String bucket used : -1 Total free buckets : 2 1st free bucket : 3 StandardStMan index: 0 statistics: Index statistics: Entries used : 2 Rows Per bucket : 12 Nr of Columns : 2 BucketNr[0] : 0 - LastRow[0] : 10 BucketNr[1] : 2 - LastRow[1] : 17 Freespace entries: 2 Offset[0]: 53 - nrBytes[0]: 189 Offset[1]: 244 - nrBytes[1]: 6 StandardStMan index: 1 statistics: Index statistics: Entries used : 2 Rows Per bucket : 15 Nr of Columns : 1 BucketNr[0] : 1 - LastRow[0] : 14 BucketNr[1] : 4 - LastRow[1] : 17 Freespace entries: 1 Offset[0]: 240 - nrBytes[0]: 10 StandardStMan index: 2 statistics: Index statistics: Entries used : 2 Rows Per bucket : 10 Nr of Columns : 1 BucketNr[0] : 3 - LastRow[0] : 9 BucketNr[1] : 7 - LastRow[1] : 17 Freespace entries: 1 Offset[0]: 240 - nrBytes[0]: 10 StandardStMan index: 3 statistics: Index statistics: Entries used : 3 Rows Per bucket : 7 Nr of Columns : 1 BucketNr[0] : 8 - LastRow[0] : 6 BucketNr[1] : 9 - LastRow[1] : 13 BucketNr[2] : 10 - LastRow[2] : 17 Freespace entries: 1 Offset[0]: 224 - nrBytes[0]: 26 Col-4: Bool [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] Col-5: DComplex [(18,0), (17,1), (16,2), (15,3), (14,4), (13,5), (12,6), (11,7), (10,8), (9,9), (8,10), (7,11), (6,12), (5,13), (4,14), (3,15), (2,16), (1,17)] Col-6: float Ndim=4 Axis Lengths: [2, 3, 1, 18] [0, 0, 0, 0][0, 1] [0, 1, 0, 0][2, 3] [0, 2, 0, 0][4, 5] [0, 0, 0, 1][6, 7] [0, 1, 0, 1][8, 9] [0, 2, 0, 1][10, 11] [0, 0, 0, 2][12, 13] [0, 1, 0, 2][14, 15] [0, 2, 0, 2][16, 17] [0, 0, 0, 3][18, 19] [0, 1, 0, 3][20, 21] [0, 2, 0, 3][22, 23] [0, 0, 0, 4][24, 25] [0, 1, 0, 4][26, 27] [0, 2, 0, 4][28, 29] [0, 0, 0, 5][30, 31] [0, 1, 0, 5][32, 33] [0, 2, 0, 5][34, 35] [0, 0, 0, 6][36, 37] [0, 1, 0, 6][38, 39] [0, 2, 0, 6][40, 41] [0, 0, 0, 7][42, 43] [0, 1, 0, 7][44, 45] [0, 2, 0, 7][46, 47] [0, 0, 0, 8][48, 49] [0, 1, 0, 8][50, 51] [0, 2, 0, 8][52, 53] [0, 0, 0, 9][54, 55] [0, 1, 0, 9][56, 57] [0, 2, 0, 9][58, 59] [0, 0, 0, 10][60, 61] [0, 1, 0, 10][62, 63] [0, 2, 0, 10][64, 65] [0, 0, 0, 11][66, 67] [0, 1, 0, 11][68, 69] [0, 2, 0, 11][70, 71] [0, 0, 0, 12][72, 73] [0, 1, 0, 12][74, 75] [0, 2, 0, 12][76, 77] [0, 0, 0, 13][78, 79] [0, 1, 0, 13][80, 81] [0, 2, 0, 13][82, 83] [0, 0, 0, 14][84, 85] [0, 1, 0, 14][86, 87] [0, 2, 0, 14][88, 89] [0, 0, 0, 15][90, 91] [0, 1, 0, 15][92, 93] [0, 2, 0, 15][94, 95] [0, 0, 0, 16][96, 97] [0, 1, 0, 16][98, 99] [0, 2, 0, 16][100, 101] [0, 0, 0, 17][102, 103] [0, 1, 0, 17][104, 105] [0, 2, 0, 17][106, 107] Col-7: DComplex Axis Lengths: [2, 18] (NB: Matrix in Row/Column order) [(1.2,3.4), (3.2,6.4), (5.2,9.4), (7.2,12.4), (9.2,15.4), (11.2,18.4), (13.2,21.4), (15.2,24.4), (17.2,27.4), (19.2,30.4), (21.2,33.4), (23.2,36.4), (25.2,39.4), (27.2,42.4), (29.2,45.4), (31.2,48.4), (33.2,51.4), (35.2,54.4) (-2.3,5.6), (-0.3,8.6), (1.7,11.6), (3.7,14.6), (5.7,17.6), (7.7,20.6), (9.7,23.6), (11.7,26.6), (13.7,29.6), (15.7,32.6), (17.7,35.6), (19.7,38.6), (21.7,41.6), (23.7,44.6), (25.7,47.6), (27.7,50.6), (29.7,53.6), (31.7,56.6)] Col-8: Bool Ndim=4 Axis Lengths: [5, 7, 1, 18] [0, 0, 0, 0][0, 0, 1, 0, 0] [0, 1, 0, 0][1, 0, 0, 1, 0] [0, 2, 0, 0][0, 1, 0, 0, 1] [0, 3, 0, 0][0, 0, 1, 0, 0] [0, 4, 0, 0][1, 0, 0, 1, 0] [0, 5, 0, 0][0, 1, 0, 0, 1] [0, 6, 0, 0][0, 0, 1, 0, 0] [0, 0, 0, 1][0, 0, 1, 0, 0] [0, 1, 0, 1][1, 0, 0, 1, 0] [0, 2, 0, 1][0, 1, 0, 0, 1] [0, 3, 0, 1][0, 0, 1, 0, 0] [0, 4, 0, 1][1, 0, 0, 1, 0] [0, 5, 0, 1][0, 1, 0, 0, 1] [0, 6, 0, 1][0, 0, 1, 0, 0] [0, 0, 0, 2][0, 0, 1, 0, 0] [0, 1, 0, 2][1, 0, 0, 1, 0] [0, 2, 0, 2][0, 1, 0, 0, 1] [0, 3, 0, 2][0, 0, 1, 0, 0] [0, 4, 0, 2][1, 0, 0, 1, 0] [0, 5, 0, 2][0, 1, 0, 0, 1] [0, 6, 0, 2][0, 0, 1, 0, 0] [0, 0, 0, 3][0, 0, 1, 0, 0] [0, 1, 0, 3][1, 0, 0, 1, 0] [0, 2, 0, 3][0, 1, 0, 0, 1] [0, 3, 0, 3][0, 0, 1, 0, 0] [0, 4, 0, 3][1, 0, 0, 1, 0] [0, 5, 0, 3][0, 1, 0, 0, 1] [0, 6, 0, 3][0, 0, 1, 0, 0] [0, 0, 0, 4][0, 0, 1, 0, 0] [0, 1, 0, 4][1, 0, 0, 1, 0] [0, 2, 0, 4][0, 1, 0, 0, 1] [0, 3, 0, 4][0, 0, 1, 0, 0] [0, 4, 0, 4][1, 0, 0, 1, 0] [0, 5, 0, 4][0, 1, 0, 0, 1] [0, 6, 0, 4][0, 0, 1, 0, 0] [0, 0, 0, 5][0, 0, 1, 0, 0] [0, 1, 0, 5][1, 0, 0, 1, 0] [0, 2, 0, 5][0, 1, 0, 0, 1] [0, 3, 0, 5][0, 0, 1, 0, 0] [0, 4, 0, 5][1, 0, 0, 1, 0] [0, 5, 0, 5][0, 1, 0, 0, 1] [0, 6, 0, 5][0, 0, 1, 0, 0] [0, 0, 0, 6][0, 0, 1, 0, 0] [0, 1, 0, 6][1, 0, 0, 1, 0] [0, 2, 0, 6][0, 1, 0, 0, 1] [0, 3, 0, 6][0, 0, 1, 0, 0] [0, 4, 0, 6][1, 0, 0, 1, 0] [0, 5, 0, 6][0, 1, 0, 0, 1] [0, 6, 0, 6][0, 0, 1, 0, 0] [0, 0, 0, 7][0, 0, 1, 0, 0] [0, 1, 0, 7][1, 0, 0, 1, 0] [0, 2, 0, 7][0, 1, 0, 0, 1] [0, 3, 0, 7][0, 0, 1, 0, 0] [0, 4, 0, 7][1, 0, 0, 1, 0] [0, 5, 0, 7][0, 1, 0, 0, 1] [0, 6, 0, 7][0, 0, 1, 0, 0] [0, 0, 0, 8][0, 0, 1, 0, 0] [0, 1, 0, 8][1, 0, 0, 1, 0] [0, 2, 0, 8][0, 1, 0, 0, 1] [0, 3, 0, 8][0, 0, 1, 0, 0] [0, 4, 0, 8][1, 0, 0, 1, 0] [0, 5, 0, 8][0, 1, 0, 0, 1] [0, 6, 0, 8][0, 0, 1, 0, 0] [0, 0, 0, 9][0, 0, 1, 0, 0] [0, 1, 0, 9][1, 0, 0, 1, 0] [0, 2, 0, 9][0, 1, 0, 0, 1] [0, 3, 0, 9][0, 0, 1, 0, 0] [0, 4, 0, 9][1, 0, 0, 1, 0] [0, 5, 0, 9][0, 1, 0, 0, 1] [0, 6, 0, 9][0, 0, 1, 0, 0] [0, 0, 0, 10][0, 0, 1, 0, 0] [0, 1, 0, 10][1, 0, 0, 1, 0] [0, 2, 0, 10][0, 1, 0, 0, 1] [0, 3, 0, 10][0, 0, 1, 0, 0] [0, 4, 0, 10][1, 0, 0, 1, 0] [0, 5, 0, 10][0, 1, 0, 0, 1] [0, 6, 0, 10][0, 0, 1, 0, 0] [0, 0, 0, 11][0, 0, 1, 0, 0] [0, 1, 0, 11][1, 0, 0, 1, 0] [0, 2, 0, 11][0, 1, 0, 0, 1] [0, 3, 0, 11][0, 0, 1, 0, 0] [0, 4, 0, 11][1, 0, 0, 1, 0] [0, 5, 0, 11][0, 1, 0, 0, 1] [0, 6, 0, 11][0, 0, 1, 0, 0] [0, 0, 0, 12][0, 0, 1, 0, 0] [0, 1, 0, 12][1, 0, 0, 1, 0] [0, 2, 0, 12][0, 1, 0, 0, 1] [0, 3, 0, 12][0, 0, 1, 0, 0] [0, 4, 0, 12][1, 0, 0, 1, 0] [0, 5, 0, 12][0, 1, 0, 0, 1] [0, 6, 0, 12][0, 0, 1, 0, 0] [0, 0, 0, 13][0, 0, 1, 0, 0] [0, 1, 0, 13][1, 0, 0, 1, 0] [0, 2, 0, 13][0, 1, 0, 0, 1] [0, 3, 0, 13][0, 0, 1, 0, 0] [0, 4, 0, 13][1, 0, 0, 1, 0] [0, 5, 0, 13][0, 1, 0, 0, 1] [0, 6, 0, 13][0, 0, 1, 0, 0] [0, 0, 0, 14][0, 0, 1, 0, 0] [0, 1, 0, 14][1, 0, 0, 1, 0] [0, 2, 0, 14][0, 1, 0, 0, 1] [0, 3, 0, 14][0, 0, 1, 0, 0] [0, 4, 0, 14][1, 0, 0, 1, 0] [0, 5, 0, 14][0, 1, 0, 0, 1] [0, 6, 0, 14][0, 0, 1, 0, 0] [0, 0, 0, 15][0, 0, 1, 0, 0] [0, 1, 0, 15][1, 0, 0, 1, 0] [0, 2, 0, 15][0, 1, 0, 0, 1] [0, 3, 0, 15][0, 0, 1, 0, 0] [0, 4, 0, 15][1, 0, 0, 1, 0] [0, 5, 0, 15][0, 1, 0, 0, 1] [0, 6, 0, 15][0, 0, 1, 0, 0] [0, 0, 0, 16][0, 0, 1, 0, 0] [0, 1, 0, 16][1, 0, 0, 1, 0] [0, 2, 0, 16][0, 1, 0, 0, 1] [0, 3, 0, 16][0, 0, 1, 0, 0] [0, 4, 0, 16][1, 0, 0, 1, 0] [0, 5, 0, 16][0, 1, 0, 0, 1] [0, 6, 0, 16][0, 0, 1, 0, 0] [0, 0, 0, 17][0, 0, 1, 0, 0] [0, 1, 0, 17][1, 0, 0, 1, 0] [0, 2, 0, 17][0, 1, 0, 0, 1] [0, 3, 0, 17][0, 0, 1, 0, 0] [0, 4, 0, 17][1, 0, 0, 1, 0] [0, 5, 0, 17][0, 1, 0, 0, 1] [0, 6, 0, 17][0, 0, 1, 0, 0] Trying to add an indirect String Array Column. StandardStMan Base statistics: Nr of columns : 6 Nr of rows in the columns : 18 ColIndex[0] : 0 ColOffset[0] : 242 ColIndex[1] : 1 ColOffset[1] : 0 ColIndex[2] : 2 ColOffset[2] : 0 ColIndex[3] : 3 ColOffset[3] : 0 ColIndex[4] : 0 ColOffset[4] : 0 ColIndex[5] : 0 ColOffset[5] : 53 CacheSize : 2 Size of buckets : 250 Total buckets : 14 Total Index buckets : 3 1st Index bucket : 13 Index bucket offset : 0 last String bucket used : -1 Total free buckets : 2 1st free bucket : 5 StandardStMan index: 0 statistics: Index statistics: Entries used : 2 Rows Per bucket : 12 Nr of Columns : 3 BucketNr[0] : 0 - LastRow[0] : 10 BucketNr[1] : 2 - LastRow[1] : 17 Freespace entries: 2 Offset[0]: 197 - nrBytes[0]: 45 Offset[1]: 244 - nrBytes[1]: 6 StandardStMan index: 1 statistics: Index statistics: Entries used : 2 Rows Per bucket : 15 Nr of Columns : 1 BucketNr[0] : 1 - LastRow[0] : 14 BucketNr[1] : 4 - LastRow[1] : 17 Freespace entries: 1 Offset[0]: 240 - nrBytes[0]: 10 StandardStMan index: 2 statistics: Index statistics: Entries used : 2 Rows Per bucket : 10 Nr of Columns : 1 BucketNr[0] : 3 - LastRow[0] : 9 BucketNr[1] : 7 - LastRow[1] : 17 Freespace entries: 1 Offset[0]: 240 - nrBytes[0]: 10 StandardStMan index: 3 statistics: Index statistics: Entries used : 3 Rows Per bucket : 7 Nr of Columns : 1 BucketNr[0] : 8 - LastRow[0] : 6 BucketNr[1] : 9 - LastRow[1] : 13 BucketNr[2] : 10 - LastRow[2] : 17 Freespace entries: 1 Offset[0]: 224 - nrBytes[0]: 26 Col-4: Bool [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] Col-5: DComplex [(18,0), (17,1), (16,2), (15,3), (14,4), (13,5), (12,6), (11,7), (10,8), (9,9), (8,10), (7,11), (6,12), (5,13), (4,14), (3,15), (2,16), (1,17)] Col-6: float Ndim=4 Axis Lengths: [2, 3, 1, 18] [0, 0, 0, 0][0, 1] [0, 1, 0, 0][2, 3] [0, 2, 0, 0][4, 5] [0, 0, 0, 1][6, 7] [0, 1, 0, 1][8, 9] [0, 2, 0, 1][10, 11] [0, 0, 0, 2][12, 13] [0, 1, 0, 2][14, 15] [0, 2, 0, 2][16, 17] [0, 0, 0, 3][18, 19] [0, 1, 0, 3][20, 21] [0, 2, 0, 3][22, 23] [0, 0, 0, 4][24, 25] [0, 1, 0, 4][26, 27] [0, 2, 0, 4][28, 29] [0, 0, 0, 5][30, 31] [0, 1, 0, 5][32, 33] [0, 2, 0, 5][34, 35] [0, 0, 0, 6][36, 37] [0, 1, 0, 6][38, 39] [0, 2, 0, 6][40, 41] [0, 0, 0, 7][42, 43] [0, 1, 0, 7][44, 45] [0, 2, 0, 7][46, 47] [0, 0, 0, 8][48, 49] [0, 1, 0, 8][50, 51] [0, 2, 0, 8][52, 53] [0, 0, 0, 9][54, 55] [0, 1, 0, 9][56, 57] [0, 2, 0, 9][58, 59] [0, 0, 0, 10][60, 61] [0, 1, 0, 10][62, 63] [0, 2, 0, 10][64, 65] [0, 0, 0, 11][66, 67] [0, 1, 0, 11][68, 69] [0, 2, 0, 11][70, 71] [0, 0, 0, 12][72, 73] [0, 1, 0, 12][74, 75] [0, 2, 0, 12][76, 77] [0, 0, 0, 13][78, 79] [0, 1, 0, 13][80, 81] [0, 2, 0, 13][82, 83] [0, 0, 0, 14][84, 85] [0, 1, 0, 14][86, 87] [0, 2, 0, 14][88, 89] [0, 0, 0, 15][90, 91] [0, 1, 0, 15][92, 93] [0, 2, 0, 15][94, 95] [0, 0, 0, 16][96, 97] [0, 1, 0, 16][98, 99] [0, 2, 0, 16][100, 101] [0, 0, 0, 17][102, 103] [0, 1, 0, 17][104, 105] [0, 2, 0, 17][106, 107] Col-7: DComplex Axis Lengths: [2, 18] (NB: Matrix in Row/Column order) [(1.2,3.4), (3.2,6.4), (5.2,9.4), (7.2,12.4), (9.2,15.4), (11.2,18.4), (13.2,21.4), (15.2,24.4), (17.2,27.4), (19.2,30.4), (21.2,33.4), (23.2,36.4), (25.2,39.4), (27.2,42.4), (29.2,45.4), (31.2,48.4), (33.2,51.4), (35.2,54.4) (-2.3,5.6), (-0.3,8.6), (1.7,11.6), (3.7,14.6), (5.7,17.6), (7.7,20.6), (9.7,23.6), (11.7,26.6), (13.7,29.6), (15.7,32.6), (17.7,35.6), (19.7,38.6), (21.7,41.6), (23.7,44.6), (25.7,47.6), (27.7,50.6), (29.7,53.6), (31.7,56.6)] Col-8: Bool Ndim=4 Axis Lengths: [5, 7, 1, 18] [0, 0, 0, 0][0, 0, 1, 0, 0] [0, 1, 0, 0][1, 0, 0, 1, 0] [0, 2, 0, 0][0, 1, 0, 0, 1] [0, 3, 0, 0][0, 0, 1, 0, 0] [0, 4, 0, 0][1, 0, 0, 1, 0] [0, 5, 0, 0][0, 1, 0, 0, 1] [0, 6, 0, 0][0, 0, 1, 0, 0] [0, 0, 0, 1][0, 0, 1, 0, 0] [0, 1, 0, 1][1, 0, 0, 1, 0] [0, 2, 0, 1][0, 1, 0, 0, 1] [0, 3, 0, 1][0, 0, 1, 0, 0] [0, 4, 0, 1][1, 0, 0, 1, 0] [0, 5, 0, 1][0, 1, 0, 0, 1] [0, 6, 0, 1][0, 0, 1, 0, 0] [0, 0, 0, 2][0, 0, 1, 0, 0] [0, 1, 0, 2][1, 0, 0, 1, 0] [0, 2, 0, 2][0, 1, 0, 0, 1] [0, 3, 0, 2][0, 0, 1, 0, 0] [0, 4, 0, 2][1, 0, 0, 1, 0] [0, 5, 0, 2][0, 1, 0, 0, 1] [0, 6, 0, 2][0, 0, 1, 0, 0] [0, 0, 0, 3][0, 0, 1, 0, 0] [0, 1, 0, 3][1, 0, 0, 1, 0] [0, 2, 0, 3][0, 1, 0, 0, 1] [0, 3, 0, 3][0, 0, 1, 0, 0] [0, 4, 0, 3][1, 0, 0, 1, 0] [0, 5, 0, 3][0, 1, 0, 0, 1] [0, 6, 0, 3][0, 0, 1, 0, 0] [0, 0, 0, 4][0, 0, 1, 0, 0] [0, 1, 0, 4][1, 0, 0, 1, 0] [0, 2, 0, 4][0, 1, 0, 0, 1] [0, 3, 0, 4][0, 0, 1, 0, 0] [0, 4, 0, 4][1, 0, 0, 1, 0] [0, 5, 0, 4][0, 1, 0, 0, 1] [0, 6, 0, 4][0, 0, 1, 0, 0] [0, 0, 0, 5][0, 0, 1, 0, 0] [0, 1, 0, 5][1, 0, 0, 1, 0] [0, 2, 0, 5][0, 1, 0, 0, 1] [0, 3, 0, 5][0, 0, 1, 0, 0] [0, 4, 0, 5][1, 0, 0, 1, 0] [0, 5, 0, 5][0, 1, 0, 0, 1] [0, 6, 0, 5][0, 0, 1, 0, 0] [0, 0, 0, 6][0, 0, 1, 0, 0] [0, 1, 0, 6][1, 0, 0, 1, 0] [0, 2, 0, 6][0, 1, 0, 0, 1] [0, 3, 0, 6][0, 0, 1, 0, 0] [0, 4, 0, 6][1, 0, 0, 1, 0] [0, 5, 0, 6][0, 1, 0, 0, 1] [0, 6, 0, 6][0, 0, 1, 0, 0] [0, 0, 0, 7][0, 0, 1, 0, 0] [0, 1, 0, 7][1, 0, 0, 1, 0] [0, 2, 0, 7][0, 1, 0, 0, 1] [0, 3, 0, 7][0, 0, 1, 0, 0] [0, 4, 0, 7][1, 0, 0, 1, 0] [0, 5, 0, 7][0, 1, 0, 0, 1] [0, 6, 0, 7][0, 0, 1, 0, 0] [0, 0, 0, 8][0, 0, 1, 0, 0] [0, 1, 0, 8][1, 0, 0, 1, 0] [0, 2, 0, 8][0, 1, 0, 0, 1] [0, 3, 0, 8][0, 0, 1, 0, 0] [0, 4, 0, 8][1, 0, 0, 1, 0] [0, 5, 0, 8][0, 1, 0, 0, 1] [0, 6, 0, 8][0, 0, 1, 0, 0] [0, 0, 0, 9][0, 0, 1, 0, 0] [0, 1, 0, 9][1, 0, 0, 1, 0] [0, 2, 0, 9][0, 1, 0, 0, 1] [0, 3, 0, 9][0, 0, 1, 0, 0] [0, 4, 0, 9][1, 0, 0, 1, 0] [0, 5, 0, 9][0, 1, 0, 0, 1] [0, 6, 0, 9][0, 0, 1, 0, 0] [0, 0, 0, 10][0, 0, 1, 0, 0] [0, 1, 0, 10][1, 0, 0, 1, 0] [0, 2, 0, 10][0, 1, 0, 0, 1] [0, 3, 0, 10][0, 0, 1, 0, 0] [0, 4, 0, 10][1, 0, 0, 1, 0] [0, 5, 0, 10][0, 1, 0, 0, 1] [0, 6, 0, 10][0, 0, 1, 0, 0] [0, 0, 0, 11][0, 0, 1, 0, 0] [0, 1, 0, 11][1, 0, 0, 1, 0] [0, 2, 0, 11][0, 1, 0, 0, 1] [0, 3, 0, 11][0, 0, 1, 0, 0] [0, 4, 0, 11][1, 0, 0, 1, 0] [0, 5, 0, 11][0, 1, 0, 0, 1] [0, 6, 0, 11][0, 0, 1, 0, 0] [0, 0, 0, 12][0, 0, 1, 0, 0] [0, 1, 0, 12][1, 0, 0, 1, 0] [0, 2, 0, 12][0, 1, 0, 0, 1] [0, 3, 0, 12][0, 0, 1, 0, 0] [0, 4, 0, 12][1, 0, 0, 1, 0] [0, 5, 0, 12][0, 1, 0, 0, 1] [0, 6, 0, 12][0, 0, 1, 0, 0] [0, 0, 0, 13][0, 0, 1, 0, 0] [0, 1, 0, 13][1, 0, 0, 1, 0] [0, 2, 0, 13][0, 1, 0, 0, 1] [0, 3, 0, 13][0, 0, 1, 0, 0] [0, 4, 0, 13][1, 0, 0, 1, 0] [0, 5, 0, 13][0, 1, 0, 0, 1] [0, 6, 0, 13][0, 0, 1, 0, 0] [0, 0, 0, 14][0, 0, 1, 0, 0] [0, 1, 0, 14][1, 0, 0, 1, 0] [0, 2, 0, 14][0, 1, 0, 0, 1] [0, 3, 0, 14][0, 0, 1, 0, 0] [0, 4, 0, 14][1, 0, 0, 1, 0] [0, 5, 0, 14][0, 1, 0, 0, 1] [0, 6, 0, 14][0, 0, 1, 0, 0] [0, 0, 0, 15][0, 0, 1, 0, 0] [0, 1, 0, 15][1, 0, 0, 1, 0] [0, 2, 0, 15][0, 1, 0, 0, 1] [0, 3, 0, 15][0, 0, 1, 0, 0] [0, 4, 0, 15][1, 0, 0, 1, 0] [0, 5, 0, 15][0, 1, 0, 0, 1] [0, 6, 0, 15][0, 0, 1, 0, 0] [0, 0, 0, 16][0, 0, 1, 0, 0] [0, 1, 0, 16][1, 0, 0, 1, 0] [0, 2, 0, 16][0, 1, 0, 0, 1] [0, 3, 0, 16][0, 0, 1, 0, 0] [0, 4, 0, 16][1, 0, 0, 1, 0] [0, 5, 0, 16][0, 1, 0, 0, 1] [0, 6, 0, 16][0, 0, 1, 0, 0] [0, 0, 0, 17][0, 0, 1, 0, 0] [0, 1, 0, 17][1, 0, 0, 1, 0] [0, 2, 0, 17][0, 1, 0, 0, 1] [0, 3, 0, 17][0, 0, 1, 0, 0] [0, 4, 0, 17][1, 0, 0, 1, 0] [0, 5, 0, 17][0, 1, 0, 0, 1] [0, 6, 0, 17][0, 0, 1, 0, 0] Col-9: String Axis Lengths: [5, 18] (NB: Matrix in Row/Column order) [Start-1, Start-1 0, Start-1 0 1, Start-1 0 1 2, Start-1 0 1 2 3, Start-1 0 1 2 3 4, Start-1 0 1 2 3 4 5, Start-1 0 1 2 3 4 5 6, Start-1 0 1 2 3 4 5 6 7, Start-1 0 1 2 3 4 5 6 7 8, Start-1 0 1 2 3 4 5 6 7 8 9, Start-1 0 1 2 3 4 5 6 7 8 9 10, Start-1 0 1 2 3 4 5 6 7 8 9 10 11, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-2, Start-2 0, Start-2 0 1, Start-2 0 1 2, Start-2 0 1 2 3, Start-2 0 1 2 3 4, Start-2 0 1 2 3 4 5, Start-2 0 1 2 3 4 5 6, Start-2 0 1 2 3 4 5 6 7, Start-2 0 1 2 3 4 5 6 7 8, Start-2 0 1 2 3 4 5 6 7 8 9, Start-2 0 1 2 3 4 5 6 7 8 9 10, Start-2 0 1 2 3 4 5 6 7 8 9 10 11, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-3, Start-3 0, Start-3 0 1, Start-3 0 1 2, Start-3 0 1 2 3, Start-3 0 1 2 3 4, Start-3 0 1 2 3 4 5, Start-3 0 1 2 3 4 5 6, Start-3 0 1 2 3 4 5 6 7, Start-3 0 1 2 3 4 5 6 7 8, Start-3 0 1 2 3 4 5 6 7 8 9, Start-3 0 1 2 3 4 5 6 7 8 9 10, Start-3 0 1 2 3 4 5 6 7 8 9 10 11, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-4, Start-4 0, Start-4 0 1, Start-4 0 1 2, Start-4 0 1 2 3, Start-4 0 1 2 3 4, Start-4 0 1 2 3 4 5, Start-4 0 1 2 3 4 5 6, Start-4 0 1 2 3 4 5 6 7, Start-4 0 1 2 3 4 5 6 7 8, Start-4 0 1 2 3 4 5 6 7 8 9, Start-4 0 1 2 3 4 5 6 7 8 9 10, Start-4 0 1 2 3 4 5 6 7 8 9 10 11, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-5, Start-5 0, Start-5 0 1, Start-5 0 1 2, Start-5 0 1 2 3, Start-5 0 1 2 3 4, Start-5 0 1 2 3 4 5, Start-5 0 1 2 3 4 5 6, Start-5 0 1 2 3 4 5 6 7, Start-5 0 1 2 3 4 5 6 7 8, Start-5 0 1 2 3 4 5 6 7 8 9, Start-5 0 1 2 3 4 5 6 7 8 9 10, Start-5 0 1 2 3 4 5 6 7 8 9 10 11, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16] Trying to add an indirect Array Column. StandardStMan Base statistics: Nr of columns : 7 Nr of rows in the columns : 18 ColIndex[0] : 0 ColOffset[0] : 242 ColIndex[1] : 1 ColOffset[1] : 0 ColIndex[2] : 2 ColOffset[2] : 0 ColIndex[3] : 3 ColOffset[3] : 0 ColIndex[4] : 0 ColOffset[4] : 0 ColIndex[5] : 0 ColOffset[5] : 53 ColIndex[6] : 4 ColOffset[6] : 0 CacheSize : 2 Size of buckets : 250 Total buckets : 28 Total Index buckets : 3 1st Index bucket : 27 Index bucket offset : 0 last String bucket used : 24 Total free buckets : 3 1st free bucket : 11 StandardStMan index: 0 statistics: Index statistics: Entries used : 2 Rows Per bucket : 12 Nr of Columns : 3 BucketNr[0] : 0 - LastRow[0] : 10 BucketNr[1] : 2 - LastRow[1] : 17 Freespace entries: 2 Offset[0]: 197 - nrBytes[0]: 45 Offset[1]: 244 - nrBytes[1]: 6 StandardStMan index: 1 statistics: Index statistics: Entries used : 2 Rows Per bucket : 15 Nr of Columns : 1 BucketNr[0] : 1 - LastRow[0] : 14 BucketNr[1] : 4 - LastRow[1] : 17 Freespace entries: 1 Offset[0]: 240 - nrBytes[0]: 10 StandardStMan index: 2 statistics: Index statistics: Entries used : 2 Rows Per bucket : 10 Nr of Columns : 1 BucketNr[0] : 3 - LastRow[0] : 9 BucketNr[1] : 7 - LastRow[1] : 17 Freespace entries: 1 Offset[0]: 240 - nrBytes[0]: 10 StandardStMan index: 3 statistics: Index statistics: Entries used : 3 Rows Per bucket : 7 Nr of Columns : 1 BucketNr[0] : 8 - LastRow[0] : 6 BucketNr[1] : 9 - LastRow[1] : 13 BucketNr[2] : 10 - LastRow[2] : 17 Freespace entries: 1 Offset[0]: 224 - nrBytes[0]: 26 StandardStMan index: 4 statistics: Index statistics: Entries used : 1 Rows Per bucket : 31 Nr of Columns : 1 BucketNr[0] : 11 - LastRow[0] : 17 Freespace entries: 1 Offset[0]: 248 - nrBytes[0]: 2 Col-4: Bool [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] Col-5: DComplex [(18,0), (17,1), (16,2), (15,3), (14,4), (13,5), (12,6), (11,7), (10,8), (9,9), (8,10), (7,11), (6,12), (5,13), (4,14), (3,15), (2,16), (1,17)] Col-6: float Ndim=4 Axis Lengths: [2, 3, 1, 18] [0, 0, 0, 0][0, 1] [0, 1, 0, 0][2, 3] [0, 2, 0, 0][4, 5] [0, 0, 0, 1][6, 7] [0, 1, 0, 1][8, 9] [0, 2, 0, 1][10, 11] [0, 0, 0, 2][12, 13] [0, 1, 0, 2][14, 15] [0, 2, 0, 2][16, 17] [0, 0, 0, 3][18, 19] [0, 1, 0, 3][20, 21] [0, 2, 0, 3][22, 23] [0, 0, 0, 4][24, 25] [0, 1, 0, 4][26, 27] [0, 2, 0, 4][28, 29] [0, 0, 0, 5][30, 31] [0, 1, 0, 5][32, 33] [0, 2, 0, 5][34, 35] [0, 0, 0, 6][36, 37] [0, 1, 0, 6][38, 39] [0, 2, 0, 6][40, 41] [0, 0, 0, 7][42, 43] [0, 1, 0, 7][44, 45] [0, 2, 0, 7][46, 47] [0, 0, 0, 8][48, 49] [0, 1, 0, 8][50, 51] [0, 2, 0, 8][52, 53] [0, 0, 0, 9][54, 55] [0, 1, 0, 9][56, 57] [0, 2, 0, 9][58, 59] [0, 0, 0, 10][60, 61] [0, 1, 0, 10][62, 63] [0, 2, 0, 10][64, 65] [0, 0, 0, 11][66, 67] [0, 1, 0, 11][68, 69] [0, 2, 0, 11][70, 71] [0, 0, 0, 12][72, 73] [0, 1, 0, 12][74, 75] [0, 2, 0, 12][76, 77] [0, 0, 0, 13][78, 79] [0, 1, 0, 13][80, 81] [0, 2, 0, 13][82, 83] [0, 0, 0, 14][84, 85] [0, 1, 0, 14][86, 87] [0, 2, 0, 14][88, 89] [0, 0, 0, 15][90, 91] [0, 1, 0, 15][92, 93] [0, 2, 0, 15][94, 95] [0, 0, 0, 16][96, 97] [0, 1, 0, 16][98, 99] [0, 2, 0, 16][100, 101] [0, 0, 0, 17][102, 103] [0, 1, 0, 17][104, 105] [0, 2, 0, 17][106, 107] Col-7: DComplex Axis Lengths: [2, 18] (NB: Matrix in Row/Column order) [(1.2,3.4), (3.2,6.4), (5.2,9.4), (7.2,12.4), (9.2,15.4), (11.2,18.4), (13.2,21.4), (15.2,24.4), (17.2,27.4), (19.2,30.4), (21.2,33.4), (23.2,36.4), (25.2,39.4), (27.2,42.4), (29.2,45.4), (31.2,48.4), (33.2,51.4), (35.2,54.4) (-2.3,5.6), (-0.3,8.6), (1.7,11.6), (3.7,14.6), (5.7,17.6), (7.7,20.6), (9.7,23.6), (11.7,26.6), (13.7,29.6), (15.7,32.6), (17.7,35.6), (19.7,38.6), (21.7,41.6), (23.7,44.6), (25.7,47.6), (27.7,50.6), (29.7,53.6), (31.7,56.6)] Col-8: Bool Ndim=4 Axis Lengths: [5, 7, 1, 18] [0, 0, 0, 0][0, 0, 1, 0, 0] [0, 1, 0, 0][1, 0, 0, 1, 0] [0, 2, 0, 0][0, 1, 0, 0, 1] [0, 3, 0, 0][0, 0, 1, 0, 0] [0, 4, 0, 0][1, 0, 0, 1, 0] [0, 5, 0, 0][0, 1, 0, 0, 1] [0, 6, 0, 0][0, 0, 1, 0, 0] [0, 0, 0, 1][0, 0, 1, 0, 0] [0, 1, 0, 1][1, 0, 0, 1, 0] [0, 2, 0, 1][0, 1, 0, 0, 1] [0, 3, 0, 1][0, 0, 1, 0, 0] [0, 4, 0, 1][1, 0, 0, 1, 0] [0, 5, 0, 1][0, 1, 0, 0, 1] [0, 6, 0, 1][0, 0, 1, 0, 0] [0, 0, 0, 2][0, 0, 1, 0, 0] [0, 1, 0, 2][1, 0, 0, 1, 0] [0, 2, 0, 2][0, 1, 0, 0, 1] [0, 3, 0, 2][0, 0, 1, 0, 0] [0, 4, 0, 2][1, 0, 0, 1, 0] [0, 5, 0, 2][0, 1, 0, 0, 1] [0, 6, 0, 2][0, 0, 1, 0, 0] [0, 0, 0, 3][0, 0, 1, 0, 0] [0, 1, 0, 3][1, 0, 0, 1, 0] [0, 2, 0, 3][0, 1, 0, 0, 1] [0, 3, 0, 3][0, 0, 1, 0, 0] [0, 4, 0, 3][1, 0, 0, 1, 0] [0, 5, 0, 3][0, 1, 0, 0, 1] [0, 6, 0, 3][0, 0, 1, 0, 0] [0, 0, 0, 4][0, 0, 1, 0, 0] [0, 1, 0, 4][1, 0, 0, 1, 0] [0, 2, 0, 4][0, 1, 0, 0, 1] [0, 3, 0, 4][0, 0, 1, 0, 0] [0, 4, 0, 4][1, 0, 0, 1, 0] [0, 5, 0, 4][0, 1, 0, 0, 1] [0, 6, 0, 4][0, 0, 1, 0, 0] [0, 0, 0, 5][0, 0, 1, 0, 0] [0, 1, 0, 5][1, 0, 0, 1, 0] [0, 2, 0, 5][0, 1, 0, 0, 1] [0, 3, 0, 5][0, 0, 1, 0, 0] [0, 4, 0, 5][1, 0, 0, 1, 0] [0, 5, 0, 5][0, 1, 0, 0, 1] [0, 6, 0, 5][0, 0, 1, 0, 0] [0, 0, 0, 6][0, 0, 1, 0, 0] [0, 1, 0, 6][1, 0, 0, 1, 0] [0, 2, 0, 6][0, 1, 0, 0, 1] [0, 3, 0, 6][0, 0, 1, 0, 0] [0, 4, 0, 6][1, 0, 0, 1, 0] [0, 5, 0, 6][0, 1, 0, 0, 1] [0, 6, 0, 6][0, 0, 1, 0, 0] [0, 0, 0, 7][0, 0, 1, 0, 0] [0, 1, 0, 7][1, 0, 0, 1, 0] [0, 2, 0, 7][0, 1, 0, 0, 1] [0, 3, 0, 7][0, 0, 1, 0, 0] [0, 4, 0, 7][1, 0, 0, 1, 0] [0, 5, 0, 7][0, 1, 0, 0, 1] [0, 6, 0, 7][0, 0, 1, 0, 0] [0, 0, 0, 8][0, 0, 1, 0, 0] [0, 1, 0, 8][1, 0, 0, 1, 0] [0, 2, 0, 8][0, 1, 0, 0, 1] [0, 3, 0, 8][0, 0, 1, 0, 0] [0, 4, 0, 8][1, 0, 0, 1, 0] [0, 5, 0, 8][0, 1, 0, 0, 1] [0, 6, 0, 8][0, 0, 1, 0, 0] [0, 0, 0, 9][0, 0, 1, 0, 0] [0, 1, 0, 9][1, 0, 0, 1, 0] [0, 2, 0, 9][0, 1, 0, 0, 1] [0, 3, 0, 9][0, 0, 1, 0, 0] [0, 4, 0, 9][1, 0, 0, 1, 0] [0, 5, 0, 9][0, 1, 0, 0, 1] [0, 6, 0, 9][0, 0, 1, 0, 0] [0, 0, 0, 10][0, 0, 1, 0, 0] [0, 1, 0, 10][1, 0, 0, 1, 0] [0, 2, 0, 10][0, 1, 0, 0, 1] [0, 3, 0, 10][0, 0, 1, 0, 0] [0, 4, 0, 10][1, 0, 0, 1, 0] [0, 5, 0, 10][0, 1, 0, 0, 1] [0, 6, 0, 10][0, 0, 1, 0, 0] [0, 0, 0, 11][0, 0, 1, 0, 0] [0, 1, 0, 11][1, 0, 0, 1, 0] [0, 2, 0, 11][0, 1, 0, 0, 1] [0, 3, 0, 11][0, 0, 1, 0, 0] [0, 4, 0, 11][1, 0, 0, 1, 0] [0, 5, 0, 11][0, 1, 0, 0, 1] [0, 6, 0, 11][0, 0, 1, 0, 0] [0, 0, 0, 12][0, 0, 1, 0, 0] [0, 1, 0, 12][1, 0, 0, 1, 0] [0, 2, 0, 12][0, 1, 0, 0, 1] [0, 3, 0, 12][0, 0, 1, 0, 0] [0, 4, 0, 12][1, 0, 0, 1, 0] [0, 5, 0, 12][0, 1, 0, 0, 1] [0, 6, 0, 12][0, 0, 1, 0, 0] [0, 0, 0, 13][0, 0, 1, 0, 0] [0, 1, 0, 13][1, 0, 0, 1, 0] [0, 2, 0, 13][0, 1, 0, 0, 1] [0, 3, 0, 13][0, 0, 1, 0, 0] [0, 4, 0, 13][1, 0, 0, 1, 0] [0, 5, 0, 13][0, 1, 0, 0, 1] [0, 6, 0, 13][0, 0, 1, 0, 0] [0, 0, 0, 14][0, 0, 1, 0, 0] [0, 1, 0, 14][1, 0, 0, 1, 0] [0, 2, 0, 14][0, 1, 0, 0, 1] [0, 3, 0, 14][0, 0, 1, 0, 0] [0, 4, 0, 14][1, 0, 0, 1, 0] [0, 5, 0, 14][0, 1, 0, 0, 1] [0, 6, 0, 14][0, 0, 1, 0, 0] [0, 0, 0, 15][0, 0, 1, 0, 0] [0, 1, 0, 15][1, 0, 0, 1, 0] [0, 2, 0, 15][0, 1, 0, 0, 1] [0, 3, 0, 15][0, 0, 1, 0, 0] [0, 4, 0, 15][1, 0, 0, 1, 0] [0, 5, 0, 15][0, 1, 0, 0, 1] [0, 6, 0, 15][0, 0, 1, 0, 0] [0, 0, 0, 16][0, 0, 1, 0, 0] [0, 1, 0, 16][1, 0, 0, 1, 0] [0, 2, 0, 16][0, 1, 0, 0, 1] [0, 3, 0, 16][0, 0, 1, 0, 0] [0, 4, 0, 16][1, 0, 0, 1, 0] [0, 5, 0, 16][0, 1, 0, 0, 1] [0, 6, 0, 16][0, 0, 1, 0, 0] [0, 0, 0, 17][0, 0, 1, 0, 0] [0, 1, 0, 17][1, 0, 0, 1, 0] [0, 2, 0, 17][0, 1, 0, 0, 1] [0, 3, 0, 17][0, 0, 1, 0, 0] [0, 4, 0, 17][1, 0, 0, 1, 0] [0, 5, 0, 17][0, 1, 0, 0, 1] [0, 6, 0, 17][0, 0, 1, 0, 0] Col-9: String Axis Lengths: [5, 18] (NB: Matrix in Row/Column order) [Start-1, Start-1 0, Start-1 0 1, Start-1 0 1 2, Start-1 0 1 2 3, Start-1 0 1 2 3 4, Start-1 0 1 2 3 4 5, Start-1 0 1 2 3 4 5 6, Start-1 0 1 2 3 4 5 6 7, Start-1 0 1 2 3 4 5 6 7 8, Start-1 0 1 2 3 4 5 6 7 8 9, Start-1 0 1 2 3 4 5 6 7 8 9 10, Start-1 0 1 2 3 4 5 6 7 8 9 10 11, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-2, Start-2 0, Start-2 0 1, Start-2 0 1 2, Start-2 0 1 2 3, Start-2 0 1 2 3 4, Start-2 0 1 2 3 4 5, Start-2 0 1 2 3 4 5 6, Start-2 0 1 2 3 4 5 6 7, Start-2 0 1 2 3 4 5 6 7 8, Start-2 0 1 2 3 4 5 6 7 8 9, Start-2 0 1 2 3 4 5 6 7 8 9 10, Start-2 0 1 2 3 4 5 6 7 8 9 10 11, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-3, Start-3 0, Start-3 0 1, Start-3 0 1 2, Start-3 0 1 2 3, Start-3 0 1 2 3 4, Start-3 0 1 2 3 4 5, Start-3 0 1 2 3 4 5 6, Start-3 0 1 2 3 4 5 6 7, Start-3 0 1 2 3 4 5 6 7 8, Start-3 0 1 2 3 4 5 6 7 8 9, Start-3 0 1 2 3 4 5 6 7 8 9 10, Start-3 0 1 2 3 4 5 6 7 8 9 10 11, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-4, Start-4 0, Start-4 0 1, Start-4 0 1 2, Start-4 0 1 2 3, Start-4 0 1 2 3 4, Start-4 0 1 2 3 4 5, Start-4 0 1 2 3 4 5 6, Start-4 0 1 2 3 4 5 6 7, Start-4 0 1 2 3 4 5 6 7 8, Start-4 0 1 2 3 4 5 6 7 8 9, Start-4 0 1 2 3 4 5 6 7 8 9 10, Start-4 0 1 2 3 4 5 6 7 8 9 10 11, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-5, Start-5 0, Start-5 0 1, Start-5 0 1 2, Start-5 0 1 2 3, Start-5 0 1 2 3 4, Start-5 0 1 2 3 4 5, Start-5 0 1 2 3 4 5 6, Start-5 0 1 2 3 4 5 6 7, Start-5 0 1 2 3 4 5 6 7 8, Start-5 0 1 2 3 4 5 6 7 8 9, Start-5 0 1 2 3 4 5 6 7 8 9 10, Start-5 0 1 2 3 4 5 6 7 8 9 10 11, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16] Col-11: Int Axis Lengths: [5, 18] (NB: Matrix in Row/Column order) [1, 1, 2, 4, 7, 11, 16, 22, 29, 37, 46, 56, 67, 79, 92, 106, 121, 137 2, 2, 3, 5, 8, 12, 17, 23, 30, 38, 47, 57, 68, 80, 93, 107, 122, 138 3, 3, 4, 6, 9, 13, 18, 24, 31, 39, 48, 58, 69, 81, 94, 108, 123, 139 4, 4, 5, 7, 10, 14, 19, 25, 32, 40, 49, 59, 70, 82, 95, 109, 124, 140 5, 5, 6, 8, 11, 15, 20, 26, 33, 41, 50, 60, 71, 83, 96, 110, 125, 141] after removing several rows at once: StandardStMan Base statistics: Nr of columns : 7 Nr of rows in the columns : 15 ColIndex[0] : 0 ColOffset[0] : 242 ColIndex[1] : 1 ColOffset[1] : 0 ColIndex[2] : 2 ColOffset[2] : 0 ColIndex[3] : 3 ColOffset[3] : 0 ColIndex[4] : 0 ColOffset[4] : 0 ColIndex[5] : 0 ColOffset[5] : 53 ColIndex[6] : 4 ColOffset[6] : 0 CacheSize : 2 Size of buckets : 250 Total buckets : 29 Total Index buckets : 3 1st Index bucket : 28 Index bucket offset : 0 last String bucket used : 24 Total free buckets : 3 1st free bucket : 25 StandardStMan index: 0 statistics: Index statistics: Entries used : 2 Rows Per bucket : 12 Nr of Columns : 3 BucketNr[0] : 0 - LastRow[0] : 7 BucketNr[1] : 2 - LastRow[1] : 14 Freespace entries: 2 Offset[0]: 197 - nrBytes[0]: 45 Offset[1]: 244 - nrBytes[1]: 6 StandardStMan index: 1 statistics: Index statistics: Entries used : 2 Rows Per bucket : 15 Nr of Columns : 1 BucketNr[0] : 1 - LastRow[0] : 11 BucketNr[1] : 4 - LastRow[1] : 14 Freespace entries: 1 Offset[0]: 240 - nrBytes[0]: 10 StandardStMan index: 2 statistics: Index statistics: Entries used : 2 Rows Per bucket : 10 Nr of Columns : 1 BucketNr[0] : 3 - LastRow[0] : 6 BucketNr[1] : 7 - LastRow[1] : 14 Freespace entries: 1 Offset[0]: 240 - nrBytes[0]: 10 StandardStMan index: 3 statistics: Index statistics: Entries used : 3 Rows Per bucket : 7 Nr of Columns : 1 BucketNr[0] : 8 - LastRow[0] : 3 BucketNr[1] : 9 - LastRow[1] : 10 BucketNr[2] : 10 - LastRow[2] : 14 Freespace entries: 1 Offset[0]: 224 - nrBytes[0]: 26 StandardStMan index: 4 statistics: Index statistics: Entries used : 1 Rows Per bucket : 31 Nr of Columns : 1 BucketNr[0] : 11 - LastRow[0] : 14 Freespace entries: 1 Offset[0]: 248 - nrBytes[0]: 2 Col-4: Bool [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] Col-5: DComplex [(18,0), (17,1), (16,2), (12,6), (11,7), (10,8), (9,9), (8,10), (7,11), (6,12), (5,13), (4,14), (3,15), (2,16), (1,17)] Col-6: float Ndim=4 Axis Lengths: [2, 3, 1, 15] [0, 0, 0, 0][0, 1] [0, 1, 0, 0][2, 3] [0, 2, 0, 0][4, 5] [0, 0, 0, 1][6, 7] [0, 1, 0, 1][8, 9] [0, 2, 0, 1][10, 11] [0, 0, 0, 2][12, 13] [0, 1, 0, 2][14, 15] [0, 2, 0, 2][16, 17] [0, 0, 0, 3][36, 37] [0, 1, 0, 3][38, 39] [0, 2, 0, 3][40, 41] [0, 0, 0, 4][42, 43] [0, 1, 0, 4][44, 45] [0, 2, 0, 4][46, 47] [0, 0, 0, 5][48, 49] [0, 1, 0, 5][50, 51] [0, 2, 0, 5][52, 53] [0, 0, 0, 6][54, 55] [0, 1, 0, 6][56, 57] [0, 2, 0, 6][58, 59] [0, 0, 0, 7][60, 61] [0, 1, 0, 7][62, 63] [0, 2, 0, 7][64, 65] [0, 0, 0, 8][66, 67] [0, 1, 0, 8][68, 69] [0, 2, 0, 8][70, 71] [0, 0, 0, 9][72, 73] [0, 1, 0, 9][74, 75] [0, 2, 0, 9][76, 77] [0, 0, 0, 10][78, 79] [0, 1, 0, 10][80, 81] [0, 2, 0, 10][82, 83] [0, 0, 0, 11][84, 85] [0, 1, 0, 11][86, 87] [0, 2, 0, 11][88, 89] [0, 0, 0, 12][90, 91] [0, 1, 0, 12][92, 93] [0, 2, 0, 12][94, 95] [0, 0, 0, 13][96, 97] [0, 1, 0, 13][98, 99] [0, 2, 0, 13][100, 101] [0, 0, 0, 14][102, 103] [0, 1, 0, 14][104, 105] [0, 2, 0, 14][106, 107] Col-7: DComplex Axis Lengths: [2, 15] (NB: Matrix in Row/Column order) [(1.2,3.4), (3.2,6.4), (5.2,9.4), (13.2,21.4), (15.2,24.4), (17.2,27.4), (19.2,30.4), (21.2,33.4), (23.2,36.4), (25.2,39.4), (27.2,42.4), (29.2,45.4), (31.2,48.4), (33.2,51.4), (35.2,54.4) (-2.3,5.6), (-0.3,8.6), (1.7,11.6), (9.7,23.6), (11.7,26.6), (13.7,29.6), (15.7,32.6), (17.7,35.6), (19.7,38.6), (21.7,41.6), (23.7,44.6), (25.7,47.6), (27.7,50.6), (29.7,53.6), (31.7,56.6)] Col-8: Bool Ndim=4 Axis Lengths: [5, 7, 1, 15] [0, 0, 0, 0][0, 0, 1, 0, 0] [0, 1, 0, 0][1, 0, 0, 1, 0] [0, 2, 0, 0][0, 1, 0, 0, 1] [0, 3, 0, 0][0, 0, 1, 0, 0] [0, 4, 0, 0][1, 0, 0, 1, 0] [0, 5, 0, 0][0, 1, 0, 0, 1] [0, 6, 0, 0][0, 0, 1, 0, 0] [0, 0, 0, 1][0, 0, 1, 0, 0] [0, 1, 0, 1][1, 0, 0, 1, 0] [0, 2, 0, 1][0, 1, 0, 0, 1] [0, 3, 0, 1][0, 0, 1, 0, 0] [0, 4, 0, 1][1, 0, 0, 1, 0] [0, 5, 0, 1][0, 1, 0, 0, 1] [0, 6, 0, 1][0, 0, 1, 0, 0] [0, 0, 0, 2][0, 0, 1, 0, 0] [0, 1, 0, 2][1, 0, 0, 1, 0] [0, 2, 0, 2][0, 1, 0, 0, 1] [0, 3, 0, 2][0, 0, 1, 0, 0] [0, 4, 0, 2][1, 0, 0, 1, 0] [0, 5, 0, 2][0, 1, 0, 0, 1] [0, 6, 0, 2][0, 0, 1, 0, 0] [0, 0, 0, 3][0, 0, 1, 0, 0] [0, 1, 0, 3][1, 0, 0, 1, 0] [0, 2, 0, 3][0, 1, 0, 0, 1] [0, 3, 0, 3][0, 0, 1, 0, 0] [0, 4, 0, 3][1, 0, 0, 1, 0] [0, 5, 0, 3][0, 1, 0, 0, 1] [0, 6, 0, 3][0, 0, 1, 0, 0] [0, 0, 0, 4][0, 0, 1, 0, 0] [0, 1, 0, 4][1, 0, 0, 1, 0] [0, 2, 0, 4][0, 1, 0, 0, 1] [0, 3, 0, 4][0, 0, 1, 0, 0] [0, 4, 0, 4][1, 0, 0, 1, 0] [0, 5, 0, 4][0, 1, 0, 0, 1] [0, 6, 0, 4][0, 0, 1, 0, 0] [0, 0, 0, 5][0, 0, 1, 0, 0] [0, 1, 0, 5][1, 0, 0, 1, 0] [0, 2, 0, 5][0, 1, 0, 0, 1] [0, 3, 0, 5][0, 0, 1, 0, 0] [0, 4, 0, 5][1, 0, 0, 1, 0] [0, 5, 0, 5][0, 1, 0, 0, 1] [0, 6, 0, 5][0, 0, 1, 0, 0] [0, 0, 0, 6][0, 0, 1, 0, 0] [0, 1, 0, 6][1, 0, 0, 1, 0] [0, 2, 0, 6][0, 1, 0, 0, 1] [0, 3, 0, 6][0, 0, 1, 0, 0] [0, 4, 0, 6][1, 0, 0, 1, 0] [0, 5, 0, 6][0, 1, 0, 0, 1] [0, 6, 0, 6][0, 0, 1, 0, 0] [0, 0, 0, 7][0, 0, 1, 0, 0] [0, 1, 0, 7][1, 0, 0, 1, 0] [0, 2, 0, 7][0, 1, 0, 0, 1] [0, 3, 0, 7][0, 0, 1, 0, 0] [0, 4, 0, 7][1, 0, 0, 1, 0] [0, 5, 0, 7][0, 1, 0, 0, 1] [0, 6, 0, 7][0, 0, 1, 0, 0] [0, 0, 0, 8][0, 0, 1, 0, 0] [0, 1, 0, 8][1, 0, 0, 1, 0] [0, 2, 0, 8][0, 1, 0, 0, 1] [0, 3, 0, 8][0, 0, 1, 0, 0] [0, 4, 0, 8][1, 0, 0, 1, 0] [0, 5, 0, 8][0, 1, 0, 0, 1] [0, 6, 0, 8][0, 0, 1, 0, 0] [0, 0, 0, 9][0, 0, 1, 0, 0] [0, 1, 0, 9][1, 0, 0, 1, 0] [0, 2, 0, 9][0, 1, 0, 0, 1] [0, 3, 0, 9][0, 0, 1, 0, 0] [0, 4, 0, 9][1, 0, 0, 1, 0] [0, 5, 0, 9][0, 1, 0, 0, 1] [0, 6, 0, 9][0, 0, 1, 0, 0] [0, 0, 0, 10][0, 0, 1, 0, 0] [0, 1, 0, 10][1, 0, 0, 1, 0] [0, 2, 0, 10][0, 1, 0, 0, 1] [0, 3, 0, 10][0, 0, 1, 0, 0] [0, 4, 0, 10][1, 0, 0, 1, 0] [0, 5, 0, 10][0, 1, 0, 0, 1] [0, 6, 0, 10][0, 0, 1, 0, 0] [0, 0, 0, 11][0, 0, 1, 0, 0] [0, 1, 0, 11][1, 0, 0, 1, 0] [0, 2, 0, 11][0, 1, 0, 0, 1] [0, 3, 0, 11][0, 0, 1, 0, 0] [0, 4, 0, 11][1, 0, 0, 1, 0] [0, 5, 0, 11][0, 1, 0, 0, 1] [0, 6, 0, 11][0, 0, 1, 0, 0] [0, 0, 0, 12][0, 0, 1, 0, 0] [0, 1, 0, 12][1, 0, 0, 1, 0] [0, 2, 0, 12][0, 1, 0, 0, 1] [0, 3, 0, 12][0, 0, 1, 0, 0] [0, 4, 0, 12][1, 0, 0, 1, 0] [0, 5, 0, 12][0, 1, 0, 0, 1] [0, 6, 0, 12][0, 0, 1, 0, 0] [0, 0, 0, 13][0, 0, 1, 0, 0] [0, 1, 0, 13][1, 0, 0, 1, 0] [0, 2, 0, 13][0, 1, 0, 0, 1] [0, 3, 0, 13][0, 0, 1, 0, 0] [0, 4, 0, 13][1, 0, 0, 1, 0] [0, 5, 0, 13][0, 1, 0, 0, 1] [0, 6, 0, 13][0, 0, 1, 0, 0] [0, 0, 0, 14][0, 0, 1, 0, 0] [0, 1, 0, 14][1, 0, 0, 1, 0] [0, 2, 0, 14][0, 1, 0, 0, 1] [0, 3, 0, 14][0, 0, 1, 0, 0] [0, 4, 0, 14][1, 0, 0, 1, 0] [0, 5, 0, 14][0, 1, 0, 0, 1] [0, 6, 0, 14][0, 0, 1, 0, 0] Col-9: String Axis Lengths: [5, 15] (NB: Matrix in Row/Column order) [Start-1, Start-1 0, Start-1 0 1, Start-1 0 1 2 3 4 5, Start-1 0 1 2 3 4 5 6, Start-1 0 1 2 3 4 5 6 7, Start-1 0 1 2 3 4 5 6 7 8, Start-1 0 1 2 3 4 5 6 7 8 9, Start-1 0 1 2 3 4 5 6 7 8 9 10, Start-1 0 1 2 3 4 5 6 7 8 9 10 11, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-2, Start-2 0, Start-2 0 1, Start-2 0 1 2 3 4 5, Start-2 0 1 2 3 4 5 6, Start-2 0 1 2 3 4 5 6 7, Start-2 0 1 2 3 4 5 6 7 8, Start-2 0 1 2 3 4 5 6 7 8 9, Start-2 0 1 2 3 4 5 6 7 8 9 10, Start-2 0 1 2 3 4 5 6 7 8 9 10 11, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-3, Start-3 0, Start-3 0 1, Start-3 0 1 2 3 4 5, Start-3 0 1 2 3 4 5 6, Start-3 0 1 2 3 4 5 6 7, Start-3 0 1 2 3 4 5 6 7 8, Start-3 0 1 2 3 4 5 6 7 8 9, Start-3 0 1 2 3 4 5 6 7 8 9 10, Start-3 0 1 2 3 4 5 6 7 8 9 10 11, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-4, Start-4 0, Start-4 0 1, Start-4 0 1 2 3 4 5, Start-4 0 1 2 3 4 5 6, Start-4 0 1 2 3 4 5 6 7, Start-4 0 1 2 3 4 5 6 7 8, Start-4 0 1 2 3 4 5 6 7 8 9, Start-4 0 1 2 3 4 5 6 7 8 9 10, Start-4 0 1 2 3 4 5 6 7 8 9 10 11, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-5, Start-5 0, Start-5 0 1, Start-5 0 1 2 3 4 5, Start-5 0 1 2 3 4 5 6, Start-5 0 1 2 3 4 5 6 7, Start-5 0 1 2 3 4 5 6 7 8, Start-5 0 1 2 3 4 5 6 7 8 9, Start-5 0 1 2 3 4 5 6 7 8 9 10, Start-5 0 1 2 3 4 5 6 7 8 9 10 11, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16] Col-11: Int Axis Lengths: [5, 15] (NB: Matrix in Row/Column order) [1, 1, 2, 16, 22, 29, 37, 46, 56, 67, 79, 92, 106, 121, 137 2, 2, 3, 17, 23, 30, 38, 47, 57, 68, 80, 93, 107, 122, 138 3, 3, 4, 18, 24, 31, 39, 48, 58, 69, 81, 94, 108, 123, 139 4, 4, 5, 19, 25, 32, 40, 49, 59, 70, 82, 95, 109, 124, 140 5, 5, 6, 20, 26, 33, 41, 50, 60, 71, 83, 96, 110, 125, 141] Try to remove Column:Col-7 After removing Column: Col-7 StandardStMan Base statistics: Nr of columns : 6 Nr of rows in the columns : 15 ColIndex[0] : 0 ColOffset[0] : 242 ColIndex[1] : 1 ColOffset[1] : 0 ColIndex[2] : 2 ColOffset[2] : 0 ColIndex[3] : 0 ColOffset[3] : 0 ColIndex[4] : 0 ColOffset[4] : 53 ColIndex[5] : 3 ColOffset[5] : 0 CacheSize : 2 Size of buckets : 250 Total buckets : 29 Total Index buckets : 3 1st Index bucket : 27 Index bucket offset : 0 last String bucket used : 24 Total free buckets : 3 1st free bucket : 12 StandardStMan index: 0 statistics: Index statistics: Entries used : 2 Rows Per bucket : 12 Nr of Columns : 3 BucketNr[0] : 0 - LastRow[0] : 7 BucketNr[1] : 2 - LastRow[1] : 14 Freespace entries: 2 Offset[0]: 197 - nrBytes[0]: 45 Offset[1]: 244 - nrBytes[1]: 6 StandardStMan index: 1 statistics: Index statistics: Entries used : 2 Rows Per bucket : 15 Nr of Columns : 1 BucketNr[0] : 1 - LastRow[0] : 11 BucketNr[1] : 4 - LastRow[1] : 14 Freespace entries: 1 Offset[0]: 240 - nrBytes[0]: 10 StandardStMan index: 2 statistics: Index statistics: Entries used : 2 Rows Per bucket : 10 Nr of Columns : 1 BucketNr[0] : 3 - LastRow[0] : 6 BucketNr[1] : 7 - LastRow[1] : 14 Freespace entries: 1 Offset[0]: 240 - nrBytes[0]: 10 StandardStMan index: 3 statistics: Index statistics: Entries used : 1 Rows Per bucket : 31 Nr of Columns : 1 BucketNr[0] : 11 - LastRow[0] : 14 Freespace entries: 1 Offset[0]: 248 - nrBytes[0]: 2 Col-4: Bool [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] Col-5: DComplex [(18,0), (17,1), (16,2), (12,6), (11,7), (10,8), (9,9), (8,10), (7,11), (6,12), (5,13), (4,14), (3,15), (2,16), (1,17)] Col-6: float Ndim=4 Axis Lengths: [2, 3, 1, 15] [0, 0, 0, 0][0, 1] [0, 1, 0, 0][2, 3] [0, 2, 0, 0][4, 5] [0, 0, 0, 1][6, 7] [0, 1, 0, 1][8, 9] [0, 2, 0, 1][10, 11] [0, 0, 0, 2][12, 13] [0, 1, 0, 2][14, 15] [0, 2, 0, 2][16, 17] [0, 0, 0, 3][36, 37] [0, 1, 0, 3][38, 39] [0, 2, 0, 3][40, 41] [0, 0, 0, 4][42, 43] [0, 1, 0, 4][44, 45] [0, 2, 0, 4][46, 47] [0, 0, 0, 5][48, 49] [0, 1, 0, 5][50, 51] [0, 2, 0, 5][52, 53] [0, 0, 0, 6][54, 55] [0, 1, 0, 6][56, 57] [0, 2, 0, 6][58, 59] [0, 0, 0, 7][60, 61] [0, 1, 0, 7][62, 63] [0, 2, 0, 7][64, 65] [0, 0, 0, 8][66, 67] [0, 1, 0, 8][68, 69] [0, 2, 0, 8][70, 71] [0, 0, 0, 9][72, 73] [0, 1, 0, 9][74, 75] [0, 2, 0, 9][76, 77] [0, 0, 0, 10][78, 79] [0, 1, 0, 10][80, 81] [0, 2, 0, 10][82, 83] [0, 0, 0, 11][84, 85] [0, 1, 0, 11][86, 87] [0, 2, 0, 11][88, 89] [0, 0, 0, 12][90, 91] [0, 1, 0, 12][92, 93] [0, 2, 0, 12][94, 95] [0, 0, 0, 13][96, 97] [0, 1, 0, 13][98, 99] [0, 2, 0, 13][100, 101] [0, 0, 0, 14][102, 103] [0, 1, 0, 14][104, 105] [0, 2, 0, 14][106, 107] Col-8: Bool Ndim=4 Axis Lengths: [5, 7, 1, 15] [0, 0, 0, 0][0, 0, 1, 0, 0] [0, 1, 0, 0][1, 0, 0, 1, 0] [0, 2, 0, 0][0, 1, 0, 0, 1] [0, 3, 0, 0][0, 0, 1, 0, 0] [0, 4, 0, 0][1, 0, 0, 1, 0] [0, 5, 0, 0][0, 1, 0, 0, 1] [0, 6, 0, 0][0, 0, 1, 0, 0] [0, 0, 0, 1][0, 0, 1, 0, 0] [0, 1, 0, 1][1, 0, 0, 1, 0] [0, 2, 0, 1][0, 1, 0, 0, 1] [0, 3, 0, 1][0, 0, 1, 0, 0] [0, 4, 0, 1][1, 0, 0, 1, 0] [0, 5, 0, 1][0, 1, 0, 0, 1] [0, 6, 0, 1][0, 0, 1, 0, 0] [0, 0, 0, 2][0, 0, 1, 0, 0] [0, 1, 0, 2][1, 0, 0, 1, 0] [0, 2, 0, 2][0, 1, 0, 0, 1] [0, 3, 0, 2][0, 0, 1, 0, 0] [0, 4, 0, 2][1, 0, 0, 1, 0] [0, 5, 0, 2][0, 1, 0, 0, 1] [0, 6, 0, 2][0, 0, 1, 0, 0] [0, 0, 0, 3][0, 0, 1, 0, 0] [0, 1, 0, 3][1, 0, 0, 1, 0] [0, 2, 0, 3][0, 1, 0, 0, 1] [0, 3, 0, 3][0, 0, 1, 0, 0] [0, 4, 0, 3][1, 0, 0, 1, 0] [0, 5, 0, 3][0, 1, 0, 0, 1] [0, 6, 0, 3][0, 0, 1, 0, 0] [0, 0, 0, 4][0, 0, 1, 0, 0] [0, 1, 0, 4][1, 0, 0, 1, 0] [0, 2, 0, 4][0, 1, 0, 0, 1] [0, 3, 0, 4][0, 0, 1, 0, 0] [0, 4, 0, 4][1, 0, 0, 1, 0] [0, 5, 0, 4][0, 1, 0, 0, 1] [0, 6, 0, 4][0, 0, 1, 0, 0] [0, 0, 0, 5][0, 0, 1, 0, 0] [0, 1, 0, 5][1, 0, 0, 1, 0] [0, 2, 0, 5][0, 1, 0, 0, 1] [0, 3, 0, 5][0, 0, 1, 0, 0] [0, 4, 0, 5][1, 0, 0, 1, 0] [0, 5, 0, 5][0, 1, 0, 0, 1] [0, 6, 0, 5][0, 0, 1, 0, 0] [0, 0, 0, 6][0, 0, 1, 0, 0] [0, 1, 0, 6][1, 0, 0, 1, 0] [0, 2, 0, 6][0, 1, 0, 0, 1] [0, 3, 0, 6][0, 0, 1, 0, 0] [0, 4, 0, 6][1, 0, 0, 1, 0] [0, 5, 0, 6][0, 1, 0, 0, 1] [0, 6, 0, 6][0, 0, 1, 0, 0] [0, 0, 0, 7][0, 0, 1, 0, 0] [0, 1, 0, 7][1, 0, 0, 1, 0] [0, 2, 0, 7][0, 1, 0, 0, 1] [0, 3, 0, 7][0, 0, 1, 0, 0] [0, 4, 0, 7][1, 0, 0, 1, 0] [0, 5, 0, 7][0, 1, 0, 0, 1] [0, 6, 0, 7][0, 0, 1, 0, 0] [0, 0, 0, 8][0, 0, 1, 0, 0] [0, 1, 0, 8][1, 0, 0, 1, 0] [0, 2, 0, 8][0, 1, 0, 0, 1] [0, 3, 0, 8][0, 0, 1, 0, 0] [0, 4, 0, 8][1, 0, 0, 1, 0] [0, 5, 0, 8][0, 1, 0, 0, 1] [0, 6, 0, 8][0, 0, 1, 0, 0] [0, 0, 0, 9][0, 0, 1, 0, 0] [0, 1, 0, 9][1, 0, 0, 1, 0] [0, 2, 0, 9][0, 1, 0, 0, 1] [0, 3, 0, 9][0, 0, 1, 0, 0] [0, 4, 0, 9][1, 0, 0, 1, 0] [0, 5, 0, 9][0, 1, 0, 0, 1] [0, 6, 0, 9][0, 0, 1, 0, 0] [0, 0, 0, 10][0, 0, 1, 0, 0] [0, 1, 0, 10][1, 0, 0, 1, 0] [0, 2, 0, 10][0, 1, 0, 0, 1] [0, 3, 0, 10][0, 0, 1, 0, 0] [0, 4, 0, 10][1, 0, 0, 1, 0] [0, 5, 0, 10][0, 1, 0, 0, 1] [0, 6, 0, 10][0, 0, 1, 0, 0] [0, 0, 0, 11][0, 0, 1, 0, 0] [0, 1, 0, 11][1, 0, 0, 1, 0] [0, 2, 0, 11][0, 1, 0, 0, 1] [0, 3, 0, 11][0, 0, 1, 0, 0] [0, 4, 0, 11][1, 0, 0, 1, 0] [0, 5, 0, 11][0, 1, 0, 0, 1] [0, 6, 0, 11][0, 0, 1, 0, 0] [0, 0, 0, 12][0, 0, 1, 0, 0] [0, 1, 0, 12][1, 0, 0, 1, 0] [0, 2, 0, 12][0, 1, 0, 0, 1] [0, 3, 0, 12][0, 0, 1, 0, 0] [0, 4, 0, 12][1, 0, 0, 1, 0] [0, 5, 0, 12][0, 1, 0, 0, 1] [0, 6, 0, 12][0, 0, 1, 0, 0] [0, 0, 0, 13][0, 0, 1, 0, 0] [0, 1, 0, 13][1, 0, 0, 1, 0] [0, 2, 0, 13][0, 1, 0, 0, 1] [0, 3, 0, 13][0, 0, 1, 0, 0] [0, 4, 0, 13][1, 0, 0, 1, 0] [0, 5, 0, 13][0, 1, 0, 0, 1] [0, 6, 0, 13][0, 0, 1, 0, 0] [0, 0, 0, 14][0, 0, 1, 0, 0] [0, 1, 0, 14][1, 0, 0, 1, 0] [0, 2, 0, 14][0, 1, 0, 0, 1] [0, 3, 0, 14][0, 0, 1, 0, 0] [0, 4, 0, 14][1, 0, 0, 1, 0] [0, 5, 0, 14][0, 1, 0, 0, 1] [0, 6, 0, 14][0, 0, 1, 0, 0] Col-9: String Axis Lengths: [5, 15] (NB: Matrix in Row/Column order) [Start-1, Start-1 0, Start-1 0 1, Start-1 0 1 2 3 4 5, Start-1 0 1 2 3 4 5 6, Start-1 0 1 2 3 4 5 6 7, Start-1 0 1 2 3 4 5 6 7 8, Start-1 0 1 2 3 4 5 6 7 8 9, Start-1 0 1 2 3 4 5 6 7 8 9 10, Start-1 0 1 2 3 4 5 6 7 8 9 10 11, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-2, Start-2 0, Start-2 0 1, Start-2 0 1 2 3 4 5, Start-2 0 1 2 3 4 5 6, Start-2 0 1 2 3 4 5 6 7, Start-2 0 1 2 3 4 5 6 7 8, Start-2 0 1 2 3 4 5 6 7 8 9, Start-2 0 1 2 3 4 5 6 7 8 9 10, Start-2 0 1 2 3 4 5 6 7 8 9 10 11, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-3, Start-3 0, Start-3 0 1, Start-3 0 1 2 3 4 5, Start-3 0 1 2 3 4 5 6, Start-3 0 1 2 3 4 5 6 7, Start-3 0 1 2 3 4 5 6 7 8, Start-3 0 1 2 3 4 5 6 7 8 9, Start-3 0 1 2 3 4 5 6 7 8 9 10, Start-3 0 1 2 3 4 5 6 7 8 9 10 11, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-4, Start-4 0, Start-4 0 1, Start-4 0 1 2 3 4 5, Start-4 0 1 2 3 4 5 6, Start-4 0 1 2 3 4 5 6 7, Start-4 0 1 2 3 4 5 6 7 8, Start-4 0 1 2 3 4 5 6 7 8 9, Start-4 0 1 2 3 4 5 6 7 8 9 10, Start-4 0 1 2 3 4 5 6 7 8 9 10 11, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-5, Start-5 0, Start-5 0 1, Start-5 0 1 2 3 4 5, Start-5 0 1 2 3 4 5 6, Start-5 0 1 2 3 4 5 6 7, Start-5 0 1 2 3 4 5 6 7 8, Start-5 0 1 2 3 4 5 6 7 8 9, Start-5 0 1 2 3 4 5 6 7 8 9 10, Start-5 0 1 2 3 4 5 6 7 8 9 10 11, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16] Col-11: Int Axis Lengths: [5, 15] (NB: Matrix in Row/Column order) [1, 1, 2, 16, 22, 29, 37, 46, 56, 67, 79, 92, 106, 121, 137 2, 2, 3, 17, 23, 30, 38, 47, 57, 68, 80, 93, 107, 122, 138 3, 3, 4, 18, 24, 31, 39, 48, 58, 69, 81, 94, 108, 123, 139 4, 4, 5, 19, 25, 32, 40, 49, 59, 70, 82, 95, 109, 124, 140 5, 5, 6, 20, 26, 33, 41, 50, 60, 71, 83, 96, 110, 125, 141] Try to add a string Column: Col-10 and fill it. StandardStMan Base statistics: Nr of columns : 7 Nr of rows in the columns : 15 ColIndex[0] : 0 ColOffset[0] : 242 ColIndex[1] : 1 ColOffset[1] : 0 ColIndex[2] : 2 ColOffset[2] : 0 ColIndex[3] : 0 ColOffset[3] : 0 ColIndex[4] : 0 ColOffset[4] : 53 ColIndex[5] : 3 ColOffset[5] : 0 ColIndex[6] : 4 ColOffset[6] : 0 CacheSize : 2 Size of buckets : 250 Total buckets : 29 Total Index buckets : 3 1st Index bucket : 8 Index bucket offset : 0 last String bucket used : 24 Total free buckets : 6 1st free bucket : 25 StandardStMan index: 0 statistics: Index statistics: Entries used : 2 Rows Per bucket : 12 Nr of Columns : 3 BucketNr[0] : 0 - LastRow[0] : 7 BucketNr[1] : 2 - LastRow[1] : 14 Freespace entries: 2 Offset[0]: 197 - nrBytes[0]: 45 Offset[1]: 244 - nrBytes[1]: 6 StandardStMan index: 1 statistics: Index statistics: Entries used : 2 Rows Per bucket : 15 Nr of Columns : 1 BucketNr[0] : 1 - LastRow[0] : 11 BucketNr[1] : 4 - LastRow[1] : 14 Freespace entries: 1 Offset[0]: 240 - nrBytes[0]: 10 StandardStMan index: 2 statistics: Index statistics: Entries used : 2 Rows Per bucket : 10 Nr of Columns : 1 BucketNr[0] : 3 - LastRow[0] : 6 BucketNr[1] : 7 - LastRow[1] : 14 Freespace entries: 1 Offset[0]: 240 - nrBytes[0]: 10 StandardStMan index: 3 statistics: Index statistics: Entries used : 1 Rows Per bucket : 31 Nr of Columns : 1 BucketNr[0] : 11 - LastRow[0] : 14 Freespace entries: 1 Offset[0]: 248 - nrBytes[0]: 2 StandardStMan index: 4 statistics: Index statistics: Entries used : 1 Rows Per bucket : 20 Nr of Columns : 1 BucketNr[0] : 25 - LastRow[0] : 14 Freespace entries: 1 Offset[0]: 240 - nrBytes[0]: 10 Col-4: Bool [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] Col-5: DComplex [(18,0), (17,1), (16,2), (12,6), (11,7), (10,8), (9,9), (8,10), (7,11), (6,12), (5,13), (4,14), (3,15), (2,16), (1,17)] Col-6: float Ndim=4 Axis Lengths: [2, 3, 1, 15] [0, 0, 0, 0][0, 1] [0, 1, 0, 0][2, 3] [0, 2, 0, 0][4, 5] [0, 0, 0, 1][6, 7] [0, 1, 0, 1][8, 9] [0, 2, 0, 1][10, 11] [0, 0, 0, 2][12, 13] [0, 1, 0, 2][14, 15] [0, 2, 0, 2][16, 17] [0, 0, 0, 3][36, 37] [0, 1, 0, 3][38, 39] [0, 2, 0, 3][40, 41] [0, 0, 0, 4][42, 43] [0, 1, 0, 4][44, 45] [0, 2, 0, 4][46, 47] [0, 0, 0, 5][48, 49] [0, 1, 0, 5][50, 51] [0, 2, 0, 5][52, 53] [0, 0, 0, 6][54, 55] [0, 1, 0, 6][56, 57] [0, 2, 0, 6][58, 59] [0, 0, 0, 7][60, 61] [0, 1, 0, 7][62, 63] [0, 2, 0, 7][64, 65] [0, 0, 0, 8][66, 67] [0, 1, 0, 8][68, 69] [0, 2, 0, 8][70, 71] [0, 0, 0, 9][72, 73] [0, 1, 0, 9][74, 75] [0, 2, 0, 9][76, 77] [0, 0, 0, 10][78, 79] [0, 1, 0, 10][80, 81] [0, 2, 0, 10][82, 83] [0, 0, 0, 11][84, 85] [0, 1, 0, 11][86, 87] [0, 2, 0, 11][88, 89] [0, 0, 0, 12][90, 91] [0, 1, 0, 12][92, 93] [0, 2, 0, 12][94, 95] [0, 0, 0, 13][96, 97] [0, 1, 0, 13][98, 99] [0, 2, 0, 13][100, 101] [0, 0, 0, 14][102, 103] [0, 1, 0, 14][104, 105] [0, 2, 0, 14][106, 107] Col-8: Bool Ndim=4 Axis Lengths: [5, 7, 1, 15] [0, 0, 0, 0][0, 0, 1, 0, 0] [0, 1, 0, 0][1, 0, 0, 1, 0] [0, 2, 0, 0][0, 1, 0, 0, 1] [0, 3, 0, 0][0, 0, 1, 0, 0] [0, 4, 0, 0][1, 0, 0, 1, 0] [0, 5, 0, 0][0, 1, 0, 0, 1] [0, 6, 0, 0][0, 0, 1, 0, 0] [0, 0, 0, 1][0, 0, 1, 0, 0] [0, 1, 0, 1][1, 0, 0, 1, 0] [0, 2, 0, 1][0, 1, 0, 0, 1] [0, 3, 0, 1][0, 0, 1, 0, 0] [0, 4, 0, 1][1, 0, 0, 1, 0] [0, 5, 0, 1][0, 1, 0, 0, 1] [0, 6, 0, 1][0, 0, 1, 0, 0] [0, 0, 0, 2][0, 0, 1, 0, 0] [0, 1, 0, 2][1, 0, 0, 1, 0] [0, 2, 0, 2][0, 1, 0, 0, 1] [0, 3, 0, 2][0, 0, 1, 0, 0] [0, 4, 0, 2][1, 0, 0, 1, 0] [0, 5, 0, 2][0, 1, 0, 0, 1] [0, 6, 0, 2][0, 0, 1, 0, 0] [0, 0, 0, 3][0, 0, 1, 0, 0] [0, 1, 0, 3][1, 0, 0, 1, 0] [0, 2, 0, 3][0, 1, 0, 0, 1] [0, 3, 0, 3][0, 0, 1, 0, 0] [0, 4, 0, 3][1, 0, 0, 1, 0] [0, 5, 0, 3][0, 1, 0, 0, 1] [0, 6, 0, 3][0, 0, 1, 0, 0] [0, 0, 0, 4][0, 0, 1, 0, 0] [0, 1, 0, 4][1, 0, 0, 1, 0] [0, 2, 0, 4][0, 1, 0, 0, 1] [0, 3, 0, 4][0, 0, 1, 0, 0] [0, 4, 0, 4][1, 0, 0, 1, 0] [0, 5, 0, 4][0, 1, 0, 0, 1] [0, 6, 0, 4][0, 0, 1, 0, 0] [0, 0, 0, 5][0, 0, 1, 0, 0] [0, 1, 0, 5][1, 0, 0, 1, 0] [0, 2, 0, 5][0, 1, 0, 0, 1] [0, 3, 0, 5][0, 0, 1, 0, 0] [0, 4, 0, 5][1, 0, 0, 1, 0] [0, 5, 0, 5][0, 1, 0, 0, 1] [0, 6, 0, 5][0, 0, 1, 0, 0] [0, 0, 0, 6][0, 0, 1, 0, 0] [0, 1, 0, 6][1, 0, 0, 1, 0] [0, 2, 0, 6][0, 1, 0, 0, 1] [0, 3, 0, 6][0, 0, 1, 0, 0] [0, 4, 0, 6][1, 0, 0, 1, 0] [0, 5, 0, 6][0, 1, 0, 0, 1] [0, 6, 0, 6][0, 0, 1, 0, 0] [0, 0, 0, 7][0, 0, 1, 0, 0] [0, 1, 0, 7][1, 0, 0, 1, 0] [0, 2, 0, 7][0, 1, 0, 0, 1] [0, 3, 0, 7][0, 0, 1, 0, 0] [0, 4, 0, 7][1, 0, 0, 1, 0] [0, 5, 0, 7][0, 1, 0, 0, 1] [0, 6, 0, 7][0, 0, 1, 0, 0] [0, 0, 0, 8][0, 0, 1, 0, 0] [0, 1, 0, 8][1, 0, 0, 1, 0] [0, 2, 0, 8][0, 1, 0, 0, 1] [0, 3, 0, 8][0, 0, 1, 0, 0] [0, 4, 0, 8][1, 0, 0, 1, 0] [0, 5, 0, 8][0, 1, 0, 0, 1] [0, 6, 0, 8][0, 0, 1, 0, 0] [0, 0, 0, 9][0, 0, 1, 0, 0] [0, 1, 0, 9][1, 0, 0, 1, 0] [0, 2, 0, 9][0, 1, 0, 0, 1] [0, 3, 0, 9][0, 0, 1, 0, 0] [0, 4, 0, 9][1, 0, 0, 1, 0] [0, 5, 0, 9][0, 1, 0, 0, 1] [0, 6, 0, 9][0, 0, 1, 0, 0] [0, 0, 0, 10][0, 0, 1, 0, 0] [0, 1, 0, 10][1, 0, 0, 1, 0] [0, 2, 0, 10][0, 1, 0, 0, 1] [0, 3, 0, 10][0, 0, 1, 0, 0] [0, 4, 0, 10][1, 0, 0, 1, 0] [0, 5, 0, 10][0, 1, 0, 0, 1] [0, 6, 0, 10][0, 0, 1, 0, 0] [0, 0, 0, 11][0, 0, 1, 0, 0] [0, 1, 0, 11][1, 0, 0, 1, 0] [0, 2, 0, 11][0, 1, 0, 0, 1] [0, 3, 0, 11][0, 0, 1, 0, 0] [0, 4, 0, 11][1, 0, 0, 1, 0] [0, 5, 0, 11][0, 1, 0, 0, 1] [0, 6, 0, 11][0, 0, 1, 0, 0] [0, 0, 0, 12][0, 0, 1, 0, 0] [0, 1, 0, 12][1, 0, 0, 1, 0] [0, 2, 0, 12][0, 1, 0, 0, 1] [0, 3, 0, 12][0, 0, 1, 0, 0] [0, 4, 0, 12][1, 0, 0, 1, 0] [0, 5, 0, 12][0, 1, 0, 0, 1] [0, 6, 0, 12][0, 0, 1, 0, 0] [0, 0, 0, 13][0, 0, 1, 0, 0] [0, 1, 0, 13][1, 0, 0, 1, 0] [0, 2, 0, 13][0, 1, 0, 0, 1] [0, 3, 0, 13][0, 0, 1, 0, 0] [0, 4, 0, 13][1, 0, 0, 1, 0] [0, 5, 0, 13][0, 1, 0, 0, 1] [0, 6, 0, 13][0, 0, 1, 0, 0] [0, 0, 0, 14][0, 0, 1, 0, 0] [0, 1, 0, 14][1, 0, 0, 1, 0] [0, 2, 0, 14][0, 1, 0, 0, 1] [0, 3, 0, 14][0, 0, 1, 0, 0] [0, 4, 0, 14][1, 0, 0, 1, 0] [0, 5, 0, 14][0, 1, 0, 0, 1] [0, 6, 0, 14][0, 0, 1, 0, 0] Col-9: String Axis Lengths: [5, 15] (NB: Matrix in Row/Column order) [Start-1, Start-1 0, Start-1 0 1, Start-1 0 1 2 3 4 5, Start-1 0 1 2 3 4 5 6, Start-1 0 1 2 3 4 5 6 7, Start-1 0 1 2 3 4 5 6 7 8, Start-1 0 1 2 3 4 5 6 7 8 9, Start-1 0 1 2 3 4 5 6 7 8 9 10, Start-1 0 1 2 3 4 5 6 7 8 9 10 11, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-2, Start-2 0, Start-2 0 1, Start-2 0 1 2 3 4 5, Start-2 0 1 2 3 4 5 6, Start-2 0 1 2 3 4 5 6 7, Start-2 0 1 2 3 4 5 6 7 8, Start-2 0 1 2 3 4 5 6 7 8 9, Start-2 0 1 2 3 4 5 6 7 8 9 10, Start-2 0 1 2 3 4 5 6 7 8 9 10 11, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-3, Start-3 0, Start-3 0 1, Start-3 0 1 2 3 4 5, Start-3 0 1 2 3 4 5 6, Start-3 0 1 2 3 4 5 6 7, Start-3 0 1 2 3 4 5 6 7 8, Start-3 0 1 2 3 4 5 6 7 8 9, Start-3 0 1 2 3 4 5 6 7 8 9 10, Start-3 0 1 2 3 4 5 6 7 8 9 10 11, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-4, Start-4 0, Start-4 0 1, Start-4 0 1 2 3 4 5, Start-4 0 1 2 3 4 5 6, Start-4 0 1 2 3 4 5 6 7, Start-4 0 1 2 3 4 5 6 7 8, Start-4 0 1 2 3 4 5 6 7 8 9, Start-4 0 1 2 3 4 5 6 7 8 9 10, Start-4 0 1 2 3 4 5 6 7 8 9 10 11, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-5, Start-5 0, Start-5 0 1, Start-5 0 1 2 3 4 5, Start-5 0 1 2 3 4 5 6, Start-5 0 1 2 3 4 5 6 7, Start-5 0 1 2 3 4 5 6 7 8, Start-5 0 1 2 3 4 5 6 7 8 9, Start-5 0 1 2 3 4 5 6 7 8 9 10, Start-5 0 1 2 3 4 5 6 7 8 9 10 11, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16] Col-11: Int Axis Lengths: [5, 15] (NB: Matrix in Row/Column order) [1, 1, 2, 16, 22, 29, 37, 46, 56, 67, 79, 92, 106, 121, 137 2, 2, 3, 17, 23, 30, 38, 47, 57, 68, 80, 93, 107, 122, 138 3, 3, 4, 18, 24, 31, 39, 48, 58, 69, 81, 94, 108, 123, 139 4, 4, 5, 19, 25, 32, 40, 49, 59, 70, 82, 95, 109, 124, 140 5, 5, 6, 20, 26, 33, 41, 50, 60, 71, 83, 96, 110, 125, 141] Col-10: String [String-1, String-1 0, String-1 0 1, String-1 0 1 2, String-1 0 1 2 3, String-1 0 1 2 3 4, String-1 0 1 2 3 4 5, String-1 0 1 2 3 4 5 6, String-1 0 1 2 3 4 5 6 7, String-1 0 1 2 3 4 5 6 7 8, String-1 0 1 2 3 4 5 6 7 8 9, String-1 0 1 2 3 4 5 6 7 8 9 10, String-1 0 1 2 3 4 5 6 7 8 9 10 11, String-1 0 1 2 3 4 5 6 7 8 9 10 11 12, String-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13] after removing several rows at once: StandardStMan Base statistics: Nr of columns : 7 Nr of rows in the columns : 0 ColIndex[0] : 0 ColOffset[0] : 0 ColIndex[1] : 0 ColOffset[1] : 1 ColIndex[2] : 0 ColOffset[2] : 49 ColIndex[3] : 0 ColOffset[3] : 121 ColIndex[4] : 0 ColOffset[4] : 135 ColIndex[5] : 0 ColOffset[5] : 171 ColIndex[6] : 0 ColOffset[6] : 195 CacheSize : 2 Size of buckets : 250 Total buckets : 0 Total Index buckets : 0 1st Index bucket : -1 Index bucket offset : 0 last String bucket used : 26 Total free buckets : 0 1st free bucket : -1 StandardStMan index: 0 statistics: Index statistics: Entries used : 0 Rows Per bucket : 3 Nr of Columns : 7 Freespace entries: 1 Offset[0]: 231 - nrBytes[0]: 19 Col-4: Bool [] Col-5: DComplex [] Col-6: float [] Col-8: Bool [] Col-9: String [] Col-11: Int [] Col-10: String [] testInd ... size 152 size 284 size 420 size 556 size 692 testInd2 ... nrow 1 rec1 j: String "x" size 99 nrow 1 rec1 j: String "x" size 99 nrow 1 rec1 j: String "x" size 99 nrow 1 rec1 j: String "x" size 99 nrow 1 rec1 j: String "x" size 99 casacore-3.7.1/tables/DataMan/test/tTSMShape.cc000066400000000000000000000074161476623553700212110ustar00rootroot00000000000000//# tTSMShape.cc: Test program for class TSMShape //# Copyright (C) 1994,1995,1996,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include // // Test program for class tTSMShape. // void check (const IPosition& shape, const IPosition& offsetIncr, const IPosition& stride, const char* message) { TSMShape tsmShape (shape); IPosition position(4); uInt checkOffset = 0; for (position(3)=0; position(3) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Test program for tiling a boolean column // // First build a description. void writeTable (const TSMOption& tsmOpt, const IPosition& arrayShape, const IPosition& tileShape) { cout << "WriteTable ..." << endl; // Build the table description. TableDesc td ("", "1", TableDesc::Scratch); td.addColumn (ArrayColumnDesc ("Flag", arrayShape.size(), ColumnDesc::FixedShape)); // Now create a new table from the description. SetupNewTable newtab("tTiledBool_tmp.data", td, Table::New); // Create a storage manager for it. // Let the tile shape not fit integrally in the cube shape. TiledShapeStMan sm1 ("TSMExample", tileShape); newtab.setShapeColumn ("Flag", arrayShape); newtab.bindAll (sm1); Table table(newtab, 0, False, Table::LittleEndian, tsmOpt); ArrayColumn flag (table, "Flag"); Matrix farray(arrayShape); Matrix fresult(arrayShape); for (uInt i=0; i<101; i++) { for (uInt j=0; j farray, fresult; Table table("tTiledBool_tmp.data", Table::Old, tsmOpt); cout << "Checking " << table.nrow() << " rows" << endl; ArrayColumn flag (table, "Flag"); for (uInt i=0; i #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PABLO_IO #include "IOTrace.h" #include "PabloTrace.h" extern "C" Int setTraceFileName(char *); extern "C" Int endTracing(void); #endif // PABLO_IO #include // // Test program for performance of TiledCellStMan class. // // This program tests the class TiledCellStMan and related classes. // The results are written to stdout. The script executing this program, // compares the results with the reference output file. #ifdef PABLO_IO void openPablo (const char* argv[]); void closePablo (); #endif // PABLO_IO void makeCube (const char* argv[]); void getCube (Bool trav, Bool ask); void traverse (const IPosition& cubeShape, const IPosition& tileShape); IPosition getVec (uInt nrdim, const String& prompt); int main (int argc, const char* argv[]) { // Get the command line arguments as cube shape, tile shape. if (argc < 4) { cout << ">>>" << endl; cout << "tTiledCellStM_1 uses TiledCellStMan to store nD Float " "arrays in one cell." << endl; cout << "It writes the data, reads the cell back, and iterates " "along tiles." << endl; cout << "For 3D arrays it also iterates along lines and planes" << endl; cout << "when the 4th argument is given and is not equal 0." << endl; cout << "It shows timing and cache statistics." << endl; cout << "Invoke as tTiledCellStM_1 arrayShape tileShape MaxCacheSize" << endl; cout << " Eg. tTiledCellStM_1 256,256,100 20,20,20 0" << endl; cout << "TiledStMan::makeTileShape is used when tileShape is given " "as 0" << endl; cout << "If a 5th argument is given, the user will be asked for" << endl; cout << "slice shapes, axis path, and window start and length " "until 'end' is given" << endl; cout << "This tests the function setCacheSize" << endl; cout << "<<<" << endl; return 0; } try { #ifdef PABLO_IO openPablo(argv); #endif makeCube(argv); getCube ((argc>4 && String(argv[4])!="0"), (argc>5)); #ifdef PABLO_IO closePablo(); #endif } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } #ifdef PABLO_IO #include #include void openPablo (char** argv) { // We set the name of the trace file here. Typically, you want the // file to be written to a local disk, and if possible to a file to. // Also, make sure the location you select has a lot of free space. // For multiprocessor applications, set 'tracenode' to the // appropriate node for each processor (for example, using the MPI // rank) - here we just set it to 0. // // OK there are two environment variables that drive us. First // PABLOSTATS is a colon(:) seperated list (need those leading and // trailing :'s) which tell us what processes to trace. i.e. // PABLOSTATS = ':imager:quanta:calibrater:' or PABLOSTATS = 'all' // to get them all Second PABLOSTATSDIR is the directory where the // stats files should go the default is /var/tmp be careful... // ostringstream oss; Path myname(argv[0]); int tracenode(0); if (EnvironmentVariable::isDefined("PABLOSTATS")) { String pablostats(EnvironmentVariable::get("PABLOSTATS")); Regex lookfor(String("\:")+myname.baseName()+String("\:")); if(pablostats.contains(lookfor) || pablostats == String("all")){ cout << "Writing output Pablo file" << endl; String pablostatsdir("/var/tmp"); if(gevs.isSet(String("PABLOSTATSDIR"))){ pablostatsdir = gevs.value("PABLOSTATSDIR"); } oss << pablostatsdir << "/" << myname.baseName() << "_node" << tracenode << ".PabloIO"; setTraceFileName( oss.str() ); initIOTrace(); traceEvent(1,"Starting instrumentation",24); } } } void closePablo () { traceEvent(1,"Ending instrumentation",24); endIOTrace(); endTracing(); } #endif // First build a description. void makeCube (const char* argv[]) { // Convert the command line arguments to shapes. uInt i, maxCacheSize; Vector cubeV (stringToVector (argv[1])); Vector tileV (stringToVector (argv[2])); istringstream istr1(argv[3]); istr1 >> maxCacheSize; uInt nrdim = cubeV.nelements(); IPosition cubeShape (nrdim); IPosition tileShape (nrdim); for (i=0; i> cubeShape(i); if (cubeShape(i) <= 0) { throw AipsError("Arrayshape " + String::toString(cubeShape(i)) + " must be > 0"); } } if (tileV.nelements() != nrdim) { if (tileV.nelements() != 1 || tileV(0) != "0") { throw AipsError("Array and tile must have same dimensionality"); } tileShape = TiledStMan::makeTileShape (cubeShape); }else{ for (i=0; i> tileShape(i); if (tileShape(i) <= 0) { throw AipsError("Tileshape " + String::toString(tileShape(i)) + " must be > 0"); } } } Vector weight(nrdim); Vector tolerance(nrdim); for (i=0; i ("Data", cubeShape, ColumnDesc::FixedShape)); td.defineHypercolumn ("TSMExample", nrdim, stringToVector ("Data")); // Now create a new table from the description. SetupNewTable newtab("tTiledCellStM_1_tmp.data", td, Table::New); // Create a storage manager for it. TiledCellStMan sm1 ("TSMExample", tileShape, maxCacheSize); newtab.bindAll (sm1); Table table(newtab, 1); ArrayColumn data (table, "Data"); Array array(cubeShape); Timer timer; indgen (array); timer.show ("indgen "); timer.mark(); data.put (0, array); timer.show ("put "); table.flush(); timer.show ("put+flush"); timer.mark(); table.copy ("tTiledCellStM_1_tmp.data2", Table::New); timer.show ("copy "); timer.mark(); data.get (0, array); timer.show ("get "); ROTiledStManAccessor accessor(table, "TSMExample"); accessor.showCacheStatistics (cout); } void getCube (Bool trav, Bool ask) { IPosition cubeShape; IPosition tileShape; double sizeMb = sizeof(Float); double realtime; uInt i, nrdim; Timer timer; { Table table("tTiledCellStM_1_tmp.data2"); timer.show ("reopen "); ROTiledStManAccessor accessor(table, "TSMExample"); ArrayColumn data (table, "Data"); cubeShape = data.shape (0); sizeMb *= cubeShape.product(); sizeMb /= 1024*1024; tileShape = accessor.tileShape (0); nrdim = cubeShape.nelements(); Array result; timer.mark(); data.get (0, result); realtime = timer.real(); timer.show ("get cell "); cout << "Throughput " << sizeMb/realtime << " Mb/sec" << endl; accessor.showCacheStatistics (cout); accessor.clearCaches(); } if (nrdim == 3) { Table table("tTiledCellStM_1_tmp.data2"); ROTiledStManAccessor accessor(table, "TSMExample"); ArrayColumn data (table, "Data"); cubeShape = data.shape (0); tileShape = accessor.tileShape (0); nrdim = cubeShape.nelements(); Array result; timer.mark(); IPosition blc(nrdim, 0); IPosition len = cubeShape; len[1] = 1; sizeMb *= len.product(); sizeMb /= 1024*1024; data.getSlice (0, Slicer(blc,len), result); realtime = timer.real(); timer.show ("get slice"); cout << "Throughput " << sizeMb/realtime << " Mb/sec" << endl; accessor.showCacheStatistics (cout); accessor.clearCaches(); } { Table table("tTiledCellStM_1_tmp.data"); ROTiledStManAccessor accessor(table, "TSMExample"); accessor.setCacheSize (0, tileShape, IPosition()); ArrayColumn data (table, "Data"); Array result; uInt nr = 0; timer.mark(); IPosition last(nrdim); IPosition nrt(nrdim); for (i=0; i arr = data.getSlice (0, Slicer (start, length)); nr++; for (i=0; i> str; if (str == "end") { return IPosition(); } Vector vec = stringToVector (str); if (vec.nelements() > nrdim) { cout << "value can contain max. " << nrdim << " values" << endl; }else{ Bool error = False; IPosition pos(vec.nelements()); for (uInt i=0; i> pos(i); if (pos(i) < 0) { cout << "Value " << pos(i) << " must be >= 0" << endl; error = True; break; } } if (!error) { return pos; } } } return IPosition(); } void traverse (const IPosition& cubeShape, const IPosition& tileShape) { double sizeMb = sizeof(Float) * cubeShape.product(); sizeMb /= 1024*1024; double realtime; Timer timer; if (cubeShape(2) > 1) { IPosition length (3, 1, 1, cubeShape(2)); Table table("tTiledCellStM_1_tmp.data2"); ROTiledStManAccessor accessor(table, "TSMExample"); accessor.setCacheSize (0, length, IPosition(2,2,1)); ArrayColumn data (table, "Data"); Array result; uInt nr = 0; timer.mark(); for (Int i=0; i arr = data.getSlice (0, Slicer (IPosition(3,i,j,0), length)); nr++; } } cout << "arraySlice z along y,x" << " (" << nr << " passes)" << endl; realtime = timer.real(); timer.show ("get "); cout << "Throughput " << sizeMb/realtime << " Mb/sec" << endl; accessor.showCacheStatistics (cout); accessor.clearCaches(); } if (cubeShape(2) > 1) { IPosition length (3, 1, 1, cubeShape(2)); Table table("tTiledCellStM_1_tmp.data"); ROTiledStManAccessor accessor(table, "TSMExample"); accessor.setCacheSize (0, length, IPosition(1,2)); ArrayColumn data (table, "Data"); Array result; uInt nr = 0; timer.mark(); for (Int j=0; j arr = data.getSlice (0, Slicer (IPosition(3,i,j,0), length)); nr++; } } cout << "arraySlice z along x,y" << " (" << nr << " passes)" << endl; realtime = timer.real(); timer.show ("get "); cout << "Throughput " << sizeMb/realtime << " Mb/sec" << endl; accessor.showCacheStatistics (cout); accessor.clearCaches(); } if (cubeShape(1) > 1) { IPosition length (3, 1, cubeShape(1), 1); Table table("tTiledCellStM_1_tmp.data2"); ROTiledStManAccessor accessor(table, "TSMExample"); accessor.setCacheSize (0, length, IPosition(3,1,2,0)); ArrayColumn data (table, "Data"); Array result; uInt nr = 0; timer.mark(); for (Int i=0; i arr = data.getSlice (0, Slicer (IPosition(3,i,0,j), length)); nr++; } } cout << "arraySlice y along z,x" << " (" << nr << " passes)" << endl; realtime = timer.real(); timer.show ("get "); cout << "Throughput " << sizeMb/realtime << " Mb/sec" << endl; accessor.showCacheStatistics (cout); accessor.clearCaches(); } if (cubeShape(1) > 1) { IPosition length (3, 1, cubeShape(1), 1); Table table("tTiledCellStM_1_tmp.data"); ROTiledStManAccessor accessor(table, "TSMExample"); accessor.setCacheSize (0, length, IPosition(1,1)); ArrayColumn data (table, "Data"); Array result; uInt nr = 0; timer.mark(); for (Int j=0; j arr = data.getSlice (0, Slicer (IPosition(3,i,0,j), length)); nr++; } } cout << "arraySlice y along x,z" << " (" << nr << " passes)" << endl; realtime = timer.real(); timer.show ("get "); cout << "Throughput " << sizeMb/realtime << " Mb/sec" << endl; accessor.showCacheStatistics (cout); accessor.clearCaches(); } if (cubeShape(0) > 1) { IPosition length (3, cubeShape(0), 1, 1); Table table("tTiledCellStM_1_tmp.data2"); ROTiledStManAccessor accessor(table, "TSMExample"); accessor.setCacheSize (0, length, IPosition(2,0,2)); ArrayColumn data (table, "Data"); Array result; uInt nr = 0; timer.mark(); for (Int i=0; i arr = data.getSlice (0, Slicer (IPosition(3,0,i,j), length)); nr++; } } cout << "arraySlice x along z,y" << " (" << nr << " passes)" << endl; realtime = timer.real(); timer.show ("get "); cout << "Throughput " << sizeMb/realtime << " Mb/sec" << endl; accessor.showCacheStatistics (cout); accessor.clearCaches(); } if (cubeShape(0) > 1) { IPosition length (3, cubeShape(0), 1, 1); Table table("tTiledCellStM_1_tmp.data"); ROTiledStManAccessor accessor(table, "TSMExample"); accessor.setCacheSize (0, length, IPosition()); ArrayColumn data (table, "Data"); Array result; uInt nr = 0; timer.mark(); for (Int j=0; j arr = data.getSlice (0, Slicer (IPosition(3,0,i,j), length)); nr++; } } cout << "arraySlice x along y,z" << " (" << nr << " passes)" << endl; realtime = timer.real(); timer.show ("get "); cout << "Throughput " << sizeMb/realtime << " Mb/sec" << endl; accessor.showCacheStatistics (cout); accessor.clearCaches(); } if (cubeShape(0) > 1 && cubeShape(1) > 1 && cubeShape(2) > 1) { IPosition length (3, cubeShape(0), cubeShape(1), 1); Table table("tTiledCellStM_1_tmp.data2"); ROTiledStManAccessor accessor(table, "TSMExample"); accessor.setCacheSize (0, length, IPosition()); ArrayColumn data (table, "Data"); Array result; uInt nr = 0; timer.mark(); for (Int j=0; j arr = data.getSlice (0, Slicer (IPosition(3,0,0,j), length)); nr++; } cout << "arrayPlane x,y along z" << " (" << nr << " passes)" << endl; realtime = timer.real(); timer.show ("get "); cout << "Throughput " << sizeMb/realtime << " Mb/sec" << endl; accessor.showCacheStatistics (cout); accessor.clearCaches(); } if (cubeShape(0) > 1 && cubeShape(1) > 1 && cubeShape(2) > 1) { IPosition length (3, cubeShape(0), 1, cubeShape(2)); Table table("tTiledCellStM_1_tmp.data"); ROTiledStManAccessor accessor(table, "TSMExample"); accessor.setCacheSize (0, length, IPosition(3,0,2,1)); ArrayColumn data (table, "Data"); Array result; uInt nr = 0; timer.mark(); for (Int j=0; j arr = data.getSlice (0, Slicer (IPosition(3,0,j,0), length)); nr++; } cout << "arrayPlane x,z along y" << " (" << nr << " passes)" << endl; realtime = timer.real(); timer.show ("get "); cout << "Throughput " << sizeMb/realtime << " Mb/sec" << endl; accessor.showCacheStatistics (cout); accessor.clearCaches(); } if (cubeShape(0) > 1 && cubeShape(1) > 1 && cubeShape(2) > 1) { IPosition length (3, 1, cubeShape(1), cubeShape(2)); Table table("tTiledCellStM_1_tmp.data2"); ROTiledStManAccessor accessor(table, "TSMExample"); accessor.setCacheSize (0, length, IPosition(3,1,2,0)); ArrayColumn data (table, "Data"); Array result; uInt nr = 0; timer.mark(); for (Int j=0; j arr = data.getSlice (0, Slicer (IPosition(3,j,0,0), length)); nr++; } cout << "arrayPlane y,z along x" << " (" << nr << " passes)" << endl; realtime = timer.real(); timer.show ("get "); cout << "Throughput " << sizeMb/realtime << " Mb/sec" << endl; accessor.showCacheStatistics (cout); accessor.clearCaches(); } if (cubeShape(2) > 1) { Table table("tTiledCellStM_1_tmp.data"); ROTiledStManAccessor accessor(table, "TSMExample"); IPosition length (3, cubeShape(0), cubeShape(1), tileShape(2)); accessor.setCacheSize (0, length, IPosition()); ArrayColumn data (table, "Data"); Array result; uInt nr = 0; timer.mark(); Int last = cubeShape(2) % tileShape(2); if (last == 0) last = tileShape(2); Int nrk = (cubeShape(2)-1)/tileShape(2); for (Int k=0; k<=nrk; k++) { if (k==nrk) { length(2) = last; } Array arr = data.getSlice (0, Slicer (IPosition(3, 0, 0, k*tileShape(2)), length)); nr++; } cout << "array x,y,z along z-tiles" << " (" << nr << " passes)" << endl; realtime = timer.real(); timer.show ("get "); cout << "Throughput " << sizeMb/realtime << " Mb/sec" << endl; accessor.showCacheStatistics (cout); accessor.clearCaches(); } } casacore-3.7.1/tables/DataMan/test/tTiledCellStMan.cc000066400000000000000000000304661476623553700223720ustar00rootroot00000000000000//# tTiledCellStMan.cc: Test program for the TiledCellStMan classes //# Copyright (C) 1994,1995,1996,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Test program for the TiledCellStMan class. // // This program tests the class TiledCellStMan and related classes. // The results are written to stdout. The script executing this program, // compares the results with the reference output file. void writeFixed(const TSMOption&); void readTable(const TSMOption&); void writeVar(const TSMOption&); void writeFixVar(const TSMOption&); void writeNoHyper(const TSMOption&); int main () { try { writeFixed(TSMOption::Cache); readTable(TSMOption::Cache); writeVar(TSMOption::Buffer); readTable(TSMOption::MMap); writeFixVar(TSMOption::Cache); readTable(TSMOption::Buffer); writeNoHyper(TSMOption::MMap); readTable(TSMOption::Cache); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } // First build a description. void writeFixed(const TSMOption& tsmOpt) { // Build the table description. TableDesc td ("", "1", TableDesc::Scratch); td.addColumn (ArrayColumnDesc ("Pol", IPosition(1,16), ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc ("Freq", 1, ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc ("Data", 2, ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc ("Weight", IPosition(2,16,25), ColumnDesc::FixedShape)); td.defineHypercolumn ("TSMExample", 2, stringToVector ("Data,Weight"), stringToVector ("Pol,Freq")); // Now create a new table from the description. SetupNewTable newtab("tTiledCellStMan_tmp.data", td, Table::New); // Create a storage manager for it. TiledCellStMan sm1 ("TSMExample", IPosition(2,5,6)); newtab.setShapeColumn ("Freq", IPosition(1,25)); newtab.setShapeColumn ("Data", IPosition(2,16,25)); newtab.bindAll (sm1); Table table(newtab, 0, False, Table::LittleEndian, tsmOpt); Vector freqValues(25); Vector polValues(16); indgen (freqValues, float(200)); indgen (polValues, float(300)); ArrayColumn freq (table, "Freq"); ArrayColumn pol (table, "Pol"); ArrayColumn data (table, "Data"); ArrayColumn weight (table, "Weight"); Matrix array(IPosition(2,16,25)); Matrix result(IPosition(2,16,25)); uInt i; indgen (array); for (i=0; i<101; i++) { table.addRow(); data.put (i, array); weight.put (i, array+float(100)); freq.put (i, freqValues); pol.put (i, polValues); array += float(200); freqValues += float(200); polValues += float(200); } indgen (array); indgen (freqValues, float(200)); indgen (polValues, float(300)); for (i=0; i freq (table, "Freq"); ArrayColumn pol (table, "Pol"); ArrayColumn data (table, "Data"); ArrayColumn weight (table, "Weight"); Vector freqValues(25); Vector polValues(16); indgen (freqValues, float(200)); indgen (polValues, float(300)); Matrix array(IPosition(2,16,25)); Matrix result(IPosition(2,16,25)); uInt i; indgen (array); for (i=0; i ("Pol", 1)); td.addColumn (ArrayColumnDesc ("Freq", 1)); td.addColumn (ArrayColumnDesc ("Data", 2)); td.addColumn (ArrayColumnDesc ("Weight", 2)); td.defineHypercolumn ("TSMExample", 2, stringToVector ("Data,Weight"), stringToVector ("Pol,Freq")); // Now create a new table from the description. SetupNewTable newtab("tTiledCellStMan_tmp.data", td, Table::New); // Create a storage manager for it. TiledCellStMan sm1 ("TSMExample", IPosition(2,5,6)); newtab.bindAll (sm1); Table table(newtab, 0, False, Table::BigEndian, tsmOpt); Vector freqValues(25); Vector polValues(16); indgen (freqValues, float(200)); indgen (polValues, float(300)); ArrayColumn freq (table, "Freq"); ArrayColumn pol (table, "Pol"); ArrayColumn data (table, "Data"); ArrayColumn weight (table, "Weight"); Matrix array(IPosition(2,16,25)); Matrix result(IPosition(2,16,25)); uInt i; indgen (array); for (i=0; i<5; i++) { table.addRow(); cout << " pol.isDefined=" << pol.isDefined(i) << endl; pol.setShape (i, IPosition(1,16), IPosition(1,1)); cout << " pol.isDefined=" << pol.isDefined(i) << endl; cout << "data.isDefined=" << data.isDefined(i) << endl; data.setShape (i, IPosition(2,16,25), IPosition(2,12,10)); cout << "weig.isDefined=" << weight.isDefined(i) << endl; cout << "freq.isDefined=" << freq.isDefined(i) << endl; cout << pol.shape(i) << freq.shape(i) << data.shape(i) << weight.shape(i) << endl; data.put (i, array); weight.put (i, array+float(100)); freq.put (i, freqValues); pol.put (i, polValues); array += float(200); freqValues += float(200); polValues += float(200); } } void writeFixVar(const TSMOption& tsmOpt) { // Build the table description. TableDesc td ("", "1", TableDesc::Scratch); td.addColumn (ArrayColumnDesc ("Pol", 1)); td.addColumn (ArrayColumnDesc ("Freq", 1)); td.addColumn (ArrayColumnDesc ("Data", 2)); td.addColumn (ArrayColumnDesc ("Weight", IPosition(2,16,25), ColumnDesc::FixedShape)); td.defineHypercolumn ("TSMExample", 2, stringToVector ("Data,Weight"), stringToVector ("Pol,Freq")); // Now create a new table from the description. SetupNewTable newtab("tTiledCellStMan_tmp.data", td, Table::New); // Create a storage manager for it. TiledCellStMan sm1 ("TSMExample", IPosition(2,5,6)); newtab.bindAll (sm1); Table table(newtab, 0, False, Table::LocalEndian, tsmOpt); Vector freqValues(25); Vector polValues(16); indgen (freqValues, float(200)); indgen (polValues, float(300)); ArrayColumn freq (table, "Freq"); ArrayColumn pol (table, "Pol"); ArrayColumn data (table, "Data"); ArrayColumn weight (table, "Weight"); Matrix array(IPosition(2,16,25)); Matrix result(IPosition(2,16,25)); uInt i; indgen (array); for (i=0; i<5; i++) { table.addRow(); cout << " pol.isDefined=" << pol.isDefined(i) << endl; pol.setShape (i, IPosition(1,16), IPosition(1,1)); cout << " pol.isDefined=" << pol.isDefined(i) << endl; cout << "data.isDefined=" << data.isDefined(i) << endl; data.setShape (i, IPosition(2,16,25), IPosition(2,12,10)); cout << "weig.isDefined=" << weight.isDefined(i) << endl; cout << "freq.isDefined=" << freq.isDefined(i) << endl; cout << pol.shape(i) << freq.shape(i) << data.shape(i) << weight.shape(i) << endl; data.put (i, array); weight.put (i, array+float(100)); freq.put (i, freqValues); pol.put (i, polValues); array += float(200); freqValues += float(200); polValues += float(200); } } void writeNoHyper(const TSMOption& tsmOpt) { // Build the table description. TableDesc td ("", "1", TableDesc::Scratch); td.addColumn (ArrayColumnDesc ("Pol", IPosition(1,16), ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc ("Freq", 1, ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc ("Data", 2, ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc ("Weight", IPosition(2,16,25), ColumnDesc::FixedShape)); // Now create a new table from the description. SetupNewTable newtab("tTiledCellStMan_tmp.data", td, Table::New); // Create a storage manager for it. TiledCellStMan sm1 ("TSMExample", IPosition(2,5,6)); newtab.setShapeColumn ("Freq", IPosition(1,25)); newtab.setShapeColumn ("Data", IPosition(2,16,25)); newtab.bindColumn ("Data", sm1); newtab.bindColumn ("Weight", sm1); Table table(newtab, 0, False, Table::AipsrcEndian, tsmOpt); Vector freqValues(25); Vector polValues(16); indgen (freqValues, float(200)); indgen (polValues, float(300)); ArrayColumn freq (table, "Freq"); ArrayColumn pol (table, "Pol"); ArrayColumn data (table, "Data"); ArrayColumn weight (table, "Weight"); Matrix array(IPosition(2,16,25)); Matrix result(IPosition(2,16,25)); uInt i; indgen (array); for (i=0; i<101; i++) { table.addRow(); data.put (i, array); weight.put (i, array+float(100)); freq.put (i, freqValues); pol.put (i, polValues); array += float(200); freqValues += float(200); polValues += float(200); } indgen (array); indgen (freqValues, float(200)); indgen (polValues, float(300)); for (i=0; i #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Test program for the TiledColumnStMan class. // // This program tests the class TiledColumnStMan and related classes. // The results are written to stdout. The script executing this program, // compares the results with the reference output file. void writeFixed(const TSMOption&); void readTable(const TSMOption&, Bool readKeys); void writeNoHyper(const TSMOption&); void extendOnly(const TSMOption&); int main () { try { writeFixed(TSMOption::Cache); readTable(TSMOption::MMap, True); writeNoHyper(TSMOption::MMap); readTable(TSMOption::Buffer, False); writeFixed(TSMOption::Buffer); readTable(TSMOption::Cache, False); extendOnly(TSMOption::Cache); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } // First build a description. void writeFixed(const TSMOption& tsmOpt) { // Build the table description. TableDesc td ("", "1", TableDesc::Scratch); td.addColumn (ArrayColumnDesc ("Pol", IPosition(1,16), ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc ("Freq", 1, ColumnDesc::FixedShape)); td.addColumn (ScalarColumnDesc ("Time")); td.addColumn (ArrayColumnDesc ("Data", 2, ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc ("Weight", IPosition(2,16,20), ColumnDesc::FixedShape)); td.defineHypercolumn ("TSMExample", 3, stringToVector ("Data,Weight"), stringToVector ("Pol,Freq,Time")); // Now create a new table from the description. SetupNewTable newtab("tTiledColumnStMan_tmp.data", td, Table::New); // Create a storage manager for it. TiledColumnStMan sm1 ("TSMExample", IPosition(3,5,6,1)); newtab.setShapeColumn ("Freq", IPosition(1,20)); newtab.setShapeColumn ("Data", IPosition(2,16,20)); newtab.bindAll (sm1); Table table(newtab, 0, False, Table::LittleEndian, tsmOpt); Vector freqValues(20); Vector polValues(16); indgen (freqValues, float(200)); indgen (polValues, float(300)); float timeValue; timeValue = 34; ArrayColumn freq (table, "Freq"); ArrayColumn pol (table, "Pol"); ArrayColumn data (table, "Data"); ArrayColumn weight (table, "Weight"); ScalarColumn time (table, "Time"); Matrix array(IPosition(2,16,20)); Matrix result(IPosition(2,16,20)); uInt i; indgen (array); for (i=0; i<51; i++) { table.addRow(); data.put (i, array); weight.put (i, array+float(100)); time.put (i, timeValue); array += float(200); timeValue += 5; } freq.put (0, freqValues); pol.put (0, polValues); timeValue = 34; indgen (array); for (i=0; i freq (table, "Freq"); ArrayColumn pol (table, "Pol"); ArrayColumn data (table, "Data"); ArrayColumn weight (table, "Weight"); ScalarColumn time (table, "Time"); Vector freqValues(20); Vector polValues(16); indgen (freqValues, float(200)); indgen (polValues, float(300)); float timeValue; timeValue = 34; Matrix array(IPosition(2,16,20)); Matrix result(IPosition(2,16,20)); uInt i; indgen (array); for (i=0; i result; data.getColumn (result); if (result.shape() != IPosition (3,16,20,51)) { cout << "shape of getColumn result is incorrect" << endl; }else{ indgen (array); uInt i=0; ArrayIterator iter (result, 2); while (! iter.pastEnd()) { if (! allEQ (iter.array(), array)) { cout << "mismatch in getColumn data row " << i << endl; } array += float(200); i++; iter.next(); } } accessor.showCacheStatistics (cout); accessor.clearCaches(); cout << "getColumn has been done" << endl; } // Get slices in the entire column. { uInt i, j; Cube array(1,1,51); for (i=0; i<51; i++) { array(0,0,i) = 200*i; } Array result; for (j=0; j<20; j++) { for (i=0; i<16; i++) { data.getColumn (Slicer (IPosition(2,i,j)), result); if (! allEQ (result, array)) { cout << "mismatch in getColumnSlice " << i << "," << j << endl; } array += float(1); } } accessor.showCacheStatistics (cout); accessor.clearCaches(); cout << "getColumnSlice's have been done" << endl; } // Get strided slices in the entire column. { uInt i, j; Cube array(2,2,51); for (i=0; i<51; i++) { array(0,0,i) = 200*i; array(1,0,i) = 200*i + 16/2; array(0,1,i) = 200*i + 16*20/2; array(1,1,i) = 200*i + 16*20/2 + 16/2; } Array result; for (j=0; j<20/2; j++) { for (i=0; i<16/2; i++) { data.getColumn (Slicer (IPosition(2,i,j), IPosition(2,2,2), IPosition(2,16/2,20/2)), result); if (! allEQ (result, array)) { cout << "mismatch in getColumnSlice " << i << "," << j << endl; } array += float(1); } array += float(8); } accessor.showCacheStatistics (cout); accessor.clearCaches(); cout << "strided getColumnSlice's have been done" << endl; } // Get slices from each cell. { uInt i, j, k; Matrix array(2,4); Array result; for (k=0; k array(8,5); Array result; for (k=0; k ("Pol", IPosition(1,16), ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc ("Freq", 1, ColumnDesc::FixedShape)); td.addColumn (ScalarColumnDesc ("Time")); td.addColumn (ArrayColumnDesc ("Data", 2, ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc ("Weight", IPosition(2,16,20), ColumnDesc::FixedShape)); // Now create a new table from the description. SetupNewTable newtab("tTiledColumnStMan_tmp.data", td, Table::New); // Create a storage manager for it. TiledColumnStMan sm1 ("TSMExample", IPosition(3,5,6,1)); newtab.setShapeColumn ("Freq", IPosition(1,20)); newtab.setShapeColumn ("Data", IPosition(2,16,20)); newtab.bindColumn ("Data", sm1); newtab.bindColumn ("Weight", sm1); Table table(newtab, 0, False, Table::BigEndian, tsmOpt); Vector freqValues(20); Vector polValues(16); indgen (freqValues, float(200)); indgen (polValues, float(300)); float timeValue; timeValue = 34; ArrayColumn freq (table, "Freq"); ArrayColumn pol (table, "Pol"); ArrayColumn data (table, "Data"); ArrayColumn weight (table, "Weight"); ScalarColumn time (table, "Time"); Matrix array(IPosition(2,16,20)); Matrix result(IPosition(2,16,20)); uInt i; indgen (array); for (i=0; i<51; i++) { table.addRow(); data.put (i, array); weight.put (i, array+float(100)); time.put (i, timeValue); array += float(200); timeValue += 5; freq.put (i, freqValues); pol.put (i, polValues); } timeValue = 34; indgen (array); for (i=0; i ("Weight", IPosition(2,16,20), ColumnDesc::FixedShape)); // Now create a new table from the description. SetupNewTable newtab("tTiledColumnStMan_tm2p.data", td, Table::New); // Create a storage manager for it. TiledColumnStMan sm1 ("TSMExample", IPosition(3,5,6,1)); newtab.bindColumn ("Weight", sm1); Table table(newtab, 0, False, Table::LocalEndian, tsmOpt); ArrayColumn weight (table, "Weight"); uInt i; for (i=0; i<51; i++) { table.addRow(); } ROTiledStManAccessor accessor (table, "TSMExample"); accessor.showCacheStatistics (cout); AlwaysAssertExit (accessor.nhypercubes() == 1); AlwaysAssertExit (accessor.hypercubeShape(2) == IPosition(3,16,20, table.nrow())); AlwaysAssertExit (accessor.tileShape(2) == IPosition(3,5,6,1)); AlwaysAssertExit (accessor.getHypercubeShape(0) == IPosition(3,16,20, table.nrow())); AlwaysAssertExit (accessor.getTileShape(0) == IPosition(3,5,6,1)); AlwaysAssertExit (accessor.getBucketSize(0) == accessor.bucketSize(2)); AlwaysAssertExit (accessor.getCacheSize(0) == accessor.cacheSize(2)); } casacore-3.7.1/tables/DataMan/test/tTiledColumnStMan.out000066400000000000000000000051661476623553700231710ustar00rootroot00000000000000>>> TSMCube cache statistics: cubeShape: [16, 20, 51] tileShape: [5, 6, 1] maxCacheSz:0 cacheSize: 1 (*240) #buckets: 816 (< #reads + #writes!) #reads: 2448 #inits: 816 #writes: 1632 #accesses: 3264 hit-rate: 0% <<< >>> No TSMCube cache statistics (uses mmap) <<< get's have been done >>> No TSMCube cache statistics (uses mmap) <<< getColumn has been done >>> No TSMCube cache statistics (uses mmap) <<< getColumnSlice's have been done >>> No TSMCube cache statistics (uses mmap) <<< strided getColumnSlice's have been done >>> No TSMCube cache statistics (uses mmap) <<< getSlice's have been done >>> No TSMCube cache statistics (uses mmap) <<< getSlice's with strides have been done >>> No TSMCube cache statistics (uses mmap) <<< >>> No TSMCube cache statistics (uses mmap) <<< get's have been done >>> No TSMCube cache statistics (uses mmap) <<< getColumn has been done >>> No TSMCube cache statistics (uses mmap) <<< getColumnSlice's have been done >>> No TSMCube cache statistics (uses mmap) <<< strided getColumnSlice's have been done >>> No TSMCube cache statistics (uses mmap) <<< getSlice's have been done >>> No TSMCube cache statistics (uses mmap) <<< getSlice's with strides have been done >>> No TSMCube cache statistics (uses mmap) <<< >>> TSMCube cache statistics: cubeShape: [16, 20, 51] tileShape: [5, 6, 1] maxCacheSz:0 cacheSize: 1 (*240) #buckets: 816 (< #reads + #writes!) #reads: 1632 #accesses: 1632 hit-rate: 0% <<< get's have been done >>> TSMCube cache statistics: cubeShape: [16, 20, 51] tileShape: [5, 6, 1] maxCacheSz:0 cacheSize: 1 (*240) #buckets: 816 #reads: 816 #accesses: 816 hit-rate: 0% <<< getColumn has been done >>> TSMCube cache statistics: cubeShape: [16, 20, 51] tileShape: [5, 6, 1] maxCacheSz:0 cacheSize: 204 (*240) #buckets: 816 #reads: 816 #accesses: 16320 hit-rate: 95% <<< getColumnSlice's have been done >>> TSMCube cache statistics: cubeShape: [16, 20, 51] tileShape: [5, 6, 1] maxCacheSz:0 cacheSize: 204 (*240) #buckets: 816 (< #reads + #writes!) #reads: 5100 #accesses: 16320 hit-rate: 68.75% <<< strided getColumnSlice's have been done >>> TSMCube cache statistics: cubeShape: [16, 20, 51] tileShape: [5, 6, 1] maxCacheSz:0 cacheSize: 4 (*240) #buckets: 816 (< #reads + #writes!) #reads: 1224 #accesses: 3570 hit-rate: 65.7143% <<< getSlice's have been done >>> TSMCube cache statistics: cubeShape: [16, 20, 51] tileShape: [5, 6, 1] maxCacheSz:0 cacheSize: 4 (*240) #buckets: 816 (< #reads + #writes!) #reads: 4998 #accesses: 4998 hit-rate: 0% <<< getSlice's with strides have been done casacore-3.7.1/tables/DataMan/test/tTiledDataStM_1.cc000066400000000000000000000202511476623553700222540ustar00rootroot00000000000000//# tTiledDataStM_1.cc: Test program for performance of TiledDataStMan class //# Copyright (C) 1994,1995,1996,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Test program for performance of TiledDataStMan class. // // This program tests the class TiledDataStMan and related classes. // The results are written to stdout. The script executing this program, // compares the results with the reference output file. void a (const char* argv[]); void b(); int main (int argc, const char* argv[]) { // Get the command line arguments as cell shape, cube shape, tile shape. if (argc != 5) { cout << ">>>" << endl; cout << "tTiledDataStM_1 uses TiledDataStMan to store float arrays." << endl; cout << "It writes the data and reads them back per cell and " "per column-slice" << endl; cout << "and shows timing and cache statistics." << endl; cout << "Invoke as tTiledDataStM_1 arrayNdim cubeShape tileShape MaxCacheSize" << endl; cout << "Eg. tTiledDataStM_1 2 4,128,96,1000 4,16,4,5 0" << endl; cout << "<<<" << endl; return 0; } try { a (argv); b (); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } // First build a description. void a (const char* argum[]) { // Convert the command line arguments to shapes. uInt i, nrdim, maxCacheSize; istringstream istr0(argum[1]); istr0 >> nrdim; Vector cubeV (stringToVector (argum[2])); Vector tileV (stringToVector (argum[3])); istringstream istr1(argum[4]); istr1 >> maxCacheSize; if (cubeV.nelements() != tileV.nelements()) { throw AipsError("Cube and tile must have same dimensionality"); } if (nrdim > cubeV.nelements()) { throw AipsError("Cell-dimensionality must be <= cube-dimensionality"); } IPosition cellShape (nrdim); IPosition cubeShape (cubeV.nelements()); IPosition tileShape (tileV.nelements()); for (i=0; i> cubeShape(i); if (cubeShape(i) <= 0) { throw AipsError("Cubeshape " + String::toString(cubeShape(i)) + " must be > 0"); } } for (i=0; i> tileShape(i); if (tileShape(i) <= 0) { throw AipsError("Tileshape " + String::toString(tileShape(i)) + " must be > 0"); } } for (i=0; i array (cubeShape); array = 0; ArrayIterator iter (array, nrdim); Timer timer; while (! iter.pastEnd()) { Array result; result = iter.array(); iter.next(); } timer.show ("ArrayIter"); } // Now test with slices in orthogonal direction. if (cubeShape.product() < 10*1024*1024) { uInt nr = cubeShape.product() / 1024; if (nr == 0) { nr = 1; } Array array (IPosition(2,1024,nr)); array = 0; Timer timer; for (i=0; i<1024; i++) { Array result; result = array(IPosition(2,i,0), IPosition(2,i,nr-1)); } cout << "ArrayIter [1024, " << nr << "]" << endl; timer.show (" "); } // Build the table description. TableDesc td ("", "1", TableDesc::Scratch); td.addColumn (ArrayColumnDesc ("Data", cellShape, ColumnDesc::FixedShape)); td.defineHypercolumn ("TSMExample", cubeShape.nelements(), stringToVector ("Data")); // Now create a new table from the description. SetupNewTable newtab("tTiledDataStM_1_tmp.data", td, Table::New); // Create a storage manager for it. TiledDataStMan sm1 ("TSMExample", maxCacheSize); newtab.bindAll (sm1); Table table(newtab, nrrow); TiledDataStManAccessor accessor(table, "TSMExample"); Record values; accessor.addHypercube (cubeShape, tileShape, values); ArrayColumn data (table, "Data"); Array array(cellShape); indgen (array); Timer timer; for (i=0; i data (table, "Data"); cellShape = data.shape (0); Array result; timer.mark(); for (i=0; i data (table, "Data"); Array result; ArrayPositionIterator iter (cellShape, origin, size_t(1)); timer.mark(); uInt nr = 0; while (! iter.pastEnd()) { nr++; data.getColumn (Slicer(iter.pos(), length), result); iter.next(); } cout << "columnSlice " << length << " (" << nr << " passes)" << endl; timer.show ("get "); accessor.showCacheStatistics (cout); accessor.clearCaches(); } { Table table("tTiledDataStM_1_tmp.data"); ROTiledStManAccessor accessor(table, "TSMExample"); ArrayColumn data (table, "Data"); Array result; ArrayPositionIterator iter (cellShape, origin, size_t(0)); length(0) = 1; uInt nr = 0; timer.mark(); while (! iter.pastEnd()) { nr++; data.getColumn (Slicer(iter.pos(), length), result); iter.next(); } cout << "columnSlice " << length << " (" << nr << " passes)" << endl; timer.show ("get "); accessor.showCacheStatistics (cout); accessor.clearCaches(); } } casacore-3.7.1/tables/DataMan/test/tTiledDataStMan.cc000066400000000000000000000266261476623553700223670ustar00rootroot00000000000000//# tTiledDataStMan.cc: Test program for the TiledDataStMan class //# Copyright (C) 1994,1995,1996,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Test program for the TiledDataStMan class. // // This program tests the class TiledDataStMan and related classes. // The results are written to stdout. The script executing this program, // compares the results with the reference output file. void a(); void b(); void testOldTable() { // Being able to open this old table is sufficient. cout << endl << "Test with version 1 table" << endl; Table tab("tTiledDataStMan_tmp.ms"); cout << "tTiledDataStMan_tmp.ms contains " << tab.nrow() << " rows" << endl; } int main() { try { a (); b (); testOldTable(); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } void init (Matrix& array, Matrix& arrayb, Matrix& arrayc) { // The SGI compiler bug is the cause of this static_cast workaround. indgen (static_cast< Matrix &>(array)); uInt i=0; for (uInt k=0; k<20; k++) { for (uInt j=0; j<12; j++) { if (i%7 == 2) { arrayb(j,k) = False; }else{ arrayb(j,k) = True; } arrayc(j,k) = Complex (i+1.5, i+2.6); i++; } } } // First build a description. void a() { // Build the table description. TableDesc td ("", "1", TableDesc::Scratch); td.addColumn (ScalarColumnDesc ("Time")); td.addColumn (ScalarColumnDesc ("Baseline")); td.addColumn (ArrayColumnDesc ("Pol", IPosition(1,12), ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc ("Freq", 1, ColumnDesc::FixedShape)); td.addColumn (ScalarColumnDesc ("Id")); td.addColumn (ArrayColumnDesc ("Data", 2, ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc ("Weight", IPosition(2,12,20), ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc ("Flag", IPosition(2,12,20), ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc("OData", IPosition(2,12,20), ColumnDesc::FixedShape)); td.defineHypercolumn ("TSMExample", 4, stringToVector ("Data,Weight,Flag"), stringToVector ("Pol,Freq,Baseline,Time"), stringToVector ("Id")); td.defineHypercolumn ("TSMExample2", 4, stringToVector ("OData")); // Now create a new table from the description. SetupNewTable newtab("tTiledDataStMan_tmp.data", td, Table::New); // Create a storage manager for it. TiledDataStMan sm0 ("TSMExample"); TiledDataStMan sm2 ("TSMExample2"); newtab.setShapeColumn ("Freq", IPosition(1,20)); newtab.setShapeColumn ("Data", IPosition(2,12,20)); newtab.bindAll (sm0); newtab.bindColumn ("OData", sm2); Table table(newtab, 42*30); TiledDataStManAccessor accessor(table, "TSMExample"); TiledDataStManAccessor accessor2(table, "TSMExample2"); Record values; Vector timeValues(42); Vector baselineValues(30); Vector freqValues(20); Vector polValues(12); indgen (timeValues); indgen (baselineValues, float(100)); indgen (freqValues, float(200)); indgen (polValues, float(300)); values.define ("Time", timeValues); values.define ("Baseline", baselineValues); values.define ("Freq", freqValues); values.define ("Pol", polValues); values.define ("Id", float(5)); accessor.addHypercube (IPosition(4,12,20,30,42), IPosition(4,4,5,6,7), values); accessor2.addHypercube (IPosition(4,12,20,30,42), IPosition(4,4,5,30,42), Record()); ScalarColumn time (table, "Time"); ScalarColumn baseline (table, "Baseline"); ArrayColumn freq (table, "Freq"); ArrayColumn pol (table, "Pol"); ScalarColumn id (table, "Id"); ArrayColumn data (table, "Data"); ArrayColumn weight (table, "Weight"); ArrayColumn flag (table, "Flag"); ArrayColumn odata (table, "OData"); Matrix array(IPosition(2,12,20)); Matrix result(IPosition(2,12,20)); Matrix arrayb(IPosition(2,12,20)); Matrix resultb(IPosition(2,12,20)); Matrix arrayc(IPosition(2,12,20)); Matrix resultc(IPosition(2,12,20)); init (array, arrayb, arrayc); uInt i; for (i=0; i<30*42; i++) { data.put (i, array); weight.put (i, array+float(100)); flag.put (i, arrayb); odata.put (i, arrayc); array += float(200); arrayc += Complex(200, 300); } init (array, arrayb, arrayc); for (i=0; i ("Data1")); td1.defineHypercolumn ("TSMExample1", 2, stringToVector ("Data1")); // Create a storage manager for it. TiledDataStMan sm1 ("TSMExample1"); table.addColumn (td1, sm1); TiledDataStManAccessor accessor1(table, "TSMExample1"); accessor1.addHypercube (IPosition(2,30,42), IPosition(2,9,11), Record()); ScalarColumn data1 (table, "Data1"); for (i=0; i<30*42; i++) { data1.put (i, i); } accessor.showCacheStatistics (cout); accessor2.showCacheStatistics (cout); } void b() { Table table("tTiledDataStMan_tmp.data"); ScalarColumn time (table, "Time"); ScalarColumn baseline (table, "Baseline"); ArrayColumn freq (table, "Freq"); ArrayColumn pol (table, "Pol"); ScalarColumn id (table, "Id"); ArrayColumn data (table, "Data"); ArrayColumn weight (table, "Weight"); ArrayColumn odata (table, "OData"); ArrayColumn flag (table, "Flag"); ScalarColumn data1 (table, "Data1"); Matrix array(IPosition(2,12,20)); Matrix result; Matrix arrayb(IPosition(2,12,20)); Matrix resultb; Matrix arrayc(IPosition(2,12,20)); Matrix resultc; Vector timeValues(42); Vector baselineValues(30); Vector freqValues(20); Vector freqResult; Vector polValues(12); Vector polResult; Array polArray(IPosition(2,12,1)); Array freqArray(IPosition(2,1,20));; float fvalue; String svalue; init (array, arrayb, arrayc); indgen (timeValues); indgen (baselineValues, float(100)); indgen (freqValues, float(200)); indgen (polValues, float(300)); uInt i,j,i0,i1; i = 0; for (i0=0; i0<42; i0++) { for (i1=0; i1<30; i1++) { data.get (i, result); if (! allEQ (array, result)) { cout << "mismatch in data row " << i << endl; } weight.get (i, result); if (! allEQ (array + float(100), result)) { cout << "mismatch in weight row " << i << endl; } odata.get (i, resultc); if (! allEQ (arrayc, resultc)) { cout << "mismatch in odata row " << i << endl; } flag.get (i, resultb); if (! allEQ (arrayb, resultb)) { cout << "mismatch in flag row " << i << endl; } pol.get (i, polResult); if (! allEQ (polValues, polResult)) { cout << "mismatch in pol row " << i << endl; } freq.get (i, freqResult); if (! allEQ (freqValues, freqResult)) { cout << "mismatch in freq row " << i << endl; } baseline.get (i, fvalue); if (fvalue != baselineValues(i1)) { cout << "mismatch in baseline row " << i << endl; } time.get (i, fvalue); if (fvalue != timeValues(i0)) { cout << "mismatch in time row " << i << endl; } id.get (i, fvalue); if (fvalue != 5) { cout << "mismatch in id row " << i << endl; } if (data1(i) != i) { cout << "mismatch in data1 row " << i << endl; } for (j=0; j<20; j++) { data.getSlice (i, Slicer(Slice(0,12),Slice(j,1)), polArray); if (! allEQ (polArray, array(Slice(0,12), Slice(j,1)))) { cout << "mismatch in pol-slice " << j << " row "< odatacol = odata.getColumn(); for (j=0; j<20/5; j++) { for (i=0; i<12/4; i++) { if (! allEQ (odata.getColumn (Slicer(IPosition(2,i*4,j*5), IPosition(2,4,5))), odatacol (IPosition(3,i*4,j*5,0), IPosition(3,i*4+3,j*5+4,30*42-1)))) { cout << "mismatch in odata-tile " << i << "," << j << endl; } } } ROTiledStManAccessor accessor (table, "TSMExample"); accessor.showCacheStatistics (cout); ROTiledStManAccessor accessor2 (table, "TSMExample2"); accessor2.showCacheStatistics (cout); } casacore-3.7.1/tables/DataMan/test/tTiledDataStMan.in_tgz000066400000000000000000063071431476623553700232760ustar00rootroot00000000000000‹{Òbì½ XMo»?¾’!„BØI„Êna5'•TR‰æT’4ÐÜV"¥Šv%BR†Œe˼‘dÊ–¢dŠÐÿ¾ó}Ïûý½çÝë=ç\ßëý_ç:Ï}]ûZZkíϳžûsOϰ6ç`[?OC×`W›` WçàU*«‚¨¿P¸\®¶¦&§ç¨õûÈUÓø}DQWÓä¨rµ4Ô¸Úêšê®ª–ªªÅáþ•!JB‚‚]áQ<|<<ýEß·yy±àüî ç?Žÿ[¤ÏÈ~T/вpuç,´áØsþþžÔš\®Žêl-5®ºŽ–§ÎlcŸ@O¯Õ¡³U Ôµ ”ÕŒtÔ•54µ¸Êzšê:Êz†Æj:::jVþÏÈ?÷þiÿÊÿÑ_þ_ÿ×ÔÖT¥8šéSˆÿãþ/‚ç`W7ðH/Õ¿  Ї–†Æ'þkkr5Iüÿ·‰ÿÿ§E„ÿÿ…Þÿ/ü_ƒ«úŸã¿¶š–:ñÿ‡TƒÃq|l‚]ý=\=z,A¼ç2¢zS¿¥Dì÷¦ÿBývg"ÿ¥ûA;„¿%àccc1ßßÃ3ô?X•úmŠ¿ÿIÙø¬‚h½0ÐÃ3ÐÓÃÂ5àÏìãõðé}¿Õî+ÿvñŸž$B„"Dˆ!B„"Dˆ!B„"Dˆ!B„"Dˆ!B„"Dˆ!B„"Dˆ!B„"Dˆ!B„"DˆüMðÝÿDê?¿ûßóÿÍwÿå¨xÍAº‡ÀgEMq{ý­E"Dˆ!B„"Dˆ!B„"Dˆ!B„"Dˆ!B„"Dˆ!B„"Dˆ!B„"Dˆ!B„"Dˆ!B„È"öO>½þá#þ§Oï?>}&Á$˜“`L‚I0 &Á$˜“`L‚I0 &Á$˜“`L‚I0 &Á$˜“`L‚I0 &Á$˜“`L‚I0 &Á$˜“`L‚I0 &Á$˜“`L‚I0 &Á$˜“`L‚ù?ÂûÓ}»öÏÎýãßþ÷ߎ}&Á$˜“`L‚I0 &Á$˜“`L‚I0 &Á$˜“`L‚I0 &Á$˜“`L‚I0 &Á$˜“`L‚I0 &Á$˜“`L‚I0 &Á$˜“`L‚I0 &Á$˜“`L‚ù߯üg"öO>½þá#þ§Oï?áL‚I0 &Á$˜“`L‚I0 &Á$˜“`L‚I0 &Á$˜“`L‚I0 &Á$˜“`L‚I0 &Á$˜“`L‚I0 &Á$˜“`L‚I0 &Á$˜“`L‚I0 &Á$˜ó¿)ö§ûþvíŸûÇ¿ÿüï¿ûL‚I0 &Á$˜“`L‚I0 &Á$˜“`L‚I0 &Á$˜“`L‚I0 &Á$˜“`L‚I0 &Á$˜“`L‚I0 &Á$˜“`L‚I0 &Á$˜“`L‚I0 &Á$˜ó„ù"öO>½þá#þ§Oï?ኂI0 &Á$˜“`L‚ùïÇ$B„"Dˆ!B„"Dˆ!B„"Dˆ!B„"Dˆ!B„"Dˆ!B„"Dˆ!B„"ÿ»äËïL‚I0 &Á$˜“`LјDˆ!B„"Dˆ!B„"Dˆ!B„"Dˆ!B„"Dˆ!B„"Dˆ!B„"Dˆ!Bä¿Ûúøyzº»Ú[¸ú;¯ PY4CÅ9ØÕÍÏSÅKë/hƒËåjihp𨭥Ùsäªýþ»GÔÕ49ªp‹W[]SCÃUÕÖTU£8Ü¿ í)!AÁ®ð(>žžþ¢ïƒÛ¼¼Xp~w…óÇÿ-Ògd?|¹ÐÂճІcÏùCz^8ì5øQ¿_@¼÷_ƒÔ³µµþãŸøløÌù‡[Äþ~~„ûêU*®`mkB\]ýƒ}ü=©53¸\ÕÙZj\u-OÙÆ>ž^«Cg«¨k(«é¨+khjq•õ4Õu” ô 4ŒÕtttÔ4þ­üŸþÿzÿ¿öm-­ôu®ñÿ‡Tƒ€;vÀ?ÿ¿¦@õx©X<ô\ùÛYŒT_Ššü·?¨ßïñòs]aàh°Ú/dÕߔٻ綿ÿgæ½înøSÔe³öt_èñÇ>¶øûŒ¡gû Ê0›ù&zΦVFÖ‹õœçRíy¨¡ž­È‹£m ¬õl LÙncl®gâl gkd²ÐÚAÔ]b:ö¼–-öÇãM…OÿùV«ƒ|‚}Vÿ¡¿?zOQƒþ¤4Ö{ý¾‘ñ÷¦z¾„ÿ¡ï·Ú}åŸ4Hý.ü!ÿ*ÿÿ>ø?Éÿ\ âÿÿ!ùÿÿ´°çÿ¿ÆÙü_U<^Sí?çRÿÿ[ó¿&¥à3ßß=Ðs•§°«_1ô$<¾¦XOjúsÞèùÿãa‰üårçÅñF&–þ—'Û³ÒÖàƒÞßÏÎwÒñwe(&¯M^7Œ¦˜YçÂÞÓ­>¯>ÎÿÊPL¡)oó=Ôf¸^çÛܹž¡Ú©/5Ôšâ ¾•’AS.ªæY+70w™ÞÁÄ4ÕòÊ$"*›¦¿*+÷¤3”¢{xgÃAš2©ä^]òOöáÍÌ6žëÍP.ÜŸÄõ4%µ&Ýùf!‹¾›Í³1¯$d¦ò|49ê ðìò)bÒ&à­=ôˆ›xæ¤të¬È#Ì ¼êËP]¹šÀsö}‹Í+sXÚe*ó¼§âí5;†¢Cl‰ž¥rŽMÚ <ó>îòÖ[ ý–˜V <—/™~°yŽ®™ú–J¸é?xþ¡•·Ç(î÷6‹?úV¶Ô}<;­ö̰€ö9ß™I€>Î <—¢TFSªÁWÓXx¦ô¶¼¨týYV­8 <[,ДŒ{‘~4Oúf¦e_kOü—5"ÏÝË{‡Àý‚Q9‘À³Ló—pÝåR •†’~¢0çð¬ýDá ÏTÿvÉKî`Gý‚fŒÜˆqg¿xæ=ù\  üs¬¯ÎI¦)‰MùQ—gzÎývmàgËf-÷dÀ ]:5l ¾õ(ÛÂù EMÿ¹rðL]˜¿vž7×j-MŽf¨£5÷ûÐÔõéæï±ðÌ$‘r€¸&ñFýYðì÷1¶û.Ïôãß<‹*P;s±‘Ù†ýº²;yžSn¹ÙxÒüäð\þ«¤㶕ߘ oûÆÆ˜2àyáÓågfŠeÊDˆoІ¤¬ }F=Ìw2ò¼Õ­ÞôñC!¨„ú³øª>1Àgâwn3àZÖ ÿ«ÿÄà|ðgùM’« ^÷ŸÝ5xæÝ¶ß6üFPç=xöûëyˆ…gÞ÷µã @Ï‚~òôÐ37{ìäRV[ÜÃsxÉK#3†¢ÚöW?Áü<1P½ãvVKóJð+—þG_Ï.•Úe­À3%›üdøyûãy{gÁ*57Gà™Ê>÷@ù‘µm íó.ï™ì™ƒñé¡Äyζ û³çÈ}†ÇKØxvLÿ`j zÝZxï;ðL™ýTTæýlrßvÇ•ËÈž9/x<óÂF¹Ìƒ|N¹îÏ‚9WtÏT±LÂ;°{Zìà xžäYŸÖÁÆsjý-ˆk¼òMzžÀ3­ <²6îÏÙqSò‡[µxæ×È‚<§Y°ø©M}¦Û¸Í§+“”ÙüYXô,kàÿòiï <ó.ÊÛÏôä)v{ÁoJŠbZ gÅv’_ÙxŒ.Ï»æõë˜õôº¥É¤•ÕŸ§üæÙ¾wE-à3à'}xæ Ñ\݆<Û{^f†¼õÏz < ¡ˆú;_ú°:‘tuðÜî¡›¶y?lÏ ð+A›î\h_ª}áð2lÿQÃ"Œ{Ùº7S€g—‚È6ží/¯<:p<¿|žùûÛOEÏTªßhcˆ;:NæáSgE¹ÕGÇo…ó·gzØB=\ÞZ <›–Æj«ÁuÊjoQÚ ˆÛod'ÛÏÕ§R4ö²´Ûñù‡*ø›Ðfð¯eÀ3õÎsà¯X86WÏñ‡x벡¾)x¦Å“‡* ½Ç«Œ<~ÞnÞR< KœŽ{°ñ\+/þA‘¡¨óœí]·òdò0nmR~q¤óôïyÀ3½´¯ÙNœì¨AUŽP·9ov~ zæ:^hí`¹Ÿ1ÿͳÀbÔ!)¨/¼ó¢¥Â¡âwGç„ú_çå¡~}ïç;9ì/?òXöϾ\ÿôï¸âù#Ào𩸠y¦ò-†©ç†ï‹ƒö6ÞVõFž9OöëÓTÝH³÷0n_ýéÏ´óËú‘`?'ÇOzfržœŽ<מüæ8âÍ\X<&PæÚ1nËd ¹¡LSŸöømîž9ã쌺П½>'§Ó4u~óÚïÛ€g«!£>³ù³•ô §c!ÿÖ¸:mž¹ÃÜ®BûTèŠ<׉ðÜÛO½Ï.«&— ÁŸ©ÖnA³iÊøQÙê`?2y§úrÙx¦C–ÔŠa>sWž þÌ4[qç–Aùÿ|2eVÏ2ãýgñgŠž®¯|ùñVNÎ^ÄÜ*ÛXý9ð7Ï­^ƒ€«èùSÏÂ×ó»#ϱÏΩ›ÀùÛ‡ž9zQï‘ç¤:«Ï64ebìqxÖñ{»Ø y.¥\b|N˜:?xÌ^ï±Ûw^ÒVåES±Óevßž™™7g°ò,ãôóã,ð×akGƒž]²ËÏMCžù!\KøþˆO¬gæƒLVòܵàÚ"°O¦¾úðl®¬ø;ðÌ[Ÿ½Ðêµ®÷Ë·Ï~x>±ñœ^ó´ìÂ~ô“·IÀ³U–Ã+Ð>uÑ"[ÖÎû{<ó´âß<Ÿý«ñW» Œ§´[[ûÏ¡¯UBg°ñÌÈ4e*ÀuÛS´‘çȲ`{äYýaòFð—öOÜ€gÓ~ýYy›UqôÌÕî×=x.×Ûõž5?gýæ9=Wð úÁõ¿}ô<ÄmºÅd²y¾±îÚê¥ÐŸâËQW0?Ûû<„q{̇‰…0¨[¯+ xædf´&"Ïù¯æß÷…çmãQÄüÜ‘g›‡ùyöñÌÇu&‡ÏН2B6žûÚ^~±Æ©ï—É·@ÜæÕ&fû ÏšWËž»‚&ž8x–Ê> ãö)枌'M™§ÿèsxè°`ÚAm‘“R(M™ ØÒFÏœÍbg&±Åí¾áu`¿ŠË«9–À³ðî¹oí±pÿš%µsA?³r[6a~Þ"¸2íÝôAÑŒ`ÐsÊÑ——Á~¤¸í KØxV<ª½o&¸‚þ4ºSI-nÓU¿y~b7ú'õxÆéãX‡­åþÊBž÷…5_?ì\pqÅä¹oÚ¾›Ø¿ek_î ‚q³B*ðª>çÔ\—ùk!®.Òx?ë@AÉ[´³†³"@o'úŸóž%×2[’Øxá+…ƒ^Û—)% ±Ël³íƒuØ‚ ¿ŽPüaÈcþŠ]ÁŸ™ûÃâ×€?9zwžy‡e[Ÿá¸*r¡Éè 4¥5Ø)VóóŠï[ØüyhPK(ø§ÔÆ•†ð¼¼à%ÛÇÂý!Uo€QKvGÏÔŽsÊçÀŸ™U§Å®òhêÔsà;5bã9mêm#øžÁºÎ‡ÈsfɪYèÏ6sR—€ÞµÏPSžKT’#êÙüYNfÄAÐó§s ûTž[Æ6(ßbõç¦ß<x ä³úw0ïË W )\„<7Ï8ûì¦6ÒöEU Γ¥d†ã¸J)fõð“H_é'ëg:’‘ïÌ€óœ Q®à·¡^+ÇAûÂðsŸ³á¸!s?‰¦úÞJà< ª7¯Ða›'|AÑÀëо¾Ïgî ¤"W7DBž˜·*8ð,ܯ{ºãv¥îõð¼Fg•!Ïûæ(`Ü^Øåwøð˜yú­ús½‰z ÛàÏüU²7ƒ}[Ù„Ý­€¸SÞuv¸´oX5±ø¸£/ï!<7«SXÀæÏéeŠÝ!NéĬÇúÿUÄÉ(¯;>VÿâÚv¬Ã'J•£?oÒ¼%uEóBËmŽÀ37ó©Cp]8ë›7æSÙsÉîí‡ú&ÎîÛ.6šYþ%alü%žÿÆ"·¸XÀŸS«?ô`ÿÃ=8à vo?‹á<ðZ;…ÕŸ•{xæ™x½‡¼'ýM§ü”6u?¬<3ú§*üà(2z•ëkÜcóÑŸuŸí_ÏÕ P§x ðÛçI¸‰óž[†mþöMÝTjÏ œƒ—.?ÓçŽÞR†ë‘Zµs₾ŽÍ‘`ãy¦êÏ7 oalžÕà™±üuÜx¦ïò'v‚»<ÍØŠ<®Šx‡ãç“ëÛ¢¡ß ›Þ,ê{ µ”ΉÀyÏ%­½Ú·Á÷†ïûâ<[™/™âÎæÏ«¤Lû¬ÜÙ©Í9À³Ëѯ˭¡Ÿ¼z_—ÈÃÜ-:698ï2µ^øå¸n1.†ú4¡cýÄ!8»Dp¬¥_1‰FÚâºAñ©Ÿ'™Þ5«€gÆ(áÓóM€+¿|_ ðÌLS_£Ï6ïùÙn¤+<_箹·AÏŠf;Ž™±ú³Ío.ì*í€x%uA©à*ðÌ‹³ž½×1¢Ö”öÿÖ±—›X€ã‰“v¾ |­ñÛ¡’ý%Þ[_`z›ò¬ÿñ(Ú¹é×k™}¡ýå§«¦Ïí‚îµgáX*%›j <— ¬)YɶŽaÿê€,äáò/‰·gÏt›cÎáHЫeû;à³9lã<¶'‹ëŽGûÇA=}™÷þ.ðÜ0äU™òìä"'›…~™5xÖ9í$w¿˜Eí­Ë&@^ù9ãÐ:èÝ9áôSÌ!U?×}f®è ¢dzû³T@|×PX¤2xÎÞøú‹" ÏÔ<êXøÂ×{C±Þ¾)˜ìzjúê:jÔˆy—½Ðž†.ãÃ]‰¯*(\—Ô{Ù:–…gεqY8?9éÈ÷Ý gî.qV¾ð›çIµ×Ô!N¶—¼¸è <Ón¹œK`×.¶[3¡ŸŠ¦¹›Û üŸªõ|蟋pæŒÐN¤ùˆ#_'qE3ðLßí6žWJ­¢c<§”…ìa๗!NrŽn+¹ýxíÇÉÈsÛóÖ®XЫ÷—3IÀ³#!xŽý»5ýÂ2È‹-wÆf28Ÿ\së Î3jÕÏýš>ªÑÁ8ýày²Ø»àjbM!ðÛ²@Òx–¼=}Ø>¶º÷ÉôûÒàWÜ”ƒ;û€~ùov”òñþïßç*ÆÏuB?Ý /!ŸpVê¿ÇõBÚ_~êLàYG£ïíB¸žmfY{ü£¡"lƒ;ðìô댺!ÏË_©Å€=¥ÍÑ[ýãFâ—›Ø~di¬:èÁÊ2ç¨ðÌùX½à3Äõö·¥ÀŸÌ-s\‰‚©ãÛù,èFø’§=`ŽðLqz%©¡?½ÿèZíó÷—ø¿Ì¡¯s^_ËÊjÜx„ëX¾ë—8ÁõöùSÏ‚Ù_ 5ë„øQÛ7Òà#Û<ÆA£éC >´ùïž… ‰6À3í•mª —˜µEtÂxƒw"*m'ðÌi+¼WùñSêÃX à¹áìÈRž©yõ×Ÿ˜ãJäùŽëhEàYw{€'è‰[òSMÇÃO Õf°ÕÏgì*@?Óëj¤sqü¹vF"Ï´Æožk.I¯œÀÏq/qÜ\›Ó•3×YçB˜ýÒ§•P¿Ù—ÖLŸY{ü0ì©|VÓ×­ˆÿ4t²,ÎKÓÁSÁ.¥¼ ýwÃs–O+P‰Ÿ_ï ù+²ÃôêkÈ“Þãµ.¶³äI†‰~*ñÓå¹zG ΃yxUƒëhJÇ{iC ØØl'<2&Ýꀿ­ô›õwC|Iè_|cô?íÄ¥ÃázgqÀœqÀ«ŽáÕoJÀ³ý‹óù,<ó²ÚÌÁ®øWi|…v” Fž'õÏ<öî"Ç( < Ïl+ÒG½äЉw@œ½^—»pMO\\áÅÆ³›‘¤1Œk¨¥b•Ûq¾sÙ¹[ð·ðXü·ñÙ8Oq(OìF°ì£ë%–|ËïÿS;ôÚݦ0ç/†1QÓYýÙ¡‡gJÚ¶²â4gêöxià™NÿpëÖAS+ŸÅB>ÎVí{Uúç2ûå8{¬C_¿gútJ^—ÚkÿVR¶xgòÔG÷Ãõß/F{Ëà9ºcû¼NÇu­ ú ç—‡6€øÙP¤{HŽm|3OfÎ‡Ï ¼çGš®¬ƒquåˆðø­ô…aÑÁ8?2Õ!äÄ&k×Û±Ð~‚Ò³xœïܱqh Ž·<>\?‹^ÿ³ã‡Õš¹š/ YxÞ7űæã×-ÁzÀ3µæ~ô{œW—ª°Âu ‰3ñ¦ãxã„waÀ;-8wËâ…®£Ê¨bĶqaV>K¿v(<ù˜¿ hn ΃) *Ï̵Õ󀯖y-AS!0‚CXò-Ofú×(¸\?ÚO×L¿Ø;f°´KÇüæÙ$üè¨{\ž9ÅÄâ¸Ùò¼± ðÌ¿ŸÒþ úU²pë‚8œn³ÆúÇ=3ìþÀõXpž+uÏÉR­]¸ž 0Ï?øÍxìœ8îÿË<+ßx¶Ÿòýæ)à÷Ô¼Š6C8¶§†­›Å’'iï"¥áÀo{Õ¥Qp<Õð<÷"Äñ€iŸ¦Z’Ãߦõ‚£pAçürˆ3ôvÎ9z;Σ<‘{Žuxßñ“"¡mô ü¦]Ê? v¥h\à?ªPt» ?³jL,ôÓä㈠8?sV²3žÃ%°ííW°÷„ší päQªÏ8°¦~Xà Þ^—G«Ìù•sv±ðcì~2øå‹enê GÞ›LïîpÜöìr Ö­ü2/8ºxŸ¨gñÏò©Çf|ý>:8n#¥6«_ Hgá™Ùý;nwÌsüXøhmM_à™ZäùZØwˆ+z<‘œÔ†uvÚ^û‡XŸTªó…ç0|¸áTà[ ó6ázÚx^Ñ:°/—\ÙO`oÂ+Z#‚pÞêÃÔŠ“¯+ÝJƒ_·ÄÍkÛË–'Ç6í ~”6¯rBø5sïÂàYÀp,¿¿ÙÆ–C qtlp®“ å>Èöƒçðnž«¿ç¿Þïý‰ãŽ„ ¦UbÐÞ,ñ໪Às˜Áé,õ0'ØÊôð,˜$fãv—I«.ÑZ^¼;—ØWü÷‰_¾=ô È}™=ãõì¨[•€ËŸ7ý°Û:“DÜZ/œç“¨¾†ã)ó[~»Ýpw²Îk=§wÃý³SšìTYxæ^º[Ýz–œ¸Ï£ôÜ>ÇôÊãí,ú¤¯öðÌóîovÇ/>¾2€øÍÛœX¯{@ÖeÕC÷³ÍK¿Q‹†úË塊n>®W¤g¸ÞœfF.Æu§›ª€?¡Wn[1KvÉ—>¡~Ÿ·Åßç¡Ü3û¹°ñÌ´ýö綆} ÿ¹èÍÚ=ê/*¼¯/gYÅd…:§½}Gâ-ÌËák©è_‚4ÿ4Îcš:µÕÜDÿñúºï3ø—Kžøµq8«¶¼g?·Å‘)p}’lÍà¥`¿Î„gƒ_™Þ¨ãP(ú¹h³ÅŸ+@Ï<~W€3®«œx}%âMöÐ$ßjg«ŠÂy©šÁšàg9ïÃd±®ºeþM°7ú¢Ìµ¸^ºC×ËkÖ9š­À®J|¦õa©“Ú˜§W1ï¦n‡uþ}1›G8Ö²é×F°w«¶Ü‡­x½EÞ«ü]ÖôUËLè@odö$Àµò|¼ÚŸe~ƒ™+ÑŽãæ].‹#°ëHêðÂu…¡_¹ýàû‚üDe7¬kÚÆæg‰Ã-¹‰Öå çΤÆów@ÏÜ5WÏ$ncá™aßóÑ÷tÔg¢S”äeJ·éåôUðý–ÞUÁo¤ìʧ–a½YâöÀç›oÞ¸ÑüÖªSÌìÞVÜç¼ü¡ÜÒã6Œ?òáçõqÞD,g¡4Ö½õežû+0ž¾÷c8ˆ°ø•Õ¥Mt*ÖC{×ûcÔlÏš@>覶Tâ:ÕÅ÷ÄAï¼³ÅÂKè_ÍWùz`×VbÆ—q~,(¬ø æí”!‹ŽCq’Ê ³*Äú¹~³%ZÍLkÄñ’æÞºN\71ÿ*fz)ï5XìØ Ýn°ô~Χߺtì°5ïÏâWæØœ8î•Ûq“e<Ä›ùäÜb?èݹ +!/SËáÎ.¸/£æñ#à¹Å,IbB®kÄåD±ø'OÇVL÷ßq†§Uà<äÊÑÇ×leá™™ÕÃ35D¾P ìÖEâæÎLŒ×¾ŽW¶ã<»ã…LЛ÷»oùw±ºŽ?Â1žõ^OuÏÍ“Ÿ?Åñ Ç;q1Îï{÷Þ´²Ú·/;æ‡ã¬Š½ à_滕Ò?–ÑTÙÀÄöð+úzÑ¡E,~ÅÜ©0R»¹£P(…ósý¤• ŸÐtç}?èWöü§/!^óo¨»oÂõú—¾ úA¯Ùœ«í|ï ×ýô[º€:ßL ›ŠûëȾä²äOæà\ß\wf²çÁúÏÞåp´ŸVfFwCÐív­OÖÃo®äƒ¿Ÿ¹Q&ù¿„3j(Î3„yæÉ2¢Å¹öKWáxÑ®l—ÜwÊòOËA?[·¯ƒç mçŠáø(À#Vc‹r ïœ1€º^RÚkú}´‡Wùï£ÒXx¦]zx^ͽð÷ÿ­Øæõâ5U£-· â‹ý#½†Áð}ï›¶J@ÿ(géûÐ?î¸&~9ħÇù3oø`^þ yZü«=†«ís:w Š1¾Nìÿ÷}×íRɬ9DS¿öÍÇuxaƧVc–ñ}ûØÍƒ±ÐŽÏÿIX7D„;C> Öu¨xn)¾Iã¾Zawu`Ääùsªg»,“Å8?¦zºÌ®‡*œÞ¹òEbœÀý…ÚyÃôK\-£·à:TÓža™PÿÑúä?Çy¹ùO\‡~4ÈÉJÀs1/Ž´„:Q±bÐ4ç_üžW)c¾Šž±Î•mþqdï$¸ïüıBœK|ötß2—¥Uµa~?:s¦)Î7ŒóO-añÏÇËGvKB/½§)‰u®„°)7•¥]&þwÜNæXíÁºÖðÇëQð¿˜Ò‡zAf£4/×w/lïêÂx=ï¸3ú'£‘cýâ÷f‚{cÝ;aìf ˆ“­ ¡÷U ýÚ)Ì)g ×L¨ßzŸe"¾À¡ê$™7:· ?‚ˆ&Ý,~euI¦AìŠï1µW+ŽóV{8§B>)‘ÚZ±ôð‘>8Ç?HœˆÃ}d-c‚±ÎŸ+q×úYŒ|‰ìu}䋆’ôàG`W‚š?ΰí£rꟄã :Ö¾÷½RÝчqŸoÔÉeåÐO©-­azX‡ïÞdþüÝ{Á{«¡Î+_8 ˜¸¥Ÿ&9°ÍW¼¯6?b¦‹‰7C¼¦2'z;:Χ™ƒ ¡?3‚µe1ßµÚv Yü3¡OJu蹤¿ôñn¬;ã‹‹ð½(‘Bèá™?°R°ãÔ®#ƒãzÙG»"ÜçàQºç•$tÇ™ìÁºhWZ ÷µ,©<ÙßjÔ>ñšúÕ2øÏã ¯NBû¦úõ±Bû(Î .½«ÌXðððü~xãÐ(\_mžðê;‹_œÿùå1ð'µO¸q9ÄkÊË}êjÌ'÷RÇYàúò+U ç£\­ƒƒc¿Þy-AÏRrn}÷á{Aý©üKp½%/fÖLȲG_DÃóºdÚFöeÉŸi¯ä «p}}rÜ39\Ÿ]¦w6 ì,[~œ³K¾ô<þ#Ög¥yÒ²àæJÌ=¨ó6+S{qÔ™®Š7,õ}–û©/ôƒ¹²~«±3ܧ»jñfGàùðñà Ÿq™ë›va¼¾f·!›Å?y;To I/::~?êY~ŸƒÉÑ÷SŒ ‡gŽRÒÒõ=û¯äÄù`;ÑÚ»=ñý·:Ï\/øü\2ç…~ìkĺDQáäáÎ=²™æàÇü¨áE¸?Òügð/|Ÿ€q2ë®WŸ)’·Ç}F®ó+`¼íZ8Vó÷€­»XôPñ|\,®I[kWa]¨|ïÆ™’eúÛûB\ ­´9ð矯ªg¼ûK›ÑoÏT°Ú×x!ü‘éqD\t’;©ù¢÷ë9{Äáz;·àõkö/H‚çåØ/ž0ÇqÝ‘†g!_4ØÈj}–ŸµÂõµÓèN(Ž+JT¥¡Î¸ûا®UÒäöÉ,õßdø‚-À3½+cB1ø1õu}os{è—2ouð\;(]0ãuÖà¹ïYx;zqÜË|Ðsí ÍŸSðy£«š7‹¾Ÿ¢¿üŽÛ9©ŸMpyvð;OœÞV³êÁv½k#eÀ¾Úì,ü öKÕ~vÆõ…÷|uˆƒÙIvîãq”ìÌ3úÀKÚáøù'±Žn ¶ü˜~šû¡7è]Mu?Õ<+• <ú¨2£ØòØñ›j’ñ¸^õü€!úqÔ.[ˆ3ås‹ à{'"¿½2Œ…ó‡ÂÓ+à¹Ë£÷f—óddV2ŽÿÆð»q^+S{Ñzqˆ#g¶»½Œù{áyŽ€Åß\.Iãû&ôÊ!“Œpý= rd?°3®<éˆë­Jf±õ8žÿ¢·> ìÀïÜûÏ€gn¹÷æcéøž®þzO¶º— ©íBžÅ߆S2# Åýî Ÿpµp|¿ÿ Ïǧ&&ëLEãÔºy|d!ÄS³ÒÛݸ¾´¡Y/~K»ÌX‡žy’·¯#žá~‚'…m8~/К×u‚̺÷ƒ0ŽLªßQ‹ã.kŸÑk _I}Ñkº õ°¸z ±˜;Ú8-Š2À}dœ¼zÞûþÊkÒ? ×}¦ÎIÌÁ}1†ºsXæ:ç}X‹q™·Pª÷ñŠç}üà íE˜Ä¾\-xtëþ°îâ˜oʉùãüÄôïq¾Î-Ån#\·tn¼j <'¢:¹¼Y¦ÑÍRǶÿ´qôëÒä¯RŽómncF@>­=2dóa\˸î\ñü‚å‘íhŒßƒPσFÊ ×Õ½=Çoé»üo‹0>ÝX{Iü˜â†dO²…x#6º.o7æùò 8~“Ñ-þÁÂ[ÞËæô@гÔZ±ŸÌSÊÃBãEßOÑz=<¤:CÞ¡&×7à¸nÑ›™P'„öKÐ= zá>0Ò< ~LMâ$—@¾”}óe*Ž÷¼Zö‡à¾¿Œr•O ×öï´Ô2hŸŠÉoš‹ógö–\ÐûŽ®7[“ç¼h)+^îw;¼"Œ%¾¨©ß{÷ámy¹=ë…¯‹…^ðýaî½#qÞ|Vô›_ÄüèC`†Ö/´›€gžæ—RÓX8&ì5°€ëGî\ÈÊž?˜­Ùë·Áœ•,þÆ[Öæ2÷S|Jä¨!’±ü?¬ó¾=ÿŽùkÒ·Ô‹àÇTräÝ'XÿÜßç‘8ÛkŽ/5ëÆ'‹Æçÿ’¸™½Çm'ªæƒS²ï%C­ÁŸdí”?ìÆùqω¸¾}øÿ o†?Ò‡Wƒžu¢×WÃñÁûù‡âDßO1^¿ý9h²Í=¬/æMR§Üà~¯×ÝíPïwš:mï³׳_8mÀºsÊrùðœwò¦òqýMQëuq ê54¾ý èõÌÎ?  }©óã³F ?¦K‰o†ú´nÅ ú.ðlÛxÓ|è›3ôØS,úæÚ+DqpùÀÞX/äÕ\¾íASó6¿½<§™ÓjãÐ'$•»‚ýeïJÞm u÷ðâDÜ@ &_΀<“Gy{xö°ó5ŠãùÏ~ÚÇYüÍþÙ;‡T\ÿz×ðDÇ—…–;>à:qÇwOÜ¿Rž²äöœÿïu¡I ô t@ö„<ŒÛÚÏM[‰ûÔxƒO_Še‰·Vý¤šÂ÷hçÒÍÚàÇÔE;MÕ«<›W ßßxôãp|oËkŠsß ¢qZU'Ä÷†|žý=ºWOžJv°m]/ú~ŠÞÒÃsèµ²I¨s×ݱ1¢©òGéòõïÌ@?V£#Æĺ7¯¾îôÿð³Å…||³µÈ÷o]¿ôº|·|Ç‹BhW`j²/Îwy¾3ûi½ýë¨#4Õ\÷9­ãæb¡ÆN} 3Jk6â>áKÍSp¾èu÷T7¨WmÛ¬†ûˆ/1ã›0~Æ3o@½kª$ŸÄqše˜çR\ǬÍÂuÌ{×]‡C]0‰9tÇ).‡wʰեWl_~ĺ¢û´}ÄÞ·]šrP¯H½¹ô…‡~L—9F~‰}?ÅTôð\²ûfýRä¹®xö’¥p,šõkÈhÿÕô'[pÿl?f Ô¼=U§¶Bÿc-ŸYâ¼C@¬K!üбëR~%½)%u2´Ÿö¦à†8ò?jvBoàßDc½¥ð¬›g2L çŸßçgZ'‰~®ò©›ŸWáûMƒü]Å1,æŸîy¤Üléáà9`к»Žø¾È-.â ·ºÓÇÔ'àû¡.Ž×U±ŽX]š@ÃõÃÎ{ïZÏQcæ|„9 ™ø…ÈvW*¯·A¾ñŒc=*Öç'´ÿcò=fÚ‰±¶ úùæ‹×ùn°ŸJ+ðÌû^í¡ƒã± íSüYü0í¢OG´“Œ15Ç-á>Î/úõ|š’#g‰ûé¯Ëï.ˆ½%LºñQ”„? ´ôÜbñÎÎõ|±w1?Bôý}¿‡çÎÃü¤-ÈsT蚘7æ·MµúàÖ·‘ø».§í•ûcž\Ë|nÿj®­Ü4êêòCzYcÞ®Üûa ÛÏå;ã¼æÄ…¶1P—2'M†|Öyç^ïíÀó *70ÿ´ ™P¹QôsI½µ.ÃñÑ7×Á_>êÚ ñÅ\gç•:äùKyñ~Üqݬµê´ÚMG¾…â¼µø‡¯÷Ñ¿}&íß×sθ­žeïç}Ãùzì3+â_µïEž.Õï@ûúâô>ßÿ–¨ˆënœÍFŸ1Žw9ÚuC½ê_­­ìG3îù€ ¸%ÿYO4~û& yö ¶ù¿ÔÈÎüÍFÀó“Oëeö@-5ÅzxÌPó}a¢q,½ö¹Œãí½îýÇFà<Î0™Îµ¢ï§˜=<Ë|Ö°?ŠÏ÷ÂUû>Æ“¹§z[€½ÙJ9ðçÕBÊ£l=qßZÀˆqn'gwlÓÂ÷úVæ¥za]úqh8 v~Ñl—~ ´Ï ±ŒlA}ñ^vô]”b¬vx¶ÏW™‚õ•æã§_XâRš¼j·òÜ·ûÂm¬K'o(¾üêÇ•oÃvÀó<ß÷ ãªûsƒàw?ÆLt8ïgf†”bÜI)kC»lŸ·÷ð|ÂÍ(»5÷…¦ ÆÂƒw½Û'ü=!꜔Á‡åp,пb ö¥±yó\?Ù÷6<Òça‹=a<"›udðfà™O:»ë›Ñ”&΋”P—¨*äyR²çC¸¯´³W=ˆÏvܶžyw/û·à|þšC±Á¢qbï¼—ûznWë…ïÍ,µrS di—žèØÃsüùw°ÿŒ£‹¶§õó36†¸½åCÞ\Шä€~u³We!ôûzõøŒÅÀ³ÐªÈ@õ­0Ͳ üèíœ;nÈóþòÈ9PR¿ºÝIÎvú¢‡<󷬌îÙg¥xÄô½¡®ôÊ7Ü?—& UÁ÷µßM‚ëó;¶&_žÛ_s:ïá¼nSÓ¡=ëD·ÛД5Fëå,odz×¢½´¸Öœ˜çbŠï¬• ™€ü?NÁýöçÏæzUÄ÷*>eã>Yéç·.‰Æzê[bò}ysà˜6§ïЙЯšÏÖ"Ï9Žç31Nùî,}¾J4ŽU¸…Ö¨Ï9®ë¢Vã¾Ä{Iç|EßO1&=<{/Ì~ùyöX}²…†c€£öi]8*íš <ó”ŸÏÄúðݱzð÷åj·NûâúÏ}}Cp>¾{wÎÇçùýÚt÷øŽûPûaŒn~qϪñ²ê/à™?c}œ¯–_»BzèçÊ–ØmƒõÌW»m‰&p,¬JýḴîí†û-ZrËCpžÁýæÍpàcCEyÞ\™?í±íÒ”Mpݽî£í³ ԇ͚Ÿ¨¿NÊ{«¿èvC×På†ÈóCáM¨¨IIŸMaürpy»3Öë{–èïÔ®—ã7AÌßS}·é ®IÍ•A\«­î[}Dã·ßsý‚<¿Ì³¢Gn£»*—¦ô«÷nÅý¯Ûì0à/TMeë.§·Uê#5à™þÜû9‹~.æë±…©ÈóÚ^E•šp¤¯ Tƒ<<ûÞ× \/xé°ÚŸ“ûñð¯ó"èºò»¬ªÁŽ ‹×Ãõv͉Éï€g†küª´gÝ ³ÅÖIt»‚˜Oùø~DCgÃ1-óÖPmÈ7¯³'á¾eÊäñÊŸg©áÞ´¹€ÿÎÎø+ðL]¼¦ÐŠõëåçn'–°ô«8ÜZ¼gÜ›Z,GÞ×®qwn8Œ/@ž?Ý›ù‰šøëýÇE¢q Ã¥qŸÿdõ;Ôóþ·:ÒV¢ï§˜m=< Š8Fžé¦[꽸Àÿúî×YÊjTr!òÜpétÄ8OKfÐÊûSåXÎoz4–Ïžˆ÷7;vg¨ì){ö}Ý’; ÎsN‹•c(Çõî¼!À3e½¯ì¥:œÏÖjá2¢õè¿i;ò°oï ¸gª2 C…*&œ EžK®…-‹ßÿñxÞ(†2ußî7 yø•=˜ƒÏ¹¥Ê® %ýëÚgjÀA ¾*œçFLù¨,º]Îå‚wËgá»_a»‡’BúθÇ%ȳ¢âòÓÃñûÞ{.H1”ä:ªæò|buÌÄ•z[®(Ÿ~«TÛy^þê8 gôbåÛÏRúd/(FžéàÒ­ øœ?ul&ŠÆ‘õÞØó¾Áî¥Uyl÷X­%Gôý}¢‡ç4‡¬øväYæóþƒÈƒÐJÿð Ò±.ò|lóÇ8ï¸dÈ'†jý¹çL0úó…ý¡Z¶p~bR4þþXÝFêMò<±Þð£œ?ò.Ɔ¡J$§=ì<ÓAëû _ÁP¼#?_ípý\ü4 ÁäÙz¼®Ùl¸¯nÙãÚ %µ0Vk òÜõpl¦%ž÷èÇ|àÁEs«*òüXæa?k8ÿëüÉ"¸¾VnÝ•·¸ÿv¢ÕNh7ëÈÓ8WÑíòÏÃõkêεŸí4âG=º¢ËPiÙƒ§®DžS'fúÃyéôkA.ÿ„<÷Í—Ð@ÜS kg/O›\÷Cž?9º¾D>Kl×åŸc~íy/[Þ~[›œ7 XÔßN4Nä6›«sÑŸ/*]V[÷•Ç7³}?Å<ùÍóîùNÍÈ37XÍVî¿òfÂ<8z¿QÀßé¢Â^z?_ óžd€œ,í÷à9(ÁÝ ¸nUmÝë)Æí Ë¿Ìb(¦¹€ï ºÝöQ…4òÜ{Wñî…pß©–¶'f pî–É2ä9>öt;Úõ…òŒ°ë‹ç%p?ï@PáXĽÀ_å¸R4¾ð^샘Ÿ%o†iÀ}ÙÖºAœ©>PØó; õ1c-½A?é{>zˆÆ‘¼ýЃ‹ ýÓ?l¾/è»I°‚žÕ^ýn¡>yÕ~ÄýÞ)Ö‰ãªÚy͉¨§kj»®ÀõÚMI7¯bÜv0èVˆžWÚGÝ_'º]ÚbrìäYåð–aè'½‡?J?áçÌ^j‚uØRe“Äåp^X2ªüÎÔùÄüªƒø~éûñø;˜ÔÜ©o‚Xô}½À»y®l}Ý8î XÒa>“¡:¯©ø„àï^Ðç#ª×Àùïµßû‹Æ Ý6&o4òüÚxÌ9Ô³d´Ó _–vÅž­è7q•ÈóôÊ 9`§Tá´ñû- ^þàö Ç–½ú¢ÝLèÍ9z¼swàLàÙªý¢ùxðKžBrn"<—Lð»q[gþÓeGÒƒŠî·‡çæEHÇ\žÃ³‹ø;³!9gxëE?—@뛞.Îk\ÙþJ½ïàæ‚ 6¾7ß3¹VÖ?í+ª—4ìK¢öÅ·Ëø{(¶+UЯ,3NÃõµêYee¸ÞteòÖX8Ÿ¹JÓ<†¥Ý;+}>àøùêZ•Uȧ䰯»O¾ÒëEÔƒÁ*yô³ûc9ß nèŒ.7OA>êÞpy¹> ,ö+”¼‘Æàø9¼FãÄ}ÊpÁ¶6LZܱý¹×S_°Þ¿égXì¥%v¶ðè¹|˜|ÓGÐ3Õ6ñAÉÑ÷S´eÏå;ij·!Ï3 Ǽຠüˆ^XzçÃö{äŒö²ßÚd˜ðyLvˆ4ΓˆIŸ^ Ï•TÓÝžËøöè1+П•rï„Áy©Ãó N’w“Éž;eT´¥áüðã·âYô-®ç0ÇÏ÷ÅB3·Ùz×3ùeç—ùÀùšîF°ûê AŸ,џ˼èPÄõi²€'?àYË’läÙNnöIS¸Ï{Pn¹!èmÛ^¹‰8ƵúîýÊÝc*Gfíþ‘·pž¤Oë‹pÌ›†\ñµ¢ï§˜ ž%¶S;ÜçÎI«[áþ¦½¶NÀw¨ÿV9ü}ã´)Wf ½ñÔ7D¾<ö6áï¥ ZjUçkÄVö;<õÁñ³ ΓDúŽ~£ÿqðTxŽSÖÃýÜПGœ®Iÿ˜yõ¾w¢èçj™?q_ÍFðÛ»ÝScÞ‰wÙ3 ô½%Ʋs;ðœvk«Æ´ã[÷ßí9Çv*á< =½‘ýðæ_ù¶®;Ðweì€gÅuI}·Ïs4-vnÝ.÷X¸‰ ò|]^ÏëÅ¥1öK€ïÁ“®Ôâüv™]ú-ˆ[<+7þà[çý2“ï¥p>6I¾p%ŸÎS,v”擗ᆭíY¯*híÛyxǧ´8à[?Òh®WÚ¶}Æ‘šÓ𸻏V¿âz•ioçÅáШ2~!ð½ûÅ›|OM¸!QüÆÉN¿çϸË/xž[»¸½Üú>½óŒuªèç ¸`q׫8ƒÍö‚ûÂõVùß¡“Þ&ªãï {½þ|Òæf–Ÿœ)^OOíý=s0 @¸=£Û®OÙ{Cx~¬™·æ%þ·bŒY¯-¢ÛåÅ'Ø~Æ}á‡íTÊ îóÆôm‡ö½ÎþZï;ÆõÔSÇo]|„uMK.Å÷YÍ¿Ï\þ•+Xìˆ? uÑ8|µ·Óšw˜§}šZ ùQÊþÁ½Tü=„®JkŒs±•°äµ3õ¹ÓÍçÎð$F슉¾™‘Ï’(¦º‡ç€‡Y>'b îEf ·‰~®ÚvÏ%q½êž¢×^Œ“»Z/…8ér¶Ýê<¾76d6}ê*^ˆªó7ü=cµ# 5çö—ÕŠ?Ðîf©ï5ëçêøjËiJãþ“O¼íÀËN]ºi¢ÛíÜX½×%M-Æ|ÀúÆb­wįÖKýUpÿ”›Ï®I‡˜·º?ðw×¥>®ì;x=##a¸.Ãß{MLÏÜèßu ן»•cÝ>¢g&ð}gþ•‘3€gáä]WrÀy‚-‘D㌑òÎú z­ùÁôÙzØyË{q, ÏtsÏ.+bŠåqþlÒv¬cɆ,„z+€ ØŸ‹ûIÎ5g„¢Ýèÿ1÷*ΈûwKŸ]UÇßmöl©C>k%÷Ï‚ö]Z6ÝŽÏkWvOž÷æÚ™ÌJàù¢f^¾U6ðüêËÏàtÑÏŽ![ý¾8,/ò/Õm3bÔÑLÇKà¹%²´[øämŸ=²Ä‘ÎÊ“¾¥¸Ÿä„uß6ì¯òÍöG`wö¼gñaÀsÝN±YÕ`_›! XìËJæhŸ¯¸o(kaÅ ¬kåG}^ý”ì{€ï Ϋފu’ò$¯:ˆoɳl>@S:F1“ñÿ ¦.rØ–"_(5;7÷“ü‹Ov‚]B„V vÙÞ^þפ_ìOç=_ÙØ‘%ú¹øÓlã_o€vÂÔò°þ'=j*øu‰š`Vð\[y/·ýÖâ˜ôNðÛö´²°ž}¤ ¼¡Ý_xÞë½ðzÉí·“ï‚vshäÀó9$q}3D·[>˼ß‹äd¨öÁ¸EϾ9üšÑûø\Ð7hc»+þnûùP=-/×d^Ú±òäÄÙSe·¾dЋ­¢ñ9aa]BÜïjsðÖ»RZÏ*¡Þµ=·yòñä–0çkÏÔ÷a‰ÿÜÍÒ¾]ø{’ꎛ .še|ÇÿB¤0¶= h˜?, ÷¿5*|lŒ†þ…åž~ öì}¹êqÏ{œ>þóv‚=q.‡Ô-ØÇó&³q\½`÷x]ˆ_œ!:Ö¶`—ŽƒÅº}p_ï±›w/ §eäˆ~.Á0¯µ¸Ox‰£Æ'ŒŸÇ»¤Á¯í¢~~ËÿÜ#!œ‚~+–y¬ Ç1Ò ûàï0•oš{ö2Æ;3{õ·_Ò›]OžžYÖ[æ‚?OU˲đöG¯Ò£pôçʤ¥·†[½;õˆà€Ñ§i ‡ãÍr°.®k¹öÉ},<‡.йrpùʧ»Æ±Å‹¯Æ6 Ï}û‚óy¦ÃÞ€_óf Ù‡¿¯‘börØ ³è&¯‘¥^Lw<•” z–˜íü®ôÌHHŠ×³ø?E‡÷ð,<Ÿrt î«k-[©Šùðaúxðk&[zÛüý¢¾/—7`}ó³ðó{°g—CwgúAÝÓbX80ÿ?#Àžø*+ÎTâ>ÐÅby1¿èðäKKÁ.ÝÆe~zñóê±ý÷óá~×W sE?—”àÙ•|9ëS¿lœ¯L³9üºä×!Éùø>Æ]{ô[þ·[âsÀoJ÷/0Ãÿ¿¡}ä“è/cÐR×½ÝvXk•?WŸk¾±óž´le¶èviåè…'À¯¤ª6záøÌÍDn6ø5ïâÉ—øûJŠÅ²&£¾¬Ë¨…<æñ^ŽCïÞ²/6½ÊÆyÙh·=,uïø›®Þø>Æ«öƒy8®Yt^  ~-í®}(ð¬h;Bïæ6üÿ%4f°ŒG&I^RÂ÷1J´ÖÖaž0Õ oañŠÉïᙾ“‰ûøhæ``8އ–u¿Lx~ ôO&íÙ7MŒ“ž{.¿æ¶–Œw‚|hOmIõ‚çqi¹OüúÄë…J²¸?ðøš±õ¿Þq‡;8A©Õ›Ï–šï{î ª_;YžkõŠYrøÞ íwJò ½”?¡ÿ_¡u½¾…g€?§ ;jÀoyÎÛ®ßrœ¿©-Á÷Ãiï~ÀñV6s„ë¦5i£'¤)3‡ ëeÁ¾\V5=¸³Ct» oçñ:œoÚNw÷»?cû “¦'§‚}5ŠÍY‹ãSÁÃÀ¯Þ~¹jWq§ð­í;ÀM³û¾È—%^P³Žßzæõβ#qÝÃH@_Û' ÙéZyÊwä€Å`'üª†–V–<b”4/ô<鈯–Ø?0ËgKýGÑ—~ûóÆ·Õ¹øžA£fñṖæ_x“`ühÅ7¾ïŦe>VG}‘«Ñ{öNÜ6Ëâ¤äÜSš)ø<Õ]-_ÀžxsoO¼ÍÔ—Õ£]R’Ô›‹`ô1 HˆŸ¾S]»ç#Ô뢟Kè—ì‹¿#ôu¸ÆÏÎËO ~–—Ü7»”úÞò {8ø­pZu­2ømíʸg<ü½ØêóÓ“ñÿ¥9¥ ⋇a@«"Œ{v¬:µø'´guîÿãëÊãbú¢økÑ.•(©dk§ôKË-TŒ¥2ÚTÚí1Ú´i#*-ƒ¤h£$$CEh£P) BE‹”JÑïœüÝûã|FóÆ}ïï=ë=÷ÜS™·2æ¾/½g¹ ?öçx{_ê%ž‡fs{Ë °Ã%®Ö¡ñ0ߥž6»À|gN>0¿@¹¯^$úâb?YG—Ê!´}ŠÄï`ŠÛÞît‡ß׿pÏÃøÕkį^œFOp(O·ØyÖw—’Ä„”Íx3ðY4x¡Ž*Ø V8£Bd^Ìþþ¶öŒægìÛä°Y俇÷¶˜YƒþNü½\ñ'¼ŸÆ¾ôŸ'»IŒ =Œ<©RzRyqEÜ[ÚííºÀ_¢öŒª9Ømã&Çâ¸?çòo*ÜÿØOi9 ?újì\ÁüF` Î.Lj³sÇòèï(Ž –ƒþÎ+ÈRœÍ…Ñ/`½VZ ß5|hþÛß ÷Vâ¼áì—€ëuÕa#‡Áþ"síðcMÖÙi=â%Ü¥æŽ}³âžÿý‚~çùrã8Ðß^ “ê7Ï€þª`Ö,Ç|WHôG]àC³ò¦¯x>ÙÁð :ŒKûÄZ°€Äï &ŽòáyFuî‹î"Î G4€þ>Ö»2Ôû›T¾Ô0}Ь]úç νIÿqŒŸÃ9V-FœWeH‘áL²™­ÓçJ]!Šõ…§dN ßS÷+ø€;æ‰lŸ„÷«k1 ú ~MBéb è-!õÕÚ@Ok›—ŒyFî-ºÀßá¤0o°Ûu>ýéçÐ?+;¼æ¸ÞfZ!èOÅ{c­E90þ‹Zá—HžKjÙž§@Êz½¡bÜÞÜáw줗ÚÉŠ³gÑŽ•0LQ\xfß r”Øå"8G-d‰H _С4Œþª1¥÷] øI|1ÃB'A¾—òeÏ}_–I÷¦ ŒçN »£ßi[±ÓüNGFú£8x¯…Q÷ø¬£:j¯gžÆ|ÀÙ‹Ïà{ø—Í<¿^“áÜ=³Ú ûJéŠfaþœ?z/ ø™›7ÃÄýÙýQ/÷> üà=OâÏQ†GÌþàyÛ‚à3«éÍýÓ$~>N³8 b»±>NíÛå8\R;ôì´ÖÆð(x¿ ½ÐçŠÀOæþkÙxÞ]Û%“¦tГœÁ—%áy˜wî_-ý\7#¢3Š~¯á̾rŒ3ª[$fàþçú/œ¤€þ T|20xôij8¶µ—ç~®Äå«Ré¸ïmÉHð™²ØÞè ØÉÔs´ï—!žç±­þ9‚v¸¿6ÿ/Øá¨¹Ó`·K¶ª,DýRqó¿p}Ô~EüVð“~W;[f^A=]ŸD0¸í$ Áϧ–— ¢Þª’ÈÂu‚êáñƒ±àšÖ;AYCœÏ‚ùžx1ÆVú*دôÕ›`\æƒ Ê$þ%}HÓ÷…+Ø…½‹o4àº×ź:`§©zT¾`>¢mHãøs”;›KÂIü9ÚkÉGqàm<&wÏá3èµ&Óó”³83yL6Fø‚>QZÍåú‘,¥­ú{¸KüR?Ä“©7ô‡ôq½g'uè­(Éøñ ?e*ê‚ð<Ôð‰&ÈS(¾ ëš­ýëàþ^šûx`´Íӛȹ’x(¶‡ó“˜­ùïÊÜÏ%SúE€zø­DkÄxuE؇4°Óû­6_?—„çû™õÑ@Ð¶Û ;¸csì´ÛƒßgFù²ßƒ`G˜9v½û™µd·O ègÊÑàu$zDæÇŽSàÿ5H…´c¾Í[o×­·pµ@¼Å#XP¸#×amy/Ã|·nü½ó;øÉj¢F©`\JÅ)z%‰ýg^šá6r>Ël~ÜŠçgÞ[¯ú[~èç„®ŸGÌý ú€q`{)Iœ?üû§ØðÙayE».È?ýê« #$òæ?yæØýž~æÙ•k>x.*ýéõ¡qÀ™eÝZŠ}@yÖŸ”ÇOÃãÖ ©{ ßð´t]œ™¹ Oƒ€¿ÔoG–®Åý¯/W¹ÀýYÃÖ/:ñþ4i«=åÉl×gUÓéj¡~”ï–ûñ<ë¦óp®‡o9uÞó_Q¡ÏpÿLëú&ÐÌ¾Ú A`‡ß~j}ž÷½â’ Îë=YÚ¸.¶ï<{5èÕÞGR?eAP~r=ùA†sÚáÙ¡ G‚¢¨èWÇÑÀ¯fp–åa¼E×6ù ·ëÛzÃ|¯cÝÐæœ nm,„qi¢¾ý!±ÿôP¾•Î`öòy:ÁïvJò þ»Z‰ý!ÒºWßÂ|áEæU¿½$|ïò à³ü…Э=ˆóµ‹•[I䟠äÎâL™úéW€uáo8ÎD#ÎÙ ÎÌ.º}û+c1ï¥rúòÌ{½Vù°ô¤|m•¢Ì;J†Üç€3%Q-h#öayqؘôS £‰ ÷oËÒüé—‡ý{Cô·åÎ+¹#ÉpÖ1ù\…ñüÌñÄ8'"dF_ì4ëÍúê•€s^SÈS.´ÃO¶-;앴Ɇë=^Ë~þ 8SòT¥Gg¯eZ¸Þ×fÛ%T8³ ®Y‘Ø š €çYf3 ŸÎtáÄ`§éaÏŒnc¼uC$4×ûJ¦?Ωw’Oo¿Œç4ž…qé©:…YsOo]ݬ†ç$¬5àÁ¼cø•Ý~ˆ³³Á¼K`ïxµ¬\@ïËä•)*’øí^…”#÷R±ÔY­(\/ØD¡Ü'ÙùâŸ<ßYâæò“.°ÉãÅ—Ïúkç2ÅWêð~Ô'¹@?2Ô\4µÀ>%R¾=Å~ŽÍÇÅè¼0ïçýÞ‰qœüZ¿*󨼃r¼è,\ÚQ÷o˜ûreý2­m{>“ИRõà¸Jò\…ú±lXWÿƒ¢vóO©<1¥€3c¥YØnŒovî¹U8ÓW\(ºyÿ€>¯O˜·;•aYú£$Ë9û\g{.ë"u*Žfå‚¿ñèmósœen<Àý ÔÕQÏÃ0_P¸º¥ý±Ìu’†¸Ï;F|IÌwÚúÔk2žö*-Såÿ ãÊX¯yèLâçÑ)bx>3EOó®oé¥ Î^–<Îc Ÿ½Ê’ëâ2PÚ?¿C‚3íhmžs­ú»%:øìõÉ]“Dσc÷Ož7¿9Üpô’‰ö€ Ö‡(zßóLÆû‰–öÓ*úö) ×%%V/Œ„÷ÜÖb™Œ}qÐ;gN«Ãh‡½è<íxîcÙüpÿD·"×l”÷žíE"è¯|?¦ }üÔß]úmd8ç— ?ò‡y'”ÞñåiäQBaÖÝ4ªHã¹{ôëgàÑV\÷ýVärN=TõÅ æÕða-6¸Þ&IeÃþF ý%?°e JÑ$~E¯ ä-æùãâ¥6b9¶a÷ÄÉÌxåVWìkzO…åŽóý¹~‰RÆ[ë«NÿNªfÀ¸Î¾v“øyô ˽Óö¸ï,œïž{J¹"Qþ˜Ìɧï_n<÷•_e‚Þ§’Î#ÉÛ Kî1Åþ¹+;½Nž§Ÿž¿T˜,?Ã\l;WÞ«LòÂý†ÅÎΔäCœ+À“üÓ~‰jäñì)Ày˜7'° p.)'6cÿ·Šó Õ[`Þ¥®1ÿP8SyÃþôßþGÍîïïù!Çî¿?²Î:ìX›´F])àlº”#‘;÷s1Ï®ÔåÃ}FöÓìûKÑ ï£ß]}öÌe ‘A¿!ªtôö‚WŠã¹s?SãΓç¸?Ì!kÛÖ× Èü‹ñöû¯þà/´é¾s@ &zpøÝ¬ÿþÎëŽC{‘É. úCõȯ\ïUïY” qi¨OÕ„ ømÏ<‰&Á™¢?WìÕì«Ú_¢þ]vâœyùò$Øíi7ÚÌϼ×)Ô‹ÄõC‰À`Wûscì€qëî|Q ñóè?Nú§ÂýH ‹jp½È8nc#Ö¥·ßù zPùÚ¥ðë€sbôëEßIpfQŸåá>Ý`îÁ£`ÏéĪHô<Á<4‹3!¦¦ñ{ɲ»…à×ÊxvÛæ!ÎÑK¿óá¹¼3UÚ¦ ™cE¢'Ðï¾^ºÖñ ü~Kª€S*þ¾ÃApVííÃ~®œÙ+.ÎæÁçqÀý™®î—?¿{ÙW±€œÉ—ß×½FÂîÌÇ-à/Ôå÷ÐŽŸiÁ'žïÆø*çËÊ­`'kÄ•²Á(‘5H/œ’϶Büçß~ì\=àì,ôc@ýóc×C¼â1õÂü‚ÍŽâç$8zªeØgòôÑw'ÁþÒ{çÝæŒ¥ÖøÕ ì¶<Z•0ß/ÿ:‰qdC£6ØÕéædÑAWüHŒ: ç]mçØñ|Œy;n%¸âø>\ó§S…B ŸâïÝ6gd£ÝIÑ!ÉÃS}¶}ê µÒ`;Ús‡ê£Ád8SNÿ“çk 'qŸ!ƒ;aÖ]¥VY¨£ß½7L®â›<Ë}3À³IW­ø!,˰tŒ·˜>VŽÎþ+å*[gZ×ëä €ëîJKŒ#Ã>lËÇõ}.ë¡Ðo=?ZçÎm;o7}%Á™y*ãë^ì ݲé5àÌú3¥¶ýîá„ß~¨?c7S°Î¬ù÷ iøÕtŸ#ã€ë‘ß &çñüñueáº×.»exžÈÇ ¶#™€sspç!¿€b6?ôHÛ™+Æã˜÷šòºuëåÿ¦¡ÿ¿cyï#ÀyøÞçEœïë‚®â¹þiRÎ0®èí÷]dy>ÉsgÎÄxˆâwú•{úa+ @>ØË?mW½¯¼åÓ Î4•&mÀçáXÃ!às‰dô^Y{N0 þɳªZaž£´÷Ž =àL«á8yq¾8ó-Þ[N”m5ØÁaÚ¡G£0"‰û“Ý8,Ù–ƒ~^ÛûÂ䈞²ãÄ!\§W5ò@¿±nÆ|úý|mŽÀoWQ›oçðC#Ôy$Ïeðh—9Ø‘‰àŠ€Ÿ¨7ÿTq<Çz0¯ÚÔó _¦×ÜОͿh˹) ßͶ ~R‰UÚuÀ¹D0ù¥æ×Ãו`_ú[ ‘ /ç‰^áK_Ip¦ë\ç Ç}1Ý™lR`‡§=Þ `|åµâÏ› á¬øî'ÊsÖÖK•ˆ³õ¤ãÃc|â7ãa\cÞtÚî—&–€³ãÑÃgf¤…ÒæÙõß̧2à_”%—xÎâç¦__%É«1O2Ãú¦² ïc+Áo£T'yU‘áLyõç³eµtÜ¿âQÿqÖIÉk@œ“ô)¡ Ç4,äÞÅuŠä䫸Îl»Ènež_s(Ž~ý<õ»ž€³ ÿ]'ØOÓ‚pþIÀÙ?.>Ü î¯*½€{Ä{Y·®=0(œ-§ÓÉpNl¾vÄŸxtð™ñ9áò·`´ÃyªØcZßðq¾-´ëúh_LTzB)Äw.ñg€3KËEB®7Çt°{IÍðÿ*z ØrIø§ÔVƒu=–î¸îÕ^ÏŽëSá*§"Á/¤±Ül zm82ƾ ý”€¿¾òwtu=aΜßG_Ù“áì 5ƽÆ_¶8ºåY£ÆÊqîâ)ð€8«Ê×+pN]¢û„gÊ:ÇÂ3ÀçáŠçžÉ€sâ¹UÇÞ‘Êóø?½mŸ4öì}ÏÙ$cŒ_Š/Á:ºkÏU¸ü ·â°›_g/›®±§$8Ó_®UÇ󥇿) žœiz=62˜—ÚòÞ;ôK÷€Ïý³Èoæ %æKÎd[ïÂ¾ì œâF`'Úö3·ã:އ¸*;àйuWvà'§®@‚3Q~¦ø˜;…H,nÿ8«Ä­œé«¢ôè çªN]˧°Î#)~ãh…«¯–âyaÑOÞY¸´l7ÿ2œeç]Û}NnËØ¸Ás'ü¡άûr«ÀîÆZ}fÎòuIK&Ip–ûpóè¯I"Á™>óctîK—ʼƒöñîéƒó1_2¶ö‡°7…àäqéÅå<9»qÝÕÁ×ðí•<ç8;w¬þŠëhǺߞývž¯Ìïà<úMƒû}.É}¿˜ö‹çg¿¯p$裖wçºi©xW Ñ[¶=fâÜvÊû)ú££—­°©ÅÄ(·fÔè€$I†8¶å1±æ ÷I3¬W¦§g:R0_2šPõç…8²žü¸æv¾.²zˆ£ÜÀg e“dzÐÙñ§•TžÿáÌ&§ï³ 晤fTâ\4qŽŽ8Ô–w¡Ý6vñìú>Ãó ·Pok]ù@¿ëw{k^œë®™èÉcœÞ) }óä\_ŒàþûUZÖ)#Σ?i·_Ìrv¤ÎQªªÒždòÜã d¼ô›¦éß§0^ÝÓƒùÏÉõ+ŒaÞUÖÛ·©€O ž/Y‡ò¼4ÌMÜ ÏÝh;Ò 87s¤ÔŸÅu…â‰ÈP aË—™i q{DÿÃ\’û ²-1¶…øæ`Ð<ÇHÀ™—íg&â\»»ZùPí œ 8'Š µ^Ey ÿ2ø<™z6Ò…ãfod'Ãù·é»Ë[@’®^ät‡qµ´Â—Δ‚­,—Ù~‹GnnœïŠNÝ&Á™˜ÿ.| ø\Ÿr¨©ø\±î ß-Ry>óçÆ=3ãú‚ºÍøíàL“û¾½õ¶¢¡ÜM ðŸ#j$ø0~¹ãÒòÌaóç¼=Ì{õÓÕˆ³@¿×~ÄyruÕðÛYÞíïc]§Ó;Äy‰ñád_ §$ÔX8khûEå’á¹i§+Ì{šLñ|à3CE!jõ äã‚©›6àg®¼­a€ùMv•,¬£¢×R«"Áø4îÜ>ÀYÜF]fåù¤Íá À»r¥€³iÅ’ìá\’ûvú´Sq_ǶÇÁ‘ ·ÛüŸ`ž»nÞÞ7{Ú`F-æßVì~‡u´ ¾R_`~må;hô ÆÝa´Î%F\NÁ¾|Ir]‰u/5…ö <«nxyÆ…BD%Ö/Þ8ç¹s2FÉpÖtú|Öè1dòfc=ß©~Ry.ý‡³Û‚õ0_/| 83%kGçð¨(kC<׎ۺåùMd3î£+¬Ü³û?ŸOÍB½mÊ`ËBœ+#ù~?ö| çÔA}2=<}ïáezžK zuj<àœ÷@ÞçÎa—4Áï1?×:Šz›ÿÂÁ0Äy»Às3ÐÏ¿;÷(`^¦v/u9ÖQú4¸ôQÙ/˜u€3õx®ÉF¸N›U5ä°²Ìø×nÀY(0LBƒ$n'‚Ÿ?0¡PˆŠ+«_ÛDÂüâd ˜ »`|]Î^˜õg§¾Êã|wØ¡XöE•±òÑ›\ø}jbÑa2œY}š*«)ñëèÁ)¬«=Ñ‘8ÇÜ9nñ–þ¦ÝK§ÞìÙAV'¹Ã*¾ä¯Få‰R7Ö±œ õi’ÄÛ³íÎÄû-fºý»ÞÀ<Ì/V~xÌDœé)5#=º„sÞº%¸žKÈ™ÏàûQ×IéÖ?íÿ4c~sG'}¶Î‘¢J•œÔ%J¾õúáýÚù!ÎBÓŽÂ"ÆH;¬åYSÝþ>΄Öת]Bþ–÷ËÅ×luû 8Ó½£wÿÐ%´ü†î›»WR£}¦—]3‡çIôJ}18—ääoçÆ<¬L×T²…øÍ›R{pÎcïØÎA†3a”môY— ê”­ŠÄuª¤â׳û–^¼3Ð%&ÖVÕ¶b|)—™3„òÌ|%Ñ8äÀFC˜ýΣ‚øÍd8B#Mº㱜ézwgmëaÄ™`ÛÌqY,'æÓè« Èp&XW‚¹(„Õ¤‡K@ž4>“Ryþûçs9ê¢5*眲««sçÛ1ÛøwŸ‹ŽYHc=q?ócâìøó¼# ìÔÈôÏVÀY|çd©úa¯gfÙŸ_t>œY3µpŠÓαJo&¡<~D¥pN\ÚážN†³ÌƆ0^5h ð™VhÍ.†8_ýµâå~°›c®‘æç0nW ë™Å9Eu‹ ŒÛ¸ÝfpÖ(Ÿ—2€õ 6g½«¼˜DÆÄå‰XÀ¹Ò÷øëϹ$÷U¬¨;i÷SHŠ}Q¶.ââ<üûÇ:sàKïVÝv¬SYԕׄ8{·ŒBü¢1mâüAalÎnAêuØçÙßruÔÛ‘…ñÆØáðÝ¢ŽL¢'Äý¶àÌÓ{Oî+™ÞþýÎôð9oÅÔYiÀ™²éó¢÷¤öy%m牃ë%M°.—¯âîò’Ëxƒ8k”íW¥a]تч ·…8Î.ÁõÉÏ=˜ÏiÕM Cœç/=…ò¬ðó7Ú.œë.oX›„ö9ìq&®G0t³<çƃÐ×d8ÓH}…ý*Ö/Kýˆ~Øù¿û­Ñß}[%…û&õ´ûEgqïcü¨·•ìý ü’q¨/)œëž(œ^Šz;ÜW ë†GE£5ç‘[Ådò|Üï³ó>x/ê-£HÛ±²qþ"2m‹õŸÏNÎtýÖ<шóQìƒá%?ÿúwkYâ«v’á\¶¤ÌT®/P{þö@e ý°>?Gásϯ…kçz­™>2yª¿ž3Âæø,™môv ™Þ¦lû‡³Èƒ›Wvaþ‡ž\8Ï °c"Hw\ø:ÜË÷ðE<úÕÏ· <Ë}”ùLlêxzÆåÉ{¸å9³yö1&‘úá÷ˆ"Ü_#÷U†)àL»ø¾¢>äiÃÕKA€³í: ÃDRû‘÷£—Iμj'?æÓ*´evÎÉuiÊdñs®Û½¢Cð{žê³/ça±W—R`\ŠJŒM›¼ówÖ³çFWš3ð)Ñg»z:༪rtâæÃ¢½Ÿu‚þâëèœÍó ¯†å’ð£âÁ­—^ ü{ÏëŽqÕ˜3× Ð³¬÷/Ûìgª±Üq¦½•8 ¿#ŒûŽ_‡q÷8 üh&Ë“ü⣛b¾'¤ÿ1âÜaÍ: ãx¦Óa>æ[JÎ5;lŽÕ®Ç}1‰ —“WÐß=—ºË“Ô>Ÿû‡³üϯÂýÆ]s•0®j¾a…ñ³ê³wW@nUuŒøaý{Qúþ¬§0ÕæÆ:õ' fX¯,óð×Q6ÔÛ¢Yšà‹±©ÌÐ;B£Š^wuPuK†y÷âÆ‚§…0_Ò?©n!“ç†ÔêBÐ#‰…7áz#6S´qn Ü‹};x쯎rô%UÑœ;ðî-]Õ¢xköáºÙDcu2;Œ{þMìèM2¼Å¤K"öྦྷº°C2ŠïÇ*oÚ„È“Ã0ž|þñ5Ù€w°¸^–Y^lã¡·¸ï(êý%&æ™wGœ4 •ëîríz¶Æë•ê6¼} 87§¦'Vâ>àbk.W¬Ã3×|˜8óÈ램üŸYu ?ÜG¼£®õ,Œß#¼+Çpd:*ôâ>·£ ç-€ûç‰;jΉ2ÎÎ+á³·x¾ àÌÈt‹"[Ï]mìˆû<-Ìsßùt¤û€}ÑzÖ vQ¨wÊö ÖýQ¤«àzF,×¹zÀÃÿ¤aBà|Ì[GTë™J_'¤Àsõ¨Ë½Öœ#ŸFw “­O~WX]ò›xh_öߘ²{Žq 8[¯ìS»E²žA{—k 8P¹—Þ>§J¾f<›{Ý d™ÉyhhstƒÇvíÆE–Öé[±±Ò‡ü H¬å–É"ô„ÓiÙÿPqUÇœ‚Ýû£;Lk±z‘O¸NÇ„ RS0!sník»‡…%xd`ã’êóO ˜GmÁo²Â’¨ƒ)_б´·íëÅ…èc·n­A l{=C’튧ÁÆì®mW° [vóŠC‘0^k¶¬mœÁ¼™MSúž`bPC>Bù(6¿€%Þd…ˆÔñƒ~ät\(” ÍÄaAwíââwXزõƒý'„úW.F ¢K“–ÀD2NX3Š Ñ +í¿“äEÖ?ãðöß5A Û­•«œ°@z«µ:LØú÷-­:tÜàC² ©G8¯`×õ¢´óîõ§4I¢gq¦ NÅbA‹È ãg¸Q07¨y#àÌ·«*ñÜžåŽè£ÍN`a‰ÌÚâý¸ð`ãkI taa ëæq¡üÿWŽ´Ãsz³?‹…%Ô#/³m!àKÇÎÿþÄ ó"†Â"± ;!ÍõYésÆ{×í‚Ö"[2œ·þÚ¢@ƒß;(® rņk£x`Ü‘'ÿabžß6:pÖp¯´ßL‚³LïÙL5˜ª'V©Î2åëI ÅŽÿ“ç¶ÓKtQOÆ_y÷ 泌ÿgi,´¾3톫MÞ߯3"³Þ¯d×Íë•€'ë|Ëñ\@Vç*ÃD”Þ†t|xæßÅÂÓå¢gígñ‚EÒj`Ò?V®cœÏ}zË–;÷sÑY_º±Ð˜¦6ß÷>RSd_žÃß;Yu–`/TKû«šá=eœÞ¶a¢*åòé¯ðY’²Ì‚p¯NŸH‡ë<7{4•qÀËø,àœ¨vQm3Ù†37¥Ao¨Ö7>®Æ]_<âÑn¬.Ã,Õ$Š1¸~9êå7i—Ÿ%ŸGVqýÝ|Ó{É ¹L«ãÆXÑkw3ÀѤЇŽacM®À¤8?׿ü*øMâX©ŠMÜtÀÀÂä¨Å6'žÜ‹^¤¾ÌôYœ)J;Ô–àF ʆgû€¿%… ì~`Áþ£Cy0¯‡k¥—ÁûQÊ7]œi_m½°PÚ8pE>àœx5ñ-'z±Ð×8¶kFlwŸÖÀýëÄ/¬Q>˜Zª{‚C$þ<ú¥ Îľã’5 WX&<ö˜h¾w\Ûq¾{Þ°žÃøúër/¸¿¿æ@Z26ÐØ×±1ô˜ñýÄùgáþ£æûbâÊÝó¹‚cdúƒ§<ô‡]«ÑU~²ߥ^©!؈CNËp²Y4«OndË]€¿Ô̾LL$Ôý¿ƒÝvýáÚˆ ý¯×òÃ<æ‘Jëb’rû >P 7Qm·Egf἟z€³êö¦þFàUCëvØÛ(þ´?fd8­ñqÁŒr§lËÏÍ{5’mÌ‚ˆìÎ3OûbÃ;ÖËþ¯à‡”è ˆÉ{ã†õAv!À•n¢êºøËPÔ/ÅóôÑ]óWJý_÷³0~¯˜¾n96Ò:(U{ç¥gŒ8\„Ä>}làUA±nûÅñ‘þ ì¤ïÀø‹ 2¨wQV>&nîÄ÷ÃÄ€˜»üÍŠŸñhŠÃDávŸ%€ãÄ‚¿ÞûÑïc()ýÅÆ㯥”áýEùÎéVÀß½mF áÈÿ§¡kgºãâgê$‰+æÆ>Ý1l°gÖpý=Ïwoø[ÆÔô´:àÉ=O¤¢ñ{†ñK˜ÿ½K#ìÄ‚Ÿá¼ÏaÜx»cS‚d8×Úf–âÆX1›=)˜ÊS¥Áß´þ‡/aúß‘ä-€3guá~={ËvøÉ‡®ç»¿ªÎß™©æ·É6à0?ÿ³Ï{Ú Æ°ÁãÌö…#Pmz[ŠòZ‘ÞŽ~¥ž=»<51ËäçìÚÏ è_o«_b•¼ÏÌtgêÔ[Ak´O+,°@,®ÕŠã.<äŠ9¤"Ÿ÷v½¬ýù*¿jý™,Iz€ö8,hÍ2¯fêþÔ ´›¢Ùò«ÑÌı6¿ u—Ñÿýrè-8Ü_^nƒùWyc‰\¾h} Ì'ƘO‚b.“ègÿúà>IàC¯|¥jˆ~×®W±@Œ•%8‘óŒql<ô}$|o&8Ÿý½ÏŠ_MPÏE¸ü¹„c#jò#/ãF ;{©d­XÉÆ‡œàú°}ö)Ühe_ÐòÍ(tùÈ@{l7HŸ=Ü»­L,—ÄÞ&îÚw·¯÷°™ñƒ<‰Ÿ\a§HºÑŠßnVžc¯ ß#øØ<>gržšņï›Ë°€â9çó4Løé¨¢âbñÝeñçÔá³.iµ7>±k5¯ÇÀô˜º%ðY÷zA:òSØ¿9 øz]¯|>ãÎò$ú“éÏÅ#•GEéeaÁçàçÔùïEº3—£Ÿ§ú^W J:›µØg!™Ý9Üh¯wøŒÞL³æN˜m‰#¯ßΩΑ¡uðÙÆ&:NV€ËdkäþŒˆ™â[0!÷ü>ë´lLk*ùÎrzšŸT÷Ž7¨×woÚ ã1ýlî©ÁçÄéÁÓé$'‰åe*¸ar}wöü”1sœ)ý¾¥¾ÍŒµµ'ᓚò°I’¬Ñ[2w &&î­OÊ@~ïb‹,&Ý8©:‹3kþµ“:¸ÁJ’ow È5ƒ‹b ~™*ÝH0ô5KöÆŠ(ô³×opK„÷›ÐãÅÄ“êÀÚkÛaüTÞú÷À÷TÇã&¼8/WÊ<žÏ™¨Â*Tþw* 6Å[E1Sëœð÷õ¶$ú“>ò5ã.T­39rͲ•=ð7úù­q9€oêÛV=À7‹¼-ãAÏÈ\޽ 3†/Ú.ÇBþ2R²X ±MrQn¼þzÛù Ì«æ0õ?æ$þ0sì ‡^úÏ·”æã†Œúû¬p£€É•¨¼Ï²”Cg#a(ŸªÁ„àö“—®á{rsu…q{=¯Ø‘-,îý!2‚ ’EÑoÅ€Ÿbw‚_Æ”àT„ÙÎ3:;€%Ó¥ ïIäSf¸àD"üÎk½àªXàsE{ç·d² ï‹Yœén»E±ñË —h—‡ºòù°ñáš«YE±Ï[t&ÂûÑŸ|ÖSÆB‹¨k­]0þ°¶Õ±ã˜p®ÌòŽœKôšVJá¼=·ã)â”:à{Oý¢{ð³ý¥ÊíË…ß[íx0E"WB~Aç–`ãá±ä”@”¯v3À™U¼s!6þ•’S <ƒþêŽD~llàt/Ëø’èh¶Þãåžobáúžƒ±.¸1 ­6ñÜà!š!âü›Ä~–|Ìç›úŠyÙFÀ î/c_lø æ™Åd^,.ÌR_jåbãAsÿ>À9ñ”'ê%ÕÃY1¥˜ nηRÿ‹*Î8× ‡‡a£âÏüš ‡y&þp{’à,¾ßZí:n˜–mý…þQⵞµ¤ ‚fq&žF¼—Fûwù`î~lT"­QäëüZׂù‘ OÜ(Håˆ QįôUéЬCí ½Èwµk9Ž '©~Iµ›±ÁcýÖ ôǨAŸ^c£»Ð¥" ˜ÄþëRâå ?[;Ýb¹Hpf 5>ªFœw*k„âËøÔ•5¸VÈqøæ?FBëspã—ò’Il(–8ox¾ê—ž‚ÝŸ1áê¼TŸûÙö“§Àïã)ßÅqpþn-³Œg™mÿ |@œSÚiOÑÿ²Sßa‡ t¿…­rpC!cK,/ÆS®Ûü@Þó ÞÆSá}d”M+5qÜŠ…m#$q/½ÆnÓ|lü¼ïÀ‘ Ø8·X;Zü¼ßÏ:qœ'#‡˜wò_¹ÕЬámüÕáFÜ`uú×­aàs”‹í†M¤J.ÍâÌürïÀ1,ඤßZ ëE”9¾b‚9Étß,àŽ’ä ‚÷:y«ØÞÏÂa!&Ñ®–­ÏǪѕÝÏ@~œ'®¼ÀFKÊ7„°ñÐ×Å¿ßk¶ YRzM1Áæ¿@@®±‰þT•öT¸9›Ø®°lÁøj´±K ü†:ßéÙ†Ò @¾¼ÂH£ž©Ð¹çöLÙíO:=³Ûœ׿üüÑöâW»ÆJ˜Wuž_K.ØOÚÁ&žH܈‘VU„vYT”ƃ q¶ )a!`s5ô™êÎ ÓdÜè'›zíD.Üï¡ÁÅ]¸áñ–ÏÝ$’xˆ>³ïTˆiÔn»Ì­=ªAœ+|¦g/ÛìoŸQ> =.í$‘O™LÃ}Xpéõö÷ \çS?kLÚxèÉ?œï:ŸåıyV·®¾¦l <Ć h:råbXðÙÅÓ‹~‡å…€J”3;hGÒOø)¢ž´7èù‘Ÿò;†|œ|x_/oºàˆñ gŠR pwþ•*¹ª M&Ë'|ðv[„ ƒwïØÊcCä GõýÁoh^ìl†óÖ7/bC¿¡õî÷N¯ŠúTؘ۟iÈ<¦æeb¹îÛç ÏKäÒÂÀ^8_,•C¿xøÖØÛ’‚ Ó½ˆóÞúvl$&Â~AôÜð*êK,(·Z‚ Šû¹Ê±1u#¿Î_ÀÙËøî&\2³|ú„¬ÁdñÕCk±°w„ïÕM\ œ¯²Çó ÄýÇš¬±@LêdÚ»ºÔCå$ò™'x§ea6æÃ´c¾ŸóÞîu{HÚHìû?½.®·¿È7¯ÃÂûµ`Gœk„ƒw`\ʸžŽñ¤…Ù‹ðzÓØKc¾Mû“w/Ì™¡z‰Èw¹Ø?pƒ'?Ÿ¶ êסòykï:·›W”€z.dr%T°tšN"W͆ÞŽcA»@c.ŒÞñè=Óðì• Ú­ žrJ¸á+éÂ+iÜàÈe°kÈs>cmæg/ÎㆷéÝ¿÷u‚½  <°ÃÆ€œ¹[Iֈ©†lŒ#^Ñ›aÞ³ŽTÜ1¿`øÉÈsC\/H7b¢%C‰y7µ“û/#¹ÇÆõ·0Žn ÉW0wþ¼ˆü=R-IÃ>ŒåƒÖ`W.¤x¨cŒ°½è7Ó7[½Ó ‘ϸæ}ŦØÐjQLOæwé#-d ™Âö³­­«°Q+Ý…óÝ^œo?ß°¾€_8ñ`ó3lBóäŠÂö¼øzìÓK)M ã–¤ $zuxdwß nžg ÷ìÓÈiEÆ™A™Z§þ5JÛróe”¤¸M ïe“Ý¥Çò±àñÙJ×YDÂM“Äbð4.“ÛkVñNà>näå[ÂóOÇLj€=“Ñ÷oñù&ØŒâIä³gsÒ",(£Î|6ÇB‹æðo–¤{­gq¦ÔrÊÁ‚ºïOW¢ß_ß»søý½ù‹ö•c¼zKõ86Z¢]i ;ó¼®h”'ô.£»®®ó½],³iàkŽê¾0K¸?³íÃo/,lf‘þøö$e©êiÀY5¬»lv£ÌººdùÛ£¿ÓçšI˜¯ ¬º“çŽ⬇–cã¬Â†7 Œs¯ùï’ -À|>#÷ <æ3Î[G^…ë=#®-–ÅXx÷a3n,¨àR;œB¦Wy«ìó±àqþ!ɧ Ç ±A±@o´©ÅÓXÐ0“L ò@n/ñ»jQ ¶ü<†ÅÙ º0.m“•Eæ÷>ÿò+q~ö4ñ?lÄm_l?²üšÖÊwuWð  æÌãÙ:FÒˆ»÷wwgÙ%lüÝå!Šþˆ/¿qi#îÐz;ppT +§ntc>޲­°ýÁO‰6œøÜ®¹¾Ñ(ïõ;Ö<‚y®Ú.›! zIËd`žÌ³kà‘zGï×Uô£S¿a#Î w¬ ï;úüZoâFǺ- ï£•O“ä{¨1×)cØ@â»iH6Þ¿§ð2è™TÍoïgÿŸ~ÓÊœPÌÏ-nàùJm6Ry 8Sò® ýÁ‚ŸOG±à‡Ê*Ôƒ8Û,xnóÊÿêKu~’¸”Õ¯‚`év=Çu7ßM0ϼbGâP?:ü>d‡ö-}YÜ °Û•´}Ì\À™XQéºgHÈ}8Oæ÷î÷ÖëCœ×_±mÄÆú+-¨L‚ÓI`¹ Ä!ÿ^ß°1¶8w2ÉPœKßO°c\*”“ó!ñ°ÎôS’ƒ5æ5û¡ý¤_ÄrªÌ0æÙ瀟 Ñ$6ƒyãõÕ\w¹/ÃFcJÊ&qƒ „óÈ×]•ÚªÀWåMW›àþ¬“#°°E­Iÿ:àÅu×ü‘å-˜!Ôsϱ‘Üê=$ú­ä»OCæ#¶¨¿” µ!†Ñà/ÐÌí 7a#Ì-?,ÑŽ,õ䈾û§|¿ó‡áWødúÃ^Ϲôàz£~® øB†‰Ê(ǿߧ“È›P…ù™.A9~™zü„­Ëï-®Âç¿rçä´Wtm.<ð…ý‘Ô¤<ØY÷‰vá¸qÖ´` ØÉ,vÛáèO<¥f(`½EOq;ð}Ãã.8Àyx±{½+È#£ò¡oI|˜ØÆeÞ€8³vÑÑ_Ï÷ƃÂÃçëjq±‘[™Š@/Æ·Ôy>,̈ oÂõÞñ­‰p}TÔëìyÀ9Qûþ¯0¯xÎ\½’D"oôGìwäÐιmµÇ<óÓíœ*ˆ/'>±=ŸDœ¿õUcþÕhE,7ð¡ÆÇöÕ]À™X{òû)ÌïWÐúmIü!–û½ ˆóß‹¸Ž!õ‰aÎ$ª3{†0ß/k}ÍyénÐS‰œ{œºS6•—qÃKôô äsJÓªw$¥Ì‘YœeÖl:6Û¨–c,§ ý@·%nœ`?ÎqõÀ†Þ”æRN\ßpåkÄ -g×EÆ¢¿•ñÜl æ3–{Ù½Î,Ö÷q^•(—‰öSr¦ÔÆ­dÛ•ópöº-?Þ ãi08/ø“<¬Ô-æðžôLoú6Ì÷/ãq ‚ù—“`ôj¶Ñõ‰mr¸ýÄÅø¦Z˜švŸùôêtàËê7müÅ%Ÿ¿žBy^³ââ ®UD‚E÷­íMócáy¸AÆý…¢è‘à†¾‡Q¸i©¾´ÆŸi"À‡µD‘„}ÊñxÏ5\çg·o"9˜ÄËHEŒ×·(76âÆ“*¯Uþ;™„­—×cO\WkŒd8EâÁ;SLåu:æ  647|ôD×ÍVò,~Brð!Aw@œå›hÔuø;Ó²½MxP™¶ì<6°þ›“¸­ç`•µ®ð|LÝ×ß{¬îŽ/úõô#ùÕÕ X;¼Ì›€ï'®äTδE-—ùÑ~* ‰øß“vÚ Ê–ÂßVƺÀ¯Ä§/) $þdݵGU¡ð¾ôWá7ݰ¡Úòò‹ì˜Ä]C¬ÓÙ¯5¸ó´e…wÂüsîÝŒ8‹ýçeë[iW_Žþ)ØhQ#ƒ~Á¢‡ææèÿÏ(Ëœ!ñKY×+_ð‚½£¿ÿ©XŠkÚÎLF91‰pþŠã ˜(òÂP¦ÈŸÀ70œØ¦Û€3í—dÀ6¬—ó[ò†›DßRÛœtç?#Ü;ð`º–_êfL¢è€SàÌü{°(\e¤]p$9˜.gj™´%úçÙ‡¼Q9–y¬"=˜N{g Ñß}g¥kXÚúåDÃlì2¡]ˆúõä¤t7Îo¶«÷0¿EõÖj#οVc=@y¬¶ÈÕ¹µ¥Û qýF⡯!Ö‰–Mo9x,ø{" À_Ý%¸K ãŸÿŒvíÇüäÆ Wƒª&ýØhþ´úÅZs ‹5¸Ñ ž—rpuÖu^ȺÍÄüéÚy´u+‚£{ç_nEÒ`¯™C›ûãæ~®æž¸qGÄùâc±<´c·ß;F ÝqÒÕú³¿ÁyÇþ~c+Ì»:†ïØÜXfܙꌨìïàúEÅц›¸A‹Ÿ1‚¼é÷æw’ÌûæeBÜØ° ÎÃù•³xAίµ%“Ø°Šø5¶DÁ>õ^¨ûæ¼ô¨ëãB”ƒ¥éQxàª|’¯ÉAݬF®ïô7;´àÓ{ÅrM ˜§ å±q±0Ö˜7ÊΜÿüÉmîq48Ó$Ñ?·Ø|-çõMïK.sÿž`¶ÌâL8>¨íGœ{£8ôñ`ù~u6#¸¿sø¦G›gQÇEtä7ç•Ï|ÀoŠæ}”g†˜ú&Ô«û|EÿVs©…ˆó¯ä%´«£oO€\/¾RÏ^¿×<`îŒëž7}ÉÞ£ùÊÍÊpĹlžÂAÐkD·ãW “HmHYèŒòì´ñö<˜:¯s¾¨ñ÷¿E½,²„ çʼnæ=àopå:Z ”¡ÿD§âs,¶³'y®Äg¬/˜/'ü…»×¨ÁçŠâÌeë@žWüºt q¶.?PnŸ‡<¿l8-Á,e#âì[Ú¼>£^ñ<‚ëšœ[ÙgÅÁÅœ gé=·첚û¾BAü6–ˆ3å@›ÎðÉPYç¶ü Å®K¸q¸·ñì]˜oÄà˜ȣЛ&ž Ĺ^!{æ9!iô†×bîñ¥6 (ïÎ2Î2𩱄¿})“0»y)þžé˃yK¨M=+¢Î=NåÃ&=”ç¡ø×‘Ïzg½?šÍý{‚)í8›Ë¬õ·Ë®[HQ¢d€µ_œBìïqf°Ÿõ]߯þ;¥L!äE¦Äg*5® ß¿q̯A!† ù:ÛgË…3ø>cðÚF- !Á!Ñ,ˆò¼¡¦{˜ ßÿj\$mJ™ó±TWÞJFœ›¯7뭂߉Ÿi’¡_=ügñÒ—Õá{WÅ£uë)c~üÞÿçÄyÁyšð½j©ç2¸£â¿iq~¾®_}|O¹9)²mîûG«öZ#δûéì ð»Ê«{Áý£‚7M{"Îò%gO«Â÷Æwvy*Á÷‹ O!΋¹}eqÜ@ƒÅÝ[çŸò>X† qfm[²DŸÓpô…ÕˆÊÉ…ïuö7}Ö‡ïk„ÚèÎ=ÎÖý 5[gv•‚Ègjq—ÖÜ¿'([gqfÊ|»4„8»èc$¢Ñub5ðúí¿‹¸Ÿ†à¡œŠÁ÷H‘^™o@¿«ú•âœ]4½—BÐey^5§mwÚó…ÚýuHqöK§gî†çY¸õ"^W\ßÕÑ8f:x:Á÷Ëž¾â²Ÿû¾´ž‹¢{gu)_ä·××[€ß´ð+«]ç…KöÆðýdÖ‚bÀ³S=µyqnM»kßç-I~|pîñ™ÆîâL]WÐ*¿ã,|µæQöÑ™¹ðý|¹÷›­áûúìšûæ§àؓڈó'†±ž%¼—Òv¾#{çþ=Átù'Ïìß::çë®uk ñþ)ÇÜá=è«È!ÎþM[C¬à{«6Sv ÁÉÞ:Û 'Óóðxý]ï¼§Æ“Ë?ðà9b-óÍ7À“¾ï…_4à)~òvùúaÊ>Š+o k.–Ÿû¹„ZÓÂŽ"ΆjêQK–®¾ó¬.T+Õqî›6<ßË'<» |Qåo>>óíaWhØØmq®Ä ~/½é[ô·ß:µö£¬¨ôïÎ>sß—¾®»`âüà¿49˜/ Çu9 qõuž{œ‰MuArhŸ•dOìw¹™Š¯úD"7%agÆËèzÄyÏZ–5êÓ¬¿ål0oy&¢n.ĸÊjWî06d<-«^|•á¹³åù¤/k>%ž(‡çÚsRÿýuôÃ\N\?‡á¶v£ƒ­cݥ؈Œ;é.~ï$4¾3xîçò’>׎‚±±·ß׬ @>½¤6+þ‡8÷»æIÀü¢¤5&ß¶=²è‰ßÆU:?$Eཙ놄…áú=‰'½µ€3Ó†7òkŒsV•¹3€ä¾’ÅçmÛ…‚>¢ÿ{^µì×4Ÿ>ÆUþ7Š4°ñ­Ò¦gÇÀ}×È-½òô¥ñ·?Œ[™¿Šdþ2>(©·#ÎŒõ9÷Q/×pN¾;S™ë¡å™ ÏÙÛ²®Ãž?.y Ïsîq¢ó‰Î̪D¿÷ÈçÑ£ñnsÿž`–ÍâL˜ÜÔ¾Œ8GWÚì€÷"ÞÞRú|•oß©øçñÄ‹0}À‘r+ÿú&î7J«0O"æPÐ{ žëþâ7…ëÈ=Þö8ŸûôK.aÛü ²àa"³Q®¯bKÜ–†8?íˆõˆ ‰3?÷tžšû¾y®V˜÷TŸnǃb¸ïo±¡©·_ä#²yEuÃý8© [ÐÿÇœθÎ,«ìò¯0.óÝÉî·!s/ÄVÕ•8_Þ8]‰zsZ•ÿ§>…xnVþK=õ#“'ähòÁp¤/Ésžù{ïæ·¿üg¯ ó‹Ñ¾/AþØÜ¿'(³8óLÎ_ƒ8ÿ½Yðô2±üž»9ÈQý…{jp÷n½l¾ÿæôd¼çZÿ]üˆóÕ—Ç.¢> ñ¬ú xþŽÕQ¶Áüô ‹[çÂ÷®v{OãÁ;>Ê÷IôNãç“5(O9Y%»`^И—r¹üæþ=j³8ÓÔ”7"ÎÍWu`þôÚ¨· <6¬÷Áy¦{ÝøÆ,½e zº3–jW‹õš¼Å Òð\´€Ýâ:ð\ô¡û÷æc~½—·d)¼/¥ùî»ÐÏß»DYÀ91oü¡AðC„ÝúgÜÜÏ5‘§¹Ž ã§žg3ýÁüexŽûÅ™÷ßelªÂÄÄ 4ˆ ˆˆÆÄOàÅàÝ -Z$ ´H Hh ÐR I ) i e@2@ËV­Z´HHHHH Hh ÐZ  U u@ë6©mRÚ¤ô&Ðf - m  dvoÔÚ´ÈÈÈÈhÐv  S 3 @;¨@»€Ìví²Ú´ÈÈ è5 -ÐA Ð! ; {  G ' Ã@Î@®@G€Ü€Ü<€<¼€Žyù:ä ää tˆt (( (((è4PP4P P,PРx  D $ d s@)@ç.¥¥¥]ÊÊÊÊb]º t(è*P.Ð5 | ë@7€ € Š€ŠJ€nÝ**º Tt¨è.Ð= û@•@U@˜@€UÕÕ=z Tô è9Ð  z  F & f —@-@­@¯Þ½jjêzÔ Ôô¨ˆôè#Ð'  Ï@_€zú€ú¾}ú4ôhh èÐ8ÐÐ$Ðo ) ?@f€P˜Ù€Ø8€8æqqññññ ÍgÃÖË Ïlh"A–þÏ×}@5±E¬ ;ö®XÀ ÆV ˆ%ˆÄb Ö – – Š * –`±€ 0°‚X‚X@,ázU ^,àû¯ïûî{k×ú­å5Ü=“ÙsÎÙçÌ0ƒz0…êà ÐÐMÐÍ`æh–h…Öh‹vhèˆNèŒ.è KtCwô@OXÁ½Ð}Ðýa[ €âéPƒ0C`{ Å0 ÇŒÄ(8À£1c1ãá L€+&b&c dpÃT¸c¦cf³à…Ù˜ƒ¹˜oø`>|± ±‹!‡–`)–aVB¬BVc Öb”Äza6b6C…`„`+¶!Û†pì@vbv#QˆÆž â×iÏØ‡ýP#q‡qGq qˆÇq$àNâN#I8ƒ³8 HF Rq±‚ø5NÚ3.ã ®" 鸆븛¸…ÛÈÀdá.îá>@‹là#>á3Jðÿà+¾á;~  åø 10W@ETBeÀUPÕP5PƨÔFÔE=˜Â õaŽhˆFhŒ&hŠf°@s´D+´F´E;´GtD'tFt…%º¡;z '¬`Þ胾è‡þ°-@Š„Á;Øc(†a8F`$àˆÑpÂŒÅ8Œ‡3\0®˜ˆI˜Œ)Á SáŽé˜™ð€'fÁ ³1s1ÞðÁ|øbbCŽ%XŠeXŽX ü± X5X‹uP"ë„ ؈ÍP![‚­Ø†PlG±؉]ØHD!{°û° F,âãŽââãHÀ œÄ)œFÎà,Îá<. )HÅEhp —qW‘†t\ÃuÜÀ-ÜF2qY¸‹{¸Ð"‘ƒGxŒ'xŠ\ä!Ïñ/ñ :à/¼Æßxƒ·x‡Bá=Šññ %ø‚PНø†ïø2”ã'D‘]Q •aCTAUTG Ô„ŒQ &¨:¨‹z0…êà ÐÐMÐ hŽh‰Vh6h‹vhèˆNèŒ.è KtCwô„¬Ñ ½Ñ}Ñýa[ €1ƒ1¤’xâíC1#0£àGŒ†Æ`,Æa<œá‚ pÅDLÂdL¦ÂÓ030ðÄ,xa6æ`.æÁ>˜_,À",†~X‚¥X†åX•PÀ«€ÕXƒµX%„ ؈MØ ‚±!ØŠmÅv„!;؅݈B4ö {±ûqjÄâ á0Žà(ŽU¿ÅK{Æq$à$Ná4‘„38‹s8 HF Rq\Âe\ÁU¤á®ãnân#™¸ƒ,ÜÅ=ÜÇh+‰W~Óž‘ƒGxŒ§ÈEž!Ïñ/ñ :à/¼Æß•ÄUYÚ3Þ¡ExøˆOøŒ|Á?(ÅW|Ãwü@ÊñbÂ\Q •aˆ*¨Šj¨Ž¨ #£LPuPõ` 3Ô‡9 £ š¢,Ð-ЭÐmÐíÐÐÐ]`‰nèŽè +X£WeqÛí}Ñýa[ €1ƒa{ Å0 ÇŒÄ(8À£á„1‹qg¸`\1 “12¸ajeñ~ZÚ3¦cfž˜/ÌÆÌÅ|± ±‹!‡–`)–a9V`%ðÇ*`5Ö`”Äza6b6C…`lA¶bB±aÇìÄ.ìF$¢=ˆÁ^ìÃ~€±8ˆC8Œ#8ŠcˆÇq$àNâN#I8ƒ³8‡ó¸€d¤ ¡Á%\©,VÄiËHÇ5\Ç ÜÄ-ÜF2qY¸‹{¸Ð"9x„Çx‚§ÈEž!Ïñ/ñ :à/¼Æßxƒ·(DÞ£ðŸð%•Å#ªiÏ(ÅW|Ãwü@Êñbñ«"*¡2 `ˆ*¨Šj¨Ž¨ #£LPuPõ`Šú0G4D#4F4E3X 9Z %Z¡µxÕííÐÐ Ñ]a‰nèŽè +X£z£ú¢úö€„Á;Øc(†a8F`$FÁŽ 'ŒÁXŒÃx¸`\1“0S ƒ¦ÂÓ030ðÄ,xa6æb¼áƒùðÅ,Ä",†~X‚¥X†åX•PÀX5X‹uP"ë„ ؈MØ ‚±Å@Ü…@{Æ6„" áØìÄ.ìF$¢=ˆÁ^ìÃ~€±8ˆC8‚£8†8Äã8p'qÊ@Ü E{FÎà,Îá<. )¸ .á2®à*ÒŽkâÕK´gÜÄ-ÜF2qY¸‹{x-²ñ9x„Çx‚§ÈEž!Ïñ/ñ :à/ü7x‹w(DÞ£ðŸð%ø‚PНø†ïørü„XÄ®€Š¨„Ê0€!ª *ª¡:j &Œ`ŒZ0AmÔE=˜Â õaŽhˆFhŒ&hŠf°@s´@K´Bk´A;´GtD'tFt…%º¡;z '¬`^è>èk(ÉN[†-@Š„Á;Øc(†a8F`$FÁŽ 'ŒÅ8Œ‡3\0®˜ˆI˜Œ)Á SáŽi˜Ž˜ Ì‚fcæb¼áƒùðÅ,Ä",†~X‚¥X†åX ü± X5X‹uP"ë„ ؈MØ ‚±[± ¡ØŽ0„c"°»°‘ˆB4ö {±ûq±8ˆC8Œ#8ŠcˆC<Ž#'p§p‰HœÅ9\@2RŠ‹Ðà.ã ®" 鸆븛¸…ÛÈ@&²p÷p E6"ðOð¹ÈÃ3äã9^à%t(À_x¿ñoñ…(Â{ã>â>£_ðJñ ßñe(ÇOˆ RP•P0DTE5TG Ô„jÁµQuQ¦UÄ]´g˜£¢£ š¢,Ð-Ð ­ÑmÑíÑÑ Ñ]a‰nèŽè +X£ú /ú¡?l`‹b a0†ÀöŠaމQpÄh8a ÆbÆÃ.˜WLÄ$LÆÈà†©pÇ4ÌÀLxÀ³à…Ù˜ƒ¹˜oø`>|± ±‹!‡–b–cVB¬BVc Öb”Äza6bT®"î flÆVlC(¶# áØìÄ.ìF$¢=UÄ+ïiËØP#q‡qGq qˆÇq$àNâN#gpçpŒ¤â"4¸„˸‚«HC:®á:nà&n#™¸ƒ,ÜÅ=ÜÇh‘‡ÈÁ#<ÆÌÑ ÑÑMaæh–h…Öhƒ¶h‡ö耎è„Îè‚®°D7tGXÁ½Ð}ÐýÐ6°ÅH1ƒ0C`{ Å0ŒÀHŒ‚1Nƒ±‡ñp† &À1 “12L…;¦a:f`&<à‰YðÂlÌÁ\̃7|0¾X€…X 9ü°K± ˱+¡€?V!«±k±Jb=6`#6a3TÆ„`+¶!Û†pì@vbv#ÑØSUÌÑ ÑMÐÍ`æh–h…Öhƒ¶h‡ö耎è„Îè‚®è†îèž°‚5z¡7ú /úU¿ùD{†-@Š„Á{ Å0 ÇŒÄ(8À£«‰Ç'Ñž1ã0ÎpÁ¸b"&c dpÃT¸c¦cfž˜qoÏlÌÁ\̃7|à‹XˆEX 9ü°K± ˱+¡€?V!«±k¡D Ö#°›°*c B°ÛŠíC8v »°‘ˆBt5qçí{±ûqjÄâ á0Žà(Ž!Ç‘€8‰S8D$á ÎâÎã’‘‚T\¬&C{Æe\EÒq ×q7q ·‘LÜAîâîã´ÈÆC<Âc<ÁSä"ϪI~ý÷ÉߣFÊï{QÄ=#âÞq†¸WBÜÓàQáßûÂ~_wOû}[\o6ªøï5\q-U\ó”ÿ¾Ž¨þ}]Nûû:—¸%®‰k<âZŒ¸f¢ø} "î÷š~îïur±ž-֕ŰX«•ÿ^ûTÿ^KÔþ^Ÿëhb½K¬K‰õ#±Î£ø½v÷{-"÷÷Ü^̳Å|XÌ[ÅüRþ{¾¦þ=ÒþžSˆÚ_Ô袖v¨òo}ªø]ïÅý®Ÿr×¢6c¸kŘ(ÿ=Ψ÷ÛÚßý è¯Dß"ú‡ßmJñûûwÊ`Z˜°ƒ r¨ F ´(„A b ð€aˆCt(ƒiMbÂ2È¡‚)ТFÄ„àˆ èŒÄ“j‰iLLØA9TP#Z 1ax@($":”ÁÔ„˜°ƒ r¨ F ´(„Amb ð€QHDt(ƒibÂ2È¡‚)Тu‰ +8ÀJDÕw°:”Á´1aäPAhQSb Îð†QHDt(ƒ©1aäPAhQƒúÄ„ œá %¢ˆ èPSsbÂ2È¡‚)Т hð3¼¡D‘Ê`Ú˜°ƒ r¨ n(ž ML¨1agxC‰($":”Á´11aäPAhQ£&Ä„ œá %¢ˆ èPӦĄdC5R‹5#&là o(…Dd@‡2˜ZvAÔHC.J`Ôœ˜°3¼¡D‘Ê`Ú‚˜°ƒ r¨‡4ä¢F-‰ 8ÃJD!С ¦­ÄSïˆ äCÒ‹µ&&là o(…Dd@‡2˜¶!&ì ƒaˆCrQÒFü–1agxC‰($":”Á´1a(†8¤!%0jOLØÀÞP" ‰È€e0í@L8À „!iÈE Œ:6p†7”ÅoûС ¦hÃp€CÒ‹u&&là o(…Dd@‡24éBL8À „!iÈE Œº6p†7”ˆB"2 ƒ%1ax@0Ä! ¹(Q7bÂÎð†QHD aИ°‚< @â†\”À¨1agxC‰($B‹Bô$&¬à(†8¤!%0²"&là o(…hQkb ð€aˆCrQ£^Ä„ œá %ÔH…0èMLXÁP qHC.J`Ô‡˜°3¼¡‚ºø-2b¢}‰ +8À „!iÈE Œú6p†*¨‘- aП˜°‚< @â†\”ÀȆ˜° r¨ F ´(„-1ax@0Ä! ¹(ÑbÂ2È¡‚)ТRbBü©< ~«& qHC.J`46 ;È ‡ j¤@‹B "&¬à(†8¤!%0LLØA9TP#ZÂ`1ax@0Ä! ¹(ƒ©1aäPAhQ{b ð€aˆCt(ƒéPbÂ2È¡‚)ТÈ +8À „!С ¦Ã‰ ;È ‡ j¤@‹BŒ &¬à(†Dd@‡2˜Ž$&ì ƒ*¨‘- a0Š˜°‚< @‘Ê`ê@LØA9TP#ZÂÀ‘˜°‚< D‘Ê`:š˜°ƒ r¨ F ´(„1axC‰($":”Át 1aäPAhQƒ±Ä„œá %¢ˆŒ±â)Ä„é8bÂ2È¡‚)Т㉠8ÃJD!С ¦ÎÄ„dC5R E! \hð3¼¡D‘Ê`:˜°ƒ r¨ F ´(„‘+1agxC‰($":”Át"1aäPAhQ£IÄ„ œá %¢ˆ èPÓÉÄ„dC5R‹M!&là o(…Dd@‡2˜2iî ;È ‡ j¤!%0r#&là o(…Dd@‡2˜N%&ì ƒ*Ä! ¹(‘;1agxC‰($":”Át1aäCÒ‹M'&là o(…Dd@‡2˜Î &ì ƒaˆCrQ£™Ä„ œá %¢ˆ èPSbÂP qHC.J`äILØÀÞP" ‰È€e0EL8À „!iÈE Œ¼ˆ 8ÃJD!С ¦³iÃp€CÒ‹Í!&là o(…Dd@‡24™KL8À „!iÈE Œæ6p†7”ˆB"2 ƒ71ax@0Ä! ¹(‘1agxC‰($"…0˜OLXÁP qHC.J`äKLØÀÞP" ‰Ð¢ ˆ +8À „!iÈE Œ6p†7”ˆB ´(„Á"b ð€aˆCrQ£ÅÄ„ œá %ÔH…0Vp€CÒ‹ù6p†7TP#ZÂ` 1ax@0Ä! ¹(ÑRbÂÎC5R E! –Vp€CÒ‹-'&l ƒ*¨‘- a°‚˜°‚< @â†\”Àh%1aäPAhQ1ax@0Ä)ÄÓ‹‰‰ùÓ†aäPAhQƒUÄ„à‡4䢦ĄdC5R E! VVp€CÒ‹2˜®!&ì ƒ*¨‘- a°–˜°‚< @âÊ`ºŽ˜°ƒ r¨ F ´(„’˜°‚< @âÊ`HLØA9TP#ZÂ`=1ax@°õâíÄ„e0 "&ì ƒª ñDbB‹Bl &¬à(…Dd@‡2˜n$&ì ƒ*¨‘- a°‰˜°‚< D‘Ê`º™˜°ƒ r¨ F ´(„Š˜°‚¼¡D‘Ê`LLØA9TP#ZÂ` 1agxC‰($":”Á4„˜°ƒ r¨ F ´(„ÁVbÂÎð†QHDt(ƒé6bÂ2È¡‚)Т¡´aØÀÞP" ‰È€e0ÝNLØA9TP#ZÂ(Œ˜°3¼¡D‘Ê`NLØA9TP#Z”Àh1agxC‰($":”Á4‚˜°ƒ r¨ F rQ£Ä„ œá %¢ˆ èPÓ]Ä„dC5Ò‹í&&là o(…Dd@‡2˜FvAâ†\”À(Š˜°3¼¡D‘Ê`MLØA9‡4ä¢F{ˆ 8ÃJD!С ¦1Ä„dP" ‰È€e0ÝËÏÁ2È¡‚)Тû8axC‰($":”Át?1aäPAhQƒÄ„\àƒ@D# ™(@9ÌÔIØÃ ~F,R‘"ÆJ$Ma ø ÑHB& P³ƒÄ„=Üà‡`Ä"Ù(‚á!‰¤ lá"IÈDÊav˜˜°‡üŒX¤"E0>BLØÂ>D4’‰”Ãì(1a7ø!±HE6¾Àø1a ø ÑHB& P³8bÂnðC0b‘Š<|q<1a ø ÑHB& P³ãÄ„=Üà‡`Ä"yøãbÂ.ðA ¢‘„L f'ˆ {¸ÁÁˆG:òðÆ'‰ [¸ÀˆF2Q€r˜"&ìá?„#éÈß&&lá"IÈDÊa–HLØÃ þG<Ò‘‡/0N"&lá"IÈDÊav†˜°‡'üŽx¤#_`|–˜°… |ˆh$!(‡Ù9bžðG8⑎<|ñybÂ.ðA ¢‘„L f$k8ÂþG<Ò‘‡/0N&&lá"IÈDÊÑ4…˜p„'üŽx¤#_`œJLØÂ>D4’‰^$&¬áOø#ñHG¾ÀXCLØB<ÑÑO±JB&Š`x‰˜°†#<ápÄ#yøãËÄ„-\àƒ@D# Ù(‚ábÂŽð„?Âtäá Œ¯¶pTd£†iÄ„5á „#éÈç¶p‹Td£†×ˆ k8ÂþG<Ò‘‡/0¾NLØÂ>F,R‘"Þ &¬áOø#ñHG¾Àø&1a ø!±HE6Š`x‹˜°†#$&¬áOø#ñÈDÊa–CLØÃ ~F,R‘">"&¬áOø#IÈDÊaö˜˜°‡üŒX¤"E0|BLXÞðG4’‰”Ãì)1a7ø!±HE6Š`˜KLXÞD4’‰”Ã,˜°‡üŒX¤"E0|FLXÃ>D4’‰”Ã,Ÿ˜°‡üŒX¤"E0|NLXÃ>D4’‰”Ãì1a7ø!±HE6Š`ø’˜°… |ˆh$!(‡Ù+bÂnðC0b‘ŠlÁPG†-\àƒ@D#I÷ï³³ÄsÉÿþ½ñþnñï#<~ýyeøï3­1ÿ￉g[­ŽùßÿÇò?ÿÏWßmåpà?·úO|›ÿü}àþn÷Ÿ¿—üû\(ñÇ0ã1þ`º(¡„ýuoM]ò~ 6AÚ œ~ß_¶[p牗(ý}Y ôÂ(Lƒ6UOçd¾€›x†Ï¨V‰œ¡†A†…XHœ@:ž •%’†èŠ!˜¬Eâpñ È5:B gÌÁ*lÇa¤â>^£ u8æmÑcà‰ù}ÚyÜ1ü7w"?ÆU$’–èýû>´éX‚ÍØ‹$ÜB>JP½ªxcyÅp¸a‚…“¸†§øÃjI#X“0ë°ñ¸‚œjÿ>§L&Ö´Ä:”X;ë=b†“ß Œí‹õ ±æ Ö ÄÜ^ÌÇi—ÌkiÖb~I#1d^V$æ^b¾$æ8b^"æ¢þ5»¨³©yÍhLå¢u£¨õD}&j*Q‰ÚEÔ¢F`¼6¦Ñ}ã§óÄ8%Æ1ˆ>\ô»¢¯ýÛúgÉÿrO$AeÕÎOè¼×·Õ?©Ä}ç &ÿßt5kijþ'Êÿüãÿó'þçq†ÿ÷OÖÕç“KÅëBwîIúk¹T"-ž>j³ØÈ„ã zŸÐHb¬¬Ž¾ –Jö¹Ô{¹áÿÝèÿ˜}Òª«xücÍgö£fósýO*zxH%ã}‡ >$^Ïçð 1ÕW*Ñüí_Y<¦vMe“¶×ËwD\Ý®{•ŽoÑdò•AâñMB‡O—>Ó½‘ßH©äG×G²d5qn×o°r-ÿî’3z|ÀŸã¸Þö6X¼Ÿ8³uw毖J,œG<íóçÇ‹r@¥­=ÅóÊp?Ñ&€ÿqÊÍÕgÅs5×ìz£š$•,­tc³ƒxþã˜K¾Ÿr/¯Þz~¾TòÆ\[t †/>{[v(߳ƕ‰+¤’³¹¯÷i$Ï–§“ Mlù×Ú«¤’šältJ#±Œq2y›T’uçðßn[þüE‚útóZÈvGÅT÷d»«{F.šÎºõuµx¿Q¿“/R–’Èô™Ã’K%ÉFOOïn·vJW¾°ÔÐu•Ÿ¨|ú®‰öý§s‡K!RIÈ*—NFzž;yvÊå½¥â=W•®µZ0W* ¨ö¹”í—VéÕexΧOÏšÄqØ´búl©¤ûùÉBHtþÖžÇ~nâß§·ó]ÿçø¾žäÍD¢Oþ+m ?÷sç™KRIå–oÝ·‘h‹»g´äDqoܪêÉÕŽcm-]è¸_Õ}ÝQOÇÙ¬Å+޳Eݵ}åiâÇ5þy‰Ô÷Wž¥ õyŽm0wÒL¾Ç¼7»Ó~"ÓB‹<=b4–ãi¡0ÞhEûq}u{}òì~yèˆÖÑ>Z~šËñ5Ù™6|Ûϯübâ]Îk÷󈛜ŸW;¯YFž}·Ÿo!•ø.Û§üªçx¬š“(òüwß„átU÷«g±ýA¥Þîâ}¤'*N½ÄùëþóMƒtÔ!Úo/E²?»]<Ï÷ ¸ya†ŸgÍYp_Iž¦:·XÆyßQ9öÙV=Û5ìXC´g]`ä~nÁþ³C¼¥b5ágÁVþýrƒX§%œïk[ÄNeÔɱºÞ7š<Tt|WJ\§Ó6öyª?Ç×8T?¤y®woe– ûÙipÛ’±ìÿé‚ùOdz}XKú“œzæªÀ?ÇÉ™£;„ã,iµE9<ˆ8 ;-ê²îÏ?/Ñ„ýÊ33® þþlgó:Û}¢ŸìØáŸÞ3¤’vQ'ZôýÕ©e÷kÐ/KÈJiׯUî#½÷p\ß®ýêD>56¦dÏ믬:>ÞËùfõ~åFŽóÈéýÙßN…/FU'ÏÅÞÍ¥š]œ'æ5›ž ÿó~9“ÿÕ]äù‘—œqBÚþÒþ3´k¯]³#3vЮ®¿˜}ˆó*ßCaS®ïÏX|°yØ·}êñ}/í8Àç£,[OkAžÝŸ:•¾ÝÁy¯ÍO§íÞnÀsÙ/ñÞØ–ÁÍÂÉ£ônäÜ}´k¯Nç{¬ ÏҜՋ–q~:î0à8\­á]µôãQ…Á#Ë8o¯ÇÅWÿ¦§¿ˆ)êÔ©±Èó½à¸©â½¾×ã7Ó®g†š~–‰<§v¥(þülŸžB"¤d‹O_ò,53ÚZ‰ó"ÿUâðzÎ ‰ô¯<»÷ y Þ‡ÖÿÓöÇ^lÿv•;i×–«ë‡ÞçûiÂGÖ>Åñt?tlríúõ¢g]R£Ež×ÚÅþXt«°°7ýÌö®ÏNù‘ç|§SãÍ8¯-^\\܈ó-¢nÓ5ãd‚íû(÷(ΟÀ:†#þ¼_–•ғ׉÷ÞM·ÏÛ^7Æo/íÚI}£|˜ÈscûJ3EÁb{±yÆ‹C½–îf?Wû&¾ï{E«’5RÉ«ÝÌ·§{è4ÒŽó+bó‡Ü´°?o×W·¥—V¼7[z~ÀúçüsÎA÷Ù~in샢=×h±l%ÿÞè¾j'í:Â3¡`šxôÄÈGŸˆk2¥íñnzÆ#IÜŠ—Éâ½bÖNËÌÏ•˜šOJú{ߎx' «qÃCEÿÿ¤å®e›þ'òŸ)?pœ:uüëÇÙ²ƒÇŽð =ÛÕäÿÊs̸a»ûˆ<¯05XŒ‡ ´D»–”žÊ÷Û*Þ&ë6~0fUç.;EA<~RS‘çý“æ‹~F;eLÇ×ëòò7ؾż˧’9/:~W“v}sÚü6#ȳÅËÛ+jìáçw=1zן÷Ë2ýÊ''ò¬1»I*êžàÀîÅÔ=®ËÖ|ºÎþTvÞ©ãÅÙ¿Òe´Û×ó[úü½‹üg$çwäûJkgN[Éççä ã9¿êލse·T2,øóÜõ;þ¼Ý€N?9’giñ„ƒçi¯6/L»ÎÚ<ð³&DœGm­ë‹çŽ?q=@»Þ~mÝÓœÃâýˆò÷݉¼ßã°žñHúxí‘g{Û~ eüü•î?»J%‹Wžn¦$Ͼ[÷Ø­£ÿ×̨Pû„žþ¿S…™Ó—Šã<éáàIÔ³N5ùmÛ¨'ÏRÃY¿úíÈ•;Š÷¶?}9÷'íFZ·æhkÚµWó³§´âû}º¼lœè'gïûXÄùs©lAUê‹PûÏØ÷î©é8¾šƒ¯ý·5·OX—Šó2ûÙ&ê’êš>‰Gè?ÏÞ×¾m¹—ñùVYОÈ?ïWÖçnËÃÖr¼¿†Ú´¥º7¾uŸþ3f—ÛŠ2ñ^öH³ΫÎß»¿ZÃñj×½íêp÷}s¶%ð}Ýï6.ÆçÛßµ|G¼ß c×7£¥’ªÃ_ŒØùçíúJ7­ôbÿm«¼7'ùßíúÌöÍ»4˜ÓFu̧Hú€AMZ§ŠçÙÏßòæu^ç› £‰[jé|{†žþB2?Ò-j>ǹÁââÔ?MÚ·¡]Ù³¸Û]Q×Ȫ>›*ÞS­n£§ÿŸ³qòCŽsÂã¯1£Åóë5_ë©ÿ$šŽ¿òlñ¸ó™xñ^úçSÖ ÝH^uîÈrØÞØ¢|¿ƒ‰_?‰‰Ú6×[u9ŸÛi¶)×GŠ÷'¼HþL»9Òsdý÷bÿ÷{Šíg´]Æö- íçË9Ú96Nq'ÏÅ®ïî]&Ï–-C]Šúó~8'­¶ynXñÓs޳…ÅJ úϘá×î&ÏùǪ§, .ˆY.ßOÿí8ã«BÌÎ÷/)8Hÿbq:gg>¯ù|ìþ‘ä9!ñ]ë«ô# †O«FêéG469¿®çû¨»¾ý–Å“[×ÅûΘ,HÙÂvþ¼³Ça˜U݇œïÇFê*®$Ï];ÞDÜCåÉýõÔ’‚ç5k‹<çœRçgI½m}¦Ðo÷ŸF!ÆÛ©³†Ö ~ÈÏ(u¸©'ÏO›ìÝÌq¶|•н˜ã,øl‰Q°žíJÇüʳûQË9NW .Y‰…€Gç‡û0Ÿ0y¿Ü5’ï`˜e æOÊóy3ÄxØ)®Æ ÆC˃û¼O‰yiÞOƒ8Ÿ¼ìêþï{, mö•qêõ O±FbsªÿÉ8i^dßa?õÊ›^1Þ/÷W?L_Ãþ ?7–#Þ# U6b>#1è7"n*qº½ØÖQÌSR §%Ó˜‡û‡çèɳ¥c“dŽsH¼[±+íÿºîñ¥\}y–FþÊs€³IáEñÞá!KâEž[î©Dÿít>øÐv¾_þ°ÁÍE»ùþ±î{ú-ßEÖå«é'‹»èŽõb¤ eëßÑo\/Øüüý¹Å^›Þ'Åüeó¼!œOÍ>d§ÿl·xy­×äùúI`êÞ?ïWÈÓ=W‹÷‚ÛÕÚ.æíNã]ŸÒ›¸}¬86”ýé÷°Iý³&zûž¹ôÏ^w‡ܤ>³0²Ëžï0ÄäR Ÿwª9ún7êáCGÒ“ûÈûó{;“÷èÙn§¼qó™ÏIÚÍî)ê­Âß–‰zeSÃi›éG–ê: æ|÷m\°½;Ça˜WÆWjþ½RÅ€=¢¿MY«Ô7.ì:ÕðÚ\ò¼õʤ"Ï;Š”è¿[åjï‰õˆsµFPÏm«zÓPÏü¯ØÞ99‚úÈr虂&´ÿCGôy§g]@¢¹ô+ÏšjñŸg-óçz/rD}kfÚq1óVËš­w}ëC•§¿SÒn,‡>wk Çñ¸vG3úÉ„Ù?zÏ¥ÝZÞkÛ½§¨»¤ÇyÑŸÇÔµÙ[‹óÒ5Ë1ÚKô߃ûÕ?%ÖNw+•Dz'ï‹Ø÷çýrª’¹Ç.€Ÿ_yüKwQßWjºí)ã¤Iç#»ì˜Ï[¨Š$sè?²Ü6NÜ$ê¿”µvÁabþyÆk¢è_&çtûÌ8’°FmžÎøYÜb”IãEïÆ5Ü÷êéG²Æ†Ù­ï%W:Ñ^ÝϱèK½e¹¸ÏÛl1¯naó=`âׇ§’“ §™÷º÷ð;q¸1g‚wßÚýçøÊÛ.vâý²£ µôà{}è5mÿ4ÚSNL¸=ý°ÓO ‹¶ÔsU«©[¦'Ï1C¯½-ã8»;çŸYÃqίÞáàu}ó9é_ÿæùtÕCWÄ{ŒÏåX ëM®e7gœ¶°ü4ÐJäyo`½š´›¬§‡ŠõcGûí󌷯óȳגZÍ÷ç×W¶}8(æ7iwMBÙ¾å÷ÈF/Äzö…nYÅ´«ª›ßVÊ$Ï¥WÍ­»_Ïñ8šû$vq6ô¬zG¼¯°Ó¾ó§#òF{¤1Ï‹‰3°ý‡¥û—ÎŒÃgÃCªüCž¾ûºFÌKfe<ÅyÐ[m9Ùœ`¯ÈsÓyêO—JræÖ3—~Ø)ümÓòT§]âz=uûÙSYñ}ÈsÖXƒªÒþ5Á»ÎÑ3Ÿ“hjzýšW9îß7X.柕ºŠyLþˆ€öõé¿ó£Ãç0ŸÔ<¯¨0¥Ýd…åoœG¿•¾wß1¿q»pý çÝÒ£ï¿ìæø<ïíS@;7ÉÞ`Û_º´ù” |ž¹3±óL‰lA΃ƒRÉí´æç¬üy¿4ï7TÝÊ|^ú¥Â­JÔCš|é»KŒÓ–ŸJÏí¤þw×~þºˆq8 [³‰ÇļÑ8DF®yä â¼ ¸µ¦å>7éa7pM¬Fâú2äãh5ýmò‡ûuöýy»EÇÞÏ¡þ“>^To!ý²tMYZ¸§[= œÎ|Ë=3$Ñ|½è/¦wç|7/éa‘ErÀrÙòœ ½ßÉ*ZOüì³SòfÑn²­Û%Ö'¼‚Ûw¤ÿ.^¸õúÆ;É”¶¯uôûùãVÜÑS·[¸~ûá#ÆÍç;êPXÔma6GOû—H»ÿʳ´Žf}ÇÅlG— /óUŃžbªáÏø~ù‘^×ÍD}[Ï+ýyésÛð¦h?W=bE}èzɬA‘ÈóÞòÍÑ´ó—ÁBÙ~dî‰87¶oîµjÙ3êÒª_‡ß?D{¶¬»]Ož¥-lÕX)Þ7mÑH\’ý~F¼gO2çìpêÿM_«N§ÿ0råZ;ÆáâH㣡ôçîMnÇy=*ueieQÿy¬¯:‰ñÓë©çµô#Ng*ô™¥'Ïù®;åKE]ðnúºNkø¾¹Z0NkÚ•¯nÎ|ËݤéæZœïæ¿oÿ@žƒö® Í¡~ʺ[ñ•Џùk+¹¬/ÏB»ð$ÏÁïšnëõ/r¬#Ï1áÝß}ý`çJúƒˆÛÍûêYÏIè"ñ´ =ùîþR¡í|Bà²úò¬™ðož?vv°ˆí¸ mïÂ8¨©³õIeñ¾ÆÎ — |?ÉÐQ«Äußo‚ŸŠƒLŽfÓ~¤Î/g‹áûKBhGî/*ÔÛH=bãòâ+yŸX_ÌbÆu¼K¿t¢Éü~‡ÉóÓ—&¢}ýq·fÝ…yž¦×[“à QßÌð©I=¤1¯~Ĉú?aÕÖu±ä1¿ª¿¢Ùf‘Ï/6矤We×ä9«Ÿ¯¸¨?_8HÝéWƒÒ?!Ï1çÈõŒù¯öÿ<*ê‚qK×pÄæîÌŸ4;ïÖ ¤.èÚv1yN‰O‹å|¯ê|Z[…<‡L}¸þÉ>úñÕ#î<ÒSçÜÚ8t·ñíØ]Dž%6”YÍãzøÑsbÇÈõÎròœåÛª“‘¾ua7õ CÚ“IkÝ÷•äÙ·Ï¢yúÖg¤Š_yÖLþ¡^-Ö×M'>èÅ8¦í}†<ÇL¹wêuIŒÎe‚J¬3ªµ21ùçSc›XïÛ|èý‹—nsæ0Žo@×–“hçùž6é’k¿{-êÁ¶×ÍÎq¼{_«³ÙþÿÞdp±zއìÐ¥ ÔÿÒ¤ñ_О,—ÈVãô]Yuêé0¿S.ôU%Ç%ÖŒÃ!抛‰õË Ýs^»;w­/Ÿ[FV}D¿q`ýÖUŒÕç¶uKOž¥vuÇ÷çx[]þÚöê;dñµ¢«¸XROÔg¯Kü8ßÍ£z»,× ›¼ÿq5ËÈÆz?q[%?JÙ¨§Î“¬:<ùà Žó Åo½Ä¼°v¿ròœ0úEª'y+–윈yŸ‰Ë×ßõä9«™÷ýçóÀÅqn¿#k²¾õ;Çç !{"|ÅuÝE;®’g§!Û¿íù™ºwöZ1žÜÞnEÿØÛl@‡­ä9kÑœqh?&'¼“åô/Öäד‹ºûêþmâýçá.ßbÙþõ=^5dûÅcÃW«÷k$ǬmQHžý+v«ç¬'Ï’YîÖ¥þ×|k½-–ödñzEG©XŸJ®9á õBÀµð«äÑëöÓeä9Ê*í6ÚyȱeJÈói㟧ó¹É°ÞÛho¯º†m©ÇxñùœÉR}y®íöTÃx³ÕøU"yvÍYýÓUäùðÞwU9šÉùSÍ9ß]ÛK£ˆû’Le~³Ù×)¥ãPqÿ½7ôåyíͯ{§³ÿí†¾ß nXòuyê1“î—w¥±3h&ýþiÕêºVzÖgžù>á8÷>•0¢õ}OC¶è˳ôú¯<ì8š¡ö!?öw†×¥ÞI¸}øô¶E¢}^/q¢.ñ]úò`è&ñžÜŸgR‡H·ðÚÁ8Yµ¼C~º¸Þ·«_µŽÛÅu«íC?’ÿaÛ:Vbû¾Ó¢þfûšž=G´¢]-îÖ^6â(ñ[ý”´9¨çx‡ž¯ZWæóa+&öë°ÃÞšEž¾}䔞º àÑ©W›/bÖ.4ÿ¶V¬K-)øÀö5ÝÝw†2n[$œ‰_Hž‡™^1žÏù®³®u¯ë8ËEÄÑëgÉ(=õ¼Ä¼AdÏi‰äd½‰/DžÇwÙû™<»Ož¿R¬ÿÌlû8ƒ<ûÆ_.µÐ3wïý`” ýiLfå-[9ÎÅ/#gN×3žK4…ÿ¶ç»Õüê‰ù{ÿ^ëµâýͧÃw]$Ö÷®öèÊxåûtû½b´œÙù™XW¾øaÄúϬ̶пôNJn&ê÷÷îgÈBpÿle°6bû×Õï-«r¼g+GY£ýͼö|¾<7\ÛéÁBÄÒö«ñVÑžâº]¼*òƒ>çyÇ/ÎàsßÚMïÇQÇ>ˆÞmE]à<>ÞBO]°rr?úËýM&ïcüégZg¤¨»S\&™0Ï+–¾’á|ogÞL(æ%e×Jéo‡MÈŸP›¸Ç\}ÌéËs¥Âã^näyIùÉsho®X{QwkæzöoDÞJŸv[s—ñ=¨î½ýGôä9d÷¬=§Ìƒ3?/c<þ52W_ž¥ugÿjÏ—NϼNÓþê©îÔµ!A>©»Þ~fÏx¥énù–o²ÓN_ýW«b¿ô#ù›Uµ?‘çbñƒf‹û´äùŒ™ç™üm1.™<Z~ÍÜH̯j(âæÑßö^²ýö âzδø”®/ÏaË*-œÂñôu0l=—ó$S;q y¶\ŸÜ´˜¼Iêu«Ú–~|Z}»GúÖÕžT+Ã8õzYÄü¿9ÎKkäN¬¢o½UÓûß<;6uñý@¢ûlò,¼÷Ýyò,ÙoÒþ”¸¾ÚfK'ÆAõbÛtòl1cqÅ*לAJ±~ãû8`æQï5ò½Ûžü'”®Ô†²ý)–•UC"Äû¨O·^Ìñžç´Ûûyþ63!uÝ¡?ïWÀ? {šÌ'ÎÑ[yÿˆû ‹††ÜepçãòôŸ–wzu6 ÷ï¸îL]í¾¾høvꤪÊÑŠeäyó£'wMù<ä˧i‡ioáëÞ#ÏÛú4žV¨'ÏÒ–9O&1ïHø¸E Æß¬ZªEžCï,‹YÆùåv¿Ñw‘ç ¡O.‹~m‡ïäkŒ“ÇŽÞ¾¸Iýêv·OÏñ¾˜»Äl"yŽO ˜NžÝ72º;¦Iɰçbýüøë##Éóº÷ÏÌãô¬«i<¾.Kå8·;tΠ#u[ÄÎ)ÅWõåYêöož‹,>K} ­<ö®„ùKÖB‡©oEž«?yíîǸ0.ô¹X_Ú~°£„zÓ=>¾³ƒ¨KŠCö^!ϣ潑‹û¯º/èZ$î÷Ù2£‡Û¯÷©¹‹‚í;íí6º3çýu§›ÉÛâ¤Û–AV©úòÜzˆ¬Ú¾·ÿ‘Ó8Î1‰Jë1¿ ðhÕÜ–v•0¿¿÷ZòQ«~à¸qÝp|)ù÷5®õ³2ý‡{¸£ÿ QÿÏØ/ý5ÿ3‰Ù·úïÕâèzê?ÍØ5 ûPºOË<®uÖÎ!׈~¬Šß ÑΣÝ?´Yô/‘ßÄú`Þ·za⺴oÉ„†Äµ´îà¯/Ï­êWuæ{}Ù\S\ç·N¾´„óé_êt³Ïç8{+ª÷So{^ûožXjtîäs[Ó΃Dž?iZIÄu׸Ùm{ˆõ“ª N‰û–Úe>«Ð€<äÞ2O¬'t¥]Ó¿LI:k?y–ÚóàùÏYÕãÒV±ý¾w‡±}K›ðQÔÉ ]Zæ“çV“»>ÉÖ“gI£ýÁ³SŸG‹û7•çu÷ëî^u؃þ¥÷¦NK’Äzë²—ggÍÌ67Z1~:=yk5ÒG¶µ¾ù\£)WGˆë†–ŒÉ×m¢ÃºgéëgŠvþOI¦xý¡ØË²:b]}ÿƒ´ÃdŸ±Ö㉻kÖ™]!úòÜf·ÿ±·€o%Îâz Åi‹-ä9?Ò}|4y{ñyïSòüîçþ7Õô¬ŸZôV]µóÝçí•]¨ÛòW¨ ÍõÔçéáÙÿÖa·þÞ6™óxÛËNäÙ²ÒíUþ"Ïe­W¯àüÎò»d9<›\˜]-AäùæE¿ùÌ?ÌŸ·hj&îë벺z±žþªÎÞ¶Ôm¯w¾(Ü'®ÿYL:Ò^ôëûƒ|'ˆõJõ˜íÈóÊë Ì;¬g¿êxª:3ÏŒ‰»s+bµX¬ú\ä9{TòFÚUÀÃo/‘Ç̓礋ëSÚ“ëçЯ:%ξàHž{6›Uý&Ÿkn?\ÛŸ<Ôëaºây6¶lo²¾<ïêñö/Î/÷‘ëj¼%Ï1{#jD’gÉΟ«2ŽHR*­L¿V¼nÄÈýâþ¤aþ ¨ÿŽ m>lqÏN4Ÿ/Ñ3o“|ë:¹Ü‘ýO/SxŠö¬ºVÞXÌ£§h½„y̓žRe¸2}fÎI}ëäÓˬç8ç$húJÉs©ï¦ÞÇõåY“ùožÃ¬:ŽgûÃ*×J"ÏÅãwn÷)oÐùƒX­ÿvÀyqÝ "¸¯F܇˜¶áÉÁÅâ:°u?y~×-£JO1²zµ”z5káõl?yÈñ­}Äö¿_Õ0ž[,ôQÍŽ§.^¸s¥¾<·|Êy'ýäÕû™¸údµÏgÑo_,Ý;Šã­Y(¯\›º/aJû"ÏY#ë.¡>Op¯btLܧ5¿jH6yv|½|€RÔOª[Ý™Ï?kÐà–¾<—ÞßàÆ¼GúquÖ êéC·jh®áç/4–p,2ÕÏ—’çaî¼>)êÑÚ—^¥_“[Tv^¬·ý¾®4žª®‹ŸÌ24¸B¡”9<⊢Ìsu2&cºd&™n¢¤B¨u•„$I*I%K¥RÞµô~už/~Ï=§½÷Yÿ5î½ÖÚŒ‘áº$f+ÐÓû¤~àLÏäßµp&Öd\}zªlƒ…‘<øqžûx×= Á™zS˜+씯PB5ÐùÜεùwIåyêÎè7Maþg5À3Ý2/ó‰š Wô IžÓ€ót ël%âÌ“ÉÈ9ë9&[ 8ϸ®t”œ©,Ž¥/€Þ²§þ„[ÃüU_ü2×Ãüôê™$aÐçY¥k/Φ‡7ð%Ã9o£ß^kÀ³¡d?+ÐÙg³í#âü÷³‹Ä²Rš€s_êÍʇ¸¿9xü2èÛæ7É¿^ÎY+xÆ£ŸV±n© à0ôé à̌׬'ÛŸÙðûü•á?ˆñ1ÁªYóÓW8¶ƒÿÒñË+µpP(¼écv%ƒ^«êNâÿã¦E„™°‘á\³áˆè&A¼­Xrôßî‚NIÄÙmpè)ËïÞ"&€sA„ã*œ™>9îf`G¸z~‡¾qFU’ʳ˜ûÎnÏÞf뿾ÊjÅsõAÅÌWˆ³u@Z-ðÜÛ¬g¨·uÝk%PÎθ€ß0½&pîp–Šœé~_ç†ñ“€‡*Ìo&•8ѾIþ+ y8Çz·¾"ÃY_X¯ÔôEÍ)iÌkèϾ0àãl$¤$ÐØH]83åöÜáÇó“$ËpÔóÛ?+ÖÎT“‰:IxN7` ú-Z覺prÊ$‰çˆÇÌ@oWÈß´ÆóÇ÷ıHx?p¥Ã ø½—º)÷Y''ð#¿çä¥Ú_[žÙâÑ8kß}nI†sQ~¯ÖF&Aœ³]þä™Xž:‹yÔlOn{0‰°Ñ[uš˜?w¶å 2YþÜßÒ€. sPÓàÆg@ç²@ wI’8Œ êþÙVøôö*ŸÝ®(Ïœ2n=ˆsë ãuÐ{c)<ž¾O[†ç€[YCô7ößk`ü ç'½xÞMœ©H‘Û ã¬åÝóÇýþ!Ž|öÐ¥åÓDùo àÜwûÅ®çd8kð5jÈCü¤»¿‡3ó_Çj÷ÎDß·å™›@O®w.Óù¥š™‹0bKËÙÍ`ŸM#¾¾œ‹ªÇ¸Wà¾Íбö`¸\'}çñg[–“áÌ«V„qV¸ÄδAQ)¬_"ÜNtõ¨¿ïËã«Eœ\oq#ÎÖ2Þ‚ÀÚKvþœÃ¶lô0%Ùâ& ú‘ÿ“¨8È3³RÕ,åÙØbÁ°ÈG‹j¶àܼd a-i~ëa­m@OãñY ³bØíed83ÿáÌgq^ô\°¯uyÈÏ6« ˆsó‡÷ïÔ ž*⧨!Î,§eûðûšmŽ7m8ïÁõô!_C6\Éqv³l36¹ÉLCœåÚY!Î}åT +*ÞÇÉx‡ò|«4/‹ çŽWÍûå`œ5ýիΔºñßBˆsÙFöÝÚà¯GgÓöá¾LæzÇw¨·ƒ²WP©„èž¾~nðïSN‡oÁs÷AKægs*±³6”ƒ8³qª ’éí”ÔÍÖ@? ˜sžÃº%­Z˜Ÿàºwó†*Ø¡ŒÎqÄÙ”}ò1æÅ[ú%‡ï»RÎj»ø‡ö=þ”$ÎÄ{y!°'MKYŒg:7½w+â<Áû·hø3?×[{#Îoyß“ùa¦R@ç(ñeëÔò€Nú©Ê}¤ö9á΄Õ×ÿ´1ŸsºãÖ•zÿqŽhó{a ñ‹üÊó˜¿K9±ú¡0ÊsõÅí,»aü3ex^á®ñá(êíß7Ñ@HöšÎ†c1'pþòñç)>T¢QSlààl\~äÎ2œ#¿|ÎУ=Ïk,:÷_ å²EœƒŸ˜|~QlÔ4Ü8û,n`]ˆò|«1ü­¬“ÈÕ|òÜ?\ ŠòÜpXé Ðs•Ü/΀3#JÛD„Lž}ÊJÍ ? žØœ} ·³~€ù‰#¹D€Ó¢g´0Þ”õ¦>;ÆRAß+§u¥Oª?ÿ°-V06&Ãyâr¬/ðó‰dâbÄùs ûQÔÛœI '*Á½õÕþ€sÇÏÊL2½í&àwèÜgfÝ=•‹õ•³ß„Iå¹ôÎé†ý&ð]ìšèo3v¹­èo³/ ~¾âf—!ŸÛ€sý»^ÄyÅ‚;^TbyWÉchŸÕÙ; <‹–—øÂw¿öû&ó˶…Gδ)úù3`g¿?nõö@?,qSÕC2œ›ß±„õ <šF›xw(p'â,Ùw`+Ä›Ì4ÆOô·m^Ŭˆ³ª®‰¿æ­8k^œ§C5l– ýv× >þñµ©ª€³†Ÿ4?Î&´¿ðëüÒÃsŠ+«Åú#±.ÃრÐAÑæOpæµrÔøýAyv=cúïjeZ:Æk=g¢¬¶’álœt õÝ”ò÷Öº›¸ÎTûÝþàOíÿ–±q~»œgfÇ®ô»¾˜·zø6Ð9Hùê$/©}~ögƒÒc@WÓô¦€€3ÑÔÃô@y^æßJ„ñº$:[g¦Ð·GÙÀÇÔ¬]œw9ž®¯J†ñÝjwq b\5Y“ñë%.Žu0¿¨ÂMiÌw KŽZ\ºÈÇ[ÝC½­ÒGµ!Ãy²ä° è Qm‘aÜ'qxÞ„y%M›ÃMA/Ä•ïr@û[” &^…qåš°ž»ÜË,ªœmS­Æ¼b ד‚ˆWæ9!}À™"^ß|™,®^·óµ¦íöç™b]¯s±aR$ŒßÌâûø«(xR3q6<šŒû$ö_xþ+ýqòC·côõã‰|’ïjµ¿í ú™®á>ŒqU´ÊS.À™YœÀèû^7åª8g]v W>c`—:O1‘ûž¯\-Fû̲Î@ôÏ[ënŠÎ²]”l2ûìéàçëãeÙ¶.¿·>/<ÞgKéÙû×ÿ.wÀº5¾ƒa(ÏrïÝqßlç‘£0®~PÁà@>ÉønêSv€JùbsqÞ¨,óø‡žÌÖA§\/­=Äç~~·àRûù)è1³¿Ôã,à<ñ°>#–Ô>S<æp¾4™`³øWø@úÔÛlÉ«ç,—5Õ~ ?"kùqŸäEê†XôÃҾФ`]•ÇΊ ¼Š*s±büÜ+&bt±õß·Ýpfn‹™Fœ}|ëG¤]^}IçÞ5ëVœé»ÍãÞĉõNa¸¿}í”F9î‡ÕȾñ>ö©©WËMO'ÿEœ™Ûwt…Q ¡’X9À9¥q`lg›•­‰ð¼XOË8§ó‡ÂIq™U]†çH-6ã9†MgÿÎËÛ8&AOʶ÷»u'Á¼7CêgÆÒ¡o…‰TÂQ¢¢ã.ï¶ñ]>Éø[À¼›ßæl×ĸ*ä|$âÜoZ$û&ŠJø¹Ú)µ2`üŒ-³d83¸"ü®Áº¨/F”g~—1¾8Ry6ø‡óŒ»¨=æ[¤g¼Åó*Á<Ç _¸è(çÑ/‚’0ž þLÿƒçD¡vàæ÷Úqdcž‘£ÝI<¯’³²-90>næ) 8+ªTŸÿ—¿±Y¸ø ¸Ü—á 8Om:½‹I¶¿½c€1èømØpoîü÷ÔR”çV½ `§¹¢-.`Þ8µÅf¿%ʳ»Vçcx/©d'æ5ô¨<­‡çU®·;}@¿o7|sÈp6îÉè"ÍWºØ™ZvÁ´70*pîW’¹±èI/aµ”}a« o8S½^1 ß·"OGüû0‹£Œ[ «ÌOÏ'ù.JW)ø‘ã‡+??£ƒùÁ˜øÑ²ò!е1Zí\àlûá‰Á$É9e¿öSA˜Ÿk µ«è\e ÀB*Ïÿp>z ä‡Y(?\8÷§l{Ï«¶›ßyŒçüÚÕ™h—N¬hŒB}•¯²Kë`OºøG?‹0Ý"8³Þ­Ó‡õêïÎtÁ|67î÷l€s‡¦p<¾wg}ªávÀٲᴗÎ ‚ÂX2Q·ºA ý°„¯£Ãp?y—Zè‘é÷½ßÌGÙå Yñ¼êrï…×Àw]<#笨ªÏï@L,’ ë€ÿçèÝÁ£ 8ktŽMV’œK¿„úK0_ŠïäÀÙÔ?òÎ}œ¿vmÝm KÕå A'ÀYQÿk4ÐÙô]ŒÖ‘ ¿åÌ Œ;Mãß$O2>—Ãa´^1×›°ÿ‰ÞSav<—|òý^3Ö•Ø“8ƽ9ãJ‚3ÃÇÃòÐYƒH’Ïœ{$d¦£IÏ«Ræp¦gxóÕ#_[çY¢<N½?„8+kê8}i¼‡%„gºÈBº;â|ñHw üÕfÙ³ðsËÏãÅó*Óe1úu4 p.:[µû0Rëö Àÿ'kÉŠ=¼ òÍç±–gæ·"P®ªšÛ?aÞÎöâ°®Ù› öžýúÌ#hÑÚ1xý‘cÑ/çƒ Ó /Ô7q›íÞò“ál.s*ÏÆ“ù%k‚ò<<6‚ý#޲}*†ï˜¸UsU—}Xrö‘àL<Ýûë×ZW ÎAôÙ§¤çÏ7ÿɳ]Þçµ0oYÖaA¬[£÷5n’À‰Ïÿ µ·™ì„qûke£á¾ný]Ï?xÞ¾hÓN7Ìß»>§ãU•áVX¯„%³ÖIÿ3.{p.{¬ÁëUï=ÄŽyCGìζ‘Ʊ^ýoA®&Æ“îù©9 ­?±oÆçç¾Ê@ÿ°{ãðÎÄ4øìAÌr_ü=4}äwðmk¸],Ö}ûzø‹å‚GâbÀÙô,£b;YÞëfÖãðý\¯”wEFaÞ”ê OÄÙÈÚ ûô/ا²ënunµ `žÜ"çпð}âz' €Û‡<"Ë'I²0 Øã¦W¥¤c?¯‹ödÎŒòôš˜ï¶7ðD~ürTv?IœÔÌçªzl¹ m™)|uâ/©<¿ü‡s»–Ï>Г”… ÄuјøcýM|O7îGe¹Ÿhƾœ·Â÷õ·xò#ÿ•šžÂü¯¼÷­ÏRÄ8¦Á^ͬŸªÙ87ûG,{Œqµm…Ú|È¡âUà÷k§³¹Ép¾’þm9ø\S²˜OrXÀFp¦ˆì)­€ù‹|F•dP/GI¿už áÒ¿Áú*Gü§ÎAK×ßźþ+¥+‘¿¬Ýu³A؆µyœ%«8.{p;ÄÆ¿5½%£ðüõ¬Ú+¬¿¹¬"vð¦}Ý:u÷÷ýžvZྨǯÙ.À£kÉ–“0n­É¡I‚3=çh'Ä´~¡¥ˆ3ãõá^°—)ÛË\¶ýØd?óæa¾÷V2œ3iþ’ðýÓWh -@g/QCÒü°žs8Oî{¼ô¤Ï¥žÀ¯Ø?J¶[’ó=­,"­ÁîQ¾KzaÝƒŽ°ëmø>êvÖ7èçNñŸä¿ã˾lø‘þíÀ œÿÏI­7°NÑüìiýL¬[½ôŒrëÆÖ=X8·6…. ó{kô7†cœ”-Ø:rDµ²,öúç¬l¨˜?ý1Ky÷Gòs÷àþ=µÿøÃ¸o“í¸ pž‘.;Žçgÿº›÷)YÎU,›ý($8á®,`|ÈξÁúùÀÃñA˜oªªã– tx)ga 8SÙW&¶a=×j÷L§UKµa\þ ïÚdy½ÝkË¥€_Ý8Mëb\u‘Ã㊃ƭ§`ÝIOZ‚‹0ß³RYÍį¢Žq¨›½ãîòA½9º[µŽ4{õ?œµ¯Ђ¸²M±ЗH¿×Ôâ‡ç€ÑÖ™À×¶ÑÜ)µð}La§k¸¿ß±-ëÒ] £±Çì4/Æ1¥*¯wãy¯sP“æÉ_x´S p.[v§œÈÄý)±#»‹g—?Ä5ä «M†s=Íà è•”ûƒºâÇ0¾[Nœ“öË`Qv_©G`~†pŒ­5àË”_æpôKJêÝÌl˜âÈcb'<’–Sϧc?µ®‚þ(:µ¿}€,{•ÕäŒß̦öa]?͈}ñV´-îu˜Ï]ŸÄ{øŒ!)^…þxÙô8Óp óšùÒ|ýÖº\œéƒÄ¬é~Ì—ÛW"Žy½û, –Δ²3ú¿±¾´ò¯ê@îûDù‰“àœ’^# ßi¦üæЙVù3'4Ûø·vÞÇú‹û#ÿÅaÜ^˜¾ýΣN#€«ìÕ½üµð}ýwÖ_{ß§¨Pšf ¸Îø[?SÌÄúöŒ®^̳ýÃÑñ ø²Êü‹:¬³L„¥Ûþ?…»Ï~=àê¤}Ä"ënªÖv’ùC1ñB€kYFçà8Ò}öV£7î»SFâ~‹žCc[ žX½9‰ñ,mçŒ>àÚ!ùƒ½ ÖÅ_%:ÙƒõWµ³Ï€þc¦wà~•ìÂ[{Ip¦½ú­±‰Žu;&g #á½”¯?J±ÿÎÑ+Çà» F‰•¡âóìïOàSû{Ó12Û³‹õÕÂ~×õdòqgx!àÚÿíõŒž_ä{ñÄ9_~ä’âz5³e>¹ú¾þ~»Y=†Èõ÷®@÷E«¥ÞÉÆ}S«¶7du7Lß8«½SÚ„ñrTEB7ú_R1#A¾é){S6Àw>ï©þˆyžv<£èŸ˜¯gï~Rž~Œ~öˆÎ¬S,2(ùÖ8o°ÒûÁý —¡g \Üyú0+jXús¨O¸î™þy9& ú“_þïÐ4ü%NŠ÷Û`Àíá{ ý<c)}¬¯j¬aXa¿ÅçÅÓ ˜_”™ªˆù‡sNKàþÉ@P ð“õÉ{Gx/â~ŽÌ‘Øüùç¥ ý ŽÂý71±NÀ™ù×~Ÿž{W}ü©~¬Gyä öK;¯ +t ºäœy vA¨%ßÎ+ó€ô¦¦IêèˆÔâÙë®ØßÄåžîƒÅZQ¾©ÚÓèß,NÍ–î³x¾=²Ì™ÄÞRǶVÉ}3öŒøc>míUËqÒzÉÓs8Ó…÷Žk ;Ó}÷`<5Ò‘?€ý}öZû8Æãþõfu[Œ§Âû¬±ï½oäÈ+ÀW¨nxž`wgÒä)eá)Îçˆïé—'ñ܈ú_’Ý´#É¿h" ÇÝìÛÌÅç²ö\®ß$rżl2y äØTˆõ·âÌ?Ä‚ý+D„ÿ;‰uÖÛfß _OÝ$”昀ñlÝ£8Ü ýZüF”íƒç=Wøô|ß­?—F%cÜS®_ùšÄO¢Ù?ºñ­;:pýlwª˜.æ±T<Í5ƒïò ød8Ó§ÿst=Îø>¨Ï|k3oê³Á_­ýýkÒs¦ÍÑ­¸ÿÕùzYÖÅòzë|uºQ¹v˜aäÎõYê€3Óà˜Y ]Vcr¬}¸\Èãçë·Ië%kçp¦~lø¹ìrÿ­‡+ ÿ%ôJ•Žýh¢/,ÜtÔ XÌ00ŽØ•q« ëËXÖßuDÑRi(¯·¾Å/œ‹^ØZu£þù™*ºp¦Äñ®Æý š¶³³Øeûu'8].aÞЇ«1dvR:R¬ä(%®ïEæç'ìКDÿðaž·&Ì#ú¾îô+ô³Ã~â~AOÞÇL3¬³N|Ó¡ŠñVÌU%gx”·–ÝæÛó­bÿHžn¿x„gJD¸ù2ø¾F»¼îè–Te„aÿ*¾óØ÷Æöi‡êÌ'ªÉø úÖþ·d|‡±sPsŽ[ÏòW‘ìœéºŽV=èkJ ›¬"âs§?Ê èpëÌaÜ?œŽû}ì-Uéy; ΢yÞþŽ˜—·'îû̇£ÝU¹MVçNø§·ïý·ã)Úî“ÏÑÿ:¾°~Ìþ²™-ÅxÊö0c)ö­ Ø« d¹ [¾ÂøËoÛ‰ûwEÒƒœù˜/A~¤K‰±óã:µ»”Ý-ÅÍÞ…Ÿí÷vv¹1ÈWJRÇè`þüë¢KU8cuf‘N2ö;fQˆŽýhÎÍ:@\]vIh§-ÐÍ-綤毛·rŠÀw2?ú¦ ÞEa=óZdŸ2èË¡ÝãëïÀ÷2ŸzÚ b\mØÊS ôŸrt–]xJœ ßá´ô(U"ÑûY?9¾ ˜ï©±½¦ ûòv‹îãûRƽÏV¾«Š[Ñà%ö£iÜ}ôMÏJϼ3ðݲé’^sû¡¶Ì~xîY—Oûvcçk_ÃüíË Å… o^Þ}À®dŸµ°O‘ñÐ=¬ËflÑÑÂxoS¥jЧ£äöQ ìï}ÒrDŸ›¾Zúä׉Ò"ët¬vH‘øK´¯%©ê¸2Îó2ýý8Ée—Ï:mŽF=ÑcgWD?ŽñsÛ ÷ë’ß9_=4y-AûKé/ñW$‰‹˜ŸŒÚš€®Œ8“ÏKñüHjØ÷Û^Ð{O™À÷ŒoÂ5À¯üFë8HäÔÇeuìE˜/I?BÝûËé®}ÆAÚwÈ|gªÒ¶^ŒS)>k°ÿtuì±åÀwÔ5J6Ê©zÈ©$Œ+OWbýwœý^SÐ]¬œŒ—èÿZtxL‚üл˜|…ù ÙvSÖày¿éëMwáß7¯9`¦_ rÅ|Uè€ýâjÌVóàÌÐ]RA‹ÄóÞ¨Ù:ŒëÅ×Ë¿»¢‘üX,ãôVÅñ˜¿ã2¶Cã=nUðK¨Ó ®PÐ~;ͬ˜ÆóÃÍ+Õ¿ÎZµÞTS #-¼¼–,¾£/ŽþíÔù5VVè^/9Šý⺬BM`Ü¢'ºWêa]Ä•ˆ»ª®Û-Üs ónþ“0nÿÕ~3$þ2óÊçK‡`]¬e,ŽNðÞwŠ×ðwB6Êb~XXå•— ŸŒ·3WÈ'0—Õ)Жa{Ö¦)&•×ÔIûˆÍáLðn^ûÈcW…µÁ>"£œ¸?wºeZþ}Ïý”bø>&‹ú‡åÇ0ÿà®áOÀY;í1SëÙ Ûº@~Êbåjšò°Ë“Z#Üg¾< ôI.ò‰ä-£_…ÿ„æc½Ã"ýv²~E÷:^Ã|)üKoa? îâqQÀÙÇÚºjÐahhC˜àH3Ž 1ÇúF}¦Ü_Ày¢yãá†$¬ºóôyméUí›W©ßtÍxĸppk2É9m8Ž~gýð[^ô“]Ùç.bx[¹øµˆúghô s×…½ ïçØÃâþ^Â}ÈžëùXL9ÆÍE¶yxW¢?î3Ó%úcÁ>œ_i»ç¨ÚJ KÑïÝšo0ÿí¨ÌBùÔ?Ѻû*ðØo*A~8³ŽCŠäž‚™3‡3%ÎÑ ÷ýˆÌÊËZ˜§fý£¨ëBÆß‡Ö޲lx|Ö¾¯*`¥xð¯­jvÊ£¥£Úû|î׺é ó3cïi­Àþ“kìýtϨ]yç$à<Üzq?Öƒ+f_aÛHâŸ*ž]Ñúô9Æ{?¾Ë‚=)³hŠF~ïá[×ñýßoi‹ à½þ‚=ͨ/‡E}žÓ×÷+qŸVzH@9ü!N¿IÌ›+IÚ@–טּÈW[ù‡æuÀ íØ/Ù}¸<±è`Ës¡äúiÉ~;^¶Ûuê‹°Iß;Ì»¤•·Mv“øQôøò4w#†géçØçó}¤Ü ðÛuÖ{_Ïœg‰ŒûUî_ '‘ÏJaÖÆ ³Ð’ĤPô?y¬‹cHúüÔûs83ƒŒb~`§4K<ç'¤ò¼ÿhbö…•MÛKÅ~×Äc¥È]Àç‚=Ï{ðü3@éû-ܯ0äÐ<ç#ÞFSX÷7ã²(óíšïa¿‡Õ7^Q®ƒž¡lXû ä=åk£Ø"¹ò‘É6Z…rj_h]Žûsõç}˜7 ¾Ïù[©Òoÿb<wÅ›nà¿”P“ëXN±ûÑ€ù[¯«y¢±>ã|K>ø3׊AÀ—Š¥#Š$ûMü[¿ä b]hÓx~w¿w¼ûçÑ“²qŸ­kQÖsÌKüàX¨ vN»KKã øyT›YîVcSº–¦‰ÿD‘ÑÁ¸U¹àº"èk"«û¢ÈÍ–ð]V`ÏÜ\–*þLž^±¿Hú³vìýå‹õÚ«f¤—âþà‰F k’¾ÝsèŸÞþ»ì9î , öžÇ}9¹3íàf\TØóÒ”.nðÅçâ™oÀ/1t7¹‹yâ/5?íÇü@§óîØŸeybŒj ö_nÿþ®' qÞ´t/ÐÝiý}•BÀÙtÊy±\îóÚ?É'ë«#mU[‡}c¶PÐÿ_4½pü—f·K1Fèoõ^öÃ~1DOM’.ð_•èÚü àþv×CexÞÒn­±ž³íŽ: ~ejy—Ƨ¡¸HâSÚµŸ¦Ï0o&úq>ö_"Šƒ…8q_8†hCóж,*Öcg]÷x |0-DÓŠáïÐû­ ÇÌ`Ù·î$v•y—Ï«p&ò?>»„ýÖkÞ8î¶ñ/×3‡ç*Žfñ@Ìo\)¤ÑI‚[Ñ“Y姨ÅžjaŸêš ¤dýÖ©|^sþvï_Ñ8àSêW‰«ÇÛN:á>qµ‡öõ«ø/S ä˜.ìPöÊ0wùì³°*tbî©Ý¼‹}Nݪ{pßÉçutÚ‰¹þß i€îq ·Mó–p-½õèÍÞi~ˆ„ÞýÜS-اŸæ*À‹þÂy–=6 gŠ>öÝzŠû«ùç‡*p?ãôTgðŸbMpñ2WÊž« xÂÒÇ ý±ngÙ­#2`/¦¶É©°_16(®YL"o>  ¼ð\*µf{îmý*uå¯rVÿžw.ìHƾÍô—YÎ5x~ªv½fp–]k†ç³ô—Þÿ5è[Êvÿ ìÏöÆŠ?䘰Ìd1°¢#÷ ìƒ8¤l´ì¨)æ× ~´ ¹?¡È^ItS>ð½ÒÅ—’¨OÒõ¯ÝŸÀT™Ã™v˜ÿļç¾Þï1¼…m•®#ø ý"Mò©Øï#ïÄ9vÌ´,Ód‚½ìèßû èç"Ú”~Æ›ÅÍß]»¢¤G6Àüô¶Oa˜gÄüoŸ´êW—ïc{çs³O ¢¿T”¹!™„Þ)ߨc?ÍþÐç›–ã}$…:ªÒn˜GúT7k®~ öbš½ŽZü—2¦†ù°ÌW¢w ã9º’Ü |Ö™F/ÀY;ö ëZà+š[ù%'y£[Lžþ8÷K:Œ®š;·;úLü¦æÉˆkm¸¿ûÇ`7+Æó«–,öǺ³xÃ@¬Ó;]òóèk¬¶Ô’è[ E&ææ_{]¹rL$§†[Ÿ'å×`ÝuéÖ¿xïÅ¥o‹6’Ü“´nkóäI s•b öñ[ÚªÕDv ÕîÎâûñ°ZØŸã’ûˆÛ&xEG ¾õ58B¢oéVSèo¼n}g rL¨îø!c qJÃȫ݅xþ:òƒÏÅ—äþG‚Û À=‰ XOä÷2ÏÏZdߨ“ÜoFP æpJc]Œù`ĪŢèv?nÜö#C çš îcÇnåüƒë«°{¾ôXò²’ÈjÀÙ§­,éê wMèjûú½,Öõ§¼¸Å˜³ŸÓ.þ¡w­ƒ•+¨DØÂ8½§o©<ý›$ ¦¨§ Ÿ~ñ«ÅýÁæ/_³ÿŠõÅú¼²»R´ô‡ßŸ¼Ýt×þjÔ‚çô´J ƒÛèGHÞd˜"ÎGDÖ‚ÿ—•øÐ÷úÿÁ'sŸÜ¿#¼Tãï]Âw°*w¿âÊú‚3xÎþß17}¤Ch° ðÁºï¯—•c~c»'kæ5¥IzØIÓ¥w”Ô@0;-2>â„ýJrÞÆà×ìŸõ¾Rˆõ>·ê1Î`Æ4QÇI. Ìe4=tFÿ\I¡ûDÓŸ¸3 Ÿÿ}‚ùpgQUê7ÕÖÞýƒ!ѨW vGâ¹î. Þ·GL ¯¯Áz{Ï´ À¹Ã¾bT+åE·UäJ?÷T•žG™¾È{Œû†ts†:ØõДø:_À9Nˆ*òTgôé ßY…¤õ"Μ’_ËP¿p_ŸÛ |¶êS櫯xþóؤöù¶ƒ\…­âQ7ÿŸ¹V/Þ÷¯ý žÁóéøú’ý€³Ë}Õïè_Ñzÿk'óWÞ~§¢Þu»½Èóº_r‚uÿx<òû˜šºH(ãþ×É% çG«ø!>g0ýˆÇquv È!eùv%_´û—7¤ž|‰å§ë ©„zãÅ¡/€3å€êCܧȱàh›œòÈ£÷ €Îü/¶ô‡à>üOªòÃ`’ÏáÌïÿ%ó¡|kl/[Âû3åâÁnØ*?|‰ûjÛ¯Fâw÷©ýßA¾>äß ÆýØ”ÚÌ’ØÏdËî0aX?máEÖèO ¢ß¨á½¿Úu’@ß«í~_p©ýóKG1o!ªDþ!‰ßÀ`{.†úkUîÕ:°DBo¦³-ر Ù_〳"ÅÍV÷g9>îwûYµ“/;÷߬ãyÐOþÏuJžUÌø8Ç h… aß‘‚`ö¬ÈùçZ­±¢qÞÑû ùëøWE7Ðoný!«!6ïÑ®B;¢¼7Gè #éd³pV\y©ÑÆe^Ô~¿”ä¾Ç•Ð]ˆ³ááXÍmðÞ½S‚7õ¨D”ˆPdž‹¸ßÃ˸Šûp³=aóÃ}]zëK sÇ2 ïèòžXÊ0ÿûSÈqöqOî­Gy¾P Û¼ þ†iW;¨„êxB'Ç\ÿ ý3Æ¢ti|×BŽH}À™Q¬ïŒþÊ…´ ^¬[yß%’ ó3ŸO¨0pã•›ÅEˆ‹ÊÞÄnìœù¥íV¿ˆD½ø$‘•D/MûǶì@“Åsd™5þ }ØlöýUÃRYŒƒ{=‚»‘ï¢ô¶‹ƒ^ËtÚòå9fÚf'ÚÕ;b2à¹Óõ™jÀ¹ÿÅžü>œ/1”¸D"'¶[Yöc¿z¹G*Æ·‹y€_œÔ·r·ú)Ñ÷öQà÷*:+ø©ÛZØÏ_Ãý±|#eŒ«[–·M‘àC¤¤éR®òf3üí¸Þ½Gèysáè^ìsÒÈÉ…rC<´{)æG2Î -ðÏi>õ¢KÐ_¸:6ªâ3ÿûUkg†¾ø×—H‡Ã6<´­ðwËBà³®¬›«ÑOmü:So¿‹Róoݽnªy휙wwþZŠy0Í]¯r€V+”Öb¾”sŒä&´o/nÝÛúOaïßõ“x^š~7ý's¯‹iGæ_ר¹øÜý«b)cRÈwcGžÞôÞþzÀôÌ\^Ðp”· éÈÐç3›o»à=hÔØ²•…Æ}“I€çÙKÏ–=œc²w >—ãµU><ÿ¼üE¯(¡ßP}ù‡úIÁÅè`Gƒ¥ÚZÌÕç‰m­B»vfû`×&|ÏÛ·ÎôÏÖÔ·Hoã×uïHèÍ|åÏ3ŒøI×i*iÂß(Ñ5Ô ~ælÄü¦_=Q‹¼0çôŽ=8ÿ8ªI¶%2˜e}~÷¤ó#é„^×ùß'˜{ÿɳèÓâÄÙ§²u›ü2Ûi®za8] ã\ÖñÕ¾Hï›g+Þ#?tÊ|Pžž•A¾?žZÁ|¿(û„qàLÙÝÕ‹þœÄƒQUàA>öJÌݳó8uZçòÕçùO±n -q~”A5Ùƒ$†–ê^û…xçμ­GAÓïá¹=¡úep¦Û6)Tã½Ë#?õ—íœ^úÇ–(GĹHïøíuð7HŒv@üìÛâÕÞˆs÷Š¥²›IÞgÏá\6áºcgþS¥W1á;6ÈKQ˜Uç‡üÎÖ’rùêðûa­ãZ*ð{xêÙ9œÏ~Øë«¿WgGQ™„£öàÎ9œÝ”FoÅߥ¶ÞÙÂ$rÙÖèÍá¬oÕ¾Ø~'òî˜3ç]VGb,÷Îç®åÈÃ{>}f«™„l”·ÀÎ%,nû7ÁïW7åþÐ`4B8hgicªéfü}Ãwx·º¦zç|§Ÿ¥ð»€]sµéüóÒÃlDçpî¿’IU„÷†Ôh9k™DÖ¿9œC6—Uƒß§-Ü¢Ì$–»wNüBœÇÀqbovÌ?~ÿÄ©3s8»Y;M á{Íû 0‰™„Úá ˆóØ}CøÝ8½þ‡>É8­7Îá,\>Ggcqa¤ó¼ÿQ;æp¦ÇþÚ=§·=¯¿®Ò‚÷ãø·U®¢—íoÌå¯ôiÍ~Ø¿ï\Ͳ ¾£L>7pNoû«] r€ß ¼5ml€®¯:ùçô¶l®ò^ø=Þ<*žI„°Yþd«ÄúÚŒånØ_ï\¦J©ûüë*k©Y4§·WœLÀõ i×Eè2 †€.Óõ¶h¼w‹%ü.ueM±“0 ,ú"‡zûñ¤D”-ü~ Ù¯ ž'TdšÓÛîžð{beqÏùçM±½ï?§·ywqoƒ÷´«|Ò§vxÎéí€eGwÂï áCFL­ò—êmê–FG/ì«ÇÅë?´þñ™™…çôv‘ÁñÇ ðEÜÆ\ŽIè¿^Šz›®û«ù öéþZ§À¹gþq<×>{;§·[ßæ K{úìçŸ`~ûgŸUì¬æê‰D¼n0€÷—ÑC€¾\ïCë× V82k¿/ÝÐ#òôê)±ý0Ï`WWø]äÐ9Vø>!ýjúÄyÍìr_¼Çǧ?7Ëø>øÇÖ¯à‡1ª<¶_ fÌÒä{æ_#3WÁqÞ'ÿK éÚ¿Eñ2ÐUCò·µúa«›¾Ñà÷¸Ö野LÂgì5âüÖäý,Ò9çÅߟð¼ÄÜøÜsôÄÓKF€ÚšèwÄþy;®ØÐU硯[Qß(Z×S.|מ¡ô´“䣖T +&Q9«x­ý°‡&»Vã=#Ñƛ}æ¿_uíìgĹ[¡Ö ôÑ|F°Hè!´ìD4â̶gü·Î°l(Ömþq¤KÔxe1Õ¨'ð%Й>®ýá‹ËüïÔe>sþö—"Ë&Ä9óÇöeH×¼#Ô‡ƒy1Ÿo_öÁßÈg/w{Ÿ ½ŠtØŽ}²×}úã¿s¸ºi|Ú>|Õ„ñ;qr|&ïÝxFih9Ä$øˆ «€sQ”Wn=öK”ÞÔÃ>ÿº(|r¹˜×ñã¦Â'”[¶3öå ·=Ôë‹”æêM¼–¢üˆ'2_®4±ïßC\E×\s ûÆ2Xî ÏkÞÚ{0甥áOïƒßKvE½™ÞÍP>Œ«|Wϲ¡|(…­f¾6M3_ªçÿµ)H‡öÉW2»™ÄȱµãWg¦-\4ŒKW¸<ê{„仜ž;=Åø™¿eKØ9"¨ûU¤6È“ÔnS·‹˜w¿Õí0ÐG’»`‚„_ÔYyñ^®QÝO@gº¿Ýˆ©×üïLêδg£.#ζ$,ÑŽÍ„7ê½Ñ}ù ó°£"üE®õ5®^@W¶­¬ÅØGq\®ãð13w‡ú]XW”jð0æ5S; mÏâýg?Øá¹“×FÙ›¸æÈëÐãHìJW>>ÿºøÙuUðÞWfÙ¦¦^¤wìwŽl ·íŸªT¼g…¦ì›"ßE¿kïülÛ[°¤÷IBlLñ»÷Ë.þºÉ]ðƒû$×…ž»DÁ8ÔcâòóÏËø¦<Ñ‹û$öën¢¾˜®â¡€¾è°aÿ)†û$Ëù7I¸Âï”kq9Nà/X9Î>¿×…s#ÿ¾¦~½FBïÃ¥¬˜7AçúA-@½™²…Gô&3Óã"÷IN¼¯}Ge˜O’è;jÑ7<¯îz²›=Þß9¼~/‰ž"¨®s8Ó[9.b?zyZÎ;°³„í¦È¯€73±U³ûDœìc& }sÕ06ŒeÁ¼ºOuÃÙx?§Ë½DàÙ§¶Y ÞÞ•¡øï^À5` ëh[m»4p.ã*UK;û×Aød,ɺv¤=þ‚ûˆê‚_²Þ‡U–Vƒ *ÕëÜ økÛ±ž÷ƒñ×]½¿ì}Õ³Õµ.ا8ƒÑOìë{%h=<×19ýöÈuÌç™äÔŒ‡y-„ ù£çŸWñ·äÌ)ÌÐ˺ì ãø¥ilùÕ(Ûm5‚ùË‹v /ˆœçÏ]™D-gËb¥kدØóÑ—òÞ$2›„è-Ê»£Qo+U¬¿·Þ+úc»•ILÄÕ§ð &›°è è"ÕÜ-Dï06,{_sû}U4á}_¿K ›IèÉLœÃ9ËoG‹1ʳ‚ÁÊ'À¿ÄºåÝŸïþD®%¡X×R¤¶õ° jàíúib-öMñÉÉýt ïã9¾ø<àérÉG柠>O~‰÷A$þx·iOέ2ÆØRa]Ž©ºIó¯K¶ðÝeÜßl{Éô&v±\̼ébµ_|qßÓœ7?í-1“}ðì cœê+º#Q¾eŸ¼CÛ.-Öœ§ V{'ÃûÏnŽŸÞžå‰ZxŽÁÜá6 ö‘~ýÛ‰R˜ßXwk{)àœÒ 6t õéá…¶N€÷U•×ÍOñ¼j$~‹ÞÇ£g*NÂGô·2Ûñ#U·Cýsî]²oõ”I™…€ÛÌ‘!ÔsÔ’¼ ¯ŽÍ?‡ÐoCÎEÜÞ;×#_=>ÕrœLP¯Ïál¨^¦2w¿ÌTì³)”ŸÔï+ï¾Èk±S²Üƒ- ¯Ì„ïÆ­Øw~ÕÎÌXÀ¹9cµL ê+íß`{uƒØ¦| ƒCû9Þg1Ý)íx·Y÷ÿýYñµ·G.W“à‰\z.eþuù´÷¶cµì£Êôûѽ8'×°„ë6Ä¿ñG~÷³U}*OÌ?oóñkVMx.©¾Ûô>!–hix÷|jHÂz„²òwå¨]KgÄ€ëG^iïœi"[zÇa\fIÿû³Ûç_Ýd %ï_+?òÕ ýÕ¶Å;¥ï¡k~ÕO0ÏÀóž8ê ;Fðúìµgî±×Ãȯ” ˜ÇM|øŒw>•à7ueÀz‰^“¬w°Þ£GŽ©Ÿœ+ÍÄŒ^d‚½Ëb ¦Ï¿.ÓÍ9NƒÑØ?ìtÊŒ¿|–¬÷ve†½ÈÀ>F«äÁ£?uÛx¦¬)»6{è}NëS˧/¼zFÁÿˆåeÀÙ1´8Á ù+ïo"_êüóR«uL 0}÷ï”Û…lã ·C1ûÂxŠFrÜ«ÑÏ:•àwôG¦AÊ ì|ìtÖ—’dA¢§(—¹Ì®`>IæÍ«x‡7Lðëo~TÓæÂz¿ÇzM h–»£IôBZ§Pg'ÐÙÖ^|ݰ •ô-$þAùgŸÍ‹«†ñ|«ïô¸)Þ·L4î/Ø¥æŽ+—‚Ó°Îo¬4èIÿµ%’òCù|4¾â~µlûo°ž~¥«ñ€§é‚ub0?S:bvïßû¯žOgQVlœ©<^2JxÏžPÿ ýSó¯Ë4ˆåëQ̧xhàÃÀû ^ cÁøÁÚâ¥dÖ½T¬<ükm¼_õ•—æs¦(œ½Ÿƒ÷«}TS8 Ï;¾žòuœ‹‡rÆMð¿2f#á¯2Õ‚ûðüò´»ÚÿNùíÒ`ÿ{”®òaýãˆà.ôGT—ç€~K2òpµœã¬ý~vâ=}Wr5$¡÷· [¬w9ª»æ=úAƒGûÖBÈZQžÁ$|A0%}çä¹~ý>g¬÷ÝdÄóÕÞï;w äZ´¿j¿âœ'èâ‰~•XÝmc¼çé‹–ŒyÞ|T§ÖCMg‘_ úC±Æãn îŸ8Å¿L€ß93ŠžÄþïªá¹QÆóxOtoÏizïíͤÎT–×wÞã¾Ê>ÚK1«‰Í㋪O=¬®„~¹¥ošŽö¹¦4$ºÎœù_Ôݸ¾—™v¢~íIU‡¿à¬xÈ+hsÐcKÂݳóÏ«(ÍvÜ û7\þ(Ò‡ú™×0ëÞ¿¶r™ç\Þ¦’íh/¾¹Ÿ²¹Îè®ï/¦CWÓ˜ú0.U}e”‰¾`rù¹ò"Î7¬ìjp?‚‘8õä:®]nö¿˜ˆR 8 ~á*ª½ aþq|’*åó>mòΉðÞ—˜:2¿–ºmgêÁæ=˜0©gƒñyݽžÇàtôÉÝx†÷53~{ô ñbR¥ø™¿Ó½¯üž¬Gq'€Ÿ(9y^(×—K[p?”±Qòbð%…û*ar]à ?u pžÊHWÍael5 §³HÖž~ó]¼Ÿ6F^}úß('‡ÁÿcþxÞ´ï;lÿ¡© rÛ?*/÷Þ§ÜJ=‹ù)¦—bàwûÐxNÙ®û:£ü¿¥ÙÎÀº¯÷ûC¢GèÃ;{0ÿAôÁ&¼/yßî ×GÔú—a¾çÄ7wÐÔÿ\?~ïoþÊë‚ùžÔ;u0®Or…–6ÿøLŽÎww|0ßá~eî#ºç¶°Á¸ÈâÞG.»¡ÝË=ãÑÚ»‰óãÆûbÅs ³áö¦}àPg›ìŸ‘øÓ󟲑 ]è¤ÆDÑð^«:ÿ˜'WSœLC{ñzGî1xnøº¬àI èí@iÍsðïyo­M$Ñ#´Äª ìÓ¡§¾ ý‘¨úVë”:ÁrÌ,_tôuŮʨ`¼O‡åsýeðS;bNÁ¸>ï ⯒Øú›Q{Ì8|Év|7¼ç"ß6m ô¸ÝÕêçö;×W·>èûØyƒÄ_,›=òÖ è|W {±;ðIÙÄ6’xÄ”ã>{7ÐÙX8͆qjŠØ‰Vÿ`Þú§·¹Y¤°ŽNÕ@^í¡J¯´8È5¿£rÀF¬7yɰ»ô¤šLª+a\·"%óèI[š20¬§Ÿ7ìà Ð7®[Xªûº¬`ú‹ª4Æ{ìOÖ»5|€se±ÍÁÃùðþ”Ù¾\’u©H|I%ˆå¹å ?™*QÒÒ ×>ö£ÏÖc=¡ÿSGà+J„AÿnÛiA§?x_Vr½i-|/½àV<Ïj¿qMÅ:÷=o6dÞþˆÛ[æêèų£?ÒÆqî4Øá‰©F¬£S$ʼC»/üypìXËúSDö}-s ÆM9êöb}AmUp>ìã»W Fàþé²T)ƒÝ§‹¢Îüßšž‚ŸMï:HbçÏhëhb\û‘½ðúU7ØÖ’ùÔWÿô¶Bë{cÌç뺽Î㉀ƶrð{RÆÃyÃ÷¹åg4D£ß~|¢# ø™&¼ö¯8àÌæÜÐÝ…÷²Ù§§Ä}Süh±@ŸÛƲ K_ò{ößWƒç†4‡µ`'v&]0½üú»±q/cþuñ¿ÈÌ6œù}9Þ:ÓÕ¢ú£½¯jˆÅúçWò§Ãq_¢îmß9ð LŒšhàýwÒW¯úµwáó¢ÞóÉÏÀny:õÊôSö†ŠQÎüó*þ÷b¼ó‰w =Ùz‹^jpì'èï”oz,²É`=»T!¿3Õ¿;Kk»é]?bG›ókÐOý7„e‚Iüú§b£(¬‹_ðÅÌ ÞûìQ½x/ðã3ƒ«©hï4,}ZQl×Bbç‰Öí~ OžÂBÊ‘@gÓÄkãIäŸ`²øÍÕKZ½œŒ}M¶þBœm–^~~ÏDäÐåÓð}¶Á¦”íxßu=! ü<±i•ñ>°‡BŸÉ†ßQ;埀þÖ ù§Nƒ>/Ò£ù¿ù} +®uÃü 3…%ÛÁNÆH_$XëŠpe9?ÿºúŸËY=Âú¸dû‡Ù¸k{Ädpvº'ù;Jˆºq-=úƒ®ýÞÝ?ø†[è@ö¿±Zqe3ð5å†äd(<ï»øgf)àÌr¶¸î+â¼™ñ$Ñ#Ócò1X7}ÓÉä•I±Œû„~ýß »Tbš×§ñ0ò{¼ëö0à÷AÅŠ5˜@Kû˵ ïÅÌ×ó$± Ô%ñò5Xç^iaÉ÷F LŠ®ý]¤¼ÜûoO/0›¸ñý‰}Ê$vžKïcößvNl,Ü ò¯xÀ{åzù'¨²s83ã$±nŸîkÙÐÿï`÷s°ÓŠ7Ÿ¹c×aÚn¼?ÒòA~àÌøx3Âì¡þò5Àw´5!wp_qÕŒr?ı†ibâ£x¯1·Þø˜Ë·Þõ`'W¥¯?!Uú=ð­€çŽIñ^iÄ9¤û÷ ŒW¿gÖô‰¶Ó¤ÎͳRŒàÿÑùÅŒ žàgæcÇPÛ§ÃhGfþã ‚çBf7±Î¶‚ÃÁ^0žÈ›^~±¬,ŒÛ£}õ¿â}¹…Ïvá}wÉ«~oM;\²¢+ øZÏüO é¡KïÀ>æîY0n‡~•÷ÿ’x&—pÍûÃ¥7-Âýóê U·gÇd±3ÁX÷Ï­žýô>íå+ÃO$8‹ŒFyšI×#ìAþŸ?yB†3ÓdgjÌà9[Ì“cÏ^:‰ûJ-g€öa7Òÿ˜¸i¿ÈqTØŸÞ½Uæ>ܼôäT…ƒ|¬§ÿlDê0 kÐçn ¹?Ö_úÔÄì>hëü‚÷Îäö´¹\:íZ¥‘š?ÿºLïuÓ¾m^9–!èÈô<Åý£ëç5!žçzïþð$èÚþäˆ8´Ûdè`·§#§¯{§â¾!¥`ìÛ´Ú›`??Üο zDöªË3$öb¢=ÑDë4šC\p~áŒÞ°Ó)_¾YáþIÜÎÂ70~ÜÌWõï/ÝÆ”¼qï"¾_"u8.û½&‰ý§'Iªºã}·¢î:¸5Eµ=vzèbÍreÐÆì)~Û@0Xîߘ"‰ó³NEg}Æ~SÆ…y@gþÁùz2=Oõÿ¿Þ~ù˜û7m|RXú‘¾¦x6ÕõâÁè½ð}nÏGö¤‚Ü0×î z«£á'ö—š*ûý ÖÃàö¨Æ}¿žµ²§±¿a~õ’0ðeǘ² ð¡Çó WòìV Ì/Á{~N2Yæ_WÿªÚ%áà÷r‰º‡â{W¹@ŽûuÍõ—jäÛ×”Œça üûÀ›Z_ßov[”M#| |oÿÔþxEpk:€ýT}f¤pR8ô>šDPLÜ–cÿ‘n«%_°ZK”V\ö±Èâ­¬V‰ì_ëèàwÙå¾Ñ™¶g_>ãv8lÈ&³ÿT¯Á XÇÃ}h¥+¬_R÷æ)°ÓLó9aXObW"kŽqþK»Ô™óSUìÄç vÓ²ê}òiÜ?Kím!‘‚™õOž//_»6pÞölZ ô#ýþ—û² ¿©»íEñûâ&åVÜPó_fD€ÞâßõÛó ÈO—$ãcêçíZËJgŸÌC-XI‰m¿}ýN“SÓÂð˜Ž7‹s};¨‚¡·°_s…‰›ÌOÿ°J3 æï1-š¹ïŠ7Ï/Šògø: ÎL)3=Œç;ŠJTñŒxØfvš¢À›þ˜ÑØÍèß Ì¬5Å}£Ïâ[/a\ú>Uàkº›­êrx>±Ò7Ùìg÷ýô*΋0ïÛÓC$8SÏÚ½O@¿@G0€ ýêÁˆxcÜWÕ?1¬ ñ–h§GÛÀ™Þ8‚èô‹Ãõ3øOµc¸)-©¯ÆHp¦_hªÅþ;\vcÞFuûàlºfŸÀY¼GÚïÓcÐût3¥Û$ñY¿yS·&È“çÔí ³©ýósÉpf¾ûç‡%}liÇóï¤ÛS5¯*ñ«³€¦t<äÃûïšÆsú©ˆ£xŽB7u©ÙõuÅkÆ÷£XÝ¢› 8û¼:tŒëŒYW}ðF¿sÿøy¡Sxò]ý‹x¯ ãzùe ?ñzfG! =¯n‘ž]?%TÕð\êÖ×.ð‡½Ö'÷‚_ØÑ4­ô p¤ØŸp^v˜ú«"Éû.P¾Ê.€ïM±öʀ碙[1ï‚ë"—ÅoÀ¹êײ›â~ÛÇÆ €sÑqªN!øOƒ£®ªÂ¸)œ¿µž‘øyôú²+±O\Cç­½¸e,# âŒ÷ˆÓHc_Ö(Ñà @ïÓ '«’ÄgADйlíʣ߲ñß·Ú'ÛŸ¡.ôŸ“gá3§tpý§oÛ~Ì÷±dð×#η¼’Ï_’uïχzÐÌÇJgÆžCg,±¯ºó`Êoà;ª¢á7s#ŠÅ›%´“|Ë Õ@ÿ(®i\9ó÷]Øtü§¶©Ìbûž³¯.’¬ËN/öø…e‰ñ³Hç­÷¥– ?Öè¶gü6×)Î`<T§:€?Æ8;éÚr^u2üú9äë%¹½zð\ñ?ë©ÍùTBùäê%`/4ÚbkŸ‘àLWIVëÇþÒüNxo}ÚSmðÇ(㻤šÀngÙi쿌üN·¿Žù#Œ—[£ñäÝj+Z`\šËâ t?n%µÊq?ØG3ãxþvk|ÇBðǨ‚AšØçšVe«üqŽoâ"‰ÏRŠTnœ:_Mn‹» töIðÍ=@¢ç æú9œé+•°>G±}穹}‰Q×u;0q˱¾OCØè’èG†HξX\G};î÷*îÏ5rœégیߢ`$Ó= úÓ‘¦æu ç§ÕwŸBy¿’¤€ý£z¿œmMŸá\YØ~!ÕA¯:qÞüT¾óDÎ|å0Á¹tû`Æí̃¬+ÀßbÊÝìÁû¯¾åÂsæÂK<÷Ñ_´^½ôª_ñÝZ6°E»†‡Ø "bó&°²×¿~.Â< b¬pfîr»’ rpERÏá.¿_°"äÈ!*ë*ØUnÝÓÑK`\Ÿ' yIü’gQŸ­kYD¢ç ªåÎDÔ¸”»öéL5ÙˆñÄ5Ûc€s¡Ì.ø¾éþGåwð¼Â¬z¡ ú!øÆì@~´_Òþ|†ñ¹V$‹ˆ ¾u3¦tyÔØGÎQû·TßcÔ¿õù\â~Ñã;9GˆÉÛS<'þC–/¿É9fú‡ Ù}œ‡ÇO}±«6íiL«SN³SwõoÛÁWÎñSÿ¶ç:•›qþ0 ™çƒ×.íõôŽ˜Ç¹òV“PŽW#šn¹ã`ÈçËÓŽˆœ7Ÿãá¼:kwm÷=G.Wì±ZÌsî_ù"ù‡ÿÞ¾&ÛF~̤î Ä~p W¥|lïI/ï¼÷cy³y²¯œ—¬ì÷ˆñBŠa–fÛ9¶O“B«È9¥ÛÁ58NÆOw÷´süÕ-û¿Šë3ÊnÅÄ÷>–¨X8&§è¿•zØ‚ŸÇÖ¯Òæ$ííæý‰Ñ]Œ8}öù_í¹XòÀ(ޱ#:5=AÎ馹åªÍ×Å Ü/9‹<Ïì97Žý=äe©j§É9nuë,Œ‡Šyn f¼Q·VƒÙ }µçâ9^ŠïCIé69ÿ1O¦èزaj꺊çÔ¾¨ŸéêŽïé+-éÓ|ä•'ÓÉÆô§ —Ý\¸Žíœü!üÝ!Ÿíyóßœeão^ßo”}B³.‘¢Ij!æW“­Šë=N™+á8˜Rºxæ±bÜÝø`Þl×”lO×å¥Iο/"ßqçxiùw Û5H+r^#+ý~­¸Ÿ™øµÛ{’mTì9rØ–-¼Ú.ïkY¶*K8ޤ÷³ q‹ãc«‘ñ#w§¼-ÔGôŸó².þ.Æ÷;ÆŸu/óRKŒ~KþólW»&ç¶Ê’ü<ýÑÔ uĺø™3bŽ‘óÆ,K{™|ŒÿâÛ—_¸IÆó~šDïK;¸ÕP&óF‹¯žôžœý®Ç7ióO»Õˆã{—º;·óq½%¨ÆÝ«Óé¿66©þ$Çóô¸¤ØÑ¾îçÇ'üÍ9÷ЗF¤ø5œã«~sEä<.hcñ=—“®µóܶ~è'î?F]mðu"ãðþ¯iDÎ9ò½N#rŽìtnŽxþÍØ³[v“sjFõ¶½Ä¸ź~WÙÞÝT®)_É9bŒôÀZ_9ßzt½Ç‘Èû“®gí©ÈÌk¯È96‘M¬÷Ÿn:Zœñ@ÌÜ.1®Žš\÷UãŬ“Wm]LΑc×,+/®Óå[¦)Àø¦Õà¶/ÉùxÅ+]½¾r®ÔUñ„~$GÉÄ µÅý‘éφù1=Íý+R<‡òÐ䀥âúÁ˜ºwÄùÕäuÛqœtW°ºñs‹õä+çá;µ½Ù>ŽÃ§ˆœ;WjÔó« ^?Εe|±½üÔ’½ÉùÞå–3ÏûÈ9lÏ˯Å÷…tþÝ©99g<ñ¾ç+ç°7sÎl¾Öq`T¾áG<âºýïó…þ;îq.ºýXï¦ß§ß!çô§z¼ çø-š¿×Z7®sb˜çhí'ΣãWνü{ êÓ×ïÝ´`>qw±zÞ—â{¢çÌ·é÷óÿ׸u«œ£v<êþtãÛ£Ëô%çðI‡Wó8ÊÜ¢£_]F?Ã?ÌWŠó蠂ݯ“ÿ¤îþû~’sÆÌg9Åytx¶©«¦­óO3^ gü·ëº½t³×½qþãYú‘]f¾÷Á¢Ë,Σ£nÓW׊ç‚ËC7-ãÂÑ—Åyt\Äï¥T©³``~ê–²l¤¯œsÜîní)¾§ªbÍLb~̤ëÅytldÑûâ{Ô㟼*¿‡œÓdíð‘sì7“¤"ã”}ïß”»-¶s™½õžûlÏù&ý7+s%ÆËq#DZiì0ÎSóï¶óf–]Iгǘïöá8˜<ëê©bþóÂ,׿r¼ ®Ô¯*Ç‘Œîƒ+Ÿ%çØ@eà9a~y†wÏ{Š×¾þàÒ‘s¹ÝpüIÛ×ðUN ç7îî ¯öœ+¤©AœgŸ´SÜïÍxŸ¨÷þ$‡Ò®6Žvþ¹!Îã Ÿ)®«ïž-Ž/[rÄq¢Ë²~;»ØÈ±âù¤WV7|LÎ/ê2ºùÊùëøB.ö¯ã§*=ù%Λ‚¿[‹ùµÛŸn¹,Î;+Z–`*õæVˆØßý^·˜"®ÿÏ8~"u[ù*½ËWÎ;¶ªF‰ï=°nÅL±Žêþ…ÏûÈ9|nÒ1OªïêÝ ¿sddë‰Õ}Ìȯ‘1~%Û9ùSés½MblÒíú¾ÆçaõþæœÏ:¼‰øžëñw!ç”5or-ëÞ5;UU¬¯ŸýùÏ ƒ˜_U°q<9‡©wÊŸþ< ßíQµÉyáy–ºbüã³’óϺ~Ó _ ç¨ÖŠ׉û;ë>¿¡_jðÁZ´9w]zîŒÝ>Þ×Ë¡ –1.ÍHÌñl¯È9¡Éè/äÒv˱‰a~I‡\ýîŠûÛ¥–úÝ󩦭ѵ¤ßIß6Ï„÷}û¹À)ÚóŸ­ê ôoÒæMV¾ çÕç3]õ‘³ßŒª§ž²Ånùû˜0¿T_Õò¢_[ÒçÁ!1ÏÁ~9k¸^ßÚ/ÁbbÔÕ—$q¿±úhVú±ôaLJˆë!ãý‘Åq´ËǬS9N®.ö“œSRóe—‹û/“×ÖíJ¿ÔewÙsíÉyhŽqÕùÊùàÜÛ‡p|z ì5‘sJHã,b^–V¾ÐB¿ÚàzÉ£¿Äùz§Š=î‹ãs· ùwü–­Èjr޳ïë—"~¾*{)39Lê¿¢YÎç:N9£tš}¼n¡湃ٿ.mSMÏû¯|$×A‘sR©RóÄ÷[,Ÿ˜wWò\ѳäœRbÊ…sÂü”s_z¶Q·U¿^ ûÊù™âI¦¶ìGaÍóíùZ& Fˆõ£‰‡7ÑÝ›”ùe ãõ˜-=úÜò•ó-×”MlççJŒXOΩ•]“|¶ç¨¿9ç9”ûûYÄ£JA"gÕÊÂóEÎ{®F»Zxö^£œŒkÃÏœý|Lä<îDÉícÃü~åϺë<9§o«ý¦šÈùÔý]r>ß5CÍë'¶(£óµc;˜òH8~Vl’æ™=ÉWΊõ)¿º³_ÜH¨’Wô›%Þßx#Ú󻤓ÕìÅ~lþomÇö«"ç/—£èoó\m“žJÎ{O7H&çøžöoóØïcÂ^ߌ$çèm™Ÿ\0ûxÝ%n§1NŠZ³ð ½gïHÙ"r¾¶·Ïœð0¿Ä„+GÈ9$qN޽â¾ÎùÍgç³½[åi°ð+uü.Îó÷•sPÜéªaŒ·gSv,ïÏ3isy±¾çÉÀ\íÙOƒë¼›£Ü"æÃÔ/zÚWÎ/¯Ï1+o]|4ça96¶Ìg{Þñ7çí««…7ß÷­0¼óñãJK9^:þG>_ýË÷¾‰ö¼aÍ1ÓOúnEwÚÛŒõÒÇɹm—Ü­¦‰ë^ËϽÜÃö£ió»§ØÏj÷´H¼~³µ;ßøœŽ‰frŽV4zÍWÎóI -WWßš?‹ØÎú¼•»‹u´ïíúÆJq}nnç†äübnSqÝ3 ¾)ç+Á¯ÿwŸ>Ñ~­¯¸OõzC;1n·cIÎQ1_ ú8o÷Sž ;D{ ù=dÙ8=¹e›í~&ö³#ž>ýØ;.–ëvº\)T&“ØßÛŽö¦¿ÓÔ-v3ÕL?òêæÏ¶¾r.ßß#„ãàf}×b¾eáÎo"Åuî‰ ôU—­w.­#9ßë8l^û›QicFV¡ýõ0v]õíÜ`Tñáù}Ío»ú7çÔðõÝ*‹çc]þXŸ= ½9…Õê+Ï/Úë59‡l ›TWä|ÏS»Uû0??ùêz;©ok[EŒ8>Ÿ¶*R:q~𠎣 ¯ùzöܽ"gÅüÎ3Å8ïzùÙ1ä|ÄñúÝ_9ïË÷"®õÏÚöCœ¿¶ü¾ºÈÙ¤ZúüÝò°¦¸.³Ãü§„ÈyN©]KÚpž×¾ù¤dq¾þöIÝq»VÃ_ÝúÑÎT½ÝŸœï]i]ÛWÎÑûëÖëÉ.¦çuwu þ%rî]}Œº)¯{,sk³8¿¬waeY1•×»àf¿KZÞ¡ªËL?µóþõÁ>ÛóŸÇ)%ØÎ;~%d9×»²{ÈùYÎÐ~ŒÏBöõu·"çè‹—´ñ‘³_J÷.Å:‡ù¾P¸îk1õpWe]_9ǧÿÍ9hõ„Yù\µ>K§Ñ?—÷µ[ä<´ì̤FbþHÛr5è·ÓWŒ}.>ßM¥_–6ô[#^.{Fýà竵ÝDÎ…ÆÆoO¾ÒEM–ˆœëÌ­Ö_ä¼h†¡¾—oW>þˆœ÷­{qk³¯œwÅ-(Z“ólÍÝeE{Î[b|I‘ó•ƒå†4'ß77ú‹ëØ} –Hãí<‰Ï§·âü"æô¬lâ~ìžL7?Šãs¶*­zÅûõXsáþ:rº³ÍüÆìãuÖÌmX‡óŠas2Œzêì™uNäœp,Ë膢Ÿ¾Ü3UÌcVL~{[Ì‹O¯ó*¼;çå'zÕÌÏþÑ=*£¢¯œùÙÙOÆ—œÜZ¬'š3á@'‘sØÖ<:q|ª?~49‡t©[ë½~ÛÏ»¤cžöbýý•:5è·Sþ´žþÜçñY:ù¿œ } Õ¶çýᦷbÞrJµä£"çŠ ¢cûóôZƒОÃÛ§´þ$rNˆwzX¦UYÅýÁ#«NÕ97;]ùh8û{x`¾ÆbKîÝÎâø||å—Ãâ>îõÇ´çØ]M¿Å}èÒÞ+µôÆÕCÎÇë^|ÿÍìãurÍëÆvŽª¹d¼ží×daç$‘sÏ"“æô׿®^µŠë‚Úƒ²çâzQÌÉ´{ØŸÏ[$o©dë&­í+ç¤ÃJO}~î—¹°hÏ©ÛO÷ûïùašqŒËÆ<{¶W!æ¥ xšÕëëø¬¹-‚í˜8娶sr|ÇeŸ|ŸÿÍ9Iþî Û5ìç5ÏA±þpÊÇœ7ÅyUr¸ãù òMÍ;ë(9÷.$ÏVH̳ußvKÃçÍ2g¸[mºßˆä6xkÂl1?aÿ‘²…yý/ÎÑw—ˆóªþgœfq]ëÑ–eÃÈÙ)O/“ì+çÆ¦Š×zˆû¾æ¹ø» ƒëý”ÿ݇¾4WÉùfÀ|G 9·-9ïev1,…~49ÔÈ´c9LýÐCÜWñ{ò1ßòY¼ï’‰Ššäü¢È¡”¾úíºSžô÷©<{›éù½g'¯¼9y~²4Û!vB?û$rŽ^·bwq~Ù¥½½ýß®Á®êÇͼßs¦ ¾r±¿òYñ¼ŠNkFÞ#žÐwútrË"q<ŸH^RZ“sT”‡å|Í?RÞK½*Ö#åYžq”í|Ü:iL!ŸÇçAs¾Ø3°‹xÈ^UD1ÛZÀ3^´çÉæ×-yá÷÷ÇÔ÷䜒uê¥|¢=Ÿ+¼<Ëtö£ååÔ'çjHì*±^·Â¢15ËžñËÈYñ|ÌÐe>rö;ü ˜íþª`ÞSô±‡z&‰ybfê·Ñ_ÄVnÜàâR¶oÑ•[ƉùCüºàß tǾ„P÷Ý»âÅïûÊùëŽaMÄóªrnÚ÷ßëÄŒßû†œSZj‹àõÂçM¨užœsø=>9ÅGÎaóφ³c‡wíô‡íü+I3lšÏö|ãoΩƱÅ:ÀëþyÅv¼“8RÜLjR§ XLû Ñe‹L'爭Šû~K{ÇŠu©µË&%S¿UÃÚµ§ˆûUíŸ. g{WiÞ¦¯¿}åŽCÄýéf‹bÚÒ¯§‡øm#çð©çVl÷u}ûekEýQbõ¦/#Åúͧeÿͬ2±õXòL£Y¼Œ³ÄiAÎáÑ3³÷ûÃÐeÙÄ<¦àu2××E;ß¼ã¿Áy{ œMÎ[U ³æ2ÿÿתßÅÔ•výJt혬‘Sä\ãÞðõ“Åv™!=N®÷æŒmútXo’7“™×ß]óYEê&\é6ÚWÎKW,תûv¯¸hÏW×w(îWÕü\»í縳ôú“ä<ãD—7|ÜLj¯SÓ¡c;‡YƒN|d;—ìïöy¿êÛßœKî½pRÍöÒùygÑïGL©bš'îWÍúq!XÌS?:èSrŒn±c¾¸4­Ø¢‘|Îãm¿Ío&rέ‚˜‡ÖeÕÁ±žîÙ5ÙöÇÆà â<' ÜbÉCú÷þ¯»÷ýµ/ÞÏš6I)÷•óÓv FŽÃáÚAŸ³‹ñÛ®¾ûô"çÃ5ïã²ÊSôbýÖˆ‰ógÇç…»üÞˆyç;5[Îþ›±ià 1/ñl!k^?úþª¾ïè?78Ô×üÃÉ{Ä<š‡K‹Ö'爕‡&ˆãF™j›sˆyKù-‘Šç l”<Ïçï2wÉãÆ ÓO ØCݸöåß—÷•³ÅÞr*ã¼(ÇûÌÑb¾SBz6Ñž¯~_ÕQ¬ooÝ{mrNº¿s¸¯ûÏž¨Ñ%Å|‡ôû“Å|ñtù¹×úÿŸs&öŒÚÿ‚vm®Ò_vt\·[Š âôÛáãDÒš(^†Ç|´‹+ªßŽWÙ3n,•Üá ݪ^€ Sú·Œ¸%6ı¹B úôé÷ýÓÅÂàRy’Šó†æ}I ™CÐCçk.„ø:,£yóR (C¦dÅ‹„+_³±"èÁ¯{Õ دä{é ÈQMê¾d‡,HÙ£§Ã‹ó/"(¤6¸|‹ŸÇ•©1OLÀ‰‰¨RbAKНžºÕ× è«ÑqÀ (Yi÷cñ€†GVAGÒ¬úb¢Cj¡r±làÀ§¯?Eˆƒl{üYܨKêö®`gêv,}èF!_AçNoµ±Ûµæ±W9 :¶ÙÈjqƒ³á½Ñì° —lÝ]DL$ôäìÓÙGÐ~)·+ ¥á×RHlç<Š•ÇûlÐ-þË9*›³¯xPRØÒ-¶§ ˜ƒ–Öè”,fH¦MèÿßD8å£ÅâÍ›T·ˆ‰C»ÎóÛxX¿‘ú)s·ÍޝŽKÍÆûÝÕÅPÈÃû ©u¦¶˜(x¬È˜¼ÿ%¶ iNÎïÎéŒ÷q‚Vlѱì¼~ì¢l¿×‰ %žvy—N ¹~¥õŸ/n„ç.²]ä¸eÖ 41A¬ãÞFuØïbÞ¿Ü.X<·î™˜@f¹½4í÷ÆQµµ˜8”~¯Æ7‡¢êŽ«xH,ÜK ¨e!çXóƒ¾Á¢Aë®QÍ6²wDÎa¹:Ó‹‰rŸL5øüm.ÛWŠœ·¬¹ßׂ¿ŠSRñ÷;îh’ı•;®6&çØõÊÂb¢SÄÆ•aîÍ´Ÿ|ŸªMñõ €³>(ÙΉ¯ŽZÊçÛ5`æá>'ˆ ÿ›óÄž5Ÿ‹ìÅÇ Šžî):At`¡QÅ@$<¸ïÖâóÅTÛÜTìÇbÏÝÈîv«p]\hlÒL–K\Pε÷]±ý½CÏç!çû<óÄÿ_õ¿…±Ÿ´2ŸìÉ ÎÂ2™?fòu"{¤g¥)bAUⲈ|äœ~dpùœb" úê×@ú“äc–Š…ÇÏ×Oä|<*ïX±0ס«,'çãy§þy%€ïh‘ÐM´ju4è?¤Ý.ì_å+ç¢ÍNŠzA‡]™Ä‚¹§];Å~æº3UL ò»3îÙ Lññc:s”뀭 Ÿ3ãD¾•êFþ ]TÛ×DÀÖwT]ÄÂçЩïF²}u§šÞFý˜Íy׉…qß;‹…á]ü/nûîc`ñ!!Ó8±_,zuÈÄëO2æíßÌç±esî[ÂÖ”H˜ºOv¯ˆÂº69wÐ A?=ËÕ²9ç˜e^•¡È­N±?o›á'RuŒ?îÜ~c?g7ø_Þ»^ x]¯¢D?jÊ6W\(Š9¹4•QZñˆ]gÌÿÿ}…5Mù%.d¥Ä ‰2ÑŽbSÝ9.S!lO/ÑnÛ™gÄ“cªdsÛLb"ࢤì/éÇ¢WúÈözqmì­C+þ›ørµŠ¸À´|ÙÕr;Qߤô5á3Ol¯â{öb'ò¹cŠ=x¼]LDÕѱ€°û¢|É9r§´IN¶Cˆ´ãÝ8ÚÓ‘³– nµú¹|Làö[QK[O,¨ªs²Ï|1±·Î§È⬇žÛÍþ™Ã9©˜¸¡œçmé˜}¾¼²©À¶sôò¦ãBD{Úži䟸ýÍyPƒIâBÖœ å‹öH÷Ò1œH„ý9¸•x`Ñ”¾®|¾Ô«õógãóEËwºçJ±°éeÕ×Ô«øÊþKLÀœ=­Ým1Á·”)J'Eô¶Rô£u²]ÌI¿–8dÿ³Nä|~û“I>Ž“aÃmgN‹ L£‹Æ¤êØÎ5­Ù$NìmY&]ñ–Ø÷%ÇÄÀ~m.ˆqÃÈ1m‰ô¦SŽEŠYÎ2-¾ñޖâ„6òsQžþcãÝÁ³|MÔ/Ü®ŠCÜè15ŒœÓ3WÿžÊ~´ù{Í¡óÄÄ‚§egŠ ¿…g(-&œYÚ{¼¸1h—ŽÞMÝă+&wð5Q¿kÑ_ÄÂçUw| [?ïÊvÓü¸&.ðDô›¼îã˜ÛeïíÛèãxýbyX31ðÝ}ºÕj±ðîþIv_9‡Ýû/ç°Õ·—TÇã;5Û÷¡¿Œ¨é×;p‚¸à¾¼¤›\ÃLÓ¿Íåó5èk›+Þt«—~FL´½.ÒO,€Ü™Û, ð:ÿü±ü;Eö¡Ü1Ð^îª)ÞdËÒc§YܰßiÁqri•ÍFúʹsû[Eû~i»E\›?cy°¸‘Ö7ªØ+1qG›·âÑŸÜW¼ŒH(Qv›¸ÚnÈ]âJ‰å‰»ÅÏ'.,Û‚v})jßZ^ßT¾A]_7Âê<<¿F,ˆmxìû ñ@´êeÚôš%r_ýî‹øÜOw™Æq#õëÀvgE¿^ysÒ{r{ðêîSö'襁e3ùjÏÇ_˜gˆöcéûU<èpfÓµ ±ðæxò±hq£>ðKíùâxû(qB59§”ùRö©8±hmZ 1«?ݼÕ×›øßÿåÿñëä/"ç÷kzdHvÔì´ö[¡åÖ{âÁTíöe7âRÛöÈ–[Œƒ>êó-§ÝDt[”kŸèoöN­WAL´=>®¸XÖEv¡áfÞgd®ÕQ›Ä‰ÇŠâšµ;ÅîŽOé?çeŽ|Ç×q²O´ã´x@Èöó'›ˆ ï"ÏL£}û½-ð¢ã‚€»Cê‰0…Ìí—0C, hòé÷1‘÷ü¦¡È9G™¶ûCÅ ×­þOÙŸJŸÌÛœœ“òþÈØâk!íʌֳÅB uÞP‘s1ÙU¸ÒqãêÇz¶G£Ksޤ[?k’stââÖ[Äé+ƒ´ë¨»:eM‹ >XùÕ— Wóó×§®RÞì}^<€±v\ÒfÑ.‡¸‹ˆ $}íËçû8Þ¾‹Ÿ‡LwZ˯72÷¶ÊësU¹)ÿµç¶…ŠŠ aM{.+,Χʞ2Ú"ÄПÑÉâBg…Å2ñùâŽ%´ÌÂçKn“m}sÑODäýtH,Œ]>ðzsÚSôö ¤¶¼nzéiMç­²ð¯²A<@§Ôü¾‹iÇ9 YÊ$wÕӱɾÚÕÎç9²‹þóÕÉïÈ;¨“#½X@P/¾àCqa¤yDÉ~äkÔ½¼¸±²òaëgb\\þNŒ³kÈq—ãö®5ƒ‹Ü5óûîÞãš“wÂ÷œŽûz`ÆZɶCâÙµ•—Å–‚’¿õ›)¢´Î~ŽÏ[}Lxvq¾‘£m@±+H*åsD.ôà‚XžÒp³¯ÇîÑ Å’YÃú-&ïø9¡w® @ÉZ\L Ø÷½óònPmHž>úáä˜×©WÙÎA¿–fÍ*Îwú>˜Íç‚ɶÿåŸÙ;cœXXõ;ìë±°ªõÍcZ1.‹Y²á:Û1Vîª&aÞ0x?Ÿ/Ð6^_’ƒ¾G~ý)Æ_ù.ÛÅñ0`i“]-xŸ)‹âúá}.º,_q¡¹ËÃÇzŽËážú%?™Åñ#³¦ûÈ9¾zÑ“5Ūš³.}¥ÿNñ–÷ïoDÓÚ;g‹þyb×ôÖâ3?wÕ:"|˜ûw¢YŒ÷Û‡x(V™k]èÆÏsD7N;.\;—›Ä˜çOìã#çôç½Æ®Ès•þ;%ý]Ïyâ¼yÖû#GÅñ~ûÝm¬çïËÍ:"Úû¨ ÏM>ÇñôÍ/+nçž&‚ÄSJ*‹/П®|A, ÛSV>œw=kU/¿‹ýìi•J~÷²onðÇGûŒ+4­àU±Ÿ}R“¢=øpÀ×B÷0Í9ÇæŠ*°P´—K¦s×ăÙRî¬1ŽvÙ3ök)1a¹ÜúASù|Ai–Q[ù|‰ ÏËØbçuh0ˆúÇŸcò“sÌ꛵—ˆ,mž•œc¾=54§}ÚùÎ}“ó©0]bt9ô¸§˜è£ÿŒíþ®¯8oŸµ&S±P25~ö~qžu¾åþâ(;.-ò": \Ћv–¾¢v 1^}_2¨«X(9ùëÄcâ}ïØøB'ÚîX}Ï-®:4ÊÇñ3¶N¥ *ú«¨—¿$íÄ8ûÐ陵èOÂneøÆþ^|üÙå·2>œB{Y1¯òUñ`¯ƒl¥n@­>#Öøzp…Nûmˆ8o¾›|®˜8Noî}h–¸ñ»èkÑábÂÀ”Ö¯K“󈄢ÙîøhŸÑ'ü7£ý§÷ϽBÜ^çìðõàŠøUÿå5ép‡[â/–Ž%ÌâÆæÊ+5ăh‚žLuî O#»ä×ß®ug¶XØÚ§àjÑoT>BýhϬžç â‹AVäÊÇ~}|ÛÌ=b"À¹†HÑŸë¦ôÝÏö×G}zÆv¹]µ{»]¾úÏò±!ÍÅùjËþ‹ÅwCæ$Û$ñ@ª1aÅÄíŸýÊ8.t»¶¨ýLÛ»Ûë?ýܲhO˜8.»ªu›.À ›¶G\ñÔ°°_Èvmß_ ”o6ì2“ÏvxHØJq\–Æ´¹Çø/nÑnÙDñ`² Ïë&bÝÚªïŠóþý›VMÇÿäÞŠœ%}¶>~xοå2!3² +²ÁÙ‘â×r!7ò@‚|@~@ABaHQ(Šb(Ž(‰R( ”A9”GTD%£2ª *ª¡:j &BP µQuQ¡h€†h„Æh‚¦¡ÂÐ-ЭÐr´A[´C{t@'(Ð]ÐÝÐ=ÐJôBoôA_ôC € ŽÁ‚¡5†cFbFc ÆBƒqˆÀxLÀDLÂdLA$¦b¦cfB‡YˆÂlÌÁ\ÌÃ|è± ±‹±Ë`ÀrDcVbVc ŒX‹¬ÃzlÀFl‚ ›±[± Û±fìÄ.ìÆìÅ>쇇ƒ8„Ã8+Žá8Nà$Ná4ÎÀ†³ˆÇ9œÇ$à"츄D\ÆU$ḎdÜÀMÜÂmÜwq÷ññáB žâžã^ÂWHÅk¼Á[¼Ã{¤áÒñŸð_áÁ7dà;~à'~á7¼øј3!3² +²ÁÙ‘¹yäCò£ ¢ CŠ"DQCq”@I”FÊ ,Ê¡<* "*!•QUQ ÕQ5‚Z¨º¨‡PÔG4D#4F4… ͆æh–h…Ö£ Ú¡=: #:AÎè‚®è†îèžP¢z£ú¢úC…Ç Æ Å0¨1#0£0c0ŒCÆc"&e__åç§ÅDb*¦a:f`&t˜…(ÌÆÌÅ<̇ ±‹±K± ,G4V`%Va5ÖÀˆµˆÁ:¬Çl‚ ›‹-ØŠmØŽ0c'va7ö`/öa?,8€8ÂaÁQXq Çq'q §q6œE<Îá<. q ‰¸Œ+¸Š$\ƒבŒ¸‰[¸;pâ.îá>àÃ…'HÁS<Ãs¼ÀK¸ñ ©x7x‹wx4|ÀG|Âg|ÁWxð øŽø‰_ø /þ@˜3!3² +ü‘9¹yäCò£ ¢ CŠ"DQG ”D)”FÊ ,Ê¡<* "*!•QUQ ÕQ!¨…Ú¨ƒº¨‡PÔG4D#4F4… ͆æh–h 9Ú -Ú¡=: #:A‘Y|ý í]Ñ ÝÑ=¡D/ôF_ôC € ŽAŒ!ŠaPc8F`$Fa4Æ`,Æ!ã11 “¡ÅDb*¦a:f`&t˜…(ÌÆÌÃ|è± ±‹±K± ,G4V`%Va5ÖÀˆµX‡õØ€Ø6gßM{ÆVlÃvì€;± »±{±/³øGÚ2âp‡pGpVÃqœÀIœÂiœ gs¸€\„—ˆË¸‚«HÂ58pɸ›¸…Û¸'îá>à!á1\x‚<Å3<Ç ¼„¯Š×xƒ·x4|@:>â>㠾ƒoÈÀwüÀOüÂoxñbYÙàìȜȅÜȃ¼ P…PEˆ¢(†â(’(…ÒB”E9”GTD%£2ª ª£j"µPuPõŠúh€†YÄtFÚ3š )dh†æh–h…Ö£ Ú¢Ú£:¢èŒ.èŠnèŽP¢z£ú¢úcTˆp Â` ÁP ƒÃ1#1c0ŒCÆc&b&C‹)ˆÄTLÃtÌÀLè…Ù˜ƒ¹˜‡ùÐcA±¬˜öŒÅX‚¥X–#+°«°F¬E Öa=6`#6Á„͈ÅlÅ6lǘ±»°{±ûaÁÄá á0Žà(¬8†ã8“8…Ó8ƒ³ˆÇ9œÇ$à"츄D\Æ\E®ÁëHÆ ÜÄmÜwq÷ññá¤à)žá9^à%Üx…×xƒ·x‡÷Häã#>á3¾à+<ø† |ÇüÄ/xñâd92# ²"ü‘9¹yäCò£ ¡0¤(‚@E1G ”D)”FÊ ,Ê¡<* "‚QUPÕP5P!¨…Ú¨ƒº¨‡PÔG4D#4FSÈÐ ahŽh‰Vh 9Ú -Ú¡=: #:AÎè‚nèŽè %z¡7ú /ú¡?@…Ç Æ Å0 ÇŒÄ(ŒÆŒ…ãñ˜€‰˜„ÉÐb "1Ó03¡Ã,Da6æ`.æa>ôX€…X„ÅX‚¥X–cVbVc ŒX‹¬ÃzlÀFl‚ ›‹-ØŠmØ3vbvcöböˆÃAÂaÁQXq Çq§pg`ÃYÄãÎãpv\B".ã ®" ×à@2nà&ná6îÀ‰»¸‡ûx€‡x„Çpá RðÏð/áÆ+¤â5Þà-Þá=ÒðéøˆOøŒ/ø ¾!?ð¿ð^ü¸ð• ™‘Y‘ þÈŽȉ\È<È‹|ÙÄwkÒ–QQ…!E¢(Š¡8J $J¡4‚PeQP•ŒÊ¨‚ª¨†ê¨šA-ÔÎ&ÍL{F=„¢>¢£ šB†fCs´@K´BkÈÑmÑíÑ¡@gtAWtCwô@O(Ñ ½Ñ}Ñý1* D8a†bÔŽ‰Q1 Æ!ã11 “¡E$¦b¦cfB‡YˆÂlÌÁ\ÌÃ|è± ±‹±$›x¼mËX‰UX50b-b°ë±± &lF,¶b¶cÌØ‰]Ø=Ø‹}Ø  q‡qGq Çq'q §q6œE<Îá<. aÇ%$â2® ×àÀu$ãnân㜸‹{¸xˆGx žà)žá9^à%Üx…T¼Æ¼Å;¼G> ñ Ÿñ|C¾ã~â~Ë?±3!3² +²ÁÙ‘9‘yäCò£ ¢ CŠ"DQCq”@I”BÊ ,Ê¡<* "*!•QUQ ÕQ5‚Z¨:¨‡PÔG4D#4F4… ͆æh–h…Ö£ Ú¢=: #:AÎþâ{]hÏè†îèžP¢z£ú¢úc"ƒ0C0àÆpŒÀHŒÂhŒÁXh0 ˜„ÉÐb "1Ó030:ÌBfcæbæCX„ÅX‚¥X–#+°«°k`ÄZÄ`Öc6„͈ÅlÅ6lǘ±»°{°û°@â0Žà(¬8†ã8“8…Ó8Î"çp€‹°#—qW‘„kpà:’q7q ·qNÜÅ=ÜÇ<Äc¸ð)xŠgxŽx 7^!¯ñoñHÇ'|Æ|…ßïøŸø…ßðâÄ ©LÈŒ,ÈŠlÈŽȉ\È<È ò!ùQQ…!E¢(Š¡J¢J#ePåPP•ŒÊ¨‚ª¨†ê¨š¨…Ú¨ƒº¨‡PÔG4D#4F4… ͆æh–h9Ú -Ú¡=: #:AÎè‚®è†îèžP¢z£ú¡?@…Ç Æ Å0¨1#0£0c0D`<&`"&a2´˜‚HLÅ4LÇ Ì„³…Ù˜ƒ¹˜=`!a1–`)–Á€åˆÆ ¬Ì.£C{Ʊ1X ؈M0a3b±[± Û±fìÄ.ìÆìÅ>ìÇÄá á0Žà(¬8†ã8“8…Ó8Î"çp ¸;.!—qW‘„kpà:’q7q ·qNÜÅ}<ÀC<Âc¸ð)xŠgxŽx 7^!¯ñoñiø€t|Ä'|Æ|…ßïøŸø…ßðâÄÍåLÈ‚¬ÈdGäD.äFä…ù€ü(€‚(„Â"EQ,‡xT(í%Q ¥„2(‹r( ¨ˆJFeTAUTG ÔDj¡6ê .ê!õÑ ÑÑM!C³bém-Ñ ­!G´E;´GtD'(Ð]ÐÝÐ=нÐ}ÐýРÂ@„cc†bÔŽ‰Qƒ±Ð`"00“0ZLA$¦b¦cfB‡Y˜9˜‹y˜=`!a1–`)–Á€åˆÆ ¬Ä*¬†kƒuX ؈M0a3b±[± Û±fìÄ.ìÆ^ìÃ~Xpq8ˆC8Œ#8 +Žá8Nà$Ná4ÎÀ†xœÃy\@.ÂŽKHÄe\ÁU$ḎdÜÀMÜÂ8q÷pðð. ñ Ÿñ_áÁ7dà;~à'~á7þ@LÉ„ÌÈ‚¬ÈdGäD.äFä…ù€ü(€B( )Š EQ ÅQ%Q ¥„2(‹r( ¨ˆJ¨Œ*¨Šj¨Ž¨‰ÔBmÔA]ÔC(꣢£ dh–S<®Ÿc3Z %Z¡5ähƒ¶h‡ö耎è:£ º¢;z '”è…Þ胾è‡þ"ƒ0C0àÆŒÄ(ŒÆŒ…ãñ˜€‰˜„ÉÐb "1Ó03¡Ã,Da6æ`.æa>ôX€…X„ÅX‚¥X–#+± «±F¬E Öa=6`#6Á„͈ÅlÅ6lÇìÄ.ìÆìÅ>쇇ƒ8„Ã8‚£°âŽãNâ4ÎÀ†³ˆÇ9œÇ$à"츄D\Æ\E®Á븛¸…Û¸'îâîãâÕÓï¿ÿ‰¹C“ÿÍÇÙño~ËóEļ1ÿBÌ•s†ý»ÿoúwOÝñﵸ—,îùŠ{³â>ª¸ß©ýwÿÐüï~œóß=.q/JÜ3÷vÄ=Í¿û¦÷ÿ®«‹kàâZµ¸¦,®ýŠk´Ú×<Íÿ®#:ÿ]—×ÏÄu.q=J\Òü»Ö"®‰Xÿ]gpÿ;oç×â5!‡ Z`† N¤Á_OM„B5t0Â;\ðBº€šC- 0Ã'Ò࿚…ê…âQ´Ô„v¸á…t5!‡ Z`† N¤Á15 ÔÐÁ pà éjB´0À œHƒÿRj" ¨¡ƒV8à†ÒeÔ„*ha€68‘5 ÔÐÁ+pà érjB´0À œHƒ45 ÔÐÃ+pà é jB´0À œHƒÿJj" h ‡ V8à†ÒUÔ„*ha€68‘ÿÕÔD(”Ð@¬pÀ /¤k¨ 9TÐÂ3lp" þFjB%4ÐÃ+pà éZjB´0À œHƒ m2(¡&Xá€^H×Qr¨ …fØàD$ë© ”Ð@¬pÀ /¤¨ 9TÐÂ3lpÂÉFjB%4ÐÃ+pà é&jB´0À \ð@b¢&dPB=L°Â7¼n¦&äPA ̰Ã$±Ô„ Jh ‡ V8à†Ò-Ô„*ha€v¸àd+5!ƒèa‚¸á…t5!‡ Za.x ÙNMÈ „z˜`…nx!ÝAMÈ¡‚FX`‡ HÌÔ„ Jh ‡ V8à†ÒÔ„jè`„v¸àd5!ƒèa‚¸á…t75¡€:a.x ÙCMÈ „z˜`…nx!ÝK†jè`„v¸àd5!ƒèa‚¸áE©ýÔ„jè`„v¸àÄBMÈ „z˜`…nø &B¡€:a.x ‰£&dPB=L°Â4ø¤&B¡€:a.x 9DMÈ „z˜`…ið?LM„B5t0Â;\ð@r„šA ô0Á'Òà”š…jè`„v¸àÄJMÈ „z˜aƒið?FM„B5t0Â;\ð@rœšA 0Ã'Òà‚š…jè`„v¸àä$5!ƒZ`† N¤Áÿ5 ÔÐÁ ìpÁÉijB´0À œHƒÿj" ¨¡ƒØá‚5!‡ Z`† N¤Áÿ,5 ÔÐÁ ìpÁIÏóìûº§gžŸ€>•(Жî@0pÈîM@0¬ eK€µÀV H®O€O@Ÿû(Ð, HòJà Ð È?`˜€>` øû€Ó@1P |ú>d%@°<€ È*7@' ÿeú€-àìNÅ@5ðèû˜a&º€à„‰@>P ¼:ù'(Ðl`p(ªÏ€t5Êt À| xtò5(Ðl`p(ª6@ºeº€à„‰@>P ¼:ù§(Ðl`p(^m€ô3” è€$ù@%ðèäëP& Ø>À>à4px ´ÒÏQ&  X@ä•À 2}Àðö瀛ÀK  ~‰2]ÀðB€D ¨Þ€¼eú€-àÎ7—@ ý eº€à„‰@>P ¼:ùz” è¶À.à0p¸ ¼Úé” è€$ù@%ðèäQ& ¬v‡sÀMà%ÐH¿F™€.`x!@âëgfÑ?æÿ¯ø_ÿçý;º£ë_s¯çY^ýÿδ»ú¿CgZõúÏÿËèLÉg¾ÐÙVËïÿïsšÿUϼÿúÿ‚ÿúÿâÿú¿!óïl(ú7êfÆ€°@7œA·€AªÂÀ}2 “ùôC!£uÀXx!À1à|m}´½ñ‚#€€`¸{€8 (ª@ZGLXÀ Øìb€4à*ð°×?™ýd%À  ¬œ? 8•@#ðÔ›aÆÚÀ2Àð"€ÿYƒv­€Tø:@0üÏ:´Í@(\nuÀ—ÿ¬E ¨‹kÀŽgë@ ðè ^å©ÀÀp„@,$ÿé@0¤t˜˜tv›?°H.UýþUFgÑ´O“ö™Ð:Ù>ýh ­û¢µZ´¾ŠÖDÑ:&Z{Dë… ÀÃF£>ZKCë_hÍ ­3¡µ!´žƒÖ`к Zë0rUGŽCëhîžæÛiŽœæµi.šEý4çKó´KP¿ê§9Lšw¤¹Bšß£99šG£¹/š¯²Bý°‡>kð,Í¿ð,ÍsÐÜÍ'ÐÛÓX»;žõD[išÆŽi¼—Æhi\•ÆBiü’ÆiœpÚ„¶Ò{ÑX/јãÐØ —ÐGêGýÔÿ§>;õ³©oLýYêƒR¿‘úzèw C?©ƒúAÔw¡þõ(¯§\œògÊy)OEÎ89ÞÊá(ï¢\‰òÊI(@l_ûžE1±®/bÓgŠ?3ÈÏ“o&J>°ñíÚŒù¯ÿÀW΂oSzýÿ|^ñšùÿû'ÚMçîüƒúŸ?þ÷¯ø÷?§1þÏ3uW/U»Š¡ŸÜÄJ#RÛ9¦X bTL¯ŽN1lƒ®qìa#°izó™ã4σñù© "¦jÕšõ#é4åCy½jöwû<Þ’½ôïXHSëóéžºÊæ«j›Pá¹<ó7##ñÀÖ |Ò½%Q(hBÂ;w傆z<*J=Zti?_½¼¸0ºx?îºç½C¸eÝ÷ºl–SC´ˆ1nh=2ø,^̸)Ü:ßW>7øD÷/RU«’@Çꦤ½õÁ‹‘6Ð[ÄøªØ”Ó=ïâ×%߆‰Ï‹IiÁ"¦ùá(ºç ÏÚ‡¿åEã󢸊††t´S­Ÿ†F²ˆ1ؼlBèñîëåùì× å1â½tõVû´ßt\ô½aKéžwÁôËy{P÷ýW{…(ϾG‰oªˆQUKû»åªæ=¾ÈqL¾põÀ>tÏ»ðÊä›Ë@4Ó(ÔPÑÂÀcm :û«êê߇DL´ÂŽû‹9ˆŽ^ã³Ö–ÞãèÒäýtßêŸºÝ {ñï˜W_ÙQ¥tmÄÇü[x>`qù §ã°”?›Pòú›Ñx?AFPâkºÏ±~옘£(ÿ¥ô:n³õBP@8Úi«s×4Ïs?™Že×2Íâûì˺ÔsЃƒ?'Óñ¡Ò\ÇpF«DÆÆïÆ÷F¤o˜O×+¼«ÏŠô‚õ^ o ëXj¹ý¢kyb_E<†þ…^k»q’Žç‘íC÷±?Ù=>ŸOÕȳ\ž‰ï•xÍøLrT9ýF—ã~°*Ûǧ„àû‡ŸoÌ£ã¢Y±ë(è™ÐÚþG&ô]¨:ÂÏr7k:6!~'ÞçIÎéÓxŸ¿sJ—£ÜöÓýq«-zw[ÐÛŽÛÏ’iµÃs}¤n|_#bädoÞ®#½»®bmY£5æ1oU©ÞïÛ!¿è×YÁcé8T¿Ûit-P·ÿX ߮㹽o¶§cÏy—£¢ú) è¾Ò÷·¿ØÐ}pçKZæí¦{ÕŒä¿àý ž VÍÂ{±SOÌØŠòeBO䜥ëC‚„#ûBž¼õ×L_î§{wO&ð!Ÿ©·‹ÆÌÌF;Þßß&žC·ý<ÉaW2Š­i’Ð+у˅Út?¢‰ÆÖNøíûòÖB;#^“Ží3zبð™«p·ŸW½‘imÏíír*h—j•ú~®{£/&]ÏÅ÷„­E;<éá;í+Ü gÙ 6„Ùã=EŠ¢!üŒxØßqÙ°÷;I+ÔéZŽ ÷ cŒ/§æ¼á8V›ì1*òeBƒßŒÏÂɃùÒàYcÒ­#K ±îÃgëèøéÐ3‰Æ< d~N\÷»×§ã’³ß/7£{Ú»ý'šÔÅsRlØ< º>êEÛýóð+"[so®‡\eè|Ϭkr¯ äG Òò\ñ\Kúˆduøg{×Aºßñ}ÝݰŸïÂ=¡þ–öîâ{⣇=j`禵aL xV‰ÜðŸ7kë-ÏaWI‹j/€gáƒÑß6ƒÏÑ7B=¡½ Œ¢{k Wõ_Œzä<£¡BÓ‹Òòˆ#‚ýšÐ·ìå§’š‚Ð[Çá?³è:3åºfFööê©\×ÌhæÜ¥ûø2Ö´?¥ë;F9ÞŒ£c„ß>Iž‡÷Iâ3ô†¿fv©,ö£ë!Zí¤áóÆ¡=QnAÅ3)ŽcµE>ïÞÜò ûZï«„¿f‚ƒÏ[CÿÖHª¾Ï-Û:N£cëƒôË9ì3÷âÞýtƒÀ4°nÒ~ºG¯e˜WÏìŠ.žya˧M£k*îɧ®¥ã£?<únïùD–ó øÛ[©8þZ0oÁ÷kÐóš—Êãeèþç‘ÚtMŽÈáÀÔ!°÷9CàØq®Ñ¯ _žßÒ\gAîFR‰ìyØW€´Ü,ø›r©H !ÇõM2OÞÒ‡²£‡¸™Á_³QYßxÞýZºF‰§ò6ê>ORYã±ò…Ù+ëâéØö×M†à‰_à}öx ]á÷ì,ùcÝ÷°7í_OåpøÕè”Å…t ŠZñùjØ1/¬ÁÑz½¼Ž_@ú¾ ßn>Ý_œ&Û‰|æNþù ·Òèº(ýF3ºæä²H%G\å}éì·†î+'.އ wµ±1g¢öLÙvÿ^Ý–yW©ûÙ}9Ñ‹Â6BÎqÎáèo¬gByÏ"ï.ž™C_¡¼Ã_êé:~”ÜÜgŽ"ÆÙÜIãùÍ›ÝÒçÛÝý=o¶ž™žÅAR޵KÒê†À]°ŸˆëÅCœ)θ(جE;«–Œyì¹Gˆ¾7Ç‚gñ„ÎçèøîUÃ2³9®©²ûv##üŠ{€þÄË–Ùˆö^³Ö¤ë´šB6ÆP¾`::Ïö÷Jö‰<ŠoÅ®}‹Öµé£…Ï-§«OòF^À¯¾â =h©ÌÑÊumÌèÏWé^ä¸V—è‘y{tÓ}ĪÿI‘l¯^‡?ãÉuÖ½GÜn4Ë›eˆfÏÃ=Déþ^«Ï,DŒòÊ~µà¹¥áçÜë'—>|Þ}9W&]¥kD¯wѵO|Aÿ=ßC9Þ‹ïâY|ñáiÒÓqßf:ѵ2­šöx¿Ò+)ŸÊï;NÚ"'€ž—)ü—Gñœú¿ ¦|¬´~)äÚx°§â²ç›ºáWôã¡ÈV|uç] {…ÔôÉ¿j¾¼9‰CÞžzáÛ—Ñ5LîÏ)_Ø?ü[_ø¾“©ìð,nÜe ;ÌØ—>wÝkœ¸Nû]³g5È+®O‰Û<Ÿ²µú¥‰xÑ1è»®‰à]{Àaoª¡£Ò÷Òu¡å»çе1N=ÏÏ/ 5V*û‰çj¿Ý[¨_2/å£/]_øIÚ\!r).;…rÛ­®”ìæ7ê²™¤·sîv¼·ÄO›¯f"F""ôýôCÍ«,ØG¶ðgf߽ݗ#ѳáÚ øyQäõÚ}h¯jì™AǺý'*îâY´êÚÂ^ðËŒùæSÖáç´<åSÈ´Óìû†‚göûËA“èÚ!K'+y¼_sG~Î÷®ãåý^¢kK¼¼T'åæ¼m›€úÙmÎê§¡_ѱv;Ó!wÉAÇ_™ƒgÞ­5«>Ÿµóläº6àŒãÆ(âù€±ïbر(pXÔ^gÈ1­oƒ:ÝC¡”î;fÝöšD!Nj+UŠÏ¢“ý¥¡o|) ÍøüNû¦,'ð,X{îªÑ!º·¾âö9{d¤[ÖÏ• q:tíÓª?ÃBÏZ*<šÉ¯±-ãéZ‚6'ç!n{ß +vÏ2wNI'Óµ3d4æqå½]Z‰çË‚ÎdرðÄvÛªU"昢À%< TuŽGœM±Ò=.¤ûrþ¼2¹ 93ÃWmž }ð,³³ÄQ/ûº‹gAI^*âùÎ9¡ìXf­¶VÄ”~ÉX×z**œ“½…®ÕÜ7sÞÿë¸õõȘ’Y ì¦ëݲ—ŽÁ÷ÝÜ,_O¦{¦KSÆ ^¾ä¢%ç`Wðµ#ªÀ3«wÉg ò”šuª¯8òIËÝþŠÁoT£8"ñT³qÄø° ô!éÉqy½iä?ǺC×Eª,ºJ÷ª â$)Oòhh½þ¥§œñ<‹ŒNNT¤~÷vi‡?<°™zWÃèš´.Ù‹°KjL†‘{#a5—ôÝiyºgwãkðª™ÑS ùÊyÈîîŸgDRÛˆgÃÎý!¡ƒÄà|Ê¥ÊDZˆÇŽï\6ƒ®»®Óë7]ûÉÛ^_?–'üy,Œxv*ñŸ …E²‹‡Á¦ïQWPÿ`ÜÝÅ?«¤úL F¹/‡9ÆýÏžñz¿n£¼šëæª9üïû_É•Áø¼rsë|äý¬Î~Çãt]í7ÏAÈkÙøž›FÒ5gs^ÅHR±ŸÝE×Ð Sèuñ‡wðiZüOÑ4å¦_ÔŸ¤¬p‘®É)‹ màÈo’n4×Ü&ž×íÿÐFýŽY3oxÁ8z9Qüš¾6täÀ´î÷™9x¾ ³Ë$={ïOã(>žŸsÄÉ$y…á=éšÏfe&øy«âtö ø»¬{ÝÁ³ø^™ ÞÛs’òü—¼É¥gð§σ‹ÎÒ5TbÿÓêÂîŸgXÕ.ž=_<8oEÏMê_™µï3¬ ¿üJUP³qų :át Òn—£a®ˆ'Ñ5 µÄsJà‹½dWß>̺»jßî6@õ‹ÖåíÔBy¼sƒBÿ‚Äã£JÍsGæÈ­XGý-k Ÿ¥y†ê´Ž7…Áä?Ð¥q#ŸÈØÞпªì›–AàYT{mÃqêWìÈí€}Ö‰¼í‘¾7€âg¯÷\|~aCäÒót-“~£)]Oi©¹ƒOý·VPÿ¤qÁ¢¿Ð/ÆÑóQêçy¶Ü¾Bþ^]6à:#Õåx{ÂÎÍ•Ù5ƒgÁѼÎ@²Ã_“ªœ9ì0É"ñ˜ñ¬³ Mü2âõ†"fN–½Ç#ð,Ût6ˆÆaJFæ=ÞÙ}9KJîùí…œ“y)Å@ÎI}~­;â×ýóŒÈŒx¦©¢®é"¿™‡B‰ïM­RÒ?>Gú#~8ç{¬.!¾Ÿé|‰¦û«ãÒÏ/…r˲¼úQÞÇW—½I㓃nË*À¯e±þcv°+;L$èºßÏêu`w nW­ŠßÂ’½Ñ«Às’ℇòq¨à[ïO0ž[âÚüÌòuG@ë*ÆßßÂï[Vô"¹M9þbâè-Dz6ÑøK+Í’üÈ/h…¼8*µ`øÈM9· |¨òœœáàƒÉ¬r‚®¿)›¤þ—ô,ðýæFÔßâ·8áÙõÍâ\ðÌ,¹Òg­“ˆ¹WøvÛ\ôÓEásL=P®çÖËMzÝ—/:ì´,‚Ú-aÙçxf$õ—6-F9•ß4†¤BnâAbð,žpP~{÷å<ù³$ãÙ•úÐ{ɳØP¡ÂÚ‡ã½X¿þ»a±ñ â9`¥·3Å·ó%$WÂŽb6½$žsÆý\?ÆT=Ùcƒxy6\ïÓXâ9¼wM’k_eÛhÈuûäK->TÿÙŸ_h|šY¡Ý³þ®Å91!< ê®þU ¥qÔcUy‡è G“¼nÝá@ý<÷^çÉÏŒwÒj¥ñ‹ú- GãUŠŽAîÎ+æ&‚gáò×*©ß=ieO'|™9Ìû0Ùu¦Æç“ˆgª÷¥9ü¢B¯È·‰gÇ7yçi|rg½w(ú»ÊÅ3~K¤~Cå»dï÷Ž#¡íÁ£ Öƒç¤Ï[vÉS¾òi…‡= ~¤ 9C<+œ¨lY‚Ÿ:É ã‰µ%¿Œ5Rñû4ßqæt­ÖŠñsª¶v_ŽÓé £—ä¿¥ûù"?õ|ØÕ»ûçQRÏ2'ùþw‰çÐ!·‡ã§ô¼Ëéú"F¥ýW¡ÇMß½­‚ÆÝÃùAÆ{YsYŸúË?ÏØ[Ý7@J;Œ 3Èž“«fѵw¢âuWáÜtúHˆÁ³xÈ.Ï4ò7¾£käü»o»ìÔÏÕÄó‘íÃàW‰ }·H×-žCã,‰é¿¡ š.Ú…ü‘Éܘ×B<Û×ýi¢üÍÿÐã‡ø\JÅß´ <‹òŽN¬B!z’'TÚÖ}½åÆïW 8~êiÂêÿôͰ …Þ z½´ô}˜Ò1ä_¾oߣ ÿRú'*ý"xÊP‡ å›UäõØÒ}ù-çx¥µÄ³bÔì#ºø™«›‘4GÄÈF/öH…ÜÖ—é<¡ÏUt{t_ŽÜökËäRèý§¥QÞ$œY=ÅÕ­û綬‹gKË/ƒžÏ¢•o—àçü•'AϦÆ>oU¦~꯾N¿…}Ÿ½51&”î^K<»oN~ KÇž»ý¾:,åòIÊ[ _<‡±^÷~û8‰˜»S†ðZÀ3³Tg§›èÌqè+óàV;ñœ”|³žâÙõ+QéFˆ '¤w® ž;ÒǼ#{{w¬âì¿.WÔæQƦFŒ¿¢±¿ÎgáóFóê#ðÌ QS° &› ]Í¡÷–›Ì”T‰çÌÀ—õ«hgþžÐ/¾ÿ â¹uûE3kütŒUì9HDκq<3ÅóŠ­Q®ÀkûõÞ›º/?zÐA©·Ä£r­ÅíÙ$¿5ëhÂ?ªÚ_ HÅï*—•Ò|‡ØìF¡K÷åÔÝ“>¦L<÷ºz’ò&‘Zº¯äzyŠÞwñœ½&{S ñBfRÑ#ò'‚áS(Î_t>zò kÕê—‹ße¿(•P{Æ%wv8tß.Õ OFÏq¾ŸŠçâ§^r§õ,ÈûéÝVOâ¹ùÌV-â¿jÌøÅàŸýºMw&ß 5|èJùl¦÷¨žøüÑÛÁÂ÷ÄóÇÐozàŸýft©É®ûz=Ë- ̈çcn»Ìà§Í £óà×ük>n ž_˜k[‚Ÿa'LaÇVU#žŸ^j½CófYHﺷmö„”·Kž*œ€ŸžK_}‹ï5éOтҌOÖ4¿+!N7ワЧN‚ù4¾r£|ÈhKêÏ>s06íþy†¼½kÞjþœå߉ga¹~Ÿqø™mþH wJK8ù™x޼%ØKrè¸>yúÊîëUݹ¬· ñüèìÏtUj¿:Û0q½¼£ñ,yæ·<ÉA¦*ÉOMÄ8êæÜøE<ﺮEåºÇü¼cÜ}ùÕ»!½‰çªÆ¾¹Cñ³¹¦§Š,ÍØ–žLÅï1Å5òÐ[fOȤG‹»/çÎl§!‹ˆç_%vdNS'í[ÔýóŒH«‹çìÓ¿%ž=Ç·Re¦èzóÞ),Ãý´‡%žƒ_$,béb×,ËÄŒ>»j+ñlqÙ®œ¿÷ŒóÕ3f™;jc žÏFaiWáïGJûãs†ýéý‰çÖ 7cPnñ•Ó£l·ÍªŠªo"žÅ'¤ÑÁ sN½ªÎ2ü·ko&ž·®ûî`ˆ¿Yßa·˜eDƒ¯× žmz~ZÆÒ¥‡ƒ4ñùü(çÄóék9òvø{_Ù Žz=[bI<óU—ŽÖÁsTFžÅ2–«>¸σ´.Yˆ¿›®žcªË2å¾S‡´ϧ™ ”+,øyD²ûòE'âÆõ"žã|æO‹ç’nùÇf™G§·ô8‘Š¿äoÜa†rvÌ2ܤûrŽì™ÀÏIãµ!gaƒ}ø ~÷Ï3¬Mϼ\3½ÄóÜÅÒsñ|§E<äë©våÐlây˜úð‰Tε졌Xæ»`'ñ|zâû«QÏ¥'u,Xæ{aý×JâY{©ùa[<ïwÄÂÁšeL5TŸIßÎ4ýv×¼ôÿÒ½CÙÊoƒ‰ç²ð}e$×ô–‡k W•ÓqˆgïBÑ@’Çž¼‡V²LÍÑøë“ˆçÞ/’œ_¹VdÊ2U;N™¼éŠÏÇ¥ÜÜPoKÏŒ+뻯7:!L9ñ<öíS}‡*Ž™>Ï ÐJÒœÄ2T”¬Ž¤âïµìäפkW¼^Ó}9¹Y;®Ï¦ø,3à'ìExÌ æªu÷Ï3¢€.ž…af_Ïg&ËŒuëìÞK ×ã«,GÏu§+TÀ›°ßìÝËPž Óz…9xî1^^K| ¨î•îÄ2’«/ÏŽg{SÎP¿êÂ2™“Óú¾¡þ³úѬk;YFüÛÏçǶîÛUu~e±ñ*[<Šôú’E,ä+œÝÂ"ð,¬ (_åˆß/Z]T\ª¢5æÿ¦þsaîŸ$o :øÜfÛδ;àYøââ»~,“ÄŸW¥éÓ}½ª×J#ÆÏS¾ÜßkŽrm ê{iÎ_kH<ÈeCþBÅ Ó­XF/XÅAD<ë|ÙÞ¶•ôýã0‡ÍÝ—Ï®9%wõŸ¥/O›‡ç´K·ŸÐf™v³¶bŸTäS_¼û˜m‚>¾ç½_¸±ûrøéõ9#Éž{…šIÝ8–_žÎa7 {º‹gæãö©%Äs|ÏÇ_Wàù†E¯âÁ÷™«ù}‰g¡áÔ¡ÐSá$orð]P±mÀb²g›†MžðÃÛ3‚\YF±¿Šè8ñÜò+Ë å¿›)¢æscÙ‡”o[œßT³›exã®K;ìâx¯¹ÞÆÄsÎàOªæ$×·ƒï–Ñy='σï ~ãŒöXß+Ÿ‰²ê¨_5©R×òaCN&lÁçyÏ{ÿ.¤q’ivzµBÈ#5>GÚ¿ûz£o«îïM<{†i&ÀO1·ëk:PtÓ÷ZÄ3¿¬|‰=ê}âíã¹~äÅŸšdòÛŸæ-›€rE ©=7q诗WB×5܉ñ<7Ý`{øþž‘ím—Š¿›<뜿wÚh|~G;]{^ëK<—=mî ½bËößJóèþyFtç_V¸.‹ÆÑOé"øCæF9ÈUrJCk½ß¦•±m°Kv™K}Ç:–}ÍFcÙóe•(;èqÒÑa‹ZÁkÔºÞa.Ô¯Ú±u»Û6zþçÀ½h÷Wþ¨¨ðÌú JÛË2Ù×þ¬) ê¾]UGóÏ®j#Õ!Oáã²ìØâ©>²õ4þ™`öú%2øç"üñŸWhŸÈ6~ë_²'›åŸðyúÏö0âÙÍ:âK0ËÈúÍõØÓ}½Îg3_çÑøg&ä¶=ž{¢_ ½–Kš8šæu˜°£¶¯œé½öÎ9ž0!ž•¾«LA¹wûS:ÂîËŽ[œO<˸Û[à¹È¡ÇõX¦Lüür*þ>×9ª…ôåM½ïßîË1×Ñ“ü°ÏšÀAà[Êoâëï´ U»&£ÏvÔ³wÞï8Ôóãç–œETÿ†æØè‡E]q(Ú;`Gœ“¤}U«‰di¾}Zih÷íœó0£œRQðËB÷éFÊð—*luâ9šÏøùpÉEÒc—øÛÖà“™ï­³Œüö”FU]Ø“`Öìh#|¾߯ûÎÄs˜§ð\xÞ›0t|p÷õ¶TÍÿ¶“ÆÃú$ì{¶ŽòÐQ^ãì‘ÿ9é {NãÜ®8|†^ g­þÝ|©×Fςʢ¥(—½l!ÜÍñ^m›½¶ÏQüÈ[™M?”w"´«ß£Ÿ§h^¹ÏóGÐñ™$ÛÏ~Ý—3–¹Ü›Æ£DoÿT*!Šžö8ÚÉ¡ŒHÞx._7böâ¹Íø@?Øãñ·|øö~ZXïH<«•~ú?¸äï¾ïÙmp < ãl¿~¦x›g°M|í¿.¿3™æ›ì<€úg¥X=ßi:…j4Ÿ±öïƒ+ûaÿ¡j?ÆEuß.ÞŠÕû‚Q~³ÜKG²Û9q…à» Zÿ)Ù³õÈà“·º_HÙïk“zšŸ4=—Lò©s\¿ Ÿ·\¾y ÅçQç7î#yÈîøÖ}½üên#Èžïjõý}ÚÕl‘EÞá9O:"žæ'vƒÔIû´´(öÊ¡ùŒ“qgw£ÜèÅÕzÄ[ùØB“x~#?ååµ}ëzß ÒΙà™×R‘Ù+eT§EÍfº/GðMîÊ:wü¶~â[<'¬ظ³ûçv^Ï¢‚ÁGšŸÜa_cDùž³ê÷3è'¨,\/Å£qÝ‹£Ó¼Q^¤õz%¼g”ÚÇœÄóìE啨‡}ùL?|¦vϺ»>ÏE{ù›>D–B?›+ìÏ|º b’z˜ ÜcPþÇSÖŸ¢»oWôí¢mºÁ4/e`( »aÞ3}‡ü (tåˆßq4Þo¦ü zÅÞ¨”Z¸ñ×¶Ô—xÖ½ëõq?©ü΋µø¼ê û‰Ö•œ7ì½øÊ¹{ã>‡~5/n*Ì£ù4?»Î÷àQx²çµÛ¨_ù²Â>Kð,*WY~zÅ3Û™ë?Rô}ÿÒeàY¨² =å²÷X¶·ûòE‚Ceßi¼lšx¶õ‡~_! ¾:Ô³%{¾åÜ> þèÝšÁ#8âËäý%Æ€g^éÔûIs¶³íD½`Dö]<ó~Üz¾‹Æa$Ó;íÑÞÙE»•¯ëÅŸüXKó4½šNæPžsÑÃ{n _¼hùm)Ñýhêþìô£àSåâé“>ij|“ã\èµpÿÀÖç»Y&WÎ}U­7¸(«»ùx7¶zàÁîÛå;ÛþãGZW"y=a=å‘:âΰ+Ç#qû‰çX?…蕨w¯˜‰ˆOåløˆçj‡í~x_AŸ+*™øÜdËùØlðì{ßpôKtýê†~û»¯—ÿóÞÌ•ÄóàxCOð(¬ŸÖ„ür@U†ÕPâyrÓøëˆO‚fã·>ÈG–ØHe¿Î€=k 7/ƒÞ }o\ôè¾üèeƒ{åÏëU7-¥~aÏfYäA_9 >Eþb]ˆ)ô$I¿ÿ.Žü…õÖèõ r¬Òž9ó%”®épèÆvñÌ;ºVÑ:±¾‡m*—˜®cÀwt ¨Å…üÕ>ß»ý`LÞ!­t¼gtɦkè¯&¥ÜÛæ…zD7[—fÏæçæmýˆç¹ã…h¯ªE^Ö,jï þ9àYuÓúRÑè‘{ý¦±Ý·K˜v/ÕÖÃÈ[Ï7¤¼@ðº‡:ü¨(t›ùÀ8Zg~¦¾z%Ü«_oHùsÄ£‡¶´®|Û¨ä÷LMÎáóÛâ×g6ú‘oßÃø¾Û6½IúÅ8+÷8GëB •'<£þBNZ‰y,;ëãß4Zó¦mÇÄAAûcåðoú ¦²Äó²~+ç£\‘H§±'‡ŸbºµϹƶÑÏúI Z…<¨`Z»kL ­?d¤¥‘¿ˆä*OãÈ_2¦¹Ò:­ÓÃz ^ðƒiäÐ F”Õųàáoâùså˜vØÈB¯Ò ~Rfý ·©”<ÊõõBÜcyç÷)Q¿áµ–À<‹~çlOƒŸÉ~Û:ë3ôÉ`Пü»ÈÃÅÑOý^…Ñ8‹Ä†S¤oæ= ÿ"N&-·ª”e´-c{¼<Ô}»<ÇV&{.8Rùú‡Í£ø¯a×*ª?{^¡õ€º^ï).6 ‰¸T°¾P¶­ uÚ<.ˆú·ê?îáóo/Œ•ÏÙ|¥GKâÐþ»™77Çt_¯ø÷½7f´Nl\Ž¿˜üsÎñU²ë‹gzÑ:gÑ2fúž¤g¡7þMágݼ×éx¯áSç¢\™´Qòþ‚}!¡+C<§iô!ÿa–xEŸ‡]óæ¼½¾‚Æ=Nth&Ãÿ‹Ê4nßãðÿ–Ç%‹¦‘=¨ÑØÎÒ!ÑGB8xfvñ,ö }°Öïvy^Cý'çU—`×bé£1ô~Žü®‡<¿vªÑø†`±ê •Ô_]:µ0‰øØï–øÔ3\–¯Mý­wdY´7ûE¢hoKÿ~óÖƒçÆ*8ò8ä‘x|ÍÙ#Ý·‹ßôeâfšŸv•Žü|@‘¯»>£eõãäºôÍîɰ[º·ù7ØmÕ–öKÃŽâïBžÝ !¿×ï~ô.°ß5¥1çPN@ÞèOÍT¹C¿×ÉáÛöÒ~“bßµ4îÁ;h{v}fwçøç´Ž»°sRâs½È´vݲ¯ÔL<³©^7¡ÜhqG•*‡¿HÒH  õ½Ú=oü¢ñ×°ÔG«aת­?¿žB¼U}¶pÒ<è‰XUãë/ÿì^iI,­ãöxýó)äœd›èó™C/Q{Ï©U-­þ”‡å©ŒA<)ÆL=»Vµ»àµ<‹ÌÊÔ¤|?³mzص$Ï=¥ñ—§0ðôÉsxOÈ7úý‰•Ôß;(y‰ßʬ«{è=˜ªÜWe,xæ{Mj—Lb™¸š”‹yñÝ·‹—ùçÃâùÍÒ§ä?ç­žé»n–›£r<‹†½/™ »eÜòOPþqGzbŸ”~™Ø'^“zzP¾ðûþSg¡_r:·³Žá93Ùè†ÃÝ×›½ÃÇA< —”\ÛN~k«Ò“(ØuË–ýªñ´®-¹2|(òó×h´WŸŒž?^ 펞°=úTcè3Žöÿ0·úΘ½”y6aÿÄ›ÌÏwþoå7ÉÍø<:èZm4ú·ªýJ|˜D– -ŽTçsè—h°KõLÚ§7ù¸?Œ•½¶…æ]ÜçH=Ë'½½#¤~õÏÈ x­VµÏ ŽŠJÍðO §~ŒúË‘w09÷·XÓúI½O—nì]м~/{—Cå5OsËöAO¢/lŒçˆóÂØ±E9´ŸÍÊ8=rŽöw(Íä¨W´¨‹gÑö/FÏg#×PÞ¡¥ç×ÿ*Ÿû[ï'˜x#ûÊn´ë×è³8UÇäüdö/¥å*hOö®±ïnB¾ªsÆQ~&r‘^ ½¬ŠšpÖ zà?mâÅþà94Ì%0,í™ßh~ùx÷íÊÞĆ>¥õ=?/Äß¡üö¥Ì{صjù™ø)àYhôH{ì–çµr©ìV¸zÿŸ´oRíäåðwl€™ôGòß®QÔ¿­JŒÊˆÑÎ÷ ñ;ÊQïèÁYþ]ód­OÆÃo‰%dì‚ÿæ/®?¦@û\ò ÝÛhÜ6^ýô]c…÷‡?§!ŸOÕ<”+Ø•oÌ„ñzÅ´Ž inÿCÔÝÆãMX9÷U½6Šâ툜Âç;ØYpÄyñ•¹7´À3?uéÏK³êÇýöqð̺tñ,\úZÇ–ö Æ^ÈXB㉓6úï‡ÿö¬Ðh ¤õÉÅ7u6SÞždóÆú¬`ºµax†*Ô•‚çèÞ³×C¾¾ýª:ĈÛUù;?€^:Ëö]8ŸkH¸=Õ@œ÷1šß÷Õ•1Nâ·r‰¥í“{Ww7ræeì–™ÿ©ÝcžÜ¡”SfaïÿÁÓ°zºþ;.ïÐ CZß=^9èôZðô«²Ÿ—ZW~mF>\°´ÇMø‘v£/-ã¹üÈóüÊ'Á´îéÁ©d¯Wf¤ÔÁ \ûÌ‹‚þžÓ<žºúÓÏaöaè{ÜýãÇVÑþX›ð%ˆ ªIU™«ã8äq÷iÜF¼×ö™á¬=žS»kúþ[åivsígÉ}RМ]ºÇñ@÷å´tôÍ …=µzžö€œËŸû¼ØÂ•ç‹¢þùí·ÍÒº)Í%í!4~~kdˆüwöéÞ%_"a'“yºß`7Œ`‡ÇÞ’wÔÂð“IáËÿJ£=-7ÎÍŸGþ»Þ`sêÇÚ-½´Üî·pôòƒ¯î‡SðŸâ§}¶¦@¤4ßYè¾]2êû«7à½y¿‚ÖúÒ|ŸÛH'øïæ²Uw£hÿÀŸm«‘d?l®k‚V½ïwL–ö»?š9nÞ׳§ê8 |^º:yf åIrÛ÷&ÿ >)™Ø}½"ëa×iŸ [ûs«óÝv<¾þ›ïádWýŠsê%Oã1Û†Þ§qlé«~“RÑ?OÚaúþ©Å1Cñ3G~Éš°ÛFÓ:®s©Ji4yYqøøo¾o{EùáÉÅ?çÛgÊjÆrÄy˦ò²VÈ™5­xðrŽ‹ÿ´?˜Ãþ¡@]<3÷c&l¡ý ßmeúSÞsô£¹"ò[Á•ã¹´ÿõ[öÇ}°›è€¾Ïþ‚gƒ“žß4hœ¹ªVá0ùAÚ}ûÀoX~×[}q[õûë¤ÑÐËrwÕ‰¨¿týkÇ/ðŸÙÓŸÅ›‚<.¦«ÞåàYlêr‰¡ý¸_zÜ–ƒœ“4ãÂ>‚gIÛiò´ uOš ü‡çiÿ/;áŸD‰ƒƒ‘ŸÉ´Ý­; ž«¶VGäƒç”»²¢zäI<ÕJm¹“´ÀçÉžU÷N‹ŸD㪽Cu(ßng)ƒ~Ê—þà™mašEún~“…Ü| ö'Ÿ‚=7<Ÿß€r2îo>Êÿ…¡Éó7ÓJv,Í› ç¹n¬ϼaÓޛϑuŠrð厗ÖÈsôÿTŸ¹ë‚gU—ª˜ÈYáo™ß\<‹jÿñ\«û~8ícyí÷âx}f‡ËÂˬ\iyŒÖ[?hÖH"»qU Ô€ÿnè¥ZIû«² wìøñ¯£ _KÙÄwàÏUÃ&!N)8Ù¦UB”çUW™Ã²ÓFÌqH…ŸŠw¹Æœì¾]ÑŽçí¥»Ö%š…¯¤þjSÛ'søï‚çÉ%ÐþöQÖðÏIÒ®ÈÁ?+(Vz,AÜý±ïË7½ä>—Xèqp3ò¤¸l£µ½áGTýììàˆbóÉž´ÏážWŒì5éêêˆ4oýðƒÿŽ0Ú‡0íÀ°K¾·a7ì½¹¤ÐƒÎãh9õ2Ê-è(e×ã÷7»û]Àóêw?æ¢ß*êó`L{´7uî¦4ß¶7xŸüŒôÙ/Ž|®e½t~äÌŸŸ&“9kßÛ\;“kü‡íüŸ/jD÷ÜŠz^Þõ¼k^@±Ö—æî.§÷‹:_ݨnä™ÏÚ•'VŸ£þͼE|Èo¤+UµƒçЭ¥ïoÐ:ú¸K,üŒÕÆõ¿ã7þ³åæîC;N³Ì#ŸJ—í< ›&o¢ó!DšŸW&@΂Öq÷_ƒçš1ùÓÑÏK ýÛÈÀd×g«_Ï*†ùú‹é¼¥Ö †Ð«$]›˜Uˆ#ÇÖ”M2FüÔOjê?¢ñ,c¹+‡ñ¬:¨%CyþÇÞ:jÔ_«šwˆòj¥ZóyÔ¯>ùuj)åùzbð|‡9ñÌ—æaSìu¢P®Ì¥A“FqÄᜡ¹´þÉ…ëú4µ9k÷6ð,Þ~Ô…ú©&%Ö{àdïêõ‡ƒg¯ÇýWÑþÓ¶¥ö…sÜ¡I3¹x)ïèŠÏ£²”§l¡<¬7Kó3.ÈSœÞš&³Œöûxg«#¿mÑ(¾~ËSdåtŸòÞ8«‡ÏÁsöÍÇ`Gžý_—ÜB>$ö18—‹ú£G‡ýíAþçÏìâ🂲W¿‚g ÕþkµRºo—èÞ©?î;Ðëú4/h¶Ì(¡ù©mÓ‚)ÿo:çv<'Ý‹TÍBžÏ+½ôs'üyôƒ%RÀ³ÀÿÏ_Š#–)ÖΩÈÃîí?·¢~Á›/ CaW¡k‹Ò—Í휭|Š£]º«ÎS?OtuÒ !ÉÙyÛð§ÈÇćtçy#ïe'ŒLÞÿ!jýÖÞ„8,³ëêعÌÊÝ–{¡×þ½T(ÿä•Þ¿vŠüjï±'6!^8ÿþ¨ÜÌÁ³ -¶‘ö¥ñ6žP´Ý =Úí^óm'Í[™*¯ F{J<öš@ßC‹Œ퀾³6§ß/ })wu_C¹qÇv\?Âÿ…Ù¿:iù윻ëñ>–Ù Dœ®*êµn0íÇoTIZ¿ßòÛînÇxŽÈIù†)â&¯ðÚ[+øyÓù5cVsõÛEÿxÞroÀ)/Ô?¿Óýx–1í'+8-6XúÕ€ÞïâJiØ ê“ëqâùé»s³`?üƒw{úBï³¢‹O‚gOŠ­)Nj„_ Fýâ¢oƒ¦¡þ‚Yïr§ây¾èxŽy÷0Ó‘ƒg‘byõÊÿ§¥ò!çlãG7ÆÓx«Ÿ{­"í¿>p½åçÌE>¦úàúTÚ§¼îöpš—ª[¿Éè4xVpáË^!¿jêùU~DVt b G¼`6†mœžÅž†iä—ùC×M0¡õM‡ ÛѼJné ð,Hk?™úíë,|vÑøß½¹o£Üv·ìq¿8xfRþh}rD{|Œ¥uMâí ~F”żÍ?Èý`ÇHøý”‰òñã¹úgŒÔ—e7 ½=~Þ@!×èÏllÏÂãÏt6á{SËzhÐücÿ§ÕàY+cSNçH<ß[ ž NÕ0A4/ñà¯ÂÈ5š º$ϼ‡c6ÀŽªBE6dç/œý žò¾Âç•wJ±4NÏ/“Z˜Î2zמ¶¤vß.¶b±ß4Ú?µ,ü9x®Ò­î©†|Lð8òàä…‚‹C.­¢8wm’K$õ[³¾ô‚?oYewÖ< c;¿[Ò8QîògøÕæ°È0?ð\µ«³ý#W^ ~œ¡´“Ö 3µá—CÛÍTø4þµº`ü:G<ò°å}á›ã¨ÿñãöà2äO2µãjQî“Ã)§9ò<&SS7Èú5né´¾5úÁŒC>æ¹óÉ„ùtΉ ¯ÍIðì¬>zÍDŽþYKùçw!gñµKNÁà™·¡óÏn®q{Qá?{>Á³^ç¹®Ò –¦q¸É…´îFô¤iÃuŠW^›&샌ž`2~ÍOÅ¥8L‡\%;þ.¡8lãòL žU•;ÚÓü–¤ÙÕÔcTi‹úϼî¿/–âXˇç‹3XÆ^\þ¤ŠƒgfÍ•GÈ+úRÞ›»-ú>­OêwÍsâ¤ÂÛž#:i¼ß튕"ò-Ï}ó{ô{d¾]¯›½öNÑ;S‚Ïãž¹ÌN„½µ¯Sþèp†eL"÷ìZÏ‘°K Ô/ªöæUýÏÑ„ûs¨þMÒ¶=hŸ\‚¶¹;ô=ÎÝ8T‘ò1Ûç[t>ÊA¾œ=ʽjî½l GžÇØäJ%Ù¡œ’Ã^¸ Ýž3žÏIj› ÿ€·šúÅ¢rÄw G•"žy§/•S¿£&ðoâ¹êÖó~žaÅÿx¾Uf_JçD”)¸ÏC¿¨Á³@3oQú±If±·ÿ‚ç‚wŒÄrð,VŽ=‡8)“¨æ4þ%FõI õ÷Ø*u wØyóê¨ ®¨¿*"øÃCšÇùݳã8âX•ºWxäí) ô'œŸ¨ßõ uŸ.ƺÂε3üxÑàÙSߨþùõÑYA‰x¾ÇäÏ7ÀóÚ•F I\ùß!µ«·hus%£OùæÖŒç¯P?ÏõüYÈAÕ>$ <7OðØ <{ÖM~j‰¸Ê—XÞ™ƒr³épñ¼K0=O97=ï¹dôôÕîç/©¥u|CÓAÚ¿«±SÆáóJrôÃŤTBΡ5W~CÎÆ«–sñ,ê½³‹g·–•'7Ðù}™:ŽÆ•C\ܹÌqß³<›½VCýÍãŸÔöÁ®¿]ùõ?rY{=…êO±? òÎîÕÛhT&xž4³ƒg¦L!4ùBKî4q­'øÕR3„Æ!7nu‡Î›9œ91’ÆáJkSÞ}rìˆ0ØyMi?ïúXßœ1.î­_Oš¨…ß²å3x溢ˆúѯDoMÉ?~©IHϼ/“NërŒŸ ­vÞ„œyw¾_ƒœuæë2àÈÛÑÑ~;å¶í:ðuN!í£à·ªgÑá|^Ö&:ÿâó{ä;Ñ]5ÌÀ3;hù®ˆWqi1jˆ#Ñc5ó«Á3oÔ‹«¯dÿù‘põ°G³ªÀ³ÌÛ³ïƒè|ץʞe¹Ë^»e¹x^³wð~ô3y±š™dÏkv4ûÏãW¿¡qxíì7«Á£¤ßhùpðÌ®¼åºñ…ßËÈÃqBõHàÀ@|ž´Æ1Ü þ0ûÞÎ?à™¿~½šÏ§^I†~ñûÍÑêEëô;f[ƒga²DC;p®ãŒ2ô]¼§b‚1xfûôÔ× C|öþyÊ;l¡y…‹çì‚ÁÇùðSq²·ƒÜðý/ª97i_ßí¼Ãwàb~„ùIëmVe/[Æ5N~q×ãÈY&>g¯òsOM# S.žÙkÿxÞ*îýÅ |)%Ϫ1¯ø´C=ìP/è·à cÃ>ZŸ¦´ì¹Ã¦V„x•½ªÇo+ðlyå›Tx–^“rÿ¬á¹¾-àYõYŸ%ßiýŽêaòó‚ÙB]ð,qj] ÏLû‘!ýí)3=Pžù¿GNNÞAþ¥ñ°Çí¸Í¤•ÆÍã.mx‹ÎC‘´žhž•ón LÂçÂiÞ[w#Ž)¦»Mˆ<§ìbç.žoÍPÜé$b¹ã²ÊÔoR³ˆöÞçÍî,G„×ùs ŸæOÜwôñ øu.ò|g0ïTråÊì1®&™£| á“–A_2=ÖeÓþÍOq5oÁ3{%`²€În›±Gy\vDõß-\<ç7ù±ê¯ó?£Às{i‰/§=7ýãÙïÆ¥‰«ð}ƒ^¢yÀ€Ú¨+Äó»4ó7xoá¡á1๽*|ão²ç#gSJ6#j³uYKðÜì#Ûõ ·×¶MG\µ4žÙ;í,Í{ Ð:ÉÅóY_‹ø7ÁÖ2w ÈYd<"c 3÷ßU;­ y8‡xÔ[¸éÙ³÷ÓæÊ#¼³{ž=gÆD}§ù›‹²“´!?vYÑAWð,Ó4ÿÔG.{v ²ˆCüÌÖ1ÔÞž“ìÛ”¢~fŽÎö%«a™åg¡ÎQý²ç½é<Ì3ç¤|ß¡\ßcÏ<&qñ¬÷,¬`Íó Ù,Gû=Öï{Ç'ž×¦n§ó“üí“–Ÿ ¼unm —=ÿ0r‰ ó¬¢nü$9Ç¿½ÜÄiÏ3ÿñgõã‡.ì6çPígIž¶ô$ñlqÔW v.v.<¥„þKôpÑ:òۗ§íÞËø¬ëõ<{æH3&žÙ9ù¥t~äÙœvÔ/Þ¤òŽâ{–?X ö/)´W¹žõ~f¯ãâ¹¾ßÖEK WiY# gq´IÙó€€IÄÕ ]ãUàYl¤·¨xñ";| íë‰Zð <óû­‘ÿ@y˜ÜûR›(¨¼žÿè-mmàâY«½ôîRÈ/HÆ.‚Æw—Ù*¡~fRm«‹ Úó=séSg­ˆÝ¥uôçW9BÞU¯¿Íïsíù*²â?×ö^6@ ò¹¼)}ñüíFÒâùäÐÖhšß(=7Á<+èšœnäÊÃ×»®p€ßY9ØR ýí3ÂÙŸqÚ³Å?ž£5ß·¦õ^Üž“=†ïìÚgÙb­‡vñ-×¾…=Ëð…“gÑúÑÜô ‹÷›+;ï4xÖî/•O<™œ]½ò®U­ž› —OëZ§ý]îO-äWžKÛ ö_íúî­Kî±üÈ}âù{n1~¿£ðî7x®r¸½uñ¬ë¿ÙÍñÿûŠEù(_¸+·ÑxœÜP¸r®ÿ<Èõó*Æë†“ß>6ãú |Ù ðÌŸ¸rc%Ï…SFì`¡'âzïÞ³ø†©ñœœ?ö»èÈ•Ô2‚}‰ç7ë²ÊÍX¦èƒé£à9ÛðAÍ0âÙìŒÜkÈñ‘s¯-Á³Âú‰s†rñ¶ÈÎGq_1äí'R=š`Òˆú¹$Vn)üY{™zxöTöíEñËiÔ½Ž,Ÿ]!ÿ<Û,?¾Ïˆ‹g¯›'ã{î6Ãè¼³°‡Û‰gÉ#ïWزÌÚ·gÓ#Üá%W¾Ý¯O-íkô¿=îšxÖþ}b5§=Ë »x.~Ø:‘ÎÑ©þS·uíŸ;1ƒì¹®åúÈÕwzã{wêW­O~¹r/­×‘Þí² x{ñü¶sàc'~>p x‘KUT¨"ž›Ž\¾¹ ¶ÕÓÚOùJÔ¯ú%©!Ã5VÛ±€Î—)Y·#‡Öi'ªyÓþµ>GGÁû®¸7„æ»kN=™¼—úÏ‹>ö§ùi Ïç/ðlìYCëPEé½Ân¢ÝÒ·ùžs-½nàâYm~º– äV§•>€öûº®Ñ±ÏBóM‹³=ÑîQfz3iœÄqÿaCê?çÞ˜y¬=fa Ê-ðŠ.'sÈ»ÕàÚ*>Þ§÷6çÄsC¥Û ßž¨åï²rÚ}P¾xŽî/k½”ƒg^¿¶Kcƒ|½åAÒ¾™&œö¬ýçm .;i¿áØ}ÊÃ9^HûŠjÇ4åÒ9 뤟Ñ>ª¤¤ßIÄó§¯?ð‡uûR}ÒúD«s/Ð<ÆÏé¹KÑÞ3wbÕŽ€çè–·SSÁ3¯Ç8ožó3ö@sË8.¡ßÌ1!œjÿÞ ía;êéü aúqÍÅÄó˜†ìKhŸÿß¿Ëä—Õ︓=kîS·„Þ̉ÔÒÏâ ™«öÔ6ùœ¶ú%•½?¦xÑ7“Ñ<³ýäç¤AÎwÆú:‹ì9ÓÈn2§=¯ùÇó«ûq‘ðWüˆRš—4m:Ù“ìÙ\üÖþÙ×ÕÂïåM‡u‰ç¦qaãà¯âvÞ ¢õÆ!r5©4±Ö'ELçúô­‘¥ø"hš¥{‹òy&îPk>ßdV#Þcûh¾‹F¿z_¨*€çnE·"O|Î^¨^?ãÕîòõ ù}ño8ó™öŸ§•qæBn­–Ç¡|ÎSóm"?°!(Êö­ï—ÆÜ¤<¹-“SÿRÜõ#7ü"ž—î5Ê‹v-“Tbn3å“üí›P€¸—[7¾eñl|ñú!Ê×wñ/ :ZÜ…E3‰gß»E ñ0»[µ:ã\'”}òƹmâ꺟ŒùaOxæõÙ×¥ó‘*Ú;BOóow¬¢º ¼ä¢ ‡0Îo•žÑyH‰Ú«îÏ/7ßvÇï©í&”7d´:ëîAð\‘;aÛ,È—s×÷»”7¤èçµÏ—3f°ýÏm{]ݺ›ô÷ù@æu®žuk½ŸÐÉ)14u⼎aœ]ç¿è’'½ìítò íë›Ëo¥¸VоFaùáóߺÈS^¯W¯µ4Æ!*(qvü!çîaº¶L~o¤ƒdæ'7;T›Î-Î;oDõŽ®™uø£3J*Ð9PøÚc”·Öj]FúÛûà‰ é/uýþãÎø\iÄÐMÝ Tw²ðþ}Ì+ïãVaà™Wº´ÔúYNuõÐ]Äs»~¿-Í3™ËKà÷’sK ÈÝ:/?zE]ñÊ.ZÇÿSþµq3ç–œF.Sþv“œçø»ü¼'ÇæQº–¢Úà¹"ÇZ?ã$¨ßmûâï„f6ƒ_•óгïé:ëÚkÚ/¼£4âcþ¶Þϼا‡¥éXJùB×þl ÿìsÆœa°C£#CŽQ<5c‡V“þTÞ¶c⨰ ‘O¨ܺ‚07ÄrOΆ~žúæo‹ ÅÍ;Œzݨޑó®š,<¿d{ó¸s!?Õð9ÿò·%û1ŸÆŠ·Æ¶ã§÷æ £n£¯µU$ì°Ç •¯ä«Üæ}Â_tíß ø%ùc? È?ˆ_:CêZ>Lªø»À=ªõÁ íf: “bâ¹Õ,¼˜òx‹b?ëS^Ø~»‚[”'ë9 vVD¿Ò,?ûÿòf°·Ÿv%²1¾6)vŠHDÕö a”çkÿ˳ýà Ö¿Ô_Ñúב{%°×üÎwçÅè\•xèÅtcv¡ó:'X½»ó)òÕµÆ]à‘Ã?²Ê˜äÉþVÜ1Ì/î’u )¯cD¶ä%’¯…‹ Çw'‡zì‡þìþT¾¿ŽéœÓá!ïŒ1¾ê ‘TŸŽ{pìFØë°{Ý«‡â9lÓÝTwéÑM­`Ú‡\$A~Ììy·–S"¡úRâÇÓ-9äeiïZBõ÷fkÖ¾ÏÞæ³zÉÿÒ+÷‡Öm|?$L&û°FS2Ÿ³‡½ïEyȺ;%õ ?Æ®Rm]Oú£"ú}Sý›ÆX‡êctToc"ÅsO*rhž…ù±ó)uê‘oÃiÝ;Íp3Å!Þõ‹— ÝÌß­ÿ¡]Á¦ÆLë•…KnÂÿj1ÄíÕ×”SõþwÌê+F1äÏm+ ¾ 9á²Úv òÉùÞÍÛ¹3T( úF\Y½|9ÆzxæŸ[žKu«—=› »Ì “Ý…8«óÁcu+:oc³ê¯1Õ®2Ûvï#úÉú¢N8ÿëB´ßøÕíy ôiàš úñüÎ,¯±5è'{îÉeÓÀóãþöSbˆ›ïŽä&HçÎiViœÄÁûÅ÷YVîƒqÛ+±é­·ÛE>òƒ¾éô)»uívÜŒ ºd‚5žÌ¦ºp—ÂÄ^áyÕrÚç^É ?æKýÎÞ;zD£pî©{»¶_^Û·0ðÌ>´.)úJ=热Ï,ЮzJç÷*ëMæb¾·HRÞJ~ið£i÷ ïOoüϼëOì}ÑnãK‘„- ëX<‹y)çvÐý /Ç£ó°yY¯l©¾ÔÛÕš©Ð÷|q¡õ_(ßݰçsƒ|Jäø5˜c>…8¬Rç€g¾„‚äQ†úD,Á´£<§|”âÃþ ^ç-xCvY}»ëPâ™çÌwÃxZ>i ´ uÀƒžÂ_éý~ÞZMù}!!‡×Q|/8’ÙØAû–‡ÆVâùJC®¤¶EÒ~¶t]8Æ]ˆÓ»\6ƒÍzõÄîþtØI‰³á¯î3èO~Æ32xNgáˆÚJê—mNu­G†Z±óJ'cšpÆ] µÂ ö$Pi™øªÓ±&¿vôJ£Ô™‹´>9ÅÎ`$üû†ÝÎÖ-‰àK,ÊîƒýÌÜ!ü“CÜÉÔBÿJ澫+$rþÚ»Ñh×£Ëy6Óú뽪^œ ú—äp{$ñì׿ù5þÆ~¼;åçY:η³¿—å‹1ð\-~pßvèy¿sRµÖ˜ß™CF=ÿÆXohÅϬ›¥¥äÏž0VNuIÖÅ.¢}ÎÅÈg”/ä6nñXz?I˽bij¼×ép¼Gcy)Å75W4¶a>TLþ-6úGÄøøˆ4ÒçSµ¿]§úb+Q~A ²VÝ[²Ë |®41ÈǺeN;å…qš=­èüæß&ã—°'œ>ÙeÓ(ÿO¶U]‡â¼¢‡+(_ïW|~>åÿ™m?ñ…ìöÌããVRÛë9O)¿@º¦ü`1æUã{¿ u%8‰f”çÙñ±}寄]:pƒÎ!EX­>IvBùwÅRèuÁ,)·M÷ü©=…¥TW'ÎöÜMÚ‡ÝjÿÆè\cÖÕÏeäw~änBúüŒÃ1|.ðYbqöB»ÿê‚+$/Õ?2Tì§ú?‰ò7­&«Òzúñ=i´>ȪɩÇüÙÅz9ýb M.¯Š¢í(²Ëj‹‡–2ðÖ6éi<Þ'$(ãîK:/89¹û%c=À°žÙR’âÏÉßx~öå§Ýû»è å9¨†Œ§ºk.fÅgW?uó\dÔa³Ë/¹qÜ&ù9°öbÕ÷¬ ¨7$;UÚàìIý|Øç?ãþýÇÏ3»3á7,‰ÜŸDõZ$¬®º0ø¥Ü§gOm ¹ùüæÞm:·© Ùi{Ò]¤;æÆ!삟øJðÁ.ãTm;FûþåûÎCŽÔe­>{b¾±¥X¹ÍK›¢U#]`/8ú¿úOÅS= š•Lõ=à jB¥È¯[«¶éùîg{¹MGŽ’Ë(ª[/úäˆ/⽇ë.¦œ¡õÔåý¡\ò“¿>^ÝÇà?±†,×$9j¶ïÝÓÆ9 Óº‰öÅÒ†%BßujþùLûpë³lbêû‰s4½@u,3WWÑyÿÍ×_Þa¬ïyãž§o=€~³üÜoKyAæbGßIwÕ[P ‘,mZçåÕXˆLÂ÷:‹Z‚§Î Wâ—о´fÀÏ…g+•!¹ñxpÚv ŸjÏv`Üã>Ö¶¾Ï©úÇtËä*W%ƒ¿f!ì\Nõ|8ÛÔoS]…Hµ¡–çùµoÕ%½¦7gÈ,Šo$wï¦óO‚»·¢)¯UB1Mvz0­³ç}M•ºVrHöÂùÙ£‹´>Ù¹RCý=ÃúCfÖs‡K¤T~> }ø“7ßÙ`žuÍy¿ŽêÏöò_NãÀ[YÿúO½¦ø¢ ü<£ž ÚŸ4¥Ì}Âß<·:õx6|û¹šîq{¬ÿ‰îq{èú¢lì™e¦úÅŸäïø|ïpaÏœgíYCÉÝÙ-Eõ¼ØÇÒ<¦1Ôqf±ßü¯·G9ÊÒºÓ³U7éüµ×ªTßÊ›ßÂv¢:weuкPÙâ[ ´N¥Ìjc ÔÑþHu¾ÅŸmþMçT¼®•@ûÖsÔŒ¨Ÿ‹'UNÄ¸ïžØÙ° ñžùÃâ¸x²·œ1\¹ê,\»„Ö#=-#hýušú®wºw°Þô6{Mù]KþÂ5Û­ä¯]Y|ø)ÚcWÕœ;Gç$Š+ãó™£×~+ÏI¢çþBÿÊ/‰fªË,¥i:ŸÖ¯¼ŽšA~ÁÚ-B&˜gÝÆÞ¯®‘^“Ò¯§}Z^ÆÏ/'!ï© ®òéü|³Ï¶‹”ÇòÈ7áƒ]åUsÕ³h]Åšû“ÆùvàšT¦ºÌÖ±:ëQ¢·jÈï˜ìáAy¹¬‚ö\ÊKÖYùGóÅ)££?ºÐçU«¦Tažë¼:óï>ÓÓ±ÙlGë½­%vÛ!?3w¼i}èç£Û•”wáu(c=Æ}VFÃÝ”,ÚW•©ž=ÐèòsU4ÓºÞ}·{~”od7O†üÿö”CïHÏxT†®ÁxVÌc¡:1ìÜ“ßPü5y¸(Õá|¹²9â?C}y|î¡X·2~Üá1Êóàu“^eÉéž~Mïc?ý¹ù “úð|‰…O ¦Ð8ÔuÑ:K:K¿ó@ý–ÀÁžÖë nÕ—Ð9ÿ_òËìâvjqsÕçoÝü>‘îAóJr3³@;;¿nÏ:[wÎö$ù윮Utbðv”ÎYV‘<‰ïùµ‡ôé·–Z†{XìY<óf%ÇÅÓ<µ\š¢Kë4jz“›ìñ³Ôÿ€1íã.U/¦ý:^¨pW˜þž¹æßIÚ'mÒì¦:×ì¼ÝÂ^W¥Aqí”gÞûð„0æ;i­=Õkq~ÔÑ<%q@MŠá€}äHªX3¬÷úòVQ§<­¥•îÇø^ñ„½ƒöÇ·Ð>hðÅSÖt^Ö¶Ø÷ æ_fàŽ óÈ¿JÑ’§¸‡5I0Å‘>ß;ìó<Ø Ï« cƒ¨zp©pÓzÂÓ áÇÐooòÒºˆõ“7yÐ9ž“…!à¹â¦ÕºÃ´¿r;ºâæG9&„Îç)•Nž@õøä\Ç|¹Á`WÙArýªt/Ru`låƒsZ_il vç—{ ï|>)šêh >\3Õc¨Ÿš¼NeM"å;Kýíyñ#šá>%–`ÍÏüÉšçÍi_¦ÏdÅ ª#«ðkéø Þ34’siªiŠUÕ—wyh=öŠohjHyüË¿ÝIîÌ$‡ša\óZÏmÄó3/ŒQY@ëÀoÏ—f€ÿ–C[G€ç˜Çкmج³>0Ý/p»¬·xàþ”®aˆ7YY{6SÞvɳm{h_ýÊø.ŠoÖù5tL5ñ,›Öà(Æ1cñwú|±q¶ <“ÖkæõÁnvnyt¨œÁ {]õb䈻ë¹3ñÁÛo&C~é¹ a]øw<«aÿ(ßT`¾Gg%æÄ§y…YWhŸzñLšü‰iq÷ômcîãØj—ÿˆc@÷)º›/n0c³Ts|ž|¤uÙaRt«çï*oÞ$dÞï¿…qæù]z›üQ7£Sýÿ>‹½k€gõ•^éÉþެÓ=Cþ~È.™HØÕÀŠ|Ið‘Yxë˜é±Tˤ ôÓy»¥Ì.ðxä±3åkñŽ -¥óO~oÍÀó%zGYÅÐý-ìËÄç_?“·à9RÿÍÝ‘´o_öèÓ}![/Ρ}ü§Ÿì!;r¢[7vdùÒÓÏZÁsÅŸ4-/ª?$Ÿª;‰ò"Ì¿8l=ðø9R“Ö±¶+¬aãó“QÙàyùÝø‰|òotZGm`ðoŒÎzø´þâ]xÞ›ôHž…nåØ.»šôÚ|îUÚç™9ûêoÈyYVBþ|ÄmjÍ v'l/Pg°“&®ëZHž›{|ïѽÉ2?WÄr O!Y‡L’ñý¥ŽP9ng Ã=EeCý®KPóci3ÅÓ¹{ó–Æû«¢xn5u’:í§»ù¬'?°EçN+üƒüOSÞ$‹¨JþÐòsî¿ñþþÉOæ_¥õ\¡ÂgCèß5Xrr5òØp³‰xÞ”sÒ~ˆïNÊ·½þàÚ!…ëxŸÆ»?(ÞËYváþ‡0»7â¾´ždö0šÖŸÿ f÷Z!nGõlÙ_fš°É¾¤cºÈ“ò3E?¥uŠú/¼XºŸñöu^ø×690 ~Aê$Åi{hŸ2^Áx7ƒÝã+ëg “ߺþ÷Ë6òGïµvÌ’*Aû®¼˜¢¸ ’ÇÄí·!çé Yqà¹3LׄGõ'UZ¶}cº®ýn“c¾â3uøQ}z’ éËð¢Xð¬žgb1É¥±h²9o6BÖ—-Èo{øê_4ÆYp©OþC_½½nÔ¥þ·|º—A-ñÅÝ{‹çÐ}»eïß«Pk_·Šè¹éC=è^¸Õëô²mÁóãŠÖg´.™t3aÞ‡%/xHqÓXmv=¾§‘¬gŠùW~þI(xæ ;Ø:‡ôK!+9’ü´àqQŠtžëö²ÿ(ñÏâ–ð¿uÇód3ðÜíèUX@ûÔ¥)»jîÿQ:úÉa}¯ëÞ)_Ú7l¿ª|ÔÎë¸(¢ú°ÜuyÓ(.²R̨‚œ;¥?[Òž=³Gк͌—;ä0¬ñít;Ú?¸¶âª7Ý#}³Itõ*¼×¨—Žõ”/ú>×txãUëÉ1Ü#X6æ,/œâ°Àªz¤¿ÎJü£s0ƒþ'húßß^ ½9ˆâ 3YÊðÿX²S«æA¯ôV/Xõ€òîïUË";ùcŠíwÌï«é¨¯àùŽòæX£«…Ëa¯Š_ný°‰öÛGþ2ƒôê‡áëÁž)]ÐÏÒuóB÷Ð:FS§é=†y/}Àbþ?ÒÛQjSi=¡dJ[6âLךaÏŸÏû\ìò_ÄþA®bCõ§î¡óÖ?…Ü­(O¢¡¯>ü«&6; ž×©­=EñÖf7)+;&ÐÉtM¤ü‹=ÑR”÷1;2ë"žöâ´où)æ)W£HΫ¿Š`^lÝS»8ãàRµ,€â¼æí·†2È¡ ú‹êI⹫ÜF„îÙÎw·ŠXÁf%ZêÓþOåë"!²Ÿ9¢Q×îÇö¹|¶ãÜ™¾Aò®YõoVa¸˜Åö#ž9·[yé$Ï]ãÇÝð_j·öTµjMå{mÕz[J~øÜ)ü{6«vW¶Ïò«Í÷O¥ýj®k¯ ä]þ¸ÁØãx>?¼iÕEg]¶¸ç÷ªS]×’ ž3­Zg%þ*N.¸{tð~UÇ &Ì!;õú¼¬ÝS~þŒTüR1áo¹#(>’:¾¸”ü#;‡ØïX5Éæëqvzó€ŸÓÙЦ€Ï³çkm¼žÕïth, r'™=d¸·—í>}Ó[’g¥j4¿fÌÚA÷ÑÔgVO¥}ô‚SéïȎ̈l˜…qp_–;ë xf³'_k¡: #GOe¸_š}Ž—žCóÓÜöŸøe­´žX¼”Íjhýºvxf·Ç< ¢}†¥×vÏa¸ç7ªÊ.â0Áôá:ò »\£{™ýO :Às÷áàØRzÿH5‘°¬°D‰H#6KÈTfíß³S´Ä¯‘µÄÊþ™IÊQcZw¿uûà1òWNÜ©°†›,Èš{‰ž.Pa#éÓ®ü³Ï÷Ú§X¸¼¹NùU‡óOPÞ{ùãT¥Cƒ÷KB&óÌFâyhE×is|Ï´3 ö,ÄZxå½íp–%½Úñx¾Õ5’ §óÞ¼°å:†¤w8‘gè~ùc‹‚gþŒè¯Ž$'ûsgM`Ðo"1¶Õ’ä/ÏYŸJ÷ÅñtÌ0¿Ô5V®¤{Ù¡uKþÿf‹kÙ°Y…ÉreW3èûßõ©]§C/ž1ð“³Éù%ññ{ó’‹lü4e”²ïÓ/bNç¾ØçäîÐ~ kÄNöAÁÛI ù—2†êL¾ñ\ŸÆùbÝ-_†ûßYl³ž3Íß5¼%ž‹ì}°?ÅÂ* 1Ïzò·”©HÔÎ*Š÷–;žO£û•§ ú l•EÜh~›oN‰yP5û¥Ñ}Úú› mM~òw£}Ë¡ÿ¶M{ý<ËyFu^¦{ŠÏ?÷÷¼_Fj¡{ÉŽnÛæÚB÷$ûè8ˆcÞI«<² žuß/¡ñNüî. yãÌÖMKël"V³).l(ú{^ùÏàMMí§¶øJR«ýì×›æ}›’Ê:É­x‡U’~`ìhzD@­ÉófŽÏ[²k|µõ Ð{æêf¯2(Ÿß*Ç“ÚMñÛë>xûÝöÒ½,/íö ð³ÓÆÂKòá$¯Hù¯ßz>Ð<õ¬ùµ`Ûàíô9mߪœDþhpƒf½êP)uüû,Á¾žyÓ•ƒ¾ÏS‹žøÒóÖ·œÆó•Êê—RÝÖ§GÙr4Þíö’n†àó¨[ÜnÚ?Ž‹—YMü7Í[þó÷è~A¨Åfå“QC<ï[áº~}_>büŠÁûÅyï1ó4ñ\ÂQ+CÏ ½§)=°¿oäM<×nÜﮎŸaw¿nR¡sçë>èÏ6[æºiR»ÃV¨âó’¬¾à™µ$çT>xaåHÜî0ü¹™­#Ü6ÏœãuëåñSÐ1ÍO–öC¥×{ÏæçŒÖÑ8pôÒô0ùÊ. {IoÏT^5yõgÉѧú ãýÑ|÷ÒËæ™?…©}™í߇!þö˜ý6‰xæK&\Bãcp@M‡¡Ÿ• ˉgYá/£â§³¬Úµù ÏþÏóž÷*t'‹W¢ÿGYyÖzå:KÀêÔÒªZJ<ó—žÏÆßoî¾»PÀÊœÐé¹xÎ>Ózy þ7*§¥€•ó髚xæ–>uZ‹¿·ÉéOY-`ÙÌPí#žw¬Äge»Íµ Ú-Î/?Ÿ°ø'Þ窾ÇÓ#1WÀŠÙóeý^â9âbôçåø{ÈáÃŽK,þ”Ñ.ÚÄ3ûõáT#üýÕiQ>ÿ”síÔâÙšÓ1Íß÷ùÅ‹ÁŸÛØi¡bM<ÏLÍVZ€ïUs²5,o~¹ª;ñÌ㛨‡¿»žå­+`±Uõúº‰çsJ¦žÔî6¡çGÖÞ¾ÿ°ñìqA·Ußã¤YÖI X"êÎ{‰çðÃÓÓÖáïö–Šv&ƒ·#dSݺ”xö,ÝMã,½,vÙêÁ¿Ïb· ðܘ¦¡ÙF<Ç>ñYC|ZnPµHÀªŽH¾£Eöù­ùè½Ôÿ”#Ž ËõËÕ9®ä‡]Í×(äâ´*6 XÎ7Çk–Sü¼ü£¥ˆþn$j^;w>ËÏü.Õ=øûBV÷óƒ÷«â€ôr_âyF]|ÜJ|_¥^d€ö.s¤º7¡c/ÙZáïz÷Å7X܉ÆòÄó—Ÿ6ãïß„WZ X‘“mÅÞƒg¹¥/^ÈÓ}¼¡?Ã=\Æ#Òc™!ñÜktUÖß«Ëào0°ºkާm!û|ùÐÅóføûòq?šL,Ï 7} ½½?ãßjw›`Žmƒ·ß¨¯=£›ôöUÇyóhþòϬš7í|«ùušâª…SÌ¢¶âï‘õ· ÞŽ÷ì=ßµÀ³ é‹ ‡Æ9ª§d—á½âÇÛSI<—mØ<‘æ…²½~ü «qüSHžŸu*“ž\©Äø&MÞór#í—ø5¬ÝAý°–: X'ßû‘Mòüq‹îQœåðqåvkèâ ­ðÃKX½…‡ñý ’‡÷Þ/î&COGâù“ÁYY×ï²Ò"¦TŸz·e$ÏcCnšÓ=ãÛÔkð~ÎO®ª ÔÝñ‹úKã<é-Þÿ×–çOÈ>ûmàѽ*cüf—y þÜ ½·Äó—•Áñ½lÁ†«x~î´?V‘<{ñŠæÑ½áŠy˜GRaF÷IžË„vt£]Þ<Þ,Ñ=ƒ·ÏÂýÞH<ÏÖ;g ¾§%µb®æéæ‰nÞÄóÁ176Ð|)'‡a>êl.7Jù‡qÁÃ!/¼c‡tolüû,¶æÏ‚4W«â9Ñwªk ß² zƒÃöL÷Ü Îç½9NuÌh(Ô`ž9í¬Ð¦øyÕëã!tÊ¥†¨àUlÁž3§(®R[õY’îIú¨ÊÚ~+ßÌï,¤¸Jex‘ÁqÌŸóEG|ïWãËx}]Š«Îü8'LrÛ;.î¾¹€å¤²—ê'±ï»ý›@ãà~“½¼æJ«÷ÒzX‘RsÝ»{v^Ú~ë…\èÖô,Úçð\:ôþ>e_­ÿ†ñXr+­ƒÖ=;ZífѼn´|_…ç—¨qmgÑ8Ü{ý÷æë¼âiŒƒª±öÁ`ŠŸEÔb ©Ý©ßwo<0xûrKÆÎ¼K<+„ª¨ÝIoO¾ ýÙî(±–ô¶¸¦ÐŸ}ø{ó”…M óÅhþãßéÞ‘ûq{0Î̵7ÒÏi¼'î“? {Û:3­’ÖÃø¯¥÷2쟞ï»pÔâÕcaõßá¡÷¶á„ÏkJGèø‚çÙð@Õ@<÷ËŒ?r~ƒ?7,ñÆçâY&Þò ÝWrçÄ!è‹XWñ¿iÝsÖÈÚÐ_<µç+1ïÛ6€çÆÔ½[5¨]±—ZÏyƒ·Ïq+3£õ0…ô]É«ð½’ùÖЛÍÓzÎISüüà¼,ÕAf/4jr`Ðw _l +Æ8‡é9y¬‚žbû. ÈeÐS,öážsw&;Rþ"kíöNØYV’iöpøÜÊ~!d—V)-Iöçž‚tøîÚy;ð,ÕÝ<ýÐ |²¦íŸUb­§Ï‚þH=ºßblÚ5|~ÛuДäYwü잺_ùàNNààÏí:ùÙzZ÷¬‘8˜O÷ Øìþˆçs¾M Ð>FäÇ)®¤'W,½g¾“4oê }Œ»¡´Ñ.[~_:‡aq冀&yþlì.ì+vâ”rðÝ–Ýy®:í–Ò½3g2mÒÞNïÂ+«Obœ+¬Î4lÂ8 Lþì>8ø÷Y‚‹Ä³þÀrö/z6øþ^“\m‹wnø¸wÐ[®½Ï_Zß žWÜ §Y_Ï«w@O´ž²Ï$¾«âÍŠ ·¿E¯¤‚×™#:ÞþLÄüç|}úÍk\ýÈýÎ>v L |óèéDàûÛLcNŸ¼»Þ£up³ža;É?‘Ÿ¤Ø }ɵ¨Þê[Ìž=Æãn4KÝörÕï×/(¿0ÍjÉš‚ *0ÿ>¼ð “';ÓX°óŒ—Ó±Á‚až©'E7Œ"¾7[˜^#ùey+8: XucD>…Q¾È›;—IŸ¼ø³9 údލ˜røæÙæéR»®ŠÆÕþƒ·/ñ»ó“ ɵÑ}Žù×+…ß±`'ååœ4(¿ý¸jëIºçÒ¥Ô@ŸÁ¾±‡ˆÜ ý–T½ãQâeÝö†ùÁb—È5KW¯û7vZ<'’ü½ì™š<ø‘‰ñ‚±tî"©zÁÒ‹žÃùxOÍÞú»)ï/}“ízÎç}cð9^«®%Ò>VͳpôWn¤g°ú›œà×”~ÊV-Š_ÉÝÂïWª’Üpu’ë÷žKÝé^ưˆoG`ùªMª—!P8›ìJ÷“ß½:|øä?­«r º,›V?T¥yÞ*+¢€Ïe.ɼ|—Éfét~J‰Àüâz·°UBnŒz¢åù³œ D]0ŸYe“üáçç¿XCùÊ+j·ÉÓ=ñÆ|€ž“Xþ#U<‡]ò.E»Ü*ß壂Æ;ðËÚ6Z‡_ùåÄuŠc’¢ˆc>Ý*+å€ç0ÑwºpÒ˜»— ÞΧ–Ç÷蜦ÀïDP7Æ™'ô:?†a^°_xæ©q2ìi?ñfëuÒ—ÊÛ¿À>åùmvŸôÖ®#ûƒÉW`wøîW¥<Þ.E­5tŸ™‘Âa<çiìŠv‰T|Fµ8ÝC$¶t±%ú+-µ»/ <¥T½7¤ûfç } <=x¿rEV®}Ny%j{ý·À^°>$gþqBò;xçâyô¨ì|Ì+^2èëÅs¸Ð½rC†ü“£û¦Ž=á¹’½“¼Py<î\æõó‹'6ñÇ>†ù¥Þò,TöÕ¼+¾Œ"¹•ò9¿¯qväƒt.Áå‘v¸é‘©_¡ßÌøÎëï_a³ÚJ=Wl 'þ…Ì;9xûro2¿œ§ué§o¦'H¦!0þoqŠçןt^ËÂ÷ôrº¿uòû¨‘þƒ·c"nW!ƹ³þøW_š’o3Ì [ÊÀïnRNçÊëS†•’½2㊡½€¥nx7Ç <Ë]3Ò!ÿJ¤ ý5ÙÇ´ìú;tÞÄo«ô.ôG*u£ ã+ôÙíñ<ŸÓÆvæ§öäÍu ó‹_Ô¶ŽöÇVGûjÈNETÿM„]®>%ÓœBùCÓÿüꂽ`™oúÛ ¹î6¶w\D<+*ÎZBí.­Êœ6xûr™Ií+‰çioš\Ÿ±TóB<6¥íYå‰É‹›ˆcžð6Oä2ø!Ç×ß–Hu6žWôcœYÕš+Ž3Œ§@÷ž5tõgR>`¹Ü—JòCêzw÷A®%þm;Œö_ïÌÞOvo˜ï†fòÿ#þŒ“ˆ§:TIÿN ?<^Ws ù!Ÿvè$ÃWÿ0¿òÝ ò»ÌuÂ|ëV°¨¬‚´œ+Ö<;r)õvzźZ£.×Êç“å÷Þ— ¶¢õÑ¢ßë̯-TO´q³JbÔ!üûã+ÞVB®3Û‹…Ó(/M¿àÚåið¦ÍdЬ’w™Çˆç]NÜ.Z­ý–»rmij’JqñŸsžYÕfÃr~6ñÂ0Ÿøa·‚’1¾1wÃn‚ŸªØSt6óè²Øº²ÓGüšyžÁÎ+q¢ “é¤ai£oÒ½Q·´Äo3ø,Á³ÿyî±ÿCyÙj¾´Ð?ÿÐNÄê±U;e‰ç†ªž?}Zù¬cR½ë1tô¿²äö¦îæÅd7\Ÿv†îmX³°c'Å1þíÄ<ð˜”òäô§Î•»â1ïÓO]?x¿2¥z/YÜ´Žúe»·ô§·©ÁìÑt®"6lÔè¾vû!>ùûe^¿¢¼ßݧú¡_x3:&oÀç“7]>¸Gšÿ6øôHg{ËØ zDÂ^XÈ&€Î·ï¼¹œìð÷cMÐßüÝvtΛ›ß FúÃÒ£`5ì˜eù3o¨GKDÁK´ËÝõXkƒßÁ6¨v_áŽq[/žFñë6á¶$įUßoG’½Uÿþ+TˆìüóÎÊD;Ÿéþx‰x.¹ÌôóÍÆ‘á òÏbÿà™§?ÉP < :îLL¢ub÷çFÐ߯TNÇ<Μ£Ôür#ØYõàÝÿ·ÅyG5ôdê·fyôGÎØ©ºã«$™Ó|„öÞ7=€yÉ®ôj‰yÐ\ýðôçbŸìétÿöÚ}[òïçlæðÌß™9â9%ö{gèouÉDKªåÑþY·úƒß9Ï¿Œüùû/z©þ`…pD"ùk2²sðy?gQ8üa¿¾¼µ1Ð#ì]UÓôˆ„¡ÂP:g²}Ùƒ§WAîŒÙª´s)ÿg=Õߎ)¸-Iã -ŽAÃ{J_;<Ë5q4Ñ®Ü6¡é vAÀÛüb‘ÆùÔNîeÄ3¬ÊZ‡uÐß61¡NÐÃ#xGµ`ç;½ï4$D ÞŸw0IòÔ-µ{ã[Œ³ üPI:ƒü#P ØŸTòKKy†r»Ž@?òT·mÝ ýÍí?çÿï¶RtôGò§/¹wú»í÷ùETß;U'MSýa—x,\Šñufÿ½µ v[ºOá ìT˜nÆñ¹á´¯¢™pú³{V¶CfæIC¹†MÂàýêüd©áNçÝ3¾¥™Ñ½ßw\Ê¡¿3;„äyx•ô³Ü¿ M¤ŸÕµkÄ'F“Ýž»j ½o*+[Ÿ;öÉÁOêyj/z$ìÕ§\1=âÑÛy“êlˆÊ&yåéþÚýíákõñ^ôÚÈ–Åâ´þS¶Ú¸<§>¹§–‚qH³c·Ã¿ä·ŽñÿÎä_.ÿþnwç¹œŠ§uô+)ËöA‡l&€|Ï;mEúÀÒ®$šÁŸ‹)v î¡ûà’Û>Ð8ýô6ÉÏg³xæ?mˆQ½^ñŒf7 Ÿ•½ÿ–ÏN¾‹÷óˆö§r#ÈÙ1Ýz‹ÿÖË¡z2Œ/¹³‘æÝ¬½¥Ð¯*ͦú¢£;¿ï:CëºúðüÞ‘ºöÒzpã©ç—“1Ëœê³$o}ãDë¡Mõ¦Ð[ê½^’²Ÿjg›žfŠêìgØa|%úb潉¢|kÓ´·x~ÅîSÞâù"#Šd&C®b´ÒÛå.#>ê2¯ŸÌ0Ä.ÓùÁÇŠ¯AgÁí–¥ðǸÓF<ßøÆ¹â‚èâY÷µPü­0Éd‘¿óÔˆÐÜPš×šcþnÇç%cÖ]¦¼ö¶Ç?¼Sð=óã7 <óeüz[átª¬Ÿ²•öUo?zNû}Üé%‹íéœLíýÖ˜ïr¢*–;1ß-C•¥VÐzEù‹ÐnæßÜ5~ ~kŒÄ”÷>öÔ¾—ÄsÖÔ‚~ðÜ8fœòº—•Õ[o½Ï3Œ~ÌÄóèo¢V°›Òz#çÚÓ~×Åxc¦¸]Ð3À3oÔ©e«¨žë¥ÖÞ&Ú§¨`ómà ^œ^úþ'ûsyþ,È\ƒþÉoà™³çÍÔʳ¬V !;¼BÚý3x–³Y4|Õç;õ2ŽÖ59™ZGãùS”’!WžV_ÚÁ³åÊ?Ý7xf;«ïFõ`ædú¸Ð¾S»Þº_¾ñkôIò ã2ÆÑ:Ì ‰TòÇj,r^CΕ’ÎOS }OŸ™­àYúÚå~Ú§õq\—1öÂRõ›£ ƒ½(®ìÌ ó šc¿Ñ¾¿uÓ…2òÇ>¼+é¦ú¦êoyR4ßÎÞ5 ãP}ð¦ÅøOI¶w–X Ý ÓËg2ØÞÊ~ƒG:Ïþ9ô„3­7¼Œ?&pÛ¤¹ ò˜ ”1ÿ­Õ;ÊŸuúõ¬nÄ8WÌ^4zž[W°û+Ïìéõh~…ví‚>œüsÿ~ÊC t)xC~·¹ò% ò?_5?yžKe|‚üñþÂíÁToÓ^ÂL ó®¢ùüwÈQ˜h±õÚ¯n×±–Öžl‡ykÏV€\™‹JëG¦Aßíxîa’Â0ÞY¹it¾9¢jºå1ŒªX¸þ÷ˆá–ÓtÞrêéAà™?ü¨ÈNZ’~¨g8ß2åþ¥LÒ_Ÿ[]h=®ÓáêZpªSžÛþÌøóg–IÙª“ðeÑ+OŠ“û'¹¬§8ùÞ–ÑÇáŸq‡Õ+ÏrÛ^ÏçU6Q>K~eÜšz´ëÑ]*y…‰gûï{§8€g3Ï„ùÛÁóÒKá©‹Ü6PÌiª,ÒûßF¸«2ÄgwþVÚн| ßöÕ`œ+D¸þ öœ%X9À3k‡¯¯ÇNÌô¶Ãi?ê„Ô…u”vVz|ÙQÈÏÜîÇ ÏD¹È3¸#¯Eò£zL–ü‘ßy?Èà%°Û!ç]G/ýÀó³.àù©çüc1Þ£GŸXxr¶~FOu C¿y||¿P¤fX>­Gð¤úåßß­ï‘;ƒ_À{}‡»ø ÕðZW«T÷ð¬tíÄÐ2ž+N—Ø?Ã8+…ÿ ñ…=ç(Ž“bÒólמy]‚GˆßÃ2:À3ÛüÀð»à¹QHÕT~IÛI¼,ôc§çG4xf‹¾~žv’Íš±d^ ýÞ‘m9ªàô®ÿR§6TŸ§ïÛL%yÿkzVvÌ p4¯<7Þ ]ou‰a¼Cµó‡Ò9ö[¥¡Ög®‰Ï»]iÝ­¸‰ê:äÎ*ô¥x‘Û6Õ¡‚xökaQ}‘LùÚÑ—)n¿£òF•ü…è3 Ð=ˆbëοÏ–®M < D6 ¥:€Qy{lÉ߼߰k"ü1ÁâòD_:yêÓÉßÁô^“ŠøþäçœØø¢$Îöw9ÚM¶qϼ&žóñí=gGÊKZüÉXÁ ßß•]ºzÐùûÛçxò_5º¬Öá+Ìåót O=~=Rœ {Þ9õÌê(&žáÿ˳Ûf ª¸ýfE/x–Ë:|_Žöžo×T¢:€a§KÒÀ³D¼g·8åOŽh®jÀ¸Ý鋺žÕÇí:Pžå9€Ÿd|sYªžoyýS(žÏîu¨ÇxÿíþÂ6]ÀŠñÕ\ô”gÁäNÁ4ø –›t¸é$O'wÌí¡8VåÒykØ‘6¹çö¹à±â‰|äŽPÊ#m< rÞ>e‚çxè áÏhßg¶­áyÈ[ñR ;øžW†ÍXÍäÿ-¶õ:C÷ÞµlßúÐöµÝz])'Ùþu Õ]ªûpzÿˆ+<ó]b†¼Å8ºŸKy•œÚ­²šPPÆ$ω’~+6S5Åà6ðÌòD%^b öœÅ¾õ?Ï#—›Àßo\v>ωöãjüý_±Wù×B9 ØÏ>Iãœ¨é žé;3†c\ë-ŠgB¿p79KO"¿»|óÌ5'ج¯¦ax~[PîÄnÊãȯ؃ñ~,9K¯<'å{/HeïÑË›5=0ÞöºÃÑzÅácÅ^_I¹Ùh…Ëš8ŠÖgV{pÏ›*÷Á_ÌO^(¼ìÄúÀk5tdí“«tÿáåNÏÀ³ßÙ«ËS™xžU¥=¢~SIYŽö=OÌù7°Og±»ðíó|\$Ú žÕÛžöìÏW½ªØÉlŽß‡p´›?»Áeƒ?ÏúíqKÅz¡ÜØÇÎízúþýí„þ—ÏÓSo³fü­]žÛÄj60¬«±2–=ú€qnNßQ¬¿û¶ªÆ‹‰gAÝÿz;i©Îá?ðæµ‡¿Œ½±¯<ó™k¡õгÙg)Ÿ¥Ük×+’çš{9ŒkØêÒ‹ÇÁsãë/v;À3O±Ðaø7’szØž3ë¿{˜Ó¾ìâ¹CWb¼=à ¯É°š·åVG2ñüÂiÎW*Þ²^€qæûé¸4Ó¾‚Ç5 ª‹"þfŸ9x6:=Ä@‡Öáîf½lÿÕNÞ1OÀ³ôÙáëåñ9GDqÑMؽüëå_5àÿÅÝ/íaà™í­bZHõbzú#w§uU'Ó>Z/§y ~afŠî+èµÆ¯î/(cŽõüY°“½e&}@»B7¬Û1ñì6Vz¸Æ9£E¨”öùçÈr_5ÆÇ-soªŸë¯Ï¿}×ÝcZ?5‹åÓ=¬i†1àYúû!öKFy4Àsç§éÉðØyý¾¿p'äM§8zcÌÛ‰ð‡º7^õíφEÇ”Èß¼Ô°ŸîåÓÉ­ë¯!y^8ovxf 5^ñ£qm¾`‹ç‹|²jHÂóù_nżO•/ØžN5Rkaà™•¿âB°3xÛ•íAò´¿¢uåq¼zdý’Zjc Kr šÜÂ(¯48Úü«9þ“†þðpt‰ÚBþÿÖÐá×)þS9””Fç¥8AËSžÛ¹J)ÊzdҨùà¹SõÂS]Н´w5êÓýSòîSòEg:ÅÑ6O–A?…¬‹PC»eÅb™xþ£>¯r=æi~çLW´shÅå àY®1¯æB›Rüt÷=ð¬Ôo;o ÓúéýÈ=3 g‘ág ÿ^ ý?mE¦ýâϬKýa7*ÜmEmÀ3ÿ›Ê>ýÏ ×NÁüV:Òo ‡ûu£;xt?ß}˜ÍªòXœ5v„³·®ƒâ·çÖ‹Œé>ÓÅNã2ñ|…Çu>‚g®¢Æ¸#°?¹ÕÑ/ûÀ³Ð”XS•Ë ýZ^<<ˆô‹¡Þy>x–ëð?C<Él€\ɽl¾Jûñm;nì¡ýxvîéÚà_=a´$í÷T,ɳ¦ýžF«JcðÐ0ÒÕ¼Ÿ€Ï@;ðø t߀ïÀ  ø ü~ÝÀà/Ðôÿ€>  a „€áÀ@D‘À(@ Fc @ Œ¤€ñÀ@˜L¦S@˜ÈÓy`0PfŠ€0PT5`0Pæ€& Ì´€  ,‹%€ ?„޵@–eÀr`°0Œ€UÀj` ` ˜¦ÀZ€¬Ì€õ€9°°,+ÀØØ›[Àà[{Àp¶NÀ6ÀØì\7ÀØ x»€ÝÀÀØ xûo`ÿªäy|€CÀaàà Ž~ÀqÀ 8„'S@èJçƒ<Ài ˆ¢à,p8Ä€8 à @"pH’à \®é@p¸dY@6pÈn7[@.pÈîù@p÷Bàð(ŠG@ P ”åÀcà ðxTÏ@%ðx¼ª xÔõ@ðhš€wÀ{ ø´­@ð ø ´Àà+Ð |¾?€.à'ð ø t€¿@Ð üú2ÊC€¡À0@Œ„@ ŒÄq`40$±€0˜L¤IÀd` 0diC©ò äf3` Ì”@Pæsu` hZÀ|@XèºÀB`°Ðô‡RÛ Ë€åÀ `%`«€ÕÿñuÐIfk£Ç±+ÆQ±kìŽmbìˆ-ÖÁذcŒÊX16ìvŒ ;ƆcCcÃØ°cÇØ°cl~ÿ=z×wî½ëàZ¿s’y€ýìþ¾¾­ÑA¶ˆF;t@Gt‚Ñ]Ñ ÝÑ=¡E/Ä 7bÑ}Ñ:ôÇ Ä Æè1q†á‘Fc ÆbÆc"Œ˜„xLÆLÅ4L‡ 30³0s0ó`Æ|$`!a1–À‚¥X†åX•X…Õ°b ±ë°°›°[°Û2ЧLÒžaÇN$avcöbØ8ˆpGàÄQ$ãŽãNâ\8œÉ(©J{Æy\€qWp×pÜÀMÜÂmÜÁ]܃÷‘ŠxˆGx žâžã^â^Ã7Å?í¥=ãÞã>"€OHÇg|Å7|G? &Ø™Y E²"²#r"dGü†¼È‡ü£ ¢ £Š¢Š£"P¥PeQåQ¿£"*¡2ª *þ@$ª¡:j &þ„µPuQõÑJ4„ ÐMÐÍ…æh–h…Öhƒ¿ÐÑh‡ö耎è :£ º¢º£zB‹^ˆAoÄ¢/úA‡¿Ñ0ƒ0C ÇPÄa†cFfO!§=c4ÆbÆc&ˆI™Äå~Ú3¦`*¦a:L˜™˜…Ù˜ƒ¹0c>° ±‹±,Å2,Ç ¬Ä*¬†kˆµX Ø6a3¶`+¶a;vÀŽHÂ.ìÆìÅ>8pñá0ŽÀ‰£HÆ1Ç œÄ)¸p)8ƒ³8‡ pã".á2®à*®á:<¸›¸…Û¸ƒ»¸/î#ññ><Å3<Ç ¼Ä+¼†o†·x‡÷ø€ø„t|Æ|Å7|G? Ë™Y E²"²#'rA†ÜGü†¼È‡ü£ ¢ £Š¢Š£J¢J£ ʢʣ~GETBeTAUüHTCuÔ@M(P µQuQõÑJ4„ âÑMÑ QhŽh‰Öhƒ¿ F[D£Ú£:¢4èŒ.èŠnèžY° ±‹±,Å2,Ç ¬Ä*¬Æ$b-Öa=6`#lØ„ÍØ‚­Ø†íØ;v" »°{±ìÇÄ?8„Ã8'Ž"Çp'p§àÂiœÉ,ž2D[Æy\€q —qWq ×áÁ ÜÄ-ÜÆÜ…÷‘ŠxˆGxŒ'ðá)žá9^à%^áufñ/dhÏHÃ[¼Ç|DŸŽÏø‚¯ø†ïâÄÆWdD&dFH‘Ù9¹ Cn„#~C^äC~ÈQQ…QÅP%’(…Ò(ƒ²(‡ò¨€ßQ•PUP ÕQ5ñ'¨…Ú¨ƒº¨‡úh%B…FhŒ&hŠfhŽh‰Vh6ø j´E4Ú¡=: #:AƒÎè‚®è†è -z!½‹>è‹~ÐáoôÇ Ä Æè1Ã0#0£`ÀhŒÁXŒÃxLÀD1 ñ˜Œ)˜Ši0afbfcæb̘,ÀB,Âb,K± ˱«°V¬A"ÖbÖc6†MØŒ-ØŠmØŽ°# »°{°ûàÀ~ÀAüƒC8Œ#pfO£=ãŽãNÁ…ÓHÁœÅ9œÇ¸q—pWp×pÜÀMÜÆÜÅ=xq©x€‡x„Çxžâžã^â^ã Òðïðð|B:>ã ¾â¾#ˆHiÏȈÌÈ)ÂÙ9¹ Cn„#~C^äC~ÈQ…PEPÅP%’(…Ò(ƒ²(‡ò¨€ßQ•PUñ"Q ÕQ5ñ'¨…Ú¨ƒº¨‡úh%B…Fh‚¦h†(4G ´D+´Fü5Ú"íÐРAtE7tGô„½ƒÞˆEôE?èð7úcb†@¡ˆÃ0 ÇŒÄ(0c0ã00FLB<¦`*¦a:L˜™˜…Ù˜ƒ¹˜3æ# °‹°K°˰+°«°V¬A"ÖbÖc6†MØŒ-ØŠíØ;v" »°{°ûàÀ~ÀAüƒC8Œ#pâ(Žá8Nà$NÁ…ÓHÁœÅ9œÇ¸q—pWp×àÁ ÜÄ-ÜÆÜÅ=xq©RqÚíðOàÃS<Ãs¼Ä+¼†o&wÒžñð|B:>ã ¾â¾#qA*2"2# ¤CVdCvä@Nä‚ ¹Ž<ø y‘r@ABaAQ O¡¥=#%Q ¥QeQåQQ •QUñ"Q ÕQ5ñ'¨…Ú¨ƒº¨‡úh€†P¡£ š¢Y˜xúí-ЭÐmðÔh‹h´C{tD'hÐ]ÐÝÐ=ÐZôB z#}Ðý  ÿz–¶Œ„Á=†"Ã0#0£`ÀhŒÁXŒÃxL€“ɘ‚©˜†é0afbfcæb̘,À",ÆX°˰+°«°V¬A"ÖbÖc6Â†ÍØ‚­Ø†íØ;v" »°{°ûàÀ~ÀAüƒC8'Ž"Çp'p§àÂi¤à ÎâÎãܸˆK¸‚«¸†ëðànânãî⼸T<ÀC<Âc<ÁS<Ãs¼ÀK¼Âkøñix‹wxøˆ>!ŸñßðAü€¸¸œ‘ ™‘R„!+²!;r 'rA†Üȃßùr@ABaAQCq”È*þ™í¥PePåQ¿£"*¡2ª *þ@$ª¡:j &þ„µPuPõÑJ4„ ÐMÐÍ…æh–h…Öhƒ¿ F4Ú¡=: #:AƒÎè‚®è†îèžÐf§}Оѱèƒ~ÐáoôÇ Ä Æè1q†á‘Fc Æa<&`"Œ˜„xLÆLÅ4L‡ 30³0s0ó0 X€…X„ÅX –b–cVbVÊ5HÄZ¬Ãl„ ›°[°Û°;`ÇN$avcöb؃ø‡pGàÄѬâ¶^Ú3ŽãNâ\8œÁYœÃy¸q—pWp×pÜÀMÜÂmÜÁ]܃÷‘Šx„Çxžâžã^â^Ã7HÃ[¼Ã{|ÀGŽÏø‚¯ø†ïâÄ"™Y E²"²#rA†ÜGü†¼È‡ü£ ¢ £Š¢Š£"P ¥QeQåQ¿£"*¡2ª *þ@$ª¡:j &þD-ÔFÔE=ÔG(Ñ*4Bc4AS4CšgO§=£U6q¢mj´E4Ú¡=: #:AƒÎè‚®è†îèžÐ¢z#}Ðý ƒ¸§§?` a0†@¡ˆÃ0 ÇŒ„£1c1ã1aÄ$Äc2¦`*¦a:L˜™˜…9˜‹y0c>° ±‹±,Å2,Ç ¬Ä*¬†‰X‹uX Ø6a3¶`+¶a;vÀŽHÂ.ìÆìƒûqñá0ŽÀ‰£HÆ1Ç œÄ)¸p)8‹s8 pã".á2®à*®á:<¸›¸…Û¸ƒ»¸‡ûÙ$ÿþ÷úu?ÎÆ_÷·xÝ"îÛ÷Wˆû 4~Þ[`úu­ÞñëÚ·¸FÌð󺯸>+®£Šë†_×m¿®¹y]ÃךÄu!qýF\gÑÿºnaýuÀýko]싽j±§,ö~Å­á×¾§ý×>¡÷×Þ›Ø#{YbÏIì éí·Xí_¸íˆu»X_‹µ°X³Šµ¥XZ~­›\¿Ö!b½ æõbþ­ û9§Õÿš#Z͹ÄÜÈ÷k¾!æbüã¬öרdùÕ×»~õ¢ý‘è7DûÖüj/¦_õÏñ+§>!ÏNLDA#,°Ã/å &”Ð@Sqâ1á†AÈsjè`„v¸àE²\Ä„èa‚¸áCrs¨¡ƒØá‚ÈrJh ‡ V8à†A'&ÔÐÁ ìpÁ‹dyˆ %4ÐÃ+pÃéoÄ„jè`„v¸àE²¼Ä„èa‚¸á‡41¡€:a.x€,?1¡„z˜`…^ “ó{PB=L°Â7|B^€\" Z`† .x€¬ 1¡„z˜`…nø„¼1- 0ü@V˜˜PB=L°Â7|B^„˜ˆ‚X`‡ ^ +JL(¡&Xá€>!/FLDA #,°Ã/'&”Ð@¬pÀ ‚— &¢ ƒØá‚È"ˆ %4ÐÃ+pÇ ä%‰ 5t0Â;\ð"Y)bB ô0Á Üð!yiêÔÐÁ ìpÁ‹deˆ %4ÐÃ+pÇ Š—%&ÔÐÁ ìpÁ‹dåˆ %4ÐÃ+pÃiybB5t0Â;\ð"YbB ô0Á ÜðCú;1¡€:a.x€¬"1¡„z˜`…ø!­DL( †FX`‡ ^ «LL(¡&Xá„~H« ¨¡ƒØá‚ȪJh ‡ 68áÒ?ˆ ÔÐÁ ìpÁ‹d‘Ä„èa† Nxà‡´1¡€:a.x€¬:1¡„˜aƒø!­AL( †FX`‡ ^ «IL(¡…æšâdvbÂ?¤ ¨¡ƒØá‚ÈÄD´ qú1aƒø!­EL( †FX`‡ ^ «MF´0À œðÀibB5t0Â;\ð"y]b" Z`† Nxà‡´1¡€:a.x„¼>1- 0Ã'<ðCÚ€˜P@ Œ ÄÉ’Ä„ >!WQÐÂ3lpÂ?¤ ‰ ÔÐÁ ìpÇ ä*b" ZˆÓHÅIÑ68áÒFÄl$žÀBLè`„¸áCòÆÄD´0À œðÀibB5t0 Üð!ySb" Z`† Nxà‡´1¡€:˜`…nø„<Š˜ˆ‚˜aƒø!mNL( †&Xá€>!oALDA ̰Á ü¶$&Ð@¬pÀ ‚·"&¢ …fØà„~H[Jh ‡ V8à†AÈÛQÐÂ3lpÂ?¤ц¡„z˜`…nø„\MLDA ̰Á üµ%&”Ð@¬pÀ ‚GQÐÂ3lpƒdíˆ %4ÐÃ+pÇ ä퉉(ha€68áE²Ä„èa‚¸áCòŽÄD´0À \ð"Y'bB ô0Á Üð!¹†˜ˆ‚˜a‡ ^ ëLL(¡&Xá€>!ïBLDA ,°Ã/u%&”Ð@¬pÀ ‚wÿâQœÒKLa.x€¬;1¡„z˜`…nø„¼1Œ°À¼@Æ‚¹”Ð@¬pÀ ‚k‰ 5t0Â;\ð"Y/bB ô0Á Üð!y mjè`„v¸àE²ÞÄ„èa‚¸áCÅc‰ 5t0Â;\ð"YbB ô0Á ÜðAÚ—˜P@ Œ°ô'¾^ ëGL(¡&Xá€~HuÄ„jè`„v¸àE²¿‰ %4ÐÃ+ðÀibB5t0Â;\ð"ÙbB ô0Á '<ðC:˜P@ Œ°À¼@6ˆ˜PB=L°Á ü&&PC#,°Ã/ !&”Ð@3lpÂ?¤zbB5t0Â;\ð"ÙPbB 0Ã'<ðCGL( †FX`‡ ^ FL(¡…fØà„~H‡ ¨¡ƒØá‚ÈFQÐÂ3lpÂ?¤#‰ ÔÐÁ ìpÁ‹d£hÈ‚˜aƒø!5 ¨¡ƒØá‚ÈG‹'«Z`† Nxà‡t 1¡€:a.x„|,1- 0Ã'<ðC:Ž˜P@ Œ°À|B>ž˜ˆ‚˜aƒø!@L( †FX`‡>!ŸHLDA ̰Á ü‰ ÔÐÁ pÇ 䓈‰(ha€y’8¥“˜ðÀi<1¡€:a…nø„|21- 0Ã'<ðC:…˜P@ L°Â7|B>•˜ˆ‚˜aƒø!FL( †&Xá€>!ŸNLDA ̰Á üšˆ 4ÐÃ+pÇ ä3ˆ‰(ha€68áҙĄèa‚¸gŠ'ÅòYÄD´0À œðÀélÚ0”Ð@¬pÀ ‚Ï!&¢ …fØà„~ÈæJh ‡ V8à†AÈçQÐÂ3lpƒdfbB ô0Á Üð!ù|b" Z`† Nx€,˜PB=L°Â7|B¾€˜ˆ‚˜aƒ ^ [HL(¡&Xá€>!_DLDA ̰Ã/-&&”Ð@¬pÀ ‚/!&¢ …Øá‚È,Ä„èa‚¸áCò¥ÄD´0Â;\ð"Ù2bB ô0Á Üð!ùrb" :a.x€l1¡„z˜`…nø„|%1¡†FX`‡ ^ [EL(¡&Xá€>!_M†:a.x€ÌJL(¡&Xá€>Q| 1¡†FX`‡ ^ K$&”Ð@¬pÀ(NX¥ ¯%&PC#,°Ã/­#&”Ð@¬pÀ ?¤ë‰ ÔÐÁ¸^œOL¸àE² Ä„èa‚xà‡t#1¡€:a.x€ÌFL(¡&Xá„~H7 ¨¡ƒØá‚È6Jh ‡ 68áÒ-Ä„jè`„v¸àE²­Ä„èa† Nxà‡t1¡€:a.x€l;1¡„˜aƒø!ÝAL( †FX`‡ ^ ³Jha€68áÒÄ„jè`„v¸àE²$b" Z`† Nxà‡t1¡€:a.x€l7mQÐÂ3lpÂ?¤{ˆ ÔÐÁ ìpÁ‹ä{‰‰(ha€68áÒ}Ä„jè`„v¸à…ÔÁïA5t0Â;\ð"Ù~ÊJh ‡ V8à†ÒÄ„jè`„v¸àE²ƒÄ„èa‚xà‡ôbB5t0Â;\ð"Ù!bB ô0Á '<ðCz˜˜P@ Œ°À¼@v„˜PB=L°Á ü:‰ ÔÐÁ ìpÁ‹dG‰yTœŒELèa† Nxà‡4™˜P@ ĉYâ©•v¸àE²cÄ<&Nu'& 0Ã'<ðCzœ˜P@ Œ°À¼@v‚˜PB ̰Á üž$&PC#,°Ã/"&¢ …fØà„~H]Ä„jè`„v¸àE²Ó´ DA ̰ÁyZ<‘‰˜¦ ¨¡ƒØá‚ÈÏQÐÂ3lpÂ?¤g‰ ÔÐÁ ìpÁ‹ ä爉(ha€68áÒóÄ„jè`„v¸àCò ÄD´0À œðÀ©›˜P@ Œ°À7|B~‘˜ˆ‚˜aƒø!½DL( †FXà€>!¿LLDA ̰Á ü^!&PC#¬pÀ ‚_%&¢ …fØà„~H¯ ¨¡ƒ V8à†AȯQÐÂ3lpÂ?¤bB5ô0Á Üð!ù b" Z`† Nxà‡ô&1¡€z˜`…nø„ü1- 0Ã'<ðCz›˜PB=L°Â7|B~‡˜ˆ‚˜aƒø!½K†èa‚¸áCò{ÄD´0À œðÀ™—˜PB=L°Â7|B~Ÿ˜ˆ‚˜aƒ K%&”Ð@¬pÀ ‚? &¢ …fØà„ÈJh ‡ V8à†AÈQÐÂ3lpÁ‹d‰ %4ÐÃ+pÇ äOˆ‰(ha€ö'?Ÿ™%þ”üïß‹þÇß#~þsÿÿ¼Æå°ŸÏ´òä'ö¾ÿÿgâùV·ùYFÇÿƈ”ü|.—ø³?ÓÏçÕü×øó?þÞà?þÞè?þÞì?þÞRòóùPâø'E~½†ønŠi`%aj'a¸‘Üüõ3¤*£Ñ¯ûÍ!K°Gq ÏÝs–—7_ Ðcüºïì.á ¾ 7Ÿ± ê@>ƒyX—égœÇ#3åŒ?Ñ ½0³`ŤàÞ!, 9B5D¡;â0=‹8¹\"Ù‰“¸?2QHQу1ù×=iÛŒëxÈG.ÿu_ZôÇD,Ä&û™oqoÚW„g•HÊ¢.Ú¢/ÆÂŒõYÅÈÉ<Ä'äÌ&‘”„­ƒQ˜5Ø‹3ðfûùÜ2ñ¼*ñÌ ño†Å¿y÷lkÅ}kâ^3u<œ\‰û°Ä½SÔ?§¸GIÜW$î÷ïˆ{nÄ}2¥ùݲü®¸‡DÜ÷!îÕ÷Wˆ{"Ä} âÞq¿€¸Æ_›vVWœvÈû׬ÅufqmX\Ï×`ÅuSq­S\ŸäMJÛRÞâÚ¸Þ&®‘‰ëZâZ”¸~$®ùˆë41¼~,¯/®aˆëâZØß{òb]ì}‹ýj±Ç<Š×Íë‹ýW±g*ö9ÅÞd<¯/öžØkûc³yý¹¼¾Ø;û=bF쫈½±!öÄ>kv9kì XC‹u¯X«Šõ¥XŠuœX{‰õ’Xã°Þ±>ÐÎ}bÎ.æÙbn,æ³b*æb®'ægÌ•dÌmbî"æbŽ Æu1‹ñSŒybœc }¼”>Ù/ú]ÑWŠþí?ú¢Nÿþï:ñ¨¸ø­5.t¬’¤•´<^hN–Äuÿ1{ÓÿsÂhòñùÿ}âßÿõþç·?øó¿ü?Jíi:bŒJrrãéÅ=V$K"§Ä*ÎDYv¿NŠxç3É÷wâϬŸz–\.Ž ¸uµÈ\•dï¢ÓÛÅï]/Ü7Äc ã÷ÑNŒQIRþÚ×>e²8žnöÞâ1å£s™†«$Iõn´þ´(Y²L›åQ²x dÎ ÅcÚÃ÷Î[š,¹ùxùôâØŒ›åk—¥’lÕNx{[²dä([Õ}¶¯»¼úas_•dÙ–3%KÒ6Ÿz¹d ¿ßw¶­ö •$fç üæñ¹öù#WŠÇ@œ›šeºJR;ÇÅó«‰Û72í/ëþ{|IÅ^[6µRI" œm´g0¿W}C¾üâøúY?bONRI®$'ÔË·6Y=·ù¸-¡¹þê¡T’då3ÙBk²$\ºç†-äã>‡ÿÌßè×Ñ7Úñß­¯ºïØØdI²­ÐéU£ÄñiåZwê§’¤©Y5…Ê’XtG£kâ8ïÖ¶oSÞe¯Ÿ7›<‰^«n&DÑ•b^Œ$“^Ö)Ïë§/©\ã/‘ç-ãU¢<^Ø;š<§ ,nHõ¸ÏÔ^Í®vQI nYÝá©ÈóBÕábÑuçzôcÊ»æËÎ…'KR|·‹zEžß½™32Ž÷ïYsÞAž[TÖeð‘çÄ/=.‹c‹†?ÎX¹¡x¬¿¢Þ½»¶¯ëiÝã£V%‰®òÛë6&Ê{Ú’îòÿáQ“úŠÇU¯kf"Ïѹ·—q‘gÕ÷Þ—·“ŸÊãŠt>EÜ”Ò{úY"~¹ Eó6¥¾ôhûDä¹r·2 ÉsrXÛqoF«$7ýž"ÕÉsÿé+Wß õX×LgåC¨×eÞõ›sšr~Ö0üàÕû\ö3Ï•/æ|Ø\ƒëÞô€<«Ú§,Ù/ŽG™×akB•¤Ë“±~ò3ktôÑ)h­½6õWI&ìk9}›8~©¤&=Fäùëõy™éDò™æ%µõl϶uƒEžwü6#–ú7nò…v«Ésí¾‡[%…Êó§—ë×D«$-¸ÌßEž“Ëå¨&ò¼hà]{¬J2òS§"5Èsd®Ç¿½ÇF®ËžS§’äLYum©8æ¸Î‘ hÏ)cšéƪ$†uW‹ö&Ïi]3uùl ñº<Ïç쨒$N²©¿)YñOÍÄ ¢=è2lLw•äR¹!Á­ä9ùîýEžÏNì¡åµÔÁÄMlUÔ•çg‡‡=¯K}œV¼[¶!¼ÏÙÛÛ÷Ç£›ÜQOÛ*ÌoNž“l¥Vÿ•瘶ñÉ”sê„\ï_SÎ1ù/¼{²=ù™ç|_fä­M;lüõåÑž?õL>-ò\yžñr•äÙêC³Ìçsß²)LqûCì“.âxì…ƒŽŠÇÀç|¿u˜Ès¥+þ¢^Ô>2G݇×oÑ ×êIâõ;Äõ<:X”÷yÕ^ò<³©|Ö‰Py.•ðÏi•J’ð­Õçì¢=½ú`n.òÜ)ŸÚ^%¹–¶(²±8&Ë|çŸ"϶«½ZhT’¹¯ææõ‘ç.G?nÏÅÏãǽ:<€ú[÷¯õ£ÈsÂôì ²o ñº3{4ÿL{+øäîßcMämc}û‘犲œi­’̼ë®rLôÛY‡-üDž%Y/|ŸM÷mÜ—Çïl|ÿ¬ÒšF¡ò<óE@Y‰<ß;] yV;œ0TôÛ¾ÛÚ0N9ešÓ‘<‡O¬ówþÇŸIÆËÆVëÉ88#W,kèGZ—âqüÕ£_ãn™Åg¯3‰ºÑxÌWqœÐq¿ò‚ȳ$¨{øª¡ḋƒ-r‘gÕÍ”2‘çøB?|m(ù°Ê©p‰ãOµÔŠÇÚKg7&aœ,9ë ò‘”¹Iä9áøÍáù÷† (sØ.Žu–?*Ï’WE¿?h(IŸ¹lnnÊ9ñõºúmEžãã§Jj>ÝÑ¥9y¬ðhž,³8vP•Ó3”÷smXrÅWä¹ÿg~ÇàDÇÿ!ϧ’ø­÷¾ ä¹fïÔIá¡ò,ùsjØË†’¸f'ÊL2‰Ç\Ÿ5¦Š<«,͇½k(Iêö,%E·3Sõá»Èó¥‡sGß<²WŒÏñ#Î8¬ý(oÉþj×Å10½¦Lù±H<>¶šk ó”VùGÅ’çÈ©c¾ØB¼îo'bÚw$Þ‰ió˜xÿçǺEž¯ý¤ýµ|rÝí¢=÷+|"ò\ÕW#i$õ"üö‹‡ÄM»Z¶VÍPyÞ<¼{›º¼¯Öo·eyþ³Œ^+ò|u`­ZCùþ~º©…è·¿ej!Tžß¶‰ãºg.qàŸG5ú1äøÜágžç8‹loÇëßòœ?NžãlÇw­óí.ó³¥üû?NÌ!Ïß ï%ÖUáÙ+o)æ;oïWàõi½DÍë'ŽË²Ã3vХʬ±ä¹œ_Öta¨<—‹y3‡þ"ù똺ÏÈsx¦ s‰öœt;×>Ÿ$¢Q‘Åâ8ueîû"ÏC —UÆñuý¡ÊÈsÑá·+ˆãµT[æý¾–“<'ÅzwynÙ¤s‰IŒÊ§‰HO}R4Tž·÷{² ?_\¹ÇSæÛ©YRÏ«Ä<ìK•óF³ùÉ\C¬«ŽészCÍÞHªf&?‘T?K9/N¾òîzÈùöØŸyžwùݬÎâóóK-ä9úåºåcD{.ü׋âƒÅ:òŸË‹Észs¶ˆö¬úg}ÚID ùp-y~µæqëÑž2Ì Ý&LVõ£8NoSž?K‹<_ùØùôÊ-UÛŒÊ5¢Nÿ–ú‹ãu&ŒkO}©p¸õïr±®šzDZ=DžU¿]©6A7Sß²„<7`¿°%d{¶þÌsý–zô¤<_ä¿ÕSÌ·÷F}k!ŽEù~+Óìa¼Ÿ‰K¤íÈsø›‚Qq"Ï•dßëѾ¢×•Ï,Ž;<¼|wSqÜaüÛ©%+“—Ú“ª^ÜMžÓ‹ì›úríþM½GûâtÉóóâ6UCå¹Gî§ý(ïK3²^G¼˜ß_Î_*öIîzõØÀ8\þá¼óäqî«Ä,«Å|{ñ¾êDýÊü¼Kwòœ¢_ßt9?Ù¯öÅû®£ÞæÙš,y¼?G•鶯۽ÿ»>âxÀÓ3|šNy7mÓDOž“ŸTi覿ˆž|zi>òœÚ¢Ý1XW¥Îîû„ò(ºô\=q´—Õ»*Ï©¾ÃTO] ¢=çÉ—þàoÞç™´¿çN¥~–íÙ7-‘¯S ‡„ÈsÌë÷M”KÂ=ßÛ‰c 3ŒÌ?,d{>ù3ÏY ò„Þäs|üö‚cűnßÊD¿­1}ùJ?œhÝŸïJü«Š¢ßÎ\tœ\̃?©NßãïÓâÆ=âX¸þŠÍ¥~÷ÿgÇÃÞäyBçI«6‘ç¤CšúNúÙ¸”¿I;(—ÍŠcÏC³Ùâý›zÞ{pöá|ž$Ù§Q"Ïaߟõ%Ÿ7oF¬²ÇüdžŠ~û©¦`…xÞÏÊyû‹ç»”©}øyø×uæ.L–äÐ_9"Dže»¥QÎc<¯gý œ§¿~R)d{~þožãQÃôǽ­qšþ8AÓ|ñYq<]]é–>âx«ì“²®&Ï黿7¸%öûòÜ((.ö/[ygŠ~yÚˆüCÄñ7Wò=äý†¿X’‘×ua_bgq\ñ•éEЉ|ûÞ&ÏÝ•,•gç©Ôž”C䓾ŽhϹ÷>}J?Ÿ’}ì ú·ÍþÊÓ™<~³÷Ž«+޳*ëØ~š|ŒY¹üÄ%Æéë¿–ãóׯçó¾úµ¨yïÜÁþcãÝøç›fnýWæü{GòùZhž¿’ˆö<(Û€qìø™íû‹ã¬ž ÉäásÆ7iZ-3õ¨`™kµž·Ìõ»bCå9›iò} ÿ¼× Ì3bZýÖ­#yV-¬Y¼&ãZø#wö¹ä¹Ì¦¯N†8Î*âr¦ª]Äxô¨]+å|s_ÆÞ·ÿûqVhйçüÛ ·ì?W[¼àÔKˆóé2.› Χ;Ù ïQPÜŸG«T®KbÇóñã¾äƒÎ,3¼‚¨Hµ#«çYÔ¨*Ïúʰ%TÈ„æ³ö¥¥jƒ¿ÇñÆ+ïú`;ñFÊkÔunÙ›ÞÁRt”—FäM§¡Æì²G¢UC¦Ô*FÁh0'§L ÀC-š/,ø$³ûçÒŠ˜âZ;NœCØ|þ»âÂ3ÂgQ'›$î¸Æ@ù¡ôîí!-y¹.®€èÀÂ2ì/M¼hù¹O‡Å†\™¡E—ã}óàÛQsNàó§¾öGÇ‹s6}…—Xˆ›¹ø°nÅB%Ú¹SÊÄ]Õáî–¾âܲ )í¿éø^7÷ù:†gºmú‹D§&}«Ý#D¢ã[Úòîå}ô?:ö`U½v½}BÈóéjþÌsBô©ob`2OÏ,!Ïiû +-òÜê•) H•X/l'9ëɦ3óˆ<·Úžé éä§Çv"þ†uŸ_%‹ëöšåð~—Í8±yŽ[›Q¶˜<§Ý<êÚÇ@¢R6‰èGž%´Ó¯†ZÈflZ&·8§øVzæñâ\²2k~PßâËu¹ÒŒ+bZâ®Cä1Ú¤*'ò<3õãsÊ=½tË …Èó‡F[ªÍçºòWç¥ÇÍÝ]¢Ÿ8o|hµmCå¹õàø LT´Ð¦ð:ªÉövâÜ4‰lâé“ iúåïÅ9º7É‹Ä9ºq®ÌýÞñz‘š‘Ú:Ä­éoSúâÚñs6)Z’‰Pr°i'?»˜@ó¬Ëu´Ÿ=Ï›vX øÃ¨0òœ²òx1_ˆqj–ÖºŒ|îgŠœH¢œ¿­:øí·çÓuû™çŠ•3íçh…5Þ9[tPûJOÁ÷åÔ)ÇçLÍqòJ©¹â\ºçWÎ À\“оǴ;p­1ñçz§Oî.Îíê\tNL€/ήš@ù&yå‰Tþ•¼ÿÍΆÛL|oîø^±Wˆ<Ç×y÷N¼~ÖÒã{’çøe;S äYÕ¨±±¯‰8ŸVˆ!µŽæ ¾M»Ê ¤Þ¥ýõÏ”¢_¹»~i$?OèüÅÓ‰ú•<±S„˜DÔêšãé†åÑ2qŽgŒ˜ l÷Ä’çôü¥"Šˆ<Ïiè€EífŸ-Î!sv#ï#²å±º]—‹ ÈÜc—‰Þ´Iõ1¡ò¼âÈZyŒ¸ð²È=p (¯ZÛ*’ç4Å­¥¥éŸnV:Q`ø:®Å†ÈsÒÔµ'"Ä9É;ÒŽ”ùBÙ¤Õ!Ïô3ÏkÖEöd¡ž¼Nz9yNj‘óNGÊY2÷aó#ô“%r\ŸI=ŽTØ^x†8ç혳<ùLê3!¯“ø‘³LE_‰QZ©k7qÎûÙ&ÚϼOÕ…C­ÖQßãî (3‹þ,Á2¨\cò¼øé§÷mÿý}ŧŒøtN,”.Ìx•‰òÞÐlb*ýfê‚nÕŦ’Cu×|ç=ûM窈<÷­¢»G=Œ+™ñ…‚<ç;”ž÷?ßÓ«Ý"êWíã­^Åg£Örldˆ<«º7TFԧͯ۽gœZäñÜ#¢žÅø‰|GŒšðt€ØÜ ~þ»‰Ÿ|¸aõ9Å]ám/â~ØòzWÁyŽoÝúêb&¼ñ5êin‹þ3bë°]ý¨Å›ýBLTÏt«ãúš X}%ÄÄ*qïæ7û)gIŸE~qó³ 5‡ yôÆóŸ)}ÒWÆÃä,é›'DRXuŸ~Så}W¾5õ:ñÒ´®Ä9ÈŽö¥Í|>ÉŒ9WbñMá¤*ÄO“ða¸˜à6›‘*Îó¼=NÓIä¹á°MÅx™\8k^Ê!©€~ü*&D+Â_uíh ñ¾¾¾¸‚ú¿2OöfbÜ=[Ú@œû³ÈÓI¼„Vw›™yýhCÞ–ú•˜™ßâÓ¥Þ¬ÿjåµyòÀ1âü÷£E–½§ÜÓåm]çé?Ü»ž K±‘ß«E°/ýsÌéÒqä9©®ûTgòœº-K¡Q|/y×á Ž¤Óó+­gü–ä©?AœÇ>óQβ‰ë‹-p9)1Dü´égïÃçž¹Fò,©)]Z…5·£bÚSxíùoà}>;=1rß_zy<ÿ"ƯK©Ã\Ï'·WúÔÏjœ¼>³€V,äß}6œ…zŒÂ²w.ßKvÜKG»<Ó¾ yTåÍÞ9‰ï“¥ÝËžã…ÉšRQlˆ>ÈPà•¸0ýwþ¼­)ÿ 3?*“ç˜Úuºµa5½zÅ¥Ô+ÉäÞ‘ŸD?–6ñóUñýú·Êçb^ï^Ñ¢ä9nÓ«yD9´)]‚¼^šõ`¹–¸‹‡ÙŽ«BäY2¥÷& ›ø6þ]‹ÄFØ_÷5|/I?t½ ã_D‡…+1¯Šøûµinˆñ6ùôEÉYÊ5aVDzç¸ùq+Ó—„x]UÚ¿yNî½þàøbá2vQSÑ_¦ïš}S,02õëÔB, œª5H,œ¯4¬ÙQÌCû-Y+.]:9¬ôKqtBê_}E?òjíqNoÒÒêÏ|¼Ï¬¥«>á÷Œ­¹ +å¬*Ö«"y®¸~áÙb¡8ʽÍ'æ]ºð» ÄFØö ÷.2n&êªû›~:©ý¢àrò˜Z¦¿æ.ãIBÞ:ýb¼*ÛñÄ1έŸãß…u.Iáåô#‘Ï—žŸlK–üÖ·þÁ‡!æÃñ§O½¾+æ}Ëç$Ϋ3^|F=K½æ-ÛÁÄ׉ËbÅ‚f˜äwyO«}g¼‚þ"ænþâ<Ñ=²Œ_°&Dü?û¦Þ¤ýJ–JËyÅúfη,aä9Â|5>(ÆùI«VˆóD÷îkÚüFˆ~8.ìtß&âüÔíw®‹ mÊL×iBå99ÿÜÛsÄÇË;Äü:}XÃe|hú>­¹Èó¸Û•|®Ä\÷Zˆ¾ä×ÿÞ4MlœÏ. ív¤bYQ¯>œèp‘rN«Y±LMò{i‹òO±`¾wìM=òÜbæÐÃ/í ¬ÓòuL“8ÏðãdòÕ£ÓbUºEûu|y»ß+%Ϫ*¹Ö—"á®3óНÍe;¿ˆñäÕöYÉï¥J±e?‰ùWÃlO¯Ò×~™ížèGâ²®<Ç×·ÛÝl"ωqu3ùÄ:êöü§×ùš¼ªï$¿¨goÆÝM}—D%–\-ÖSQ;F_¢O~>¦v3âõ|4º–˜ü˜¾À"Ïß¾÷$¿{£gEÅׇ/ï÷¥\ÙqèRÆÙ„—ŽSù³°hWyˆ<'ÞÙ»K”ï>{x¢èä%ŽlY"Ϫz?ó\Y—r]lh.rí˜çΙwß'Æåå¾”cÄò¸V'Äü«ãÎ i|¾8Yb“Äxà^m›(nìˆØº¦š/ì¶›ÅÆÉÖ»šó>cJ\tͤü7Ÿÿúåí8¹H¾€‰v]û@‡{GBôŸÉ#/dîfçKŸJÞ@»Ž)~¹PuÆëè©ß·fó_•>ÙÉcò¸ysðþ"«/¯_ˆ÷‘x1¾~[1{£Ø?´û‚aoÉE?bO8”]ä[›asˆy’ªÍâ>ÚqjÆsk‹y×–ÓŸˆså·ž3dç`¶?÷‡—œ*.ôtÊ Æ…1G—¶7««í„©¡.4¾7w‚¸@õDÝ9ZÇÿ¹|ƒ±¼ÞìÔ›¥Ä¹Îf7Kóšèï…[…oï6mžW\øŸ›X×E9‡·»ùþÝ¢¿Ÿóož“»½Ëñžù—$pckÉbý¿6—˜—݉¾9xޏÕvÓ=>_òð!çÿõx«èFâ«V8·îó®­7sh_ý¥C÷ÜçCßÝvJÇûìòûÂç)÷{Î-Å<»öžœÇ×ÛŸ=¯õ#D»Jî±sƒo’¿N¯û‰Ÿº~jÇû /v¶ë31®“'Þ+ömî¥U§ü žVB̼»Yˆ÷Ý'J¬ûbú«ØTô{wÍm¸A¬»NýbüLžS¿÷=ñùºU±/ ÿN>1#ÖM= ¯6Ï܆ú”ê {ü†q+í¾£UêCÊ„JšæÄM9\»Ë+â¦[5KêÄ’Æû‹yvøÀcè¿UéçúeŽëç_×ÐDžïvk>í$k•/iÃCôÃY{‡÷ëO»˜=Ä™úï¿TRõ­*Ï*Ó¿yNÔ_ Ï"Æ¿¡KvûÏWç8£óœ·]÷2Ï‘Lüqñ•Øoz?v_?1?™8ÇtTœÛžge–gä1üÕ éÿоú7Þg.ÆçLîzï3)rZa)?ßÐìÚeÝvæe×jöÑ®FVYÙ¤Vˆ<§uŸqyW¼¸±æãd1{[扎þfÌZMc'ñb©¿¬ç}]6­qV1žèâ[åó^š>øÆcqÞûÓˆw]ÅÏoüC\Kó}6v÷Xý`w«Pó¤§e÷œ&æIC .êYêÛGk¶Pφ=aëʨco²Š _½y’ö~MÒåØÙü±FãºÄíÒ{ìöz!ö±TÕÄ…/ÉäŠW,âBçõaÎ(ÖYùf˜eb$rÙyßÊ5µo|Ó!úáÔïó‰ý‘ð3r¦Qε+îÜaˆÏ•¼íß<Ç<CÌg'[9q9yö&çßCÈã’ ç^‹ßâ'×}+Æå°Û+òùZ\Î|éõ.Rù¶hn±ï4ºñdqþ{ÖêõÑô?©¥˜&úóë:Ö¢}ÕìzG²dYË63Sÿóibcu!úÏ´ÖRýÅ8ßèV±ÁÞ2F3L\ØŽ½úu6õ6õP†ÁÝ)wÕ’e >˜¨OªbsÑ¿$ž8›³´X×·^q0?ϺáÈññ¬ãƺvuõêÒ?oΗ1OJË™õú‘ÇOÇ‚]7"\:?Z\OY9~¨ï§?l/@9HÊ”‰ùÚùùÿ)i#î•ÎÃ$ij¹óèL¡nìð«WÈ󹪴ulí#úIib| õèÝÕ¦Qÿ:mz;@ìGÄ•ž¶1DûŒ Ó:ž‹ýΡ÷V|7Ö,.·dAˆ×U]þ7Ï’k-[gëÓ¨ë“¶Š öeëeKÄçs¼^Pvš8¨HìKú³KK¿?rˆ}‹R'F^ýsÅlÝ«ÿÒøë¯RÏfŸì6Jì?Õùòyª¸@<¥~‰Ê=s¿šYNÙYßj;ô —/.ý27D»Š;ßâU~1ß]лieÆåÔU=O c<‰Þ‘VR-nðÛUÞ…~4nwãµ×É˳üïÂ>ˆùÀ¥l{wSß.Õ­Xÿ8ýù´ŠçºOc¼ˆ‰ëXbƒ¸€_'ë‡!ÆÏHó²Î&?¿òz“WŒË)å'Õõ¬×ÊÒWÄ»°[ ŠR1ƒÇwËF9ôÿt 5hûÔ U&·à•È©ÎP롦9"ÿ7’ŒŸùi/ýµ¤Iý}{%Kî^½Òª§¨÷Y—L7HLÕ•N Ñ>#nØ™z•¾þ±Ÿr~Väýõƒ ÿý÷%ÉŸíyÖ£éïÈsüÆ ÃÍb_lÜ´5ãľ\Á ²Âü÷‰ 7>¦6-ôy#ýY¸kfàý“êv|Õ&bÞ«ü¶±•h_Ƕ”#Ú‹»EÚXÑ¿N{:Ã|1ž¥Ÿ¹·“r¹þÇŽí´«.o§¾Ö„hW ƒ´·þ7†¼Ð1M\ ë¼¥?ãÉG¹;…ÑOE´V'¯›)æyE‚Ûh_ϼ‘ÓÜb©îm.Æí¾ÛJôáç'õõÇNÚ..Pftíõ*vÑò°ãgj÷c—:ˆ}݉+uûbµî·#ö»Ûgú㡸±Ù6ºPAQÇ~,é)Ö[W¿$öo“®^Šñ2±ÁÐ!!æÉñ·ÂÆÖa<”XÊb_¬â”œ{òþ'ìu%Ï —¼•ä|¾è+ÓWì Ñ>£”®UP¬ÛïÊÞûü½ÆçÝ>?DžUEç‰wsãHjb!K/ê›dAùðt¯wXš8˜ñ"ùcóñêUü·Su¡úÕ›×îäûª¾­ô׉fçó增Yg|$Êñr܉씃jL1ËrÚ{ô¡ãŸ-b½8ï}ž6Ä _¸)5<Ô<¹ð¹w5iG’1ÝGÛé¯ãmãw»gú…) ¨ÿÑ‹ËMº»H\¨¥Û¢}&]M¬a¥œ£ËÖKr‰úÐ_¸6/Äë&7ú7Ïñ½ï¦mxM¸{uØ+¾¶+ë»Tû‘mÅÄüõP‰ØµbŸ·ã…ÃϨ3ë¯> ÖªwRìb?*ÜßA\Çê24Î"QÌWüu[‹÷™ý\ù.”»ùÏlßßçð®cøy…>ƒv­1_‘TÉuÿ™È³<ƒ§Ë±>T¾ªÇxR¡ŒnB7Ê1uÂá˧‹ù‘íe]ÚW´kK¢¸)±ÇãZêÛ¥SNæç¯¼î­Å?™øíÇ(Þo¢æ„èW£‹-t^äùÑè …ÅõÙ ²—u¨g—þ(]m ¨ïëÆ–!ÖGSž¦ÿÛ¾¨F™vÌóTµ'œ]#ö…dµ§„ZÇ&Δ-çsH‚Òþ è¯%gŸZÝ’%7ÏŸ(Vœñ,¾añËÅúß3™"Dû<ùumŽ¢kÅ:rµj#ï+úpŽzoæ„(OÕß?Ûs©˜6Ä N؈}×2ÏïgÞ—óuæE"ÏšõzC?•«§Ü•²C5ûî"n÷­ÖóßÅ·(ñ×¾û·—¶ö&® v4'H;ŽójOuú»¬ =û^ì¿Ì,ý“Ÿ‡O]cÿ@ý“œ¾1!/õ&1sÏ›â€Ä–åŸHøyí‚/a¼ˆy Ìÿ·¨WM§t †hoª…5«f‰ñ³\ü ±Ïon‘éãEÁÏÝõÖî)³“rŠ/Ò%îõ ©å‡/Hž²G *öñm™3×1J}ºöŒQäùlòBýu|MåÞÞ]’%{‡íî¶–<' ª¿°ØoÊÜOš5DûÅðÊw‘<'WIÈßœþ¾ÿ©ÂÑÿ„º¾¢Íøø ñ’GlŒ[)ÊeŨ^ú™”°¸[“źýÁª61~ëÇ5kCûº9k¾Cä1uõž©Óÿðußa9ýÿÇ(;YÙddŒÈ$+! •ˆÌ‘[*!ÉÊNFÙÉJVn!;Y™%+;Y •ßóUŸëú}ÿ¹o×õ¸¢ûxŸs¿_ïyÎyŸCýóŠêæËçMWÏ|*qîÙí\?Ê•QÄÒïtÌKÃŽ¤%W–ëêvë,ŽPÕ;οr¦œ=× —´W/„§=‹4 «‘/7ªýÔû¼ñ¼÷›Ê/ÊüýCõZçtô«ªRu?¿–ïóæ„úõX}¶ÇCµ#åeYÿß5wË «i¿Èõ˜C.ÝX®=®W÷Œ*ú”™”³ŽöÔþÖ¤}—êÈOÕÑÂ8»Í ÷J’8wéß\οªfZu`<˜›zô »ŒûžOéS{ž|¾?éýUü•ÿN猇ӃÈW£šóK¼!_ï÷yçTœ~FUæêôõ”¯°òŸwΦýŒ_þ(½ÝQ¹¾QÜ Mò»ÚÕ£Çtä·Ê§fƒqrý$ dœõXó¯j‹…´3nÍo¹— }t+ý|¡¡\þ“úp»ô“ßš“y»e‡ õdþëí²ü%õ|lÃ/³mè/RRN) (Á¶êÆ“tÔ7õ‹EQ»%Î)³¯}’ë:?6$ÒŸÆÆ×Ñßµºð†ë­†²áE›E³ä:äð·íÏÛ«šþFºaÁuwÖѯjê;֓r£¬=Òo)õXÙºêû:²îȯsˉ³jãº%¨ÇFUªŽñÖžÎÖ[WÇÉ Jã­67’ëw&»åüö?,Œ³ÙÀŽË]åºÌ¤/i‰Ì딹Ç_¯s“…>åÖËy ¥é©¯ÔcåRü;ô—å +x4“ñ–ªv‰Ñäk¬þÇjäûî¦SÙ¿I»šM)_ÙÉý®O¡Ÿ½iRìSÈQ¹Q²O?ò;÷G/Ÿ:ò;xhþë[rÜoýõçË ÛU­@;sÓ6¬Ù|é_o©6=“ñ®}¬û›¹rcíÙ/ˆsÆð/sÊ›ÛË+ùü÷—âbe¡É²¬Ïô›ÆJ]¥£¾Y5ôj*íЈEÆõä<šGÎ0ßíÒ­×i׺Îy€zœñxþ}¹±|ÁÊӲУàÑJ¹N›]òïÛQ:Ú[ÍñÃf%Î+¾ThK=Vz¶ü1D£„´ÿ>=[Î÷žæ)÷1ìòÿÛ2H{:?º¦M9G>GŽžéµGêq­nµ/Ѿ½¢Ê/j·ïè÷y+q~÷å.ïŸ_º™þãø“VvFrÿ€uýÒÆr£o÷Ñ pœñ}½˜&ó”$ÿ€62Ï—ñ2€|mlÖS-ó&“]æKöï;«=‰üF›—içÍ«½–릗3Ö-Õ‘ßsn>Ê•ï©nëÝ&XŽÖU[O?’[wiöâ¬vÛZm%í§Ò¡G÷º”¿û[fœåV^½\%ýµO@G>ßl™êùœ8+ö†[S^;}}8JÇøfÎôï"$Ϋ®þ[-çûë݈¦ÙèxÔVÊ»žm~W¹Ìý’‹\¯7¶öÝÖþ Üg¢jÞtÕ*N±ÐÑÞz·ÿ’ûZâü>ôçí¡üÌ-÷b»=ùóvâ«þQ2O±Û¿‘¸ÅSGÜúëo]þºº¥q ý¤flx^S9¯œ®×xü`~ZwïÝv ÿÍò7[‰sF•æñrûûò cuÄ-8é×åáÄÙäÔÆU+ä†Ô|Ï‘²0Dë•maœsÓ>Ü'qþ¾¬ÆfÆÊîf#‡Ð®Ìôm³aƒ´¯ý{çÞ$¾*½cUóýõW½–8/ø¨ñ”ó Ÿ&/N¾¾ÔDWj+󃀲šËùág þ‰¿q\±l÷cr]ñåìYx9åp÷:Ê}¶áL×ëA²€OýqÑX©¿]ï”eRjËŸp‰ó¿à.WäÆã%ÎÓÈ÷«•œÎµ‘óp%÷Å,!þ>æŸVóùð‘%‡猬Xó¶2ÿ³˜ücŽ8˜4©\ÁíÔ‡V/3”ëé î8ÑÙ—­z]êsÂéK©çê“׿l£ž‹R%ï \ÇLé¼Zêáþžó’tµŸÏ×'qî©ìxE|•ÀíóªôÕ(ÍßFÛ¿"Î&Ó¦n¸%óóßWd,ÔžN¿ηÂå¼bjüétò9ò÷ö¼sótìWãYç½›f{ÈõåU·Ý­¥ß¨aöm+ýÆVӤײðA}qÊ¥)ß›Îù¨<¨W»†.«$çé‚N=~N¥®0bw[¹¯$wÐx‰ó‰{»òå<ÃÆZoÓÞ[ö¹zy#qVunÝO®û4MìW!Hûq…©â^Jâ±{Óë'´/êšý¾¢})1ÊhY *¿ßÌAæ[•6ß®B½ÚZ9îÓb‰s×Kä<¸ÛšU÷›ð¹ÃïAÄ9r‘ržËtÁÁ3:â0Ç:q”ÜŸ¢¶-ù¥ ñUþ¸ß€qÊýÓ-ÎäK}Þc¹ÄW®7m˜ãB;³w~«ýŸöuó÷ÊõÚÄ»­©µ§ï6¶y‡p‰ó‚;öY¶üì[Å㣵FYþ¡«^½hþïœøªz¶øúiŽöt¬"6XÞ”|^>OêõÄ€A¾Ú·WTk ã¼qrrú 9¾óþöñÒžlý–z–òæðሟ,dR—h·¦ŒÃËlÿDÿíµ!Ù´“,Ômo}ÎFî/šàüôåüÃ/·raRΦOw¼$÷-œLû2•ü.—èYìq69÷Z¯8ýIJóÄ :Ú%£ÉëëZJœû½¸͸TtiÒaÆ¥a¿;8W•óY%û 9"çs4Û£ºRï‚OlvYî¾`ßÓQæ-W öMæóS?Öö>DœUãïçÞëlzÑ3Vèhߌ–¥nú"íö¥ƒ)g¤ÿ:3ïÅ5Ê—Zßhsɇ´Øš²Kã^ûg%ò!+Ôÿâ â¬Ì‰íê%ýWŠùÆÑ:êUFñµæç%ÎY“®}’u5TŒWi”ýŸÊ¥ Šæß}mÏËý¶õSë}öÑžNëy9¿ÈgUi§Ã=fÉ‚™è:©Óµo¯hâ 㜽ߨÉ]‰sƒ[gôãgW—Žmm(¯ÛÖ’(Q¥ò·Ëù›}Ö×Ѯǭ=Dꪽ"œúJ~¼àñÆC£$vÉjwDâl6 ¾˜,ôÜ»¬íÀ³‹+ßgMà«jîҞןjå¨#?ÜÂjÎ+q^÷+ÆC<·œýÂŽyÌ=‹Õ=$Ιë”üvùP}ù]ÎÄaÈo9ÿ²§WÅñrßUÿÇ–sø<¤Ö ÛÄYÝcô¥*2ÿŸ§®6[û~ƒ« Ýo"ãåNæÇ¿É‰X+ߌ—ž=´1¶•þëÌ*w‰Hí³‹ˆ¿²öÌ÷sÄYí¼£úüÙr|ó»c:âãf6«„Ü_©”óÊOìÆO¯ÙÕ~wÒ(uo5¾6+štìÖMIxm06¾â©=JM6¾«!óí§óf_’þt_“ï{&kß^Q¥ÆÙ-qlÛL‰sÓK;&ôàçÑÞ8ŽÜ¥ÏmÚIœ›_s¼# ƒGz4›úžlzF™,q¶Ó{ôKÆçÑŽ3›ðýýg=vUÚ“W.k¦ÈõÕš5g6àóe>N#ó¥~õåª,Öxž_ꬣü… tvö“8·L.k)íÛ3ëß´oS?\zç&q^Öà¹å(~žî_ý ñ¸¿²²]]9ýøÓ¾Š´³Šm³_”ÇnµZ®ËúìzòþGŽŸ;¦Ò4íûµ]ÚjV/‰s¦ïÑ®òÔÑC:ަÿ,7áM½Ñçmßné“Ê“  ·'7ì±ú…ĹíüÌ\Ÿ·nÚ0’öô#WvYÿSâ¼÷ÙÝmíùÙÉ÷ö,3b}áç±°hþ='íÔ$å®#¦Ùk'ó°W‹ÿý”|¾0nKøíÛ+šâ« ãüéÛ÷¯g'oÕ^ö«<ÚÜëG Æ7sï8©$ΉKþdK;3üÓú3´3ïLÏ7ö•þùfÊ–ÒÎW\¾ïÃr)¾³ªÔç‚Ò©áCå:“ï·iäG—>£ÊçßšA‡ÞÉ}­KöGwÓ~\f%cÏ…ÆyÃñÎùÙ³l™…äK䃀¡>ç©Å H»3j×ÆÏ½ÿÿ[Æ\ÎÏ¥<]ñXâ3BÿþKâó^¿Îg©Ï'zDÝ“|8øâøíû5Ú=âýp‰óÁiÃËH}›iÙÄ•ú¦ùµãÀT‰s—°#]%âÿÙ5RɃ ¶/Ï‘v;Ò%표Ç|ê:£ìíég„ߪ'õ=lLµôüÌ®ðt_=Ú‰}Ãda­rbqŸpiG V½û>D{:±’ÍUç%ëºHýû—Þï·½öíU³Â8+e &þ”8kŠ_ðÊíÎÀs‹³ú?Âü¬%În}Ò÷”Sq ]ƒÊ–R)ª3³æJœUMŽÖ«ÎïÕm ¬ª¨”ãy‡7>‘8ÇŽ¯Ó¶¿ÝYñޱJÉV&/¯@œïeÁÛ›ÉöujªMUÚ˸ºµgõå…Þ ÛE¦ÿÛ]QO÷¾:GâlŸY¬D%~ï]1ÝÒP¥Lü‘± ‹ÄYÓȳjU~ÿnþŸØŠ*%¶{¿´l9¯ž;1vuc~_{¹avû½u'ÏEâlR2hCq¶3i$ûŸ8éñQïÂó:ΧKËñ—Ú®¯R2N…=È“8oü6KÒ «=ýO]íéG–M¼c qV±âù«Þ4jãëîJ¤³cT4¿üèZOòM]¶u¢±ötZê7Z`-q6;ô½¼ä¿ýÆmªèø^»Â8«|î†fKœKè8Ñ^ò):d™™J‰_“sÂJâÜ2ªj`~_µ]¿³½TJUƒ¤™瘞¬TŠºÖcß<{¶_réÇ}‰s«eòùý¹kcúU)¥Þå½.-qŽYô Ö~ßôowwíÇev´fZˆÄÙ¡÷]K¶ëy®ä •lóÔzºÄù‰Þ§vüþ¢k“ã}UJÊ­z;ÛJœ?õI6ÂïKªòòyä¦÷>J}.?ºyÔ8öëuýÊ1Ú÷›ñdËAYÈ®˜eøÿR±µÒgnW¾×Ì5í§Hœ[{¶÷±á÷cæ¦ö¤\xý8»‡u!]uHÔ­÷ÎÚÓ7©SiV1‰snûÄ,)çFÃ'4P)y«ß-ùsÖõ÷Ò™ÿfþaÚÓqÐá•Ĺoåp¶ü#Ìo¨öíÕÌ¢vûfÊ„WçÖŸ ?süÊÆ¿»«”¦?sе‘8w ¯¼™¸)•ºVÿE>0i­š q^VyÄâ¥y<ØÅÑï}³Ë¦ËÒ?ç–ÿSÛƒýÛm¯:V¥8u*æõGΓ\xÛ¦¯/Ûwöìtl†öãÊ®Sgë<‰óUõ>ýØnÿëcåÈ߉O|nÊ•¿±Íç¹~Έ¼3N*¥Sû•~5$Ο‡Ä‘8füP·àó›uKK—ë'–eB?ú°ýõ'¥NzkßoFøõ<+‰óƒ°GÊ­Òë¶õ”þ´_»ýÌœ%ÎÇ3¶Kþ¯ÐkbJ>ÔÎ1•FœUíôì$]õ䳯?LÑž¾Ê}üÕo…óª{%M;¨äÍR•Úª”qö÷·¬Œæ÷ÎÔœDþl ®l4^{:y»®ôl#ã°åÆÏ"ŸSËÁÆjßž ãlòtòŸ[ç?Ã[äð½”%·'ûR«Oï<¹ªÄyñ.ëcÄQ=ÐWc@y5ÛcÙ~°ŒÃþ])vu*¿ÎZØx"ÇÛyÓʃRŸÍK9ÉúÊføzñ½[&•iõBæUn_Z &ß-éw`¾öã2*YꉳÄY‹Ï)§!†+ú“ï)=&YJœ‹}ó½N>hötyeË÷뤟_盌·'uY|Œ|RZ´|ÆçW'Ÿó¼"ívóKóÒξm¶ðìíû ;=?·†Ä9ßv^3y@ÚEKÚ«Øb¿c{Iœ“‡þÍÿ¿Þ·UêHÊѽÜÝ'å>Ïo·“n†µÇæ}3µ§¹úÓÝ'gë^Cl©GJÍ+Ý»PŸÞiÇ:´ÑSªÛÑ¢¼Ä¹Z»·(_ª÷§ï6§|uZszí™?÷0™'ßÛ$Ác1Ÿ‡X k´WêóÛ~—.!^Õ†½©Ö¾_³ÅwdÊü9¡Ë¢l‰ç6›77‰güB½ õeþ|ýÄÕ†RÏ‚¢ ÎÑn<ša7z‰ÄùÔøe¶’îØˆCu”_õq‘§$Î1cÅX«äíö#{pœwíÍï/å}[GyÑœ0)RGyÉkËï›äs¨wëä³R¯½Y:ÚGEóº0ÎaŸæÎ[.qÞudúgéWÝËu£Í4P']’8{î GyQm·é’G¼k=s¬¬´Û6׺mM9®½(þ6ñžZ2}‹ û׸-È¿3ãÝû£ûbÚéf,Y-õyþÍ>•VþɺíNi?.'“ Jÿ‚H¿ã¶=O%¿O5›x†üÎöìÒ\ÖªÒœ_dLç÷³o~P¼(7Õý‘û•Ü ;ŒùÞnWžæá󱚫%Î6á›ÌƒilÖ„ оßà3Y=¢$Îõ>fÝ£?Ro>i±‹vl\©®Ósä|˜Ã`¯î“ɇ£;{1á»w©ˆsÆö:“nl…• su”#õþJ=VIœS7æb\¡Œ>hÒ›vámÓ¡Õ¢ù^±·ûQ?†$ˆ˜«=k ¦öëk¯B¹R%Ù6úì«}{EU&¬ðºäÍbËœ%νÓGñýüò‰÷¸Ö%_®”r<`ëaÒÿx_2‘xŸ¶t&×%ß?©ßtãŸC†Gï2wfÐÑŽ(š6…qVevYÛXâÜaé«£Rzto¿‰úckvqâ@¹^¥¿¶j¶´ Z–¿Ê÷üxåöny¾BL©:CÙOFàš±ãÙϳÍO¿Û)çzÍZÁñ*»¶®G¼cGô ¨%㰲ǜZÃö+vŸºJûqÙÏëo53ˆãñ{JêÚ+ÄgþXÆy•¾ ³ëamŸ¢}‹<‘Xõ<íÛÆÄÍdÝJ“¨¶Íé‡5ž/æðù»¼'GŒÈýïí(_šfûª×Ѿßê6zæÊõ¬#y¶S)Ïš¼Ü“zÄÛ[?¶l€ÄùË÷û%¶äK;ÒµVC‡0âì¶n÷É?”[Ã$ËyKµ§v>th=‰³sê…ÄÁl·lˆKò@•â›}¶Ó¹Ž1q|T=úµìä++O-ÖžN@õþvvr]rkóoÉgÍ€7ƒN-Ò¾½¢VgÍüîÈùYË;±ÿd¼ú:`»¹+ÿxÿyÅ%Î\O,¦~Dš¤–å{+[ªŒ—ëUŸg4[Î~L~Ž-V‡ý$l®ï)ן[ =\ãU§5mvãíåöaÒÝ£%ìÉ&WëõÄåÏP×G«µWäW¥GÝ òcÓ§SÅW©¾ôvÉg^Ôµþ”Û6ÊõŠ…óªÐŽ˜œ]üxíHˆã»r_⳺WI»éïÝüånéÞÙCÄr<ÇM,±–òæt²éåë‘ý—•rý9£¼¥©Œƒ–Ö€z{6íW«nr½jjŸ}Û(WnËoÍ@;]å°qC⬨^^!ÝŒgãïì_®=}Õó¼ÇrðK±6-e>ZíL¯ÏŒ/½5Ïw—ë’W4k ('‘ór,èh®š[TBÆ»Ë3ÊÏ ”qÇ¢µ%üµoOÃTgõÇ$#9¿ß¢MÝ:ä§êúœíôKFÖ»Þ/ž>ûF)ê‡É¯ääæ|ÏÜu_ìëH}¶u0‰çxŒÎ&ÄŽ%žÞ­wß²“úºaÙRy úôJÆ?Sƒw«‰sìÍáÙ—6çÇÓ¿®Õ~\n•yž“ûIVkæüqd#Ó‚™Ô+£i‡êL"Ϊ¼°#c¤üV¾Pqý‚²Ó¸ÚÍ­”ïÓzöþ|_U¥Æ*ðùñá'VÄʃßVèAù²/}ùúåKs£l²¬ãÐ3›5Aú=ÓžôëÕ§¹}–û![F¹—¡\©Ô•‡1IñzTa¯sŽm^gâì6PäCùºjapéŽò•rßîwèrö[;ñr{™oïÙ{·6ã’ìûm®¯“û†RÞÞ›BÿÙ­×IÚ·RÕÞί'÷‡5zþ`#éj–goŸ¦=}“ýõ-:Hœo‡wÊy›Óä5Ï©×÷Ǭ=7·<Ø+íêʉæBHê}㈰–·‡í”ëò×/̧\ÄúvÝIG¹P4—‹â¼Óöd1yP긥¨»õ\?”zÿ뀷¬×ÄU›úMÆ[¥VíG½~”`½ø“Œo׿ ùÁñØ{ß‘MþÎù]\î7Uý¾´mý`¬Ë¯gר×e\ §hd;­þ—[Ùnhà ƒÚË,â˜þ3¹Ÿ¯„Ãé2OºW|½ŠyR§ð•§n(\_ežéO½^¨ú@>«úVîÒKG¹PT ãœñ¨Öоr_ò¶÷-dÞðôèò承¶VË}\¡B‡Ÿš¹µ\m©×öëâW´–ùê N_Ž'²u©Ý{Éß®óLë˜Êý¦aUÜ…RÏ\A˜m–(ëÊMê¾›žÑ£½Žy„¢²(Šó™9ÏÉúªãKQo”-†µ§?¼š{Ñs¹Ü?·çÙݯÔõš¼u(ÏÁW÷m•uvWK~¥ÞªB·ç¿nž£†™Ëø,ØÐi-å2»Ç‰~”ƒH¿ÍÝÜbeíÒ£š¤2îÝõmÚ+víÝdÝM·Ó3žKûyyæá%´Ÿ7Îi³E»Ê6Ëk©ôŸ£Žíc\ràQ»…Ì·L–m ßM¹25=§-Ÿ_}Óû|¿CÅ(ÞÎI³]¥LÜ”¸p÷fíûUo3õÉ]*ëR'È<|yÏ“·é‡³/¾™{Vîk›¬J3'ܾzvË ¼?S†ZÏßK9j9?˜t›N|³º—Žq‡f“RKî£TgÝ‘*ç‘·ªÞÓY¥,Ø´¬ÉBY_åÝüE)ÊÉÞËUvßÖÑÏg¬zl*ëMÕßÿ•Ë’ñßÃÊúÕBuÄY3ª0Α%¶mº!ë%õ=2žÊ|"äö†3´ß¥ú?ïR\ ™ù­uiò3²–eËo´ßñ̓·ÉzÉÚ¥&N’ãéYþæròwΊš÷G3>ËÞ2Ô Šr©qlj,íÊ«f¹Çé'J°y°S¥8Ýîi´e»Žü¨œ½7Rî½~Ô³ígä°¡nëh¿Ãroîçl™œÌyøTú£Ñ52ŸÒ>Û÷;öù Üï·²ÊPÇø˜›m –{ü ż'~ï«à‡´#Òl·õÚª}¿niû6›Kœwwé¹VêëÄçSÊù>µo»S¡r_âñ4•?ÿ^]UÙIyÏÝxjä±=|߈‘9¦¤ë]Ê<0BG¿ „¶]Iî#Ìõí™Ì_£Ùÿ¡ýެ_Á>ˆ8Ô?±¡&ãx•uëSh¿í/wJ‹–ç`´^ö©åÝͬ±[sòáø‡.÷åyw¿ûFŸ ]ïCwß|ÖÑ/¨ }«/ëQ}Oû?Ëv3Î>ÚEûŸ3f¾ô·Gœ•iáòâ£þßï®Ñç;Ë88v¬~leê¿jýùtÔE]g³õ2æ°ŸK!«÷Ñ>ªöÞ®¶›y£Y¯ò ÏÈó ~-œPKÎ{ôßTp‚v+lææ»h'íoºE´“zû¼Qµåä¯÷´»«Mè·cÿ4ž{ý‡å¬zïD9ØkÐýÅ4ÚO·¦º½ÜMùyç\2h‡öã2iQ¹r¹O°Å´n+å:Pb¿¯›h¿ƒ'þj ×Ý,þú„Ñ~Äþ4«XŸöy¢ûƒeïè·Ã®]¬Ò“ïkT¼å-[>ß­V¥ Ý//ö˜[öÐNêɳó+†ëhG²£ž¶[(ë—î_K}U7­Öv/íwì– JPå7ÿöòn”÷ÈA÷ÿçg'æW/&qvØpÅ‚t#[xVh«£_P~±ø(ëK|Ú¦&ËùóŸ¾DÐ~»Ý¼Ô_Ö¹zü|¶šö@=¦Iñ+:ÆsÛ˜—Ù;¶Ñ«NýOyÒÌô˜Žú¯¨nÅyá¯gå>ÃíC·Î“ózz¿Ó£ý¶ŸúiØ"y®çÀƪªÔU`ή¥þÔ ëï¯×ÈóŒn†•áx2ôG§Ýˆ÷+¡’õÞšfÝwHûsM5°1å`wRÉi?m'¥D<ŽR)¯îm¶SûqiyOX+ëpÿä¾:L>«ç\¿·’~R3îÄç­²~þê÷»©!r?Ç•OíhŸ½·'Ùþ”ulµkmÔãûÆöõÎX!ã’‡ó¼ºÊ8égïþËiGv š:^G;¢©õÇ'ZÆùáYòÉøò’þ#æ&«k7¾‚ýžílÕŠòî]~EÎ%)ï"ÖU“ç ¥&Ù9nÓiÜ'è_ª,{”(÷ë].=딜ê¾3Ù}ŒÌ‹T_žÒßyTjµöÀÉ¬ËØ#:Æsa†mÖ-a|”rË» "õßûÝøwÚëÿ–ÿ^9(ï-”÷›Ê»PåWúJÑ{IK¢J£ Ê¢ÊÃ`„Ѝ„*¨ cTSŠÞÉZ5•¢wœÊ{Yë .ê)Eïf­hˆF0Ec4A34G ¥è½¬­Ðm”¢÷¥¶E;´WŠÞÇ*ïaµ@GtBgt%ºÂ Ý!"oùé‰^ŠÜ5RôîUô­RôŽÖ~èJÑ;<í0öŒ¡pPŠÞ÷èˆápÂ¥è=®£à ¸b4Ü0cáŽqLÄ$LÆL…'¼”¢wÂÊ %§c|0³à‹Ù˜ƒ¹JÑ»eçc!ü°j,†?–(Eï— D–"˰+‚•Å*„a ÖbÖ#°Q)zWíflÁVlC¶#;°»°Q؃½Ø‡ý8€ƒ8¤½÷ö0bqGq Çqq8‰xœÂiœÅ9$à<4¸€D¥èº—pI¸‚«¸†ë¸›¸…dÜÆÜÅ=ÜWŠÞû+ïq}¨½÷1žà)ž! éxŽ ¼ÀK¼Âkdâ-Þá=>à#>)EïöÍÂdã+¾á;~à'rð ¹ø¿ÈC> ðR™‹+zOpq”€> P¥PePåP†0BETBeTAU+zçpuÔ@MÔBmÔA]Ôƒ ê£ÂÑMÑ ÍÑ¢XÑû‹[¡5ÚÀ mÑíaް@GtBX¢+ºÁ Ý¡*Vô.äžèkô† úÀ}‹Ér5ê3` Áƒ1Cá€aÅŠÞ«<N‘g¸À£á†1‹qLÀDLÂäbEïhž Oxa¼13àƒ™˜_ÌÆ\ÌÃ|,ÀBøaä}Ï‹á%@ ‚°ÁX†åX„b°k°ëн;:°›°[°ÛíˆÄìÂnD!{°ûн‡úâP1¹Ó…úŒXÁQÃqœ@âq §qgq ÅŠÞi­Á$â".á2’pWq ×q·ŒÛHÁÜŽbEïÇ~€T<Ä#<Æ<Å3¤!Ï‘—x…×xƒL¼Å»bEïÚþ€ø„ÏÈ*&W¸¨ÏøŠoøŽø‰_ÈÅoüÁ_ä!¿XÑ{»ÿA:æbÐCq”€> P¥PePåaˆ 0BETÒ+zxT…1ª¡:j &j¡6ê .ê¡> !ÁÑD¯è}âÍÐ-ЭÐm`†¶h‡ö0‡:¢:£ ,ÑU¯èÝäVèz 'zÁ½a£'+F©Ïè‹þ€°Ã Øc°^Ñ{·ÂÃàˆápÂŒÄ(8ÃEOžÐ@]ÆŒ…;Æa<<ôŠÞ™>“0S0žðÂ4xc:fÀ³à‹Ù˜ƒ¹˜‡ùzEï__?,‚‹á%@ ‚°ÁX®'w8R—±¡X…0½¢w¹¯ÁZ¬Ãz„c6b6c ¶êÉp©ËˆÄìÄ.ìF”^Ñ{á÷`/öa?à !‡‹#8Šã88œD`!ü°jøc ˆ ,E0–a9V +ŠUÃj¬ÁZ¬ÃzlÀFlÂæEçc¶b"°‘ØØ…݈B4ö`/öa?âbp±8‚£8†ã88œD[úV饿’~EÚÿ…ÿµ§‡þk£Òþ«ÓR÷Z•(*Ï.ÿ••ÿå·äË|†¾>i¢b<b=á2Þ U H½á‚YX‰(œÃ|†~IÒD Äx,ÄzÂ-¼A>ª–"Mô† fae)Y•Mšx€ÏÐ/Mšè€…X¸…7ÈGÕ2¤‰ÞpÁ,¬DÎá>C¿,i¢b<bNàÞ UË‘&zó°Q8‡ø ýò¤‰ˆñÄ6œÀ-¼A>ª’&zó°Q8‡ø ý ¤‰ObNàÞ UH½á‚YX‰(œÃ|†~EÒD ƒ'± 'p oª•H½á‚YX‰(œÃ|†~eÒD7 ƒ'± 'p oªUH½á‚YX‰(œÃ|†~UútÃ0x"Ûp·ðù¨jLšè ÌÂJDáà3 «Ñ·À ŽðB"‡dd¢ÆÕék`Wø"ÑH@*r`Xƒ4aGx!ˆC22Q㚤 ¸Â¡ˆFÒ‘ÃZ¤ +8 Aˆ@’‘‰×&MØÀ¾E4’ŽÖ!MXÁ^BâŒLÀ¸.i®ðE(b„täÀ°i ŽðB"‡dd¢Æ&¤ ¸ÂáˆAÒ‘Ãú¤ +8 Aˆ@’‘‰7 MØÀ~G ’Ž6$MXÁ^BâŒLÀ¸iÂðC8b„täÀД4aGx!ˆC22QãÆ¤ ;xÀáˆAÒ‘Ã&¤ +8 Aˆ@’‘‰7e¬;xÀáˆAÒ‘Ãf¤ +8 Aˆ@’‘‰ÔmNš°ƒüŽ$!90lAš°‚#¼„Ä!™0hIš°€<à‡pÄ éÈa+Ò„á… D ÉÈ‚AkÒ„ìà?„#IHG Û&¬à/!qHE ÌH°ƒüŽ$!90lKš°‚#¼„$ Y0hGš°€<à‡pÄ éÈa{Ò„á… D#©È‚9iÂvð€Âƒ$¤#†HVp„B¤" ¤ ØÁ~G ’Žv$MXÁ¾E4Š,t"MXÀðC8b„täÀ°3i ®ðE(¢‘€TdÁ  iÂvð€Âƒ$¤#†–¤ ¸Â¡ˆFR‘ƒ®¤ ØÁ~G ’Žv£Ã®ðE(¢‘€TdÁÀŠ4a;xÀáˆAÒ‘ãî¤ ¸Â¡ˆFR‘i²rȲBHî–ŠAÒQ㤠¸Â¡ˆFR‘ƒž¤ ØÁ~ï)+ìI™(€q/Ò„ \á‹PD#©È‚5iÂvð€Âƒdd¢Æ½I6p…/B¤" 6¤ ØÁ~G’‘‰÷!MØÀ¾E4Š,Ø’&,`ø!q¶ò${ÒDŒû’&là _„" HE ú‘&,`!qHF& `ÜŸ4aWø"ÑH@*²`0€4a;x!ˆC22Q㤠¸Â¡ˆFR‘;Ò„á… D ÉÈDŒ‘&là _„" HE ìIVp„‚8$#0Lš°+|Šh$ Y0B†á… D ÉÈDŒ‡’&là _„" HE HVp„‚8$#0Fš°+|Šh$ 90t$MXÁ^BâŒLÀx8i®ðE(¢‘€täÀЉ4aGx!ˆC22Q㤠¸Â¡ˆFÒ‘Ѥ +8 Aˆ@’‘‰"MØÀ¾E ’Ž:“&¬à/!qHF& `̤¹5là _„#IHG ]IVp„‚8$#0Mš°+üŽ$!90t#MXÁ^BâŒLÀx iÂfŒFVa“&Ò‘ñ¤ +8 Aˆ@’‘‰»“&ìà?„#IHG Ç‘&¬à/!qHF& `<ž: ;xÀáˆAÒ‘CÒ„á… D ÉÈDêN MØÁ~G ’ŽN$MXÁ^š(wº“&’‘ ƒI¤ ØÁ~G ’ŽN&MXÁ^BâŒ,L!MXÀðC8b„täÀp*i ŽðB"‡TdÁÀ“4a;xÀáˆAÒ‘C/Ò„á… D ©È‚Á4Ò„ìà?„#IHG ½IVp„‚¼‹îi’?Õ•ÿÿ{íÿù»Éÿü½éÿü½åÿüݬèòkáŸ7JÑ=FúÕþÿóÿ³m·ÿù{ÿù{ïÿù»ÜSdùßß+ÿ·_ù4à ECY€ÕˆÆ¤ü·ß?¨@"ÐvpǬÄNœÄM¼@Êrà&è€~™X†ÃU¤áJ'Ð6ob3ã# ÅK·ÿ/ë‰á˜ŠÅÇhJÝ7%פåœzšœ“óYrJÎɹ9?#çTäHÆ3—*¹’ÌodN"óûËx]ÆØ2.–±,lÈ80GÆy26“ñ”ŒdÜ"c HŸ.ý00 ÿÊ’>Júé ¤ý–6WÚIiÛ¤=¢m0&`RW¥~Iðúÿ8S´ü‰´Mîœä}Šz|pÇ‘âœÖy ZžÓ"sÂWõÐäS‹¦’g›Üö–ç0¬y´¶Æ@âôbõå­ÄÙìì™jIR/”ŽrÐ7ç#÷ 'α—Ró7ÏšÀ Õ t>‡álaœ›vœàî&ñzsíC]Ygzø‰?íŠó§×g{ÿ[O:Èú’‚´vòýãÃ÷ü{NœÃ´Ú™#Ïo™•Ôºùz}Í«W-Ø¿º]ÎÉ˲߃zޮĿß`«£®ÄùfÊÛÛF².pç²8]Ï+Û1 ã%òˤ’…êÙX¶keeGù{¶zË­UÄÙ¾øü·­¥}IT¢ÖÉ:úå•ÛDp§zåçÕï®™çˆÿøÉw¢†çÜq¾‰çˆŸúZôô':ž‡ÑÔÿƒû(ÙîÇùÐÅôʧ˜æþî*¥SgÏy—‰³›ÛàÓõ¦IûÝäÐ}ê¹ÇÁÛÝ~gG d}iûû²uÔðŒçõGgõáÞ1säìHÜ‹Òýúò½Ê>—Fœí_ÅÛ5&nê”V&:ž«r½Â&õjòÙ)øþE+i¿6åçÌÓ¾½¢yQ猎Õ]–±½zX#¥ùp¶¯[û~[Ú•¼Gû^$Αfµ:"ýä÷Z®ß(ß1®t/³ƒò2óåKâ«>*yƒþ*éÁø7£Ø¿ª¬aVCiWßè]HÖtßÒsõgmWùPîU/²íÎë(÷Õç ï/ív¸{«ÚnlwµÖ»£#UÊÔÇÅïÜ–8Ïž2¶ºÔ‹ò;Mó©W[Wu¯íCœ3~–ð!ÏÕI/H‹%þ-w¾®ìGœ'n5(TžWàâYe„Ž~LÓ)vêÎyΈφ*ôJ³uGä9Iaw×.þ!σqˆŽ —zþèKŸRäÃÝñ>Oº"¦Üïµ”tÕ¯'ÔÓQ5²Z®”8ÿ¸á\JžÛwÖkÄšÞ*eçð>Ý«DK{ðŲ„ôŸÇK‡Óñœ¤å ömJ$Ÿ³3»(ÏÍI{Ú¯ØlíÛ+ª’gûSoÕ¥>ÿ¸¹½‚ôA=†G ¢?my¿ÿgy¾Àxóç×äÌ\›Z‘™cUÊ“éG´&Î&W:í•õ¶nSóRßö¬Èþ#W¿èí(ÏgÚ7ü|ßëYËÁ™ñÇä9:oo’ø¥¬ì˜à¯ý¸m×·–~êá–ºÄWÙ²¾JÁ0•R®ä×xƒÍì¯J`×k2>]Êý8ý÷ÖV_‹(ì§] Ç9ÙéïLùühsÇ]ÄÙìÌçv%ñ5†\Rk߯ʫþ(yž’ºiÇVK¥|5œd2í[lZì£Úäƒú\èÁ—Ò4\—Þ˜|ðêßx>qV©jÎ$]UÃz¥èH³úàq)Ÿ®ùžÄW±Y-©‡JIûeP/â¬ú´ñÊ2Y7Óãð i¯´ý ¿?úô'òYSÿsÉg2.̽xø§Îçžµ,Œs®ßò­×äû¯k×ý…¶Óh­J)aW§†‘¬/‹6/XÆgáÝFȺò£ýgÍÉSó—ÈxeÅ™”‘´c55GÚì‘ýo6u”öôÇÙM·§ª”±Ñç<%ÎFgüή ¼«oÜÜÛt¡öã2ª»ÞQ⬗òc­ÛÙ}²¯A2²dGs⬩ôyb]iWß®¹Ùv5^Uwõ{⬳îÔGÚûuë÷S.—8&uL$Α 7|'õdn|ccí[©®d£Ò°LÚ^Æ'Šº³íÊ—Y;+â¬Zõ¬[¾ÄH×'–Î*%1ÊäzÌ!ÙÞý[wI×cáÝÛ:â»qÔÄ_.Ýv©øi[Ö6Ú’ïó¯”Ãd‰óf“3¨7̤Uóu ÜoöÀöqVþì°)ãäo¶³­iÿ&ÙÖ{ø“8›Ì ÏÞ'Ï‹·åNŽçôÙ¶r_5KúÑ ¦fàç‚Nîå)wÕ[\)1\âÜùiÉn’ß;¿y•¤¾Ù7ë¼³Òv޳TÏ͈»’~­Äúó{ù=Ÿ>&Înó3W”ç*ŒmöÐEG¹״żNgÿcyžãÕƒóŠÑ\³ôÉp©Ï.ö žK¿Ùjh:힃١!©ÄY:âøLI7:`X¼—öô3r¯ÌÊ’í|-VŒíÈÏlçá¾í© ̃‰³rîëß7RNg>Îé8A{:“Æ7ß-ãÑåûK>§~nqmœöíÍìÂ8«ë7_–%q®}ùÖbÙ¿íÐ̵ì¿éõ´]eÚ‡+GM$¿?­èÙ‡xú{FÌ ÎJÄö:ý$þ/üOw!þg}ºûÝ•8›wLÙ"ã9¥ÍøÆs¯§]ëSò8ÿVÕ*NýVçMqÐñ=Rf¬þ#ÏYRœßÜ-å>lFWÊý»½s=eåðÖû'Iê>[|€8ħ¾?ÙJÖo~¾ì|FžÛzó”|~¼Ånß÷ÄY½uø‹J2?z×öË ]ù1ûÏÏÁç!³ƒ}¤¼§ît/Ey·¯³ãôD™wøE6–ç¶Vù¿Ê†ôÿìòÊ–8÷vÍú#é†ïj2wŒöôUKWHœcÏ®[ÙœŸ¹nÃê4V)?œM¿n‘8¿TW?+ó ³êxÔžN¹©C+v%ÎÊ€÷ã'Ësw^›±¸Žï¥ÚVç°>·kþ”8§,û'ÏŸv[qÕª"ãȪ‘õ¬%ÎÕH¥ì¾TqÊ\‰³Ý\ó­ò¼\½ýùmUÊ¥1ΟHœS–|ØÜ‰ŸLÍ7W)§7×1­ qžÝ{êPyN£Iƒ5U{k?.ûWÞÖJœ¯Ú·¬TGö»ê|ûê2®;c;GâüÄq®—?þŒjAý®7øMg‰³ó˜6žòœm>¥Kž´Ûš÷­ÙKާ›rwùýÞa†´ËF±?KJúu&}+.ëá›=ß-qެ¸»L7ÉŸžóZuÒqœ÷Ò¬%ÎuKf•íÂωu[î c¿šÄ¢8û¼jñ]⬾Úýws õÙWïµµW?íÛ+ªw…qÎØß®ý;‰óÖ[ úK<†™•µÔ(ÖD1—þù¹ƒá,9þèEãLí5ÊÔ¬˜ÖSes¶]¢¼·a]ªyÊ(21®jû2¶~ïTJÞKa[Ú5œ¸fO»TI!Α?¦ôl)ÏGï¢äÞÑñ~”yÕ­Kœ>ÛaÃv‘÷Í.÷$ý¥—z#ÎêU•ö¸Ê{¬.L,?L£¸U[3 ÄùÌ”ï…ïsøö}õ='²®¦k¹WÄÙ¤ÇÝ» äyc«~®öžª#?Öy÷ê#qγ©;€ížŠÖG£ä>Ü?Fúç} wm‘÷0XWþ.ï“™iwå´Ûsåo“t'hzŸŸ =ýŒî s¥ÝŽ×¶­”ßÈõ}Û¶&¯sÖʼªK­!áãù}öº´÷:ÞÓ1§™Ï7óÝò|Ô¬ö’Ïá¯èx†¢)Xø|ø”Ÿ{çëÃ\ªI¹h>¶ûöÞ%£ê÷ﵤ>ßþܧ¹ÔÃPÍ{äïîš>‰³:àÅÊ'²Þ_/19WÞ?ÖVݸ´Ž÷ô¨Š¹}Ë(|þAñ¼ÖòÞó*½Ût¢œºTóœ#qž_áÄ0)eLœÑQ;¹Ü°«½[žg±\_žÇ³daç:Þ£¨ÚÆY³êˆãç‹k—|ÍŒtz1PÞßt3GæÏ[N? ¤œ©;¶3}L9›ø%;ÅBæÏ}†Ìç(¥‡_$®å:ú¬•yU«¾+Ê{4ß·TærÜÍãÎf'ʼª…þåž”Ÿ-—ýu½w+ãÁöîe^µþûæ’Roó*G\pÐ(ÞËZ̪,óç žùÆ’^qªþÄ5¾ºYA¢œ»Üôu˜¡3Ò^lZ~Å/9ïÙ¸Ì Ú/u«;—îQîƒƒß ëIœ3öÎßNÒ-÷ÀüŽZ{úöž×Âä|˜éÁéQ}Ùîj‡”‘´›¯ëýÝ\]æÏ·Ôõó“çnÚ¾p×ÑÞu¹;&,‰|³òðî+ï«\Üci¼Î÷èùÆ9~ZÔ¸QçA“²½å}V»íŽê3®p»w" Dú¥¾¦ÇyK»ÔåÍbâ=ur}‹§‘òü¢CJ/y.ÞÕ'Y'ˆ·ÓK Æì?£ôʉåå¹(gº ùB¼÷.{7‚8?j»z—y㦘˦mu¼‡´TǤÝçä:ÆÇª†¥ÿ9oZ½€xçÕÎþ?öca÷¾h(ˆÓBcƒ0M1M”€0%¢ÓDŒèŠi¢Ft„! º0MtcH¢PѪ0 ˜’ 8@DÜÿ`à=ß¹ åú$+wÝï̳ÖïÝçüŽä={fÏ.žÙ;zNìµàKõ©x¿Ù¤‡Ôç¹ü㺼/ê9åÙB û'á­7«òï;bŽÓ‰ö\çíŠOÄü0—Æ ²tFöwN¶÷=/w€~"!fÜ£;|~ô/E§§ˆç ï”,úɦ u£¾×Dì¶ÞÏ1’çÜT‹yñJÚä8Š=TyRÑžì59Y¬µ¼h‰Ô÷ííŸ]XKùÓO¸›M¢œEIÝ6›__ÎÓº›Z~Ê~>Õy‘·+û9eUëÇÃǽþç)Ÿ‹zòòüÏ^ú[¾:YCÔ÷ý‹k/ˆõâ>ËUø{ú­ÁOOŸï"ê»\ü¦!bÞ럗…Ý£ŸntËÚ+IÔ÷¹•í¾úḐ”wÓz구òÞµ_Ws|î¿#ß;!­å×ÍùÞÛ'=^úN¬­i9ŸŸï§[² Èz·qý—'‹ûàížä*Æ'¥‹—ÚS¬GZïj3ê;­cÅŠoŽf¿ÌkþAX<çË¿}wFÌwµ±sƒžâø;£Ñ)Ž¿›g&Î--ÎÓKÒ¶óõí¼÷qÇ ÇYØšÅÞ|¢¾»wÔ‰õ^£Ëõ1¤(®¼™çî\1OM¶nû7ˆþäÌãîkèOªåÍ_i õÐíȾ:¢ÜÁå[]°¼¾ü‚¿eÜ­,ÚuóÃÑÍÅøZ›û{çÉÒ¡†ðUÔwÁ©Un}Êñ’6èX£ƒœß¢²åÙ%ž'(º¹Úg’¨—¶¼AŽEÔ±çíZQGó讘ÈP¿ÚB1ÞÛ^6"qÇÂÕ+SÞó!­¹P;›èãççJä÷Œë>úÃáÔsÜæ®1%Äçü8ê­Þ|N\5S‹­«Ås¬‹'çñ}C߈ŸÙˆï»eæª)×·ó½&l¯òÕBêkÚÚχÌ{ý÷Z_!4W˜h×?Ä74öççæÎÿåΉU®§ÜYB=”[ºv°˜1Ù™K¬³›è¹r®7õœÔµå—UÄq~«džrüû{ëÞ;ÿ}R”"2ã®c¾˜gitzTå ëW/ [=þsñü²ÿÁ¼ƒ8ž÷Ž·0Îiµ¯Õ™¨çĦ—ú•ë®üøÍ›ôs›ÊÍçx–7Õc5ûÃXæB¾olþ†õ;ñ}‹˜K=7wœû¡™˜®FÎ_¦Y~Om›Óâ½’ª#-=Å|a7×&=6pòÛÌ¡ŸˆzVåÛ~@Ìüt‘ý8ýuýjµ¥/g|˜-Û¡w)“¿M,Îw…VœýŒzž>´±é4ÇWBþ¢F9¾ÂÒOΩ*ž«>åË'ÚmËìeŒûÒ*.üây|î ¯ÕóL¢»þgú·v‰ýÛÞ¥¸},¾i_1ñÊœckY>ôr’o™¸/í¹üþÛb<ôžwú4Æ¿nGüÏ¿Šyá:N\Єã$åloX^_N¯YöaGØÏW§þîöNÏvÂ,ž×;rãkí–MÍz¥(š%ï4PÏ¡[›‡çã«<úNœ7n¿ºŸëÖ¸)}‹ ãû¤¬/²ëû7çC¾QòùÑ·šÅüÌÕfèÃ8»ùžØµ©ç¤ö{ÉyD'çØüÙ¢ ßëý=·û‰zžx7¶™¸N;þMÕþ´«èáÑï奞C¿÷]€vål·p*ýt'û˜ ]ÄûC5v·ÏÏq—x%qºX×µ[¥æ[[RÏyF7yKÉñ}éSWÛ ÇWâW·ÛŠçã)'.ŠóÔü ¿¯æ¼|ÁúÞ ‡x¨ÔcÿC±Þp‡®¿gЮµêÕ§ž¨çòå?h Êmx.©ìÜ×—š´æ'­¨ç÷/_Äõ™¢ÊÄ:¾%nŸœK=g”.кÇIB÷û«cƒŒ'fOm¿û¹à¡Ó§ž±ŸÆÞ855ÈþL©“YÏáu>,+æï;ê;+Æ!WžЮ þÑõ­âùëôýLjó^މßãÿù ¤žÓf¬ùcß'!áá%brw`äZÆãa7ký^¬Ÿ [ÝÀñö¨\dzç8OvªžÿFÅåb=Œ"§W-~ý÷J‰-‘ëáê³Ì¦ð9bX1WäqÚutÓÊ9§‰ù?;¾›}±h·óÎ ÿDœr÷<¼Œãkû–ïîñûÆæÞ?ßÀ¿/)÷uÉ›[©‡·›ªs|z´3{é ÇWZÅz9Þ¡žÓf~zÚ.Öeô‹oM»ÎHÓLÔsäñ­Ä¸g숼·i×ý['¼·~#ß³õŠ¥ù(7±ýÎýëƒôŠ<ÃFÜïm\š¹Ù%î fTí0P/®¶-/E=Ÿ:7âø×b¾Ûõ5n ¶Ž{ÏÆÆœ«Å}šïÆ1ÎŒºë~uZÏêñ¼žc·„ì1Šç…šC•ÏŠëî±›h×®¯Znã÷‹½o¯zEœçè‘÷&ýdUZ¡çft¾pù3ÑÏŒ®ùG; í96ÿ³ºâúºÂÍ~uÄ8kg»x1·s£}æQϬ;jóŽW¾ÐsÅÒ ßkpäÕ‘â=³ö‡ßýg+Ú’F1ç¥ÞÅü}3oü€ã*ê{©Û´Û¤"…£ßóƒwúuù—ü¾ Xò ýXÒ'u5å¨ç%šê½[-£œ§ùÞ²½þsc;¿;~ûïp¡™ÅýUÞ_Rh×I^SÏkâ}˜î•WÛÄ:ñS›^;K»Nêó“;÷Fñ^Ú‡ORnBxÂûeƒôŠ£ß'Mõ<ÌûPܽôËžh×t­g¯×Å÷u©#úÿ«ãÜ·f¿¾œN¿†…,a?Ï-; â91?îêîm¼3‚|nÊ”ÌöܾðWÇ‹÷²²ùJ\/Ûçh-îC¬ß9½”¨gEó¼KÄõÒŠÒ.rÝGêægÄz7?ž¹ƒóØ£uê­]6P¾fòŽéKÄ|ÁíJÛ¾¾üPMjêÛ¢ž[¶¿QFÜ(YA]ŠvU³À¥râziç¾q=òXá 2^?8üý2Ôs…„‘¥Šó„¿Þ'g‚\G(¢6>¯gE©p÷ñ¾WMûb}ì%ëÞ`œ›'ïý7×ðûeÔnyi/㬨UÇâxŽ>ÙùÒv·ѿ^O˜Ëñ”8w÷Œµìß%ÉsãZSÏa‰g'uëaèãÜ¥Å<½¹.C=ß8Ü|Æ*úOË™;CV©çl't¥žS>nªýgÔù_ó‹þ³`³o×XÄ÷ì¿ït9ÚmâÆÖ]Ón3.ܺFÜGÙñ‰^Çï§\|Ç¿ïùÎhqßêê:ÝWò¹ Þk×ö³ Ÿ;ûäÂ÷¨ç”±Õ[o÷+Ö®›Åx$úvÌê{sļÑÝ+?ã‚|EKY8Õ£zåU멟iÖÔë”LS‚;†6PG åûTy»ðjq_ºªû殌SË?ùjéjŽ£ ùÛë%ûË. rž¯½øÈZöó’U.ösìîšöÿ)RNfÖó“Ž¥‹÷"¶gT(î<«5a(×aËÏ -)êÙ{îÉqýäËY{1Çsdë þ¡ŸŒêcÙTW´[WîõÅùøãï/â¼½þ£º÷†Šë˜ÏëþžÁqWÜñí]úÏÈMÉ+V®æ¸ßüiö†+_ÿ½ÒêNÐ¥L¢Ÿì³-ö¦¸ê0ºýçh]£Š*±>Çò¹ùMë›ý4!QŒ÷õï}—*ÞûøÈúL¬“VæÞ;óïßéºa×=ůÍ\G?’ñSú[‚ô#{åÎÙmûûìPWqþÄ8ù:ýwâðƒ=îYé×ö¬ôæýG§¸ƒ-9u:±ÑåuQŠGéóž§ÜØaßÔìdÜÕ肱©X—¦ÞÊÅõk¿Ü·×pýzîþÞ…â|vß?'§8ÏŸÎ8»:Èy>ÉøMƒPêùèu'Å~v¥½1/HûWDýú¼ž>,Þ,¿XçðÞþ¢kÄ}bãéæ÷è¿ ŽÛt¶ÇqRµ 7¾óó=÷ÅVŽç‚=û¼@?¹þ—¥Ë‰u!Z.|Åþ­PhçOÄs…îëÆŠu»ÎÆeë%Ü8ºøË3ôŸõÇo?#ÖIMh3ªï¤ÄׯèË%“JPωC&•×s¢~èOÿVhu§Ò®~ú±Î#úÄŒ–ãb<øÞ™§ŒÏBOåž¿ZŒ >Ú²}'ÿþ0qg½yŒ‡§öµY"Ö—v®Ô´ ýHÁfå²Íáº=j@ã/<´×”=e*V÷cÖøõê§ôKî-$öƒº;ßûîæ6oPϡף#úSnh¿œw69/¤$t?SoûÙ:4v×3г—z·¥ÿ÷ýð)e¤šœç3Fï÷®šÿúrÆ­)G{zTd¸þû9åÄ„£›ƒ´.Ô¦=>YaÊú·Ä{†i¡Ã>¡L¨Ò¯owúïØgŸY~ã÷›«Í«º#ÆÓÞ<4‹þûöoËê}G?¹>rcDn±nÓѸº Ù¿ý£~ßÝ•óvÊúQåÆqžš[gËÔêóÄs•ˆU›è?}°½wÒŽï‰ðn«^ÿ½2îv 7Ndÿmùec;ösÊþA'úÓ'Ý;ïùûÆÙ¾tŠþ9ôë¢S_,Pt±8oWoQBü¾ëÛsóï}¥[Í`œôè ëÈú‘¹©w÷äÒÄe,Ïp‰yÒ?¶mí5AsÄ?þ;nbç;‡fÓ¯½‘^¿€¸ÿs¼e«³Ôóú/fªê`?lì%æmO¼õ¦å~°ñe“ûß—Ì÷Ld])î£or4Eÿ={Zß)´Ïæoï[ÐYôz]d<·Ä=xæösØÚŸVÝûyÒÝkӃ󣢞×s¢çVJ~1{-µÛ‰ëÆÜKKb|›µjf2¿_ÜÂoÐnRv,Õƒ~+ñš©÷1úɹ‰…†¦‰ãǶÐoL¿ã¬P‘óömUÆýa‹Ä}ƒ:+|þÓ7BŽ÷ƒÓ¬§7¬åø‰›m®ºúõß+ºsÕJ{Ä}Ø]öçÉôé;bè¿ýX?B¬s¶¯Õ‘Åâzâ†'[`šÿÄ?*'Æá'rö½*ÖÕùã›á­Ÿ÷/»Æ3NªùáÒ™=èG2®­žÝ0H?’hnUÿœçwþðøn1Þú¾Hô Æ[q󸈿˙qå§€8OÌÌSÜÎñ~nß¶ÚµQŠsOZ/£ÜXKïÁ•ƒœ¢Ú¤T ëÀ<(9[<ÿ2<ú¢xþµ¬K£BÔst©ü5Gˆõ[Ç›d<We¡#ìçGÛ«&]ã†×zt Òþ)}3Ûs·%Ÿˆ÷åvÏùî1˜ùCúïÐåÅÇŒë>®ZW1ÕÂþÝnrE±ŽN•†­´Ÿ=Ó7Lýs¯¦Šu@fçþö6ýù’†FM ýObÛ?|)Úûñ'Âé?oô(´z‰ƒñݵÊßÿ¤žc/výXjv­6sÙÏQï”iÉy2ÏÛùVgˆyàÕÍÏžaÜÕ·@O%ýs…‘×óTâ¼y®_ƒÅâ¸n2òã©üûšäÛ÷Û1NªYÑZªýHä´}_= Rϱš·¿5Y¬'¶e“Xÿ7j±båÎÓ‰ã íÂuuÁïk¶O×ç•n7Žåxì½ËÔëÞå£Öýr':³w\dgÛ¡>!ÞWûú=»QÜ··òôê9ª¿»ñ.Îwk¾˜ ®ó»úº‚ŒçBód\Çø(̼3×YösÜà}i»ž‹šõ¼žS $½çë_VµŒ¸"®c†šºmõœM{¿´X¯ÍùuÅâ>¦jÅÄ»ÔsX ÿ·3h?­³SL¢ß(xä|eqÝ×ýiÃeôç·§UZø!ýOèªm¿Mäó ¶5õºN»zè­;~õÒr쓟¿þ{%<¼ìî'þ^®zÞ´eâ>ö–/•ÄzMgjߢž£sé›=×wšOÈÎõ{t—¹ûʈ÷_ž6Õœã*ñ ‡ü{§6)örœ?£ŠuZA?5þÇ­+ƒœ/¢æ96dL¢_Ò¶c ÚkB9OûKœ§c÷mŸWX¬ÒóÓÀ Ç{„{HqöÃÑS7Övâz¨ùƒ/>þ”rC |àŠrþOX–8«?¿—ççåZåOÿæ6ÎÓüþHúá Õ¯=›ø™¸o^Jì:¿IƤòâ½ uÃ*{ÙÏa=»¬žS’ž×³bÙ·#\b}ÏúëË/÷jý²±1çéÄ%Š‡Åº æmÓmâ¾WÝ}WÑo% YU©³h?Ýg¼ÁqW°N ÇÄxlÖ“J éÏóx÷O}[¬ŸÓçôøZOÚO¦]åißwy‡õŒ›ŠŸõŒzÎP:?­,®cu+›ªÄsÎï_ú…zŽ-›ÇZX¬[ã9½?C¬K[­RÙcœG’J”{ðýêøâj¡ž×ß7ýÖ0H?’Ҩў ñwõ{ÚNŠëÈ¡q7à:2q‚OŠXŸ´{ÒBqÆz»Þ1êùöú˜.íWG)?ˆü½åF¿ýíï“íAêùÇSÙ£úñ{)n+,î;v=\¶;çé¸°åœ´Ï ÍV4Ñï'<ŽË–/ȸ]Q¸Q'·ØÏGU;°Ÿ z.t[¤ŸWD¥fÖóö“ŸOÁqÖów÷‡â:æÙ•Éç¨ç”K>èÀï—²¶ïå>b¹+ׯêè·Âžš •¤ý\èßíÉ qp¥W™ìß‚%5.ÛÄûÖº×øüSÕ%®ñùy”_½÷íjIÍÍ?…nàúèa‡«o¯ ²?räÜ þ~'åΩŸg°ŸSö¦×>Æx,ö}åé1\ßô?µ"ï"QÏu¾ËyŽñÖÜBkóü.Ö‹™?gÏq\G¼ùûþýè›ÍØ Þk¿ý̓Ñ~®ÃÅV—ƒÔsâ{SžÞb\Q¹}‰¾â¹êÞ¯O‹ç}±¥ŽÖïÅ8<.I=¦ Ç{hÞʆr¼wšS©HSq¿âÄ™]_¯ë×ìùhJqžâÍ‚Ùîˆ÷Þß²Ž:/êyÛ»ŸQÏio®Bûì¤xzµý~tþf‹¿ VϪ_òvæ¼YLóFõ^âyéó•­‚]·§©â°|ì‡ ã\«1~Z³¿AGÊ=¥Û°¨nó‚öY£i}ø½nþ8gVq¿á‡éK¥ é1Œö9}UÎ-µÄ:<Í.Mžäú,cÊ“–ibž²¹ë•¥Ÿ½rpøÏÁê9ªÔôçë”ûçÜË9Œþð_ÇŒï¡Ltð²ww¨´®£¦Þøö2õœvì½ñ3,üþsÿ4“ö3½WÁv8îNÝX6ÂH;š›×ÝÅ'žWÿ¤Q¶÷¾mÔµ0ÇÁž^±‹ËÑ®:ä-öáÂôwOǵvÙßÛ¾ŒÝ(þ¾yþ¹RñC¾Suk3‹ý¤YÏâï-ß]ð`õœ˜kRž¡â>P±/5]¸Îïä8¼.Iô_?Þ$îÇeôv>÷ .¾ùõ|ûq™Ç¿©gEëã=Ç‹õs}õRãÅuò³âƒÚ‹ëäC=USŸÅæXª9B=‡žêWwõ·¬rWñ>˳ö®RnÜ£c…6«ç^÷G–èM=·‹_UkõܰvƒòŒÇÂòìm”B?šô~…è÷Q«¹>KÛÿûÙnô§GËý2ê¢X-O¬eZó¹"Eû¼ž'NŒÊñ±ùöƒ\âyÔ¬"+ÚŠ÷Ñ–{ûø$ÚOõG»&‹uOæ ã±FûK×í§Êä’bç·}Ä8 aUÔOýiç'­˜ñ@¬WiŽ2¬àó×6¥ürö·êV³ên¢µ/óä‚#È÷šwç²X7øbŽâ~DB‘g¥/‹qw¥r³/ÿ{ÇÏó‡/ýÊ“ÀQþ=Ï”Áï‹ûƒñ=ßtf\°çpÂׯ ã‚„ïöÇÖç|‘VA5ÍG=G—›ÃçÇ.~Úû*ûÛ¯2ž‰Ùœ¢X21¢ž'H=§¼“‘ò>ã…N]#c7‹öôéÀêOÄulåu˺p¹zº×žùb±Ò Îï‘Vl•ƒvþS‰ø·é? æžuršxîS1¦Ù2Ú›»aÇÈŒ â7å(Ó2Øø¯~Œi‘‰v›> ï—SÄsí!O‹÷qÖöú.þ<áʃifúµÄ¯cs¿Åñ^°L鎅eTjZ“rÏu9x˜ß(‚óTb¿%O׳Ÿ/´oÊä|®ˆÚYϽßhÒšñ~Zãeû âyÜÅ”Ä1âú*ª…åýXÿœÓzUüTìçÕ£©ç”ÍC·äb¿¶ºÚÑ]–þ%¶kÿbÅŸûD÷²ÍŠRì4]ß5—Ï¿=cOÑGâ=ާF.aSèÍêyÍN÷èÚëƒìoU“qìï^urùÅý ód·I\_Ò-|çùüÛŠæ÷gZÆÅþ*ê9¼ëÙQŒ¬­›{”8O´Ÿ¾õâ|ñ÷ m¾u2¾‰ÞpÆp’zž²ÔÙd}°z.n;F?æªP)T<÷œUíçÏé:?²_<ç¹S/ï#ê9ì¶çÉ(ê9z°æÜ*Γۣ§ÜœG¹*z2žWü·»rú…­Æ÷D¹ñÿÍÀ÷?QCS™zû Ìï—ZPÏ· ”»øqûjŠ-¿¾É~¾±y »<ã¶Økç.š‚ÕsÊ•Ì~{MÃHóóq kë—âúå­]£ŽRÏ õ:D|"î‡.¿}©xŸå„iXªhÏ¥„²_ç¶<öùTê9í;_ÔsBù#½³QÿÍC _¨ç¤«÷ã:ˆç²õ«gײ¿ãç™úÑ–Å~{., VÏg ÕÄßW–ßÝ¥6û9qJä â¹BÜÖp1/Jè¼Ë£:PÏÍdk)îÃ%o;ú¿`½ä[ê¹ØÒ\íKóïÑyÊ×sqÞ;°ãÄÏáŒÿ–|uøØ“ õ5º²îˆ˜/æÉ³…æŠûª]@Ü/)¶p2ãÂ$GÔÞôki?ãΈ÷7ªu©%Ö…|zç½â7)7ç®.õ{«ç!oËÕ‘ý¼%=ç1ñœ¿Zèôh®¯ÒVÚ¢ÞªüxuÇtQÏ¿Ml{(ØýÓvËg²Ÿï7[B=»?!ê|Ðöœ}Æózθ[j-ヨ}Ï&†pý²ïýâ:Z¿äZQÆCôΉO¨çS;š\AŒ7×yÇÔ`¿Fî¹òì¢hÏukTÜF=G¹s¦mš"öë1|~ž»½køüÄ/¾ÙpK¬Ë\úàðÍÔó—ï6¯š¤žš®˜ÙŸz㌊íiÌ©[Å{©_÷šKÿ²þX· ©Ç£;]2W¼W:sq#ê¿ÊW}þ(Fÿ×g­§ÿ÷“k‡¸þ«¹·RÇx'ög}##õœòèô¼áæ(ʏúÛJq‰î—Ðöž¸~;Ý¥^+Æm7ê 'ñù‘u§FÞ¡žcˇþ„óÏž ‹Ï¨çœ%–ë*oò½š¸sÍýK3ͲDê9ôÞÄ‹D=¿™ä-H» =Ã)žÇ߸k„xµgÁjê?l•ªxÞsªÁ¾.âyOZç³­ZSÞ7w8+Ö½]îê1Éäs/-ݽdõÙkÖ‘‡¢=_Û;XÔ³-)ê>ç‘Äß½–Bï§î¨ÖGôkG{æ«Ì8Å7zyÕA”[¥‡2ùd°z^k]°8šòcu…Fˆz^üAä5ê9¶Ý¸«_rݦK|üôõ¼Ç³û£Aê9eBÞ·±ŸgÇ[Üšñù©'Ûû VÏQ­3ëù§YWwÒŸD¯?\f‘hÏÖøž£F¦ôË›ùSb~71`Åÿ¬“šÕ5RżÁÖIÍê©bÁ`ë¤fuT1a°uRµŠ¬­‘*æ6l¯xý:©Y]#UÌ“lÔ¬®‘*æ\ ¶NjV×Hó7['5«k¤Š¹ ƒ­“šÕ5Rż’ÁÖIýR‘µ5RÅ•ÁÖIÍê©b¾ËtÅë×IÍê©bîÌ`ë¤fuT1g°uR³ºFª˜Ó3Ø:©Y]#UÌlÔ¬®‘*æ ¶NjV×Hó–['5«k¤Š9Pƒ­“šÕ5RÅ|ªÁÖIÍê©bnÖ`ë¤fuT1Ïk°uR³ºFª˜36Ø:©Y]#UÌ?lÔ¬®‘*æ² ¶NjV×Hóâ['5«k¤†f¾NjV×Hóõ['5«k¤Š¹ƒ­“šÕ5RÅ<±Ù_¿NjV×Hs['5«k¤Šùƒ­“šÕ5RÅ\ÉÁÖIÍê©bÞå`ë¤fuT1‡s°uR³ºFª˜:Ø:©Y]#UÌ-lÔ¬®‘*æ©¶NjV×Hs^['5«k¤Šù³ƒ­“šÕ5RÅ\ÜÁÖIÍê©b^ï`ë¤fuT1Gx°uR³ºFª˜o<Ø:©Y]#UÌ]lÔ¬®‘*æA¶NjV×Hsª['5«k¤ŠùÙƒ­“šÕ5RÅ\ïÁÖIÍê©bÞø`ë¤fuT1}°uR³ºFª˜Ï>Ø:©Y]#UÌlÔ¬®‘*æÙ¶Njlά­‘*æì¶NjV×Hóÿ['5«k¤Šµ‚­“šÕ5RźÁÖIÍê©)9ƒ¯“šÕ5RÅ£©øp—pWàÅ5¤á:¾Ç¸›HÇ-ÜÆÜÅø)gæÚ¥>üŒ ü‚xˆ_áÇox„ÇøOð €g7¾²!;r g®Ì5Q•ȃ¼xù Â›(ˆBx …Qo#EQ ÅñJäÊ\gµ$ÞG(J¡4Ê ,Êá”GTD%TFTE5TGš¹2×oU£6"QuQõÑ|ˆ(4D#4F4E34G ´ÄGh•+s]XÚ mÑíÑCŽè„Îè‚nèŽô@,z¢z£O®Ìõf è‡þ€„Á#†b†câ1&ŒÂhŒÁXŒÃø\™ëØšñ 0“0S`Á4LÇ ÌÄ,ÌÆ§°bæbæcæÊ\׆ÅX‚¥X†åX;V"«°Ÿc ÖÂuX ؈MØœ+sÝ]'’° Û±;± .ìÆìÅ>ìÇDr.1;í‡q_äÊ\ËׯqÇp'ð ¾…'q §qgq瑊ïp—pWre®‡âÅ5¤á:¾Ç¸‘+s átÜÂmÜÁ]üˆŸp?#¿à>à!~…¿áãw<ÁÓ\™kð %íÙ‘¹ DnäA^¼|ÈPáMD!¼…Â(¢Ì\ó8EQï ÞÅ{(‰÷ŠR(2(‹røåQQ •QÕPa¨pD &jAÚˆDÔE=ÔGhð!¢ÐÑD™¹>³ÍÐ-СZC‡6ˆF[´C{tÀÇУ:£ º¢›2sÝçô@,z¢z£ú€~èˆA#†"Ã0#¯Ì\OÚ„Q1‹q 0ã$`&c ¦Â‚i˜Ž˜‰Y˜­Ì\§ÚŠ9˜‹y˜XˆEXŒ%XŠÏ° ˱v¬D"Va5>Çeæú׬ÃzlÀFlÆ8±IØ†íØØvcöböã€2s]ídÂaÁø_Á¯qÇp'ð ¾…'q §qgq©øp—pWp^\CšR¬6B{Ƹ›HÇ-ÜÆüˆŸ”™k€ûð32”âÍAÚ3à!~…¿áãw<ÁSüÄ©lÈŽÈ™;smq%r#òâ äC~€ o¢ á-ÆÛAQCq¼ƒx7wæšå%ñ>BQ ¥QeQ <* *£ ª¢ª# 5ŽÔ̹ºµ‰:¨‹z¨øQhˆFhŒ&hš[ÌþD{Fs´@K|„V¹3×Xס ¢ÑíÐCŽè„Îè‚®è†îˆAÄ¢'z¡7úäÎ\»Ý[üõ,m1ƒ1F E†a8F #aÂ(ŒÆŒÅ8Œ‡Ÿ 1 “1SaÁ4LÇ ÌÄ,ÌÆ§°bæb`aîÌuæmXŒ%XŠÏ° ˱v¬D"Va5>Ǭ…ë±± ›sg®_ïÄV$a¶cvb\Ø=Ø‹}؃HÆ!¤à0Žà |™;s=17¾ÆQÃqœÀ7øœÄ)œÁYœÃy¤â;\ÀE\Âe\ÁUxq i¸Žïñnà&ná6îà.~ÄO¸~F~Á}<ÀCü ?~Ã#<ÆïxŠ?À3ˆ‡ËÙ9¹ DnäA^¼|ÈPáMÂ[(Œ"x!(Šb(ŽwPïâ=”ÄûyÄŸ‰ÑžQeP <* "*¡2ª *ª¡:ÂPáˆ@MÔ‚µ‰:¨‡úh >D¢£ šB‹fhŽh‰Ð ­¡C4Ú¢Ú£>†Ñ Ñ]Ñ Ý“G¬öA{FOôBoô…ýÐ0ƒ0C`ÄPÄa†câ1&ŒÂhŒÅ8ŒÇ˜ñ 0“0S0LÃtÌÀLÌÂl|Š9˜‹y˜XˆE°a1–`)>Ã2,Ç Ø±‰X…ÕXƒµp`Öc6b6c œØŠ$lÃvìÀNì‚ »±û°pÉ8”G¼ÖK{Æ|/ñÜøGq ÇqßÀƒ“8…Ó8ƒ³8‡óHÅw¸€‹¸„˸‚«ðâÒp?àn"·pwp?â'܃?#¿à>à!~…ð¿ã žâð âE‘lÈŽȉ\P"7ò /Þ@>€ o¢ á-F¼E1Ç;(wñJâ}„¢4Ê ,Êá”GTD%TFTE5TGj ¨‰Z¨HÔA]ÔC}4€" ÑÑM¡E³¼bVqÚ3Zæ+ŠÑ–¡CD£-Ú¡=:àcèÑÐ]ÐÝÐ1èžè…Þ胾0@¼ÓÓ0ƒ0C`ÄPÄa†câaÂ(ŒÆŒÅ8ŒÇ˜ñ 0“0S0LÃtÌÀ,ÌÆ§°bæbæcblXŒ%XŠÏ° ˱v$bVãs¬ÁZ8°ë±± ›óf®ÇéÄV$a¶cvÁ…ÝØƒ½Ø‡ý8€ƒHÆ!¤à0Žà |‰¯àÆ×8Šã8oð-<8‰S838‹s8T|‡ ¸ˆK¸Œ+¸ŠkyÏ#Þùâ}œµ/Þoñ¾xD¼·!Þ¯ïAè³e¾[`yñ¬ÞõâÙ·xFÈ–ùÜW<ŸÏQÅóNӋ燎Ïܼ/ža‰gMâ¹x~#ž³_<·°¿xàyqo]Ü÷ªÅ=eqïWÜ£5½¸ïé|qŸÐûâÞ›¸G&îe‰{NâÞñÅýû‹ûž÷Äu»¸¾×ÂâšU\[Šk@Û‹ë&÷‹ëq½ Æõbü­É9¦5¾#Ú_Œ¹ÄØ(ýÅxCŒ Äù[œgc^œ›l/úz÷‹¾Sôq¢?ý†hßúíÅòâøs½¨ÓtòeB ̰Á 7¼ðC•2¡FXò‰'(¤#€ü”  0Ã'ÜðÂUÊ„za.xŽBTŒM ƒfØà„^ø¡z“2¡FX`‡ ¤#€’):`† N¸á…ªB” ô0Â;\ð Ê·(jè`€68á†~¨ S&4Ðà ìpÁ”E(jè`€68á†~¨Þ¦Lh ‡Øá‚~¨Bø9h ‡Øá‚é ¤(u -b`‚¸á…ªb” ô0Â;\ð „§Lh¬p /üP½C™Ð@#,°ÃÒ@H Ê„10Á'ÜðÂÕ»” ô0Â;\ð „¼G™Ð"fØà„^ø¡*I™Ð@#,°ÃÒ@Èû” - 0Ã'ÜðÂU(eB=Œ°À(+R&ÔÐÁ3lp /üPU¢Lh ‡ØáB*|PV¦L¨¡ƒfØà„^ø¡ªB™Ð@#,°#©ðAY•2¡†˜aƒnxᇪeB=Œ°Àd¤ÂeuÊ„:`† N¸á…ª0Ê„za…ÉH…Ê” 5t0À œpà ?Tá” ô0Á ’‘ ”” 5t0À œpà ?T5)ÄÀ+HF*|PÖ¢L¨¡ƒfØà„^ø¡RS&´ˆQ‹Õß($#>(kS&ÔÐÁ3lp /üPEÒ†¡E L°Âd¤ÂeÊ„:`† N¸á…!u)ZÄÀ+HF*|PÖ£L¨¡ƒfØà„^RŸ2¡E L°Âd¤ÂeÊ„:`n V–¤L¸‘ŽB4” -b`‚$#>(?¤L¨¡ƒfØà„é $Š2¡E Äj¤b¥h’‘ ” )³¡˜…2a€6¸àA:iD™Ð"&Xá@2Rჲ1eB  0Ã(ûS&ÔÐÁ3lp /üP  Lh ‡Ø‘ŒTø H™PC̰Á 7¼ðC5ˆ2¡FXà@2Ráƒr0eB  0Ã'ÜðÂÕÊ„za…ÉH…J#eB  0Ã'ÜðÂÕPÊ„z˜`…ÉH…Ê8Ê„:`† N¸á…ªa” b`‚$#>(‡S&ÔÐÁ3lp /üP Lh¬p ©ðAO™PC̰Á 7¼ðC5’6 -b`‚$#>(M” 5t0À œpà ?BF‰™U(10Á ’‘ ”£)jè`€68ᆄŒ¡Lh¬p ©ðA9–2¡†˜aƒn¤#€q” -b`‚$#>(ÇS&ÔÐÁ3lpƒt22¡E L°Âd¤Â¥™2¡†˜aƒ ¤#€O(ZÄÀë'b•NÊD*|P&P&ÔÐÁ3ìpÁƒt2‘2¡E L°Âd¤Âå$Ê„:`.xŽB&S&´ˆ V8ŒTø œB™PC#,°ÃÒ@ÈTÊ„10Á ’‘ ”Ê„za.xŽB¦Q&´ˆ V8ŒTø œN™Ð@#,°ÃÏt1S,e"deB‹˜`…ÉH…Ê™´ah ‡Øá‚é deB‹˜`…ÉH…ªÙ” ô0Â;\ð „|J™Ð"&Xá@2Rá‡ÊJ™Ð@#,°ÃÒ@ÈÊ„10Á ’á…ª¹” ô0Â;\ð „Ì£Lh¬pÀ /üPͧLh ‡Øá‚é deB‹˜`…nxá‡j!eB=Œ°À(7P&ÔÐÁ3lp /üPm¤Lh ‡V8ŒTø ÜD™PC̰Á 7¼ðCµ™2¡&Xá@2Ráƒr eB  0Ã'Üð•“2¡A L°Âd¤ÂåVÊ„:`† N¸á…ª$Ê„10Á ’‘ ”Û(jè`€68á†~¨¶Ó†¡E L°Âd¤ÂåÊ„:`† N¸á…!;)ZÄÀ+HF*|Pî¢L¨¡ƒfØà„^(]üÔÐÁ3lp /üPífA=Œ°À<ðA¹‡2¡†˜aƒnxá‡j/eB=Œ°ÀRáƒreB  0Ã'ÜðÂÕ~Ê„zaÉH…Ê” 5t0À œpà ?T)èa„$#>(“)jè`€68á†~¨Qæ!±2eÂ+HF*|P¦P&ÔÐÁ±b–˜µÒ 7¼ðCu˜2‹UÝ)&Xá@2RáƒòeB  0Ã'ÜðÂÕ” b`‚$#>(¿¤L¨¡ƒfØà„^ø¡úŠ2¡E L°Âd¤Â¥›2¡†˜aƒnxá‡êkÚ´ˆ V8üµ˜‘‰2¡(/P&ÔÐà ìpÁƒtr‘2¡E L°Âd¤Âå%Ê„za.xŽB.S&´ˆ V8ŒTø ¼B†za.xŽB®R&´ˆ V8ŒTø òR&4Ðà ìpÁƒtr2¡E L°Âd¤ÂUeB=Œ°À+>Ï#–@V(¾Å÷ø ùó*¥ ÆGˆÅHÌÄJìÄ1xófÎ[&æ«snˆ¿ó$ÞÙŽï­‰wÍTã©+ñ–xwŠã/Y¼£$Þ+ï‰÷wÄ;7â=™2ül9~V¼C"Þûïjˆ÷+Ä;â=ñîx_@<ã¤Õ«ò=Ä3kñœY<ÏsÅ3XñÜT<ëÏ'ù’Ê6ìoñìN¿Ÿ/žaˆçâY¸¿/îÉ‹ûèâÞ·¸_-î1äóGñùâþ«¸g*îsŠ{“ |¾¸(îÛ‰{mâþØL>6Ÿ/î‰û=ⸯ"î…ˆû➃¸OÀ5{רq -®{ŵª¸¾ׄâ:N\{‰ë%qÃõ†Šë?í<]ŒÙÅ8[ŒÅxVŒAŸQŒõÄøŒ±’б_Œ]ÄxCŒÄy]œ‹ÅùSœóÄyJœ[èã•ôÉ>ÑRôoÿÒ}üüÿ&ƒ"sö¾í[·0öé+&“z>³_EEæì~í-Zïóÿ"’&^uåXI•ǗΜŠïÿóƒ9_üà‹i‰^õ_2ÿžø÷íÓ¤g|Ïöñ­{{ĉ«6dDõj=â{öÜ·šAÝ£CûÖ5þÚg„……©kÖ ÛÚêZÏ·aá™ÿýy"Âk…ÖàGÂÃjGÔªQC­ û{~Åà9"¾çp¾JŸ}ãú_ÿsü˜xJøÚdþ*¡¯¶ÿ¯$WHnÑDZ÷ìÚ¦}h§ÐyÞlÄ[ÝbÚÊÏ™ÍèüÿY‘ ;th÷â?Šÿ1ábƒû‘lÿó¿¿Ý{èj=ãâ8Ú†ì9¼§1~€±¯bXõ°°ÈõÔ ‘꾑õ´†÷5 ]¯†VÛ8¢¶¶qÕð¦‘UkÖR‡UmX+"²jã†M×Ô†GFF†×üöÊM^ÓþÿÆÖÿ'í¿V­ê°šÿÞþkÔ¨)Û¿ŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒÌÿ_ßaÀà¾}šôŒïÙ>¾uOcø!qÕ†Œ¨^­G“†öhÒ´}ãv-¢;´h£û ŸV»V­Ðç[uæ6,¼fæV$"¼Vh0uÍð°ÚµjF„†…‡Õ¬¦ ûÛ~Ë 9"¾çp¾JŸ}ãú_ÿsü˜Á¤œÌ_%ôÕöÿ•ä É­È®P´îÙ;´MûÐN¡/"þ7E^„ãsˆÿ~þÿ¬È†:´{ñÅÿÇ4ø·Éö?ÿûÛ½‡©Ö3.npßjÃFöÞÓ?ÀØW1¬zXXdzêð°ˆHußÈzÚÃû†Ž®WC«mQ[Û¸jxÓȈª5k©Ãª6¬YµqÃ&kjÃ###Ãkþ {å¿&¯iÿÿÞú«ÿ•Ïø³ö/ÚË¿µºEh­¿ë— –ÿòöÿÚþ¿}tÓÆÚ5lÕ£c ]“6ÿÊgü'ý¸ìÿÿ™Èþÿ¿:¯iÿÿÖúÿR÷ÿŸôÿáµdÿÿäµýt›V ÛµèÒð/ŽýEþƒþ?,¼¶ìÿÿ‘Èþÿ¿:¯iÿÿÚúÿZç¯øúÿµÕêš²ÿÿ'òÚþ?¾g/ZäࡽýåÏ`¨kÖÌRÿ_CöÿÿPdÿÿ_×´ÿ¿±õÿyû¯QëßûÿÚ5jÉö/óåãd¶-rŽcì­xÞG•Žg[RüoÈÈÕèE»XnEfטýÅäø—ÿžýÿ5²ž×_ÿµkÓ¸iûömÚýy–,_ÿÕ¨]³v„lÿÿHäùÿ¿:¯»þ{ÙúÿòÅŸâ?ºþ‹ˆ“×ÿD^ÿü§M»M›ôèаQ«¦ñ3þ£û²ÿÿg"ûÿÿê¼îùÏ¿´þÿ;÷ÿdÿÿäõý‡†þjÇÿ"Yÿ«k×®)ûÿ$²ÿÿ¯ÎëúÑúÿޱ¿HÖûµ:¢†ìÿÿ‰¼¶ÿ×¶hÚªÉßóÿIÿ!Ÿÿü3‘ýÿu^ÓþŸ·þÿ‹ý­Úòùÿ?’ ïµÐuh¡kö7|FÖûÿÚ5åýŸ&²ÿÿ¯ÎkßÿÊlýË)à?¸ÿ&ïÿü#ù“÷¿ á=:´o]ã¯}FXÖßÿЍ.ûÿ$²ÿÿ¯NÐ÷¿þ–Ö¼ý×®No_ó{ÿ³†¼þÿGräÔ^M°mž™.Í-këú/·M×ïÒŒk×ãÕ6ûÇ»5'Gx¹ýêÂMt¥û_nCJ%i®nuàå6%f§¦ð»Ã^mÇviÞ ‹~µÜi¯¦p™Íû_nW=ܨiXäÑ«íívhmsàåöϾÚ€$Í;sÇx¹=wi›¦ýÏ^m«Ú6j–=ºÿå66n³&ÏÅÈ/·Êæ[5m~újk.´C³ð­q¯¶‘[6j>_ïåvÌÎMš¢æ÷¼ÜþÙ÷{«µK“'­Ý—[ÓæÝšÆ–ª¯¶+÷íÕLõöþ—Û´={5ç>*Qïå¶_ÆVMÃ:¦/·‘ß»4'Š6}µý³Ï·ÎnÍŒCõê¿Üs»4#Î5{µ­¶[Óèpý/·¿Ü«‰ðåÝÿrûgåŸ)ºM£Ù:þÀËmÑïwi6ìújûgÿÿY8TÏïÚ©©[Hý—Û[®šÝªQ¯¶Jç^Í,e½ý/··íÕèf¯÷rûÓÍÝšþSÊx¹Í¿s¯æ|¥Jû_nûNÛ«5­ÞËí½[.ÍùIMê¿Ü®Ü¿Ws«Ì[û_nãÆïÒìèUÿåöϾà£åå1^n‹nÛ©ùuäWÛÝ5¶j"_m?è»]c1Lxµ-¶È¥é2»Õ«mdνšE½û_në¯Ø¡‰úfÔ—Ûæ‹÷j®ÜºÿåöϾ_ŸC{4;âû_n·ŒÚ«Y•¼ðÕV—}¯¦Zákõ^n»¬Û­éS³Zý—ÛB'÷jV¤”«ûr; »K3}BÛú/·öùëh"³Ï¶«ÿrÛóK3{K·WÛ~ ÷hú…äyµ­µSÓ]?üÕöÏÊ+·W³TûÍþ—Û.«irW;Ö{¹ýÕk[·kÛÇ×¹m¾v›¦õÙ ¯¶ëþ+w\E¶½ —àÁ‚»KNo^—àî\'NB$èÀpú¸äh7îî.ƒ îƒÛ|}ëþÖž©së~]©KÿóTÝuz¯7aî½§ê&¯µÒ¯ ä¹.Ì_—‘—Ûz0:Ð#åÝN|ü¥#CBlÈÖ-Œ;å oj ä®`@LM¹]:èz]—HµüšH5Êdïê.tüµ×í€ÿ©‘Ül9Üè%UàPF —öG ùå¨í¾äoŸèflÍÈ8“ ÆŽçªå»xƒó³–yÉH—Ï å²íFÔÊÈ ev es¹ózšÑ¢·wJ´NHµþ[;9°·ÏFVÛ›Žr»&sëœ2aMމܧu8Üá¡@ª­®† »ç¶`dV£ ÷v.äþÄQõ¤Z»s>#¦˜Ñ²ÆLn¤É’‡q?ô·cðêyÜýGmì¸[!Å„ ÅÆqµt¨&ÈÆe”_qD7‰œtÚ†Êä×_=P¼Dªå­âF…S¥e²á 7J$=‘È‹<øö%ûËnœÌ%Íj;qrU_FL¶¡ñ‡…ÜÝŠˆ›×PCFæ7!Oì0™TË·GðÀö|·@¾æFrQF¬mÅâ— ¹7MX{u"÷Þ8=LWüeò¤ÉŠ57¸jýÍ •§ãÍŒü±ÕŽÏâîåj:øí/‘†jF\ÏÚE&ÕÖŸ~ψCc3²ã&,§N äOÕWMHš7Ž‘a6#òìÏÝwÓ‚Ç—pó,0¡ò¬1ÜÕýôxº«wÆ{iÏgjÈÐ9ô4Êd©3Š‚@úÖ\²"âe˜LúÖŸÊÊIrs>ô­[c¯÷j6ªlÅUËzr:Ž=•ɘÃÌÎ:Š»D´¢´2R䡇é(Ós÷„Ã#A¹¹uŸ{páMš—LÍîÁ®'¯$òýh7Æ5*ÁHµ|Ãg{˜Ç"‘ï{Àz~ô’(íFÝ"•™¿«ëîõçž´¸Ð?[#n¸rfHÍÉUëŸ÷›ïëcd¾Ù¬n?Ž«™æ@KÛßæzbÆ­ ù\µõß=ó Û³ÍrmV7nl¨ÆÈŸ8ª_îXQ9#_Ÿ³`Àïܘ²n”*q:ÑpÆ@nýÅ.4hÛœÛg·›*,à2˜ðãÒd®ÿ7Ÿeç®|%"iwC‰¢³à}Í™TËÿä‡ò²2¹*§Tå6üÕ… sëMô Tã–ÈÂ=XPã”@½rÂT©#ýZP¶y8wA€²*³B ÕòU}èÁ/ýokÈSáì-¯È•%Ò±½Î\Fž¶Xá—=Š;øºÒgêwÌ­Ã’¹ç$R­þgéˆ+9‘ç§ãÞ¦YÜÏ=õè× ·÷¥Ê‹^Rmýƒûì“c#Ÿ1!¶ËTîOÕõGÌxÒx#“›­™Ê=úXyÚžžÇ=>ÚŠ|ç#¹S'›±ãÚ,ní3z ø³wõ%ò€U^²Ó=.ª$“}ózà6=—ÈTƒ u“1R-ì}7^‡ý%‘K"<8”"r?¾R^l9 !Û•ó`å‘û4ÙŽ‡ýÃÙj¹a5æp‡O7À¾2Ùøb:N´áªå[gs£õËÜŒ,žèÂ5±wJW3v'‡rÍUŒ¨˜¥çß–² Ú‘2é¨èBÒ£6\µþÇìH:5Ÿ‘GÙÑkS8W¬m@ÌŒ¦2ikj…fc$WmýçßuÈÝ5#{)#ž½¸è%⨶¿`Ä¡ ÃùçÖÕìÂÍö„¦uB¸Q{ (_±·ÿkŸ7Èu>¸%2|¯Õ?ΕÉÛÓq2z÷Ù˜ÀÈL¿‹¨‚÷^R-ÿÔëÔ-PF"«¼ñà^ÙÌ2k ʾnËÈðFé¨Ô`w@ Ù›sË7Ô¡äë4¬¨¼ØNÖÖyI1»™æe¤Z¾Å¥œxó`0#sNO‡mO(wÖ$=Ú&–â:Ûë°•²É z^^®!˜£2R­ÿÚ4žÅF2rx6†åŽânYéÄöl½d²“]Ù_X(‘jë¿9¨<’æW—Ion Úw^Àý‰£VÕ€Ïú&ŒªÇŠ\e¸ßüõظػ´wF ä˜çÊHo ’ÉÓÏ,¸])’»;ƬÇü¹Ù”¾û"L×XpsÇB™LºäF‘Y¸jù_î÷ Ò’ÁÙ·§w¤#ÜìÏ-èX9’‘£OqÅ6ŒÛRùŠåÔ5–ȇÑzŒ]N&/„Ñ~ÇFzn‹öô­—TË·a‡ sæF22ÛY ª_‰à>>'Â:¿œ†L-®Cÿñq™;·ëã¦3rzŠ \.jý?N²âØÕHF6¼hÁÝÇÜå=ØXã¾@ö(ã€1r2#ÕÖ/›Õc×§Èdð17ZõÌÎýFÕ÷:~[‡[Á ¤o=ô¢ˆˆØ(/é[÷4p ±Ç$™ô­³ñnLúµ8#}ëí>»°¼T FúÖÕòWÚâÆÂ˜|Œô­¯¢ÇΜ%é[ï´Ì„˜²“dÒ·~µ¡“§Î”ÉŒæ[•ËŒ‘Óé[*¤Çæ'“¾õ…sôð[RF&3ÚÿT‘2Ò·þl…EÚÌadFןñÔƒš­m2£÷ÿãêTÒ‹ÿŸ›[‘+2†‘÷•ãTŽqÑÜØön¨W…[a€ÕÎá®t¸ðbl ·ã;ž‹àÞr™PöÀ î…ûÊÓ Ÿ·Ð~‹ŠV”È+-8s.J&ÕòÏ,èBÁ"]dÒPÅ!Ujr-V¦ÞäÚ—x0£ÿn‰\êÁæ<'2® …?1òjŠyïFqfÒ!¢üZTËW8¿o]Õ‘;<èZÌ ë¥ãØÌ…Œœï´¢ý¼î׃:}˜•[n°Ä ©Ö¿í·tt‰šÉH¿ 騮™Ï-èQgg)n‹ˆûE/©¶þ>åß¹ÙèF~ô˜Pê÷\µû30ª¯ö˜±3k8#×)#¾gwÁG;*Ž çNiEžÖ1\ÍX3¢äùÜ+&=êí¯Í­l×5ÉK^\¢Çš™ÕdrreOé÷Z"g¦»plI #Õò÷ÍæÁ´)YdòævÊ$$²Iq/ßÕhÈq-=ð+õD _ͶcC®(FÖ 3cÜ–\Ã^=i-“¡ßÓá>“«–¯þE7¾…äedí.¼ ¸ÖVÊ‹ó¸?#nèÃ}\Ú‚i"e²MVönÇUëßü¼qIŒÜÞÜŽ'r÷\ šÞÕÈä« +ú֊᪭ë²!¦œŒìcÑ¡ö/ùG5v—£3²ÜeBµÝ¹µ®šp\(wMš™nuâv8""¦h,>÷Ü/‘ݘ×:\&{JÇäs³¸ž&,_9‘‘¿*O×ìO$R-ÿ©Ì^„î.#‘ß•‘œ¾§°†¼YÍ¥ìA;1rx­tÌMYÈÍRØ€™Õ5܈ì:|ɽA E½(Ùe¶—L¹áÀˆ*c©–oBu'ê­ÁÈ÷ÊqªØ­y\G;=~[†;¼Œ½6èrMA/j×Ù£!ÓJ;ñ$~$#ÕúWßhîÏьܖɆˆ…1ÜrGœ¨Ô¨·LjþðàÛŠñ©¶þèÝzäúXW&×µ`ƒ.’ûGuvv®ÍlÎHÖ]ù•ýU–{á‘U+çá&þ!âMîù÷µ ëõ’ÉCE¬¸v'š[RrãF§BÜ[ʳ•ÒKÖXPïF”L–näAÇæ¯$R-¿þ¡›†ŒȸiÊIùÇé¿=aÁÖÑŒ<°ÁˆEGpŸQsOG‰¼®‡~S™L.cÄ‚¦ý9ᨈACK¤Z¾§ëlØ÷W4#9-˜Póo é”u.? ${|qÉ•$‘+Í&ìé=ƒ‘éw•Rž©Öÿe°3ÛÆ0òÅ žöŒæ–«âÁá·ï2ü~:Þìe¤Úúq84zšLŠù=¸\)÷'Žê§ß÷ebäHåiøGÄ~L6‹¨ôq›—ì¤Ã“)Ç%òX¨³ON–É ÊÉ/&5?÷ý7>fªÈÈÓštÔȱ{EÙC&ÞäN©j§Á¹jù78ݨàÏÈúû]øq²wI%=…ãÞ<+ÂU5H ëM˜Öh†L>Hµ!ñk4·õN3’k†qùܘ¼±W-»¯l¤þ eä–æF¼Ô àžË¢Ç– e²ŠÉ€jÆ n¦mz”{X“ëeƒ_ù®Zÿ…u,`¿E2rÖ3V-^È­[׌ûãçr‡ÖaCÃl\µõ×åòbAËlùð¢ cG4aäOÕ¤Ç""óG ¤Í)â㔲†¨Ç‰×µd²ÎoFL›?’»¾„ÛJ^ñe3{£bVFvod†gë\î±Úz|/Q‚Ûô€ˆ±re‰l°Þ‚Y¢dRuT78áiÖŸ‘:9p½Ç4nÀMóþ#‘¡môØ–­ŒL^æ„Ü~·Tn\ø.‘ëB<¸>ô´@z™ GÄ0R-ßå“z$~ªÏH?»òbô—@~º`B߆¡2ik¥§”$n¹qf§÷Y„råC©Öß–ÉŒGƒg32{ˆ 8?•Û㤈Ǭ¥DÝH»»È¤Úú]ºÛñ8<Š‘9JïœÄýFÕ÷Ò‰"Ê4þÍCúÖç°à‹”Ißúè‹4Û>–‘¾õNʺÓË8ܤoýhc"Ƽ”Èÿ¸_%Ô ;Ö·`¤oýñ1#<%Gˤo½²nЭéÒ·þn‹yÓ> dFóþ"bó¨é[/½ÛŽE"eÒ·þå¾G³D02£ýÙ#:¿ÃHßzŸ*…ÄÈdF׺J‡QO_ dFïÿÇõ¡õ¿?òßÔÌÑ£âoµ9û¤# pGÒÁ9t¿@–ŒÑôGw ÙPcÆ©Éá2ÙQ9iŽšÍ-£Ê×/‘}–{€)Wry‚²ÿ1L&ÊžóåúTTË_ÿ¬uŠrm-RûecäÞã&Œ0«»£GJ#8S‡_Ú}’Hÿ†F¤O&“1EO]##ÇP(_Lªå³Íµ¢ãÛXF–šá<É]'âAéJÙi„²ñúú’»1«ÚË%9á›o—t‰Të2‹~£bYk”ÓÿŒáÖÉæÂ±·½¹Ó†[Qëf,WmýQ¯\ø63P&O6ò¢ño?¼¤ÚýÕ ­uX=ów¬®¼(¿ÜJåF)ÇÅyVJäÖ7:lK.&“û•=ÉôÛŒ»=¯“~Ÿ&‘§‹9Ñîõ8FþË‚OCb¹sïYQ¨tw±ÒWª&jù×·sÃ^‡‘ªº _Õƒ{=‹gö¸RZ¤ŒÜ¯«½ä+BÄÊd¾*4Ê9‡ûm¨þ˜ÉýWßÇ›+»þéÿ/_Ñ=FDöžÀȧµ È:º-·úm=òÝÕÈäËFõÈM˜a˜LÞ­ç‘uݹªO­+|ì?™‘ÉcÒq)‚Û{¡†Õß$rÜnöÿÙ[&Ó¦¸1iR5.«èEñù-$²÷ò˜ÉÈ> 8Ó:ˆ«–/ÿcJ,ÂÈÂÙuø³•W × µ ÷ÙüúÆm¶XîyxÑ´e¬†|ä„ñÏ`Fªõ÷[iB«¡³™ðÈ;¦p'7ѣ같¬ÜÔ‚“b¸jë·¿a†¶R4#«ŽÐ#SÜŸ8ªóEô¾ÿÔKŽß#Â~+J"CÎZ‘½TœL>ž›ŽÄÜæïmøú}#³O0!ºj(÷`+ä·%2ñ¨ ~LÉ[=(Úï¤@î*lE³°XFªå¿{Ó†æuc9<ÈŠ†GþÖ»EË$kG‡{1\ËezµÔ ä÷Í.˜ƒÚ0ò俜]-‘i¿ZppË"™TË×%IDñ-™²Ì25Ö¿ö’ñ“è’¦Lú¯tÁ™«7x‹ ƒêÎaäz›ˆ3+Æ ¤Z÷t#´«‚9ö™]à~ösbÙññ2™¬qžæDʤoÝ^Ç}ïê3Ò·¾‡Ùñ´ï"Ff´ìa–vÏÏHßzàJÚn¹%2£ëìF*«ÉÈŒÞÿëñŒä¿cÔã]£ŒÜöZ‡>—+pSBt¨œ3·ü|ïÎõÈÁ||ŒLV°Z‘^([u˜—zeçöåžå9rÓ[+ ”LÉQ%¼¨ÿ4U"ÕòPž–vÄd´Þƒã3p+w· øH#ïd6bÔò‘»FDK]‚D>JÕãÓ-LVz¦ÇæmpR„TÞ)‘jù’FØí÷xF¾eÁ§L‹¹‡»‹èkmï!‹(gÿdƒDf[eÄÔLS¹{€ˆ‰ú#R­ƒNV8Å32d»•u‹¹þ9<øeJqîæ€tØÛ.⪭ÿ­”­§Ê\Ƀ>-nH¤ÚýÕ×½u¸¸<3#Û^‘ùÖ´qdi.‰|SU‡=¯Ÿs3'Â&Ë䯹ŒýC"½Üh¨™:ÇŽƒÞ8î–T' ZFsÊžjaho®ZþøÛnÜVŽ‘wáLD;îëe:èZåçÊÊÏñÈœK _V6cä‹(™<ÖÏŽÍ%sß³bå¢x,Ζ›«–/÷|æþ¶€‘Ý÷”Ìò§)Ý*Êä¬3”*4”»ª‹Wý{pï½±ãZX®ZÿðÌŒ=ËÈN#Í*ÅpsV4a}ÂlîÀ¯"ÊÚ/ ¤Úú—•K­³ òí޹z1ò'Žê¹X ÚÍÈŠÊ·Õvm¯†œçVö¬[!“¹•=hh£™ÜâS¼HÙ´YCö?ëÆŒÒY¶¡ ¬˜ÃMî¯C™qY¸º"Gœ—È.’gÄˤZþ6«œ¨7k #ª9pstwI„SÚ,‘#'ê1ã{#™\_×…ãûs›ìñ ï¨kyCù äY]Ž‘»XPswW-Ÿ¹«^‘zŒlÑTÍwÙ§ªÞEÉd¯=V¼+šÀÝy݃q{ŽJ¤5¿e¤ZïA2‡E0ÒUÛ„þ˜Ã=¨<½Ÿ7Jäù¥F¬Iœ,“jëínÃÔñŒ43¢xß`îOÕ½”=Š®‹—Ü8SÄÛI¤k±ï‚Ëäãöv¬hû·CÊ9~+Œ‘½fZгîbî¯Á"6*,‘µÒõõ‡LÞìàÅðf$rÍ[Ð:#ÕòGm¶£ ‹cäíœ6xŠ$pû·7¡Ð¾¹2Yò€—‹%p{OôbÌù}2]y¦v+ÎȆÊn¸¬@žXd€ýQ_™TËW*AĪñór§ò¢Ï{?ÎM.œŽü-cdR’œÀבܸ@äïñŒ¬RJ›«qÕú—Û`DÔÚ)Œ<˜ÏˆÉ£¸r˜Ú8™ üÕ—ë«sÕÖO<ª¼}“ì9Ú-Â>c§DþÄQí,+ljPDÊ5uØûø%wÀ]V†ÍÉeþnüˆkÁmÚÔa{WFf2ŠxØy“@VcA㜋er’Ö¤²³¹Ë®ëQþukFéð¢Ø_©–ÿþT °˜‘×ï™Ðav$·\;:]Z “/ë¹±,-ëÿÔ¡O¦3rÎE+’J&p¯;XÑ|–L:¦{ÑêcA ©úOñ¯ÿ~z:R"Ï·Ð!çâOÜ1¿¹‘X¼ºL–záÁçõ²D¾R~í næÖ¯fFôç(™Të?'Þ€Ýíú1ò‹ŸwvwäöœæÅÓó5ä·ï.Ü­ó #UßÏ ÐÙ‡Ëä»26ÔÌ—Àý‰£ú§¬ÃEÅe²cˆ–K¹7¶(Ûã:O%²O°¶¹Y¸® "޵\νšÍ€•9;ÊdÄž=ÿ!‘Õ{ñøC?ߎζ8™ü6Æ‹-®^R-ÿÆéF”\?‘‘E«ðªPî”Ì^hJ™$2r¼ÅÇ_Òo÷Ñ98„‘:Dl)­ÔȲÇr3 géÎ\µ|KèQcL™¼~Ê€Iφp߆xÑwb — ­â…5d…@F>sbàþ¡2yTYמU$Õúg[«Ç #[ÖÔãj¿šÜª±6qÇsKH«3˜«¶~ï&.ìÚÝO&ÃjyÑõĉüFÕ÷ keÀØ;ÝdÒ·þ½ Î Hßzz šMŽ“Ißúѯ.,žõ #}ë•Z9ð%2 ÿ§o]-ÿáBzt_•‘¾õ†½•=羜Œô­—ê'bD­†üží”¸Ì—`Âù eÒ·~°Ýe¤o}ì!'FTÉÈŒöïtT‡ÛJ2Ò·>0\ÙØT)]ÿÆvÌ?#½ÿ×¥ÿþÈÓ©<ýîîìÁÈq õX´± 7ª¾ßûþíå]"j:+]òé‘5K}™Œùd´~‹¸Ç“\¶ôåÆyQ¾rŒDÖ1 kɾ2¹J9}Ë4…«–¿úJ/n(.wx13‡kdÃè"‰Œì»Û„ðL1Ü¥eEŒßú.Ôä×aDÓï¹x—½K,edçh¿<üM ÕòåO°£ÐF´âhµDn˜FDTã¦9¼±ˆq¹ò…Ó:&2òäA支ÌUëÿq»YX"#¿}µ ô²¥Ü½^œ½ÖU ;wWþ‚†0Rmý%­ìx¶t‰LN¬íÆ…m¸j÷g`T3EèP.¨8#÷½qsÞstTPž6÷b5dó "š´^)‘öÛ–-A&ß}sadÇNÜsŸ=H›vE ƒCœ¬ÆÈý¼˜’eµ@n®jÃÅf‰ŒTË?,Þƒ²/s1ré76û7æ.2ê‘§g{îf‹ˆõ}Ž dârL+“Zà¿”Û ³}Z÷à^êí@зȿUÉ·;ÌŒÑ_âéI5âC¶Yܳ‰èÿþ½DnIÐ#ÀÓJ&‡&‹(›+]"4+?GœLªõïÞÇ‚»þKù‡ÉŒygs×N³ân©Dnñ£z”^Û‰«¶¾;Ù g‡üiŒò ûD?FþÄQí%ÂZü7\.ˆHžÈ}¤ÃåÄR2©›mÀ´­C¸óFy7-^"7 ^„$ëòmU+rœZÊÈœU ØØ¬÷áj±ÕY}—§ÚˤZ~kˆ _ `d“¢Nde³¹Å•¯Xû㽤_Q¶Ï$“½j8ðªX4÷ÂV7v~iÈ}½Ý‹os¾{ÈG‡èÛw#Õò~§ÇŸW»1r÷fR"ËrwŒÈT¶L¶jbÁgG·bKÆÿ>”[BÉQnY} ©Öߘ׌ú‡1ríTB¿FrgW0Ì«!åó:TŒ©*“jëï v ñòHF¾¸mÆÐeñÜŸ8ªç•ߪúù4ä¦ê"J?{è%£Ÿ™w‘Lf7Y‘Ö2‘›mµ ÏY_FöjfGìŽ%\§²¿ L å3""7ÜHVÃ…?> —ÉKå¼X­ß#pUò7^Ž€y±Œ,ýÕ†]µ—rŽ6 ÐíA2yVkFçb®u¾ºOƒ$²yk/jöØ!›Gp-a#sŒÑmL¼DªåËkÞåˆ@Z뉰ì,ÄmùPÙ˜ô]*“›Â(·)‚;ÂìDQóDFn¶™PÚÍUë{ɈÕOæ2r@k#’§OåÖûaFŸ¿âer^ 'Îf™ÍU[?©š§37fäóòÊÈ— Ñ?qT÷VŽ Id§ˆ5YOrÎJÇiÿ8™¼úÙ‰ £¹¡&´©ÅÈC.F^©Èýu¤í.„ÈdšÑ†+ö¥Üʋǩ¼xÈ[ÊdR-ÝV¼ÈžÈÈÜ^3*|_Ì]ÔÝݸ%2¹ÊéDLñ‰Ü¯ÙÝè^·##×tOG‘cqÜQ¿XPæj‚LÖÌîAç‹\µ|Ë•¯ü’î½dŽ "&þî–Hä=dòà ò§åã)â–=A 77Уjd™TëÿØa@M£ùW}VDöå6,ëÅ—ã{%R*àÅð}ÇRmýµ):ú^Z&Õ3JwåŒBþÄQÝÑT‡Ù•rÉdÈuBÎTã6ú䯄"U¸ Ì‹×E‰<©¼X—MY¬!kÖÐaiþì2YEïBw©÷ê4/†èB%òV_#濞&“÷.lŸÚ“«–¿l=,“ÃYï·ÿ 榶WöLÖb2™¶Ì‹’“+KäÒ¬”÷O`䈚4;чû2̃Ý,wp9+ºï[ÊU=Ö„éÀŠ—Ie²¦w¼ò{X½m–D6ÜèEGëA ùf£ wŽ-•ÉO“<(S¢W­ÿÈt=î×êÀÈ}¿è‘ÍÑŒ»ûƒÚÏåÞ¸jÆàÙñ\µõF§#ì\¬Lfò ™µ÷UßKÞ¤‡ff[™ô­§)÷MÙ¼ ô­ïŒ2¢eí™2é[Ÿy°gßïé[o}Í1ßSô­«å¿¯‡us+FúÖ÷÷òâcËô­RŽWg—Hßz»­"}Û%Í5Öˆ¢õ§Ëä|à“¥â® \Ÿ«îCÚÜ»'íü² Vcäü|²ÆmŒÌèú#”ÿýÅÔô@2£÷ÿã2ÿþïü7vïgÈ>ÙEæ´çÞl¯Cu¹¤LNkl@»vùã¼X5i“D¦Å*/’É›2ó\+ÊîMbdÅfô[:œk]«œl³ý.YP Çp™TË.ºP}úFNjëÄ4¶€ûè¾–Ç~^òÇ ÷‚¿Iä×L{'“YtnŒx˸ë/{Q´ÿI/Yµƒ‰y0R-_Ü=6ìïÇÈåá:$³Ê\Ý#zž/“osYPãJ"·RNåmôc÷_ëV¬wÔõOÿýóe5c`p<#ë 7a}ä"nÕ?µð:s äá…: xZI&ÕÖŸ±ËZŸ£™%¯cî&rÕîÏÀ¨Î Å£Óµ5äÛZtsÝõ’k<&¬Ø'“™’­p.‰{û® úôcäÚ v<;±Œ»½¶ˆ7ËVäúx½B.Käá÷NôŸ"“u•a’×*jùç¹ÒÁ–Ç3roA;w,ç– 0 WðP™œºÐ ›c 7§Î‹ÙÅæHä®/>äÞ*#ã Ç1²i ·ÇΖHµ|I‘"r ='‘Ÿ´¨½ ,wý9Æ–M’Éš1ÌÚÃmׅ襓Ùë¬ Ú‹¹ª_•½F¬³E0ò[5£ò• åí6ãÇ‘¥2ùÊ߉"ë¹jëw)©Ç²ü-Yí±±¥Ò4äOÕEœœ+‘#ÂEļ?Ã]šŽ?OÉ…\hVb×8Ï„¼ b™¬Ãš·Õ¸E”_ÉáË¡2©Ùdð‰IÜ+[­Øy5‰‘‹•“ñç®É©–ÿ—áV\×'1r˜ÍŒcë–r«·±ã~ýå2Ùù„ý¦sýâF›ÒAŒ,=5û$pÃs[ðíj¢Lž¬æÁ¾5¸jùn*_ù×îxIr™‘µYPúÂJn~ƒCÎ'Ëä…¬®Ï]¾Íƒ;*0òE'ndæVÎoÀýÕcdrSN'î”YÄUË÷«/ÒW7!'*#³*A"KqaiX¨Ln­éÁž»Mþ6Y‡ÔMù"¯ˆW+M©Ö¿§É€WóC_Ö€vÇq3Õóàe&2éWJÂ¥Aë5¤Úú…‹‹¸²Ú)‘áë È´a†LþÄQ X&â^ÅL2Ù¡–õ¿poƹ1gFW®-Ћ<9~HdåWÖû¹Q {(Ç£ÖÝÖIäñÇt.¨ÓþÏÔü^4>˜›kðŠ8>(wÅn;VŸ[ÁUËCoBq #w”2bBÍ0®MtcÄ•Ž29{»ì$‘%®[qðF2#³M5âÁš(îe#S°s' Y´‡#;$0R-_Šò4ýõlv™\¡¼øF´äö\®<Íõ'$²@YeDÆýâ%¿ú™‘Ã(“3]8ùjW­Ç5zŒž>€‘ƒŠé‘7ª#÷P°î=ÿv¼ '7&sÕÖßFîN–Iÿ=.< Íý‰£*¾ÐaþÕÖ2é>«GæŠÃ¹ùòKX=6ŸDžáÅôOÓ²å*=ž “R ÞWXÉí™MB¿f ²üD7ª”êÉÈuÅ$4žqÄKiá@üžFªåߟ®G7¿!Œü=N‡åqë–pµÓs Ùûž[Ofd³:xz–çBÔââ„írÂ=>êÃÈF‹^÷xIm!:þ!‘_GèP{# ÎÔ¢æ’r©ÖŸ°âúÆFÎhÁàFk¹ò!7:ìéÉÝȆê1)\µõŸu¡ÌÅ)2Y¦¨„´‡ë%ò'ŽjÅ`Mòæfd®Z¼{}X ãâµøK?R"]Ê‹|ÜéÂ2ym GÏäK^ÜqÝ–ÈᯕaóDFγ¡ÂýnW9gk¬ä¶+¨C”:\µüÑS=h•7q5ndÙ1ŠûÇ`}[ä䆌Ð"ÈÛYC먇ôh"·âJŽol-“÷p£F$wÔh#ä:q\©ˆyâ¹jýïí0£Ù«dF.{o‚çâ îø¾¬j3‡;z³Ò‘¥©úÔ¶*o›,dÙSN<ÖD3ò'ŽjÇåZ¼š"ƒµhÑe£†¼1]GÈäÓ/F?µ„»¦…„o?ú äö÷nxÚ1²ÀQÊÍ‹äjr‹¨žëŽ@.¥C±Q‚LöhéÀ•Þ˹jù«vâvýhFú7u`îÒåÜYJŸ®uîH¤¨ÇêyeÒ1ÛºÁÜy/½¸ù—W"™¿÷òcä³7F¸—pUG¥«üš32g=õ’?¤n¢·¾¯’Éf=”EŽuÜm$œÒA"¯¥¹Ñ÷G?FªõídÂðljŒLRÞ~WëÅs?Ía¹‘O&KÏ0áÝÓe\µõ»¼³ üìFnŸ§ÇÛl#¹?qT›ŒÖâNÑKn_¡EÕ¦³%RzbAó1)2yä—¯æ®^™Žâ_V2rô3ÆŸ[Å=­Ó¢ùЉ¼ª¼x -œ(“{‡KØÜ/YC>­’Žj'’©–R¤Ûß­aä0ÙŠµ5×qCV˜Põ×å2yMyšGÞKácôe†ä×. FNQöàt¯HŽ?nĺ± 2©–ïàb-‚.EFjá7kœ—\ËŠ'–Ëä\— ó{…pÓú[°¤îZF~$âkæœ\µþç2ñÕÍÈ€ñ„½˜Ëý­¬ÍK'Éä^|;W˜«¶~Ø-fœ+=zŠÈ²$›LþÄQ}öH ‡÷˜DÎ/CýdòÝ'Š%GsµÅ<ذ²=·áKÚŸéÎÈÊÓùZ“ÜÐÁŠqwSd2çg'®.ŽäΔDìÈ]–‘q‡u6¼³Lªå_÷Ù KÉ5Œ”;˜PãV"÷~G'lwãd2w_ ´à®Hq`EÙDFÞÿaAϸî#eãRlåD™l¢ìÁVž^/jùòíÒ"9@+‘òZÕª•Éa.:GÕæfùêÅÙ¿,ùcˆéy Èd©Í,»º–«ÖÔe=zåŸÄÈà®z,»>˜›µž„%Ãòò:¾´œÉHµõËDQ½Àb™<¡l”Ö]ËýFÕ÷úQ@ψ:2é[ocô"1â‘DúÖ×¼×âD£ é[7¹½5õŽDúÖqàÀê%2é[WËÉa@Ë…Œô­ÏQŽoy¶¦H¤o}`š÷G‡1Ò·Þq#*ˆ›|T‡§«p×îP~®©rø"ª‰‘ÉÊy¬˜Ý'ëø¨ÇçAó¹¿Ív¢›c W-_Âf|î¿–‘c¿Ð3S<×SUÄ´ld2ïºïÂÝ~K‹[å>J䄽f×X/“jýY ÖwØÀH(ÇÝîönÐ~å¸ö{*÷Âv²ïÍU[ÿŒ(áð ½—œZÁ‹K'k0ò'Žj¯H-â{ºòI#-. âVØ/¢Ò­z29ã¡ûs¹•fHˆ X"‘ÕªKØ9í„@V:nÁ‘©Œ¬ÙA€Üã¹­‡kqR¿^ #ö0Ó+“jùOù¹1¶t(#óÄ9ñ¥Áîé’Z ºæõ’=[ˆè9®ˆL¾?èÀì\+¸“VzPûTk®É abÕÙRTŽ_MW2R-ßÁšzœÙ;š‘ÕÒEônU‡Û¦—YŸ-‘ÉíÕ-è2gÃߎW~®*¹7·Jx¶!«@ªõ¿­7áY›ÕŒ¬ð͈Ñ?’¸ÚÂZYñW ™µ§ó¤2©¶þÑ^T}²Š‘o«™QÇ»–ûGõ¶Ñ4µ€†Z0òµ²‘jpº~ ùGuÈ -jîZ-‘¿îÑ"òÜî_çұϴF&‡ltÁY8·és#ª$%1²Êi£ 6ä†ý0`¸_¼LÞ|bC…œiÜ×§”Ï»TFZZiñnÐtTË¿ª…ƒW¦1Òbƹ)ÜÒëíHš·A&÷ sáDæhn»ínìoÌÈBóÒѪg wôF j¦¤Êdçj^H³ªsÕòý Åü[~y»»_ŸçÊÊ>·ëQþô …œ0;sm”ä1sîDD°£¶Ä¡Åå]‚L÷PdÛP ©–¿Ìq­Ó-SÈ9é%f½ë$Otu¢Ã†‚lu+Sòna;\6ãqÇ%lóD’rgcë Sà·þŽBÎ*â„ÿ±á‚TËו„• ò‘)¶²ï;˜ äïËZ  KÏy¬ O(dÃ×ÉÈ|dŒ Õæ—û6õv 2AŸù#ÙõÓxîÙ Éë­4ÌúA!ÕúŸnÄÅZu%9m¨á BXµûaUË|4aDö ‚”YL°þÕ™µ]2¢yDÖÒÅ€/-/hÈ}Lx´jˆ$›ôŽEÆ/¡ì¦ÚNàü¶é3ûÕWȃcð6ÍIæ/àÄúþ#Xµü¥ï(¸Õ©—†l>JÁ•7ÙÍWäí¨ì貉­µWóÕW»ÈÜM¶W‘d®yVl<"È–GõHéùÉTË::‡{íäèÜ64Ðîf{ÔãR¿ÜöµËkÍWÈK…ã1*B}¤E^fgÕæï™eò쑂éˆÃ£"»Ù-³¤–8£!‡|u`Wõ5‚Të_l¯5í”dÊ'¶õmÉþÆUíÙÜûfÿOUAz?Í_ƒÓ³.÷ÛM¿½)¹ ‘w2ïSÈ™×¼gØŒ;%Y{¡Òôaûœw!{»"‚,ÑÛr¶`öüE¶Ú ³¡™ã`˜Áªþª¶¹0ývEA^ãÄÖ°>l³Ö&ô-ó/—70`æß{4ä¼T ÎÝ[-É‹lØ'’} X`J^ÍöžÑýåRV-ß”½VÜì´CmwY°$n;?%—dß&4Ë;Ž}ñÝ€kÏŠ³itqˆú²‹U›ïú‹EÍ"y¡q,ÒÌ eßÅšÎl‘§FlÛÜî_ªô/ì5¬kµd²DFf¶m!È߸ªu*°7$QC.¼¦Ç°í-Ø£F#Î>k&É;oÍÈÈŽº¥ Ôøþ ÙÿOï½Õ·ËÇ!ÛAjBMÈžyûá´½ìÅ4dï™sÂcï™çTý¨OÆ— ä@«úظSz<îYP!‹U6"ècIÎìçÀ¥­ÁlÇÒ.8 ¶®÷ÛºuÁ^rA¨3j† R-_åƒ&Œê4Zs;qî\ vÉ”¼9,É£ãðaÚ.vS/'ê=À=¥àÒä%Rmþ‘ùVŒ¸"ÈÈÃ1xÑw3»jiÆÍw‘ý¼OÇó†HR­çãvÌíºSÓÆYQhRûWõ€÷Û2¢CtSr™wežÞ/¢.ZÑ*~‡$ïg‰Ç*‘l“ûI¨½i‰ Iñ¨±›­wE§›jÈ/ 8t²$[Ïtba«¾l“ . éVYjù?í·cËÔ‚´>‹ÇÙî»Ù-?ÍH-$É×b±ìi8ûø¥‚À¿+*¤ì§ ß…G²e]¦Œì)È|Þ7÷œ£3JR-ßä´¯wVC>ôÆ=ùîG:' Sþ]’ œî@ƒ¤ lËŒÔ̼Y×[§ÞVmþ¨¢1(žv½ óO±@mûèCò-Ù-Éç'“°qu «Ö_;Öˆ}ë ò¦÷ uqö6ùWuj>Ý­Ãæ0¾Ù{vFãDø•ß.ɹ“¸v;¿c ¢"7²¨0âRϪ쬡1xÞ#X’KƒPúIÛzn,ªm äÑ}zÍÕù‘jùkeµ!MØnAVª‹¥ÝCÙ#yìX;0\’Á’Ð9p ²(÷×Îd¹vX»…³Rl­)Éei8+ædÕòÙ.èQ·]M…D€CJ=fã¿$ãѮђ|è=(u¨T„]á]éÄŠ_ýÈÝõÌXuoš$Õæï%Éò™¸†l‹!±ðÏņ˜4]Ë.ó~„þ6M{^A×·²ËÂ$컸V’ºJdÏPHCªåëÛZAçZ…$Y½ÄÞ¡ Yþ ‡_Uä«;É0U˜Ã¶Ú¯ H;òþs;êØv Rm~Àª$\+È7ˆvlbsVr"gó©l‘Åñˆ׳jý/Ô#—(äâ _Í)HµûaU—FÇ£Êl½ ß¶°¡ZÄ¿\FÁÙœ•XÛëd,œ3ë_fQPñ lÿœIxT|#›÷M¼§"6M.3.—Xȶnc@‹ô™Ù—Mh.fKR-ÿ£$%Ðȉg­&³ÅMI°_Ãõ~»6\Óˆ5‰È ý5d=¸ÐòAAº‡'âfóp6v© _gObÕò=ðzum'i-)Q¶Åf ¢q ë±A^­€Ú¡:¶`˜Ïâ—±¡ÃôxÓzC2©6a š7Ù.È4Ó±ón{v½Ÿò/cïFê‘/×F ©ÖÿŸ×ô.ºIž•qø¶RÇþÆU­x7î+Ñ‚l¾Ú ¿†l½Zd«±½ÿ#>éØ*›lÐ]Ö³kk[Ð%çZ¶w¼ÆGjÈ-% è¼þƒB{WúiÑ’ô«)±çþb ©–ÿÌ r k!Iÿq Ü?³³»¼+è*Ñ_!ÇåhYK§!suu B"È;ýl‘ gG¿Ô{Ï\òÇò8 \¦“¤Z¾¯=<š™O}³¹Ð¤FOÖ1ZiÙ”L1Xpg3»ÅlÆxÿå’,ûÃŽw±jóç:쨻[ÿ¼O@pH4[~‡û>—dÐÏTJʪõÿ¡3áÛÂi‚¼¸VMÙ'kÈ߸ªM[ÅàèÑ­‚<–Û‚)«Ù ±ØÛ"šýnAãnÁìä)Œ}›½4AÛ>¸Èá-pE­‘d¾.ñXHÏN®Ÿ€7›u‚Üæ}ÝxQ#/«–ÿt.‰»§£Òð—w\äÅÊ.8–wd©ÛÞŸeÛ‰Zm`>5@ë¨Å~Ѻ0fUÉ>t!ÿÚê‚TËw¿W2.NäØö¬Â>™e€Xí66-Á¶8ª`iÞ Ù µ{4¤ÚüuàÞ¡d÷€x\׳ãš'b\ÖIFßsáGó¬Zÿ“ ¨‘O’ެþk3ûWµËGÞ®ž'HÓ7ï›{ÝAlTnN¦ÂVlk@žV™Ù󓌸ö÷Ÿ’<|7™Ûíd'wIF¦=A,^(XUu¯BVÚfBÀŽ)’L—â@øÁͬZþþ$b>”Õþ÷üx„Í´7{éùÇ+ž¿ gWÖÃõ6ÒœÜÃSB6IÞNkçÞzAæ-bÀTÓ[ ©–¯º÷Ì_* ZówÙÐ㦞MY§G𪩲×L=––SÈÎmãp\/ÈNV#rGvgÕæw²aø-½ £¯Ç¡@™¹ÑOâq‡ÁrÆ®dœ _$HµþQÃн–N’¯›:±óÂ$ö7®ª¨`ÄØïMypš‘«ó³‰“ô\ŸACíÖøA!‡ÎO@±—ZIžõþˆÏ·/b žSpu÷E ©Ëš Û—å‚ÌÓLbþ¾¾òûêT=¢¤ZþÆ•¤™_Rñ~üìÆž=fBÐÑ¿Ùaí XR3 û}ºYI2mÁ8ôš¤e[×1!÷ülWv ÖîbÕòÕi‹¢›#y?C þ9³™ÝÑÍ€‡u³I2)« ÓkfÓ¸õsТç2ZQ§Z¨$Õæ.‡fhií‹É÷"ÙÐ6˜ŸèÙḚ̂æ\Àªõ®‘XÒl€BF6WÐû¯Â‚üVÕ÷êÿBÛ¹/hHߺþ¢­«IÒ·žzZÁп.+¤o½ügZy¤o}ÇF†×,.HߺZþ¨}É(9o¾ }뻽¯™:ÜlBúÖO”NDO[„$}ë1xæé¡¿š/ «·ÂÒ·4Û‚C/×Kòÿ™?) Y¦¯“ä¯Î_¹ÅŠÜã#é[O^¬Çã;5ä¯ö¤s`ÈÆ-‚üÕûÿíª~ðDÿ‹#úJŒ]»WCfº® ÿÙì‚ìóƈjG&J²Ê êÏ a߬ŠEßæ&Až™lÂËöãw¦f_ÎNö>hÇ÷NÑ]ºaËÝW’1}±ªµ–UËŸ¦¦÷ÛqB)IæjjÄÃ]=Ùc;â±ôYŒ o´°âí°höyx öMÜÅ®›kļBþì˜z¬ùV’¶ÇqØêåÙÑ‚–ú-’\¿ÔŠžg´ì×’6œ»Ã~ïcÇ·Fö­Ë{æ~÷ÅEiæÄÝå‹©6ÿËv¥û”drc_ƒë³)•c0/wÛø³†ýyXµþE‹ÄÃÓÆ*ÈæIftN^Ϫþýþÿ¯êΆ :§k È7£\ÈP­?[w,–ü0IòkÙô¯Þ§ÇÄ2û4d¯^ztKŽQÈ›ôpV½ËΚbFò®5’L> QïÞ!'y51 jF R-½µ&dÿ²H’KYûj;r¡ k-äÊWzdöÏž÷Ó#ÿ­r‡¿AI 9®¸‚½šJRú)xúºž Õòåx‡Ò»b$ùv`žY-ìÚ~Iè{h+û覛Þg;ˆÅŽfA.«cÀÙö¥Yµù»pðJ;A:;»šgÛÊûs¦/–¤Åv›½w$ÕúŸŒÒ#þÍ ¹t¸/n( ùW5hšo s9ip2º|ZÍêÀkO¸$³×sâu•Åì ïîú§™ìŠÞ•þl`ÓFذ¿…•í~? ;³ƒÖ‘eÏA¶›mÀÝ›µ%©–Ñ­Ì.)É´{c±ñ“‰µý£Ãw£¡)Ùê¤Ý­$¹ø¦ÎãÙ·Õ¬¸z+ŠZÁ†ÏbÙÿº+ïdaÕò9”DÜÊ%É}“P3˜-eSqna¶²N⩵‡B¶În@áÛ%y12‚M¬Ú|¿CN¤->Y?:±êÕ"¶Âáx4HŒ‘ä° 'Îì˜Åªõ•ËŒi·—Kr_Þd±²¿qUûÖN „rÐãDĽßÍ>èª`ô”Z’<½L"ëþM yö“Eªè%yò†ÊN`Ǧ•ái؉ó$2µŽÐ/'àáfIÖÑJÌ=Õ[!Õò×rص½U’{Ì hþÊÌ6oiBçâ³Ùýocg}${²S¢ÏXØL%“‘îý:¶àR#ü—f¯t±ãó3#«–/Ëa'Þüœ$É-xÖÔe'$Öï¨!ÝI n¥dÇɸ•!H’öHتéüHµù'v$£o¹•‚œõ1 ûwl`‘ìý=×Ê®?v!c‰V‚Tëß ) íêl’¤ö«‚ÌJ:ö7®jú v }md¡ è‘?†Íç”øö9“†¼~GÁà Ù9}¹ÄúL›ò]Cï¿ë|CC6«êDÃbA6ošÓ {6W^ ØÎÞØlBå1‹Yµüã6&bÿ÷(Iöý+ ÇVvSÛx<­ceß­t à†0öî2÷?•eË{û¾Éw>™¼9Eb|Y“B®Èîý9: R-ŸÒBâBçó é'%Å3Ùþ‰9Wä‘‘Þ긕-æ])Wø¶Ý0+ZÒ²jóo¶O‚¹Ë6A>ÛâÀËaìÖgv̪gc–ÅàÌ¡V­ÿØYçæGiÈåi¨u9P¿qU·íµÁÔÑ*ÈÐþq¨¸ÙÂî+èÂ_½F²‡%áL‡-l‰4NzÈþ8–€[Íì…ì÷ÛÁ°°CÖsZ‘3Jróäxä·²jùãÞ'ý"H’%{¹ÐªÐ@ö\v{F±+ü$j_½¢…6)¸’©´ ãë%Ãüs-ëw6×öìfWdÖÿÅ?R-ŸÕ$1²S+ ù9§ÄÚ_ØÇ#íøºÅ(ÈV5mèþ&†]ºN‚ž›2ùŠ%²·’¤Úü×¼¯§m"y¦x"BgëØ¿*PnU ÖÕZGÂR­Ë;ñø#È!C-(ü÷Vö?XUßËï“5ßëé[/=ÌŽîÑFAúÖç²¢D“(AúÖKÐö¾¡BúÖS»{})¤o]-Ý Æ¼­,IßúDÅ»‹ßû‘¾õ|£0 Ä"HßzîBl Ý$É_Í÷ý… l)Hßú¬ Vlþ)HßzŸæ6Lo•ä¯ÎékÇÄcFAúÖ;™±èé*IþjÿÞ×ÓÝs òWïÿ·«KC7þ/µÜØÞsŸ‹ªÕ\£!wd´àV¹’TË?£±åý'àÿmþ|ìëÞP’y$âey“ «w¶!G†D¶˜÷Íþà ;Û;‡IÃw²Ï†êq°U¶T#2n'Iµ|š0&ä^-É«É|¼›³×ŠÄfVvF¾xìáÂwÃvC7føiHµù‹„„õQ>ÁÞV`XT-Ø2J¥D6ê“ þ!ëYµþƒN;ЭJ” 5iâpÊÏþÆU½QPbTÿ’‚Ìã¯`xÁ¶lrÛTÕJ²rT¶T±³Ï¾°fòAžÔëPõR+ é¦CQY„}ÿR± kJ²ö÷dL-µ–íº]â-O5¤Zþ!ÍŒø4%@’îX^fXæ}bÁ÷Û‘‚ÜÛ͈km&°'š0ëØöާǤ7÷ýH9ÝŽï‹â$YéºÄðñ ©–¯a¡X|ÒÇJ²Ê6&×Mü—þ‰Ø0ÕÂÖ˜Œ2æÍlRŽ$Tûs· CrÇàX–hVm~¿¤4h!ÈciÌzЇ­qQ‡y›wkÈâ{ôÈ¿ºœ$ÕúëîÑã¯E‚¼àð>m“¦kÈ߸ª—æ»0té8A>ŸåÄ« eltI;îÅÚ$™kX®Ng'OÔcSîì"füS=˜ízÈ‚)v³1p}U»½h<.5NdJW=æ,KǪåo3È‚&ÝÃ%Y½‰ý—›Ù§…õØtCCê¶ë&s&…¬ó_¹ÿÉ.ɰm&”Û¿Š}4Ú‰‡³– ò½÷õ*LYµ|Ç‹Ûq&Ñ&ɬA̪e_q¡ùÓÁlÛ)y3¤g_žÑáÖÍP Ùª–Ï둤Úü– \Hš1NU_{>G°Ù:Ç Ý­$¯½¶Ã±ÄʪõÏiÖcÒ‹’¬6‚¥ëÂÙ߸ª†žÉȘm‹ ŸçKBÁ»Ù!e]ø{Ì,IV+*±d~ v“÷ÛµgU;[~™ÃzhÙJ+ˆ YÊxîý¶¬°[!³L2¡IÈ IŽ„k†pV-ͺq0ø%H2O“xœªžÈÖ{ªGšÑ5؈s&4¿°–ý⠤Ʊív”[ˈÐáÎå²²Üf3Ì[%©–¯ž-Ág6HrÕr `ÇÕt£û¾ .òû ‰ÅÇ#4ä–.ñ/(ÉûEôîÁªÍG_'~ _!Ènöd¼3o`gÔRñYI.©îÆ Æ/ýHµþË'`C»$׌÷þ-gãªæ’‰8?É$Èý½ìú!Ž=WÔ±•ÿTÈâÙÝHú{¦†¼SZ ì.ɼÞQ¿è‹|9RâVX&AN­ŒÇY·±óyãw‡hÈܹìð¿c¤ZþjÂŽól’¼_È$=;õ¦Š•=3"cw%°ÕœHì·œ]>MbCûtl޾Ihr*œÓÀëSóø‘jùŒ¯dyXI’».I\>âPÈEÉ ƒë òŽÞ‰?N²ÇSÎhÈä'‰˜=Ä(HµùM³$#¡nˆ oõLB…gálÇŸNä¹>-U2›jØYÕ˜÷+çìÆÝ ©ô“ødÎ&Èÿ`U}¯,÷âñv…]¾õÑ/Õ¯,Hßú÷BɹJ Ò·¾ÑdCß&‰‚ô­g®iÀ›ÌAúÖÕòwüš„7©!’ô­ç‹IBye§$}ëÓ+¸q¸h é[?zÑQ#ù«ùîÕwc픆.Ò·^ü©óý#é[¯óÉŒˆ­;ù«ó†8ÐüT´ }ëëLf¬Ý&È_í_âqšÍÞ.È_½ÿß® ÿýOþ7+íP°¬d_I¶*1-¶&{kn,šÕu ÒR‚°|Zöç(+V¥Ø%²ÎŽÁ ’Ùü¬(‘/#~õ¬®}*ŽTùd€Ûò5¤ZþÕ=˜¿t‰ = ȵ¬›:%1Ýì’œ²Ú‰ùŸÖ³íJ%âçDvÉs'¯b÷<6âŠvµ &êð~¸THµ|g–êѹE#Av]¦Ãátg5äã8=Íùr„Í"â5äg³Çt§]䙓Nti³FjóŸÙ¼‘µ\ä°8ïÿ~o±¹­²ÑŸJI28Þ‚CŸ¬Zÿ¿:ëQýHIælnÁ‚:Víþ_XÕÿHô—V’ýǺ1»€NaÛ‘ª[,HÐcÂø l¥œ¸ª_)ɩ܈zwQ!×^A“Èl÷è$Ì.£e ¼—ØÛ.{y|Þ-PXµ|Ƨ:ñÏ)ÉÐÊôùÒ‡3Æ€Ú cÓ^3¡oÑvGf= Y ±¯[1ò²ƒU›ÿ ‹ßÊîÑ?r»ñGžl³x7^¡ùîš ß>ϤZÿCÝhðÔª3nH<ŒÈ.È߸ªãW»ñbüt i÷¾.̸ÌÚzê±lP5IfgD‘ìAl ÷Áùæv¢ ‹}´âÏVIìí€8Œ3)ìŒÜ&XϬeë·ÕãË×JìÒF¬èµX’jùìСh…{ Ye¬ͶԖäž'/idñ˜ûQ²÷zÅ£ð·Ùî¹-bÍlL ÜaØ“Þ]£‡U$©–¯êM#ºg[-ɟ͘q"Š-ô(m dzù:ØðñŽdµ?“ÑýËVv“w¥Z6}í$ÕæÚ+qçF~AŽ%qor öÉ÷ŒÊïbÖ´`¦V˪>5ü“q8W¸ .³az7ûW5Ê*ñhUQAŽû¦Àíßš­ÖÑ‚F$oÅÆâî'»4Äǃe‚ÔÍÑÁ¬=¤!]ãuÐÌt±;ê°6"­$í£—Vv—÷Ûܸ¼…©–ÿäjVå#Ƀ_Œ ]öWbà)ddÀ#¬W²?¶Ð"]; ½÷é1|“†,½Ù{.·$ûÔXÿ²)«–oá +fm°K2ûË8Z,ÙN;v­Mb Ï’¿.’Íï÷FAæë‹®áɬÚü.ÓZ³–*ð?ù{å™õdNÖºE‡g·o*¤êWÝIR· ò¡N‡¦ÃŸkÈ߸ª_J*p|'È'E](vg9Ö.)’Ì™ê@Lw [h†9ìUȾ+ðÿ°T’'¿›à°ng=;lè×͆Ùñ.M‚ ÷î1`æüɬêYUo†²w—$ƒ–Ç`ë¥XÖœKäÜE›[‡„‰±MÉ/‹thÞç„BºëÑ·Ï"I6½¤àÁ,÷*u%«–oÖªä…"ɲù¨t!Žõßî­Á Ùàó/–g'4Õã“­¼ ß´Ñ£ñÓJ’T›¯K«àЀɂ<9Å…n}–°Ã»›Q©S˜$‹þŒÇ§6’UëcƒS_]UÈ®›MT6Kò?XUßkÞ»dô[½M¾õ'ûœèqw$}ëîX!_8$é[oÚ-·uá’ô­WÜ­ÇÂÞB’¾uµüa«b1s¨S’¾õÏuXõ!§$}ëÓÇ òH›$}ëE²è‘±f!Aþj¾á#“QëˆwþGßú÷uÞ×·6cÒ·ž{B,ÖýL–ä¯Î½èDþs«é[ªáÂÏ2Iþjÿ·Â†Ÿ¥$õþ»² þ/ûÕÁëûI^ÓYÑü¤‡m× ©+ö³½ö8°;‘]–àÀÛŸÿr¼EÁÕ4SØÂb>C{¬£µ®ías´J@ÒÄý‚Ü”C õÁªå/摽… »lP°kñ ¶Ã&ª_Ã6ÜæÀ·ƒÍêíR¦B2äDʫڂ,þS Ñú²BfúbE‡Ò©’TËwÔîÆºÔû²–I"ÿõ?¹*UbÐ$û|rcêã(…zV‹¬U,2ãý?³Œ™&Iµù/·º0°ÞI©¥ Oñ%lhI=gn)È}f-^gšªjý¿k1.Ë%…<ýÒˆ ý·IRíþ_XÕÆUãP uŸ$âmpë°5o;QÕ¶•íÔAâÃý^ìºìÝè¯ÃÜX^2 5›]h9h$£¿¸¡¯¶[!+·6 ¤Ë,INˆ‡3ܬZþÂE\˜©ß$ȽC’1½´ûÐ?Ëörƒ\ýæ`GpbÒÀ0öGc;:ÝË^|èBdŽå’|crc­ç¹†TËW¹´Cí@Aæ«äDéKìöztlðÞ\4Áûí³,‡ o´!)ô€$¯5UpâÏŬÚü*.õ÷L”äÖ¦WËôc›M7¢nð¶Ïå8LùºŸUëÿ(<)Ñû%9Ì“„ißcØß¸ªýD,söKòNª«þHeKqãq£s yÚσùêø‘Õ¬NÌè°]u>$`Áµ½ì¢n\ìœíð2W»G²yjx`v—RÈýñI¨:Ô*Hµüóÿv`áê$A~ °£ÀŒ=ì†FFì¹µ†Ý¤CJ—âlß+Ž­ó°õôF䘽‰­2ÓŽÚ]÷°# ¨[4UË7é¢s' rŸ÷iÒöÚ6²‚ÇE&±w:q«d»4—=ŒK5äÕŸIXœl¤Úül‰¿§ü!Éyܧ+³wb’1-»–-¾Ãý{¿(¤Zÿê—$NOl$É€JŒÞÙDCþÆUýÒÔêY’<°; íÞXÙÁ[ÜxÛþ»†ñHAÓb£ƒži\lº¥„§_Ìæ«{áØøÎfdì«gW§3aÂ6¨€?ÚöÌ2-¢~Æ7%ÓaÀ‡<’,2N‡³Áùyw½O'½s‘êéÐaYIúíŠÅÓ¿ö²jùzDÛð0ò€ “~ZQ°i*[º¸Sœl€!wÿ<ÀÚgÇ Tl2»­šSÓjó¯vcÍ \’ìçFô¤Ç 9r±õOdd¯‰É¢gÕú·m¢ ãÈÅ‚|Ÿ;–4)ìo\UóÛdä,)ÉÉ•]˜wl#{¡÷7… 2¦¤wãœì²}ZœÞ¡!Ítð{úQ!3íÒ!(gyAZµH‘s²Ìá8|¾·_’ý/K´«ÔˆUË.¯³JAV/jA›•ùdÀû+$9÷®ÓŽÙÙ\­ôh^²Û¹šÆZÖ¯Ÿ‚©Å°§¹ÑjK6Aªå7ß‚Ž7m‚ì¬1aF³lPÅX48˜Ê.¾fÆò ÛjŒdwI^퇽Yö³jó_€=È19á†Ƨ°]Ö1¬ÌFI–(‹JŽT¶ËZŸZÊê[QòV {·«‚ñ} rHA+N—¬Zþ7LèT,T\¼>Ä>[cCÇ$ù³•!Ç“ÙV3ãpkÐ~vmNËÅæ<™€-÷ ²E¾ÁªåkÕË€ü‡çòýMÞÔg/æ2âÔ°Uìþ/:dºÚ”-ÒÇ…µ%6H²nC²§?íGªÍO¨íÁUÙr±Æƒ¬å;É"»µ˜ÝICÎð~d_×ë IÕw•Z´x·ÄÜ>TÌ£ºIò?XUßkÝ+‰'SëHÒ·.æ˜ñ*—Q¾õ2cqc‚[’¾õ±˜ÛÒ%IßúwØþ÷~¤o]-ûIz$•í)Hßúù.¬è¼A’¾õ*‡\XUn¥$}뚉:t[U@’¿šÏó\ mÀ! é[:¤E&m„†ô­; 8±K¿:ÿlc–vŽò#}ëÁSb€ùÉ’üÕþ–«¤ßl—ä¯Þÿo×ô‹ÿýOþ7÷ÐâÂìŲÌËhôãGöó³Á’û” ³¬ŽÁëAûØ9'Ý8R¤œ$¯ðà•§¢†ÜT?º>{$ÙÓûzÒ¿Ú1vöT;.6:&ȯÝõhÚu2«–äj;t÷JrH´úÓ©lö®Z³Ñ}²iÑhî`…ìfÇVýQAÆ_µÂ±í»¯§ ÛËyO½ÿãŸñ ÝG°jù6± Ïb—$?¶"µÇ6{Ò~«"È+9´x4FC.™äAÓŸÇR;ß{0ú1AjókuŠG߯§$Y£V>µ=Éÿ3†¢Ç¹«´ï&­`Õúß-®ÅÀ6k2*À€6k%©vÿ/¬êÊÏѨ¹.·B^®¯…c™ÍÚÔ„?ô‚<¶ZRUç°áë%ö°“’0£˜“ÍòØ…'žÍ’L¿Ðƒá•Ý ™äÔaZÚž’éïN°jùWìHÆ ?›$»Ðvs(µC‡°Ø3Èyx7[Ýæ5n— ŸVÔafŽ:lã n¤v¯Ï,”€ñãO²jù–̳a¼ß)I6°câ‡cì¹ìZØoPÈ\Y½¯“å«HòU¥,íqR_0ëöVm~X.;Æ 9.ÉéÏíx³ôëy¾Íîø‘÷réñ8×Iªõ¯yÉŠJ«ŽHò­÷`uªÐ>ö7®j-þ•I’¯šè'KCö¡ÔböÑ<‚léý‘þNÕ<5ц®õO rVK3.4‰g E<¸Šllï N<¶D±{àéé'…ôŸêœ5;©–ØC¦JòKy7ÚUéÌþ,fE«ÞÙœ_âq|ËI¶ùóht_â$«´ÔaáðÆ’,1YÏA3©{¡…_½2’TËWí¬ç )’<ÓØ‰´·Mìë×zøgZÊ:ÃLkf÷¿ŽÆÛq.rÞg#Ö5‰–¤Ú|ãÁD|vd“P2fØÃ~Úƒb[÷±¯'âC§¬ZÿôÍ%ò%É:§=˜tø˜‹ü«zõ‡•ÿ"ÉŸ/ôÈx2ˆ=×\‹×• Y%T‡€]$ù8ÉkDAƽˆÆ×’]üÈJ¹P~ÖIAžydÆÏIl³¹Ô™-ÙÞ•™ð(ØIªåo˜Ûƒ!«rJrðnïÊïݤµc“Ðd S’‚\ˆ]Éæ»oÄ5¿(¶~±XÔZ|”}“1õjŸ`÷lJföÒaGÁFo6!ª“™­¨õ~BºòÛ«h˜cw¹È{n ŽõôH2ØàBý…!¬Zþ^‡- t’S-Þoïuru^ È’ÜpÞƒûû‘7®Û±iÏI†eñTÊîfS¼÷=4æ䣡v(㎱jùÖ”ðàÐõ,’ì-=8¨tUȳÿØñ¡ÍIÖí Ë‘vJ&”¥yYÒß¿{YÕ³ê§d<Ü#ÉYqNÜž¬e7]U𣠻²cøû#¬êY|L¦|;!È“(_:‚ý«¿ÆŒüí’,þÙݵ¶ÅÊXLytŒíÒ!…O²åvÇ¡ú€¹êïëÌj»©‡Ù&³³Ç`߀T¶V4»3ò|ÁœðžIµüÙ*z°ÿHFA¾jå†ùKv•Ç {b%¶Ü'NÝÄžÙæFõžu%9ÌÛ÷Ú_Õš’YZ0âç*A>™«E‘ÕoR-_ÀQÞÕÌ©!«·õàè³7¬«¦‚àá$™­‹÷w·b›m1cP½DAZj{Ï´ktRmþä!.Œ]*É6™”¿·‘½lÁ¬’nA\¨CþJmYµþþ³µ(7õ•†<8C‹|SŸ+ä°ª¾WÉ™V,úzH’¾õ /øà‘¤o=¡ Däò…’ô­éd¬Ç$é[o·E‡§Å: Ò·®–?8·Äµ? Ò·~ýž…ë§Ò·>`©½K7¤oý‹´¢Nù#’üÕ|Óº1¸M{AúÖ3¬ñ~$lV…ô­EêÑüÖ|IþêüÚ»½õ+$é[šO‹¿ÛÏUÈ_íß`¥…‡KòWïÿ·kÖâü_~nÄe&A>¯a@ǬÛÙŸýSPáÌ…,94òçx YÙ{¦};“ CfGcnÿP…,é‚+¿^;’Ð7ýe¶ƒG¢Óö@öî+&å»Àªåo}OyM6K2~ƒÝ“,ìçænè:Lä‡ÕFƳECìpG_dbvœË¬ìŠÕ6ôx{…mÑÎ{&KšÈªå;7)©ÖiÈ)­£±ëÏí.Òí}0W¨ç䕉Ч=Ë~¦ ]íPöûù8Üt…U›ß∕äSm jN±_ ›PâI<ûw¯xxî_aÕúO¯š‚#†½ 9,ÖƒÙY Ríþ_XÕð¢zTê·Tké ÿ̦tö $O}vT#‰Â¥×±9¡GÉf+$±Þ‚¶I‡Ù\° æœƒ‚ ÷>XÚ+“Ø–[£qwuІÌlÄÅ"IµüÌ6ãêØTI^ƒ4ûO²3 %bÝÝs‚Üš!!Ÿ.±Ã_xöŽ’,´ Vmw‘•ý<¸?º‰ ÛÎK@À‚ˬZ¾Â±ÑhØáŠBÖ6kq|Z I®¸`ÃÍtW™§œvžaK×!gº@vïÊhŒ(jSHµùó–[1¨û9I¾‹vS/²µ‡»p§£‰]’'‰e®+¤ZÿÅ\h}&F£ïÅ£JÀö7®êÒ ZÊRWw®z9Ò°—Ú:q¯p»P&¢[Ñ3ìžÊ ¨“ëŠ$·NqBß'‘=Ð"§²û‘Åêÿfˆ$WWOƤ{ØçóS»q˜©–¿ÑþX$ ¾$ÉYâ¡ü¼ÂÞj`†_~ ÛxÏÌãO¬d÷Oö 6]U¶þ^ˆd‡6Õcî­åìäÞ3™=Ÿ$Õòݬ©GåOË$¹ ’÷#8BÏu˜ àäÖ züè°‰õT0£ÿb·$ÿìá@šâ'YÕoÕÞqè³ã²$4·!Ó¡+¬u‘/ëUä•çÉ8½ÓŪõïÚߌ*}R™å‘³ïteãª6ž²SwhÈ7m£1§ÃQ?rúj§¿*ÈQ™¬øZì ›å™Zu’äPoßÑiÚ7%?¤˜‘Á¾W’Κ (“ñ ;(W™òâ.îçÜŪå¿ÕÁŽñÁ%¹džµjž`Ï<ŒÆý“9¿]4úßs‘Ù÷&"ÛÏÓ‚Lw<§]bC2Ä¢R¥ ’ÔÄ+èP«–¯÷B3‚ÃR%¹¬”=VŸa_'j±·BkAî] ëm­†Ì9σµa%¹¥°YF¶¤ÚüzÕâñÏÇ+’´HÀϪÿÒMÒ¾,Ⱦ .¹›Uë?ª£÷÷X&“BöʦGÿ¢A’ü«zðÏhèJ\v‘¶9Ñx{%L!ÓÍ3¡`ñ$A]£ÇºòëØ缯#G»³Ûþt¢\ž$¶vCÊ®³H2ÔâAä·‚l'Ñ—‘½Ý q/³jù‡­MÆ‘‚IžšèÂF‡‘mµV ³½)›{…–áì"aBÈ» ›VÔaDØ ¶_Át|IC6ú’ˆtoN R-_ÕGqX1íŠ$ƒ] ÈþóÛ£³÷é•%ŸB.(­E©†E%™l"ŸdáÞ&È%‰¬Úü3ßðcÈ%IιoGÖðólïG¢Ø×h y4L‹žY Iµþk[Æ LÑ’<þÁŽÙþçÙ߸ª_nFÔV’Õ¦iZ};ݹx}AÎáµ&hHÛ‡x˜{^䢡›uˆ­Ø mC²`¤‚ÇÍB2ÙçFI2°ƒ§ÚM¤Zþ1ù%ÆîÚ É…“ÜŸ}4›þ”;úe+µáCÚ«ìŠ®Ñø¢-¥!eA-ž”dýïœi ä‚qÞo­ËR-_Èl6;!ɯˆÐ9Ù™t(^g1ûx‰+sXØÐ!Ѱ¥ У·êë$©6_{(ýd{~M޳ßw˜µt;µ®²—Xµþý, noÞ&É©ÍS°å†A!ÿƒUõ½*xÏŒîwC$é[Ÿ?&7œKÒ·þª”ç,¤o=>w"´ÿœ¤o=ñe,j¼$HߺZþ1³=xPI’¾õ…‡7æ˜$}ëÎî\î½S’¾õÆÛ,Ð>=,É_Í·»•‚JyÃ%é[I± ÝÇ#’ô­ÛpÜû‘"uþˆ…I(qP’¾õÞ6´Ü&É_ía¥%”ä¯ÞÿoW®6©ø¿,Z-æ*·YkAʹÏîªoÀÈ¡&I¾[mÆ3y’óÀ…Y“ØÍ…SÐhJ#6ßé(¼}²GC&w‹ÆÍØ¢’:À†ð‚Ù lýØUËÑY‹?S rs¯hœÉVœ}“Á¿±‰’̃9í¯³³Ãm¸Uï¡ ;l5#!ç)våÞ(ÌžcSHû *~¸ Iµ|#KÚá?åž Ö¶Áôé;¨¨Û«’ddU‰ìyv³¡·$¯Û,È=Cã_ä!«6¿³]‡±Yƒ%™­—kV‡²S÷J,ŒÛÊvn’ŠÓŠ*¤Zÿ ƒbQ+î® u5 ø3›‰U»ÿVuéqvi ²rñ8ü˜sŸí6"Çöߕ䷉ Èyÿ>»£i*&”Ê«!»gñ`ä¶™‚œqÕ€g$iʇïãî³®Un´¿³L…ÆÇbáÕ»¬Zþðä(,ì­!ŸÏBçŠ÷š’g^Æc}øINÉœŒÏªõ¿ø3 gÞjÈ<¥£1òN&IþÆU-2ÙŠ¹ko ²ý Bƒ.±ÍþJÂÑWç%yý¢ ëªÙÙ Þ3ׯ›‡9c@<Ê–xÈ6¸˜„~[ÎH2Ÿ÷õË5b•;GÒòXC.2IR-~]õ^¡ßkGCìÈ)É·$î gcj¤@iP‡ ’Q¨iTÈÕÕtÐ9—Jòg'rõ<$ÈÅ]ÌØ}ê8«–/Ov#v'& ²Ís²4ßÂnn™Š ¹–û‘ºèšd¤ç‰góÚ%y°žÁ~÷Xµù5þ6¢ÿ—$—·0¡Hë}láÅV=uK;ëóëfV­¿a±㬒Ã.Iµ|[jaª4HcJEc}h&vÄ+7^OšËºF*kao|pã[9’,x$ƒ?תßêÌx÷è¨$ß½4£¿á °< ­+gÖÿµ4pš$Õú×W’ ûž•伫n\µý«ª›iÀì"VAnk®‡»òNöIóTÔÙë"ßOA•Êg4dËè(¼i½”ì¿Ó“dì¥dìŸDGëÙ0æë6w'~<Èf~dÄÙ°V-ÿ͇zœ¼¯•äB—ZÅ͆x5c« Ÿé“ñ¡Ä16ò²y _–ä¼ö ‘ìÙ¥L~~…mp9'›ÜTHµ|uÖF!ñ~U ¹rs8;+äƒjÉÐ9%ÈŒsíÈ<õ.;cv2¾EgÍç-xr‰U›_á´ÊüK’L—ƒµC¯³íXPbávÁˆDlls‹U럮u*®Æ×v‘ã›x°êÒxAþÆUPK‡¹– ÒÓV‹íõüÙA=xÓx([û«‚À¬Ñì±òœŸm”ä?­8k¸ÉžØcĉìAÎT¢1¬’†­…E¦+dǾ1Ø2÷ª$Õò—>hF®¹§%iûDÞ`·îNÀÔe÷Yv°ÏÛßbGŒJA5SII¦6NÅÅ%4äÈ {ÂY³µ÷õaÌ]V-_ùјZ/·$ŽÔbOÚ‘¬Ü‡5½²ÞY ®ºÄ–_˧«²]>EcB¹Ž’T›¿&ÒŠ^]nKÒ Só{lå7o›ob·7KÅáNO]¤ZÿOý“áˆ:!ÈZsâp½üö?XUßëËêhXêÔ¤oý†3¶%Gé[ŸQ1ýnKÒ·>Ý»‚ùîÌPHßúÖ*ÅËî–¤o]-ÖNq¸rñ¾$}ëe¶Q£¸¤o=Ï`L!#é[ ŽF±{µù«ù×c 6D’¾õs£ß%HßúÊ'1pt½!É_??0-ë=¤o½WFN×ü[¿ÚßÕˆâщ‚üÕûÿíÊ]vþ/{L…k{]I‹HEþé_²IÆ=Ø–0†õ.5ÏoÖÕüãpøð[Iž„£ï±Ý“ðÔuWJ`,veyÃÖÙéćþgÙ›/X-N³jùE§âCÇ÷²ÀµÜlØJA?ÑÿõSIž¿êB¥ØbŸmXÿí KxÌú뻽ƒ‰O_³—nêpîŒUË—µN<ž¾z'ɱ51Îï%»úp"´×ž òùEÆþóŽ½ø- û·ÞbSÓYp»ÎVmþì&ރƨL‚õ-Ý[žÖŲDÃÓ¹“ =7#±½±E!ÕúÏóîÁnÛf…ìø9Âý©vÿ/¬jÀçTäû{B¶,½]Op‘Æý)ð[Ò^Ö»á=”]7 ÅEJ2x*òÆ}Uȳ5 X_O òýÊ(ì»]‘=¦Ä±§7\dÙfîqC’jù#’<È(Hg 7†þÉ>ÞêÁͳË$y•Š&Oo(dç-:˜ïE ²×ð(8¯f»½ŽDjù} ¹î®ûÞ–¤êŸ"K2ü[Ü’ä_:În?Ħ{ƒœËž r”Ÿ ·³Ÿc¿ë£à÷¬6[6_4j¦é*Iµù¯NDâúõ²u\$²(UYS =nìµHò_,þnûšUë¿ü¡ þÉû©4J€ÈùŽý«:&ÿ8 —×/¤bbl +æ»oØAö?ì@P–Gl¦c©è¼ÿ˜†ô¾Ê vÑvAºDB—¨÷&èè}Г“r(p}ØË¦ñ~:OÍÞ¢!ÕòïY¢ {u· _-uâ¯2çØCi÷À/t¦†|9,5–d€#}n´WÈK4òf˜.É­?(¹ò$Û±ä|˜dv‘jùF¿hYÛ É9=ï·„ý'¿[¹Ãѧ±îŸWz$É„WÉÈÝñ2«6id$žTmàG¦DGb•)ÂEŽëœ w‡’L—'m;Ì`Õú_+oFøõ«‚ ˨E×êìo\ÕÇ#R‘j*(È¥ÞoÏÌkÚ³ñ¨ñŽ´ÅŠO_²‹&£ÑÞ›lx)^Yß±[+ÅàõûG’ ª“ˆ«Õ_²y_:P¥ÉCAܤÇþ« ¬ZþÌý’0óÈ=AfÉœˆhå%Û%ŸD扬ÿœ$„½»ËF½4 :ø€$_z¬¨vè%»¢¨‚>}÷ rrù,¸ÿˆUËW³t*úÔ’d±§©(X;J!;WŒBëÙß4äÜÝ‘0ºn%‘_–¦âs,’,Y0sêL¤Úü×Þà ¡2Ó•Hh³G³ÉÞ¯¨¸¦e5äƒp7þŒß)HµþS¼üƒkú‘1æhl?5M’¿qUß}ð í©9‚¼ýÍ£7³ñ&”nz‰­ÓWÿÞqì†Î&ì’çØAeµ°_YÊNë"Q8{¼$ï¥"<: ;«x~æ}«ÿ+wTUßõ PBPTlÅÀ@•³÷×îîîîîBD± ¤ãÐÝÍÙ›>´b ØŠÝ­¨ïyfžõ»®9Ï̽_ævÿó™aÍ^ëËaãŒwäO¯R*ÿ¿xxÄ~äÈ‚>±°?òh9ï>2{ߌD¤*fz³$LÜñF 5ž¦ãr|óôàœ77àÈøê Ø$» ¤T¾9ª©ú;–Ù’÷…,Œ¾-#[t €Cç_ ²IT l[mÈ£vIÐÓ|Í‘#bÃP»ôSj~¥~Rl(HŒ Àà CløNõŒÜí%G/‰ÄäÚUL©þÆÇÂáwûš@¦ǦœÌ¿¸ªÇo (ÚÆ‘éçИ%0ûæâvÅ&T飿dä¤44X4ÙÔ3ãºäš,œH{/#7E ¸<3’#­û©¾õ2¼O¶ôèÀ‘RùWÞˆBѨ—ùé\Þ™ßeÎ3Åë2³Cx wmfF¶ÉÄÛÍÒÎ0}›CAVŽÇÁùR©‘ ×£;˜’¯ù™˜¥9’#ó†f`X×SÌWMBÐÆ1R 7 Ç«ÁW™·^ùÃüV†ŒLX„EEÇRò®é€k£,òPuú·Éì¹; ôOqd®êj>}­¢)ÕßnF:z½-ÈÍñ™Ý2Žù_¬ªúÕ%%ù¥©^VEVtðN#ÕëÉçC`û+A ÕëÅ1I0üŠ#Õë§$bÄŽ÷©^—Êÿgk®+áHõú¬ x8ùÉÈÿ“oz.„5ãHõú7E&–>Í‘5Í5RÀIñ©^Ïy…Î/R½Þæx 7/5·*O:,Hõúé¡A8ÂÈšö7ÕÏF›“edMÏÿ×ñ§yþ“Ž%!°¹D #ú‡Açémæ¬k1ØUªÁ“½Ò#à8òGnŠôÃîwÒÉ’è ôÛ-‹ü“Á ÿÆ<,BÈg6h ½È2ŽŒÌòC…÷ )•ßûh0Þ¤¥ d›õ¡èÖé*S)à|&‡ùÖ4 yÏv1gš¨î‰®q¤©I|Ïüf^ÊB_ëŸùù·Ý K˜Rùî4HÀÇ=š<9µ:¾4˜uùá¶w¬‚Ô3@×ßSR»ú®Nvùj|2\K¿3¥æú…-?~räÕëÙñ€¯Ìç/Òá]!cïfá–åp¦T_o\ÓM ¯Á 1Ÿ)u~ VuåÅpX$=È›"a–ð‰y_?c/rä;Ý ÜõóafžÅÀåy`t vtÕÉ ßl<ìS­ •‡³Ñø]Žl44?;¾Èc¹ì*S*å‰p\ˆz*6×#áwé óý“l\ÕQo+²Ñ…WÈHï3ÑÐïñ›#5χâry9ómÓ ¿ioKŽèª@tE9GJåþ=c}åÈ=íÂQÔï1óþo°fþicŠÔ£e̺MÃqóî#Ž,›á…ûL™RóÓëE¢Çœy»MŽu~ż{1?{ÈHë5"öKæH©þf c°Lü#·¤@~û#ó/®j¿NѨ“Y-ߞŠÞ} ‘´¨òGëìáù²ØÚíÊȧ“aðM ñP@ÜÝ æ´E"æuNáH»!Éè¦ñƒùÉ 7çÛÈÈî7Sñãà Ž”ÊïÄÇ`Ls ‘ NŒÃ£«šÌ£o2qùÕŽŒY$@öZÉÒ4qí2÷_÷ƒî,où 0Ž}áHn?š¬lÏ”Ê×Bõ`”œXÈ‘KƒðåcóKX–Ý}+Zn1XpLC$‡@jùøY$~X}eJÍ?Æ…ã|Ã'8/Lõ ~‡éÚ5 »üf¦–GÀ÷ó;¦TÌÈÀÑÁÙè~6ÖæÅ(È¿¸ªE1qؤú“’=·' Ðæ]Šü°Õû°‚ôÐ_† ä·_YˆžÃ3ßæ ãH{9¬v4ò{Us¤ŒN½Eæž‚PœÕ¨`~ˆöÃPÚ R*Že"*5DÒÆ5âÞoùÓ;»ž~àÈuVñ0º¤É“u,üqÜWS Wéa½Óöd ¶uvf~˜‡19š")•ïHX¦oØÅ‘û6øcЇÖÌ÷á p4ÐI=¿h_û ç:§agýgÌ=|6£:2¥æk E×qå¹YVB1óÜ@¸6wb†øAsž–-)Ù¿i6FìëÅ‘!}d›2ÿ⪦÷KƒƿòýûdôHøÂLÓ B¸™³@+Ú_dö¹›‰˜ÈƒZ™Ž[Ãn0ï,òGÞùfÌMµüñ|õ-u*vÂSÌ NÇä]·˜Rùýž¤â{û9R_ÌY×™O“"1Âü GN¬Áx½R¦Ãç0¸>È€.±¨j¡)’¿Že`×*låš «ÑZ)•ï™ÒÖ9ûdä‹?hm6Psl¸5¤\ oqˆšÊ4¶Ï‰· 8²bQ ž ùÄ”š/~†îä,ŽôŒ‰}“˜+úbé¤yxNž^xÍ”ê?àt"üêið¤I×HT6ûÈ‘qUOz¤bÌ´W¹Á*­ÚÜcÆFàˆÁ;fÿ’4ÍÑIÔMć žlY 'Ï/iž„’E‘Ó+£Ý?3‹›ä`C÷[rÝÉ4ü¸ü˜#¥ò¯S=sÜpò\`b—É™1í1ÃúG¢Ž?,ê=‘ÓB“ñ½ó7,ž/à q>3®v*æ-|Ç‘:aèéx—)•o¢µ?Vwª-‡úàJìæ§íYÐ?¹˜^Q6—¤yóLüö’# “U¹Ãú2¥æç;!tmG>øˆý¿½˜Ç£1¬ð@6©/â ¦TÿÞS‚ +äH±Ò\ßù_¬ªú‘ªÀl®L ÕëCôa­zfú_ÕëUgƒÑZ–Αêõw×Q5á@ª×Wn ƒþÕ;©^—ÊŸ]; U?÷ ¤zÝ'ßß/Ù)HõúÏ¥Y˜Þl…@ª×'(ü¼a¼Œ¬i¾äëhÆC ÕëWMsЩW™-©^·ª€ ޲¦óÌÄÓ/§9R½^™=Ÿ*ÈšöOIòÇõ}²¦çÿë¨k›‹ÿ¤Æ#?ü:#µûÁź5sif"ÖÞÑIï縉5ô—@nšŠ"ñ)³tU æÏ«#’â`ÕÕùûu( ë+™×ªÐuv G–gùÁÄnŒ@JåŸíŠ>/8²õñ`˜_aÚ%a×r=žŒšƒÜþu˜ÅC‘W?’#ƒ¬ý0ùŽ)³2#FšÙq&Æ|ŒàH©|­\2àõAȘcY7Å™Ù9X5¢/G&ÎÎBå=7f· Ap^ž-N-ã /’Ró¿Oá´ÏyA+ ƒkýbÞü ]7}‘,ß—ÿ'ïRª¿CŒ_!“âC{Ÿ)¹ÿÿ«ºïªV%2R8/GïY]˜MT/ów–ÜÈñ31{osÅ7Õ3mxm‘ÜrRD³ rU·\t·4R©ÞÙ°p]É‘{ÑØ¿CO$5Â3 í¡H©ü¹i˜ú&‘#÷@ƒ NÌaöþ3sÏ€ DÌ`n ‘Ãý‘½‚¬—í3÷ÒæVºçëód¨m òFp¤T>Ë?Ù88f¬@Þ«ÊöÝk ò—§ŸÝ¥iØ9o~d:ô±ÊêŠ@Nk›‹w—*H©ù†õRÑ7^“'¦àüêÚLŸÁÙhÈÛ ä㎹°v!#¥ú§UÆÀøZ‘Ô:žŠOfÌ¿¸ªëŽÉQ·ßÁt29NŽ—Ï]䚯Ùè2^ ]LrÑåûIyôY6> ™"Ÿ{å"Σܖ¼o-à#·Êa²¯OžôÈÁóu8²ÑídL_›'¥ò×þí‡_׿q¤^;? ²hÈüQÏ=§.fÞv—cÅ9Ô"‡‡ÞHW›(Ø…j‹¤¦®Zª%qáXéVÍ”Êw¿_.,Öx§‘3?ç`óáéÞ'·çêðäÝZñè¤ÔgZ„gÂxo(GºÄ&Âø½OJÍßÔ0¶:<¹¢^Œ×ë1ÛžËDþúpŽ”‚µxRªñ¡L,_!÷kçâûÈùWµB‚µ2ü¼ÖO³¿i.~Äœ‘s¿g£_Ëñ9¡g6¾[Ú3† ¸%»ËÜ‘š<Ù"&#;&rdò†`, ¼ÄÜž-Gÿ!q R*ÿ¢`9,ÝídäÕW®ÕíÓé¤Y=? ÑHÓÙØ—íÊÜô=SsêˆäÂUéHô}//+ÒPyî sû‹´ä/+H©|þªg4k³!Ù)1 oÍÏ0.FÁä±O¿† /?säëPt¬bîú,GѦO2RjþDNªÃ“Ž¿â±p¯>óÐã(Tê2g,±kY)Õ_Ñ.kräæ"Þœ¼Êü‹«š1Þ?®lH¾O†83—]ÎDŠmG¶Íp·Û æÊ9Ipú¨Ë“C‹¢ ¼¥ÃR 7 ‚Ò?‡9ãw*æ·ûÅ‘öy‰8~]'u~£ƒÑ Ž\=Ì661Û§aÚ/¥@›G!Â^[$T –S«lÉ{Òáãð#¥òÿÚéÞ£ö 䉫¨Nðgî׎‡¡R_$MâRP'[‹©Õ$—{•‘??e!nÐQŽlšWr¦ç‡%;[¤T¾YŠ4¼Ûó•#“²’Qd­Í“åž~àVàÈûþrxÅn”‘ß.¤áîã/ùÒ6M6öcJÍßx?'oÔáÉÎ7£±#@Ù8)…—î $¯Zá‚Sú")Õ_»iVù+8Rë ~ËÈÿbUÕ-‚±Ûçº@ª×³fÅÂÓçIõzÉ9.f”õ#ÕëcIX9^W$Õë÷†áªùGŽT¯Kåµ'ͬrR½®HàvC Õë2ÕëbÿÛ©^¿¸.ë®iˆdMóí÷L@ ‰>Oª×W¨Þ ¦ÎqQêõûYÙp¹7Ÿ#k:?¼< …ïtxR½®½&•gÞ dMûïå­ÚM²¦çÿëh$ÿÉzޱXuŘ'­Ý£Ñ쑳²[°ê™ãG.üêý˜TϽä«2ûp.7ZfKÚ—‡ÜƒMeäÕGnçÅ‘Ë4âà\×D$ƒÞg¡á”Ê?©_6.D„qäMÇLÔ[Âl3: ‡·ÕÉHý4ßÕˆù+-½M˜£ÒS±©DŸyjŽ/º9ž’‘–bóØ‹)•Oc¹š];.N|ž¿LeŽï‚ùM?1CFÀð¹¾Hny€vJ%G^·õ…ó$ÍtRj¾Ž}:ötäȾÕÙXèèÂt^›Šó)"™…“Š$”êßõb Þ4É—¯Rñ¤“>Sòóùÿ_ÕM‰‘è1»>O6¿ŽƒCô™¯Ãäèî·”#?¬ñÅ$Û(¹*03Ús¤I’ˆ{ÙUÌÙcðÏ×àIÏ x„šš0sgçbʬ Ñ(K7äI©üz'E„=ãHûÉ ä…ið¤‹i俌D²ki:ºÖb*2 }C ‡Wæ`LJmÌ.#0¶ØX$7´ÏÆÄã)•¯é© ý}O G ErþæÀ1èz§‘HN¿šçeÆÌmñ3³D ÏErª±HJͽ"áÓƒ827? ú® L»yøØw…‚ÔpËÁªm)ÕßlJ†§ ù®w†Ÿ¨TqU‡6CŸ¨ZOö›šŒ…U ˜åg3¡W,íŸæ Þ»ÍÌ›ópdcq:©½%ËtFpäê[¹¨s¤5sâ†4x­ÔãI©|GÇD€ó1I÷cÑ0¼hÄld‘Š {ë2eæZ|ȶCDÌ{Åì‹säýJ?œ{áÆô]ë ­…Ñ22bœÉFäŸýá®SG$¯4P÷äw”Ê?*"—ó¤ø5ýë˜0×mÊÃÊU‹dý<8÷ÒãÈÕå™XÐ4Ÿ9Ø2®!Ú<Ùü@~OúÇ”_4ÛW‹#¥òí|‡O6&" $áVž³äX&f[”äÐÎ90ô8Ëž‘ß|82ã^2äÕçI©ùƒ]2p`ë-Ž”'âÕ÷'ÌmŸBQÍŒlæ‡ûì™Rýc¢’Ñ«»êREoõ?þÅUé€OR8òŠ‘?º¬óa–¬ ÃW=m‘|Q…Ñn ™¦ùÂ/ÃNFºZË¡¼À déè ȇU2'ÛF!i‘l¥ÊñnÆ«TrW/G¾q¤Tþ&#£ñv•OæDEà}Z]æ ´lìÙéÍ‘F"üGW1Ç%àk¾1OæÈ"±ÏÆéÞ1Í»¤äœqÐïl"’RùþìNE›")Ø+pïìÜ«ºZkصfÊÃ%á|:ù}w&A›'·É3S9Rjþõ§”‰ï82[S@×f´ênÙÍ|—‚¬ê€ÝSRªÊ´ \zRÉ‘íËñëÇàü{«úÆÌq#ýçÊÑyÐXføÊx,èh"’Õ¿“ñpo}æôÐ)+È'ÂáS__$öOÆó ™ï‘W+ï ¤…g8ö-«Ã“ŽùâP§›2R*FÏ0Ü6®Í“³žÃvÊ{ŽL>˜Šø-ôË€cú=|s$ [.E ÿ‹UU? K|¡w¡!Gª×§VýŠŸ5DR½Þ:0훈¤z½ã¬\Ìyê›NJÍ?4X‰¬ÝrÂq%F)ë1w¹…¨m}‘ê$Àë—Sª×Ê(œ¨ÝJ$ Ö¥àXíæÌ¿¸ª væ¢ö"oŽ<²,? S™.orÑ`ÿi SýŠoê1/4LƒW°)OÚ>‰Åám˜? ÐlkÀÜÐ8¿š1ïØ+¡¿Ðˆ#gŸOƒEmž”Êï>HݦÚ"¹m[&ªG¿È3²1ìöEŽTx øé Ç“oåÂhRGÆ/ËÀöŒjæ„þJÌ(ê!‰wrP/(Š#¥ò…¼‹A{3žlf‰Ý+š3݇$àâ=3æØ h¼±hÍüµ&«Ç\àHŸqIª×¡V<)ùUÎU"bx¹‚¼ûB‰ó3N0‹;Šø®-’Íó04ü¨@Jõ¥ºúÏÑ|!IoUWknó/®jçÎÙ0å GnÉË„IÎs¦—êÛ»I¯¿-9rž òÛsd§G¡ø4±OZê%»/rdÄÍ0||Öˆ'×Ïô‡«ë޼b‚n×ôxrS®7n&µ“‘Rù —©®¾yòU\äòaÌW“ñpR žÌ>‹—Ï͘‘7S°I»sÔ¾XhÜ7c¾øˆ# Z3Ï|„rÑWŽ”ÊW§_ËòdJ¯`¤m­ÅôëŽÛF&Ì`ó`䶮Ŝv$ë3?dûà©ÖŽ”š?E+ãÞê*ÈmªŸ¿¬kÜìôA Y«­2òe‡œœ‘Á‘Rý»_VbÝ y©{lVŠùWõ,¯ÿæHÑI@ó(=žÜà’ ÑÔƒ#;–dáþ·Ììƒ>(«Õ“iYå\¸‚|kãƒÃžõ8Òuº†µÈ¡mƒ°õæwæ™Î)¨çÛ\$¥ò‹Óó°gíÌ©ßÓŠ¹h]8Ò*LxÒ];C|ãÈë“Âð[Óˆ'ÛŒ€÷­» z†ÙÎΖlÓ#é_ ¤T¾Çàþ#ÃÇù¡‰K óCŠ?Î9–3•¦rœõre^ëë‹’f[òtB(bÎÖI©ùÐÈÇê•ÍddR•KŽg§Ád¢1O~îƒA×[3¥ú×}”Ž)ƒðäÖãq0åÚ0ÿ‹UU?Þ•¥côé<©^Ÿk.¢aª6Oª×Cä¼9D Õë“ý±hh¹@ª×ççaIÎT¯Kåp[‰3“"¤zÝêŽI#9R½n8Ø×{mçHõºð)ƒ—©žÝþךæK<ç ý‡9R½î¾Ôî+Ì9R½îæŒ-D²¦óßJ,»##Õë ƒ0FãGÖ´ÿ€á¡Ø[T'kzþ¿Ž­eøO^>çK¿y£NR;kŠäµŒtüÜnÆ|¥—‰¯óë2ùѨzhÉ“S¬‚¡±Ô˜i¦,ÀÎBAöÎÇ"çƒ9¥V ª_芤ýédÈ—uaJå_ Œ<‰'Iè¹Ë’©§ºÑŠŒE²MûHlšÒ™i±ÏÚ?8Ò²¡‚׺0-Nà»a…Œœ¶'£ÎÔçI©|úž^x3Ï‚#'j{a^£E2²á†ÞkΓ©ûR°Ø­3sÊ%N¯l!’‡Ë•’tV ¥æï¹‰&9"yîWìÂ,™–ûມ6OÞä+õÖs¤Tÿ†A°]j(’㬣1ç­%Sêü¬j‡€@¸a ’³ƒ1tˆ S+8§Ú=ÈSË”0zîË´®öEîõbŽlmç…ŽZ0—Æf!h™&OÎ‘ŠŒÒŽL׳JL=î!²X%4]8R*¿µvrzYó¤Y,ìX1ýãá2ËZ$·~Nżz˜³ ¼P>×^FÚGxaÖÒ…Þ6–<™¶Í/28R*ß/¬Þ¸ZAúûz ¶ÈñP޶æÉÚÑXœhɼҩ¿“:s¤A¿L´ä xRrUÍc€FV"¹x|,FÿþG}™¼þÜSÓÃåˆ]|S ¥ú7©— YDR?< mÎk2ÿ⪞êŠÓ[ŠänÓp\øÝŽ9¿ƒê#zc![/`%7%3Pµ’;ž+HÙH_Œ¨›.ŸäÑèq +O¦zaÛò†ÌAåɸõ‚ÙÓ6 cê0¥ò+Eáþ.<©»+ïÛ›3«R2ðŒ3ɦ³saX}U ûÞõŨ.…LëAX±®¾HŠŽ^²ÆL ûÄãê}S‘”Ê·¿ÄqOäL{_ÌÈd†o ƒ÷û6<9uHrÕg>Û‡ª•ÖÌ‚§xÙ[—)5ý®8üXi-’ùAñø=õ;l ÷± ³öìDl˜dÅ”ê?kX>&þ²Èi˜f8[FþÅU]Þ0Ç»tɬ¦Ñ8zÙ’ÙeZž˜4äÈØ\%Ò~91ç¨^‡,•†"yëy$ä-˜çø`ô‡8Ž´Ñó¢ñ›e¤ÉGOÔb”JΫ]uDR*ÿÜ+!0}Úœ'ÏO©÷õ™£¶çCñf£@>Î/ÀnÍæ r¸E$2vÉ2e^Y1'¥ ¢Mg楎ª×µ™!)•orªnõ~-mæbƒµ¾HfÄøáŠï+ŽìÏ1Z1Ìu«½¸süp¬œã-RóO^JÀÒ+‘|ý=ž1]™Uª¯pŒ!³]¢ \Rª€2ã»Çs¤øEĘF<ùWõªE,¦?³ÉÊþñh·Úšß%×+8rll&þŒÖãÉ“P»EW‘¼²^„QsæùéÞxbºM g‡øÃαšÙùN2FݲIsÕGTQßE ¥òg q§&O~lä‡^v8Òɲµ.wd^IÊCÅí¦S ÿ´É}˳ñåúWìS˜§ó¦päWäc[ò¤T¾ C¨ÛL$—G…Ã4Ùœùæ“vr“8ò—®Šu6ÉÈ^‘¡X!k-’[¥ p“Å?JÌ7h“Œå­,EÒhh ÞÖ·`öÏ-€`ÐOFZŒÉÀ=)Õß=!}«»òdÞpÛv`þÅUÕŸ’ˆÌªg–ÿõÌ¢d$taŽÙ¯ÀüÈ–3¥ú·©(ÂçnÛeäñŠ|ü†À‘Rç×`U-[„cìÏÞ<9¨I([3¿T}v¸sdÆ%¾ŒzÀܧ,³€0Õ¬¶ ãÈÖÚXÿ!]ANï N³@ kõ ƈÎDr‹A:zÞëÅ”Êot-s#ú‹äaÕƒ‰mŽi‚£µ­˜ÃGãlÏüy5‚5Ç“gGcå%0ŸgûA'À©|ꎔ­óe¤T¾Ôô|ø¬ÎÈS áÈe_÷€Á§YÙã•;Îií‘‘Y¥>0÷\ ߈€÷­¾")5¿|¦4é.K=Ð4qÓÅ3ÝÖtÉùoëÞŸ)Õ¼Vº87çÉU®ÉhåÄ1ÿâªFt ÆÇÇyrõÄ@Äå·b¶}šu³ê3«Dx¯ïÈ|=D‰óÓqdÇâ ¼îÓ–'õÄЮ­Dòê(œoÂ3_/Ä$ûã9Cu5^ÂÅs¤TþnÉi¸_Ö[$×íPë´%3´<ñ7Á œ¦À…ânLŸ!(õ²äÉÂr9ŽœÕeöá‹7? äèú1¨õ")•oîÒ"ˆ›Z 䎇E8Û©­‚¤ë_3™­:xB»û.¼:>Lz‰ä Ã|¼itY ¥æoµðÄìÜÝçê‰×¦g˜»J2е{[‘Üž’Îù™)ÕÿáøHtl$ãÉ&õppVcæ_\Õi.þˆ™gÌ“µÊq- ƒüšŠä'÷"T?þ¦ %¿JÅEØ“ä##[´,BñÄ¡yŽóƧzJ¼ßSŽC´DòúÝ"$; ”‘ó€_9Rj¾}#/²P8¶ëƽ:íÛÈx2cy(N ݘRóÝ{{cï¤G¢¬)Yç˜ÅÝ`xý#Ü]±UùUFÚ.,Ä©²þãœÚbÉÌœ†¹ÃFòä²ör[·bêÚ§B»h,sD`-ëɔʿ^¨ïô…#CNz xb S~/Wõ'n!’í×BO~_ “†Åb[«)1òÄüç7™ÊÝ>ó$³‡ƒ+†7»¤ ¥úú•àÒ® ùáF>ÆíÐâÉ¿¸ªëåhÙðGæÙæã¤‹OÎà< ‘-³úzãÓ9m‘¼k–‹L¯6<™®¡@ŠópflGwt˜ë)Ó¿ûÀq˜‰H¶ ŒFñËIÌÕOsPlÖŽ)•¿ó9WÕëÑ Ùk’+ºÜßiKÖñ(Á¸¡S8rÁÓB ûv•YUß }¾gö]îŠoíŽ*HGS9®”´ÉÌKq8z` S*ß—‰îÐ~ã';šy";á³û„R|Õ UMUj •‘ó ² ýÖš'om‰CÑ®)L©ùŃùWõJ`&º;LÉúyÙø9œ9ñ|)^_*Èð³?w9s\‡2¸5ÖçÈAZ¥èWÿ.Sãvê]^Ì“š¾èÄ–Ù1<Yk'2Ñ: YÆ‹˜Rù­œ‹09®Or‹ °ñL[¦m”§Ígîï™ÇÏ«™‹Þ9£ßŽœ´Î¿ ’¤g+?|2Ì“VÓÎcy»)•ïÔ†bôûQ‹'»]-ÀûŒVÌ |àö£;3üÎmȼV7íÖ0ûïöGJ³QL©ùcšDcYÆ*ž¼¥ˆD˜ñJæ‹©¹hÖŸùqY ö;®`Jõ›VÙ‡¶"ùèØL­ È¿¸ª3 r±a'’æJŒÿdÍœ¹® ¾ú*Èô¶ªóšÔãÈJØÆXñdƒy"ö·šÅ ìî†øç/8òûqgT|•‘:IªÇïôJŽÌð>^ïŽ ¤TþnOópgXožÜÖ%?‡2ÛŸ‹DöÌÕ£ƒð´×4æš`øµNHã%žH*i*’éõ]Qñ´@ Gí ÂùGÓDR*ŸIN¶Â†'WÍÆõê‘Ì`{W8_çÈf›Ï£AíƒÌ> ]ñ¡Y!óŽÜò/õRjþàEˆ{¾Œ'‡Õ G–0ã–…c›ÆRæ™>hÖ«7SªÿÃMex·VSF^Í)Å´ %GþÅUm½¦®\[‘ì©[„5Ë1sã.`ÛL?Ž|Ö¹M®}fæŸNÀôqkx2ôtÊÛ-f¶zÙVrÏ@´Ük(’ÑkÚDæïtD·[Ä”Ê?«o&fžÌ“ ØÚnóämtÑ›¹6ÖñCô˜U6þØV4B$[î ‡žõRæ¼Ü4\i³„¹bb1¯ÒfJå[å(âËÙ™<ùã`‚#–0ë®rF©o¸Œ4Øè o‚‚ìþÓ;¶ÉæááH=¶”)5_žŠö& yÒp]Š×Íe®ýæ‚7™yYwŒ3vu›—NJõ÷y¢ÄŸQV<9ä•^þó˜qUOì,ƳSµDÒ7«W½ŸäÑÀTM7ãÉOCra¿·?Óà°ÎÑÿ˜¹Ïãëè0Ÿåø£|óh‘üá—f«˜çôKÑÄøŽ@†u¸ˆ´ÓÇ9R*By*¦5XÆ“w&¡ÎÛUÌám]=5#/¬u†k|¢Œ<µ)=f¯Éëƒ< ˜Ë¼:® ›ìsedÓùùè’nÁ“Rù†ÎHÂìú«yrT³8,ò\üáxÊõ޹$Ð 3o1§ç*Ðx¾H†ªÞê~­Ç”š?¬c0"ÆÎäÉ?U¨Ÿ8…©˜îŠ)£/ d†ƒ/ιöI©þ;ÇbÈâ5<ùÇ$?VNeþ«ª~XÞ*EÅÑT¯kõÉ€Q»é<©^×¥úˆí6s¤z}ÎëTŒ»¿T$Õë“2!ÇDžT¯Kå¿n˜5<©^oëŒIš ¤z]³e –ÙŠ¤z½RH@Zß5Ä<†d|oâÀì~ÇÖòdÌ–óèýEŸ)5ßjM1œºõâIÏEH☚A!Hwß+’SÖ$aß^¦TùSg´ÙUK$w½õBkÛñÌ¿¸ª{gô1ÒÉpSD¿2b>P½î40]È“­<±äü ¦e§«ØÙyŒúé¢ô²8RèU€øÔ1<¹ÖC€û¸=Ì s!ë³@$ «^^9 ¤Tþ—·P­uH$·¦ÀjîA¦¹ž3¾âÈÃÓÎâÄ…ÅÌ/WÏ!ýc–@ÆwÅüžíDò}B }qàÉw±ž@Þ0¦T>~X;s¹HN^„ÜVÛ™ih'ìe^qÎF½ÕÌ(ÕØ¸uYÙ7â<ì’뉤äU}f!ö‰CxÒ îlü䒃ôcËD2äN)–ó-˜Rý;ß_·}"éÛ+ù/˜ÿŪªv^®˜q¿Hª×¿U»`ä°–<©^_´£¾væIõúÎs±Hlˆ'Õë3ç–ÁXïGª×¥ò÷ú“ÏNv"©^·>¿¾}O'Õë#6ú eõ ‘T¯‡69‹Cµyެi>eb,,ˆ¤z}³s>—LIõúË~8=i½HÖt¾Y³|è('ó¤zýãÎ+h;[ kÚ‘MöžÙ ’5=ÿ_Ç øONxr ß‹yå]9VözÊ|uù*FMÖåI‡%—`Ú¾snAlJEòºn)´g3myã€û>æƒ0´?èÄ<ï•‹Í&‡™wn”ã×…)•ÿúŠ 4jjőʈë˜ÔЉ9bl tÚ¸ˆd†k&¬9É4êŒØN`n UÝ-Ê·2 lS1ðÄyæ ïR”´›Î”Ê·©2ßc\E²·w2zع0'۞Ŝá͘sî9ÃoÅ`fÓÒxxº2›U¢j„=Sjþt+%¾µÉÅûòq>`s\qž½µ%?ø”£Ñ€)Õÿä¼FMjw)@#ÛJ©Ôï®u®¾õ“HÇü3èQw'»Ù&Û,`ûÿþÛýœØ¬Kùð;܈mð{î¼9E*íO8Ÿ…N)ë%òxåDÏZÉî·Î…mâ$Ô«™Ÿ/ViþœÒhh䉈߽SØ¿xªW1gygœ4á ŽWÙF&¿?3å{°ïOÃuènÖ:ê&†_· Wº‡JTäð£¹h–3^"¿Ö/@wÃÊì™ñЯ$ãú¡ôÊV©Ê€||{ò@Þhzy{³õ3óaX¯±D&›"]8¦&mS‘19@"µÌ@£:îì­ LľÞ.—ÛÇã@y «Ôïþÿý?Rr–Hv٘㲖]kcv²zm£á³ÍŸõÐùý‹sn}ܘt66ެÒþâÞÑ.r¡D¦Ïº„W®3Y£„ãX›$aSájs/ViþÂX¸;ôȲ.Þ˜õ1WEþÅSuw»ŒëA£r«ÿ%lh0ƒ]»6 DZn%qH»ȪUW€ýؘ{p/y p½R*©Éè¬B|æ©"GšxÁmìO ²kO?,½i,‘Jý­#/C+d¤@Ú—^ľsؤ“71zæá4²Ðö&ÎŒU䣋èT{¾Dö¶¹íÀê7 Á/¯y¹¾ÁN¸nõS‘Jý†^¿„Éý§IäFWPw[vòüc˜ãÄ%žDÀÚ@6z` ƒrõ†ýø~Ö…UÚŸë‹å5'J¤§ÿe ê6еî„%òÎwoL°¥"•æüè‰sã"÷õóFñÍ+jò/žê­­9¸0w™@.´Ê†µ›Ø>vG º±‹­2úÙ¹±YûÔ¸²a›ºü8,ã‚Øà³×ð`\vS£Ä¿ZÁ. ‹„îM_‰ôk™Ž]»¼X¥þ»Ÿg¡ûËui{<‡“·³&ÕnÀ5籊<ûú*Â[™ d¯Ë7·:CMÎPßDœ‘OùÓÕ¿ŽÝU“JC1$s‹D*õ›þø‚ZIdßßÆŽªÄ^»«F•n»ØÒ” ¼zîÊÊ&~8ÑX ¿ÔôBT‡ƒ¤Ò~Оe;R3Ûå"¦>X(š¹Rÿ,Ó˜ÚÖ_ 5óÌÉjôËÙ%š¹çÕËèÒq„@jæ ?Ü€‡˜¦&ÿ´_î©›øº?$ÔÌ#_]E¿.¦©™2‹GØ—@‰üÓýîÈÇ£f©™¯>†Ä ‰üÓùÝ3á¬ç,‘úþ<Õ\ŠðßüòÏn ^/‘Ë„XŸÞÎ~M¹›áŸTä€ó7±e`g4VÝFäëF,¢óqkÝ|6hx(´¥ ‰œú! ‘¢Ù‡Ò=4‚öýüºb•úŸíä+ïê ä€X7|«ýTEÞ8ë†ÖÅÏÙìý.˜’З­¬uÍJN²þ_o¢Ú ìš Óø06èx,¦c•ú™M-B•ï,ȈC÷{×SEÎé‹ fH¤Û+$pb—õ8Žøñì¾Úçaw=”UÚ¿´ª7>Wµ’H|õÆzvøŠ8„EDZo®¨í£¬Òüb«˜vkŠDæVÝí¼yl«ë‘-ú°UNÝ@מV¬Òüoº'ðÚ(^ ? 9ãÉGÙ¿xªÞ¶¡¸•$‘JÂÑ«k랃é[ýÒõ÷‰í?ºo”„þŽñìÅGðyìQÖøYzÎr’È[o¢~ÛnlÊ{ ö™GÒêãAœ©Á*õOå‚Î{YÅ\°"P•FÚŽ÷ÄòÜÖé9˦¹3Xõã3x=î°@æ¬9õ8¶ÅjWìw:¤&'®ÚÎ}7I¤R¿íoâýîžù`D¶íÏž ÏqQ9½0§ı®‡îbMÅEYoÊï©¢U©´?ï?Ü[µB"›÷ßMéX´½‡WâÕ¤‹utÞë©H¥ù¹þ{P/|·@®êå‹´NÓÙ¿xªm< ^ñ‰4£¢Ãì¥ 5F]‹È5¦'±âT{½g(ô܃ØÅçý0½x »rÝ=¬®¢&}'¡æ©ï¤}üNDëȧ]ð¦î°4R©¿U€ JŽwQ“¦–®˜0#ˆõ¼ˆ•MÜ%²³C8—„²ƒ®D#aP¬@ Ø «ýlËü(D–H­ŠtŒ¸pˆUê÷¨â*¬Èm–—qì‰+ûtcZ¬—ÈOõOÃi} ûZ•Ž1w#ò¨V,^e•ö_á«»Y=*kÚï`UUnÃÂÅP µï^AaæVViþëÞî˜~ü§ŠLî‚ôä_<Õ•ºGpËè¨Dj?:нa1ì‚ ˜; ^ -D!ma4›é‰f«Ú³»f¹bsq¸Š¼SûÄ¥Õ²ÿ³|œ°œË®öòä7A"»|;ŒÇåGX¥þ ǺaÌÞj2^íí¹ûK$U?ÊJ¥ X9/ž}>'’‡@νà«{“ؼÇJä«ñE°3ÔR“Jýlºå zÀnì|9[F‡±s¶¦#Ü!B"[ù^@æ©¶I­$zùdûMžˆzÜ–UÚþE úZºK¤Qï`´í¾“þzÑûòð‹$L˜Ï*Í?}æ÷Ÿ3NªIõ’XôB”È¿xª¡r,vM“ÈÊ>!žý'ç >(q~{±D7u°rAµØõi¤ë27±¿£&÷úgãE“g{J¤M›khqp5ûAt‡kM ²Î6h­i$‘Jû½Bpù•¯D¦¾ØƒûküY×ý896R «V"ÐÉUü®s&1j7‰|zñ ®bÿ‡SÕ|>¸&¢Žw¼Djæï_൮‹@jæ‘Ñ;ñåþ‰ÔÌ[Ç£{ƒxÔÌË·asýY¤f®Ô¿w¹†EL‘HͼMb6YûI¤fþ|´ ôOD¥‘šyÊùc8è/Ú/mÆIl8'šùÏù7 {ÛZ"5ó%Âàiç!‘º¿šM(šdI¤fþæ¤&Öì-:?qÒ1ø­—È?}ÿ?žŠ3%øoÖX“Œæ—òóäãh±á_´ËÇð¡aì'Ã\üšÏÎîi}í/X=înCÄ»5ébx ®øId…ö}d.´fSgÃáÓ œÙ6/M/²Jý#³ï`×»UÙÕ¬æŒc'.HD«ßÿ¤é…(øvËfã*ù@»¿DVjŒaØüuQy[kvõæ¤\2V‘Jý†éù£ÅìQìýéA¬Íݬս(‘‘1nÅ%öÎ–ÇØÛ5YE«î`÷ùõ©´ÿî/Ôüä.îc=Ñ$Ó™]]†ÖÓ$rЇ8¨\d•æççï‡kÒüun7¼+±JïÿÁ©ÎœO²_çÄ&ç°.ç›–Ʀm–QÐê<Ûâñv”?ê,‘‡Ó<òÖ‘uôýHjÒ?¡ë² Ç8ì‚ÇÛP4šàˆ.T¤RÿnÃ`̈Þ™§ÿgž6fÛm܇AÇ%4+ Ä ‰±l[÷ƒ8ª•!‘³RâÐ,÷"[bsÓª/HƒiYpX•Ê*õ«hè…37Üt stV°tNãØl‰|ÿ6K¦d°Ws`^-Y ]B’1·ë%ViFo\-°È»Æîhyc5ûjŽŒ†ç%rÿöËØ_Í*ÍoÐÏw­ÈI&NnR›ý‹§º¹Y¾&f äƒÃþÉ0KÁÖiÙ¤ÜØŒºÄšÔðÇìì‰ìÝk¶‘ØMÏ¡w5<Úï.¶æ,eG?q@gŸ»j2k”/f=–H¥þçŠášÿZMÚ…ÎLÈ©Á!¸u)‘}³eKf›™ú£^÷ÃlóÙ@KI"Ëó® Å=‚}xð$N^È9]¢`œžÅný|GÇO”Hs¹¾ö%•úëœ(eØ ²õ¤4î:\EN0³Góëg,È1Oì±·Í85y.£¯ïÈ1sб©ÕG5¹ÆØ•¯O•HÃñ{`43™Uê××Õ—w´R‘öUí1`cÝÓäëè{ §Jäà†miÂF×vÁ˽#Ùø"Lh}„UÚßÚÇS Ì2þÒv<Ó‰=]z/ýW² Z\Á£­‘¬Òüú?ö )ù„Dfh…Þ´lö/žjÏ—(õŠÈè þh}˜µtðFQO?¶«Ÿ;f÷ÛÀ>k|«Â%2çÅ|oÑ™sÞƒ§éÉ™)ycz«ÝìÝ{˜2‡5K>in«Ô?hÿcŒ®­"“½Š1ùbÛ2Ü“ökI¤‹ë4°µb¯ø— wM5YÇ¢§´ªÈÈ#I0òûkÎÿó–S.&Lc•úŶGßÍ=Ôä3XŠg¿£Kÿì¦%˜’µŽµ«ˆ‚Öäl‰ütFîe²Jû«4ÚŽKaÍrúr'”¥ê³©ÓñºÓ9¶ìqjØ^b?`ä¤à¨áE‰,Ö»€â¬ÓìÿpªšO§‡»PÚ!\ 5sûß¿(®[ ¤f^~û1‚F«IÍ|gÞõ&šy¿·Qx2*[ 5s¥þkš?ÂèR3O®é‰:‹·K¤fî=ù|zè ¤f>`ù}̶$‘ÚïÄ~Gt¿¯%‘šùÏß¿=¢8MjæÅmóqeD˜DþéþWŽhUU 5ó“§öádY ÿtþüƒùx›°W"ÿôýÿx²N|Â3¦ó+èo^"‘'j¾ÁH“IlõÄÝ»òP Å{ãpB6û6µûÞ² ³ñ--ƒÝ±s/¬^¤°5wx¢cšÛeþkœ/À–h=†ªè «Ô¿_GL1W ˶A7Ü‚]µÿ$–U}È6x³%¯Ø©°Ò¢/k®» Æ?3TdÚ螘'awr0Ø¿œUêw.9 wIdÅ«SйTÀÖþü ÛÞÕa×'=ƒw’¿š–p?oJä¸.öøÂ*í?n“OWmužÿý·kevÉDìß)‘Ÿ+• ÒËïjRiþÞˆýx|¦@"/ë$!ùÓVéý?8ÕV‰o±²›¥Dú¶zIafì•êFÇ ä®å.è7|'[åyF/+a]ˆ‡ÉÓ¯ì ³-èzi(k³ T¤Ó½lìn÷T ×}ŽÄ‹a—Y¥þW§Ûã×ÁzùvæVxY^V‘ë6Àé]ò®„+­ß³Ã§ÎÇÃI¹}É›f³ðèH¤šLÖŽGôÅb4|¹ †áûX¥~{š¥#Íè¢DÆõº€Q‡%öîž§x¸Öá4i›\‚ªv¨HUõ|˜?y+‘ Ç>¡KgY¥ýeâ™O ^eàÈ´ªì‹Üß{Œº¨ÈBûG§Ù¤ÒüH§ót“$2çp! >„³ñT/Nþ€y››IäIå˜y»›0f;¶./•?9àGó:¬[ýƒ((Ôù`u RkÈQU¦#vg¥4r°ñ4Ô½ûHM&Õu…‰Á6l</ï´P‘Jýã?lÁì}KT¤aã­>¾Ä‚þÑF5ž äòY®8¸ð;¹xÖi/‘6,ÄêíkØ_#7`]¿w*²Üf.rýϨI¥~Ós±Ç5A" î_CÉó}ìËÅð.ª.ó¢îcàƒáìêO¥(Éž-‘=Ú”¢âÛÙ4RiÿŒ¥gqOGäÐgg0ßó_çN¼ƒ›# dÇÇW0(±”UšŸÞà ò —Jdø‘7ø1µ6ûOµ¤ÙG|ðx®&gýˆ¡2[œi޶.*²LØŠì×zi¤î/ôoýT {:ãÑ£s¬¡0ÕÚHdÁñ Øl·‹5›4 ÍëweW;"ñêQV©¿ëÅ­°“ÊÒÈ@Ýmˆqß­&ÏôvDû£é¹jÀF,^q•Õk_¶G"{l‡mQ2k± Ggîdç¹£~ÏV©_ÂÈ›ØðÑ["ÝÝÃôØìð;‘ë"­ÝÀ­²VÊDÖ³ø×ÞW¡ŸwŠUÚßiºŒ/u@N¹¦Fãjl£ç¸p¶ ›¼ …«³Jó%§0)l¤>ø„/u\Uä_<Õ—_>bhÕÝjrlíO™5‚­>c+b²w:lËgÍ$ò§µ=œk 专ÐÜìÏw´¹yLbKCð-“}”t5Çæ±ÿd_€°Nf•ú-s@|¥—j2#ÄwZHäs»ÕÔc«@.1Z…¼õØ-5½±§G–DFÚƒÙs¯±oWEbIê-¶<å‚?f•úeµx­5¶¹fÁ4:Б5¹wùÒò—õ%üv‹íØZÂä¤/¬ùÇ(„®©Ri·þi說²zü)¬ïü¯Ò¶Ã(9ýS —Ï Â’¤|Vi~°Û;œ8ßN W}†E Ø¿xªGž~Ä„ÉÏÓÈ;>¢üæÍÓäÝáŽ0M™'‘9×¶Cz¿›m°{.š»©Èåg·b^äR59[/ /åJä•c2‚_g±fßžàŽÑ6vصhX×EM*õ¯Wc;Ì7[KäêSÎøÞn5ûvX< ¬Ò:5©Ôo·çh -‘îkʰpþq5™7ë<Þø— ä‰ÞgÐ;í½#§ß|flÜàP+…UÚonŠŸ_u@¶p8‰ûþõ`/O<­æ/yµœà£S—Ušß¬ïdùGdàô j/³ÿéj>Ýž—c ¶Xšyí¦®¨Û0Q"5óÛo·c¶­¹Djæç_ElúI‰ÔÌ·¾Ciq ÔÌ•ú»F¹ ©ÅN‰ÔÌ}Û#Î SMjæÓzŸÇײDþ är¬^ðMÅþa¿ã7ˆÞÔGMjæKKRq²wšùÎp‰ï,ºßài2ÐV¤fþÄn#p(,üÓùæ§Ob¨Ó%üÓ÷ÿãòH[üon¾¿å•9©ý6Œýµƒ½3Ê fz;Ùé¶î¸:ñ{ôG1F´~ÊžÜùço^c§Å=‚­S5™¼ôûëA¨û}‰´òû†Ã‘9¬}1œ ŠY¥þšh‹q¾ÕÔäUßJ¢Ãº–§ÉÞgd<:ñY ¯;C–Õ[6ñþ!¸GübmÂüQ2@¤‰öL\(§‘å®3Ñ¢I D*õ‹ª•‰¦ÁUAžy¬†M…6«Z ëKo2ðÝ>lïüžaµ m«¨È ýÅHv˜!‘JûwŽø‚AV—ÒÈýË?£îÞšla¢ŒÃÏëLòƼϕX¥ùG"NáS c‡îÅ¢Ìä_•ÞÿƒS­à ΫwKäSl;¶Ÿ}žà‹ŠMרɃQpú «^õ¦EIlÛŸpáÅ&v…ƾ¡l´Ïk„ÕmÊNw§lÞ ä£|o”yÝ`•úOÌÒ—ƾ° ëmû…ùSÖ©Èêa´¶D {ýÚ ùD»»¡+nÔy9|=®w/Ⱥvøtõ£DžØîÞÇkˤR¿ïŽ'PíW…@žþ³IŸX÷;ؤÿŽ=1Ù ³§¼b³ÃW£_Ìm‰œxt–N¯#“Jû›8~BöîÊäÔßì¼æ°/BPÐ=_ Ÿ©êÏ*ÍïÜt?®]j²mÚnTtÐfÿâ©~ sG`tœDz4߉ÊãO³Ç_íC–YU™ŒØuù‹ë²­+°q¹DVküß'ø©ÉBëW¨bæeA.=ú¾³Eìn7~w‚Y“péßU¤âOõç(¢V‘]+¾blæO¶cª îm8+:×"ªe {lÒ|l}XÄöÉ›Œ²»Á(n÷*ˤ:âì'fK¤R¿qS"1Ú´L ŸIáP|Èf„nGð˧ìk¯õðŒ.bS6ÃÆÞ@&ß]?†Æ÷j±Jû¯o.‡ý’*òéÜðµ\Ξ¬=#6·Ƚg­Q×ï–©4?¸³æçedfÅf¸y/bÿâ©ö;æ‹§‘$òõpÌÜ•ÏÖl˜€‹&2ùϬ“H_М½½§]k÷W‘÷­^"q½©@.}Œ-Kޱ3Ìï!ʧœý±Âá#2Ôäfs|ŽH¥þ=B>aêñæùöÊ{¤¯ìǺ—ÏDœŽû"l4¼Ÿ-eý3¨À>h:oZa¢Þ¢éÀ™ùci%1ìpe5©Ô¯zÏ <¿u] ÝnøàÞÇóìû7‹puÇ5vQàtجÎ`ݺ„÷%rëª'¿¿í`•öWíÿ.©!*r_ÝwÐ_¦fßû÷í˜l5ÙLŒÙÇI¤ÒüuqK°mùvyûÊL”ÏV“ñT×¼Bhû™¶÷*?g}gKØ'µ’ÉÍ52ñºikviþÜY ‰_ó±»ÁÖùl>Ú,¬rDL–§µdo!Ÿ§W’Éòe¼5hÈ*õ·Ž~ û32µë tr`ßë Æ¾©ÝØ;»€eUäÅi“¡šÈ: œ‹ß§‘WæWå¢éúá Ö~ f•úuJvÃá¦)9ó‰#œ—aoFŽÃÄÕÇØª}G!Ðo/Û½ÆWÌØqHMVRIœ¶ER‘JûÛ¼ÁLó*²†×kܪ^I nLD×={a#^õKg•æ7ÈœŠN ·H¤eìL¸xIì_<ÕkÃöÁs͉<ýõ †þÉvzá«ZÈdV㫨vÆÅ†ó0¬òŸÏ)xlZŸíl’†3SÛ° vÆÁ`YSö|ÜäZiËäâ{o°}â.‰Têß~é\]î+ïæ=Dá›}ìØe®µTdÃÝýQž˜FÚմÈ&™»Ü±{ÜYí/yè\ÿ†@zMŠÂlóϬR¿_×A¿j@îkº•t\Ø’ÉÃõr;{[ÇÃÇØ²îN¿nµ–ó†%³Jû3Ì_¡Ñ}<ç%>iÍ^]ãËéw%ÒçC4ž~É*Í·í³ s”IäzÁú&Ueò8UÍçHD$ªZV–IÍüñ²ÖH_&5óÔ*1°¼Þ ¤fc½»tªƒÔÌÛX¾BÉÔ*R3WêŸÒî.WNHÍüE¶5fŸR“šù¾÷hç“!‘šùÔ¸ñö£@þi¿q¶3QV‘@jæ³M‡átMCÔÌëϸÒmwòO÷»Û½@ßá=R3ÏypÚÞHäŸÎ?ß½ÂjÊ䟾ÿÏ¡ëºâÓìÑa´92¤­Û!ظþkÓW÷Ñ®G™LL~ ÓÜu8Õòo´Óé„óY½Ùå×~“ÿ 2©ÃjÇ×e¿Õ}‹5nKdƒAŸÐÞ43T꟰¨3ÆÎð“Èyc:aËøC¬÷àX}¹È w `oÊŽ÷ k¢W dû»Z¢ËŽSl¾—º½/‘S½#ñʼšL*õ ¿©ßKäâ.ëá°è ûpLK”yÏS“ë|›¢4ú6;sÈ3xÄ äÂU±åbmJû[/-¾Î%¹ßö!Fß{À¶·Q¡Õ?{ò{Ä<ÚõFE*Í·×qÃ>×r‰<þ=ù®-eRéý?8ÕœOÒûg8ºvÆöêø K“ÚÉä‰ÑïñóH+6&¨/z½Yª"ÕýFAoC”š¼4ÒÓÏ×y?R„}³ ¬°ýŠZ!jöŠÃTunR©ËNx–›(‘uÇtÆ‘óéì ÇlÄÏorÆÆÓxsÓˆüXK<£ýJ ÿª€§c ?^æãeC™ô÷}å9MY¥~m[:cÂ埙Úi'®Ïª$“½Ú7ÁÖÛM%²•!†=²d'ÄC7+s}íBñáøPViÿýnaãyW"¯V}Šæ7Ù³“¦ éR¶šÜ’²·WEH¤ÒüY±pd2{{<ê«G°ñTõ²C1àó){ö ¢Ž%kÚíno1”ÉoÅ_ñ;úД]!‘I8yunmŽöï¯ d#+ Ä7ôcW—݃a›Î`&Ãð{V©ÿ‡.¸8ä²D®¾c†‡³o±hƒâWz Kì…G%¶èõ´KmÈ:N~ƒÀCMÙN=´Ä•M+$òÛ äÕÚÂ*õëˆÇ+Ëd¸í~¼¸[•]©9zîX.‘MF·Càiw¶´ûN$¯ê r\œúÖb•ö/Ï+EÓ²|‰<îñÎáyì.Ûáb¯#“iwv¢Fï.¬Òü=§Oa¤j„L~n~{*Tì_<ÕÄÕÁ¨X?¤Ê&¥½À:Ü]Z2iÝ÷;J?I¤ÓΘ9¯ºLö ÁI£öì‰KÄ–i ¤iÇ ¸S¾BM¶Œw¾ó·òHJŒqua•ú‹szcC›b‰L4ààëqß{ž”äùëÐÀñk6º Œ@æuÌGÿ­ltó¸>銊l›RŠ?ž ¤R¿â.Ñèð¸šL.­—Œ çj°oëu‡y½¹¥ÖÄèIJCÒÁýª‡@zÚ¢°øG©ø¤Ïk8¹(‘&wÞ¢iõ lË¢`Œìm%“1]£þ½a¬ÒüiV×pùi3™ÔqSž”Iä_<ÕƒÝüñvT£ëûÁ+»;»¼ãwLVÈÜmß`~Ô•ÕKŠÀÌ$Q&_?G·ïÃÙðñ‹ñó’£D®6²Ç­›Xf0whžŠl8¢;–¾ÍU“Jý³ª ‡·ï$òWÃáØÒñ »Ásj‹HõÖ~˜ãÉ&œAµz ëoˆAô°lgƒ»¨S«{³kn¼³b•úݾ)aöÚZ22' ¹/tYó綸¿E-‘S,Äþ¼\6îÇ@臆°³VÄ€)ßY¥ýK—|€êÌY‰-ûˆ¤õ{ùBzodrf½óȰjÎ*ÍO¯\†ÛV¾9»÷Wä5¼ &ÿâ©”û@ÿHg/ v"æ½1ûeêWÄÛv’ÈZ%Ÿ:ÚUM®JŬȑ2Y£[Z¯ÄîÌpÃÕ)†ìÓ±~(ÒíÃél†Ö®év,X`$“JýXÖ%?%rŸ× T<Ñ’Iia'L^>C õm†]uÚ³[Ê!ýú×ÖÛq#þ%:0=½LA†íÞ‰zƒ_ ¤R?ŸÀ«˜¨_G&cÆÞÆÚÂÝj—†Eicî‰cs^±“ïNBú F2YØtŽÇ4a•ö˜ú½»’HÓß0óe2û(ø†ž/c<(CiÜ.Vi¾¾¨-n¬·WEÔÑ—˜+ÿéj>[=14©9HÍ<¸Ë'¬ÞREjæúƹ¿?3u‘IÍÜF E^k™ÔÌK_g¡uó2©™+õqc1*'kˤf¾È¯z5~¨"5óÙ~³gpS 5ó‰+ÆáÛ¸ ù§ý;?ÂØfz2ùÿåAA˜ëùS"5óÏ`à=™üÓýªüÄÉÃÇ$R3oíõ +«É?oÙ»š8¤Yœ@þéûÿñ4¯a(þ7õÆ~‡ÏqÙôôw|ÊÆe}F«ŽÍdòëÄ/°=­ÍViò uÖxHäà­:âÈ%ÔdiÞP ñ½ÎÚÛL€ÁЉüQ¯3RÚ˜ƒœ_¹ºœÔe•ú[ŸÒ7å¹ äÚŽÕEéB$;·gñæ°V쪚bX¿Al€ã4þœ “~ÎAðƒÙ¬¾z2ÏÈÂãC >r–H¥~e5Äò„NÙ±F51öÔ5YïQ;t«ñN"O­á<æ3û¢†´´¦‘f³‹…Qó‡K¤Ò~¯0Žæô—ÉÆÚÊë b:ŽÀ—ÞÙëK`²g©4ßö{!Žn­!“+,ËáÔ(O"•ÞÿƒS-ñÐM–zJä!ÓÊb‹ÔulÒº¯è3,›ý:ú;r¸°^µôÄ[R”¹pUC1OU¦"êeòÚ¨Ù˜¯;˜í8ªIZ©ÕbæOË–H¥þ¾5ÄqÝ2rÉ¥š¢Þã;l‰Sñµ×<6þJÑh€ kðÏ*,ë8[&÷ÎÜŽÕ·'°Niý°>¶5ÛÌo¾Á*õ?­ŠX·mXYq¢’8ú¦“Š<7{2œü’È*÷×£ú¤ª29oË-Á±a D¾jÝïŸ`•ö?ñÞÈR&§ü¾Àí·¬Ø§ş ‰tXÚ7ßW–I¥ùv¿äŒB$²ÒF}ñAÈBö/žêÄsUEã %²ò êbÜy vןH*ÔaSµD­IÅ©d³ Ñ»£¹@κØXÜå1ŸU™zfÊdØ ;¼·ZÈÖlo ƒ ý؋˦Â|¶-«Ô?¾¸–ètæ­@6?¯+½W ¤0²šø¬å>Lþù¥Ÿe¶ãu?ø_µ”I³€Hœ›gÆÎ«·“R²®¾G1âvkV©ßCë ˜û d‹ß?òùcÖ±]7z"lµ®L®½f-²ck̃Ùùg™1}?^l3’I¥ýgMD8T&-Ñ­ÙkïZ¢ëYc¶ë”ëö`•æ§.6MÍ$rÓ]QËú£šü‹§šSSœªÛR"ý>êŠ#;Ug§ej‹«~j dJ¥ªâ—yÙŸÅúb‚–;sDMñåÎlL3:õ›.“÷áD–{rvF>1c“‹ ‘¿VUêÿbAmQïy ¥éµÅê;ë²v¯Ÿ£Mã§yòp!z¨k€Œ‰”ðâò?2ùcM!ŒCj±yJð£V¶ËØâUÕY¥~‰U?¢Üç¨@¾«ñ^7Ù£ص¹Lî}s ¯û™°·–d¡Õ0öPá처UÚÿÍ©?ò‚‡Éä¥opvÊpö°–5ÂÄnìùÛþÓ˜Uü[»J1¾_5iÕ°–hv&LEþÅS}j¤'.G±š4û§žØ(!…W½cÒaJ ñKÓGlÒ»Ÿ0´<϶™\‚3]?²ñÍÃ8¢£Lº›_öøºllR5±î—JìzÝúâ·ï¥©ÔÿÞ…Ú¢ï͆ SVÕµî7a¿ ÎÄ·¹­þõLtÇZ°KõÞÀ'ä›D&ŸÐ‡NzÊ^ÿPOT7ýÂؤ+\À*õkíù kƒœèþ­O6fkdÁ6«§LFY Oãþl£%/p¿ê|¶í×èXÜUÚ¿ãÈ Ü/.“W=,Q¹ëÖ÷ÓA˜Üÿ‡ul“ƒ@«¦¬Òüò/l4X+=Ζ"¢è>ûOµÇ{}1ÛÑWM~ j šWšÄºêÖ*ƒüÔ\W, ÏZ†_ÄŽèÆl»AIhû¡+L ï¢r‰|Õ»ª8ê]!ÛØFWü·–íëð·Z¤RÿÜÓºâì4#¿Bk‰á>ÍØÄ´ÃXºe ûÇœI˜Ç>(¬%&o¸%‘‡{é‹osYëëZ⻌PÒâ úo©R©_ðg(=Ýä˜OPøA`SôÀzÙ0™Oj6W^o¤&5s“Yºâ»X#šyj³ý°l5¤f~銞øfÅ9‰ÔÌý–—ÂvN{š¹Rÿ× jŠAöÍAjæ[úÁ¾Èäÿ÷Ïg×P¬·N–HÍ|ºÙSl5äŸökûó1Ö R3ið[¾L’IÍÜ*­¦8;è°@þéþ¾#àuu¸LjæQ 5Å:‹ÛÊäŸÎ¯h{ÃãׂüÓ÷ÿãIÞÖQüoV»•,T½/ö/›~屋 žž½%²UTK±çÖljâ ˜ëú€ôš^„tëÍì¦"zL9!‘•µaš­/“…ÍŒýùWÔäÅý…ô«I¬RK§®¸šóNEÆ]iÿ3zl©I*²ɤkÕœKŠ`1º¾xdM ÛuDÿ·uX÷muÅUåIiü­¹8¦´1H¥~OÏ$ §÷P3­¡Ç…^¬óå(ÅM–Éœ—‰Èm¼ä0A8÷m³D><^•î³Jû÷zLFè6ßK'ÿþ:;”5¾“b×°] &àE_oVñßÿ¯—ØSPI&C舎•½$Réý?8UÛ[BòZI çk½ª.;ʶM7ÝfµÈ[…ĉ¥ÆìIõy”··)zÆÂK]Øåµ÷Àcà8™\š}ÙÜÙÅ>mä¾C"Ey£ç8ȤRÿ‡ Рy5YÚ¢*F®Ķx]¯„ˤ\–‚Òæ~슂W8{¨>Hí¼\HmØœ]ÿˆOíôØ›?µÅéæ:¬R¿‘ý¶-Èóm\aØ¿1lxŽûÜeN2¢:°5OmÀ»ÕÙÊ‘èq}.«´_¯ö¤döiüa2~}ïÍ{;c†ù³õfÂÖÒ…UšïÑ¥±8¼W…šÞFÜòÀ™ý‹§ÿä§õÁO »N©Ž†ƒÖ°33LćõºJäs‡¢Iöã-TÏ49¿V_T˜Öc½Å 4;\&_?;Š“ç"Ø¢³ñµ(„u)ßû0V©¿Œ—Âêæ™}Yhà<‡í!ÁËÈA&»ÍÈFEÔ8öeYš4éR ðÇÒsYã—j¬ÚÜ“}›ê§v°JýZM_ ýfµ@Ö¶Ë •ØSnéhS&“†Ùé068Ä®œÓš{ØŽ®'±àç!ViyÐd´.7¹çË$¤&µe/?™·WCÙVsÇáÙCTš¿¤QGqX¹«š,ýÚ^ô½—ý‹§:Þ¿|Í Ö› ñ¢Z6ÎÜXÜ{¯£DníÑRœTZ—}óî§0ýC#õ—†ÏkÍökü{dÒcM"~,pbÿŸÆ ­ 슎¿°#ê¾D*õ7ëp\˜æã*‘óN ÷3‚ÙDÕ-TÜA&}7•aȘ_ñÚ [ A6¨°CìF?¶È{ c“w8cŒŽó¿*ô»ü«佟²oŸ&øhñ† ÓJÇ´U2y¤Úx¤`‡èŸÆÓî¬Åu,<ÓUÚ©? >ý@6)™€CúìÄ¥±áî1y±¢D8}¬¯D*ÍR·¹¸0º‘š¬èWWô¿Ÿ¢"ÿâ©:½i…ƒ¶^*rùu<B¹Áw6äg^\Ù.“6‰Ù˜ûc!»âaqGzO‰Œ~×Vüø¼7«xJAc0Ýä…@ê«F£qÇB6°÷$áø©#yîÎ=!wB«xj™· ôœòõâœôðaÿ‡SÕ|FwL—HÍÜ)þæÍHHÍ\µ»=öõo/šyç=­E1ÒU"5óNñï1wÚ6š¹Rÿšé‚i×d‰ÔÌï~l'†§u•Hͼ÷s;,ÖéR3ÏÝöAÈ·œ"‘Ú/¯Ç:Áor%šù-ãkp¹d%“šùϤúâCËÇiäŸîÏôAÍ3R3ϚжJäŸÎ¾´®öüÓ÷ÿã9fi)þ7}¼Û‹©!CAþèÒI´°a ¬.¢ØK&õÇWS:Ícýš*D6sV““¦ÞT×5­!‘)§…àÅ~¬ÕáXàTM&¥„áê-[§¤ãÏÅR|³Ó¬Rÿ¯%1`dòŽcŽÆ4gMâãÔÁ!]AŽTSú˜3mð¹ÆÝI‘È1Ãúáà•L¾0>«šºÍäÞf·„ÝåKY¥~Wzþƒùk®©I§üŽÈ Ú)‘½~ïí#Ëd~žâ÷$²Å-ÛÒ.ö»«­ÐB˜Ê*í?q.ýǸ‚œ¥sëíd÷ºÖíâÕäÇc?ÑÜ+R •æÞÑZt-•ȶfbß¼Z2ùOµ$¼‡8¡ê4Gfô¯ÆMeûVïÙIdþ1]ÑhÈv™2!V}/伩·n….c¿l8‚C?Ód²VÄ.¾ÀV\ÖÆ#»`vC\° ªÎ*õ_©US\g#‘‹oÕ_:z²ŸÓ½„CûÛÔ?ðI0îúY Ã[ü’I³G²Ðf\;fõ9õú}-Øz‰%ê)U~I¤R¿÷™Æ´•I¾ðß¾•îç‚m‹6²/‹ÃqiôxvñW]'é³[/ûao@WVi×#ç`åòð‘³èóûë"i–©%J7±FoôÄǼY¥ùyOÍÄÝ‚¹L&¸·ç_ÏþÅS=àØ[l²l2È„ï½Å‹Ç³Ç/Ö‹ãV d“©FâÔIl·Üz¥—¡&gM·…ûê8‰¼2kŽä¦Édã”5ðù°Ÿ=8Ã'g[²8“ÜýY¥þÖÞÅðÙK$²æ÷Äö»Ç²¬ÚÂzív®ôEÉù1§ÉÇ"„I ]d²Îð¡¨ú\6YGõgJäÂÔpL$“JýÚ³º rñ¡þ ¶E¬¶8KŽe“r>Ã,ý«Ô_û[k1´É‰¼°¨ƒ¸Úm6ë1½7¶íôd'N5¼:2ÙÄǽ‡Z²÷–£Æ1;¬U zÞMds2}±|q«Ôïͦˆ¨rT&ÅõaïÏfÏ|îÚ°®j‹†YØiÃncãÜLvùèshý}'«´¿W™„ïß"A6^&a檶]e}±`ÌJvbf)šMïË*Í÷þPSüòàK_²×n=Q÷VñTGØöW>í²¡Awñiƒ>ì²Ì^â÷«i¹=L%ât0ÛëÑ(¼ø'“u7wÁ‹û¬õ¾ªbbfk¶ê¾æâªcÖϹ C«Œ`õæ×þ\¬&•ú_ïd*f;Jd/31[7„µ÷x$ølQÉdƒVö³Ùï{ŽÂîÙP¶Þ÷Û4w1[yS(­ÞËêð»\³Jýޝ­†> 2ù²ËáøòXö´‡®ØÎ½ýµ¾èÜx;C÷!Ýmšº5Ê¡©´_w˜„œð›·Kè²-‰½4/ 6ÁÝY÷Ù«°cÄbViþÃ*†¢Éó9 ‡­k+^Ôvbÿ‡SÕ|L-ºŠû3º€ÔÌíœDÑûú,ÔÌ'Ç7Ã2ãt™ÔÌ{ÚwÛ^´”IÍÜ5§ñÃRMjæJýè)Æ÷8!‘šù´žQªY+Öɤfžñ¶mµ:ç©H¥ýÃG‹GŸ;Ë$JGЍéÆöørÎs±›:¿ÄV×TViþQuu1²UO™|Õ¯¸Æl5ûOõMÒbLê-“ƒO:`ÿ³ñlpÖqF“ lßf–â§^ì¹™Ÿ`/ý«÷™ªbÅè4vCD¬Åýlë ¬óºÉNÌy#Œél$‘Ó¯ÄK‡6ÚȤRÿ{-Ó1`¨-ÈÑÃb`“×— l%¾Ê:wè+êU­ö¯/†ˆ?.FJäG3ñŠÙ-öR\s1À³L&{|øŽ ѬR¿€‘mEýÇ62Yò¤«˜òz!{`a5qMÛ?·–89!›½ý`žê™³¯¾& nʬÒþå߆‹«¯{Èä}µµ¨Víd/<¾Š­í¾°‹ëBãUÏŠó“;ˆrc2y-¦©yý‹§Z¸Ã~ÖËdþ^?T)óüWu1ìZkxÞDœÜn;mèwŒö}Ê^Ë»Žº»²o<»¢WËw¬nïsBvåÖäØ')?cšD¶Š?)|y!XJý…ð4üÖdlÄ6ú´d/û‹o& dÁ[=±kÑvêÞ¦b+“@‰ ¯)–9oP‘:j‹•·Jä¡£æâˆ#i*R©Ÿî¤>â}+'™¬-BŒé¾‹Ý¢SSlÿõ>Ûð¥¶h±ý Û4ë–Z½>‹5M™©z§ÞÆ*í7í9Dì™ô_4XŒ¹êË Ûw¢›ÅC¶óÌhtñÙÅ*Í5\G4îõH&¶}‡ª3’Ø¿xª5 ƒq¹Y¨LüŸÛ1ló#MEçá~졬:â›;ÛX-«Xì¯ú‰¹A+¯²‹:d z×SÙÀYCpÃÚ…m4uÜ’°XÁ@Dÿ⩞ ÜBüÏÔPÔ¼z I-<Å]hޜ̜¹ÍÁSK{¹¨Œ4Äfr—zDŽ™ áæéì'nsGqŸOR„û žÜÞö™‚±U1×2.†dxuãJõ/ôkE^5Útð²kÂÕžã¸÷ºS2÷ÅZ®y}5Y{‚[š¬ݳ)÷žŸ ?¾Î}9JX ¨a6ÐÃ1• RýJ#@ãÛ*ŠNöíÉ/)·`%ë3ÞqµMv’®Gïqø×‘ÄðU?REF4µb¨Ôþ¼½öðy÷:Š^6²…0÷m1Àú¬sæ¶}`v+ò¹RóW?ë “½Gh\¸+´x§ËýNUö¹ØéŸoËÆ×)*›?†ßà.¢²ù{M ë>Ѝl>mŒ>|Ø·–¢²¹ÇnEðPÞFQÙ\²y¾fLPÙ<øòQrÕ®‘ ²ywÒæ×œ#¨l>YO€®móÚÜ~ëBMÁkúaŠÊæNjùDaØ1ŠÊæ¶C2É€ò;mîþ=óûÁ¨Àµ•ÍãOt¹G‡(ÚÜù Ú^02i˜ˆ6÷ýÿxŽÉGÓTéB`u'@?fÁÕúܼ±=ÀF_‡¡NŸuáò5m®’–"Ô-þ(¢á½ °¯M-ší&ÖÆ<½LÑ=Z–tÕ¨ÜèS „ý”m0‹&Žƒô¹Rý®'ˆñÛú¤:_Ì&ï)ÚqãNâ°ßŸ¢ðžÌ¾µ™[æ;’šOLÐY AtÓ–õÜ×+B×ni©ŒëXÀ•ê·+N ìZÞr@©XpmäÖ‰k–DôÍUkb¶€ ¨ÔþK#69(ôœLÑ[ã­…]îÜïÔ„Êêwµh÷$=º¤x“*5ßゥ\‘EQö½€Œ½Ò‹ûOuGŠô7m hÀLk²N™»æè}²Õl?E ºC:”/ã:.…'•öÜs»'BÁâ$®ï_¡`Ü,Õ5\HʺÞå²i–tªÝU‚j}Þ,åßàJõoxHÄÝr)j©<½vGB4wç8М¨ËP+«Äl{gî!¯«‚{M-AÕWŒ&iÇc¹ÇÔî‘E¦º€œf‰¯Ús¥ú÷Wƒœ®zµHæ}¾ÅÝe\I®êÆPgÛu¤o'®Â‘·ÂÞþý)z^4§']R¸Rûçg­ܬœ(j[uH0ÿåÀm1Ÿ¾ùäFÐà:Ú^kWjþë[òàY<”¢—'t?ËJî_sÔô$ïäBP« úgp¹ˆJõýÜaú{/Šž,ÎÒò{sêl"ÃttØß—¼|%ÏÕ­žD‰trëÄpÎêZtj} Pz°ˆ û渓Œïn\É¿Z¤+øÏl 襶Ö0ÿéO®òÌ`²é† Cûµ™@úÅ>¢è´†lªsAƒÛ6ø²Xf•ÚD÷‹p'´?Eo›ë­ª^ܺ4»w°ƒ {U¿ ãÞýP©ùt„"/U†5|&#šrÿâ©~Ò³„qî4߯,;ÄÍkÕ§©E#¬AA?é :)\~>5Ñs¨¯DP·ÚÄõµC/ÆÖ:$^Ñæ~šO­‡4䦱`aªÆ•êŸfÒ’¼Iè@QÕâÁ¤v¦ wüZ¢°è,w_©Yze=÷UY~­µÿE…nö´Õï—ÜÒ¬‡Â‘š€*÷ÞD¾nêÊ•ê÷à–Ô_üEÐ+ÓÁ'æ#÷ú©IäéùrЦ%®"}ŸEp?Œ!Z©EÔým4‰X°—¢Rû¿÷!3O÷ ¨Õ)7bôÏodÔoÄmaÊà¶ÜÆ ú¢ÎÕT®Ôü—/kˆÓb†žÉÙIäF]¤è_<Õ ý.p' Œpçé‚[ËM\èë Ýî´Ñv|Àîæ îŠ‡^Ú„´jo¬[Í혙P³ îEu½-¸ªÆs·#Õ{¸Û¯P£ìm\©þ{»‡“{ŠNÿ–E2‡tàžŸ²•8—{r›f¼!§ºÙrÝdRÃíA"šûf=¥¬* ­;î#Sâ¶´…f‘pÞo»€JõÓJIctàT/Xë{”{DÈ#IG(ºtïY¢Ó«#7½ôµÃ€Íò mï§MÍ".PTjñÄÙÄl“9EÕj£ÈÓ€ÜþoGQC3¹‹ÆGÓŽDTò·ê½»$òûPŠ^Z¬AW7‰èÿpª²Ï‚¨öp·c"Aes˳`ŠÊæµ­WBÔëa•̓ßf’ãÁ=)*›§E £w“Ѝl.ÕßKØCœë{ST65ë»yPT6ß¿4‚MlPÙܯ¬]“¶Ž Íí0p"Ì + ¨löò'q8Ô‘¢²yWß}ö•ƒ'R´¹ûK?§»®½(*›O51¢ó?ŸÐæÎ7ÙíAóÞ‹hsßÿ§ðOü7Ý­$ÏR&S´¦á*ù0p·ñDqÝÜš¡bi1Y }‰¢CJȲ'—šl~›&€ÝNåbzÙ¢rqY¶…/×Ô¢ÅÊjc®6ty1\ë ¨Tÿ“«ÞLStÁìÑÐ!E•{ÖøÊA#n¡ëKÁìZ+îzê•{ (Zå3~úÊ}óãX·š Ë:)9þF•êw$ï&¹þx7A­s÷ïm‰Ü´Â'äS;W†Ëí!¥ªc¸¦ %ªë´2Ü–öýÞ¢Rû×ûôÂúj‹¨ul†ƒÍÈeÜíë£áj킞=,SL¹Ró£žl!sOÏ£è‚ýÄîùw®ÔûÍ8UóÚ&bpa1ETa”ÕÜoÏ“ñ ‰Ü7at•1×ë˜.Õ ôõŒÁfÒ—{´‡)|@Qú#‘iÉ14ªe ɳ-%¨ÖÌíbqÏ·*Õßúµ/(9©úíÓ\¨*Sâ²½¶$<´– G£{’y»sïèNÓòÎ UØß’úZp ÿ-¾ó›¢Êéå´­½Wªß¹×ñdˆµ'A¡»)93Ñ’û#3Ž\öÎÐ{õnd^¶5WQù}·5¦¥ÇÖPï”…•Ú¿·¸e­ªåMHÓµN]ä:OV„‡³Ô Úª>x÷ÙÉ•šAêHëGv u=AÒõGpÿ⩪Ÿüç›[öFŠ<6…•mvr£º›Á£ &¢ZŸ‡ƒåÈ\Óm.Ð÷§< ¿>[Býïõ­hjMÎÞ2gèç^9â ­NÜ©£”hh½ )÷"Ihq¥ú·I …ìåMõX³—ÝäþÐ+VΜZ‹–> ‹÷¼Ñ åÁmƒ6C.:DÆŽ¾BQƒÈ@±Þ碀v™;š(.^MQ©~ ^8 /Bõ ã!Šå&ÜÎÙ¶D¹¸=C×W¸“Nì6EïMý*4þîNО†$T»/E¥ö_RŽ MDT£(VüV¢híó}d³¿ ÇûÀéŠ\©ùk “ÑÝ¡:{…«wSô/žê2õÞPÙú0Eì@çÖ-îðqÓÁÍG‰{}å"8¦3•ëløŠl¶ïNPסDoƒ×wõ8ªmrš¢uk:Ñg'L¸5Q;„¤îë´Uá.ê¦ÑÈ•êŸPϧï%讪((/IævôG×½­Q‹[i{qi-z2N$ÏÇM¢èÙ¿HÃËlÝ0\[LhÆÐ»ÂéÚõÇ)*ÕOõCWúqá,¤îåá܆½‰¤pùŠŽJ¤DcÊ0î¾~~ÔìK 7¨ûkjz¡%WjÿÍi_Å’T5ŠŽ Ð¥5{µ¸M‚Q'[Zÿ` ¤Ë#¨Ô|÷å®ä~ƒ9E?ï~FªÌ<¸ñT-Ïó>Pôö°¡ð`˜2C‡^ C‡•RtÕïYÐÓ©ž»d¥Ió9JÐ-³†‘„5ú€æxÌr¶ìÑ.ý3HbÏù­ >%¾üCÐUãˆøm1Wªÿ¨œXóf$AKî…BòÇ?jd_EË<µ úàL½šÇ¥çú‚ÍvÍû9+u(:ëA彋ˆ†þØMÕ•QTªŸòí4š—ŸGб™ÔßùwÅýdyn[ŠÆ„@ë jÜŒqh‹˜LñˆŸo–ˆJí6²¡á3u(ÚB݃Vuà¦i??'èñWã‰ç‘b®Ôü9yáÙŒGÖÖ¾‰ýúOõ Ï0÷UgèfæÝË ¸-j=`hI[®zy?èÐÓ€ëêxºY˜q_˜@í×%è€õ!4äÀ@k>o‚ê1±ÝÚ1*iPTªßãIhx¢Ÿªœ+úÞ:ˆê®Œ!Ç¿;24ùÈ|òG¾3·º—œ0þË‚Nø%hïË¡¨ÔþiC«Èñ®s]Øêi7uw†m*XDj2´^Å `±Wj~òDm:ɦC'¿5c¿]¤è_<Õ|“n‚‹º KftNxq#–EÃçñë(jº ¼ ®p¯”¯§NO´ÍÑ^¢Óºý\ñÔm³C- éjŠ´Ø.Z¥µѤ쯴¦Â“ Rý“¢Z¥¹5lØ#ºU0nøŽõàô‡ g&Á«Ô\nÏû¢˜{ËP«}Z¤×(n6 sM@{ØYÃê¸Ö€JõÓø ÂLï$-é¢E&šZPt¸~ ñýŠ{(ó8 VÉávcÿ|•½­ÎÐèÍr,Û|E¥öë§\'´PÅœgä¬K0wÊ<5ˆZNÑY Á à›€JÍÿYK=ã(ºt€)<ú*rÿâ©ö=­MZT4ü‚# ´Êžk‚[3ô„\$<‹ìƽ6ð€°³`œ€®ÛðÞaNí*Ц¬´";í¹Ú^ÈZ¯x®\ÙZ‡Þñº€ÎMèD®lþ& RýÛh˜Óvƒ¯Rtʳåtû»›Ü%Ogƒ§Š!AwZ»1T©n`hèÀK7‰VÂ~‚ÚÞÚs[.âÚ/Y ¡:•êçZìKÔlÒ)Úé~4i¸w›«££ ›FpìíF…:Üs¦ïÄ¥G? h©Â,’rBD¥ö;ÿùDÄ· M!Šphb÷hè\(ùП ‘IëÁeªE¥æ7¬²…Jc†Zh5=jã¸ñTožžE„Ôþ€ª´Š&Î3L¸35'Áû£6 ýœl ƒÇöç]H/40ж8{ž»É;¯ ÷¶bè9XGV.Îm‘ù›ÞLÕ¿_I/)ÕTªÿóïÛhEÒyŠö[qš¶”ÛÇÝFž“…Uš}צWgpå’N—4¸Þ‰×…’ÈjíñPn J'hR†+ÀÎ>€Jõ{@cI¦k;†DÌ&¢zrõçO†W÷{PÔ®dLú´ŠKíE­*e†þ°ûI·íÿAQ©ý#´†Îë窿DöäÏâ.[˜ëä~Stk+W8ªÆP©ùQ‹ë³MÎ í+îZ׊ûOÕ½b QwU4`A iûüA=ÇÉA£RW††-a¤$L‘ÝòÍžª+ žêg¨ëÙ­}× ΈÅÐK·ÒÚº\µü9¤»³á.G#wèQTªFé3ji•FÑ$—¯4l¯#w»×ÒJá3A“o~$Ãb»jqÿ‡x<5€ ãWQµG_¹³®'APe(÷ekNl;(¢RýÆd›“ ‘Cz¬´@hì9†«îq‡(ºFa¼VbhÑ¢ ÔæñÝ{E‰ø%Fr¥ö¿ ÷ÒüMÊ6¾\‡:=˜56˜¢grmÀ°Ÿ5A¥æ{ÎÏJ3)ZlXG6•$qÿ‡S•}\tÖWæ—*›O±ÞJ~T×PT6?=ߊ>z“ ²yfò ñÜÈJŠÊæÛ¼t˜Õ‚ÊæRý3®þ¡+NíQÙ|qx?xTÔPÙ¼©d ½›Hý?yž"D%?$hsû…³ÌÚÑÇ<*›ßôž6Í*›OŒH'ßNÖQ´¹ûucÍá› ²ùcõ(øruA›;¿Íb,Æý¢hsßÿçB8…ÿ¦¥ª´·Î¥¨ò@e 8Ÿ{âû3Ò?ª ×Á¿+ĩ֊hå'M¸îåEQ¯S`÷Hî›Ùà ֥ˆûxº?tÖÒcèÄÉvd¡ŽHÑ[çôYÇ-[¹Rý‡wÜ —ß÷dhȰµPAm¹_zsòxîLýsäµ±·fE6QTÁu[òU¸7[;¾ƒ&\\(¢Öó®9ÈÿnGQ©~ߦA®¥ Cå4c@Ëׂ›™2ê\¸w†œ¶ܼQiWËÜ#Ó ¨¯÷ŠJí?t· òßž£hž÷ØÒïwÕ4¬8‡¡íg:˜&¼¢¨Ô|M#¸2d9CW†ø×†.\©÷›qªößÕÀn¬E“?Àœø2-ŒšC[SÔÅb5< Žãj(ƒç– Ýy×ôNåÊ{t†c§rO,µ&gÏÍäîñ‚žËAP_±9`µŸ¢Rýãâÿù/¯°cèD…ùð+¸/×ÒlÉzbÆ ;•„è?¥húª¡$ëz÷¥e9XÌ-IÚ% ÿíÊеÇиú\©~Û½fÁâÜÁ ÍMr†ÝÞ#¹æ™ d@wG®YÀIBÊ»pg¿!×oPôÃ(J ³C¥öŸñ(„°á(š{=jÿ¼à®V GÛër'U‡D§\©ùª[‚…8ƒË=×-Ä]·ãþÅSM/´‚6‘…µhË ŽðtO[µî C¿?¦èÇ~9P^dÌÐMŠûÈ—ñ‹¸á¿B„#EÜu¢xS­×nŠ©àäÃ(ª¾ð0 Ð’¡awßкö±•êÿkßXX¶¨+C•àÑÕÖÜבgÉ«ö©í9¯Ô$ÌæšiŒ‚ öq'E¦'† ½óy>½qQ‘ Á-Ú¾(*Õïc 8¸Ë‰¡ÃVh@bd?î¯ó$xçkŠFNrl=÷@e=\>€¡eM{hÌ£©•Úßø)ÀŠ?¹5¼å^3î ® ãÚ¥d/ r[Ë•š_ï`vù;)úô²+\Ìпxª7ˆ7ü,ÎÐÚùÁ Mor¬Ì€ž;\:Ö'fyLáêÅ ZŽnËŠÈM‡ ŠzlI*åÎ?? ìÅŽ U¸« ü91Zä:Cçú›1TªÿVàX~¢YÚ†º<›{Ði˜%oåŽß³"óê:sœíéÏ5¶ŠÝ#¹G½ èúnc¹«Æ+°Ìò\ŠJõÓ»ªJu:Ø[è=§¨[‰|±î̽1}t6= ¢á¬íDQ»‡µdjê$†Jíoá—ê-ßRtصXøµªžÛ;l‰ /?¡=Æ|Züóa•š?¯§)t[ÃP×bò¦{,÷/žjÓ’X¸ø Q@‹}×ÂSûwÜ%Š^àxÛ¡{ötƒ&KžÙÂĨ}5Ûé5Úÿ‡Tÿó#@׺~%q¢¸Ï²™õñD{ô 5¢ÔOp%úO\¬ úS-):}¶ëc.¢§CÓ!ï¥CǯZ f®S¸)û¾‡3¸ª4…¬¨Çï,O<«: Äë§PýP¡’¿ÕŠÕ`GB&Eõvš‚ñ‚ÎÜ-’¡4þ†ˆ*ì̓÷qNí8†Ø3g1C{žL¤쥨Ôþ›!+!bêоp_ÃÞáê·ÿABp{t*;Ï•š_ÖlìlÂPõ²\2Ün6Eÿâ©V,Ü ›–žPû"h³9{¡«ø¼iÃП/_’žÏ‹)jj>LžèÅЯ a²å"î=¯ÂÍaܺ#¥…N"E[÷§Áö Üé±À,”?‰¨Tÿ1If!Ÿ" ¯®Ø@L¤!A¦Ÿ#Sz®ìQ“·ã®ÕJ$_åR)úrÜ3²}ïEí?R½zÖ–¡ßJïЗ›Öˆ¨äH: æEšˆ¨‹ò87±€’E;`¨ÒNŠj;nƒ×š?¸¿¯ KÖŒÑ~Š$ï©C¥ö÷ô‡®E)Úá™\p<µ?£¦¥ãºþÓk!ýxWj~çôÎðËI¢ã]À'ä-÷8UÙgÀØr®\ë€Êæ7œ¾Õ™Š•Í×'FÝ*›ïü¹Œ¿0¡¨l^wº£ƒúä*•Í¥ú÷/ åã *›ï)ÜH^\þBÑÿ3àzøðª'åÊ<-3ãÈ ÿ-mn?Çùñð¦i¹€Êæ÷ÓaË—Î •ÍŸ¾-ïÉG2´¹û—7Œ„¥´’¢²yßg-éãÕCÚÜù¥#º€Þýé mîûÿñŒ(¾ÿMÕ¦óÔ³ ÏkÚÜ~*ïmA-ëAeóÕó/À¶k+(*›Ïïùù> mîþ~zà9ê6Eeó=V³Éƒ *mîü.º9Ð:áÍ}ÿ?Ÿ1Ïà¿ù¡$²†n%è\xˆÍžÂµøçó^šZ‡Zôi~àäBÐ ³s*\:hèú½³>·O®ì LPÍŸ¯‰Uã9=vm3¼¹? Ð ÂÌÏ…•꟞q”$ED3TÿR ¹>w%·Y©h99› ï>ݧ½ÎÜu £3’¡áÏÒàÒâYÿÚæ0¸T´&#² ")*ÕïùnúÀꪈ*UëÛPtÎ×4x»$ ПÅùp`õ|nKÓŤfRnñ¤x¸~2WjòU:~r“ˆª78S…9@ÑŒ•óhcU7®ciróàJÍ¿ë4n,rtíéèôµ Rï7ãTó6ÅÀµ0U‚þ¸¸îmß  -«»ÃóQêöC€‚˜V€ªÈêlµ)eÞ—½nZCÐì[HøîãܬÐd èXÅz2¦x wzL*(éÁ•ê¯rª91Csδ'†¹žÜoÙ÷Q# 8u.sßs†{xût¸Þ®C¯´œí!Š¢ÕŸÉ„œ½ï°º p@¥úݰèCüî†RôcC¹²j÷¹ívpþçÃzÈ~ ÷dý\r>f>âjj@zõF®Ôþª.“è­Ç‘Õ¶XDŸtÜÊ;£=±8®ÈÐé ,qÓŠJÍoµ±®Ôt¢¨‡u*Œ·‘gè_<Õk ੉¶€þ4œrWÐï;fChhÿ©PYq SîÒCreÜeõãhš¹ÏAtáb\Zú]÷&TÌ7#h§cNÐ3»EN^g‚º*Õ?lRØI`èŽn3ÈGîEç±,ÛØPë‡rÌNtâ+Å@cß{jÙj;,²HÐß—aç c†¶iœzßQTª_̵äwʪ P!»Í[3t5¬Âtâ'?(\/¢~›’5+}5n½ ä†LåJž‚W<Ý~ýEã7eÑÜœ:®ü“E¬}Ì$=¶Ž°Ãq« *5­Êd¨“FQîa`ûqAÿâ©®´t-KÍ<'€kD×eÈ6haBÐÂý» ¦Ì”¢ÇÆ®¦ÇŽ}㶉RcâC¡TwBûßkQÏã¡£Ú‚*^÷û?¹¿>8€³Q„€JõÖÛIÜI;†µûD k丩·÷‹ zÐÀu+Hx»6\¿/UÐýi²ˆ¶ë^›(SáF·} êÙ´?µd¨T?÷'%µí¶j2tPÌ$j½¥ ·}%¬ßô]D§žk Å!Üµó ¨·–úºÀ®¬¥¨Ôþ‹ze´ì׊&F¡!Nª •+¦†*×¢—:©Ó7Õ*5ßêt%x>i èÄÚk¢±‰ ñT/Yõ‚á+ô zÑ¿+R\½޻"… Hß Þ“ú1îÒÑìë n®÷fòû•ˆî¯2‡³o:zËxhMÆ}P0ªºÊsk5NCºþJŠJõsÞl~¢èœ wÌøÁ-Žò–™Í1H!ýÛrÈM‡ø]. tñ=ù­0ñ_Ÿh§z w£`«unPTªŸË´ßþWMڬϊR¶p W)‚¥¸O@Õ² ¡Ý»‚¸Ar¿ûÜ;ÛÏ@DNW@¥öߦZ_´Z¸ú=ò²÷ìô÷tc'kîêiVlž¡*E¥æ/y³f©›QÔá´´\~ŒûOõ«‡>PÞMÐÅ[u`öÀ·ÜÕqà©èÊÐÕÝf€µ×Yùµ¯È ¨kv¡jÈ1nƒ\!Dß;Å}rl¸?ÿçsÒÿw~×(øÝb7~ðªÜP©þÕS—A¶*CÏÔ'Ã(CnÅìEf ªvo1m¹ë·ã‡rá™C‰u)Ï}HÑ÷öCnH#wè—¾àã;‹¡Rý¦)fÎÝå)j­éÅJÌ6 h±¦Ô­¾EÐÅkÒ ä]ïZ OMõ´bG¸¶ûžˆJí÷±Pf5ßMÚm¯:{©iÁ=³æ ýóDÐ:åµ½ûir¥æÿ<Ù@ö ÷¤èžsë  ÿéÊ>鵡_@eó§£FÃ]­*›÷8ÛŸ|ùÞ— ²yZ`,øÚw`¨lÞW‹ŽÄQT6—ê?sd6ÜhgËPÙ|ýWJSÃÆ*›¯Öžy]RT6wv,Š”¯P´¹ýJ«&1ïX‚Êææû`]Ü'‚ÊæõºCâ¯X‚6wÛn†lPמ •Íý¬ŠEmÚ´¹ó'~ßá±SE´¹ïÿÇãoö þ›!EÄ]lAÑïî#`\3®âÐóà»?˜ 7uö€Âî@«Ë]¢w¶ÀýãÉÜŒaÁt£Ó:@oÔ^tÏVsS®ì½3ÇTéתvó*E¥ú_éÞZz*4ýÜ ªúd÷Ü‚k‚ê§Üù×’ñ¯ôµ ‰l¾×¢i+üˆ©B“ˆžm*€Is‚úóý[R|1š+Õïˆõ}ÈngÇЖ¹—`÷ýqÜëÂÈ~ßÅ€6n%þ[¸L‘=ϵç>US£~¥\©ýÙë"èÄ]#-TH§C¼¹þÅ=˜¿·+AscÏÒ9¾§¸RóW*Žõr€®Y²“lRräJ½ßŒSsg ¼j=•¢æž ÿ#‹;kÌZˆ˜½œ e­g@·Ó·¹‡Ÿ-‡Ç×M¹/ŸÄÂþ•­ i»ƒÜY“Á5øò]˜\kÊ-Ý™É,<ì)šñÔ“úõ¾P‹JõÿííB=w^"h…–%½’&óB@÷[? –*ŒûOµÜ<|í}ž¢F?ÝÀnŸ"C¯Øö´´Ïp;91ûXØË)s†U‚åÈ0 f´÷å{]²à ùÙP§è)4àä=Š&E0“ {@¥ú/pJôœá hÞ±®É—píârÁ¹1‰»9³¾¦ruo lЉM"z55‚]ºµ ¢éAÃj†>88„þ|Ö+Õ/¬2„Ã*šŸý¸FòÖ°nI- _?ÏØ|®æúrjù±×â;#oGTj×€júôW ©ž”Fø¦ráéjvëc=E?ìÆTÒTjþæÃavÍ@C/…œã7 úOÕâÀƘ¡ßþó##C¸TÇÁ豑€néT=œÛÿ[:,Û­ÄÐË_üŠÇrÝU> ŒZ h˽!yÀln¿méÃË}¹ƒÆ{±À±1*ÕXUR83аØlòaq.·ÕÀ-à¢eÍíq`&l¡ÍMX:Ÿ9qý亲çÚ¸¦íáÐu†.6J“Ç9s¥úmÑî£kŸPÔ'ÞÊ ¢¹›×zA¿Ê%€6U8©µfÜ´÷­EÓ<‚ž) ´åø @¥öï dô}ÀF@逸{¿är¤ˆc…d‚š-õ#ÞÝ&¨ÔüþIv»‹jš¸„Tô/žja¬.­ðchƒZ8·‹¸œ;¹z¾ó6°¡_zèœõ±g(Mé ]N<£èeóx8b«¨ó_ø|™ 1ßoÓwwm W„!™×*Õ?^÷5Ù•¸Pÿ8=p¶+áV¸(Ãç‰æÜ¾:ëˆsÈîÈ¥”êO&迪Ü΂¢‹b'° !DôÕ¥TÚ1?—¢Rý®÷Ó¹%GEt§¶)Ôîï€þ ÑEͳ}9ð*©»Å{S$lè±кçŠd=¨s¥ö··ÝO¯½Ù¨šc) î^Ê=Ûy T»ËUï4Œmñ‹ ’0.{À¶Cí³«JOMàþÅS=©@‰Òµ†Ö†’ãõÜ>òE ÿꀾë’w&Sô[r&4\-â.µ¼ÞwúòJ=)ÏèýÑå$çs>×föDêcåÄÝ+$8êP®TÿmáÍé"@«Ï„(ã\nB6É:šÀ­XÝìLS¹MÆŠÌÞìEs–ذÏ:Ü53äYÖÊh†ªÉ³¹7{STªß²¥vàîØ]@ã…Š °Ztg‹XrÀ|Af%×îå.ÝSLé•s;t8+¼©˜¨Ôþ™ª[¨_Ên@³ï¥ÐBåýÜ‹Â6®.škî]N;´Ë•šlÁm˜Y¨ ¨ÃÎÕ?“ ÿéÊ>þò/„º Y •Í£GƒÝ—:ŠÊæ6]‚À} •ÍOnè §Š( ²y`“Yhõ” ÿ§ŸDÿ‡Û=à@åZ@eó%'§ÁˆÊ9€Êæ?ìû±Eí[T6vKÿ°‰ ÍíGÆÌ†%~†•ÍW¤“¹×••Í-Êc àŸJhs÷—X„Ò÷¡Õ€Êæ.êŽ4Ân-A›;]Á`Ø`ðš Í}ÿ?žðÝrƒþ›ª÷C˜’²=A¿=™Îêw¦qµã|`¿á8@ëì6ÂÕnܱebk1mú½ñŸoÓÞ\—ˆZêw«’û´8…G†ê´‚æÜÐÁÝ6ƒó#•êï¢cmæ­a(ùçƒHîäná•\x®eèÞöU0{‹ 7^o„y=#èÆCQ?ÐÁäØˆi›(êQÃܬõ*Õ¯î„|?ÑÐG‡¾‘Êo„ÔíYá“@Ї·‘³b¹¯Aœ‘VIѧÖ“òÓ± •Ú¯8¥ú­èè=åc`Ù¿·|Z&k?·®]ô`£Ï÷Tj~îéÕÌw°  ¶U¨§œ=Wêýfœjƒ¶+{Øí(AÃZ±Ë*Ü8¥]`q¼Ž{'±fLç–Þ£>Ò¸WwÆÓ•ñsmsC™)ýèËP¶ÜˆuV+Q¿HeÃZ†nÓ0„JCG®Tÿ»‡jɵ . µG:ØrÓ΂ƒ¢9 Õ9§àî¶‚.ŽÜ ÎK³µÒ8 Ç*“¹ÙîS­Ð@‚ºØ/&•›Í¸RýÚž8JÜ<*gUBºnàÚ-2 ÏJŽÔ¯Þ²¶|K@;z^ï=ÇPoU_º£fWjì—sÒPcÍë0gŠ×£}fµš 'æO¥¼RTjþƒMaÂm¯R‚Æ·{'¸?¤è_<Õø…­ÙüÉmõ>C›6tâ>±ÞŸÏŽ!¨ÛôTxY•ÎmP=/ô +4)$Ÿ ÝŸÁíØ´ƒ™ép»»Ú ›#Fp¿÷ß ƒ²Ójи—´}?š+ÕéÀQdyNÑ6³Ý7sOž.†ôß§EtÑáy°$øEg§‚2j hö– Ü9 9{Ú3CÇÖ€†OTaů‹ÿU¢_ÙÐÝ$9¢PË ä˲cÜt“«â‚‘!Ü73jD›Œ­Ü6WÈŸ’]h4̈3g¨Ô~/û{p·‹ ç<ÃÊw*ÜeïŽÒãöQÔ¨•kÜšLP©ùFÑ¥â+¿µmˆº\{æÛ2@ÿâ©6¹†PïÝ tÞ.æ÷éÅôa hnVàæ~Þ¯Gq Ïþfº{sO̯¤)r}¸í¿·­]û± w̤ÖlaÛcÝÕ6‡lXcOÑ#w ©Ò²l†Jõ7†‘7>®•Û1—¤jq×iÂ0á ·÷¨GIJÛî[(yþ]@}cߥ¹êÍÓ^º*€ŽÞ×–¸¯'¨T¿– U`|é@O4¹þç¹F [ŒßáÞ÷Dt¢.q';³Ñçk(úêË ¦aCP©ý»Ý_€ó_í û:t¬çúkÿûŸŽt[ÃFÒÔ:•+5?¦Ï ’¨qP§¼^`hQÆý‹§Zj›$x·ÔÌă8o³än˜>'r¯ø¬õ›ùÜ0·Þ,[}·T_‹eNä¶g1,`·w\|(›Ùz6·ï›.Ì}نξŸXæJõ¿°-•\9¬CÑGEäMOî%ÝpL+{Cq!TgæsW„v„ýG×ôüÎh»<ž[9£ˆ|n è§™« 0½WªŸÁþápAå {Ë&Á·ÏÅÜVg*IHÐi®qŸÇ$8¾‚;?§ž*œ)¢èçò~Œ4z3Tj¿ý¶·ÐaÏU‚Ò÷ï`ÖhÊÝ|`¯°yÇp@kηf‹Úq¥æ‡çÖ“3׺áãqÄcî_<Õ9ÖÛÈRuS@}Þ\#Û7ëqul†·Bî¥AeÐóU7"|•8öj·ŸÅGÒ]È]np›Êíòå*r,¿_%è\åeôì`è¤VêlåïK•êï·o/þ ¢Af{ˆMÆGîÃüදC…´Ð}ëîâÅ"(®nCQˈPÞS›¡¯¶$ЋÔEí+¹·þWªß¯ß~`æµPvž0ó‰'÷eÀâàÌíR•M‚wåª_ÌêçYRÔïçx‡à%ï *ù[õë;¸´ƒ ¹ÇßÂ‡Ì nÎT]ÿ°P»áÎ4íN*Wj¾Ï´ :)v Z R¸ÿéÊ>ZaŸÉî45@eóÀÐT2PÙ ²y­í]áN¤! ²ùÞ¥Âßt‚6w¿÷Ô×C£ *›ÿ®½NÊ:w´¹ó-ãFBtÈ0@›ûþ<>zʃþ›Ù7 ôåyuÐ<&íj¸u/ÅÌ1å€ú ¼ëöqߦ‘ø[KZ41ŒŽ'ÜC%ßjx'sË”2iíü3ÝÙÑŠM"D4,ù¤m$¨Tÿ=©¤bn  ó´tIÍ©$n±’œû–EѺ)‡ÉÅ †Ö¦d“±Ï·råÚ>w¨˜ŸÉÕì ›§iRôêþHˆ¯/ Rýj}>S•C}P½¦û”&ôæ0Ö”ªèËÜ:eÑ3‚©Z¶Æ‹hŸ@ xÑy C¥öYÐr5Ö24iSg8˜³Ûú¤<›Pàh„¶+{ƸRó•’Ô¨‡â1Š:žH¥]{lãJ½ßŒS}á´ÜUCEÔ[käyC-ºñq É:°ÐàAJÐ)(å_}5™«I&Cǵ}L+Ê—pmï¸ÐÀp‚ö¹wMÌj+p{(Þ#OZÔÏq:ÜøÚ@P©þþÇ òã6:¨!ËÁÛª€{jûháG CS>î§#Vçp&ÓO•ÀÝ»ö0}Ô CÑ÷a¿!pB$A]-΂ór#ŠJõÛݳ’FÔ|"裔ª´FЃ赣a­÷»+N˜ÔM@_†ÞÞíÙÎP§=@Ã4Œ+µ¿Ÿ¾Ük½‰¡”AôÎå.|²ƒ¦ÎËthUy·¼WjþE½‹´í»<‚ÎÞ¤E—GÇúOõðð ¦â) ›“àyˆÊ+5ÿËÚÔ/:Ð[þ­Hîâ¥Ü¿xª¹3àYׇþz¸[>ázu˜…¶swd‚Iw%@ÝW¦-åÔá…3-=¦À}Ó»’®{–AÑЧiŠÚ&‚è=‚]KJaèž}tÝvk®TÿÚƒÁ½Ý4@þN€ðÀ\÷†÷Bú?ŸÉЯ:ºÔXŒâŸšÍàÌn‚6žªžìäŽèg@´¾üÑ@2IüÚ‹¡Rý‚'Ïd-Žº4nGó¯[Î]ø–7Ó@¯,è7&”s ÷µc¶¹§)ZlT&®KÄPÉ¿:ò‰BMC·ÙH³ ÜM}[CÁ»å€Úövq1˸Ró—+€Ê«ãÕó‹8/ý- ÿéÊ> c—¢[·*›Ç›…º0@eócm»°Â m(*›·7ôÃÕW*›çéwP8šÈPÙ\ª?ýÎ>'¨l>å|5]«ÉPÙÜõç.:we¦€ÊæCÇE2“ÓKÚÜ~£³rÙ2‚ÊæúŽÃà‘e  ²ù@—´Ì¡€¢ÍÝ?íCÒCeóñ$µR ÚÜùkLT@gÖ‚6÷ýÿx†wUôßœaÃTLGSt¤©sMÚÂýrÚBÏ÷%hæ“ùð¼ÇYîã§»ÁçMCK2s`ÑöHî]‹É©õUmº¹€ô¼\Î >? B'š0ôyŸ`©ïÏ•êŸõd{y®‘¢k¶§±S†§¹gÕ™Qù @¿YNgñ㼸÷DÃwµJ‚†7®…•)úbƒ>£Õ¹€FYBM+ ¸Rý.ÜÊ„¹™ Uø’žã‚¸Mªw¨š8ÐñE×è‚ ¸J‚A¹ú Cû/úDî^Í•Úo÷5‹Ê]™ì€VÕgÓ¡K£4»è ¼ØÕ’¡#ÜìW¯¯E¥æë®•cža¿ šnMrç·TêýfœêN%'ÖãùuІnË–f(24qt<èih|)Dîöã^Ð)‚ðF Úç l*¬¦hÐdêïÒ‰ë6Ì™åVŸ#hzúxl±„¡¿JfAE¾.E¥ú¯ø¬ÜkTkÛ7ðJ%!!$IZI²Ë>tÇ´Hv !IBHZd—T’v*I¢¤RIH‘¤yž!I”$I’(„$»$»wñ<Çyßc¾ã¹¯Ñ¸]_~þë:ŽÿZëXs͹>¬]G™N×HŠÎüÍ<Èî«ÁŒ÷tÌÑ…Ì`E·ÎÖ R7Ma¨{Ørð\Ï=ÿ­#ó ëh´Y”tóªÙ\±~1Ñ`X°›¡ÝC y«×-1ŽN´«ÜTúa”keÐ Î瞥hŸNdÐù¶ =ýH\Y. ïûDP…wjíoSÓ2÷r§,z¾EQRTlþüŒÞ ùëA‡K©mýƒ§jdÏ&÷ÕdèÀ•[Ø»yý¹ÆEpüÞv@·Ì| K\¸QÏ*áŽE5Eóº_‚m•F }|Äš{¾ Љ³¨YÂuëÞƒä+ÏehéÂIŒuÊë?|ßQV©×ƒ¢ƒÌCÙ·í¯¥¨Áçti? ží¨ü—ÙÜÆò à¥0ƒ¡o—ÄB@KoîóCóàáÖûÁ­z®X?“©›Ád¦/C«ãFCÕ-Oî™3¥Ò‰ßÓíУTúHˆâæ%/bsõnR4´ßfûÈ?Û/Yvˆv·1$èxûP:1È„;ÏÕΖÕS´î!ß——qÅæÇ {FàC M øÙ=î<Õ{ÎÞìþtc†ú·d÷†sK‹òÙ€’寠ì§>wZ¯3d$CSÚÕç Më1†©ª< 誄=lcW]îÇ,Z¿ã0CõÍU™ÅZ?®Xÿí]ö2¯f)©äÄn¥¨öÕóBãò‚¦§u%ÖçÇqSuË`¿Z¾ä-Ì“.änо jª‘€úû^[¯t¸bý^{MŽ~]ÃÐKÅ IÖYàzË/ Þ.ž€ªæÑõÃsç÷Å,úwdè£W.ÌÇu:WlÿìÌÔq/ôDU ½à"p߸/%S—<¤èÀÞOÈŒÈ ›¿j;X_ÍtV®ôèÄýƒ§úÔ$Œåk ah¥p„]XùwkÛ¸k¤h³r<¤îüFPÃGã×*ï¡hó¡ùÂpíBî•ë>Ì«ç9‚> Tbyz½õÐ;-l*•ÐÇ…h'‹9ëßmÁBÝ÷R4þÁ(fu•ë׃8_1$èªõÞ¤CS.Wxz³Ü:F52CWp«êz«:‚F¹îªwÓ¥¨X¿bùö¦†C:0´9D….Ï9KÑÌ7]Ù‡Sí;}°q&—­ìÎ< ’²Ñþï½É÷‚ŠíO[H.'hçƒA4âaîµäG›†Îo»OºUë.Wl~-;CrÎ.4g¦5™œ°–ûOU~çQæ ]zeW$3ÈüIÑa=ö@ã™ö€Ú*XAМ~\9iÏ+ÓBV1tÜô8Ú½ì ·¹xQSiè±ÍZðÕ3‚ žk—°£´ÿ%e6>¥Wô/•§>»k®ÀÐ-]µÙÚ³z\«7êPœ8ÐÕ¦ž`绎[_3Âo+1ôr}o·É„¢cÿ†%oŽ2TYg;ÄZnäŠþlu±¦®ò†MÚãBuML¥h¥÷0–¿PÃc¶…{§%I«j èK@ݘ¨Ø~G¹Pj¨DÐèæÃÔqÉ-òYÐzÈÐú£Ýà®Ûr®Øü)3þ‚Ú-- …œêžý/NUöùõû+0¿KQÙܠѬüF*›_PÈϺÄPÙü·­Ä™ß'¨l¾"}Û5øAes±þ^K´YŽ•ÀPÙ¼%:¦^rT6wÖê•.Jýÿþü ïÀê‘N mm¿Ýc7PɬGWPÙüÆÞt‹Êv@eó.Æ¡BØúB@[½ÿF ­fn*›;†(B'†¶vþþ˜âÓDÑÖ¾ÿoÏŠaª’ÿ¤Â†P¸#õ&¨ÝÜø±-wƒÆ}<ÍÐõéš\aV.Û’(Zö%Fк¢å¬u ·Óª7´Ûî—Õ•ïNg%TliòIAâ²ÊÕ8_.È)C?Áš£ë/ ™ælWoP±ùFŸ®µ3»•L%ÂËM\±÷[qª·W\†É8‚fÞ.€æi_tXØX;WPåΠÙǘÛIõ5È…øÔ拼¤gä ®·B¾Ðô¬†aÜv7q¿¦3S½ù€:Tc;îôçŠõï’×@$FG» ͤûîNR4µ…Bc“=A¿-J„æRtÞ^/xÝq/ 1r[ Wþî‚{kÙi3¹•“”iå·áëW5ì4uñeèm…#pö±'7~Ýb¶ÛøA‡× gšŽÝ øuN¨z«ÈÕˆª§†G¹bû| r~ô º)µ#‘ÛÚÈÕÁŽtÚ¨Wƒ[óªWlþŒ%PìhèÆÌ]PÔy,Aÿà©ZT<õ6‚€¦ü~–Û:HÑø1ËAíÍ8@=lWCXù@nZBD¬].E¸·W+(ZÐG LÚz A ˜­·²x‹üæGPÛ+U¤›]/@Åú“ªÐG뾺©e­(ªro-X›”póŸ’— 5’1íú¼RÒB³¹ý¯™ÅÐû=Û6n¯Tô¯ïÚ£°c=CU<ãANg6Wïõ}ºáÔ@@ ÇJ£†p½6µïrmçL¡ÛÏíæŠí//ëþÏ—ªW¸µIµ¤¯)zqe0›)aµZåÅ.'h*6ßìÖðvT`証}p;Í”ûOµåiØ&¿“¢I ?CsåŠVoØÍÃ?ÔyàhŠèÌ=l6œº)RÔÇ_ l]óÑa;LaÊó|@SÏîÚbîvMX¹)— ©gà÷Ë€Šõ*Œ‚wG.RÔ#j: éÜŽ¡ÆŸæ # u¸1Û*¤+ßhr¥e½ bÇC@­o[€žV>××îÜœM¸Ê¯ÃÀZ'‘+Ö¯iG:h=ÎÐñ¹7 Ò¯÷å¨d2ótO@­–õƒÉª¯¹í.-²™èü·”¸ÖæŠí?0Q‡4&|0Eg¹èÔökTëa­éhjÓ)b¶Õ‡ bó»·‰‡ ³:0ôƈP}þ™¢ðT‡îj‚Œ~{):1ú ,u<ε[ê «Níá^=4 êïê2Ôi“BQØ9ŠÒ÷õ°”\à¾Sê ¬fhtù}brƇ«{7Ãô±j,·}ýxë"÷F|2© ÜhÓ0 ø¼„›9ih„YStÕîNP¸âCÅúg¬³¤;a Mr˜oÔS¸Ùç„øÉ§¸r©îYWÖÞãæ´ùD’&¨aV)1ËœÊ}±~X Gщ¼aF߆Šõ»”òœšŒzkj-¼Z;…[k¸T™A?Zì€:Ï,îáÉ/À¸Í®rº¢Än¥#Wl¿é€ÞDí”@P¹m}ȯánÜSÏÀûò@@%ß* íå.®Ø|ç'~ÔS¡0Ø ý›¤èqª²‚ûKÈ/L¡¨l~Ú-,(ñc¨l¾)ú!uÏPÙ|i­+$ÌØCPÙa¨l¾xõ`ÈÞrO@eóøN>˜Æ0´µýb§—ÂÔ¡V •ÍM|àî_š€ÊæŽÒh¶ŒÐÖîÏO×'áÇ*›¯Úw³æÚÚùmUHÙmíûÿö¨†µ—ü'c_X§sÃZ·S…T@GnÞ¶) ·?› ý l ‹ÜS´¨(ªÿVÔ>ì8|˜:–:€y̹Îuër™z–É-Ž•cªþÜÌåºl~C W¬¿c×MÌUm ÛWoe½Ò<¹¶ý¿d"ÏPÍ“ÉÙQ½deD5%Ÿ¸×öeŸ«J¹ÖM=ˆñä()Ú=¢+ŒoÇPÑ~ÌèÒÃs-_%¡]½Í¹v“ªàž\ǪQ0´¾·¹ò¶ÔmB‚€Îké]ú:*¶¿®ñ0Yàè¨fÝAÒTêÎ-Ó /ê"¹žýKHOC®ØüˆiUpýÜP‚ŽÔV¸ŒàŠ½ßŠS5K êŸß¥¨½iú0ч;~Ýrx`s{{¾9$ ­rKûA€^_qz$rU:Ó#˶rê,銃]¹†í? íÚpã‹”àÇçÕë?ðÎ&¦1x? ½¾,g©FAÜzÃ]¤Eg2EMÖU w&e£¡ß}˜‰éJŠÆø±SHÑe=N±\—ŠþÒ_ÎÎZ¶a¨X¿êN¯¥…CÌÕ½^gêV;{»ÛJ1¡?×n² øÐA܆¡p¦Ù››1Ôê’ã¹¢E˹´€:- 'Ê·s=N¦“G5Ïêj; R#Tl¾«¢¼D…¹ h߃ F¶oKÑ?xª}*®4%µ¡hcpa¶ÇO…lô3‚I‡=jfÜ 4®†p}+ÊàN¿P@WtȆÓ¹÷–IßF+r=£•¡»Å;‚V4TƒïB)z)ã /1§¨X;£I̯å ]tXýü@nþ‚féG§þø¿îß}€vÿ=ÐÅ]lŒ×>ŠzÎÛÅÜU†:ÌËz(rm×$˜6vŽ!¨X¿âg«ÉÃýuô5™¡Ø[Þw»Lâ¾»m ÎÜì¿6AÓæ{ÜÃ7 \ÏŽ`âZ$ ‡å)dô¥¨ñ…Ïp.O‰ _'l†”±Ñë¿2 ŽúÚ èMéçŒM\ÍA´Å1‚û¥Þ€juºÌÝÔî‹Y`ÌЂ¢pÖ’®Æ]ãdÚ+êýÀjÉ9®X?Ic8Sñ t³=döXÊ5TY £Â]aµ†]¿À½òNvy¤T³»È©^AÅö_ˆJ'?>¬4×ñ:Yqb5×ãÒ!X峡ϳrAñšWlþÅo¤¤ý Šo+(¤/eèÅݱEAräó¨(Q6@ÑŽ½mX}°'C÷~8Ag}?Äëï6¶Dð'¨ÏãQÄûã%‚Ž–§»Û4u[–u!’[h_èÑšùPè•CQ±~§nÈÅ7)úkõ1˜5ñ7wNþq8±/ÐŹ'@áï4îº@Ohžu”¡,-’t¹¢?ëää ÷Us@ù·‡C»ÁN–*0ô“b9qþú[ŠŠþlËh¦¡Oü4+žöÕ;Çý/NUö5–šÙ„T6¯ïüϯóÚ» •Íýˆ±|yŠ¡²¹öÖBxµî8Eeóª¼ z«çuŠÊæbýu~­%Õù®•Í‹¾ª3Ï¿â•ÍÝ®ÅàÖ•ÍMª‡Á=[†¶¶_ììhP}ÚŽ¡²y§‚Cà²ù0 ²ùÈy:ð°Í†¶vÿR‡àYo ¨lÞÕÉ_ÈÙ¹™¢­Ÿ-_C×¶Kchkßÿ·Çe{É2ØhŸê3€&7ö§GVžà>^øÔltÜ¡Ïñûª)±Ð„ÉH”¢É+w²†!½ùn=«s͵s†™\ànèX*D?=DQ󟿨Ǭn\±þóCäIм`†ÊµuïžÃmžùQ]HPŸ¬›¤~Ä @§°ƒˆÎŸjt÷o û&:m{!ývéEÇwÈÇÌ7ëg?7”Gès§æÑ½'A³ÇíaÉaj€Þ}€ Úø‘ ;uaŽ—ƒ)ZPÂ4c]TlÿÛ[ïàþ#ŠVŸj€iûp¯|ƒûëâ :³C3ü0›Ä›ÿF%:&DT?3 ²\4{¿§:`o:þX š{ÓçC|¹=¾ÖC_ù3ºA¾–}ZHP>^¬~çD®ýÂ@¦~Í= •œaõ€6ž÷aczÉs?tHfç|¤èËWA¬÷Ê©\±þÎ9Rß) ýì°ŠFÓ4núotÖ»ª›ë ó÷sóV„†;Á\•ªLØþ3™»± ”÷±ã–ž¯KqEÿV褳m¶; Jû\b½Îq‹ÇF±òkuÜ ÝâXÓK9@'±Ô–R4 9‘u9AQ±ý&ÚïáéëÁÍS~rÝÆpÝ–TȯÅ}îõ ÆÊµTlþÄœ ˜¸ÐÐÊÅ¿ ùâ"8Ͼ=•)ü4Ô±ÿWú±]g®Ë‹ÐQ{ bûf¼Ê“k)š”ú ÌŸxp§žÝO§çÆ„~2UK>Ï›±ÎLNµ´~ë¸yóAÿ੪L™B;´Úu$5—ÉÕŠy 9.µª~Sµ¥èf3êœo" 7´m¹’qÙ¨ñÍàxÝ“ ^–QàñJƒ+9Zïí|Õü²Ž/›Êëÿe„?GÏ2´)ÏœZõJå®óõ…€º¯=ÎU×¹ûçì€ä&g®ùw°ú4ˆÛiµ ÔÎ÷e¨QæNPîWÁë·û®›x'A/>ÜÄ~ÍVçö‘_J“Æ–rmNÈv;·ƒ«Xñó–*w1ãí¸bû7ÖÀWÓ@Šþhû´”"¹ù§ÖR‹ ˺Ài›;$Š+6ßñ¥!l{HQ¹ W…®¦ ýƒ§jªÛ…ö”¨äB¦´Ë5CÀpô)úöÑt°vnKтƃDÓbA­ÌÆýóH Ð’¾éðá×%‚îìP Y;õ5Ôšêžc¥h±³Šàg¼‹¡bý…ŠïÒŒ·é •›¿Fªzñ27¤<JÛž´Åm/Ø5®æÜ\ Û§A7•è@§°8ŠÍÅQçZ¢4 &TÏãŠõËËvdÍ¡vº.ÊŽI䇚¢/–)‘üÐOZ»gq$‘Üa}¤p1<ÐͳÏÀœLu®Øþ,ÛG0&/¢ªOJ`Oçt®Ã Wö¸G0C7Z eß½¸¢u>'hC¯å Ÿ—DÉCîqª²nء캞*›/ÈQ˰[RT6÷ëqÒT67ëª$Ñk¯¨lîÔdJ¬[.0T6ëŸúd[¬Èa¨l¯iŠîÚ€Êæã>û“ŸSû1T6o<¿[HüGÑÖö«x²ˆI<LQÙüúœ$iËW•Í›%š 7{L6ÚÚýFûnƒòNJQÙüðÀ—tõÛ`†¶v~8õ¡ÍOÎ2´µïÿÛ³øçÿ~§û?ƒqX wVÃÖ&I¡öí0»þ«¹bý^Þj«Ekž‘$µlnñ<-rMÄÝè7’Úß3æúÖž¦ƒžl%h—8#bÙ}…€Šíßµ/Ž„E?eèe­hÒýÑ3®U¿ÙäК"@Wû'×ößãŠÍO{¹9ëRT?+ˆ òãþÁS=Ðï1T)Nb¨Þ“*pMÇütiãKPyèÎqqÜDƒ PqPU5}ðI8Ëuý"'YH~QT¹ím0Ìi澯ŠgÚHÑé°wT¬¿†z)©wS#hµÊ òQ‡»bNm ÎÔ³9‘*$äp«àÆØ‡Í>vTŠ¿r BƒØ7+u†Înÿ•®ØÐ†+ÖÏõE:ÙòHž¡‘WÃI ëÍ=1­†êvº›ªìËL$4tèF8v6„ SužFÐÛ¿ûa8Q÷©ahÒ§@r9¤–ks_‘h ¾¨á0!Ç/+6þ¹X¦;ä3Eu«Ï1EÏ›Ü?xªK_?‡ Ñ pÿ%´hs×NZ óí" ` Ÿì¹c=ëHåç0@ÃÔž„™»¹þ‡ õÔVŠž]´ bîçHÑq*Ù$¦é³)êü$ 4œT¬Ø!mPÙ&ôšÉ°½¹‰Û¡F—ffj84DpÕŽä6i߄ȶ— ÚÏ2êzPÍÉøš]½>‚[ó 6å÷T¬ŸÑäd¾úh†Ö#óOÏæÚï&,åÈB‚ÊwžÇ~%­á4‚†Ž«€N¿ô%¨Øþßç½H—·µ ŸãL¼ÿåÄí‰äZY ÑÏ¿¹=¹bó­:²™}Ô(zmW'&·ÃKŠþÁS­×xR‡0T³=t|=”«|è"|Þ®HÐñÎ7ÀlÄA5¬£íúúæ4èùhyŒ<ÔŸNÑúj´¬.€»ìë/ˆM;@Ðõ'ÞÂ×°®X½‹Ü-†Êæ¡ö¬±cl6*›»÷2¡74QT6ë?üê~˜¶Ì PÙüÎôÞ`zÆŒ¢²¹ƒs9ݧ¢ÁPÙ|‰žäé«P´µýî”|&sî0T6z…n“) ²yóì]LÕ¡3C[»?'¿Qpþ»Œ¡²¹­áJѽËÐÖÎÏWÝu{ÚÚ÷ÿíñÕí(ùO’Aš¬ºÛ)jddÀäVºpaSv¢v¦ÞL?ð1÷úÇ' wÒPí'r’Ç{ré zgyEë»Ú²„¬‘ ]µíX9úl{˜»— bý=$rß‘ÓtéÐ/`vmwä&±¹AQÃìFöCnÎyÉÓÍ5}îÔ¿¿äæß©"±ïVqc&솩}ëê›ÊF'w¡hÚÛ8¦å¥Ä}©,)PIÐi±í$/›S¹ª3c‰îó#Ùh—âþ°«ïk‚Ší¿òW4 s£hÿÐ8æ%¿{|!ÈÕ•š¢Á¦ˆkœEÅæï<Øœ Ö“Ö@Âç®Øû­8Õ ûF2kÏÓR4±zS‘¯ç6~‹f6{ŸS´{NÛõý&7騼äGÝW‚ªNz÷§|ã¾pœÁa ïÏ.éÊ `:äà[}®Ã4U–êÅëßôæ5”ŽˆÐÉûŸBŸZ®V¶KV¼LÑökG2·„éÜúOÀÇIP½{°Óç3AW ¼K4¸Ý)ø©\±~_ŒÃ™îÚnmœ±Ÿ%/3áÞiQ–˜Ðý]8LNâ“·[”ž–û-x:(‡rÅö'\Kbÿ¦¨N̶%dWN[‹šõ{Âms@‰]¸oÃP±ùõþQ`ºi* Þ÷a¢ëvî<ÕïÑóYÄ"-Š~xëÀü×»>‹¥ÜŒä:$g°{+¹ñç« ùlG@—e•À€Þ-¦Ì»÷d¨·­'{|Á€«ßN‘Ý~ŸÁ]hÖ›IöFpÅúÞX3»ô»w<ÄU[w“Êù˜_A««ošFÜú- ÓŸfA¯°0‚~˜êÓÆ èF«$Øw}+ síûÁ…p‚ŠõS ÜÉb†.£hPûµ,tèß\‡è÷pGs'A_™VC÷’ƒÜj§+ ·,P¸L8YÈÛ¯Ö=í¯]IÑ!3˜ùó\íÛY&ÙËÐ-K‚ØŠbg®Ø|ãSP}ØÐÏ÷j -j.÷žjÂéílË Š6Ð}Ì×l÷÷ìSìæ?'‹6G°mcæþËŒ ßÙ I?\ÀC®m6šž¾ŠmoÓDÑ_m˜eeO®dñSïûÜ˪9ÄfÝ!‚Šõ1ú&DÎEЂ£ Ô ¹Á·ªÈç•Ú\Ç~@Û®wÑi° {(Em{'* ÊÕ«ñ ûsWt=M=»2T¬_y¼5S¶ÞHQïÉ,Ón*·º_)ø];IPÕk· ù nÆðprÂOnî˧ĹÇ&Ûÿu÷e¦úb2EGÎËf«—ã&mufN¡ëZè0ŸÝõræŠÍï×£&/µôLÁUè•?ûOµÆ.”©µó¥hýãcìÈî‘ûÙ† žÜí—w°/‡¸ã6–Û]÷¥¨ÜQ?S³ØÜñÇ 9›f hýæNÙt‹ë2o¸ß=hðã£`øü4W¬¿Ù‰ (¯DГOCÒ•(]w|h¤éôμCÐ!-ˆ›Ý¿--ÿÚ“¢¡¾—¨EÞnÏãÃÙÜ(†>¿Ô›õ¾šÆë—|+¶þ.Em~)2ã%JÜeo³!µ|Aož‚¤gò\7÷ó‚ÅÛ\Šê:—Ò–4o†Ší_ïNÙë¢^ÑÌØÛŠÜ½ó†3—¾ Õk±§ó廊Ø|Õ÷ pU¾† ~×-Àrˆ»€þÁS5ÈH`¡…û)jºý›˜»‡» ÂùO â®q™ÁL/›sî¹ÓœÔ¿¹S:²~¿¾rþ½r³=ª¨w 6Yªz~WÌw¹È½² ~êpÅú—åÉH0EµËý Çb­ÍM÷{55~XfcÕuX¥ÇnÏúIÑÊS;Ø–ëú ýØm%¸?ö]näQT¬ß¸§©åRW=–óZêq¬=Aïi@޶J6:éÎ,МûIŠŽ\±œu8ÆPÝšÉì×Ýp®Øþʬöøù#)š{=‡Y]?ÅíF‰ú•Dí]7 n„Tl~Û oR~ò‡Íœ+5Šþ§*ûìÍÌ`))*›gÉ÷f1«Â¥¨l~p­+_nÈPÙÜmÜCP•ÍãçÜÿçWµÔ•ÍÅú“ùë ut“•ÍCZ`éÀÎ€Êæ«—űáj}*›_ùŠ´Ô$hkû¹wlC²o™T6ß{â+ѯIQÙ¼Ëpu¾ÆÐÖ‘Ãüv•¢²ù‰>^ð¦T{á1†î1 ¢sŸážÊÓ9#©¼tc×QT²`6]øB…¡z)i\¯GP±ý>¾‰nÎe@m+jHGÆMž¼…JÌÖ›¢ÑG3ßïG*6_µýTŸ (óïšó˸ðT/mÚ= j·ltø6ø_^,#f†šÍž¿”¨·ÓP:­ Ö¨“¢­¥–rz–p‘L )b¨Mº5 §éÜ©o?CDø3Š&tŽÉñÙ\±þ³&«3(´öšSp¿ÉÕ6/cuðr’~™¢ªN‡HÎŒSõ‹Ê ÓýÍ-~ö·TF‚>~ßM ÈàŠõëöϧßñË õ}úƒº%æpû9ÂŒ÷%}ü9Š—öf¨–Ÿ Ìyô› óÄB¯ikÛŸò¦‘0\@Ý·þ"*·¸ç63½Û:#ýËýŒ+6ÿëZCˆP½¨JGm°¸WÂýƒ§Zrèw4!¨wæ>ø¬4†+טnZ•È-G";úØ;œ(îP$h{•9P}m€JæÈ“7ç:r×v2H7¢%Á—Iª}[®Û/561§œ ¢Ÿª¶mÙm«»€f;5Ñ3£J¹gÞ+JLw_“¢“Ï·•t?µœ¢¿G«@ÖÀC€Ö§Ž€«½ÎqÏ6Ű”×ý¹Ëä°à•6\±~:vg•¯1T X® ãNÕHÛ³¸©ðýÐ&®iÿ*Šô†C-L,\ËÛ¸·24ßtÒ× ±(æ^VKc«r´¸Û3ë„+6ÿׂa°Åâ E ¦@Ö_¹ÿÅ©Ê>õ®‡!bþ@‚Êæ*«Ða=æ*›G:üó+òz°•ÍN¹Ci¨)*›‡+îcÌu•ÍÅúïþõ:ø>T6ï}·¤ƒÜ~ŠÊæû]'zÛl@eó)Ý—3\@[Û/dø`6òíe†ÊæŽg™CeóYÃr`Lül@[»ßådWh×á> ²ùêJ ö÷M@[;ÿÉ¥¾ì¥hkßÿ·gå¼ÿùCþ/ZÞÉáÙ µl®‡Ø[ó¸Fžk»‘Ü‹Vaàú ÷J›jØ»à>AÚÝ€…ó¹V‰pñhE ¾Þƒ¤oú íùP "/%rÏy•J)*ÖÿlI莟`Іu|rWŒG7}̓Q!þ€.œw ¶.\ÎZÞš V’“#ÔO)’ú0´ËAX¸£?W¬ßÓõW!vëQ@ís&#§T3tøÍY„¶»Î›ßãc01¾'!¨aÙ#aüAe@ÅÞoÅ©ÚȽ…a%Ö }~ÿ4/XÆ×f7Ì¡5-×[Þ-ǹIV¡ ûã—)š~a Èõ ~n¨ƒi*aõžá¶ùx‹L¶{NÐrõAPrä  býwÆAŒ‡W6ºìQ.O)E#¢!Zw ‡›¼Aw]îH¿× rþšÕZÕ&#(z¡c8É9¯žªÇ4 ÷—*Öo©´ÜR¢½P åûŽqGLïÎÙR´‹£È—ŒG§îwƒ¯—v2´›Î(uæŠí×7V…ˆwYRtbSP¬JÑ7ýÆ’¼€†Ú^Ïô+í¹¢uñŠtÖéÅ€ft;@Ý–Åqÿà©^˜R%+j O ò´#wEæhPÔœEÑæëí!B㦠>èO,·D Ü‘6RMÿܦˆè!ÂPyÇ+àyo/×jéNÈÞ”è–á0S÷*W¬šòe[#E»K“¡ñg×õøR0SMP—›CÁ'g7cœ$ýÓLŠºV(I’^{rçí3 —;hÖ„é¤dÝ®X¿2¥20t 4pÜ#8+pÇ8ø@p»¶Úø.Ös¯í=O$ARSÔö® ñ¸,4{pUÉ}. ‹-ç1ç»f„;;¶ ÜÉÐý÷VC ÌæÖôÞ°y: ©Ùȯ€eýT­=wŒ»RtJžäîïÇ-ð¯!ñ :hýg!%CŸ¢ãs•$gÜà¯h‚îçZYCÂîçú<ë ‰.;ÈëWr¹¾NÙh¥÷}h7p÷dE.ä$XKÑNN•r@¢ åä–ßI@Í*ö‘íPÎÛo±Q€È•Á=»Ü Š%s¯Ý“À°7ýþõoXôvWl¾D÷íøÛ%€jVºjßæþÁS}-w6)¬gh¡I&¨X¬á·l‘Îõ. ¨…‹%Õ*Ö´Ñ7˜õ½OPa~3¨ì hQq¸ð.E'í½Bì¿ù ¨ë7ú¾º™¢úî,=»+CÅúû·³‡˜®í®0¼/wæê8¹Ñœ[>\CufkëÏ­½Q E…ª U‹=_ŸRtˆÏoò{ÂQ‚Júè 9µo(*ÖoâÖ[P(%€šÎ»u Ç]|ÿ-œÕ hõÇOðq67jº:4þóe Èæu—¸bû\æÂ~¥<Š.?±Æ<ãN˜Ÿ?¢ŽÚçÑ]bÍ›ï.µÄrÄ>)Z:Ürв¹ ªz^$hʼnn?n‹ÍB¸3u4EߨÞʦ&T¬¢Ç ˆÖ¨%hßw›!]Y Ð)zA4Sé6Cשׂ¦“só¹š?È»qÿò‚v¢ñ;ŽÛGþ¨è;š·à.Ø|÷çŠõ³]9ÚО¡v‡G1²è#EÍ{BÎZ5µ¬ ¾²=AK½$Š?4iÛprDQ±ý=.‚_$#èÚÐT¨Xw†;âÆav¸F Ðô£‡YõD®Ø|ã)Õ¦ž“¯ôkÃBÚÇVP±÷[qªÃýz‘’{@5¢;³ù;¸u:; a†/EõGÍ3cîƒ8Ð~À­W¾ oâ•ZÖ¯½ä ãS´£¹¢Ä´óTŠV~{F»õüçgÆÿºýKý¼/—+Ößç óÖ´¢$\ÈݡՖ‘†šÅufM;.q!Þå[¹±›HÖúaÜ]šß`qÙl@—í+…ÀvO*Ö/ä‚.kŒ¹IÑ«[”™Šm—†ÛƒýÔM¦¾Ð5ôwÓµ=s8OŠnhóž4*) ¨Ø~§Ñ§áZéQ‚6o: ‘ªþÜ.C™ÜæÕ€†M[ÅôCörÅæWmyI;ÚhßU¶32‘ûOõާi¶Ý¨Ol[²rØ!nf™T÷<'åÞýN<Æ?¶dÝ…òG£jôä1ä]Ämœø¹R^݃´ö'¸“%ƒéü–"@K–ì#cnãŠõ<ð(è¯m)L‚Œ¿fru§ôbV6gúÞ¶?“ß{œûõZ8éýÏ?è•×Wˆ÷Î)úþZ$8™iôLY ±-•P±~1‡ hÌ9µ|´™æ(ý¢ Y9OºÌžÂ$ÖÀd4”¶¹^ÂÕM­¤¾mTl¿åÌã`í¾… §‹"ÀŒ,âæ»|¦*~|GŸ6dôâŠÍŸ­rŸ~xPè·çëèÃŽ%Ü?xª‘º’ù¥1€&O1"ÉßOsj!~òÕˆ®¦õ<Ä5p© ö^ ù£FÏ=Æuï• ½@JÑá1Ë¡®à×pZyn¥¨£¢1©Òu¡¨è˜E— Cœ &†yàõy ÷K÷AlSŽC'ßÖb5ogr©® l©ØJÐ{~³ÿù寨îždJk² Ú0Ôš­ ¨X¿e«œ¤êæ{¤hØiUb<[‰›µ¦ §Ô´té;P^’Ãmòo¦»ß&ºvG1]½«„+¶ÿK›Pð=Aº£Ù|_påêý£† ®Ë”3$~ì7)*6ßuJ•tgó@ƒí6ιÜ?xªj+g E€6¸Û‰Œë8Ë\:üÅo‚f~èEM Çÿ8 ÒÓê1áX ŒäV̯%’Å1Ùhýç1¤29‘ í:=¢^¯61t\›õl¿Ôƒ+Ö?e])¬¦Kušó ~̱â:t«¤m<:2Ô*Ê”ê]¿BQ¥¯ÀèÖ0@# †ÈI¸Å}˜û‡îê.ºlF]2W¬Ÿõ¬$⼸­ ¿¨Åo¶pì¾Cn×P‚np‘—\ìoÎMíä•=Ü P§6D«ñ8Wløe/¨Ý… [’Ý`rª·1ðyÞc(C“õÒHjÏC\±ù%Ç"HÑÏ“€>/|DÖg.æþÁSõnB6ž½ ¨WA2yeVĵ)šJ½Ú¹rGÛL¢…5!\¹|0JÜÃÐíݘ;s¸©þaÙJI½µq\I-ÛmàÆ ŠfMÎ ºYºžiÂqŠŠõ74¬Íã3=ìþ·•p‡¿= tp§¨ðä îË};ò&t¸j踆Jpy2‡kZ»‡j%ãf†Î%ƒ¨W¬ß>‡AP}ö¡ ͱ„”I}):¾K‰Âr%‚Æ=k#ùµ/V@-v¥š³Õ\w‰8ޏ. bûǤ¯œôZ]qj1DÌ-âê/û †ÖµÝ.XsÅævœzP@Pëv¤Ñã«)ú_œªìèyƒh­¹¨l®uýÚ9PÙüý]è Ö¡²¹‰ÛQ>î ²yaL(u û Ees±þ£Z ³÷p@eóIéÊz×^ŠÊæ«Ô¾À÷æ€Êæ9»—“yQ´µýØg0ÍOQÙ|ÆaEIËšST6é<[_K™¡­Ý>Ñš©€ÊæÛަ¼«ÚÚùï"a]›tжöý{Æ©Jþ“vP…òE€*M¥M1³¹1Ææäá[ ‚Ö?ô&–ãÚHQ»èÏpëî‚Y½ÉUô„Û]"Úä; F{rZ!ji"Ew:§€º‘CÅúoOn€Ék»S4Åäl>š›óLoï¸ÈPkß)Ô¯:—k±!™Å„(˜¢—½/±WmºÏÀ˜ªd¨·j«,ÜÀë7¨AØÔLahLî!?t7"Ô‘©ßh'Eÿ²ödΠ! Ë·њN#ZÔ{ËSræŠíïj'—!e M;b+p§þh‚Ý5K¹†‡ŠÁô´Wlþ´\Ð>Ÿ¨VÜ#¨û;ˆ+ö~+NÕ)MªÚNÔmO²Tõò®Õ³ýdûK/Š–nÚAʇ¿á¶ï]êW¨€®üqÊˤèäZàT>™¡ªH‘4›¢ðQ‚R)êþeã?ç­¨Xÿv[ŸÂ²— (j¨ø²«¶pUã hJÏ› }Õ· ºÎ¸Å—ÒØ•‚>ÍŠf=ãzê·ê³ìÒ¡¾vkX~iŠŠõ+þ¾Vhœ³‹¡r¯ÓÇ—\à~ÛÄ-¸+ j‘ÌdÊ‚j釱ÁOì:æl"{®ÂÛ?T{»TÊÐ'ôat¿ûܲ^‰àœô7wY 4)[rÅæ¯\ îæk×{-r£¸ðT%#ʲk´Ú½ÅtúM®¡¦é²§/CK†’<‡Üfn`¹7AŠØèBÌã®úõUI1¯à:ØèÁš•_ š°yÙ<0ÐcÁ裥\±þ6‚IͱºÁŽq“ ôÙLå‹ uí8ŠÝ»}ŠÛ¯n/»Ì £`Ãòûäj÷A=ŽxŒG×ùÕ·µ+)*ÖÏy„»tDP$C-³4éôQ‰Üåñ¬Ãq/‚ÖIaŸ®I¹÷‡³¿Ô½)ºWi«>4Z@Åö›Dô‚3}ï1tÜÎ.`z¦ˆ«Ÿ5n™ªs¿×¾ eïoJQ±ùá¾ù4»Œ ®Q`y½÷žªñæ³Â/e@U~ª’5•tkC2¶mf¨î_ž‚Ñã ®aB:ÑH”4lØI¡Ù:ƒ›ðp>h¬@köBXñNîJSoøUPÀ ½r ŸmáŠõß°÷"Ìï~Š¢UЉ™Êµ6œÂnÄe¨ûqK¶j7o[lnèÚ’ª‘¥Í½xÓT`(C«ÖûÁ9!™+Ö¯)Γúì:ÏЮRFÏÎäf¿;Ç>œ”Ô­ù4‹ßÕÛÔq 5>÷I@ŸyE“Ïê)*¶ÿtd;èRp›¡¶íåaþÎ|nºç+bSJÐ v°j²9 bó&ÎÝ!Yª|¤˜¨_Ö!è<ÕÆã&$/7ƒ n‹–GncvO©\CwŒ¦õÆéÜ’~ãèœû½]¼ö4ÍÙçÊ {q¾(䯳‡0É)«rÒ¶_¼OФ/š‚GíÑñ¨XÿÿÇÊGåønÿO…¤’Ì ™’$Iõ\;!™CH¦$I¦„)I%¤Q£J’d*RQÏuEIæÌ™’9>ÆL!üüÖúî}ÎzÎZç^­ãþçõÇ{Ý{¿[võôo]b nVG—-Ã=éäô£DÓò‰MjÙG4tkMæ[í’7WzÂÐ*.¬ò²>ya+<×¼*ÐÉýß°KN÷H©~.—qsÕžo×A¤Çž ãZíá݆êÐ+\,r"õÎús¶*ul·t{†’?ŠÎ}`_Ε4wùcu²ˆÌüÇ"š†êã?Ž’RóÙ³E²§Îk:V5•›,¸CþÅSu¾l[!CŸ/dJ¿WÉÐ~YѸ@î|ø  ŠyHúkÐ|ÞÐÿÈ'‡ˆ€vÛUÌ¥ú7?e&‘í8ª˜¯-ë´‡³9ª˜?}ˆEï´¨b>bŸ2è· ”¡õí—X8E˜½K¨bîe)V˜;ª˜·}ÄVŒ¼/Ðúî¯Ý\À”ùQþÇן\-ƒ7Zßùú´DÜâC€Ö÷ý{ /¨Ùü7Wuk`²¨€£M»(Ù„òr²ÓÞubìÜ€vôÞ,ô“GcœØŒ¯D>X d£%‹c誣sÅç5I=”$~8ø“î ­¸R kô\ôdñÞ3†¡Rýso.PzŒ¡_žoWnž^‚O{O@Ó¶|ƒÉO†‘§#T„Mî1¾d&¾¼Ï"].¬ÚÐJ†­í(L ÊQ©~EM…Ïáuÿ°—£:—–¡¤d3dã¨Ô~£ï»DMù^k´[| ²%zgšú9ÀAXm "¥æW, ËŠ]½Ó{-dœ,$ÿ⩎ðžÄ5¨ÑëøþB‡Üèá.¾ÅújËX‘Ò"Ô+: ‹øqÔxTÜEξX¨û‡‘7Û‹üO~rÔ!v/O¿ PU¹K¹8”êoði›h)Ú:£S˜8û´ixá5 }!c诉`¯,CælÛŽhZwÑ­6ùñ“§Ð-tòÒý¼kó)Õï‘RSñzn^kü‘÷¼¢Bî¿å ©ËVú¸óBØíá@¶ª+ƒ&'^ptûË-ÐË4”Ú¯zn˜|ÌR éâ–‹)©|ÓB4Z–hM]sñZ¥ˆ”š?u#8þùs­Žj Ö…•ä_]¬Q©~zËøäs×9j47œ'îK%o9Ï‚”£Ý½ñÉöuøÎйïºÀèŸwäèÖnIìô»» •ÚtL†¸ŸÒS ÷•3Å÷Íú¤nÍbîóó< Uédº¥9¤Ôü1¶ql¾e…@_¿Û";Z™GþÅS V¯·Ëýzíúm¸wÚŠ|{ç'·ùuД¦¹Ü2ìY»ü ïÈÐÀFÞl2Oþ¸Ï,;†pÔÛ~¼xª$Мs„Ê¢¤iÏý‚¥^å¨Tÿõ—ÂÅóÍ 5\*Θm$¿ŽÛ±ÝßËÑ ¯¦Úç rÝÔtno£ÍÑ”^.…ö§vÉQýàrÜÞÍ]ÜM8¸º‘’¿µDžÑs)Gmúd<õïOò†0™ïgèËÏ,?q<¹áe1sÜæèð5AMç0)µòÄ·kkZåáÍÉôšH&¿hZÒq¶)yj_æYÍ*а%:²6ÅcÉ¿xª]K/È{ƒ:]ã ¼*F¦”Ùó¨×‡-ÿ¥)ï´v©?ªSMp —?¾ÖD‘ K=@)}†@Ý#ÜÀ#6‰¬õÉ«ž©ÈP_;Q³ÿ C¥úï½,ü¶ntÇ/?q/#Š–  Jg𢮭òYV² C9ícUsrÔ¾®=¸|!'ô ¥'¦´Úaq2¤£RýL‹:°àZÍí²›­¡AöÔ cú>?dhí½½2ûŸ9d¸ñ¨1:¨vÏãð¦ô)µ¿®à hôTE ¯#]?qtaö!–¦—ÉP{åÇ2Õå¨Ô|OÞ˜)õûÄѦӊYí¨æäÿpªŠßã“ð{Àp*æE›¯ÊžÏT1¿åy‚YF窘›¿Xu— T1¯Žy'÷]ÝŸ¡Š¹Tÿ™9‹E*æµ&²Â¯çª˜×l ÝÝ•ú_ŸyˆÈªÎ²FëÛ/aÅ;¶Í¨Gó®®·åU¬P†*曦ŸýÛþ€Öw妃¬Ã#Ž*æ®"yúÊ,ŽÖwþ‘æÃ¡üæ Zß÷ÿí)3nlóßìiâýòÍ2ÔíähQ;ª˜Týóì¶*^ ZOsá¾×^RéóyääÕ¤éÆ|vÓí¤Ž÷\¥?…£MÛƒ‰¦Y]Êò:u誩“Yh»½¤Tÿç»vp·ã M9‘÷øý†¡ãÞ-†´šK@NÞI¥d×½³…_úŽt²@±xa3†.Ï*¯¾{P£Ÿ¯ØL;¤T¿°b-à6 Ð33´ÀWcÙzI ÛŸZÇÑgóÙö­ûI¯jÀŒBÈO5‹!ÇÙH Rû'™ñË3cífÄïfî"¿¼½‡n-!“¶}†ƒ`AJÍl×Rõ‹úEõ³žEJ½_S5þ2UÔíPaèëjw±­GORsl p§L¶3Ú6ä‹VbÔ½0òCÕxÑiævòÆÏÅp£tGçEA§i ú<çϹ]@N=ÐÖ>ÚüaéצM¬.g蛉ïÀÏ,Æ •škïhÖðN@»²co#LÈ¿xª6— çþ:dçáoº‘¼·Ô¼?' ÔIÍrÖD’‡‰ïêëɶjÉ¢ð¸-9¦K&h¯L߈Ø%[IÏwËħÆ-H7Ç/|Õj}ŽJ~©ÏöZ™†0ÔákcYÿEöäýˆ“ð«ß@GžÍŒwv$|ŠËŸ9‘?ŽnÚdΞPÆŠúxàYØ/cRªßçË ÀáÞN½ewö%IýÁOÍ»½8 ôƒehBæ!æ³ì‘@·tWâNŽçI©ý™G”¹ËÐC€èWÊŽ’£ÏÂ÷öívp,-ó#¥æ÷sùóíÝ¡”£:u á“~Kò/žê½å›E^JCv†§ßùäu­ÞÀžô–o+¸³Ì“t›&"Ì_s´*9FnIzí†/] ôðª `Z~‘ ~<Œ•Ÿ®•£7zÀÎö&•êo¯’)sjöJ†^¼Ò€åDZFýL3Àhƒ ÎÃb¡É§ •¹½µ¢=t°p>K~Üú‹šðŠ¡Ÿrz å)¾€Jõóì.Ëwš¨Ôü߳π¾F {ð.©îæè_<Õƒ­£Å5M@5}vŠ ¿.ä¡ö÷ØýS/9ª4ò3»D2£f¢ÎeCcÓgpu‡‘¤Ê¬kÃuýb5ÿI$©ÖúwyY@õî%÷ZFJ~VõÂæçÇpT'È‚ñO×ÈgÚÂYm:ôãVnAúÍØ,«z²ЪãÙ’Q½ÉNk?2»éGÉšrØèâBJþSÜyU ßëº@ 4ã=*ÿeêìë äY!ÚÖä185êÆÑ²üõ`Ñä$i:öTø€@¥ö;µ¸d­ñð, ‹d /£·hƒIU™²ÛªjRó×f¾„Üh-š½ð™Iþ§ªølŒÛ).?0T1¯®S‚û«ãä¨b^¶9Y¦´áˆ5ª˜Û x`µí×*æzáI¼J;“£ÿ1_¢¿~~[v, ‘@ó×쩬­f,Cs¿ÆîìÃÎ"†*ænkγµJÛZß~C'næ[– T1/ú'qT1Oi¹B“SZßý½k eåÞ—UÌ×^M†ÇZßù¦7Á¶q´@ëûþ¿=ƒ4´ùo¦53Žjµ Íog rêÈÑéaäÜy=w÷6xO"ôóQóö:84 TïÿKûóC™mÀÆOÁ’,5Hn­Å„5eU2ßbe¢ñVŽJõ/ý6H”©tg¨¶þHᘽ’t 7gc“‹9ªº¯ »•ßB ^SÒdÉî7íÙ#5/,#}&–@»í±d‡CwàL€)Õ/ö‹!;ã}Ðõ¿"™Ùé‹ädO}ÐüÑO ËšÁ¨œfÿò†6?ôÆŒüÝ^“529ÎQÉýQg˜™ó2ŽäW°:›d–W­Ìã}G9ê8Üx‰£Ró?f‚k«c½5d#¬÷>CJ½_SMrÒwe¿ZrHYÜÿÒfAÄm‰h’vŒ™‘ñ/#.BFn sG¿€„ïsÉЈ#ìhÑ6Ž&÷êUü…5_þŠ»|àh²“¨ö ¨Tÿѯ'‹ êûª¼×Et?uÌú=Yf½w@½Ìãä;׺þ!š`»6P5[sX<%‘œØ(ÔNo–¡ÌÍÏå¨T¿Ž6·Ø^•«€úk3S®‘^Lö4-æèä†Ðßz6ùòDcèÑxY² <¯Þ%¥ö'ë¼f^ÇWs´oèo–»ÐÌhöXyÄB å7«åµÓ–“Róg§ÃðqçÚzßÖÝœ“ñT_7}Îõúýb¨kJ)÷™û´þœ¯â³Úa¹? zsŒœì]ÝçµTçy 8j§3ôPÞ Èùç'ÂÿÙxh<Œ=–H†ÇÉ“.Éê^c.ts3H©þu9óÄóp%@_,W|[“B7sß­:Më7¿ò0Œê6ZgoÔøâD˜ÖAFæL¬až¹ëzóÃ%Vöa Rý²{C|§k€zÜo¯}§hƒŸc²ýy¤;x¹êËÐbË0(ò^ Ð_JË üý!RjÿÆMMàûœÍUê« Uç¶’MzýÃ3ßnhQâlÑñÂFRj¾ãéM2Ï[»jS£,)žMþÅSuÓÝÇŸé|bèÁ ^þüùn¯#øè¥~½Aÿû^rr³—à7¸‡ ]èZ¾ç¦pÔÔ= ÔN6èÛóþºœFÊ‹¶‹ñíÌÈe›' —¶árTª¿‹Ó±-×P s/QµÎŽü^âµ£¿@GÌ\%ô´ÝÉþ¬ Ï…¯ õ¢ÉYÙÓ¡p| OÏåÂ"ûRªß¼ƒó Ãü" Æ‘þx½”äº6þ¡1CÇîç5'‘V4‡îu·:sâïBn’Rû¿çµÓ=‘í«nïÉ·¹1B«£¹@[îK¦±Y•š_T:Fv9F[ ×]™ï³<ŽþÅS5 rà~• ]î¢ÍD\&Ãî7 câ:zØCöáséÖä<è|”£×.@«IûIŸlù0Q G_í€|óS¤VŸP®tC…¡µQ^ì‰ÑFŽJõ×x¼XôTžh¿jO‘?bY¤.l&Y tród!ëß„|îÃÊz{3TÿI¼Ì\#€´ûðF>³ô®ïp|±G†Jõ{Ô*î΀V|O‚‹ÛŽ“ µ×@Ý‘`†9ºÔ®$»boósã÷ ´ËæbÅïi¤Ôþ>múBÙþ}=uq T”#ÓŠÓD‘ñk4ñÛVñâÑy†JÍoá÷ž.uæèí©£aèâ‰ä_<Õ¬mÙrÕW'9TM¾pÍòmåqí#Л1¬Ñ¾‰$ÿ0ª/Mäh™­!TÝi,G{¾ Žý® Ôqõ;ÖÑê©¿D™nq$$œæ1{bH©þ'戚¦[}Rî$¾Ž&ÓÞíU6û9àu@äUÞ”£Õc¿ÈMd¨^P?ûë#• <Ÿäh‰õ|Hòæ¤T?ÍÐp¬é~@W½;uªñd+Ã466‰¡ƒSN€Êº²¦Ñž:¨„£z¾…îëŒ ¥ö{µ²Oæ§8úâñpðxv…œÑÂB˜‡èjª•Íw&7'¥æŸß kO^äèJç]p6}€@ÿ‡SýçÍrëƒóƒ©ðh&²§nFUÌ£<ÔáûÚ! UÌ;ŽÓb™ÎT1oä,¬Í¨b.ÕæÂÏ%PÅ\íbºð|Üœ¡ÿ±_å!¿•d ¨bÞa@kˆh/CëÛÏg”.ýùBs‹—àû+†*ægO¿a‰i9­ï~WÃq wâ!GóØiòÎú§ZßùnòDè}Ñ_ õ}ÿßžà`›ÿæQ‘tæÏ/æÿ3|ž5¨ú¥‘oû<¯s¥2´óªjÈš#G¯0yâ>.ýt—9t-&ï6ø÷oæ¨Û“W°i|'éšÝ·ÉP¯ºKЬC9G¥ú?ŽŸóJÊMv\r·ó¤‰Jÿ vâþBßVfºÁÂw3ÏTÆ %o§w£%䥰 ðßq””êg—Ø~^è¾±AÜ»MV•œS›8Z3,f,:B^ŒþnéÈzÇé'Øî¼ÌQ©ýW÷O€K?"}<Ú Ò£w•G°ôéŤ÷øP>·Œ”š?~’?ĶVb¨Aá^°™;È•z¿§ÊFõ‚ÕZ׸øÈ&G弓‘´ö ÌüFæøÊÒ]¨S ß»´„|Õ²ÞžO‚ŠÅ¤mô È-Š$MwƒØÑå¤TÿÕ+‚ÁæÒ)@?æEƒÚ°<²íŸo©š¶}jät *÷Ï$Y3«è«ì!Ðô1g¨ÙÍ*0˜5бC®Ã6»G2Tª_jìIfêX!ЬÐ1l§öMrÂ4¸z™£³vF@ZàCRóŸ 0éç'P× Ñgº‡”Úÿlà 8Ýd+ öƒ\aþÔ@ò‘Õa¼ô ©—âËבRó‡Ö xÝÍ™£šæå°-JM ñTŸ=h‡uózRéX\@6}P .MÉëª`úä“ámDØæ#ä ‹©"éqyü¥|léCöJÓ…ÄOÃÉ”î3 e•'ÈÁ÷ñç¶ÓI©þïZï³€ZÌɄݺIä¤ÈC°ÝØW O"Cá|?Ò¶Ÿ8½÷g¨ãã\æ¿ÛÌ.H€†«B9:ëäP»×œ”ê—ònl^˵î~Jn׸„ì`¸¬ä¨r¢3˜\.#>2€U¯SÚ~ìr¶¶«')µÿ÷bw8¸Ð˺žPÜs5¹¢ÍcðRÕ!uNݧ•ê •š_#»? ¼ª²ýøÏ ÿâ©–^a£ …@OÞϼš“­,n€çˆÍ¤ÛÜb¨vŽ'Ûêû‹šþãȪ-›EõËG}ÚýKÕl!Pýœ7lÏÀ޶‹Pç±%ñd²ÏifgœFJ~Ïýya€Þ.Ê…¬p²ëowHßã#ÐûƒÝW²oyVÔð$CKR7ÉÞjè©7¡$*„¡SšyAPm{@¥ú¹Muæ6Þyí9,‰¿¹Ÿ4µ‚Øš]Ýü¨X{ #WYÍÊr>’Z1¿˜µûzRj¶á"Hk±Pëè%`xÎü•rÜÜq4,q ØÜ$¥æ÷8ìFûÎT­eøþö2ùOÕ=ÑŸyˆ3ûx»qó,qðX ?Hn; ÁNwïy ¿Œ¯r42ÐL¨;,CŸÚéƒþ”L9ª1z¤¸n/D-þü•is|¨@§W `'+÷‘Rýº„mæjù«tGmø]3T Âß³â ödFÃn².6€º8î—ÅVù×K!âycÒQí3h6¹VˆJõÛû ˜œ/Ð󇼯ÍVòqe;8 ç+GU½:ÀéÔí24`Ò ¸¶(£–ÖpúYWJíÿ«,l§!¥úy€ÛÜìÌÑA·ù§_Z¤ã¯C°:(P1„ÃâWÒÝ·Æç½gèõ…S2‹|T|ƒi,>@ž¬í/»vURýܯ0Õ9áhü}o»aß¾ª"+šAÔY5?¿ ƒUÚ“Ý×æÃˆv‰¤ÔþŽÊOṡ…@GÛTÁ¯†CH»!±`¬?GÖÍ€k~6¤Ôü¨Bˆ?ìÎÐì„T°ñQ-Dÿâ©ú?V†ùƒ73Ô×îSYG~êâ'd½¯’½wM_"›j´GUtÛeJþúçóЖ ¤y«©ü0ßKŽÊhŲfŸ!›Œb¶C“ÙmìágÂLRªÿ‚G DmùS9ºD¥µP›íGž­,†¹åíÕUæP‘ËÐÝà×g¹5š~$üZþ£a kä3ã¦Zk–žH'¥ú™´Ò†Ž½£92°„Yg’ûw[Áhïn­n!ƒ*;R£½Ô =OöŽ,Å©””Ú¯!»Þ±cY{ߘBÆÿ4‚÷‹ã Ñš\Cx{ÚŽ¡RóÇ™€Me­ì<Þ”“ñT}/d½ƒ3´ÆìÛdUBv\h.ª ]=WC´¾iJÖénåË›¥’UêO nmää[?]Hª_/‹æh}÷L;j­— T1ïRž qî2´¾ó_܉kZß÷ÿíycüþ›ó݆ˆ Úo'æ[­&k³Òø¢Ê-€®½Ú¿Jú—ÙÙ]ÄpÇ$²Ê+§ÍÎ"ä–Óդ®Õ{øvP“tS6½g8jÛL† —£Rýß—;[µÑ.èÁoä4óÉën“XÓ»—ÉO ÷ä–‘¢ïQ›kÄÐûCDìz@¯Oï‰}<Éû‚ëÖ— •ê·àä ð™5†¡{ƒÞ‚}¹ 5^x‹y~Ý*ÐÑo·²²ß1䨿«EÒçE õ±T‰#[*µÿTX";kX$Иàl¹÷)²«õþEň¡J£ìxR£I¤Ôüjû† k¾‹£Óv#³¤Ôûõ8U£ì©B盫@—̳a4éà`"+¨šÛ¶~Ë ²ÇáÙë´,rü—g¬ó³2qñ7hüé” }8ìÐK=Îѳ¶IÖX9š5ç0Ìœ¦'P©þa}ÝùÓÕ‡ºã^ºiÙÇæ"»’GÎxÜUX.J"‹®ÍfLÔ(ÁNø‹&Ë=/°œ) Mi4•í‰k¨T?ç¹5ðV§VŽ&ꄲ3ѵivYV=6E ; åÝ´ÒÈàwä›ôZêòìŽlôÚ# •Ú´Ïh¶}ái¶OÐa˵‹I¥¶¥÷—“¡ú“­‹×ß“£RóCÔ&YŒ1GÙb¨i˜LþÅS½uzžh?t€@ÛX ˜¬#é¼+‘½í<ЃQ%̦š‘Úûú‚Sêf2Èr,¬qI–ƒˆr3nÐÜYÃ=È«ç7ÁÝÜ Ò§¢;«‹»LJõÏ­½ÂzÆÔúÃGÞcIiÙ}¢ÞréÝ|±8úcùlÐ>ëÈ1@«LòÏ%%dÃO™é•õäŸß ðuÛRªßù¤÷P7ì-G·U¾‚Sû ÚtO ¿—“JzoU×_&JGý¬Óì¯ÉÐ÷O·óª'H©ý)uù²ò¹Å}_ÓBìú/KtT¹Å™ ŽÞÚq‘û¿‹ ¥æ{ùïõ÷ÚýÖ2TÇy’ñToZ$†»4¨Ö”Ebvñ#Ž2yc!ú€ŽK»Ãúçª ãÆÂÈ&Íɶæ l*ú%~&8´X*P§H˜Õyùñyñqné³`™X]'8*Õò@5qõÀjþ³[K˜_˜Ae-÷/Þ㨃ÌC¸ê‘ööËØý)gT¦g Èü·÷!>l ¹æExM=(G¥úÕ~}ÁSG ôr»[àÕg¹Þ²¿ðÎÚAŽ›7[”e®%˵›ˆš¶ç:ž5¶}ç*µß¯Czáµb: Jwp:M›ÒOx£§/»‰“­3Tjþƒo3à¡R²@M-Àšk…ä_2㶇Xè×Ð_ŸMEÊÆHRj~Qº½,pH©@ãÜä‡}óÈ¿xª™>³…²™GM[: ‡å¨Ã•ìÚð†NËïΪÏć~$±i3ªÈvž1¬}'@ܨco7„qtó½^à•¿‘ôkùËÊfuG9:: Ç+õâ¨ä?…M;14®™@=ôZ‹CžrtÅ?¯ùÇ$†–'Fð#+’+E$ ?6P÷~‘ÑW…|w˜i|óhÛ­˜™\‹”ü[àÛ¸/Oh³›; Ð.‹ÔVN²óµ?š*ì6$Ïì…³Ýí”8‘’Ÿ%­ä:V\ «cø9ãBÒuL$?vŸZí¶‚½ ¹BJÍ~~¬èÖ%B ºKÅš¶–äÿpªŠO\ïñ"¬À^Ž*æ¯ÎÆÈt¾¥3T1o°£Œ-1Ÿ¨bÞãÃdØùýGs-íbÉ¡@†*æRý‡>h!Þ9çrT1ÿó‹“ÿ6O`¨b~­}0è$2T1|pœ%Yîáh}ûí^†Grª˜+ïÙ-Ö‡ËPÅœ¯€œ'cZßý•qK'ª˜ŒÝöŸ´¾óÍno š†p´¾ïÿÛcþ þ›Ú­£ *l‡ Ýu*ª¾·FíUƒ…¾Ò ŽÞ\$îîC¦m‘ èÑùñ3G“š%ÓÄŠ§íú´Õv0x*GY:Ûœ'ÐwOЏ¡¤Tÿ’]-X/÷hŽêÔš³±‹bH›+ƒ¬õïÍbèÐôlùÓ÷äÇqåS>GÍ2WŠ”z…èBåSÌííuŽnl …Ù{I©~º;'Џ1å*‹ÄѺ™¤½únÈâ¹2tÓ°C§#GŸŒß ³æì4fÿ#¸SÓÿ_Jì¯ ©–ïìú‚¡©S¿Ê,ÿDn!ª–žà¨¯Ú.qçý,†JÍo2h ÷@Ï×Ú‡ •z¿§ª©› Ev…¨Å÷=à2.BŽZ÷Þ2ôH‡ØÕ(¡Mó’!ðØg²zÈNÐÜ0¬ÑÛ)*6î > š‡ݼPRÞl…ú©ð0‡¡RýÛG:³ÃuIÕZ¹Š™ì9@FÅôå¥"¡;ìgó{e@WhÏæ…1tÛí6ÂÔ¦–¼ÛÖ¦ûth{½r6ök )Õgl6ù¦š¼YÌmØÔŸ• ŽK‡qôû¨è+ȯ@齓]¼J“– Tj¿—W^3° ×W4äËÔÈ©v›…[Æræ^}±§E)5ßrÑPú4›¡O†ª»ÍÉ¿xªeŽ ß«G3›„íÉÚ·Ã…Þ?÷z¯F_¬]«èè› lwúºÃtH¹Z\ˆê0–ïb}ÿÍŸÛÚž •zß±²ØbãeÁrTªÿ¯AìîÙBŽÞÒ e¥‡®’Ë8s~® M5xŸWndfU>¯¶Ñ%ïŸjÎÛ=jMöºÅ£.íhts?1){ )Õ¯ô~¸`{˜£¹{EˆfO2p`ìÛ¨%ÐFÑÇAGe8yj¯è¬q%Ê~±š´!¤Ô~ë´Æ¼POÐÒ§¹ý›Ö¤Ùä¹Þ1ò\Þ}6Ñ3“”šÏ:tƒ|ÆP÷+m!!l7ùOuŠÆ1/˜£m³á^æaÒùËÞöN@Ôãù/=cÒÁÀR^ª[£#÷ö¥“·dèù´õÌõ¼´Õ3¨yˆTsm'l;q†žžpž·^½P©þ£ÿü¤þ¸ê%G],·²Ú¸-:ÝØ:gO ®»Ý˜aŸ$Ò+WEöÄ©™¦­Â:6¹ÁÐ~*Ébý&RþÀQ¸gû*ÕÏôÿÿoÞN7-Ñá+Ä-ŸN Õé• »ã}ª¬_m¢É« 1Ïß9j±ì[õ"”Ú¿Ø 7úÞ P›‘ʼ÷Àd¯ñÖ9˜|ÝÀââÍHÉÙæàkùœ¡Í+¼À"¹ ñT½v…Ïoq´ý£ ;KI ~É­ùMc@m®ûÑôkfÞeÓ:¼—ŒêI&üëZ:÷~xÏ6#ýË_2fp†,™˜ {õbH©þkû±FÿhT=Ö…iukONó«cÓ;g:ÎèÏçh›ƒä¶Ô–,hUC3F’Z¹.‡?^µÏÔÒ¸'D|&¥ú­š>[xLÞÉÐz#DXÏëdÚY5Îèu]˜µø$é¸C&äTpÒº¨´²¨Ô~±³V~,ÁÐAÆÕò:"·¾õ… ï‹jïí=’‡‘RóX삎s+:÷Õc¦ä_<ÕP×£pãY+ÖÊ‚ ×z“å?É”*[j4Ó€8*“!l3<9´¡§¥ÀК6ä×q0%¤€ä‡‚!x|­ í[ýZ»®aèµðz¨®@¥ú—¿1eå'{´hH¬ª“éÐ/*‡î´òÑ1¨SßA¦h¥N7ËÈԹƅ@ò±Õ{¡håÞ0´íz†Jõ‹ÚÛGdnWtrgMÑP´"3+´ÁwÍN8‘Î’}Ï“±ÞQlÃâòUÿ¾N+’”Úod{]ž—7Pw~B~núHòGÓžàõªC#.êÀú³Q¤Ô|_»kP5£=\Íà>ýNUñ myÄëÁUÌÛµcî×ï2T1Ú/j—æÈQÅÜq¡+ØÔt´FóØ™ö`qqŒ@s©þ6ãd®eUÌ}s®Ãý±³UÌm[xò/ã¯0T1ï¸D®`h}û…¸<á¶M»ª˜¯ ß*[Üôœ@sù>qçìÖw¿gd‚P©þ?ç4+ßpÔ"__”´-#m>ÌËöG ´Ý†n"åûFò¸z4?hõÞ§²øWÈÙìÜW†Æ]N,TªßÕ¥W)™Ú5³´šªJsTáhÜgŽþ<õ…Å'Þ&çí¿#£Æ täÔå SßMJíÏù~Kþ%u¢@×k5àw$o¯Û2“7b¡Î’Róu &@Àº¾€~ €}/ÛóTgWÁ»BŽŽm~Š»| £ç‡ˆågJå¨}|¨x¦dÆÐ3"ENp5G?GÇ Öp¢ ð¡%4oêèÚ¨þ0Q],̽ÉÚ½¤Ç’®`¼î#C_fL……OT=sØ4+?p4%]n•¯¨TÛ1îB}ú3†Úo›%΋ր6ÙR8.I"›vsç+—%Æ»CÕ ¬Ñ ã‘2T­ M d™aï9ú¦Éq–97”ê÷.=U¶èÓÀX¨šcJŽÔ5€…þdE¯©v„{Í[ tá´?'sΣھ̈́ã…V •ÚoX4™ÿÈo.ЖÎüˆWCRéã ëmm;:Ôv³óýÍPÉϪ½'óš_'ª\h"n‡…‘ÿé*>-÷DC¯…kª˜èÔ‰•/:¨bþ¾Ûl¸:ÜPÅ|•ÓÈL­b¨b^h²_óß&PÅ\ª¿ÒÓ‰âæk õÍ™çÈ€*æ¦m‚RŽ·5ª˜w“3× Ö·_ι àƒª˜ßj Êuýª˜û§¨‹¹º¦€Öwþ‰i\M|ç¨bnrÂý3§¡õ_Ôkx´ª«@ëûþ¿=_äïá¿©ºžsÝ6O9z~Ã=žå_Nª½Ÿ!f¨ÅÚÌçB2©sõ1¯Ì$m6w²Ü24b Þ§ãzÙ`)ë×!‚ìÓ»‡™ahܺPÙüÅGH©þ×zÎçS[è÷ÓrÞg\2Yû³üùœÊÑÞ¹màšîR)eLAR¶)C³b—Éó7Í$ƒïŒ…IGºý}snØ.”ê÷´‰/tµÎgh´«Øú…ñ¯äk—Lçhʹýëžd÷B•·ý}ëð4ZaÏP©ýG³·ˆÚ›u }òc³H0lèŒ[À'®C³m“Áï\#ŽJÍÏzÜ–×@oަ»ìæY£|å¨Ôûõ8Õ°Ý_yñÈ]aÑDŒ\™DºZ«Ê‹4@ß´ SÉíýŸ±´¯»È'MÆB÷ù›É¹žÈÝRæ ôÁën<°éþ.œò“¼æ ¸é2Tª;çß|ïºp~5i/š½ÞHúYu‚¶i›9ÊÝ{@jóhò²úCù§Ži ]wï­\ýû'òyƒN¢â‘š@?Ù+ ›÷2TªßÀø%°*ÂŽ¡>©î`rªéàÓBn™Ø‚£~¯R jLš’©›¯@³¼.}wh¼*#¥ö/¸·QœZØ Ð¶¾¢¶__²¨K èÝh.È8È:¾Ž”šï¾ï)_Þ¢/Cßœxÿ÷mè_<Õ]òV¢]{?ŽîïÞY(åZ“gnÁÖ±€ŽÞVÉ•pá¤-éj7ÇU34mw:·?–ÌÑʾܦc™Õû^ ͧ té“f0üÝ^Rª—ffböãÙ]øÙNl¾&#½ÔM@íÔ ŽÎ¸e³3>‡}¦ÊCL{ªz^–Ôo\£Â½µ‹i~NÑ#…”ê§»r:ä¯<(Cã[N€rÓ‰¤©u\OŽ*•쳪›jHNûÆ>ŽhÚ;ƳœíH©ý%×½„ýè~[w1Lc ¹&Ṅ¦ ´âùù“á‡H©ùÁñI¼õ¾™€Vmÿl½"&šü›?U¡·P }&G3Êû —¨fä+›bði=Pû;0_­#¹áóv¸¿jCu×ùÒ0ßBÔ£Ëo¹Í÷V¨Én]ymûkTsh(ï}aŽ@£JªxyµG¥úç—: ­yzuZ6CL*zÊÑ݆† Ù«@û>U‡ÚgŒlwö[ü` ñކPDV÷“ùØF1tr¨ž¬U± Rýṵ́“†2tlo¨êpÕ­}¨Ð9Ò‚£¶Ê=]mÉÓ±¼4 “¼ÿ²/÷ô’£RûM~9 –Ó­9N,÷ô ûMï*,ò¶ ô÷¹ ‘”­EJÍ72Wû]Ò"Zº'ñT½÷[ ¯2Ô%Äau†¦˜UC8;D~jð¾ÔèÊЬS3¡Gþ'9ê«b ½ü´8Zôôfa~Y‡BÔþãY¹~Â2¶A7¾Ü´C•zqËÄ Ÿ2Tò³˜úlQþ9–£½Šf‰ÀcH‡îe,ýó ¾Ž5cÞͽÉÙáëA5ÐÐ_öÛEȱyl¿Ì…ܹ 2–»’Rý÷´͉O­ÑûwÍÁK_W†–÷Vã×­8jqu2_Û·#i³ÿ{¡Éåá…¨ÉË5rõcå¨ä¿ï&[q±å @Ã\ˆÈ ~dÞŒ‘Ý¥\††Î]%Òrm•šßyôiì h»vÕPat’¡ñTCýl…‹¹'CÝL‡Ç„½da÷7 ¢±•£kSÃR=-zè=>ËÑÄs(:^LVïÆS*·Èвª$®1bCí„ WM“‘]:ñ¨€ö€Jõwº9U¤üZYˆ¦'Ô·½–¡.‹Säk£WÔxÊ1þ4ÐÜoq®è¦\á¨TÿÌжÙ@ß··†*{²J¯¦0y{#6¢½¼n® éíùÌn×9šS¦Í-—ù“µ­¬À¹Ó%9ú¡@ùÕ/¤T?[/±¿y8CŸ»ˆ¶ŸÉ²I¯dã–´Â7UÞÏcù&¾ *gŒâhçv Ýv¹@¥ö¿ìÁVÌÍÔ½M.3ž{œ,ñc]–£‘6 ¶NP©ùiWw3ãù€îL÷Rï×ãT-&xrŽÚo™«Î’­Ú>û í2옖—¡üZð;ÆPS·ÿõzª±í↟$‰”$ ’P$Ej­c†‘$$$I²Oö ¥¤’v’ ‰R¡’$›Öy ! Ùe—dŸì’Ýs½c<ó¸î±ž1îó5nç—߇¿sÎÿÈ´¬XàŽé€šE¾4'1ôô)UÖóG ×oq¸¨æp¿Ü_F½Ìs¹bý ݦÂ-_;@WvZÑn…æ.¡ºŸ›ÍŠø-üŸ^hjçË‚IagÊ?F¨~zV@Ó–O¨ì|ŠúÎ3‚w!v ëuÙ‰ym74kˆ«¾?ƒð=ŠÚ7Îf¨÷îFzoäh®ñïN°Õ¨«’F׿Ší×~½:è¯Ê_ä|ã¿vXÖ‰VŠã6´(½°/Ÿ+6ÿjr&h~ð´ðB1¤-¾JпxªÖZ“àÓŠkÒ ï_Tr;ù?†]gp?M¼ õÎ?¹+ß5Qó¯)€úö —T¾8íV\Êžçtf¨ÒÅíÌçä}Zÿj-óô¨¥((eK”º*Ö¿û½-ð0Ï Ðñ—ãÀp~¢®¯ìº;lµHKçzL(”å{P§u…ÇÉÿÚ¯ô¨Õ&†n”™³}´7W¬_ytg¦™ èð¶håÉ®iŠ½Ñ€¡IÓÆ³!QÔ=ÑŸëd¨ÑØHÖ{AÅö?Ñžo8hT C¸QÀÝo1VþÎ䦾O„A+ü¸bóÍZËÀ+"G@IÆ>¨[ò†¢ñT ˜ xkÝ¢h¡¾9¬´ý×½w(¸ÙHê‚—qgjj€C .aPžq€rz3S¹Ô‘ûm†)3¸Û00EÚõæFî½–ÐìmÎë¿°õ>¨gí5“†ÍY/ :~<¡+_}áz\¯FûjÜpF0& —ÑÔÞ]d¨®4=ð "¨ßìVlwe býru7ÓYk2-<0Eøå’Ïmsp>K›<…¢~ýV3«l‰]¶¯';³ÐøËªÄ¿í~®Øþs0w9è¬9ö@úã¸Q»g<$hÃÊcÙi‡€ŠÍWºº5Múʳ,<0„ûO5Ù¥/ô=}“¢£}{€FE·BáùÉp†ºt€Í;¸á NÁ¦ó]Ó«¬CTa¬&-\Vh¬_¹íSĽU8 fTIs(XTWHQÑï‚]  Ÿ_>AWNAc—\•>¥’ŠÙ“µ]ãCü/¯â¦6ýó}”Ü—¢¥ÛwÓqÛb ªX¯ ZîгÈH[ÎýTÍ7&ÏžôHÃERvVÆý1x5»é¹Ÿ ×îy³Ÿ;ªZêS[sS^í„9ë~Ttÿ47.Ê4^k1¨Ý8•™ÅN«G Œšçd]*6¿u…ŒŒ?݃¡3]—“úî Ü¿xªã6éBòÝrŠê†iAtÿ2®g¾*T‡'04’IúóXnÖïJ¨è(ÐÞ3xt¸¢oõCÀ~m* c7ž„½ý§r_V:Bzõ~):Ç9|aEÅú«Çƒv+J¤è‹ÌR¨Ÿ.ÐO¾’A©!€V†vÒm:–šlÁuszz,áV/¯£½ûtKÕxwÛ¡býÞÌÖƒ™·ÏZztHþùg….ífϞƽýT—uÎÍÍŠ…rå"´›Ùlò ¥¨Ø~Çð à¼( ÐÚÉ[A™Î-õlúwfhûXé˜ÚLQ±ù©c‰×Œ2ŠÎ=XÙçsÿ‡S•t·©ƒ_׊ÊçYëŽI7ÿ e¨|Þ°jlÑÍPù<"á*¸Yï'¨|îC\ &Þ™¡ò¹X'·2øu4Eåó+ Ãár_@åóÄÄNRﺀÊ磾”“ʧá ýÓ~‹ºmï¼,@åó꦳tø¼ý€Êç÷.ô…Ë(ú§ûŽEƒþTàÊ=WRR‰ŠãŠþé|ãkâJ)úÇýþ},R^À³àf8Ýܰ ÐHcªjšÊmNïÌüo{s×==K}‰âÊjg±‚›R4½y,“¤if õ­JºµA Ù»r‡©ÄBÓÒH†>öJ¶ÙnœÀëïmù´ƒ zêÇ3¸®Ë5y î4þÐ)Ð_É­›ê©;»ËÐLCW¨þpW@›¾÷f·ÙSôG‡Óôé3‚Šõ«¿\Ã*z¦Ï5ªfÍÝhíç7ÞБ'§ƒ•Ž”¢Q£uÙÚÏ¥h·è½9ª€Ší?å;7\¡¨EÂxXô£šk;/„6´ÐnXtâÄ~\±ù.ŠðkÜJ†šy†H%Á\±÷ÿàTg¨i ß255*”†Údsß߬§§qݲ%¾ÕG¹–Š,—næóX)¼›—Ž2XumKÐ3Ó‡Á’3¹R4£IŸùt«¤hâó»ÔU¯˜ býmí_±9.EK[½€°õO´ÝÁ;Ð’hÚi]8(t_÷Ù`á·‰¢ÝλÂÝÜH ’w¼;  1/ɱ×ï *Ö/¥ú>nhhÂüZ¸{3œ ®3ÀvN4Eϼcú]ä(!Êú€2§ä\₊í_6x\?RGÑ.žcàÕ†OÜÉŠH£R@_-U†{{_TôçRA•¸3ôGª=Óyù•¢ñTÃ=ȹš£€VÎ/ c*q/E(B¨>·è£ H4p#áIsœ›û¹¥Á#Á멺­á¾ý4ryKÒûi7@¿™¿'‹FÕT¬¿ËÐg0ÞyE?Þ‡¢Ÿ?¸sãŸAüƒhn ûuxoÆPͶ·¢õ¹‡­ÕÁÂЛÔæ9DP7c¨Y½›+Ö/kÛ ØÝ¶L‚îÿ®}›CQɬ °gS †æ\°íuú\íèv`á{œ ·Ót!¥CWl¿³ž¨š·`è)/[XÚG…ö ÔÌ%¨’Bw ç¢¸bóG¯`×p)Ú0…e]ï è_<ÕÎñßÈXÕ|@ï­ÑõÀ<î“X˜|9‰ûøM>ÙÈ=âHáˉùÜcåaHÍE‚š\› 1¿QôÄ—©°~ÁîÑ—íAr oV[ƒse×ëÓ|X™jFÐ õa°A²•¢býŒg?„3i¯(ºdI ?€¡ÍGô¡®¿ ×5¶‰ÒtæZ…ì칕 õçVý“ÃRTlÿ s ´ï¬ÎPØ2¶6uâz·1…¬æ ‚êyŒ‡ ûñ\±ù7ŸÒTûx@ƒºë‘οOpÿâ©}84Ís=°{.t2Ìâšì¸ º–6ܯóA~x5A¥)Ï¡öúŠú´)’cÃЮŦðÂɂۧæ=k÷’æf(VŒ— yêðÆG—¡bý}<Ã9݆žÝ /fFqã‡t„Rգܒ·Fdáí#ܵ?ÓÒùc¹5gz²®VªÜ„¸qÐãÔ4®‚q¹¤¶K8W¬ßþËn¥Ïa¨MÇC°¤6”{®oY”;‡Ø­ aó¹“_†‚OïýþÙç;0Tlÿ‚æ`VÒ¡5ÇMÀhi/î—õËA%w„m¯ V¦>›9l‘äzÿÃ1pÌý‹§êÖw3ÌÞ¨ÒÍXXù!‘›h÷Úµ9)EeÕÂ^›(ºÑ( 'Ä1Ô#ÀBtr¸n•y<ôáþ~vŸ–N”rû.K&–­Â¹_^²=%\±þ½înƒCå»Ú­Û"ÐÕÏàzÓ¨Û‡D®K®"3ŸåÇñ™5í—Pôá®Ù,»j%AŸ×Ú±Ó´Öf9 ¯%¨X¿§­b § ‘¡?¾ú@DÉA®±bœààɱˆ¤¥g¸e×—“k㸓ԿÒÎ3¹bûÍ¢Œ`JC_†&¼Ò‡`[3®­|¼Ý‘»mª1k9+6¿êÖCpºVKÐ3’§pnØlŠþ§*ÿ¨¦¤‚Ï»(@åóÈ’;ð`µ&CåóîNmÈí ‡*ŸëëÛ²n2ŠÊçU W³5¶§*Ÿ‹õ?°Pf}súÿäm†°]cz0T>7º0‚)tùçæÿ*Ÿ;˜øRØ™ èŸö;snø9 •Ïok¾£eCÍ*Ÿ[OZÎ2V›RôO÷7\ÒYS†0T>ÏZ $hå†þéüÂàKúD†þéûÿñ8Ü| ÿMÇSutɦ^€îMéÀÝ\ªsÒ”+­¸®šÓHí eî[õDð Ñ$h/ë(pRBQ×=úctn½¯?õ ™.E÷-!’í5—õ„’4‚ŠõÏrÏ‚ÍɳÚ§i'ŒHàºn4bfº¸*«ç³Û»r{ €ÌÓŠÜá)D.èÍí¨f µ¶{¹ WP—·þ\±~áîÛH…nCÍvœø&gsŸ-ºJ±±º&E€==QtiÇ‹0Ss8Cmç[Az÷\®ØþÂÂÝÄ«ÿ@u¤§ˆÔÌŸ«ÄAÍ*‰û¡¹ÚOíÍ›Ÿ°ì$ØmÓfèð¡ 9Î{ÿNuOCÍŒTižuuœÈ°„uVä–ôºBFMúHPÇ…Káù„›а˾ª ½â¹–öNÐ76ÂsÚК’¡ ÷B ¦ ô颀ŠõŸœmÎE04¢«+ä}ÞÁ-é¿™ m3€¢åëÙäm‘½sù©þlÊP£eáDZfÀMì7Œi}~) ’µù´2¦7 býÒçΡÉýÓZ<¦––iÄsÝÚfƒûFmîq“x0ɵçÒ^qôYÃn€Í|vÉå5EÅöW›WK﵀Π|OÆU­äÞ3­ÃŠ`-sÊ”½æ ›ÿ«O‰±9ÄД¤éT©<…ûOµ¡óáW‰+ Å ¾2¯äÙ\õÉïÈ ã‚NlR‚†m¹7&W³4z~é.¢ÜE‰÷™«ß&Ú¬Ù êŽÏæ6™Cšs#EßLCº³f¨Xÿ'ýAábCÓU¾ÿ~{¸½t'°W7M-*hÁH» Ü—+«¤gºµb¨BŸ^Öçë.P4lÍ ¢ß4ÐòÛ“a}OP±~^‰½Ymè&†Î»áÄjMà¾Þ· LJs}ž ‚XY —5Mf/‡~&¨FëôéŒp@Åöç´ný6û:þyÐ[»ˆ+ù:œû‡14à«Ù<4‚+6œùHæyp C©b(K©s è_<Õ²W©R“;suš0¸‡Í㎿­‡·#è¸JøÚ/7qy¢2ö>E]UNH»]>Ì}üfl,·Ô\8B¿³]ºë-ÕL½DÑw—5™™¢ bý“ÒSÉǾ»:ÖR“\}–À­Þ†*ûí4õE9l°«ñq” 'ÙLÑÀä•BÄbuî)%O°=ðM‚ê–…5s(*ÖOÙ­®Ñb¨ºmÛð=›¢ÝëÈŠ1Á ýå9‹,»Àõ^kî[ .Ú>þª\±ý&ê2›hwI?ë1›{£| 94–¡Wõ”YBóAŠŠÍâÏl÷+:wgGvùZ÷/žjºE(ùUâ¨Ìó8™•9‡ëöÛ¬ƒ"úfd_0—,çÚ¦÷ºœÚDQ…‚gR}-¹w‡Ÿ§½ß4wÈ!¢jÌвæùdÒ¡€=}®9qÅúW©î–fèô6&tŸûnS|_ðÎÜ ¨ú€Mp„¬çœ˜&Ô¯±ÐÀ&Âò˜z zÐBüŠ3)j2ï2‰™ò+ÖoçÒpvnU¦€&.cG?YÔàçC›Îm½šq¿?¿ÛŽËjö@mÂ[­oœ •íÕ*¶oš%\œç hó[¸r׉»lÃWj÷Kƒ ©ëWR{ÏF®Ø|»›¤‰}ólaý†esÿ⩎=]CVM h׺–Ðõ» ×Ð̦9Tû± è»p=m„_*4O• ã‹Ðó5K 1/‚¡Úæ'È‘ƒiܸJˆíz•¢?Íl`ÄÀ†ŠõuO¦ß,ghàº[4àÎdîê')Ð9ÂÐZy°.¹ˆ ¶^jBÀ® )êaxM–øUJа×2i¯šOu ‹£ÞÞ+¹býÂ&±â[jç÷÷Të\5YÛ•¡¾j¥t}ê}Š¥ß"'Vöfè†%祥e)*¶ßæü¸¾Ù:Ø×ܬl¹f6Òïk»q?º•Ã=¹bóµã²¡}áj@WÁ0I!Aÿ‡S•Úêu†¾ç•ÏÞŽ‚ùU·¤¨|^ñó‡ óQ‰ òù¶6áôÝ® •ÏÏçvf&Já •ÏÅúçõkÉæß1g¨|¾`M>n¿*AÿŸk­Ï¬<G¸rOâ×Ëôø”£ýÓ~Y9#YÒO@åóã{ïQâOQùÜöÚCÙaÛuýÓýÁ³@{ØP@åóIG;‚cÓS‚þéüW{o€é”}ýÓ÷ÿãIyöþ›·>2Èø¨ h»u¡ÊðAU6l#™cîp [#µoró³Þ j»¬U¥³‰VŽ7·BC…U¸$h™ë|jeê èY·µ0YE‹;ôm:Ô \JQ±þK«£…ó!§ êd©,tœp—;uD†ôùÃ@ÓŠ¾“-õ{¸fßÓ…;ÿÕ`ìÜÓýÜÃÎ@`mAÛeŸ†cÒ ë7 ÓJ¸¼Ò¡?ކþGü¸·_íYâmŠÆN †[;1ôWët‹÷pîŠ=Yj¹Š€Ší/Ù|<@ŸŸ¹×)qI¬T…ç2To¦ývâWl~ÅùXYXsC#Õ{±³‡×pÅÞÿƒSU6» Ã4c ªÕâ¬SìĦx“L¿y™ëÕ±‘$g2î³ÓúPÆúAK—v%ã\㮇 ðÞMnâÆÓPf€¾ï1~úuçvȉ&•ò:¦~(ÍMHãNÝ=[zÎ;›;*¶–j¼ˆâÖ­îÉì´{q‹†˜²æä“ë÷sçszÇ$¢Ýf¼£·Ïkr«‹üèm÷ÙÜ{ŸWÓ˜ÉÙú¸·–4Ç8š¢¾g? ©f§Tlÿ-—;g?—¢î?« Øh7÷GäFÐü>‹¡6¾·IùšÍ\±ùëÍö@º¢'Camj<÷8Uùç÷ïd8¼5˜¡ò¹~÷©aFQùÜ×ÇŸ•Y¢ò¹¹é¶eÕc‚ÊçÛšAKi ò¹Xÿ ³aS£: òyEŒ Í f¨|nyz"«YlÉPùüñ‚»’Ó–ú§ý¦i¿¤ ÐÕϳ¶¹ÓÇŸõ¤¨|îUÕ‹Æò"èŸî¯W¬„ÙoJ(*Ÿû´x$ížÆÐ?oï5]ºÃ1š¡úþªÅÕ ·‹¦P4¯M/ØÑ.Œ»jI¶;¥7 jÝG4–+¶¿2&:ÔÍaè¯X°­[Æ]“롵€æ–äÂF»Ž\±ù©GÓd:f£ jYI.NœÀ{ÿNõñx+aê×§ª°ÅGpLO嚣…ÑýÕ›:FŒîÆ5¼mßì h_çPU“- ~WÒ`Ür`è­…#€NÚÆ½¶@¦ì:MÐÄkVp1«'W¬èW–ëTBQ«@O¶Í ¸1ý¥ÙA m˜GÇÎæJÜiöË;j?!“ÆôÖ#¨Õ”B¯ß© õm¿Šéü ƒ]×Ãí û úOÕãÒV¡}ñ5›œ äêÊÐÁ BõU@Õ»„Ây‡‚–mÙŽ«=³S q}º2´l€‰‹Œâ&¼/¤~ZS¹ç=ìÀ7šPÔú}qü¡ÄP±þŸL½Xà•¾töÍY¬sÚ$‚ú^ëÀÜ&vgh6æ¾p+ECÊ6ÐA3« ZSë+d2ôt§¾ÌGu.׬ƒ"D–fqÅú¹›A]m&AWž‡ºU“¸»¼$]ï^¤¨jÀ9ÒbôMn»aYp?ß…¡WªˆfbWlÿ¡ ~`Ñ1‚¡ô®8hDs Ð9ÈUȾ-ù´œ+6íˆ@0Ó¼#CSâÝÁîÛ^ŠþÅS óÛ'(¼{ AÍNšÚ„HÑ#%±ð`@_‚*¾J•©€Æç>!¦“LZÑ«mØ[Ü›5ÿ* èbIJ§A=¦ÏV95Q4ƽ˜&>;*EÅú?1‘uõ,#h £5[®¨öAf>B• Ïçš³¡»UUô$ê¸_"Õ`CÂ$î† ÅÕE—{áe°: ëçÑn.EÇÏ‹…óZéÚ?!‘<1©¦h·U®¤…Ë-îÉ,©×j†ê\ÛÀŠ×;ƒŠí¯ßîUþ;Ú5Ǭvq+¦ß ‘¯;p[=iËšT† ¨Øüi¾†P·à#E¿«_"–¶dè_<Õµyû„Š‚J)êÚ°S°õê@ÐÚ#ñ s§hàѰ~ù•kæ?X¨úðœ{¦Y>îÇ]<ú"}У' =¥ûîÎæ8ï²™îoË-3›é‹þU¬ÿK;=¶Ã` «o7Q Ë™\Óͯ髯ÜÒKO…é4š[ÏæÀí郹†µqЫì9Alh&Zy»:°¼53™9š+Ö/àQ4X,¡èÛ°ðÝPÂu­o–šn¹ÄUðQ—¶aǹµZÌeÕ:@÷¯­"1_ŽpÅö«9CXX2Cu´ÀÏ`/wTP1mùù>A¤A6Æ:–€ŠÍ0JÂóžQ4ïb+iãÎî_<ÕŠö[„Äšaµu›.Œó÷äzxûÁú¦ ]j4Æ5»pU´И·»e¨ÏÔjaàÊþýåßî?œèÓª9 ZÖ‹[Ý:Ú ¹)  ÞK*ÖÕÒ#TËj *oM¨Æ¹­ÜϦ)Ä#h/×µ€¿ç.nÀµƒPÕù‚ív)Nù&SÔí‹5ë4a A‹Ÿk Úkë7}n8MPfhªùظ¸×6—^ wP´lò{áˆî$nâ´\þé¨á*èШBQ±ýC¦(€‹Ö>†î¹s‡9©\_ÇÄ,u ‡Z޲A­¸bóª{ •t(j¦;Ipj!Aÿ‡S•<uå[*Ÿi }^,e¨|n>@ÒuW2AåóüÕp¯C4Aåó­9Œžï¸€¡ò¹X…K 6W&EWîÉ[!¦þ€ÊçÝ]£àh³ Cåó‹ŸfÁÄ ýÓ~cÚƒý¾‘ •ÏuÆ  MU×T>Ÿê¾kƒú§ûýŽä’¥ý •ÏUæ-g¿qýÓù GÉžøô$Ü?}ÿßÇ$ù&ü7c†éC¬ã>†öp}OΤàÖ–¿ ™•!€ÖJáîÇÜ÷iàû­wcÇ~_y®HPÛÏÛ%êeº€nþtŽ´¿kÌ5·‰“Qã¹Í®…0pm9EÅú?ñþBã²gTGãc~œË6êÒ!þq€¾œ‘JzLåÊJ· ý6„R4Æÿ›`Òb—€*Ï˧žÝ6Zo:þùIqÅúi]ï Ž#[1´.ÿiŒèÀ-uh h~i (¯IÐGý ¡µZ=Eícg›Û5*¶_á×FIý‘y5°¬—¼;¶Žë¾´ô’6pÇ]; ¾ZÚ ¿ö˜rƒ)êÑ0Aý:B@EßÿÿªÚûȨ4†Î¾ÒüzµŸëwfÄÍèá^»Àî¡wDx!ôùœ¢çÎD@ÂëÅ ÝÞÂ,.·ôUÒ<èò4 Õsªˆßè] ݳ[Âû·ãŠõ·qœVÙ4´`B0ý”« èí´± í»‹«Õ˜‡Ÿ/ã;^L7<—¢ »k‹Œ„tiÀ5xîýAŠzÄGB^䆊õs»FÉV7†&š“.Ü’ôÞ¬IÑ;y'A?ÿ WáåHš×˜ÂM©‹£vyã*¶ßCÇT:48” V‰ ¥NåÑÜü[ `fDC ÌS¥e–i\ÑŸò#YBD ‚*\9 ­ÔãþÅSUP*ô~°—¡v×uh–÷.nø›ƒp}^Am‡†Ç1k$èêëm¡ºí.†–Í›-ÜV㎸¼ŒeAš7Ö¢¨•OW¶WÇÐ=l0¸ ÚÍë_íß™vK34µ¬VV¸z×)šÁä¾jÜê¦ë ò:CжcJ¤Í{ ›_í oô«¸Ê?-è•‚] -öðcî)*ÖÏ%L™$td¨BÆ*‰âëÖÜK­öCe¼”»lG\úµ’Z=ZúÉÚP§¡†pô¥Wl™u‚ôç‘D‚jÙ–èîçêX²¥/¦1ts?’, bó½œýÉìà³mŸòˆ$Û_àþÅSm쵓zïb¨cç»tNN0wõç4¨¹JÑî; u¿– =ðEƒ5^ŸÈ3|Û7‚¢zB;è¡ÛÄuxCn8þâ:õ¿§³{4Ï#´.F1T¬¿ï±ré¢kv€f-!ukÇqµãËàÍûk5sšb¦0Tò¶–Lyp™ Ï½:A쥃Ü=ñhZÊ@W>OÕ’¹býŠ_ ®ßPÔwº„>4¸%/†B¦$’¡×ËÉÌ q\‡ÒH°}DÐyvá°(êEÅö·s®‘V´?DÐ¥ŠdÒælåÕ‰ÜÔ¤5æ<„›i_]¹Ã˜+«ßpœ nÙ*¬h³ a°\Únh Eõwäƒ5·Û¯;ÔmM(C/<^Ìü”wT¬hÐb°l ?HÔM;®‹Ìò£ã·ð#)Ó;ÈtdÞžJÐÞlà’FŒµ™s.— "hŽwØmf¨è§ZR0]_BÑV£öRc·6Ü\ÃCÒeç·3ô©ÚTú8ÅŸû@¯ôš9“ëÁ¦SGk®Ø~?µÎÄxÉI‚Žw2!ºe·êýøv/Pë `¢aÊ›œ? jmÃÔíµ9”Œ¢è_<Õ6]mþ1€¡Ý™3ó_Ñ–{`P#ù]àÃ]Y3ØßðâúŽzmcKwš2ÃÆOMáê {"úIQ…ÀAE£ýZÖê™´r`: £v‚Ïm+®è_ÅÖî Ô0 ÐD kˆ2ïǵݿ]æ·wC­z¼¦çéfnAGyîÐL¡é‡E«s¾ Ë]z/ΛµŸfOQÑ¿ŠÔCTa€·5¶?“>fêÝ{è5ȱg¨FŸìrkn7¡+³=9O@;ô>F Tô ˆÇ2,²„ Í¦ÓI}~wüÊ+ 0þ™€ÖE¤ÂÆ·S*6_ShWÕPôMÛ(Åš¸ÿéÊ?MOæ0éš+•Ï÷¼i)¤G†Ê篞'ƒRþr@åó‚Û¤dïi‚Êçï^žæ •ÏÅú;ÅN†I}µ•ϟسÂ)½*ŸÇ­í™E)*Ÿ÷ÒxJiïÕ€þi¿”; ´mbAåóbͦNð¥¨|žX÷˜xùú§ûofù’:Ë«•Ï[´Ùž$†þéüb‹ëYÅUŠþéûÿñhŽ»ÿÕs>0$nA%m–Aí sé@öHê·zvÚÀUPu”í{>’¢ =CÓ‚ b¡E(Žah¬3Õ“QÔW¿—´çÂD‚N>©¤ø&W¬¿Û¯ídtÖŠ.u#¿ \‹ºôý@Œ§ãiÛ¹Å:Û%~´àzí‰&ê:sõ’ã¡ÀКk¶+Fl?KQ±~ûV<';Û÷a¨ÍÌÝdúñ¾Ü„‹Jp3#–ë4ÜŠ´èÇ]L¿} ¥h±I \@P±ýã¯ç€Y˜EãŽ=ƒ¸ç·èÀÝ †L /¶FqſׯKAy ?ãî».ÕÊኽÿ§ªýÔŠ{_–¢_†¯U'$ht¸ÿÇÐ Ì b™ÄU˜´\¶þ»3A;Y(Ï®§¸eš™]Ê0î’¸TÙÛPYåHõÏ– ÒyjÐ~¢býú‘®G)ZVqQª°v×lœtöT¹õX¢>“ëTÓnSãöw]!è¬ÀÏdŸÂV†ÎØ’]Õ…+Ö¯ünO¢ejÈPÛœã’ÛéÚ\YT'z¸~ ÷nÚš«>“{æçb"‰èiæ‘T+¶¿]}ôy¢%¡óª2nÏI“ؽß_¸Í‘3˜íõ"‚ŠÍ¿ÙöíüÆ’¡GX³ì)ô/žê\ãPöh–€~JY‡{)Q4ð`WaôÃX†V+¢,a-·¦}y¶äA û«‚¦)7g’i—±ÐWý ÎÎá*håHzôQTö½ ½Ú býmÇJ_tØBÑbcMIϘÉ\3ß,øÅ”µ®= k7]’¢V#æO›¾]=q>¬-,ÐO§:0Êýâz”T Ý ¨X¿zÝë•Ì mxæDWŒ,§¨²«Sý®ÃP`{¦™CQ¿(ð)ðвGcÁåyo†ŠíOöÛ¯(jw}L<ÒŠ¡×Z§jj¡€'5’Íû¸bó󴺲i_•}ð¥. àþÅSívzh8RôÄŽ9`Ø+‚ÛI½S¹kÉÐcŸÆ3×Å%-ô°„7¦›;yì9b&EuÖì‚Åé-Í8 ÅÞsôàüÄÕ«5 šú®°±p9áŠô7˜~\–’¯MQ$S¡Î÷°€Ð;ö¥‡)j3'XhÉÐ~—àÚ”,ŠZMêF ¾qÃ’÷‚ÑË‚Þ.Ї'…j ëW­CÏŒ è”,j¡Ü’›Í&1…+¾Rô—Áp¶\å AÍ4H}ÖŒdèŒwUÔðpR6¨nEåóõl±ì €Êç+Sœ!¤õK‚Êçbý=6æKž¦®"¨|®¨¨Ç\+RT>sïD7ÌhIPù|±×n0²ZJÐ?íç³;OX’ª¨|^¯eËjâ•ÏËL‚Å #ú§ûãzÌ„ü/³*ŸŸ×šÅ. $èŸÎwüTNŸ®sg蟾ÿÏ›‡eðßì™aÁÉñ í#S‚¼ŠDn†¥¶ 2ehá€ðµT ¨4o4^aõ1:«n¨PtÍñpè7ã;WñAðéçÌP­¬ÎpàN>AM]FÀ›çŸT¬ÿ™ñæ{ Я-¼ÁLa+×^ˬQý×±†ýa˜sw|} Œì¦Ç]¦s»%hšs9fM¹“žö‚2G®X?¯ß… LyFP%Ûb¨pî·àýÐܽCK/l†ü>\ÓÉΠ±µT†æMì–.QTlÿæ…þ~1„¡ÉóAii7,dÔ[+pWÈnsÍ¡\±ù2XHP4"hÓïpˆÌ>LQ±÷ÿàTçéŸ"/g$3´Û2Ã2‰°|ÑÈÕ˜·V¾XÂmy*®vÕåêHúA–Ù<î¹ëߥ[¸rÓçgPí¹yíe$hÒŠ<õ.9÷äŠõº.ô.ôni dËúj èV;p³e«šçµî’¡/’(šX·¢-š±ÇzwïFÑdÄvF W¬ßËgáÊÙ4UŒ,‚d·£êiÖ‡14ÆäQ[ý¾~$ vzFÑÀºra@[u®Øþl/gH™ÍÐÞ7%³h7äüzam¾6WéüQú¦Â†¢bó VJAÿ¡9CÇL "Ü¿xªŽ -¤¶ž;únþ}¡ôc÷ÂøþàÁ•Z ‡ýb¸––úäôƒ%ÿjHÕÊqMªZ±ôv[ôaéšz§Ž ¾[¥í«q×ÌЄÒ>Å\±þ톅SmºúvDä¹IÐ_ÕÇÁìÕ} ªWvJ½Ê)ZÞ®'¾^ÁШÁz$4rWáÕr‰s¦-E“x~p#¨X¿Ò;9ðÊ^›¡÷æ%Aaë \ƒ6߯M鱕ê•úq —ÙETwB.éÛô–+¶æ #èZ˜ÀÐ¥ý4`ŸI×ió.z©õ^‚æ=œ/-R6Tl~bû±Ô%¢C 7ÔP=g]ŠþÅSý8 )‡2´C¿óÔ,tW¯°¥`¤Æõ¾|”úMò↩i°yŸB(¨Á–¶ÛDP…ƒ­mꦴƱ5xÏÎ]þy4h• ¨UD9¹ó˜¢bý¿œƒˆPG‚š:õÃ6ËÐN'#à^óp†>¼0.vÝÀ­ Š¢­Fq¿Õ`¿Î—R´Æ÷ Ñ鎠¥ñ–Ðføo)*ÖïBôFÈP_ÇP»Âá Ü6’ktE‰Õ6Zq·_Äz|@ÑÍ—‡@M~ Aƒ“ÆAbÝOÛo˜ÞH2¯$3ôNâyò.f77jg[È%Õø¸ ”^$¨Øüý( ñË!h±j+Áq 9 ñTk®*°­…n -?Õ•-Ö–pË~wbfŠ=¹Ÿ|$lï’}­ý•L«†ÔçL'â}{7< Ô~tâZ{%ÞC÷¤hŒÍt¡ÔÞŽ¢ÅÒŽiëotâ,¤¬_JÑŒ3§á“ÃSîØÜx¢u?”¡Ÿ¾µ =5–s­¯ôb.­HQÍÀ&zòƒ  )Oõ`þ²pŠŽYHZåœáŠõ«­þET ž¡žû“n'ÿUaùhöjÿUóÊ–‘ + jìٙܧ¨mæm©ÃÜR®èwÉ ÄÃ7C×¼v"úÛ’¹‰ª¡àÕœ# **“X*1Tl¾Íƒ:’Ui¨ÉZ/(–jrÿ⩞eΪè2tñ4;vjÀkŠ*»Ža.FRÔD°dY5¥-ýl?õ½½y= bCt¹ã.o‡Å*•ݹÎü´F3ôÓ¹v`ô³œ KìÀJ9H@Åú—ÿÊï{=šý~7PŸÈm“ö–·6áÚ ìÇìâ)jè¡F]·ÌT²0X^ຶÆû¨†Ž"飽Ÿ býì~5È*–lghÚ¤º*b7øt–½¢= Š«îP÷gn`Ñ]ÁöxU0²•¯Î!¨Ø~­¯¥›Î&2tmÇG’Ö=¸›ò‹É.ÇÁÜ‘Ç #ÛªqÅæw> ŽÚtÛ—PèÝ:¢ÿéÊ?ϦLb_쥨|^=£ {¼ÝPù|‹K̼Ҡòù©MS‰fÜt†Êç–If-Eås±þé[á©ýJ†Êç_•ÍÙºeê•ÏY§±Påî ¨|>¨¾5$¶Ï!èŸöëÑæ3Íi9™¡òùz?uª+PùüâÅÇÄfþS‚þéþò£¾Â‚œ8†ÊçÙ¹G¨³µEÿt~åÏ!pdÉ †þéûÿñ´÷(ÿfepI¬MÐUñEdÛ÷»nÆÜÞµg(½îÄ"SGPÔÅþõ)2äV5×PÕó©mU“Ã{ tÉsP±ÝØífyYZ5Ðçû®Xc kâ¡øœ ÊÏ·ÂFîzÉyúøM”-’è‚ïéU}gqË(=á ±r¹ÊØÝFHP·Fp~EQ±~e1P¶w9E¿©o…¤Å·¹ÍŽ¬àŽ„;©çD¦\BêcQ'y¨ÍÐjÑl‰þIŠŠímxI_t¾KÑmÚߨªÚ)î)£k$nør†¦¼[K'ëãŠÍ7p˜0@Gܷ¾8¡bïÿÁ©j˜>$æf/:|ïOr-ë!·¿‡;êÀ3¹ÞÒtíìŽT{¾9÷VN*‰X6“[‘+7Y2Ôyk?0iËÕ|¾‹fsÕé¾Wþ\±þ†×d„<üBÐ/·Þ’óÃ>p‡y” ŸCÔ-3“!+̸BOòuìŠ6=V”ž<ÍÞnFÞ$q›.DÆÄRŠŠõ»ól,¨ÅÐìp{«±àšíÄHc@³éõ€©Ü/O:0ÅÄaÜŠOˆ,r'Wl¿r¹"køº“¢;޵fº{s«?k°oD™«ìt¶×QTl~ÄÎbX<¢?öDƒ,d.Cÿâ©~íÞø QZm@B$·¹3_ …Òöþ\±~KÇéÂøÑ 5ßu ø=…»Su 5¿ hþ6 9õ8žÛv›väÆœÏŽ* ÛßͲ-»¯®OQ%g5æä›+ £M:‘°Z@ Œ»¦Q\±ùç’+ÉÖ܆¨eÒ )k¸ñTוƒÃ¢L‚nL ‡{Dq÷…çƒí1´7XŒå~Yº»W(ho¼Öš ý1d2»ëÖ$EïÍmÉwôòŠbXòmASgÁŸ­ ë_' ö³ú54<¾ÈýÑ´—¥sëLƒA£ºmz"{1› '¦E¿ð'Ü[¹CáÙÛm½k4“Lt~Åë§òd"©šàÂвÆW6Gs­]Úƒ£J< í| qj7ìa3雲“¡¿·´e{”GqÅö?jÕž¥.ö•¡kÔ˜ëx/)Ú3oä„t²^,œ»TDQ±ùÂÈ1,}@>E›ÞÛ²Š· úOÕ,DÖf^m7r$hTôá*•m‚ÕëÝÓ6î¯Â}£¦J?g2Ô#ð®4àôn~®Q/þçSâÿÚ<Ë Ê·p{Å«²õ£Z³VŸ©êôT¬ÿÆo“Àé”A3w¸ÂŸ0):«l ,vßLÑÐlx.mæ¦+·´j‚jü²€–Ÿâ¸ööZ´¡¹‹€¾¹ÓŠ}KP±~-ÓÐS3Lj‘šFMlT¸??%Á„BG@=oÀ¦w¿ ¡4Ž9º¤rSu¯ÊÂúE*ú]yp[V–®@PÝ‚Ö,ŸŒášš˜€ÇP_íRãCÿ*6|~(õ¸è€ÉM¤qýNî_¯.Td '–T>ïÕ2JoÎe¨|pD JGÇ1T>ëoaé+F-¤èÿóú(-^؆qåž+Cˆð,¡¨|îî—EænéÀÐ?í§Õ]‰ïµ”¢ò¹RþQk1T>7~½ÖmÞÌÐ?Ý4ýµ ,#¨|>½)QxoèŸÎµ9ZžÓa蟾ÿÏÆûü7Ïݰf “(7c‹MPçî9ÖéË 3é ë4â¹O÷Nv’ îƒJUØóT™¡0ÿ(¬˜õž¢ôètèéÄP¥Ôh?˜[äÚŸzEqÅú{¤—=—)EÞ>°I8iDÐÝóìhþþfîµÉEÒõi}sé3½î=ˆ«ÐwªÌ}i0·cã^ZéÅmì·2ç.áŠõûâ9.÷YÊP é+²ªv#×óÅ(xðP‡ út,çÙÊP³CΟê- Î9oÉØÓÛß«t<¨Œ1 èâ'¨ò}/EµÖÌ[évªlh*k(*6_ßð4<FQç%›áVÈ|†Š½ÿ§ê¼ÖŽ™tÉ— a¶¶l¹™ A½Û|&¿b(EnÈ$•;¯rÏoOj’•Z»^è᳟¢&ë¯K—†ogè—(UֹȂ»¶BÊæ*î%h¥Þ#’×3P±þ ³¥ÖàKÐÄwï¥ÉN \ßÙUÄwž C,aÙõܾ–°?1œ{Vw(UOâfúœ³qqÍŽyFîÅ0T¬_þW'R½~C3 ÓŽ.ãN­%ðã‚1E]†ô|Y<×`T„ÔºqͿޕ¬ùЇ bû=MÛÄÝRÔqÎ0Ó–»íB)KèÊÐë»ÒæW)*6ß¼*–|”D3”v»IÍ·8qÿâ©úéa{«sºz¸û:°™«·{¹?˜R´¢¬—tME,÷ÇhmjÐCU†žrÐvMŽ"èÒcÙÏ¡}%¨±sk¶¦Þ ЫÓÐ[K‚:·oCC*Ö¿hïH’¼:› ƒ·‘§¹öýÀåŽÙ\ÿ;þ`+1—¢•C  õÙ,‚žYwRB(*ÉÈ:”6pß¼~L¿,qT¬‹ §Ó¦81Ô©ë+:Ý®צ2¼È(Z[LzݽÆ-ìHIj#hc›¡Q©ÌÛoã=RGµ—¡Í«¦BEkFË KѬK{­Eꀊͷ} ìöZ5ŠÆMéÈNÏé è_<Õõ¾º ù-}îcËM=7](nÕ¢õÅgM% ºhéæRCP'wÐX^ÁÕÞ$­èøw›È;©c Z2ô¶ï~¬æ¨Xÿìo…„ÝÔþ×"µ/äF=^õGÖRT}Ÿ /yõݹp=̉¢EmF€ó…)ÜÏûa–ƒ2·íº1òÌ’¡Róc3áŽÆ‚Î&ï¢èoPÅ\ªX„#êÏPÅÜÙËœ*½]% Š¹ÿ©›ÔwÌ8@ó×õù TkU„6·ŸÉÔ6Ôrá|@ÿ¯~zÄåïÝUÌU˜Bd•C›»ÿš‹ þY@QÅÜ]¹[í¦OÐæÎ·º°—îÚÜ÷ÿãY:¤þ›Ÿ҉ͽ»ýkb9 SºÍm´È C<å+7‘ÒÜÜ·^–,Þ`<7ó°œ^½>P¥÷÷Å-CT¸k’›Óús¶0„»hŠÖf¬Rœæs¥ú?—Ÿ„’k…"¾ýh»ï¢hG÷$´&‹ ¡O•Ê:¬]\±ÅP•Ø*â¼v·‚…‰ýí#º(½/{~h‡€Jõ»”bJ†%î%èúM1¤Iv‚ëÛ_Cœoj èk» 2°ÀR½(»|tC?Ž´f•§PTjÿŒËÑPó±3CÉŸpb‰%·É7˜dÏú‹ *cÍÀpÝ#®ÔüøQóhàT@óm(‘ϞǕz¿§šÖñ‰s… Ï¯¶€Ùf”;çlèt+{ÿª#Ô¯ê˵=<œ´w‰ôK+?0x²€ëúÈZ•µçV-ÞWnüЗJ%–EÐk†ýáЃµ\©þ­Y¼©nÁÐwû·Cit_®ÚAu(ÿ;ƒ ç¾÷‚Ym츎å# ú  áñѰæcA:]êÖEªÛ/¸(¢{p#j|àðw\ÃkAvØ¡Jz[K2íý¸RýJG´PN$h½µ,;:‚›?. }Æ ]¶l§Ìä^;š ۲ܸÝ@¢›=C¥öß7ó„ÂÞ m/:A«Yã¸}¦‹$ó W¶´ ]‘ÜDQ©ùòËÛÁèY[†žÕéÚ«Wpã©.|Õ 6ßKЈïvðnH_®ëc°Òˆ§hwËÏ$gq!÷æ÷$ø2Þ™¡¯ª àkÛxî¶ýI²¬´‘Ü©/èÏûw(ºuQGjº©† Ó¾¹ÁÇ9†€JõŸœÞ–tcèö«¤ÍÌu\C¥_${\EïÙ¤’!"·x{ùØÎ„¡nmÈ–éí¸ãÆ+±Â®ùšûç Rm0PÉ¿J';Ø[}C@…8@P§“%¨oÎ*ˆr¥Ü ½‚·ÑLŠ6˜6þÃÐÊ!©—J+ŠJퟻÖ¾Ï b¨~~Hy½ûò4­.—¡AŸzÐÌ~í•ü©<ïœH æ14ê«1»µò!Eã©VLì=†=Ð} }Gà~¿ºŸ,-,¦¨öqB‚’³¸AâR±.{CŸûµcÆ“;rßLoË„Ž#ZP5‡>.wtÜ©8ðÞ{œ¢Ñ)™Bïj†Jõ÷tv$×έehn’‘Øvx47eÎ áØõ5½¹OË(© ¦æÖ'{Ó(¶"ºüãJ¨ð*$èü•>pæóŠJõ«e¡‡^‰èÂAf°þ}E^ kúVrGõ€i-µÚ7·-ío¨þÊîpä¸ WòÀ %8vz&C½^ß#Êßçpí^Þ$^¼­z ÇÓÔ¹’ógÚ°>J© IXHÏmšèï<Õ@geªí´Óî9䉨OÍFáúÕUJównÚZ&¢ƒ. ìõYÝÒµ%«Pê èì3çI¿ëK¸÷ ¢!~¦Œ›iÚ†?Ú‘ &¥ÇˆûëÅ€Jõ7º€:ìch=½IKOyp• gŠÝ†E hTúrÙŠ²x‚zÅW‰~sc¸U»” ý£ŽkÚë«ì•‘C¯8P×f•ü®cÓÚ¤l§è»¿ˆžÁqîáÉ$†ÞÑ &Ïu¸ÆÚkà^|¸€Níe ý«4*µÿÉÅBb“ÎÐÄ™ñäMÈ¿ ³LŒ mš0 ¶Dõg¨Ôü÷ùßHèÞµ€.š¿f=ÀýNUñ)¯´…Àé(ª˜»ž3”;¶›, Šy©óhÙ÷©«U̯„€èZUÌ÷žË‡‘L ª˜KõoÜК©Ç2T1ïg'ÇŒ[UÌ÷]¹I¾?xOPÅüþdzÄìÒ‚6·_v g(ª˜U™ mC?PT1÷yî+4¶Sbhs÷'÷u%š‰sª˜»,OÖXÍbhsç¿ëWKj˜ mîûÿñØ:ÿMÙhOá{K†Æ¬Î5†qh ø±½3 )éýá]Ìg‚&tœG’³K(ZëüCÖàà̲V¨ê¸˜ mÓ‹}ǹýÔfÐÜaãª[¯ÎF}AP©þǨ’äûZªçKÌj²¹t!}ÔOŠ.MN#² =vj:mÒ(¢Õ]]èà¹;:2+¼Z‡P4oÂâóÉŠ¡Rý|{ª±Åíj)Ú« ‰;÷Üçb¹Ð.ÚPì;›{±’ÒTçl‚nÈÞEÈÓ±€JíO4›!«ÉP™÷Fñ­êbni•+\Ù›EQ·!'ÉÌ/: •š¿_ù'é-o hÊxÙÿO®ÔûÍ8ÕëS†Ó>ýêÕqÕ¹iÊUZãmR $f-Ý' Qåq.:Órõ—›Ë‚^úa š“‚¹SìÁ95FDƒ¶Û@î…É€šXí+Ë •ê¯z/™ >NÐwçˆæÜ"n²V术ÌÐ|ÏqTHN¡¨O§…BÚ¯ö€Ž·ª&Ó·Yqå‰Kè›ÅE´fW"»þ Rý6TtfF“£4ô˜6Ó8¼› /|ƃÕ@O@ÿ‘dT[î׸MP¿¸Ž ÙI›¡ñºC¥öût¢õÏg2ôò¤Pú§v÷ëõâ.ÿ Š~_¼›–ö:" Ró;Í›E)y¢ÌÊÐfèo<Õ¤žW©fG5†JýH/%^§è°šIpá—&wþãA0J5ƒ«±êY×ï0Aç1‡¯n£¸«Í4 eR&EËj&C Æí6‹4¤x3ôù‰ó4JÕD@¥ú‡›ÿMöç"hö2°œŸÊ­.H§® e.èÈ{ÑÔNÌ#¨õoð}¨ è¦1qp0Ô— ×ž´8>Šl×>~Ì£¨T?•YèÍ=ꀪæFÓu.ܛɩpÿi ¯B4 2Ü…+5ß±Áœ|0êÎÐý»bhóý§ªj¯ÉVXm h—Ûí™eçÖܰ´Nài_Ãõ>XAœoä:Ú8ƒÊ¼¥"ºÓ˼ÒöQÔâYOqù…ÞܦÄ4Ñ5Ì Áòȼ+==³- ,ì TªÿÎ…ÝÁ~û‚þn…Ç-¹…vfâÖ~†€šx%H¸s$À•ÖÁã4êÇ1ÔÇd¨ÐÛ`EKÕºïMÛ *Õïg^Qèìè˜_îDtžÆ­KM‚˜G)jç¯Ïudh¿µÏ©…º ú­`àÅh®ÔþÂ÷¨‘F_†.{ÖD7¹våþ< ^ùŒ l†Õ)*5Ìßetco ‚ŽksNŒn2ô7žªÊ}VÛ7Ç-¡ÇV›w"hì’ Ò5ê)EÛV ëtqßÙä­ºG\Î×e™‡þm¾9±ã AíÛ€ãŸ?¹kÏMM F5¥• Rý4dØ{—€Îþ àS^ Gµ÷„³yý }ü'4ÜüAÐìé/IŠßh†6ÎO“[¾Ìýªf ê¾Õº3dRTòÖí‡äú™`@Ç$ö‚ÆŽ¾Üœ þ _7¡FPEtÉîÏÝð§çn‚>Ê]oÎÊ*µ¿ÂO“uц¡7ãuYcæGŠN±ÃZo†vŒz&¯¾ëË•üR˜ùŽŒKò´kÒ*PUoÅý§zÿckÖ¸nAë~)±¯ô7¨›–èµ`EÓã#ÅD“­rÔKŘFLŒQ%ûZ±ávA5æO†Ÿ—grTÍ7a­}yÛè0E+&¾YTª_˜Æ¨¼ÐИ§Ñpd¦&7^§5 ÏÊÐqן‹æšq/<*üy&’;£¢' ¨AQ©ýw>3÷w—(:q£;qn7TI›u­ÛÁmµ[™ý8ý† Ró·í†7›C›ûþËté¢îÎP©~vš«©Ñ¤:‚ªUO[éòZ™žhÙ…¢QwúÒŠÁ.D~Ê2K)úÝA™ö¸ôU@¥ö‡ß‘¿Ø—OP×x¹lëçbî¢ÙýÉòô…€~]Zú2®ÔüÖw¼àN¤2CŸmK%;s㩚øØAÑ¥Íò±‡ ‹¥.¨iuš°æÂL@/ ~LºÍý“[ü&‹jÕv`èìJl\eWõZòˆTÍ(¢hùÓs‚óºLnøGC:ûx1·°ã„݃×Ôgb[rån­ÚvëtE@ÃÓ\é2½-¯›F7ÝJ‡ß%G”q_>Õd&‹ô¦üQíºP©~7ô¨p‹% ûê¶‘'–v\¥Ô¶ÔéÎj‚FÌ—Gö¬ã–Í( E·¹ôƒý*µß¶`0XïA#Ôæu7¹æµép¯IK@ÕŽ†Ã†¿º1Tj~Ñ¢p ½Z24ùÊGj”ÒE@ã©&ßïu–q"ú^ú­S§hо$¶˜è¶îëáùF3î“Ã4¹ ‘  ¢5È—CP¯ª;bЦD9ªôС$ N´´Î Zô·â†½¹HžVRTªTÄ’±£ˆ Þ%È{9×âÑ>áRg+@òêH–1p›^ŠŸÎŽbèò/ ô¹M%E}µs¡Án*AëÎ…´mÓ*Õ/,ì y­Ò Ðòp/´×p½5Ùùêß®~u•ìÔêÊÍKn ÚG÷R´â–p'¿Wjÿ÷Æ$rÔ‚z•z‘›>¹õË£‰Iä(†ÎŒI¦þùÜŽJÍwí¹ŒÎÖ ÐÀ¤S$9r:÷7žêÂN]! q0Ei¤Í_Æ-½™ åÞÙ-RþÑÈ6]ÁCÉÐ1ù `§cAUò©YÞÐ×Ó¼!Øá:A•.q陿G@'¶Õ†þZÅ•ìß«–ظ!¨Ïm5¸ÊÕFAÝ{S@ƒ÷¯ƒÈ’<‚ª„j²›Þ5jçJo$ëŸÿŽ:Ü…¡1oÑÂM•ê×ÖÊ ÚÇ?#hÈÅ©`px+×ü3˜ÝZЙ,ºŒzNÐÜÊÃÂûYé\×Ç&PöùWj]úròãAA—z$“Þ=ãzÈPï­ÞÜÚá¢Ðë¼! Róm^FÁ‚g½Ý·*ÂÝËdèo<Õ@/uHÛ—DÑ…òwd·¸ûÅ-ž ºÍýê­œJ®-`ÿ¯"zn­(MMÑõÑ« )ÏZD§5º*Ï ·ÜCÑŠðé¢Þ·F®TÏ=À'l!AUzBhž)·^+Fg%Šhq·µUù‰¢ s51hZ7#Ø©îÊ…7øÔÆŸ´%Ò2ü(*Õ/tÄxræ½€®šñχ¨1ÓDÔ]/çÔslØÞ{%G/ùO²«Çrgr‚ÒªúTxk±­PtyžØîÌP¥r•’£M¸RûGŸt‚Ü>á5WvËíøZ!ÑPÚÔ ¢.·»Aék-†JÍÏ=þSöýT=EËjãhmý•z¿§úµ¤Ñ89‰¡¾xÊÚøqónÊ«^â^±îÊ1åöžÖ‚™49Ô«~6}œgèB/gR³$…¢éke•†…r´å@gÿáhÍ¥ÅÐæô~ŠJõ/ŠË‚½ÛQ4dÅ.ˆÜTÆMûQF ’1Ô£þ¨x3únX„&\ü“QôôÊ¥Ä)é9ׯȋ¦çúÔâéÒrý+®T¿{òÖ0ûç\†N”o!²”EÜ·_;ÁÇ£yͬ~Bl2j¸×„Ñ »î è…›H™Ê7‚J~)ôÇ3]êºn8„:>Ш^¯…äSß)*_ñ]Ì+ËQ©ù©Îteûõ™¸‘Œíèo<ÕišzTvÚ¡Ç‹ã©üNon°\•‰yubÀ´Åí2ô˜_6™úz6 ! ,®¹q«#ړη×tk—÷$õæqnáToÁíÖ/ŠV O»¯J$¨T•c› O«Có®.½a¸á‡êhKM-nÛ醌:QŸÈa" ÙKÑg}iÔMG9:w¥´hAѺuHü®$®T¿†-gd;+¦34Â7€Æ— á¿’ÜzCÑùÕѨમv†#ûMz¯…:lÿ¼ƒ¢RûóöŽ„J“xm3hŠ ]˜³VИFÐ Joˆæa®Ôüªèåk¨ÏÉd°ÍKÐßxªkìíñµC/Œ{CŸzEQ²–¬þ#èèý™´‡Vo@—^Û ®–4jý.w,£èU€ªl-‚yí .;")Úw|w˜©DÐ6ÅwHaBE¥ú¿³ôçwcôº#D†ÍäzÞPeo––tÍŒ j¯L]ÑžÜu” o´'½?:ºXXZ½ y-¬áEë\©~«ö\§³´;2t}†&sè}Š¢ÓkÅðQ Ü- ý¨é$ ý’* »ìFQ¥ø!jøF‚Jí÷6󅆯r4b‰/œsÈÑol ÅÝM@µ>«ýóÁ`E¥æß ¿:·eè÷G¤´&„ûOUg¶:+Ø“IÑwÚ³>ßûq+Â6Ë+«¦’{ˆtl¹˜¾ÞÊÊÇ2ÔÆÝ, çþ1ù ñý¦Æ:l$ÿ”úÏg¾iÛRI|4­€>Îׄ½_Tò»ÆÚ³äê…ù mp·'SJfs[÷ùƒ«Ç"ú›4åÔzbJWî½ð¶TåÝnØ“.ðD'‡Ë} äøDŠJíÿ¶t ÷T£h©ù(H¨À]¼¬;Ù^’ƪ˜'Z%ËP©ùCƒçÓUj9¿#Ë<ñBD㩵îÈBûÝ–£9õÙœ-Ê}™Ó\ç:÷þ*øPfË­|„¾›Ò•¡W¶cñí¢DÔ~h*µ›ÇõÖ¤†¾µ½ª<ôÚm£¨yÂ&¹ùª® •ê_jý@n¹'¡ÞÇÐ3‹\¹Îÿ|µ[Ô´0{;4Y›ô›z"„WаMk óÏGýt9Ž&œ½MжãlÁÚÑ P©~Ûgý¢­òoôþŸ‡éݦ.€š6½’ÏÑ᪯‰%U­¹·ŠÉ”.† ͘E#&ô§¨Ôþ>+F@UÆ,ŠºD…‘6qÃuß W¼*j<\Âô>s¥æ/v¬¤·F몿ք¬ZÀýNUñ±Õ–}>¾ Š¹úü4ðW9GPÅ\eóyÚq«2 ŠyEQ$I ¨bî߇ÒG¥7ª˜Kõ?õñ8|Ç¡Šù11†Ö¯¡¨bxÔvÊPÅ<ác*t[èOÑæö{· Ök ŠyÀduPšÒPżâó7±qš1 ÍÝ$ „™ß³)ª˜> ‡åG_ hsçÇ. …;ÚÜ÷ÿã™Öj/ü7ià=ZøFDMBîR´Å2î /pÜuKDs eà¶lEU²7Ò¾›µ=v6ˆô7Æí-É–AÜŠÕ#!¿'7bñ Ú&¤Ag–´øç3S_@¥ú§\;-ˆb)Z¸ÀLøÑdËÝ`2Tlú14ÆCÌ`:wê¬ZaÍ­µ u¹fÙ[žµæ'î§hÀekò2mC¥ú *ØJr,2 º*¸’lpÉá¦_õÕ­lä6þœKBã4u~уô7Té'Ì ]É•Ú_V73nQtÄØ¹Pñ¦‘[þh Œšüœ õ•)°­!‡¢Ró¦õ¨™f€ÖFD‚Nÿ•z¿§jk}™†÷kGÐÉÃŽQ¯ã1ܾfz³ùEß›•÷¶ 5®q†™9 íO¯‚Ÿ³Ã :åá.¨ëÁ ßµw²f'èÝw÷ãü¥p‘ªšùm„wºFQ©ý7÷Z‚ÝÜþ ÝÑ \s€ Ë’¥“l_;Ò52*ù§ŸÙÖ¸› •UßI²s ÷7žê¬·×€~^D ®õâÎ:šDÝúæ4|–¾ª= ëCöˆò«'ÔÜL-Î%hëç +úÚpή˹§£Ì ÇóVÜDå¥à?ôÛN¯é ¨aÀ|Øà܆»õv:d™íѵ8°<¦ÇÐV‹¬ ï]¹€:X$ƒ£r(*Õï¶[èq¢ÃïhçF®NØ#R½ã·,r-Yª›Ë=~qÚ:¡UZ²ë^É•Ú_¾Bâ=šwâÙ«;œÛ1?œ§QôIÿ2%¢‘+5_§ÒôŸ&ÊТmê`q$‘¢¿ó»jL>ùTÐ ÐóïëH»ù=¸±N-‰e¦Œ›¿²–XÅ ãÆ&¼ -ò:.Þ¬/5h¨ób˜djOÐkŸüáûçŠ6´¬óŒû2ô¾ï5*oÛDP©þ%ZƒÜ!Ž [†ts—aÜüË¡<³/÷Qe<\ŽŸFÑú¡~ä†68 ȵݪ/*Ðâ¥_ÈÎËŸ*Õ/_'–Ü5ùAÑeÁ×…Ü´·\óà¤÷Ü?¹J…ö‚Çø›"ª{i5UOµÔËxœê=œ+µÿ`ÄÒÏÌ›¡¥;ÓˆuÐHn¸rwÁgÆŠF]dÖEßTjþeêFÜ RTc|Š˜;;]Dÿ‡SU|Ê'·†Ö3 UÌéy€ôÅ€*æÚŽ] î©%Eó)Iü]%†*æ”ÆÂÓ@s©þOÃú€õÚGª˜×Lôƒ& UÌ_–ž¡µºê UÌ—>›oiS´¹ýŒf1èæŠ*æáË”„êÙóT17öM†Bõ®"ÚÜýy aä®èÍPÅܳ*›ø©e´¹ó]ב'ÝÍ"hsßÿçÉÆ4øoÞt^½;3Ôå­9<¬°áþÑ4øyŒå†«‹â¹j®JÆhµîAѳÎÐw—Cç¨ÚÓ»#¾S”jߣ÷¦öP§NëÀøU5AW,rƒ ý=*Õßúá }PCQo]Aí›2C/=…–eòJ†±:"š9î3 ˜ÀPß/qã!\ýUgI<@ëÖ‚êm[•êçÖ=¬^VLÐÃQOÉ»Q¥Üľv,‡¢ªm ÏT¡ :ÒèF€>ñ€ÎÕ¸Rû#´÷ 6Qî M¥)ûôØ…û˶TŽWôžÃ4P² (A¥æ‡þõ˜:eô$hüÏÞâ‡4s@¥ÞoÆ©Ÿ¤7—º0ÔðË5r'm÷ðÈlÚñø;ŠV×М¡&\Ý5ŽdÉ|C†FŒE :–PÔw¸•Ÿ7ôôüjr¸`,Wý[%Í›[Dт˄›E€JþQÕ%O?k1tºx˜E'.T…DÃ?*µß§Í\Ñb‹C—³O⧃fÜ ¹P@.Sî´Î$ûtWj¾Z·ÖPðÓ ÐCfƒ<'Ž ¿ñT{lK É•ƒºqI+RÐàÆÕŸqƒµ"èˆÐÕtÄÅ'ÜêeôIô{-ô3¢¹3:Ú8b5|ªÅ­ÈJ…ôE4jÓŸ0ì!!¨Ù››dT?%†JõOx@‚‰C]¿\¼|ª)Úeöf²bËD†ènHƒ&9q‹¿Í¡»Ÿë:Óli©1Œyæ¹²D$hq×îpýוêgYã oö}ÐcÑ~`[¦.¢¶ëNŠS5uÚòi ½Z]DÑ?äôeÖ3.õÙD}ûDP©ý_shD@GÆ5£¾ÅšÜ¨×dÄL@ã7l!ÃÖ§Tj¾Ó @¸¾oEóæ¿%»ûþÍý§Zºw¿Kmc†ê~ú éXpM¯»Èû´7ôO³dÿÓ>Ü¥E¤Îe7ñët€ÕݸÇNCM{†ÞÊšHÇuÕ”ï¬Þ. äV;¨6 Rý]ßtµuɧh¡¹—¸÷ØîÍÎuôF[9÷ù½,öÄA]ªÝÖ[êï ÞR šçuG¨ðXCÑÒEñBíqFPÉïj£aγáþÜ2ÛŸäªäÞ¢)ˆhöÆôóâ©]М±té»Å t-•ÚÿÝp=Ýðî/ŠÖ^ÛC—/¹É}™Ñ ÂÊ{ôEeOµšGQ©ù¶7w ¡­³(ªÔÒTÞ´ €þÆS-´›ImŽ´gèšÒl÷°¢µ¼]yi0ÌQ®'èn•p%ì—€~ ]U½ÞRTõºfýGnŸM÷é¢×U½v›ôEІ23êï' ’TÏwˆ³l®Š¨“g¼^°W†Ö~=HçvT´:«ìëQwnmðJ°ØÞ†¢wµÈ?ßùßsÝWû‚•Å1‚¼Ÿâµ*ùYQèÉï¾R´uã22E—¡‹»$RÛ @ïõ!âwg®’ßgÒr×7Š–š%Ê׿íË•Ú?åç!š~ˆ¢µkåô˜ý:®êu¤a@=7¶¤ ]\êÏ•šp0œpÝAPy„ü˜Ìý§jåq^kwŒ¢oÏÕSÏ=“¹Y3ƒarQ0A÷ Û˜Z9:O0†µv~ ­‘M‰õâÆŽ*v¹o¨ÊU8q±'wÚ ˜sÕ”«:&6&RTªæ;òìU‚†~ê%¼»3…+‡—d®§7 Û7øAÒcî“bò:¦ C»˜ O¬/QTÕ?Ûu`覩ùôN]K@¥ú‘w›Iôc†Æ\<)´¶3àšN‹!½×úÚ{ˆ$\ÅÍHJ^¾ÚJЇ³»ÃÞITjÞg©­›E­C/QŸÊܶýè¶)‡š•¿—/·Tj~u+øXtKD{d>$Å=ÎPô8UÅçQÿotŪLUÌOhŽ€(ŸeUÌs#®ÓNž¥¨b~q×TèŸHPÅ<ʯ®DSs0Cs©þ»†¶$7n%T1ß».RŸÄT1Š=.O*QÅüùÚa0꜠Íí7«”ŠÖÇ•ª˜O± †“­­UÌW}$¡hs÷7¸L/ '¢ŠyÝ–pغëo‚6w~GC=!exE›ûþ<Ì·Ãsk‰y³Ðœ¡Ö}Ê…$CnAÒ,H¶i ¨kuœv_OЀGް2î¹€z2ÝwŠ:^œG?m%h¢}gòdH@]o<,éaëÂPÙ•Ó4¶ª‚ Rý/MÛLu÷ÖÔÄÝ’¼44óÔ ú}mA'Ú“Qç,í¦QOÊ«S(:­“¹&OçF¹Nýþ'-ºK´ïZ2Tªß ‰`ª×(¢O †@õæ8Š7*“¢Q/ zco¹²U кë¨ï‚ŠÞ+*ûÚP©ýé‹äõ=fR4è–‘xæog®Ñ’ÕÄáÓV®R7¥ôç%¨ÔüoÖdÄ?ÿ,A?þ E½›*õ~3NUÉçªSå7 †^‰È/Ù½¤èÔÐ>V&¢)¢ øÞAŠ–ŸªÖùQ´®CkrqÃ??ÿ¥ž½2lm è¥þÛ pH‚ŽluG2Tª_fÇÃÄÑóEœ}É™g"×ÝgLõKPÓWÑàå=ND÷tøFvÊÐ"Ë$º;éE¥ö7ø‰ò{Æ"jzËYô¹¬!Cÿx…29ÚÒÙ NT´a¨Ôü+ýIûSõµíÓA´óSáþÎïªýé¹#ŠšO_Î!"ºWo. 73ch©CqÉÖ*Üo;C͇“uŸÇâTu¤]?Î’¡Žm^шô«"zëTO0 Q•£úW¿ ³#)*Õ_Ý@jÞ΀ê:„@7g®‡¸sŽ¢GJÓHeænÚÛ@ÐH`²š4óäè0Y:½­CÑ¿÷]f(i*ÕÏvÊeaJÛuõЦoëÂíø+¶LÉå~4jâÛ1Ôo õöª&è¬kz°ïÓ_\ÉSmÕJ4½ü‡€Åmó:Çí]X"lßbËБöÔ/z2E¥æ«ú !JÓj»ì){·ûO5úðP¿+D@ë÷ Çó;tùqSzqâvŠzÉ<©î!wUþ2þˆ8HÑ¡¦ï‰s¬)CÕ¾DQõïõÝ»ƒôí ¨éšîDõä‚îÝ·îlÁ•ê¯bµÒì>´²W",Ÿ¸[;å¼z~5EÓËe{ü-GO¬ ƒIs^Sô…–&„”†6ýåõõ^yƒ¬_—GQ©~AÁ ²tõb9j»b¨pdçm}x£ ¤µsfhXïÕ$3w0·÷TWø~ç…ˆ¦è$.û(*µ?ªG¬Ë‰ÚMïòQ–pÉž»×h7]êÿ•kpó¡½ý•š?.Ê‚ÛuPÏxMØv"‰¢¿ñT³cjÄ:Ý­ÞÝ[¼²r7÷S§t^•7½î…,8½ëã5Q^yÚ€¡Çû¢ ½&QôÚÙ‘pegAŸ,€ô3r´G›»äé2Cç)¿§Á—÷ÉQɯjÿdØQsC@׌H†ûô¬ˆ=wOØÔQ èþ…;ÉÏÛ¹­…Ýé2†vr>N*îR´Ôf†ððó~u¢ »svTªß÷ŸÂ”?‚®l=›la¸ýn8Š? ìúJ%™¾JúAÑ okÇkOtx¹2T›dTòŸUa ¹›A]#D!wÛn¡S©©ÚF)gùQTjþÍèÞdQo‘¢ã6·¤åssEô8UÅçž}ka놂*æáú³I½ÛUÌŸ<)¦"ì#¨bþs® è¸ß¦¨b¾lÐi’[ï ¨b.ÕÿD‡D0ÒÝJQÅ|Þf5ˆ³† Š¹Åø¿©zõUÌš;BTJE›ÛïÍð$🟠¨b¾åÃS:ðîxŠ*æþyC ÁÄœ¢ÍÝŸ–Ù‚ì;KPÅܸ¸+Ìw`hsçËžn·Æ”´¹ïÿÇSÙMÕõ¿Ùim¼Yw“ ·MOBû1Ün±+XJÁ*@Ÿ?øD'=ƽ£mB†\*dèÓ%lmÆd®ÚE_©÷Ž¢#êßþóÃ'Œ¡Þ3 諟ÉÜ?ïoaMt•êßñ`? ýlMŸ¡.ÛÖŸ[X¡B<=®P´ÿ}znwÙ xrh3CK»§÷LæÚ8³²ƒ#¸Ñ™6ìÉÀ€JõKß"Ò:šhîOÙLûSÜÒ²Ñ3[´áM¨¸æqWuà[¡È*У>G!רŸ+µ?ØÁl¡è}èyzWÛÕ¾Z1®Ï 0ìáÉP©ù¿Ìm`¨c)CU]Êi¯QÛ¸Rï7ãT'ÅQ(ïÀôë¨2HëS(¢«TÈÜ>Uý5bö˹ßãÙ[+Švîò‚Þt>CÐîwУ2ÔRÁÖ/ÚEQ·%3!@í: ¥à«Z †Jõ—Ý5$i›ûêqrq:×›[9$Ž8{©Qts}!é\0‰k»º;seCõ.Ïf ]Ã(:KÙÞ:ƪ#ÛkRóDTª_ÈȇäȘ“€vã^“r¸‰/=Äúòw5UKÖèè$úŽìtfh›’Y$^¸Ï•Ú¿w±-¨®N¡è„qÖpôÎN®þ—~ôSU(Cï¼Ì¼vPTj¾ÚDOvÞa E5w6RRûŒ ¿ñTýû–A‹àuõø%ªi¯¹? Sá¸ÉV@ô: ‹²ì¹EeoäµKu¹†·6‘E•¹Ã3›Aƒ¸†ÊwÅиñ³•ÅÚ²t†:k¢jÞ2‚JõwlsƒTuTcê2÷Y+nÈÑDòÁâ E›bÜ„‹æÞ[ÈÞŒj"蹺ëÔub  úû4 èÕ#Š–ëW‘Ñu;¹Rý¾­‡×Ú%(Š}¸‘V»I~ÈîÇ®f0öA8wëítö²¨CÏ4¹°ÈýI€Jž’ÜtÿJ£è¸]fðWë=ÜØžãX”ª Ž=Èý¾ç¹RóÚß¶mè ¨îÆ‡d¹Úu‚þÆSípà8D'ô`è‚qûA¹Ä‡èx:|L¸P+Ub(ÚÎå<9¡TFЄø¤iË)îÚ›!¼W) ›‚¿Ã¢­Ü͇·“¦#» Ú£ôoá $s¥ú/êÙÂkÞô|´.¸×žåöóM¢Ë»_¥¨É“ö,´6UD•º¬#q]öÚÔeã×pËrj…ù¶& 5¿îÂjW(ËP©~ƒ+NÂYke@o”@¢‰AW,Y _[Nô²Þa0~Õ…»P˜kBöpkÎ×ã#Tj·Ýàe@E™ÂX­Lîkv ª?î´Fö\=W‹¨Ôü WI·Êq]šE6µÚÍý§jà¶ nE0ôÂ…(b¸[G¥Àê>úÜ;­ÇƒE>p—zµ„Û#k ÚôÞ,^×p¯îMnÎ ]õf*TÚ‹Üepì½# uS‹`H+s†Jõ¯h§}_ï#h»ƒàÀ»îàWf¬Í¸nZpÝpm  J5Ùp,@»C;"V Ñ”;ñ²öCªÛª¶Øq¥ú­> ºÅ"ªâ¿*þÈ¡èã•7 R=Œ >îÕ<5¢CÀÚ‘V Ý’E<ާ¨Ôþðk&0mhEcúƒ®,Ÿ{uE˜tÚÍÐ coaϸ£\©ù­dZp©m @ ã£!mÑM‚þÆS[;Î\HehY¦ øjdsß>Q…÷EÀ1*Œˆ5åv»r] zå{*È·î¢è…Á¡´àVC£z±½ÂgŠné{„öÞÏÐŒA1,0º+ Rý cÛÂÊ[]ñ¨T&…s½kÛ’ ;ó¾r½FÆ¥ø¢ïŽ[ÁJ«® í£•u¹“§9³°Î›¹RýN/ÜC”úíÕ 8ñ¨3W6ñ ”Úç^ÛœzéÜH‚V E× iÂì™]*µÿÑéÎà§VHÑÁ³ !èôqîpc¦é3ôö ]”{” Ró5{äC¡Z?Šftˆïióú?œªâ38²‘X™0T1_o©M¦-ª£¨b1x ÏÏPÅ<ûkgÖné‚*æ»|½!²4 PÅ\ªÿä;Zðäñ‚*æo<Ï‚ÒU€þ_Ö‘n‹Ú0®ÂXëÌŽÙÙÚÜ~!ó@áÜž UÌ÷¹Á…_窘îoÅÊíoˆhs÷/5èïWÊ)ª˜—Ÿ9(øô´¹ó·÷&fWrÚÜ÷ÿãÉÒÒõ¿yyâ18W&¢/+„ƒãæS´ÑrËŸr–»×5›-N]APÏ7ˆÝ †:ÜF"Ë(ºpà<-&0Ô·x}î¨Ëõ?|"³¹á)ØHå¶\©þ¹`ȶ÷Y@Ñ=Ú½XX·9":+l=Ü‘m¦¨ÎÃx¢¤Ï¸û`Îß›¸>ݼ‰®ñ6®SÆ ;¸¡^½XvxWªß'SÂbmg‚Î÷Ú/x™öã.QSO™ ®]Œ`^ö‚Vï’‘¿b²µMO…IÊ]¹RûécHk8(¢Å]CeÜXŠöêº ®Èæzš¹CïOî •šûã |è|‹ n;¯CÄ<†J½ÿ‹?m¥~©¡}LÒ£ýz8žÿäžÜÀÊ3':#ʆÉf⎷_Dj6úSôìú,reØnúõ¥Ôç²LD/ì"ØnlCÐ#–±´´» /wƒgÓ;q¥ú8Ó›ýœªMЖΖìÞìƒÜ¢ð«Ä¡UOÍÅÜ×ýôÓÑ@îõúìËÌŠ:ŸK`î¶€¾ëm or¸RýFïVzÔ´ F»`)·cP'(\¦Ú^V«pÏÇ'@šv"ECóÛ€™Þ{®ÔþçjàAv>E³æ<€s×ê¹£ìIMä4†³>Gý* ¸RóUgÏ‚Y­ z=Û‹þÈ(âþÆS5sÜ×õj®¼X¾×vR¶8uu% âl]hÕów·ùÄbG5EC´¡·ßqu]ÌÈj{ª *Q |S‚®ˆ˜ñ}à5²®] ¨Tÿ†ì°}+@µ•ØÂ2nè¹4Ñ$yCýŸÓ©ÌšÛ”£Åœô• j¤DOþzÍ/˃RÇ9rôÎ*`ýý'E¥ú)YÛËJúçÔgÔ%Ù— ÇÜ=E÷H°ÿߪ5¶˜™æNÐ=íÎÑþW(êñH›üŒSg¨ÔþÒ w@wŒ.C/W_‡Ç^öÜó&,ñB¥ þà =Qe¨ÔüµŸbXd'k†Z¬c…w_ô7žj‡“KÁyÎp†Òî R÷íò(ïR hÝ [00"Š;¼§3]Ô‘¢sªº2ó• sœ©x"‰ëZ#»Ùb ŸµÉÑga -ø1„¹þ*(A%¿ÔüŽÓè“¡€¾Z×›†måf¥Ù2—>‰Õ-pa¹“Ôµñ¡ìÝ9}@3Í7¥Ã&¿JŒ>˜P4…%ªÛ3Tª_Ç繂V§v€zuL´ŽÙrÏŒó»Zø“Lî{›Û÷J-]{nEOìþF·~´Tò§fÓy0l=’¡ŒÂ%ï0n•ýdb³{9pLƒ“c¹Ró]³Òv»ýþ¬; øç'2úO5ó™) Ãzh§1<»€»yúhL½GÐø?ßCfwgЦÝDu§lÔí)ý·ú€¿ájnЉbÈ®ê­T{+¼ýÂßT ðý)A¥úß*t2î'šÙ=‘9¤r-Ç´eM#zpe.#éµ%\­ýIcÏo5y®:­Ÿrcv™°úQeõÝð„>-Ý¨ä—’Õ bØÑІý:°µb,W~Ð_Ô æÂ™—¹3Ú@öÞÝ€zVÞ€Š”j‚Jí¿àq d/W1”dæÂì¼ \ùÂ\Øáº… {¬öAô„JŠJÍWñÚž»--î› Maé"úOõ¯’/äMˆ^øç_ÂCÿ­ŽeT÷㪯J…’»ñ\_õ3༠Ðë~ÀË›ÔõD ï—Oщów€¶[C×jE€zÿnNÈ|z郌+Õ¿E…:üTN´`ëp³[Õ:ŽÛÈ]÷)ÚÜ÷ûò§Åíhµÿ÷—ü9®¯üÜnEPBe¸²Ô†{-¾ ¸{;2´þY9QhÁ=vôøœÁýtçÿ°rïQ5nmÿÀ“¤%9¥¤HB(’µæ•„$Lj’„$$9D’¤è\’JJI*Q‘J­5gÎi‡œr*§MBìC´ùíßó¾×|öXïxÆ=ŒÇúçóÇ÷¾¯ë;jv×ZÆÁ<=»r•/%‘G¢¸wßµRãç3¹·?/©.E·m§Už_ŨPÿdŸ¿È”sÑ þC§Äq}ggÒîKà ڴ}­Ô}È}¾¿šÆmøÁ ôë(Ù4Ðî é÷ð$nË„Ñ}ü¸BýB{MêrŠNÙ$Ú[…¡›îÍ‚’¡ÜN{_‘’QÉÜΛØég™ïÔH§M ThÿãBðÞ8DŠz|='~å6]5Ý "ÔåÅbx»6Ž¢Bó¹÷†þv MÏEÒŽLæ Ýÿ§ªÐ¥ê7ÛÈ™!ã j-úHtçpëÈr²>[™¡Þú«ñ9–i*ß"è›9Ý™ya  wç0…&)Ú6F—¹èj×á‘îŒâªl‚I¥Vêo~ÓQü¼û†¾2IÍ¥áÜ Iáã.€ÿÙ‡„ à^Güwöà:Ùß'Ež/Ú~÷TþPbhH’3yú| W¨ŸË­þ ÃPý¸§¤s½×CŽûíò;’uáAs®+Ãä35Ü×j:TK׌¢-ºâŒ•›$¨àSGm™n²Ž¡tI_%oàÚûŒbÚCgqçy±^¾÷(Zû¥'¯ ’¢C-Àò‹šÚï¿ó ø/* ¨Ã—°X\Ë-Ìöe³úÇp+õœ™± Bó]ïwf-½êþ:–þöý…Gõšsgðé´ –ùÐûÎ!®±† ÷ÞEQË¢¡¤æM·í…õ£NT±k.V£hx×=ðI¿ ³ggÀ¬8W1ªâÿC*ʪÔ$­V}}DP¡þSåK~ÓŸ¡®ÎLaõï=ûÖ úl7®EÜÔn%hÆhSxÕï&׺z=¬Û—Â5èâHíž,çÖŽ'éFA€ õ‹~@i · C% Øš½&Ü·v0ïÞ_EhÿÆ5ìÓñoÕ:º¤mŠ ½ñ=B<¸gWh¿ènœ=ý–¢ò满¯^'†:wMÜÌ4ûr9,dû¸Bó­ ã‰â»€¦wÜ  K¹¿ð¨v7tL>A‡lj+Žs-Gíg^*¥èFvBúG×2îé¼yàå>‚¡>+¤$1owæÃ(Xýä2EÃ2¦AÉê‘ ý>sd÷ä¶-É!K“ô(*ÔnæV²®'EG>÷fº·[ÄèÌ7Ç `KW‚ŽÕ:ó&dRtCÄ^¨œqx<º\9j=¥¨‡è¬‰zKPÛsé©—ÏP¡~û~sa™;ïP4åu óÝž%E[5G°êën€¶¥Ó‹½öqo¼XÍzí¦h“{vÁm7 BûG‡×Úî ¥—áÔ8mnfä¨ÙUŠj™…ïÆ{*4_;þ˜ßK%è û£àà{ƒ¢¿ð¨NÍ×95íâß’“¹>õ1´³C$Emν _f¦JÑóyCˆéFQ×.Kȳ[¹Y½®êí ýrÃUºöO[n´åUòû$%®÷³Alóõ8Š õw^·œ8s– ¡ÓØ®®íc’ö)v }V;î/ÝÇÕiZ F/å¶ÔŸ&£Xîóáöìnm:wnQÓ¶tTðmÍò],¬-… êÙ>¬¹×h@{¬P&‰-G¸S²{Áª~‡¸‹Ù:‹+ä¶›7‚›Çc1*´ó´õ°ÕÏ€¡3ò–§øsw9ç§q›z°æ¶ñ\¡ùïÄóáïp†jt˜I|Oyqÿ‹£*û’yÙ© ‚ÊæßrTYlèb‚Êæcwg'#oŠÊæŸ?Ò=&m•ÍOtÑ£Í~¹€ÊæBý3‰>Ë»ã¨l^nDòže¨lî5;˜½f¨lnb¹ ù€þl¿ +–¨l®³Ïÿï·˜€ÊæÛmÂAÕ(š¡?»ä½9Ðâ<š¡²y—ý¿±Ú…bôgçžÞFÁ½/Cöþ|Xõ-²ó¿.ùO^°óeϯ&¨ÈÏ‹u¦ h­v(ÛØâÆc4uߟϠêL.#„ÛmC0yf™Î]U´åñh‚>ý¬@-˜Ç¥ûL‰º¡' Ulàbl=A…ú›®=º3ôzÉu»æÈÕï´ ,·.¢“’þþ–åÎ¥è\µsR÷IåU œ)žj>ÐUÚ*à½b 5>àM;ST¨ŸïäÓ5ï­51σµGEËô‚¹—4 šüØù\£•=f´±Cªu-œÜ7l ¨Ðþüz çzõôÈÔ³o¡Àuº— Ïæ™24v ¼\ïÚ¯¢0 }KzÀp'íó¬˜+tÿ?žªB—^™»ÝìJÕ¶¦~-wiÛAé…¬+\¿¡¡÷ÝZîö¨Õ3m÷ù¤½pÖùAz&…)óÅè—뉒ÿÃ\k­Í ­Í-^±ÒóG1T¨ô’°¡.œ¡CbÀ¶o×£pôë¶™¢.:: ײ„;$í¹ýÌКQÑÐhæÆíÓû3!‹V3TÿÄD¦TÛ‰+Ô/%k/È…ô`he7´4ãÎR¼NΩ”޹@Î:xq;›¥‘ÚœŠn¬L¯7nã íß·þ£¨l^o;н·ìÉП_c-¦Œmú³÷ÿãÃ*ãMJÿºä?¹Ã®7›ÓhXûyj25»ªÇyZ¹jm­º¶Š5·‹ z>L¬¿TGѺî*lÖ½ \¡~iÉî$꺠Ÿémb7b>w̱¤Ùø(÷FhOhÍJåêUÍfåÁ6­½|Dz>5P¡ý½CHð%@µ²cˆþÊ®ÜW‡-HN¯³Ü.GR¡Á ‚+4¿L3Š&ôU¿¨ÒœAP¡ûÿñTº4 yÍiÈÔpãPñ¢\î÷Í3™«Z‚íðÒŽ=ÄmsôéÁ¥@"fro;¯…Æž[¹­ì/#q‹ÿÞùÚ;ýc$+{RTh·MI¤6QÐy_‘â½¹cº^‚õ“´ ª¨˜ §²U*4_‡–Ã…5†îxä Ü_xT½ÊHì·#€ö{÷””÷ÊäšïÓa:s¬¸yj‰TÑ(ŒÛs¦?óÿÿCòÿõlÛú1ø7*±?3ó^Šz]¸G§©&¨‚h-D-ÍÔÄ)¾hÛST¨ÿÄÄ Ñ|˜ ±6;ÀtÕnŒi¬ûö‰k®xž*ÆrKö›Á-#€V%®nÜãÅï R~1C=R¯J—ØÜä õs—Ä€çð€êùÁÚm]–#¥róh È-O¡uó4Àb¯ C­7›§ŽRThÿ¸EE¤Û®ßm©¬$Y ¹OS‡Á§Ö 5wM&Ë‚D\¡ù£†äJ5r :¶j“K!Ü_xT3Ïè€i` u'CøµHn¯s?ÄW{àf»«ƒ©Z2Wqå[’%ù«+ ÛR¸eƒ†QrM•ëÒ.>Ö},·ØTbßRôÓðrbµ²3C…úŸê Õû;ÔîTdØ#AwºAËÃ`)zøaÈoèÌPךKpóf0A㟔‚Öæ“uR‰erå6ÔÒ­]~~' Bý𔝱'A_500”¢&>#EÑ?&ƒ×®]Üв2ß;œÛ¤Ù]Ç:0Th¹A\ ÿü9©\8Œ{=MòB}C§õ«§™îÍšÿÅj[r?Ž Åêwi£A8 ¿ð¨ž=·‚o´ªç0)˜Ïuù±|‚‚¹;º¥AN¿ñÜ»%ïàƒû1‚¶Š@ñ»þ }à}¬0Y h¯‰^`šãÈ}1³–²ÕÏ(Z¼ü-[<P¡þ“ä’¡ÑbEo¶&ÝKÆmﺚÎí`è8ÛÁXÀUí¤?á†,]]¸;⦂ÛdU@%[í!Í`?öôæÑ"8×}$×.P¥/ÛÇíq(¢Š×q…ú‹z%Ó[ÏÔÐ5]sá÷Õ ÆÅ¿ù_»Õg€ß,†öOZË®ýþ‚ ñ·:²Ñb­9$Ï–ß¿JѵoRíÐL‚ õS¼”DD×=VÔ]L½Ös=z/æÖj1ÚKÇO-¡þ/.‹cN:°´.®òå íz¬ÀÎf¬´eÉKúÉ6ˆ;Qt„…ͦ¨óIVÕâ ¨ÐüïGÿ~ß±g KOï¥îß ú jHË`:à`C:WÐô ÷En=¼lKÑ ÞìÉÐÒÓ¦ìQä8®^WwvË_GŒx=Å0SŠúv)†Î>ûúpØhØ«Êݵò2¹usW¨¿æ&ú©îAµråiשŸ¹MéŽé9¡ÚÅ÷È'3¸µ)®¤õëI@Ï/ö…½y¸æ)^b’CPÝ÷¶$Rn BýúWùÓ¨GËú=K-H™Àí µ“”)Zx(Ü ·ùVœíkÌPÅÆfbñŽr…ö[¥œ§ÃÍ£MNH¡¦¾ ÜãǬˆÿãÃÜ€÷>P`>‡+ø¨‰C“[b”…€¢á#)ú+ŸªÕjÌÈ/‘¡mù<ázä'Àd—¹Ü^J`­êÅ]½¾{Úd ¨å¹{¢&³­Üé¡§IMÉ-†~­±G×r¿ß¯P5‚{l¹Ûß}9… õl"•.hPtnç-RÍ&unFóñÞq: tIµ(œ¦èÝ3§ak÷a€øœõ©"twÚIµØ PÑïð=sCŸjÚÀf}Vd¨¬`¯{P´¾g48J†2t«ùpâǽÄJõW^[U[DP¡ýŸÔQÅæT@Õ›>Ißúæ^ Øþ ¶ß§ƒ¦ý)*4Ry¯½@Që±+á©á†þ£ª¥¾Š±sgèbïPötÞXîäs]Àbü*®|U±:4X ¦Ö8šæ2ŒéÀõî’Æ^¥øRTU´ŠÙ|žhå3QYe ×c‰¼åÀ@-®Pÿ¯N×$×Wêz÷Q‰(H<š;Ùì 8¬|$E‡œ™Fë)s÷åçÙmÜ1Î0qý††Ž.¤ÆS«¹þÛ²Üó³*ÔÏ|íf–UÿAŒFDz²üá Ú·@F§2ô·þ"Ò÷÷cÜõ½ÔèSÓN€Z?¯¿gÍÚ_Ù¸OÒÔvPÃ¥ân.EÜö£ÁDe³S¾òFRTh~ÍÉ#D47•¡ï—¨²í;¢¸¿ð¨6ôKd-/:0´im‹Ë‹¡¨ýLKÒ­h,Ì]4zÜÊ $¨ÕvK8P7žkøú´y]2 õµý`ŽK÷óúhH*é1‚™¬´á õ·³.ó´TËODˆ ×¹6è/ZMQßóZÄ;¶œ;Q¬žŒfèÑÓÇÉù:mn˜íqݰÀöÞ ŽMº\¡~ÇMfÃ]g*ÊïÌ^GsuShÐö†¾Þ7‰µ¯‹à¾rœóÒõ8ýº¥­#¨Ð~¯…3I§ÈS€’ÒCÄÁ³Œ °r¸5C¿œØ$~~=˜+4¿Ms«|KÑ¿…°›óý/ŽªìëæûÃÌ_ª!EeóÚÐ"÷Ñý*›ûÚ9Ce&•Í_NÚ•sõ•Íçì9Fk^\#¨l.Ô_»C)qY¨l^µûw飂«•Í[^Q®=*›+F…À€Obôgûu.˜OKM*›'ËÅ1ÃJ#†Êæ*þÅø%“¡?»ßiér¾å4 ²ù•fì³b†þì|cÏNtŒþ%@öþ|Xu±³Ê¿.ùOjv-…º›º =bš }^ÚrkKºAWÉÜÊnaä“üZnÞ·^´ÂC̵Ý>­ó~CÑ÷ýÁY,èçÄB0ì7Š ÷ÜchEa( Áî…¶¸Býýè@´ÛV†ÆÚ¾">ŒàöY¾*‡ÄqéñÃ$³ü$×ûIO±Fç‘€^ÖzGž¶oà¦Ñ\6ôiGî*³2r<ü>W¨Ÿ½ò5Ð[NÑ®x»§CUÚlYŒ²< ¹Q]X÷Ä%ÜËrèñwÖ Uz˜MËF†HQ¡ý]òYFÜ)ºKï Ùs\ŒÆ‡Œbö Ñ ýã'Kò'E…æGÏ‹…‘‹l­¹䎕ŠQ¡ûÿñTºTéé>X¾-Cä⺮ßòFé°û¹ÏL;°!/§sµÎº±r‘%A5öè3m¿ €ZxVÂÌ?º0´ýb dÌá^=¿ŽûLä>¶:*Y]CQ¡þ‡·ì"/¬âZx[Eütz·îR]3%;挻÷Ü{Ò- Z]õ¹­3ÏÔîÁÿW\ùð0¨óÁP¡~¿)dÀƒ²Ù =~Ýbcý¸àmB,Œô©Ê.¢“žÌ­ò›M}=%FÕÔ ©cª< Bûò™ÂJO‚>ì—ÇŒ#*¹¡Ö×èíÉ¿*GÔI·í¸Bóu¶Î…ºçÛ)º'¾ˆÆXpáQý¶Ú fôŠcè‰4#лŸk5Ïžè÷‘¢vs·³Y5eÔáÍ9ñÈ×Å€ŽzèËåsåÌ6Hôf^bè“ þLrÓë?é©«Eѳّ´Âê6W¨ÿ­ÁŽÔèdC-Do虘Hî¡æƒìX« EOôÚÇþŠéèù´[p¿¡œ¢ݳ`µ~(C _‘„¡FÜÚº…âêïö\¡~~3dí†z&Ý"osÏ>7†öË1€Æ¤yëÊ]:,–ØZäº'ß•U+¸Bûþúû»/ €NšÁú<0ä–OM"EïêSDª4üQ¨ †±%RîÃÙð{e&EáQ5éþd¨`èŃ{ÈÆ„d.±÷eŸ¯µôDõxÖ«ÌP5ÿjˆúÜŸû±¢ŠÛSÔ®áÛ[¦DPGûqÌ09 ÐÂõ4dÊ îÊ#ÙÐ|ÊŸ+Ôßm¼)KSØÀК;«˜s pw&Œgvsöš/"m¼ 庖¨Ãô ­nKÍÍà¾ô]Ëò/ÆQô¬‹*›oP¨P¿¬ 32ÆË›¡r=œ*bÿ𯷠|ºÚË0úl³'èÞêÏð¦0ôõëû$æÞS®ÐþN…IÌ;a Év3õ›žÜ¨—"tq<7«ú(À ‚ Í?þp |ŠœÄPWÍCãÇTúqáQmM«üÏPí áR×èHî°Y4§Ó@ŸäL&ƒ/çžéêNÓZ2äOâ­åÆÝ“±‹È/<¨ôÏ0vÌj®Âò£Ð¹tCåŠÇV¸5Lá õÿ8(’™]m§¨¢ÿ~–´{*7ÂQ*ÊOêm´, ãÊ/Ͷ$Žc¨Þ=[Ö:-YŠ>ûZ>ê—ðŒ ô*ÔÏõÃ;©Ù ]†ž04¦!é¯)zÚg/DUK¥h¹µ/$®ÙGÑžd»vÕ‰¡†úsØyU@…öË+îbt@ WUÖ³Wâ¹~«/@)ùJÑ«óœ¡6îC…æWuÉ.ÞÒ`èãW–o²Ÿ ¿ð¨¾u¡:!ÛªæTHa± 7­û88¬pÐôá‰>(Žk¹ÎA¬—Ñ‹¡…ÝÂDr~î½±<v†l'¨ó8û¤EÍ?Ž–&>ÍÝyú ¬½"E…úŸïÎRÉÔõ~23rkænO-„15=Z~înk£î(±>wZÕIƒÆ~ÕTáÂ6ò¢&…¡{V¤s¶¤PT¨ß:˜F}È èÔëß¤Å·í¹®…`Î’sÜÑs¼Œ›GřϤú‰(_7à í/X;ŸÍµÍôêV ¶Á÷8wâÒ(º¹cC_Vz³g)*4?X¾Êï TnNØfÏý/ŽªìËëãÔê/ †Êæ£ò%ð¦+*›ëøÏ$ŸÏIQÙü¾‚1üÜ‹¢²¹‰SÕ½ ¨l.ÔÿAu»0q* ²y‰yxuAÑÿ³Ÿ±lê¨lkQ+MRŸ*E¶_õëp©ÒMŠÊæúUÍD+'‚¢²ù»¶pnœ1 ?»÷¦~ÌRtPÙül‡ÁìØ%-@v~ñá½ åÓП½ÿVu.øŸKþ“N¾ÀĹ»%áî wq­Zê A®Æ€†tNÃmjÜÖ)¾°þØnÈ—0kñ‚žXÚª6‘¢‡T­à/ŠVÒ  ´E…¡eûib[$W¨be.|”ÛÁÐlpøË>†û¤p ¨›¤Z¯Zu í¸³bû‹‡8KZ´«+«ªÞÆ5/*­M»HÑÐO{ˆëaU®P?K§I¢nKuîIìÎs“¯fËœ,†þÞ8 Jî\âÆ?9N®ŒJçN°Wcû? ¨Ðþ½–²oS<^´€õQÌãžüs³³¯áú–FJ‡çõTh~ÚŒR8ßsEM–ÃŒ%*tÿ?žªB—¾t‚Ç÷£$詳ÁòhW£¥êM÷tkBØ?‘¢­¦sÁzôëYgx¸Ù;d•"0Ÿ9 }8Šö|»‰ûgÂ:æô   «m`JÛ^®P‡ü9`¶—¡ö{äm{¸ò£n@±ãh‚îèÏÀröCŠÎq±`›c)·ÁSåÏ»*F¥æzÓ‡}²¼/û þ Bý¶t€úËÑ€Þµ› _VGqOŒXMlŸ×2´I'‹jž‘r—eö¦6½¥5²ÝGr»p…öí`ÇœÛë ºà‡ˆ²Rt–F7R£ÑBÐO«UÉWû¾€ Í×íÔJ¬3TúºæQo9KÑ_xTõ=§·Ù. º§Áfi­@-zÖÁ.Z +(‡¦Q\¿-‘Û8EŒî>{Bž¡¨þÑEÌÀÿ÷xÁ–³X¸rPî3’r¿¼%>k¸Bý»=½Hfî g¨ñw¦°•û<,¢˜÷ú+5“xsŸ%ÐäÚd‚ºS GrÇ–Ï€kÚkÕÈÞ“¢BýÆyÅ‚çv@û]? SOæv•³fÚÝâzBu;òb(÷±ó3I»#æF]¯š9 P¡ýò e!šÚ€z6÷a?ÎŒàz¿o'w¯á&×dAÂÓY\¡ùe.I„»Cý¿[“‹wfqáQýnd µÅ ãÑ0*ùˆ¸«wYeh¦áG²zã=n¬O¨ªnã^Í¿LÖ®+æí £VNÙ€V$ Y“Ë%× »7¥è‰1TÁd C…ú{WßÍsahà¸zQ÷Ë–\gŸ^dãfk®Ü KiiJEËç(SËš‚j­5¥ºjÕQ†;ìd¨úöFiâŽ,Š >Õ¢¯BSÿÏÓ‹ô¸=mc˜…¦EÕº°º†t‚Vh&ÿµé€íÿ_½§¨Ðþa®Ÿþ}Õµ®W™Å-™õôw—HQÉ"¸¢–ÊP¡ùï]?P%§É }2l'+ž^ EáQ5/›•ÚD¨ÝË¥ßw<úu“:-S¨cèγf,ßã$7ö•sÝšÅ<$ž)ªq-ןƒ-†€ÞXJÝ)šQº€¹–w´Qo ÄEq…úgñ“|I3`hÙŒOùjÜŒwIåHŠž/Ð%®RTwî éãÆå€æ·\#)AÉ\Ïȇ¥ùâÝ)Qå õKÎk€¦]¿â 3Öa¨›ëzx( 3‡^¤úÅ®ÜÉZ/HÚƒl†>£½Ùõcê\¡ýÐi+­Ò¡î\¹&}òôÔU†îlpe¶m+¸Bóë³—±õùíDÄ{×ÖráQ•±*Ý” · fÜ+E36Ʋ&w/†^ú–ËtWR4bOs.xLÐxÓ^ÌjM2 •yn`^­Äл×:€’¤‰¢^ a ¤»€»@ó*ù6´;W¨¿}%q󢵇©dTm3·qü~òé–wÉ»82pQ ÷Ð×H°˜ è a¡¯˜›þy7íý~7wÓ•2¸Îêg‘qž]ËPq¿h°%r-åÇIWçyêGÖ‘?Î-â~ªJ Z~§úöÍj²õF4WhÿÍ|]šk»ÐüÎ;¤‰Uë¸%Ÿ×3ç@7‚*5ï Úƒíšêr2l«*tzï×Òðš ÿÅQ•}5Ûï¹Oª•Í-‚³Ø§®A•Íó· ›mŠ•Í½üDä+(*›G š*û)*› õ÷qC¢ú®‘¢²¹ª$[õåEesÜŽ–­»îHQÙ|ÎóëyÆŠ¡?Û/§@ÝëCeóóç눶’PÙ¼V뚨žN&èÏî/ôù$Ò(Ú¨lþp””¸oèÏÎßw4ª0ôgïÿLJUn»üë’ÿdÅñ ¦>ÿ©­ÜÆ ÷ž£™ùÁ÷{¨}úÀbÇÚPô®ùê{ã*CŸoŠcÞÎöÜ>­Òðç êÙ7\:3v?7*5‡t¿ÚÐe§ÁmPG®Pÿs¼XlN!E“/±öôp)jüÃt\B-Û™ & ½¹ÃÅApyS=A\*„Fß¹Ü[gÕÁšV04åÞöDs1W¨ß½Z’­¤LÑs|"_>”¢ròq”z1´ÛâAl¿Å(®bq1™ tΜ:P‹ÊÚo¹åIE‡k#*×´Oâ<Í€ëQuPê*)¢Ý“ïPÓÎ š¦H‡9E&1tFŸelµ÷Š Ýÿ§ªÐ¥kJÃYΤt‚~ï²Æ*Q5Æ]½GÑý¾£êô IâÛĨüÜáÌûÊ@k‹­isá_µÜqPrÂx }祀ǹà 5-·aAr~\¡þc¼v1… k šßâφ”*:!0⻘4cî&xlæ(E—N»ãGPtî€R˜gÊPÃ÷Y¹Ò>@U•çÁ¤Y\¡~¬zBØé)z|ôp°|ß—¢g«ÜÙ‹ iÜÈ0?¶ªÁˆ ÆA«§ˆ¡«†î%]Ís…ö·$\”ø§õe¨UµäÂÇnÜâ”*ªRìèÚìGÄëÑ ®Ð|g%æu*‡ 6•¥žCosáQsÚ%+Xªâ¿˜i÷ôáÊ?yCVgz2ÔÜ^*ÞÒ̵y”+]Ðìh¬SñÓËÍn0%‰G¸§ÙÐX°‘›w ™®J7â.ívŽþó:A…úë6/dVö€æyaÞÅáÜ óS€¸dIQÛ½b+ïQ~»e χ_eè¼I¿¾»ÇU.Ý ÚÊó|Ó6£Býl¯0éCQ¯RKЦϸÚœX~¤ Ç¥]XÙ¬]\“ê±yNE3ºú‘¬+\¡ý6«s%«7wbhCÉÙà¯-®? v€Öú„€<1E…æ×*ØK—!()Bç PôÕ1-ÌðL úJÝØÂóG¸»réF=†Š¢Æ°ªÛ&\•‹‰f‚ _2IÃ%cîܲޖ–]b¤¨Ms-(y&0´²v=Qï (}P*µ¸Bý­~ÔÒ¨'™€ú?y)µé$×zÇ¢¿Ÿì"´úG(äH^KÑWw6²ó© u?~œ”g]vú(L-Ñd¨Ž¶%yXËêW˜52ï`èÛñ*¿]›ÎU^qOZW~PÛïçHù—n]ÈEêxR¡÷¿:²“ € íìaVQ[×LÑœÐ>¢¸ŽO¹¥—@7Ÿ} õÚ›„wNå þaÜW×1Ð!Sá~Òaî/<ª"b:ìj #½å©ø×Õuë_BѸ¢ìx‡@‚Ú=Ó€UC<½I<¹½ûô€õ¦×z{Ûuš14ƒk}ñ|LÝÏu1‚9Vlç õw-œNBÜËM4R“¥¥\ÃO  ¦ÔLÑß wAÀ•U }Þ%…ÝÐ ÐuK0}Õh®Ü±S¬Ë§­Ü§Âæ(*Ô¯GqùÒ} C3úÎHÙÂ={.¬š‘èÛ³ñ`hãÌÝØk ùÚõ÷jäu0ôŸÍÚ_UJd­yŸ¢•cÕÄW;Üæ>¼z†zÏVc¨u† 51ë+E…æûLºÅJN€þ}þ-oÝÛDÑ_xT·}ûCly P«„R—}•«uy>ÛßÑ’;·ïGJíáZO} 3Ò^ô~ÓW˜¤÷”¢&VÌØøÉ¿ÕìÀt×ê´Vúˆ~šÝÐEwO’ÜsEêof&Œzè’Óap±-ûþV¨ú+›¡…ó|%®çJ¹¾ÎârÐÇGÕÀx¯÷Î _ˆ5ñåÖ[™’Iê·@ìG Å^ 5°èÌŒMåŽ}š iï :%'2tJÅèëŽú{`hUÉ52mõ2®Ðþر n?yƒ¢ f®kÊßäVÆŽ>÷•rõͨS/CŠ >ê¼D:ÂÐÊ[¤Ðã÷¿8ª²¯q}¡Ëôß•Í×(‹4Ž*›‡þ¨•ÝÇ*›·žîAÇ}&¨lnn¯Kâ’»*› õ/>‘ Šò>€Êæ_N™°²âý •Ík,Ä0b¤> ²¹±W x?JПíwçÎDÙÜ…¡²¹é÷xP’ºPT6w •'ÙK)ú³û‹UƒÄ©'oSôÿÜ?¦3›<.” ?;ß~„ótÙÍП½ÿV;ø?—ü'Ç)%2/Š*¬Jf•–Uå¨ÕbÆQ›8÷N©µ4ÛaÙ@ªMÏIƒrm wµV èwù!EÍ.™B¦ÓMn@åiÒüC‘ •s‰ä–< Bý->'°’äU ͺ’ÅüÍ5¹é«Ž¹~õÜoë©iŸ*nF¡±Ø§á+Aß$ÕNi\šBF…ޡ蟃Ä|C…ú%Ç‘W6 =¦0È0‹æ—nñË#¨n[Gqew+@óK#aJ‡sܧoØ *êõRÞ²ªO¼mœõD5/)Ú'ûš(v\µzcBý¸ÏT׈?ì¿ÍГ¯rY7·ß¸Bûk»ƒoTƒmpÑ‚>=¿rò‚Ië‚3 µñrd¾§|¸Bó;ÝÒ…µ—;ŠQ»®sá»û‚þ£úýÊDöÖ=Эsz°ãCÓ¸1ãî“ Wo†Z‡šu¸®9’ˆy;¸Ål ÊRåª×e­>û¹þÏSY̵HŠÜÌ õ¡­bt–Žš´ò]w@…úïl°f[{Ï4âšKÞÈ}v¦Nª¿;‚»~àIâ;q÷Ñeˆ™Ì8c?t{gíHXwiC?ö 꼤¨P¿/%pçôB†*øÿŒ}ܳÍqäÆ×N½fžFäÚ"¹avnlñŠ€:iýAtv%r…öߪÑ“B Š®O6‚¹Á#¸}¯†0­ñN³Ì¡k?mTh¾öŒ$Ð÷êGQ¹1 Úº˜¡¿ð¨Žp¨ Iâ|@Ç÷ ÙNqå¼ÌÄ¡:1Ô?}‡ØÉ%›¢%¡»¤æK²¸•IÊ—TqnaVýgêº9Lq ë6;bB§q=ǃSÓÂ*øÔzžDïï´rk¦Trðß–dh€ÿ8£ÛGt÷>$¨÷…Û°È{צý6ŒŠéÌPÝÄùd˜ý^nöªtÖÞôš¢BýR“âÀ𯓠D`ùJ®ÇK²ûÐ]ŠÊEù–'ýÙ¡FíÓ!dê'‚V”š@Æ—W\Áåà‘odOQ)cAb¸kÜ¥‰°ÌÙ€ÆõêOSK*4¿ßç“$óYC eÝïæþ£ú´Kž8Mã4 ÏÖ3¥ÜÞsHËlwŠzMùsßEÐy|åî¿9ŽÙ¬ÝÅ?Øfß´íkDjüNP¥ØNLqȆêFÆÒñ·*ÔßÜð‘8ëå&@ F“ânK¹æßÍ•m=}Ÿ¨G¶sm—ƃ‡YCŸ”‘ßÌOp}Á–R@s6„­™\¡~Ó•‹ˆÕ')Cå,V(~Ïç–eFÓÔà?):ji6cx4×ûG.ì{@ÐUJwÁLÙš¡BûÏ䌇û^‰]¶Ž@ëùBî6¿j’yî A_žW‡ÉÆú€ ÍWz“È\U(ªú×T¶¾}  ÿÅQ•}-­SÃÄ"@eóøÐC„Ü”PT6/ùÍŸ©^¾NPÙ<Õr6Xfv– ²¹wÍXÒjÝ*Fes¡þMKIÅ@eóãÏz@úDc@esÿžÅtkŸ†Êævù ?H‡¢?Ûï£Ýª9dCeó^㘺ö 1*›¯˜q…TT52ôg÷ûxl1¼BQÙ|üá 0<§èÏΟœ¯N&+§ú³÷ÿãêp3Õ]òŸÌñV$žõaè½çâ1ïå¸6.@s¤/÷`c&ØÏâ~iT¦ç#Ör;°c/-¬¸/®Ñ!hغ÷RûV1ºoù ª_¢BÑ^yê,óX4 Bý¿lQ€¸'›MV¸Õº{kK =â?”¡ÞQßa+(Z»¡IZ}¡IçõYâådî×øÅÌÚ-FŒÒÎKÉÅ1ú€ õ“À`ÒxK‘¡J«Ä£‹ô¸5ËÙ㹃)úcòXVÖaAÍW¼–¾-ÏU[õ‚öÙÝ…+´?œBË=5¼_1vâNô¼ ³o%0T£ºìú‚+4ÿ«±t_2´òsµær…îÿÇSUèRãê?Åê(zbQØq‚Ûxe ÚPÈЯ,…|8qkì|ˆ©÷ÿ.AC«âØÜÑ>€úªFSËöÅ"4pgot«Œ  §Ê ùþq@ƒÇÝç>"† õoÛ½ ÂÖoôw*ô;Ÿ¯Ñ$ÕM¨¡ÅãI ©ãnÜÈnì|JÑ‘sYÎêýJÙM w‹Q›cÊdÚr@…ú=65’æ³`hÙEš7t2w^ µ^}œ &iµr%WîÓÍw÷Z½²”®p…öÜ},޽"(œ¼γÎs/_æe24îœ/Ób@Q¡ù†ÃT©—íSŠÆf]›×&ráQÝ!&6ó“(úîÞzÒÎ ¼ÝPñB{C54è(w3®Õ˜ÓÔÿîm@›½G ¾Ï•ko¦‹Tc¸^Ž”œ¯»Áõ¾¡N&™ÆÐÕ*ÄòP"E…ú'Þ+ƒgeC±ý*¼Ò¸OЦY.dÀ³Þå'nuÍâêé¢äGZ¢lI2>p%£‹aܶ~\ÑâlX_ÅP¡~!þ¢ÖïÄ ­MÀ Ü~ã~q“JO½I%¨\Æé ßù\I½¢¥YC0÷m@6ˆ¤ñ ÚßG»<Ú“z+£ ?ÎææúܤÒßpc‡ˆÅc»r…æ[jlj/ Rf¨wÆjÒ›û jè·âÔ5”¢5Ý‹ˆÆõØû®]jþ’k9R$©Ìäʨ€?:DÚÍó*Œ|Dн)ÌíSèŠDEËËSµóŠ¥ÝÅV M:íÆz× ‚_*ËzØ(Õ hážÇ08ƈ¢Ã6o¤ã2¸±ç”YKïîRô™|-)ÞTBP¥•ûH±‘"Wîz+}ìô÷ß°ÿk¯7Xüà ‚ õ»±É_ºHÑãñÌ£=^ŠžðÑ çmt *ÊØCMÇÚs]뎊×1´ìr¦Øíê>Š í?£õ l2oˆQ%÷—hø´õõ/“~ÉKÐI³Sè„w}šoäË,ÛGKÐýƒ™V€ ¿ð¨~]~‰÷ͦhÉûÛÄuánáûbóœ nK¯~¤mÌmî¾Þ`»0•{bÝtÐìt‘»ÄðˆT­úxŸ5gp]ÿ@³‚sÁéú#Š õW?rî¿ûDÑ¿B«a”º C‹šuÙEÉ+‚^þr‘Þ,ÜhŸ” q}g[‚Êm]!ÚØCPeƒhBmT>¸3­Áê—ô0•@*Akú$±HñX@«Þ¢‰s/nöKº`Õ0@#&Xïøî }ÞuÛY¥ÄÚo¶ô d-h—¢¿¿ÿLîûQÔ<Ôœ žrÐz™0öð®ÐüšÍ)âªU×¥Û’¡2û÷Õ½û$¹™¢±Ÿ®ëÁª ­µ‰ï‰êÆÕ©75l0àÞº>\¬&P´2Ôê?ËqÃ5ÉáÛê=´FrãÔŠjW¬'Z?w\îø´o W¨•FÌöÞÅPƒ×»áHd:w¸ª™<ô( W‚‚|·qqùm‚7 ³­YÏ=ÝLž®Há>ÞñE"¨P¿ÁæA,Bèo1̯ð475ú=5/˜[&}——Ï¿†•µæ:¯zJB»Vs…öK´Àšf)Eë|ZÀáÅnɵo°:z2Au"/ÀÁ­;*4?Íì!,-¾@ÐÑëNÀPw†þGUöå0PBš¾ê1T6ÿãÇ Z½I›¡²¹ÛƒPH~ÿœ¢²¹ÓûID)4¢²ynÞ$º.f%Ces¡þ.Çæ€GP!CÿÏoËÁÓÜ ¸2¯*—3 ?EPÙ¼ë¬Ý°êz)C¶_Ú«BºvÈ=@eó9QRrÓ%@esÛ¸ëð­XПÝßgÊ[øÝ~CeóÝZ»:Ÿ¡?;¿mÔP¸§ ýÙûÿñaU]ìÿ\òŸ´\ª)êù÷/4£¥Tì:Þ–ë>,ŠŸ²fh¥M›ÄöÝ*î ØØ¢Î5K±6›XnÖ™n¯3\Œj¸ÁÎï} šÜ–Kj¢ºCNÑÒFT¨¿kú~6>F‘ D³ª/¹ÉoDz/! ]iÉ\‹®PtÁ…Ý ß”ÏP›¼Rò+‰®…™¶=qÿI:aÎP¡~éMÉ0cçy@ý\/ß¹ îüȬHc>·ShýV‘Ì’ËZÎzJQ…så´~þ@…ö›D? i«Ëõ=Ð f=ý·•CÞÒ¯wЏÇ{öƒýr…æÇ¦øÃ±¿þ"è¼Zœ$• B÷ÿã©*t©Û+;Rvr Yqù$£‹/×pâ#šØ×ƒ¡áÉŽLõ Wa÷ ±~ßJn·OsYMæ!îïÞ©0f| EiŠ”­ ah7“@p8ÍÕì»–Vú à õê±–ý¹Z ¨o7Srl37…°'+³ zoç`¦(] hã M6üÚe†šlJcµÁ˸í?NÝë¤èÊðÍlC…ú~Öþe˵Lý§'~ ¨oì"’7»P©û˜ÓòowÈ$_Ê;q+wlj´7¿ ¨Ðþ½gGÀ©ÜÓ€¾®›>’nÀÞ ÿ›÷Ícc°8EPÁó£TG å‰P¥¬Ä(£’ ¿ð¨FuûN´ê‚Ø63wq O`ë'FSô–Åa3"Š ¦_KÙ«%)ZR”ÏzüÞPu‡Ä³E'Úá­3«>µŒ[¹µ#i9×JQOöçÕ»\¡þ›ëiù§Ý€*¨ÑM#’¹_^í‘Þ(áÚd4çܥώ±YÙ"tèÊlq:ù†³¤ÅЩÙF¬`P ‚_ßc!gàJ1znòs¨©Î¢hÀ–ƒ0ëK2 ç%סYn×=í ñêÏíÐvâ·Oç íß¶ihõ;è¼ô0Ш9ʵë¯øvãvW>*¹\¡ùáê¡×vž ùó%ÐÑr7EáQMq^úz"m¿ðãfz$²• ¹ëÃç°cs?ÿ?Vî=*§­íx*$!’P$9甤5¯I’S;I!$$çT:¤R©TR©$)‡T÷œ©$TB¨„Š$çÃvø=ã}×|ö¸ßñŒ5ŒÇýÏçïZ×õ»9Ö}þ¨ÃbNrÏ\ÚG4fqSá ËGÐ/£Ø}ƒ½€ª¯èËœ(ãÞýÆ Kéà¿éÿW‰&¹»1 Ð5ZUäzà!nùÃIP0Ä€ëf7f%hÊ‹£”xêUññ8æÈµµ ßG¸K’`ÛoŠŠ¾o)…noú2ôhöqpI›Ã Îm„#B zßô)ü4ð èûç 7{C #bIPN.Wl¿Ã‹H¼Ðx§d¨XÅõ®{ΚõRÔãþ88<™¡bó;gCû9Gªpå´ qºÊýƒGu[I ”'@ô΂7ã̸ŽwSg×{Ü!¡ñĵþ1wçy°Ý-7 Ê2ß hÒ£»Dex0 q6ÀéêÜ}Ò ¸×JÑÉ£ºANÔ1)*Öÿq¿þà ¿Ðóßg€ÝóyÜ<Çn°ãX;‚îzx™¯´æê Ådߪ΀ΛæI²Þ]!¨‡vWø`·”¢*ûáù¼®X¿Å°úój†Þ0TÇ®äfN.†‡ #¸Áà ¦ÇVn];vgQEåjMŒÎ[TlæËlø¹ì  ¬  LÕ›;Í(‘Ü“obhi'vwR8Wô©=k-û|mCí:„3׎ôÕɩŠDôtßÞÛЫÇ=‚:;/ÝJ倮qê¼ã¸ŠzöÕuìý•¨TÞç~kk ™ghåúçÄñEŠ)Ú£y4ô5eè÷þ=Yß”\±þqÎp1¿? E§ÝÀ<ì1A£,òˆê¥çÜ%SµAíƒ! s‹3„WÒŽÜÈ@’®¬æ:x|!/ÏQ†tKb¦qÅú¥]~Jö­™ÁÐmmB‰÷úÜ1L¡8ÄŸ[GÖ…nç:ŸE²´m(:dÉGÐA@ÅöX·_èßîÀ§¢\µ‘îÌPišõÛ›L“¬ä›oïÖ“}»Ð‰üÙåÜÿâ¨Ê¾ Þ?aï|‚ÊæO"ŠÀÆÊPÙ<Üv*¬½1PÙ ïµ PÙ\¬¿ëúµÐN߇ ²¹}7˜ã0PÙܲz8¨lž?oK;rÐßíçq×dÃ[ŠÊæ¶–/…M;ÌúöÕ¤_”BýÝýá Àµ´á€Êæ¥>›ž"èïÎäü€ŒÌ—ôwïÿÇUïýï%ÿÉmn»Ù]—@]Ôæ±–û™\§[±`¨¡*EoK·€ËÐ%ßi'x¾÷dhåh6ÑUzœ c?Æ0´ìÜ:XV?𫹤ŒØv¡¨XUŸW¦rå7)ZtM(¨ç:èÍ£Z’h@}Ú4’Y ¹ëÏïemÛLdhíñÓÌÓb€.ñêC Çp?åäЄŠŠõK©(Í&4²£µe½€Ún'5ßRNÑñW ç;ÄrIáõñ =? ‚d:§pÅö/(M † ·ÕÿõŠ ^›Û³”Úž¢è¶’Ì“uP±ùro"'¼\yŽ zG@ÅîÿÇSUìÒïuºìþ–@/6Uе.÷¸™5£ÁÎd9E/ìëMGs¥è fôËO@]ómXËê=€®º™@ÎÞ­fhý½‰ì…A·ÀqªYÜ^™ÜËý\±þ»Ë§ ¶š¢Z!ÑäCGG®Ê©p°÷J4ʲ6“q\¯~Á,wÙ&®ªB1½Ò¦ˆÛ¸DÂO”¢ò«Ù¶P±~Áóî ¦#¤¨änkžÁ-Êݽ‡È?Êáê=Ž!õíš{è!]›DÑó–š$ä¾&AÅöïÑ‚³º·õÈ&àô«‚[ß*0s_®×¦cdëõ{\±ù¬ƒÁ±Ÿ3 iuÁ}ô7ýƒG5ĸ/¥Ýžš3ùžð®}×tg;È|ÏôEÔ`𘸖 WNŒ.È:5t;T?LæÎ=Ì ™RôçÔþlýûa€¶vðeÉK¸:Ï€ŒN¹bý×–‘´1¾ÍmßB:µ?ËÕ~Ù ‘Y?óÑ}Æ×`j#†š„ÊC¼B@m¾‚¶ë½¸m|ê`h©77T;<æ f¨è»VÖPª¤`@Ñ2©õëóR4lÐ"òÂÛ”¡Ž™ÏóMN¯à:¤w!FEÙÕÌt–˜¨Ø~§oK@Uí æm¼àت"nÜ€"ˆZp˜k0ð´Æï”¢bóý;^†áÅÛú¡©œœ_ÌýƒG5æS&@k%:ðÓò)7s3äŒÝLPë–PÞzD@-¦äÁݽ Ÿ Î ­RÔhP/RÐË ¸©Š°`rA3]ú€›‡7Wù‹7û6 P±þ4û yŸ÷Ž¢6 ^“ÙÚ íÖÍLpÓÜ%!ÁAܬiÇ­Ï"¨°u'D6=’¢_ú~ Ï>}¦èš¼UP–ö‰+ÖϰóOªc% :tgwï#hpM UÖreè‹âil^¹%wLŽ;}åž莂lØVÄÛÿtlÔõÊôûÚcÐÞì ·!r/ ˜:¡V+ˆÊC®Øüp£tnÿ†Žë8Å>ÛBÑ?xTwÍp€šo÷­0ÙWÒ¯píªOÀ˜×»(ê3' fè×}vð3ðE'µª@‡ûÖø0ñl, ñhþÂc‡ ªXø:-ÆÐ‚;]¥¦SnsÅú¿ûzÜMÚ dKÆÕÚq=+JòŽÞ²á6o”§—ÏÞ§¨ŸIO°WÀõ ¯'æ)ç´Ý|-â9š2vžc^n(*Ö/+­Ëúªè6Í÷´º`%·Ï•`f¼³‘¢Oާ2Éãzæk |h»¢®§‚܇] Ûoày¼ä“45¦î8òo½ˆäÄ3)º@Õ:BQ±ùÞ†”ºÎ:GPC·;Â䙇¸ð¨>ñ8¶½³Ý7äŽæFøAïȆ–­îA4ƒ»m©!d*Ç èG§àYð$M0™¸v^è/…í vg'wìZ¿ù hº;1Ü`HP±þ™Ù^¦>Ùkºúãš¿–ëomÚØ‹¢gõüHº¢"w˜î˜‘DД,?°ÞáÆE™|* #'ÚÃçr®X?ßH/ÚÏ(PIê&AeÚiî™…'˜ZaO®C¼ûÞù÷¥j0¹XOQåÃá×DE†ŠíoJ¸öµûý¶å&8‡lã¦üPw½i õ=—A“%›¹bóu¾MªŸ ‘¢Ÿ›ª¨Üžý/Žªì+äöeHýè ¨lþrjž`ís‘¡²ùƒ©®7CeóyËë øêi‚Êæiý iëPu‚ÊæbýW%?§Q½*›n»J¿;D¯ß,àŽY}†ý¬Pù ál¾Ñ<@5ï]‡gƒª¯° \:,æÊ})ä[u¹~ÆÍ›ÉëwÐ(&ååºkÝ5˜uÿ ×5ò ]ú0‚¢9…1T®íÄ ¨Š$B¾?¥hA{8Э’¡bû«™°' úê ß9Íù®ˆ”dtehÎn?·È+ú¨¸l¶·u)úá)#ŽŽÃMÑ?xTµ'¥¦ÇÔ*wo?­ìTOÑÜIA°ÕgC/„L›­f܇7Á‡ñª­KÿIz¨åºúN6ß?ÌÐQ/*i›¯ \³l#VÐæ®)ªsn ¹·—ëïø±‡P| Ðo-—IHr wÿµ>L!,«“t@°y™ÏÕûþ•<ï<€¡_´jHËŽƒ½ûð>˜+lghÙ¦F©šfW¬_I‡:P—@¿lxS.4¾ÉUê´»·Ðs< ,‘ãšoßÁ²z%1tç¹hFzeTlÿäKJì@ zýÛ:{Õ4n»åìIB-E3mŽ3ý ›ï¿¨3,M¿GÐŒwG`ÜÓÜ?xTKØÑáÉý•ÑêµßÄÕ¼¡®a=*ÿþ9i:S@ÑÇ6ÐyæD@£]ò¡¨TŸ[Þ3’•ìSfèÞa¬ä€ “ãí˜öÄa ^-l·{ ¨Xû9Dì¾hîˆÐÙXÂÝkÝÌÄr$8Ã'»ÜuËþõuÐ]‰r<àZïBî,y-æšÒ‡ë?!FˆÈìÍë÷}L%ÈUš¢ àì7ŠŽ›Bꋞç£OF¤'+Ü)ê9bÔ-c9 –ÅãA› *¶ßit}rg ;®¥áCçp‹ÛùÒ{µ\ç 0ûë9®Øü×&× Îë%E¯Ô®©çúª½á(iàvŠZš#”iér7íT²Ù3¸ºß%8W—ëZû\Ÿö–¢“uaÝ£Ý í¦M«ËÒõ˜5nNŠàª(¼†áEݸÝs¬ wþP†Šõÿ02ZsOZcsÏ?ÀuùeQ©•=Ù ìU·r毃)N uÉ|@4\Ò¸CÞc‹â)ZR§‚†.»êÉ:¦øpÿàQÕR#Á›RTë…‘˜Mç–]ôóï¡Ü;G¡»·C•#¼à—*®ä Ë>Tr§:W6´çùy?¯HŠF©Cëövfº¢–­`¨XµÆ‡Ðëì,@×õiýÕrÜô1—‰IJ.A]^O…!€>¬É¦9oO2ôÉYVþ×,níÈ00ØRh‘Nt_ ë7ÛØb'TRÔØEºÈæ>R©¡Co^âÞ=oÈlu ¤hý¼p«j g—\IàZ†Ší·½`&\}<С‰m‰Åt3îùë  };ˆ ?¸ÃOCu@ÅæÝÌ;䚢=õÚ±°}ýý/Žªì+|]©´ð¡¨lÞ#ÛL6»3T6¿¯fÈîÝô`¨lþnèM"·?•Í«C–³bÃ% •ÍÅú_îð†ÎžNPÙ<-rTMÚ¨lnßÏzPÙÜöd‰ß@Ñßíg](£úw¢¨lÞ|ÒšôH!¨lÞº• Íûâú»û·EØ}?@esËüàxËT@w~N¹·@‚äýÝûÿñcUµîÿÿ&øLÿ~…Xþ¥ÈPýÔc¤9çE3û†.çw´Zn"QòµPÃóµDí¥ ñãb¡øç¶{¶eBȵF†vÛÄÆí á~zv‡p;CjÍëìû~V#q`è­²ÖÔ]‰[³& J×0M›º Ü>IQÖÕÓ{RôÇ“¹±cC7¼jß?Š.ˆ †Õé£*Öïî¬q,½|8 Žn÷i˳\ãOúĺ“/AÏ®‘#º åõéa™’å-ÖÞBßÜçŠíR5fŠOF4-Þ€õŠòâFÔejq‰ ·|:S‰ÞDQ±ù‘y¡Pµ9ÐÌKïa’Ê‚ŠÝÿ§ªØ¥áûIñõcu{êKêÍçoC*·ŽáJš*¤Fsçül‚îÁuôl†•ÏMZ©²}©8.E“ã_ÓÃ::w& 9«ÆÝqxÙe6 býó]"Y©ºES삘Ëy‚èß<ÛÅ磾ᄼ/A—×UxÌÐEªghîü£Üǵé§]׸òÆ'YcÓ‡|T¬ß“MúÓÑPë[ÉBw[nòν$ñÈîôî]ÀV}wGÿ-ðî¾wɃ¶í“¿Tl²}6qT AÍbYü’RjcÃ-³Ó O¢u(*6ÿìÅZ¸ÞA`¨w†¼8ÎýƒGõÖÊâôô¡U}Nâ¿]ÌGõªù×çØŠ®î*°ÒÁR4eÁ!XN(C'èG2#K¸Ï IæÞû]ºˆ¨>¼™ZÕ¨³ûu*€¶t³‚åpÅú;¿Þ̾ ¼AÐ…æ(×Ðô SAyÛO‚²öQà§ö‘›¥°bÆPÉVLî hý½¦V¯BÙgWÚT¬ŸgÐ:Òq: ¹ÒP2ºûu‚ºÙ¸Ãº™[]¶3.Yr œ.‚Û³\†:ÍЦ¡}2¸bûõýÓ‡/ž´Ö.‡¶Ä|æfɧÐw~(ª­î® ¨Ø|¹0=éÜÏn 5™©Iõó£)úªÇ *"y?L@]÷ËC«v2×@o=[ix’ ÷ß»°šúH޵ËÙÍÐ_Y®| E5Go§ÎOJц«íÙŽ|y@º+H\ût•¢;Þ«@¦ïb†Šõî%ÏÔÌ=cêEìçq   ä‚? èëãu`¬ò“¢;÷³†À€¶$Ÿ¡×P®qdªpÄ)Ž µ¾~ •å ¨X?•;‰IÁZ‚v!O¾}УKj!üÁm‚z…¿†ÌîRÔ}s>µ½gFQ ý£Ä¥V‹+¶¿f{-3VtûÂi4÷B{nÒº¾ÿúŽËMyØ:ÝäŠÍ/@ê¾’¢/Â_‘N ýƒGua§>Pz^@Ë¢L 'À›k»~û®xгÇì©ÙûîüG½iýöS¦h䅑Ķh8E[—–~>u ÐðZ¨‡lnWc50Ìv”¢qïûB튆Šõ—øéä›$ÎÔ2uÙ1|"WË'¦Æ‡2tÅyðà:÷JY#YmtPßÒµ¶Ž{°÷{ÐÈÓaè¤Þ[ˆÁÌ;\±~íÆJ#ä èÐDgª9Q Wž‚ç"†¦ú僤S(w¦’½6t$×y'˼Ѡbû+/µHçÙwÔºIWzt²—þu ƹ/ä®Pcž¢¨è»ÆŒÍÒÃV^ oäÁL®›sÿàQ}Úk>xvfP‹ëÀÂù”mìTHœ¤Í€1Ù Ñ’‡ÜÌcJ‚Fš*CC£²¹‡[ 9ª^@PÙ<*EÙúª1ôwûéz<£µ®ö€Êæ*ÚŸ¤-*›oµ-å³ýÝý“õÈÅãÍ•Ík»þ rÕ rÑßog6‹øn»èïÞÿ«VöüßKþ“ãgf9«l(úåãdæ,‘¢Õsw2ÉåxÍÔÂBÞ!èà)¡Gê8¾?qÌ-ãÚ*/ ­!Ó¤hñ|9ˆ.~EÑŒAu°ãtC÷SjbÇëÿUÿ:‘ÓŸ>Íÿ±Ÿ|é\  ­ÏîHõ®ªµÇ‹Ô®zMPçÛ›™­Z@´™—7Hù-•Älç oƒôKAÅúéQg0q/ Eý£©æ™4îO=)¸þõTŠÎÔŒ‹åÝ7·•’µ­Žl&eƒîpÅö—};J£#)ºëÍ)â4؃[Øå5|wÓTóa Üz3‚¡bó½ž÷9Kû|Ô!Ô‹ä]&¨ØýÿxªŠ]º¿d³¯Q!hYlOi77?ˆª%wTÉØ[ÌxCÐØm{?†FïÏ,ÔS´rN‰`µ‡¡Üf2W-wîÛ~Ùt¨³*A¿oö U/®ST¬UŸ©døÜõMŒENWÞàN{AœÒî ¨¦ƒ¹àÿü¥í6•(ÊÝ´¬.vºK¹r ò ®TÑñ-ÑUNT¬ŸÎB!kz1 ÙÁ߈ó¬ n0] NãÛ0´.¡;Ø™ÖRÔ¬þ+ôïhdk-Ü6Œ¡bû+z½ÅæÕÿVC ´¹i ³¡Bñ"Cëzî¥ |¹bóÙ u˜õÜЬ§áÑ î<ª/}íYӖ΀šßŸÅ¶Œ3çÎýìGr·ôGËb(‰ÊGwì_As cT®ƒ„h;œ”¢ñ·.0ý­ç¸CÔ#ؤ½€î½·’©6_ÐqˈZÔm@Åú·Ž[Fê_t4Ýîy¤<™;Z?žìV¢’ýYî’‚ö‘Ü“Sm» œ…}Rtdt;‰«¶< ý2aÅ놊õ[Öv-x¼)t{i*|‹ÈàVά#³—P”xJ"•«MQ'“WdŒ›C³~Ô‘ø†;ù¨Øþ÷ˆ¿ÙW)¤ÓJ3˸n÷+¨“÷(Šºfô"š³óTlþ°iŠ'EµcO¤É ýƒG5rœ1»b³ЈöÝYDp½ë¹ö5ï8‚ö3÷æVíW'["”š´¼’FçnI´ MüQèëL P¶Šçš–>µÞ{ ,ߣÊP±þ—áñ±€&/–€æñ\ÓMÔd³3Wn™žÁ]|ú(´Õc¨c]8a0ž+wv·IØc;I^\NŽ¢¨X?«—·À÷± N½†M.ù'ëÀ&3{‚V}Yž/}¹žz{aÞÎgM‘¸“Ì Û¿ø„&TŽ<'E¦€RPö¿mÌ$—§WSTÿì)*÷f*CÅæïV²&Fýo2ÔÃ~SÚêÍýƒGõàŠ*:Ðò sm©¶ ã¶ÜL=ïIQ½5úL1Aéø³Çóõžsœ}<ÔШ>°dóK‚>ërR5S49~ X›|Ððø88Ö#…¡býGÍð‚¦PO@…¶É0îÆFàI ¸Aç΃jt4w]߃äÒçrŠ.5¢Äs×™|ôAXmÂÐŒG¬dgEÅú­¼øâ«ü êÒÔ ÝÓ•(ê:8*åÛô¯2 ª}Í)ºû0»êÜ›¡£³‡Ê>€Šíïø¯ÏçþƒK¥¨q²XtxÏ]‘Áêu{QTµÏ~-P±ù96ÌqÚI)ê<×›~ŠÈ%è<ª®4ÄÔk_ NÞ~$í¯*n£DŸ}ê?‚ÛîÃ]ª%s½<Ì™ÕêlîÃ9óIòÉû\ éRózAMÞß#K/L´\u.³Ó=ÉÐƃØ:»^€ŠõoœU c~ÌÔwÐsØø¦;w†Þ;ˆY Î-9÷bNÍ¢èÎSÁñ†ƒ€¦Ä„ÀàI}uâMëtÐú{‡`l®X?w‹ûÐWûE saú_& ­Ë¹ ׇèqŸ/¹±ÆÁÜqC•a’[ «ípÿ¼— bûoÝ_. =):§Ó h¸eƵ»’czoe O«B G%‰+6ßóñ2ìI’€.Ø#í«™OÑÿâ¨Ê¾B‡´…XÇ»€Êæ}ƒMÍ’.*›¿þxLV&*›ïÈŠ„Ç.³•Íå;Ö“*6” ²¹Xÿ/5ßaÂŒ8‚Êæ+\‡o51T6 ‚B· •Í•{é‚c½®ýÝ~;ƒÂ +oCesÈ s™¡²¹k½ðôîS‚þîþ†GnPI·RT67K¥çaýÝù[Ÿ³?¿œ¢¿{ÿ?~¬ê×ú¿ÿRð?©RžIº_¬f¨jÛ:aǬ:®ãþ¿ó2ÛÊIÑá[×R§sÖ}’^O´6;ºëÆA°Öóç3­‡ú½»¥èì̃P0^`è¼9Ý™µÝ@‚¶Æ“ÛJLŠŠõá[JNïeèàMÝÉ|ãî•– –R}C@cÓ±ôŒé€V( ì¹ñ÷ûC‹g A:’@Õ%MÚçÍ%*ëgPw˜XFW2tÇ·Vé=÷K\ÓÕn rPƒ¢–sCÀÔ­–«´Æ•F®%è…”«4üC bû-OÄ’ëì ú©5…ºäj÷ˆ!›lšå6ˆ}ɵæŠÍÏ_šÂêë”)ôee=l»ÿOU±K7–u¤_ÆÕ24Ýäí½ð÷ù©H` ¨SeOú|õv®fÏW¹ª÷©¹¼d »”¢w=Ý}êP’LTÏHÑUZ,~Ö0Šž¾Îè”íÕ€Šõ·OQ¢]r3´ås-5P;È}POØŠG­ÿ{„ÔñöSîÈYÿzRÍrËCU#/šÊiOËEK-½ Ì3¡ úNÌbó¿ë—çú’¸sœ¡Q¯F²ù­[¸#¶…Ãw‹Ù\‹¥ž :†ûBç8 h¾hÄùrp²IŠŠíSšC<Ì hÃÛrRÑ®Œk¥ÏnÌuájé gFËÏ*6“«%ñÜ=Ð0«äe%‚þÁ£jcЋ­~\ÌÐÍ ­YîÈ,®cÒyä  ǃ är7*^‚VÇx†V\飔K¸‘€Î¯B5ýt”~4R4»ÿX¹a# ïZÌ Á,…¢býå$F,¨y;CµVº³äŸS¸ÚkLa¸F  jRaÞãnõEWZØù!AU/u‘Ž?âhÓ=lo§ÕÐßAJ—¾åŠõ±}K=Ö‡¡¶Ì`Ý~QTº¡øï¹ÌPÃ(R_;û²*ÜÜÝ–ky ŽÄ¶ZTlÿµ^ää¸ç½¾N4¯~çz¨Aò· @ üN@¶•5Wl~ŸKR¥Þ¤èƒ#?郔 ð¨N»‹å4G1ÔÏ!f}}77?.Ûg:`òSˆ¼ãÅ­¿A?ÇbhÕÛŽÌ‘•PôÄ4?(\–Îбv ‰JO¹/kõaòÃ-9YI4*ÖŸNc#âRôbÛD¶Øn$÷ÐàØô®  ?„LX*KÑ3ºðf{6 Yùp" –Ûê|ˆæ¸(P´Éþ+]% T¬_fþ&÷¾¯)ª}þoº£ÈŽ köߣ÷–34ãóJfÿ:‰›ø1 ªU®HÑùõl{¡¢¢PÜÐú®к°3·úí°vî! &#n“Ö1*›¯4‰ž²‰tÙ¬yð)…rÿàQµÈeq#Íz~^2k‚¶ÜnkÛJT—È*ÝÝAâº÷ªõ­ý‹¶îéDÐCfD5õŽ€>¹³-?~œ¡æÏ“Øêã]¾h55¯d¨×OÖïp7‚Šõ/ë•ÊÒW%hЋ& jh½÷&z?‹¢]ËÛÂ_­±Ü—+%†];¼½$è ¥èÃütØ#—hí™x÷±CÅúµkô¦e•qÍìÙ˜Þ&Šësà(ûÐßš¡òÒX7›mÕ6ÏÔÞ_dhnÐQ¶£!X@Åö'æ›Bý>5@ë}m ¹Y«;¯=˜XOæ^;‘ ±Qs¸bóÓz4Âñ õ²ú£CSôUÏ~)ìùá}µ‘Èâ3îä£F7¾Á¯cªÔó*„,óãÂÙ«$pßYŠ*=X(õ¶1eè•e„É©èyôˆàá?š›ØlC¼7Çôñ€Ì'´;EE?bc¾-4°x-Ûº<„;Ÿ%®ÕB>:Ôöy8)•  å0Ú`?CãóæÁõ $n늣d£÷4îB»n Q´ÉGÅú¥LèC¾}4'¨R¡7inо;ÂôWD´ÊÝ‘mÙ2Ð2%} ‡L¹Vm—U‡=¦¨Ø~mU°|¡ m<á}~'îå9‰›mŠæ¿¥`S•ÌPѯU4>bhМ"âQáÍý/Žªì«àj+›1› ²¹}Ð>èéËPÙ\ý×4f÷^¡²ù‘ƒ×ÉYç1•Í{×USåÞE€Êæbý]N°°Ð @eóJ³ Wå#Aeó¡_V“æÆ •ͧ킷¯ú»ýž<ò!KH(*›OixDÝ\6*›§ÝÖ`®£]ôw÷¿öØæ í•Í[¬ˆÆÄR†þî|çº÷ÂѸ{ýÝûÿñcÕ´˜ÿ½ä?æ"/¡CäÚøô<ÜoÌíßFb¦•- ÙMP÷æ__‡þ¿>Jmˆ«UšuŸVKUûèp+¾‡³™yk ª±êí?o: 'tsiU²6AmÌ«H]£¨X÷@´^ô×ò6’‹¾W4O癯ו ~ºÍí(Út&ƒuŸ¼›[;Ú‡Åìj ¨A·)ÌñI£)º!2šhg»T¬_{­¾Ù~AÏO|E4'¤ hÌÐclsRA‡íbcë'êX³ˆÕEÍ&¨Þ×ñ¤Ëx®ØþÈEÍÒ¿Òµ•ß’Ý9†Û?^›}N ãšZÿ$—fWsÅæGýxý:õ¡èS÷°±hCÅîÿÇSUìÒC¶ 0àõ*†^ìTYó¸ Q lÁ|î…#›àúµ]\‹LÂU%ýRå'¹ö —§/¹õ’5¤Q¥·5Ù»žµ¶ßLQÍç¾´?½¨èŸ¢á;Mô¤¨…s3t™ ÆÐå|©ñù )jÕY…i~ 'hqL.ýËù/@=O¯%Û½ ¸a½Bi»¤ïmxëÇ®œ¨X¿¬òíD®[³)š™g*Ù‘" úº±Ê·þ€•û0®ŽýxÓ5k(Ú'}³-jP±ýš~qÒ]ÎV€Æ›éK%í¸÷šoˆ#{¹Ý{?G73ŠŠÍÏË"!-CjuØ—$N9-EÿàQ’;UE‘ µšz÷¸Ÿ;ÈCçù.ÜÈCÓˆâá¡Ü—ož’[×j­¹” »ZOq›ÏfS‹)1‹œPEÐà5Eàn¨Ýþþp¶ŸEÅú“º[à³€¡vgAúû½\ÉKyæ6ÄÐÊS¥'·gpoäÖú!¦u©Ô%ñ(j¼] Vé¼´Ï÷Ë0©ßI‚ŠõóØ3ˆ*Žp'hôâƒT}Úgnêå`R²f/ ç÷w„âöS¹r$–Ú3îåÖ P»”ÊÛ_p]ÍT÷Ã6@ãÍ‚·åþÜ¡J›@oÈ\†ÎLìCš(*6¢þ0uG@5”NÀÜE_(új\¯]P´ñC[-À()™ë_pZ¿‘IÑzñÄQŽU\× ç?5ôà¯JP·h¢hB9æfžhâH%Ð4«á1Fï²ànšýúwùFQ±þ]z†n8C•fƒËµhnëÕ^ ] hY·S ø0»Ú+‘zÌz+Eý–÷fU ×9å- ÜmKѨ|Öüì ¨X¿¥†‡iýs@= ô×»îê}àÀ¨&ÂM”Ç9FÜ%Ÿî€R«6ESÀàí*¶ÿÃ552äp s7:“‡µÑÜùáíÁsË;UŠ×—RTlþ”Ež`ìŸÅл}úJ³¾>æþÁ£š>´4Ç3tÒÕ;äò’pn“[ÑtªP•&SH)ÝË]½4Þ-d¨RDÒ(¯Ë5 ¹…gB}þrÌ ¨ÁZ*8æ—1´¨û&7n)*ÖÿUŠ2$ÍŒ`¨ÎÄhò¡9€[°ª.|˜hý>9‰œg„1šlRMׄïTÿU‘¯(á–,|_Æ=¦hKM:IÌ|ÄP±~r5š‚Þõ“€N:|žTí/âzÅzÕýŠÚ<ÊRª¤Ä Sï 1±>ýæ~ tî¿ÊGÅö—öN$}:&:ÿ@5éþ3;ke èê1TcQ q(}Æ›¯õh/[²6œ¡ÞcY¦©‡ý“Guï~b ø34r¥ù䵎[c´SÆPômì>ˆ‰Sehåö-$gH£?9†öS%èœÏÇàÉc†^ûñô³ËU4Î _âó¥¨ÏªÙÌî[†Š¾Áz~T­Ö0ÔózÚ„¦&Üæâ:ˆø4˜Û´4Zzq«/›aR@kš›ÁÜÎŽ[¨³“}+Ud¨…™¡t·Ïq‚Šõ«Z/ÀЮå€n÷4ùnè©HÚ¨y˜ ÃÂoÐw[õµysr%9 ½sšVK®qÅöx(A¿àLàZëƒ}ÂYnG¦§˜ÆPíM‡YþÞF)*6ÿÁI öýâH@µÓ–’î=¹ÿÅQ•}IÖf7_[0T6ïä¾: ðd¨l¾[1 œ†N¦¨l®@Ât)*›¿1Ã^›ŸT6ëï:°»”Û1T69£4×í`¨l~²úX?•þŸƒÕóK|º2îoö;Þ¾î ÎT6Ï‚ctk‰ ²y3sejõí(ú»ûÓ7NÕ^•ÍÍò«ésÇA€þî|¶«œ諘¢¿{ÿ?~¬Jìý¿—ü'¿ô$҄뇵ÔV%Ö©Á\Í‹‹ˆçkÕðÑ!?ž? èMË Ð)æ®± \ä¦PÔlõræorœ¡—Y›g>õ¸&±2†*¸td_5»rÅúµµg*}(êžJ˜ó¤díf–M†*72Ô솔&z—s%ýƒi„Öq@DÞ$Ù«¸ýôf‘{t4cÒlúÑ¥P±~r×ÀcB: óÆ-†°g¸×v¼&ùqˤhƒõMÒ¾ïІx«A}lŽÍN&`ÀP±ýÏ_Ìfߺ34È|%û{½Â¿íëHæÆ¤r};o`nÆ!\±ù“²‚¡Õù ­?ƒ˜¶=¸b÷ÿã©*viáÜ£d^w?@¿žk ò!¹$à(y»¸oBô }âF®ñ’G$ÇZBÑúˆ¥$ãÔ‚¾¬°fº•Ý•´<¿×à!Ü&½ â¹-B@Ãúö¤Ëv2T¬Ç:u–ì¶— ñV—¨‡=åºEÎgo³ýºñÁjÖ-êE£w…¼@}¶¶€§ù<®ýò<ÿГ[µÆ"wϤ¨X?å¹q ¦yЙ–סð` ×uürÚT“¡/ï[P!g8÷s}¦ú(“«u%ƒ÷˜) bû—ncë'ÖRôµ‚/3؟•(Pfý50}ÿh1û¥ë¨Øü{>%0¦çeŠv{7 šË3ôU«âNà*™趯}aä¨Ü 5ûÀkàfnŸžE`¾Ø”Û»½ø,²âέȀ Û3¹î#Ï%oT§›²ôɃŠŸ>Ëzª•´ÙÔ‘ìZP±þíÊS©ÿM‚~é£ ¸¤2®ÜÔ~L"W$ Á‹ôhŽ|A5{|Ï‹¥¨»$ÆN# 5UÌ )£{:zÑmx7_+Öo†F ìœbhKzÉÇŠ‚ÚvìÅŽ®Qc臫›Ø¬È½­ÝA»Ø4£€ÀZŸž\Ñ¿ïòÌÊlj¢dY(‹yÿHŠÚ÷žA^4ít_súAÅæ;{$áî³(zü¸>ô–AÐ?xT/í6€ß€ÆÎ·J ZÐ ªŽroÝþ–~ÙEQ…Ÿp÷Æ(‚)ü‚~=-:"n,s:ו¢cËÙÆÝã5P³ƒª%:/‰–Æg~åŠõ×ÜfI&Þ$èTåxÒl"Çm™êCl·åö²¾M¤.^-žf·nm`hÝ“md™‘W/èx×ÇqAáœÈë·kŒ¢äÃÁ‘RôÖ¡ïð~äŠÆ¨„1Ë ³ ªu7„- Ý0ú/"Yl/EKŠbiiÍ @Åöϯ`ŽU£MѱÅQìnnG‚F†(’/)¤èä;ô^b>AÅæo Ë‚‚k+ :Ó¨ÌÐaè<ª»µáu‹7AöW†æDUîã´WмdCÛ §ðEîw¡G*B®Ùé<âRÆ5Ù¼‡†Ž)ôï©KaW»Ü#'a®z 7Gî5”y¬`¨XÿÁãr‰a'g)j3=“Økí¦hpéן¡S/ï¦Ã¶Xr¢ôÉü4=Šúôhù-½:iJ-]µG@{NøLïj¦¨X?#ò².Îghü¥‹²î 7·qû0<PåÓS÷;Üc%3aˆé Mo®Øþ§G³Â6þíµ-š}˜r+9²JúU8èƒ]AP ¹È›?4,âV–0ÔC᜛ôˆûjÎÑzâé3]@_÷J Õ¦ùh¯;P¡ë¼+IÁ˜›Ü1#ã鄽ÜÌ/yTãnE ·TCëd[@×zÞ¹šU=e!„î c¨ã¯"â9ùQ>*Öÿûx/2íû}Š6†– âÕús‰“óý·ý•±‚óGóÑú«{A2ÝÆU”6‹Tj~Ј 3ñä:”%³ÆÃë÷äÅ>0ÎNdhÕñ1u5•Û7LÕhOëîðåuwx¨êNÍe¨ÝoÒܨÏÛ¿sܶo}{@ÓFfO~pg¶ÊIfïT⺼}/Îc¨ØüĵӘ܆޹:ƒIb¾æ£ÿÅQ•}=ï9žxšõ–¢²¹±E®ÔŸæ0T6›–+XO©PÙ¼ç/w°¾«ÉPÙ¼2ÂÂ]B*›‹õ78WùdCeóHÕƒ¬KÞ@eó ñÁÌ"‘¡²ùЮ7Èé9@·_NÍyRïÇPÙüܨ½`“ ¨l¬ÑLêú»ûgºda¯§*›—iÎ…Ö} ýÝùÉ©Ýh“ýÝûÿù?X_ßñ.ùOÞKýõ4d¦-îÑ• ß’a®ç*!vRe¹B®ÜÞ£BåÙã€þ½j#$ê_ä6çOà„ =ùÈŸ]ËÐV½ ˜•oíº¢J,ލsÅúç–¥ —ò)zw­70–{hý!2´¬# ]:¼'÷I[‚þüQcâ;0´‚?l~ó÷ÓÅ}~Ýøƒ Ê‘edéæz@Åú5Éç± %;ŠÆ^Ïc+:Ì'hø;G¸²)Pß=§!eb ×Ñb,dVÈsÓßçN¾)*¶_ºåU}ÇÐÝåØàÍÑ\O}!òb2EgNî ­ß{Tl~þöKRÕÝ¥èM{tX¡€ŠÝÿ§ªØ¥róŸç¥OLÐcÆjDç‚6÷üœžlå© †~šæËîžßÁ5èÑ—ÀЄ&ÉùÏ£¥¨ªÚÒZ\ÀÐòÎ ˜õDnA¸HJuÔ¬‚#‹*Ö¿¼‰êÅ$S´Òñ™;z&AUwÄ’þ6¾µêÜ$µJ¨äZþúH^ÀP»M‡É§Q](:ìùèvMPÍd€NÙ%ýS–dÛÎiÚ¹!=WßÄUv~W|'qËm${VøTþN!UMèzkHo-âŠíï»Ëšw˜¡nLÙ¡ \­¡¡ðqA:EÍãgƒY»†ŠÍ/x•#˜)Kí÷! bn_ãþÁ£Z_pTß®ÐqkÊHýáSôíæ¬tþ ŠÚF¤²3[• ê# XæJ†öјZ‘‘ÜXÝL&EQƒ {ØÈзý÷2’èÎЙ㥴#T¬ÿÙÃ¯ÈØ ™µ«k‡=äþ˜¤Á :O¦èã_ ˜ûŒL‚v|¦sz†r¥AæÁh®-ü z=í¬û ¢J *Ö/Ós KŸ}P•ˆ«ÔyF*WÚ½ÄH3’¢^QÀ5iC'ÜT´<ùLÐSòÁ7Û™¡bûýlÙ¶> Ú¾‘¹F»q/*gSÉ…KÜv]’Ø—"}®ØüdÏïà2п¶¼ƒF¹hŠþÁ£ªúoR­”)E-íJ¦ö½g{€U6Tx9†uuÛÃÓy&)½€¡e¿:ùg«(ª´4[ÚæÌA@ëÜ{ÃÚi£¸íÕ+È÷×ERÔ²ËbÖ-T¬ÿß>v`÷¥‘ Š«À%­œ[ùL`Ûæì´Š7]­u…ûñ])T-AQ")üUk:Ø7< ¸g;öd{=ý¸býâk&åïì’ è‹_)äKþn÷·Åðª9†¡“&úÁ˜äóÜ}½£Iì²ܸ¯šH–Tlè½ìz{;†Út8ÄtŸîÝCÌÌWP“$Sª’·•+úÔ¶ß! %qDºqÿàQ|G:~:OÑý1ª0zD†ž¶Ž†w ÔâÐ,ºn/×àCOhÜqW@“φâÖ¾=YÛToë h],­ô-“¢ßk‹„ î/ÝgVðJ+Ö?à_Ÿ—š4Ëö"´N‹Ðêqïˆù '€ê(FA}ÜnU‡PØp™¡vãj…Ã:µ\—–ôK¸-A-UšÈ¾TU†Šõ+© ßJvj9q"Ä÷ÇͪP…þ­Ù óÖ€X¶‰äöùtö÷ΣhÑý±0©ä*CÅö‡çG±ˆÍº uøœÀVw‘çRõg$¯Û]‚º>}.ø.£¨Ø|S©7±ø+ ?ÕAÈÌ#ýƒGun”Ô´Ñgèü™µ$²b*w蕟ä{¬ ï^t‡ £o´iU\³r`¨·‚tš[ÌUŽ—cý¨< g#L©Ã·ÓÜå±má{Em— ‡Ž €Šõ·k¸‘¡†ìQ*ç^qgÙVƒ³ÿ@×—6€ßù4‚J&ŽbïN%1tz“{y¼-—¸9±Ž7qKâ3½“«ý€ož™µMs˜V»s]gI¡Û†Þ’ÎÌ|IQûKYâè†zòdmæž"¨Ø~ïØTv¨K!EûÄd° -Û¸Þ·ú3O eStøÇ±,£|7 bó“r²Aµ"„¢¥ùáPÇÐÿâ¨Ê¾úöŽ!æ*›ÇG·¹›gT6ßWkDŸ÷¹ÍPÙ|ÿó™0×·PÙÜ®¦ÚoUf¨l.ÖÿFk)ÌŠžÌPÙÜæTŒéDQÙ<çÑbV–ãHÐÿ3ÿ§6¸é¥ú»ý&oUÉš'ù¨lnh:_ðoß•¢²¹õëÏÂÀݺ€þîþ[ΰD§:)*›Gö Y5÷ýÝùCo“•Ç2ôwïÿÇU?(ÿÏ%ÿÉÌ¿ „zõ#*¹³W:4JnXÀ“OI -lH'·ìs›,ïÏîW¤¨IµUJ³¦h˜` ÕÅR´YCøß:ÄMɆºíû·ý¸Þ˜ÇP±þEÀ«¹…¢=åýÀçÝ8†Žz¿B¶_tôû;ÐìˆKÂî”Ê%Ü‹$®ùŠõˆS£uáî Í}çMlÝËT¬Ÿ•‡©¨¿y iUòâ†\_ïŒ3¹jæQ`¬éÉ?ì$Œ½¡ÍøÁúk \±ý¥%Í`a²š¡™+ŸAÛŸÞÜyr7©\àã\4íÎ9b0i$AÅæëTk ŠËT(ZäÓœþÒ#¨ØýÿxªŠ]jÐNZ>I"hàÔ‰TiÒG®as7á©CåL·JÏŒøNQ#½,Z8DÐÞŸiÌr@3_8HwTçÎÜÐêWDr#Ç '¶†~ m¿X”Æw%¨Xÿ*©;¸I<ÚðÜ ’Váîü?—<'¨Of#¾ÛMQÕ€w0µó†ž$@Ý”îô“›Á¨£-wðˆjÛ­˜+Öï–¿Ø0Põê±0Y"ϵ2=–±êÜg/CÀ_iAç¶•T~²çÖí< GÆF2Tl¿íî:è­ÂP½[Ðñ¯8îÌSaÒð_U=y&Ñ¢@†ŠÍWN§R(ji°>±p†þÁ£ª3Ê’h ´ùÅ:ÍÜšë8×VÐ{Ü¢»|ÒˆcH˜€nÊ]EÎO¾èÏ(*/áædIáム®ÖÕö’·göt•M”«îaèGÝIla·4®XÿQYjP(9ÃзË#ˆÇ%îÞ·§Áü®÷ ™°ÞÌž;Zò‘˜O=õí›-eÎÛ¸Ol“™­m¨€æ,Ú/¿W*Öï½Õ`hœµƒ ©¯¡~v}ãªê¦R4rN;0Þþ„ë©ùÌÔz0ôeA¢#©4EÅö§¸•ÀÌ='p 7ç~;êÏŠÒW³ÍYV÷òAÅæ?ÉK©¼ÎP£¹{˜×Á`î<ªÆ»Ê¤Oô¶ê¹úÄ„6ÁÜÈ3#!Ó¦L@çÞô†Vš(E÷l½øÅ€†Ø¿¹…‡óÑÈØ0LÏœ¡C»ù‚ê¢ãÜÖ½Lkœ òß$é{NTô?ÕO»ü™[Kzå`ݽô27cçòòz?nsï dõÕÙ­ O];q•6ÇeïLÑéZµäš¿s’.s™ëW— ¥ú×à ÈÜi äá‡u°¤S{a“&íð‘j$·¬;Í• ¤€ÔH3F³¿#9Œ•óúuÝd”|JëÍ@zj×;\•‘Rý®ûbn';0ÒÁæÓi,¬Vcî}¡µ°&™áqu¡õ¢R6ÿÐ=aõŠ¡²?9)µ¿üJ#‹KK®ÊÉçwU,²7.㤩é9¸ò6’'îŸ(”šŸÜ¹-dDU#é½ø ÏÌþÁ£šq®?¾½Ë…³íPפ—0çf*T®„ËŽ““ZÞ@ų…¤UJ*ävÒGÒÁžÒÛœT_¸:”-DR‡âJšVr2ïxèêù‚“Rý£Ü•,dEûùU,´—ù&&6²šeÓy¿³Ïgbeä’ºì¢0rðã®`=²'5«bÌÐùÂrç&™ ¤T?=Ͼà]†2ò®Å,ðÉÈ•“ª‹ Aû`áYSGx8‰sÒuÂfÔîò¹tjiˆ«Æf)µnÛ† ïò9Ùj­’EÙÏoBëÔ-øh޹÷å𠽌”šÿ¢ñ\Y¬„¤KDCtËÞÃÈ?xT~_€EkuÍI+ÓuxøÐ$&T4.tŒ‡,Ücߢ½û#éeÙ›é/ÏÉ aìëÅÎHªù®g-&ÊÈ¿ªžs®ŸŠ¤Y"öÉûÄHɧ†ªŠÅæ N-ýûíu‘ì3Í Ú|ÎädEßîÀ_ÎD²~BohQ`"ÌêÆšêG £²aS-Ê¡,ñ»œ”êçtxLŸÅÉ'ßÖÃG3=$5‡M¼F#…w»ÁçÁáÂUsÝ XvH•›'àޞ͌”Úmô,º"©Rÿzë› çÜ(c–º1ò@ènæ>¶;’’ïÊ_æ±)–ùŒlQݾuÜÈÉ?xTü}ñÞ¼ŒLqóÁ¹:@μ©…÷zÇ {ó¦S/ ›gÁÎ'©2f,›¤ä®Ý³azò FXu”w,$íµN±0· wËü˜{YONJõ;æ t½É;MΚHaaòvmG¼pa³G’ŠyßÛGPµS#óžüí¾4 ó’æ7¹ÉœHþn¿ÀùWøã]ùH*ææÆSöMṪ7 ë´t$w¿þ¨\(HˆFR1?¸r;<{»Èßo²J¾¼¼Èß½ÿ__V5¤úŸKþ›ÉÉ`”cdãÙ…¤n"ܪõCžqrtØzt>’>¡Æf÷V®r°ƒ*œ(h-üK¹’÷¸ÇÉêïiæõ§ éüì;½£ä‰Ù“Áru¥PªÔØz« É7ßMÀ´´R(Ÿ×>¤r2ãÁVî>Éî+¬AõÀdF^Ì9펪qòíèþöž›#R‘\}þ+;™tQøù^ÈIØ,'½ÆGWr2æÆ@ð¹²PFvÏ_ë§é#9mÈKÈXÓŽ‘5wŸ³iš!)ÕÒÄ ìþª;H*e§É¾DU _kFñ½§ ½Ç9 kZ7¡éç,È+¬ËÛ É³Ž ÏÄo:óÝœ¬è»F6df#¥úÙŽ …®;û!ùjmDõÞ$Ó'v¨/2áb¤”X ¿÷6„ϯ{ Šf%Úï]¼õ;yÈ…{ï°%ê÷„å¡>WÓɤó÷ØÕ £BÉG]dV¡jçœôýË~TÉÉ?xT^¼Ÿ¡+ùøÖGÐHh+#—G.»‰¤n‡Æ¸Ð©Tè°Æ«n:pòmŽÚ÷‘®2Ç­G‘t§‚cÓó…«C@U/ —¶ü vœ”ꟺj*?ãW†d‚W7›%<<~7æ]É’“ÚbQí¹êûB˜òÏ*$=Ž5ħú…Â/f;À“GiÚЧx'¥úµÀYPöë’÷¸&\lR$´±§æT1RÝGÅÂ'µ§œôd€'¯dä*¯@¾Ñ²H©ýy-`Òƒ[@®8ß熰ª¼.ˆ>„dGƒÃXÔ½ˆ“RóʽðãM; ÕËò÷›. ÿàQ¢ÿ º|5ådLÔW˜ëS.<{g1~ÍÞ‹¤‹Ä¥é ¼`a‰­Þ&i|QIfÔüŽÐ«v?–o…äË>qøÝÕ‘#*GAÔ½ $õ^^æ>ºÕœ”ê?«Ø‹w!©ï?¿ä¸ ‹£æâ“½@™Ë™v™pÉ_lo1Éêm[pß‹ýr2÷Çx8¼( ÉÔ‡äWüŽpRªŸKïmì®z’9û*äq·¯M•-*ç4Þ•?Š„‡Ç€Ë” Ë*@ÿÑXFJí‘0 U/Ù×m6œßvVh_¹'Å÷Úµ~W8Ãn–Pj¾J\_¸Wd'ãLP¾¶Aøjn»ÐñA7$]O?‡œÂÕ £0¸£' '®Å,'F®Z*§€´òÌ‹;á¯ÑÛÃQxæq‹Z7XxŽ ƒ0Í‘2²ñÍË,pk&’RýÙÅ™8d’’­FÎ@³_'9yèÕ1¶ÒºH‹±ÓÁ{Ín¡¶r'|©þ˜‘JáºæÎGU€Ü×̪F>‘½l¢Áuy"’Rýb½æç3K¬ë0# ÷Ë·è!»ã /{ê¥êÀzÛNêO¬•…'Ÿf¤Ôþ࿟ŸÏq S×±G9ÂÁ©M ¾ù,F~îß‹ûÀI©ù¦3‚K¿|ëÔ¬ïáä<ªöuwàù?$¶=vn{„¥Ûµ1(²1ö‹'òÀºÂ%¯Aظ92²yæH(¼ÏÉ3{ÚM£2ÒpMc¦²ä 'û8%aUú@$¯{Ƀ¬f)Õ¿uƒÑø4ñ•œÔ˜Ý«òòd䌀ôî duü<°ûÚ‰‘_º×2ÛSí„ 'êÙ­[w99¹^ íåH^ê韽?ÈH©~™~ø¶Ý$/„F£ekáÙ™oYüÐcÂÌç‡d'rÂ…U!ç°Èbu‹Ã '¥ö—$†Ãý‡€¬õÝ ñúqŠ—F¸óP'WœóÃÆƒ»)ùYuÙió’·{d¤Ïø:™Æ½‰@þGUñUz5 b>FR1ÏKÁô[©˜G>ÁT¿£œTÌÝšõÄJy*'ó Ù­ æx9©˜Kõopé /ù¾Š‘Šybñ7æñµ%'sm½a²)“f"©˜«`Wгù»ýò¾íÅ=ŽaœTÌËdÝxƒº1H*æóU2 ±ß$wÿÓ .v'ŠyÌó3<ôòY wþ•Nî ty¿{ÿ¿ÿõ“ÿsÉõçîŒÑ@V¯hͧOú„Îǵ/î0²É‘©8©ãB Ǿk c"†2Òhb ²½ÇÉeöž,o÷HFè?¬2¿ uϧ ÚŠM2²~ÂG™æÒU@JõZt ÷ìòÑçàÖg±°s¥¿ëÌÈ¡Ãzâ©”Á@î(…pm{$ÇÏ7‚ԯDŽ»¶®„µÝ€Œzr•ùxŒà¤T¿ÏöN<>Ø H£‰r¯k¾BSë,¶§ÃN$3Y¬|å«¡ÍÐlð8»™‘Æ­3}C_NJí7z² çlràšýz›Ðp¦«ñê!´–y‚ûâmB©ùK²Ãú|[Næ?hÉÜI©ûÿõT•ºÔ¥õyójµ uo:²ƒSÏ £¶Å'» µ:uáÊk® [¨mäŸ÷AÒØs.ZE'qrÁ‰³P_ÕWFæm¾ á÷‘dUræhÁÉS^Ñ:¢#¥úÛ&~†ûSô€t¿ñ’Ss™]ĵî$3×”ùM­ÚMZ"WÉ<äÆ6èz.I\ðn¤CÔÏ+B©~öËû0=—ý@Žíþ‘ÍLÈê6Dzg{T^½ W ]6¯–;æy%|9èèJíWê0ãßGù¢v<>ÍÛ'œøù9,Ø\8lÔPš×I©ùy¾~üùÚx$ì@½ìÂ?xTÍw\gJ.éwQ*b¯ ]÷岫„VuÓ¡‹]…ðëxeN=#?ëXc‹ð@æ©-ƒ<Ï‹HfNÙ(or«\˜Øë¨|cÞ -½³Ààxo¡TÿìÄ/à¶ PF~õ E¹¶\¸qX}» d𻛢…dbòN¬œ¢ÌÈ@“נݥ\˜ñ×\0Ï‚¤T¿&vQ—dï¬Ýp¤õ1စ‰˜·_ÉúeYh½ú¤\h¦d1=ç#»• äg{!)µÔ AX®—dþ¨Ž˜c˜&4>¼´È@²0»3‹é)”šÿ<5w¯\ÉÈ2ã1xaB ð¨Úš Õ®9çSDæŸvî†~ÜQ‹]Ý„ê?Ìß^N š uÍOm«ŒqµÃ2$‹uB}› rÒÆëS9âÏI»n:¬fp ’opûnBÈ›»œL )„êÌÎHú?„¾ÏÈ KîBÝņŒÌÏïúW´€´øÕÍÜE©·ÐÞ|ûðd>#Y6,2RªŸî¼HµÈñµµ´u…pn›CX¥r‘ó¦¢sW[ ײT§ÙH:odëwõc¤Ô~¬~χÊ3€Œj˜Ï].~žùBþ><Ž“ë×üdU½)5ÿâЧ²‡"¼Ò ¢ú¶þÁ£ÚÉ3žç"MÃO@ÞÜLa–7‡½Go3ò‰^ ¸4Ï3'‡Þ •ÎÃÜrÞ2¯ËÈÊ«eÃVleäâ÷&`êÌåd½Í3Hï=È!&ùt’Rý¬ˆ…ì\S$='®]ݬ„KŽd€ÎËÜXãÇk”°î-û¤ÌHû¦*°")†“‰s^ÁÙ›vH.íÃjŽ ¥ú9ÿži9u|C‹õ[63Òе/¶Úä°§‹ùÉéá¤Ei ÒÝ‘·ŠKàU$¥öÏ4ÝÀ9gy 1×]ôSÆC`Ð 9¹9+mERj¾o ³ìÏÉÁÜåN¦ðÕÁÀjL,åwÀ¾ÉVaçZp;µŠ“ãWwŸsñÂWò«lß›IBc£®ÜîfŽŒôf IG§ ©­ñÂ<^7EXßÅJöòÎ NÞâ Oçžf¤T ë¡°å² É8Ý_,°æJÛô6“O˾·L•‘>Ó^ËAR1—ê_ù,žuk¥Œ¤bþÔ´/›5O HÅD û¯mŒœõÌ_5Œ2kÄ,Þ-&ÉüؾE¥Œ”êï¼}" vÉŸ‹›ÁÓsÂÚŒï0¦'ãjë@Gi’«·¨c>;.œ³!Ï^Ø+ñ0h9írÃÍ\O¹H(Õ/¸Å<Ø¿ï gŽÆ‚©Y¼ÐÂ,<å{ŒŸàÞ¯O ¯îº/¶2Î÷98탤ÔþýÎð¸€<$5ï½å›ï.Û÷c„KF6†œg…RóŒtì„2 -ªnBæ­mB©ûÿõT•ºÔqc7è›|É„EU,çÜUáúðd ^mÄÉVËvãÐ{1ŒÔöޏ,É#ýãðcËöÂE7oÈ_¿dC«‘Öæpð­òÄcqB‡±&÷z¯Œ”êß÷ü6Z­ÉøÀ²dŸ á‡Ø“P{é 0±vŒÉ…£Ž‡ ]×srR­à õ·Œ¼ûE}JËÉÙûÚÏ;2RªŸ…Ëh‡;€,o_ ²Ñ³„=ûw€Â…7ôÚý@–s¥ƒ!¸¦œzyúðªöC…RûMÌ[aËÈ$$_–kcåˆÝBõM1üˆõf¡y«@ÔŠ“Ró±c=huQg¤êªÃ ÜQÉ?xT¿TÏ`5á7,—g†]úÌðÀIy}€|¦ŠÖ‹f £í#PéŒ3#ßõi‡mûò§òNèy¶…pkGˆqWædö1-ô¹uCNÖ÷¨á–å@Jõ·ZÚžŠBrºm(·½³B6l3[iyXèæ¦Ã߯\*|pE‡ Ó2Vý,‹¬-fd­\ Ï G2ïörTJòJõkµù;l7iä• -*µ]Y³*ž×tÏC2´CW¼ª¾Cø¡m$˳ÃÈåÊñPôd'¥öïøf„mMw YËp`¸0fþæ¤;‹‘Êñ`Õ †“Róûlð—’vñ%²¯Ú;åä<ªÆrÃÒKHÞÞ–G-*V¥VËßo˜ dv›ööÜPáDZMØ\+CaÈËÖ`ÒHŸ‘ú62·vœÌ¹p†ëM:ÍHƒé ì¯ Õš»Cb›HNJõz–Í#š iÓñ_âö’“]ƒx™u…°ú̹ZäxiTÛvÝïËÉ#ǘ|E ýûªƒõìËÂ*Ùwùí†Â# wÂÎ’ ¡Tÿ§Çws­Ä…œlQ&ãEüA!ùæJ>ûø!ˆ‘N§;„ÚwÝx‚Ž7’Ï{;aì‘ÂÕí°Euœlœé…ƒ¼„Rýj[VB£’×2AÓØ¥B“FŸ¹¶ÈÈ«µ¹Ý/oFÐôÃ1œ¼ŸÐ’{é½a¤ÔþÁc¦¢×#$_LÃ×[Û Ë[Îر!B¿ÐífsJNJÍ¿ðñ5<éÆÈ–_>AB˜’ð¨Ît@÷Ÿ!Hê¶\‚±¹Ë„ãV?g¾»Ò99XeKÌk¥-:¤w ߇eGKdä—¹'Ayâ! mZ¾¾æ #m+>Ãþ„aœÜ· `W$¥ú…UadiÐfÕØChva7x·uæd÷œ`òc$’ai ¸áÌ`NzöC›È¶@öî8ÃÞ¤ •s¦‚Ù‘”ê÷¤8jÖù!¹£5€W˼¨íÌl³‚Ù¯_kh×»¡pÕCu™ÄI—wZ²M׃”|×Äióþ='7_v@»ZúÅßå.vj@.<ÇŠv§1Rj~R}"ÌïvÉ(³ÃÌÛ?]ø?UÅWëçëÑ8ËIÅ< ÷^ñd’Šù7óí¸ÇÐHÅücÍiXµ“й÷£|vããUF*æRý« K™’ó6F*æÔýàÅ©$sÊQø×Ôh ó—µÓ!>ñT!ù»ý®~ÊFÀ$ó‰I6p×O‹“йá×<½©-’¿»¿K߉Ø`u0'óÈ¢ÖÐ4á='w¾ß7žé5Éß½ÿ__V —5üÏ%ÿ͸ð+ ©:ÈNÁÏ–B÷9æË*„ÝŸ)Aûü+Â}Ý«!S¹Œ“†£J@cÝN$Õ7 ‚%Ö—„òãùФ‹Â6>üëÖ“@š?üÉ^¡T«Î› ëàq Ãb`sQ¬Ðwf6\;xIËÍ3@Ëé‚ðñ.Þßì¬p™–5êl êwÔ„ µJ GÃ¥ÙŸ)Õ/³G/Š<€¤Ñ™˜T-ôv­º„ÉÉó­àöí¥ŒŒ©y G?zªWÁ§YHJíËCÔs<>Äf=>!¬Üx|Øn -Óæ²I™iB©ù­R0qò%NR÷ÿë©*ué–·o û`' ›ßüp¡Š‘ÛÇ.‡´ž@Þ»ž3Öï>{4¶o8dý·‘²÷(´k=ŸD®~o9+Â.ÉÉ%Ó½ Ë—mœÔ¸gÄÛ*e3Rª›¤4ø ¾@N~’“Z -fz2§Eçl?#”§A‚Pn9-‹9'g^o‚y¿ïu/ƒ“ß½šrµ”ê÷1c1NS÷A²íÞÔ4’ 'uÜŽ‹&9jž%†] jjÖ±žñ_û*9œ.Ti2“Rûm¾ÏƒûÎùHf.-ד?O5‹Þ@ž·S‡FN»÷™O7^ä+÷_ÜÐs'‡š5ᆓKùñ©šY [u^KÔ©ýôÔ…\‘RýÍkra’– !9™°ýõF6Çô—HzýÁóùN*ÛË‚Í/0òøÇ¶tßT¸`i8D}O²ÝžŸ°ÄÎ_FJõK_zõºÅɯ‡ÓÐmÖ99bý1n‘ ä*ÛT™©î?>Q›/1òþÝë×>—“Rû~ö‡gC ôSj:ûOþ£å î•ÀÉMm0pV#¥æWí ÎÛ0ruP8;bù†“ð¨fš([”mêÁÉcã~Â&õaƒ%» ¬¢œT¾`¦Z¾œ´ÛX[h1:#gt]c¡õhˆýõ•“ÃK/ó•õ´ •‘RýøóDìÙk##4 Å÷fÍìÓñ[&¼ýÅü>Ú ÷DW*BRv?„ÊÎJí÷YøµÌÊEržfᲄ#Êâ4ž䞌'ìÕçB©ù_¦üäÙ/Nž2 DG[5FþÁ£¬ö6ÌPE²D­¢^?ùˆ•ü礷îY;uaŸÐ37ý·¬¼-HöÒSÂë® Â¤»Q+qÐ¥´ÒÜb¤«s1¬l±I©þ:Ñ‹àóãEœL]5<ü¢„«›¥M!2Ò­[¬9.社•µììÆ$ýõÄÜ–»…M—FÀ=·þ@>÷’GµERªßÝ‘®Ø{ç( OnÓÀ“Ó<„ßžÙBd§:F¦ô1µWd丑êèQ2—“ëW–3·Æ)µ¿bS8k¦vÉ`›±Ls]²ðãý4˜ñ~²O€ÏƒÅŒ”šŸÔ{ºŽö2Òc&7<'üƒGµñuˆúæ€dj~d¼ñúMíëúwbdpÂÞ>± {ö\cÊWÿþ[ÿõ;ÍësåÂW÷qÏõÑŒ¼9Ö§4wrYä_òÑQ[‘ ¨tdËöû2RªdÁ[–8&š“J¶°‹?– áÅfè6ÉÑž gÆa¬r4fœ2jõLÊáÇ É9ŸúÎ*DÒ¯é^~èá;NJþU:ƧE­RmC±yÇÒÅÂéÓ•Á¥ÑhN†›3½þyˆ—¾pg~w$}\†²ç¡r¡ä»æÎ—2ŸkqHÚuè%{`%<1Æj^$q²zy^¡Rý›BRj~ÖÅ~àú, È*­p°°2þGUñ¥ž§óý‘T̵ t¹{è" ósZ›ÐÝf9’Šy܉Öìœí ó™³Ò@Ï¢’йT£>›d™õoä¤bl ÁÜmÊTÌsCýÐí—Šù® û:‚‘¿Ûϯs x7HÅünº ?Ÿ’“Šy]‘šš/Fòw÷[¨^/ô>Œ¤b®þJ^øs¿;ß;Ú \úDÊÉß½ÿ__V%˜5øÏ%ÿMwOÔŸå€äÕ^Ûpbæ@aÔ’l5°‚‘ÆúcTæX ‹Ž¼…Ô« 9©Ùè/¸=ß Éy3ïÁŒô¥@šZ|€z‡û2ò§ý8?ý›œÜ¸Ö ¢n )Õô—t86&Éù vÀa{„»Ø*Ì´ÈAÇ”°æÊáÝ{ÑnyA!Yô×<\¤Ñȵw&¢±æRa‹^_YZš¥PªŸIx(.èæËICy,:æ•‘#6ǽ}âü:Ã?ŸõÚÄ%áÚÎ{8Y²Í<ž¤Ôþ¶‹ÎÁ¼Á€´˜QÍÊÜ„?jAéÄ‹ò–KôZ¬¤ÔüÁ—+À³‰/’Îþ“Á~WºPêþ=U¥.]Û7 gj6Cò0îÇ®; 8¹æ¥ZToÒ¥$ˆGÎÌÙ *o#Yçó„}[uT8(ªÌúk £Œáûë‘BïC™²«·2²äG6ÜÏSRªÿíø —‹de{-€Å»…/öʆÿ< ¤õ¶ö :8A8qM:ÿi7UÈÔÖ±tKáw­ ¶ÿi'“&ÏÇ­ý„Rý¾:Ä£ŸF>#—«Dã@vkƒO3š Y …†]jäd­s ›zÏÈ\åV`¸q;'¥öûi^Ýðq L¸:W£&§²M"é£c*»¿m#¥æçå„ÊÒ»nF2ɱ ߸8Š“ð¨Î8ˆÎoÌ8¹q] =q7#+Û³¬Ã!@z,þÊ‚ÏVx(Ÿç¾Éø‹®ü¬i'-ì•ØLÿÇe;‹ù®çh¾ÕI£O»e~e…RýÝö_`—… ùyŽ#ûåã-<ú~>$j;i3ÀÞ Ì`dNw —¸F(+PËØ#œœÚ×DÙèïÓ Ò…RýJü7ãGÏ@^´ƒ/Sv uøàŠŒìbŒõZYn÷Œ;ˆäz£½è²n#¥öqä¥,ýû·úbçoŒ¬‹m&K€l¸ï$\{9O(5U«¾‡‘:Þ~`ë–.üƒGµÅƒط­%#¯.ÛfÏ …5uÁç½:Å2èšgÃȼ隬þýrᤉc¡Îù°Ó·ð´x&jí8|_c)¬°ì.š"#SB=Ï“Rý2vÈ ÓÆ"ÿ*µ°.²½Ðãõ  *’“õŽþ,\žÄɧ~}ÙôÕ£‘4Ø}‡WÍîi6 Œ~.‘ÆGâ¸~Š '¥úõWSÂSò MOOôφؤsO#až—•,a›¡ðÈªÎøuÐAáĸÎ0Ó$F(µ¿úÜuPŽ>ËÈùnWágv¤Ðèø¨h |Üý6LÚ<I©ù÷jÊ@uÈZN¾t’ÃAHþÁ£šQ´ ?º5²ij zv4mîjJšœ ?šÎÆÇ_Îú”™ 䤗J”xÉ,Uý™Î'd¤Óü'ÐÎ 4léŸ N Ä¡C· ¥ú»Ü›&\w’“Õp¿Ðv~k¡ÎcOîs~›°A³¿‹6v”‘®%kñÑ5$«Å£ÛÃÁ2rÞre4h¿È1¶³àF÷sB©~µ¾¿dî1ÿï?ÀóÿYy£‚=IÞ-ô÷:ÅNÏPZyv‚žÛÜyÊ{:-j"ŒùpY¾±E'¥öT”ƒ±ë(F.ìt´5Êe䥿ÿÜ?.ARuåY‡Õ–B©ù5)ÖPøè<’îòäF' „ð¨i/Ç”å¬ù0ƒ·x­ßµ”ÏÎlŒ¤ùÓ;<¯Ï;N®ö‰€Gæ™Hf¨e^ÅW…{û†€Wø~aÔ_ûXÆöPaÒ²‰|mz$#sš-­¿Š8)Õ_ûœ«,²ãv©a4œÙ}ßÀÈ¿šÁùZ-ìµ¢'®$4H ÅÚPSapk]4Z³G¸lÊVÄ1rÃ׳lìö㜔ꧦÑf/ðÒýþXi!”ÂÛ«Ÿäd¼AG(êø–“F'¡»V -º™qƒšb¡Ô~Íñ% ”geN:¯—ƒFóp9©={¦¬E3_éyF·U”š¿ÇE3m\3ë¯nÕCNþGUñe÷EÏŒöR1ï8 ëmý9©˜Ù„ðÌw9Hþÿ|«ëz|çB…×wÏùrëž'TÌ¥úwÊgýþ*e¤b ¾Š[©˜kî7ë5äŠy‹ˆ^<ãâ ·ŸúR'®öž‘Šylé öëë$ó×÷aÉ whþqèÒYƒ“Šùýʈ4sdäïÎk¶äÿýÿ^ù»÷ÿëË*¯éßÿóãý7«Àíh?$þ^fï¦ýcq‹¹~H‡Õ2H–— ¿ßQE½uÛ…EÞšñ9¥ÂêôŽØrÖ$Íw†¢Öñ^ÂÁyPz3 H—ø°ie.'¥úg¾Êã ¶üýáãÿ<¬õA®Þå¨Ð/;g-2øÇ'Ð}L˜Ðü𠙇Çy$'õÌÝcc…Ö?íÀ´Å »_; !G)ÕïrøÒy¹ª 8®¯Ð·Å"T:°Jx¢Ÿ>ö³< ì°¿ZÕ‡ ù¶éq¶¡e©Pj©Í<ùää2÷þ²”.ÿ8äV ðQ*Eòb3O>É0D(5ÿºW¾ rfä['ì!_¤ÔýÿzªJ]úH£ ¼]|‘Tý˜Í>E³—ƒšV6úéàè½Aø´pd|*ÞNI†yòÂ6öÛ02¤#í†|çaI@®?™Ï6~ÖFR'| ¸ÚH©þÞ¦ïeqAé œ|”¹uJŽðLæWy¦°¶û ¶{B†ÐûY_L¸­‡dF^ü,'ÖÜbõòœôéÆí¾tRª_âÚ:m{‹‘‘–_¡dXcáOØÍûšiUE6"ù´P÷ÓcÞÃ`’ÍÍ£3Rjÿ\5æøIdFà iB^ÀÛ ä¤Û€ÙÌ+f7#¥æG.K“_ì¾ HÛWmaRµ•ðU÷Wiàf’„¤ë²˜wð˜pÁØ\–ÒÈÆ¾‡áóF?)€y/…Wî‚vŸšq2n×:¶«¿&¦2CXZaNnq¿ ;JÚÙ©íè4v’Rým㛂Ýê0 Ù±2c¥°p§>X… [„Ì•-º uDÉ#§ìb¤wÛŸìê£Há^csÈ8Z¤ÞÊÇ?D](Õ¯Wûoà™fÂÉÈ;õpÓñÝ?Æ~eYeÇtlc ?D ½öm} Ùþu”u @Rj³iì£YoƒªØùãÙÂ;•¾PÑW…‘¬$®è!)5_m³ Øí2”‘W/¬d/ר ùê[e?èW[€äÖ¿?S½É8-lý-ì^ÉI? ðžÇÉÐòÖÈ…ñ;ŽÞ­i*dy Auv_$[_nÉŸðêf¨³Û2Gáý®œÙßyÎH©þ3އé=,œ?Ä œ› C.:Be|sF®7o F76qò«ÆrH}¸MNª¬ÜÓz ™ ^'Û ã'Ƙ›ž#'¥úò¾ Ý1GRý8BkåÂõK}@+|#¶BäÏŒ ;Û– ¸q I÷1ã°ÃÙÙBÉ¿*ë•á` -<;Âûˆ¡Á…¹°$:É¢wf…×mÎ ¥æûPÆuã!YÔÎcr×ËÈ?xTã§ö‡¾WÎ éÕPûG»n·˜ÓÒcœ\–!óiì&,-ßÅ]Õ3²þy‰¼8%HÕFs°Æ$_ú$â·„ÓròÝÕÊ¿ÿØ,Ìɵ¯Ãás6’Rýý·9AfE"#µ/ÛBÒ¬2²Èà´¬Lû:'_ÈÏðœÑû„9;'CãŒ$m c.-O ¯iªBÞæm@â¨'àÑIW(ÕïIûXPÙ†d¨õR(LFÌX;3²|…9›krò•ÅO^Ñv<#·oh ià#”Ú¿ö³lìd£¿ÌÁâT´P7Áó“×#yücÔ¶¿''¥æç/_å0ÈÑó¸EÇÂ?xTo®Hg·ß!9êI?öVû¤Ðª¿5Wj­+#µ+S¹nîyF~û¥×Ÿ²2y7LÚž!¬s À²8#aëQ'¹VÌvás=ntn!’ÅÝCY`÷DFJõùuDÎÍ‘“µE-á³ÞNþüûTUìŸ(#o» öýV@^jÇÕ;æ#9h–úú =TËaÈV3aQ“.”êw~BO8’„dÏ3¬Cö*á’âLu`' WÈœ¦¦ Ó6¹BŸ†HzwÙ[Èâr…’ïšMÇÃðä #ÆÌ€i÷ü„a[/Ü6æŒôv¶€ŠÙ2RjþÃ'ÏØÚÌL SãWÃǾÖÂÿá¨*¾ŠZ$›{¨d ©˜Û—ìåÁÖ@*æ]õnƒúfG óO¿8«<2HÅ|د°J¹ÆIÅ\ªò×Bv±{>'ó&«‡aiê6 s—'~8»ùN*æ9Ì…-ˆ¬eäïö³²2a F!©˜w«áï_Nâ¤b®ãåm×LBòw÷_Û<üôR17O_i/#ù»ó›­p†e!]eäïÞÿ¯/«2~|úÏ%ÿͬÙ˱üp c*=°Pg€0Ù<K–ø0²£m0öŽîd³~JX1-GFvTÕÁ£í‡iU~6éaäÙ=áµa2'Ï[X€ŸaFúÉÇ냤Tÿ»1upú}.#ïÜx—7§Ü»Fë„Û½‚m_VprI‡™ø3d)’—ü£Q×þ'3­ p_×ÙÂÙãqa ¥úë•‚Q†œô(½!é»9i…KqòËŽH.ØŽŽwrÒÕê!³Z"'ïþÐÀi7)µÿów_,™=É*M¼ªóÂÑtÎ-sòåöuxóÕL ¥æ—Ž>WÎuCr…YŒÛ‘"”ºÿ_OU©Kë’fãÂMÓ€Œjc‡ nV‡ÍC‹k…¶…±sÜ>aJÏÃ<Æ+YX;l{w^è1þ DýýFúªþýd+ \ …Í„¯FâÏ[)Õß#¹Ö?-“Ë—ßߎQœÔØR ßÌ‘l<1T6… Íãv¡ÛìþŒ|»}2&]ëd»ê*V~夰G]¨t,b¤T¿Ñ×àtD;$µœ.ÀFõ½ÝcñžcºŒ¼8$ 3Û=gäfµCl±ï Ë?$Àæ›>B©ý-úmE+»æHf Äu‡žpòqÕZ>úÎA ݽšAlÄf¡ÔüŠ9Y‡Içu×ÇÉ„ð¨ÞÙaŠn;œ–ÓÕÏÄ OÎâOsÒ„_å½Yÿs…/ªA‹'„/ÒÁî‡pß3]6÷C<’õ_—ó>“õ„Æ4ƒÙáŒtý¨ nARò©UW ûûÕqr}—L\Ð ÉF^+à{“TaÇä§,fÃ~á´s¡üþý¹íÿü´2ˆÅ¿,T¾øQÆ8'ol·Àòñï)Õ/ðÐIð˜¾I›ªýpÕ$Ex~æô°¤æÞAȧn.Y vyLNæµþ!;¼p'¥öõ Äöûó8iÓb+*Õnº.·ƒSîþŒ´ö<ËÒ]á¤Ô|3U,rkƒ¤—îJ®肌üƒG5vÛ{žÈ²4®ó÷‘#õv*AÑ •ÁãÉÂè=G uÁFªY ©.s8iº:_îÒ{ Œ¼:Q lFW2òësK›Šd‹{˰R?„‘Rý‹EA« FHN1\ 2† î‘¥Z!|©Üˆwðh ô|  ]Fìd¤ÛÓÞ°Öz;'?×t’{Ý(²¾4nœ JõS»¾ Ö·>‰d7…$ßSBóYüöæp ƒÍï™… Kí^ò9%­…eÊì…cPj¿æc?\X2„“_s}Ñ®ª@NõºÂgÜþÄIië0°yµŒ”š¯ºú>«=›ÁȾ ÿþ}Às†9ùêÕׯ<¨ŽùYwa¡jþia«#ìЩgäLëñ`|CFÖ¹ä²a~Ùœt²µäÚ&3åd’c Ôú2RësŒ_ÀÉ¥%ÌÄþ3#S·4—•vHJõOô–¼½)’;F¨Â_§õ…1‹ÃåÕCÊIÇ6³ØÈ¯GyòæIÖý2CÒÅ€w])Ô±]ŽÚrÒŠmâv/ËI©~©­ªÙågˆdÌž¾ìóÊá±ǘsµ@ê«j€æë!ÂQ‘ûài\²PMý ŒÛ½™‘Rû»´]‡Es« H»KËPumŒŒlÇæ¢Ž?ÏŽæàzR(ùóé&ÉE#$}båÐ#Â?xTÇ´Ðd½‹tϦ˜ §héGNÆŽKfW ï­u*y½“*v½œù×Má‰a9°¾Y0’‰c e£báÕ½˜l´L¨ÿÎ;Êê)Õ?¿$“5‰k‰dضá¬ÅÌ NæÝׇ ƒ¹Öy'|Þÿ˜‘y̰UÙd$Çn݉Ê8Yñø8Ï:¶HË&®Ð° ¥úÅ7Z/¯ÒŽG2Çbßë' 6ŠæŸedÍ£! »¾«ŒÒ\óNTqò½_Þ¢h»Œ”Ú¿ Ç ÛõèÌÈá5¨Ûd®Ðwª6dDì2íûzÐ\«,”šÿ~«Œ ™uÉœa ðŠ}œð8ªŠ/óEõL³i>ŠùЮäWó•‘TÌó&¦³ÞÉE@*æÕyýdpIż v7Kù4HÅ\ªžI„,[c!'ó®óNC‡ëêŒTÌ_ÌŠÅïm©˜,BX3NÎÈßíW–ÉÕÜ­‘TÌë\ÛÁ'aœT̵-" ý&íòw÷{®¦É‘ŒTÌ_Îî w§qòwç?¨Ž77Bòwïÿ×—UƼûÏ%ÿÍàæVPÞ<ÉqÛõ D5IhPw‚ï)ðÒ\õ£üô CBŸÂï,%÷0#mMœÁàqkNOé Q)±Âœ´™ßcs$ýOt‚þõÙÂꤵ\=ð'¥ú[>Ž@ïKeœT9ŽŽŸÙy#úõ+4ÿ+‚6ç¤ryìi»I­Ï¡°Ïv'Õ|­äE { ¥úÍ|ÂâH¾LVÆÑ^:µCÛa©¦&ug fÌráÙ¬<^?£’‘‡ËµAUs±Pj¿Ñ·º¦IÃ-aÿGËä‹\½›)šœãÅ5«”šo›8™Y¶j‰dß¿øí7ªB©ûÿõT•ºtð°Fð*$Éu·O±=޹Âfï'±èdZ:´ÿZ,ì9¦L³@Ò1ê{l'Üt¢FEôÆü FMý^œœ0Ö6-ªcäÚù—!æd'¥ú‡ùîB•¨ö2²¯ûl×É›‘Q›*Àçvc =\^Áõ¡•2òzÃ-P’vÉÆßتg„ªNN¼Çy KG,ï3'„Rý<«e¨÷éï_ßþÏ‹%X›™ÄIí¾‰Ý^["d’¿½ê'”ê¿*~¦ÅÝa$»8õÝ;Ùåï#i¸¥ˆ“VK*àô$›-’Ç{çsµò¼>°Qåpf×=F.X6_ªå¤T¿˜&è×줜85 §·ìËHÓŠXöòÁ '.î ºmO ž»âDzÅHÚ̘‹ú³ÊH©ý÷¶E3ÿ£H–>œÎ2;æ çw*„GφùþØÈœ=@NJÍo9²)–írÞ}3ö¸WŠðUIò´¤$­-ó*Ó‡ãp?q.Î3åðfzS¡ÚfÌÚ٠ɺËÐhš9Y;Xîd‡ÑÚ 7Á\8J³”Ù”Ÿeäõ]ŽÔïâ¤Tÿèžý0ë¾ Ö§>ñ¢ì±Âæ±°Ø'É{†CáÌáB%UÜ[ö„“N÷ñ¢‘#dd`áXþÈ@ƒ‘ŽþcXƒ¤D ¥ú›ŠQòŒôéz¡€,ž³ªvg‡«ÇBÏÆ!Âú{ͯUµVõªJ)rRj¿þÁ¦,3*ÉŠÆËd¯M2„§´‡I/pÒ:O•§N9-'¥æoÊd ·f&!“§B#>ŒüƒG5iUw¹zɰ9ÊhP¨Vš ©ý‡2r³Ú°hþ^N¶»¢„‹š¼däÚS×dcš©“ÆÀfèb™—φ7DrØð¾°@w3ŸÇ.Jõ׫æE¯&°ÔO®Òs¢°¼ú 8²Éø -e3®µNi£Ë–™¥1²_cP÷Ý'tSÉÒñ;¼í\‘+Cä¤T¿}³æâàE‹€|Þn 6Ý,\t>BÆÙ— ×0²Õ„ÆPã°Éí'¿ó½Y±B©ýE§ (¥"éÝ=IžÓ0A˜eYÈÕŽéi±hœlañ!¡äϧz‹9æŸãdxó î½D É?xT+ê´±æ¯$55Ͱ¦n›0µ‹;Äè&q2þp+êz^øèK7¨Ÿ·‘Õí§B^ÔNŽkÇí t÷^€þÕû9Y²KÖ¬4”‘c¦$ƒÕ‚WrRú³X v7ÐÈýÛ’˜å=}¡Å4=yÊŠër2fá+Y†F#ÏÖìŸà’BÒôD*xÊ›#yàáY9éÓ“ßÚ©¤T¿7MŸñà”½@n5’+ßOv™Ÿ.±÷d¤º~¸•js2äè|ó䌰üC7ä¶M€”Ú¯êÚ†¿Iµ‡c¹w]˜P?b*D:©ý²BjW ¥æw”MðY'ådæÔõøŒwò8ªŠ¯¯Ì»ŒðDR1÷ÌžÌæÅâ¤b^ñ©Dx ©˜™Š­6æ3R1w|â¦+³TÌ¥ú/Ø÷ž™.o¤bÞÄºŽµÛHżëÒ(脤b¾·Ò©¥ù»ýÜDÈ>‰R1çÐoßAN*æ‡f”2eŸù»û¯knä~Ÿ¶"©˜¿V ¦C ù»ó=gu€m±@þîýÿú²j QÝ.ùo愇óÙ‡ùyÒ:Þ<ßAèm̲g« éç¼TþY©“pÖgK¼óÖM¸ö®n”ÄIUÙ[yÀÖt$Û~áÊ³Ç §¬qç¼ÅD G} >µÌI©þ1S[sûM‡€ÌL|e®çœ+´ m c~4šß2y7+eä$ÿ,\¿ çûö†ÔĉŒ|º‚1­¿8ù>Ë -ž†ÊI©~V¯±§‡Îp²ld@aÉãµլIñEáÝÕM™û¡FH*EëòÚ«6@ºŽ×„*Ç„RûóÊ* H<×sn€›™°5adzR‘TÓÈýÚZ ¥æŸp’¿\Šdãª&¸Öï'¥îÿ×SUêR¿æ|Þ™Õ@>mpSÕh»ðúÐ Üua+$Ÿç DÓž9itº-ÂHíù:æ¶Ê7„Ei×y?ÏNœüž²EöÑq/#¯o’ùU Ùöc:mj-”ê¯âèÄtçp _4»Ê´ŒO Ÿ¿é ¹ßŽprµñÖï¦)’A‰S¡mI8'5 eÐc™ ’í²'ûû¹Ü?‡ií‰JÕÍëym¡?'µSþâ¦?jää(ƒQÜU‘\ëп¾ÏÉvÿëvÕÓÛý?É’$„¤H¨Tè«Ï¹v¦$¡ŒI*SBßdÈ)•f•TIRÉPH¥>çºB%™I¦Ìs2žû¹Ÿg_÷w}~ë^gY·óÏë}®½ßew>ÃZäÐÂp# ÇLOCH°ª€JÍÏ»u 4nm|”³ºp#v¨QÞj"÷õub¾ôA¥ú;.¡:S~ è¨#$)ø$AÿખF´—gü¨·Ï%¡±j/7lš'ë§Ó…¢{÷²[ïÔÔ¨ |‹Jä–ü½Ô§5£hÌÒ¿Àîí!‚vþ¶Je‹èé[æ,P» Óº¶„ò¨Û•üU)÷€¤#P¥+¶_ ¸W[¤ «7ø0tÓ­ôÙ½ÍÜA]N“—yñܶ¿u㲸¯½fƒ}ð"‚vèÛJäÖ‘¡Rùjîj1ç=¹g«}»‹ cwŒfžÚ+(ÚÏÓ›e¿ÖÐ(³áà;ñEó"TéýòrTj¾yÞ=Øÿ¡ Of<€I•· z«Gø´½È]äy L›¦¨Tÿh‹@xU¢KP½‡¡jÅMŠþÁU5Lr'¾ªú¶€¤¯>Æ5ÝÊ26ãΘáɾuŸÂ=½'ÂÍzóøx87=™»På(tÞfÊu óät®UR è·x ×+ÁŒinßÌ•Ê/›±¢\s•_Ø­W'r•]Ʋߦ3ôà‚`æ;°Ù,íÁ<^ïàŽÑZÎÊ3FsU?­d~LYŽÆ\0c/‡*•/ÜgS}ÑзçMYÚl;n€Ýæþ3Ÿ G­aÞ1ƒ]+ëBuZ¬â^zlÉ׎s¥æoÒ©‡xrŒ «ÚÕCdd·;O6¹3´«÷Jèt*‡+Õ¿:+ºÆ‡2ÔÇÐ : sÿàªjåÿ óÜ ñC†'qg½6eÊڡܳû«(½’ÂmxF¦–”0ÔwF<Õì˜Ëm•øžl^Ê­;Óš&ŒÜËU*`úJ2‚ÚÜ,´Yf¨T~› )p÷[  S"3AçÀl®[H,Û¹"]D› f3»!¨ãÛ-컽?E§éMbÎV®=?ò}a; {7O­â(•ʧk܃™ôÅÎtéðî÷÷ÌþwLÇ.Lö žû͘yã"®gØq¸ïo+¢Ró¯v}ùíÇÔzÜ=˜QùT@ƺ“Ú%‰ Ý0Ђ¾èÛ™+Õÿœa?Aæ‘ÏК¡W¨ùxOî\U ÕqÛ®ÐïC–€Æé£ÜîÆâÈ”=Ü©‚ÈéÊxî´¡CXŠË*†ZeñkNS´ ëúÁÄœ¡«tÓ©U²ˆ6Í =çVStØÈQ´Kx"C¥òûF€é] s´¯½!è¼½c˜‹í`@µä…tuß™\›Œ>wVæ¾5É·½­¸.4ˆ~3µjº(€Ew_$p%òÕo ¦¦å’Ù[Ô¶+áÙl§bN&·XCWPZœÍù£5L›øˆ¢5þ¾ô[F‚JÍWM­›gú©ç-°ï)¢VÖr¿+= ªRÑÍÛ*Õÿcþmº ƒ?E•~t°ôßDÐÿaU¯Ý·‚ìÎ@ëÓÚ‚­ïz@ëŸÖ*±º±*U¬ç‡Ú“7iGªXÏüξO)ªX—ʯ{x- '¨býôµ,¡k® Šõ˜ÝÁ=$œ Šõ¨5zìÝšm€þn¾ƒíI¤-T±^²ï(yq'PÅzñwE8 ¿;ßµæ*Ü?]/¢Šu¿®‡@ôÃÆÞ¬k‰& —òªé†ÈXnÃUt‡Qô…¼1w;C¥ò/ó˜uª[üa(˜}Œç­Ña.?mê½€ö?Ÿ{w±1ýä›ÊÐk>µôÔk{®Û«rèëèõá5@çST*ßÕ™³©v›xõ˜p@Ô­LP%s0éÓ•¡j«Èé)Ê\'ervÅ.@{7ÙÀËn\©ù!ÓÆ‹ òÍsÛ/{Ô¥« !œpÞÆÐS†ýØÐ£›¹Rý-Ï›±‹ ]¹î=x1™+uþOUÉ_¥wr®»CmfæË²bœ¹5®‚Úš ŠÚ3¤~Ócä¨WÖ3xt …ˆÖ|«ãi­ZÿS˜Ú«ÐâÃA³û®of+ξDÑò+¯èƒq]•ÊŸ£¥+†ïcè Û$|d.7$ЊèNhcD 8éÚŠ»Àà.í~9ƒ¢E»z¿|. uëû@Ÿ}Z:ì«Øh=Œ Rù´òä‚Kz,A+“ȩ˹jµíHMcEuV{ŠZWrDôûMØ;w/EoºÆÒs¿ô*5ŸÜ‘ þ³Nñ΀ܽVÌõmÄ”íë)úp®'K™âMP©þkË{õ‡E€Æ›àµ‹ûWuˆù#ñg¿ù =²ºžXÀ5Ô§-bj3h7íਠèâæ  ¦>“¡ZûBÏ›3¸'ïG€±®3 g"`—Ár‚Ʀ †)5㸊ÛÃË?†J®êWy(Ÿ¡.…ê±Ç¹±7‡BÈu‚.;öœ¡(û±‘øR‘ Ë­ CB)÷Ñó|bÓ´ÐÉGáÎÍ®T¾>ù­¡£Ú[‚š_ v¸«RõéÌpg®N€m]Ô PyžS»#¢ÛW¹±)5•šyÈ*âRhĵݤxÙtž˜GÏEöáfM9A[(s¥úÛ5†GACµ \ÑS|ô®jÙó2êk=—¡×N+³QO¹Þ\iÿ¨¥€¦]}$OßÏÎ0#.-ªó©§Xó&¦m:` +¼):~UŠP°µš;ýA76Ò3’¡KÇÎeJž/G Rù—^ïL5ZahÊà4*×ÉæÚ§µÍŸqÛšÌ"I¯à®›7[tÑ€"ÐÍÚIÑçû*@©[ö´¸y_x}¢C¥òíÚâKû\%hËñÉpñI2×m\ˆ¸*g* _|? ÜB¹ÅÅ}Åø[‘ܺ#¨ÙÓ–+5ßÑó Q(Ô$ù¹u·ˆëí2ZÛ!¢“ʇBBŸñ •ÜÕ—ÄÓN¤hÞâQtKë*î\Õýst™kå†Êbõ5‚½rɾ{Ð[?Ú‚ñãÜèEQÂ;;Uî§Ã¯ÈÊ˹BÕOjãÚŽ¢Ï&[³§7~riÞ¹‰ÛËm9\ÏèB8?Ó†§½J$pÏ=ŸFjFtZ•& ÕHT*ßà¤#ûE“ Æše`1DôEj)9®µÐ)¹áîlnDK 0už¢k®л'2TrU£[Á­%€Ú¸tsóc\¶Ä9,•¡£RZ0»5±\©þ“ÍY^cˆ=ÑÞ‰íúÖÐ?¸ªN³ØÕÃz UYáÍäáí¸§:ÁëÄ<@—FC¿3©\ãO›Aeb·«ƒ ÖÓ¹ÆSû²ó?·p=ê{Ó>yܾW‡“ñSs*;aÇžý°æJå7O1f«Ì=Z»ظ×ÀÕ]³„i1çÞ( cùr(êû¯z“ãǪô:‚ì·<Ì-b ö0ÐA‹Œ‘ÙT*_𼼝Sôü÷ àÛãW3r-˜õÙè§^»Á£1–k»ÎŸmÕN§èÚªµLùu'@¥æwH0„iV¹€Nìm /âr¸-W°£K{3ôæ%'fÞÆO@¥ú—vVg—ÇwÊ–¡Å¹ÜÿaU/«-ìlÕ]Š*Ö'<Ï&—Í€*Öµ–ÔCÿäR‚*Ög8=&ùú¤Qûi„«pIå?ä`Íl;wb¨b}â¦ö¡4N@ë~Ë^ŠS'„2T±~ùŽ6FÜ£èïæ{tâ$u¶`¨b]Ý«füX ¨býL»ô¯5€þîü›×m¡N¶PÅú—ÃñÔGh"èïöŸÿV~ˆôwÏÿãË*Ûfÿ}ËÓ¯cKqê{­)2,%C ›wcÆþ[Ýy2—ê«Çr=æ„Òµ»ehëí± Õ+¢õÏ~Q§aá Ý‘fÌsÏ®ÞE-6¯4Ö«¤ô©$¨Tþò¹äõÜŸ 0”Øšwbh^Ï ±Ï7@Ó¬HQy,שd(%XŸ@‹ÛlƒÁ¯ïQtÏpm:Oמ¡õµ›˜eð¿~®ÿ_©|sÎ~¤&Õëú!¤=[ûy·ãÔÁðõQ@½4ädŒe÷XÛøîm@QýOK!V»C¥æßeÊlè†æ Vg°‰«)ÖÒËD@7Û‘øÙ\©þÙ“Gˆvƒ3úüJ-¿?•+uþOU©[kT»ŠnÕ Zjæ.ïù6Œk´ÂWtxh¿©®dÿ6îðúþð¹ýŠ6\R')Ã-ï¥Âì½îÉQ·N¦â“ùû ºøÜ]RÞ‚0ÔkG_£ïΕüSSimi6ºCw%˜Që.=¹ÃÝ[Á¨š,@—º/€6™Üè‚­à>p>CO´öõ»¸âf±ïFê€Fd=¦ßË•Ê÷éngf>I¡ÅsÚ³¿ )ªéÒ‚6¤zÙßø:eBæädŠš7¦^Ú*¹ :¬fÂj†Z¼7b—ßyr/O4‡%G|ÕH™U¹•ê_ÿBNéú•U2©/µx3Aÿદ©TÉí­ è9ûtaI`s@¯å¼&g.ç¦ö¯bsîªËÔ1v¼€fä£Þ<ª#¼)$hó·!PÓÕR@¼üØ—Ê9\í¿.ˆòp}@¥ò÷½s”fÒdèŠàæìµSknõË$; hW£xR6ƒ[qó¹Û“¢³ý ¨›çW:´Ôïš ¨Qg(ýÕ‘Û&'ƒ4+MåìB¹’¯:½d,7pCçºÚ0.3¹ý/ôßíýÒmˆlNŸn •êÿ6½ ä5Þ'¨Á½XÈë¶•ûWÕ"´+ÙÚÐWKIîc ®Ã‘`ny û…±Ðö]µ8hK_¦úÊÉœdM9È´,žÙ|¥hÞ¸xè”ÀС×&CëgÁ"Ú|x#±¯ØÎP©ü÷åXªÇcŠv¦¶Ì×àwÈ! Ñ?´|ÙQXõò¡€îË*¦«ZlcèV¡5› cÊ=XÙ›Õ>ôãz=_Á’W¨RT*Ÿ–¹ õðêwß´dY~n€øÐp®écWPZPr5yÈ ã8%@iv¬U^MQ©ùéSY`-C=®Ía× ÜýuYá¦çMíïÇn:; ¨TÿkcO—³Ó):õoþcúWu×Ú}dڀɀžœTK:|˜Ë­=hÊc)jú©ä;>çÆTéÃEÏ£€Rƒ¹qÜ›GBqþa†&˜¹‘ròÏmìÈF…Fp'Z0yv¨ˆJåw_¼M5¢ù×ײƒm׈¨‰á:4š¢Cë'€æ+Æ}Úí½¾Ù—«g4‡:®T#è…%ŽÔ»U @O!ó›T*ßë³H`%hæÓ{Äo`·Í$c°èçIQûÞ÷Èn£JîÃydŠÈ]rþ&­ˆñ!¨ÔüÕÏÝY‘¯1C]ôbÓÎëp׆.a#^õtd§:À•êï•°ÚüŒg¨ß«® b–Îýƒ«:bg8ýÑЀþ ë¹^·cÈÈþ Mû•ay´×X ˆÝ/´Eà.X‘¶‚ nÎÓi²?C#¡†_RÔëF*yðé2A›o‡ÁÏST*…±?«j,Ðטá¡@‚ΞV]¤¨’ÆKm¥Hnû¡ö¤èY AG2‚â['¹!¢ ü4\ÈÐgÍ¡bTWò½ªÛPí-'¨s¨+ôrŠã>~ÒÜ?S´á¥ÞüÇNm3iyX( môÎu§®Ôüà >L}s'†Î}äËLò›q_Ìw ¢l5 òw`!k$¨Tÿ„g„Rõ ÕÙ‘Ž¹q¢ÿê*^Ás¬ÁÐy  Šu§ñôQ¡-Cë5þ†X2œ¢Šu-4ß²T@ëE' `IÝA†*Ö¥ò›ÛnaéÕ× ªX_­¿“ZÑ¡rT±~×gØÇšT±nhÈ^-ÇÐßÍW}!^õ5!¨b}›þ-š&«¢¨b}Øî`ð,ÞèïÎ?õs³kþ¢ŠõÕYfR8“¢¿ÛßjyЬA÷ü?¾¬ºÿ´îÿ{O÷_Ôj%M-¢†qs ݲ=E§kL‡©"5¸-s»Ç0s/mnR%Öt>€›Ç—lNÂÕ,þ ¾ >Ü€fÁ »þ„¢Jë³ånC&r¥ò—&õÞl!hö½‚s—\ûÆ…ÂÔkg)šg=Bô$\޶Ób_n¥QÔeÙ)j0)P@gÌ; a]g2ÔÓ[º?Ú͕ʷ¹²Ì,? huÕPxµf?×ÿL!œ\ô ÙYUÛÊ[*O—õ»°„¢~kæË+“z*5¿2˦ne¨Þwp9ÎU y.¨¬I'èÊ!–Ÿ¹Rý7ˆ*Ô[· zÝú^²P®Ôùò\&è¾R[XLʸºþ/ÈŠ^Ë]Xa™¿¶r_Íwë¬`‚^ºÑmšäèu+? f&€z>/«P©|ÇFê@ÛÜM€ô=㸠._‚N¦/)Ú¤^ ž:%ñ ´¼”DКŽaP§|€¢Ró«ÞŒvÆ) ÕímcóÒ¸sO@ˈ©ÜfækáܯH®Tÿ/ä8øëý¨MÈxqt¶>Ô&¨Û™^P\MÑ aT¦te(É;IÞœTâÆÖŠôúykІ4aë,­•\Õ+Á©eA‡,ŠÝý˹ùB ØS?@cK `¶Ç4nÂÅCà_^KѰwYà2d:CkÚ‡ÛFf\çNMäö£Ñ\©|ó\R .¥  Cz&ÁžàÓUJʯ§“ºùx0$ô]È•)/'Í•Ê(Ú}V!ÝxÀƒ RóÍó auvC-Ê´aœz6÷ìdzDWß«¤]òdfs®Tÿ”È@ FË)ú*÷#©›~‘ûWÕ1;\fÞ hMðQð«æ:vI€ë³Ê¸_­"ÐQ 3!è‚&W*ÿ€ìp™ªBвŽ9 4h•%ÚwÚÈÚªÞâ T\˜GPí-a½ƒ¡ïŒ€ôwYܼS>⎋‘Zt6Ÿ´>¸P©|{Å@Å+‚jå‚UäK4s–Tžqg¨r[È2í?öØF×ùju\NŒæåq¥æë»t€ÞÏsªaù‹˜ 8Ä-}Ÿ'7Rë@Ðé?óÉE¿€JõU~X,(YKÑêå¯èˆScúWu÷YúÃ’Rôàðjw+“ëÕZì*”º96ÄŸ»OÑSÛG°s;s¹­_ú°û¾Ã4oÚ ±Ã¦•€Î* ÄtnŸ¨'äT×ÇU[Ò‹‘›* •Êßò¯|pòiGQ³Úbè?!†»-ú d»^ÑÚ¡Y :±˜¢[ýôcé Uz3"pG ÷ɱÝ`{ÃP­±U`•½ ’Ÿ.-½ñZÝbi.uá\«½)Ä&ÁˆqK>.Åï(:mݰ{îhqV8Ln³œ Ró? z@|/fh5-#•·r¹j+Áw¹ ¡)’šzs%ßÔ·`Yd€Ú´ ¢òÜÿaU¯a£ÎÑyå!U¬7ü:$\øOQźcÆrÖñ¯OU¬;rñü^@ë‡D_ÖK ªX—ʯwV„N†(ªX?h´ÊÞ5g¨b}Õñ14åR†*Öù®†¥ùO)ú»ù6iË”Š(ún¨]Q2eÊÊU¸®þ c“Š)ú»óÛ÷Ê g­óªXÿ¤]ƒËž èïö'§’õú»çÿñeÕð ëÿ¾å¿¹iž>™h¶¡y“C¸TU€ã>-ø`ï#¯ÔÚ®S¹æÊPç~3Y…¦:×+ÁÔ¡ ·&«| ]Ï}å1ú9j½@ |6TþS毽“ ­|þbyç¹³ÄÁÇ[: Më&sgܵ"¥Úr‚&½ï?ZŸãz·u`w·q£/´ÃÞôT*ßåŽAÔþ¯‡r*›nª%èåçaà LÑ»ún@~†qo›\‡œ¾;D´qÍaøUÙ‹¡Ró»u½KÊgÏôÊš/d’ÚB.ÙOŸíº,¢±¿š‘ÒáY•êjm0h7T èŠülg裡Ôùš+Õ÷ÃLx.L`hCy L¶ÚÎýƒ«ê¸ HœòÕ¡5GTè—* î6MM¸¾¨ˆ¢ú3+É̉zrÃNЯ’ ¹*}ÉÓYO¹³[0ŸùÞ í¤Ãú+sÖvgK4eÍŠÉÊF®T~­¿Ò˜)]uœ:‰Î¼Ô{5¡”Ø Èb¨ßÝÛ†¨DnŠu„i¥èíšãàümCý>8’½UÜsëêi½W*_‰J:u»5 ÐW½×Qå’ ÜÖGî ÛŠ(êy®-ÍíÊ=·¯VlèÜÐOBѻŕšïšª¶®´ Ê šør«f…‡4)Ú²W QíÃP©þF™½àÆü] dGª“‚¹pUóšúÐy#z1Ô ÈŒIVæ®(L¶=š&NNÃ-uéã•S zkáJ°rÒ’£)ýÞR‹«sDÔÊ2I4Ö‹&h›Š90ïôYŠžó½@ÈÃD†Jå7`'ÎqïhÍ‹KBä÷ÎÜèn·Åv£70T»¿½yÙ„+óØ !q¾Ü3VéµíÜž¹u´íÊvmö6‹86TT*ß X5ús| V6ÈÇmLáNv„¦D}Ñ8efw*X@Ë.ÿ IËk™ž«†r%WI}øŒÝh™³=Ôêoæf½Ž³+ÚVÖžüXÕ|U6K­ÝõwßMÑÞäˆþÁU]ðÚ‚’Ô*ŠÞü9„.rßÁý“EóƒÆ2tؾl¡ÞP®ÿÄ(H›w‹¢ÅåapâôL†j“GD·WAýÔWAÆg?îQëJº¨2˜¡»Ç5Ñ 5KŠJå_tÑÔïkh¿ó$~üK‚ŽñÛH3F§¨’FjOSä¨GSèѸ¡)Ñ$1yÚ%Ã3‡î]&㜠•Ê—ûJ™´[²Ðaý¢É޼ýÜsz²1ë£ :JÀ5ÍVô…^<¨Å>•¡=ö+ÃçRŠJþû&»À[e?@[‹žàüd3÷àBžDé3Tm“Žx5±HD%_•ש £R‚æöWÓ6&€þÁUýß‹®ÚoNÑ4ïŸbRFˆˆŽ~l»K†ÊSæ³·¥—)úzÚÐü+Ž¡GZÂülnä–,H™xAD‡%Í5ª µn:J"žV´×¶ (í"C%ÿªg¶“r‚jOÓƒ¤ê@nÑ‘-œÔ0n‡IÇIüÜ4?mÁ{à@†ÞÌ×­§hËøåðji$Cµïb}Å+•ï¢ó}R«»Ð=ºBhù^®ª™.›wewi–*«¶ü›;áG0­*w§¨R¢1k=Tj¾˜ç SÎo´Þ4îÙÀõÐØ*üXØŒ{äÚ3âtéo®TÖ3šªe€Ž:'‡ gZrÿ‡UU¼th–˜öEW@ëµ—V³q“§ST±þì¥IÑØÃPÅúÊ{ ûã †*ÖË{ G†*Ö¥ò/ý(€Þ¾6U¬ŸÉ1Æp @ëV+ãäæW4ªX/mì&‡' èïæ ü< ¯&ªXï v…*_T±n®Ý‚Ím¶Ðß_ý8 Æg¬T±žíºú»ý_ƒOO:ôwÏÿã˪اÿ}˳¬ëyÈ}¹‘ ­õÎÁ€ïýÿãÁBø¾I›¡÷çí…˜·2®¹%|úk€zWXCÎŽ"Šš·RbáM„¡ýæ~¥ÞïvSôjXmý„ ÝÜÀ«t5W*¿ŠY6ENgè£í©`_¼˜›vx¬Žñ'h'K˜t![Dï¨cÃ{34 j ß‘LÑm?ŽÒU+õMM|K*ú×T*_œÊsvû.Aû©jƒÅ.îÚKVb·Nq µ8:Š–ànüÚeŸo% ÷-3,ËÏTjþêÔ-T7yA»Ï¢Ë?—pW6ÒËУϣáoóµ\©þ9ÆÐv~C3R÷Ýoq¥Îÿã©*uk9=£†îЯO‚RÌòb´NÔÏ·g¨éîÙàuc7ó²>LÍýкŒæqÃ×P-%* ¶–dŒaAç„Âç=jfÕ²ƒws¥ò7ÓÙz»×1´qX¸¸ús=véƒ[³K=^ûŽ 1fèÆ÷j¬ôÐ]K4R3–®íéGÐin`¸¢€¢+5@°ˆ`¨T¾Y'‡B‡=‚º$ØUUh Úð$Ž*›ÅГýСCõ¸Ç ôñ$èIË$x3!–¢’ÊSBi³É·:m{õ üÄu9Zúfý-éUù3&Tª¿ŸïwÙo†ÖÜ´5ìçÈÑ?¸ª%'äPw#]D7›@Óžž½­×f.ÏPÛY—ÉØéFÜo74„Cw⸥vÒÀ/Ü´ÛF0 ò"Aõ[l‡M㼸ÙK¯Š¹z몒7˜6Ô¥Š¨Tþ+<`»óV†Ö錇ÝYÿñMÀRò8`×[ÅZ ï²›’"Ô^/#hca#1® \›u%bâˆfÜzC5»q¥ò]œ >­lœ²Š¸Ž#óhÃÛ«Ü”I1ôˆõHw€øf1C‰Ò Ø;5™+5¿µfuÛúæa0ÜV‹ûnA ä ÕZÑ”=ª¹RýÓö¼v7Ô¸ê5™ÜÐ?¸ª[—'ï=½vLŸÃõvq%;tZ04ïåVA-)¢ßηaÒæ04 £Û»¦שê8ÈßQïî"t.lÆP›Y] L¹# U/ ¡ûÛ‚JåŸ=e 5 `肇mÀú×znCu&5ýË—û-O-žê¶t&¨·ò è¥ä(ˆ¥"¢ö(SóCºÓÞö4Ôq¥òå?œ «Ú¶ehÓuGPÚ9”«ýÀ„ªµ½ Cý– —{Å:´Æé¥pä•/CMn7£—ûdSTj~©R -ëÞP¥ghü}3îÁ%BÛViMªiBÇ~*Õ?"ÁZ6ót·ó>8Ÿ;™ûWUym2„_;LÑUAÑP×ë,×Þ¿‹¨Ý)IDÝ4÷‰3 '¨ï¨VìCEWŠnHï§"¨ïâLð5wdhÀó°,x ×D~>·©¦¨ëþ‰pì¹ÀP©ü3fÝ Qã=zäû.¢5“ ·-Ù”vƒ¹×‹&±ø¹·)ªÞy/dŽ.ä>ÉÝ ƒÔ†0ÔoÍqP:à.Có†Â…šA •ʧ3ÍlæÏah”ñðoÚÌ]Û4’¼POR,¢6Ùm½'©•šŸýxx$¿,AGäþãOlÑþý<áÒ—zŠ’{7HaaWª¡Çu¢bÂФ¨ áÝ´x®Ôùò·a½·àl•FPñV ©ÿÚÐvJ&ìjz7îÉïçi†ê(îé“Ù`7W‡ÛZ?Ìõ·ˆ¨T~R°ÎGQ4'q+D¹\àßj VeN"jéÒ›üëãzkF­Lú‹¡zBÍúûE…é ï÷T/  .‰!¨T¾Dzyòî Õî+°û%ß)ê½:W”3U5ÙÕŽöŠÚLЯYà =Aä®ÑO‡“8•šßñæpâþVDwÍ sFSÔñä_¢GÕ$î-VD§'•ê?-zí–ÆÐ¥™•T×ÑûWµl%¯Uw0”$¦X!žëk¤LÈæÖÜ;—KÄÖ];qíê5añ“€¦ìZ Ÿu<¸毗{¥×ñH1ùeÞ‘;ÿó <ú,E{-hÏ´ŸEˆ¨TþDï`¸3¬CŸ Áp½^—;qÅ6اœ þg3²¶ ZíLiÈòLµJ='»[éLЉGÆÁâJ 5È¿A¶‹á\©|}«nN)jÒì/¶µÓF®ý1 Z›ôš 7ÝŸŠ~× uÊ,†åí®PôfŸTP‘Ob¨ÔüçM!ßÌ¢!» Å8’Û\ù]ô©7 ºé´iÕ®Tÿ ßké‚¿RÔ´(”6ŒvÑ?¸ª![V‘Õ:{ƒ1i4MäNºšD—½oÏ}¶³‰’E¿(ê0*´úLž{ûrõÀŽnÕùa —¦Sôhºk×Nг»¦Ð„K‹¹RùM;n…e=¡šü@cæ,nÜî.¬ów[@ WߤÇûzr-~&‘7 Z0_>”w´CPKºÀ!Š¡Æ_.SýݸRùÚVb÷‡üQ¯®Ý™ÕëK%¨RgcQsÉ,@=jV ²Þ~Ü6l8^âÊPg—‰ä݈V\©ùI³ ¡¶:›¢›õaÆ£2®ß¯Éò];b]þæ(Ù½)œ+Õ¿t¶»SFPç_ˆÌà)÷®ª®ÒuÁé}C÷öžGîäþ\ҟݳ»HQ¿MÖìü’(®î™pw HЫY‡Á±Õ]‘j‡X_†NPֹ빱‰¤·´O¶”N§¨T~—É«´YÎГkAr?w¯ª½zÄP-ï\™æ°U\:e<íÚ»´ÿnКׂkÐJ¦t+Г¿:A’ÓG‚J> &4c»k4µc9õHOÐ7óFÿã‘€š ?@.ŸÚɵ2Y/6øU ¨ÚKGÁ4™*5ßVS&Í©£hUŸî°¹ÿG®ý};ÚZqs,ÌjL#¨Tÿ þ^ÐGé9A+Ve‚]ë8î\U€³ò3mãÚzù!ñ×vYlO·6ÕíÆŠ‰(C_oß~»Í(*×™IÓsÿc@ÙÕl'C•ºuè„ÿøô %¡\~tÖ¸®äǪ›ÓÁfc8CcŸŽ„‹®ûlòjš# Ÿ'¿"ñW ¸ßFƒs|>Anž…¸*enþé 0ºžÃm§}uFPT*_ ú+‚ ^TˆÇà¾\ÿ•¤ÜKt…a_Øòz'w|Î+¢±.Š©ú7\ŽåJÍÊê ) -ªwH ÍÕ¹Û2Œà\KŠ–Å#=õd¨T''¡¦sê¦vZmM¥èÿ°ªŠWÛ 5ª_ÁPźoØ,V{a8AÿO£¢cx–r®IÛçÒ¯›ªX×Þú…ªÍzNQźTþæúƒàÇóx†*Ö¯W÷‚ÂÛ— ªXT-‡œÐ—"ªXÈI†ê™: ýÝ|ÙÉ~Bd%¨b}ôTk(ÒT±¾/ öG÷ôwçWZ´€zמ U¬7FDÊSÞ82ôwû÷î|âSÔú»çÿñeUˆaÙÿ÷Bô_ô žNJ/ :,Õ T&<â^>ÜF3ôயä´| 7fÖxbt‡¢–¯3àW{#†.èVBÂV´ºWwÈ=ZOP¥(;˦JGŠ^ 9K‹'4T*¿UÖ`Ù­…(wùrù¯vßDÔÏh6œØ=¡7OèÀ³Ý6Ü.óê|ÏSô/UCx9„¡cÞ½¥á÷†ª¤ùV¬¾Ä•Ê7ó´3˜›š:²Õ&?×”ûñ ‹ãHÐÌ€æìmNw±úXèuC¢ö3'½Ñ¸Róe‡‘6¶O *ŸUAfÕrŸŒØN×~ЦèÅbmv¿ò¹•êï8üm—¶’¢»¯l¥&7ÒTêü?žª’ÿÔ^[ÀtF-AKUáÂú÷×íäè„'CçÌ$ƒèîå×Q0uøl®æ7øÖ÷o®úp èoÛY@óЇÀáÓg(º6Íœ¯ ôê9Fgûs¥ò§Þ—ë I¶DK¯Ü•{·"hLn5i2€¡Žà@6x è÷Í œCûe&‘~†a\¿'‰ßbW@·ií}*•o“o$ì;ÐÇ3RÀoxnªÇq`qŽ  º&ôŠù}î´çhvåEŠZÍJoЍÔükÛï“gªºbÜ'r]r}”[Ú+‘›z¯»eÜ P©þîŸl®§ô«c È ¬æþÁUuè ãE‚._•Ëväp­®Õ9oû0Ôo‰(+šYOÑS-hÑ•¡9nDó— ·>¿5 y\ÛóÛ}°‘ëýZUåmè¬eîQT*¿R+ùƒÏ-}=B¶+¬Œ{ßx´1n-EK?;Š ¾2tæ´=ÂõWQ ÍÞœÖh†pc34 Î¹wéÎ"áö‡•\©|½Ì²aȱN€n,8*š:ȤZærç 7£í¢?û6÷‡Óh¶-d/wi¿jš¿¬+ Ró¿o›¼4æKG8ö&•Ûï£wÆz?±‡lÅ•êßmÎ2hãA½·î?]î\Õ®Z°Ö5Ž uos`Û½õÜ´¢Ãòâ#^½Ÿø\®:ÙWŽúµ¾*Lô¨¡èý$¹üQo7ÝÐ>L?+”¡þf‘ÔÄk÷ˆq1òã|îèìëža\©ü 6‚õôŸõîqN¸`ÞPƒšBñA2AuøŠ›¿vtyB =÷tC=»‰Ô$twÇôëô»É<îð(u6Ç¡Š¢RùwAÝ¡‹í•&GÕ]Ü'çn“°j‘ëîÒ ÄIQÜ”:drG@[1º6•š_ô¨tŒ èý•}áÜÃUÜ­*Éh‹¶€vxg «/õ&¨Tíø"h?÷™ˆ´?ÏUQô®ªÁë<0ÓOPÛæ Ü™«tPÝÒ¿ï$îÚn‘BÐЮ·›®8ûf:×-i¶ðè§ ŸP§Õ#z'îÍs¸KÑi†l\ŸÖÜs]{æ²*•ßoQùhhÍÓ…¤¢¥÷~öYö‰\ͼä~åîçåEôgx%E{ýÚB‹F?Ñë¹3©ê-‚ÎèÞ ®¶8Ï•Ê÷>G«ÖS J³Kàô΋ºäÁxp9Ñ— v{¼À$=P†žYç K5K(Ú¸el±XÀP©ù5»£öd‚¦Ï MoúsoŽ˜ OR)ªC€†ç„¡Rý÷ìÛ Ž¢CW¿ó5÷®jzÿb0ðÏУ"„<û.CKm·…]ݽ“¨Ø7påJòã„'wtáò«2”{eá>ª²­HDïOî+–_›BP9¸Á†Ì0®»V.ìªYOQ©üÍbIa'7@‡[Ÿ"-+½¹§Ó™Û¸¿Z¶„{Gb¸ö¡žâ¹sZµû1´lÊçÞsÙ 1¦2nPÆ xu9•¢Rùöï*%Õ/ÅhZæa°Õ¹ ¢Kã¶Â’+)JGDÁšÆ mj skw0Ô3ú‡ð=7+5Ÿ`ñê§€¾‹ ’p'?nÝO1T÷ÕTbgË•üý—õÿŽÆ ít"ˆ,lÝHÑÿaU/Óõ Ô®=’£Šõâ’zßÁPÅzÚ‰ðju ŠõÓr‰ãº ‚*Ö½H‚áÇ0T±.•ûà·äɧ€*ÖåíÌáA$ Šõ­›UÀDé#Aëù+wBÛ‘Ãú»ù2Þì…ƒ£(ªX¿üy|}ü™¢ÿ§ëF±Ã †þî|·ŸvÐ0j€þŸŸÏ4GtUÚÊÐßíï×ê}‰mbkŠþîù|YU1°äß·ü7OD ¿;J×€󪹣žN‚гU÷¾7d̽ĵ÷P ×ø ¨›ž²¨ýø,A¯Ý …W:ºïlôýøš AGòȽúbŠÞkÊ¥!³Þ‰¨TþMÊ‘dâ’H†~m7‡œ¶ÜÆ}ùX‹5yÏQ£îoèA«|=Ñ~ÈAËž _ hP’@îfû14/ØA\2§5W*_fÎK¢¿lC£ž2²h+7Z­™¸ fAMªß ƒoÆpOÛ¼ =¼dÌu:HQ©ù¼cɳ }\Lb &snuƒÀàP@ë›Oãá¸Rý/蟦r«ƒúåTKö.¸€ RçÿñT•ºµÇD\¸“IÐŒo2h轑{ùü]b“wŽ¢ËÿõQëýÙ£Üûç „s:Æ€¦Ï6#õïs~:Ó»ÆôÅòhß1G@Ã*û³Å;Ôôæs:s‚9 Rùß4ö&:[ò¿Rx19ˆ«[½,AÐGÅ!âóâD®oýxx5€¢z7 ¢ï#®ßÞã–?Ê èœ~ùäSº Rù&’xRé4¡Ñ‹]É/3î±+ÑD×§” ö§ž‘¯ê¹}´^’'%åT’#þ|ïÆ•š_Õb É,ÇÐ1Ã\IÝCàJ\ £Dh³I« Íj‚JõŸåÓ–eÜë(Y}‡¾‰Àýƒ«æ'ƒ/ËtéÐß㨀~è X8ES4¶æšXæ2–Yy”( TÕ« ¬²àލÚm3ˆh¼j·ÜAÑ}šº Ôº_?¸ØÁž RùZ€°%Ç¡Jƒ÷×L^É=´Øœ.É'è÷·Éâ)'¸ÊÇÃC;3†ÊÜÍ Íî  ì] ¨Á´XŸ®äªøt'V=µªKëSï):ù¥¨oýBP½ ;(/W4yÝ;ú%𥀦èÆNLn"¨Ô|CKò(o8Ckúš’4]c®o¦=Ô£-«ONä.Ù³òÎèjo»&í8Fмù:ë`Š®E=~¥‹hÙ­ 2zv0C•’þ.¦Ù+ù§X+VßÁÐüSͨ—š·D¶ œiôÎî4°R*Cozl&ñÛêP%mÚÀ}Y9˜<œRGQ•»”t>͕ʧ´ÎHþâK’ˆZeì’× Ø* õ©©2ð AûÅä\=Ì ›sˆÜé è“oƒ¡££A¥æ«…¾ª7vfhÍö³B³Cm¸!MoéÔ‰:ÜAg°Yo(*ù^¼d8L 3Ðû7mÀ¦ã~ŠþÁUÕ`}@tH¡¨sT7°¹v’ëpR9/­#¨G©:»àÔP•K`L™€4ôÛ³Ë)Êz·a®ô&è½Þl{ßj®RøKK¯­m¹ÝN’¤ +@¥òˆkOï”´cè´YjÔÈåEm-Á3›ö\Ï£0¤w,÷~A¨ÁÜèEÓÒ‚„öþ/¸Ã¾›²Éí–p­uÕ˜ÉÛ2TªÿÒ¿^ÜÄÐ:ã^p´Õrîÿ°ªŠ—÷25H~ö˜¢Šõ÷qm-7T±þãýyrç~#Eë7k°§zªX¸â“F/T±.•ßñRª·.—¢Šu™jtQGQźڜ2ÑÂ#‘¢Šõëõ¨¯Ë‚þn>ïôv‚ï¯wU¬‡ì,†ò©žªX7~ó™X†D0ôwç«í*r½FQźg·úhð ‚þnÿªZ’±#œ¡¿{þ_Vœÿï[þ›‹¾¡ÖãÏS4ÙëmÕ7KB ZgëôI&Ë\Æ-ݳœj7ôýÅ꺠Rùl[è°nõŒÖe:¿²¹&‡´ØùÌ7Üè­ØËW-õ7Ú¾Ï̸?[ì«ñ? *5ßëb| GÑ»ép|Ý,îÕγèu‡… ÕOßCŸ5çJõ×ñ4(Y½Ë‹¡_w¤¯ö,æJÿÇSUêV ß§ÔÕ7ž¢ûôoÓ¦k¹¯þßÿ~Ý¡Y=&ÁƲ¥ÜýadìÇ­€^»¿Ÿ4p[ªÔ‘R翹3ºi@§3ù¥n õNƒ¡#·‘ƒ2ŠJå_{Ž˜ƒ ÍŸãE¾¥-ç&œÿmCÛ3î¼uç.iú V%{së_øÐ7GݸêƒvŠVñkípj)ÖræJå³܇ oª ¨Ñ›L¯ï n@d=5tW´xýºbp7îy<­AÐÃc] ``ŠJÍ÷[±VW/¡hÄÆx±i=÷ËÓTZ5­–»Êjõ²¹)¢RýŸíÛC ýÇ3tþÎBjß™ûWµrCuOCÑÔÈj;F[ØÝò"3ôçx-hÓËk^ù‘8.TÓ»;fXrwu û­otÞ!s¸¾Z. Ý™3]0ú w¼KUëÝ P©ü÷m£‰¾é@“fæ`]nkcQ4I÷dhøûô”¶;w㈽Ô(w$÷Ûà#´r¥&· HìžÔ´hðpø8Œ¢RùVRgquÊ€^×’y†vàîØâH?Þèι)Yü¡ß•{^­š z{Œ¢»–U‰*‰5\©ùö³¡CéVеùüm\µíö‚vÌd‚¾þ\@žp%>º#o7EýL{S·}_äè\Õ¿³©É¦"l²ƒ:÷å~þLtR&3t·5#½[p÷-4ÍUŸ ZþhD Ö\ûÀ§Þ¢-ÓTaX5†^{8Œ>ºìèèeËÉ*» \©üy §ÉÅú@@×åÞ#+§qíµ’èw­i ½PH?5 çÖ·O§ÊAg(ÚÐl)ËÈ%cz½iÉÐè£Y„©r¥ò¥{¿¦§+»Ú¾Õ9:q^/î£o¡BY‹\§a³‰ó€_ ÝŒ5ÝIÑ~íÍ™½m¸ˆJÍOŠ¿÷ÅR´÷ûHã—̵Ê5ד»zõÜ&Õt¹Rý=W' 7_-%è‚×E.ßãþÁU¥w×д.Z2´4‘]p¤–0žµj­ÇF±jýúׯîr’ÓL¬qË›ùlnÊûi² ]Ÿ8‰âñdòý¦­à<í_m+ãÏ#$soÊý¾jÈ­Ÿ×‚E#$U”v¾•— °B²çíâCl³µ°I¡õÿÿ? ›rV"'Ɇ0I{ È«µ¬±bT4«¶?¾Å"œnq";$¥`ñ7Y&ýÚi5ûL»!i’|D ÈÍ.¿=VY]lá²Ån Õú•ìº&Ç/ rsýÍPö0‹ xê€/õÇù®¢î H”ÈûG–¢ÛZ{$ûu]‰ïŸ}Rmÿl§¦r§M“‘ì×ú–öºÞ6·Îk)­Ë ?ÅÂéUVHªÍOïŠãª¬—H÷éSqöׂüGu|Ýžxd^’¯Ì=1Ëu4[l.’k/…¡µÃÙàFØÒ» :ouôdÕúuŸ<¾o¤ÞšÛÒŒÖì©émð‘e‚L¦;Ûàù£‰Ãp”q ÷ ëˆðµ’ Õö›%öо¬Œd®¶•sÉ•<äø¿d‹baô£I¬Úü´À%èÿCd\l,ÖJ1SÈßxTg%B˪£Ô[7 ýŸŽøÇB#çÜyF|×CjVÞÈ –cqv3$_ÇaªqEöÇ‘ŸbZá7 G){…Y#c$ݚ쑌 g°Ùþ¹0àDV­ÿä« mâN 7ì…Úõ°ÚMÛ0ï@† ·ßÙ€†f—Ù‘Í´`Ry’w»¼†¯ëG²W×E¡Uw;¶eVæ—Ó©ÖÏêdÍ´úÝéut«Üû¥>Õ}ç°s—×ÄÿXë…@Ä]aóöYÈþY5Rmÿà «5³¥©H–N¨$¹úÇ ¹;vÚ7Ž5™ï‹—ÇôdÕæW{·m«·WH§C!hܾ;ûje½Qhyz’¹×Æ`©Ö‹5uWf·È’ž`ܯ„ÝÞfNײ6ÆaØý°±â£ÁP [ü— Õö»ºxH ëNEr\ï%ÒÏè`6H @½3¶åéÁè—¤ÚüEî(oqSȱêઢ6ìo<ª¥F0òB_$o˜Œ¡?z°®SçƒË†Zÿx9ì[Ú³“6û Þ”‘ÇÈ?’m0¥ÙêÅU8úâ/‡Ø¢aTwöçÚ¿ŸÈ6ýòjÐ}xoΪõ—ËÕÃÓ< kèÙãûe'ØúE=1ºk‰ w±@qŸ5nÛûÞLòÓ™¶xjG®D®è·@nêÓD!Ý”SÂån3Võ³ÀÎM"Ò'E"mæˆÓr] oŒ» šKýòj«`éÀÖ¿ª¯,ÿ¹Z+º¤ˆÒz—µ¤ÚþÓ{vIo¶OAræþ RýÉAlÿNh“v_KÚjŒ±°(Ujó?D ð;d¨†‡Ã'÷­‚üŽªîkcX0ÎòqAR7¿ï‘UÚõDR7ÿXöÄÖLAêæáÆ žÏMgR7ï;в½ú)¤n®Öÿˆgwôy—¤nþUs ¾¤ÿ!HÝ|×â68ommAêæ. ‰ø¸W‚üÕ~ã눾¾£€ÔÍïãJÈR¦*¤nnvá³æá° utÏRéêßï9IÝ|C̸“_C!u~R3Ú´qSdòWïÿ×—UN©ÿ¹ä¿ùrnä¾J?"ëbÃéì´àMðrg*ŸF>†·¥{Ù?Zö÷?ØŒg`»·Âj¢×c¿32yÙ`þÚIGâÞ —w‡$²DÚ GŸ˜¤ZÿàjIZôŠdxFÍcåªû±³3BÞx`§Ž«…·Ç÷cC ;á@û6‚|¹Ôïæ°qßíe×Ýž@æ/Wäs«ë!©Ö¯(w16ž¡¯ùržJ¨ÊŽi&œ:.¤Ç„¢86ˆ-êí‹;ÇY¶x –ŒrfÕö‡½»)í´UHÓØrd»6léü>xi\;$GÚNÅ5XµùY+,”+a©ZòRTåòø…©vÿ¿žªj—ºÖ¿ŸdOëjh¸í»0¾ÚEdiy°=w>Ä:T¯…åg±ò\ÑjßN‰tëá‰ïCy¤CUlvl«õ/‚e@º— ƤSÙ¬Zÿð™+4¦ŽHÆÙ6’ʲÙ!Ÿmppóaì›iÑ«­/mà†[Æ+ä©Ï®øÉÍŽÍ¯à ›~‚d‡]פÛOG²jý:œŒÂ»æ yèÊbìtךíç{@Nôd‡öwEû9­Ù†N;0õð*‰lT5û(“jû«7ŸV´UÈŸ«­Åµ,;v¥Oš>h‰ä¥î‹PÓ±2«6?sÃmÑ´g £‚¦ ·G#ØßxTõ&ýç7A.êd„‡öWRÈ‚8Ó𠽂qQá>¶^úü²d¥L޳\Œ– ‡  â!¯E:ëÖ·¼tÉë;[lFÃwq@j«lE³à?%R­¿m•ixâX$µyG¥ÜÂ@65°;–x aýxbäÁÞlŠ«„¹Ž}2ý³ö,É.ßU ÿ~_ýÿì,›bõw›Të·¥hæ\j¡UXˆ µa³Gý%’ÛVäÓzU”º§drbþ<ò¼µ ¯ºjð[Y4«¶ßri;±)Ã^!¿vg8°gëÌÂ÷²€lîïƒ1†&¬Ú|½[œ¾>\dú„¥0b;ûêû ú˜1¶–B>:ó¢â­ØÁ#Ñ5s'¥q+qyÒfvÅÞ•8øñ>AþÕm-Á§l±y?áàu€-{VT|˜Ââ×YèsV+“½Cõ°~F¼ ÕúO›òN:43I=ÿ: ãÇš.÷Âà}X³Eƒ°ìuö³oL0V!×|¬†9CG³…Ç]ñÀ>}AštöÃZß*(¤Z?‡‰s±Be…Ô~Ɔ=Ú±Ìk+E‚d2kŽ…=-QKv_¼šMØ%Ȳ͞²û‹LVm÷°ÂÕ¨B~ ñ“Æþ£áçÖ˜[&áÁµ4CjóÎ4ÆŒ£@6¹çoÎ`ãQõîø–Ø·RÈQ/aiGÖA“€‡µq@ÞNÞ‚Ëm²)Q«QžRA!'O‹Ás³ÌXÛʆʱQK9ez-Åï³×ôÔ±I_o³ž®g…爧¬Zÿ7‡:‚ÛË¡Hž\8V]ÈΜ5OüdY’~´Î`Ÿ|ݼ2Úþ,`žÛ¿ž/νØm˜é„ŸdÕúå\ƒ¦ëÒËôï'{9;¶|K e^Ñ. ù°e-ef¥™9…VÜMd•ЦÊü²é¬êoì`a±¦Bvï&ê.ûÇÄ¢UðmD¶Å¸?$ÛwíYµùÅ9 ñpí@öp-ŽýGÕ@<„Ï‹@!Ÿ?½·lz°ÆÉ)˜íæ 䛦ÛÑ Û‘M{2O\«®™àò&lý³Õ”·µ^Êä¹gŸ„ÕáõZÒèM%GïäÂÇEØ’G2©ÖM|(¤wõ@Òud€GÖÌ« >ˆ’€üô³Ú´]¯!÷ÿßúš+dû'=á Ç=AÖ÷ü å<[)díÛ=@ì? Hµ~óλãÝU­2¡­3j†4e;¯«¬¼;+‘Ûì¿‹ö敌½TCIËy/“•ÏÝÁ±Rmÿ«å‹EÏv ysÃ2úÀµÙ6OŽ]ãÎZwv+¶y°jóÁ | dè£mXi“ û?UÝWdù‹p»¦§BêæV-·áí^•€ÔÍ÷öj‚9õR7ï»/Mô\&‘ºy†ÆP6˜ÒHÝ\­¿Ñ£˜ýÃIݼeÓ&øey]AêæÅ/K±ñÚþêæqÃ7È^#Kä¯ösÝ+Mn ºyê§âÌð¶@êæ¾ÞuÅ–/@þêþÝV‰Î ©›7‰Ÿ,Ú'vTÈ__çòFüpÑ^"õþ}Yõ-gë.ùo¾ÝNè}ݤ%kF› ¿‰­4dHÚ3aòJò[ÝbqéÆ%6bæE)óÙ®/×AÆñLÖdÁ2h¯ @2ðõ.°ZàÍNƒéñ¯€ü¿ ÷ïвjýË œpÿñL™¼2¿ úŒ«+Èé§¡Ëw… ˜èŽ{{üãÙå{ ™ž9›c6GÛ³—6>ÞnY!H˾Ñ"4¿«Öoß3Ìl'H£ÕÁÁ3½솷$I!³L:aH厬ýåj"߸ ës¾£¸aÛ’UÛû¡~ÚjANr€Ó<·°>ŸË uÐF¶×Ê-p¿Î7Vm~•mÎ8·¡¤ÞùíÑyyVíþ=UÕ.5ºxSŽo&‘_|bå“Õì£ÒÇbbú 6¸!Òæ°jó¦5Ç#U¼räû˜wÖýG5øÍwmé¶+™[SOóá…Ú£ÇÅy¿lÝr©¢[üKvG´/Æv«Ë.ø)+÷I¤ÏÄ*s!È€W0£K56uÎlT,Ȥ嫎5oÓZ!Õú;Á@47Ø)ÈÉ[$ä±sõ«á”õ²øä걟\KÉCY½á¥š‚Å^ì“v¹"£C¶DFWr•ßk©ÖïÖµ‘P>fŸ cjV‚¸/‡Øþ]+âž!5rèÖçpa˜[¼k”׬%õÛ oé+«¶S˜†½V9pÌPtJ=ǵÙ#Ï9ÝC![î¬$V{!«6?¢Æ¨ÝÊ^!“Ÿ†]™•ÙßxTf¸H7Ú·2\{@’z±¾þ‹D9ßb¶ðz{1Éè ÛÛz5ÆU»¬!=ŽoD¿[¥Zr«¿#ŽÞ·G&\ºc”{‚ ë&u_œ²çvåUƒUëÿ3c8Ú&ßä§e~xã˶¿ÉQ¸ýãûáÝl8Õvþíg‡‹n ©·ÿä±lÙJ,»¸ Èo•]p¨ÙTé'¾ ”´¹9‚ü’}Wë{ZaS& 0ÿŒ·Œ"öuñNXù]ƒä®Õw¡¯¾«¶¿{ûá˜Xÿº “_ŽÀú}°Ö_ª‹â%Mrö™×ò!÷÷‚T›_è= šdZØ6)Ùß–ýGu÷ûrPéëx ¯ºµ‚¼‹Ù˜Nûåáoo³ieÓ5·§å³Æš­Xä–,“ŸûoÂÜ 2öÉÕØl×gA. ‚kº5RÈ,‘#¿Ú0Mz‡HƽÜTë”7Û‹ iÞ+'𱆙å`°ÏXA&evÕ< x-“.MŒ´Í*+¤ÞÏæÇÜgŸdò³E¸ì »÷X ß®³jý²ä˲¦ô„ +×m+b-O³¶‘žà·žµ+ÿVÚtcÛìqy,ÿÃIçˆê8iZgVmÿê%#qp§‚ìm< Ë ß³vmfÊ^¡ Ùð‘s4zf'sHµùùsGÉýê•—Hí {±/ÐÈßxTý2¼ §Ñ: }«…Å·ílBöwéÏ'Ø -¦Á;‡ÙÎM×à¸ÍY§s$~éîÌñA3k'…|¼Ã?[wc‡;ÅÁ¦—u‘ü9ê1ô× aÕú/¬5C{ÔUÈ6–ãñþŒFìÓÛ§d—Vå´dn·^b¦WžD†Ç××hÖ¤ÕÐ[š ¶ú2ÙuÐR¬ùt)ϾÁ´$#‰Të7w]°˜âxVóo¯KÌϱ=·•—bgUfKwõ‘£ýÇÊdÅ–¨=ùÈâ-ÍQÿä;‰Tý,2Íó[— ROŒÆa‰årœÅ%ÉA»ÈoI.0¬þ_ÿ¨2ÿq·%¢Þý< óÒSDµðrHþGU÷Õ:e &R7Oü|:G¦©››ø¡6wª usß Z5tUHÝ_ØÏ.§½Ý½ÅÁæÏyU€s·Èdi¯‰è0ÕFŸ#NÛcdÀI;rîjýúÕ‰•ÞÏtGr»ç2)ò~oö²>êä ýÑn=»úÌ0|úÈV!Ë×ðBϧÍYµýùù.RŠ&‘íºI÷³›Û½ ^.¤vì5á²~"«úó1ΰȑȊ‘ðé€c2ù;ß|Èw>²‘Ì-­Ù÷^Ãök3WÞÙd½ ]¾ÖµÌ^Ýù÷/ÙÜ5ì˜ÎµP¿Ç?ž´îZx Ùpïfhõ`0kᜠg¦¥y±usLªjˆ¤êSky¸´·’½ÝÛ£s-6%¼™(š'È´aOä!­×±g‚ÃÑhDÛJo)VlÇÆX6ǃgI䡘8iÍ:Aªõ¾BJ‹ìƒä “õÒÄ©½Ù£}SaîÜ‚œØ(L{fŸ?ì€ËOTUÈ¿ e§;‚TÛ¯·­§thÑC‰Ìmî!…Î72¼k’ð±h-ÈP»v"ãdRõ †»'v](ÈÐ’”{ž`ÿ‡£ªûŠû˜£)¹ßÉÿsk®“ÿØ?eVçµÖâ*,¼• Èÿ“[%CÞ¸ÁHêæòÌøÚ¿’º¹ZÿO—ûÜA&HêæfK&Éù#»R7ÏÙ¹›nÞ*HݼC#†Î©¡¿Úï%ì3{!©›ç9˜ƒU‡S‚ÔÍ[o\ qýãù«ûós{KšMV@êæVÑ+´âÏ…ù«óÓýfbbÄ;Aþêýÿú²jö»Õÿ¹ä¿iÙ¤#S_ ²þ[­¦¨êÖ¶Õ;©[£@6úÂÉt@U¶Ä#Hä½>ÀvØ»AÜ3ÎcÌ\.Nl>¤Ãk7a8ù(›ôæ©<ÑT"ËMY±&¬Zÿ¹™· JÏL!SÞ\‡Ûë²ëûGcB•5‚üÐaÎyÃYއ·w°»_Ö‚U÷†²çžTÃjÍï³Ñ{6‚×…xV­ß•W pš4 ƒv7ÀVK—²ss&CÂb=…<ÝÛ ZÎ0e Çã^Ï$A–œú±áÆ ©¶ßÞYoM+ä±Ñ_Á³}§ǾC°Õv@h–̪ÍjWûžrÕûHÜ=ô«vÿ¿žªj—ºtßåd_&Èt¿DíĹåÒe›¿Ôa×X™´ú­)혧!4Ù#&¥]äà {„i¥¶P³R>í¹È3¥IKê®g“†v‘7-/–Èfi áí_kTëÿ¬Õ5¸•d­½_g[²ÃÍÂM"9ñÜh ª;‡íyp„<ú±Lúíp”F$iÉð}N΋ëdíÊ"+¨7«ÖoR#KÜøjQ> 1{b5öãvkx=³žB>F-›³Õ®þ€¡•Ùîúx5­«¶ÿdÝ÷Vé® Í_‚_áuÖé»!>®ºŽMJÚ ¶õâYµù·'Dà‹/yýÈL̈в¿ñ¨†^3—#V*dRäyíã\yEëÜö¼DêÕ“›¬´rœÇfüú’ „DˆÑŸ´líì…0Èn WVÝ-UÙ-U¾BÝ[HîúnŽï:´gÕú{͇»> P/ž¾ïÄ 釿O¤«:臰¥{«‹›Ñ9áýTEÿ¸÷¼ˆ™;A7œÏ‹CsYµ~½4ÂúI[$²ºI´6HCšì*‘¾´³WÈÈ×¥ž]:³¥Ã*`¹%™qò»Ï¬ù;3ôðÏ`7,mŽ¥?o9“jý+ì¾ ŸÌ{(¤•]\¼Ø‹u¯uŒÂ7²Åò0ÜË5¸)LÎu2¿É}ñq»ÝÈÞ¬ëËÆ•,ƒ§“JYµ~CûÛaÍ…v‚ÜãˆG¯L`…u4ð£«Bš<––_qaõªÇË]zQ& |Dîí§Î¤Úþ2‡<Ø1ᨠq}6¸-<ÀöõŠŸÇë+dêýÙÂeQ9Vm~õaUqãÙA¨hˆ5;ÜgãQ5üòL.¶¬§>aÊÍ“°gž®“bOùÔh³ðk“ÏNZsY*ªkÏÎü\ *oc'nŽgG zm*®­`È~hí‰õê¯dôÞ éò€Uë_rå:¸ÏòPÈîo‚qÞìÄ•uà›÷IA:ל&Å̾Ç6̾!B-l€ ÉÏáõÙ¤÷» 㬒wN¦Á%¬Z¿ù¶Îxúrœ ÷šwÁHûÝlûª%ñÐY! ­ `m{vuG!† —Èmsž‰€5þ¬ÚþÈkà¾GM^×;ØŸB#šhž³iSnÉ'M2Yµù/7•ÇûY52ú†>†hËþGU÷u*ç‡üébC…ÔÍ'•Û)¦Ì*òÿäù­ =ãº9T‡C‹~©›kŽí*>¤n®ÖÿíÍBh졺y¿ö ´FÕ? R7ÿÐo³ˆ<_ HÝüù݈lÕÉ_ígÚ¦:<.HÝ<þ½ ä]n¡º¹ïñ›¢4¡¥Dþêþ¸{[ ­G’ ÿÏ6×´ï¿yöçëÇTÄ᣺(ä¯Þÿ¯/«>‰ýÏ%ÿÍÂ^ÀO©¢v°ngÄ6ñ¸x"ȹ¾‚|÷<ëgÚ^ ùÞI½7'Ží¸ß„™¸E ;éÐ³Õ Ö¾¶ Ú6I×¶ý±J×é‚TëuÂ? ÖGÒ!h86žóžÕ‡éÉ‚ëêuj…– òœt&Ÿ0TÈ~CvCðø•@¦…ãÚþ¼°]PÚÁÉ€º;!çAgvlY¨°xFͪ,5v?gÕæë­³sSE$ï~ˆ”{í«ÁþÆ£úƧ;ÜÝòJ¾t×q÷Ùî^ÉðÔl5;Åc>´?¼˜Yc»¼¼)’é;ÇÈWÚ}²þ±¿äÁ³9î½³\t`5ë4LLYøÐ[ÿS&ÕúÛMWþò[µž¸ºäÛ³i¼¼ÁPO!]·•-#LØíŸ&àp,dw›Œ1xÊE€²4G$v^ Ÿ|U}ªþÿÿ×ëÑGe2vÅO9õÇNÖ§J>ô³¯©ÕG€ÔØŠ爾&Y­´7–YþdÕö§_ÞtBrÞö¸pªð—°¾øEìÌD«¬ÚüÙ¾Ê1îµLn%ÖÅTaãQÝgÙÆä rµÒ~Ìc3:Â,‹ylñý;Òò¥ÓÙÜåMMºrÈÇFɒ݆£¬K›­šV7;²ôÒLiT%7™¬_XËFLdØÞݰÁã!«Öß/ÓS—¼ò~ „㟲îOÊïšÔQH§ôDyÎgKvÚˆÙ¸xÊ5AÎÝ3oΓ٠IC¡å $».€snŸTëÔJßK–ÉC¦ƒÄ›•ÛÙŽ{îÀ9—– 9ÌãBÞ4UÈÏ®ÙÐ]Xµþ£ztÂò[ |3¡JçÙR9Ô¶±B† ×.kû/öaþÚ‚´Xâ‡Op%Û¯ÿy˜í›¤—eCtYëªõû4{ž˜6q¿L®6\)l޳WÃKabIG…ü1¯ ¼¬œÙ¦>6X¿®’™Wo!jû}Ü5Û#Y8r#¤•kË–N+Õ¿ØF6+™¢–T›?|RG9¦õ OÍY" QÙßxTzµƒ“ý™¹»%Ü/Ã& Ù+¿„^TY„•®fËùÝ‚#É+lÓ¼2&½Hdõ¼’slÒö³«beïQwØÌz¡ú\{… ›~&8—gÕú{>oƒm†k¼”ßZ¤³z+j–î·TH+ÏYÒŠíµØ¦ïzc×¥óQ×ce3Þû`ùµ€LÏÃ%á¬êQ±Ü"2«ß–ɘ3»…Iæg¶‘ƒ^ºä¬6•ðÒ_ÙS¯jàýãŒ:Ú†jHÕßš7Àƒã¶H®ÍN€«-ÙÒ2)uou`Ï›È.ß–±jó¿µîᥱ@®|¼–ßZÉþGU÷•noW̤n>t^'±ì~’ uóÀ÷Mñ§ý uóø}f¢Wã@êæg=ã ÷ïR7Wëÿ¨ssLMLR7ÿr&W:ÛÏH!uóû+,ÐíB  uóÄX¾È_í·éÚ!Ñr½‰ us'OSÌÉwTHÝ<àŽ+¶·3ä¯î?½w=¤¿jޤnÞst™Üæn6¿:ÿµö#4^¶ È_½ÿ__Výi¼ä?—ü7këa·¥mü9ü;¬lÇ^>eÍ{t2¸=¾HëÊÞHÞõΞdnü°søÈ:ur†Meã€üÒc ÄŸÝÍ^Ÿª/"œ¶ ²—S 1Ëí-«Ö¿s©9®¾&ÈØšõÑu×Qön³bÙgtC™ ™ÖQ™wboiˆÙ¡€œôÕê9²&zçeÛÃØÉ3„÷îk¬Z?—ö.š £-²tp-ÉndC6¥ÌöµDrËs}œÛ·-›³~®ñbû@Ž/°cÕö[ÖM£Vפ^AЍz¨{á{LÚüŒ5îÚ 7{f±jóCNI;ß9éù#éyz«vÿ¿žªj—VMøÚ#9qíkø’êÀ_Ìé dY-¬³r4`“¾-Íò¡¼nèY³¾ßƃOÚ# îö€ÖÉFHƵ–—­0QÈqÓ·Jæ]¾ R­¿ß½†øÓ÷A&tj‚×®°³úLf%“£GÇ û gØê½ÝñM|/ ÓÞÅþJ0ëÖ RT˜™ÈíÑV|5V­_aÍ’V¿¡BÞ58,ÝvoÀ¶øó3ÔÙo¤uŸç0,ãƒç%jÖ\Ý(“!sµÒ…í;$Rm¿¦ûv‘SÒXnSv?G[öõòö8¿æjÖþkmü;‡U›bb/H‰Ø äãÍð&jûêú1E°Öº’! @ïÿh³uîþd‘‰/öø¹’­®wô¿·Vȧ0ó°[x¹:´›^Éðª$¿ÚØžëÂî;ëé–·KJý¾Y&ÕúŸªÖ½žÜäœmÐ-¸ˆ-ÿa«XÑÜ”½ÿ:UøUÔ°Eqh;{5¦ž“0ký~VïÖMçäyµØ—Ž Õ(V­_î¼Rf … ßSÆ_©Å¶¿x¾¸·E2tÔi°Škɦ­Û/ô]dxïëÎ7|N±jû•¾©âsT{AÎ{š*&ÝÖ°±µ_A¯ÌPVß1NF¯gÕæ/½v "{ƙܯz=ÛÈþÆ£jxã&$ä8 é»9nݰgkÖ ÀÍ)@¶L‹Swf¯¦êafv[…œ<ÂO.oʆgUД¿c‰äìär̓5Xƒcyr€Æ_"ß\!Š‚Xµþë·Ã’Qo¹7­3ê¥~`Wí5½Æ³C·%Š„ÑlbéD´z~Èi·ÇàúÍOØÿx9ï6Ât$¯5CR­_Þ’&pÓÖL!]7u‚Æþ•X©$®§5FrÓ~èP`Áö¼þH¾oõ Èâ ÃÄ÷µ…¬ÚþÙv;EºM7AÞsØ)¶MèúԞ ?÷Ÿ`ÛÔuƒ‹¾²jó'ßiˆ}*È@Zl?¦>gãQ Xzú-°C2soXiÃV\9K´g€\ú9 ãn³™z5pzJ-…<ô¾:N³.ϸΗw•GÒd•³|%í6½ ÖÊäÞÏ+ž³N‚Tý[ƒ¸Óî« ¿Mê†k˾³äháñi=+ÎŽNû“ØúFÃ0~ìŸ@–•öÆöÇ¿°éÕ&Cþh[$ût}-ýLlÁªõ3pë-V•SȰ·ƒ`¾òN¥ßwÂë3$Ïål‡²ÛïÅ"QõÎ {ß!„Ù$VuIªè±a ƒ‡§Šø½#Ø3‘] [ª£Ùó'À3çV¬ÚüOKþ~ó’P ÉZZ¡ÓÇzìo<ªSha`˜-’Í_†ì;-Ø‹FcšÕK õF ×âOlĬʨ?àŽ nú ïþG—rO“ƒ@ïÉ•œ2ײ5³ïÈž›; 2¿ÿÉ´±Lªõ÷uôÀ•òÙ~8) ŒÔ¬¥HY•Ìžl{_îûr;ÏOÂt§÷@öíÖ7l}Àž|;[ ­Œd·¥.§Tëw¡¹/$¼»/Èí«FÁúÚçÙw7v€õÆR Ãîѵn²ñ£NËö›­Y·C‘Rê‡F¬Úþë‘;ÄÖ¤1‚4 Ü.¢&NfSÿXï—µWÈÕ2T¶°gU?Vm±Ä×í›#©7¡ ö|kÃþGU÷•j“zך!©›kÜáºÏ?ÔÍí¼òáä®Aêæ—*x‚¯ÿb uó½\)÷H7 usµþ§«xããÚß©›› ’'íY*HÝ\~#|]vHÝüΧUÐìíJ µ_èÛ‘0jõAêæ…9‡¡Ç]Hݬüàcã u³rÛÄâŠ3©›Ÿ{sâ£)ä¯Îÿ¸æ ¼n…ä¯Þÿ¯/«Þ-üÏ%ÿMË[û`fÈ6Av‰Ü÷k¦³"ÄŒc‹$2ºW 8wÊŽêš÷wVBrÁç0ñQ ¶Ûƒ0wÇr6pÑ8ôßq˜ihˆ)NÛݹêG³jýÿ()…¾3Í2sð'ðˆ0f}Ëwy…ñ@F5íÇÏf§8¢}w°/§Wƒ¢Ê+Øç;šB“ÄúHvjxX3þ«Ö¯žw,´¸¸L½·…Àg¡l¨µ8Yekv«TÎ4ËdF~“«\&“åZI‚TÛ?¹®+Ez+*¡GÆu6ÿmo´_èL~è×ðÌ1Rmþ³MÑí{©–\–Óå5»dRíþ=UÕ.-úº,ëd‚k24 ìñ?‰žA4ä¹iqâî’]Z²fÈZ8rð$rß4HóKb6Š.ϯ±éšîxhücöR»!œ7žõ^â‹…agXµþ§Ú}ƒv Ykr íö]/í@õW€üðñ¥´.±˜à0¬³ÃÙ}WA¿wóÙ¬ðNòÏËOX—¬MÎ.¸Uë7­^ Ddü¥àf?õÛœ(;Ï=À&´®,ŸtIgOÖ™/>»-c¯„¸Š»õóXµýe–•q·ÃmAjÎ›à—¨ûì›>õ±úíH™¬­5ÅÆ©6þ~£ÕõÄy™|y1ËØÂþÆ£j1f ´59#È›àÕ¥+¬ÁÚ1äÖ ™Œ?»Qh›æ³#«"|ºæ-T2éÓì0£ò}ÖonÝð4»î§ n=¨‡¤ió:x»àO Õú¯üT5n½¤Ã¹ïðÖç!ûáS”T¶ê z­;ib$=$û­> Ÿ—yû\xÝÜÌ>ébÿa##¶ÀnïhV­Ÿ•yshoßM¯pqÖ°¹?w8÷]™Âfy4–âO¬eKƒå}ɷ؇V]¥G‘§XµýQ«LqòîÇ‚Œ 6ÃɉElF•*h²)Š­>£:eÕæïß3 /Œ>¬%ß6Ç*q~ùª{¯°gçAÖ«ºÖ.|ƺ—n}®Ug¯ñÎ쥳&èæ: È ­Ð{M$û~°¶–ÌŽ¼P§dçuÓCß:©¬mMS¼`kΪõoõâ ü(»*ÈÃ>À¾²?Ø„ˆr|þ_@–šž’kÿÄ^KxY•±»>Ã$¿{lÓÆakÓKlˆC.@²)’jýžÚâvdþ³9’wM'ÖëG¬ôlü"6$#[Ê™ìËþlS|­`'¼Õ‡‘sª²jûVÇÙ^/0½&:ÞxÍëÞ ƒl Y›]Ѩà«6hI&>6²Aå´îèÍþÆ£úáÈF8Ùã­ ëÃfˆzö…ÉY"šTðc]W‹ÛÓÂØÝmqþˆ­@ŽNí„£bóX÷ËfèkZ‰}¼Þ7Ÿ(‘››tBÿˆ¥lÔƒQ¸³‰!jýÚ¿÷˳ù|A |º—­an&¬[9幨¹ì2»÷Ñg¸6OIí¨ýÜŒ]Ó„µ˜U,5ÚQŸUëçWðHÓê‘‹ ]¸y•ë4Ãö³jóÇ…¡s#€ì?×,aãQ-ݺœ—SÈÏ×S`øž ¬o1h}Œ WY ÍüUl•ÅNhø­Èý1%ÒÉ”ãhµ»¦†ôÓöB½nÓ‘–÷ƒ0Óe …Vƒñºõ V­ÿÑ÷ÀÒn› ¿ÿuÖoÞÀvZæ*®Ý9dÃÁ]E×É ì<‹›`mn‰ä¤Q9p~u3¶¨O)ðÎg =ï’äÞ{Xµ~Å •ä®G2Òf†\ûËT6Úò¦ô­,M&›øh¥® ØÒ°þ"<¬¹–¼Ùk¥(,|,“jû½«ÔÃ1> Òÿ. ÿÊf–aʽpö¡ãXL™Ô”U›?7Ñ«:L2Ó£ >-oÇþGU÷Ur3âÊ+¤nnc^,;-Z#HݼAãX;»&’ºy›é#QoBš†ÔÍ#rm±‚w!º¹ZÿÔy§ÁÕk• uóW9ˆIcç©›‡<ÚwF·@R7ÏÓ@í9á@þj¿£iò–v‹©›ÌŒ“&Tw&uóƒc…WÕ Aþêþ~4Ä÷ç¾ R7ºÇ ÝvæÉä¯ÎoñØõ]öJä¯Þÿïÿ °þìÿ\òßœ?ç4WH¿K÷`þ¥Zìák¡öÈhAîþ¾úŽ_ÇîÙày/lvb#»¥ž>|ùnÌúHz³s­­ÐõÓNAF_ë‹~µ·³jý‹Òka­ë1Ù¦°zíÆfÖ«ö5‘,¬´B²ýY‰}±¼Œä5ô"˜-<^ ŸÕždxbcòà «Ö¯]<ðòˆrLÎyм\ÁÛ$HÆûM2óÛ7iGŸš¬þÀ¯°}x$K¯dÓûTÛÿÐ?AŠ’#$²Ó«Rä°¥læf=´IŸ!ÈÏ+¢óšVm~íº=ÅðÞOeÒÏéµ\ÞJj÷ÿûßWU¹4mýsþ¬ºB¾Í+¿­UÙKzaP¿Uª ^„À…ýÙ¬0qÄÇÝS€4²°FßUØ´®¥{gCÙÙ²ÝÆ(vxÅiX»j Aš?ž‚.©’Dªõ÷x\}\ßjÈ·'ÍÑÈÓ‘íôY«yØð+áòËcÉæ·Yíî©0ºlº ëK]àFìöâº:˜ÔFIïÃ÷!ͬ«ÖïAÊ5ðÛ¶Èî3nbÊ>­á11uòËþ±°µ§9;wv2T.ÊÒ3é˜ëE³jûƒo‘âßÇIdqÝ?¤à9X½}˜²WdAUGÌ\}šU›ŸP¬u\ë,Èàt7érRcö7Õø² L2?Lý,Øàó3 âíK‚´Øc¦?eãîÖ@³°H ý›`Fž'k:¶¼˜h6“Mí7P,<Ü‘í‘ìÅúØ7?­ñ͆F¬ZÿÝ3êaRvÎ1Ò¢È{»îÒ’>YÉÚI1Ù@ZÖb§DÖöX(Y¾U/ªÀXc…ÜcÔùû˜’6~•–Û¼R­ßáw!Çð÷ÎÞƒgóئ+À!¦¶B¶Ùœ ‚«³1žÀ § O 1Âù¯V±jûgï¸*¥Hä¦É¤ÄÖ{Ù {û¡mÏÂÜZˆ}Nưjó3ÖŸ’"kß‘Éè3÷¤ 7›±¿ñ¨â}ü룾Bv1¬ˆ9­þ¤´lDH_ÙþA!£œ¡B~šb‚Aá5€ 8[o ;.‘åÂ#EÍavz¯X‘Qy–tZOl¬%‘1'ÚàãU£dR­¿ƒAc4xk$“+sšcq¾›Tå²ó—Èù@&¬k"=ÈÀNÕ¯ ¯ rXÛŽ°Ñ¨¼B®¿ñIj2 ž+}alßñ¬Z¿¸÷wÀ¦ëU s½nBíýØ7QZ¸íl¢z^‚ãnl‚ƒ¦û\òØÍrØÏ§"’jû}_IÂ-K"ƒ#?Iþ O²ïºLûW­i?.ïºÕ–IµùùVÛ%¯Û5dxÕ­šèØIùjëí•0ªÚ'AN¼TµKØsûöƒÃõ* ð÷Ç—gI5Ø׬ÐmX¤D&V¶Ç/ÃXßa"uà7™L*uîf^‚Ü•ãIu[hÉ·ÁQãØ HµþwJm±È¨¿LvuÀú«üÙö6»¤µ;:i¹Fü [³¼§À›‘f é{xä´²`Íÿ~ºEÖˆÒ+ý ÈÒV­_¹O—aÜÞR û•þsNÿd8Þƒ†Ÿ> rÒ­"¨.?bÉÛoõ”ª ‡Áw-Xµý[|õà^F¾DëW€¯î±}]ýñþ«Öä‡Î×ÿɪÍÞ"G. ‘È0I‹Ó5äo<ªUp@‹'‚¬§˜â¦¥·Ø˜üGð¾b…ŒŠúõ/ýã¼€.è3å¨D&ì‹Ê¶š@†l½!·9)Ȥ!Sµs¦/c÷eù`bø1 ËkZ`ѵ¬ZƒàNè—(“WçöÜ7œm’b_â7ÃÆîÃN­pYZ*ä#ç÷;ê±³ûG@†³>’Áå·H)6ƬZ¿ S³aè© HŽÍÞ Åî•ÙÐí¯`á» ‚´Üô ¼Ó³Ø»ƒ÷JZ3$LË4NWó€TÛoa[ å½’ÈI&0±ù7Öøn;œ\¡=—Yá¦Ý§$Rmþ¦¡½Å·#drï€qâÆ¥‚üŽªîK?²*V/¹,Hݼ ÃÍïÔRHÝ|Y”7  ¤nnð÷OjTÁ|Aêæ'?~±ù,º¹ZÿØx7œ=ÃM&us—`¸Ýqºù«¬ò³ºBþŸ¿þÚä{÷ù«ý>™'³U‘ÔÍ žCŸ;©›[Û/’¦ÝYä¯îßô¥*L]l¤n~Þ«.+ø !uþŒ&CÅ‚³‹ù«÷ÿë˪š¹“ÿsÉs¦Mo\|e-]»ã®zIìLpÔrU²}ô\(é]ý­ ýR&Èfb!¤õÖÊ)ôï'ä( k×K€ŸÆ²C–yÁÚ6΂tkxYÚ1ÝUëoÑ_®¿ã€ Kæh¹eã¢kö7Æ&ß }ê×—²¬¯7rFkK|a a‹g6‘¼snJ¤IE>ö|«ÖohÈH,~h§%-J†â™sG4¤wFkxž`/Èê[€Kû:¬Ü¿¾º³L&C}k`ÒàZ‚TÛ¿vÁ8øàsBqצ@/ó\6¿¸%6^WHóÇu1gÆ-‰T›¯·l|®ñe #fÉýßmdÕîÿ×SUíÒ {aDß=@^xÝ8¶ N„Ü{f é8ö¸ž©Â~ Ý # Ù;#æ¥fÛÆï†Óh OŽÝ©~;ÙᎩÍÃ]‚LÛP ÖÌ,§jýsÇw®Üä˜ ‹­«Hšv2ûä‰4ÛÈ‘5lÝŽÔ`¿=o†£&ya|+üÿX³ó°œÖ6nüi–$IBTÊTI’¤îë¼B’)2„Li2Ï™£L’(I’9™¢{]…"™2UÉT„)Ó»Ïï=Ͻû=žcŽÇúçóÇw­óü»k¯ºoc/l&K¦¸‹¤‹›$ô“™pN ¨l¿‰Cø†vƒ:µIÞqò²´¨Übj½·[LlH¨Ûù6üSš—@ç5õâÍœ:rûûæÁœJÂÅàìtšù͈W¹ù0ÔçM#îp#4•›oRøEaÐP‡¡JÉI»„¡ð¨êîÁçÕ? èÅÖ]yß9—Ée¿®C¿ºY¨AÇç`sDœ×F y · ´èb̪B¾>²&å_tmÀðsNv?x’%ã,´³:üW-5R®xÌ2·\hßÛ'Y?K%y&ÈúÕy$¡Wœ5ÿþE¼üîgÃ{8ÔÌÜŠgØ=$C’jIoón ÔÏ×6¤•’rýò½xÛ¹ øìÎWf>! —>aåq%4çå>äxŽüÀß~^MÞÔ›Ìsîg3TnÿˆÀå¿BN4R/ ŠŸ%ëU¶âãô%T˜»ñ÷Ç”¨ì[õ™”¬åÌÐm<*NþÁ£zo'ã§Çß´×Ñ.ÜãÆCÒÇþ œûòF õ«ó[3ï‘©;@ûÑ2 h%¨-žL¯7‡u—ª-=4ˆícUäí~漇Fª@ýöÕãï?ö!åúG^zÉìN 4yJ]˜’{œœó¢¹gô—PÍ3Ø0}SÒôUC^Çå YBƒn­ÏQÄuà­}J Ë7ÁᛋI¹~U[»ðú“>34|°#7qQÔ´Óp0󚄺ûg)‚J+É! Gò0倎¦à«Ž'åöwZq‡º/,ööÝOÞ×óæáã›1Ô{øpÞüC]@åæë·Ì+ù«u®ÇÇE2ôÕ[ø¾Ý/µP:ðÆ—ß’†§´y÷ï9]ã Ëõn!½gÀ¨U+ÉÀˆ>`\”HF.í*- ,´Û¯ÒFÏCä¨Ü—ðuÙyÅ“I¹þÞÿ}aüwÿëMgg(óJ%Õž(vè5“Ъ˜ÊðæãÉ+KÀбGX‡òü™0ÙõhÙuøÐ8‚”ë—7Á–[vùÅPÓÚ-¹XTMf¿Élf ÐHåÁL{Ò¬–)Ï~œ hÁ†ï0Å8ž”Ûï8<ô§¥tÝ‹µp?<‰|¿o¯ 4äro¾@3‹”›[¢gžgèíj%s©±ôÕã¹¶|tçÏ€†·âº÷~SA‡;Ûí¨qMÞ¿ÙZrO¸7·{üêKΰ/•=%¡ ¥t):¡®@“n…ÂŽz`pÁPƒ\e® Qmo ´eíPпN*ׯ&ÈœŸíò˜¡¡§›ð’“H»cŽÒË—CZÕd˜ôÅ}9Y½· ÞŒð´]w-ÞÓ¾)·?±ýz°Û• ÐÈ ~fYhìÈÃTz7«×ýNÊþ÷·½¡ÐzßÐò½AÒÁ,ù?UÕ+¸ž%ï}«GUsûÔŸ°Óf‘@Usõ´è7C= UÍCéŠ6s ªš¯9ž;ø» T5—ëÙ}Ì Ø(PÕÜfT±4åioªæ§ôuàFÌ @U󃞃ÓB£,ôwûé5nÄïOKa¨j¾Y —Ô|¶ T5_¿³ îë èïî¯7@½ªæ1/+¡êE5 ¿;?Æ¥®€gº€þîóÿú²Ê£ð¯ÿÜòß\òΆ»oºÇЊÖ<#¹Œ|¿ÁÂÛèâ*;Øû8‡l2Ùž?ßU h;îÐé+iåyÌ©ÍQíÅã ÝN‡Ì<µâ¦ 42k%ø*Ærýoz=†¼† úzÌCH¸?•Ló0æÇl9 áÍLy²Î$réŠëàI6³ÌƒµGSÉਸÔT# ݦ]—O T®_¹ÆAéi[g†—‡Kþ}hv«ûÊ 3jñs£¢±q(9ÚÅ‚_x¸„ܲ­+=I¹ýßÓdª­Z«´sj$¡M1mïpÚ-¿ÁÔj­!jŽàóJè›Úcx»m@åö‹Ô®|ý‚“€nýÜg¸’7e ³ˆ¨T¢!öS)»×J¨Üüªº5ÌQ3@Sµ–²mvmÈ?xTmj7ãÅå oÔ„{&=%ûZÆ€åÑê-;AÁêYèèÔ5`”ñЂí=ÀÁ¿ˆœ¨-½Ö"Ó¶´”žwèLºÄ7â‡b_ ´ÕŒ>ÜkïR®¿§ÖM(›6\ _…ÇAÃÈ37ëò¸Vù€^©Å“¢ž‘&ùƒ¦A™1HüFÿãm¿×ð|tW´¬Sa¤\?µâS§ï–ÐÙç¥ËÃI›ouaÎJÖ<²ƒóW–’c{ñ°Ò€®êÔ‚{–Ý åö_3WpÏïgÕkß…ûŒ¹HŒx¡\u¥‰@+Õ¤+W“róý×MPNÈ5Ô#÷˜¤Ý0‰‘î¨Ú4âû2tó/cî¼§\:è˜Ujf¡£«áæ¬\´º!¬ˆH4ßN>^YO–Z Q®H ñÂ¥7%¾ãÑK$t«/ð{5@åún} zõó¨v÷\®Mz$—wæ7@ݯd‚®¥GË[±`ë;€Zœs¢Çœ! 5ã>bªÌèÉoë!åúîÊ,Nn•PÓëYË%ñäÃ^|zª@ƒ· ‚±3ýȼaUpÑ´а%'`Úðã¤Ü~]+g¾ì  ;~8rg¿d¤ZŒBëAŽ@g©}d4>“ró;ž1š¶)Q›j3qü‹@ÿàQ}_eÄ#ž]ah€[}–u–LÝ­Ç]«Õ²ÐN~Mø`ÍMkXŽEŒ´µMkˆþäLº”eyFÑ 4Yóï¿còJɦ{Mùô©v€º÷¨ÍGór†ÊõŸSûl÷è¼£Y]9 0‰‚Ç'u8z;§7Lh¯E‡?‘ŒJ6zð«¹Xòª9:f,÷˸©DO 㣫¼•ëWüô3½'¡‹_±g‘dÉ>?XdÓS &_‡ƒi]g2ýbÔÌ ÔsdŒìéOÊíOÉnÏK*ïj9ÍŽ;vºGŽ çÞèf¡G½®"A‡”›¿¡æ‹d¸Z áŽLн&È?xTï”ÕãÏמ`èÏ Þ1%•Ü~¸ :R&Ðõ:qï—Iëùžðج- ßކ¥G:’ßCkC=³Ñ }Zõœ}QO!{njÄýÏeÙ6Üâd@åú»OÐ&y‘@“Ç ÐYFÎ(xÃv´úè«Î,éBùr£HX’ÇÐÐ mDÈy WtšäÀã—´o¢›CÊõÓ\þžix,—Pï@uð´Až Ú 4-ºì.·$›OÍϹó5\sž[ì&åöè¶åvº"²%¯yø˜¬ÊÐái§«:#®ä˜EÊÍO~»ˆ½¹òT Aùn° s5ù?UÕkÐ@¾}òv†ªæ¥:œOÿš&PÕü`Ä8¨l6PÕ|°í ¶néi†ªæ[[òX×@Us¹þf" òK× ôÿ¹ÁpˆÒÛä<*×—Z·øˆ„ªæþŽ9Ðeb ¿ÛO³™ìÝ^BUó)õ:AúmKªæiE1°hä5@wÿŽÚ-¸{¨jîiÓŠÛ5 èïÎ_ª± –Ý©èï>ÿ¯/«Æùùýç–ÿfšz1k°ÿ¾@/ÞЄ­Ÿ’Ã>í`›omt@åm…IÒÒ´«>ŸSMº„Š˜õ¤û4gÉ $A W…1¿¹yd²{/¸æhpÄ2¦Uw)׿ÿÊpuÜ}ê”ÅÏÈÍ·“YÂî(†–ÿ,bvÃ<É­¼ø•.MmØ‹wœ”*¡'6‡îÇV ôUB\ßv…”ëWsÑ|>› Ô¾¤?4q²"Óµò!Â(Ðì¾p¯ý^ÒáewÞÁ ‹@u,†rµ°TnöÜeàÝÚ€&¾¬#½ŒV#=Š2Ø0ó>dïú¥lÅk5Rn~Ø¥<0Ž!Ðcn@õ ¥¤Üóÿz«ÊÝz¡¾9ˆS¯úx³+,îõž¬úrZ¹yêL@3¶…K>«=H1úܰ !—®¸“¿%¹C@è=úï€{žwHëǤÖÂZ°ä¡ä9©›@åúWœƒ-Õ¯:gÓVˆ‹«&Ë^©Ãûæ Ý<×ô™¹Â»;¯Ò:놆»ð%;™R®Í*òÈžwMÊõãã½ Ñ&ût…ÜÍÈV§÷ãq€Æ Ý¥õdófýyÓŸÉúíxZP&)·?d¹½ôyák†(é/Uœ*&#ŒŒàv»Lò¡Zøeúró“[¿„.&QÝØ©6ßsbùªÂv($]ý,Pý¤9Úù;Ù÷â-©j_]@Ö»[Ç3tdç¸bÓÐ/=ïÀƒ7=É“fWàæ I Q¿*`ŽF<©Ö}2›zè&Ù&e7xM~CÊõß>î”tÿ*ÐùÏÿþ8uí9Î¥¼6ìÊPõÈz°Ák©¼bÃõvÿ$+N™ñq ºúöž”ÞF Y›úðìãk*×Ï>Ú šœõh¢¡ ÜÝ9”t~¹r¦.TóÍ6°]>•<ý RµJHߢP ¿DÊíV<]šñäC«Ú®–Æi$õEsؽêò®Œ•<~CÊÍвW중¿ò&€üƒGÕ(` ZòK =ÅBï+jYè€Û Å—÷¦ t Ic»PB÷W܃‡:Iã>è¯XJîIû»Î tÓšðà¢Ùc­&¿»ë™úÙ?ÙÙƒ”ë¿ÿÆm±ú.Ð’û 9ÕvÛ>3ÃþóÚf¨’}êH¶šgȧ7tþ)Þñ`CrC϶¼¹é2iŸ&¿pn&)×/:¥3œSšgìQî¡äÝä8øª7 Я-âáaÒ@òGæd(µØFNŠ¡¤Üþò˜-ÒîV›jfµ[²HšG&ö°g.©Ö¸XYõ>OÊÍßêÍÛµ«–ÐÛç‡òaËû(Ð?xT7˜¤BÝnµ²ÐuËOÁ,óÜýÂ@x3†¿~#µkØŒ]Yº±€z­ÏSߣ¤î WðøjG.ªýÞè»’ºFÚüxýÏê¿X‹[Õ6¨\ïÓ?áö¯Ul¬Í¿¬{Kû1‡í– ñ>ª¨ÒSS æ#5xBc@Òàí>3´UÌ ¸éãè…Ð7°b¨3)×oÕ·nð0:R U·úÊÀrý˜Dp|é ègÏݰë­ùeÆ Pt Û› §=I¹ýfYG$=óî 5™)m¾_—4 \+”.¡S>Í–¾iŒ¨Ü|ƒæƒyfƒf€vÞÖ¾¼‘üƒGµüÆp¯–…&¼ºþ¶?ê?t¯ÔêÐ*2$ÐX ’ÈO6AÖÒ@›{zƒ‰—I]ßW°cÌ@jþõ LîN!bÍyF“FdÌÔ®\W3_‰Êõ/ðoÀß>¨Ëa3>º{ iñ¤¡ò •…„V=z¯ët…Ü”¤Í_ÖýÄÐÑQuøž×z€Öoü z†/%wî.€ ½2I¹~O^Žýû#ãAöµŽî÷3QÕ|Ã(˜×ã—@w¾Áæ[`r¼Ðß}þ__V91ŸÿÜòß´®Ç{w¾ÃÐÇQ†Ü{n9y&ë£tr#'ËÎVK¬ ”h›OÎÜúÙæ½öäMÎ3òÐ%Gpº¬IþÙâ¢Û“ç³D 7%úý¾+ûc$P¹þŠ×æ°ÃWÐÇ ZBq·w ]åU éb´@ÕN½†×¿Üɉa¥÷JÔQó†ä^UG ç$©ßüÖ€Î9œ-ÍÏÙ'¡rýb{q©§Ç @ ZÄHiý}É&w€ƒccÒÅVwߨ–ÌÍiÈ3²íš>ΜÇÕTnhäF0z9\ ¬"A=r4ùðóZ‘a, ·í\Rn¾‹•'ß|… t§v_¾»ÜKBåžÿ×[UîÖ…ëêóÑ6?ª;ʈwkQPõ bièÍóz0,I*.µè×~\ïÄJ Í]׋\åËжí!Po†@]ô‡‚®f<¹.õ3Ê;D¥¤AËKR®ÿž¹6¶°”¡ÛCœím2ïÃ+ЭÝN •s*àÝ5+râú‘RJíYddøNÅÖIdZR;6sÈJ²YæXXþu)×/.&Kʸb ¨žõ}é/›2†~=©Æ7ep@™‰wzéGFÙ6áOlW“Ÿk¾Á÷K¤Üþ#ÁqÖ_]u> F¯H*çUgRÈÏ‘ñhžOÊͯ÷̃ûKaèõùÁp ð¨>úbÄÓ™š9¤w‰iEO´“~ ö¨Zï07õ†kÈã­O˜Ù PÛ6<ïã$2G+²nHõLσðª2\¿Ÿ\–ûïºñ¡ë,I¹þ­lœ`¼î†úî k/’g÷–CúXkÚîxÝØ‘ƒç”2ßWYò 0>šO¾É:EQ+È÷k%ˆÒ›”ë>åÒ£N3ú³î ©øé7ÔJ|ƒ÷:réX?}=i8<¾Æ½ _þ,bËß_%åö;FCÙ©ÝoºNö™M¾¹P}‹ÈÔ,þ%=—”›>ÀŒ/ü±Ð^w5y½µkÈ?xTLjÀ÷Ýèh'»Ü­¢Y¼} ž¾C 3®?gž7ö‘^î y‘ÚZ@KBÕø½žQdÚ¼ôé5Æœ¶â—ú'ÝË]¸¢ßn†žLiÀÇ¯ì ¨\íÂ.à7îCC†ºBUêEòiæð+t¨Çe5žu‰“O–lƒçóÈô‡çàQþ²r¨ÚeÞd—Å*-'åú§Iºµ—Ðg$çÂÓ¤©Ö ¨x¨ºC!ØŒXï"ÿàQ]ºÙˆdú:z{}>Ðz"Ù!¯¸ÛèÜ>Pr;t^ýò›¬ôåâ ¼ˆüÔ—ñÅ•+znU_®ü¤Nž¯| îÛ8 }>>GglH¹þ9_\àåÁ|†ÚDueü5rÜ8=nô¨«@¿¨™p½5.¤fl4µO&E‡ÏðlÛÒý—OqøÇàÂý¯Ú’rý׎–z¦é Ôe­B:ÃH¯Ö$ ?èêQ©ðrLéðºP¹÷È ŸÉ÷.P¹ý¶ÆÛ ªó AÚlË6+Éðª~¼`F•*:vå;|*7ŸG6Xlötóø,pŠîFþÁ£ºÊÕ;Ô,4wŠ¢»ŠÜá¾<ts2<ÎÛK> mGTm× ð;Ö‹L¨öæãÖ*¨½ðàqßë::ï)¬¸H¶×; ž‡÷‘rý#õ`iüM†~Ûªòîá›Zðë#,úî¢=w1}+¡û7ªñ§cúÊ«oN¤÷¸¾¼g¹CZuᓦG*×o@ÜJå GÃê¿;BñF-˜,‰X*/zÁsxMÈ'ëùÀþ{z-!|¯õ"åö/ˆ‡X³pê¨'²úkÉ©møš9!€ܬë÷Ä’ró-þ>¢Öµ¼5t¾õžFþGUõ²°×ç3šoT5ß5ð´3‰¨j¾¥÷I(K÷T5Šrà^~óUͧvn 6›NªšËõÏÛÛN±"†ªæ¶]øâÁªšG¨ý“FUÍ=wiò¤•Å€þn¿´ô¡Ì~w¸@Uóõ›Ìá“g. ªyé—õõÓY ¿»¿<7 ÊÕ7T5_aöâznôwçGjï…;×ú»ÏÿëË*£u^ÿ¹å¿=ѯ3m hÐOÿy©Y:$4 íú¡f-„}q%K«Nƒ^¾Ù}6ÔZéDþ`É͓۬ÐC?ÛñröJ´‹Mmø$ P‹z“¥ÕÕ •ëÿ`·7ü˜Y#¡ž5Þ vU[ ?’£!íH{²yãXx3û“º5àÓvô'³s­ø5s²UÌhhyЉ š° ý£I¹~fËzpEçÙ t©e^¾v#C_l}Ë4óvÔ2žý¨^MžÙp\¿&tåÄPæ2‹”Û?mF;ð8³ P×(k˜¼ii©±zójpެJÊÍñÕÊt©=Ðf uHÛÍP¹çÿõV•»Uó½ íü ù}œ¸a«edæâ(x¿¹¯@‡¶b¦?Ù¾w¤v!!èÔ\éF¿èÀ;v¸ÍÐ[™mùÅD@cÇô“üÚw¨Îi[`^9¤\ÿ¿žô‡·?ë 4.×zå4'îÜ­6t"ï L‚ ÝÈ:pëQ$´rJžÚBCA¶:"ùÍödhv¯sÊ_)#*×oJZ7Þª© ß€[9“Á1Ì=o(Y•úC‘÷¡¹ñp Œ:d.P½ßaÈ©*û*ò6ƒŸÞkõÚg —Þ…’Z±90 ßU ;®ÀÀ¦íI¹ùîF*ÓŠ{Jè°õ^¬¹‹©@ÿàQiØž<¼ÐD¿¶¼Va,ißv4¹>G 5=ŽÃ¦‹¡äÖ¿^By>äãå¼EëžäÖÝÆ¼Èï  GfT@ŒýMrï§'`X/K mü›òÛæýH¹þiZC å/;iøAÜRyÌùÜÐL&ÜÍ㸠¤®•+¶|ÆÐV~޼ÅâÑ€ªPÀ°Lîv¿?kÒI¹~NuæÎ{‚½0¡=ßn¹žÌëpM± ]>Cg4z¯Hï ¤Uguþýn†„.\ú÷Ç»ËIٟ﮺ЪÖ@›tÖµYÿ˜T ®NMP«‚;÷ÝHÙ£zZ^¤è̉£ âsùê°ÖV¼á»Ý€v[cÆo¼:Dºi]„Pûµ-5xÛgl$›s·\;ÒÇËš+ûTJhÀ”xø¬öPãV°`Ù%r¦ ¡²ýfµä6žI€F7áã»'Ó|ì™Z€Å4£á2v® =ÔŒWÍòU †!–ü}‘  rûM¬*=ô…í¿8‚Œ8U›[·rèäòf¼g»o*û[y@LÉ ¨‘n*ì_ÑïÿÜQ-ØÐŸ;~ÐÎ= ø&ÈéaÀÂ}÷Y‡«MŠ /¥:ðŒzú:©¦~kC “š³m[âݼ´¡”Ý\\ªÆ{¾<Éа$5>ûW3@åúK ó eÿtd¯€–+BȇÅj|Žî2²f™!p~2irñ>„º¨W,1{Hš9ÖæiÜ”hã¾:üø¯h*×/cK]Þìà9@ó=Ô¸…ëU²`ÐQVò`«D•3íÊˤÛþטm€™^“gWH¹ýßÞ2¤€zÙ¾`sVô oµ°ç댞*ч#;ò†Mo1Tnþ‘‰Çá`/7.1UÂ~?gòÕîç´y“E9€°ø Éã/‘ÚÝòú]— ´Žf n—1ŠŒ½Ýmæ hÇV¼…õfò¸óxÉ×­CgØSVdæKè¥ç@sÆZ@/§ÌƒV{rýu÷¬…~eá}V+¾žŒ$ËÞšó E=Èä-íø¯7úÿØÚf]4ÐYöéÝ!²ês]î08Ž¡£Mõù~³^€ÊõÛ÷ê)D(oêö3 ­ IÝKõ Rï„V×·s!šÍ:Û®(.ª¶éUæžãËI¹ýÞ=KÙñ®€^[~µšÛž\Ò†¼·œçY—¯ñ=CÊÍŸ7ót>Âê}RN{“ÿÃQU½’W¼ü/WUÍ_/lÇ7Ït¨jnÖ¢¿Ý"PÕ¼`Ð0oÐG ªybë^ ÏkãUÍåú+¯ï‚×Y[ªšÞïÌ#®”PÕ|¢ÏÅ©¢@Uó¨/Àq* ¿Û/dÊ!0Z|PÕ¼tfW8îRG ªùÁÓ)Ri¥CwØÍ;,a«5 ªyõ“|¨±¹èïÎ7-ü 6 èï>ÿ¯/«Ö¹¹ÿç–ÿæOï›,ÈF[B«Œ/³À¶}È·Ìø÷·¿4\‹ÌkêÄ“¶ë‘‹÷8ó‚%ª¶cÖ™›åº®ë ös½@§›RÖ{®%;tº5)×âºKбWGjvÉÍ2£#ÙÝe4Ÿ_\»dÄEý`èš+UàwZû|…†×È~¡áG›,z9Ûð]i=I¹~Mfsÿ¡h`çúüé6m†No }vHèñN=ò%¸ôxÆ/èÞGƒ”{þ_oU¹[K¦\`_×H¨aR3ÖO#+Sí¹oÕ62uGGnÜ[‰žŸlÏ÷¼Ëgh³'ÿ9~ ½°Ü”U³zùãrÐó^IZD^P>}IB¯äÖ…¦G*×?qteæG=@Ó–,Vn‘²ºÙ~„†<#+gøÁ¼¹7É+iê<Ð4]Béñ[CI';{Þd{#†>\Ûõí¨\¿aûëòòÍ> ñ£6;{5|ζ\Ðê‘# ¹Þ2$8Þ-ø€»@ÏÛÁ¬ {H¹ýN-ÕYtÌzôëÈ Z.'½2¢™~Ð*%ºtØ:Vï›¶@åæßÓ«Ë/ùäI¨rmcn]aNþÁ£ê_“ËR#Š$ôéñ|fØPM †:ñ;gÿbh¾AGn±K Ѐ]~oí<ÒõèS˜˜µ’´L>#í—tÃUØ7r8ÙÇìlr‘Œ¶kËÃÒ“rýôÛªlÖΗ¡3ú­V~±ê ô‚nŸ%†*¡óöDÒl_}¾X”+ÑòøF¼|¬¤@«Bo˜ô@@ ý“ ~#åúm¥Ím*v2T·JÇÖ?FÚÃxèY&¡Ã«¦€éÏwdFà°q>(ÐNKÔy“ËH¹ýfUìàþÕ½7kÓ,\O&¿üÆ6Z/'‡jÏ‚äíi¤ÜüÉ,¸óŒž 9М{¨›új¾î-æwÙX ^ÖØÄ3mIçüxî ¨Õ+.Õ#G¾RÂÄ)ËÉ6– 40,xñÞ†µhx@”»é“÷ãÚñ楃ZÕY—§šTöœ¹…²Õú'JT­£pU?ð÷ߤÿW‹c¦Ð­<¡3|~1§Ò\zïCcn´éC_–˜ð¸×­Mv[Ñ·MIÛuApwˆ>)×Ï"é\Ð>ÇС-k Bó¹dÒlèzF] ÑU @¯E=ÒûD}w후6I3àÉÉ[•¨Ü~çû™wò&nŽ¼È¼¶‘//Â÷sdV—;÷WÐÒÔNÀk**{T §°¥][ ôÊÇT&ì8Ycr™™vªMÖòÿ̲®÷&¯€Nít@_Zo…çÍÏÇä³qVÃz°ÃZÜqŠ”ë÷cäЭ®dèÒ=¯àî´¯¤ä²ÊRº ôlj,ÌJæ ªÝ̧ 5»ú^fº*û·üËúàû+E £~ZBêÍýdÛÓ®|TÄ}†ž,µâÇ—*7_mË@æhäO5iÚ—ºäÿpTU/­g¶è»Z ªyša,LyPÕÜcå`°~ž¡ªùÎ]Æ ZÚÁ®a •›?ìê hX³†¡ÅuàÓ®))÷ü¿Þªr·n<| <ú¯ÔÀ$Ž˜ýc宓`Ò!žÔÙ4ÉužipÇÁŽœ¦· LÆ6#öü„™ó§‘Ë®€«ãRòMU Éš.Ð^óÓ`À‚LR®ÿcŠб«dè¾™Oá^úw²Î¼Æ|bÍ@÷Äèðè󡤧cG~3ë“D¾îÌÃÊ™¨®·-,Ÿ8¡6Ó~0Ç9%T®ŸVv%ôwh“650ðA{Ò[ã:˜4l@æ»ÞÓ:¤}ò&Y/Ô½&ÊÍÝyCåö(ÕäyM#:~.OŠ_G®½ÖL'˜(PÍâž0«×%*7ÿ¯³Öæ~)5\Ö&þ\+¡ð¨þuS á¯×ºâëQPÞXMÆ ‚7Òvò¤¶ Ì©IŽx²¬³ É‘×ÃÁ§J›ôµ "¯M$7Y/³ä…•Íù’ù+Z¿´_T}[Êõ_?ò1|ï èì’pp‹!Ù÷Ý ˆ~I>ˆ¹‡¢I½G>#÷C?.²â+üÝ Þ¨ï1V²“”ëç~Wƒ¿¯e%Ð>‹ôxêÌú¤Ã´ûpÜT“L0*ƒ…Í¿I¨ÿ‹vç¦@'÷‚`›å¤ÜþZ–ú<ût¸@’ëñe3ÃÈvƒ¡éŒ¹:·ñh¨ô8OÊÍ÷½5Ôn P˹~PgÌXòU­{Áæ~·ã¡³å¤áëûÌÉj!9±Ñ$¦!<ÉÜsàiÕ†¾zåóº’†ãàÍÑÚ€Þ™9nTœdh™ž!/·ÐsÖ‰ =v;)×ÿZÁ]ПÜP˦w`ãkrdÏxxÌ#Èî-—AÀîÙd¦]®¥ØD~jý²7î%•Mðk³Cj2 ÷=d'¡rýzeò†Ÿ$ô{ ÿq"‹<¹° º¿&çæ¿‡¹%d@ÝÐæY˜@£¦¼†Y£º“rû pËõËš;Ö„ëÍ›KÚŸ™ sv’£m€OQ¤Ü|u¯mÿÜ× ´pð-xŸ²•üƒGõsÊf¨¾Ð-Vëà,ŸNª™D)&~Ò!Ý]j2ðå ùÚ.„Æ’Éõô`\Ô=:u˜7\c#šXÕ ¼^R ¹¦ ñL[@Ï·Óµ)Y®¨\ÿE‰×aÌ{@ïô¾‡O»zëBª•ù)¥ä%6"Ó>{^ûȺŠ%°¡p¹nN+^š1ŠÌÝ|î]Ï åú fMyr mü­9/O/S¢þok`ÈÐ Zí­ÎƒSÉðêZü†Î5Òç]-®Hx¯DåöÏŒiÌW' tè3ÞÌt ù!ê8lwžBÚU¼€ÚŽ3I¹ùëöéòùš«Ú«/|݉üƒGµ``Ü6hÒÜ…0Óhiqh½²Êƒ)Ñ'Û(MŒŽI¨ßäJvn9¹"·šÕaUdC7ÐÝœ‰Æ¸BšZ Õíû Îûg¯‚¿Z•ëï£w"\º:¸ð4txÔ›„ ­aÍÜk íש%ŸLn̰‡z‡j¢üȲ'’ìa]ûxò{|¶pr!CåúíœbÉ]ö tÊØ<¯h CÍltxì¸åÚåhîx´9é¿à°[”ýöÖù†1TnN¼9ÿ^2P f“[ðoŸ{»Öã%•]Éa‘Ö|iù ‰”™{…#¯íÜ[B³Ãùæš<†þGUõª‘¦Böð>€ªæ‘ßüÊyÖUÍ­‹›Á‡7½ªšç¯p…!ï²%T5ïå“S·¨j.×?àp¤÷¨j>+ÂÜ•û¨jçu‘•Çd2T5wj±}<öPB·ßîtK>¿[6CUós‹êqݾ†JT5ÿ²ý#dïÖôw÷ß3lÉãf¸T5oêaÇíŒ}ú»ó-uZðFfú»Ïÿë˪.wþsËs÷ -n_9\B÷J:<7ÊŠØ©‚mPßÍÐV§ž°ì% ´bþ!X;!N š¦¯ jÐrêt%|}>Pqz#Ô›ÙŸ4ªÖãwN‡ ´pT{îß+þ *מ€úµ]º’à_Ÿ;,èé0}¾v 'yÔ­6x‹¡‹_©ñÆe¶€†½jÈŃjSGš&1T®_®ý<ØŸu¡­Ý'ÂúigÈÐúáLEª@Ù*u^xt9£Få«F3túFsH¶v•P¹ýÒÕÙð¼ë)†F)fBÿ‡É…M¹ˆhè›×`°q')7\ðZPÏ«'Ðé¦{F:rÏÿë­*w«omžãwS‰KÕã?Ø’óÂ>²œ6äÊ u!öÅj ;^ŸÓœ&Ð}][ñòÚäÔ¡cáæõF€vŸÚ úæ`¨¯‡/>½P;ïPˆ<²—”ëo¾mD»Úãƒ?mï@.ì`Âï¥}“Ð¥…ùŠGÝÉóמŸ¿mä)ÁŽZ¡¤÷úÇà™Ùžœ¾7Ô¥Ö¤\¿nÛü€iïb¨´¹Ô­JV¹ñv¹+wµ9^Ý›”ö‚WÉmI'ƒd2b)·¿ñ…ipaw C='N› dÞ‹­|u7  ÌÀcÐBRn¾Fý< =2® F^nGþÁ£z 0ÿ„:b¨>W>9©@óÛ€ŸÖ- µ¬î CÓµê’cÏ<3ÑS}ÚòñŸ4}ÝÀtµR¾=Ýž—•(Ñ㘺åW†6ØÇ¦×2¨\ÿZ¼ pŸ5  Ê:€³«1Ù7»’«@«¯rx”¡ý6ª³+:¶+Ô<™FßQ½2ÔØkÄ,R®Ÿ[JWpw†ÞÒJ#K¨[Rkþ\CG #ûÛñƒ}æIèŽ/5p*v¦@ý{Xð¹¥…*û¿Še”ßÂÐo'@Í®õdóhVpà9øÝd %¡róëÕ©ÃE½–жsñ+I÷•è<ª†Éú¼Û ]¡ÏÝL!áJLéÔT úWÃÓÑ­I=¾Ød! lßÂݻÉ%š ¹RBÿz·æiž'&ÅCÇë®TçágÜH¹þOV™ÁG?5@ˇՆ܀› ½æ®Íã6êjbýÞ>³%õ¢KY•i1,{ºrC_]ìyó(ÑŵÆÁ¸ñß%T®Ÿ®k{0ìrÌ ýþ¶-äÀ6%Úû__’¼¡¦úuø—ˆ³¤X 5ìZK*T²!çׄţŽú)r 8û$3ÖA—Ÿ%tl“PãØ@ ‰¡Ü=Ö% Ÿô¬?0T®úÙw¬àC±¸[,¬Éæ _mhÒ‚-à’¬NÊõ¯6ÏaO[Å(P»ö§Yb½ñJtIóT(ê¨ÿü-àÐÍ‚ìköšÍè1M úBã¬í¤Ís3n®ÛUBs$þ±¯ rý:]uþôª„. ¬Ôè੺< b! u߃[Gª]½ì6pãmê=j3ëú«‡@åö_Õ Wúz2´‡ÛHø^äDv×û›:TJh~V-žóÖ…”›¿[§dU¬Ô &œiÙ“ÿÃQU½iq/ÍÆ€ªæ–C®Á×'úUÍ#»\WŒî DUó{?AŒ£¾@UóÀ§c!§dCUs¹þ&ÍN± »»I¨jžc²*›èªšß°OZ%ªšgÜ˃Øèïö3Êî IúUÍ_-¾‡WmT5¯ßb%8MèïîŸâ³l¬ªš{}ø ;¾ŽS ¿;ß0l¨"<ûýÝçÿõe•u^ÛÿÜòßíÜÒÌž+QÓx³‹„n­¿¤Å =0L½bHõ¨A°ª–„ÆšŒ‚î/SH«—ßA÷u ,ât{»tÞÑ–[lš¢@›çªsòU€ÊõÓÙ”ÿüÜ™¡CÍò«·ãI“ª"ˆ9èÖÌcP:xùðíN°ž¥+Г[ŽÁB†äl C8øô–}~§-|¨h$P¹~Æw¾A¥ÇF@—ÀcHÍØFÚ¥¼†^†ÍšQþ>u¨‘ÐÁ–‹¡ÅÖTÒ3(tK_“rûï ‡ñ.=:õÑzÈIèIŽl1Þ #†9: ;.W¢ró?Ũñ‰ÙK™Ôˆ¿5"åžÿ×[UîÖñ?Û€yÏÅúe–#,ø¾ƒ¼áêC‡2Ô­¤/äœ)S >çÀx{-îNÜÞ•íH‡»#àü¨€F<¼Èô®w%''MÍZÒýffòkCåúÛUrç1÷êYZ‡ÛôÒ´îöhÝîK~x< ¶w$kÖäÃåx†Ù<ÙŠÚdÚ¹ýlFzÔáíæ{‘rýêôÎÇö;m÷2âWüãË/jüˆâ˜„NªRçÊ_ryßS`¦¥-Чi÷Á¶û •Û_:e”üÕK ±Õ±Ð>¢7i5|&Üí9KB[¯Yð!‡”›° 97î§DC®7äî‡2ôÕ3õ¤zœ–Ð9i^0¾è&Yý°;„,Š?ƒ6Üéλu%Ô"àèŸê.P×Váè F jÈ«†24l;ÓyzAB›·€;–:·…€à² R®ÿ0-m.òÍMoø 2Æv$µÜ¼` •Yïó®ngèœ#5`íy[Bõ$m¾Yùz¨5ï$h8Óá÷',T®ŸEƒõ°"3пÆC7Ã(ržŽyr«­R‹»WP Ý‚geˆ„θ¯ÍG/·d¨ÜþV³ ׸@áñnh­óKâÀ¹¸é§vb/“róƒÔøŠ£=õ,½M–L&ÿàQ]a=ÅUHhP÷¨^òƒLö÷€ ØÒ¥Õ˜i|“ô(¬Åï4hiwCþqð. SR Î/гÍCaðŠXrà0M>÷ê Uk­Á¯øy0T®YÇw [Ø Ð´Œ2ðÐó&okÚCâÀ\ÚåPgxß$K‰.,®Ã"žº¡½µëð›ñ;zÖhøùìT/w%k]¿>)×oVÏNY¶л#êÀÆycÉ&€ZfK†6:ð²Ú…’ƒ|>ÁµY€úÝ:ûûM"åöç±}0t|o…†Ü޽È_ŠàbOCòvûxf}KBåæ{Vo†a‡ú&ÉôZš“ð¨†=™ELG ‹J–@P²4|LW‡¼œÙuJLx^x?šäÜ€{¥=ch‹×Àgwœ@:êòîf“?m ¡ü: ‚÷€£ã?Êõùt ô¿ TŸåÂyi(Ù~z_0û"¡)«&B³W‚T¢Á3BZ’^ÝÛ "×õžÏƶ­‘бC¡£Í•ë—Prƒe­qô¤vë°F›|³¯bÓ÷3´ÅÝ20H¿DÖŸéó®õ´§±9>*·ÿä‘cÐg~O>—2 E§ä@ÿÚ<ùæ[%ªp®ÃÇÅm`¨ÜüwšŽPäŸÀМPëµ÷ úêkU÷º@³-Öƒ‹½)ÙÝo ÈíFžñÞ_["ÿš<(ÅÐ9-îÁè´Ud˜e3ÞóÙw ýqÞ’{Ôg 4$f9˜><ÂP—ˆA`§¯@e…'!~©/ —töãRÒØ! BL*$4/b3¼ITè#g¡èñ|@•7AÿvKÈ>ÍÕxá°EM™ÚŒŸkÜABåútYÀtú¤0ô౑lb«w Tì)ç¥ Íñ»6^ïÈsÃ/1£Cz ,[/þÚ@Êí7? ÁƒYÝê;ç wædZÝŸPSlè–ª|x˜0“”›ÿâq_6¥„v™°>µ¿OþGõÿ¹œ6Ãæ&M©r‰Cg! ¨j·ìDªš{Û›ð5÷jªšOûíïI¨j.×?]$ôT5Ï÷KI¡%PÕ|ÆÎÞðîìh@Uó³ôx­Õ€þn¿à¨@–6ç„Uͽ,s êî7†ªæìmœIˆèïî?crάt¨jþsI8Ô ôwç'ކmÔú»Ïÿë˪§Vÿ¹å¿ùìPc>aš—„FÔiÊËU¢E>ŰÃM] •úOaõÉÛ?ú!T¸*É{žoÿþT=• Ï>¥õôšÚ­ öt%¡á¹ ‡ÅV†kM.&*×ÿLY2Äžû ¡Î B­kßÈPçW¥h•­:×njBZ'gA§íä×J0XíCº.ÙvÉ_jã+ƒbI¹~õ¦”@ðœÙ€>¨“ ùՋȹBŸréB½þzø’Ý|/±k= t†æZèÖ`)·¾Esnv]Bw†›ó/}‘“f5¡e¨~ëÃlôp}†ÊÍßßë-»ú0‰¡ã~ä³Eƒ;K¨Üóÿºänݱ§)wxÙ žß”7= ÏÐó³+áòê õZöVmíMµ¯¶ÐA‰:I5âÑ…¡û¾©Ë ½4H“«™.rC“†Ç@öüºG÷ŒÐ(#åúëí>«Gª tó²LØÛ¤é¾£.™pJB=n5àúöÛ”¨}ßÚüö±Æmð¹!ïpt­„N‹ôïÌFäóN«`—©¹@åúùo? 7-T³*´¶Ì"ª][æãI~嘆 $ ·}† —– ôžÒŒ¯üºOBåö?hÁEHèÎK~$À›Ü‘rƒ™¿K:ßì·÷ϨÜüØm&Ð?©­@Oè.‡cöËÈ?xT‡-kÂß%.ch dÊãgfÓ†øûStš5ð²Õ»¡_òÞÂCÛ }»¦æ;4´úH-Þ5éC‡ž¬€Ô+€–}*‡Ã0OB/tùþ×:1T®íèópãË/ “u’Ï~%WlÀw_ŸÎÐá¥úüÓN]@niÌóŠß*Ð4õ¼cku@Ÿ«ß-ϽQ·.÷-¡rýúßX•ëÆ:åÉ(X<¤Yb¼>g'7Ì]­Æv%gšð}>2ô¹×#ø$*·ÿx‹üµµŽ„ZÌiÁ‹ –*Ñ’Q'ÁcI¤@•/Ôy›ÓI¹ùN¹ ®u„@[|Ñä‡ÓÇð¨žq2á¦+ß2ôÙK#~ßÎÐê oà⣠TæÚ|ÌÐ'mNÁ³Zµ] ßõ:’ïž ð5éGzƯ…Ó¡}È\ëk°«¯)™Y².ïjBÊõŸR~ Ö +¡Î/ïCÚ¶R²ýœ_p>¶ ¾Üƒü…óÈ ùêüu_2t`!”¯$÷åÖá·õ¿14ðr6¸ù-T®_uM-[@s†[ƒÑc²ƒå (laN–é8Ã5þŠ¡Õ÷—¬²‰€ÚÖûY •=ªK[ða»4ÜЈn-¸bûNÚfY3þ¥×} uÚaÉáèd†Êͯ,kÊCÞ•ÐA–|ÿ¬& ýƒGõË žfi èlg=~À» eÁ¬ECCúÝ…ÛZGɾ|!Ç´ NóÌÁmüV†Vïu×¾&€V…a]‡04ò±1¨éŸvCwÄúCêiWÊÕ„'0`ÚU 5+y UwOéVi1; P£K¡à9{ ©˜»^ŽYKz' ‚ÄosI½kP0F†=ÝÎŽ5P¹~I-`Dâ9†jFëÀbÛ¿ÈÜnÖP{þFrUsPŒ=¯@¯U@táf ½zy[ðÿÃÊG帷aã/¥’¤È”y&SfêþW¦Dæm’›2‡$šhP*•$I4Ý×%¤BÒ@‘2%sB¦Ÿw­ßù}Ÿu¿k=×j=®>ë:Ïcï}vw÷϶;;2¢PtvÈÑy˜ëèÇ•›Ÿí×Xhy½È’ý¯±o÷/žj÷§šìg€DÍO$·[Æ]’z u_cd×¥Wqî×ýÿëZÌ,¦$ï2ÆR÷»"9(ã5ûZ´“ë8³'†õ•ÈI¹eèY½ˆ«Ð0 .ôI¹þ¡?`Ü×"¹Ñê z¨;qX ‚¦b*ÈìÂJ–ÜÀ«5ã«s²'7·ÊŸåœöfdì¹k¶Y"oGVÀ#Å+×oÍYMÄg;+Ȧê!úÞ%%Á:Â*ý·©s?´>ºU$§œÏÂŒ*s‰l÷î+<ú牤Ü~±¢­0ÙÞŸ‘íCÛáÛÎq#ïlB¦»È\|`š3»qåæÛ Gúä.ûcDÆ îÿpªªO²ÉŒg›Aªæ­¼RÑàÝsFªæ·7OÃýßL"Uó¶½qæÂ‰TÍOÞÔ†,R5—ëŸÞîô|zФjž=7œ p`¤jnŸµ“é®"’ª¹ù¹v‚NÂ~‘¬m?íwF}wƒHªæùÃÆbnx¦HªæÓo© 33Œdm÷/Ñn-”ß¹ÍHÕŒl¢÷/®åMI¹~ºtBj[‰œ5Ú³ÿÇ ~]_E"YuËP;š;fàØŽúÀÈ£çû£,ÚÊœ”Û;Æ %Æ /av mÊmÞýVš\I­è§È8²‘+7¦ þKËyyþuVõ8EIʽ_‹S½þ²¾PgjH–¯j ¼ú4–«»óêh9IäôB áþ„ñÜžygبs²ÑŠJf<î½Hn~ô‡ûKdu‰–\R ’†OÆÂ=F~·X‹Ö߉¤\ÿãMª‘îR-’msÃÞù:·yèwvñ™?wÊÅN2ª¹D}޼Õ÷RÉå^åÈÿLj‘Æ^øµõH=R{C%W®ß[ëh•²X"‹Âa6gwåùÆÂËi#ä»ù†ÂذýŒLêb ߯y}M"†ÙŽäÊí?<Íï߀,躖¿t¹_lßÁeåñÒ)ì%Zž÷d¤Üüv ›¢ëÂÖñrޝãþÍSUè U‹o)É.õµ‹©dLˆ°öK‰ Öl!蟌IéóXTï!‘3þ|g¹·ƒ;w^C!}Z¨99ÎHGöã#Ÿoº‰Ì îù½¯Žp«PK$eªi:,D$ûj ÚÅøkÖÛ#_ošDv˜†.ë¹?GÜÇ‹9þŒl“{8šÍ­Nµ!J²Î£»ÈIÞÃH¹~Ÿtâ°0~³Dþ3. ÝÃ×q¥'ºÂÀÑuAjF¨ ÉCs'þB S=‰Té Š7! Rnÿì_+ðk¹&ÈÓC#fs #$æ"+³š›}ú¶ÖÑ)7߯ÇMŒX¿C"Ó´qÀý‹§:ädÁéMš‚Ìš¨'|?ÃyÙ®•>úkyÅÞX°ÛïÎHçÇá`¹V"bu„¸9=¸M<˘:uãÿUfjö^D&¯ÉèC¾XW³›0ÔHèzjwøØ†B£®%Œ”›¥ßF$6ÀÈKœ°TÃUIʽ_‹S[ ,_ Ò9. IGVrÕϾÇïõÝ$òùù:´ð¯"÷SKP–&02zÐ[Dúwù¬ßc¼ý¢.‘Ã{ÕÀ´Ç2‘Ìõ?ãúœ¨==ö0R®ÿõšq˜–zV$kÚ ™Ï#îþÊRŒ8C"·š« kÔ»sg\ƒËîýDr¤§Þ9ÉÝ­«/ÄKfJ³ÞÁ«ç@rýܪ¢ÑÏä)#\ ÃöC%ܶ…É&1 ²å¡†Â­²“Œt¸¿޽]ÓÈñI¾pé'’rû[ùûC·‡HîŒ ÀÐ _î0Û8Õk.ȲÑàÐq5Wn~xœ;¶¶ôÉþGà6á*÷/žê“Á¾ðR,ùÓ|ŽÍ°á†,©'ÄXîɲ÷õ½Ã”äÙ1ö¼˜ ²þEO|eÉõ^øå/sdÚ•—˜–}‰‘f‘Ó0ì–DYVˆ¹7-¸rým;;aÇP ‰ÜÐx7ö¥4â8è _t®ˆdœ–‘ð#¾·’Üù8 ûŠ‹EòÁ´óèø×Õß¡’5H±C[$)×Ï´at_cääažx3ç ÷²­¶`oÓ¤ ¾ t¨wYEæ4º%’-îáU· ®Üþ>wö# k°HÎh‚Qea\ï‘ÝaiÚdé»l…¸NAÊÍwOEFëk"¹5?Bïî_lÞ^˜7îR5ßÖ@]¹ßM"Uó²×Ç‘òX$Uó)žz·t5ª¹\Óeè‘ZG"Uó’ }dßxÏHÕ\,R¢¼U#UsçIÃ0ÃT!‘µíg’°Kµ|DR5ß8NZó©šïXvÖbS‰¬í~OŸÔß¡ÉÿçŸÿû5äô_ ²¶ó;ïÚ ×ùMYÛ÷ÿã™ÙPøo®~”‰…ƒDrI³ìý}ŒkÙ@x¬ÃuÞo(ì¾³WIn—Ô„5Ý(H£èwÐ:WÌHq­¡ð6ç½9Ù2JKØw¬.H×ü¯8±§¯DfÔi(ÄÎöf¤\ÿÅC‹Ð q#ow΃î°0nãb,ÚäÅþ|ff¯S×ç§áql,#ÏýóÛ"ë4×mÜcÎäÞúét‰‘rýŽFo§³Œì^I_q=#õ„2$®E©†°dacÏWé Ï{>ÉÐm „ò„XFÊí©)˜U$+Éé5ŹîÜäcŘtÞ@"¿Çž¥;DRn¾¹×gH‹J¤^ ¾°½¡Hʽ_‹S½W–Ø‹¤™ÉxøíæÞk \¼4Ž‘×ê$=ç¼–ƒ‰­@z½@ܘN\ã¢bL™ÆÓ|?¦;Íá69‘„:ivÜý°¿íFÊõ×i{ ¿âÙfÆ%¬8(qջ΅Ǡ¶"™sk=Þ¦áZíÞ¯{©sÚƒ_´äzôõÁÜÈ îûø-pŠÊT’rýÜ2Jѱu}“sp¡MKn`Ñ[èg‚ÛA™ÿϳ¸Õúù¾CÏæzÕØ ÕhWn¿õ M¡lzµÙ?QSha¯ Ûxý‚ÎáNŒÌ5)Ãòq?¸róŸþÔl…Œ4ºûŽ^¦ ÿâ©”ßEÔ…%"Ù]½öFwÅ“Ÿˆ¾Ó ¤Ë2ôŒÀWwâfÿf¤¹ëp¼Y>Ÿ;hǘê7ùi$*Rš6£àd×^"­š<†yÒ8®\ÿÍZ)XöO#,‹Gòˆë\ý ~([øI$W†ÆÀk½ŽDZÞØ —òÁ©ä<ÐI$#‘0+äV)îaÒ¤(®\?CE&\ïÒªóIDíÄMýz¯ÚqÓ¦ûbá—\ƒÑê8x©¥‚tk> []{H¤ÜþuVšB'û"y%JCÐkÜŠ‘=¢âp¤¾1È’Û‘Uç#åæE&Ãx•-H­+ÑÕJàþÅSÍZÿ‡K‹äUE:9ÝT’‘ÒE¼¿ä$†áCæs¿ú›¡E‰ºHÖ`‹ÛKŸróûãPA>·Úl#޵’H‡O‚«î&©\€SÓAÊõŸù¢Fe1ÒdÒ\²‘¸»]Å¢éu$²N«RTÚä‹ä€Ç˜v=;íÉ ¸^Hå¾L¯€Åúg ²ÇãK(éõŠ‘rýj&£ŸOkCŽì†í-}nŽb!_›q5úaÆÛÜÒUY¸snžD¾U¯/¤;䊤Üþf×ëíëcd|Šº]¼™k6 ¥î(Ș.vøxÂ[$åæ¿ÜÕ¹ Œ\U¦Vd*’ñTÆ?FòºJòîñÇ08Ù,l7mö-·©f4ZÆm¹íõ÷#AÙY"í/ãÑË^Üåy"*4ì¸[æ|‡Ùä^\»}ýñ;Ydõê®xø…HÊõWŸŒ—Î02¸¾6Ü8Èí<©?znÉœ…Ÿ`ºÎNIþ\"¢WJšHžìœq¾Aܰ¨]h7ù #¶.…—Y‘”ë'(WazóŒ0†n©Ü¸~†ˆ‹:Éu:¤Ž÷aG¤®žð¬a*#ÊAÕšY åö7Y§&X·d¤ñŸø Ãݼîÿ|ñ©+‘ÍË20¿Wn>šÅÇ~í%²YÛd»ÏáþÅSµ²zŒØ—F òÖ€2ôi}œUÐæsé6Zo·5åê •pí£-‘š›5„€l‘Ì=ÝP¨*_Íõ;©/œß½‘ó–g"(i†D®Ñ¬Þ7I¹þã=Q ¹“‘ãœÜ°qÐ8îÒMïi݈{zh9ŽoŽà†è<Â<›Ž"¹D¿. †˜“ºy‘8µFM"ÿÑ+GTÈ‘”ëgW3 áoÖ0²n‰9zjd+È1–uáœ5H$¯=i‰ýÕÅÜ÷q³ñ«®)Èžu‘^™lFÊíyº4.2ҴΟï5ܯû*]%’¿’«|Ñœ”›Ÿ÷ð =-%òîØzB™÷m‘üNUõy¶ú!Òb~)HÕ|†ƒô;×(IÕÜuO]ÁÅS‡‘ªù@õj<[:¤j>fê+Ìö R5—ëßw§ Œ”õ©šÇÅçA+ù#UóI•aU `¤jùì4ôcdmû}½8·SG(IÕüÔî¨lÞ^"Uó5A³€”žYÛýå¿?¡à#UóL­g˜t–‘µ_ÚPø£Tµ}ÿ?ž˜|=á¿é¶Sžåõà5CŒ+Hëè Ày_‘ÜÑõ2–¯ßÁuîá ã n ç^‰ÝîYŒüºh:ìZ7•ÈeGŽAz2Ž{lñsLíÓäË£s`t² W®õÛÐð©f䯔,l‹« ²eÛëXtûªH&–ÂxÓn–²â Z…I˜iÙšû+s?fmQ“È{ùQÒ0_$åúUV¸`Iì« åößÚæ ?ïÁ “Ã<`œÚ—ëÓæ%Žíê!‘6ë Òw[‘”›¿r†1䛈ä!Ýy°yÝR"åޯũ>OŠN %ÙNáÞ]‘~ïêTŽá^‰-À”ÅŸ•ä´=]¡6 c2YV5³³ERïm9žmê/‘3»ëŠ'D²xF/Ü 9Ëóä2&ûYI¤\ÿ1çSñï°ú ¯8œÆüÿkHÒ ”¿|ª$+½úó1XA>ìà•ý @Zo^„/#- ?ãñ™ÁÜÕk’a²Ò¤ìÊ:NHŽõ5'S–¯‡ÇÝ8%é–›ÎéÆ"élZ„ Þ»”dâŒ}0»9K$[ß»€ž>G¹rûßÖì@»Ç& =,6 J·#÷Y¨–0âõiF6s몆‚”›nÎ)x挗ÈÈ÷0xÛƒûOUí<Í\(’ÏoŽÃ†‡¸ žÜÓƒwi¤VÛ;ˆÒrS¦™ìL$Òh£·)¸Á ë «cû3rXÊgœmÛ ¤P_°8*)ÉOïýùsMR®£Ùáð§ Rãs†tûÁH·eha¿•«†[Ø|àwZü(øSz™ãáfn'’×#"h‹#{8¯Àç‘”ë÷ìÝ68X™‹Ü]»Q©íÅíY^« ÌÉß%…èçýVAö/‚åîÆ"Y¡}³[3Rnÿ¸ÞËa7¤Vþ|6àê$ùã°‘À­Óy »f3RnþØ3õýî"YÙHWÈm´‘ñT«ÞÎÄá½WDrÓÁ%¨ø‚;eS´SŒùjm6f.°ã*ê¾F©‘D¾/«+lY»U$/¤bìø ŽY—Ê>ܦËGÁì»:7ª³RÌ^ˆ¤\ÿóqð +cd—û[ÖNäzx'ã¬ñ®éâp|¨ºÇ]Yo U‰äV«”·n#‘'Ê/ãCŽ!·U¯h×û‰’”ë·¤ÚlEœHúá &es%û›ØnfÉHƒ¾´Ú…k®uÑ¿s7>Ûˆ\¹ý»‡NƒqS-k6üŸÿ_ØgFê7i‹ÝRG‘,®ë€ãEí$RnþÍ—•(IêR-ø4¤Ø Ü¿xª©{6Ât„†D^HÞ…Éë ¸ÉïE¼÷ö`äÝf xVâÇUxj ï»rÍò?£Îg-q:¢,:„‘—œZ¢÷T‘ôÙ›öÇFHdè2=áE÷dsR®¿]ã•xúk#ØaôÎÑÜÖݼá50†k·|Æ Ä=·òBû·”È•ˆ¼øT$kæ<@¼ð‘…‰žpLÕ)ׯ$åV‹ä†s‰p‹)á¶4IAÛ?Föt<*Ë}Üeçö ¡Ý%%¹sá TµÙ/’rû{“¨Œ¼uÊ ÊEÜÁkRÑ|Ñh‰¬;¡U®õ¸ró^ÌÇòmA6éoŒ˜•Œ‘ñT}¿øBû‹±Dª„âQ`n£ÁDZ¬¯'#ÍÃÂõÚ"®ÕÁ,|ûjò¶Û>?ÈÝØÞÉb{‰yó4¬{M䆷ÍÁ£AN½×ý¬î0R®¿_úŒÛ–¡ µïÌ‚ZFF*¹ÙyÚµï‘F®pÝŒ£ç¬EÒyŒšpa¤)×í©šð%ƈq-Ç fk„’\å},·©DÊõóq¼û¡ù"©4»‰ï­•\©_$V7ÚÅÈG!xùj*×áÁuhwÿW$ÓNcêÖÆ RnÿÏÔþ¸ ÃÈ×y½°å”7UÔô:§‘éN¿Puë'#åæ·ué£ÙÑ"9ëÁv$y ”ÈÿáTUŸFÇѽWW‰TÍ×® øóçHkFªæÂÆ£åïŒTÍËL*a°»‡Dªæ¦›¬aþº¾Dªærý<˜‹„;÷•¤j;{tÞ$Фj®£x‡ËQ÷©šßý·ÔrE²¶ýF/+Æ…C"©šëzûCw’.#Us·¢Ëø\בµÝ/TwCŠÆbFªæË4/!¦óHµ¯& ÇKdmßÿ§÷0á¿ÙåS>6º®Q’ó ò0.ÜÒvïŸ_÷ErŽ{¢ÿäfNyŽe>︆E?‘êÝk´ðÏwªã’9é÷;«–ˆä = aXÏXî2Í÷ê£R®ÿÅÇO±{(’%ÿVbóâîØ_eøþÀŠ‘Öu à;<Ž;eÃ$—8‘tý½†ÝÚH¤UB>>K¡¶8a(9³ÇOèÔ[ËÈðÈÛˆH2)7ßÑ4÷ìýd瘵¨HIʽ_‹SÝT]‡{ÌÉ~ó ÐVZ¨ ‹“ i§.‘.ór <_!’»'þ€eÆÈ}‰OŸóŽû(ÿ$bš9‰ä~—‹¸»qW×n#L΃Ô?Vžn")×ýåXtÞR$ £«Q¥’ì8J‰eÁÅŒlUv³–ü_ë…Åb@=…DZ'ßÇ šnÜM_B1Ø¿#gM ÂFí¯JR®ß’é!˜2²/ÈÀԤݎ»¶nÒHäÿRhîý,’Ž7—á~â~n;Çxôëc(‘rûôþÙ M•¤Fà2$Æ…pCmBs¯5ÈK‹&#Íä(#åæ³ŒBî»"Û# ¾‰Û¸ñTéçc“¤ ]uo¡û1 Föûü±É"¹jLöXÌÕ¹ž‚—fÍAvïì‹!~ZܱOrÑüfž’|—‡‡½ rïQ%üf•H“Z‚–vº’”ëÿäí˜>^eN^v«FaÅWÙ-d/朎eäñË[à˸g4Ô„+±…"y¡®Ž þZ¥$û'£ãƒM"9Ö¢å[/¥‘rý¬¼âö¬º 5ŽFŸ«Œ Þðþ!gEÒMç+‚†pSÍÞáöËcÜNs¾`KÒrFÊí¯Ls@EJ¹’l¹ .ÍD²÷zØÝžkÛp'ʲõ%RöT»äB×H$Ý/„MÆ ò/žêË?ŸŽ+l;32±é5L‰Á-¬ÿ½WîT’Î¾ÅØ³9 rʤxnÀÈËUa±¯µ’|öä Œ:Í`äSË8¸›¹r?î/Dó _ZOÄ ÛŒ”믳æ#Šõì¹öÞkàöQ«`ÑüS*™»q&‰¤n–†`“y„‘‡Ä—èÜÄäÕ'®/\ÇÈ.•ÁX—ן+×/þlèþ—‘Ó™à÷¿‹d‰Åw¸LgFÚ?©Æ™6 Fn3»ˆíÓ›|ç´ï0Rnÿo‹µ¸¶Û\$íۀ̓l¸#g¦£Ý­îù°´& ERnþÉö×°>n#OK€q°÷/žêzË«XT8‘ùê—0{øZ®­Ës,}¾k¼ó..¾TrÛ¼[‰Ðàx‘üå{&èH¤Zñ X<˜‘úû¡¹³—9y|ÂlͮɭQ÷ þ®­DÊõÿYýÑ)¹Œ[ßE^Üví÷B-$U$ÃÜ¢°À¥‚Û9, ¥ƒ ¿¼ñF›^Üq¶h¥ÕH$W]KA¡•WöSm^O¨¥T)I·'Cð6:@$§l|â“g¹Ð²;Ú½åòƒR“"Ù~m0¢¶5—H¹ýA{‘>s»H.h²ʹ¾Ü ®šB]“Ý röêJy­Rn¾ùÓPœ¨^ÀHÛÑþ=Ÿž­ ÿâ©6´U"3m#Ã7'á¹¶;÷ÔÒL8.Ê]:);Þqm/_F—÷ºÙxë3d_LÉ“ ¤£7Èþ$ºÝtç.S×ÄæÍÙ´o"¼>ö)×ÿAâ ”·×)ö—p°¿>w@ó‹˜qêH¤£Ðñ2w½ù8¤\}ÂÈùúÝ †‹ ²NÓ»˜úMWIÚ˜_Aæ?FÊõkå4±ERëêJܘh(‘6¯¢àH='Å!çrnx£r˜;¨Kdö”:BžÏ,)·¿¢²Éþî8™zŠ{áá¬è ò©Ñü;R+7ÿâe?,ro*’n3¢°Îb÷8UÕ'À!sz2R5wõ:Œ#Ê©š7 ©Fœ T’ªù½é—”ì"’ªy›½@ГAŒTÍåúolüRªy‹½¯Ðx¥­Hªæ£¤˜¹ÝW$UóÓ‘e´‚‘µíºÃ #§u—HÕ|X·ˆæ Uó!ïîa€ºÈÚî¯ÛßÓÉ"©šGÖôAz›Æ ²¶ó3']ÄqÛ ‘¬íûÿñ|m¡)ü77wu…rC]Ï9B¹E;½Êöƒ]DòHR\¾å¶š‚ÁÏq#|ðÕº¥D²öAÙT¦ _‰þðÜV$+À°ú=÷û’çˆh0‡+×?Ê;£ÞŸdä¬áøgå!î©øDÜ=×䦋¾¨J2æÖX_Ǿ{ké~·7„s³¯zÁÇû<7=l3†µî*’rýÆš¿„ZÎ%9·è%üv;(Èë/vÀýÍi‘´ì€o}˸K>¡]Ñ FZ6=‰h·® åöw;>®£Er[U¶í0çêOVSÎ1rîád£ Rn¾e]Wàe°‚ô;¼ kŸYФÜûµ8Õ0§ùpu|ÀÈï&aó”óÜ+O#÷ÙY‘lj ¢¾îI®÷׋XõOW‰tûü®9U"Ù¸ùqh›s3Ç_F‰E·´ãmÜuŽcdý`_”š‡råú—^ éFދܳ¾6ÜI ñ4¤„ë5 +î™po…C ævÛ³9ÖŒ{Ó7¬n‹¤NÊc¸¬»­$åú•—>ƒGIwFqx„M=¸uæ@«³_ErkO%ÏüÌ}{b,¶T02rà?x{,I$åö;f!úe/‘ôÔ¾e^n¸—®.Ïedo«EHn¼NAÊͯv ÷¯ŠdNHšo¿Éý‹§:Áqn­ödäà Æ+rí£®£rº·H^w¿‰‘f\Ÿûê¦tCnƂߠ³–‘í¯Ü…îŒ?üÿöÉ{€ â8¹q¹'¾<#’ß\†Ã´®\ÿÖš>pRëÈÈãFÞØÿO†‚ìÜËu *I‹g6-¸*’´v!¾•9é(zb³º™HÚL¹Éš¡Œl\w7®\¿˜Ò;èzŽ‘9Êkˆ\w›û~Ü- \,’mN?„ÝøHnb^öõ”ÈÏg¾ãÓ¹‘”Û¤à:ÖOÓɼ•9xbòMIn>êŸæ!"ó$nìzË•›ÿùט6\ ’³_#¡Þ ùOõî¿CÐ|i´‚Ìí:NËÒÈËï!üÔ~%Ùaé=X=í¢ móÊ÷B䔂T%wäúGÞ„ÞåFÎ ŒƒWÏ ®Âï.jl"äÈ–§ÑÃÖ‘rý=_{bR·Ïæäo/86o¤$ÛJÞˆ,1”Èðg0`QGn ƒÈX-’Šñ‰xéq•»éϧkdÕ{%yªÕlŒðI¹~†×Ò ?æ%#-ÊbÑ{çîï„—ØØÄL$Ëì+Ñnç×4rе_h~ˆ‘™Æ)x‘Ô¤Üþ »7Qµ°PIfëÝ‚GØ)î²;·À‘|:òL ·+I¹ù_×ÞÅ¢ÄFn>y±)_¹ñTK× Eù«'J2w´€êë“E2»óm¸V4f䬄ëx°Úžûbê.¨»jƒ;—Ü702'Æ›¹:ë7ÃÉ{¬‚ IòCYÚ§T2Ú>¶;׊¤\ÿêò=ø˜‘­$'øacï6"éy¾þË$rìÀwÎç‰ä÷%¹(®ŠæNØõC­L¹· `ÿÈHInSˆ˜×~1#åú­ù†nÛŸ2Ò]ÍÛ®s¼_!óØgi^Ÿá;©õk¦^Ìáê2 Ñõ¶ˆ¤ìwy Ý"©šo {ƒ9JFªæëëgÁ>¼‘ªyhóKˆÏ(’µíiü/2³)HÕüØ®3¨û­˜‘ª¹“¿š ´ÊÈÚî·8”¹ÇSÌIÕ¼ù6tZb#’µßòJ6~m­+‘µ}ÿ?ã1jÂÓtÉMô¯3œ‘sv^CÉ;{îä˜Hütšëb: _cÛq§¹¹¡ŽÉi%©üwfî É•É'Ñõ¦#×íøeôv^ε>š‚É÷%F:Þö@óô£ R®ïV iöœ‘CŒCÿó®é{xÆ]àº^²ÆÞaWdàÕ•˜V?O$mçÄæ-M%r}•/⦸3rÂÏ?¿ܧ‹¤\¿ì½E˜S§„‘5K³pùÄw®«*á{ì„Üê¢,hTꉤZÛ,8§”*HÙÿ¾.#þUc_o¤a­mSnuüÄ(Ië÷qب?#åæ›ïJBúê;"‰÷q;%ˆ+÷~-NUãA6Ûîdäzß‹¸­ðãÞž€Þ¯¬•äÊÔ9Ø’·C$o´8ƒÌŠ\îýK·pkþynFû;HowLI¾3¿ÃÞŒ\;ãúi{‹ä®i· v×GIÊõø1]ß`¤A',²<ËÍé?ɇ‡ŠdQÕ Üs›ÛÀ*Ru3‰Ùí5>^,Éa)éX{¶Œûd÷{äuîžFÊõ[¸ôr-Vç£0l{]nlÒä^ñWŠåëà·ñ˜’lý*>a™o Ó!婤Üþn+ÏcÀÕf ]4ÏÀÕ¨9÷ÜÙdÜxÏHÿ˜o±Ÿ+7?Vÿ ÔntJzø®…QŒü‹§ª•–‚ 9Ìí‡áÎsÞŒ¯KD²ßɬӓHƒÆ/`ØZ$‹ÎV ¬®:#熧#z@8·(.UV'¹¶ç“ñÜ]CÁêÙ›“rý?6÷ÄUæËÈw¢lÂDî™v¡i¤/‘ .)1mM#î¡Â_pqú¡$݇8a#Ç6È„þ®_\·Uø…Ã\¹~c½`TVÍÈúܰ°o>W{§ Œ[,ɹ{0dÌ®Pq ^_FpûF_ÃCõJ%)·¿$4ŸÆ5Ù†E¢Óª&Üdõ­°]§4'Ÿ¹xÁåF°HÊÍÿÑ6iú¿Ù*ѽvßãþÅS­ßø4ª¯„32Òÿ8ÛæXÆ¡Wl ‰¼rüσ^¹ÑçŠÑáS:#gY$¢Úã'÷F›ÝØÒÝ„[Ä\0øÉ#%é·4(€H>~W]'*H¹þ}4¶ãÌ— Ò9À:gš“o”âÆ·"Ù:þŒöíâö«ÿÆÏª9!,«2Z€Œ;¿é-“Er`«\4Ÿ®'‘rýõZ…6#1ræ™yðÑÌusOžˆäÜ· 8ô¶Š{¼ú:†ë3²sA4œ½¸rû;|9ˆ‹[ ½½k·êsÝH€¹ñ ‘¼{õ!®î;Ê•›_áhGûß ²‹b Ê~íÉ¿xªÛ{„£hT0#-õB1Ýj·Èì ô†I«ì*,³ çÖ÷Ú Ãó÷Y½1òç*¸ómaPvA$ë NE©Éî!Çh03GFZ/òAÚÁÓJR®ÿ·êmH4X $=¿¸Âþ¡®H®¿ò¶ß3SÉÙÖïñíÚFZt÷Gó[º G/Z„ÇÙêý/¼é>IA&¼ŒoÍ[€”ë—×qÜ`¬ GŽ™ŽW-*ÉOB6>ôy%’§ƒî£M³tîmÁ >ÌQýJƒñä ¿HÊíÿYæ‹ §z ×uõB–ƒ÷G¿ˆ¯nª “î#Ù'‡‘ró;Át]m‰÷ õ#ÜÚr;·ë{¨meF¾í£DäÖ"ÙrÕäM[£ åúïôÀp›©"{Ó—³<¸?•ÀAyŸ‘1¯”ˆ¬ 2çÌx„ÿšjNš˜.B¯IA"iqc6NèdäþK«a÷¶H$åúö[€¸ ÛEÒhØ:$Z¦sû®¯À’°-Ü]>¢jÀ %9ça6>¶‰ÉYk_ ÝBo3Rnz‚Z<úÊÈ©ÆÛѦñ+îðM'QY¬ ²Ào 4S£)7ßX§ :-|D²ºÛL¹¡ÃÈÿáTUŸ“Õ^0ÍïÎHÕÜÕî3&< g¤jnqð4˜H¤j~Õë F·a¤jžiyÿ¬ºÏHÕ\®ÿ>ÇŒ¬:"’ÿOµÃØóû#Uój~Èm¤.‘ª¹YU!Î¥ëJdmûM>à¾U"©šëîÿ¿VÙ R5ךƒCv7YÛýE.ëê_ÌHÕü r*ô?JJ²¶ó?4~„µMkYÛ÷ÿã9ó ÿMÅÍéÑ¡DAzMœ =ßhsrjÜjxDÕWRÆZH[ê‰ä§Ô ü[˜ÅÈEóŽbîØËÜs‚‘ö¨¯H¾[rÛ*6rïÄØ Aa=îôvJ4ë§'‘rý_o¨€ýí òæö—x¼¨)#kÄh”ºŠäŽþ—‘óó 7·~6BŸÍ`dŠF"ÖF»p罃Eår®Øö.—é‚”ë7¿k*jv´Éª.Bïúw%¹öàœmЊ‘)ê!ˆí™¨ ³¿~€)kÏÈ]iJ4hø‹+·_æŒØ³§ù½lÚLJpWæAéwnÁJH7ö*H¹ùÅõ$h¿ÔÉûû®Á¯Y)÷~-NuÒðip¼º[I™ïÎÆ"Yï§+êüä†u ÀÊ%…\§FÞx>–‘¨räÃÓÈä“hõ½³H¶ÿx.Ó¥¡ä­ýßPUaN–MBÏ-ú åú(|Š5«™q¸S}¢¸5wà—é.’¯Ï=‚ëÔKJ2îsî¼ÍÈAKC Ù+XAvû>/Ï*¸=·€v±–DÊ~hdÀùC˜’ÜTuƒ/^M#󌡶º¡ù#'W E%™Tꂯý™½f+ZÚŽI¹ý4ááÆÈ¥†ËQ=o>7B{/bÎÉÈqqxDz¸róW5ÈÀŒnÆŒ|7â vöuàþÅSM³!Æ+DÒÎÐBÛ0îÀñÑØ7½š[p&_¸¯/ù#üá ®ZçÜw á^Ò1òi}F^u:rÕFÏF«9 òÛÃØ’§&‘rý«zb½F6#û?ËBPâî2ñìÆ*È!«ó‘°”‘Å]Cá×ÃII^]q GÝŒE²êîkäl‰äÎV>ÇëñŒ”ëgv{Þ|3'óë_Aû)Çdâ„P\ï?T$§4‹Â®§Û¸Ž½ð0°Ûiθ\èÌ•Ûïb¸{µ0ò¹î"\ Ôæf÷*FBû"©å[‹ºæ¤Üüö÷‚ñìDFVÇí…ß§¶Jò/žªßÙMˆ¹wQ$?½rÃØç¸û“ï Ò·ˆÛhÏ t²â®tº ÍÛ¸Ñòprí %¹qÝ ½ç¢ ëM G÷;QJòåúJXn.’Û—ÝDɧ\FÊõ_?> º>1²°æ4nOøÊÝ-¡þàP®X þYÄ »ž?]‘<¶ê<×ßV’ •ûÑTùŽ‘©ùK±·Á9%)ûQà&Á~éwypk2|9·2!Áþ"ÙT÷"ê4òåš"|äFî¹xÉ­öqåö÷¬c¿ŒDùð›-»öçÎls5IŒ¼8þ$>®û•›ÿ>áÛ.’õ–%¡ÝëXî_<ÕÞõ|pgOµH¾þŒìƒš™¾ô#Š÷ÿP’«CªP¯ÕY8+y3ý¹ÃÒ/b÷j3FÞY~GÅ"ùÃû2J’´¸7ìÂÞÏ‹yò×~(¯Ф\Kßpø¤}`äÃøu}Ä5ÛŠÑe³¹>öáåäDùíÝUÔykFzü›ŽMçŸ*Èå9§Áü_ˆä÷ÁåhôA+×ïšÿY\´èÂÈŽN1èÕ–ÛÚ.•'WˆäÀE˜hÔ”;z©?ºå|5'-—Cc–")·ÛG[¸Œ™•BzU,@`Â%é^±®íyÔjÊ{½S’róÍwaªór‘üùë)â»k)È¿xªn»#±1MW"󞆉kn\Èx%º2rQÿ‡°[RÀÕMEâ) îHã#°rÎRcûå ]æRsò.ËÀžÉC¹Ã+ëöY‰äj§L´84–‘²¿àÔ=0lâeFžìå ÝY¹ÆÈëÅ”d3ÃChßR$‡ŸÃŸîŒÔ? Ç.pw÷¸Žu¹Ü’›¸œ;Ÿ+×/úáQ< ÅÈq;£ìYš‚Ü}¿j+ÉÂ&`-¤+Ⱦ&W°wŸšH¾|p.Št)·ßõáBŒ8}^I–gÛÃg»–Hö¼ç‹¥Þr›ëfá㚺)7_'øbÞf¤WN<òW=æþ§ªúÜš”Œ‚Çz©šï]’S5@ªæ/f‡aw‡†JR5· <‚Ÿ;©š+G‚i~¥‚TÍåúÛ>s@‡Ó3©šo7ŠÁæÞ“DR5¾ÀÜþ¤jl×m]E²¶ý?„ÂC½ƒ‚TÍ'ö/FqÌ™‘±ŒL¨Ž†ãî(îê{!8é­)’ß:ÇÏß–Ü]»1õÊF>³v‡s‹) ²ÎæÅè×õ7S7Κ/DR®ß¤ Áh3â#K†y¡Dân»”Ž)î-DòÇ««H¸­$UâÀw_FÞñßs¹rûÝ÷ìé¦Ñæä˜º{‘>iu9Óé"Ôé ’Ú‹ráÒ91•”›ÿ1¶áƒ:3ÒÖí"b¦áʽ_‹S­vÁšòÅ"ù<ö ¢:ØsíŒ@È$7FN?ˆÝ}ÆqG  ÆäŠäõù8Ý ž›xè,®G~a¤õbOäÄŸå^=‰jõ:"Ùçõ%tѱS’²§º"’] ã8€!Ù›¹‹ï&¢0Z$lKÇñzÜWÿz 4¡wjMܶçÚ~ ‡žqÜõž£z†#åú¼ë CoFJíVá„f+îí™PÛP“J¾o˜+ßM ò¼Ùnxï$’Úg΢n!Wnrî^8ìêª$÷>óÁôÅî\—-™P–cd«×'àæÏ•›oÛ=õ^x2²l„?Ç+È¿xª^Q‰˜2v¾H¾±JAPÞdnÕHoÄïÎSfº#9:6<[ö–;5E²¸ô%ŠíÛ3rÃG ‰5U3·ì‚d°K$ªâ1O§!#­„C˜¹ÒUIÊõï€E͆3òÒk_t7­ÃýUrÚñ•¤ÏÍ+px2YIþªðGhã\ý½¸×¯#“#ÜQvNG$åæ—¼Âúò§J²Ó¤S0¹3A$ÿâ©FmÑ|.Dòr£Kh~±37ò¾'ö1æö)Ùk}On•g ½ÁÈÆCNòãn¼q4.v¼%’9=rpme4wÚ—‹¸óqW}q!b64b¤\ÿý}à6s‹‚l¹Ý.›ÜRÉåûÒsä­‚Œ)†:Œlòò¾î0ÉEå¯PãsSA>¿t ý¼¯Š¤i³ûX°è¹’”ëgÕe 柳É6‘î˜>"’›?å4VÕdä‚ÇaÿúŠ‚LÈ>†ÈãÕŒ|n³+4t¹rû_Þ ÀÍõDòÕÛýÐÓkÍt'yÙ©ÜÖ†9HˆMâÊÍÿ4÷"n4o*’ë__… ¼RÉ¿xª êèû^G$Ëûfb”f’tœ¡?Ήä÷©çP6#“ÐÞut$FæV-F~ë|9ãðC4ÿ”®$†Ãû~{F*¦…A‘Ü&Í]Q}°³HÊõŸpÚ>v+ɺިó™¥seŽWdâŸ/ õýÍI‹÷%xžÃÈÕD¸~~Ë}5'Çê¹pÏ Eß•F\¹~ÿ§Ç’L‘ì0ì(,ãîs÷ˆÀ¯N]dÓ#á¸5üfyîÛ.ܸ}I$‹V]ÃÄÇŸ¸rû_ Á3>"iw9w>€»7ú%§'+É ¦moFÊÍï% Ê^*È]OÁfì7î_ì¿Iæq‡Œ @SÃ\-5 ¹7ù«$¬låf§f kÜ=‘Ô zÛŸ¤\ÿ£9ð¶q7‘üaý‘c¸‘˜Ú-HIÚÄ!Ã&"iÃBñ¾ò#· ~’/÷Ñ”?ß¡ÝõDòŽ˜þç‹Ð%)×Ï$4“‚‰ä¿ö——^ÀÕßpϧŸW’.œÄí$=‘ôÑýˆ‘ G+ÉVˆ' n3RnÿöÔ2N$»™†ãã骆 ’ð6÷##‹üݱµ …+{?NᘫhNšµ<†Þ•Jò8UÕÇôS,h§’ªù´§h”Û]$Uó¥­•Èœ£.‘ªù÷:8wô°‚TÍŸÀíG?©šËõoÿý8lÏIÕ¼æûy\Ð0IÕÜfíBdŒ²3'ÿŸÜ!wßWµíWî‘‹œè "©šŸœ×ER5_賟-o2²¶û4<Š×"©šož³GÞ§µ¯wåžMï#’µ}ÿ?žoðßL¾wÓ_!;RŒA⢠îæyáH9—‘ÓolÆ«”¤\ÿ¯GžãhÏj9Üò1;ýÃÈ}©'Py0”Û73ãÿáΜ{»<äŽæŠI.>Ü__a4Òq‚„}-3)×ïê¸ h¾¬˜‘¾«O`ýÐ<®OÆdØfs[‡áçO%7Ù¡“ŒEÒôÏ•Å÷c)·ß¯i\6” %uœ 0Éç®9ù35 ÏŸßIŸÁ¨ÊJâÊͽúþ?Öî*¬ª}{8¶(&*¶bw»æ|ÅnÅØv ¶bba! "ÝݱèkNº¥ËVTlÅÜX¨ÿãû;Ϻ8óá9ΛÏÅûÌ1Þsöجµ®v™RÉ–ÐùÆ”z¿§j7ݳxrõ’‹8=ăY=Èçò/s¤§æIø•' dÏwÉ€Á‘-Ç®ÔGL‹×ç0ïÚp‘l¡î‰Gãb™Óǹ`Ê0 »ERªÿº>Î8}#€'Ó~]CM æÉš4´}½QFf]Ðõ¼œ#-]«á= 3OöëTŒÃ>AÌ7Û³0Z«;GÖÎÁûXž”ê·ù£%¶L›'’Zûíá5Þ”©t ‚w“L®g2Ö.}ÆÄgDµ ãÈì~þþþ‹@Jíwùuf.óyrCQ.bœ71—D¦C]Y ’jÂ#ìÕ‰)5ß7! +†hˆd ^2Ž®Y ñTsæEMé8‘ À˜)Œ˜kæÜίÝ9ëÕM”[šr¤`'"@¡q­Œ¼Ñ6òî"Y™u »5bÆ,­Ä9ùHž|õÃo¼B™RýõnY`h¾O¾ùp w7bz‰Â§9}\ ì‡1¿~ˆÂá¼,ž´ëåg7fêŸË8ÞD µ,# U)’Rý*í½Ðm³HF¯ ÃSSO¦Ð§ Ùm3™Ó ÃÖÙŒùÞMÄŽ¢ë9cvšçs¤Ôþ¥+³ñ¦ùQžÔqÉÀÖ.0¸‹n;Ç1½þ|ñi”š?ÁWÀ¢ßv©naT-ó8UÕ§ƒÁE\Úà,’ªùÛ²"˜úæIÕ¼ÍB»/jò¤jÞeG9=½Å‘ª¹Ï' ݲP$Us©þc MófGªæ/½¡e:@FªæG¿˜aÞã&<©š_íôþ Ò²¾ýFÆ*pí“£Hªæ:Õ¯qûwµ@ªæ…Û¼ åÄ‘õÝ?×33|-yR5í„ö÷ðd}ç›Lô@è¿ý9²¾ïÿÇÓçø ü7 Í"ÐþÙC|þ< Ößd4‡®W/¹{”úܽ-C[¸ÀùâU‘l©ˆCl¡sðêb½²cöO}€Ò6=Ò}»€Æ/†ódIÈ5Ès£8Rª¿æ¦"/¼(’qnÂòóæÑowP¼õ$G–aÁëí<ÙzL/O‚É—2æÁWÑå‹ÓýÙY„ÝšÀœ3Áû—6aŽú×Ë+¯ ¤Ôþp¯Tl9•Ï“Ÿ ZWÂÜl‹Ê“ZÌÜ·~8ùãGJÍ¿ÕS€^ÆŽ\w? §ý¢˜Rï×ãTï4ŽÃ ëtT |õcêtqÂÍm3E2鋾†dö·ýóv'Çœ`SŒ”º¬DòJѵÃbž4RÆ"I×–Ù}>öÉu!Äï‰@JõØô>¬¶ÉÇ™U8ya«@:,IÅÉòž<úççaã±ÉÌY+® Ç¢#³ëBSl ™/WN{¢i›•"ÙZÈÀ@»J”ê÷|oÚ ¸Ã“yOœØSÈܳǽ“‘Ï6ŸÄÄEDrøÑDÔXœH— äNÌåH©ýxZÁ“Ùí¢ñþÂM¦ùä .9+ûW'ÀóDSjþ¿ ¨îP›ª!?ý[ ÿ⩎ùˆP›ÓésY@ºO?fúÄh¤ßÝ/’=¶%Á§ÍLfÑýL$…«ódÃ§Ñø¶ióp¦+峙ʶöØŸÔYFg¤¢¥ñ žTjœ„Þ¹æL©þã[>ÆŽØO2Ò3¢ ¹Óßqdƒë× \æÌ“…[Ï¢ùÔvÌ?­àpÝD$×v Äò)̼È?x“#íø£yîE%)Õ¯ØÂÏJ#y2ÿü%¬¼gʼp :y—Er/ï-—$fpGO8'øsäù®î¸¹ª§HJíß9OßäI‡ÞAhîPÁl=' Ök»päèÐ88Á“Ró3Œ•¨8H$Ïô-À—ýùOµóaënŸW’5~"ª7iÊÈ)î™xÑä«@N}™ÎÚÌëþº¾º\Α}ü}P£®${êz£sD‘Üû+×Ü[3–ž9‡9¸ä!6û5dJõ}c›,äÉÝJ¬.¶d†Ž<ËçrAü%x^¿(’_»fa|g%óùɘ´m=Óy—€y» ¤«_&‚¢ÕyRªß…¶&pôèÓÚvDZ¹b8GºøC6ø–Hvü‘„Ú„Rfûí©HÞxŠyÀð.ôMŸÈH©ý3&ûb|i OnÚãò™&kÜaìÜ›éîlÆÚ ¤äŒ‚LjÓÛÜȼkËü‹§:G[ݬÙóž+ëv1O¯ÏDuom¦w«d´0™-|BÑ46L ¶$À~ºÓR#ûtgr± H1¾Ê‘3#“‘çëΓ&60µqæH©þ¯|  ¹/”']–¤ã…a:óñ 7|ž”+’bèàz‹©¾÷9„wá2òÞó{ñºÀ“»º¡îˆ>SßÞq™}ERª_Mù Œ;ä$n3ÎcÞ&N$/M(byónØœ¸yçs2Eó乯æ8×ýSj¯,gl(ÉàÉM‹í¡©dúù»ãü|S‘´|–ŠI!žL©ù‰CÐçó%žüá}ú«ô8ò/žj¯ 0ÞáÉ‘£Æcÿït¦NfL»½a÷ Ç“̃­RÐØý¼Œ´MD¬í7޼»6µC˜Ÿ{c Á%Ù¯6 ëMÉw“øOŒ”ê¦,ÖzÅ<ÙöFbF•27­/E`בì¹ì LçdÞ1NûÆÅ<Ùí—²&ýŸá½Ó`ß ^$á)?³äH©~ËYBó“¥H>∖w™ GžÂd·@Š«Æµ†öòô,N߇HÆåÄàt£;L©ýÖš6|%çIí«èaïËìðìŽmx(ÍKîÂ>}=OJÍ_Ù¿ E²B0ïä æÿpªªOìÑœÛñ€#Uó6nØàtž#Uó¹ÃCà¾f Oªæ{["rM‘TÍ^ôÇ‚`ŽTÍ¥ú÷|âÇœžTͽ:=ǯg½8R5Oÿóñ­õEžTÍCGa^Õmž¬o¿{™~è³7¾ŠŒyšyòúfÁ")ÕmR2VVŸåÉNgâ±}¬)sçõ´¶ÙÏP‰ṉoΡ¹KGr·Î£Ou+‘\o謡©<¹o— Ò.e(I©~ýÃX~ÂQ {LqXm9Óµ,%+ñäw_¤5žÃü(¿ŒÈ¶Ì6ç1oûu¦ä©>wÝL_žœ¶Ì¯Ž;3ý óàPbÅ´ãà7L©ùŽûsà›!’ÝÒâÒÛgùOõùÝ„‡¸ñäm/Ì8îÀ°úßÈŸe–Pk¹O$sm\ ïî-jÛüñ»d™HfYVb¿¼R “ßBzXž4ç6ìÜÇ‘9‹ƒQÉgò¤TÿQg"ppû9ž|79- ö2÷*}QÂôû× =¾žf^T³ÇÍI~"™í…š=ÙL‹G¡˜ÔJÉ̽~3[G ¤T¿ò3)Ð×3Q’GÞ¥b˺O22¨«3V9wåÉ0=xž²åÈsŸL¥˜È¼?ؾ }DRjÿÝFvh´ËŠ'OªÙ sÌyæÓ®×`1ùGÞXáÓ¶?RjþöËwõm(OúµMÂ+mæ_<Õ ogÌ´´äÉa>¶ø:Ƙy Å ïûˆ¤î‘0œ=ÃüzS‰1–'˜Z%pš¥ÍÌwMÅG£`žLŸîŽÆŽaÌqga©Ü"O+`ÓR.’Rý_h{£rÿ|ž<Ì î {2‡¬<ŽÑyüÝ9\ír[ oäB±ÒS$¿†=Äï¦jÌÓÑ+â,ONÙàŒ7·J9RªŸõàT¬0ñâÈ„ŽI8c©Æ“>­°jß;%ù|ê5ä_h%’ÉVEŸíÆœ;ó1rjS8Rj¿ób+l{·›'ók-1>r1³vŸLÉ5÷RÐoµ\ ¥æ+&ºÃÛÜ'û[a¥¥GþÅS=½Ä ÉuKyrÈ=s,9Ôyæi®=Iíp<ËtŒ¼ £»Ã9ò¹f4ÚŸæÉQÝ.àxïÖL Í ˜÷Y[$ß}«Ä<³Y9äJjmäI©þcMœá:ù6G.pÂŒ¶úÌÜ…VxõøŠHþ†Œ¾¤0+§?ÄB§|ŽÜnVˆ7Ž–<™»$§#¿ äöéz5i¬$¥ú½¿§À½ux²í‡HL90…iÖß ·ìˆd`¾Ö¹0Kö' ùäžÔülŽÞt™Rû…ïæØ\;Š'ûl¼Œ­-5™ãN‰(n¸€#—¸„clÍu¦Ôü(OÔ~n&’)9aX4Fù?œªêã¿ûÒ£r¤jþ¹{%JújФjÞd_ :uãIÕ<«Êµ"©šŸ¼ï ¡åVTÍ¥ú«—9A›ûdR5¿›„!E$UóåbðR3”'Uóo·‚°ÿ¬9GÖ·ŸÑÏ dï™Á“ªyìþX<$’ªù8ckœi5M$ë»?NÇ wO=ãHÕ|Zj¶Ø=T’õ¿òv2ÖkýÈú¾ÿ§ÙCü7ÛD «Ïežô;_ZŸcz>,Ä­'D²8ü&ÆwVgª °ù^G xŒÃ8R«÷uŒª'’zå!éâÃ,ÐrEþXOž<ØÏ]óuDRªÿ§1×–ºV$£š:·³ l±õÓ`fÂx_ðiǘ †#,¬”#Û—y£õ0¦ÅÆRÔV~d OüPÛÆ“'¥úwȃšk»‰än‹lž¨Ï‘5{Â:x¥Hº+°+p³_Ð%Üs âÈòb{$i؈¤Ôþ«[ý`šø‚#lòÅö—yÌXX{-cÊý½0ÀÊ[ ¥æÏŠz6"Ùm¾ÕÍgJ½_SÕüè‹ÈĽ<9ÈÚ½z,cZ¾¼â•u‰äã«hkø˜#;ú…áä}¦ç¥?ÿ“wŒQ’­‹oàÙK‘4Ù÷¥ÅërÁ± ˜7?+’j•¹pÿVË‘Rý/öÄúá^"Ù®o0¾¦…3slb‘ñÏæÓçip ÎÌnë ý<¥’¬rö@ÒÂŽ"é3é"¸½S¶rü›+’Rý6…¤aéü Ž\- Á×™W•ðëµH$“¾BCÞêÿš‹2'¦ÂýžýžÏ‘Rû5\|°ºk(Gîl烗ɖ̛m"1èÌ\‘ü¡‘‡iyZL©ù- àxl·’¼û%ùNj<ùOÕú‹+Í“áŒê>­™?ŠnÀ®x!³07 Š×g˜«åAøgñŒë‹’ž"9õA%þü‡'5–%@7;Šùª?ªšÞäȳ«ä¨4¼(Rýu¶Ç ±"J$å‘I(oÆÔòÏAì’@^Ǧ­8²é×0s•'¥ú-<‡Õkð䥥žo8òÕ‰ äí+[’+gmΑÏ;+;Ç‘'´ÇÜQ]˜Rû_gxã´ãŽtŽñ†ÁFmæg³rÎ5g:ÏHÃ÷—Ö<)5Æ‹4¤Î“-9¿GþÅSmvÐ1Í‹8²©ƒ¾ö5a~j èœåI½q~˜¥¹ši0=/½n dûQEh*ëÄ‘9{¡QgÅ“#Ι!rÂvŽü¨-×C™}¶¸`ÿüP)Õßp{fŸsÉ;Ÿ‹aßÿ"se¤ˆ€—YÙpo4N[Ýbvk[†.v?ò~ü-ؘès¤Ñ}{Xï»Í|9!bÿv")Õoæ¤PìøÀ‘¥{ƒð=HÕ|K+ h˜IÕœóHE͈‡©š«GcÕ¤‘TÍ¥úo¹rÁï8R5?j$¢:µƒ@ªæ#M/àÕÓ>©šŸ3HÅ–‡x²¾ýœFÅ"g¬±@ªæz!‘uÔ›#Us£·Ùð÷êÅ“õÝÿùM:ðÓR5Wz¤!?Д'ë;?TßMÇlÉú¾ÿϘ»øo»Z-‹®"3¢»^ dÑ;ÔÔE%Y½-Õcƒe¤é¥"¬šêđϛf`Ø¿†<Ùè]0zë.F%±8w£-sñeoäÛ'3KÀ†‘%JRª¼Ÿ?f5ÓäÉÖº^Ðn×€ÙX‘[›‹Ì+VqØÍû3c‘…ÇÉÈ’²t„¬hΓh©DÕcÙY Ÿé<)ÕïçžD¸õ‘‰dƇdü¨hËŒýé›ÞLuû\¸ëÏÔ ‹Æó…nÌ©SoC-¾R ¥ö'œJäáê"yÎ5Åßêrà+¡«0í§aæpwŽ”šïרOÿéÈ“÷[‰(å=˜Rï×ãTÿýV†£íòv~tïÞW’Þ¢–7‹#ã5Ã0±ýbæ¬Þ¡˜YjË“æ¯c Í¦žZæÝ™Æ‘†?ÂaÝ÷³¿U Š÷ÅËH›hè?(’RýWýù¹a°$“#­¯9b¨ÝfPw/íìÈ“ nY£uÎfÓËqheÀ,ì[™' íQx:H TâH¬¥HJõË’Ž¡k”Ùâ[:ò–öa–ìNÆÀg®"ù$↣Lµ¸||¹z’'=Oz Ã/3¦Ôþ>nYдz"ýŒrpx~Óåb4z•ÏçÉÍCñrF/¦ÔütGWtS³æÉo—!WÚqä_<Õ^Ö•ðŒ—‘Ñc+±¼é5Žl!üù:Ÿ-#]ó}xå†’Ô 1ƒ¼áÍÉäÅ™×0{oö篅»GZ[9᫆@*ŠQ¾yGÚô BEOJõwîjªÝw&“Úzv(OŸ!y‡ÍѵÓ9u£= dǹ6ð}\Ç‘‘æ–Ø±w¦@n\|ÃÍzqd‚S~}ñçI©~k.¤Á¤é×ÉäS>jnã9òfyÚ›¶Ƀona²N¢’tikÁŽ-r×ÛøÔü#’Rû7éäAØí'Ú^7 ˜t–×Û 2«CL‡¶^¨´²I©ùï[Ãä÷P‘|·:WL¬™ñT×Ç–ãì_ŽìÛ¹íŽæÉ—v~è|#L ÷] Fy«6"yRƒ†>Ì*ÝLÜZ|•¹]æ [˜¯Û¤ Ãê3Ìí—ñü\ˆ@îÞ#bi´‹HJõ¨oÅ–Jä×8!¡_‘´]ã†~á»™·E`˜ÁEfèm;(„MÌê?¿¬Oñfzµ´€Sñ3ÙcΊ¤T?y^"ÊõÌ9òÃÛX¤ßwd=¯À×%³ƒc*ã8ž<óAŽY8DÍÍ9Rj¿Öã|<ë4V åO  ­žª$ó[d`ßÒë"9 çú9[¤ÔüW31øÕ2‘|:¸jJQIþÅSÅÎBn[Á“ѹ˜ýô$sü÷(8®%’7ˆè¬óŽ{X­É²u£r ŒHàÈK>p+.È/»*ÑÊP'7–AÑF)x2vOJõo¥ãÛ—‹dO _|,:Éì|=Ã?­aÎ_K_ d#Ë l^n'’Ë*QÙÙ|m.:9dèÅx4\ø˜#%?µöEbЋKùþw>ŸÄD\*4ïåIÏ‹1èëuŽùù^¾V%’gõðïK/”ÚÿÒ³U2õDÒ´cøúÉHÁ±7,åIÝ*9 *\™Ró7Ô¥átÐwŽÜôBŽî[òä_‹çHÕ<+,Ǧ‹d}çÇþù“b$¿­$ëûþ<í'ÝÂÓhR)ŠSÓ9òNÏ"<ù®Î“NÆ^H±i&’£«‚qp[w¦›w.vÓáÉŽé±8£»ž™Ö²Gt:1­V'"e›3¼NŽ«gˆd~L>Ï1•‘RýVÛÀ5ó¦@Nè¯=Erà j\Ì‘¡+s±±÷ž¼ó ±ççqd²G>|ãÖðävë0h+’í+!_þ¯’)±?´[.uÔȉ7+>,GIöþ$‡é´‹Yùç_© /k%)5ÿL`ŒïÈÖçb`ÜéSêýzœê þzëåÉÓ Yê0‡Ye Ǥ6"i•˜€n…iZî‹i#zðdÌ)4Í×äH»§žèßh5Oz˜ØãÅü1iÔ. ¯jÌx2z½%§¯H©þe³Ý yc¶Hþžê‹ ÿlbþk_z–<ùâ¿gn<~[˜þ–žøhx‘©»+ ³¶a¾õv†ÞBm¦T¿÷g¯áh«n"1ß61ãzÛáÞœ–ÌèÞnXôp3Ý$;^“‘_òCqO/P ¥öË*áÙpÀdòŬJlŠý,#ŸQ@Þzº@®_ŸÃ)“9Rj¾Ó±tôyd#3VåÂl³ GþÅS-HME§Mëx²“©€µ·v0õ `Ëá¹¹¿&WLH»?lÐÙ,ÏBäø~Å‚ù±“¾V2§jÉá²HC$³;%"ºÃ5¦Ç¥bôíbÆ‘Rý·Û…@°[$=’£pçÝ6æ¶N8*jñdü ;h<-“‘ZÆ×°6¨O^ècµcÒÚÚÿèÌI-Ýhß7bJõ <ë…¸Ãæ":5 c˜­FáY€%ÓesóYîÿççX€ŒÜÚ1_=óäúCXd=S ¥úWÏJ@ÝÞå"Y›–„á«Æ3O†;#Ý+H ‹ìýУ‰¦Hv©pσ±ÌNÍÂV£ÏtÞVŠÏÏZòäÆ9þø•º—)Õoþ™x|œí ’è†ó/3{-ÌÄÙœ³Ì÷&ÅȺ;ž¹åy~48Ï“‘Ï,ñ{œ&GJíß2¿§W=áȇïK±¯«OV¤^ƒmß8%©çŽÚkûERjþÜ1V°Uqäø/ÖðùÙQ$ÿâ©ô C͉;ªÎ äÆu‘ˆº—ÇZäÞ"9¶ÆïšÍ êƒyÓÿÈÊÌtT;D3¯G£a÷•"9iT92Û·âH©þn‡²Ðãç* x”‰;†)Iõ×J4~öùm±jœBFÖmO…µØ’#ÅÆ L\êÊô¸“þ¿e¤ÁÈ`$68.Rý†w©@Ø+ |9­Å}÷ÈÈ&¥ã«Å“Š 98>ÜéžVŒ α9.-åsVð¤ÔþoÿæC'ŸlÎCMëL³¦%P4À쫃]––L©ù3ZVà³b‘@nÐÍGÛ_Ýxò8UÕÇb…æhüàHÕGIÖÄÂu‹-Ó»8C;WÉÈâAXs0›#¥öÛë–cQC\°¢)y‰ä¤6I0~yŒ'[žôFënzL©ùS„D4ô1åɦ;Õ6áI±GR[L&¥ú™‚®~µ’Ü%Äà}î fu†âÏo+¹þxÖÌâÈš"ô° d§á(|»C$¥ö¿é\™»ŒÌ6-C»üq0Öý³edažÔǾH©ùÍê®cì›™9®+Z—Ö ä_<Õa_á—=TF~Ž^š‹ɹpCò÷I<ù{’ŽÊæÈYÎî˜Ñýª’ìþÖ éV­D2ÊÙ A÷òÍŒøœ2ÉŒ²@è޼ȓ´ìО•@Jõ3#=¼äÈâôXÌ>¯Á“ã—çbïM‘¬«Í‡£Zµ’Œ{•¨ñVYÛ>:Ñ}8RýM<ªÌMedÀ ¬¾öŠ#%ÿUj‡“­§ÊÈ»ÊXøkôäÈê®ñ8P¨`ÎúóEEñæ SûQ†Nz+n߀^8Ï“Rû»û•âh©%G¶?Z‚¹%3$)‹¶ÝÈ]Ý VvTIJÍŸß2‹É¢jk²sò/žêýïa°S’wç‡Ã \] ßl±G¯ói“É©ÎÐ(pÈØ)‘pÕY/’Ö³2Þg ³u2v5ÙÇœéðçÿ²Ûñ9Â<f¾½EÒ(-‡ áH©þ³æE⻋6Oºô ET`¦¶^æ=qæÈž#’о] ž4ž­„|dGªÏŒÄ`¿æ„ÕÎ( Hñ¢WåGDRªßÈœhtŒ;Ç‘jäP× `®Ü Ïyòˆ…7~µxÄ‘Yv~h¸å O޽µcg'’Rûý~A¶íGzëâòÕ<ù!CùŽ9ålŽ×H©ùÂT¥»O&û]ІãK-Žü‹§ztpüþ™%.VrÌ}‚ùë–ÞZ7ɘÂ`ŒîÖ…©¶£9ž3R³E12O5ãÉ2ã|ý‘' ôh}ž'Ã/GÀ¨Å+%™m—Š÷/>M&¥ú‡ø£a÷îS’÷zÊqëv¥Œþ#cjâ0&ì7sçèHŒ8óY ï7VÂýjc‘ì}8µÊÓÌ=9¸?k4S'&{Õ' äåeih»®l2)ÕïPwã8òÕˆxÀO`ÎSø`}»\´t Ä­‰]E²ã{ók{ð¤V›@ÄüJáH©ýF¥`‹P$#Ëú%cֹ͑5·åç70Óœ“1¾ÉNž”š?¾:úÙ}•dhm¸¦§8ò/žêΆި[¾I$ÏüðÇ‚‡˜Ÿ„s³N äZcj¾#‘íw»ãñÇ84˜¿³Dòa x æýµ±h+®a.φfú=Lz‘ ¯ <)Õ?3' ¾ã>päµUx²”y±c&¾«E ¤z“HI šDiÃ̶£@Zι›K 9rõ†è?æò¤–ò:ÿä9RªßÕÏQ¨ûÌ‘ãïDÀwÂæ0;9¢^s"Ùn]"ú|ÊÜØ0 Z¥+ÉÞî1(ú/#¥ögü¡pÁ‘îiL;Îg>õôxn+O^Xl‹Y=fs¤ÔüÕIr¿lΓ«Žzâùž‡ùOõò´P<ÚyJ$¯E½‘ s·F"<;EËÈÇcaäߌ#«§ XøcµHl_€ë£> äÞÌ ïSÍ{"Úa)2rrvççÈqýP Ÿ.’Rý;üùåþc` GZí Áºû˜ÚN¹HŸú˜©>,X»˜'‚E¬0Ù÷`8®¾Étš€¥5‡DòЬ|¼·¾#RýܹpÜ8З#›M Çan¿ŒL앃kú‰¤ãÛØ<;_ »ÿùt9`Å‘éý<ÔÝU ¥öþóió1TŸ#÷L„£Þ~æýÅnÞ …H&E!Ô}$Sj~åÜZP£$g;yB£hªHþÅSõk÷ÏŒE²R[€Ñ‚½Ìˆ‘X°S.#sÌþ|ÑËI$מ+Á %8ÒhCn5›Í“C`TÉ‘NGàA·õÌKµiøxB û]Fœ )ÕzÏtrlÅ‘¶e!p¬X)#õDæA#žìÙÇ {þ]Í\â‹•OŠ8²k•&:î‘““qê·OêN@×ÓÖ)Õ/Ö?ò¹‰äµQ¸7Æ^I~l_Œ Î3mM‹ vqãH¯.±¸úvµHjô-ÆŽ1§Rj|…‡sä²Kñ°©>ÅLå“á<éº@fd&ÂzÞ)5ß·8F=Ž‹¤‹Zfi0ÿâ©V¥`pê‘Lþ”­§2ïí–£å1mTkƒÊ×'˜úþúñÆ<ðÀ÷:aškÈ1ñ^K%¹-3j ˜kGÇáhˆ–@æé pœQÑRýÖ‡Aí®~"yÙ8žX¯$OoµƒeìgŽÜžw ¦êJ2ånÜŸôÈÔN‘(ÈïÈ Èüóúc¶’,$GŸ×V)ÕïËÐXÖnJ2¢Žý1Ë{çájjž,‹HÁÈE[™w¿g |‹sG˜Þ¿[È”Úßmz6ö;É‘î­cáÝê³IÿH伌“‘Skþ|§O-Ró?Ãùýv޼P‚¤i xò8UÕÇt{öí#’ª¹Þá¾¾ ªùž( mÅ‘ªùÈ?\“Žö‘L©ý³—À¬k¡’,S€¢y;ì0-ú~ÛÉÜݱèÕs“Œ”šïXá‚RïG9©­=ÞÈV ä_<Õä7Qð4í¡$ó«£ ¥ûd2N…â]I‰ä™ª4ìn-#³Î¥cùA\T—Ïùà¼Çs^2r¦U V&ôçH»Ð0<:qC }ÚG£êcÀdRªËSpøÞ‘\¿9 W§ f~4Â4îÌ„™1ð}Ö†yýD8ZìZǓϻ¸C°Âp+Ý"‚2–KÅ·äù<)ÕÏsX2܇¶I=³4DÈÈ‚/ŽxÓ+Œ#õíàÙ×s2³6V™üàÈ‹³ƒc°™'¥öG¿ñGÕ&^ ëüQ·g7óÃâ(|­çÈ>ë}Ñ´(Œ)ù©ÜÈn,É‘bó/žªÇ¦(|´7‘ý #±¼6•9äR Œú,àÈ‚%‘0îÉLTKÃÔ·ƒxR÷¼ÃGme6}#G±kN"‘‡º3}rOj(x÷`¦Õ©,ÜÒ4eJõ¿u9ŸwÔ d•N]s`:LÄéÐ8æÏOJŒ=õJIŽH±Åú(]ŽÜðÙ½×$ ¤ÝtW´­Õàɵ/¼Ðÿ`@Jõ»”žŽºÑ rï·4´m[˜HÇÞUþùj¦+š—öÉ5Y¶H¹pSFV8úÁ¬ßD‘”Ú߆ À^Ùì€ü¾L›|\°ÄÜêâ©ÓDRj¾ÓÊ<4‰¿!_›åâÄÖSùOµU9JK~ÉHõ}áØÛ´Gέ AÍm³ËA?Œ>êÎäo¸ÁÔc8Oz_µÅªQ_ddûÏIОL¶ª¡ŒªâÈžiJœüùOFþ²ƒ¢•GJõOêTˆ{1J²×ŠtÏ|'#ÝV+ ˜6›yb|4N޵dÆ›û@0ž.’‡Çàóð¥LÏÕ ðWóÈ£>Q0êÌÉHÉÈ‹)ÐÝýAF–ÿ+ úâZŽ|µßû§¯ÉF×åX{È9·Y2ö~ÿ$jE覫s¤Ôþ…~y½WÚ})eî럌ã‹ä¼VE°¾?Q ¥æïšˆwÛóäš¡ÐØt#ÿâ©Æ8„b޳&GN ÆÒ„&Ìf=0íPæÈ.npD¦’|På„.Ÿ²ã« Ln?G$?G 'wOF¬vÃãܦLw×`Ö†HÖ¨¥"cÝW%)Õ¿ªÁ hÎuåȾ˳‘vû-ó»–fn:JòêòHh?˜-ÂÞT<¾ÛZ$ÇdâôáöÙæ@4œ?{2w=ÊDñæv2RªßåÏ ¬{~ž#FÄbHß3ÌSAJ$t]+’“”ˆÓÈ\Ù=ÃFÊÈæñÐ×D %vö Fßi¸"²'uL÷lÌ;Ò'ýÈ1Àh!Sj~Á¶ dØÅËÈKï# o¨$ÿ‡SU}šBKQ #Uó :îh8#] Uóm‰Ðo7D$UóYígœ¢$Uó^þü#è^-#Us©þ½=Òö´#OªæÜÃ8x4¼&ªytu â®-äHÕ\ù!&Wðd}û¹N‰Â‚_ó8R56-ÆÉ¢@ªæÞ!IÐþjÏ‘õÝ¿96—ƒ›‰¤j¾ÄÝA²9²¾ó?‰A¸]¹Œ¬ïûÿñüœ]€ÿæÁñ ¨~s¤fDf\oÓ oäBórš@š—ÝÀ ¯ÍJÒ¿‘#Õ*9rõÇ ›Ð›'eË£ÜØ[F>ºÒuJ²Z‚”P+f©™GФTÿÛ"ñâi¡@vÕŽÁdíH¦ie>ÖØµgê½±FŽ™t׺ks”dÖ//pm dàO õQçÉŽš®è·º±HJõóÜ޽c8ò›“€2fÓw062f††CsY#fÍHxjz$’þÓâ°Z~M ¥öŸu‡·û?Ù3Ç÷²Ì™­Ü›÷Œ#W9`ÎUc”š¿nh*&þÒÉO9h»$KIJ½_SÍŠŒF³«ÝxrÑJ9ÊÕú2»¬ÉÅÒ>ç8²nD::gvã…Këó¤ý GÈG_“‘gú)!¿ã£$Ëk“л÷AŽô~_¦–‘?ÖuÍ“Rýß¿‰EË!˜åæÌæ¥i賿Oš6‹ƒçÄ9LíÄ0ÿš.’w÷‹ÈHóÖè$$UŒaÎ^‘ã9k8Rª_ù \(¸Ç‘«'ùCÖ+9PM޶Ôddˆ2 ½.‘ ~˜ëáCddqû Y7ž'¥ö·›í>.X üòÀå±yLynj¿ÏÉçÇ’°EÉ”š¯ÿ43Í“8òÇØè?_L1ÿâ©æ‰¡(žÛŸ'÷5 BêÞ̯á²K›9Í( †§»1‹m\ðø^­@Î: óì¥"yçu´œ»ðdí[?8:÷azŽrÅí’ìÞ#Z/1¥úß’Ç!cÁ[%Ù"?9>«™6AÈé2’'oò¬•¹ÿv.ŽZ•dÑç|¬Ú\$#+ü"p¯»/GÖ,ŒBŠfL)Õ望¶¿pâȳßÝpxhwæãÌXèÅXL&ó²â‘2ø©ŒÜõÛmš¨óä˜ Îøõ­H ¥öo}ç‰^·^¤fgotÐn$’-]²‘Óï–’Ôߟ„ïÖ )ù©¦Šèkó9ršsÔ'ä_<Õ«ý°¤Z‹'¯ßòBp›Ìe!˜“ü‹#Æ`^ŠóJ´¹ßT‘Tÿž~oÂÒÀÑ6?sdßmÎÐnóP WGÄBÿ¹¥Œ 6Å»™›RªÝ¤ì•H>>“¹E“ȱE¾˜$ÈÈ´;>xÑu˜@jmÊ„î×V³÷Zî¥\Å 0=%÷¢Jõ[Xc†²hãB`¼Ò•C[(rÁ9qƒz?Ë‚‰oÏR7dÁ­(9Ž™Pk¶µæP©ý \b@ïk un—:SO7È€IJ&K—ê*5`ƒÚŸ[i´ }±EúñQ újçäp8êÿ–A>‡À™Ájª³n½jFß"Fê«Ñ©çÁ¸kw-VpàøŠ:]– ÎsgðèÚ"T-@õƒ³P´õ8ƒ~ŠŸ¯–rTªÿ˜%è wT£º­ÒÁy[Ý/&ÁÐ_µº®„ˆ-©+MÁÉÇTŽ6PEÁ®ˆPu:•¾¶À£OÛ\³Â—*ÕË7½äP1$‚&<ú;5|tªÔî³nmøD]x34göRçXg@‹±¹Ö¨Ôþ‡gâ!Ω7vNVÀKÅ`j}Ÿ,è¦ò·F_gdCèö# *5ß¿w4y•à‹ÝÃ@§cõô/ÕÊkÁ0ÊÀA¯.ªýíú"C £R`d›Ó,º³¯üÚ죖wÉ3Ÿ» ºäc2Lv˜@]ùò<<¼ÓŸCÜ9úN3è©°Ó`ôþ‡nÛ)€-Ó™G¥ú»5KN<Ë¢=¯¥B^boí6)ìºÂ¢}2 jƒ·5ÚúZ4äÙ·eQ!8,Bsè¬cgÀìJk9êv8 ¼Â Rý Ï+`úÕу6~ Ýu‡S¶ÑÀµuz2tÙíl¨\§#G¥ö/Õ$AÐ"†Gí*RàèêÑT“ÅI°Ì±œAã­ÂàÓ÷72Tjþ¬Qðü»>=®‚±3©ÿÅQÕ¾$BP7w ª· *ˆ|9ÙÕÎoNƒÇÑ­­Qí¼e«l¸Ø÷ƒjçgê ð®…'ƒjçRýº¥íºíª›Ú¦‚óÅkT;ßऄ¦“Gq¨vžt#t ëqh]ûmý– ¡3yT;·=qžÞÊdQí¼óèXÜéƒÖuÿ·!épZ1žGµówS#`ȨÑ=y·7.àPˆ¼…®ú jk,@”nomÜ1jÚÏ”¡Rý/?fa]ðÍòàÙ?MähZELΕQWÆ@«½]¨F¯ràôó<ÚkmZþ•C‹¹°kÚej¢^\[žgJõëU}ŒÝ«´ñjо´‘£“Ý¢ ´æÑV|"ŒY:˜ú¸(âv1èå*|ûA•Ú¿Í>2¿*Y4ýx0$<7áÐ!#R@/u‹^OÈÓX†JÍß±IŽ}—hÐg»Óa_¸)‡JÝ_‡£ò™‡„äèï(Y £º;DBYÉ m9%:¸Œ êV‡AÒÅ/çé?e<ºus> Šï!GK_§B«ô1Ô7 ËX-è™Fù­ä¨Tÿ‡¹Y0LÙE޶3PBôã^ÔXî4ô·bÐÎoj…š$³èL׋àø)ƒº¸M1äüÌgP;‡L¨h8‰Cg==UWG Rýd‹r¡ÕÍ®r´ó¡ ØÑ¨#Õ0°öõïêPõYªóލaåáÐæÓµù˜SfÑwn¡Àx^æÐ>ûX˜¥Ó•G—õ*€Sì^ͰŒ€Ñ‹0¨ïÍ0øy>“Ež“À½ Úôb"L?jÅ£Rýßè&ÃÓ^rtHƒxJ:Rw YÞ3];A‘ö½x´B‡‡}‹ÉÑÁ+Ó`]ªŒºõP6TÝD5ù Aó<TªŸÿ¡8§'GÝî) &§ŒACJDXaüCëw: 7ë-¢)†Çm5Ôå™àêÿŽA¥ö· 9ýXu6ljîSïûÅÂú—·¨W»dAÁ¬!<*5ÿöúsQfàW8¸0ÞLŽþÅ£z¼C4ÌÈÑ™k“ þÔœŠh°äMy´Wíë’“8šz$;ê·%C{xæÇoµü–vf%º¨Q&ì~”A (€M®©Å½²aBÊ•êbYtiÞHŽ®ôƒòÇ—´J• K{šóh¥3Õæ5úôG4¸¹ÿdÐé³#ÀüÏW:,#6|ìΣ-–‰`GR8Tªß¹ÿyóN>É Ã'ÄÃH‡T÷™g`äk{ ª4à@ƒf :+5¦9ÊÑªÆ `t¿¿ •Úÿ¶}8ä_þÁ¡²ð8’jÈ£–‡Š íó}íVstR×1¨Ôüû}’ r ÈQ‡!Ðw{ƒþÅ£Z¿sŒŸe"G='Ä©M©ç²aÞÖÑ<šá&@öS*÷'Ž„É4îvè4¹o–×Ë×öYÔ+´öÍ{Ái z³8 _YPoȆAî2¨Tÿ&ŠÓ0Þ݃A{¶ †ëócdèHý|èÑi‡Ø ©N¶FŸ‹ËFqè›ÓÉ ¡Ôãm4âÑ¿xT›7ŠÑìƒ.X Ç-ÔÔë ¡fÎM™\ÓŸQíÌÕ%¹¨N–zÏÄ ÅÞiº»7‡>x“ >ª©}CCÁ›µaÐïeI ×Ö†G¥úO¬ WóË,úÜê4¼WåЛÔ`ú¹-ƒ~ÞŸAa†TÏo0°r5‡*ûeB«KÁ,šÕ‘‡*†A!?L:ËQÉo­+Éß#EoÍLƒÍÓ¨["T0FÕ‰A÷åe@b¶— m×*¸™­Íè^<*µßÙ<&vëÏ£-m`ìÊaÔ´>Ñà\U$C#ï'‚w¼‡JÍo æYõyÔ¶ ±­ûsèqTµ/£eðä¦'ƒjçý{ƒ¤T†jçŸdƒ¯ù=ÕηÏa_u‡jçC¹óЬËdÕÎ¥ú} _˜Çª;ÏUAÉv2T;¯.I¥wªgØÃûë,Z×~¼^/šÇ¢ÚùÑ'ªÚ#ÜJƒjç‡ß‡£T­ë~Ç͉PñšáQíÜÊ=‚}dѺκ2Vð”¡u½ÿ߮ȡà?yyKŒ´œ­AïÙeƒNŸ¦j4¢}¼'S8tÔŽ‡êú”¦®2tŸ=/2T÷u¨:ï•¡;úGÂ=§ûê|\õÏ|—¡«>fÂó—­YTª×9‘ ó§=aP©ýºpðöéÀ ÂAµ…—¡ã½âàFÛåêg›FýÛð¨Ôü³“2!z„B†~ÌÍ€NÝ1¨Ôýu8ª#úgÇòjtªe&<¾±Sƒ†è'BóŒ<ºø­>% ¤6ôÉ„‡ŸÚÊÑšñ0¬csêƒUi°ÚÙŠ§þàFÿ¶Ô1ü*]Ï ³ê%€mbS9*Õ¿I;5ôÿ CÇÏã¯: Úº*t]8ôÏtˆwõ¦ž2QA§æ3X´åUÌš0œCc•àq¶dšµ.ªw…p¨T¿ÓSBaߣ-z£Aô1)£¾ÛWk—5~w”…Ô/Ï"¡W¥‚:ëB8€ê2‡Jíßö¿ÚÈЉ:ÿõ¸ýt\„&+õyTe~ÊÛ¥hP©ùŸ÷*àç•Hü0Z Е¡ñ¨ö^£ #ux›–æÞÔOñj(Xð¨ÿî3pnªõšy(,¾µŠAõ¿†@»¡{8Ôfv}UŸºñF>L;ɠˣ ª&C1,¹Ô”G¥úOï‘ íš¼”¡®dÂÏ«qÔÆ—T6„Có[ªÀjêt}×/Ž^ßÁ¡';‹Ï5¥ÂøÚ/Ææz,ºZ/ ¦'‡p¨T¿çXíߊGýLá‹£õ }®üfÐõw³Àw˜®ýz%' æÑk …»¨‡Jí_}8|&ú²hD‚UñGêGÃ3à~°±Õ¥ÃË™&T©ù-ŸGÁümåšÜN %½‡óè_<ª…piûùš#M84ÿ~~Œ¤²ªb˜ü¸7‹îчŠAÆ[X†*±ÿœI sÛÆ¡íb¡äôajñ˜t0+j@ (O‡ü‡_¬Q©ù­«óÁº¬€Aˆ™ÐM¬¡þÅ£Zò. Žý³†CïÊänÔã¡pëi(GGŌׯšPóc5й~…EYÐvL[jÌc´¾vŒ*/M„ˆéyÔŠ­aÐG®âP?.ú·çQÉg±Ï9`uRÁR_åAl_jIHhl>r¨^ã3`ö —z{R¨CÓ4duˆbÑ72Ááa ‡–[eÀH—®,*ùÚúö,T5OàÐä_>«uð¬TÐIin^xîGF ½ÕѰcaƒj2ÂÁìF‡Jíw<Á®Á:üF<,/ˆ¥ØšWtÕè·± åã9Tjþ˜°dh©ãà+ß§À·ïCeèqTµ¯µ òàöýªŸ²N…k*T;ù¤û¶>Ö¨vÑ2Zlþ"Cµó“A”\ ڹTÿ5PùLƒjçcŠ «k>‹jço¢à[ì}ÕÎç=Ï€,÷4­k?ýéÐmFÄT;ÿ“ÂBª·ÊÍ‚)µ¯£h]÷?ê¡€€Í©ª÷øœ ý÷´åѺÎÏeÀ˜¦ Cëzÿ¿]6Ÿ‡ÿä“áp1£™ ý¢ASö±èðnga^ã_ÔÙF…à¢#C× I‡é«ôüƒtàH” ]41TŽºr´Í»H(/W0¨ž_ ¸ý8Å¡Wª•Pf1šE¥ú_™ü?ÿãÇOÚ.$¦õÎÑ ¾öéà—5É]Ø$Zô  Þ8šÛdÝx´¬ò,œ}ƒC7Ö«}P©Ö“¡÷jÀz /‹Jõû8:—°¨[Šª³¼¨sÀuM;9ºhOžw—A-U ݷ2ôÁ›xh×çUjÿÐÈ8õ§J†6¬JçWЍi-ãÀKWOŽÎô…ø"™ •š~h6x<«Fï\TCJó[2Têþ:ÕoýÃáùsÍÿÞ¤^hPsB´‡(ÀŽÏõä¨Â)Z|‘¡c:e—“#ƒÎx ôpè¯9q°q²¾ø™©¦éÚC]ñÛgS¥úW´ ƒvSûsh)ò¶Ôß s(…E³Ô ¨®ôæP¿Ê"0é6‚E×+€ ¡ zQ¥†–O©ªwÑPþ§Jõ›z–åñh²V/Êd¨ÿ”pX)›É ó††Ãg—5hBe,´09Ë¡o§äAji7•Ú¿ef*<-CËŠR¡ê»'UÙºö™õÆ-¸¡‚øàßT©ù—tò bp>ƒ¶é• «ÉÑ¿xTÏLŒ„‘mÔj>0ìÛ¼¤îy— ó2»ËÑI×ÒáÛ—:Kƒ¤±Ù Ú1>ÈòÓTåø,¨<Ü™GožàYg%‡n;¦þ…åèèâ(xnºŸA¥ú_v‰YΡ' ã j¨º¦ÀÇI¯9tà‰,¸–Ü„Goå«Áüè­7F 箾¢¾^ •ß³84yr.œhXE•êwÊDÿéktÜEì^gSGÿˆ„ÜÁ«9´øRìJÖPo=-µS ‹z×¾žY°Tjÿ”±i`b/CÏ]Nƒ‘ÆÃ©¦“5ðÜבC{4ÈY5*5¿ÇÁH8cÇ É_Ba.בCÿâQív*"Ãñ¨ÛŠÐõnG-«¯UM{9:Û0®ÙÜeÐd»ˆüÔÏÝ07 î-äиSô­QSÃrË‚ú)Ѫš¡½ì©ï^)Áû¨1ƒVžŒ§* ªÔ~'%ŒÔK†:¼VBäp95kƒVK¿kÐŽ·ˆXÄ ’ÏÂmDh‘Ä¡E<9÷–¡ñ¨ŽŸ”*Û¾<Ÿ‘yëÌ©ŸVFCüøåzrMØÆ¥QÏš€]£ê¨!7é„ =ºGeÞþÔ7Õ°²‡ŠAÍβÐwš‘S/ù2¨T¯žȲÒåцß9¸§¾Ë¡žy,Tvk%GJ2 ÅðŽT‡ tXn8–A¾M„_ƒ©}ÝÂAoÇ)ªÛ»8¨Þó‰C¥úõùÙÇôäèóUQà×ñƒ~­Ê†y±,úetŒÖRwÍIoTÚ`MlØcÆ£Rûï;d€yñbº˜Í€ª /êó_JX6 §=­ÑWo3¨ÔüÙçs`ǃ :nA&ÌþÙW†þGUûºò1*¯vãQíüÅÓ¸úIàPí|ÖgNLgPíüä‡tX°ÉPŽjç¿I](T î2èÆ¯‘pyP¥ }ÑY„ª£<‡n9ÎBÀ–Q *ÕoîŒ3°Áç‡î2= ·—5 ö{ÏAõ‚Ôù0ëK}ªÎñXÈ2è£ÑQà¹7œC¥öºæBä“(kô†N(ªudhÏɉߔhЖ[pïi(‡JÍŸáÐ#¾‹zûÅÁ ¯Rý‹Gu–&&¾h¡¶ GvM¢Ê[§ÿÖÅÔ/ jгhê p}CUSÁfc=VPûú4¡Úh–wŒ=YÔo¡®YveÐÛ{ WÈ•êÿ±O ä[uÖ {‚SAgËIê§S¹0þ>à R²á¥eµ^M ŒÊìΡ5±IpÓŸºZ¥‚/×9ê?,Œó[3¨T¿h·3à¡0±F+Dèå¤Ã Ç¸³P=f¸ ½lwºê¸1è?2A¦Û™G[ï87›åP©ýŽyðÍdèéÏyàò'uìmø5àÑ#Ï@…N!‡J~kdgÀ•·8tÑ£ñéà:jŸ5ê]û`bkñˆAM?³Œ:Ë_ mÎîæÐ§Tðøbcêù„T;£’AOØ…ƒ¢ÓQ *ù£¼ÍÁÂÌY z1W k¯{Pc{‰0äú}ê T5å[ÈÑÅ[5àë±”AÓWª@׿· •ÚmŽ–—¥ÉÐCñjðîõˆš¼ù¸ *¥¶d¡¤°†A¥æG¥ç‚ýìi,ª¯Ì„ñwÍ©ñ¨.ý’·Z„rhŸÉ0vY$5ã„K¬c©;JΠ êÖga×gmßK å·©&näæSókßD+ÛÈÑggR!3y"ƒÎPÁ< ]ªTÿ±ÓáZ¿ 2tqt:ìjŸJy3 Ö%dÐÃA'{¨M”àíàÁ¢­Z¦AJõjûšT¸7«-ŽÜ*@¹ï•êw%3Âx2hG«,0é´:¡¨öÓlcW9U™ Å·ÛQ÷„æ@ÀÀ ÚâX¸ïÉgP©ý~ÔÖÓ€Auþ¨A8gFíð=ž/|IÿGpÒ„*5ÿ¾g¤ïíÁ¡\wœN˜QÿâQM¼˜ ¦ Ã94bK:¤Ÿ¢îN+kïn2´âX´áÅ {¶(aO–êX/”Õpm,W½¡™Iq2tö»xð/scPkË8h•þ‚C¥ú¿Ñ¤ANØ[:oj* ²ÑgÐtÇHèQÈ¡êz  ×åÑÎöià}ä‡V¯Ê„„ÂóÔ÷sÀEÿ½ ì‘‘6,*ÕÏì„ " ƒú¿É#G=jUfŒuþHµ½•§¨üb¡ùŪ*0¥»q¨ä³|} p†¶ êýJ ?÷/§^ë” –݈­ü”­×úÊP©ù«òÁ‰Ï“¡òÚ/æÒ˜— ú_Uíkû]%è(}8T;¿¹]noT;OþJË}2T;u$ʇ¦q¨v^>V„ñSrèÿ¹_¢ÿäÉà5ÌAµs‡Ú/~ûNF<ª_Œgáö…3ª×”i bкöSTÁ†qJªG&GB°J”¡Úùˆ×J¸`ÖžGëºÿmŠZÏßÇ Úùú†ÉàÖœAë:Á©tøø­£­ëýÿv½^RÿɦSÒ!ààfuš­–ÙQ‡,àø˜{Ô ¿<°ªÐ“£iééPßË‹E嵯3~7Ôèž— àq¾öÙöu™¤‚fרeQùð!þ µSJ\ÔÝÇ Rý;'dC䇙ÊyåBßÕ ©AÆÑÐÚzƒ6|:ÿøiÐö£“ÊôÖúx£ïG½ìÅ wq蔣¹àóJG¥úé¶O‡ü©~ÖèôÐtpn4Y†Z5ˆcý¶, Ý•ùdCÅöU•¥Âöä+T©ùGƧúԖr4øN ¼¸ fP©ûëpT_¥Î0sõü”õmõ¨Âç ¨Ðo.G î$ƒnÞoåžS®F³hÌÆ$hG9tBítF”#u¡~XÚz²¨q Þ¹EËÐQO²àÌÆ“ *ÕÿUíëAΪ™zà ¿G ¦¶;‡oØS&ƒFH]¡ŒË1‘jÔri4ôuŠâÐ+ Ð%å»5šô3 ˜þ—Tªßùiéð~C– =0 ¶<®¦ž÷UÀ·#:9?\¿Sw–ä@ÿª‹2T㒠מùkP©ýÇpPs/Ú–‡á-ZQ‹ŸDÃä·Î Ÿ ŽYv*5ÎÔ(µÌÒ ñÀ{ó)ý‹G5Æ>^~ C=Þ¤ÃOUuìÂhº†gÐÃCã ^uù:%ìR¿áЮŽØð³‚ú S&¼f“Y4ð]\»8†C¦Ä@ü²= j8:æ-«âP©þ½nð`ðü‹†­¡ÍKjó~Jø²Ù‡C•i*èÿbõËÎ$°ØQŸG̲ äà¿|X˜ G†~•¡±G3`‰aW•êWµ,N¨}&ý_%AC«/2ôQ‹'>ú`‘ öÅÌ¢®2aá¡“ƒæÕ˃µ_¨Rû‹Ëx¨?ì‹n=,€ÆUMuýžzfxtБ{ªETS]Õ\^“Ô,Ø6Ö­Mfл/¢À~€‡JíOë-BU– ‹vSŠàKƒ6 ä!1TCßtPìÖi2Tj¾Á¼ ȹ™Â¢{ªÒ¡,–CÿâQþ,Æ Ì–¡ï–Õ>CVÜ¢æ…$ÁÍ úºÖ=š™5¢6ràÁeòM ” i‡¿Q·ä¡‰Q)uáéÚ_%­åhdeØ×\Ö yD ‰K²Tª“fù0E±‡A' º “ú¬" \¶çІwÒáÚÝýÔåjhÒ¼… ]lcªöPƒ÷h`hÝðD„Üù2Tª_ÿaµGzøU5œQƒqèècJÈٔȢÎ6é0%à 5a}¬3{É¡9wò ¿SgªÔ~®c>8zõ´F/¬É‡¼•cehÀá,XhK]r%®x¡A¥æOy˜ïs8ôr%7˜ýÔ¿xT³2U°ª]}}Y¨‚Y².T»t`âxýê›'šQ§îH†°ÖJuËK„uƒQëÇÇA1s‡*Û.#6ËЫ]cáÆ=žAgZÇó–C¥ú»ZsPÁ>aІýÕÐŰ-Ë„€?*ÝüM Î;2©O¼³ ÒõBºþxL tdÑq«2A¯Þ"ý˜®Yá2Tª«Ž‡ã’9ôŸ¥‰0áöcjί4Øe3†j>Y ­Ÿ¡:3µÏÄÞ,úîE̬°äPÉOõÀ|З-CûÆæÃ/† ú´ Ǽ“¡#ËrÁ´Ë5•šïýù,,·;(C»Uð`×ûƒþGUûò<ª‚ŒÑrÕÎMÔ¾nuºªFµóYÁ)p­]3kT;?ŽÎ›9T;Ÿø9þ´ àPí\ªÿž l¸ô®™ÕΛ|¡0x ‡jçòùé¾FjçA^*°?–Í uíWdš õfêò¨v¾kB&Ôä¦q¨v>b‰ <¦Êкîo~4"†Z2¨v>d_2t±Ò‘£uÿhU<šÐRŽÖõþ»ÈWþ“#oçÀÛCG8tmJ.ül¿ŸÚáW ,ëv”Ac[%Ã+³ Ô;Ís¡çfjy&ô{AÕ«ÝÓòÓ5jøCŸ–2(·2˜@?Íݘ‰_Q¥ú/¼”VFÆ#Pöq64cÑNÏS €aÐË;’àÍ„ùÔ©ŸsÀÏaêŸL˜iA=t$ƸàÐÛX˜:ÀŠE¥ú™~Èñ‡9tÕœ˜÷ëeÐÓ›Ðëí3j2(n aÐŽ§’Áwð.êÃy¥¿› 2aå¯0jø5¾»-bQ7+5\šµA¥ú§öTÀ®I‰,ú6HÏ6ü¤¾?£€1 }õ~š7,dèÀŠtx6.œANÂÞTÿ5<¸mGMY­‚³(ªT?}_5ìZ9C›Õ€GYê¡þàó3’ÊmÊ„¼µÔÆi £t©S]RÁ¨þqªÔþÍsÁ&/‡.𖋏R=Ž¥ÂÚÇ©£³T°ãV$UjþÕ˜±õåÐÓ=50ù™õ/Õ ×Õ0Ù`‡þZ¦½Aý¨%@f²© ]~/š­2dQãß ( 4bÐq· Õç֨ûÜt0ìÊ ¾c“áÁû-TN …³"©–}A/Ñ”*ÕšU"8÷äÐW:I`8ºácŒn½E™÷ à‚ÚphƃDøsi4ƒ®®€ð;y24 $ Êo­`Ðþ— i­•ê×ÍŽ˶¯XTsŽ…â¨T!2^?É¡K"rÁ)q?uÀ¸,x&„R/ÿRÃÒ«„*µ›Kï¬ãÐ×ÝÔãäH™ :û©-ê±pæ>Uj¾ðo¡,Úl= fö×dè_<ªò'—Þ’CgŽgáäá7,ê²D¯Þ–Q“×'B™‰%‡úLRÀˆ×Å,zyrD6Í¡ëPzî» x-ø#‘´0PýÎüâS\Óá*”C¥ú©L‚S¿×phóßɰ§‘'Õ]H‚_m—SH¡Ï1jÉõj‘«Akò°cTc¾(¢\·QGôË„Ëz‘T©~ jXp[ÞXƒFÖ>8 {c%C_ÍTC`´‡NVh`ç÷.Ô·ŸXè¿ó¤5ºÔ0ýà•ÚÿÂC ç¦ÎäÐo÷ÔPígKíTÉÂÀw­Ñ³½4°ÔÊA¥æ¿~›ËZ®bÐÏk_7ÿSÿâQµ bÁË-•Emk¿ˆ]SçU%Á3s'u3¤¦¶<” £SolÏ€~ò(j™i"tzÐí_Ú¦@î^Ôšqy0ÒÉz¬š…^k­Q©þ½{§Âæp_ÍqJƒ?Ç©Cz(ÁöK8õçðLèø)‚jp5 –t_Imx5æÄž ¾é®TËT™š…àˆ#2Tªßh7®õ=/C»_Õ@u«v êt˜…!Áy,zñ#  4èÿüíÿ¹&‘ š³1nß=H•Ú¿¿îX çP»]ðŸaNíhž×zú3¨¼~XŸ‹¤JÍ7®½Þ ºîE2½ÚKý‹Gµô ¹é4´ö·ßåÊ‹¨ÑŸ4˜sšCÇÏÈÒ-Šzz{6Ø—ž¢Z<ËE·ÕÔT%LjI­ì‘ FÊ ê˜Ã¹ÐÚëƒÚŸJ3ï ªTžK‡e‡Ve@·g‘Ô{}s SߟºZ6Í×SSgg€U÷(ê¢Ê,xw5˜ÚÿE´ª„Aï—¤A@R0UªŸÙ35èËÇ0¨—ñâ:êØÛ,ôNœ,C‰5 ßœ>Q÷V€[ÊYªåáÚo¯AÕ,*µ¿E²ßqhU}èS§¥€«å~Ç)àp_=ªäQ{¤€-[2è”Ú×Ûîg¶æ¡ÿÅQÕ¾Ž?gÓí(Cµs»ÔLH¸Ρڹ¬_탂ÝÕÎKôÕ`µp‡jçmv°P†jçRý_¸ªÀÁ(ŠCµó‹1Xûµ+‡jç&¦yà5ÂCµó9 ø²½ƒÖµßÎ¥¹ðsøAÕÎj_?¦?·bPíܧg*höørh]÷kd,h<>²¨vîVûN1¸é Z×ùŸ_(àÚVZ×ûÿíú”øŒü'½ÏÏ ~E:ÐpÀŽ\L½˜%,jÙ‘ ãw¿¶´oHu:uLøõþ¸ˆÖÌì/ÿåíê`!fè1‚>ÜßDl`nMºÜÄUüËMBÁå«>Uªÿ4åZ¢£<è‘)kˆÊãõš×Bù«_ãZ3r½ðJߌºãÒ Ýlm[¼ŽüvM–£;?&öú"š6#ž4Ê:$ Rýäoë‘ißÝZšt¶ºì§뾃4³í"¢ãOùoÿÖTf½1L]BÝ>|1»q‚*µ?ðc–°»}°ˆ6·:!¼FµÈ‘‹s£´5-Ôä´Q©ùE–]D”/÷!럵P©ûëpToðë«„r4mñBÁRHaÐʆÍÅjK€Ng&æ5[IÍùü Ö5pÑõ÷»“.A3©‘¢‘¸´è2 õ#;‰VZSG–ðK¨¿OÜãwŒP©þ5f«IñÑw€N^¶Š”Ûètì¸Û‚NÃ{€®ŠùGØgM]Öz6É|A¹n 1+§_›I ŸPç'& M—TªŸE@ø\?LÐ;ÿôƒi×Sã—Æ#¯¯š½‰%ÁŸã©‘“4äåæ{Ô£‚‰³è¨Ôþ½1Ž‚GÛhÕùm$˜ÅQ]‡…0N·ÛP÷†M¦zT©ùQúóÉÏ åSº“—²áÔ¿xT~ ¸ãQ_ÓÖ ¶“€6[?HyñªgÝO|—±I@»|E \GˆhùεäõðîÔ½-Ä1‹x´¼²Ÿ_* ­‰]¿'Ô£_ÈûÞ‰€Jõoù|1¼-A/.ZJ ¡7ui÷/‚ÞKj½ì¡ 7$ `ÜŽŒ´ýhR—Ædž£!AÖÁï%uŽÑGÁ{ùtªT¿ÇVd= N”œ",2  ŽjXBJ·ÝäÑUûn’¤¶ýíÁZ‘çYåÔý#“':"UjÿÜF‘üõ ÍÿZÄ H¢Úý£€V/[P-˜.ä!4¦JÍ¿¾ ÉêÓÐ.3~Añ•Ô¿xT³}syóy«ôuá]®Tçu´õË>S]‡_ÞyõÑ êcäåÐwúãs'4£. ¯JæÎÑkª²ÏSUþï¡U¥1A]¶Ø@3™Œ*Õßeï|2ç7!hdûid\ª=U %Âô>ÇÔ⟠Âmö5ôé;¸7¬7Aw} ƒ¾o¦S+š<nÊÉQ¡fŸ Þ®/ Rý¾8ò¯F¾ug›‹­Œ÷QL-%l«m¾«<(´!¨bh8Ù”°r¿ä ;Uj¯üƒò½MRDtÞÙ¦\ö/Û<]J¶ø®ЈOs‰Ë¢=€JÍwÕQ@Ç‘€®7(Ÿ´‡úªmSKùæ«Éʨ”¾ÉS_üÓEXÕ{ªˆÖŸÿ\>0ÓúGBêó4¨ßz²-׈ /O7#K{Šèª^ëÉœq½¨kb[ˆãg !èôñ»ÅÑ™*Õ?©!5Æ› z¢¿9Ñ,?@{QpîÛ\Dõng ÙÇQÍ×E3̶}=ŸÚf¬¤ý(ägþ# gz4#ÞcŒDTªßålF ˆq$èÓÎKÅû^ƒ¨M§vŽ{©ÕªÃÄãPµÓìí|ŸåIªWÏ€˜^1Q©ýÍ>·Ÿ)"zÛíعÿËÒÍ÷Á<£ A]׿_.{¨Ôü¸­ý…ÛS½yT·à ïâÐ@DÿâQå~Œ†Ý­/hÖŃwí2µæ2 -&؉膞­‰C@ªòºÙd:˜ .ëÀê)Ôãß\É0õýtoNP©~–Õ»Ä/K¾:ÿØñ·—šòx ‰~KÐQú‰×Úxêƒ÷>Dä6 躮^äÊi Rûg§¨Àgi²ˆn½‚²©»üN åž îh-ø¯ÈP©ù¯&ÃTÓé"jòx2Ùa=ú_UíkuE2¼>V" Úù±²¡¤ ÿU@µókÏòþËÕÎ3sZ“îÍÕÎïÍp#§wÙ‹¨v.Õ_¾nGT;·mùæÎIQí|¿Ùnñ»¸[@ÿÏü]½…±“ÖZ×~+î[h|T;w?ä .…T;7ž0‘ uiEкîß~ç ŒwOQíüõìjæ}L­ëü¤'È)·*­ëýÿví(« ÿÉÎ3Id“®õíîHtt;S½eGY¨ˆ–µiLŒv¥š”;;dºÏì£ÜgÃNªzëJòuföwó!Í Xtйâ¨CJ@ÍZˆÑwˆ¨Tÿ謢Ùåi":»§±xæ¢U¯Ò€H&(ÿÊv”„üËßá²( —w'ϦPû6þJÎZÕÛKˆ}¢KP©~ÌaágÁís)ƒï9bõÃPìÑ› Ì‰òúÆTI«˜U>6"ºÿ‚a3ü©RûÞ)äÕßè‚Ò‚ÆÞŽZsº±õþ( ×jŸù'¿ïH•šʽ‹8ky'@?˜‹:)f*uŽê©3H^V‚ÚîKÜóÚR[êNî½pÑÒÕƒIºr!5Öðº|@×i€¶ð aæ » Go#¿MŽúm|<9b7Ž Ë•Y¼î¦µ"ZÒî ÔþP©þ{|ÌæÑ`'¥ÐâJ8uÃï$~ö¡- Ÿ$¼6N½ÜÓ‚,°\À£9Ãú’kšÝ êÔp˜HÜ£´ôh¡Þw•ê—’«{Z Ñ'VÀ¤6¦TWÃ[\Æ}‚ÚÅò«¯|4Òƒ%Õ×lD´wêe2rÐf•Ú?$»«yÓÐaÕ‚Ëü/rtÎÕ)¤³Z4idDÊGTj¾{ÐPñÅçp½í×\<¬°Ñ¿xTW¿eHHTK‚n¡ÄæsSꔩr2 ÅDõ7ž<ÜÔªÖ›9€ºÿi ^^w¨Ü=Òœ‹$¨ÓS²{«©Š«§ÈÎ+E´é ž?-bP©þú›¬…Õ'’DÔ¬Ó#vŽ…Šª‰œ$tnô Ùm„ЯÔï!ÃIw÷Îrôõ®¹ä {—ºìyÀ)W]SÐ\5ö›5Ñ¥½¯Àqá•€nüõŒÏY¨wOaóÕ¹-$Ø¿(§¾›@|›Tjÿ<½×üJ£49jš—ûš- ž+ß(L\Ü… Í5šŒTj¾÷™Ÿp`¿ˆª¼‚þ{¨ñ¨6ë8€d´yË>DµFŸºî®‰Ú4LD×ûl!ÆsûR|û•³¨wÖמ ¨ËÆÚsfF´å“Âî K©)·Go²€F¾¼ãÞyR¥ú¿65«²EÔ›SA¥cõ¹s)¿´ü( ó<ñAí×QožØA¹Sý³÷’ ˆ®ÝkžKö[ ¢=\“í•ê¿ï |ÒQ h¿ã¯à’õqêïOn‚né@s X xO½Ût#i²ûAuü–A—¢(ªÔþµ/÷ñ¯=o1èæ_ƼóÛ­Éø.p¦ ¨|ªFxZPL•šßó©=¹”ØYD?Ü_J\ª› è_<ª{¢z’Å­ô j?Ö„Üìa@´õqXÓND5ςȬÅ/tz|$)fzS?vM$ÛF†j?Á@4 mBÐW‰~m4€¶8aÏsP§Þn"XmTª¿×ÉÄÖ-]D›ÍéMÞMM Š3» ƒuô[›±‚Î׬Q0ŸG>Ùº´°æ;üùI-8éNf-O½—ô \ZP¥ú½;ð4]% þŸÀ©£¨žßŠ+ýoÚ*b xöwõÅÂwBý«# ªlwU˜æ¹€C%§ìÌ¡~“\˜³Zó¨ÍС×?ê¾Ý…aâ4Õ9ØŸkRUJ}<׉ٽ\APÓ2aôÍãÔ ñ1¯Sh—œƒ|RÙ!ªT¿za åŽ~:¯Þ ðîA]4ÈAÜÖ×ÐNó\Dç.xtåšPù³!žê´,Þ=nG•Úï§ÛKÎÕŸÌ£ÆÅ^òâ;¨:]NYW݈QOÒ‹$'çQ¥æg^ŠáƒÚüë1x7Ήú_Uí벪9™îÖ† Úù´p2yÔ@µs‹{-H¤lAµó>õzŠk~PíüøÂùü$ Ú¹TÿJÝíä\ª§ˆjçz=àP„¡ˆjçùÁ¢AØ‚jç×lšqï¾U h]ûí!ŸÁsn'ÕÎKí-V_Pí¼Ù¶ ýz¶€ÖuÿóŒ\yóí¾<ª¿žKì:úŠh]çÛØ¼’“ h]ïÿ·+úÝGòŸ¨¹\"¢¥ÓÒw~ µaÔ+¡ÉÝ<ú3·J^º:D@¥ö‡¬y&è¼ÏâPnô#ÁÞg8V$ÄÒqAOîëMœ¾ì¤JÍ×ó6Æ9Ñ"jöµ1(q¥þÅ£úlo*ñ<hŸÒ Rã«¡ÎÈè"6/Þ( Æ-›‰ÃrS·ü $Š}":&õ é2¨ù“Sã'ô韯BüîÔC‚É>Mj‘IO’§9F•êïºú›0-Ï›¡¨”ö£94ÒÓŽ(®OÑã›f’vM©çoÕ'Ó¦ût¥ñ!¸TCÝl—KÊTZÈÖ@Kïq•ê—œ+û§CÐü_Õ¼ÝÏ"@;í[J6‡¹ŠhV·-$ûÑHjÕœT°ß×J@o{ã‚S¥ö«šÜÆòûyÔøçmAo¹‚ºaõchÑ#Œ Ì]o‘D•šöÔDrñÝðÕ’„]>( ñ¨Ž–M²§>tnL.±Qè´}Å-áSý!":~ðNaýòÙÔõ5…$0t–€VWÆ’•®S=òª‘h]¿šzÐd˜¸ùv1u¬<\hŸdLPïþºÜv[…€JõeS#0é xtýÏßÂñ vTŸy£ÉÆÖ¼€ZX’†Ç©K‹>ò¡Š$‚¾b(V?ýŽ ¨v.Õ¿`Gc±e°?jçóz“ ·}ª÷Þ?_|h4‰ Ú¹÷;_yÒ­k¿Ä~B½Î]T;o4y™è—, ÿ§½1äÿ±^§áX¶Ýßø%!I4 ©$•¤Ð ɹ/M’&)šEI•"M*™“ÌS’„D Iç¾k’4Q‘J¥I¥I³¤ú_Ûöÿ­ýwmç³=÷ñØîŽ7ŸßûXë{ÝVΓuâ!@Û»ß8ã:½‘ü]ŒJæ{&öbï+Ý(ÚÞùYz&Pþè®mïûÿz^”¶ÀrêÚP F¥öŸ Ek{P4»p!{fËЭ/´YÊJ_îm—«¢WEÙ€FVŸ¡G§p?¦¦[ 2ÌÐ5chM÷RôаStìl#†V\,Û\Ëá õÿpõ+Éï@Œj ùHJ¿ìæJë …:GÚ¸dÉ„q³ò÷@³ƒwð¯£Ðá˜W?e˜T„PTÞx%ްT¨_øàÎbñº5 ]3@•(yyp_mÒÍ»½”—­¡/&ç M½è|Æ.VTh¿½1¼ Úh`æ(Q°ž{î\Gb¾X—Ûõ}= kËš¯<®3¤íÙ¨É?ÊöéáÂz¿§ºc}2uLZMQ«ìýÔ@5™küð*½šÆÐxw:âp8·Óá.ìµ”< E#ïPïªÓÃ*ËF–¢ ÷P…9ù¶᩠õÌK· Š õOü‰Ð©ÞbtJ_$qê~îŽ*whÕÛÀиŒ±d:wưjøòìEí>@ô@u1ZÕŒèdè@gþ´¥Gc†s…úýêœGôg¹3tF^Wð–sâ–öšN;u úIÜjOZqóîyB'£½€&%w!ú¿Ã¸Bû{AÃ\W@Ý2€ð­k¹Ÿ71ét%îñVçñQŒ ÍWL<$½a šâA¢9³—pÿâ©nÍ\I'º]¥¨tpZ}ü Wêà†Rµï{šv?[´<Ñ›k®[*^$³Š¢›ÂlɹeÞÜÕcŮߥZ¯{<*ÒãÆœÒ‡í7”EèjÓ0ÄpA…úŸY(*ñbtX¾*”Šà¦êî‹C7ƒ/hªLäFU~€óëÞ4±ê´j¯ôýÏWTê¸w_XÛ±ðA…úMxDÀÆÉ‚¡«¿¬‚ª>ܯ¾Ù†qõ »MnQû_·œ§ŠrbnÚñ3Õ\Š íMc×:üÊp0=¼š›Öו©ëˆš 5´6Ú+4¿Im4µ+ñ´Y_‹õóÙÃý‹§j,%Þ#ו¡Uï™Íü©ÉWÓ‘hÕnâ׫ï Ü÷4\[bDÑõ?Î’ÄîÇmp(é)79÷Ôtx"B‡ŸjÇ6v4E›ìxŽ¢BýûVêBKÿybô‡©ÂÆ–¢ñÇ£á䊩 u2Í…Öƒ&ÜÌ¢xxv- PÝyS yÙ~îìŸ+˜bÔ8†æ5ÉO\ÂüQ†ø@©ß ŠÚÕí…7ž£¸mC®EÚºyÊU¢Ú‡Û@Ÿ‘1_¯P´¼­ èPÈÚ?Pw8”¬ôÈa}ZýØí¾øÎ<†FÞ&¶ “¸Bóƒ×±Œ±Ú€z=Šae!ÝJпxªSb‰¬ÛP†Z©ø‘˜2#®IÑ`-·kwx% yáÏU;u›˜ö;LÑ“»Á-\å Ðhè­ûXäNîÄo¿Äž›":ð}"Ñ]·+ÔÖhX°ØA„–'®S5‚~wØ49†Nˆy ó΢èM­dqTj`9¹Ò˜>³”/9GÑìEOÉFË\®P¿é ö‰kDhÒŠ=P½1„ ¡¹Wˆ•Ï ®™L>y“É]k6.Ûè0tþ>1ŒQá í7ê¦zº.€N·-[×q­ßÄ‹Œ~®e豦_„-ôæ ͧ[0•Š ½”–JksM¹ñT«.‘.cJ‰ <ê=š¼4üjãn]×.-á~ „¾ŠR\M1<=¼Š¢óB¯’þ^€>2 $ÉNÜ v=¡Îñ CƒJ®´á õO±Ø V«ƒš¯³úî¯âX>„]‘VÜîJàêx@—¯¸L*gip¿W•å›úp}×B•Ê&úͽôIÚ¨P¿ãfΰéä‚N;l­Ó4½K½HíW}%ŠMí è…wà‹›?A³¯ŸËÅÞ€ í8Odw»ÚøF <¸zFÞ`V®ÏК‘`n=ž+0?Y}¿È|bO†j²í|Eÿ‹S•|‚ËÁý”á •Ìµ~M²†JæªîŠm‘•̇z»“_Ý•ÌÓU“aËÞ,‚JæBýã©7XnST2¿60üÊg*™7Y®&†•ÌűŸhð‰€¶·_æ$U(o¨d~5F‡†MRT27®øyf íÝßòk0ÜØ±PÉüs¾9\ÒÐöοS6-Ê¥h{ßÿדkøþ“Ou^E¥2@G¾L#2g7ÄÚ‰ôŽi¥è﵋H¥º.Co\êKdë·p{<î vó]¹ÖÅyP j¢èðÆ,h(èÏu˜º ì܇*-²ƒ¼ÑA\¡þ¯ÖÌ!a7¥­²Y.j¬ý_籂µëzk•ÓÏ6¤¨¡E•ÈýC=fL J:r­÷€”«5÷)«­Ñƒ¹BýÊä¶!#}M¶Xd¶h±Ïÿ:) ‚7ôæ.ŸzšLæŽsûE‡äUtìÝ´ç«n ÚïØå9žéGÑ­¦mDgž?·Íe*ßµÐÖ&;öôÛD®Ð|™ßEbéÛ½õ« ßüq A…ÞoÇ©~º7Ü*,4éôFQ¤L!W·g(©‰XÂÐß‘Þß¹m&>`8\…Û{\,x ïh†¶ÌO†­]ï‹ÑèåYàï>¢G.Í¢ÁN€n]IûÅPT¨ÿ¥±5b¹Ö/Õµ ¤ßUsÇdÎdþu Ý>±Žþú9…ÛÒ…¸”ç:(z¯aÄíºþ!D¤\!h‡Sè3P¡~Ñééy›=ø1Ÿ–XÌí¸1 Fåzr/u ‡ùâ¸K®õ„ó:þ»?lÚö¢BûU¥¡zª?E“ŸÉ‚ñ㮿Ì2W!èÛÂP.-ÅP¡ùéz"’Üf†¦¹&AOü¸ñToµù‰3Ngj«5–öüœÊ•i†?z½i¼ì›Âͽ¼âÇ+ºó¾2ì]êÀWy´zH‰QÅg _~… ÝûÊÀ7Gm†><Ý n¼×ä õï’r‡ÓK$èLuE6r®·LMÞ¬uÚl†*½.#ÛvêqËìgˆb—ìâ*Yî#14ƒ»:8Ð'› j¤äN|*Æq…úMÚÐLï>è…¥Ù×kÜ(…å =2Û?£¸=ËäjNòîÉÕ‰:[&ÿæ íï¡v_÷PôP~wøµÄ‹kß ÉÞ—Mg¨áªž¡Ïš_c>®4´¼;EõߊÅ^z1ô/žj'¹0.hµA1 ÄÍ+N‡t÷†Jª„Æ~\…V-2_Þ Ð 87±á0Âý5ô"ŒNráŠ<=`ñâܪUÑþØ—¡ÛuÛ ¨yE…ú‡fRµsŠÑØÕ&¬qÜŠÞ~©vKk¹×¿KWîMO#xšÅÜ ¿ÿäsßO*§æ)Ì í¤¨À’­|*Ôo»i7Ÿöƒ «â™FU%×éw0yO*Õ¤Sšÿ~WèkV‰Ñî½îî @…öÛîí ÓWl¤¨^­¬LXÎUÚiH=ºË2Tkç`Zb«ÃšŸõÑ’t­ ah¢¬5$~ËäþÅSíõê=âàhW—ÏtÕ,nιg ;} C­BÞCvŸ÷ýþ-I¬ë0PÍÀb2á¯Í$)~\ó²Ó%n‘ݹò…Puy×Uwl»DP¡þzå%–StD½óÜ«ÊÐ/MƒA*H¾-º¥ Öź"tF9¬LÙËÐCk`N†&7oY5ÉXPÆýrk ¸Îõä õӚ܉eYŸ$¨õϾïwIáÎ t[¤ ß­3h†Cgn•ó\Xõ0˜ë*›L¬Õà íoÛ® £²gSTÚÖÆ,6áÚС„L\ÄÐ9'&Áµc¹BóZmF! Mù\!zqÿâ©>Зbì†úǾÛå/ÏmÝÚÙ0ŠnÖ»Z ñ"ÔÒs&uÉtôp´"c‹¼¸i¢­f²r*-2Ð'ù£ºˆÑ-éýaOt@¥¢£i÷åê?w³=k*C.©3Û¾îÜFÝA`¢µÖ ÍZ5 ¤Ž– §^{%Šæzkµ¾cýÚ3fÀÏ Mj;UƒL¸Býž‡~¢¿÷¦4öàw°ü ×lçq:;}—­ö¢½—¢ù=‹Éš²ƒý$­Gz,Ìã íŸ& Þ IQ›æÐpà»þåj¹rõ¾P•=P¡ù•Ç.Á#=Šš›ƒk³ Åèqª’içŸTçÀu‚JæîçáÖ?9£’yBìV¶`ÿ @%sÅhÒ𢛕̗ÌxMcÇ-f¨d.Ô_¿å uý°¡’¹C=Pñ#L„Jæ­½|  d”•Ì÷œ€ŸÖ´½ýº½’e Ô/T2üî†øb€=E%óoÓž'SÅh{÷‡dN…†YbôÿÈý~­±>€¶w¾\ØahX FÛûþ¿ž}!ÒæÿÉ8¿j±Þ‹¡Å=kÉ.åÚÌ' áY\ÃÛJ ±ò87R?ZLä}Ö+ vW/àn+€Þ—nQtŧTPØ´™›æ²Êš5.»¤­ê¯¶ gF34Uú”ý æîë#hlv$½Û·;÷¸œ%(l8ÃmJï ÆÉéÜWÁ¶äâÙÑ•^ÜM¤õÔƒ+´¿hy®È¯o C=ö—ˆdëC¸†a¹`ý V„–ﷃɲãš/•û\¬jäBз«JΉQÁ÷ÿßOµÃ‹»pcœˆ¡ëäïÂ4¥~ÜW'ƒl]"·‚8BÕѽÜ¢$,HtžÇ0Œ¿ÂãuÖnÏ¢¨âÌ\HyÓÈ5ôPf­;ïôè÷nl\³C…ú_þ˜ç¿ogè¶maÊ•…Üé idnÇ€V%> ûËÕz ê¦ÜÙóƒáÎ@î–ˆÚv͉ shåÃ] ê÷lS2}ù¸ %;ËhÛþ\2FLÖo ãÖGD“”~.\õ×èâYÎ =ÛAæë”s…ö¯îV-ZH#ªkÿQô#'ž[fÿDômûl@›wÇRíXi®Ð|y¥þ¢SވѦÑèîŒõÜ¿xª+²nCø)†:ªÞ€âJŠÚ¬q†_îëšZ±tÿØrwÌñ'Š{/ªºvU­â´Þ×]cüv·!¨Bël:¥@Ÿ¡×‡I±#\¡þÛ•oÂÚ_£qT¬»qï®t — =ÕÔ]ßq”»%1Fvm#è¹è£ðôUO@ß”t+¹†6ùö„Áù‚?Šiïè] @Ívc)ÖòÜÆÆh2ÿ“wÅËâüB“«³4:éaè„)gàZ†WðTW(‘ÀƒI %CI—4®Êã(j¦“DÐ;ƒh‹öK®Ð|„®ìôü=mŸÂ_Leè_<Õ˜ärÐ~š@QŸ3b0î°’k,g·>Îfè}Ó± ÷c17T+–ºÝÔôb‹24Š1´Ï³P¸vQž+Ôÿ‡Y |ª­¥èíÜj²$Ž{§É–5÷tðÝ5¬ÛôN\ûwÇÀì€-×?#~\ ã¶MÑ€qñ u<™ Vvqÿ)…bGÞÝ"¨ªÓh&¿k;wÔ”jòô’ óÞ<#™•¹Ñ~Û!åŸ3B•KvÀ¦¸b®ÐþòT+b'œ¡Ý7’éKs¸±+{±¡º€.² aEw?ThþôŠÌn{C3VV›ÍU©æþÅSm©< &©:õ^’ ‰áMbÔtïX0Žrg¨Áœ°ö?Wyæ=QË"jQ2‡ŒT²àÊ "/Ç:!¿ ÕŽ &¨Å-upVNä*ÄÉ€ŸR*W¨нJ°z8•¢“œÅmb4àÍTÖöú´í¥ûƒ•¬¦h‡Žzàº$P—½H’I$7fc6P?‚F9“4êç?–%ÃPºVGŸÍ©GQ/Ç»$k§& º¦‘æÔ!Ü  H0^Ü• .)]À+} BûÕÖ†’ÑóòºöV:é]WÄÍñNg¾ÛÕ(ªðq KZ6¡‚_¦÷„çëÏ04Au¸ßÛÈý‹§ù" ¬?)Fï=ˆÅÊ0®z¬\€†Þß §#¹} D†Ô ­8óBìaúχíÿ¸×챸ÅÜEŒ:Œ1¯­.æ~¹£Ž"}3B•e%äq…ú§+Åébt¬K6¨ïßǵø G¯j”RtSŠ>yë›ÇýQæOçE Û»×ÒÔË ª¥b;^48Ž®¹[,ߣ/ Býr³”ÙÚ½³ù*5­Ô`èñD Ò=~ [ ·ŠO'öàzl”b‡VÎàf”80·i"Th¿Wì9Ò©¦”¡*{{“2îëš…tD©÷•mð?ßé»pOÕdtX¢ÆPןÄïœ÷¿8Uɧ s…P1*™§ÊçÀO²–¡’ù5¦KƒÇ0T2÷^µˆ:ÐûãQÉ|AñRö9¤„ ’¹PÿÖ…é>Ç&~Ñah×rY¸¿YÄêoÐ|H¤ñq! 6ÃTÉàvÜŸUw‰Gá7‚Jëý$ü”{`Qõ–we¨[æzÎy$תdÈŒ=èè¦Ã´0.+ÔïŽÒ^P?±‹¡f³2AoÀ ®Â¶xÑœ›— ÈžIMÎq§î‚§ ×ÅhÁ›Ó` bÚ?ñ~7–[vž -&ªlòþƒ\r? RæŒå¶>< ýnÎå ÍÏh £ÁS(ÚâÙ…Ý#bè_÷/žêïº'4Á)‡¢V“ßÒ­qÜŠW ep$C¤oÃ¥ç¶Ü/W÷Ach7þYD³á†g¤™Í² ÔtÇbªœúš > ë uU9×3î2=U–+Ô¿Â1$lð4©>‘Ì|èÇ}T²†zïå~¾<TŸáNR‹¢Ïû´R´>CŽªL5ahñ‡ŸTYKÐW!¯èþ}*Ô¯µõ1„è§èðeO ®I‘[]<™]Ï™hÄà ìkJACöÙ†S'›=“ *´_a›‹e+BÏ'j±/‡ÕĨ¾g7zÑËÐ'[F°–ž^š8Ù &U^`èÈ[ «§÷/žªó¦_ÔBÍ¢£+0ï»k¹W®ù9†–˜Á‚›¡Ýê¿^­Õc¨÷ƒp~¦9WeGªXËâV)ªÓé½(þ{_Š6“[¤æè@mŒYÆÎp®àBJôjB½÷Åž„\ŒãŠô¯ÏÍ·¹† J]»Õq§i“´­Ëútìˆö™Ãuž8 ZË#¹"Çýà3o(W¨Ÿ–ýª~f†žh¸ iÓÕ êª=€™ N¡y9wiÕ+Š&¸‘†ìW"´®—¼[IPÁïòRýY¯4EiB¶‹.ã®öºw:ËýlTNßÖH3Thþ·-Ó!;¦Cu¯ÊÂù¶·ý/NUò9zJ5V,¢¨d®6(JÃ?ˆQÉ<Ø‚´Ã&†Jæ«e¬EÒgâĨd>âM‹÷G„JæBý3VH‘«s“•Ìcn]¢0@%ó–`kõ‘¢’ù»:[\—£à¤(ËÐSCÁ·¾\¡~q#í@g}& o¢5aâÂ3ܯ)=Y=íÍ…Û"VvÉ™ Ñ½>ÃÈ“è…Ë®šÄÚ¯þ&IlžtÐ*Aô}v2÷ŽYDKY24Zª^õ~n† Í~U,5 ˆ¡{:†‹VǹBï·ãT-6\"ó6÷'h÷ wˆ×ŽÞÜUyÑ`gÈй*ÉðÈ5¢s^×»GúîŽÔ=±ãZtvdË5½}°Õ”Mµ4*¶Šìî¹ ÐC- åÜD®Pÿåuäø © 5;»üèâÄ=–µÞšpõ¦åÂÓŽ½¸—zl‡…p‚æ/œ *Eƒm¼^ ™324æY>Lѯ&¨P?CJ.]¨ôm;r~i÷O‚%û9Æ¢w<Æ0ëYªÌÆŸ@c@{|”#ýnŠP¡ývz;éÆÚ@Ÿ§YŠi\ã1Ápç¸=7Vç9ÏšÿÎü0Dýaè9àW7œûO5`ß+Ò6A‡ &×ÿ“=¸f¯Br£³Íè!ªÇ Zñn8h½~_‚v}7’z‹ÑR½#4O*ž ò߇‹×8<àN5„²ø2@çt{MûÍõá õ÷¹;‰xVù04ÄÀ†œ>Í•½òÆ? £èÔî¿`|œAdÄdýÙm€®|ìI³ƒ¸]üÈé'Ö\­sfS ?T¨ŸÇó(‘÷Ôt@¥¤«K–LŽåZýQ`etC]^dРÀ\úd…U8A?õTa7¾vThÿ6r—nŸ‘¨ZbV•ÊmY¯,>ßqWO.G´ë¾ˆ+4‡ï~h¾ô…¢“‚ÙD†þÅS-VP‹®›êyP –¿Hà~j^uª¿¸ÁÍSAFSÐlŒÞkIÐPeK¸c¨ÔBZ›©ÄmÕBw>NЭa1tF>E¿©©’QC¯q…ú¿¿ëKº¦3ôõ‹Sä™Ï®~\ œ=ÕPµà{¾‚kÞך}}¾˜kÉ2·>&hÞº=¤6Ø Ð’ëX˜a!Wð·jEIé^=@Í_‡š½òqáVíiëzø3Ô²·’¨aó.nùÃLví`¤-Òòcïn¸2Th·QjÌþÙ@{è38̼¢œlÒ_Å-[‘NJgp…æGF¬¯5 ½>?š†9pÿâ©ú€nw/tùM]8ý´…­2 —¶qsÆŒ…i.×¹Ïf‘ÉE‘€žüv˜N·8ÆÕŒRaO÷ÅS´ì•7%.> Õ]Ô‘R2æn5+Ϩã õ3ÿH”cÏ1TN¯?LW¸Â5S< ²v:ôœ/xnXÎí—Ťž¯,AÆ®fYeÝýÏù¸8¾'hh *³ã KÓõ—sÕ¯”‹JOÇSTh¿ësF³ºxï\f;>Ž›ÓÙ†ö4.àn6ž©~]ΚÿéZ%Ÿ]@Q“g usAÿâ©¶†ëCϹ½ý­¢Žy&ܼѶðZ) ³gùB€c×ÖÌ‘9­ 4v@(«îߟ{*ë)ÈghG_h<”Î=b±È?îµ)ÀíÃ1*Ô¿“¶ìþXÉЛžWVÎÝÓÉlÝzEz6¼œp  …êìÓeŠži¬¦ñƒ=ÅhIJ â½Öœ¢e?+Æ›Êôd¨P?·kÇHY† öa÷HEã|®÷ŠâçôzÔc"]ü{×üò$±Úº ŠÚïþMæÖØ0Th}ÀJvíc ÆC=™Ã“}Üšé¦LêAP šýÙŠ:4j1Th¾½Ù,ÐÊÙ èó†'¢è'¾ÜÿâT%ŸÐŠÁвPÉ|íáÃ`=òA%ó§õîÌ~éB‚Jæ+\“ÀZi5C%sÓfC0Ö@QÉ\¨¿NM8Ô/.e¨dþBaLZâOPÉüÝâºUS‹ ’¹Ãyh1¸ÈÐööóÓBf»®T2·ššGŸVŽe¨d^p¡nÍïËÐöî·Lóa¹}ü•Ì•üÓÉáT#†¶wþèëÛiЉ€¶÷ý=Î+;›ÿ'Mv ¿‹§1Tºe¹ÿuw½‹Xœ 4”Hƒ¼ö^nâÝ¥bëÛEè¤û«èÖ /ÄèÃuÄáãwî²¶DRvØŸ¢/&|é!YÜ#ï@ÌT¨¯*ÿ|ð¨zC»…´ãŽ]ãÔÿCå§]–qŸÎ,…¾T¹¿.ƒŽ‚¯­#÷Eë.˜ZÓ¦ZŽç õ›!'Ò. `èÄYsÉÇÎÞ\E;¨°ÊàØªOù½þù¤#•ÉÜ©EYÐozWhJþ p ߨs÷¸7ÔŸ«Q1š\³˜@ÐÚ<1‰/›Êšï[1Žï“!hŠê 09¦Éz¿§r¤3ézu-C¥Î5ŽÛÅUh8O>ç»:/‘~“fp4ÛQf©ÂÐPÙ!¤[A0÷ µl07öfȨ,áº(Ì„âÍç Úæ¶B ̸Býk¯’©K]üs*ñ·ÜÃmɃ¢>8QלcnHP¯Q' û•‘€n†•Ð1;„«Ü 3&—s­“ØÝŸI\¡~ñêáäÙ?ß{Q¥¸¢]:ƒî qŸÖqwi¬…;—&r­¾]ŒŽafèÉísÀîÒ2@…ö‡{Þƒe2ûMûP Ì£¹=ç4Š|ç+p¯ ×bП+4_¦Òf…©útúa"¥¾—ûOÕÃM†Üê¾—¡N*:äKn$×6ä¹¹k j®z°Ô¸׿5X˜Ÿd¨Ã¦Hl<È¢ó^Ñà~‰7Ÿ\Òß -Ê+'o•}À\Nä þ§Öû³HôaåRÚ³s7úÌ^ðê+Íí¶ctïÏu¿òœ¤A,·l×trzÌnnìHE¦;¬EŒ®6‚t*i¢¨P¿ÝÚžÄmßH†FïíBþöçî´>Ñã6Zt‡àaÜ­™ëÈÍ|3@ö„Âõ=¹BûB¯À•ò@¯*ƒZ¿dn¸óªù"BÅÏÓYc‰6C…æG¯êÀs¢5”Ž`— Æsÿâ©Îó[Dê—dè®)QÄz 7ü”=èͨÏ'_(ëmÃ=³ÿ%T/žÂÐi»êaWK E?{§ëUõ´=cÔ¹©ó ˪T£èšôTzïj)A…ú[«=£2ûZiZÅr“Jõ€¶)p+ÎŒ‚ŠOe5}µ…D™èÛãR¯¬Ï½4È‘ÎrÚKÑû£Ç±ç%a êg;¦$¼E¡ƒ‡õ£¡ úq]Þ\'O6OâÖU%æ½]¸ƒç¬+mñ€^>ºè×q…ö /€ôÕG=1 lVdr _aòÃ÷0Ô¼0[lW\Á_us²È†-t¶f:«l)úOµ¦õ<¹–wŒ¡³¶’e‹s=åCà“? ï×úÂÈ1©\ÿÎ'ÀÍg£³ b7ö¡h %où€ Uƒt!Ü A„®þ6582C½ß.ÕÎI`¨P }v÷q kz²œ®^Üq\`•²/AŸîO‚k‘\É–Ðe‰  ›üí¡0"’;_ ‘¯­ch÷$ØsÔ„+Ô¯¿8–™Q u_yæêÍã^È'‰ÃöqDµ‘⃹¦Ìn×øšRÕD=fç‰Q¡ý§F²ÕY€>{×6ds5?ň:÷ö£hðˆ6±×/5®Ð|ùº):KŒF?ZG‡Zï!è_<ÕÃþšðT÷8C'q]:7~Ó\8 PÝß2ð«üwÛëÙð:¹Š¢Ñâ°á®C5/؂֌u"4ú‚#ÔõøJÐÌ¢cðV¼¡Ý8Cüú×ê¥~€Mv³TCó(ó÷ÕåFž< Ã;påc«ábÓbîþCàNúYîä§SÉ{§ ÜI©Ý ¹yšJßœ^p€¢Bý²Õ©ŒÜ.†NÔH;ŒáŽd×ý“¹i2»!3#†»ZéȲË%Êí“%®\¤ÄÚßÝ2\7å:½ÓN8¿î51¸-¶yס棤ÀþU Wh¾Îå·tÐÚSÍóÁt—ÛˆÑÿâT%Ÿw…Ë`Ñäd†JæINT2·wOõ •ÌÁê¹¶ÏPÉüXŒŽÝÇPÉ\¨¿uàq6(»™ ’yÓõs°²ü ’ù´¤Tºù ’yYãS°ú¤NÐööSÕÌ¡ '*™[,>O³Ý*™KÛLe—(0´½û÷-Y C—g*™Y䃮•CÛ;¿çšj©oÎÐö¾ÿ¯'ÿ‹‚ù²¹èm©uuAo¸hÐçZUÜÚ"q­º4 ÆI&$(Q©ºŒvÿÏ=¶†ýùåÂuL+!b‡Ü‡hivWÕº25÷0´W[ó£…~"T¨ÿûTG’6åCK—?"ª—R¹;o–ƒåæÎ\m‹XmBÑS±! fЃ¡!ý@ywº½y~)“ÊücоUz#’¹­ÌP¡~-®2°lÔC‚–úÈ™›Î]ì:¦ÒÜG¡k`¡k°U2”!¶Cö0´<3W¬N/PThÿý/•ð(O‡¡žU7@í¸Wn[0Û¶ë0E;­ýD·0Th~Êd[Öô+…¡åfû©ö¾:®Ðûí8U«ìúÉà:A¿^¾GÏýóÅ••FšÆL´yyQ‰ÜÎ}\z„9ÏÔxe+_Û—¢4Û„-°Ü¨X‹—-!è2çñc €Žºš@”Ì*ÔŸ”éÁÈáÉ » <ôb¹7¯ðýó)ZZbC2¹KCgAFkW‚ÆO¹cúŠÐ45_Ñ[? ŠJï7d÷Ýæ2T¨ß™}š°èúJ‚>î1Ìž÷åVú­„Ý?÷S4×Ô FÛ<åNï-ÍF?éËО5¯Eó^â í7É«†ð3)*yê:dru÷~!o•jÝYÖs¸BóÝnÖšŸŒ¡Ó\,àuÁî_Wh¾s@:9¤Ÿ¢[½çÀù9f ý‹§*5-e¤_£æG°X¯íuëhÇ4 Õ¨òd«¶î!èS™!¬_œ7w×n f}?”¢ÝÆz°»ëó¸CØ9E†n(U¦Ås軎i%,e¨Pÿýzh›cIÑõAÑ}”5“¹YÇsG+–ƒi_]@—L’7ß{oA½ª>Âå·-‹» q¯Š(:j”yÀEs@…ú]s½ Š1CM…;0\݉û¥ß%€¼Z‚šìÌ×æé€Š»]‚ãÓŽS´Vã&¤mí¨Ð~qÒuh91›  {®@rsweìXݶ›¡eÚuP1Ý[Œ Íÿñä tZ3€¡Ë:™÷l˜ Fÿâ©Z˜Ç³q‹®SÔ¸ì |Ñ¡ï]ÜYJâ<о¹1ƒ%k1Ô¿C–³‘Û½÷7Ñzš»ÌX•=ùq‘«3I†|˸ÖG­DE3ïr‹Œ‡Ã÷—}¸Bý7Œ±ûƒÙrÄo¸[Ë2Àp` ýv¸À,§s\3£“àúÓû9K]úìâZ¹,d«0î†1¤ÏÏöýBý’3oÀôUáÀ-¤P²÷WuDL ãî¥@Op/S†î&G¹ïo¾¡úYë¹Bûgø]QÝU‚^:KáÐàVîðaÑrÕÐGoþgÅ…\¡ù¦Þ@îÐ @|= ùi[¹ÿÅ©J>ò²¬6†JæI5]˜(Ü¡’y÷lPœx–¡’ùξ #ȃ¡’ùó8mâxôE%s¡þ^úpʦ/ ’ù¥y߉BÀ@%óÕÑ1dÆ&@%s‘6ÜÙ’KÐööÓH> ée€JæëW†“ù€Jæ‰[OS­HFÐöî·Sò¬; ’yú’Sä÷΀¶w¾Œ´%ä>@Ûûþ¿çAŠæÿɳñU`>GU„Γ«ƒî*ÿ“+jõŸè#j8›[SpîÞºHÑsõ‡@ʶ¿‘WB|sÒzÔKf>·çšYÕˆ–'¨·t_VáÑ™¡Býs:Ó©Í·íМCKÖWpetäÁ¹ËYŠ^ìæNŽOîÂP)}?¢ÿYJÄú¢ÄpÉ ‚*5`z öjæ¹—ýV ¢¨P¿ç kH¹\A 7¶‰¾þ|Î-󨹩FbÔ¤1 š›ËEhý >Ö€ÅzÃ<¹b®ÐþOÞz4á*CW8L†¶ÄJn°¾ÔnÐâöÒ&ä—šmÉðv¨ †z¶°ëZG®Ðûí8Õù[êA1* K÷>„VÝëÜ; ?hä„Q€žÎ±d_ï44üƒ hH£Î¢V².éEgìO%!Õ­ÜÃeæôÔrY†.š»‘x•¿à–:ÅÁËÁž\¡þ~ʳå/κrúHÖ6á7~K†hÈÄ• ÕÜ©E‚mÒ¸±g¿Ñ…~Ý`ãÂÞ_w¡¨T™ÏÙã^q =ýíI=Ø›+ÔO«`®8ZEP+©º»ëH®õÕð¡°€ êÆÑ°á˜!pw—ŸsæjKS±–ü*´šÿR(¸_ÁPÓ›t,çöPÞKŠCÃ(úéQqêîÁPÁ_uĪRz‹PÃåÉ`õ<Œ¢ñT߷݇rß.€N<}ê-Œ¸aÿ|Õî»L„v˜Ì\=¯Rt®åp˜µj CGEM˜WËé&¸ïɵ–.~5â wRD5ÉŸkEÑ…Ï4 |áL† þ_5ks.Þ ¨·±xp'W¿!JËj¿ad]áz¿ f‰;­¹Sü˜ë¤l®ö›Û´Ô¹×s÷:úų”+Ô/þœ"û=ÐJÓ¹,Ùh$w™Îv óãêÞ҆ǹ¿iyùú*A‹š/ˆGêTh¿Ò‚˜¨}ž¡µ# ØBÌíµe3<’?Ë}÷©‚ݸBó ö>Í‹ábt×þ&°—ï è_<ÕìÜJèêgh‰¦Ì·qlö0Ón :ü÷ Ö6ä ·RöTÙŠÑ‘J÷À¢~,  î"hÒ.dhÊåb%:rëcZÁéú#1zb‹ÆÏT¨U¿µÌþÔ7‚^ÑZÁìç­ä>¹~Þ´cè’Ì*xÞdÍñžÒºiÕÜo=æ™#ç¹EbÀ>!šþ±Ì T¨ŸìÜ`&7° ;eõ¯tSL(ɶ<èã¾=裨 îÇÂ}¬Ä „ëèÇî—¢BûË´‚xO!Coš¦þ«\î˜+ß@Vë"ECeóàŽTA…æ§/O‚é0²˜{àþÅSm0Ï“^û­õ;Ճܧ_tëµ"†î¨ïOûu:Ç­ÿ˜ F7s-k°©w7¸±¡uP54QŒ~9 núŸZÖ_Li×á¤/(ÿz+B…ú°¶fR+KQÃŒQÌ5Ê¢Îׂºš#W§E äúa‚š|Ò‚µ÷ƒz_]=]»p+=§Á³ÞÍOIVmu† õ[SWÀ4Âu êÁÄìýÄ*1º¤LŽËR@o,°a6c“¹ ëçRï¦ ]ÝPLj#)*´¿xI„†g2Tqó?™Gá&+û%/úþóU¢ý³B„ Íïm“A¯möôsHíhZOпxª—7ø@ɶt@Ï´…»õÙÜè¦^DÓ$Ÿ¡ÊwóɤŽq\ÿ0²x@. ¯Ž·‰LsOŽÚ ƒ^Ôe5h¹ÄŽGWi©€ö­L@ßÏßÄÔ꣸‚_ëº1Ãí(ú֣ЛýâÚ%FÀ¯:€ö[i m³¹²ýTÀzƒ6Eß>‹%«VˆÑÁMà­I±Õœì¾q€ õ[ý¸ˆ­¯¦“Á ìÏÐkg¶±qö€šÇmcšÞ¹ÇM¦[›ÅèÛ†lÀœ Ú¿dóY£x€¡~6ú©†s{¼› †ò«ÄhIß|È=|— BóMBÂÅÊ6¾­;@Òtúú_œªä#¿d ŒøyPÉ<ËðquZÎPÉüýÓSäm|A%ó=þùNt³Š¢’¹ÑÕ,wL˜•Ì…úõñ¦o¤”*™;Ö*Ãs%@%óºU:äΨ³•Ìg<Ñ£fÚÞ~™BÙ>é¹ •ÌeGÙ²ÉÉÅ•Ìçýé#~²þCÛ»¿Hæ<„Þ`¨dnžx4âSmïü‹\‰§G, í}ÿ_OþûÿÿòSnáujoÉÐÖç'¨ÒëtîyGv¥WA=;f0©È3"ôvj1W°ãZÁ`ÐV-$¨¶eh¿ú`Hr‡K¾J1‹°f‚~Y·ý^´—»0õ´ØP4Ýæ'LìMPK%mâ>®•ë½ys•  Ÿµažã[®ø×PøâÜ¥…s!¦P¡þ-ß‹¡f— C³TNAíôŠ6ˆßïÈb¨þ­‹äÍÈ`®y7ÇÒ&c• Œ_æµ -ª"¥ƒ=ÍÕˆe7òûq…úmþ9üw¾d¨†­XW6pWZÆnuŒ4^c›`mÅMœÜH*QT´s¶XêüÝThÿ½•ëÙ 7†š?²c‹'mãžØ'Þ UÌЧVdPJQÁ {Ñœú6Îå^•¡ñTUµtÀtnCCÖÎÏëϹ“KÉ 7®ÌûnP{k 7[´ö²¬§høðdØÌ®Å?'#¯ŠÐžz¦ h¶•¢!û²åK ܤÄLŨào­—Gà‹*E=ï´/ãQùNƒ¡üþ0†¾ “z…S4.2’XÏ7¡iC5Á­¿3A“ÌŽ0µÈÁ }5ñ·Èn¶'W¨Ÿž{<Ôì¾ÁP— §à–}×ÞtÛ>°‚ nFúÌpñz·Ñe°b¨ój»+´_´°Ñý½úûä ïíÃUxÖj ƒ¸[>ž€2ó ®Ð|#ßI‘‚Ön!5gúOu‘®¬u~ÀКÐ}0vÝ î3y ˜›˜Kѯ™ˆÖl)núÕ}äÚq"´¢û±UUAUçV…2EcsßC_ò† «µvóÛwÌКps62^P¡þ9³Ü!¼ãKÚÇÀâG %¨¨æ ¶[Œêô'-~w¹/“Ì@Àh@ƒ[åàlq2×nî?Ÿš•§´aPG† õ³¿r¤‡1´nÜeèdÍU*½B£GPôxn…xöX_®üR7|¦„¡~ú1СÇaŠ í/Rdm|:­´‘~oú_u4:˜ÿ:kÅmòùÍ㮉PÁ?]ýÄ#SZ±Í‘Éý/NUò±;ÚÖ”¡’¹þíxâ]¯*F%ó—Åc™×ûM•Ì=Î<‚¼°€JæM‰§Ø¨ÎJ •Ì…ú?ÒÒ‚¦KPÉ\¢Ä£«(*™ËÏj3{?í ’¹á¬ôÒî£ëÚÞ~+¾Ÿƒ’·Ò •ÌGžìM¬-çPT2ß" R®É"´½û·7äÓ V†JæIÃâÀëà@Û;ßáX&+õ1´½ïÿë±PïjþŸÜÕK®5–z¿ã%â×ïW§.­ŸAP¹îéìiÕxŠ&î‘an]–ÚçA+-íÁ-.z¦yŒ¡÷毂û.W¸R¹GÎ^Ù°’{E6’’ã¸BýÍKó‰T @CeHô¹znù­$ЩLãÿš Er÷¸u Ùr»CuëpœÝ9t†¢»†G²µ^Öíw~SzÎP¡~¹LÏÐGÓžÂX5njD?èQ] hÈæ¥ämU÷ÓŽ¤ãÏCܸ­ÊLÇíWhÚˆFš<0P¿r¬Xã(·-ð&]Pu›ûq­/{#åÆšŸ°åèÎ2£·~‡yGmz¿§j5n*ùö®ЪÇj%dÀen¸k$;þV›¡.q‹X¬ž7wòõTöþIÌ•óc ç—3´¶¤n¿;ÄM3þ ·£¥¸ÇÛ —Ç1‚Öð,ï¨Pÿ ç ±Fý5@­µ¨O÷B®qL y«þ†k®¹€Vv®åš¥.fsRêþÉžvyÄ5w>FÎYÆq-RyÛ1*ÔÏcÜoX pž¢Ë'È›'¯{%Fktm©zÐU@ãW¨²MNp›?`o5>4ê^/æhß›¡Bû§ØöfÕË0”½S ã>µ†5/ì+F«_yS9Í š_ç~ l6zv&ÄZärÿâ©–Þ˜A½èá.WèÓ Rî·éßh½S"Cã/Šå¦pÏu½C#­r¸ ò!ŽnÙÜ[ ÎAËÌN­› >K¹ƒoM‡(ß 57{KnGT¨¿åòLÚü6З‹/Ñïc½¹‹ºvfŠŽG¸ÙãÕÙ¸n"®Î‘Tâ¼}%CÝ å áÖ/Š*ÏK¤Ñ2•ܲÃef‹j*ÔO;¡‹ùLçqÕ?/o~Çý w—ÁRöìêv@ÓífÒU¸EFëIÐë51¾)þr¬‚ BûÏÆŒeç¦øê±f˲XÏÕqð!¹GÍ(î¾L´²è=A…æ;žäÑŸÉ€¶Nñ'ÿ+÷•ãÚõ ó¸ïq=ã½ÏѸ_~þãœóo™ªk}ÈÄñ­€þÁSmêÑ…E?èý˰IáÖŽYA:îßÏP…)ˆ¦«×`´.,_èÅÕý¬C*(ê:ûÉÊx*CM÷¼'j½z1Ôs¢3äTÕZ=qñœÏë?xÌX>Њ¾4ëf®æ€Ë4öØ‚VÏcRIÄu3ô±òIÒòP†Ö…µ¥Å/: ¨åÑLh,Ìa¨õ‘tp+oÎé7ðùo8€:ü| ^Ï6rsfnaa# ºëÀ,–­Ù‰¢}Gÿó‘ì„ t°às*¶å¶pßl@gvu`Í©æÜÞ/M˜Ê“‘ܬi'Ù‹³# *ú]yÉð¢SehûKÐcØ=‚þÁSµ*\ľ½Øhû e¿ƒ»EùQ½®ÅPí¹…ä-‹¦èçAв¼(Cï~¡µ[WR48~lWÏз÷ sKO]³?T>Ÿ–Mjç ¨|.ÖßàØubô¢§€ÊçΠÎc*Ÿç=Ï%ö'o1T>_F˜  -í7¤Ói’úÕPù¼îÜ2"ùžIPù\÷×oiÁ‚+ mé~»Ul¾]Aås'—iDË'O†¶t~’¥!ËVFЖ¾ÿoÏȉÿïÿgýÿ¤%êµ+ – ʵ—¸yï ÙËV£Ú¸³€¾¼¢T¹;7á0C«\ÒÍS&ÔqµNŸ$^çj¸’!Æ]ÊmèðZèWò”÷»–z»pÅú޽Kݤ ­ÿ9>­*ánSI¿‹…\«Þ!"z/wÑÙw0µ±wØÏ7 Y¸Z@_š½‡ý]ÝzÇ'â nT¬ß—=ðøX ysBÄ_•Üä¿ õ™5\ï}g¨]ŸJ®R·BÒÕéw}ÌT6äKWl½ê-(^¨©Öh4‰äúw¾k^ÌáæhÍéÂh®è|[%‹ã^Èб)oÁXÉP±÷[pªÍñÇ@ícè’.2é”Çmê”È»÷æ^Å↻rS÷¶ªç>¯JŸgéܳy5@7Ûp ìdàv⎠ízò[¹ÔÆÈ„8†Ïf¨Xý‚”¡V{|Içk¹\§Â¯Ðymî/×Føñ>G†ºPxàÞУ‰>à0f·cy8Ñ2/Рò§-CÅú¹®ëï<4¹6‰¸V¼äÎzbÊ|OsÌÞÂFÆ…p;/°` R4å†)“He¨ØþäÐb¨x( : þ«¹ÓœÝhÎ5mW@܆<3GÅæ¿â­€~9ªGë͸ðTK×_ƒäs) Íý.Šà^4Tfºn>\k?eÚó—p{)äBg® €Ä TŠ¦Î› ÚÚuI[L~X§¨0ë,\™–ÆPÚö=|žFP±þö‰5$ph2CÝÞ¨Á<÷=\׈s`™³ 'zì„áÓ¯qÙô¶Ð¸ì*wÉÑKdî¤F:6¯­Åõim)Z7†BX`8 bý,>÷7רþPƒ9{éEëîÙ6ÁìkÏqÜÛÓv°“\‚Žü’¤®êê:½Ú¯o(8(;s >h“†ÐÙ€:y~%eƒîÈP±~³Ù_ E€6÷Á·:À-»8)ì×Ð4ûOTݼ/Ef¦³‹/A#ö®f#Ø,†Ší·•FÀìµÇ=üÞ6ÖÿK¯oð­M8÷aŽ_¸Á›ÿ¼)mO£¨cñøÚ\FÐ?xªo|‚(½^ uÈû “oߦèNÉä:öY©>¿‘ u…É+ CS3•@çw"—¨ÁpÅP®Sk Û´;}}ê#Ôß –¡›^ÇCvêi@Åúè §Ú>§è«[j Q’p?Vî!åµ ÛÔr¸‹Lo¶߆Ü¡¿ ÜI‡¢#ß”Á‚Ÿ ú*Nî3o*Ö/ú¯)¬ç¼Å€~{1&jpõ:|–ÕÍ™GQ×TgbTÝŠ›^B¸¸eCMJV걆Û?8д5ŽúáÐ0SHã.\è¦9Ü•íÛ±¼¶ý¹bó¯=*…h¯$@eñ¶°éý=î<Õw>@îìUU_òDã=àš]H´S ýò¦ríäš—)XìVCPŸÞgàMž éV4(Ý—ûvEw™¡bý[÷¹K«Se¨^~ ±H+ ³ÁV<öb¨®â˜ž;‘kjþ`o{@mw”ÁˆàZ »G£ØAîí;©TòÃÕë—ÙyÛ’JЂë]˜îAe}Ñîy é\‡ÜÎðyƒ” ­¼õYMÒW®òùHöyak†Ší¿ºp ô.8hƒš1˜YæZt˜-S¿) OCA–3AÅæ»¹[IP{?ÚmõDîqªòÏ☧ÐÜê—€Êçu¹ú,O/œ ò¹U°’…;IQù|Ê/cÐêl ¨|¾³ý1Èé¾Pù\¬ÿÄYBÓåO*Ÿ.ù ]väQT>·Üé óçžT>ÿ0s8˜V'hKûYç£6Ï;ST>Oè§Yƒ •Ï7‡Ó_ãÊÚÒýU·ú@÷€Êç> |ÁmÂ*@[:r?W!ìæMméûÿö¤´û¿Öêÿ§oWG–x)Ð;§þbÍ~Û¸±gޱj;5î¼¾™¬y`µ€NJ+‚k¿ãz¸á8T ç®òA$Fu\£Õ8~¯„ûµO]__ÆuÚªÆDqÅú/üòv¬·´ºé,‰^ɵžu’•J“tΘ6–*0ô%Lý`2 ¹;$ììµaܪQ[d®]*¹¿=£ Þ ëw²ÉŸíû=Ðs“™çvÜ£[‡ËÔo>â†øÜ£2ó*îéIÆ`¿ÿ6·¿îgj•œÊÛ_¾¹ ^aèî´b$ÆrWž›¾Ê¸ÑGak·q\±ùãL>BóRc†*~}z³Tìýœêk‡0Ö3{> Ž_XŸc¹o«c˜Þë$Šv|àÎ&µÀÐüó­,Z÷ŒP­Æë°~  ù™Epk»=C¿Ú¤CìÈRZ˜ó’Øé?ÐmÛ°­‹z3T¬ÿ†Âã²Ѐ/û`…n07øÊ:öòŽ7C3é»K‘ܤu.¬iÒu­)¹B—XCQÇ‹= pr¦ %wçCú®0@Åú]¿žË‚T‚J'²ùCÛÉЗ»1+ P…íkØÉ6î‹„6¬"È‹ î{¾/£©2TôKѹ Ðx%„¡‹e—!Þ_Âý«Ñ$]?KÑM±µ‚Ų-æ¨ØüÄÙÇ 8j$  7u }J7î<Õ¬9,dcG@Ízæ°ø˜R‚ÓhÇN:0Ô5E‡*l™Ë-ù¼rgmÔÖõY"3æ~ì7 º®ŸJP}³½‚‘K“ ­Tîé†úëôMH8cÆëï_ê›Ûª½h¼MpŒ®›ûD1Ô{| ù¨oέ=’t­°£¨û:mØvy0A}WRíÛU€nèÌ4æ–T¬ßû†<ÖzÓ Š&Ha3û0t|§­l¥¤+ ž%‹˜Úc3‚ÆêK ŸÝ @uép²õZWlÿåýàqCµ-®­¥-×>ñ<=ß·7W?ü¨,™œâŠÍÏ8°“ä=˜)C'´7gS†þÁSÍoŸÏ¬í ú¥c>S÷}bŽz™&3¼Zï^N*•ïS4vÆzp¯Asï+@tÀsŠ6¯þ)˳œÉPŸ$ih>7ìéˆCÐÄ(Wab–)CÅú_üÒ¾õY ¨_¨”¤?Çb~’EwQt°n6qÝ? Ž #àê;c@W4–ãŠhnB ™Åá7ݽO4î4T¬Ÿå_Aì§± Cw·eS†ïâöUÌ<U)šªšK=_§qŸI˜×á@_i°Ä³d¨Øþœâkpv«1Cë4¯AÅem®ž¢¼µ©ä:o¯†Ø„=\±ù_Õ@·õ^ -]ñìç}£è<ÕòÁ9,ЧE ¶g°~Ǹùͯ‰ë¼#2tZG)Y±i/A]ïîyoF24ãG-\þš¢Çúã”s íwµòÞoáZ_<3Žr­öü€]5_T¬ÿ˜÷Áä—“  Å¯—¯ô#•ÈÎJW§çŽ üJ5o·ô£ƒ@-X7Kg38ÅíUК©ì8È]ú"‡­ CP±~#BZ±dƒ0†ÆŽŸJ¥S¹‰ûúHÍ}¥}6é)/ÞÁÕ-½!µ‹¨â&².PI®Tlƒu¬XÖŠ¡s.C×)ªâñ Ž7LáÚˆ†¸ø³›ïò®•…ï'FÐòKW`K¯0@ÿà©6Î<ÌŒEÑ‘?ÃX¸aw†z$)#É µu£un÷vÁ/ð•´"쬖:Öä 4?íJQSåȳoèãàž{bw imþ•¢bý%ÇÖ’òÝ•z·ó¢¨ö@†¦),cïÊ$}Ö'œedzRt[÷#lØŠ€ŽŸ’ÅŠ+@Š*ÌSfœ¶04IJ™ìév‘¢¢Ÿ¦žœKšÐ>ƒ¼NZÎM/U‰òó"tÙ²Þ°É  Ê:ÓBí[v˜}ÉÖäŠí;ë¬îðÏgÿ1eR1,ù´”[]VLŒ'h“K¹Ð÷DG†ŠÍ¯›hêÓmþ½üòžÇý/NUþ©{³mî<Š¡ò¹o­6S‰µT>÷.Ї÷<@åóÊ—¶PY¤ ¨|žÐù5ìýùϧéÿQ>믺ëiw2š¢òù‹v{Ù¥ƒ¶ •Ïû}`ú÷:2T>.Y@çÓ´¥ýÒ[q¿Ñ ý_ï{tGS#@ås/ß$–´½C[ºßÙ²ÔNw¤¨|^z·'¬¯ÞÏЖÎ/XnD®¿Ж¾ÿoÏcCe‹ÿäõ²èh×›¡î=>^æGŠæw a~o*™r„%_ýEÐĆ« i×Ðs`UÝAnÓ€q´â})ChÂÌôÜØûØŽî)úùÁT*q+g¨Xÿ‹Ë1“’‚VŽaF¦£tÅØxhhØÆ`øøõ·æü}¢¹áCS2wÂ!)wGõöÎ%ˆ{Q1†qÅú´~D'üÓ MS—Ð £Š¸q;¯šë_ô[}­z‘«ošÆ÷#¸.O’^gosÅöë¶ajÛ"ê1p û|5–—ÆŽÍØEP?¥­L±¦;CÅæ¿Q>É¿s*iý†å犽߂S qüMkS(êØ«µÅUÐáNe[­“Tílk^OQ_‰-Ø<½¨âÊ‹Âàq×¹—’àÂc ½oP½uä^º´æýmÌU›Ð“LÖÜFP±þf)ÁÌ!Æ„¢Ó½ØgkÆml£íJª•th>ª]!×Pí$Tûmdè½¼ º¼˜¢3:ϳäH®–ý8ê6íAEÿ*"jŠn4W1ÔðYi®¼Ïu8:š™>è So>í/nÍ».¬µÿvnåB u›qHŠŠíW)íÍh†Î8¡Ì"“¹šÚw׺þ%™Ú˜IQ±ù=»ÕA…y €®÷Ø;^uôžª·M }]fŽšœlmáqÈ– ñ=—±Ð-†n»Òž•Iûã|5͘ ¨ÞÈÖl¢i[®~F=ÐM¹·‰iVAOd3§24é ”,U¹ËëßyÇV–¥ÌPÿ£XΤ¾ÜmNÑš@@Ÿ©§Ñå]¹g"¼¡ÎѺL|4éÖlö-À–¡ êõHeí®X?u? øœñ€¡g\@êy“»Þ/IczÚaì.Öñ#A#ÿšU°D†N¨ß{-W*¶òÈûToëQ†jmÏ é»Ò¸W5ŸÝÓ_ú¨b1ͨ[¨Ø|ô„dvCвg'…>™ý(úOuç ØxŽ š!ÿ[{@™Eªµ„0têȉdòÕn\ÅÙý©‚ƒŠ Ý»÷) ,]FÑlö´ëUn˜Å4j½Ó¡=®æÂÂÍ:ÜÐö¥$û¼AÅúßÉϦsû2tìÔ6´4Ï€k~gý BP½.MB`ŸÕOû)Sx§e†'§žO¾Q4o<îvg¨4}xuu"¨X?¿/¡àù‰2t—Ò1Øô=‰[»Ý…%Â/šÝ•I'¦¨ƒõ ²Îå –_糓 \±ý‰KÖÒ¦Ué =íÛ‘®3þ—®OüX¯ ]@?f°€Û)*6…v ɼµ€¡ÇðTõ_½†Öcµ<ôFÎuâ67?'tÅAŠFÕ€ò̪~ydÌÝð.v¹ëúàË~¢#Èê\î;/qÒs©æ˜\‹ckÁaþe®XÿØî ÂêɽzRº‹8¼VàÚ.ê «ÞZP´µŸ?¸¹¼5G3Ñ B C›–y¾q«ç—Ñ­KOQ4ìy„°H¯œ¡bý¬ ¡êÝ_ ½¸æ º5‚«£¹–Z?£h¶í¡Úû2Wµp“Ä\7G7¹­#¦‰ýûõ7–Y=Heèä“„ž«R¸í&-c]¸U1î²Y£rEÿ|%à׈e ½ÜÚbÙÍvýƒ§êrû*8{l´±KÄLäznh¿ãª ê¼3‡ä?èšÄpïm W/¾€uðÈ+$¨XëÃUdÙ­Šª/i[Ïêq¿û¦Ã ç§ýÖœNÝ×ê¡6 ¢£®1TñM¨ÔdqûšæÀÐ-G¹ÏÛŸ­Í*Ö϶…hRAÑqyÙPÜ^‰9ô.Ñš>ƒ»gD°¹ÏÔLg&¨_TÔ0蛩–ÅÛo¸¦†D†¦ü^B¶Î‹ç69u†0¡?wÌUx¿p,AÅæÛ¹?õÊ€ê¿Ü ­¶qÿ‹S•ÚßO…Ñá€Êçú½„öá«•σNÒ+€Ê离õ å•Ïëúe™/ùfÏPù\¬¨’8)Ø èÿêwÄJ„S€Êç?„wÅŽ •Ï'§ûׇ¬ ÚÒ~niqP;þŒ€Êçðj,üh®'¨|^0Ê1½`@[ºÛ݃DåF4Cåó~ÆÒ†yꀶt~Ý×Bbãk hKßÿ·Çó‚‚ÅòdC+Ûè- ¹ûXìÉs2´n»6k^hûŽ^ìèãíÜ­c”ÀEÙ˜›¨?CºfKWën=í·Á¡]Û´¡^ƒ3¸9 ûnš A/›d‰}e¨X–Æ&14ðÐyè³'ˆëÒ/‹Ä¯?Á}1ܤܡÏs £û^@//YóîŸã¾lÀý¦Cc‹^„蛣bý––ǃÛãÉ Ü…‚¥¥÷éÙ9¤ü`. &†útq7Fi!ÄÓv ? EÏTlÿÁ¢Ù,,/ÐxEÌ(Qµ¿S^¾›¹S¤³ ¡øWl¾âýYäήx†Îûà Žýò¸bï·àT¯Î‰d§gî¤èâSQlÊ—ÛÜE›£X/ïá€æ›$1£Ý u5ÆÒ|wJbا“ÜÇñÝànÿ+ ½œ— +†¤qM²¾ÑCkNq:¯‡»G“¹býé­kÐzØ|†Þ¯» ïÖõç®ø;jßfpø¥r‚+ÖŠŸ ,t'š£Ù­2¡4ÁŒ ¯õ²áâæ+\óY+¡ÌEPÝÌB™Í“n»¶žÜýt˜ûÙð¬ëëèÍ^]a•êE®X¿©Ÿ?‚Ú¾Ž€jh=…m¾Ó¹ñ{™WçT- [É<ëÖPT]3£ÒZÅÌ ã @Åög<•0‡€} a“†~&è½²= >ÙC@o,K‚©Oæ*6ße°6±ÒÛCP·ü¾²{_«(úOõóe'Ö¸r?Cu?ešc¹µŸÖ !ÛŒ¹mtΓü"e®O³.Èz èÀ ÝàjAfKè¥ø'”[/}8eC›ÚE@ñ=7stÓ‰1´.ÞG@Åú׬ބօIMøºR6_ãnc/È5?¸ÙN—­±wkøFpóW¢èÔÇG`³Ãq‚>ŒÛOͦõtÈüëÄúéiŠŠõÓW» +FI5zš>EáÜ"ë~lÈ×—­>NCÔªévž®y,4*o «Ú™+¶ßb÷vöe{A ·°!á¹O¼LŠ‹=½W/(ãŠÍoššM&¦ìbè”Ð`ØsˆûOõ—k–Ó?ƒ¡‘ïnÓþï³¹Í[zÀãoŠúN5„”EÏ4#Jl2ŸèsÓVlZÌ<îß&!­±ˆ¡f²S0aj ×´§N›ŽrµÝŽÀ‹È\ÑŸ•ºZÀ› úBE>×ÝåVuµ“99S4{‰© €C&ƒýòU€ú5¯…Ÿ§¹M]Š z“7A-ƒ!|PÑÿ¾CwC¨J ³ÿƒc¹K„÷¥* ¨t• ž}Ž¢®BUMÌàÎ[ß̳‡qÅöo*öfÛ¶, hÿi«˜k©*·ßõ·Pƒ*}VwO‹ bóÕ. pì<®mCÑÿâTåŸÈèÝÔjSCåóÜ"ððŽ òùŠu¬¨FPùü™ÿ3x>CŸ¡ò¹Ž÷)Ë- ò¹XÿÜÕ'Èq­B‚Êç$§Üc¨|^¾¿˜ì͸ ¨|îd>‰NIÏ´¥ýtÛ~#o}ö*Ÿ2ë 2T>×0 !Ž€¶t¿é“…¬Jæ. òyÝ…óÒê#mé|Ÿ ¨©èhKßÿ·GùÄ{øO¶ÍaPÒBP5ßó¼¶–kwùÑ#Ðnf´bÏ~®·åøhº€  f¹Äfa!W·ä‘mûÈMŸ+S¹hŽ*Ì÷#ÝCã-OCìÇÅ\±þ·½î’·#/êÚ>ñÛð/­rË…ÊÊ^ܧÎÒ‡t¹VËžREçÙUoÉ"Üÿùñ4dçd»æchìßö0¡¹+W¬_ì?øø¥˜ ’§~p~_?@çúü$y=–s}'¹—¹{¹:^ljfAo]ˆ¡÷(*¶?ôà)8‘蓤txý(š««uÞ\Ók&Eo>j+K&1Tl~ñ—Lᕵû;SÖ4ìEÅÞoÁ©î˜P ÓÏi *%`ß{wËšvÌóï=Ü&/'6c™7½Í Z;ì§€ÖìþI7m¨¤èq=uºvß †v½L”4ã¸ÚwWAuR&Ü©ÕG„6ºyÜœ™sXìÃ\±ý»~CÑŠ@k×K`Ò€4nVEìªéÎítj(zpÅæÏøPëV¬fè‹C/¡Ú%ˆ¢ðTµ2€~ ´u›CP3ñ0wÍÊp¦Ù)C†6|ÞÌì£*)ºuDPÉ)ahкh( ‹çªmªƒ~'“(Zš]‡û'4úTÔ6õám$3Ñ7äŠõ÷: É6œX hÅìl˜á:kvcó £šÓ£5uONáÞy¿ž· ážë5¸Y£B(šøö°¹ñ6€Šõ³×ïIò,¥€¾ÜûX6-·»Â&œdß!hQQÓ, gBæ3;kÚ5«Ì<_]‘¡bû•¼W‚Ò»t@ƒËfCXù1®ñ½3dßfw®[¯{Ò½Ò•›ŸBÊáHIAm{­¯U£ýƒ§ÚñÞ^ð[’ hʯͯxš«÷s4 Ú1€¡^ÓйV{óa°æn¶ñQ¨M —¡…ꎰuQ7@u`"éwã AS,;Qµu¡ M7ÜÇc\±þêáúìG•  YCtÙF›t‚*7%nIQ ½k>æ\ræ¾xà Å÷íeèVk èÍâZ·p=’Ý“¢)*lÏÊd†Šõ»÷é u·Ì´kCÖ:ç÷eÜ.öFoE›ýf2ó¿Û0ôí,øa›, cÇôƒ}z€Ší×W·€EÙÇmØ0&§sóú-"ŽoS”ø®g †ŠÍ/ÝNnløIPÝë‹dúÝžÈÐ?xª+rfCñßE€Fæ ‡ë Œ;6SZmÅÐ^Z‡HÈ£¾ÜŒˆYàõ%‰ .jî$2?‹;Y¯ 5QÞHѰèáÑ^ÆÐø:èûûÊП=äõ$)AÅú+†©1×3ªëù€fWw‘¡oØA»15õ¿ €[\g5¨!$¶¥½àüœV¹·å~ž2$Û#:å£-è®ÚoŽŠõ3¯¶oþ.@U¤.,ʆë­Älôj­"Ыل«Ôåµî•èºC;Ùç¹bûÃõºÁô”T@çY+À­‚#ÜØ;×a í+ŠÞwn„ž1›ïÔ«QHº3™¡›`4X­ˆäþ§*ÿ|~¯­ÿ.T>ïèÛ }/PT>ÏÑ ¢ }Ρòù®-½!Ez„¡òù’À5ô®ë †Êçbý_£zêc(*Ÿ›ú¿"íM**ŸûŸdží=NQùÜO%œxÝ'hKû pö`öKT•ÏWXõ éÅÃ*Ÿ÷n?˜]î_LÑ–î7mWK6>? ¨|>R­¦wØhKç“5¹›µ“¡-}ÿßž«÷žÀ2³s]’+¥èƒAÏ©·e+†j[ކÝ3C j^ÐŒÇvTxA±Ü#IJà¸à×&¤Æû3‚–ÏNchÝÜ0éèÀ­m{UX0àAÅú'ßêÇBT]Zbßž-VØÉ¿Gê¾}HÑœ±q¤òí®ajUØ6ÖU¹“ øK¦¨¯ Ñ{+åž”‰ã\ÍQ±~²/íaÛ•,‚jôÊ"Ûé+îVb /ÇU 趺ð0ñ*A†U˧ú€–ŒlÅT/ÌàŠí¯v;+634œžÕÜXóëp{@)Eï¿9 þC*6_ûÜ|ÕЯ|áàÑ%\±÷[pª…ô1MªÇP×7èµiÖÜÝŽÏÆé€oî%|x¹†«;[X:‹«S7Æ­»CPŸ—ºçh¢Ã[áŠå‚V„‘žecÚØµfF˜qÅú»ܦ3”£j¯L_ÛájçÂ:eŠ®–­úìªP¨Ë+(êöpX\wâÆoŽ6d Ï5ÕȯïǸbý–J 5·”Õ¹Òš– ÊÜOìéw}×¼` y?ÖŽ[û&éEŸÐɌ٠B¡bû«|d06`Cm+M§r#_Œß^íµ³X,¸½ØEP±ùª6gÈ{uP‹­Ò¥›çôžjs¿BªóСùcèÌý~Ü‹#èÔÞ€ª´×b®~.ÜâãCd:ã¿ÊÐe§(šô›¢’i}dÃ7ËPµ_^äq¯ ­ ¨‚uUwÚkü2¥`< bý·6”Ë^Ž:ÁÐ#o2…KÓr¸· wÀ¦t%@·]^!³Ws]j3 kúx‚öφ«•†€%—Ñ“é—zG%Ÿ„úTRT¬ßµ3ô’Q#A“檳‚9\oM jÔw! ËÛ½ žŽÜÏçɸÃö ­kÓ¹ÉP±ýåW/‚}ä8†Ï+‡RÿA\›ôL·­J/v¯ `¨Ø|¯3šú€¢&ÙŠ8Á¡ðT«ëçR³0†¾^ù·ÌS+žÒ~.ÛY;зwv°×}/4eÜjÐ<Û™¡ñ§!eÉ!Š~Qž¾Âz†^‘VO¬¹n_B¥Þ¯Ò)z`ÞFø¶BÂP±þÓƒI–sCcŸ6Ï®'¸šZ¸ó ‡¯N%mNrmî‡M;÷q.›BhæI®ÊòLP±U@{§Ä€õ…}€Šõ»˜bÆœ–=½Ó™e7KÑ|Sæ'ŠëžêBí½Ò}jבµÿ+[çíM´‡gq««è7Ï=Üã6¤KB2×ès2•í'¨X?‡¿W0Ë›(ZT²’E¼ûÀÍíÀ–vºëw »Vý]†~M bçÊv hyv6ÈÄ”¡bûûYTÁ"áE!² ´æs·.)‡i_£¸3ŽÅÓ€#›o1¨’=> ¨I~TDôžjÓmäÚ¶†Ywž8µ:ÍuyáįVä*—vgK­ÆrGƒ…~( SW÷ƒGq±Ü»‹@ÍQ+®Íh ðJ˜ÄM̈“ÎÔž M‡’»ï61T¬ÿ”uËà–ãN†ºøn4îáD`7î´r°+ÜBÐÖkKè÷ýµ²‹>Š¡¾€õ~»Œ{1Þ—M?|‹¢צ²vÒÁ íÛú š6†[G8ßP±ýëºVÃî}Ý~îty:“ë¼¹-ßùFÐÚÌT™ÚÖ§*6‹Ÿ Üøähµæ|ò×ÜNÜÿâTåŸ A\žå1T>¯îN×¾žÁPù¼ë³Î¤l òùdã£B€Éy‚ÊçÊ¡|á†ÊçbýËõ ——!Cåó+¡Xú¼›2ô èr»ÈPg;åÊ=’‹1°»8Жö›ÜÜ)Mb¨|^0V…580T>·Z·ƒ¼ò_ hK÷?Tóî(QT>· XO¿Gi2´¥óü¬evÇ?JÑ–¾ÿoîž*øO6y:À¦ÁË :$Òâ6ärCO¶éh†–þÇÓ^QÔ¾r“ €þBM;Ør5ªlàØŽJŠÚ-H‚ãSòe¨RÅ/ªî=‹û£@^fST¬–S¸nТè¥ø·­M0\³÷ú,±¼¾Êý¸t ŒÊŒghv«èﺙÛä³S€i„k²Ü‚ÏpÅú-¯ÿBÿÐÔçS,Ñ*íÈõc‰Ìúe®„:5QÔ;ª/ÙØ=‰¡Í×–CÀÝ\±ýõJªÌèª;C?¯ùE?î^Ï-ÍOâõTãuݛЅ¡bó#//ˆ¼7µŠ:#wuàŠ½ß‚Síèl aïªyw¤ÞÐÔ×a<|^9‚¢×ªÇB“ƽ¸|k\Þ ÐÂê•ìýÆÁõý}^¦<ງä†Õ€ÆµÚ)ºÝ¸±ìØÿý½¨\±þaš¹ðÛ¤#AS·ù¸/…Ídq¦/ »ÚI_7æ ¶ñ Ñ½+Cç—G‚CnÍh,§äL®¹•ÔM*ÖÏíïÎäÚö›Õ­¼bn¹Þ‹;¯‹#˜ÜOÑóªã¡t]Š€¾ …›vQTJ{ècœKP±ýƒ®=§zÓ¶3ToÆU:_q7W¢»\öÑ)”›´º-_žÌ›ŸaR £Ë ZÿÏÏõ«úOÕÆÊ ,Fjöp$J¦sü5¡èÙ?߸ÿÇÈÕ%dw‘& Ký]دÔ8Š*Ç«²31Ôëï¾ÒªÆý`YÔ;ˆk»ç=-›7ˆ«U8xsEEÿ©ežyͯ *=œ zÖ}ÕóIVcq’ ú9A²'ºÈÐÌvK@ñ½=A¾ÿ@<—p/ ž[¹FgTéê1c*zª-dÙŸT4öSˆì›óXŠ®sî ³tlF!)^þ‰;9!ŒÆ*:s÷ÿèÉŒU*¶¿¬w5® bháÊ(Z«ʵÜï|¸:Ëâ ;KQ±ùù¥a…]( ÎùÞd€°ûO5°Uw0U[ ¨¶¶D¯ÜÌõwmüïŽá=4 )5„ë;5Tvb¥CÝú—é-®×Ö64G¨÷†ÎÔpã1ÝPº SdèËë{ÀÙßP±þködÂ÷‰€V_8 ¯í–q-Öu"ŸvŸ¡¨gík2m¸:Cá•ùœéÕÐȤe­‚ÔìÊsŒcèTǓР Ãë§îßW–yâE- u…e&š ²¹Lx8¾3 }ÇÑUÝÿåïêžôÔh†úÕõ……æ{¹bû­¯yÒ6¹á =hD×dGrïOX ‰ç, zmR1ßv‘+6ò Ù¼Ûßê6ÒXæ!äÈÐ?xªW']%gzî´À.šÙ»‡ë5¢ž–*rK²Ø£ÊܸŠp?Ïš¢+âMÁÎûA³ë­H›ç(zº‹9T¯We¨[ïkÂÀ'Q€¦¯Œ¦ežÓ *Ö_µ" òoìÔuã6(<Âý—#ôÏÔa¨~n,tÛû‹¢»íY«Õ™ÜJï÷ô‚®CUâýà˜WA«¤Ý„¯ÿ¥X¿%ÚIá÷ñ }%K!]‚–pÇ9?¡Ò; Z·_Ÿ=,;Ì-•øBùM)Ek‡µƒ=ÎYÛoú¤NöB)†¡‰7ÍdÓNÆrï*¤¥ôåuU–ËÞPTlþÒ¡ÙDUªÈP+êÒ¢^Ü?xªö&$âÕ>@Ëw >ºÜA=ç²Q½ôþÌÕìæWm-ÎúJºÎ$€þ(›/ÔlßÄÍÛŸGUORt]ãyÐ/kEЧ»A›Æ‡=› }ÿù¹ë?æÀÙèÐý†ðÍ;škÅ ÁÜm3E]®–Ãì^t«ÆBjðn/C• 'C×'sUÇŸ ãôu¹FFÙd€Z2W¬ŸwÛÖà{g CIÀh˜9m'wÜq[–¥™# ý% Xƒ­EÓœÐÀíE%.–ìfYEÅög?>hsìC?=>.¼|ÿ/K‹é3ß]ÜèÚM¤_I2WôŒî¹ M9Iшñ@²†ô¿8Uù'ÿÙYt¿@åóø<V=d=Eåón jéšë+•ÏÝÏBôñA€ÊçÅíVBñáp@ås±þñêJ°¬k ò¹ùÍ ÷ÏÇ ÿQ>ßY¡ ?0T>/HÙÙûíÚÒ~ùéË¡¹»Cåó;ns™{ô#ŠÊçû*ïÑ}Ö~ mé~«=ˆð†Êçþ#l!±Ýn†¶t~Ñ“<8chKßÿ·'öµþ“ EG¥'o·Ðâs¤±³£d¨Æ©LÙòX?†6îNƤîâ:?†¸m¿ êqÞv¥vôn¤»y9 áþíþïï(¥èL­5ð´L‡¡½ëzuhAÅú÷H %§ÓžtÕ²‰¼ç†Ç“ÖÎv …Z°áÑj®jè,Pß½ƒ µ—ÇÊ÷º€¦.oÅ<£nÊЙayô”­CÅúUœýDuÔcÎo›}… ;ì[C—ó{ªc1º[ùq—Nš–n€nJhOíéÌÛŸö¸„J‹&úlÎ=*»B¸«§+A£O(CÝÖ‚©Wlþ'ËÖl¡s7@ݧ|¢’²*z-8ÕÊf3-1µK"ä ÛÇ_Ä5ÚÌPkg °vtä.Î"©;´¸ÒHê¦s[@ýÕ™Z˜Ý¥(Ù{ŠD?¾ÎU[›F{n $(©ìÎ>Ñ`¨X£Ms¥ª#>ôG¤.¿ï9ב,ƒðéËZì+{Yq;T†ø¾+­½;žL|âÏu[y“ì ÜÆPåi³A6d?EÅúée~ µK ÚVû& \+ Ò-n 3Ÿ¡ßmgï!\É®‹fçhQ´Ðù;i]?œ¡bû»†¼§ñ¯Ç:k˜"3jȽz),ƒ“(ú¼ÒŒ/%¨Øüôœ~t°çNŠluŒØ¢Íwõ¨ûn  ©–¨ëÕÉ\_("V*:öå[Zú)—+ÖOmäaZõ&_†êè¦eçRT»r'ëÔqÛu’@i7;îñˆh0žÔ‹¡Í.RXðþ†ÛßqfGv#JФ¤îì–™6wííP2:«˜ ÅºéÞ,®ØüEÊ6®?Ž¢ïO@Ä‚õýƒ§ª¾q>YéÙž¡¦j1¤Æ¹7±­ |<«CQOøÜpF@GUÓì ݤj N>Ûþ¥×ø9c: ÷Ç–“`“ ®ÕÀ42åa+®o».lÛ FP±þÍ*J윲'AÝÒµ™dJ³€öø’ æ5¡\»¤ÈP?DÐ [uÖ{ô nnçÖLÿQ€®¬1fö¯Ê)jçxVöy·+CÅú…½‘úTÄQTšÑŸì,?ÍÞãºß7š£šŒÑ˜BÐ¥d¤ÿlèÇé·ˆëñN\±ý꺬nX'@u±˜¿Úp/whÇ´,%Õ7}B5ûX2TôœK~1P Œ†~ÞþÜ?xªÖ'q z´ç"oÂÍcÐú},Al“§ªÜ÷¡Þ<¡C|ÒanÃcŠ:L´•Iô}âu‘VlšÀ¦Ï.x¼§hÎ|r6Çœ¡¢ß€Ì‡0‹“"Ôü(6dŠ*EWûÛÂÃ'Íëú‰¬;òŒÛ˜J“»Ï¦¨Al¤ðËYÊÕ:Õm¸ÕuÁä}%AÅúõË&TQJQ £¶°«ç ®±o[ø¹þ(AÇG‘ñóK¹ngúÊ$´¤¨Ú#eòðÅh†ŠíŸÜÝmi$èçØQìhë[\ÌV²÷'ýz1þ=vg3Wl¾Û¤]dH¯P@­õÚÒßó¹ðTo†k€Åe †¶·5€Ž5¹‰w”IðŠv€–Ô7É<•Ús}í’@½Y_† Ár«½]^ñžV/NäÎZH­¼_ÉÐç­Çµ,muAŽvž¨XÿÏÑÌl[0E×±ïõ׸n­‡“Ü«G Xì'3’ä hªÆr|`E­T§ƒÔ¯'wQÿ)Ô®ÖÐò9X^É4‚Šõ0x{DQ¿'sAÍWŸ;y¥ð~=%èŠ:m{$×ÍÓ ¾­ØÈP¿}©ðЮWl¿OVw¶ )«Í˜Wùanâ0[¨öíÄPÏÅ“AÒVUŠŠÍ?›EíìjšõµÜm® ý/NUþIœd Ž“*ŸûÏÍ£µ§ÞT>w;Ù†8^'¨|o:ˆÌðÊ¥¨|~hè@öñ’" ò¹Xÿs0…ú¶ •Ï—ø:fãŠÊçj$`3hAå󾳞ÓsûÛ0´¥ý*7CöÐRT>÷»)£…ÑÝ*Ÿ¯˜•^Ë3´¥ûå ¬ÆuAåóc&UDgöu‚¶tþ5æGäS´¥ïÿÛ“z þ“ƒ<¦Â_&Mš …»Gqk§Ó¾=Ó¸»¦µúÊ ýxwŒ iè¾Ë/Èm‰-÷ËãðâlA}LIòå®Äk)|³±´d¡@¢opÅú7­t’mya@P»ƒmdU«Æ hó⥠ѸlŽ–  î ó ª¤O×¼æ.}n:N]Ý0>ܙ֓ÛóR6]n¦Ãëçj|—.Ü ÁÐg{sè;•áܦ)ÊDïØ(î×õûHÂ2{n@ô|Wsš¢ÞÓ/“‡×ÏpÅö¿SÀ’¹Š€º½ºH;q¯|ZÅ*[ÍQ“EÐ8@P±ùùÃÑEêíbþ’ÜÓm¨Øû-8Õ;/&€ãÀ\šn™­ŠÐúãk¨ç‘Q µ0½'{pd7j]r-ÑÐå–F4þÎ,nÓô…´cj5Aí_µaVøº­¾›û—ß“¡þ‹ õP7†Šõ·¸¡a^f#CUêö ¥fE'4=$“‚Žôµv/²z@:÷âv(³êè¶U‹HÄÐ2‚²ZÌôª€›Ò‘nÒ¯§¨X?Ÿò­´ºa"CßtjE¥wfsÛ­h ÕÏss_ôƒø›î\Ý~3Àhzµ€.R2‡¡cG*¶ßèÞ&п¬ ¨~oHY;€›8I_x¯OÐÆ[Ǩۖæ¨Øü‘Î þsFº"éç´Ü?xª‘I#¡ió. „ÝKu j0c¹ðý”'C—<ò!ÍÁ>Üá÷.е_5ýQG¥Q]­Í~ŠþÎyIÍ}êÖö(Ì÷´¡hwY,ŽxCP±þ÷jÏÈpŠ9¸Ä –qõßß—=ü½Š W·$ЦݓTݺƒ´ù-·nQ”4ÙhEõ]:K×ÝÜ×_­!xçC‚Šõ³ÛÑd¦Ø<Ÿ¡Š»‘.çÿå ¿ð¹ßчyú÷×aÙûç€Ê¹w¤=~ÊTh¾Ê³ÑD·Ã1@?¬6!dÜ ‚þ§*ýè*æb¾T:/¬·%•Š8T:Ÿ)ò# sl*;[Tóš}*oõ‰¢AÇ(* õH #j(*ÏY¬DJ¯qT:SN$EÛð¨tî¦ù|¿-#h[û™D"ŸÊžò¨tî0Þ„hîT:OŸº_Ĩ´­û­çõ'vd1A¥swUYÉèæx@Û:o×lØßw AÛúþ¿QæNòßl8?š&¾½ ¨ºÄ“VXž`OT#ò÷2Ý%EðíÛ×ûº“ÉVý$(Øø‘5¿OM§OE6›HP•Ž[`@‹Ónƒ R½hzðösŸˆA…úG.ìÓ¯™IП¼?<>nÂsù&0s 'Œ?ÑóV¶zÐý2Ø\4tv'X±ê³QÇŸ»¼Ã¢÷%ôüN¦P?7µD-‘— #SK e¤&SÇNZo¥j5i—¨¨Ç¬å»ý=Ú´ªÕ–,Î}Ë£BûcÆõ Çft” ¾SIAO%&áÒÀÔ? PO×ý|nywèÝIšï³¹ÝØW‚Z×j@íb{æ_<Õ¤XZð\Ð9»´éå• ÚØÿ ߸m23ø«­¼s…G+ ¿Á„@×E:¼w»™áÞ¤røІ+Ž"2ÇÝPƒ/ÉL™UMH˜C6ÈT¨¿£îªõ•¢Å-I¸Â-惭K¨{ØsfkÝñxÏvt臗Tµ:uógš›Ý‡G×FÜѾ•ÉñåÒ•Æ*Ôo_#ñË7– º‘“È/=¦Ó»*ŽÞÊ£hS³ ˆ¾3ÇO¯¡%{Æ §žRc×ùÚ¿çóPòÊKQ‚*Fº‘K˜»U×Ñ#z]˜u’¸€F3¦Ð|y¯G0]¶»m’3 ÿ¼O¥è_<ÕPÛW|¨A“#jк“/Ú0G¿6N¢ Å)êÖÓnö¾Ç ÝVÁïj¨;I¥2Ës ÑA2‰ç¹U€&茂ò3‡˜Êê¤ÆÙ ucÊD Û ™Bý×íÖ$9O÷SÔý„>‰§³™»Ç8Á=¯ÌìÌ«P|¬v•ºÆôÝèIKro2sË'Áö%Ìs9@ÌhKP¡~Õ.þ$ðŒº}¹&œÄOùNÑ•çÓAeºŽíw¤RΚ1ÓƒÏòöJ?)úñu LŸu•)´_ïÞD’V×N‚Þz6“ÜÉ“a*˜T@Ú%&hêUYš²ÌˆÌLöàÐ{~Ÿ¡½-è_·ù€ õ›âCBkË(:Á)ŽÔJe–æél$hÝl'ÒNÝŠÉ9˜%Åšñ¶ ©ï¯DP¡ý¾ûª¿Pt¬?¹¤÷–¨¢GÖM ¨ýuX5N– ‚¿5”F@YAÏlG÷­ïÄü‹§j–x†Ëÿý%éAÉîÕLqåjŽ )A[£< ü¢'SoëÞ,™ÿ¨yìÀls›®šGÑzÇDªé¯$A˞Œʄ¿7‚g<T¨¿õÐÞÄù‚˜Cw/Ò%Ó×¹ª`hIÒFNeVöïEJ×Ö2Ϻt NÏC)ÚÔbFÊG‹Ñ]SîÃiÛB@CÏïÝçê·Yyq)AÑäÄ8’hrœGEsˆÍÈ^ôÔ©µdvGæ·ùŽð&Å… G{O¦Óûé2…ö|_EG<§hé¦õD2ëÓRo·¹Õ Ú1Ô\§#Sh~â¯ótÇý+€V~K/­çÐÿáT¥=Ÿù0ô`,E¥ó”½ùðý›‡•ÎcN•BÖC *ÛÉñ‹Í%¨t^aШT:êoTҙ̘•¨t´X–Ì=Ô“ ÒyÒËAdNt ÒyÝÑb´­ý&<‰!é+NŠPé|zï(Rl›¢Ò¹Yu* Èh[÷óc6µ«Ê)*Ï ~LØh[ç®,¢ÙšÖmëûÿz\‹6‘ÿfø WbÔCM‚:¦{’–òÌÈf=2j‚)óõ3{¨«ÃLœ}†ºu' #¸;*„C :Œ&“½Ô$hK¶i-¥­ý|š KäÑf×kü.…‡êÿN´”wAQsõHÒÓžÙ:ô,ê°@„¦§´Šr–´£¨wS$)²zæ€j¸FEÄÐïâ6šÔdÜipÜŸ)Ô/ôÙiÇ-J-> áÎÜtaÎ3Ž çߺêÖ²†t¸ŸÌœ9W&m± º'>@ª†ShÑærqè…Ï"T†Øüþ:È9¢ÞÆ]IîñP@4®ÁÍñ²šÿ}˜.ßî›–­6«ú3…Þoéþº1…øtøHÑŒŒYdñ‚Zæ—ùcˆ‘£¼u?:•Ì)*§hñVu(X6Šyn[*ø/¾Ì¼t ùnÕ‘ìFº<Ë¡å›*`饣õ™Ô‡ÜÞ3P¡þ«Æo"ŸBÂyôëH¢þ9دîÝ|S4»çE¨Ü²•yÐy)ù¾d™¶¯ŠéýŘœ–=BQ½^ê$t˜1 BýÞ¨;õ·i'”Ëlb>7ö%G¦^´z„;‰Üÿ˜9¸Ê‚TÌ¢è)G]ÒuøiÚŸ«¼š/üPŒÖNåežùòèA»ö°õX‚–ßýÌ¿¿¦Èš¿DRþï´%hðÅnd¯lEÿ⩺«Ì#A¦—)*~ëC4ÒŽ2„Ì"©®Û˜M#ËR{2ó^É“söߘžqäõ¹¯ÌŸùHùÚå€r‰»µ‘Ì—gË TË€ Õu]øìÏÝ™Bý³Ô7’jñj’F¶Xtt]¢ác¢)še@È‹Õc™…£:zºPž§ÀÛIÌüìÜ´®=dêFÓú×;˜Bý–n+ƒ¯ÍMÛ'GV,ˆdòݬˆ¹ù[@s;‘kþ-LûÛ`¢HÌt¸÷œ¯,.d íê}šoM;É£#-nð†’·ÌE~é×ì€V«ùѨ«Ã9Th¾þw#’gý‹G[Vv&U9½ý‹§íâKrõ(:CÖ—èwZÊìZ?†øË>£-Vö$bb‡ú.ó#‘/¯P´¨ÿ2óŽ73ôl£cæL#@ûÅYÓ¨LSÍðÓ£›6vô…çÈß½†¢BýaøjÒ¼u8 þY~¤1` óÌð9$~íUDŽ+ÌáPüAà1ÅP• œsØcÊ>L§Þ½È£¾®¢cfg(*ÔO¶V—ô¨õ§L-[’ÕÑ™9ùÞCÐýZ hü¦t¸À3§'çÑίîs¨ÆëíÔ·yE…ök¹ÖñÉWµ(Z°è /zeÿÇ®²|©±-³åò]Ãü–.9çýßÌ9÷Ì¿xª*z>Äfî Šfì™Cf|aî÷íAÞô´tâÉw0Ü>ˆ åá¤Ñk—M/[HzZ™zÜyM5K èº´èÃ< š7’4W[P´¦gIÿÿÄÅÿ)ÔÿtÊ â|$ÐÔ5ndÏë,æ É:â4…yyãJRYZÀô|,’ùâBÑ£#¸×n2¯iÛºEÌ{2éÊ @?Ë…¸’æ~Š š8¨”¯ãÑÂù^°.}  nÛäàÀ#f”Z7®5¬”¢OžÖÂÍ ñLÁÿ¾Q_ùš¾S(ªyS†fÍ\Íl¬Õ"{®¸2s›Æ“:.A„ ÍÏ.R¢¢”‡€^í¡Æ73ÿâ©.œA¼OâѪ§^¤Ü‘£>ÙÐëx, Ó#CÁÉÍÌAòö¤«s³öÞ{¸±étµ+Ñ·—]¥ižÌ7²æä¦y‚‚Ò4pÞwP¡þÖ9ƒ‰Ýç<@ã2‰ó”sÌ Í™D½Ç'打Žd±ŠAcÎü|®!A*O¯aÆô}¦Jêõ"hÊ)cÐîbÎê§§²ˆ¨Û.B½® ÞáÐôC;8«gM"´¼ƒ+1Oyôê sòQ6‘CÝŒ{“jµ÷€ ž’—*–¢d镤ÝLÏ>dÍ.@Ø»‘ ” *4_!ï$Ýž!âÐý™!4v¼%Eÿ‡S•~\/»“ê#e"T:÷¸cÌÞ\¨t¾ d)¤Ì[ ¨tž»þ¤ÝûBQéÜìÐ5Î÷°9Eÿ£Ÿ@µ*ubC ¨tÞñ¹Y¿^ƒ ÒùëõȸlK *¤nçD™€¶µßœèUÄéãp@¥sûv‹¹‘¦¡•γ»œê©Ömë~çë?ùAÃŽRT:î¨A&þ0%h[ç§+UˆéË4жõý=•VÿæþüƒPÕО áÓáNuGfá06ÉÐ}CöBá’åÌû&`Þw6E_?£¹Ì«ÏÄ;IG‚.9®HöMêÄLXw*Úi´Eë”ÈnÒ+@…úË|íM‚Ûª.Û›4Y…0Cï©rƒ}ºQ´QI‡³…LNâJã§ÞvDS´ñë½ãÑõUKˆ8$RŒ¾jMj“—*ÔïKMUì»C}^…Ñæ›¯Qù.MÜ»Î<êì«ý6÷¦hÏB-²Rm¾vz C+·*´ÿÓ} bԘġkŠÔÉ×eÌ£‘E\òˆTª[:, çSTh~‡ëhKËrGtà…´w‰Eÿûýÿ?ÕÇC¡û‚ŠoM.–jÌdÛá°;q fáÙÜg[æ³ÌÞdÔ8BÑ›õ®$âÀ.Ê÷!!KïQTãŽ59¡º:èÞ¹%õqš½úd…RT¨ó}Òus k_ë-xfì?™ÜÄ“w)ºƒöƒ›í%¨oþQ…ªEãvѸPfyR¸ù3:Zzµ ~}¯¤?Zæò¨õµÉ4>¶™érÌ&‹")º2,2ó˜2záóë@m&gñNAåL¡ýÃ=ÕȦµr€ºnW&*¢žÌ´Ò2(®¸DÑÙ} ‰þ¯ûL¡ù~«køŽ¾©­ý~œ›¢~–ùOuÂVkX´E +•T é¬*34w¾XÞ¿™Cµ>òâ:̨€ÙĹß.fO»yähéR@ã*Œ‰÷ÒQbô¢ƒy‘m hˆÛòýkEò—“‘;8T¨hç®$¥ó=@Å›•ɰŽï˜AÅ@¾u“ OQx}]‡ù.þŒçþ¡hk ðÄô€¯Ê»ÂÜyx(˜u• BýÊ®[P{c Š6N|Ë7„Ìdn•ƒØ±w™ÇîÃÊ>-LC /,3Ð(×¼Y]2 í¿¼Q‘xO¨“Y{òäŠó„Éxrv|E#Èb¢>o0SðGEX*$_Ï hf·_P¿Ô‰ùOµ1èyV™ ~–‘œ{¢"3ɰM¨ðäÑO¼5]Ò¢‚Ç“a×Å€Þn±&ï‡42ÝïíIÓ˜wòò¹úGLãO@¦a:íɆgIL¡þꉲämGÐØŠfÈÝ Â<]ð®Þê"Aúó²LûÀ$HìÌ£÷¶Œ ‡äJ94¤ÿW˜pì9E×Tè'å™<*ÔOôm^NQ­…1¢É…ñÌØ²öäÄ×ïÌÆé=Éäe?˜û¦[§¥Ì¬Wòd̘LÁoåhY²}éB@ûù[ÓB™.sˆö.úOØ`ÒÝÔP¡ù‰ H«©mêЗhÏ ô/žj½¹7f¢e õÓÒ¾À•I¦hÃ5Ma¾ÑHRî½e6å '³ìî3{” '©%"´ß>Obë¾P¡ýi/ÞÁ;õX@ý»4A'…ÌìcŸ`ÎÚ0æUåÙ°±Â“)4¿e¬6ÉèòP¥sͰk–Aÿ⩪w»!~õЖn®¼ŒùS&½vRºAÑéz¼Ü™L¿ˆÙPl¹P—jY°Z#áPQP8m̲áÑ)`OÜ)ú+ð\®ï.AÇ›Y/ê?Jq|ÎÔ&¨ÛƒH¨ôþcš»qhøÊ£C=õ‰Á©£"4á˜%i$CÐþÔɧ‡ªÌÛj"šŸh~ÓZ·@k:Þ…o“0 eD¹žñí:oyµ/E…æWoH„H=‚^Ý<ö+0ÿ‡S•~ŠÆñãÍn*?0²ãêt¾QT:ØWÆø”G¥óêƒ{źï¦èäo;“Â=5* õ×Ý2´.iT:[­Hdh*kÚUÃ͇Ê•Χšv€G—Q´­ý𔫡¯ÇRŠJçc¼ Iu§¨tsÅB—&ÚÖý ×Á>;Pé<(!‹—yt¢mz¾^ôNî, m}ÿ_ONý4òßÌ.G;Yò¨xF+WgÍtúy…ù\‡¢2‹ùX»ÙÌù9qûÑ—Ù¨ò69sú—ùü#-ª Jß§Úò®3ÌÚÛCëÛõƒÇ¹û)*Ôâj rHçgô%º&2;¸‰ 9è1 ¹Ë/r]u2óëŸ÷‚#5‹ëIzޙĬî=•$ çÑt-W2oA_@…ú½·Ø²?fšìûGÎgoF¼úo¡¨Å«1d¤ÂBæÌ™&$â›<‡µµ&Q]bÚ¿´ì 7Q^¢Æ¯¹c?Ô™:·åa„Ʀ‚ývØœp‡)4ÿS‰™þ+ÐQuJD$›)ô~NUEé'^ÚÊ£¢¿p·ÊzSôz÷Tþy@<³ ¯dw„ytxW²;¶ŒG+2,H¨Q˜-U×¢Á®¿xô²è$½Œ¢ë¾^„bY ºô½IUJQ¡þâi½‰wâåshà‰$t˜.‡†N;$ZxÐø øŒ¼ì?zêVƒñž)´ç]%ˆàzPT±Ÿ>Ì1e]jáîúTæ›îŠdäïJ¨ÐüŒûµ°²ÝO@ƒ;§Aå&Y‚þÅS š+ W¶¹RÔ©\®ôgzL.Úè.aº©îâÜ×^cì‘ eš€N¨µ™±ÌÏ”¸—ŠFx9C—™iÌÄ׆Ä_árU ‘1P¡þ"‰)‹8À¡_(’u Þ1ƒüVñ:¿ÖZ®äÇ›m×aîîñ4˜sªóAg|,³<ö„hŽãYõØT/ÒìQ@Q¡~דûr»íD€:ÿ Ï4Ìð$‰Zö<Úßu41hÙ&BÓº,…ݵ'-ªìQÎ×9Thÿóaö sŠæ¹Œ§é¶LgN ]zšCK=dHü–@…æ;h‡‹½Z¿î䕯ü‹§jó^CÕž@ÅŸ… Ïn2[&ê(óLj6шO¨ÛEÊϻΣ±Yc!ÌzE…ú眾 +Fz*?+¬f-fn>×|>}äÑÂëËᬞ=EC}ßtq .ïÎðÝú…3åA4ï–ð¨ÕÍÏ0ÏÍP¡~Çdd¨Û‘ºìnšÛQNŒ&_ïIÔµúxA‘\{0‚i9Q•š9Ù¢‰.Šcš©º‚OŽ1säÛ°}¶‡Ž1ˈ¯ÔÝ@ÃÏ4¦P¿–J{éÇ¡jÎȳnW™ÓF%ƒB¥; 3Ü×€xŸ.scŠ)©3N•ôùL¡ý ƒŸBah& 'G?€‹·÷3£BzÅoû˜¿?#+æ_f ͿԉT~T¤hº¶1Ï åÑ¿xªý®Àà5>UÞR“mÇ1}SÚ“ïé·xô@‚é½÷‰uØñ$ãfêü¶Ίö3[éeQÜÐf]©,¿»ÚYa¼•³\;•©á“Ì+]=¡BýÝ“ÍH²-€ÖµëCÖî`–½ù›ê63-O^…¯Ö0[•¬á˱MyY×ëeE¥s—s:ä]ß8ŠþÇ‚¤¾âó>šÀlc¿Òàþ:C@¥sïFP3PLÑÿ˜ÿ¨föê h[÷7ïÝ êV*ù(ClŸ_§h›çGq?§'óh[ßÿ×ãgKþ›ý&t ^§Ò­—•'Ÿ"Ò™[B,ìŽæPÑby˜ðþ—}ù6D‡ª*-™³žÜOûQÌuûàðv]¦jذÔýÁ¡YÉÅ0±s  BýϽ? òûô~?$|³eŠOt"ýa—þ„ôgæí€n$hÒYêw_ž¾—Ê¡_ß}ؘģ'Çk“‚‹G8T¨ŸK•9’¼ŒCOüþÌzâëFÜæÔ§g7r2ŇÙoà@âôá…­V¶!«:/çÐ>æ¿€Ng¶„´€úœy€ í¾ZŸ˜¾u£èÃ3úäx_gæµ^dê¬þªNúo¥v€ ͷΔ'õrͪi+G¶îè_<Õ“­Á v ¢³÷`í¦fq‘wØÜˆ¢±©¸Î©ÎÌýº‘ðÏûfÙ4€ÅG™&³²8›ëþÌ¡[¹’8æ¼Þ`VÅV¦8Uƒ¼îÏêo? ?8ò4 ¤<;àÁ4œcU×8ßÀ…^9‡N°xôû—}/@çÙâ,˜SoY‚÷É<ª3³Œ2_FQ¡~=§v!]BŸ­NìÎÇ2»&Y“Š{ùêðÈ‚,¹÷’¹ôþ5Øó ^Ž{ £bSh¿Mµ>y¯çHÑÓ5úÄéÎfðOcÒé e¶ZdÎò5L¡ùáï!Æd CýîÁ~ó“Ì¿xªæÞ· êL ¦_K I?)o¶‚ù`E üsË›˜ŠïÏ‚|Õæ[™;ážÏ x#‚q·w2U]"á»j“–ê“r“¾ÌA·TH²bG*Ô_Eë%§—î¨æ¨s\³§ Såèn]izï™Ë ¼ÒôÇÍíE#_K8f–øÒ£œ»ŽSÔ£ê¨õ?Êü€òû/u­ÃI€Ž¥ŠdáŠ4æ‡v}ɤŽjLË^dÞã^Ì“ËÀÉÄTæ2{Pð²c íwè“SŠîÛ¨O‚’ô˜ÊžrD7;ÐÔ³7áf™Sh~ŽC!Ü|vP…¬8ºñó/žêÅíE0ýR, ë@ƱHféîÜìà0Šž ÖŽ‡#˜û.6ÃŽAû™Z¡?á§ÕæÒµ¹¼m1sÝÇ»0#E—Y#»:€Êk[À–ŸS™Bý+Çs§îr€v=4ž{3v 3dÙH.»À”¢óö/ä*¸‰Ì:¯úsEƨ·s¦Èx>ðèþ ÏÀåþ Š^úþ^ß,£BýJrÛ‘ÖÏ]Ôå;x÷8ÈlÚÕ\ϳboR!¦ ƒ™v›†A€…·øó,µ(*´¿Æ\ŸL;Ü•¢õgôˆÕ6%æ„ê]àhhoY/(„q¨àOí›ëÀ41 Ð}¦@sÐæÿpªÒÏñÏ9ÐI²PéÜä—3Ì­Ø@QéÜ´£<ñ~6¢Òy°J+œ¯šÇ£Ò¹·ækÑ?n}•Î…úiip9OL•Îõ¾îã¶Ž¢¨tîµ?–[ݚϣÒyhÜkØQ1жöYú6–T:¿8\†¼îÈ*—œsÕжuIo=²{¬ E¥ó®é]àáõ!<ÚÖùºZ®°¹Ýmëûÿz¬ÓLÈs‰<¬ÓØ@Ñá~@sBTgbx~ ÿÌV&{¹3sLà„§óäf å:í˜gfŸ…âWŠŠÝ†Ôv Ì Ë»0åM1‡¶´Û.¬êo³A²­_q¨ÇZcõ>ÏTr\ ÃÌÃy4üDLŸö‰Y ô“+9´úp|šÚЄ¤`~5‰G‹ ¡x™+E…úÙŽV'{‹P“"%R²ì%Sî’#|DQõÁ³à}“ 3ø^ëï_þº—¢u!Ý!ñŸÌŸ4ˆ¡o¦æ¥Nda^) ÎWØ;voô&·Ü_e ½ß†S=;4.ÐXŠÏÞ ýò㙆äÉÛ›¿ÿ<ù?¯nø wWŒaF¸$ÁÆ19TÙßT7d3‹.¼órÕÛüÞ‹U9Tû»,ìºlhzs"—qü‡ õïÐך ¶sèá ;èzÔ•Iúl«/–㿎ŸÁ¼·”]{ºÚ*LP7fRq‡Ã2QÍØ”ÛóÙBý ·È“Ÿ5„C“6ƒË60çæ„ƒå sŠŽ;’M¹½˜‹«{£Õ>žIÖ›˜Bûù (\»˜¢k:ëÁ‡e̤ýíÉdµƒbT<æ%4Æ-ãP¡ùsſt›d¼r1$ÌÖàпxªov§Ã]ÕDŠx“ýK’˜rÞÏ î¦3 £L+@YisŸ©L°vâPÅâJNæa#zø¦ ©MzË¡y2Äìù0@ßøÈiu™(B›·…Ážçù<*Ô¿|êp¾'B—ŽƒFGôpÃqðÊ¥èÂÛžÏÝÂTQíûGZ|¤ž{?L›™0äˆËûS´éÝkH OŨP¿Ð_ÍPj‘Á¡é¡dÅfGÝ#°þagŠÎÛ,õäïþqwŸýœY½< ¡¶k¹}ãnq¨e ,±Úòƒyúí7X>r BýüŽÞ>å!‡ntå¡xÔG¦ZÄm{³ŒG-`ÅœT¦¶Üðëè˜ ðá„·ÚÏ5ô…‹‘AuŸdQ­ÁÌ!Î3Á¬ÓD@µ~ÿô5~çÅš?õl ŸMQ¨Üc^nfþÅSUt< ¦½R)j}ò4døãœ§It¯žCŸÙEÀ›EÙL­í‘œèÜm±•p½T)új渺9Ðê‡<ÔôÞüGïÞÄÐï8~¨MBg_vD…úO¹ ¿6ð¨g»H¨ÎaÆ·¾€’ä8ŠF¯ýGçF0½÷q§•·ph¿ŒTîZ·vL—Ý&úñÑIpj e ~+å½y@õôwCýæ›±­ÿÖ…G[ûÊñÜ}b´P7B’ªxTÁ¡†“ÑÚo×~ dw]KQí׃À¦f3}ÑÎpZÙÌsÎ?Ô˜BóÛÛ†ÃwwQ4hƒ¦vþã_L~Øk(8|šCÿßÿ õ8ÒP¡þó4ã ¡ôF%ÃAYŠæôø[/`ZÚÊà‘3Pé>çtõâ9”î¦ñ<êäÕMo E©±bT¨ßÕó›a“¯ þV‘Ž&³ ·2‘ùf$BÞ¨“ öFz>µ’½×QTo±ÙÕk SðG‘:VF¡•Y8¸œ?º\­åwØË¡—tõ`r†¼šoàv”ESôçð§Pë1•ù?œªô3ôñE(º½“¢ÒyÄ”þí»]ŒJç¾QÑbÛƒ¢ÒùÇÎ!P2ì‡JçãŠÛ¢ÅK•Î…ú‹\öÃÆÏ](*»l—!k—*ST:3n"4ßMáQé¼vë ÐùbNѶöKU˜×Sº*]Ò|œ¾•C¥ó¬;ÏÌamëþ¦O# U5Œ¢Ò¹èÓ|°6äѶÎo£U(ÚÖ÷ÿõܲÕ!ÿÍç9WaºÚ}5#— ¾à=sÞ÷j¸åΣ*Ï¡FÍ„¹Í¿,Ÿü£•I¡`}ƒý*Ù’.î€%ì€# :LH—#º±zup1è?/` õH:Ÿ¢B)ú-â„d†1)¨‘ŒÉ)<š×­+YaÆ´3Ê€5•J€þˆÛ©« ™¶ùíH•Ï2UðR&ê=Uê÷ýS-ø Ÿ ¨ßÎûð¨ûRfÜP3Ç9òèÀ+ˆvŠgŽž·®öpTN2BÞLf íc=v:ôÁP˜ßɘéh«@†õ6cŽ;!G~ed Íd• ­dE[›x˜¢9)ô~NµÇÅ"p½ÜÐkD ¢Á™/>€Õóh1z3R†¨_}êˆê9WÂû\u} –ýý óÿü¢:ñyºÖ( ä"Eh˜UL˜ü™C#®iÀúê×L¡þŽÞлFÑTË0³ûíÛi‘üõ#xtÅmÒ£ƒs»ÒrرÑЗ¿Ü¡º«ótò#ÍžÙ~|œám˜Bý†—Ü…Äa€ÞÜvO®aÆZÃÉ æOï jÝ_2 »P<áh\'Xö¼Shÿ3ØÙúä’=ÜïÿǤ]!ÆÌƒyn]-d9e Íß-s j«ì(šZ+ >ðè_îêõc2¸!F…æÿ(~¹?)‡˜6Àêú€þÅS=]ž >ªC- ÜqF1Ûu&s=áÐÙÎDñª  #7†Â#n)3-i9ÈŒ™ÈŒ|«V®¢è¬°Ð°&„ùµH†ŒìÌ´~ٙܟ÷G?+¿ òtE½7TAÔÕñÌÔ*Ò°|‡¦ty·2ýdŒ!½‹˜Ùè>¶ŸÎ|»^/îEÑ÷üt˜8Å„)ÔïÝópé Ö½òaåãÙLSOKxÑ4ˆ¢™ê82Ãóê KÄ<¦Šýï_Ôµ3™Bû£ízÃ;C@›ãõ¡¢s/¦ík¸¥ŸÈ£^ô úŦÐüχë &ØÐI)waù¸Ì¿xªnö@¿V@ åw¶Ç$æ…ŠNdàXUæÅ5ª$°F“©te$=±`ZÖ.„ƒ_94xz,r\BÑËOC­Òh¦Ýu"RÝÈ£-›¿AëÖM"T¨œò=âáBÑ¡^÷!gÑ ¦›OÌU9Ì¡³VQxèYÄpp"„ˆ=DèÓ˜ ˜îûNŒîpÞ sä(Ú2á>d(…ò¨P¿OÇÁÐk 9k²aU3óº–¸69PtŽÖ|x¡7ˆùuëP]aÅnô_–0…ú'?8$ðèš>¥>~3³OÓ]XsP´öîCÐ|4œ¹¥ì\jMçÑÚ‡Àa:Ïœø­6Ä Ô9^ ê²L¡~áŸ@ÞÊ-<:PTÎÇ0ßì@¢4Ÿ3í4É„‡5Ìœ‹áú‚IbôÂxf±xThÿüê×P¹ÓV„fnõ˜fµÃ ¨K,âÐÄ–0xxÎ)4_¡&®çlåѺkáú„¦Ðûm8U+íP¨œ•Ë¡ÓMÂ@‰¤2+¸°Î(Q‹„Òž#˜&ípYf‚þI(ßPÊ¡Cl(Ì»;ÐrP1‘™#ÿnwüÈ£)戞ѦPÿC^еG­§×@¡L3Ãæ Ô4§h¬ò3°»D˜ŸTÁÄ×U<ºÄ¥vüxÉTÌH†–Á+84àÉBðf*F…úÕý|Q›¿‹Q¹¬û•bÍì× =PÊ£ïÝHßifÑSØ:ò:sò^sP8ÿ†)ø£Hù=X;î¡wº|„¬‹ÌÃÕ›Ápw–u³Mg<*4ßmÉjù©GÑÌÀ`(4˜ùOUfX8x­ßÈ¡s–l€åóg2G\ Ú~ Ëg„‚Äusäá4ÐZË¡·:l†’ÞÚÌ!raëâ!€:OɆò¡Lÿñò$ý@>ò T–„ˆQ¡þSû=›¾ƒ[è3Zò£æ{·ãÐãýÒ!´¿EÓlßB;¦Ðü…ŸCAËk8EËÜ¢ Byó/žêíz׊COÖo„Õkä˜ýw­…˜&G@?(†À„OVÌå…þÚó…#º$s,d½9$FK¤Aß5/8fj"˜ìŠ`zÞ¾f룙säÀå=€ õ÷zÙYÍ®í£ƒ?„YCä˜F·ŽÁ½CLÏAwÁmðR¦Ð~×Ú¯p"· ‡ŽÍý׎õþ£Å 8˜áIÑ(‡XþlSh~Ù¸íh7¢¯ÏïƒwEÝ™ñTYl‚c-Y"tÝú(y¯Óþù¨I0ô“òZ¨û¬Î¬ùi>£íyôÖaC¨ä#˜×õcÀ­ÛzÏ8ªkôxô~xLµqôû• (ïáÈê/kóæ×™ó¨Bw9’£Ï”Õx Û¶îb*¾|µ÷™Q© $áò}¦üvyb‘p–9-ï(|º~i°¤ã0…úi»ÜíS§8T¥â: H¸Ï̪~¹æˆQµ.ÏÀÿ^¶#:îû7Ø• Ê£ûk•ˆøÆ,1*´ßnÅOˆŸ0Ct‘!Êû‡2Wœ¤p¾Ç8ŠŽÿQ %N™Bó~> ûÊx”w f>N`þÅS]_ åºpíj½êR–‰Ñ»£×CAm+‡~…AïìR¦XO|çàÑ)Ãtáä¯3L'ýe࢖ÃTÛ=2.þd¦CìÌ_ÚçP$lßX+B…ú“Jy–ß™G_¸u ©/Ú1×@kŸÍçP®° –ýÓžC;<’!•·ð¨ÉÆÏPU`ÁT•´#êñòLËz5B—Y‰Q¡~ξ¥`ö‹C”Jàt¼: ý*ïCøúk"Tõ}Ä¡ëßw"sõ’ÑGcTˆí°“"ThZ½ Y¼}‡&“%ò&ÞÌu~×à9מ¢®=*àÈ <*ø`Þ=è÷¬IŒ] “_y‰ÐÿáT¥ŸŸñ±°õá]1*°Ùgõ“9T:÷ŸÕÎ]äQé<ët8yC—¢ÒyËpQ>Ä£ÿ1_ ¿8¤#y²¥YŒJçS?W@îX•Îåt_‚·»§•ÎGÔ©’¥[ŒEh[ûÍ9{ †«*Ûí/éÖ9T:ÏŽ–!zó{qh[÷ßZ"G&NZÊ¡Òù{ÕÐÁÁIŒ¶u~×ß`VÑXmëûÿz¦è@þ›š‰ë¡I«+E뇭ƒç~šLMëxÙÕбñpR­ó«ë5H;îÊ£>3Âî§–ÌqÉ0­¿  9vAM®ÓÀ§¢m&ñèÃ+òäû¦P=ÿÕ½M›¢•®«ááÑ?Þ¾}ò7ÝæÐŽ.é0¾ã¦îåeÐÉlOfŠû1O_KË3Ýo dÐ æíKe0Áò©õ·Ï « *ÔÜðÑíFÑí½Öé¡Á¼ü.ÖÍ1ÔbÉ6˜ÞÑœùÿ±^×QUìíð±»Û£‚…­Xì™ËnEìVÄâ¨ÇÄö(*`¡"]RÒÒ { EEå(vçyùã½ïçYû]ë7¯ëqþùüq­¹¿—xïÙ{_wG˜ÎE™ü#â"rsΰôì¡UvZ Ï?†——ʤâŶ£ˆsùW&_ÆzɤaÞg–$‘Qiÿ Á;LEŽ8€ ‘Ž2Ùp}&^D²Jç¿xm ­1Ûòɹܙ]ö% 6F"9k”'Vé>eç/=ŒcS›%“Ï|O `î™ü«ºbÜ~8×ë˜L®9¼ýœ;°[%téµÒS+[±ßWÕ]Ó_&_½¬=úÀò^lå~;ø{MÈM•Ö0ýðD"çÅùÂao'cM¼ÐeãV©¿}ÿÃØ·½i2ù³ÏQLÜÕjqÎí‚ôv>‡º“ú±~ jÔë»Q&úçcíÓ±¬~í혖pƒØßIÎ5“I¥~‡š›C¯IÓdòëú¿Ö²»Äઞ­ȯ«JÑ3^fW?­@`Yk™tÝñ&sj°ŠŸç¶Ø6ËVE~(³ÅÉo#’Èáßíp­y3»mNãëáî¬ÒümЫD&†ùctºûWõiËÈV·O&í¿À„]íXíöÛWdñ,XúB$W¨1úÚ¥–2ù1é3Â,K%rs7K¼9*“k¦V1LHeONòF' ·LõÄÂ’b‘Têßø˜%n¬¯“LæN8½^Zl»X[|Ø© rW+{ìÏj˪FßÅÑu:2¹½óstÛŒ}¯»ÝÓ¿²kúZ¢Có…”z ?ù[£uœ.«4ÿùDìûb&“3 à‡¾ìo\Õ0ÕATæi'“Mn‚É¥Ö¬Þ; 9Ÿ#’Ö=qy«Û®fFΜ"‘Ý Ê`žwGE -G=—ɪK&лP#™\ÑÍ=Ô–xC{,‘I¥þLªÿÄ5>È䉲3øØäv‘‰œ© rîBG8û?É u>ÃÌ¢®L¾êUct~em¶ÀÁÏGزÃs±øïÙ¬R¿-ª½p oŸLŽ±Ø‹×ýþãbý'nIœˆÅ”Z¶lÝ­ÑïÛr š¾+‘IÍ\©¿_´-Lîʤf~;Ú{ÏEФfÞA®=:ýC=™ÔÌM¾Dž8T&µßÌœ}8TýzGjæ5sCñö`¨HjæóuJ`¶¸H õ|›þ¶Ø\³T"5ó½ÃíqÊû„HþêüUãjŒþ\q@"õþÿºÞ¾ÕýY9<>®ÝdvN&¬kÉ~­¹öÖ'DrÈìð_dÇû¼åe2™=x3Ü»Übk[¬GØö™ÍNXlµ`ÿ3évÞùu×r¥ümöm\óH$3·b—ËUÖ«á7| žÌª÷½†¾y3VéüÑgÞàà¶«ùAõWå³ë“ÊáUùN& {ßÂõ=Vi~νç¸ö E&µc«`eyƒUºÿVÕýÐeÌW[&—%fCwßK‰,x½7‡¹‰¤eÜR˜ìôbmÄ€y¹2Ù§r t-cØ—_,±òØRÖu»=LëóÜ Æ äõž0}ýM"•úß„+Fo‘È“9îP‘Ù¹Iˆ¹>B& ÎgA7JŸÍ˜ø3GY³¸ûnôòaoNƬ޵“Iýîj.éÌ*õ{óó †µy9»O¾7cË/çcîµH¦÷̆*ÍŸ}ß³ µ‡¾Èèq—¢µH*ªü%-²%ÒûÀ ”•¦³ú§‹ø0J&ŸæáÜSViþ“ÿ`þü29aË#´Ñ¹ËþÆU]ñ=ú—oHdÚ„«0Ü{‰5_» Ëúˆd~¤)Þ—x±m*öÁÝÒM&÷±BÓÙGÙ5}ñãŶIR,ÖŽœÎήÅèœvFzn_o”L*õ¿ÛÊ•]kÉd‡öÞ¸Ú 7ûaz1†¦ lïÚwqªÔmÿïlãÙ­Å/±Õ'‡5©ŠF¬^ûd2%-.ç_ˤR?¯W!iòFÏ«XáP‡ë—†a_mEÒt±“C欹nLknbÇùº#3=†U:ÿ^ó*L[’,‘OÎ?èº lrDZ¿ógìóðãßÓ©4ÿkáô¿!“ó„k»íÆþÆUµ?SŒ~aYzý:|çx°ÓÆoÃn"Ù4qu<Ç>9O½•2ÙÄÛ§'±CW]ÆåÍ»XË¿ŠññŠ [8<ÃÏuI&ó2¼q̵«ÔßçºZ¬)“/'ù"¿l;ko\ÛßMY£fÏ‘1É‚-7y÷Á×Ù¤«ÿ`Î_·X]‹xL«²f£[çÀY;H"•ú•](@´þO‘´ð¸¹âÛÄ$sW²Çª?²ÿÎÏÎóµÃÝÈ÷l[Ñ‡åæ •Î_§û6ÜŽÈú~OÑøz kaT„ZÝæ‰ä””[uÉ›Uš?R§–¯&ËäÍ ¹Ø½î¸DþÆU=©} aV68ã.ÖŒµdO,8‚}YGD²Ù²cHÝjÌîÙ†3öcdrÈL}ö²¦%w-±ßê?ÀÃá÷ØÒšð/S³F9zŒÌ*õ·Ûè‡ÇM­eÒp¼?¦­÷`ÇÜ|…±ÃØ¥no¡ëêÇ®ª€ãŒVív™;ÓØaéåз#gÇ|Díe¬R?½ùhaY.’¥Ÿs0uu!Û­‘?Þ-êÇæÖòÀ¦õÙéÎ8²¹ ÈÔWžýX›U:_×'(|ï)‘*P>Ò‘mö{ê]IƒNÏøªˆUš¯»<…òi,^p¦®ÓEò7®ê(³û¸ûmDÎ>ñSÖnboXcÀÊ¡"y;ÝWæüȱ-óÐ4s³Lzäß@õ6Tû!¾ïøÌ¾µ½vµ’É#CocÌ…iih÷Ù'k°Jýk\õ‡ÁÌ™ l€lÇKìˆeïp3!š ñk^ÉìÑ¥¨ûÑŸÝà[Œ÷Òavñ­Ñ»¾ÎÈÔÉ?`l03‰Tê·|~n¤ˆä¬>8Ò6œuýé€3ÍÙaýÎàœYcvÄ» x`–ÎFìóÆÚa3YÅߪ á®×)‰<½ïªú™³eů o’+’Æ®¯ð:*šUšßO«f."ÙgÒmئg³ÿêj^íŽ?†®Ï‰ÔÌÿ¸b묩™7–î£û¨`™ÔÌï¸ÑÅ5’IÍüÈ­Ñ éQ©™+õ7n€m‹eR3?·îl¯¤É¤fþ¤aÞ·é#“šùû½ÏÑÕÒFMþj¿úûR‘tÊC$5sQ²Âç‘MER3æés ù«çw­¼cõz‰ÔÌozÿ]‘üÕùFòÿ@$õþÿºN¿ùˆÿËÚ¡àS&’Mó2ÐÅå:5ù9æ/~/‘w=A—ßY'=Ôß(’ö~žJœÙÙ+o ù£yºðv53gþ¯›ÕdYÆ3™ã+‘JýµÂãP˜Ò ™Pƒ)÷þc½È4Œñ}%‘ß“ÒqÌQd—,öEr‹‡¬qL(B›w’É‘û.ãtû*vÞÏúÖO&•úMZ•Š¢–HX­ÆÀºìÐû)ØžQ(‘#¤ÃcÜ&¶VÁKTíû&uTexŒU:ßÙÒg}j&“[¹ánj ¶ýñ‹è6aºLæ$à@F¬Òü»O a>√ü~å2œ«RñÿïÿÿªîNÎÀâQWDòµe.Ë`Ã<@ïÒ÷Yêsº;ËØ Úîè³TÉÊ“n¨Ûï>SúKt– ¤Mëw0½¥"ój_‡QUy¤ß%”¾H¥þ#ßGa¯Aƒdòd÷HhݨÇÚ,ÏÄšY±*Òc{.–ȃ®±PU̓ɶVj¬ÜyŠÍ\…áÿ²©Bqwa «Ô¯Ó¾8L]ÜB$×%FbsÄìÌkYðqNMvŒÉGóv'Udñ¬÷Ø: /‰\´÷Øê/—H¥ó-¦¸¡uºV2¹9Ösÿ•ÉN¥Y¸«º#‘3×Ý€¡ôœUš?»–Œ‹Ÿêˆd±s<Ê­7°¿qU÷wÍÀÅHµHn;ŽøÀ(vuƒ¸]+‘uœ ‘zëkuUþŠäŸ•ÞXw¶HS˰µÌP“Þ °û³DŸ–ˆâD‘„Y t_»5)F)©ÔÏW?o‹dö@\ˆ™ÄÎúZ„kÃF d‹ý7ñ9ò8;¤þ-d»‰Ù¤A6zûW¨H¥óÓô\ѯê»L6 rA©ÑWvÓˆ‡¸r· »æÜ?x½z«4ÿ|ý8øÜ ÉkÝàóÇ5ö7®ê¡ü4Ä[‰¤Ù%Ø.õbkÉC}ßj²ùÇLtõ™«"C¦TÿF¼^ä§¼L8\‹µºõ¶=’ÈuÁ•XÚ¶ŽLNuÌGËã?D²øÁ5¸w¹É*õï¼ ÷¢Êä¢ÖñlýGöÒ»p¼½@$墇xã¶Ž]¼87½É§Û%¼ ¯‘LŽ|p^]]ÔäÊÎ//,“H¥~õzø!ì ‘<˜à•{ذzeÈhç/ ¶>„}x [ýŠ:ºˆä†˜8tžäÆ*~ArÁÉ£Ÿd2*Í­­Þ³éöÏñ3ç {Ü´ýY¥ùÓµe|ÿ^$-íÒÖ§&È߸ªc/¥¢Ç'‘ü¹;z}ΰfÒ‘½E ´IE“šeìZÓH\Ë#’ †ÄàIÇbvðº{¸‚/9úl ʳ:½¯"þê‘üæš„a}X¥þƒaû¶J&×M BóíÙ]9•ˆÜa*’¶/0Ün9[wT—ÖO&Ó—DcWeV[÷=277“ÉÀÜÙg1«ÔïEºÎþ}R$·¹ãfwö…åSô{#W;Vácn{è["^ˆwE²2: úýµ@*ßt¥3V,y#“ê:ãÏš/Ùûƒ ¡mV|o±JóçÖÎE¾I µ®àPêk‘ü«ú f2´ÇZŠä˜Õ¾uÜËîþ$ãÜzvÜU O?,b=÷Æ ]ÿ‹ìÎû‘¨]ç0[7:§úÍÕJMöî—†¨±öys£'º·ÉáOO uâV©ÿ¬@¼¯wS&‹‘RšËÎ9ô]›LÉ]gß¡i¸;À) ÏûÖM&÷£âùW™´lqµš/d;é\®ã‰Tê7+lۊ䇚.0«ÏiðÉ¥grx¿w(번ýÚ?©W~Šdêâ"œìv‡Uü-Ÿè„9æÏdòÑn'L¼ñ„5´)@¬™]õoÖ¥ú¬Òüþ®…èý¤H$›X`ˆt‘ýVUóºTýôú,˜Š¤fnä!a§ÚJ$5ó­&!È}2P$5s‹*M7Iͼa££ø—$’š¹Rÿ¾ñ˜Þ(Y&5sç+ï0º ¾Hjæ UÐkù@&5óÓúÁ\ µŸ?g,:Y$5ó.¦ã\K 5ó¯ö˜SuA$õüâqN8Q÷‘Ljæ3d`øfù«ó;ÌɆU†…Hþêýÿuµ¯ÿÿ—KN\BvP¹LŽ‘Š;‰lžƒ ßzá¹MH‡I;ö…m<¼„{é8!¶­ú‰d÷7l­û§Š|ÔÁ-÷‘Èß0¬Ó =븠O+‘Têoø$;<‡ˆäè’DÍΚ wEê}ÕÃZÓŽ'’ŽÅáµA™DNº’Œ3õÙ²‰ÞX²ë&‹uܰJ&•ú•dÛÁ¸n¡Hnáˆ#»¯³¡—1:®@&OÍMÇ¿'ï°=ZmÁ®W×rEÎdÚ©DRéü¢ fxhpZ —¹î@ül7vhÑmܾ.’“ãîãÉK;ViþåÔs¸¢J–ÉvmP·óyVéþ_XÕÉøª[)“ãæÉЪxÆj?ÎFU«óy}Q:E\dKJdKÏ‹X¿ŽL˜pã2‰ä´dÔo“É*õ_ßø%šˆäë4˜Xîa½ô­¡5cÄH²íK¤NZ«"ËB’§“DN}q¹!¬ÅKÔ2Ý/“QÌ0¯«ÔïY¹3¾=+ÉèH7”¿ÍZ®KE¨ñc™¬j #$ÿ9Ûæý1ìù¹\$ üð!D`•Îßôd;R=ƒrÁ¬í¨lŸÀ𾽕VŠd`Ç;¨LmË*ÍÏØc ƒ÷ÛdòšìŒ‡í³¿qUórÕh÷¦J&w OBöä—lÕ%(­Ê–Ƚ%7aûÇ;¶CûpX­HI#Ÿhl°¾Éú] B¸­LÆ_òÅšÒh¶ö]ìÛrS$v Y¥þ¦g j”•HÏ„«ÎYVæ°¾ÔE M»îÅÐ5vlñè›(þH"»Ý¹ Ÿ©d²¾ùN¸»KäüoV0¯8ŠTê·ÈÁ'·ÝÉ»¼Ñaë ÖeC"t޽”ÉŽ ±xù× öÞ/öÈðSð¾F‰T:Ùõmø“)!{¶!qíuöäý" Z˜Åš–]ÆÏP/Viþñ^>¨ZK&}Ö„âÀ¸ ‰ü«:Ê-_+^Êd›q¨‘õsÍï [­Öli÷à¶u;$%צ=Éf›S1íK%[ãøyT Ì–ÉVŽxÒá +d‡¢Þ˜Á"9ÿ®JÃß©HÅ§ÆÆl4{ì(’ksQÏô8‰|±/]|³%R©ßý±¾Ø:¥H$¯<ðGãæ¹¬Ô= ­*eòì­0,ûvŸ=aá‡Óz³‹tì`óÇ>Vé|“QÛÚµB ϴ݆¸ÛoÙ:3Q˜•Å69†ÄÔŽ"©4{q¢’ÿ•HÝÌ4X_Ô“É߸ª­Œcáùâ…LŽÈŽF­%Uìðvå>u.»î~9\ÊÍØá.™|ô¾HZªò°Á$Ý|ø 6¸¤Éd©t;G]dGŽ´Fó«‡Õd—Ú{𾩡@*~+T¿‰ÿpõɱ^W±i›?ã¶7öŒe_ÞÇoLعOïºN2ùsù]tëÌî>ŽÊ=ûر9Ah™Â*õ ‹ÊT‘Ìš)?cÙ]õCPÕåšLšÜ‚ÍêˬîKK˜÷²`?®Úƒ¸¢ù¬Òùº­¶¡s†–H6ï· Ÿ›Ög· `Ä_lËf~Xúð«øT½™ƒW 6Êd»®W`åÊþÆUÝ?* YMŸÉdV|ªš=eu=ËÑ2õ${Ìä>Ù…-±`›ô€yb;`±?Æ JgÏ´8‹eõ ²êóq\i›Ívî‰Ãr–LöþᇖÉ6¬RÏ ÷a”¤-’ßêÜǺÓ5Ø;ÿF#°s¤@š‡âvƒXvkNBoÄ‹äêÚjŒ¬ëÍæ®ŒEÔÙ"‰<>&­òdR©ß|uœQC ƒæd´ÖY9<•sãEòã̬®fïÔñ©þ;†%‘&ž±§2H"•οf›†ÞË'‹d]ÓtŒÕÉnÐ>‚q°«[žDDðlViþý¼`<Ëî&’‡,ƒÑ®E¨@*Ýÿ «:iH" ÖÉÕo¢ý“Z±S «¼íÈ:Ô Åæ¬Md(–ùijw¶Fce´7[6Ë ‚z³oσû¢-ìAÐù£µL^Þ›³n©Ôßxê=øÞ¹%WG”aó†(¶Ûç|´¿Æ6­òAu]‘ìØVÆ KösŠ뛳Y÷^2ö”É™‡#pª€Uê7²2 ŸukäÛ¤~êÀN ,ÄÒ¦î"éݧ·N±“g…âãÒq2i¼ÃOÚ°JçÏœ”õÙ9"Ù¨ÿe½…lûvžèô¢ŸH~Ês‡ÉÐ%,ê$á:±&ú±8¸ë@Ú¿öÂÈ»2Y³Ð šc•ú¯“0oáD\¶/ºC±N)ÅHz·C$w˜c”½ëðÍo½|e2gàiä>w`•Î÷êXýôž¸L$MZd¡Ù cöô[_̹[‡5<€Æmæ¤Ò|³Ì(*‘ß#¬1¯¬JMþÆUÍÒQcÎë1"iÓXÂ×g†¬º~’Zd±ú§$4œ˜Ë^_ƒ3±ªVá¨#¾¸C E2{›†ôdoMȓЙ¼÷8¦f³Jý7Œ,…G^4n|Ÿ½®¨È£ÃÝ!MµɼUç1í_wöKiòxHlíÙ‚pWxÓ‘É1Í‘)‘Jý”Çáå‰ ™ƒc¶WØ#mаêà‘|õ¤•cš±zû-‘½u¾L¦ô¶Bzôu‰T:?°I6ê®Y#’r³Ä—¬g/·óCå²gj2¼—ªÖ$H¤Òüä–ä±ME–†Å_^ùWµWg­{.ÉÞs“qÓ”mßéâæ°Yæ—Ñta{; -· äˆËžˆ™²€=^q3¢-ErDÛp¸UMb‡ ‚m'™”=ƒq/¸ «Ôÿsh ¶îت"õ*B×õw ÈIC¼0TŒÉfúB¥Jg›{áë‰,ìåŠò!÷Ùa£¶ñ®DW^ÂÃ…2©ÔïÝÃ(\òú.?\"a˜ÚV$u_åá½m¥@ÍʆA ÌŽ­u…ksÕäŠî0o·:‰T|ÑÎÅëDòH÷<˜™þÅî_g‡ò[‰|8à$¦Ï½Ë*ÍïÓì0‚òÛ‰dÌ} <ݳ„ý«zûb ÖVíÉẗ 5á ›mžƒœ£jö|e>Ê ÃÙ²¯NÆŸÈRçsÈò¸ËöÛ†÷¾±õ¿ÁÞhlšˆâvéÑ(½§–²JýŽ@kDð(RÊ˃V»¤Dr\PXˆdÔ¡p@,aO.q@ç#ØÉ¾¶¾kkx*5Å‹2©-G¡³U!«ÔOûFÖl"’ßÜ"p>~&ë38ɉžù2=½mØwásh¦DÎÐGÊ62©øÔœÒ[Dòáø+¸0r»ìÓaħî’HûQ AE*Íïûì¶·³IÇ<{\_kÍþ«ªy½IHƒoÒQ‘ÔÌ'[_Ň¹^"©™8vV³{ˆ¤fžæàƒã¢¥ŠÔÌw,Æ©í›dR3WêŸÐ$æQ ‰¤fî¾)w+ Eòÿ3`Ž-ò»Zˆ¬ÆugäüéY&µßªt/3Iͼñ(5ÒÂÏ ¤f> "…¹æ2ù«çXT€ìÛER3wÿrùOòW篹|Úk-DòWïÿ¯«•TŽÿËiFYp¾é-’Zï³q°Ž;;z`NFë äúŒ8lºšµ_é×^12¹fÿynõa£Š`l)ÿ|ŠÄ«£ì×UAhY©"]"}ñ᪷D*õ–G£$‰ôÞ‹tgÖÞÅWx²guŽÂlHšL¯‘ Ÿˆ•y³k*¤5ºì›¶‰oñL ƒ?…¢°ëNV©ß‰!¡0èg,‘ƒ"Âp}¶«[*aÈö‘|póÎ4 cµÓŽ#hè6ÖÂη­X¥óÿ9ræ“rÄt_¦g;F‡Â$ÚW"³Rc¡õ`?«4ÿuG &1"¹½ì2Zµô`•îÿ…U=¾#öá"Y^ýÅéUq†¶4½å dÅÏ8œ½X[$»d9¡ÿ¢c2yä‡=~t4d­}ƒa’h/&á¾hû€µ,³G·¤‰ lw czÜK"•úOØ„m•Èè¤xXŒÁ:YŦÁÏUäŠKôöñÈîj$Lï¢&‡ŽLDN“š¹"Éî)b'wô@í†ËER©ŸkT8|$Käéh±ê9;<(Ô"iÝ&ãγã?xaÀ[GöþÏPüüp„U:ÿ†~˜³¯‡@þLöCHIKvF©Œî‚$rÂÂt4n×T&•æ¬Îò{EÒ87ZýÙ߸ª[¾æãhÌ1‘üúý Z 2gÏ]L€_ïîìC5ôuÖØØ»þh"“ŸBáôçE‰üÇÏ6}û‹ä½=>ˆ?¹½©w ^5:°ž8 DÇ‚Uê¿`z"6 ¬#‘Û©Ñ{u¼š|qô>û}H»3gP°ë‘ügp"^¾ðÈ^^j,_ý‰àê‚‘ì¨N@Ø”DV©ŸÜ5ûƒËä¿GÃ1ÆM‡5|^€ùÛmDr§V1l™³Žãc0mØ6Ü=Vo‚Ré|áO´Nù¡"§ÔÀÖÕ%l‚a‚s–ʤoƒT¬^nÏ*ÍßÜ2Û•¤Ð-æ7²¿qU“Š p§h«H.Ü}nÙÂÌd¤Ý7b?=MƒvÕ öj_8Ž‘Hý˾èZ^=55¯Û¸ˆ¤½I8^ë…³™û±bî~6{H$Œ'6a•úoh+#âþf5™"¦Àh`;ÖÌØYùCE2w‹+žøŽbǨRQ1© »p[&zžÌÖš®N¬ó¥˜÷À*õ[¶4 ÓRFÉä׸.2dýô¯aöšå"y}ûu8žÁZt„½EwÜÒøj²÷3;èíŸ"“Vý`Ôå¹D*Í÷0Š„Ür½H>ÍŽ‡ÕUköXUÍkÙƒ«Ð m.’šùõÕ·Pl­'’šyçEá(ßø§Ljæ—£sU{‘ÔÌS2ƒ1|z¼HjæJýçzä¢kýt5©™ýŒï R3ïm[=7¡“©™÷é™ #Çs"ù«ýR&{À¬¿…Ljæ&ÙWP™¦"5óü÷)HÛí%’¿z¾“V0 æžR“šyÜgW¸O_!‘¿:È‹Tœùê,’¿zÿ]å;ø¿4UùÁÚû˜D6*¹€]›Ï²ÆAÿVŠ@Öcñäæ9¶Ç`;”ØÈdSŸs°Ÿ^)‘ÀoÞ™”ýPýîÏNq•ˉdÓ§^hùi «ÔÿÃÍ"üSC–ÈÕ‰¶[Ÿ³.EbB×Yil†ËlÙŠ`w4œÖš=Ý×ã=Ø¢ðxœìj¯"OÆÊ°4”R©ßug,z{H k~p„«m8»ÑØ!òa‰Lšzº•ªÉ¶OBa€Ù"iÙ5=·X±Jç/í]‚—gˆä©%pqùW «¦g`ÆG™¼Û7÷DViþ—æ‘xâf/“Óúcë;Véþ_XU³ù>hd/‘ͳ¼`:À‰}P³ŒÙ²vŒË‡±w&Ú¡é?S%Òå”´ÊÆŽ"Õ a.HäÑŽ¡ˆ)5b£ÔaèœÒX$Í·„Aº©#Jý7—áøî&2ifT£ù=Xƒó!p˜Y(ú#Bp¢úuœí„eŸj³ccœq¬t4«½% =e ¾1¡“H¥~µ&8žÒ­“#Ž.j"’×zœÀœÄÕ*Ò°þ T®Y#‘s³q0çˆHiyKSôY¥ó––`R*”–—àaa)Ûm~š0¾£¦Jd‡¥ ÐîàÎ.ÍñADM*R« †]º&Jý›âÓcA&Oj_EEò\¶äl(N4×ÉpÿX?^ÈÖŸêŽV}÷°éƒ|ðe¼[gë ØètÉÑkrPsþ1V©ß+GÜóè#’¢…ü¯Od[~:‡ YÙàÙ)h i,’Eïoáõ•åycæuÜ(8$‘Jço3ª^ÑàËùzh º§D±ª=ž8POO&¿x!jSšD*Í?§s/Š[Êäß;OCËo—DþÆU}ûØ ›lOKdÁxW”ýmÁ–=ô‡VÞl|Ä-CüØŽÿbÏLG9N Aý‡Ô¤UUL7kɤyn"fÜX[Óóص_$SÞ†Ã0ÆŸUêßdËìÚó§L–o˃nÄß삱(\f&’{ª±óÞ6þf^™ø°i£pycÛéJn¬µ‘IÓýA˜c=‡Uêw¯µ æX%’C†¸aÝý}쉼3˜wek¾×V_f±o¾çàí£š2yû³Œ×¾I¤â£¨~õÓ³“—@®+(FDái֪ʛtÆKä»!( Õf•æüjƒ‘…sU¤Ž:45È߸ªZAΘön«Dnìàwݹìá›>¨qó¶@ÎhæƒïÆE²cl<çl”ÈëúÁȹšÅ:ÅÇÀ´™LÖ]‚9¹§X]ÿ tÝyZ$/¾‚Nµ³JýûôÌAã2éjŸ‰lXcõ%l ³É-{³áýìoÖûKªÂ¼Ù’3iˆ¨:Çê ĺ7ùYU;fcX¥~wŠÎcN›"¹í±ìÛ±‹²·Ý”]eî†CF{Ù„éñË;"‘ÆõQXm@*~”­Š‘]°[ o,ƳÛóؘQh7õˆDÞÛ‹ O¯Y¥ùžzbÕY? ÛçCÞ'Ù߸ª¥î(H(‘Ïôíq²².[9ÙËoõÉõ6Ð/g[£½šViÉä&Á o7`Fú å¾3¬{wg¼ênΦæf¢]w;©›“ˆÔÔ{¤Rÿ±Yé|ç(“KrRñq»;kŸ[×3«E²e·k(­T±û—fc‚™‹«yh³IÅú†%¡w¿H‰|¿LŽ•I¥~_—øbÙ^7‘ÜÖ9FáÞlì/48ÌšNóÃþ¹ÿñZÒ%Ä\j+óQž§k@*o_„¯eC2jiô_µ`ËFC»÷H™ +¾ˆ[ö²Jóu>ÃðìÙ:î" Ú¨ÉÿaU5¯fOmq÷c¤šÔÌw<õÃ÷6[ER3·+rĆÕï%R3¯[ߺ5&ʤfžð îC’R3Wêß:@Æ¿æ^2©™Ï©[ŠWOˆ¤f>!47i‰¤f^+<ºo­eòWûmk‚p??‘ÔÌs+‚P¾q¿HjæÃZ#hª$‘¿z~Þ½B”N­P‘šùÓ¥Þð)Ø+“¿:?úd ló÷Jä¯Þÿ_׺³7ñÕ1¯›´•Éš;d¼ø·5Û¡È k ’öÂÐð±83oßÉǦ ýþ?†-pAƒúMØàæÕ¿ÁŠ"%2ÏCÂÚa2¹(5ËæÍc•úÇ>¼„®ç¾ªÈv+SQÄÂ/ ìH¤^x ~gŸÝź[íÔded(öeõ—È*ó"<©8Æ Ïòp&r¶L*õ“ì°Gï_|Èccš‹äÜ HŒ>Âö ¯+ælpí0tu+S“½SbR¾C"•Οy'çãdòÖÇ<ü}f{üEj×ȶS× &«4–Â7·ÉOÙñhÙ3E •îÿ…U­s_þA-dòæê$XÿÑ„mì‰È9*‘Lî ëÆ†lÝ0W$ì7‘ÉÁÛìW>ý˜yýÒ Õ¤ŽÃiðµÈóMü1wgªDþÓ' [¿S“JýM¤d”/_¬"'Ö“±5¦>{Í(V6Y7;ÍÎÕ‘I“91YÇ^"/4 ÀÝÇûWõë×½—62Ùäx jÔ=Ê*õs=Eòä ÝïÁN#¡/ìb‡%ÄâËÄ5l³ž±X+Õ‘ÉùSC1\œÄ*¿ n661–ɵ sÑoàzvZpRõÓ¼£Š¼˜Sýè˜Æ*Í_XýwízÅH _™E Ÿ‘ûWutD =PO&ƒ3ã1óŽ;eI"æmX"’: ’q¯ÛJvHîL3¾)‘Á«NcKíïj²cô9Ô·è(’?Ö¹ bÎb¶ÅÓ8rt–ȉѰò"“Jý3f¨QÞ(À|ì’ã/9ìþÀHŒÐC&GEE°O­½ñU•)‘ýüܰçiÛÄÌÝÖ´—É6¼ðuß ‰Tê§ÛÜ.I}DòÙ ?ñã?& U“™ì·Åj”é aoßñD§]F2¹â†-Æ=oÎ*Z+…/ÿ’Éûo³4ÙŒM¿‰‰Vi¹;Cú¾a•æg5¹gžy§]¤¤óìo\Õ.ã°=þ­DêÇÆb¾ø}ú. Ç–-Éþ7³°ì²!k?ç,Ìtz äãö8SžÇvtBá‘|еú‹vù.öÍ hzÞX&‹óÏaµO3V©ÿÆoñ0žÙ\E:ü£äUlL×@¤©gÈäÆ&>ÕØõŸæˆ‡!†ab óø7‰ädÿ`˜>®¡&ëm„Ãñ ©ÔϾú܇.º"ù$;F“;±« %Ø®mÃn] özEÈàeÖ0#_M–; Õ¬T:?®4îÃöÉäÛØË°ˆ9Ä6÷HÄØ n"¹üNV˜õe•æ—ŒòG¬\W$kÍ ‚·å8ö7®j³§1p>S(‘óÆÅ ê½ÌJ›óÑõì(‘ÌØ[ˆŠmØg•®Ø­j®]âƒî%Ùºsbáün>»l^:êtgì‘s![Eîýà„Ë~=DR©~ßXLèpQEno‹‡ïÙ™EnXwqŒLZÖr„w€.k~ÁÝoÈÂ=çÐ}íÖ04¥ɤ4Ê žôd•ú­ÐE`zs‘¼ÿ9 {Cj²?J¸ÝF-urÕ°)?ÅÍrŹG¢HžŠ „U홬ÒùKOe`“ñ™¼´8-ØöòQù£‘Höíx ÍWŸH¥ùGDàíÃm"Yÿf"¾o?ÊþÆUí—uê`‰4Ø'ØÇÉ%˜5æ@f4»»¦žìûA轩¥HÞé†öC«ò ¥¯ë”°yC1R½‰Õ»„æ#‘ôʉ†KùMTêï©„·=ò¥gÆ¥.` =‡ÀŸ$òî­ÓЉµa׿³Gß ‘ì˜éаŸ±åÿœÃÙ)²DV58k3f ¤R¿ák"°aýcÜo‰ÉCÓÙÁï‘P8ƒõ¸­¼ölÓûQ(ÞßQ$MFÆBÝÔ[ •ÎÿØ- >IV2y£"u­³O¾ÞÀÇs9jò(®¡Ëù©4ß|iVÜ+’“.^ÁécSÙÿaU5/ÏùÑøèyT"5ógÓoâžqSÔÌžŒ@c/ÔÌ¿A;¯Æ©™wö‡yú#©™+õ_²( 5c,R37žn ­¨-jR3?gäƒnŽ‹¤f~9Ý’ûh‘üÕ~ ’"!ÏðHÍÜ£I,bj¤f^Õ>v¾S‘¿z¾Á…\ïrR&5óÀÝù°0E&u~je 6_þ!¿zÿ]zu®áÿ2&ÍKV~ȸ|{äf7É”S™¨[c˜LÎìŽÖþóÙÕÕŸî$Ÿ9îQ$*®Æ«È݇£13erû±Ä—ôb÷,µÁÕ˽$ò\¦=ÛÕI¥þù6v8ï-ÚcÚÀÇìC›h¼¶óS“j«Xh=;”Hvï௵D²¢M,Ý[°;ö^A¦Ü•uŸTŒïWþP‘Jý¬Ö„Â#þ¬DÇ]„v_6²\Â×ò¿ØÚ “°jìJv{/©±@&7ÔwÁ ¤©t~oæõ–ɯ^¨7­'Ù+ï·\W‘¿…â½öATš¿,?ÆÏFÈ䨃aŸ0›UºÿV5²®#úwë,’M6;a²]?vuY2L÷n•Éîwaùì0;[ EDr´ŠLlŒƒ™}²aÍ ü\\)‘ƒ‚àw”uýÓû&Îɉ©Õ¯wÍz²Jý?v€~LC‘Œnì„}ÑÝØÔà (ÐNEέ+!õß:lO¯4È~'‡õ¢Ñâu烯úO%Ò¿–÷’ZˤR¿þaA0i-‘Ÿ–`Ò5Û& Nâ(¶ï£X$h±ÑSÜ0æÞ¤$Ò1áJ RéüµÇ<±l¦ŽLNÛïÿë°™ÂÞã©@¯‰Á2©«H*Í·ôÆ„ ÓeRßÒ­þìÆþÆUMü׿ˆä W¸LaO­ŠAè˜c2醻ÿÑ£U ª¾y äðÀ@¤üý“=¸0Æ«?«Iëh—Ä7HÂÖGæùâ(„¼ W‘Jý‡¿uÆ©¾#D²s_7”'NcÓ,RðúCHÙ{Cî–~Q“ÑÕOû©µV dµHt}^®"ç-ŒÄ¿ê×Ìÿ×»¢àtYM*~k•ú"µ•$‘sGùÀ:,†}_˜mU“Ç‚b¼j…Ùop®Þë#ëZ„#÷qS5©¸JÛÏ#üQG™ì³ÙÑ{Ú²!ëe˜½,’5e¡åì¬Òüª†¶0ù;Z"û¦œÅÁöþ‰äo\Õãvîè:džHî=íììý¶A˜–qT&KK/@[o›Õ;¯û‰då‚(oÈ6Ö‹ÅÛÀöYC?á….lFó´Ì÷HÓê×§ýDR©ÿ”°ó8^g™HjoóB§KXõà Ü~i)‘‡Í2°oÁmv|H>×›®"_–#µñ6MJ‚”pXMþ«—ŠI)±©Ôoº³'Æ\÷•È”fç‘kaÍNŸ‹%/ªÈ”È8Xº¹˜2t¤L*Ío–ã'‡'Šdóu¡rœÆþÆU ­å‡?ÿÞ)’‰“Pûî^v¬§3µ•I·Žp-½'‘… ó ³´H6ª*Dpf©@n„ÎA*™<ÓÓ OÿÂN („¿Ù#‰´©•ŠÁ±‹eR©ÿÓ¤ ä„É‚o!höñ(­NŠÉke²ý‘tix€ÝÙÏÍ3ndB¥Ü{vÉÙ7á°r„Lκd÷×H¤R?‡ÚNÈ~ÐGM¶-tÀç³{T¤ÇªdX¤­H«}—ÐuÓxöàH˜L]/’y‘8|ì8«tþ¦x'ìõA"[ÝwÄ‹úOY7£|Þ¹F&ë=‰FvÊjViþÌ Ñè×nH—€×†5ØÿaU5¯Åç‚ðÙþ€HjæÚ+aþê´DjæÝ/• ñ°º©™ŒvÂÊgõdR3ß~?s6ˆ2©™+õï:*ë.Iͼáß¡¸=ôˆLjæ£oì‘ÔÌ÷†8"·_¾@þj¿{]àÞo @jæ1{2`º¹‘@jæ>¶—°fË‘üÕó_4wÄÀn¥©™c{”J&u~ÑÄDìnä.¿zÿ]óëáÿ2o}(ŠRk ¤¡~Ž>Q‘ûJ©,P“5œd˜Ï%RüEÜ~W[&“ö=R"Ö̃ù‚ö‰dø¹<åxIä£9v(7(T‘Ù=‘¶´·H*õŸÒ,ÚåÝEò¼Þ¤6euN^À3ØŠÈ ´›¾š½¼-ÇÒR{tfvÙÇÝNÄ´µ›Ùàoé)sR‘Jýú$`˧Ú"ùáCÚޯˎÌõö£Rÿ\.ÚùvU‘BеÝ&5yºvœ™±JçÏôˆ†V¬á(r’I4B­ ÈŸµPÓ¬—L:Z†AÏp «¸?CÜ1!ÖJE. ôÆë#GRéþ_Xի˜¬"7ìGŽý96¯i2 ›ØJ¤¶‹ŒÀá¹ìþï0èvÖÖ‹XÖÊNM°Ê¼CȤó¬ìùc›êŸÉCE²l€„lÛ0Têor÷2ºO~'fa™ÑÊfë=‰Àðº;D²üHÌܶ±^Mâ±f[m\±3Ÿ'ìS‘ó+2qôrºDÚ.—p£é|™TêçôTFCëÚ"™ÿ(O÷ÈŽ²a}/EM^8“ “˜9™Û+íÊö±O¾W¿þM}Ì*¯=8S¤Ïl£h軎P‘»]€×ÈdÍ–.ؼ»«4ÿÚô@,ê's>†"cÆYö7®jJy8-£"?÷‰@„a˜y²›ÇÍ5d²C@"&wd7 ƒÖ»OIäÆQð-;¦&˜ÆÀtØ™,øˆäm°uG7Ù¸8]î°JýëvÎB¹§»@:êeáa·5lË)Ð[!’ºC3Ñ뾊me‰­í«Hk›0èß)enbK³M2¿ÎÛ·4b•úý]ä œv*AM.³:véÈ÷uÈ)£SÑoy45Èã½Þ°Od`ÏJVé|û×Q˜ãm¦"¯>ˆBìQöÊr;<ù¦+‘[ ì°âèß©4Å÷Ì·é)þ¨?·žŠü«Z83FeቤÓô¤»×V“…8|),“ªzQX:–TýiwÑG"Û„Eãö'öðO< ʤïl´I¨”È…‡£1af†@fÖ¸„!z¥¬Rÿûw2±}CK¼8"î»O©Èõò0evc‘ô{v$þmBÑòæ<6Ë<‹ BØ~³°+ÅBEÎøÇ³úÛ¡–H*õóù™“/ÝòfÓ<|ß¹„}],áP‹4‰L³ND¥ÿ±ö GxøíÈ/¦ç1mùp‘T:ÿý½(Œ˜¨"u+«!o±Ys\áÚ½½Hn«~½®{h&«4ÿø_¡Ób´š4K „–q ö7®ª¾N*GoW“z×ÃñrX;^ Å¿©2¹¼[ Üm§°C#`:õºDS]D¥ñöÄ'¤'5`û§ºâ€Ô^ ŸËGù Ý$rJ`/ê)“JýÃ&]†ÖëGñ$ZU¿ž¬ß¯&{],@Áß…*òÌœ+X2륚lÞ6ßÕUC½¦"–\ý›}ZsÖÜ/ó{¤â°_>J×ßU‘Óî Õ°Fù¹zŽMA¬DZÆ`î]öŽ_Hõ¿c§H~]#ÃäÚrVéü­?¢Ôü‹Š¼Û%m66H!»úÿ½Ö‘ܽ& Æ{ú²Jó»\ðEýõjùWó 8¹ÞE ãªZ›„ãÀâr5Y÷J^¨'‘?}ð½\”IAÇÏ÷aÿ¶ñÅþYï$R¬:ųÔlJŠZE… ¤EUš¶¼Î^r‹ÆHû=2Ù¤™'Âîe•ú· JƒõÃJ5ù¹õ%èmî*‘_ur1­Älwº~l|fÚ¥k‹¤ÅÎK˜׈]ÿ-cØ©Èú¡xü «Ô¯²cféÔ‘È}W0<ú[…t/sör~$ÖÌYûÑÙØmþC ­Æä Po°ŠT:éŒh”´ï$…–Ñ8·§/«e‚]7Ùž)héÐUš¿m¡šµj(’F11¨Rdÿ‡UÕ¼®h‡aŽmG‰Ô̵︠h×ÿÃÚ]G•no BÜ-8${÷‹»|pwlРÁ5îÖIºÓš¤»÷×ñ@ÜÝ]×Û§n­ïœê_ݳoê°ÿyªXìµÞ®¬Þ25@9+i_G–?Ü'¹1Ò¾^Ë# 5Šdˆ¤}ÝÃ=IÕŽI¤}].ÿâééè; ;#íë;fàÔ¶RVòÿÔǽü²HÚ×sâThÖö„H6_9ÇSè±7‹‘öõ“’á×ßI¤}½ü×L\k¼ƒ‘…ïr€=ó¼Dòÿ|þ³|G›ÈÂöj—ŒmQ£daÏÿ£ÉŸ§ñß̽’çå°’KN1ì ìËøXºŒÞkuù×8á|†{¥•zyHä§/éxzó’H›Ÿ‰F¸úÀLxÏê¤ »5?_R.ÿŒ%&ܸœ(‘§Z¤¢ï“îó:ì{ØÝJnŸˆ;îm¹£“¢KÉÊMõèÛë·Ã—h\yÜ_AžnmÆìmC¹rù&°à½w+YR• C…ÆÜ, ŸÏ·P±½£±¢îµÝ ¨ØM$­WÔXy‡+7ÿµsrJb¤¦D^UmÊÞ%¿¤qßiŽ ÞÂiR®oK:¦/Ð d¥“Y(Uû…DÊ_ˆUµ„[p¨å0+ù~´ ŽÆpòÕÐÎHa¤z½ ëwïæ: @÷Õ=D²xÝPDú\å:uMÇb³ÚBN¸mAÒ°2ŒÌ¿u/”µ’õ¾é±ºA7®\þ±“áxQ+‘µnÑèI ·×úXœzãl%K‹‚zÁyFæ?0àPr‘le1 §œE kÌ?Š ±'D2`i. |ŸK¤\¾FÌÛYÉ×ítXØÖ“[vC<ŽºSÕëià³jwV3žqæN:œƒ$‡‹")7Eù Dl,ÂÈO‚07ß$‘'¶ùãÀ²\‘ìŽe-=¤\Ÿ6Y˜‘ÆÈ˜öVxo­m%㪞ÿœŒíÍ'XÉÑ; ¸72÷b%n÷éÇÈ•+U0Uú%‘º!1x{ÚYAî*§F'¿êÜcR0BãÊÈ&5 ð"‘þ]£‘>é:#­Ñð™8Ì‹”Ë_æ¹yHdl3Z6wâ6ùßr©œ‰ ƒ²$²ÁWÛ·c¶ùm¨mE~¤y’[’,Ð(ÌŒ;J‡„Э\¹|~{ÕPέdƒ3ñðÙàŽoûJm˜ª oε`^Òhîjv>1Z¹tN.®-b%åæŸŸí“$rÿ›©áòÞ¤¼‰ ’Ÿÿ1Œ+׿ÛB#¶îìe%ËEÆãüŒ~ÜßyU­ ƒÛñ)VrÒ$|ïôoã·ªQîÉ,RNƒze?[Èç H]SZAÎ^eB›ü3"ÙP£Gµ€×^ä]—d\±>ÈfF5þY[N$=tXú µDÊåO ×#£ü3Yi”å̹^äîÑP.+&^nqxÑí=W®ÂÐf¹9çñGY‘öQ åûÞ9òžíM]õÚBÊå[U!Ç´·’“GÂñrîŵéÈŸÝCA¦žÍF~‰¦Ü~%$œ2ÆJªââñ®è¿•›ÏN!ãÕ/2·~0üwõÈÆlœnèª ýòPîÔ‘”½kô ƒ¿K#+ùà‘|³RùWÕZU…€Ô‰V²Ol<žÇ­uZ Ÿ†[Ⱦz¼iyƒÛjŽW§®ÉÑ™f°î—òÓH3Ì?¹C³­pÛàÊuþ½J0rÚËHd%ÕI¹ü+é1A1[ go×#íJ wÏü¸Ìi$’o|ÔØÈêq·(àþe+wÛ¢<ò¹•§˜¡n·‹‘]¶éðÚZÆJÊåë[1 §ûU±’uƒñ´ÊOF¦µ?†(…£‚<¾ö8t¶ˆäá€`´žZÔJ®Þ€®ƒNZH¹ù.Û‚±69J | Fï1÷¹µîÅü(¥Dn½—¨¯áŒ”ëoz¦jOrhsX2-"ùWu|Ï8¬úém%Ý?GcÔ­\±¨Oú-—È}_Pž~Àm‚TdžîEêJápò©™¼8'eUd¤·íñ;ä4îí7J ?[SAN½,ÁëµÈ•Ë_¿„Ï&~ȈlfWIý- tå^ äé¶»G}onQ?-^ (¢ ¿ H¢¶3׉£V²cctøs¦DÊ嫺=SGgdÌ>?ÈÚÍ öÏÇÑ !"ùlk>2ÜÚdé•áxßÐ,’Ãï&âàÉÏ\¹ùgW„ Cg‘<_$KÖµâªbÆÅmùŒ ²]‚flJäÊõïš×–ä‹r ø»Bîÿ°ªöGæ±H¼úÕÍJÚ×o÷JƵ{i_²@‡&Ò¾®*+Á剫•´¯k‡žÄcÓa‘´¯ËåO•Œè×F$íëGjê°+$ÎBÚ×7EcmÛv 2óK"½ëν¤OD©Y}­¤ë(¤8»qÔ‰DÞÄ& òší+#õàÊå‹ì'ٮ歭ä´]©X–Öž[zŠ©½‚ <† Yµ¸ÕG¤aÁ¬æVòå·$¼žÚž+7¿Ø§PìNrQìM,sjpó^ž@ûšùùsR>~­ d¤\ÿÞ£"Àv5´’Å›âbn2#åÎ/Ī>²½ùÕÓÏ·+[¨àpieg2¶¢Žõ=Éz‡o—`![4´½¾¼~ÂÈ 7qRù•ëuá«Ú_$_ Ì©3U¸£jDcÿ”tFF¿¶>H¹ügý‘ôä‘@þ ÷ÇþÏDr®›_ÿî® Ïä˜ñéZ[îº9¡˜üñ)#‹Ga¡ßnÛÙX¾ï¢HþÙ?[*ÇI¤\¾c¶×Í“Õ:XɆ¢áú>ÌÄÆ÷?E2¿C¦æKÜ»×£p§èFzFF ïçf‰”›ÿéS8 ¯¯ /—ÄnÜÌýY¸›[ÇJ.~•‚Gƒzråú«ßù¡^`13Y·^~TU‰äo\Uý®Dh ¢½È—‰¨·¾@HKFŽ~“DÞ›‚K‹¾rK–Áá–7y±yÚÇïä>üšƒÙ^ÙùaO6ŠWMgä«Q1¸w§¥‚ÌýlAÓº½¸rù/ÄàAb¤HFÕ‚wêyî3·tä:+Èײðlè)‘¼x=“?ô"Ÿå…"Ê ‘ü:΂аdF:oÖ¢YÌ^®\>¿‡*dml%ß?‰G±2Õ¸£äÂz}¶H~ß“‹¾ó“r›{_û‰dð?¶»ß¦E\¹ù‹ZDáâ¹Ö 2od4Άuàž(‘ˆ*‡;ZÉRC"1°ä'FÊõ_¥ŠD…[•d® ›ª¶áþÆUí´+å>ìÈ‘î ¸P=;¯[ üü»3²î¤dtø¼…[;>>7\%rþ® œ.³\$[Z­¨p·¶•ÌüÓˆvݹ_äãK5_‘ìª9Š·µó)—¿ÙÏ`ïvP°à}eî/ÿDš$’e§å@Û´¶À5Fáî¤ã"95<ã o¸¿N‚ƒÓ&‰|õÍvwñb&åòåÿ‡×ÇKXɘÁ1øý‘×s>£™D~”…¸«uÙþ•yÈ©/´XÜâ½…”›ÿ`G ~Þ÷T©GcQ.\‡×¡úµcdjå|uz"²_EçÌêÚ@AVµ¢Äã"ùW5îc<&7sÉz7âñbDne­Q»bY­—¥Ó¹ÇÛ‡!rByÚ3Í͹•ñ`½ºZÉ— œêʽ_2—+7åv7ÄâñªcŒ”Ëÿ÷ùüÓDA^JŠF^ŸŽÜ5˲àÜçDæ éØÔt)#3Wkà1äHú4"lÑinMM æîÂȵã´oœäÊåÛt2 ®Å-ŒÜ|-?·ìâz/ÊÀé+k¹/6XQ²I8×ûr"ö…šÉ¬%ñ4±¿HÊÍo[V‰Ö™Ýd§QñhäÝ›+”ŒCÖèÃ"y÷¥uúÄqåúo2ð%yH>ž’Ž ÷ñ^äJ>Œ‡ºT‘¼Z)§ ¹§»%aUæ)F¶¹–€È§¹7kÆ(ÈÎ+ÍH2÷ã^÷À¥8F~LòÖGcrߺXø$­÷"3uIX<¥¬HÊ埑‡%û»*È£_Pep_®h{q¬ÂȦ^©Ð„Çqg¥ÂtëˆHj6›°ír[îOÛr¥Š‘kLÁè»?^ åò]Z‰šÏŒ|‰U}OJääÑüŠ60Ò©c*Ê9™¹–¢IÐ|º'’㟙P𤤂”›Ÿ›€Îƒû)È¥ßÑ[;€;à›žW‰dì6#Vl¾!rý•æ.ÍÈ]—R2Ä‹û?¬ªý1}NÎÞÚ!’öõ¸ÁqðÙ›ÁHûz‡áYáÑHAÚ×ët ‚[v®HÚ×\×Áçæ i_—ËŸÙ/ Ú ¤}=<Ò€íµ‚ùêGS1}ûßi_/ã…jÊj ²°ùnvBFç8 i_ÿámD­ƒ:FÚ×¥Í9|*A$ ;¢·–•ƒ¤}Ý9V®VO²°ý=l+~wa9Föüÿ8n ÈÇsï+%Þe:YÉSã`ÌtäAÞüFÎ]¥Â­gQÜõ÷#ðtê9‘|\"â¯Ò ²Ë£ 4Êe丿p=p©Yzr$L­J¤®¼ é{ýDR.ÆN5&ßtö"NOB×á7¹ºå*¼j +Ùh] rz»qO„g Y•Êȼ|KYÉ.Í%|¯ÛŠ»>C‰‹êpåò½«¥GùûYÙ²¦yù︕,f¤w?ÂÈ¿¯¦à½c÷ÏIH¨"’ )Xò¢€+7ÿTs .Ì.¯ “=%t?_†Û)0_k\ɧŽÁÕ}¯@Êõw}oEúãqŒ|Ô„~炸rçbU»8د‡ÿÃÈr'¢‘Òó:7V"’rù5Ñáͦ:Yî‡Ú¿žy‘•{".K%‘[m¯½uœE²üÇôº_ÓJ& û Ù.ZƒÁ/3D²Ã‹L[Ћ+—O‡TÔkZ\$ß2–m½+׋kñêø\F–¸•fpç®Þ”Ži?JXI‡…:¬OñäÊÍ28 ¦ ¿Drsÿtø¯ÿøoð)«¸•låÙFÊõ7nM¨¦%²áx-êuïàEþÆUý18'7îdäñÛ˜Øy·^‰8z¬H—ôlÓK$ÓºN;a&›.7Ãøü‘DšZd Ìž×"éø65—áÎj¬àºe£²æ®DÊåï‘¢G½¤þ^äѾ8Šƒë!oFNT"|ŒÂBÊõŸ7Úg§X/r˜*­$ò7®ªKT²Ç4eäe¿\ò¼#‘#»Û~¤ÏcE²û‘h¬HzÄýt:b«3r®F½¾Hä)lÔ±;éLÇï`dð% îT·’û¤X\°4äÊ埮2`WL‰Œºf€[¹ÍÜC1JtÈ®¦ O×à™±w®Éö }"J$Ë_ GÒØ² 2%Z‹ —n"ågBÂ×M\¹|Gº38×J´ÿœcxð5Q"jÐ6|—… X¡EFµ?¼ÈÖ}CðÍxT$Óf' Þ†Ö Rn¾[¹,<ó» ’÷³plÁ1îɾ*t<#÷iÑzÕY®\ÿÁ'’1óZ+Fùn{½šs€ûWu)‹Àö+³%2Ø){›ÉwŸâðv±“‚¬%¨ ÚØ€»¯‚£„Aé2U‡ü¯^¤ßZ&°0Ò´ï—깉y˜Zæ’D¶yŽç”Qrù¿Ÿ6 |n¦D:üm@ƒmŒ4ÕHF䇪 ²‚—gï:p%/%ž¹µãzdjqù‚ûÍ'ë§x‘³F¥a¹é#åòÝøÈàVP‹‘C#\žÌäþ¸m{PÙ|É‹1Ò€¶Í"¹ŸBÌ8U±ž‚,•Ÿ…“c‰¤Ü|_M6–L7‹dÅE9ÐlKàÆèñ –¿YјÜn‡™t.¿÷Éœl }ªf%ç˜c n]…+—߬3 ²Z4#×ÔÔcáû$î×þ9È(~ÑLÞ[…¶q]¹ÂIBÏK¹YoRQó¹Ä-»,Á§ïrÓîE£yÆ.‰”Ë7Äͣ牬lϤgÚrŸKÇPó[‘lÖ0þµ<-ä2Û;À¡i™"9­s:^V˜Ê•›ÙA-}– dÀR=zþ=’Û#)Ÿð"¥.°ž4‹¤\ÿ«ÛSÐãì‘Ü/!0؃ûWõ­íjvgc‘¼!¡îÅ2Ük±w‰»ðN š<=Ì}à•%ƒJXÉ·y©èÛªwä„dxn!’;K¤@eš"޶Ô|huiPçâdÓ1)—¿„Ÿ ;Vy?!AUU\õ#º†=æÕÓ¢²ï5îг±¨[ÿ87îaʬ^Å-R;³Ÿ}ñ"ýªëàÿºžDÊåë¿ ½ŒîŒ¬z_…ÝanÜÝ-Ž!„wíóœ©Ïu_’… ¥+wÛ wš³’róÇëQª„‡@Öº¦ÇçÊÜ'á `ÇÊ+È–-’1²V9®\ÿö/Óàðˆu&ý"Ò|n#㪮™Í0¿å%YÉŠÙörƒwšàümŠH¦›a4TäV§ÆÒõîVrvf4.í¨Ì]³ÀˆÅw¯YÈûÇÿõß?;H¤Ïd†’_î2²üxò[_çÊåo}R Ÿq¡Œ|^:Çïäæ7Q¡c£d®a}<žXÏÕ> ÕF–Èç‹ÂpaüP‘“‘õªD£rT+näJŒ¼Uœ›®SÂ9ÙÏBÎ=‰n®Š¤Ç3%fjª*ÈJµn+&’ªÛVÜ«2‡‘²'”84ßG"#ŸÆ!cõ 3Ùt ]Z×¶’| ¸R­÷çÙ Ì«üF w¶ÆŠJ¤ÜühÑ€ÎËx‘íýë^4™È¼VüR—cdþ0fØÅ•ëÿÔ!u;ZIï:XæšÀÈ߸ª·{¥¡óno‰Ü6> …\nÔžTø_Ï5þLFß}znÿv!X°u½H~¼‰s.߸34:}ãɽbNÆ«Š#¹£î0°Ô=Lè¨Çâ‚V)—ïHh«\•HŸN‘x¤÷¦€‚íòý&.(&’é7´¸æÔRAf3£óœÆÜWZj3G+)ôŽÆ²½ç)—¯Èì8ŒQÏÈ¢£â°§Ò/îÛëH‹Ëµ¬dzÓ,oæÄõoD÷Zd£cYÞ?I¹ùF ü"Xȳ# øê¡ãÂ]ŠÝŒìÑ6 æµçÊõ?Ü(ÆðrùüH(Š­ðÉÿaUíQ}ÒPvqEFÚ× 5"rÉI‰´¯ôHÄ©îõ¤}}LK Þ«"’öõì@5Ló›¤}].ÿÀ“¸³¶Ž@Ú׳ªhP䉫HÚ×½Ï@àåb Ò¾^fzêÍMÈÂæk—‡Ü}DÒ¾8%c¼b¤}ýpËcpM(/‘…o`@;EQ‰´¯Û­F½û$²°ý;Έ㉊ ²°çÿDZ²á1ü7½:¤@ìÒ@A[bBßìZÜ+3²¡?ifäK:¶WùÅm¾5 ZqŸ… >©ë½N ƒ~ÄâûçÉŒ ?…É=×YÈÝ3ãáPò‰@þjc»úv`")—ÿ~t-¨d%Ýsâp-°4·D’¹K<$ÒïŠ>#y’Æö‰8Y£«HÞy¯Æ(ƒÜgßt¸õ±6·ÒJ>¸ rù–y«àØÅ"‘‰cQÐr·ÃÆD¼r»ÍÈ®§â0Õ+›{©T,Vå’ÈònqдØ!’ró¤áÉØË"ùsB:þ*~œ{#%AÖ’Œ|83Ã|råú‰`Ø6ÇÉJF½ÕaóR7®Üù…XÕý‡,˜Ó·Š‚<}„áÒó²ÜEN«n%ÿ9Œ1CšrSÎ&aë”"9ýW4ã¹}GCÿn6·TT<´½nq‹IÃa6wtN:¦ne¤\þî5bð´ÇgF7Gb{ä%îà‹Z° ž™4^ÓÊmÜ mõ¨õR#’÷§¢Ã«Dn¬—»f7`äo=*.ÍàÊ峆&`rXH?˜ÔurzP4Žxû1òNÛH¼¿#pìÒâŠò½H>yÇpºW>WnþûŠòÓ,’M’3pxT÷ƒÕˆ¯K\­dÁ½x|X”+׿¿þôÀÉŠV²Y³0Ävˆ`äo\ÕñKÒÐû¬ƒ‚lZ'ÖžOD²Ûu ªôof%¯ôM@α:Ü”UÉøx|–H™gÆþÉ-¸šK:üÜ_JÁ}iºÛ'‘ÜWZ‡¡Á¬ä–Eáˆmû7#åò'ÂQ¬Ž†‘)®ax¡ÞÄ-ò¯¿:Ãeª@žž“Œ7=vz‘;/J84r³Hžo—Ž™mŸ¤ûÛÕìÌVFŽÎŽD¿¦")—oI«d}NÈ—;ðéh5‘\t4#2GJäàÍáXU6U ¯¾ÊFï-/²ýåtlQže¤Üüb}2Qº~ HV>–‰Ç‹wp»žˆDȯmŒô7FàŹþ)׿¥1{®|õ"»‡[;‹HþÆUÍ÷ËDÇZ'E2ìjªïSsGõA]¿RV2Ý+î0r@K†U÷Z ä<ÃÂÎß$RÙ< Õö‹di÷\Lî<ÔBîèéV‘ì5ÙˆRÍš*H¹ü9¯BðîF#Fžï‚ck%rZõ¼¯Ùš{ª W¹íÇgÀx~?·çÕ4üåü7#ûÕPÜ\RA¶~`EÕŸÏDR.ߪ‘‰¨°m¨HÞþ[…âg6r×ýâšù\UTšÅ›¹åm¯—mêYÉœAQ(Uå#åæè…PÃb‘³/ ãnâN˜‹]UC¸ñ³“Чýe®\ÿj”èú¾Š‚¬sÉ€_ƒp㪞»nv‹äœ¸Ôª=Š«]ŠàøŒìP%Çb%rÚT 4!™¸!ûÚžåv>—…7ÚXn³efU°’?r¡û¸N$ÿn–…´ƒ)—S$6n$¯×… lƒŠ"™³ÂmÍŽŒ¼ÙÄö#ϘÆ=¨·`iÿkÜœuF”«[ÂJþºš‹—½$²þ~†3œ¬¤\¾.5“Pr³ŸH6/ª…ås÷áƒX¨ö¿ãÖ-“ˆìžN rgA0|ó"ºDáÖî")7ßÑvÉé½»¹H^vÊÆ‡%¸mGÚ¾Âãžne{Ð݇+×ÿGŠïŠ8(Èã–l\H/’¿qU‡µÎÅl'g‘œþg.®œß/Y}B½-“ù=¶Šdé÷I¨}ú>#ç‰J¼n™ÉíQN‡¶™­¤ï®X8ï.Ïý×°ˆºwäÜ0¼rèÊH¹üqSBqiò ‘ŒÎ Ãi§Dnl JE-ad\IJéFq§öVã›g)+É&Ä¢oî=FÎã¯kXÉõåC0§ FÊåkÖ\ o:‘ük­ÙFnÐ) >­¡ _7ÂwjMî—T=²–6ãÎ]”‰…á·ERnþÑvÙèÞð¨@^í– v-· DBƼÖ²ß(æßnÈH¹þû†å`JŽVâÆ¥Cé•ÂÈÿaUí9+rá0öBgÒ¾þ¥\$o=#’öõ”’QÈ(7†‘öueÑ0Œ®ÈHûúˆÓÑèÚí¨HÚ×åò/µ=8<½$’öõ"B©Øó§ƒ•,ìùÿqL¿‹ÿf­ðt·*"ƒÎ§£`B˜…œ<Šaù’ÛŒôøšŠñݬä6ר¾UÈ›¥£á¦Þ.’Õ&‡ÃgN¬‰çD¡§÷.‘\ṎG‹NYÈGmµîò@"åòO 2À»E}F®;¢j×åºÖHB©3rK¾Ä n¾NíªÁÜ#ÿú¬Û_âîO—p¨Kïbd5K(Ò^ÖåÆù„!²ç nÍf¡xž-‘§Z§â‘³I ƒ%#g¸DÊÍoš“‹[;ªKdѸ\lV‡q7º$àÍåòƒ› ïbn ¤\7ÇDdtu”ÈÞá* ü¬È߸ªï=Óñrc{F Ký²³¸ÏV%`kóGÜ”qè>ÒÄÝó .ŸÉy†Ë—6so´`mÕLn…‚4x†·æNmkAnËÎܯýüuc¤\þÜJZœØÿJ"GoÑ`~Ÿln§¯”ñÕ‹üTΈ¼„#²aÛÕ¼E3¹hÒ_c!åò¹´ Å=%%òëüPÌ[gH_!¦Yjîqc.ù®É„Ç:Ô¨pL"ëDiàRµ¯…”›_jY.:ÿóV"µÈ…p}FzVÕ"yo‘œg»;•Iù%rýÿ°hÐ{k‘tÉÑãýÇWùWµß9+.xìbä Gq¯ŒÆäÎk¹ýúFáøõwÙuGzÙ&-ú[Ñ$âƒDn|žåS®Fð ¼#?^Óbi\EaϪÎ\¹üóëið´ÙA‰lö, ïZuã¶l@ŽvwînE$sï|ÖãÖ'î¸æ:¬‰ä–ÈÐ ¤¤•ìï§tg®\¾£«Âp0i”Hžì… £¸WDâ‰!›{o@,|7çjÆêð¹R´@n©ŸŒ¡%7råæo?•ƒ‚ýýi—ƒFªÅÜÕ?Œè9´œ@ÆÜ2 Þ¡®)×ê,# Ð[ g#žT{l!ãªÞŠ—0øœ‰‘ÇFX€b'¹×ÚEÁÇÐÇ‹,Ñ3EÖ”É×ûÍx¡Úĸ{Îu’Œ5·H7 ί‹àŽƒ¿nèDrÙª(ÂÊ(H¹ü'ï&Áÿ|ª…L*®O—€ÎäúZ¬Þk–H¿uI§oáï£éÒî™OjøäE˜ÉcCáø ]"ç^‹FÏöERöª“ÑDRŸ‹îU¿s'?M@…qdÉOàjuîe ‚[–È7 ˆsÃH¹ù›ÏgÃ4i'#×uÎÆø‚®÷0ùï”ÈŸí5˜]{»…”}À¨¯‡©Ë~‰,—¡ÁäeS¹¿qU¯\5á˜æ#Ÿ)Ráºû÷ÑØX¬O\!’ýâñkg4·N¼«.ddv&ýÀÍüšwa0÷éáx¼iÜM"g ÊÁÜÁ_²kå4l¸ùŽ‘rù' × ¼’Ù‹Ì˲­Ü¬¦ù®“M°ûï©|b’@ÎÏV£ÎÊ=ÜæÏ“ðcI%‘ÌY‚Ös+*È•§³`öTˆ¤\¾.0cO9ÙäªM«p«TIƶU¹Y÷Íp}WšÛn‡ Ïš cdëçqpˆ+b!åæôÍBÛƒFž=ž‰–‡2¸bc N­ È©þ:l‰ª*’rýÃ’ =4ß‹,z9 wê\ÈÿaUí¥Û’ñfìFÚ×CCT(Ñ*M$íëç÷'`„wˆDÚ×;'bTœI íëc<ÜtÎVÒ¾.—öP-òœ§ ¤}}É&5Žo**’öõSÞ:<[ÝW$íë÷†¥ct×hF6ßÛê쮪 íë{ˤaz¥G"ùÔL€öåV‘[ÈùÇ?e eF#íëc7%ãÚ¢*"YØþ%VêÐú²»Höüÿ8n$æà¿Ù¿ž„Ä*UDÒoƒ„WÎ×rdn$*úMÉ]Gb0e“†»vžW:‘Èó¿´X²úw‚· ŸŸ3rºí¼±û¹G»é0Ý)E ½LÉ!8råòwÝ©DâøË"¹øqš‹¯¹eo§Àwzq+)ŒÔÂú±÷ŸÝÑp¹ f¤ô4C³‹p³NDâ¤Kl哈Ùù")—ïÄ­X„Ékm•¨º­7×{¢_TQûö$ãÊXîØ©øÐF$ë6á̓·Rnþ¶ö©¨56‘u¦¥Àã|.·‘6??•²’÷mŸ³†*ˆ‘²Ÿ&d™ÈÈ¿ªð4WîüB¬ê¤d ›"|2'SªsO¼HÅäxztS$WcÄäÏÜÝS5èÛ}D†¸Øžw¬ô$ÖŒÂlç·rؼTX6I$?N6bsed‡&jÌð®É•Ë?ÍG¢ž Ò½‡íõ̽8·eëäŸxËÈ~bp´¤–[+8—?¾H]~ÂkEòaI3Ê¿ÿ·wÞXá–¸ßBÊåK}ëEòå³D<wˆûp¡áíä¾4l3_Éu8\­#ç1 O,Æ•›ÿÈ7Ù³N0òV¢óËp7õ Ç›ým²Æ÷hôÙjI¹þš~I(zd#«lHD»uW%ò7®j`Œ‡VM-äÄ©Ä Ý%rÈ=Z¸ýÉ1RPbÆ nnE-ß,È?loÖëb¸3V'âÆª,‘̯lÀ‹œ³Ü˜ñ (ˆ¸$õ¨:"P$åò/¯i@Ò˜ òÞ?ÉÈ9RŒ{cL$\êµf¤”®Y&/²×ýxäk‹(È Ò^Tä¼™‚ŠÝ‚Yb¯ ïWoäÊåkS7 ÄDˆ¤e–¯Çr×ËD…°#ÜkIÙVVæ>õ°=óiÌä-|¦Å ¤ÜüŠÇ ø*gäÁ›zL‰ë£OÂ¼ëŽ rŸhFÁ½ç")×_µ$“Ëßô"·35æ•tÉ߸ªÍ¾Zðªi‚D&,²À£ø+îÆSì©'’ÚKV¼øyk*0Ày×:lúÝŸ§?=IG•Ï„ˆ¤ƒ‡ó=ÿÈ `X»ã57ÑË‚9û)—ÿuŠ Ÿ}ÉC$H¹î³¢à0o87¦Fjø¹YF3úÖ(® Õ°=ÓåZDòÏuñ¸ó9΋4–× ´ø0‘”Ëw6NØ Ñ"Ù$Lj…¦î›69˜\rª…<Ó#iž­Yâ”í+½oœ@Æt0¢Îо)7ÓÎU»ÂH‡":˜Ø¿=Ô5nnSD2Ý9E²%R®\bÔÉ«NF\.x#¿qU+5Ã\Ä•‘¥‹˜¡hÇõÏNG½ŸÍDrûÒ k•ÈÎ×8=zD¶n@HÓÜ™çîR¹9˜±n÷:Fü¡ÂѹÜÕ=£Pï[²')—B¼y~ÇEÒ]ŸŽ‰'â¸Ë;¨ð°Á}:t˜ýš[ç{6>=/Íe sP¯ú/‰l›ŒéåEÒ(¤à³¹¥DÊåk‚ ±ÛEíM^OãÆ:d¢o#õ~VDJ׸ïëð5ûšD6ŽQC™ß˜+7ßËY‹³#®0rl]ÛƒÃíKÜR$d_5p¿ÐÃßå!W®ÿñ)ÉpXS o¤¡U¹IÜß·ª‡™PgK?F–ò&p5·ÒáRë¸D¦×Mì¥9-O ¿¼Š\©\‚³ŸJ¤ÇXÞ? ad“—jY¹«;“ˆþŸФ£»Öeé\¹üic3ñ0ÝG$}dáZ-¸O&¦bü½+\ë wEs+Äf ½ÅÄÈŠîŒJ.a%oÜÕ!¸ú ‰¬ì¡jQC )—oý3•o-’[Y°tüC,>̌ՊZÉM[p^ZŽ‹x5zÅÜÈA}õ˜Þ~˜HÊÍ?Ð& Uÿ¸ÀÈ*ÝÕ˜y¼€;Ò3U:¤pÿ\ƒDÑ‘+×ÿvŠw¬qyi…ncvpÿ‡Uµ?>{¤Ø^æ3Ò¾þ}¦„1KÃi_ÿÛUÿ'Õ%Ò¾ÞiKny”g¤}½RÕ,Ì0tf¤}].¿áÙX"ÿgþ‘4ŒÝQ$íëÕ]ô°wµ’öu§0æúÄ daóiWZÐó‡·@Ú×7úiPÛZÖJÚ×_u2!xK)‘,ìüJcTpky’‘öõ§î±p¿ZT$ Û¿—»ž˜ÉžÿÇÜ6 ÿÍÅÛØãÆÈ?Ü ¶=Šûºƒã»x ™¸T¾½—ÈmÇ%<Ýš,‹ÏI8¶K-‘ãÌp¨:‘^1`îõnt’ GM+¹T¨â¹‡+—¿K‹dœë,’ ¥ ¯È>®ŸV‡º×£¹?.%#ÅÓŸëoÁdÿ–ÜòÍv7Uz‘»jIظ×E$£JZ <בrùÖ–pñÇMìP‡áÔWnÏf¼öðfäÔ©XåïËÕ67cv›9Ü®£µ¸Q*Š+7X­TœvÞÃÈ–‹Ssç ÷]¿´*u˜;ª­!͸rýU.¼´k‚ͨVz#åÎ/ĪN»¤Ã»»ÑŒ ^­Å¬qQ\mžR€7·Ã¨T”»½“ùÅ„n‹¸ã\Œ©Á=¤Fvù#ܾ”ˆ,ݘ›t*[Ûø då-Þ=‹I¹ü©ËMÀÉu"¹å¤¯JxsËx›_r:÷Ê¿þC÷2¬·„óKJ2ò¼íÒ¯Ã&nËl vOˆà&ßÃŒ£a)—¯’m%3¾ºJd-ÛÊFt½ÅmfD™·¡Œ¼û·ó£¹®9J4yZ»(@‰ã*ˆ¤Üü k2zÜñgÜâÉ.Â79Ïúxq_GÄaʾ²)×IU#N–‰`dpªÃ/áþÆU­ÖXƒmŸCyæºOÎøqŽ5Â¥J87»¶Š…ÑÜÅÛ“ðým÷îÇx\hêÍ­‡§_½HEµ ©0F$³»IÈË+Å}{×Ëë%Œ”Ëÿã–šFõEòN”„Çqçr˜íêxó^”…UVƒ³®ŒŒT9;’»ËÏ+Á܆½1æ×Z‘Ñ*ìvØÃÈà-‰ž²‘øA q­?÷aÛD´¾±š«Òơέžy`¢[¿üHår ô Éæd(¿ûs÷'‘ýlWãÃήÜ-ÅR¡ù´‡‘ró=éÛ-š‘ÅôÐÞú· ›˜PäÚ&‘?Hhž¾C åúÿÝ'tE²Ö Fh"¸¿qU›mLÀˆ¡3© Ç Ö½¸·ªÄZp__Cæ1?‰ly:-ÚmÉ­:\íͼׂºÙí¸u[2L?¶ÙBnØ …ÓŒ(‘ìÛHÂg®ìG})!Àé DÖ%¡E…bŒ¼7G‹S¢¸ÉKTØYy7ëxÒ¶mȺoâ1¢áp‘ `‰h;u #ëgÚ¾’=– ¤\¾ƒf=ÚŒˆfd¥IZªÅM>‡œ]7ÍäÊoq°Ä¦ ä*¦Æ¾OG©=‡%Žû%Rn¾GWŒf¤Ï:-L“£¸‘o-¸ô¤÷¢_2*œ àÊõßÕ9FßÃ"éûØ‚&ërãª6º«„!°#/P¢ÎÒw9+8ŸÊÝô"'NS¢í\‘,º,ý³r›²`ù™¶\ïfdÈH§ #Šå:V6Ãýû<j q€+—?j[vfä ÁŒg•fq'N‹ÇÎ ^Ü‘YqØ5o•Dx­ÆÁáþ"9úª}›Fs{·ÑÀ±q·NœÛ¶ŒáÊå³VKBÁÄF~qT¡løßÜqýãqÍÝC$ÏNI„០Üs® 8m4·®Fà)Ñ\¹ùiLƒ³}"éðË–c@(×ke&âžý¬DþÉÆ\¹þÕŠéÝ$òÊ3ÞöœÆÈÿaUí©¶×ƒ»KÃ$Ò¾^»f<þ#’öõ+¶cù‘×=Iûú=Û3XtP#íë}Œq(¾b¨@Ú×åò§OÅíj3Ò¾žt!WjHûºnG*þ<³C$íëíÆKHî_„‘…ÍçÛ%Ïü'0Ò¾^¼b.´ Iûzõp ú.n-’…?¼Wž/d¤}}“5/ÖÎÈÂö÷J6ØV2’‘…=ÿ?Žy—%ü7=ï`jÌÈïôhû+”»&RÂ,¯¿Õ‚ðu™pQBÇê /Rm»šZ:ÕddµÏ)8]l·b9|χþÛTœÂ^*ï7¸påò/H†ÿœ"™0?ÅçnávÙbÀëN!ÜÎÞ©°r+wAÛëU¹³¹Ì,!ð~w‰¬fû\ ÆZÈÃ’qtèFÊå Ø-¡]?m¿þÍe™'97À6b#›WKÁý¹û¸%æ'ãÓ§CÜ®kT¨÷ÏV®Üü1)Øõf'#—~OFŽÇ~îâjø´ å¦L±õ}²…+׋íAÍ|è™DnmŠU¯·1RîüB¬jt7&¹„1ÒõÛ›æø`nËÝ& 3ÍçÖzžŒ¢†ýÜ'S0óˆ/·¶Ÿ †…q§,JÄ4ç?¹šÎJ<),‘ê쬮2I$c{§`q‰½\¹ü³¯˜Pýþ4‘|QÒ‚£]:q×t²`ôšÖÜéF ƒ‡ d—›fÜ #³¥ ^¹}Üzq¤ÓnóîJ,h#rùVÙ^OîÈ97mÏì±¥9¸ƒí>‡p—µÓ¢u•ûãu¯ ±GÖ%¢ÛÞe")7¿Õ dTÕfdå#:Ž à¤D¯DÖ:«ÄÜŽ")×ÿKsŠ| e¤ãÂDô¾”ûWµµ: Ï02b¹_/ìáθ§Ç²¡Ü¦ 0q?¯PáúÑ­Ü%O•x”S†Û(P‰«µžäêE*<_µU$¯¦Ihy¨­DæÆPÿb0#åòzcA‰ùŽ"©Ü)!Y°CB÷*Áù<ÍŒþ=ùvÑø¿Îª0zÀîšbZ\Û,’ƒï˜1¹¸rùŠÙži÷NéÃÈ­¥L8’´šë2J€K»¹E&`h÷iÜÞ]Œ8|6P$W’p´Ú”›ßØv·üb»[’Ó›° J(7]¥ÆáµûDrõ.#.í àÊõÿ³­óbJäƒkR? äo\Õ;ÃT¨Òe #K÷IÄZÝBn© *lž¸ƒ;ì_ÿ´×ÉáܤqÈßñ΋lû0 ’èñaÜŸ¿RñbónÑzñPV¬ÆÈ´ðdî'’rùóÎI¨ŸàE¾Ê–1º¶D~OAxÍ]ŒôNÑÃj{ð oÙ^·ff4á¶z‡‹«Â-db²gVbdÌ~ ãÊå ®š‚ò‹ö1²ï+F|âάÓ]¹ «*ñGÜ|‰œ2ׂ›£12ï”ëC¹ró¯ÎÑãBÓ0FîRé0öߪY0ª„»HZŽJ8[Q"åúÏÉQAÌß.’ ô(¢ åþÆUÍk»ñ¦e¤bO<þtmÃõµ(ÑïÚ7‰œô!Çç>°c'a@öA‘¬¿Æ€CB¸KH(s7Y ‡’PêÃi‰l]=ƒ§íÉQ¹æ¬*‘rùƒ&Ip®›)‘îñÛ\y&. ºSG¸}%"éè\î¶SJ¤ .&’£óq«ÒZî¬<%Þ-ÂÈ=Ðpó ‘”Ë·.^‡ð1aŒ ž¥AêÔ@®Ï÷8|¨,.Ç•ØY±¨HXœ€î½¹£Y­·¤Ü|ß×Z$$„2òf{-òk†p=L¸8}·Ø =>(¸rý'n0áZ…"*aìé­ùWõ×c%ÊÏ(ÃÈs”º÷¨DNk©ÄÔ¡{²þ¸xŒÉh$’wÇÚžaÇ.ån-!gò1ÜŸ˜ •n##Ýïé0ujwàU6(‚¹>ãÑðPm®\þ[±fü9«#GM1aï®%ÜöÙJTšëÀ­û<ÝVÞH%ï´Ö¢¢SˆHF|5"ø/?®ï#6ÌäÎŽ–p$k@Êå»ß[2cw3²`Z"]XÂ]27õ«{‹ä*‹ ás·sÝsÔøá¶ŸûWz ‚vråæïY­¨ ddTNÌöã6a‰xw5÷ÏÚ¶¹ËZ‰”½ª~¶`ö„¢Œ¬v*±}¹ÿêÚÞ®J8¤Ì‘HûzääDœ]»D$íëñ‡moÊWöJ¤}}u5ndmd¤}Ýw}ÜžŒIûº\þ û)8”³ƒ‘öõEJŒîúK íë[Ë›Qà>A$íëCŸ¥Â+p# ›/ár<® i_/½YMA HÚׯÙdöÇÕÈÂίY) ßd¤}½m«x\v®)’…íÿÁ¬ÅÝ!¡Œ,ìùÿqømûËÿ—j¦Áñ¿Ž0ráÆý!Ù}ÜÍ“¡­ºŸûÑM5=¹§kq&П»=1;ÚÎç^ë¯DèÒ’©[iOªŠäöI&Lì6ëka¯#åòçü0ãdé¦"¹©À‚OÊ'y|ƒ„þíR½ÈíŸ,øXñŒDªË$ÛnTû™7Q‹ ý¹ûm9½ïVÉõhù*€+—¯õL^‰‘—*™aZ7ˆ»8HÓ÷ÃÜZTè亅[eš‹÷oÉMRPwª/Wnþä?Œèêv˜‘]l¯«Fƒ7òz<öÏh!’ßk06ýW®jtU2òZOÛëó–³RîüB¬êÖ‘jŒ¬³‘½¡ÂÔÕ«¹Wêjùè 7‚ †žë¸A¶gض-³%òÚ%lß)Ï.«Ñ»Å.‘lk1ؾ~ÜG©Zv `äöYJlx7S"åòß!¡²ï ìoûõþz’÷îšÐ¯ïF LÆ×){¸]l+Qv·ux¬q.Â]qÛ‚±]o d¨­O¯a#)—/qn*†d¬ed÷Éx1`/WŒJÀØÛ¸ƒâÁ¼¿HäÜi+‡äÅ£&„žÍH¹ù‡ÞèñvF#§wÖÃW äÎ,š‚ýíÉ  ¼÷|H¹þ#ãÑÿG}‘7 _zíåþÆUUvMÄâ^“Ù|J>UîÎ}Ó,:{r]­J„Ï•ÈÌ"‰¸½oŒH–Ø£SÞaîû§&Œôðæ–Z*aÞÏ:y¡3•‹d£½&ŒÑMäÊå¯nû¶8÷öÔI÷»Iè¡>ÀÈMC”xT¥¦DÊåk>Ì€¶ÁþŒô­f{F?À½k»ÛL=PR"«ÌSb]•Å™¼J‹$µ?##â1ü¢Wn¾iƒ<éwL xpÕ”0¿®Q"k”HÅÌ=›)×ÿ·7ûÉá3ÍÚûW57*–ÖcäŲñ¨ÿâ“Dþ”˜0å‰L4(±ø¾õÿa宣£È¶÷áã×à>˜@° !U<¸&8w‚%H°@8„ ÁÝ%U­étº»º:¸îîînoßu¿ûÌ]ý®û«•u©>kÍî:û™dwÕ9³2õ¨õª/É*¨Ñ§#÷ø@'r;Š22ÆÓ†’’¸’œxû,7·k¸ÃåU\­üó½œ`+02w)Yr£ÖË8V €;Q—ŠŽ#í*–Ý€JÍãEÒ'. š¬áÎ5ë‘èÃU³Ú1ÈùZù–<5ÂãæFÞq½Øw–ã¾ ‘ÐyPq‘\ÕP‡mïqŸ@›5y¸=;šñÊoW«Þ"fì\¶’‘žMÈõc÷´kãvεq#u e,{Òž«µ¾1NE›Ž-ýÉwW´9áÃÈß8ª×¥âT²¬’!A©èØ€[ ”Œù¾D²i=ËDr?ŒP!È~g8ÿH%42ãsöUŒüEFÙÙ½¹u¤bÈõ¶*m@Ø‚y"©•ßo&3ra{nt™Ç­˜Šœí äÇE t.'’ÏÊØPpW÷sš‚AOp‡NU1;¡ˆJn~çÚó.a¤V¾÷Ut˜ž>€‘z&¡ú”jÜ-ë Ø;>I$Ÿ5#"y7m»ÞõçÞ«¢j¤ŸJjõWõF]œÂÈÏYŒHë¾€ûÉõ ¹>×¼²ZÆëÄŽ"©µ>+»RšÍ„ò½—rã¨z‹©˜Ð?F!÷uJEò¯bY:» Qý–Šäó¼ÄuYøÞ޶u§3ò†ÝyÏjn‘Þ©hÙH%×t–pzF‘\¸Ëõ_úpφºŽ;u 1R+«MV4}»”‘·nY°$f57¦™ûVDˆdß&x¬ä›¨"®¯—@yæÄºª—TÒÿ¥Œü…‚Yûz*êí»*Zù LÅèËÇT²U‡Ôýùxî³~éèçÍÿóAuªôNâŽêjCÍBóy!Ù€_ÿQ«ÿ¾~|ì8—‘9U=<çÄpY´êþU"™TÜŽÍߢ¹Zë+%eT¨Öˆ‘¿\#}ÿ£ù?Œªûu;)7ίH÷ú‚‚8æHI÷zÕ¥Ì)›ÄH÷úüŽ-Ÿ'’îõo®§í{×Ó–t¯kåß_" ùÿ^ÍH÷ú‡;é¨ÿy…Hº×´v Q§QŒt¯?.›æÚX¬ÉÌæ»âÚÓ~WN Ýëý\{òcò ‘t¯÷t½m†µo§’™í/Uq߇3Ò½^¼’Šb¹Í™ÙõKzËØ‹&"™ÙûÿãŽä¿Ùû°„®-K0²¡Ÿ„Ü·î©ä¦„TTLíëO6¬&áæûKY/*'‰dþ8mÇûpçoPpU®ÁÈK¿¬ÈÙj÷qcKÊûs«Œ5"µö|®VþYûl½U™‘Oô|ªÖ™[8TÆÔº ¹ûö¥ßTò¯sô/”(’eúYPfórîâIFxŽ˜ÏMá€oÇÞ\­|VɈFéÉŒ<¡Çƒó3¸‡Kx߬¼HÞ:ªÃ›”áÜ/íeÔnU‡Ûåkž^ÎÕê?‹kzx #;v5¡¥çbnlæøÿã£2Fþ]…«µ~%?c«œdØ VwlÃH­û31ª‹Ï¤¢aá•*Ùvy*Æý,È ÖÉ@Íf"yèµÅEqKù«@¿Eþ¤½¬±9 1rbM*Œ^Ä-ÿ¯¿hý–/w‚T|Ì8ëOžÿaÂÁ]KDR+®~v„Õ˜ÌÈC2°âD"wYZ*F6-ƒ!ãñh‘ôüˆéæÙÜ1ß„þòäÞµ8‘0å¨JzI.g¤V¾yuÈ»)‘¥wKX׬8WjfÄ RI"Yª`v‰Ë¹ãW(Ø\Í›ûTvbö»S*©Õ?|«s&3rì7.øÍåvÙŠ“‹ë ä™K:´.,’Zë+ÇÒqQZÂÈÙw X4;‘ûGuR\* fYÑ„¾1ǽd%Å„ÇRDRvZð·ïrîÚÙ6¼èÃÈù³Ò°Ó¼œÛѵîÎæ ™¯­Œ3{k‹ä†ŠN$–/ÈÍ9Ç?žô`¤VþK>VRéf%y9wjWÉ[×ÌøãÅ2*ò®ýÖ„ŒñsâPmF~²JˆoR”û1HF°TO$µòí¿—Šû wªdÁD—‡Cœ¤ô:%½–ˆä›M6œ©ÍMìd…Þ¾˜‘ò]:×ÉÕê_¦¯ ËÄ22|—“§EpkxZpëÈr‘\îçÀƒ¸ZëÿX!uÉÇÈ;[Sñ¼Kü£ºæA*ì•%<ÝKÂÈÒ¸¦í¸YcžHž©å@ñ³¹†Õz, gd–.Z¶~£’¾±hÊB‘Ü{Ú ¥Ã?'pöV,#g¤âfÉI©•?ÄdưUË9ù³ÓF-äÖýËŠ!óŠäóx´Ä5Ô²£RUWþÿsÞ% žçZέt&AË–ˆ¤>§ŠÑ+¢R+_~C*–‡ H¹‡„fßq;Ýp ËÒv"ùç'¼w½ȇóS1xñ^rhy×qêÓ‘Ôêü§?ÛŽcäîõ:x9qê©øUû«?94VÁ„dFj­ß6D‡Æwûˆd™Sfô8¿ŒûGõã ©D²ëE¿Í«×£€é}Šr—WÑÿÚ Ü¾Îõ+¸çÍ]íq°j ‘œ­ çn5Wß)9æ*d·Bi˜†å"¾Ì‰5ko¤V~µ¿GêÅ2òÔ#>8FrûLq"Ëé×èºÏ»—B.nkÄæ¶IŒ<¸\ƾk×#ÚŽ.K&p7 7 `x,W+ßé0×ϱr#‘¼þI‡gƒFsÖUq§V6ÌUPE›¤ *ÙË‘qÛ\‘œ\TE—q#R«·²:=èÌÈܽՔ;?§ÞWR¸÷§ÐÈ3Ž«µþç6|«-’ºD'G>Èß8ª’t(7@$?%è±ùêTn*:ì«¢’ÇÛ8±òvFŽüeÄ:×ÓŠ\QÍŠïR¸Íû*è¬6`äÍ…V¤/ZÄ]fG轱ܾqz´Ø4•«•ÿˆ$ãøs0ò†^B…‘…¹Ý†;q°ýW•¼žÇÚ¶¡ŒÌS1«dJ¿§"ßU Ó’R±û¥ÙŸ<½Ýˆ~ë’ER+_âß„}LÉlwMè?x ÷v>'bÖeäЖ8z÷ç~Ûâ@¥É]¸•"Ìx[iW«ñ†2.î¯ÉÈ•g%ÜJ-ÅmSVBÍ1gT²O ŸK^H­õO¼qbqÛ*™nÇ<ËXFþ£ê~íXgÀ‡¾ñ"é^¿ç@b§Œt¯{®s@èÓU$Ýë«F|(¾€‘îõÇ®‘|JH÷ºVþ/_R14EQI÷zòZ+æw_ÄH÷ú¡pÔýDÒ½>g¾½Ã»‹dfó²`ÿ±å"é^¯q#—'2Ò½>7ZÂâ¹™ÙþŸ"%T4æ`¤{}y¢Muq"™Ùõ”µ`ø¡åŒÌìýÿq5ûîÄÿËn®ýó§õ¹ñ»„™-‹pÿ.-aÿÊ5*i9šŠEÎH2e‰•}‹äƒ„ šÏýü͉y5!}»(¨4¥#7,W0©CY‘”÷9°é;©•_ßÙ‰¬Òu•l§ÀlªÀH6Æ€ gq½ÉèÓ‡»Ìõ¢oT[$‡6bTÛ8îÞ¿S1fÁuòý}# ›ç‰¤V¾{Íè½h1#ƒÌF”›4—›TX‚÷ãìë#ãÁ ²"éá-!pkªJ¾5ËH:W_$µú׳™Ñ'r1#›z˜±|çBîÌ¥X]5ž;ÄÛ„£…æsµÖONUP<¼¤H~}íD§÷¤Öý™Ui’„¼Ï¯©ä›BV÷Há®#AiwQ ó>‘!ÍDòÈP£+UãîþâÞ÷ö'ºŽ3Õ}’™PÖë¼VÈAŸã¸E¯¤Â¼©˜@jå¯=Õ¤Y°¦¬Þ8®èzZßl3C%'~JÅë‚ã2.Ü‚ÃRD2ÐiÃÆmÓ¸ÞÓØÒ§÷hÍ·^RI­|Oêñâ|#'ÖÕáí¦6Ü9õX”g°H 1bÏ_ Üí-x’”­4OÁ/Vž«ÕÿçLÙ’9!ȹ‹çr¯•‘°­$×ÿI*®çì Zë,w |ý6Œ,<7ßZ/âþÆQmu1[}”ŠhŸMÈ• HŸ.’C/˜0éÃî‰s ¦ødä‚ö6ÄyÌæVŠ—1"¹&·“k]¡n?2ÿ 3FJ‹D²b'Î/ÊÊÕÊ?>-ö€8F.ò´¢]¿Ü5Ñ2ʾñÉS½ "gr¯ÿ­àêÕ‚ÜO®§³ö‹›mzYÂÈ&'$ô¨—‹«•ïék My¹¢„óŸ7ªdK¯4 mœ"’QoÓ‘zg÷C'ºº¢’Y~¦£©Íµ‘ú?µú'5¢VP#}]Ç¥ éQÜ(=.×.’{ÚYK$ÌÀ>K,W«ÿ¼Ã:Té܇‘^t÷¼=w|C…ŸÔâæ9›Ž¿ƒrµÖ÷þfÆÄ/‹EòÓZö|ŸÎý£Úø–Œ¹ýEÒi×aht7á¼×$¾øþ´¢JF¾•qò¾(’Ç\{Т‡’¸‡\/Ò³U…,]AAÕué?ʉ©9N d«±v,ë7Š‘ZùÒ#çFÖ½æÚh„4áp}®Aò…[Õ‰‰¥>¨ä–}®¯dDCF}šŠšcTrÖ@DYŒtœNÅ€±güI­|±^zì›2L$/½1 rH,wâq'ƶ§’ÛO)`} 1òøe×ÓmvG<ÓÏõz #µúO8/£æ?F+cÅI/î ×Wúç(îÇÎê­= ’Zë·wíÙ¾»$Ûd'ê^^¥’ÿèº_Oõz4òœ ’îõC®=ã‘ଌt¯.cÅ’Ö DÒ½þvŠOóé^ï¸X#É¡Œt¯kå/óÐu2nžŸ‘îõÀ«v”zÑŸ‘îõ]M]ÇI/‡@º×ƒ¯±jä<‘Ìl¾gïLH¶P$Ýëu‹8à{°#ÝëmFšÑcÏ"Ff¶ÿú¼2f´,ÎH÷ú×qùxàEÌìúUÃí8Rn$#3{ÿ\›÷ýû#ÿÍ^‘:ÄÝiÍH½MFžµÕ¹uËèЮ·{= ÚŽQÉ Ïõ¢Éb Òpäùî Å &ùä±:‘Üñ“ôß•Ÿ¬ EòŠëø°p¢Q µòçqbQX¼Jÿ¬àÛÃ'Ü=}Ìh|o>#{<ÑC½ÖRÂô‰ÚE‡€lÍDrÔy^ìÂÈe“%Hµ-©•/2Ì‚il#' 5£÷·ùÜWY$dOØèGN˜ á[v“@vI0`ËœpFÎ) ¡ÕÝ—NR«¹viøž¶€‘¡ë\#¿8™›Ô]Á‹ãe¸!­ð1Íåj­?×׆”rÓEîºëRëþLŒjÓb2Fäfd–eî>ª’ Ih?7›@Æ_”°Úkîš¶6¬<0M$+œZœ›{x²…ž‘CtéhUv>÷ý;ŽzŽá¾¡GÎ.ƒ¹ZùÕ¬*ù#{uÀgn7²· å‹'÷¬§Urè#öäLÉ%íÓ1LLæÎ¿gƈ* ¸?" VŠrµòÕi„s{ ã®Õ#Äîxמõî¬j"Yq™g‡s?dÑ£ÉýžÜQÒ1£KW«ÿJ×Ó¼ïæ$F.ídÂÝG‰ÜI…È'Ìá.sþëOßVI­õ}æ:‘x{–J¾[kGÚû>Œü£Ú\Ðñäl•\›WB˜áŒ“\7Q‡V­D²¤Õ€Å[#¸×w9!]Žq’õª*è6ðF¦×7b´4×VÕõ´nž»s„M£O äæ¹dÛº@äjä?lCÏk¡Œ¼Õ5!ïb¸ÃH˜¹h£@Þª Ãɲ~"Y£ƒC¹µ¾+Ø·ñ‘@Æø)xo®ÈÈø1f4-’ÌÕÊXZ‡á3rô ÏkfáÖýiÄ«À‘¬iLÃÂï ¸çK:1dÆ%¬7ÌõvÚ]’‘Zý»ëŒx:*Ž‘û 1ßk÷úx׋?Å(w7 ÏÉ"©µþä”?º€‘ã>ëÑo[(÷7Žê×ÉûJÉæþäȪ¾do/¡ÁfÔË•,’G›¦ceï¬9 ïwÄ0rÁ\3> Iæž*,áeµ\é5T‡©!-Drõ<'žï‰ÈWílh¥ŸÆH­ü3Ôt´}ÄÈÙžTû¾€{º )µçˆd) Ư ¸…T'æõNòdoË—fd…®ÒŒËTòd]=ªçî'’Zùz4“0HˆSɧ®½ùü;ùm˜÷šÎÉR×l(áÂZ°îôFz¼–qãW]®Vÿ£S øÑc#ÛÜ×£Ô»±Ü«ÓÓQÑ1_$žw@ÞX—«µ~j¬„<Ïv©äèѼ¢$ü£:ØõmjWÏ(ñ·$ÜòûÀ z›ÿÈ™"ùBp gP[nD9=Rîôfä× {»ëUòL=3†Lœ/’sR3`ŒÃÝwL‡ÔŒÎŒÜù\ÂæOßR+¿eœCË%3òîY#öÅsvÊÀó¼±"ù¨¶äæ©aC³ÇÓ9üuÞæXÈÝž–ɘ#’ã]}g囨Zù¸Žk9Æäû726ýäÚ®:j­#’ë9ñêÃÙ³¯„¡/Öq+¾2¢è—x‘Ô꟧Ÿ¹…AŒ¼sI‡èÊýºÛ‰K+ú;É­hÓ†‘šŒ4=öÆÉ-Xþs÷7Žêñ©2lÊŠäŒÚ:xñ绩 íæOi7:QºCQîµ6ªÜNâ.ÎСaí@‘Ì®SÐ΃;ÐuŸas1•4wO‡g•d‘\jpâëÚ©•?´‡öÓ“iÿ®C¾ÐžÜ­èÑÊ)^Šù*p’¥2 °Z#Ù6RFÌ›rÜŠ‘˜{:šÛ<»oªöâjåëW\‡n ‰dÎåz¤>νîÖfˆ?¹zª••,ñˆråF‹äz׺Q çû‘Zý“éP¤#m/e˜ŽÕåÖÚhÁâ„ÜÜëô8³`WkýJ}íè½g°Hö™ìÄ–óKòU÷«Q.=ÚNï%’îõø•NrôUI÷úþ_&”?‘$’îõ8|¿#Ýëw—dÀ§j4#ÝëZùw_•1|­#Ýë·*Xz?#ÝëßòHxuú˜“t¯¯"áuÙ­™Ù|•45F$ÝëK(è~¥#Ýë Ø±âòHFf¶îy26FWd¤{}aC ù½&«df×÷¨èľ\çT2³÷ÿÇœôïü7Ù, ÍÏSÉ;m%Œóþå$‹´Ö!úi}‘llÀŸ¥Æq×¹N~{Öó'ÃÔ6äad¥Ú&ô“c¹Ò"*qY7 5_UHÕŒ­}扤Vþùl¨öd(#»ž~=frû‘0¤ÅhÜðXFdXU‘œdCç•c¸§+(;:'wÞee⯨ä$ ¦$'1R+ßÕ(BZ Œ”:ËÈ™‹[b³k„'ÎÉ×HÑúIÜ¿Z)¸}´07ጂzËo¨¤Vï«F,<ÍÈ0bu|$÷‹ë+xi Þ GѪD®Æú;¥cy.#ã/ðIœÊÕº?£ºÞOB…×MHßÞn¯%ãš°‰"Yâ˜7OÎãF~ÌÀð1SY·}z5MâÎu­kŽÛÞ„¼M‡~'jˆäØã lîä÷;®÷`¤VþQ¯ÓqtU#Ý-X0<‰ë½Í€‚Æ0‘Ì^) _ò&qG¸Ö¹54»¹h¦‚…Ñ9™3¯ŒËÁÏTRê¦CÀã"©•¯ý< «—.SÉ•M$|ÌWÉIŽ}žŽ>© "™4ʆĮ¡ÜÍ+Òñör"#<Сdh{®Vÿ=; hþ2Œ‘ñ% èñj$w~Œ9$‰d¨§k?¬WóßÏCÆ•ð§*Ya¬kä=Æ äoU¯$ÓFôÏ"ãM™‡Üѳ2ãY¤HVúÛŽ'÷;pSèáé9„‘æK>L9¥’þ³LXR'^$+>²"ϵhî’ò®Ù„QŒŒZ-¡åÄ-©•ÿ…ÙŒº-ç12K~ –[~·eĈdÐU;l¦öÜ)6DåÃÈc³-hØ8‰{µ¿OƉdT¸kVX µò}.aY¯þY&— !ð ·þdª,i ’_¯+8ýà¢@>r½m–GÖà†4b¸nºHjõ³P«û1²Q=Jþ äþHtbʼÎþdÔª­ÍH­õ'@2㺋d‡Aiðè™Äý£±HF@Õâ"Ùª…µçÔç‹SPô@6®)ĉ · d—þ6loÍ›¡Ãä¹þ"é[EAŸÐ’Üm Nüz3ÇInúž†€EI"9ÑÏõÏ»,H­üƒÖ0ªr#×·qýJêõà.}¤À2óoÜëÚ¨„òv’ãÚ±2ÿLFz¾‘1îf5nýœ¡Ü-Y ¸’-˜«•/oIâóÔÉ=‰z |Û—[,Ɖ*÷öø“«º;ѵj¸JN,dÃsû$‘|>Ëõ9ûÔŸÔêoX¦CŸú`dt5žÔæ®ë’Žò\n…öø\ËÕœŸtûÉEp–± äoÕÎEõxÒI$÷þÔC½2‚»{¼Iój«äËdÙ Y©O6!ej¼H–²¢÷8îç¹øö«ÇÈM®õ“• Ü'ÓlR)„Û²‚O:sµò?¯¬Ãõ]>ŒÔ 2âËeãlVp6ø“Jú÷³cðÉ F %d/§’)%Ì>¶_ ô“p&ª·ï1òE$ˆ¤V¾§­Œ(u%R$ ûšqðU"·lÖ`¤ï:;–ê¿؎”Rݸ›0j|W«ï½2B†•gäÁ`YåãžK‘`Ì¿N%×å“Ñ ÏRkýkeœ¾Ò ’Ù`ÁÈß8ªÈfÄå'á"™¥” ^ b¹WoØÑ´u{F.Y˜ŽÏ#¸#;°}Lc‘|êDµÊ òÙ<#–™f3òy˜Œg• r?ñ(ò…@NjšŽKW护Vþk%\µnUÉCm$| ÿà$ï”±¢Ý³xF®˜f†—u.÷|=¦½ì&’ß ›1uv"7ÒâÀëÞÜéI XˬŒÔÕ&lžš$’w+æö‹ç–ï“oY¢iÜm|{7ß1 Þ{ªäz›Œ•kËŠ¤Vÿî%dôºüF%§ž•аæqn™éF4ß%’ëÖYQ`v,Wk}6Ãõ6 IbdH=‚·÷äþ£ê~=ö7£¿Ç\‘t¯?¸nÁqýÊ@îÀñÜÝœSÅà$Wup Ï·ºŒìrNAÒn§@núlÑ¡}©•¿÷Fö‡WUÒ÷Ší)ÍÈVœË9œûò/A¬(×;Z‡‚ÞõErÂK#îÏâÛ/aê±U¹Ö`F|Á‘ÔÊW.ÁŒg†xFVÝdDDžîÈÎÚ,wÞʸ:­¬HfÌ”_%'UÑãÂö"©Õÿ´ë¸yÁ3‘Ã]_‰-ã¸w;[±˜Eÿc_#rÔŸÆÕZ¿bŠ‚‡ï dÖêN|VI­û31ªã¾¹^,—÷«äëmBËFpmÊø”|S ïI:,?ßX$wû»Nªýsq79QŸUv’ÕŽ¤£õ§XF.J2b܃éÜšwôhžÔ;þ„‰/ÓR3ÿy;ZîhÎÈZklð=:„[&VB£2¨d1EBµ­‹r\Ñt4/ž ’ žÛ°$¦/7)‹‚Šr÷ÿrÀ?¶(#µòÍz¤ÇñòýY~ŠÚÕå¾Þ§‡¼©·H./`BÉγ¹e¶¤ãÖ¬8îá "¶]H­þ['šPÀÍÈð F̘Éõ’QìÀu•üy\B*Û$Zë·cGT®NŒ,1É‚V•¹¿qTON‘°òÝ9'y?DÂØ^•üÉv§ H;N$™1¶y<·çö$”ddë  Hb8·ÙJEMy¸?\}^¹àO–neAhÇD‘L÷tâLÙxÔÊÑ0»sÏ`äuK:~´ŠãÐaþþZ"™ñ‡+¾Lâ–Xèú•½/?z8qøE¼“ôÑ›!æK`ä×}n†­RI­|úɸoý¤’% ‡Es¿­OCÁ®½ñÿy°gÅp—–U ÿŸ‘Ó,øR6‘«Õÿ/aY7…‘Áö” á 7`E…Q"Y¹W:&„Äÿ£Æú†æ¬?1Œ‘ÛžIlïPÉß8ªC’$\ÓD -§%Œ7må6-‘Ž„| "ÙÉè:NeŸÄ3£SF®ÐÔܕûv©¾gˆd×*f4Š‹ãN •=G3²„¯OO¶çjåXÑ‚Žƒɒ̸ªÄs,IÃý D‘¬U(-rErûTÐxk!FþÊgC‡¿B¸çƒôHÙ(’ÅGfàHïi\­|ÓÇJøûH…”7KXVv²@~_bƒ×ð¡"é1ÛG/kpït×Á§kMF>vH8=s±@jõ¯WÅ€›­†0òÝz=>WîÉ=1Ã÷{þÉ®·á¿¦NRkýœ®ã^ª²×=¤öýEò7ŽêJÓ#,TÆLVL$?Þ±#ä#¸ùÃdûZ ^•°|›N%'m—0Æ'R G Ì@…‹ÓDrw3ÝZæä>rõ}qå‘@n¬’Žà—ñ"©•uVÞˆbdÀ«ŽáŽøaGÐdA$ÿõwÔgûÓ&cs[0jt"#W1`v—‰ÜõéùÅi#÷ŽÈ€oÈ4Fjå³ÈÿZ {Ö¡b‘¬qLÁƒk{òIO'îú‡ú“²™pûD”H¶rØP²Ñ ®Vÿ •õ¸z¤-#ût(èǽûÝŽqwÆj Ú¾Nàj­_ÍÂú£E²{I;zLìÅý£j˜®CÕ7uEÒ³­G_tähåÄêºÅòG}'¦íj§’;Ÿëð2¼¹Hîýe³¼qÜ*œØ1ë§“ÌYÌ+ü™ØÂ‰²%Ê dûéhÓq*#µòÛ:èQ­g#Gøê0eˆ7Üuœ›yJr’yº(9“•‘GnÈ8½¹$w­kcRçë'©ôÒãýÖ@FŽp¯_%Zù–ŸÔã«ÒG$ãb\ǵiÓ¹eŠ:1`í\•ôôPpüNaFu­Ó¸ñ/?²ÓYü÷g¤Vÿ-uø¸·#KÝù×/-ÅvUÍOûr­•ðæ[°Jj­Ÿ« í§% dUUÁ°‰ÇTòU÷kµ£› I÷ú¤¶ ÿ•‘îõ2Û¬èŸsŽHº×½¦§Ã3g<#Ýë›–é0ìIFº×µò—í+ÃæýI%Ýë7Ù0ÃÒ›‘îõO×ÛäÛYt¯otô»Núdfó iƺÞñ"é^¯ÔÕoKFº×‡T2!Oá9ŒÌlÿRI2|®çb¤{½t¤ŒYIÙE2³ëgknƒ÷§QŒÌìýÿqµ«õïü7«Ä14f#=æPóÓPî’f¤ìå.ïoDÿæ¹¥Kèp«^)îÕNŽt’Ñ>zÀÜ\$•cfD%ÅrßÉ@Áñ£¹ÚK 7›qµòß>©àhÌ:”*8Q´ðkÒç»÷¯y0rÝV~öèËžfD£_S¸âdf®®Îp߆b!=D2臂³?ª¤V¾ £ípžiÃÈú—\ÇÍêc¸éKõè;[û¡ ß–…¸•78ÕQF$ÏìRðásªJjõÏ_:—›Å22-‚½ã¸CXà÷"V$[¿·c¦¾Wk}“kã·35^ =Ìz4}ÞY$µîÏĨžß¤Çƒ¡)Ñaw¥†Ü¼®«·¥9wùG?|UÉÞ.]þ*±,,’Ïd`Oünj_e# d­0Uԧܼ^éèY)V$µò뽜â»ÆInz  áÑD•L˜‘ŽšbéŸÇŒË£¹‡™„‘í:¨dßÝ\/!{Ó‘Þ*š‘oúëpõÃ\­| YaÊ>‡‘3½-x¹6Žû¯?m1¥I’J6_%áÒñÆþä^V¼ˆšÅÈb÷ô|¯W«ÿôkiÈfŽcd;1 £:ücÞšNÔþZØŸìÐÕ߽©µþw×q5Ïê8‘¼¿Ì†Bkûqã¨úWÐÁ¶¥4#Ù<âñ*y)CBʨFÜú ÙוÈž¼Ø'’éÁ6¼IÄýœ×‰“‚JöÞbǶÒÍY¥¸ëé·µ¢@6HÌÀÊnã©9 øÝWÉ£Kð‹.ÇÈrOõKîÉ]½]Æ•ÓY¹-ŠºNª£šŠdöÅ&Ü9Å-6CÆõ´—ù©œ[âDR+_Çfd”adÏñF (Œ›«ä¿þª›@fíªÃ¼UDò¯Æ%9š¯]µÍýDR«Î fxVe䲬fXZDsŸôHGÄŽn…Höá\­õo:¼tÙø‚Ë·¨äoÕË%d¤>ÊPɵ‡%tHÎMtýŠ.>{)ré1ú‘ ’ Vpaý=¼Tщ´ðN2_Þt¬ËÈ_YŒ¨¼v,7ꦩçqã7ɘ£Ï"’Zùw,´ãëÄŒÔÛ/ë`nÜF ç³r’¯sÈX_YÈ·kÒñäH´Hí¨•Ò‘[u¦‚bºséWƉãë0R+ßâczÜKêÆÈÐP‚“½¸ŸªóM_‘Ì=΄”Y³¸ôV´»<ƒÛþŠ‚¡ï–¤VÿK£LxÑa#ugŒè´u÷é/×ñ°ïv•¬"Êø(ŸH­õ'[l¨™£# ÛÓ*Åqã¨NX/¡ø£“,áÚcnyÜÏŸÜåmDrÇq"¹ßdƲα\i¼]tUY脳ÆNç¾™(cØú§*9Ê*¡Óú™””ŽÔ_Ñ"Y¨²¾_ñ'5÷JŸ­˜Ðl#Ó¢Òq­Z 7ô€ÙzûŠdŽ#n´™ÌmºJÁ#Ç~TË;q³Ìg'¹n¡ 'öF12Ñ$aX‡?TR+߯‘2Šv~ ’ºc‡q÷¾LÃSœH6ž`E3¹Ž‘¼}ý#KÎKù%q\­þOšQvõxFO7àE£`n§FF Ï6^$ ~OG>s¸Zë:¡‡ÓÞ‘EîKh8x¡JþÆQÝê”ðtg+œ•UFõù;¸û]/†G;bD²îÙ ÌŽÃý`FD³FVh­ÇŽ­¸ÙŠé1hvS‘ ØaFC¿Xîï üÚ9™‘oWëðrWó«ö1 “qŒœ<ÎŒŠ[c¸‘¯Ò0Ä5dt@®ãÎ28ð k)FVݘaçC¹›p¸ô‘ô­jÃ_©Ã¹Zùº^Üåb4!G_’PhFŒ@np=:gé#’7b˜6 "×ùÞõ{(X”‘ž•e¼?ÈR«ÿ½Z&õcd-ƒ«·tæ–r:PÝé)’õ\ÇÑ5Þ;I­õ#ªº6Fßw dÅ)¼3D$ÿ‡Qu¿†„ÊhÔþ±@º×;=µY‰¤{=ô¡„{á‹UÒ½Ž´ dчˆ¤{Ýû¼kOlÊ'’îu­üoŒ¸Rs:#ÝëuŠ80ÅZW$Ýë×ôi8²=Ž‘îõá®§ûœCdfó5O±§Òt¯¯±*8h4 äÿoÝ&Tm0Gäf²ÿ‡zdIiÁH÷zþ;¾kÅÈÌ®!Ö‚Ñq"™Ùûÿã*šýßùoVö6`W¶žŒüØM㣦ܗçíÍýc¬Ë/–çV{-áì¡Áy-¯~5ŠdT¿tÜx:›vÈŽ®Í|¹7$Ì=^[%#áñÇh‘ÔÊ¿5›[¥&dÿ‡ òw¨¥’ eÀ;j#v¥áRÉnbŒŒ¯ï¯©ä÷ýb‡ù“%¶ÙÑLjÌHÿÒ&œJœÂÕÊ×ÿ±­ãÂÙ¥D:èæp¹6F#;UÉ-Ç]o·ôÛNòJ¼‡Œ¼8Ò Û½Ù\­þÙÿ´À#O #›'§!wÖ4VAÛ dbŠ‚vs%•ÔZ§ SNÉɬxÔaWëþLŒê˜¥:lZ•‘OÞȨý077¨® ß¼’J.Ù#áûªª ™vÏÏ.3E2µ§3*Mçú¾QRÒC WçRÐoÜg•¬¬Øù­HZ³¹F¬ÑW•ÔÊ¿/Fσt•ìxͬé9Ù2̈üB¸+vé`ÙîÍíùAFçU"YóonõÊÍïÚSV‰Js’~— H¼:T$µòmöMÃÞÔhFö3á}P8÷Âc ¦uÈ«eôzõÛØzª#}¯ÚPèI'®G«.¬TIÿÓ6ïüåO†nKÊ‚1"Ù<1?ææXnÃÍ.=¹Ûo+8#ª¤f¾­<Î?ˆ‘Æêz<íÇ}ÚI<㛊䟌ˆ_>–;ø‡›zFs#_Ù1ÀìÃÕêÌLˆÍÉÈK^&¤VÊ2I‡ó>¹%ÿ’P+rª?©µþµÁ”XWŽq½¬ð½1ƒûGõü ݼJ©äf×Ór¬W…¬QÓ€êezŠäÇÙ&Œ;?Ûy˜‚ ÉGTòÅz2âz0²ü¶_­Ï=ü·„AorªäD‡yŽLÉ¥eðžY«•Ô’ Ä]ÅȃZ‘}ÿ nå§2s‰ä¾&ø ïÅí™äÀñNžÜWïìªóÖŸÌù$9¿G22t¥†ŸU¹Zù&•±pSFÖ’±±è•¬¸Ç û¥9"Ù¿©ëøÚQP¹À0•¬V8Õ¦M`¤VÿfËx{#Ã<ŒxÓ*˜«¸Ž»:7ÉöÍÓPT‰æjnpr™`NÌÈ:Üœ–ŸûGõá) ½¿~÷'ûg—ñùl¬@~+jA÷b1"ù|“wOáÎ`ÁûSьԕ0âF#¹9vËÈ6ô‹@–iDMŸ‘äp`†šŸ‘Éé&DØ"¸Zù/è-¨?+š‘çj¤áüê4ô6Ã{îl‘Ü­¤#`Â,®ïDŸsìUÉçeí8ܱ#ƒÊèYû®@ÉbA–Ò1"©•¯Ç wþÞã$Ÿ“°sV¼¹&Æn£D’m°cQO?nòvסΠF–9(a—Çj'©Õß/Ú€Jýú3²oÖëÊý“ÙPaW‘ÔSðñúÔZÿÌ U—û“CU÷ôÉß8ª…Èø”û@޽$£uP6‘ÌùȆ¹R ÷¢äÀÚ¸BÜE·dè‹ç`äK×Óøò@S2ÇðtärÌÉQ'ì8:µ>·Ï^ !µß(d¿æ&„´˜&’Zù›èL8ÁÈ-Œøøz47ë1b¦vISמ¶ò7|›Çù(F¾O6¡n÷\µ³‚ {ç24›e«ÉH­|!e˜w d›z:ì*X\$§–R°(×kL¼¯àõŽzÜ­z=>k'’S^¦ãéH®VÿE zL½ÖŠ‘IÅõ°ŽnĽàú9”.üM%uV¼7…‘ZëO¨jF¾ÆQ"¹®GX(÷U÷Ëw²ÙºVI÷zUÁΉ‰é^ß^@Ǩ"é^ïöVAÎãÙÒ½ÞtŠ‹k–I÷ºVþŸôHÔ…‘îuù›‚Á“Lþ¤{ýLe=J{ú1Ò½ÎÖ™ðíá Ff6_ó‰z<ÚB$Ýëû_)ÐO* ’îõ?f(øÙÑ)™íÿFÖ¡æ‹êŒt¯wýhÄ“™Ùõ/gUÐsÍWÌìýÿqÙo)ø©Ë•럳9ûs²›Í]¬ kq£Jþ¬å@µ-åÙǤÀßk J^ØaGÄ÷ºŒPúz ò˜ÙŒÞfŠdBN…n>H­ü±®“ðä^"™í˜­å,Ü™ bæïH馂zÙ䑟6¬^Úš‘ÓâÒ±mh>2ülsUrÄ:=B7I­|sÃ\#ºv»JOuà­97#åÝi¸q`6×s€ Qû'qCvÈØÒæª@Æõ5£B±H‘Ôê3ÈŠ¼#'3rÎtô[2;ù•„îú“Ž=&ÜðI­õ×®4¢UÍ1ŒÜà:¾–oùM%ã¨N¼iDþO¡Œt<5 _AÜØ¹iX³x6·LaZOàþ¬C½–µ¸;F>¾h@°2€Ûq´C*–à¦.ÓÁ2«¼HîÙèÚ3oÃéÙû}rŠbBJÏi"©•¯Õ 3b'Ïbdû\®·É‹ñÜ™µd$äš+›\¥=ú¼"Ù¢˜ ïUòà ~-ª!’ZýŸ1ãËÜYŒì&˜Q´K·É+r~šÀ]ZÖÿ—ÿ¨µ~ú’Ã_ äÉ£ ìAž*ùGµÜLÚîWI/óÄp×L8Ã)ªM‡­Ç«ˆäåiÔ/\„z]A³¼£ýÉ‹#­ˆØ=‰‘ âMè2z 78ȈâÝ‚¹ ?I(Ö®ˆ@jåÿ˜jGŸŒºŒ|›lûÕ]¸mŠÈ(²¿J:óÉ“2bœýÌÉð26×í˽|ÛŽò¥þä®…‚§Ÿ©¤V¾-# ÀŒ,›]!n§zTÙÒZ$ï]4ÂoY(·LНGqVæWÎÉÕê¿ÇµÑòNŸÊÈ%M8ªNà~} #¦î•ìèú¹ ï(ZëXlG'¾Œ\[9ïwÌäþ£ê~¥¾•Zô§“t¯?]c€O>"é^öÊ¿¾VI÷ú¥8Z¼(ÃH÷ú†(3‚^EФ{]+xû œÅH÷úª>:è†I÷úW×ñëÌ”,"é^ßÖ‚Þ³f32³ùú¼”!>ú®’îõÃuÓà5J$ÝëÙÛ*¨”¸_%3ÛÿJ’ýÃF3Ò½¾ê½ætÉÌ®¶‘æîÙÙûÿãj·÷ßùoîoíÀ¬ÊÅ9a½eãjqÞLƒßíH‘|ÒÁŠ¿ŽMàžÌ!£XãŽþäÎ^:\~O$Ç?ÖÁ£XUî·W&œöœÂ=,Û1Jñfä&fDùç#¹Zùs¶5àŒ(’;æ1/f8÷Hk~˜<y±¼ëE^ÖK%·å‘±¶«ÞŸœÓTGëÜ"¹0MArÕwN²‹kÏ^u#µò•ì`ÁÖ¤H‘ü–ÇŠj'qŸg·£Í]pK$;pïVNnä<=‚>ø1²}~ß-GüI­þùË8й]FTíØ<©·ÔWÉ.3Í»ÎÕZ?>»Œ9# ú“ë%’§–I­û31ªÅnذòCKF~ÈeÃçbAÜ3l6'P$C;0&Ä“[,Úˆ|ó†s[l¶à3‚›OoEÍÙ!Ü#¨3®××CÆØÅküIým¾®˜,’ZùÙ%ŠLÉŸÍÒ0¦H$·zSYN† äªõ:äˆ.%’ËÞ»U?n­…ÿúƒ‘Üaz¼õjÄÈÇýd <¸T µò™Œè3o€Hî®ìÉu×{¦‚v·¢rÿnµ6”s’OŽ0×§·Hn;bů²c¹Zý7÷´£­­!#G>´áǯÜùÇ2PsY?‘lßZÁ«oÔZ¿¼dBžÔ0‘ ˜mEù㸿qTY… šÌÈA5¬˜Y:ŒÛ¦‚Üû6 d-×}7S½š…æÛpêIG‘ü«Š‚j-þÈÜ®Ï-1E)d±æÜy]Œ‘-Ç:1<¿Hî÷v ÷ü’ŒÔÊ¿Ð`Aþe"\ÜŠ2—¸9pÄ÷çÖ)”†s"¸Û… xÞÎ-îåÀÈn¥¸£—˜±cðtn´àÀ¥8W+ŸØÕã勈$óV¶[ _uRдýN•ÜëëÀð¼%Ytº‚©Ûã²jö¬/ÍH­þ­Ãl¸NŒ¼ö!Ó‡÷æ6î €­’UÒãR&ú2RkýÎ}\'}!‘ü¸GA™‘‹ò7Žj¡铦3Ò§ŠgvGr‹+ˆ¾´\%wqà}½"Œ¦W ˆTòm¶£#½'ZáôÏg1áò¯0nR¸ ÷Làp´é­ ©•?}tT*’­7ÚPÒ£w±`ÅÔ¹ErÚÁöƒ{s«‚”5²Õ 3|ת¤éŽÌë!7ñdê܈d¤V¾Ò;ì¿SY =˜‚mûbœdßy6ä.ÀHç+dÏÍv* ½\}Hß:Xzrµú_˜’sƒ‡0òþK+–TÍI2cQƒéÜ7uø¹ª WkýµPß”`d+×Ïyæà‰Üß8ªu˦áãÎF.-iÆÕŽS¹Þkm¸?¶7,ÌŠÕÆsãV[qj@(÷~¨;ç…s{tø¾ ä¥ò&»"’Zý÷ް"»y<#«_JGÍד¹m]„æEÆ8È,®=ëßÊ"©µ¾e„ ù¾Žg¤¥ÃôöŸ¿qT”5áÑîFªeÄqŸA\? „ó‘Ü&kMÐýÆe’¯×ˆÜóCdˆ]¥’ƒªë0èl6‘¬¢ÑMÉ}ºÆs·²1r®Ÿñ§qµò׬§À2Å)V+x$µáî0¦«dÁ8;fž¯ÃÈ®¯Ôà¸pT;ц›­w:¦D‡‹ä•I ªwNH­|9ôvŒ_èÍÈ|=mÐïÂ]'à«ëÉwO‡Gýÿàö-mÃþ-=EÒ©(Ùk‹?©ÕÿzÛtü};œ‘Û­$ÌŒàz»ŽOkï…‹dˆkϽ¢WkýV®ïð.²)ÎÖ!û°b"ù?Œªû•hÀž³Œt¯/iÀñr]é^?ßXFÙ¿F ¤{½éÄtØîMI÷zµ"2O}ï$ÝëZùÿµ¾>?ª é^»e…ð~4#Ýë/,2öµ>¥’îõÊ ì0&4adfóýÜgÅ× cé^¯´CFÐ÷C*é^O(gǘz"#3Û? ²Ž=‘Œt¯ ®ãÓ¡FÌìú›ƒMøž}‚Hföþÿ¸¾lù÷Gþ›V9-|VÉŠyx˜³$#£<ÌhR,L$?Œ·`á²pnúFo&˜Trdê_[ W÷•ál;ž»õ°S³ "yj–‚ š d–“ÈZ3ˆ‘ZùÃcõˆñóÉuë mÑ…ûIáE›3’ý!{ßWɵãd=9{®­ ÿ)­r¦bG`Ý?D2°°‚‹ÍT•ÔÊ÷å¡£¦‰äú4 N^úÇâ‘8|¹?wT;U®ËÝ—†Ø7áŒ|ùÿ±s—ÁUlÛ»ð‘àîîîÕÍÁOpBˆ;IpÛøÆÝ]z¹¯N7îîî›»;¼}ï¹cž]ë_çvQ‡ªû¦j­/¿ªŒî9Ÿ•Œîž³?D‹Ñy™jóWjëBß»ye²ÿs'Z$”fq®ÇUáH¿+:ô;Åó¤ÚøÉWø;ÏJdXkçªúr¤Úù¿Ðª 9‘'¸žLVZí@w±-sßaÚ6 ãÉwWðßæS/ê,¯È¼Ÿ`ÄN[$3S~3®œÅ<¶ÞŽ•Ýú1ï_ÓaDñ2ùl³€u×,©–ùU åÉlõMØÈ%0S+ X›0;l=O@x•ÍyêŠO2—áÉiG (²hsÝ ªE¥È¤£•ò`ûš‰©–O³aÁ“O¾ØbíÉ+ºPuîcŽü4^D`SžÙåYn¬f&(kÂ:Þ‘<©6û¹N®[C&#«9‘=KSfwe»¶¾Q*O6;é@ö«-™jãg›¥Ã‚Lyræž?OfþÆV­pÐŽ;{ËdÛ6ä.3œ91Ö…Í:/žlª¬‘ô)9k¾{;&ðd²ò \7¦13õŠ ÍG^ãÈü³EØTJ"·*&Ÿb©ê àጎ©–Ï´DÄ÷ÀûédÃú"Îm•ÈO­x5&V&ÿXg†¾H*óíiü¬ù° ƆÏ9Rmþ,m0÷ “ɼ »ÇE1çh´¨þS"/*Ûã–»¼9Rmüü¥­¨\2Y&÷…s-˜ù[õúC#N抓Éo ¸zx8³âCåî·.–™²1ÏS˜ƒæqójÓPR‡aU˜M”5âít ¬¬ ‡¿*Í““¾º Ì<Ì‘j:1ek™TËß}• 'ê~äÈ‘ DT½…yIyP&WhžNú\saøü«Ù¹·šã‰2¹n«Þ»¢™kóë‘Öš'ÏUTt†0Õò­t¡C\6™|ÛÉ|/ê37÷3âó¦f».z´ÔùÜ¥¬ùb’yòI€õ´ ˜jóš¬ÐŠ—É{”…Ú»$æee{[9Ç“›\ÊBªF*Smü³™µøXþD¦6ðÁ7/Gþ­êþY“Ëÿµ}dÒ½^£ª¥{Ž”I÷úƒ6Æh!‘îõ‘’é“bxÒ½¾¯ÓWI÷ºZþ³DHÆ2é^þÒ¡­y™üß/U‡r×Ȥ{ýÖe¡³ÊKCþj¾ ¯í¸(“ÿãüíZ軕I÷ú¨E"ŽˆÓÉ_ç ¼¹™t¯•h;×'uüèŽ:ìû\‹'õü|Îú×!ÿÉl‹ðyU‰'sÕsÁ9"sù r~N”É©=MXU,ŽÉµÖ¡×¦ª<ùpƒ]"#˜^´Xt¼³o”©­G2SWº°ªåS‰ìzÑ„CdR-¿þz|ºØN&Û÷Òáx­Ìèºfª›Ä“åÚ°_g‰4á•3މoVˆd–вaU÷2™Û¦Cjù.UÔaNçŠ2™³¦3*?“Èú:^Þm$“Ûiq'ä“Džlâ@íÎx2|šˆY[¦jHµùóNap@C–T~Þ·È]_²\¢€5ÖX‰,ùBÀ‘™§8Rm|SgÖ€9-»þM¯s¤Úù?Ù§€Ú¡g.$4½Ë‘틊èön“«lÀ²Ü½e²MWš¶¬Î,¼ÆŠÕWbx²sy'>»2—½°"¹eólº‡—en m DîX«G¿3<©–ßX-¶7Ì!“Ë/ xóa¯Dþ¼âÀ^³†'÷¸\8~éGÞ*áÄ[#ž¬WXÄø!;82_²ò´˜ÈÌQÀ„AubxRõ÷;U€½ñ ‰Ôp¸ÚÑédýXï÷È ‹´2Wv'*íi"“·V™0ýTQD¡|'5ä´ .TøñP"Ï÷p`ð5ž\ <èÊ¥H¤Zþîÿ=H"ý•?ñÄä€t²r÷qeš†PAÄ óZ‰Œ'¢ûèûéä Ö.Œ¾œC&_,q¢„®O.þ_—ð®m©zWí. ‡½ G®< `ál“Ó ¸5g+3=]‹s(Ì“mkh1âøS‰äÏ+ß+b7GªÍŸ/\Dpt‰¬ÙIÄÉûi̱Ϝx.çÉiÊyši+DRmüô7>jÆ“‹E<é4‚#c«žS®¶Æu²¤“«âETËì#‘”_qó¯ÞéRnì#º]gŠÑN$”©!“³ÛÉ,eÇÖ˜~̈o&Ì™žÈ¼b·`î¾û3Ÿ¡oI¤Zþ‚þÆüñ^C>/`@Û…yq…aå+ÉäÚv4fvÜbG?{1¹f<þ˜ÄŒù`ÁØ] ÌÖV-²}-ÈTËwo¾u^äæÉýZº\lÄÌÒMæÇÚ2íЬ crר9!ŠÙ/ÚCÛ1ÕæŸ¯\’]g¯•Ȥ—.\ ÜÍ,';‘\¯¬L>êfEþ¿â˜jãïñ±ª…E"õ(ÿ$@&c«>ñq)i¶Dn¿§<+b Ñ¡nBMžœ¥ÜèkNîÃ,œÇä‘É¥sux4¤>3ÿ î—™5M@ï1ù²¡{çTæÉ‚ õÂB™jù§Ó"dþ_)Ñ¢áb<™—·àý«$™¼Ç±¹fs–À¦B˜ËïkñgƒRÌŸd*w#oÌ6Ãð)‰'ÕòÎa@ž%ÌÃ1§×0žLíÄ€õ•˜jù‡%+k¢á~2Y¨–{}˜IsMð~à ;¥G…™õËÚñ}Øæ—¥%0å:"ÅNáÈίµdR-_ÑœNdÊÞP&³8ìx4¸ótgÑ ý%rôŸ.”}ÀœºÕýÕ!<ä-¢àòE©6ÿ¾‚ÊB®G^ž<1À…ª¥¾qdÒzNìÏ“…gYñzWSmü³•¬ðñ‹åÉ•¨l«ËT;ÿwUµCCªZѱQ,O±á=7’¹v¸ˆ|·¾kHW;3U’È6íhZu€LïiFýOñÌ¿9qªdæÂ~6l Îlètaóû³¹:ÔŒäò 2©–ÿ† /ŒÉvç-( Ç1ê´°ÈÃ,4F@žÛ¹}€nö–És’ÚÂù™ÃŽqŠ gVš ¬á”Hµ|§šØ°æ¯0™œ½Â‚*3â™.?'®ªÅ<3ÕŽ¢7‚˜=5.<7z1Z Þt‰gªÍ¿v­ ß»ÞâÈûW\x±›yîãþ(É“M”óì û’jãë*bõ”¶éuÇ…ÏM%‰ü­:ä£ ××ódžÓvاtc~ÛìBJƒë¹9ȉ~Û«ËäÃmzXÇvdv/©Åë%W$²Â#FH2™ÔL‡™=+0Ë4®º\"Í_uXß—'Õò÷šdÆá ™ìÖЄa £˜GF x}¬ G¶KÕbÊ’ÌqÂ÷`ažÔ*kÊÁwÞpdýÊfø§ÅËä½×q„0ÿ6`[–>ÆÈä¸':$„5cªÍÿ FD)~G:Ùdˆ‡á%%rÇv3ÚMKàÉÌŠmÍTßùÓ„û_ãxrÃ;vœéÍü/ZÕýSiˆˆö÷òs¤{=b±€®Å§H¤{=§2îäýãDÒ½¾Œáu'B"Ýë-DÔ çH÷ºZþÅœó6t¯7(bàܲé^ô— A£E‰t¯ÿ-bsO—†üÕ|܉¶Yé^¿ÕI‹¯­_q¤{½Fç ¾ð%u~/å’LM ‘îõ„&"üö§r䯎ßo± ºæ÷8òWÏÿÇ˪\ÃÿuȲÆE=J­éÄ“š­øúôczO2¢s¥Pf°ÅŒªaqÌíC˜sq(ób]+²\ˆb~8) {öY,X¯†­xrL}ÛB8f€üòÖ2©–EMÙÚGJäÅÊÓ¡ÌfSeûÐjÓfZ&ö%ç‘É»çœhU©OÎ.®%Gî 7âCåa2yaœ€çKêI¤Z¾¡"I}ÉF-E½ÐB"wOuàqý<¹à‡}çaŽô5cÓŸ±2iÖbผLµù¬q Ú áÉÂYpm¯ÇTÊŒµbe²ÀvæÞ©ÍT‰¥f•ÉÃÁÎןäKªÿ»ªÚ¡…¦q¦u(OúEšpân$s×C+–.ÁÜôÒŽS1˜-·8°ä…/óç]RF82!ÜŒg™âx²ç;êëº3+¿×£¡O™´' ¸ð²(Gªå7”qaÍ ì2ùNÙÒWe޼oG—–̶ʃ‡¢˜“«ˆ(X%Y"ŸÍqâàq™œŸC‡¶O~eCïC˜jùÆìsáÃØ½9øñóòÉäÿú¿²[›®åÈõCE$Žn¥!“¶ xSw GÖ½fÀâyRmþkCœ¨y¤Oø³t ¦+àŽ·—D6ªE㽯9RmüâéZtÉ››'ùFFdÛ1˜ù[µy_3r݈åÉ.þœ¬ÇL\íD½™¥™ó¸ ÿ¾“#Ï+;ËKsHä—ËNDu+,“o–¸P»ÐUŽœÒW¹ûU½žNö(mÂ'}8O.xíÄ¢Üù™jùo÷p pÖÖ2ù ‰oæõaz˜ æd†oÐclP{f®£6$ fžˆ4ca¶8fçN"V.Ç‘½6:ðF£‘Iµ|#n9à¸ØH&#þ´c‰ÌÒÊÓÄ«È`‰æÂ‘b/™[Ù±õiožÙXÄÛ>}8RmþˆÜʃْ‹'‡º0šÿÈ‘A%øÜ7„'O|·bâ‘0¦Úø/–Ya­Á“Yܘмó7¶êéÆVd³Dñda:¬ÎÜÚOD…/g4ä·Ö"ÖÕj,‘«.ÚP½t°L*hÆ…|±Ì›°tôfnÉmÊ®aÌr]]ðø,‘í2¡ˆ3Z&ÕòOÈdC c˜LÞO· Ãó¦a…Šy1;O`Ó7—È#U 8s¤»LÎߪŻ Ù™Kê¡6˜YOÙ)¿ü^@"Õò}-hCÏBa2imA˜-–™·¸ÝÃë0?ô³£Aû^L¯+N¼íR˜ÉW·`n×8¦ÚüÝ•5ä²ÛW9òÁ Z&JÌvßà³ååÉ6ÃDt­T%T?g7òâÈbZfv<%‘¿±U'~³aý‡þ<¹å´^wfÎæBÃ)w$òïÆN$¯«!“³'éá3« ³Ia-šM<.‘ïE#B‡†ÉdÁr:ÌïÍܸDÀýmay)B‡i~<©–ÿTŒƒóÄÉdùª&<ÉÁÌ4Y€9EѦUZLåÅ“••ßÊ—™%RÜ# )y9GÖ÷Õã­Ìóä>‹ ÅžbªåëtÓW›™|`Dà™!ÌR½­èö8’Y¾±«ÆÇ2?µÕáéçrL“r‰ÌÙ\ˆ#Õæ¿ùÍ…`ŸÕù¹’ˆ¹R™ñ˜֮ŒL¦[Ñ2=’©6~²ÅŽ2•ºÉd3¶f‰cþÆVm¹È{9žìÐÖ‰GB5æšH;Z• ’É =­øë{$3û0!ËhÈœ´JÍÆ“í#\íx3\WK‹²K¯pdÂ;––ìÊ“e»‹È“˜O"Õò O ðú6P&ÏÔ£ààŽÌÈÍz ^Úž'µ]Mð«É?K‡“¾5˜…bŒ84Œy/PÉQ1GæŽs`nƒ–2©–ÏûªŸw’ÉïKtÈ$ÔdV,oÄ)„Y²}¶`>Ëf@±¡]y2>Ô†‡2ÕæÏ¥<ýjmnÊ‘z‹Øb¤!]“ ïö–ÉðbZorR"ÕÆ÷ Ðãidòþ.ÚbË$ò¿hU÷×eMø½ Oº×7(;µceÒ½>z´–¨Þ<é^ïWÉ€3»ó¤{ý…Ý É7\&ÝëjùûìÑáÀ¾z2é^¿®,löš£xÒ½þþœ¶ù1<é^¯÷Æ€CƒeòWóMZ Å”)YdÒ½>ä¨Uä¼2é^~åB«Íù«óU¶g¹²´×îõˆ?ôêÅ‘¿:þ-â·OæÈ_=ÿ/«ÚÝ5ÿïC¯¯ÇœýÓ‚šõc˜ó8Ð=Šgª?¸³ ï7|åÈSCD´é½ZCþÆVõñwáß9rÒFæüqÙ8J@{) óá ü Ì¿(Yµd:¹ïŠû-GÙ¢ƒ:2¹ñ˜€^%rÖw™îe~Sî÷¹g©–¿[Q–Œ+Å“Î#:ÌhÓ€¹>H‹ÄÏ9{tXÛ¡>O^, ix¤DÞ SZøã5 -àéìYvi«Úó¤Z¾éDlé^#Ë+?çl®æd“^\ªßš'§-tB^âÃàBDÎo)‰hÉ)‘jó tbA·ª<™o¥›Æ•a¶ùË…#±&ŽüÑSDäÈü©6þÌ«.TøË.‘OÎ8ðêb™ü­ôÜ…Þ-vpä­ª"ŠéR™‹ŒZ\¿œ“'Å+:L-ÖˆY$E‡„.Õ˜Ü;¾= fb„€¯moiÈøyZl¹˜…'Ëß7¡í±æm?'|fVgªåo³Du{Ûñd­Q\Ú›¹}‹¦ôc^¾l¶ù1Ì>õ´XRñGF\ÐÁgECžì½ØŠ›#˜šXÆŸ{Æ‘jù¢ºŠ8ú²ŒDÖøáB©…k˜Â4ÖUø‹#ƒ«ˆ˜ž6†Yê…·V4”Éz,7Ç1U×’ÊÓñ@ÃÂ<9ÍÛ…G£r0t¢ëßÅd2t³ iù3ÕÆ/á²b̼p™¼êkB¯ÏÌߨª‹”_áÒ1e8²`¨ˆúuÆhȪ ¸=7'O?6búø‘Ì͸q=Žù㦠K‹3³d7@s¥+s}3 ÞqÌW=DxÝ/$‘¥cíxÔ#H&Õò·aDÓÄaòWÏÿÇ˪cþuÈòÇ-:, “O‚µ¿HäQƒ'/ޔɡU•ë“@æÃ®g«ÂütD@LŠA"ë õ´*G®ÑˆØÖ*U";ôÒâg™É­6¡ÇÉXžTË_ÿ½oòæÉÐq.L¬üŠ#LjXòóª† 1§T ‰Ü\X‹Ë¡W82×þ «Ë“9oÛ°zYˆL¾ó6"{ìP¦Z¾e;5]÷A"õ²Ù³”–ÉY)X{ôãÉ•Œø< ’ùGæ·‹’ÉvŸµ˜W¬Smþeãµp>ÍÊ“µZL÷ÏÏb]:¹J¹;n>^Ê—ôé(`ÁM ™{¦€ýGsdµ?ÍÈþ#'+ µ!óÖáÌœ¾:¤ÅT”ÉGk¬©2_"}{jqðô{¦S@XÈFŽTË%bÏÛçéäˆØ›/^"/-1#*W¢L.I0botÓû¥»òáÉ«>"ž¤/æHNr¡äòs̽…D,¾¸F"ÕòÕ™jÁúà™ÜÛÕŒäéÿve -7×àI]wFEefæõwau¬ÌãI"¦®_•NªÍ¿9ŸÏïr9±t!™ü­ºD¹šnYjÈû‘þôã8²o%-l+ï0WÔÁgmžô½á@©3uƒ ­&ßæÈ‹}THûª!Çiñ´qfž¬ÚÈëòaLíR;º1ÕòÏyí‚e…U"[+;Û“»ž2ÛLÖ£íçö2™ò\‹E(Ál?ZD›ÉWDòþgžû$òJ[%‡­ŸLNŸjÄÚÚ#˜jùrû˜Pk^”Lîû`ÀÈUƒ™½Düñn+G–V¶Oúæ`.žå:T”ÉEÝ­8z%†©6ÿƒº£x2·—÷ãz0+L°cÞÓ^͕̇…R®ìLµñß%Ù³²·LÖncAðãæolÕÀ¥:æžÉ‘“ϰX™1íôØ>Â'?X èz sY¼óÈݲ`=I˦Iä‡/Gðä¹]&¬ÇÜÚGD}sŽ :îDå%eR-ÓÌ.<ÉVP&WqÂ÷|5f—‹ÊurH¤©»€ç.9ô×:!¬ð‘ÉH›Åëöd–×*wÿÛÙßIó9R-ßÀ{zÌùÜU&O>ÖawéæÌR£DL Ÿ•N6¨+¢J¿é¹t¢mþê'“úBZ¼xxY"Õæ—0}fžÍ“îõ {N|ýTT&ÝëU¿Ya³…ó¤{½c Ê–t¯«åÏ®uàë=^&Ýë)J-jÆ‘îõ°®V”#“îõk\Û›'5_˜òôɵ´ŒLº×,ra÷‰t¯»”܃¸æù«ó)lÄÉCxÒ½þm‡ïð2ù«ã[”K6Ï‘ùù«çÿãeÕß ÿuÈrý< ¢ùd™uÛŒ‹F1?)k¤ Ërò¤#·ûc+G¾g„p.š']6 VNb.ÿË »9Z&×MnžÈl$"aÂ(‰ô¶cíÔ2©–ÿ^^–ëË“/ ñ²äpfþ.Ê̬!‘—5®m9«!O(k¾úãjqdwåW:ñy ‰¬”lÃäÏ#xr¹ìBüÀÛ©–ïY_®6 çÉeñvœjØŸ9$ÚˆØá2yþ»ç⃘³8±{J3æÑ5L,œÌT›[=Ûow’¯˜ôµ…†<\€ËÝzˤW¬kò3ÕÆoj°£U¡Þ<¹ÛÇ…È™jçÿ㮪vh·zfl˜,“K'šPß7¹;MD×ëe82Ë|ñ%{¥“5Âíø¹«?OÞäD>®s»YK »Êä„h-N—ÎÎ ÊmDîs¡Ìu›tJÝ*‘jù«}6ÂùG O>lÂãVIÌc{|˜*räÄ ZÔ(]’'Ÿ]øsô}‰\X݉·eÉdŽÖÊöeól‰ì*Û1'/™TË×/É*ïÛñä—þNôüP›YÖ¥ÃÃMeòÝ-n-ÄÌóJ*ÑÌÇÄì–Hµùù Xbß«!G¶PüS!Žl©ü|c£}édYÀÜ5ŽT߬lgý³úqä„A"Jn"‘¿±UMŒèÜ-Z&Ö6bôø0fZgqc'K$oqAü7S[BDxŒ–#íʸϦôÉ]þ–Ï+%‘A‰ÊÎþÕŽÜ¢l×ú}ÎÜÙÈ„«íãxR-ÿÌ[fœl4Š'ë·`{ï$柃õÜèϬÙÞˆå[G0S Ø¡­;X&g­µ nÁdæ†R&Ô™ËÔ*ÛA£3;S-ßër.d;S€''ïráeÄ-Ž4ÞðìÙ1‰¼>HÙÎvïÍt p¥êpŽl¬¬q_|äxRmþ½üt´æÈ™Q¼>%0ë]Ó¡R°†'W)ß3û´X¦Úøéïxy»¸L®L´£|¡þÌߨª×fà¸,“s_êqxX 3舞ú0cW8`‹oÍì®ì\ƒ_:%Ò0Á‰:õ«Ë¤å‘Û”æÉ2# p­À\æ@¦æþÌ<³D”“®!Õò?`ÅS ONÑÛp¹ïpæê3*k“™¦XQaE³‘ò'y29V&óÔ£vùî̖ݘMõ8òK 3ǵçIµ|·ŠhwkGr£ElíSšyYy0ûu(’ëÀqäþf¼XŸÌ“陫SmþÓ|î²€#_nð}ïvfên+r^ŠáÉ Ÿ’š3ÕÆoÐÙ‚g—’e2¹%jgþÆVí?Po?™Œ:§Ãâ?|™CÙq¿ø æÔ­V/eÖõµÃµ3˜8Á‚#“™·&˜±ûA2ONÝaÃÂYÙmÆ;1}pu™<¶Å Ÿ£˜jù¯Ì¶cEb_žÜ2À¶;0ëM³#êb_æžVNØ›×g†ý¥Å’NÞ2Ùy‹ÿ Û$ràe >´KâÉÛç•…A2Lµ|w×?ìÒœl>VDƒoE%ÒtD@Ÿ.»8²Ü,-ŒÊÓKòŠë´pdj7e!6j‚DªÍ_︀¿ìæH¯wŽ^b>Ê/bJ33óö4oÛ]M'ÕÆ¿ÜW‡·EjÉdÛõNúl–Èߨª·{ê0¬KM™tÝÒb¿³ó½—SjŒbb²qñÌZ&#šgŠbn¬­G­£­™Ú>N R‡'û¡énàÈEïµ8àë#“½”…B¥Ö%9Ru­TÊ ¿ëy2Ï~'ÚÚË2æ»°ââ[ެ7@D休̷ÊÝ,}öL‘ôš' ^ð2Ž|ø§ˆ\Åæ¤“ ‹8‘¸²‰Lªå;ÙTÄ—“‹%²ãžÌºÎ<6O¹%D&ÝëÉZ|ÚøC"Ýëëæ‰8qkh:é^ï´K£K»ñ¤{]-Ñ^.”öÎΓîõf DØR+¦“îu¸´8>¡Oº×Wí6c[ÍQ2ù«ù¢«¹ðmb~™üu_ePxOº×‡,ÖcdµÎ2ù«ó?™¡E;äáI÷ú‚$W%“¿:þeð|­?OþêùÿxYµÐü¯Cþ“Ÿ{Ú0t\4OÞaCJõ0æÀâN̈Ð0sWr!ô@1æée.ôô“#+ͱhQ+æMvXêÉi{,ؘ–ÊRöíºÈäמ /‘jùº t¡˜)Löœ(W :³¼K‡ù;9拃?”dî«fÁ™»iÌÖôh²4ˆ©6ÿÊ;&D}J‘ÉšSM¨ÕhsVª;1M­h'™©6¾}ž/¶ÕâIAÙ>:;9ò7¶jæµ\½Üž'k–u¢ê,_æ|£r57n¤!¯±ng)‰Lñq"d­¯LV¿nÃúú#˜ i‘/Ë{‰<^NÀ¸ÂoÓÉ€§¶Ô¹'‘û“,ú²Š#ÕòçùÛ×ZÊdÁšÄ•êÁ¼g‚s_2sÁ„-C˜&›€ðʇ8òõg-¾¯Ä“¸$ÀwÎŽlé0 ÑüPžTm•«ÊNúe+™ùB¹ÛœìÅìÿP@þ®Dî(ÀÖwsÈÛœb&Ôp³NEŽT›\uªÿ(“‡OaÈÏ|sÕã¨;ù„)o[0ÕÆ©ôŽTH'ï¼u¡Þ¼ ù[5û'²¼­É“û2»X»4óQ=½õyyº UZf•É--x·79u…Õ«Å2—ô0oÕTŽ ´jQ({)ž\7Ø€˜É!L¯A6ÜËÍTË?dˆ…f‡È¤yŽ â÷&F‡>ýÀ¬—¤ÅÅù™Ñ'ôhÖ¬OÎÍdÂV{3ª” wÆ2C¢]˜’’“©–oësÂ|ÃdR{à ¯ñÌ&%”ý¶#lXF@ÈÊó²t^ÿ]“'ï•6A?=‘©6?—dD!Z&s•1âÛ±pæqYiùƒÇ$rw!ëæÜò%U¥=ê+“Ml(Q+ŠùÍ0Ê÷ÌÍ“OD6{½æÈ±KðÙ[S&C"è<%€Ù¿™k:0· ÖâìæÜÌ¿¿ê‘eg_žqÍ„Ä[)Ìà{.\¬x“#kµñqèF‰TËÙ`EÅN‰2yê˜çS™—§ 8^n‡DÖRZbÖÌéäÊ!˜ÿHãÉz…3kmP:릓w*81§¬¯Lªå«´×‚¨”T™|·ÞŒ”GiÌE#…,äÈ…_´Ÿú„yd“ Ož†ódÑ. ê^ˆ©6ÿðƒ\.“ãã è1~03ý™€Ûwîqd¶Jz\·´çIµñ“†˜±gZšLÊ¡´;Âü­šßKDQñ8GN÷q<ëf`a;ZhBeòÛr+n>Mdê,`å“x‰<î- éçA {ÇŠrž#Uÿ~ÿ¾«ªjMQ„'\9Ø%`ë›ÌºïX›5E&oÍ0Àï]“»àÂ9r±MÙ ~iʬ¼\„ëT‰,1Ð…{;ŠÈd÷tî¿õãɯ­-p´ÏTË?ù– óÆåÉk»ÌHþc<3ú´!5Ûʤ5Y‹]Š3ußv`©ù{:ŸKÙþU©É‘£ïºàÚ÷Z"Ïü´!½V„Lªå[¯Cç˾bžäøù1H&­S­X]*yGÙ®íökÀ“áJ.ÛÊù¾¤ZþÕë,?Ž'ó·"gû4¦i¢€^)»$räÏÐwÉÅ,½W@» ¿82gQÊԨ˓·öñ*0Y&ŸÇiÑzl1¦Z>_ý* åÉþœ¹sÇ1ËË6¤­ˆf.Éé@Sï~ÌļÒ5äHïL:¦ÔâIµù;Ôtb~+Èd¡ûXÙŽ™ätà]©N?ê3;ÖÒ"ŘS&mc¸úJ¹k¤±Y›É¤_{;²4cVYdÂÖÒc˜NëasögqâêöšÌò³•¸vS-R¼ &ðd­v4{0‚yXYHŒ}_CVPúŽ•#Cîëá×k ONËgÂä=)ÌG/v`âÝæ²ÎßZ´Ÿ['ÕòõèhÂÉái<ùs¹Æ3O7p"8¼ómÎÇ—fnÞoÄêaÉÌÒum¨jLdªÍßy½æw–ɲ8}™%ΊxV&ïÔ3ቘÊì¼R‹íÁe˜!~ê×_ ‘š7:Ü0øËdbVÙ"JH¤ZþM¯ì(ó¨?OÆh„fkåéðH_ˆi×é?¾sl°1ÅÆ3oo´¡àÿúßYÿGïQ&T=šµÕŽFÕ3ÕòUI± Ï­q<™<ÇŠÛ§2·+OŸ¸m8²ïéö…ÌR÷œH¾S•'+îRÆëÍ‘jóë²;°¤e?™,ë²ãÔ›AÌË8ñ)ªs§ÝíÃh¦ÚøW‰7 #Ïï‘–-¿DþÆVÝ G¡†}xrv.uÂÜ^XÀì°6¡U8‘)sDté5]"ÕòWØ”íh,OJ+ìxÿs03ò¨ˆ*kok˜gD¤d]”Nž­-â}Öãù|€ÆA=eRmþU#ìÛ8T&»³ãMæpæÙP3>ÏÌ^Ó€1E‡0ÕÆïÄ…Êù¿KdÌ9Ú§ùËäѪ 9‚'Ýë•jq2[nžt¯û¤ 8`%Ò½Þe‚r Ç“îõ×oíàwôçI÷ºZþ;i.Œë”Ÿ'Ýë—Ê[5vOïÊ“îõ²‹ELj0Q"Ýë[jX°røx™üÕùƒ÷Ú°³o´Lº×C–kš_yÚýuüýÕl(û,Q&õü¼¬Òÿü×!ÿÉ‹ím¸Ùo³˜²¦Ÿv±S-_Ú,ÞÙtéWAÄÌ›™×>‰HòÛ®!ÑzŒ”NÎú!¢F—\28›2n³©6¯Î"LUÎsdáÖü»™úeaQVfžTæ¹öæ•Hªßá»ò½Fû§“eúˆhõõ„DþÆVýò—£•»yÑߌ]³ÿmZ5•më82Ò%ày¦÷ÌÙ÷tˆzÔ™'•?±²3%‹ÔbPÉ|Ìûß”í[ïnÌ‚™L8³s,sû;ÚÕÁT}* 6bWõ™Ä2M‹f¾OÕ¢áœrÌ€ñþÜxY"‹¦è¡ÉÙ_&gÖ¢Ut-æ›/;t(V"ï… ¸ÓýGªåû¾Ü…z ÉäÛ·N슪Í|ì1:fšDžl)âe¾kÌÍ%xí5H&?{Y°äî$¦ÚüáÊšÚºAàÈ.ÛDܸ„y®ˆn·.Kdí'êôm)“jãGîvbÉŸdò@¸¦GáÌߨª³õ&|N™(“ŸŠšÞoS?E‹Ú~xòðdßiÍÜ1ÉŒŽû'17µâË‹ÑÌ“Œ¨žÊ¼ÚÍ‚Pç$föP¥rä¨e"¦l”Hµüé—ô "“!ôhoëÉô~²_:Hd”òtÐ+¯!—ý) ÿ…ÛyýÎT}TéãÒãAñž<ºÅ‚É=&1UÀ¸R°­Læ q@Ý—=ϯ-E˜ouN|Ù„9ç¡·ÿÊô½# ëЬLµùM»D¬z2‘#g) Ea}˜5ÖØ°r}’L¾‹Rb–ILÕßÿ V÷™$“ý÷Qr÷hæolÕãÈÔ/U&=3àöûxæ*½×;âÉm_ ر5ù²µ¥\ý™›Ë»ÐþIæþ‚vløÃ<ãÄé-˜K8Ð«Ü ™Ü¾À„" '2Õò—OÓaÙ)?™["/×`ߺ|*òXª¶/;¯D¼¼;H&më‡;§ÁT(@ më=Ž™P³ÿL‰´­ß8jB”ó,‰´­{ZtXZµ‰@ÚÖ•òòÓâó«Ëi[Ÿi½úçíš+¶õ&ßôhhš+¶uéU2< Ê䝿Û9.éê@™´­“‚{{eÒ¶þÖ>?GÈ_ÿ¯orr†´­WNÖ¢Qù7*òWû IZœR¿V‘¿zþ?¾¬Ú×ÒüïCþ›m¦¡ø‚å9§p:r¦°™“µˆ÷D"í{¾ãucÙ}’0òX€@¶h”ŠcƒÙÎÃuXµªžL²ÓâkºD®’­S»‚dÒó»õê7UÅ*å—¬sⲑˆyÙãØ˜ñ&Lïõ@E¶^í—$Îb]7èÐ+ ¹@>ª—ˆ¨1ÃØ1´^0Ñ̦ia>SX •òulŸ ´\.“v“P¦ÅRÖl}Ò¶ÿ1NE:63ãtåùΤ*0©'Gd—Ö&ëÕõ›ŠTš?a¹“ßÌÈg§ôpx¾€Mo¥ÅÃâ9*rÙEþªÛA •ú—ºc@‘êŽé>Ù„'îªH¥óÿqUU:´êštôñÈÐ*ØXt>Ûh—ݵ%Òo½ºwÉO ÒÑlËR¬¸&³æg§­ÓàγÓ&ÒîµåMq*rü {–’Èu-ž¯-(JùW»èQàõL,ðY€w‹ØuÖã¢G8‘م̸i ‘)ù’p|ØbU2uTÁìóæz„|™Á޽“Š'ï‚X¥|SéÑÆl™\·(gÝÆ²Mê›qþù3iyjB?÷0‰Ô[û”YTÒ‰\ÕÙ„5KÞK¤Òü?«&ámè19 aýØ7=†øÍbO¶MAŸÁ¬RÿáuÍÈq}äLâ› «7N–È߸TËnÍÀq3ò™8°uû¢Á1ÿÁ*r@mëvx6›ÖÓˆ—— ä† FžS‘íªë0·]U|ÿ5ÝåAlæ6=BÃ}Ø/OÓðôF«”_4%¡çÐ òOƦÒAlàŽ¼,‘Íc¨ªqIuÝ4Ì$3f`§‡ÛÇÅ€>z²­®šPúÛ©”/Y›¯7}dR“§Có$'vº¯ þ¸,‘y7­¹Þ••Éâ[3ñxð0vó½TÄ届W=CÎ{dt‘dT^È–tLG…2þì²™˜Ù¦?«Ô¿Î'#þ’JÈäÇ–\Ò‹ýK5ߦLx&Îe ¨ûÄ“-mÒÂð´[h¹íCš°/jšñÕ/¿Št­`FiM$ÒyxŠˆ~™ý-·³‡GñV®Ï¶²ær­{ȉTÌïž‚‘ÑÁy×>ånÿm³‰l>è&“o3päà$öƇL4íÕO çáù¼›sÑ„Š6I¤s;¼Šõ’I¥|u=tp?U[&çËZ”ÙY”mWÞˆÛZ³.µ ȩԇ=ùTݱOvèУyKViþÑAɨ¾i¹@ÆÄ'Ãw^{÷½w[—d¯”4ãÍZW©Ôú•tl¼H&7O…þ\0û—j{?*—é*Óݽ#{qAD¸³£Q¤áx6l¬ Ç=”ÈwŸÜJ&çÅ¥£¾½¯@F½ÍDè–~l8# |­&“Kz¤cúœe¬R~Û©È÷1H ½÷¦¡ûõ@¶aßt¤5X&“nkRñin0ë¸Ü„ïƒÏ«È]˜1÷Evó…T”.,“^uõXá9ƒUÊ·µ‡=+_‘È.ÖÏ ûql‰Ò™¸{{¢LN¹“Ž%²KOjpïû|‰t½¬A…ëÁ*RiþõÏɸS-X z¤ ÄÚ¿u,eÆœ¨Îùþƒ{¾”I¥þy¾I Z&“Æ’‰8Ühû—ê87#Þíj,;¶ah[?¦GZü6£b2ÔÙ=3±¬æX™Ìm˜Ži»üÙ]‡¬7†ÕòÑ=„¯V‘'¬Oæ‡+,–Éò7µhX± «˜j:¶×ôHŸFX%ø°ƒŽ'ü?H&›Ù%!¸ìÖ˺Gì8í¼™M4!«ŸA"ß&j18¬LFmÐ  ®Ÿ3©”ogºw_ ¹Þz^Ç#~fòüÖ4,{(“íú§ârf0ûIÖaÇÇ6y§lìJ-a•æ÷ù3§/ ¤ø*åsÿ¶ö«L .Ñ_& ;§cá×e¬Rÿ'5uØ–XU&«¿ÔàcÏMù?,UÛWÖk#º+%¶õê³R°;3X mëË/¥ en°LÚÖí››žͤm=y³UëœIÛºRþû·3P®Ö´­[˜ˆË Çʤm½G¦[V’IÛúOGz~®!¿š/ĺ$ëòu&më ¥À½U°LÚÖïOƒCœõFýu~ÓN©8}8X më7Ú§àë¢`™üÕþͬ×W<«8“¿zþ?¾¬j8ùÿùoæfjàßj‹ŠûPƒ—ï,leç7ôÈèh#òMlÌžÙiÌ vÄó4ø, f [÷¨³û.—É&…­O¾íưÒ5ìî—H7ëãÃòëãdR)ÿøzÌ5_&[Ÿ¬‡Oa“¢Ò1{y {ñV*Ö…±QïR;}…@:|MÃX§`öÑh ´6;“/>hQs~MTÊ7ö›“@ 'n5¢K«†ìøZÙôL"§5¨6o+ë]&UüdrUëä| c•æ¿9­ýë$Yã™Õ}X¿zxº-•I¡vùº³Jý¿NIÅšÆ+²qþ „ßóc•ÎÿÇUUéÐuUµ(”{GEvªÅùG?Ù}¬OΊd¹Û&¼-˜­"{•ÊDû3rÔ]|¿wfOÕ±Þ¸>7Éô»üµH–È5;RÐyô ™ô‘PfÛPV)ì…Œ;:\&œP­C/6°v ªä†±µg$ážz9›ô Þgg d½58yödëüiýüôe?©Òp:5”UÊ7¬‰ Ÿ”È…[M^푊æhÐ9 ºDN¦A鵉Žä¦Å‰X°cŠLŽÑk±Ä¥<«4¿x1-ªô¾¤"ãiq(:—ÝÞÏú{ß÷M"ýfhЮœ«Ôÿº‡Õ®öÈeL¨4ΞýKµáv듽wYüøJ‹²¨Á~S™‘Žx¬P‘GFhP£L/3éºKƒ¸ *²D¶/Nî’È“‡4ï£V‘JùéthüÑY&Ýšèð²i#6ç|"*äŸÍ.ŸK_6ë™Båª¹î¦ Nö©Èi>/ì#ý š±ïÍ.©”¯ó¾šu*rµ“½Æ°‹}4èé܈½`ÑàÒŽ]lqo V·¾g&i“–­"•æ¿è©ÅÉà/*òô-‚ÖÈ%«4˜¿oªŠ”¬ižåRq©Ö4cªG„Š|1ÁŒÓg˜É߸T‡uÓaÿäfù~§ƒ¯8±'‡™!Tzc&ßV5cÀ(‰¼:ÉŒ‡‰kMäâ²f8|Ú$‘[¬#%+•ÈêÖ%²*½/»éD„ÃÙ¥é)8꽂UÊ?Ê E±Ÿö2¹¶¿¥Ú}—Èé-t(x©‘LÎì¤ÅÀkï$Ò4ÄŒØðü*²˜UÏI…$r¢õq«î«vÉYºäu‘I¥|£ÌXúçIgrŸõ}zUMm"ÏvÔ"pižŠ4Z'›~¯*EÊ' ÓÜ=¸ '//g•æ»lÖ"©MlqX‹;ÿ`‡è°Ñm»¶OÊ–_Î*õ¿qÙ„å‡Jä” #Ík*“¿q©Ž*š€&¾nÙdnæ¸õcÆ›0õ͉œ]È„£eR¨eBÞ¥²ì(»ú÷`ÄY÷@o}ò[­Œ|Æ~HÌĵ°±ì˜»&´]¥"•ò§ÿԠ䀳9_Ö IÞÁŽ´^½å—¬¹ªLFg¦a®K›i—„¹ÇX¥|eš1ßÎN"S˜ñyg0{C£Ãփι}s|†b‡ŽLÃÎ!¡l½÷™8Ôk8«4?ú­3îÔÈÖ˜ë£lO¤bRŸìñ:Ùr)«ÔQD&œ'OÉk5Óá¿5ˆýKõÁýôl6R í†&âJ­IìêvFÄ|í “½] ˜ä4€5ÈÀ•qsÙ[»ÒðÊ/„M0¦ágûÜT//=§³;ÌXÓgµDz÷2`~R?™T|¬Z©ïöñ™;źqÈÍÏNŸ«AjHm¹ÎºGÞ:Nfÿõÿ°v=ÞS&7Ü΀]Ã9¬±³ækMYÏéZSR"•ò¹Ý2agD¶D›0#ÿgv·&ËÌÈKgõ¨´sëºÄ„šYŸTd9kß%«9‘Jó§Ð!%¥…@\¡C)×lêNôsgsf›0jà7©ÔÍÒ¼éa}Öø%èáçïÏþKÕöÕâb"ÚW™-¶õdë’úL—IÛzÄð„5[!“¶õŽM(ÝÕQ më3—¤¢tå2i[WÊï6\ƒ>›HÛú¿þþÕë*vi[ú†"}ƒeÒ¶>ÏA Ýë»*òWóõþnDΞ?dÒ¶¾¡x2ò[?¤m}g„ ‘…ó$òWç»gë`Zç"ÿ_>ëÕ>±NYù«ý³ÖYïÆ‰eòWÏÿÇ—U ñÿwÈ3fµ5â䆾Ќn“ØQ>:”tÒ8ÈIÊ> Ô¢QÊìÁË:\Òôdÿ(mÀ–›ãXÇF¤ iÁÖŠ6ÂîK{™\µ=;‡°Jù3wjqǧ†@ú”ÑáìªV¬÷= ®\z¬"§ªµˆ\VS OäeàÜ÷E¬‡Ÿ=ò³?špÙ®‚L둉Á·ç²Jù>›Ñ]×UEvØaFd­3ùîy*zLŽÉÃRPw|$[¶º}ƹ™É{z ùS‘JóÝÆ¦ l³H™Œ­œËåvD ,{ìUdú fêòX¥þƒRp/¤L>½¡ÇØsA¬Òùÿ¸ª*Ú`¨Û[®“Ȱ¶f”A*[u@":ØÏȤIzTyÀJÄ ðyì™Ix}>”­ðÉt¹®"›Æ˜ñáVYvéƒ$å29FÒÁ0¼«”qˆõ#ÔY I@¤£»Lg½ñwc­Û—½Ùç9F\«ÙœÕ1Áþû{™Ü&—ûDÊä¢âzèg/e•ò´ºáÉ5éµÁºÑRR"kmNF·Ü•2é´0 C¿†²­ãt¸Ð¿«@fFëü3Uš_ãl2²‡GÈd^X2ÚK+ÙõóuHöÒ)&\ôa•ú{˜p¿ýx™,öU‹ÖË›³¿q©ž(bFýô³Yå‚ ·¾b#“`&µåd8´`ø¥à¦k${eMŽg¿D›1¢[M‰<ù̯I÷غ£5éÎöó×ÀÙn¯ŠTÊßjmF¬- $âв鬿«íŽû³ö%¡rÇlëi“&XE¾ÙkÆóÀGN䨣ZÜýPO&µÒ ÃÓª©”¯×,3 e–HäÀ6fœªÆ6­Ç÷䙼3(¹=f³CʧâÎÈH¼‘áÕýY¥ùÕ;%£Ç‡p™”>'¡U‰¿­Q=7BÃòÖ™TÔ‹‹`•úW8¨AçKÙ ±³~3“¿q©þ¹Ñ„†Â26Ø„ÕûíÙíoRà?1R Ï4HCÉç+Ù§«20ùç2ö–”Áfë2¦µL^y—‰‘µ'²aå°# Ÿ@Ö’Œ#ª•¬Rþ¼ý‰˜~¡@†NÐÃõrû±o Ú;D²¦G©(ãÁ 0Ã^Yî£ §o^g¿ïÒ@j·_E6.š€>.^©”¯ä]îÌΕÈÎÖÇ©†R2ùeœõqöó0ö@˜ÏÎtf§O5âü(•@&·3cËè©4ÿVJšþ&“1Ó’t8”Í7=>>Ù.Õ€½®ýX¥þo5(Ù¼£ŠìbÝ8ôŸ•ÇþÆ¥ZÞ΄·½jËäbërQŸìñéøxÈ'¦$ª¶‚-<7Uº†²ßždàÝÌÅì 2&œ«-“;.°|iÖíxºŒX!åJ¦£f¹¿UÊ÷ý‡µ{דÉÅsHwqfËçh‘Ó©>ëÚD §…ÙÛÌèXû°™<kÂÒÇ…eRiþ§úIX9=D&ÿ¸§ÇÖ¼ ¶Ï4z$•Èe3ÌxóÉOE*õ¯ÔU‡ÄpGÔVJDHÿiìo\ª?fQTr–ÉgøæÆv_™‰Ë/gd±†ÔÌÚšQ¼Ÿ§ŠŒÞmFò°éfÒ©Yò¿þ7h™¼û*'–°ãâÌ(¢}æLÖ8c›u¯%R)¿ÿÔd˜f®ÈÀê)ȸÁ΄ŽªQl×IFgTl㙈*>W&;íHÇëþ!ìÁnñÈzõͳWV‘Š{ÅK$ôí#“þ ˆê7†­j}<ªþå¨D¤Áðkl¬‡–)“q•ÓÑëZ«4¿Ð=x.—ÉóCõXÀzn7#$+ÃL¾úa¬ÈK©ÔV$¸;„ ä~] :ˆdÿ‡¥jûZa0 ›ÎK&mënŒðuÒ¶îánFŸ ;$Ò¶^ë«­î{ʤmý@J&Öœ*“¶u¥üGÎ¥`£ucBÚÖ¯ 5¡»»½@ÚÖWžN…÷–™´­Ï‹2cq@=‰üÕ|ƒgf¢eÅÙ2ùÿ嫯Áõë·Ì¤mýíŠd³VÊä¯Î©¤G†‡ŸLÚÖÕè•ÙN&µ}ûtüÌ È_=ÿ_V}ý‡ü7‡gÀ¸=X ã¾eàµc ûe”íÝ=dÒÓÏ€~ÉãÙ#%S5,Z ¤aXB;Þzõòhëþ·9&4Œ-Í~÷2 ÿI2Ù¹@Þ\Žb•òk zü°^]È  zdob«4¡þ×R7ÓŒ“¡Ù*²QW3nÝÈÉ“MÈV[&wÏMDô½¥ìé1Z¤ÄÖa•òå~Ó¢}A ·¯Óa¿[_v_“T,~ÍæöJC‡æQlKfÌZÓF"v™Ð,«¢L*Í÷ZiÀé‰ãdò‰õ}þvsë`Ýhø¨Ì~¬¡ÁÝáë%R©ÿ³CÉhz7J&W/Ö#dp(«tþ?®ªJ‡ºOÏÄ®KrñÕL¬žË ÊD¯V‹er†OÆ·a_”ËÀé¤0Üü Ù›æ°zë“tã‰kTäAëÜϵÉÑÞzÌ»"“Èø×ŸkÍ*å/¸0g[úɤýÝ\N™Ã:œ3Cõ *2Ëz^Y÷KF²?Œ0Mè#“ÅÎg¢ZÉyì®GjÔñ¯$‘3½45g©H¥|pnÁ8lÿ<3ÿœËö’—6¬Ÿoö agZoü—£&Ëdߺéh71’Ušß¼²Ÿ«ÍÉV·2á[{.[÷‚õ5Éj½5_gV‘JýoNIÀŸN“dò½I‹9¯[³¿q©^íd@±yS²âJéG³u¥£ÂÊ™,×< s–D±å}Œ0¬ì.Ÿõ&”dÏÙiÆ´ê1yq¿ /»”•Ég¹jèÔ”È7 5¸ºâŠŠTÊ¿¶ovœ “Crt¿n»æ¤Ïkô’HõDë=}˜Ø&=Ÿ„Êd­Ùiè}f5;|•‡ƒ<Ò³VBV²Jù>«ÑÜ=@ ÎÖ£•uOG:øgâÔ’Ål¹Ù¼þ:í°7 ¶GÉä—Á‰ðÿº„Ušÿm{&ú¼X “»Æg¢»¯/;¹ov6ÈSC°eãDV©¿w˜AoïJd ëïñ”PýKõA!#ª¹ È¿FáÒȃõü‘‚Ö¢e²ô™d\yÅv6Cç–¤"=5ãç-É™LaÄæõ2i¶>žµÝ²ˆ#& ÃxÜ¿<¹½£X¥üM{êà\­»Læ\Ö¢o¶@¶ ¸_– ¸eĵ#›¾%ÞD³EO'akõUìÊiptŒÈ”z4Ê7UÊ÷XLÂYÓJœT1“*­f£=ŒXy§[¯¤ eÃZ³¾™ZÔÛZ&Ÿ[÷´Õm–H¥ù­ëg¢÷)™4<Ê€æu ›O­Ç_ëó?nqIÁŸqѬRÿ.­—˜åUäµexž¼ÁþÆ¥Z>Óˆ !O‹˜Ðµ}ÖwN,‰”ÉAzxl e]5£Ù‚†Y®¿ê¦çÙSÓ±ªY„L®™ŠíÏ¢Ùeu2ðÔ#L Q¡KGV)ÿ­éZÜÀW&më1Ù:|š?P&më}Ò ˆi;J&mëJù8iàØZ'‘¶õOURQðe´LÚÖõÑàí}I"mëýôiXc¿Z&5Ÿvp:ZE¤m=3ÏŒüsJ9“¶õ»GS!tŠÈ_ÿ~s:t^)“¶õЧÌšà¡"µÿîÒiˆËŽÈ_=ÿ_Vyu•þ}Èó¨:Ýž­—I8%cî¥ulµ+Ôt…@Þ9”‰ÏíX9D‡·²Ìä÷UjÜñ.®"[j6¡2¹$O‡Ÿ‹&³A‚„Ë!ûÉнf,­vY"•ò;½T£Ê’t)ÔÖÀÕÿ[âRJ=Y+“E¥¢Ð£6ÀbF÷ÄÃ9±‚O:–—ÉgO“pòÆZ¬°= =×®c•ò-pMDª¼\ ½NDF߬—¿{ßÙ9“¥cÔ¨çX_Ev ÊÄámËe266 Yºu¬Òü–L¤Ì Hï÷™˜‘¶”u^¦Æé™*ŸD*å?¡×bÔåN9³’çæ{²J&à\i2Ù©˜|z³^†4ñXÇŽ«šŠ‡õ7°û­—Å:4e<Ë„v™«”Ï}g "ênÈ‹SÑþc û%J‹·uv¿ Ã+a[a…_&^3“ý©1°T´ŠTšÿå­¡ & dN#N¼Í&¸¥ Ê½öѶ4\µnlH¥þÆFĬê+½ž›Ð¬B5ö7.ÕúQcxLZÞ&`hŒ?Û!¿„ú_#UdJk UzWdõ¯“±fVŒ@NߟŠáë5[‹fÕ:²¥nëÐ"Á›mVDƒmªÈ|Î:„?ì/Jù—„éÙf¤@Îÿ¡C©SØœ&Z·.1rßB Öºf-ÖëùàõlN®îóV³±óÒP®üzöLV\Ö²Jùü¥aûñõùíIŽí\Ëvû®Ã°;SX÷3 xs|)»(Ÿqýê²WªXï6éÓY¥ù¾^F4‹&EýØVa [æVîücë_7à‚e«Ô¿ÙI3¤õ’Š,ì,áVûÛÎäo\ªµ×' ÆÃ…2蘀G{g±u­ÇLw"=%óòÌäÑðtÜ(-IŽ™Ó8„-¶!n«ÿ¶DPê\Ãn3éQ×g»îr*OŠa•òßöMÀœùÙìgvt `¾Uc×O£DŽÙ¦†å¡;5†šÁ2¹ºlÜR§±;F&àðóØ×5X“TUÊW|o:ú/ȉ£2=3‚™œˆìñ¡ì£-zdŸd—^Ò£¾ÝjVû8ñ­6°Jó½uFÔ˜î)Ñ—Ðnug›.4Áýu+¶Åf3ƽ­"•ú÷i)ápd ‰ŒJ0#¾ÚyöXª¶¯ wuèß[&më#ÊH[úI¤m½ÅÓ¦©i[ï¹*]«lHÛºå`&¾u HÛºRþ3ÖÇ‹N•CÒ¶ždÝçÖ”M¤m½ä=-ÖTé!“¶õòÕØ´¿¥Dþj¾+v™ðž*¶õš1I¹m@ÚÖ;JǾ+«òWç/ÍgBïÄ.i[ØJ‚e~eù«ýÇMܼ¾Lþêùÿø²ÊyÇŸþ‹ö>2ûL—ÈÖ#%LfÛ§"öIœL~ióŸû¾¶Úìû&ò™¨Æ„ ®*òÇt<»N&#˦ÁûÚf6f]ޝÈ¿¢Ó0wÏ&V)ÿRéRi£LÖX”ÕÓM쥡Ö%Z?‰Ô·P#ùG>©4Ñ=î=Y/ÏÚ&aP¾ ¬é“uc3v£LîÌLAЗ8V©F5ÞŒ¹êLöY¥Fè ©tþ?®ªŠoeg …>k%2¹¶„ ϱƒ[%£ÐÎM29Áú#¾úÖKTÃëö)™;Qƒ7ªä:ûø/Œ“ÉÚµ’°|w [ÀÍ€Ñïr­½±ï±Jù§ÝOÅ¥&q2y´g*>]ÿÛ¸új„ŽYëLÎê«F¥sTäÏCihQb“LÊS1:7Ž]p%ؾ^ O7 ·ÒÉhöa“L.Û„JÛcÙÌ ºÜ{èL6°ž÷iN-Yĺq¹’óEE–Y¥E¹ƒ½Riþ‘™I¸ž/V h“ÐýÙß^]˜„V berü°Dä4^É*õï1Xƒ1äC-òn÷dãRmþÝŒÞ~·%²ÓE3†íþÄÞpÑc•9Z&'öKÄüÇáì² Z¨/uȽÛu(0qûØ=¶†ËäèÝ:ô96‹(áR¦›Š¬ÙOBçëq©”?;=ß>ÇÉä½ê)È þ[µ·Â«ÈâÍ4˜3 ”@ºª“QöÐf™Í ß° ¬Ç5»Z߉Ü\@F×&'Ìä¡‚ 8s X&O^Ó¢Â马۰TŒ˜´S U¥Óa8¿•õÊIC¿”mìÝBîÉ*å_”Ÿµ™ìnŒGŸC›LäÒ.l½.›ÛqT·Œíó-M×î”ÉM»’ñpÄv|D2Æ6ýÛøuÖ=m÷u¬R¾oup+,“ùçêp!Þ—ýkf*þ´S íº¥¡Ké¬öVNÌÙÆÆzeb¤¸–Ušé¥s–2¹»½sW5f«Yïg‹°)?â³A"•ú{¼–Pu £DjVJØ71‡U:ÿWU¥CãNÅc¸gC9èF<úê²¹ ‘£Lz·ÙÁ\Ñ`ÊS7™Œ. A‹5Ø1Ɉ6mÈœŽÜ´’]¬6á½³kïmý‘×>P‘Jù·šâQälGgréþx.r›K›ðtˆ·@î>kÂÂj=Ù¥}“ðÚs‹L¾)¦Çz¿ ì«ü:ø-ŸÅV·×`y÷:¬âRù¤ÅUÏ™2ù3H‹Åǰ†Êé°×nÈeOÒáÔ:Ž=çk„Wí…ìDG3be'Viþ®ªÜ{\W&£òiP«e ¶‘9[J0“%.Çã´C7©ÔÉ 3.{U–É·¹&4›ÒýKÕûEùù_اÛÑšc|¿½NäË@ -¿ž‘H¥ü·ÎÄcÏñ–*2óa<ÜjÏfû-1CêÙ\ ë“Ðߣ4›u>ë Y÷¶ÿqpš{ʲE?ÄcjÉH‰l| æìGΤR¾•´ðzØW&G¥iPïU'öSjŽª7ä™ÐW^Ã.›*ábì]Ùù‡„Y1ßœI¥ùSrÕ8â]Y&ÛQcöêòl_/5Ü>ÞP‘j»¹@*õïk0bÔÚÙ2¹«ŸE=ÂÙ߸T§UVãô$Ù¿­-ofoï3Ã~V ™×ÓŒÝ;°îÖïÒ{ÌäëÕ¬ØÐ›Îd‹‰ej äÞyv»®"?Þ4áLw™Ìëiݳ­ g•òýƒ¯bUäfëž·ã4ÛÃúûôý2;,ǺçÓG³§“´˜ª™(“™'5¸Ó°ë䯆¼é‹ŠÌ¬ªÅøþ^©”Ïa°;¶’É1­Kàl 6¬š•+òÇCZ—b«<“ÐøD/‰ÐRB“½dRiþ[ƒÒÞR2o½ÑwM.Âí«Åé’C²ñ~îY¥þ!‡2 k¿A&ÏXïÙš­ìo\ªó¬Ÿ¦=enªÈì¹j|úð†}q؄Ň{ÉäâÖ&µŒeWZ÷4Ó7¨ÈÐyj\˜óöoH¨?k bû¶ƒÅ‘´TMÇ…[eòp|2JOÜÁ*å÷sWãÀœË*2b¡õjñê=Û÷/ µ*”c#¬ç¹-1eÊh€æµe²õ¼Õ†÷<3¯TṸKJTÙÂ*åó7ªÑýD)™<¸@csßKd^¤§{ÌȶMøëÌ8Öîº ]¶»Ëä¬|Fô<È*Í©†^—O&¿Z®Vm?Hd½é‰xVe­@ÎÙv[X¥þŽ#RñxâN™ü—ŒÔÞ;Øß¸T[ƪ1ëHA̰¨ñ‡w¶N”ÓJΗÉêw Û%ˆ5U×À'ªž@Žý¢{·^lˆuû3s€D†ùJ¯¹Èšˆ—Ë×Èd×h-ÚíË*åשqÉ»¸@&>RÃáG%öê ± ì%rú1 ÚÔ5ì‰jØÞÉî;*O«±>ƒRÑxêNŒ•Î)Y¥|3 ÆÛa§%ÒPV ûùñ¬Éh‚Óñ>êlFÌ0'¶ñþ Üï¹A&WwNÃ_v°Jó·OP£ç‡'éÚÏz Úz“Mm™‚mw dh½4œöÚÁ*n é±pJœLFtK„ÝãhöXª¶¯®OÕ˜r¹²@ÚÖ3ëp²h„LÚÖ÷Z7¼–ÌHÛú–3FÍwIÛúímj,jSD&mëJù}ëkض@ÚÖsH8ûåŠDÚÖÛãao r"mëßí8~1@ 5ßégñ(xÖ_"m뙉f¬Û[G mëUtÉð¶C&uþãNjø·>+‘¶õá¥3àâ³Y µÿác:„Ù/—É_=ÿ_V ò\#þëŸþ›ó»4û¬üb!f·E}ál²gl˜XüˆHÍ_ñÐí¬[|uxOw!Ž®ÆŒèõ 'Í>ƒjÃ\E²Î®TÄl®Ç†ZïÀ¢ìúŒÒâ»MùA*åïX¬Xú{2ÈÔâé‘+ØFOÚeÝqLd'¥Uµ8–¾Îæ[¨7¯/º­s¾ †í™Íþxi½q·ÆžïÚA¿ë&«”o퇬ó‹»YMÚ¸ lÉûRC´ä8‰äÝnˆö!"»b6Ò´ag-9ÇÛ§@*ÍO¿¾#¢÷ZH}Ëu¨ý5…½U,?×Ë&“g$aٶάRÿ“‹5v÷Ù¿›ØÊôÑ…T:ÿWU¥CTÄù®¥³ÉøP±ï8{Öcê8qÇÕN øw«Þ-ÏÆ7OÁ “ùEòIë²ߌ½YЄ7g. *õÎ{Z²©^sÅÊUŽäèsKÅЀµ •ò{×-(®Èo ‹¾ÅoºË–øXòE=Ù1m½%ƒì'ÛÔ“ñ¸‚XµöÔ¿=ÚT>÷§ Y6·Œ˜^ºLîΈ€Icw·cÄ€¤ºÎ¤)°§¸~ŸC9j”‹˜vhÈĘ2âD»jR)¿£¹¸YÔ…œù´¬x j™,2âj¬¥íŒtêË-£°áámŹͶ³o’!îz‡íïsWïF±+½]Ň3}³H¥|kF¯ÅÈW ¿ëÓ±¿®š­1ñ’ŸVÉÕ®×ñáÍwÅú‰óïq!SÊŠÕˆ©4ÞúÙ88䢅<;ÀBæÖ¸éÒW¨(’ç;\|º]Ùqô|ñ–Ÿ‹ yÜ3TôˆA®éÒXl]:›«Ô¿xš“Øo¾C6¹5£¿ø0¶4û—jù¨¡âÕwU²É¤U#Ć‹þ`ÿ˜p ñ¦­òfè:ø {h@Cœê rçE5ެïÎò ËÌÞÀæ¥N_݉e¿öì&>ïÛÉBž¾°ï?†±JùKŸj&Þâd!ÖtãþÀt‰rXrž yóUoĹžf¿”¿Þ3ÎB~;Ü_lì3‘•v¡ð—^ §:^€û6=Û×ýÖÜùÈ:¶ù„AÑkؽ5 ‹w«g“ó–Y÷‘ý²Jùê^Ž*#‰dYó-t+Úš­Ûµ¼Ø/„L®ºÛXô°LÏ"‹«-ù³É„®^âÅËj ©4?¾·–*‘MNvíˆO˲7^“c+°×ŸVŶÓ3Y¥þÕ¤àeÞ ùÆâÒíàHöXª¶¯‡G‰Y_ f“¶ugu ±N­JÙ¤m}áÂÙâÉÄí mëÙ?ßbݶP i[¿vWB…^—-¤m])ÿka¬8rÊH i[ošü¡A?@ÚÖ‡˜K‹ ºlu!mëò:G±M¼ÁBþj¾«Ôÿܼåbôä- 󺇊åío°Jçÿ㪪tè‰åE1¶ƒHöëRJŒnÕ’íºÉby®6XÈñ¹Ñ–ãg³ÙÆÞÝ-+”É5ïe™Ì9 Óô[`iÛ[$ ÕÚâ¾u3@>î>ÚRkî{ꙥ,³ŽÔb•òoìÝÍb_±¼HN?9ÑòöZ)6±ÄB÷ÙªŠsú¨ØË3犑 ¼AÞ>¶TŒ—Ø?Ý«‹vMêˆääÛ)XãéÊ*åûX¿šXr¶{6¹)±¶Ø¥w'¶HÛ.›'tdª¹ÔÖ•=ì\Q ý¤b_nÅŸ_ °Jó_4/ú¶o! ¦Ib ϬZ½Rüs×x¼ø-DÌÛ3¤Rÿvù§ˆÅjþòëVâ¡«Øß¸TW—.*V»U_$¿‡Û«Ê¾ÚÂR¯‡ÙB~Ö+kUû½ì CKô×9 ‹Ö‰Âç=ÕÙQþZ—Œf%EòJÎM—ƒjÈZBÌ»Þ\$ÿêxC Û³&¯Yâ‹ÏA†y‰ÂK‰äª|Û u{ ÒÑÜ@tÈáB*å+?¦žXµz›l²ó•:â²35ÙÇe>¸äsîÁsëŠ}©®ìÔºâ©FÁÒñG:œ›Ì*Íw®0]^ë“…¬Ð~¶ø<á#ÛèþxñÚн Ý·WøOf•úK ‰÷ o¹2ý+:Õ‰v!ãRõ‹ûŽU ˈdÉBŸñÎ); Ëb—oí‚-äÉõàTd3Ötì=^}Av¿æ OÖ°5ªD¢¸©'›±§¸Xd¤ û²Ät(ë$’=Ôãð¡ÛJùk팴ló¾ òijËS_#ë÷Å„V]î°gÛf`Ìüuì ‡-ÄÎv"Yl°½è—ü äã®Î.äÉÀ±ÑétJùú¾u[n+‘MîºX^ ù’k!]w„ãÅÒvÙäí¹‰Hr¬Éö_d}ª}ÅBF´¬cYá[3›Tšß­¢Øòë{ ÙN¿P¼nÿ–méW^¬å´8‹,ÔÖNüpÂB*õk®%–ÞWÃB™>\Œ0neãRm½ð=EÜyÐáÚ´8Ȧ®uG‹ÏK³È·{ú"ãÛòt©ÆâȃÅDr¢O-ñUdGöñáÞâ’ Ñ sV//¹ÁÎn 6Û’çBF”Žs' ©”\P‹:pHÿ9 ,ê ؽâ%ŒZu˜WRÜ™:Ð…ü¶§¨xwÖNÜËŠçws!ë²îß?yË®¢X±}÷,R)_ÖÝBbëgf 9dt.Æ4YÃ&­< ï6…³É7Ïcv‹órÀµƒ.é%je“ë×nÆÌü9Ri~ö—%âûü¯,¤ó[?ñàù'l¡©éêMþY¡Ÿèv¹*«Ô?&+L<9ÅB:UX#Fz®`ãR­Rþ=¦}Û ÒwÊ'¬Ø¼œuónŠ6«š³½²]v_ÅvH/!nÏë-’£>‚ë¸Îløì…bF¥Ø{G¼Ä1˳s¢§Š_*ŸùæKq±é­¬R~—ýƒ,7‡äÒSÍ-©”aßOë(Ö²ÄI¤¶¤·ø¢ÅT™œÿ¤¡˜[å¯,òE‡ˆ+ë,dÏíDƒ±G¬W§aî³JùrKîèöÃ-¤°*w×g'”:‰ÅÝãØÓOSprØ@öÉÝ–Øéº-‹lSg€EåxÝ…T|ÜDSÌ} ùrj ØkÄ vÝø©â‰^ר!®£ÄñoF°JýŸ< ;—Ëo!ë†y‰ãÆ…Ëäÿ°Tm_i¿£Ø°n më¸ö\Jø×Hÿ¶õÞÇð—‘´­',­.:ö¤m½ZÏâ©*Ò¶®”¿WÙ/YÏVžu!më}Þ‡ŠS—F ¤mýgæ 1@o²¶õù[手±¿šïjL|]ÈBÚÖ›éCp}_1 i[oeʳï7Ý…üÕùõF,o;]°¶õék‹Ÿ¥ÂòWûìk}¤uH”É_=ÿ_VùÝúïCþ› öÝ]zî”M¾ïîÒ÷jO¶ö¾â¼ï,äŠM‹ÄЀÙ¤ZUH,Ux;?£Šx<¯=Û9ØUܦ¿rÖÔjâܘò쳄>¢ÝžÊÙäFÏñ›Óe ©”ÿñíb“ÝErc\=ñSOv]­¥¢à~ä(u?ñJ}-ÛÄ—v%¼ÙE9û!»/d]{ŠÂ‘|²ö rn7f•ò]Yç)>‘n!ïußndG‡–+ 8çB¶Lj.^Þx\&ß"š’‹XH™‹Eÿ…ã\H¥ùCÖ,ÎÊQR$]¿¹e¸dëÄL{uØr{U?ñÕퟬRçÖùÅ™óûXÈnO¡Ýðï¬Òùÿ¸ª*š¯®Ù¥ÐáÙäÕîï]vz g— +Z>YÈÕêUâæ›çØ\U)±¯×'véE mBÙøš•Źû:°w'¨D¹Ò¶íò¦â²Ê³ÈÙλÐÕÇl!•òÿ9°’¸ j‘Ì{YT”çôcíÊ5ë|è 2ågY1ßõ9frZ9{1Ñ!d¥Ûõʼn} ˆä…+)¨5 äiow1­ÆR)_ù5ÅZC,äjÿüb÷¯‹Ø}a½Åù Þd‘ùÌG½kl!g/&ÖÒöÈ"ßt¿„wËŽYH¥ù÷‹”ÈŠü¤Ñ!T.Ñü1{à»Øýp=‘¬× ¿¸/¼*«ÔÀÝ'prnŸÍl(£bãRíz®*:õMöúè‚F1cØçuV‹³žn¶çV¯Sû:³Î.ß]Ú¹Vd[¾KÏêòSŪúΔ=Â~ì¸JL³ïÇj£Š‹Å Î&ëû9‰+zT`•ò»}ßQ½E²Qä)ÄÕêÊÆg“Gõ°³{TWJiìÅ=Å›ÞMEÒÓ÷ fÞëÆÞˉÓšdƒ<0{´xI_$•ò•‰ÀµOµ…JûKÓýÜÞ:—™n|Aœ~Bxìæ!¸÷ù¨Ðy˜6úhUH¤Òü‡.ƒq„­'gÎD¯ûyŒü‹§:Ã?x“‘œ4 Ž„ûï‡ 7xÌÇ´«dòñ¾˜á®"™qgæXs/#æZ°¯}Œ8ÙåÎJ<ØáªŠ¬Uk6¾od‹ÝïgÎHÕ°Ñ%+4ž•ú›õÙsÚvBr›ÑZØ¡…°cn',n‘ÓôG¡ÆÚ;§'‚!ö@Ò¿ß1)j”ƒðæ?Ÿ~v™u€üâÔ_l˜ÄH¥~³L`£g:##ÓGKIL¸(8gfèÊäýk±ÿµ©<+>,cäÏ ç`zÓAB¥ý˵½¤K÷é°AÒ3Ú&lzy^)1“Éy nya©"•æ^å‚~×TŒÔëv2ÇþÅS=¦±Òm}89wôXûª§ðaŽ ç5î"‘Ã÷›£¥Î[iZäY¼Í•“—í,aÈD.}肞¢$òåmìiÑ‘ù{:Á†&Ã8ùsb ØtÑ*õßí?,~#é×{$H¡Õ„£~Gb|øF.8 ® «ô3%¦¥‹¤ÑÀÞЬñA »'`ïÇ¿e²ÙÖ8,¹[H¥~/Ÿ5(ùزœ‘õ¬:ʃü59ù<0îuòSDšVþMrÁ—Œ´pÚ…¿“JûÏ•\’êJ²,IN$¬Þo„½ïx ë]Ϊ|¡Òü2Ëæ°|a)#ãï¸JƒBl9ùOõȯ=°trr×!8Òª½°vŸêèUè(“=O<÷ŒŒöa Ô:nÊÉ1ËæIÏŒœuô5,ºð[hf„#Ó8™²«2fÜ(!M^mbÃK’©Ôÿ–ĹÝ2«Ï0°,/Îþ¶í e²FÒr´m£"·eÝ€3~ÓÿÍí^{ ôÍñÐÆi®¤Ñ½wÀJï1R©_ÌO¹é‚œÜZ7«Äå¾­°vàx|Ý0ÈqÏzáÐäBá·ÍpQG‰“>–èýì#•ö§ç4ݳ@_í ß`ªðê[?4M¸~ÈoðLß)‘Jó‡Ö„¯åý98fÌÒè)ü/NUýõøÂi¸Ö»'ÕóS:×@’ÎHõ|ÒÔËÒåWÛeR=¿›Ðkº·å¤zîÒû²Ü ¬9©ž+õOÕ›'V©žŸ4Ÿ„éïl$R=¿Uˆå@þŸ¾y8»YNþi¿ßåúÒ¾†8©žO.k‡ d©ž/ï ^2òO÷Û݆Q¾#€TÏ϶o„sÚú0òOçG¿ÑFÿ¦œüÓçÿõe•ËË-ÿó–ÿd§µèßÒÈüÑqøû¾›°jy‘t¡ù™L× Umв[ic<ݬ&'ƒ>†ŒÓ9Œü쀩öǫ̃þ-’„{˜;ž(ÓÖß¹qO¹P©Ô}?UñeFNé¾RŠ4Þ"üiÐÇõñådo[ }c/4ñÕÄ=aÞ@®jÑ«»é /Ä]€»¿b…]sfâ9cW¡R¿Ó’7îô«‡dtÜq«Ðy”“Ô”¹rò£×o©š›¥Ð9uHIY§nÂyÝáUø5F*í!ßyQOEºç}s»qà±pã¥lX†¥LmÖ ¼Ì[©4bU&ž9î#“îŸ6âÌc:@*=ÿ¯OU¥·žÜv»;ylÊBÌnâ!¼³öy‰i÷ÕÂÝô˜‡Î ¡üÄÜãÆ0R'±Óé%|Òo5ÞýÈn£&àm$·è¶G·w§Ë*AåÁ$R©ÿÔ”'ÒJ­±Œ,ç ø·L^óm€î¡ŸYöVŽÚ {WI?îǪHós·ØÄ)ÉŒŒåÀœÖksRñTç¾–R\NKdóɦp%KHû%A줷’ÓŽ°ä‡2Jóæ¾ÓÊe27»6³8ÀÈ¿xªmsû õ›–&¡]RºÐÓ÷>ëp?ÈY§ß²­ãÚcj —ûtuâ¤ñóÞ`¾¶½pÝÓj—²SE~>ÞÝ ŸË¤Þ¥ÖøµÓ Ÿ­ƒ½ö®f¤RÿgïH÷6šy¤G¸´#x¸ð`+&ÇUF2Ò+¶!븯LøÁÒòZ#ùÑì!<CaCHFÓ‰þ@vméŽÚn•ú™¹ÕFã­~HŽøý *]» g¶®-›¶¯’È÷-ØšŠæ@.ˆI•ôÄÈŠß×J^´ORñSݦlÐÈ/M‚ÃhIxäôXVlÕA¸«ºÔ×]¨4?Yg<øÌÈüOñxôæuá_<Õ¡mðÅûJÙ|³%þjý°D¨ýœÊ= "ÓêfáùOdrÃõƒ ßQŸ“KrvÃa¯lFj¥ÅaêØ8™´ÌËÄ¡UeÙkfÓ)A˜¼ò‘ì܈“íô¸Íê,|²ÿ*dX–™ú\“Œ…fa5pò÷H‰ô«6 ›÷Ïa¤R?›µ×¡Ö{$_'†]ê V.gyçë¹!ç Û±X"'å0w»‹@f|ª`“¯˜J¤ÒþñV!poà } ‡¶ù …UÂ!§{B‡ Ì é„¤Òü¢æ©¨ue#v_‚f^eò/žªv”1ŽWÉdçÐZ¸€[0²¤þz¦õËGXçz;¦ç7_øk’„çé¼hʶŸð‘Ée!0b_,ãWOľ]Ò„¯½«áùs&Œt»¦…Ý:5æ¤RÿeÇXã#ë¬* b+®Ö-´†ú7ý8¹åb2Ô  ü9³ –]Ê-!'ØG`y‡ 2iÒ9ÇxÙ3òLn–÷«¤R¿vëöÁ¢9€Ôÿz\åtaå™›ìÈ—‰2¹ðM9‹Л‘‘îžLsú4a苇҄õÛU¤Òþ[íbÀµU<µ·®W›ÍÂ^%ËaäÕHÞ츥ä̳¶B¥ùojwÀ¬ËKd2þøy0=‰‘ÿÅ©ª¿Þ°ê8Ô=‘êyð—òš¶Ã©ž§N)cqdR=ÏÝÑÝrš©ž—».Á’wœTÏ•úk¶ÝÀÎÍš ¤z~Ââ8osà¤zþª4 «šž+&ÕóÒßÏa×*.“Úoäç³:¡êyQ³kÌêF#Õós¾±Rù0]$ÿtÿi¯DXeU¤zîzòœÜåËU ÿtþƒS[áÂÍ/ŒüÓçÿõeÕ‘çyÿó–ÿd¦qMž”)ˤc׺|ÂYsF~)Û€QÖçJÈÐà _Ð]"ÛY=…’Žm‘Üõ3šŒü ¤Q÷|1È“ÒÖâ+£$Fš¿x7ºÝv¹’©ZÝFC¨Ô?îôf×ÒH¿0sV`^S¸hÑxkªÃÉ uþ0Óa #§ïdËt!i°e3«r½ä —B¸×ÊSè¸×w϶*õs½Îµ'"ùxÛzØ\5Aèuq!V¹ÇÈÁñ X‘zV¸®Ô¿¶ä¤Ç¶è{k #•öïo"ëéÀI]¯`yFžJ8òe&kü´ðáRMùñÒ:B¥ù:™~X`®bd±.v©r*=ÿ¯OU¥·r nd9œ‘ë™ò{ÖqÂÝ­&à÷ù(D…Î+†•!}/Á•Zs€Ì\¥·$ò¡cVt9*“š£v =FZk³Ã£:sòRpÌ(ì*Têß펉⨠äù£›¥o?™ÿiÞ”ISù­¼¬$‘Y®Ê¿rY4¥äµ.ôø…ÑÉWé pŒys¡R¿¨)ýáÖÎ$5" !xaáÑØLüö$‘ÑÛ°ùä®ÂÞaÇŽÂõ>Õ¥ô1}8©´^v_ù³UWNFlk.êõ:Ç•’“\¹3a£œê:H¥ù=Ì£ Ã÷rF¾_;¦—ôâä_<Õ§+L¸Íè\F¾oÈo¥jï0Æ ZFBÃ~aõÈ4ávQh2¾1ÞEÉx „†gxe•2Òf[´ºÖ‰“C;Ý”GŸ–ÉZ³Ù½»YŒTêÙÏH‹€UÐ~t˜PÇi[pt·DºYhóØSµTäëâ—Ðu×A 7ü¨‹ÇgÖCrëe|;iŸŠ\x'J¤R¿>ï¤ãk; ©]© {®×®é¿«Ÿ—IƒôD4køNEêŽÝ­Ïä¤IjC¼;¨šPñ•—×K ûøpÒqnI‡KC…S Ž³Ìœh 7µÓâ{ì¬eRiþˆÖ—Á1<”“®[¡Aãþ¿xª¥¯uøšý·˜ðñ7fs¢RØÅå)\pú$||ã'àœ&œüÀVãíµ‘|À¿GX ᘠµöåä¬ú˜q¼•°àzŒt9·¿0ffÄ×ÛÅH¥þºB°VµÎÓ·óÂ]ŒyÛ匴¹÷“æ Kf¿ƒ¥Æ] 3jŸö˜EøÑ¬¥°Ï YS¨xªû»Àë7@j[ §ín}Ú6;%2»|žçyÂ+wÁFF䔜²¸"TÚPg°Ê$2€“UÆæRpÿ1Âñ½v0Üq†‘&5cK¦†<*Í7Û^0²A; žÑZøOuëÅ{L£ÆgFº0VRþ]Ø¶Ê o”;ròëqw´|ï$¼3¶.ú?ÔErÐúóp©ù ‹Û…¡Í¥Œt_è‡Wk\•ÉŒw…ìdOOáëÛL5VŸ“Jýë¼/c¿i»á"´=kˆä¬nÉìYü[Fzµ “/|×àdN3K¹ú-$§À×EK¨£wY&—G¯C÷¬—I¥~í\.™Y±@þø®‹¦3F k›Ù¢åÄ*²€i¢ë¨|™ŒÛ½Tš>i'Ç/º •#„JûƒÝ–JA-Çs2óúq©Qµá”ã0§]SF^ðˆ•mŒRi~ëî90hr[F.¬Ð…÷¿Þÿâ©6>–ÂNcdè¶ÉìÆÒwÂozã°_Ûæœ¬n…PC8mCtm)‘o·">Ö¼ZBÖ8¯‡Þ9†Œ<½N›?–ÍŠį̂֜Ø.tßTL*õoV —"­‘ì±.ê®é 컨TÌ»ÉÈ¢)``¶D˜c‹lúˆÝ@N=g¨JÓ+^½†2W÷dRinå,lyÔ—“]ËÂ0Ïl ðxÄvøý¢-#ÛÇ@õ’ºœTš"÷|1Ma¤Í 4.­ÎI¥çÿõ©ª¸ªt2¶ê݈“ÁÑqk=á:×ÖüÖ°/™Þ`Y ™ÔÉ¿õµQ‘%!8àÜT‰ÌpÓC÷Ny2iìíW¿`äά*ˆº2M&?;aÉÕœTêov` [óª&'‡ÞI`ÜšƒÛÿ–?9tÒXûµäÙW8®[è=_Éü®P§2HÍ ߇Û"ù©‹.êé¤â©¼ÈU­«< “nZ¬þ»‰}úKXû•DÚÉ<—ùCk(Ü ©%‘íNÌ-ùéJûSVDà7羜lwf önÛChzH·%NÚ…bÄü ¡ÒüÐ/¾‚‘ ª<̲qþÅS„†[>3òK¯ÁØCï¶0ȼ ×Yg.|yî- '\tk=Væ§y`]2™"9íël\q–‘ªÌݘ=ÔE8aÆVL¾V œ«r×keR©ÿ‘ÅkY‹£9ùU3‚ie{ 8î„üGûìíú2û6Drã|;¼<È]¦bðóíB¯¨$lç™,|=q6ósDR©_ß>±ÌwX+FºÃökD¤ÐÅvó¹ÈO±²†G….3 øÚÕ‘\1¼ŸZ~_"•ök¥,Ã#N&ˆÁößœ…O­Æáý]¡‹I|ßy#•懞\‡~Ã$2~tw´\½REþÅS-×ñ¶ºGY{Æä ëöeYKÇ ÛÞ kÇ™ ?Ô[Œv¶*$¦8#÷m"¿=‹kì–È}·}0æQK ´ºž¶ 99âJ Ö|¤-Tê?úQgææ6„“‰«®Ëö6ABÛ{¯a¨½’s!Üp¶ðàµ(ÌÐ×Öõ£ËQ8iÍqhòî(ç®vÀ&]Ö¨H¥~}J>²ngŠy´©ï¤ùEØç© Ï»™ dÖ VüõÙaÂòÀ8Éu’…DÎ]±lÂï©´ÿÅ™X,®ÖŽ“«®Ä¸n6Â# ÛÀÝ6 Œ ©¡·›6æ¤âOå䯒±”‘³]µðÔ{#NþÅSõùÔm+Ò©µ^cú®ÚÉy’µaD y¶[p°òñ"|àŸ.T¹ÔÅ·}ê—ý/UÇáž‹…êWÇ R¹¸ËB¼º0¥˜Ü·ë>´ø™‘Jý5>µ,nÌÉÓE÷¤ÒëÂç4a¾ßL$5ÊçÉu4{ gÚXâ¾_¡NüÓÜSX'b–™¾Ò ÂvJH*õ;÷»:Wu6å¤ß·çìRí¶Â™»íøû÷1y"˘o—)“éòfõÑ É]N&¼Öî™@*í¿s%ήhÄÉÏ5Wac]Cáªø/ð`Ëá›~hìÓ]¨4¿´Äk—[sÒ´ÉZ||ã#ÿâ©>;SúݹvÑA8_™$LºÑž¾É²ÇÏLéø¼‡Âj>=°¬½ #·[-A“±=JÈ zÞØtý FÞ9¼S“ò…fýѯ®7'aßbÌk¦ËH¥þÛVŽ‚ÑCsÒ7w ÜÜÓ]˜S`Ëbôõ‘ŒoÈ~][ä¼à& õTIÝDoБ ä ïÕQµ¬pmúlc"“Jý¢«°£%NzÞ™Í&‚‡p•Ï-¶’õ`d‰Ã¶Ð(RxþI«å0¹„<Ý, §ïRiÿÕÍ+1ù¬'fÇáëì*F~[5ƽ†ÒÃ]Ïâ…Jó9§à/ïÎ2¹aÈ|ü¹î‹Dþ§ªþêõ;âjg1R=¯Þd½|=ï;êùÜæë°WD:êyͰ|\{©«LªçoíCÉÝŒTÏ•ú§¹  ÛsR=XQüɨ&ê9ù´ ¤z^é»§WÔFòOû,¸+[–ºpR=_™m%·mÆHõœ_?(¥,ñ@òO÷«Ìb1#î.#ÕóØ ¯¤W]î1òO盵³Ã¬ÓA2ù§Ïÿë˪Píâÿÿ×ßÿà)m=¨lÚÉaZ*iÆùN§ù~èX¬"‡<øçÏáƒ%ÒCÕÿža-ìàæÂ¥§QŒ´š××ùŒádÇj qG¨–ÐÕò+xSä‰t;¾`a®L*õ79¸Bºñ«U 9ÇþFñç•'%òÕ;'¬ø0É}‘!G»°ï–=Òk+ ‡>?%¡Q‚ù)¸m[}$]zõ¿©ÔoÒWï~ÙçFjfÅËÎÆJȬZèn=”“»×z îoÕênœ"“ó;LarÀ@‰TÚÏôÁC× ÉùëA켖™_Óqõ”êœìS³k™Ù2Ri¾{Ò ©öåsŒÌõ;+—ïóà¤ÒóÿúTUz«Ž™e"’»ôÌX°WgáÒwZ¸¬á¦r~ó Hü܉‘5ÍËY`ò}áóf!²ÿˆÝº5CÀjÝjaÈäò«§…½Ë5À¸ü{ Ô´3›çyH¥þªswd—½ÂIãØ²IáBÃ’Xyñžpc`|_ß_جõ 8ôò—ðλÐuEG$S7Á–gT¤ï¼lÿT I¥~-•í—ÉÀFq,×x$#‡nŸŽ™¯Ús2¢ù æZÂç^²Î·ë çy©d×0?¡Òþq;-Áó¾9’ž“;€\W¸+r Þ;ð]"×v0ÂpŸn¤Ò|¯¦%óV‘œìf¤M–OþÅS]=w!KKòDrIÔ–—! o›}†¹NÇéð¡U“w5ƒC}erÕØi^Òp ÓTÈ)q8™cµV&Nš jÊŸ®]ÏÈZË…« •ú›µß˾\Œ2y^«#¯Æþ„á-Iäësn˜£e dØ~Cfô¼9ìã½ë í`Ω@a ƒêxµõ •ú½ÕÏfB02® ‡}î\›“ö«1=:‡‘¯wÄàÐ’–ÂZÖ¦°1:YØ1h›°¤©D*í/Ié3QÉ>ÇAAëw@Z}ÓÄM?w3Ò/e2z¿~,Tšÿ(¼Öý¨ÁÉÄ#ÔÈjÉÈ¿xª/Þ`‡w8"9¼ëÖÉÉVxÖ¸/ÓéÈÉäGØk?w|ÄÞ$L2jc >éºL–”8â£#wµ*b³4ÚpR_·>²œ‘ÓžÚÀ‰E·…Ôf¿å,bÕÚ©ÔÿVŽ ÿlR"‘!Ç$žÕ³c ùkìpÈä¶üIr #­ÆÞ…á#¦!Ùÿf:ØD¶jûFÂæÝæÂðÎW¥ëm —/ŠCï1M„ZGÀ 8/¡R¿F×ÛÃñ)³8éübÔÔ ¾¶» MÒê 7i¾„°ìÎB̓Z<è§Ð§î\ù½£Piÿºí·Á¤íz ÃUU°ð~ŒpÏo?¼ú‹‘+.$afî>¡Òü%pá`{NÞ]oŒ–­cäqªê/ûyuxÖôM@ªç™ýâê¶2©ž[š*§Ý;#‘êy³03t€ œTÏÛíêW"™]”Hõ\©¿ƒ{]>¢S#Õs³„pJ9 äÿéßÏ‘–ú ©ž7i'fDù§ýv¿Ü¦c“êy•]=,m5–“깇ÉuH›q‰‘ºC¶6¶ìäÿégš†gCÌ€üÓùCËúAò)ŒüÓçÿõeU‹úGþç-ÿÉžÝSYüw'NjìËl¹—pðÝÏ,¶·.’[“ùët Ë{5äîaÅdþ»æFÖš<K½d7Òߢ!Nl‘*“8ÅýgMæd=ü«ÍH¥þ/u»à„SC8ÙcãxUÿSÿ¨Í ŸúqòWeOyѳᑆÆ|ôO‰¼1¬Š…étg¤ñ¾+lßᦜ¾æ²|¬ãoF¦™k¡þÙjœô3ì%þ˜aSàè¡ŽÂÆ‰Óp¾Ïl¡RËhÜ2Ù‡“=f$àèÜ.Âó?3pðÁÃŒ|]¼/ÍÈ“IÃôØjÑV wñB³ šHöú6 ç'¹)nJ›†!©Ôý#q{==$·o‚c‹Í„?äÀðÎ@î[]>i Yôɱ·µðí¬H©Óƒ ¡ÒþúI fùr^©ô`d´pì¬RØQ¸G8{¼&Lí¤Òü®zEø•G2òþàuhÛ;OEþÅS=QßJªœ?š“5ÆþtÒ…FÑ©,æâ?¿«þ?w_x!ÏuNøÅ1U;î¹L®ñ¨Ç¶:Žrý›TÍñædïñi8}ÌuF¾Zb†Ž—— ïô€ÌívœTꟕ†ÞmÚpÒkZ&¶ZOXìï‡OzåKäÓêÆ¸´2F8z@{ŒJôB²nÙ>HØ;E˜t·:¸ù4®: _ß R©ßðÏöXPÒÉögt±þÍÚ¯ïHá=-„›]¾ÉõO[ /À²¾œrN‡"Yê.‰TÚoúª!Ì9³È^=À8-[³³ \¦Æ#©áÿε|ÜX¡Òüç5œÐö×Z™Œ[U7ö{ÄÈ¿xªþŸ¼À/a'?DBàÍ~ÂñoͤÞ÷02±ã©ÞÉÆÂùåléæ%@F,lćºÚÉä¯aqúç!ÂóªãÍ.Bƒù1XµÞŽ“µBG£Ó®‡2©Ô¿íù üùæ# òSѾbŸÐ¼èÌorC&/¢ö>œËÈnMeß#C‘ì™Ǽ õ#{á‰ÝÂ×ZÓ G^"TêwpÎcèñð®A½ú»…žnÅÌðd=$|©ËgÝ2º_8h{Z!9Æ×”Eßž/TÚ¿`ãtÈK(rC³X8éIèÔk¦\x`¯ÐÑ|,+x*‘Jó‹¶HxjyNšl\ß|fä_<ÕØ€d8dëÎI÷ZÙ0x¢½°N“±RRö ‰4}rT>m8È€oÙÈ^•Œt¯ù@ncØž“nÍoÀ»É;¹ü]W\Ó»>'[{ ïövÞµŽA½ÈVB¥þ9«±Ô*†‘çF eŒ·0úGOô9õ]øzx •æ¿ñ—p¿ÕT kF;aòF_Fþ§ªþê¿"BçÞf¤z~ËÁ•·] ¤zžq1_•,“êù®®‰ø%¥ê¹E‡ÝhÕ[—“ê¹Rÿ†½a–ÞFªçö_ó±YoM ÿÏ?Ÿ|ztŠDòÿÌ©†ãv\òOû}Ö^гâ#%R=ï2w ëñ¨R&ÕóþNûfk"yÏaë>‚“=Z³Í³— 5:”"*â€üòyìlê¤Òþ×ÿùX8¡ÉÉw×JXËõ„’s9ž'9¾÷¸6º’Jóï7Íb’~ #½­ksÓõzœü‹§ï7SK€´ÕÀTi—ðnÈC÷.ddµÄDhyD““F¾„ÍqÖÂë}±«aGaÑ»téé2y÷\gvi¨#»½ãáõ,]¡ÍÏ ]+I¥þ_ ÓY­¼wŒ¬³9‘Õ8hÍɘ&ƒ Â)A˜Ý¾^-\+œ,EÉco:#Yàý%5R qülã+“W qýJ $•úù%Ç`¼¯!’F«0ª¹°‘J²ÐOàäXÛUÐÙ#^hq’Ýjj‡äiÿ2–T“‘JûÛ¶;ÁÚn²âäù,[óÚAX£}d‡#™v´++u *Í?˜3—ÅÕÍI8’ W˽…ñT×Ç¢ãïg@.7_¥" ³ÚY;qòa—k»p˜pü¹xtþ9Fê”nâ›%òQÙ;öö¼>#¿d±C+8ùqævfWî‹ä…™[a˸ •únžÎ^ÎéËI¸\—9·˜.L«×›Nš,4îaƒ Ö[§~G¾=ç§jµá_×Ycºžƒ¸¢õ—£ÐÙ¤šP©_²Ñ?ù½òlŠæ*Vvù¥•s8™1¶ Îuvn=ÜIrë»^&½O¯fÚ3´Tü( >ϵðभÍYV®Ý_¸BïÛ¼ÈCZûä×÷ˤÒügõâÉneŒìS‹Áín¿xª3¢}±Ö‚¶HÆ4ï?u{ sô›£žßNjÛŽE¿½½…ó-VàáØ" -5½pZ­1ÂW¿öI6Ìâdë²SÐ¥©JXj'MáHžúç/á5^@*õ<(µdFsònß·R§qÂ[6×!UŸ3òæ§oÒëA[„{ÓòXÿ}›‹ÉÕ*})vôk‰,Š˜ŒoÇ/Fr‡]6ôžÖY¨ÔogÅGh%÷ò¡Î'h´X"CZãàño¹hr .4ˆÄëGYñ¢„%5XÍH¥ýºÇYVON²/f· & _¥·×{.@÷Ðaý5GRi~ÿ“Tîç+Yâa.½]ÀÉ¿xªù·qêÍ1H¶Zf.™ó…VÇWbцúœ¼¼>½î¥0Òi„¡/…]—D`JǯÂêö~ÿüâ°OØêBËÎLNÍ]£êúÁ ÈS‡Ž±\“þH*õµ}1ÌÛº’“™³8¬ _.lÜ0I^rå=#Óõ:Ëæñ.œ¼\åý¯]²"¡¡*¾%’#>÷Ç·fÞ@>ùÖ W òAR©_›8}<òÕN&5'´Â6ª"aÀöR0ŸP—‘53çÃv†Ó{ÁƒæN*òؤf×"H¥ý—æ³wÕçpRCo#½x±ðµ±=ÏjÚI[‰kø4Ri~¥Æ}H¾žÂISŒ=Vø_œªú«Î€èÔ&Iõܦ~2B’±LªçS×ïÄ. Ê©žï{þ™})oÆIõ|Wô;–Óg™LªçJý-²_éÊ0Nªç©:Cá•ËDNªç5#·³šk#©ž_ñMŽ8+!ù§ý\ú À%zKdR=¿ån·.ld¤zšnÃën{ÇÈ?ÝÿlÀrv÷ŸŸ6¤z^·ùæ{I&ÿt~ƒüƘßè"#ÿôù}Y5§Féÿ¼å?yc†-ëð‘ì™Íbœ…z[°k¾·€|¼ª‚=V& *Îúù—ŠI›©ë±Ý•[@vêaObqò`ÓÎ8<}˜päb]´ÕJxzÞlæm¨%TêøÂN,]‰Œ<Ûþë/+&kxF³¬µÛyºÝBéÃÏžÂÓ.²O ƒ‘tX–ÍàüO MÞÖÇ3sId;z¸¾2I¥~ m³¹9Å£+û¸W¨ßõ93jo€dZçln¿@¦zøàйç„]T!¸§ÏT$•ö‡ÖÕÁÚÃ9y#·†¹.>=t š¤; 5›Þú1Riþér¼c4^&G¤ž†ˆwÛ©ôü¿>U•ÞÚ£ñ~6wEK$ûÏz sÎ9óýŸV™y2€_ ,‘±ãýpTƒg@† uÁOý/H¤åÒH9ƒÍÙÛÈ›ŸÉ+‡{ó{—LkDrkoo¹rh •öo}Ún_ÆIÏ0gÜe¼B>ý¦¼áC.#5û"uÝ?œ“Чzµ7fÍnÀÉN{w⮎ÇùOuÎ[-¾¾ý ³"¹ýÕBá&ÏQÜvt7FŽxãÆ=û6é?×7hS€÷–Äï„¿‡”v§„ªµQ/b,'›hË•ÆÂ[§V`ă…BÅSø¼]©ÒÔq: 1Dò\Ç06»1ùx`u^ëi‘Lª•Á‚‹u‘L½] rµ; ›øV„7³…bevóZc¡R?³àÆl¤¿#íZdz°Ð$áþ€"©ŸvªÐñnx?ÕD²ÎöãP#å–ÖtãCâ‘TÚ¿®¹ö ‰ådȺ!¸iÇÿ:rfOl±m‘Û¯Àe³L…Jó¦Æo}ïIäÓâÕ¸wóf ÿâ©Ú¯³à×íGY´Ù’/¨—-‘§nÿ`Ÿî32px(˵Ù+´s9‰52%²ÑÙLlóø #‡¡çÃeœ¬”—ëµ>Ö «gÇ1r×¼ Å{sR©¿ÓÑ^øVn„¤µFKÞ¿¡0¶užÛô'#%=f'núqòU¯íÌÄÖÉãû[òÂg@Nq{!›Zß”Iýà©2Ì0BR©Ÿùøû¬¨u"M¸[¿Œÿuâð™ÖÉ• ªAËs„ónüdÝoª„¡Ï´¤@*íÿ1?ßYÁÉ[çàéŸË…Û;à&ùŒL™ Ëí8©4¿ûòΘãg\Bž6‚wø3FþÅS]·Ü‚^8J&³Öó ‡Ýi3-X°¶j·Ìí†j‡D†öéŒEK Ìqº 5ÂS€<ŒK°¬;F†Ö4Ä&M;Ëd‡e[°Å(F:†õÅУ€Tê¿«Ê߯ë!ùzÇwHšüÈ>][²ÀÁs9˜™'õã+|¦kÇ¿Ul–É÷ΰªÏSÙy™ïÃ?i¹4œU»X‘JýLç9òe®¾@>úèɵ–„Ëãä£Ëœwx-“t#„GÜwÃ5ma€ýv;¢¡Pi¿åˆHŒ5_ÊÉq|9.ôŠ>þþ2.,^u›‡‹† •æŸÚ¼Œ¼ÃÈN“óqµæ ‰ü‹§z6H›ÇÚ$1²ÍÇg,¶Ç)á‹=q²µËj §»Íd?&ÎϨ_õ$RwwŽåB—×!¼(—‘ÏzãÈKµ8Y°{-îߪ'4Ó+Ä7c€Tê¿dàG¸{Z_5ñFöláÚîæÀÇèp2¼žU±멌¬=ë±l9¶­LZV-–5^ïrÞÀ‡’gàváîèfüœñ™Tê×þ‹ÄÍÚY2Rû¥5?5f¯p\“—,âò@$5Ô䦣õ…ç3øÅá&Œü:Õ‚Ù87*íϾ ×·ãdãF ØÛkº°^eÌ?»1òg³Žx£ð¹D*Íï1ãjš#ÙÓA­6þ§ªþÚà$³ÞÍ_2R=ê|—½H_ ¤z¾©vŽNÕBR=P¹‹J©ž¿èT¦æ@ªçJý›m„éƒë©žï*^ËÊ}ŸÉ¤zþvKÛ¿ÑIõüãÜ•lø—^œüÓ~R?2 s-Nªçƿ޳ˆÊp Õó·"XóÒêHþéþÊlj['˜“êy—â:RÎÈ?ÿÒY']ŒòOŸÿ×—Uò¥²ÿyËrq§]x˵\"_ÙnF›sÍô®q–ÖáäMƒS,þ®§ðóôå¸%/Èé-Öa·¬HÞùçw"÷£ÂÄÛEà|w¡Ð« ”æäµâAìÈÉTF*õ?gß Úx½Pèê_*mÛ$¼:ë–^\Lvú½¥ok€TÚÿÞc6®»É"îŽ&¡áÂģȇö3ÒcáSöqdMN*ͯ­wOîåh„dÜH'¾õÉz ÿâ©^Râ-K í®wÃq'Ê$ÒàÛch}>';?uŪSÝ„E+ö´9fHÖ‰µÃZ3’€Ü8«;,ÙV Ô^°¢`’QVm èÃcF^ýÓ-ã¤Rÿ:µœ™‡ngN>=Uy ßècY¯L$cO …Î)Â~·¯²7yU@ž½¨' MqÇE>ÉŒ¬¸˜y/ •ú™7ÚÉNõÚÊH§y^LÓ;KXââ =5”ÉÑ•?¥$íW™Úº/vy•ÏHÍ_9¸öA™TÚ0¯ÖÍXŒäÈS–8*þõòÇYsró¶õ ‘ËH¥ùššÞܸç*Fö¬^ÊTÅo„ñT‹3Úaºn]‰Œ¹g‰þ³Q&KGÄ­58Y?h(6úÁÈm¿çâ”;@NëùÏ'ÑþHvk^ [”$|ZŽÚGŽÇ{ãÐB ËÞœTêßãu_9ÿÂ"NN.%Ÿ åÂZ†Ig¶ŒG²0Û ¢ §öo%'Ï×r—‡&Lpx+4,OÀ+öc8¹üÛ´^kÌH¥~ùëreŸY!Œ¬úUM>ùk“LŽ[:EÞ¸¿:Áñlɘù¬ÍX\¼½’I§“a[Q¡ÒþQ? ñA¯p$ÃÇib³³ó…î¦1ëm©ŒÜ”öšñã8©4¿ èŽü¾Æj™\Q/ŸõºÈ¿xªOüL‘ké2rΈ8ãÈHáµÑuZMaË55ñ`|GáƒÖ¹0vX8’q‰CYÊOaEÌEf”’¤Õþ4iÁƉô8’ƒÕœ«dµG+Ͻ@*õ×ïEY±œ\õò%øŸY&L?ú¾×;dûï.hÕø…P§ÕÛÇþHþêUÆZé/^bŒçœß12íI Ž}üE¨Ô/üÕ4¹gÎ%‰Ü6·%óψ²åëÌêYgFÞ©¥Å?Ðää.ã‘xãS!ûÄaîÖ($•öÛny ‰3‘4þz†Ù†Ýoœu]±‘“:®÷àÞÃnBÅ¿u4œùä ïlľ|ÀÈ¿xª×Z`?§õŒüRb‡¾Ÿ÷mK!ãÝ!áÛ¥WÀã·-'¯*`ù›²ÌÝ‘,W̳U_ í–n&¾–ÛL¬‰dÿÃy8gvFNÏÄ/~-Têo2ƧËó9yòU?œ¸{ˆ0êa´íÖÉ#7õqï”i¾œy—gaDëVÜxvÑ‹ãó¬HNZÓ R©ßs»í¬óã“@ü¨ÃÝ~s~ÀªóAœtòœÉÚ ^*ì¸a$G²Ìj%\µRiMÕY0>7 IÇS úXø ?\õ‡ìÖó¤ÿ@>£yU¨4ßrƒmqY¡;'Oµºëi ÿ‹SUÙ¡„Ç,O0R=ï6ÇO| á¤zþ¾E/(œ÷HõüÖ™¶<7gê¹Ñª_°ÿÄ)‰TÏ•úŸû:Ù÷6œTÏãõ¿J¥Oç"©ž·{±ŽU-*“êù6(ÁÁüŸ¶ÿÏ?íWµÇ•G.= ¤zþå‚£êµg<'Õóö=šàË^U@þéþíŠàùD/$ÕóÀàpð«6ž“:¿¢á$Ðh>‡‘úü¿¾¬º9éîÿ¼å?ò®T•ñí Æw `|á[¡ÆÊÏ,tÊSFÞ»¼œyd Ñc+ú5É{­yü¡ :á ©“ÀI“÷ƒÐ°óJá^ÙRzÊòb2Ðû©|-|'•úG7]Œ›bB‘<Òi*¶‡ÕÂŽ ñÝ€ ï:8ç[Çò2Uµ§†Kä…¸î,*à:ì»Ý² ’S"Ë?«3R©ß„ 8vž#ÇMÒÇßë„g¦Za»‹ŽHŽô°ÁC“ „9®Bã_ë&æ©´ÿ°m›{¶6’wœóYá½@:<_ƒ,Þ ÉËÖõÐhìb¡Òü·V‰úŒü1( ë5¯ÁI¥çÿõ©ªôV¯7aÐîÉo 5Ÿì‚!­j#Ù<·)»>»#ÌìËÎn¬äÓL7®k«#ÜÝushËÈ5é£qÕV[NJ&·@gÍ/F.wZqòu™X½´*õ/X2§ÔÏ@ò£…&³í‚]pN×ê@¦œX€÷¯ŒökËwÖD²VÛp¾=e2Wu3˜Í $u,æo¿º‘Jý~ ¨‡®±7¹Ó¼vË®ÆÉY«ãÙI›¼@ëhaFÅ1vèj™ šòèú{„~²> ‘Çb=¡ûò(™ü’¡ '7 áäˆ:oÀíëáO74])\x{4î_)‘Jý÷öøÖÆùHúMȃ#¿·3G„#N×ÚÆ8£_U °Á„)ü}e/FþÜV€k‘éer?ÁÂW²›W£€TêvkŽ8P“©]¢q™ÁuFö—ž»ÞŠdAâ ¶93Fø£ÍuùÓ«@z~ŠO•ö)߯N6ÈÒ8jûõz…°Ããöû’pô§z’Y÷H$•æ¯XƒÍ²ç»vhÙj„Šü‹§Úóào𴈤¡ÿwøÊB„&7òÑGz9|X Ϩž&“µÞ aî#¸‘Þ¯šð™ó2rø•üüb '\…vÞ:ŒœeâaK ·µ¿óÞ$H¤RÿËã¼áLX"’§†UI_æE ûl› =Wÿ¯-®tg ¿ /Ï’}®D1Ò¦á )7IdÌälvjK?Nz^WÝì+Tê×*=÷&Îddû.Ûpêxsi5yÛ¿§+’«\Ùõ«o€\Ûç üÐ¥3’eãzò«©´ÿŒëj&kLÒ¬õ"²¶¯pM¹ï47Éîc­xlÉq •æ7Ôƒe~+i|$‡{Jä_<Õî•Oá´^’Ÿ2‚gÒ !‹ôà’Æ1Fvt{ËXsN69ÕŸˆ™",XçÄò×ÛWzb¿ ®@®«lˆŸ”ÉáŽì%@:¹¯G‡cöŒTêog¡´Ïj’ëß<—¦k% .?Ê|»˜'G³¨;ô}ߘu xÀêj‘É‘ÿü lú¤'­ÛçI–m*õ o—‡9Ö™v4“4 œ:-M•[ äyß°  “î§²‡iŒ ý’Åo(Riëɾlý¨N@žHnÉZT·ž ±“½Kò%Råê —/_RiþµMøÐ®’ÞKKÁ»N”ð/žê½²µÐ´Ù$ow„Ì ÂËä¬Õm9ylôp06žHu‡ƒ„“ýoJaË‹Ù;9 ­?^È»€í½„ûS; ½õt$ûoýÿX±ó¨œÖÿüM”L‰HB(I’Ê}_¯(sBB’ˆL! I¥Y’hФR•TÚו̄2O!™Bò;¿÷ú¾®÷Y÷gµ—õvÿóXë<÷~½žœË®}Ï"µC?T¬ÿž[²`ª" ×S‰jöE‚º…>–ÄÉq ‚HjJ@ü}$~Òdèù[Âݼ ?½KQa@I£ \±~w·ÈtpäžqãðºD2¶'4ùâUr2üwÂßoå¯ ßIQ ¹]ôî°î Ûdü'¡èz‚ºií̾›p»êí¢×®½åj&Z°mç\(*6_­m[r£i R xóš ÿÃQ•ý¨,w“¶”ìT6Oêx’øv¸AQÙܸn=m1]GQÙü쉫P«qˆ ²yV·Ù0² ²¹XßýÉí|‚ÊæMaˈ`ï¨l>»ð)\ÅPÙ|de"üj;¡¿ÛOgÜ(§´PÙ\uÍ&²oh?@esߢjíKÑßÝï˜}¡BÁ*›¯>ÊàÒ•+ÖïPö²Æª„¡gæéÀFõî†ÉGÉŽzb•<ìMšÅ-(5™ñó}3w=õÏœÆÛ¿ïÞd²gÉŠêd."g”œ¸ýl³©œa4AUM:³ú¼hŠŠÍoh†ï÷gªµE› ¸ð¨šÎ±"ó·$2tŒc<ÉTÊæ®­ìA¦¦µçêtÏ zW(š86œÔÎi–¢w7Fã¿ä½^×®'2tÔã½pj“7ÍÏn;þ hÙpJ®«qÅúï.²‹'zy²˜ßÓ¥è´ð^´þÞ.Щ5»®µ“¢ëÅCçã}—Ñ‚L¸G<¶B¢bknHGyâ™´Ž+ÖOcIÏ¢ê$±Bܸñªà“~— ßꮑE{ª›w›6~kKÑ/¤«àük²Ûÿv¨yõÊ¢Yk|HùynMø÷ŠèìÕ uë¥K‚¦ÿW‘ùªIà.U!è׃ÇÁõÓIî<ªw¶ß$Z›óÚ«}°(âÖ¿‹!^ë|(ZÞ!Wz¼Ü˜viÊû¥2…šyíâ^Ù—V¿|%EÌ,¨ä« O‚¦Fuî±Þ°~ë î“åÝ!uêT®u»^¬1ã AÅúßÔv€‹i MµÚ¬çö7‰$c‚t¹©Ú¤Qô§Yñÿ–èšîaTuÔn’ö;Á!ìA÷Ö˳«sz3T¬ß9§CÒ=+:2nNœ´‘˜sÇœ™Êzª4:g Ûk®ÌµòH¡q›ÍjÉ `ôù®Øþü bé”ã™4i„’4Æí57cÛ\pŒ&cóú:"ä ±Þ6‚[sÌN–$0Ô}X2øoáJ>.¤MÝ~V §z=¦$v=A=âÛHS„HÑkÝYñóÏëWy2å¸þY-Šn[+O´áµ€Ö¬ÿC;Ušm_%ÅÜ[ï¶€ÊJÂmÎ5‘~Ÿ5€+¶__÷•T½çFŠFuÛ! [ÄM͸'ùü£/C[ëµz\ÏäŠÍOÊ?þ§žKÑ„^«àÎ2-@ÅîÿÇSUìÒÈ6¶pSÕÐ*ûîpjd0·:T©Y½¢Ç—1s÷$]Õm%|ðßNÐæeè¸ šûfÈUé‚Àh†®¹f 4î0WçîJè|™¢%‡ÚÃó¥ý*Ö¥)#öªsmõÆ—üš¿‚{=¶ß™0Ôz{Üô·1gh¥S/ð$A{–Ä@ݦR4³1– `è»sÓÀ»ígмv æÉÐ6ÉÎ`Q" býŸ\ ”æ Y h‡ží¨ƒÍn—-Ó@Î÷4E+»öS =†ºo\E»Lå._XE²Ë'r[PÁdŸ7æc8ŒŽ­£¨h¿ÀnBoõe5¬ˆ •k¹E率ÜÃSzÄÈš»xROæ+¿™;¥Þ”ž¿OP±ýÞ„І¹yÍvî#Æ0®Ùè²l«-·4¿–t0Q`¨Ø|g½jp˜ýŽ ·vçÃò!€þÁ£ª’¤AÂïZë­#ÝT²Œ;í‡>[>Õƒ¡3üÒ©šÅ<îò¥`g¨‰ßvØ»‹{~Ï bu¡C½²ÛÀñdw®ßÐmàôRÔ4b$´]¼P±þ ZyôH›Q€šNþA›žtá=3Œ>.gè&¥HqÊ[Óά| ¨³Y+h:‚»ýŠ>œiŽã¾Ù\Nb¨X¿ÁM­˜¶“=A'®°aå5¤èóË›ÉvÖЃ;šIàfEn7©³‚åû†IQïwÉ›‰z ë¯Ñ.œ¦1´el5i¹RÈíZ{™\Ì6àf\‘^©ëÀµ¼—ÂÛ%ÜäĤi«;7äD)¨DªªujØÌÍáŠõ[´Ó Œš *Ù¿NÛrâÃ!xZEÓýç@j÷ÖôÍÔ[ôcö^@TYß1O*¶_~xWèªdÌДqZ±j÷¡E-{|LŠ*ì3§Ú~/*6²ýR§ èb5G›Ü]ŠŠÝÿ§ªØ¥×É1¹ï󇣪Fgè‡wZ|„åm}Ò¯Ap­5ã¶«Q„°·®Üù{¬éY÷(îÀ´Tâ6§'÷GÐðê4…»vzgòòíAŠ:D̈́˞ù ëÿvhH /gh¡ÁðÙø_ýã’0à6ç‘Ìôuü0ÿJÑãÃõ  q›^’-?Ëê¼[JsÇ*Öo†á.Xc5’¡ý?„c9}¹+>öõ*)šÓé©lš/AËõ[‘"E@KóëéƒÜ6\±ýuúÀÏ#Úu«!´ ÏUZ©Ë{MÔÁ+ŒMiŒ$¨Øü&ÀŽ˜1”m¤£m¹ð¨*Ä…Ðüï=µA‘^, äÆÄͧÚŠ:ظ]ú5OŠæ¥›±qµV€Z7wgŸ%Ôa_?èõ®ÐG¾¥Ç/™pBKê_Q8¼ûú2T¬?‰X §g2´}÷mpoT ·—][xÙ!’»jb0ÌfÇ­žz‡Üž•@ÑŸ#•àÐ`)C»·±bÛö4¸8‰~Öp ¨X¿õý´ž}•¢'ÓN€ÚÏFmz¯˲Õ)ê÷̦ô âÎxXG ê1ôÖ ýäªþ0®è?å±p±Ü‘¡a?máêi'î=—_tHØ1Š®¸Ò™ô-×ãŠÍoYíC£%SôË{ 6[.’ûjeÑÍãÅ;Â)š:ú´ôêTîC§rI`ù ZåÛŠ¸¸v"èêûNtˆá!n™ƒ5üb(鳓Ü1àŽŒÙ-ݼ֘ fY»AsZ÷àÇÇ$ln9W¬‡sÉ0ýrCWHËóã¹›,Ò`¶ùjŠÞVØ œ zzH0@ýf†s: *7¹äØ­ñù­5’‘óý*ÖïÎäÃðmºA¯ßK€±¸7úFÁgŠªæ‚Ø.Ž4,žö}Ù‘¡‡´h”êa®èSoÔT˜Õu:C‡šGW8q‹{oZKÑRóhzr]/†ŠÍÏé+eé[&34vBZÅi×î<ªµåƒHRõ$Šæ›¸û2u®Bç8é¢n§jä`Eçhj߬μ–r79,fªêš<9“¨\;À­qëM[¥Êê–Ÿ;z®áš4"o¸bý÷û$À…ÂÎ }s=üïß+/PÔÆÑ öv,$èÆA0Y¹7Ó÷ä>+£èÓõÀMÕX@ßèŒ Í·'QÔJ§ ]T©ÄP±~YC|Àû»& §ïšÀ¥sý¹F'΂ô^A뜂SK  û½Í@/-˜¡?Ç¥‘ºEÛ|ÆïŸÄPª¸’ᆌ=FªÆdq_&®‡Š€õ\±ùŸÇv…Nš]zëÚ7âV/OÑ?xTwæ¹’[Sv 軾RÒõÔÖ 4\£ë½h ½˜U¬<׿©!ÛwÃ’¢ŸZÜ…ÞòW¸«úŽdŽÝ$hAò@6{¡-CMçÁ½Y]µˆŽ ×zNäŠõ9häDQÔ¯ácŽj­¶¡–of¨â"e¶Êz- Î‹'-‹ë_‘Ôž½¹»–¢ÛúÞ¤áw^p5O~ íÞG÷ñyúUY‡Û¨¹•¹û¿“¢•õ+Y®}=ECL–ÒI€Ö»±t¯ýRT¬ßR!:¬# ½¶.6…ÈqU´¢ÀF£f8*­ŠËú©µ¼ºr¢™ ·Àæ¹¢€Ší?´> „1Ë­¹Ï͹† {áþ˜ý¨UZ-DzÔTl¾w XߨÇÐ^§a 'pÅîÿÇSUìÒ±&[¡Ò:¡zÝKjTNóŽUÌl‚úûï¥ÙC¹sûúÿÔÝ€Nxr„Fø{s›ŸùÑaG·Ôߦ#s›ºE@óã5Ø­ò)šÚäJzM b¨XõŠ Ôx’&C+§E ögõ¹µó× šÕÍ??™ä7´p7D¦åÍjYlDŒ¬>S´îÊrr¡÷5ݹ¶„Þ_¢ÈP±~6_ñ¡Åí×û ¾Y/Aã×eAý±€&Ž^q\ÃÎ{asÜDn­o|UÜDP±ýö‘°f^$ }ÃÂ`Ðâ8îžvAP¼r#÷eìrSMƒ+6ÌÓ8ÐÉP§è9§þ°Ú*€ûjØ‹@x#7‰¢Ó‚àòÄdîç×ýXºUŽ€Þ|1žÙü¢ë†Ê³wž@PÍ‹o„ú–Š:V¹Ó±.“úC«„øq(Qø¯¿¶㉌+Ö¿vo‘Tkc†zP uÜM 4‰[ÅP@s5è—sÿ5êùÒh¥JQÿg$«ìúsMÏw„_nE Íz  «ËqÅúåÞË›ÕCjøu+¼¿äÎ}—E üé>@·,H,íöpum* Í•ŽÜ¤Ïdî¹`®Ø~ŸApD1гK×BÕ›,,Rè¤LÐiGÉî›?øÛ,ȺòŒ¢ãìwÃñç¥è<ªn!a¿iž¥¨ñœm`1ø7¬Ö‚'{24¢©žÞšÀ]! &ã-ÇQÔ¸üŽTš_æ®ð—†ÿkÌÊzFÑ•›{@`ùn׳Еi1T¬µº1÷øAÑÔwæÄM¯š;èU–å›OPuç^ìNi8E+w?•Lÿ©ÍÐòÞŠpîM7Å¡=Ì´ÎáV»îß‹ë´À†©õ&¨ÊDop4ì,AMI$ í è²êD2ÒlwîG¡°Âœ ]ŠúíŒË\±ýíú,­ú<@µ%Ó!ôàA®Æ—×Ôr«3׳*„õ´›NP±ùf© ÀpÑR@3® $¾B¸ð¨j·NìÆï͸Ê7|ã>yûSx{/™¡ž‘—¥’W!ÜAêZ¬aòQŠF6÷b§ŒÃª»®'Ô]\ÇuHJ…yn©Ü‹ò» k¢# ÅÍÖpiŽ2W¬Uá6©}@E[Þf £ÓpUÉš“5’¡‹ôK\r»I„…_¸ Ç+!·w{î׳_È@•R4{j*T^©!¨X¿˜Ë°Ðáï×Îÿ§}بz¸‹;Âõ+Ññ h°§øDYsWYÎb°·#÷ÝÖeLÛú EÅöCŸ‘°V»Ðc¶Æ°K¾Œ+Ÿ¨ÃÚQOŠÖ·?A•CGTlþø€xiBû€j­ùNj´í¸ð¨ÖÍ8 þ3žRÔ¶©Z}.çªnS"OŽahàë÷ûnuã¦YV ‹´‹¹ƒ?ï#•¢¸O®ƒ¡œ17¹U$ ýb ç÷ZpX’yÌ„+Ö_ç•ÝÔ÷E/”¡ïT:0%Ÿo³â~ý<ƒL-lÍÝ6oèšO¤èÀG@³!™{ß_Ô»”ÚúË_¤mûA\±~†EðV(¡hOr ÛÁ]pÑ^‡,ôNòxèp2»]?›žY˜! dÜd¦YéÎÛ¿ºŸ4C9 ë㾓6§Ê¸}Íf^[ :fŸ ‹¨l ¨èOµWÈI ÑeQt‰{1÷8ª²Ÿ ça´dEesÏpz Iƒ¡²y— 6Äeû†Êæ{#†Ãìm¦•Í}Ž^&¾Nò€ÊæbýýW6Ñé‹­*›ë]}%×vcèÿùóÿý´‹ÜcÄPÙ|ì P Èôwû»û  ¯T67ÖQ…õZ€Êæí§YЇ¡‹ú»û„^ Za‡•Í‹r{IŸ¼ÅÐß_W©ÂBZôwïÿÇ—UïO~ûÏ%ÿf£qÉ×p´ÏûRsq÷¤òzðQÑghIF<|þ2ŽÛ´?âÖŒ"èkM/˜ù|, Ú·Iã³zc÷|ê WË ™íKf_ì ¨«Þ 01‚¢bý•ÀݰŠõ3‡=#q?˜ ºç]x±õ߯Rû¹aÙ4ú³ ž¤jaRô˜ž>]³~" ¾³"IAø®X¿Ã3zÀÀ›°.'•Kœ¹O2X¶Óªqq!óŠšÞiÅìl¢êÖËLzóóUŠŠíÝwXrqy2 ÁÆÂ×ò î-ß@˜Ñþ·W‰9;*€+6ÿ‹Ú.ÈÜߟ¡&þP ¿€¢b÷ÿã©*v©ÁTÈÅ ô˘·DgÔ`îNír¸H-:ܼrGݤ¨«»21¸ã è(½«T©Wé²2+~µœ¢]Úd Ê) ý"MÖ?¥èúâ%¬¤Â‚¡býͬMÀ<ýEŸwÒ‹‹{¸ùÏLáEåp‚N¬ ˶ÉÜä?º0Ëž¢sO|ä,~C'÷Ðaß2Mó?JÍ®jT¬ßþ«sÈŒ®=]$ú%#è¾Zlegy†ZuGµ>÷ãz„n£—Û÷§h«wzÌè¯m Û¿Ïÿµàz8ÐíOFQ]‹<áäªo$Aë7_#Érǹbóun ¿’PtoQ 2+áþÁ£Zè­Á¿†ýJBnÚrÛž¾rµ÷¡û§ÀÇŸ•£‘GË®áÚ<¹.8Ÿâ®.è‹463t¶{ˆîfÀýÔþ éO¦qõ¾¹’Ñië¿íÕW2ë‘EW[T‘3Kœ¹Mñ¹ðt²Ð-> ðÒ%ƒ;¼K=mXBÐÒaëYEêC)ê|P•2çí=å ]• ëçU@œÏÍ hˆK)‰ø©ÅÕRBVOìÌÐ@á,ш=MÑè~áäμ,†Zn’þšÝ‡+¶?;r+=Ùí ®ÓRã‘ÿuÈÌ´¯:—xz°àé(*6ßÂ9“Í4 ÌK¶újn+)dü5гýƃîF_îúÜêÒ¶äSq CÓßÅÁè@y®Øþ&ËgÔ7/ÐõÊ*l¬C.wD«L¥ŸC-N¤G‡uàŠÍÿ´`)ûº ÐÀ÷oH†É`î<ªÝ'8À¬ˆ-€›5ŽÏHåæ·É%‡%èªÏ»Hâ îâý»Ø¢@{î¥À±,yu4E¯õN í³*[ëp÷¨œíãš}øx¬|¨XÓšã¤ô`=EWkʃ««2C—éO%¥/}í;ð*ñ9aÊý²%“õšÎÐf§zá‘ÆŠ®‹H€Dé~†6öôç9ë׿¹ˆLñWô£]¶ZŸû$]›ô«É¤èºö¤8½C¿”½/gº\²Ž=ß%EÅöKæh³‹ÒL@}4±©Û“¹+ôXõ Š*”Û²Ï*6¿nè(˜>b+ -¶9DîØî<ªÔÐÔ»åZÖ t¥G¹“c{ÃZ[ºh枺¼‘¾¹ÕŸ¢Ñƒ¦ÑOºúµR˜ ÁO&Q´´E N¼Æ5÷¡d no‚n*p¡vVv€Šõïe4Æ·tdèíË î}kî-%#X?Xèzb{6qéÈtú½‹'E?~O o|ັ‡¾_¸é=¢àšª- bý6êG³úZúúÇgúIÇÛ÷YI_êÃФµFPÝ&‘«×û È…;:;Év­ÍáŠí.–´,P÷ÓY†|$WåùjiFP2CC¦«A\µ"Wl¾·rÍ¿¿ÐÀ°¥4uÁu‚þGUöÓnüw’²ª PÙ|·D þÚa ¨l>kûwZ9ºN‚Êæ7@³VEeó¶i®,7ñ0Ees±þ¥*;ÁÏ¢–¢²yb} ¹»Ðÿóç#½éÍ†ÊæI¯‚¥ó®Íôwûu¿iËN¹6T6j£@.oCes¿°¶¾3 ¿»?¯Á™QݨlÞôh'©QGÑß3`©+ÚNÐß½ÿ_VÅlùÏ%ÿæƒëq6 ±ÚÓ.¡Æò÷\ÅðYXhàu)èê–s}W>¡þGWÔÍÈ‘¾^jÁ¥·ƒÅˆ>5Ví l”7AcnÒé{ì½:ü,=µt‹€Šõw~`"\Ô¡Ž®qÃ=VâêLœÅä>u§hÉõALû©C/,í~=Wz¼_+ëWÄ}ùWÍyÞš¢ÒÖgi@âx†ŠõKkÙAÚ\m$èê%ÑäAÀ1®äu9ɼ=“¡m¼"Ýï¶â*v1ñãÕ¹½Yâ;ñAÅöÍïÂQ; Ço£èŒ_àåXFP±ùþ‹~ Gçîî¯nŒ”uáŠÝÿ§ªØ¥ö·¾ ·õ)z"»3­ûÆ5¹ˆ´IËÔÒâ—Ô#׋Ûk@½ó ÷À@wfÙŸÛ¨® >ÆRô~· hlBÐ=··²È+nü§Gt¦­ ãŠô9<¿bsËŠ>/ùKWÆ}ðP‹Î±„¡#Ÿ“ëo¸ s‡Uô4ª4À¢™:ÜÂ-ð †×áo)úHu4œ;±D@ÅúE8—‘X×ÝýyïѾYÆMxʈAôŠª|ð&ëùp#¾í%éSµIÕd£”r¸bûÝMж×}úF!‹Z‡sóÎd@ÂÊ¥€Jª ÀñÂ7‚ŠÍw°p¡+=ºSTÍc>I­úK@ÿàQ=ø± U¸tš¢wf 6Á­J É›Yj€ö‹‰#¯Î–4%+›MÿðZŠ:¤ld7Ü>S´êvÜ< hõ“BR³KànY5ŸN»–¢æzR°ù9—¡bý»¡k½ ):ŸÜ¦žòç¹û4FÁ«Žw¸×­!$¤E@à ÞѸ¼§x¹FX1µ ·øéCpó}& 3^í…ëV*ÖïÈ"mxwF Дå ¡Ú–ÛòXBúì=JÑÛ³I÷íÚ»»#WHи9«©_Ü;®Øþ£Òpj9qC;Ü´§3]Ò¹»ÜÀƒEtÚë¾P“;P±ùE+6 略úÖ¥ˆúK†þÁ£zîHÔðÑòú mÓØ†(Œá¦íxBšw%h¡ÇSru´ ÷¶×Ð “¯mõ+„:-þ  çU ?-¢mþk,©iÏ×cŒÙ“NPã¯ÇÁ|“T@ÅúgÛwfÎJ Õxk¶å˜sõ_¨CîBŠÒD=Ø[Ï ^48Yl&hHaдªˆ{26vÝÍçQ¶‡F¨X?–.°9:Ða9gÉ‚²lîƒkzð¡yCßç…ÃmªÆ­ïcËlÖ. \¾Âµ‰ Û?&RŽzöÝÇP¹•&ÂÃåEÜöëÊéèæ€jR#)*6Ó¯±d¸Q)CÛGÍõ°Tî<ªÍ^»È»%sê}þ;Q9¾œ«}k1©£ èK'5ZõY›»¡›"ËÎø% þéªNýšT@ì’vzr¡e=r¹ÇÖæÃµc±Üë!x|ð AÅú{´·`ºÖËÚ.RŸ…,ÙÉím/®ésg~®a¢"÷vÓ]ª—ÙS@ó{ CoJ hþ°É%÷~Tœ»¦-EE—kx ¥ûÊÝûi­«>Êýñ¥>g…QôlîY°»®GÐàð3(ZxU.Ï2b¨Øþ¤¸‘R.e ËN$¾ñ8×÷ÓvZ4.“¢ME˜Ü‰›?Ó`<ÌÅЮ‰~ä:î<ª&Áƒÿ~½YÍÐÆœprÁRîµ·òL3õAs& e¥F§t¨~ɼ»–¡½ùCù‘>Ü—–™¾}@7T'Ó13ÛP´÷ærjÚ@…¹ liFQ´go]ßÏP±ù«4æÀÙ~{ºég9<ëàÈýŽªìgÈ -°'Ò…¡²ùó=ìëÈPÙ\eÃVп! ²y‚“#i¥¢JQÙ¼ÏÓÚñ~gŠÊæbý{Ï?!+9ÍPÙ|Я"¨í¨l¾~¡ (\ËPÙ¼êà~úæI7†þn?O¡lŸ§ ²ùùÐ@Èn7 PÙ|‹HP_LÑßÝ_Û÷.éêÃ*›/ +Þi™ÉÐß±s*Ø7JÐß½ÿ_Viu“·ùÿÿÓ¿Y÷ øÛÍbè‡çÆ`¸Ê‹k=¦Ó nð‹4xþc 7äú ²tÒT@[ôµIIánãã$çn‚ž›e…WC¤¨Ò•§´ïÆ«ªùhSÞ±ƒ¡bý]z“Ì=´?žœž·†[ºX‘I,FÚnÝr6ùù‚¶(^‚ÊöŠ\ïÌ4øËi0 žcòhD­=CçªZÁØ7®X¿ÒYS©õ°]5Pò¢·n\æ¾ÝZDì=¨hK-*M¸šáÖBcs†¦ü&A/¸bû%ÇKˆÍéÅ õ­{AJ¯ávÊmE>/©´)î=Ê›ï“Û…™úÛJѤ>~ÒcÕ#(*vÿ?žªb—æO·…9/W34LÝ "×poºåCð¡Oݺ<ÊzFs%îiŰ#€®àÎ&{pÇš³0åæ>ƒ=wйÉSuHámK†:mn^¥¾\±þ­ˆÚ¨ŠN¿yœìX|‹‘çÍäÞjq_Þ6bc7•sÍšaÉd@U·•j÷Mï|ŠTåÅ2ôgËqH ÚÁë´ª€¾ŽLÐÜ3MÔF¹‚ëvICpÚ4ÐÊ )npoӳ)zQ®'(̽BP±ýß÷t„bŸ†ºøƒþà náîs’Ò%C}T~©âÁ`®Ø|µíIåÔ ýÊ€ª}(£è<ªK{BÀ»% Í ¦7fpcæmöQ-Þ ¦Ÿr]#°˜'š_ Mó–tæVŸœOß\ڨŚÅTç…÷ÜÆTrJŠ®’3s‡Óëo9K,íQtvÜ0¨snÍÐ-W>Qßv­íÎäê’¢áÒÜÁýdNÚïŒàöó]YçGQ´•Q4ì­ÈâŠõ;¾U‹½[lNP߃}XDÞuUNCSìÔVY‘ »¿›»Sâ«}âê—:&…€ŠíY5²JƒjÖ°4Kƒ¸/#õ˜}yo@sÚß¡é^Œ¢b󷯜@š+bZ60æ$qÿàQ•œN‡ËÍ– ÝÔz?”Q5îûŒ8¸Ò¬Ï=¬Êm¸µ«*…¦åeõK¸J —ôá&™çKuV-ô‹Öbº?-‹ëa” Sª2¹Ÿ,¶JÛíÛÊë}Ãj°ù‰¢S4!óÁ!îÒ„,_IY@…WÛY¿×)Ú×8Kð‹Ñ´¬`(‹éaÄý¡§]|Wso¼‰¤ßT¬ŸúöóÊuŠŽLN£¡)ƒã2íïW’H@æ±]-æÜSƒ‡r¹Š·²ÈÒ­³¸¢?  ƒÀìÂF†®¶ ŽùpÝ»"÷W¨r}Î%òÕ9›ÿôi6ôÝ5€¡>PnЃûêëcEPq©Š¢ß‚kO‡qm\ŸÂŽŒž í¾¥>­ÚBÑmÊ©ßeU†~òÎ ß–s¯ÔMf®õ®€^žcÂb{• 3^Þ ó.::  «©EP±þÍöÁ´]gЦ¥Ð+ð™m¶ŠË{0tgÓ-j3x+®&°õ·m:æF+¼zŽ¢Ç¶Ò v€¶ Maºæ” býjêÛJæºÌf¨‚çarì'·d sœ¾ƒ ICXѧVš{b³ðéÕn@c{j±- *¶ðÅÐXÄPƒ½°põLî’AÄX}1·ÚÖN..áŠÍOLãj›9ì.¨ôÉýƒGõ¤o埔 Í÷ÀÇ5sêÙë ¨«sko]…ꄗܸÙëÀ&z C/Ø ‹Ú?¥¨é¥þEn\ƒó]ˆi«T zÀ=ÌóFÑØü_ÂåÆP†Šõ/†“°tÈ‚Ž*€è[.=?dCWØNžE)p£õÙ¼¶Üi!o©´ÃvŠðʧ5òµhÏÓýXþØëgÚÑj"¦2Ôû£+Ìó0à–ø¡÷{S´uÆ¡âx™€~9<—Œ¸GÑ¢×*¤pÅö?é~fµËÐê•GaS9W×÷4h•Ìàžz“oQTô]Çò:¸M‹’¢u ^0.½Œ ÿÃQ•ý,—º§ *›·ÌÏ€ô¬¶€Êæû:Àø~í*›¬T¦Ó¦…ST6Wˆƒw÷R*›‹õ_ܱn»º*›¯ú:˜è [(úL7eæï)WæÓײ#Ü<æÊÐßígP´jÕ?RT6o×¶Rá»ZŠÊæGìbÉ ÝB†þî~ŸN•ï ÃPÙܰ*î†þîü]ß{€f«þRôwïÿÇ—UŸï)üç’3£>M°˜jèØ¶ç×*;®ËVuðiJchê÷õPË•ËPˆ-£¸ FM ¹Mé3Á«¹ A™zÁ¹.‡tšÉOb»)PÇøºQ’ÌëßþÔOzÊo3A[6¼ ZË ¹IÕš,vI EÁ¬¥ºV õ•Žbo“5¸Š YÅòtn}âˆY:Œ«&'ÓK *Öï˜J±¶#è.ǽ¤z¢37aay# èYÛëdã_«¸]ìó…ýY¶ÕYzŒN/Ȥ¨ØþôñP,íÃÐý)øY…;ø™‹(²t_q3eTl~ŸðºY¸AÐŒ)ÊB]ÜxŠŠÝÿ§ªØ¥ûßþýCçÌ\@Ÿ ¢ÖïÖrcôwÁ”Þsº¨4j›ûpojç@Ü…‡uë¿ ½A[Û†ŒozRôÆÍû Y4ÐsGä…ºå+TíÚDš°Óœ¢býÇ=þ@‹'<'(­ïÈ:å¶Ô°ê!9ÒnCýlaŠ­÷EÝMÁ^ñ47—–“ÁÍÙÜ^~#¡C‚VÄ §7J#ëg™x’ÜäôÒšÃ$Ê¡ p—t‚VŽK¹Uz&?,’{5×^>ZÉÐÇÛS¡úþŠŠít7t.ߣèz§ì¿ÿßfsõéʦ­|. ¦êsX‡œÝRTl¾—…©ØS' ÅNI´ŸåRŠþÁ£z`ÜIšû3Ð6ïU˜45’›k 1s;0ôÞôåÓ¹¾ËAiP·º+R¿çÊÜ.Û¶Ac›®·Ö`8âéÃ=åáí¬º= Ø+rÅúw»7”õÓì¨ÅzöîC7®])ŒË¤¨¡¼6ÔkÜæži>DZb–0t¡I“Ôxô2®Ëk¶p–.Aå2íXÅ«¯\±~휉a°  ~™C„üÁ^ÜkÚ=¡!u/×or ™£O¹ç,w‚‚ûgŠ~j/oó¢,F@ÅöGµÎ…¢US(jy?¦§— ¨›ëkÒ¡úV4îÂf®Øü!IµtÊÒP†¾-Œ#yM'¹ð¨¶Û8„ÉÕGZø`S]ÌMÔ ß î 5ï“#'mæ†d¯$=ž4Þk8Õ Ð;V‹ 9Ж{*ehšÄsý2žÃ©Š¡ ]û~'¨ {GP±þ1á‘ìr?9@•¥²²­yíe ŸûŽdhð½xØ‘îǵ\Dv¤rvfhëVÙPy˜¢bý†¾òeF š¾2†Íl©!h¼\{6ÒÂÐ’?¨uôI‚æ–øA¾ÿ$@§/ޤJÏ{sÅö9$ùŸµè´Ò¾•r §ô¿†:5µÚ›¿+¿,ØšÎЃQ» 9â¿þÁ£Z¥›Æ&—ö4aCûó’ ž?CîóÙ2ã8w3!èF£P–¾ó$÷Â*]¶zD{®öHáSocŠFŸŸ@î y% šîÒªC)]²j àÀëßPÄÞ};EQuï`æVØ™¡ÆÏÀü« Aï¨^·’¸ìÂuî¬c·È É\®¤| |Wéhçu¥@OµåŠõ³Vˆe[•åzÿ`0kÐ9+ ƒ7†Ð öLŠÚ˜ Œ©Žú‡Ì¢“MJ(êrg] :\±ý©^QÕ¯ž =Á¤¦5 {ÍóáéOWî¼ïöð¤YŠÊæÏR5áÌjK‚Êæ7V:Â_ÞçýÝ~£Ç¹°ZŠÊæÑ}tˆé‚)*›_‰ÏÆo aèïîßY¶LÝ5•ÍCÃCÀKÎÐßÿÆKT˜šýÝûÿñeUëÕJÿ¹äßLö —ëµ(ÚÐT. ÉmYÞD{üé –w–*¶¿•z5¹ø ¸ã²H»þþܱÕi°Vr• >æ@uÄ]®Øüz¿HÈ¿šGÑEÁ©áÜÿá¨Ê~}ÀJ«‘¢²yæÝ`7ÕC@eóiñ°ÒæAes÷{ lÙ™ @eó'^{Éò3Ñ€ÊæbýSF>&+¦ìT67ŸáQ¯"•ÍŽª0aEO@es£ü,ÁÒÐ Ðßí×áºÔn¢²ù›”Knåê•ÍV<–Tyùôw÷ïl[¨l>£ûQX«œèïÎ×ê¹”weèïÞÿ/«^×¶úÏ%ÿ¦º|7p+õdhÚ ð]àÃý®S vo^TéX&|h èž_·©ý·p‚v­ ôŽÙ nSñ²18…¡°åãŒ5Œcÿóøã¹ÖëõÄlµ÷4MHP—#ý R±9 býÆ=Yh~¾”+Ö¯6z'mœØ ФSÙÅQí¹6#ßjqU bžžæ>!WiÌ¥@»6®dû"*¶ÿñ¨‡tz½ PËéJ̉ær}üªÁÄA‹¡/º¦ÂÒjŠŠÍϘ0Y2ô­{ õ©öæþÅ£:sqDz«2ôÕ“xr ›¢Ý“ˆE: )¾±4|‰÷M³3´,a%Cï=ßEìÛDr»ìcx% ¯Ï™×v@è (>«#C5=¶CãÕ£\±þY ™Κ4Q…Ž{ÅUy¼L-½ÊP}'WP3¸ÅýdýZ…/Ô¾>[gqozWÕ²‰ =¾Ú ^<]Ãë'›ÏNôB@0«°tзg7¨¬³ ]š– ÇÆ§qç®ÈÊÌ·tÙµ½lû]¢bû-gj1Á‡jmÌ,âR¸–³ã¡c«DŠ–UÅã*6Êè,ºæÛP†úí§ï#¸ñ¨ZG…ZuŠzÏü}d— h÷®&,¾ã$‚¾HŸÄçûRôÀ`Op52`hú…E`SwB@ßšÑñ/ºZ–@©FüiÝÛ.Ì´3}ü¢äÚÂë#ò0-/Štù€tvÉ2®·Õa¨:—ÁÐós!vª÷Qa'øa¶ ÐQÙ—ÈLîAÅ5 ½õ˜ ëc Hµw!W¬ß·’m,ýXEwl]Ï.~nÉÐüº"븂;½Ùy0^hÌlq“úòãö©oGœünPTôcÏ•þ,¥{4 îyC˜•y·±C/8ÑqW2s4]>öAÅæÇèÞ"šÒz7Ûkirÿ‡£*ÿ Ú åÞ•Ïßéc…Wû2T>©ß>øÅT>¯U_A½Üö¨|®[*¿Î*Ÿ‹õyQ…Åç›*Ÿ?‡ëgU*ŸÏ=ªZ!Z€Êç[¤ÛGú§ýÜng+3T>gó²¡ÛÏo•ϫҼIÍ…n ýÓý_›†³õݼ•ϓשÒÜå[eèŸÎ·¸JàþÅú§÷ÿã˪õÿºä¿™¾*‰Î¸! ñÁ TrX î¦AÚ¨£€¶·Z -Óʹ†’©ï‰î¥Ðú6°'W·«Ñ2¡êKÀ”I7(ª54~àB{ÄÒV†Þ€Šõ·wU„eª íåþ‚L ¹OÑj `M§0†}²^Gìæúö9A§;«QôCC-uX(“¢ÙcöB@Y4Cõîë¸V\±~;玅‘¿º:˨+¤|éÅ­[Fýbb(ê;ÚHöeù=nû>p&½C›>m„Û½{*¶ÿÛã®ìã=†²Ó=½4ˆ[rÿ÷ÇÇòK2T±ò¼U¿Â›ßôQ[Ö/7ÐÉIÃÙ˜7Ñ\±ûÿñT»ÔsÙ>ºæ³@дÔ7Û‹ûùêcâÔå –û.œæªÌbÔÿç3‚.¿ôŽ–;ö4¤½#Tv:é­9üÆMi×…)Ô=ËGÇ&ìdr—QT¬ÿ SE¤©c!EoïÉ!˯p}ûUþ›ªìùO=¤èÊ=³Ù¸ KîÀÁÌêéX†¦ÞþQÍb):rõn¨úà¨è?e¡´êÑÐÔIPÑž[c@.¼É§h?…‹¤¢n!7^u1þjèÈ­ä¼â&®Øþ&¬¸å†útíÁ²UsÏ{|ïÉ n #aàª}\±ù+ —1Û-Ûú¸1ƒ6h,¤è_<ª‹¢ÞгåÛ úÙO“•ÄpSOéP¯%¡€&–ÖËšGs;¿ógë.ÜûrYÈëÛR4»6 ®XÇtmC2¸Çj»rU8ŸÁÐÈîÇ$Ž .\±þ'ºž$Cž<¦è<áñðoâ*üx ŽS^ hÈÕ#`£r˜ ]mú2«ûDzOÕmq‡g9€v‡µŒ$ĨX?Šîàí« èô8€¥?͸þ«Ï’‹6ºÝž–LÎOZÄm(Å\ËÒ•< a_¦XTlå C&iëÏÐü»Ú,eú®²b'8¨0Ј!fp/r2Wl¾ßM6¼Þ}¬Œ»è¡Ì¡d,EÿâQ½ÔdÁæ¥l#èøþ³XEÈ nN²ä­¶! Å. Wuçz•og©[-:ù¦2û9ÏëÔ¥tT¨OðYÿ¸±B8xÙughèÛËD­æAÅú¿Ùzù6cè̱CÀâçkŠÍßµÝV´~d(Úµáš-%ʉñ õ??™ø¾ñçVö^A=«;qóºo!6 ¹býnŒرÐQ7|”ã¸N)Ú´]níÁP•þl\À[5[¹œÅ,øBQ±ýî²–l¥ên†žú@ýïfpýž‘1­éÿšZÝ#\‘ùÖM{Iä93†^i½lPô/Õ¢£YâÅ'jÎØS)šj5LâÖm v†ÎtÐiS®á!FïìÈPaB= Fp­7ÉŽ•ùzÉÞˆ ²ÝÜŠÇÉû¾Ü~>!Lí¾)W¬¿Cª+ìÊÌ¡(m}óõ¹§”ãa™Û.‚]ß3Ç›º§Çzâh³›¡ÓAþ,îŒ0¬^ÊíÌîÀ±Uøbý,¥ía×ç\@_]Š!ûß_᾿·fßÊÐÝÃO€gó\»NSÈ-“Ü´ñmÁËó½ ý­¹á*ÕÛxœ¡¶–{häٳ܂²yÒU͹ßõß(*6ÿÓ‹ Úc7AµÎÁ5]@ÿâQ ÕId×nÚQ4ga2‹u½Ä=vëluƒ ýÖ)3¿´yR45ù£Ì k C_÷ì ‰ZÁÜ!%㙣v ŽÃ¶²f¿¶tÀ¹a¬Ó#Š~¿Â6 ´g¨XÕâ0¸eU( ƒÎm‡ò.” ¯Óaûüy€†d'ËúsÏïŸ k46ü?Šz˜gÃó¡ÕÜI%Du¸IPѧÚÉé²sE倾}žî»]Èõ ®‹WŠ&UUã“Jì{Á‡‹V5ˆL€î-Ûß·èÓòm§¹ ˜T¦ó¸š¢?Ÿ–‚CÚ'}r¯®ß]hÈE ¦Y#Ac¼GÑÔ[Æ =·b˜ê?¡h–I ¸8uŸëO^…Êþ€–K“`žò&®¥G‰ÄܸfûdÞ;¸fBàý$®šãx¨+ÛÆµý(£~Õ±\—SGØií0‚ŠõS°{(Qrñnˆ–DqÊXnŠí0||Š §:¼†º)c-8÷ól¤E ·¥_2Ö!›+ºÿ|ˆp§E/†*÷¸.œ?@¸!ö •‰áp¼•÷ô}-¸/Ñ`èúT{¶0š¢åilØÚX#¥æ™•üœ ïœT]?Û”>9Îë§zqˆàr²  “ß)ÒªsêÜV Aph‡7wXh0|ÝÁ½NA/½E=Þ÷†Woª*¶?)÷ IïÆÐOŸ5àÄÌ(îÉå#é½Î€Î Òag,ãŠÍ¿šá#+Ÿ5Y†öéU-ôYÖ’¡ñ¨ZÙçH^-P`è-­$aeeî´ÊðQ/ ;wïë&?nãÄÌ=âAe•~´úIwŠpŒ¤u¾hÖ !ëi wð‘‰0¬å-‚J_3’iùŽ+ÖÕš ©Ôû”;$ÎÚÇmHùŸ;Q´ŒØ@ZmÜöJ-¨ë$‚–Nô¢#K’¹Ù7;1›¯]UŽ gµ&z\±~^Q¿?J.*$èj+4$ì…€Ž•¸ƒVË#€ö‹ÒÉœóÜõ5ÐøÉˆëƈ )åŠí7 éšOcêñÅfïŽç~íÎn}{-  ‘¶tÇì– ›ßz„t“3tŒ[¨N пxTs½û‘ 5ö MnØE¦vYÊ y}ô‹аˆûPÕ.„ +’éðoYܦ1Ÿ©WÆ÷^‘}Û è¢Òbj:-›^ÓŒ)9%qc÷­bŠû—KP±þ] †jxå éÓ:—û6x”ô*¥hw¿"ÈéÅ]¯v‘*¬™'C->ÜõéÖ‘¡Óöº³6šÀ]Ðû©,^ýEÅú½yŽ®‰›JÑmîTYY‰¡ñIL94§f.Ýè‘ÂõÖŽ©ÞÆÝnÐ@ÍžÏáŠíÏ>°VùÄ3ôf“h¬‹å^½tŽ”äm è×·Y$«aWl~¤NLÖé(ô ‚‰!Üÿá¨Ê¿,g½'ÝFù3T>WMªï7Æ€Êç.wëe{½»1T>¯9¶šÅ„Z*Ÿk`ÓÒ(*Ÿ‹õÿµ~™¹ê òù2¡ôl¢*Ÿ/ø® ÊÎÖ •Ïu&“o³¦1ôOûéV+ Y*†Ê笼œfM¨|nó!Ži÷úFÐ?Ý_°/vgG2T>_7 ª†>¡èŸÎÿwvÏôOïÿÇ—U3T[þë’ÿæ)µRÈ?³œ¡±½JÁÅÍŽkÜe=í±²+ —õééµ» ªšøžîm±ÐI;}™e§‹}TX =s@Õ§Cå©ÃÜÓ5•VõS@ãl‚=ÿßÿøÿë¿7ƒ0‹¡aµÙùi ÔÓ =û~ËŽ Ú'=™#Šz\ÝGôÆÅ:Ç'ŠÌ¸¾‹{ºÿnÞ8œýZ‰l[Êë—4ö#¡º+ÚVÁ òŽçZUM„þî¸nK&À’w¬r%=sêAmk¦‘½*¶ßäõNáÆó µÖS"ùSÇrÓ.f²MG¹…öª$^ëWl~ø†XxíEQ›[a |~ WìþE[e0¥‹úúa„ÐòÐG‚V_-ITl講çiësŸúxh½{°9CÅú‡8¿•¹©£hQÓ%aU¿7ܶۛSvë(C› ´HØín—#SØÉNe½ò¤‰*DP4¿»£[24t«2ðEÅú9Ulòú#zÃö÷©,› µaÿï§-z‘}„VÎ}¹MAo`ö…ÕÍÈÞäì.@Åö;¬Î"ÉÂ4†š-»CVÚOåÚ/އ°‘6ÜÊ–àá`IP±ùGHa{ð~@ CsÿâQ=røؾEQó³Ç!ºæ5wÀ;²—ºp#JߟR`èüm™Ëç ܦŒ¤×ó‹ÜË÷Ñš ÍZîl&‹_®ÏÐ{¶ŽP~°E?\žYT¬ýþí¤êö5Š>ºD¢×§rT,É)ˆg蛡nd`×Ã\7—ëRë­Ë)ÚCñ»P± O†¾­Hæ<ÊÐÞ> Ýò‰ býæ è …}´Üà)ñ?×P»Í­‚tý)ºÎVÉÊýã(‚šîœ –ãÂ5ÐG´ç4qûõ 'îõ4q—¶,ªdWô©¿6–,ž¦håH’ôðACfÔAn˜2 M—`Ðp®‡ó@–|y÷Yë8ê“mCP±ýðº´)" µx0Þk äæX+¨¶®!èÌšáì¼Ë^®ØüÏjÑôÎË@f§`Àý›ïUáDž~BÑ'¶¥àK/s‡µ…^ƒ,Úwÿ ˜>­„¢æoAëßïqÐ+³ã`èl; ¶‚no:º[ ¸šÎ­7ÏT%ဦmd7—]¦¨XÿAχQ2ÔvÉmâSªEѡՅ0«~Coœ»ª ­¸ ˆ ¼¹Ó‚ûõóðb¤ µ±ÙÃj,›Z£>…¥/6¥¨èÇš7®$}}Aw–¹ëVeÙQ=°"_ës­wåÒÄÅôÌ%[r!Ìœ¡bû»:NŸ1† ^áú jÜ6»¿ùèÞk´ãõy›úí4-è`"C§Öï´ô—¢ÿÃQ•9$Þ…ñÞ©•Ïo9 Ò‡;e¨|nÜ┿|-EåóÏñOÁþMAåsÝݱôò@JQù\¬¿âô‡D#q&Eåsç£`õªEåóØÖ!¨º# ò¹GÂVÖÇ•¡Ú¯nÎ6bf©¨|~Ø; ˜Vo@åówÑziµ ýÓý×®­Ó¯)*ŸÇ«ØÐqÄŸ¡:?CÑCfÒå® ýÓûÿñe•óíÿ»ä¿¹À.^Ð~3Щö¹2ëÎC¹–ö¡¨nËÝ;®”';r¯L½Lß­ÞÁÐlZ™FØEndèvií¨tnáG_b_è͵,±†~g‹ÕPl+‹TJàŠõoçž'®tu#ƒc mud¹3Ø“ ±©Å‚­’÷mh 5zð•¢.ISIä¶© ­\”Ž-æf•K@#{˜€Šõûtm=•ôW§h¤ÚU,YÃÒm ¾?—¡Ã kè¶–ûÃÜlc£ÔÔëQØWMP±ýý¯À„³ÉUµ¾º#ù‡ c†}žÊЇ¦l‹óyŠŠÍÞw, ™lGÐ;]V²&Ÿ¾ »ÿOU±K=,¤ãlm8™I·nÅ=\íOVÎïË Y¶B8>ð¢a²4®OËÕ‚êþ*µÞgH¼N\“¢Ž%©à¹¿C+ú÷ʃ ÉJ†Æ!+¹þ½FCÝó[õ¬¥ñK›3T?Ñ ÚûTQT¬³I ·+ÐÔcë îâynÐá=äÁf†®93êöOçúO”Ý3‰ëñ94_oänéu„¿Á=6€ÖVúpÅúYÍR`&a ]øeí`•Éu%–J¹ds©Åº#ÜÞ¹Alî‚>1YÊfœ¢bû»>„ô?d¨ý°Ç P0—ëѨFãê0º|ÚÊ›_Ñ{ŸP-f¨cq?Xåx—ûjŽPCËg¹ôXxý<@“ýl)_ðLŠS%V¦­¸ß¶ÃçV1]|v'X­ù, =¦ÛÒùAÏôlGJ‚4¹çmdä… hã@(ÿšÂë_7z·Jð{Czžý·%WÒ!ï®"CædƒæÅFª|ään¹ÆM¹úˆtÛñ„»,&ŠM®tæ–çGI:úäsÅö³¨¯r*AŸè?ãMUj˜²¼2½7ë,hhHd¨Ø|ósàóŠ} kºKÈŠþÅ£Ú²ö&µM¡ª\{™ª¾ÞÊu8¤-Ë]T*C«½ûSKÕ³-’Žƒ…iF€z-Œôüu¹~ïˤoôV|w–ãÖ‡›ö2¹*\ç6N²f?)1T¬Cþ&R5³Ðø§…üo§¸KÓ ¬!H@¯ïÙåJ¸ïòŸãuu@]φU•C¹.ì.$œÞNQÅ@õV*֯ɱ3Œ[sŽ¡vŽ`óà(w@ wŸ~Ÿ»¾*’FæÆ¯þB»;q•-ÆC@ÈL®è3ÏA±pAMŸ<ƒÎ Ü9³#^q[©äë*3®èâ hu½%E‹Ûf¦2ô/ÕS‹¨áìu—u•ZFp g¨Ðˆõ„¡‘7ÚiÝ·sKU©Ë+­Y’#Ï2(Zo9‡ü4ahëö{èÑý)\sçN,VïÔ¿-›á]ùbýËï·•vÈTõÀ"YÖñÝÜ¡»Îƒ‰¶=A/êWBI?}@µý²`wó‘\ïåYðÍ=˜;Á+ŽH”~ôõe=¢ô£Œ+Öopè:XÓ”ÄP•XÔ¸;¶³ ŠWs£´¤àábÎ5±‚-_t°ª>Ñõ‘ bû©ÏS¸áv™ FKARø7î‘Í™¤Ïª\ZîKÍÏxsÅæ¯{~+j´¹#˜ª¸rÿ‡£*ÿêøû£…>ËPù\Whië÷0T>÷“¤Ÿ-9ŸMQùüã5CòZ/¡òù“P®nÂPù\¬Ã×$Yô³8@åó=]K ó²D@åóöú›áóS ¨|ž:|«,{)Cÿ´Ÿ¢_ ¸ÛÂPù|\øPßúƒ¢òyf{s©†ï^ýÓýÕ¹÷`²ƒ6 ò¹c§vÌ( Cÿt¾Â OÙÏ7÷ïÿÇ—UëBZýë’ÿf'‹£¤Ñ}/ ®sz“ï³¹>ýF’éôjlîLöêq ˬe#OÇj~L`6§¸&›Ö³Øqš(ÍŽ?@QýOÑlÍ“"‚>Ÿ7Š•ku¦¨X*Y3m:ÝÏF-äF;KàüÄBŠ~ð €÷Z hpë0l¡ A Ï;ƒáÜU‡Û‚ní@Ý7X _ wqÅú-mkA?ûlchÁ‚ Yÿ‡¾\ˇ¯IbD)w{Œ+¼­(äÚ-{AVwäzõ†n#Ã*¶î‹$Xý|% »v¯²5Ü™‘>°Úé)A-χõ ›ßyíyPð‰’¡ïzCse@ÅîÿÇSUìR¿^Ž’â”P@kSžÈJ¬½¹&Çb¾×Œ¡ÒXò уk=o?[f¬hâådæw­-E[Ndz—¸Å½\™ÙÊМ˙²¶ë·qCo¸}ô ëß/q)œ¿uš ÑËœÀ§g×uàj˜ËÚúägØ]îÆ•²¤hc A'’ˆ7š€z°mëÆÕüÀ–ŽÏ¤¨X¿jÍéyå -8g'™îîÅÕ5KC‡4®M³3°| 3·öÜn¡…ê”,¡~×>Tl¿ËåD 7ªM¡·Ÿ×¢ëVRÚÌ|7´¢¨~Š ;}N‡¡–> _ß×2T>‘ÔGsT¬_ºÑE’?'’¡3ÇÁîsa\·÷!P0 YÆM]9É;¸í秉J‰ ÍÍ“X–ÃÛÿP;æDúqE, îǽû­#h®Ò`hܕݰÖ~Wl¾jÏMTwÿ<†f²2ù÷ÇGô/Õà+}¨KéD‚VGÉ&üz* æy¯`®.CøÖÁX²ˆ¢7Íßãù µ üý§Æƒ»öÓ1PÈÃ5L¸ÕF¡2ôè9F–<®bèå—›¡X W¬¿eÏ,™·ÿ;‚6Þ^D÷²Pn‚™{º} Áa,¬t0wG“«ÚfÉÐûÕêìÐý™\Ë[K@³žÌe¶R#ŠŠõóíá ¡SÖ2´Å‡m7v8WëðJ¨öÚ,Cw¯Ú ©Ÿºèq?®_ÌÐuÝ¥`ðä÷ÓÙ¿ÿ[t²Ù èÞ ¨ý²[þ¾M¼dhDéU4¶Tlþ®§ËAg¯'ãÃRw'î_<ªåw"¥!‡'hEÝbAcœUzü\ô¹:%åprø+-+˜÷Ï*2´iÔ(èÖý%EU<à eY{@5º?'Gú¼!¨÷Â*°Ö9FQG§! {Z P±þ© Ô!ÃX@“¾GSËÇ£):J÷{î¨GP]ót¦¸è)E·ÞTb/_ìe¨^×ù2ßÂJî]ø@[ äö{·ŽÆ‡]æŠõÓ>RÔ22 *—*pÃã6€oÛM- ›¹'¿r=NnŸ²—2ô‡[(ä«^&¨Øþç/·¢J žj°É2—«Ùm'¸zreéà]µ+6ãõBØâÂÐA-¬ÊâÖRô8ªò/«C…TG] *Ÿ÷tR§Œ ¨|~©ózè^±¢òyïëÇÈóM&•Ïß:T“ Êû *Ÿ‹õ—:L¥9f%•Ï»¬`Š#ì*Ÿ·uúÅ*ŸGÅ&C³­Ù ýÓ~–;6ÁùXe‚Êç[ ÀÎÖPù¼`â"ÁU{@ÿtCÞ:è5Y ¨|¾èm1Ùtþ  :¿Äù3œ~ÕÐ?½ÿ_V­7jý¯Kþ›š¦í¡<·7CgÊtÁj‘·áfƒÐík* 5¥óil}"×Pë©ìÌÚ@Š–ÌN’-nžÇ 4‚B]†€Ž8W¦]VºìnK˜ÝáE7i©CEÛEëþ²€ ëÄP…F(ÍÒä.¼¹’N_#hzéf˜nåè÷­®Ä§«ׯóXAR¢É]0Ö”í¼¸õ¡.BákÆë·U¿€˜˜]Tå`¢P!¹Ì•f& s-O1ôáEHüN¹©.3%ÞÎÔúb …Á¦\±ývõÔ<´Ac×½§º è}ëW@—ê´ôL)|{鍨üì$qT/ŠÎ©½CjµËd¨ØýÿxªŠ]*±0†ÏÖ ulm&fS¸‹7«±¯oWZvs(³ëßš›³Ä“,MïÀÐB k˜b^LÑÀá }¹ÐÝRFrCŒá‰¡7¶õXV­9‘+Ö¿níh¿¾‰¢ßʃ‹ÉIîœZÐnû~@ -2$ëfqÍg6#+ :µO¼P‘~–y-…µin¨ŽÍ*vÛ>€¢býÔço¤÷( ŸTZ2•mû¹{.óÌ£ ­‹Š†q61ÜUs®Ðé“ÏPôm‰*H½Ó¹bûßQ…k$hÈÖ:j¸2_ŠšÿûGÐ/Ð[ï~ ·—OáŠÍ¯h9 ­¾ÊÐÜÔ 8{¡‚ ñ¨FÔO€_õ. 5Ïó„”G\OË~̧ⵀŽ0zF•-ÆRôcàRèÚi9A‹Z+C ôô®‘‰<²•ûQ½5s æ®òrf·4eè°DWf9áCÅúÏ«ñ…©}'PôëG?H¥ž2ô~z UéèË& 6!` ·sÛf4bT?‚:]’éö‹¢h›M6l¡NøBýŠyZWµBÛ(ÔõQ7fù¨? ñ½f°OÓ;scmN›vú Ú° ;ÔQ´y‚Ô?Sbè¥áàâ–/AEûÙÛ3¥¤Km~³/«´ìÊ]÷"–;`hW·RÐÜŸëwAJ´ûîtÌ2CV–gÇÛ¯P|“N(S¥(\B—zwåÞnæGu›B¹v¾J°Ã½%CÅæ›ºC\½çØ™mùÔ›ûj¨{.ì:‹¡7_§ÒQÜ€Ò¡$±vE§ÑÈÑâ^ŸA­–kê{ßœªUãzP‘ùnýBÑAíΓͫ¸ð¥æ„ôT¯Ï$zÇu®XÅÒ¥0ÎÖ” ~ÑKÁyÏP®Úöx3á·ç²\ælÜ¢+Ä€ßE š~Ý:-Í#(&ä­þÊõJŽ•æ<>LQ±~‰Z-Xu? únb1-Ÿe&CT½-úm—²ÕŠë)já7†µ`é“Áë7dÀnšºhŽ õ&œºØläæ.SµR˜BÐãK€©Î@Ý×µfÚGÏ1´Ï«äK׃\±ý¯b]ÿsÿ|æ(ÎÚNÑ?í×éq4uWz Cåó n7Á¿i ò¹ýJ蟰Ÿ¡º¿­Y!ÍîBQù\µq§,¼ Ð?ïþH›ä™d2ôOïÿÇ—U™ûÿï’ÿæó9'HñÒõÞs–TMjàŽÖk©æ“e¨î‰W$rBºUõ?ļf«3T½í46=7•ûQ:geTos1Û§ ¨V~.‰6h¤èÛæ‹@·“2 bý‹«~AÀ -†&¬Q¶J™œHÑ·Ec`щ‘ ¥óAr­;÷À–‡àgšDÐòuepPÝЊfñ ´™¡sjnÀ€Ã/(*ÖoëbM.‰¢îÅ:Pš½—{óÂSHù0ÐC¥ð¢ýNngçÓ ÑèÄÐFMÐã` EÅö+Œß6|zûƒZpùª$ðùc *Õ# û¤h½FO¡ý†=›ß¬¯t,ºÂP'ÕSàAó¹b÷ÿã©*vé­}WÉ}FŠæÍ«!9»š¸Ž!/HmÅ <¯ÔôwPˆÙ¹êã ýUšOšOá*\k.5¼y— VóêdÏöTq'÷ &ÓçtEÚ>\±þaêÊV Òô|Tˆø=o„tR<žcÂД‡‡ ÊĘ{Yq;Ì<—èµ3WÉŠ©iÜäû‰àå=‘;Õð0±ûùŠ býò댠ù[E}O0# šøb$ ;è…3¶P<é&×Ðý2,hãÇ Ól WÇláŠí/Ÿ;I(î“!E«w†Û­“¡~¿éÃ)·Ùw¡™Î6®Øü‡opr^†ÞúžX´'è_<ª[’aQÑŠZÖ‚„Û¯¸*Ϻ@y†-Aë—ÂÐôçÜ£'[BßJ†¦G:C‰/W!¨ûà\AÐàŒ¶ ëdŠ*N eÇ­4z:GvÙÉëOß×ÁóE@—ô½ ôç–®‚U”¢Ç[|‡ÏÆ„  Mzgë;º·èž4ÆJ•[‘wMh¿1› ¡½ìˆNžCÅú]Óò€º«(úkqxÓ*nšjiþö  ~›fI;ßÌþ·'|ˆÛP'n÷gWiü›GÛoB^ ÞL†äi%\«.ß$£»2tõÓNÅ›?²K>¸v¿IÐ 1YðyÅ@ÿâQýh öê·(ª§M ªÃQ®ÓóäNÁP@‡>2ïÏç¾ï}ÊubèGÅ ðIÞ. ™ÓF1ÇAA µS-K¼w;öúZ82ü.÷Þ ¸_$¥¨Xã³g`ûU;@g¿Ü £žNçz_©ƒÇO&rßM: ÆÅ»¹ ®ùå¯* šc¥MËep'tóöÍ(zÞ~;k!¨X¿l0ï—@Ñ)‹ ò–» n5ŠîzhSÏ 4pÜ®Ñú‚÷ŽG5P7úž©2Tl¿—Š%q¬nMQÛ.3ˆõNîl&…ã-ìÚ~Õ5pÑö—¡bóétÐ{± P…­—%÷C¸ñ¨>·ŸW¦¬§¨ÆsG¼A;ÿ\õé¨þ¡.L:´3·Ã’ @›½"h¹Ã)¢¯¾ŸJVÕf04o´)Œú!ã¶/=+3ò šãB¶n›¨è{U“ÔÌPwÃͰçãîöëÁkPwá,3˜^q˜;ãž/-nIÑ|¯T‰­þcŠê~^ ¦Û:iHs–S’ÁëçT~²Í=SWÔ;9?”¾J°áú5£æ×soLì±5ÁZ|a5Œ_ÞP±ýªeÞ$ú˜EãÕƒÈ ç.\×át]A×<º ên€Š>Uo¬ Ï>”Tc\±¤v}´€þÅ£ú±ål¨Þ *A7ožya:xl,›ü)’Û~ÇX–Óó¶VŠ&VwÏ èg• ¢Pt™›×ê,Ôµ8ÎPm¦h;»#7w«- ~º_@#–™’VKŽPT¬¿ßÂ%p`‡ Þûæ@“õdnzÔ’·ñ$w–Eo²i~1×þ|[°Xü‹¢O¿{€ÌfÜØ¤%Ìd„ ÆaGØìK­*ÖÏŒœ…K*sµœš ¹Û¸ªÝ~ÐÙ®¸©=X« WjuÏYêÖ{ þÞýXͦî\±ý_ûG5? (ê²;–4ïÄýh¹*îj½Ã]f4å:Wl~ºzŽ p~¤€fÍ!¿ÛHÑÿá¨Ê¿\ŒùjQ•Ï- fžË(ú,¶Ü"áʽt}ÞÂóB_‚Êç'¬2 6é› •ÏÅú‡u€j³•ÏÛ¡ ~úõK‚YÊn‚Êç>zɲ·®Çú§ý:øÃó®;•Ï›¶ a–Šñ*Ÿ+Oñc­2 ú§ûm&$§ŠÊç£×|£› Ð?ŸåÜ <¨¡èŸÞÿ/«Ê5ÕþuɳR± î˜èMµ:ˆðãºzO ¢ÊÔªí[’9ö÷ÕP?èg’OÐc3`s'Æm5V¤ýwPÔbyoH4+’¡-¤ŒøÕ„TK¡51t=/ bý½ÇøÍ£jiêH¤GZs w®ë¯Ã¹¯+†õ‹qÜ‚njPqP¡­vOéœëÝXI¸½"hõòdÙ*ýÎ\±~6߃3Ì¥¨†‚Õë‹çô¢SkTY ¨Ó´#Lèòž ÞTâì †:ÎR²²8| bû^òeã•dû²¥¦×úuùSè³g C÷VÞ„Š²á›¿æÇ)¦ž=š ·†¦±Õã*vÿ?žªb—ª½ ßwEzöÑy¸šÊÝô)\kCÐ4K_¢ï¸™Ûï­9ôThhûÊ]ÄZÈû}™T´½)Cïăß‚ΊatËpM†¦uš I÷C¹bý íIqôE]1–$~Ëäîߨ*Wö!è£K;e-ׯ;,­]GÑP g&súI½6 –¡³,¦¯ö2T¬_þàfV5µµýHfLT['‹ùM±“¢¶V±Ì]ý6åÚ ÆÙ¾€Îÿj’Íñ\±ý¦Òuì”Â.‚®ì±ŠišÌXÏTqs>-» C@ÅæguÕ`e/ö34G}ˆÌóð!î_<ªi?€ãäC€nÊÛ dr7§bdºí(‚V{›ÑrõkRÔ²ò®lǼ‚þþ.Ó8HÑQg#àdÓb@ÇVýCÜl­SÐÔÔ‰¡}â¿BŠõ(@ÅúžR_Atã$bÓÀ­ö~A:äœ%¨ir;Rï–Ãí\s4Æ©ÉÐÙº…Pס )ÖËá먆*¿þ&rÅúMßôtK"•žÉƒ¥qg‡Ïcgªz3n˜3K°å)ˆ Ñuá€j›+°ê3“Tlÿ^³¥LÁí•€~äÊ :6ãÎûD/rT¹©¥ƒZpÅæ?üý›Ó"$Ÿ¡T#Às×qî_<ª:µÁà5ä Ì—‚JÍ¿Ukµˆj,9IÑÈE‡eWZögh»”2’ÚHÑ+)ÃÁãc2Wsƒ~hZ¯/t‡ßn±{H,dÜÄ  ê–wœ+Ößìè1¡ö\‚P&-¬ÃõêýQ¶ ~¹€æ„TÉî,aUº›Ÿ>Fºìµ%xÚH¹#ß(ZÍjìÇe#œ Kf6W¬íÆSò}sÂ:¿§Üû{ôD¯É =fžD›¿Ã5t³f§˜RtA•„T=‹âŠí÷ ™Î"¯›ÉP縱¬s ‡zÉòôPôÓŒjR¹É+6Ûâ È/ÀÐì— zÌ[ý‹G5Gy"´˜tÐôwƒAÅó87g£#©í´¡?ÛüþxàÉ]üÆ–¦t hðfOð±oÆ}c8Ÿ¹Ô[J·eæj·´C×dæØu¶ u´›îaÁP±þ7§µ¢&ûŸ h£W-¨Î— #ìɈºÖSÞ>Ê}Ô‘\ kg'ST¶ÜŒÖµaèéÇëàѤsÕ¿]G³§E5q!»¹™Û´¥Œ&=½CQ—À2óz ë'LÉ"Šæ§ïaMNÒ<.©J §ãú±ÀÔDp{ØúQ££*€¯QdiýØÙ½ ËäOQ-ýv,ék×C5<ÄT»¿¨ÎݨØü#«zBC‡Á€úM‹¾^ïÊýŽªüËkò;3=Pù<ì\8_Ue¨|>¨h6Ü·Ëô?îo›)œo}’¡ò¹åP?’§uX@ås±þiÍÒô£Í*Ÿù ϵS(úýu²Ùñ%@ås]‰ôü~¢ÚOaœ«$ìÃYàʽJÛ¯‚a‹Ü*ŸmÀšÕRôO÷[å*±‘Ï’)úù¯ +Lôÿ|‹»ÒƨÃîÞÿ/«”¼ÿï’ÿ¦ËO=èè8Ð {¿•%Ó¸‰×j {Ó Š×¾‡îýÓeè§·Ì/n“õíϲb¨ºR ysÔPËØ2Ù‰‘\Á3 †Gs-ý^SŸ–e\±þ¶&†äD/'†öXHJÖq÷ö„qÍ“jû<…ô˜ò”¦,“E÷ïÍÐaÕ7IØ2#î-Ÿ(b¶®Š¢å÷ÇÀšHw‚ŠõûìÊtÏë3tHÞ"vÝÝ™8›˜h Ž-Ýô3kk$ç ç¹Çr¤ô†á®ØþœV­Ó’K - Q V¯K¹û?ŽbûÔò¹æ:'Hð§\±ù>%ï tÒ`†z¬®†È“)*vÿ?žªb—v:K.µ˜¨ÞöyäÚ´ñ\ÍáM`7!’ ¯Ö~€º£–€žlÕ‡9R†zOéIÌ+¹ó¦9°e§ÖÚcÜ!¦aKšö+õ<òZ@/*z²w}ý*Öÿð÷¯Ä$<„¡w¿YÀHÇp®ßC§áßutÿÀײŠÊ"-HìŒB)ªc© ûãÔ.ø!Iœóž ™'*e=¿8*ÖÏïÅ@–=%˜¡m'¥©c¹;Û2ö›í7}{P›HÑVû@14–¡Ï§~€©§d¨Øþ‹úfH%C}÷އÎÓp=´] ¯ý.@çâY+¸bó¿yޣÄ}ŸëB-Ý‚ô/Õ÷li¯Ò(‚:ÎM-ô¸õÕ÷¤>ûÒ}Ú0æÝÊ nf•äüŽ ú§d`¾8 PŸ±9Ò½€¡åßaGToîM ¢kòI@{^DR+VQTôG­Ùuè¿·œ¢ŸoÿþÜ’Ûèàg+JžÅ’õ1)Ü”µ{¨¹µ7A­¥jldØŠþ?Öî<*ǵýxHƒ!™’) É"Òuw„(d(I‘)mB‘$Ó•JÈШ%•T¦P÷}ž!•L©P$ ÉB2½û÷>Ïqþöºßõ¼×j=®>|ïë8¾{ïcß»ÝZ{í5O¡bÀs‚××N*ÖÏþïokÝåy 5/0G“[\᪠뒢Æmm±˜íß0[°rOØúkž:H;LæŠíŸÓr(G”1ÔSÅ -K¹’y®¦ÊçFúÂ$ŽjÝþDP±ù¿öý”×™v”£9cíå?.DRôžjÀ6=šqä)êvM~áH‚ÝÒ7ŽV¬^ h@Õ]êýÎŒ;e„%tšÏõ/#ãVíãº.{ ö‰@Ð=s ¿± jG·@Òa-‚êjC3@ÅúKKÀi¹Ô¼êÔì1#è§+vP¹!†¡A7ã!ò™w| £o¦O䦚®%ørWN>N“+€.›ÂdÕG *ÖÏãò&¸ïy“¡KUS eA·•™)+kͲò>½Èò¹9oä¦=N›ê;4™*¥|ÍFÅöÇö …$¯[ Ýv"j‡æsM'åÒZ5=Š–ÖÏ%Û/ÿäŠÍ˺JR—¾¡híÞÉЩjƒý/NUñÑô—é:PT1w°Ñd•ÝtUÌSi´`Ò&PÅ<ïz,D|ݨbž£uœ>|.T1믡Ÿ'T1÷õ•Ù(M†þ?|eïÉÏ{> UÌÜÁ:É¡hSû˜•€cì1†þ?pï 8iÌ\ûÅrô†.]Çg™Eªöôã¾YßXoî}íÿù­A8wð‘x¹Ò©ÅSº‰¼ ~­?d}Îô•Ú2¶xõi‚Šõw˜Ý‡ª.b¨n/Cy¢–7#¼I–isD’¼rî¤÷ã ñùX@36Ÿ¸ÖÍÀgÇ µi[ö‘)*ÖϽ “Y>¦ècWè§+: ½³ÝùÔP i([×:› [é1н±P§ÝI÷Œ»\±ýgµ6¹¯„¡‘–¾`]D¸!zgÈ»7 š£5]®°Ñ›/q¥ò’†Þ0¿—$rÅÞÿÇ·ªØG§ÙÇœ?™´ÓªP¥å+ n¿–‘_—êêòv*Q¾ϵûœ+¼HÔÚõ--š´ƒk¬5¦šÏ¦hÿz]è¶*Š  ídô¶v)E/ßH'Û+æ0T¬Db‰PÚr;CÉ’çìÏý|ª˜Ìz:[¼­ T´°àF:†-ïëðß@üöáªF‚…ñl@­wl% cvqÅú%„w¢[¾ŽahA›|Áþ¯qÜeWâYÎÑ¥¦è­É ¬ôX)EWH–3 P@ˇ†²ˆ1*¶BØßõLš¹ðøŽí¨v%fIõ2ê)*Å\±ùK>†Âä:£ËUÞ2ˆûOuHzÿ4FŽÅØžÕ­;’uZ^57²%6U ¸›„qlþ÷¾€®×÷`z±Ýž býT¯*ù/«« ÞÞAܘí2jðíH{^%è©qs d×!@E–7§°e—:Cuò zË'Š*¹ww¯$ÐÔaŸè»î\±ùwRP¯wé€êͲgê7ƒ¸ðT‹ö/eÎ3tÒ+Ù 9Ìóá.‰(Ì¢è£a¨}˜;8ÔdËÏ2t㯳 mĵÆÁš†XnÒéC°âÍB®É$z¶Ð/×±ÄÝC¹¢ß»ÀkskŠFin‰{¨)êøbTFÆún±2LHãÚìïgý•¹ï“å4¹uåQ´ù G†º5Sƒ3wpÅúù–¾!zÕ­(š²ù 3ø»=vl. =!e(”{ÂÖ3 ¹š½7ÈéðI€ÎºÞ‰Îö>OP±ý•Á·` ãmŠ>+º á‰\·úì˼®€†žNev ¨Øü¥Íҙǯ‹Í”fÖaú ý/NUñÙÙ¿=ÛmœÆPÅÜYw ¸x–ËQÅtØ´ ¨bò×oAå T1OZ}œL˨b®s”œžKÚÔ~‰¥Ê` ž&Gó;ÃvÃéÉf UÌ `'ÕœR+G›ºÿׄ Hôó ¨bÞ#ž½ÓJfhSç/³WaŽåÑ mêûÿøeUçº}ä?YT¿žtÝÿœ¢­SˆîÐf -XxŒyÙåR4ýü^–òÈž¡ší;“Ö¿:RT2o¡-j¸‹$Î,Ö)С 1,N÷-A vZ±s›:ºöÞ1z§ÓH†Šõ©;*<Pƒ­†p-c9AmÚ•ÃŒ¹ }[­,ÉØÖ†«d®C²Lc(úqÝ9Ro>†ûÄ<r<çtê€3*ÖoÉè±¼¢4§ïß;œäæ¨Á˜i í˜ÓŠB¸šßî€Öº î·þÀ«™-AÅö×O]IöOžAQWg72½x*Wgr0¬Ó_ hÜ2Ø3+6߉LØ•R@Pá¾ðIÍP±÷ÿñ­*öÑ©ûž9Ô:ÛTúþj͵͚̈å®è™D—ŸÉâv÷è£}ûpãÁag EWÿeK9p?5leÒL†®þL‰4°-W_ÕjwØST¬ d08­¼@З—ºÃ’o?¸ë?¶–ŒöèÆ]}é|è; Pg;±k¾]ކ­¤Dwôbî7÷ìò£m€¦õg wÞ ¨X¿Û¼7 èT¿¿¹÷ªe£ao.ƒÄЙ¡?–AåœÜ’á°!t: ±ï‰ÿ•(®Øþ³mדŠÎS(ÑÁ›Lÿú¿VT—PŸ¾›u{2mÉPáŠÍߟIuòt©I_Ú²ª+÷žêêAÐi†*C×~š 7º5P4§RDQ†Ú½Q†ØätnÙÙˆ{+ ¡)0xÒ;‚îo_OG_•3ô‘ð™¨g?àZî"{’âJ_¬$.vRT¬ÿ­_dM‹€w=Mª7ríse ëv”+ãÓï\à¦<¨#ÖÊÃdh3;e(L°$ha¼5ó|pœ¡=¦,×ãE\±~I=;‚ùŒ/ê¼ò5ÉØ$ôhÎ[Ñ-¤hÜßpöž¹m“ðƒÂÆq€žÌ 4bHWŠŠí×ê)%ÅC§QôKW_b`9‹k¾#Å'G5ûnbÛ]¥ ›¿ Rž;‰ ?bläqŽöúOµ Ä F¹KÑuÂzÈ JáÚ~› Ãï‡3TÍÄÌé6‹áMÈ0@'÷6„ƒµÜ;“ nz,C]B˜»2wbß2Þó EïåÅA•ëC9*Öÿª‘d×Ý1€*Aìë#¹‡Î¶€/Dz¸¯î©çµ™ÜÔÜ8R°ÿ AçìJ‘þ¨âê~ ‹õ‡1Tßø'¬ž6P±~«ïœ"^ º°Æ˜»èqõã›Kv-Ìå6Ûk;9ª”Ù^vÐñE=‚"Ií¸O\±ýjS‰Úy;Š.~²—¬g ¹“¿ÆÉ7Ï-cèÛekáÁÇË\±ù‰¡ÜhxŠÎ9´ˆT¥Ë¹ðT͇l‡Ü]î~åÖ'U¹ýC`®¹ C¯ÔÇCß¾¸eû·£ªh@—½¤-n‡qÿÎéÁàqhÜò‹`ÿù A·#°ôèF@CÚ«R§ÑÛ¹bý¥ûR»Îz€–œ¢ÃT¸¹;öÐR¥T®N• 9´››Ðü •Ò7ßœÑÕ±ÕoÖ W•*¿Û™½ ÷áŠõÓU³'^ï+4û¶ƒv¼¤Þ‰€ÆŽ‹²ø6wbÄèô÷&hiå:’sù(AEÿü´Œ ×9SÔõÒbàéÊ]ïvÚÏÛÂÐuß ³ò†Téy9Ú¶}8Œð¼¨XÿËùk™Ï»§ýQ³Y;ÏâÞÛ:”iÆŽ`¨Å¥GTÿøzîS«vLêû<5 ûB»åß§è&˜EG*é3O/ãŠõólŒ!û«ö4à}ÑÍ›ÈÍ0s[¿ rTʲÆå˜;Ptêú+48éC‡Çﺄ‹\±ý[v½…'ýdPGZs{š…Q¥ÖÙhÍk4Kå7EÅæoLªN‘¡Á‹G—érTìý|«Š}ôlø}(y´‚¡ÅSžÃÒåS¸¿Š†2«K³™¥;jr·o¯¡?Ò×0TòÔÐôk”wôª´ìÓFŠ–]lw»Ó’¡ù±CY‘a< ›¥GÙ “rT¬¿Q )«Òy.Cs6®gzí6RÔ P&ï^ÁК°+¤Xÿ8W6 5ý2z<7wÍ)¢¬bÅ5îÄ4&êâË6qg¨X¿_/¹üE@GT Ö’®—é¡Ã”}-jÓ’ØØ$rmVÝ»o¯¹IÃîÃ;§æ€Ší¯Lüý5«(Þó#ämHáÆùŸv\ÿûßþÿí—!+¡¢ë®Ø|—9“HŠwEvƒéÏ5úOõ²úGx˜Þ‡¡mgÿ„>É)jh€½ÙP' ù!lVHEëŒK‰vË@†:k/ƒÑÊ^ܰ»äÆü‘ÜBwC^QtäÉlªþàC—´½ɗ¹býWLXÂvTRTßÑŠ÷ìÊÐÄÛË!óp70>ºEZrû¼ ²­¸{õè{î/ŠNýp™ l,gèüî龃+ÖÏ64–ØÜ" §ÏÇ‘ºÛw£·ô2Ee]­HÚ•\óhkh|{зÓóéÊç¾\±ýQŸ¨«É*Šîs¬ƒNRun‡]é0=â-7ºD9STl¾æk?X&¥¨ÿ÷Ð<ÝŸ ðTsÆ·¸Jg0´©ïÿã—U¡zÿúÈR‹ÄÀhÿþ€^ªYn¸[ݘÙ×XnMÓÓ·àN˜ø6^ ehÀ»VûÄ$Š6_ý TVO`¨ÃÔÖ’ ÎZq\‘çr†^· †æY‡¹bý­:w'z mUºK³Ïv’p¦v†NÚn Ýï )K¦sÓ¬„‘ºÛ¸îk“àÕ¬¡ÜØsÖ´ÍùhŠêïl «z&sÅúù»%±[ÑC)ªôã£&-Zóp?Kˆíȵ\)ež«·pÇ¿Ø)ÿâõPÛÌÜ~WlÿÏFUÉô韚½§¥äâ‡n€v[¯ÝŸÜ`¨Æ‚D0t€+6¿Óx-2øcèéWs¡ýøD®ØûÿxDW Ø6غú£ øú¸r„x–yã0AýúÇ2¨•£¬[HnèhðŠ,(¢Ç¹]K!Lù÷Vo3X𼄻fÀ[È9{W†^.·…Gµi€Šõ/X£BKËtõPu¤EÅ_ šQ0ÝX‰¡Ï5!®_„ýÖõ X éDÑÅ3OÁµG½·¸„(u›)CG7¿Mê,T¬ŸF¯Hv½Þ‘¡þ’2kÏ(î­Tc6ÿP×¾w(}œx«—¸›i¸|£h”e)] sš¡bûG¬j.±›4ЕÂO¨›kÏ]«ô –ç~£¨ÿ_ïÀÞ­ bóµe§`nË• >ö)H½(úOõTíTȾ½ÐŒîC@îáà ‡½lcÝŠžKXÇvÈg0Tfn ‰!ù€Zß né]å+Ü)y?Ÿë}_Â’ŽDq[·¹OeÛfp®˜°¤µƒ(*ÖßMsýhžBÐiý·S‹…f\]÷0:={ë =\vo èéc ãµ7ûZK¸c:›«Ý°OˆÈ¶£Cû}'¯ò•*Ö/Iw Sªb ­Ê¿@oyȽpÒ•4‹©åV4N„gKª¹ÙÁêèîÍ'ÏÀ~㊊í·t¨£Ç9ót»È ƒç·R¸ÑƽˆÿŒ3\±ù5ËJÀÿD_@‡Õx€å£HîQžEêÒZÝ|õ[6‹ûOÕ`Öâ:ÔФÛHág?®$É?û×Öë uØß@¶.,áÊ{¤±„×å¨ô‰KÙ·†¡±L˜ð!{+È\k¸Á}uÊ´?´¯¡;Q¾QÇýSÕJ./û™+GC”Ê´¢èîÜÝä¬V)Aú‰îx‰;gi½¼å’*nbrÍ™;‰[ñq>y>e# ¿Ç²nÁë§’=ê÷– qy åÁåZÜ×»éàÔ¾Íy’/Ëû^É}ïñ‰z:˜¡ÅR‰öüã\±~Û_€{;“ºoq1ßóç®~þ¬G~ÌFw4|sÙ¾IJÇ^mÏЪKˆö+¶_Yçh&P@=*¢¡ùÁ«ÜèÇÍÀv?C]ÙmêÚsEÿøö”ÒÂúÞ S=žäg;pÿ‹SU|¤‹åÙƒ¶ª˜Óy­Ìf2T1wˆ_íS#ª˜œQ v«Ý(ª˜¯½¡͉As±þcj®mü )ª˜ëÌ6•g;)GóÑëâÉ2ÛUÌ÷´yýG0´©ý,<ƒÙe“ª˜»Ë_BÔ.¨bÞû#|ömÏЦîß¶t˜[^T1wz¸ Þ„ëR´©óF€çÛn mêûÿxhýë#ÿÉ/¡p÷{Šî©«ƒIkC¹^2¹°+Æ—¡š½Î’`²‹k=@Ng­ôû´ ìÓ±‡ øµ R;tt`N¹*LàŽhŸÄ”ÚÏÏFµNÏ…]e¨XÓŸ™5²‘¢Y-ö‘g qÜ)VM hQX…iŽ^£€FåïýÛ!€š;«‚ÍàîK¿,`£¨¦ÌDˆ~àÃP±~*‹ùÇÐ…¤‘õ‘qkê—Ãò 5®ò<pøqŒ¢ß½§Bê•,†6_W }cÇqÅöw‹yH\ÖöÔèL!ñoÀÝo|ÂòOp-¶¸­q%\±ùÓzʉG ‚NXJœœßš¢bï7áT7~þ .JåèÎç_¡Çu& —ëAÅ–`†ºÝ\ Ê~ÜéSY'½~m[¯ÅÌ;öf¨TËCøñ©  n²YòöËú­ëYèz€¡¾Í%OTZ*Öÿé¡Û$cPW†’®­ÁÀº;×ÿæy]°“€ªÓ–ÔàRŠm ¢´°¥ÖZ^ÜÕõûÁS[ÊPR =ž&¨X?Û—„ê3®ž± ¨âÎk½ªÒräè+à ˆ›üF@“¢ß@) ¨¯Ý¨O¹ÄÛÃì"ñõ è~­ã¤âåPî¨JcVî¹›[÷~?AõM×€gÎCnþö7tk~ ÜÝY@û¥„+²Lóƒ$~ìp@­3·‘'sFr—|rfïï´`hð“[tçÏp®Øü1~䋱EmBo‘Û¹ðT_V¾€ªs#ýšÿ®_Ê-ÜY .Ê&Õþk…TrÌ„ñ ҹ˭¹j5±ÂǹhUA Ößåzþ}’µ>R†þœ;Êóª¹bý¯ÜZÑ·)š4Ýlöp{ %GëͺóA˜W6Žkè"aCmºrÕ*÷R—eÛ¸6“0i¯¹}Þ—Z¯»ÆP±~©mO@É…|†Öµ¼c¹m$vðÌHÐí~&pYÛˆk9Ϙ©‡.fèŠ_oÉ=•®Øþˆßsˆ~ïQ€ZêOfm͵X¯Þ‹Ï3´Ï½Ó ­¶œ+6ßâ¶)¸TøQôÖóÅP5¬N@ÿà©*5+„e­~}Es{º õ÷Çr'‡ŠAÜö¤«¿ÊPëÌÛàºg4 2·Þð*­;E3õ•ÁE?I@uü«NN]@Ò ¾M  býKGí„à–m)ú $¤Ç¢Ç¡gï¹CþrU†NzN·7Ptm‘?ñ™ÀÐðéC@+z)׺0º÷àj=û )³ËT¬Ÿn˯¶`=C_j)ÉñkÏ]¬Õ¦ÏŸ ¨†a<ñrtçÚ9×ÖôdhíÖd¨ý9„ bû­ÿz/D[·{pjÉÿ:ϼ¤¶924Ž€Q6 bóÓÒÊ;µU[M¤·Uýƒ§:¡Óaؼ=Ð9ýýá¢êE®ýÆU¯œÈõü¡§7dpWNÝ iá\—j°Gõ ×qÛ¢Yé+ þ5gHNÅ24;Z›¹ŒÎôµcSÙyŒ¢bý=ûA}_ ‚>V•=¼ƒÛÈ‚¡ªÏ StÚì0xì‚æÎY &ãôúæÝnØfšOÑû vAm‡R@×|4eQ‡S¹bý¨•ºDÓ¯E‡ú«H^«º´êÓAS÷ž€&M¢E¹GWƒq/{n•f©Ì¡›'Wl¿´x‹é˜Ñ€zéÈ›ß5âž(¶„qîë¹{·"¡A\±ù=|Ú‘GoT6§£þû_œªâcqg. Ýt PÅ\¶¯;™ñ1PÅÜpÏ{¹ÿä³€*æ£dDz6ÜUÌ¥fòŠýgª˜‹õW“¸Á–y™UÌ‹<7Cd«¯UÌ—7‹'½ùrT1ß0ë{³¨LŽ6µ_È»pÕµ3 ŠysÛN,3¡ Šyé²FÚpÀ˜ MÝ¡qYž÷t8 ŠyV:³ë8ЦÎ7šúŠÚ\Ц¾ÿçé§ý—‚ÿÉœ¹L긋 E–‘òˆó¸&“ Í£C–,†{®RÔdŸ äÿÏÿEïßö»èŸ#(šúH‹æ˜ €~ìRN{;EкÜÑ`¾m×àS5±}„+Ö¿öq1 |4¡¶“*!,¯ˆ¢û³O‚RÜf†>ó¬€ê oöbý1ô]³z°<â@ÑJŸ§0b¹ £wö"ï orÅú Ô\r®ÛCŠþV•,>o&G½UÒ@©Û%ôRì° !輓ñptðe@m?çQí‚®ØþçÐTOÐÞÓÃaLr+‚nþ6$K*µjYNQÅ\ÖOƒf¨¦ ¨bníÔ$ «(ª˜Û/\Ë í'T1¥Úî]ÛLQÅ\¬ÿ¤½§Éà£gUÌk± îǪ˜ì7 2²²ª˜X³Ž_´©ý,ïû²ú·ÛUÌ]#š1Û¬ÃUÌUó [Ùd†6uÿü³+@ãÀ@sÍ lŠ.E›:Ftwvµm M}ÿϳÓÿúÈr«C¬ÊsôíI_xìË-S“Guý_µæÒ7^ôÆ–ãpêq* ã_>%[—sÛX +¾DpcÂÛ3W+]±¼‚C)ºM>mo ¨Xÿ†Ù?…3kG1Ôúýnrêœ)WuþÁºt`ø3¨ÿ6Дq™PüMÎ5×ì^﫹n;1™f\C·OT¢2€ býœF<#’êÉrÔȺœèz\’¡9’lªk9— f­z^-C=ßob+Úù0Tr&€ø*UpÅökäͯ5¾˜Þ1Ü´Ù¤Îp¨€Ú…&éÄ2®Øü£¡f°HÓÐ5f{IFËY\±÷›pªûóÁº»{-cøø8œ[3ï(Nˆ$håÌë´èú+M ÄÞ8Q@g©ïg+&LâXˉ[' +ŠÛ°Üæ 5,!Øì}IÐêû¨ÒµÁrTô¦?"óMº ±#\ÎÊm’µƒÓ5¦Ÿos'?”Ò틸S{-fßäjŸ0¤ßö˜1TeÇ<ø6Œ+Ö¯¢õ-R5ð€ ½÷4ŸäôxÄÍ¢éæ7Š^ùnNí;r+fTÃöóûJε”Ä-o ¨ØþÑ’(Hý–hðÚÝ0lý nt['ymŠF|ÝK´×|’£bóõ·¥5QƒÝšó™î è<Õùþ­ÀÕ-ЗEYäšîº_•´lÔ9ÚkámúFéP?j#EÅú7ó7‚%Сš>ÓA5«×ÀfªkîŠßi4Õ:ûâî-rùÉU®öç@h_eÏ͌͆ÔN}uÓ<¾>'hí´íB€Ëx@ÅúÏúê z×(êz%ܘÌ´™7h[ûPááDÐÅ3w±àÇPwÇ'´×Ìb®²!Þ× Ð•4êÉ.ŠŠõ³;TMt£›gÔ%7*=±šŒ¶ëÇЊš[¤ºÙP®³÷iæón>A·[ÊÒö¥1Tl¿)³„Èo…vÃaPX6×wGÑõÍ3EÛM Öþ1rTl¾ïܤÚÖ¡I+œÁ8UƒûOUóhsê8)Ð¥ZtËc)עƛ´Ô\ÅPýÄ01é/n·Ÿ‘ðða*÷˺WieÌ}½+¶7´DÅ œû¬äŽ-oÆ,ît§èˆgˆUù†ŠõÏ{°”ÆêQTfµ çÈÑ7ã2'“,Š–Îvcû\Ö0t¿÷[2†½æ›¦Âíê\n‡˜òëmWî«+¨]UIQ±~óà+‰‹¬5EoöøD”ŸФ›]!Ì|0CC¾ÏÝ.ݸ^s¦€vÏjnjúWX¥;+¶ßx…ØM“ªVô•ìïO¹ƒÊ IÆêÚê\")ºhŠŠÍï¿ÌÜÆö§èŒ-¾°”8ôžªÃ¹«ô‹Ìб.ßè½@ ®ö´ ìšÄPó¸ (ÏiÍ-þðÜ$6Íͺªv;Ôo™fêÎ¥3\¨Q¸ ·0Ñâ×S´Ù`Ø§Ý P±þ›e. :T@Õ’@×õ7÷ù·o4õæY†¾-ïK¬üïqû|×#vÜ´É-%Ö‡mdh—6[À¾×‚æü у{*Öïfh5yöé·€­É'i{{ÔÒØZ-®¡¨Á‚ l¼ˆ{aì[(_4 з6Ã!8ã1WlðØ+dà høˆ½¤q™œ;±<‘´¿ GwF4»^‰\±ù·oϾS»zý¯óÄaÐLîqªŠÏöuíY¯~€*æÊ"Àhå.Š*æQÖs jE Šùà›íؽà UÌ3Fí‘›ÚÙª˜‹õw1ž ’ÞSª˜o6X ôÍm†*æVéï`ïI¨b^}ã™Ü×KBЦö[» ‰t¨Õ%¨b>á\$Ý( Š¹~¼;sûûŸHhS÷ÏM3&ÚåÙ€*æÔº944¾ЦÎ7mLmºõ´©ïÿã=ïßÿ&ø+ÅZlè­K–TڜۧßA¨ÊN“¡”öµó#¨î¢%²”øb@uÈæâÃÕLXù¶ ýýÞ›ªšùsëåICã!ÍÛ¸“hwÛoŠŠõ¿w{%ÄÚFôõÈè{ƒ;èÔXñqCƒï½€°^¹÷“âaIØ9@%–j°`a9×ݰ sëÈPïjgÖàŠõl˜J¦Ü™KP­^ûI¥ÉBî‚$7Èm·ŒKÌ…]z¸5’ݬ¸[1EsŠÖÐ7²*¶¿Ç–ä§o ’% MÝçr_…< j+5:Õn$ÜØq¢bóSdö¬ªj;A/÷³f×[2Tìý&œª0j>«›œFв;KXí|}nëOHºñ“;{›ÐMýÝ`˜Î’’euSJ`Uñ}úTú‚ôïÃõi±FžÜ }oé¦íÙhwYQ*ô4EÅúÛ[Á«¿¾ô¾ß(oÝÐúâ¨8{G@—•?lh¯tëýÛÜk;¶³Ë§pkŽÜ»è89êyD Ûö‡*Öïìwg±Ï† …älÿQÜ‘¶ƒ`ÍìN€º_¨![ûscdñ–ÎÐ{íšI‚­Tl†Q±Ü(ü *ÝÒ år®m¨\ؘ/Gß9Ú€ä¢?AÅæ‡ÅùRÿ¼ƒ ½¥§Ê™ Ü?xªÍa“ ûe( ZÈʦ[PôVk`ž&€6OŒ 5ÿk¢z?¦)?ÍÐV%ÛÉÌÎ¥ÜÏEàÐwE§UP0ïÖÐG/ ‰Äð‘)ºyŠ5Q{+ÖÿtAW˜s¡/ RÓO¤høpn~ÜQ°ÓNä4Öƒ¹ÜÌAYKÛ ‚þÈ>Íâ&ueèå *ì©‘? ÷z8°Ê5yë—ÛP+$µiÐÒ€d¡Êj2×:Ì™XúôTjöÑtßñ¶\(PZq’«ª?nœÍÛ{4™fµ½è–åÕtqà)î„€àÑØŽ«™g)#Tl>ó;ŸÚNc¨þÀ|xÊÐ?xªõ-f3+÷lŠ4’0¥å* -hsYðó hÑ´Ty³”ŽÜ…/‚!Ê=“¡f;Ëá–ÿ\î‰ñö ²P¥S9Be—Ü*£<ò kE§5¯%Òã«LQ±þoŒRÉ៣ Ø=¨×qÖ®–5DQ®‘_;–ÉÍíïÀrNf0Ôër¸¬å¹'ÜܹI¼q(wiódp’H¸¢ßúG…*ÒZŽRÏr¡*¤–«V^#ß3¢„ í\-h}·mÜâ°pv¦Õ7îóæÛØóh_†Ší¿v¡ ;W‘ èøýÙÙ6G¸ƒ‡ô¦ÚO4WP“ç.³§¨ØüœÒcàÔMЪ½ãAÓÛŸûOU§¬?Û:dCío·béV–Ü3×Ó‚ÓOjc”M]Oùq[Y7Bmÿ1rÔé÷S(=c èÔŠ´Ú}"·ës kó÷¡NÞ‹%qjÞgékæFP±þ†-Ì„Üeµ^“!o½ª ·ì–;‹¿I¸ãìc?.™4¼¥DéÜgèló|x6;šÛ}H,º¢èÙ#mˆRE$W¬Ÿóì>¤âÅ(ŠÖ·v%[k¹múýE Ò´›Ïjpi·í¨¬ï2TÅïH³-¹bûCgJجÞá€îj˜Ëô æ’U#ÉVã Šj¥Õ_׸bó%C„)—÷æÝ‘ùt7çþÁSè[J=L:tÂnêqr×Îï)µîÑN@µF×PÃFŠýÜû'2@Šx¹<à ÁrÂ>StzŽ3}ÝèÇÐ2¥*ÁªbE]生Vs¸bý“.YѤÔotu?]:ówå¶½ìù¨RŠª_s`j.[ëóz§«r #›KŽÎ ¨cƒ1{SÍÕ¿_MWœ°`¨X¿Äá1äYörŠfÈ!æê6Ü¿%´ªÔѲ=û×yn‚Å øž9Ðñ¶&wð:Wl¯W«XL) iߤl¶»3wâîAÐMzˆ¢ã$3¡ªñ½)*6‡÷"¦âoŠZZve³2&0ô¿8UÅçÍ“–tÂ?†*æñ[ÎÓÓÉ¿(ª˜W ˺D%ª˜¯YÔv‘ UÌ—öèÖ^wå¨b.Öÿã¶4ºô#Asåú‹Ôü؆*æ5³ª`Ï ?@óÆi“`ÉŽÝ mj?U㇄tBQÅ\×´X–î÷“¢ŠyÃ) º'ÚÔýÝ>²¯åSŨîë@öã mêü1ãï Ñ÷1´©ïÿãÙ^ÕZòÿ³ŽS«§i€ÆK»°”‹qÜãµi¬ñqŠõlÍr?è24.f®,à§Õ½n&Ôõ¤h†ÃOÈ{ }žÿ I€VKë Sßh‚nt´H:¨XÿüwàÐ!Дø™ýâ$—©Í‚‘q[¸{kIñ¦ý\¯-åuRÊP»î Ywž›ã7H¶å’ ·4y'Ü:ˆ+Öoß››$nvº µ]Lr”MÑÌÕéŒF|¥èÄo»YΪ­ =íŸÆn—ØQ4urµ[ÈP±ýç#½ˆæÁ—rtäbw©‘ë1{ Hm™¢³óÌaåù,‚ŠÍ/Ùö bçšPtñÕJ¨1š¨ØûM8Õ=ÍX}ü^@ .-gÛoà¾ì²‚É+‚Zºïj{ŽëR>†O¹NÑÄÅwɹI¿¸É§oQ€::ŒdÊÍÏru¥«™šÒ#‚’›y´mĆŠõ°[^\È4ú«7ùè<·Gº5Õ±ôãNTéÍb]†s/:÷½­û>œŠ (Dï‡ÇÛ^4$Û”t0P±~'ûÞ&†­ ôpÛ|¢½À‡;£jk˜zŠ¡};,¥=‚‹¸š#ŽƒÞÞTîà¤OÑy“€ŠíøvQ+mNÑMê1ä†VKîð^¯‰í™n€ºÙûÉêºôàŠÍ¯·\—ZWãMkë¯rÿà©H|ÙK@ßdîcç?tåVm:nŠÎM†~î¤ÒÀ"®©‘)ÄÎ~LÑý¶BÏŽ\ïÁilÆÜ‡}<7š¼ªÓ'^«,á¼L€Î™C믻ÒHnÖþ Õ™Gh…ÿQ®Õü¹ÌÊYBÐGS§±Â²SÝzú6´o}•  Bâà£ï @3u‚¨ÌGƒ Jn¿d¬ü9EÅúÍ{$#e‡ÔªÛ’‘uŒ;û|4ñßù¡ž-l`»Y1÷ʈPøÝý( o¾{ÓŒÐ3\±ý+[%·ÁÊýqþ4Q Ä}SSEƒWX49¯ˆÆ~û@Q±ù¡+Yaðd@ÕÆ†³Æváýƒ§zóí!F{Ô$ô(ë±Ëˆû~¼?Ô\£ mˆÏ%ënÐU)4L'èŠ,cÈNhü¦«´ñâ%†æ …χ ¹J§šîSÞ hñ°áLR¾QŽŠõ¿.Õ`­_ªÓÏŒí?¾˜[y¯ûáfÅÐÚ#ª4O?˜«SbæÞ‡Õ;.¨µŠå¯ï ?¯7RTÏy,]Ü› ¢_=OÏÛ èÀ‹ $cF2wö´$h“vŽ¡Y!/r'·dk“e­&¨nVbÊP±ýe$nÑG9ê:>\xWɽ±YŸÜùCSÖ;€SW ®Øü¾ëtØÊY! }š´“|IÏäþÁS-\vŒiš.GMú%°çÝC(jaóÎ †nߨLâ¯;ƒ¢Ñz~ä³ÒD@GlÕ¥Á£ºpïÂøêïËP·†ðʲ5E›m&~6[ªvR,¡¨Xÿ¥Î,e› Ïofê.t»ñ9¢äÅPiö°ðÃ^nüŸ´«Å:@•ŒY× ê}:žœNˆç¶)M¤™)*ÖÏÇ)–ÜÈÉв›¡$๜ûmôoPS×bèDGUÉ›¨NrôÑ´™àté8C£F߀;'QTl´ü‰Ûœ'G úÞ#ÒãÉ\Ï-qPെ¢ScÁ’µe›Ÿ¤)ê{æ>ÜmõŠ¢ðT{žˆe‡z·`¨ßóƒÌ¸Ú”»AUIâ¤z… Ögª¡ú…+ …»¯S¥« :üý#š:(Z9âÌ<вË =BÎý9c ¸öìÌuº(ß²¥9W¬NžSš™, Þ“7±9&(š85–ùØ1Ô'ð¨ßË¥hì²%,oç>n³î­XÛô íï5‡(…’£®ç‹‰ë«§\±~sÞM(P“¤U¤®æwÅ¢’ºÓꀸÔÀÚ1Ü/«ŽƒtÜ4®4u¹±)Ž+¶îì R¦(G·{B m-¹Úþf`éè¨E/-ù¤¤®Øü'ƒË `k#Aõ&ï„q£bý/NUñÑzäÏÈ÷u UÌ3LÒ Ìâ$ ŠyE;M›¡Š¹öò>”^ÊT1/ž ´tbEs±þwô–±¥O)ª˜wH}U¬ ª˜ÇY"î:ÈPżH=“ìz( Mí×꺄ÔNPÅ|O§ 8˜ÇUÌ5Neœ›ÚÔý›žÍAJrT1#5c#ZÚÔùn»Î¥i€6õý<:ÿúȲӡì‹_E—E²ÔÇÚ/7˜é5ÚÔ·lK° ¡èŒ7ñ°¬ì8CWm¬¸ÃKÚ‚×çP9ºÚ¶#ÊÞ¨nQ›–ßТµ­XYxCÅú;½Þ:WPôÆì@¨cæ\§ŸM·¾Z hªCM¿dÂ= .gRT¹*–N4!¨Õ’Jø6yCáù{x®-T¬ß¢:w¶co, nÙ‘lI¶#WÉa+زK@g]Èt×úRÔ¶1ìFŒâêœø?¬ÛkTŽk7ü$!I’$!"©Bè<æ…² !„„–mvÙ%I;•JE"!©$)¥ºŽ#TRI’$¡l“$ûžõŽñÎã¹ÇõŒqŸ£q;¿ü>ü×9ç_k®KׇµšÖhWdÿ¦J'˜kÐP¹Õ:¹7÷Õc}æß ÍК”:Á¨ëA®Øüž®`·“¢ŠÀ£tAÅÞoÅ©ÎÿèÌ.ïšÊС¼” bûGLY ‘Çô}•1vkäî•_ ©Ö ­ÌŽ„š?Öýù» P³IPg_bd÷/žêz­ÁìýÂ@†Nßñ‰Î9s’ûYeý¼™q/%ã]¥ÜåËÚÀè§€–”¶eÒ–k\»q[„úùþ5¼®Bu”lTîã?Ò—·:aðV¹B—¢bý»†Ëý šêKžqÿìÒb«wš0ÔÜq½ëËuÛ¨Hb·ëº> ¸}ƒ 6KË©ÿk í,ÉY NsÅúµÀy6øù]ŠïʬïÏ`¨DkŒtâËCÜõ’ÉDõãÜK¤…™mã×Dœ'}çŠíïf/AR3@Mg€3›-þ¯;½Aeß ‚ÎØ 2:ŒTlþ7jë£ èëž½Y±n‚þÅS=ùÒŸþœÀP—AžÒõê©ÜX§µ³æ&÷\ï H¼š›u$‚툙¨Ñ¸Dv±³Em¿ªÓ×q\·OÃÉïË õÛx‰lÓõåŽH‰ãÝ(*Ö¿_Ž„}Ô´H׆.•p7îH%÷«Ž3ÔÓeô<Ä•?±„ªNí* ù¯Ã¤–¹.}pëÔEôÌìqÄöÅ=®X¿4;G¦Ð/ˆ¡á¬s6¼Ä]<Û̕¹ãŽ@„‹×ç‹)?´¢£êˆÎR+‚Ší—?¤ ¿‡Ìt·¾: }5[S1KоmÀµŠ?F›î®#¨Øü€beæfÒ¡5»: ñ þÜ¿xªsTz’ʦt†F\%?Žþ_Ÿ¬®‡]ÛæqçIä%¯ ¨ï-ìð®C U R¥í¿—qåL ýÓ!\µøð) ¡hE=д44u†}SNP±þ/ÆBoƒå€OWÍÎÛ¹GŸ‡—_&1´ýˆ[ppV E£&)=³¸Ï㯑òmç¸]G…°’Üíq©=[¨ÆP±~]JÖQç„› }¸zQÏ)ãÊ•K!&d(WåÒ}õ¤è¬÷D]/\@ƒzÆ‘š½ÑÙ¨ØþÑÃ@1` ý—¿&#z.ãZ>šN[ÜŽQtÛþDqCÅæ‡ç-5_†’ ëó›)úOõicW Ã/3tåHsª˜À­ZÖF’×áAåß¾„f§Õ€VX,%– Ýßå LûȽØ!rB:¶ß,x%€F«{HÓôïRôèíþ°úV8W¬¿›|‘Ss´¥ôƒ ¼»ss1d.ò&èûÚˆ>0PÅ]¿ˆÏ:Š:Å*B²üu)ÚoXd~‰b¨#{ ¹}.T¬_úÊ.ýå.Cu ¶@òÄ<îv­‡`?ÉŠ ¿Ü„3]õím]JìÔög£j+ëHï°¶Û_º9ŒÞ³Ð3é1ÄÿÜJîb{Mêwž¢.=‚Ó=)*6?v}.´›—BÐõu>ptï@ÿ‡S•}¶Ämº'*›‡ÞNEÏã•ÍUº*I :u¦¨lîÖÛƒœÚ»PÙÜò߯2ê9*•ÍÅú›-ΕZ†oT6Oí³Úx‡*›ÏM–¹iDzPÙ<üyYmhkû)µ=¹j*›_»uÆÙº*›'mŸIZô´µûmÏ»’¼Ã€Êæ¯ãA_©^@[;?Èè0©?uÐÖ¾ÿOc’ä¿9q¹!Äzç·x À4z«ràöô;Ãæ4æÞ,?À¾¹%QTYèË,šƒßtƒœk<È=}6ÂÏÙså®eÉÕÞÎBë/í'ÎÝST¬¿ÀO‰' ÁÒZâ“âÅý¸vÝå$×Hß›-}­ÇÝÒ9Še›Ífh„ugæ“+åzE]£¿m󭷹·o¤¨h¿žÇ Ò)Ðí3­áEz·Ù\‡ ªÞÉ-¶‰5Íêɵ«šGVTôD½9‘W¶Tlÿø'£áÒ¥*@ÝÚ©Öá'ÜÏÂؘáDÑ¥Z *«$€ŠÍ×ô9GJ³óúOÏ‹à'DsÅÞoÅ©^ËxON+Ï4ùj*±ï6Û|2ƒÕiFèe¹‹¬r±*C5—ûyÜņG`•q7=®V¥èð± ï74ÜÐŒ”R´ÝsK¸Vo ¨X—"G²fŒ; A’ÂÝ6›¹½CBXô‚Êl´ÿÑmlÝÝA ?ê)³­ä.êR¹Nwg ÁϹ£º~†=fsÅút¹Kr ïšÓáLöû6w¸56þlâoy‚F¯õeE×)º¨“*}öAmÌì•ÎÍlÛ†UÆÅrÅæ3‘“Äï §è¯gõÐ8ÅпxªýÛ¬'ÏÊšªvOò|>·nŒ+ó=ÌP«,5Ö¨ZÀU{g§ àv«ùC*b ð@¯$ Ћ+÷Q­ž ÜéUYdP?nu¿fz¿jCÅú·„w¤‘5Ö€Ž>æK3ÚŽàÎ+hÇ.÷ bh\ïÕ‚ÉÖ$îäù5PèkÁ­Ÿôš™ž€~”Ü­P@ ¥´.¿€+Ö/}áC '³]yo2›¸/Š™bÏ4/,ahåàìvñ n†ú¸lÊýu¿&¥PTlÿ‰mˆŠ_  æ.‰Ùvos}¢Ù¤sÇúu¤; Ú¸‹¡bóƒ×ØÃ‰¶Uu…¦_½Ãý‹§jõ´2»ôål@ 2úPÏhs܀ µnò³ŸqZ²AsáQ@êé@QU w§ë^Vüd7@3k¸@Q{o_¸¿Ó—¡¯‚ÂË.€ŠþWÝDéæ­J€†Oª¥ V9e‹Œ@kr%몹ñTÏeý¡Åoäµ>«Á.Yätð9IvŒ C/)Hêåõ´11™mœ,E[¾y³Uá> ˜‘ }ªNsKå$÷5S(j$·”ô¿ñ€¡?ý‚ï§¾RT¬忟žN‰RTõÃušäIÑ^9éï§ h¡ü>Щ9ÀÕ7ve“>ip#Þy³›¹•dæIgÏŽôñ»E,'hO6*ÖoCÀ¶p£ CÉð­ÌÇÏ››3ü1¼€+ùò vÏö¤èî•“¡¢O9CcÌÛH¾‡ÖPTôo룘ry š[¬™í×Dî›gFP^ú;i”.«X‘Í›¯ré:v8ÃÐçÚIÜn¬¡èÿpª²O¯h}&'·— ²ùºóÍðñKo@eó˜fЧG •ÍÝ×3{k@eó«·ÀŠñ€Êæbý뇅ӒÈ|ŠÊæ“kŸÿ¡€þ?ýºŽ`¡^+*›ûT ðÉ›¡­í§Ú¢Ç|²*›³ä/à’ïAPÙ¼rpÌt ÐÖî_ñÛ‰¥žT6S¸Â|¯$´µó;M{KìvÚÚ÷ÿã Ïè(ùoÚe³ ÚuëÔ“™Tµgè}»d2»ôE/é¿$›×sÇi{€Õ„)zÍzÁ¿¿·†ÔÅ} l>1•¡F³c >=ƒ¢æãI‡ÇUfèö ÐÐ)ˆ¢bý•ÿ95q€^É]G‡dpWõÚÇúëìãj–õùéEÐÍ£áË2m†:ë®…†Î{)ÚÝu>¡ÎP”“pSqW¬_UD9±ÜR ¨Ö½Âìåû\ÇRºÞ9œ¡JóñÜÓâX'_ŠªG,¦6žù Û¿òž ;ªûƒ ÷ Ƴ×+nruì•…4•»€ºWîg¡†ó¹bóM;ÆAâï“ í0÷L­:BQ±÷[qª+Ò¾ÓÞåz ê›IÏí“pÓ~ý&.ùòífô‹è„ÜÌFç®ûNÎ/}@PÿY‹IFB(·º6 Õs¥›–Ëo@£Ç˜C‰ñG‚†È¢–þꀊõÿÑ2í) A™+HHD7Þ8’•)0Ô&j våÆ}\Ž%4H] ¬úÅ4·_HºöT2"ÆL¾ÿ®X¿Á*/is·€Ž¦sÙi»Ó\«Í]á–å9†šÏu…þG¸>‰iÐbÁ}õµ,åTlM_`{ìB ú(SÂÜLâÖLògÉG+(Úë¥ÕÑ9ÉP±ù÷¼‚S}׺Ía ™ûûOuŽýZÚ×Çš¡^–.eö\sÛç$hùZ]ö&ƒHšŠ¸ÅO4HMþ:n‘¡ѼU$Ekl›½Ž<èÈ_V&pC¿=¥óúY² Ó=¸¢ÿ*ÆŸ”öµKÔF=™ZÇEq_ÄŸ“.“¿ÆÐïè2–r¿Š%—·E´fÀ0bþBŽ«ó¦;uùÓŽ¢%òÉ|Uo®X¿W?™cÀ&@Ÿ÷ˆg-]¹÷¾œƒ·W§1ôs' SÒ¯S4¾ó@HÑITKn›±l:Wl¿gG ë!Ð}&—f›…î.š ýÃzâp ”µÔSTlþhù&ú#ÿ ú¿Î±™Ú}¸ñTsÖ ½Ö1ÔØrqJÛÀ5üß“;–% ÃÇ ?ÍP“ªZ"w/]Š>³èÍ)nZþăe˜éPtÓ&}ÖaœCc6ÜyÚPÛŸT]+—+Ö?cLW¶û² ×ìöôËLî’Gg@oQ C{/ƒÌ Š\m:—TÖŸ•¢:a×I\ÏQ}iù€˜ºÐdïN$ºû£lT¬Ÿ­k à' Ý¿'3¯íÅ•Îcä'ÐvŠàIe5Aë?é²ëöµ ëHN80TlÿX5Sf“'EŸþ1f6!„¢+æÆB‡É7 Ú7tt°P±ù-óϰ¦4]†~þ^KßìÏåþÅS}:ö"I¾±–¡#?‘%öÜ3UˆNa„mQÑ$ãÖÏ£¨ß“d}_‚6…{¼¥Œ{Š7É;œËЉêñ[qš»AýßßËÚeQ´MU;p zÌP±þMfKXº±2 Í£Ö²'Y-´¸¥ÅŠÜ-)°,x =Ç«AéR)š[?‚ héÂ+Ä7¢š¢ÌÜÁgU[®X¿CnǙǯ… íµ…©GEqçÛùÁ¾¦€¾ðžcýWpM¶ÁÛÁE=¸¶Ü ¨Øþàöú캢E[öf¥sR¹r^Yîuãµ—œ£•³'TlþBç пÏ=†Þxÿ¦lwæþÅSíþ®'TšÏÐOGCõRs®æûu$°sEå·]'Gâ*¹ªzß²}Wž hŸŽû©œSJöýëY‰µ¸øV†,ô—‡‚Ä`F€}œk¾ºëÞi-s»hh†~K¶eÊ~{(j´vœsôÅ®|²á¬×AM ~Ì¿HЊ“牧ÂOnß!£áqÅ¿Ÿ2ÿ¿/"»³.ê\±~Jç»± \†þ9¢L+Usòï¿9kÝYX&4ZÌä–'‘a¥•3+äLÕÍP±ý#¾¨²/«(úqp[ÖÍó7wOÕg©uãyîÖv)¤%à1Wl~VÕoaiBÐÚŒ Xß;ÐÿáTe»IV0ÝnCeóUÙÝ@ëX-Eeó¯½TǬ„¢²yóÝ@µú ²¹ÉŸh{N PÙ\¬ÿt‹‰ÌÂö Eesã~§¥Ñë•Ísîú›9OPÙüÁn]¦oÃÐÖöó¸šHÊ6¿`¨l.¹ó[Ø PÙü¼ú¢ó¢'E[»?נަ¹va¨l¾SªÍË•(ÚÚù®³Òþý¾ÐÖ¾ÿϼœ’ÿ¦oh.!C¦èNjüfs÷Öe’ÍcìºgÊ`U–pvfÀí˜\ur’ÙÒTŠV/™AöhÔM~fö€7‹(ÚW:ƒ|HôŠÑ¦h»ƒ¡bý™ž„Â匢[íag‡!\ÝSßéÄ®‹jã9HÈ+ðæ¾OmË\5s§† zOs¿©/…;UÚ´ÝXÐjÈäŠõ»s äûyÔ§xôñ«4¯”úÏ*`èÙwÓȱÕÜ¡×k馣Ü-új µôWl¿Ëʃäó»ý9!‚Ùès³gÇ0“|­™>…}ÜîÇP±ùºœ`ñ¼BŠnëoÇTìýVœjqÝ7áÀºÙ€æT¯ÍŽ œÆ-Û±e ¨[ÏxßËuô´£›û¼¦¨X¿Y†‹I·ŸÆ€ÊÉÍÏ`VÜÕ[j ü«Cç‡}‡•ç(º¡mÎ —´Zo7³ï´™¢ñT3 ¨ŽKw@Gdþ¢®~$裹#`™i—Zü"´K¹Ó']f:‹Í¹FAìú)†Ê•ïsÝ[Š:ž»,¸¥TP4x² øÏËÐÉï&€å’~ëÿÉÅmÝ èó©³ÁbDWð·½ k]{W¶D´Ô ïxõ¾Î“HaÁÔ nÅVs°¸€¡ îdÂÐö»*Ö¯·÷Ú?I Ðõ×®S‰EAû|…ÜY/+¡ïç©€: 4£Ý5†–DÇ™]WlôÖgD2§QÊ×HLårí›™¼Þ hÅ¢·Ôðq AÅæ›ŽoÃúu fhb´tiC¹ñTŸèugÎ&—ZµXU»Ìæ:wŠ"Ý»eq 3&³Öìã¦){Qýñ7:}ó4èŸÇmÜõ‹Ü ×áæv‡–¢G­™>—(í,СK tPT¬¿Ê@ePËt—¢é¼<–«MŠ"› ZéV/è-Œã&ö³bi¿O:÷êyö±àAC¶”;‡@m²‰ª *ú©³Pžy´_DйݘÕé6Rô‡a,øtÚÜ™Ð26…›¤ÚVòêê‚f÷ÝN{‹Û?¿™xœ‘¢µÇä@¿~÷ÅOš¿2ž¢ëòö“®½Û2Tlþè× t„Cݤ?ÀꇧúOµ±`8kZÚ©f4³»øAŠºÕ4 Å©€ªÎïEt4¥hJLt½³Ž¡Ë–¿„ýÄ,ÔoûiR‚¾ŠÞ ›O8ªyAª{Ôô+5Ú-¨XU³¶R‹Õ§õ¹C_f†quŒ'uo^ ¨O»w‚ê×RÔ{ÏYæ¬ÌЯWÇ3ͱ‰\k¥¯Ô·&×Öæ.j ãŠõ ðUcJ³ŽRt›W U<Ô™¡Š¤: ÐÏzæ4Ùþ$×éíT¶:(‚{pN0‹»ý‘¢bû:(@ÇÁl´×.E¨ù°Ê =1¨Tlö¤èýÊ`÷ÑA@EOíFLV8 hâìïäA\÷8UÙGoä(öyjEeó5žÄ\0¤¨l^Ñ/þ™1 PÙ|ô©Á¤ë÷@es÷¼bjSgÎPÙ\¬›M꬘T6Ò5€˜-FQÙ|Áx="9RÅPÙ¼4ê5¨¿ß hkû9­M¢ ¥Ó*››,Ua7U7*›ï˜Ö¸Å¦1´µûs«ÛÃçï‚€ÊæåN‘dç²fmíüê¼ñìk2 ­}ÿ?žÉ í%ÿÍÇrz,´ü »»/fóRqÛÜ÷`ýíÛpýª}Xô^AŠ&§’…¿1ô×HXp¥€¢^Ç––ˆnˆxµ#çq«¥sˆJ®7ìŠ&äL[/ býK}Š¡]ó@·Æ&Ãíî܆Qk©ãæ$‚Æî[Hå ›³Ðmc™Šä-A=O0˼6 µºÜäf|¯££g'qÅæÇ[Ž„ø)·FR2÷/žj\vr… >_γ3VA­±ú.m©bèBÅR¦Ê ]½‰tHKк×u‚ô`¼µ¹ðP0¸“莙cYäÙÜé6ÁÐF1Š r|¼tÈA@Åú‡kc“5~mGŽ IàŽzB¢(Ú©¯"œ­Y*E7¦€Æ3çê´¹Éí¿Û‡~šSLÑ®0‚|Ö1T¬_Á’eL1g A·åÚ0ãW·¥è'{?¶3CÏ.Ê ,.qõjfƒdÂ()º]û`¸ ¢û»ƒÙØQ€Ò˾ ·b:W%?ˆQã#Õ¸mÁ¤om*6_Õí—`.ÏTé†/1IÓ¢è_Ç]œí ×ôj ~— Pý‡¡¶ëaX¬wiz[IŧiÕÊt!îQµ€Šþ¨äÛ±Š†U€†:2µðÁÜå–ׄºöfU6Kåôê¼€Œ ´Àñ¤P•åÅUüζXô˨zÜþCÅúY¯ §‡“14§.$ûJõVnÜÑ#…Ür‡RHøÁ¿ Zöz´bT ÿb¨ØþOlaçy7@·YÀf%îâ¼ÈmCP÷Ôɰ+ÆP±ù_= I‹N€þ~®LµŸ*rÿ⩵ugÚæ;ºf‰ Û¸ú7w–?,ò$¨Å^{Øßù37y†&ø×®TÒ?àß³ÝÊMšæA; èÄs}¡J5‘ _d•¾®fèƒpk2 ´ˆ¡bý5&2wr‡ éiSYjin8=-Øã E›ª ºaW{‹ö»©¨µF{注@@õFWBŒŽC­j« +ÁP±~ö–‰Jí†ê·íËWsåÕ[À²v0÷ÕÕv££½¥è’3ý™¼k6A&(³ïÇ*úQä2b–:¨°”…p &?ªýzp§†eJݺ_P±ùßÇvdnCÌÐrÇbzeÓ8†þ§*ûŒOÀ^‹g¨ln¿T*w©*›ï˜hÄ®tT6WZ@¬VT67Ô«…ª{)*›‹õü˜éë·RT6O^|4Ì7a¨lî»í µÊ¡¨l~øsUÊ¿hkû‹˜ý2'1T6_êÑé˺*›ÛŸX~õÚÚýî>›€0@eó^&í–5míü>“ãÉ¥1á míûÿñxmS”ü7ÍVl'I‚ý¨¼ ÜRß›¡Ÿo¾Œ¯•S^× /sWÏj€ðY[ÔÆ.jêÝÝ‘8‚–dpG};K ë¯rOO`ýß+ôŠ©&±_s›¡býW<¿“²“º{Ì]X~ò7¨â6äÜ ‘¢©Sàr„< µI6pÊ$€mÙ|çÇ5cÒ¥±Rôº²<Ä5(PT¬_s~$´9¼Ð ×—ÂÞãG¸’gÇé†Q>\«Îš,¿fw¤Öb®}—¢¯Fh€$©8ÛÔжt¿ ¨éŽ ÐlWÁ•sðb&§nèõ¸¶¬×ºU ›ïÔÏPZ—¨Úðù,¶«WìýVœªk£+lKèMÐF;W˜³&˜«æ¦Æ÷¤hÿ¡àpl AÀÊ­Å€~^_BÏgS®ÛópöÕª/7§àsÙ‡¡þÍÏa5[CQÅÀN`;$ P±þÞ½À¶j:C§þ‚¤µý½a¼›bhŸ·Éué6®Éä *ÿrw‹DŽi^oPÓ$/2wÍ‚Nñ¸A›ÒL(*ÖïôÜ6Ðàw P/ºäUv×ûÀ(öÖüA'|Ôc6WÚQT’¤O·…Tçöxš™æOQ±ýß´àÕƒ‡€Ž]öâß9¸a#Þ“°xo†º¼ò…Fç›qΊ…8ŠÞvÓ Ûú¯eè_˜êI.QÔjÜ/AýXG†š–}$Ñi>ܯÉ¡F{ë粸3—µÐY™Ti­·[íkz!ý EƒOëÑYKõú¢º™<_ÄUߟµwPTlyt(QõÐ"åN¤Nû!×1xd®%èïw¤ß¼z®Ø|ƒí½!"ÁŠ¡Vë¶Cß¾ý‹§Ú~É<8¸ÖÐ6gÇ@¢ÑT®ï²FnåÇ-½e&(q5ߎe bšÕë8yô¦œ›=´‚ÚÎä–Ný qÕ½[Ñ@$A‹¹áƒÃ¤CÝwST¬ÿòÂ÷ â¥¨êË ˜Ôo·G–/MPЧÛS¥Íæníeô›,þyŸ;}Þ@Èq})E7e_¯€îˆˆdí‹ ¸býŒ~Ë1e³?-;£Ìš.ÚpCi_rvÓ†NßôŒ(ŸÀ}» ªœ½%[5 ;ÃÛ¯óþ@öè‘÷•×§¡îp£÷4 Q¶Æ½œéIÂ-†STl~ä xñbAëTÒÉÄ÷‘Ü¿xª^ý5À¦Ûr@'ïyJ¾mâj-©¦EÃwq·„ÎüÿþÏDî*ó80 e¨ËÎwà;ó.EÛ.9 þç•è¯1K Káµ^ ™ã0´ÏÍH¸óz* bý52Ó ñM ý  %ú wô’‰D1%¢GçÔk_nâ¢v`rÔˆ òSÖ’ vÛ¸£w÷cÞmÒz®ü:x=ÆëW`݆Ùl•¢?åÒª}!µ“„x½Ü $ÎÜÇÍ'ÑìÂÕ¶\·M´I©ˆ¡bûS5£hbï@÷¼xFµº3®Îx]¨Œz-EÇÜuGR*6¿R¹;ÑZn†:oŠ#O·úSô/žjäp?"ÿl úFÂç®.\r€y]DЋROö¥8—¢¹“Á^9 @}ÃÝ fX×kùöbÕpn2™½øìFÑýǺ3Û›>€VU¯bÅ-® 뿱½òM?y¤¶Üâ¦Åw† {;¤hŠöORñX nsó²ƒL´Tõápé‹Mç)j1¾n<^høYUš<8–+Ö/ªÔš?VGQ¹jÕÌW•º©ç8Üh+E¿êôº5í Z=í„;ÆÐèªoØ]P±ý»V«1£ý©€Îï?œÅ»Äq«å¶‘ŠòQ\‰ö;Ú¶÷ ‚ŠÍ—4š‚ÝRŠž½µŒ=Wô8UÙGÓôŒtcGg@esÇå Ùë{k*›çÜ«Þ,=¨lní6ŒFß·f¨l®; 6ºÈPÙ\¬¿]æ~!òû @eó@¥@Ò2ô0AeóꇻHÆ%=†Êæ.©Ó˜•ý$ж¶_쬅¤4½ Ceó¦yu¤Aº ²¹Ò“jâ6ê ­ÝÿÈa&óx PÙ<¨­1+¹_EÑÖÎ_»²?¨ü0´µïÿÇ3»_;ɳiï|aÙ˜2@§ž_D̓or5,Ó¡Ô®+C×´¿6a¥RtBG))¥hý5PÙ ¨íÜñôÕ×ëÜý•û™N”wÊó‰°e`AÍ–'ÒwÁ½ë?Ôò Ýê>„¡Í«_H§/áÆí[Kåíç’Ô,òlßiîÜÏr’I¥ëÚaFt‘ ´yß*ª±•¢3„%`üE• býÚoh#Q8íKP¹_ïàXÞ8@­öï¤ÆƒÛrÝOEQ§å³ j·z|]"Ôëë¶bõ®Øþæ¾þô‘Ý2@å’¨¯æt®Öˆ¹Ð)ù8·»\1ÕûçWl¾U—ß‚ñÞ|)ª½å;±¶²¥¨Øû­8U¯aõô­]: 1XÆÈî‹÷÷ÀuQ-AÃMÒÁa†3 ºr‡`à4®ïÂëfÙÝîrOîd/)º¼Ý,ú¹o C A‡ÛϽ1½^>XÏý£Úå 9ûjä}‘´yï˵¾æ Ÿ”ø_﫳&ÜSÃ!§ô ‹í£ý¦sÃ;x wÜm¸ü°û¡ýù¼ò÷ ‡‰™)\uï-Ô®o”}’æ.qô£htª»d¾“¡JÃG½?÷¹bûë¾Ñ¥ž&€þÞÿ‚f7ôæ¾ÿ|Œu _IÐc_—2§ô †ŠÍz'º[o´Â@ÚÎîè_<Õ»¶Ì9nìgáBîè‰ûàö…$î}Ïûdúì®çgöóª/·'MbëUôÍÐÇ&Ð_ß¡K®‡Â¬½ÙÍ)K¯n—ýµÌ›íø¹—+Öÿû¬Þp$ëC­3`ÍNoî—Д¥. ŸïTCnì@Ó†¯f5ŽÓ¹Þ«<˜ÕˆƒRôÉÅïÄã@C?Ûÿ‚ŒqùRT¬ßàLw†äújU0ñx“›±¡?yq9•¢K;P"ívŠ»è;œ2ä>í ãïTlÿ·Oßèñ#Š€îîÝ–­ |LPïoÉbi%Cû?†’»{¸bó¯Mîí< PÉv}–xfAÿ⩦w `íúô´CÂæØþ"A­#S³ôk€ÚWOe³"¹6îbu̇¡&“G™ùL)å†~v>Í-ù™4ÌüƼ§Êž¸Å1´l¹î§OäŠõïz4œë614öu ŸÜÆ·Á`s;Ð73£‰ZÉCî 6úìÎÅi ÚtËLçÛ!nËX˜Þ½PwÓp¦,¯ËëtAƒæíÉts[ÖáKW{D;0ô0¥hÿ»]Áª&o<ª}ªêxzzí@vfÿvŠŠí/ŽoÏF¹@Ðgq™zèfPO~ÌÍ?°JÓ›ï÷³/{n;Š¡Æ[ ˆÉÝDî_<ÕnÛØ\Pœ€îØÂSôÝ…pÙ -ÆI,õ¢€þúäYJi UžñF¸«së.|ˆtA£‡M!©ßûR42u%œÒ²TÝñj•v bý”‹à÷Îßuîúô¼,¹3“•˜£‘Ðqs°mY ¹S ‡Ã¤¡ÎŒ»Õ´£èæÜ÷´¡C,C¿WÅCçk˸bý2¯Oc7–tg3KÔúIPóÇm`õs ×?0…¬Œ?Éõù@ôæŽgh¬¾9¨6ü–¢bûêudÇ7õ&¨‘©"Ë8t@@O_ˆ¢Ž÷buYº‰íÞ@P±ùž†r”¡=£šAï±Eÿ⩺wóeé¦å ½ƒÕÿîÏPɦh6u.÷¬ƒ=[ºú7Y±Ú•ß'¨Â¸tà  Õõ]ÁIc9EŸûÍ…3= =®€ht÷¢hù¦ÄÐ5‚Šõ×ÛW ¦¯ä Ú¸å1Ä6r«W%3‡„ìlt‰A û:yCMv¹UkÔææÈYBÐÜ5A°v¿! ve»¥W=U¸býTv3+×L­H[É>ý:LÑ´”DÑ, ãzÄK‹náÎM Ú­àÖ,«†¸RTlÿ†ŸrL2ãr6ê>ðêX.E«ì;°¾N¦ Uß›C¢&9qÅæŸšw¬Kù…¬:óˆû?œªìCìç0ïó*›¯XtŠºô+f¨l>`Ã213PÙ¼%§Ôÿ¨l^³û*ݪ|Œ¢²¹X«´О1Ðÿçèø‘N,¼Ê¸2O]qq»\IPÙ¼èt0·Ûж¶ŸëŸLÝC‹¡²yj“3•{Te†Êæ63`qêOжvýÌZLM)*›û¯Ûm–P´µóW÷˜ÁÂçÆÚÚ÷ÿ㱸ÔVòßÌùÇ‹Æædtú’@©û×fO,É (䆎P$»fnåMœᬘ¡Ëë`Ù:®œË2Ðá(AõG× £íïKÑ]þG¡Yq,EwÏ?IÜ:=%¨Xÿ?3Ø9³„¡‰i~`¢žÅ}pÈ“Ž8ÛDÐ{ª™\Hœ€Žxá¿zg3Tkÿ}pÕœÁݼ;„uì1š[üÒ <\ ¹býtêKgÍÒ¦¨[—ûYS/\àž9%'qO÷#¨sòs¸í¾PK£P87¡z~Áø“g)*¶ßÍ>#ÛÍþL*7Nîäf)ª¡<âzÌTî¢ÜøÂ!Ú\±ùoŠ®• Ú­O>ÜþÄ{¿§ên~fÜÍP³Øµ4ºgG)*µO°‹Q2C«S;‘Ò½Ë(zyÕ78³®AwnMƒ€ó=Úå=àEŸ_ >/ûqãtâH@Ë!n|ä2èÙ.Ÿ býËêRÀ•F1´Ðõ.,öw↚tcµJõu~êK½è†;ô Jû½Páe2 ´PÓF·›OPçŒËÄ(ë býj• Î7䪠Dæ^ÑåüЇYñ€jL›rW ¹;Mc ‚\æv0Qc sÎsÅöG¿m+DË?‘¢ª†‡„ƒ´)Z:ô½4:»%Íú“L4}ŽRTl¾c¡¼¤lf’€ªægAþÀ³€þÅS ð6 ©CÕ(:uäiÝö ÜÏ“—îYÜ ŸzÀÆ¢\î„5ÈêÛ©€šGÑÈ2oîÞAû þÞ{‚.U~Mž}€êÞ¦:ý€ûSYÙy2T¬ÿ³²—ðr‚CKš!õìNŠjº'©MÑ M|à Q~á\⪠Îs#mÞ/ˆpçÖ=Öc9-/T.Å›¸Óe¨X?›Uò°!x$CC&A”êPné•öäó˜"@ÏÿSM{›\àªzû²"‹DŠÖ_O¦œd¨ØþæaÏõA“(*gÔT®YÉ588<žF èágWIáúN€ŠÍÏ2žLì{Wj¹–]ÝâÆý‹§ªºÌ´Ö„S´¼¹QPºÀmL¶—•nÜÇQû¡ÆÓà ¾Ç‚³ !è£_JÌr÷KŠžR©¦—Þ¬4|³Ù" ¯ !(Õƒ¡7¤ÂÆ'çë?8¦¶½МO¡á~A5výû µÚt´’ß½ÌF‹v4ÓaÂW‚oxEõ-ê¥èMÏ 1ÈPI·RI×3T¬ßînûA}IWƽ{ Tò³) A6ì­Ç@Íßz±Í©7 š|$ žÌxFÑ/,!8GP±ý묉É:Šª ÷$'^{q7>;Cí+ntÐÙ®,u튊Íï§ÂÌ R4ôÖ z÷u Cÿâ©6É­&CG¦R´ñf9ªšÄÕùtè_%¨ûÄåPrÅЩ?æ é},ªí©Yëzp“F»{ÃûxÙbq¢ˆk’¾­íî è\…¯Ôê톊õO ,€Ì~@*ÎCI™ ·Fá1ÈÇOâj¹†A•B 7sJ¬ôô§/U Ë"þ¿qÍ ß‘ìCC«¯Bέ/*Ö/òãhÒ»)ElLÛ¨•-f’1˜¢·Íd!³Y(÷ÝOÐÃr©D½ÍqŠŠíÿÖñi9çGÑÇDJ:8øsU{ ~ÃÚW/v:yqÅæO]Ð,3Ãjò œ¨<÷/žªü¸*2ÉéE –ʃÖê=\W[e¨œæ ¨ÒK!z 7×7e:lù¤BÑÆÝ Ãì¥[Ka@·c Ý­ÐV’]7VŠNÜ~ÔtÕº4D.¼¨XƒžðóB ‹¶šú×ÜW{®‘qñ÷¸¯¿¥.†YÜš2x0¢i}ÔÀ¥´'A#¼S‰ÊÂT@{ý<É^ß¶'¨X¿D‡ PþFÐ9KÃÁ­­÷™÷}Z³c7CãôF µ¹¯Cm‚^.=DZ”ìý[sæCrFó Ew¦|&–eû¹—åÞCKr€€Žq9 1×Î*6ß;-ø4é¤"øìUô8UÙ']±ŒØ5„¢²¹Ža{p3 ²ùîÐiäxÓ‚ÊæA¯oÁèyG•Í£—k ¹iRT6ë?tzYÙ9Ðÿ§ÿ¿¿ã­] ¨lpу=;NPÙ¼<î—ô\Ö]†¶¶_}åzzT6ï_ÕBæ¸y3T6ÿöa:ÓZîCÐÖîOÜÑÊm ¨lnå\,ÝçÐÖÎ7¶:$LDÐÖ¾ÿÏÑ~ò’ÿ朳qðÔ¡Jcs ý×nZ¸hŒÖPç;êPûý0A?ÖÕÀÂ[½*™òŒþýŠ…–t!Åaåí}h!XÙq=ÆjÎWÞpåÖœšçRT¬ÑJafÁn@s›æÓ•÷7p½òa”_îÑùçAwÿin‹Û°ìÓƒ¡!EeàaÒƒ Ú¯îG†šu/×sóëwêM# Þ=‹¡E?†‚O‰7fÛj¶û¹/7!0JÎgr­ °_šÜaiÄ^åWlÿY•àðqC¿lõ†à“Àµù1,­=¸Ó_ÀIµJŠŠÍïr÷#ì¼Ùƒ¡“ÔÁ°M#{¿§ª¢P#3Ô|üsÈP«¡h„!#·úûþ)A¾@›£!ôS 7~‚±´¼Ï]=/ ²§Iº&Ý’¦oÔF¸@Ž™çfž:δj³¤¨XÛÞ ôÉäY€6¯ÊèÍÕvíE'ó¸/Þ%q×/NoÕ;¹¤‡.¤—1î¯o¹´ÿU®ÖÄHæ°ª=CÅú]·› ŸKžS´Z9¨¨Ùq㮬&ꋊwºõ¿?G)÷·æ-(´¨£è§nà¸o bû_? †ÑÆ t8 zWöáêô+€Á¬ý°â5ñù÷+.*6ØDkX+4ãó%êfÃý‹§úÃô<54§hÔP=ÚO@KèÓë-pzAƒfæ èoc;f«胋§Xiâ!5ÐGzóG Ç.­e cµ¹†Erp¼o9C'ýäYë÷Ö"v3ý*AßÛ˜\®€Þž9©Ÿ™ è†=[˜Õ7ô<µL¸ è›1þÌtáLnî2]0ÎjYu]P±~+ŸÏ·Í7³ÐmÇAƃAUË9ç Cj”snæ: ÎRý‡ÝmŠh#8 ¶£¨Ø~Ÿ­ç@1Z‰¡j¶ÉÚ¿Ž¢_C˜õ’C€F=Œe‘Λ¿8È‘½¶AÝXãÆ> ý‹§Z^ö$sâ Z­Vjm4c¢ÀØŒ#]»·€Nû9’¡½wز­œ¸gîôî_â6>òe+Ï´ã6˜ÅPíéYÜèô7‚Šáy@?¯Ò`©jîëÿÏ“ÝlBƒ;E-µÖ³%NÝÚ_Û”ÉI¸Î1«èø7+¹/âØðûÓ(Z"±g_J‚ºlöÝ× Ðgæ“©$^AŠŠõ;¾¨+4¤$è6¿|²þ]7üi5¸×ѵ7\õš¢*ý|ÉêÜCýàÑöIoCSwa•ûp·³Dò8ywrô$ˆõÄÕÐ6+éT­íÖc®r;í Ïbt):ñÉB"o­¨X?—ËHjg‚̺$èï“çÞ\½ÃgúÇh!h-sçVTaƱÇ´€þ^éÀP±ýí>Ý…¾jUξ ’€ÇÙè“°GpcÇiŠÞh: LJLTl¾›ítÈèÓ( ‹bÉFß‚þÅS\‘r'c  ޱéÜãÏÖÁÛ}GÚ › 'Þ®âfGBùÐl‚J®ö½Øa€ºlx ˦öä*ŸÛwæ¤rõü·7š 8õ2 ¤¨XM ÚМÌÐèî¶§×¹>& €:Sˆ k»vbJK"(j—¤m—žd¨è©›é?Ò‘¢r}*Ìþts¤èuײm® rCÛ˜[¾Œ[r3¶ÜÜÃÐןÃzညí×–+„êz©€–&‚Ÿ-Aw¾õ#ÙÉ{U½|œ¾šœNPÑS³mVW¶FŠF?t'¶§ú?œªì3}ºd:ÞT6ÏŠz 'ÔPT6·{Y-t þIPÙÜR˜OAï, ²¹neoøó9PÙ\¬¿^]5Q¾r‹¡²yî—¶`“r‚ ²yÀ›ÓP—f¨l®ãÞk#&´µýLÞ ¾I%•Ísfï¦!)Ý•ÍÛÙ“>çËmí~Ëì[ÐíÑ‚ÊæúfvÒõ¶Ámíüîö« ²Y‘¡­}ÿ?žÚÍr’ÿæ{¶"5²Zsp«<ãêÓWp·t&W£÷=0ÿ0ÐÝ’`&7*½õÒ:°š¡kK<™ÖÞx‚ÖÍÍ|N™0Ô9Ø—EGÕ˜¡ 5Äèz)CÅúï[MR÷&P´º6Ÿ$N:ÇÕé9›QÃO¾ìc¯¶¯ è»™f,çìx@OœóevváRôd¯¾p´mAÝVI²‡Ô̦¨X?§¬,Y#J@­®w`&öÐØ‘ý¶p!ÜÅ/h庎­q†Uµu#¨æ•GôgzCÅö¯Zü \ ®S4´öX9¯âVL§ÈkRÔÔÒ^=Y¨Øü?§ÃÀ´O¡€N5YÝ{¿§Ú}è`HHk&èNEh)ûÍ­mFÅêꪮÚEÜcÑ6d¶}8CýØ–M³¹Zu“ˆÓî“?Ž>X—{<ý'„õAÐŒq…è„@ÅúóëÆ}¼(Z¼e méÁM3žÄ†ºmeh‹£5û˜É=>ÏšÅnÞÍ•óü•ݯ,Ÿ\¿‚ :qx†ÂMÿ*Öoú¹$jâ÷’¢s²ó¥¿{tghþúql E-;ÝÜ$mÚ©æ< J÷ç=™ Úþ_ *¶ÿ‚] <ÛvPŠV—ÿ„ƒ–šcò8kfÿ@/nóe~z]¹bómÊ|²Zt­ÙV¶Í¸7÷/žjhz!Yýå3AëoX_Í­ËÈ~´¥ÐÓºýÙ«ýç¹që‚¡fò')š9Ù,mò ºjÑ6ÈYñ€j-úH&et–÷fGQ½áÆ \^Çë?ãÈXëž==ã"Àæèa]qv\þ˜ÏЛ³aÅ¥£Ü½÷@S÷kÜš?5 §µ«œfÌtmýu¾´…õÏÝÇP±~IHq@o†Z|»EÞïÌ};§€”¯§¨R/MPuóåJ¬7Fu@“»M¢gÍlTlÿÄ ßáÇÖ=}‘ô¬hwoˆ'‹a‹Z_kFÌûßãŠÍ?aâÇÎ/Õ`¨e‰-Õ—^ãþÅS•ì3%F¥TÕªBxîÍm£éËä¦Ì´Ê+†EÚ è,›CäyR1Wn@µ™ÍÍÍãѦ¾‚ÇÓªoEêû”Q´©£5©ºMÐf§kBl܆Šõÿ?¬×kTíÖð$IŠ„$J’„$!ÑZ×LBH’„„œC¨!é$©T’tB:©$’t_WBBN9ç,BÎäô>ö¼Þ=Öcߣ±Ý_~þÏ=ç<¦e-§v¦`Tw˜ ÷¢»e?ܰq_@¿M=E?\ûÚÆ÷º{d3¸ÇõáÖë”öß[ýaµ¾ä3ôÊšxù­ bý‚SÕAx}¢næFâ²”k8ÂJ·•¡“ì:]äb‚š×*@žuE•Ž«BÓ¶f‚ŠíßgôÆgµôYÍ3ˆ»8˜; é (®ˆdèÖ‹¯àJ`$AÅæ§x†ƒ=ÁP×Íï þÇ}ŠþÅS­W_,ýòG M¡]¤kô-¤hïññlâš/ ˜Ê®3Ô*3˜Í¿LÑ60¬ê×çØ$¨ý“ëÔ64Y’ýcZ F¯ )Ú¹ô iÞqP±þò‡’~Ãu•\^G¿æÆ|(õ󱆿ƒáâoÊÑ»7$ä–pã„ ôÞÓd®‡ýlaâÝMÜÒ0/EQ±~aªƒáŠbyšz­¨ëTûÎQòâH9WV¢4ôãYnÉ­{ôÚ¼{ÜkÛZ1“ås*¶?àð]ð}?ÐÑo.CY©'w­íj0ÔLæfÄL¤õÆ›¸bóÿÖÀ~:ÐþΚ0'.‡ûO5U—HSò#ÔdÅaiɽ¡½Þ:†šœ8ÄÐwƒ²ÈÞÃÅ\Û¢8ˆy.E#FB–Òx@ a™d770]Ê2¸Ö˰J³‰Z•ø‡è‡U1T¬|…Dÿû@_žžD!Skx¼^ð#܆çÆ,ëóÒz)”l¨l´ñ2èîõ$¨l.ÖÈáËô÷‹V€Êæcýç±ÊS@PÙ<åçdê3ˆ¡²¹ß™1”¡-íg5j‡Tçv Aeóìµ:UKQÙüÎïáÐnÔE@[ºœm„½Û¨lîþ¡ -Û@іί >#LrjÏЖ¾ÿoÏ™Àðß,鮯Zw0aè=/hÎ×y\ó¨6Õ¼•ä r¸Ÿ%[á‡v#E[¹ÀÞ¬…5^ÝÝ´gRtÅÖj(U 4ßÀ–γãÚý|5\¥¨XÿÃNá8°ÐŽSÍÀìèy.IÜCÕŽs©› Ù´˜PaÀªNmã¾^ïǤK*o¯¿ž ¨ÍÌ9pé¦9 býâ“rávþ>@熯€«å\£êé0ãÚAîEÕ02úJ7ÛÄ”8ti¦¨ÕŠ] ¿7Ÿ+¶íü³àòüAg‡•Avƒ" ÕM‰ôõ»*ŠJ\NƒÝC*6ÿîŠËä•Ö‚–;,ñÿ±[ŠŠ½ß‚S}5#™¾³ÛÂЂ_„ëâ¹Ý¾œ€÷Áý¸ûÜkí.• ?/Ú¸F›(l+à:¶yAâck¸…ÑR¦×k'×p¸5¨õY* Þ–=ÕwT¬ÿ§ Ä/¿Ð:Å0ÁíB%7Ê9Ev‰•¢Ù†[ÙLùq ûÄŒÕGvæªMRî¨.¦l¼!´¯O/¦¼1¡býv7=!s=kud$|{TÅMp^NމáºméÈ~H¸o…Ã’ \½sòlÀ¥®ØþIsŠàúr]@?*ûá#¸Å­! ­™z¢sÛ*úQ×°›èL¼DQoc7ðöÝÅý‹§Zµ»Tš6(ƒ¡^ٙĢw·|ù=˜fÞ@P­[¥ =Ðå±vÌí¹/÷û¤½ÌüF}áËš^'–¡—uTØIÓ@†Î›NXCŠ·ù\ì›ÀëŸ®Žºæ÷t$»}c'×gÊ*Í?ÄÐÅǪɗÆ}y¹Üþ1…ë_èÅÓ(J=NÁø¢h†úU 0yêf@ÅúÝÿAo(êií̧…r»Ú³£­"š›¯Á©!E‡OÞÍžº¾ç¦U$û—*¶øñ$xÕi áñÛÁøúl®õ/ ˜ó¡‚ûj‚1û-ìãŠÍßì Ž;îô•W[X4m; ñT‡%v‚+Óóªà0ì6çrµS×B—Ç==>‹|ysÛôx:³l½¡5ÉcÈe›‹ÜŽ&Í$bÁNîµ0êD+®’a)töÛ ¨£°°™³¹bý+×y³ ÝY€.ɺ=ÿMПæ1ðzhìLpT6ë_ÜD‹Ï†0T67ò–§Êý÷½‚G×*›OóŒ‚ “rmi?u‹ä]»t†ÊæQqõÄmV6AeógõÚ,áä^mé~ÍWyä¾– ²¹¥B'éÏU méüPµ˜s' Ж¾ÿoÏÍk_á¿™U0‚ÙëÅ3tšðƒ)ä~+µ­‚SÕ9éõ› ¨õ ìÒcc@Ÿlg7¯ï§h²³œzSŠê]½MŸzŒahmS,ûÓ܆ۗøÁœù\±þFG(¹ÙÐúܑĤ¶w¨¹Øæ¦3Tia.H¹ÃwAÑ pî„ø ¯“BÑHóûðü0´:>2U*ÖOÿk™?ë;AôÛ‡Š£\ǧ¿¨Íú@†¦Þ(8$äÆlh'}Òx¢?¿ÃÂ5ª Û??n!L¼PCÐ?Cì¡ôÉSnê² ø· P“4"½½4‘+6ß`ÇIP^ü•  *VP\‘ ¨Øû-8Õ¯–k©½~)C‹Ü•,ýRÁ¦Ñêz-&hþÒ³dâé|®W|'&ß´Ÿ¡›k[Á’©ÕÜœ-ȨÖ3¹Ò  nyE³¬Á\}@mfIiûè¯ë_0GGj±ùAåúߎàJÇ=‡_å'(z³©Þ¿'èÏ+™ kô€ÛúùPXW5П£-ØÝ¶Üs^´³0Ž¡býtÍ %ýûTN­«Dyñ?ŸÞÿ²Lªã N0TgH4Ä~ËâZÈmsè`ãþÔ{Ùa®ØþÆï°Ìý3AKmúAо nžìݰ ‚Fë™ÒÁ3>QTôÔ–>§=3]ú6‰5m‰ è_<ÕÒ‚D’g ÷;½ü)nÞÛ i}v$A¯Õ ö&RÔZZ ¶2´fR#”ŸÙ_†^ÏÓë°‚º:¯—ö³œûmÀbœœIѯŽÈù¯&€ŠõO õòÏ•¢r¥[Ê)Pr­ ÃuùÒ>øTqëUZ“èy:ÿ¯ÎyA¥‹6AÕ68ž¬­ûœ)u¹NP±~ªLúàI0E­zo!ƒÎ×p;è\‚åË:fÅ{˜=ìE·åîaË6 h¥ÍQA9˜1TlÄFuð+•tîý¤0àÿí°IB‚«(ºÄ¬'È¥xTô_5ÕEl@¿ ]TJ\‚Oqÿâ©:,›‹ê·R!‹[Ð^‹æ™ÄR4àí!‹Á«z2´fW!¼s]h<ùHäo§p•ÖÓ"Šºú5•^¶ &ز\=_Šœ4 Æe¨Xµí‡¥f½–STèN$^YÜWÇü¨ÊÓ@‡5Îcuú ¹UŠ¥æ[(z]£tpÌàfôêGî~·b¨Æ±ÓðåÐBŠŠõ[ué Y7àEÛ· æsîqíã>A±m AßÞ€ÆNóM /# }44ºÌ3Tlÿ¶o×HªÆ‚^ü|ˆÄ~øÈMøù¨L¥ø2·]à#šÒ+š¢bó»¬8ñ!¾ M›[ /·=”¢ñT÷쇟bš{°ô»®æ^o8Hõ¹}wYCOËÙܼ¯»è½ñÓÝtã3Ѥ設©ð¹¤ CŸ7†š9uÕzþŽ0tlÝzT>…+z /‘&RŠ.µê5‡¸­V…³€G×¥hdGva[/†:™©FM´ðÎdx0§ ß$CàªÅ îñ»Øí_ *ÖÏPÆMßEQr6ÔN è’¢$ÈR<h¨‘ôN)ç<˜\FÜrS»–|  ¨ØþbßpRñâ A«†8y—ËÜl#92ù@8C?mN‡ C×pÅæë,^N«§Úôýtq†1÷/žj—y•н—C‡^òÂ]þ1F¯1dè«YåPdDÑÃwÁðämnǼ×ĺk 7Æe><>š ¨ƒm=~£€«Ö@'{3TáÜ ¨1 ¡¨Xÿ´³RHR  èP¹`~ë¶€:EùS‰k(CÏž¹F<[‡qau,)ìèá·¨±Ä–Û:Q« 9PJƒ[qÅúÙõÞri‹ ª¤ Ù“Ú* ‘ô .á6 ÿMÝ®îàš³†TÇehô¥]djÓX@Åö[2-r½²˜ rš×¤‡“¸í¾ÓdOîI½­ ¥yP±ù®µÊ´|±ý´+…ܼ—OÑÿáTeŸ%߯õ wŠÊæµ.ëMPÙÜ:É"®þóõ_Êæ‘é»X£N#Aÿ£À¯£’.¯çn û[þð„¸nRT6>i)˜FŒ`¨lîr×é¯íKPÙœšêÃïA€¶´_qãXÒÓÐÿxÿ»=[é4PÙüÌ??Ý´º´¥ûu/IN_GPÙü©Ê)êòé -¿³Ÿ-Ø ý´¥ïÿÛÓxî#ü7·>O^î>OÑ늭¡I.÷JxŠ4_z õ:¡e®¤h–Ð<9 з±ÚTµ›×þÂáH¶WŨ‰ú«Œ%h§ŽÖçºÒƘ=…ORT¬ÿÞN,Õ(¡MF˜©s×WÃú§fꪳ¯Ì4"Ž;àIõ¯g¨iˆ\¨.⮚ù“V…¸Pô¢ö0È¿‘ÂP±~šÎ#ÙsMÂÐØ{(Tnä.ºßË/Ô6¹‚%+e>»a»n%E×_Šº?@Åö¯ú¾™¦ygôøTª75„»d2<ÏKâê¿ú)¸\›Ê›/ÿ[€Ë6‹*ÙÿwTìýœê±þ=aÁÀ-Ùl úƒ”¹]I?¼ØÂÙP@TÞpÃËmX}YA¿eÛ³‰;û1Ô;ÝCPzs¢®àð‚ nÒù6àzøWûŽ3±wË !’¡ç‡&éÛ¸îC| õ¼@Ç©œ!eÅå\í?©çÌîöÂX¦öˆ¢¢§Z#Ðë ‚>–ܤßV_•¢J¿'Ó)‡º2TTp8›ß~u,üþž hϦ§‚â ãÜ¿xª{ŸšƒnŸnehóÞQP0ï·Õ7† ƒ§õxà3åýþß+÷„Ñ« êÈÖÁä´#ÜâÓ ¢6€ Ùwš¥dzW@Í Îœ$eè‰k•poW¬01ûC7]cpçÜŽ¢†ë«(º¤yÉWnÏÐ;¨µ±Tc¬~Eܲk~L'C…¢[Ç<'JO¼*Ö¯òB¸¼š¡v–@¯5øá Ëiç¬b@Žfº="¹¶§Û#e -9xœª)*¶ÿBR-Ït y«¾Rß?Ùú¾øt®;EÑÔÊr¨y´ P±ù6ÝÙå€ö€žY0’9­nÇпxª!Ë†ÂØâ`‚¾ZÛ ½Os³Gýóå•×Rtõ¢hpEиáÆï> =Ÿùº>~MÐöÉ ìÓ°Üáy­™y‘9CëVœ+TwÛVæàYIP±þ³¦$ÂÞ›‰ uZY»¿®â¾ÍSMùŠ&¬=ß• rgÎŽ”³ûç;Ú¿tZbÊFœŸÊ}Þ×ìØ4Û&MðýØJ@Åú%Ãð^uÍ’ß ‰Š\RÉÜr;Z01™ÊÛ¨ºÚ4p‘XÖ› G–GTlƒÃú´aE 6ʱ£Sb¹*ÛIÀ¢J@ûœô`ugpÅæ_¯×"Aý¼ê]ã?ŒZqÿâ©Êy´‡=Wš z¼®Ž¼íÒÐ)õΠ³Õ“3»†8-ÚÍM*Ûy÷q-CwH•ï§s²Éºá9 5K*†€µ›¸rªóÈû›‘ÜjXöušë¿öûEÐkÖf¨ëíPdEÑ|ïÖ°åbA㎜—šøÍ}äìÈR?ê•¡ºzשN»¾ Íf‘¯[Ý\ ûÏ*Öoê>O¨ûùKŠz}nç÷ôí½@–[܇¡‰ÚìEN÷Õ›;äUHEµŽ­‚š§*¶ÿšÞO:W¥Š¢Ï4ѵõ¿¸<61+mªk¿Jj Â›ï?ì5†4&Ñšä6gqÿâ©Î¾MÎ ï ¨ßÐ_ÒÉÜuêóc7÷3dCíWq{j0«þúÜ—êj̽i0Eu ÞÃÛÄ5¼º½÷Z¿a´p¦tç° Ð·ìÈP±þŸ×‚[áj)úéõ¸zè$ASå³hì”mX‡¡+(ú{7ñ/ÈÐqIæ@½¡è©5•´4;І¡lù2†Šõ3ê¦ KL/´ÙpÑ×>͵qM/ÍaèMã/Ä_ñ÷ú˜Sœµk`§m)*¶ÿÕþô™w†êÿ:K '寏m¹æ#¹fÖ! û`AÅæ»¾W¡µ=ÇRTiÓvBocèÿpªÿñë 6ëWæ‘41ÍYÏ *›ÛïS*ÖG‹¡²ycå~iVÛ2@ÿcýG¨˜¹ Ðÿx_¤¿ÖÎCPX× PÙüCy> iÇPÙ|V®5Ô«—¢²ù]3`Ø™} mi?{³z©¡tAeóTSo¨rÛÊPÙ<ãÆC駺S méþº·YÔêÍ$†Êæž›—´¥ógù#ƒÚÒ÷ÿíÙ²äü7›“O _ h^C,©‰á¾,yS¿´ÏöS¤ã`s@¿üœGâ—ôã^lõ”Jsï4¡M/²NéEÛëMŲC\¯4gp›1†¡R3àJŽ/ býW.Ýñ‡‹ :êù:ø±FÐ çúÀu{®Âºé½[þÜ€Ü×3ý Šz¹ÿ!†b¨ùätrfs$·zÿc°yì$ býâ Ë¡ù» _¢v@Ö·tn^Ò6k€/C#è÷|îΪ?”HàÞyµÛ|¹bû]¼"YÀC“2ÔàS$#©½)ZáÉõ(á¾ØÑŽ.ß•ÆP±ùÇ.Cê¤ ‚Ú~Ÿ…ã{¿§ª¾mxôÊ4Ua1t¸Œ+±ì&Tt´æV7¼ qù]¸äÌYÞÊ<Š.ðŽ .ïw0ôûþHˆ¿“CÐs!ÐÓ$PÇW6fÕRîoóLjw6‰¡býßmœ ·³§z·¶;¸©lä~œÚ9ß°æ^¾éÍl&ô…°TÞ§èÌ ãÐE£/A½l¾Õ§õlȲ býò†B؇ã€.Jô#u[J¸)É¡‚¡wÜ fûQn„r,$hU´Nï¼Ô«Ó®Øþ×K#X…s E;]ÚÊBÌ^p+BÕ 5è•aï²¥¨Øü„åAB¹T.ÞÊ}õ9‰þÅS}¦¬FK QÞ'ÈyÜ›ûiL#59<Íü˜GQÕap¥_!C—^¼›¸‹ŠîÑíêÁ€zÞ gVyÕRtâµ48»‹¢ ›ì‰Ôò>AÅú¿½˜C¦\´q^+醚í\ךUìYG-†¾µ~AÍ›öqgË%ÂQC@ç|@R:qìX@…M Õ6ì½JQ±~=· ¥/ ÐD=jµµ= ¦WBºDþìØ¨Èmå¶l¼r•¢RÕðø1ZŠŠío7 ˆ=©ÉPM¿Ó‚»Øù7éü³ åç2Ê4TÝ¥¨ØüUʼn$ì[EŸ'M‚rƒ'Ñ¿xªTžJýS@« iþ–£Ü‚ }v}Ó\†ê½w¡ß·¤scJ¡îÂ:‚zŽIƒ¢o; “°d­ ¾ö é¹ý÷C?y¸:I™{¸oôq ¨XÿàôHzj÷@½Ó;²¶SqmË%Ë+šöqì­ÍäΛúƒJ S5xÉrý úrp©£ê'E×ë I¯f1T¬ß“£R&ç8P½ŽLéW A{>yMŸ7IÑÐã§!"BÐ=’OÔvr›¯cÏF+qE?Š:.`vfsú`õ$æzÌ7‘çZsóJcÀÕr6EÅæ›mêê™ý=õq-íyn ÷/žj¸Û%*7#и%=XÒ­(î«>” ð,ehÎ4xÞæ72ÇŸ Ë> è(U¿eWsL),Ñ_ÎÐÃË ßÆ‘ ¥6l¿õb@|@fd2Tô» Ûdvë‹! K>¯g7>œ hdâM½±?C§ýª‡u=:ôÏ´mÌ¥^¡eWoÓ­ör7TAßñýEžFÙT¬ß`2ž5U^PÅ¡=m{›¢¯%1ðxÜ@5æëAõÀ®ÒAmvã~ šzž ¨ëÊ-ÄoâL@Åö;ïk +Æe2Ôà@&ýò&Ÿ[ó# ùÔÐF#vç»=CÅæwë® jŽe Õ*©kËIÜÿáTeÇÛëYûW*›Mx¹k*›¿›ú•œ½Ûž¡²ùËüé´é¤•ͽ«[¯‘‰•ÍÅú×®òa‚‹ Cÿc\’Ôoa ²¹J`¸Nc*››Ü*'ÚÒ~ö¡ádm”%Ceó;òÙ´fNAÿãýIu‚ZÑMméþ…ç,hêÑb†Êæûïm'ÏÊÚÒùÃþ<×°§méûÿöÌLxÿÍ‚b+XµÖ Ьà>Ü€Gî´rói‚~šãLåâÖŒB—]}ËÌVQ´¶¢š®L4Ý3†évô»™¶re¨“IU(3å®Vàô~'®XÿÉÔª:TïZ#ÑVÀUÈ‘‡æí3ºiO$$Ëç*«L¢'¼º2LЕžOˆR5×òRUYè´fŠŠõs­5  UwÔÍB•n=Ì(º¶it j ¨›ÕH½>ƒfïëûçPtù¤bb WIP±ýn­·²#ÅÇ)ºÚ<€mløÅ5v’Úÿ\ÃPÏ£öpÍ'‹+6‘ÜoÓ Ð› bVŸÈ{¿§úmð bÔq% 3ŸJÈÁ›Î\¹^¡¥.‡b(ДN/œá†N4¹¬"@Í«’éW‹8®-уѽö14ênøöüHѰ^¡ ‘dú"w"SYMP±þG’Ö¶ÉtívKú4*ž«Òx Ü·Š‘; «Úš’¿Ž™í´­š¶Mahøà\X>¯¢þŸWÁ*I: ¢)ê1º½úR=™S3™ë¡¶™,‰´CƒÍtßÀµ™ÿH¨[PNQñkàcç ÛŸd¼šÍ ËР@gfÓ¸63ŸÀë FÑÂW¥pe˜7 bó¯( aÌa  ßí¶³Â£K)úOµÉFQjø­“ dªW?X&žÙNÑ&G{å`¾n=–_z å%?¨®Ê[Šú•„Ái9A.˜d@ç=ê0ë)1WAP±þû}Ò[}]ð¸Û~È„kÔ) ¯Šçêy•ŸŸ¥ÜWEM¤`½ÀÐa»‚ñ»0nÐ~_µv  YKÃéS ëWó]bý04'gDÌšÁµZmÄô+†êǼYw‚>èz~¼lèC«$kY)WlF±3š1›¡A} ÙýdonI¡-yW|Pë¤Ìk^k®Øün¹wižÞ~†v¨¡ïr¹ñT»Ó=5ï*÷l6]¡¶—Û[Ý’ŒNrßš÷„ˆÖC­}mGºj™1Tâ2úøåSÔÒ°“ð8ï‹­™–MT=¿R´hÌ^œ¬ÌÐÅG`ÅŽ1\±þñ ¦Œ<;OP‹ø!Ìûe•Úú!µÝ¼PóÁ˜º—>w`Ô]èºs&E¿9–À«#½5걪×TPô¼×~ïñ býŠŸ%Áïn¾¥<ÖUPÔ'%€<Ë×o‹êº€¡†ªÌÈw.A;¸ѶQ;*¶ÿ¤q{fÖ=¦Zxr\ÿËÝ|sT1ôÁÔ\˜3<ŒkYè#”­~/ +\­Àkö@†ŠíwÜÐ\Ö÷Ý^†VÊߦoJ座¼£ ã)êÚÚ@š¥ÎP±ù9cIÌ…"ŠfgNßîÜÿáTe7“ R »•Í7ÎayË*›/ô¾O¯ÙÁPÙ¼¹!š™÷ëZ†ÊæMW'Óš¨c•ÍÅú/Ú:‹<¾¤ÏPÙü»FŽTg¨lþ+¸–(—ž¢¨lþ£["Y¸+‡¡-íÕ`ŸÃ •ÍÇi_…®›:0T6_~ÿl2zDЖî;| 1ëžÊÐÿè7mô-`hKç§8®‡­£äméûÿöüœû þ›æÏJ‡=O&¨ÜžAe;¸qÅ™´0&¡‡;…¤®áÍ Ù\ hAÊ+Óu7×?ô Yš•ÄУå;`ÂhMîÝÜ2ð˜= PóÅçéä¹C¹bý{=ÛO‚<%¨šŽ V?ÂuZܤ‰eM›/…ª~—$hQ£ ìk(šê~]òÅc=C½ÛK`€v)E§¤Žâ#Kë7åT »®ö…¢®ž™›ËÐÕl0ðïè²{Ë cätn’¦»¨É5º~€ZeN`¨Øþ?V˜ÊVgŠªeZ°ˆ,îÍètH긞¡WN P4oAÅæ?{HcÅZ?3‚ÝËíNP±÷[pªWúŸlLŽIÑ‹i*Tiï4Õ5w…ŠN¹ ½ûöd-ñá–ïóe›²Zóm›¸ºC;4†ÀÅóžuÍÈÚ/\k‹R°åE·wG”v¶T¬ÀfWÉHýIU4YÐÕ-A F©Ã€îú&½5ùäÜЩE“àN«(†Fšž­IúÜ$û,ꀠóÖFÐäŒ,†ŠõSp}I«£âºûòHÁðI6wä·wd²Ÿ+ n¡%Ò£Ÿ†sÏ|˜ .¿^Q´J¸Mm¯ ¨Ø~ëvYåò»]G»1ì6 ­Tï Í®ÓÕœtZxÐtš ¢ÿÿ×1…õ ]Þã©~žÁý‹§ª ÞBÄŠVÝû(,Ö)æ~É®ê¢:näò‹àPÜ Ö‡ÏHSòüÑ<t. àÖ³‘BÀøS¨¹Y41–ïÈPÓ;Ù·8Š®Úµ:¬ÝÇP±þ&í…øÏ@Q±šÒʹÕù¥ÔÈâ&A”é3G÷+šü,µ=:óx6¾hqÒ„Î ­¼9älë§³ä q^ËмÓãáùéDî¢%j¶ãA¿íMë.Œ•¢*h­ÚzŠÖZ÷‡¯ü*¶?·§<ûvU‹¡moPÉ#®ïÏÒ !iízpœ™\±ùõ+öÁç׺ mÜ ObúOÕÒwº0w™Cë=§JïåôæŽ4ÌÅ ½¼d1Œq]Ìé ¨W˜[†ŒøFltOtΰ`X7³‰¢EsòÁ´Ë‚ž×-‡'fv€*ž¹KSÇYsÅúÍEJf¶eèõäS$å.·—B[¤I¸r®íÊpgû=#UuÇÝœ~¦üˆâÚüÊ<6¯ÐA³—C]®"EÅú¹l‰„æÌ5 º- B~épå<‹ðQ¾=³i7‰út†«~ø”±B)j³¢?lœxP±ýªGÒèΧ í¬ïB/¬Ï]´i=Ô4µömÙ‚!\±ù3{Ü%º+ÕmzØUh­^.Eÿæ€fB IºÜ´èŸO–)Üß'n’éjÕJìVŒäÖË K‚fÿnEóÚ_¥(œÍ> €¾Íʤǻìá¾±2 šÛ3ô¹gPô˜ býk {ÀM;C†Î(œ÷õäÝ‘@û€\­ÑÅ`¸Šb>-2+&¨¡u–èt‹¢äc—P@obåׯ ¨X¿q‡A3"˜¢J@FM‰m[¦ õô E=“] u[€š:ÎbNõ©¸C•úÚ1Tl¿ôÏCA’:…¡åãT³wäênZÅÎÝIÐKò¦ÌÐi=CÅæç¿'©¿ÞS4®x#Lø}œûO5Š´…•’Y }þc(ZÏäúª¦{T´Ît x›gVj^ iÁ³jêúšpÃneƪéu(˜ÈH¦CË­z°¨J?ŠªÛ{ÃÑÁ) ëO yæ7Š>lµºNŽãê;>úWÎÜ_q×àGYg@í;ž-‹Ý;‰¡Ý4@µ³.w~`É9qœ›Ø®~”¡bý¾,Þn'A/žŽÑ=êî«×tV;GXÚ­'  v`îÔ•¡cfu€”~G*¶?¤Õu©Uôt†&¬v$+<§qý’:@Û¸c\É¥³ò]¹bó[…ìýŠ ã\ÔàŽy" ÿéÊ>EoÝ`Ó[†Ê柋$®³¢)*››L¹–nHQÙ¼Æh5éQ’ÎPÙ<¯ .v³T6ëÿT/bžõPÙ|UývØ` ¨l>ÓàŸò¯T6¯KH GºØÚÒ~{ÆkBôT6¯*€IæB@eóžUãéÿÖmé~­ {È¢nv •ÍWlɆ ³»ÚÒùškÛ±‚»[méûÿþŸf=„ÿæÍìØÕŽ€^鳊u=UHÐ5¯=¡¢ ˜¡}¯ÿ's®eÎaéþ•å 8vó¤iW)ES ÃÔc ¼¡…Ýò¹µž‚¹b‘€*IC@ÎãÛ(T¬¿|âd&Zz_ß—õ²l hî?wöÆr™ÁCòAßÐo kè…Ž»šûý ‰íUÀP»…DÕ«jz»3óº|¢býèìHâ>¯CW¤w€Š/#¹o¶e’AY…€>2ŸN ØÃ=6y5¸ÜzHѱ]cÀ¢Ë@Åö»ëѵuzMM¿¸˜»Ø4’§Æšâ¨D»ÿIæŠÍ/y²GsïES~kÀåÎÓ*ö~ Nµ1ÞŸeu~.E?z²OžJ]·ðtÑ\ÈíYi–t Ök2}v;†Ž‹ “»(»¿- |詾ÿ,*Y&E=ŒF Q^Û­kteV“2T¬ÿµP–ðûµÕ|̪w¬¦èÏÞ'…µo†ê$íÂZ*'èﻩÔÑ‹¡ŽÉ§!¢ƒE-óV@ePC{º_†?Ê€Šõ;ª2*û3Ôgw,Hû¶æ*÷éÍö)»;}«yw‚ ý峩^] ‡¿°w^Ûïf©/X§GS´>î½DkЮ±°X·e ëäL«´ô*6ßùO!LÕ­ ¨]»Rø~¨? ñTÏ”n`§UPTÛl »pA›¡Î¹Ç`¢Æ@/í÷„º û¹Öïúˆ þ;- ðE4 æWŸÑ}+Ú¶ù;ømw]ävX5-«ØÛE÷T¬¿aá v>J¡f*£˜û›YܦNãØØ]k):2T™m²„¡æ×“ O¥ U®aN›ÉÜÏtÁ»77i+í¼óEÅúe¦ÝÝk(êR_ Ž+ŸHQÛ£ãÙÌv](êVц]6Ueè°$Øm/W'gôܶ¢bû‹æÞÖ÷9AÑå÷F‘<˳\ù'Ú0´ôWY¹èVúTl¾ª:d)´XW…E ãþÅSï:ŽYlžÎÐüÝ=˜Ã ®m×äÌ­|@óuÆQ]ïDn«ÕyýÿZ¦®fí¸’Ÿëà릊&ø€ý“‚ê6Ÿ-ó]ÔŽ¢×#Á²)CÅú'yKMÍ·1t®“ÍiÌ­ËÝ&µÈÌæÖßr¢r€k©7[²æy@Í×V å«%eèÚéRð_ÖA@o´6¤iÁëär4Tee¸¤ÏTÛX‘ê,ÇÐef;Éã¹'Z¿!ЧZ*:¸ìÖgŠŠí¯ %NN(zæÝaÿoÜË@¡ös5AµëR½Œ¢¢÷3ß›\:e:iSš®=¡ñT—>¾B#’âºàé(Ú\›ÁUޣɞhNô·çæQ•HP»&vÏûE­î†Ð³óâšz?]rlFA=µ¢=î\¤¨kks‰®|< ]Þº³=º2T¬’‘y*ÝÏÐ˧¿“ÆËÉ\•­¥àf9‹û²ÿ¸qü«€Fl ;öœ¥èËTkp ØÃ ª¨§N/0Ô (,×pÅú)çn3õX@%Ú=`µMWA]>ïÆPï[hÎÙNÑ+}½ qdk†6¦ì»: @Åö¯)­#~’³=ØI*GœàÞÏ "­Ûí`èªí…P•gÏ›_z¤#Øn™ÃÐå¶^ðvê ŠþÅS­l³FzµÛ!†–oÜC†ê掻lÄ\:L¤h•Ú)Z2Z¡o‚Áµ±Ü[ù)p°Ï7Š–Fý$=5ç1ÔýU.èRäÖ+Á7¥`nm'è>§  býÍs²"z±!ŽYÌýTO!e & eÇVÂoß`n–ÚÈÞiKЋ“a}¬ jzÐe¸;÷í‹"jW1ˆ+ÖoŒö(÷+P׊54M!ÛMsÓ ’ÍÔvßM)êzk8è}_EQ±~çö;÷Õ\o ­á~ÑêË.æÎch\=P½8nO‡rˆ9µWŠZÓ…ýó*¶_í©$©š2Tý…´~fÌ]2z&$+ô¤(YYN6\ ¨ØüTŸ›t­ª ÚÓØÏóý‹§znz6µL¼* Ÿv®¦{½(zwÅAzSˆôÆ@+æ›1–ûô¦”4®¬#h“‚©pá×5H?ͯ{º&¹=<¨ å6-9E.~ÉaèN«<¨:î@P±þB[HÒîÊÐxŸÑà÷P•{\á¡ÔiR7æ®ôí¿ƒÛuiGVö¾·`…°eø.{^LºÌô]Ä"V·k¸€Šõû´Ò bôÖHÐaΆà4|A÷Èå’á+’··-8ksÃ'±6Sõ½é­ÞiÍP±ý Ëa”‡!CÛ)¬„ê£:Üôw[¨Ü>; ê:ò`™R #CÅæÏú¾îÈÜÅÐ)53À5"•ûOµÀî¥p1-Ÿ¢3›/J.¨å>îÍú¥{ÔrÅBÖK3“¢7fì=jm´2Š>S4e¿3ü´' í©µEŠÊ¹5Œ\mN êww8D¯­£¨Xÿ"-_8íúœ¢¯«b¡•óv®ð!fÜ×ehÔ×,Óq“e ù&{Ún÷Ò%Ü).‡É[ÿÙõì57­}œê?¡“ ô¢¨«Ä:ù+3Ô재XXJP±ý{úAíÙN mÕi;ÔœnŽáKú¬áFù]‡™OOIP±ù:¥}×(šr4ÞÕtô/žªÉkÒðá1E[Î  ¹£º°»± }Õó³ðhc ·¬þ d½EÐäóÁ°@ºÐÝž5dÀ‚óÕnîyRTþÚh8õd# ‹Vû°­# *Öÿ:¤ÁKw‰€ÞYš 7- h”ÊÆ¾Ó½ã®Üÿ…įÖôÚ›ð4å•Ý?( h×9€>hŠ!_˜s©‘ÏkRTôSµ¢‡Pðä A›C·Ð¨¸ýâ"áž Ïý±ÄöO»Ã »DS¿iS´JY rø3Tl¿Ÿæn°,¹OQ·ö©ÐSĽ´"*ÖGêý«Ixß.ž+6ßv_±ú<PÉb]š¾â0Aÿâ©nØûš4PûìÁtóîàapSsA—´&œ¬Ý¨X?sÓ+´|ÕË“è™]O¨—e*EóÔ>Ónr€ÌêMVtíþËߪ@@sà‡×~@Åö?¥àBrEãUò¡ö]Onpâ:ƒ qºT‰tg¨Ø|•#ÑR7ƒDІ0‡ÓF;¸ÿéÊ>nÁ©õ,ŠÊæ; ×Ãk×K•Í 5Ö³aå©*›¸;‹…]š. ÿQÀõt™KOEÂmaÿ×AÚÃÍ€Êæå56$õþŠÊæ~Ϫ¨M†” ²ù—nÌîÔж´_þíã4:E—¡²yê ÷esžIQÙüÃxm¶oßH@[ºäµ"°/6PÙ<&§78ݪ¡hKç[võ‚c™ÚÒ÷ÿíqÏ­…ÿfÀÀ ½«W)ZguQꥩÄÐ.×*HÑÔÛU+ø) ~ƒ[ði"<ÎÇu/ $¡+*¸ŠÕ GË¡þƒ‚ÀÅ'Œ¢»ÚL„E» •»]i‘6ÜŠ¢býÝ=kéZ¯†ÎüÐŽ~¸™È59wº}uâV¸t#.Éܧ[ÇÐö+þùYø/É&%f» N@û9¼$5_æ2ôàŸJPÜkMP±~7“ÏÖÌF-ð9NGô´ýEò£¿#C{$;@Sšwpf(ø—›ê5*N¶+¶ÿ–ª6T\gè¥uðRa×B9ô~o4aN€p/{Wl~xÑc:ñ†? ªÍ¶Ìþá[ *ö~KNuÈRòZ£7Cz—“ƒM¹—6†ÓU¶M#7hɦÁý9cµ\çR†ºÆß-»¨§ÇоåF`6  M÷‡I»? ážTÙn?夨CTë22f' bý¿¸˜‘/g2ª°]~Nýõ»;ÿóec7ׯ÷1¨È‘p•;¤´ïDîÊ?;ÉöN‰\-‹ d©V: Þ3›­OT¬_Óƒ 6¾ µîI[èËíu6¶½èÎÐ]è”oKQßG‹™ËÃý\åþéä±Üv†ŠíLœA[ú¾õ:8Ù-€«8}&Ûy¥ …ÓÞRµŸ& ›ß°m3õ‰ÅP¿ôŽPÞ×€û7Oõ¹2\ï<Š¡ËÒF@ÕËÜ‹ ÓƒË5¹®ƒ-„Z¥5ÜåôÁ(Ç{ïÝ!è^Õš«{è¸p3÷ E£Ç¶µh†.WžÂ º«p ö¬€u?'pÅú'åOq }?5f§q»Ì©Ú˜)zݤ*´´õ8Æõ! zž/*¥h–ž2äÝëÀÐK ™qöAÅúÙ^œš €j7~ mV¦s‡8 mV&TÏ,f}èoõ¸W¨NÑ*8"d]%¨ØþºŒmÐCÕ‹¡~r»Á~Ÿ+wí¥?dPÄnòÚÙ`™ê" bó}5'Aª³¹uùµ“Œ®ô/žª«Ö"=c C­¿‡‚utnCô5bð%š«~Ëìl嵩„ÃŽÔðN ŒÛè'•lØ:ç5Eu*NB•n+@•ƶ‚iFÏ j¾ð­ô€¾2CÅúwQȄꆚ|:3}Zqo›jYëõø>‡4’•ܹNÁ÷ïúÔl:CóuÔ¹HÑ÷)^péÜV†ŠõÓ•.®½P»?aôáìPn¹¯¤5íàÞÖ{Nœ}3¹IÕ†<¢èòaPµ® CÅöÇ-H¢iËPÔ|0.Æ­\“@úåŸ'hüÇ@j½÷‚€ŠÍÿvk•ûd$E¯ßíC— ’2ô/žêq¯DpŽzIÑ2lX¸t7·»k>L†V¬ªQ[(ºH¹D*}´P§ËZì±û@î™ÒQp)f/×lÂ>º71œëçR ®:Ë%èÎNÍRãˆt@Åú«w,ƒæÃŽ ãPö4WŠJ,ÆÑê†ö€Ú÷£JQƒ¥èEO#²{¯? ÅC“i¿ö­¸u‡(mÕà,þ(Ü¿²Š+Ö+2‡cu*Àv–Ü%hêª8a¾U g”~Ñ‘K¸’ÇYðIm4w²^zÇ,‰+¶ß1¨vWh1ôËÛrè¢÷‹¢J}”ˆíJW†.LÜ c+í¸bó›ÓM!cçV†–÷ª€Ÿ(úOµ¾Óp½øD@S”ÀÓ¡'¥èã‘5 dOPßÌCæehj¾s‰3 èQ³TajS?†f]ÏnÞ¶"¨zX1ÌíÅЮG²È '(ªýv4¼XªÀP±þ÷VfCç㇠¼tľRÔDÚUú*0“¢äõ{’– pçG‡Ò‚…—´ßôQD.¹–¢=£›„óʹ»YAí«¯RTô Šç fÓ?AŠ>Þ£Æün¤èÈU£Øø_/êtÏ‚•»ü)C§[LfÊwèŒí!DË}CÅö[:o~”ÓÿcíΣr껸gj04IH¥’$•ÌÉuöÎT!„$!¡RÆ$$S*I3BEæeªëüNæÌ™’)™É<'á½ßõ¾û÷<ëzÖzÎj=Î?Ÿ?¾÷Ùû‹}_]ֺ׺I·òKuw)·üÞ%¼ç4¸ý|q [‹¤Üüˆ*†ÝÏ#™bŒU?6pÿ‡SU}"¤cèç±HÕüÂÉ`Üošˆ¤j>î…6²¸z©š¿¯ó´:ØK¤jþ;KnŒ“€TÍåú_™Œk—9 ©š;á4³Û%’ªùŠ“í1·í\FªæÚöi¬ð’µíVœÏr¿W3R57)R—Žå?b¤jž03¿VµÝ?¦S Z´©ÃHÕ\oC+é¹ÉÚκvs¯dmßÿ·çÄà‹øßœ9ÌìÒ@‰ŒÌwcÎ&!\mhé¾Éd–0aâ®ãæPüþÈ–›Áþ¶6’ü¦c¤ž“D^éƒn燤ñUmºËɨ=yÐüÛFÊõï¦ý••¦Ýe¤–"•)2”È´(a‚Ù2F:>©„Íïqs~{CËS÷€Ìt6[Ħ’tðY£½Œ,ÙÚ7ÿ$åú}i‡¿ƒõ%²èG*–+ãi´~ q!‘5A^Æ4î« ˜rïf ÷¹3.påöÛ”_®éê íÃ6rMVûa‡—ÉH:<`7¯/àÊÍHè&ŬÖAr©™´Õ÷ #åޯũ~°º¢|ð"V"Í\ìÀ¢ÝJnÕà4f7w*’ùóK¾mêq=˜‰øvj‰@ª]r†£®0RÛû3ïy¤Ÿ•¸õÌ1Ü:mzWµC²m¤“ÔÝ®>W®ÿÚ‹¾â«FöÓÎêÕéÌ5ëä‡#-–2rëÄŽƒ ®:ê-#«l{ Œk*‘#ö‘.%§™ZÖ_Yù}­DÊõK.ÇÜ ·ä—3± x9©‡µ0g‡DZëþr?y%ÃçáÏù²$ ÕBŒœH¹ý^­0µ&]"×-ë§O¤qÇ=6Ò‡6ÈW_í„ûs›J¤ÜüÐrmØqq¨Dnµï“.3ò/žj îIðî±V"§·ÀõEk¸k5•¬Í2kÉ9Ö¨Å.F><ã‚;^rÓ1­{$ ÜêWÂÍCZ9­å(lUi˵þ6M©ý%•;pó?ŸÔë’DR®ÿ˜ˆ‹Ðfl‰ôÌoŠÃv×ÿ—êÁ8ºµ#’ñuoA I,÷Û«õè;€‘+×oÅV?€ =·‡naäǶ%qÚI¹~­²»ãGÿ›@6úWUr™[öìÍÈ‘†àé€Ì­g€ ‘TÛRØAC®Üþƒ}&¡²$Y";G-Á2År®of',K˜ÄH›–_áyEró­¬ÑðÞ #ª¿C÷sÿâ©Æë E1'E"-<–âY뮲$@L?܈~3&X4á›µ#9ãi4<¶†[øä¦6ª#’·Z¬ÆS9Œ¼:Npº° ȅû ¹ÒD"åú;$uA³:ùŒ|>¹Z‡Xp¯?Ï.ˆDò‰£“tà³>×Hg¦Ö[Á ò»£ˆª^Ç+Ñ~wEF®ê—޹7ó¤\¿TõXZï<¹¥í…ºfróöhãëÜÜ‹£æAj‰«žSÉf— ‘H‡ùƒÑûH®ÜþUÑJäÉëq¢Ãd®ƒùjvàùa ßW³‹Û5%RnþáA¬¬o<#u·G«gHä_"ySKGz£1 I¯ç~’CÙÙ^dȸ1V¹ ɪ¯­%‹2îÑéøáþ0VRDnw®\ÿ$[IJ;FŸmƒßǹÿÙ$I-ú¨HúÍ´&÷è)‘é]-¥ow$•«FJ~u(É+‰Yg-’6—<‚N(IÙS-%–´BY±ÀJ _5ƒ‘»¢ÍÄ(/? 3ÞõgÞs3Dòf¥Þy°È  ‹bœv”Ûóæ6¾v¨Dæ;íC¿SݸQi{anðZîËýX>£Wö †q<ºÿ'‘EΣšÑ¶ò/žêÉ¢TÞo,‘iѧñÊšýŒŒ7k‡¾WiS|\ª¹kØ¥F.¹¡ÿX°ˆÛ:ÊVêšÅH³v ëýúI¤oû‘â¡n>Ü/mcîø)׿n€Š ò4j™/~ßçNž$JZñ9ò›.¾~Ͳâ#{ôÌë¼Ï Ôrš—§s¹·ûø g|k åú…×d y¯yëÉj¨L2‘H¿+f°ê#?œ-\¥È†_nÀÜ݃¹éÖÿüm½!#åöïnqô$R9²·œûÁÈÎØûÇ Û,hŠËç#)7ßöÒ*´{<ÉçmἘÄýNUõ±UcÉë*‘TÍc\Ú ·u5TÍgåû#¦/‘ªùˆ-¸Ðã+#UóK‹g2¿ÃƒDR5—ë¿qJKø8í ª¹·ÛRÔö·HÕ¼:ãò#-%R5÷Ë{'ú ÉWµí×¾™:|´“HÕœµ¿áÖ“%R5øØ ç+W"YÛýV%ì"#Uó² ì—f µß¿äó_ÿÈÚ¾ÿo«×)üo®¹4\Rs,p$5Ç ’¢#F0²Ñ Ø™àXFþ©±†âôJ?„OÖE@foQöp:"’¹É»QÿÏFž÷í…VÉHÊõåÆçyäõ̬2Erɘ;\"S¢0ËÖ›ÛÉØ û‰ÞHêY°­ÁÅ@Êí¯6M¼>'Jä ÓÞpa@27íù ˜±½Hç–Yœ¨$åæÇµ¯ÃŸDé|­˜Îdä_<Õð{ߨøæNùìÄ6¶¡z×k‹™¤qW .¶–NbäÓ>b‹ë¹^c/BÅ¢.¹ðírv)è+»£¥ŠÞóY9%µb¶™i_¢,y6] åú닆úÙbênD?½/@æ*=p™ÂÕQ–:B"åúkæîÀý¶@™¶ O$–qÿù|fआd@Ž©ÛI ·5ÇÑ{ô=hÌ„[˹‚Vd&HäÓ»EèØª #eû}gga’•þ]¥´Äܱîx:)†›Ûò,î´‚ë»cºj—,‘)WWcj·÷Œ”Ûÿ<¹'†íš/‘[=ÐèW0·ñÛÞìйH9Ð@ O2H¹ùQÒ;[‘zïcXµæ|‰ü‹§zͦ-ð‘ÈÅn=qâYä:ÝŒ…n¸aWâbÝ9Œ p¿ËöÚz ùm€•äÖÞO †„ø»ý‘Ô}•!ž¼H¿&¨†«¹aõdz1‘)×?~âZÌ™n‹d«á‘Xòu—Í4üKä·šÎìó³(e5‘ŒÜ¯B†y1Òÿظ3{:’!Õ˜¥^¤HÊþQ&å¯èdΆ!’ò©‚‘fñ±b¾ÿd$ŸÜËzjÆÕšg‹/~27§Ž˜?ÇDIÊí_¼$·|%‘¹Oçaç[}¹ ÿ¤4¹Nç:¢¿–3#åæ«l¶‹Â%Rÿb2‚a#ÿ‡SU}â^ŽÄÍgL%R5×=µ¼ñR5»8šh×D"UsGß4áÇ¢XFªæÑ«Ðj¡£Dªærý7l듞Å"©šŸoñFw]+‘ªù7§4ævÐY"ÿã×ïᆠµŸŠdmû ©´•ÜwÖ—HÕ\ÃûË9Èÿxÿx[t›`(‘µÝ_v)ªì$R5?¿î˜Â+ k;?$Ù_wc@Ööý{´¯à3ót’bzó&H•EŠêÊ»@ŽYùº%t–Èš GÜèó’‘f'ë‰>+#Er`ú1h–v‘‘½ãµat¼-’ÙUu¥…£¶Y·j0®v7”ÈM‹vÀ‰À åú×ìé"ùTòôÙ>Òï#JŒl²¶B²C—ŽèäæË}¹e$@Ç&y¾‡9‡¤32­µB2󿿨‚eK%R®ßS?–é8L"GÝ)ÄÅp^Œšå€Ôµï(ÜïšÂ-=³64Arì¨& >;’+·ÿø¶oò F:¦ïcs·|ᚨK¿­o™¾¦•t勦DÊÍo«ýÔ¥—Œ,Ÿ· sVФÜûµ8UïÛÅÕ>k€üýGÔ}ÛŠ[5÷ÎRg¤òKgìœÝÈ’ƒãÑÖÄ‘…Ñx¡£ȱ›I6›ëH¤ýÄlA×&žë·M¨¼eÉvî ¶Ù“‘rý Ï€ô¸KFŠM¬¥Ï=ʹF­ìàK–+’Ù#š³\ƒR ƒ¦Û¡QE{.붪ô«þeÁR þM s]í….”ë·Ðë$X'“ÈÍ h©¡àF¤(÷öW’‹DáÔåUŒlöó2«ð/P+M.ÂÉ6·)·‘Ír¦®)‘aãìy]n»%~ð&>k’»=~[qeO57{u²Dò§[œÛºœûOÕyòmÑyoœ‚k>N4p»/’ŽþÏÁûÍ! çN×íNG¸[Ÿ˜àâ„ $[8ÕˆùsøW:… Ã×I¹ïÛVcS—‘ó‹K¡zr’¦ E–>â’HÊõwÛ¯&Ùš´“ÈËÃÒ™ËìaÜ8ßæL­ÓQ%yrdC°OÚÆÈ—£ÌÇü3ný`™'sEr©Þm`)‘;îÄUss€”ëwh§ZwÒÈ?Fhw7…‘fïõ1¯¹>NÖXÚûw´dŽ·rë£ìé†w\¹ýæ£.‹#®K¤½Î¥âª÷Øì;ÈØ§ÁÓâWn~{S-©·ž’ír:I¹1Œü›§sEaZèÃHÓº0rÔ:®q^=¶8ÂÈîs—³z uF^¬±—¾æÞr¡¦­äq¬‘æï|ñšY]$ÍZû@„Û7 ‹Øã³RFÎ;&.8¡‡¤\§F¡â¨ºS%rèˆ~0¢G(7|®&ÎqÚÉH¿ÏÃÑïµHFŒ?À†?é,‘þ»'î±8nqâXáØÐ8$Ë·Ô—’Z a¤\¿{ÎÐ9C]$+‚úadh7 ?œ›§oa¤îñ•XTž]HêŸxÚ|é ‘G,œ1hK4Wnÿ§ì4áëç¶iQdýúXqÕ&G+çôþ¨$Íß܆G5%RöSûÖyq‘Æ`‰ì/µÁÛ“Úpÿ⩾vZ!;1Òó‡GEnNÅh–­ö“›Ùá“bíŸy%ò‰X¸Þ—«[h€é{pÕ Ú+MÆÅŠdÑÓp©à#¥À\6,e¨D9gàØÕ-¹rý')E@þÅS}Vý¯eÜEæg’Å Ý~æM•H/¿Q¨¸ÀÕ.Æm1Œ<;¸3›dùá¥8¶i#IÂE ê!Ù}€1ÆÞ´â†”¤ =w„1R®·¹CñÈ) ‰<·` fk>f侓EŠÅ‹Ç ™¨û‰Å%µâÖŸ¿ íÜ€tÒñÁå Ý‘|ýðs%2²øÊ ´¹´D"åúU3Ï5™@¦/j(n(wàZ'Yaë¡Kœ’­\㹑¿;)ÎM×f¤¸}®ÏÝÍ•ÛßbéeèõÚP"·<üfws'ŸŸ 3‹‘¼œÓ\ ù¨É•›_v¤KmYÕ!NLZKäÿpªªO³Ž€¡Cæ0R5i´·•YJ¤j>mO/˜m» HÕ<9ø Œ‹¤jžÞ9Ý÷œHÕ\®ÿܸ0ô©žÎHÕ¼á2…´wÔATÍ+ÇAŸ›‘TÍç­Ü¹çYÛ~ù«µYÈ'=‘TÍÓJ‡2¶{.’ªy³ü0|éjdm÷oŸ …¶©I¤j~Ç÷7 Ú•‘µï5ÃoÍ”ÈÚ¾ÿo¿xÿ›Æ¹Yâ”+ñ@ÖßÙ…ymêÊè¶’]ú=B"áÐáˆc$÷ƃhÚBÉ­ë3YŠáE o›hâ§È¥é¸t%Ú”ýfä¤Â-8À±’q§ÕYÅMs®\ÿÃs/À/¡.’aWƒ!Ç­H¯á˜þx„DvLØŠËÞ2Òùi]ÅÙ@¦›§ zú2òk²”o»ÁrÌ3\÷)×ïƒc/ñ¦o9# „ ·­%òh“e8¿o$Íòlq¶ûL®æg#ÉlÎU ŸÆê5µ‘HÙýå•bXi–HzÞ_/*[ê2²Ý<¼à ×  vO·Rn~d÷^µ iõÃ@šÒ±H¹÷kqª-—°Š³òe¯uÌí㑼vBõ[FHä f±¦a?®ëÅ&’G]OF–d„±FJ¤öŸ8Œz?È_?Á¹[$ïõ÷)"#Çìj>uë!)×?S¼ \Û¼Hµ{ÙŠ5—Zs[šmÁ™?Ûr÷DLÃk§!yìÄ{{ÀÈ_¦ MT÷A\1ÛÕí"‹¢°‰Õ$R®_X·Èš€g„IÛ]¹Q{RÀAw’‘='ˆÍ-{s•ÕÝñúâ F‚û?=ìRnî¥gÊ ©nŒtxvMá4Ÿk0c¹ð¡â<‰å…lô;FÊÍÏÊz žcdPÕ6uÈ¿xªOõ¶°ß¹¡ÅZf.r³M°[ý{ÜÚI¾eŒHƽ}»=b$Ò{É ´iÃÍlQ©Huê¤ÿ’¶0%õ#ŸÍ0”Þ<äµGCáÞ•H‰”ý¥¶°*›»@I–X˜ .'z1Ò¡ûG(ìa‰dÜœ³Bùµ g$àØ‚e\íÁžXí;ɘ´D|úº‰Dî7l‡3þR®ß.S?l£×]"ߥ%£qv#®Ã¬eìäëJ Cަ3ý¦Ue5•yïɼ·ÛAwê$‰”Û¯(Ù't~“ÊÈm[lÎäæîÒVÕ™(‘^ƒf¢_O®Ü|ÿÆÝ1ëL*#+·ëáÜ·+€ü‹§30œÙ|­'‘Å'Ú°ÑþVÜ_ëCŹz•56‹~ÀM±Õâ[{'ŒÄà5@öOB,øùš‘Ìq ö{Ÿ$![—cdvº‚ÌÝ9ShóhrýÓëhAAò.F¶üžʉ¹Îûêˆ)ý– dý=°#,™‘ž¾óÀwm$’¯ïc™}¹ÎxPaóÿ_«ÿ_ícs°¡çA®\?ß…ÙØüúzFf$íÇŠ{ IïEì÷)FFê­ÖwÊãÞØóq¯ôán}”Û?±h,<ßËÈK+agìA®Ùí}hô\W$¯ÎÖÐ IÙ/ïò ô÷ÙJm'›ùt,#ÿâ©Fj¹ˆûId ‹|!¬ÕXnUüA¨PÞ²¨õ%áæ•eÜ­“ :°†«–_8Ñ9EàFNèÃnHùi —s£¸g3ÚcE³æ©Ÿ›‚¿:[ )×ßóî3¸&ýdäÁfmðÛÊ\mñì×*ῇ«fs:XI¡ñ…9ÿ)cäÂ^èwx’m–eÛråú5°Þ‹ËçmÒbɬœdˆ¤Gcp#Zd¤æôVè6“Ùp‡¨^¬¤fÁBA?5…‘rûÇ.: K#1²~;à뿟ò슠Q§9’E“†Ša÷cERn¾ß–fBqo‰tñ‰­"ý¹ó ÀðùÐ0ešDö¼z&ç…qÕ¦ü.txÞWIz ™%¬\¿‘s‚‰)÷éŒ^è\p‡:½Ž´9¦!’ ãëJóîÎfä“-9¬NtW$‹«^õ²MW—H¹þ%šÃñBÞmFê?À1ÚéÜUê±èØ¢=9SñËQ3$׫PøÝè)‘ýã©Ë͹óª²E¿×ÿÒ(¥ƽ©/’rýæ´_„ C‘lô¸†÷åæo0=+VA*îÃßm2àÚ@tþÖ]IîOʇ) ú#)·¿jËOh["Us£+ÁAYO"Uóoþɘ;+NAªæÝªW ¢±¥DªæÙÏ-pBîX Us¹þÓ·&¢³ôK$Us·¼ºhÛ6IÕÜNË_Ƶe¤jþE¸/V-Ͳ¶ýþ$–€d’ÿñë÷ÕÂ’ãÚHªææ]¥òã)@Övÿ¶RÌÖ\ÆHÕÜ4CoVz YÛù¹K!­ã@$kûþ¿=ímvá3íY‘ði’ùÍæŠ/Íq—y(‚ç ¤še7…é¨ Œ<ÒÆ½"FrŸzáÅà ‡Zg³þVHî{ÿ†-œ0P$Ï C$3g¶aÎVÎŒ”ë¯×6„?k†äˆÉ®Búåéqo¡pú‘‰D:¤~çe¹!5ó0¿ý ñŠn¿ÑIÝ•‹ñ‹†Hæ«wÃWi@Êõó¹q‘Å)ŒÜÛ^]²¨8äg÷žXdTO !²>k° ÈO>Îø®ª#4±ÃÐe”ÛŸë%Ј‘ Ó…Añ£þea™Ò»$ZIî*X;,›I¤Üü²êØLÑZ"/ämDûÈW)÷~-NõìðÌwˆ’¾m÷±›ZÜÍvà=à#ËlžÃ¶5-$Ò9Xï^í†djf–¨v¨×ab;¥šW=‰´ÝÙ]ÆÈ=»ãÓßͲÇ"SYOI¹þ«ê5aS·¨#9fùFVÙæ:Ëógb»¦¶i33ãG®bdÕÌÛBTê Õ¦—*6ùíIóÔˆy¯#‘•á›PqrºHÊõ‹UhKjÛ“rÉÖŸl`ÖF¾w©ÕÓ´œ9 ¢Ûq—OÍaÖ1m¹'ºýd PÆH¹ýÇÚ<4zÇ1²²Ü¼´·rKªæâÔ $r›S^Z<H¹ùKî÷Ä}‹Ì¿^„F/ü‹§º½ósÖë!i®%)3 ½³];éJäõ¬åÈÂ3òð·Æ’ÖÐ, 'Y7–êÙæ3ÒêM[ôvn äL¥'d¯Â5pÝÀ¾î.‘…Òb,3þÉHÙþ×0»ë ü3¬ž¤Ù Ÿ@–žÊÆÒ!@Z¬‹Âê k$c\7Áƹ)´îl%nŸà:h;i’âa‰Ù÷²R®_pé¶ãü-F.=£Å¢ªu%òQ¿ã¢Ú`S$cæH¬kå ;{„ì©N¹¢Ê­§,H¹ý§s‡ÁÍCÇÙóö2()ºÈõ]ëˆw'"éÖ©PÌ1¸ró?.SøÁ‘|ìù®?ÅÈ¿xª‹÷7—ŠŸŽòSKSi:&7í30ç`C‘<¶aZ™2Þe…è’å.‘UQor'ÿÊ`eoL\Ûuóð©+‘Ž]ïÁ”7×t˜éëþÆö]/1Rö Fø,¼é˜$’s5ýñS†’ñT•­Œ¥+^‹ä¤Ëz’éð$Fnî…ë:"y´›ZùLæ.×€S7­fdúÅžørì ?vZ aÓgI¤õópŒYãÄMºº¾é*ÉQëRÍ CRö @`1{¬×B"ç8…²’9}¹ÓtíYžó@ö+nÇüæ U’w'4AÍê $.uºLäÆi¿g.¿Èe+4àý,‰”ý.Ç4ÑÊ¬Š‘ºlP³p ÷ò««¬ã§–y:ãhÐÑ›[<#õN¶åVÌÀÛS.)·¿Ü¿žW½fdƒÁè¹æ-÷Èx;ôÛ°Z$oÜú#FýRn¾×…¥pmçT$ëm¼ËN•7åþÅSÍö‡uÚ‡‘. .³Sµ%2Ð;¶® EòËØ(±¡ßPnÍ´0š°ȸÊdQmù1'2×dÚš¶ÉÒ£pÆ>u$!ýs¬ÃÈã‡,P­¨'W®×.mÅ›k&Jd©–= å¾YR(¸T¥1²®Å Xm—ÅM›ð‘¥f¼ÒÒº®¤}÷™HºvOEç%ŒÔO¹-¼Rö¯Uº£G“r‘< a‡Õ_”yhõ¸-u0zS4ªy£Ih_l.‘ªù}¯z8Ì/‘µÝ¯)਌[ŒTÍSÏÇ)g¶ ÈÚÎkcˆ7sêIdmßÿ·'¹ýfüoîqöǽy]\8²/îQ¸q?| Ž'Gsë¼>Žâªõü&:þnÀ=+¹°HµÉ 2ü\*º¼(’j£ñÏAm$CrÒDýf¬œïŽËI¤\·«+°û'@fçÌÅ×£M‘ôúüììĽßï¾pRÐâöûÇÚI¤åâUøøÜnFæm\;;½2çóUVÒf #åú9Ú û¸Ç@\ñÊŸ¼æú-Ê…ò_‰¼< #Ψ;œ;;á,:ÚÉ?‡.°OÎW”Û_”©#švîÆH3ÃsÊ“Gær§,ÐG·¡[¸Ý—é£ÏáP åæ›dc > ä›ÛXé׆)÷~-NUˬ5nx8¹nŸà´áXî÷©,$Ô–[bPÎL'ž²g±x>Íbd§:¸©d ÷Îî[©ã‡äÂ9»õÒë<|Îq«ÃÈ.z¹P_½%’rý¯ ‚w2{#YtÀ»3onÙ÷¥âÖ5ÃŒ¼›§8v\‘Sª6`î‘@ÆßDÛºÖHÚïù óç—È·k–£ƒ°P åúÖ‡âm—€ÔÄßÂð ÜÔàpÜj&‘×®BC½ÝŒ´Ü˜ÉôMšJd™ak´µkÏ•Û)l/Œ\°‘‘™‘Çù3 ¸AýsºcÏyŸå1w¦Œ”›?yòj(™4Q"o–cšzKî_<Õ붃ó!$ݯ;‚áÝa\ÿ‚¯lW•š@š°ªe«9Ï«æ÷íä™™àÝ =$¿¯^ÃB~¥*ÉUc> zg–àÕã¸iŸ§aÞÑ åú¯v¼—®N@²ÞÁ°Êf,·Î÷ 9j #ÕVZ`ÐùÜÆöŰÀÐÉ\…¾¨¬ù/Œ‚œ[Ç€|t|t6¿ÁH¹~…äƒ23¾‹0kËa‘,)MÃÌÇ‘½HË“18ñæ ¿Õï‡õÝês».Ù(döáÊ~ªÞŸ.ôÛ›‘¹vW…°ãŸ¸‘Õ z5ôu•ȵ ýÑÒø_ÊÍߎ~ºé4°%Ö¬º ä_<Õ]·ú Nû ylò q‰¡-÷ùˆï¢Ö°:Ù1Ïz_ÒçÞUî½5Gr—îgv7¢#Æ™Í0¸­7#Õ†š`çŸI@4Ú$.ÚÞIälvÙ=FÊõ÷‹®¯8ì†dŽ ‹õ³æ¶0 ïuH«!8­u[·b²Ïdî‘ ‚׋%Œ¾~»ò÷óÝàî> IÙO5¯ï‚Eõ2F¦]stй$w¬†äW½zx´….÷M»5¶‘D>ù‰ÍoU1Rnÿ†{FÀH¤ûOȵÕå¶<³ µb?‰d„š êØRö_ÅÑï„*P },ßaä_<Õ ?-ÙÕÆzH:HS™"ü)ñe•aóƒ‘ÐÊWµIá–ï¿Ç6øÞãZ_ï X¯*‘c-Màž¾’]4޲x“Y@®ì×ê¬fdþŸR!»ç åúOè™ÀÞ”ÿòÓü}ìPôîÄ7õq¨q$óÓó„ÀTWî›øw w6‘r¬ô´¬1¿Îb"Ò¿á)8[ô‘rýZ¼„½m^2ò}±5öJ}Èý¶vØ®þ¤÷ÉX¡rîjníì|õ÷Û4+8×T¤Üþ;5+àR“–™×4Šë™qMw€¦½N45[aÃH¹ùóµšb…þOFõ\Ž;êsÿâ©êº%°±Ý¶ùÛzK»£àÇÌþë”dlÓ¦xäÅ8 ¯,l';÷’È"ÏéèÌHëK—Ù‘÷7¸ÏW¿²Æ‘H©ŽgÇý²m-Ö¸g2R®Æä<¦}æ»@oÜĬ‡ÍÉ:o²Xì<=$'~T—>02îg',θÎÕî´ò^Û#Ù÷K5ìšä«•­@Ê~Áz5Åìcä·[KÐs¸=W­é4…î l\ïŠÐ£¤?#½vë²’÷¦"©ñ[µ›Ç0Rnÿ&ƒˆ©´”Èç ¿ÂÊíí¸1›£…ÆyFîÞ…ŽÒJ”›—EáÅõ|0¿LûLäþ§ªúœÜÏ<Í' äüýÙ¶‡«ò1sÃb…+ªùõ®®XîÐ]"UóÞ“ÕñôöHªærý‹ù²€éc©š‹~¯Y㌠FªæüíØŠ÷­TÍuëOŽ'ÇKdmûUwGï@7TÍgO‡¹ŒTÍõÒÌ1îÀs k»ÿî£&¸ÃÌZ"UóĈy?œ¬íüwŽ#Ù·#fHÖöý{⯮Æÿ¦Ë£\s|1#+›Äbàäo"9ýÉ輿¥Df$câîÇŒ,·ðÄ­f™79 ·Oòddü„0bÂj #g?RnN±æž»€7—Hà .øcÍ; åú[šâ G‰¬0í‡3žsç~ì‚3^ígä׸Püði¿HΈlSêl²*t).Kç _Š7¢ö1rðwìïßI¹~­vØcqÝg 2ÈÅ7ŒÒÐÁÜ4t‘TëÜ»pÖ B 3‡ã,¯‹Ü®çÂ„à™½”ÛÏöv`~–Ò=¢ÓtOT’]WE£UÏW<¸!¾ñû¤ÜüÌèáPÓ'š‘‘ãmpì˜q\¹÷kqªÞ¯£±(ro/²ñ E¨¨ûS ÿD9âH¯ŒTôé„õÍ-²ÏÀxìï‘ä¡Ñ¶X9¤’ዲž6;¹8;,rzHdjm¡¸DG #vºà£±þŒ”ë3Á½O|dd‡ÀYØ1g3·¸ÓB\ƒÙ¹U@Êõbàƒ Ú©½VÀ­{¸;,Ö2,åš}:ÈI rÎÊ$Œ´9Á}±f;ùÈÒYñ¬(¹®D^Û[%ÖãÊõ› ÁvþäÂ^élû˜CÜCsì|çŸËÿoŸÄþ‰JrâW¶^¹‘r* tk‰”Û§‰¬Oû\FÖT·eù:׸áÉèžÔÉ»ËÄ]çÛqåæ?Wú²ÅÝž2rÙª=à*êKä_<Õ@M<8ÿñËÊ D·œ5X›Û;˜‘Åž#Ävo¹MúuE¿ S‘<ð¼ÖÏ: ¤Ú„º ­Ã¸÷s,`@«@F:=‰g—kr_iŠ–j.)×ÿÐ#,ê¸È©_ƒ¦úqî™:6¬8d#4‡ðYO¹¢{S\{½/’f¡„9]¸ëºh¡õx ¿è‰];1R®ŸsÐUöéb o:=b÷e‰dúKwá<È8×.¨þò3·ù¸d¬x#»wÂÛ¹êHÊíOÖkÀÔ²>02{Àqñqg ‰¬YœÏvuù¡ ï÷º7{ÀHÙŸÊ›;b¦¾#/4ÓD›ÍÁ@þÅSýV?ºD\Ò_œwÎçq·[Á<ÍöiÓª¦,Vp7‹É°}L[$Ý‹G³–+êp¥­°25ž‘7{úáü:@îòžÕ™Áܪ˜Baáð®\ÿ>~Û Éã½@æ—ºmN×è×G(+Écdç8 ÐiŸHn>äÊ®?È25¡=kØm•HîÍè†ÍO·’Èo÷ãñî™í@Êõk¼é;ù|#3¯&1Åõº9 å¼®¶BòËôáPv³;7¤g!vÏD JÃé5;)·ßzGWÑç›DFê6Q4 4çþÜ ƒŒ—3Ò5Áyç)7¿ÅUBçs±@–utb—ìù?œªê³Ý^¥§©šo›Õ»M´‘HÕüê•W¬Û£ R5ïl‰êHªæ¥`ˆÃž0Fªærýß\½!ˆ;¬€TÍs£õqæ¸q@ªæáÐ`ù6Fªæ^Ÿ Á»ÃÿýÏ^þŸµí·#¼X|Ý^"UóCGsĈ6–Hªæ%Ûƒ1ôy¾@Öv?—Âóa¶©š¿sí*¨¯> dmç»9*t\k-‘µ}ÿßžÁVàÓ“šv{ÌȵóV•=¸vÛ…Çs“€ìª8/nºU‡ÛJÑgžÃHçƒæhü¾Å+áÓœuÜšmyp³Ûin]å5H©ÇH˜l‚í<_)×?§z³h7ð£@ ê-ꦄ)I‡ÐYØoÚd G%Ûã‰ã¸úO£˜{ÅYîu_ÆÔO=W’_†NÁ õ~ŒÔk{ŠîìR®Ÿç‹ob>{ÉÈÜG±Š«a-%2§£ycÂÈÿü>¹[ d¯—»™G³a"yhÊo¡Î{‰”Û_i¯¸yÿ _Öõ9½äÎÊ À§Iï¹Øe6îZH¹ùË2;²˜ü–Œ|¬ s¾råޯũîŸ~ì&]adÕŸ×ÐÑç0×âZ/V]ÉýCš± /Ž0ÒýÃeÐ))2dòeû¼Ç\µÊlaN«xÌÞ=ªçof¤}M:{¸. Èâõ9ÂñCz)×ß,j·Â¡I}FZ<ý$„záfD@Ÿ]/€ ëX–ßHàNz³ú«o0rI>BÙ@}‰ o­ ¥ŸéÝtŽk¼†+×o•ó@˜iØI"I—¡ASGnÕúAدI"g–Ø£Vn5÷©ÑPÌEFîñ3F£’( åöû|»(.ìZd~RK¶¸ê4×§MCü²®’›úàž3~"Wnþúõ†ÑDƒ‘Ì5-Lò/žjËL ì]³š‘+. ko®óÏŠc'õ%Rmù2XèÑ™ûn}&»åR!‹w&²á3rÛŸ8qn÷Çå¹VG Ç¿²Ç²æ)J2Òì~aìÊú@Êõ7¶ …бŒ\¾÷8$[%pSâ„Èþq…ä}çáðlÁFŽÑ³B*¸ÖÍðð³·"éyÆëOn†d®g®”U ¤\¿»ŒðÊ;‰<­é†êíõ¹K×|‚GõÛ ù㤠_iÇõ?#ÔwHíOÛacÂoFÊí~èÆ Ù~ ?;Ïf‹×pߌêO"§1ò¬z;Œ›¹+7ÿ“X*˜[•(Ƴýý® ä_<Õ®Œñè×ß"ét¨5zUjqGè4Â'7:IäÂwƒÐù¬.·ðòYÁC£w¯BÏv1áN<:oŸÑ@rµ`3Å‘;ãö:0:×N"M^Æb‡‚HÊõ¯Û¼ ~šÏaäŒfÍq~7;®EÄgØú{7h–3žù3ž{naL@®ž1bR’¹ÓƵ‡ºÇV2ÒÄÅ_ß3R®ßÙÕÁx"ë#-Y€š>ÜüKÝÄã©-‘LN]À®¾¹ d×' Ñ|Ë4FÆ-ñDÓ:šHÊíß¡•ÈæŸ 2"`#Ӻܓk·a~13æ:Ý3Á½­”›'Ë—ùkÜgäìhØÝ]"ÿâ©~þaŒÏÝ‚òÛ{Œ™©dë{sÑ[#CG.ÅàØ‘Üök86½ÍÈôNØF§ ‰ãXLù#®Ý*7vê ‘„íÛáØï@¤‚¹1³)×ÿ›SÌž/’ï|»ã‡Æùiðdtº˜;ûýhü´ú"á?»ˆÕ+Ndœ÷ñôêXFæ~÷TžÙòÈUailæ2R®_lÃ…Xöç¶’üt|*þ¾m ä·Î‡Ø¡·ôÐn6.è¶HƼÚ,85Ò@RÍõ±S‹–jŒ”Û:|'s=öM s›æ²=Ÿ‚¸‘á½.½7G2pðJïðH åæ[TöFׂ:w¯/úÌU’ñTû×h`Xëñ@v~ ýR¸¡7¦ã¶}¹Î–Na\«Þg¡tL×Ã8S˜¶âª@v`7Rsùɲ1öiäÂýõf&nÑòõÁ/ÂþßöHÊõ×íØפôòiï~xVgsgn‰Oâ­0"›öàfž/\ÛÐL"MË`ý{¶ÜË u0(T«VØ5J»)×oJïÁØãv:¾íÚ¢±ãY®öˆ¡Lx¹‡‘E·_÷úüà3·mŠ·¶çþš¢·ÕŸ)·?Eÿ0{ù½‘’tñ=ÂŽ-^/’!±]>UùŒ4Ôø ÷§råæ›ßi„6Ùx¦”ìÉýNUõѰ= þîR5/ìW£wT©š§¯Zk&0R5Ö×c6R5/ó e úb¤j.וYK¬ð¤jöó”X~EIÕ|\` ÎÌÏÈÿX`s¯—ÚÖw=¹µì×oØ'x}÷:ªyµñb¸v ŽDªæ¸G%¾ýdm÷ï}“Çž?Óe¤j~÷žf@ k;ZûCÂðÄŒ¬íûÿö´â3&ìŒp´P!©%W…ëÓ´Eò˜E±¢M‰tiæ³qkÇA·>wþjÜ”ø‘3ÎŒÀxÏ2î¥ÊYØâíùx›>Ö¯ÛÉT­"qËì¦@Êõ7\þž®[dÕ툌ZÃí3Ï~´Íåæo*/yŽäVí|*½~--†º õšweäë”x|'•[ÑÉ ½=-”ë×ÔÙw Œr†¹ 6žSÄýYyFF´A²Ä×^0âz¤Æ2§‹ÈÈ KÖ‚–²¡DÊí/V˜£w :’a}›¡{‚6÷ë2ÖnÄ6ÔMÿ%涺ÏH¹ùc7ÕU·[ ¨[8rï×âTw…ý*ž>ɹZí!ü»#ç7¿ ™%†i߯ê~bdß_Ýð^É.¦pÙåÞ@Ô·ÃfEÜ› péÚ®ÍÉ·Pñc<#“Ï< ÍÇ@Êõ7Û9 ÖïÙ亾÷wÿ$®§ö(R9W$+|›3Ã㌴ø<ÎzÈÝô[wGÖ‘ÈGæÃUs$Ý®Œ`fNz‘rýò+[c§½/€,­ó\š~â–iXˆÅ>rÕ˜²Ê–I\çëö6òªH*¾¯ƒÿ%@ÊíwÔÄÈñ;þ+„_0âVô)×­M%Ò(°'æ(ç2Rnþ›Òcââ}©^³ú ØËý‹§zòùølÎÈȲՠ½g5·õuÀ,•Üûî}ÑlR‚H.~=  @ªíjÑ3èõ&%™ž\(Øtø¡ ÷H« ëÐ$FúµNf‹&žHÁk)œ>ÞD"åú;ßjÞëÆÉ±@¾™ºKÌ÷G +,w_C $rgêðÙoÎýÑÙ-7og䘹 ±*«RAî^{<œ·32òP Ä7‰R®ŸËfè¾ì!;ªœ 41[sÁu¾7A ¿}Åø`=F–upNuà:)®BŸÍ%Rn¿KŸ;°ð˜)’—[+᎑9wa‚!êGurPI˜`îÆ•›ÿíOStô9!’¾ƒßò@þÅS½›^ÑúÛ¹Ðï9LÙÍí>ÃÕR¿ ä[Ýú¨w9ÈHóPaŠ«’‘o\ÎÀ¨¯ºi÷ØKÇG1R1p(Ü:¨'‘rý”Ã/Oƒô/”ŒÉHëú‹…ýë91r)Üý•ÅY<3mܹá­:á{›+@Êí–´š½4Gòæ‹HÐ7þ—™±sGú32ýöpÒÔ”HÙûÑz'Ÿ;ä|c/ö"æ¸@þÅSl¥…ów0òx¼) YÏ­ŒßÙc"ôùØ VFXs_­vA·#•ŒTS¯žÉC+:¢ûÒúH¾9: ‚ ͸7Ü%¨éÔL"…Àp¬Ø5QAÊõ²·+Ï‹d¤Yþ ñú‚\×´Á¸Ðj„@†?6ÁmÍ"„®}À²êׯÙJÅ’^ nqK7´yµƒ‘ê9¿àÏ» e¿+,"ŠFˆä±6 ŸgÇȵšå0Á,–»x…y[—»cº:üŽX däŒåxz1#åöOié ­þù4%¯ç6G'¸/#0Jû2#ó…£ÅŒû)7_7«-»ý™1²cÚHžÙZ"ÿâ©ÎXÜËfE3²‡z?ì¤>[tf –zב´Öî%íäÎHÍ7cqóàõ@&7ªÓ¾qÝ…†Ì­g.·¨«&ÓïV$’¥Éébx ÈÓ÷fC𥩌”ý®Z§¹R:òƒ‘†o Aú‰Ž`¹ Hµ]Fàô܈ë¸ë°=è‘H.ëž ¶ KiÛq­Py¾ 7ÈÐÏ­|È•ëWÞ8ÆYÂÈ/;KáîÍd®Õd#¬)&›4Ɖ¦iÙ oØ'’nëÚ¢C¬:’rû+Ór„·fH¦{¨ Gks÷tiÉ#~™ñÖ^‡qeÿUhcÞ÷_2ÒLk Ú>l,’ÿéª>Žƒ†£‘Ý/‘TÍo7ƒ” SŒTÍ=gw†ž‡TÍ/¥„÷³©šGÝõĸe×R5—ë¿cs(,ÝÒZ"Uóc¦Ûõñy"©š§=4ÅêFŒTÍ{›Š_¼WYÛ~QI:¸óøRFªæÅs¯C¨sÿ1Àæ±ÒT¨‹ÜZî÷+]¨´h¢Ž¤jž³j¿ÓI$k;¿ôx}<~ ÈÚ¾ÿoOÕ½@üoŽÙº½NrȆ(˜wh·´«>> `dÿ•F¨ë9^$‹ML›àpFŽ ?Ã>sãÞw oÍíª 7ãsƒz9AZÿH mgœƒŸÅqŒ”ë?"ê7´«>ÇÈf­p·—’[]ãVÇé÷¨§òl—Ln°ÿdè×BIç™Û”'³YÒ3 Úêßçš¼õ`ÅÉJ)ׯ¸|€»˜Hvk[¾5g¤ÚœK…ASv*HëÌL¡whFútïÃt¬ù¡ÎP!ÅDb¤ÜþÊXÌo3O$õbA;T’FuÚâI›·"ÉntÁÎÁ‰@Êͯ[Ð_g‹ägÝñ›´ H¹÷kqª­Nô„ýÓR€ì·´Bp7ŠávZÒí:– d¢q4jèäþ}F˜cò™‘ÑÓ·Læ¶íiŽ÷žq?ß›€5^Üš Ø | ÛkÁ€»jHÊõïõ¬+z Ëb¤eÄ@4TÌâîrö¯HÈ"'waeµ#ŸVo½7H‡eQ¬0ü?¬ÝyXN{>ü($I2BÈBj}Ï•yÊLf’yØdž©$IH’J¥I(Ri ÖZÍ$S’dΰ™2µ~Þã}®ïó÷ïxŸuxëŸÏç±®ëÜ{_n÷ý×VÈ¡vóQ³Q×­Å‚-KeR­_ºÇJLPÈ©­“±íŽ7}ä,Œz7…«¼Ë@§æÜ^ ôÄÏÖs ‡ÿ ½þc@ªí¯´œ/Ö˜Ú„‘kæ‰ï?ÄrÛšœƒs™®HΛ/´L©6NÐ%ˆ»õD²äGuáÕ}ðTucÚm@žíá$ûÙ…Û~Ævø ë¹UOg°º]î‹SEû‰Œ´hÓB ëpdiÒ(1iä:n‡eè9±«5X+õÚ)ä~=Cñ[Ðr™Të_½ùTQ«žžBžqrýªGHd;-g”Ÿ^£ -Þàî¥~ÜÇUÂáïÌõÏÕßÍù,“^YÖH|7¤åËlÈÚ…Të—Òö#ž5ë¯c\D»±¥2¹.®šxwP…,êÜ\4[â*‘!zK«Þf¤É´Þl×´[ ©¶?ÒßAìw¯&ÈŸ7gŠ­^õ壮Òyø<™¬m†¼ÏÇRmþªºõ”v#ݹzdöÔÓE!ÿà©zþp–æÞedÉè‹òå®Í°DÉt×®ûü­òµÇ5²¾ÛaôœrÂþr¶Ë±Œ‘EÝÚ£F£ -·V…C“-2yèô¯ï¸¯3AÆn7Q ÷/THµþ-^8ˆ^_a¤S¯ib̨Y ÷ÊD³;Û$R¯V±Êüñ gû4ïÛWãŽ÷¾}qñÜû?zˆ~#ï*¤Õ³þ¢ßd{jý„§­Åô¾µ%²Ã3±àà;Fúmj%fg4¹ç¾¡xï[7ë¼±ø·ÿ…´]d+ž íÈHµýæ7¦ˆ©† A~ /Z>ÞÅ}ØT[l5±ŸBLÑ?èRõ£®g.œÙ­ùWêŠé&SÈ?ù©êT&?>k!“®#*äÀÔ* YÒO”:ÎçÎ þ‡UýŽk¶DÄ÷À–ÜøOáÐ}7×°w:¦9q«w2·ØVá.žVM .ºËm‘­-˜ö©ÖÿQéñÅã #:ô÷*àöþxerWjxo{Àµ«lw¯rÍ®äËúuœ¸ö/û£ãÝ™ÿöX4Â^îUHµ~ØßJLª· äQ›:bÑ×dî!÷00ò6×®‡Rô?pû¹¤`ÿd/®VruLpG&Õö¿g+Þ³ 9Jk ¸øy,w@ŒŽ|?ÂþU[Ip°a¤ÚüNúE(°>2uí^Vz ˆû'OU~,{µSÈò~'da¨÷uü8ÕK#…4±Ã»êÜ…f}D0=…ôÎ!6|Þ ä¨ZÄúŸçqo¬*vŠÔIûŠÂ,Ï;2®‰Ï|?)¤ZÿúîÍD›m@ 5+á{YK$sû‹²I^Hûä~ÊÙ;9eÖH%v†µLÎìwJn¨£u/Nö¿"§ÞGíú"©ÖÏÑñŠÖ>9Ü<s~ãº::1ƒÍ÷¹cZgÈ㦇rµlôDƒu‰ iÒÍ^t®îÉHµý~YVâÉà ƒSÌÅâ^ùܳm,e¿~Y 9 U†ꥑjó§k«,œ[K Ë»-’·jg(Üÿÿ§ªùä[¶’{·HQHÍÜ?ª‡è8¡X!ÿ¯1ÍEÏwàj<¶;_²FúZ"©™ëIÄ®KÔÌÕúçE¦ac®–Hjæn‡¬”Ù•mR3?T܆¶ßR3ÿð ü¦ec¿ÛÏjV|_\R3?\b£|S¤f9ZG¼sA[$w¿½N;Ñlû=šyî‹>baï} ù»ó5 Åäºiäï¾ÿÏÕÿïÿŒðÿK«>Ä1^.2ù¨~1=Â+•,™u!Z6§öã¸]¢÷Ê[ÜÎì»<Ï…[rNï¹Ôçÿø —‡æNXr‰Ån¾,‘ƒ~Ô w%ʤZÿø°]l°wdìÂ6ò&g…TÛßrÍWäþ[!·,¯&¶ÿ€lvûf¥ì±­Œ¯÷çªÍ·Iûb­ëÕä§‘‡Xîý"™T{ÿ7Nµòn/1øÊF™Ô[ÜÚ°*H³¹o…¯ÊcF:~­-‡T—IËf'™–QX_²¬Ÿ3*žuWÈÕ—fbÙÌ! ŸùtÆÃjûd²õ‰8>ñ&È1±9²÷±WŒTë¶Á>ÙjÁSïÊ|^1ÒâÛlL6X!O‹Ù¸°õ ·ñL«ÚâT²³Õ5ö¦Ö_ ¹§cUñ\É 9³æzí¹jý—X¢Ç[áòá8n™W-Ñ¡NÈk#Ža¥û®îõî¢Ã ÙÜ®‡ÊPö5œ{ö[*æm<¤]Ê¿a³Ÿ.Hµ~ã¼Båj¡Õò…©ü´çVîêÍrÕjÕ@?öTÖZ§'“¯þê*f·ØrÕµ­˜ûf7WmÿÖ#}Ä•SÖ)dÑpk1öÈX®ÏójÌ\Ljëvý”Ž\µù5Fd°úCâ²Í‰|œe§¹ðT;~½‚¨ˆR‘=O ‰×;®Ÿañ¼Kk…l5ÆBÔòòíKVÆèÈ/®wQÈÝSl¡ó5ˆkòl³_l.“Ñ[÷`×ò‘ 9ôŒ9^‰®ÜU;¬E·[¹jýŸÎsBBµd…\p.Þ;OsCæÖÅŠëÇÁMœÁ²4à$»@èìÄ=»©ö¦-e¤—~…ÐûF]?j`‹‡e*¤Z¿±6Ë|§[lõѬyWÚb'߬Ÿ/ܶ±Q¾Üé ƒ°{Ãs™\ÿ¨žØØ_PHµýÙ¥LŒÎꨃ¶‰âeA›Û4AO\êe*‘Å…H:ìRm~Ã$#qô‡ë2YÝì-b–5ù?œªæóa“+Fy R3×Õ6g¨R3/ùô÷N®THÍ|óëj¢«{ŽLjæ]Þ5_Ž;R3Wëï3©«*¤f^é˜Ì\ß÷‘Iͼ÷ûiðj×P!5óõ¯ZŠF.Gòwû)ý·£ÿ…ç ©™¿2cBO+¤fžl!Îî±äïîOú)ŠéºÉ2©™û}8ÉC÷ƒüÝùé¡¿® Èß}ÿ?ž#­Åÿê œ5Þ¥‹ÞÌ@LÅa®¥¹ {»¤7ìM)‹:çÊñû‰†l#Ÿ/‰‡å ÝDàI+… ¸£+æ†Èdמ¡XxBVH»êDÓ)Á©Öÿ˜qñùþ é¹°—øÐ° wLðö3ÆE&÷Y4ÄÎå³r¯á¼Þ°äÒêA{Åõ¿a(µ3àžÛ5î•»©Ö¯°ooäé­kz.…©‰×Á. »&ýúÎü/'®šŒ³³p4,ŀХܓZÃp7ß–‘jûíÏíd /\²õÆsÌ/t7*!sOáz æ–šw?;¼fäô¶Úâüõq ÕæÏ e¾"HÝ¥Ù’ù×Ñ2ùOuMŽŒŽ=SÒ*õzüÛÎ_0ü]2wØåF¢Á/îÀ[23èíÂ1õƼ¸ ®ruvhK ×·kÄÖjä¾]ÚC¬6¯•BîÈÿG3AªõŸdd.nÜ~“‘å—MÅsÁ@V|i"J+(¤ù¹âÔ•u¸cºN–tý£erZµ·l×„Õ ¹}”¶8Ð!dUËVÞjqÕú½Ù]ÇçòNj}Qq•šòÉÙ¹0ð´4fÁm™´êØBì¹d/£­X¼î(Wm¿mûp*¤[ë±Øúó·§ì€¥ÁÇ@FT9Ꙟf¤Ú|#×ö¸ç…<Ý4ë ã¸ðT‡Nù Ÿý 9rdMñúß'¸ì£™8µfGnˆW'ñ»_]™ÙY[<ÅN*dn×¶bÎùæ\û¬ñðñ29 r®¥O €E~ˆDöºn F|·PHµþÞ7•[3@^<¬#n0ßÊÍ/ï!šŸ©ÅÈÛvÍÄ6 sÊ´[!WÛ¿ÓÜ^Ü: $ytàæÔ5_M)e¤Z¿v—ۋѾö ¹÷©…÷¹&÷ýé.ìUëÙÜM ÐöÖ!n|dsÚÙΚÛì2¢l›*¤Úþ¥yŽHk|J!ÛÏvG‰k wë§Ù¨>±&÷þØ·xüý…LªÍ·ú\KÜ×s€BÚm«)jµëÊÈ?xª«³ˆ6óR7ØDôõsãN÷m)žŸ‚‘/ tEß#f M‹E·éð^^=A.ôz€¥eõÒÆ½ŠøtñFFö+o&6½º¤cXmx¼wáªõîö)3w€üº3 ·;sÞ{‰‰Vs‘pz ×ÿzUÑ=¡4•,×¹‹×IõAVŽ8ƒçÜɎžiVŒTë'ø[‰ËÆËäͱ–âõ¼µŒ<øsÂgUÈâe±øwÜåzâÙŸ¥Œ\ŸŽÖæÉ ÕöOµóƒoÿ…ìÞð8â&žä4=~edm³,,.‰©6?Ç6ŠV íÚ4@ÌÄ¥Œüƒ§:qSq霙 ™¬o.~úõéI>zUˆÒûƒAúF¨oGîƒ}ÛQ¥ÃÕ?ÓSã»ËäqÝCÖ€\Ö¦&¾¯äflEüÄÆ iÚÄ@,ìŸÆHµþo?aHƒ… Ë_:cÌD;óîxœjÌÈÁ#p¯£LÖÝå sq8Èó%2ë¡óž‘MG…Wþ0…,…»dh]ô/¶™o>••žš!ª_ ÿùw ¦)¤i„±XÝì¾LþÁS v‰eúé)¤[î ¶²_gîi!:·“H‹Û.X?,]&Kv~Ä~‡V «TÄbþ°íÜ1&ˆ:zP! »j‰ƒ­vpß,ÊÃLÝƒÜ s[Š“×V•HµþvMâÑ„'2yóç+t¹¹yc(,=*%2õò ì9ÜH!+ò«ˆn÷g€ìÛ7 íjâw|».Üø‹YýeŒ«Ö/¾O*{?00•\v÷[í)“IÝJл×F¦Ù%¢ªë7nJç«h:Lä„acrßP"Õö72¯‚í~d2ÄÂc~r­;ch½n iüLA½ºÝ¹jó{iµ׋#»¬Ðõç¬ù?œªæóókÔ\4T!5ójM#ÐCl©šù'} D|_R3÷îÚLlÚ S&5ó&ѨӣHÍ\­ÿà”w0k;]"5óëÃÍy…Bj殣gb±¥'HÍ|kR„oÔSÈßí÷a_7(¯?Ȥf^tj7rzå1R3wx†qgV)äïîï”jŠ15 R37Ö{iSÉßÖ>Sß9‚üÝ÷ÿã9_«¾øß4º]G|5t¥@>lg(¶†ŸVUAÇcºw–Lªõõ¹†Ø`|#?Øj‰Ìns«yÏGTSÈŽáеÉ]²^GœZvP&k5©@¾T*|ObÑáFÚzz ÙŠ‹2©Ö/ר {¯TUȽ1Cáž×»ù™–8®MgnÌQ=Ñ®í=™4üú.Ûªl±Ã¾Ñ!ŒTÛ_§fî Ÿ òÖ5¤Ï5\oÈ\Q&îOD×ng¸jóÆäÌþ ï­ÃÁB‰‘ðT]1.°&ÈoжV¥ ÷viN¿°â^ùŒ®í[r…n?лó#™¬øúbg¼°&' OƈïytÞ.Ü™¦Åtk‰f‘½erß_Ïác:¤Zÿ)Ãßâôæ* _à2»ês>^…»¶™BzÛ¼€ÙY™¬–º,ž‘ ·cèÙÃÜg®ÿ`›GK…|]ZGÜÜÒ‘jýެ^ƒ<ã± YYï vºÎáz9Öí¶7•I红ÑÒwYwr Üþº(“™b%]ø›«¶›–7¶Òæ´v¹öã.É|ûŸMÒ׫aÅM@ªÍo\¶/ÓjË䤡õäTî<ÕË*Páf riÜH›†qÍ+vâA#­ÆnÅð©í¸[|®â~h÷Õ_|è°—Û½ÿ~4è–*“цyèie¢¨:uuÒiÙàÞn;-“jý¯ßÌÁжú =WÆ#ü@îÈ›Ïa>aS*y&ù*›º22©îFä7liMvù{Ú?¿(“ŸãÐö³ÈžEîÖTH¤Z¿¡·c1sÈ<…LŸz ogØqWªÄ˜‹ÇysÈÔY÷”"Õ+ ù3¾\‚{¨Hµýeo·cÑìÞ ‡nÂb­ŽÜyλQæjË}ײ.JÎ1Rm~jñ?èÜ¥šLº×·v¸ÆÈ?xªŽ)ðÔn"È…Õ¯¡·4ûòÃn–$²,>7®”É6Â"Øé’H¯³aÒ§†B 9ÕÄù·}êþË‹Sê‰z¹1YíFÒæ3²(ƃ3&ƒTë_m~(ÆeÜadÇâXÙ=Œ[`‰x–Ê­Ÿ¶;'ïâ~K<Ó}2øI&”ØþÛ€wxÿ V&çUÏæëƒTë7ºÖc¬Zm©»²Ê°]1àNq–ÑÞ Fn‚®Ó¸»í—£±÷nN5?Œ_´O&UO)×ýwƒl¿lbp t Ñ,u¡LúÙ»ãZÃF ©6ß¼[1<Ë&ƒ4é»gLgqÿ੾iœûmÓA.XwÛŽNâþ¬~Uf_’Ƀw õ1‹káƒÃÝÇ)ä¨éo1,{0·8A[Ü¢Ó äçÜLØæêíê‚{5e2®úe8½ÜÏUëß:Ág†Y32ØÏ ɧ rØn8™&§ÍXbõ^I&¯&¦¼h¬eCŒDûPQ&Ãø`¯ÖdvƒÓÌR­ßÊo¿þL•ÉÒ7•1êÀ=µÎíyöçvHsŽHäÒeøñÕH&ûïúˆYe& ÕögŸ}é;#ÿñ˜Ú¸~—¯`A›L™ÄÉbŒ)êÆHµùÛ¿ö€é‡—Œ4ë"âøÊ,‰üNUóY‚OsG‚ÔÌï*ÇðYÛdR3wØS[lwåšLjæ%-Üáúä#5ó‡S\ͤf®ÖÈ•xݺT"5ó›sa“ÓS!5óMÅúbíôíŒÔÌ»‚‘:{dòwûù%}Æû²4kR3ãûöGeR3¯¸ÃgƒüÝý­'¢Ëf™‘šù®°#ˆÈÕù»ó÷Î8³S92ù»ïÿÇ#þúeûßTκ@8]]!gþôÀû6 ¸ß–ÆÍŒ`¤Úþ¢KZb÷^Ýù&ü*fNå65ÝŠÈeo¹w-Geᄅ6ÿÕÃ=Ø—o 2=yo?ÄHµ÷ãT«;ú`ùa…<’ŒIfܨ p¾ßdÉã@VÔä^=š 뻦\‹¨=¥6w]vî»érŸØíBŸ 9êúeŒ}7^"«ºøÃ™=e¤Zÿ'ÅÓfl#“•Z+‘¿Í»0k0Nô’H«Òeøê~@&?ÍuFÓù\;Û£èhÎmç|;MÇ1rÃÞuØš)“jý>VÑëTÕ’É%:b›Åš Ÿ^UŒujÀH‹¸¯è`Âýô·¼ûȤgÿ<·ý.‘jûMºW ÏÑ™‘^C¿¢kn½ô ,¾ï “«;\>’HµùÚËQØ1\"•>!(HÜ.“ðTSÃO=»(¤yF|_ÿÛî=?ë"#_ŒÝÊ “¹yc¶Ã/Ö“ÛjŸl÷Õ•H‹¾žÞ冎NÃ“Š¥2yC˜ß:"‘N…a[t-…Të?fßL¸!“Ë@ëÙg¸föÑ-(k³ï ;Dr÷ÜÉ‚ù…¾\—Å×P¿õa¬­—°zÒæD9Žw2’Iµ~³ÞV¿dwcd?¯ïèþO ·vê¬øx›»R¸Œ×5Þqƒfæãä¦,î᤽ÀØú Uÿû~*ƒCŸ£Œ´è÷ó¸Râmq™Øt\StA~Y(â‡Y3FªÍ÷ú‘Œ[;´AºŒuCܦšÜ?xª&- qýa…4½}ÌUŸÛ¸f=œ “Ò‘tdwÿ²çˆ´¿-na%xüú.#ƒºåcâÜæ ¡èˆwë8GL•?ÉdúÌ›ØtA_!Õúß_œ‚›N2yâk~Då.?’Žæ#½9ch$…_çÚlÙ€kcïpóöG—µc¸~fW±À[_!§ÖÒ¯ÞZ)‘jýÎŒ¾ ýCÕ@vo'aƒwUnS›CðœÓ‹‘í"¶g”iž®Ó¶Ê¤oí,|þ¶™‘jû?ÀB·›ŒÔ“‹1²É=®ûÓ¸tÄE&g6;†œÕI\µùqÁý1ññvFά;§Ö—É?xªGlžbê«r™ô4~=ÇBn·îEØ÷W=î ÝC+av é·WÁä˦ óöãñ­VÜg=—¡‘ÇuFê”-@v·êY­Ý-ÔÓÓbäÓ/îЪ¼yŽTë?48C¤"‰,x{6Wg§õ]ÝPÙø #²ÃÛí\¡¢çûÙ±×L\=-“…Ý °õÉ#Fúß9Û¶a©Öo^x4¼£^2rää`¼}”Ê]ú3 ö¶”ÉO7NâµÛ®ûZŒo­02÷€n/‘HµýM¿Àub #š^©ÅcîãÐKй–(‘M2p+ч‘ªÿ|}wB«Ï5™|Ô- «$sÿ‡SÕ|nüúÎb›{R&5óþ·_ãÍõ>ŒÔÌ{Œž;‹.1R37Ãé³eR37x~s›+¤f®Ö¿ðô< ¿&šyÞz+”ìÞiMjæaÞè©­š¹×¼ó N’ÉßígázN‰NŒÔÌ­jdâ…^´Lj楕H}]G!wÿ“I°#á#5ó½=½0nÇFþî|û+ð=ã ‘¿ûþ<Âý÷øonêÚm^3òÎûpü¨ü·Ae Šš}á~s>ÛŸ¸Fc×bŸ†\ß× Ð08H"ó¶EÁvÄ?ŒŒ±õÄ?É ÷ÎÔ°òúG&W'ÜÆŒ[ƒ¸jýõŸ¿@»òº2ÙiÒ l s•ÈYÒe¯xÂÈŠ=IxÕù+wä[˜|;Ö— Üöwe+FŽø¸z>ÏrN­@ ¿0A&ÕúÅ=<‹Ë(a¤û“˜û†[Q^ŠO“L¸O+ÿFlï=\ÓîóQïU=‰Œ5óžó12©¶ß;ðÎFÝ’Éç®bE‡+\ÔHl(-aä·Û^°<…«6ë¶ÅX¶¦B Wæ¬GŸðZ2©öþoœêÛ3è_þœ‘Y áÜù!·yzs7Zyãý‚H®Î'Üly@&¿} Ày¯Î8|©#wÛ‡­·ÎHä© X,Va¤Ë½ÐY6K&Õú¯Øú7 _Y“¯÷–À|»Ÿ@n­Šã;î1²gOoÜPs•ÜKèÁ ,:ƒu¡Ë¹Nݳð~\U´\Žáö±ŒTëç †@7¯”‘‡Úù óæûÜ‚;·Qè˜Ê•—框N!÷ÁD Á56ÉäÑ2¢ãÎ ¤Úþ—û¯c’w†L>i^ˆzU¹þ@ýèžÙç\ pý=Wm¾ç?X¿Ý%“í&âÄ–Ü?xª»ÚïCnÑuFþv!dR:÷u†'¼æŽæ¶jº]× ä°AI8q÷¸LšZæ`¼Csî\ÿ“h·¡7&2N©î©deû\øÍ|!“ïÚ=ÇP+]‰Tëoþë?ñƒ6ÚŒt»|—:rïÇ,CnE¥ÿéœJŽY¿%éy©$·Þ'Häi§E¸®}[ õ¶ïÆ¢Çþ2©ÖïVà.Œ©‘ÁHOgT<ðçý: ÏÜÝ£¢1&,…ÛgòØN³äÖ¬¹1CµdRm¿õä"$:&“ºB1®öçÚN-„Ô}š@N¸—{å8#ÕæøeàêR/kÒÍæ žÍgä<Õ'&®05fä¬Ûðã»77ýÖAøùjI¤Éª0D6»Î½~-ÇHåZ4zMí̸Ó.ÀíÇtn~EŽe_äþå–€ ¦]¹¥p±X.“jý7<ËÅ©Fýi™˜†¤w¹•:GQßµX"'<8‹€ýo¹SæÄ£Ó‰å2¹¬~tíåV­§@͸7*ba÷Sd¤Z¿Zl:•Ø1òɦµ0èÿ\ 'Ï Á ¬=Œ,( À!ó\Ûñ¾°¸ýP&‹ºçàjJ"Wm¿eÎmÔMõÉ ÷wPÏd·~Û8D®ûÊÈò^{°ópWm~¢‡Ö6òdäùF«Q:ç«@þÁS½”½ß82røó5(Í´äÚ{Æ¡ðE¥DþœŽé#%î³iûpÏ‘ñY[±ú^N*y¯ÿ~˜ê]bdÓ]+1~÷TîÅž1aZ€L.Y÷¹ÞVŒTëß#/ ¹z3rk| 0»!wþ¨<ä¿k-‘…×n`f÷|¬:ê\ meÒvï[<^Ÿ™JÖý¸ú­£RwºFF$ʤê©|Ù›­ÉôzÛQÝÃM"#åÈ6H 븆!{ÒL‰¼˜žÃõ:©ä‡p`Ü'TÛÿ&õ.6wŸ,“mï!ô‹%×ÉË ÞϲT>€ø‘Ÿ%Rm~ËiK`zÀH&Ïe{`Ô§î<Õê+WãÁÐ9nÉ\Ú¹›§cd»T2aSÖ|ÍȨ>ÛðÂÈI&uO{Ãæúîçž+^«ŸDN¯³Ãg-’Éí“NÁ#5ó­·N£òû{™ÔÌÿ: ÞËçʤfþùèsxzž“HÍ\­ÿî/Gp9^I%5ó¼fqØÜï##5óáÚ™øÇ#‘šù°·àšwT ·ŸlŠG-úˤf~·Ù%|YY]&5s£ïèÞ+W&wÿÑfwq㟳©™ IÅ „hFþîüÝ,˜?fMþîûÿñ,zˆÿ¦Ù¢Ë>h›L^÷ÏÇeÜsÉxvö #Mœ„EËnx«x¤oñ”ÉÜ l¬­åǵ=z½{íedátØO¶àŽ{‰¦®2©¼|€ëûu$R­ÿæ‘>(IÜÌÈæ“ö£xÌ(._ˆÎåSd2.ë®Ôø$‘/¸âÑÅYR´›£OJ佈ûˆ¼é÷ù:\WŒd¤Z?½¾žxe&‘ç vchµÜò¸°é2‘…-¯@šëÅÑÞp£^2¹ñó1Ô÷ãªí™è‰:!Ýe²£åÞ_?§l¸·½îÀû© #%ŸG̸]\µù»7ì@ÀÛkrøwŒxW_&ÕÞÿSX\÷M“er@ú-<ˆfܧþ¡˜êÎÈ%þ0ˆråVpÃFÏ•ÉȽ÷PZõ¤D>ŽrÃÓ¨úÖäŠu¿¾„jˤet¾þúô/—MÂÕ=B*©Ö?éü>ôïeÌÈ+§öÁ®{@>¶€’¸c)dÀÅb赺,Åg`^~o™ ¿ŽâÈQÜkŸbð×íÖŒl×4ý-ÎK¤Z¿3<áx¬ƒL>Úº?mçqÜÍÂÄ"yªç94 ‰ážî–ƒ‡]d27.•o”TRmÿºþ^h{m¤LÞ_î/¦p‡Wž†ÁEF&:†ÙפÚüÛ0àÕ™ì±ô4\[ÎãþÁS-p¹ƒ-d22øºý#‘™Íbd‰%#CJ}°qjŽ@n™z ±ÿâzÚå¨uFZ¬ˆÄºumd²þ²$” û%2Ñù†¥[ËäãÙ0Às¿@ªõïZs?¼Êg ¤qÆä–u¶&­\‡egF¦ÜICYª57ª{<œn$“Ñ#ÏÁ_§D>jv_§äpË#Š‘ Fªõ3¸àƒŽÙ»e²·ßaD5æ6ˆŽAäôHF~I;ŠF^û¸)-£aóx¼@ÚõG÷Ø*2©¶NªzEÍ‘ÉË-¡õ÷%ÜÚyñp©uZ"sª_ĸ‘ç¹jó›~LĂ4‰Üc‘}ùùOõ¶ÎÄ߈•ÈØè8«?‚{òµœFX“Ó§† 8©žDÚžOÂÚ5U×8ÙmžX“‡âÎâBã0Ü•Ö5†2rÐAó´vr›t:­ÕÕR­@Ô!”Œ¾“JÞÜ#‰t^|.õ™ ¯ý+²—[n—soˆÂݬ֌,DâÌpÛw>Š “æ¤Z¿¢¹áØÖç¸Lj{D£ãðÜñGä>‘Ï&áá0}î‚¢øëÉq™ìmwívâªí?rÈu6¯”I«æØÙtwð˜bdJí2¸w!Æ/b¤Úüi›#0¡EUF޲Øg÷ ä<Õ{ÀlÌÈT²ðô}xædZ“¯OàÇ g‰,üvõ½q/|ŒBÊÛÜC{“PÅÕN&׉‡¹m4#µíÄŸ¼ÒóÕ(‘È['Šq¯}{Fªõ_Ò ç£}%rË·cÐÛw”k~- a»Í¹K«ÅáX»š2©õ#I&]i”剫? ²Ð!O–ÅKdÿ7±¿#Õú9Ì8ƒãCe2Ñ:'ìç”CÊûõí¿œÑ>öãCRÈA} ÐÒÐE"LLERb°@ªí¿Œåùdò”U(êlä¾n˜†s‹1ò²íq$;»sÕæŸ½èŽ'Ö1¹èƒ¢œ7Éä<Õq î¡Árô»tKÏr«_•?2 •t|˜ ÅÜR ûÊEÒãÅ29 ®ïNs}wÀ¿fˆD6í}ǶØÉdQãDÌN edahµL Õú}t MkIägðjÃ&î•#"WŠ2éº,=—‚»:y,R=RÉÈ#îØ7¥‰LnúKFDD #_¿Bõ>#¹jýnú¦!¦îj™|X% ãVÜ{íObó‹y<2Å·r3_†ÃcEîÒÜh´HÝ)“jûoGïõdr×°HTs\Ëí´<>n¹¹ëðI̲–Hµù–Mâžn˜L~pm¾nçþ§ªù<|R„'Í? ¤fß,pÿ.š9f?D‚ëðTR3xw ïí—Ȥf^ã}&ÒëΑHÍ\­ð”³ØÚºžDjæeEX¬#“šy–‹Lno–IÍ|íü(ØŒë!‘¿ÛÏÅ*=ºkɤfnÜ& C}¬%R37ÌÈCÀƒ2ù»ûõä㨿{…Ljæ¯wgB #‘¿;ÍÚ Dþ(‘Èß}ÿ?žæÜÀS¼z¦§âòÕÑ_?Ÿ6ïçÚUKÅ— ù&$å)Î\/o gíJ%ìrðàDW´^…êâwîŠ&A˜ÜT›‘£ÃNa³û| Áõ/?‰TëïÛ<a]e2²å9<ØÂÛÑn_%ò@u´º¥'“Ö5/b®_c®Ç½|èOO”Èqv§‘¹e·@JçýàuàW­ŸnL¢úö”É}kRácÜ;oÖ½iÆH7¯L´m5’ûñÓI”Ø6±"MêEÂ%¿–Lªí¿à‚…?f0²ñì#è0jתC"Æ{O–É­—²±ašWõß¿„뱇Ò¦ªlræž#ÕÞÿSõ/:€Ñì2k©7fÓâf/9…ùG]ù~y$¤=¸&ŽPwnMîËŒ <;6‹»'Ñów/HW£ý˜W´D¶kšÛJ[^–¯À™ŒTë_ž"aÕR™\—™†-'rßMÂó#¹Ý:…yú:®ù œ±9…ÜÝ÷Ú¾ HaîìY÷@"á~?ìɤZ¿Šƒé¸T:@&Nä rœ5·ÈMƪ/+YÙ$ç›»rûJÀõôM2¹sÿ¾ÈUÛ¿yx0Ü †1²íø „‚[Ïôï—JdyË+ðZ~ÆŠT›/¤À|·§ybX†V"“ðT{¬Ü˜Ñõ¬Éä7^øÜ}i*ù)&!»ìÙ¾"†KÛr+ý|²>æÊíå…·»Ws·´òÇËõ2ù|U$þ®Ù–ûÊ&ñcJ2]JÆÞ†–©Ö?À0æö•ÉI»²âÞ”î¯âmÜF’qòê.³?ù¯´y==FåU¹ß$áÙ‰ì|ä<÷ ¤Z¿°ayèô°½Lnhvã¾ëqcÛÄ"L{#üNÀÀd=7øï‹óë ο\1õ<ƒÚ ¤Ú~í-(/èÁHåâaxnkÇu\—a­—@Z®Ž‡V«6ÿÊÐh(ÓÇÈä΢D$4©Åýƒ§ª÷Ä ÊNc‰Œ·M¶pMŠ£ê÷;YuA®öΆÞ1¹°]µòHŸ¸8$¦Þ‘ȧ°4Z?•\átÝ®ÈQËÓ0sÃJFªõ÷j–ÏŸ$²,# ƒ\Ïp·THX­×I&Oú(è»2W"œÿ(ÌXxã“õ¬Éá>hZEK"ÇáÓ_ceR­_jA>tâ®Häß¿~É'¥®çæ‰(מÈÈCOB°Ò«-·óº_ä§ ‘Èå™§QÙy°Lªí·:ŒéMù.?£Îès[Ž?…Oc½$rcyž·í*“jósk%£“K5‰¬¹$vöÈ?xªÙ}½Ñßü”Dnõ>›Å\§Ù¡Xøªµ5¹Èô¦=J¯séϪX‘ŸM}‡½æ—Jæ_ÿõÅá^ @šU Åèò<î¤ö¿¾î_hÊH3ÃxØ<‹M%Õúc{&.•È—+ÒrõG*¹úè¯OÇ1Õ%²‹v"ÒK­­É ÚñH¼å)‘ƒ*ÏA^û…ëðwª[ö‘ɽ?Qò82…TëW­wú„¤’sóÑzC¨5Ùïå8ž,HiQ(je¯änOÌ‚‡ã ™œ?úדÏ'%Rmï=xõè‡@~Y€ÈÎO¹ ;fcq~2=4FŸJ$RõSÕ,=t‡ d¯‡0ùFz*ùOõéøƒ°©%“ýV‚uN]nûÜS(eE©ä¦ °žÌ;CÁÂÙ§­Éæ†Y· ¤Þæƒp~jÁ]’²¯N­‘ÈŠ—ðóÅVœn&#ùþ&Fªõ·ˆ–1ùS½TòØÄ˜t|oE†¬:‰Î=ҭɇ}# Uè’L¾Û‘>µ«Êä˜7WðÜ#F"'‡`ÌÔÛÖäè©~04’Iµ~· ¯bಙ9opê–GrY»£ˆm²×š|믅f©dÊêm-ËÝãñÖDŸ«¶ø†Xn»(ß ¬îi®ý£|ÄÖaM>ŽÏÁѶçRm~;G_ô¯+“•‘GðmÜ,îÿpªšÏ²‡þX ™ÔÌ8‡ÚþÖ¤f>»" ò¹!ŒÔ̳?‚íÖV2©™‹À^ŒÔÌÕú7þõrõROkR3Ÿ¹=¯æO•HÍ|ý“ôŸ®—Jjæ÷'œDa«Í2ù»ýr’sà¿æ’@jæ®%±pí–Jjæ»OÂûî÷%òw÷»ý@°ñAÔÌ ï&aþ§hüÝùûRN¡±ÛR™üÝ÷ÿã9>àþ›ÁVÙÐê¹êù÷Æx¶ìfMžM…ãó,|Sžsûꌼ[%f!:&í{êq·µ‰Ãd“î“ð8è¯)N!ßïÈÁƒý“²qg>æŒTë?Ï+NÆÍùlKÖîÄÝuûב7L +Pb­pgÿ‡(Ÿ[ÿÚµ+å6ßsvp¿{¦".à5W­ŸER*Ü[,µ&͇)8éø»uJ*NövÈøîn¿À}è|¾u®J䫇™pšY#…TÛ¯}øŒì®¤Ý‹óˆÿv‡»çüܳ7•ôðˆBú~Ö¤Úüå£Î yQW‰üYû4Jœ­Iµ÷ãTo>†]2­I¾`UÔT +ŸEç~í™^–€ùÝ×°^6¼kM^ùvŸ†ä†'ÑmŠÈ]09ÿ„/â^:ÚŒ§Ç#}XC+R­ÿˆ2zìÅÈÊu)üëïSrõÐxì(ªÉý>ã†øµå¾›‰äÖºÜ/NGà1ÿ“@Þ3ŠÿõËûœ59jj*î™{I¤Z¿ÂžéÈf'éc3p..ˆûvM*Æ%Ug¤ƒpv¶åê´NÂä|7kòÔê 5u•Hµý[ÛœÇYÿçùmdb›~äšï …½GJ*y>/'äI¤Úü¶n‘ˆjÞA G †×}kòžê“`6I ão\€g•ÝÜÝ›cÑøçF>-ŒÆÍnƒ¸¦ÑGѸûUl]7·?Dsï=;Œ…½ý­Éq¾þøúm¨DvIÊDñöKÖ¤¾mîÕ5a¤ZÿñéɘôZ`ä0ÃD8õå^˜y ýotâ …yeSnLHÜ›‡ dÄ8T±Ë²&“Æ_ÄÇÒØTÒõöy\ÕÉHµ~«öd`øäù÷£tØ4Õadü¥DT&p«zÇ¡´ÁPî÷e ÐÛÞB&ïd ݯ9Wmÿ’EÙ°_ð] ½ÖeáC/mF¦·Ž@ù¡Ž2yÔ4y#­¸jó·ä ÍáùéaŠþÃýƒ§ZóÔx[G äÙÿçÓTÉå¶>†u­z1²Õ’|¨Þ”ÝÇg¼ 2 Ž?V +I%-!jÂß¹µô(ZyÔ–Éœi±È\ц‘Aãb²®ÜšTëwg<>_éÎÈ®ƒbÑð^[îû_{ͯ¿ÈfM¡_¸ÛX+ã²§’Æy¡HÉZ%‘Ó&ãvh‰@>Ú“šÙçHµ~¦¦a“¥1#›×—Ñqˆ9÷KÃSèºt×éÕq¼›iÅmäsM'l‘ÈOÓmmMªíï·1ËjÕ`ä_«2p¹³wñ¨$¸ÛêÉä‚ Éˆ¿ºH"ÕæëVÅ„·-dÒàj v†èsÿà©®ýõ¿Ö•¿òü†XLÕbäͨ0líþN ÿ0Ìks”{éx,HäÅNGátžÛcT þëõEÛô Œ0‹ˆŠŠ˜"°³}›³b˜QŒˆ¢ˆ™  €ˆˆ…MÀ.að ˜ÄœszÄœõìWçëö­=uÞ)êqþ¹ªl¦û‡¶3÷Œ¨ú’´X¯„Þ¿=È­Ûõ°¿~¢3zK\q_«‹Q¡üÖsðc²…ýþYI&ÕÈŒõIÐäÕqzÓ'|ÎöÕ¡ã/¦Ã»ëytÌ´¯]‹lÙ¡ŠíCÉÓóOÓy›D¨P¾±ý´àx Ĩå×<àκ’qÑipr— É­=ƒÍ«’¿lUpá@¨3º9 Vh xTh~³yEÐ(¦®å^¹\=rBb˜ {àˆ*SAÀk *Ô“BcBöñhŸºJ0±m¤CÿâªÞêwzyÕ£«õÅ`WbA~p8¡múshŸ¡‡Á¶,M„†ï‘Amód½T‘?§ÜСK&gAÑãÑ"´»i$gæÐ†ÛTà¹ÔIŒNH”‚]`‡ ån"Ó9w9´Q‰’ sÈ»5C· uè†dÐiœŠ,(φžŽ"´×ó,xq:’C?t-a¿‘u\ràêÒR(_›Ý*¨—ê&F¯Í‚õ.È;S¡_t!‡VÜ;Õ¿­$¯5×€mJû4Ïð¹ûâ· )4¿‡³RÚÕ£7Û¸ýñuÍýp¼Ý*]Þ7ο5/@…ú?:/]æÕ9´øF Œ8U"BÿŪ_ ‹#ài×ZŒ×ïΖBQµíΨq}›á [sB5®gŠ÷Cõ÷¶j\¤“^ÑZÔ¸.”¿KMìhÆ¡ÆõuG³Á$û 3j\·+ƒü9Ô¸^Þ(ÎNSëÐÊæ{ÙëÐÊÎ÷«ÍÃÊÃõĨqÝcu´{".@+ÛÿÊϽ ÞöU‡Vöþÿ¸<ËŽÁú¦Cô‡w:4ƒ¨c3Õ½3ükvŠÐxÓ\¸”y•Ì8”Ž[ ]•¹ •[3÷ïƒûß×ñèGñÖâé¶J¶©Ÿ9ôÝDŒ¨öÊ?"p?¼ß¨E½û oÇúÃ'êHR8tŠ]¬°GnÙWéÃ:£;ŠÔÀoh*BmN‡6Ýx´^×,èµf‡Ê·{I&L/ТoNf¼ãÃEè»Q™ÐóÐ8ò†«X&ûšçÀVS‡þPfÁäö <*4ßç¡B½T¢wU2øp ,Þ¯áPû%ûÁCgC õ¿ì´:úîâÑmßB'ñ9RèþJ¬êïC°øš#ž?™¿ÎL'­óAîÝCoÙj`.ˆ ê®RË7<ª/ÒÀžž ä™z`ÛåÙbŒ¦*“]NdC“SÏDhÌg.tgZT(¿Æa8,z¯C?[ïõÐÞ<úDª‚oSD詚¹°¢öªå€Úóž­5-n¶âÑÏùRȽWÈ¡ ·§€b¿œÊ—°#b›ý‘Gpöü`UËĶõÉK›ó`ò?³È}óàÆ¦êèË =Œ©ù‘G…æ·á!^cV€ZæœXôYx¢æŽÑ¢^c¥P–à¬CÿþëKá½Ï1ýî— ª%ëÉ¿¸ª1îipnO¾«ÛÍcÉ ’Öæp¨u? |Ô?&·¶Ë‡ïM[ðè¶~JxÔ2F‡&eÁõMMy´"FCw¡-äÅtj‡¾¼®y®&bT(¹×>Uc>2ßcwÛ¾åƒDÔ˜tz¢ƒõú“]^gA OyÆLƒ=$'UÝ×¼rF»­Ë€*Žwu¨P¾¤Ur8äÈ¡µ5R¸ö&žÌ—g/ì#£[åƒÂá™5ùÄ}žË£¦"Ù%jQ¡ùfPáQ¦h+¾´·¾ñhM«lpH’ŠPél%ÄHàP¡þoªdÁ~‡Á:4cž ìç¿¡qUû~:Çˤ<Ó8 ú®*"‹%ùØÓLŒr#r¡Sh3²Až ZŠ th•épìûuš›‹î5ãÐcÀÑßÕÈJ³áÓˆ5¤Ó65* Ö¡BùuµR`HÊ!u“ ÒîZò×çBˆüÔ“<3¾’†–éÐoÕóá€cÔ+SÇu¬Z?~UÔäÐEeàôë6)”/B–}b%jé{¾R’§_æÂT›ä‰G*Ø3¤¾MrRB˜ÙAº³c&ø»¨xThþB;=À›·<Ú,Fï<#«nRÀ%ÅGüà0$׬&F…úïšt¢:öàP#ûaàJý‹«ÚaN,ö/ãÑ—wÓ`Åâ§dÿÅJ¨ûÕZŒÞõÊ„Fñ­È‡}æ0~iNß« :íƒ6î5èò²d¨*æÑ´^%f• B?Ž+µùEÊ?øòèõû$> Lç…—È»nÇ`Öçp'Ôfá1ø`S…C›'°Ø'è–¡×j‘e; ÁªãzÑY=]t¨Ðüi+ ÁçýIý‘\3Š Ho¿Cpò§Ì²^EÉ N¨Pÿ'KÓ Ã[É£/ü¤0Å<™ü«j| wO‡êOk Æõ.® èx•Cë»#ó Ýá××í•C—´mRm‚Éëât8î5”\§ƒï:£S ¹)MQ¡üãÖè`xÔBjgXq¿äud+4빓GŸ}‘CXŸ¤ƒ{<'phbvôh¾—<=K .æ‰q+uà£X¥C…ò-ÒÁøÒ:jŽÞŸØAž‘ËÁ´þv½1WMJÂÈ5 oõ^Mó͆;V‰äæYðöGž>‘ & q¤P¾§òLØÂÅñè¢ÀL¨²3–|ÿSCïæÐjŽ™0iyûˆöîÛË£÷k*!šK$…æ×\žCF·âѤ…àšÛ‚Œëdø{úZ,BÓÇèàèR¨?AýW‰Ð(ÃÛr¯i ùW5ãl6|g‰Úýi68øc‡ž9`ax1¢–æ@—k{È>Z8dÁ£YNZX?Ɔô2¼JwkÐkÖépóc_gtA? “¯C=лoI¡üÑSt«ŽÓ¡c ÅEûSÈÎ- gÌ1[y´ô² dù[ÈeÙp«"‡NvPB·.œX¡€&»yôžµÊ¢H¡|m'dBÀµ=<Ú s&˜7þ㺙0i|,‡Ž1¬ô$ç82sd6Ä|ßË££b²à×­Rh¾ëÃÓ³¹V›–Í6X’Í–ë á "ôÌ6´r"…úÿ0ìJ:ŠPo}:ô*»EþÅUUš*á›8‘Cû¶T‚“ÃHr`uÉnòî“èº'†¬ú[ÍK»óèì" ˜~u$uH‡Ö)åÎh ûtè{¯“]í§…qÍ›òhÊ êzBù·¹êàÝ&‰q†ƒÃy{» â¿ñè‚¡2øh@îŠUÂõò½j8ƒWuûcã rÈv ãѰÜ€`R(ŸÈð´ÔõßÍ£‡.* qv ¹öY&¬­Ï¡í²`ß‘?¿• óÄñhËN™\o)4¿l\¼¹cÁ£š°”5%3tÐ\óÄÍ? mµ¾¤PÿS¯Ó!§â«­°ÉMß:úW5ßðôøÚ9‘CÜ +Úüå6¹°zÌ.rðŒ\Øùn'ù|ƒŽÄ£|? X;"ï»~…êóEhæìt=r'i?CñICx43G ]f’Bùö2œIÛò:´Qœ{p” ª!‹¶ky´ÿ1),>ïKÖŸ¢‚ûk8´ºFÅ+âɳw¤0|‚Žì+…ý3 $s8tïÞ,ØüêcV+àð¼hqFù¯¶“Bó³]2 &¹1ò ~4$oÔó§´y²êOÐkQ¡þ…Ó3à·esmœ=ã;ÿbU/åbÃÓ´ªá©ú¿×ÛÄç‚r}‡׿¿VçÁ“yÔ¸¾*0–s™"Ô¸^ü5®uZÉ£Æu¡üŽV:èN‡þ?¿ß)Èû-åQãú ˨×#ŽCëžñxí6†G+›/tŒüwò¨q]5 ,gîåPãºs{9t”låÑÊοÕ/^OnÀ£Æuÿ:˜P«½­lÝà —÷áÐÊÞÿ×Xµþ›AÁ¹0»"€G'ÎÊÏgd'¹ÿi‡V8äAß0?Òón>8}MÞ­†eõ’Í{g€õ†q"4cj°ÀÝdúÃ<ØÝd!^ÈÈ…Þ’M¤Pþ„©2XzÖƒC36ËÀÿÙ,’[¬èD¦ßP€üñf²üœ ¤ñ^ç¶…Bý¿ÄI¡kU7•YË`ÛÐý•XÕg\.œ/âQQó\ˆÒ“5Còàm®‡vºœý×/"G«Õp§O²§¥Z­iGþˆÊ€ô±E"TS–ª×ÏÉn“ràíõPõú „ž¤Pþu™2XûÈ“CëÜ”ÁƒÂdÿ™pÆ%”Tf£ÁÛÈÞo¥à¸ÓG·‡K!k¤ R-¬üöN‹n]®…ðù:T(ßWN ÏEjêÝA ׯŸ _4Ì‚Âåa:lA¼ö '£ÞIáµÚGí G;H¡ùצÈ!öÍ ¶Dv#W’Ù^ hP/ˆGµ™r(­±†êÿS.ƒœ#žúÃEÕ}–“qUÝ?æ@ìžÍ<Úýb”ym!í­ó¡èî\­ë•â‡díe¸ù¶¹‚×@‰Ò”\Ù^I5k‘’ p¢–äž°lp¸GÚdÁÙŒ0R(ÿØšrh»v ‡³—ƒ²‘)³Ï‚°ûaäuIì¶Š »Ia‚‹#Æ^–@‡-]ÈŠÚZH´»®Cw®Ñ@Ÿâ<*”¯~=-è†_Õ¡ÞkÀ6ò )*̱k‡&YdÃŒ»ì%7%Ö<Ú¨†¬³MIÁÿ*åÐ&m‡fí”ÂÏ~d ‰¦¼XÌ£¾Ó gú¤PÙc9Œ _Ï¡U"lLþÅU½(Ë~=BxtÀÖP¾úãy>¤o›À¡)oó¡YùH²º‰.§Þ¡Ëœ´0õ¤–<úRšvj5K ÙQŒ¼t\Ó}6óè/Ã_ihÚ*R(ÿ«Ùr¨¿Í—C{GÉ¿åG>j› ¶o"È5ñÙêP‘V»EUxt¤w6X ÜΡgóþcJ\¤†Ó¡›†fÀÔ›I¡ùåðu?‡.ΗŰ5ä³#R˜j>‚GÛžÖâN¤à[¹_&dß áPö,´w·‘qU7Î͇ý¡<97t+Ù¾ΞqáÐßËÕÐÉ[L.Z …¾Õv‹PM„†Ö™N¾1|9®Í¡~^2ØÔxùlœ ·ñàQ‡‘RHÿÌ‘Bù- åÀÌ×rèÎäÐÎj9 ¡rl'ŸnUÂLó?vü˜«}ס«´À=ºA=Éù•<º¹4ºÍÞ@ å;› =áµx4Ýð4\Ѐl`­„s·sèÝUJPÚüñló 0‹¬¦CjgÀšù­Q¡ùKOËáᙵºõ¶ºÖ\OÊ“ÀˆÈö<:ÉUÎö H¡þöfAâ—p=èž ×:n'ÿâª.mžŸoåQÛ*X·qÙT¡U‘=‡Žzª†¦'º’=iaß×z"Tž£»EÎhÙ4w[Ì¡­¢å`úÛ<¸\!_,yôÂñ pmõ@‡ åßk­€ ¦j7Úð¹47lj¢‚»#"ÈU~*(ìN> Í€Šçš>=z8Ä‘¯äÀÄ!/—l$]žd@Ãiïuhùž ˜9ö()Ô‚™|oçÐcz%ü,Ž ÿŪ_¹çUp¸V×£ZiÀb‹ ‡×Ûúþ±Tƒ×§öV@‡˜5®·pÏ·M:Ô¸.”p€¼UAj\W¾TÁ¥ämj\!Ê€.WëPãzqG%¸ÍÜΣ•Í7¦®E´çQãúÒu*èÑ<œCëm3`ß­"´²ó=›)àÛîMj\Ïœœ‹²véÐÊö?ê«‚ÈáZÙûÿãºÑôÿþÈÿÏ[³epíå@½"ƒ)¿\ÉÄ5DG6#Ë®¨¡÷è:äïÔ,|,Ô.ɆЦÁdlš NЇ‘fÈAï1<¯“Àš¦8©‚Uó7qhFx\ïO6,Ë„TÑFòMnŒ|HÎæ$ÿP/Bo6–‚{y#Êßðf&4^µ‘G«ÇgsXO²Í›uCÉ{’<øÈ÷†Ï›ã[ëÐvè“ù†¼P®†S-ëpèÓH „õÔˆP¡|¦™ypõîx­õ>ÒV"[WUÁ™šAä„*¨r7€ty/…§ú’5?Ë`Ì€±¤ÐüŒ¶xªÿ(B~¯Â”7džá‘òªv‡†U1< £7BýoÈóÀûÛxµñ̇7¡É¿¸ª›†É!N:…CÓ}åðÜ|:{TÛE‰Ð²ûH¾€l2$Þysèì¹ðxÂ\Òko6¨‚Éü@%´4¼èÐýeЫ>#Å*9HßÏ&…òÇMÊ„ÛÊ5‹CËNÈ!kå\òÀ ,Ýa'B+Ì´psû3gôÞ¡<ÈK˜À¡?]òA{ß…´œ¯‚MÕɳ.90¦ó:Òcj&ì,]C6n› cø R(ÿƒrô¿ïË£ºXìñ!»[åBÂÚåäù°`3üÉìoùÐÿ`òÒº|htž‘Wni`¼¹¯5AßRo’BùždçƒÕ'-~•7"{‘ÇósÀ}†¹¶M.4´]NefÁćä•ZJ¨9(˜š¯«Š:·E¨í ´=^N–çÁ›úã8tjx>¤“Bý×6ÕÀç…ßEh³µí{œü‹«zæøÏçÐÑÖ ˜>m!™ÕH O‚ç;£× ÷Õ?Ÿ:_ˇUcì9ÒC W´%m¬saêàådÀ«\ØéïIv{«„p» ò—*΄ù‘Bù×LQÀÎKy´{K´p\Hžþlx±½Þ@¾œ­‚ª†3-êö.”a£Èf3ó ‰~)VÔÝVd¹w>8Ž@ åkÜ^ ·vvæÐ#SÕ°ad[òtH.|í·˜\ø"æ%{’uçª Õ<ä³s Gš)4ÀB \ó» Bk­Ô€‡õir„X +_µçÐg†ÏåÃëBýW×Àäª;DhÊW üØÝ•ü«j|M¦€ÄVK8Ô¸¾Ø°¢‹­|´¨q½ä‚JÇÔåÐÿç~Ex¼Ï¡Æõ-ãò`‹ÂƒCëBùŸÞ•ÃÀ.óxÔ¸þ󔿝 âQãúÔ’\h<Ú‹GëOlòàìºYìÛԊ̈Ð@OwúË[nwVA¦ m?™C/ÝR@Nñ<òµk¬ÎIžn“mIñ¼|¸û±y:G ±_߈P¡üî§Õ°ïécúà5\÷¼Lª²%°Ã©ˆ43•‚©å òûÊl˜~ÓŸCÃF+áÁÕ?þx¦„of«Éßgsà­™)”/ï¡ T‡·—CÐ7Ò·Y.¸ÕK.Ú™ ÉAÓȳSóàò÷áä=ùð`OgRhþ»Á¹Ð`î,½Õ!"»Ì!›?SAð²k¶øÛ«I¡þ£·j h¼“mÜ_¿,RÈ¿¸ªÜÑ|X×£N4|Ùj;‘[Ü4[÷êÐåXt•'•dÂì›Ë8ô®2 –Nò##ïåÃ)ÓÖd@þ9Q…T®ÕÀüSEè¬ÃPm^¡E…òOi nK‘M7œ¹šºËÉ]3¥0ý‹‡•¹ÜäñN*xQ¾ŠlôK¹÷—‘·ó uõÒìU>¬ØŠÊW«r‹Gqè©;rpNŸ@~¯‘CÞ%Vçÿ‚¡¤ÇN5´÷ù-B?VàÆ²ý¤Ðü0³\x~.þzžU¶Í#«¬Ê†…7ýÉ]¢,è¼Ç—êþ¢<?С:¦†&Ëëóè_\Õc¾ù°ÛÚŽG“‡æÃ„i}H“[jðêu[‡Ä©¡ï’dÈ¡l(oµ†CìU‹vþ亪Ø\ó¤ý1KÁ‰!äè~Ø7ã°u÷É yTð³j¨J/ċФÙø]7„|è*ƒ»VÝ94㜠Z˜ŠÉ;†§cLm/23:4K¦‘òj(t¹ B[„kÀûT/R(ŸØQ³LãP»ÜÔÎ&‹äÁŽ×@¾uˇÚ;úS“4ðÜ÷¹3º2Um4ZTh¾èLèZ{ñè Y|N[@ÖÍ„zü±c…Ú¿›H õ?‘gnváÑ=|Ü(Hþ‹U5¾ÄÖù0t™#ׇŒPÃæ”þS;r¨q½ÎÃÁëaŒ­ì|Ëí9Ðb×ß½’Á­áýy´²ýÇ]Î…ˆ{y´²÷ÿÇ5lñÿý‘ÿŸ¢éXy%V‹Öš"fÈ”j(}”&B;j oÌò±‰<—óhÌã,àz-#ÏYÉÁ"Ë™CgØ*À5Ø,Ÿ$uc½º²LO?HD¨PþqÉñ:ôvˆZÖœ@FNQBìI­ÕCm’—’«ìòÀ§­+öš …~ÉŸ†§ÿ“N'´¨I©Êíõ:T(ß:¯LÝË£V_møœA/ÍÏ„Ñ׿rèÓŸ™0=j!yö¶J~g‰Ðþ'¤°MeΡBó+>K!VÛ”G]îIáöó†¤mt6hê.'uÈ‚—‘BýmÏæÁò;ú1;"š“B÷WbUGVäçæûj´æd (gßtBkŒÐ@Àïn"4qŽÖ_ÍsFU¥™àúË‹G])`܉idòL¸·q‡zmÊiç¥äñ–2›Ñ‚äë( @3šÊ_{¥æÕï CŸÎ“ÀÍ鯵èl“ø'À‹C¿ŸÎŸÍ3HÙ’ȰœË£ÏýU0½t1yÈpfßš×’Lyœ k:Ž …òE…+ ÇŒÉ<:ÔF=§»“ŽqY°D½”Cyûl˜5Ô‡ÜÜXS*œÈÝr©ÁÚ<ü‹«ê8M¼W;£wfI miioø¹×9¡ó4°gP´­wV?K‡ñè²ç2èâ×—4/ʆÀ~Ë94ÅTËýɵ ³`ê2õ­v—{“‚O- ô­©EÏþËÝŸ¦Ñ "rÁîØX5˜k &­7*aæJMȆÎïÿxࢠú¶[Hʆ'µ—“Bùd¼Âw åÑÓäpÿGrÁ¥l¸Ë9´»¬c}È€uY0 ÑRÒ|–ê|HÁUH2<}[×àÑ¡R˜Ó´ 9q• Lv$÷GIáÚ‰ª¤Pÿ0Ã#lÏ®®"ÔÉðç_ǘ:¡qU?/@üŠ*"4ÐWG‚mÉAc5pô™•}ÖSÏ"“£mdP³gKý±V OýÖ¡]D9é8C_ÌͯÀ ¤ù4ÃçKÓ‰dYù0¨k{R(ÿƒÿy\?æ„æVö¨íNgôòó<¨ØÛ‡CSóáeLòÔæ,ˆº”G?GfB~è<²Û¤Lˆí8—œ6YjmR(ß§Ÿ2ˆ‘÷ãÑø4è%ÝÉ_uUà9ΛCÏVAðêEän.tžG¾{g8˜X'…æOõ•©ïßuhðL)|S¾''<‘@'÷<2i½ª~áH¡þÕ {1;­…•˜j ê²$ò/®ª€ºÎt¡×vHàäW/ò•aä?¤CSƒÓβég TXD®ˆ—@Ûèµd¢ot-g*›“¡mÉÇÕ`îT"B] Oë‹ÃãœQ¡üÃæK`Ýê g4ÆpßQ„†Û;êpè×5†9ûž‹P¯ xU:…G;—CÅ„aä¦b)4¼_—®•€ïî8*”oòXtüeã¡ÕdÊfd4ËæµçqèË9°¬É r‘W>hìlÉÅAjˆ(y,B…æO!…î/uèU') ¾ú€nø/>«W©Í_-Ëý{ŠP¡þk׫aäúg:tìñ|°ô²àÑ¿¸ª‰)}p³ÕçÎ|mv“!†üžéЦÕu¥:¶ô4üJ‹îhQGÃ×ËïƒêÝE Sí«sèXÊï/*¡6]5°Rï§CX«ÁsEMÊ*Û'ŠÐ®û OÓõAäÒ«j˜â¨!Zk`ê–Òü» ÚÎèÇ£±.2ε%S ÿ„‡/ÅkPï8à"B…ò-ç¥PïRõ0¼m¼.ýÒ¡ïgç‚oêÝ÷"Ї“ zk`Ø€…"ÔùúQ£Bókt’B3×:ô‰…Â’/’¹·$0~T–Ý$…äz¦*Ôõ£tô³àQ㺙Ÿft¡Æõê= /×e"Ô¸¾xzÈ^àQãºPþi: dÏŠ¡Æõ–£5Ðu”­5®?M—Bϱf™Ìýf)9?ª!›þö)íeÃ&Í;Eþ÷n~ãÐÔóûÀ£ô7™˜–Â6Ÿi®G‹ÎïfŸ—"­Ç¤ÀìÔmäôÄ"Hpð'…ò¹›ÄF¸ZÖòSº5'ßʼnYƒ&Åä0«iìÄ9ùɺ›´÷‡ ~Þ”eΨ[ˆvÿ¦|\èð .¬à~0)”OÎ Ù‚½7=Óª„ æ’yƒÙæÏõè¸άõ÷"²‡{Õ‰=HëK Ý_‰U½ÛXŤiå€ÌcQ³o“5ê8°ho-¹p×ç,'3–ÂY~€å·˜1R%¹°G$[ïöCÎ( cžmÚ¢·[²gÜ Ð×Ñ£Øóœh@…ò× IgßdwÄè Ú fãE>P®cï¦%jw>’]k¸›lñÝœ]ÛдmåmÎ’W5#ÇŒdÚjuŸ"gS?Ue¨P¾ ŸŽ1Ç-/ ;{‚mð}KÖ^äÌV׋ѣ8±ã‡óN¤³×[º0t‚ËAvÇ·>)4„“ëÖáº}gX¡GMíov-r$ÊYÄK7RpL걕Å9èµ¢RØÿ¢)ùWuµƒ†y||hÄYûêú‚Ë„ŒÞÏ|¬:yw6ëoY“Ê×öðIV¥îG@‹bN2ÿ»”<ìËì;Ÿ)@ßõdûëŠt¨<…Ù¨2ú«§l'F…æ?˜Ç±MöÍ=4™1ÿÝÈã5”lå„ÉbtæT¶FQ[ õw«“öEó¨ur0ürL ÿ⪯§gí¹ @?~+dùÚäà­¬Ø?Ž9+ž5²Ž'»Õðcé%ùäµo[Yü‚÷d£¹alå´Þ…蛩!ÌjEsÒ3n)›s§%C-ogM6g*”¿Ê:=ÛoUÊ££$†kèÈô«Ïë“U«Ž³/qäÕ[Æ–êÑZEݘ¥G$)òHgמ†ÚXv˜µ[—^€ åk”q‚}Ûnø{ø_wÿ8Æ&ž~MîuëÂl\4bT=܆íuqô·•Šõ>²X.QaÉ I¡ùz d9ҀΩÌ”I“Éúž±ìÎC…uŠÞɲô5 Q¡þfQÐ?K£'_ñЦeªý‹«}„½IÿhÖ•b–ùúÙ$2Éf$‘keY,âàARv9š½lÓ”¡)‹ãXèBºÚ¯c0÷µ]V>“t×NÅ&;†‰Ñ¼nWØ" ïT(ÿŒè“ìÆ¨èôHës¬å†<²ÕÇs,*/Ð üE¶õ O®ÍêÅ<×W×£÷F÷fIÎ79tÐÖlV¡;¥Gݲxv¼£y!*ø- -a—<êR^ÄÄí¯’Î3š³Ád°ily˜ô;vŠÍw­Ð£Å%¬|úmRh~Õ]nìhš7 g,‡±P Òuió¸Þ¥»n'ËßéL õdÊü“z‰Ñ†‡»°*^Õý‹«zýX ót4eh“yGYâö*d—¹Z6ª‘Ð닊™*JM¾òH`Wd}šw=žtüc˜Œc}^vÑ£KÞ7e#& *@G_¿ÍºÞÝJ¦¶=ΞÌÉ$…ò¿aYqØå4á~»rý9y¸Ù%VwH! ‹&•2›Irþ|;;ɼ”ÞÙZ$OZéXí±f…(ßä +¼¤G…òé’ ˜wËã€>µÕ²oã³É6§k°‹’Wä?àF¿›äú^rÃÓ-OŠ{†±–I¡ùwF°áó·tzÛ¿(ŽÜ9y'»wƾ½q:‚uùÞ‚êÿ«ÇxVQhó¦þìÊÜSä¿XUã«uÞQ¦^\•¡Æõœ¨“L~·PãúEï8öâaG†×kíyQ‡ÍŨq½sÿ½¬|æ<=j\Ê_ÿãU¶âÍ×Ô¸þ¾Úi¶ O ÆõÐ}-ÙÂ;€ׯMñcc­ôheó­ŒÌcößã5®ÿŒú*F¿Ô¸þ®ù–Q-UVv¾õÇ1¬bî@ëIgY¿òWz´²ý‹í¶±íÙf ­ìýÿåG}›ÅxDèQÇ9álàÞR9š5wОòq,!¤:CŸž?ÅlãÏèÑÍzö:§ˆ ï‘Ì6ZxÚ*0uò7£9˳²±M Q™ásÁ¤gwR(¿ÙºLrý¡}gxqªk\"n>·>äñèc=cMî Ei«Ù)ÇаëØ9o1)©8Ä&[dºÄô0kºã 勚ÝõhãU€ÞšØ‹Å¬ØÂ¡M|[³çÒzT*iÀ‚ToÈc_2à•¤¹ìÈOP§¾äQÁ³¤µ+=÷ÐŽ‹ÙË´ä¤hžýèëªGן9ÉÞ>@ õ·[‘ÌŠ ’ôèA—Íì…,ü‹€€>!Ì¿k’µnÈž˜"O™È¬“›1ÔÚ| ‹XØÜå®`‹Ö(õèâ> ÌÕ.¬f–Ãì:Ô#ý­ YêýäÐö0ÿ·æ…¨ã¹…Ì'~¿Ê/Þ˜7×ëÑØš–†/ûtòÕÆÌ_âL¬SÊ]ñ%ê°o®ö Œf—¾·%·–ÉXÅ^žé©b7-D…ò•^èËžžS‹Ñô'ÖtH'@#ŸWc^ì›MØý˜Æ¤=¹¢[óÒÐwÙÞÌ넎šÿÏ2VoÁM@=û²––edÙšRæÚê„-ìyž™¼»@ õ¯0Ž->¶OÎjÇbã¤ä_\ÕüÙÔz™zÔyš7ã¢&ÝŸÊzlìÅPÿ$6d§#Yª f~õèÖSXÅ­8²„?Á^N/!}¿ŸaË ï’AMX–iNÐÿ ØÞì&F…òjÉûDéQÐ6l­ûR²ö 2V¥J$ù%­œ…CÙOº•=13cèÅVøæ }Žg±.X¢ÌNYT!…ò½h$fõúøºãcՓɆ ¯¡œU)Dí?ƒjSMÉím#Øü :DË,z“Bó÷ôógŸ4gíQ²ž-u?FÚò%lø½zôÛ†,–| —ê¿týwhW¤Ñ£UªžQ§JÈ¿¸ªåW=™Õž#zT}h{ñæ4yìÐ vs­ˆ¡SçÎfUû9“ÇêØ1Ëõ{õè“[†sKËTòÔÓìÑù'dèÙ#,Èå6Ù8"÷ÖŠÑ6}*àm¡9 BùÍ lY_·~ztüÅN¬îÚŸhÞ+làÅ}zôaÎE:a?)ßÇ>L ôÜ—T)7#« \ÍÆÎ;¬G{+Ú±1êN¤P¾wo0×›E€Æ‡bÏ÷Ü'¹?‡6ßõèJÓ7ðnÓ?dPÞ^æ¡7C‹‹Ù#Ÿ¤ÐüGª6øA ï³Þ>yä ¿=ìÉ£zôíãåÌ:3ž<`$«!#¦XúUا™ä_\Õä‹cYƒez4pö0fºú6¹±Ï¹­ZOËУ[õ¹`Oz˜la³–ï#Át"ãõÑ䇥*V¥yc†Î K{Ö€ʯ;؇Ew'FWís`‘#§)ÙÙž{ô轩Ì_C^lp”™¸N%O¯<Ëü}¢ÉœYðÌ#CSÎX2ë‚_bT(ߨW.Ìõ9C' seÞÚ‘uXÞùz4¸VV³‰ô Q²…±Á¤skaúœšŸ¸(Œï¶Ðõo"˜É¥82Äò4ÀšîïN®»_Äö­IÚ¹iXÕÒjd;©‚éy^ž;ÄV•'“BùÖ¤JØÏ:] ÑÔé,ÌôJÿ|¶Áá‘•?T°GÏoYív²=“É%¯ÜÙ½õ±¤ÐüÝ×αà£Ã}{–5¶adw;9+Ò~¡ŸZ'³ }‡ëQ¡þ–Þ>;œÅè¢>:V÷ÑÐô/$ÿÁØqÁbT9¥:æ0y²ßQ¸75ž,rf㮟ßÌeæ¹?•¤²à'Èv–ìö‚]äš]iì§óròü±r–`Až]aU½HÁUàn±îc ÐÎòÛ,´^™“µŸõ¾Ú»=¾?‘ òéENì¨`N¿¾êÑo·÷±©/ï“Åý:±Vd·1ÅpþöhR(_ê¡4vÁ²S!ZwJ*›?ß–¼h™ÆÂ¦—êÑy® †*&}–Õf;FD“.å@YÞÖ? Ì¿;è ܨ v·O±{:¢Â¬nâY=ÚqN0s6oTˆ õo7ø›Y·½u»»ƒ=Lˆ"ÿâª>|eΪĞ£¿Ë,˜âÀr·IM6ŧ uZµb?Ú‘¿E³]uÂÉw²y¡-H“m<;Ñm 'γý/-Èœ‡Ö`ì*-¸¾“µÕEêQ¡ü?½ï°kmS P¶ésIÑ’Å=b™›¦s!:ad{èÚ’§.yžù²¼–= ÑéOVÕŠ#…ú¿ò^ÉÚœ9§GGÎÅÒÒL Ñ¿¸ªî5Û°…KëúÆ¿3ûÙ Ùû¥=»ðv<¹¬t$ñx= 5óØ‚·­Ä虆ÇXÝ: Ð+;n°öwÅèÅÓwØ«‹;È.;§°ó­k¢ž#º²/;BùËoÝf?K/ ƒ·ÞbVž“‡L¶2îE­BÔñÊzvÐí©µ3–-éHvðéÆ:sÈ %–,¹ç-1:ßw!û웨P¾£Ýv³ßêú…èsÃÓëM‹d·“‹ÙܸÝz´ñÁ±,-%„ ÝU‹ ñJâPvÞ…[;P¡ù}ûcŸb´öžÖ¬û}rýþ)¬Æó>…h^ï1,ØÂ’êïéÓ“ «Ý´M·lÉ>¿mEþÅU=f݇­íîhÚk޽ÎIztñb[kï"ÏÛ°­'“öÙX`ß&zôÚ‰«ló¸ ¤m‡›l}ísÚ¯Õ¦ŽÿƣቭX;ÏF…èºQXZ÷h=*”?3øóïb¢G¯]ºÊ [Ö!K"¼Ø¨N¹¤ë…1,Y¶‰ «Å®øV'÷„^³Ž)¨²uë—ùÐêMTì®y3† åvkÛ{ñ“µŠ f{ï’¿;±OS|Èý3m˜öÞxÒ¢(Ÿ4rÜ^v?¹>C…æ?ºu„­£aM‹˜lû¡?Fº²)Wõh£sºäN õ·HlÈYZ’u0÷fä_\Õ©7\ÙÞð¹€æ^ÃêZC^Á[ñä°o±lüüRòtÈuvÚ?P6ßr…¥gí Kžê™ëÛÓè̱láG=jÕÛ%_|/F]›fc—þTðX¿®ŒzßT:_`§Ï·!_ø:±„éIo¶~vÇ´É· ( ¯C~›¹¢ô;DhÀ}ûкCä± ;j@…òÙŸ\Ã.8®G_ßYÌZƒœ¼>µ®aíE¤ì~ô2mGº¾“²ÞÍlºêª’U9Þ‚šª¿ž=é¼UŒzÏäYQ÷±¤å¬ÌÁé)‡ºÙ·cfm<@îjʲ]/ëQ“g­ÙÅYɱªÆ×Þ{SÙž¡€×Ó¦²9îj\¯sþŒáE«Gë6!;Ù…NazÔ¸nVc,»º¾/CëBùãߟaŽçºêQãú¬3uÙQå\1j\= M&µ£ÆõrNÂ<Â{ŠÑÊæk:]Ö£Æõ’^ç¡æ¶ºzÔ¸^¿4›I;<´²óG¬Ð2ÿüFbÔ¸^±¥9›Ñä. •í&íÆ N/@+{ÿùQc+,n²Ù}=]’|“Ù_EÎëè֭̆£G»ÕëŲÔ(@§Î]É¢í¹B4gÄ|Öå¦9Òf/,Jë¥G«µ×Áâ³™è÷u=Ù·N…¨Þ»û^X¦G…òKÎõfqý¯ñèç*½ÙR·fb´^0›¢…èüùýØà‹6äÊÐV»‰ÝÚ>L’rÉy¶ÕØšsf…èѹuØ­)zT(_×ËɬµÓ@k—f­¶$¹íiLöx•^u}l##—Œ’±‰'ŽˆQ¯&ǘuìÃTh~ÍäD6±µ}!:oë^–u¿ùIt ^¡G˜œg–Ç’Bý=†ñ¬»ec†ÖxSÄ]ª‘ñsö&붯? ûSn²ºz‘_.vb/DŠÑVémYxdw@—̸X¢ÑõF°Ñ3=Úhÿ0Ä¡£«´eÿ¼4ÔÚ­'[™WÊ£'G¹1‰GÊ?s}/¦ØVŒžõbAŸÚzµSoÖGU¿]øÞމ­ÿÑ£/–½€)a ½ª)Ëÿ=˜¬ý¸ Ûßš¡&ѓؕaY€ å ôËbz± çãÕ¬_J=rt•ìñÉëzz§0«¦…èóÈĶ{¤ý6ü"“ü(!…æ»,L`Kû¢'ûÇ3qIoR×DÇìî+ôèæÙ),L,#…ú/s/b~w.êùJË|n%ÿ⪶¶¾É|ï·ôçÀìå̆dÅÑÌcB(9pj3ölB¹8°?û?¬×iT}û>ð¥’"$!¡¢$ ©½¿‡YæyžIæ9!S(%!ÍÒ¤¹TѰ¯+ ’™çÙm ÿû¿Öïü>kíϵ¬§ýæóâØû<÷}¶»Ê](E^}¡6à©‚´ê0Z³×32‚÷š–"ÙŸá±y_îæW<öŠÈϦ–Ð Ï•Úስ{zŠ$€ª–\µ™-0êþ,ÜýºˆõïmÈ•š/?ŒQQ‡r"ö!Ýû9·OÕ:ý :''C¼nÀû†/w{ˆjiƒ i—ïm¹¦›¢Âªœ‘;4º¡a–H™Y°snı+ÀÐLî´ªã?W ÈSQ§%’RýgÛ`hÈ#FzÝmë¸×6ØâÊMnÖ\†Þ‰Ü1•©è;AÁ5,Pà˜y.7£YL¬/ÊHó ÿþbœ.Rý~ž¯ÀøÊ¾9ä³¶U¯b¤ üi'’>¾a0Åžû0¼†9¤)È“ÿø1Ÿ7Ïe¤Ôþ9ïüpw¢…HZùÆãUݹ+Z°Ôa© ²Úykâ<1‡”šïl´ ·Ƀç"/©·OUU÷:|×M”“ÓÇ–b¾N®yš!ìj:Rݯz™p¿<þ÷§û§1·—i”n°â÷¹ŠWcŸ1²®ß˜´Êäýa¥V"Ùä².&p¥ú_/ë§iMA^Ó´CFPKîŒqC1(à)#wXŒAR mÎWá—”ÍÈ®%åèy‚ëYŒ.DrÈ oèÉå\©~›ªÑíÅ^és¥fes¯ôõÆ‹±¶"9ù Bbzqsëö3™[CFÎjSÄ^_Ä•ÚÿÉÂeSÍDò±x´M¸e·2ëæ®rrôœ*6ÉAƒ‘’?Š #ñ ¬HZûØcÈ\KîÿpªÊ¯ÌÁE¨-ë+#•s}ƒØýÁ¤rþ¸|8tvXƒTÎýÒ/¢óÊõŒTλwƒ5HHå\ª¿"B†§ç A*çëLÒ¤r>å[Ü6…2R9×Z²Wd¦"ù·ý¼5îÁ¤Û©œ/‹rƒÃvS‘TηÎ0Ƈ-žŒüÛýƒGÀ™ÀÎ"©œÛ½h‡ƒ½'1òoçï…±QmDòo?ÿ_Þªl#vÈߟ,ËÞêÂ0!‘ûc‘&n®}ÍÍöÿ‡ /¸žÃën'…»Ï¢c{n\G¸Ì0I³¢aí͹a }±ôD#Ûì8‹ÍÁ¡9¤TÿEVwà§ÿCN6éqùMs¸¥+kØ&vP ­Æ|dl¬75þ"žŽUY—™‡Mæ7©#¨c´zw¦¥:f\×)ÕïõÑHæ³dŽ@挈c¥Óûrñ¨lê F&>ºŽËö¸m–›bHñ,nÈ~3LËúΕڟÇ&ÈØÃQÌýÓnÖãlöÔç¶N9ŠÀ¹\©ùù>ÓQµÿ&#Mæ¬Gf–.Èz|8S ×ñY×RfŸ£¹Sź×r;(`?4op+,sð-Xä¤WÜÊ_3²å0K O6ÉÃÿ>Hh´çv´¾K×8t™œÛ‰ª")ùöî›ØÑp‹œüÜç:T¾“‘æÇÔQš}LAÎ:hˆ²÷e¤|ØÔe¤þšlÌYÖ›{ª‘^»v);­K´¹RýýNc“:6È—GsØ«Åù ²Æö¬zgäæêrü‰<ÌÕѰCxqw!yÓá£fË•Úþé(¢"HÂ`Öó†5÷È!_À^G$õÅ=XÜÛŠ+ù²·[€lùïŸÇ¹ÏûqëñTsòÞ±Væ‘ùeu-+, å^.<ÇÎ<Ïå ;źìHæ ˜S–ÂÈ×½ÏÃ!nwùú–ØhÜP$gj‰SÏ äÔ™‰Ð·m*’ÓZm‡Ó”\”êkL TnvÎ&O]¿Œ×îZ ò‡¹ zßScdiÀX|i¼†Ûß#¹¿;ÊÉŸ °òl‘‚Tßã‹EýÙèa ÞïT“‘Rý¶|/a]OWG>d:G[æÏÊÊ0Éh9#guú÷ßÑÙ‚›Ùj'ÊÛƒ<ýÑ–nUŒ”Úï¤À¯é&‰Ù¾,¸¢#·å*¼¯‰äå/“+Øp¥æÛ,€]oËâ¡!tàÖã©Þ¶-gÓ‹rC϶]îËï‘ÎN ÿÚ.Y,#e1WÕöŒæ|–‘]mbðú½š@†YbQ>ÜØé=`u+BF¦Üꆋ#³d¨ùSÖ¸Õ"FJõïu5WìUÚ¸—rG^YŽŠ¬xF†WïÁä’kÜ·nGP‘¹Z cÏyãÖ¹³\µ9 Ô6Žä–/T`ioM‘”ê·Rÿ'›.k.'Mºiâ‹Æ1î—ÐRówb ø¶È€ûék4^Fj3ÒÔ?ùs­Rjÿ«‡X¡VkÔÚ±ŸíÒÓâ;„ýU}‘¬Ûcƒ-:•)5ÿWéq<ßò‘gµR±`K8·Oõä¼L¶ÁØ[ ÜJcÝöp-Þ^aKvåÊeµì«[ž‚´ù€ÁŸür§£^o©äÖ ƒ£³鮲ǃ.sÛèÝf“_r÷¶nªš¦ ¥úÿ3àøykç(Èe/R‘7þ¹@¶OŒ‡—cW‘”Ú_|`»ò¾@æ¶ðbG¼SS“z"yׯõ†ÏÜžrRjþ×Õɰxx]NæÙÄâ·åY§êÇT37 äÉóálÝöeÜ÷ë˜NfuÚs{ÈI-ý8ûSM$<›÷ ¸žnø8²)È w‡Ð"ט«wÎÞ!µŒ4;†qC¹’ÏJÇq¾=ÈýocÐçÝ ®n«x6Ô™˜›åšqõÿ‘¡¬I‘\™Ô›–rg¨ÍAå”HŒÔí 7'{RªßãùöÈŽÇÈ—cpIË…û³2¦Ã» äòÏ¡P=éÈm¦‚úŠdÊ#wìôêÊ•Ú?¿™'[ïT¥ ë¶ïe2pÕµµ¡¾ŒvdVÝùÁ•š÷*M£ÒtÑ!|¹[ÇýNUùåÜ/˜UÍš!ÊùûΘý㡜TÎw°Æ½øÎ"©œÿ~ßí– •s!'Þû_)Hå\ªõ0d[9 ¤rž1MDã Êy€µ.‡¶Iåܬ ´½1òoûEæÏÃç*F*ç=auÙ!TΟØ,À¥/òo÷ç8îeÚU'¤r~ïçRlûÖäßÎO Ý–éF"ù·Ÿÿ/oU6½`¢&íaäÞvc‘X¼û¾:ì{´ÉøÉI˜pSÛ[ß;ûå $Z4Á¬5ç¹GG âš;#»öÇŸ)ãådå%†êŽùiÒ»œ™ù¦p¥ú{^i†n«©ÿUS'ûq÷ü`vV­¸*æhùy"÷ôn/¤¥ÊERã¥+†Ï´ãn~Ì–þsUNv‰Œ°¯{)ÕOgḤÈmuþè!žâŽþn‡¥ž•Œ>LDZI) #°iþ"\üÕq¿p¥ö{ô‡Õì7#“oM€¢*ÈŒ¬{ˆsX$'×:V#jß~)ùg¹áL(zjŠd«iCÛFÈz|ø'ir]1òµãbŒÀ½‰×e:"ylf .[4åæÝ,b«çÈÒ/‘ìÐ}Wî¦ô$ÜÙcÆ]Ñ+-s¸:ó0ÅÑ\ËÊ«Ì0:\NJõ/êÛ'ߥ0ÒnŸ:&.,ä.ngZ»¹žÎÅT3®Û–ÅØq×B$]e¡ùº7&Án"º,·nlæJõ[ly©3® d™¦â:½åŽö_ª‘‘WÍv3ŽscÞÏÂT·Î"©on¿ÓݸRû½|&#ã¼:ȺéÓ:¨ׯëªïÛ d|l&Ê|=¹Ró/úõÅħ¦ùd¦5Xù:9Y§j0`І„0R§çvÔÎ=ÊmQâ Ö¢N ìÝ`#»Ïí~{?›ÝɈëÖö0{o¶BAk¹æ"ùöm0fÌìÎ=rȇ.ïfdì¶@ägåJ>k¿VÇ^ç{ŒÜ:[>|æÖé솕jWž€ÿã6ÿAPx#“"û¡ÕÖhî]Ë»è·-ANê®ÆûÝO¤T?ëä]\ÛH$'pÃ?ÚÜÍã¢r˜#oÔÿËe\ïZøõ´™HÑnŒœÏ©)µÿ~ÿ™x® òO9z¥÷ìðpÈbÃr™ÛVªåJÍ_ÒÁïo`dh—á¨Ýôž[§zÌÏ£ƒÂ©µß¿žGps+V`è†ÃzD8}Qwbe¤³‰ÇùN r’ýn¬ ¸ÍȬ£Ñp»ß¤Ôþ¹ëœÐ¿C'µqËpy[îü÷"óêÿOÙe`Û3­RNJÍ¿òÈ¢äâ¹!è˜kÍ­ÇSõèŸ]áŒ,¼y ?å®50CnÀÂrë=Cx¦Éɬû y„‘“Cý0òÙœïm •ļ÷Õ z˸U›×áÞú®bG3ädüQRý3[áͱΠÛ|3AÆd3îþÇ•ðO;%'§äÖ ×u³Œ4|2åk¿3òíäåˆ_ÑäUíHÖô¼&#^Nc•÷’¸RývDõEè3‘üpÀY/M¹ÍÛ ûuì·æ –ܘÂí¸O¢ü¦ 3Ê‹‘%¯b¤Ô~›g+18Îd]ïµH êÎ}:;ˆm~µš‘C"D6¦ëq®Ôü.ƒb``©²³U2þ¼ÏÈÿáT•_[& Ø,„‘ÊùÆz8pÁž‘Êù{õdœZ±Ÿ‘Êù=ÕÞp;ÖTN*çoµ’ØïŒTÎ¥ú_>h‰çÍA*ç“ßÞÃüE«¤r¾Þà ÷©œ¯•iãý黌üÛ~«×uBü."©œwn]ŒÙÍ|R9W÷/ÆçÕŒüÛýUÞ 1¾HåLƒ~‹a ò”w®t™ÄÈá)(7}*'‹U»áM?“ p9¿5w–]6J¢#é~>¯ Z)H©~½¯lÇœwf íFy@·£·rØ4´Ü§Ím˜»¥µ¸Úo¢çÑ]ŒôH»˶‡å¤äʲ84 œÈHqD" å*lÂ0rÉ'ì훆ERjþô¦>¸³ÇJ$Ÿm€ëvÜz<ÕÐà«xa-1©…ø©7‚û+|#:xUp/Íšƒ‘Âkîü’2\¼ÄÕ«)Á¨ð›\/ƒpT•ŽeäþÇ¡òÂZFzôƒâ…¦Hö¿\Ãò¢? ¤Tÿ±5IÐ./Rk§Ä½±¾@:†ŸGTêe{²¹Ód½º« ·h.‡|mS9ÖX¹ÿ #§ÏóÆzŸDœÿCŽ[Ã?q¥ú [²Ç»·yÏð0"ú¨s·Ù¿sî2³=˜=ˆær ¡)ÈÝó³PºÙ[ ¥öl{)ñýy²"ëæuãjwÞ€3lEryâh˜Éúq¥æG­ŸŒ3Þ äžrÝ÷áÖã©.u¹ˆÖus²ü‹C­×q[ÅÄù?¹}ýzÀ®]‘¼oT€Yr_8‡üñ:\±§Ñ÷ã\<ê ÂÔ ¹7{žaÞm¹?^5AlT¬œ”êo6-¯ ȈwÁØ´[\SˆWqMj.Ã.(™»0Ïãß$0rNF$ ;Ús—ï}¶í®Èøía쌳WªŸöÄ \¾õ‘‘CÞ…"ûÛuîïÔýHjÜd|ë„ïø¡{ü1dSœ@ʦMB¼ù^®Ôþon'áhÀÈ^mN£Á8Mî“ d؉äÄmÑ,ñ@JÍ¿jÖ·/Rc“ú _KFÖã©ÛdBgô.|Ýû 6ªï㪯iƒ¨h‘ÌœÕUÅßòëÖxÄ™´É›þØw§)wCh,‡5ã: ‹Aú:s®ƒÍÞ%«/vAåƒ 9¤ÔüǪC±ïÙFé>:ÞZ ÿ‡SU~iÖ¥aé…C©œïxñˆ­öB •óñŽnðžñB •ó_ of'’ÊùÉÚ˜¿^‹‘ʹTÿs·vCvî¤@*çê-²3½•H*çµsRèTÎKï†×´£ŒüÛ~ã\bñúðF*çeo“`ìa R9Ï—ÁÒ*O2òo÷§NÏBÃø³rR9·ñ‚÷áŒüÛù}G¹áÆËn ÿöóÿå­Ê¶ZøiŸ_#Ö.åìå4®nJÆÈ-š§±av#îü†Yè¤+’V‰˜afÀÝy<rw äè¬íXð9Œ»üÓiøœ¶bäé×1燚@Jõ¯ÊÈd±Çªä¤Ÿs:S[cÄH¢Å0uûõŸ±ß–Öpߘ& ¬EÛ4P×Ô¾•HÞÊtCíì†\±ÝPÄm È~Ïu1øËrîVŒî¹ÏŽ$º­ERªÿÕiÌnñ$Fn‹Keµ…»¹*ó}1òt*7µWzv[Ì]ÿæ*Nyà:ùT`–¡ '¯2„ФڑõèeòŠ‘RýzæÅÊe³r¤N|3ãO/aKV—sG:ÍÌ8Ëí©’†Æï¸_óÜa¶/‘+ùßÇá$<þ„ËÉøÁ'°Þp*×ãc6–úç02»ø:¬zr¥æŸÑÞ†KYùŒ¬¹ÛÈ=Üz<ÕŒVÇY®^#lZÅ+/+H“Wð»Ñ®uT¯ü÷[÷ÿœ¿q.öŽ-äî;k_-á\»Á¶y¢ ÓÕc˜­B_N:}Ùˆ4×—¹.µ/„ÂK Rªÿõ'™…ñQF&Çeÿ~£’îÝã°E‘-'ýo&ÃìäXYQ\© ®çÃk0*´µ@Îí¡‹#ߎLÃÂYýRª_]]WìH«Q«D} ©÷ü‹(Vëã/²¯A¬0d6W³=Þ‰ƒ¹‹"U1½Ç9)µ[§4|Ëy #Û=L[›áö乘Ü1½/'‡ªÁ w)5ÿò¼D˜ÜŒ•“+7œ†jï? ²OõèöÖß×KA.;Ìšisoé”âGÇ#iSŒxý|®^µ5v,èª S6XbÃÖŒ´ÙÄâõGpƤ±fßÒ¸Y‡¬QÕð?ŽŸ¸]»›”ê8D`±«/32¬ä K¾TÍ÷6£T²Çêã`Âq®uãJ¨÷àþÙVM¯³\‡ã±˜1\]$½§x¯•Wª_¯f°š,#îPEô°Sròû@6ýe3ô‰gf!›¤¹ßær,‘‘†GÔTÛ¤Ôþ/Ç‘ø+5‡ôi– Ûáýä^ëpJZ$u³³¡˜žÌ•üVýu¶$ äsý“xyRU$ëñTõ¶ù°¨‹êÙd™Ú>ô¼NF~ü}4_ ¤Ó„<ä l"’ÆsúÂùS#µvŒ€_ Hï#ÏØ¸¦Ïù2Ã*€¼tÓ÷å^Õ;ØÀŒ”êŸÒº’ÙôÇHõ·ÏّݸowÇ#aÌŒö€Jt ‘µ_€A÷Û=/ºÁO¹…„õï%’×'âÞ‘G)ÕO¶ã'KÿÙ‘‘×3?²“§–p“'¤±Ê wYé|‰¥ï:+'s¿õÇ3× ùlÆ€Jc®Ôþ[Ôဂ´¸§»9ÜÉ;# &¤ìôlûžÈ•šßX#wzš‰äqÝÃXªoÏ­ÇS;Ú“©ì‘“ê£<™Q@!7¿23C[ŠdÇéP4nÏ=ãËN#cB÷`ÜØÞÜq£a9÷#-Wn‡ov%wžÓ9r½#'÷ †¾;Rªÿgƒ:|¦ H³êØ=[‹{¸{æÎ2É)^H9Ý—»É1Úɲ¿±;†<¿Ä}=§žïŽWµy“0¡¹;#¥úÕo™-‚ùlï;fv0“k½æ93û§=×ÀZã®MãÎtÆßÜnåqÐ^¨Å•Ú¿]% ^)Hö1ØùXK ;ŠËã¦r¯vi‹Ü–µ9¤Ô|³¾®È¬²ɺ„ɘ;Þ”û?œªò+¢á>6L¡ÊHå¼Sa"z‹¤r>f\ ,žõ©œŸuÀ¾w~ŒTÎß C0¤¦L •s©þ§ûë!'Z¤rÒÔ¯?ÈDR9ï9`!\† ¤rþ¼k î…?aäßö+úç3›iRÅHåܬSGÌí°‡‘Êy«ÜhèŒȿݟjt ÚË: ¤r>o oƒ‘;û¦~¸q±¡HþíçÿË[•Ýa7Áýé¸ÐMY·"of¦´ùìé $þû'ip¯3X[FöÑ ê¦o¸{Fûaír3ñ›“|Ô„[Ý-Ӭ够éAüö<%’á–ÄãÇœFFþ÷ù®FŒ'Æ$·9Ý-Õ¥¿ù8<oß­—“ 'Æ¡®_…‚ìŸf‹—¾V"9Ëù»‘ðN ¥úµi{ + Ò©™}WpC×ßÄ¿h¬øSŒ•|®»^[¨5åNÞRΆwO•“Rûg8Âø¦")KHAƒ\#ŸûìàᮌtxU¹\äJÍïV3W‡ äN·v(ÈuáÖã@創(ó eduóíØÛé(·aã]pÚb²]„2JúpôGA§Ê€{:~…Xs«)ž¯Ë w¹ Oµ»Œ\)³…§A‘\g˨f ¤Tÿ «IXòe0#¿i%£çBnj@&7Ìã.5‹C²àÊmVŒ´àí©:|/—ãŽ7úÉ:½ý¢ Gv±ER£ÁŒ”ê·ÌâFŸYÃÈwSª±¸…5·_Nj>ÞH—ºó¸>à#w¸ì ³rÌfd^ ì[Ü ¤Ôþ„Œ$XhФ"…îÿñ}ZT×4yúš¨Xp¥æû¸>`.1¤³Wûá¼INÖã©&žsÇGy°Æ[ ‚¹þBPs«ÈÅ«beÅ]»Õ)Nö\­›á˜¼Í†û±¸&ž;7è ^Ø 'U¦`—¶T³nc7îc¤Tÿÿ~;û„®““1Í1àÉ“l2%7·/ÉIWÓx´Z6UAzÛ8âdƒ»9¤t:>ãþIðÁèä8F¾X‘öË‘+Õ/Ëäj/<”“÷0|™Œû¡S*î¨ýHypêNýæNÐõb3B;/\^Ñœ+µ?$'M5EriFžÎþÎŽˆ:ѤãêýWµåJÍ¿¼:–/ÜÊÈÕNy¬Å™Üz0žëùJ Îw¹aÓî±3¹æKoÃÕu†‚\õ¡ã5²Rª_Ÿ°T?’CxÍ&%*È…Åþ8öó‹@¾µöD»/O¸U'£PÖ'‚‘]âÑ/%+‡”Ú?.%]«5D².2±-þã½kØÖø#·NN„ñÎ\©ùQÅ -k r²ª<œÛpëñTu3 3Ï‘³¦Ec\ÉF®c í«‚ës£ûÜcdõ,ëÇÄqëº(;³%W÷—'ÊΆdó%Sá1ÞŸ«pÏA´ÍVnذTTo'’Rý_~@ß3ÃëCáTçÆ<Ò«ê#ÓÎöRÛºce‚L$»°Åfm®TÿªAX &V®‡°ðê®ÁÃ)°êÙ\$;FÛ Ç»Wë²;Sñ*Î$ïE³„gêŒ\Ó~(˜ÝFt¸d…“»:0Rª_£•Ű¹»S zà©[÷÷ž1pñwã>‘Ã=Á˜û;ݺ3Z‹dÝ“ fTôZ ¥öÛí8Œâ†MD2`ËAô×Wå^ì_ñ@–}Ž4ERj~f££Hs aäáuqhê•"'ÿ‡SU~…”žBë5æŒTÎ×_½ˆùÝš1R9ïQp¯çHåü„p‘éÕÊIå<µGG zÞQ •s©þ%Ý÷!¹öª@*çqÍ[áUÏf"©œkq‘mÉqc¤r>zä4°† ÿ¶ßzu–& ¤rîno ·&«¤rþŠÅ°Úô…ù·û¿»îGôÓ"©œ¯›¢ƒþ šˆäßΞEÕzù·Ÿÿ/oU¶É°jXÌ5Uw“«ð[-;½*†=ªÝ'“±}»ûpw‡Ó÷ õœ“ѵ°‚‘Ð…ÍBC‘Ü3Í µ›q+wV¢“Æ<–¼ì˜‚ÌßéÉŠ[”‘^=ObÉF MEUd9iå5—ƒ+ÒåN<ØãÄíþ|=.åq/^ï‰ÇCsH©þÓJ±õ±ÃrzD›­¯Ë”rÕÞ0r§vVÝKàZ5Ú„Ó× Drµ| ô T¸?¦ø#+»1·³ÞD¸½H©~Á]Û rð 4Ól‹–Os;® ÅõÄÛŒ\5þLâ¸öªÞpU¹ÊÍòÁÓ#?å¤Ôþ‡3‹X˜þ}ôY™ÏÌd·¸ÁÛaRaG‘ŒûžMµ},Ró;l»Î>ŒÜãS‰ +åd=ž*~—cƒ@ᅥ²q+¸_¾0ÛŠOrrÊâT»|*##îGcÁK,±>ŒÊ~ÜyÃþýÅö+9‹ÜÒy6›ÇÈ[lFVpƒ·ïƒí0sRýƒ:íg?+O(È·[<Ø)Ëç9¤]NަÚ3òµ_:fOí$'¸È0dJ¦@NšÒ# çFü´ÂŠÆÞròLî$$~ÀHÉgÅíq>s¬‚’»bÀ£j9öi,V.fd‹_ kúJNÇè;³Ò{‰žV¨ˆ¤Ôþ¸&T‹rvóì÷æ\n£ÇÕ,øŸ9\­/*0Ú¸PFJÍ7Z }ã rF÷, MðÈz<ÕÜEXôÇC ­7\ÂÄ\·«×˜æÏH¯¬l€Ã®áÔèò4W OýŒ7½®q¿²Ú³„‘ÏÚ{Á²@ä%Y¦oÍ}—ROÕMŒ”êo›½‡ÜõŒüñÒƒ58xFNú_M‚^¥¨ ³>„Áçà$|:±'ÆÊ!Ç’a†F{FŽÈôǰ]F {]¡i©É•ê÷¤›ܼ‹å¤j/9F¦wg¤Ë¶Œ×ÞlO†;Å"àW…‚4Ý<i»ŠäóA-РDŸ+µÿàãt–yV /öLc¿w&swf[àNÒFÚíX†[+ƒ¸RóÏ8F"qvš@–ÚîF±,‰[§:²æ"¾ˆH·/ ¬ôJãæ÷m%IEŒôYÖ /ƒ+¹cfè¡‘vŽ@.]VÉŽÆxs'œF¶¹6Èö-sò¸×i[.¾ÖÈßr/<ºΕê?NŇ…•é1Rãq Ë_8†kþÈ ²åʨÛ{‚{£l BBù9z3^…Ýàæn¬@îЭÜò®B'䦂”ê—µu4d«—0ÒóüÌ9Àýy;£ÖOÈ>M‚açw„;9IdnNyÜñ?N³Üñ) Rò[¯E2{k)Ƴš}þÜ=gQ¾ê0#.§CliÉ•šß)b,æ§®ȱ·º¢êª‚¬ÇS}~é<Š+Î d»Y§±yác°fn#ŒXŽ˸ÿÜ‹beÏ ¤—èÅ ÉÉ÷*eðÕ/`ä«»(˜<ûjkWŒÉo-VÞ°T³ÍŒ”꯳.† =°‹‘µÎgØ•C¡Ü¡“Æ!úV†@Î5ìŽÚ—ÉÜ7W}áñã3#ÝGÅãCöO®Îýc˜oL Ç&ÏõCo®T¿CÞkqs}4#ï ÝSwNq›²ˆmyQ GÔî–Q¹-v¨¢ù*;FnKœc\©ýëbØ ‹žùzî161fwÙäBDÝ¿˜C†œ*Fß·»RòoE:P¬ÃH­‘ºxúå<÷8Uå—iV*¬ô/ ¤ržW²Í›ä0R9ŸVâÇ–MœÆHåüûÙ»XºÇIF*ç2ßöˆWkR9—ê?ìäe:é#•ó÷ÓÕ±ã©@*çCS³PQúŒ‘ʹÅ=ä6·—‘ÛoW‚Šwf3R9o³Îí¾ ¤r~"Ñ*žŒüÛý‘lóÎy©œkÿÌG‡ãŸòoç·¹`ŠèçÍ@þíçÿË[•}{·lã|²MU# k¸k°ûŒìJå¤T¿ë{O¡ý®@ÔÏLÃo[½CŽ#/Mù9nÆ÷ÝÂq%fdϼpøt5I©ý2ûVPT>a¤‰ƒ>Þ©|ä²%5P÷=‹¶ê:J5W ¤ÔüâÞà°=S 7¥êÃØHƒ[§ºóôM¶=VO ,.±ñÉ• ²­õ.¨ª‰déú¥¸¢Ó’«öªˆí[|[ ó÷…2­{¸ ‘wN‡ûæþ¿¿hŒ2¹K¤³×zSääJíxºâ#¥ú{Û‡âû,F†\‹@óks¸ÆÓJ±Õx7ׯk º™TËÉ—y¬<6‡‘!xÉ2Ü€ìiñï˜õ\qÉ YÑ\©~s“ÑØâ‰@fÄ¢WU‘,غ ªƒÃ¹ü÷.,>ÅýT´yå2‘|nÙ×o¶çJíݧÊ~1²ûˆŽh¥ ò†â<\ÈF ÄàWjþù:s|.L’“ß.NÆg/7FÖ㩞ôËf+£ö(ÈN±ç×®L€ßy‘´sº…ÝÈçË}˜î;o9+1†å¼ù '¯õ:Ùm‘,˜¾[­¸ƒ­ᬖ?#-jO ¤Q¼‚”ê?eC4úäw`ä·6qi’)' á³§‚Œ¤½írú¨¶hû»-ÈmW£ÅË.ܽt°ãí™cüžåüûKJõ›±+s[Šä—‰¸\dÄ}ÛÈÎf3²Ÿ”Ûfq{»¾df^ÇòÑ ?Ìígy6)µ¾mg<é¬òê*S®mεQ߀´{99í× ,u•šÿêš^¸¤22Ö!=’¸õxª;Ü“™—UžŒìfÇìÊIÃÍÖX/DäƒIæð9<’ûɬ†u*ôa¤‹§>Lb.rWybÏ™HöOšŽRY/®½W4Æ&4äöÝ»G³l¸Rý¯¥ÇõËE¹Zž€ ÓmdóN)°™½E µMBQÜþ·KÑýbr˜¹j¶hsÿ9Üë¦wá¶½ãŽê2®T¿ßÌ›š‹d Ûn¸ö·æ=ŽrûTFN8}a3¹¦Ííñ±l=÷Ÿ^°ß•ڿõ©9úíÒ9'Ò U“ÚrÞ³Àçu“ä¤OtÈ>„1Rj~úRÞ….cäè•×P?INÖã©®iÍž¼‘“:æÇXŸ»&Œ ]Ý3$(ÈìÛ=Qå×XNŽ~>:+ùíÅVt ÌçVZ`­s‘ X«‹6™Ùy Ý/æÚ»©H ¾ÀL&Ô)H©þgãQUúXAšwˆCÔ‘¾9ôæ^tɉâ¦Ï[‚Øû~ÜÄåa°¸~‘ÉEñ8(;È=oxÆS›‚LÙ” =󎌔ê×#m-nXõÉMg#ñöÌ»—‡Ñ“—0òæŸb̜ђ{ÕñŽÄp„å!¿¸¥@JíïØ7·éœoƒ^ÆÜ‡º³àPÙˆ{×Äú#;q¥æOhQÒñÝòX¿2$lÈãþ§ªü2ØÍ"ã¦2R9Ÿ5ÑÛ<š3R9ÿrÓU—1R9?žÜ c2[ ¤r>Ô©#¬Õ\©œKõŸ¼=G7 ¤rîº~fÿûŒI*çš’¡ýêšœTΗ÷‹†NÉPüÛ~F!ñËÜF$•ó§o@Gm¤œTÎ U ñð{üÛý Ý ¢ Hå|õÛ$ŒcòoçÏi&Âi¶HþíçÿË[•­>¬]Ç3òÀicä±ÕܹMS1ÙµÈ-ÑèPüƒ‘‹= Ñp~+‘LuûÍJ÷• äFµ04lqƒ«ÛÆsÏi‹ä²Qgð¸qGÇúÝ„ûÚ匔êoZ6ÎÕDÒ÷¥¶uä.:€µî­AšìNDTlK¢é+±èŒ— ˤ»ž nm¸'•§šáÑÃb®T?cw¶ÁA_Nº¸{1ǘ?Üš7Ä»Ed€‡#b“¢¸åÏnaÀóK9伯×á·FH©ýýGíÁÈš8FŽº²²Àî½máÌ­úd6yÎ)Š º—‘Ró=Ç ÂêÑŒü}z;T–\âÖãÀœ=ÝË‘Qwm±­Ý!îþÁ hmÊãÆ†äÂê¦'·¸áKÖìÜT\âô›éä¶Ï!Ç"/C‘lÐ jº:Ü›.à’ï$ìøÃÏq¥ú¿lÛK5Ú‹äÑÔ®°Öç^šíýƒüл_V2ÒyzO´èé/'ÓLGâRú\F¾È,ÏF*ÈñÛSY@›TFJõóIðgùí§1²ðD«- ànê€A9º¬ZDìàNÛs w7‰äÕî˜~‚+µÁho{'2²æúXeÿG‹ïY×C¿¸ ­{ *Ȥäü˜`„¹y0ÒÎéÿ?ˆm“õxªÓS‡àS^0#]*Çct›îž-¹˜ý¥7ýUf‡©ÈÉŠŽ†8<˜‘§ú Ç€¼}ÜŒvEÌÚð¶@4÷gáצqKÓû bå©mIŒ”ê?ú“¦ýÐI>1¶gSîÏgSÄÈõ³kp¿›·lâZÌ]Íýtà |}Îqmßtÿ÷™[dÚã}ؤÁ•êh$°YŒÌ_Í6UÝå¯iƒévyhh#„Œº¤ Ó?Ë«ùG ö´‡¬i®ÔþŽÓÃÄ,‘‘^w0ýz<7gÒ:)è 2]#¶—Ô¸Ró—µHĤÕV¹fHžpëñTÛ˜™Z4#+ —A} ·Ÿqœ'+ÈÏ®)èúÌT ë2· Éä0FV> Ä“f{¹_*0ßG%2rìô6±v#g¬Ú…ŠäÎ ¿dã…­.Wª¾ó¶µ´N ÓÜo°µÑ¸¿ÜCì•Y9¤Nq%Zé&\ã`Ý2…‘%úYe·‹û,8çÏà>zD{™@J>¬ùÃÂWFê i‰ì‹M@nô|É䨓u}ªþ=«'rrÂI[4YÞš‘Ÿ»!+:‘+µëÔ?ËȹEGq°úwi‹dè}ŠâžRKÅ™ å¤ÔüK3<ðþaS‘ôz9A9úÜz<Õ?c]p42š‘+çì¾á‘Ü‚-Qøqi@ž¹à÷ïÿòCÜ·’1çŠ#—Ì?£»·sÈÙ±ðz_ÄÈŸûcÒÑ7\ïw°ª{7n§VX²ÓK ¥úh+X…Ã%ÌuNfãY"÷k¯RÔí>Â]ÐEDé¡RîôÊ+è²ð½œ”],GÉtyÛÁ -? d¦U|­ âJþÖf†™cµA†X0ß‚{cyË»º„‘kÜgÚÒ¸f¥ ø«?Wa\3;s9)µ?¼O$ Š`dª_4^áö‰»lrâRw‡'q¥æû¶Õƒ÷-‘|\XÈN»—d=žê&O´Ø„‘fŽÐxóaî-wx, H•AËQaÌl)÷–Uòïp…èíx’_ÅȰœ T æ"çj|® ¤ý+vkµãJõ¯Óc!šž9_ÇÙìuàZ|KiÈ;®£Ã”ÞúÎUœ¹ö¸ÚžÅ`Ãor»ô>ÊúÜß''×W²WUï)ÕïÕÜÉy²ȉƒ—£rUsnqægæ%ÂHWAÁ“4AFy]ïõaÙcì9<–ˆ¤Ô~­Ú8L½ÀÈ!í’ðR÷÷]æ0¤Ÿ:'ƒF6Å~çÝ\©ùnã±.˜@îýåòuæÉÉÿáT•_kôýÛÉ“‘ÊyÑá1°¿W •óW±ñ°[ÑF$•óa¥±¨s.”“Êy´C?l9ÌHå\ªÿ¡“>,«NE •s½rغ¿Hå|ª¶€eê:"©œÿö‰g㺃üÛ~uw¢,Z¤r¾DÏoTβ} ºÚV$ÿvÿ‹)XâÅHå||j.sËì§ ÿv~ïî¹LëÀ~FþíçÿË[•­Ü_‰W½] Ó»0¨Êí<ö#EFF_­fšM5™Û¡æ=º.'£.NÄÏ—ÞŒ”}Oe]ÎS%£Ùä›c×éû<ç°‚ ^3Ïgø2Rªuò/ö´×bFvØ¡…3òý܉S`¼ê÷ý\”^Ëâf ‰asß½ÊeÎ:…\›[ÍðâµW±lö•Gs%AîÌÂY™#nÍ‚K‘&׳ü$"ž5åzu•Í:ÊÉ—Þwáô¤#÷t¼Žê3Rj¯`„ß`¤ZÝÜ~}™ëÞá2>ï³H+›w=ãJÍwOMÀ½Õu 2î…/ŒgïÈz|HïTçsÍ9Ðåæx”“ö“BÞc#ÉÛ!¨:Œ;eØØ•\äNlwû_ÿÆe¬{ôî®mðjM–Û@wÌF&œFõBs”êÏ"Û¡êL$#5^™ãò±Ü+S"Ò5nG§hX†^åºÜùÍ&/Óiqº;¢E3nN@,´}Ú3²Ò±öäF”üQÚõï3{ó±rrLÂŒ÷ÿ“Cm  ðÊ!¹¨@D·ÁHÇñ)¨hñ«ª»oíª¹Rû' ƒš `d¿ÅÈÒKç*ÎÆÁ$ÒL$ßïFñ@Æ•š´6E ¤ÇK|³[§Ôé>zvT‘“êÆ÷‘š™—C†%öégñ| &Úår‹ï¤ÃúÂnÞçKø=—»Ü` vÍî Òü˜7f5ç–? ‚×Z=‘\Þ½æn1áJõ××–c€Åȯ'ÇâÌÝ\nß…gáp*…ÛtYš¬å¦W̆³‡ È!¢'Ô-¹Agýá¼ H~Ù?Ž›r¥ú ÍN`y·J÷8½X dZKÏ›‡q-¿žGßÖܽlÇçÎ;×7\[0RjëiQxô#Ž‘ë ¢Qæz„ûÜÉaé–"¹8·êFÕ ¤Ô|Ï 1<\A.{ÈÂl:1²O5lÓ=Ì[tLA6êRÓNêù ¯3´£³¹ÎÉ Å¹ªù7±4+OF®¬«€yѬ‡ƒ›‚Ì…[ëDÆm’ÀìòŽ dr˜øa#%`¶ÌG—Ç9\{=«Ü÷Ëp1®@Nv9ZÁ{äÑN¨žßd£ü4¤m|ÂH«ëçXÕá\ô³‰dSä¤T¿±K"q{çbìx7¥¾žÜ7 'q{Jc‘tñŠCsÏvÜ×7V!¨ó F¾˜ƒ€™Ÿ¸Rû?¤Æ }¤#êÄÁñÉî{íÛ¬õˆ%üžå±—“’ÿ¾ÜïL÷N)#Eó.¸x½ÈúüVí~Ý2ë[‰C¾ë¹W ÿ“'#KL"Q××–»B·9úå¹yt6«´ɇsÏÁÂX!'{„‡Eç/ R¯ïPÜiùŒ‘·®‡à}_®TÿuÞ»ðOÕFî?ºGÛ&pÛý¸…õÇV äø¯¥pq…›vi_½Yvü|–§ÉI©úÐýs‘+îï‚qõ-®T¿½öc_‡@Îý÷ÿ^ÂO—áR±ƒðŒY2òuÐL|¸T%¬ÅS5·ÿ ?†Æqr÷…áX®Œ¶øî†¿/ uÜã%Ô{Q†’º;9éalÓRöyňö>%LŸ[€Ÿ;prWSøM²Y£[ˆ¹¯)×ÿkú!œ˜RG"Ç…FcÃÇ&Â×z£aVîÉÈÜ=«Ñ¡t¿pû˜ ¼||Hx4áúø QÚín¯áä_±½lÈH¹~Ÿ{ÛcsR># ÞÀÂf®.hiR³ÏJœÓ3®K±i'#N^3ÐǘœîŒ”Û/ÿ&âü 8¹²Q –íU_ZÅßWÛ‚\°ü2vn¯`¤Ü|Û>`ÚZ ßtJÓ&jÂZ<ÕÅ }±be' j¬ñrÓ¡V¤ú`¼0g¸–ÝËU’û2oÀYÌÉMZgpúp®020 ëf}Ý‚ÐK¸)7q.ÁKI^óHÄêòÛœ”ëÍkÚùêKdëø]øûVgad·”=ÞÍH«OQ8:`®°ôÚ)„·º><=ó›9¤›õ¿Ï°ºŒœ3'Ó¦ŸTrýÊúNÂÃøz µ›,E‹ñ:¢Á~ø8ÓB¨5" yú=…Þíúá®Á/FŽ?è½Mº‚”Ûß©]1ZŸœÄÉÔ×pµbÐiÌ]ð1-9®Óm|vèÎI¹ùÏ]FŽÝ1FRÜÁÅ¿O)ÈZ<Õ²'Ý0íÇzNšî0ÀC7áóWð|œåHÍvÀžy èvÚ>sÒfòÜÙúCø4}1ê¯HÙì„Q· öžŒN#S„÷þ¶ÆÓÐý R®¯ŽÞ¸”Ü]"{s¿o.üf˜ ç¢÷ ²®úI¸·UæfŸÎ£ÞÃÙœ<:W‚]¿2¡÷íÓXU–,¼q=ÙQf)ׯ݇­¸Ü·5HW­]0×k+Ü|ñÔûwΨ̆öˆÂwœãã@Má¢x¢7€‘rû›$_AÙgN޵¼„Û¯ö ŸkžÃˆ¥Bõă°œ -‘²¾ïX¥ãÄÉÌüK0û°O5§a+䜱åä1ÔQGhßÅæW—0²[—iˆµ¶é9 ‚J8¹·W_L»]8ën/,;‘éH¦RàÃzF¾sÞ„ÓJ„SjN£áQ¡\ÿ“—gbØè^ù£Û(\­6Öl:…}ÍúpRÝàZÔIZKFaÝV¹09ú–ÂðöÝ‘ìÚM8¯y2«9¶“rýü—…aÎ}gsaÀë–ÂÖP¥_ÃÈå7‹7ãŒppß2`êNŽ[~žþÊþýì(ÀW§NFåä#«ú˜pñhl[¦.‘7g;ajÈ NÊÍk—‚çSÚJd‡k;ñ1ÄXø?œªêkˆºZ|ÍW’ª¹M &å0R5¿ÖÊ^AJR5_c:¯N–2R5¹Y†òÀúœTÍåú7ºèǂÿ4J¹5œTͳtÖ¡S…Dªæ­Î±ÄF–ŒüÓ~£Ç‘Ú Us¯rü5y #Uó-_¶â¹“¦Dþé~¿ gѧ:‹“ªy»@SÔ­*S’:b¦+l.‘úùÿòVU×ýÊe3¦ÇprUK²ð¾ヅLäÅñ¡8ŸSG8°ÕÄF™ çø!𓱰NF)^Œ`dZß{ÈüîHæpD´¶>#›úïB»ñ’P®ÿÖ#xÜn#‘]¢Z`•ž–ðÞ–qØØÍ^øLi„YÝ» E„³ÀGßI³µ ¬³û6FšØ%#»ª's?mBýaBÙŸÆ7á¨1Ž‘§žÝ‚æ“ri Þ_9ùþô>8‡6“Èø¢þX˜ü“‘5‹Ññz‰Pn¿Æ×@LSô”ÈÝÒN|}`!Æ ×ÊrÙV7 þÇ åæÇ_tA÷¸-Œ¼ßÂSÖâÀsÏtøÚ™“S¿§°M =bù7É…_V„Ð:$ _5An{˜‰­±©Œ\X‚4=oNî^žU ç¡Ô᳂¬¬sÓî^ç¤\ÿ¹O4`‘ÿ‘“ý½¾°ä×…‡úþ`—´4$r×¥ó¬¨_'C*n1·£¯ée¤‡ÈhÞfÝà6÷œ’ôïÒ­cã)ׯóÜ;P3™œE–ûÝA½A÷”äË¿}1akG‰|+-ÃÆÏÆÂf‰0ªù¬ ³Ÿ‡£ÁðÑœ”Û¿i…ʤ[ O$ñÞÂ-» UØKI*OìÇÖ¤]œ”›ï~'Yq»y¿×iÔüMAÖ⩚…¤²ô ‘J2yXSk¶7‡ìŠ}é^œŠ»·+ÈißNC½Íoá~ï ¸üLT’[ö†áוÈÖ:«à}¶‘°ñs,¾ÛGØj:î6Êõ7(zʾÙæäÂê Vqj³pidV¤k%ü˜tœ-³âHn¯3 m@†}öD‚…°žÞfD5l'ZªDtJ]¡\?á·ÑnÊN¾:uc…¾@ßñÆùÅ»;†­ë ä·]—eròDÝÖXup™Pnð˜MØZm#‘oæ¬ÁãC¶Â€‘ëð%£”“é¿,óóªPnþùvùÐ7PãälŽš„µxª7<Í\ÇwRõ3αYò„ÍÞEê•aJr‹ÇSõ¶œ¼ƒ¼9Ë…çûAÛ+\X´¨R(œÜ¦®½…Ù#Ò˜Iul©°í«u…Œ”ë?E»Œi÷èÉÉ=Ë™ÏV’{”²…»©¢‰q1Âê©Ñ¨Ðî2«æô,Ô„åÞÃ3Í­ ²ÏüB¸%ßá¤\¿¯bûíNúZœ‡Í45‰\¤h„OY…]#ï°¼³¯8éz›™?¾¯ §:iã«ÖoFÊí·_¶çïÚJäÇU³¸ûÿ牗làŒ…œÔNe—'.Rró{\I€öضi3À¥á¶ÂZ¢Ê>]hð!÷ô6)I¹þÝt‡ÝrÈ¡¯˜Ë¦µ ²þnsýq‘!®Xó¼Rh\}óoG sÎæ#µg±‚|—¼ ¹•$Ò<ß‘=îqR®_ß§Ñ´Ns‰Ì¾‘ý\Cá¶ÈT¦6>•“w>³€`Waýeóá¬e2Éê(›åöÇn˜Œöm$² `,²6ö¶ê™¹•˜×ŽAÊÍ/ˆtĺzéñä3K£)¬ÅSå™ïYË/ë91½.–¹ Ó-ÿN'—mü†ÇB…ïg¢£»Ÿ#Ùå’!®Ìe¤k§­H_sPû>i;…Ù†›[r“ï-5ñd›ŸP®ÿÀ¿~²þ ê3²ôUCì·î/üug3æòá«È`<ÿtL˜îÎQåJI>’ŒA®k8¹mÑtÇ€Ò:s:.ú2R®ß¢ª¤››I¤ÝܽhZÏR˜0È‹…kø+ɳÃ}˜Ù„09ç¯"„›$1Ò<¦9i§”¤ÜþéCaxÂJ"'¿ë‹‹-„ã—,ÆóQ y‘ÑpJí!”›ï§‘Èâü9YYÁomw$ÿ‡SU}=ÚÒN“㩚×YܽJ¶pR5÷üà€rÏRFªæbNàÕ;©šÿ=[bî:K©šËõß§h o}FªæE>QЬ?‹‘ªùXÇý°LLà¤j~¾$g6LdäŸö{›ºÇõ­$R5(cþuÝ©š—'¦«/qòO÷Œ´Cèas‰TÍc,r1êÅkFþéüU+ËY ·FþéçÿË[Um1Ïß|gä!Ÿ@ºÿöÜÒ o]ßqrš£6÷;.äO\–}–‘¹7½qÂöïJ°¤ÃQNÚ½ËÆÓ£º¹>:E+Æ2òÍ—è?ŸÏI¹þŠÅk±æt#“Fx"F=Fø¶ñünÍÉo÷#pýÀa£Ø‰8üFXg—5zÎæœÛ†Ô3í%òô>uT-¯'”ëWºe(Š»Kä­8³GO¸»ùLŒhòyÌVìôî( šÚ_û‡ròд&Ð×;¡ åö»¬ï‰®£ü8Y<©z†o¶iZ€û?J„zmcñáW+‰”›oX¾­­v3²þª?·ZWX‹ú¹¡˜møƒ‘«¾DÂ`çá½ÿû¿¥:MãdÔÖzX²>YIìCÆ %Œ|g˜€}]dòÒ<^ÔK"×8Oó Ý„릢õËwœôÉ(acõÆåú[ÔñG‡YÑŒ\³g/Jö ßñƒíp-‰\c8ÊO-…®+p½^sN´î†EÑä÷\O–­«$·ôzÊ‹?3R®_Y§vÐí¬!‘MÕµPÝà'w]ÛƒçÕ† 5¾'๯¾P÷¦ ‚—G3ÒëúL±M(·ÿô?áb´Œ“^¶Çˆ•“…‹VÂdcK‰,f…Âã78)7?Fï( c*É=›bq?/’“µxªá­°=û-##Ãü¬Ç––ZȽÕMAFЇÖó¶Œl¤‡šs2ôô^\öZuïƒÓj)­WÃ-ON6Ú[ÈZ<âŒÔ?ÎúŽ åú»ôïOGŒL]©l¹°·–æÝl*‘6oaXÈN¶2î°Œt4Úoƒ…W¢Vbn¥)È.§Ò ;´œ‘rýLý`ÕZiœŒ¿PÅ:ôØ tmœ…O›lôó<†?aä “$hÆÖQ’#††Á¥è$'åößMÔC+»œ|¡lŽöÛ{ÖzHoÕZ8¾È¿TróÚ¹o?rRmö8lëÞ@"kñTßgGbX)#Ï7̆–g¾°õD Ø[-»v8ŒÆîž¿Ó>^çdRÉüJÿ½#ê W;¨¹åt'\iü7#ï9Æ O-a ¯cZußR®ÿcv“'÷c¤Nót4œ£)ômt™éwIãäÇ‘aìîàîBo[?äT…3rÇ‹(Ä•¯æï=÷£u•¤EqöGrR®ßXåSf¢Ñ‚“6¾dzÁ½•äïûÅØT?‘‘cjn#ïz?áùÉSñdX‰ÔíùƒœÁI¹ý5=u0yM NÆÖ×Âf‡JrE7üZÀÈY{°<.H(7ÿíHŒ:q“î#³™ûŠÉÂZŒëw5Yš•Ž¬¹¿•¤Üüæ§BØÎä# Ò£y.srž‘µxª=Û¢g—­Œl„«¸d:Rès*ÓÒý„ÏG ›þp¡Þš\f•¨.Ì?yŸ ê•'|Û,±}Í…©cÚÕJ’}¶EìôÇ ²ûe_\5Ìa¤\ÿ9usp{Ø^%i°$g.êqòÜÆ‹,Ûó#Ï.m„Õ[€4xœ‰g·srðà$ø=–?X&vj ×kec¿)#åúí]ц“™:ÚÝí—”ÃÊx;'×μ†—¶—…«R&`W˜=Ȱ‹ñÑ7Êíw«_îîÍ”¤–ñOÖñGÎirÎáè.àd†ÿvèNo&‘ró3­Q¢ir器¹ÚAø?œªêëbjâ&·`¤j¾!9svÜTªyèó68®ß¤jaƒµ>œTÍ?PŒTÍåú_q”s9©š{\±‡ÿ3ªy·}{`“ÔR"UsËì[è~¤¹’üÓ~}ËHÕ¼|ØY é]O"UóŽ ábÉÈ?Ý¿ÖæK][èHªæSÒ¨?º¥Dþéüη÷ãŸÎ=AþéçÿË[U]\/¯._eäŽ_Ç~/CØru>tŽm/Ä’OtÚŒá&©Â•î Ýú°P–ŽTߎyìºü: —ôLG_Ëf9¤Ö…íh’t€“rýÙŠr|Õ[ÈÉá“Jñ(Nx6Å÷G¾`ä’[Qè])t*žäÌßœ<—e†eÛ^{N¿ˆ—¹×„?¶ùâÚ6‰”ëçÕÖS[¦2²Éú(Z}BxY}+ü¯éKäšSÑ÷m{áû¼+èºÐ™“û[Åáã³OB¹ýz‘½‘?b'½£-ÑöÐ_BÛO™sM¤pë°XÖ´ÊÔ‘”›ÿkd.åîdäËÛ‡°eùbY‹{œÄ²˜ýŒ<ì{ ]ª=„|g!ªjÚ)È'òQZﺒ|r8 o÷Nb¤Gž„e¾ëÉÆcðôk/‰ìç‹©¯&<›kŽÒI…JÒL¡ÀÊŒLFÊõïtŽEœ|h}ž;k„®îASólF&Ç üÂ:aqÿïÌès'Ç­Ía"êgÙvFÛÙg…èaÖé R®ß½›1ÔîÿþûßÿÏËI~xÃc…ýæÚC3¥µDž¿Úå]4…Õê3‘þø 'Ç™uGãŸ]„rû+;ô‚× ÆIÖÖ»v>ËcI O3²¾dˆû5AÊÍŸ˜ Ñ]8y/Þ•##…µxª«šœ†æŽ~ŒLÊ>À²B-ÿ´2‹“ _¥ _RŒ0ëD.Þož%ÜètúYO…·ºb~¹nÂØÅgØE†‚ì?=V—ëƒÜؼvkÔ)û tG‰\??i5„¶&â쮩fyNI•äðõ‡™V‹g Ò1ì3?q‚‘CÃÂçE”pÝ…8/n-”ë÷óËnÄ^ögäâü0¼ù6MØàSÚ´’“!_²ÙûƒÂawq»Â”‘-gzâëòH¡Üþ®¡fà­ÁÉݦ¸0»DI~nºg\È‘MáR/#¡Üü7¯ÿ±g‡99¹Eg%¸ kñT¯ï:,BA ÎÆ<‹ޤú³h<õ=ËÉy<[4® 'ŽÜÁùí%òDƒ¥¨˜d.l2VƒÊ“Y8Kª„¶:ç1©ì '­·¯‚õN…DÊõï8ã0²–™JäŽ=8b-ÌÛ‰×6Œ“Š_»°sp ÐI­Æê€”´Ð㔥ШM<Š’6sòœñ"8t«Êõó>}&Ëõ‹B¤¢î~v/Öœ“5Ë}Ù,—²ºí:Ù‰‘Û¯€ïÕAœ”Ûÿ¥©).ïS’F:¦htr ðý¡Ë°6U2Rwg úFF8’rómêắ­#™׆AŒ¬ÅS5¾˜…ç‡jrÈ,­ìâ•äªÜͨ¹ZÈ…sfá¹Z†ðI– ºê(‘ šT°´~/9¹Þšì)#‡ Dƒ‡‘Âe¶e,rp!'5~—²à«c)×_«:÷qÈ—éžïç(Œ5[Àz‡8ùZÇé3¢„CnnAÂLòºd¥÷>]üµÚÊÉgjÌÛm#åúíïváµsÈ;' ðþi%ìáÏnê1R¿Y<»|'L¸¦ó~4^ .‘™Ñ¢@O(·¾¿)Ü4’rÈ­~fè8õFòŸä<\ÞÂÉE+Âpã{¡Ü|«IðQh€³™=M„µxªï~¦cü¶ºœ,o•‚&‹Ì…šk‡Ã«i€°n@,6·ºl fCŒ6wòf¬0EA~H=ŒN+Î WþŽ…Á5NNz>¹ë¿0²ëÅx²Y ”ë¿eü2¬ŠuÈm&âþÒÞBm·^P4^ÉÉGs["qÛS%™¦Á±·X ¤Ç¼ë¸3é#ÕæY¡Nˆ)ÈB«84 é)”}PÄBõâdÚƒ̨\/¼ûê*köû# †Õ‡¦^S¦7γÙ>ÉœÜ÷c'³|X—‘rû£ë›cˆŽ¦‚œû­'Lf®v¼¿îGßprÃpGؼÊ͇­M;ƒüxæ^?~ÅÈÿáTU_Eû“0âüxNªæ ?Y"Ä"PIªæÃf±‰'©š?Ùw‹]œTÍ= Gî³QœTÍåú7 î‹€Kf©šOõh€y(HÕܳyž;ŸTªyev ÌÜ·0òOû%lÁæØHNªæ¶£aºº+HÕ<¹ .~u×ù§û«g[âcõ1©šOøÑ åY&JòOç[M»ƒKzfŒüÓÏÿ—·ªú|â$´—ȧ}ƒ‘¾ØHâ˜Åž[zrÒ± ‰ ¹‘£$¬»álã׌|ø{:Fwl ²ËÙX02‘È–óW Ø±Ð2ã –Ö±æäèš PW"åú¯V[·Ÿ9¹£Ñœ~ø\xÉ=”5>7Xøü¦«¥© 'Œ»ƒ »„ïÞ–B â䊉 æÍÈuoã0~ZSNÊõ›гmÓ9©ûÞÜô²p@ãµxùW+jK0X[_¸K ¾ì#+œ&£÷esrûÿê}˜VþP×C"->LA÷:ÿñXв§3²,ÆÚg «®]Ç ŸÃÂj·»Øäç¤ ŸŸôc/^¬T’—ž¦°×^;©>*»ãü…'õSðb¤$åúÇô·‡Ý¼(NöÞ×-m6 KôÐõa{›ŽÄœVÂmÒ¤û´“ÈóznÈ×n*\~Ë™ºœ‘Få!pš© R®ßÈ.ˆ<øˆ“&öް«)žÊ˃þŠ›ŒÌv—®ìV|ÏÄ˯‹8ÉÆî@Û˜‹B¹ý¼ÕEV?‡‘®©7Ø„©û…~OÒ‘©î­ ?GgàN÷霔›ÿvèj|ŽŠää̱pðÜ@a-žê‹ªaXëÔ]"÷rÀ¦]„L½?²©|Rw®uV¾Ò»…U~[9i±÷ S_"+~êÀÃ]ä™ÑSp9¦£ð÷[_,ûþ…“>½âçõ3B¹þÓ¾™à£–='¶wAþˇJòF¨'4®Ø€\Ô)‘K» §¾7ƒÑ“ N/k†ÞÛÖ?¨]ŃÓéw³NpRöd…ž6Ìåä¶:HX%l³­šhÌÈ ç+ÐT£4‡4=ZK7pòj…5Æ Urû7ã>áv˜‘{2_±!>YBØHLYÍÉ^áK0ܶ•DÊÍïlŽÊ©«É—Çñ÷‡UŒ¬ÅSÍþb‚O:HäQw=™ÞJÈïx=ûÅÈwY{qõg¥°kI,²ž˜KäêÜ÷±V²½¸¼Idâ¦4D–ä1ò~tö½ÝX¡¿ú´îlR®ÿTç.H÷êª$ã­M0Úè¡#9öu&&»ªƒìèq?RÙµ[côÝ­­$ˇ nlFÎ{„ºæi~R í:¶ÊõûÕë{¶n'g¨?`cS´„s.Ü‚ú³B‹]ÅðIË~<´Ó‡Ä0r¡z Þ< Êí×oð—œgäÀzXm]"œ2[Û¨Idåð æì0Ÿ“ró½Ö/įŒKŒŒ‚=c+„µxªÏžk¢{Km‰Üùé#Kh§&¼· çŽ32­ÉäX¬¾Ñ³Ä€ NymÓ6kú}Nb{F¿ùª ³2P]ZŸ“ ‚O!ˤ5Èë×nÃúË­R®qÇÿ5ÎQ­Kl¦®Ï„¸§ÆÍ„†¥…xÜlµ’œpÝ f02~ïRÌL>$LÖßÇ:íÕU’m“›â|Þ[FÊõ«ô¼Æ¦ýúC6J+`zÄ+Èü-çá>÷''³Ægâ’};‰¬9”ƒÄé9äÄu)ðºÉI¹ýfßÁ)¼’‘úÙº˜Q§Jø××PæÔ¼µ°°â%s¯Röàè $GD02Ûú"¬+<äÿpªª¯¿³o³_VO8©šk¶IÁb¯Ï R5¿ŸœÇ¾¬µà¤jÞ´Y$¤.û8©šýû>i$‘ª¹\ÿ´ßýðÄp$#Uó½ê¹¸›ºœ“ª¹N•?žØËHÕ<¿áèÛhüÓ~;^aKø`FªæNš±ײHÕÜ¡ëzÔ½f(‘º¿êœ>¦NúÌHÕ|½«3ÆŸ²ù§ó¥ ‹èÛe='ÿôóÿå­ªÞñ_€æ³ 99Ú}ê¿¿(¹p¿C)ZwÛÃÈ•…p*~  ‹§°ìnœnåÇÌÎ8’Oš§aòNZ|Ü ›G]%²æå]D»ýv —¤Ç”L‰”ëV{06í¹ÃHÝTW”¯ü ܃]ô9ùÁ>¡~¾ÂnÓá31Ž‘ÛGnGfF´p„Ãd$&… ÏŽŒÁ™…rý²ÊY´yùi²ã°DfÑñ“‚4ÑÙ‹xK ‰´± “t…NŠa·ƒ“cF&`Œº©DÊí/í4¥^ íc§¡i¸•pÀ¶XfÝŒ|¹¡<úë”›úÙqÄn×Y²åÆT{2²Î‡ÿ¤p²¯ûP|m)ìiZ%mö(ÉŒm%p龞“ÉŽ1õ#Œ<©ßý[‚\7¹?,mºJ仇Ul|ÝjN:_°BØc‰œvŒ•Ô?¡$åú‡_„ÁÖuA&+7côìB›ÞèÐ8…“ËÞ̤5™B— àõe#mŠGµh¹&T ‰JRs@4 &kJ¤\¿Ó2X@ïõŒ]y™½Ó=-\Ñuü•-$ò¬‡=òÎi ¿7ïïÓ­… ¶f°¿ NÊí?>n!Žõ¹©Û³½ÙÊ9o²šHdýp[”¼ÃI¹~w·íCÏ[×ï$bßKS¡¿q ÛÙªŒ‘¾iÀ´Mɧ\p¶|/'#4þ=³.sûrûoØÄ»ö=Aþ\žKf*Ìõ²†Y—¯ 2tú,8Ü d¤Üüñ_wáqŸFF%ÇcvMáÿpªª¯Ÿ]¬±5¬PAªæ?ÒëãÃØ)œTÍë œ…äõ$R5¯ %¼Ëà¤j^¨y£Ð^"Us¹þ76!ë}¹‚TÍÛ7ŠGÐæ UóúF8õš‘ªùŒvÍñ.”#ù§ýîŸD¯Aú UsŸg¦pÝbR5¹8ÖÍw1òO÷ÿö;‚:§º‚TÍ?¦at³]ŒüÓù†Ýâò‰%ù§Ÿÿ/oU5¤4Ž5úý#¸gÕƒã…j÷®°ß†•œ´mÁ"ºù ßxœ€Q›öi÷?¦9Wäg!§ ›’\_? ç…q²8ÅV…Œ1V‰} $)×ÿB³ lHiÒÙHÂî‘„NËn±®ãK‰ä¶èZ¡ rH›GÌÝz·’\7ø›WaÁȤsÿ cx}‰œÐF ©Mb8)×/°ß6¬7ryÿÝÜXe#ÜvÂÁ‡SÉ|úÂ'ð‘pؽ®ý÷NNV,Rƒs½« Rnß­Qè?ä#]^Ç ùñXáði¶ØßÓCha»G”åæG[áÎ:÷?+ñzSa->Œ_|žñ ùÈã.{úû•ð€Â‹eD+ÉãÎ>lìå7 re‹Á0¹×K"bë ÅÛºB«ä\V¨ 眚Ïq:ÂýŸ`äÞ—œÜüÖ»·”H¹þËC `·í#Ï÷ºŽ©6§„W» [;éã}Ta,|µLƒ\ŠÙz šõk2Øþ ÛÂHïuñWãž åúÅÅÃG½;Hç“(™ÕZè2rVÝ®'¬3{+Œ•Ú¬ŸÖØñµ†‘}B÷âD’>H¹ýFö è¡ÆÈši‡¡³9HØk]rk® §ÍÁ¾Çã„róÿò¾M;F&}¨À©ß9Y‹§zÔõ+sÏQiêªç‡Í…m^ƳaO02$¿‚­ŸñFxûb:‹÷ZÊÉIúGÙõ 2ò^Khâ '_Ô;ÇÌ[2¡ÍÀìù­qJÒgp+»µ)×?ïìM8ÿôddu[p\ÒNø-$ÕŸtAFËBœîmF¶Y¿-MAêf„fTgá–†J¼ÜDhúírÏLP’rý"÷œÁù3¿™ræ¼æ ‹ã÷béh-Ú+ñ`Ý/F²¤KpÖJj[—¡¨f8'åö˜v~½f™‚uÖ —Ǟǒyß”äû•(Üÿ”“róÍÔò±pÇ{Næ¶ ‡æ}‰¬ÅSÝ¢ÓÉèrT[s4<Ü]hZ¢‹¼ºzBW Áþ=„_³â›ÇisÛW<€ÜÕ%•-êÓ‘‘¥¡ß™ÑÅOÂa÷Ädx½»Húå® åúŸ™~÷ü-äC·[˜_ÇKI>uËGeÈRF¦à^©#y¿ÍäžüÁH«-¥̶ [OGÃs$²í}†åS» åúý¬,Æã»ÞŒœ’YŠ 5M¡‹Ë)üõ³Hø«ê<¢ønáM§ch±ÉH"Ë ‡ãQ'[¡Üþk7ÒP>}*#"³º ^{ã‡Eó$²d“|RÚ åæ§¾žŒ¡%rL~S8ìyÃÉZ<ÕôÃ?ß,A¾v÷æöÂм%Ø<ÅFØoò.¬R³–¦¸ãäÙæÂ1õ"`7ä#[ouÆ;£^ NîAe3[á׉鸏¥™DîÞe‡›ÿ(׿WRŒ62çd¿·×a}ÞWh§8ƒMþf‹–ép¾/´¿pí/ÖW’çÆÜ„É»8N6]~’MÈ7æE4ÄÃ’óŒ”ý©Ö­ j£½íÉMKðýw=N¦µ¾Ž¡[tÙö× ˜ô¼’CzoÍdé NsrLÒ6˸#åö·X™ ×ù&Œ|>? mGh ÏÌc–ÇS9™P¶“0_© 忇?-c:9ùdþ+fµ³#kñT?^›6Afß?&\±'¶ …Md!¨…ºPoÝq ÝÎÈýNc_ÏÙî…„yµA>Þpwš-fä2öˆU~[šC~¹;nŽeŒ”ë¿þÊEÔ×Ëâäõ_¹¨|\)Üñï3r™0ñŠ7B\žÓæqÍёȭ?ÃñÙ¶·ÐÌo;úNo²oN’' `¤ìw‡«HöÚÄIc×|ðLá…îŨÓfˆ0vw!æ:)Ì[oŒ‚Ü6 mz…À7½£PnÃlt||KAÞß{9Ÿ]ç–±•c^1òÁ¶¸Þ§7HÙ ÝQ’ÏÈáÆ‹a7àð8UÕ×Ïë¾è1OR5_ãy?¿ç2R5Ÿ?æ8¼½ÚsR5·|x%ßZrR5wúû Šóƒ©šËõ‰‚ýu$R5ÏÛ8Y½¯sR5_t1R&9J¤jþÍ7QXÌÉ?í·_;ywsR5Ÿ®Äò3j©š¯›ž-ß`Fþéþ5ù9Xdk­ UónW÷Ão È?_±1^)ù§Ÿÿ/oUµð”ØÉÈåÓqyÓ^aCÃèûw;öç”0y\GxÕ<9 º£]óðøû=Fº)Vák‡ý¾{ö€¿ón4õ…Ñm‰¼Ò§¼§læ¤\ÿ(£xºñ˜’üÔ=;‚ZrÒóÉÿý§D…:}çbv½wÂIîâî©}Jò·z"5NsòŒ]GL7®r²}$JGtÊõóiV€i¡ËÉ9£óRpII®è—¦)-sÈ)Ç‘3¾!'§õéþKIªôÁÑ#)·ÿpë=Pw8ÅHŸ¾¡Èx‘*œxÿs»™ÇÉŸ5Éì«÷GRn¾÷ð6¨nUÎÈþaÓ¡=Ad->\¼‹lÂù¢Í:”ŽÖ׿ŠbÃSB»ÓåpÆQøæ~1êÿ£#ìÿê*Ò zprê›Ô×*QÍ—Fâõ*%ùõ™H›¹^=†ã…rý—~Á²üáœTƆã~ˆ‡ðÉ\;\úvSøèPS\5Þ'Œø}f—[JäêNÁ0øh!Œ)-‡qVF~Ò;šSß9)×ïY¬„;Éc8yiÖIX”‡ wI†yÙV!›‡&»O ?MðD\j8#=nÇ"ëÝ;){ }À“b䬿C,º!,¢†•×Î »¥¹ ÷ìf åæoŠÀ»Gm@FM>‡'Í®1²OÕzñV<¸ÎÈM–> Ü'¼pürWõÌ!=öÝBŒÝxNšÏ?…÷»J…Ç—íí§%²qÛ°6Š“å}Öc™é5ᎇ x9z¼’\¶|#^Nà¤\ÿ öbÂ¥Næùãפ¯٠’/JÒ¦ë=æÑ:]AÎñsE×m=$Òø]+xŽl$Œœ57o: ç§0ë_G8)ׯµæ1´Ò䜴hs1on ‡G…àÀÉGB«/^Üð§ðÇ­h´ë>D˜:σO åö‰ÂÌ»™üî XævaµÅ>|Û¬R£ÏI„lØÅHÙ¯ŽåðÑóVÿœ(ÂåÕÑ\X{§ÚïŒ?ÆýÄÈW¿‚±Ï~»]ÃÒoœ¬;QÂÖ­ÚyÊf>vñÿ81¨üÚÜä䀀>ت\X⮠ţvÂð¯&¸îµVIê¹Á­{ÙŒ”ëŸ:à ìP'õ@ÈîH¡Îâ¯ì¨Õ>FÚöjj«OÂvþ¥¬ý(N =Å.gvU’½îæ°Wù)Œñs b.šƒ”ë÷Írääè¯Þˆß÷V¸qÍ\L^G" Î9aaܬcßW,&uâ›a“¶)#åös8Œüg-#›¼Gõ¦©B61•S:)ÉÉš‰Hü{'åæÛKiøö¼Dv Û³%ŽÂZ<ՙš˜æ¼Š‘Âqý·‹p`—$ÌëÚ]"ý£ƒ°ªn¡™¤…¶1C8içÔ_[ UKÔ ·¶.#ôîˆ>m¯Ç†µÚ ¡ÞÛ³è cËI¹þ‹¦/ÀM‡PNžóœ 7¾B›#Q¦²t±'jJ» Ó²n1]뙌ô¼Õ ¿šÝ¦–PpMx2ðZ¼³ã¤\¿Mz«`×ö'}[¸A£¨LxýQGœ>yUx3»>œ.Êó/kùø|jž}Êíß|“ `¤{u"6è&¼æmß œè1 ‰S÷ åæ÷yn€¯:KäǰT6d]'kñTÇUFaHw FÆ}ˆAŸÕ …wç.…ïI…Dê9BGËR˜û¦+.koedÖ‰Ð]š#|sf&LœtAN« ÃË3ÿ1ÌÆZÚKdqd%3¡ä¤\6`$Üg.á¤[›þˆYa`\ e¶9e‹[B¿1rD¿18ض!È1÷·Ã¶Á´6 Daz]‰4hkŠàm±œ”ë×â× |®Êáäå¼Þˆiv@èõ€5óí,\dp•å*&5–^Ák5F¾ßyWZæsRnÿZ$˜oÂHßçIð¨÷TANÈï  Û•ä•rfîéÁH¹ù¥­½Øù,CéX¯=œ[ÂÈÿáTU_]GÅãP¥‚TÍ®ÒÁWz©šÔÝŽú­s©š7>ƒÄߌTÍaÒ©šËõ·[èﳚœTÍ/v-‚Óž½ŒTÍçÜ•â.#Uó³e~«#ÿ´ß›Ý ½v'Uó’!E,ØÐˆ‘ªùÛ±;0ª[/‰üÓýþ9‚¯:É R5ï¸v9÷1òOçŸr¶†7ù§Ÿÿ/oUõyTÞUë´q¶7Î/ BÙÊÆ™e±Õv-…f5¯Ø¹ç œ¬ *`‰çîæ»úZàŠ®’“¯?7EŽå/%Ù9ÞG6˜,y‚})×åqT×}ÊÉß&Úè{æœp~Ÿ»¬xÛJFxéÂëj©°ùó†È]ª¯ ÿÛF/fä÷™Q².W˜ý­s…rR®ßÕÔ%øûíANÞ™ŒIAÂ|Iؼ>Thjî„Q«…·jÂYb‰ #ë›Ã£yrûm[!+)Œ‘»œo ÈÙKèé>ïÿ*äddVKü¥7Q(7¿½óLô資 ƒmQd(¬Å€fM.b¤ÓGFnÌ¿†M5ׄßVŽÀÓ¦ùxŒ®…üàd‡¸jö2Ì“‘¯“z sy•Ð;³ L–k ¯4v+V8I/ƒêüàd鬡¸¬f#‘rým:a¯„s²ß—;ìû«©ÂÍàÀDM.kÖc SSa‚ÇP<:x’‘kš¯Gn§BáÊÈØÌ^";Þe7}â¤\¿ŠCÃùi'««N…Z gè‰æê…úCŒ0oõ%Yçs$܇€|²ü2Vï›ÊH¹ý{ÚÝÄ„r7Fúú–⥋¹Ð¼^=˜ß,VcãM0G·ˆ‘ró'xtCße‹ä•¢¾ÈYâÇÈZ¬Wï7J²Ãü–Ðh®Rö·BV!Ûøå³’ìÕæ ›V/8‡œÚe}© òáÒc0{}ž‘m2÷@íÛQá8­xü3N˜·‡íµž!¼pt$¬“zƒ”ëwt¡ ²"Ë•d€§%²‚ªsHE!f«oV LðúGoFºý<‹íV{9™©€˜Ú)·ß<° Ͻ)HÞ³ÍNE µ{àÒø¦ }êEÚ䯌”›¿"l Ž5,`d÷ƒaxX“%¬ÅS-¾zõW3òVð=ÌË WM½3Ù†ÌÁJrŠï¿?yV7f䫺éXzo°02©3')H¿‰` g­ˆÃöVõ8Yiz iíAq/EÒv7%)×ßÉ7—ÍÚz\AÚÚ\dñ32Ò0%‘ë§5Gq¤jæ:’£ô ò²®‚œû9Ñ‘œÜ¡Åñ5¯HiTýü®$åúÕ_j…÷›ú+È={¨¯þ&üµÂë¹.c$n­9-¬iÞ;½®p²ÛøPkwþ)·ÿÃÜrì™YGAÖµ/GnÕ²Ò¢ÑeT¿#Œ»Š¢/nœ”›©4 —ìùm\®n9£$kñT~ÝCøß}rÈ0{xö¾PI–þ]ž̌e¤ÿAu<ÿüQhð3þ™Vœì:%ƒFÏ ;„k°ýªC„Ý{‡ã˜RO"'T¶@å˜bNÊõÿ¤QÁæç„0R/üS,¿ 8( KGvæd­$üÒ&NÅè[…s¶Á¥[¸p‹C¤í$r”¦!šüâ¤\¿‡=úÃúA_Fh;Ûg®º[ˆþeB6ßSò+…>•Ž ;úgú£ú¢R(·?>¶ }Ü•¤úáRÌwKx¶2ïšH¤ó¤µ¸£a!”›ÿ3øB7œædˆñNtè¬)‘µxªý/Uàb¸‚“–åh³ØWø¤‰Úèƒüµd>Ä÷¦OÙ†vÈçäu]L'<ß½2Ô ìßÁvIÝ!uáZ1‚‘ÿ>(4™ý^(׿Q7môÿ‘É è­ 2jxFûpòø†Í’W$ÌØ>(ö¶ù›áE`Wá»ßìÝ»% r÷!ñô#åúM×›ºë‚™×k5r½"„]z‡bÁÄRaÕƒ|×Éö´KÂþOO¤IÃh\¬ÎI¹ýלobÑ}NvÞqg ×}цÇm‰Ì5ÚÏ:œ´á¤ÜüÏG¢ïßêéZõ›=NŠçäÿpªª¯ n€×¤qR5_þ7¦ÚYTÍËõ­ð}·)'Uó‡oÐ8•‘ªù ‹iØìИ‘ª¹\ÿföø~¼-HÕüÎzWè˜dqR5ÿZÇߪN“ªù˜±ÈŒËeäŸöۚ艧:ÑŒTÍÿ:Ž„‚íŒTÍ­FIôNþéþ½‹Š°æö Nªæç^Ų‡üù§ó]ëžej'þéCþéçÿË[Uð5©&9’±oÒïGY÷”>Ôë‡)HçÙqÈc#KŽæ¡û/SafÞ¿ß¶Ÿ+IÍ·°{Í)á§½y¨iû”“Û‡Q`ÇÈ¿#`Øj5'åúþÏ”ãdtÔ–hpBIέ:­o‡8©139¿ õ-¢ñù¾·ð~ÐN¸í8-ì4ºóLÕ…- £à×C"åúe½·€öF†tBµc²pŠqt›ÛLè÷Õ‹ûÎ.ñš9¼ß*|±ä/„ŸÕ)·˜Ïe¬ŠŒç¤EÒE|(Í~6¼‚ŠW‡9ãH 6L¨P’²¿•«‘6«˜‘•9xr´•°Bõ°oË>%9cSW¼ÿÞ©#£9üûh(V gýHÅÁ4Nö¹3«…šçöaö—®¹mð(Ô›ÔYÒs<¶îóæd¢¿-&ºT(HÙ?jÅ~vÔ Ž‚<»7–}ÔhÃÈè×;ðu ‰|þØ Kç·¦FÎÑÒsœ´ëíןÂþàÙØ¦yx¢/bV­ åú½ê9ýÆ_däÓè%8z[hä: /gø ¿ê¯CÓØÂŽ–i¨S­òÖë›hêa¦ åöo]|¶ý®ñÿÇzFÕܶïO¦(©„H! ÉЀTû:Hf’yž2u›%ÃMH’¤R¡Ò¤’JR©´¯/"!™2–™Ì$dæïÅ>µö­ç»¬§ýæóâØû<•ÓÈ—×O¡äÔ#áï¹p\úQøÐ` æ{´•H¹ùû/ƒQ3N^\œ€®—Ž kðTÕLЦE[Nj5ÙƒÂàB+5wlõ˜‘cúcŽT!즘Ç‘o9yë~7¤¤¤ ·ßUƒké ¡N'Îêt‹V’7ûúbܶhF6“Š[;rý3e ´}¹úh)[®\È*L¡ið›“.nïÙ×TáÙ!-`ØÕPx-TuVOQƒÛ!h %ÈèéyÙT(û®S± Öúv$ƒ^” 'ÎØŠÐ áÂv¡“¥p†µ/«I¤©ÓB+m„rûýŸqLpüÄÉê¤<\]["/ø›CcÁô(È^/tä¤ÜüùG¼ð2ªŒ“šÑý°6u¿°O5dRʧÎçd‘f8FÏóêåGcOè5Ff¯MîÒTaâX}4vù¬$¯E Øäž‚ «[Á> `d°Ÿ5.6¨rÌï 4ËÏä¤wIgtû´L(×?hÍ7v©ÑKF†l‚ð+ M%öQùPI~qœ©+´©÷Úµjû †ôǵ§·„W›Ý…©ãI%m•ˆ.»:I¤ì/äæ1XÕú#›ÌJÁ§ ¡bziõúÖOÂé7d«œbvwð%NF´ c_W e¤Üþ®-ŽàÁ"M‰ ëw Oô„'ýß²Ï…ŽŒìx¬':ê¾ÊÍ/40gÃJòCX„éU+È<Õ»Pì3 áäà_˜e%|¸'Û\§3Òÿ¶„«eÑ ²ÞÊÞÐÒÛÉȧW`i IêëƒX‹& ]êÀ»GŒèk˾B¦çï„rýwZwAé=Ï S´š}ÁêÙsF­l ÍƒÚ Õ>yâ͞ߌ´ÚýççÍ[á’öMñ!PG"OØ$±†cä\?£ï‡pzÍVFw kïöNøž‚6MýìÉfyÉhºXIWvÇkW wíSpoû”Ûÿ±sÚ¦·È9·’`{²µÐ¨~ú×¹xüA¤T¤3Rn¾×ÐèZ½“‘UY›0dÄ~a žj­%[PÔ&“ ’7¢¸v’ÐrðI\^§$áù¾µˆ“¿´ƒñÉ1ƒ‘Éköâb¢Ð-ñ–zvè"áùÉœ´8}úê#ä‹oñPÄÔ‘H¹þ×öÏÁf‹N ÿY³ý5Í…«ƒ¦b¬ù¬° Fê a§vG0£U2# ú“ÉM)-q€²ÈX­ ø4UÊõk]—£Mýé—+aöŠ:Jòà°|¿3˜“åív£Z#H8!» {3²a«,úPÊI¹ýFÄ#¾i{‰ô ‚zb'á:¯ãXx¿¹‚®ŸŽQ&Þœ”›?Õ6½/d¤ó¸d ,2Rÿéª><|WáË$Nªæ)¿¡(WÉÉÿ/?“ŠçÊHÕ<>6~Ñœ“ªyp¼6%¼ç¤j.×ô“íˆ{ؤj¾¸a&:¾m R5Ÿ\«^æ­8©š›®;÷AJòoûmÀa¬ÇIÕü¹v šöHå¤jžuk+”ozKäßîxt>Îé"‘ªùÜÉxYy‘“;ßðQŒ÷›qòo_ÿ_žªªyþäÎUrÒNûø'ÂGk×¢öHFÚöGɳÂS†º8zÍ•“­ë#âçwr×ßÌép-Ý‚§Bÿð‘E1®|,åää cðR£—DÊõ³Æ×ÞŽeäuggì‘6Ïϵǀ[8™×¾ ºoKúüJg¯«,Ù|øæôBØeÔmÌWÌU’Wíö!«ÀL"eV‰Ä£1† SG€áM-¡_| ü ³™;,+χ ]^/ÄR›??ÿO[G+œëS¥$eÿ}—'÷„…áö–Xa©½#Ž`ä½ò8Z,”›ìæÏ†´lÈɶ“ØäðHFÖà€²îh1´ŽD>qÊÃÚúÂcëÃÐëµ'#;]Þƒ ÖB÷Á­ÑñÀfaªúH=¿$Œ_–ˆÌA6œ^‚¥K/0róZ?V©¸©$ok íáíAÊõïtg2ΟÛÎÈÛ»çcIçHáùßßXÿ‡õ8i³ç»¡¾GAú2Tض¹¾p *oZ –6FúÁ&y´(€m;R® åúÙjäáᎷŒt7(Ä®áG…éMa°lŠP»m*,'¾Q.ãÙŸŸ†vŒ,‹ÞˆA—£…rû†î@`Ò>Nf-Æð;„‡%¢Þ¸hF>¶8Šç÷†*H¹ù÷n¡Ì°-H+§ÍØ6Ï^Xƒ§ºrI&Ü'´•ÈnïC…ž…°eÂ>ô¿—ª Ÿ MÆ“z»óÉU;|ñ(«Œ‘aÍãá*¥›~º Ÿ{ùäéÀ‹Øìz““UKŽcÃ.}Æ>wðpÉ4NÊõ·©µ鱌´|`i#ö\Aü„&Cz¡Òè²°Ø7Û›·©—v+%F–Ù1„/°¹µ+n5Êõëñ¤¦w2òþ‡kP×ÉW«À+´žÙkÅ~ðg•ä x¬HhÅÈÒðÜé߈“rûë˜b™Á!NN.ÜŠœñ‡…­t2Ñsº—0?' +„ró×v:çyM@úõ¹…'Æ3Yƒ§“‰p ‰t4Úç–½…w%âÀ±OJò²ƒ[ËÆqrLZ&¦ Ñedk×C0¥$Wú$@wcw‰,º?Êͽ…¶ã·`TK{aºI6³Y”ÀI¹þÙ@ûÒ]Œ,·ßNwý„®÷æ¢ËêÂðá83à­°l[,B×+H£w×ѢؓçÚÞÅÕca§£ðës‰”ëý³•ZS”ä¿/®àT㡜ôý€— ú «ûïÆéU¾ÂW¯7á”^ŒÐ5«ö®*”Û0Ò÷Ïwä\Nî,öFÄñ<¡yÎp¼éüD˜5D ·5ç åæ/¹ ¿ƒã8ywU.z„jKd žªÁØM(±±—ȱ–Àb¤°öÚØv?“!·|‘è/¥Ü‹g|„%ó°xöaË­¯Ùá)µ%2o¿Ëò®ÅÉq—>°ôWŸ©¡‡5?Úƒ”ë¿Ì}712i,‚NõþÓ5o/Ÿ®ˆ9„¨&Ë…Ú—•ئ¥)‘Ï»F ö~!Ö~fuÛÖî|ÆnÌH¹~µ¯‡ñÒœœ¯qá…/„k†bÆ€a¤£¦|Û+,ÙÙ ùm.*È×ç!$z?#åöý»U¥Ç99aø*¼½õ¯§}gn³>*ÈÕã,ðü\#e¿@Þߌ”íÖy3ÞW „5xªêgƣ̣§Dfwï‹CÝ„u&»ãÁœt<¾ìîv·…¨Tô?å&¬5+‘-èÅÈ–:†ÈiÔä­Øè“¢iO–ñ¸V)×ßôPŠ/h1’ÏÚ‡I3“d¡K.èxþG¯Ã¨çùNI~n0 ~õ’HcMœV×Þ½2|„ È7å(pûÀH¹~ êg¡ËQm‰L_¸'m…9UsñÊ2’“g§¹ Ût£0Ù1'‚ùük& Ì/ç“rûÇ·YŠÂ3yœÌúá†AQ¹Âò8ož  rÀÆLhÿ,c¤ÜüAÎ'ØÍŸÁœÌõ9À¼^Vÿéª>ΤuÆ7S3‰TÍ%£¾È‰™ËIÕ¼p¦9.Û V’ªùÀÞðß ©š»¯Ò€ÿä;œTÍåúŸHI¼|R5ÿç×~Yµ•“ªù|‡ýläè8Nªæ›œKñaÐbNþm¿œVÛQµ°‹Dªæ¥_Jj÷â¤jÞtVêfròo÷~=÷&æ¤j>bÚyôŒ;­ ÿvþëÄúxÐë=#ÿöõÿ婪f^ôFzîN¦>úŽýÒ„¾ýBi4ƒ‘§Ý'O+È7=¢ïÁ¹Ó>«GçìÜú'R9ysÀV̺ýN˜eŠ¡¿âdæÑð:ÂI¹þ¿?ïGƒøz φFÀÔWŒÜÐún† ›žL„цÂûoÒp¨­±0ìíÔz†‘“žæ ÷eMûÿ¹‰e¾(I¹~Δ¢v—ƒŒœ0´‹NÚ ·ÞTÂôDm%YÛånö³ä¤Ñл8¾µ¿’t ÈFdz-%Rn¿»"ýƒ×sÒyo&¾ û\º„èk\¸éR,^ùw•H¹ù~mÎ!Ú¯¯’œ¸-¦Nr²¿”Z-†nì~N6.p…òò^á¿¿³áa«©$ÿù¥<éK[NFUæ`Fÿè|rˆá´{ÿ'»¦ ‚gY¹ðk}]lÒ[(|S<AÝ„Ò(;dnmÉH¹þ+6KpÚt’‘±‹Î"µ~ˆpZ¶O† ½o#£•¹pwömœï¨ û;_Cmã͜ԱŠÄÁ%rܳoìꘜ”ë·¥Û}T¼ t Ç » ++N:gf Ùîpaò«x¨]9+|ÐÙ w»JäÚ_{Ù´ÍÿpRnÜÙƒh°6Š“é©xŽ¡Eÿ>ˆ9g!‘MªN1½5G9)7¿Öð5z9!q–mk$¬ÁSÝnæ2{#ÇÎÑÛž åúU»'}b8YX|g7Uo A‡ŽO…Ïî­‡F¿7Â<ã«lÅ Œü4nÚ€”Ûßtj2üæpòÈͽ~ ¿ýŒ`ϵ?*Èf ñh`²ŸÊƒ¾1󫉜´÷;ËÊÏ·P5xªO4œp>΋“•qöðî7]Xw–?´;v{ïSΗ]ì‹àâÝ©N¦È;¢&´7†=k\9/i«ikº A³wrÒ÷¶‰ñ¦B¹þ ÷]†×Z¦$ço(A³mí8Y4%}|êHäþ!(G!=ov®¸­ ÷âÚÂáŸ. gàMF>ot ·&ä¤\¿d]%&´Ð“È jw'áÃ+“±'ÿ>'û¤8 ¸#ñÜ/xÁH½ìx=B—“rûóÛÅ¡•G'DcÍ—ËÂÈæë°1Ýdÿ§G1FKS(7?Ë¥."œ‹0rî§´Yƒ§ºp¡ Þ¾°åd‡í]¡óH[˜P:·<²„QËP¯ ö¡XY$”ëorê:ÎÝÊÉÄSp»/ÔvŸ%&¹zº ¦¡°Íˆ<æíÉIµKIlZ`˜‚|³Å$Ò·E:Ë4Lå¤\¿;#Âá³ËV"7tóÆ…¡LXffŠi϶q2a¡>X‚¡ðZá>Ì63–È–˜dÛR(·ÿLÿø¿ÅI‹;a>ÿžpß©й¨ÅȪ±¡37‰“²ŸÊk¢‘Þ䛆§qãêaFÖà©Þ73GÄ%™:¤Ž*¼5¤3Ü×öâd =œÚï£$Ë‹ÿÁŒÙŒl¯„gåiÂÊËGP^–ÁIÃ’Ðýª!‘÷¥àA _%™`º M ÷sR®ÿ-³cPw{ÉÉ‚‹ñÃIC"ÃOæä¸”mXt¯Bø)4ÿ®k+‘ŽCá5ÊT(7ÿUìUè\ÍÈ''YžÂ®Õ8ù?œªê÷¡ú 4:Nªæ³CMPî7UAªæ;ú$ õ÷FªæÇ«\ÐC©!‘ª¹Þ†.P÷¨V’ª¹\ÿ¦ãPþµ™Dªæw‹Ž²:í©š¯Þ¹N}ÌAªæ‘îwÐzÏ"Fþm¿7´à>µ¹DªæÏ›š î†ÅŒTÍÃü¶Àñi{»c¶ Ëâ'Uóªy÷YïÉG9ù·ó΋€å¨¶ù·¯ÿ/OUµcºVæö’ÈcuþÁú>=…W–åA¿ËYNf¾LÆÂ_ê¹keý“ÆIm«á9ë¤ðÕml\ÙOhnÖjŸ5òÈ3¢p"Æä|Í»xbå@Êõ¯ ;Š ‚l;¬§WW0Ò$äæŸÝ'tù¬„ßGmá¬sq/|9'Wí†ö“”ê|vh¼##7˜‹Æm;€”ë÷r|)æÏóU’6.ÃÞf2'ûìÙ §Ÿúù$a Š&´®4pB×^9¨w4r~Wåö?ï‘El‰Ì߃>V©Õ=‘´ð»‚œé51Û)7ÿÂÚ©ÞÀÉõcíp>\[Xƒ_ÊšDR¨¥D†ÿêÁ§; s‡`¹]SaÅwŒ^o \0¦v1œÔ±ì€àËM…ÇÕ0<Ô—‘¥ýæ¢Cv´ÐÚ0¹“ºKd÷©×YtÁ}NÊõw?\ŒnÚ‡9nã5$8/.:'Á×Ë_I¶?u‹—ûra3/¸^¿#“6 µN>ŸÁÌD‰‘bÎ!¯K*'åú­>u?x6'{‡GõøJ¡SÌ0l\£+‘«ç·ÃÄÕœ¼ºCB½%Ù r}F:FýÈæ¤ÜþÄWa8¸°§DZë£bdoaל~¹›‘ÍÆÄãâõîB¹ùóÞY!øýHéÜy†º¸2²OUÓÔ¿KdڋƘ=SW軫¬Ök ^j‰{/Ÿprô¯6psqSÉ·{"2m#km€ÓáhaºAøÝ…wß7mŸÎJRÿŒ;Š~v’ÈrÝ“ì”ûJNÊ~WœsC‰4H‹Ä÷m„®©cÐÄlN¾I)c}CXj¼ ÔkIäË -|ª^ÉI¹ýíÚlÅýnö9ÒÄßj;‹'cö˜J2),¶—–rRnþHi%*+#ù|ÅØèoÖà©vûM´¯%‘…ËÊXìž»œ|´î KÔŠþ[vŦÿV’u;¹"dÇaF^­½ möÿ”‚¶ì„=âÎ8™},ÈÆö±˜»ØR"åú/˜{«›sÒnñ¬;½U˜4`Üë¾ÞeíŠb¡Ú[CÌ?úç;ìÿyb­=J5“…+kcPóWÂQ"Þ¤\¿ì[àéÔQ"g…º¡§¢ƒ°ãÁbö úõqrxý;,ÖÞ’‘»7€úúÙÂÃînxP\ ¤ÜþñýVBû¢½DtqƒÇ6;á”Á^pV‹çdS¡pxé!”›Ÿ{f/\úW+ÈF7÷¡ª[ˆ’¬ÁSMÍ=ÁZ>ÉádEüÖó‡ŸðW›+Ì=m£‚¼qô%Û·†‘­‚âñ&ÈWxJ7ïô)Èê²ø_Šäd3·yˆì%,Ýru¿ÿ@èü³˜ùÞb¤\ÿêCçÛ¥€“+$D¾ø$ôÔÑ…Ç……Óýj¡Õ{'%YGsžÎ¾ÆÈîÉÛÑzïEañèëx65_A>Ø‘—Ö:)û®£ß[æI¤ÿ6ˆ¨)ÔéU λÒ¹£Q[Œò¬Î8vo³%¡Ã»“h $åö;m˜„o}l%òåãaxUi#ŒþÖ Æƒì•dÅ{ÔîבróÜÀ€.K8éX×zG kðTÛD3Ÿ)=8ù oëÝ倒졦ƒKÿ^d¤×…˜àQdÄ«4pObн¤\¿zIµ±2ý1'Û5»ÃÌï& =C£R«1Èòº«p?Á@8êö´¼ó’“'' €9ª„rûýC€ZqÝ%R;Ê sÇtNvúÑŒl¨‚f/wåæ)ƒ:†9ùoqO„ç•(ÉÿáTU3Ɔ°ˆ`;R5~犄;ú UóúV[ðìÈ1NªæIm~<µg¤jn3>‡ÇYI¤j.×?9|›w–ÈÿoÿÜaØ™\ÁHÕ\{`üV§(IÕ¼¯ššiòoûyü“ÏŒËí8©šçD†"ÇT¤jÞ¸×gf»¥PIþíþ2=3ìkÚ^"UsÞgª‹ë2òoç«¿ís·säß¾þ¿¢<½…Ý;­Äs¿…rû×ýèˆsêêéóokì,ÿÄÉÈãMñÔÖH"5ï³ÕG8)7¿Çø«8Q÷¾=ù1ùÆjŸád žªzvkœ]á§ Ï4n‡«_ KBÊÙœÏ9ñxKG¶ì|>®»ä!›4¼ÿYI†\Ÿ½ÓÛHäÕõQ¢~Ÿ“cÛÀ!`‚‚lub!ÂB®1R®¿Ct$:é.åäñâHÙ±Uxï\]X/ºÀÈŠ;¸tÔyµz´nØ0òPîÄõ u [ŠEÀźi7½-œ•qRöÒq(ö;ÌÉ3mà6+[ès´¢Ë@ÎÑŸ†)–Âáµ³à1#™‘íN 8¢.'eßÕ·6C¢K'mµ1æßëÂtí –”ÆÈî–ö(ªÝ¤Üü]qáHûÝZ"«ï8aµ¡‘°Oµ¯FG8>lÏÈ›º"aÂ(aŸ{#ñú­ È5ß|¿ÀNXÞ*¯ìB9yý†/š¥(…Ï«Ž°„3¡JÒ{ì}V´0‰‘©ù™hTá,\1=îY±œ”ëßu`ÂÒÂ8¹ ïl½+|öz 4ÆÔ<ƒÃwu„=æ&Ãz¾.'§uÄ$/?áž oÙŠïÆŒÜºi¹7)×oÔ¶¸ëÀÉ\Ç&øfe/¬óÑ~ý¬A¦¼Ú‡.L…ÎßÃþ“›ZØÀ#ø±Pnÿ¦Ðzh²´€“ üfu\2…[ãð^ßä㊫h³)‹‘ró+=bn9œtšµù8KAÖà©F¸öDk½О½]ˆ©‰˜ãnr_Žu‹´„»OÄ…áYœŒôçdŸÙÂÁÑغ¡Èºî¡Øj*ÜÙ{(xI 'wjˆË9{¤\ÿ¯6 áÃxNnt\†´à=Âý~¹PÌ¿ÁÈñíÏáàÝaÂoŠ-he˜ÂÉc_æ`ùç8áôˆc8Ù0Œ‘úÇà^¹“rýº=nˆ Ý}Jr·¤¢±å¤Ù%æ,­ rЧ “ÍÈKó^² ›òÉÛ ;CýîFÊíoû¼ŠÝéÍÉ8Ãç,[„ W–a‡¦…ÐÁà¼[·H¹ùw®«ÁÖñ #sÓQ’Öäÿpªª¯ƒ¡<ËHÕü® xÜqFªæ[[!eÍ«|R5·ú-ÁäÈ=FªæÍÎ.oü{Fªærýï«ÏÂÂoáœTÍ+JŠñàóH%©š·ÓrÂ÷ëó9©š¯:¾ËÖ–È¿íWX§–˜6`¤j^¢yöúŒTÍSwì¾! ÿvýÊ{l^‹YœTÍ'û/À³ç½%òoçÔMB¯6† ÿöõÿ婪n›dŒaËt%òŠš&Ö¿Sz¼f<9yãã¦ÿ¤v>™óççϺº›ÙbÎZÌ}'4p«Ê— µêßAnÖrN®?y¾˜€¼pú⇛qR®ÿÞÞ§P´^ÉɺGñeÐ+aË~8}á?š¥NCLב޲,Í«Œœb䯢V ‡µ»ÌŒ%(É‹}¬a6ú'#åúe.ùN»£8É.»áåþÂm¾›ÙM ÈŠH–é·†‘:û+Ø’ž¿89èHówîÉH¹ýý.-C¿ÊÝœ|ºp!¢]Ã…Û¿% Gï.Âåñqê¡ÜüQ÷ p#æ7#÷+lj¨m ²¿|ûÀn}ÌÉéín°1ÚJaÁ…(÷h#õn°ÂäG–†âàÀP¡IUtz 8ŒÁÕ­$òW¯•0üè TÔÚ„Ck¡ËLæìâÌI¹þ¦À M ‰íƒ±M…s¶tÇÕÇùœìõH;'L†÷Cû¡AvJÎÀ’- „ë«Sp=ý#ë(€^è5%)×oôÔ‰X}Å—“ÕECQ¸jŽ]»ÊŽ>«`äµ›úÐ¼Þ dB5pϹ»ðø•#x=¿™Pnÿz³yøÞl''ר»â«n°0¤¾+ªR„ƒnY¡â¢µPn~σQx†“AE±Èij!‘5xªÁ ÎöE†r2vfë4rˆÐ­»F¶3¹ÔÙãÞZ nJÅÂS¾äÒÐ$ìúÙ†“vAõ0ßQG"c…° vfœìèbЦßt@j ÍÅçóÕŒ”ë_ª B€®‘D^m·©õ[ ï VCœÓô|²Qk h*1rVÏóx:(TÈ^Á-ûßùäêÓA8Ø@M"sÛ¶BÄ NÊõû–Þ9VœÜd‹FSÞ)ÉOúCÞ义›PÛ˜ /·+Gîä“=:d¢NEK‰”ÛߢþT4œ¸“Óì&`eÕfá½¶íQÞÁCAzwuÄ–“ÁŒ”›¿Ò{ Ž;ZHdRëÌJã'kðTÝ߯±#Î7”ä­vÑLÍÕ Ÿœ÷ç—¾QoΖ‰ØßÇ\˜Ž«íÂ9Ù¬þF8Ì9"l<óûÝ ’‘×ÛuÀâAí@Né[„š;8Yõtü¤)×_Ïd&öØJd£@§?ôºÂªËí±úÕ FjÁU_º)§5*•“ÒË$\ì¯)‘=§7CŽÇLFwÞ„#  åúÍ9cÉS¦(I§mV0nîè@•ÇaxœHE«ãø·OSáÇù=á2ÇT"ýlXªæ'%)·ß a4Ïoàä¥~#íî.4_´NçO3rÛÑ8¬¿%”›_t7šÙvPú‘_XÔ팬ÁSmÔ6–•/8¤ µž$±:z32)I‰±Í€dý/Ã-3‹‘Ǿ„gi'—F[`áQ…0,/ÅY=Av?‰ƒÔ„ïG_e§Ã¬”äÂvÎèá£Rö]k¾9ÚÙÔ–Èn)MÐ\ã'ívøÂ"¹>Èžÿ&`gõ;FêŽðBÊ #‰tßmm4„²0É„‘ _±?'åú9UÚ SYŒ‚ÊÎA6þmZkðTç>Âr•ÛyF«˜-îœ+ÜÕô<ìj {·¸ƒÄî÷”d›0#xU5w _\0G´åDFÚŽ¸ƒI›s䯉%pZ–ÍÉ<û\èÞËÈ;rà90“rý µk¡Aõ!NöÝPÁÚ¾X+ôø 'ƒDF®+;ƒý#(ÈKö?™ö½œì«¼Ê¼|u”ä“¢ÙÐò8ù«S{x;ø(IÙÿJZNØb=‘Œ\h´Jx^ÿ>V¦*É‹çnÁÊx+'×=¾«„ Œìuâ46_?ÌIÙ¿ÏÑ~ðHíÇÉ|÷>P6±æö AþÓë­ËFañÒÿ(7¿àe1÷fäµ³W`8J“ÿéª>ÌÏ?cûû>d¤j¾nöe ÉOá¤j¾¬Í(¸ÿ>ÆHÕüuç½HÞb*‘ªùÙ£Ð$õ'ÿ¿ý2ý¿M¼Î\*59©šŸœUˆ‡\“ªyräf÷ïFªæÍc‡`aÒ6Fþm¿ ßið,d¤j~hÏ9œÿZÉIÕ<Éd#ìõÚJäßîO^¯ÀøÖœTÍ;XüùùÖÌ…“;¿IAòzÿâäß¾þ¿6€û ái¬v·‹ŒTËoˆ):M@–é9 ÷…íŒ<„-ðÚvD(û®ºu;RÜ´$aK¬­Ò®:/šY ä²ò9)7ßzÏaÄtà¤G¯t ÊÖà€þ)ÚH©ç« Ÿ¿ÓÃÏïBÛ9í`ý¾6ÈóíÇ`ìWa¯â}8¿i»‚.@÷9jB“Çàý{3'ëÎpÇÅ·Ÿ„rý£´px³%'mëÃ’L‰>5îòdáä¥Û sÏìCZ¢9#MÆ&aTü%)·ÿX°~íÕ‘ÈÏ×!nµ®°¶Ù¦å®ÏÈ97[Àÿ‰H¹ù3ìWÃ7ñ 'Çõ¾ÙìÖà©^iÒ=5{0Òÿuœù=OX§«ž]haË<\ßX¸~o4˜ÞtN69± sG õލáÆX]F:®·ÂJ³óBçæaèäüDø³î)t?ÚCIÊõ†ß $2±n b[wÎ9ì‚\µV ½Ûðú‚±pذ2Ę[1òsÁÌýóAHºwoÉXGX÷VO˜$E0R®ß‹#ðLí« ½¼uÐñWKFæÿŒD÷/– ûÆbµæÂÜ;ÐÙÅÉa—&àËÃÍB¹ý_#Ýa²HW"7 ‡„ :¨¤mПbreöI,\|‹‘róO·k‡_nÇ•dûpsìɨÍÈ<Õœ¡æÐ¨ðg¤ÞO|¿+´4ËBx‹ ag³“0n&¶a~ú%ròÌÐþ¨xã%ì—µ÷¢>22"/~å’ð˜E ´žhJ¤îÍ·lmö&NÊõï€ߺKä®äp5µž¿—ŠGCë€tò>‰®êû9»û)h'Ö—HŸwáÐ{ÖK¸vY0ê9eäÝiXÓ_AÊõ3©oŒYÿ22tdw¬{»Wh9ö<žlº-<Öç& ÚL>ò´ÆàW%¹ö}_¼ˆÄH¹ýk4§ !ãÏýÿyâµ ýÒW_Åë5J2¤O.ìN<ç¤ÜüQ·QàÂÈ™ðBÜ¡a žê±³}p,>ƒ‘Š%Ãѱ:_8ú@tŽ?T‘fEpü§$ïvÂzE¹Ð±¯)R»íUš‡q3E—‘?gf£ïžÏJr¾©Ú?þÀÈ_33Ú±H¹þvoFáÉÊ®9<Þ!:í„fc/`u¯hi¡{~VœŒçŒ"e/‰,ZðehÔ¦µØ C_Nޏ=kF‡ åú î9AŸO0²rö4l¼xUx¬qRLôóɰ5×Q÷Ê"N:oYºIIŒ||4VÍÊí7¬5=iHd—Iö¸£VGXñu#z[ ë~0Äó«O9)7X½pœJõedùíDÜ y£ kðTßçLBØáBF¶™è†;ç‹…ï$´™±‘“#­á^Â9áž¶ø·™‘egáXVPÃcü߯s2m†7Ü „‘Ë®`ò“5ÂëÒ?08c%‘rýçüùéÑd´®D¦Ý­d~'>pòˆáÔO)Ni KëÚ©Ý0„M;6„“fQÛÙåöŒœÒÅËa®$ .9ãV¢;#åú½ô\…&U·Y¬ã‡¢ÀkÂÅ=þü—ñ,ãäºÛ‡0«™¾D:¶H‡cA¸’Œz² ‘‡9)·ß¹%ž?9¹®u4ÿ^踼ŒôÜ¢$Ÿ'j!wê>FÊÍï☄’u«•d½ÍQ6šÄÉÿáTUhµ­.1R5)ˆA䋯œTÍ/æâÔ…bFªæK7:ÃàE,'UóžÓCØ©•¤j.×?6â +èxœ“ªùü`O öÕ”HÕ|V÷w,gmªy¡n:6`äßö‹÷ݳ ©šï:Žê#橚çÞá'ÿv¿ÕA#|‹ÌIÕ¼ÓKWè¯ òoç·lé‡#UáœüÛ×ÿ—§ªzݬ VŒ|ÇÉ'þꈻpIXÞþzÞŒœn`—cÇ„÷ öÀeÅRáÍɰÓÏPÓZ‡ãný@N^8ê…_~ñB‡pyR[‰´ŽøÈ ½Ô…rý;Nƒ{š9'‡Fã0%<3¾?œí’…v.íP9Mø]ß ?v3²¢ÚvS® Gß÷Úú O-/D®k¨‚”ý.÷¥9~LÔ“H—›•LñÏ3N®5ÒÄâ×uA/s€} ‰0®i]Œ^?‘ãê¸áòqmrûu\FÃ_«·DÞOt‚ó áž÷«0üÓqN6öë Ë!B¹ùwÖDŸÄv 2èN*öØr²¿¤xÃÈØ/1øÔ¤\ÿ3 "ÐíÝ NfÞ EJÎVáK=¸¸­P’{ëëbuÉÙ¾*“*22T#WŒŽ-5ãaÒã'¯Ý·Ç vB(ûܲ€}7ŒáäÞûY§öM…×½<`²® ÈÏ¿vA³®™P9)£Ž_a¤¢ ‹£8)·¿pjOh¬è&‘¡ñæ}Ga˜š>nåtS’ÙGÚ@§åfFÊÍ¿ÿ. éõó8™o¿¢o kðTÍ6I̦IwN¦öÈa^ºû•dúÜ ¸W_g¤ÑÌäf ÏO Âö'Û9ù«ÇjÜ)–ZZ¡á¯r²ÃÐ=ù­ä š.ˆU’ƒ ·"4Ú@"åúïmˆ¦Ö»8ùã‘/ÄKµLѨV$#— F»Ö„£×‡Ÿká5Ë“˜±*AIv¸Ñã¶nWWgÌ‚M#åú-^œÈº×Ít í"sؘFÞÌMÇ¢%@ê=;‰Øýi×+ ¯ÛH¤½—.>.h ”ÛŸ5É­&´•ÈŠ ÚÎ1ŽñqʼnŠJFºTÇB'ø¹Pn¾›QgìÏÉ3AuÐ&|Ž’¬ÁS²>ƒubì@fb._UVÞ)H óf¤aƒ X?Ô~;¿‡úp²¢§-bL[  Žƒ“"©aº£êÿºtÆÉ…¯­aÚë"#åúkMþóE¢I<'û ZŽœ§q¾§=qà·Èk–‘(Üÿ‰‘WÚAed '×ïÚ×–H¿êh¬=²†‘‡ß'an‹%JR®_Äè[ÌuF*#Gl¬‡i/…yñöÖ$áí&W0ãX+2ïW«·n¯‚ܯ>ëRn¿ýwu¨5k,‘¹_Þ±«u„:ÛNbyˆ#G +'åæßè×Þ¶¾Œ¼6c8>Oûówþ?kðT3—æ±qï2rZãóÌïLˆpÏ„Z£<òù³ƒÈy{TIÚ¿3‡šgãcäÝzVpm×…‘ ö xàaz~ZŽ;¤ g7N@ZöFö½—ÑÙ_•¤\ÿ“ÅsÐsZ4'/ENÀÅÙ;„[õ²áyŒ‘oïàk±¹ÐzòÜ›¦'‘YÉf˜Û³š“}/!¹ÇvaBÊ($NõÊõkµµÚžo¡Ápt„‘Ð¥v1ZNèËɹc%8Ëúe!÷tk¥z÷á5gÚ1Rn¿vA{4¶Š“VžcõïÞØƒ6éš9wÈpä7ÊÍ·\»g’‚¬òWb¼:#kðT»vyÀtÌ”ŒŒûçÛù©Pi™ ç:à¤q` ZÌÚ,üÑ~,Œc¤zõ:èÙ ‡xdB£KKN–uŽEOýaó×eÌ#1FøáQS»Ó‘róŽ,AbF]%™¢ÌGdîMNþ§ªú¨lÓvkë€TͳµC±9†“ªù©Ó»Q:ø#Uóúk0Mû'UóŒ¾ÓÍ–‘ª¹\ÿ ‹zb_•'Uó6Qû1c¸ºDªæ& Þ³§JFªæ}gÇaU«ƒŒüÛ~<â02B¤jn}Ðaµõ%R5éÀ^{'(É¿Ý_d¶‡éúlåäÿ×/±æ×jòoç/5 ‚ºm{‰üÛ×ÿ—§þ望ß2‹é;•äÁÞ1µ íɯ¾°ýž« ï³…¹uwFv°ÃvìN8“¡úµ„…‡³q¹Ye>ydæ¬Ï ädø¿qˆ~ßGèraвw åú49·¯œ,i}N]t$2ëEûá|–“ÏÏïb£ÖT’¯í—cíÐŒL/Á/}.ÜU:i9­Av¬Ça\˜ÁH¹~£NÇÆ°^ýççå¾­æÂÕßõ‘rø-#×´tD•©È€ßGX« JFw^‹`m{rûç϶„Ö™Gœt\Ö íG– aØÑB"-®œ`_3b8)7×ÒK¹:‘‘–k¯@Ýi'kð @Õ‘OÌ6!BAö^«†ª2=FV5 ϲ¥B ‹)41HøÞ&f‡(ÉÅEñh…Yœl»q;Lš¼þ^â;E¥ÐqŽÌÕÞ(É)õÐc´/#åúß<ÃÍF¹c^ lÆ™ ¶JcWüÃHsuÄ.ü"ÜÕh/ÌwnZ¶8€âÂÙµvbvr’'.D—à÷B¹~š·`\ žD²aWXëN8Ù}ûr¸Ÿ7¹ýuvØë û„ã[—BFx ‡†qRnÿ 3ì^8”OÖà©vohŒÔé Œìfß÷Šs…zWd* n†</á›ã0©'í´ÒmÛ SúêÂqQ)#'uù?ƒì¦·§3u$rïû«,âÅLNÊõ¹×óèJäIÜí¤&üÙÿ~ÜÐYoSòf0òÌÀ]È,÷áä_÷ß-Ì ÞŠîýãyO;‘O¤ì©<ÙÌv\MV/F±ˆ(_FfŒ<ç»ý…ÙgOÁOYê@é½bYa½iS:}‹Ô@Êí_ÔI _’=9™¬û>ã„Ã/Ÿ†óÛJòÒ—$¼¾|…“róÓÜJÙ«£/y¬ëx¼©m²OU=¬7Vi]`$?;^ß>³…ëÇ!ÂYñ‰àƒë ÕFZâÞ¸çù¤Ë1k87ÿ  ÇÜ݉±: ç.¹Œ'ÇÖ1²Ê­-Zhë‚ô™wµ„rý[–hAzz“I£¿°ŠÍqB­%‘¹#QAn\'}[N®öYˆA3ÄoŽÀÁ¼Õ¹‘ñ¸üÙBøf„ Ü åúi ,båw™Ý»²âu@>òæÈ5ÔàäöU9¨×{«pÞÅ$ïÈedhþaÄi¿É'åö/>QÇ»qÒìXmLÒŽë¿­V Ýõº£Ô;R(7ßÔÿw©—s§3v0²Oõ¬ç}ÄÈ8W7ŒøL˜W’‚^¨ §[¥ÂçjW%9Oˆ661²æ"$¬Êîiv“׸pòúÔlÄ{5“ÈÇK0u '÷/î—÷»K¤\ÿ'v˜ó'N^¸r“UDå*ɲ Ã8t®“‘›#a7K]"¬ì…ÊÖœü:ʳ/ä“ê¥à¬3”““KŒa¿‘‘²¿ô-˜hr²ñD,é-ìÐ& +ó9¹¿ÿ.¬/»-Lº‰ÇÁ±Âؾÿ`äî}B¹ýõÖ¨ãךSJò­š:&´s–lh‰çzoòÉ̶Ö0NÅH¹ùÞÝnc³ќlð- a©Í%ò8UÕljN«ñõÖ3Fªæ£†§À6½'UóÀê Hµò©š×Ê^#Þ©š[-`Îõ©šËõap›¥4>é@ªæÕñÿâk³ú©šh{2R5_¢¹ kWªƒüÛ~÷m|Û]R5w?½ êµrR5¯ÌéŒÉM8ù·ûWQÇj÷Ýù¤jþõÚlŒ«»—‘;?ýðb\β”È¿}ýyªªÖcœÐpÁ/NÖ¯k÷so…·Z*PnÕMxé¸%b’Š•¤ŸÚFÌ  ²eíD\v½ÍÈ®V'`pR¸Ó®oBóÉŠ¦A(ÙÓ dÊÖ2\þžë@Ê~XpuN;1—qïÁ%éæäŽ'±ÑœÌë4¶îÛ…»´8[SÎùú]Ì­o2âß6eš'+Lͧ§ R®ŸÖ£H|ñU“H‹j;6NêY¾?i¤l4 Â<ëúx6¾'{†vB#?FÊíõz#‚’b9ÙPm²ïšïCF¢°Ð%:R¶Pö·BÀ”üfdÝúwqödYƒ_îmë¼Ûœ¬¾¡ƒ$aLsÄ=X•OMî„b«Diëx¡×2²õþ£ÐX2MI69y ·¾<âä‚ÃÁ0:h)‘Þ ;ÑüŒ­0!<¾²‹“rýc'Ÿ…A^–piÛJ¦ Û™tB-ï7JÒló&;crþ÷ LsÑÐÈDrëÏŒ<®^€qÆFœ”ë÷è™;N¾Ô•Èáלq6­‘pﵡPÔ¯ddó§Ëqhõ7ᓱкŽ$ø1ßúÌÊí÷×[ƒ;}â8™ÑdÂÿcIË™°ÿU"Œ 3„×o¡ÜüÎS‹°fì]NÚnÝ•ë,$²OuH‘:ZyÆqòüõ7ìÛ¡—n7äï°eä| Û(ÜW‘‚Å…žœÜ´,Š•ÂÈ‚¨{ÚD"ÏŸÛËz6ÜÎÉ—~¸s·HÞ„#±ßFÊõÿä¥DÔÑ—œtlw­mI¤×fcLH‹w Mº˜àü-+FÖ*)ƺq»…O¯^E®O%9iÅÔ ­ääDKs|{ê*”}+P·@ÇAj{[ Iv¥œ|p%™__1òù©hÌÎ)v¨JÂÎ0Ó|²×Ý(Ô)›ÎI¹ý]ë-Å´?ïždË7ó¾#V¦ýƒåôUYkÌðýÀ=FÊÍÿ\iϧÍ%2ì÷uíÍÉüóo3Rn¾™ÆMVébÊȶÿtÆå]/…5xª.íÏ2¯ÇÑyäΨB°÷‚þÔz!÷iQ+ §¤+Âa?p¨[{N&7°F›¾šJò±þV|Mp©xy3jÕ:6ª‡¥­:(HÏ©óP¯à #åúŸî…òPK‰ÌÚ;Û3» µJ}acÿ™‘Š xÔÝùPØÏ{ö ԕȬÿ”“¹ˆDÚ¹EŒ\³' ?Þ)I¹~úÇr˜šËÐcduô16­‰#5o â rDiþ+vTeÛµ  ÄNÄ¢TP@T`Ÿ66*X¨¨Ø…]`""©´”´€€¢(ìóÂBE@1PA»»û{Þ?ÖùÍìwæ½ÆyÜÿüfîcﵜ5Ü×f„é%éý¾)6u=¦ Å GÈËWŒ”Û?Îk2:måäg× Xs%X˜{yº¤õ%>Ÿ¨%‘ró¯ÝØ‚¶óÔA޹™ŒïFœ‘ÿðT#²JØÖ‹‘ñKo±õÂÎZéø=8]èûò8îß&ì²Â &+Èð)ƒprù\F~žT…–Q‘ r‹æE<¬9ÅÉÏ#â>؉‘¶ Pl¼­$e¿®é‹ˆù†¹á€6\jk ÇÜÍÃñ~QŒÌw8‡^u¹‚l?®Ú.âd3uÔøL´#Ü›¥§8ÙùFܶV åú?v• >r€‘·5j¡½ÛKa¤4ÔnÎÉ7®ñèvqª°¨y24¦}dä…‰— vrý Rnæ°1xµÎŸ“ZG p™ðý€ ˜ôµ·D^wÏn–×Ê>àXƒå«rù©üš•·çäqªª¯Í…/™Ûþ,Fªæ…óN! ñf©šWÀ µ“©š{TD ¿{g‰TÍ-Âw¢dY<'Us¹þïú~a6msR5ô;ߎ9©šG éËs3©šŸxÃæöìÎÈ¿í—ÞA¾¶©š¿~†GÁ»8©šï:šŽv¿šIäßîï3nÞÆIÕüi‹ml¹™ºù×?_³<3ˆâäß~þÿx«ª¡jعö'=ç=gj§b…×`»ãeáÀñtr©ÐrÔ1Ö?c#ׯÍ&„ýǸ¡© È fÙ°;a ô-=Ë^;Žå¤æ +LÑùÆH¹þG÷§Âý±«’\¯ŸŒ®:e‰Caä…¶9~º?–Ø Û±ÀÈ ¯œ<öí5[þ:PŸ} \7 7ÿç·ûz;‰”ëw2v.¬ßÊÉyþñ ®ôª‡,­¾JrȶÆrVA®©L†é”H%ù²NÊÚà¤Üþáob°¹i3‰ÌµÀ¸ÊÖÂG ÊÐ3æ&#}ôncÚT NÊÍ3EÍ[‚¼6,.|aä?|ø¦[ÉNìYÀÉeë.³$ݖ¿¬°ûýaármìj¹Ux}òr<ùÜdìëx¬vUWÜBÞ¤uŒ<ãr«¼·rrjã4DÔ=ÄÈ­×ÓQÕ¡'åú»5JÀôçÍ8Y³)ëZ ^ Á…WVÙ!ó;«Xª)t6ÉgÞÓ‹ìÈš÷YñÇùùck6Ç™“Yí10T¤\¿¸OÞx°-–“ &yÁxIª°SK]<äÂÈõÞè2>L8{Ÿ3*w–sRM½ò, åöŸè¿ÇÛIdÛ&~x£#\vì0Œúj ³»Bo©©Pn¾±åq<Èrfä–6y0 ÊV’ÿðT-W—2ï6ó”äè——Y¡ÎS;ÒúRmd ø¨$z~`K·÷S.ÕùHìÅÈ û9Âú7Q’k3ÑÎLO"ggÍÄA7sáX¯­ÐhÇI³©°D1T(×_;0 î˜ÀɽcÃ0pør¡zT4ûòÑSØ¢ÐzPï̱A«#È¡OwàpGKáË—)¸§÷“‘%¤DŸT’rýb-ÃÁΜœøb:ôÝÒ„cZŒE‚_6#ìaÜéã¼Àf( )^_à‹U%z åö[§ìÄ/}‰¬ßÁ ÃÇw6ð|Ë:­•8é0²”©Í“¤Üü­®àÚf/'ãæxcÛ#ÂxªM}*™òž‚Ü^ýŒÍ #?þþÍòzÍV†¶Æ„š  9™0¹º“E½ö¡~]IXêý“¥'=®ë{Œ½my·€ü³»+²êÿTCÖƒ[=a¤\ÿ‡ÅÁp1ÜÎI»~ÕË_h1³„%|=ÃȬ°NðÐl 2nS.VîÓ.k^¿+ûyïÜú^æäf«þXÕj³P®ß'בhù'†“ mq»Á¡k—­°ð?ÌÈ‚{à9'^8Z½ Fl¾Ø|SšTsRnçðlÑY"ënš‹U‡; uúB÷×=FêLߎ~k”›ŸÔynZFqÒçÁ@dŽþÃS½ãü›M,ÛÊȸԆ0¸q@øî„ Ê·i€<ºjÚwè l²ÞJN®ƒ#“ƒ„SÿÔÆ³œ£Œ\o~‡69dÆ),úÞ“‘U©¸þLS"åúWÖìBÙÀPN¾[½ ­ö <[ŠÑ;»ƒ¼55EÑ…O¾ßÁ³”¤U1D¼æä¼±›ZWAö‹²Û)×oÍXcŒê4““Çô;`Ó:m¡E@FÛÅÈ5û±º›³pEÈ<üün+‘ï|ãYÉ…NÊí_ÖÈc- $²}æL Öj¥"ýg,#oÿÎå )7á0K¤:ù+ÉuÌññBª‚ü‡§jªÑG¦JŒÜZÓ .¿® —™»@ä÷éЊk,Ô]m‹øóÆœ|ñÅA]”¤Áì0üÑýÌȨ¦‡a½i“°÷±oìè›7œldsMïþ€‘rýs6nÄ%)–“«–/ÃÓmqB¾/.««y¢>–6Â'{bñ6ÈB"îŸ Ç&}…f[’~˜‘›cZ”·‚”ë—~»9Îg®W’‰ÇáÈæ òåŸ8\Ö–‘w-1Ñ=]A®it•%l®fä–m» |kRnë @Ëòöééonoµ„ýÆ%¡ÈÞ“¦{0Ü}ŸPnþýVý[¸‰‘£­àæÓÂÿâTU_q%6P÷xÂHռ˜Óh6ù*#UóQµ­pÈ)SAªæ½[Eʳ3¤j¾/ý vv©šËõÏä÷f1œTÍ=jcÇË4%©š÷ýROó4%R5¿Û(“¾?åäßöËÌoŒë9­©šz’„ûo×Û‘ªù0©ïc÷0òo÷¯eˆ}ŸšI¤j³¿/‡sòoç›l݇¸‡¥ŒüÛÏÿoUuû¤=`'®ròLÊô÷¯ZÏ;ŒûÉÂ;gpeÈ+¡»ÝfLmÍ$òxÿ–0™ÕV¸ù[[iÜŠ“u‡JÌåy:#Õ'ïEY>Ââ—Éðí¥« åú7ò,Ç ÇÙwÇMX,œ.|Ó"/“Õ¤Õ‰d\?1„“w"`Î!ᎋ‘¦Œ6´9Î-ÉR’¿;ö„“B¤\¿Š÷)³IÉÉÌ‹‘èðåš0íë@ (>.ô{m5-o¡Ö¼/¬®ÇBFzÚ]õAÊí?Y;žú}%ò~ïÝpg+lñÉ…Ã2f&ÖiåæÏïŽÓ¯'4Aahk[ò>Ì¿´ u~=âdú¬%XåóLØâ¾?Öi"‘> ‹;·ð„~"³J\ÍÉ!+’™fŽ-#M[0T41yý¾™/ЦMŠÆ»N¾°µÅß ¡\ÿû«P¤ çVÂta„’|w³]œl?aŠÿ„ /Xއõ-ÂFýp&³¯Ð´ái¸èÖ¹|Ò5ÜŸÉI¹~q üP÷P5'ÃS½ {£J8»Òźk%uÁ‘”äÎgmq‡‘Òù øèíà¤Üþ®[ñ¦»Dê¶Y ­l[aÜémXþ,Š‘¿#°×Ë[(7F– ºlmÉȬÙc±äÅá?<ÕäqÓ1£ì)'+ÞŒÂÂùûB³°±Dž®‰·sîsr~zs|iVääkKqQ_Øè·„:÷2ÙãG|§¶S’.ΑþŒ¼rË»u)×ޛ볛ÊÉ£á¥ha%¼¡æ…«¢„&F à>zŸ0ÚוÁ‡•d«ÂnèêÐPA¶Y=+Ž[JäWó¶bë%)×ïæô…¸:­Œ“æO&¡,-O˜·§ÆÞ³#[üÏÍ”+Èíüþóïj'‘s”7ØŒóÏ9)·}À\¬¾ÛW"÷›„YÜZè|é~  '9äâØNÊÍoç¿·‚ið2E2„ÿòkUÃgû[œ|Ù3´Š…ÊYx}oa¬U"»ïd¡${tJÀéê ûä(4ÚÁÈ ýYˆ5Jådý=ØÒ°F5÷Üô›+È>Í÷š6)×߬Ç)l{ÊÉùG ²$üéO§íœ|ež+ÔÜß´lùèäìM9(üµË.j¬·( Î2R®_·¡Øã°Ÿ“}ž+pcäFá§Ávˆñ4b$_ìÖÇEhø%UUí&ÎAù³® åöŸ9¥ã-$²àƒ ,³L…#/ïÇ0ç§œôàŒôå$RnþâÊ4ÌÑ_ÍÈѧóaï³Í–ü‡§j¹B•Ÿs9Ù̳9ì5ö õn¤±Ä9CiV}›Õ÷¾)|š\ˆVt”d…×!¸úp²úô±ãÂÕµú¢áƒ)„ViìóµÂ…w 1¢ISrýGìO@ý”vùám0Æïì$|e‚ëÞ”d¿ xZDÙ’–ÑKð1æ!#Wzíƒ{ä+á¯ýÙèÑ.@IZÄÂf×^NÊõ ècÇGœ\wÕŸüR’éçÇÃû“#„»ÁA?X<+}zœê>,DÓzœ”Ûß¶;vVv‘Èͯt¡þ°£ðØò×ÌgçNþŽ?Ìîgë1Rn¾^b.æ´Œádß>Áwµ±DþÃSõêVï?Ï<›9éŸð‰}þi+\>¤=ÊW·y%dvw³Î6Ù‡Á'9éús5ú®/WöĶûã”dí³V˜mwXAƯ8‚„!ÅŒìÐæ0þœ^ÂI¹þ-mÅrû.ùqælðÆzÂÊÙ:È;¡ÏÈßÓ{biýšÐØæ~DXÙå<ÔÞ¿Pžz³ >9“·tE½Õ÷”¤\¿03Ø×ß¡$÷ù™¡pŠií±ÏFÆ22È}º–' O¿ D|y%'«ŒGB=+N(·_±³%ôª[I䟟p»°X݃“Û‚ÄÐØ´´ÊÍ/Ä»6—ÈøÃ—ØƒNþ§ªúzð³Ýþ@Iªæ­\|±þ— HÕüWÁ8¬ÄIÕüÓÚAp¬™ÀHÕüë­h~Œ“ª¹\ÿ´®C¹¾•Dªæ#}Ç¡™ÍFªæ£ŠQ?„“ªy?c|­ÊÈ¿í×(Òg)HÕÜmœ?®‘®0aäß~þÿxëÿú­tÃkí$òY_/<Œ¶î–Î`¾#¿Å\‚ƒO¤‚Ü}¾I¾3òʰ[x”Ñ@XïJ!{<æäø·¾ø´EO"¹U>µ\£ Ííö#±DS"åú?œ4›ýß1ra‹M˜tô𜵠V9~&|› ÇŽ 'Í¿€ßŸ3òµi¶åU(ÜÏGiŒŒ¾p KÛ?RrýªwÜDÅÜ©ŒÌ¨Â°~õäÚ û1év '½Ô 98W˜¡{ ´ÚG!â²¥DÊíoPéãѵ$òTÅV4>©&´rM®¥pÒ¬c{›H¤Üü†Ïq²Vª‚4vMÔc[•ä?|˜77ö’È…U#qtš©ÐL·ñuZsrI‡ó`ºùÂkc®cÜ0álÇ,¼žÝI"m¬áèR[¸%§–ÇÉÓ~g„—&ÿ`5u4@Êõ¯Y±ë–ÔY´, ZeYS†i^º 5ùp˜ô“‘ýmÊáÈÉ•‡²0¶s#‰|}&OgVsr‘ :i7•H¹~9Sn°¤ '?º\°)±Â§¥[áäpXØöÅr^–,4îô“ý@‰Ô½ÉŽ]2e¤Üþ“o7bµÝoNúz`ÎÖoÂÅz0|¢#‘}šû³MÍ8)7fDœµ…“µ¯úýçû¶ðžj­+,Ûª/‘ eºHÔR¨['ëg6>ºŽ‚ÑÝ…EÓ—âz®BØÿ’:t–7ƬnˆVío)Èsïúà„z8#çu=Ž[Û€,Ú{ $sR®ûå‰ð<ñ†‘Z׳ñÄíšÐcî5”~Z%T ©ÂòD%i™¹ýÝô%òz¡ê¿ÔæeD°£ýÛ2ò¬Ç"Œ·P€”ë×mþIìÿý˜“_ !$«±D>m1ê¿÷q2nŒ¤*/a£]£á4¯H-Ã(‰»ÇH¹ýïýàã’œì_gšu|)´ á¬Õ‚‹Œa㌠óÖ åæ×¶ƒçmßs29¦FhïþÃSMõm„A-kKä–#_YçŠÛœ¼¡îù{K¤KD/ K2®qŒd»üí8y88›½œ¼Š‘ cááÝláÉjì%,ùêˆ_µ´%2 ðsÈùmGÊõÏøX€n]39¦GòÔ6S¢ÊÐéW 'ŽBæÏN¹jNCÄ'GqrÃÅ?ÌãM%¹db"ö3òþããÈÚYW"åú ¹‰Ë“;Jd‚þNôü­/lä뀈‘“8yõ¦SfuÚ;â÷%™³0׆pRö”>º }òNV͇“Já’¨lx?l rÚó+XÚN‘ró×…že®uŽ(H÷êÆx°å#ÿá©.®—uÏÎædɆvàîVá³ŸÙÆýê™å‘ÌnÆrrÃVÔÕmrtÏM8}LWèø. óÿ,ddi@Bï4ÖŸ µ.<¥ÇÄ€BÙgíW%ðhÚ‘^‡/ciç©väûüí Í$òÅK$Nî!ñ®ºl·fä׉vHì#œÙ¡âc5$ÒÐã7[£å­ åú7ñŸÛKä·Âþxº¤‘pʧ^8èT£î4oÔ]ø¹Ål¸G$ròiw a‚±Pnù‚ø~þ'ñg.í?#<<ð$nHžÜ­!‘²ßª×áæÙ^ W9ÓYÍ„ÿðT Ÿdêºqòíl%‹º¬$FlcuO,ªí b£Žaäø )¸[ñEx‡¸Ÿ÷Ï€çþäµÃ±¶h'ë=OE¡C[òûÎYo¯úë¢_i'¡Ü|=;ÈÊ®ËȬØrltš“ÿÅ©ª¾†¦²>•Qv¤j~ºÅef1 †‘ªyG‹ô¨¿KIªæÝ7…"³O'Uó’áºHQÂIÕ\®ƒéçð즒“ªù½qìTpgFªæ­êáKš/#ÿ×Ï—ž„Ïäßöëø­6ìÊfqR5·øb —_«©š·wÝž÷>1òo÷gÀý#c8©šëxuÅ™Á¡ òoç»Ü-$òo?ÿ¼UÕGñÐ4kÃI%q°Ï3¾ù:ƒ¿é€<ú|+†­0ê^h ›K9ù¬D gÓdRssËirµÿnø]i&´jUŒUWü9ùhúHX,2—H¹þM çâA}5ÝGlFtÎwFFìÊb&ÎæäÔ5™,fh¶‚œ2¬9N½oÏÉ|ƒ_ìþÍpÙøÕ1¬2àd¸ãj´Z"”ëwDÃZ—º€,m¹w&öv}˜}ã-ÙA& (Èç ~°ö $òåù½,¿¤#åö;9À®“m8ùãH<¾FÂõôq/°)ÈuÆþ¨“ÝN(7?Y×Ïï×åäÖOMð¸ç]ùrÞÆ`Žb$'oèD£á‘yÂØ5ñ8ô¸È΃9b_Ô&¸¶Fþ–BF}ž„¦ÚÚ û¼ÍDÅÚ#Œ\v÷š¤ù(ÈPëlÅô¡«é:œlRönˆÌ ^2rj“½Øxÿ†0Îö=ø3GxÖÈnuê€Üû°1jÙbäî¾ÑüSS¿]ZöádqL¬Ém© åúÙ‡¡µŽ H«q™˜|¦½ðÉǃPä[@>¼™ Á9J²¤™#†7îRz—‹W2Rn¿çŸ8hëÍI˜ÅÁÌÞA˜ù3ÚùY¿=핤ÜüVƘ¸«”‘ c×£Šiü‡§šù0‘K·r²Gp,4 Z´¹ŠÓ¹/ðNÞ‰RËÓ¢`æ¯rHèy¼,eäð阸_›“‹"ðìÄáî¸RD4Ó`ä•Q¨}ÿ>'åúóŠHlªÍù®*#~Æ ë,ZŽVƒ8oÍýÂÈ+Á»ñôvg+ŒŽá»s}ᲆûú0#ßO?€óùÏ„rý®ÅœÄ®×j ¥'—ñ{]#ýô`CAsN> 5Ë~ÂÀ£'áÝ–+IûÀ0Øü”8)·Å€X(ô99Ú*OFÍÎ=—‹_áÔê]ð”,”›ÿrn>ÛÕ™`YŸ¥-ùOuû³}ð’b9¹»~”g3…·ªåÁ„n¯KáYZ)Ô_röjýmÉÏSJquè-NjôôïŸ}B7Ÿ•8 ZØåãLzê/Üàk'mëØ’²_«º&¡YÀFN¸—‚5žÂÏ¡)¨?î°Ì0£g[ ë–_Çñ•«„É©U`Úœ|št×*(Éø =°ñ´”H¹~îUbÔ{FòƒÕPóÏ#GÞÊEœ¬ŒØ¯¾ÂåÊÙ¨•@Ø1¼nŸê-”Ûߢv4¾ä­çdû¤(tNÜ!œti!–‡KÎÇVûUB¹ùöÊ1íZ'«šÄ¡µ‡¹DþÃSm¾ejŸÏçdÃqÞX`N˜r8™5$2'!Ï{ _^ŽÁÓM½„9CFÀý”•ðaü(Ô„pR«³šm±>x6º2òá·(8Ž¿'”ë¯ÎÒ¶ª #ÇlIľ٠ar¾Þ:mGþüÏ?}C©$ï¶8‹HÇÚY†Nå½…“¦1o­dNvj¥·Á åú ¼_§A8Ù-í ¬ê+…›&ø"íy¼ðÔÕ-Øs2WèûÆj956äñù½0Ð2–‘rûkëDb‡?'Sæ„ã‡M˜ðÔ;#7û¦$ÓZµÆóߤìÿ•ߨá] ‰ ©s”Y|Êæä?<Õ›·`[ÌeNÚ®Á¸±7„{7Ïdzy}%²¡)"Wtv1ªa£·?ádÁÀý¬ëÞJ²õ7u´2\ª Õ«ÛÁ{gdÎÌR˜5æä…Ák`c`%‘rý}>¥b¦®‚|\™Çïžùä¢ÞñpaÆIåp Úì.|j: Ç£-%òüüWLÍè3'WIAÚ¯Ö ýZ‡ãìT%)ׯ÷¡Ó°H©'‘î­³ðÀ±“ðaè",Î)ä¤ÅÐ10¬Í…Aÿùæ¿6När ¸Öh=#åöÞŠþOb9™6}bg§ ·öÆÇM·ÙÃv7¼¥– åæ÷½Í öw`äÇ6mÑqf}ÿÅ©ª¾Æ˜ûç+9©šÛùÂ,¾¼ç¤jžŸv—}Í`¤j~QÝדµAªæU'·³ûáéJR5—ëd~268vW’ª¹¡_ üwsR5¿u0Ží®S›“ªy³ž˜³â'ÿ¶_«éá¨t±’HÕ<­y/ü\“ÂIÕÿG¼šÊ½õÜäj´^‘§$sª°{ÊNZÏŠe!CYúí*Ós»#lê‡Ã@NÆLõÂñ'iÂô«À¯Sׄ£õà}ÒJ¸táfe¼B´Î ;ƒ”ë¿ w sýXAv(ŠcõngäùØP¬qÞÂÉÏüPq2Aè6Ü ~f·„£fYbØáŠÕ'ؘ±Â˜Ô&p_v‘²§Rѽã»02-¬Ω¯f$oÂÔ£ÂǼq»y’pø˜S÷ê”ð˘“g4œ“rû/Ö¾„€Y¯ Ȉɗàc~@IÞ FñŸPN¥­†f³R¡Üü›$ئsòkz(~Ô—Èxª­îV`Ç»HNîW» ËÚe´×ÚXç òLḤ÷~ºâ‚é–G9™çÝfǃ„å†[|ÌŽ¬ý¸/ïmÁÈ»ƒ3á¼'K¨' _7×å¤\ÿ„˜Sì5+adÝ9ïY§%õ@.èï…'½NsrS‡)0‹(ê9› ò£’ jÜz¦Æùd}ï0(Z¿fd×å‡Ûu–‚”ëwès/xLMaä¨ð¸<³\hPåu6ÁBÿ˜}xØÍK8£¾?òêÞä¤ÿ®ñ¸²&L(·ÿ^çK8ÙU“w]Ê×ÂVX²È‹ÔŽ ?Ö°?V+I¹ù1éÖX<½³DæE§3Ö1œü‡§=èµùÁÉKÏ ð¥^s‰Ìµ]=í@wÌa(tíñ›m}PI xÏŒŸ»1Òºá X·@èPê†ÚÉÂêÇ~ˆýäÏÉ^ Gù<[(û[kY|}§rÚ^¬`.eÙKFåäÎæêHRº 2­`š© õìàv¥'#3“c`WЋ“1+}°ŠÊþûèŸyºÛh}®r]½H¨7qe¤óÙX,+´öúhƒÉq2>Òéw‡*H¹ý” Ã³%œÜ\÷"Š; ·ÿbõŠùÜÞ“Ó{‚”›ïò$?†‘å“Û‚­ÔùOuªe6¦Ò“ÈÔ• pùf* ¼PM³OŒgWÙë=…ûƒg¦²~€ªüL…öž»0øÎEF.ÑIC³ÏY Ã_±’¤ ÙçÇR„w)×?ôŽ+ædØ‚º%9i6þ,ÞŸ=#´±;Ž×Ž-A.‰ª†÷Ô$)7?$Ï_Z‚Ü^'»‡Œü‡§z´n(^”YJäR»]°ÛG¸µú<ךÛ!å°¼³”“¦äâ¾FK{®ßÄêàeŒÌévç÷^TVÎÂäy'/|­Ä¶f FÓOD°aw‰”ë:$²¬AvLMEP‘‘0zR8~¯%,*vFßgÆÂý]| Xþ‘Á—“qÆã’ðf3OðN UâcóÙŒ”ë7ÆåL“ß1Ò\CB܃SÂE·²qY?¹€tÞqW?hp²õürè·Öed¯h —§Õ•H¹ýŽq'|å6'û¸*QqüƒÐÏ÷<Îîx+ì6q'â§XJ¤Üü®—ààçgGžv̓›k'ÿ‹SU}y½[…¡›{K¤jþ¨}!ê/¼ÅIÕü¾q<ë¹pR5?;9_›v”HÕ<`à]Öùq'Us¹þí|Ž#"²9HÕüÄÕ}˜ãnR5_·ø^©»3R5O\œ Ÿè¶ù·ýŠ£/""j #Uóµk³PÛ`;'UóßPÐÕZ"ÿvMhâëJ¤jnª–ÖV“È¿ÿ¬N ºNyÁÉ¿ý|/ñª#wÕk¯DŸÁ£Yްx=aÑéBÔŠ{ÄÉe³Óð%¯£DÆÏÊdsê32»¬lâGtÏdö=•¶ä$Mø^,dä>Ç%h:Þä¾UøÚ`#åúï/Œi¾œô 5m·õ¦¾aÃ/0r”ºN7ª²Øeæ=³7#·mo„yß…Ç{&AûÖ ¹tN >{¢$åúmßó/ïãdõ–pÿÀ„Ež=á™+\’¯+ž‹„ßû}JadÖØ@¸6±)ׯ2Qwf)È =ôŒ¨ÅÈMÓL°Îw²‚ìºÞI÷n ›µ^ØÉ99ë5ø{·îõ€u Ív÷Â,÷™J²ïƒnØq¬ #åöWº…FãpÒBQÃAÂê¡•N3²ÛÓ °±<ÎI¹ùÓÕËñÕ¥ˆ‘†Ua˹Jòžê ÿ$;®U’ƒV%¢™A²ðÊ›,¦8ÅÈŽµÐ T d‚„hLçäŸ~ûÐpÕ!aSÍX¯KºœØ†yöŸ…Æï7áÚõ6ynÉ~Ö”ÍjR5·¼ñ5/ØÊIÕ|éªÃ,mÁTFªæ©Yøéþˆ“ª¹\ÿÊèVøQíÌÈÿõë{Ìi¶ª¼.½m÷Oû2R5¯u;®õw2òoûui»Z=0R5ïÖÉóûÜa¤jÞöZs$õk'‘»?o%‡K]u‰TÍSƒû#úã Fþí|k}]h6þ® ÿöóñǪ¨zzhñþ3#‚Ìð¦KC Æ{Á5ÏHXdƒÕû: §\MÆÁm„¾q—ááœÂÈ%GúÚ d å)Ô{}—‘Ë_OÂ#åONÚè§³ºÎ ¤\ïóXûž“zõOàüÊ–ÙôL2[wžÛâëUÖBÏÌLÌsÍæ¤ÚÌ`Ø” 7„Ú½f 'Ž9‚ãg: åúm9| ).œ´XV„v 7×;‡#Š-Jò·gŽ3gNn=ÍŠš0ò³û@Tn°)·¿¡ÛYÌ0t`dîdzÐjÓNèªLeÆ»'7Y`‹NŸ`¤Üü_k;Â}æ9]Ù¶/•Œü‡eà× mƒ8#ìŽÐfA>ê…×.XZ-íFNŸ{¾ŽEJráù³0õ«%‘ {_‚ëŒÉvd†qt,pr˜Ó<œzn røù›a7Œ‘rýÏY¤"vž¡D& ÁüæÂ9­º¢ØE[¸Xÿ%3¿zœ“ö1ëQSÄ…v%3°÷J„p·ßUlh×G`¸5%$R®ŸïŠÃxýªƒDfLÜ–¡=…e_2ð‰Årò§C4ú%œ&ç¨x¦ r€ÆUt]Ú˜“rûSÕ‹Pxà–‚¼sÚ3½„v¡Ž°wêÒE# ]éåæ/Ý»a¬Èß2±ð›†ðžêš‘ËàúºÈÆÛaãÐ]¨1ã?'xô¦‚lýí6\ësò}0ï-‘‡óûàÜ›.´µÁ(yu›“ë.FÂ¥Ðwã¤Ïi$‘6j=ðE/ž“rý;lCPSK‰œ5Ú Ú‰=„›•²ùKs•äT··Ìx.#‹gŒÅË‹ë8¹ôw?„«ŽXfŠk Ÿï´À^»Ÿv¤\¿m_¶ãÌ2+‰ôZ6 ÓCzïVøãL±ÄI­mà' 7uÇ¢iõ$R9}ôæpRn¿ù¹3°WdÙ‘ ÛN#à{Hy±ú*6®1`ä•ñ§PhÉI¹ùõÖVÀi(cäÇÏ×áËÉxª»!>Ù¤ûûhÔb$¬y!¥œ,ó΃uÝ6¹}Ù=vBë'³S.²›‹ýìÈÓ¹c1¤h'ý“û£þ5{¡®¿º:[(È_ÛûciÕ6FÊõ|ÓvÅú)ýì‚/~M„§ÂuVy€‘…k†#wò9áìQ&H‰hÈÉ[š­áywU>YôÄCÖ3²¬zoj”ýZsÌ áö$²[L+œŠW¾å«0Q2'?kÍǘ¡aÂvÍqíøm%yÕË ­šöVrû«jNÂ[}®’4z$Á³¤@xÝd/ÒK¿pòA—©hg{N({j®I³ï!‘u8`™«…ðžê¯­ѵ–.È'nÇ`Ø¢…°®u¾™KdÅ‘ñ²6¨Õ†áŒ4p çÔ«Bóó&Xi{KI†?o ƒV¾ rçÏ}èuå#‹Î_Ãì•JR®ÿØ”&¸’ò’ Wþ`3gæ _¯]Ž’ÌÓŒœpÌŸö]ÖáyŽÂ&‹Œ1¬eÐ)ñ2l=ô…§7åàÒ€Ž)ׯdðwö€)9Ùd^ ›[<[81j:õNXáÛ>Û„m­¬1pà^F¶žº·²êƒ”Û?9R‰²ýjœ<Ð8ûë O÷¦ ‡=µ†D™’”›?ðÑ5Ö¹’“ƒõ$ÖvCŠ‚ü‡§šbw K&Öy!ï"ŽN©ddʱ>X“«+‘£"»ñ;Nz6ñ‚õƳŒtp @ƒ+ÂÍvzð:Ÿ!üü~ʵ[‚™°ͳì$òë°pvµGNÊõ_©õ„e _ÉÉϵk˜ý÷ßJ²ÙbüÄȲÆ!x¼r³0Om>æ=lrŒf,~Öï(ßMãÌšKäØÞ…lr÷ Œ”ëwqvû˜¯$›=cÛÎNUc|Ã)e%'ÖX¡çM'!³½ƒ¢HFN=\õèBNÊíwœ‡¼¾ Nª9æBsÙDaâð®¸\°ÏŽŒZßmú®f¤Üü‰Å0sT #gì[Œé_ ÿ‹SU}]÷¿ ½NéŒTÍû¨?`š:{8©šŸÍ CÃß‹©šOLįo­AªæÃN½kR5—ëïi÷ˆ¹ž]Y@ªæG›FC¿½+#UóìçPóê#UóÆõ=a0±!È¿íw»ª6ü÷š0R5/.1€kP[NªæÇ×®ÅþL"ÿz¿eÔî-æ¤j~ùÄD¸Vù·ó, †cv.#ÿöóñǪTíGlØ£4F:·¨·õ%¾>Ýð²õN¾»ù•jrZ8½t7†4Ù'\= ЇŠ÷Œ¨‚Ljތ<O9ù<Úu}ys_ò ”ý­q022!% ÷ÊÒ„•)sÑ÷Ø/ay•&¾­NìÓþCÊ89z©ÄÖûvn›6ñ1…1™†˜´ÊYIÊõK<> ® ~2òödO4¹¦ r¹a=hõyÂÈ9÷,Ñʵ1ÈïׇàÆîÇœûôûÊ/*I¹ýºcý¡;ˆ‘ÏçàD}á:›ë¬Ìê¥ðãÏÀð¾ åæ7\¼ÍBY‘#õ½Âø »¬5œÓž0òpýn(Ôý),ž{”)²‡p²Ð&‚mJj¯ .Ì…ýŠ#œ|Ø» M3„§L’`¦ÕM"C"ÆbfŸ®B¥å ¬•ÂÉúÓmPïKg‰”ë_ä ×/ Œìü-sº„ ûÿÃX³4ášA1zk‘°gQ:Ó.. WkÇk‚ToÐNÏ~)ÈoŽN½¶”‘rýŒzBï~c@‹}õ…E5s°dp+a¯Ú>äÒ@ø-MG;6ê‹e}z åöYˆ¡A»ybkº¯ò–zÅå¥ =— «Sœ‚”›ÿ`\.öê[3òÛx¾¹«$ÿá©æ÷Q@ýa]isÆ íœúÂÏ¿ÒYÎ¥@F†ô{ÃV®2­V]¯ºƒ”ëÇ+ ›oed/¿ãÓp˜0÷HLnø$àòÝ*é×z ÜÜm@6‡C¯Í…1ó³þóuí#¥Üë8¹ˆ“rýç‚?zÇȶ/Ï`Zc.ì‰x§§Â ÷“`Þñ°õÏ[x]õGAÖhçbpò;NÊíߌ¼ŒíŒŒHÛ‹‰=¶ K²s±ÄÆ“úõ‚[- åæ»9ù½œœ[î…‡ßÍ$òžªWÙXYTä­€Õ¸9R]è¿ÌQÅÆÂ_=¡ØE!^¤Žt—猬>4áÍ!l`€uIkÙÿæ8\ªŒ®Üq !¬DA–<‰Bö‚Ýœ”ëo¹¦ƒj}U‘!gQ³ø¥éS˜Šyv#ä»S‡Ð¹‡¹’Ìow±)9¦ÏMu>¤ Û-Ù€ê‘L" Ë`Š…c8)×O{`9nV.adé¡›¨È÷W³{¤cuÝyŒÜQžïUµ…NgàÙ“oœœÚ¿Ú åö-߇kÏ<¹¢a(ØþÂÅWcÛÒœŒº< Oýý„ró?pM|oÙ\"KÔv±Õëqòžêî±ÛPÛ¹H|ÙîÉ™ó&×YƒTtÎ…Bj#0 ?ËM…gŒÊÒí#ߺ­@›B£êmó‹æDlÁÚcñœŒ(ï„‹û…rýï ?ŸS£”díÖç±<¡?'§üÉÆÏéLè¤syÑyB³ö%ÈwÙ&Œ7ËÀǯï„êo»àô©Œ\Ý&/? åúõè^‰îmî*Éö×Ñî^0'Ç–¦Á±áHÕ%®Rf>y8ÐOK¿‡ešâĆÛ_jÜÖ2ÒþB8jo^)\µ¿/Î;®çä°ÿ/+„ZC‘;¤žD¾¾³š·ÞÉÝ…aãê ?®š‚â]Uœ|q*»=Ú2rå¨,6n­$åú]Ð(Fûô§œôP‡û¢6™0, ®gû)ÉôÉhÚ)Wh:ËêNù¾n6ÌvýÊíŸ36g¬—1òì·(ØoY(ì5¤ކÝň‘ú åæ×NŽÄÞ~­A&N?èAŒü/NUõµãPÒ~œ`¤j1é ~¥$Uóa‡ÃP¥#‘ª¹–Á9„­}V@ªæwÖUâÖ(]Nªærý?¹ÆéÓõ$R5Oõ2Aø—f©šwù8ß æ¤jî–ƒG?qòoûÕ%ÀÜÄL"Uó‚S‰ס'Uó¼äJÄl9 $ÿv¢÷Ýæ2R5ï÷í f®Òù·ó~σڡ€òo?ÿ¬š]㊨%±œìkê ÝqBƒ¨ÃÈÏc¤_Ô)œnk-\nμʇq²Áàlæöy&#ï¯9ƒÑ-&pò¥ßA,øÓD"}½ÎâCrîvÀŽÈB¹þ¶}Ûâú¬f ?Üí ­/…í~Ç!sO+NiÆâjõ¡Ç“xè¿o’Ù`i\:#/Ú8£d`ˆpdÛì¨y*”ë×¼²–NMRsŒ°»«&#ùãtœÔE"wçù!è¡•P2€Õ(oNÚ?ß…Gf‘B¹ýqû» ýQ#»uƒºI®ðóÓÍѹ¡¾vÚtÊÍ7=žשׂ¤Ûë œÞ‚“ÿðàæGœÐŠç佃1»]¬pE gøÚ’†]K±¡Ÿ#'£v›­2Ó~jÆ6^ÙáŽDOK‰<3ò«·§–0>×Qß;+ɯ_,ÑðE FÊõ4-«;|¹b’&Í¢À»sÒ¤2%í/ ;ê"ê‘¿‚¬~m<Ô8ÙÜé ÂÚ;(È\Ý$,;g"‘rýšìê…Û™¦Œú@C« j3þóLÜS"7}4…f-¡nÜdtêQÂÉCÏß°£Ã…rûuGÕø³ŒŒ]Ô 9½¯ Ó6*‘g\ ¼¢Yˆ€F· H¹ùÇïÇîÛ£8™Ñ8£ïG ÿá©Ün Ÿ‘œ\û»'v< Î8 û‘ׄkCáç‡6yÓ?Fö·9¹8 S?m.¼½U³R’¶]*Ùà¤ëŒ´Æ| ø)ô©W c»¡\ÿì{0]»È‚Œø[7¹Í“¿qò±—x^-‰ÌÞ€&;gpržm¾YnÎüú‚õÐþ!¬½ê>»ïÂH¹~ßn BÏqŒ¼?š£Ü„Mjà}¿*N¦´zÆF·\+\µò1»0»Œ‘O{í‚­»H¹ý3Žõ…UìCF>¿eåÔÂ=§’ñeÁJNVÖư´=B¹ù¥›¶àñ”Çœœäm…Cßÿá©NêÔYëwp²fª6¢Œk ÆŒ K‰ë2sm„?û'c`H‚4Ýš„Z“5…µpû§ÈÞ3#‘Ÿ¬!Ü.åÁ0M["½~ÖÃ̺µ…rý7{&bŽÎGF>ÌÀ¬ÅÂSa©•“U•Jö|¨Ð¹C šŽô¦ÅîBYXªðm¼曼adëkû1Q×U(×ïrGÔ¯¿ž‘ß>9áѤ=ÂK³§Ö*É~KÕàcV¨ ûÿ¼ƒHF&ö8‹âºJNÊí*w€fŒÈû¡c ¡!œÖÅaû99²Ó|‡ÊÍÏZs™)3Ü9Ùÿ×AÖù—+#ÿá©–viŒã_ÀÉòµQâÔPØ=´ üÖIä‘ë÷YžÕNºÌJƒ¶›µ’â™ï9¹¾2Ûíddê,,Õ{gG:|«‡×Ýv22Ík3~Êõÿñ6ÁšÑŒ\Ÿ”‹ùnnÂÓß÷³—%—äæ˜Rö­ÎUFô[‰EÃK9é»y޾¸&4ÉOCp‡¹dØT¶Šä¤\?ŸŒÉ˜ÇȉïgcsFžðåm- y7VØÌÏ Åw ‡x¢Ÿ©†D¾xÜ™VÞœ”ÛoÔi2®\jòÎœÙøt·¹pwæ¾ågdrrz‹bæ½þL)7ÿ骎PäèìrÓkòm…ÿðT—œûȲÆD*ÉIOXìåoùäË{qÌaýNN2³@¶tÞØ²øÜlxWÂIã„í(=Ó\"syÖ”å*ÉQoã°Í_Ÿ“ÚCB°%Þ‡‘Sop¤·Wrýµ›…ýœŒ|–|f+ƒd³KÐýiaÓWãÎ…°ágMĦGq²•Éi¦]á¤$kž ã.é:Ú› ÍV’rýò?z`æ¢KŒœ¾ “ø=áéEHÓÖš1í7FµM¬ 48 $^í„§/ü¤ÜþðqK1P_ ¤éõðÚÔVX1ë ÓÖzÂȰËqÒÞ¤ÜüŠÔ³ðø†‘&U×°ÔÞÂŽü/NUõõæÌ]æzg—‚TÍóc³˜²óFªæIÚh©Ö\"Uóâ1ј0ÅŸ“ªyŸàh<Ô‘HÕ\®ÿÌÏÙXÚk¨©š»%âv/ªù‰ÝEìŒe0#UómAæ¨kdòoûyt „VëŒTÍË®¸#ã](#Us³^Ö¸øù:#ÿv¿ûë­èÿ°=HÕ|âVŽ´MAþíüYóóÑ=8—“ûù¿øcUÿs·k*‘YurP¼JOXqÀjå 9麺-VmõW’9#:cí& FVÜ7ƈI©Âr'?\ùîÏÉ3vÛävHøãR*&õþ®g‡G‘Z)×?áë2¼àUœìÜn:Œ•„5ƒ:#\=H¸jÂoÖdTGá®îipµ=mKjÎÌ‚ïÜ[JÒ\ï4V.,S[÷g£lH3‰”ëwhÔIT÷m ‘½30Õµ‹°‘– ê…v¹÷‘ö;ٷΙ ËOÖÂð]xv¢š‘²ÏòͰ•wÙìd*3™ç%<x𥠼(|bÔ~ƒ”›ßüa9þëvUsÛþ?J’(¤’T% Iû|›ež3“yž3‡Q„J%*JE‰Úç§ ¡’$Éxq™3eæï»þ÷qÞkí߃ϲîö“׃÷ÚÇñ^÷}\íÏÞëºÊÕ99þc"JžV kðàå›c¸nb'‘f÷B°.·›ÐÚº>:6o¦ ÕÒÄmýŒt×€Ðéõ@ºÅ!r¤‰p¬úT¸í)áä´È3LÞ¸¼‡u-±`¤IÔVhu)û ;d Þ5~ÂÉA‹íñ.«\˜šq“ùóV£bž°­‰ Œ¬Xw÷9Y;!™_~ 7ië![ÍX"Íß$°.êC)ׯV“`¤.ûóµô?îÌÙ «ý]…sŒüqæ¼Ⱥ•'°º—¥Ð­§„!Ï.)É»c ÷ç¤Üþ…ÍN²ô—áŒ,Ö•˜aÚáÝÎ1ØðSäŒegÑ4º##åæ7³WVUsróûQ˜è{\Xƒ§º»/Ô›)$²éúõèðã¿zøëbnv#ükƒ}ýjƒô¸u“^˜ÈÉyÓ èc!¬ÁS ~5Ió$ÒĽ/ wØ ;‡Ú–-A6ùíŸ3섟Žß„îŸ/M¤¢o"×·‘ȳiðñ~gU[£ðÔ¡£ð/z¿Œæäð×6xQ§X(×ÿ‘ô%¸s2¬ÿyöp«–0þK8NýùëL~»þCë ×ÏdUóã9yÙv;óZç¨ ë­ŠAŸN¾'±óú NÊõ‹±×ö8¹,ê+‘,\˜Q†Š³oœÉa³¯C#‹qÒÝð›up‹°÷¡"ÖíY"#åö³²wìimuÔ‘cÝPØëmª­áä¼£{ÑðÓ ¡Ü|­ªHè§$ö¨‡åmYƒ§šŸßCBÛHäΦ­g(|³0[Í[ƒd¿r1?@C¸+j>ºZtÈs qïÙN7-Æ™´Fî¼r;c*•äÜY,íÎnF „CJwrý=¦0=–-\žÈnÞ¨Vf•Áaó*FÆxÝÁجD%¹ìu[ZÊÈÞìÑf¯Èaw·âé3‰œåË®ÛÇI¹~ûÞ?c›Ë‡rÒ±ø9Ó ðQ’S¯ä¢uß N:÷O¨ÊW™m×ãBš¤Q®Éc¤Üþ& t2ÖdÿÆ&¼¬¥ð逘?H]"Ó–Þc=GGpRn~÷.Öhqµš‘ó?zÁúm;5xªå‹jãé ‰¼¸ò–[ÉIûê[ˆT;ÊH§ë•¸T±MA9ü‘åÜjËÉ:t0P:­ GoLÃÖ¼›œ4Y僬Ü×ÂñûKàæÐ3›´töǦkÿrR®¿m~: °óbäÞ×ר¯ð¡ÑL¸ÛepR5_ 3Us|8©šËõ/ï÷•q­Ú Uóïq>è:£¾Dªæc\$4ÑZÏHÕ|æ§3ȉkÙƒüÛ~oú›beñŒTÍïÇ,€Ó•rNªæö…càftˆ“»ÍÔþPóêR5 5W;¿Þ…"|êr‰“ûþ¿ø±j¨¯ë²/Ùäœ,¼òKT’Ùqè{´$›\Ö"«kÏP’{xãzZW'Ɯ‘Ü:¹—½påK+‰|²Îß'>社‰ËKm%Ò;T…—R9)×Cu>Ö^8ÎIs í*„뺺#ÉÕä{‹ÌÙRÈ·ùãç玹¢a_'Ù —N‹ABG a¡….Œj,”ëWd芹ó9&g–Ä>ÞHJDfø=Nþô?õ¶º9mm4,¼wsròZO\íØP"åö3 `W*É_õÙ¼>~Ù¤Ö·Dö œ\1;ŒÍ-ŸÉH¹ùW•`ÞÏ Œ<pæ×Ws²Ì&ÆÎ0uNÖ9œ†…›¬…“ûÇ â`²’Ü¡‡¹õ8yñ~nÌÈðÓÕ^iJr“OsŒ5íÍI«kexe“™^ÖXeÉH—·ÃPï—P®ÿ´ Tx{ýâä¡^Qhè_O"·/ÍEÈ㟌ôZz–.žÓzÀðëÏíü5n?;Þ¹Žð›u(›Q:‡‘ƯFßv-AÊõËþµ­]Ô@vòÛƒs1u…‘áÛÐÒR"ŸH£°,ÝJØ¿Ã#Öbj>'_kD²r@FÊíŸÌ4?›+ÈÄãáÌÖ/Whø©=®m0ºl–¯i(”›ï£–Šõ«ZJä¾+s°3«£°O•$a÷¥¾œœkGÅáqXé8QȃZQB—Æ 0m³L˜Uë N×ñf§vFŸ"W¹ÆÊí‚0²ÐÛ±Þ¿…Ó4Ëè:]AÊõ^çJHd›$o\Ó̬Ğߗ•¤ëò")¼ÆÉ%WO²ú¾Ë™ïkëÄO¾ýS`}a°Y£XUûf“rý m"`¢S¤Á–pßý•‘[g4Çȃ9&ã6ÓŸÿˆ“y­gƒíí²cæY|J)f¤Üþœ¶1Ìj‚!#û¾Jañ[ =¬ãÉÌ@¡›O^x‡)H¹ùÍ¢t0ïϧ—0·šï P’5xª—ÏFÃþ÷NÚ^?‚'+– /¬Ǹ-•Bÿ { ßªžD¦·Ù“œTËŠØá©köHÓˆc¤ÝÑÙxñFäñ+¡ˆÜU"=ÛV²òÐ#œ”ë?>k ÚÎU—Èçf¡Ê¯Š“!Ë“qÛÑD"‡ÖÛ‰©6Bÿn˜v¬>ÈUïöÀó÷+Fö™•ˆGáC8ÙòÜ6|x®/‘rý2ª$¼PÌÈçÒUè .ïÂ\Ú,åäî?_ }LôœÉUõÎâO€’T7ÀÑnœ”ÛoS dü¶#ÝB YЦÂίc°¬VaÙ·M*þ©V’²_«Fš¢S#+_õA-­¹Â<ÕGîáØßΓsO„bUñja|«%˜sÌP"oßèŽi+õ…Í,VáÉÉ;œŒ²q„•ëCá…_‘ÚÜä©—ÅX“pŒ‘®EVÈóÛ/œÙd)ì: åú»Á²Ì[œ|ÿi N™žv¸7#ïéKä¤[f(ª“on†Á=9š‘}†AŒåX¡vÔiæ67’“±1oÙ·WµAʞʘÛ8ô¸>#ßÌ»‹÷Ó)I§îal·ýfFŽº^ÉŒ2Ÿ ÷ÆbͶœ<Ôb)t-?åöÏky¥\K`¤iÇ·¬•3꬈CYT'“6mŸÝ\"åæŸõ Í1[Yä4g¥SÂ<Õ…!U{'ÝóƒÙËCX1J?¶~¾lr–%¨nOzÎ Í÷Ó^ci}¤Æ „)8ÙþYŒV4’H¢ èz^e俯7P¼9!›”ëÿÝ \†„s²``4`›ðyˆº%lyÌ“»(ÉøUGaЄ‘.Ä¢“æ[iýí0ìR A*¬³`Ò§ƒ‚”ëwÍù꼚ÄÉœäbØí¾( O4C­‰† ͯºâÕê6ÂÌÙÏÙ©„HNú[§³«b)·ÿ|¿ZX{‘<êÃàG¥ðc€6ÞO¨#‘‘¥;Ø@ÃUJRn¾QÐ.Æ;FÚ«„z÷ráÿpªª/WˆyéÉIÕÜ"p7›á§$Us·.™OùKFªæ 7lÁàã$R5Ï~¹ ¦8I¤j.×_Gƒ!äòTNªæã«¬PÕgœ‚TÍõëŸÄ¿¯:“ªù»£¨˜kÇÉ¿íg2àòCµ$R5ߣ¿{·©š'û-Ç6¥3ȿݿ¼…†µ{ÅHÕ|jÓJÓwŒüÛù¥ûJ°Éù±‚üÛ÷ÿÅU—ó{`E—$F:eõÂùˆ  Þ3µ•_œÈu`“£ÁȯG·ÃrÂ!aIû}xÖ!Y¨t‰ÝÊÉ…›Ž#j»©D®±NC×µvœœ—ýMB¹þÅOúa›z2#‡¯Ï ÝRصqâdƒŸyÐøNÈC²˜{ÕPá¿–6쉂§¹>¿8™óø kåp[(×/åüOö!u'Û^ùÌŠ›› m\Ñ%e#'-˜ˆ%· =‘Ãî†HÂEIk¡•ؤÜþ¢]†rò('{ökŠ”ä½Â•ùÇñòEC‰´3›‚y» åæ›:„ ÑOF~ßÌc‹„5øPÚ`ΛV0rÞ“1H‹y/f‰NGýW?'\œ¼Rèv'¡öÉÂЩçPk˜‹°EÀ øœì!‘µ®¿dU¡¿8ºm5¾d}ž»”ÆpD!”}VZ: W^÷9ÉÊ1Çb Yƒ§zez¼6Y<à(Žšé {Ïšƒo#‰à?ð‘0t~Óþ\ÊÉõV)ìã =%ygònä3Òý®?Œ6ìÎx¼ [¾å乘 ¶ÂB¹þ~}Ï lÌ'FšuÏûóõ¨@xûSS˜·ß*üèî |Tدé1DOâÂÓßÙ"4¸ÏvZÌçä6E]Ì«nR®Ÿm|_¼l3ˆ‘¯|€Ívf¥—à‘°Â©‰µ÷+ÉWN³ì}éŒ<¾wç[‚”Û_Ñ ÌC•¤Éúæ0?9\8)ò¼*º0rxâY¬=Ù\AÊþ£8¿.ú·(dä©úS1òÐa žêh§ãH8ÞäÝ(öTj8ù!èðWFfÔŽÂäÚ_„w"~0›Æ»…§ н*ì1ô–a¿Ð󳄨V … ZÁG³ ÈÙ'”йþ˜‘rý½c©Y#×ç–áÄ$[á…~PzX°qnØ cF_‡ç•YäëŽWñ~h '+b±X§È=¾g‘ÒNÏ™”ëgRè=õ%ŒµxŒ-|„®Moá…E('ëö¾ˆzWjIdÑ$¬Ý–ÀȪ°Tk­ åö¶0…^ʯlòŸæÐkî&4õ’0)(‚“câÄ"+‰”›¯øì‹×Q9¶ ów ÿ‡SU}Ý{ƒ“^2R5_Ôð J¥›ŒTÍÇX,Eaý\Fªæã§À»åNªæÿŸÂÌÍ=9©šËõ÷{ZQÛ>8“ª¹·á2<}ÊHÕÜtî ®i)‘ªyôå#8÷Ɔ“ÛoÌña°îÍHÕ7‡æ¾vŒ”ë¿þ|.p«ZA>.½Ï’ʦ3rïø–6>@ØaJSlÚóQhUäŠÑï® ƒ· ue]ö’axÊPø¾äе-”¤\?DÞÅVýÖœÜT^‚—„6ÍÀ’vA NÓ 7z·p«{oÄw”ÈžXeIGNÊí¯Ó`'–ù„p¡Ÿ7ŠŽ„ O»/€ŸB"Sæ?cAÿ¾á¤ÜünÁÑÀ§'JÒúj4¬8Yƒ§ªw®˜•6½ÎÈW>2ÏŸ„!/Ãÿ|°çq²žÖ>채¡û–@?ïÐQÓóvDýÌ.³Ê5'„# v±Mo+Èò±C¥±‚‘ÿÄ{ÂuªH¹þ¿ÕFÁ¡ƒŒÔ21¹‚ á#Ѩu³ȇ“¼à¾ä#GùEÃʸ>Ⱦ}.£ÅyFNxñ«9©V€¬ßÇ„rý4;_FyÏ/œ´9›mƹÏa\¥=œDòÌ ìÛ¼.N•3ÒðèYóN(ûµGÍ­6†s2~Âô58*Ôp?Í.×óu&¥KÍ0úÓqFÊÍ¿õ! EVqœ4ñßÓÚYƒ§:<¾!ª×Ôi»É'}õ…¡ßw`mY'ÍnEæÇ¡Ÿ‘X–»FøA»/N¤®–c‘YuAö¸¾F»:÷—Ü„‹óRFjv /×·H¹þ_ž·‡î®ËŒÜ—×-K® uÞùcFÁá«Ì¸éù _Yÿ5 rýÐR,ô:ÂɹP™¡!‘;üÙñFϳI¹~y¿bä›öyä±/ŒšvÞœçˆ"wNÖ›eŠ==…{ß@bš#{*Ã'dµPnÔ¾í°›rŒ“FaÛÀ ‰EÓ¹IÉÈä_Ðii˜Pnþ‹îЙo(‘í†f²˜Ÿqœ¬ÁSýbÓ“‹šƒ\÷y$vö1¶KÝ ÷‡8ygû:Œú-l3¡%L'nÒùÈŒºj(ÉïZ1ÐjZäW§a:1#G†chüKN®îbŽ^ù[¤\ÿŒ•cqx]#'6ËeyÂqWâ{ÜBᩘ0ä'M.œ¶óšI¤–ñ.tÊé$Ôpï ~¥„}Ò±càsFÊõ³=¹}4;Jdå¸þøÒÌ\xßKe܆“>w+˜‘û÷lrÿϼJÚ¥ ­:)‘[öŠ“rûO­ô¶°œl¤·“Ó…º«·ã´a2#5/ƹêËBÙOµ5>lüs}FÜÓB_Û ÿ‡SU}-ë<=›˜TÍŸýùŸ8»(ƒ“ªyœÃcuׇ‘ªù¡Ð¨½]ïLªæ‡»áŸë©šËõ7éµs³©š?Üp 3¸+#Us\ì Cw‰TÍVœÂ² ”äßö«Øi…mYu%R5ß™YÀÜ3 ©šï½?7—:Iäßî¿»Àæ²8©šŸOÍÂ%Óù·óísÖBã´È¿}ÿ_üXU}t>6þþÅI‹§à×õ—Âó{Ž¢ ÒX"~ðÆêÜN»­}q±Ø¤Ù™x<,¾ËH É cÛ‚llßÖÂðåÑÕó'WêÙ¯¯åúϹ µy_º ߯CïäÔlÒwl[d•¡$£<ƒ™ó¹Œlg‡m?ìä×Ö-a•¯ÎÈ Éaüç›8ÙÞj+\¨ÊõËyÕ6ëê1R™cƒVWÚ Õ ö°Þ®„Õ­sØû ÂvmÒ±bn3á«´<ìvÜÃI¹ý:ÝN£båF™·à :n°ªç7ÁªþŒ|"†Öž¡Üüñ·, k>‰“†¿`ާâ³É|Ø7ÝœosrÕµ¡˜<õœðÚ”1Ðí ‘ÞÆ0-4VÝKCâ0FJëà`¼SAšÝ/Á¸F¦–•!í©'ýÎDZmC–+ÈûžSðoH¹þÿ>:ŽŠàfJÒÌ0k¿ /y>e½j©\¼¨¦Mh'ÜzÉ ×1R÷µ3Ôgh€<¾væ ØÏÉ[ïÛà¬Ã!¡\?‚®ð«×‘–ù ÙÍ„o^Ôƒ_‚6È)F ÔÚm.¼_î“ÄnùlÛkv¬‰šPn¿ÿ¦,Œwñt&OÏV¢c’"›Ìx¹1­0²E½5¸‘xF(7ÿÜ §ì¨S*#;te(q±Yƒ§:Öf Š‘ÌÉI ûÃöÜ!¡ý¿·Ø¢¼WBÓˆp¦·^˜ƒ÷ËÉÀ7ÇÑñàÆlònìi,ñ”“Ž_w£EZù9ŸûÙØ5;ƒ‡sR®¿õ?ii®ÆÉ³O¡Ö¢Þ°^[QѤGH$®6Žêé‰ÈÙÖBÛ”D„÷7We³0óŒ|Øk*Z+@ÊõÛáۥΌú¶7&õtÎÑ]ŽÍ­@öú龡‰ðih.‹:§ÎÈÈW}°1ªP(·w e·Û*ÉÄ 9˜ì,ÔwƱ¯3ñ>žŽÊÍ_W~]›w¹Â² †ßdd žêO»~˜äËɵûÁh¾‡ð~çfnT;›l^?MVsedïâtÔ[iÁÉÑwRÀ:_žJƒž_Ï -~MD'÷ÝÂêo¡á·U¸tËB8/j ‘rý½vžÂžÁ;9ÙË, ~ƒO ëmHÇà°sŒ4:ƒãO…š6ÅXŸ™/ÔßR‰Ý:f òBQ>ò¼3²_v:–gºsR®ßü3½þz#³zöB§ÇÞBý®‡‘òñpD—8d¯8%ôùº%8"œñzÖæžÊío埋ó*•ä©Áç`û¯>'3¬Ê8áµ’¼×$ A›J¤Üü›ž70ïaD6Y|/ád žªr~?X'Ïå¤óó~8òn¼ðõÙ/lÅT‰‘—Ô¬°@z+Ü4' XK‰li>gÚ·>;9­r}8ù©s;TÎß%üõÚŸ¥ÌÖú9t­C– åú'W‡ñÚûœKŸÙBÃu+q½´‹Dþ›¡‹§GqRnþåÁ¨í¡ää8oŒ=,¬ÁS}§ÝÇ’†s2hN?\|1Hå7M/~g¤_’;~y)¼‘u‡}[ÅIu6ãt¼’ts{Å®™_ÞU¿Ê¶·öbdjÓèï›+ l‡BëãΤ\Ÿ—‘ÜÜP"CïF•Fkáíô¾¿^IbI,ßI:¶÷Eô$òɯixÒ÷%'?¤4DrûÂ=Õ¬³ÃFÊõ‹_1 ûT1r_ë(uÕùaB †$ZAªæ¾I{4>¨$ÿvf7 ¿4NrR57öì[þù·óqÓ ã®ßääß¾ÿ/~¬zd‰Ë%›i±<sÇxOÙãË­‹ ²]´ ’Â1òIé@è?Ú&ÌÞ0—í6 綨b]g÷êìtÀ¦OÇ„7ÖâÎÀÂT?8Ú/”ëo³ïëx¹È'qÆQh.L×SãM÷9k¬=|/Z‚ =¬‹Š¯í”dã-Õlb·æŒ|¿¼>×ÿ(,KJÁ§ crýVlºƒ±GäËê»Øàª$KK#áùÿmÛT[‘Žoi…ýF¢¯ß'%ÙN‡sc­$Rn¿AídÜŠÔÙG:‰QEu„Ç2n£w¾ 'wt=‰y>æ)7¿ED(–Õ²—ÈLט±ÑTXƒ‡'$âÑ®•Œ<’‘‚ÔÛ3„SÑÃîÌZC³_ª0òòd݈®KZ„úO^ ×ß„¦!—'gÂÿb€°ÛóSXß'P8è@!æ Îá¤\ÿ·áNÈÝ d·)(Ÿç$t_î£: á €c(H´¶ý·.Š–—2RóPOnlÒ5¤öÊb%Y9%ÕOJ¤\¿OëÊp៭œ¼ßèœOÝ6év õ‡íeä Q%°ûôBAš¼´@Ù ‰ôü™ÃfÅV’rû­&ŸkúoF~ºÌ±;ø°òÅLŒ°ë,‘ßic·Ë5NÊÍß×OFÆrroЧ¶)É€8§Âi;”0 ÉÈÉ[–B×õ’pü—ýØÖ\ d“Ò¡Ø«è,‘†þa)gJR®?6xâXxæ)ûQФ‹ðë¬ èÜï#>ÝÀ0ÓiÂýCwÃÖÇ ¤q—LhÎi&œújrÖÇpÒ×à շåú™®ÈCN}‰4z”„ñÝ-…ŸÝÁE–’l¶³7ÖEp²íÀ68çÇHk>š½…rûõªÏcÓ¨‡ŒìSrg~ 5^jÂùøD%ù.¹Ƥ_RróCËíP¨ÐVr: äæla žªža.N¿VÍÓ.¢ªßzá¢N;p¶‰5ÈÒQ4ôj-ܾª&fŒd wp¥“''Gjf£q—rF¾w/‡‘kº‚ÔÕÁHË–ý¡[7Y(ûX?í(¦¯µ9[™ ÃM„Ÿ_CÉJÒÒP„uk89bÈMÜÞÆHm[°oQGI®÷{Íd‡ébÀv]rý¶59ˆåó:IäŠ^h›i/ì·ó"†,¯%¼6,j·N‰šK'%#[ý<„özZ åösNžeäê×1+,Z˜\·òë¾Sîí`“²Ž‘róOšþyØêÂÈS?lSë¾°OUÆxî”EV=(@_íP%¹ha66÷oò÷âëèP|Ž‘jóÏàjX#‰Üí䃧›Ú ëÜ„Ûé3œLµ»h-‘çô¢á]«)ÈÓö•0¨S©$åú¿é| š~aäô…9xQû¼páóx¸8Æs’/ A×Ñ Âú³r1¾g€ðžï1lë}E˜˜ˆ9­A¶¾_· VÙ¤\¿´n8ôÔZ"={í5†÷GÑünÂã"ïZgá'ÏÛøü£/#5g]Aúû·œ”ýÔÔº‡»Ù·U2fÍž75Ô©½)…Q­…ró¬\…µV 7اÁ¹¯‰°O5à]!ª-8i<³ â– ïUUÀ mKF¦«ßCåÈ%é"õGƒéZit´5v·Šà¤Í®i°õ²”È©“MQm~…“ï­÷ `lK‰¼¡ßï“·sR®ÿ… ˜°›‘Sõ/"ͶƒpLý½Èq“­=w#M=@Øã^$ÙJÂ:¾ÛáYrT8Òã0–LÝ+t›ˆƒm…rýî kóçÁê 'Í \#GèVâ„€Ù-$Ò4Þ3·à¤ZóÕp:g%‘s M‘i|ˆ“rûuO”cÖn{FV«ÀŒIŸdF«"X4»ÁÈ`—{hú<_IÊÍ¿=ñÖ/ÞÊȵJñáöhNþ§ªúŠn’¢8©š7¹èŒ`Nªæ—Õl 3º!'Uó[Í ?J›“ªù¾>‘1D‡“ª¹\ÿ©­ÎãÛ·©šGÔó…±[,'UóÁn^ÈL ã¤j§6¦ßëJäßö¢®MoNªæ ëàø»#œTÍM¦wÀ«•äßîo¨V‰»v(HÕ|üÉL_Q“È¿?wj bÎþääß¾ÿ/~¬ª¸õ‰™×zÇȱõpЭ>È5O® ¤EsFÞ_ynêúÙd¤ioŒ÷ÙÇI§wM±³ÿFaï²(tÄ8Ờ£¨kpY8U¯yƒî)ÈE]áÐIm‰”ëïvhJõ¯r20iª×¤ £ßCÑýZ 7ÝËf&Â+oëá½ÏHNFÔ-d=õl²È®gô0ÄDK"ji sñ$)×oè k|é7œ‘ãb»âgì8á ¾#qxÈyNþÚ9Cÿë£" \§$c4Óà2ÐT"åö—í›…yÖÎ ŸœX'¯ˆºál·±ŒÜs /V~*”›¿ùk(†:y1Ò:6cî,ÖàÀ¦YÍa]ïÏÿuÿ±ÑûzÜV¸ôÅè ý¬$?tHƩ쮜\±í:ë¨9FI~ô>Çb»îe¤ÓÆè2ÊH"55Û¡ùææÂ‹Ï&b¶c"'ãÎÂî¡!B¹þ†3Á?ì 'uBaj?oaEè1T_hrqøEl»ÿŽ‘«O^b_‡…; h§KMAªY24®°bdƺnë“ ”ëg>¬'š³ÑŒœù¥/~ùŽÙ0oÏmædEëaøÑc±Ð)¥km%2`݆Aöœ”ýÚ¼ÚúKÈZþÞˆHP‹î÷ľűŒ¬œ½f“€”›o²?÷®aäÆMç‘–^¢ÖÜ©"­?Æ,w¹A}ÎŽì!ÔÍŠÆØ°~œr'†Aø¡JW›üÆÝ±&W!šÈ4œc8Ù÷Ñv ÛOÙboûÑÅLIš6w€õ0;rý§| eÌjNvm9™ó N•ÃmÉtFž©]‰ ‡“”ä²6³0g–3È=ã«aá©sþ˜ai!|°¿âÏ3«=#åúyŽï‡¸£C™°ELr›*ôþ1O·ºs2gТãá´Éæ1Ï–‘cÏ÷úÓ¥»Pn¿ö!?|‰î2ÍóÜf: ×ÖJÄüù¸ eˆ7ÞÊH¹ùÊÜ«¨>¹’“Ó/¤ÁfXs‰¬ÁSÙy-6õg ëžß‰]B—˜û‹qrà¹#øi,Ê4ùÿì·h„N/›€TÍxÄ ·ë8ù·ûG˜(¡ûF¤j~Uý'‹1jÀÉ¿´¸'lNû2òoßÿ?VZ¿fç—prý¤{l¨ÞqaZÔ)¸ío ‘ÝfF `«°½S2.Içä©Í¡x´Q_"+º"½%È[ÿ†w`áí¼>0™“&w}aß@"åúO;·u=ê€|‘·ãöÕ&uÝßCáŒ|š{OÓÓ…z=zÁ¾W 'ÇÕÃÇñéÂYïÂÐ/ú#=ÖG ¶Ž‡P®Ÿ_ä ¸…Ú0²Øâ&w,t& öa—^ø12gä–w°J8F-¯ç oœ¼‰Ú«+I¹ýKJž°a'n1R·ó6áË?ÂUü æ÷KZ¯½‡¥o+I¹ùA'ú@-û'ÛÅ?g³†& kðàŽG kÕÒ‹“^n°’>]…%î[¡?W!‘³Ö ‚PW¡ÆÛ˜Ó¡£pÍoMLj$ÌÝwó|ÊùùSþ]XË™œÊ/0ë¹…œ©þ/ëÝó&#åú_ŠË: ·§$"²–pÚÔXØ–)YýL ‡ªƒÂi©¬ñ/]N.Ï:È:×qb¤ûÞËh˜è¯ £eâåé&)×ï‹9ÌbërrÂŒR˜…N65Â©Äæ ]¢£Á[áõ˜Tøë´’Hç=ñ&´Pn¿Ë@M¼qüÈÈwõàÐQ 䋎wO´%2£rvwl#”›¯Ým/»0í•3é:û&óÜðœ‘5xª;—² Æ·”äuûlÙ }a uCô¨6–ÈhíØÑ÷99`C‹Zá!Üñ6š}ŸªÎÈ/s`»4’“ZoÃ1ãç5áû;à÷ð1#íÅ È¡\ÿgÏ`šÃ7F® ¸ˆM6·…7_Gó.ߤ‰Qú<ÐáäˆÃO؃y¿Ùº5ßjÒ.ÝKgYJdtÓjæb¸KIÊõKˆ)Äúž9¹ÀãÚw]‰Ô¬Þ„©×l@^í~Œ„svþfU[ûsòÔmk蜯 åö_ImŽxû: ·'µÁùVõ„þ=ðÝü!'Õ´:cüXW¡Üü×·£Þ{[m_A££°Ou~§×,çZŽ3Ù´Ç/öd|ˆ‚ôÓ>Ï,ë­â¤iëËìøþJrC˜LO\`äì̓ ^뉰qÖ.x/9ÎÉfÓ½¡½õ ÐR- UGƒéõó*B÷\å¤\ÿ¹#ŠáÚ2Ž‘¿6•aºáTáë‹W±»I 'GèÄ‘ƒæ)MÚcn 2¼OšyÔÞSvAydµ‚ÌÞæ©s)×oé—Dè´k#‘_ß#b{'¡ÍÂhî¨f¤åq(¢Ó…þú¶L Q¦Û›C¿~#åöïIéuu›Ã±òN¡•ë'™q26 %Æ&6ÊÍïpï8Fç&0²îÀxT­4Öà©ÖÂÖ¤W òýµÆhºÆ€‘zÕ/ÙØ› ÒÑTº©C9ÁÞß–KÂ_µ6á¢O˜0m&¤çqrŠýXtüõCæŠɶ¹üDä ü©$åúgm¼‡˜‡gdûÇ•øæØNI¶}ë‡Ä±Ý$Ò¦õ8¼ûÕV8ðáI\ dä„;' ñþ—‚4lµº[€ô[voÞc¤\¿Ö×¼p,ª“DFÆÏÀ›YVÂkC’ๆ‘žý0%­µ0>Ïe :‚lÙ>¡ ß0Rn¿ÃøXs¢!H󼑈Ýÿ_çt}ÆŠ—õScðù¶|ÌH¹ùÉ-¢ñA÷©‚üÙóüç kðTß4Á¼¶Œœ¦hƒî)NÂ.ž6x`±V¨µ“a úý®¶Žƒ„:#|Qá{G˜ýU¯osòÖ:fù!KI²Å°,rŽ3Ù+QÕCC)×ÿ„wu;srÀ®R|¹uPè2ݱfµ$² ¶!Ê.âäÂë±0j¿JAö©‹{Å+„­Ã 0¦K0';uñÅ1E…P®ß¦g}0-__"g»´G“á_99«} Lö~VÅ?¢°nù?†]obÙGcN:yAçR¡Üþ3E“°{oç'͆kV}a8Ÿ†Só„¥~ɨ½«¥Pnþ§‹¹¶j«’ ŠÊAvîNþ§ªú:jÍZ½©š÷ÉwAdÊFªæWrbñcãsFªæ“Û%³×Ñ'©šJÞ G]Aªærý^EÎ¥œTÍóüM=Nªæ÷V¦ÀdG=ùÿ,ðóÀè 0.üË~÷šáðÒ œTÍï&Czû*©š_+ÚÌ9ÂÉ¿Ý7a ö¶­R5ïõ¨ÿYÎÈ¿_]/ )©$òoßÿ?V¥÷‹×ÎéœÜu'§‡?ꈳ‹¦(ɧ:íëRáªÊX [ü„‘£ï\†{òaaèDÀ¨eg‰ì=¿œmiXÆÉÙÏ#Qád#‘¡¥ßÙÊ/ß8)×Abë°$“^AQ¬2zž°âv:FîmÏÈaÍ’‘°[A>­í‡çÍNsòVäV|ÿ(ÜÝ"%ÕÛ„úU¾ØÜí³P®_íµïØæ«/¤"¤>FMíÁȧ~pÑÙ#|v-6®aÂÉU{àÔ÷pƤýØÒæ„Pn?Ž}dŸ×7â䲆_Ù«ûJÒsH |^§)‘–U( ìÂI¹ù?/`êŠjFn\‰öm.+È|˜>êöŒwåäÛÓ˜á4Lè§´EÑÁ-Jòí‰ÐëÒ^8ó×]¨YEŸ%³}o@ýŽ’“)3sYÅ;{%9j²æ<ÁHÖB7¼µ…Öê@íqí…rý{ûF°Œ5·”äŽ71¬*¢3>"ßœÛ*H×y±Ðí5D¸×`&¦5ÈÔmZô“)š¸Ù¯¶DÎ `=uc¤\¿mïLai1ƒ‘=ëtDÙê%Âé-ŽâG¯8aar2tFG “6dáþ«8¡ÃŠ;(žlÊI¹ý§NýùzlDIN8U³j ~Œ´€Þ¡–ÎäËâµV(7?ò`1ì¢8Yw[,úkÔ•È<Õµµ"°Ày'7D hLá¹+õ¡—›ãL6KýÁܲ22t_2y™Iäº_[0j…•0¡¤œ®x0räkì7Ÿ(ì¯Ü‹{#|«‘‡F åúk·Ëb:}>*Hÿò2vxÍXFžzæO›(ȱËÏbY¯¾JrV›cÌfø6Nžm¸¥øTAz£±Ä¦!ÈžÃöâÂ’FÊõyÁ;‡Ïcä„}±·z‚0ÔŠcF-oá¸Ö…H7n&t:Ú>ÖYQÖ jO åöõÐĿۯd“±!Úðœ^+‹4SAÿS”‚ü8¦!&–¤3Rnþ·­È±ÿÈIí¼ùÈ¿›(¬ÁSmU%ÝûrrÕ÷È8XXÐã+KÈb¤oB}” z-t{Õ/‡þæ¤aG Ý,ô¨VUø1rLëá¨üùNz´øhÆÉž³ýQ¸ªR(×ÿh­ŸL³¹# mÃuaŒðßw9Hª3‰“;–ea®Ó]á‘ÊžÚÑ„róÏ÷^„KùÁœL0„®A\Xƒ§ÚzòQ4^;“÷ND"D±ThqÙ̬È!'ƒytmëŽ[u»q2&œ¡xZ'!¿º >S[‚|¾â,vÞh <Û ëïEpÒí ƒ›ò›P®¿mak;ngprñ­ß¬¸‡RIšu½ 7‰ŒÌˆ.Åçȱœ‘µ“EoçÎd´n_迲)ן¹ @‡Æ§io5í& [=ïM ‰t°Ð@£aõ…‹¤ Ì­ÇÈÏÏñ±Ûvá~S¤ÆnमvW´’”JR®_NçøžÃÈç]{"àÇU¡~“chàn'‘ó—úàý«nÂÛùËòɤ­Q.ÜÖf¤ÜþÀ€?oíV]C,aZ~PØjL2\ æd—²`¬ÐñÊÍ÷™Î¼^ÄÈÄY azÐäÿpªª¯æ™Q8 ‰“ª¹ÆÏlœ³Õ©š/XZÈêkÉHÕÜËIL8ù–“ªù•1Gñ2;‘ª¹\/ÛIØ9ú#UóõiØÎ—¡œTÍ=Píüçå?ªæ·ÔÆÎô0gòoû½ðéçgFªæ~צÃvDªæí.Âñzu6ù·ûcY#gE¤‚TÍ|FãË;8ù·óoÞ[ƒk!@þíûÿâǪÒÀþhõ5‘‘Vý]qIöún‡_ílAš™…êŽÂ]ÓëÀúÓ(™ðe bdXX¢¯·™j{7·'3òû08Ú¶—ÈÓ7íP°žsR®¿ži*´¼[*É铎cÌ— Bïñ¡½å'#k»Þ„Í¡ÅÍÃp4€“9åGp{š£°ð€=Îå¯U’¥†Hy{+[(ÓÏÜäFEä1rçþDؼ(Ìï|Zþ$òAD$z¼µ:ŒØÏ–òdUa…ì¤×Œ”Û¯6jÆ›eqò_wägŸêLï‹ToFîOˆœü¹B¹ù «X[‰´+9Ì.@›“5ø0¾ùÌžw‹‘u'Ž‚ö½—ÂÇwãNx'‡ý#‘oßLèÖÛõ›—}_‚VC…ÖraýÛDA>úóAÓ½0[Iºêtää§9ïX9OU’rý?æÄÁ¸Þt%¹8#³ü Ëô¯C­¿÷ràspyçÈɶ™1¸»³ðP‹c¸\q]xà.ÿÈÈVnÁRÑ ¤\¿èQ™ÐNgä⪠Â…ûÎøÀrm‰Œ²1žFB…ÉZô®däóñk¡H‰ÊíZºƒt“8ÙÍq%ú:Æ _¼ï…w¯2Ù§áR¤W5)7¿ãðL{àXF¶­°ƒZ@‚°OÕÜf¾hÔi0%Óë Ç=KAçñ¯é]‘‡9G…Û:ÆÀ{-HiÿEœ×)eäŒaÇpÏ-FI¯LÂûR¡¾Gh—6©{(îuŒ…rý,ŠAÕØ¶JrÔíXxVM®ž@iÆN¼;‚F#þ둇þŸb ‘ãûM‡ö¨öµUW0Ú¢/#LÄ·u8)×/ñö5lÖ‘—vÝÆ‡Z»dï¯Ãà0BM"—¹£6_á¤V€?–ÿ`d\L!ì–% åökÍ\‰w£#9W¾ï×F 1ÈÙäÙ/¥È×9ÄH¹ù™ac¼ÜŸ‘ ÷†¶Óa žêýõî(^¢ÒhôVìk.ü}8•½éU7‘ûo)È5›óѳÓcáÛVYˆ|”§$'®LÇø¬NžO:†f#õ$2¹2=í§(Èi·âq¤ÇÙ¤\ÿÏ© pûØFI¶÷JFaà¡+Åëó9ù{eŽýž.l«lŒíÕF©m‘ÆŽ Nç乕 8øUCX<:öYê)×oÍÍrØëQ’[¿–á{«EœÌèöçèsˆpüÕž˜±d¹°ÌýêDy µÃ"ðË»¹DÊíß¼Ó¯Oâd³±«0²y¨Ð¯ÿmðµ­…½¯¥Âïv¥Pnþ7i$ò]dä¸*ämm²OuÉ_Ì7õÿ±v§Q5ïïÿøC¦„$I2%I2Eí×3™çdæ˜ ™g’$¤¹TšhPIhVí÷»’JÈP¢$cf!S¦üÏ­ßõú¬µ¿7Þ«uÚw7žk_×Óq•÷ÞwN_è­ÑåNLŒAÑ g¹åO$vdä^lƒý£tòe»pÌwjÂMm½ß‹†‰ä›ˆÖ8©¥Å=Ä•Úïm°}ΤòÛИȽyñø¿¯w¸ëv8Á¹(ˆ+5ÿRq,‚Ûilò©žçâÙ€§š9/nã{€ôHÁ…b®F£3Ø4:TFšÿ‰Ã€’SÜMñ1ðßU S‡ò¤ îäÓñìSw;îÔ7Ù=åg2²Yy»¸ÈH gù#ó¨5#¥ú/ëp »ZLÈŒ·—0d¾·¶·쯩ˆä:×-°‰ìÉ5.ÒAeÁeF5Ÿ‚¼W¸=Ï÷ƒŽZ×&Û]fïçJõ3k›‚Ç]µEÒòI8nïÃM=n‰…ý÷äüf&¸ÖÒ™;Rø¶?Ç]9ü+ 3äJíæ´–A¹öÑNÄ7 å¶ëëŒLµdî`‡hòö/WjþëÚ xø’“¦vÙ8¶ºX ÿé*¾Æ¾JÄçï ó–­°A2R1O³ÇÖ¯ÍER1︿\ÔÜ©˜1„&Þó©˜KõŸ», Ɔ驘Oœi‚ÊŸ=DR17ø¾µb8#óÅÁ‹àºë7#ëÛïÛoÔ.1IÅ<Ñ«*Ù ¤bß>‡Š¾ÆÈúîW©Ú‰©§R1¿W«O£Yßù`™ðG ëûþz|YÕäiw†´ɵ†mð$¦÷ITB.®gäèéAØ)8s‹^xc¾¦Ÿ@¶>ê…“Áw¹ì7¡ÝXS‘\>¬bF5å^+AÂüÍyõ›;–å?áJõÏ£ƒ²åy¾›26„‚ÛúÛA8?iò\Bòn¶çªta}?€‘7Y:,ŒoÊÈ‹º³0"¢È£ŽH¿ÂH©~Å«4Pr¼³HvÅg¶MþW ¿~ÿ»p÷¶šýãâ¹qÎ t¶9÷äáHDÝüÅ•ÚÐâ ´?f’ ñg;â÷ªæ!ø-ˆ`dÍ4oX±'\©ù%ÿÿÿVyÈýý²p|A5#ð`XB-ßå»@îҸϚ*çÞðŽÀõ!>Œ4=vq+<¹÷¯ìEâÈv"¹.t0¼ºis6nFlHÏž¸þÚSNæe;ãÚíP&áÏË–")Õ“ùkVü©¹@6 .bJÏ·ÈI½âKˆ¿¤ ²n÷$Öžg¤Ú—s°¨Ò1'_‰‚ÇtîÓχ1ìÉ#FíËƒ×æ|®T?½7Ù) A ¿½Øï÷›¹‹V2Tk ÜÄ= ¹&“›ÖkFþ5É-]R˜]ú9”Ú?8 JK3É}91PS™Ã}›Ž‡Kixð:<Η‘Ró»¼Èù€fŒꘌ-þGådžê…à ³ò’@ºNŽ`A“¼¹O½³ðûÏ*Fvü~åÁOd$›\ÌTÞT ¤ÚFvîd¥œ\>m0ÂÏâ*éÝäÓÜEÌ…Åí¼Ã-o¬‹2£ %ÿU˜‘Á†ü1'ÿyr‘]oß’‘³]+ñ©w’ŒÜcPޝ#RkÅYlû¢$#‡.IC‹y72ÉÚÇ÷p±ÀU ËGz"}µ†HJõ+]’ÍfŽ+““~³0­:3²ÎR V… d3µ+lܤ©Ü~{±‡ñ¦Œ´™dYoo®Ô~·žg6|M&©Ÿôïg e7îPãbXº' ä˜1‘°žÑY$¥ækŠÂÏ1ròÐÞXÔ$;qðTÅ^,aü <ÚÃ…m[øMN®yqJÑ«¸­ïÂ}ƒ@¾*=ÇÖ*o`䕭긻ñ1WëôW6Ùy•Œ´Xÿ†]zÊȦ1.PžÉíã‚3§®ä@×$V¥nÇȸ旙￿qÈ«“óñ8êª@®ª‰Cw¦$’'¦ç@<6A zÈáqà)÷yã%xµ:Š;4r n­OåJõ»?¸†]ŽÎ“‘iÃZA¥uF&3Õ`=9©™íÃ4Æh32õæ8:mà-œ×Òj®Ôþ7¶ñ‹º”I:Ê.À¥ÛîSù&Ì×B$‡µœ u›X”šÿnB*.Æ[ äÞ–‰xôôýÿl¸S}?Ô‰]6l"'/Œ9̯”‘_ï!3ï‰@n~ ³ûh‰äç9Óq}ãFÞ ÛŠâÄ nù²þHªî rβ£XgoÎg“ ¥6rF¦«ÃÐ5E ¥ú;‡W²cKÞ1RY© nY¶YÐØ6 þ dòÁƒX’\Æõ%í D²‡ÿnÌ3e\ë«ÕLE+Z £E²œ ÁŒ”ê÷xy|×ÉÈߌз|÷ŸγWÎsÏ™¿e=ƒ4ïyw–põ ½kúsðT}ºz²JG5FFXb:ë—qÝÕOAyW‘?ø0ÔÏ âª;oÇÌÜFžn³ êW.pßq=ˆÇ­†‹d»ÏF¸õ¬9WjþJ-c\l?T eǬ÷æ6à©n¾sQßÛˆä÷ìÓpxÙŽ;¸ã"¬úV mga42¹‰‘w‘4Ù—¤—‰¡ïÔEò¥R Yº0rjåelëeÁíwã [ŸI2½¡¸S"#¥úïsN@HD‘œ}".¸éܱc¤oÄmûp}±rèz®ÿpììÄuUë‚ìoE2)á34ŽÈI©~~—ݰHOÎÈ`æ‹a#oqçœðBm÷G7?¼ Hã®™«cû‰¤îסØå¥Ï•Ú? ™/Të¾127ÓÖš¿¸¹Úè[½C of‚Ñ“ÏrRj¾Q¹!~Ÿ+¾?1û¡M¸ xª ›‚Õ¶É!O=ñ~C[îë9øÔ=P ¿~[ˆg]¹%3½QôÜX$÷íXˆgƒ´¸k{–âÎÇ9y…¨r#î¯Õ5n#ÓÖÀÿÑPRým•°:\S$í£•§:÷…Öîã,'Õ<ï¢Î=B òOáé½0Fö|‚ÁWNr»š`¢ýRÙhu¤ßy•êת0>-2rLF,9VrÔøÀb—?÷C’'Œ¼q§\P‚ìé /õÚÁ™¤Ôþ‚” è¾ÿÃÈoÃÐqá_®yÒ dçôÈϦðîT''%ÿû_JdŸ¿÷dä÷Ÿ™íde xª~nGÑ&VE$ƒMp_G™ûcÝ2DoòÈÈ„EðNŽåÎè5wWqCË͑ĸ=G‚Ÿ±HÞT±î^\çËXrú:#kõSðlç'9)Õ?;ü Ò¦©ˆ¤CîN|˜\'mÆeãúŽ")„£e—^Üý¯o U?Fjì.C¯µ—夭o4骲ã›3ÈÔ7äJ~ìûž ý_·é uN> Ü+ÛÝÑhœwàS4ÎŒâjé÷‚òîÅr²ÛœWÌgî@FJíf 1Ù£"Õ.¸ë]E6áWO®‰YÌî¦ Rj¾W…5 þý¨ï« =¸ÿáT_=þîEsïZTÌ{®œ†Fó. ¤b>#l"f:.HÅ\vѱ'¤bžq&£ï$ËIÅ\ªþýuø¢Z%Šù€„CíÐS$ó^±· Ô¨@ ó¹y8§ÄÈúö›| Fþ©˜_w?íëF*æ²{¡pOõÝo{ >í~2R1W+´…`2d}ç{ތDŽ¡¹Œ¬ïûëñeÕªŒ}øu  Ȩ#ΘRÖ›6n+¤ dô‚ðÊm¢ø¦"y´Ùu¶Íi³@ƪB3Ü‘KnTÀgc+9Ye7Söº d…OóißANJþTç?aóÚù3ò[weüîÀÍô¯e™µ¹ÁÖ½Qd¨R§£3ü¼2²ñåÝpíWË]1Î µs.2ÒðÍaÌKHçJõ»¿. #v˜ä²1Øïp;³±76%œadïjwÜXíÍ]ÑnôtDrn{@94D ¥ö«®m í-Ëypv#Œ78ÀÝöï¿.Ö9MÒEó"|Õfq¥æ/·øÁ<º¦ ä‰EžlÞËh9Ù€çOºâE°*È ;OXÎl̼|/FÔÈþEh«Ï}ò#[Ò-ÒŒ|¾« ^=¾'#£µo!ùu¡@šåÇ é]'‘ôªÒÄ„ÛA>SóGaI#¥ú›þꈚ¨lFÆÿ1„õä|î¨S+°ZÇäÀ^ýc7¨ñDÈf´É©*`Á¹Ž©sî#GV¢M±»œ”ê÷ñ{:†ôHÈf—SðdÎ;nÏ ®(ðñfd‹uÇP³-’›g;+ dÜj] ðLäJíŸõC ž¯<ÙlY#tÎñ1‡ì¾äÕ½G`¢6T$¥æÛ´Œ`[Oú3ryGÄ^k ²Ou›Ž/^µþÀH§C'àèPÆj¿GŸ ä´IëSÃMz<;ÄÊÈyúBmÍxî-Gä¶j'’¥¶K0õöul¼ÕÏÿ80ÒNíT§Ê¹RýU˜aëÂ\F*yO@Te:÷øÝX\ë¬rWÿTÌ–ÏÈ(û³ÌxE˜@®ýêÄ–íÍÍ$·=‹uª-Erçè=0 ÌH©~!‹âñj"ù¸Åi,[£Ï}ÒÉKʲ¹n©? ˹W¿†°îzå¤GrvkÃFJîjŒ¤!Ɍܗ  ËUùÜ)ë{£MMo‘ŒÜq›ÉüϤÔüeÇVC>S äç»GpiK1#ðTõ®úùùF6o€n›ýئ"™¼¿⌚q¯'ªÁãcŒ¬[øï§¿Œ\¬jƒÁ¢¯@6Q߀?-Ïqul+P.Wâ:„ Ä¿›HJõÿ°}îO;ÇÈO£ç!lS×þw ¦\_νõ)Qc¿ÊHçÙÒŸ¹vu+\8XÆ=ß× —% ä>}3lÎUI©~^hßq¨HÞøà€Œ%2nÛÓQ°SÅÈ7R±cï}®§-Ò¶wY¶ÍN³)µß<¶9<’ʹçp+<ó–›ñ¬š9¤7#ŽÑGiÜc)5ÏIèld¤F7'\K¿ÈmÀSÚàœªÓŒükr §ý¸ãW5‚Žfµ@Þ׳…5ç¹É£z`]„:HKµÕˆëfÆmëd…Ž«rÍŽÖ¸Ûô·àü|ì »Ä½0mòmR¹Rý÷[€ÇuŒœryo?ÌMÚ…ÑçseäŸÈßЈ‘S~ŒDôŠWÜ+Ý=—+®ve›6÷È­†°mî]FJõkã²µãÍDR»©N=êÏu-/@\»óŒ´»¿Ìäžv=}?n­_(ÒΔs¥ö?›¨yéF¶¯é€Ñ§›ƒÌJ7Áê³d¤Ö^ Ô­ºÃ•š¿ä†>ÿ÷áïÿ·'z®Ÿ¹ xª¯=}ñO7Fv¼î ¯ô#Ü[)îÌnï@|QyU?ˆ3'çî Áõ½& o¶“ÃÉô#µ§„²þÚS2«ø8Û 2€‘b§̺s–@VE1]KFJõßømdä©ïVøµú07bèY¸MÒåzl¸„eU®üÊZ¬êq’{!x)Toœàíd‹M¿r¹»‡/ƒMÏH®T?µ¾]°ÂBG$O™7Ãõõ¹AÅ᢮.#˜>ÄÉör¯ÛÈõtbdÀÏ{øfî.RûÃwê ^I äˆâž(}ß‘»mV3dÌLgdûe† ¥æonrÆ®{ybÞCtmÝR ÿé*¾ªu½0Óô#ó;I'Y•ñlF*æç2s±Ôj1#óƒe?Ø¢Ž5ŒTÌón¯Dèß– s©þJ]'##Ý‹q^ë\ ¦õœTÌsõbðÊÈú¾¿_V}‹0¹ýûò¯ú@8Æ.çÊ›}›4D²@½?î«và6]ì›MA^)=œÑ-¹aÁöØhuJ »÷Ý—1•ÜŠ 2íêÏuûqª¹Rý·D£Î —@Ο‰C}Ûs϶ÖG›6Œ4ß8ïææ*û£`ß‘Lr†fþY;V ß-ÐFkçDF^j´ v )Õﮞ ›e´BN.Ùuˆ!#‹'!aâ=F¾|›‹ý¯S¹¥ã7¡]Èwî±ãûPTĕڱy LÚùD ‡¼©c•ûîs#ºàÜñb®¿Þa¼¬ñçJ>à$­À猬¾¹‹/”sð`kÇ¡h­b%š!-}ôÿüÔÓ>7IËUWXЇ"lvì&ô‰ŒÔ>\‰1LFjÅY¢"¼…Hª=¯fÁÛª²_/dZ»qV¹ ÷©ö")ÕßúS8 ƒ~ÉIÝÝè“SÂõøhûùi 1¥ªq_=‡mÓöä—‘§°Íz:·³ñe°ïy¥W&ÔÎtÌ$¥úùåù0£=ó9>:‰ÝßË-šZŒ•vÜ©;*pàN²Œ´-߆„ÐûŒ\wá4Ž¥h€”Úï?´š9Þ¹!mzU2uûlnåj7Œþ“Á­Z·^_Z‹¤ÔüÕçáQmGæÄ£W¤6·Ouøs éš‘i6 z³Çp+¶Ÿ`;wnçÎxu„9?[,'?ëÜůcŽÓÿÜâ>}Œ-SëÂëÆÎÙMc¤ÅÃ*V1þ¯@š\gQ»Ú3RªÿŒä(Ì}ž!'«FÅbEü%nÖÌÏ™ËÈ{5Ѭ2‰Ûã÷)Ü/í"ë­ÏÂèõn³3x¼D#ƒ, ËÁºÀ·rRòcÍÓ'ìÜ›Œœýºö̬ã:ÚT"×%EN>q¼Ã'ιtÂ=|Žòc¤z·;èµ|¹@Jí¿ù¼ˆý9š(áYLG#‚{þTµ´EÒ1ë"Kß’"RóGŒ¹ƒA¹"#W/*G‹Ð£ædžjç1c1vé4|:xuXÄ ¸̶&ÿ‘Þ=n0ͯΌœk‚áž@¶¼âå¶g¹…Ua|Ÿ‘6'aŽz×Bw$Zç¯æé¡û¢C\©þmƒã¡âU"'ÇwKDóŽ-rÕ$}Ü*orÄó©H•qoIÀJ,ÛNÅáqŽ’H~w^/»r·ÙBXMo®ä©0€Ð±È;5£ðû¶N¼†Þ·ò{ÿtè5i,’/¾†#zb¾@6éæƒÄøC\©ýO&²½2ðò–Ûè76+€Ù¸ ’‘ ×”a·(”‘Ró÷±+0±=(:ßÿý‘ö‹à6à©æŒ‹¤ôé4g2‡àÞ6ku$FŽ*Õ‡Õå\n÷OŽ:*úª°Ôà:O³ÁüŸŒÜ¸r6­sçŽ÷]…eAv}Ÿ‹u3ß3Rª¿o—T<Ú=T «ª/aJËm\½cN8³ÓdÊ pL?€[–î‹YF"iòe%ò6˜qß:ibç¯Åùê3hýîÄ•êw>k Tš€\ÝzþÙð‘‘ÕÊg [¤*’éK}1½¢%÷è‘P©§¤‡·ÔÇöI©ýc7³t«y1ч=ô7ábÖHäì gdAÉdŒó[Í•š¿ Èi½G dž¡ÝùÜ<ÕYóu8*ë Ãø½žÜé#q¼m&#C¦Z#ÿT×Aæ‡fnŸòQohhöÉœÈéXòø*#‡lÆ]_ ï¦Ã'b¹œl;#›ü÷g’Rý;ýIG³·!ézçJïäsul2¯òä|ÔYÄ3ò\vw%º"ióñëôþžÀ©eNƒt¸;µä¬ý™KŒ”ê—X¼‰_ï2rUÓýˆ~›Áõ¨: çÉ?ržª zÜåºX¶„y¼†H¦kœeÏßß—“Rû•¢Ž³q[äêE.ÌôMšœ<üx(Ψ¸3òÖ\@¹º1H©ù^eÑ04¸(s¡Ó•ö"ùNUñµÞÛn{üR1×ОƒWAÇ©˜›Ì†§ûz‹¤bîx$߯õ©˜Ÿ.º •ý^©˜Kõ/ÑKc¯TÌ·Øåa—Š&#óÚ'e¬*i˜@*æc5WÀ¢´?Èúö{ï²ñýÎ0òÿüùÅ=П(ŠùÇêØzÓŠ‘õÝå½³VƒœTÌûpB¿û& ë;?4u-v`¨HÖ÷ýõø²jõæHLZ"#ý÷GB«Tä®èçÍ t–g’þý/2w=5FæŠà5 L ÷´ïëC¸Ö|aZSòyý„ª³žpÃféaq°9·®\Aé2RªØF}LòZN¶Ý×?¿çNh7Új"ÙÃGÍ´àFßl [“c2Ò±äÒ#Ó&„°Áó¶ÈÈ—AíÔD¤T¿ óàê}–‘[½òP¡³–kéY€êó¯r߯ÔµÉ>‹’1Óç‚@î€f¹Rûõ׆AkH‘ܧ„RWc® úcbí@.N.@Þl-®Ôü”ë·µÎLNî™ëòÙ·áTÝ¢ løDFî;c¾uÜøMÏYzÊFjSƒIó0îÄbs IW|þd–,ç–Þ]Œ‡/0Òrþ|dváê9«ÁÒë'}à[5 ¤Tÿ6 mü¨DN†š7ÂìÈîÒòfõ¦@ “=Ù–O“¹¥§ë˜å’׌_h?“Þ {7?‚šsemŽÁ·›#¥úÚt›UZ3Òé©c=\dä•áøÝNC$¼½àžÚ–;c@$&÷Ù#ÂÖ­DRjÿÑé'<`Hf®ò‚™ú0nµ:àøð’@¶³´FA÷\©ù]ü"P·ÜB kÆEâiî9Ù€§zöÈY ªÂÈ®cÏÃ>¦-÷jk#d†s׿,s½¹¿&u‚Ö±ƒùøh [vD—;æË8jÄ1²_í šœ»%-™} ’~ÈH©þbÙ+–åø6“üÜ»˜eín%#Mw»1áðosµrÖìöjFúîXUÏÌAömíéý¸Ÿ\Ý`7å=#3–]Eá6WªßbõTXxD›“=G]€’UWSrÓ¤C˜½¶‘H®ë³½Ã+r{èlø•Éù›±ðÝwRjÿ”)Ç¡Ûk„HjµvýbsîÌ‚¹¨v‰H[}CÈšßãJÍ× ID ’±@N}“½ãùÜ~07N—q¥úiGÅB©¶•ÙjФˆ5'Y»Øµ1M M‹ö`s×ëÞw6À™äDõ(Üd(#%ÿ~ñø’L$WxÙÁ¾?ãŽ8p™UoóÈ ›C,ÊÖWFJÍÏŽ,å^"i—º?dÜ<Õî›ä8à÷\Fj¯ÎAìôs\esÈvedP—þè;Ü–›æÙ¿ó4Aö|1މºÜÉwobÉN‘æ>·QâºLFNww@“О"YôØÝ ¤Tÿ[I"ûsÇ—qµn°Sò¸s'`ÀÙHnŽÃôŸ‘r}=‹<s®Gù)\ÝÝ“ÙÓ5—òD£íz•ê÷òpzÆŽ’‘:%Ѱ´óã­tıã'rÌpg$6;Ëí[¤…ÇÓb2Éë—ËÙtÍŒ”Ú?=}# ™H&|Y6æ\U͇ìÈßRFvílÁ³›”šïíÐ~UD2 Jhf% xª^óáÔeœŒ|ˆkÛîhFnh¯,¯`FNÓGvX)7p† ô“šƒ|þÛ¹w³yt–vwÔòiZ4.ZwáÆy/s¾ÁíñêKH½À•ê?â÷sf·ñ #Om‚¬Ü6 [ÊÇã‹åJF¾^:¯;q_Š¡Ðm¹€ë›‹î¯Žp-OÌÀÏ­E2éGk—)RýÆß9‡Ú’edËÛIxÓ>ž{OîŒß ò[{GL+¬áz6Dï}@þóÜëîr¥ö›7ÇûŽÉñ§aIûaÜŠ¤­¨‰¹ÉÈ66aWò ®ä³êhu¸Ýy,'Y‡x>ÍûNUñÕ2ª.{ÆÊIżÜ{0ÖŸV©˜_aÇPSçÇHÅ|ju$rFËIÅ|–E"íëÏHÅ\ª¿C-†v©˜,ˆ´² F*æ“zˆx4e#ó'›ÿ}p(ù)#ëÛ¯ôj&\÷ØÊHÅ\¹÷F„„·IÅ|ŸU-@n‹v ¯>¯oÆÜ0£ÿ~kÂ5ö;ŽEeúÜø€(¬< #o¿Ë€_wFJõ/˜ ¥nŒ\çxí³6smúEBÏ~"yÀs5ædhq·—ÅùqC¸!s¢]t7îäBدÿ©îûÔýˆ@Jõ«Íߊ^Ós2ùÖvdfÅqw>^?órG±ØžÂ ֽǬ7–smž3«ñK2I©ý›7WâÖß—fd§ìJh½h&'w[%Ã"×[ WÄaÏr5‘”š¿ÏRŽ©i¹@3•}Û‰d>|.mºqñ2²Sj3,Ò{Ç=•; ŸFŽbdͧáøåß{ÈäÞ.Õ9£óUhÏyÃÈ1åIØÔdÆÌÛpY”ÁÈ}5Å(}&A]½1+¹·HJõ?ýåVT bäù¥ð溌ôØf…®YR3v N…&ríÆÞ½çúU3¸ÅæÞëô-=…‘]ÔÍQ¦Ï•ê÷¸p f‡äå°kðIîÄCkÑýŸîÌÛx+€kªm_W™tµ;”ê*2I©ýó#*‘Yæ+'iWÂ[éWûµjzÈD2¾«1ZïÕãJÍOí† ÌDR>w<>»sðT»)¡¯zF{}g s&qÛö…ÛÀ‘ÜžBWD;À­p»Ív£¹Êy¨ÉÉ$“B‹aj´AFþñed;t}²FÕ©ÜÞ wBCé Wª¿ñ·rxL6'­¢*ÐBˆ‘“X;ÆážÙnÉ*Œ7ñåª|†Ç™¸ö£"å7÷Ðôöð5ÆÈf¶ú`îÚ ¥úíØ~«„ dZÚaÌí˜Êý0h¬§ÇpçîG|Ïî e¬l†1#«Ÿ·Û~MRû}z=Ę@ Ë{€˜e£¹¯Þ4_` 7×°#Ôâ›p¥æ/ ÒÄÙŠWyå‹2W­â6à©2ïO¬cÖ~Fšf`·6qM¦tB|‡îÕ”nˆœý•ën•ˆGwÊåäŒ6g0j¡+wäºX”ÏÕÈìý‘})'¿S‡Mû¦"9S8Æ î 7'¥ú»\{•ÍÃòåÂûÐ.=Î}m‹ާr_j/@W¿gܱ%3aV›ÍÕ.ͧÜ#JȺ=dеT¤Ë<)ÕÏèÔaxö¾#‡œÐLó7Ìpª¾ç®-\ŠÆs›‹äµŽÐØ1dËÌHD%Üc¤Ôþ•ÊQØd­@®.¿‡+\¸êC‡áÐħr2¼O?üôš&Ró•Ó‡àÀÈù´=h>·OÕͯ†Lgdé’Ÿì Iײtª<:lµs&š¬È` ‹ê‰äЗ¶3)“\p)S›•“:K³‘²s©@>vêà¿ÉŒ´Y2 Ƀ§r¥úW¼ƒYƒ3ÒÚ½ß<妄™¢h|7cm+¼ñúĵJ­cmЏi–Qì½°lw v§t™Y“ ¿¤Ã\©~‘#íñ*°¹Hú^ëŠöÜçÕ£pq¿W׳;¬dm¸Áæø±z #¯w€‘}Wj¿Õœ»” ûî`ðÆX®ÆlØ»~ÜߣÃX‡ë¹2Rj¾Ý§npÍtÈ>fwØ» –Ü<Õ°&0sþÎHcª(²TÙÞ}/N6—q¿xø@KÎ-•¯ÆCR8~Šïqµfât›Ü/-C1jð ‘Ðæ#¿0²RétsLAJõŸñ= ª”D2kj ¶ÕµâNÍÉfCÇ& ¤þ{W–0¾7}ÀAv¸ µ9ùèPó™èÄȨå8ðQ.'[®K†ã1u‘”êÜ|:véuÉîCSø? †üd«öýÈTge^á<ž ãb‘mUbÚ¯LRjÿÖ¹7¡úñ’@^{w §úpuÇÝboîV1Òý¸)víÕ)5ß Æ‹¹þ‰“‘9²Ûr“‘ÿáT_&%ñ7´#HÅüå¤h|,ï R1?2ð<œûꉤbþzº-ü?IÅ\£Ï4½s˜‘йTÿû¦g1µ»ºH*ægaJZ;e¤b­­„ƒÊ©˜· ¶û÷Aá›@Ö·ŸJ×NȬÔIÅÜZ~‚] Ù!Šy×}9heðH 뻿՘T´¸+йÕ.G|í×d½ç§ô‚ËMõ}=¾¬*\rü³2õsöo)ã+#Ãs,#vGÎ-nÛG þ0ÉÕåd«ÊÕw.FÛ{¿åäùépžsT G®5ÃØ3AÜ«Áÿ fÌy®Tÿ‚WØD­œ¯ÃÆl÷äjiÇÀ9ÝVNF©ŸR^{3r]OO$xø0Òf³ ZWs'²TX¯½$';y ˜Ý±V ¥ú•65F‰§³9ùüCO( Û„Ûà[~¨@.Ú¼ïüî²ÜÆx¹7‚‘zyÃp³Â¤Ô~çÐ ´õòhÿSØÒ΄ûhbúG¶ɦ¯Ž¢uB…@JÍ?:EO>½È{k51nÿjn>n<‹ø¹59(?’›ŠäaÍNØ÷ë"#+wÁ•´jnП™0÷ÈHõËq¸û î×¼¨[îâÖ¾?…k4¹ÿ`ßš–rí»±ÃaŸe¤TÿžÙÞì©û š~˜u|,'_wЇ£LFÞk–¬V¹µïcŒ}#Wåûà…C#Ÿg9‚í!’1#úá¾ÿ=”êw9DÓüÍÈ Z k¤²Œž:·×=HƒÂQ(ªúÎÝÉñ¾ýAZ½=¯“Œ”ÚÿÙ;ãÊú ¾ƒôG±íä d#O È:š+5_éâ`¬íÚQ -,,1±ßbnžªwm<Ÿ¶IÖ: ,ºp{ýíÇ'@63„*7cîÕ7ÛÑhY”@ž¿²®ÓŸrËwžÇÉ+m¸Î3Ðî¹·nýÌ­¹ÎH£ãaºg WªÿÏýNLÝhO&yé16u^ˆŒ´û“É[öÈIµ˜"œ;±\ ?dÇá鎖 — U1²—ýTÜ[¼I ç¯4ƒŠÛ®T?ÃQ_™FMœŒ¯d…²½Ü<Õû1{Yo‘\%óÆóªþÜΦ›qr±9È!SŽãÕÊÿYýuÚon%’çœÔpçusîg‹$t*VážÛî‹6ѦÜÕ ѾÇgF¦^ô‚ÝÙ ¥ú'-;Éœ½z3rö´D–7h;·cT!®¡X ÇÊ@DŒ†Hþõ/ƒn¢#»VÀ¡ÿc32`H ›rÚ^ \™Ÿ‰#%ûÝ,e¡Ë9­¬Œ½pHâúÛ±ˆ—Wr´j‹¯8Ì]š‹¹gÎ2òEi%~ì1–“Rû#s“« òfq*Îù´åžë§Î?uDnß<¦¼ J %ïç³Ê.É sG±om‚Ù€§Ú3ìÞl*’Õ›¾h÷÷É08o1 ñD$s= viÊ]çŸóO1r\]Š”/ÉH©þ+Ößff“Ù%ø3KŠ¹ÈµrÇ=Sc‘ìq×§ à~z{woLÈžñ鈙ÄþŽÄ:Õ»Œ´\0 GOlçJõk2å-ûjñš‘ÚÛ›bj­*H“Æ.ìËròµòQv(TSÆMËpú¡@íöƒÊ˜G\©ýÇÒñX§ÈY¯2a3[‰ëëöœ•té('75éŽÁÙ+e¤Ôü¯T×Fä¤FV˜9§·OµöÖv¸‰æ"éd·¥ÆÿS³8 ííß0r“EÅpÇ®Ob>§:p‹Ç6‡_¹wé¶.¨iã/_‡ÂÊÜ~GRðn» #ã4Ëñ¶U_”ê¿-»lzd12t~ô.¸Â6g5„§:"÷Ö4L”ýÈÝSÏÀfº?wÁÊT=ZÎ=P>÷UŸ3²\Ç‚óRýmêŒez€\ßx(r-ús[(E²O·3²ï”bæ3Γ»þ´'œÿH»>§ðfB6WjØ Cc?22Í) 9ós÷¾ˆÓk™dQ+uÔ,ö’“RóÔlÁ9ÛBF~üf‹·Ó}¹ÿáT_[gÏ@¥Öp‘TÌ·—§Â¹|%#óŸ#â’ê1F*æ{zLÄä~R1ÏÚÐeDR1—ê_¹½¢æ_f¤bna0,!C s9NcÇH]TÌ»Ægcý錬o¿fš3à>i(HÅ|ȇ¦p ºÈÈÿÓÍQ´`(’õÝÿ$;ñ/o2òÿä™×XÎå$Yßù‡ÎÄË¥çYß÷×ã˪2¤×ê€Ôå µÍ=¹Ò{Cõ¸·\¬ÌI©ùf GØ“©ûO'8Ouä6à@ë¿®(èo²f¬ j ¹ËËgCÛ„[e¿mûʸN¦t©?#‹®6AÆÈ8®Ð%µ^ dŽ‹?2Ý DòYÏø2Ο‘l× ~ŽH©þ¦Þƒ~„3#_½5«!\×›Î+€¼¡‚à#ý¸‹[+Có]s‘TÉKbÊ­2Ò÷Â)xízÌÈï %0õñáJõ[þì4”>*ƒ\º,SRšrü,dzSü²ÓúP¶æ)¸êãa½i#ݲnÿûû_‘Rû§Æ~|ÉÈþN8ÐnÈ=N¡pÓíÌÍœÛÍç]ÈGùí°wæHnM³3X÷¸Öœø[„ÁþFŒ”êrù&T·O–“î®â¶}1×úØM°N~Œ\ò:~§ÿ”‘?þfê³Ì©¾KŽ'r/gžBpÜ@ÅR1Bs¥úµÑ»…[³i)¿‡^ܰº`V¢³ž»¾à*kæxž»ÓÌÖïž ä+ W˜æ{s¥öOˆTEØí¦ ¯œÔ€ap îüͶ˜ý=I G®vÄØC¡\©ù/\f¢½_l6ÁcL6#ðTg¼¸„Ïš ]GdaÞUnÆÍKxêËH¡"'ò7r]ÇÅ¢d)wEr|¬q¿Œ4ÁééÊiço/¶3¹“J0ß*ŠÕ:«×wIÉgÕ›9y¬&Ëâ3àšÖ›»ýD6²L×™‘Õ剰?ÞKN&<”æŒì8z ž¬—‘ûwzaÉÁá"¹8ÔíÔÛs%ë ©€Ý6SF^ô­ÆyÈÈ&=Áܨ„‘ƒ«»¢«ü ·d£?V»=HW÷­È‹è/’Rû÷…ýû9ä‚ H‹0=°ÖªÜoÕΈjñJ }Ÿ,ÅžÓ]DRj¾½Ýr˜y0rÑȉ`•qÜ<ÕgéW ´X äìÑ…ÐË®bdßÍñ}èÏýh…m:Ü‘Ëýñ¡h×ø¤ÌOq]Ž÷†Iy€@¶³ÉÖ¹pçEîĶÌd®å wdXÝâJõUž„댲ÅýxìXÒ›»{U ülË$ϤDÁÕu¼ŒÔèßÕf\›™ßX®• #3`´hµ@FgOÅŽ'¸Rýì2ÊÖHMNŽí{EÉMÒyápL»ZÆÈË'ãÅÇl®_TÑÉE=K™þ”Ú¯îbËdUkÆ FܹÿÙøt{¨jh‰d¯Élz¿‹)5¿Üa2N©4¹o–=ÎìÏý§ªørϼÀkŒTÌõ²C‘½É„‘ŠùxÕ 4mSÆHÅ<¯£‹HÛ''ó%>“pø§¡H*æRý»uŠEðZ TÌWjŸÃØå÷e¤buµ’íŽe¤b.»ÒWæ d}û5¹vÆV©˜:1 —ÒO3R1ßz  ºOòÏ ë»ß&Ï cÿ´©˜çôËd-¾v’“õ/[Žg‹€¬ïûëñeÕºù­±Á¥N »MQB°ñîÀÁÛм¯)HmeW´™jε|}Óeç©/æàΨ(î‡Ðñ”¦Hß´Â¥sý¸F1n˜iuŽ‘½U’ù@Jõ?½¡–i?i"’ îÏÙ}çgéf;¼ê#'_­óbº©Mùw£€‰ÝT@Þð¿œå—ix»ÆŸ»"‚Öø-_NJõû䜊üK·R£E2æVýæ~K gÓ«2Éfñ^ìszžŒŒ ÌÀ®ƒûåd‹¶…0×MH©ý%ÿ0J"¹IùûøI c¯]ÆïÜoŒœkR½+6\©ùÙI l¼…9é6ü {"ú0²æ¿dËÎU d×½·˜¥e1÷~q0²Z ùóGæùwç¾=q+L9âk%^mš+纷AÒw-‘¼ž\ÄÂ&‹Ù¢º-ãoËH³º4èÕíH©þ'¯Ýgr3¹@žŸr—Õ|÷ãF‡F²[kBy½º’­yÍ-{}“ïàv˜r Ymì2É6²‰Åg©µk–¾?ŕ꧷ú<ž.l/’ÇFÁèL/îïÌlá”õŒÔ6Md #S¹½´bÑ]ÏP$;Ïߊ#»q¥öÇjW±B¿§iuèë6²˜û´¶ya®rrÚt˜Éf¤Ôü*çZ¶'¢)H/S6»kqðT¯½Î`ASä´/–±iÜ⵩˜nÙäûV™8évƒ‘¯·—`âw¬\)Ç«_·¹ãF—±‹\35„Mÿ•AfÖD¢.8^NÖÉòðÔr²@JõoŸ\Æ&ù¬ÈÊÎUì¦A/î4ߎè×±%ÈàÚÿϘk"B¶þ“÷zŸÇsÝFÙÿ{t»Ü—‘ËÂZÃâÒ#®T?­÷'qÜ{Hªþ㎧ÅfÜ¥.OØ­×o™þ¦5¾Ä4ùáþ|ä<ÈÈÝàêÉ•üØ3áë.¿,n«ï³éÆIܱۣ0Ö_ƒë±;V+ÉI©ù®ÂFÌߢ²š\ÂÙ€§:*3ˆU=qÈÑÊÞlÃÚ9Ü©OÓ±HîÃÈž’aoͽ ¿Ó·ò×\ò åN<3[j:ËÉÙ»ûãèT ìk{‰ä»K1ÇXƒ+ÕGïo,îì#9éàÑ㟸s/-†§y¼ÑZ<^•Å’…ÓQ6r2mK,”ï癓Ù{VÀÞÆd²q$ÖÝhÁ•êgÑÙ…Ñ2‘ì»À]¿›r# W¢2aÑx¼vlÊ]xbÔ{©ˆdf\¬Ô…+ù£ò¸”uN‹Ⱥ˜R¶f¯/wù™Ã7ÈÉß¶×q[Ï] ¥æ¿_†–}}9¸nßÍå6à©îò=ÎT4: dùWV)'í…8LÚ“‘‰s"ñ·+·rõIÔÕÎȲéшÞ;ûøº ޏŒä.òHgWNËÉÚã«Õ×_ LìpÅFI$¥ú÷Pi‡ÁÍää²Ý:ÐkÞžëô{%„~'y÷ÃL ´váši¦Áå°¥ŒÔR€[†3IçKA¨ÍtdäϦ¸«\È•êW­5·'‹d޶®æwåf/ÄôqùÀw5ît¸Á-UMb­ #òEÀcf1)Ùœ”Ú¿)óæ,]UÊÙGÇM\ÅLíÚˆdÜŸ+5ÍÂ9ÐZ ÒׯÅ[sðTÝîº3«P9¹lÜ V›`Nfü‚‘Œž„/;nÞ“DŒÈy9 ˆ.ýË]=Ó—¥,5bä·Ù…,Âõ>w¯cKd>QÉØYÞ 9)ù¬]¡ëÉ=åä±ñý‘ûy%w­ª%ë‚9m¶)L†ÝæNnCóþhÜ5¡Z=¯qC¦ýûÌ֬ޑe%°ÝüNFJõ;æg‚]*ª"édÔÍ¿ÈÈOk²-–‘Ë`ÑÚ“kûÊšý—ÊÈßt±¾Ñ%9)µßÊñ!¯4K uÛ=eOkFpÙ¢ÍøšÕQ$g?_ˆþÿþ¹I©ùÏÂb`hÖ¤CU6ì´?2ò?œªâ+øèiöÓÞOF*æUsàûæß‡ƒÿ§b^r6 žëŒDR1_s·üÖ¶©˜[Ÿé…ºU 2R1—ê?ã×`„.?#'ó·€ä* s=9 œÛ‰¤b>jŒˆûS7 d}û¥—öÁº¡WR1?0 ÿ;ÌHÅ|´J!³h¿ÛŒ¬ïþ¤Ð—ÌÖCW ó˜Ñ;0ûE€@Öwþ®ÐËhõh5#ëûþz|Y5¤å}ŒnõTF®¨)Çì©û¹áûÂ`üÍdôšóH¾¤ËÝìˆ õE²b¦Ò¾ âž×ºÍß{­ëÍ®mÏm<µ ËÞôåæýLDÉUU‘”êß1÷,ì¿è€Lêeµ­¹êN¶øfd&’›t™;M¸{ºžc^›#ÒÔá s]Ö˜Ûmî=üqôæ>\ƒô¬/\©~ÍÓòØÌæöyÿÆ6ðèpî¾=×Ù“/ÿ+vUãþþ¨«ÇŒÌþVd‚Ò]xÝ,%ÇlÏBA e;¬sáã[ËÈߺA¢®/#sµ>°Ìò­a[:a”„<~û[Þf$#ãß{ÂA¡H ¯{…ãü%?®XÿeÅiØîñ/#?ïNÇß:î¯ÚX4GKF~ÏWBî«g9wh%ÛYýŠT”ñ`Ìe ™ðÖ ÈòÁI°PñäŠõ[»À—…ê–II§pW¦ý&›üœÜ%»þÈkop茾ã.dä†P›é\±ýÙ/ãàX£-#ãEcì®Å¤x(z3R²;}VýÛ⻠9ºƒÜ¨VÎÙŒ§©uÓÒK¥dl¯;Øà¦+Æn¹ØÆÈœêllòYÊ­K{ËÆîHlê›îŸ¤ä‡}‘‘Ú—‘уñ¢ã< 阂áozÉHÅaÀ•¸býcf§#ñK#ö¥!zß)n/%e4j‡ äÙ¬nÛy·øU_tÚå'!=ÌñØpQ9CçÏ⮺2òÜ¥ÕØýÒ‚+ÖÏò¡+Ë)H—c-ü˜]ÃF¶Ý2 5 @~é¿泺s_EÏBRôy¼»§+ö¨½ãŠí×uŠD§ý:2²å³Sˆz¬Í5ròÅêÐ FŽÏ‹Ä˱?¹bóë¾Ä4“FîÒ[‹µÓb¹Íxª/LnÃäÐ\¼>¶¾—\¸JSÑÛ]‘ÓƒâñÝU‰«úÉFž7¥äÇéÀ¦Ö£ÒC½f ºšC ;Ÿ°‰VвÛ.=|Ì÷Èþ;ç်7W¬®äÊVî1æŠõÏSŠÅ]·ÙŒÜü=“¹ŸŸMFß¹‡róÜÑPu¸ÀõjSÂjÚv”’yÏ#Ø‹Î-é?Ä_u~ dýãW,øÜ7®X¿QwÞ±´£yŒìk®ŠÍ¿Ê¹ý¢±÷[>·hÕ”ÛEs»Kµ0áØne®Þ~¼"!ÅöO»ág»ÊH•ë¾P|ªÉ5û}¶êºR2b” ­GŽH±ùåGObB†9È÷L i×™ÛŒ§ºþF ¬Bór̈B m¸Î½ø).>îŒüÖ3 éÅpÓ{ lŠö^LTõ`¶¿WKɵí{áJ¡.Èûƒ—âÅ‹\ßúïlÂìUŒ¼×Ö—fJH±þ$§ð÷¨™Œœ:.Šñ«¸öFHj¨È÷Öm`pÛaÙY6 ñ #Ãòž±Ø¨ï\;϶bY”ÜUÔ.¥ªŒë[©‡Ñ5×9°£\Fq[´°‡Ââ@îºÖ«x7„û×%$çHÈÐcW˜Å‚@FŠþUoé $v”‘S·AA{5®Y‚3{ä뱨(KÏ!Åæ¿¾X“!錜P& ÿQ]îÿpªò¯Ñ³e¿¿^ åó¬û ¨?–ÃHù|[‚/Ëý:€‘ò¹~¼#†|d¤|~sÿ6ÃÕ_BÊçbý»n?€IŽŒ”Ï0ï‚x”Ï׬ë‰ÞUAÊç.à¾G;)ÙÔ~™ Ÿ×¤1R>?9÷BR)Ÿ‡oëŒçSu@6uyO\ÓT–‘ò¹É÷l ZßÙŠlê|Íù ä–)!›úþ&üXe™›Ã #Yë‰]íÅžŽÖÎ=@j¼È…_ïÖܯÙUìUu.#C޷İ5†l{)!×}Íbo³cõÚ áW%2råh äÚ|H±þ³Æ¯E¼ó@¹´õìTîÍ=v­#¥d`Z öŽß$[þ¹…å‹™ðô:<4%òˆ© \%ÝAþêbÓkn3R¬ßÝ ]¨.rè7ìuÊ­\k‚~ûq(MĹҡ\\cãm äýŸl„þS))¶?S· ¦|ÙR.;sõÆê µȲws 8ñ'#Åæÿ0X‚ág‡ƒìßå0Ú]3â6ã@°N ëã\ÉÈÓCn²ÒÜa›ŠÐb^7>¥/œàêŸ14Œ@~ÎY…s¯zq3Ç«ÃbN #x„n‰”û½íjLÙí#7V¬G÷[J2R¬§Ôiˆ=ÒUF*¥NÀ¶*%î‹U¥xå”,³Öe¡Oïî`û|<µ—’)?Ràu+€{ªn:ê{22Vß‹¤-AŠõ«ÿì 5uK#,BñýçnãÃ5è?Û’»Yé ²F˜q-”5¡¸«Ÿ„ü1dþ¾-•’¢§`T…þyIV‰ƒ\¯ÔÕø8;ž{úê$DUoçŠÍ/)„¦öwF®Á1„Øå6ã©^8ñ„ÕõþÍÈy/¿²Ä®J Mò°åÉTF†öÉžÊÜÚ¾Nø¾¸ È)=]ÐvR>#Õ”'AÃm·¡j0V%tå¾V@¹£ªŒŒ~w™ùÎk#bý-Åû¯9^o,ƨç~;kËK\µVá¯å*jþy&;¦žC&Ic°wr†„ÜØÏMHëg‘—ªÄë··<ºÛL@ömH†_W=îo§#ðù·'·U[ä-jÏ tVDÀ.\¤ÈN¯iÈ!Åög¨WÂ×PFÖ÷¸Ž¶¥=¸3Êúà˼®R£!,¦*ƒ›?a²3ÌddûŸ[‘r­ŠÛŒ§º|q[¼0ë²LÚm“»r¹'ÃD»VBFíŠqãkî–ÄÐÖ:ˆ‘é±óñÏÛ3ÜeŸÔ1»ãnõ•-jyû²· —<Í!÷îé¥Q{R¬ÿ6ïÎä ¤i·©¨tŽ;g(&ZÎõþ‰%õ¹6)Ї‘eù˜mЛ»¹:ºY.ÜÇcõ~W¬_GÅ ôøÞ¤o\;~aäÏÇ`ïÞpã‡GÀ´BîÊ7 L;1œëç©É£§\ÑoèÏ®¡hr+ÆÝQŽQ65òkŸ¹ìòÔƒ#¸·é?Eïg¥#,>j€ÌÑ Â›€þÜf ‘¹vñó`y¡;H WU(ïÀ-ñe8¦bÎ²Ô‡ÍØÃ¤díâz¶è¤ÀH±þ›;Ì…q‡pt_µŽú'¸C['!K÷»”lg…ß;údÆù*¸©¸IÈêÚÛ°úV&%í>Æ¢*·#¸®Uø÷q #ÅúM\$@­¸Œ‘ûì¥èeÍrßBZ wÿ,˜)sÍ,§¡²ú÷çESÌ5æŠíO©(ÃZ³D Y÷µ?þqýÏà((Ti>3-}Î1Rl¾W:–uÔQŒ_Ž7ÙŒ§z"ݶKôA®z>ŠÞzÜô±0Óög¤‚w,ò‹“¹l⼸jr¶E0´5†qGÙ…£)¸{†x£Í2%îw‹±°t g¤Ÿ¶Ê·¨qÅúçZ­Ã¯[þ¹CØŠÞâºhæ£õ/7nà™´ùz•ëöú®ÄørÏ–Ãîqwä’+¨Ø–C>uŠEÍüÖ\±~!YXiæÊȧŸÓ`=v>7ëoG|ß|Šë¥ê€÷R¹ËgµÆ­Sq\· }Øgö)¶_÷S1*®–yÅEhÉÕ|ãÿ7GÙpËŸåsÅæ»ÈŠ0ô÷TFN»|åÞg%äÿpªò¯Ó§Áó·6HùÜÄ5%dŒ”Ï-HÆé—Ý@ÊçÁ#½¡ØSÆHù\õf#»Þå#ås±þ:ìÆø÷Q)Ÿ¿])E£ºŠŒ”ÏÃ_¥â´¬Q åó²'µ`dSû©%ÁnØ0FÊçæQ›°måMFÊç?v ¦b0Ȧîÿû@'·"åó´8/ô3k²©ó‹ÂNcäײ©ïoÂU³'áýšn2ÒË!²y½¸Áй8õAdn^12–½fdÒ£Bèr·&Ü‚™±;÷âž›¬8ù ÷á& Ôµïòø—–P~¥*#{]»Äöž(¢_[â¦ÀN¯?Èéš‹±òD_®ÍQ;\L¶âjüôÄW#¹Ê"YŠ›@ª|8Ëç_–’›VŸÄôÅæ2Òâ³-dgÚpÅúMŸT…(UM>ñ˜å nØO;8ë7È Ý•×9Ø)ÌhŸC>~—Åg¤Øþ(›)Pº>䌻³Ð0ô?ýŸ…²Í?t2A­†Ùž×–bó5]ÜàÕ(Ùîuì²ûp›ñ N1#‡ËÈ¡Jк<ˆ«ßú*rߤ3Rñï2œ˜t{ëÎÔ}—ŸoàVx‘”,[<%t@þì³ÇO·àþ›Ôõ½´¥dŠu?¨¦{ ¤èÐçõ¨Óï Ò:zfšiqíŸDØ„þÜo™g ¹ZÛÑ žM´H±"ߨ¡,u‡„”œwƺÁÙiV0²Æ¼âŠõóÍ­þóŒ$åÑ×qDÈå¾?†¤7Ã@Öê†áæ¹~ÜÈ'½à48…‘iYø|îÇÛ?ê¯ø¡ñçìÿ¼ä° V[ús‡|ëŽ7ýŽHHcƒ¡;{¦”›_ÚÚ÷Œ¬7B͹0n3žj?ÿ“Hx0TF~+9Ž)+†swν ÌíÃȆŠ7J%¤ÿL9A ÛžÇÉT+nn²=Š—]`ä¥W6Xyʃkwì 4[ ‹}â™ÖÆiŒë½u+ÚƒÜ=Õ 3Ö¶àÆ÷ŒE»Æ{Œ<û8 +Îr+ºC1ó Y`;ìgŒ”|µÆ‚¥Œ\» æ–tçŠõܯ i3 äųˆˆV‘=&Ä@ysg­GžÅ¯%?™y¢ìϸHÈNÇJرZGFŠíµ]•©ý@ÐÙˆ—©}¸‡ë#Q ìã{efãŠÍ?|ßžŒÜ0ÎÓ¦gs›ñT¯{ÃÆt¤Œ\zõžýõŸ›~¥A!ÈOBÎX~›ƒNs§¼‰Æ¸¸t)¹â|ì´oZ‘×,|ñ#o1B}Ä[îÕ\cÐ:ÈÝÝÐ }÷Y‘býuÎ:CÚæ%#«69cy‹J®õÁ@(÷ôæ½:‚iF~Üu¿àÐg„@>½¯‚²+¶Ük/ ÑÒŸ;þû2Œ¸òŸbýTeÙ¸pSYF^ÐHu—ÜþùñØ{½œ‘çG#ïu,WgÅO6¯\dö£Éøe­ËÛ¿g¶ž5¹xß6ÀJŸ›8×—ÍP¬Ê!°èË)6@¹Þ%¶ÙjR(>«ës›ñTß?rF—á2r„Ã^¼šjÁMû…BȤYáøüÚŠ‘Æ2þ<÷¤HÈëÞ%t\ë VHëf²ÏòMh»SÂÕ ËdƒN d䕼V¸‘¥R¬¿é¹ƒØ=6‡‘lº Šç¶Üè‚pI Wm¹l#s+Ã*Øú$k\ÈÂÇHÉ’ÓPòZIFFîyÉÚü«ÀýÞ‹ó2²•SmU¸îjahÑÛ‡‘û6 òš;·°ý,hUÇ}«´Òó‡¸bûuÛ§[´AžŸ°×ghqï~Ëê†0òدþX,pÅæ[.Ì@ϳ]A^0+Å›†:F6ã©z>ÙŠúÌddð¢uø<´?·rhì[:3275Ï_FqöÝÂm_AJz^º…äDoì‹g­ÍAÚï‰ÀÚÊÜË0½ÅWF0]ÝwpÅúŸmµ ¯CY?g#&-<Î7Å^ÇTA¬:£¿õ¸Ž=ÝØ”ug%¤öä¶/ý {Œ@ŠíŸ¢p¦/5@ª„9¡j‚:wo÷1ÕÛ–‘›§„îž )6?Ù° .¿–1Rò%Õg$äÿpªò¯YjË^d #åóçªÉèü"“‘òù)%Hõ} òy¥, ½u¥Œ”ÏCRºaæÂ‡Œ”ÏÅú·x·ù þŒ”Ïó£4·7Hù¼Êû>«oŸÎHù<òƒ9îl!Míwq‰7úUWäÿ×o+îÏ(e¤|¾ôo¤ŒÙÔý§~:£ûveòyÍŠV¸¤Ô†‘MŸ‡Þ®g$dSßß„«ÜÞƒ€ü×Ñ E͹ç äØ+s¯(FiÌgF†VªËÜQè‹w{ÉȯõzxÞ­Èù+†£Ç£þÜ÷ã4þv´@V®ôaÚJI±þþòð¸FAF6úfclh[nha›ví˜@î÷9ÆòÌÀÝ‘V†îÏ꩹ì&Æ[à~t7†K­:Èâ{öðV’pÅú Âh¸—v‘ÍÑ!¿3×»›Š—›‚T‚î¿Í¸5OÞ³ö'͹;¸–áŸû\±ýÊ󎳒…*©0éû¤ûLJúŽ=ŒÑëGÊHÍ·Kp÷àP®Øü%YnXn;äd…päšæ6ã@Ðnw°CAZåÁæ¾–\·Ë×Ðki1#—/«DŽNwŸÛ˜«‘I¦³¡xÀ’;¾a&Ý råŽÃ¨÷6ævT,e»F1òò÷!H²sçŠõ¿> ÓV*ÉÈåÖ‰ÐêöŸßfº²u©¤dúÎãLç-!Ýón ×­#?ù–Âè¡ÌŠ|„Ï·•@>žà‰sC)Öo‚¢>Ôv‘‘Å]Ô Zß‘ûsu,l暀¬mHAû_ºÜƒ ±ÿªwžÓÜ«SäŠí·5fÓÏdHÉʧXqäaîZE+S‘‘GºŒE’n¦@ŠÍßž!]¤Zn&ží,ed3žêqC?ØÝ´9øø ,{;Œ»ao:\›ÄÈ,ÿØ{+!{~2Åûƒº2ÒKÛýÒß äš)H¸Þäò tØœÁÈçot°‚YÓ5uê2Š‘býí½¢±¿¾Œì{(+ïýÈQö2¶FÕ˜‘…¿²Î+æsMþÍFÅŽú2\'¹Þ3G’“–c±û]F¦ v‚ÌAŠõ+ïò/”däž¿î±ýËra\~'ª€rB@ú‹Œü¸eÎ> ã® Šç?]¹¢_{.G2ÛúQR²rq,Ó_'‡<Ýa²¢ äÄjW8©rÅæ[œNBÂɃŒ\^zÂ×q›ñT^„ÀÏ~(Èm-"±«·B¸„÷ªû$d¢ëEøx÷掼l Íű™â0î =¸©JްüÌÈ1í—ãä?÷Ž»ʦ·©¥>/M3R¬¿‰ËI˜o{'óOÀÁã67îFWÔLd$ûcmt[®kÉih5æIÈ‚Ñèpn#¼ð¸]/ )0ÃH±~ÛÓJXdx@jߘ™æ®d­“œlFzÏÉBÜo?î u-,‰|ϵ/…Œ{¾žþŸbûwJ“˜~L›òLífWÖÍŠLþì‚ÓndäûyË16uWlþ¬ý^¸û%š‘a>8rù ·Oõ·4 [›€\Üò,VL3äÅ'"M}„œ}÷ r7¾Ýˆ´y'²wânÙßävìeƒëïÛ‚l§´wº àÞŒfø÷ÊDFv»£€'“.rÅú×ÛÇûy…9ÈÁ-%I\¿vƒñ넾„ŒµìñogIIÿóøóGZ:åA÷s(w÷Ücð·vãnÿxCÍ[‚ëw×.‡u^à#ã–æ3) ¹ëLS1®çF®!ë's3^¸ ¸Ç@›Îú¡ïŒ§ŒÛ?(\ÊFeWX‘Û²„»ÒrŠÚ>2’‘ÅšmáZÛ(¢^‘8ZÔ¤Ð;ƒßvà6㩚]HÂí=: .Ç£\MîýXÖÙ‘²y¨µ•ÛfÊôÕdä§±Óa£cÈÝßßz÷F€Ôƒ-kûrûˆ¶ûs­f;Áa:WôYIý8ÞEžÈ"­Øù‰ks© î:?–’Ýœ˜‘^7¸w|v3rƒÛ$ë½”¹ ©hiÔdÇU¨Å1Rô¯Ny% ÕÓÈ/ØÝ‰7¤¤ÉópkOe¤cu Úånâ6NÞSŸÌBÆ·Z®Ø~Ïûå,í¨„œcVÃJ¹Úõía?ÿ˜@:œ×S¹¢8í®¢xU #/×—ÃeÔîÿpªò/ïÎix7_¤|þÉû ,Bùÿåзê-#åósËR0÷ã7FÊçam¶ ¥ã FÊçbý×­†¸R åóL÷\f}Î[JÊçS>ÝAà'#))Ÿïî}Ökr%dSûYµ„“Ÿ””ÏÙظcŒ”ÏWÕ¬AWOcMÝ_^÷€wÿ_?g4,8*M¿÷U.Œö\M}~¬J|b‰Iµê2òÖwS|NêÄí;ñ<ìW·™â™ƒ„)-¹Ku,Px§@BvÓÆ ?%+òY”¼½ùúà vúÀ5Tˆz%vV³ÐåŒ#Åú?´ñdN';0ò{Ô1V9†[w*©9øÊ¾zÎÖÑ­Ê[ÈÈÐí± ¡ צÍŒîqA Ç­>޲”d®X¿ø×>ç#g­W‚KI7rÁI”Z(ƒ|‘yß5¸†u.x›v‰‘Ý®ÏÃXÁŸ+¶?oàaTÙ‘óöxÀ'h8wó)’ºKH ¥*8ZêäbóCº¹á¬R9#µïy¢g[uÍø Õ‘úš2ò€––ôüÏ›.£lèGF.Øp Š÷®sgF~d÷eçF’Oq‘mÐ$dÿá¶pMÔ©¿ÎƒS-¹BtG˜®êÍÈ Ÿî±’”‡\ÑSxÁ.ºoed£{{×ýwXËTŒÒï(#m.ÅÁ6Ɉ{¬Ž…´|+§×¥2C÷îù®'á·^GFþ½Á?:J¸bý.ín…£}dŒ oµÑ÷¸>»ÎámR#¬²°øjGXñœ‘…ãp¸)H±ýÃo¸¢dº¥ŒÜÿ—3–Xp/®Ã½˜ é7íâ \±ù‡‡Âò|RSÑúœ·OõB€â;ÉH…Ê(UçÙVcl} #[l¹â‹ÿp+Á¶*:p¿©g³–Ê…\Ó„Sˆ— ù¶4{®tâ iˆnÔãêǭǸÆ+ŒëoçQÂf}fdÀ‡l¹pšë¬‚µCe¤Á÷#˜’>„»Òà<[7ÓF ­bGT ¥¤×# .Lj##wF¬†ò¦“)Ö/ì5îÊ~3òÔ'elÖQùôt>üªÛpã]J0dãsFv8ìŒgV– ÷ù`OL›ÿÙ߹ж‘‘;¯ì…Óc3î–˜‹Ð}W'ýoDaCiû=?S åsv ÿèÅHù|¦Ù;¦½Z‰‘òyÌôÓÌR>wû0-AÊçbýâ†âV¯aŒ”Ïí°¿2Q åóOX/­*”Ï'õ› c3ÙÔ~«/oÁRß åóŽÒh¸¶b¤|®\v²ÙÔý¯g¯F——]d¤|ž¸.Çb? dSçwu,Åð‡Œlêû›ðcUèÙ‹¸r7Š‘I7²¡šË]¿&ì’¸/[FCÁâ:7ÿn>ƾõæz¬JÇZ6Ž{ÿƒ#Ÿ…s¥$ü“³‚;ªv¢Wúq>â~ûO\±þ:6UhÓ9œ‘Vo¡Kg/®Å‘0lþ1˜{ý~”Õs#S‚ñÒ}%÷S±?<'p»Çü†® ÷ÜÚ‡í`\±~…­£`m¸€‘cºŸÄóW:(ÊÎ\ËÍQpè™ÄµÝäŠQ݆‚Ô‰Mz¹bûÛUÞE^ßòYò]<}®Á}°ùî]ÚÍȵÃð¹ÿR®Ø|åÍÙè©fÃH§\Vµà6ãÀ°ž¹ˆÏHk‡´8ðŸ›l’âó‚;ib 'æºÊN# ~$÷„ì8Š:sv Fÿ›¶Ü˜‡‡aؘÄ]x´~9&ÑÆ ¹bý_סÆÚž‘[ïÞÆ¯íc¹ª;‚Qñ=—Ò/câÞq7Gž€ºËKîö¹‘Xl¯ òrJ \k:rW·öÄâˆFŠõ»©‡²‰QŒ|±5 ¬ewæù8|x_Æ6$ï=àÞZ€‘¦ï¸n¢æþ ®Øþy ï¢â¤—ÙX~÷«œ²ÈvÚGqÊ+š‘…/=Q5O¤èׯÒãh8íÌÈ˽ѦÇun3žjyZ1¶oŒad×+eX\Á5ÞŸ‡› ﹫BJ±pêînco|µ.äz8ÀÚÖ ·z¢"î#¯ ºè´mØŽÄSŒÌ\9C$—¸býN«‡ÏeMFþÚ~n–HHfŸÝYmA–_ÊÄÏLE®‘ÛytÚ¯ÉeŸ‹À÷ÊÜ@¨î!0ÒÜv"¶µ7)ÖÏ@垎-bä¼¹9pÓ¸Ê]ý2×lŸsƒ" ¡qå÷’çN¸¼JåªjnÀÊüN ÅöW«ß°îd9W·›~iJÉׂ0ºW/ˇ'ã;3àŠÍ¯q:…×Úƒ¼ö)!Ût¹Íxª®ØŸÄHMÕjÌ[áÍõÜP‰–·çïp[ÈÜ1ƒz€<í‘‚çt¸w â£Õ—[ó8 »4¸{Î E{•þ\/=GÌ+3æŠõÿ{y-6›­”c nb╃V¤šfb52òYh9Ú˜Usu6•@ù„ÛnN歎ᎾâŒÛ]­@þÒ ÄÅMm¸býî—åC÷k)#ß¼,ÁôvùÜ}Ë1K·œ«½¥§îžãœõÄÚ”á «+Î`¯¥!Wlÿ˜ug»@J^9[‹’£ÜŸå2¤tjÒÒ[À² Œý«ÝKŠ´ßA¦÷¸Œ¨GwÙŒ§úNínvtfäC¯Z4èlà&ݽ»ËK¹ƒT+!ëÄíÓý¦ÞTùµóehµ¨däãá—P RÏUõÍDi·—© b»ärs®š…rÅúwyrëŽæj·ÊS0PJæl¾Ž›šáŒÜuÿ*÷]ÀýéQˆš)K¸áx¼º;×MÙ]§œâ¾7Ø£×¹bý^GìóF–ô½ ýK!Ü^)·PìÉUv±µO¬HùÜÒÜIÚ#A6uÿ²ü xOm'òùÚ½áèÒÒdSçÿ°÷AÛéŒlêû›ðc•­b<¿§(%ín·ÇDÁŠ[;ò,OIÈôí§1oÉmî+‰€ÞíÞ ¤­]&Â'u–‘¥ÒQéÑÀÈ»³ŠÑúæ+î$û]…¿åÚý}[\†‚ý€µŽå‚ªŒ|~Òt;q³œÍQa¬2&z,¢ï¿ad'½ÇL=+Q }sZ¡Uõ®öø а’‘’ü‰8'UäŠ>ËßÍ•‰ù=eä„q•>%ÁxÇ5)9ûÏýv©*Wò±Þ`>#ŒÆÂþ¸bû_lØŽ«$ ÜvãrWÆ ò†)¦ç2Ò,»‘9ѤØüN–0ÞR¤Q«"¨,Sà6ã@ʆ6°ººDJ~cŸOmäjD…ƒ½gŒ41G¯½žÜÑO Vm(#oû…"T×€{,¯%CÒ©ø³%7WpO¸Æ#ѽ;È—ãN!kR#Åú¯55DæèÎ2r”gw˜jtáÄNEm¿lFVÏŸŒlÁ™~R”¬òÁ€¿ðÜnwÿ²ðvO$÷FŸP¼–Ëë·áÆV dÐ:/äu âž ÿs 9Ö5忹¡“|²3‘±»ü±Ý¶H±ý—ö£qùqÃAŒZüŸùýuöºwëÇ1øR©Î›ê÷Uœx–ÇHç²b¤\ÛÅmÆSÝûð9ëmºVJvp­aïNåæ«Fa©^*#Å"ói5wè*_,ÙÐ^Fªö9ŠSSo dØ”|4¨}“Ž6çðÚü>w­‹-’_`äæ°…¨™aR¬’a˜ûhÊÈù;ÛâLeî²: ´ÿêÍÈ}Q°Uj¯£ë-oì’h‹]‡®p=/E¡ëF}™½òº·ÁëׯeÆm ÈØ'!¸³*‰›[‡>S©zéôÜݹAÅ ¨vé Ò¤ CgµäŠí²×é¯$ Gy¸B§û†µS÷ëŒ 2Nëm¹bó3‡e€}ïÍÈüÕQh•4„ÛŒ§úWë+LÑÝ@JöMÉbÇ+rÈA/’ñÖø5#tÍBÍ¡®N­ZþòÈ_ÂÑ)| ÷v@(Ú=˜ÀH›ÝÇõ8™ûÄé0ŒF€|Ü&Kë)Ö¿äá¿ì©²ŠŒ¬}ö†íoÅ £Šûž‡$¤é'lN»ó\³‹¶˜´»µŒW¸žº\7©-rö(r¿©‚Ŷ@ë7*)÷•òr€R 4;×q_d\Äãå§¹òJ.ºlˆã¼›µI¡\³Š dÕìåŠí×8äŽúç#Aîßs-Žà†Î6Dm FNùû=ë9@“+6_eë1h­?ƸÆÞˆ^÷„ÛŒ§j[—ÈFÝz“M¦åF²½×[‘gGåcÑ OŒÜ9¼¯?=äú}MÁ—9&ùü{¦µXÅÍÓ ÆÚú_Œì™‹°ˆ. gî²GÔ¡ Fj5˜cGä'®Xÿì3Øßí¿¤þÉ;LMó7a‚”5zi22çc pq×þæXtP2‘‘]òúax@?îÔú¼´ŽH«VéØ9§£Œë×:;E? ¤ST½_2Åë)ŸßÞÚÖÁy)Ÿk4 þ×.lj¿„®QH:n"#åókë롣ܚ‘òù‚]1x'0²©û§ß÷ÅT· åó‰£Fc¿Z0#›:_oY&^ŠedSßß„«:…`Tébü‰p,ê9™û×þÐÿèlE.)º £y9dÛ^úˆÙ !'ú Æòè•RrIb¶ e™õ:Éßb¥ä(I,ƒN1²×útt n R¬ÿð°=°R«ddeìvX_|Îí´g.„jc9Ku4Ö>ĽÒº·;<±í$†½1äj©‡á‹» 7>ÿ¶ß|ÀH±~†ŸÙ›>Œ\´¥ÆŽ÷ྐྵ}£ÏF ä¶’B(yÄs7tE‡÷ÚVdy»X¾te¤ØþȾ¬&ý[é·÷({À5I¨dY%dæèÎ/^fEŠ>‹Oφ¾ãÓ‘ä×é±xüðœ„lÆ€ §NcÊ?\¥‡Òwƒ¹ ë `wîuY¿( £fùr‹‡Z`Å–Y9µ /žKà†ÎÝp 9ýM^ßÅÈÔš2ø¯àj_ËÄv³é\ÑÔܰÿ×oFª¾³‡«§ Èà¦0Yf"#û¦õÄàmº\wƒÄ©õé¶ö<Œ„žÜø{¡- fä£Cñ·è6W¬ßÔ·Ðϰg¤V=¬=‰ûÞRŠhý(¼×þ¬vøpÓ•¤èìQÉHó¼J´|žÉÛ¿lœ'³ù*Í&Ó2ÜYñ³W#ɆÖ½ô“”\±g4&;„ ¤Øü»µ!ø>k+#GÁäìrn3žê·3‰ÈM(ºF)¸˜5€[žŒÆíG¬È;æ±°q ^NíÑwÚ èû€õê]dzì"žvºÈȕËQ4µ«ìä‹?/îïvîèøR¤XÿÉ}p*  ÈÀñ; yIŸëP[º+ËÈÀàžH)*O>¤#N§=Ȇ¸ °Ÿô”‘K³- ÿÅä¹ãkÑ/Þœ+ÖïH{# xÞ™‘ÑŽ‘øî‚„ìÓ; +—ü-¿‡Äaðmn»ËUè¾n#ó.æ àGž„Ûo^çÊn¾Ž±"·'¸0£Ñ*rçStÐ~/ºµG²·¢Œ›¿-:\Û\½ò"þ•vä6㩆{§£þÇ ®dÍ´äîz…~]ùnÖ)´ÜÈ­ÓMgãý¥äÇ>xájêÞ@FjYÖÀ®~·×€pxiYûçÁ`ÉñߌëדּöçúÔrç:™q-> ‚Ç©@‡LÄ¥®ÇÀDô6OgäËO§°`Õ îÓVû± Ÿ¹Ú'f »¸bý–ÿ2ÿ{j­HãÙæðKì&%—FãÅIî!­|8ÞŠœ16 µq#ilï!«’+¶_ÙÌ…Å|-!m×¹°ôÐÝܯ²;Ì3°A k´*Øß¸bó'­,€åÀÖ }½Œ/®2²OÕ¿@€Ï¾1i»%s†ÌâvÏ ÅÚWŒ´6 ‡ÊŒRnXØ%æòlˆ@ú¿ndCV½’’óC®ÁöüK ©¨!ÀdÕnºµ7Î =ÉÈf`zÝ-®è§ÂKtˆ·éÑæÇàÎÓ_«#Ù¾ÓAx{‡s<:ŠG9~Œ=˜aÏ]¼¤ô¦äNl‡¥š½àŠõ{»ÛÿÚ]•’#úÁ'¾—@®ì(²6þJ³û0²[¯Sp ê²…FJÖèqÅö·žåÊ_JÈ!¯ÝX¥F7%ä뤱Y «.öÇ·±c¹bó«WË0ÇÓ—‘[Û&âG‹•Üf믔v¶– åóH`«ÊHù|ñöEðþÙ¤|žts0Š”IȦöK«Œ×ã|R>º®Ýúº1R>øxƒ×cdS÷k ÞìÔfFÊç*û·¢{–Œlê|ÍÄýÐÐdSßß„«ön;‚¤í™ŒT°óÀŒ±ç¸»®âéôã\ÓÕÕð_}”ë|é¬kmòð'´û2’»Ð<Én’ÒDVŽdÛ±ÜÓ11˜¸nŸ@:–'bìú| )Öp7¼ÐÈÊG$ß=ɽá…ØžÜòÖ10©6–’«Ï«cÊ8?¼e7Å«ösça0>a.#ãß÷Cß;ª\±~?µ£ð´µÈÁžIŸ¬3á^¼r õ!+Ò>¼ ;gypMªÏ e½LJ>» ƒ¥Þ )úÝßsëd¤Ï'Xj˜pÇx8A½ï)´»ËÀR®èŽQ&[ø3òïÏùÛ1ƒÛŒ/ô]ëÍȇý`ÿßaÜbËZÔÙíæþò¼ »§Ó¸ëÆAò?#A:];†©OŒ¹u5uˆ~"%Ç õXvÉX ú”êøFÎ2¿ƒ×¡µR¬¿û.wÕ HÍ ^PøôŸß}“0Ja»„ìÛ1釕ù`õL“¹ >hã©o·ë_åꛆ A†Y¾)«3=rÈé®Áè´á'#»ì97Žú EOµ]ŒÒ’²qX445nëo5PߨÃHµÆ;P¼_%!}{Eþ }yXÛIK‡rõûúâEƒ÷Þ'/¼û’!bý&‰Å›3ŸùaéiÔxÔsOÌGýº*t¿žŠÞ%ܧˢðíp7Fú<ŽƒÅã+\±ý'Cwaÿ(KiÕjr«†sõz»"&´%×2ûZØÅ ¤Ø|Wé9øÌú"!£>ŸDÇüyŒlÆSönÆ{0rúÉq0Ì}õM+}K )9{÷Cvr‹,†bsª7#‡_Ó„ª{"wçù3°Œ1àO€ï¼(nì¹,üõã ×±$ ×ü¹bý}ââÑïäUÜ16 ‡.Þå^À]Ì©2“RjQ‚rúÖè2’ÉH‡+›þ|ýÎõ;Ÿß•ÒsÎU|>¥ÂëgÑ+ Y¶—©t1}¶_à†JCPLº@Þ蟄íyAÜVƒØOPœZK‡zFŠí?8Û[´FÊȆc[¡ßÞŠ»ìYØŸ‡7kì¢ñ›t¸bóçhyãwìEFZÿs?v¶ÙŒ§º)Á ZþaŒ¼Š!8êÍ•: vYBv[‰Á§µYý²¾ªçüô‹›ÒäÅöYتu“‘õ{Š8âW{êj¬ý·€k5`2V÷2)Ö?âk2¿ÈÀÌ´ók!#Ë?WàyöX)ùdn!ì/çzM]¶GŒd¤¬v)ŽU©qw¯»…Èwið<g¾¦sÅú]™ç‹·!1Œœ3áÖ†âÊÅAgØL ˆÆ—]¸YÅ…PéèÃÈiÖñ8}m>Wl¿UÉf\K¶’‘mlÄó–n›Û¥Hê7[ ·jUã¹v8Wlþ°iDZÒä-Ë0tÈ6ãþ§*ÿÚ`g‚Ê—IŒ”Ïç·>‰‹k)ŸÏ¼¥Åm]@Êç:²ëÈ-Ïc¤|žY¹#÷ )Ÿ‹õ¿ì‘ 5/e)ŸòÏÀŒèÚR>_öï:äh?Hù|Ö‚xD$Mí·ë–#¢ƒ)Ÿ;EãV±½””Ïû|óİôPF6u¿¢éz°V)Ÿë–Ã÷U¹@6uþ'iÆ—öÙÔ÷7áǪ6ËÔQ`-ÈNnQýü×èY4>NLÏ!7wˆÉ| 9qx,ªÏ¶’‘ʶ¨L/È=Ó°ÙVWFn½sŸ ¸K>ÇÃvD_®ePFeõäŠõïei¯4@ÿh·:JÜbEfÅÕÏóÃC–\½ƒžèÒŽë¤|jzr…úaøéFÞœ} VM@ŠõÛ¦_.«äÓW%ðÕ⢥/Nw:ƒ“8×Ý–«vÝ—ö…rcÄáÅl{®Ø~ížÅÐW I.Á¨ã¸ëÜòÑ騻”ì¶åœO–pÅæÛßŧµ‘RReC¢: d3>(ÚicûÄDü­Ú£ŽŸáö_‡åŽ}Y“ˆ/{¸~„aUÝ>Ñþ &]Ï’’ "éÔYF®} ­¶¯òá7(ì?ÏÝ2–U ¥¤XV²£üËÈZ[dñˆkq0A& ÇINàí˜îÜìß~È]gÎß+o,¸£SÏâ}™>785_}î0Rô?ÅÀ l×5Ècªá²{,w¾V4VGLç¾ › c¸!- 1çãI.S¿Š{J·¹bû+g•aù£“#ÉîÓË1*vwãˆz<),’1715Æ+6?ëÄULuï+a£rà8õ”lÆSÝam‚ø†Sª1»½ƒ¸G¶¦b»Z#÷˜e!Ó+›{åû9<î5TBÎt`î®ÂÈLÝ“è6ý¸@®Í‹Â‡àF)»¨ wŒ$[|¨Çñ°RR¬xõ¼y^ÂÈÁmÖÀëF 7´?昵ùæÇ85Ö12íu¼'™‚d]#ѦÃ-ž²u²`FÖ™À^?‡+Ö/Vé¦_[& ¶Ö ¥Ý^®W÷l{1»JéÏ_ß­ ¹Ë/aÒÞOÜ#™Iؼ'‹+¶ßhÅŒ '¶®„‚Æ,²YÞ¨Û ä»°4¬ÌêÈý_áéY(<,AÆo‰À©½ÙŒ§zz&Ãà}ÇRíõDßòã*°KHQÆH»‡—‘£UÅ5·¾ŠhCs®mi ô摤byð$+ÒŽ™q²ûžOw¨€2µr²u¹¢À;kxà°@úß©'¸Ã-K?q'wÛø ÌK÷â$ŸÁ¹é¥¤×•$=×d¤ØþE™Uåt>›T} ¾=sHÃ1|üÊì…~ÿ0Rl~Ú0DKS) :ƒþo~s›ñT;\™‰ɾùõê"ØiøqÏm¾Š¾ŽŒ\;þ:tŸrõ{ÝÅî†GrçõÛÐý6›ëj›+¹!³î¢"¸#·ûÿcÕ.£¢Ü¿·cc!**"*""(ó½ ìÂînEEÅÂEJDDDÀ ;0¹ 1À.DìæøœçÅÞg­ù¿¸ëç¼ù¼¸Öì}-Îî™už‡Â4ॠ»~:…5Z”ëÿÛb$ž›/HÏÉý±.K°‹çŽÀD×]ìø¶]áP{'ÛØÞãýO°¬WáâVÃmDðö ¶¶hÇÊõÛ^„:ëB$ò]½¨£ÅFü,D“ 0öÂÈ›Ð|ŠÕ˜˜ƒ ãùcÝmXæù³² ¯Ý††åú òI·»Ð_Ëv_‡…ón ²Ï´30Ì­ RnþÓQ' kÚ dѼ4lîÒœý‹§ºûÑ"Öò“ÈùOW£b×!Öç]!æN:%Èî- qfT(û ™ÍšZ“Ѳ¡‘­ìGZ8=€¥òw™dvö•dɨ Xnø$ÈœGÇ`2ô(+×ß-Á¦¯Û 2~s'‘j³û;4FA 7ë° BÔÿÉV³ŽÊûÙÊèŽØZÌÞ^TÃ"•ä‘÷GÄœŬ\¿„ypZ-‘Á›/AªÁ¶›z¶;rØvùˆ.Ïg3W߆Uô`AN±:cî?¤ìÇN»b$¿ŸA*j©"k_Œ¶*$rËÆhû©7;L,ÇÓ¶GXÙ 6—1õÏw‰œyõ"Ê?ýd/÷ÄàÁdûf˜sÄ/ƒœo÷Iü8\!‘]oVGã¯ÅìëŦ¨¶ ²ÖÄ^(;ûVIÊõ3‰í‰ˆ%/•从–%ÒÃ{9fÙ[¨È>¿—ÀrGWvòÕú¿&A"ç_«ëûX¹ý¦¸"Ð}Ajß ík/X«MiøZúŒ=¬¼€í9¬ì_eÇ3ÚZ]EnHDʆB‰ü‹s‹0éÇ*A>}_Û± Ø_óòÐÔQ"ã~å`Zò\özµþ˜<ñ«ß‹2µTdÜœEˆo˜)‘.SÜñ0ä›>utß6V‘/îEBkV®ð…8/ù-‘¡#²ØöëæÑf5f7ž1À×ÎlY—¸Ææ™cÈØ0V£d6.]/`{]ŸÙ»:ªH¹~Æ ÇÚ̹™|<:”x³cõ—Bôn§"õ=W`Ç¢FìÊ}î¸þ1A"m‡¡2¸‚•Ûßz² þù^EÛw!,¥‚‘‹j[KÙzÝsàòQbåæoˆŽBÖ6g‰ì¥…MNJò/žª"ñ:š*Èu °Ðh ÔJ‰çk,$òܨSxû]IÞ îˆ¬.íTdùÒÖ˜jÀ&w ´Ê]"g_8s߬eú.èô1P‘?îà ¯X‰”}VrPÁ¼ô»DNœ•ÅáXOES8d&±µlj@Ó¦€ý˜>S±C*¶ãÕô#ì¼øáøÇ¿·ŠŒÙ4/¯j±rý¦œŽQK“$2mú\tè\ÀZí_‹Wõ~±i÷ýûQä.ò6v÷ T¤ÉÄØØveåödïDÀo †;Qæ]‹=ø!½ƒ9²0³·ïeåæ‘_³_ R¯ô "\<ùOu}õ«h) ²<ò¾ÝR°0LÂí™#”¤¥N ë*È®vÍíßBEŠ”6¸ø²&»Øút’%ò¹EÒk±™R1¾ï %yx翟lŒgåú?3<‡OÃK%rë“Sýä.[²§LŒ?ü=²ý†³ü1t`?>f%³…1!8\x“qy<÷g- £ñõÃV®ßVÃ…cþV"}+!{\-u~7–Î;%‘Ïy£æêƒì•¥A˜åª¥"{ÿÄ­:á)·ßå¨3–Ö­ò–™3F.Ób7Í܆¥á‚W<óëÝaåæ;Ú\ÀÌõ§iûùÎvÍdÿ⩦UdãfKAv©‘‰äMæì:çpxÝ®T{£BpæŸ9‚ìPÑÛß?È…ÆâVÅi¶¾G. ‹ÊYOË,hÞxÉêªî¡S`=öQ“óØ=ùŽ’”ë¿üT ‚v]–È+玣ͬS¬Ñé b[£ÛlΤˢÒ6›M]ý~±+Ο@óÝUdʤ+xkè'‘Õ~_‡YÛˬìJå"HùMTäÎå a§Çöí‡zs3 Ëó†±sócbà $$œÇÌÖe Rn÷§N(‰kòiœr›³©»Íñ"¥>›dÔIåX¹ùSÞß„yT’ ïÞ.À‡á>ì_<Õ±éxõÖLœÿÉ^ì®À`¸Æ³ÖÃBÐgFû©úFŒ^wL"¼óÁúÒ@6õéiŒXžÍÖNNÀÜ´mìuçP”&´dz,ò©dåú߬“ˆîñÁ©øÿÿËršëô¼D ¾ŸÀÚ´ø-²b±§‡&#´Ž¾Šœó8[LŒÙΙ°÷ÎÈ0e<&4eåú5v™OŸN*R/lö3eoœHÄÝs&¹rÔYÔœ«Ë&쿃µ¹my8ìô]Qrû½œà—£ ²Én'Ü:¨Çެì½ßÝÙú‡"ó¹>+7Nx&þ©´äöÔx$\˜Ëþ§ªþZ\‚o,©ž7u Û‡Ÿ©žW<Š€fX¬DªçG:FÃ[×GIªçÝ„cÇÊn Õs¹þ‡îÆ 0†DªçeË›Ãsœ»Dªçç{Å`îq©ž¯ý…:Á6‚¬j¿‡ÆÓ»©·ŠTÏa…=ýZH¤z>Õô.ì¯ «º?ÊÏ Ÿú‚TÏ“ÜÇ!Lç‡ «:ÿ„DWAVõýUø²*¨q˜ºf¾’Üíç+ÊOÌdó¯$#&T—m= NÅ ­I½W˰am]ÚÖs U3b_Ú}3§*ÉiCڣΗæ©(ïƒË§*ÈKn¯Eœ %)×?æí a#ª)Éünˆaoû°µÏya‹{¹.Ý š·û°³÷'àzM-Anyt+÷Îd ž†“…5égv·5ê R®ßŠ+ŽøïW²ó¡-Øz±+»ãøè?~"‘Õ/íDgó*rbk?ñ Æe%éyl˜?Û‹•Û¿z„¾ùë r@†9Ü»4bÿ˜¯ƒíh=Ë<ÖàjGKVn~„ÓN4\ÖXE+ðÀóùEù2÷x‰ÏCÆ+ÉãµÝÅv«]l†NS~…yx°#Œ-í9 {ƒ¼ól.lrû±:÷ÇàdÇ5Y¹{=zu9ÃzµÚ/Tž¦¬îÀTQ>º+×ÿ[ŒJäôœ­$§=.&÷XÏVŒÚz yc´¶;÷e ’¼"È;SœÒ¦˜÷¶§+dì“<è]Icåú½˜½*Íö ×—nFS¯l˜¥+„•ŽŠtS¸Ã¼G{vÿ¢kÂÅk‘’ü~Ù›ßiH¤Üþíz¦0«!ÈÊu]Щñs9bÎF¡™X°7vaåæßèxù'%òVûX ©ñBIþÅSw‹íµ{)ÉØÔ]"´±>[ùç&LÏ©¤›w1–¸_bM¦/ÆKsf³—aÕ‹vìßî8úë—DÎ~áƒñ#ôTäýÝFX£i,‘Nù›±æÈYV®ÿ «P±pÑv%Y{PxGnaËãÝP0ÌTEF qƒgëv¬ÙœÕ8©Ôy÷Î"$­0`w寠BÕQ>›\p$=•ë÷ôÏ|;\d·=kq½Õ?‚üôïÏåÌ´*2å´;Úµ¶dKuçÂ'æ DŠ»±%»šŠ”ÛFeŒwé—dLMC‰Mb»¸nÀFkM«b棓ñ)AÊÍ×x”mûÞ¤Q|Ê":(È¿xª>힦 ”ä‚ݢwæ‡ 2¡ËŒ?$)ÈëGÂ#.޽+Èy¥¾ÈU˜«È'¯|°Õ¢{mŽnËn óƒjÕÊý¨Š½ÄÍÕJ2à“«¸óh:;¨È¯m5Uä“«^°´¿'‘Cú/€û¢^ ›F.Bdwk6õö\øÕkÍÞj>qÆýX¹~G7Úaĺ{‚tÏYˆJ¶Ö)7ý¶V‘¾ã\Ñ MÁ‹÷—¤îìÜÖž8ðÈš•ý­>¸-òžTž®-±ðÅV¶t_œ½Û[ËšÁr²¡‚”›?àK †8V™PiüÅšü‹§:ÛÆC|³¼šAúö-3bÙ²KðÍÒWA®Ž/AÆûì»C‘°zª g6éŠac‚dTÞ^œ|eª"wñBÝÍYãsû0ãJÖðMRŠ6I¤\ïf.B/n€’l2ÁE̪fÌ®›|Ž×Â$rù°#Ör2{nÌrü˜Ó¤MïU8ù°›÷Ï\¯èÀ~; Sì. R®Ÿ»ó dn;&ÈζèæÄ†—»ÀÒº¯Š4x¸ "ÌØšß\qÔʈ5÷Béˆ{)·ÿü•¦–f« Ï4iqÙÉ&wÅ}%9&+Hä$:°róëõ+“‰¾5ù¼C>2­'²ñTù‰­]½3HÍ7GDEŠ;mé-ô¿8SAvïW€þ¾³Ù´á aº»¦’ÔšõPÜþSÈÖÍÝ—©%ò„sì{°ÖMÎaÔ˜• 2põm,Õ[rý­’ÜÄb¯zJòKkoÑàÁ³ 2Û(:;+IÃΩ°t+Ê :­Bµ§-@V¯\Ф½_¹¶‹)&~NWŸ ž‰P³kJR®_Íbø› äô‡}ðäGvû®]x0¨£Šln삌Ã-Ùw?A¿U_‰4¾t'vêX“rûCÖCF²†‚Lͪ…€¬sÖ¤FÚ.a= «’ »(, Î åæK«Ò ´BAjÄF¢ro+Aþ§ªþ Ÿ%®œ“Aªç“7_€Ùü] R=¿_'Q<¯'‘êùÛ‰Q°/»¥$ÕóJ0]»™ Õs¹þ}w5W+3Hõüä<&NÔUê¹–ËLX¬I¤zþ­®Èi¤-‘Uí×ÇØcF_Pêy? Wô_GEªçöQ…x§¥ «º_ß´N¸­¶&Õó›zwʼn×w2ȪÎ?½Ï 3}YÕ÷Wá˪£ÏàÂ;c‰|X˜Ž}iz¬ðH€_ý6*òpr2u³Kª©àÔû‰lp-±/j¨ÈUvèÐ'E3Sº¢ÞÑ^ì5‡}ø=ßõmº_*”ëÿþa4Öµ8¡$—6ŽÆ¦½÷2ÈCÛrô.N" TÂÖÑZ#×ìÆ ì×Ö!Èö » è¡Y¹~Ýk^Â|¯JyiTæ5o-HÃ1ç„qï:¹¼ÿñâA}öK†3앉‚ /ê‡_·–²rûíÓz`ÓçÚY¼ÛíTÍØ‹Éÿ~œM8,Ⱥû¿ˆ†þ ¬Üü:…Æ08òB"—ö +ÝLö/>ô딉OZHä‘!9pžØ„µTÆÀܦºŠlþ= ­_>’Èlÿl¬5Täñ¥*øtÿ(‘îfxú;9ƒ<ÜÁ ×êÏ“ÈD£±0¯Ö¤Ïúq84Ѐ•ëÿÅ; _ ê(ȉӢÐû“’-¶;ß fœ–ˆ·½<”äµ (¿4T‘“ïZaÌC¶¢ÀåÕþòÊK¼ÔéÇÊ>K¿Žî†=¹æyv%`UþŸtšKääÆpþmÌ^iõYh ê#È“ÁÁâ÷KVn¿íH+,ÿj$‘ë5vú˜³Û#²Å&’ çOMu²X¹ù]«oCÅ≴ËJ/ö/žêŽŽ—ñöwC‰¼”sÎê±WB#1zØ)vñçx~ÚÍ:åœEÿIÙï“’ñdš»*½ b sYË1†È(©­"‡uŽÉQ‚<óÛÓûÚ(I¹þÕí£ÐiCkA¸­€ÉìÑ5š4]A6éн;,y0Ó_ŽuS‘†ó,P§¨;»JÓÃ;Kä4Óšð¿^ÄÊ>Ë}»ƒÖF òK³˜êNd ÷vD† $òÎ\„fsX½qıõýiÖ÷ªømT‡•Ûÿ#m Œ=m$2dîPDß›ÈÖùsSŒY$Èê{«c¶ÇzVnþÕð0í&‘¿þýë5h¥7ûOµ¨å5äzkJäv½B<¶ªÃk‰^YÆlt“(”ùlV’š q(n÷”ÿ-Ç2¶(H-]Ü]¤«"›µ@P©{­ï+Q"‘fý3Ä©—Y¹þºë£Ñí¡› wN‰±Q8Õâ(Ž×:Ê:Fáä‡b6î\_Œ.3V‘ŸF Fàªlÿß/Ä‘Ÿ$ÒÚAÁ™ÏX¹~›ºßƒ™bŠ »›Ýƒï”ÿŒX3 oëî’Èùõíp()ýv¶9?½›NîÝ9; I¤Ü~Ëï#ñ}Û"‰Œßj‹ðeØãÚ!rZ¸‚ôձ™]ÿ(I¹ùÎkoàëÉ@‰L}{÷…°ñTÍo!3¼¶D~îzÇ£þsâ×hœYÚQAºFÇ!7FKÇo†£í5lžUfä²;ßëa{Ýæ*rÙ&3œÜYã?}ÿˆòiI9£ùdt?pˆ•ýQ­ŠÅ‰[‚w<O±EIð?_dßâ@ØÍhË®*žŒ·ÁÕUä‘¡«aox[";”G¬£±/êDù³rýž¶,Á#›I‚ô}|ó_c½²œ0ÿJŠDºDx¢—C>«Ñn z-xÏnüî ³ÓU¤ÜþOï&Âþê.‰45 ¹Û½ØIWG cR<;þáxŒ?P[EÊÍ/èwõÂ%òTêtز…ý‹§:§{1NéÖ‘HUa þ¹õŸ—k$A£ÜN5Š’ÑæM¶9 YSkƒü='жìüÅã±ö®D:8mEÎú86l÷QtmÌ.,SáŽf+×?¶aâËùnK"®iT²çB‚ {¬+È΋ãG‘9{ì¢+HQ¹¬ôœí6²µ ÓðuP$;£îuhd²rý<öÁÎ|Œ ǘ\GLÍ‘¬ÇÊøu¤L"½ Âj*òpÑ.ܰìPw<÷ïÆÊíŸï; Q#Jä7Õ9¯O$*÷°¶)Dƒ(Aên÷ܱuVîGÃ^: 7´pA§&¯ÙÉÑ3_(%Òâ@"Ì}Ý”¤\ÿ†õŽŠŠúí¬É…å±Â)÷Y¼ÉE5<©7'&E²9f—°2À‰½iQ€”Saìªjybüò»Y^ÿµp™ÿ•ë7óŒ>$•ÈëÍ0sí|öZ^SlŠþ¨$Í·×Á²‘$23:÷ƒ•‚œx?Þ±rûüÞ… y|Ôn¬úð]"ëÿŠn‹ùÜ«lMn[“²?ÇãÂÄï‘’\vóƒp1i,‘ñ ÃŸ hì$HÃO+°¶ì kl‘ŒåvÙ®y¶tsgßyŠZ‡ž+È>’xøÕ]6h1r'GÒ%ÿlÅî;–ã’>ÛÖ¥!ùά\ÿw]Ï §Zk3H×È\aÛìÛiäbDd R‘7ã̳çÊn |ÚYö‘˜¸?‹õicŠ7'•9ÛØÃô‚X¹~7ûØ`Ž÷F‰ì|t2j¯qc_M¯ë¶;±{"Ÿ ßí‡ÙEK²Wò^“'$@çÍuVnWœÉy'‘zÜ1vù 68B5Ü[°4î‹zç½Y¹ùʼöèã9H"=ŸM„ãö/žêî§sa°_L° ½¬µ÷D_r“ÈUóòq>ä?×Ú~ý÷—þ¦¾dH£6¸8+II+ZâÄ¢… ²í·2ÑÒD¡$û9]Ba¢» ÿùsïŒ÷±rýí  Ó¾ %ùÞî³x—êÂ~Ý ÛÍ ò¹»>:çi‚üù>as³™v+{6(ÙVs’qÌÍC"Í^_Gû+n¬\¿‰Í–Á¢•ŸD¾™¹]†²£. »ùÉìÝýW„OË,Öf'~ìŒäáC¡õÞ•Û/9{`Ý‹{÷ÇKû²;küû›zþvõe¥À÷‹¬Ü|ý–»pfF’DûÌö/žê”#óÁC±&ýazÔ•-œ{«ê¸IdÏ•w°T±›m<²?vLÀ5YˆO«³aï’…Þ“/J²IŸ¢Ð§³Dön‹nFI‚œ8´š5¿ÃÊõoÑ¡ú?NR’¡iø€µÔ·jƒl2³¦ýhÊZ= Í‚TA¶‰]#cÙ/3ŠÑàÑz‰l¼'m¯Öcåú­ tEë¤ãùºë>Ô4”Ø&—³E¥*Ÿ][-G8F±_UÇÖ)kÙ½"V$¹,båöt÷¦•—%²Go|Ú)±-ž‰ ²ÙÛzͱ¼ß)Vn~çwqÈ\MEæÜ:¯´Ø¿xª•þ½13x—  ꂨ¯NlõMÅpØé,‘‡wc.6°ëWíD³wØ+í|p^«ŽŠŒmà+>W$‘v½Nˆ¾¶¬oP®è¾ù¾ 'ø&Ú'`åú.H¹~{džDÞ{x ¥ÏJذ„\alu‡=Rç®ðh|›-›|@,í7Rû‘)"k°²ÿ”Óö¡ýŽ“Y6o?f,ŽcëCÔÂHvH¶3æg±róÛ$þûÉÿh9²žSë²ñT½­ ðËj› ïx·ÀƒÍlnØX¾˜/‘ªï7‘ðn({Ú-cz·U‘}v„bʨ.ìワ…tl¦D E;ŒÎ³g_…˜À¾|~9õäd´3Í‘H¹þù KÄ»@"«Œ£¥³Øñ»êc˜ÃA[7g"öìà p7d·Äwň‡ÙŸX$Ÿ G˜€‡û°rý.çGb®K™D¾­€¾²­ß§âìÚY5`3ê*ë4µô/Ï g^å–i)·¿[Ó(³‘ÈÕù~ЪãϾŒ–kØOµÏaU›£¬Üü¦Vç oP*‘[Þ¥ÀõÏ1ö8Uõ—Î-Ì:´^êù´”‡I¤zn1!¥={¨Hõ<§½-úÏòHõ<­›=F\4Q‘ê¹\ÿ¦p2b›Dªç-=taQ;\ê¹fÍúè~7Xêù¼ñ#±usGUí÷hK2šXi¨Hõ|MfShwÉ–Hõüiø6Ì/¼/‘UÝoìãWöH¤zþFº »IáYÕù¿ÞÆcwYm‰¬êû«ðeUAè'‘¶-Y"jàù ÿ|ús:ô#ûªÈÌópâév«v“ÒïÆ]|±ñ`ókÃÔhkRž×kÙnK„Ï [ö]Y©ˆéÚKAÊõ·>ƒØVYèyƒÌ?²Mj`y³^¬_æu1 § ëï%6¥öuº·hVÛ€Õ9•†ÞF5UäWåE,óC"åúÖí€#•† 뤴×íÙÞ¹qÜÕ]ð³#rŸ·`MÚãÏgK9®‰ ‚ç¶dåö÷Xx W¤“Ͼބӣ𾤖ÇCq¤~¦ ;Ò@Þ¼¬ÜüÂÖ™bùàAî SФé©ì_|˜f§ ûÓ'$²Þym pœ­Y±I­ú¨È“·VÃÆ¾ûúåM¬h ÈZ ®`uJ0kÚ!ϸ°fŠH(fz³]û#e§D-ðÄÌ1OXÙO¸ÝÎA´«r—ó9ÌôÑdvĉk/äƒ<¡ý%žýbè)´ÿ`O ÆsÃX÷7™0)‘]r’0¬w7%)×ï[k˜\4YäÜÕ—è±'vÄÁêõX—èŽp·ú$Èè´K—È™q‘ouRIÊþS^]ˆÌÆM­É¬ë×a;Ì“¤‹S¶ 2á¶w¬#‘ró‡|-QZ®‚tÎÒ€‘Q#ö/žj=ŸV蟙$‘y× ñcq"›}r\:©HŸQÎÀ¾fìÄ›ç0ûU¨ šEá\të=øxì«§5ËÂCØŒœXtŸ¤«"¿Í;ƒº-X¹þzNç0ï¹H£sx§£Ã=í"ÞŸ­ û'»Š=3­IÕ˳âúGìÝÓe"æO}%ÙµYêú R׊7)×ïšosœ1irS\3tkÖ„ÍÉî„—u® ò±£ Nöf¿¥žÆÉ}ÙM[²0õÓeVözëkðÙ–gM6›MÙ-j܈“È£M7Âe] )7ßÓMÚ#šdýû£ÿøžùOÕÒ±+žÜ—ȈhÖ6ŽÕWºÂ²EMÙ Ô .«K¤ª`Öî;*ÈmËìa/²G¿wǰi1lÿðšÁ ì• “tT™´5 554Y¹ýV+¯âl„‰‚µá œÞfu2ÜPíuyUg?ܾôaåæg¬›ñ‰y ‘3Ö[þ`ÿ⩾yÔ³ûÆHä·‘ÐíÅN/õų÷'YS‡@h\se¿4¯¨ƒ‚lý´~iìgûY\AÇâØâ1¢|\0»wJÊ·^b'hyã^Ç® åú/È>…úém@Ö*JÅü‰mÙˆ÷Åää%u zõ®#‘]¦NÃE…+Û¦r~­>Î:f FD×7‚Ü>Ï.MÞ*H¹~¥£Z"Ã÷¹ õcu‘Vq™Í‹ï‰QþÖ䎩æ>*AIZ'…c\µ÷‚\¬Ø…sSX¹ýÚ+.ÁèÃ9bÔ$MØÃ¶¸zs‚-T¤›ë„,2`åæÏÙí~¾­Ud½p¶ïÎþÅS²j2z´ŠÈ.óP¿4”íÖ%ï–ugJ£àÓñ¨’T­{%æ?ôäî§„Ã göùÑ0¡kèÎÖÏ=+nTÊÞ³Û‰ž:Ö Ã^-ÁסZ¬\ÿG›“q¼° Èí'“Э٭…F¨ü`)‘—âm ¾’ œíŸ/·Øß¯‚1®ÑwÖ{lGŒ¿´ƒý]¢‡Wšš*R®ŸW`[Å òMMœÊó`k×íƒør…D朱†VûÙî)0B5EÖWˆÒ„)·ÿ²n6Â4)Èw2Ñ©Y2kÔ&QQ_$òÏpdøïaåæÁ¤¶½TdyFÆÏìÄþ§ªþšg¯°`‰TÏkoI„ý8GkR=/^ NÍZ*Hõ¼Sç—âVü©žßÕˆ_Ë' R=—ëïä.‘-Aªçë//BbÙ~‰TÏcß$ £†¦ŠTÏ?$cŽi'YÕ~Aÿþé}o´ ÕsÃ¥Ö0Yœ)‘êù»‚`q½(>ƒ¬êþ‡;%tÚ˜£ ÕóÙ+¢°½};%YÕùI¯¢ðÉEKEVõýUø²j¢) 6‚ܾ¿­fLb§ÿûü7qe¥’ü‘ÖM¶˜KäçÙšX¾õ-ÛYÔƒ4\KE^ÓWboÛYê¸ø¾b_ˆÄãv]Ù*ë(ônÖ)׿e¡ žû¤)Yeo´jÐ@"‡æ_=žauc/Š÷?n²Íãq#=y÷øi,ŒiÍ®pI‚õ’Ñ‚\¹7ië5@Êõ+ùa\¬$ßGFÁ:æyù­¥'öô¤ÕÉÍÈ-8ƶ;ÑZµÊÙË× `P4’•Û$g;† ¯Çm…öj]*î/NV’ž ψ+gJ¤Üü]V¢ûkMÕ/Œù÷´>û‚÷– Üx‘ ýz– RÓZ4#—.•HχsòÝ‹߯)FŒo£"÷Y·Ç賉£¯áÍ–kÙ4³å¡áltÎ>´YѤ“‹# Ÿueåú'iÀí}%2òØ(˜ŒZÊ|+¾½d³½ËEÇÏìí•éÐÜXEúX+ñyA¥DŽ_ñ6AºÝ @‹ #V®_—–1˜üz´‚Ì‹Ÿ‰ÕY{Ã4÷‹dÓþ Æ»‰q¬ë€>¸uKI0=Sr%Rn¿ýŽ˜1U²Ý¦M˜éjÍÖM'††J¤ÉçLq«U+7ÿXAop4yæy{Ä 6fÿâ©NßVŒ§¿œ9jÍ]Ìvtc]FlFÏ?á¹ÐÆ–ÙgYé¸Ü:éªÈ#{&ÁáR=v×ÏK¨È²•ØütXÌrU’–“Æ£®O¾ Ýì§Ä‘ýH¹þ–NÅ1/O‰l`»ÅCCØqvµá°ók²¤ ÞNýÈ>ížä±yìRdzXãáÍÖ;¼é^ ²¨[À¯+ׯ¹IܿϜ¶‚¼˜‡Év{Ù»EÝö2íµ¦Å¿¿2Yc|·ÓS‘í{{ÀØ»/+·?kÎLvìrù0„˜÷e˦üys¯Iä¨óq²è +7?àJ+¬ðn R犌_ ò/žª®ÖlpÜ+ÈoaâO_v§ëAŸÏ•HŸN¡Øº½˜Íœ¶ýn—²§#öàÆ‰xÖze<º¯NRg`Ç‚µ‚칡->lÉ‘È|]#ø¥¨H¹þW„ý¿ŸÌOIäm½­Pi_ac-²÷9;àî<~{¾6ƒ*±â ñÏ—~¤Ö-<~ßD"7M.ó}òY¹~9µâÑT3Y+¿ÆA{S!;wH ôûÇf®}&Ú›G±ËkøCëZ/¹p[0ÖÖªÏÊþ*2[‹ˆS– KÚÚ£ä·;lÀ\|)N•ÈÇ){¡Us?+7ÿDb| Ý/Èź ¿ù´‚ü‹§zkÈMü<$ÈÇ/®ÃÖñkø&½žKdfÀ)d{¼fu" ý1[v­¼Ÿ»(É5pÁÅ´LAºl™Ë×?ÙQ¯‡Àf¹‰Š¬ÐwBúÑ ‰”ë?ûí.t»zO"o%yÀÃü-ûgÞ:èo:Ã6ÿí²±ìûv¡ØÐÉP ýá’v˜ –~ŠõT¤ÍåÎxŸU—•ý­ã§9å µAš¸(üŠ R×å¸èþÀmÚ.·í÷IdeJÖWê(H¹ý=ê¯BaÃÞ ƒ¬€I©ë9;‹›Œ”ÈÍŸ²àóæ¶’”›¿iƒ%>Mÿ¨$…ù(Ø “È¿xªÕÍ ðsCˆ Ã{]ÅÎá¬îo%B;½“È:ràÖý »aJ4ýäׯ°Îb” í ¸jò¦¿!ÜtÛ°÷އ`¹½±DzHÃÙc)×?wø>Ììø["SNú¡wf-Ñ;sç®–È­ÊXôÍèÈ&ëzaÌÕG‚ÔÜèŒÂK @îêaüÌkÙów0ŒÈÊõKŽÁýÆMAnìšimYëˆ`1s» «¿ðQ–kÙÖÝ#PÒ.™mq76)·ßáÍ2øº™‚üüx ®}êÆÞ /‚Ï#³ 2cþDSró­UóàÜè·D>ºF} Täÿpªê¯M//Á¼CIªçSVÁý…!Hõ|ÎÇó°n2LAVµŸÇˆH q7©ž;.8(b뎤z>hÚh¬µYÕýKï.Bj§. Õó­î〞 «:ú W”L¶P‘U}¾¬§š"wå#9Ò¤>¦nN`ïõj„â°A9y¢&Ü:ìc½nÅïù‚¬ÞÃ}ê7Ù3¨/j,l!‘{ Æ Ôá«Ý7T´gÇ^6¼&2_båúÝV€€'E‚Ì=~Cµ²íÃm f®"ó´&a†õö®„sÙ& ïjú¢óCK'UXøÌ^}GÀ°¢@V®_ëÔB‘Y&‘!ˆz)_ÙÑFÿ¶ /¦gÁoÛvÕ¿¿": º§$“ôRp1m0!ªX:}õq©áVnþÇï1h^0Y"ïW‹Æ°ÒýÈ¿ù¬Ú¬œÖ¯Q¹¾‰ûùšluÓš°žsN"ÍîWÇÏGìá¯^x£ß dׂP´²b£=—@G£†Š,†l€¡m¹)‘_'íAí/Y¹þÙc/ã‹÷ A-ÌÁ£Ñ¯Ùå— çGW9ñËfX´cß6÷@iÈSwÀί[[1Æ/¼9¢ÍñjNoV®_ÿoÄ‚û$²Ó¡JQgg ùæz&”jƒ,ß§„åmvWK¸lÛ+Èþ{'!ôd>+·Ìî 1ìJS%Ùõ›¿’jÃÖ+îŽ2iBy¿d†-Ü(‘róç- Å…Z‹ù0>«Üfÿâ©V<*úV?û‘V%÷„v¤_yoVM,ÔP‘αšhÝM‹ý^b­· ·¶š…ºõ,X‡‡ÞøÖÍ\EæšFÒ: öýªs(ß¹GI†¦ÝÄt_kAÊõoúM…¤þïù¡A:öÍþÈîÞêŠ?½ê«È)'öáì‹g9ý¨ƈN jÏÀ´Úì§Až¢°m–5¹)"P|±o ‘²ÏŠR]œr¯¥"÷û4CëŸÿ©_ÿ~·y`F*ÆœlÍ®>×ýZ¿d‹éWÄ ƒ×X¹ý¥wˆÔÅ땤C÷ýââ+_6äÐ"¼½øT"ÏìÂÚ-U¤Üü‚C{‘a¤r´©3ú†ô`ÿâ©.ýyMØ~ê¥$­Ç^§v{³^áP–ßREVè·BgM}vṑXhe2þ“8UƒÝÓ/~=;«È³õPsCvæŠX½'S‹S1¡îV®ÿã”SöQ ?GïzØóVp‘Èš9ÁPI£Ù¯‘ðŸðX'êc$?öò–ZØí"‘ þsP¿åQV®_ÜQC<¼]SEÞQšb“^uvIHÞOkÒ¬2[.´b#nÇ›[û¹cG®X¸ô‰‚”ÛõÝ[¼ý§$Mzïò²Øp?TšvW‘íuÂaShÊÊÍßn¾ Ïvõ™®1î-Ø¿xªæÓE\@–’Üw&Utÿõ•Zf»íU¤Eªbÿç¥9ÆÔ>'È>Ù¦HycÍn®v_^^•ÈGE‰È­6…í4È»ç¤ Òü~GÔjÀÊõ¿t;öoY¹"ñã_²7kFáó,%i¬ŠCï> Ò¤›ú¸Vdûµ=šÒMI±Å­qç L»¶dåúi^¤ü”ÈîcÑÌøkuìB«ë€ôN<€²nõÙȦ:È– $MþLÁäo.)·ßýåqvY‰’lßh8‘ý‘Ý{>¯Ú©H‹ñ©8té‡DÊÍ7z/x×ä†Üž˜fþ[ñTm$ˆ] ‰¬œ-}èʦôˆÜú*ryõ ÈWµd£G›akmßtré+ ,‘†‡b ñÄ3L8ŒWcy ìªÈn[]"û%‹ä»©¬\?éú|,Ø{ñkÝÿ¼ˆÌ3SØj¿’Ð).‘ÍiÙe]§HäSÃÞøv3}|òìšµ&û[ßEÑÒ‚”ë·üÖà܉\´ÅO^gõfzbbY¥ Sï9aTû§ìáßû1º(["³OœDÒé{¬Ü~ëOẬ¦D¾‘<…ÎåFìÉ–©x&‚Ø­DˆçáJRnþœ›]°`vˆ ºAäÜRÿ骿êv Z¿m$R=÷»¼3êk©Hõ<§÷`l>Ÿ!‘êy­¯9ë’ Õóå3k"óÄ+‰TÏåú¯¸îy‚TÏOM‚ö·2AªçÕ'*p e5©žÿˆNƒuoAVµ_ƒ'Ì}—!‘êy®r"B3ùÞ?7·\–Ȫîï½Ð‰zMÙ‘úpꥰ&¦ÍÆÅ:)×ïzÙ@øÏöä¾Ý=áÅ.Ÿ‚fIdxØq,½÷AI®<‹Â†?Ùµ®cÔ™} RnH©3Ž«¯"3µ\ð$C‹Íʉ›N–Ȱè7Bç°róµ=‚[W®JdBE8Ü aÿâ©–ÍÙƒ³‡$²Mª^ôôb)Ë]{ òIÖa»$SAêŽ<¦5TäŃW1&ê¥D.:Øã_†³ÓsÌÐw°†Šü¼ôœ°-ÑÍ —êŒPƒ)׿‘éNÖÔU‘ßz¹áCW6P;›§‘GBÃÐÜ´ O.ë‡q6Ú*Òâù ô‹¬É§ûcéúJ‰œ˜š†ÆOë¨HÙ_mÛ"xúNAÞ*¯ô©+ØGþñ¸à—Aö…+#d¡ÿŒZî&È”À<œUÈÊí¶rÅ̯UäüÅîhüC‡ÝžÕkžJ䇞³ñ¼2‹•›?õzº-xiMÞžÚÁÛùOõyùALná$‘qÕãë‚Ŭù͆pºÛÍš4+ÕÃø>JònÒu Í:#‘‘ ®ãì¾M¬MýÁØÕ^EN®;ŸÎ[²#}ö`êèc©U&AkÝZV®?á… ¿ŒUdÛûñae¶A RÜ©ñztOä°µ¶Átɉ ÷òFHA{Q\Ãçò|vCI*/udåú-Q! Žä¨ß÷ÄÜš]ÙøÌ`˜¤öb§Z î¡kž‚ÎßÞ³×¾ìCiÚ-VnW?O´Ím¡" ³¼¼P—=Å ÷}J¤_‹hÄ–è±róGAùÜç‚l2b?J´uAþÅSÕüŒØv#%òÂéïèÎÖN3ÃlYÉ0|^:‡MÛÍg¼R’Í÷+¡?ÝÚôŽp„Ï~…Š“¼.û{²‘ï#!°›’̼zÙm¬)û`æÁ›»«H—‡qD×”½¿Ö«R¾ rúV{,{Úä ×G01ÇV"w܉×ÌUJRçM Vyæ)HŸ>î¸ï$H¹~†Ã²EŸæuY¾)I<{ )È›ûÚ%EZ®¨_r‡muo ÜÞýŸîmPôÍš”Û¿è½7]i£"›µÞïozì« Ø[ε&‡-¸‚ûË Rn~Lã]°óî²ìáM²fÿ‡SU½‹mÚ©ž;äÌÅö»û$R=¿v%Lµ©ž?_€/ùÍT¤z~|ÊiLÛ%Hõ\®¿2:lMU¤znªœ‚=gÚ‚TÏMb’‘ñÍçc5ß^„î…‡ÙzïeÜ©Çêü9†Ÿ±ìâa©0½äÇÞë+¡~A-¯SPýnmV®ß⸳h´QGEêo–°o¼»Ïq<|_zJ䈕8a|ˆ}÷.Í=/ 2r£‹5 AÊí7¿„aIó$òÓÒ£XÕÑ–½”í†ƒá ¿m—âµcVnþ¸_¶(Iß&È›yݶÄSAþÅ€O¥W0Ïè± '\„wvû¶M N¯ÂVðBþB¶Ø3 ™¡¦lÚ¾HlùèÄF¯+Dê°F¹…ðÌŠg{ö‚ÑC3y\9_n]H¹þ¥ÚàÕjµ K%Ø%º°—¢.âÍ´¹ùÁì<úxßP’ Îç`š–DZ,D›mÍÙ¢ëó ñ5^#\L q¯f?R®_ìÎlÜR[EîLº„š?+$Òû´ ´ëemøBÃî{Ã3 }KZ€,ê²/çý¤Üþõ߃±[[Hdîº0xØwa3ê AãV­Ajéô@bÜ]AÊÍÞ÷;וȈµÚx:0žý‹§z¥Z6­?Ò¥“ ÎßX›¦;â—Î^ ^ŒŸ޲‚öâú¨Cl^³e°íÈŽ«q5ËÙöc2°õ³&ÈS>;àª4Q‘ÉŽáâoÊÊþ§°: ‹—û颛Œ§MÙÓÎâƒÏk2kU g|RÞõï¡úÉJRgÓCÌ~ë  o„·Eiƒ ‰\ÙÃRX)×Ïáe.œF?•Èç­ó¡õñ2Û§qÏ=ÈúßK€iwV£ðåånA&õì‚ù'JRö³ÈƒœÓR"Ç™EcOJMÖíkGÜë1D¯ÂÍ`X>QIÊÍjÓÚOª«H»ÝeÄþÅS­Þ$Âå§ ëçœÆž¾Ø™wÆ¢é™u¬ÃÔ~¸òMŸ|Ê… ]Y­G 1öòv¸ 6kÌ拯î5Ùãç°uz¹D†/:‰¶½£”¤\ÿFA±Ðo"ÈsWBQšÁ¾úxµ§²!.{Q»Ÿ/{qÞ} z߉Ýô½Û»°Û 6 Ï(…Šl°=¾­Ú°rý:‰Lž‘(‘96¸\ׇ5 ;‹é;—³ãZgcÀž¾ìô”NpÝz޽sΟl£"åöç­ÅÖ¡O”dâÑx¸O<Ëþì4Éqû$r@æàÆVn~—‘hÙ³Šì™¾×.ýç_<Õ(›LÌש”ˆÍcª±[;cû’= òÒ°Ö8»ü~YzõŽÈ|–¬ —Ší(ÉtMЩv[®ÛbÍPwÖÑøš|ÛÊï6x󱜕ëÿ$=W:F rÕ®ýèÞö?3.o„ÎÊDöàù x¼-ƒœuI'ãØ—‡Ï®K6kk‹8ý@‰œðOž9« åú0ÉÇïÊùYþ.ý]ØQµóªY ëzþÇw+ÉPýµ˜íj­"[gúÂú¨9+·¿GJ"|Žx+É ãÇá7w{¹çLÜTE+ŽÃ¯ñ­Y¹ù³S<ÐEÏXE¦¾ À;5Ø¿xª9¹1¸Ù¶ÈQÂá`¥Á.wÖÆE- ‰L÷©‹sï²õb‰íó:°ã‚ƒ…¡Â‡Ú¢ŠÍŽ*H³ XŽ©-‘™Ÿï‹j>7Yhá'2¦Y³rýÃzy`Cd„ K9¡AV0›fŠ¿$Ö¥™ö¿Ï{§Ø}Ç"±Áî*»³ñ~ä´ÍbóWyaEÓûlÙ)[ŒøÞ¤\¿#/a×Ú«Jò›q6ìôس îÂéëھ䛽÷ Yx+È“G£ õ©¶Š”'ab'G‰”ۃɘºëyé8?!ÿÜI';zH¸ï©¥"#þý8Üõæ#‰”›¿pn8\ßÇHd±[4Âr^*ÉÿáTÕ_sïC~ÿß‚TÏŸûÔFȱp‰TÏŽ8/²¼³%R=®;‰TÏsݦ=.+Iõ\®ÿ€>k°|K€ Õs“±/…ÿï`Aªç‹Ã7ÁÂ:Zêùñ ¡×±Ȫö‹8®DÌôHkR=Ÿpø>Æ4¤zîø#¦ç‚dU÷'7?†ë­IõÜ81ÇîyHdUçÿ:—F¥ ²ªï¯Â—UGz|›l;)I»žo„ÑÇP6oA,‚¥Ùl÷qQðÞ°BAš:(ÅÔvµÙŒ©Áâì…ÇJ²úðD;hŒ gF_y«)HwOW‘rß“Ýü¢H8 ^"‘rýÛvÀǯù9"µ3¦Û—³¿P‚²üf¬¿ùXnl©$ƒB`ûÎ0ƒœ=0 -‡7äX« X&ÌS’F ‚ ág'HÙŸï k¸!HŸb d|sd‡×ÕDEpc‰T&UGëýl‡Öƨß.QIN<Ý 'çK¤ÜþþÑihà=PIÞÐ?‡ËÕ®eÞ‰W1÷[Òé,î®×¤ÜüÔÅø•ÓBa{nã•á1ö/>èä=¹¿?(IMÍ"d‰¾D¦u ś舘„W¡nìÖQÇDL”³DjGœþ5³Ù釪ãô» %é{²Æ>]#‘f#áÕ÷$ÛãåaxýYÀÊõ/?Ø{¼ª«ÈøD‹ÿÇÞ}€E•&FÄŒ QEIÒ§« ¨˜1c#¢*"QŠDc796M7§Í09§1gtÌzqgêsÿÞ9—]ïÜ]¾çéyê®*°ÎùÎiœ|ZR‹9Aÿ4œ¾LLNnz½›ÙkÃZ|0.šY%= —Ød&÷Œ=ïd1÷h¿ÃȦPÚvhs¿ Ž3mpÕD‰˜t[SõIyro¼&ö>q‘Y?ºv²o©"mæ†câb1S¨~»@jl© ïËsqjìzŽ4³Ý‚IÖË€48=‹Æ2…òÛÃ3±7\ÔN†š­4‘ü£êðøLòqåÉ÷þg¡Ï˜ÙLì¾]‡å™Yo öq¹Ê¬úô)¼›ýš'÷í4Äþgª«È†&£áÆ<ÙRûϯg|Êcsó[brØGïñH¡þ§ß4zØDE®ÔwÅŽfÌ…OòЯl3;íÂ.Óc˜þ%£0sÝ/Ì·[íðЖ—Lë—+¡Ž8†#´J‚™Ýó¤P&MlQãy¼œ´ÖíˆG$…¤ÏÖ˜4³ŠŠœ"nˆkŠë3 NÄ&_Û1óm³q`ÓG<)T?ór6¶”·“x# ^Îc÷pÂIw€|߯»h3…ò;MKÀ½w´‘‰ŸÝžùGÕ$«,ß,çÉÇëJàéô&nŸ#úòâñ±¸ÿ´’á5\p`y¸Äï|Ó®¥ØðêæíÅ{Ñ=þr!¹ß9 “ÊoyÈŒQsðJGc¦Pÿ3껡VÍö*Rrʇ?°eŠo®Å«¢ ûœÅ9>ñ̲šøÄöÓ$û ¼Ö>Î|3·.î_z•'yÌÄðôSL¡þFØ8ãhÍ“þ]pã¢yL£ê-QúÐTENõ¶Ge7+¦Ñí´Ò3åÉiÛqþˆ:@ Õz$Ç™ï“Ç6¤¢É æ&÷}Ð÷žU!¹ØL §kÏáI¡üãKÇbB× •íÄøhA3æÕêÍÎÂÄÙY<×í4¼w9vnOäGÕFò×ÀøÒ².³Mßå8àæ^žl”‡m®Î`©} 5†õ“^ygññ‹q@V»ìˆ·£Ö3?ÖtÆ«¦<)Ô¿Ìp*vïà¤";û¢G©ˆÙßÞÛ~^dr¯NXeöhf»É0qFµ:s%<é¶µ*Fï×Q‘BùSB,ÐP¾ ´ÒlŸÛ­æÉ8ªÃ¿^>©çy2Òå6Xï¹Ï|Í7ÆÒ¡µ´®­‰¥µª3]>ì¨-£ IwóÜqNÈ€¦gð0·Ÿy³ö!¬Ý© ’Žû£µ{ù2D‚ò_˜BýK BðÂ1±ŠŸu×e åßîÝŒ~åIýjnø¢µ©Šü7FU}Õ×zoyR=Þ~â]8ý-êqÍ R,ÉØ¤z¼ÕÜ,,ª¤z|Cðqt^t—'ÕãBýÛ”,Çs;8©¯Âß~ËîBR=^ÃûÄ·´àIõxÔüÐ×ÉŠö7ça¾«§"ÕãOžE¡»g+©¿ðëj8íëVHV´þ³Ûз¨êq»+pÀé“êy'…껆ÅáÚÁ­‘lºeF×ønõ¸’’'¯eÆã—å®L¡ü­ŠîÃëíI…dàÊfÔ|óž\djõn‹>×Gò—QáéájÌ–ývá*í€|$™{˜Bý—Lá±…bÒ52$ÕÒ:0Wõ»õÝ6Ièºó!sju; ñʸØ/wÔ“Qõ:c€£’yí«â“^êïŬ½¸ôþG +ð°RÉ’G†¨Ñ¦9óðúhîX“iºm.º/¼¤áÅVX§å3¦Pýs÷£Ñ×»%’öZ+ñÓ;S¦×ôí¨Ò®Ê¼Ð-wtlËÊÿüR&kMàI£wù¨˜XHþÀQ­×|~(]“ۋ7á"«YÌeçðeI] ‹zœÇ}™:Ý·á¸-õ‘”®Æµ:0.@Ö¨Ã@šúÄ˱L»×ePð©'³Àv3èªË“Bý_©–Œ7Þ8óÙvô®7šiá¸÷M¾É<·-É1_é„fúuxraz´ŽØÄLkú¾ÜªdÆJ<[Åž'…ú«Ù$oîh‚äˆéè9©%óBÙWW}¤žÃKX¼ò s™ÕaпŸÆŒÜ ^µJĤPý€ÖQøÅ©’Ã.AÓÆÌu3ðäHgæÇ$'4½Õ)”ß?ê0Î|UL®¹uãü£jkýë÷åÉâmØt•9³_Y)îµH{ÃSèbZÊÌ€WDHæZºá”<'¦‡Ï8Èé‹ÉÄÞW …¶.OÆ ¸…mŸ3냆ÍëªH¡þ#=¶àœ€@ ‹&®Ã–·£˜§ÌÆ ëƒXæªÝ0æ° ³Há…Eg.ñdBæR\<¼šŠŒ¿onNâÉ“帾ïx¦PíìÁD¾=’þ±A¸-S'å1ì/X ¤µâ9ÜÞٕɽHCV<™æª‹ã—d3…êÏ[ŒNW U†¡SCfø‡:lS“é+ÿ“ý²€Ê¿ñÝ~Òñ#[DÙ¸`´ ’?pTß=Û¦yŸ Iý®{pŸ”éú¦Åï€,<Ë£þ'=$ûÙ[¡³±9óLËú9P›9òd#ŒZ»˜'ß6è‡E†;˜IÚëp†TÆl²'k.“Býß7Z/ú­Òy[8ÞË4Þe?.“'§¶Âv’e [°ÿ½š*2¤Ojè3;÷?‡&¾!b²ÑÄ3ø©ì Bý9dmÄÅ«:"9·~4&ÿî¦ÙŸá‹D)&¡³j4~^@†˜üäO¦×Z‡Å²H¦Pý6ÚÁØul#$7¦úãfG¦Å‰ª8hï 1Ùta }t¸Ê_mI<& ²AòÀÆ,ö°dþ£ª¾žuHÆüÂÖ…¤z\Û(ý ©?™¥‰V_ä@ªÇg¶G­¥ñ<ùõ›­ÇÝ^©êCƒy¨Q#Hõø“S¦x~á4žTg\,¿}óýÄ“êñ_kf¢C¼’ío w$.·³CR=>¡¥!êkä’êqóì4Ô8c""+ZßÇÇ¥Çë ùß¶;žXÄ“ÍÿàíܲÒÉŠ¾¾oVÝ/<…+·ŸÒÁ³ójßb6ßûE_áÉ»õÍñÉPm©Ú)Ãasë#y3n'ÞÞfÅ,N-E2O¾û &yj©H×ÝÑÇñ䜵kÑëp S¨ÿ³çï€ßR-žlåqªGwg^ùzW~˜dñ¤3å·_<óp­(žÓNL®ŠÙí"’Qšcgs rŽÏl˜¾ž'…ú3;ºòU…äƒá2¸}ÿ>szã ÈgÏãIyÒlq±sì¡xô±Ìrïû`4mPI¡ú)vÁ˃«Œ N„ŽU"™’ìpºå„BÒvö@ÌxÓBùÏ÷݈ù¹K\x7CRN1à@ó]GвÝK û/=ˆ×J¿2‹f:bà¦*rjl_ ¼ÞŽYt1%uœ‘<”áƒú­í™—·µÄÐF­U¤Ù‰!èЬ#ScÔ>¬SôFI¶)<‚›)ÔÖÅ«páÙ"žÌé}~ᓘë;Áƒw@:P`ÐØzHºÝIýþËy2íܨZZÊœ· }+a&äœÄ“uî’Býô< [×Ûód“ÙàtoæpÿsxïbûBr•ä4®È&&Ÿ}è€K½›!ÙyÂ%x3ðBõ7}Œ…÷æé^wH»e- Ã:Wïòd‰˜¤[EE åŸÐÕûy>²ža]L«…ùGÕÚ`/vž«ä)7βÐg.‹„}ÁQEvŒn8f-ÞhÖ Éâöx]ñÈ7K£Þåö*Òº µFÕfF5ÞeQ–HfGLÅfYm˜Bý{mûîãÉI…Ïa‘ï fçuÉX{Œ ’³{ÆâÆK-™G">Cޱ¶Š¬uà 7™5b>°ºˆ%‘þ@4UᙵºH õwÉ[ ëMæÉ„ÀF¸þ—$ææñGñØ‚Z@>½«ÂÂÜ‘ÌÝ&[¡å“ÌÉóCZЙBR¨þÅýkà“…+0ë­‚a{¬™tyô¨z'M;‹ŸFmb å\Yo&yþ×hp£Ïü£ZÒ/uÊïþHÚ™Øïª³}·8ì<§"¿TÛŒf˜â•Fh¾l=ßþÍÎÀ"1™¸A‚·Ý÷óäºÙi˜æÌ--Í1±ó §®îˆo’Bý¿,¿™ìý‘'=;WÇ:ÛôTäîÂ¥Øu„1’Mfã]“:Ì=ýúá±×†*Ò>m>­Ãü²ÛoŠdÊ«ØÑï$BýuùÔ|ÌàÉž¡ˆ¯úÊ™«rP/k9µ‹wã×׉ÌÞf¸ªÚž47óÇÎ3¤LÁ]§ÚràN6r䯥0!YƒyoãuŒ+I-$,ŸGÖR(ÿÞë‹QH4ÚúÛÃyòŽªVR2+j„¤¼Î.4ÉiÂÔ,Ù½»¶S‘ŒÓ±Eœ!3&¥Þ Î.$[íˆAïæòä£{sŸ.&o<ۈ݀Œý¸[÷=Ä“MŽð8r”)Ô¨[]tïÞPEV;d„ùóL˜¿„¸c­á/€<Öq­CsVÉJôØ]Æ“æ[ðÞ²tæÜ4\Ý+#K‡ã’¼lžêoÜzwôWð¤Î¡ihrNƼÑ$§Ú§¹õórÜñà»–}“Ðú|žYš‡)ŤPýɧ"apÂi1Y-5ì6$07-;‚³vój 6ßt…)”ÿu| X}’'w9[â…/eÌ8ª©“1:ÞÉÎ鱸<ô»ógËP3^KEF.D×É—yÒx} ú?Ncn©µ»,ÛÏ™ aßFL¡ú›mì±Î±{<¹ø«#¾ñŠÉX‰.%«Œ:>·öÞÍÊ?°–9ž»ÿ‰'Ÿ €+¬[«ÈxPÇM÷š}Qãvib~ä°B²é€«Ð°ùs ͇=‡ö1Ëôì°©±E!¹°´3úçÍâÉC­`f¡+G ’ñY›íÌú’8œz«©Šìs:·ý:’'…úŸÔ9ÇZªx2$! —›oe®(S`‚ÿ+ ¿¤eྒ6²8;!‹'£š¬Åü˜"æÐ–θ«:¯ý­0ÓfO õ§ƒŽ+7™qmž²È`Þqê‰MÝoódõ‰¸wð5æ—¤wdXS/l½â ³¿,7ï)æÉu–cíLæûýñ[³ý‘l¼–)T?¦Iw4rÐU‘ë>÷·õj1Sä°{´#O¶û|Ч\b åTV~vn "'t)«sŽòäÕ½£>@Õ#=x²çõ÷pÚÀŸ™·:ög“w'%@ÉÌ…俉[1OOGEîþ”‡?ò¤ýR'Ì\aä„áÆh²Ã§ÜÕûÄN¼ &-NÀÞ Ý<)ÔÿÒžÙ {­<68 F´gºàƒ½b$ㆠÁ^M;2;Ä“ÑÖ9ê"&à¨VÛñºnZÇ“½ö|ÿ‹)Ì̹ùP÷“óÅÚ«päLS³è8–id:Ÿ¼„3·Nd~l†go­aEÛ£íÂÌëέñrº¹ŠäÜ–ã”Ö¦L¡þm§¡É¬tŽ<3HН_ï“+WÛãCgS$ã ñ™D›)™v7?mä8‡[|w3Ÿ{Ȱ–Y,³[ôblk”ÇêϨg{ÔiSä“7Ð^ïsòœ(1–’P´÷Ž<Ï‘1Íøè†’Zµ7cZ˜#S¨>èÄ1ºMU¤ûé±hVԜپÖR\Ý–Yß( ¹†šL¡ü#‚S±Ú tbÐÄ6–ùGõØ‹ª¸aÃAžo¤PÿÆîDùj} ÓÖ$`„KgfîºxyàQfkϪhøf!SÕè(n;ó’éR>’“ ë#¹)³#v )&]™á‡I«xR¨¿öŸ5Q+'ÈU©÷!{×&æÊöß²&óqü.¼Ä„÷£ñÓE{$srŒ0¯à3BõK÷{âDS™xcžwoÅlkœï-cx²kZîíùXI å¿tz*Fé®rÓ {|Ù‰ùoŒªú’ŒÔÇœ½/yR=n÷^„KNð¤z<'õº\Ü ¤z|ýä]Øq„¥ŠTÏÜ:¯ŒåT õ÷Ë&ì¿w&êñóµ1ëÆ!1©·o„~MZ ©oø«æÌjª"+Úßþ[Gá]¹@ªÇ½:mÃÿl Õã%aºpÊÈŠÖ3õÁ©&mT¤z¼nõ­x¨ZO +šÿÆè:¨𶀬èë+ðfUTsSœ`{ž'#fZášÍט_æ\ÃZ_ˆÉ.¶—Q[ËȆž°†·¾ŠŒ‰‰¦í™ûªÀ q÷ü<Ù_ð‘ÌÒn®è¯äÈËf¸ú@Œ˜êß{’ Ž®äElbÏœÙu¾Þ|µ¼4_hÖ“Ã>`iwC9k{W,kÕ–™9ØÃ#Ú1§WY‰ao;0…ú«ó¬x¥­"ûÖi…¾¥˜Ã[z¢$²ÈbëX)3rÉH|¶äóö+^Ýž)T¿°ÎbìšË“Ub–`››™¢‹¸¡{"3kÄTÍœÀÊï¥q Íg=“K–âææÙ@þÀ €UD8}ýmžì¥è‰ÙMa†o,Å©“³<÷î(.òú…YoæœÚ®“ŠìÅ%¡Þ ¦«Ê ÃÞ5“c’;ㆇ¦Ã\™²›n¶2ÿA26:·ÙQv¸Î)$>¼„MŽ9i«»'&kV÷ÀOVQ<)Ô¿N‹0fä©BR¾k;LO±æI· K Œ3Còm¾æÔe¾ÙŠ”Ý€¬ñp9ÆË62OYG­"9°Ö.|mÎꯃv<š|µS‘»ìÆñÇ,˜>ãÑ«5Ï“üâèw9õ×8¾¤’þNcQã`c¦Pý Ÿ¶b‹/ xÒ¡f®Õöbƶ,…MEW€l|)öfµe åŸÕ±ï ž¼®=“6ï`þÀQÍ_» ‡gó̹1èè¿Ùyâ@\7æ1)©bœ¢`*NÇ`µØ æÑý>Xp=‘yûØ)Œ™vŠù¬g¦n¯¤·ÃŽ.äÉ»;Špκê@ õ¿ø×Ýá¾€'¯}Í€:Ö‰LígÁ#é1/ÞHAG¾ƒÙhÚ Ü¹'Ùº›Gîb¦l:€’¯›x²y¢ ¼JĤPVáxÇËHE¾‹Êǽ.úÌÞ3¢PgýKž¼ðxîÔyÏL”›cɺÝ@¶;fާS*I¡úk4wâ™OzÜÜmú2áÀ>P’˜ÑáI¡üç{ÇàºÖyÒùD::;§0ÿQU_CŸlÀ݇×ð¤z<´® Þ]½Hõ¸c¯ž81ÇHõ¸UL<úÒIõ¸ÙËÜdQIõ¸Pÿ»¶ð`ຟ'Õãy#6ÂÔ ÕãÍÓêã”Å@ªÇµÖFဆq@V´¿Q¦*L\RÆ“êñ Ñ»Q'²Œ'Õ㲕c°_וÍËs’Ëx¿ñƒýú÷ë7s¶ÕLßVcýÆOðò´šÜ±"Éþ`Y[[;ØÛ›|ÓÑ¡ãß´¶ýíó¿-;ÛŽ&6åO±µv´ëhogbmãØÑÆFÃÄúÔ\s}ýÆû”·2išçlOï?~^ùÓ&Oþ“<¿}+&Ìÿ”¥Õ°ºFU ~ã'š l2Üä÷õíkºåÛòÇöòÇ·ÏÏýߥìâîîöû‡ß^±©ü¡þÞv•ï_o0qÖL«ñ³g—OÛœ¹ã}Æ{ûMóöÔ˜ÓÁÚÚÉFä`kmçäàé$ê1ÍÇsò,‘MÝì{t³´íîdgißÑÁÚ²KG;'Ën]\ºÙ÷°urr²µÿ?•ÿšõÇÿ<úë<þmm+ÿ¿b•ß~”o5Uë–X뎂ÆßŽÒ*;ÊÕû[„¾úíL ¡¥¡aê÷û'Õû\£þ´™Ã<§M™êçº`¶§O·Y^sgÒ´êßU~ËYþ¨¾lûXCãeùCÛÍsâ,ŸI¿¥è[Õß¾ââé;ñ÷ ߊÔÜ«g¿.c]G ìîÖmH×îc{¹üù¶êºtqÿÃ`“ÁÝܺ¸wsûgO2ìÑ·Kϱݺ¸wï9ÀmÄ=«i¯~]zöêßsì°î½zººÿÑÓªüß¾o[³&}Ò¢ü¡Ûkà,ßi~Ófyÿݳjþþ³ýÃ'|{4øžþoOþöq“o¥»zÍš8ãï~¸ÿR rýw,¡ë?ÛPã_¹þ³v¨<ÿÿ%«òúï¿zýùõß8ú…;Ǽþs¬<þÿŠõÛõ_•8?ºþ³Òø§×U¿ì 7¾*¾]õÔžTþú¼þ«¦ñ?¯ÿšo?«ñý‚ÃWCýúoˆÆ?»þûöø7®ÿØÕX•ß;j¥ñO.°ªiüÏ«°?}Òü•˜Ðþo÷jüKûåñÿ׬Êýÿ¿zýùþÿ#Ž~áãßѦrÿÿÿµ~ßÿküÑþ?FãŸîÿÕþnÿ×Õø¾ÿ7ôè3ÞoâT—ÿ«Ë®†Æ÷Ýn‹†úe@€Æ?» ø–åÿõÛ@ÿ㛟øzAhÿÿ'Óé÷?Ö•Çÿ_²*÷ÿÿêõçûÿùQ ÿþaÿ·³¶«<þÿŠõûþÿíüŸïÿÚÿtÿ×ü»ý¿ºÆ÷ßÕñ6eæ?ÙùÕTµugïûÖ·ÓÅÿÜù4þÙÎÿ-òç;úÇŸþ†¥jåoX~_¸ÿÿök°~.? Æ·ã¾cÇ íÿv•ûÿ_´*÷ÿÿêõÇ?ý~D ¡ãÿÛñ¢vüÛØwÔ0ù1ûD`ý—ÿ|þïÞýGœû¿­ŠŸÿl*ßÿùkVåùÿ¿zýÑù¿üèÿ!çþo«âçûŽö•çÿ¿býáùßµ×`÷n#~Dáúß¶cÇÊóÿ_²*ÏÿÿÕëŽÿßþ³ü+×ÿ6¶•çÿ¿b þý±îƒûÙü{5¬+úûŸòƒÞ¦òýß¿fUžÿÿ«—ÀßÿøGÿŸÿ6ŽÖå—úêç[k›Êÿï¯YûOÿöý#u¢òàþª~Ù}O.,tˬ:DáNJòÐÅ|ضLA6l‘×Rç+ɽcr ¾Ñf€WYd†—C}³¹íµº¼c>vˆY ”¤Pÿ7§e€áêJòìåLü<ˆi¹A ëZQãf§€Î%'%©íšžÏb×͆uõ2R¥°±þ9¹ '+I¡þêõË›nJr^Š ºEZ2 ä±¼‚¼™/‡³}šŠÈ)/Ò¡K§yJÒévoÔ)Tߨ“ –©DyôpøžíÉt´–A×}œ’|Y(»gº R(I£L€ô@%Ùèv.¬ë2Š)ôúÊù¬œÏÿäù<—›ÎÜL޼Ÿ— ²Zó™ÚirX®-R÷Urº®ªˆ|ò‹ ¦.n¥$õsäp®m[é¹DÓ:/‘OïçÁ¹E.™ Ã}³z rv`.Èí&p¤Pÿ_údƒö•J²Qf¼™;“)³I§2o¦¹gDNb6^Ÿ#Wôe:iÊ¡¬ÑuÉÅgCçâùJÒu£®>˜¥ …ú›¤Ê‡l¿/ 2u¾¶ñë˜ý«ÊÁªþ 9r· &Ù[qdÝSrˆßÛÊ™œ6:– âH¡ú»Ë‹¥n9öd.¬Hõ`NY—Sê0;uÎÑC}˜Bù­[Éasb92©ü8»6LDVÎgå|þÌóy#= 6n äHיЯ4ˆ¹[_ír.‰È3;e0wªGžw‘äNriœŸo;÷âÈÙ³³@³sæÁTxÙnsÃð°Ðp`Ö‘IAêvYA õïü%#”äà62pÝ.ff-Ê…z'Æ35µóÁMaÂú§våÈÈ´4°™<•)ÔßÙãr(™»²€ •ÉáIбˆt)Ê…qV£9²ÏÊlÍù3 Jñ°ÙÌÁîRhÜu‹ˆª¿­W.ìu÷äÈÖ{s y¡ÓòD¬×ŸÎŒ?)…Ã=ï‰H¡üg,dÀû‹9²ZjÜÞȬœÏÊùü™çS—»8òÈÆt@ _fhZ.4¹7†ùë°lµnsÿ‘,èäÈ4Ù˜¥ §0÷ÄJ¡µò˜ˆ´7*ÿ¹†ôW3Nf] ’ü¸]ù+H¡þýZåƒÉ‰¦JÒöi>4Ž~¨ G,–ç˘y9,,Ö‘Žíó 8fGÖY›ö¿2ù2 D.°u&Ck¥Aˆ1JR¨?•HYOxùrL>¬mP#¶Ï„%Ï™!WÓ`ÃÅéÌÛS’!íB=%Yœ– ë¯1…ê§Û–ïÓW}8ò˶lx/™Ï<ßZ zû¦*Ȕ֩p¹Z_%)”ÎíT84yGºn•BƉbY9Ÿ•óù3Ïçóø4ˆ^0…#ƒ²R¡fñ0澫ðàüBf…iÐr®'sÝÐdxTX›éóF[žø:“~óS`Pj'%ix*îmYÈT¼Ì…6†#9r"VöìÂêÿ¾¿´øµ òñ9Èuîͤùp7¯G˜›=Æ`68“ú]¦3í$ƒ£ªóÉŒ<à*É"¹®l,"…úóo/ƒSíÅiš“ #ÃÇ0ÏIè¸nÌK¢òº{õ˜]šçë2#%y+J]k/‘Bõ¯ÕÊã…Ù$ ¼ƒ˜³`éô %9t¼ >è92…ògK¡fáPYec ¬ë)V’•óY9Ÿ?ó|.º”=]8²°a œÞbÉ ðN¼'æ¤GRèí[&"K/JÁFôXAnU¥‚ÏÉáJÒ}y.4‡ñL¯çùÐtá¹0L ·ãÌ;®Y ¤$…úßW~ßá´Ê™4_-‡³¦Á"òRp6ÀVŽìÛ-âã¾û©ü:C>f ˆœŠR0«¹\A.î•¡ïæpdtÛïdÍêoˆn8Œ÷ãÈz]³àäÎ f³ò:G‰È‰¯$°ç°œ<|*ŠÞÍàÈòTð÷Ϊz7Ö<âÈ;ý3!¹Çw_’ƒGç3äâYùÕ¨6G åçêgÂìÄ@%Ùôz.ÜY>ŠY9Ÿ•óù3ͧúZ23ú«Ï‘êñ}Rx+‰Hõ¸Í¹Ly¤$Õã”r°K¨)"Õã™árè¥ ÕãBýÔ—Ã×ó"R=îÓ7J‡ àHõx´i ´´ì $ÕãK§Ia˱dYÑþJæfÀ‰ó 8R=¾ÁC ·Â’¤z¼ñ ¬‹“9“­_¯ v‰r¤züæ×l¸™ëÇ‘Íïn&‡\÷ ²¢¯¯œÏÊùüß<Ÿ­_ýöõ?rÿ³\èØv,GÎþ˜Š.ÞÌo÷76ÖP’sÓä0¯I¸‚â“ Ïm‚”ä®þ2èú˜ïªÊÁ¡íK¹ý¦úÙh‹È׆™0}n¨’\-–Ãp“R)ÔÿÄÅ™ý$TIûœš{B˜óJ“¡ º-sãÜ4pÝ;¹Ä> z”…2ç<Éû!c™«WHÁ§ÿ9Kš AUÔ¤Pd°þJ'%©³36¯Õgîx$‡FÓÉQ#äÐãö~Y#¦¼EžJ²Ëy9tki¡ …ê·ý5šø¸rä À<þË`fY¬Šf­S7»ËaâÌ“"R(¿É«Ð+ôV’§Uù`êZ)ôúÊù¬œÏÿäù÷!Š;-àH­YpêV3ë™ny;“K&ɡׅˆ¼'“´µ 2+Cc^…ŠÈÅ2èãÞ#K£²áäš@æüQ2ÐZ f^ÔLƒV['0…ú?é•GŸø)Éðù0¯Úf”$š–Ïyè^ šÅ<ž›E}t™VOäPúrK§%‡Â‡Ïä›Iù0Å®1G õ7vžVÔÈP½ïÈô¶€„¦ù`eÐ’#kõ˃M·‡1‹3d0LÓŽ|5â´B™Bõõ?åÂë1Ys^.¬ë1…é<+0ë»:ÓáZŸ¦Pþ×å ù8Á™ÜP-®Ä¶æÈÊù¬œÏŸy>?ÜÌ„–µÂ8òÅ™ ~#„Þ,*̘;å­Ï¦õظ81ÝùlØj²92% ¾œóbÖû$…µ˜kžK š·UÒ xÓ6DI õÿðK(WöT’15òáìLs¦ív˜´gv˜.?ç|YßU -NˆÈ>Ïó ͬGÖ Ì€fNÁÌ…¦å÷‰F«E¤Pæ÷äÐyØugòD°öKEäšÆ9°ÃÒŸ#Ofd‚žVsÔåò:ÞŸE¤ö")DùŸQBõk=ÎÈ&³8²dTÜÞ:—ù~P2 µiÈ|A†Æ’R(ÿÁ}Ù੽#¥AD_ofå|VÎçÏ<Ÿ›‹Òá¡ýŽŒ>œ‹œ½™G”ïû'0MÊ„š%¡Lo¯tØyi.³ý©d^fÁ\wNÊá1d¯ÉPzÈLIÑ—C~Ú—"«h;Žê?âN>¼úª £Bäph£„ùöyùu¿68“Ý›ËaMÑÙÇ+î âÈ.«Ò!Èb>sìœÈÞ JÒþlÏf õ·)+º>ÓåÈF+dpIâÈœÙ/øµ~ÌôV©Ð¢ê ïf@뢅J2·… ¢ïwc Õ7=š Ñ'8òˆ]6¸m fJÚ§@¸ƒ’ÌrÈçøP¦Pþ'Ÿ¥ ÛO›#ÝÊçZ«‘¤€¬œÏÊùü™ç³Gi*2Ë‘e/S`SÛ¾Lͧià`9›¦Jã=™Ã^HàÁ{¹p„D¿æ+Èà½éÐæ­¿’ïÈâE>ÌÇÓÒàAíi©qC­àM)Ô¿÷e9XÕ6R­^Êáv³*ÎdµÆ2höÂ…#ƒírÀÌf>sx§0ÕrbÛJ¡É‹-"²Eùuq{i)ÑÊ€Ñ)ÔßÃlj}”äÉÇpÝ,”ɇçCµ£õ˜šåu{NdŽs\ݨ$£ÏåƒÁªL¡þŸí—ƒYÔ(9dn*Š˜ZO2Àµe(GN:‘ ²Æ0±ü¸ª.µW÷%ÃøIÍ•dip*ôØ9œ#å×%0æÑ«R¨¿ØY0ß?”#5Og@› !Ìg$ÐÜ™Œk$…aS#¤®n:lŽœÃ‘sL¤`0b•ˆªÿvF&½Ê‘¶g3àÖƒæBc9Ä[Ü‘r!5Ô‹#…ò7«– G/ÏT’æC—AZÌÊù¬œÏŸi>Õ×±ëR¸6ñ«ˆTû•@HDX©—ÛäŠ3”¤zœ›š3¶7âHõx÷÷2XehÁ‘êq¡þÍó!0¼&GªÇ7x$Ãîê9R=Þke„7›¡$ÕãmÓÁËÛWIV´¿tø5tGªÇÃê&ÃNOIªÇç'ƒ^”‘’¬hý_ÄÐJÈ‘êñÇ«ÓÁ Û|ެh~ŸGrhÛ5Ë™¬èë+ç³r>ÿ7Ïg¯&ðgn} :¡áyçLhOYČ葵;´bš σÖ%Ì5¹2x:¹ÓõB6<ž¼&Kƒf|˜¥wÊ÷¥¡z̺û%°¸A ywMœ:¦$…ú÷­#ƒ:}•dJ«|ðhÕ–™‘)ï;˜ÙQrðÆ+È%~rH¨Q,"#ëÊ þ»>yqcèß cÔBˆñ)Ô_ýZðòþEg2t§ú5L‘örà¨o GäeBáÌ¥Ðà^5fóQR8 )UBõ]>å@ß0_ŽÔ›–mœ˜óDÉ`¹Ûi’!;÷$¤Pþ}åÎŽ“B8ò­< oø0…^_9Ÿ•óùŸ<ŸÏUé°»Z0Gn*Ÿ£`•sáÛlh1!˜9k|&ÔèÎtžœaÊæ…´dè°¿=³eš"ûEg£’a½ok%ée#‡Ûz/¤oŽ ŽFuâH¡þ‡hÊaÖ̪Jòê9­HQÀ¨[ÎÎ䔃žáCù|^6Äê„q¤eP:LI\ÈLñHç»*I¿Ï9P0Ç—)ÔŸõÙ|ø4[Ÿ#Ûï”ÁóÙ"ff—òûXÀÌÕK…ƒ±îÌM3`Ö´P%Ùm  Ö îΪïT’ ‘Ñ!¹Ã)*Øg§€Ã-g%ù¼O& iÎÊí¼f§UçH÷, ôl €¬œÏÊùü™ç3¢0&ÙOäÈæçSÀoÏf»‹ipgŠsý–и֋ٳHá lDdcÂûAû䀢tˆì¬$ÝNä€×™¹L¹E¬Z3#·—ïóZM¤Pÿ'ª€o¤ ?—ÏáU}gòjkT—öâȱírÀc ³jýðmãÌ Ñ’ÂÝX™Ú šôW@n¼’ ãZMæH¡þ¦µÉƒkÆqä›99ÐðÚfn÷dè<Ùˆ9ÖH n±É"r}ho©r&·4̓‡KÇs¤Pý6ñYPø~G&idAH`8³yQ˜Ù V’οÈáÓê© R(ÿ$>tÞZ)ÉÃV+ eVÎgå|þÌó9O+.ù:q$7 üçôµ³ô¾Ì[Ö`®øE/uDÌ)`ÛÁMI2È„K71›(òáJ¯ºÌkåss&ËЙŒZŸ®„)ɦvrpuz® …úO¾'‡­ž"2r–}9ùÝã°mÄ"Ž<› ‹ãÇ1Ÿ’@[•«‚¼œ É[[)ɵF©°Ða(GN;"‘£í¤P6eÁ¾¯‹8Ò./¦µýn]iyžó;‘ßKàœ,ZA®IOÕ`ŽÌ¹U~YÃGD Õ61|]Â9òiQ<´ˆÙ¼•¿z#"ƒïäÀKÞ#…òGºæÂ¡I³”¤¤–Λi0+ç³r>æùÜ™'…Ïû48r|ù¾üKÈ~¹6]fo“ Èþ}¤ðpæ1yÔ/æ{)I“£ùW‹ùf\>¼ÕhÁ‘'sÀB;y!S+®wbÎ4Oƒw£¦3…ú͡¶¦õ8Òz¿ ¾;2£Ì’Áί!óêi ÈÌûˆHËÔ4˜eç£$ïÆeÁŠ‹˜]w§ÃÚ¶AÌÜšùàoÉê»S~ŸYæÇ‘‰N©ðL:œy¦j2$N«£$[¥¥@ëÔ>L¤dh~¯-S/, ôŒÃ™Bõ-3€ÛÊ‘s×§CÌ’@¦•U:Ü™êÏsX ±¶šL¡ü›t `!jŠÈ{ge0y\GެœÏÊùü™ç3úBk…ŠÈ¬< ¼ùÔ™´$Ãñ픤å®T˜0ž¹¹q$5¹ '›U‘ƒv‹j9À.äÛü™GÛ'ÃçÆ™$0YÙRAÚl΀¹wÔ¤à|ÆæÜqGÞí• —Îbš^•À‚2Oé×-’4”ä¹1y ìáÁ44ÕÒÏ rÓl9\}RDpY0}\8G õw¾8V¼³æH½ìòûžû_Eä»Ò4bë§$³ºdBò¹#M¯åéÝzÌÇ!rP¶<$"…êgi¤ÃýQó8Rkv@‰7s`±p¨ SóS@Á÷U’BùûȆÁa©-Jƒ¦»g0+ç³r>¦ùT_R‰ŒìwÉIõøüÚp› U’êñIgsÁqÇdŽT÷*Ï;Ç(7ŸT±—Cˆç3ù¯è?ìJ6lî‘êñGSAÞd’’T×-ÏÛçÚ9©(ý-oEdEû«ÿA V‹HõxS>„*Iõø‡;Ùp¤jGV´>w7z¿ñäHõ¸»i˜ÍW’Íÿ(F ½‘}}å|VÎçÿæùüµëo_ÿ#ç'C‹]í9r^±ÆÕfN¨+…¼ÑûEd“Epø2À™´uN‡^ÁJÒµ$&Ì]Ä4ªSÆÖS¤û*9ÀÌ "rÕ²<0û2FIæÞ“óÍq"R¨ëÓr°l”*"7´“CÜPMŽÜ{, & _À”ÞL†v"f'_)tîþNAÖ³M…œic”døY Lô^/"ÇOIº5û(I¡þ²ü3ÁõUG¦,O‡¼âPfÄ& Ümj¦ {+¿/ýøŒ_-öœo‘Ó>I  oŽ‚ª_\5ô&Dpd» 0§,œi©)ƒ£¯3gÍ„vW#˜Bù'<—Á'ßNJ²Ø®ìw}) …^_9Ÿ•óùŸ<Ÿ±]¥°Î÷†ˆlS~ñáZ3ì”–ÔX£ ·½”BÒÚ†JrÿY̹Î1wèÀŒ³äɆyÐýÅŽ<ü5ÞyD0ýogBݦ‘Ì%åuí·ˆH¡þ7wχÔ`KŽìi.ƒä˜ÌËU¥pJ•/"‹Ëçlûº2¶^&ø-ŒP’5[å‚]õùÌO£saø/¾Ìou$´”ý½Ö_U*„žÆ‘Ú¥@µI.Ì6ד¡æ-g%ùìi*Ü_áÅ|—µ…1{¶’Á²”L¡úÊéy Œ#=Û¤ÃÇä f»ÉÌ’òŸË…ÇëI¡üã[Èám´6Græ¹Ðæí™`[ô]½{é Û‘’ä&gCÏÛáÌŒórpÃ="òs‚ ÒûtãÈâR t>½NAnÙž+I¡þúFK Qbi´R›_K½r¡o-_%Yo òtº3'&¦ÁH«ù¹9K§VO‘Bõóç¤Âž˜‰9ùq ô³Î|¯—+MU’kËÏ3§]Ò¤P~ã0 HóLɉ_¤ ¸×XIVÎgå|þÌóiÚX ú»+Èë¥û¨š’Ô½–#wc/̇nz†LÛò}ëd‰Gæ–_¼oWÙ”ßÉ‚æ-"”¤8N#.wcÞ•HÀ²š»ˆìý ê¶÷R’Bý©Òa@FG~îžÁF>̈¤<¨}ÓCIöÖ“ÃŽ5˜ZÚ90ngGßO‡ôÁ‹˜õîåC«ØzJ²ê»|8д6G õ—ô¥ü>ób®‚̹&…çë)ɸvr˜3D“騠ÖôP I2hݱS’—¯eÁøöL¡úC½RÀ}Žìö&¶3½’ä Þp\DÎÙ’ Òv³9RðúÒagB°’l¶*žZ}·r>+çógžÏëvÉddª$å/“¡Ïh13í¡>›lV§ºÀN¹¾3yp«6ÕÐV’.ORÁs¶s^³8žÕSAîõ‘ÃÆE÷D䚣2xïJÒ±—Vµû("…úwxšW=†säÕ½É ÕqdêÚÀªmi|Ož ¶ˆÈVÝR``ÍžiýAšÊ,|0F?^Ì‘]¯I ñÐ)ÔŸSó8~¤«’<š– çÚLeîs,+Îä«h9$„ž‘mÄrØ9QCI¶œ&‡ÇAE¤Pý+‘ÉPöÒŠ#÷¶H«ÆLceùq¸=Œ¹è‰¾&6` å÷?–'ö5R’ï:€ï 'g²r>+çógšOõeq<žXQ’êñ¦…rÈ )‘êñ¬éY°ál„’T?»š '>ÏàHõø“àµ-œ#ÕãBýÏ;.… ½ks¤zü¤]>@kŽü‡þ6KàÂÖÖ R=î>0`‰«’¬h’ép`~¨’Tg[æÃ¾×Ö©WqÙðhÈbެhýˆÃRX> GªÇ;­‘Àžþº ²¢ùGŽÊ‡8®-GVôõ•óY9Ÿÿ›çóÏo_ÿ#ÃS“ᵘ#“^HÁý¼ sãl)´¬®Ã4Àë3CEä(ûTuÌSIšdfBNÝeLó1r8ç¦Å:¡ö­Ò‘[_eBí&Ë”ä„Æ`ý(NA õ |ß¾3\D.J–Ã1߻̖2`bQ$GÞ¬’ Vÿîz  t™‚¼— ï®9+I³ÇÉðÖ…#M‹% 0ÎSBýEËÍK9òÕ„ x§±„yx€†dö“* Ô[›¢ 5cRÁ[c&GòÃ%0=¹È™ªoÓ+ò-åÈÙ;2 ¥t ³ž¶:ÏlÄL0Íl—ÅL¡üŸ ó ë o%S!wñ)ôúÊù¬œÏÿäù|1X gWUáH—˨rM%"³ÇI h¹Ž‚|i.Õ‹'ÌQ\™z)Éx9L˜ôEA˜äƒíGÆÍφƒ‘Ìĸ<¨“1‰™ë•~ƒ™Bý/½ž“šsäל éÎ|±R Ò.µ˜Êòïã~ºŽˆ|Ö2Æ? S’G‡fCB“%Ì× 3aÍâ¥Ì)ýä°DS—)ÔŸn@øïZÈ‘ö¦”ÏÅ(¦G™6öo¡$çžJú£™1}Sàb½ÌÛ/³áRí¦Pýà*0ùHGöŸ"³pfõi°yÙ<æˆh–}VD å?_~_×ît ˆ|å!ƒ£27ެœÏÊùü™çóLÄÿaï.ƒ£ÊÖöáãîÜ‚» š½¸Ü]‚®Áñ…  iït'iÝ gÐÁwwÜæí§Îÿ^óVŸzž]|;'ÕýåW•›½î«“««÷®š:Gƒ¦çHd5×÷FúÕý>äb»=·ƒ“ù_0ï—¹Â23œHÚšâCúžµcN…jŒ¬Ô̈‡± … ¾ZTœ”C¨ý¨Á“•çeÒÏ–Ž„9aœTÊß1ÞŠÆó'02¼¶7Æ/î3j`•SdrìTæ|ÿ…“Ùða²¯°Å>»*“×]½/˜X™‘Åz™PooˆP)_Zo«Ù¶¥>‡ÞIä Zixü.ˆ“ö™ñ®t¸PuÍIûŽÉ¤¹ˆS—f¤Ò~ça#²/_ÉH[#|±PxØuñ¢ŒA&ÏG°.j:'•ÎÿÚ73_†12ÍË€2ƒý…ž~zú™•û™g€jm/'¹e®ÿè,“¶PÞù‡ròI— ÄvúÇ‘•-Xqs9#Ì5¡£PáN R~+)“õ3u˜Wœ¼ÑÕ‰1­†Ê亷Àw6#•ò¥d  aä­¼ép” úv1¢øEœ,wÈŒK^áÂS˜pþ€™ézNØÜ§ #›Åi0¦Ä‰<¬GÆãÁœTÊW>\ƒøÉ‹%Råz*t?ÄN†ŽÈD‘v«9)ËVàëXaX›tðïaŒ¬Y^‡‡k •öWÞd@ÐúŒ<\Ø€- ã„|y¼Õ!œl³ÓŽWë•Î:溌ÉÈñv 2æ¨dÒÓOO?³r?{p .ÏÓÊ$¯§Åþ'¯„CïZ·|'£KØñ#¤­°eK=¤]½™Í Á£[%²öšç åä4µ1•£¯éPåuFú÷Ôâ¥×ß2©”ÿþLàÊÈk÷Œèº PX¹³Ý/.å䫯vD'·–xfÁ¨§³¹ð3bÊ… ¯Y-ˆm=Ÿ“–ÙN´ÿẊTÊ—íþ;ºÓ2y¾­yC? '¤ÚU¦'Ë¿tàóF.“º~‹¥›ÔNêÏAœTÚ¿0L½‡0òK=îìí&ì?ˉg‡–øß¾Ûp·á¯ŒT¼y¡‡6c 'ßULG½ÂáBO?=ýÌÊýü‹k12¸ '»è`ºØ\x}›ƒ>“ÉAþN¤/Ê!´mÒàx»µÂ+¹ôˆËÛ“+:ðüÅ™¬=‰'ªJää%è‘ÂÉoœØf{é$•òo™m@¹SYºŽï%œ‘Ý ŸòF™ œìD™É}È· èáÀÈ? Z¬ÜVJXý *X&<ì¥Gÿ =„Jù"‡êPwBSN^;¥Ç´ç#…oœ<µ“œWÓ s@¬D>·bØÁQœ<æ:7#§Ô†TÚŸk½‡Jø0²]=® ©'¬µ&¥ìa²²^ G•ÎØÂ†={‡pry}'zŸŒIO?=ýÌJýt-o¯ÇÄ;}8é>ÿÞÙ‰¥çFJ¤û<³© ­¦‡pÒ}~ì« ¡óe¤û¼z{ ¾týýÿŸîs¥üGŠëÐ}r-FºÏ› t`ð¼ŒtŸ—¢_/µùo lz®’…?™/<܈ëK–qÒ}~¸¸}š•f¤û|âoVøÕËÈŸÝßý˜ew”c¤û|Ø ×s_ͱù³ç_?ãÀî%g$òg¯÷ôÓÓÏÿä~^Üý¯ŸÿoZ]ßÃwUý9©™Á[Zƒšèð}ð?^Ú£AƒßÎJd¯Â:äÌÑ„“«?1kH°ðDŒ þ¦ÁBý$'ªÔX-“mêÑ»Ü`NÆÇ[ñ-Û ¡Rþ:qNÜúPF"ï4ubnžt¡ix:Æ—Šbäà½F¬È¶ZYIƒÉÛßµ!}Šhá×ò»L†î1a`ÙHFöX¥Á¯R%R)_‘ð ?ÎÈf«%\î£APó–9¦¹EC¼e2ºE1ò÷ÃZ,¬RC¨´ÿã.3r°(F~ûjB…èHáþ•Nœ½Ú["{ôuýzŽd¤Òùí3ð<2‚“SØqahG¡Òõž~zúùßÜÏl+µ¨Ü³ #¼ÔàÆâi©êúÞ»·Æ‡l=EƒâdòÁÁ ,ÏÎÉwßlÛ­»ðÜg’g]–Hÿ+F¨g1rè'fäH”È”Zéø£U#•òs Ò«|ŒŒÜdGJ‰æÂ`ƒûw¦˜4Ø8è˜DFÝÑ#ÏÔ‰œ¼ðÈûäHaÓìz êÐOxq =¿þ£B¾½ËÓ0þK#› øk¾ðìßø¾/“ÛÂuðv´çä¨ *åË”ÉÐai®÷ÂI¥ý}™p·D#Ó°øL¨pý,3î––9¦C…õÝ…JçÛœ°v-"“† úN-ÀHO?=ýÌÊý줹LªD®•4H˜ÚFø:F‹KQå9©] Ç¬í#…‹Ç92+L&·HN$h%òm-3òœŠddÞšzli5Hø(Qƒ5u,Yg§ºøsR)¿9À†-ed‹ÒVäd „e\Ÿ«.=8É¥µ8Ü%'ÔµàO¯U ÛíP}i&|½Ë‰o ¿;ÈÇG,ÂdR)_¡4 Vô:*‘æÆ˜TÅ…í¹žÛGrrë *o])ôK³¢tÚTF¦¤QÁ¾J¨´ÍEŸ.bäÐ$Ìž)lü# ƒþãäâ²VœÍ±@¨t~LmNgoÎÈU\=/àCzúéégVîçÁl*,“ý¬¬Ëù»ðØüLœ.ÂÉ+Ÿ­èÙo¼Ð{ž«1ò7›c/Wîk@ç œL6¤ãrF¤ÐÏu_nuÝ—“7]÷\R¸D*åo4ÔŒ—¹£™ß™†ªßC…Á}3 ÁÉx««ËL~ÍmGßFݹ®o&JŽûÕ„ŠWÂ9Y/·=þð*å[ëúœGô}ä$ólÒ`êm»Lê÷Ù ÏéÇÉs(’\X8t¬73Â%2¥©µ[pRiÿ‹u·ŽcäßMôˆ ,lVɉ/'öˤ\Ô‰1ŽK¤Òùë“´(þ½'W|2¢ï`¡§Ÿž~få~în©Å‚êù8pM‹€3µ…¿|²cJ©šÂpæDÛk™üÝõÜ=#Ô‡¬WW‹È"¹9YSgC_y€ðÊ,'FjçÉäÍÁ,y=‹“÷­6ìšÙ_¨”¿Rc#LÓW0²ñozÜúÛ_¸¹‹}Í^œLŽv¢Üô2™Ó„*%ÂéWOV' _-w`/+(QÙŒ¾"…Jù>.ׂ•)ÃÉÇtX×]8Ùõ{HÜ1_&›mq¢›ù°ùfK:îää§iT,[T¨´l¦÷ëweä_uÈei%ÜûÁ‚ ] ¯_IÈaB¥ó­ÊÄòsk8Ù&›­ÌÕ…ž~zú™•úéþâ[uð™Û‰“îód×u3R–¶!Ýçª Ú5˜ËI÷¹æ¹ûÜ–H÷y‡«h ?“H÷¹Rþ[a:˜SÚ3Ò}~p€Û%I¤ûü“Iƒ³kI¤û¼óv ~ù¶G"6_ÐDJ7™ÍÉûŸ(rEº½=r ã½{ù³ûO\ÒbY³ÚŒü·÷ǵ0ìðfäÏžïçúùË™™mÈŸ½ÞÓOO?ÿ“û™vû_?ÿß|ÔWƒ°i9(·ú…]„7ºhQ‡—ã䬿ztî{zŠDf_dF¥ý1Œ¬ÖJ!‘c„æõTÊu["s4Ó£hž1œTÊ¿BcCÙ#9­“³ØRáãûj˜žp’?NjpÏÿ›L~Íf×›NæÐÚá÷– 7^r¢´ïïN²VW+¢ .e¤R¾:l:8„‘kWh‘Àjµ3 è_r 'ßæ3¡îå(aõ¼®û¢Ó„ÿsnµÆÇlÿÿ¯ý…s¦a˜#1bc`°°Ö_j8­ù$òÈ2-†>«ÎI¥óçì± þç Fæ(d„»QB¥ë=ýôô󿹟K¨ñøtòíK5úØî:Éu#b„p2[‚CÏÅoݵaÊ !Œ\?%ÏOF w5ÐàMt¬Dn Ó`@À%™<òÞŠ~+fp²‘ë9!Æi–H¥ü‹m™`kù¿XÚì^+,ï­G>ÿQœœ¹, é–a^­ ¼ÊäžÕN|È¿]"džè!·™ÄÈ–e5¸5qL*å‹ Ô Ï¨søIK+ 7žKÇÄJ1œ¬·Ú‚ù»V ;²aUä4F8k„ºk¨Pñó±ß€ é+ù­¶Áõ9'ì¹7 ?ŽFròÏV”Ú°B¨t~¯r:DiËÈÚOÔXS>Ù‡ôôÓÓϬÜÏØjü>mLú­Ð`õû3Âuó2ñ×ípNú·¡UÙiBÃb# 5]ÃÈð-Ö½­-,åú=¹4“>[Ó1zjŒðòv3TWbÚXƒÏ½$R)ÿ¯c̸¦‹aäèô4ß)¬Ó1÷›¬åd“V<)0[ü«+ôdd…™™8ø#\¸"¿ ß®Dqò÷ÚhZW¨”ï†ësÞõê'é3VƒÇ%¸LN¹níB¾œ4ns`o·M5¿N"K}ÓâÉÑÖœTÚ?o½Ë2&3òH!=ÊÔ!ŒèéDŸê'er]7'ü¼NI¤Òù»FjÑâC%N޹mD‘­¡BO?=ýÌÊý4äÐâÛà‚œÌ¯×¢BµÂäê,}ZWøa®í³e2ø±ͧÄûU?ip÷E>N&œ´ÁÿäPá·/’Éq­ hìµ€“I¯mX¨$TÊ?°œEãV1r÷=–œš"|<ÏÅ+præQ',ªZ2ùç×4ÜÓF12,›¿\&̛怵DQáš¿Mðn#TÊ×¾—¯O–çd®:t-ØW8KåÄ€gËd¡ë9,ÿ/§}ÈWªt´ÃÉZ&•-&TÚ¿'R‡mý{1²|)Jôe¨‚V”+µRØÌš†¢êH¡Òù¹7g¢«w8'KJ¼¿PSèé§§ŸY¹ŸCê`:Г—üôðÑû }]ÿ®ÆŠ”6dÄp'Ž=Ú/‘íæ0q 'çwIGÁC1ÂîåœÈ·ÿ©DhEDã%Œ\ñÑäÇ_%rŠ¿ sžD3R)ÿö:äiÐ…‘÷ûj‘TAøwˆ9[o•„{íØ{¿ #GÎ× x¡yä³·òzËäÚá,lh“ÈÌ#:4÷ÈI¥|޾¼¼“{Û¦Á/.\X¯®%6ß‘ÈÎ&;êdŒÜžÏ‰ÉßJä”9h™áúýü?•ö¯MÕ¢a‹úŒäµè_©œ0ÿ"-2‹TÖ~§Æük…dRéü·œˆ¨_ÕGxÍŽ3G[0ÒÓOO?³r?Û7À4q1'O,3¯óáô¿ì8r­)#çžq=WdÏ~µ"ùótNõvâÑà2yë¬ Oî¯eäi»§ôùKñ+óKäÙF¼iÌI¥üÏÃ48´ó’Dž,¬A@ÏQB¯ý}ÄÈçÅÓ1åVŒ0—ë>¦kÕœ<_Íó™UÂÓ,Ø:.XØÁµ—ÌR)ßÓ~fÔÙÃÉ\Ó3Pï·há»+ÎÛç3rå· ”y)Ì¢C§Í=…†Ü´œßU&•ö·x¡Á ï<Œ\¶M¾ÿD¦¾ÔÁù›/'š‘ð(F¨tþñl™Xq1’‘~=ŒØöfµÐÓOO?³R?Ý_}w§áº$'Ýç'jg¢\ëHFºÏ{þæÄ”Ó %Ò}¾0Ÿ«¥¾é>ŸwÊŽ r+NºÏ•òß½§Æ‹ckÒ}^k„õ'3Ò}>uU:zGÅpÒ}~ÆnÁ€Š«ù³ù¦Î¶`rÕ`NºÏÛµ0C¿2†‘îóÍ%š¸Š“?»?ûX –<•%Ò}îs݆Z%|9ù³çÏίEеBŒüÙë=ýôôó?¹Ÿ¹ªÈø¿<ÐNƒ_G]ȽwÕè¼?D¸o±•_½—É¢Õuèײ'Ç¿q`íòbBT”ÁËOð!›ÞÎÀ/ãb¹Ðõ½y°F¨pý=úuž.”Úh0 ð™TÊ_{„ßß `äêì6t(²@8Š«ÑöC=‰Œú¡Æ %;dr32ðÅsré<Þ_ž&\iq N¥JÂAñtzV‘JùlÓõØežÉÈý—´¸ÒNø²Ÿ*Mçd¾ËFølÖ½™Sb… “ˆß_&•öÏh‘†bQÑŒìiÄ‘ûáÂéÓ5زê•D:Kj°çk†L*?âë÷”èÇÈá³Ò‘œ” TºÞÓOO?ÿ›û™Ç¨ÆMÕ-2j·)1dÕÀÿùÿ‘YÅÉ7ýM8Íã„:Ðê·:ŒìlÁì—¡ÂWW5¸9³„Д¡†.™ü’Žw+8Ù÷±[¯”I¥üc[p²U#wœÍÀ•ÄXáóZTºNÖxh@öÉk„{{;alþ—L¶/+£Ž3݇l8Ú„“7ãÙû­ˇ'I¤R>ÇN ^^ÉÍÈ¥ÔHÚ.‘ûUfì;›ÀÉoË3ÑãháE“= Tfä„éØiJ*í/÷È€³Ö0òTú¶®ªe@þ"+8Ùtcä®qB¥ó3÷ë1,ïF&ÔÒ`r¥ƒéé§§ŸY¹Ÿ Ž©ñ¡E?™ôöÒÀqÍ"Ú;¹ËÅsò¢·«°Zè;ß„±åâYã¢G6Ϋ¦Ãê¿»sòBŽûÇaVTŸÄÈ€ñ:Ø0@¨”ß×+SÎ'0²þp*\ˆÑ›1ò|'G.³ ±z˜píî\¨ÊÈ—]­¸žs•°F=î'NàäÖ¼VÜ©,TʲS—™‰-È©®^®Ž—Éò¥lˆ\>“Ûë9°ïn‹LÐbsÓŒ|YHƒ?ãŒ2©´¿¿Q?—ÌcdX%=:W›$ÌÖØW [p²@y‡oô!•Î/YFƒË‰V™\±Al›æpÒÓOO?³r?½£5¸W-'»Ö×¢É7oá;Îé-LoãDÁdòÄ z[ÃÉÍEœh~8¿PïÔàÄð‚ÂØ½H<+TÊ]gDAM#w—7`J½åÂt~—»qrÁ.'ØY&Ë^3ãðõFæšiÀƒuAÂ;®ç¼b=ºû¥ûY0¶k8#•ò%¹¾×wžÍÍÉX×sѱ•í„ý׺î+t'e²h%W/&ýê$¿HC{'Wεá÷?§ •öw[§ÃøÙC9ÜK‡BAÝ„¿ùÛaÐÿ'§ã÷- B¥ó÷!c÷&p²Ä>ž//ôôÓÓϬÜOÍK-–\éÀIûY²W#,\DFâÄÂ2yþ¥³?Í’Èvñ:<õÊI¹¢ ï«Æ ûç’1¤US‰¬2ÕŽšåû3rƒ—ŒæsŽ:ÉRm-ÛÎH¥ü3uèS`$#o‡hѶò/Â&%e\éþ‡xÏí§J2²UY-ý«¡Qã)»|È) uxôx# 6Ò hòQ™TÊ·tºuô39Y¬¯ Ç„ w"Ï-™¹Éƒ½æê©vmmrÀ3+ö|XÈHÅýÛµ¸<©#‡¶Ò¢d»šÂ=uõx?d²0ô¸×ê ”H¥ó+ÜrbBR¸L|˜žòR"=ýôô3+÷³ëY=®ô[ÈÉÔÇÔ]ºF8ò†1^¥Yýƒ ÑêÑ·M,ØU ‚“õ::0ånSáŽØÔ$ž‘ 8zq•p§ë¾ÿ«ë¾Ÿüî§E€½'•ò§mÖ`ÚÕœŒ ûªF«s)™ÑÕççYŒÌæÈÀï÷c…{–hptÑ™ ¦ÇãçÓ9ù¹¦U'$õ_(0±ˆP)ßy´/â8¹&:‹&wm¸Rw"#6·`߇paÀ #jÔþÇúwÕðó‘H¥ýk¯h°»Z FjÖh`ñý&‘¿hP¦c9NNðNo-T:À8+¶Wbd½&T›/ôôÓÓϬÔO÷Wfñ4D]‹â¤ûü÷L]ÆH÷yC×÷òÁ>¿;I÷y—X j¿ÍÆH÷yúËLä\ÉI÷¹R~/½ÛÊ÷!Ýç¹/§¡BËXFºÏ½ãMh>:ž“îó ®ÏYâ妌üÙ|‡ ™hý,Š“îój®çÿ­ ŒtŸ?ÿ¡EÁn9ù³ûk´Ó ðÃy‰tŸ«tx_ áäÏž_e°«Lýù³×{úééçr?[Žÿ×Ïÿ7+ý¦GŒl±W‡Eá“…=à (>"T¸ÐK‡mÅ ‡ŒVã©ÁÇNúü®¼°*'óõ4ãâƒ$᫯VŒ™(8C‹3åÛ2rH-–¦Ôç¤Rþ³Ãd¿ã$ç¹,¿ûøÉŠfÑŒ\ìºÏÐÆ®þÒD‹*…~Ξ§ÆŽŒêÙÙuߟ£ÒQáïWÓ±É?‰‘JùŠ´°bÇšPFV©‘‰¦ç„~™Ì‹© ¼²E”jù<‰¡£¿ kñ |ì°Ž‘Jû/OLJ[IŒl~Ä uƒ Âõýx«oËÉ{cex¿äC*_`¶_3s2½I:—ß TºÞÓOO?ÿ›ûzA‹r{1²G~-ÖÕm ¼9XƒU–ÜÂÖ®¿[É{ÞéØa@ý¦aœÌ·'Åb“„oN<Ú~[&û–‘a;š$‘±ºL\ÇÉf®\åZEÙI¥üysËX®–È…cœ8/?#ÛôOÃȵñÂQgu4kª0ó³ºe²ò6.ö™ÀÉ· öݯÀÈ¡¹´(С>'•òy½3ãQà Œ,t( çG& ¯MPc@·}NR]\ƒÝÊäW?¼Œfd±¹jÔ‹¨,“JûYQ3®mIbäÜa&Œøe½ÿfG×}ý…KGÕIB¥óOœµ¡â38Y±´ŒäGeÒÓOO?³r?«ùkТP~Fæ{ Æ»×G$2$L¿uceÒæzΙtº$'¯®³áhè\¡¿ìÄÛ-™óÚ•;e*#Û,NGÕûIÂÞ<gëÆ ;Ó"|FC¡RþU3h_¨ úØ‘c÷8áà n›Wà§FOgrîoiHœÈÉ;Ó2‘œ/ôš¯&ë…çòËP Ø%“Jù¼Ó ˜“ÆÈçè ?ž*¬§Å‰-8Ùc¿×ë Ç7€7 Ê¥¬(&TÚowZý™ÀÈè÷F8þˆN¬G|Ç…Âñ)jÈG#%RñþÁìºïÉñP"+²â‰Ï*FzúéégVîg·µjü9=@"§ú«Ñ¶×òúlÊVóãä³/œ8!\×VÆ·¾¹ë½ŽŽ=Yô˜• }òkP'߉\6N ¯q'ûµ³àòÀµB¥üµO[q«É*F–hiÁ¢ÈµÂù®=½Þ‘IÞF‡ÄÅÃ8iY`ÇIíáâWNÜøÛ)“¬„÷ŠLbäó7Ø£"„Šýè­EÕ­™·±>H¤vjn~çd«~®ç®<„C;Ê85²«L^M¶cð!ŒTÚ?¯»cžD12Æuv¥q˜ðÓ L× s²Â#Þ=‹*ßë U$1r×bÞæ+ôôÓÓϬÜÏãÕ¸SJã$wŪQ«å™”ŸšÐzB'¾É@µ‰ÂĸL”ùÇÈñ~i˜|.^xZ«FëQI2yÅõ\R|ÙTNî##eH‚ù¬f&jŸL`¤RþiØõn#Gs3Ö×Û ˆ5¢Öεœ¼êº¯¼—$ôb2tGJä×^ FέÆÃ¾Ú“O°ab8'•òUÃ?uŒD–«Fù“œäˆúT;¹–“‹l6, Lö5!¢ÑzF~®Á×ìy…JûÏe7à«y#½'ë±üå"aj% ZWˆá䡦N|;WR¨tþò5望(‘ýúk#"'=ýôô3+÷óùc5,Îã2¹x²U€“ïYá•°J¨ör`S\a³WZt9Ó—‘M]÷ W[H䦮fLº›ÄɼŸ­¸(œ+k°;%F†Ñbô˜œTÊ¿ásLåÖ1’w5¢îÍ(áýnV¤ß ádþÁón+ŒM² ¶R#ïÿ0¡H’ð±ë¹Î+n*'[”–wz£D*å+¼GoµLòõÔ®]š“£môj ÌñÕ‰³›dòÇR f,ÊÉò)&D_Y/TÚ?î’ŠLc¤o¢¯æl,ãc¹åyiƒ _ÚÍe¤ÒùWP§h('Oºž#5Z/ôôÓÓϬÔO÷×¢Z8ürÒ}ÞÑàDÔÊÇ2é>_÷^“¿\I÷¹Ñîĸ™wdÒ}|Æ‚C‰œtŸ+å¿hѣݲ•ŒtŸ/,#£àö$™tŸKÖãþøåŒtŸw[—?Ç:Fþl¾€{Z\oÓ‡“îóÜ®ë2ïÏmCºÏ«Ÿ±£dP?Nþìþ§´h3¹'#Ýçš™ñîD#öüJN;’:ääÏ^ïé§§ŸÿÉýLOû×Ïÿ7§Ì4àò…HFÞï­‡o‰ a“À4d´Ü <¶MƒÁBË95ç-‘r35æÍê*“oÊÐîM'_çÉ@É ›…O¯›qàQ2#®VãM+§D*åÿ2Ëõ½%“NƒŒvg»8É®ïñöo{2rby+~Ý- œ®G̘UÂA­4(“­¤ðõ6'’ædÕdOlÏH¥|A®çcÝeŒ¼¸Ï‚£õc…q-êðРᓼÂodLù´^&ÛwàxóžŒTÚ?#ojßÌÈ…=Óñà`²ptª^Wb9yYãį…J矤×þrœ,þЀ ‘1B¥ë=ýôô󿹟¯ºèÀrLfdÛÑZÔµw~î®E{Ma’].îHd‘yZX^ôâdýgF„¯]'¬bÇûß' ó­q)`‘L6ë£ÇÞÒAœüøÔÛ¨…B¥üGelØJ"¿Õ”“ã”ppÍt”º’ÌÈÈF$Þ+¡Æø·ìäÝN8jyq²–. Ínl`äÓî®Ï¼J"•òUIÈÀ¢ˆMŒlÔ׌#;’…Öúj—ÌÈÓçM(ò}³ðøh#Šl•È%Óm;=ˆ‘JçÏΟ‰¦E7r²Ì@·ï%ôôÓÓϬÜÏïIô;W›‘ÎjTzšC8­´§¦øûùf«Q»·V&O´ÏćÖ8¹óÇ=åDÅÖ9˜ÐfŤaÂÞEe¬ç7$Ò>ÈŒ+ªdF*åo4Á‰;*02ß0®ë)L8¦Å³ø¡Âõ»]ïk扬G­‘«9Y£  % -uø<|‰0u},B¥|EK¦á³ïzFNü®GÿlaBG- få*ÊÉBOµè»¤p×M5nVþ(“Sö§Á¿îFN*ígeMØØu#á—†¾IžMˆ¸½Yxa—9N *F#ãÈu“œYÕ‰‹¿×e¤§Ÿž~få~T#¬¿M"Ÿþ¢ÆÅb=…UjPýfcNÎy¤ÃAË"aõ92ÖxGÈdí:2T³NJdõ&}¹™‘õºêರÃ5~×m”È•ûô˜›±†“JùO°cb…yŒ,bÅ—¦ÂÓåÔ~Õé$û·Õ ÿ¤Rœ|Ø‚ùb…ÓâhpªƒÐ¨—1µÖR“dAË6qŒTÊw¸žgögdíL ¶o(ì8À€œÏ#8¹«Ž ½núÇÉ®÷UsªðÆvÏ7å”H¥ý·tF<ï˜È黧 ÿ#F¨.©F©Ø¿Û9ûk±XîÊI¥ó ° ÖÓxF¾­††ÎõBO?=ýÌÊý¼åÊeœYÔ‡/ä!väç9YÜõœWXSAضƒë¾çà‰ÌáÐâÖê!œTÚ?¿„]YÆÈ +tø¥ñLá"IÆ©\&åÛNøVú"‘Jç¿ú m8¹á»ÿ\'ôôÓÓϬÔO÷×Ñ>L›X–“îó„ ®ç„;­8é>¯í¥Æ—‡>¤û<4ÀŽž§pÒ}¾¹¹ç^†sÒ}®”œÃ€õ•Ö2Ò}Þ©™›»Õâ¤û|[Zl/60Ò}^ÁÕÃ7+ê0ògóUIÕ`{B=NºÏʨ[8X&ÿíü¨¸a3'vÿ”wZÌl8š‘î󂬸>š‘?{~‡º쾞ÀÉŸ½ÞÓOO?ÿ“ûY6Çÿe×údlˆad»œzÌG°ðY•4Ü+ž,¼ÜTC„ÆjÄV¾"‘g>©Pýl˜LÎXlÀÈ"ñœ<©ËÀÝ”áÄu&´¿²‘9©`7•H¥üU—Ñ)GˆL.ÉÉ1ÿÄ=yª¯=bG2²ãm f_'ìqE‡'=W ÛYÔ°- l2N†OÌ]™\TÖÑ'ý©”¯½Ó†ð…ÁŒ|¬µ eÆzá»ZÈ%}…ú-joÉ),ûDF±²É¯íÈ{|2#•ö{ÿHGçfÛ™!¥ãï’;„1s­x±7–“7:Êh‘ÿ½L*Ö_ƒKšrrîX#¶6ß TºÞÓOO?ÿ›ûYõƒã Î`$ϧ…qxo¡ù²íR: õ}ÔøÜá¢DŠÕâq„'[NCîÏÉ„Æà?a›ç2·üE&´2àm¶XN–,å@̰ñB¥üUîʸÝkD¶›(ãê©[ÂĽfÕÝÁÈ;; ØÔ'AØè  Ôt’EÛh—Y‡“E–z#;œPaßÀ>¤R¾äI86h#'5£ùîíÂA§U¸<´¨$|­Bh£å2y¹Œ ¹w¦0rW£ì«‚B¥ý™Rp#'XMx\v»pÝbû+ž“H¿/Vl«ÅH¥óËÌDÃÀÍœÜ÷Ã$ßBO?=ýÌÊýØNƒêßê2rµë{åK\N¡s¯ë{Vµ± ¹¢¥wó’É×3q ÷fN6^é@©lC„Cþp¢`·²Œ¬8Ø osœðKN¬7—&ç5A=7E¨øûIrbÞšŒ¼¶Äõ[†ÛvÒ·ê?†6WcÁ¹l¿îGròG Û‹í>”õÐÚ#…e¶Ø0š¯*å›}Ј[½61²Ë6=V™"„˳iP)¨"'}jѶø4áËïj\^A˜-Õ„Ÿ· •ö;ß§!°m #/¶JC¶eÉÂw_ÒÕ;EXö™I‰ÝþQáü2.·ô­g'+ævbA—öŒôôÓÓϬÜÏ&µÔ8¸Á"‘+¯«0nc{á 矷åäÝ·:ä^½Z8ñ¶ŒÊÓ‡Éä°®2rß+‘wª›Pà·FJÉZÌ;Nøþ¬ ƒ2ËKd6‡3ž$pRñóUÁŽ›¯V2ÒlEÇ_b…¦3*<XZ&Ë×Ö øC N.jÅåõqžUœpc¦®û†N¥IäŠäLÌo˜ÌH¥|µh1±×$F.í­Á… „«fðæl'?L2áýÜm„A4{>BXË«$RiÿñåFŒ¿²‘;ŽðÒ7Qq@…lÓ–;É¡®û´Ëü8©t~ïß3±´ïfFÎf„׬ BO?=ýÌÊýüÍõ½ÒcW2ÄÕ“gÊÊd¯KFtLßÄÉùÒáWg‡°õ'¬b¤Ã–ŽÆ)Û…Í®ª°él‰üüX£§½8Ùi+;ú [Ç:áׯ6#•ò<œ‰us63Òø<çûo®û[‡ý¥‚9= !϶ Ÿ¼’±zaM™äCe”¸øX";4Õbö„Œ,±K“rsR)_@5º7;/‘\ÏUÓž~÷!AÈSr'Wϳ¢¹-VØ!· ó&2òú1=Š5‹*íŸX΀ ÙcYr¶ªëaÂÇïM(±j;'_œ¶!>rµPéüÝS48Ô³#o¹ž3/-Nr’ž~zú™•û9§˜Nl—ÉqËÕ˜Þöp~+ |ªoädó¢v¬Ž.ïiÀαŒ,Ç4¸<°®pÑX^ ˆã䚸 Tyš"ì´4 õâ·0òÄ!‚—¦úJùå7#ۖ팬Õ( kú' ËDôÈ­œŒ{hCïÕ«„íx=Ÿ‘Þ¥2aì·UXjŸÉuwp2$» GÍÂB¥|æ‹*4íÖP&1U ¿ÊO„éŸíx¼m'¹ž#{Ô*+ sõØRó‹¹½™÷çrRiÿæ³:ÌyÄÈÇ­uø?O¸`ŸŒã-eòä'´>e©t¾¶²5ý8y : …®nzúéégVê§ûË¿´WkWã¤û¼^'.œjÁI÷ù<×}A›ÉvÒ}þ£¢ýZûsÒ}¾´­…G&qÒ}®”?÷4~ÝÇH÷ù“}«rÒ}P- ‡Ê&3òßÞÿŠ\îÅÈŸÍ·!PGbKNºÏw]•±Ù9E&Ýçùëg"äûNþìþóZ-¶Ý÷g¤û¼x”Î&IŒüÙó/?±`™DNþìõž~zúùŸÜÏ·cþõóÿÍáÃ,X¸d+#ÓsfbÔä]¹}8¶.'“ëêr4˜"‘§uN ŸÑŠ“sGsxùþâ$/Ì‘qóm~Fz7´¡ÓÇXá¢ò&Idðµt4U1R)¿®Y&Zý¶““ÕóZq›„íýÒ0ºøNá]lóv C\Ÿ›_ÇÍfÿCFß·e²ÏJ]Šæd¾syI¤R>ßN2z7òâd}/Žƒ’e²úC'ŽýYŸ‘ÞµC[c‰°ãaeÜ•È/2Ñļ‘Jû§FØ-R„ÍYQ ÁFáƒéhù1UX'ÁŒÔ+*atc=úŽNWaÄô¹®¢½cÞËdœ«ÇÏÊÅsÒ§!ÇA©”ÿÜ|'ŠŒkÏÉÑÓdìù» p›«wΊÃdrZ!ŽR%²H_+R¤ Œ¼;ÔŒñ*á¤W*ºzQ&„š02$•“Jù¾ ñxA Fúp¢uƒBunvÜ-Ü—Ç€w…ët:LÊÉj?26r›PiÿRk&ê¤mgä·wˆÛ°KX}“‡>UädðßÔÊ‘,T:ÿGª_WÎeä¥h H¤§Ÿž~få~¶éhÀ‰ëy²¨öE Gd¤á`û]Â[õhÕ/N0[)oK/ÏP¡AÒ{'é_SçÎ(N–蓎˜£*aÀ/x“˜ÊȤ©j¼lP\¨”ÿlŽ{gwʤz¸«wAj'y©¶ÖÐ>Œ¬|Çõ\ O[¥G¯Ž±ÂÏÔØmm$ü¼Û‰É­¹ð‘%£ë3R)߃Av\ fä”îV„OØ |ºH‹gw'©?¶¢°ý kŠß”ÉaQN4Ж‘Jûã›d`ϦTFöŸšŽé*á´vLËŸÂÉ]÷øÑ®Péüj4M)ÁIë="‡' =ýôô3+÷³Ï-ÞF.c¤ö«—›Žî,ªÅéœ~š]Ô(Ö1¯ðY\_Ø•“Çî·Ûfa@;ò⥌ˆºe²V’S7ÍædŽ}Vl=’(TÊ?¬‡á}5‰ý@ƑDžy¦£æ #]aÄ‹W[…“Tp¾ÝáC PC›Q€“w²›ñq°Š‘Å˪1GûV"•òÕÏ™‰ÊSw1rù63ÜR ÷­U!.bŽDZ BÈjo™ìÝ%SW1²—Qƒ¢;ú •ö÷Ýi†ÿm#wÝ0¡TÕŒ÷áxÒcŒDÎßfÇé­ŒT:ç¸ ôo”ÊÉ×mØ|q–ÐÓOO?³r?Y ¦|oÃÈ#sÕØYRh™¥Âê˜\¼]ÝêX™»<å_íæäy×ûz±1PXú‚ŒkÛ/Idj~;ÌŸCY¬-ÇòC¾ù=2uïf¤RþVµdd[^‰‘]˺¾—þî'<R‹à …㺫±ªa>á÷y:äÿ=˜“ÙK›0hÖna§&Z]>^8àÏLŒÙ½M¨”¯Iû4”KÜÁȹ øv.Q¸©Ÿšà¤-¿ófÛ£Âä#z™¼Ûˆ&õ’9©´R¾ÍßÍHã 4Üß!L>e†î©JX;—ÆÂ+„Jç¿8Vµ!“;ÚÉ<¼ #=ýôô3+õÓý5ì¥ wŠ^”H÷¹ê’V×ã¤û|ÿYc‡_‘I÷yõOftt½oÒ}¾)^ ÿ†é>WÊ¿ó•–-g¤û|û8òô¸ÕštŸŸªbÁ@s 'Ýçp<ß;@&6ßÔü:ÜÞº‚‘îóàÅz}ÃÉÛ?ˆ|óÖròg÷‡¯3¢èôFºÏíA*<¹ÛS"öüQ©VøÅ¯cäÏ^ïé§§ŸÿÉý¬äÿݧÿ/Ž÷å˜}P"óÜQò|AFy£A½ã39Yg“¿,Û |‘ßvZFž Ðâ®~…ðÃw-æ ¸žC¦Þ'‘}úh`.êËIƒ¯v •ògkèúžžQ™“EÚhðhÛ@áÉMéXóÜÀÈ›íx;n—ðÅVÍÜ&\»Tƒe^£…“S!ê}vNšŸ˜p(Ù TÊ7¡§Të8³Æˆçv ¿T2ãƒðûL|=¥¾urtøë³“,ÛÖ{¡ŒTÚÿy£Œ*C*1ÒÞJÆ—¸_„ûj°¬èa«O*¨*_®l:öv62²M‡Þö¡Òõž~zúùßÜÏÍ-dôÎÑœ‘o&:‘«Þ0aÓÃiXõCËÉ/Õ20¬–Axnš 3«’ÈAƒTèg7Èdƒuï ÍÖÁ¾-Š“öß8šÝ?ê ¯Y,hÞp'#•ò7‹Ö¢àç@Nެ¬GÚŸ  +µxÔ(‘áªwtá;JÞŽ—ÈM£Užj“Éid Ô†“ÜGƳ×Í©”¯Ðsªl3pòíÈ <7ê…ÑCmð=º^øø– Mö~Ÿ†Mñ:F†4Qã|÷*B¥ý¿sâÈÕnŒtôv"¹Ø(aG×ûÌYÞ&“å·ë°ëàZN*z§ éo^Jä^Þ”eÒÓOO?³r?ƒç:ðV½„‘³ÆØÑçc¤°ÌX+^ïÝÊÉ‚Íx]'Hø²‰7>.†5wõø“Z˜=ÅŒÃíÂþlØ42N8*Zƒ|üÙm±÷n5æ¤RþÀÛ,öÚÁÉìÓÿQ+4KÅwº Ùñ´ ý¹8tKGûé·õŒ¸v{§pN 3>=60rØ Âïæ*å³ÊÜο““‡žØ0µaœ°²YFî¥e„µS9žÈd§‚j”¹Sš“—v¤!6N+TÚïsÔìùw "þ Ö8–Žæ'Ç;pnÓ"¡Òù‹è0÷N('u6ãM>£ÐÓOO?³r?}Û0 9ꉦwÛ…ûʘ4»'φpä?œ “ç?f¢l'Oßt`\¹Â)Ù9øÖl™Ë8òtJ‘È—O2ðèW'›ìæXzf°L*åod5#w#'è2ÐîO°]-zWX,<üÖ€b1;„§{e`×9½0O%;r¼[+,½FƒÑkƯöÉħç¡R¾|Çxó÷,NŽh/coTSá 5GT…ž¹Ç&ãöþÒŒìùÃŽÛ¹‚9Ùò‡¹^ª©´ÿÔ&;|½Ã¹èƒ ‡7Å awýž”É…OœÈ]±##•Îon³¡[“Nîþ"#¯œCèé§§ŸY¹Ÿ9c31öµš‘^ 20 ¤AXÂÁñõS‰¼qWƘ9/”#&O‚LþÕÂõïzߔȶu(¥bd»6Ð~Ö Ï±áåˆÂ›‰ZÔž$TÊ?-Þ‚ÃßwrÒw¸ {­ë… ]Òñ¬‰QøW¸¥c·ï…Èx𱚰ºëÜ7%þ°“·fsL¯¦•ɰ‚®÷Ñ{5#•òÉí9.öþC&}8‡…=w’ÝŸ:P8r#Opõ¦çzayWœ[c…ÝÆÑÉk·Piÿ­î6èú$1òù:+^5ß"\ÿ<‹Æª„†ÎM*?eÇ…å;%24»®¬f¤§Ÿž~få~&4CÛÓÈÈäa&ÔLÔ •vbø  £6œë±NX1›GŸ¯þ8™ëu‹§ô8ì³I¨i®ÆÊU…{Ϫ0afnN&¤cjI£P)¿é{‚9Yi½F /tbÌÞ‰Â0ŽÆ×®Ê¤W‚Œ«yª02½™º¿£…>ç ¸~`»0,¯ £Ûö!•òµzu”ÈO…9~küYødB&¾¬Ó0²cC3ú¿1׬U¡ôÞ[i¿ªFÅ‚9©´?þºw0ò\ ’§ ‡×TÃ;¢¢ÐÙI…ǷȤÒùî¦ãGœ‘~cõ(³p½ÐÓOO?³R?Ý_>høNÅH÷y•q™è¿KÃH÷yP#*¶ÞÉH÷y¥R*˜cZȤû|×s“ïQ™tŸ+åo:CÆä·µ9é>Ÿ)»zô·tŸ—˜”ôŒtŸõÒ#!9“?›ïûK'~-Ý‘îóE¹ŒHüºƒ‘îó!íÌ(ÓÈÉŸÝoðÍÄÌ“FºÏ-½t|ÁÉŸ=äŸ*„õ/ÂÈŸ½ÞÓOO?ÿ“ûÙ§Åü_:íÁƇœä‰Æ{°ötºù}}*Æ_ ]NÅÛK;d²VÇXÿ÷é¬ì@«I‘Œ\PIÆý×ý…º}Ü®î?%£ù„sª›«\†P)dt*FÞ×´f¦bÿë2yÙõ\òÔ¿8#»¸õcµðsªãFO^ž`Å–©ÂcSd?ßQøù6.Î*å˯ æÅÉ_n©1÷aZK "æM~µé°¸ÜáÇYjÌîÔMX¦˜òÑL¡Òþq'8’ ’È®¿Ó·ï…ÍŠïA…Jã…]üÈ÷0”‘JçõÞudM™ìyZ†)³#•®÷ôÓÓÏÿæ~³íuy¸D6ÞÁñéû}á.o5¶^oÍÉmôX95ThHÏ@ÃÇéŒ|ü›…{r¤!Á7M(õÐàÒÀéÂw‡RQ¯a”DnÊ­ÇmïÍœTÊ?¿• 3ùÿüïèþË’%Ô8Ô¿'‹·àUu-#ë÷6£P.‹°| 3ŽÜÌ.¤‡Í³ðùXŽtÌ'¬ÜYƒÜë¦qR)Ÿ´E‹E#9yÍ®ÇÇ1Û…KaikÎ/‘Žm,ÂGœø®ž-ŒÍµóGK¤Òþ@Æa|\‚ ïÈPÖ–î¹–E¸ó££7Ä•ÎuÖŠ~uv2RÊf™=éBO?=ýÌÊý¼Yšcâ°JŒ,6Z†é.B{ê'ïædí&¬«“)|þU¨?FžV¥¢îåŽ9zK*Êñ²Âw¯T˜²²!'·cNåhaß¿®{&‘JùýÚjðqöTNîIÓâU®(aö§z|¿³ƒ‘ûip½ó á©vj,º aw×}̬7|H>/ßMœ¬uƒÃ|ºL*åkᕆª4NÖI2# ©åG[;G/l:ÒŽªºD¡­ õºng䆢œÌ·K¨´¿ÔBûš·gäÉl2="lp)Ë·Kd…*”Œôæ¤Òù©w50<‘­®û>Û<‰ôôÓÓϬÜÏËË»f#_,ràÏàá®J™¸Ÿfæd‘q6\›±U0S…„¢…„wËêð­~œ°ïQ=f_Ü.4ÌÌÀˆ áÆré¸ÜÊÂÈ}}UX’C¨”¿ó(=Z÷ßÊÉú­ª>+£Âæà›™º1Ùòæ‘É&ÿ“û[ANnIÒÂûp„ðñ$- eä»óla¡R¾ß+dâœÅÌÉüÁV,»[øò¸ížv™ÍQ('cXÜ¡Ò~ø:ðÃ?Œ‘ý2íøK+œßHFîç}8¹ªþŒjõʇT:ÿQõ ÄVÍädÔt×ß¹e¨ÐÓOO?³r?‹p þ˜¥eäáA™ûÞ$¼Pn¦Ôî*“ îmቼ[E_ÝŸ“Å]ïKø—“|5ãö–<ŒœSÃŽ'ù“„­JìúžŸD-’‰ÑwÍŒTÊ_eâfN>h`EŠM%œsˈղQxn|¦lËÖ›ê€eh¨0t.Gl÷ÂB¾6´>óÿ±k×AQ¾møð±±PQ,ìî.Ø‹ÃîîîÆÎ¯b‹X؉Ao»4,ìÞ×.ŠØ…X˜/¿ù½çõÙwÞçÿ{f÷ŸÏŒçÜçy€²;£pN;î,-ëLÊå ~'Á6»'ýnpÜ<£—Èñb÷·eäC]2Ý…ý ç.)Èø±X=%˜‘r÷lãÝÚ‹‘÷‡Ç¡Þ+aÿ?”½³VX£z46‹Êퟒ÷ïÌåþ)áøª-ÁHk?­ýÌOý´|Ù>ŽÂÇí1Œ´œÏ~+aKÛ†Œ´œÿªÈoW‘–ó!‘Ý)–‘–ób̓𡘂‘–s¹üý¿ÇáC¢'-çö¡q¨+à¤å|y=NW©¦ -çg¯ë1ëK#ÿ6ßã¶&ì^ÒÞHZÎ_ê±nR#-ç­¾já{ä8#ÿö~{/=:g*i9ߢ…ëüÛýÕžÇÁeõ1FþíóÖ~ZûùßÜÏfÑÿ÷Ïÿÿlp\ÂÖê£9Ùw*DzðæÂûkÂáÒÚÀH]5¼íUÂ?³Â°3!†“^{b0¹]¼°|…0T³®. ç6z¡ºoêÏ”ùb¼G탤\~ÏáÁXç±™‘æÂA°Û:D˜¸$¡Cb8¹ÄÓ€u_÷ ûԈş‡±Âͯ x¶§0éy0n©<½POÓ¹DÊå»ä¡ÁÀ®9x«§ ^VÏ#4p7tfäÛj¸øF)ÈoZ2Ôä¥  깋‘r÷_Eæý\ôka$§EäýùãMÎäц(¿Öàäþ(N} Êí7Pƒ¦gq²tgÖwU åž·öÓÚÏÿå~nÿÉ1z^NŽkÂê jIØ+‰êMŒT0 ̯'lðÓ€,ÍN.í`B`Îu‰ÜõÒˆW–qrg  )^U$²è-®ðædÕ³ñø¹ø˜P.ÿÖ"Ì_èÀÈ·Õ¸ÛU« ·.æpÊiÌÉ…:Ž?9c$Ç|ÏûÜÖ¤ 'wz›ÐÜ«‚ u ÇÔ›qœ|o0bM–›P.ß™ jô*ëLží Fîüeù²¼ûÏ«ŒdZªMÛp²èãp4œkddùuäti'”»?Àׄªµš+HózÆ´ß%,¶?šÆs2ÃYBÕ9³…rû‡)¢qîÄÉvƼÌß-´öÓÚÏüÜÏ/*Jt*-‘Óò~$TâLƵP£l‡± òLÞ÷µÂœeÙl¢ =ì£äÅ™F<®áÎÈû=8^No'<Ú'eÛ ×ô3a|÷4¹¬H4:0ÎH¹ü.y¯ s&3ª±ãEg‰t+mÂì'_¤VgÄ6ýZFî’Ð)`˜p¨2«k©„>q$÷),¼9?9ë%¡\¾à—jTžTš“'aTî(áœ9AhÙeš°À팮â%<^Lƒ ÛŠÂ wÂ0ó¦^(wÿé ~ÔNR¿Ë˜Ð¥ìg¡K” ïPљ̹mÄo+)Û£©&´{&‘+îrdû–d¤µŸÖ~æç~Î÷4áÍüå 2¦ +\o #‡k°ubN›ŒÊ%·Ýó>W|xËȪ_Âл{œðkæ…HÂeBvi·°m/ r¿7zt Æö›8)—ÿ÷q5ªÔ{,‘æjàr¸%'“^èq®º–‘­&DaÍ.|<" •~ükŸ×:t × C»Áä=^x!ïs@‡ìFœ”Ë×ø^0†–ðääÓoZ¬8(¬ø,=+E íûEâËC.Tý‰ÇÐÜ#ƒy=êæôÞ@ÊÝŸ˜ÌñðnyFΙÅñxq3á‹_јUÞ(lß\‡•*•Pöýˤxœ¶óa¤ÃÖH,·5 ­ý´ö3?÷30ŒãÙÎ*Œœ÷C‚iRa“þ:üVsò~x8]3=¼‚¡º•‘j75´ªS Ò8_ ÅJ£ðDº»} p2f–>ì˜Ð|<ýÊ™„Þ‘±È±‰fdrRV®[,”Ë¿U£…”ìÇÉ-ÛBqäF¸Pk§A|™ÊŒ /£FôÂp'2w£G—HS›`Œµ‘“N7$<õïÏÈ:ï"ÐÊ åò­Úr8YÛ^×"„“Žqòáþíë®×.pÒàkd]F~è©AÇ— 8)w_]@©ñ‹ya‰CFmNªEƒÞœ¬ò' _{r¡Üþ»ûÔXú.K" »tˆ“Ö~Zû™ŸúiùZ›1žGi9‘bÀ°G»8i9_ð0 üž“–s§!ñx öá¤å¼¾¿F2NZÎåò{ï ÇÊ©NZμVcççÒœ´œ/?І3#9i9¯l«A‘æù·ù¦ÏŒG‹3yÿÿ¯–ó_{LhÞs®DZÎË,Çž?ñœüÛûá× (Å“‘–ó-ÍŒX`ÞÊÉ¿Ýÿ‘EâÏuÎÉ¿}ÞÚOk?ÿ›ûY‚™ñŸÓ:ïŸè9y[†ÎÌÂ>í¢‘¸=U8"IhÿXáÖh=>þù×ù: Y6K„•:„¢Pá8aFÿ´¸$,Õ=q Sy°”õÚB(—¿ª™ƒdŒ´O‚ߦÂ~h2—°ýQ=¦˜ôÂây{¼jÕëDz}àHx×’‘ŽT`=nJdÑÜ0ô«™ÈI¹|gcLØ“øDA¶á(§ #w&rLLSprÔW–>”È©—U(ÞH§ ‹ÔB©9Ë8)wÿí#&´ÙÅÉ)-$”uÜ,HP¦rrš9Ë~… ­ý´ö3?÷s ‹†Î-•“c°³K¢°Úžw¸"‘Í(eßÊ™lfÀŠ~ÇÙês4ÖßNnoÂõöÂ~oã‘54@X¶™ZS ‰LŠCã©aŒ”Ë¿î=6xÆ12×5V$ ÷uFÒý]ÂÔ-j$ rŽö CƳ°&¥V6Zƒ–ƒ“„õ‚ÐºŠ»P.ߢëzt,ËÈ”¼÷5½n' ËÇKx^y‘ða{îW÷zØ™1,ØCAfý‰Ã¦x-#eÿžuÿ,éÂÉ/eMà †ÆcYI•Ðñ¸ ©É¹)·¿é Ž‹ ;pÒµ³OtRÖ~Zû™Ÿû™ë¤GÓâN¦ùǡχ0áäÃ&|ìûKAÎx&Á©êlFî Åp£° G| mÚ7‰ÂcŸ4aÔ@-ŠŒÖ= †`Á/á– jüîùR"åòê…âûÒ™Ò0™Ú«ÂËE9Fr@5úmµá¤³_8^ŽKÊ妌Dv@#ãþ„ÁÁ)QXÓQôƒÐ5( z§ cV‡¢Fx¼ðh5–Lþ¡ åîßžm®‰vœìa‚rÑs‰œ¹É„¶ç‹0rÄÂxÌñÒåö÷ê$¡ÈÌMŒüT&:›¡µŸÖ~æç~j?Æ£tíN.nhÄÚŒÂk£ xrЛ‘¡ÕõxanMQáüþã R[O ç—_$²¨Ÿ[J×e¤{˜ ÜM"kŽÀ·Ç©œ{“£OƒB¹üWÊ…aaqÎȦUt¨& ý„»O¶srÍ#–eÄíºkйz?áÀ&Z¿Q ÇHXZu½pÂ2º.ÁH¹|óÖéÐÿ^$#*B°Âå„pKýp´KOnº­Å6/°û ð¡œÌêdÛT¡Üý7/Lˆ°‰–È•ÌØ4mµ°ÑîÜÚšÊH_—`ì´[(·¿ö…PDž422ñ“Ùnó„Ö~Zû™ŸûÙS!Á§ù&N>lÌQ×q¼0mKæ¤1rÁÝ0ôŽJÚŒiµp²Zíp4Ð'  BL¡úyªßO>,aþè Œœâ†‹Ž\(—ÿÀ ¨z’‘ŸAxe‹ðÕ®Hô?žÆÉ?Ýõð:/ì¾2÷'¦ w—ÖvFaé Ñè×-…‘]ÛáÇåB¹|ÝG¡üi7F~º§ÆG×¶ÂëvÁÈœ¶S˜š«FÑ,'aåQFì®¶“­Û›Q²ÐEgRî~tK3î,)‘›f¯óÜ@VöWar€‚\‘÷sú¾M?NÊíï¸C…®9›ÉcS5(6k'­ý´ö3?õÓòµçÇ‹¥­8i9gnZ¼³ f¤å¼ÖÜXÜ]`â¤å|na=Öt3rÒrþ+ïsHÕÔOΤå\.ßEÄÕÎHËùÕaFl¸“–óF§ŒØYg'-犅j ÙY“›ÏüZ•ë)i9<¥BQ•¯‚´œ*àëëÇÈ¿½¹£IËùþ%¡ÀºxNþí~]–…ÅpòoŸ·öÓÚÏÿæ~.¿þÿüÿÏÔ *\[½IAÖz«ÄH÷9ÎäçHèÊd2ÒÖ3ï'¦Ý.˜p¦rNÞJ3ã¹¾‚<Ø6êQIœ¾2c›dW/ÁõŒü>T§Á‹…rùgzÆ@ýä,'§(õÐ\L–¬BpÉ3 rT :¬™,‘C¼cpDs–‘QYaÐ=#L‰cu29Ù;Š#zè ¡\¾ tµÉÈɯ§Ã8쌰d)5 ühÄÈ[¥TxºeŽ‚Ü¼È §?ç$Rµ.ïsã»ßb@FwÌäd³ÑøÚë‚бw8‚ªd0Ò¯frmÊíä¨Â„ž»$2Ð5=}wsRîyk?­ýü_îçŽoJ4ßSF"o¶UA¿G+,î¯.FfxjP£±›Ðg/‡k’«pB\VT5mŸñÂ|ˆ“…6˜1½¡I"ã j,+0œ“Ó'EcyÎy¡\þíÇã±Ý9’“î“èuè¤0ð¸Þý/¥£ôiaÓÈ<#üù²¾+KµvÜgBâжB‡ŠÑ˜?ÿ‚P.ßæµ‘˜ïœÉI÷v1Xø9Cx¥¤ 1fH¤]q5~×mÄÉw ¢á1ì#û¿ ª‡…r÷½íb0gÊ9N.ƒg„æ%F»2]X:7 ç_v~­„ëÐͲQ756LïÈÉj‹5ø¶e#ÕoTpnS‹“rùš\ÖãJP'/u4 Àƒáû÷L*ê!4x‡`|¸V˜ú^‰Õ"ŒäÚoÁØÓIÉI¹ûÁé±øfLãd§“z)œ$üz(U¤;Þ‰ÅçiB¹ý…:sØŸ]ÅÉVÍXt:ÃHZûiíg~îgÖo5þœÂÉ?o4(ra‹ðJgÞ74Hd£“j¸ú àäó¸<}‡12âß«r&ØE£îª Œ¼ôL‹´oqB—5:´ZÍ…ýòz²àÙ~)—¿}3¦ì,ÍÉÉþy=O>(‘-ÃãÐi‚“öK¸¾Chÿ$·…m«†£Å¦³ÂE"ЦåyaÒ+ Jº åò ì'aÇÖ=œ¬ó‘c™4Zèš »2iÂG³"qªE¦°¶{ zØd;t2¡´ºPî~ùqˆŸ%qrQûx¤¼ˆnrçHü0_¸åŠæÍžÎ¤Üþ25M˜¦ÄÈîÆ8*dZûiíg~î終ÁxÕà('»ç}êÝ]#üþ2M‡…ßµø|0^˜>Bã¶L¨<‚ÀZa}U^ï½1òÇ;%´á~Fò±I‡ŒáfNî2¢í/¡\þ~yNÛàn —êòÞGìqWžą̊ÀKqrßU3ΧLv&ïÞ‰ÁÁ¤3œô¶ÍûWÛ_˜÷\vp)F>›i^†P.ß®jfœºcËɑ܌ti°D^þƒÏ=Ïp²õÈxèÎD —5Cò(ËÈê“ôp^Ÿ,”»ßâk<²…rrU„«„³$ü»…‘C2c0ýÓ¡Üþ s"°äÇyF^8Œº5}…Ö~Zû™ŸûµK‹òÙ1œtü¦ƒúv‚°ëŽp,y–!Ô/¾•.ëøG éøÝùS§Yxp˜%\ ¯— EÊøDa‹Ff(ü 3òªC4ÎÏ¿ ”Ë_¢¾©gŠ0ò]w´¹=…;Í&ÄÄ6ÖùjDæåƒÂKGMh:¼5'§åí½=®‰iÛ#3þìdä‹5*Töü ‘rù\Ïš‘Ó¼´‚lÚËŒ³¯>Í%쟾“%™ðÄ¿»Ðå°ÛÄ2R×R…˜]j)wñ#æn;ÉÉžE%Ô}|@xo¿«ª›9aƒåôÊퟴZ…:Kß)Èô*Ø/}-‘Ö~Zû™Ÿúiùª¾2 ¿Ÿâ¤å¼ð=\?›9i9vàض“–ó9b€° NZÎûVãeÕþŒ´œËåß_†ãv—Œ´œßy¬G¥¶ Œ´œ÷0adÍŒ´œçò0´ª{†“›oy{&÷ìËHËyá]y?‘ai9ß Açûë8ù·÷[úçõ~ïvNZΧګðOß5ù·ûÛíÆ8NþíóÖ~ZûùßÜÏU›ðŸü6=i[CùºYú?&ü36õ.m–ÈêSÀÿÌW _+1ûAQFz­VbÍØ“Y-Àcy #ÇGct¡›Â~fŽÇÜ…~‡a‘ý5¡\þ5XÛé'£öchœNø­³ ê~‹9^o@“™QÂ*^10)¯q2¶¤†­aÂíž‘öñ–°k¦Å-Ê廲H‰Ä{ä’Jøõ>f$MyŸ[êµ12òáŽXh \~ž&¡`Ë“Â_W#poâ-¡Üý®gtØ×î,'_ªBÑ^‘)Ì­‚j/¢„ÿŒˆ‚ùÉ-¡Üþåp&(Y"§…›±ÚÖ‘rÏ[ûiíçÿr?}ªhÐ`Œ#‹´Pƒÿ3Y˜0Ð ¯²m…³:pTª¹GXTwÙÎIß½:ôŠ;-´ƒÍÝÒé“÷¾»¯´HØíˆ<dÙýÁ¸%é8)—¿Ëj-²æ&rròÞPؤ^®¨‹=®0òHáx}½!œþ&ïsSLNV\Ÿ€k;ɆÎf<™Ý‰‘½ÖFÃuýM¡\¾JáJ´ïwK"[jU8·¬+'·_‹Ä½‚YŒ,[' ûN\Öü¥Fé‚îÂäJ̨)‘r÷×n ÃÄ¡W8ùq~8ú,½.l9݈‡ýC„›Ë& ¶Ö‰”ÛŸVÕˆ—B9ûq¹ÞZûiíg~î§Ç*Ú¶fä켯»”ðF/WŠnà±Rå’0©a4ZÙÝâä‘%hFÅ Óº*á›YÒ™¬Ú^¨S8éÙ4‹f$ _¯K@™ŽÞΤ\þ©áˆž|ƒ“l£ ý¹%¼ßN çòfFö¦Áüó;„©‹Í/ØXØ6׈·ã„S4Xs›pq)êÄØsR.ß½æ4üº•“ëäýÜÍÐÏêC Z``ä‘küîwPh®§ÅØM&Nö¦‡ã¡ìï÷‘uü&'ÇwŽDÑS·„aÍxÛ¦#o½ŽÇÅF¡ÜþÁcµh4*‘¶ÏTXýp°ÐÚOk?ós?;®R¢öÒã òC/%Üúu&—?ŒDËBYŒœU4 ß«^Ú¾2¡^÷œœš·w¶M_'òs‚…c’9ihZEn 'ÚÅa¢ö #¯ûáIi?¡\þûýb0ÿuNn^«G‹æç…—²•xr¡0#×õQb¤ÇF²dr,Jü¹ÈÈ‚çÂQÙ ¡Wáp4hp“Š( ýÚÊå¹A‹ýÞ‰œÜZ# ÃÂ,^n1Œtª¯Æ ï‰Â1 ¼ø†‚ì‹‚2)—¯ñ³l_v‹“ûÑ(ùç†pØÀ¼÷Q¶ö¹¾¦ 5ÚWádù¹±è¸ÿ2#+ ß+”»éW4~O¹ÁI·'1(îsUX6ïç êw¥‚<ë­Âp[pRnÿîn¡¨Uå<'Ï}ŽÁêIW…Ö~Zû™Ÿû™{O‰èœl²L…À“m„—çåtlËH·©JLo±@AF~Ž‚vø-FnœªCÕU§„õÛ% ——^A:HxîâÅH¯Å&T»2‹“îýLÈ쳑rùç”ç˜ë·“™0¿äla¡LŽ=+lp6Ÿ d ·V"WUCAr¼_:p²í¯ \j§bäúyy¿ÇŽl“H¹|^«õÈv9ÏÉïE ðU„+Ÿ©áØj“ðùæ`ì(¥žœ¢D¤«‚œ}DÜÝÃI¹ûªS±óä"'ûªõpîtNøë¸IÂ¥c`¬}C(·ŒNƒCG9¹´sß ’Hk?­ýÌOý´|Õk¯†)g '-çëæ(q×°Y"-çïjhp5̓‘–ó¨2±Pý¼ÂHËyìÛp4O¿ÁH˹\þ9«ÍxéÚ€“–ó §õ˜7'ƒ“–sÃÐ Üy‚“–óŽGuP½<ÍÉ¿Íçß]Bƒr>œ´œ{%èPðËNZ΃OGâ\ÞÏù·÷glˆCMßtNZÎGFJèÒí('ÿvÿµfT_›‘û¼µŸÖ~þ7÷Ó®g"þ“UšÄBÛè#[¬F3O„~mƒ0sj's<µxÅ/Ýž±jQ´ðP¥tXÒAh1_$)Èø!JÜ ¯ÂÉ©ã#áã-ÜrÀŒ#_ÆåòûT¡wâdF¡Ä¥ŽÂ…ƒá<7–“¥¢BáÖ÷ŽpµO$î·Éfd¿#ZD—ÎîHÄj·H‰ŒÙ§CýÏ×8)—ofõLZò˜‘í[F"äëSáÄ*krŠ“9J–õž¼ÏqzÏ!F&MBTål¡Üý1jÌ-¾Ÿ“%Fh°Ëó¤pi2džˆ#Âq±^‰”Û_xb8ZD¿æ=wšÐ÷áVFVœŽ Y„rù}â±aŒRA¾^ˆõ;‘—ÞFa¯ÏSN.)². WÔøºp?#¾ DbÃÇ r#Œ(3Vâä….‰¨õ>™”Ë· O¦¾¹ÇÈÛ´Xí}A¸ÎÍŒ“ë'qòòƒœ›tM"×îÕ`Å ?F>Ûˆ~/Œ¤Üý“4h;IÃÉ.ú °‘B·¦f¼Ù²ˆ‘A3ã0TyU(·ÿúŸ@ø^ú¨ ËÖTbæÃ¢œ´öÓÚÏüÜÏʋðf÷}Fö½¤ÃÉ-7„.ãâpöÝUNÞ¹nÄž&1ÂkE 8pï#WŒBíjÙÂv×ã0æð%NÚ_1Á8c£0°Œg\m™Ñ>ÙC8)—¿¼:Gn—È_-•`ÇKsò㎇é>ÂÐf Úµnáh,‘žMÔP<8ù§‚vÃO1rÓ -ü3Ï åò•- ÿØXFö|­†mçÃÂCÝQØn›3©öJ€ºSIFš_ár¹N¦·‰Á~çÇB¹ûÍþ ÆØ4#'·u Aå)ÂJ›Âp6ó>#OZûiíg~ê§å+×S ]«æŒ´œß5Ä#róYFZÎWÔE眴œ/ÏëýÃi9?҈ñ¶?'-çrù‹ˆÀ­ëO8i9¯u8Í9#-çe'›â5ƒ‘–ó-û•¨ú¸#ÿ6ß&G &¨¼8i9¿2;ãËi9ßñ"úÁw9ù·÷×¹G [›§œ´œ(bÆÅæÿ0òo÷†£2–‘û¼µŸÖ~þ7÷³Lí$ü'‡;$Âx¬5'§ù&¢üò\‰ìT$ G£çgå$¢ùÕC ²É¤œ>ý‘“gÇáéìÇÂ!îqxi|ÄHÉ=~¶„­Nðyìeá½·Áðd…rùO)ñ¹ÿ'YávîµïÎÈ-¿c1öýKN^Í2¢A×4aÕo‘Øû#‡‘ÕÌZlw_x¬_ b_¾Þ¸§Æ•KAB¹|Å[Eáå»NÎm‹yÎo…ž§c¡ºýŠ‘¯¯GbîÏáõqH=r_˜XP‡­ åî¯î”÷9lVQFÎú€¡Ý.*Ȫ¶J˜`¤ù^ŽuÔI¤Üþµy=ð<$‘ý¿%à™3#åž·öÓÚÏÿå~º~K„ý?»$²[Í$ î¸ÀH§&Àys_FŽßk¤;'…{`BňN–÷ID¹ˆïy¹Yö¶áŒü´#)ê 34Èxy×H¶_©Åéaw9)—ß7ÎŒbîŒ4t5aê?ÂçG̸wy+'ÏÑéå]‰xX íã@FŽ˜ÃíJÂ!ïX7E"÷<ÒbÂ蜔ËgcI]ïsrœÚˆËÇN f‡¢ôÖWŒœå‚%¯iáüª¥°¶½ÍmsRîþ»ó¸s瀂ì[©±0¤šw“uœüàŽz½Êíß–mĤøTFJ¢ÁJç­ý´ö3?÷sNù$*ÕUE®%bax‚­3Â~ÚFŽ=­ÇÛg¢‰˜š¡ U’ á1Òø4ŠˆX‰|¼@ ÷¼ÏAä¢RŒŸ“…6y¿'2WVrù“6KÚÔÄÈwŒ«uExª@œO®To§% Ù¶iŒtÕ`Ôݾé«S¢\áåœ<òÇ€ê;.ûWOÂçEZ#)—oöŽn-ƒ8é–nÆ|çÍŸå5øÒQÇÈãÁJÌž°Lhú£ÃþÏ8ý.eúßÊÝ÷À‹ÆíœÉev†øÉyãaêw—“Ë& W¿B¹ý·ëjás'‹‘ÞETÜÔ]hí§µŸù¹ŸÏg$"1Ä‘y¿Ç‹íê+L×G¡žoŽpÑá0|íøVø¼J<:$ßúԈϰá‘¡xÿé'·´ŠEVÓ·ÂroõhÔ)›‘jš-”Ë_lLVžyÌHÛb±PJo…ƒì9Š­Nr‹ƒwÎ#aàÛ (÷§qò­9 MN½n¯"aÔèdF.®ŠõOž åò5¯™ˆ1®-8Yõe"ZJäÀúè±ú‡‚\ã€`ãý82×#ßšÙr²ºCÜZ-g¤Üý÷y?w§È¢· *©Æçý»áT[A>õ1¡wÔ FÊí_’×ûò“›;“¡Z%Že.㤵ŸÖ~æç~æ|6£@¦#ü0áÍìCBרÔtº!l5ZƒI##„û†À_N¬­BÌ-á²A•JFqòÑ–Dø(m…«ñ§ÜG‰¼‹0è?½æ¤\þËþQ8‘ÃÈS#°yûG¡²E&¼Ï¶¿¥Cô°l¡±j,†®zËI›gF\ŠJîíˆÃõK22ê§ ô^œ”Ë7!ïsf™U³œÉ<ƒÃn+Èj ±¥ñ/‰¬ª„såœÜ±1¶Þ0²OD0 Ÿ;/”»§D âœJ$â‘ÙŽ“åÞÇBÕê#{gè0¼p¶Pn…!ð¿}“ÆQXšœ#´öÓÚÏüÜOÏ[gÜ‚i< ÁfvJQâöUBä}÷ù¥ ‡PvÚha•hÙ”“ó5‰Øeø  —ê8.Õ1²¥SÞÏA™@N†tLÄò6 )—îÍPœé÷Š‘Ÿhñ¾þ=á¤úAx“) „(¡t[&ÔÕNÀ»eK8¹Ñ. !‘??GÁ}r'ÓlptÇ*¡\¾µé W /#S{šÑ«ùáëÊÔÞ¦ãäÒ.!xÝýŠ0ëuêg™dôT¦ñä¤ìû·“¸Ö¯'=~¢s¾Â¶kT([b#SòÞWŒ^ 9‘rû7Ž1¢Ý»³œ\•€ åƒ„Ö~Zû™Ÿúiù mjDFú9FZÎÃòrJ ¼ ¤å\Xç_Ñœ´œŸ Eýî¯i9ß[6}V}`¤å\.ÿŸÁ˜º0ƒ‘–óq7pü ¿‚üÿäˆóÁUi9ÿ&%`æãþŒüÛ|¡}96 ‹b¤å<ùu(¾â¤å¼¶g8*9~àäßÞtUâiƒ™œ´œïí©Âv¶ƒ“»¿b‰$Ô^2\AþíóÖ~ZûùßÜÏ?ŽÉøOnËР˲ Nj;£è³ÛÂq×ÂávÎÆ…lgÔb[ß÷Œ\ªóÇš1wdFX ºú¯áä´=XïoÂíjxz”°lÝ Øê2©KôÇ ï9)—ß{‡o NÖ^„V®Óü8íK¾«˜ˆÔçn±y^cdE‡Xøîû-¼˜ŠN-pòóo ­í2„rùî–FÎÚ.äÈŸáðýh#,õØ·½#$Ò¶B šÿÁÉ"?ÿÏïÉŒ|=X£çr…r÷=>‡bù÷ŒìòGÏÀ¯Â/ð »ÁÉ÷‘Õ¬·Pn¿¯wÜç·âd÷Eì2¥ åž·öÓÚÏÿå~ν‚&±/8™³T‡ZÑŸ„Jaà¶ Œ|_L…{þ>„]Aèºê*'7õǪæ6&R囄GJdÚÖ$Tz_œ‘å{jñ£áN~(¡ï¬+B¹üwv…à|è3Nv¸®ƒÿÅ/ÂO“pÅF#‘ïn$¡©‹¤ ½÷…¡D›ßŒ,p8—®^¾«’ŒÞ·6;“}šK»q•‘rùzçê0Ðã+#×Ö Á§'›mU˜R+€“j!~G¦°T•ܺ÷˜‘™c0u}E¡Ü}ciÚLøÈÈÛµµØÙøµðÞ…$ühwJA¶œo†zFÊí¯U.³L8™³<~·s„Ö~Zû™ŸûéÔ( Å~ròÛóp”~`c"›d fRoF¾:ë"õv(ÈU{õhø“xÄyÏ,5Í„Iã¹±‡­l¾ ?•LÆ­Édë[ñøîþ’‘rùº„c€£‰TÇDâñ•Âopéõ&F†OãP¼Iö¨¢Dd½ ×ýQlœ·‚|xZ‡M;¿0’­ @å¹õ„rùªå}nÔÇœf¤y¦ _rTÂ/ÁZ̺÷Ž“½Â1e§‰ì±)ñå–prús¾·ø*”»¿“…àp¹§ŒTN F'ǻ£Íc±¦ËoaüU-|?¿ÊíÇ36õWs²üƒ$,H —Hk?­ýÌÏý<‰¥ydÛ¢qºÃ¿9ãÞ[%²Û£|=Ø‹“ß~%"l‚‹ðƒ]2ÌÛë)È^…ÃÞî'#yŠÚ›„kOa¿Í áÇ0ô,ZX"åò'7‹Ám6&²ÃQ=Lë¾qò‡wÜž}däÂQ°¿XÀ…,Þ$ž¾8éZT…n›}„λ•XÙøðã°H H.`"åòmÄèÅnŒ\¿8Ý>Ö~‰Æ¶’L¤­Š\ûÈÉ X]æ¹p­KbB åîÜ„惮2r× `ôYáMJu<(´Óø£À¤‚Τìþ*Iè³¾#58’jZûiíg~î§Ñ)+ýæä‡z´‰þ"4P!¤–ðTÁ ©wAØñ^ÂuîŒ ºcDV¯›Â»Óz¸ªpi¡¼˜—%‘¡{B°‘?ãd²ÚˆánYB¹üþOã‘[ï%'û–0îºðY¬}êaäÐ ¶='Üò9G•8Ø4ÙÕ ˜È_;Íps àd£IhÑ¿ #åò=OóGËäõ òe¸? .+)‘œ%dõ¸ÊÉ,fFèð a…͉Øõn #oL‹ÃÓŸ„r÷M¹jžÈHï®j í+œã¤ÄÌa»8¹u‚ÏοÊíï²7þ¥m\H‡æ:Ü©šÃHk?­ýÌÏýÜ}<F½æäâFÔ¨}_x:B‹í%ß ;g„£J²‰D©|ÔÚ¸ÕÏépðÄFÖ7«1MÇ…'uèì³ðlåd,nèL.ÜmÀ÷KO)—aÞG²›‡Lœ< 4#b–Ÿ0¼®cZîd$Š Ié‡ rT¹¿qòìd÷ éÂÈÂñ˜4õ=#‹n FÛm÷„rù†¶ À¼F…9éÑ9—#¦?ý“ˆ»§ CN%¡_‡‹YßQ‹¡ß^1ÒNŸ—;¸“Pî~ú!‚3òaŽ~Ÿz†Æ ×é?œ¬\Æ„ËàB¹ýíG¨PÌOÉHÓ°Nií§µŸù©Ÿ–¯„ ãY&'-ç=JÄÀ>ÌÆDZγ÷«QSad¤åüýõdùÃIËùÜ%Á(qå.#-çrù“ '"ûÇzNZÎ}Òý‘{q£DZÎÌLÄèªs8i9"ùC½x°‚üÛ|úëJÔÛwœ“–ó+“Ѩ]¦3i9oQ8ç· áäßÞß>Y‰g_ö2Òr~': k+=“ȿ݀ë»:qòoŸ·öÓÚÏÿæ~–rNÁÒæ±?Ü¿ŽbdÁîþ8Ò²¦pfB ܵ5‘Þ¹ñ×ó'—Ž ÂÓ3á9×pLžTÜDšºç½OÈýÈÉ~‰XtçðZv šW2òj¢?6à¤\þñ'ƒÐñøKFÖôT£V…ËÂö±p›mëB† GJçâ³=”H-£c¤ª¥?†ß­(¼cN†ýœl¸!r´Œ”ËWãˆ'?rN†ïLÄ ‡„­‘ ×>3>wî{ [-VáÐì$N¬ C` )wÿ÷< Ÿy¾ Ý ýÞúb^%LäÕõF<}ÏI¹ý[jÀ=j'‡Eiñ@(÷¼µŸÖ~þ/÷sý?li‘¡ ùa?´×TXÙÌqwÆMN¾€ñëTÂ9ßôhRØD.ßmBÕ!ç9éÚ*­›ÙKd¼wšËȵR6¬²5‘6!f9.qR.ŠA‰‘oc¹µM Êž?(ls#½>|®í¢Bh_³p¥ÆÇo–ÈÒIØ÷`';dE¢uz 2ÈY‰ô®ZFÊåkö' îrò~v2ŠÜ»&‘¿N˜à³æ#íñîQŽpK{\[\æä¨:)xßp¦DÊÝ·+NQ\ÈÀrq8<¯°Ð§{ʹlä䓆)hy¤‚”Ûo¸Ž ׊›È‚žñøTËFhí§µŸù¹Ÿ wú¡”“»‘ÔGúáÕ‹#9ÿkÄ æä‡4ÍÝ-‘;ž'ácœüÜ.‘ǯ:“Zrür¹ÏÈŠ8¬/îBî>žŒ Ë3²üm=F(ìBÊå/üÛ¿®Mb¤m]tkRNø½tÚŽœ.¼}Ìk›ôP=›h°µg'vÅÆ "&²@1ÔØSˆ“6‘!˜ëõS(—ïS šÌ÷6c?'cÙÖdy¬£·'u!ïŠB£´Â&! ¨°.ˆ‘G"bPჭ )wi9=œµE]È9¥cQa‘­°Î¤/ ad_U–<*èBÊí?ë‘€Ùóµœ|P8¹}µií§µŸù¹ŸµKúC£.ÂÉÃþX4 ÂÎSð=|—‚œ˜›§êƒ9¤mr›mìÉ‘¥¸'\­E5]²Z¸}Æ0R¿Xiʋ’üйG¤DÊ埦öC³cä弟³·÷ÉZ¥ýÑ[S”“Çb}ÒQáÒÜhŒL.n"§ºãû“¯npçÀá?/“QÝå’DÊå pLFËZ=Ù(&ïêïÚ_…Ó¢.¤çõ`œõ™‘Z!°a¶Ðí³Î,ý¤ åî//ƒ­±Å]Èm¿¢0u] ¡Ç“Pü´+&3[…×)·_ª› åü®Œ¼éhÂÛÝW„Ö~Zû™Ÿûi€ï——sÒ¥c {ꔀ8g#ë$sÜkuS8wB,æs!{ž EZVQáŽl?lêñXAVéíejqr^-ÆÄü²¾8?ÊåϹ臊ü”D:ÿ㟻Ûr’wÓÀ´ò¶ðÕHR×2‘Ëw'ànf'ù±d$Ÿ//ÜñÃŒÀDZŒÌ*‰–Þ%\H¹|­2ÌØ;ÄÀÈ9K8Æ5º#|2V+S3…³v¢Ê?'…>`Q»Ýœ\\6éC‹˜H¹û‡r"ñ6ï}¹ÿEÞìù׈{~¨vKAn4úãͺ~œ”Û_¬C,ʶ¶u!ׯÔÁóZ!¡µŸÖ~æç~º÷UB±HÇÉ¥î*¤$ ÇüŽÇ亿¹95ž×m]È¢¿Õh`“‘óz Ãâ¥ÂzFýJãäÙú¡Ðn.b"íó>G5™íLÞb„Ï–Œ”ËÿkuÚ÷ÛÀÉ]Wñ3:@¸¡HìÒJ˜H‡È8O*(,X9—ÚìP?>%"²ÛFVIDÑ×~ƒÌ3VWã¤\¾q’ï×~edl¢gZq!¯žð›×…‘üp2b‰‚üvÞ€{O¾pò•s2*/qÊÝ_ò »ow!ß Ãª@[a¥X N_|ÀI—¼ÞžÚSÂDÊí/RE׉‘ÿø¡ü©ß ÒÚOk?óS?-_˪±Æç:'-ç‰ã"pŒ•p!-çÛýpÁœéDZÎJ±˜;¸˜‰´œßwÆ•ú9Œ´œËåï·V…ª-’9i9—â9‚orÒr®ØÂq·ómFZÎ/,Ôbá5ù·ù6œˆ†Ò¡„ i9Ÿ金ŽH¤åüAbŽÜŸÌÈ¿½r5§ßu!-çEæqgßNþí~Ïvþ(X¸ 'ÿöyk?­ýüoîg½í©øO–Þ×Ë\È–ÇÂPõ±½ðN+5Þî~ÎÈ©_°É)Z8¤p ÚùÍãdÒÖT\*?Ë™ì¶>)îU䟼ïóæÈ“Œœe‰C¥L¤êC"Ê}ç¤\þaNI8¯ fä­m (51CX»0¶®,l"u%ÂáèV^øË m{a?c<–f”î™à‹VÛö(Èò³•X6ð'åòÙÌöGÕkžœ<舯⅃h0¹Ê'aP9-ì^”0‘Ë_¢nZ#¯;ûâаFRî~ÑÍÉhØv#;ýLÂÔmG„‡Äãp\I¡KÄn)–“rû›_Çû¾Lä«×ñxÚ¨„Pîyk?­ýü_îçÒÚŒ/ãB:Þ {Â7Á~hí?“‘çûb˜s¨‚tU&c̣͌tˆ5á~R¶pü@ é6.ä U‚*:SƧ`ĸ!ŒÔ–×cÆôr.¤\~ÛÝ&hÎ1úø &Rî~ÿ9I­bdJz"Jn\’ŠœNs$ÒÆ+®+·0Rn­‰èmàœ|ß>½wÝ‘Hk?­ýÌÏýì錎¡…\Èë‹5è9ø+#‹ðE`Ñ$2~Šª ÄÉp=Þ)ëB.Õ†áåO{áûÌ`Än/*ì{7#l#i*ªÂü.7…g·ø‚=¹$‘rùÙ³xÌW”p!Fê15»¬ðêþ‹<ËÉzÏ’Qúý2á-÷Tl_rÖHYž‚YEû0r^V Šo¯)ºØ€“sm]H¹|;hÁ|JšÈc;Ã`wÁ^X¾I<¯+%TÔç(×$—“uz˜pjÒka'¤b(»$‘r÷›UND§$FÖš˜ŸÖ„Ã%øÞù%욉È.¤ì~¿d¦udäð83Vo¹)´öÓÚÏüÜÏ¥Uhrè #çü£„bbæ¿n@té`NŽ?ªÂè„é5Hºø™‘îøãÀK/¡ï_œ¦ µƒü /߇“6„@]¼¸‰¼ìÈQjw.'åò÷ÓFãÒÖ .¤ék:w.\šŠ¹®Ó%²A‰TjgËÈyW0¥Jº°{3#ŽjЏޛ‚ñ{ؿƅû¢êúBŒ”Ë·úI$>up0‘@,²Rí…;0¾I'{6N†ÝñýÂ!Ièû͇‘æûztI-ãBÊÝï~ÄŒM+²Ù-Õ„×¹O…+?i«ý)ÔUõǃś…rûÃCõh×:ïß'rU(|þ«µŸÖ~æç~†· Äê§qŒ¼l€¦ }„ ƒñÕ¶ˆ‰|™Šþ^å„]FùÂß¼QAm釴óŒ“çú«à×ëŽp¸s(b7”5‘5òr¼ó:žtkÇq|û7FÊå¯Ü7 ï\í]ÈäP->J »’°vµ7#íÍ&ôÏ*Eãkz2Y¡ÃúvÂc áØ4Ž“¦D¢Dc)—ïÏšxÔÞUÒDòÍîïÿÃÉuyïl6ÖžõHÅE~ØHæ® Æ0q!·­ôöd¤ÜýëÏ8ÒbÞ32©‡êäwaXÞû¶VõÝ$2»i Ö¸ÇqRnÜ(.>½ÃÈzÝýðë{÷µöÓÚÏ|ÜÏ·µüÑîŒ ˜è‡ÆÝ CæFaJCùó·Ö•ŽîªDÑÌ N>=Ÿ2%LäÔÎz¼([Nøf¼ž{Ÿ“MN„`ý¬â.dù¾ðhtKAÊå7· Æí …]ÈqÏÕpñ‘z÷xD-/éBÞ8…‘ŽÂ#?Tè~ý)#§þãKæÝÂî_ÐÙ'…“{¤âKâ))—Nfl3ÞçäÛí‰X~"^¸°v*ªtÿ¢ «E'£ÍÏŒ,»Éホádå#y_§ºˆ‰”»Ÿa/aÑÕ.dº£ Ï‹Vj‘äXÊDÆGÅ¢¸½Pnˆ›/ª·J‘Ⱥ-182Ž“Ö~Zû™Ÿúiù²Ëð…íùrŒ´œìž÷u}¶1‘–óšÊ(Ô«ä`"-ç Ç¥`“!œ´œÏø ÄÉÜ뜴œËåqC‰º ¯1Òrþèˆå*–q!-çÚѾ0|uW–ó#õ$Lw/àBþm¾“’±ÏÍ““–óÈÁ‰(XÖÄHËùzð(c"ÿö¾gu¢·w!-ç·Ó͈9zƒ“»?de¦¿°5‘û¼µŸÖ~þ7÷s}ñtü'Oä¤ÏX ‘Ãòþ<ãN9w¢ÅŽW6‘ß*DÀÙ»–ðzB>õ¨*¬\ Ï|⤗!]2ª›H{Ç8Œ5;  y½zÉÈâEÂÙ³† )—ÿÿü?ç ÕLäû—á0®«% Ý`À¢þÂ÷Ì8µþ '£ö„ãf‹Z&²ëG=T¨&ü2ÄŒ1rc‹0ö¨áBÊåë©H†gäÖ û™-\ö8 õ'žTwϧâg­IŒìŸ÷>çàUaöÌßÜ–“r÷ìLAÝõ'8™k— ‡±žÂ+O40–/åB&õC}'_FÊí_á‡sƒ鑿çýÛ*H¹ç­ý´öó¹Ÿ ^¥á\”§‚œq, ã‹0ò~v º.ªi"7î2`zmáÕ’©x‘äÉÉÙEÓñÈ7ÊHzK@èýwœÜ–ŠÜs„ƒ=}Qÿ›#ÍòÃ??NÊå¿iˆÂ¼êµM¤ç–X|:QC8òw2šÌ áä¨%i˜¶ÆQ­6a峂&²wj2–9Fp²Ø^? ÉQ2²P´ì‡÷â¤\¾!.&4èPÄ…ÜÒʈ/í…?ã“1¿e$#³àøí…°O–;~5‘÷˜î×ÊÝ/푊?+8Yðu* –1zÃÍs€‘D²;„pRnÿø™¾( ÞÈÉbmTX¤þþÿ°_—QQííûÀm±•£ˆØˆXX°÷ew+bbaw·ˆˆ "Ò Ý Ý0{ÓC Švbç10ŽñçÅÿþžµæ·Ö³—5óæ³Ö¹×¾ïËáâ0ÃTõSÕϺÜϰÅ$Ž|½²¶gëÍ™Š,n3ˆ¤Úô,äý,È ž¹÷ÝÁ‘MÕ2qðÒæÀw ïmaHþU•òéQyö~4â÷i‰d«&Y8t«B ¥ò÷ãSPܸ«Hö©LCÒ¶Ì7?¸òy¸!i锋%vs¤®‡Uã äÚ<LÖ–’ÂÝ0´ýÒA$å‡Ó0ýN;¦T¾ÇIëÆ“ýwÅcJž3´•€ñ#[0]MÆ! ¦Þ³L\jZ%‡ÕòÐföì4Rêþé‰ d~"ól˜¡hÅ<âŠöÚmDRh¯ŸZL©ýƒîDâ|#M‘Ìß‘Œsþbªú©êg]îçêÃ9h´Ö›#ÏlÈÆ?)L×·9h{ÂN Ãjÿ]ïœZ1/·K…Wˆ:O<‰UÛžÌÚ ¨¯Ý‚¹«s<ú}ÑbÞµP ùšö¹Ü%::ñ¤T~÷ "¦tm,’dàÇÌ·9mI¦Ü.åH¹§€,Õxòí„´_È‘qÒq0ýsÞX–È„»ÙhÉ‘Rù.¾Æ@ -žÔ0ÀÑM1Ý'Å£ô¾svpÞêô`þÜ–…­³.s¤ÏœDd<×äIÉßß.M­“÷^*à²ä<³¨ŸˆoáE2ÿ¯\L?+RûgÔ~YQÿ¥@&¾«ýÜÀ-`ªú©êg]îç€YgTΑûr3Ð)ûÓ«öïȵ±äô• ´ÍëÍ‘ý‡âÓü¶<9a}Ö-åÈK7ÃðñyžÜ¹42ÙeŽ,yC®«ñäžoÜLìeHJå/ØPû9 ÷²@¾n–ƒ>ŒùðZ-èÆ“Yö1xõB‹q3{k0g˜Å Þ-æË 8³ºÓþCk¿p¤T¾fcÂ`å׎'“õCº¿!Óh8n·ïÄ é‚œ ™Æg.â”isæ‡,páròÝHXy¶âHÙb¬ ÓÈÉÚÁØóÓ~@2Zùþ%’RùÅŹ8¶ý¤@fNW€®Ç\»#©UxÒ½q0&¯þÊ‘• Âð«~{žì97Þ·îqd`š7¦÷27 { BZék”Ê·é\ |ÂqdøtqMf~H€£uS¡î/óúh_kìH»øPD;´I©û¨—‡­›5 Éħ ¬;ç ,JE§ùyòóˆhL¨ìÁ”ÚßòqŒ&¶åÉý6±Pçz2UýTõ³.õSùõ¾, 3íÚò¤òܤˆv)yRyÞñ¢&î½(Êó… X;¹B •çór±.{µ@*Ï¥ò»­€ý‚9©<×»ë‡ÄêŽTž·žè‹Jýƒ©<>Åcâ-‘üÓ| ޾hþÝŠ#•çîë}ྩG*Ïݼ“°$½›Hþéý·‚ùî’Êók‚1«ÞOŽüÓý㦆âxa+žüÓçUýTõ󿹟ûËòñŸ¼êè]ÿ—y£Y RÔÉëéiøç óµZ¾¬jÉäûFáé#]ž4Ò A½õ™ZŠ|¬ætädÁˆ<¬u>Å‘F ƒðãeS‘´°K‚߆L©ü«ãSP/½/OâI"†ÑeªÕ~OvÉž½#°ÇhSÇ,:¿s¤n;„lweêØæã[ë*CÒøX:fØ·áI©|Í=½ðv¥GÎoì…•Ö’í>È1ùþ_<™b– S·L£+rØmî&’V LJtH©ûÇnD K¶ŽH:þŒ„y˜.S÷D d»ñäÍ Þ(oµ“#¥ö´ †ÁúÖ"9gpV¼ÓeJ=¯ê§ªŸÿËýì„“7Zˆd‹å!˜<©³AH6.ôz$6(Ðþ…/sð_äTqds/ô›×¹>&Áêóä²i)H/éÇ”9(°ÈÆC c¨ïìÊ‘Rù7ŽG¶þ`žl¡ó‡zÌç8¸.,’û?§`e«¾Ìe-¼PabaHZ\ò²õÃ2"&#Fêòdê_¼´LçH©|w›xaëî­r2À× \ˆ®@®Îƒbæ`žìÛ8 ¦ ºÌòþùø•8€#[ŒÉ@w®OJö³O4ÐAO$MçÆ`æ¯mnè¯ß÷åäâp?ĘÞH©ý]Z¥ÀcY‘lž‰ž.õ™ª~ªúY—ûyaP(Æ-î.’GÕÃqùW/檾µïË[Ü_šÍœQ9k|m½“†Ó}1­eš@~ò‹Â°“ƒx2Å36¶cN¨H­—:ÌáAˆŸÕŒ)•_>!NäɦGÂñwï>̧ÉéxεÉ®ËsÐúÇ5}Ï30 ƦmDR´ö¤mZ9*&ר‹¤T¾ÅÞˆ}t^ —ZøbI`3|w¼ÿîÉ“‹&#çtæóñxºy03ÿY ^hÊ”º¿óH,¾o,’yÁqøµè_ûî ÷±'³ÑòìZ Ç”Ú¿lJæÿ4Hãô|·^nHªú©êg]îçÆv°8@$3»FáìU]æ@ã|<éÔŽ#crHýéÄ\¡ ]Ek‘¼õ"~t˜.ß}0óC,GŽTóÂÚ¹{ ÉN=ÑhRûrÅø èi&’RùMÊ/BýÙ_<é²8þnÜq0ò·»²:/Gëÿ%'§êD cÍ‘,SÄãu¡Ó&1U=0¯ôS€_zQ ¥ò-LñÇ­o²çÊ ìÜ\$Ó£ýQîûš#ÇÞ÷Atƒh掭^ȳjËœ;Õ›Wx ¤Ô}Û+ñXŸ­'’o¾%À3zói`íïmtkf¯tÚº ¤Ôþ@E.æãH±FĬMíyRÕOU?ër?¯éÄ`ñs=‘¼36½¶fÆ ÌNeGÎŽÉÀï™j<ù}w"u$’å;E´ÿ«Óe±7ž¨Èå`nýƒ9ànfÜÒÉ>µïKUW”ÊŸÑ1 ‡ëóäÇþÐ7È‘Nºùhxµ³<1U·ã™N}åhý[S$Í6f¡¦ò‹@Ž*Èó•F¹M&‡ßìî<)•oך‹Hhª!’#ážÔ‡ùö“s 8ògS/5ÙcHêG„b“a‘Üß!A{tþUâ~‹žIب©+’í''ã]æØœ|-Æ’:³r1îX:GJíwOÀèƒx2÷n8B ú2UýTõ³.÷³¹Q2–艤ýÚ$ÆdÎ:!ǪˆîøŽÛ䤿žp i݇'+n{ãë;Ž”Ê7b`Ò_èŠd\v,nìÌôëåç)ròÌoô_°K OŒÊÑãJæçÚ梁êê¤Ô}Ýõ)ˆ>ÝO$Ïš§âÔÉÞÌëzLhðdèÁXØ×~#¥ö;í ÀÅíß8rÎ o:¶‘©ê§ªŸu©ŸÊ¯¨ )xs²ŸH*ÏÜ…ÅîÁ<©<}&¡ï’Êó9"2_vIåùøˆšò¤ò\*¿Ù{/¼Ü?#•ç³~%À#|O*Ϲæùp[5š#ÿÏýóÞ8qæ”@þi¾†ËñtÌ ‘TžZë 7A ÿÏ‚[Ùx<æÇüÃûK]ÓàÕ¸§H*ÏûÜCxgžüÓý¦^/ –“ú¼ªŸª~þ7÷³ñ«Bü';Ï‹Â]ðäè!Ю4`f¾(„~åá4röBŒù«G¾‹Ê‚ݳ¶"©®—å“âòz•/üð‹#Ó^zàñSæG_¼æž2­ö€É¬ž)•?½">x‘,*ÁvÍqÌy¾ÞØÚ²L 5 À’½mErÉä\Ø…áÈžcÒ1¬QžÌû’ÔqM™†çãPÏpS*ŸAËl¼œÓZ$ó¼s±ÿÃ{aå ‹E_9²&Í S‹“˜ƒ} xwŸ¹ð†; ˜šFJÝ_×Òm¸B9ù€ó@„Z ̯}ŸÕíz2 L}1!÷3SjϪB|rд©ÊÃ/)õ¼ªŸª~þ/÷S·[8fÿ3‚''t ÅéÁÌêWµÿ_>äΑé+¨™ñi¦(ÄóÀ09©Qˆ“¦pdÆØù!MNÎ›ë ®~¾@6‚iú‹ä¾i~_Ÿ)•¿ýõx˜\+’VµßÛ FqL‹8ÛH9¥sžòÌ®%AÌñ¤}u6_óE–?š¶f*ž¹#yÿ*CR*_JZ|¶fäËE°æÏ2;Vz Å§e9ìµ;3$3K|Ðçý |7î¼oI©ûÇ<`Ôe¨@v.ñ@×c¦«g(†l*’«ÞÅ#Ç},SjÿÜ™èüOn‘%AÓ‰cªú©êg]îç¥þ!øXÝ'·ÎBlž&SûYv,kÃü/Â{g?æ›I ¸?æÈ~Eéx3J›'OÂäAš"yíd$\ºðÌWS °ÀÂF —Ô~.XÇÅq¤Tþ!I©xP6B$wÐÐN—Z‘€¸›`Ëq¹hÓgÚE”xéòdA…Î84eŽ^â‹·ÕŸrf›h4ü‘”Êg²¾âžyèQ!úkËÉ M=ðs|S³¯'="׿¦áw'}‘\Ò:o;\H©ûûu<±<ç¨@ÆÊ<ñFÝžy¤8ƒ†j‹äÁä< ÈËH©ýæF _CžìÒ*§–ufªú©êg]î§±k¢WväÉFÿøÁ&[ùýS zŒbŽ{Ѐ±L¯óI˜Ä1›DDâÚ"žyÁ1× DÒ90Ý‹:3OžH†ýwžlò5—*º1¥òwÕÈ@¦»–H&ŸÌBg­öÌQYxRÑ‘¹ä’q‹n äÞ >{/rdbüþhHúgb‚W‘üä^ˆÕ_å¤äïOQ!Ž%ú’ݺ¢hþdŽtä¼ñ©•B  ÷Ãéy D²ò^!’,Ç’–ãs_8Rê¾E/<~ã/nÞ^¸¿,žùûI!6y~M#ó¼óámÄ‘Rûs/4­ŽæÈwÝq}HSCRÕOU?ër?‹,|‘Ññ+G& ñÁPÿ;ÌkÉQ˜ž,‰ CQ¹>sÕÊ`ŒÕïÍtQø`à²é×=ÓG'döÝBÄ<.'jÜqÂÂÃ|fä‡úÛФT~ÿðt]Q_$W ›ö]ìU—ˆ½Ì[µ?‡ëÚ?ÒÈv¦°~¤-ïzcåÑ|¦o’]æÞäÈÝI¨ÑãxR*ßžûùØOŽ|Ù%w[]a¦¾ DÈ…n"éc ë^C˜3·&£wOCžLß ;aSê¾ûo_+=o{ÿ°‚oš‰™ ºòdðDÜ æ™Rû/ûxbÈ`¼°Ç¹ËZФªŸª~Öå~Z7õÆÓ Ž næ…¡n~Ì ­ƒ1'T›'ë5ðGÜ7µ-ð€ç;#ެyïY¦œÜ¶©3u­8Rëw65nÊ“©IqX3dœHn¶ÊEDæ”Ê_ª™ÔŽ 9bErõÏ0ÿ2(ÄÍQ¹"%Æ‘LíS8z¿ƒHæ¯ Ã·;ٙÃa¶¶O.óð€¢É8Ž”ÊçP‘ƒ³¥¿9ÒB+ —:ò¤ìSîŒáDRÿq,êÍÇ<ÑÝÝr²8Òü‡;6o•“R÷sl}`™X--fûBÞâ3n{8~WŒäÉØ?ølk”Ú_95vÉßæ ¸Þo,SÕOU?ëR?•_Zc<±í„9G*ÏÕDoX¿¹Ê‘ÊóË&^èç|I •çÓÐ~âpžTž/t,Äöž 8Ry.•ÓØB8­ÓHåyG§ll]Ñœ'•ç=tb1lô8‘TžŸ{î…{_åù§ùZ¥×~êÓÊsùÕ$XçDRyî4Öe‰ DòOïíâ‡kꋤò<çŽg˜päŸîŸ:6©O{‹äŸ>¯ê§ªŸÿÍý<º»ÿÉ KpÐÖÇ|Ô°&¿;säH¹¦MëÀ“-L<áü!GÚE¥b¦öL‘Ù"Güú3·ŒHÇCŽ'×~ŒGŸ; ™ÆšÅ´±H½B4kV‘Rù{žñæ7Z<©>Ö{µazOMÆõvóDòKÃtøªñÌmK3a;rOö¸˜‚KGg3×qEhî&œz1Zv´âH©| B=°ei)G^øå†Œg¦bV*&´œ%’Eºé8¾ŠcÞÿÛf S2­:Zkƈ¤Ô}ó¾´ïÄ“œ– Ú·fö™…‚³ƒ˜j'¢QƒL©ýgÆ'a‹Ç|‘|·)ÉO†2¥žWõSÕÏÿå~6±(F¹Ë*ŽDY’3™EÝкò_/¹Ë°_ñÅ4XS€ / d›êb\اËÌX“)ÓyrCo?„ Öd6µHAãÂÙÌiA8½a8S*ÿAx¡S G^´õ@Èüb¦ßýœÚŸk7‘ì½£j~2qJ hñ¤U›Pï›Ì4mž+Ùhf℉›Ë”ʧûX†žs¤Å ¾šlH^rȆºQ‘Ü077®4e¶=Ÿ€ü ™M¸Ly¤Å”ºkº7öm“·Ox!cäŽìU†SÇgðäjS_˜ØuaJöG³^‡Ÿ ¤kÇ4kÒ’©ê§ªŸu¹Ÿ÷^ÂÄ'#]ï`H¯ëLSd¡³ä¤Åx7X4_-ã{•`}=Ž|¨^ˆ“©¥ÌåEîˆÐˆg3’¡Ê£Ô€¼Ÿ/Cs}MŽ4Øî…C~ßR*ÿãaíÍ‘†»Á|åRfïåÅX™°K ÷ï*Á´úÛÒÈÜ ?L«ÑæÉ©óK —öÆ'ÇÆ"yO+^=y2­žÉÎS™1ýÜÑ×ÄS óõ”N"Ù=( E¯0·>ËF‘V/¦TþŽ2G¾4$õÈ0ðÁa²™G1æL6âÈÕÏ 0åë5æÓ6nõe*sôF¾ö:+'­ÕýP^Ü]$3®ÄâìI#¦T¾šùîhüÖ_ ix"+þsè¼|i*'»ÖZo\¨!¹*?ß æÉ[ûbQxĈ)u¿h—.ÍçÈ‚;îxg—ȼ—ê‰1'_ än§ ”¼%’RûçIG³[<9K=.Š…LU?Uý¬Ëý•‹ÂêN†FÈɤ/2tP3È]çrÊuæÉß×ÒÑÚv Sw£; º ä²;Þ8ÙB$·:å`Ë1Mæ¨i%8Y_.'¥òÍ\ï.3›Šäü>~3R“9©öïø»šCÙ¦öïü0µ"¦÷ôÌn¡Ë“rÝPa{Ž#¥î,vÇÔŽÜñÀ wNÚ1MGÆ`£º‘H¦}u"SjŸÉaP¨MçIÍg¾Øµ§SÕOU?ër?³t³™>ˆ' ¦G1ïe†cfò,‘Ìèƒu­˜æ›üpµOΰöÀT×"ŽÌÈðíþx‘²UÅ_Ú0«"bÑ©¶×äžå>xªÑ–)ùùü–~[^È{Hd^”‚†{æðdÓ±ˆØdÄ`„y#ErQÿ˜XÎeæÉG¶éŽì旌ׇçò¤T¾²èã6T$ssBð©þxfáœ<¤W6åÉ×öÙxº¤Óg‰–.ß"/uýaaÐG$¥îGrC§g9r¤–Œ˜Ç¬hœ‡g›‹äM¿bT½Ÿ'RûÚ¹£~³KÙf¾ ;Ç&UýTõ³.õSù3RÄæ¡ãxRy¾Ý7_×.IåùTg:ïykH*ϼ.B·­@*ÏšÊÐøÅ9©<—Ê?1Ön1/RyÞ§i8üÌäIå¹§]žo](’Êó§Aø»ëžüÓ|ó…ãà_³DRyþ~`í÷ SŽ'•çg[Ç`s}#‘üÓû\• 3›ôçHåù˜ªbX6ÑçÈ?ݯmãŽ[‹ÃòOŸWõSÕÏÿæ~Î9X†ÿ¤‰I´>mÉsŠX ÚÀ\d#Çé„UÌð;™èÑuSÑPÀÆ•+™32rñuÞHæøQ¥ðëw†#KÀðNGžüÖ=ãOÏÉ…ai(j¼–)•¿÷RX¶ÐáÈ~z¥(T;ÇÔª.…‰0†ùkg ®0mr"ÑÂt OîèŒþ‹˜k¢ ÑGÖR$c6”!4õˆ!)•Ïà@ŠÝNo:•a”QgŽ,ë%Ç·ñ¤ÕÑHLn³…9¡Wn™ëŠä‘˜Rì:¹X ¥îï—Å£lÆ6ž<*"þuµB÷‘ƒER—Ý"A ¥öïÔñFÐïþ"9ø}Œž/gJ=¯ê§ªŸÿËý”õ”ãÍÕÕ"i²]DîË¥L…L‰'†0GÛ"æŸVÌ”™Åè¸àƒ@6~VŠ©¯F1›ôÈÄ«sy²-Ÿßþ[™ûÛ–`Çš;ùV»¡óÎs¤TþO¿K`9RäÈóû‹ñùÁæÑ‘(Ã_<™x"ï¯NdΊ÷FŠú ¦cí÷† Í^p¤]ß|¼:Õ›'­¯'àÄÉ­L©|î•bC-Gn8_‚¿¿—0«š†¢ïU<Ùå]–OšÅ|évõƒ9²A† cyRê~je rÏmãɪÑøþd+Óg[ŽjO7$W£é¡‡)µ¿Çæ8؆lÉO%"Vû-aªú©êg]îgyP†ZÎÉ6¹Yø1•9ߥo® d¸C)âL62çô-ƒ[çæ9¡A Æ´¹Ç¬w; ­®šòdýK¾{Æ€Ù/<™Ûç3Ñ# ™×2¥òë9bal+žäÖæc·½6Ó RŽÎÆ«˜'†'ÀúóVæÚ÷Îå?‰#ìpƯIrÒSÓŸZLäI=clìu˜#¥ò]ØU„1ßòäkùø;]“¹dµܾe†Ÿö€ÃävÌë-ãñÈ|sìÑ$kÌ`JÝŸÕ% Ò·ðä-yÂ:nf¾\”ƒ@±Ì’qÂzSj˜q> ?h‹äãs—±èéETõSÕϺÜÏ¥ù9Øu€É>—˜ûi0séŽ2øö-'Ó´kŸëÒŠ#?T)`­Ç“mWŠ8¡¹Œ4Ô q/^rä7gühñÅl’(CAÚŽL÷vþ{”Ê?äY.îNÁ“fãçÉÌÞŽȺ±‰¹uf0žé3·…¸Â¿Gª@v\ç‰Äâ®"™ÖF†ªgù9ãH0\‹¤T¾Nٹؑ<¹ev*Lg†XÈ`RÉ‘{]жÑ)æ¨v2|Ð(`Þõs†_M+”º?qí%ľØÀ“SZ†#똱Âq Þz¦ý hè`Jí´§ ï·×7$¯e—À8]Á‘ª~ªúY—ûÙc[>dœ¶HoZˆm;0sb/ãÀRŽ|> ]®fæÙÅcñœm<j†Š^¦LÍÉ.ÈÒÛ-ÇÆ{ ûñÖ"YÚ>úÆó™“¼ÓÕk-S*ÿ²ÑX:q!O>î,`¯•LÛÛ>:ms{Œ;â&©1ŸŽ ÀÂi"Ùýx8Ô¯g®ÌIEyÏuÌMó‹0qKc¦T¾-Ö"j–òä÷S©¹´ŽÙr‹3J|à É»Qï« '‡þã‰Ýýzˆä_ááH9·ž)uß/-½;­áÉÖ;.¢h‡ sûWW¼ÍÈåÈ–³œqdÈÊ4Rj¿Ï~ÏÐãÉI¯åð XÉTõSÕϺÜÏó‡‹ðüBC‘ôÍ,Æ5ï'y6(Okñä§I9°8>–ÙÂÊÎQÿša掹͚0Ÿg bïL‘ün W-LÇæ%èÒñ®@†õ-Eª GJå¯HqÛ <ùèp"š½ÛœªíЬEAyy»3dq †ä…=ñ¶|›HVNð<ЄymNöXä’]Wåa`šOJ囼$ËÛlåɱXë¹yÃÚŠÖ¹.H†v·˜‹säˆï¼J$Cm ÑòK+¦Ôý)ýBpiöRžüý4mŒ˜òÅ2ͼ*é–¾p”I©ý‡Lc0ÉtOþîŒï›1UýTõ³.õSù¥{«Ug³RyÞ`T:Ú÷ZÌ“Êsµµï«ù^ŽTž¯x“‚9Ö‹¤ò|~bü0Ÿ'•çRù+ÛÇ¡^ô6žTžkÄ8cÁﮩ<¯ß=-6ˆ¤òüŽÔÑÛxòOóm~§©›yRy>$ÞË:uIåù¼­eèÔz‘œüÓû/#a4—'•çq §B7ˆäŸî_«ð„Ù´î<ù§Ï«ú©êçs?½†ÿdÞ ”:rd±ò-dniYŽu[Ï1Ü/CAå3æ1w{”ŽmH»ë€–Ãr”#&˜'2‘ì†'Þz"9¿…ÆtXÃl—ž 7SL©ü%ó]°}@3‘\·L†AC{2‡O-GÒ³ƒ™²³ åµßëÈ»JÙ°OúŽÊÃÇ6 ™OçÊÑÞõó«0Š®dJå³èîXûqdzK^ÐgöޱGS‹9Ù2ÍÏÚä‘¥îHš©/’=ž„"kŽ9Sêþʨ+Hÿ§ˆ#k:_AÃ}˜Š£Ùhº‘'§ÌN„í9K¦Ôþú†%X©Ö‹'Û¼ÏÆ›§ë˜RÏ«ú©êçÿr?5®Ø£æY=Ž\^Û3ßô‘†ä—´wÒäÉfȉšÈ,ØåŠs];‹d³ÛÞhøÕˆ9wL|ÇleŽº‰åã,™¯–— 6¶'ó\ëk·Ì0$%þñîðž;J$›{ÁuüL¦Ùõ",?;„'“Òsq!p9Óþ®ˆîÁ‡˜ž§`{Í’Ö×­ª›0ïØcn+”Êçêjä·í É‘¢=Ò¬«ä¤úe'ìnþD ûÜpÅ ˜¿Dr‰¶Óê™3ºáUà0¦Ô}÷=exü3ÇL/ÅÆ&yrŽõEœuœikã‰óÉ“™RûͧÔþäØ)žlw7ã-˜ª~ªúY—ûù¸ö} ßYN&Ô¾ï:˜5çr0øÃž´ù&bÑœCLÙâ‹0+=&’£^Åâ;N3C>¦b甓ÌÛ h1`3h^ÿœÄ“Iù±¸‰ÓL©üïÇøÂ®h…H5À¬Ä­Lƒ3"ôBódK¿D¤n°dÆ|Çc ¦ÉWtÐÞÂ|êâ¨Éâ³Ý’)•ïù <›zD »ÜvDp—l¦v¶LªÆŠdþ_D\1a¼XŽ{!¯`DýŽ”º?÷Çe˜´lÓÝt/ò¸ óÀ6'üª®âȳörù[ )µ¿ßv_,¸k“f¶®Ð\ß…©ê§ªŸu¹ŸÖz°óœ) ùèºûóÚ?É0ûiÁ“{k{s£¶7ä•ã¾{IÓ/ Œp3bÊO_ƾ¤ŽÌ¶Ëʱ¾b»@Žžå‰Æ/&ò¤]´=¬ ã I©üÂaYqP$»tCÒIsf¦Y$†ª[òä¿`8•d¶åœ‘s‘“3ÊwýäÈ]+s°¥l­Hæh^ŵ©)•Ï´Àb}‘L€ [h1Ã/áCÐ>æ¡¥áøR|’ùãŠ-O,âÉ>y h’nÉ”º¿~N šŒÕæIÓbŒ^;9à°6FÞÈìOÄ­œ"’Rû/v€CラÿÔ ouHU?Uý¬Ëýœãäˆ0ý$ìdï„·î0¿;]BÓ* ž|çŒÉ™L.c™«ºH^yy­ë; ä Ûå8rO#~)ÅÄ{õy2û´ j´EòêÈ|Ÿ}Š)•?Å'íœN‰ä\ƒ4îtš™{Ì~‘Kxò³¹Æé2WMu@jÍ2Žle±}¢äd×U<îÀ‘Gsá?zOJåÛ?̶› E2ÝÅ® 2WMŽÁΧ™ç„o],™CïÀãávžŒÞç‚5Í™R÷õ¶Áiˆ>Ozn*DZ0Ǭ|iîÇEÒh["ÌŽ[2¥öû=sFÏ# EòÈ;/ô0˜ËTõSÕϺÜÏc‚3Fµ¯/’áꮈzÝžù°…Úª¯áIKMO¬s™ÀÔí ‡L0$'º‚HµLŽôó—2‹'·{pŸsŒ9ïQ G­ÉË‘ôÚZ ¥ò¿º N‹¤õþd虜böQsF» ×ieì€ó—M™5בö1S ãldX5¼—Hþ5–<ù>ÆÈ”Êç±ÄÛ—nÉ…›ƒ‘£y™ÚA@ãøãÌrç,ôUÛÊŒ¬ýžÒ±GY*9ú’ Ì“Z‰¤äç‹¥0'ñd‹±ù¸»wóŠk6ÒÎmÉ‹wK°‘ïÆ”Ú?àÁEø1I_ýä½²dªú©êg]ê§òËÜK†%z‰¤òüëWLŸÒ'•ç;ÏcÆ—<©Ó$?#‹­E²²i $,gŽœà“îfÌq-ÂÐû”ÓÅ+{;Y1ïÞ¨ÀÏË/R*å¦*tèªÇ‘ŠK•XÐΉ9mv2šôtÉtYÆm³eª/rFL0„Ö~n©ØÏla‚ñç]˜/½KPÜk1S*ßž;±ø-ÉÞIfîÊ\hà€S5˜+î;ÃÓDf×’8xɘÍVàé4 ¦ÔýÅz |u°IS³<¸š1çUáEð;òƒO:ŒûÈ‘RûmWæ"r§%O~©—‚yq.L©çUýTõó¹ŸZ—¯a˜_SžÌ>ZÙ; æ–%¸n»œ9öu^¯9ÆlRSŽŒyErû§J¶8!o+‘|ÛU$õ²qöÓ&ÆT¢ÓèHŽ|Ú=ƒåI©üß/\ÇÙeŽ\Ò©³6äÉzùXþÏ‘œ}¦3ç0cí‚ ßäóû¦(th%cfTá¾Úl0¸ê ëó¤T¾Û3ä¸òÞQ$O–g@¿­-óè|w,齑9¾ösÆX0ó‹Ëá¬ÎìX»÷Þ¥’R÷£ùÐM>(’± ¾fÓoF Æ-åÉ6Í3a—|ž)µßôy8h¸ðdbPmî ¦ªŸª~Öå~šÿºÓƒxréâ2Ä6ËT×ÉBßòsL—i°žîÄœZ…>× Éàú×Ñ,°š#g…•@³ÐX$¿u¨Àp†Ì뫣о¹Œ'Èð¼l1S*ò„r|Ò'¯ÿu¥ƒF1;ä–C£]g‘LЩD&# O¦ {™‹HNê™ õÖg™7d¹ˆx{Š'/÷‚ÿ'W¦T¾{=sѵÆR$Ïõ+€©Õ~æ~ \_`ËlÓ'v'œ™çšÔ~¯X×'Ç+0þI¦ÔýG£ŠÐ/d“Hf®)ÆëÕÌnѱØ-ãIïå¸Òãž<1å2bžX3ŸއÖþ(‘¬é‹ƒ—˜o¹L,¸Ä“aõ"ðöŸ¦Ô}ãΘzÛ\$ÕB]°¯ÿi&×è& ­4x²Áí2TægJí;ê,VÆþäHù,+dît5$UýTõ³.÷sWË‹¸Ñ-L$< ƒ§÷%æÆ ÑX7!Š' 7†B¾)œ™bÍÝý™k¬qô‘GÞju c·©ñäøgåHœ²Ž¹÷¼DÇÅ"9ø{0ºÈ”ÊßÉè x^È(á,¬4I§¯!جÆŸGc×ú(æ S\Äs<¹.ÏSï,eþ0¯€xÁH$ßßÅz)•oþÐB¨¹8ñä Ë¹86Ï›iz<>æA"©mŸ‡ÜTwf×îˆ;oÇ“ýØ ôq¦Ô}ÅKWL9+’ÝF¹¡Ïp[føÇl´ ÷ãÉà—ñX¼6Š)µ?-£öçŒ$¶Úbó˱"©ê§ªŸu¹Ÿ^épX)’ ¢‘¹8ŠÙ½Ð“&ðd¤£'¶¶tešOµBÓˆƒrÒzû\4»%žÎxÙÕ…'3:h•Æô¯ÉÄ «"é¢[‰ïÏÆ3¥ò?;fƒ-ûŠä{[D]Ï<÷6…S#˜¯C²Àœ6ôÚŽoÇ“Ù߬÷ùG®Î»£k4y2dT~saJåû»&Ô¿ñäb#9^ïgêö/Áãµ6"9¿÷Uhìe~k…sÖÍ ÉÖ'ΡÞ>u‘”ºïzÞ—ߨ‹dÊKÜÛçÌ´ö B’QO6þå W‹³L©ýc2\qI8#’O‹]ÈTõSÕϺÔOå×Gë8´¾%’Êó¯\ð¶¥O*ÏCÂmñõÞ‘Tž÷r‹ÂðŽQ<©<ÿtì.ŽvXcH*Ï¥òúd‡™AËERyÞ;®Gf8ФòüÅ<+´O •“ÊódE ,£xòOóÉW%áPp$O*Ïn¸Ž–7gˆ¤ò|ëLlöœÉ?½ßt¾ºæËDRyþ.é<–4Å“º?ni F‰äçSõSÕÏÿâ~þʨƲپè.æÉ/Ëb¡uè_'ô+ǬéÞÌü^Å\; ¾ýó IýÛ'ô·»@Zi\GÇ2G‘üÕàr7Í`&À¼&‘'W÷‰À+½"¦Tþ‚[pø{·Hv‹M1—lŒƒví¿ƒÔË …ýÐfd};4èí,’õµÜ0óp4³üÀ4<Ñ‹¹÷h5’‹ûr¤T¾™mœ¡u+ˆ'õÙ¡ýJsþíKØß²H$C>ÆaÑÎbæ­cá9$#ûr·à¤8È“R÷o;æ5gyò¬‘ ºæZ2÷¾öF¯r‘œô1Üý"¦Ôþòr?XÇgðäï'\øÈ”z^ÕOU?ÿ—ûi:! …5E<9nÐ%D$2­ÌˆË™ò£é¨ÐV0µŸÂ§‡ƒD2X~îïO2­—>Æø‹¢@:GWãÀ–@Cr¹ν÷âÉn‹Ob‚ÕGŽ”Ê?tÖ},˜=J$KÛ?DÆÓÎÌ~‡}1)VäÉa•®˜´$‚ÙçlÂêe‹äšäHh–1«ç߆‰Ú6žìb’óÝ)LÉ÷¹ÓyD;ÓGÏÀ´ÉN¦“4ÄÜ*Éï3±uy6óŠk!F7MàI+÷¬RÌ”ºŸ=ê®T˜ñäí¾gÑóú^æÓtt2Tˆ¤ß©Ëð« gJíï8î,n=ÜÇ“Ku,àÖµSÕOU?ër?j†â[\>Oþè ·¹LÿaÉ8nRÄŒ/‰Æü¹ÅLfÎX[$’£Fúâø4‘yäÅCŒjÚ„'ÃÆÝÆñÂmÌyOÌ1Èî¶@æÏµÇšÇn")•_añÖåorûc´ëÁì6Èwòœy2*÷ Ü"w2»ü•Џ-…"ù»g.z¾™™e‘P«(âɸù.¸³&”)•oZÑiÌø5ƒ'SWœBF|of'û|¼o—"’ß³/Ã{h83ª›/Úk‰<Ù¢­-*7ŸgJÝï«};ÎoâÉ÷¬ñׯ̆ò›8ð÷1‘l™õùn͘Rû«G™çŠ É7Ææ˜z?E UýTõ³.÷Ó¯GúÎâIn„/þž"2[í Űù̳3üa´<“9ÚîÆ=+ÉüÙ)¨¾ù¯­Æ^E³a~%ÍDf¬[8†®-I#>f)L©üFÕÐÚ8O ]â«ñA゜Ըq ¯ûâÉÇÎæÈÉ,àÈ=(Åç¡"iÿ¶½¶Ù0;O²„¼Ÿ>Ovb†÷jr9)•/eïIdöûÆ‘IÍ¡¦ÂŒÛr ¿ÝD²ÞÖhYnÉÔÊ> ëO yrø93t2é-R÷Oà ¾ƒæó¤ÎêÓ8¡9™é#V£Eæ×4rÚêG˜)|æH©ýñ™§qóÐ ‘œP}wœ˜ª~ªúY—ûé¼Â ŸW§ðä 7wÜ(Žc¾;æ1 LM=g´Ììœ ôEòSi’Ï1$!ѵˆ'M‡¢of>óø—;3^"’£Ó«ñðk¥)•¿Ib5¦xŸ6${-­Fç!³8rñ03ô¸–aH.xbÏÞ‹2'û.ÞÞ›-’ LáˆögÜ×× ¯­I ct[À”Êg`m†Ë§µ9Ò¬±&n›F¾ ¿ƒt"9¹Ó„OÑa†·²Â+Ï9̨»ÎXÜë"Sê~/;K,¯Í“Qŧp.3íù-¼rÞÅì¨U†‡ÇC˜Rû;üð@|B¢Hf7C“¦ªŸª~Öå~ŽxåŠçç#x2|±3:…3§˜_ÀÝŽÌ!Žg±vÜ!æ³Î7±Ûç¤H¾¼´1#-=ð43'sÅ X©íÄüØòV¹›2‡%( ®“3¥òËüc^«pŽL8ÿËŠ^3{úœÄR¿z"ie}Ne–9W£d_#lmXÔz›82äb<ô–‹ä ‹,^É”Êw)Ø GõòÇ0s̨‰b~u{„Áã_2 ¨ÆòüÌ=¿BQoYHÖd¤wr™R÷©ŸB±wž\¹Ã¯SÚ3S&fâ­nóõãh4[XÌ”üþU˜Œ0"‘|Ô&òÓ˜ª~ªúY—ú©üÒ}à€ç|xRyîlVû9úÚžTžºù²9n©<·-= Ù˜™<©<÷>Oæð¤ò\*ÿ¾1ï¥:O*ÏšÛ õ–S"©<¿°ì!ìôÛó¤ò|ÂŽ{X»g’Hþi¾D¿“~¯žH*ÏÖ~Ž ìŸF*Ïõ)GÙlo‘üÓûo*NB3´1O*Ï“R}‘Ô1'ÿtÿ†€r¼öÉ?}^ÕOU?ÿ›û™ŸXƒÿä¥AoÐþèV‘Llþst–2Õâœà]ö€'c¢. 8º€ù>¥¾\™ÑEø.Ïfž¶õÄÔ—ÉÌæ§m0Pîȼá-•˜Õõƒ»›Á”Ê?®ÑI˜Ì^Ç“–¯O ¥!s·_¶7~Àìøð†m}Ã\±Ä» ˜£[îFߟÙ)ŸwnKJyÒûV!&;bJåËIˆÇ ‡»"ùëM*šW0[}y†·fŒ† ñÎ93:¿~V‰ä"-Eú_™R÷cççÃÆºȶµç2÷éÜGŸ­H~©_ú¯þH©ýžA~xœQ!’—›Ä#¡¦Œ)õ¼ªŸª~þ/÷S;î=v "’öÚ°Ô{ówmÃ#xÒa‡ÆÍ²e6z!ǼíÕÌÀ¼(è<ýÆLv CЧ3^Ú„è.yiq§Nýžòä/!x9ó2S*ÿ••føÐŽ'߯>ŽóS.sä#þHsøÈ“·EÊz}`ÎZ±––UÃÖàáÅLh…ð¢G<©ñÊ>¾L©|š™w+ÉÈ‘y˜,2o{<Ńýæiä„j4ö?Í‘œZ9F?y/’Œ"°8‹)u_}s.žÙ5ÙñM6.š4f¾,©½Óm0GVš=ÄX“ù<)µ?ÄB×3¢HW¢â£SÕOU?ër?‹–}Äú£š"y}é'¬¾ÙŒ½àŽo6æÉ†5æøÑ£5óL‡TTÖy¯+RZýÓs­D„m}99¹¯ ÚÞ~(ñm­¡ÓåOör[W·´8R*ÔÇcX뻕#5:‡[ìVCrÖç èÖìOîXc€M7˜Ë­A§ÖýEÒÿÐ&ì=µù{Î!÷7G~š¿%Î)•oeßxXG‹d—{WQý—ùªÙ#\¸«Æ“ëCïaâýY̽5ÏQ]°V$õ{?ǯïYrRêþªmY¸Ó¤ ÈéÏ2°Áæ_×-¹…ª9±<9ðq&Å=gJíÏìø¥•ÛDÒçâ;üXÑŠ©ê§ªŸu¹ŸÕšŸññÜ \ö§½Ò™rÍ0p¡G¾æ£àm9ÙòÐyŒïõ”'õ]-ñðaSƒß€¦æ‹dEì!ÝãÀ¶t ztÂÜûè$⮄1¥ò[Çñµœtmy—Î: dƨ“è–É“»'Æ–Ìta¢^{ˆä¨.§°ðnsFÐv„­¶e®“E‡…L©|Ñsªpèó‘<³ùVFfþð¹…Ù%V<Ùë÷uÜxÄl¿ü>BÚþë¨+h_šÊ”º¯»2^5¹üª€®}›2Õ_d#/«3ac*—¨1¥ö‹Ÿ¡S©.«ÔàkkkŽTõSÕϺÜÏW_?czc'4jUƒ9ù³™j«Ž#".ŒykÀ ¼|¦)’?g˜Á²¹+O.J:„:`þt?‹ÞU1"ó¹;\¾ç2Æû£¹Q)³{AøéL©ü²í截ÿJ ³ÝOâ¶¹–H¾Ø³“ôóäÖn»QzP—y¬ùxèç‹dhªÖ®»Ê|¿;[Sn0?%çÁ­&)ù>k=D½} Er߯'P÷ÈÔ¹så2yò÷Œbüžyƒ9°—ˆeñ_™£?‡Âk_SR÷‡Ž—cפZT*úWñD0ªÓ~òäŽ52l/gJíw;ó7ýxrï·gؼéÿ±_§Q9®íÿÀÓD)Q))M’±Ô}§±’Œ!³dΜ)dª„¤‘)C!¥Pu_çÝΔ)Cæy–„Ì%òÿ½9޽Öý_ë¹–wÏÓºï7ŸßuÇwïýÝkÛ+IÕ>Uû¬Ëû<øúŒ|[€Z¯þ_ïÜÉGø­—¼I"zázˆŸ7‘Æ›Bá¢ûZ:ëŸe0éÀ 9dp¦^º,¢WrHúpžìðóÜ·XAö½þ š6Ž’£Rý u"À}©¯ˆÎ; 5mæ‘/ ‚ýÒeèZÅB˜S£ÈG7mJƒ-íKEÔãû °È¹ü¯YÏÁªñRòvÕ[(G¥úíiÿìÕEtÝü ˜:ù˜->n®Ðãn p+øE¦¯ÞùƒÖÂJ½œßœ* [Æ^€^öœTíSµÏº´Oå_û·_! ÂÆí,T÷ã"úÿà_aÞ”Ÿ2ò/ûåÙ}„ô%]å¨r>ãåI8ᦠ¨r¾·íJˆÊj+ {ßôu.€­6 Êù«Å)» п}ß=ÿô ¿$ û½jŸª}þ7ïÓç™:ûO.} Oùa¿†üYMÞ 6cý×ÁµáGÉC¿žC?›×ä‰ áìëä˜Ìgà^Ÿ£—¾—ÁÎuEÔ;á'ìO=  }íŸCøÍç¤Tÿ/fê,3¾¾½_­\Ø2uSpxvü‡€–†…óÞ•döãX—ú‡´k3¼ì¡¨£ú>• _ׂµÙ•ê—¦wÌ“´U¼Ã ZuR67|/U è–O»!¢ígrœ÷LØb«)C·CîÊq"*uC¿*èå}©Ý3ë4Þ¡KÞÎæ°ÿ­! 9ñé0éG=Rêýƒ©§à»µ )C…ã¿J}¯Ú§jŸÿËûlr+"çmQ…K¬8º‡|{$j—\'GöL‚›ùIùÜ*py”Cvòÿ kÆÅÙ߃IüN2=îìjlN ͇Sƒ*ôÙX¨ˆ¹EJõ~^Í8\ø“G-”¡ L÷Å‚—ÚåÏ&àÇo’›š®}‹Õ½’¼J;~ÐÙú!ðýÚ7= nÇrTª_ͪãPÿO­€æWeB‡ßÉu÷a‰Ñ'òøÈõž,NžÝ2î‰èðC«aÆØF•ºo¶ê;oÒðDGÿßÿ÷Æ<½@–oK„›oèù*Hß¹™”z¿­ù¸~É PÛ‚MPë Nªö©Úg]ÞçÏ]ë`Kz¦ˆF[maùä±÷»á|-ަn<7‚“‰6µ°¸—»ˆÖ7©š€9zÛ÷=hvˆñDgz ñAL@;†Œ„„ûI¤c2ÓÂ*ùß—¿à‘\†ºÖVÃ3¿I§“Qð0ôÕ^¼ÒZfGGL†eO‘]KFBÅ/‘ µÈ‚µ58*O½ˇ‹¨T¿¡£À@— }#&ƒlßS²hg$½{M~ˆYëÓ‘y¡I0h¹)G?•“‡z¤ÔýÒ¥_aùtúzâˆ÷šEžhý–¶ÐÿøBã„»ž¨ÔûIm×Âä’"=S»ÖÆN#UûTí³.ï³ÛÑxx}àœˆ~ðÛ oºMÀKO3޶ §X‘÷¶׆Ýeècïw½ÈE@{—½€°éGÉqî!-î+ùk¶?$÷+’£KÝ£ÀôGªˆJõï´í;Œ>f% •W?Cáœn亯©@–ï±of½Z ÐM$Ÿ˜…6>dbZ%˜÷ Ñ_3ê±]û5ä¨T¿·ÂÛ»¥ºöV<üv–üüq\[}œ¶e, šWDþ¹{ ö×ÞÑes_õÊÕ¤Ô}­îŸ!êä6º»ñ'0š)'?oîw3Šå¨¥Ð‚ö Q©÷fN‡³"d轫ðòX±UíSµÏº¼ÏùŸ·ÂNÇ"Z¸k<ÔxKƉ°[lÅÑ¥:gàƒ¹ 9ãÆ¸?=Q@³«oÀ&ã«dä?7 õÔ&€öË8³ Z’w!~Œ­ÇÑ'ü Tš6%¥úû¦€å¿ô¤k98l]I~Öî »G·'ïo0®x!C/Ž ê;³ÉðžÁ¸ççôêäzŒ?2Ð5_>‚oI¤T?çܵ°ßõؘ0Q†JÝo}ó#º?‘¡:1ànƒz~k8h®Ù.’çÃûn…¤ÔûÆgFƒsÓ0õ:Q1"©Ú§jŸuyŸ×ûî†õó¿ˆh~õ>ðø›tîs’çZsô¼É5¨¯hNBèYpï­h‹yðÂ¥ ÙÖ±£[“S6d‚éLsòlæ+¸ì­ÎÑà‡!bøF•êo?ã\›/ Ÿ&=…Ûw“Cf p½ C›nê_³ !º!p?ÕLD/ÏZ‡·¯#Õ«J m“[3" ‚ÜRýÊ«‚‘ÖVÝm>êiG‘/Gö…óï"È{Ú¾à7ØŸ\þ ½Ã2>Bhß\Rê~‘û{hj$ gᅢ©¯lÈkóÀkìû’¯{¿#¥Þ÷ï:&þªÑEÂ*0rÔâ¨jŸª}Ö¥}*ÿ¦-/ Ž*ç/fÞ‚¾ÍŒ8ªœŸÔ̯RK@•ó ßݰQ» Êyk¯÷ðr´µ UÎ¥úçµy/4ލr^^ì 㞟’£ÊùîÏÑÐ&®HD•ó_'WíÊoú·ý†úBE“iªœ¹ô…|Ýæªœ7w ÊVÜп½¿.¤<ü: ¨r~áÉiPwø(¢ûþÙÇqÐe—.Gÿö{Õ>UûüoÞgJ©>ûOvx¶Zì¨ÿÚ´æ_Íß?†68šû\.ÿëÊS¶ÀŸÄ hˆ¶3œ=ïFκ _ Íq˜vYÉŸ+aœÎ=5îõì]ΠRýLk CÆ%ˆè¤ÁÎ6,…Œíýæ]iè¹7Á1Ë…2éÌOŸ' öÔXÔêSä˜è8ä±ˆŽŽ=ïÝësTªß¶aSàäâÏ"Ün¬œVE>Ü*b'ÉÑ…ñæP–~ ôy¦Ñ™:uîE»ØP©û63Áî¶/EtÿSøð i?H­ZlКTx¶ñ£ •z¹öZؽ櫈«I„kZrTê{Õ>Uûü_Þ§Ž°Îæúû;\Ûõ%»8½‡9m8z|àgø}°™±Õº|œ!CåÝ€Ahš½Ô߯žÕôñË-k´Ö¿ô¶ÉÉ«+¿€V¤% RýͼœáÍålm<¸-<[H–¯*†¬Ém·8>Þ± G¾Pc õ÷zìO-¬_¥è¯w7àfsŽnŽÿ ³.˜“Rýl[FBÀ•ß"zÒy”ޝÇÑ.öf°ìž¹ˆvõn}Ÿy‘™G¡½·; !;á˱>¤ÔýÇí_À õDôšÖk°ò¼Cþ3bä\*–£aysàÞÜT•zßúü>˜º8Z‘MäýHÕ>Uû¬Ëû4(Þ =~øš·};¤6ò"]Ú‡{aÍ9úóy¤¶0 ¦L²C³E4§h%ì=pŽŒ´qûÏ×´™·'d5M çU<„æ­ÛÙ4š×“Rý?®l}®ˆè¼ûàiÐ]ì3àù{@_ï€èzä£UÐædSrÕȰ%Åœtî¤Ææ˜×ŠèÏ_µP¢FJõë¸k Œ;¦ÁÑdÿ=Pþ@‹ ªgWÏQ³m`Kþ:²¬ãÈëèÐÌ•pÄC”º?«¤ Ì+nˆè±èw™\BîmQ˵9Zp踵#¥Þßž úËúqô‡ÕYØ^+#UûTí³.ï3{^Ô.ê¨lÐ(ëä–ÈŸ0a£G}=j ìÇ# ß°'5à¨ÇÎmpž<žè‡+ÔÔÅ)î-G[f…Ãî³wô`^'¼&Š”êÏ&¸Ahëç"šm!Àæ½ådôãhØþꥀþš¼ŒW#; |Gœ--qºÝ—™’éVß tÄUj›W‹½P©~ÏÛ¥ƒÃ‹úa˜ çN땆Á½Ó ÓëÚ‡IŸÂi°îZ´€®ßâ·Ÿÿ*@%ÿÿ¬ë|QDïW‚yƒsdËGIÐßÍ›£íöC“‡}I©÷Çx_‡+¯-9ª?î1ŒzU!¢ª}ªöY—÷¹¯ýf¨ÐÐM ¦¸#9Ë©FÊŽ‹èå?ÁýÐÒ 'sG?¼Í‚ö5~dò°`ø}i•ˆÎ³Xwï<#£-›ÁÄ>%2´i¿Ž0£ò²•ê^³|©ü$¢šúA˜Sº~èMPù²nta=™“¨€#× mšé}uȶ¦ ‘^'òŽë¸õÉ›”êwïŽA ô8ºmÂy¸\®Oº¿õ‡ÇarÝc*ì)¹Lfþê F;·‘ãçô„£jH©û3¦™â‘¬ø9‹Dòʹp[!p4Ðð,y[‘RïjTÀ=ïx r«†’¦çä¨jŸª}Öå}ÞüFÛZ~sd|¶#«FWC–¿³ˆê½ü;®‘£³·ž„ñúsT§}ØÌïEn(Z ×F5'_I€Gú]ÉGÚKAmãq]{ôÀ ŽJõŸâ=¦ÿÑÝ1ã ö•GÅ©Î0rÖ8íåa Ù“a[€oýó¯6p+ë¹³çèãè®MÀ°÷{•ê·å 7jÄÑŒ!÷`Áíݽs9¼kþHD¹¯‡£Þ“#Œ€Â^Í8zÛ|5Ë0#¥î÷]níO‰¨vþO|—K>Kº }ÎV¿žT@YæFRê}#¦Î´Ù¬~TµOÕ>ëÒ>•aÙë¡OŽ ÊyR»ï°¯AKªœÙ]†#í8ªœb;¡DÓ—£ÊyÙ‡ó`cÕ“£Ê¹Tÿò[Á ‘«ÎQå|ZB3èbòT†*çA AizG@•ó᳇ÂÏ¡ú·ý‚#ŸÁKŽþùÖ­0qýoUÎû½==§pôoï?Ñü 'öQåÜ&æ ×x.Gÿö}/·úÌÇ2S@ÿö{Õ>UûüoÞ§•NsöŸ4ò¾üÈQóü¨°“Ì<ÿZ9Yr´zxøç«“šf?¡Ñühí½L›õŸþDŽ–•ôŸøRrù 0í“'¢¿ ÛB^kw@'k8C»ú¤TßSÚlIÉZ]àÔ€‰ç;›±;}[‘suMÙ®n½ÈÄUc ùïŽ~ù=’ž‘Fòpæì½}ÌسH•êW‘ªÃ¾qQ'úìð©srÔðYh¯óIDOÍe9øY®cjêS ÐAÏ…“ýDTê~Ì.ºÐ£&ê|5ìE&:õƒ_ïb´K /€‘ë P©÷ýknáe:íõ›•ˆ¨Ô÷ª}ªöù¿¼Ï—ÑjÌqÆzMqÑ`Ö'’9 «¡kßb²z` \xEÆè°»bš':unSV"«¡÷…&½> &ë÷&;õÙ5U³î “Ç‹¨Tÿøh6´}‘€N¿¤Ë ^Ü'_†³1“Ȭ«˜E(Ò´Å\˜éÄÑ0ï^^Ð ¶!-zAÍ~¤T¿ac4YcÛ]híñzlàpz:h$Dîý#¢šAƒZvWXÕt‹ˆ¾·i/'¥î¿Š ¹™GGýßì"îz“¯'¸„oÑ•3àÎg ŽJ½róÿþ=³Ø&¢õ±'Û¦’ª}ªöY—÷9ü´³ .¢½°Ì³žäÆ_¿!ç¶6¹ê¤Sñü$j¹Õ”Å:¹ èø‹&lcôdR^5 ÷rt×ìøì=•Ôµ÷Ó€näÅ™£Á=ÈŸ”êŸõ\…+*Ôê¬>;ô° BÿúìMËÝšûû”ýà¤Sil¾æÅщàô¤ä$Ãy0âdOrMü!èwφ”ê÷Ô·Üãš ¨õÿý{~cðBÒuñzØ5OŸ£óî€-›’Ct&A‡³oD´hì(_aÁQ©ûÿ8284¼G½ ôMò%¯j ®ÿØ‘®Oá«~'Rêý“Á¦Ì%²ƒˆ.y`ÊÔ|¿ÉQÕ>Uû¬ËûÌÍÐe£õ[ŠhÂ7}Öß¹9æŒ:›û[M@óêi±ªI‹ÉßÏØµµd`?]önÃ^2ãf$8wËÑ}Û·ÂñóÞ䉠­ÐÿU2÷Ñm¸±À€”ê_>¥!3x«hYaCÖ`Cc2äÃ[hmòZ@Oì¿ ä:€f¡üJ Žþšì¶é‘%/á—^#²Ýæ3·)Õ/[ë|;$ Ÿt>ALêrÊ·tØâjÅÑOÁ‡nŽäÝéç¡«w_2åöØó*˜”ºÿ3¼;”$õå襟=àŸQ~ä~5_ØÅÚ“g÷®€äßv¤äŸ4YV79êÝTuPì’¡ª}ªöY—÷ùÚ€͂çr´C CÖìHÉz×g±‡³tå(VeþŒÌùôš{%[| ×od–Õ1°Kuâè:÷« >¬1y8§>k\U\¤ß„ý¬)Q©þÏ5dñwšš7·!S{lFþì}~Nlõ¯ŠÐâIÎ0øqÛ~ŠhîquÖgÄk²ô‹!“›W‘{—賞óI©~6ëßÃÓÞê€_W6'LH½›çÁÿ|gަùÞ„®&ÝÉfÓËá±ÖdÒöþpzÞ‘”º¿ú`/xüÜ£×¢½@õÿ}8>nA®j}¶x›“Rï[ó?°Øt€vú§ R=&UûTí³.ï³Óg#V¼*^ŽVo5fîõFkôõXd¢ ß­ôÙÍF¤WòEXnB¶é•¶_ºÂØû諈¾wÓb>Ý&M鳚̤ÇÊ_°o™§€Jõ¿œ¯Ï‚ ,ý³S%ÇY’ÙûaFØ`²EôvP™D>¹­ÇrCïŠèþ.FÌ´ò2é[ªÆ>픡۬?B÷0M@¥ú%ýzeùN€¾õ nÈ<£'à;³/Gj•ÁË2Èü;ĤÈET­•:KÜØ•ºïpÀÜ{÷ãhùO_Hiý¯[VÀÒUVdCo f’jKJ½ÿgíMpo h`õØ‘æOªö©Úg]Ú§òï•n36§´™UÎÇë³O‡-UÎOZî¯VCUÎ/]5`gŸQåUûüoÞgî 'öŸ¬7W¸©õX@—;–ü)!5gëÝD´UZKÖù– y2»Üõãû }—’Kf3è4금j¨­—b#ŽÞ¶´Œ&_•£§t ¯åRý½Â]áÚ…O24ój+ج0 ËOB±] G×håÁéœT6açz$B›m®lD®[јÍýš# v?­Øà2@¥ú½V n} ôNN纑WÒàQæHŽ^x— —MBÉ^+„Ó?—ŠèÓcºPïa)uGôHعb 5e#á³wÒÅo1Œ8<Ÿt½å±¤äßÿ?ï`ûÍzÝÙC›­ÒˆQ©ïUûTíóyŸÚþw…Ü¢€NV{'hÍÅ{בÁq¶°uÝj}– ‡G¥ú?õ4c+9Zf­ý=#mzž×«ÉåyPf•@ξùþIi¨zÉeH[“6¶`¯C È;¿ÕÙXwmRª_ÿ}‰^i èÙÖk yw2©y>¬Ú½Ž£Î!Í!‘Ô= Ÿz6 5@§Ò‰¤Ô}ƒ†£ ïL7@í¾Œ„?5näÑÊ!0¸ïf²Ix ø{E‘RïG·3a~]jåèäÖ,ìI$©Ú§jŸuyŸY¯~ ç¿$¨ë¨д×|2°È‘=5tÑ·+˜©c'òÛÝ•ÐàŒ) “õ< ÖÅŒ™–;‹“9úáÍ!8q:•|ôÏ>¨~´Œúº–oÛEJõïVôN˜·ßED_Œ#'Ω"ÄX¬ähûqÅP›6”|W‘ ffÝe‰›aÆì‰¤Ý;9Ì]Ú™¬< ýN­&¥úµ;Œ,õmè?¼ÎÕ#O­-„Övq´yq!Ø™¦³{ç‹îvÒiÍ ˜ò;…”ºÿuëH°ùêèöªp2Ç–¼òj¬}߇l5q(|êðT@¥ÞŸÞ̉õýºFŽ–UÛ³ø;HÕ>Uû¬Ëû¶Ùâ;€€ú.måõ5ÉLw;¶ã¡“ˆ.ëÔ’(kL~üô[û¥ Mfü&ÙÝlÓáFÏ힟 ¿¦„“É5ù0[-€œíôV§=Q©þŽ câÖˆè¤[„ÇEId¶ì.hövàhü’ ðüGDS?„CXóH@kCàðâòQì è5t™»:kGþ«D¿+:ßñ]@=ºšÁ7Ïä.µB37•£ë+ ºp/éc”¯¯#7×–ÂTEGRêþ£×ÝP³—’jDŸá¡ŽÊЋµ/…ü£"*õ¾Oc+65½™­íÖ˜m~œ'CUûTí³.ï3üc+Øç#Cg•:ÂÓC{=Ðæš³¨¢ý~¤ ;j»µ|+QxmÙÐч #7;’sžÙtoŽnÒzãßé‘eÖŒßÙ/¢­wÚ1ý%ÇI©þC,v¬Ý/¢[^θd‘ÎÕc.#N‘+7dbúj²M×YðåÝ6@­ç…ÀÃ$²‘ë400i@Þ[ÙL×ûÉP©~ÖñÓŠ m[xCØ÷ø©~œÃ“–;8úñª~‰'Û6Wg¹Yy"zc¿9ëâÛ”º?mú0hö² ËAjtú¥¥Bg§"ª=ËL˜Ÿ’KJ½_”¨ÆìÊ’TûÔ¸:ÁPÕ>Uû¬Ëû|£ß¦:-•£‰«ÝÁ⚦ˆvì¤ÏŽöØ/Cï}Ðdí‚è°K÷„ðÆ€öY¡“ß\P‡çš,*—‹h\t3–eOè1…ùub5<˜úH@¥ú×sî¶ÊÑ… l„OïsIsÑ”EÕö%ݯ·dLÛ“K¾†BGÓ@«Þ.¤3Éb/¡u;}9ß@ˆ mIJõórÍì5¿ ¨Õù8á«i ¹íw”_àè ìb˜øk*9û©[]ØYDÓ?Ù²ooÝHÉýl cËÔH6Lœn“[ÜFÇNÑÓ÷ —ÒH©÷ÕÎÜ¡ó@?ß„Ñq¤jŸª}Ö¥}*ÿ:8û‰¨ržõ &;" Ê¹l“=ìîn/ ÊyÛí6ŒX#¢Ê¹sÖg˜8f Ê¹TÝBSÁÅ5WD•óßÚ°äWUÎÝÞ†@°v@•óË+¾7¼F‰èßö+é´PHYPåü®ÝuˆºäÍQåüwNöÔëEú·÷ÏlŠT9?Ð z¬¸-¢û¾ßŒÛ°¦Çn@ÿö{Õ>UûüoÞçQ//öŸŒ‹µg'·õôW;g¶¼ƒBV‘%ÏÖ§és žeÏî¯=AJ½¿Âö6¼8Þ£=šé°qžÚ¤Ô÷ª}ªöù¿¼Ï Û±ˆØ@oôíÀb†"'üÑ`ù‹Ûp´}º&»?–'Š“^ËÑä ñ¢³Ð†Œš¼ fñ@Ž1ÈŸS;É-® újÔ¥j ,Èù&¢Rý÷™VÃë1½â¢Áv|¾)¢ƒÏïWùôtKb¢ìùÊd+]¤½^ÍQ?°Ú°]d÷“èZ% µŽï…Fß.ˆ¨T¿„Î]@´¬Ñ.2xZdËQYdo˜°¬ç:tÊâÈÐ’ÑBWq#Y³Æ_°F“R÷Ÿ.†îƒ×:^û´5Ü@îXÓˆ-?’%G¿ý V1Têýý«mØÊe"jìßy”èqTµOÕ>ëò>_&wbZc=8® »–9šì_Ÿ= ±ÑGõ™…O„ Í 8,¸í¬'j°p¶P(%«BBÊïŽê¥n„½IçÈÚ+êð,$‰ ÍLd’I©þsÔtÙÂå"|·1{·j=ù£0FHÙÓP£½ß;ךlýGH4Iáh‡g\h=t9xÞiù¢ÝÖ¤aöKù(Í?"*Õïó†íóçh´‡'lŽXFú%DÁŠi‹ÉwÏ“áÒÀadpµ>Øå‘Ë®$ÀŽDWRê¾ëÁÓàŸ–èþƒÿ@WÃ$²Ã5&ÞéEZ|4`{Æ’Rï—¼îÀ6 î=²ÎžM¾2ŒTíSµÏº¼Ï½«Ü˜ÙÌ‘€©qc=ƒ‡‘Ç.6aÏ3ç¨Ùh 6Ú!‡lÙÉÑñcýaݼL½:>^.à¨IÞ|ˆû²‡Ü7ÎNy‘ ¸—w'¥úûÆš°ä é"ª[Ó‚ÙoBxÛ‚ï‚®zÀ˳ƒóÑOR…S£8ÚÈï‚ð¨ÁD2WÛšÞÑ©'“atz/ŽJõ[¯Ý,yH$¥ÞïaÙœOòâh±3˜õYDUûTí³.ïӮؕßè›—]XÄÛ>äðC¶ìÔó«êxÉ…m}~üçì\hàÂQEØ ˜¹-Šœ?l |{ºœÔ7¾ _šô"­«³ñü0™sát(Uû¬KûTþ¹xº²=EíUÎC‹-/ ÊùÈ,K˜iWÈQå¼órgf{Ñ‹£Êùš ]Yê//9ªœKõÿÓ¯3Ëêt\D•ó1Ódãg/ä¨r^TY ¶jë9ªœ_^gÎŒ¿isôoû¥´-XßÏQ弴”eOXÎQå¼ûVOf×!IDÿö¾e?媜÷èZîôoß×éÌ6¶š èß~¯Ú§jŸÿÍûÜ•8ý'C†Ʀˆ¨K˜)ðqWÈ¡fÎlß75Ž–íØâͤź+¡[ÉÓãö aõ‚É-rM>ûôuí)×”Gß@^¶ ¢N€Ž~ÚC,ß®NJõÏ›S -mÖúmn)„y…‰úÞ¬°q2¹6»/»EÚÅÊ~õÐûáìѶd9·å ömÅÑoš6¬Õ“dRª_fH}Öij+G™› ‹ZØvîÀ®å䲬jøö(‘,Îì/Ü”" “Š à[d‰ •ºï·j ;ô6’£PÖŸîZ²SÕ5ˆœ8\Òö,[s’”zÿ¼;Ъ3GßwkÍæw˜Gªö©Úg]ÞçÇœ`Ø—ãÆÑÞ'Vž7ÃȤóýØ8³PÒÃÒ‹ÝCžüËÅUh±Údhj*ø²=¤MQ4,Œ¹C¿ðQÜÖBDÇ^ÍSâ¨Tÿ‡- ¡G@öÍ€A%ä®ÞÞl˧>d¤ƒ3Ъÿ¯å>ì×Å"úeUvµÃ]òR¦K\_ÁÑN_jà\Q:)Õ/±¿-3z1ˆ£/_¹²¼SɽSë³ùƒŽÝ/뱑GŠÉ{O|àµ;ù¾:Wør‡“R÷gýôcóJ£9úXîËä² ä¹×`Y›*2¸Q ˜Xk)PÉ÷s×9ÊÑëæ,éÀCRµOÕ>ëò>o¯Ž†çE½±#4+Öÿ«¼+Ûu}+Ùü¬#Ùf;9¦O ŒM^/)…Æ›~“×»B—–ŸH}·ÓB±ÆKÒñèwñFÑm•uB¨*µýZãrHÕ>Uû¬ËûÔ½W,wrôf‹]w/ƒ´:hÎ"ýÈ”óØÇû+H5ïðGë;™ÕlsœæP,”ž$·Œ÷[¾Qd³Ñ³amN4™ Þ &vüNJõ·‡¡ðäEK@·Mi Úɵ_a}T™€úö½½z3@ýÏi0çÆd£¯õY·¯Ñärÿ1,Ô-}¾P`ÏΙqTªß›¹Œ‰_ws4ØP`·{f’Õê•ð¥{©¶ï6xiþk½·ƒ@¯“19ð]1¬–M%¥îŸ ìÁN¾ßÈÑŠ–ÿ×#.¼ò¾O6'åµd.‹¾ˆ¨Ôû›ÛVÃç¥^}žgĨ[ˆ¨jŸª}Öå}^žº&^Ìç¨Ã©Ý—ZLÎuÐf^v>¤©f¬N^? •«“ȥݞÁ™Ö=È=&' ÿŒ~äëQì‘ú0²½g’ÐÊ%tŽŠ„Ä€6¤TÿÔ@(ÿì h÷Å7…Ò¶Cȇöf¼ÙH:¼= «Ï‘‡73ûíœ|Øšµz~‹,ë×€u›â* –NúÌ©g Rý¯tcŸßçqtÌØvlC'÷¹qØ”ølÚ:lN?$O| ÑaÿjX”}¾ê(P©û{r=Ù÷c ½níΖ üWm7OÖ!Á‡løØ“y,ÛKJ½¿æU{6fd ócÚÌHÕ>Uû¬KûTþ]m‘ ^­nqT9δÙÚÊ"ªœ?öl ወ*çAƒÌÙ§ã9ªœ>¦Ákíç¨r.Ù?k¯°Ä3På<äúi(õø ¨rníÙ¬üË€*çcš ̦á@ÿ¶_B¨- ˜ðG•s¯F{A½÷Ž*çî=’À-ë>Gÿö~άN¬ßÔUÎמ³gjÏ 9ú·ï¯kÀúÆôÑ¿ý^µOÕ>ÿ›÷y¦^ûOBž›zªC¿$Z³Ò/æä'ÖÅÜDz}7c×o6%55ØÓ_D4,±KíØEŽnï/Ê#_^çhޱ3_Ñï6Q<[øÔI“¡•vг›9)ÕßúÖ:qíAuúâÔ^q;|ä¨éÖ ˜HÎWÿÁww’™cûr‡qݶnß±{ùnYW6'àyøðh6Ät)ÕïHT#æQÿ® µÖ€Lx, {ÕcU‹ª8ú©ì<Ž3R mß á®3Ò=ÐΣ·ñ¹C•ºeò5yIJÅýxeA‚<˜¼ÁfÁ˜  ­ûBØÍ¯*õ¾Ï’_³h¥@¿.‰n¬øÍQ©ïUûTíóyŸ¥av¬ ”¡Û:9±ÝQMÈ€õ™è¨«@Õ•0ÕýG3òÇ0§¤O4:r1Û¢#¢ ´ÆðêöÛ9úY_ƒG­oB:·Êª·!€ZèU}~' RýÛ5J×ì¾ÅÑË÷cÄ|2Ï^Ÿøx“4M4`‰Ï´èŸúíyšZ! #ÄgpŒƒÖlÜÝÍÚp®¾Na€Jõ«î¥ÍÚðtØ„LèO¦d~€µ;[)З57à[G²‹Z‚¸~a8 ¥®`7[! R÷¯õÙ!So;†£w‡º G>$»~h$dŸú Gícšó…i;d¨Ôû {Ù6Ž*~îÿíHÕ>Uû¬Ëû<ëÂ:Û6dè”É®¬G‚¹þô#H±;ÁÑyÝîC³¬Å¤lê"ö"Û“¼|lÛ· †[1VØ7dšˆšYÎ…L›¤"È™÷(ÔøûNÁzïmRªå5K樳Öù¡udÆuÖd„™u1)»ƒVda@©00_¨á²v5y¦ÑC˜gkÆÐ‚ k]Þ˜”êwvb#¶Ëæ$ ¥LØÌïwÉ#­²¡´°õqO€Ž-,Hõ¢÷Bnçν":ð󾱤ÔýYÛÖ ý]¼8êžW(8ü–‘Ú+gñŠoý yÚš76>NJ½ÿîn=6,­G¯oÁ³IÕ>Uû¬Ëû”íseŒ~:¨S;˜ô†ôlX{pÔ¼ZÃ\µØŽOLàèŽV Ù•ò¾¤ûñTˆˆŠ%×¶ˆ…Ê ïdQ~[ø *C]æ˜ð?ݳDTªÿÔ¯e>pô|Z²¿·=ùÄdô61Q S;…²òz¤Ù©QÐMü$ cô´Árú9:þ­6Ó|<ÐãÓBâÏþ¤äŸŸÀ†Mœ\ 赆®lÖË_¤ÖäØq»í¤?:­~ÆÑ ÊíܤĈlr]|ºÄ³•º_döC¸Ú™£÷LÀ8¯ù4¾µÇvÐ\ÝŸÂ5*õþðÙ¦ì@€®µ®ü}»Ú’ª}ªöY—÷ù­¹3Òû& {Û°‚ÌBrŽ) ›ÐCŽ®òreêæ1è¨0 ö륈V:˜°®æš€öÏh ~ïš+Ы«å²èMÉoãÖò©®½§•àhÛˆ”êߺ>T¬kÆQÝ´î ŸÜ€ºQêó.‘Ç¢›Èò̽r׉WE”µñä:µeäámO„¢|m†jµßU;lH©~ïÊØÛ«¿½1Á‡ŽüBÞ*/¯dq4>zt|µ’üd1Œã DtàûX9;—£R÷?ÿì“/8qÔ¥¸?Xÿߟ ÐÀ>÷„qÝ’ŸKÌE“Ò8Rêý²²|ðZ`¢@/îʵ~W9ªÚ§jŸuyŸGÍ[²ûS2œiÆú×ßA²Ž>¬Í}5>šyÜ9G.{¬¬Ë@ór§°eKÖ¦Iëòg?}ÆÑß6÷?ݵ¤õs8•»ŽUû¬KûTþÍo̘Fªœ]ŸÆNèwá¨r.×[ÎÂßõT9yŸgCÚrT9ïÍÄtQå\ª€>oÛsT9µkÁ¶¶ÌQåüÄ¢•¼WôWUÎ3uøúø@ÿ¶ß”®#ØÌ%ûUΗ”ýY¡)G•s›±Ç=³»àèßÞ?ü=Sÿ$²ÿä¼Ùð*v Gó+KáS×Iäçs»Ào§ž§Áì¦×8úe~:,~q Ð ÷œZ3ôtJW‹l玪EmsO-[/GÓJ2å‘¥ŸÍqX–}*ÕÿüÆ*‚´:;xk«K^ju£`âDk2Õ¯L°»©Cnâ,;gGóFÏ`¿ «ÈŠšâ¾=k]ÜB¦O´æ¨T¿¢=wàÖóc€º&Ÿ€‘û£ÉøÔðÍÀO†¨åÀaÝAä§ñn®»I@³ÃÜyÇŸm8*uÓèÂ’ŽMEÔuu¢¬KßÅäÁM¬T~ÐîÁ#˜s¬-)õ~ø‹Ý0ãÂLŽÎ>ñ<^ÿ$¥¾WíSµÏÿå}:È¿‚EÉŽF[ë²áãÖÕϯÀPõhÒí¶׊ 8cÆæäwgè»IÝÙ’ÖÉÓNúj GyM4Ì1VS áõóaûa@'ÓÚ¾P©þ®ïÆ2M/]†V›Áò25IE®;„…Ê=Ñfް'ï›a¶YV TýDÓÑ‘<é]+¾TËQ­ÍY¼¡§Œ”êwùÝZèá: Pfo G8“5IQp=À[>|Ûfnw%5´>ð)‘r”ŸYÏGÆÎTê~nZ}¹®ó!]o$_%}Æh°'Ó Õy»FvÈ ¥Þ_ OAõ»°6›÷!UûTí³.ïÓð¼);°}+G÷=·eËõ3Èp{;ölŽBD¿{3群í~_ÖñW=†þþîÌÞÖnôèW=¸t×A~o·KœdÜ‚ßO“‡¾mÄÐØ‡« “RýõcCÙö¥_<{% ^|‡¬i~@X>y¼=ülŽ˜–S!¢[çÖcý·4U sç‚ÿ€µX5U|;úª€¶œ14¬á¨T¿Ùoz oBÍô[)ŠY­I«íî •ÖXn::Z(îqôáø*ás­= [‡YBhÓŽ•ºM+BLm])¢æ[Eñh­&Gå¯ÃÎ‰Æ =Ö’]8êDJ½¿1uƒ ´T &¹­„Ò'Ç8ªÚ§jŸuyŸ‹ Û³l½8ÚS݃™Ü½Kz™ÀúÖ$o-ŸÇΘŒ'},Ëa§§= FïB¡ù–þäØ5CxÓÖ8út} þê\k2?ücŸ  :©Gx£Ï¤Tÿu©áìõ„\@ä…³¬ô ¤éØ!<áý[ul–‹‹äèù(^ÅÑK'CeÙvÝâÝTŒíj§@ÝŽ„ñ›ÎrTªŸî'þe¶SæòYaden4¤.ÝÂÑ~ÑŒÆõ&w äv?ÒÉ9öï¸mI}Rêþ *1=®G‡L1ãù¹Æd‹ÂQ¬ßy†¾}<ˆm²P©÷.õƒG•ý~ìäÙ &UûTí³.ïóÌÂnìõžO½×»{Ü[Kzÿ³€õê}˜£+j§±¶^oÉ…ËAüèÓ€îžÖÖ­7gè®ÁÓ…]»‰hËΉÝvG³CŠÅÔ²?€nÍbõRª¿]+Ùú? e¾üPkÏ<ž9¬) /îá%q{H~¹#ërp°ˆîù=…íZnÂÑY)³…}E4´æ>Íä¨T?­{ñ|ÏÞ=€úOMâ}ΑËÕÀÒä†\bÁô†7"‡ çÚ‘I:éƒÀ±ÛDTê~ˆu6Ù„£Ú†ƒyöfäæ"möëÛP@Ï–…aEi¤ÔûÓ÷X±W“žq4¨¡%«;)PÕ>Uû¬Ëû,Ý›Õk¨@w*z2ûL R[>˜õJoHfubÍÚZ~= àÅú ­*nȶÍû×Ï/Ãñ-ê 4ïä˜ß^ çoïÍ‹ýšqtzÀ}nt⃀Jõïqe+Ï PV4†Ýξé‰Þ À=´£C¨Ø£}99®C{øtG÷_ZÅ΄'?ÖN¨NÀ'qiH}Rª_üïXnÓú >yÓù?ñZ ݮ݅[ìÅÑk½˜IA"y¦A8àSJ¦tą̊š”ºß÷ã Þw¥GM="¸¬¾™Ú¶RhäÝ„¡ŸŒw‚»¡')¹ŸAoa“ç@Ú9nh_t!UûTí³.íSù7qc/¯Ø)På¼ç½¦¬°e ªœ¿ŒkÍÜÛY0T9ß÷¡!èÕï­@•óÜ%êÂÜ=›UÎ¥ú×OõcQŠÏrT97‹vÍ)T9Ÿ›Ä’[(PåÜÿÏÈuoÃÑ¿íW°ÍŠojÈPåšéì?yn·3缪ßã§¹oé°9ÞÝÆQ3ïZè_>Ôuž-ŽœFºM9Ì{.Ó%m_ì€À=†d½KÿÀž°×ä°?sÀGs? óõúò´²$9*Õ¸ÃZáê³§ª!O"ç§ ÉË~ƒ»Ïk@KüœØùk†nÏ^ o ý=|n¢üÏÌXrFzSV/니V[1³é™€Jõ»¯{ŠWvÙ(¢SXÿ—÷y¤I¼èþ~8C—^,?>o4iæÊl2œ8ZSÈÎxù’/.k(j½jô¹^ O[{У½¡sQOz™—ˆÝòF’{ýøïR†FÝg°§ú_¥úÍr7Ñ{~šÂ½¸ }<vìÈÐø¡ëXG;²{Ép P·Móùü“n Íÿ¾ƒ´ÐÓ86WÓˆ£RýžrãaÑ2T+~µ8Ñònj¶<Îþì©@7Í‚?õ¬ÈSíÔ„¡?ú:¼&\hz|G¥îõʃ³63:W猟MNrcŽ«š(з ú3¶À˜”zȦ|T—v tÌ{;quõUŽªö©Úg]ÞçÞÖm_ÃÑ ]8í´`r.€\¹8‚}šÀQÛÙØÈ}7ÈY›¸×Ë@õO·½NF'×òþ;hOgK~ÃèG-´Š.ñ E4f{Ï?: P©þµp@n´(™£–•9bÿ£ 2ìÐ&ÖÎ@­&ǰò¸d²í#QL¾;’¡.Ç¡]¿ÁdK#}fyS@<\Ùš(=†Jõ3úÅ„É#cD4½¥1Œ°u䨷y, PN&…»È6ŠÃ|Å=C±SM±ÝaG¥î›ÇÞ6g.C5v½‚K¾!丙Xø²0ŽNûcÉ,¦T ¨Ôû¿N%Cô°(Ž.r³eϪDRµOÕ>ëò>;^h ÚyVÒ¦:÷"{„Űõ!z ôœÚ*öjUòfדBƾ!š°å£lº|Gc—»À¡žždÓ€O°1`-©–¹QÖ~­Cg¬k7vV ¨T}#nн”£ã^-å?Ü!¾ fÃXz(Õ™}T  šOsÀÍÒš¡]¯Ýãu'u¿»…ͨ?ô\¸€…:špTªŸ_ÚXhÔe3G[<Š€Ê‡÷H]f±£y2·3³N5!/Û~þ& ‡Õ§ÁöùçDTê¾ÏŸo ¾ŸËÐXÐ`…#æ§Cg°ôO]³‰ùŽ×ä¨Ôû•+º0õìV ÔѸr !UûTí³.ïóÎ…i Äufhð™ÔšœÜdûxº‹ý¾Á™u÷ïLZ_íÊK*Õ¾TÂ/ß\EF]ÑbV¹: ô2K€å³½Ií¤ZþÈj±5”ͯi>TªÿëŸûùј+í´ì¯¯vœÜ¯an^@?è§BÚ©D²Þ³Thq͈}KH_uJ@ž4`w»m4&ѱŒ •ê÷˜¯†$?j±2¶„·%Ígaåœ8ꑾ˜ú¶‚䞢qž–­ñøÅ÷Ÿ¨á¨ä?çÙzÌjÓL†š/lÂröN#Ïf jµMÑñcÅ¡¨Ôûá Þ v;|¨}èjñH‚©Ú§jŸuyŸ®C?C†N™ _W:lˆû¬i£@—,T@ú 2¢þo¾}¼™€3¼Èý.¥úáëqRd?zíî\.ÿbF6Ú;ì}¹h_u¨9G¥ú'~Å]â9ã[Å—äö$õï€n¸óz¯¶a¨ã£ñlÜ@]Á=«"§ÝŠas²CÉ2=Û_ ¢Rýmw€9}{)Ð3‡÷ ŸÛ"ÍBÃÙÀ¨BŽ®W_Ʀ¼ÓT æ•ð.Ï+D4÷†&F¯"¥î¿s3gã'24f{+6xöXRö´9›æÂÑ‹É]˜e'W@¥Þ6kþ9‰£i–OaGz ©Ú§jŸuiŸÊ?_“õPîp På|œk ÔœÊç¨r~a– Öë ÊyÒ†Ûâå¾ÙUÎ÷˜(\jÖªœKõO,ý×Qå|AX'öì@g†*ç_ÓÇñÑÀÐÿ/ߣÁÂ7<ôoû…)’äÎ V Êù‘X—v T9±r3TŸÊÑ¿½o¶ÚýéÀPåü¹a8ûQºп}_À‡üæèß~¯Ú§jŸÿÍû, ãì?鬫Æ»&sT««sÓ˜Ežûù :‡ë“²‰6,JW.¢Ùßš°[ pǶT¯"+‚{³Õ¾Èç&2+ãæ tĘk"rôîes…éîRª¿·éNvýc[:¿÷Fv”»“U^í˜Ï†¡ädóËð®U2ÙvÐhЇ쿰Jx܈Ú¬‰¢{ZªˆºÎ¼)«WkÀQ©~ÕsãY²sªÖ$’u$“b°·Ö¾ä³îVlWÃÁäž~O¸³YdÁÇŽÜÂQ©û…2ÙÞ÷—9ºgä!¶»Ó]²›K„hLW Ï&ÉlוsTê}‹&ÖìF¥ tùüÑðÎÒ—”ú^µOÕ>ÿ—÷éù³óð·æè†olúÚLM ÅöõÒ㨯ãö*$Š4Ò aÃvk+ÐŒ¬ù²Þ`+vÒ×kÅâÙí E#·!±R¹•ƵäL†÷èîã¸ß~±ŒUµàŠåK›µ Ö%ŽdhânÈqœÀíSK†Žæê/½BHVOjRÿà!Eë&Qº³*v¿xZ x{IÑĉPôçw»ÒRr¡]W®Ã…ö°ÓúWl¿ê!HûEoì A,¸²~ÊúÙœû™bm|SŠÐ–£áMžš€šŒ1M¯(úypd¥ê14^ñ$ù6s-wó/Oá|ê4n‹r‰ä‘º×b~ÁÚ‰Q´ýšsÔuXK†z?«¦åí *–ÿ×Éé°qm/†*»ZÂË{­¹|¯“÷íB)Úå(ÜáÆÕï0 4Nrïö…ø×: }úu}xK‘ -ÚAFÑË÷ÙÌηfèØ-`§ï`îÓY%ÄãØŠú:ȹ¸—[ëCÏe chfC x¹€¢b÷ë¿ÄÀ0¨¦è¥+°¶ö#÷¾^/˜X;“¡=ž î“ùbû«,{€Eò1о¹3ÞÉPY?eýlÎý|Hágz‚€­ò€Îô·`kô?bËÐéNþ°lÚ|®VÐp¡å5î(–JYfStZZ%9÷%ƒ»ªdŒta¨Â3áÏå)®U1]±XŸ¡bù‡´2‚ÑY¥í¬^›â¸g¬7‚~È!î̼½à›¤ÈЉK¶Áõþ‹¹zæã@ÑЗ{aÖAº×`:wÛL“•HQ±|Z÷”@i¶&CG:*B™VE'5o¦ºÜ‡‹\@·OÝÌÌÈËݵxQD„Îa¨Øýλ¡}Ë{?~m«âô^/t“_ÉÐ~S¿ -þùCÅö¯ìßzíbèЉé¤Ú0+맬ŸÍ¹Ÿ ëáVY½€¦Ï ‡7#j¸ëgÁ臎 ÍË3€†i—¼5‡Ù~')ªÄ"‡wdè“áô?o4|b# Šöã¾Ud¦—vŽ@oZ©¥Áê„+’öº®Ðm1E¹iÃE³¾ôšW$$UZ0tæ¶­ ?q>wÏÉoÄr‹ W•î!{‡kqƒuå‰ý©5–(™õS8ýB¡¢¿_ÓÕáÈŽŠjëz«u¹ã[„@FðC ªp" >YS´Ë*Iÿ„u íe'ý\v‚¢b÷ynŸ¯)úÎn Œ;ó”ۭݲÔk·¿6@÷Ì®Øþro²O·7CÛg&’qn•õSÖÏæÜÏì5û!~C±€‘ möïàÞì¥NÕmú³²’ô¯H§èˆ««`îìY m\£s×rK§5 ë;Žå–ŸÏ°L±–P´õ `ê1¢–»(˜‘ò *–ên}ð‘ß# ïï…_‚¾Nçç3ôFæ{¢.oÁ ï´“4Ê…R´rÆ[’vâ–2A‹½«ÆÐïOieü. *úý@ÇÂJßÞÔVy9ܘÝV@ÉÚ#0FéE;> :þàþ}OX¿k¼¬x$½Qg¨Øý~®‹¡Wê-Šj¼7GŸçŽ(n}2f2tï—Bä%®Ø~ÝH]øe­EÑ}Á¶àäù‘+맬ŸÍ©ŸÒϰéY0N¹È•ž?´þF¶Ç(RTz¾wØfÒãÖd†JÏýÜHÒßõ¦¨ô¼üZËösO ¨ô\,ÿ¬ e4“ Òó¼”}äÝoý? ˜AÝûþ”+õ´Œ "V‹Pôoó^ Õ ›TzÞáy$ø¦ËPéù›G%¥ò¾ ýÛû›j'ÀšKQéù ·-é«í£ú·û3Æ÷­ç‹ú·ïËú)ëçs?ǧ߅Sµ¡„Ú›É3ô[[Ø­Š¢‘‘ `—ÚÇ•?ª +,Uš¾.b:ÈÚa7L›µŸ 9úBb¢OZEÛèx :§ÿ92é—+E»«l‚oV2T,ÿÖÎ]鬣ô%–†—¯ã¦.­•tmFÑ/fHƒ¹3WCëP\% ÓÏè±ðñ‡ jr­IÞPç{n‚jRW,ŸBþI‰N™* 'åE à*.΀­'”(š~v8.>Á 49S_aèþAõ¤X[+v¿¨ŸƒDQ¿/Cmx]ÐÔášyTÓä¾£)šòþ¼åx½}\±ý±¡©°ÝÖ¡šª~ð4} Wì}Y?eýü_îç:WmvyòUŠÆM1c·—'so;M„ô“ÚýÞºxÎP׫ª m}‰ ½Ôvëò—sRP´‹m. VéÅP¹]%ä¤áv®Ñ¥ÏdD?]ŠŠåÿ¬_Nó¥tùþ6ìCÂ®ÆÆB2íÊ5ŠöJH =¶ue¨~9f¿½' /*Ò¡«gq ?žÝŽk“qø}·#¨X¾iWN ê¦Óíô,€Œ \Émc¼OüCÑû™£ U¢*C«›®“! (êÒÓŸf\±ûž…•–OŠ:2ôÂÝýB Ê yNŒ'ÛpM‚ ³é®ØþÂ9ÆP¾x0C½ô{Bï'„¢²~ÊúÙœû™qi4‹Z¾‚¢Zºvlò‚öÜ¡•±Ðû[gnÖË4èzó÷uz&ñ{é è€?&à}owƒ¥Ð† b¨ñüÏ$?ñE«n‡¶A& ÛpÁ²“o(W,6ô`VÃ[¹¡/bÄ^6Ix´Äœ¡YÏãéO“ÿ(A’ägšðGvØöä6CžöçB‚5!a\±|¿ ßË#[MÛÙÖ>\×Ý]!çðŠ> U·J™Ü%B Ëõ†ªÞê Ÿ÷®äŠÝSV"—™W ºþú+!kA ×Ò¶Õ?ßiIÜácý„Ù?¸bùÚ­s€…5ë=-l;gî@…ÎP|E¢¾‚V»ŸŽ@ÕëTàç kŠæLØ7õåŠÝßßi#9ç_IÑ}kâÉ«Š2îµÃ÷WôC¨ Ù3o=EÅöŸ¥ÁPoÒ¡.Ôzøpeý”õ³9÷3{Ûtv!ì…€ŽÊÏL•Ì꘳´W®b¨ñ˜ ?É™k8Ô¬Âë‹PÍ23øù,@@ÛæíÓ…(*yž»2´ž¼`ôÁ…{£i¹äצHŠŠå·Û§ÌºY¯tʪ;ÔÌ`#÷a·ÌÔÍÞmáQMOOÏP§!´Ñ¤Œ >SS…E#%S¬ÁÇe 7Öj/xÝhÅË7Ô"bvŽôáìð~¾WµÒ ®FU è·h7]• ëÍo ÚæéJRò1˜+vŸ•’!mStýì§Ä«ý}nÙÞjÉÞŽcºþA'¶ÓÚ™+¶¿âîQb”°˜¡ª)ÈçÊú)ëgsîgñÐÌiÎb‚ª<0dGZùq¯8[Âæ A Òîm¨ h¡~pûõ‘ ó>'‡E€Þ\:*ÿì`è„6ÀRw,wP¯®P­îÂ-0ß*ü™ÊË?9=ôd= /L:Ð6Ön܇SÝÑ&t¿qåÖ)õ¬zN÷´öÔÙ¯ ´vr3{æ’ƒë>Ô=ø0y–ì¨X¾ÍÞ¾°ì™ ëÃÁ«;Ï ê­Z‡Ï hçs×™ÆIPKßdb¼±%C·û@^犊ÝOñüN&}½KQõ²ÖЫð>wÖÏlLC#wBÕNjçrZ‚Šíy?,gRtNÝAh»£˜+맬ŸÍ©ŸÒÏFÿŽÌ u;A¥çYQƒ`ýK¢Òó± éV Òs—–Z0jjE¥çùuc…Ôƒƒ(*=ËŸ4«hÛ¸€JÏ=Ý*œ Öc¨ôüñ¼™$¸Û@¥ç1ò; ¥¤# ›Oå“9¨Ç^%¨ô|ûª›pøþŠJÏWéú@r²Cÿö¾†³ØOzBQéyž‰)sP¡èßîïÙ5ZGY2ôoß—õSÖÏÿæ~:M} ÿfÝÑ;æAWL †À¸ù\£áΦ®Q„¾é7lÝm êà6LXžmËP«1wi“n7®Y¢3œX:’ ~ &õ7ôâýýPý|, 7u×À’¯)Ëuìöñgh·ÛäÁЭܶúã¹q­ùòœ ¥ºÜcåÙ ©9¡›ß†ÁíuËþc›s`w¦= …Q §íKQ±|9Ú´ÌäžU:­)¤ Ó¦èòÆ0ø¸~7 ?Ó“¡`û*nË>ëHáœ6Üô9ðbï\®Øý{–tæÜ Ú¾Ö†*,ŠFm]IëOp-Fï “§qÅö?³ž×Ú~-º7ÖTì}Y?eýü_îgR|Ü÷V%è[[ 4-Z@[ž6„ŠI7 :ù‡ZjU5œ•›w¦¨_ßAìCÃ.‚Æ=.#›s.qc?»ÂÒ=@§+V‘©é»¸‹BAù[?®X~•«ƒÉåU+šPÜŽè$Ús¿×fM“&ÔuÁ f—WÌ=—¶´Àл- øQ´þôWâðŸ[FCmÏa–¨X¾‡FfÄù™E?×z’»ÛNr+ÌÓÀæŸoMô술ןôëÑsð9æ%·cÇ¥yzWìþ©žsèãW¾íl´–¾îrˆ»Â¥1º¤ÈÐ~‘ lgüŠŠíoµ/ îv§è4ÓP˜9Tž¡²~ÊúÙœûyéjxÓ»³€þÔ™r®w-Ѧ#n`m²/r³ïTwÏ3zV.“»±j ëët]³ŽA´m @›º>‚ìUúí~ÑúÇiPôÌܵPìn¨Xþäé=IJw¡G \ÈÍѽ¹·l¦³8==@M_È1 ‰57])ê• ¨q«4Xk<œ ¿—ÞcVz mS?´nÕPT,_À}ò»¶š¢*ÃTHNßÖ Ý» eø ýÅRÖKPçý?É®­óÕk½ äF-àŠÝ—›LÓœ§hp|,ML(çÊ¿^ËÚÌ‘ #;´ bûÃUæÂpÍ0ŠN3ôóÏ[*맬ŸÍ¹Ÿ['ƒJ'c¹!ÀDŸL®í¨Ã0ܨ7ASòCafŠ^œ¾^¼øÛÆOI^ ÕP=íŽ4¡ö?fBõñUJ¹vÏrÕY‚MËï¯uŒØ‘¶ uoû…¤ÊqCŸäKvh@]#¶ÍmÛp¿Ã7!´­a |Ök h@¶ôx2 ö û þKK†Šå³{}´¨í¡Ž µ ˜CM´á¶+7†½ñMtÁVP›îÉ •î;Y¢:Âݽ»K+“fþúAѾ穧µ*Cå²ü©Ãå"ôv÷ö´úV{†Ší7¹– ö¯[:»è>xvˆ'¨¬Ÿ²~6ç~Þ6ã¶t#è­Å½à¬âzîƒà+|¦èÒÈýà8g0ãn˜Âtà&:Žb½¿— ù§úÂõj]@ë­„N³ÇrËZÁ©^òÜ¢× ²ÛVŠŠåŸZÒ†ÿBÑåîvàõƒ{T8O²ZÆ4A{yµX{^n·e¨Õ­Oä·ÂìÿøºØ·ßÃÝ'XÀv͇Ëgûð!œÿž¢»÷wc©{pS¶)‚±ä¤€ªÇê@ÛšÕžì!ƒŸsŸ¦ƒOB/@Åî»z<¡¾ubhÊö:z¾²;÷ú¢Ot_wSîö…&l¥Ž*EÅö¯¯> ËÚëSÔòÚ,h¹é"WÖOY?›s?§uƒå‚®;¤ nÃ?rÇŸ{ʼn Ýnà¦N–\åËtDvA'ÆõNºÈ­•KÿÒ«Ü×€]ÅZŠ®êå¿[äqƒGFÃå–€Šå?½`#¸wVehqUL²Ôáf»X¦ê'¨zé:ÚòøKn—º=äæ[#†Ór’•ø‚¢-Jó!ѳž;æÛ pš·Œ¡bù*d6†ò5í8‹Õß' égAùöÇ]·+ Â@нOÛ>Ý4ûHO¸ŸS*AEÿ”YaSo†œhÏ*;q‹wÝ¥^ËZ®<¼hààŽ\±ý?¯Ô’¼qöÍKè ›½5TÖOY?›S?¥ŸÈ¢.ÐÖYPéù›ISàÇñÖ •ž÷»>„|kDPéy˜k Ì¡ÁPéymÐQX{>ˆ¢Òs±üK&ÄÁÃ¶æ •žïm¤4Ô{: ÒóígCR¯3•žÛŒNR•ïRôoóeœšÃ¦9TzÞ·þ$D}!¨ô|¸–!ìüHп½¯f Ã¬zõg¨ôÜÙäŒ$µM;‚þíþÙMñ°9pýÛ÷eý”õ󿹟‹õ¿À¿é™*@Эm²3†èsÇ”À¼|‚>ÒÌ…óC¹ÃMî@\ÕMŠ>[zž_ áFõ û¬#}Xt[èzý4wÏÝ|‰cÌ ‚*ý* êîQT,ÿ]C jl¯@ÐÈVTõõîÕ÷Õ/û¸« '3ßwÔÄ3PR;´© ÛâLú(4HÐë aÎr†þüô‘¤ßòçŠå;oúâÚZ0´eâmÈy>ƒû Î›äÏ[h}´q]|€›ÍYEâîuuêœaÄ»áCghŠB$µÔ‘»8½[ì8‘ ‰×éòyW¹bû·*Nöur€îZŒÄ+抽/맬ŸÿËý\þt*¼o½€¢õ}í¡ÛXáà㶉 ™­]ÀàÚî¹·›àÕƒ>ÜÊ׿µ5 žjGÈÓ]Q\íoMÂÜ¢>ÜŒc1ÌhÚŠF½±§Îo¡bù;ÚRûc· šÝɘÞY¦ èê‰õDå€;÷´ÚBè¼$Œ[‘¥FÊ6FSt›z0-ÝwkN·Yí?ŸÛ0ð¹3‚¡bùÖ/+0ôŠE2¼ÛêÄ]Qqœ¤Ý-tÞCxv‰ëºM‹Í°»b‰v®ÛΖ†¯!¨Ø}n)ô÷Žå€†&eS¥{›¹./hÌÅ7T~î`Öç¡CÅöûªCú¦šoZFŒUWÖOY?›s?³Ï„Æ%íñs2XœTdèÝ)ý5gž—r_ ‡aÓZe®õØ\0žà- ì x§” ëW¿£ž? µöŸO—^)¥èn?mÖÛ} bù×»*±w±4éb/Ëë¹A‰`S¿›»?&#]¹]ŸÌêr¼½êÃn?>LЛ~®ôŒÎi†–E¾ÌËçÂñ •œ _÷àö7…ˆõE€6~öÉÜŽ{³¨ñg®Q#'¤Tì~¯¥§é›_A€†ÚSê3/” o¶³ÇŸ«(Zwl,SY& bû÷ÿn…»õº5.="¨¬Ÿ²~6ç~Œ‚™Åz m:óÏωŒâvWS¦ûz ¿Ðö›¹C¾GÂÆ%†ÞipNŸÎµS©;ãê·Ж!d˜wðg5úâÎ ®ÕÌYÌuz€€Šå{Jƒ¤, Ô;0ŽÔ­Kä¶~l;™rû,ýã;swlXÅzX÷à:Ëõb»s]û´ƒ³ãÛ3t]ï14d† W,ß΃aJÑkŠ:ë@ÖAîþðY08w=  Ù£áj¸>7ìSkÉ´>I->HiË™Q€ŠÝ?îÊè§¥û½t¤ˆžø–Èuý±G2]!¨þgâhà  bû‡”Õ“¸¶·Ô$ø±õÌ%¨¬Ÿ²~6ç~¦v÷-Î M¼ýƒh¨oæö”DƒM÷‰€–膡ôAÏÞP€T³ ¥{BÏËo)z§o0œ7ï¨ÍüyðÙãAšžÐšgæ€zlV„Q1÷ *–?¸ër|g ‹ƒ´ÀÆâ(7ÛV¾ÎîˤAl<çs'l ´ë¥‚V;¥Êm(º6Ð9x:HÐ÷·Ci—äDŠŠå{0X äÖ_ Ç:÷ò)C,Ñ_žZ Øñ:A+‡ß#åü¸AÕ©Bt¿@Ë+É^hÏ»ßÎ<ŸÞ¯> ¨úè êa˜Á½®;ŸZ¶ï>–rùEPÑï¯;Óàð1€š/„Œ«\Y?eýlÎý¼¢@‰Òý -Oñ"KõörÍäS¡Ûû­é >OçRô{H ÔÞKån0¾ŽÏZy·ŠdÅ ôù”,’ð5™;Ôm6u2±æžwFkR®Xþ³Í¡úZ* §oL?½D®»B‰½°ƒ›½½5Xô å6è)²ú÷)š°~(ûªÅÝå"Ïb·ú3T]N­x4¢bù6n°»Ñ†üj:d»{¡ÇZ’‚¾[ú3n:ypæwC^:=«•ÅÕи.Tg/TìþÕÔyO q¥{hŠr>÷–0ŠÍ(÷çöuÌ¢j+¸bû/®~KRTµ‘ü/Ò¦AAn8 ÒóõWÂøÜå€JÏŒÌÖ¶kMPéùæ·…ÍuñýÛ|dª¬wÖ¡¨ô|‹v$Yñ@Pé¹QV,ýç;ýÛûG¼è'¯Ó€JÏmÛ¦>áýÛýGB´ö‚þíû²~ÊúùßÜÏÍ9rVÿ¦êsO¦¤<‚ ß_/bUǸƒœ _g åûàªwzf½¤ßýŠ6üÞG+²¹¶>EÔùq.÷MúJ†ŽfèÙîÀ}ùi°lzHP±ü¶š&Ðfå.†’¾ÓçzrSî&BE§Þ€žhw ÜØrƒµVƒ÷¬·ÝwÖ¢ªÜùGŽ_OÑiål²i7†Šå+¿Üš.kúòìw’ûpÝ Jɧmqî0©[ÈÍþ—°\оù³—d] d¨Ø}Åù…0xK?@K•/‚ñnÖÂÖnEyº¶,šÑŠ<‚ŠíO¼¶Í9PóSuÔ^nWì}Y?eýü_îgmç‰ì…Á‚nnÂî¨Ôsƒ”ŽƒÑ¥rîÓ¹à²(˜›™RJ^†qï ¦[ƒ—Úæ¡2Sú1ˆ¡lS¦«ž.A5/ÿ"¹µá =ÜAruFsÅò?;[Dîß´e舊 ¢1T‡i},ûz:á*<;œ@Ðu¾‡ÀfC, &.ÀÅÜn\ÙsÚÉË• ¶#Ö‘Üýú\±|j—/ÉÓÖ*gr”(ôŠæZ¬Õ&o^$¨s•qQÖž€v±¿'y~áCUçÑ#…+¹b÷¿ÝÏž€êu|Ëçkq§µÓ`z~Û zyÕÚÝq EÅö—Å{ Ofe4¸m`÷‚Z¢²~ÊúÙœû¼¦5[5W Ð@ÇbÚÝûÚô0|½>• “…Bå©Hn­j‰Ðßû  »=“ɘü(n—†#´‡¾&×p¢¹°ßg<·iÈ1°Š‹d¨vý ¢öi W,ÿ†á“ÈRAÑ6ºÃÈuÃýÜ+×Ò!ò÷5 ºöÜJXïq‘¢n{ÎB&54î@ŒÔM"hB^;¦3º5 ›g«°ôéÿQ$_æ˜â“ ¨±ûCòmãEndï{’Õ<¹Õ.…’¡Q‡¸m|î’?GRtMEÄ%¨/CÅîÏQ Ïzvô†ý+ØZ£ÂÝXs›q’¢=ZɱúC!ÛßÃ?CòÞ9œ µ~wŠŠ¿oTÖOY?›s?&zRdž€ºÚ¤I’ÍpçÔ­‡Žû¸‰‹ÀñÃ$nÊõ?Â;GîåU¹tœ·]“ZQøç6Ü©sZ³5j z\-DïAÑóÏt¨ÒÆ8†Šåׯ’j§‰•;²‚ìIéÄ}UÞÆ ÕÜ“^cƒóÜhms8ZÑ$ ókˆÒŠaud#逮*€N9©Æ´íöT,_Ë5*03ã& WÎô¹!%ܵ‡,õ>ç>ŸgE4ýns/…ذ)%…}ÿÍ…uóJP±û9vïÀæÇ/‚jtý]ª¸‹;ÿ” ¹æ èáÚ}¤¡u(Wl€Ùe²³Ã@­“€ŽQ&WÖOY?›s?3Ìw ŽãLÕï=Ø6æF/Z ¯vrï:m‡ö’¹Þ“²¸öÜŒnXìµÜv,€-ÍÑäÎöbKZ»qU÷dv—3tÙ¸|rÐt W,ÿÍáäî9MŠz¾L%ÕöÜÛ]5àb§0îCÅ5p:&™»Å« ä_ØKÐ’cþ wg&7×%•|mçè—%ÛÀ5ÒŒ+–O;ÜT9 è‰Ì9ðýk:·Uq.ñt¿ÆÕ3{E<‚³¹«ª¨Bq*E¿f f¤Þ‘¡b÷Gþy÷J?ÕÀ²)”»¿à„°ÿÈ8@ KZ³u,¸¢=J¬"Å÷×ýùˆdü+[®¬Ÿ²~6ç~.7=L6´ï¨Sõ}’¶_‹Ûµ`?,œÂ½m• ý߇q}6o“L¿·ƒ;Øè31†s7i?¡rÇçqÏ¦Ú ÆM÷ºBy#½þ:§U{¶õ÷m‚Šåw>y‚Œ+K¡¨»~õ™û"ùL>ÙŠ¡BØe0<4Ÿ»n··¡¨±ÏCÈêß™¡ïì$ ¨­z#)Ý{™+–ï×ogПµ ÐimíaÉk{nåÒ§ÄÒ1„ÛóTñ8>ÛþÁHVµÒ˜¢Î?gZz¬ÿDPÑßï5ðÄýA/}„º˜(nÂ=¨Û¨Å8ö4”+¶ßia¸ ÐèÚdØê=\Y?eýlNý”~:y%9aê€JÏ]fCÃÑÙ€JÏCǛºSû*=79yJÈn?Q@¥çNÉJlé.;ŠJÏÅò'ÎM$ç“ úþ@ÐÐíɸRO̓Ë^aÏPé¹Þ‘àT.èßæs͵„^›»*=/2&<õÕTz~B#Cx:/’ {ßqÁ€1þ•žÿ.z@2u»ú·ûƒ&€¿çX@ÿö}Y?eýüoî§“–²Õ¿é÷2*K$¨eÇËлm!×§¼R35 PgጥcùInk ~¼ž¡©³½éLr{öè÷¢Çn¦R -ZULÑc]&Bzo êò"§×T,ÞÁP’½"Еº’«»¹éJJpã{,EËçŸ#·v1´hO™^qˆ+§Va™½*†Ûqì_Ø‘¢÷ò}!¸j¦€Šå+rúJUβDµžSªBКŽcYCh'@+kéüµo :êÔ0ï>S‚š¹Á;Ý] »~uHìÎÐÝñºp&!šÛúŠ vª^Ô±ÓaHr„"tß«[p P+%èî¾ç?ÎëÈ&öŽaè µW4;k=×ü©-uÝ<” f¥÷%±j·Ÿâ3èaß¡u½6ÖT,ÿâKkùûµªµt49Ƚš6E8;-€¡{>çÓñÛ¸;_„Ð/¹À=~޾¬Õ¤è'ïßàêàKЉF×ÁfSŠŠåËéŸK} ¿ô媴K Ђáîôþo‚V9?“8Ì1ÐJ¯gBM^C­ó4 Co®ØýÁÝ:@iëx†jS‰c"wÍë#4te cN%šMý¸bûoiÝ¢j5Iu‹ïD7ù*맬ŸÍ¹ŸçÆEC˜Š½€îÙžò­3 ê$Ö€¦ûLï´KÑLË«Õ UXuн»º;¼C%Í´S(:¡%›¸û÷ÛµyàU2ˆ 1¯WAE¿ëÜWorÀ©z7CÆ$ÀÚ4_nÁñuäêÞSmx´šô¿“Åõ( ^³{3´Â¬ Œ»-æŠå}½…UÞ¨§è®´0vUç×òz{Ö#Ë ÐïÆ‹XðŒYÜÒÕþФžKÐÍõá°µ`'EßEwcôt" ~S×Ó>Ù¹bùn>޹% Uø–ö3ܹ ªO©ºd6 3SïÓÕ7£¹J |ú.C‡¬ýBJ¯mçŠÝ·hŒ¥rwçZ¢§ªâè˜ ~—Z dèøÉ òÛW¡bû»†Ë1{ïßìíOWµTì}Y?eýü_îç1%kÖ¯âE½Og¢ºsÊn(ëßPwù ðÍqæÞÔL…Í{0tŒY1ħœ¦¨ûºØ¶;wòX–xúA##£á•Ñz†þ:º ²“»RT,ÿ–-ñL§cE'þLd>d÷]Ærü?‹é/ˆäV:ûCöš± Ý9BVrs¾«±àȶ€&Ú$HÖºLáŠå[’”†ÅÛ¢‚¸^i‡¨Ñ¾<@;ÊYÓº™®½~7ȹ˜EQݺÄ8§%CÅîËõŽ£a¥ô“n Uø¨NP§2WÈ;ûßL bûgäw‡.¿O´¨<œÊn ¨¬Ÿ²~6ç~F-fczvah¿EëÙÇé\Ó˜›pàŽ7 ë'VÁÜ%Ü„—¥pcr9E/kœ‚¥F }ëÀŽ¿r´ØÊˆM~=‡ë¥¡I®¶˜ÆÐû³F3ÖvW,¿ÙöxVª§IQã ì»w•ÕoøIç¾ÞhŒ_+*ÿe ·öñ*ðW°eè‡9awSwÝpöÁ Â­Þ #µª¹bṷׂ̀ùÄ`†– e×ü¸™™÷%£~äÚFó¾ä‰À½|t6›¦w…¢}|™ó“]E¨Ø}«yQTÃÉ #GÐQ{̹Ó== ëa5Ek¨/ù1ÿ!Wlÿ¡/ Ô…š¿ÚÂ^ÞáÊú)ëgsîç÷vo¼)Cwµawv›qï?«‡É§J濃‡¿zsÇu³…³&ƒš½×‰¨¾ª¡h®æ0¦ªò€ .©¾luÇÜúÂ%´zÓ>†öž Ê&»íäŠå÷îÈtü%hœò véöŠjŸËjç¿&h^nGâcÁÍîñF^êÅÐÇs>ÀtÉ,î*í+ ®è®à3píW,_ÕÀ*ÿmCOÝžE ³€ ?“xøªr™®4säÎÐÂ&¨1ôÉ;ä9ž+vJA(u ‚&—…Ð÷ýÖ¹ÄzÎ#ŠöëþœØÆmf¨Ø~ß­àpî2 “.NÍÎ{¸²~ÊúÙœûùÂ<’]Õ2ah©ËN,êÅÝвn)ÚØ"²7'¨á“n-|)Ú5C0Ó.áž¹Äü»'èóevY¯ >zÇ„5÷åôYI(m;y*AÅò7%Nf[)šò`ÓO8ÇÝzH“¸Ÿ1$¨ËÊÒæëE®ðâ¤neè0Õh(ˆXÀ-«ì gì+ šàé eÇKP±|·å[[š´ahc¸ 6‹¢ï;2“GÖ€ö?ˆõ]=‘Ëi0ýô"ÔàS¹5‰ b÷sg†P£“fm¿wy¤Ë=_ïC~*Þe茖Û%´nqÅö¿a™älÖ|@ÏNt cRݸ²~ÊúÙœû)¿9žyBG†žÙÇô ~Qt ¦/Ôf¶ÔYÁöLíÕ“t=3.Ü…¡ãQ‡™ÜÆÛ.D]¥ û×jÁ7¿‚ú¹Íañë¢58Õ‚ÈèÃýùøõf·&(0t}Gm斥ǵßn§™ºÔÒ/çV¿¶†èëÊ =]Ý,Ö˜StxÈ:˜ó>ž¡-t¼á Ýj®X>7ê)oHÑt_ÚÃÜR‚– di) 5LUaI6ë¹7šÒ…Ȳ‚Vè„ÐÄÚ @Åî»ÊEPËe‚&6s® hœIƒ õ>„¡Õñá–×|®Øþ±¶½àÍúQ€GFÀÙò®•õSÖÏæÔOéç÷Ÿ(V<áE¥çúµÀ~ç@¥ç'Zôcý'b¨ôü³:šp ÒóylKÿ·•ž‹å÷Ÿ£ÍÎÚ •ž7%€õ)@¥çîZÝ ÔC™¢ÿçïWr–^Áпͷmø*j5éÉTz~)p]¯â ¨ô¼ƒi„¹²п¾)‰–3/•ž»†+A›]+ú·ûw$%CFÐWŠþíû²~ÊúùßÜÏU­þM…UpC@Ð…ÓRáç7îªN÷ ÿ8C@WöÊñ]¸Â¤‹ lL§èÃ/WÁgËá"ÔH͇%º/£è´±:̫ӂ&•[jß/” A Shʬ!Ëÿj{0uÐÐÛy?IRN%Ú/¡NÛôµêYx¹õ4·Ðd7ñ›9P “‘PYÀ]=·‚Nì1€›³E7T,_MA¼º¿Œ¡zã)ðçFÄMbµ¯^´òæ|¦èp—ÛÖå=í¼­‚¢=ä5ȳ£»ÿèaÁÊÃÅí”óXS¸i~†:z¶Ç.-˜0™mé.*¶ßèó9¢ž¹P+}k"T¬áн/맬ŸÿËý¼¾à4$YY´àz14Žû" #CÁmš †sÝ¡‹®)·­jÈ…Ô鋼U׸Ü…«Â×—¯¹1¦°íëMî·ì0f©7Ð%ålÓ ®Xþ—kˆUݨ†ÐH4¶µ• ÙMj¿.&è÷ÙiИœ/A§úC•Z  Irë¡ÛU_îÌ;nì縉ÜÒÑ-héw3‚Šå+¸¾vfèu…XÈzæÇMYîȶ™~&¨Y¥ëâÚÐÝ¿ e”¸ªi'Ãx®ØýÝÁ¹ô º&[Èm¨åöNÄbÛ®ԿƆ-{×–+¶ßö®2(©Ùºº` Ül?œ ²~ÊúÙœû9ùéh§(hÆŸ·`·±M6Ôß[êã¼"÷ãæ¦Ö@ŒÛ| Ú×ëx½[@Ñb]-(6W44U˜³?·ô¶-‹û¾“ ÎgÊHç…ÝËO¨‚®Ö= špEªÙSTåŽ8˜ßå^yA*fuahxþ Yè«RÈ(âœ7'J÷šÕò(,¹¾PÑŸ¯[<¤nZÉP¿Ó™ÂÕ«ºGWé¨{Ép‰R‚ ×íºTû×yêXê}|Wìþã‡ÿ|sjžáªi“r«ž–èÉEal¢#¨½‹?;ÚP±ý6×B!ÀU¡C.o‡ë¹–\Y?eýlÎýlzQÎG?JÐôY ÐX:–¢å«6B£YAÝûùÂטöÜ}6caEg%ŠíRgÏè"tà&Kûê* ÙY óæ6×»K,Zs‘ Ù>™ð§"P±ü„!ð1ö$E}ƃIûV 5ý<]\¢ÃMÚøT²è}®äa7ˆÙôP‡ë“AOë*7xá ¸2…p[TE‚ƒNW,ß×My õÈŒ¡#.^‚¸ºÜŠ!GÉÄc]µŸ×ƨ¾#hÜÆ[ô¦ÓH@g| Ä³r(Wì~è(R›Zg‰NòÐ!Ù­— ¨Ö£ú4\Ðì¯GˆÍ† ‚Ší×PL“Ú0ôÒ × úª¢²~ÊúÙœû9`ËWÈïHÑQ‰_`®ëàrÄ—{.jTßêÁÐkäa”ãЦ×_!%sF1ô¾Æ&(i›hôÓvïÇÔç,D5Îå®J3†¦Rc®XþÊ™p7Q`¨Ç§yP–àÉÍî?UòÝÖ„[`y@ècàÁM´XKBÍ’ tçvÈ=ÕZƒ¹Cv+ýÖ‹T,ŸRØ#˜mÑ•¡–o`©qn^èl˜¦³ ;f¯Ç;'sý öýó»¾µ€êå‚àô–Tì~Ê¡îÄ4®H@^h“ÜAª Yù‡8tÁ=[Càݲ€Ší¿óçäŽÊP亮à“Ùt®¬Ÿ²~6ç~š×ÃÍÈ㥟ªa.9Áý¨Üöþ\ÊÐÄÇ÷ˆyf·Ç­|Ëgª¹­Òúk'¹—RŽ’²_@¿ì¿Âæp Fo„N‘uÙÖJÄ2T,þr'H¿ÉÐô%Sá}» nQN2æW.{ká·;Ü³ŠŸÉÑ_ûjXxŸØXsß®´{³µ*l{že¨X¾SU°â«)C¯Y¿wnc¹o 7€*[HÐúÉ› Ò¯»oÌ[0U¬ã¶ÈS²Z¸È•+vß²ow¢~D ¨ÜF]òÛÌ‹{äQ&|z¨Õ÷§[±…+¶ßýùNØÓU¡Ðß w}• ²~ÊúÙœú)ý(l­€«%•žóJ%3ïîd¨ô|Mâ#ºdp C¥çsßxBª­/A¥çqk"àkB8C¥çbù{5X™S†JÏ·î˜@/v|ÎPé¹ãÒþP´þŽ€JÏSþéyMbèßæ;8þ>X°g¨ôÜ|IÜêÕP鹫$í’ôoï_ÍëMò£÷Tzî²=” gú·û[ª ±(ú·ïËú)ëçs?U#[[ý›ß:Ç2´r³ y jÜËǂގ"‚;A¹PKôæÍ(_§ èâÈPg=œÑ—ùL½Àõêpš¿,àÞ>(ÇT‹wq æ÷`3j2¸bù];®ažêõ^ºuËõã:|)Š5‘ghÍ1$«MEOÙÑ.VŸ¹ç·g e÷¹_5‰é˜ ª£ËJRZ1T4ß:wß4@»XÑŽ¸ =#ea]¹®eC`@uncéu‰×ÈTÞ¤ zº*v¿²v™éêh—ʽäëý­Ü‡ÀÛÊ8®ŸÁ]ÒÕÜ+¶?f\\8>€ ƒµ¬<÷ÆpÅÞ—õSÖÏÿå~Úä† Õ¯nQTŽ^±|”ı|>cö# -óJƒÅÆ!€^Xp 4w§qUÔLiì¼ ÜÝ•vtÁÞŽ\ÃÖõBýBEnÊMeøÙ°” bùûÝXÃ:õßh·/óY¶ÑnµáÒ¤3†¢æËK…³Ò‹ÐˆAÌÜrESŠw²ÍÖ?%è<Í#ì¢GE÷žÏ²ì*–¯¼m•¤ÄÄÐ*-½ÞŒä^ï¼4à.ãAÔ˜ûÓ02¸ùœ òh Wô¿?óö¹ÜÍ€®˜MZôóæúÎ#O^¿$¨§ó$È6P±ýžJòV*ÌK@{îe0¸uKŠÊú)ëgsî§îÓ3_Ó)ZVRäóK¡bF0zŸCmLÛB§sáÜà§áFŸ@´)‚:Ÿñܘ;ó$•¸~‰-@còG‚>­)‡àÐ z*ÿ9˜Ý@Q±ü F³M¡€>í Ãªg„p¯Îl”Ô¯øçûàÿ»c[(Õø3Ð¥-l˜ÿvŠúMß¶ ª ]2mSÐTâ:/Kµ¬mŸDP±|·_.%&›Ìý_El•4¸{.‡0ÑÜ×@¡“;·¨×øºöwß%?ˆ¨ÚÊ»ïüh?y¥¸ÐÇGRI±ÂZîÞWÑpãw_†î;+÷rÅöï° œ/*BgMÒ«3V*맬ŸÍ¹Ÿa;Õ%vÙç´QY_¢?˜ ¦ßT ŒF1ÔÂH"="¹»ûÃÀñííÞm Ràú©Å€¹çMÝ'O!ßà EMO4ÀñËÊý6r-d O¤¨XþE»+ipT  ?×ì¥ ùk¸]êöÐ&×î—j}ªÕö4wM«P–4Ó”¡Å7£YSž:wÙ }m­hôû9ǹbù¬4M!óé#‚Òµ‹¡@s.×PeÔ ‰tý"xá÷ÌGØâ“MÐ.z §jx»"!ü¬[ èE× dAòR®Ï©(p ÚÎÐW…Aéü ®ØþçJßÉÝÖÉ55k)(äÍe¨¬Ÿ²~6ç~Êí%Ùÿ~ A]7J†§ïæ†TL€»›¢zãàVH«M⎚4‚ôszì•5¬ËlGQ[Óó ØærUn=‚ä` Z³ÍŒ¸Û9P4èG9=þaCÅò-W¡)ì•sö/4´íË}•. ¸}†[/|—\ö=Æu>¶y}ÜJQ rL®ÝÉ3¨oÀnÈýpÐ÷3µ`[Ù®X¾ãƒ@®àóôù ½Ð(çLÑ7Ë} cY1 ÷\# ùÇnWëmé°–¡´ÇÃ’ 3\±ûÕǺwó=Øê=iL±ç®Î¼ž »ÚnQ,œÀÛ¯–ÔŠ¥|ÞÍPóæl;õçÊú)ëgsîgëY’Š-ÑÍžøG’;6†[ÆÂ¡ña.CÛvކ».q]4 cˆ-·ÍålÒ='»i½‚Ulà ¢dÕ¨/EÕº;±ê0?†Ö%ÓI?¢¸bù½†ßü,ä z6„ÔŸ"èPɺ­å~@³7*²ÂqÜÛÖÛ‹|îOÐÆ×~Äù8ᚇöíó=JüÏRT,ß‘k±çx…¢¿—î‡I£þp§^=ÉÛ¯êx1Öår—‡øAã¤x†²Ü$Hï‘Æ»ï*'ÝÏMô÷®ÖÐwpW-Ôù% ý¬ô˜¸û#AÅöWæ7҈绺gR í©wœ+맬ŸÍ©ŸÒEÂpjãIPéyuû]pûÍ-†JÏw#bWq„¡Òsí %ðnùŠJÏË.çÓk]/PTz.–_ç·)¿êIPéùÍoí˜_¯@¥ç^ç“HXA¥çæåᄞ3Cÿ6ßÁ)‰ ú¢C¥çm‹£Àcí>@¥çƒ§ëÀ#E_†þíý¹K4Á¯ÚPéyÇ»„³›×Rôo÷É¿¦n­rú·ïËú)ëçs?=¼ÛXý›a‹ê–gz´Ö€Æ.Jæ>›õ Ú9 ¨ETÄü9g‰ÆÌ2grÉiôè¢Í¬&Ü„ W>®d•^C¹ ‡e2›Ý'¸«Ôî ‰/¢(:á×oê3©3W,ÿŒpy²gzCåZzŽØZÄmœX/D8†4¨ð ©d èøº…Óþ3An­²}  ã¼Kè÷SO(:L¸Aê ÞT,ßâiÌðvŒ€¾Z‘Ê|4º´È—T´ÿ¶Pf¼ºž jm;0×Óa-. g]z ¨Øý×>½aF-?Rãvôåž×ÿ÷–§tb›Føi3š+¶ÿ½ÊAPKM hï‚\(ôè¨Øû²~Êúù¿ÜÏýºÓû#í²·;}eÌÕüV =å3t•|%Ìû<‹ :ºþ¬zó(îâY!¬Ýù­Eh„U&+®îhmNÖMž[׿(ˉ­§hÅ»=¬û"k®X~÷³µ’àA mXâBi.7ï:+=íq1f(íà^^´jn„qUÊ Àû×QîÁÕà±îBîýÌ!Puÿ W,ŸœNÛè¼™ T÷ë¾â8÷öðöø|%÷DçCìk… £Jö°ì¦z º»1u¡¨Ø}síOð¢ª?E/·ør‡q½æ”ào· úÊÿ% —k¨ØþQgŸÂ¨Y‹-uü 'gseý”õ³9÷Ók’>];k# vËÑAJ.ÜÄU`Øtœ GŠ?•ÜCn1¤± =ûPtêà<–=x…µñw`zßfÔÕ”°àD®Ê}æ˜Å(êóqÙls• bùÄÓo 'úðb.ý2ô÷]L$Ì÷Ýè0û=°äA8WAî.°ï‡¸Ã~^¿¦]ÜÇg]Àud!·ÕÕOÄ.?œ+–oVi.#ïÔoB:;ªÿŠ{iM<³·×TëWsÑ2à¦^[ÂÂ&Ç ¨ë÷ÕEr¸b÷ý§Ö€ßÁ15W ç[ÙsX^ƒ‚’éë°~®oOQ±ýcs>€ó @u?ä@uÁ‚Êú)ëgsîg»VôfÍt@®£A£­¹í¦5Àï›×j3ål;ÂM4ÙËšþhr{ýêÍn÷µâîxž.ì™Â}Ñ5…ŒÚöˆ{ö×.P=ÚÐâ¾GaËü$®X~GŸ|ÚãMC•Z¦SíǹSúû€VëÀ¾¨[×/SnçŸið[͛۩{0¨Kþcom‡”7=  k1T,ß×8VõåAÝïfOs¸‰×­™Â/}@] ¾ý?ví=¨Æ¶íø¢­$$»¸“BZç±ì#%Ù%IBHB·m’J”J*¡R©$DJ±Öyf—$IBeeB’ì~÷Ìû;Îgf½ó>×øïyšuýó™qÌußf¾µjýÔª=×óy>©26'¨s°?´í6 P¡û²ßÀãË)ššñ¬ùp'žÜEê&æÆ‡¶ÐL;ÍÚ½"̵´vÃz¸vm?AýTô³9÷S}ÂÚ®x4 U^¦ÔJdÊÕ‹yž5b´[d.Ôet“¡k -©G¹½ÚÍI,*%EM­·Ë¾Ýas|^êp%oÂç@@u¿,…8lj\¡ü_†í¦£èI†6ä[Qû®Ü•~ u÷& [—ïÇ—¹»fl‚´®Õ£­`ÿÙ˜Ûn©9ÔØ2´ÎfPíSÉÊ·í–{g3A³î¯a¿¦kq{¶X@SG–qö‘zŸÚÄÕ®„$Ûy€Š²®€r’3Wèþª›ÕðÕ"˜¢?Ôž€žJ ·àØrj=f!Cg¹b3æ íw{a¿¦¨hÌEqX †*ú©ègsî§…¾6íÒb ’³92íKFÜáö‚‘Ùú¶b28x¨Q´°nѵžEP{ËQ ê¨ hi¯LøøëA7·©‚ó› 5Ò›Z¾#eh‰‡º8Èd C…ò‹+¿Ë²ßf2Td·L¦‘•Ë +„2µ#€6yï纥\ÃkKaãd èšÒÐ."‘¢Å3úAÉáÓ -U™cªl¹Bùò¥n¬1ÜYŒ®8ìÌ$-[ ÏUHAøg1Z³}q#1Ü!=eèÚé'`FŽWèþy§ ‘ŸLQG¥°½}&×u‘{Ð9”¡«í³ìï;¸‚?ßüÐ÷]24É6•’û>\E?ýlNý”ô#öK_uù Pùù¬<°‰¸.CåçA™£•Ÿ[vP‘ônÝPù¹{ƒqh:ËPù¹PþŒïDj½(¡òó$]+PÞÚ Pùù¨úÝäçÄ> •Ÿ×Þ&Nù’HÑ?ÍWùh“øì±@åç—g\%©ë¿ŠQùy£DDÓGHÑ?½ß?à¨n¦•ŸG xA—¾ eèŸî¤þ´ñÑI†þéûŠ~*úùŸÜϹ?ÿçßÿ/].ƒÛÙ. %ueÐÃÁ‘;_U‹9‰Ž‹QƒêgÔÉ®?÷ý±V°<` úמiGqÏ|¹f*—)ª×ªÖ–ÝãZÎ\ óû\çjwö"&ySe¨P~å íÔi‰¿êÜÕ_&™´_Œ^ï¬Eéu èÐ!§e7Ö_åž}X ~ŸÔ¹š.J’·í_´e©#H&Ý’¡_]vQê{…¢BùÒFÀ䔽UûjU!jb´Õ?¿'U]8HÑÚœ&r)3‘[p‚EºXtJÓ^6e~Wè¾…M*q®¬`è–†dâ[û)![¼"hIåp±T'P¡ý'÷¬¥æÓs(úxªË_Äz_ÑOE?ÿ›ûY¬vŒžÏb¨¶~9Ä®œÊímCmj‹Ñ÷Ñ2Ù|oc‚>Ü]G2>ßôèRCH«zÍý®ù†•EPÔû’ª¤,ð(·M7–ó9G†šœe/ÎØT(¿ã±eÒC1*‰ &å-šú·*ÒÔsýø(—{3CYb\˜LÐÛƒêÁ$"ž;íý¦”Ú’¡.­0ç¾K¹Bù^\o KgE몟’TM)·ÄV\’…pW™R—Û&ÜÀšãÔøÑ‚j'ö'6‰Q¡û[IDì†æêÅ’NO¹ö}¦“ýËŠ]êG.íºÍÚæEóÕ§¨ÁùfìÁUôSÑÏæÜϽ}Àcåq íýè1x¥Žâ|2…˜Î$h h‰‰ÜÃ1\yP MðO>Éõú"’Ì&¿(ªªvŒò¹'±³÷çËÐ6õ`›l#¨P~­2Rë­IÐÎËUAýSî¢Ù´.T ¨oc m™œÇ5ñx WGÞ'¨ôÐP/ùÊ- aßìµ:½õWºh•W(Ÿ×óL²¾¢Cc.F’xÖ{dR5ÕowKŠj èÅÌ%ÅhøàÕpèdA'öx :!?ĨÐým÷#‰–5CS?“ܰîü;ÊDoàU@†ø‹ó‚ò¹BûíN%0ýAõÕ¯:Å”}¯qýTô³9÷sÁëgbdÊÐ~w^@“Ž wù¸¥`çCÐ=ócÀç³ w¤ï+ò°>ÐÍG$yê6®ëîýqlEOÎYñwòdè(u)‰o¨·@= ‚ åØß Ôÿô’ù pº¶†Û¦ZŸJr5&öêÃmèv bÔr ÚÇ& ^u¦b4/û«´je4·ú¹7¬)è ¨P¾þã÷;-3†Ö"vǧs]¶–~`6A[´·e¿R—qöÔN/1:ùÜop›} B÷ŸÞA´ßÖ0Ô.σ¸úý˱SÈ¥{‰€Æ>ûND‹ºp…öÛ· fS{jRôÒ–vL´i‡ UôSÑÏæÜÏZ7Ð.cCuûÖBÛ׃¹ªû³ ~£2AG{\ËaûĨÅ@0sîè›7ýá}o1ZßjGO¦hMzïÕ®ã×_pf/AWy _#š¸Bù{g‰áÖÍ8‚¾Lž£K¹{Vo 3.ÎtóÑÃäwÄs‚>ʰôí)jº?ޏX»rÓ‹a`g@iÞ„å.ǸBùÌísÈùM«šZNd=ƒ¸–1‘æ‚^í´“,¸!F=νýÄùÜÔ´à&Ú%C…î_Iu 9¶Õ º DÇö)WsVñyspšÑ¨ kuNRThÿiªFŽ“¡[Zv%N¹?,PE?ýlÎýl\ ֎Ú[õº,5çzì.†ñûO‰ÑÇÖ÷¡¦Å@‚þ-k17”ú¾Åmâ‡`êöÞ€*ú©ègsê§üSóðŒ0 •Ÿr¬xÝ0‚ÊÏ=7?"_*?wÙÃêÚ&HQùùÖ®æôj¿ŠÊÏ…ò½¸ &9Z*?/šÜ,NXRT~îêQNÔu*?Ÿ×»ä¨SôOó•ÖÃE •Ÿ½ÀŽ+åˆQùyãô-Lõ=Cÿô~^AØcÝ=†ÊÏŒC˜þ-†þé~é¨Rè¤ßÐ?}_ÑOE?ÿ“û¨ßVòï$ƺ¬ªã0Ú¿¿!-öä¦À ¦>ä,E-ü˜AðîåO@tÔÐnD’í»pé JZ´ð=Ek;8±äó¦ ]ò÷°qôéF%°KØIP¡ü>‘$Ðt²]0ø X^ZÂ5]µ›Iæ_¥¨¡ÑfvUzŸ›·©…äÉÚj‚>s¿ç¾à=& ï–pãÇlƒ‰ûʘÁÌÒ´)zæm"Óۡ½£*)¿û “ZI^4fp5¦&ýg¤¨vI_ØÒë5A…î_ø+–…FxS´ox"ÛÑâonÜ,#½*³@C-Ú¯D=Š íß¼¯x(AÆ-ƒäúR®ÐûŠ~*úùßÜÏU¦ÌÁ÷¸ M©ÇÔ[Ôrë¾Å²ù;ŸQ´SÞ¶åû5nêÁ’¯¾Tcܸ3á÷E¿8ƒ†0ÔÎ7‰;л‡õ ûÞp]'i° ¯Ã\¡ü o^CÙ°1:~×hã_ÃÕ“:³4å\ж^nʼ“'s§<÷–€š÷¾ ›ýë ºh@,xÐÐVÇà3dp…ò}1‰dúË;R´nÊ.–æhÎ-jR•˜Ó]=D$ñÏ_Å-ÎŒ_›]€–(‚6ªá\¡ûÉ—RÙÝ”uí‚­[ÅuÓ£–}q•öª°³wæ3ThíîÃ`±f" u~w`¬×F®¢ŸŠ~6ç~~µcÑsô(úñ­+ š \¿Úó,ýZ ×5-›Ý^ÌM:ýÒN¶Ôñ|)ôëÞDÐ!ê¡Ì­S†ú9ù²g ¹­”ÙÙÜÙ–Ý™dg4W(ýê{0¥®A¿ûCáSc®æŠkTäou­ªºf}ý·üä›Y=›Åí6k#Ë!;º~^[TâÁÚor¬ª¢Ü­¿] gÏä*ú©ègsîgòñÌØsEßÓh¹…û{ú1vퟞ¢ïC£Ùß#fþËì³PÐÞ› ©?<ÁG¤&E33—°J ý­ÄlváJæ²ð»SÉÍÕÈ#óWì'¨Pþçf× fÆp‚d ånÄ ½þ˜Ô/îÆuë3º9·âÎ9Þl"îËP§.C‰z²*E{Wû1Q»¹‹:§¾ÚÁ ÊWžäÀTVSÔÐo<ËqžÈ­êSA—ŽTãÒu(Xx„›=t/ó“{åÅâÑyºÿu[.Óx>ž¢¦¶R¶tÁnêæ¾’¡7]íØ­\¡ý}:ß„ñ æz¢ð"t-˜ÌUôSÑÏæÜÏjçp¦Ù*¢µ±ovslÚÅVñånÌÝľXïçŽZ]Fœ¶Ü‘¡¢ƒA– ¹£ãŠóÖL£µkÛANÃu®§ml½µÐÐÁèÙq®P~Ë#ÙP6Z… ‹Æ‡Ô ‡Å芸¿AçŒA‹l÷C›3!\i_5Zþµ EÃÏQëü½Ü.qCÙ+‡úì\wÖýâ®P¾‚¸~¬Äá» ÿK™™ÌSá:¾•BFù&‚Þ0:©O[p½·ž[¿½BQ}2ÚtÆ¡B÷Wn¥ìuqWŠNidìÇ eîNÛ¡ÌгC{7¹P»[(*´_ãC2\lQMРËÖ`3h«UôSÑÏæÜOÃìd~sE-6žbc¯lçΪte»'†p—yNa¹VÜ÷··Ò¼ŒuÜ ïÛ²>¿¾rg¯[W¤>Uî}ÖØhzzK2ØyfqsÖAáÏ\¡ü÷òÄ,ÐnåAg½\†&\É„­·« jr¿,Gj꺤7»1í'EÛÄÖ_6`è—àNlYÅý%¾šBQ¡|£§6 ¼Ä衼×2ŸC­ z[oäuS—¢ãЦîÌÏ2ÔtÑBÖ&.‚¡úÕãÙ¯[‘\¡ûÏç±Ï*dè•ËyÌþò1îtoJ´.¤ˆÑî¯F‚áÕp‚ íW«ô#åGÈÐœÉ 2#ØMQE?ýlNý”væd³ô 7ŠÊÏÏ·èÎâ—DÊPùù¾å^lt¹CåçÞ£îà 1 òó¤wHü™*?ÊOìV@†Yƒ •Ÿ‡'7Á‚핟/uLdC5{1T~>tñKÒôê(Aÿ4ßÖ¶JDzÝ‚ òóG¾ƒjSŠÊϵ‡ŠYû¢†þéý):y¬ßo/*??Òs< èŸî×»˜Nû‡ê2ôOßWôSÑÏÿä~~zþ?ÿ¯éÿR¶¦Lfò¾I†îOhŠNsc¶É`‡4…¡ Å©Pr„;#½?3ž°“¢¿®8±­É\‡‰g‰^ŠŒ ƒþ:N¦¶|.Cßë1ÿÓYý–¿—EªŸà åWÍ7¦ºÓÜÝ;s·ú›[ÿÃ9¨›QÔ»°Óß÷ZŠ®ü­–âJ†šH?“““Ÿr?ÕMgS3–Sô±­-qq£Bù’Þ‰“WSTÿ€Ø'X‰¡ÃûÕPƒÏ­­ük7õž[HМS›Éž1…ZÛ™jžÃP¡û¯ÔR‰¶ý^@ÕŒÓIV—(®ššÄ§ö¼}üC]¢ý.S† í÷,.!Û6§0´üR ™•eÅz_ÑOE?ÿ›ûù×µ1âPí}2tþ{R7²#׿i8Ì>ÆPç‘>à×~WÝ7˜Å\zNQëŠlõÚ\‹¨d¬Ëgî å¶`Òß¡#Ö尾ιM–z0ý0*”Ñ?Ún¸ SCiÌÙÝÜ NÓ©wx˜-¿`EÌ¥1Üv/ÒÈ ¸ÇМsÉßÏpÏäºBü“Vbî[UJQ¡|>¿jF:Ó‹¡ñkʤ‡ÓÆqí>‰¨Q¥/A%Ö[$]ëÌšp€~žÊЙ ا1׸B÷W¿=M4OƺÉ"—„Š’¹]užƒ®ÚŠMO›ç2Th¿Ä7™¤¾õ¢¨óÒ"B{%¨¢ŸŠ~6ç~ÏÅD4sæh4Pý( u1£÷Û,©ÆX†îª™½\:pã÷JYwíãMû}œ¹Ô 㪴‡¨V™ U^s‡dªàNŽ^L£‰ M_?$Þ/ĨPþ™3®ÒÞOö¼æ­Ð?̵èÜâk¶[ é‘ÖPUó]ŠæËæ“ƒw}êhˆÔwÄíØFMân¶O††ID’‚/ÉÊWÒ¸Q¶÷È2†æ ©‘½xÈMÕ7'ÃĨ͎«D´yå(´ð¼«ýÀP–~•–Oñã Ýߨê±þèì!E$ð@&7fÿFÈkj®WCDÃïqû£–NšºvôÖÇCD+8˜«è§¢ŸÍ¹Ÿ¡?’‚'Ä螃m õcwÖusˆ« èåöº`¤Émµh³¿Ü–ëØÕ–™ôÈmúKœv>`貚,2èéî¶ù·¡±wŠ^½©"1©ÏÊ_¶­ÓÝš¨Obkr†[Rà> -¥èâ±Éà³vºÝ~&— kQDQ·ÇÈ©Ì Ú×ô何P´Í=WhJÚÁÊçÝÓ‚v›}ˆ¡Û-CèÌ'¸ÇòõAÔ_&CÆ/ý„•ÌšNg?WghïO”Ô­ìMP¡ûþ÷‰~^. N•Õ¤­+ã¦_O%–+-ÐØöf,ðû‚ í×hý‘„Ô^”íî ºv÷¸Š~*úÙœûynµ1t³îBPgÇiÐæÛÀ™uXéRTj·€hµê&Fé¤Î,Tç• }P±œÚˆz‹Ñˆä,21¬˜¡ó3H$ÍäN|[Ñ‘O)šÜ>$qÓ¹Bù§×b4XhÍ%MÖrë5n7«›`â“.FÝû}‚¼Ô_¨†û~’7åAƒg“É»­-yºNf0E‰ >ôa“öds…òµøçs8.—¡O~Pï”ð‹$Ê›” ÚZ}T]Z&F%3Z7§Ýjºe#1Öϧhih.ÉpQãzÿÒdcóÊ *øùî¤ÆnØßTêÞ@O /ãžø ,±ØvI†Ž?­&étl!E›©Ãùû­Í»žâžlˆgé¯ûrE{Yèâù\¡|m;±‡c/1T ÙOƨ“N«§q£3²áûþ5\‹¾!$: Ы®50öær®Ðý¨îª`ØX踯m@b]ÂÍÕ<Öäéq÷lŒbýWhÿ¯YC`½õU@‹gM€óíã*ú©ègsê§üSëÑv*?W_:‹é<PùyŒkø\•¡òó}Šˆ ·@åç‘Êl•U@åçBù·ýºO]ï*?ï~KIÒF´‹¢òó]^ã@KM ¨ü|B§…¬ÇC@ÿ4_ØÐÌôm.Cåçgã΃¡å†Êϧ ɃIÓýÓûžG;@«6w•Ÿ/}hÍÖÍÒôO÷?:× ÒvôôOßWôSÑÏÿä~.¶ýŸÿ¿lÙôÌ£¦3Ô¦±®ÛrÛ˜žgSn–}xÝíȽ T;gÝ!¨k««°ù¦·Ð>²¾§¨á×ÛúÍ€¡]îëA̹ğRŠ å?YúôG±@#ÚÞÑÅ~£Ñ5_óaxØn@gÛžƒ ³r'ƃŸn{‚&Eƒä¨© RI„´ûþ ÕÞç ³7õå å{²ò"$l8¨³ïU()?ÌõíáLDÝoËÐ%7Iôµn>¹³»f( ;1[Np…©$y6Ã¥¨UõWRµÇJ†ž|êAL'T1tèµi„¶ºÌÚßùS(1¹-!¨Ñ½ ñè}ª€ ½¯è§¢ŸÿÍýœ/z CJúìÎKhœåȵUÚ3h5EË{Ï¿¦8nª}8èÿøefž¢žÁ£Ñú÷¯`’új†FÛ?gõa\¥O×Éxçg-×2†Ò'Ê¿9±â}vHQÇŠ+:ÎT†Æ¸ÇB¬þ0@£ü@…6×4è5¨Ÿ¾$Cõ–4€yå0ŠžmIòNkIQ­ø±Ïy€ å[ +ïôX@o$—@yÀ!î°ÉÁCj(CµÝ¬AdR:¸Ë¾žÛÌÐŽ=†ƒ¬{®Ð} ˆ~w^†Žmè Z4(ú¦ÏH’¿'™¡N—Ï‹ ºp¾%)ÓiÇçšÝq/õvLä*ú©ègsîçÙ Õ]º˜¡NðbŽ»q嘲î4Š6^n Ñ:×dhè¾ÝÄf}Œ˜k:_¦»û9·!º:·ch · à{{'×~Áf®Ét}¿H˜ª‘+”ÿŒj.ˆVÆËÐN²4¨ûùŠë·ŒFTÔóÚ`ðÏÛÎÍÞ.’d~žJQ¯JIêk_®‰m€E¤è4 çÇL&¥+Þp…òÝS¹Fž‘€ª€“â=Ü®þÚJMŒÖ½KŸ¥Û,PÇpbžÞVŒŽ·â9 PÁþõúk£(šœ>´jܹµ Çi}7Ʋ”;Thû»ÓéÍî·­¬™&7UpýTô³9÷sñ•» 4Ò¡ùŸŠÀ¿b%÷ÒÎÓD"³@n©Ÿ\1A¥ïQõ+ÏÄè\[æqË’p§'Àú›ºëöR(…éÜêîÛÖN4Cú‘üÚãHPÁÏ÷šPdÒ¢òýáÊ®>ÜÂÝÕ¤.©¥5^Y/NÏ6 èè+*’{¯rC5@§ÓÊ -Ì©&w }vþ ‰½·+”¯4·¾NØèC¿;Ðj€#÷håÈKv¡íÜBúÞÞM.÷!׃ŽjY@6B9Wè¾õj1Ä,¥èÉ…–Pò+{鶆¼i è÷¯ë`ÎÛ5\¡ýý·£o—ª[¹OÜ¡Û ®¢ŸŠ~6ç~¾]†5-W2ô¦y¨[/ã–4­—Íô+$¨µ§ Õ+é h]`(+îu‡ b»ÃÌða/@‹KÚÀ}·(:nçâò-HŒz}ÛM?T5R4Ú`+Ë”v`¨PþÝ­\ ¾ƒ!E;µ ~¹í¹=ܽiÞu®¡‘srÚÍ­¹ZÅ75ª™p ¿>¡è ÿßä÷˜ƒ•ôì-ΫyCQ¡|c7\‡›2¨…íx½¹sï¼…“:{(Zõé3|Ú%垬uÿü­ŠFg«Wç¸B÷]=gÂ.•|Š.<2\G<厱K…‡ãíYq õå í÷˜@B[ž´1±’ Ü÷7WÑOE?›s?­?¦ƒŽ¿3CµüÀßǸµµ¡4Ýr! ÅÏciŠê.îiÓV5g÷L×(¦²]Ì}_¹Œ/BP§SáâŽ&º:úëq¼;Cß*`>/'ST(¿š›&4Þ(‘¡É­‹ˆ¡)÷ì!+V5õoŠ6 _ÇÔ׮囵 rFïçÖ ²…ùFs•K÷Pó¶;omÍÖo8ÎÊ7-0 îÉ45Ɇ­?t̹¯ÐPIQ —&85î÷f|û:kq{ðQºAP¡ûÞ.†¶?(êm¿<ƒ[3ôÓÔ2¸½l Ž=‹`RÏí\¡ýÑŸóÉ*ÏW-x@t†u£Š~*úÙœú)ÿ¼·Ž„ÕµV •Ÿ×?Œ§¯6T~ÞÔ"Œýš8Pù¹Ç¯±²)‡•Ÿk$ˆXÁ÷*?Ê_åèKªrî[ òó}GƒY¥uEåçý‰@ô+I†ÊÏw´vcûUCú§ù4¶ÀóÈ2‚ÊÏ ØOpQGQùùÔ m‰×¯£ýÓû•oÖÀùT]†ÊÏÏ7]èf èŸî÷Ö›K6®Î¢èŸ¾¯è§¢ŸÿÉýlZÞZòï _2™<û9Pý &$:ך+Þv²^?¢h}Ù>P>–Ãõ÷¬!6ÃdhÙPgÈ+–rufUtÉ"h媖ý6Aܦù³¡h¢Eß8]ß›˜LP¡ü)>‹ V§† ½Þ­…LU@'ô¡9*7Z´¢ŠŽ¿RÀÕý±‡¼õ/ÏvëKt~'r{¶8êî€æÏºó¿ïæ åsZ<‰[Õš¡ÎQÙó‰¢ÁV] o¹¦µ©CíÃÖ-ÛÑR¢üSŒ¦þ]îîÆºÿÀ' ‚bA—‡g@åŠÜaW£XTµ ™£X¡ÃX®Ð~“ U¾ã/ôëûÙ´§So@…ÞWôSÑÏÿæ~ êJJ÷¸ªÛ†XÚmâ¾ê± ÞO ¤¨Áp[°ô4á~4L„n¯÷pkU/›$†ÞëÓZ²Ïí‹ÚÖJYbÑ~"E~{J;vqtã—JZp…+”ßÿîâ§ heiô;7€»IO…<†Z&¶g ›ÎqMæ"~å¸> kÈù•C¸[t¿ÁÜ{Óu (ƒàV*”/ì¬>«‹¿FÑ‹ëU™ºS4—Fº€ËÄÍM£Ð!ü,wÍ¥í•/CW)} u**bTè¾»Ùq¸Tv kŽBŒÆn®ö¾p&Z»ЈIK˜AØN®ÐþÇë_ÐöÃWÚ+YƒmŽIá*ú©ègsîg‘¯ itÚ¨‚Y^ÏÕH,¤é]®1ôe¯6,ä2ã–œ;Ãú©WôÉùXÖ%±  AKö3íN t^Æ Ê:ST(_É÷åâº[*z9º4w÷Û¶f<ë–ÕÔ‰aæTÏ ‚ |äÂÐ'S؃Hu®ÐýÁÝÁ¡se }t×ÌúÜáÞ뚩븎Á; AÕ†+´ÙÀ‡°Õj9 I½+ I4œ«è§¢ŸÍ¹Ÿ’a÷¤5³ûZµµÉbò]®‘®=ÑÞÞ‹¡e®ƒI¾ëîUKo°Ù™,C çëCüƒbôëËW$ݪ’ë:¿7,[ü• ÉkçµÒ]4ÂÌ–q…ò÷[}fÛCÑ<ûKÚï7ÕЀMUÍb¨WÛáìöcÜ>¯v²\f舖óYA¯Ün݇QŸ>£ÑA5Ä{ùbŠ åó¶U6,$†¡6çuéäá)Ü6åI¬MÜ‚Ö~IgŸ/ɸw¢"Ù_Z~Ý©âȪö›‰Q¡ûæÑ]áD¯Û µY,Ns Î…ëZÜï5Ïɽ×d¨ÐþÈÀH~ Ñ^‡Áær®¢ŸŠ~6ç~š¬=)¶CPõŸäSe5A%•Hößkªÿ—¯¸ÿƒ®Qr&ÑI‘4bÈQq£C67ù¾è,@kÞ’ÍÜÅ~ð«°~! Œž®ç å_µ3 ì:£èc刎Éà:M`W“2tkœ [²ÂŸ›olÈæZõtyiÕ9ß›uÍÔa0C¯ ‚Sâ4®P¾†D_ê¿å4C;ÈšžÃ•¾;Å>m¨wãq–´¥·¡í2jrê³}º#–Ôk  ¨Ðýã1­@»ðCZ·»ÍÜLß—Ä%¾Œ cœu`Éx+@…ö'úƒÎ‹QÕ%D+·AýTô³9÷³.Μä_É&¨÷Gâ6ߟ['í"s%2Ty“­5Éä–öEgÜé èÜåÇi^€7âù ø¢¼›ÄîÃ8÷`®úQ ؘu‡ ©_tÅ>5G£Bù¿ý?fPtí*0LLæÎßkÅ4JlÛ~ SqéÌÍ}XÖVTMÐ*Í¿Èãb}î-ÎðBë6C퇽#7p…ò9ÿ¢fÊZص;KŽÎåèx„…õ¨Mÿ0æàÀÕ-ùOW•j×u3têÄüùsý#i¸~…¡Ù^ÏHÄù©}{ìܸc¼ë vž×Ác"ÓoEÐ\£pö@WP¡ü­€3ááMÍž%ßݸ:³ë¨ë°›\×dW*ÆÍ7ÝKƒ¹nW‰ñ†·ÜO:’W¥1 •<Cf4öâ å;çgÆâ3Î2Ô®÷Öàr‚;¶²ôN€š~ŸË”V/äÎ<î¸ÊЦó`…a%Wè~éè2b:[ÊИQ×HË´s\3ÓpnM Ùî…`2ç0Wh¿mUg¦©1Ðñý Y`ô^®¢ŸŠ~6§~Ê?ãƒHLб •Ÿ·Ù‡¹å0T~n;è‚lîÉ[€Êσ’kAë…* ÿknÄüºîT~.”¿íEc0Ùו¢òóÍÀ"éÉ"ŠÊÏ›. û#Í*?ŸœÒô;ú‹Ñ?ÍwH:›™~Hb¨üÜ3b[gæ¨ü\ïIY7å!Cÿô~ãÎ ¤%=ÃÐÿõõÇ•@ûÀ€þé~ýÚìÀª€þéûŠ~*úùŸÜOé uɿӻO IÐÊ ÕøK$ ¡%ÜžG·0ë%ÝíṓéÇMæN›ÛB2u†ˆûh…H¢->@Pï3KØ—M± =‘À¾Ûør]v¤¢Væèõýö¬nyA…ògßõbŸIІÛ˜ÿ­2îò™¯a}·å€&íúöÕ¸—•˜$;“¡ïg™²†º ®ÓŠ“ÒªmÅè™ÆÌÄPE† å˳ÊÖŸÜÂÐOú³iÊK¸çG»Â³oy€¾’y‚aÃI®ßˆKÐw¢9÷UÉøªü“ B÷ÏF±ø({†’ô¶l‡ wîºÇ²è33jw[™é°l®Ð~j·‚º(34qÌ^°UZÈz_ÑOE?ÿ›ûùèÁO¸¼æEëß68á÷ú?Fz­tÿ±ML¹u0×Mù(~£¾‚‡oSdèÚÖ{X`ŒCgd—*Úqo f« C²U߸Bù—ø±+ú_ZÐm[X¢(Ù$’hÔhsƒºþ€Kïr:¸Ç¦ê•ÀPK‘/›rןë;9‚i\N&èFÓýìµßT@…ò™§÷bák§1ôܧNlùÓá\ª´|ºTÕv=$×ïæª¿P’l•YD’q{ E…î7fŸJ&3´Tõû0–›î9…}v4ô‹Ÿ ½+€+´¿|mŒ*?ÌЊ›!õ¼”«è§¢ŸÍ¹ŸÖ3¿BõV 5®ýM/u¸ÛÝ\YC^  ovY³øvI\ݼk°r¸EY„2«î²I«XßPnÙ¡n,§ÞG†ÚD¥‡fTY¦Mâ‹fr…ò÷®ßÃÚ³®€.èÊ®Õ äÞ¨…ñ/Åýe[–JbÔfñvüÄŠ¡IF®lØæÜÏõËY§’@í×£mÿ¥P¾§" V»¤;CKÕ>Ó~·”¸ÇîyBÂÚu€>ëåGÜl¸@«Ü— ~½ ú=Ǻ¯|=‘ÙgŽb¨›y2»ç4”Ûò®9S]› è§mY­RWhÿœD;°ÓºÍÐWz`!}ÌUôSÑÏæÜOÓÃï@ïKO†–ؾ„!Kús7Ú˜±âíi€žëÆò7œâöv†œæMµè ¢Ò!£Qݬ9Ô'ÿ¦[•8¨ý”¡e£ˆÈK2 uÿéN Ÿµf¨Pþº áìHÐ@| g¢í¹'/È`š5Ÿ|t]ÈPÓ41Ó4ºCÑòEíX¡ín®X‹O÷´|ôD¢;ð/ T(ŸnjýE—„ÑC) Ü{sBü™¾€–Õ[BJ÷&‚.ùðLýY!Cw÷‰%—>TTèþ™i©ìa|?†>l™Æšvês;}ZE×ÿ,´*ù©¸SþY®Ðþice£ÊZûa—øÌãs\E?ýlÎý lS.ÅÃZzç><¸4šû¾â'•ü:h¼F6šÉmôª !Ëìê¯êIìiWúý!Õ#ˆ¢ž–#áeˆ¡g?.fJ+'s‡ö;ÆHÂmŠ åßz3Œ½Øé¨¡W»jºûuú^ˆî['C·½™ê_ʹ[æ$SKIŠÆ÷w’Z^<,Cõ_Êt²04{UfãìÂüý‰u§©ýÖPT2ðô…ßa\úÝìé1‚nõ…äšÁÝöú2±Ûãè¤M=@]ç$Wè¾½íq¶Ñ 3C -ÒYIX[nò§}DVhRlÙ„+´_Ô°tô²©! ]­#îrÙš«è§¢ŸÍ¹ŸùÅ0ùÁ†Î×¼ oò&pã ,iDíI@K~iÉznNäê[µ&Êm¸^OÇ‘¯Ÿ"¸îùn J^ÀP×pp‹Žå6®OeÞÏ•ÄèF“‰ìÓ±[ÊôV óÙ½н¿|؃Ô@tµ•uî˜C2âLšéBª«PÔòG7púÅÙ?”åçÎfh õ“ì|”¢Bù†æu'í´)šý×â=Y“ÛO'”è¯ÿ.F[þ<Ë ´ >Ú¡_¼ËÏä Ýÿq!©Ö(1tÄt6Źž¢î§O$Ý4‚Z¶|&Vî^$C…ö/§jD4¤ž¢ó.“F«¶\E?ýlNý”|ž‡ßÃ'1T~ž·ó¶øÅ²0@åç÷–ç’QûÏ*?7{à]I*?õA¶ñïa•Ÿ åw<»Š hqPùym["ýZHPùù§]ÖÐ×µ%Cÿ××gÄ2^eX šïàºdqŠÊÏ œï˪ˆTŒÊÏwÌ¿ú÷}ýÓûw¤3ÓîO)*?wî¹&oÈ èŸî?Õv”Ü]'Fÿô}E?ýüOîgÁ 5É¿³Ÿ °:¯bÔåüTÖhu™«üÏgŠ‹w Cµk²á¡ÇQ®èË^Ù>û¿¹C·§‹4‚¹:žKH„þlŠjøw-Sî•ýùä\Ï õžcOBºå åqx/uÉR4iŠ-5úýŽ Ó?¬‚¤O7k¿bËó¹G1Ÿä½ЙkýÙ*÷Öõ:=^öªò Æ?ßG ®P¾ÐËÚ@%ÀЫ ´a£ænçÕþäXŠ>Ÿtšï>ÆõX§Ä8ˆ[ÿiœkÌP¡ûY±¦´Ø1Щ.Æ´2í0·á}œ¸·š»ç ¤ƒ9Whÿ˜‰C Aÿ2C”oɤ ®ÐûŠ~*úùßÜÏA sؽJ­}åÊöõãjYÇuHchWã]à'Iå&šwdVB¹«f°žŽÁܲŸ« ,ÿ0E-€žóZ0ôÅÙëôìý Ü9'²”œƒ\¡üIešôóîb‚Æ“ù9Ÿàæ¾< ~QÙ€ÎÉ΂C3sG³nßS:Ç.ž º­è;™+ pž;­Ð|Jí¹Bùöµkû¿z3ôZH+hqs÷¬ékBþžJÑ~›;€þ¶|úî°'ŒUd¨éð1Pÿò Wèþ©y½hD] ×w ï S¹>]ZH¢;Q‚¾³ý>¦Q£Q¡ý÷ŽN%* 581‘¼7á*ú©ègsî§dûZ6w˜-AÇEnc¾C·s¬qÏ/q uPŸg7íãž<ÀšÚlåê©Ç1iÖXҠÖ1ÜËNCôêÝÜåÖ²zµv\»êý·>E…ò‹ž“ŽDP›¯jâa+-¹ÃÏï!Ûrí,¤~˜È…úhæõÜûýÌÖÁÍŸ{Ö½Ë}6â‘8ô× ®P¾/Å-ÀæA$Cß“ ‰Ü+±ÃÀGÝSŠÞ?4 ôçŠÑƒi'ÈúµOº«¯ˆ:Ør…î§jIÆŸ´·þcÀnúþkд’¢}ÒÚ®Ðþ!Nÿ|Owϧ¨Î¨×oÏUôSÑÏæÜÏ^;Ù¹øƒõ‹ b¿s¸w´©ñc轡bír®³u 7«¥hU\3Ü5…ëa| ²zÒ{ -)â>›@J.5ÊP·2#ˆìfÂP¡ü–Jib‡ÖoÄhÑ­$çÀU Ôgh*oÓtî„hhUQCP±Ë 6@;Ðé]ÙÜÌhîçÝ¿hýÌ7­?Ûµœ½P¡|ËO”Ãø †šsŒôŽ“r/ Ù¡ý*Åè‘cáàvDƒ mî'Ð4š¡ýú%ÒÜÑb®Ð}ýY§eµmÏ*ݰUÖ#åw‘«#äÅN¥hø Ðz°XŒ íŸé;$ÚsĨƒu Ø„Q®¢ŸŠ~6ç~å½Ìؼ‚ µaá¬è×w®Ùb%ïlÉÐÅâ7dbX/îš¾¾LT?@ŠFèÙ°¢y*õ<; ʽJêúîQnQÌ=ò¹„ôMâºy"¾XÁÊ_äÖ‡øµ‰‘¡µó,È€‘£)ºªõNq„ kB`ÏZ[n÷‘×iàÉ(@Û˜ö¢«‡pgj*±È+2nùíâKïq…òyÍYMZ6å3ÔýÑgñ+ZÌ}6ûx|hAÐ*Ê@?hŸ}UúÈÂxF4Eó¿k‚y¹Wè¾Ýt-™~Ë<@ã¿]» Õù ·ê¸Œ<«|GЬ •$Ça Bû/º úš24ôÑ#¸©|„¢Š~*úÙœû™Þy?+uÓTk}$ ðù‹{¢ÛòðâkŠŠ¦<%¦÷¸Ä¸5ûá´˜ ÑÉ h›)\¥q“‰Ê†æ4Œ^V½«Þù)uz}0 ?[Õ-W(ÒÆqdYNEuÌ ­/åF^Õƒ²…zÿùi 3çú,Ø)®ª^ hUî ²Új·çæÏdâü3ÜO%&°Ýɉ+ø÷[Å› úýï0Tú¨55zü/ÝýSÏ¥¨žÉ3pPíCÑ‚œ­`ÞêFæT~lôOó·ÝIwÿ,a¨ü<Î[¸0(–¢òóøö)—ÀÐ?½? Q*.ñ,T~¾ùv<{’èŸî÷zƪíg蟾¯è§¢ŸÿÉý³BEòïLj=”Ù©74§koæwöwjò3˜²d)C¯WÞÏ™ÜGCÖƒÕÒ@Ç„$òÃiY8žŒõ›ÍИ™“Iì(M®NŸÎl榊ŠÌv6Ñ|/C…òçÉ ”ú´ƒþfwz×)ÌŒXÇ]¦¨rJ+r/§C=f'‰ã\ËígtŽ´•p×Û^®ÁÑÜî'*àª_o®P¾èCrÕó* [í#¦—ЏöËõAëû†z·«³­ÿeYzâ)÷w7-¢j’EQÁûW‰éܵõË)'?$ë¸b·º2Ôn’ 95â&E…ö×KÓÀ¹c&CïÛ[=¯r…ÞWôSÑÏÿæ~Æ:h³Jño‚^9Ñ’ÝeÿR²¢ì:ÀÐØgaÚ‚Ô^©Ù€.™ú6-ᆄŸ"gòöP4®ÿ_PE_ÊP3¯7dÓGŠÆ9ˆ@y` C…òO­µg7Ú¤´åQ'Ö÷ânÆo{±ÅÑ‘ õ0; ‹ÜìÄõ Ò‚±›Ïª>Ö VÍ>ĵUÍõK;ÅèöŽ õEM† åë!¹GŽ*ÝÔçzkpŒ/åz–¨q™¢öRf±ˆû:W ŒÔÖr/yÃòÛ•\¡ûq:µÄ#ëoŠùM²Ý}¸©­Ÿ‰÷2ghÉÝW²Æy^\¡ý‹âÁ¤é×Ú9åékF¹Š~*úÙœûY«ñ‚êùEPçø|º~I×âËAxsš¡Ý½|aä»L®½ç'è»´+ :/>]‡d‚ž8·r¦üóÙôÿUÖ™‡¸¡d±7e uýdÆ:e§r…òÿ8»”½úÆÏÝÚØ™;3d'ݸ{Cçiÿ¦·žøs#úL‡Î§·:¨ÈæusÏÚ~"˳·ôîÇ›¤àãt@…òž;bz–êV9ÜÞßâžÝ|ìâdèÏS}ÁÃY_Œ^ yž+úK´ÂêNp…îoßÑ š廊hp¨º¾›Ûªÿ[šö~7Có-b=nlç í·»´C¼üÞa†J>µ”E]^ÄUôSÑÏæÜO—N)ô¹N=AÓƒhÉ‹7ÜGíÀ'àCoúMG¹ö­_ƒÏ#1êî\¯Ï¦èP× P?ߊ¡ï /@Ö„y\Y^0›ÑÕ”»v§-sÒ “¡BùV³=Ùƒ57ó`U[&r›~«µóeèdGo¦ÛÁ•;Lu4 ¼ñ• žãÔ!\õwáéù qКëÙ°Ò2Š+”oiúRH5+tÐ>_8t'ŸK;I ù£A­ÎƒB­YÜ™ëÚBß÷êhû[ªóö.Wè~Ó¹n04qE·1„Ô‡¸ï³£˜v3†¶OIfC£3(*´?/š¸8ªCýî8“ÏÏQTÑOE?›s?Ml¨¦Ïc‚z9u Ã˹¡[Á¶i:uÂòñK×¥ÕqxÔë EK‹ü ã¬c\§õ§aÊ“C =óf/ä˜]äj ¡¢2%‚6Fxjãíʯùlë×r! C^-g9“½¹ym˜dÖ(†Ú·câa­¸/–G‘‚žÕ¯Ž›iúq'~üSž´rãG°{™(F…ò=í•K åM±P´'‹«Òaü8HÐqgö‚ú­ƒÜÃÑ÷éõ) Ö¾-[÷{WèþÀ.ƒ¡àX E/€òüLnÒå$–7¨Á=ôm7{ù´ BûÛùÔéš¹½?g*Œ_eËUôSÑÏæÜÏŒ=§eÊoÎîxu™û¦DîûÇYÄoÿz†ÞÕŒ"ª)¶\úÑ^Ý´¥hÁXC¨ªP“¡ý^O»!·j÷÷Òct)Wu2t—÷ÑÁK4*1Š+”ß/w1û¤± Ðê–^»Ÿ›ô!…UIŽQÔÏã8;÷ø® }eÝ êžNPÝ€ôÚ¯Ï܈œ‹°¼:¢W,–A¬'å åÓ 9™Çõþ?”c¸ “`‚u,AÇÄç‚Ò– î'Õ«4aäŠêþó}óPg2Wè¾GG Ô›]¤èËg“Àíù-î‚væÌ,HСڧid\[®ÐþA…»aóù"Šn˜{®%g¨¢ŸŠ~6§~þ¯ç—Eú²@•{´býI‹1Cåçnm ió8‚ÊÏ{L×&iO¯2T~®êÈ,F.c¨ü\(¿cÅdæã”¨ü\ýÿ±_çQ5·ÝÿÀ“)É” ‰2‡$©s®B¦ˆ$ÉœîÌ!•T¦JÑ$M’2Ô¹®(dN¦L !nC2…ð»k}÷~Ö:ÏZÏgùïyZçóÏë½>{¿ñ>ê\Ióž6aè¿Ý¯û˜ßI0TyÞ®+ˆh+Gÿ4ß²‘ ¸úÏçUž[~¸ ß¿X3Ty~áì[ŸzŒ£zß­ëh}ê1G•çѧRŒÎ0ôO÷»+â¡çþéûª~ªúùßÜÏààº6ÿÉ#:#`ã¹¾·üŸásdPÏ/•|×ëx]å 5¿]g tôà›°Ùʗ}š™9•ôðÖQlGËÛ丩peë5ŽÎØ©AÉæ—€i_?º]Ž‚f)¤Ôýçë\àl£M€Útƒ¹S‚È'Ö™0hɲõÉ[´†”Ú?¤ZÀ›.ÎmnQ›#5ªê§ªŸµ¹ŸÏ5†L½=­ö‰ ÈÏ!5U€«foòÖš2˜6Ø…|6¬Ûpˆ\j9E$< #¿r„-–‘=Rõ þÓ02±›K®¼ô9èÁAþÂv)•ÿ}«$ð7ß¨å¬ HÒK 'l?[z¯è³í[áR?Ò¶¯L®ôg¨ÓÓlæŸäGfåÄAý[9:ãtÐxЄ”Ê—ø~ |Žñ5ʺQØ5, Ûu]O”rT=ÞL¯’Ÿt„oj0Ú‡ùvžGJÝÿ½ÈέôšÞ<Èï¾’\Úæ)xÕÓ!uÎ܃É˵*µ¿J~~æx ´î–C°ìx:©ê§ªŸµ¹Ÿç»^g¹B ‹&îc^MòI]Ë[0oøÒ}v>T8Ç’úFþ¢ªß²,dƒ¨xõ„£åÝÞ³äæÍjtì-KÇѶZ<º –ܽì,³ëJJ~? Ï£9a€Þ͈Ãáþdçß–²L »7²OQ/–Wÿ4C ’ä î4ôÌÛPÊÐI½`cµ Rùܧ8sïí>4¿±´Øj ÑU»8ºáIy%WZÉ }$µwþb2µ¤Ôý¬® !µÙb@eQ‹¡ëEwòWâqpwÈѰø°yDJí7Îô“½çªÑ¢|wTõSÕÏÚÜOxæ)Î tôÓþìÖí dă`9ì¹ùìvnpмÿ`žðKÿª@·™ 1™r´ÜÎŒ&e(Цs ÑmK.j¹» Ø"Ði%ýÙéÒ½¤Tþ¸.§aóå¹€ZýÊ:Æ¡%ü®"PÃðJ–¿Ñ€L¯ßE¾ÃÕPW§}òè²edúÍóñ¢!é¤ñš7*ÎE¥òíy”Ï?Nh„ÅcÞÇfù´´-ìo½JÖójg“·ÈÑÀ .P¼0ˆ£V²8û¼³@¥îž²:»9zj¡7TŒ!£ÍGƒÇç-Ù`‡_„ËP©ý>«Ü™EËBz…©óÓzIU?Uý¬Íý´izQ?ê¢@ƒe³ .‘ÍCÍoNz¦ €™7äô„<µ@ÆÐ¢Æ¾†ïhlQ ío@¾øøˆ]ZZÂÑm«¾2Û6>déãÑÌxÒN@ vße-â‘e–•°p’\ ç–î…>zɤT>~õ·œ¿B Ñ5Ü.™ì¸Ñ¦XÉj¾È Rý·‘»_‚åÊí]£Ù\»R÷«/yCÃyCm¹è«­È¾ïôÁó@ C»îãZ*µšS?ª¾^ ²9)ÜÕ¶©ê§ªŸµ©ŸÊOX÷…EßKUž¿Ú]r³ª<·1y•«Z!G•çŠq§Á¦[*ÏŸ Þ.Ò›ž•£Ês©üUŸNÁÊ⺀þÛŸï\[ˆÖ¼®@•çÝõ¬ ãò\@•ç ÃŒaj¯Dþi¾ê÷¿ùö™@•ç]ÍÆ‚Ñìl†*ÏKíåöwú§÷&,v)¦€*ÏcõvÀ‘AýÓýoÛu—£ú¾ªŸª~þ7÷óÞ¶:6ÿIÛ†.à¤S‡¡±ÏGCá»wr´ûoÑãÙ^ª»@dên ýêþ²~4 ÐΛG1³ˆ~dÍþ Œç &Kvij |-r¢ƒ­XXz–£3³¸_ÂN9*•?Q/Hæ:i¦ 5Ú¹%·ÒKCnú5žïqÈT½~²Ü²&¼™öŒk¤“LêV‘e"oõGŽN~—*d|=C¥ò-Ú®ÃÆ~è$ШØésMÉé÷/É·¿zÃÑìÊ~Œµ;MÖ쀭þwÈ“W‡ƒÖ¨=¤Ôý!žŸaÁˆrŽ–µþ/¶×ÃN°aÞ×ýëät8Òÿ2)µß:n­ÂL÷< ê†ç˜÷bRê}U?Uýü_îgÄ>ñVŽúïé•?ÉØÜ`qú¼‹@÷õÙ)ú•“fÇØÒo Ýì1‘íŠK"#µ6±IËÓȒΧå&ë,É0›ur§ìÇ T-yEŽ•ÇŽJå·é½JaP§@M×¾QD®yAV<Ïâ7Þô—wo˜èrô³ž&.È‚!» lÌ!Ò&ÈK8/_G¦‡ñÐËé¤T¾¡›¬˜Üü+Gu¦º²ØÏ·Hµþ[YìùXÒÛêëN½ê ‘ù ´ó‰á°ÌÇ…¡R÷×m­7[MZ¾…Zzäó°}p·2 PßÅ…ÐÄs:)µ¿c#œjyPyd ¤nÌ$UýTõ³6÷3Á¢¤—5ehçOmaÈ».dYÚná¢ÿ€£jIâžÌ† ó+Ûwr`è´aqŠ+ïÚ×Lõy^«qdîù n‘›ON_ÕB¬ùÒI qó·¯E5 T*únKnÑŒ£AÃüù÷߆äãŒ%0yL këí€[?÷“™ NÃrMd¹Ñ¨ž;„l°ú3*ÛN÷k Ó­º‘Rù\¶„0»5'8:9x;<8‘¼lñ“%ÔßE¦ûêBì‹“dãG1Ð18€¡7-ÿù,•$+P©û¯ ^ƒSJ:¼„¸º½H¿‘W çnC›ÖÉ…÷7ÉQ©ý™Ï`ÒÈPÿ3a«¬©ê§ªŸµ¹Ÿí´µAã»-Cg5‚Eeîd¡]œ°9Ö]Žö=ºMÜ|¶‚¡ñ#xå–Éä+½¿¹Þ¸ä¾Ë·ù ó΀¦=ºÈÃÛ.%?Ÿ‰iNæäãÑêÂvÒ!R*ÿ¡ÊýÜævŽ̹Ë?ýÒ&~„•Ã9,*p#=V‡±'*zsi.+˜A>ɿŚ.ÚOž®î'/¾!H©|îá×Y½Yáhø}×ßB·ï_–‘¥õÍ!ò‚†@{¿¸ ƒêÝ|OÂð¶ñ¤Ôýöêå𢫥@lÊàWýÁ¤Ýàh°?Ú£ j\ ØÏ†”ÚŸ™ ±™ ÍŠK›eõrQU?Uý¬Íýôªsm`è*»¬î¢òS'?!ïyƒì¹kšø²½ &)õD—]fäÏ!å¼,di¡;…gò=äÈt]vxæ9²ÑØHf;8“Ìjc?㦓Rùç?©#ª‹Êè⺭„ÆL?òBi>Ì.2TOCQD6CCõöƒ_/šv(üZüP aq«Óc¦ZmžÈ^ O#¥ò™ê¶„ö=£8:ÀÂdä¾$kpðî"Њfr(ó´#›L†ª!—È>‰%N>OJÝo*ÞÑ£Y}2oM"cš@墘\´*»+¼;kÇP©ýcÌÁ¦´ZrŽo‹HU?Uý¬Íý\uí#ëœÉÐ*ó÷,Àº€l¿ÀB”uìèÊÙME«ÛfdÞ&îÓ8™,Ó*Ϲ³ž“ïüô þÐURý¡ÄGæ‘ Åí¡Ôê4Cïe­c~¿OüK‰üœzµzÖèÔæÖÂïö9ê5츺¶ghó^ñ`£È’¡fæ !2ªG×—ËàŠîaiçG;´ZíxwZ_É/ƒ, e½ù½ÿ³3ì &–ÄòÑñd±ëWÚ£)u?lÉM0>åUÐ^Ë‹¡š/Gk,X”èTy~À@,|bª<Ù7’FGqôOïH½­|ª<ïT” 1Co0ôO÷¿|0âÇW3ôOßWõSÕÏÿæ~¾íýþ“sÝ‹tϵß2F̵^IVNå KCõ½QŸ¿Nø—YYÄ0§²Ì+ˆ§Î˜­ YJgVÉߤ&Ç~yÎ:¼|e¦@·=Håå)d/›+<ìú Òåigaµ0Ì;°Z4v™¨Iœð;Eͻ̎=Jdhbƒ),%FP©|γ«àNµ7ú…ç¢8jÓøš¼bt¢@Ówä*ºh§’ÁïŸ(ZëêúüžÜÁ÷C¥îéåÀ¶,8+Pƒ8æÓ2ŸTÓ?ŸópñQ9jôx·,í*µ?Tc˜ïìÍQ¶Èªêï&UýTõ³6÷óÎÙ9Â`HìŸ/˜¼=é¼+ž½ë0 Б̦‚‘-÷öÉÉÈV£aµû2¨ð"D™ t]ó]px˜'yãRÜÏN'—•tc51×H©üÙÕ×ùy;*ûð‘/%­ºÃZ¬!½›,G~ô'ŸüÂg: h™i}þ¹ €¬?¿œ™]_Kþó[ |ݼ”Êw)¡j†¾ãèæÒ×pfoGj¦ìäŽ%“Þ›ÔÄÍWq¤Ú?Yª}±­,ßÂË–ž"¥î'Öœ”ÍÎheU3y°Û¿,ЩÇ-ÏmäèmW¸ÿûRj¿—ÿ6Ъl)Ðo-B¡Þ˜y¤ªŸª~Öæ~. X(†¹6¨ö¤…bfþŽ2E c¡F€ŽI½Çúe×%ëÇŒ†šú n&ú%v:86["ÐÉÛu`F‡ äǽÄÇÙ1ä²ù‰•5‚£Rù'Ð7ö¯èßIÚÂâ² ™wØG<¼ò€£ŽrOá¦cBÚÛÿÅNºè„BH¿Cž|÷bë_Ö€×” T*_õ×'<ÅA ×ÚÞ¯^‘k­ú ïÃÛÈ1sfŠÂ _²¨e#Q¥‰¡cYaÛg. R÷ýÚ¥å^×ȨãÀ8E»ÉgÉ¡“ú ¯ü ôì5wqZwC¥ö?úæÕv Ô̲¬.Î%UýTõ³6÷“[Ì»+ŽsÔÉrŽØ¾‰|ov‘¹Þ¿ÍС,áðarVzs8lEÞ+¿É†ŒH&/öºÅZ¾ë(Ж)…,pÐŽ–­l#¾{-cèáWï„“RùÏX´š— Ô°‘(Е´¼8R$ú—£k«;‰ÒgÎ -³r±ÓS”²–} $;Þ*†ÀyS:ÜÕ†Ô!¥ò½ ¿Ú­6 ô]v6|K¶™(œ9’yÛEbZ;²CxK>2þ ?ƒÔaõ¨+¤Ôýsð¾Žgjü‹q‚L¿ë)øõô×g3‘¸~;)µ?/Í^4ø¼@cÝæ™«Nª~ªúY›û™±l¦P7·ã¨Y gáèòQ:^_ÏŠ‡Å1têÉn¬âÜNòà6Õ¥Œl;o'kkoè¡[5ìݺ0ŽnxмN®'ýZü²¶YÙ^:æòXµ•üû·i+†Ä4¨gëVâà£rŽ.ýû ÿâ”ÀТø~hér¹ØÃŽÔ£ïvˆèS—|“Éš~ó¨~;]f®Ð&¥ò™|K‡Š46¾½ ‚ì“-ÕDGù%ŽÚIu’º’çöDƒñ•,@“üoÀþ‚ɤÔýÐ˹Ž5hÎÊübï\ÒmÔv~ô!´Â}){z”ÚÿâÒhÑ¥S„@õ‰ÕúV¤ªŸª~Ö¦~*?1=ÇŠ°{ª<}a§\ç[C•çu¶²ÅãUž˜;¾Ÿâ¨ò\»åp±ø`C•çRù‡î'Òƒ¼åè¡O±«AC5O솠£ŸÉŠÁ; ùº©dUë¢dý6òçó@ÑüÊ0@£š´jê¬Q?Ž|Ìb¨T~ƒíÎ,³&£ÚËW0Ó”ýdäÎ>ü¼8ÁÐmö3y¢½: KµÇŠçsºùnafSMÞ×7ƒiËÚ Ô uý5ˆ”ÊÇÓ× ›“f²{ƒ˜]¿>i4#œ– åè÷‘9Ð+Z?š¾µÊÉ tÑ LXÜU R÷½¼êðªu½¹´>ÿ+Pƒœb·A¸§'§ï1)ÍâH©ýV çƒÚ§™ =pz”ÝoBªú©êgmîg¡S:õhÀÑŒ&àÖS{²úÝ0Ñúï }Pe$|}›ê0?Â’Œåè›vÓ ñF~.j´ßBX½´ò›?·µ=Eªõ¼gm¹9T†æíã…ÁA T*ÿ¯ùÙý ¹½Ó|+;ðù×|gÎ/ö´£fSÞëµ;™Qv’WØè‘Ï4ámŸ´"{\‰ä‘Ww4ª‰Ÿ˜5€”ÊwþáFqÙ6“£Ù)A"´yw2h@ì]¯-ÐQÇA§î0òÌ/ÐYíFÿbU©ƒI©û²Ô†<·us@Ï—7äöo[‘æs6­’O¤o6T/9¦@•çN ÜÀ¦ª½ UžGO·Ë+£ª<—Êo“>FîvE.Påùªc7áá虀*Ïm›Íã_Æ^g¨ò¼ýâºp³÷~†þi¾P×gÜV³ Êóá›ä‹4/ Ty®Øë)î]%Ð?½?o{œ"¹Þx@•ç¥êÝáh·— ýÓý#ý³ YÚ6þéûª~ªúùßÜÏeÍ?ÃÒßû-ŒÓ?!G:¿æ.U¹h?÷~þÆtŽhk*š¶aäb³ólUlÒË#K;÷_¾®öêšF€ÕP˜Þò&ÿòûz%ßZܾoGJåÿU‘Ãn4蛯¸Yz}òŽÓ6v c é·òXnªÉ.²zÂ*µ×Ѐ~§yñcÙ¼“‘ük·^mT£.l›èRù€û“6ê>ü«5Xì9ÆÑn; ¡èi1©ö}W޽Q²uÜm¬8[GÆQ©û…!ùîµö ·ˇ›ê‘þDqý{ŒœÁõ •Ú?¨•\s\ ×qVÖ®TõSÕÏÚÜÏAÓÀ¸3šÜ7 Êzy‘—Ãnp·´C€ZœÓäŸ}8©¡y‡m9»,Mî6–‘ž‹;Cï5ú*} ,xVÐs™öмôGÓêÁ¢¥•Êo;ÊChM{ÎPûÍ3Ä%Ñ Ð#Z§Å ¤f¾|ñrA¬” ü[†–ô‰C4rÑ­‰A,#¬’£og³ORùÞ§¥ÃÂR–ECÙ,3r„^GXà^ÃÑâzíáA“V]0õŸž\ìÊÑ–« §Ëº •ºß5o"ÿq²‰@ëX9óC^õIµ/e›õ bëÂìVýf¨Ô~½žyÕ¯ÓUÏ5wÃÂHU?Uý¬MýT~Z¤DA¾Užï74dE Ϫ<¯ì2n ³Ty¾bò6ÈH.c¨ò<×t3/öß,På¹T~µòñâvˆ H¥Çsm6oÄ)@•çfm€Ú1oª<ï£`n¡Mú§ùŽ]Üû±AÄum¨ËðXcÌÐ,ÛÝàw±G¥ö~ªÏ« 'GÓ\“øá‘«¨Ôûª~ªúù¿Üϰ¤¯<Ä)Ž.µl$F,O ÝdP÷e* oõ ð@2¹¥ßs–úuù¬Ñhè6w9{Ü3…{â\>z³“÷Жôx “Oîtâê·à®ÄP©ümó=kÂúÕÔ@4~³žô³6ýÔ åÆÜ$м¦õXñ©}*C×uUõSÕÏÚÜÏ] ]ÑÖÀ£ûºujÙ2òÜí°i4 Á{ÁúCi\—OÛ’nÃáö˜ †¦&¥qû£»9ZÚn·i_¨@[χÓºäYcö~)•¿Scs1óéL.øl'6ËI/-SÐ8sŠ£.wLafú2sÙE¨YO@5¶^’'ôJWÕåÞ-óiqQaœHJåÓ[> N.? Gc[Œƒ"³ñ¤YGYLwŽªìµ®™Ò•œú)Œ}thê{Æ;Û‘R÷ nz ûù Ð}¶bhÓQä꘦`1$M %/+ž ;HJíŽMà­öN´lËgÙÒQ¤ªŸª~Öæ~†AOQoësš^ÔO¸F6&_ÛäòVÓõ·¿s5Ú“ë>o‡+2To¨ ]•‹zvú­°ùþÑ5MÒST$ÊÐæC¶òž—g 4² ŒU8rT*ÿÉóNB{NkNþËELÈ+çhR׮мG[ö)ׂêçŒl{á [ôh ±N]¡0p#Y1ÖO¾Ì6’¡·¶–ëæk*•¯—‹=˜†Ö—££{Ú@Y»2´Ìá`®óvKŽz^Þ¤˜çfKž Œæç3ȇ¯zð¢y^ Tê¾é/g¡Ób* ÍÇŸyždßi…å‰Íý}q£HÈÒ&¥ö›XÔ…M÷º¸Y´ðˆ#UýTõ³6÷Ó{Ÿ•pô.G]ãAd:j14ѼÂÙAòS÷ð¥JOŽ>3ŒO~R «êÚB?mŽæ•ßÎ=YØ.µÿxAa7ŽŒ®Ó…û˜é2T­·ŠßøSŽJå?¢5S}Žæh¼"èh;Ò±[!Kûì"Ð7ÑæÌ»‰793|-Ô òô×å}ðvá8²]ô ¶OîJ&e@º)•ïiw+h>¾\†>¼o^Fzr´¨§ϹiÍQ˹oŸö¤Í¾ï¹¦×†å¢¦¯V+´>ŽV ’ÿ¾¶âJ‹¥€†¹õÛ×ù‘'\vЬNErtëì"5ÛP©ýÎBÐ 7@Û¶­€“Ó UõSÕÏÚÜÏ­~¶ÂÕbCÝÍ §¸=dn··P·é&Žú&?…%­µ:o?0ü¬@ãwZ@Þñ|²bÏTžX"G ËxÓá µuy½T9ÙÐÕG*•òí)"ñ×ò\4qÿ¡µùu]”¨ðZ*ÐÞ“Žòò wrŸå¸þ¢ õO”£ï9Új`¤ˆf¨é–ÐøiJåÓÙjžÂNŽzº „}Û–’QÜKvQ–Ëåy1#rQ˰¿¸£c?9ª6õ?Ù)Ÿ¡R÷ì&’uƒ]ÑF ÛDN˜YÍ''Ä‘Çh².s²H©ýÆoßBbŒ/G»F߀éÆ TÕOU?kS?•yä n~—¡ÊsÙçë>`²@•ç¶_íÀõÄ+ªƒ9÷pTy~Dm.°‘Çú§ù\{¬Ð •£Êó«³qÇ'ÅrTy~úš-ß0Ð?½¯ñCSX´ Tyþ×Kø\?Ð?ݬ%-v ôOßWõSÕÏÿæ~fÁøOþÒóƒ×s’8ºl°LÓÏ"59¼¶\hç7Á,²/ùpçáVÝA jQAbfRŽÍ óZ-¨kã æ:2Šl¿#¶uî¨û—x£Rù3^výÆÃ­4A™¿=YÖº*w÷–r4l¸¢fvSÒ øô.79z¬°%·úËŸ¬Öµgë ôC€¢_H©|¶^žb_“p†ö~á*ôK>“…^Ë;ŽY,Ð’UÉŠ¾ž É·±…Pê2’£Ú¦CK}JÝÕ'‚-} P6Ù¬÷ìãdÁª1jà5º½]€`kZ*µ?õFë=ü$ I[gÁe³Rê}U?Uýü_î§å8O`BÁQûi³â©«ÿLí/3´ÓÐç`vþ¸åÅ…ÌQ†š¹»¿íS5ÝÖ[ñå{Œ@OÔküHï¾à¤yüò`1w7Ë$¥òg;O„Û^CõÑ]iÛdd‘öNEIg9šú[ñÿŽ¢‰­.)L²[qrÎ0EÉÓ3 4eÉØzàG½fw…wC*•oë%Gá¾ÅÐt KQò`*éûc+·¯š!P¸*~°iü[6vÍ&Û¦ðU~¤Ô}ý)÷Y××ÇýUü‹TýË‹u…]½²²ñù¹ÝGH©ýWc÷‚öw@³ÏåAÊ‚« UõSÕÏÚÜO+±ðé¯kݪoï_“º«Ë OÛ©ä§17¡bÜOÒç]57ûš¨WYñËÓd‰ú"ñü`+Ö»°Exžx‹V¼^!Ü\Ë9ÊúÈÅÂzm•ÊßîþxtØP‡KÐY‘•¿ûòɯ‡04.x7OI%]GeçñØ£@W¶T”ŦýËÝä‡,× tm®™ØÍ»Rù®lk%´÷:¨ñc^|bÙ+¡«°[k$ИI¢ÏcŽN^-Œõû´ëðÍ¢Ë\O†JÝw8Þž¯9èÖ:ájè12ɼøüÞK&¾†¾y“RûMæ‚{èAÊÒvË…o8ªê§ªŸµ¹ŸFÂÄÞ»ÂQý`ØÖ³t;¢ %!;š±,õy8™þ»ŠZÊh—© xœQÉÑ·†`¿"ÐákOÀ®žÉWÅ£!µ$IŽÎ·¼`.G¥ò7‹8 ZåËÑ—{ÏCÅ™ýôð+ë›hÑz;Ð’ÿ˦§Â¹é‰:¤#˜ñSÃY²¤ºîJR nÅû•Ê÷fF{˜vç4 çMÙ?Ÿ%t‘½(˘DÞyª/t-BÈÌôm v¥Aj`:Ö{qTêþè50n~: åã7‚ls*yÞ­œëÙJ MÃsYËÄï•ÚŸè;œ¹O-äè4»QÌÒþ©ê§ªŸµ©ŸÊþ¦fàÝ6Ÿ£Êóô•Yrÿ_AUžW.Ý ôíª<Ýqœ­’ª<÷dN9N Ês©üŽÎ…ðëG•ç—ç…À¥ž^€*Ï££uå/þTy>ôËVü4D šo¾ÁFð8œ¨ò¼¤ú 4; PåùýsÝáRŸýÓûjYÛÀÐ"H¥çrB"Ó}™£ºßø s âýÓ÷UýTõ󿹟æ /á?yìf÷¯Ü èfc#®Ù+‘üžÚJ¬¾ãA®|z†ÿú²•Ì-wÇnÊÑÔïÃ…,EP¿`+¨¨ŸÏЕr8°s9P#ªmh™{¬õã¤T~þ@/ Š¡'kžÁuÃpÒ¤l3Œé³ ÐÈ}'¡yˆùbâHHŒj—‹îí<J>ÜS Õ?ºˆ;í9ZÓâÓëC¥òU\ÊE =Ýí hbE®µr‚‚µ÷èàSÀ²µœ£[íôÅŠµÏå¨ÁË5üþLM@¥îl Qk.sÔ|‡̯)!mfòÊ:Ú@“Óƒ”Ú?ÒI~ð¨©[ ¼J@J½¯ê§ªŸÿËýœÚDOaþ- Ð^]³åAÖÈ÷7+ RSÈ_·m˜WÉ!²ª¿ºÈäþd–«âÝìtòòœ`Ù¶1COO OgÊÑ´jCáiPÌÑèç÷øäöy •Êocÿ˜õQ9z¾þK^õDjí¹ u™ º+_ÀȺ»Ý 0÷^ÇQƒ‚É0Aÿ!¹yusvøh;@+Ã^±¬¿ß3T*_BÉð[SÐsÊáÞ͆N 63·qôôÛI0¬ÇÒWÝ‚504T8¾dg£k*uq¿p}ÿ ޶q¯×|"ǯÉaUõÚúzQ¸¿ëo†Jþýñû§ ´&Ñ^´~õ•£ª~ªúY›û2Ú•-=hñœclXqy1T‚V!s>:L{?¹¾±Ô7GÉØmv,‰<0ÜŸ6“£¶NàÕjµݼ¤.ëòÔÐofïÙü¡/*•ßiÀ3pÏQã çg 9+òD>ÜFúM¿¾ïMªÝàö6C2ê˜wIî]¯³öïc¨ójc(]GJåKßôâÊÐŒžÏáÚ·™•¹Œ‚øuuzðœè­4$õ¶i¹×Q†ÞIч„1¤Ôýq퇀¦Yžt·EÝ4Èà‡ºP18“¡õÔÚ?»•”Úokü—M‘£A•Dúõú©êgmîg«Èol¸æ@ï/7„f~‡Éå&á0þR Yöæä}YKîÍáËñ9dÖ•2°(½ÀP“k3 ì÷|Žÿ2VÍ}CzÕ{úf™Œ+Ž$¥òwžuÜû™47?ÎM™H~1>·2HÇó«ág¹hþ=Ƀ´™p>7¶péþiø$š2tTE0¬‘mä¨T>ãàtÊkŽ.\X}z ôû~CxÑÓšœ^Íöi#-ƒ¢:ndhÅÙ¥`ÿ$CŽJÝÿËLM[5(l«uIF½ ýûz†¶wu€õö¤Ôþ›£ŸòDûH@×·kÏZý>Nªú©êgmîç¡Gƒ@Ûì  Éq³@·s:i²ý*è÷·&¿Îy GBJ*Oxå×m9êÙè °ƒÖm›× ^:š“ÝJÏ2;—™äEmÈS”¡‡›ùÂO}Jå÷t; gõ}z&.^NÛJFZ´„óš‡Èü·]Ù¼;ûÉ?òós†“¥§;ж–šäŽˆÐáä$RÍøŠ¬¼M)•/é‡Û©3jÝr,,"ÏvOaó3g’~õY ˜CŽž]’8:èó8È82R R÷çN5Ó|–5®‹:‘_V-L[9Ú´uXöòä¨ÔþÍsÆÂÙa@|È‚é½HU?Uý¬ÍýtîîF3’­w3|>D“ÑC^‚V£r4+½’;­ãèÚ®1à7*B ®¾½ °õA²¬ÏÆ\×Gžäïgøù1r²ûâXÖ¿~ùåUg¿VFJåïtoì»'Pƒù o˜Fº*&qçѤS¦º0sñ&§¾'ª“d}´s†8pˇ¡Ïˇˆ¹“tÜh‰‰,g¨T¾§õÃàà±hÖ|õ„Ðü=¤±z„ÂÃ× 3ßÌÏŸq$ ¯/a×F“c›}å­Ò¦‘R÷M·v… •ݺãµ!ؘ’6ÖÞðñNKrÓDcȪ;›”Úëö#p¼VÎÐÓ²§pvà Žªú©êgmê§ò£™žï¶ª<ßœ.Ó¨ò¼c#vç\†@•熆6â£s.G•ç·æ-ËmN2Ty.•?yž L»è¿ÍYˆÃ:TyÞõœ­PkóÏϺÿSy>ÒÄ‹CÔ^@ÿ4ßé³½ÁSqP Êó;Úïxá3*Ï­Æ.iËzqôOïW^l .,ª÷h×Å!L^hDF÷(t>¿R ²GxqX@¥ò¥ÎšÉc{¦4oX9/lI:7>Ó×ê‘GM"Á$Óžä"ø³ÊxÒ×zޏèô7G¥î—˜Ýbý=V:Ûï=qˇ¼ß«:(ÐBǃ°ËL Rûu{Á¬÷ 4!f ¯w%TõSÕÏÚÜÏÊV¿ò'š·Ã+×=vÙlü;6Õ¸”¡cªëAå¦ äŸÙÕôæ-X´“5hSŒøÌØÕocý®­ /ŽÎ +sÌ e\GߌÆ ²¬*•ÿ‰QOP»#ÐTlux²“þ(ñú¦  9Çꦵ†|åsK~Ú ¾@Õºu²*xqŽ£ÁË3Ãê1€^¹3Vò÷ •ÊçÝE”­è쎢ôñ(òïÝ‹ÁäüÒóY_Ïõ#EõxñjÀg†6oØ›?¨Ôýƒ ëC/@ž·€ö+擲¯c`\Ï`ú~Éü„’RûG˜ n{&”«‰„#9ªê§ªŸµ¹Ÿ…¯å&wgê8ª›<›t¸Ó 26f1tDqkøÚs½¤'Óþ€£“5ŽË .eeoÖÁÚ+V€š)ö¢Ç†.Úù–k'^äè»KÚÂT] P©ü1©‰ìc÷8ï¯Í®>ÛA¾Ôˆ7ðÞ hâËP–a´›lþq¨¢½ÌŸ£~±>ŠÐÍÈ“õÜÀ&ù› Õ¿2jJgrT*_so±¬TG ÍlÅš8Z6ýûkX€@¹¹°Åw}I]!múF@rv‚çjMRê~ï1í¡˜ h;Yî:ƒ¼qe;Ü7\ WÛ7;¾ïá¨ÔþP‹ÕÂ&I ÐYQ-Å¥k¡¤ªŸª~Öæ~¦š±_ùî€æºe.{g’οÀj}(Cß îf²%¤Mj¹ÓÉuU;6÷t¢WòÞ Óà¸ë‡Í´ØšÆ-ü>‡Ýè€S°ãš#)•ÿ–f\nFÈ6NidÂwOß@VGv½Q€6ë½ö³U¤ÑñIŠŠåC¨ßCÅ’° ºÇ¼xçíå¨ÉìK,lÂ#R*_Ô¢qvé^½$XúÔŸ¡F?Y·jì!д=¹CÜR”ÆCùŽ· ´¢j7m&P©û»RúÃ…Ù“ý¾Æ.ßs$¯ùʇüjÎÐÄU>ÜÞ­Š”Ú?dê:yt÷#€n®3z <@ªú©êgmîçðS¥léÄi€¶}QÚþp";›šÁ¤œ‘ Õ+³ÃéF¤Ÿ›µâá—"zX“+ÔæEæ ¥ ¡êp¨@õÌŽ³ý{RȈÀbo{•£?M­Á¶ÏAJåšË¿Í]"P¿•·¹ïÝñä²' Ð*Ô ÐÞ:‡aelCmÜ›(|wÉQ×Î×r£¿Êüw®¼Sé'Ž:GpR*_ð˜õ"ïöC†ZFx õ-=Õ9ÃMV´¨W“ó|U⎮O½ÍŽûtèš…òó…9*uߺ`Ä?ßííçåΖ6¤©õ1ùäGç|Ö9¾%)µ_/â4Í^èܼ[0P–ÍPU?Uý¬MýT~·oÝ Fª<ßñv(̹u[Ž*Ï‹~Ö(Z¬ÇPåù¦F!üÝÎ(*Ï 2[ “z!UžKå?Ü£®˜s×L Êó¹Ë€ß–«2ô߬°:ís*‚‘JOô×Küè„C ýÓ|鋘Ón€*ÏîºÏÕØjŽ*Ïm®=ÊͰYÉÑ?½àëzª<{¨%Œ®~ÊÐ?Ýÿz× è5a7Gÿô}U?Uýüoîg³GðŸ¼ýQ@ÚGM@µV^€[o3TcÍ&¶wØ]2»n+ou“<’þNÑd§% š|Ó9èA5×EÓ?2´pònÙË Ð3Î+`¼†9àm*”öYÄQ©ü‹J¶) O1Ô±EËQ÷ȉ¶iòçvš’óƒm¨ˆ'M×¾çó¢üþ¥ÑÊܳO“ȵOƒ_y%Cµœ‚,yJåë­ë—|¦ ´fžôÜïMÞy¹Ñw8>6nlÔè¯×ùAä_;ŠÄ+ Tê~¾ÿ%pS´ôùékpýq=’…·†[!™m?m$ÿv<™”Ú_Tžü=] ››ug2–“Rï«ú©êçÿr?˜^…Úá Õ©sVªë’Õo²)7/‘î-«Xì^A>;e…b> ÏÓüaFýAäù°`fRµ™4ë¡6—û@Tó^ÝtƒVÎ&¥ò‡,’ÿÊÈ’¹Cn#@·ža¡adÞ™$˜WãNšÔãÍûÈð¿ á¯(²¸m=øûý^vö3Gï{“RùœÓƒÖGÖ´ÅßWÙüud§)ðÄÚœt6ì1óíÈ!u2øð}º€&–¬dË{8R÷ƃY«' -±½ í÷Ÿ ø«blZä½^˜æ7’¡RûµüÖŠýúYõ9è+²S9CUýTõ³6÷sÇýbP۾ݒU ÉCšp40´!êp„¡çéB=ãdÒ¸í>Ⱦ“Œ^{ (ÐÄ{aó s@5Ç\ý™mjeî.î\±'‹7µàÓ=â•Ê?i_¦Ñ\Ðk_Ù×qF¤~b>ü?öë<ªÆ·ûx’dh@’PÉ%uîkgI2%SIH¦šU¨„IHŠ„ ûº2%cÉ”$!ÉœLécø=|÷õ¬u~k=÷òßó´ÎýÏëíÞû-oŽÓys nÛ­÷àáë¯ZrdÑ.€îxÉñ©cÌ4ò¦r§íH¤ï&*•ÏâÉlò!ÊŸ¡5–î™٠¸7>~ -Ôœ¸¬G,™~}×_W€€¬v€ÊÏ-ß¾2‚Jݹù!œ<¸… Éæ tÎp.0„ùœ €6?oDgoÞÅ•Ú_e`ÈVÆêÔL&<>ÌUöSÙφÜOŸÀ"X=?†¢­GÝ¢÷{¬¾+8ëÄ4¯8'-áêêƒåñ já«î2®½Ú%x:þ+E#ì·ƒ¦,˜¡ñKâ!ôg$ ¤uXéõ¦¨Tþ?æ $¶ ~CòɵケÇÞ‰ÓStÂñ\ˆ¹àÀÐYWK¡ýZŠîÞx~«:0Ô=l ”×$êètnFT*ß³VWEAm„ÍÖbèÚo"̽ڗ›zZ½lÚÿé0W{& “Oé‚Idh^i̯ÃnîïÑÚ`ŸÎ½güIlªµŸ[è04›ÎÍlDzœHæJå;û2þ mËÐ]nÓÓŸS´ÒO®nñwÞÂ1ôò’®úñë¤ùÏÛÜ‹y“É“r®Ôý4íRȈôÑ©ê`‰¡6EƒKò §I8Aÿ<¾›¯ST²?1·á°gAK>_£ãÇ(ªì§²Ÿ ¹Ÿé¿OÂÏQcJG‚²nùá}wØY%CÕK'‚|c„ˆê¦}§äùj†Þ[:…ÝhÝ”ûy€%«?0›{~W ‹NÑšý«™êÎ_ª_7ŠNz¨Tþýê} §Zw@?v¿ü:q[eÆâ¶Y Q=€‹?À´{¦pq^wØÖJªó*š[µ²+ª×›kcÎêw!¨T¾_;^Ò‡½ã)ÚqêúàrknI®}0c&·ôëJ;>CD+Lt…L³Šú^ø*¦XœQ©û÷'<„,Ç9ñ넘îæþܼZÿ3¡ö¾ÈÍUÁ\©ýk,’ Uu6C]ÍÌÀ?e;WÙOe?R?Ÿ?vÁ‘! Uœwî4 2¢,(ª8÷õZË ì ¨âÜÊ| [QAPÅù¦ú@ÐUÛ¨â\*Θ™°¡VPÅyQ¬. Ï a¨âÜúÜXVîcÍPÅyÅ‚G²ý“—ú·ù&뽦*ÐÅUœÚ4ƒV|5PŹç½ntûO‚þíýjÕb˜ùîEç^žŠA»'3ôo÷;zN¶9Ç0ôoßWöSÙÏÿæ~½]ÿÉÀAëäCz÷¥¨Ñä?ò–Zq¿ìô%†Þ¦€æ¾¼E†¤õáªt…ž?fsÉ¡Uö¦Û¿”á‰_švô[R´àükQ7¶;AF &e׸Rù­rÍÙãÉ„qÕF°I ­¸cO †›«½¸Ñß‘ªÊ`®J«m¹ÅÇßRT§‡-½¥w€ѹ|Û‹2tî©h8-’¡Rù‚Ö¶–ghhš5 ?®ËÕèr'R4«Y7ØÖ2‚»bQ3¶;ÙPÍNƒÉ2º•+u¿86ZUÍbè~Ï­àPµ„»êØ ¯ŒôØ¥c°~h®Ôþ”ãäúà j\L®ŽÕz_ÙOe?ÿ—ûYád+Núþ\DU¼DçÔîáYºÓ PÃIƒ`ððŽ\ãqÐâÝ6‚öpÝ÷Ê3DÔïÆµzß{0Ðq›¸·¨ÀÄçšpÛ®êÊ•Ê~Þs¹DQÛÀÙl“-pc?¯Fg1´fb­Ø:“+sO ¯Ѝã˜tkbHPÛ‰¯ÄnRꫵ‚éÿ:LQ©|=\]áæq5@¿8úÀå×OZÔ&lÜGÑ'š@›Ÿ9Üv›àö/_@K§^‡€¸:•ºßöR$ØýÀÐü“AÊí˜ ‡¬€{"úuþN¸úЊ¡Rû ç ƒÖ¯;ÚÏm <³ Ê~*ûÙûéqm£¨•g#¢ããŬ9ÚÏÛJn©ªÝ>.>EÐüÜ ßv‹¢çw×½C ú‘¸ÍÑÜø9ÔOw÷²ÇPð!µûXGœª1T*ÿsOx£‡ yw:kw`A}o·bîc;14ƒ›á½‘¢¡ë¨å´‚–WúŠ_L=×¶ój>‡kÑJ6çâJå›aU•é]~:ªVŒãömÿštxt•¢Í.’FÃïr[<³'0ô–Ë=Ò:!+uÿð:?èß&Š¡ô‘ŒÖ‰á‰F 9«’ñ@ö'q)Wj¿ÿà@°hýPŽ&oŸC졨²ŸÊ~6ä~FøíU>”ÉP‹3ûźf¡zôÒV(ëÓƒ ªoâA#YÑíÇžóq½ZÔ­;nÜ‘›bÂ꟢èObÊ2&A=¦ÌW¸ÔQ4vFMxq\@¥òÿsz,ë0»€ ÎvläM@õŽfVƒ›ôå+6`· Í7Ï&m«Gs¿mÖ„uñã¸ëÖåÁ¡öÜ+¯s-Ðg¨T¾ ©C#ÔiîV¸¬›*¢½âȳÞ%í¸Â4špŸûs¼Kì¶’¡ú·×±¼5?ΣR÷«·8ýµÛÚ!Óì\vr‹¦Ü¡›ß¶â6yÖ‚Õi Q©ý“}¡jÁgŠþ£}|Zܘ¡Ê~*ûÙû韵W,:U, n5;DÏV­<ºä£#)x< ì^çZ¬í'Þûô’{¾Þ>ŸâÇõ~•–ué ¨G@Wa\#×öSÖ:p ,fAêÂ+•ÿõPC¶Íh  +ÔQëi\óà·ôÍwnþµçâíf³àÁ”~\ãÊ8èVð’ eëê‰nÖn†ö½Ù”õž6œ+•/ài õ_DÑ÷Qà»î×­º^0»ÆUñÒš±“ÜÊ4]6aÅj@÷ùß#±ßŽr¥îkŽ6ƒˆˆ] 5Ð?£=ÜaAy´ñ×ÇU‚ìÍô­•Úe>€Df½ hÖÕ&BíŽd®²ŸÊ~6ä~i…‰ å êà>Eµv6×cž¬YfÎÐŦƒaTý®†^û~·õšT"ö]Þ‹ ¿×6‚ÇO¦úüÞ,h^Ð[Ò4ZÚÜQ ‡y‹*•Åâ£T×v5 ï{S‹¹_Í“‰GЮ[€µ³wrn„{í®hÇk©pÖwEݿٱ¶cš÷RWÔ3Z¨T¾)s‚ÀeŒ:CS¬ÀzŸî\‡cDôôÞFÑ‚ñÅ£㸠“Aȯ€ß¹­j5(*ußf¢ LÐÝËФ‡ ™•ÂõÕ© )=ÜxX6áJíw)1½Õô)ja0Nt©,C•ýTö³!õSññ¨Õu–†Tq>8´)tµ˜¡Šs‡È>²;wTqž½2 J[ÅTq¾1“ÑËm0Tq.•_åZýq1ÀUx²ÇA¨ùZ@çÜ¢áx}o†*ί~™cÇŒôoóüåŽ{‡0Tq®?¢­»W(¢ŠóIqàëÂп½ïwô©QÛÇPŹÆÜ%àê7Š »_åà0ù3¯®„û—ï+û©ìçs?{ïº ÿÉØa«ó^†vqûHÎØÏ­¼ùФ‡šS)À£Ï˸ûßݹëÛ0`/U êðu‹L»ÀÐà/‰Ö#3®•} ôæÄ­w˾þ7)*•ÿÙ¼o4.c&AõuJé«“\¶Þ€Ú¬ôõÔÒõ` Wž¿Eì¹.œ¢±kˆ½íQõ¹ÙtvÇ`@«ÍgÁ¿~R\©|º…íÀyH†Veÿ µQ­¸7ßœ…§š ÔW !èÓžÆÐT³š¢Ž[§’»[t*u_å÷zYõѹ5²®–}8±š;cq&®ãŽº}|uõ*¹ßÿ„ Ž…PÔ£fŒ8üm”ˆJ½¯ì§²ŸÿËý쯷— =ÀЙ7:‘ßoöqýίƒ¸i£=Òm' }¢Ã™=¦½¤èÅóQÿÖ‡¡[õ‡þ×›ú&q.´HÐ’Y÷ˆßð MÚ-c½Zr¥ò; >IïÙ×ôÔ˜úåXk@ z¾;¹ºµ‰päånžóUÑ|ÝKUÙ]™kº?‚ ‹nÃËyŸÔcûfÈÚÆP©|î·)Ùè®ÏЄÞAD£w{î¥ÔK0¨¾5EfÎÙï¸*¯‡Ð¬ÚdnrUš5Š R÷=ôÍ…!áµMð\nÆp³ï/€iQñ =e•"XàJþüw=•ÇG5"¨ÊýBqç8®²ŸÊ~6ä~ª¨åÌ+ÛÃС…úôмÜÈw¡pn"AŠØU2tea (i±“¡sgŠ´ã¸ƒ¯/3yˆŠ «BSÔÖ«Û£?Ð$ÖÜ-ws¥ò—¬mG;04¥ Rž³r ×%†ÁøšÜ’ºBÐx›& -™iöQ$hð›mä]ç{\õ_ýéS;šçáÇfœjGQ©|"ÔIü©6 UI[!S}Û”{­É>(Þ.p—l ‚k¿—sÃK† _ìŒu` Ç_[p¥îØÅ ¿Ž&Twè9ÁÈ`W¿¿1[üj2Cƒ[ù±ÐÌ•Úïéº–Ì ¹@P­ä§d—㮲ŸÊ~6ä~ÖvÛAçí‹f¨s»GtVfwå×P~-œ¢j·AÓžºÿ›«-Ë5h*Ù#Š¢†bKèbPÇý$–ÜqþÍuéUç2º4Ë#t¯F3T*¿ï‰›ÂÂÛC=´ˆTùâêm/€woSÔbĨ‹ÈPÙûJ2±ì:A_z¶…­×r“¶?¥’Ã]þ| <×Üŕʗ÷:Wtk÷Ž¢¾Sdô‰•Ƚôj¤Ë63T§ð&™v%Ž;:38¼ "èÜ¡‘°0ú>E¥î·t-Š´tqš*œÅølÍLÉ­4»wKâJí/–u‚Ž*» *+”•G+®²ŸÊ~6ä~ïjÁÔ½14nhw¶üÖXnJâ*ˆpµáÛlQßݸ™«^w’ î,7ØÐX*´p‰¢·‰ä“·ãï‡Ô}U8C¯Tø0?õ•Êtž-¨QÔO}w(w‚ܲcâ7ç3)0<Èw´>˜DP“ýöpM'V@ígÝë—, š9/ìMƒ*ùùšB×ä„R´É°=Ô̽÷˜ñaaÉå- }®9‰V$¯å–6nÓ¦q=ØêålÇ•ºï§ÙŽ˜-:CP'—ÞÄ @äÞû8~”&j·îôÖ1çJíÉ•‘"êþÖ òûFSTÙOe?r?›uü³C;1W¶vY î~ËZòç”wyùdâxÇ“ë;ì­½Ý hòTcpš”ÌÕÏË“¢z ¨J e®Çp?‚4y!÷MtØÎðz`Ë•üùoìj5MèoÑV=¹û¶Èýöìe¨m—·ô2 æžjcòÙ{Dts¨ûéNÑ’ÌâÒ -›Ç´&;RTòûgÊaªÒgž±/V0m‚î9üŠe:2T§{V¦Þ”ÛQìÀÎÌÑV&'hÎ @%¿Ÿy &7_"h½ùR]ÀuZ~Tœ^ˆhUT ¬?‘¡Rû[‹MàÖÂrоkM¢YWÙOe?R?Ÿºg³˜°êEçI3Cço^îµì¥€*ÎO=0"—öœ#¨âüÃëH*³b¨â\*¿ËÖñ0®‡ Šs³gŽ,g¢ Cçqþm =÷ EçÝtžSj²п͗ü0ž¶Hˆ&¨â¼œØ±Ic|)ª8O¨ª ž~!€þíý»‡|I•õ-‚*Î]i<)‘¡»?¯¡L^t‹¢û¾²ŸÊ~þ7÷³õ¨Ûð½è6që*k¶*—YqÏîI’$c茸`èÚvW¥¹³|ïË!­é*zœíÑÐþºt¿ê†VÉ1sC9E};wºz'tü yw¹RùÝo!Ã]¡hàbwòÇRäö¯šJËWZc6‘˜MÞÂÍÓß"kû³×3)†hjÇ5ܵNÛq-vfÂà-(*•oï²—d‡Vw†ÚOÛM¦œìÁ¿ªwÓ¶r]Ù’Fâ¸>»èÏáÍëMCú"¨Ô}§ÂL°ˆ°¥hÜñ£ð©k÷r˜>< JfèÁÔújc4WjåvÔû®eܽ…‚nF(W²Ê~*ûù?ÜO½ç~gr]@¿ Z:+NËИH 8ùOCƒÒÓˆQz"WeÜRùš\ Ú¶¿*™Ýá,· u64y wQÜ~ª>ÏPyñHY›!C…¹šP“ZJQ©üÉT·ã-(º*¨ø'q-F™B»µ€ª7 ƒEÚÓ¸.å-Á{“&7°Ë0xôôA§~%{U62tÎØÆìÚ°ö\©|7u%ºæÆ uÈ<){ªÇ•G·¥GªÃ¸”ÑcÚÓ¸çùYÜ@@Ï1wØL5¹R÷[V‚îCRôRÀAh·¢€ÛuÜ8Vúç·~óTæP˜KP©ýw[¼¡íÞY3´b°˘øID•ýTö³!÷sŽÙ2(x:]D¿$/#ÝÔ(x°ƒ8üÉV†ŸÈ¥,ÞŸ[®uˆ¼XTJМ^Í¡õÚ|næ8[Ò2m) oz]˜ÅUÑÍ”ué"§¨üŸfôAJO‚Jåt)¼jFÑ<³Ö²®±ã¹¾‡à7SÔ®ò øo¸& ¶ƒç‚W³]9v>øç\Ñ/g[1âÏýævœ T*_µA¡x#½Ck^¸ÐeCnRTÝÍ”5ÿGŸ¡:!ެub,E=ü¢Á딇ˆ< ^š0Têþ.¿}õ†¢C “`ìÑ& ½Ýè$ÕÔ Ô,±–ô©ß˕ڟ¥ÛMþ®hÙ·&t<€«ì§²Ÿ ¹ŸÏ-]#gŠžÞ6 Œ»EqÛjwa¬zâ‹só¹DÑkxg¾ [Ç‚¤£ª¿j'ø¤64;êäÍ›#¢çï'nžMmÝÙ Öç,%\‰üFSNÊ“³õ(ê‘h.VùÑý†'À1ÿEíçï€ýÞÖ íy}4Üžxˆ¢¶ãÚƒé‚܈]{Àôu Aän‡g9š •ÊW¢K/Œ¢¨Mò!Ú_½17ƒc*7|ô·Ñ ¶TãA-Z— ^«†0tê‡{ÔøÈvŠJÝw‚óä¶ ½øp+ô7æºÍH†[Âl@ó~߀Iá5*µG­:¯ßhÕôXè“7Š«ì§²Ÿ ¹Ÿ&Ãå¼#ÝÜc4œ_PÀ-sžÎNÉÑp–—vŠ IïCQ²-E§¶7‚¦{Ïs+&m…]b†ºWw‡Ð³Ë¹~Cáæ»+M–¤çM†JåOIœ ªíÜ(GFŒ½î»è­#®°ãÎ*†fõ/"WÍ#¹õrH1͹C—~–5úFQ—î+ˆË°• -ÎlÆ4Ï ¨T¾æiYÔèÏyÚwG:Õ ²"¨F];–¶³ [üNÒ +ü¸/ŸçÒwæ Z°{¹e2 R÷/Æm‚ÅV --c[à:ý“Ù*ý¸²¢~0åSWjÀòsÐéŸQ-Ö?­+>STÙOe?r?M¨9úœ¢6ïÌÁÐø÷üæîìb˜ µI7©¦ÇJnáÁ»$±ú-Eïí³&³=ãªÌY"Ô¥ø34òc%­Ž4æZAæ·‘£Ž­K¿-z€JåÏ k'¦îú×çÑÿé ï*/˜; Eòåró¤@†ŽoœG'Gàvœ.º±ƒíŸ×•Ê6>ÑzÓ³Tãs@'˜iÂú•ž\©|1·Ðïu+Aë#ª¸_LíÅøÑÑ€/Ž'‘vr»Y.‡w×:q¯H‚jÇ•º5x-t²ÍÐdm¨í1‰»½s êœÇ}×Åý<؆+µŸ¾ô‚6Ö2ôÇr²U¶•«ì§²Ÿ ©ŸŠÞäN°‡5a¨âÜ/(^tHŽTqètFnY•DQÅyvæ#?(¢ŠóåÉ®Úô5AçRù=ÖgËž§¬ ¨â\UÕ¹=¡¨â¯ÝYâ¢m@çպư¤<PÅùÍýãÀGeCÿö~\—iým&Cç—u§³'ÞýÛýÎ_nÒç«g0ôoßWöSÙÏÿæ~¾{RÿÉ®i½!d×v†v—«AVQ7ÍZ‡ér4§Ïiøž/RTÈJ‚ÚŒ ^¦aÅ Š®: =§þÃU-ë^=]ª{¨ì˜MPó ƒáÝË/"*•ÿ¼“lÝ¿Ðïæ…ÊF®£®YÕüߎ4î]ƒ¸NÕ±0¤£!w‰þPí4€ \CÉ ;Ê÷¼8¯åJåóü“âÄUsȃ¢ Àý²ê;õdhþ•`ÈîâÅ5ï :óåhÖØŽ`ÝöE¥î{¯…Ô«¡ Ý5z>¨-ŽâF„N‡j;î2ù}bÕzWj¿Üc+Ä«š´îO$lÎ8BQ©÷•ýTöó¹Ÿs;Ÿ%¯§îbhw÷1dªu"7`ß^ø¦×—«3w,µˆÛølÜê`ÀÕ—õ„Cs¹ ÿÂö»qSç§QýÏZÜ,-9 WFQ£ç³Åk®]¹RùŸ¯Ž‚Ηú(?ÒŽäx%£¹–éð¦ü%AíÚ§Aø«DŠ&T…At‚5CÓ’¬À¤SGŠ.k%'S/q¥òݹ~n\8 ¢ª›sa—ûqŠNšm ek"Ûû Ñ\Ë-\3„„¸¼ h`ÕM±O m®Ôý OWHÃP“»2ˆ]¸zy蟭ÇU»|œ¾+²§¨ÔþSËèüÄŠ¡#&‘€Ñ£¹Ê~*ûÙûéìÝ_p˜½ƒ¡æ?ó?Gs¯8õ÷Ø(®`·Ÿñ‹åZ[w&çÊý[»ªY`Éí}¯ Km&¢OòoД‡Uõ5Ú(h•殚Úò»çq¥ò·xÎ6ëèûÁ¹pÌæ.A—œ‹7e¨aÁaÈ÷¼IÑ›-»‚ñÛe îgHÂ7¯æª¼Y*sMw hàÉ²ÙŸÜ *•/ÿa&¼qÔchéÜDÈi:†kBí MÜä.©a¾×Èç¹£éf‚Œ9FzÔ½çJÝŸvÚ:äÄ3tqOØÛ;‘뼓^kº‡ YOæ ¹ê¦€JíOÐI'Dµehκrjèj@Qe?•ýlÈýüÜ'檇3´UÏËÔ"|×0§±hªÁwý8õçÉÐÔas¿„R44P‡-n¹ *›ÚWMv´Ü¹)Ì›3…»ôëpÐ-ÈQÛ¨›äzlE¥òÿ¸Qáε8|ªËѶg¢ ´~CŸ\W;¬ãVEÓ¦£‡q¿×a¿/çS´Ü÷Ñïu‘ ùÛ­¡Ù ?*•ïJÌzHÓ^ÍС9ƒ@½Åf®é 5VYkËÝrÕ’u9XFÑàë6PžKÐÄQPõKD¥î§Ö’ô»ú0á2ù»›½£#}Õù¼Ô½¯Tjÿ²ž”†úe4¯yѹ¯ Ê~*ûÙûY~K…mÌqgèͳ˜žŒ[ð§-³PíÊýâ%c{í¥håï]ôÞ ¾€zoKæ=XÈ ÍŸm¹vž» ép©€ÆÚOó‡R4/@G8í|€ RùMO_€ä5‹)švþ|ýœ;òØv¢û8œ¡_~4¢]u–ríntcšÐÖuôÌ' @“ŸÂü%‘êMšdžçJå«,ùM4`;Cgïõ!Ïü[•¥ÃÙ›}·DtÖF®,'¨ÙìÆ`Ùû1EÒ£çäs¥î˗ŵ»ºê­ é¼i7¡y8xÖgЍ†Æx Öj •Úo_VE;ÚÛßò„Ö\e?•ýlÈý<2ÝŠÝëcÀPŸÉCÙÙ>o)ªî6‚Mˆ2ÐÞ¢5;TžOÐü¯¦ð«³' w #`k¨wÔõ-à£QLÑ«íÀOw8C¿\l ¦¿ntÑÐÑ`«$¢Rùoþ΀y¥]šñq7ì×Ëmvà= iÚ›;´oO64`;E=4©[Ø4@eÞ©Äúz7Э±Øw»—ˆêè«’îzû*•oèïyÑ¢- =0.Š®ˆZÇ 9×…e,ÓTuÅC:#Ó•˜ûHt8Ù^DUL„A%™•º¯»þ­°áBCýÛ<•5íÏÝGv:÷ã9yXÒB“+µ¿ÝÙpþ¤EÐMßÂÁ¤iE•ýTö³!õSñy1qëûjEç%S›±Š-V€*ÎÃ&€i7ZTq~vÃ$Ò:n CçFÖOȩ֕UœKåO5ÚÏ—3Tqþ]ÝŠ­^¢MPÅ9k;î͘ ¨âܲº)$heôoóuiö•f6ÏPÅù?mÚù~ Šó«W+ˆýüçýÛû7ûŠ 2ãª8Ï8v”ºÚéPôo÷ÿ²£‹,ú·ï+û©ìçs?µ<.Á²8$‘$T¿%èŠí¹dÓ?ÿ6®Ð‚¹Ðb(-ta›SSt‚ã'ê•k̽W_N›_N!h“ò0¨‹  ‹î\„¢-föhGëëBõ­­€ž¾|fì×àJå7Ó±#ª/ ªþr1>UË]#»L+ÞEËЛb"]ðO*A›0‚¸¥ñóŒØN¸ÅíÛÉ †êKP÷Z#p}MQ©|E±P°g)Eho„DŸÜ¢zgvꡌ;®ëX¦~‰Ô«•ì‰F CKŒ‡°EÏPTê~xÍkúªÝ#ŠnÒûA›kžåž5½Mâ-ehò:¾ó@®Ô~«7ðˆtðƒlÈù×÷JTê}e?•ýü_î§ŽùbeñŠ ƒöü"·=áöòpagÂGsçÇöen÷uõŸÙ†êÍ·âÞÏL!QK¦qK¢Raùk†ºnì ½oå¶~ ¾ grµUèÞ7k¹RùoË yò ßî¿'—~âô(¿†jZ`!#6Ë,¸b«ÙäûÈ­«PÎáÆl± ïvårë®D‰fÄZ@¥ò=|±¼ûê24#Ò"Êûs-vX2RÛ ÐŒÀZZ0‰ûíY+¦š0ûYõ‘oÞÁ•º¯~S•Õ|ßAÑm'š²VæqK¾ê°D«îRHµôÕ•Úµ#‡Ì¥èϤ‡Îa¨²ŸÊ~6ä~~ï¤)…dÝȑ۸æòm ¶Ñµâ‰YQ\ÏË£àP*p+§l…Ñ5W Ú`†ÑÎû×çÕÿY?Ýâo†q»moÎÖŒÆÐrÿά¹~@¥ò¯ÿ1\Κ4}›”yEèô‚Uà3#˜¢á£á¥PÏ=¢ ?”Tçwhü%Žëè¨Kkêۋ軇MHîÈ÷•Ê×ø@zvjo†öO9@{Ûkp}I„19΀ξ 6|øCÐ(µQÌyB 7Åà–<¢g  R÷‹úµ`©*58Õ”e“\óÞ½Á‚b¨¯ÞTÁìð¿•Úï”N=nÚg|©]³ƒ«ì§²Ÿ ¹ŸóF@é½"Mx5lÇwâºçœ!"AÏg^ÿt79ÚkÀ1ºÙ§C³ý[3½m(:ãÑ)~UÐÚO |\VStp¬ žÞ¨ê° PÄDT*ž¥;íÐÑÛ§'Á¹q=òµ9¨õ3bèŒÍ¡Äd’17zôX8[! oVŒ„96½>yLm~‰ f“Üá‰Á4ŠJå ;\F¯¤Ÿ¡èŽ÷¿éï,S®›ÖE°ujEÐí#ó \߇¢ýîG@jÀb@óî\švPD¥îg=Te†´‹óÚóm:·ñÀë4µåŠ®\ùœä{TjN¿xX|c" #ç_€ëÉí ªì§²Ÿ ©ŸŠqöH°H-’£ŠóÍ¥çà~á5Š*ÎKrT™÷ØKUœwkùwç0TqpTò‡Ç1Tq.•¿¿µ3,æMÑÿïtWÉõñnƸ O²­1D;DQÅù ¿CdNX+†þm>ÝNj,oµ€*ÎÕ²ƒ¢ËPŹÙÛŰ:8˜¡{?hÊjX@PÅù”ºñùv @ÿvÿ›àLh|QŸ¡û¾²ŸÊ~þ7÷sýcþ“ïØ±4ïDÊM̶Ækstµ„ÕG{p#zw€Õ:Û¹Ï÷L„–²4nYqsHz®ÎP˜–MÿHQz| tõ b¨ZJ2´ÜÞ›ëÖ‹zÆq¥ò{¸,>*]@UÞ—ÙÇŸ1%èî¹Ciö¾zîíñ¹Âš=uí+-œgÉUé1I>cq·Míš·Ù“[Ûs ¤ÏYÄ•Ê÷mv?¸Þ}1Cû oÈŠÊõÜÙ¯†AÙ}‚v¦#!/ËAŽZ&pù¬‰ˆºf¾'6[ÏQTê~·|'ÐaDPŸ(¸çûQ@uWÍa‹U7¶±å•ÚßÙø¼AQ×EÁp?t>C¥ÞWöSÙÏÿå~ºúe½ÛgËжԞ óš}%¿c)E½×¥“â·¸—78‘ò]j ÍÛºFìâµ¢½× ‹#·0ô[tsÖ.·?׿H`sT÷´Øð)Éê¨Tþ𢙂ø4áÃGa—K<×wæ=â;×P›ÑÖ°¤°·‡µìKˆä^0Øj%ã¸é^gÁbTE3b_ÒÜX†JåËþîBJÖl`hz€¥8ùøî¤J?¯˜Qt‚MwÈ–oç ‹ìjWýÛÇ[e«>u'¨Ôý٠ǃCÂnuž5ŒF´ànºO â;0´ïš´¾ìE¥ö[ÝÛJ>ËbJ;Þ¥Va.\e?•ýlÈýô3°a{J2 ºr)ûÞ·žk¸Û’<îG)ZTÐMXU´•ûs¸5êÒ\ŽžÝZÜ9>š ‹ïŒd¿ô¡f®MÙªj[@CmÏA¯M êÚ¢¼e¨TþÜ=CÈ®•²‰ì<Çuìµ®·Éà®}¸dVZls š^8DÐó«Ï€Jr(EeÇú²Vù5Üwo+è·E.€Jåc[#éä‰. uéð†NÚkß]^÷eÍÛšGº=ºÍÍiCIJ&#hm³U¬Î•ºo?o¤ Ó’£õ+&AQÓ ":-æ-0:" ‡®íuj*µßá9°þ𛨆›ÕPe?•ýlÈý¬ÑnÅT‚Œ]Pÿž¾ôrà¦\œ"æ5éHÑê¼ó¢Jk5¾p:é8¡œ .3T@gi×`JPm ¨Sè ˆ˜È3®-c 3ôï>¢9P©ü?r»#'¨ãïgDpÌáFWxCõQŠjïµ³Eï¸;¶Áìü m~ÚŽ·ÜÄ­è<N¿ÔôÙ9¨€f•üüËÒg_V= ¨Ásæø¼Ï¿Í÷&ež¹‰ÅÂÍžI\¹uW¶"[6k¹ú½˜+uß6z"D;(¢ë‡üšÿ¦šô;;P›Ë+á`dA¥öŸÚpD¦½.P㹃@×l=WÙOe?r?‹­OЗÆÓuáN#º,ãF¤Fˆ¿ü­š'ì–­·>È]éÑê×Ìäf'8¸Q÷£YàkYEÐB16%R´ôÊØâ<Pz>~‡¼¡¨äÏߪ 4†4‚ÓiÿDlá&løLts[1Ôöã8’jÒ’{j±ƒ°sú&î­KiºúHn·¶z¤ÉýöR‡™jÍ•Ê'mɬºŸP½éƬmM6AUVO’ÕBQéo峯MÑËÓUhÞÝîÜÀ)B{9A¥î,qBo[ŠžÕv³¹ßÇ‚u#®ö¦ñð²'C¥öÛ„§Â_õ™rf%zPTÙOe?r?>mËý‚–zTÈŠŠànßÙ™T–—n¯tâ󫿺3ì¶Ž¦¨à˜OtZ]äfì[³ºy14t| ©vÞµ5Ý(xõ÷à>jÙ„U÷¨P©ü—³M`ã-w‚ÆO±€ž´¸yww˧QTnÖž¶>L¸¯²±fúaÜÁÞ2vìs&AM^¦-íÍ=²­/üÔ÷çJå#]š2›½z€Ú—C;]Åõðˆ“—ô:% Fï-…»iU?jÍÚÕr›ç/¼WErTê¾ÙdGˆøFÑmgC»c‰Ü6É?á“ZeœKKƒT¹’ÿ®…êgcZaB~ â*û©ìgCê§âÓV‘ès›UœOÔS…Ùskª8ÏÛ†œ\“BQÅù£×è¼·Î Uœ÷¶ÙCöw¨â\*¿·¿ è-PŹ“)Uy$ ŠóIçŠé„ñn€*Îß¾9 *½Î¢›ÏpŽí¹ÜÐÿ/_Q[bÿ5™ ŠsÕF°¾T‡¡{ÿ¶½ –¯Ê ¨â|h£,dAÿv¯‚ý4+Y`èß¾¯ì§²ŸÿÍý\3â,ü'¿¹§ó’‡ý<ã2ñV¹Ï­5ÛG3_pÏ7ª#y‡îpß;õdí¦pSOÈé­Â!€ª||$ÆPå†nË$ÅspO66€tË`ŠVì lý¸Rù«äg ÷v–ˆúî̆–C÷PThâU~ ^/2‰j.åžhy¾(ÎPÕðRb9„[ļÅVëº"¥«:ž( Rù®'‘‘±û º9&ŒÔÉr¸hˆ~F=}kéAk Ü@µk²'ƒúelov­ÑlŠJÝ_p#Ê¿t`(Yå9«{rë&x’4ŸÏUl›žr¥öGŒ[J§Í±ô¨9%òEK¹Rï+û©ìçÿr?“ô_‘õão´êVcXdB¹‹¯t†öwS¸nÙÀ› ~\‹£Ikûp@´˜íž/ã:<-.¶æ–®Œ›w~ hµÊcA…$èmƒpüq$W*¿&K‡weúáðNÈ îÇU?Ö.ÝGÐüŸ=ÀGË’ksy ”k ¨oD0„~ñ'hÇö…Bå¦õ€êöO€áÉM¸RùBŸˆdMô9‚&.üBvÍËà6¹ð™´ºd è$kðL1ä¾?v”.ú9ˆë7× Âÿør¥îo7ô†YÏ0”tÿVC¹»,µú8õh9Ž,¸NQÉß_òÈ5ù׿7ÿç|Ë#°9ù³€*û©ìgCîgÿê̇#=?©3¬¹kßš@õÜ Z÷«?˜…jQ4Â< þYßДN9`ݪ)E56‡Ár¿GÜÔ­Šcr¾dZQt½I†8ÙôW*¿™FØGNehSÿ‰0Øn wѵþP“+G=ÁìÔ](:cE0÷iÇõ/wO#?p G‚ìN'†ª´Ýž›j5‘+•/oLk€F±}ÓÛÖžÃ=1 _r½Ö®‹Ù ¹·O¦ÂŽƒƒ¸ƒ-ƒØAV •ºÿÈdd}rfhkÑZø¸qûÎIê1®l]_GQ©ýò;¡ãKm†^Ñé -CÖq•ýTö³!÷sùë°õá‚úÿ´„#úqVw‚^íÖó;I_™Å-þ¹ ~L±cèëÒvP¯ÁÝqx›ì`ÒXnôîWô÷£ݾBŸÅ”tî?ƒàËb@¥òÏJé «»mdèά[Dká&®Ê’æv„¢%æ»Iâ<‘{~gùÒÊ¡ƒ´HÜüV\·)*,«ËQ=´j)kç¨äŸŸ­%ì/»# §æYƒGû3¹è„ô t Ü‚ËÁ¹ãBŠÖ¢gè½_¨“j ŠJÝ_i ?}<ªw´ $¼˜Ç}X}VÌ—ËPoÆ4µk@%ÿ¸4_$KX߉ÝÝð„¢Ê~*ûÙûY4c|) jBk}àþ¼u˜¬É:OÑ–§ ñˆ?Èõ׈•iA ­šØŠuš¥Ï}7_› úcšQº˜>»<P·sÁyÿiŠ'¤ }Ê\*•” ¹ÉÐCÛ:ŠÚ£ƒ¹ ‹_ § wQÔ¨ø€|T‚.×?÷ØnÎ î›xg8\[D¾l€"§,‚úmpKßPT*_Å8Kð:þZD—1ÍÝ):ø†+„ö»ÇÓÑæ6iÎÐ~‡´i¿C@õ6tƒìÓæ\ÉïGCTàÔ…… uz[Bý\̵¬.&N[œ-}¼N'5åJî_hÎúªì&輨å4?f> Ê~*ûÙ û™9t6ì–£íwÙ@‰õu) o- ¨JÒ$»ºíEtÈ5½½ÒN@ãº4aE*Ý]té*é_¸šû(#"ʸ©FZìêI}‚æ"Cß®T*¿ã2jèÍÐ7´˜æÎU1X(v( )²u#êQ*N\Æ-ÝӈЩ•\£õ²×ÕjwÓšT›Tòóϼ5h%ì¤è/ËϤm»ÓÜŸ™öíú ™'©­Òávj %¾:§‡ (Õ`¨Ôýçײˆy”/CcFwóþ­wÏÝ0|_‘ˆÖMŸ q*µÿãш×þH@WømŸ—¹Ê~*ûÙú©ø\¾gÓæ·¡¨âÜ!ß@nÓj–€*Îóì\e?焪8¿é› Áƒ›Tq¾?ÿ(ŒhDPŹTþÚ-š¬éF†*Îû[ Äfß]‚*ÎÜ,&?$¨âüÑ—+Ääú‚þm¾Œ–¹d<\¢¨âÜ£ÔDÐöúDQŹKÕ¡¶• Cÿö~|?Ò,v1Cçök¦Ð^> ýÛýúŸ…ÕåL†þíûÊ~*ûùßÜO«ãÙ🔹Ž~6™Äа}b3Û‘\wÚ~íìhBÊøö QúKI|Z.E+ì~Éj¬í¸þ#"…Rý•ÕNÉ&fNsû«/ ‡Fº1T÷MSæZÿŠ RùO=V#ñ¶4¯íbRžÆ¾ERÆý¦èšø$2\Ö†¡§ÎͧuÍ‹hY{:lI"AÇÜNšó(zdz"qùÖ‹¡Rù&tWg+[UP´GF{&’¡ÜüïîÄhý2@3í[Âù ‹¸×îQºÛ. [Òöòb2 R÷cMÈÊ\×3Tæ-¾W[ÉÍ+u€›ûRtЈ3dá†Jí?Üè7é#¯!h¹êx¨°Š+õ¾²ŸÊ~þ/÷³pöhÚwÚ†:éï¡:ÅF\•Hw˜bž@P ^à³æ€€^Þh¯3÷W/À”Lê™@Ðë¿:A³™žÜŒÙV`·;LD=všÃ¡‚Y€öÚ½zN§¨T~µ’x2Øó4A?˜å“fKÎrI¤Ìîh#†åF…øŠº´_.$ýi è”^edþŽ^\yìjúnå1-ß3€È T*ß–¢¬ã¬`õ:Õ’iœH&è+—)Ðkð(@?‰2N›[¿1Þ¬¬$hÚ¶­P[¨ÂP©û“2ÚÓ7U zc¦]ÕÒƒ[_øJÜ3©ˆ¢?W&Ó¼Ù*µ¿ýÒ¹pö™ E×çtŸ}-ªì§²Ÿ ¹ŸÛºß¢ÍôÕz|÷z=¶¢#ËgBÁŸf\¿gC`œÚ>®FP ÙÔÿAg›Bý qÜ h²-•¢Ëg ÆíãíCjœZ•s•ª ¨T~_Ó¯äð‘ãM[«=ývsË2R¨CÍE{tlI0µ´w¨3LxÒ Ð˜ñá˜×‚Þ~Ðøô8®§¥|ùr„¢RùT}Óâ½MU;L7Ù[s‹ãwãÍ8ž ©›FŠè6ïr{´?Cc‡·a÷]£¨ÔýséöÍ® ­Q‘ÓAC¸#DÕ z€¾ž§÷|í¹RûmjLɧŽÝzxO­y™CQe?•ýlÈýT³jÆÖõÚBÑÎ÷[³ž4¹ÞIía”U9×ùX±»ÿ…kcnªK׈è.'pJ:@Q³—ÝÅ€‚>ܺØ$ÑÁÛš žŸŽ¥7»zi‡7˜Y¨Tþ]Ë»ÕÎÕý3Ú ²N÷äfYšˆÛûjè´@ŠÀ]ò8 njzRt¼ítx3¥#C] …>íFR4O½ qŽÙIP©|¿ô…ÿg(íær+woƒ°Æ')j9ÑÞæë3´d5kj¨Þ´0øZ0Wê~ÖÇÇ´£F?†®}YGcºp_ §£Œ O2·BÈ–XŠJíÿõ"îcFP7­|1¸ÎPe?•ýlÈýT}§Ç*ú¥Û£çÇ´e!¦í ¾Úƒt |AQߺa“ÎqîótÒ¼ò)W§C¡,Õìø¿=jBrßÔÊF lVýæFÚ]"Ã_ÕQ4Å5J ¬KÊE¥ò»kÈ`ZŸ=ºè;€Ëå 9ÚrXw¸r¤? ^ÏVAMñ/‚¦Í¯& ]Zë—$ïù~·^½' o: >šà`§,ŠJ~ÿ¼ÿ„^òt|l¨ÕŸÀM÷z•ƒÚJ‰.Ïýœ«F%ôé¡Mð¡R÷‹&6cÝÆh1´8B—Õ¦~¡èü0 0¨pf¨~àKyÙà \Éþ/ü@ܶ¹Úe[¨5mÁUöSÙφÜÏG_4Yí¦­ü£Âêéc®G׿¢Ó²!M‰X/Æn—£Nª¨ÿ U±ªkîo#¨†ß,ø}c!7»t1¼óv§hEuy±ôA¿XöúOET*¿Ê9–ªQaÖ0¯ÅLŠú¯Øu¤#A c¶YÓu}ðò&]óþ×ø¾«èe$C-6t–/êx‚¢E3ÞŠCzgT*Ÿ·Æt¸WÐаÁ½°7BG“ø¦;2Ô­°J|¼Ä„[ðÌQXui=wAQwæ^>†¢R÷|ëĆ~¸NÑÑ&,'/×K¥%ëR™Èm‘܈ýºðŽ Rûã<’á]Ìp H[åwº3TÙOe?R?ŸË³žÐ»gµUœçíO–÷Þ¨GPÅùÙª®¤+k¨â¼ÖφŸhÇPŹÅÙ^ÄdÈaŠ*Î¥òÿù` ÇeIUœ—¶[ §ê;0Tqnòâ1𬠊ó«­:õ¨v€þm¾ê-±ð²ôAçCžÇSÕ_O(ª8ŸÓ)Žú4èßÞÔ‹=ROQÅù½…W„õ“Vú·ûß~Ï'¯·ù1ôoßWöSÙÏÿæ~¾ï” ÿɶÓ[Bã‚ý nkw "¹½5›²g!Mø©Í> mDP«éÐëîaÞ³ÕQ_ ¨ë¬$hƒ\ÒÛç$7B­ž–| è‡[4rø`@¥òW—ÏkPôìÄQ¥WŠ&Ó„‰ßQt°¸…LZÐŒ¡Ÿ}%s›m4E%nOÄU¹Ó\6ùV5A-KÇgÆG®T¾²IO©oOÝٕѽ"ja`L¼îP4P|™Ûhwå›`q£A˦ڂû[ †JÝWqÊÇ<'¨ÆR7±ÑÀ]Ü‹QØò»‰ý9ô=ãÙP©ýà:4_'èó=þ RV,G¥ÞWöSÙÏÿå~ª¨›€MóI}÷°7xŒjÃýPþŽî{ðž[l¾‚NYMíh3ªoNf¨k^Oâ–?—{kkWxpÒŽ E–½@¬nJÑukƒÁwV AÓF@r¶ •ÊŸ÷h£èuF@U<¶Êõ¾A‹,-äòÕ½j;æýšÃu?™?ü š¿;ŒÇî¥hW«Uðô)×èeª}ç¦C*•ϲYí8³’ ê¥—Ä ½Ú:ïm#šÓ¤3Eô£EÃìÔƒü–¥æÖRô§u#j|½^@¥îû> “¿:p” rÙöïç¹+ )Ë­ßì Íõd\©ýšœàÁúF }¹c7yúÌŽ«ì§²Ÿ ¹Ÿ†.–pöz‚€t±³5ö¨QY’Z°ЂaÏH×%«¸çߤÍ+Ú0tÑ/æv¯‹ˆ:­~JJœ¥èåù‚ݦT®ïºèôynÖåm®’R‚Jþýêë&$‹$¨Ë mrsj:·B}' ·½) ¾ItmÛǽ\鋇2”Ž~H²UVp«_4c†+‡´X~œ¨uY¨T¾;m©ð(®' *wç=-¹*»µ©íƒ‚Ž=*_ß½’{qA9k}Ÿ»oj?Þî°€JݷȆµ- èúå‚ie1×´"Jêš ¨úI_Øò¹+C¥öŸ]áK¡G†ÆßüB;&tPe?•ýlÈýŒÔ*{nÑ=¡ë¦¦MÐs‚ØÆ3ÝÑm3TE›pŸŸØGã3j º,Xƒü8Nu*} zÄÄÊQ•'Ö¹pŽ y•¶Ðx@/®÷»käEQE¥òú/#ûÏÔ97“,¶’sÍž®wè¨û‘Jr°p묫ÅoWÆ14àG ­2¿GÑ -Aå‚V^u„¤ó*•ÏÛûy«ÚÐËŸÌ Ä« ×`so²ëõ¿ y}‹ìjÞ…{$¾†´<¹Ÿ¢E› ŽöåJÝÿ¹ Y?î1AòœH±Ë3`b¸~C†ÅÓÌ)*µß¡ûZºxdW@§m;Gâ×Ïç*û©ìgCîçòö]À=vE©G[Hò[ËÍ+N…ËÎi=;"FÖÑõZ]`¸Š9 ã.ƒ]6éU=ÊHy€ oç:ƒ§u!AU®_²ï~t¯€ÎÐn šŸ'¨dþÄÜ>› .÷Õá~ÚnnKaT~4Ôóð&XŸ{„ ª^ÍX±s¹€ZZï¦wâu8ú„Ž;Ñ™¡a_îÒ¬˜€JåÓîå­#^tÞµ9ÐîÄv®éTX¤©èÂÿǾF帾À/Ò$C$$•!’"E’û:$SE íT2gΜhD’I“„¤î난Jd¨²ŸÊ~6æ~z»j@Òí]"GöЏ_‡ì‡§Cos¿ ƒýÍíJ®¯?Ïo"z>ÊTf¾¦èæÐõÐm!¢±GÇCÌM†®ÏÇší£hYÐL±Ý÷\©üÎû:‚ûì%U³ë Ù&Ü:í—+¢…]£ ½â3E—dib:Ф®†¨áÀ…è!ð¹¥'×'.’ö{PT*_àèùð´ôo]?ëß¿1Çû‹¨S»Pð¶]HPç‰Ñ`uìé %Ù)zÍå1síÆP©û‹›!’jzmÔ9’ò¼š{Gµ+¸¬o赂ih˜& Rû'-Š‚ë_ͺ<üɰXËUöSÙÏÆÔOÅç¡ûUât˜¢ŠóS·ì`eá|†*ÎÌ&cš3Tq~zéòa„Cç×·§[ž| ¨â\*ÿ±Ìû´A@çë9‚{­3Cçã–Biês‚*οùì'é\ú§ùŒT¼a\©Eçó†…AÂô$Š*εn§új9ýÓû—=$&Õª8/»` £æPôO÷_Û¼ƒn3œ¡ú¾²ŸÊ~þ7÷sgL&ü' ï’y¾ ]¢¿™¼3õãzM3=wâìêË]]3Dæï ãNï]N ËJ(Ú6wth¿OD7š7…óƒ³)z'C—^zåÊМø{ôIÓÇ•ʯ£zêÕC ê ¬c/Íè³Òœÿ¡hÝ:O¸}t,C»L²„™›U œo–×u)jò+l>䆒Ù92T*Ÿf($ÛöahÃÑÑà¾a"wÖQ;(~PW„ o­!¼Å Š®Îv…~Ç:1Tål“¢ m¹R÷Dzƒ¬¾Aí¡êæ»FqµB¡¸¡^Dßî ůµ*µ?ëä/ÙÓu-©Ž Õu‡ETê}e?•ýü_îç·¢¦DóÔ4†^üê,w´ôà¥ß”?x=–{yôšœiÂíãß”5ØÔµn}’m èW{Rµ"¢)Q±² ƒ\9ÚÌÑä ´êò2hyæ E¥òD¤Cê§~ X“ k·–p“~–öñK:¼î„x3ô/îì`-¸´œQô̺•Änû ®G+MÉò ¨Ù³¯¤ÙæW\©|÷ä-`Þ¯ *#²„¥Ü·ß:ÂÇÙM«|J,÷Wq¯šM/&ßЋk¶’’&ß *ùûë6lK ê°Ém_hH¯×Bü镯ù!f—dˆ¨Ôþ{ìéºî÷ ê>5†Œ7Pe?•ýlÌýô×jGegœz²0’ÊïôáúÉÕX˜MQÛ홎¸K†æyd¯çz fƒÙõ!ÜÊà6¤ÓíÍÝaü7Ùsó$7wÆaÈ­ß-s‰–;­%¨Tþ&y[![[—¡Ù×V@»Qƒ¸AGkh3-mn«™Œ8!¢îkG‰, •¢ùç&Л¶rtÁºÐÆ«)Ek6 "‘ÉÛ¹RùêãJe‰e3<Á‹F–ŒàæM^Grn½¡è¢J/h–qÕ6ØÃñƒF½×Tv}ÙMQ©ûÙ©nPa) ºaã ž&ÈÐ%™Q IÝ¢ò†x¦ãJíà½&ê~*¬²· ¨²ŸÊ~6æ~n´f´û·¶ ½8é ]vúE5Kš±º{Œ ã¦ÑîÚ}]y=¶¬&hÈæd²-¡è>5€Ú=þÚï^KQ›ÉÝ`ŽŠA[Þ!¹Ñé•ÊÿÎÜìßMd¨Ïë°vö®s¹{³²„ gùPkUèæà6ôHò ‚:–·!}>w4p\¡°²r A³›ZÀË1\©|ë÷Ý su:0tó~-6 ÏiŠjάƒÆFsã–xP“ŸÛE4ðëaÔ•!U‰¬B\b*uŒé¨ø&GƒWL€óÒE4ÿ%$8 Pí/êÿþÝ´•¢RûËÇ…ÂïN­zÕé1)® à*û©ìgcîgëy,g_EwßiÃúþèÏ-›½M^ñÀЀ¬£¤C³eÜ Íc äìD†Z: !Kü‚¸MJ&|Wç†3”Þó‘¢VÍTÄÇ‹¢ôÉ!-H½ô’ ’Ÿ_Qçȵ‹‹ZïdM|‹æq[ôý‹¼ˆ4gŠ9¤Ì™ÈÍkÞô+{q]û.óáÏ :¾­ Ô¿BÑp–+^-®çJå»èÛ‘=T»)¢/Þ¶cÁ{4á´/Í÷í½ÔŠ6yWÊýÔž¶Îä2[w89•¢R÷¿¯.Îê-î1¢Ëq—­êFv%qCÊŠf†ñ2TjÿH¿EtñƒA 5_Ô¥å¿Qe?•ýlÌýôiÑö¿-G3Ÿè³ùqª­Íì ü]p=¼/±âÖ ;NßùvaèÕGº,R7DD­Gî¡V ²¹cô½©Á„j‚^SítwR´GôVyõ]*•¿Øâ¡Ü|Ÿ7CÇ8ާ¥K¸öÿ~îX|l hnÆ.h°èAÐï±ôCU@goÝ~=¦èç+4úÜm‚¶šd¶C•Ê·kîoÚüÐM‚Þ_~ŒÞm0Ô¤á•ìdfk®ÆÆp£fÁ½UK| š55OHQ©û}׌†ûçRtpèHp븕¤÷N¸êZFÐÎ.z0»Ý®Ôþe¶ôÖ¸v€êGy€õ‹¹Ê~*ûÙ˜ú©øXŒmžœÜBPŹƢ$ðlrž Šó&Û.Ð;TUœ—¬%{Uœ{ö¥ôqq9AçRùOa G:d¨â<úãèºÄ“¢šïÝb]êßk Šs¯é âÛPÅyÙ—ïâÿ΀þé}ÁæüÈ ¨â<÷y¬>ñJ@ÿtøŠ@¸óh ú¾²ŸÊ~þ7÷Ó¿y*ü'©÷=ZàýFDîÒÖIËdÜY®`›|KD³ d0dU2E›dÄP›m:€æó!FqÛB37Ú‡[¶Á é:sƒ—½¤-útNQSHp²T*Âõ3‚8%œ¢¹‹M…Ÿ VÜ-Fó ‰e†† WS˜É1·š[p«-_7ïÆ=›¾Å¤¨× R›äÏP©|CsvL³4‚®÷« [grS® —Õ¬ûÀýðk ŒÐÔþewÒKÜBPM»ž07pWê~Ƀ`¸¹ÿEGO\eo>pÏ>Žƒ±Ó_´®"vÖgRTj¿—_™c huðZh=pA¥ÞWöSÙÏÿå~ZY\¡Aýu :}Tu=Ƶ1m™ÛÞSôoÓ"âîÔŠ¡«ìÁKÖÐ6gÖïyAõ}” 5½‚¹AÉqà¼þ)ECÏΧ¹6ÜjíÉ®¸Rù‹oyË\üãETåà ÙÈÏehõC#Á1x CÛ|È¥i:þŸ¾GèEïCÝ¡óŒ>¨Ð hº§sÒ=%C#[, ­GT*ßÞÖZ°óúv‚ÖÛvƒIƒ'r÷L©%‰‡UÕ°è MC.´Ò||n±‡¢¯4š’À^Z •º_>ÊîÔdèâ‹Áë];nè%cø¹v"·~Âs1õû@®ÔþÜÉS`õ¥4ŠºiU“ÄÙZ UöSÙÏÆÜϬ¬í4f¤HÐIß&Ð —Þr¯85U}uª¿þ®èRIÑ ß¥ð«d!Wg£ dk2ôB¡”êzs³¼—Š&yøݭ÷ ´‚ º»_Ð_% Rù},í×C?Ô!ÿªàEÇ4~bsæîfKÑ쵪,x|,A-Ö¬¤iËôu'£H¾“¹Úá 7l"A?¾í–ŸÛ1T*ßRŸ öý²€>î @q_u%jõü ºõç0°‹ù-Ccn¤Óþ:Zpþ¥°ïö@@¥îx; F.íÊÐ#·íaw¶%wKõ+šñ”ˆ~Gèð/š€Jþ÷! °m}!E+mbEw› ªì§²Ÿ¹Ÿ;ªjÅ럵í ´’GL2àÊu¢éÐÁ3(ºtS>H@ò¼ÈS †MÝt#(ê:¡–öûµž;Ó²j†t¾æ2|v1EÍv= õT*ÿ²w6¤*4š :ã#IÊý½Ü †«hÞ³ž€43%Ÿ<'qãÜí fàh®¯U,´Üû’ SêÅË¿–Stñ3ÒN“TòçÛdÜèÞ—¢/UzAŸ¼ÝÜ>íÀä“>wºVGèc³•ûqÑJ¸D5Mû¾‚:^§¨Ôý›©æÐoÁ@†&w‡LàÂêGdå4+@#£f@Šæ~‚Jþý×ßìü°— ~xû®²ŸÊ~6æ~Î}xKpÈíè—õ>¤ýõ^ܹ'¶Ó!6™ š«B]Ö·tsÀ>Q~픀ö0mGNf´Å‹OBû5­?o7ä¹gBL¡û‹æÜXÕ•à9òE¥ò·Ê($_\Ò z[ë-Y24™{Àª#DjÔÀkl±oÉÝq;ÒME4S=ÌóÚ1´ùÒÞ`sW. Ìa!™•Êw{HKè|ƒ¢e.wH¨ýnëÙIåî£Ü’µQd¥^÷äý-ÄnÇL†>xÜŒÝp§¨Ôý³k4 Ò~8C³ó_‘T=n‡C^`çíOѧ«ˆoð®ÔþÖæ ÿ,V†ìÔ³ã±UöSÙÏÆÜÏa‡Èçœ^€^ø»†è.êÎ ·kFÌÓdÜCëªIï°QÜðè—$ºi6A'Eö‹Ëõh¿ ¦™XôúgOøñ%¢õÍ*ÄìÎ6 ½?á:•·j ¨Tþ¢‹-@> ‚ q#º@Á£¸‡®ÄÀÙ4îãŠH¸éOѦú ÎÍ—¡õv 4™í͵ڡ/æ-Е_Iâ•Ï•Êw¨u8¹kô“¢«ünYIo¹=üš’> –sUr­…á“oЍÞå Tc% ®Áé>.\©ûG‚KIÓ1 -NL">nÜ Õn‚û¬- ¹"È, ~¨Ôþ+tÒþE5''ˆYóRDTÙOe?S?Ÿ³Ó[@‹9€*Î途eUœëØCÄ3sŠ*Î}ƒŸÈ»* UœR™ϼ†ª8—Êÿlv_°ˆz, Šóª©°ÛH“¡ŠóÚâRZ­§ÁPÅùÊç‹àÉ-Šþi>ùDô¹YJQÅyÐ*¡rÞBUœwž¹]DôOïg×Ï&wÅ1 Uœ;?È êýÓý›Ë·ßM'蟾¯ì§²ŸÿÍý|“ÿÉ›vŽàÚ§C¿íÊ,¹5øá¹A¢x¾r·Éþq kÑ¢ ¦À&Y…¡óÕ¬éÝÑ?(JuîÑ{3 ¨]ÇMÐùU%A×,[g¨T~‹GC!ðaE‡ïêßUzùE84++!hˆk<$†·Ñ´I_ˆ×†N(rcFŒàê¯?Gºg»Ú¹& Ôn[ ¨T¾!ݒȆU…=òŒ¼[Ì}8’ò2)ªÖɲMÔZÿ¾ dè‹€áЩrWê~°Nª`âÄД±Z²ÏOs[õ‡ŠÉj€Þà*½½ŠP©ýÿ<¡vû{4òWñ}R@¥ÞWöSÙÏÿå~ºLÓ„›+3Ôàëur'i(÷˜[ípòE *«hæH#®ÞF[²b‘Cƒ'Î¥9Š(:ÁÅŽÊ/zfQ%9–3‘«ñ½‚f/( hÎîUÂÍ‚€Jþ|꾑g_´:S#‡^tg¨O kr±U/n³E‰ð#PŸ¢S—¨A¬Á_ •ºïÞrhס«ÙgñóS®Ï‚öC®P®'’q&‡+µ_½k ÈùÕÐÄó@žAPe?•ýlÌýì¾3šÄW chÌŠæ$§~WV9=Þ› £7ÐÑ—žrc4JèÓп4×ÃfÍêè‡ÑàýHmnYúHq|"¢![—èG„ ¦on’±ýU*•?ú‘ñ#* uøzIpu¯¤¨ñ¼mdMÜT†êf@}¦Ùq ¿Ï§{_´tŽé\ÒLswméqru…HÐÂ.ÝàÆï§"*•ϼÊÞø* y¡`U¢!¢V›N‰3´Z3´Ù³z­²€¢k~Êimús.ußJ‡~ÿLP©ûß²Ð`¯ŒÛ~6P¨Å y½E¦IL4rKµyA¥öÛ õ†–R4{Ñ[²×æWÙOe?s?‹S®þ0¡zŸß‹­Í¸&7Ëû¶1t¹i 9ø¬/w¥Y©<–ûm&À†®\ljÓÀ@Ëš¡·Ò§’%.“¸zk剕»”ÜÒ…²QS*•ßáM[Y«Á‡(šÛÃULÍóåÞìTCË[ɹ/î5eáùGt¥NxTg¨çðµ0¦í‚f»Þʆo¤hñÒH¡ú$#¨äç«×8˜ÿÜ…¢./ ­Í)n“¬[4áÉa͈)§_–Í èf/]Tj èÊwË@åz°=*uÿ‡ÁfºåÝ?­¾¾®^q“[»¿+Ì>Û“ /+zB`ï…•Úou3Ql‘NQ•f&ò†ƒŽªì§²Ÿ¹Ÿ¹ýæPËãmº±8ƒF<ª§èÇ¡-!û¡1 n—‡Á|Õ:‚îm’WgÿЛàA¯·Uû+“nÜü‘Ûwë}ºôu{‚ª]¿MŠ"E™)õ´PÉŸÏ‹Ýâ\Ëk"jç)å¤ÊÐêoG肪€VÚ!ûv‰[í·Ìvµ¤è]mòïw¿¹N&@o³<‚z½ Ÿ#u*•/Xè ñï¾Q´Å‡÷Ä-A¡ËŒc©•O@ïõ%âÁö\/¤YòwŠ›ÆÊ7¿µáJÝ÷ýu”f¥hu”œæYo⪕o"õƒê¸áEÆtY±'Wj¿×‘ rØa7AåÁ­áç\?®²ŸÊ~6æ~ö~^×Í£èÛóuÔyßtnú?˜^àGÐeýÀ*¬ZŽ.:CT?†–†É|Ã]¹áGŠÆ÷m²B ò/õäúOÑ„ù×L¸jã#Á+&–¢Rùë·Þ‘gT©4ðs/áÝ_®jÉç1€îÚâÛã:s’×a-kl*<µ¸LQ5OoÚ¹_[†nqˆÞ©i¨T>òn -íÌаK§„ýÚsMüÃHŸ(O@ûŒhÑ7Ær÷ÇŽ$µ¯vôѼn:í½€JÝÏþëµâJQ‹ÀËÔ}*7Ѫ?Ýé{” é‡RÉɳրJí¯Ø>ÜÑîiHa÷RŠ*û©ìgcê§âóxàwºf}šˆ*ÎóµFCˆû*Š*γ‚oÐŽËÏQTq~)yôŒ%¨â<Ä£¦HKkCçRù“G6#å·¶Tqžº)ö< ¨âÜ'ü¤è7-WDç/¢FÁØóCýÓ|s‹©hqR…¡Šs_s?8ÕÂPÅù‡õÉ”%Ç)ú§÷_¡]&‰¨â¼&.v$"èŸîï`ÐNHpɡ蟾¯ì§²ŸÿÍýÜÒcü'w¹“7Kz0Ô¢ïYa{nÎö¹oÙ P‡Êh8ã´™ ^ma]Ä u=jz^Nµ½´~4ÜAÐXëNä鈾€:”?*ên5˜¡²«ghøƒ2‚Jå¿ì¿ê¥ÖÔÈÉœzÕ¶4íôPú#ª Sûu&cÏ›ÚU³Žœ­L ¨G;r]žÂ q˜7åGE´àv8ѹkÎP©|ùšSÁ¤Ý}Ú~Tn‹ háUR0¶– å©9äêm@kn¦‡Qô&ô¦²o•ºŸò¤@^×}E}nŠ¥Ÿì¹†+6ŸwpUº¶.RIyQ„Jíÿn±ŸŒž €~ü9 ú4Tê}e?•ýü_î§Šû5»Šïš ½œ&^îWKÑ12Q&¢ ïC ðÞŠž=u„ÄÎØÃÕiuB(?kÌ5Þ/€[—ž€ê®Š…v‰}úφaè¯Kp'í!E¥òW[Α×häù$jöHnßnÍÀAϘû ÏRR¯4kooÑêEԇĊWŽ!è’wéÙ5:éí.3` RùÚ ëV½EŠ:ùMªvßãšÎj ö€Z[»‚­ è¾0Ghp¶ti› ò«E¥î·ž3\Lz EQ•ΓEŸÿ~ùÿô¶3%»›–T»¡ ì~|—+µèøpöÉ]®Û*lj)ªì§²Ÿ¹ŸÝ#Úо¾íÑè(ÿ\ko;µCMÒÕàþtsnuSœWµ&è—~d¾)㞸¾ÎÚ¼¡høÁ¯äÒ°q Ui nk}…¢ÑmḨ‚Jå7Løÿlg­ Û[zyàNðÑ– ÇÖêBÍqG†JåKëpŒØ:ߣ¨—ýRú\ä:¹/… jò*\ÇLÑ}m¿“½ÃF2´À|;Ý»ý4E¥î×·÷å÷:‹¨É-{ÑýЦ ýëIäzÉäh3{;È/kÉP©ýWŸx’6§ë(jÕ·­ØÏ£ WÙOe?s?U>zÒó†íñy2­OD4µÝdjÊÐâ…E;ò›pw¿íUïOÔiäEÄ ¨š[;ºy’9Cm[¾¢Á)×DôÖéž` &Gõ¯}>Ì[KQ©üíe >ÆP½ÐÕÞˆ;ܳ-”‡§èñâ$R‘v•›ôÖ4£A6BƒV¶%K¡·uÂ(úéÀua–Š6 Rù¬|¯¾­b(ê0\[˜¹Ó˜Ûáw0Äùfq¿MsƒªH]†zljBǸVtîõvpàó?\É~6o.š\ùK@}"¶È'¹žçöÉ-vÅY1ÔͺŒz„N§¨Ô~5÷!@ÅŸ V«’’w;¹Ê~*ûÙ˜ûzl$LкeÝéÉCºú¤ ½4uE]eÎT﨓ˆª~uƒ¿‚Pt¤ÉßÄ>Ü„¡ê_Ch{º›tg¨ÉÆnDíÔC‚¦X w4åJåoÒ{#$õ{OЊ^±°zê.nµï!ðŠ¦Äž•}ìþIŽæG͆ió_Sô¥¶†6üãut̺r²yS6E¥òùøEËR4 å¨Õš‘ÂñÄÛú¨¼%$éÚ3tvŸ $-k·Ï øq祈&´Ž&³ ¨Ôýîáƒówë4Åø£,ú²57Õp/]éùÛþæaBûx*µRHðÓí& Î‘Z°3;E•ýTö³1÷3#¬J¬Ñ[LÐʽ}Ä«ëör?wlJ>°ã¦Ô¼”ù¥ärÝ]§Ê+δgèÉþ§it¯i½~Î ®&6ôiÎbH)Í‘£Ý[Þ%ÏVɺPõoêw倕ü|ñŒ‡ÝUåºqt<ܧçDtäù{ÂÖA.I$¿^îâ–}´ö¦ÈÚÑþ$.»KÑbËY£/ÔÁWöf&T*ß§_‚o¦A×µ˜GâØnÿr[ñWû~ }Õ$ž¾Úþ“¢>ߣÛëOÔå¬*T¥Tê~ÊìÅ‚|ˆA‚E!kçbn®]ª7´¥J,"E'mkFÏ.ÈQe?•ýlLýT|îY·vl)"¨â›¡cæ´§¯~Ås—ßcOŒZ*•¿Ã‘þú[š2KC±s 7·¬ q~•¢Ÿê“À󣸫ÖÁÓ£ÛZÜu´<¥g<׺½=+92ššfÉž:ŽT*_JœHkè!@sô~ÉæXŸæ?4“›Ó” õoÅO"¸jŽo…‚Þk=á~²:{r¥îû °G¯8Š~Õ=Ïìäê88÷ތëþP º;3Tjÿï–0Ò¶˜¡jƒÏÒ^cwr¥ÞWöSÙÏÿå~N‹ p¶-ÐocK ©o®ˆš­oBô½¨ÚïAvPÎýiËÞõ¦h'ã—ô¦})A»}ÜMcÆ.a¨¹<˜m^šLÑ!+怗ú @‹×Ã7õ¦ •Ê/»k@’¶ tø©Äî|nňbïªNÑmu¹¤SÎ4®Õ†Ž¬ôê$†¶»2Ew™MѹªÎðÖ6 ÐÖ²]°qO¶ˆJå p{DŽ?h×ñ®à:-“[;\¬;ûŽ &ê±BÔá¾€N£áx¢=C[Í%‘Â}®ÔýÔeV ¶!¢S&YÀ‰;‰\ý¯ýéç ½óv:3`7E¥ö«OufÄQT+ñ%ÕÏ ªì§²Ÿ¹Ÿž6%ÐÔoE‡ÿa½ÿkî/ƒ=pÒh ?{¥éÖÜ‚’7òê•z\ƒ[[ÉÒUeÌrèP®ê]1ððEn䯀jÎøIYPëÎ]Û{/90…û±‹)L|ÄÝq;…Õè2´´a0[{p; ’ý‘›Þ?I”l ÿ´ØÇ ï9‰…¨¹êmÛÜ·¹À•Ú_߿ްsK@õb‘Õê7ªì§²Ÿ¹ŸmŸ„Ðèî ]<é ¨¹s½m¯@ÛoÊ`]“0Šê¾@òUJYNâNs£Ê·AP¯b@·úý€¥;4¹ÛŽí" Ç÷´{ñ'á)Äs¥ò/íÙ‚ªÞôB¨8UŸãöŸ°®îv¢FOÛ°Àê="ªb¼‰Dï´¡a6”LÞÈ-ɬY1´ÇÁ¬zª •Ê7¬ìœ³P´¼}Ä 'èšà[³©€^iw :¿2æ.¦ÂÆ€}ܪ GÁáäh•ºßõcW¨õÚOQï<˜¨Æ}ÍNCåÇÝ€VÉ~‚ƒó•Úï³æéZ1‰ +ÓÉÖæ{¹Ê~*ûÙ˜ûÙ~ÈnȹÌЋà€máî›úêsï´˜ f‡€»ÒµÜv«&hÃßÎ`öºŠ{íàhbÏÐõof@…µÈ]5À òþ¶´fFŒhÞƒ¡RùËtõÁæõ‚êi ‡ßEs‡½2e-'Uq“üÊè–ë+U©Ê€YW³ êîT ~3¢):ú.iÎÐÝédГHŠJݺnþ#3)Ö·3èÉq¯­É£Ž{ZÖyŒ°oÒ ®Ôþæ2m¸Üª) ¹‘¡´ô&A•ýTö³1÷sbõT(½¸‡¡%i–0A3ƒûö©ü]ÜYc£Éèp®ñÄ-åí@Ы?ö€|G2E/ ¤9·2ºÄ•¥ _(gsœô;ÈÐýCØwh@¥ò„·‚u·‚ ºæqs¨ØÄSÝŠ$ÆäqÊ~Ï庺E€¹øž¢ïNö†u½»0´¯ö!¨É9ÂîoÏfwÚÆ•ÊwfÉ0¡ÂÐï¯fAþãN\ÙÔ«Plí½¾m?´o—Â]<¨žDk'Pt³$̛Ӆ¡R÷Ÿéê¹6Ï|Μäºt&Ì·@Ÿ¡·Ëõ˜OÖ ‚Jí×ê~rÕûStÛH㿈¡Ê~*ûÙ˜ú©ø [ûô6Éa¨â|³¹ñ_ZCQÅyð°¥àrh2Cçß:1Ý•Ç ª8Ož0Ö§ª8—Ê?ýŽ6<}²‚ Šó7Îç`qñz@ÿ ¢ H×¥-Wáñ®¶gyý¬ýÓ|‹A Uœ80.þ>ÏPÅù—½ÙYërýÓû+Ûw„¿×É)ª8?[zDpÏé èŸî;9†˜^Íb蟾¯ì§²ŸÿÍýÌòmæðŸ¼25ŽF”ˆè§U¹pdÒ"Š~0Of‡|ÏqS2ز=k꼦œô{iÄÐwF‘µ±%]⸄ŠÑ P¸…¾°ÕãzN îk3¸A ƒ˜›j+®Tþ,0`»ú.¦è>^lv×ù":wöf¸#ÛFÑւᩊ>ãð‚ùŸ¶rÝ»Ž!zwríö7ØÍ tíÅ2‚²¹Rù>kž–éØt‘ëAÁÕ¤?w…º8ËÚê`l 3.´2YFþ ËÔ*eLS핺oKŸ@Rý-ìò*"&R´W—­pýp×ÙÔ ú|vb¨ÔþðŸ¥ð¾Ó-‚I¼Á [3TòßOÙOe?ÿ‡ûi` ÓÚQŠ~;¶žvþÅ58µ…M› è¬K&›u”;Ùz)©Šñ¤è¹Íéäê¨Ü”+©û™ˆ^ÿ!œ¿^Ç;ÀšT­õghžÅyêQÑž+µ_mÞ\˜Û<—¡72\éÏý\e?•ýlÌý4µÝ7ô ÚCu°C¸VÓ2Ä*çéAóž÷¸{{üEÌvWR4`PKzÛûWo°)Ù`ÝD@Cø©x¿)B×OƒœH3‚>té×/RT*@¾;fÝP{¶$FÆ <Ÿ$ÅÏb¨‡í :ƒYp2µ™¾*A u¶ÓS¿_s'˲¡Øv¾½óW°øñ‹¢RùT,¬eE³ê>ö²ì«Ï{ÄÏó“€jO,$ÇýºO·”Œó¼JÑáuȯ †JÝ/žrôÆë1ôJå xâjͽ°ßˆÅ^¬¡3–Òüf€JíúÆÖv´`¨¹×&–{·– Ê~*ûÙ˜ûÙöÔJ°ŸïÂPÚm ¬ÕŸÂ}»:ÎZ3ô8‡p]zj2“¥(:ÿAÆÂ¾ôI¦=ó·s„Én6èç:äÄóÙ Íù9‚9üÎ)B%¿” ÉÞ×4­ÇVBCZsGɯÃ÷FM`çˆÚ£6 •Ê×áE– ÝQP×.Èvž×ðC)Ù!™ Þ¿Èt›Û\›«Õ4êü&ŠæïýNw|ì ¨ä÷·† `Сû…ËcfsXO'–{Ww?œj?‘+µß!ãÕ Þ èçÝ`Ð¿ß Qe?•ýlÌýL{.@Â’Ù =šØž-XÌÝ6ó=|Øs ‘Ëÿ†´nöMº;êùÆ:ä¡ øø?ß÷Oƒ \ŸüBÈx`Ì­P+¼}ŸÀ]ÔPðãA¥òßÊ ì¿¿дn±ÄpÀ®ùøV¬atw®l°=¬¾‚«}ðùÐó;A^hBëϸaÉF¬nl E'lyJŸïTò÷§w>1èà hýÁÖ°£l"W~¤xxMÐÞ°Êç\ášÍj ©{u®(‡²„J‚JÝ¿8<dµëJÒ²`^ö®|Iìvˆ#è¾Þ tJE¥ö7q=Î{Í-´É€†Ù)"ªì§²Ÿ¹Ÿÿ}%oš3ôbTù0òÿlm^9•ý¹ë÷@ÑÝHîRpò[è‡?¡vñ6uȯ‚“ýQtê¢Ý 3$¡QÚÁ 10€›°ˆ^~/ãJåoZ¦¿T“ÍÙ1œÞìä>hANÎŽá.›$€aäFîvsè\u— º§½Á±ç î„oÅð\hüü¹.c¨T¾Ç€vO@£_†ÃÇ„ê¹æîGÐ&û2 æúMí”}Î/ahÊ‘õEyàJÝÏ{¿ŠÇ&0tŸK ÝÏ%}GB³ñs¹Yc¶ .VýŸûµõfAü U†.‹k­¨6WÙOe?S?Ÿç ’_S–0Tqrb8–%1TqþsÕC8›·š¡ŠsµÍ;Œ+ Uœ×ά¤ç¢'Tq.•ߦs( ù ¨âüöÌXˆI ¨âÜB%ü~èTq¾R+žŽq†¡š¯jm,»ÚPŹlB&‹qÜ;ÄŸ,ÊÐbhÐâŽÄ3ð:EÇ|‰ezZ};N—Ùå&ªvo[­V+¢_mM˜ÏÑ®€ºª âÆÍÜÖŸjaØ)G‚Jå·»5Yx®»¡uæý¨ÅMYê)Ï­n¨ÅßHøÑ.Üô…qdõƶ\¯ñȱ¹/ úãÞq(þ­ÉÐðoòôùt®T¾©ưV°eh÷¸§D£Ò‹(xÓÔ“¸î¦yÔ­Û&îùp¸,ñô¤Ë°Ò½  R÷×Çgƒa +EÏÏÊ›¸ßM;AµÝd†î[@—¯æJíϸ1˜ŽûСNµ´äñFŠ*û©ìgcîç’Ϥ pA{ü&ß2#¸Ñ½Ú‘Ñ ž`CFz¬ãF$g“òý§Ý1}˜OŠâúœ[&?¶?‚;ÎÇ6l á^߯«ÝaèwǃlOÿ*ŠJ凇ç©ã¬`†¾¨ÓcwVŒå:½Î!ËÒ­u« \Äý~é=I;wŽ YåZ0üÌUîëVFÔÀdEëš)³–ÉQÉÏ¿VcÈh«… M2: Ö%.æŽ_jÍ {»s'{ÌgúÁ÷)ZÖÐ,†Šh¯ÉöàÐÐJ†JÝ_½q?¬žr”¢ž {À~Z77#˜¹Çp‹»z³‘~€Jí÷y Áªæ4àu,í\€*û©ìgcîç o XÚlArÚ@û»¸mF’¨ 0Š:ëM®¾ÝËýZAa‘õ ‚ª·8!—S4ªÅ6øÜ]бcSÀ=ÎG@µWÿeiE€Zí=³¿=&¨TþQ*„%¾5f¨½7S›s…¢%ï¼ Ã:sàÚÇÁ-ÃM±éunq./‚…»öpM›O¦®Oü¹•_v}æ¡€JåÛòÒ © •/1eó¶[q_V¬gAí¿ÉPãóØç#ß jpè/¿ª3ôæ¯h¡g»P®Ô}Ù½PrúE›Øm…Ž]›1Ô»ÅXâgwÐŒK…ðÛÅ•Úïd¶ƒ¨¿ß辦ËÁó‘/WÙOe?s?utj›CÐ^‹­¡ÕÌ#\ëíBê…S]ÂŽ‹ÏZpOg{Àü€¾ ]:S$ñÙ]¸nU›aΓKLqü9ýúËÍ2,Úq¿NÏ"¾ ])*•Bê<–¿°Eû=b&·ëÔííaX»¢Aœ!©]½ŠÇ¤FýµvB7ƒ§ ”Ý„y›ßÔ¹t¤vÍa¨T¾]§²Ôw)ºçu ^—&¢õú²Ëå~€~ ÝGÏëïâÞ|9‡é›o¥hm@[vÎo+ R÷m,"aþ]†ÒK!pr!7uS”õk!¢Á/‹m •Úo¸ã8ØÝO&èËñ‡À3ø&E•ýTö³1÷Óî„c‘ÌÐÁP©|Õþa,òë‚¶ÎXÊÞèÛÚv¦‰¯ÏäŽÈЇÙp§±… —Ëýa÷ü«Têþr—E°j¥)CÇdûÂç¾–Ü ažäùéÃÜÚmÙ›¯ƒ¹Rûß áßßj†¶Qu#Á'çs•ýTö³1õSñ‘‡Î‡'C ª8ÿž¥Ãb#¦Tq>pkñ2¢¨âÜôË'ºÍê+EçÇ›w¥oVTq.•?•tgÙw'ª8/t%ÙÏ1Tq>ìjþrCçá#áàÎ@ÿ4_VG– ¨âÜh×jøj ¨â|ÝÈ(Ð1ßÂÐ?½ßïþ8¨÷¶a¨â¼ùîάlR®€þé~³Ók(td蟾¯ì§²ŸÿÍýü¾IÃá?yÎ5˜=/O'¨lå|ÖÌR Ð2ö¤ÞkkÞéîÎáFÓÖL%%œÛrñRã°;»h}µ AŸ~Q#'ypé®þ¤µÙ\@/> çc+ *•ß®Oø´Õehy~9DÞ˜ÌíÞl 8¬š$¢Ãþýw:8¢Z•Šà ªâ&Œ²èlCmšÙMD-’&Ã’þ•Ê<ü4¤y¼Q+»lXÉ(ZøP&\Ð#hƆïÄué%-në.{1ÔàFy`öˆ¢R÷s*)”êš9ªvØ«q½î'BG†ÆnÓ†W‹–r¥ök«M³à|†&™m¤jò¸Rï+û©ìçÿr?¯Mø‹ÝjA56¯r×÷ë~ñ\Ú5îÊÞÐþ^wÝæ9å2“û|Øv(ñ>@еú6$wÏDm(—g­Nç:,CÍ-ܼ™S`_Ž5C¥òo™~߉bh/Ïp˜k Z.§èT##P©ŸÎíµ÷;¹] èUë-ðb€·Cû/„L™ÃÐîLJ2Ͳf\©|{Ò¶ƒJx[†ß ‚¯'pÝÕËÉÙcÚ¥¶çH‰ç|®Æ€½¤,k'E—'‹,¾r¥îïZtÖܹIPgŸs°ðz2·88i°õQ ís,ž.›úŽ¢Rû#ý6°É§º1TEƶêÜ ¨²ŸÊ~6æ~Ö.4`vkbõWIã?îç¾í± ôÏçsçÞ9“š…q³Þ,‡ mÚP´îQW1ßÌ ^{[l²¯’ —ýü‰SØd@?Ô$Ѻwž mv {qå+A¥ò;?rƒÜVú±}SèY˜ÍuõíϧèÔn–0ቿ—â.tβ§Ðu™¢Ã6·b¾o´M|„MÞÉ•ÊwÔw èy c¨Ê˜°i(÷ñóçäcä1‚Ö½ï6q÷¹Éqª¬ƒÆWÍ™›K§8{*u¯Ñ9(ÉOPGy)ìp{$ -ß¿¦•·Î‰èÊ™ÿÐßý.TjžîEzlc  f𝛑ÀUöSÙÏÆÜϪÃóè“Cǵ³ Ÿk/rWVƒŽ¨Å_*CzÊÐCõ/IÈ¡– TÕ•{£íÑÜ­ðûF ó&—@ØøÓuT^êºsm8ôO{( RùM¼ç#'04÷ï}rM“\‡ÁÝ z©6÷žI ™»z(׫÷ b¸S;¶‡,• ®ñ¹ó°qCE“ÒÚ@CÃj†J廾2´ò³aèåûd¤wG®­ÞXÈÝu‡ ƒ[…CéÖn°íh¸ðïw%ÔqÞ5Ð1@P©ûKœÏ@q¾§ =V)BMÎ^íðQ‹Ö˜Ï45ÉÒ¦lçJí?}f#äkŽ´Ð$êTÙOe?s?ãT¦ó¥€ž©zEÔ·žã^ZXó‹ÞP´Ö:–e¨Ç¾!°úý9mµv,D§¿¡¨ÝKª¢ÃЭB‘K×s‘+¬ñK§¨vÂWÒ<Ê‘¡RùsgùÓU±! ½àYLÃKܸsN ò㾸Jé¨5nÜÅ7Š©ÿ΋Üúû±,ßo—\p¡Ë—wæN|MC&×ÊQ©|s'Ü,~¢èÙØxáîæÓ\ÙÃdp° Ѓ³²@>:–¢¼¶@|Ô†N í UåJÝ÷< Ý¿÷ è^¿ž‚M®©€*Î¥òÜþEWV§¨âüÄÑ@–m4 Ðÿ_>ëxjj"ª8·øR±½_‹èŸæë6ÕŒÜü>¢Šsäîе[:Cç3„BñzS+@ÿôþܶûÁsn Eç•ÎÖìƒC;†þéþ«N}háÌÐ?}_ÙOe?ÿ›ûi±LÓá?¹Þµ=Whä³ÔjÔ>îì¶giñìÁ ­Ÿoƶ¯}EQkãf@oMfhJQª°Vr õï -n{µ5àÕ;‰ûâJ&-qÔgéy8Pº‰¡Rù ßê@ÓÂã-|d¬Š¹ãlb‰á]@Wß~OxZqó­³k¾RôâÙìÍ g#»’EîPôŽ®6s¿?„+•oobÙ\îèz›¸öȵ=¬JÞXâÞŒhÓ’¹]/Že…F´ìR¦x69P©ûí'‡“ Û45Ȉ!ÝgµàÖ¥Û“,ýnóÌdxdÍ•Ú_  dž÷´®S>\ÔCP©÷•ýTöó¹ŸkûÒ¬G€š-é-Ü=vûk¹óiµSDU_¹²ªÃùí¥ó®öËК‹XÈЊê¬dB«cûª=[••µnád;6js>×ñåfVüe Rù§øXÃR›s­ÿéyëE®Ïjc˜5¤  c¦Œ‚™ÇÚrCë{1D®J\_qÕ¸LnÚãÔÑ8Ž;ÎT„’ª]\©|?ºCÜo@Ÿî÷íÆ½í½^´[Åýøl4ôëÁ="ü{óµwô§lHÔšKQ©û-—%²ø6€z|;@ò~·çÚ¶¸‹†T]=Nfè0Tj¿-„sk[1týc?0UKá*û©ìgcîçüµ$ö{& Þ?%…ú©\»]FÌhœ#7»U™ ?.S‚vœ e‹Cdèêä¨MßÃЮ‰æ¤YM"·›­¶Ý%è¨Ør(ÿq•¢«ÞGÀÞŽ3õü+ÔPTòçóe,ˆjÍРªÐîá†X‘ë'rƒú²ì¿¸ë¢b‰Uï1Üû'ùo-¸î77ÂpwŠ®]§c"V0T*_íîípyÔL†ž=âÙ7£¹ëŽiÂÅ]¿Dtw×öpÕ¨„[Òâ0PÕý€öSoâÐtèiŠJÝ^£+ì=ÿª;\®³çnY]E´×ânÎÉûÿß®Ô~ïC R]„Ú×öƒÏO")ªì§²Ÿ©ŸŠÏªìí™z ŠóiûÁõIEç—]KéÍÉö Uœ;8o‡À–É UœçuÙI¶¿ëÈPŹTþ&ofÀQ¿™ Uœë\ÝÉléÄPÅù$ßÏ¢ó¶ UœŸŸÑ¹æµgèŸæëhÐXÚ.†*·&[€Y¤6Eç)# Ã…› ýÓûݵ•ª8ïâñ w›QôO÷k{Ô‘£~ÚýÓ÷•ýTö󿹟µþ“o¬ÚBhFC+¾”“y7Ó¸zuÞ þ× @[]=×f¤s7ÎO†âÆËP«ÐD0dÈÐI•¾²ãU5ù牰zc¿ÿ‡ý:ª±mûž$EHæd!dJ²¯óÈœLÉœ$„„" ) ’TŠT’JÒ¤R{Ÿg’$Sh !™CB·á÷¬õ®ã|ÖÚïz×òßó´öõÏg­}Ü×q|W¾w »—Œ€ f 5é4‰­ÌÀË¯Ó¯Ž†^ßFPËÊ<:þîÂ<øùn ùá™ðÀù7Aµ¾UÓø ózŸ­b5ûC(úbóu˜}v·Œü"6[B*–oÑÆ!`eªËPWU°î>‹ÛN6&¶T'hd䍸G@C§ÑÖ•w(júc;ûôö,qØW‚v» #ôO2´G/ évóÛ_”v–HæŸdèçlÏ^_®¢ŸŠ~6å~Vvfu¯š1´vS$ Lð§è¼9&¤mÊx†Þ[•/œr—š÷£Ý :q œ(ÏÕ{û^öns %ÝÁÂÚ‹ûm‹ºS†J; gëL¹bùgNÎÎÛ›ªí*!•ÄškUâNú/Û@Q—|mâÍ$¨Bù³Q =w)‰ä—êp˜mJ‡žÔ½Ë>XRÛ›+–/iÙT6Ìf. ’Ė앾תw8õØÇз!SØÏ͇¸o–,€§Üµ»ô ÚFl&¨Ø}‡¥sH Ÿ‹€’ÌÓd¡}×ß`Ý°É ýžºMxyÇ“+¶ÿGÇ–[@Q§^ì^Í@ýTô³)õSþ¹÷ù s“iÉPùy‰÷>ÉšQÝ*?w™iVT~þzÊVÈßPù¹Å‘ó´èÕm‚ÊÏÅòë4‹ Ö[•Ÿ¾){z¡˜¢òóº×»ÉH›N •Ÿ«úzAŸ†Cú·ùZ^XD3GŸT~¦Èôr3T~ÞÊ-Ü¿G3ôoï[®|Fòë.*??±n û¦Ú¡»_ß¾5ìп}_ÑOE?ÿ“ûy­e+“ÿŸÛdBé½Þ =;:º¾6ã–d´…6ZS¹¹måMÜ„:Ó;k¶§Ûìøž¢ÃËÜÀJPô[p2èuIÐÇküiN²7 žk’!bpÅò»öé ~¶;`ö†||rˆÛuõZÈÈ¥IgHtv×ñY'A«å@oh"Õ?·r#h<RÝœ»~LI:XÆË7Oý6ôÝ|¢wm¤ðáH†¶úaÆüÕ•÷mÍÚ¯àî[G“>Mf¨Ú“Xš5ÂK†ŠÝßÝ:‘En•¡ûûžg^G’4Èk$›wÌ¡Ï?Ù³P‡q2Tl¿ß‚±Ì Т! t>S@ÅÞWôSÑÏÿæ~ªU‡Àêã›Ú'Ζ•zq]W×Ȇ–9q_ŒnƽžÅÕ¾b˲%&Õ:ÒŸé¸nÔØ>æqƒÕJg¸Jc¶JûιÎÐg[ݘôž+×mÊyRZ8’¢Wb}hÎÄ\±ü÷.¡ƒÓŽ1ÔXòž^ö÷áž~ÅÎ×›R4µsûåßÐüˆûPV™MÑkkb`Co†îN~CŽ Ì-)]*þžÇË÷ñÓñb¨}è}âôÀ“{å¥>ü¼á¨¸;6Ü•CˆÙä(îš°{ÐjýZ®ØýÊ_ÿú»÷»  Ó‡D²®åzÜìé¡$2åA½SHÁn-@Eû_4ô2dÜ'sáfn4EýTô³)÷Ó ý©q‚¡×¢Ž§ca\2Ï…}»ýƒ ©…ãYç,g@Û¹‚ï·^ܯ9Eþc9EgVžfG³ÔºdÞ8¦æ hrŸ ê5m-wÝÙXxwÑ+–ßvüh¡²•¡E×3+àî;6žÍ´4QÙKVsUƵÉЄ»§Rš;ÌŒ>É}í²‰%^ ¤èk ¶H7P±|19cˆ¡ƒ#C•:XæüšÁx.8·´³Þ)èºkA~ƒO0¡oß–ÿÇÕ\±û-’C™ã±€†8Ì4ïÙs}_K`ÀµñܘÂó w *¶?éI?hð™ÂP›Ž§ÇæºrýTô³)÷³>¢Dˆj b¨ÎÖƒ2?îÐò×â, Ï⦒7³¹—»øƒ¦å,†f úHµm¹G"÷å¥I€Ê>î…±†¸*«ÏAËÌ UJ›c[9+–ÿë6¦ø'EUÝŽ³ÐÃÓ¹‡–ô„œì‹€:>‹—à*¯ÅvchßÇf¬~F˜ }јÎýÎêzìè_èËP±|6_>ÉÆ¼êÍÐT=}êuê-E/9ßB™ ÍžìÁC(Ú‰ìe·‹-ª×ß‚}QÖTì¾²ê~FûxZÜj ;q+ˆëºá*d’FŠ/°‚’À³ Û_Ðb»v_‹¡U•6,Ñà8AýTô³)÷ó³·5í鵋¡í,“),·æF´gTÒ=5,N äšl^(ôìÌÐä¶$J®k(zwu0ìóÚCP«/vpåY[Š}% ®ÅÝwé"u/¹%CÅòç7;ÅNžV&¨MYlûŽ»çd2>Ñô\v<ÚU+ ª±®Æ ZÐB‹4öTåê.òª(œ¡GÖGQ‹áË÷Œ¤¾_"):ýÎ?²ôó¸6ÉÀbE7ÕO â’²¸ Tˆ~¹P¢~G—+vÿ¦El¾Y4 Å;ÙV—$¾t{ó†¾Îud.\¡¨Ø~Oådª¼Ï P%‹Ö°Ë4ˆ«è§¢ŸM©ŸòÃ×çtâ/c†ÊÏG&Já}T~ÞÓmqÿ–'Cåçe*úõ­3Eåç–»iïÛ~€ÊÏÅò—ú²«“¦*?Ï0 ‡6³)ú¿î‡ô%&µ•Ÿ—ÈB5§ËпÍWøö LmqGŠÊÏû¼#Úq‡(*?ÿÔÇ òÆéú·÷oëÎL$—•Ÿ_i6¿® èßîO?s´{ú·ï+ú©èçr?[^øŸÏÿ/-]¶‚U{%×€ÒµMÆh‰·#xØèêÕ2ôvµãÖOs-ç‡s½¾;ùò#M]Ù ¶]–¡§5&Â/­ ŠæÒ­ ¶£C³ÜÓà>\±üÁ¹ñðUi/Ccí¯yþÜgÉAÓà$ YPºt&×< —0ÈJÊДýmXAá.®QöYIÄ5Šz7!6g4¸bùL,§HÚj­Ôv˜=™yÙ“VÖ1 ½Y32^ç=K"·FžâN˜×ŽUõ*§¨Øý£å+Ù?Óìz0e1몚ÀMû؇͜WÄuÉô‘ Kè¨ØþˆÙ™ßiEk VÃìý*ö¾¢ŸŠ~þ7÷3ùµ%T•ùJÑ‹'æ‚ɹ®V].TŒ!èÎc¥0Óó™ ­=Ü  A¯XÁ“ízÜAëU9[0ôûHÚéÃ6îÇc›™eyw@—šÂ´G¹bù&Z€Á£ ׯ LÙu„«<ò.¤/Eн½˜Ì}BQ kc¶=€r+í5Yâ‚b•õýW7ŸPôÙênì‹f@ÅòEÕ5ƒŠ~€>š9¾oðå¦ß@Ì^–0´¶g íxYÆ]Ý…šv‘t°Y‰o¯Ë»ßØl&³úYAÐÅ$läDU@͵ڒ"­:‚6lÐ óº*¶¿w‹z29²Cß•ͺ+UôSÑϦÜÏþö3ÀqÌ~)z¤Ò̵Ïå ÆJánkm†¸ µÍ|¹®;|  fš€¾^ý.S´ÿ¹eL×í)7éÂV·\¸J‡ Ûyå~ÿ@œ7©pÅò·­¾Fæ=ÈPý/kȕܗ‚/[Ƚó¦(I¹Õ)ÇhXIAmΫКsQܱٳañ¦3€v^ÿb‡u¢¨X¾q`¿Ç ÐîwÒ`zÕTn¥ÉL§m CS5ö³³¯†p«¬^HCÙCë[,¬Ÿ3P±ûÊχ0¯Ž:€Ú¿ëÊþ\Îuüü“<¬ÚÈ +ŠcÕæ\±ýYÖ_É¡5* uû=™\{hÎUôSÑϦÜÏ߃͠$½r<ºæ@îg wÃ~ãA­÷•lpzÌ p>»¸Å‰7ȦÍéÜÁGЉ–±€DnŒç’; ¡½#¥hª—!U1ØÁP±üŽ…Ö uW!iÄkåÜ™8mŸÌUšm"Ë ¯¥h¶…:5)º@PíM£©{‹Ö€Nl®Ãîc¨æžYðÞŠŠþ|õ+†Ú^ßêoX Ëúªp;™ù3ãŽ})Ú®õnVZyŠ 9CÁíö,@¿ Áo>STìþP›u¦×@ß´©¤[Z™s3Ì_CÿÃ2Tz7nµ;ÉP±ýŸm¾P5Ë© }6tKŸuA†*ú©ègSî§QÖ"ÈÕÙ*Ag¾^ îAÝÆ£Û4i–J)C÷]ÃíÒ¸o”˜ÍÎîÔAALŽ×dKèîÐôA¿  fמ¢‘™‹™Mvk@kúNƒ _®Xþȳ®Òïº ÍšÝ í¹¨7ò“®Tš;‚¢ùzwwÚ{þYUÍj@ën“p0®ý‰dЕóê Ý'ÕàŠå K¨„~£Ý)ºeímªß“¡¶3Y›3C3äíŸnêýŠD”Ç2ôíÂîœ×äŠÝÿºø±Î ÐR‰;M²†«TÛŸT_,fè¾Jföc-WlEì*¶%Ñ Ð€D8º©„«è§¢ŸM¹ŸÊ¯6Aîù{RwyÔQéÀj×80ôú?ñ¬÷†dŠ:ʬ.T4htg6qc ¹ ¶`T¨ÆÐG·›š´–¢*@­÷bîâŽÅäŸ!í¹bù•èéš÷¿)Zr†JG–¼ãÖŒ?Nî+qW| $ý–…sO7ú€ñ„0@m­‚¥.÷ԷôËçÃÜm·²àAäA®X>ãÈ4xvnC…î~`& æš(“mHpÔ•l&Ïó–q ŽQm׋ýð~Ùy×+vÿ^boo¶ÐÄ–{eÁ›¹ß¶0+w[‚ª½ÛKuÎTlÿÅa°« ÐY]ÞBxå[‚*ú©ègSê§üónÞ~PjР¨üÜØ3†5´ñ ¨ü™¾KþEP“½QÒTý]€v[vyg::Û”y(¹rÅò:ìg*64±Î ÊTt‚{µMÐÈùÛ j̺rÆ-?âEç÷É„c¼ª÷Ù‰e«…ª¡¾¦ü‰áŠå›0±¸'C“F “ÏÝ(z¥` {5!‚ësÀ•­¯LÐò±)Po/aèú!GI›1˹b÷ëŽ]“ºEtc¨éÄBéÕ¯m¹éá´Uº% ›bŸ‡§©\±ý=­Ô˜ÃÅ8‚šæ~•ÙyÀUôSÑϦÜO‹K®,LÅÐVnË™N'g®ò³÷dC´=CæÉ„5ž\Ó§ñ²Åïl °<@\uÆrc+ `EðYî%v ´;qN¼£ëO æ®l›GÎ|¼CP±ü½ß-e‡&Î4ÁqsL?ÈõÈŸÄ:F†š@)»CúÏ}3x9¬˜¡ úü’5~zÌUÏ< :ê êyÏv;P±|fw´õ§¨C¦ èÐ\rK–è£ h’¬5Ë2ßÏ5( ŒâŽQ4²+I޹Å»oº!^ºa{ †vÕ[(½âÙHÑôŠ|¨œéh‰ÛWØ PTl‰Êª€*ú©ègSî§a1Ó»hµ¶liþYîþÝñÔ©¯3C%¾†¬à·Õµ(Òñ˜ Ç¢Iåu}îü¬æ&&­ýe¨é»P³?ÆÐÜ’-D#¨  ´<úäjsÅòOüSB}ŸEêöìµÌ|[wòÞeÿú£F‚þñ†8é[úæ¡óÊ?ÉÐ5II,M™QtÕ¥s0=£#C{ê˜'î%\±|É1Ã!º¬C?ŒoK]fqÕ×>–•fŸÔìwÉþÇ-õºF—¤©0´¬q K» ¨Ø}÷crJJßQ4λ«$°y57óú hëÂP—!]ÈÁ–'¹¢éw›{:èÂt( =ÃUôSÑϦÜOIÏt:´8 ÐŽÊÔT¸Êµ±ÙÆzyQ40e/KjæNЙ/´`ý ;@Bh¹=·K×°eô†>Øu‡F‰äN¾v¾ž<ε>œ-ÉÙÃËo“<‹x­É4x° ¬Ìäê5ƒvjï(Ú#y?ì¾µž¡/[‡³{}ZºyEÖ_ëtþ"kÝPNÑÜVÌB-P±|ÒcÈ÷öÙmŽÐ'|7ÿÁ|X?û ®ž©שó ÒØ&[ìsôÜærÅîT\”LîXFÑܱí„âf¸OŠ/Sǹí:9Ò”Œé&CÅö;Oy éj–€þ«ô&÷o£¨¢ŸŠ~6å~îúç¹`r¢ЉÇ2Hil1WûÆ"v¼¹ w~·¯”œ>Â<½ fG¼&hYm#Lé[MQ;ƒ‰L_ÿÙ¿íØŒõÞÔŸ %²§´an@—=J#ñy)Ë?ïòYžèŠKàÚcÜÏ÷{AÁ¯X†&/p‘Úäer]¬ÜrЪsí@ÿ¨+÷ál0pá–œßÑÒ)Ë·Xp¥z‚Cu[2ýÓ¹c«ã!âÐg‚N‹‹ƒÈž™ú¶9…^Ž}Zq›ÌذŠ+v?`,?ÓîRtñœµBGå{ÜÜ€‚ÞK·ÿjÙY¢¢ßß:}–¡¹÷sH²Ý]®¢ŸŠ~6¥~Ê?ãrºAëY7•ŸoÖU—t\xPù¹÷Ÿ\hu8‰¡òóúKè¸F?‚ÊÏæõ&a핟‹åOOUeg@åçß/°¬ôã •Ÿ 0|D@åçúþàX>„ ›ïáÃIÌç]k†ÊÏGÿ5™5EåçK¼•IlT&Eÿö~º†‡p2íEÿ×û†-ÙÔqÞýÛýó†·cöÖ‡ú·ï+ú©èçr?ç^ûŸÏÿ/Ç©3ggŠª¬c¹&ÙèÄå ô}·z(ï \lWGÐØ…»ÈVR詸ÈÖ‘r7h»CÿÖdè˜ë£!Úòwwî%òî*Asçé{me@Åò;Æ2ÂÖ34æV s3êÈ=µþQê^ÁýgK ݵ€™¬/8W64ø})´ŒàÒ“^d¤÷CŠîuŽ"FÇ0T,ß1Ï@ì h‡i "Çøqõ¿ž!E›¶14׈[&qǵ?Ê×ý ºuÍ$èØ’+vÿÍ‹*RòS†æÞüH’;UqM¾—Ý^Ñ> ´4l¨Øþ…2Á"&Ð/7Cùh/®ØûŠ~*úùßÜÏþ+޳ôMÛªô:I5¿r‹Vuð•¡u÷¯C†¤Cíþ® è—Šf&ñ?’(:³íl° ø,AÛ$ùäábŠNû£L=Zß+ö1T,ÿ Íxæ´nEÇö=ÍLS® ¨v¦{èîÉÐk‘g؆¯þÍ49Júw8! I m…ÜDk®µÑ|Öõkw†nê°‚_:¨X>Y÷Pqk& šg~‚Äì>AÝ+2d#eÍšõ'A¦êMQ³ÄcœóG@÷œ‡Òï¶ »¿¦£µQ†*ú©ègSîçÆÑÞìլр¶]´‘9ü°ãFë†Cÿ+†Þ7 F}ÛÅq: fŽ>ÂUšÖ ¸=W€ÎXrc¦_!¦Ã“¹S4Ø€s6܃¥ö4na3@Åò‡÷ b­647n'[UÙÐmylI¯ç}öb0k˜èhœÆKÙׂöþÑ\Èm?ÐÄL˜Ö,[Ýï^mEQ±|¾¯•M ºIÑóß )zMÑ®±·%ãJeèÄ÷Ä}‰+÷…ÆFáËñ M{ÏÚÚöàŠÝ/ n.¾•2´ÒZºvjäîNð$õ‹/3ÔÔa s¹èÌÛßâ~oØt£¹€Îl3~¯DPE?ýlÊýü}kû°ÆÐXÒ®ÿ¸2âaãÈÐÉÞº¤hó®ÍÂ8é¡{¹élõˆÑàjŽïÍêsÝ^ždþ·}(ºû^$©ð®Póžíd¹ŸÚ*–_åd¶³Ë@ÝVc§üœ¸/.—Êú>ÄÝÒ/¸LÚÌ}ºXügyr'Í>-;¬à>Ü46_ïÃЯÝ‚æó×Ë÷=£^ZÊP×Ëà½ò.ÜmlAÑÛFDé‡÷ÀL[¶|í^@-µŸ“žûƒ¹b÷ïõƒd-Šn  ó=‡s»{1íñ– 4‰£›œÛ¯3;ú;t§¨R¹?Hv.g¨¢ŸŠ~6å~_˜CC…D@ë@c'\ä*9Œ¼Ë[0ÔíÔ^ÁÒ:–¢ÞûeF+b¸¹'–I³WpõŸì`{™j³ý€ìЉ®íÜXð÷žÁµ7<g`¨èÏÏ—¡ôqú^@swFˤQÿ6#R ÜÆ þ·]µ ÷Ñ'u¼ú–9ášþ|#ý[2´wð"2tÞQnìúSìgí[ŠŠå;z¿Òê7€Àꇹ\»× ÉáÓ(ªäë’ú±Cÿœ^Óš“i‘ßßpEÿÿõ‰ƒçQ´ç´± ÕÛÅÕo]KXô\@;w‡ê“Ûßý[‰~‘Âл‡°¶U‡¹Š~*úÙ”ûYÝ:AˆÐºè—'HýåLn—ùƒIÝÜ5ðÌT¥¸qõ?¢ ž7rßÇL7íçŽ8æÞõôGãðѺIPµ€LuÐV†öö  ¯‚îT,¿‘ÞS!æõ6@uGz’ô¶+¹FQHG§\‚Ö_*#š>?¹f+ƒÀnL(CÝŸe‘F©ÜeÃÙJ= hܶ(¸[ÍË7K=…Ll1TÉøIŽêïDnV´=éù‘¢#W¶f³‡ùqÿÄÃÞå]¯öƨOf¨ØýËqã¡Ì!˜¢«6¨ÏOæîr-$ÑyQ}¯ Sõû*¶_í}0[hÓŠ¢¿¦³-?Wªè§¢ŸM©ŸòÏÊRÐ NT~ä}š{RŠÊÏ3z¸1w*??i2L¢[JQù¹cÑXR?¹^@åçbùkûe’œg¦€ÊÏ“^t€S“ô•Ÿ»uJ§;»b¨üÜxf"ôГ¢›ïëÌ#´ã # •Ÿ;9Œcš:kT~¾vö-’SPÃп½ïì°CïEåçãÏD‚^ž »j¢&™ª~п}_ÑOE?ÿ“ûypŒ†ÉÿÏ8GUbÿ«+C¿÷})~VâšZ?…Ž#\¸Q5Ñ03?†û½FæÚÄmqb&{m<‘ûZå6=ñË 6–éΫÐÕ©´F+ŠvNÐdÑçýËÿ}‡ >ÛhX;€ûõ»¸÷wdгnCêøäV!AÓÇ©’:RÊuÚåÎî舚ù,nÃq‚ú†&ɪ‡Ôô¼:™±ÚP±|U£ËÏ34ëšM2•»¸o¼!‰ ‘N´P=—«etû£#€føí…µ[\±ûýçƒñù7…´[`ežÏ½QlÈf Žfh`ž ë=M—¢bûõ†jP³jŠÄÜŒJ‚¹Š~*úÙ”ûyø@L…RôÓã-¤.ð ×ýAeÎ+½ í¸P‹Ž\3†;Ñðu{ôÐwŽ#€x–q•~¾£Ë4ü¹K(É/½Ëu¼«I&ÁÐ ­.“ÓÁËü8 ^d töžbx£UFÐZskÒçEŠšÛ õ61ܾ½OÓ>Ê#ÍP7!‘ º\é¨t·«;W²<¶dû2T,Ÿ×Ó_tò'¡%''0]ÛÜï¶2ÙÅ÷' ª¹W6ÛeWZ¡j2¦Ò“ûaw,HdA »ßU§ì~†ô~d%è}Ëw¾GËO½ç „±­“¹bûMt…ëÔêùŒŽñêÂUôSÑϦÜOïŽË6Þ-jŸB´îüÛO?eÆ_sMFH¤%>Ñ\¥á9ð¼™/ mí‹aÄg‚þéBa~×@׫š„›\$èL‡Ú^˜ÈÐÐK¶¬K*úõ1©'™A“TÁ@ÿÁºÝ‰‹ä䩳º.íeè å’¾-ƒ jëBHú`U®ÒzZeyˆ¡ß·`As*–¯øÐ.¦|ýE“܃˜ÝÏ šê¬EóM{Ty„Ž;kSzNp×ßÌЬÑ‚mqEÅî_Ö~¦ÑwTmÍkp׫ÎA]ܲdßãÇtÊÜp:áS7@ÅöOöpa&?GIÑã—0íÝ®€*ú©ègSîgãêëD¿[,E3>? 6K¯r“?— Fq‘ܺÎÝÉÃÜ.‘`¶ô$7uó,èØâw…Þ+hLfhUˆ ~ɵîã¦ngñŒË;O)*–_ól”}j è/ïB©iÊДw½Ù5é‚Þø~ÞKÞh×p¡¢¥A•v®•8uPT]×PÓUölI+†âŠå }r’톓-êÊ|„±€,½Oƒç_%ÜØ×tñú¡€š0‘8µgèË6ؾ5®Øý1+ßCÌâŸ2ôæç`PæJQ#o#2pZ ýúFÃØ3'¸bû‹¶‡ ëoJw…Anìy®¢ŸŠ~6å~^è[FY½£h@Ã2y CKL}…#¾m¹æ-ºP½J]îý;ÁzâŠæz/ŠoJ\«ƒÉ™µ»ê8¤Hz÷âŠêäl!Úw\ü hÙÁË_ •s÷3T÷ía8ësŠ;LC—LrÐ[žKÁT9[³<Žô˜`ËÝÝÒoÜÂÍó{Gª×†s«ö~‡>§ *–o ‘;$ñ´‡¿1sM¾Ä=é—J/.ðää}JHäN ÚȲÏMæZ­¯&Þm ¹b÷eê`ã;EKë`á«?ÜŒÛÿÀ¿©íés¢vîe¨Øþˆ1O`eúU‚ŽÚœ CÖ¨0TÑOE?›R?埅ý¤¤¶±/CåçÏÿ¼¢…Ût*?·-÷†°Ï/)*?·ü<…¨yGPT~Ÿ0…nö_ÇPù¹X~ëó`ç‘ÌÐÿõ|È{#àÊ=Ö—Ayš: òó6æ‡aýL†þm¾ˆ7ÉtӠǀÊÏ-|e¤lÛu@åçfwàŸt @ÿö~×iàæ¼> •Ÿõ.!ú·ûŒµ€¡û¾¢ŸŠ~þ'÷³4à>ÿ¿4YÙQÒé_¿‡£‘u™‚Íx3îš¡¾$ýâd†æšþš}ZÏ ë NušÜ1á¦ðÃ4€s¹-õ& :º¶°ïw7‚†ýˆ'E~I€îUR51U¡Ëosê8ï¯JЫ‹ý˜“Ækn؇±ì»±C×öa6)·(ºøêaP®Md¨iÂâ•ñ„+õ+ÑfM-»MBS*–ïTmÌÞ—¨«ÍupÍ‹ä.òéÀR´q[x{Ðr¸>ƒâYÝ{ª’—M+mTì¾_9‰Ø ¨Ë‰¶`^ýos} S¸IºCj¯(®Øþ€p78ÿëA”˜‚û™{_ÑOE?ÿ›ûiûf&ÉJ[hL`"‰líÂÕ›ô”w³cèÁ°%Lã´WåpªÐ¿[.·mÃ|V}š{Óñ$ާ( w…¬u^ mkà w_âv춉æºöáŠå÷è°‰}Ü êÒv4ó:¿ëμسu±}¼o S•­´fvG6ìö †l‹`%ž«¸?ÿ¤‘Þ6ËÐçÔáŸ1C*–oÔ‹pËZ¨Éɸ4é A]–‘„¹€ÊÖL‹º{øK4ùžÝ‚›{8P¢³ýAÅî½2.Æ_ôm©98K3¸»n… ZÜ÷Uú`|Å— ¢ýQ+%ÉJ§%¨ZÌ282— Š~*úÙ”ûéÛö7Ñ.õ´ß!ཟ{zØ1¶e’EïŸaþÃ} :º1“½Yñ„¢)‰¬ÃÍ^€Úm>-Ì•¤2´Ù+Vxq7wgsR—WOQ;S{ö±øW,ÿ¶š šÝpPuÝvtÛð0î÷7Gdw³ý¹¦‘ïˆÕ«ÝÜ•/Î3ó ±tÈ:'¶<zw"3 ÍÐ鱃مÇýúžÿ qýÖ hÞÔ—PTCÑÝ;¢Àü{ ùÒ;ðNi6wMÄ-âÐ9ˆÛìÇ#Ú3‹+v×¶Í Ý=Ч€VÑ9îÌ^ýá¹K[n{õ(h¯ËÛPÓ~ä4q‘𛦍¢ŸŠ~6å~†[-…þ=MØú¯\¹ÑvÁl]äRî–ƒìÄØ$î·K½Y„á9nÊåC¤ëÀTn<fAÒ±ý>²9+×Ûh‡5½˜-eÜÒF©j‡ÿ­Hþ%׺’Ò­a€nоOnúáÞz2r‡èq·XNÙF' ÷ê$%¹ž€z/!.Q6ÜÅÁàµæ8weôpMñ¤¨èß'Û  ÓÇ^ =™vìæq²kA}øQ‚–KžÃo=ЦXÕ@ÿ¹óz%$‚øgesÅî[¿ ˜òÓ€FÚÆBñÒ0®gÅG°ëZ%C]ʯÃáa± Ûß6)ZÎ;ÎÐæ×/&¶7¸Š~*úÙ”ûézí[@+{¤ÂDZ“¹6¥Ç¨ãCıêwsÃØy`#÷`˜5$ ¾MÐ3OK‰Æ°@Oùm† Ã;pÉüáî©:ŠNÙ ²Â¢d¨Xþg}ûµò^@3~Î˚ܛNà¥JÐÝO®£µæÜB>9´®-  ¦»“ÔÏ× ê¢Óê-WR´XÃjrÅò.wõßÖ3´tü0г–›<)ž¼Î 'ºìàV¨j°Ë^PT©y™±aÆL‚ŠÝO~“¿WT×<Š4=¹Ó cÈCå× -hcÁJ'sE˜³‘}+ÜËPKõ`æØú°€*ú©ègSîç¤ø|Bºzhÿ=èÞå!AíìVÀµ[€®~*€†¼Y\­Û˜Útê½?–ž™©¨Ø~«-ÝXØ¥€v~«C”ÓWsýTô³)õSþÑûR Âþ…•ŸW‡äÅL3@åçÁ‹§ÁÆÛC•ŸûvxOŠ¥•Ÿ´YÈ‚»¯T~.–ßqÓFP° òs«N`žõ8@åçf†€¯îq@åçÒ XÂñÀþm>—Rk’Ÿ(*?_löFpr›ÊÐÿuÿdWú]í( {?xQ%& T~^0¾^pª>BпÝÿÔ®’Œ*ú·ï+ú©èçr?¿>üŸÏÿ/]·ìa¥ö!€Úk-`Ê“¹¶w#À £¦ ½'Ûöú+(¹ËRpÿâÎÐ ³QlØnbÀ-PêNQµgñ0æë †]t€UU£¸]W‘ÅWÚQT,¿æ¾·¥[w(šï_(äVs­û/ Ú&á€îkVKæ4¿ÄÝ”±Ÿµh6¡e§/0wÓ ºÂ£'5{[OV“#ãC(*–/®8Oˆ™< ¡­µÈÈí›t±ÓXÙÔí·(:îÆ!C=‚{Dö nŽch†nI¶‹ãŠÝ_TM ^ÜtÀŸ·dð¦{Ü'VÒÅWÎSÔõÚ,æÎ: ¨Ø~¥¡ã߬½HÐþ_“Žl{_ÑOE?ÿ›ûù³¢+ß~ ÐK¯‹éFû‡ÜäÇ£ÀÒx5E3©Ãë“Ù2ôAGføÇ[@¥ìÃú½€®»MÒK1´êáöJï7×fûs¸Ý7Û0¥7>\±ü{nM'¦l=EµÃI}k®Æù`°òˆ4̬ œÉX®Gß–½Ê‰«Ù<Ÿ^o–Ç­]aÂlΡÊëÚâ bù” ’e¨Ii]ŽžŸ6åîÙK”Ÿfqû?;A|«Tš}ä ]q†¢f]I`yW‚ŠÝ÷ Ô†ô>wuI#`û§˜[U'°©ö^\§(²ãæC®ØþLó°ékhBÅ3Ø6ªQ@ýTô³)÷3Ш¥žš5é¡ð¹åk®d—*$aú*l0¸LØHÐëgÇ™JWvt'º0>ä AãR½ÁÜm w$Qa–Êñ€Ž˜`ßnsÅòy…zо†Á€šÄ; Ó/pS–žeZWºq­#½Ù϶G¸o4ÈUß*жú: þLPa¨Øý×Ñ×ÁªÌÐÆíwÀ.Е÷K ¶õŸÎP¯‹I4ÖÄ™+¶¿wãáQõúíõ}ª´÷ €*ú©ègSê§üxï*ÄuT~þfZŽ`¾ïCå畯âAz±Cåç VW@À •Ÿ'ô½Bëô;T~.–]l ëaÍPù¹¯ë ²üM EåçCÎÁTwŠÊÏ‹”óMzýÛ|ÒåÏHïêl@åçë—ö` æ*?W1ÿ$8-ŠaèßÞ¯M~ Íë–*?oÿÖ‡ ë÷M†þíþëo;±œD3@ÿö}E?ýüOîg™Z“ÿŸµ§Èv ?ÎÐïÏÒÐèý\ÍîÕpÓm! ­>CÛG ºó\,u¹~Ðð{áà‡,®®‡&ÔîØÉÐèo}ýE¨º“;qšÝÆÞ-] ¨X~Ë[¶¬çoc@3¯Ob7zrãô»±Í4˜¡;Þ±5/‡rû¶m÷ËëÕéßÌĽ.M‚¦®ÚB|ÎìÔñ»&týš* bùzÔ¨Á3ý·€šöß=w—qפ§Þø0´¢ð#ík0‚Ûå©!¤Vü¡èžä`RvZ‚ŠÝÏ~¶†©¦%¨Í–¹léM@“Ò ÃrŠNÍ›K5ZrÅö[/‚¹€Z×…¬‰{_ÑOE?ÿ›ûi¸ßŸÎ½’¡?ó"hÝìqÜ—“^@Ö©u5+¢PhÜ¡F}¡•£5 4É#èÆ‹P²i#EÃ'̓²øŽ 5gÕÀ^öãê+ëÚõW,ÿÂÇm™oÙ@“_¡7ïärG¯Oa¿«‚T93˜-4\hׇ7á¥é †hîöê˹Jßo Êu*€Úß<cÌæŠå;ls²ÝíPsÊsC?Ò•OB(šuåUj1a<ªa?ŸS4·¥øu*a¨ØýGËŒYõ¢~€Z+÷e6º£¸³?ç‘kI횵ǛžZ¶+úýáêTX|¯EëŸ3bc3L‚*ú©ègSî§Îø“T¥ÅP¥‡>´¤ME³'úÃŽ}Óš8,vLæ>¹=êÇi´"ñ7éáWÆuô“8—cèÈW%´Ùhîä4C–Û¬T‚ö¾è tÝß]†Šå·ùÚE( ¸ h㇫$0ö1×§°'k”Èí}ÆO°x#åöÿùƒÔ´ÕeèwíÇäƒÛaŠ–>)‡©Íw2´È©V¦Õ5+–ïšzt0!€~ß\ Ó¬/4òµ£ÌvOî÷qÄ÷šwêN7–Úý Cw] g¤{AÅîOº¬Æü 7+éÜuÓ¹ª«CXutE“-N³*¶ÿÀ²¶°2ñ!A“>‡Íó.pýTô³)÷óšŸ%¦KÑ?Iê´K'n×Û*àÔ¡Ê_jÈë”\Š>Ó³€¶³'n/…¼‚Ü[ÝBÙµC­ºß-ˆ]óÓtR¤%Ó™0” KÃ…–ÏËo5O€=Í~zo½ÆÝoÞ¦.Šà¶ƒËY\‡U¼Mr£] °Çîemæד{`ü !$¹W,ßÏÑ% tÿŠ}‘› éC¶Ptø˜8R•W#E«‡Ç‘ê5Û(ê>|lKÒj@ëWE£Aλo;*V?˜¨Û“4X7_Õ‹>t)ãjØm†¹?.rÅö¿7.„ 7½^á »§]d¨¢ŸŠ~6å~ZŒ”ùî4¦èÌ#ó„"í>\§]P4w·ÏOð߇ëXöŸ÷¡“úÔ‚ÃÓ= í¤N%ê2g4Ü™ÂÕhþ†åuâvΚ Ù õ*–¿~DÔeŸô±ÅM¼ÐkÿÇÂâKz!¹;XiîàÚ.t€)µ¶ µO®$í¸C¾cË")š?o[<3P±|å¡PÑê&EcK6ß55\‰Õp’¼á!·cŸ‡²Ö¿suf[Ò†ò»]3<›¸ØF*v?¢TÆxÍ4«r€ì”Ú\®[ʰ|õ S;Ìé )*¶?kTO™mîc†®ºáÎZÇíã*ú©ègSîç€0-à4¢Ú¯l‰ÉäÜ¢K.0õçQîƒã¾ÐÙ³#C[…x@æ÷û\“,G¨/áN?®­%h·Œ¹ O††ùw€ºª$Y a«Ö0T,¿Víèž>P‡žµ0`½7qôUb—MPû7ùĽù@ŸK•Ý×e*$›¬&h¾ÎvRû±œ+vß_Óˆ©T'hB¤ëæÁ ؇iŠaè‚Õ3˜F¸EÅö‡æ…ûÎq€&_þ5Tì}E?ýüoîgp±É¿EÑ-ϽHÕ …ÜüaÍHÉŽÑ\“×Å2í§¹ó~€Üεqkk$ -Ñ8ľŸ–¡±‘ï鮡m¿¡+ L×âºÛLvOHP±üRûPVÐa2Eã,ý™}½2AuûõwÕH)êUÜÎ*{tuÅ}OºL3…f/<ÉýzZ‡6ì.ä*cµ¯ë¥¨X¾ê]éos@ÍïÆ scwí'1ǧpgtn‹;8qÝúm‡ÏåÞÜ•-LtÎýP±û±V]Ø„‘q¡Â"Wp tf3 n‘eGRÞ›¢bûÓ/•ÁMu¡žI¦ ÷ê4WÑOE?›r?ï® $¶ÏŸÈPÍ÷‰$²ñ’}Úý1BÑõíV°5@†Æ-:« eèÄá}Irè5nîK’¼¿œ ë.#šOîHÑ™;°ò @?tš «[WrÅòÛ½wfoti® ³QêhâæiÐÊõ7AYË0ðÖúÊMm~ˆ¥ ™ÌÐÞ&),Ÿ<Ъ‡%3ßtvÏñP²˜ ÿ½» ªñíúž IH…<‡$ÉsHÚÇÚH’$IBB És”$ I¥’$©$)¿¤öy IIB’ä9Ïÿ{æžu\3û?÷uŽw×ÕìóÍg¦5çZß©ïh÷ŠX>ß•¤õrm@s„ƒdxÇõ²÷†•¶]¸%.„Xs Ý.€×‹†ºMéFöHãŠÝ7Øõ”>~õŒ å³³i}ÌWn†rý°«’¢Ý´=Xõõ¾Û¯¦/8|õb¨™­.5ESTÑOE?›r?}ú—éÇÔs24tKäê¯bK†œ"裬rÌL@“Ÿ(±ÙÛú§¦5ËQŽ¡¨îðMÔýÂ)­¾Ö’m–)ßQEêÙ£½€nþ¨éó*–ÿåe¦­bèYó´Úy׊@iÿ úöx˜jü¦¨Ï–=¬:x  õ‰géõ¾”k™,9ìv” å» K†' bù4^m!fy+Úq°2i ù*AÌ/‡ðÊ»ÝþÒ; ¨÷:uz`AQƒ#Ä£¼ WìþÃM!´ÈTÐMs&Ñœó-¹'Vöd—‡Är“w‚6ù·¹bûoÚMªú½ÐWáoH¨O†*ú©ègSîçœ6= ê`–-Š2ƒìÝ~\§UÙOÕS€ž;æL->>ä:>éN«66G#Ï%Nùƒ(Ú°àðõô1@ÃË}  2¹íMµ`H¦«€ýØÊïf¨X~é.=™YÂt@­“§’̓Ær»øçÀĸƒ ]<`ÄWÞà^-ª!ËFþh@á [ÉÝ×ý#èäê1t\÷õÄÐöW,ŸN‹Bd_™5Np§ºcõ z-þ9øÎ5dhò.HÛäÚªéÐëÆC¹îÛ}Yú­»_r±^˜áÜ P»×½„#ãÕ¸tÖå=‡»ªoŒø}š¢¢Ÿ_¦¬Ùì`¨ãHfvÃ’«è§¢ŸM¹ŸÏ»:‚o[6µÚ¼¬ÜO hM›KÄM¨t€Ùˆ–>æ¦S“è¤h24ÔGƒ¹õåþö¬›µ} ¬…UCUºÔN.iu³„¡w{ê³R‹£\ðv‹ 6wýc¸¦oŒ˜æð<Šæ}«–T ;& bù¼Ö_¢ûc z~Ô[ºûj{@U²¶ÀÏ^C þ”‘²°ëÜ_‡'R›žy€VŒ8z~{¹b÷«ª[J>RTsÉMÉ}_õj °FËŽ¡:c¦‘QTlqcÛ?k+AÉ–Ñ%PE?ýlJý”,Ÿû‚Åw;ŠÊÏc»2¨·> ¨ü\Ëb?KplNQùy¯¹þö$Š¡ÿß~•ƒàÖOPù¹X~ÃüfààxŒ òsÍú­lúCm†ÊÏ}¼’Èí<‚ÊÏ£’T˜S€Cÿ6_/Ÿ´ÜÓPù¹F·/Bpý†ÊÏ78åú9ŠþíýqÁúäÂñ:‚ÊÏËÛÿ"Je3sпÝ?Ûb* ØøÐ¿}_ÑOE?ÿ“û¹¤óÿ~ýÿr´­Ë^jOÑÆÏã™ç~©€–9laÒ+q4½ËúøA&„Œ£”¡®£ûל"®“úLÒ:I@/;*Aôå7Më_›ÏD04è2¥fv¹\±üß n%ƒÉcPÙ¯=¤±mžmxqOпa h—í;HùÒ·u¿»Ž9iµÔÅ¥Ûx‚¢þžJc6q%ïŒ@h|MP±|‡µŒÝ h~Ÿhª{6…û[_ÏYÏÔV7 ¬NÞ¤hC%+úZYGŠúßãŠÝ/ú~„„GGRtë»ÓÄÍȇ{©Ý[øéÕ PÝÇõpçÝ`†Šíßñ²'(Y;ËP—ƒ;Hîh‚н¯è§¢ŸÿÍýÜS0ƒ9?Ô h§©.l{a7GBµ[ªfê'‘NyGÐÅÅmØ]ç] Ô‡Yi§hÉô‰aÆv†~ò²ež]¼¹ï{gRcwM‚þ\çCJ_Ý ¨XþÒÉ ‡UMˆFΔÜâNzAÜRîKP]KIàËZí6‘¨*=´¨"¶x \¥™*¹†×L ªêúžôROT,ŸÞIÆäË€fîÿNܧs÷Óyà6ºC+â;ÂlórŠZT}ƒž£=l(‡»2Tì~q÷KDž%E ¾?$Cª»qST¦A±ê†VtÞIã÷pÅö³WÚ0õ¥ }ÏÀ“ņ\E?ýlÊý¬ pf¯×·ÔòÑT¶~”%×áë.’¾‰ ¿ê÷’!Ò(ºyÏbš=$V‚*µ’’n.§4îÎyf°áîí6.t' ;,ašu$è¨EñD+ê. bùF-$U¯:š:û4y¢>ž;Ü ŽæmSPéžÖ,g~6A{H€Ù̉€6ŸùÜ%A:4º…Ô³›2 Ow¥Ãâ·ñ Ë·°ù ðyWè¦Âdø‘Æ-±­ »§Í§(ÝûœDª—™£nfoˆ‰— C3~U¸ê{2TìþÇšO$Ð⛀†èµ‚Âô"®×£bêæ7Œ¢ži]‰î´\ *¶ÿ×ÌX;Á—¢ÝbOÁ¸¾‰ UôSÑϦÜÏÈQ¦ìªýf@#ZvdÁMð«’(}r¦¨eëÁ´·¥·t6Y¡ÆÐ‹Jht.áD›Ñ„_E€¾M·u›8®yáKÐÓZCÐÉÝûÁ¢íš ˱Zž[ hâ<)èßÌrê55[çÎUEÆìNMãÎ;sZÆê3ÔµG/8i8š«tn›YØS wœ²6xœFQ±|6µw àé.@ûž~ k=q׌׃µÎ-m\¾µ\_ý0cË Š&I½Iz¿4†ŠÝŸwRJ†þ# ¿S A-$ó_Ö¤“+“Ë(jpî4Uz7‘¡bû·©Ù‘‘}n3ÔÇy SÛàÇUôSÑϦÜÏ}‹Ki?ëÓ€ê88ÑnöŒÛܨŽú†_PýåLU¶– sõY„kEý¦gŸ´4ê•?Ì_WKÐʰ $h™£‰qëÁÎì» ; Ç:%1T,ÿ°);àõA_@%ÍaÔ­ÕÜÆ#Á—äqCþÉÍèhîÊžûÈů7)º`$%¾[ÏÊÐʰx2|H(CÓžD°‚-¥ý>_ø q¥õxÝSÕ(êitJ”›tVÍž–=¸í»æÞ¡.Ãæ±Çêþ€ŠÝo}à Ô4Ñ ¬Z}ä.Î`U½ºRT³Ç^~=P±ý†ÙöÌuÒ)uwð£_"rªè§¢ŸM¹Ÿž4Ô|GP n~»HʬRnÔ€}é3˜ÛâÓ}ÚE²Ÿ»ÃÇ’Ù,Ëä>žîHO=âZ ÓÉÃ·Û jöñYp~ 75Øì^§:Ó´?[9»+ bùk¦‚ɯ)€ô kÞuäNÑÿ13µ¹ÿ4BÌé©ݲf"¸Þr‘ I1¡`´øEßœ¬¤)mÎZõàŒècÏËçmõzvûDÑ!—r`ò,3†Vd_ƒô¹/ç_€XÓýÜQÆê0Î+ÐeÎðþº“ b÷ï<š Õ):½Íb¨¾cÁ}5ÛüÁ’j@Ÿ—†B¶Æ ®Ø~ß§cÈÀg'$è™Û…žº2Š*ú©ègSê§üsp@sˆu½¨ü<¯ç~s‹•Ÿ¿ý¼Ì–$*?ßœ O=¦*?Wn]EJ™1Aåçbùþ„1SŽT~¾¸ï øþpCåç¡¡!pÉk3Cåçê]{kU/ýÛ|[B #w.Cåç>‘{A?æ Cå熞U’ç÷ŸôoïW?ñ‚º¢òsó£'@íeAÿvÿ†çÀœ³QôoßWôSÑÏÿä~önøßÿWîÿRãf:éx¡Œ¡šÍ+$›§Vp]÷üÈMo®$ ƒ6¬ nÿØôYjé²Î Э·ö~ w yTíÜ& ÓÒ÷AÞh CgLïÈìf÷#hCp¹«ÆT,ÿ«€BròòN†­íHMC¹WëCXRÙ- ›z€¥¦M´¤oYãæÌ{Ôê} úØ=˜kÎ§è‰ ¨+Ð ¨X>ÊCÄ:º„¡›¿7¼/rÍ—yÆ>ŠZ;„‚¹W9Wm¹'¹‚ ç“®ÑðOE€ŠÝ·>Kn|r&è—†$rÉs·[§²{­C3¼ú³Æ;®Ø~Ù‚$VU,A™JOVÔÉP±÷ýTô󿹟kŠZÓÆQå M5»H»Ï¹Ã}y:’î6ÔÔ­¤3}¹lW·óH_ÚûÜRYÚÏ[ è}ßäõ¶ u)H$®gti7u EÏÜ`t¦2@Åò;'©ÑvÙ‡ZÿµœjíãVV¶øÉy@«~ \ï>çú?¿3§z墚‘Ì•ºMÊA ­w@‘o C« ܘպ)–/׳–î½wœ¡Qo†2džõÜÁÃá§Õ4®Õ_08Ã}¥wúÖ]4"ë&xX+ b÷M ³IßÇi­~“·(âÚhDZ[Ü.úƒØÈEçÛ¿ÖÓšønh˜Ï5RÛL Š~*úÙ”ûioØ•-{z™¡ëæØ±œ¡\×3Iäá“€êÌ Ã]£z\ãZ|µ S/àF>™ z.IPó/'AíW E3û\…%«×ú¡Þª-’(*–_I:’…Ômbh—%Þ,ñ÷n·åæ0Hç! O&ÃŒ§1ܲ žôRÛÇÕ¼ØN}ØÐ×{·³m>TÇ`3)\ðž+–oð¦©,ùX†ö]?…uøBQauoÜ~…¡C¢THUÕî´+šp{[s®õÞ Û`EP±û×»ÖS£^ôÆJнö“»ÃP ¿š·ë$dÚØqÅö÷¸,¨uÿ$ •‡ÓÊ[” Š~*úÙ”û9yÄV–]ÅÐÀí˜Ým\ÙÑHh™hßñÏ!òÞnÕ•ú5æCKß·f®¬€¢''í‚K S:âÓI‚Úsnm¹Œü‹ §n‰¾: ËOÇ„±Áq¿)z¡y›7{(÷€Q¬ýÐÐ_’tZ« èÙ¼^ðnS& i28¹;–Ûà~€f{¨Pôµó7ºTT,_ºl0SúØÓí–õƒnΟMÐå{ÐGsn24íëæüö7áó (Ó¸* õÊ« Ñé|nÁP±|-jühQÉQ‚¦w®‘eÙGqý÷aŸúØ1´27…u°ßHÑn–£™ÖÇ Í 9Â6Wï— b÷dæP¤hU€=$Öis{Íh fvã¹×OfBlÔt®Øþ”N5püÕL@wØü„áSTÑOE?›r?}{'±—‡‚(j=8ťݓ¡#o}‡?Çú1T­ó5]¸‹ 箑à sU«œ#øÙ›3ôêB”4 ›Æ:ŽßÆ-îÏb¶6Ô°Ãæú¬Æ]MºezT,_Ë.=!²åj‚f}CtǤJÐãcl݉P‚ ÙÊFTÔõá\V5 úßF“v¿â¸b÷#çÖ ³R»*ÝG8²Å„Û'®ûšÆ5·ûM.N+ãŠíúõ z·éAÑçÞ{aMþL†Š½¯è§¢ŸÿÍý<àT }ß.eè…6Å1c7W%ê<Ítäž?¼n\ßʵJ'LµFPÒû1UŸrŠëô ¢ßÿêŸ0r¬/E­Üë Ý-†.j@M³ÒÔ¦­ÓýNÐË19t–û,@}Ϭ ›ü ¹a]Ò'~R´úý.v}ß @ÅòeÜÜD”:Ô™£é™¹æÒÍIÔ oVò>PÃËßâÆÕsm¾|y5E{¤.fNùÕTì¾î®£ÂVw@ã, iK7Û0øðNnÇîÏÀÕË‚¢bûÓÂ2Hh½1Cm„ gTÑOE?›r?£”þÒüH†ÚLŠýGñܯ­”¡­£7òÀ$¢zȘ[ûî9¹s½Ї3akÃinݹLjÕo,Eã"¦²¡cJ ºy>ø†Û:{O8×Û¢bùIÅð‹™ÉÐÕÛ2 õãN®´V™y p´äôáÔ¦4î­œ‡¤j€9A=Jz‘¸}#)jºI –ê½´ÇÏ+0®÷)‚ŠåóÙÞŸªö&hô¼}T{ÒWnò•ý¤`ùN@³ö´†Ë-'r•H,mʸWÒ@ëb2Wì~Þ -ó^Ÿ6g±_âµ(k¬¶ô80Ôqø~âúš¢bûÇL‡!ÃîIPµ“à0·‘¢Š~*úÙ”ûy´ëVÈ_s’¡ 5V0òD"70ïŒ$üV:E«|FWƒéªº²²¾¼&è¾?% mõš¢ñ}”˜—e  CÕ@×â!·ÿàé0|«wí´ЧÝwŠŠåo×)tz…3TÍex\æ6\ë úÑ…€u8 ªS¹Ëv$PŸ©ït×¢î¬4þ:A#'¼'ÁÛœ(%“Àòß=Ë·`È!ZuÞPŸ9úçÃnî²N=`ï°×„› ®ÓGrç¹j Ý(êžÜŒÖ´d¨ØýO׵ȀC¡€:¬q'Ë£¹Žá-Áwý ªrªè§¢ŸMºŸ;÷CI C#—è“/;VrŽÜÁI&}1G5Z²i=ÉP# q§qoM‚Nÿz ž-7eèõ_?Ioû\UÓ4Ò'Pÿ¥ÓØìïÍ*ú÷‡ï‰¦Ír†úÞH󺓷îrD|1â¾^ ¿wpËÎû ëêÀrölî%½-ì{¡*C­,†ÛüT,_é* ·¿ è&¿PHQ.à<Iktt`ø-úaƒ öïNBŽ4›¡ç³ÏÐ2éu®ØýÝ>jÐ{:pí À9þ·™‡+ÓWMah·µ‡˜lg€Ší¯<¥Ã~^ h·”¤£Kg®¢ŸŠ~6¥~Ê?Òi’Ûo­*?oã½Úôõe¨ü|›j¸§¨ü\=l7 WéEQùù;óéì­å9@åçbù=ûu@½CåçµSºB]Åf†ÊÏO•ý»—odèÿwÀH[Vàßžqÿ2ßñ–ùpÏ( Pùy£ v*?¯cžL«ªEÿö~êš  Ùõ< òs Y}éÚпÝ϶Þ$yæèß¾¯è§¢ŸÿÉýLèþ¿_ÿ¿lìL„ø‡µî¦Iì’÷su/Ì%þ¾o%¨Ž¿ùõ²’ ·­ÓÀ æ2wåà­à¡4¢Ë±@³ã ½RÀš½ð§hÀØí0Ž1TÅ£5û¦Ûž+–?¤¹3ÓîAQïdÂÜÇ%JЙÄX½†¡·šàw“+í³ŸFt9èÞÈÛ$sM)wÖo?¹]O‚¦›F?{”*–Oéº øŒItƨyvò,÷úæ·Dvt¡€VÛÝ&-{ž§h¨ŸTÅf hf"›ðÝ »ÿòÕ4ü½#CC,—°«TþeOWâ“Ì h»šy™†rÅöËØ îmøñbšw⊽¯è§¢ŸÿÍý¼äp„Ìè¸ ÐoÿTåÐ5\²ûy¿¸ïBõ!uì®éü'$ÛNJѪˆ$íôa‚ÖÛ±^%Uê¿HÖÕh÷µ~1ñÝ!AÃzv¦y ·0T,ë m–赓 q6©3åzE:²÷™ ]S¹ŒuˆºGÑè­Ç@%7Pÿ õàk9ƒë¼(F?öå–.wÈm¶˧îp´tOjk}.í æzŽLΘë2´ö‘•dâ~­êÄ4Ÿ¤s»\Mc¦l%¨Øý—6²UcË)úV%€îIâJU(³ûœ‹~|2ýéµ P±ýü À¤óŠvø0 ên1TÑOE?›r?m.·Oét@7~ë C‡ æ®~;ú­ãö蜖ó̹Ý[vÿ¹6\‡â4¹kËõz–œ,x'Aõ:¨ Ï*³)zùÌ9ÖY« uæ®d몫€ŠåoQdD…ÀÛmì¡"ñHf\¥‰½™T)_‚O³•êÛé+øN™' ÞRŒ˜Dj®šF’†wtøÜ»ðÁQ›+–oŠN=l™`h}j3éçâ‚:µîÊŽ,×bè§kkÙÔÈ-ßAÛÍ> hZþ¹¢?ßE{™¿EɃ,æãuî>…Ô†ì4åLO09sž bûÝwœ"áÞS)zü¸t˜FPE?ýlÊý¼¸ÍbZ;½Þ)$háî:hUz„{çîXи•¢*¿áþ­a©òzw¶fèà£#˜Û?í):â¦;[³m4 †Z³¡t¾Aã ¢…¸ôo\±üº­ÉØ»Á¨GêÌ”¸õý‰ýӿܮvw‰à±ƒ¢—'YÂ;«Zñl#Y8Ò«rüªŽrAå4{šÀË·ÕDUúißP½sà'|úŽ¢1aÌzõ4‚v¹Êî–ºzø,"ç, ù±´ðá@Åî;VE0×ÒáæèˆËQì~Nk‚F†ª’Ƥ'º7ñ} #¨Øþ aw} AmGVÁÞ)z UôSÑϦÜÏm5Ýàm½AûõQ‡ºMîÓ”7P7:C[ ¢Ð¨t’;Ç'Hè%®Å™\âZÄ5[·4)ôÇݵÅ-îáˆSà ÌÍVz E>‹*–ßhTÒÆ]@í'§ç.Û(ºÿ¡ is´C'^ÙF®·æVGÇ}Šúwj²ú®7!ž.Ý.A;ùJm­£¨X¾‘ä)d\pdhÜÅ ºr7§f)û4è( êg~Pï{ÅÜc¶0Àüwæn©¡}w®Øýç?¢Ù¥fíº1š}špƒ+=¼Tø&9håÖÈ“^àŠí7‹†£K ê£ò$çÄ®¢ŸŠ~6å~f©"¾þ“%èÛ®ñ$¯Ì\†v½ç—ãn0t¥_ É3¹Í5G÷Æïä¦7æRû½´¾ Æ;ºÂ÷(}Ö¥¨æi+ÉÁ­a uý“O|Ç?‘¡bùŽÞA&ý|DÑšƒ7%{ã´ú{¾S ø—}Ô°¼¬#2´êÚNN¶7G‡å§€ý\M†ZîÉšùr]ŠYM§A€Šå{ö*L3ZzÜ2®%s«”ŒŠª´³]Gh|[ÁtP€ŠÓ =ØÔÕpÅîou˜­j hʰCìÙC®mƒ’tÚ5®Çû—ð*k CE{´bSzæÏP“kS˜4æ› UôSÑϦÔOùçeçÑÄ×¢»€ÊÏM­r„@šÍPùùˆ”‰Ý„‡T~Þù7ØÝ×e¨ü¼$ÂÂ=B*?Ëo˜u_ˆ|6€¡òóHÍ}¬]®  òó1qûÀÂ*¡òsãö·H£ð ›/ûa©r?ÊPùù?Ãv‚UL òóý:u¤¢f$Aÿö¾­ç>öv òó"]h bèßîOLî@;™½"èß¾¯è§¢ŸÿÉý,[ÕZúï¼?"–êÇKÐP[}z¹S{‚¯yOzf1TJf êJ—¸J;HJÎôÇÒ5`p['›ûãÇPôÔ0:·‚¡ úÅ0UfÏ-½ªI¬ksÅòç%‘1e½¿ÂïÁ=°ê1.j h»VÉ#Òœ ¿=“¸V m¡ëÞÍâ~¹°šU¬ùEPõÈ"²`] bù^+ç²µÙ½‘Ë·r$høW¸º6Ѐíg il0×Õj¤+sS?fKÜÚRTì¾°þ"Õ|~”¡';*1£uÑ\_{Iä…DŠÚŽo ?»Tl¿lÓEAóVG½­÷€ìd ¨ØûŠ~*úùßÜO%Ç—¹©ãzÌT‹èïÆÍšÞ™-9ÆÐ/“Øý¬Í\ÃNÕp<­"Íú:\@5µLHÃå<†Þl;“Ù Hàæ…ûƒ´PO‚º¥°yh>CÅòƒѼpÂ-q=I†ÛTss,éc@Q›¶¯›ø®õŸÏdßõ¾ ½öù2¬E¾¬„×ÕÕMh“Y@QÑŸ_Ñ)¶ñŸn€¶­f/µ×rÕÝ_ÀÕ€qÜ›N*Òí‹ ª|ïÕLè­*;HmÈçŠÝïs·˘qˆ¡^{ÍÙ½Ü.ÆáóÌTŠZÆM‹ñ ÛŸ÷&[b¡.ÚûSÄܽÎUôSÑϦÜϪ¼}¤ìn©µ¼ˆTÊ6G߯;É _QÔ)"™Ý NPå;ÀÒ—0´‡Ž#t‰ŒäÆöJgaÊQ5<¿ ýE}ßg'# Þ µ-ЮP±üç½!#ΧÔÉAV |Ìý5N‡¶Oѧf2ï)émýB¦w>ÈÖ€ô}Ñ\'øEªõ;Ú¶× ˆ*p%¨X¾tß ,uÚa@5"®Q÷)É\¡c éHÝHŠîˆúž'¦3tÌmiý³¯í7A™î »XíÄ6öghȦ5Ì3Ú‹{A=“JÏ_ä¶hw‚5æpÅö'úþ¿ÐÉ€ÎZÿj”¢)ªè§¢ŸM¹Ÿg~2µt=°  ¨™;SôÓ^vNÝPI­ kïµkÒÖ–N›ÉТ?­ˆò‹¥U[)4;»Ð ïî°bÒ0nKíbòóm¾€Z·›Çê¢%ËÿÃ6Ìn¬!¨êÒÝà‘r“[òBÂ6Nßh ?º¬ËUîç…P:w0E‰´dK—3Ô( |òò¸çZwf;}wqÅòÅ='ÛÒ.ÐW’H£ì0·ãûËð¦.†¡ãÆî“Ä,nP÷h»°/÷è7]&ó *vÿà¯ìFËÙ µou€õzN¸½0‹ @ÍN˜SÜ \ÑÏÎAàêÂP¢s”(C®¢ŸŠ~6å~Ž¿§ ­¿dQtOŒ& ÜŒ¡§®¤ámBµ:0•\¹“kø©3Ôl¾/Aυꆞ=UÞ4ïêKÐR:O( (Пåù’ޝ ²(x£ÍË¿ûÜqX`LÐ § Ð0)N‚–ú@,û?TO5 ªŽ^å–¶Ú—ª¯0tö¨rÉ!½r®G'kÚîDPk×$(Y“¡bùІêÂ÷‚Í€Z qÝGq3Š5¡OC&CMÞëf‘Ü_ÎÞî¹Í4Æ\c¨ØýpY‹X׋¡._ãÙ²vÊ\ªý‚äv¸OPÏç/%þCRTl¿¹àG¬>ÇJÐßZs!Ôö0AýTô³)÷Ó!J63`¨£m9‰,žÈ5¾ú›üŒµôÃ«Ž°zø]‚¾^z®Û¸0ÔOÅÚ8\æªÇ)±ÞTÐsæÔåûî¢ØæðÁ;¢NóAkC@ÅòÏ®¾ ‘‡PÔÈç&hüó†;Õ© Ü·ºª°ve¥T:vûpúC'¿ÞÅj7ç/7Öúö\nAÜ!¦j ¢ÆÙ€oz9AS\Æ€ÍV#®ç´:Ìd¨á;‚mz-E/` Ã#ê{À—5s8MP±û~±Éì@»Kí“ÆÎ×oäúÝéÃ|uÔÍÑAŸG°´›ÛÛ";4‹C)Z( ‡üУ UôSÑϦÔOù§g÷b¹Ï¡òó¸è¢tûœ•Ÿ•¤/{Üe¨ü|ÏK[p(T~>ûa´Ü ÎPù¹Xþ[ …05zËPR¥}X‚JïíŒC ä–<ûr‚¡—ªSÉÙ‡¸¯­ߎWÔ¬L‡ª¥ØQ4Lb†e—´NGŸÞ9ÀMŽÉ„ŠMA€ŽÚôà6KŸ bï+ú©èçs? [hSëg'l£¨æî0±í†ž”ÆCÅ„$îäSë`dk'®ÑàzêÔá2W,ß@}Ø»z" Úe#`¼T™kc~¬cµ¹/jC!Pm Aç4—–|qæVl9‡GD2Tì¾Ó¶ è®ÊPý;ÐzÖQ®íé~0nЊjž:+›;7˜¡bû5LÂÀítE­ WÃÎPE?ýlÊýÔfM uúZ÷jdiÇuup’è?í@Ñ­þ)Ä54L‚®ÍYJ²Æßôwhäß,àfgð¹2„ÛåZKéû³A]j75·3ôs¯qlN‡®XþaZpIz–¡ïEŸ‹ÜïÏ€åù1\¿þ¶°Ê™;\ú™XN<Ëuê™)0÷ÜgN‰ÌÉé Íž»GB~,T,ßG#¨™º™ ÉoT¡jZ3 ûÎ4µÍ4rz 0ÝôŒë«ûÂÜ…ú0´6¯Ñ“–˜£b÷“¼ Àvû)††ï¾—Öfq¿ dù)„«Û쫨½CP±ýû_†æ&•Ü`èH‡ílǾý\E?ýlÊý4ÝZ$<Óߨﲓc^ÙïçFž éöEÔá¶4Ð :Uúqó u~JsÉÐÈØÏ0Pß’¡Æ@sîqnÃNÖe” Ê?n“Ôíg*úýù=[f»¡¡W÷¥Ðm ®pÓ¶œ%µ7zsëº!Ë®M£hiøhêÙ&Ÿ«¶Ö”¸.ü`ŽNîRN®*ST{®9“ÉÆT,Ÿ§i$©r¿# Î~m$¾[Q4æ“‘Ö®1G•â'ç¾n'èaí]á{¢ ~Æ0!”2Tì¾ÖÞSÐ[;¡Ž 1à)-ä®eÄl¥:qÎRÿè(Wlh|2 žš/ »e™•») Š~*úÙ”ûé1]›×ÇZwò(±ROãÆGÆÂÁ¢ Êm+Mæ1ôæó“â­ËÝÔFïjÇ5Í›IŽ]]Ãõôì.I1™EÑ?>ÍØâ¬›ÜŒqæìç㣀Šå/1Ñgúg3tIÆ2àrŒ›fIÉ÷ìéàoþñ—¹É†£Á·*"½3|78ùLÑÒ 7…^#ÊÕùq ”>qÅòÕ¾^J•| ¨IÎKê{V™ CŒÚ æ&†€:ø7'çûmç&¹Oc‹ÿ¤1TýÌ!v9T‹ b÷÷} §Å =æµ¶½»Ëÿ>ÚkÝ"臈]Ä7B“¢bû#+÷JÚŒ&€šÕ?'iÚ TÑOE?›r?µ¯)Á¥º,@ƒëFÀ3µ\îŸJXöçC}Ã{´®ˆû½Ù.Ruf €îš¯õÊ¿ j«Õ\¿Náž'!)§=E«Æ/÷$@/e›˜ŠŠå?Y€¹ÞÁÐú×'˜óÅ1ÜnËS@/~&AK»Þýñ(Zu}-º¶Ÿ¡;äɽ®sg„CÑÛ"Š6¾N|;Ž"¨X¾»!ºÌ«öAéÃ4 »ÑÖMì\LW@ÿö}E?ýüOîgÁ¦VÒg•‰‰ ÓS¡—¦;ÒD÷¹ g;:¼/ 6õ}áÁ°ýÕÝM›¹ºôrY7ò×Ëæ MJ^À½Ô|#‰p…ë_MT;Ì¢èò{Û¡Ç£‰€Šåßxç (o tI›,h5v=×)3GÐ5Ÿ¡zA÷hë±{¸N[:‹ýîqw¸ª°ö1ÜOî2ÁaI.7RíûóÑŸ¢bùê>ΡݸP4.y¦ a`ŽæA%½–|•¢¿®Òª–öú4&šeVufhñ—ÁLià@Åîçj.ÝØº/AkÞ5—æœühކôÌ‚¸C tóý Ñb¨Ø~Ÿî7@­tE‹§É :&’¡bï+ú©èçs?£ÞäÑÉy¿(ªUÑŠ•÷xÀýQÚ†Ü- ‰óViPE_5†ó6…€^éU½?,äö¨=)9Ünm¿B¢fW®j¡vîx†º.úcnXUÀËß÷ ý€- ©OëaeçÜ–-uØ3µf õÛt‰*Áå\T7Å„yÞ‚¡f¤˜6lê)áž+ÞÓ¬°l®¢ŸŠ~6å~¦]Ì>Ü;B¹óí˜þˆ~ÜsÚÓàÀ}\­Z%æùÏYíÑü=”,»*C-N&AVgC†±gUPNQ­e[ cá2†ö¦ÉT©‹…€æDOî^o(*–?Ü]I*É‹!è3¦*ÕóÜž‹Ža#©öœMÐ3ì'i¼rD‚®|£OŠ”€ #_t« êíR¦Â"G/á»´déI€Šå3ð>…L‚VHçoZ–€ª­0½¸WMà©=¥è‚©;˜^·FêÜÆ˜y[§*v‘¶ŠôØÇŠ¶Ý¨$-üýƒk•äÇž-4粘s´Äà AÅö¿i±”­ËWb¨ëAæ–EPE?ýlÊýös)ËÛ¨oŽZ˜nb©Éö„«ÌtZ(×)n2;xc QÚá3˜¡kÇõ'†«§P4¨Ï~ò½¨+C5·o&Ëâ%èó²ZJ “ê;&ž ÈþJPÑß_jªÒA')´ê'uÐgè€Yc }c:EKöZ7—¡ SûƒFînúh7ÒÊ0œžÃsmó[€ÂøŸ*–Ï9uÌžšAÑW?6×1 íb6²›OàTô„Æ‘¡\ïEn/¹¨êƒóPµƒ b÷#†|‡qŽÝªÚðúŽá.¼WHÆ=éIÐãûwë^ ý|øm1™1.‡ U­àG§­UôSÑϦÜOÇÛYåâ{=éæË–fõt²,w™‰m5ó&WÝäìr ª“¶€§½* ‡χى÷:Ôû:(ì$Cz\$ûÝ‚=,ñ'î…})*–ßzÒ+è5qCµ¼K«Ã¸²ÄñäîÞ8î2õg4~J2÷ã¥?tk~÷WŸP6uöh®‘Ávb\~J@û9OƒöÚËËWTé ]î­ahÂ?†ðŒFsÔo%ºC2¹¿®-¡_Ëd܈¹Ê(ÛFQ‡Ä åP@EÿZ×Àã>3J\*aÈoOî—-öñ´wñÃc¬äv¤ Ûß¾YÒÓw6C¿pd/",¸Š~*úÙ”û¹hØZq|6 A«æ²ŠáÜí‰AÄrP5·¸d<3«ä^sª2—Iз+ ¾÷†æõ/…‰V«¹¯7/ãž÷¸9)C™Êù©€ndÞ¶êW,§³‰°ëw Cé–m ›xžÛiÍNWëË×%ƒeí¼JQç3,pi±=Åv±À‰K øŸÏ S§ ¢hýŸ™`:·ˆ¡bù¦×f¨ÚL†6®>*»YA¹Du$ÛèvŠjÂz&ør»^? !;CÝõ»¹Ô´eŠŠÝÿj}6öghúˆËð)>„;òÏj6ãÝ@ o˜‘éš7¸bû÷µNgŸºgJP£';Yò£½€*ú©ègSê§ü3RGÂꔎ*?~zB"/*?˜r6³« •ŸçÄv¥ž½n2T~^–XçLzST~.–ÿãÃÁ09‡¡òóå§™Zç‚ÊÏ÷zåÒ1)€ÊÏ/·~@G,ÆÐ¿Í¸ä6}q(‡¡òóg SÙE‡– •ŸU «”S ýÛû†–Y{,‚¡òóëöÀë[ýÛýv!ÞÂêâ|@ÿö}E?ýüOîg³ájÒ§41†œ³´Å|$hàîê £^StbŸƒ°ë̆úî3S¹n #Õà|n;îsåû4¸O%E«~¦˜7\2"¨ËëŸôÈ#-@ÏÏŸãÖßçŠåò…] úþç0-¸Ï«À—€$Ц=YHŠÝG3´×+P;> E玃ÎMŠ~˜8˜f'ÞTu[œºæÏË×¶'Ô¬y) ×ZAÉWFј¤}K3@ƒŸ˜}²œkîÅt Õº2ö5½¹î0 b÷Ë«<$“&U:t©±)zÌõî8Šæ TÐÀO]!òq½Û¯ü¨œä¾í¨‡Y;PK´P±÷ýTô󿹟îj%à|DP½kUà\FÐ>K`ÇÁ$†®¿þ\H(â6V6ƒsÇv¨ÓY\Ä}ŠFÞ¾·—IÐ^9«aó,C†ÎUitZ]QKfÝPg¨X~ûi7ÈcïG UÊL‘| ¿Ï}×%œF?Åõ™ìȤôäš6f@f 5·>{$Îû‡{%ÎêÍS´dàɨ¹ÙËgk½ºÄз ¼ÿ6°Wk5 ÇŠ áäe îÏþÆÐø®÷÷ðrÙ=¢b÷}òcÈ  ]ýˆ¬Ôªäïó…ìêN M¸^IîáŠþû–!SëSKÑíÏDZ!¿ÊTÑOE?›r?ß¼ßÑëúâáÐ=¦-A=óÃe«c0T¿c ¶Ì¹€ë¸Á„•=p¤è‡zfóƒ  ¶<%N»Î0Ôi²*³>•Ã픪eý]Õ¦¤vËŸä=“^ñ/dè±µõÔÈ:ƒ›:å0˾! z0ÍZ]@µbBˆòï2†zœUa5†2î·1{Á‹†j»OEê›oFQ±|lþ9ÎÐJÚŠZæqm>ÂÅ…eÕòU•ú&õPŸáF¬hú:‚z¯ ¤[Çå*v?;PìŸ<t#Áõ÷¸eŃØÒˆd†v2Jey½ò(*¶þZöå ZÅgè§m·¸Š~*úÙ”û9ÊðtûnJÑÈðï°È·˜{õÑ ö=3š¡º$Œ­:ÜÒq¬í‡@MŠ”$CZ?â®}y”o±dhÝ€XösAÇß·„ðʆÔÝ¢¾úUË?/߈åb¨áÎ)ìÛ9wn~ø"¶wD4 §ç¡D¯»òÛvÖA:ƒ¡U»ýXÌ›£šõk ¤.OahÒÒdá¶ÿiŠŠåsí¿›Th•0ô\L‰[~›k¨,½¿P[!¼‚’9[¸]ÇF‚ëŒË€Æ–€á3k‚ŠÝsÌœÔn:Ðm>\ß}•ëp³ëǵk÷Q6ÇnWl¿jì@x[hg“tP¾»…«è§¢ŸM¹ŸY:Ÿ¡Ó“ž ]p©Î uä®W gÁÖRÔxÚF–ÑÛ™ ›ƒ–ªãE@-¼²@ZmÇýó¦óñpâ^yNÂ7ä^#f°¿Ë ÚâÁ-¸+¡bùIÑ\6ʾ7CÛN˜ÃÆü¹@Ñä·gÉ:«@¥Ö³ÁgÃa®žrgV§õ‚ J¡úæ.gTQ÷…² O$h?›X°:ž¡bù"›¿£×Ó/3´¾£5s Kæþ3( Ü"NrÉ#/¨ëwkÔ6ÛVSÔpÚKIhâ%‚ŠÝþŸßä-®Q@“6ÂÙgç¸#“ZBCëymÜ‹LŽýLQ±ý¦sN€ë  zĹ;X´ˆ¢¨¢ŸŠ~6å~:Ô?‚Ú¡þ ¦} ìÜ¢¸{ôXPX @VL£úÜ•ï’aÿä…´uú=Š£è•(°i¾T‚ohATW¾§èçVvjCoŸ­£AsËß®ÙDVÿV@uç÷aeÙÙtN@úô´*n1Ø}ïLÐo½^Û‹:ÜcçÈÇÞ Ä윆ÞìëÁ}>KP±|éþ샎Coì‹`ãÚq¯Îý@âFŸå¦×&KΟ å–„@¬ãj@óµT¥©pL@Åî_Ž…Ç_’}¹=â c¹%uCØäs]sÍŸµÙP±ýRÏKæ—?DIPß)õÝÊi€*ú©ègSê§üSp'"?§2T~ž2ž8¶T~ötU nVp»Þ×dÁ»t´Y_vñäH@÷æïƒP=†NY2’¾ŸåÚµ6>u4üÕâë1ž¢bùœi\ð@‡L;(¬½»kj•A¢:`h:9"¬{{Œk3:<®î ¨IÛÄÐx;EÅîyµ›- Øè° [YÄ»Ý\ã¹F¤zm®•Ä ÜWìæŠí_™16çØR4ç‰$R/†ŠöOÑOE?ÿ‹ûéÚîºy•f. úœÈ‰™W¹áOµÙ«C©Ü»Qå w¸š[é°Ú 5ñZÄ,"(ºôüUh((A³wÜ‚ÐÇ. %eq2:HÑ‹k·2«ƒ*–ß6¾Ï0ÔýÞ/HLÊ"hNDíñ(ÐÈE¦Äf ×Î~¥ š~¡[Ï۰׸Á¹Ÿiàî¡€JÁÂßæŠåsX=€¸Ôº×2÷X×À¸5+|ÅPåàõ¬Ÿl=×uÇzÁ)ËÐÛ¡«¡·a*Wì¾RÇ%,îÓA@ß¼œÂj²c¸ÓkaÙ°Ö\3Ëo ´¸#CÅögo÷§µãº,w/3ÈÏUôSÑϦÜOó½¥Diê-@ý‹ô äÈ,rÇè ×¢~6t³+á~ÿu€Ý^Ø@ÐÆÞVL#ô8 ÙšžíUÄÐô[…–‹¹ñýÎ[³ï:Î'ŒþéÏËŸÿ Ü–JÐï;ë /Ë–r·Î‹w þ˜ «ë“¸ï7E³×3z04>ñ»?C™ #Þ^·bnÚóE`ž9Š¡bùZÚ9BZ}6 ý3Ãévg¹C—ųì£Z mðÌ`Vë/Ü1JÒÙçn´çe9™k*vßrøpVlhŽe'vÎ8…k’zžh¤1T–Ù•Dö ãŠí¯M:ů[GÐB“IìÆÔ@@ýTô³)÷ÓáëL¨êxÐ…_ ,ç:·k·8Øb–Ìý²W+¸qµ~˜”•r탦C}ëó\Û2¶ÞÑ“¡yÍ;3C›j³ö&Q=½“¢v={“ê‘€Š~þy!ï+("ƒªô® •í| kÐ++ ¾H… 99}˜áí€Jÿô4wUêÏu07#Ÿ_-!¨ É„ÕAC˧¿ø2$Í‹tÊË—´k wQûdV¦zƒ ‹g2—î¶€Ž|H’œç3T沕l>4ˆ b÷YÕ':ZH4\%‡ºÞ:ËmœûFøKÑÍ~“²>Û_4ºFòtj CúAøÀ6\E?ýlÊýììµY ÐV¡ç!{Q:7ÇBô™r‚¾28 ®­³Íѱ©Ñpße, ~×·Cúí` zÿÎ ‰Ùš]]ñi˜ºPm°y §úÏtÔˆ\H¯YÎP±üOÖÌ,S†zMÛ‡zZpWžNƒÞu©VíÿT+1tè¦äkï`‚:´R…5 ‘ôÿ±_§Q9¶ÝÿÀ‘9JHB’(Q籓) ™C*sIH¦J4H¥’ %IQ$„¦ëØ!™É}­ åÀX$ tߨ$uHV¶ •ß{ÃÉ{½à匌”ËokßÖ\–Lìð‡%~7–]Éf«?¤s²äÙ égÃ=é?á´ÓÛ ÉZµÐ×ÄZ˜ý)ÿüîÎI³úc@íÉkR.ß /5Û©ä†ù‘ú“ÜCb—_®œ~³&=û ë,d…÷n!Ù`ùpìòÅF(w¿$#¼`ηƒ@Ú;.ÅjŽˆ8Ê>\¸Žäh‹¾xxÃn¡ÜþŸÙãÐ$À‹‘üÌÐV'HU?Uý¬NýT~V—N¹©<÷ª;—í±²ã¤ò<º$„ßì@*Ï_o›Š3k®CRy»½Jy’Ês¹ü7_%±¶Ô‘Tž¿´êÊÜfj© ÞŠ­H&X ¾ïŽ ¯nºoz421à5¸ì‚¤ÜýÏÝNñÄu9Hê=øÀGÖ= ßRwKã…óÖ†ìÙ§…rû»t‡Ã;Ïi{ûdÝ ʽ¯ê§ªŸÿËýt^Õº¦žGrçÜÛ,ûìUáŠèT _jÎÉF^[°ÏƒxFøöCôJFr¿Y"V5l.œ{ë†âÝÏ @Ö0¢šìöº¬H>˜(ç` 9ï¶I¤\þ®ç6°ÁZ…H&…t—Rý3…ŸráùÅÝÂäçÀa :NmÎ*H­N K´?0òþ·è_ôLANÙÞ üïÝ“H¹|¶î§ nòRóç vv4ksn é³å¡”ýàŠ°á8S˜šv\èãíÏo7ï#”»oiÓƦ Y~Éoöß"Ô^Ï÷Û m…ðð°Fœ”Û:• ßZ›‘šKöºŽ.’ª~ªúYûùíÑ$V}É¡9ÖøôšÐ’'ŽÊéä§(M´Ÿ;E¸yL ªreäÇ.Ͱi×Þ@þVßO7ë8B¼‡:'ÔGÿ;7deû2nW[¤\þ šóŸaqHNɇß[$ŒêÈÛíNŸnÈ?-^ |xE‹Gõ52Aû4‹}~’‘ÏZ°oZ4’9w¢ZJP._£ÀŸ°Þ² Wº×´½i0•‘eK’xY»$#[´Á«Ú„Ÿ›Æ²œáÓ¹P= _Là¤Üý ?̱©Õ$‹öˆöÆÏúÆ\:¸1R=)Ôˆç¤Üþnë<5BÒ)©Hún°QAªú©êguîgæynZ|É»;Nó¸¹'…·÷Š:ÁH“‰»àÌÓ µê{@r“XNÊå{u˜ÇαDÒQçŸ?½œ“&mÂøûá£SkZ±Ã$ÒüysØTÚ•“ûÇdŠðÞH^Z†%cyuw4{ÞÝN(—/oJ Ûf{êr²cãÝé­0¹ à¯Ý‘,YÔ3*7rr\ß9p¤Ë!$WÀ[÷… åîÆÁ¸×3‘´¹9Õw:øúC¥Õ᪊)h×%¡Ü~×UœÍéÜH»%›@+ËI¨ê§ªŸÕ¹Ÿ{]µ°S—#Hfÿé…GWí&í­'&\cdŠWS°YñÕ†<:¤œ<ü–“êÙ÷‡ZCÒ3m ï·(H³®Ú`?å²ð¶ôS1 ¦pͰ±(S(—ÿå‘-\?y'œ‘x!X@¾¿’Ǫ>‡1råœ àr¯»Ðàþt¾ÓÐÉ×]°ýáÂ¥Íw±¹!o9Y;ËMzúåò=ox4â%$¯e„º „–_¹Häç¥Üé/#wéánÛxN–îlÈ}ŒÞ3Rî~¯!ãÑçŠ9’Éo&à»àæÂK g`¼C„0(r=F9WrûÏW½ƒ-£ÙðÛØe„¤ªŸª~Vç~N ‡¿#ìÐp>&õ]úšlÊàd¯Z½Yr^K$§ÞŽã2t„aý¶ã™EùmF.¨Ü ¤cÃràËÞ3rxÉWر³/'·ÏÖ¬1I¹üæÑáj1²8¬;PÛSh}~ ø6uåd»ì]`ùk ’Qé;qå©^œôîšˆŽ±Mì¬Ó £Þïªgëýÿ*—ïÅÉ([„䆯> ÿõ‚+~(Œ‘ݺ5†fk —<4…H÷Qœtÿ¨/­¾†¤ìßo8ã?}âdàåqèô…AI÷¹»“ cϲÂ-錔۟R™ ³ÚîC2Îzó]›!TõSÕÏêÔOå§ñëhqÈIåùºÎ#xÉ‹žH*Ïجǭ¦ž@*Ï«ÊNÀŠ«œTžû>Éc7ª®2Ry.—ÿQA1Ss e¤òü³v¼9ž†¤ò\÷æ |:~3Êóòç!)ùxù·ù®þ|ÉúÃ|$•ç#Sá~>'•ç¦ßcðÄê¦HþíýÖ]Gb¥áœTžÇ6†º;?qòo÷íšÎ³|z"ù·ï«ú©êçs?ûI5mÿ“‰ÑW@OsŸ]žÂïþvBÏÙ6­Î”Û½RƒæyW„ÛÛ=‚,õ3œ4TºË7"©½²'Ì·¿(TüÆû¤\6ñÏç߃s´>· ø‹mB¹üZ­†C»… €ɇáÚîÝHÚN}—󛸙õi¡—¾=®Z'4Öу­›@‹S¾0R._Vû¼0v’æ§ÚcJþf¡gßÝ8 u”‚<×h®__ÌÈø²Psð+¡·öm˜ï↤Ü} ä‘Þàöì˜ðæVÀ#}·i—>ƒÊJÊí×m”†É£/r²W›ÍÈŠk)÷¾ªŸª~þ/÷s͇÷pxwK ëßú pþ6#×;,„ôŽù@>¸¾&­Ø(|õd"¬_yÉÊ¥ˆO(tj<_Ä.þlØK¢.*Èù} õ·PNê>0çMÕ3R.“”tø@Ž~‘ £jÚNöf.sÏ"Ù|R$O‡B…Ýd´;É99ùzÌñ°edéö 6èA&'úÔåZ“z)—¯*sNÐöG²é¶Ô3—„£tÖãÜu#€4Ó£®E õôÞ²ŽIÿüÛÿSm܉‚ZuzpRî¾ãÏ™PꚇdÖì¡Ðpy¾0õë(вí ä9'mÈñÐå¤Üþ¹eÜ}u £ò™Í¡ªŸª~Vç~^ºYÃÖfãF–¶©iù¢¥D¾v,6ôÒ`e„OÙÎÈ ¾ò‰‹¬ðøÃM½Wr²un:ºˆ‘U/µÀÔÚ^بÕ|˜¯}Hƒ—¿àmÄy‰”ËoSvFé×2"; Ö¿ÛÀÈð‚ú˜Qn‹¤Ï_T8£é8sê'“ìZÄŠ9H>é ö6nÂ$)@êè)‘rùøëdìØi#Õ‹ÄOÖõì¢óŠ- ‹Þýf AUc„[ãÖW+DR*àƒg åîûÏùÌ:ŠdO<Á¢Wö?S'ð ·f¾`_oåöû›_`8yÜ*‡k1RÕOU?«s?õ>ÁÊIšHi=‡¸y=„×sŸ°¢_Ñœôí°_jf©-ì²kt<šñ¯k–ƒnÎ$;©áõ©;…)Ý· ~rO¡{ñ%Ȩq‡‘S]OÂâÛ”Ëo¸y.|}6—“{–ôÏ 8áÒúƒAmu„DNo»–QpÒx€½tzÕN$×öìˆGnÖ]¦›ùrèGHÔI¹|÷NÅΙª‹¹<…?^ ‡Ø–o™ÖÅ´*ÎKäÐÚèY4ƒ“+_bÓk¯d¤Üý’ÕѬžÖ~$ØÞòTaUi:Lú´ ȇ‡ÿÃyŒ”ÛŸÒy Nd¬çdmzV¨ê§ªŸÕ¹Ÿf'¯CÜqHîÉ+„Ì÷> ñ]øm³–Œ ß¹†7O®ä֭טúÕ~ÿüŸ}‚NðÊ£ aEi2n½>˜‘·Ìql} ½bŸ*Ç#¹î¦3óÚÀH¹ü±ùXòÍœTÓXÃ.üò›@èœ> ÉÁÞÝ!{Ò~a‚úfÌ'—•›£í£` Ý‹Âøæ~qÂÉÙ[ Ö‡}H¾õÁ~,9 ìWÖfzÂ8 øùn Ðwo–T4àƒDý: ¥9j@Êå¿›4 Œv& y³¹>À¼-Âò¨`©ßï\ íC›ƒf¯Â‘Ë2øo§ñB¦µœeØY êg²/ßr2eô, î$”Ë÷}\éæ1ra­Í¸e·)m5âñef$óµãдu™‚|îZÄÆ?°ò¨z#0]µž“r÷ƒô®B‡è¡ Üy ¯vÆÞêêÖDÒßÐJ* ]ÍH¹ý9Ù‘RF›@$Sœ[óUóâ8©ê§ªŸÕ¹Ÿ3bv£ë{kN®Zž†æ/<¬É›ÍÙ¡}@zÎûÎÂçÍÞ>öX1Óc>’I¦òÓVœ´£Æ^`Æ¿n¨’68ŸdäÇŽW ~p’æ_¶HAgv åòOßqž]Öˆ@òë4göÇßWxàÓ,H6pÒ±»7|ì‘ÉÈìvº04y™PÊ×»„ýœßÕ“÷oRãŸÏIò3„rùŠÖb•÷" /ØÁò´MBÃîþ¸hW#Í",°‡Q /9½â®z ¹Â|º/ÄH¹û?ßÜE±Å_oÀI׌|›Ðt-çYs{.\+Ÿ)”Û¿¤Q‹ÞÊHCß >=C¨ê§ªŸÕ¹Ÿ æìÆ®MíyÕkZ¿.–½íþŸ´ô<)A›GFæLÔc•Ÿ Gt€·®{…-ô€—''©ÕŒÃÏev»vŠ—‘i¹Çu7TòL)—߀¤iÃqðÕä¸ð EìÓ'X(—ßýÁEìò\N>‚Ò‚á³ Ÿysÿs¡ÂõÚ¡¿…³DN-òÃ'#´¼­‘„Ó÷’È™ ÕѤùF ‡ wƒíÎ åò=ø#yÄïòæö"u‹p­Ïqvb’ºp€wKèêÁÈã¾!lnaüçËŠU Ò9)w]É%°˜:ˆ‘sZžÝKyñŸï{Õ‚x$5ï—Z,µÊí/K³‡‚'çôh‘£0?–/TõSÕÏêÜÏBƒ…˜¶p$eŸ‡bøO¡ýdžŠ)Yµ‘´yyçtùÈÉ¥þ1ðÄ& ɬîÏ™ÏÉ«Âm]#À'z‡0îév–¹>R˜â5’ûeÄ22»ÞBÐZÈI¹üg§J±:ë%R×¼sú¹’‘Oë ÁYú ì´¨#.ï&4IÄç‘VÂðÆÐ|ÙV¡×2œY”ÈÈ•ßO3‡õG8)—OK·=L™í¤G©D—Û ¥“¦ðáê™d¢…:8iÞjzè×Ò¶­57);)”»¯7¬ÔrØ®+ [?ZAL™,5¨ ‘Þ§$ˆ¹« ¤Üþ­îº˜åêˆä2·‡üQ£ö RÕOU?«S?•§o†xj°?ÊsAX9|-'•çæŽ<ëc6’ÿß%Šž?¹Péùé=Kaßñ’Ês¹ü?]òX·§ÅŒTž¯Ó^ÂS©<70ÝaÝ©÷^ •ç b:ñÌ ½üÛ|Ú \ ŸÖ'F*Ïб?ßû#©<÷ f[îòoïGæÖ­t9©I‰a }€Mo¹SØß;•_åYÂçí&±-#2…¾¯ºâλFHfæÔÁ¯!á ÒeÙV©8ÆIÿ6QÜé[; åò%û½…ÁÃï02Öî;õ­-ü [xWë T^‘ú§žvøòŒ·7é‹dIæn›¸yŒ”»?c­sþ¢23dƒô|¡?ÏçÍ„prz÷)Ì'~ #åöÇz¥+.´ÛäðЦ0êÑ¡ªŸª~Vç~zT¤ÃtË$§zÅÃÌÝ…³ŽBß4 k샯ï‡3ró‹|˜¹®\Xµx4ûR“‰›–³Mfz@ZI¦àYbC®ñ¸ ŠšÙ²éyhéà…¤\þáIuÁiiì`wH›´XX°ÑD‡ DLƒÅ Ú ;üŠSÄŽÝÄHߦ¿ÙÕ'±Âm6y H£ÅÏ ©·¶P._§æ?À;Ý’“±÷*á–óÇMøÎ9¤s“áðëáfá“wkÁÀØV"›¿‹ƒ3mÖ!)w¿^tVe䇰ÛìÜ‘ÃÂ{7 ¤k-F²¢ ˆºb„¤Ü~­@KpÚd*‘WÏ/fåËj!©ê§ªŸÕ¹ŸÔƒ Ûó|$ƒ¸ÀûÌÂÆ?"Ái]…‚ Òu_·DN.‚&³L!LÚ°¿à~Y]!Ë© šSº"ÙørCþºûZa‡LmvWr–¶ál̽׌”Ë?éÈ0˜ØÞÈY½]`§k=aÄg¸™TŸ‘+l‚ùÕœü®»ö<Uµ‡Bæ„öHŽŽŸÜ¦Â¤‘ñ6Vg‡(H¹|Ç}ïÃÓ{6HjAhì¹P¸b?èGOr K0ÄþúÊȨÓMY÷‘ô2[œž"”ýùX¡»Ûgië­ŸbÒ„&çgÀüÍG‘,üh]pÝñœPnÐ.u\>TÉÂfk1þè ‰TõSÕÏêÜϤñfÐõÊ)$}jªCí¾ÿêÔösYp“^©1’íéÂâK›øT­gŒ¬|]¤8™¤¦Æ4¬¡;Érÿdü±ó„‚üxõæ?ß+[Òï]4¼w=Œ¤\þµ¡.U’ÌHƒËÃ!Åí†DšœÎ\çäÅ)ž=x»0{ãh¨äÜÕQ̽áqá5=MÈ ½Ï–„rù^4OõˆP$#í@AF¤0fÒ0ÙX›‘—Ù€…'+ló’¦Ã¹~eCH¡Ü}¿¯&°J' H§6`{|³°ÃNKÌK]ä‘*34ó@AÊíÏ[¸/Á Wåp[cBU?Uý¬Îý¼µ(ƒÝýPˆä Ý؃\á3{®Ö¸ƒDÜÜÃ;=ÇÈ?XFe.7S·À¨õ™Â·Ó×á™DsaãA¹\?~½ðµ‘?7?;É“í"YH»dFÊå/ÿÞbgd+Èç… á«ÑJNþþ§J%;FJä] w4(mäÅÆ9\['Éžn.°Vè©y z[ ÃÌ™t®ŸP.ß¹aN’­½O±‡—矼Á4{ì䤳é"Ée|†0}õTèRÓIßÖÛ XâQ¡ìßou‡A¿Ô c†L‚ ‚„QÁçóïZpFúºÚBɆÃ)·ÿñ‹WÌ/+ È=IK¡ª«½PÕOU?«S?•ŸÂ©6žµ2‘Tž)ÚÆuÃíTž·1º ÚÎ@*Ï¿üáìæþi@*ÏûþɆi×8©<—ËŸú½€]h—ÇIåy¥}±xO(Ês÷A8¥þ=N*ϳ™;›ûœ‘›oÀKÆ`’Êó¶•üSù(N*Ï }ÖbÓe£üÛû×gAß>@*Ïm2Bzy/$ÿv½E®àÑF"ÿö}U?Uýüoîgæ¯/ðŸ<4e!^ÚWÈø›žX`Ø]˜jƒEóý©3<;ond½njX2!["u4 ñ@óÞ@¸t VwØÏÈÓ[Ã;ÓTNž³µ… ÓŒ R Ö"I¹ü÷ã߉OGyïF\~VSžvœ4– C÷V@è·Eœœßb2þŽX€äŵ›±Ã˜{œÌ²7Áím¦#¦ Ã9uÖ)—/ܨÌßê*HÏâ+‘±…“pŽ.×Aröêht®½‘“S<æQæ+Èû¿tqªU@ÊÝÿú3‹¦ôAò¶ÞZ¼iø¯»¢ÑjÚ²|ýr¼U1H¹ýŃÕ³m‘\dC7¤ åÞWõSÕÏÿå~¾M™‚sVO2®‰Ö<°Zø(j&Ú¾ñ/ÐÁV‰Û…i÷ñxŸTáó¾¡ìcâ9¡ç°S[øÏçŒÿ³–é?¿ãG†ÌÖÇqõv K*âï;µ)—ß3õ9¬x™¯ .¼:qœÔ]óÖý°A²öÈ£Pku”Ð&qNŸbÆÈëGcʵN@6{t›]º’+lÿvÔÒ)d¤\¾ö›¯Á‰˜fH껜‡UÚ“„ÛÚ%àç ‰¼Ð;³š½fd Ö^6/à<—>ï„À[þB¹û ºã§úHfõÁå{_pòÙm?>øÞn =|êABL PnÉ´g¬Å¨³HºÀ;…ª~ªúYûyoƒN·È Ù­PûT’Ð4׿ÌN~Wtffgw ßÜž ھɧ÷žÂí¯:°Ÿ“¬ü¾wm$1R._ÈÞ\ðœ¸IÇÛ;àªešðÜä•èi?H½m=‘_#œ¿(œr˜‚ÌiüKÚ7g'åî›w Áæ;r8éØ Õž‡ §.t‚ãkiï}š¥?¹ÂI¹ý&ÖšX8½ ’>sã½ç%RÕOU?«s?B?ñè¼ý@“Ò¹á?=#6ªAሔà›Åháæ­û¡qþFjö^ {ܧqÒjižÂ½s‰¼:R ßdä÷×vèp8ɼð¦q#åòöŒƒF#Ì‘kººgöFí^&íé¹HX®®Á[xÖz?4€Öý72rúËÎàg¿ž“_ËZ*|nYY 7N‡åòi]_ +ç"™Â­ %à¸Ðæó!~70HsÓp›ÖQÂb§r>­¨±P÷Œ:{ãœ/”»¯÷,çõæä÷£èt;_A†uºÂ'ÝýÂIà Ë1¤þ#‰”Û¯¹´”=?ÉÈ®sBAÍ{’ ©ê§ªŸÕ¹ŸWßYð°·ȯæhæ†Mu† †•Œœl? ,ÂoHä[÷£¬oÐaNº ·ã–“dŠs<'ä1Ë1_¹gMp_설\þd_ Í­ÜÐ_žž0ÆÏ‹V<êSSA:7qc¿`dî­\Öî2CÒÝÖ„ÿœºXh8|!8¯lÊÉl5w*?¢ åòíiôˆ]~…HÆoíʾ.ÎÜu¹¶÷ÒXSôÞõŠÝ/S…ZÚ§`è–@FÊÝoÝt9Îx”O:]ôBM¿x‰lÆf ¡‰¿Oæ05W(ûõuHË æHú'Ì»>û…ª~ªúYû9¤ é|È©c“ØX›BaÁX}°ÚàÌÉ„¡©ìªé=¡½áM^é¢ ¤S'Wþ}u´pdT6¬¨ŽdrÉh¨qRxuÛ8L5÷ Bé-#åòçe±:‰ ‘Œ íÇL.ádN©1Œè¥¤ŸëFøºã#s>[c£3£‘tÞˆ¶-Îp²äÙ~èàF íêL…š-Q(—/Ic…â¶A’Ù¶‹x`B0¬o(œuš‘eOzC‡m$²w} f»ÍÉOAíyƒÂõ)wvötlÖ¾#û•ÃufÆ@fÌ Ó®=?u¡ÜþOÁëívÉì¾5ðʘD¡ªŸª~V§~*?6s+™^Ý< •ç}Úh(®æ©#©<Ï™Á:§©<”ÓMÒ…#H*ÏóŸoai_z©<—ËŸc#ÖÃIåy›™' ÅumF*Ï߸%`ûOé<7™‹°l¨‚‘›ïÌÎ,®åa¤òüíÔfðëE'•ç¶1A£6ù·÷½W F«ÔXF*Ï˧t„ûÎ8ù·û–õÃû4üÛ÷UýTõ󿹟O†|„ÿdxýp©þf$‡®7‚"Í¡ÉÛc|kþZ m4«'zîúüdiG÷1r¸¥+˜†B¿>ͰXOÈ·§k ^üBáéC9¼rÒMFî»dšzó„r÷Í´‡Èe…HîkCù¿Ú¥^àÚm­˜Ðò,?Y¶H¹ýÓG3»F ‘ìÚã¿û^S(÷¾ªŸª~þ/÷³W_ ¨ˆÈDrùÝãl«óQa½O£Øædz¾64ÿ~RØ.v2Œ`‹¤sÜöÌ1Q¸úX[Œ‹é&ŒÿŽzÆ89ÂÁVÏ}ËH¿Y—!>÷-'åòGlÂZqÍ%²«ÇlÖÒ—‘q«KÀÿnm =Ý+àzŸ›y½æ(Jßd ‹lÉ®SBMÞþœ?Åýç‚ï©cB¹|Þ$4úRÉ"Ûùø<+…“]“yúŽD KŒj+ªr…Oœ]`Òe@23i¼ÔÅð Pî~Ö·Ì»N!’ÑgN°”q\Xj;‘¥”q [ä@=…²ÿ¿õõGÛ7¥dä¢@ÔìÕHU?Uý¬Îý¬Ós{ß=É€ÅñR¯ïÂ1•óÀÆõkò·ClÕVaÕîoŠ?"éÞb£…ÞQÐ}H Õ_µÃTí0á§ xg_€d˜eÿQÑM(—IÒjLO¼ÇHva"{´²õ?=4]SÈÉóKàÄG$ëÍU$ùf <æ95ü…Qƒ.Á©M9Ûkôºøœ“rùâë„`P½\Ùc|NlØ•‘V% ¬üá) GÎë šžŽ{=«ÎÌCÒqÒ 4žòX"åî?ÝÌÖÈG²øñD–¥“#œÕ²ž¼ê䧃» kJw)·¿áÀºxf["3K­Ù³NiBU?Uý¬Îý4\•¢HOÉGÒÞn¿muL˜=î”&ÏÒu²ÞO¬+4Ñ ÄC["¹¡ƒšïH·!+ï†KѾ1@¶lF#l„ƒôŠ™ã¥ÓŒ¼¾‰ÃþÊMœ”Ë¿¹c7Œ ÔÚ ¶õ?)ÈfWÔpnrFú¿&y©¤a:Ç>ó$2#1ëWɾ7&Á§‚|aâ­<â%”ËoTÎ +F¹nA¢VÇ‘ÂKv³uûç!™t¾¡4éZcáØ&˜—u:#ͺZ€vÀváôZÙP÷'ßv(A(w¿ðĸ‚]j{ôm—¢È®¹SxÈ®€k4ÒvîPiÎɽBÙ¯OósÎ;ËÉèúaÜw¾’ª~ªúYûYòÖËžÆ#©§geoC…{Z{@|‡N&íkamÎ Ÿ|k •3ç0òQóñ·Ÿ“Ck'òä1&HzøÎƵvp²,b“T¯8’‘CƦ€Ù R.Öì~ˆ;BS˜Ýc¡í#EÚ¢ë 2~N…”©›ÎÈÓeÁ?¼¨€´:¶¼õ‘ÜõøƒCUœŒõïÈïl¬¤\¾÷u_ñð´m@ÈÕKw [ÏJ÷„©m¼¦p2âÀ|ÿâ”ðÒç¶È‡×Rî¾æÔ&|÷°-Hj=và¾o£„Æ1ã!v\>åñ|‰Pn¿Ž4£ÜrdÖøøŠ·RÕOU?«S?•ŸïÌ [÷÷FRyî}x4›™´—“Êó’/ ÖÄIåù·É‘ØhU#•çÎ/|Àjñ!$•çrùgoÿĬj©<¯cÿ–5ph¤ò¼Í‚8톤ò|ÛMKx¢•äßæóØ#}9•¤òü£@·í»9©<ß;©˜z}aäßÞ¿®·Š} FRyþaÂI°êmÂÈ¿ÝïíÖ×…&ù·ï«ú©êçs?{˜¿…ÿdvt4Ÿ²·'_G-çõóÆ }-ÂÙá)µ r] øªÖRèöÕï}˜.ô»ï„=S8©)}P¬ Î@²¹é7®>e˜pì2ÎŒrÐç>àA߆”Ë?¾1³z/YÉ6F®G…Ã#ëÂ_5…6w,aæ­›9jí*m\Ès];Þ䑌|¹ˆ1ýE8ùé Ú¾ŒTrùTÌe/÷žâä™ë Šv.zþˆÕ9yAxi]æ±WIµÍøó«Ž@N¦íkÊÝÏ9S…ÆÃ€¼ž}L¦[ Ã~väÐ$µŒzð ¦öB¹ýÇ»çòò‘HÖ¾]ý‚>rRî}U?Uýü_îgÐg>óÔR _Ö¸¥ˆÓX/¼Þç<Ÿ:§’¯³{ UÇ œ4?¡æóz3Ò`–¡ÍpõÂÂôë¼›wKNþL[#U9ocäõõqRÐÍuH6­Z‡.« åò×rva¦q ßÔ»Êô-Ž _¿ï GìçäR‹¬Û-+$Ã’ÇCÓ¢hNêšJÐÞËÉf‡G¯] äµÙLk¢P¶Ÿ+øó‚µœ4H{Ê­~•)ÈA&ƒ¸7j"é7®~ÛWÊÉ*§Ð0Y äáEÜ@"åîgݹÍneöÔÓš Ãc´¸A[-…°â&³˜[ÎH¹ýMæpƒQ¿%Òîà9¶5ø8#UýTõ³:÷³0¼aAʼx ½_‘>\Ø% ã‰FÍ9¹kýZ¼ó±+#{tkß#·óç-í158=·8¾ÛÏȦßw@¡Í<YtǃZë9F§6GÞc¤ì÷G½l=ÈT»æðõ¨ðºF‚´dÅb$WÝÉå/®š6/bo²â„õæ½SnJVÌŸNÁ³Ù¨}ÉÔ½1’rùJJuÑØ%]"ïìŽwß%2r`LôlíÍI#O/ÜS¡+‘‘={ƒß°*Nf…7àŠ½ H¹ûYaï§@>÷†Ÿ¿ÇÈ;­6Áâz—…³<¯@š]9)·?Ê*Êó Ùáq&\ð¾ÍIU?Uý¬Îý4Ù:“ù=ÞdƒwGYò’ÃBãе˜z¨†pÜ8Oü®7JX´3Â>ôDòö‘!pfì6áŒZ‡ é†B·ua`±-Yh»UŒfÎï‰-6®Êå··"ÝÒ,¸¸4—lª»Ä̪±H~]jüka+ô¨ˆÐ]ˆÅ)ý… ª¡?ªÑ{âëÙa@Êå[¿¸/6xÝÈwg{`Ò$GáÇœù;›‘‡þ,E¯hS }mšs á•gæ°íÆ¡ÜýUO Žf¤Oý'&l„§àùª™Hêx-‚&'Ó…rû/¥%€N\(’‹MzB£ÎBU?Uý¬ÎýÔÍþŦÎ<ä¸_†Ð{kŽpBETo*<½÷ç×¶ +ËÃØèü|$ýÆÅñëlùÈöÕ,Þ?¥Éãûíªå­ÁŽj6Œ´ôLª»ÀH¹üö+¶Cé÷ GE¤‚AÆ$¡{H,nöNV5,ƒq|óƒŒt~€?œ99¦Ãpt±ucäÙ~7Xÿ‹ܵz(èæEJ¤\>C³Vسÿ _o~ÍçöÞ.üñq$:½],и9Ú<Ž~7C°ø0Kèv:(H¹û×uCvÃŒ4ø!Œ;ÿB"÷ÅÎdwçlArE+þº}S¡Üþ3&F’G6’%–׸ÅO¡ªŸª~Vç~Z5 ±õù£ûhVtH¨g¤è·}§ptÆ:Vt>N8Ʋ;nwõAÒ*g Æ--âäQçü“¹’>†ÉÜvÛbùsRh3å'{õ³ãÍ×oAR.¿_dŒÕ12|@´¾ñ–‘Sw @W‡®@êãKÚÚßBþÊE]X¡hÁ¾ïª#||±Rá?¾5’=f­Á(½Y’P&ß“€`^6æ!©m­ó…Ýì7rEzª0¯™¡¤6{pü/M3¬Œ“%~ü{J]FÊÝopì_6“Ȫ6wÀj}i;ȸÀÿZFÖ:ׂ,ê)·ÿsö=>½Q 'Õ~5²\±Ž‘ª~ªúYú©ü츼ld©<Sü–©<¯òUÃûk1RyžêÄÞ&d¤ò<õ÷zü1LÁIå¹\~ÃÌÝpèÄzF*Ï‹n¤I:TžG¯Ôƒ™!ë©<\Úß/Ýäßæ[ÙÕ‰E8 Êóü݇ØëÛ€TžWfT*Úž[äßÞw+¹Šž(H广Î~‚2òo÷5ìŠFþíûª~ªúùßÜO^ôþ“­Ž±Í+M‘|°žûj+̈êAeï9ù,8‹=êzKØ-n'´3ÈA§n¬÷UŒü´²-êä·òJÖ%¾""VXy]Ãî9œ|]`ÄÇMÙˆ¤\þcá~ƒµH²„žŸã„‡J Ðõ·–^Óyû!Ó„¥³Íx•_’7ßå'+œ„îåÅÐ*v$7{—w™ÆI¹|×ÇOâ­ëÆ)H¡ŠýÕ©¶ØÌÛé ©õù+¥.œh ÎN{'Ùö§=¼µn)”»2fˆ¢²Q6Yî{mÊšj­i&åºl@ò¤‰ZZ-”Ûo}¶'¶šeä¢e7ø¾ËÛ„rï«ú©êçÿr?¯ê±3z#‘´Ÿm“í",é&i-]ÇI§Ã&Ült9?í%”eh(È’ï%p$IÉ q¿¥Ñúy@æ­ì -ô2„~©ë0oÏN_+çë)—?]W ¼{ïFÒôÞ9¶¾ßaH-3=ÈápÜ­Žpºq)×»šÂÉœÀ½Þ¼’ÈûËÛA»¾í,ìõMñaP/FÊåÓÍ*\“ct~+;yõ²Pën}Vò!“K<º×ÒäÎ-`×”]œ¼íËÏü逤Ü}ö @ œ dø{cVz#OèWsª;=áäÓ)ž¸}”#åöûë3í§9@Æ™Kà™(TõSÕÏêÜÏîeŠßFÓÌ YÁÝr§ M:ÆqhFÚ›îàœ[9»fÌ%$Èð;XÞ‚u–ÅÏD´¦Êe]­Ô…rûkCÙºî@ÚyCԨũꧪŸÕ¹Ÿ'^à~ƒ¦ y£Híž; ½2Üx§È¹@&]/+HÞ'ŒJéÉ\Sj#iPÕFQò6:Ÿü™Ñ¼çqrˆÏvéèÚK±[b?Ï$眂jžoú’rùçÞlÊ›Õ9ˆäö®I¼À`Ð)©.¬þb*¬g>mç-ô··h0rVç0LÛÌÉW»ÏZË=}ɼší¡"WI¹|‰þ0·ÝuFÖ² .?ß&t¢ðI äW¿/Ò'÷Pa^^{EÜáýnP²³žPî¾³ç)¦ž¤ù¶×ìNiŽÐËu(Ôî£ ‡[B|»!HÊö§Áæé¨àdÖl; yA¨ê§ªŸÕ¹Ÿ{'¢Ûù¡HÚ͵ë+ŒZäÍö”îòίz`öàˆ0jV¤ôÞ±°*³œ-ŠY(”.üæönõ9ùrÄ |t‹‘ëa5›þê»/„—>pR.ÿϾüʇ­Hn}Õµë… Û&âÃ Ö ç|ÖÅvg =£ŽÁÙñöÂM­7Cþôxá™WcøÈ’~Œs¡ m¶ H¹|]·„د-iÖâXY‚‚|PÈŽèîrÔ.Pš±G^»ô°;ËÉ¥××ðÒËÔígT¸3ç(ö®-ÁÂâ°Ð£ž1s K@Òn»:.Êí7aYB È܆1ñ»ª~ªúYû9ñܼžÙÉZÞ^X°¾¾pßè‰P±% Ès£ÀèT‚Ьj5Ô¶N¨cŒ°bÐX¡Ùèöxöw€ÐãI[þyq–°ýõÞlÈèHÚä:âË_ƒ„rù-¶›¡…'’ww®¡áÒ9Ø¢»…ðVNf_Lç¤ß?ŸƒÌFR­"œíµÎæ`<„=ݤél„6Œ”Ë—Tp¼nsòì‹à×ê—°E„/ôl·È*ýàñ!Vè°,×¶Næ¤ï_T¯h¤ÜýFñ&0ÆöÃÚZÃëMé”ÚÞxhn[$o_™ˆuý%RnaSm¼:;„£ú†æªú©êguê§òc€§/”rRy>ôU:üt] ¤ò\wÎè´­‘ÊóqŸ±ìFû€üÿ$çp§éIL¨ôÈåß?r:4m‚¤ò|تüT¸I"•çþ Þ(F ERy~õAk8þ“›¯,÷ lmj…¤ò\{~ŒûµHåù©úùÅ?KüÛû·o:À}›Ý@*Ï¿fÆñÅÒOFþíþiïZÀ¯ŒM@þíûª~ªúùßÜO‡Oá?é߸¶bgƒ ²$Ç\‘1ËΆ4©ÙÍ×¹ùøÞQ;Vè1õ8úî°!57Æ‚®~'Ÿ¼üÃ'öZdL’–†i O/IäV«—;ß¶·;ÏH¹üÅXŔߜ\×Ù’9X4A2«Í:E»¥î@&UÚ²œâXáÄü ?(—Ì«»ºV<ääÎÞ­ùTC'$ŸÜ]…ÖÁÿ|]ÿ§\¾É§?sóKËüÒ}¿ 6]"™Tr~³6À:Kx¸^ üð2ædǪ¹ÛZI¹û¥¨Ž–§‘L練f•°…â._c½ÈÕöÝX\Ì¡Üþ=#ú*»¦ ùêÚ ^üh´Pî}U?Uýü_îgI…{cmFöœYÐæ]˜°›·Ÿbä‹8 F»±e„½Ÿt‚/ ?q²òŠ6ÛéÂbýZè4ÿaéÞ¤‡âù´½Œœ}¦”k0$çÇ´ÇèŽ3…²?_µ4­{öo…db|O>¨yaï™uÀ®$ ȹ3§C®}ª0êèZ˜Ùe’¹u‡€Ó“DaÙ¦ ø£›6áiO¥± åòU•6E‹áÚHæMnˆ}¶ãd ×6°nE2WGšaB«Rêˆmœ´8bÆç¯¨¤Üý?•X2t ’V»áÕ÷žÂ«Ã,`ÎA? ›mŸ<0Rnÿ“ל/_ÄI5ó'yv³W3RÕOU?«s?“j]°™µë#Ï8%Ks‚jy#½‚ ¿P˜dóó,„>W=¸sì‰L9p˜{l仯pôí1FÖ|%:Ö9r¾?~=?YغÏEEÁúŽ@ÊåoÿàOÝßIïàšX1QSxéÍV+ŠR§ÛQx~bœðÜí³¬áël$ÝO^-Ø3=Sxl?è1VAž]—ÁbÔV#)—¯ëÌOüyiNNò?ÊÝ=¿Ù–õÓÄñ@vk… 릧°… Â.F‘Ð(d‹Pöï}<4É)nöèÑ|¼°ÓÅ6è·‘“_[v·™Ü®%’rûß%7‡¬iü0²Z®ªú©êguî§U¨ÛÚÈò‘sÙgV‘ýÀÂú&#÷J¡Ó¤R‰´ÚçÀß$Y>Ñ‚¥Ú'¾ ^ÚãdÖà8h’¶IË#@óe°‚¬Ùûs:·I¹ü :c‚Ç3N6åèg¼_Ø}?‡¨.ŸY¼àø¼y*‘»Óò¸Æ$×Jš8Ô ‡pßù¶x÷©¿pþ+oÜæÝ€“rùt-ì¹G ÄHÿG=ò§÷Ü*„ß+ .t½°Ç37P›žŸKš?Eh_³nPnÿüøP÷w’þå:P«g²PÕOU?«s?ûn® EŸ½€<¶¦@åráü{Ñ,#¢’IR¬éÞ”±{m€ÔJï$oFº»Œå÷Õ‘ŒÈ8ÈM†½áäü[ ìqÕUFºÔÜ]Wžå¤\þsfxáC±Df^]ƒ&ûƒ9~ÈYtá2'Õšý²n­!lhéÄr^æ3ÒήäÝ9. Q¸Âo“H¾¬!Iç첄rùªÜ;Cƒ¶Œt uý‰›„Ïž·d÷_8Yù¦—Þþk“z©¼8,ȺN3í‰G…r÷ƒW,FíÕMœRæ‡æÙ5„¯§d ›%@¼7+›Œ”Ûÿø”T¨½ÉÖýó·rRÕOU?«S?•ŸàɃÀÄ%HåùD8^vÌIåyI“yËzsRy®k Á³­ %RyžsÜæÜ߇¤ò\.¿…C&_ºÉHåù’Ž›¹-·, •祋7€SlF*ÏíƒL°|Î`$ÿ6ߥ‹ÁPÞÞœ‘Êó ïð$› œTž÷Ú žy+üÛû'¯BÇšO9©<_’Ö¶ÏÉ¿Ýo»·SÁv#Fþíûª~ªúùßÜÏG/îÃÒ´Î"ÈÖ_Ašlš ÉÖ 99¶ÙX½"HãÛë¡vY°P/Î-æ·nm¥öÿØ»ó¨ÿíü!IB•$)’¤R÷õÚ I†Ê˜©’)™gÒ$i¢I*SŠTê¾^¯„$If2ïÌ$3á÷ù®õÛ¯³Ö}Ö:×òß9­ûúçñÇvíýijÕÝV!˜›{5Ù°ÛŠÛ¡p |–qƒmÙÍŠª¬É”{[âJå/Nì,N~·‘ ™_ SÛæºÕÍÆÞ8OÑl';Ѽf‹ i¡Ã¾ÝI¡¨çÂ3Ôttˆ€Nð9'2t~€1è=Û˕ʷáb3˜XšheE?x³â 7è\>œžý” ™°½Y ·Xž*ë~i.EWÌ_L4TêþÅŒˆÊPã_³Àóä®føKAuE*A—$·ÿʕڿVT¥Fº€ëv%²Í\©÷•ýTöó¹Ÿ9žC!¥¹EïŸ1ƒ_q\}«ƒør .É öä;rgžíH}×¥q=În#m£Ò¹öÀÙ/³¸¬.õ™Ö\_ûeT‡MáÎm¦fåJåOoCŠrK ªw¡†Lûp«ro~ѧoE\7k2¡‡  *ÃGÊGì'hùÇDǯŒëö+I,½Ñ‹¡áÃó„$¯}•Ê·ô§TíÛèšßÿ÷êF4·õŠÛ¸pŸ íh[ ‘gQÔÉ]bk¶úeM:Ô/YÏ•º_°p*¬?´•¡U­ÝážÙN®Ž÷Ap>³Œ oœ+ »ÓVŠJí¿©k _›ï4Ù$Ä-[¹Ê~*ûÙûéÔ§è\΢¨Ÿé#òéY®Yy¼èß ÐÒQ—a´Í‚ú8÷†ØŠp@õÊgBÀn„ãM´K@ òàðn9E—¦@¹¹) |eà·8š¢RùývtKs¯ô@± Ì!%\£ Wd±Á"@g–„ô?¡Ü73¼À)cA¯ÜM€Øæõrô¦C ¾–€îw¾.|" RùN<ÍsËmþ`¤îÆõ½zÚX¿¦h}Ûb¨›ß“¡cÎÚ•D‚VµŽ€{STê~Å»!ТO2CºØÂÐìn¯c§@-r,·‘ÍJ(ûÍ•Úÿœ„ ã€:‡çÀ«ãà ªì§²Ÿ ¹ŸÎwö‘Î=®Rôþ½!DuÛm®Yq)Ôªì¡sËOÀïc)a3BŸêÔûœVÇR´\X“;2”dŸ&ïN«p·W‹ôæ'Šn>bÉVÙ;*ÙÏmK`²ZA­fG‚ÑÁEÜ\!Üh  Û‹ó`Šß8î®ËG!¨´š¢2ÀÓjOŒL¹*:±E5›p¥ö'G‡±XDÑ7Ç>“{ã/s•ýTö³!÷Ó#s‹ L¼EѪMÓD!°’ëÑ~ÜœTÂýî0rï}á.ß&.=[0tÆ=4[C‹ëpg¢h¾ð«ˆ.ýèAg¬ª&è«Ú+ÂM»4@k'BØ¥\©ü=3“Ás¬*AKZó¥öhmö%h.k hۦנü’Aõ7F@Bì6†~ìi©2¸Ùg–‰Û.G hÁù\¢~d9 RùzÄAù5‚êd‡€Cô/{4}ÒH¸xnCG6Ö€CVãþe§(ÚgU 'åÄÂ'›+ußijtyyˆ¡ÚöˆeÏ£ÜâÙr ­Vÿ;—\ì¨Ôþ7¥Yb^ÑJŠV.zCíÎ !¨²ŸÊ~6ä~î=oIÙSŠGGÞIçú«ÁÈr†nØžMâËQôÌV;V¶óWýõ2öhuÍ7Alµ~  f“òÈ31•Û5¦†œéøEµæ0r[•¡RùÕæÂäe-(Ú·ºzŒˆãFÅžƒL¯«"ZÝ/4GR44ð0I=‘ÊP•7qv!ÛB¸5'ö‚Ë­@@u†V€CæL‚JåË¹â ÆÃu(ºÑÞ<ïmá:ìO&λ,·è“àYø¢ãVm„‘/ƒ-ÌØ®ÍTêþ'óÇdõå,†VÒrñÎ1®©ÖX½h2 ›“)$ÖwáJþ|ô¤)Ë8,Ô¹yõ“ïä*û©ìgCê§âÓß±Œú”†STq^ûç¨péOF_#¨ÓÈ®Lõ†C§vŸÈÊ;´åúïr‡¶Ðœ[•Ñ ¾l^Ã}ã7 º»[êä«ËÖ¤¨Tþ*kw¤¿“ Í– dÙî¸Szî€Ïw š¢·Ò,p'

        m ú ÎÒ|Ñ “™h<”¢g.›2£ c‚¶gÙn†Î¼;‚êÑD®ß™tÚê-wGÌ Öö 1E¥ò÷hÞ™]ùHÐ}ßiœ™ ¦ãüÀÊa;CmÜM¡Ã ÜAw7Ø´jþPÙü«^+@ȃ/j9,è÷ÎP©|Û5NQý= zvw]¶ËÐá%]Á{Õ)Š^IId» ܲ!fP÷Ñ–¡†¤§°Ó:ˆ¢R÷ýÏ6‡!·æÚrGGZÄí!öÑ©– Õ…)p³e,WjÿÞ§éðRÁÐÚÒpuØÊUöSÙφÜOßqÌww†Vå¨Òo¶Ü¨àæìŠšL¼H&ŽjÅÐÓkwÒàî zLµy>é9·|JS¶lFC§L6dµ=skWê1Ó¹Ú€²F…dI*W*¿Î‚ã4nL{@=ÆŽ¦¯èq¯ï*&#{f04ðÁ]amL7Ù©"ôSônÕI˜úÊ¡Ÿ<ÈþŠ4nÙª'4ÏmW*_‘j*õ¾3Ð7]VÑÆEk¹ê9¿¨ŠÎ/ӠǺlæ–¨kÛ5ô‹°ƒ?ŒáJÝ÷ÚmC—šWÑê}Ws+&‡§;PTÍ ¬4»2Tj¿EºÜ𱇡‘æ#Ieâ&®²ŸÊ~6ä~f×w¥>v 5 ëKs’sç÷&Q­¸ý7§ˆ®IC¸Åž`xãݽ3s 8LÖ‘£ÉÝßSÛëÓDÔÁ>QìcKÐæåÓÀçìyŠ–­¾DÈÓ†Jå·ì9Rœ6KЪWW„èŸí¸±ºwŃ×2T¿‡?½}Õ’+óÛ á;VsoMX Ö7¶r;»G5–´ h£÷Ä£¶œ RùÌ·kÑßÃcuè¹V>l]2wDÿšó]Dwx6f#ÏlÐ’« qá.@§Û‚ùKûq%ûÓÖ– ]hÉT7¨6ÙÀÍx»ú~^ÂP Yw¨ùµ‚+ùù°o’è4Ë„qÜÝ,߉¨²ŸÊ~6ä~ú¾µ¥dwEoÿ¶¢³gmã~‹Ë ¹aCÚÿ@+6Ó¸7hT ¤øÜ¡haiœ:;‘¡úä12('h`Û¥ö5{Üé"}qC÷«§iZö•Ê?ûr yr  ­/øá¯ :$pMs‡:1T*ß±7I‹¹íß=–lË>È}5­3²&– ŽÚ¦¬~Jc@_ǃÖöç2´ÓÁÆðxj1E%ÿ~“<á}ã@@ÕÅù0µf÷HOBjbLªµÞP¼žP ¢’ŸW©[‹b‚ë¡ÖÍ-UöSÙφÜÏO¹téAЦüÓÂEtð+`[=Û3Tž<ƒ½/¾JÑ·ã¦A‡;zÜ] f„gr£7f@ò¨K"Ú?á$tX¡ÉP§úã$òy9A ¢Ò ¸]¸ •üþ2Q ´OË ª?Î+C¸9…i»#¸­FŸ$ñVŸ¹)úB@¯^ ½},Z\µf EÕâÁ›yÑ Õ¿OˆÓ5w®T¾ËS‘j£ƒ€¾êÔ6—îçjö5b>×ì¸ó24Y¥ýîˆ_›hEé,Šª$ôaê>” R÷ÅìÕ0æÂz@ŸX‡À£œµ\?íPá×ÌFÜœ/Èä+ ¸RûYçp¨¯”êX&‡KçÔ¸Ê~*ûÙú©øÒ 1囑€*Ϋ¯,gÃ\ÇSTqþâµ!IÖÞÇPÅù’Oû óób†*ÎKŽ7[†*Î¥òÏû,€ñæUœŸ;d u[´Uœ;,Ù!·¹¦MPÅyq®X¸%A@ÿ6_ÈW;È¿ž¨â¼•Ö5ÚøR Šsý¦lz£ €þíýÊ"`xÚ*@ç™^+¡WA »ÿĦð¥¦Aÿö}e?•ýüoîçöç—á?YÒñ{½Ž êÆeÐógy$~®×gè#Ÿý÷^Ƶ1µ‡/÷ h@¹ÚV@Q›f*lK=ah÷éßiÀ‡½½AÕ3kª;Ëü‹—s¥ò«öÍ„°èñ }¶u7¸Îá¦dõ‡åqAmch£/eŠè}ÍþlÔ–. Žé϶lK¢hÔ¯ãté@w'¼'å=îT*ßÕ—$âî‚v×ÔwÛ=Ü•WDÝ6;j{Ü‘es×}×c™š è#û4û󕺿|÷Fj”´‡ zÂ袯EÜ%uæ@ ‚=þ2جäJí?ÔÇ4fD04m÷>²÷Ýl®ÔûÊ~*ûù¿ÜÏRzûÐ oOƒJÜ¢B´«a<™áÆPë½SÀÿÖ8núU=˜ûI½„ä ã†lYAuT¨€¦¹Ø“!fyí3m3üzÙ™¡}ô sÓ^®TþF†[Áxï*†ÖõO¯ ®ßðnt…¢'«?~q}ºî£+>úÀî°®ìHÐqyÞ`¶8¢KB´A°d¨T¾I§ûA«CÆõÜ5*6¡µ5;hã“zºGí×Ϙëw’@W¿‚ž¶O„w#¶STòëwÌfÚÈõAÇm !_¸žÇóÀ¤o@‹ wÂh‚Jí\ýS¶6@•¡U·mEm·irTÙOe?r?‹NÉáÞ­TÝ`õû:Sô®qW˜è;œ¡.“®’¡ã-¸?ni GïïàÚIC¾ErSîZ@ÏèË5iºÖóçfλ.3^ÃPÕìÞ´öÞn•Êj±lÊÐ{†ÃaoÆ¿|<üìà Pu·´_ËM Oªo–´.¿Žô)§\çUEbÂë] M»óŽvHÐåJ峸<"—ÙRôb<Ë(àz ʦµï¯s“GÇѧAÜ ¶ønC‰Š9ì›Ä•º¯Þ!‚N›úîé&ꪡÃýโ;Euw…Æ~•\©ý)û^ Û ëÚ§â-™qÒPe?•ýlÈý ›“|)zvå?#ŽàéE¶6ehöëPA+1…¢?.4gkS¦14¸µ!Û¿¢)wrÅIeÝÑ=Úå7b¨ó¤öѸ5 ¯òAïý!‚JåŸ2¦'ô³f¨ïÓæàôg ·¶2Z\Íý‘­ÅæŒõäöŸ7Ú6ó#蕤ˆuTÑVûS›£FÒÒöÕÞãJåË}:–jh0´þ¦¨ììÇÕlIµ4.ÉÐÀ…äþÛ=Z5ùµóf5C-ï6¢W»fRTê~±J-Ñë¨Ê‹µ4þQ_!‚F³Š&Þé®ÖÝTjä®Y Ö(нSÀ…c®\e?•ýlÈýl¼2 ¶ÜÈ¢èÒ°X¸gpžëÔ^Ôo“(¢Þˆ èjÇfìSyGŠFõ ¡RÍ ºzN:¬¶ñ`hðËŰpÓF®¥ü,|m^IQ¯ƒ£àÄK¡Rù'LºEb†ÏghÎÏ=Ä0f"îÚ³1-zsoŒfñÓïR´m»ý>8Ÿ[sl?˜kY14pÅIP9‘ SØGŠ*ηGkÞ^€*ÎëuÖA÷‚™Uœ{û”È7ýEQŹç|°X–è¿Ý—Èïà+LÐTa¨â|ÖÜKÔèüV‚*Î_Úë@ð¢ ý·7-Ž?E¸™Ï¥q ©ËÜÃPŹǓhX´£š Šs/û ¨ûõ”¢{ß9º}¶PÅùb,låèßîÿv"ž\*-§èß¾¯ì§²ŸÿÍýŒÕ¿ÿI„N`ØoCÇ×µ„¥ßƒ¸ÞÕ“!¥I7úÉ~hWõ¥hqVQ=æ=×ÁÇW4ë/¢?þïûªÊ¼24$`,+Y“NP­Ù„†@kÖûÀGÃP®TþË/fƒa4ÑÈä°æŒ ETÉWå>±]Oå¶³¹ïG 1ÛúÛ§ mR¼–{òúCýúef¦ýa  RùöiÇT=]ªÙ®«¶ázk6#‡ŽçQ40&Ýþ{Ï>\ý‘Ÿè|R(¢Î™‚qÍn‚JÝÏüÇü’^¡vÇÂðSëE´G÷ùpåÛŠ’‡·H~þ=®Ôþ|¿›DÕ>œ¡‰1å‡qñ\©÷•ýTö󹟎³~“žÞ›êÛá!i\Íõ)ÔŸÐC=Ö÷iý‚[»&@˜êBPñNyò½+ -T,ÙõT]îéŸhš¦#÷ìéL9Ý«n6&ET*?ÉÛµb(z(!b¨qpWè]GP©|§ôc>r=†êwØ£¢Ÿ X~L”3MµÜÓ‚Äl è÷ŒþºKä®0I…]–;Tê~ëÛÀjÖ{Ý3½LsìCQÓE¿ŠÑÜ;¬€Ž;I*µ\ìhª›ÁÐyé©‘‡;WÙOe?r?K&QòVsCIB2Ù.ÄsW[4&dƒ:÷þÕ"Q½cîÈ'`NÍ@“÷̯†~ÜO3ÖÈýSë‘SHþØ´æÎøº˜l|ž¢¾-™þ‹H•ÊŸ° î÷oÊÐÂ&¸ùĈ;j±뽬” Aç{3²² :-¬iø¢tuØ]&{pq*AGå ƒ9;Ujš{‹l·p¥òZFXeJQËFYh›u\·Ú´:ñ-AoÏz.Þ0trz!,jq¢·»îUùh†JÝ9Êrû΢hø^ HîÍmÒøý¥  ÚF©´~éZ®ÔþK?«©ï‚§µ.ØLk»‹¨²ŸÊ~6ä~†o\J–ØÅÐ)kû:ëîèë‰táÇ–Ü;ë)™ý‡¢îŽ  Óu< ð"šîïÆ5Nè Û&xÔð—ìº2ž¢ÇSmY‹Ú€žß3†îº2‡+•ߺu(,ì ][Ú'qwìmÏÚýt4ùmz²Û|®íïDòÎï)Aóf§Òv€¶ S£¾î1 íóí*5<¨Ë•ʧQiÎYýQÿŽzÌáí•"T¥]±ÃÜI€úU-d]¹ÍÙÈšëÅЩž£È»f\©û‰“Ì º2“¢åL`³nàWùžmÛ]ôî8Ù»~ Wjñ”YB\UA§žúFd¦Ï¹Ê~*ûÙûi¤rS˜üqC§îï"¼ŒÞÉý=·{8ò2E×;± sc¸FçNÁƒ^"A¯gdG³}ºx÷H8ʺ1tDcC Ñk¹ó×Õ“ÔM-íšé¥½S)*•ßÓu9æ‹zzÅlHºÄݯ9™^ÏY ¨NÀ1Y‡þK¹tÌRxÞQ;¯Ç^ÐñiÊ5m¦E“u ôôŸ68ù3A%¿þG4bi{«twëRê—:œ ï|‘ “Ñ€öp˜\=³“ë`¹F¬ ¬P­×‚uTê¾KC=íE+ºêÁ†Ÿ¹nv& ÷p´üöP˜T—BP©ý#‚ü¡«ÊK‚–/M‡‘ê;¸Ê~*ûÙûé|^~NcCÕßùoåN¨˜Äöé6§¨Ñ`oVHDúvëVÜÛ—¢rÃQ8þØ¿ Î&{íd¨ŠnkŽø—ÏßQ²¹<˜ë÷*NΕÊùöxp^·…¡ÛŸ‚C¶qÜYS‚È›q€~u}C⯙r8ÂÔø\‚ß>;*ssϦÅÍCÜúç!ÄÐŽ¢RùBLéØ?‘]ûª\<2ì0÷õšï$ùa" ‹ÍºÁÆ·;¹Ã½!Ú«b¸Ñš àjüf®Ôý°Œv¼K¡ÆG5!ߦ-7*ÍÊÔœ)Z²£Œt~Ö™¡RûµkNCU»2Ô[ë,4 ÝMQe?•ýlHýT|4Ò´¨Ii$Cç«#&±êKúo ,ªˆ¡ÙyÊUxFoN¿Ûn`¨â\?ôÕšô’¢Šs©üMLÌá×Ëx†*ÎoV@þÝ«Uœ‡h–¡ͯETq|( *'2ôoóe& Ñ;(AçƒÇ:AQ  ŠóÁ;à`l@ÿöþEÛ¦ðÄ«3Cçu‘Ñòäw ýÛý]Ú…øä¶ ýÛ÷•ýTö󿹟áf%ðŸôß4 &«¼&hÿÝþ :â÷jV7轉¡Gö|'gå¹q“CÅ}ŠÚ¿Mƒ?--ê«[D"–´Ò@ŽBP•˜‘öõ=(z=ü<-QGP©ü½ewf®¥ÜE‹äZüÑ@‹)pjïx†Þ>e/ö:sÛûÜ[}¢5ÍàE´C‡|xO·<¨J‡÷båå0®T¾‰g§‚u_@5[ç[s?1f1C<šÜ„½?”ÂÓv(Ü2¦¨ÛÄÄxð3®Ô}YÖ ÒÜå9Aå“ÊÉ”°jnÝVºòK,E/ê³G_ÊQ©ý~Ð)K(º÷Z(µ¼•" Rï+û©ìçÿr?ü7‚õ„j‚knr§KÜ?w³HÁ©ù ­›6‘˜ÓÜ«oc`ì€)Ü5Þð£ÛnÛ¶ÐÃ¥€fZAÖÙs]™bCT…z½Œ‘2ƒ¸RùSBÉ ­’ìÑâkä}š4îX%iÞ“¡àNÖ|¢èá w˜?C»§'’îfÜÀšË$pŽ Q§úCJËe•Ê·~u4jè?’!p@Wîn¿“4ضŒ µF–ôšÍ#î¸Çe4óâeŠ:Ø fÅwòETêþ­È­s]<ì ¹©E¹ËT›gÀÝý°;»ÓGP©ýŸœoî&èw¦p(¤’«ì§²Ÿ ¹Ÿîí·CÞp‘ ‹–&ÃÂm‡¸7ª„Cï»24p®(+˜ø„¢yÖ:д©Cy§‘,¹OrÕÁêp].Dúx7à­ *‚K ZÛN Jz>¤¨T~•f~òÇ_ƒ ZüÖN¶'¢„û¨Ï`!mØJŠõw­.—¡Çín¾‰ahÚ€&´ªC8w{š6ܛڈ;ogp÷Ó®T>ƒ¾™`u¢  ëòr@õp=AÍ-+ež÷k¸is‰É”»Ü_“³¨ðýÜyÝ+iîÂŽ€JÝÿv²)¬È"hÜ·ÖpâÝnn÷gŒ>¨ 裄N²=Ä‘+µ_wÚBhy+‡ ¡ûÀ0Ј«ì§²Ÿ ¹ŸuÒ`¥×‚Þ{¢®á¦dÉ sü)ú(á¥\Óuµ T¿.Œò«¢è£D¹üYo]Û2B?i3CƒúFSKÿµÜœ>]ÅèÏ3¸ƒ{j³ïûús¥ò×F: Nã4 S™pɦ ¦Uùâ;Ó$‚>^-nøÞÐE»âhÙóÙ ¯+RËÍæÜmãoÒŸ–>Ü1mÙ4÷ ŠJå«ÛY÷Ž^&¨AŠ<4÷pkÊî’ˆJ‘;ËÓÄÑ1ÜäÆķqk@ÕšCœ‘3A¥î<Ó…Öñ‘}´¤”=]Ê q‘ ¶Õ´Õ‡>°üJ‚Jí×/€–Ó_ˆ¨iËSðòYE•ýTö³!÷Óôm6ôÕNP—&y°«q;®Ê‘¶öAÝFsWêF aýª¸ÞFâ”Û©\ïÄ)³ߦ€¦}yL'/ÄÐû;ÞÐl÷g¦Í†uUç–uì$ØôÌ ¨Tþ@_C¢úÙЪç3I¹ÚHî£ÌDzÌ_£¸²{’G×r¿.* ¿·\¤¨ÁŸ´`ð3½yl"Õü¤CÐ zàzÓ \©|‰à >– 4³Îî¼, sÏSÝ:rŸ?X¦†ÈÐs«`^‡"ŠÖmœm}*u¿j¯9xè»4Õ§Ô¿ëÁ½mç !5»)jrK€Ú—„¡Rû÷Ø¢!C—X ÎZ2®²ŸÊ~6ä~¦ö(Ó \=î.Bø‹Ÿ2´Ø¥7ñžÙÐí{wUà†,YB~šÏœÿ‰ü¹¸™{mæªU ¢\»‰¥7ÆTÞ°6=‚;Kçì©ZCQ©ü¶'ü6Þ€p:CÔ.pƒÏ!Ö£¸ÔÔàaN×mó|±¬L‡ #'jõ¹Ü‡ž;!ÎZÆ K;o®î¦¨T¾ƒ{ò@Eó[!š’ž.†—DtÞŽP˜{­E©] l£)ÜÍõj0½zCçÇþ~ áJÝgyؾù- ⇂oâa®ë?M@ïLCÞŒ%#m¶s%ÿüK:CPë> ms*ŒÌT¯£¨²ŸÊ~6¤~*>ÖkhÝx&Gç…EOH|+O@ç)§ìàÍòH@çgÛ#«Ê ª8?ñ*´ÂPŹTþ­½ß“š/ëUœË[ØÀÓh@ç¡TÁRå3Aç¹Kv‚Æ þ ýÛ|iïöÑ4GŠ*ί~ÝßÿùJÑÛ¯^'¶¡Íп½ïý{$Ô:š è¿ýþ¬‰^*¡ ýÛýÍ>¹$¨SôoßWöSÙÏÿæ~–÷*‚ÿä©ÈxÿAé;håSÉu|>ÊÏWPtØÇ.6ý ×ÍOE4[$ ÞÆEýÎôæñPÈ¿ÖÐçÓ Ûç· ËÉ&ŸRôaý1>郈Jå_ß8šŒšÍÐï-¦‘³öQÜ×ÿè°ú€é"j¡÷ŽqÈгi-à—Œ´ä…LõF@Ãò 3¡Ù›ÜŹÓÔ¹RùÒ½&& 24æ9#³Wxqcµ‰¾qý jYùQè};Ž;õôQpÉ.Ѭ¹ëaúä#•ºÿ,`;±ê;¡ÿTl"ÛM]¹Áwt!dÓf@Ÿ4}¬åJí¿dr–ÊŽè·3jìæ<‚J½¯ì§²ŸÿËýì4J—î§4í‡ j»¬ã^½ð€8g—QtÑÃ5äãùãÜGL…2Ã>€¦NéKž|œÃùå$ŒïOÐW‹ò eëCq±›³3‡ Ö·_Ò‰#l•Êÿ®® 1 `høÁ‹Â+×0®QåVz¤xAŸ†‹/ ¸«Ÿ ‡1=)j|Ë"»=ãî?iÿ«ô·€NëžK¾¤Z*•o‰''`hì/rß¿/÷ĵXb´¬˜ ng^p»ê¼&5E¥5/:$þþèÍ•º_ÑtIÏÆÐ!ý½È½§À5OXŽ"´Ñè¥Ðüp%A¥öOZ¦ÁÒv”,¿OßmÁUöSÙφÜψ@|[Ô‹ óîÛA¿ãú©}°`;9–¢Û«nˆ%žC¹Ñ•ž›ÕôoKÝ"¹v@#ý—ˆÆk†A¡Ú6Šši3ሷ9 NÝ»ÃåVn•Ê¿«)2T¥÷þÂ*×%Ü£slHþÜ\‚Þÿx—ÌsŠû¸t8<Ù—¡²Y}¡ù4?îáKî 쟨é¸í ?\É~,Ó#uj¸ Ng>RÔõµ!´ ýFPã°‘PZªhÒªô[ÈkMí©ËN¹ÖTê¾™½3y–=€¡UݬIŠQîêt7¨züT†n«Ñ„„½{)*µƒªÍzí ¨Í ]¡±×®²ŸÊ~6ä~ºµµoÓûöh–‘ ¬¿µCD/l¤#gfqÛdŸ¦)?µô[\?Pï¨Ï½ F¼¸£/öÓkŒ¢Ç¦d‘Q‚œëœî y=¿p'÷³ƒÈÑ •ÊŸ9ÆJÔd> -0Þ'î¹ãÊ}“Öôµ3 *å­t‚¸•¯Û€Å°0†úø_#©îÑÜpÙ¾d/A·Ïw£…Æ•Ê`'s¢ST¥¯lÉÎ)ÜòÜe°¾úA·†FA™Ú;®Ã·§Tåã`@s戃\ܸR÷Uÿè“îBw†VT«“BÕÎ\ÿÝî¤k£V\w0¢Rzr¥ö?6I!'¦÷ô®_K˜ev Ê~*ûÙû9K»ø^èDѬ÷¦`j9;¹ëG*dš´í ö$);wßlÈ>g¨›‹/ŒÞv‚ ÙáEòUN›(ºæmõû“*¢%wÒÈà)›ª’¸ ¨]®ä×WHµXyߎ¡¹gQ-cn‘, &?Ó&èý½)à ÒO†ÞöÛ@â·na¨jp…¶~-÷õÅÞäé˜{õS}@I»³\©|*«,䯾%ЍCÚyUÏP}²{7$÷ºFÐîq‡®gq#¦%â:Zó£7´öÐ!¨Ô}­Í¯…ÊuíZµõ¼Ðèhsnxý{:v”!×ü|O6)ïE¥ö§ €qÖúè¶38·>HQe?•ýlÈýÔf]AtO¦èÔ]p¾qšë~º-›:ïAýŠÛ²K“[ªze )qPÓÚ.àr~EY—æìðµ.½fÜ…míVÉUÙòÚÞ?Tƒ«|š$¦9*•ÿÓŽ–ô~Q †Ž›¤E-<ŸQÔÅþ(¼pnÉuŸ¬ºlç>ÊÛ,OSue¨¥_–HŽ´å&s`Õé©"šÔÙ„¥EÆT*_qåByíw‚ªL±‡ïYܶoN€ÍÛ î¡q§`‹W7n\×±°ÿv)E5™µK&3Tê~ìó‚ÅЦ¤„ -ƒ^qûÿ´f®-ærŒ´˜åû[2Tjÿ¼¯î˜3ô^8ÞlWÙOe?R?Ÿ€…ZôâŠ*Î?îÐ`´ÔPÅù¯ÈýGuUœÙ ÍŸé ¨â<òš7Œ¼PŹT~+Í©ñªcUœË4³¡½Ý=Š*妕ˆ¶~ Uœ¯[cLW{¾#èßæ Hm!¬þó Šóð…P:v¾€*Îû¼ûJìÃ#ú·÷µöŽzÝ ¨â|¾n$}Ö{1AÿvEx5IÛ¶…¡û¾²ŸÊ~þ7÷ópï\øOÎþù: ¿@Ñ$ÿ´Y·l.Ù ±†Ã¼wžÂýö4\¬¾6P¡õ½UÜ]#2„Š·AÜ YÙgÂMî—áIçDtê‰ÐùÖ3ŠJå_šs](ï>Ь¨¶$jùxî«·dä–Ö õ2;H˜ßî®ÃNd¦Ç4î‹6_d²ô…Üâ}‹¨~}A?^Ž£^í•ÊçÒÔéÖ…t~¬3ü“ɵ<ªÃ.¤¿ãÆÞjÆ^¿Q4È" V¿èËýÝt\ÿMP©ûþ—ÓàÛ–a}`› 'WMâ^o7‰ÞtŸÉP“Ô}ôäy®Ô~Ãù¦EË÷ø3ôûžAô;9\©÷•ýTöó¹ŸÚ«ŸS¯Õñ=`r—ÖZÉ}óÿþŸç›³šÑi4¬+™ÇÍ3éO†~ôÆ£ƒ¤~S0WMõ)žº€;AWÚœÀõ+öÚ µÛEÂMK(*•e!qG|ÍæO~¤,âî:$þÇ8†jœ°Å[ÿI¬H à>yµŒ¾;îÍmk¾Stˆ_h«3Q¤Pg*W*Ÿmï®l@}9A-ÞubÆÝ^qƒ£ŸP³Ym-\s˜.î­Ëý%‡ã÷ìš5Ôòz5§¨ÔýÀÅ{ayå\ŠF®K†Wë×p¿=ßM+ÆUs—:ØQçÛ"*µÿÅ}4?h8Cgì̧öñí¸Ê~*ûÙûyqmµ{EwGQ—!ZÜ|=ÈŽœÃÐßÃu ¹7׿âg25d ô ?Íž»§Mo8èt— >Gmàær¹€ê±©Ôwð9îpϪÕÅP©ü\b‰‰õZ@'"›Œ‚¸ê}DÑ2u>C·|œ@ÏèÏ⮳ÛO-Ž âþèC/.éÀÍ+è#kª ZÐ{| éOQ©|Ë¿´e;î5´÷*56s+î¶ôó-=î õIâ/“ŽÜ Z•Äüý ŠîYX!ª&Tq¥î»MI€VÅ¡µhòQ\­­n‚~œ+Aß~Í#!5\Éߟa2Ý–½—¢Ö]¨÷orTÙOe?r?ódRËõ"ÚÛrõØÔûiÓWb˜ìÊнNŒ̱å˜Ù:,ýJÐÒgý!Zpâºùö„eO)ª–¢ ý{h1ôÆÓþôÙU?@/\D–ŽÁ•ÊŸ]{–\~èªcÉ’±a\7DúSgC¯çÓ/µ¸OZ¦ÒÆaç(ZÛhݑ݋K†tzÛž¡±Ç3Ó áJåK xKÏ^ìhËfet”÷ÙÍBIÓVÜÉý§©=ÿ4|p#ÖdóNŠvoiÃÜ\¶ˆ¨ÔýÄíq°àÀvŠvù C“¸Ç,á­«. ×ËÖƒ¨eÄ•Ú?y¢pûÍ<‚ú¾- põ!WÙOe?r?éƒ4¥½Ž -J=æÍ囉^×N µüeMn˜} hÿC}`ßÇG"šh«Ï·VPÔXïÑY6š¡nºNdײ¥\ŸÉaª×‚fOv€«•%•Ê?Îü‰_h# hAB¹þ­NÓáíz¨ÏI:Ýû*Ewåç‰)êÃT5úŒ°+hAŸ¼•‰'—3Ô þˆKFkq¥òµ[°›êéúìþTZuî_Îî[@’Û½Ö\~É2¹M;3ý:ºî0Í¥r®Ôýñf[ ñý>о°ß-Þgp7:&Ãõ­]|<}ÎTjLŠDUiZ{b ŒÚ®ÇUöSÙφÜϼã­išýuUÕ=*þ.îHÐÌ*Œ‰ÜOQ‡Þe&~]¸‘IHÒèî µ‡\ŽŸÀ}ºÓ~ŘõÜ‚8=z2wwC^Gh©þö“»êÂ8²¤W EÝÒ¶Š© .܈¶«àøC½gÊSž\©üA»—Ò1MÚTk¡}¶v ×Ñj>ü¾Ü—¯»BÊåj¸·ƒØýF)A½o;„êÚ*@—šÓi -ûQ#‹ö\©|{êõ!µûGŠ ›@÷AO¹)áæPpPÐGêV0ü{AO«kÃÜD@Õ¦CõS®Ô}úMbýý9€ÆL³ÿ™ÇU™±A¬HÛG¹é U½wÉP©ýe‹cIÕ‘×Ü¿9¨9Äq¥ÞWöSÙÏÿå~zŸ·…1Ÿgºù€Í÷åj5Ç;´`hʬqô‹+(k’ºÚ_•@s7[îå»ií¬i":úÈgÚÁðºÀ½A¾›3ÔoçvÁÓh"E¥ò;ŒÏÕ‚§TÅ»µ<’­çÎ7Ù*Ÿ÷Ø¡ýSRáÅQD½ûÊeY­†ª¹/O˜>‹«¾î¹ø¾¨EU™“Ëz‡*•ïÍÁ+DóôeŠîÖßMêp}ŸYë&3 ú¡™5õMÐy›aÈv+@ÝÇ@Ý?ß *u¥‰h›¼P·Þwä7Tr‹;¾2åTíK$9¿ÕP©ýûƒ_Ëê²d¬üÞ™¢Ê~*ûÙû9G×òÖøúZÇ gpkŠbd8E³'iŠ4?ˆ¨CØlx\à¨ë±`8ºÍŽë´£-ó× !¨¯š6+Ì-㮬Jç9årn~—fT*ÿH•D¡Íâh‚fl4!«o$q'f”YE g‚Ê–®¦`EâׯtÛÄÍdr‡@nÀu0›`Á]»f¨påJåsZ0†üx±‡¢*Ûª…e½ƒ¸¥K,à±Á.=bo ‡Í (4[Ó54kÊ ß›STê¾Vâ0ù«¶€Ë{Ù¿¹ò/sû½$ßÇrk"ºBøãù\©ý™³6Ï/FÑØÈHhŸªÅPe?•ýlÈý\‘8 ZOTeÇðy6í_ÞV·/^£. ÷FÝÛtîÜ(xqª; o5b!]£÷WÞoºøö‚NgG©VW @‡ÔÕ–qOù“1gq¥ò/¸ºdÎË ¨ZÂQÒ¡SWž¼Jr²)šv7Ô´.s½ºË‰f‹€Þô–|ß鎺# ,¹fAPÕHNP©|†gÉë;Qt\þ^qä«&ÜȰ.àZÈ]¥ oÔþp6æ{…[’¥'ú´c¨ÔýGsãd+…E€ÖÎm.8Žÿ—Ù¢Øfùq5×yÂå™Î\©ým>ló¶6 µË] 6N\e?•ýlÈýl¡2 ÎO´øÚL¨•ãjM¿'L±Ì ègk¢áö†›f±æʹ¦ëÁé&áFÒÏâò­ê€¾èÑŠ¬Mª%è¤Cس#œ›öî.=DQ©üÙþwÉ”Ëm¢ °Šr÷ïÛe5O)êÜu ¼®yË Ñ„ç=í522€ùw[pÏΠť‚ˆÞ,’ÛýoQTòç·%&T%}µˆjî\B½{µ¡C›AãË mÛ¢Ž\^dÇݶ6—~܉kµK­ýCQ©ûŽ#„.º‹õ¹IøÀõg¾ ¤Å5;?¼Óc *µl ˆ{†0tvÿްµÆ‚«ì§²Ÿ ¹Ÿµês!¤b4 ÝÀÚ_Ã¸Ž‹Ö‡„öÿòr:±2³âÎßí* ½ŠÐsûL!µ†P´×Ë«$ÿe.A_M2µ0'îïíÿ÷ÙÀÔ¡Wý:ó@®T~±Q'xÓª„ íT¬ nË®~3„~CÑC3õ`}ÀÕèc£oeôKY(=P, ÑnDc†a¥Ôá^w®T>ïŒdâ‘* æ éyQ— 7ý®Y¥;C¯öÊ!Þ¾\ý«MXÔï8ŠFJ¥µ.ËQ©ûçÞ¥-tù± A?×}©-˜f>£æ2 ¸]“NQ©ýŸB(ñÎUc¨ÚÉnä‹Ë^Š*û©ìgCê§â“´>Vx8ª80¢Š´ìë ¨âüsýB÷§¨â<ðÓLPñ¸e*ÎGu §Æ¹1Tq.•?ÏÕ <>”Tqþ]v|;rŽ¢Šóƒ¡°f{Š*Îvý¡ñ±¯)ú·ùæè^§£=§TqþbH[ÄPŹVÅWÙ£)¾ýÛûáεÂÕÇóUœ'D w«Ú1ôo÷§t,“gú-Ñ¿}_ÙOe?ÿ›ûi7+þ“¯Vw†ñ.[)ú+D†<Â]L^e¤ô‹×ò¾ö(÷œ™qÉùÅÍþUFÒ&0®,|'¸•]Ñ˪Ápn­-EóbßS‡¹úFHÃϬ"¨Tþ€6)r˜0ÐÀlí¢Fm½¹+ÿ&%sw‘_{¨žãÆ]zÛÆZYPôÕfxp»ûÓJt<äJЪ(&–ÇuT*_Mq(t[Ö„¡Ub”îjÍé¥Eí„RtÄÜ ôE¤?·f¤'dt‹ h}è6x3Ýž+uý‡[¢z†9C[EþCúZpkׂJ¿¾€z™/‚¥Ûl¹Rû ¢õØ•õér´2¬%» ¨T~ó–AÂÔÄÙ€ÊKò…âÛ³¸é³œà͸I\Ÿ ®rb$7ÕQ€â~£zä«58ßöâªEí'­o›:Plm?ì&¨T¾=5ÁPXÙ“¡šn„-¸§¦ÿ¡ûú4§è³N-ÙmÝRWµ òž÷¦èUGü¨çJÝ7ØÜ—&g[1ôûíÁôÉ2kî…Ž+àæý‚öðñ€5M®Ô~•;í¾?Š"葹›‰ï´4®²ŸÊ~6ä~Ö%4ìÙíú¸ì ‹7äN¼ŽÇ3ZQ)»¹ÑGc`â“,Šþºòà÷…޵—Ã}3ûmö(• ßW€Ç¹ˆŽ\«úÙñ•Ê¿xá!w¹/ *>‰|•7·UÔ8Ȳåj‡ú·¹_=!sÌl†nûÜ 'ÏàÞ>í9YM(ª9ÐÚÿhÊP©|ÖóVCÓÖ •ÿ €.Ãúrçêt`5cüE´`• _œ(GB³H÷¹)Z¿ÛUtyyœ+ußiýXê¨Þ—¡_–úÐù³ÿ¥Ú×Þ0«ªZDÉÃodúælŠJí»\²ó³ j|¼+Ïá*û©ìgCî焟È&«^ M {E6[ôãZËvÁIy,A«÷í(óÜÔ°86eè‚ÅP¾B‹kÞBMßLÑ…KÚ3ï¯#¸±&¥Eó¿Ws]/P×iϸRùßå C^MôìÆIdëµ±Üå+Æ@鉭OŸ²¹Ï®#‡lÇ14Üê;®{'OX}ɉÛå¸|}2–+•¯ðÚLhµÓš¡ãZýßgŒF–ÜÆfzlMÍAúȬ=[Þ|¹€®‹–SÕ{©mÙ¬[W¿„+ùùçTÕÛÖ—¡NÃÖSÝ-ÿ2±f+ù1­%·§ß9Áüƒ Wjÿ‹Âp²CAÇĹËUöSÙφÜOUúˆ| & }þì&¹c:Œ«±/N ñ$è;“4P=Õ›ùt%œ¹Ö–¡Ç“Æ@ÔM®þ…6ì}ûW"ZþÏjxr§UלÁŒCÝøä]¿é±ˆJåß¿–<PG¯XBF âjÂ~ùÝLû씡ÇþÄ“£ujóÔ™œqŸ¢ú®ßI#×^ íP=ŒÐc9•Ê·æ¢ ÜÛÚ‹¡»ú؃l’ wàŽÌ÷ÃÝoõ“Úè´ hde;–YX'¢-ÊïÓ€Èë2Têþë¨P:ȵ/Co%l¡kZsM÷¯#·¹pZÓèý#¸Rû‰j*<üêEе÷CódM®²ŸÊ~6¤~*>!/‘jmW†*Î ÍöCõðæUœn …ú¿(ª8•I7d ¨â<[¦&ª.ìAPŹT~õÇ©då¯~€*ÎÍLŒá[”.Eç/Æ^"ã7PTq;5Aç5Q@ÿ6_¿fÐ|Ag†*ÎӿܤeSûTqî9A—îùfMп½ÿrèVzÆÞš¡ŠsãøÔ&qCÿvÇËIðé’•€þíûÊ~*ûùßÜÏ…{á?Y÷¶/Uùž,GµÃµ¨÷¼^2tiæ?TóµHк/håÍJnÐòKB1Tq¿ÚA²OçjnØBld¾€Îz{n˜À]´ ŽÄ¿&è§ø­p쀜+•¿þº;}\D¯¬~º]r~<8üt`¨ï<8:ì_^ˆ:Lº«èp Õ‰i¾ ·2é¡ð~O4E F‡ÓµUùRù²žjÁñ¾±UûL¬]wqï ;‚ÀÐM[XÚb×êrZ¥1ˆëqq½inÆ•ºOš¹A“Ì8ŠÎ2»îáz|­'½ý“¸Ãcöp¥ö·Üo«» PeöQî\©÷•ýTöó¹Ÿê—n‰áÝô›G¤øF3Žû¸ö wä.AÇv¾I3W=ãNp¯'Þ9[¹Í&X‚c/OnÍ” Òkß@@ݽëÈÑß­¹V«àŸJ‚º¸ô„¨v•Ê_Y5 †–M¤¨yñ(ˆnÁ]øÛ²O dèí +îÍhfCÞºp]ॠ¢1‘k¥Â.=QÿX VæºYŽJå³~RF’ R(ZÐ;™°è îͤžpèwo†ZÓ…Kã¸?ÌÚRÿø×µ\æ*¾Q7çJÝwY0|ØAŠþ œõŸr¹*ÇÚ‘Ž_»1Ôt™ìR•ڟŸÙòZN`¨W];(¹àÂUöSÙφÜÏ€w?åµû¯h±¶ŠìÓKu‚ÊóOÓ‹Þ/¹ºÒéÐøWÜáž9T—»áW¤Æd ¨Ç¼–QqŠ ¾¯»Bö 6ÜôU d¢ú ЦDm-êaÑ›¡RùíÈXÐQÍ h„à=w•pW7i ?ë3ôÅÙwDè¨Îýâ*ÌÚ7™«2µVv=t÷ißbšÝÿ”€†7wëä *•ïÎ5/Ò8"‹þì×iT÷?ð(I* IšÈ’DIkBÆÈLB’d !„$ É”)•)IBȪs}N’y !$ IBBøü÷þ®u~kÝ×òì¾[çzòz°»ö~ãÍqÐȦ ªú 9¼O}vt\Sê¹ï ܯA–Þ!d·ë¨I·0FøNJÝßâÁBÞ+8:rª'sJºNzZ—\é¯@;jÀ·Žb¤Ôþ°&U`Ø©«Ýÿ* ŸjHªú©êgmîgQª‹ð°{@Cå'!f é峊×ñ*%ótçþZ/ÉÁ­·²¨Æwd¨Û…]Ìûq…ÝçcϦ?*¢N.ýØšA1mïÀS«è€D7¾fTR*ÿïÔ ÌzG¿®÷f««Èá:çàɯçdÕ§ ¸Üå(úã’³Ý-Wªv"'óK=kò¡â<¬¿uÐ ]˜§þÓ•ÈÇ«G ò¬ ŽV§Ƚ®(È„Yôg¼&—Gm­0ù¾ôlþ)cèá­à®îMJÝï×}‹3yÀÑýe™‰ûs²uµ/]ÛF]}/žTÉQ©ýùc&C›€LŽ&‡öûX“ª~ªúY›ûy¤²4ø>Ð{® Ût5éxBœðñ ™\³@öd~.©-ÛÇJ\÷‹è·á»YÖœò彬Ýáo]8ŽmëÛJžçâ»ØùUK;+h¨Tþ9ÙSXw^O ôc35õIÍSu`¬Ç4ŽÆŸê#{åû^D]ÚhÉÛÙ5T j¿Ûg ºÂÑý¯W±õ7?Ú¯ÿVóã)•ï¼xG”U\ähÃ]ø³+¤uøP“½“´­ûQØýp"Ù®¸.«ûË‹¡ÎaÌ~ORêþÖµ“ØXÇ·¬=™ÕäW’¶6‹ÄQÁ+ÉÐIKdjú9¨ÔþÜ¥“Åa-ë ¨|SW~ÜÏPU?Uý¬ÍýôN­vêÕ8Œ$’1é?…Ï_.’3;̇“OÓÈžm·±é'Ûs´yÏp¦Y=„ÜœáÁô[;)ÐâƒCÙ·Ö}É NQ°»¬CO.†á²q¤Tþ•ͦ³àþ-¨Ù V¸°ùêÉeÑ¥S9šÕw _4*[@C£Md²9jáùXvÓZ]DûŒ^Çš¾ZèëïãXr¼–€Jå[º#€Ïµ¿ÆÑåOvðµF×Éê 7$+#|¦‰hý…fLžShéÞöL=ç“€JÝךïÃr;×pTOaããê*Ð鯷;ù*@Ä»Àx“?ÿ*±¿¸ïZÞ²0Ðì”Þ8´CUýTõ³6õSù霰 übΪ<ûvz†'ª<×Ëófò¬yUž{ݘ…yoª<ܢˊ¦g¨ò\*ÿyϬY„•Už÷ÎŒâÚ:½UžWÔí%tºh¨ò|ømæ-„£›oç—|iÊ5Ž*Ϲ©:êsL†*Ïîtfƒ rôoï½÷e¾h*PåyQ“õPnÇпݿ¨ã!~a‡!Cÿö}U?Uýüoîç"çXöŸT[ü<óî)CCë´”;­d¤¯nÞM¯¥Mi=[囑©NN,~þ2:&c ‹ º%¢¾é|¹[9G‡õ8Ì}Òž’CNue#†uQ ëÛ³’)®¤TþŠÐtÙ›Ïê²çº,·ô_‡½,ËóyÀÑj8,úõ/#­XlÏÈÐ.âÐÓu$Ý ~ðéW[ËÑ•s*xÛâŒJå+OŸ'—®ìÎWì_E–™¶b¶ÞèDõG ?îG¥òkMz' ¹ÞŸ¡Çô„Éûû’õê´å3îüâ¨ßN>·k}jµf?¤¹[04ÙŒƒ¯¦™êµƒkDP—£5™g޶T*_Ó§…"0b¨cÐ1!mGríýPæ³rG[?`O!i·e­-:*µ©“¨Ôý¢ÞM„ýʼn"šel* h1ƒôw¦ Iá Y37‘RûV”Cë¹c?û#DŸNªú©êgmî§wèydˆ+C]ڹ˵5û’7ßMâósZ*PãIÃx¾[3RÿÉzv1u/Gm*63›à#¤‹Ö.QnôUDCcoËòõ¬T«ë !}úŽF&Žç³ïËQ©ü ¿m…:A½:¿÷D¡Äȧ®kx޽®½º` ÿè OŽ (†6ǯš•«É²+"È/ù» aV29¡McvÝA“¡Rù¢j . º04«f-:’sîMd&£Çs´ã‡,5} yÊÃ’¥eÔÔ»Ò† í¬8*y_¿P¤¡%¢ñ6B¿Ær4÷“†ØnÐJŽŽœ:™ÿò &¥öS íçCƒü€·ÌIU?Uý¬Íý Xh/ïôÇ•¡.6 åKýÿuŸ¿^ÕPn˜bÎ=Öÿáèü›™vA ™`ÉJw'“y„è#“]cí Íw]'ÔÉsSƒ´¥ñO~ce )•ÁŸu‚ŸGO†ö¼sDXÚ¼7tŒj¤@G§mà•´IM=söÝÛÐ껎l:$C?lqb{ mú#¡?{­ïLJåÛî-4öva¨‹Q”àÖ<»ÅŽ5à¨FÇ–lјPÒå£ {ÑêYÀG°ùŸ¨äŸo{¡L1K†¦†9 !±&záËyž²q%G£/ó§W“Rû½Ê5ÿV¨ofc™µ™TõSÕÏÚÜO—»3[ïÃPµ¼.=/¥ö"]Uãy'_pT¦Ÿ%§s2äý 6âc<é_êà ëÇ’,Ë1´¦l3xZÛ‘_îæETmW”<©» P©ü¦=î Ù¯¬jîþK(ÙÙ–lš²œw ª£@ÃO·ÃÑ{âf·t¯ˆV œÅìæYq´þÆæmÍPß[6îúu@¥ò k¾A¨\4ˆ¡‰C× á…ƒÉ;VêLýB$Gª?„°n;É­WdzW/¬hÝ&£ØÐWíI©û¹¹.B‚" E¶}¿¢äžnï¸iÙ*ŽÊ§Ýç.;g‘’ß¿†Z²…ÆZ?Ü‘½‘)¢ª~ªúY›ûé]•ë|v3chVÅÙñJ9Ìf©x¨ÍNŽº˜¿—{vKÞ;ôÏw¬mäԞ͘zÿ½<: ŒÝj~ltz>–4v>Wç§z«s{ßH“¡’ŸŸ›Ãíc¦ <¨Ø;7#BÛñÏlŽ&)Žë¼ƒ¼Ê´&Î!;©­cõ;F‘‘fíÙé‘‹ôLdæ¿mG¥òHÙ$$‡aè&¬yƒÉsîI°téAŽÎjzÒÈ7ElãÅF ôÏJ fæø”£R÷Õ ά*ЬönBðr-@CûÄsãÎ ¶íÆ£X”ˆJ~ÿ4”ùõYÉÑàr_&¸Hªú©êgmê§òõ%CV^Ø“¡ÿçzg9ùLû,’JÏvã{°òq GÿÏÜb?dOËP幸h{ïÓ¡Ês©ü_ï ‡%£uª<×_ë/æNêÅQåyÆ¡M¬íž}Už;̱gÁKš(пÍW¿Sª<϶3 ‡ËUžwÞµ¢†Gsôoïçf d»-Už[Dl’óÏ+ôo÷§x/bqaŸ8ú·ï«ú©êçs?ƒ>meÿI³ œ÷Ž£&å²’FU¤u§OBßV~dÄͳ‚ÞˆFd¹Ûžýþ$ép,–?ÓÎ&W,ÚÈ/Ý{W®9ûÿá;KO@ëž‹çç7èRù—žz kÔôh‡02±¹sx‹ÑÝÆÑ*‡UlIq$Yb6>~9H)k[žy’×_6fÛ’GwÁ¨›Ñ¤T¾»ïLÙÌmÉ€Î9bÊ:­[G.͘ 1«Õè•Á®Ðq¡™?U›ÏÑòË¿`C¨¶•ºßÕY=ž_ÊÑÌ)ßAŠI÷y˜»çYÒÀÜA»ý¤ÔþWZaìžûE@·T†³#ž·I©÷UýTõó¹Ÿ.ý;iD×p4Å;N>ki]êrÀGp8|¢¸}…ö¿n¾+wîrC@ÕZD‰m6[:Ýmx›£¦a|ÊW9i˜¾FÛnôî–§°·Q¹W÷;´x¬ÁÐÃ?Ø'‡î¤Tþ1ž¹Pàa§@}[æÂ«JGÒ3p0;³|>G{[Ù1;õ@²â˜´š" 3+çñH(ù×c7xäÒ™}è|ƒŸq[JJå<¢3‰ß+ :m˜õüÑ2Tçp¹PÝ­« ¿ø@Ы'Y1¾«“ÍÈq+¾ÀÙפÔ}×’bxr‡£mÕžBôºkdƒõ!µ ‘´r0ONJí÷pñaË?Çšux‹KëKªú©êgmîç©Øâ·¾z 4«Z~j›ýéiôIŸ¹ù¤’±ëÚ³ŠßQ©üõŽÜƒ¯Fý¨…mܺ5Ôìh…Ær´ÃÆvŒÔ2}Äu®;šÛ¦YaG&jiÁ‡îdTùzxå_AJåónËš®´åèñH{vîîL’·ÎF~é­@õóŠ…w]H5ƒÈ̬u·D4_˃g=yåŒJݯ±Ë†ƒ3Ïq”íLו'I÷QüÛ ušTÄ]VÕ!¥öŒoÄv]‹äèˆúš¬©C!©ê§ªŸµ¹ŸšÕ¯ÅR³– Ô#䳨~¿)yõÕ&î¿á  ¯´öpo›\ÒÛ¡¤EWrÑ7Èkx€œµo»6Qƒ¡Á÷ç±íõ4ɪÎCYK“Í8È{='¥ò—ß}ƒ»)Ð~³v“Áä¬ÍÍáǘŽ:7/D=#ÍÓò`c+@s³xhž _yR¯Ù2ôiN2\*w#¥ò-·vfWîDqô˜Q/ÞõÙ½Ñ9+Ð| mÝÜÚƒó¢ z`Ékî»Í‡”º~?ìRŽr´MÙ!ˆüÍe¼ì ™<÷±˜£sŠ”Ú_¶».+<ßDFìØÖ­»HG{öÝö¹áØ31»:S@oÖ¼Î6é¨T¾ðM÷‡¤½Z6GPЈÔüþ œž&³6<]“{¤Kÿéò¢òg€&GÕ‡£¦‡I©û¢ŽÃÎÅCÕ…ì2òúŒSâ‘ܵM[Ù‡7xžHJí¯ð GŠ“zä+7IJ½¯ê§ªŸÿËý|XìNcë*P£Á®íWÍчžCvv™ñù,í“@&> xcÉPÝÝbÞ«–äÔ´uüúÖ[õ2ŒëÝ¿I>ÌÚoßI®;¶–ÉBçRùœË4.×e耣#˜Y:d¨^[Ù¢Ý78z>ÞR´Xóš\:`(óÜù”láÅ쟿%×FC›šÓ€sX©ÇP©|.Ceç³+E4yh±tH.Ù{Ô=h\ÁÑë˜}QSwfl49D›GJÝÏ9 Î õmq2ž÷$§Õs«‘W9ÚNw%orä )µ_mÓˆŒC»ë3´ *\x¼ ©ê§ªŸµ¹Ÿ<úAÁÞw­ªî½§’ýFí‡Wú[ɹnË¡{ÚjrQ“DñÄ =†¦š*ÞíöP“Ì?âØ‘é^é,–œÜJºÌY´tôH¨êÅ:^ø,¢Rùmga[7ÿôGãlkùWr@Ûh1VSMöN<'š…é‰_g² ¬œ£ý¬Y¤Æ+rNÄI¨I¶g蹞ëà«#%?ß“ÄÝSΉè†M¿Å¤_‡HÝ\Öµ©5˜”‚¥=Ýžyé\´qÅ`Vcö›”ºŸr'.ù:2tYbܼì@úþñà­oUst„­>ïeQEJíª÷]ŒÔŒ¡1,øŽH]RÕOU?ks?›õƒéy¹ݪè+¿d“§ÎÙÃbãediáSaãºdÖÆ¶™þwZ¬µ_°=GºØì“uzäÀÑŠÛ‹„É \EÔ$¿«™8“£!ÇŽ@¬[)•ßûTo–´ö …~3žñŠôêœø©Msê”'.ùfFΟÄVϽÏÑ¥G±GËDrf¼'tmÅо~+àºë7@¥ò…ÌéÄKŸíÑ3z£ù‡Í‰d£OáºKG:Þí5¹Ø‘u7»³ ªõ XY¤)u?>.â4íz÷Ì^ÐkÛÌ9GÜað£Y}Ë_ì#¥ö¿lÍÝú(è}ÏîzLªú©êgmî§Qƒ¶…g8z{¬8îO"]ÊÍ…K‡IÛ wåk´BÈEÇ`NÉ^@þŽ…5Qäù³›…'-D­ÐWpË™èÌÑø¡­ýÖ;z2R*ÿäþެî¾|@?ÌìÆ4…d…‡›lm©@CLJÊ×wù×·Gç°Üí9j¼Ö›½d›ÉaÃo@W £ÌÌ™ËvR*ß× e|þ¬"ºUs3·Ž¹@Þ ­€Yå=è¯e50Ê™lëaÅLZè3ôÔ?•yå*uÿÌ—ÝÐIJ+Có'í‚ä:]ÈŠyK“[6µb=?&G¥öOðï!Fv> èå%k…qŠ8RÕOU?ks?}vƒœá1=u¤#Ö$ãÇÏÌXKN(iÈC*¶’u¼ÃÙý›µißÅ¿#ÕFíϰJ>AúnÙ Ž™ü”<Õ² –vU ! `¦s]R*ÿÐ76Ìf‚ÐÛ¹ÙJãRmó9Ùºf ÔbèbaSb3²í§Á¬Ïºe kaÏ"Ó¦‘©•¬îv@SÒCØÄòPR²f{ù)ƒ'"yõ×9õle§ÁnßvV V© Øí?=ÉËïš°_'fºæœ%Óš/C%ÿÿö%ž_°fèöô¸çÛ‘¬¨Ñ’ŽyCGtù±ž”Úÿ£ó­Øèæâ°ññfRÕOU?kS?•Ÿ¤P ø¸i1G•çžËùúÂxŽ*Ïý*Û²ß]Oª<>®ÏZVª<¿64 ²þù{*Ï¥ò¿èÙž%ÅŪ<¯¾š%\¦¥@•ç…›Œ™ëM?Ž*ÏãF„±šÐ€þm¾Ý÷ÏðŽ;u8ªIæè†¦&¬÷ásdA»RÑcйˆÎïÁµŒÉ'gÍYz° þßíÙH5{RGí†hö‚œ½È—9rŸ”ÊçÒÝEVoŠ™­ÛL°dN&Ôh3­ãº÷:[êÞ…ÌØ¹”O|  ´~.FçÙ’R÷ÍZìç“·6á¨Z^ot¦%yóçp¿ç5©Ýg Û3ô<)µ?0gŽàwè þo^oR²I©÷UýTõó¹Ÿbª è\W†ÎÚþª“ìHí[Àr' ´¦™k¾y ék^õh‘xªµ&½~Ìä€qƒÎûµÕ%B\¿IGN_°O0êõ£Rù½Ÿ™³ß^—8ãØ†…Ý¿K.2Ÿë³5":eJïzó*i0xû=ÐäJO6\@ºš†óz‹âÈsý»ðïZ@JåËoºB«›+Ð4áÉ S²ÃçoÐüDW†¶òƧþkÀ²8Ù¶{»D4p©\¸™xP@¥îËú%òŒrKŽºÎ=Ƚí­É÷»³åM·’]¿²Ÿ–Rû/Î aû=«½>¬YOªú©êgmîçΩ%°½u7†š?‡ÁþÕjßhväK %:^¬ÿïͤÚPÿÙYúº½‚Eivdþè¶ CCÅ Þ†­È»ZÀ‘§;9êš}XHú¹GD¥ò_nÜzYÀÑ%#l˜k@ Y·jßÔ^,|ŸÄ½ëËÈ’(?f´P½¡þìüΤÚãGÎû—5#ËJB§É>¤T¾¬eo…SÆ 4ô¨.̸یì~ë!TêÂÐàÉWÀ"ª#™¼ã„ç¾ÐÐÁœz\&¥î+Ü“ø·5Ý9ºìU÷"#7¾ƒ§‚IuûȉØIJí_wÿ1„ˆtÿ°lÔë]¤ªŸª~Öæ~j>|1v õÚ“ v%›¶ôe{|í?Í;œFÞKRc§Ò»(ÐÙuXÎÆ¶dèùz²ºOÍ´¿ƒØôtR#3[ô•ùhåéM¼$ÀŽ”Ê¿£¸+Ÿü£Ç’{2µ¤*r“ÖAÞtÔ Òó@9AÆUÌbon:ÿÉT¶sÏK2Ò'Ê–]"ÃôÂþíú •Ê—½¶ <²ÖW ½w;‚¥OR(O‡É– ÝátòŒÉ^ˆ…¯-­7žÿÜžOJݲ=ÄS¬úrô™Ý!~`æÒÅ0~Ÿ¸HÚ´p…[7¿“Rûg?5gCꉀšîÌ~Í{Cªú©êgmî§ïºk0l…-COˆÖgmÈú›§³rùU@×}ócšQOÈSjMØ‚„f ôL¥›ßº.™×{¹xH«.Cu¶8‹w“Ÿz³ÕY>Xc»ˆû¶™¿æÄQÉߟHÆÙ~çèÿ¾l{ÍO²JŒàn_w’üÚdît"ž4ÑÏ¢§}´¦b0ë~¡šLi<r§X3tHŸ÷Â︤T> ×ÐaKòq4,W|âhÅÏCðÞWŸ¡×3ᢨA{»Š7zzÐÁ'r®ïOJÞ/OâýcGs4`B>6‘¼Þ Ò…æ 4}ùLxí܉”Úÿuí?ßíb0´ÙÍNÌéKKRÕOU?ks?çšÊadˆ5CÛ¿Kƒô§È[õ¦°d‹2@íÕ&²Q«¿’a‹2õO9Z´û;L¸Ÿö¯.udCuNª}4Kp:µlšþTºÇ‘£¹ÃG>S´ET*¿—½ëÐðG«®cþ¾5¤»Ž #^“Á¶­yŽîqRÿq…xJÿYþC48½^DëtžÂã}9*uv -–Y÷.GÕ65`n©È܃™ÅŒ|g´šÛ1o󫙨Ôþ×_Ú2ןrt}FO&n;,¢Rï«ú©êçÿr?K¾'‚YË4ŽÆôÞí9yáÒ*>`ÎLz}~/X{XŽ6 ÜgOç*º'C²w¹ 0² i¼¸ThuЄ”Êç÷BÖé… G]Ü"3oœLZ¥>Jw%#Ž>~y©“çƒÝ—òËr´¦ù>Ñ[qU†JݰÝÍ3ýÄQ‹«F¬µUY/È“=t\ê4“Ù‡œ ¥öO?7™/ tøR7æ¼b-©ê§ªŸµ¹Ÿûö³eúíAL8ZôjîÆGïŒäè¦ .[¾…Ô]íÄ4”z¢¾=K×dhÂ;fq¤© õ–dj}d¢f…sØ)—m€æ[ŒeZ?$¥òÿšò ÌlpôçŸ{°sO,鸾7¿ÿô  æcûð>³cÈeÆ µ‘Cý'gÀ­íÈ’!m¿§ßu{X%¥ò•Æ4ûô˜ÈÑp«…¢aõ<2Âì‘ð£&YDÛxÈ…‡½MÉŠá<4¤½}4p3ÏÏ/Q©ûct[²©U_9êæcÊV…~'O•Ïa ÏBÉ"ûi,Á¿-)µiœ=kd7ÐSnmØ«º¶¤ªŸª~Ö¦~*?å’ ªŽ¶Už[•ŠN«¶qTynjÙ¦7e¨òÜfÁ$¦63Y†*Ïò¬Y½1ù€*Ï¥ò'-»½Gmá¨òüÝ;î?m  ÊóÀ±ðtJ†*ϳe¾`¸$пÍx.YÜÛm5G•çy‹¢„¢gTy~rì>ªÑŽþíýa—ÌYåõŸUž¿::йÊÑ¿Ýߡؘ©»п}_ÕOU?ÿ›ûYdÄþ“˗܇€‘F Ôûö3X~»™¶k;NŠà葟kÁ}ÆòDøD62n5 ¦o²vw‘{[ªCõOm2ß#O¨ŠL.mmÁz=ÄшûîÌÛ0‘”Ê_’ÒŒ5{) 6ùÍØ¨s}Éóiê`ѽ)Cól¬7 ËÂŽÂó²0ŽžuÛsÞù³×†³ ³d㞟&¥òuk’ £ÜÖ:5ãÈÊ6‘ÚV1‚ö =zêÇáতúÈï8A—¡wÓáeá@¥îùÄkÄ0u|wH¿Ž<µGY¥,äè·[õ™ó¶Rj¿a‹|ÂàW"êíô^¬ íÄQÉ|ª~ªúù?ÜÏäo`Âkú1»¼÷5"o«…€I§$Žæ½ „›'ÒI®cÏŠû%ªeÜšym‰%“ûôž] &ƒÜŒEÛ]kÈ õç3ÃF8jT<—¹$ *•ß­Øyôþ(C?æ1­¡ö¤ã7¹¬Èü; ¡bYæ~£'¤üÈ<˜R³€£&B/x¸a-ykGso£ÎÐ1i…¬oJJå{žp¼ÄÚoácP´N"_5±ƒÈÈæ ´úÄ4Ø7Àˆ\´–d:4þ ©ER÷¢+£´´Å%!`I,©vܘ%Sp4¯‘=;µõ )µ?Ë·Tn¿Ý™£)®ÂxKRÕOU?ks?G°ï®¡£@sCÔ˜·™pc!Ôr›£Æ±¡0uÁ+2ª  Ó Ô{¬KÍJêM«Ëgé/"“†ä+Ózý÷»±RuòÃïÖìCl+R*ÿ‘…-Y|zF&j\bÆ÷>,G=Îï—ûG¦jq%@ÎãHëLS(߸£¾ouaš¶‚Û4ÿ§›¨ÕçïÂF«÷€JåK{[šg}ví¼.Î&«ô6]¤¡µÙ“Ï ÈÈ/á¡Ó@/ŽÓbËßm!¥î¼'h–$èîÙÏ…¸ÎÇțdž1ëiÝ—ïdžäD’RûSw^ ŸŠhÄÕgÂÌGíHU?Uý¬ÍýdÕêìÏuÚK³>Ëèô‡£Âúõ&|'‡ÏÙ©u4è×¹:lNh@}¯5aÇ_Ð:¡áÜA–F.¸§6Ü&G= ÙÅ]Í4ò¢ +Þ2YD¥òÛiX2Z"º9£=+͵#ãuï8W‡/4fGáyîržº!¼×*æèø.=`—V]ºóáW¡Íò½€ÝìÓÜgRù¢*Ÿ‚UŸ{€fz†'^ÖÈታŽUû|.¸j1vú,Åã ™ê°aõ*ußÃïÀ]Ï h@øWÁÇ<‡üÔk>+¸gÁÑ®ÓX«¡ˆJíϵHFML”¡¡öÉ"6ø ¨ªŸª~Öæ~vNlÀÖ4þÊÑY·2ùéròúñ`÷@Wú.»¯ã›+î[0×ñá×°+«¶ !½†ð¤‘?D4¾Â…ÒÅÑÃcX|‹rôcÀ,&³ï¨Tþ§Ö¬Dk¸ˆöÛ`ÇL¶øÝ­ ÛöÔl›:øäw&OŽ™ &é+P¯´ÝÑɘ4úçs6¼I £RNƒ(\%¥òÕùz¦«tXÅ%Xrå7yÖþ˜ýÂQÿÇ%` ¾ ÍÇeõ– t'ÀØcRêþ^/5x–š+ êõàË»g¤{oVø.ŒÔù寖«ÿ&¥öGìÃ× ¨/èñøÕ)2TÕOU?ks?ÃÂtÙˆ/9ÚR¡Çv¯{LFæ¾€ÊúÍèš5¿Àäö¿.óíÅ<æžÐøw¦8ÐÐÀ}E›‹á7O¾dÁzòøyš h]YVrÿ-)•_#À‘yÇû‰è½åÀŸ@¶I°…êh{@§/Ó§ !çÕ»­ÌÌè çJ€ƒ-É áaê¬ÎЀº{…+mR*ßÌyéày¹C§¥ÒA ÉàÄw°òÓMŽší~cRΓc r} †jéÕÈœîe*uߨº¼È~' þy:0«ýR» ›]¯ Ö[°ÝG. ¨ÔþÝžƒù_Eô؈éüámSŽªú©êgmê§ò£Þˆ”ßá¨ò«'jØ2š|FŽ[? ¶Û8sÔÕüŽpp+)•ß*l¸hrð$G+ÎeÈG:GFEËN´OÆ\ˆ¾s'ט5aï,-]ØÙŒ½Õ‘¥‹Úc2 ¨Î…˜ùf)•Ï3p+-²•£Æåžìêõ³2tLjgxÓ•£ÅÀ¥{sRÞ˜½{º^Dƒ½š°ø±Í8*uûŠéPåq‘£Q÷çÂ@£,2·´#³ÜÑP£â,các•Ú¯¶~…xÝò aA%âðO»H©÷UýTõ󹟱–Y˜ûQ@o¾ïÇ.Œ8Kv ˆƒ¬gú Ô~ÚYè}U—ü|&Åäqtpj,KH#»Dr½@s¦‡$ïCä{™`St˜£É± `Û¢: T*Ö´ Î Ûdr´´µ® ë&’^¾‚´ìI½±ZÐ÷lòÇ›vÌwòL@oÎèÄ&_ÞNø»ð„+ÛDô«‰5wHˆà¨d¾é£Ù¦Î#t¶ñPÖmÖn²è‘)ÜÔ8úä€6v|&¢Î­Ø×Ô]Ôr3íÞ•”º/;¼Óä…_ÁàÐ=œðÓ€U8Ðáš3Û»«2Q©ý†«Åñzƒµ+»k¨ªŸª~Öæ~ŽìãÊ5¾è•ö½ØàÀäŠ?w`H_-ª×­:žP#YÉáêÒ}t%æ7%ߟØ3®_t½Ïrðt)!û¤‡D±©jÚý©£FJåˆ^!Ä­9:8ï¬0¤•œÌ˜©C>Ñ›ÿ|OÙEÖxvd}»§jbnÉÎ[’¡ uÄWïqÔsŒlJ-"¥ò]wÀ:Œ¸$ >ß\ØêÌ—¤~ÈK¡4æ”fÚe“_·ø°ßÖ’÷´g±œ§Y*u¼ßÊþÝHãh”v8ä¿>I6*oÇü|uD”›;³ÐÊÓrTòóýµž˜XÏA@s5ë Aãd¨ªŸª~Öæ~>Ù'°ô©x²'s½[H·©†ìêmt¼.»?ï ù6y7h>ŸFúÌ\ jÁ³È æ°áZ EÇF ‡… 2oˆ9뫞ÌQÏÃXå7R*Ôµ2a¯u:GýuÁÿÒ2ðÍCÁåüPÕÈØ$xè‘Fïš±†ŽUpu6ª½CÕã7€»æ9Žê­ÜÇï“Rù*vödg|ЈQvÌÐQ P#ûq‚ϼÛ"êâ­Í,*'G/ÀÂ]Wêå!ckNœ!¥î÷™¹â,Žsôpx|„|ªíÎ"¦š ¨û¸qÌì³. RûuÚra@âwj;NÎøF ¨ªŸª~Öæ~Þ÷±g‡¼ÔBnËZÜøHêŸÓd}jr8ºÎV‹iç ÝøÀÄ5«I¿H7hú(žŒ é%®ð{hï?¿ÄÍýŽ‘/•Á÷Ã8:.ô%äÏŠ'¥ò»[ZBdÓrüï98@ñ€d2^í¥l·¶©ˆVD‡É#̦’wW€¾]†.iulKÿõ÷Þ±ðÜz(G‹_Ö…Ï-"I©|W§ub­zþP£m_VEf½ Íl®ÇѨúò”LÒ¤ŽËz‘ hî¦ðoº‡”ºo7.tæ$qtÛõð4"¬<ìÁªBà ½1ˆ-ÑPRûc Ô!£¤F@óªä‚ã@UýTõ³6÷óÌ¥NÌ«Ç7@F´cZO~‘³¡>s°ÞÍѦVl¨ézò`„;4m{‘Ü<t¢JÈ:Öù¡²@?O³æ‹f¨“Îu XÎé;ÍmèÊ›9ª<ï81_ô5ˆ£Êós:õánô@•ç)ÃJ ûRú·ù´[4gOç$ ¨ò|;'ª å¨ò|ã>cV2Æп½Ïßo‚½O¢9ª<.+‡Š7U€þíþhG]¯µýÛ÷UýTõ󿹟®§°ÿäòO™Ë¶' kÏÎ'“•› !ÂêGƒ+¬áЋÒx– +ÙŸ¨•µ!³µÿNZö;K0T3ØNY×'3Ïíƒ8Û¹R¬†1²I¤Tþ{^ÀÕfK9ú~R!ì}:›LumÊNwb€F˜±Äú3Ȱ;0Æ'Š4muÖŸL&ƒ¶ì…k-Õh¬æ¸‘þ£RùJÕSÄW4¿4Bôî6\†fµ{*ŸùeG-~o–µhºŠôr´`— —“;b{1îן”º_“ÚUèô”àÊúy§’åË‚­iˆz|û*d ªCJíÏía M²R4×I¿I©÷UýTõó¹ŸçY²Ì7ú]°`ŽCÞ’Ço s48>f¾|G¦„VAÎ †i(‡ÊuR»[¾0wÖg@c´#dVo.“u/ƒWkp´ÁÓ ¸Ôç)•ÿÑ™' :ùrÔ:ì!tó™H.zdĶÞÝè™-ÍX_ײñä4ø9÷©9{'¼ûù€œí¤Ëšoäè“ufãDJå³ÈÑeôl9Z±ö’ܺ{sÍm¹LÐtà¨õÊ»‚ZuäRñlñ0?ýÐ`ë\  ¨Ô}žÜ‹m\rП ''}ýw‘år4Ôf¶,éÀz•Ú_¡ûCð^f hr½!ÖÚŠTõSÕÏÚÜÏŽ LY¾k©€.60fý^‘ƒ[EC«“?8ª½â4Ì ª«@½’×Áù/€æîê ¶ÞÈé~šâ{­-dꎶbI×¤ãžæìXÌ;޶ pcí&¥ò÷«wŠçŒãèóà[ðb¦™qO—ŵ»èè:,aËk²ù oÐÐ+&ÏÔO¯Íó|%^½8šÛöœÓ '¥ò©åŸKä%¢¡ ²eE+#ÈŽ?uáHÎjŽþxn o†…1YxÑn@ר·fýŠï’R÷o›ËX¿š €jwéɆOºBæŽ#_sÓ˜£Ár5±ë굤Ô~ï ÓäÓ.éêzé´¨Ù,A UýTõ³÷³“Osy¸P@·ÿiÊ>$CFÞ“r jëU÷æÿë²µÍ ,ò ×­5àËÍd‘åhyX<Ѐ7úBr~KÒç»Ûz~¹ˆî,ÇF P©üÇÛ_ƒC<9ªÙçýð"]‹Á=ó' .73A«U=†–Úµ‚Ú<Ô"ÛVÜ:)ƒÌiʆæ¨ü|–§=‰”ÊäÕK°8»SDŽnÚ.ßCt… ôÙ Ú1&Ïó$¯zTÀ£‡€†/Oƒ9ãÎR÷µ,XÄŠ›€îþeÇ<ï’QjѲzÏr8:_í‹ð\ý)µ¿[†×x+G;V™ð3ÕzUõSÕÏÚÜÏÊ ùú¦€ú87fአdòmæT¥¦@í=Ù(/Mm¦ §#½mß±=lýê@:œ®l•¡‰¿…Ü«EdËCFlîlk@]ú6`^¬T@¥ò6Ȇ]®~]tRYåsIÃ-ðâl}†æå ‚i]ê‘ù/Eƒ‚m€¦|7çËß $½¢'3Ïó÷ähÓsÌ«ÂP©|ù¯Ò£[q"üæ‘ú:Š,8ì Ë:öç¨á÷q`¤ë@žº’?æEÚoB*LèïMJÝOÊê ÊÚjŽ5³³BúŽ]Ù´èÉ·A¶·>)µÓjÑÜo-G#ì1æ6'UýTõ³6÷óAq#V²>M@¿ÕcÝ’’É]Ç­ØÌÅ=ÚÈž¹½A¶YÜ^˜tôçR/9ѬYÕ™x 諊¡ºnÙ[sæŸAgudgû*•ßÅŸƒUâ2Ž&úr¨¿6œ Èý ìn÷ÐjËBÂåb²lƒß»üª€®ºlÅC/ª;¡sD[¶gÅ9@Ç«±Y9¤T>••‚ºëJu÷« ýlÆ“gƒF@¡Aަní J[‘f³s ßÂE€ê¯»%H©ûzZ˜u×B@âڲ…/ÈŠóõYjzGâLÙs;)µ?ñã2áÃÍWyÝ–ô¨"UýTõ³6õSù9BíšµK@•çEõ›û=•£Êó”H_(7¨ò|T§0aCHº€*Ï{´oÅbœrUžKå7á ¸^´ž£ÿçôGËÝ /©ôTo®ÇÇäŸQå¹·]ôœ¾пͧaZŽxuQå¹#{8•׊£ÊóÔGѰlÂm@ÿöþî­™¶ðPåy¿Ží˜uóŽþíþõXXñ œ£û¾ªŸª~þ7÷Ó×Ó“ý'Sëæ MŽ<åè•»°¤ý+Òãënaûýí€+Ï“&¬#zé°ÀŠ­¤cÐx½‘t™ã êìåèÍ‘á‚ç«d¢Ë@ÈKhPä ¡žîHR*ÿÐÕ}á–ïSŽÖ7ðÿà×äö¼Daï-Zúû‘`íÑìÛn»yב£ÉͲn3’EôìR3èsz Gßí …;±7I©|?®ŒáßÌ9jS0Œ»[’§ê]‡HƒS€f >Oº"mËú°®z=9Zßb,S ÷qD¥îg-Ì•û=nh|YC±l«éúè¼àaîFj\$„½W#¥ö‡_» M÷Œçè¾Iw¡jd)õ¾ªŸª~þ/÷órcsàçÞqôÅv'XIVT§Ë·ÏžèùØqøZW’{=ƒ»Ö¡dHØ]˜õs2ùë‘ ø¬zÂÑáA»áI¿d›}§ÅöÏÃ4wy¡ØoFoŽJå›;ªÞs4pÛNˆ‹«"‹ßÕ…Êñæº}¡è–d˜{VQï‚3äÈ–[ìȤ·àdZÎÑÐç6¬ç°,R*›:šOêÈÑÄá½àÒv{²]úXz2и±û¡›¸‘43ÊZþAþÒéÌRgf’R÷CWڈߖ¾ÐaCÅ·çòÉHÈëœIªYÁ£•ڟؾ znáèfûì`Z,©ê§ªŸµ¹Ÿ²Nc!áÖ7Žê$Brrð•ûbÅa]@¯Ç´ß# zäÂÍŽƒ­îÿž}èOž5¹ ÷ÞˆÝòç-ªï!ÕúÌf»GZ%€³>Rùwù…‚>ß9º¸DY·’¾ŽÍá½~/­Õ6 ð%å7;2í¿É·çL˜o“€®ÍÆNYqT±ÍeY/ Rùl¶:ƒñWŽÆë;Âã}cI‡²]3{% b¡ÓÊÙäѬŸ\¯€ó( d㮑R÷=òçŠ/³´¢ÃZÑ·^ ©ÃÍàÀšgdè§b¡àÅRjÿ°¶­™ìP' œÙÍ>¤ªŸª~Öæ~ø¬ƒcËÿp´ï±tSMËkÆ«+dèÃÜçÒR=òö ¤è õ§ BLýû/qtÛºÏðìʲïz öxÿi2ù›>{¹¯/)•ÿÈÝ<µ¬áhÁÓ· vÿAZÇ~ô‡.P«±rák?²Ý"}æ0× ÐÅçê³n)ÍÈMý;03£ñdÂa v9{)•okRHã³9zµ©3lqYE>NŒƒïÚýÞz&Œ e΂"‹Xòáœqà'KJÝ/Þ!h·]@M,ˆ ‹Èø¾6‚ã3NªµÈ—WT^•¡Rû¯rg;W‰hÞűÌc¥› UõSÕÏÚÜÏM†É Û»ŽݰòÌ7ÿ×oô¸»PŸ£ï?ˆ› !cVç‚–^  6ž£1'I­‘ïàÅ­n]Ö >è8‘ZšìLão"ê\Y60à¨T~÷ôß÷ç Ge›°ê Éü13Û]­4Ôý¤¬B[M†šOPg{[ÔtéHuö¶ó7mýî wôòª6֔ʷægo(ÜÅÑŠûC!Ìo/¹qR<Ø•¹ú­ßØÿq8YòÜ®d‡Ó Á'½)ußDqBÔ6ï# F2ÅíOuI¿õâé‘§DÔÿëñ§úTŽJí×3Å2›˜Ú#¶󺱙TõSÕÏÚÜÏÒ»—ÁeªšÝûîxwúÍQﱇÄvÇÖ¡~MÅa<üÚ|(Br5ëמM¿Ajy»'à¨Æ”?`øØŸìcÎÎ7'£g÷bZ×å¨Tþ\ï&¬éÇŽ:7a^} H‹—Íä)–"Zñ¼R>¹ûMr[‚&+Óý* ^[²ƒïµmÜâ+ô!÷È…i3I©|/Ë&C¢K G‡N\'÷¥“o%Â^›ÿǾF帶ÿORiÖ$D£©HRIÝçq&©ˆÌE¦LI¦$s†TBE$J’„(™R÷u E…d ÉT„$äÿü_ç^ëþ­µ¯åÝó´îëÍçűÎãøîík×›=ЮíÓÀ7a³y6@]Ñbføœ=¤±ØŸ)tÿãû"ίæ¤ͺÍ>úê€j4눮<Ï£1Aò°~AShïÖ>´û­K€îéÝ‘>éñˆ)í§´Ÿm©Ÿ’ߘ3¯ Ü¯…G%çå÷bDgO_äQɹÜå\â“{Pɹs™ía¾ŠG%çIS\èŠ!S•œ å?ïÙ“^˾ͣ’óŒLuîÞ •œÛ~W§›û*9ŸxÔÖ•=ôoó™+oƒ?ŠxTr¾É(.Y¨ä<×Ü‹›´Oп½?-ë!×0îI>*9ßñ4Vÿã»_m÷=Ð=ûп}/í§´ŸÿÍý´&ãè¿iºZŽü€ /b5¨ÇŠZfÞ¥oÜù”Ys¹‘ëlZ.Fû4ÙRÓ×ñáæ˜ÕôÇyº+—Jte¨õ;of¬yWúÒ|+ó{ËO8áXºoü,¬çðè–«±0c›S¼blÉKg~I‚ƒR¦Ð~õ×ÎÔóL:Ao ¢™"@¥ý”ö³-÷óy³&Í™ hþ$-j·§ó‘_?îÏD•î «É<Û›ÐäeÝ5?`F‹¿-`vˆƒKw8uÉ)†ˆ†rf„Jwº°¦è?;Q¯h#¦Pþ^fÖ0Oñ&A=O †È”ëÌËGk!g–)šxNZý˜ƒª‰ç{ŽÙiáÐ>]Êüxé$<Œ e~ÙÆAÒ¤‘L¡|0®–{n³Œ ­ªo¹G¯¾: &üOøòt  Ó6|…íK·35¦$ÁÄ·Ìw­IÈ—[L¡û¶qPó`1×Û çÝ—3?^«ö£23.éÒæœ"¦Ðþˆ±útíï`@G<–£êÛ"™Ò~JûÙ–ûùt=vÇP›~ZÔ¡n8óÑþùdõÒ<pû q¹sŒéæ¨CÊl´*L†VºÆ2u†ËÑêWe<ºç¢ -q?Ët¬µ£¢Ñ‡ z>]‹ÎÛì ¨P~ùŠ!à=÷A7yÙCCÆuæ«ü¯à]aË£Î7dè¥Ê|¹~¼yQÌÌyvž—æ1ë½8¯ñ`þ2¸¦P¾G9¹kN8tÓï<ζâ"S¯Ã[¨û’¨¬e˜M;ɼyÔŒ xº‰ùks§í=‹ B÷M·ÇÃÆ7«y4Çf/DÜÀÔzbK+w3öޤS‹º0…ö  6·Âý~¦Lû3¥ý”ö³-÷sÃnMZžï èŒýèxS?æÀâþàhqšGW¸ûBÕý¦íÖgPÚu ï‚¯Aúêu̦Q„ׇòè•-£¨¸I–yµþ8º{ Ïó̘Bù ›íà]f)AÍbm@œTÆœ;W‰j>Ê£Í2ºT)ÒŽ)—PÝ,Ò˜üÀïðz_$ÓñM·üG-°£>·Ì™Bù‚;Æq®ÙJŽWÎçѲäð,Áº¯½& ô#xTA66vÚÆ\\Þ‡Fm´ü®2m<’ÀÚoøŸ^š¶óTsBü¹„)í§´Ÿm©Ÿ’Ÿ¡… è±PÉù¡ñ ¿nJÎãGž‡šg@%ç±±–ÔÍ{% ’óŃ»€Ù®ó€JÎ…òíÈC‚JÎÕ̇Ðào«9Tr%ótu;ó¨äÜåMÝüп͗ãE,Gð¨ä|û.hr)Tr^ݼ.µÚòèßÞ¯-J…ZÙ<*9Õ ‰®{ýÛý1òGáÁõm€þí{i?¥ýüoî§f´ý7ãüi´^o@ý[E´µd8³zRÈUôãѯ-Û ¼ÙžYÝp”J˜c¶@»ÍÖÌßchŸmÅz²µ?­Õè+F‡˜u„ñ/S5T_Èm5l$¨Pþ§‡=à÷²uiñ™[ò<ú;-²³0{tI€Ëÿ1ÕI‹.90†YPdBÇÊ0{퉣+€ú­éJvŒ‰c åÓß8œŠ/¡Œ†ÓÚm; úvï'"W|PÿžIäwãVfÞŽÓ`ÿ#™G7û½…»@¦Ðý%ýÁ9o ö±¦°pצ‘b"$(­âÑ~³ÏCV¤Shÿ&ÏùvgZNÔ¸Ù‡ *ô^ÚOi?ÿ—û)÷ÅŽ& žh©»5Õ赑™ _vâQÑÉ} ZæÃ0²r&Ú1Áÿ´Ütb®~;xŸ ÷òûÒk"?@fŽæ¼ æQ…‹æ@Ü ™Bù缟Z;ñhbÑ8Q؃©sðôÚaìŸ ל˜Ç¤¦ÓOphý¢!4ø½ˆÙ+‹ó^îBЂWÄÒ§ò¨P¾EÙN´W7@Y5yhË\½Ç™8{12~‹Š¿vgî<ÕÓOð¨ÒÎ_0éÂSüï‡>´zDêvLJ>‡1;$ÂØR{=|ÆwÀÚï8š8û‘+‡NÞîFzØéñ¨´ŸÒ~¶å~îÑ@¿Š4Å»/mW‘À´è{ºÞâÑ–ága×õ0æÞ9ï v¸;óEH{jÜÛ•¹÷°6}è hV@챸Ë<Úô4Ô/ñhŸnô¾Áh¦Pþì“ çŸ~<êßÞ7ˆ˜glOÀ™ÉsA;q>SÑÄž®6zMÐ^ÞVÔ8x ²cEð <ŸG;ÞƒÖ–¦P¾SÛ#þ€^›?€î7ÚÎ,X&ZÓ¿” ¿ˆrFÓd°,ýõ8—C×nP£é¿C˜‚¾‡T¡W»õ€v¬f2ÿ˜ú í­y4Y¦Ô)RŸÞy’éÐá:„YlãÑjµ§°?`'3X_›:õcŽs3¥b÷z½( ¾Ë<TûŽ ¬ÙXÂ<®Ü'f‰Ñ±}:Òéù›*”Šî,àÿŒâÑÜ(è<`³Qå&½«™ ÿÿÿ” a¾Ò1¦§«úh³Õ[Ä<æ«B•úðhõ¼N4põ ÌØ“š¹¤§Ý•Îz–™=΂ÈÌ6ÌCsu6’+cúph¦wÚè)B56Ñ/uºÿܯ|­óô¢39~}*3êBGjÚË’GÖv§®ýr¨àï‡ccaQÑlÕT̀㡣ÿQÚOi?Ûp?ËwèÐ+gÏ:ØUî <34çŒ]»“G?W 2 ¢˜%–4W]…C´ØÐ)÷¢ ª‘Úƒì‹Ot÷.%n 3¸Z†º¾;OÐðTºüOw@…òsÉ+!ýøRU+…ž¡›˜ÏÉÐ ÅÌ–tìÕ…LÝëO ¢©P5·dX¯ÿŒ©oÕ‘fu£]F)гâD¨P¾ÜxUÚ=ó  ¥Î2ÔÐþ³|ÂiRõt/ÇÔ¬%òõ7˜YjtÎÌ}€>Ô»º¯o2…îÿ²þDœSÇêfþ–…gÞ3¶ Ñš¯Äè³iƒ¨N·{ÚŸåw2G8ðèz=1÷¶eJû)íg[îç°+ò´ëºB@ǶBÚ¼¦ü0ÚièFU–3¦ýr§3ºÛ:h¸ 56ÝÍÉlø®J-'&t†ž =®?P¡|ÇÞ¿‚(ñ=@Z‹Aä‚©X¢õJŸ9´±“\Ù$Ç£—.„›¢ë€ÊìzŸälS辇k59`hYH%éµbsMzšY¹–¹ÒE•Fzæ1…ö¯\v gQuŒ{ é=˜Ò~JûÙ–ú)ù¥…~„Òæ›€JÎ?¬íOw/³äQɹ¾±2½o\¨ä¼|Âd’¤æÎ£’ó”Þ#D.es•œ åß>.íåQɹƓÁ4æÙf•œûË]x¸ PÉylóS°:žèßæÛ´è$hßTr^½l(œµSæQÉyæÅt®º^Ÿ {?üî’¼×PÉyãËRh1{èßî׫øfs&òèß¾—öSÚÏÿæ~F;8Ò³Õã.ñ7“çÐíÄ·¯;óÀ=}úëÎ MzÜ‹–LéÀ,îfMS÷+1ƒØÒò=ãĨÌÀ¼³JZ£-xtéä•p"};  Wz’“{3…òûE—ˆfÝš ¨ax‰ƒQî æ0»5Ðc•:sÛúHŒýMÐÈ› à]ù’CMÇý€áeÌÑaßàwŸK<êfkFe»2…òu Ô¦>^6"Ôwp'újŸ„mzͬð†•+î2ofËR_½ÝY‰ÞûÆ´îgA»îïLÐçÕi¿QÃÊ7ù¸*­Ý=Ž ¿;ÒYË·2W_ñ€}å×8´qÚTHSÀÜ´:Éaõ G½éó0…î[÷”%q{‚xÔyô RÞ3„é–GTü·ˆÑ “£‰úOyÚ_©¤JKÆs¨x[jZgÀ”öSÚ϶ÜOŸ–"’õC_-%:2<ª1І>¸<‡ ¥jƒ¨á!@gïQ¤•ÛV2íO¿¿K›™Fi'ašÅz`y ŽM›Ât×;&]gÆõëKó'2…ò½Wܽ¿'AFo7›ƒµ»ÓwŽ "++¼?…©¬ ækÅhmRgZ;‹¡ awafŽ/ >©Ðéa åÛ9]žšÕ$¨bƒ,Mèt†ió`XL ‡NiXz­Ÿ™¹¾y`f›É£6ëeiW­L¡ûú ¾$óøV­ ÜAä*¶3ÓÞý$;MC˜^ò¶?›)´ásCjàJÐ='zPgY@¥ý”ö³-÷³Tññ¾¡Í£n¦Ï‰_^_¦­Vúç  &“L(×)œ9í½ü…0û%Cêx_fùÛçð)|FÌ®ƒZæ“Äþ´GõD‚6 V¤z“üýßÀPÜkûK1*3ˆ·—=±™C ÏèS­/AÆý!ÖÕE"´òkª¹ëAßUéÒĽMsØq÷õ˜æÑþðx’ S(Ÿaê¸&… ^=[ N®Œ¹~Árš'Ë£q k@ÉXéq®M,ûÉ¡]³ÕhZÚ^1*tß6ô8ñHÛÅ£»c®Í}Ìòw×!Èñ sQ¥¶®'™‚ûÝ5èÕ›=ðþ=fe1¥ý”ö³-÷3ÆüÑ> <*w ¬›À ì©Ku§­,OšÎ3ÝŽDB¿±C™sê×Á†§Ý™w~‚›JuZ×¥,Δ0Ó+yxx| SâáÈ 3¦PþŸHÑýÕ·8´|²&9–©À£FÉÍÙIbÔúÄâfÏ¡Æfªôööå€^&? Hg?Ó¾DLë®ÔjwY­vKŒ ås^ý y>"hÓ®Ï`íTÃLü½ ÚÍëÁ£aa[ÀÖÂ’Ym׿˜t§W+øl‰e Ý/ÞúšvOæÑÈ>²`s%•é1‚N¹ìËs¨ÁÒ0ÚÃ*"^ô€ì`Ø´ôSÚOi?Ûr? O¨Âô—óy´¶¨¸œ^Ç|°î-|^p P7‹øøòÓ¾ûR¸d/ËÌ›:zè='¨e«,Õ_”Ρ‹UTèpfPÍ&Ø}EÐê  -uìgÅ"²ahO½ù-ƒðý(³E÷ѳéÈlçó\º=’©m{:æúÎt/¼é‘Ç<û´”Ì5™Â£™·ï L¡|¿§}ÅÆz‚n8ò/ùÁäìv@Mú0½œ‘a^Ìâ - ˜ÿŠ ú·>À»|;@…î§¼ëžÒytz«dÜ=Îì{ÑžNzBÐóÕ&Ôùì@…öËÄ'áÖQ€Æ´ÊpKšU™Ò~JûÙ–ú)ùuxm¾ž[yTrž­‘‹¾ÜTrî¼y"˜~¹JPÉùÁCÚtÓ¶¨ä¼OrQ|(/B%çBùï'?'OâQÉùÇðžPp1œG%çbg §#³•œ? ’§Æ“ÎðèßæëmYÁDPɹžy:ÈwóçQɹÇü"xS èßÞ/õ²‚tûS<*9ÿS-CÅöÇýÛý"oÎåÎj1ú·ï¥ý”ö󿹟“'ÚÑóãº÷07*Њ°ç·*Šùêì° ÜÌ|¬s\~ïb¾K{iCì™±_ !ÔÇŽÙ:¡¶1ž Á6iñÕ€îêÝäž»1õ"tHÊh*”ÿ×òzøìUFйÏê`bQS£Ü’êÇÌfð½i¯ê€î©F[îçÑè㦴ð´/³<| P4Tf1¨ÊUT(ß¾÷ ôáÑ÷žÃ¯bs‡o¼ÖîÅÓ,\†f6dj4n|ùǺ¯v£F¾Œçѳ}ÚQÛA»˜†Sì¡®»  Nô‡;Cà *´ò­  ÓIÐGÊ"׿Sð½´ŸÒ~þ÷sç©{àŠQ¡ýs.›B¶cI>ª±± øµnãPi?¥ýlËýœsW " ýqÄw¶2“¾ùÃGn?󼼩Æ0§¾Ü ¦Ìi·#`\ƒ<Ógú!ˆ)ócîÈ …w—DÌk›{Ðõ«By´Sµ ]×x_„ åß>íü¨èòª§¯Áõù-Ä}Žaj¼.€'ã˜J³­h@Ñ[‚~[gBÕy:1n”™Â£jžg`rÕA¦P>ÇÇíé—v&<ê¾N‰f,ëÄ´\òÎêÉ1“5k`mŸêóv6ypWGSŽ€Õf!L¡ûíŒThÁŵNS§—…3û·ŸÝVpèŠ.3 Þù*ShÿÜ_½¡Sœ­ð噳˜Ò~JûÙ–ûÙáàQ0{ÌûI0¦{SãÃbm²–é×yiÏ»0‹îÁ«†f‚¾?V«`jÌ OwôÁ² p§îí®7“ù5ö,ì·]ÄìW÷:Z-c í>¦HWÉmåÑZ&´âƒ SÚOi?Ûr?ËLJÃ}½¹€¦®X Ë4'3 On781:ö|±®æõ^XO®D0C‹‰2i`¶X:⎴|4IͲer¨JÂ8®–Ç<¾| Dy·ãQ¡üã”®@” +.ÂÀç#™0¿7D®(#èh›ž yu!sg®¨Ÿ\¨®ø™pÒ‚9;Õ¢$1%i‘µ +*”ïà"#j'²¡‹fÓâ‡ã ªo¦@æ†pèÓÊÔêt¦üS±"´9ü7D{†Tè~a’ýU5žGõӟ߇3wU§UõC™“cLé†ÚsS`ÿýP+ÚÑv$‡DXÑÝ-Å•öSÚ϶ÔOɯ…[ SÜ•œÇüô‰Wšò¨äÜôQwøúq$JÎKCíaÒ畜— ‹ºôåQɹPþÙ§²!gÌx@%çQæà(>&B%ç‰n×Iíž|‚Jέ÷‘ogžqèßæ;œcDW9Tr~e:U¥!F%çÍû¿AÁay@ÿö~¥FOš`Ç£’ónÎýh?íqýÛýF Æ´óÓ@ÿö½´ŸÒ~þ7÷sÈcKúo~ÛZÔOáУœ-Š5aŽ·©#;d´×…—¤àD•­[u¶ÍOäQ9½÷;!ž¹x©~¼Y(q'¨/ÃÔlT¢.†ñhÅôÔgDR*”¥g<@7g@½ûFB¦åP&ø´À÷!M¹GÐà÷2´K9 áïu(ÿô‡jìQ§1z©ÊWd±Ž_ºMÐÞŽ~°}I3¬Ó È«ËàQ²E–VœÞÇ hAí–]ºÓÒLí9Tè>wk9¼z ±¢e0æë)æÁŠn”hØÇ ¶ó ShÿÜÕÛ@¶XG—ê˜fÉz/í§´ŸÿËýìçÙ‘zߣ“3”¨ßWsæÊðo¤°O.sóUHx»•C nw¢så–ð象½h­±£Ò~JûÙ–û©‘¦B48J…:Ž]Ä„›þ°È¦ª Þ ¯fôf*ùèÒ`ݵ€ž0ÿkf®—› *1›9tÎçu°Rî*3yA ¿G#IJ4"Ï)”ÿå}øæ-híäŽP4û.AËåiâN%@uM¿Â§×æL¥¸jRnbÉ4\5‹¼Ú|Œ ï¯»Cñʧb4¸Ý\˜;ï‡ åS´CÎ8 ¿>õ…BØ'FGžìGƒO¡ÚúPËÏÇ ªYÞ“V+aª$ÉÓR¥€ ÝÿU>š`3AÎëÔ5ÌvYò àÓGéÀ@ø&7“)´ÿqžíô=“ ]@…jú¸*í§´Ÿm¹Ÿ#äTèú´ýÕSQ¦ÍQ—™|H\oßG;Åg@ã3檖LžžhSÌx°—ÌÜ™ CÞ~çÐY]B‹•¦„©QÇ»|4bÁW°4ýJP¡ü9—?“ò¯I úì0pcö÷}·CD€]ç¡H˜rÍê¤æG¡ÍðœCvêÈ£A2ép®ys‘ÃØ“æÂüù§ÕrüsèæAfÐÿÏ.&t7¢úvÒÔ¥‰c˜=*“@s~&ÓúËMR¹œ)tXé,8tÜ ªÕ3akŒó¨M¼÷ŸÇ£‰«Aí¦=Shÿ¾•¯áAÜv@¿8‚ؘLi?¥ýlËýLOêHË6¾%èûs 49FÐ@™ 0ô” Üu¶uaN6¸A|žÎT|Q‹¨½«&¨oï Ð2É„G_>€xCSf-ÝÒ4uM<Ø¥É2…ò7’W½öˆÐ~.’õybt} ¨õ¨Ïªx°t2dŽÒÿ@†/áÑëk=¡Ë¥ýL³7úÔ@q(‡rºôÛ(K@…òÙܲúꇃÆ×·çщ‹é캵€>Sü–$š)së†Ãø÷E¨ÇôÝdèŸá<*tÿV‡ps” A‡;Lƒ_­™Ã”~®õZz©-üdÇÚXÁ.ÕEª¶'‚tд`Jû)íg[ê§äg5¡u“ë¨äÜhRüx©Â£’ó˜!·E3F&‹QÉye—&Øc¥Â£’sßW³ °êA%çBùu»_ ;;q¨ä¼Pw+ÔwUTr~Ç"ÚUeò¨ä<·²Êá ›O³`¤ªò¨äü}ð8µe ’óNÆ›ÁzV þíý•›¼!ÐÌ„ ’s·¯­pàÇ\ú·û5½DOEèß¾—öSÚÏÿæ~š÷¥ÿæ Û®­ÿFŒê=5ß»C8to§õÀçôj¦?è¹íaÊÆN€-î84Aw: {—Î4y÷ ?„J¢.€Ó§ýLÛ}©á®E"´G‘,u®Ý¨Pþƒõhë÷ÁõÒס·î'1užӾ€îÍ?Õ0Ÿ}:¦Šþ ¬UÐa.o¯™¯î‰Ð7úÂ׺Î<*”OûÁO¨wÞ èzx¹û˜ýÒ?À<š[ûš¶pèD£`0Þ›ÁtñOÅêL¡û•c#`žÝp]ü|;&»2§O…/¼&A³NÏA!bThÓêW°g¦v¦ÚŸ4™Bï¥ý”öó¹ŸóZû€k0‡6ZÁš_˜wì½!3Ì‹ U£ 0¯F„Ž»ºæYtàÑÃ)ñàQߟiùx*\¾Шg׉Òí¡LÑù`Ö²é³}7Ñý3— Bùû5jPÛ™•u©V¦f#UÝaû=™__,ýg­˜-‘¥p#I—GÃÍ^ÂrQGfö•ã¦Ît)W¦ýW¹1…ò),«íÿ.’Bÿñ]³ ÍáÐ ²TœìÉ uô;Èóè«ì'`>ì+‡ ݯ^´ ªæŒàÑ„Æ5’i2e™)sîÌcÒÊ,íåËÔØ÷æ—làÑJ±>Ýü㇠ݟ’iH-×EqèÁ(#š5Ûƒy ý1øò‰i{w Ü?È£Bûöé˜Ծ€ø7"½£ªJ=§Åq¨P¾1wB >z ‹^N‡àI#˜UÚñð½` sÇŠµÐkÖPæÄ|]jìþ oÜžC¿P¡ûgéS5 2¦Õ6ˆÑªéçÁy} ŠßÊÒ>_—2…ö[]UÓ(5n–£§rf2¥ý”ö³-÷3ÏZ—êmþDÐ×ï4é“~š€6^ûן¡íÍê Ï÷‰}Ùç¼ng¨yp üRÄüüšOÝÑL—¤mp1ÌYdZ‡Fé1ó«6ÀC]™BùÕÞƒÈ)Ÿ8ÔöÝÈÞWÍô®&ŒÔ¦¹J×®d^+•¥”=™aã+ jõfæ±"ez_å'A}o€ƒ÷z@…ò5–ë`háSÐ|¡Àh4*Œ ˜5 ¶PFß´ñɬñÔ|¢XŒ½DPÁ~n0¦“µw@£œŒ©hÿAÚgcwÚ<â ‡Z0¢pz!A…ö××t£a§sèx#z<°+A¥ý”ö³-÷³ù­Í62t¹­=ááÄ„š oKÐM£Ãý§™£žzB¡ž: Ö+ ÀaÞ^‚6µûQº€6D4“!)“óBdT.: | ã¢= ö3ù%Œ]r‹Cõ«ÞAÃãsÌ“lÈ]¨fI¸,_Ï­8ïfncz¤N€”Ÿ+˜Jɦ "„þj?Éz¨Å¡BùR{jÂÔ”+•‹S€`ó9Ì"'Sè¸j'3Ú¤ˆf]¡ejW±›CoÝØÏûØñ¨Ð}Ù#:µA— …ß ©Ï´éLçCª´âôX@?g”ô€¦Ðþâ-Ú­Ô ÐÊø÷·"‚)í§´Ÿm¹Ÿ}_ÊQðõZ~C®¡?s~Þmèð§„ ½ýŠàLë“\®“«&‰ÑÇû‚ßÖGj[øž4W„1&÷»Š<:¶¬ú5ÍeŠÚw¥wÏ[q¨PþÄÕŸaTó>]íöÌÛ1ŸºÙ‚œh" Å÷êI®j'¦¼×"{¬³¬1–Ü<¹ ™g&@¦ýZ½Ÿú".ne å <-ÙÅÁ"T×£#d<¾,Fp+8ÁÔ ¶‚î‡×sèøs×Á«ÑG ?5CÄ rºÏ½3 ã|c j”؃¦l8ÃL}°®m´ >¹É}˜BûgX§@Á¸]€nê9†.fJû)íg[ê§ä—köF“µ€JÎõ£ò@õÓ‚JÎï¯Oþ•œhmƒ¬óëxTr~ì–<µó ¨ä\(á/PÙÑC%çÅÓRÈ]ëE•œû^#J+í8TrîpÆ*æÄsèßæSø¤ ®Vq¨äüÎБ0-å‡JÎ=oËÐÉ…Ú"ôoïÏWèN_=¸OPÉùñå;ÉN"ôo÷oŸ eîõýÛ÷Ò~JûùßÜÏÙuúôß\™'O'ÿ¡Ëw¤;.1­“ácf(Ú̽ Ÿw…1Ý7Œ¦î³u{÷‰œW—ef_]¡½ytã©S°UÇ•¹íQ%ÌÎT%" fMn¨P~•gàÒEŸG=«ßÁŠcªÌYgU¡¾¡€ v½ëIsËt7õÔžÙΡŸÕ@“¥>Ó;vp‹wTGe””ãP¡|š½L!ÏÀŒG§¸ºƒ÷„Q̽ï•)UphãíNÔ9Ó•9Â&|†&èásƒ :ÃͺŸ9" +»z6CÈ]¦^߰Ĭ„Cå3^BáÁÕL¡ý“i{ä÷Š Wf–’ÆŨÐ{i?¥ýü_îgi­2•¸‡C_-U¥u_G2•ÂÞ‚¬|zÞkOŸxŒfö+Ï"¿ÒPÍÅõ¤ë¨]ûü%D$âѦJyº·ò.‡îss*UúÓq9tö‰C…ò§ë4AÁ¦&5(û¾Á¥L½ÄŸäÒëXæøK¦`7\G÷6¿òeóÐ…Q¯àÎm‚v:­ë?pèáƒwÁ·“˜)”ï£ûjп8G+RvƒýÔÌ%ç´hí¤a"ôÓÌNtdRjz·÷*piUb~÷ù›–¤_Dƒ’j¡Û¹H‚ í7T×…Þsºóè¡ áp3}SÚOi?Ût?Ej´qÞm1ÚkŒ•¹‘‡OР˿ëðè^¹.TíX‡òßFBÓv/ýR˜C˜Ó¦«Ó‚I‰è(mE:ô×'‚¾Ys ®íéË£?*ÒÛ÷ä9TðçËóöT‘$pèÀ{òTááPfàJ_¸£2‰G'%ù¦•ÌßÞÀÛ©±íQvàp1³©à-hÈØ‰QÙçàfn4A…ò}U<s²×òè„QסoÊ &_£Dm\;*wH†æÚ fÎ;Û »ƒ:ò¨h}H¡B÷½[CëB9@O™Ç×¶tÖÙ2¸~­‰Y|ò(¬—UThŒù-¶2„G )PneJû)íg[î§Ý1Uô!_„^£Bf‚^™­OS\›óÑ«¾]éìø­ ~ñ ¹,çÑ]™ŠôÔTs¦Nä+Ø;m4 Ã·…@÷L‡# â`Ìüõ å‡3…òÉP¤o–ì£*î©c•ƒmìy¼f¯âÑŠMp§Â“)ê¬îÿ– _œÁ£¡o˜“dsÀ§þÓâl$(—Æ0þÍx»C}yTÍéÜéêÁœó¯MÔ0ò:ÈÕ2G%5Ac—Lï'É ûŸn¡‚ý|5 "Þ4Vq4¹T2MŒ6B­ã æ·ç# j¯>Shª¨+µMYÇ¡ŸëÑCÇOTÚOi?Ûr?F*Q×òh‚R ù#s™òƒ´h¯¡J€nñT _†3;ÌÖ¥ƒWqhõê;k!AKO‚ø)Ž€†|hS¶_ hiëo’°³ÞK^NæãyT(ÿŸ¬ÿü=Ð2'hö~®GgÅ1¿ŸQ¡ïöëñhÓÊÎô+Dr¨óþmЙ¿BÐÛ6A |1˜iãÖëgˆÑÄ1P>®ŒC…ò-R–£S·÷çÑüÊ´ãxyæÜYéÐ- ú¡›¡g„?³Vµ›Þž>£ŽL-çP¡ûÆÙ£!/º˜ ó_¹@ËsÌí=]¡%s‡zøl€_ j<*´ß2²=U­èõ˜Ë Ý°…)í§´Ÿm¹ŸI³ä¨ìÅj‚*&ÈÐg7eíö •™so@tðffkpɘÙ4ê)$®™ÍÜÑô”Ls7âÐ £žPõÖ”G[ºÿ€fw¦R?=Ê÷êFP¡ücóÛSÞíAƒêZÁ·ª ÎŸ»Ð"ërjrL‹^5ù@Ъ†™ "B¸Ì„€±:ºVç:Äëþb^¶j‚ÁEöL¡|#_kÐSÃÏr¨R•ývМ¹ñ¬¼ùåè–™ú`¡ÙƒÙ#hTÞÁ£{^TÁ×åL¡ûpÊŒú¤´QǪmgzÊâaË£Ûlß‚»Kw¦ÐþóC™ü @[¯¶ƒŸêLi?¥ýlKý”ü®m ¢¨ä<ÑæDMÙ ¨ä¼Ÿ\—-Tr~dÈ6ÈÉ[Σ’óQŽÁXa- ’s¡üú5B“}W@%çw&w wÇ;*9›{]s8Trî3ü ÀÃÓýÛ|¦3´iÊé¨äÜãÆ'Òóùu‚JÎW,Ô¡} opèßÞib Ú镜óFÿù=M.šCÿvÕ²c$àgnú·ï¥ý”ö󿹟K :Ó³_Çú.t«Y>Xî `Ž ¹p{é$u ðÅÈ•Yܧ=•»z‹Cƒm”iLç®btG]$ÈŽÔåÑì‚ pºkO¦RÂg0¶ºÄ¡Á ¿ jEA…òÏ ·˜É*j§D!xd0óפ­ðúÕ\r6öòe~rYµ.þµ¹ï ޾¼º£a=,,3ãQoËxúÕ†)”oN ºßÔÁì,}TÃTúªJ½rh¨–µÈµbfž; ×\$è’ø0qâ긟W¼Rs—phư¢e#3gˆ6í}šé4Rjö®$¨Ðþ«V«á¬–5A÷Í¿ö¡bTè½´ŸÒ~þ/÷óÔüL€…Ë >•.a¶;ÝVöáÑ7çd餔f=õ[žŠ«ršaûRcûúÚê|üÞŽGú·€¥¹?‡–Åž„tM+@Ç(Ø‚£y4A…ò—¶Œ‚Iy§9´åÙ è¼ã93¾¾ FŽñâÑõíh`»¾Ì)ûáÊV+uŽŒ€vǘ[”Ôh6o/FaÊ'ˆêg¨P¾ðÆ °2{IЧEI°ñ@%Óàgv\„v; NoW#è¢'!`šŽ¾°‚œc8Tè¾~l,( ˆàа´]0äÝNæPŸ·p¢ÿ4@«]÷Á"“eL¡ý)§¶Âún‘ª{Â=Š˜Ò~JûÙ–ûY3x'D‰üýíG¼f0æw¤Ç]¶shuƒ2Uù2TŒžÁCôÛq€*_Š„Ÿu.Ìms~Á«Ú›"4ÿj-L*¾LPûÔI0ô“<Úù߃i·™Bù}zAÈö<ºJk Ä]Ôdj,R£ß¯rè)ymú+ÛBŒ†½H‚¸‡9ôé¤s0(ù9346yw@#´’ëx*”ÏR}(.!踡‘ðajóŠõ¡ è ø‰Cì™þïr`ªæm=Ôå1ÔõÙú?àA<ìê½—C½Œ`xus›s_p±4´jß#²˜[!B…öoÍ΃Âî%ºþΠ Li?¥ýlËý\T3ÒµÇúFw ø¤92]*Ñæ;‚Ž›'GÍ\gÞôu€O¹ݤ® åŒÚœp TƒÕ{˜ ÉrÌs™ªtQÔ.1zãçhq € å碌 dÞwçQ·‹)àgʼhЉš?@PmEyzÒ£; ÷ªK¡ï·ËÚdü®;ma.Œö‚¢­~Ñý§1…òyo‚åÅÛ ÚA-†š¸3+*!Úh2 ‘úùàS?—úû¤tp@[UÂ'£3º¯·5¨¦qè¹$°9~ŒY§!•x4½LÎ^ÈÚo·ì9ŒïÀ¡É«A;+X„Jû)íg[îçÖ•v`êÜPWoSx êÌ\M·[Ùð Â/[0/ßQ‡9÷·q¨UÝHsïÉ£;CáÆÝ\‚~^8LWՉБæwÁ @¯«¶‡ã ã9T(ÿè}§`{|/Ý?^ ~™0_GÕÂ葞€N¹(†Z>€™÷ô%”/­£í-_Ýw/*âþó{À¯ŽÚgO+X9 BùÌÒü!cyµ]öh¤Ìv@e]÷Ãݲ™€ní»®ÏÁÔº• ®!¿ *º°.f§3…îûí=ײNq(ORal—3Ìת• ò^È£åI*´saG¦ÐþWÞPýdA5\Þ+N2¥ý”ö³-÷sw'-à¯þ&¨î 9xdP¼½î*¨ß@Ÿœ: ïnücD»=¼`:.7½ ½zÍdΛ¸\{èr¨Çâ(Pé“Ã|`|®¦ÍçQëeJ4ÊG)”ÿzx üŸß[®Ÿd†z»gr"t»rìµ½LP¡|»Ò¿EÐi‚Lycmꘑ©*´ZñÓ±ª=?G Ð7K•è›~Ï94qƒ*}•“IP¡û¶©rÔþ]®õ|!GEgú2s<„±ç4x´yïOˆö áP¡ýQ߀Ÿgã*»ÕèFõ*ô^ÚOi?ÿ—ûù¸º`Æ^µ7{1[˜GjÐK—GÔãEG*{á Ó¦ä&ØœÕ4êã!85”ٵâ!x$LbŽÐ‹Ï ©L£@66Ók6Äœ'¨P~Eƒø³(› =¼.Ãâý<³]ïiakÀ¡7o¯„ùYL·-ñ5,š Š'#`ðÛn̈;`ZêfCö:J»&F…ò…VIwe@µroÂùݘ»ÿûu•S߆ < ™2ϳ;óÐþ™eŽÛ<&nB¦Ì¤™hP*MR©$Ñ´÷Õ ’&E¦%ɔ̼÷ûáü¿k]ïZÏ^¾=OëÚ_~εÏó £á*¬‚v¸]Ÿ}ZÄ­ÑÎEÎæÅÜ#ß–#Aw$Wîþ´2 Ã’ù5£É!1†íý×dÇ#¿ y¦;#³û–`Ãô\¹ýÏjšb¤î½Ï°8¢RÕOU?ks?uJï!èêZ‰ìSç!Öèr7>û‰»½AZý(Aÿð™Üéõv rñoFXÛ +¸Ã„¾vKë×EÜðBÔX>ÛM»(H£–Oa;+—Oýx¬ÿû:#‡®BÜø›\í«®(YõQ"7ù„ሥ¦‚œtËV¥#ÈÏ:Žxù¨»Dú ÐñÍçV ÷alÄ•Ë×LHÄÌ›Ý@õEH`wn—‹xuÊ”›8ß«>/àêL®ƒS)íÒ¶1ö[÷Sr÷wiv_S(×‚Ô ·hÏÈ~A‘8ÛHdñƒ¸^÷#åöëÄAo³ Hõý›ÐËȫꧪŸµ¹Ÿ×-ãÌÙ.” ûöÛ" HBÕŠ• ™/Æ\Áýâ6m‹ëHdÝ¡&(X÷œ›;H§ór¹5£w!x”‘‚4ÿ¨ch­µ[ …y¸ >¤ü¿ã<‚&^gd_ã³HY®àÚ5O‡Ùüº ²nû‡¨\ž+‘CŸz`ÞMî¼gça}5[ž\1–/²ßÓxÅH¹|ßf{a°s#ÏÚÃäŽ67KX×£¹у± ª ÷áæë¸{y™‚¬ªÓÈ0Ù<["åî·¾Y×°KÃéŒŒŠ¯cR´‡;Æ×ŸÜȰž¦øpÞI"åö—ÛµBvZ4#7—h‚êK¤ªŸª~Öæ~FÏxЏ+Eò^ðSè„¶N$;ÏÛãŒ@ªéŽC}½NÜ.Ú'-öPkö¥âIùî† ê¦Ü½K¾côì\ÓãCð;®Èš-½ðîL™DÊå¯3× R.2Ò«‘+vÞ:Åía\‰ýJdÖªÐßa*’?×JŸ(‘¡=r1ÝÅ“ëd‡Î³/2Rwÿ:ÝD"åòŠ›1¿ÍÆu_€f¶ ÜÈÁÍÊÝ~ºÞù ¤–NcÃM9ôtª·.)w¿å5Ãi]<©÷ã'Þ7ãîÙñ?ÖSmJÒ½¢!Wn?ZOŇÁ]dëNþÈtXÂUõSÕÏÚÜO#£§/×È;CKðW‡`nP^[ Ëf¤íd ThÅÕ2¬„õ_ ¤ÆuC÷Ls‰ÌŽhjX]º…ëªmxÅþ8#—mÈ€gìié­mhô®µDÊåouy6Œœ¾Ý»†Oç®ÛýÓšs#F•"x?×[ó –-ï&‘kµKaµr¤©•€ [ÕäßKä}U"åò™~›¿7[Y¯ØýÕ3rʤzØw}¸DÞxÖ'jЏï"ãW=}ýÝë!¹2n4)wBÄ7¬N ^¬Ry>eî+,vRy.—tÅFŒTžGFå ~ÜF*Ï+Ã(O`¤ò<àÅGhœÌÈ?Í÷%i Æ‹¤òü‚ýxT¶é¢ •ç[=ñýäŸÞ/ýýæy/©<Ϩÿs=/1òO÷—ø45ü& 䟾¯ê§ªŸÿÍý ËmløŸ´µп´!#ÝŒÂH½9-ä*pÅE"õJÅËCÜ}ýCϳ7È¥I`ïp‘_ÌæÃ´C+¹þì9(žMçž[ýsÿê²

        ½$r÷ïp«Óq¾ŽÐKÄuîXŽsvýäòS ßM$Rnÿ¦z8™ÛW"Ok-Ãò×í¤Üûª~ªúù¿ÜÏ—q£Ð]GÉÎî_Ó’Èq÷o¡nåîµð<ÌYýI$çíµ¡ÝâÈ’êñX“)‘«Jñb÷¹°¦¡°ë¼D-€{Þ—¸Kž¥b¶«‘‚”Ë?åJþÛä5óŒÌýzÇ–¡´ü¹HV6…ùm¼òqW7l¬rÚ3|öW0rLþ'<½8‚»ekúnÒ)ûõ«»qá.düK8Þ‹IÛì\ôHÖ“È}ú…ðh'’1 ŽcôíEÙáþUôwäÊݯúvŸöé8f'ªµºq_øÔ7ÿ:‚‘-¦ÜÇŽêQ åö_^r‡³f(È€wЩêÇUõSÕÏÚÜOímãñ;U d³!…˜dßB"+ÜÅbŒ”»?}à˜Ž×Y?wNzép5cÝpF×[·Ç0XôÊd¤Üþ©jÿpÈÊæZ†ÙÍ2RÕOU?ks?««â̱k¹ûÔZÔ +ãÎÙƒñzŒ|µ- Wšr…zo þPWA¾+©g¸wÛ~‰¼ª“€©3f‚ü9Ååâ_ÜV&bô÷:Ü £?ºL"åò_‰t„³o #{>ØßÎ×Ñ)—ôžqõWûá}õ}{anV-‘ûüQÚ¡£‚<_šŠ÷Y͸íü@çÏDR.ßÚ°‘éŠS˜]˜ÉU¬¹ƒ£'1RgP†o±âÔ¿‚_g¸»^‡£ÿx®Ü}ûQó ×ª>È­;§a’ï'Fj·ì{E7‰,ªgŽàÂÎ RnÿíòJÇö©æEøL®ªŸª~Öæ~&Ýýñê òjœf[êpãÞIxçäÈÈ{­£ñ¢Ø•+n`˜ö®wtî'ÔýTdäÐn( ñfdÊöv3p®D:ËE—sã¤ÏúƆe}â H¹ü¦-6áù¯ãŒ\énŠÉ6“¹z;áȰ0®é†Ý˜>l8÷ò¦ðÒNA–éT" é¹D~[òQ†ß™s ê åòÇŸÇÆ¢"‰Üy9¶aÅÜv}ãÑ)Ú•‘ý-"P=é8wý壈îœ"’6«Î£ºã ‰”»?ðä8ô zÄÈ;FC4KãŽØš€6f“d½™ßPmÝ+·ß¼l6ØtÙrˆÂ61Fªú©êgmî§Ëg4ø¬§ Õò|ðÄ£+·ùˆ`¬t˜‘¾§`}ÃŒktê:¾~ ²Àö8Šž ãîêbŠ8©‹‚œp;ÓÌâúuÊÂÈC“Aν?ƒî2R.¿kòL?& î.‚ZZZ¹gßftîÒ/‘Üh½—§Iä¾)j†W'èsmŸ«~ÓeÜISðm¿¿Hnv:–ÝJAÊås¶HÁšQ¹)޾ïD®bp¶4·cäwo”¿šË5t úü#‘‰Š0w ”»ÿ3a®j„1òuÎì½àÌM´ w+J$“·ÿBõŸŒ”Ûßɪ't‡Hä¢G{d˜‚TõSÕÏÚÔOågn0ú è¥ •çÛv¸£´¼#•ç]}g Ýï÷ŒTž—ô­„Ž}?©<×ß= ¯)Hå¹\þ“–"úî‘Tž‡/> Í71©<×Þ"5è#•ç÷&WáŽZ¶Dþi¾Éë‹pÕû´D*ϵœÜ e¬ÅHå¹ma*>Õs`äŸÞ7¬éxõÕŒTž¯×HAX ÿtÿýoɘÙl†‚üÓ÷UýTõ󿹟Çjþ'{~ÌÅ.ë­"ù47Þ#ɽŽýûwô‰\âà 3?¹s^b½ó[n³ÂŸHpêÃÕ]uŽÁ Òõ·6oБÈÕ Çöç®×xÏ¿´AÊåOzúö+%‰,þ§{V»s§þ*Á÷GFŒœV/.ã"¹s0[EJ¤õïýhÖ»£‚4ŠÎÅûçE²UYú¾XÅH¹|Eni¸ª7dKÓH´l9’û¬¾nìÖV›4.bùØÖÜqz¡ò­>ÈÞuÖÀ´p#åî/¼º÷vvÈ›Îë ¶:z¹°ßOh6ÜÆH¿€øÇê‚”Ûo¡ïŽûkܲÇ.,2+I¹÷UýTõ󹟻kò`þþ¨9xE:)V d¡E,4Lë(H«eY¯TH¤ý¬˜Ô‘1òxÌ3De½å>É EXëíyÂ* ÷v™qµLw¡ï%=š3†Âè°­DÊå·Lý³+“$²YH * Šd·‰"Ö{1²}I ­ý6ô ÇІ‚‚œ÷ÿõæîþìƒn¹hŽ'v5ø"’rùÖÎ÷Æœ ƒ@z$Ø`xƒÎÜmõ®#ÙLGA®t{cŸ$Òâöz<ˆ9Áíl…Á5Sr÷Ïü™Ñ­DRÝc=b"½¹>˽‘u¿È³ÙHìÈH¹ýc'!ûíF‰ ï——˜\U?Uý¬Íý4ÓÎÅÑí ´Öºƒ>çÔ9øÓ#„ûÇIäæ)81t5Wóf<ÊG·Ù§‡ FºÖçN}–6·sDòQd¾™Èc"\ŽR}ËêÖo,’rùŸU}†þÓÍdªm ò+¾doïcXÎÈàÔ½pwaÜ‹êj†×Âó%òj=MCé×f‘\í‡nvKäÔ1y(ÝŸ’HÊå3rZ…‚Eõ@j4ŒžÎéŒôÚùnÞ—$ÒVó <ÍGrF¿EAù9n÷%Ÿ±7v#åîW&š£"¾T$[l†£Uk‰hiØwáš4µAI¦¶‚”ígÏl‘Í%òɃ|,ï×B UýTõ³6÷³üßßÓMz02¦Õ Ì ÏÍoô7ÙˆäÉKU˜z)K ç¯ÄK#wF¦VϘãDòųkÐí¾€‘Ï'EÂa´5÷É|t5è²|Ú, W/`¤\~Í­PÔx #·Ý rÇÿÜŒ1m>&Ùá{9ÓX"µ®«.Ï8ËÈÓR9z´Ô™þL›«v0²g¥vä áÊ勺4Ú“ÿaäüÓ}ñûŸÕY<æ;¬æNM®yVƒ‹F„ƒó[ƒ|»} *fÝe¤Üýßc¶á†½D6?¾{†/çNX˜ŒÎwú(ÈÇkзy¾DÊíír–‘ÆŒ¼p.z^Î\U?Uý¬Íý´œ”³üeŒÌ­“‚Åã¶qM¬^bÝ˃\=›{H*¹ßn‚W”Dþr9‰î35¤ZÑI ]=‚‘'‡¸¢Í¾#dðÌÅ(\\!‘ûƒî£ÎÛN R.ÿÏšg‰Ïf¤Ôárê~àvîr jÞ ék„•Vܾ‰x8BùùþZ>€;ÝÄíë7—ÈÍ7â‘oäÊ•ýýº¬?Ôâ«EÒöÙHT…¸Käœ]¯Qz‘‘«&ãPç*îpÇ)xØ·­DvÙæ… m¤Ü}Ïcû¼ð D®lyâRîUk Ãz}írñ–Jœ}­Rn¿Ásœ¯YÉH“Éÿþ?G,HU?Uý¬Íýlj""#ñ#ýöÄâeî…u°Øðœ»Î8y‡ÞrMRSÑó–‚l±ÿ2“’%²¸¯'’Ñ•ë¹&½o;p××Ñ0”Ú´ad«A18òaH¹übn¡´K}ÒN Ñæm“„ÞH¤NBò-R¹–ÓŸþŒ‘+´{C IY·Õ=Ìýª%’Ë ®!ã¤+#åòµß> áO%²~ú&ÜšÕLA.¼³ AÎ2ŽDVj®_óR˜×Q™sêæ8/H¹ûÙ"³Å‰lâæ€Ð„ Ü«/ÂÒÈçºkñÏ5®Üþ¤TW˜9´’HÛAØ1æ8WÕOU?kS?•wóh,Yu˜‘Êsë#g`3¾„‘Êó–Þ5ˆ4ERy~~ <ã¬$RyÞñàùl8#•çrùwuˆBîCÊó¶Ç^¡Å&‰TžOT Ń.©<·™çëºù§ù|Á„y}¤ò|looHê •ç#ßÞÇÐ:º ÿô~½!N˜'ÄI¤ò<àÛ_HîØB ÿt†q‚M<%òOßWõSÕÏÿæ~~i«aøŸÜÓË≦ {]¶€¸W;¿ÚkFXIäÙX_X} äz´Ÿƒ/s¢ñeZ;yzš3Jæ2|%¹ÁyD'‰ÜWyÍjÞq¿¯} ÿ&K¸rùƒœB0ñ](#õóÃß›Ns/DÅàÞå> w'¹ :VûmÚM¿¿•‘Gü®¢`§73ýœ®p“}÷`l‡^)—oªA9Ô²æˆäÒÂr¸Ú› äͲCpx!‘“¸ãë îºèè\x‹‘“Z…"ĶH¹û½¯¤ÂÙz²D¨NÃC\íùeh?ç2#—ž‰Cð” åöOªg ”{ ¤ë;l{1I"åÞWõSÕÏÿå~ún_k‹GŒúÖ{æ\á¾ßì—$²•Ž„FZ¡\§/IØüw/iûé5¬³ª%²E›`4íÅ͘‘Šâ1ÞÜ‡Ý po_$#y¹à¡W.ÿÃkÞÐßëÈÈéÇ1zÐrîÐØUxî]Ì5¿>ï÷åÞÒõƒÂÝ‹Ûûè1dMcÜÛ.18iT ‘šñOaµ£@$åò•>|Çâ>Œiþ Z9rë.;ö—¾Häþþ"Î,üÄ­:?{+Ü0ìoT‹•H¹ûÍ®#¤|€DnpbNG®ßG¤oÈfä@#3ĵØ!rûëúžEôƒt‰ÌòNC›ƒ·¹ª~ªúY›û9Ób"îl9ÌÈÃô6 ã® º‰ÊùNyÓ¡³FsÔ1ÜÜŒ›¶ò7†jncd—k÷ µ ­Dþ•óžE‘¹kÃa|~:E"»½I…ù<®\þÎØ®Ö‘ÁºN8ñwš@ö0u›ýÉ1/–Ã'/]"Õ·CT{ÒB:Œ=uFKäò9·0[Ç‘-*<á°Ü–+—/ìá] ð¹ÌÈ,ñvpßM¿ƒá#Š$²cÄc˜ÎàÆäDã„nùéâw|¼-‘r÷ÏæÝ„åèõû#uº»ÁlÒ%nÖ9ˆ;3J" «wbØÑn£ (jZ+È ½_ãCR‘DŽOƶK%ÜgöïÓ£O")—oÕº«È6¬ÒèJÆ¬Ç ݉ìkn)lØ×]çD²Ã«08ûcd®¾ôG–&r÷{oº‚¡é­AZi\„µnîåKq¸õ4Š‘n»Ü±bÌ ®Üþpí7P»Õk9üñXç1RÕOU?ks?ë'Æc§ÿIF޵½ ‹‘g¹ËöíÁ®×Å98Ô:;+HeíÑA" /U ¤^F.õKFÈP?na¤ªB¹&Wb…Ü¡½|À®1 åòhséÌ…‘ÃzØ dæ,îÅÎ>ðn®­ W¦ˆ˜·µ9÷tþ/Xmÿ!’«¿C𹀑S›d@Ûî·¯ífü®\¾©NîÐ-©ad£®¶X5(—ÛÀÆ zm7Hä­ì£9å*×°âŽ|ÏrëTФÜýbŸ|œÞdG€î›[rãêì‡ÉÑ€|auV·¼$RnÿNñHÔþÍÈö1N`Ÿ«ê§ªŸµ¹ŸZD æš#Ü‚aÑù WgR$„·UׂÿýÜ4 7ärº~Lfä¢11¨qüɽÕÑ{ûôå2+ŒxöD$]×ÅyÈ—/o!Ýz–@ÊåÿKý .~~/ûÜ÷AóÒO²jèCÜY%‘¢ÞC÷¸wp£gÐ{QÍÈ™¾ ØœÖdä•-Hn'‘ÃÚg£ÍüÆ R.ßé›ÑqüiF.¼¸ Κ#¸SÎÀñÙ3‰\ZÓUÕÜàš›§©ÇÈy!Ø·ïWî~×ϧ´¿9HŸ‚ض_›ûëV4 ôÊ$ò^úc¤äÊí¯°X‹5¿²§°%¿l$RÕOU?ks?öóCáD/FNjìƒùFG¹…£Ÿ¡ñ¸i”Yõ&~ÜFGŽ¡Ù•ûŒ¬9±¹Kî è”\•Ȇ#ð°ï3îi‹°ÑŒœfæŒÄS")—ÿkÍÄè¬Éß­±æ±–DZ^û“ï äâiïðõÆNFŽéã†6w´@N63Ã÷sŒlÿîÞô1ȘS©øÚ¦-H¹|9ÝæÁz9aÊ|¼j÷X$?fâý_¯$2Âó:¶NæA÷÷KrðC/<;å&‘r÷–¸ b{C;zÁusuîÁïUÓJ ›Å>@œs#åö÷˜áùZ äyé.tÒâªú©êgmî§÷rìŒÚÃÈš©.Xh=[tÿÖµ|*’>-~£¹ÏclõïÏõSÍ$Rgžú_{À=5í îì=Å]ÚëÔvOMVý%"`ÿy‰l·ùræmH¹ü^6Ž·|®D†ßvAêuGîÓÅ002안ð ³.΀߯¹d_}3 0ö”È1·ã¼Ö)FžHÙÓªB‰”Ë×lðJDî<(‘ºcw fR2weÖúîåÚ=ù€ê¡çErÉãL|è)‘‹¶•¡ó*§Ñ¤Üýäh[´}ò…‘sõ¢c‹WÜq»CQYÔdžëVh$„0R¶GšÕÐlë,‘5½`Î-MFªú©êgmê§òZsú¹}©<·6ý„™Ïý©<s*Cõu¤ò<ýÈ3LîÆHåyƤ‹ø{óF*Ïåò·ðÆ„ê³ùÿåW;ƒ£¿¿2RyÞVÍÙÍë(Håùèê|\NÖRšoöIGXާf­ùM ÁC‡|‰<4$Y?¯q³eÂçÅFÆ«Ç`[ˆwIá[Œ©ÜÀ•:Cj‰H¹|+z%àÛ¡Îùwußü.’ÛNÅ¥&í_Çáýc2óË{è³.Œ´KѤé/®Ü}ÑwÂ/]`ä÷’èåÍÝ”u¯:pó69Bqë˜@Êí/j¨@ƒrm‰|pü\›ŒN$åÞWõSÕÏÿå~›‹t{‘Ô9»N=ô$²áOkÔýáÁõíéŽMkó¹Û›;áåxF¢ÚÆýÆ%’q¡ihÿ½‡DvùpVó£È;'¾¢ú›¿ùpT,úïÕ)—ÿ}þslݲ‘igb®sWýÛ]¸f8HäëËO`=7E$#?ùãî›ÉŒ¾Î¼²÷÷¹(¿$pû<‰Eõ¤ì÷¿zö½÷ÉÝÕé‘”žHæèy@mKÓÑä,¤7“D2ö¡º¾vcdæÖýhg2C"åî7×°€­¿-#×5Û€še+¸þ Ž!lÕ ‰ ˜‰·ì:Wnÿæ&iXÐ[‘oÇ_„Í s®ªŸª~Öæ~ÆMY o½iÚ̆|¹Ãf„àøün¡{úæ}æ¾Nqƒßã\µax`íͽa˜Œ Ï1òêÄTtíÆU›¼íoe äדg°7GMAÊå¯î—KõLFyqž1ϸë¥GXó3\ GnÉE¢û:Fõòk¿í"™¾ñmõ$²úÞkdí à._âuY#åò™ø¦ã蛯dn£kè2'X cfúàæQ9§uìžàZ ŒÆc|n÷%`uµWî¾U³µ8V(#_j™!Å£7s@¢»œ”Èú.SÏÀ”Ûßå¾^œïÊÈšÈcpýØI$UýTõ³6÷ÓõÒn„ÝO’ȯl1õå#»p)ä6?Z†îc<¹›¶§BcünHׄn»%’»vœÅ¨ûVÙp®úÜ ÉrËJLÚ3N"®¿âÙŒ”Ëo9#ZÝ?22ÿ[ f~áîQ Ñ®›oþÞèÉõ½W­¾yns [ˆdtå ´ß22!wŽ5¹,’²ßÿ¶ ¬Y÷] O탳…#—VFÁÛËM"[i%¡nsn~ß|øMXÂÈ£Iç×þ8Wî~ÿº¦pM‹ÈÇ_Mà×kwaÇ{øæËȤ¡ø°ã3Wnÿ»è“0»|P"®Eç×á\U?Uý¬ÍýØÐwÖHäë^È<¥¡ “×}@щ"¹Å» ÛÈüEÙÈYèÆ›œû-£ywÃEJý$ò‡S*Šcës‡í´Ã±O«úëÄ)—’‹œß3òq”;­ŸpGòÁä’Å\ç®ÇQ>;F ¿¾MGÝ—á£IÇ’±ûÊsÜæV&‘ßG”¢ùû:\¹|7Ü.!iLOFvÛ†A¸L³PºQ"‡•b–n+îäunèõÅ€œ´öú›$‘r÷|0Õ”Eñ䑊•ðˆ)’»`ÝÅ‘F+Q:à­HÊí7°+ÄÜ}$òç¯çˆêS_ UýTõ³6÷ÓÖ>»µdΪôµnÂô~ƒ#1ÖŒ4ò¦kó¸Z ሹ0†;Aï,Œö]È©ƒ³Ð9cy¥áè쑌ªÏÈé‡Î äE¢@Ú?xˆîMˆd~ËG˜f˜,ƒú^ñãjYþ(VB²@ÊÝ·~¼ ã#®ˆdiæ8¬/‘ýï»`ÝÎ*n­ëø°µž‚”Û¯éuaïÎ0òHVr7?åªú©êgmê§òsÇ8yO+Håù±µ™ø1W¤ò¼l±/ì»6I幩Gò~b¤ò\ú¹•©<—ËoòÂ]#2Ry~P7 {K¤òÜðûI,2L •ç^:ž°>ÐK"ÿ4ŸÇ{8Öé*ÊóYCŠP¶Œ‘Ês£§!HýÜ”‘zNÀzLÈÓ—Håy¯n`2`‹Dþéþi÷±±#ÿô}U?UýüoîçùeñŸL^臌•³$Rgù9d/çN)¡°‹‘£.⎣7±£"ÝýE²Îƒã°*ö”È—oßcìl|Ôú!zÿÙyÿ¸ÿÂ]³ö,‚MGpåòÏ^Œ€pFFׄÀÂ>ˆ»å¾7B4$òk÷`üü=‰kgç¹×–0òÅ4ìk;G ëîYÁ½¾r3´Ü±O£L"åòÏôBÇñ/Y<ö0Ì‹Ü)ɘãÐV"¼JGô‘ü=UÄÉï.ŒÌ¿ëŠ?—råî;= ýÝ“ È)õŽ!ÙxK"¹p{†Öí*‘ ̲aÕ#&”Ûÿ!¼~Ã{0ÒÄ6 aóÎråÞWõSÕÏÿå~Ö8„akéj‰|~A]×p›ÛøÃÛØ–‘óOzÀþ¯éÜñˆÂÊ)Ùysc."šDqcN_Ầόœ¶ú0²¢.qÓCPS§®Dþõ:=5MER¶Ÿý 0õ`Ü¡'12swõ½t4œ,‘g$#¸mcî«áÝž;÷›7lsM#žÂ¼$ײùKÔ,8ÂH¹|îíƒU3'F*:oÆyöÜ‚PÛù-|×4 F.»òÊh{8}ï.‘ .^Âì|®Üý¸ìc0·ë%’Ç^8cþj®ÕÞ ˆ¥ÓÙþu(ÜmݸrûMúø£aÙaF–ŒwÃ)½(TõSÕÏÚÜÏ#A1˜3u…D¾1ЇgÎlnõ'DÙçdó…ˆ O$/•”a’†D=,GÑš.ŒÜ¹Ò#Ãõrá^;(tì$r{u–i6e¤‘ái,Üd-’rùù¹Ã¬õ8F¦¼vAýºÜ_Åéh5L$o_ƒùÓ™é>7ÓoIäÕ ¹ˆnÎM®ñ…mæFŠ‚%’ëI¹|Ñc×C'?Àì½i>ùç‰ä‡*ú” äÆWð=F‹‘Oë¿@ǦS$2ÿßïƒæîŒ”»_0ÑïâDòW…|Z”qµáþà¶ŒŒów@ÉeM‰”Û_\æ ËÒç"ÙÝøúÞ)‘ª~ªúY›û´SB›¥ÈÔæ)h“Ôƒðà0Ž_ÔãþU|Ó´s«çÁlÔ-F¶IÝ>s£ôBÔíŽDfõËÂM!ÜyŸ“p÷ÃnÕùÛÙœ‘rùO s†í½Ùî 3¬vÛ&Ž'"ìl•@†ÏˆÂœfšŒlYþ_éI¤Yé+|s¾-/S.`°SºDê·~€•f/ER.ŸQϽXqÙD";8`þønîœl¾¤ÁÈ•]ƒ±æõ5ŒÎ<‡€àF¾\¾Õµ¸r÷Ëï¸ãv¿†ùªê7îÀ~×9™ ÜͲË•Ûÿqinµi%‘–¯Óa…# ¤ªŸª~Öæ~F×IàwšY:(5òDÒbŽ?Fý¸,‘ßç^FÉ‚ ®{GÔÕT02»z5r;ä ä‚3Ñæc²H6õ-‚Óƒ.ŒæùžÜ–m¬Qsª‡DÊåŸá çîö"YÏÉߦ|âi£Äâš@Æüû9jL#7rÌ»b¼Ìcä–¡¬?Uq_-Iù†VÜ+|0h“.W.ßÿÍ1²8C"»Ž ĤÈÜ'ýñ«{OluÖwÆÝN$/µÃ­‚‰,Ü|³ž~äÊÝ5Ö/Ôÿ’HÓTÜ}î±rœIŽÉ™úONŒ”Ûß"tJÊrc¯ X>õ+WÕOU?ks?'y]‡×0g‘t쟉¼àfÜ€)ØQ”(‘Cç!ÅÖƒûxÈ&Äv_Æ9Å­š•që«)}Ûƒ‘¿†Àhè~nfB®O¿/‘šžo`RöQ åòfDU‹ÞùC×Ú¦pÍü1··§H®ñ Åéf-%r9óÁ»ÊŒÔõØ‹F±.Ü'sPãÐX"ïJÉÿ~N¼(’rùúúDÃØó‰Dþ³&9Éy\íx9ÿŠHv½Š‚ØÆé¬õVMÉh–g+ )wÿà?¾ˆŸ.‘½õýðaÂBîÕG±¨ÊþÀÈB7ìÏ‹çÊög»–Öñ0 G·;‡Ž•"©ê§ªŸµ©ŸÊþÇës²A©<¿•øͳûH¤ò|]Kê(Håù÷º¸xF •çO½Î£àÉOF*ÏåòwùŒåO—J¤òüÛ÷+¸ª®/‘ÊóåÛV!m¢©ùÿÍÍãpïÝwüÓ|¥ŽÙÈ ¹*‘Êóq³£a½²‡D*ÏW9ǧI·ù§÷›4 D˜õJ‰TžïY² cξ‹'ÿtãkWñbþ_ù§ï«ú©êçs?ù¿Á2îþiÌ/{ÌHõ˜û*›Ûþ« Vn¿ÂÝ<Åÿ,]ÃÖÚ}ÆIäƒÁè!xsÛ:Ù!*Úƒ;A CŒYwÏ2?Ä_¹ÇÈù·`gú#‘”ËÿåìKö¯Èq“žÂ¢ûߌ<žp•§|¸ƒ2¼7ãoîÂgp¬çcîô±Ö0¶ræþêö Kwû'’38Þ.‘rùÒ§_E›õEŒtÙr–£r¸Îi‘f’ÉÝ×Á?ŠÜ8ó ¸ëI¤þ¿ŸãV?c¤Ü}×Vy°ÚY2ŠÔÜžcç{äÏ„ ¼|yG"Gä¡úz,Wnÿä-g‘Ÿ(J䇣ièù=‚+÷¾ªŸª~þ/÷óÄø£˜¼'ž‘‹fÛá@¿³ÜÒ>»`uX ýZìù‚$‘ìô6 0µ•HKÛlt>óB$«ßEÝ‚d‰ô³®ÄêБ¼tN„Ǹh‰ô.D^µ“@Êå¯ ¹Íž.ŒŒ3ÈAæ¶pnY¡+ÌîÈœ{î˜ë ’Û-Ìaz'Ý€ì·`?Ê ¶KdÝÈÃ2mÅH½ªÓÓq»DÊå ~}¿Ç1òê gµòàvÞ|cœàžd…»ßr¯¶ó…åºB®Ç'KD½ºo@ÊÝe•‡ÓÖ ä8ƒ<¤ß÷åG¼ÆïC#DògÅCTÕd¤Üþ´yÅØ^PGâêâ¨þTõSÕÏÚÜÏ탨{ÉŠ‘§ÛYâsãf8:àHÞ&‰œ“é 5ËPnÿïY0š¼Z ¿]VàÇÏ®Œ¼¤QŠÓörw+’¡QTÂÝ|1ÁéK¹ý?"·Ù™¬ î&‘ÙSq:=P$EÏ{˜4"T"· /Çô¿òD²p[–/Èžy—`ðA$åòmÝc W)‰äÂÆöxyè…H6)´ƒ}§ãíqå¦7¹ê9Eœ(A;¯ FÝš‘r÷˶ÝÁ—íÙóémdNÊ”nدá"Yi|=7¤I¤Üþœo,U›%+Sýàiœ-’ª~ªúY›û9S}#êÑÉ‚ž0zÀ[~ '|<$òdƒBh=0à6Ùã¹Æ"ù|pÖ§<å:½¶ÆT·©ÕÐO‡]áŽv †‚E2L÷¾Ù ‘rù—võÁ[ç™úË U¸ûªR¡óz…™a+BÏ&J ž.E@¯ÖŒìÞ*ÛC¹•k30¸M¬™vﮘ2R.ߪGa6nªD¶±ð„ÿp{n¢w(ê•s…NIX2ç%f>ˆn!×»cà»Ï")wÿÔ¯[p<5‘ËïÜ@ŒÏJîìK×Ð0ñ¶Dª‰OñÏ V\¹ýAñј߯±D†'Árñt‘TõSÕÏÚÜÏÌ)–¨Ê&‘çqŽq»¸‹ŠÑóבœTQ„‚£ö)žpÕÝW$-&\ƒ¨Qc@ÞÒ¹†¨ç%²0£uÛ©scæÂ:JŸ‘ß½QéΕËo|Ï ý³ŒYùÞ¶«sý‡D£²ç ?,žC¹_ÞGcûÍ Fžèì Ÿ`_nÊ¿½JÛ]O$Û½ˆ6·/I¤\¾BO´[ê#‘——Fà…½Wìšë:éÜñ·ŸÁÃÇ‘ûÎWº;î"9ar<ê…e ¤Üý9 ®£RÓ’‘=O¥aõ<[®Æûh÷Ï0®¿×¿Ÿ Õ/påö1ó÷ lèqP WÕOU?kS?•]S;8,÷‘Håù›ü;°êËHåyÓ"6ص`¤ò¼íºÌ|qO •ç­Ðßl†D*Ïåò5·GLåTTž¿ò@û^¤òÜò³#¦>«ÇHå¹s«çžž*’šOÿJÜ>zK¤ò¼gékÜÿ]*’Êóì5þˆª8)zŠ_ &e¤ò¸/’ýµNÁÇÎY"Å]Å•ì“ܾ‹rZq‚Û#å1òšvÉ3kEh”ddî7D݈H¹ü-VÞAØ ;‰¼ÚªG«gs-¿#gõ> 1ÏÇô×k©=$‚÷æ?Œ[`ÏmïŸS›h®ÏÝc89§1W.Ÿ{ÇÛhd.2²¬@«ÏùÜ­œÑöóIî™—‡qo×hD,æÔãú„¹…î")w?Ò?fû³ùñ©ˆþ?r¹«œ® p_î7ç°ï»­@Êí¿×I„qÚF\ú(ÎEsåÞWõSÕÏÿå~k\Å×k"™(Æ¡:ü·gÛ“(Z3Q"ŸÏáKäVn[8ñÀq<É?2ȗȃžî,FîJ¼Ån‡¥çP=8P"—ª?Å…sÏER.ïúàúSK"Ÿ¥—`Ÿíj‘ôš‚}iÙ;C“¸“æC¦SK®Þ {˜]˜&’Çø¡~Ó©-¦¡÷‰B‘”ËW¶é"šö*fäH懽³¹7îÂVõ·äËUû0r¦ºD´L@•“µHžZ‡#o¤Ü}/õ8< ¿ËÈëÍ.ãm÷ÈèPôÍ=$’‹âá·7”+·ÿS\ ÔBÖÅ‘+KCuà·Hªú©êgmîç÷ ?~@$‹¸Ø{mäe\{`!‘×(Øt"÷Σt("2²î‹Ëøºò/îöôÓðŽšÌMÔñ„…¢µ™“–‚F{n12±ñ>[kråòoô ë®|4 ý.–àÆø·YÇÝ ‰û02{õ!hŽmÆ]÷Ó^îV¹D/s{%so΋CÖÖ"<Ñ+š7ìI¹|9NÇñ2ï#³l°à¡=÷ðyô¼yX"71/´9¥à†µôƒO|°@ÚèAÑÂN)wÿ]ëHŒëEL]ÿD$OŸKGè册”Ëg«cï³]ÙþÄn¬º;P O ƒAß{Ùò»5ñyÜækS´b?w‹ù˜Ø?7 åîO„áy¹Œ\¹ñ,tGdq­ŸÁŸ.Ü3>®ÐÑ\$e?ݾñ¡M¹Æ7é<¸ª~ªúY›ûiÔ^€ -ìô0 ~¬çX–ŽÒ.í¹M’ e/qµÃQÿJ„HÞ6‹‡çx/îÑÆIØÜ«W¸ä=Î9ñRna¤Uãã°?î#rù+‚n£ÅæpFžš} åæ×¸Ïæû¢zÔ ‰|ÝÓ÷¸ 7•A|i@>,{ˆ þ¶ŒÔÐóÅ&\Ï£¸šÞM"åòUìŰm'EÒw‚ ¦®$ÒaD.âæ^ä>ˆxŒC#×r‹«“Ðg×eFZ9ë¶\¹û3|°<7‘+gy¢ç¥Dî¹à3°™f/‘G_¦`Ô?®Üþ„þ'ѵڑßla²ÐX UýTõ³6÷³ó‡xìYç'ƒ¶ÆÂâ÷5nÏô«°oWÉÝí‰çïr·6I†ÆÒ3<W<¾ äƒ%PÓ/‚ûQ#½M Éî5Xºm¤Dþõ !ónrùæÇÂÕ8‡‘:·. fPwå²<„è]”ÈN?‡ýT}nñžT¼ÕÈad»_þÈõÿŒì’ Ï:±)Š/ðìåQ”Ë÷÷Ì£hññ¨D>îçFőܸ/`µ!P$û\-…[]O¼ðâ<‚D^ÍŒÁõb®Ü}×ÇRÅH¯öÎèèÄÕ}ù;—?IÍÜð¼¶Œ‘rûh9á÷ys‰l€ ˜ºïWÕOU?kS?•Ÿ+–1°^÷X •çM}C°ü¤@*ϧ ¼€3‹{3Ry¾I+—7•Håy]»`Lë)Ês¹üžŸ…wf&#•çþ­ÊðëegTž_;ï€7®vŒTž‡oÆÔ’ûŒüÓ|ÓÏ¡ë܉Tž¯½ö– ©<Ÿ| †Ã[IäŸÞÛv6w<©¹ôÍyômÏ]ñÆZÓHdëáÈØÎ•ÛÜÉ:!Œìûʇ^}H¹÷UýTõó¹Ÿæ RÐu¥##k¼aÒɃ+:Árq2÷DKw¬’Ns3Ñ9\3‘¼¯‡Í¯rJÙY l{†ÛûóI¸6rI›ì¬›s%‘ܳ! n=æ0R.ýgÙÐÝ:–‘uc¯ã¹óznÖþL­E²ÇžBôM¾,ízDàÕòËŒQÇ‹÷ãî÷ÈĸJ‰ì²üúÞlÁH¹|­.FBÍÒC$ý<.#êv07qq2\{/_NÅÃ>CŸ‘?‡áÚ³w"ù†¥ã[Ã2®Ü} )!ç/3Ò4î,t¢Ã¹æ>ùðyç$‘ûX ¦ž¯6 e>XÙ!ÎuD†.€ã%‰«ê§ªŸµ¹Ÿf^Ayî)F~ ‰Ä›–g¹ú'íÑxÁXîÉV‡Ýk°@ʸŸIeÜßjÿ~Nù>ŸûíX0¤¦ùæïdÔu€[¶ÇiS[ˆä¾mq0q “H¹üKIXPzˆ‘­ÆbíP{î?î·¡3n3·»y">øžç­´†æ©xîÙ ki‰\fŒŒþ)ŒÜ¼Þ ©i‰¤\¾‘q˜»×[$;Ž’°]m.÷tþEä.˜ÉÈoAHÕ0â~ˆº…´K:ܦÖ!˜ºÖ+ÛϲS811ˆ‘ãþöFÅnn°ùMxåºp=Œ‚VéÅ•Ûïm‘‰ ¤‹ÙîÚ8¼y)’ª~ªúY›ûYöà<"/ø2²¸½?&ìöâöZdƒªê"‘ü™j6Käã§Õ!@$ÕÖãwîß™q´Q…"™´ý®E4ed£§XþÏfÌœ†B–ÎH¹üƒ^ÄÖµÖŒ|;:Z¦›¸›ƒPšv{î“ :~9ÀµSóDѨsy=8U¯sž†cT“Dî ÷bLÔŽI¹|“abl•Hîx›³¥ ÈP=,ôÑcd„ñqøí÷HëöˆŽÉ}Ô×Au%RîþõP_ïòس«¨ªÚîmàŠb"ŠŠÝÝ>Ê^ë»ã±»¤»{Ó±îE§"``v·bñèçw0ïÿûà]ó÷eì}ò;˜cÍy©ºöP$÷èØ#§ÿ!îãæ§qÂø¼@žŸéŒ£ ~0RiÿÊã7ó­‡H4HÅ £@®¶ŸÚ~Væ~†øºb”HöôsÀ×þ»¸›ÒÝ0鎟Lšl‹À½qܯ×$ô·ÙÍ5Ô»—ÑFÜB÷ |°ɬž¨îÁmœp6Ò2F>Üšû:j™TÊÿÌÈW7NÉC;<àYµ5·û¬C3ˆÈoâT³Œ<_P„¤YÞ2ù5â~ÕÐáný†6QDrèBW¼º~I •òÙuËÀLKL6HÅ~‘ô«n‹ÙÞJäÓa§Qx¸®L¦ÙcPžwܨÈ/OH¥û®Sl±âí‘,,·Á è)Üò®j|Ü4\&çÞNG‡9jF*íOì _/k‘tíh‹Y6'RÛOm?+s?÷MµEZÅ4‘ì~ÛS·tàÓwƒdÒ¨÷9à:G߀ŭ^ùT¿zö‰d߇±³m=î ýÃÿÉH&ß~»ŠñV£²ûÉ0l6\$’JùXºÂÝø†@MtÁÈfÜ‚I¶xñà¤LþB,¾¤s¯Ž¸‡I.…¹Òª¯œmD²`jöEgäÊ mþª.‘JùÞÝNÂíùE²Áûh Ý4”kÕÑ×7ÉdpaºT¸q/nLF-ãt‘Ôÿd¶›p•î³ïÖXZÞW$Û-:Žåuô¹÷Ê(©:Q §ºEbÀ›3\¥ý1ÞN(ÿTS&Óó#0¹¿)WÛOm?+S?5?kŽ!+æž@jÎ?µ¼Š‹íõeRsþ׆84i™ ’šóÜ2OÜ3J’IÍùž;Þ`u–3Rs®”_÷² ÌVx“šóëñ©xÃÒeRs>ã~žë‡‹¤æüÛõl<`-šÏâßä­)’šóøñx<>D&5çwÙaÝá2ù§÷:YáÖÞ'©9ž„eŽ÷$òO÷Ϻ‘††¿ù§Ïkû©íçs?½­îá?ÙÍ> ¹íŽ‹d×_!øRï ×û^~,ß-“%‘×0¨©.W§+ÃÒÛŒ zžˆì=Ò°íô­(“¦WÂÐÇÍ{ÁÐ…¼Ers4/ì$“Jù?ö?ÌŒy2SÃþõOpÿ‚–ìÆMä1s·êöHDD\ÈF—}Q¯§÷Ä¢K(¿úË ¼¾·H*åät:î “kNäaë`3|³.áÝfɤGIV¯àv9†Ûî!y¥ä,RõìeRéþ©å8šòL ,öÇÊçç¸ñö°ó™ÎUú ³­/#•öOxŽÝ¦ö2Ùbc.¾ULà*=¯í§¶ŸÿËýÔÿàè”õ"ÙÕÎmZMçÚ<¿’Y)äƒS¥h°ö@D`O3®÷±ß¿NóþY¯ä<ž˜ÛȤ冻¸T2Ÿ‘wdúÖ™Ô¹ZÏoå©”ÿØ>o,èå#“ Û‡âkf$7ß>Ùÿlã>~š — ^ܼî0;'Id™«R'Ȥß#Öe$Ôøœ/“Jù‡ebÚ„lœËPåëgî))m&Ëdê·"è©ëþŸ= pÙÅ›äyO~MH¥ûzn~˜Ó<\ W5ôÃó4îµúÑ躜LþÐ;‡áç ¹Jûë\¸çk$òÖ— ºèˆ¤¶ŸÚ~Væ~Ú}qÇýýDr\ñ¨]=îâóp,™Ä-*HEÒËýÜ9êü3å&#:$âÄÅÖ29ìîUlþ÷‘Ô›ž “¼î‹*(«qM ÌVãêÚ#ŒTÊßieRJcdRŠ+U#¸†ù¸ :ÆÈÓsá\£®@Öø»÷×ÉdtÍLä\ÃýdxIÉ–Œ\º>±ûO‰¤R¾I[0gn‘œ*©qÉû•@¾Ø]Šs«0rYÚU$ØY äÓ¦âÇ:‹d×=g1®o3®Òý—Ù¾Øç—¾ñȰ‹‘šsÓ°#ÈÝî+šs‹×yôi#’zÿÓ« 4‡3Rs.ye¢0ø¨Hþéþp3/Ôè¿H&ÿôym?µýüoîg÷¸[øOî8u†'šËd\ï‹Xãô‚‘Åo“ £+K䣉x4 TE=VŒÙÃ\òilôü¼V$«½ E›-iq1Ï7àN9î‹Â³iÜê;’±°ÏE‰TÊŸˆÑ5õE²ž‰ŒVáVO:û#Ü“¶ X#rã‘‹g;¶¨È‹—³6³–H¢Ž„²¹»TdS9¿·‹¤R¾×¥À£J&³ß§áGinü¿^°oëÄÕ=…÷¹&±x:Ƀ;lØ è$^e¤Òýä½™ÒKW&ºg£ä[#»¼`Ÿ=‘{vk&FõòH¥ýÕJñø‘¼SWÆ%Ñ‹«ô¼¶ŸÚ~þ/÷óó·Ë˜ÙÏ‘‘7 KarëŽDú¾Aæ¹Ñ™¨Á¦pG· ǨK"iýà ºØOåšê$cüÍá¹öG$ìÚ¿àv´ FɆDh ³»]dR)ÿìUîX25G íN;£‡ã2nHKloê,’¯Û¡^þPnã ¨³„[ÔÒVG–s÷EѾFþÛE¶x™TÊ—Û= =æJŒ¬ý- 禵ã^\“†.OÜe2úa!ä…Û¹: …ørjHzïñBãŸV\¥ûí¸jk#“Jû›Þ±ƒå¯2ùvNNZÚqµýÔö³2÷sAü¼U}ÈöM/¡áön"ùÜ1MÏG0rÑP\©[_&÷¨C°¤ª·Ì$×§œâ®Tb¡Ó2îËúéh³ŽáX3ð€Š íŽ7—­$²ÉÒ`”w[%’×L½ðî;·qy6Â˲wqþ™¼B$¿¿ Æq}²¦a1vÖ°âVÿæ€úÝEÒÑþ vÔ=ÁH¥ý®×‚Pa­È¿ÛE üK Fjû©ígeê§ægÏñD|´ñIÍyý¤‹Xݾƒ@jÎ{exbþ¡÷©9wïsßÖ_HÍùèHoz¡"5çJùgΗ1¢ù)™Ôœ L‚‘Á{Ôœ/¿™„šÏDRsî?3%î=EòOóu© Ë5"#5罜]¡S/A 5çK'Ùâò¯uù§÷ý}Šàý$Q 5ç¹ÁÐÛ1B&ÿtüï¿G,Ô7$òOŸ×öSÛÏÿæ~6rÿI‹!—P’‘%7[ãáw]‘tÙåƒtûš2Ù¯,›W´äzø j‹N"iý& ¸™u.a[§&\Û9)H_aŬPãÔ*2YWˆêcªH¥üNsìážs‘ƒ»8Áëkk™\¸©]«—dø¬,j»L$oÞ½ŠøCã2Í«þ sEr¥]îI£Ud| #?™1R)ŸÞ‚“üÏ:Y”uó»Ú1Ò+ÁÃÏ› ¤ÎD{Øz5cdц¼¾Û@&U— žñYâ*ÜoQŠc¦Œ|­‘=ó%²íG5Ž?"WÿeûØI¤ÒþýÁaØu§3#ëŒÃ®&·¹JÏkû©íçÿr?»ŠçÑÖ¬‡HîKÎEHã±Ü2‡X8§Ö—IÛ”dìö(bäÑ+þÞ»•HÆíõBB}t|ìŽÕ戤—åY<›Ð_ -FàÅ+‘Œ]`ƒêY ©”ÿòèŸ#“¿†ùãð?‹¹Ÿ—$â§©H>ۀȯ;¹‹%¢Ãr'n 7>¬=Â5YŠ1]ês_ûºÂt’W)ß»§±½n ™ŒšàŒÃÉ‹¹ mq{lnl[L¾·˜›eó—ßTä—ÂpÜ6 f¤ÒýPÕUxWílL>}‹ã?©È)û“ ®7‚‘ ¤cíPcTÚï²# íîÛ3räìX-µHm?µý¬Ìý¼‘&‹ç‹d“£ ó®›sMÁ°lë8Fn¬/Áò¤e2éø& ;-eä“05¾Ÿ<ÁýÐÄ ß‹®r‡ªá6YO&óš¤ ¶ñi®×±´of%JùW:†A´F&½Òbpóí îù.Ø.ŠdânGè=¾¬" wƼ*"y¸æ9 `¤þé4J& MòatÇ‚«”/ø€¶ZËdø°pTsâÖí‚'A6\·¥‰èóÑžëð>wò¹³I|-¿ ¤ÒýÒ/¥˜¼wµ@Ο\Š”+á\ƒù ÈìÙC$gÏòŠ‹í¹JûçžbºP$íšú!ûÁF®¶ŸÚ~Væ~ø;–s׉ä¦1˜Ôx5÷y“D\ßýEEÎúôÞ·8¬d¡D&ÅàM¾ ±ûR°díF^ LDzJŠ\nНÞÝDrÁ–`L¶ÅH¥üF'£bý ™,ÏLE¯Ùƒ¸{"]‘åÂÈâ³hõ—¾L6+uƒßÝÜ&5#ñÆŒëºâ>=©#’‹ÆâgÆz®R¾ ûñaŒ“L¢m&FN8Îm3)òpßY– ÷Ö î²§!øQåHF?±Á¯ú©tÙ„+Ø7û¡@Þ{w šë‰diÆi8´OHÝO”ŸÞ(“JûÇõ·…CRˆ@úb¿ dRÛOm?+s?ƒÚDàÍîE"iŒøñ“¹¹‡"ÀÌmTäåMaØ:5D"G5 á>äf'_A%²c»H„ÛžW‘©¡È1ed÷¤xìõTE¶òA›$]®Rþm2Ñó¾L¶Ú•ƒ+ò9js4z¹èˤ KÆŒ‰9Œlx_Æôc=dò^V6j7;ÈÈóA®xöl27x<zÞä*åÚæ<Ž^(“Y~á~«)×ä¯kX¶Î™‘¶ ×ðlIWlpÑ =†“ÉA¿ °åêB®Òý„a1bmG‘,VŒÂy"×ß>·õOÉä÷qWñ®s:#•ö»åóÜA&'çbFâ®¶ŸÚ~Væ~º]õÇø­‹ä€¸·0â6K ‡yÙ!F.š˜Ûç¸=Š¡×VO&tLÀ Õc¸!Íâ0~ÄgF^ÍÉÂ#§XîÉÐXTm9K&‡ô½‚œFuR)¿Ç–\´úw6#ƒîçà™yO‰Ô})¡ú“÷ÜoS’ #$©ÈŠ•°“ë¤\= ƒ§¹s½NÅÁ²ã/¹¤O(Rªìd¤R¾^ÍJñâ#Ÿ/EIûu*²±å%ô÷7ɤ…ùØÙk-×3³ ]ãr`f4®Œ)’J÷¿}.„:pºH>\zoê™s­j\DR•ÎÜö¦qXmcÃUÚ?²N)>%MfäB“B4øÙB$µýÔö³2õSósb¦ÆêýHÍyÄú$ØårÊõeRsîÐå<Œ·.HÍùäåaHZ~V$5çJù}̧#7U¤æ|A~4:\n$šóñVQ°k4[ 5ç‡d``¯2Fþi>{ñª7g©9¯ÙKÂŒÒ"©9xÎ /êÿ-z_×>>óv‰¤æü]?WÄî,’º†n<ì×.É?}^ÛOm?ÿ›ûY·Q)þ“ûBâ¡îØ@ óýcá}¨7wÀø¬î%’;ebÛÌÍ~ µð¯DÄÄcù/ÙõMúε[[·oÜ»¹ ±âövMÄÙÍ«d²ý®ËðTïH¥ü £Æ›,Hæ"¡Ì+Š»7 MËä‹ý2RG¯ãNÜ…Ùó6rÓ›§cÛÍ!ÜmñøXþ—HÊ­Â>å„1©”ïh˜˜=’ÈÕ,ï Fre'ýþ^RWE.Ø™ŒùA£òM±ZP1²I¯(½6—I¥û¯š^“ÊSE潌†…2h€3:Dæ©È¢s~ÐðŠ‘JûkVœÁ€W£r`wÔ»TÎHm?µý¬Ìýìù5¦y=Tä·A‘h£?9…¤}"’¿†8¡jß<íꉑ-OIdË×>Ȳ­+“1®.¹ÌÈW#ƒà·w‰Lf_†Éµ#"ÙØÐÇÛ2R)ÿ‘Éh5ñ¹@–dÅcÌ!=‘4£ë{ëËdEy!œuId‹<Ä ²edy£tŠm'º¯Qf}TEV ÁœÓ/Rñç§föÔ¦"oIñÔk-š'bSQwôïïqI¯¾pî£Ç׌¬vã#µýÔö³2÷óÎ÷¨{;Jä­ ‘XrE—‘¯–E›C™Æä°%®Ð»àÄÈø¡Ñpï´@&íFç ²ÝPnÇGiXý×î(§ß¿O7ÙÛ:VþmeÒ"3]zvH¥ü£ÇGã»›‘Hºµ GLp;®‘i>Æ?tÈÖ½SѨam‘Ü5F‚ºO®@ꎊF·€(îßs\q9(Œ‘ò §ÔÛdR)_ŸüX$H…jèêqg­ …Þ§ê"¹í„/~Ö¾/¹Ž¨ºl¿H®p:; &•îü[ ÕŠékR„ã§j‹äûl5Ôæé*rè0ì,ïÉH¥ýw#ʲ<ÉÇbáüÜP µýÔö³2÷s{·(ü3š‘n¶jLx¹›ûóº^ÛU“ɸ¢PôkÑŒ«c~ ùÞ£©_»9{kŠäå]ñ5þ‹@î¾ÀЪÞ!‘Œ<‹Ú/$2Ï1ïž}4&•ò‡†¢jË–"YlêƒçSêpëÔŠ…ÙO=®õïï5_¦f ä›áh×½ ÷{XÔ?%rîÆ+˜^!dLŽ­­¬DR)_Ü«0ŒÎÈMúA0¬ïÇ­ÿÝ {¾Zpç¯q†yA×%Ëä‰Þih÷:‚‘J÷íGbï¨V"\ÿÊ:÷áÚ~ŒƒN¿—Œ¬1/ãêå*í÷F$Zyo–H«iq˜¿Ó‘Ú~jûY™ûùpm Ògœad·ˆ8¼³ñáÞo‹µ5ëÊd߯Éèߨ˜‘?&¤âßC"¹öŸ@„;þŸ¿úbA§U\ÇŽ0zR ÏD †Þ$‘¬zþ4.ü˜ÁH¥ü›¸Ã¼Ã-ü7ÈÙ‹­¹)·‚‘²µ6wEy’./•Èz;0 T殀„à —T¤¼ÍqÇR¸/gF Bê&“Jù6Ùø"fÞ ÞØ æÝÝTdzC4zœÆHaš'ŒÎô–É…ó“`>½—@þå‰ÛSÇH¤Òý¨àF ƒÙÏ)þú®©í§¶Ÿ•©ŸšŸ]‘Ù?ˆ‘šs w ƒÌg3Rs^ôÒ“늤æÜÕÈ 5ç3RsžÚ;Gò­dRs®”¿aÛ³(kê§"5çÑ©ÑHïV*‘šóìoI`»Ï ¤æüôjB^´Dþi¾.¦0¹öJ"5ç¿|ƒ0ØhµLjÎ}½RP¶g'#ÿô~›%™è4iŽHjÎ/­uEÑ‘üÓýYIX}uŒHþéóÚ~jûùßÜÏàA—ðŸì?ÔRé{è,,jȤ*Ì/ÌZrëLGíý¹sÒã`igLÞþÇŠ½YkU6\Êû‹¤nH$:¯ÚÉ­ûÀ§¦ÈCq‘èÙø,#•ò›þ g×(|\+ù÷¸ê‡¡Ð¹á&‘ë'‡áñGF.I<‹êY¯¹?ý0¤b…L–;Ê(y˜ËÈKÝÀ@ •òEîÊÆÚzºù%8ûzZsÃVùîuc¹½/X—NŒìi”„K½vÉdéØ"l¾Õž‘J÷ýžfÀä2ŒÉ¡­3`YºQEÎ? “:Cdróú<Œ™T›«´?¶$y;ö äŠQ(oB*=¯í§¶ŸÿËýôñuÁüÝdòÐAx¦å¾o×S[sßm‘ó‚‘/sBð¼C°@TxÁ®ö%™àŒ=/ÛŠäàVö¸ìýD"o·Vãú«*rÿ‰`œ¹QÀH¥üãvJ˜²ó»@ƽI@ÿˆ_ÜUý¢Ñ{ÿ'FÞ©.ÁóTu™l»5åÒ>î:§|ÜÝÛ).ëuy|z&Ì¿lL*åÛR!áVdŒ@¾è0îø$?,hXÀH·`\Ü\& ÞɘPÞJ$ ë#îgº@*Ý¿Û7ËX±Š¼Ü! s·ÕÈåŸrh!7Ó5 ƒþZ%’Jû=J€Y^{‰ /O†Pc¯@jû©ígeî窪¾¨˜±X&÷ÿÄÄÆ[¸LLÅÁÑ{9o— ©c$²ÑO<øÀÈ%Æ!0-“÷šy¡óspïÌ‹Gy.wíŒ<ègÝfdê³ ø„ÌI¥ü9ù1ðø^ OÏŽB—=—¸G rð]'–‘ºGzzÈÒB¯VŒ´{צ 䜅±¿ÿ'’†Ò<øWH¥|§>Å âc¨@ºÿ¿s{:ªóRɆóSÐîë0á˜X7K"ÛzÆ¡øC¢ŠTºŸý¯Œ$ÏÞé™ÉpÔ`÷±·äƒËEòðŒn5F •öÏIUc×óZ"9{»7ž®»'Ú~jûY™ûy|x8î¯Ú+“§B¢áYÍ’»F/ÞMbUä¦ñ°¬)†1Lú1G&»4º€3}?2r}N6v¶{İ;Fé*Ò¸Âk ¿ ä@ƒ\PI¥üo†ãG—`´Ý†ù‹6p\ 5ìW·g:0oŠH…ÊXÐùonûÍ‘8õ¶×åï L{³E&·Œ.Ä;»›ŒTÊç)Dâü¦öYsp$¶ UdJ›llžÛA&_ŸÇÒ1…Œlùû=g_²­@fuðBHKwF*Ýßúû½çC¸™@nì’gÓÜ;S<ЫJm™,JA¸g®Òþ«'p}â‰ãâ ½âa2©í§¶Ÿ•¹ŸUñîÉ.™¼jÄ`1q=7jw4&®R«È|«(”™æ§ó^ÄH i±0×kŽɨîɰx-‡÷Eán‹Üc噸¼›‘NÅ"yìBTÊ?¢uš8×H‡Ëap.¥"»¶FÎf ‘lÝÎë>ÏáÎI÷Ǭ‡Åټ̃ÍUd”qöþ2Iã 4ßg'Jù⣠nUBžîƒÛýÏJä‡F%˜Yå×áh1äféÓ,§^Ï‘I½ö%0ï¿—‘J÷K“°µú.œ~,öör3Ä4¸9ÃÈìœØ¨"•öû—$¢ÕN™tÓ¹€‘Åz\m?µý¬Ìý¼ZœŽnse2íc6FÖƽ½F:;Œ©S7W_îæšý~(w‰dÐ]OÜnÚŸk­§ÆàÛu$rEN"t –pçõKÀö0CFž3epùF •ò'/ˆ€Î-³òø®(xcDî[î›øO¹òÜiÕÕ“Èô[!ð|؆‘M¢q¡Ð€”£Æõyy±«í^Ú ¤R¾/=âÁì<$2ªaœ;Vã^i{§2:Šäå¨tô™¼œ{ë{6®,3åšGøàÝÛI\¥û-F$`Q‡=éY/¾u·qÿêüç *rØ›hôÎìÇH¥ýïCKàún¥@~”ŽÔáERÛOm?+S?5?GWæcÀv2©97ÝšŒ¢—‡©9_ã„máuRsÞç÷{ù³µRs¾²ÈÇž¤æ\)›Ñx7ëDjÎ8ÁaÖ_2©9ßø"÷據ó@µ'‚W¦IäŸæÑ7I#ÍU¤æ|q~JÒ,ERs>ç¤3Î7bäŸÞ×{‹n¿ÌRs~¤¦Œ}Mú0òO÷Ç»†býóñ"ù§Ïkû©íçs?ûúã?Ùùh$BV©yf€Óºtä¾k†Y­‚²ðmöy¶åÖ¸ƒ2Ç_Æä­PßÜ-‘>iŽØZ›‘o#}áÒ¿‹L.Fê”vãÆÌ.„å¯LF*å¯9'åMfʤui8¶™˜s›y£t•(’ñ¢3|¶%äèz Œ¬z&cîHdÏMèñæ-wIE zV H!•ò]»è‡œ±‚LNß«g¹µ'cÞφ"¹öW4v·éÃdž€åézŒ\>)é'"†J÷ïšÃÛáQ é„`CK‰L]›ˆ†VÉØ+aÐmc#Jûï d¸öH ëxGÀy|‘Tz^ÛOm?ÿ—û¹ÿD4RŸTedÞŽ©£$ÒÞ/ ¦#ŒÉhƒ(˜ ‘™ë"1v®+#ƒÝ1sÝCn±{ŠÙÉ–Æd‚:%«SSÈýeq¨ÚÒNEæÌÂŒÍöŒœ\‘%Þòî!Î}Tä(Û8ÌJî(Žá¸¿û<#ýÅ¢ìC1©”¿Îçtl½=Q&,ÍÅ©áݸ¬C0¼SKnò¨8ø?©Ï=³;µWÏɧÍ<Álºs;_?‡Q!ŒŒ2ð-m‚H*åóî™Ï dÒÔ*±›ryá‹3^µ‰H3gGx·÷6ævÌÃlËydL(â–,I¥û±¯Q¶XddJE *֭ᾟƒ¯"²ÝÔ(Žà*¾VóÃÂó“drwãî^ÌÕöSÛÏÊÜO¯Å1øpv‰Šì°63Ê3¸ÝÅÁ¢ÝD¼05KzysSt21ìuW‘49¤F¯¾Ë¹5^©Q➟BF& b{F®Ë‡èʵݛ‹ëúG¹Jù¯?‡OæŒ,ëtÕN;q]?¤`_x÷ßì}!‘½Ó° ÆD ~:‹¶sÓé8 ÊõDrÞ3tÜü†‘JùŽee¡¢ß$F®ÿ–‰ ŠRÈUÑg±~v #_ŒrG­Kmern®Ò_S‘¥Î°ê0X&•îׂ°^u’‘ƒPxןk_è†Ã'ºr×d¢dØp™TÚï2ëþJ<Ïȯ5 °{ù^ÔöSÛÏÊÜϺոtñ§ŠÔ݉õ5 丫axs#‰Ûlsúm÷äŠç=pÔ«—HúžrÀì¾_Td£O©0‹ 5&뾑!Å” dëL {þýG$£:"©®“@*åOmR„ÛzqÙfæ´Ìy«"=æ$!iøîîA±Ø3À†›hí¶k„Ln™‡O½¦q½ç$#PLJ‘Ûýb`ÑTP‘ŠßޤÃdÍ{yå3ã#óòÅÆl1[&«QcÞ–µÜq5Ó°þûGFêt‘a’¥+J÷Ã&#Œ‘gÚ„ á—KÜ Óðaà™_·vw3Riÿúa)˜x«‘HÎí½Å·RÛOm?+s?ãœÂ1ÖU_ G„‡bZò_Üš¼0|K3nŸfpFŽDÞ-sAg·Œ4xãFceò}Iò þɨ9xPPƒë銭FÉ7:ÈžÿU"•ò—U9ýqîÙ~F2o¼æ~7TÃÊ£“Džš £»cÉÖgàÁ­z2Ù?(û¶6bdýM±pýäÍ]}?%KªH¥|Ç?%aþÓCÔ;ÝÛïçî ‘Ü|žL‘²‘`Ö…;«e4zv‰V‘­aöö/F*ݯÙ!ýŸ1ríÌ0¨Vp-=ó0~[c‘ ¸«Fg‹I\¥ýV„ Û1QE{õ»µ©í§¶Ÿ•©ŸšŸZ‚a˜tAEjÎwòDÕ‘YŒÔœ¯ˆJYÃî2©9=Ø »Ò%RsÞæþïß÷–T¤æ\)[¯Ld>6I͹p/^UO3Rsû( §' ¤æ\z Ë#3EòOó¹ÁÄŸãRsþdø9ìJ“©9÷ K…Ñ׳ù§÷—ƇãxhM™ÔœOõôFHµ<üÓý·Å!ÒñŠŠüÓçµýÔö󿹟ÿ޹€ÿäæAÉ(g¿R?*#ÏÔÉIç  <“‘Ö—Ïc¤ÏR‰ ¬&¡ÎUœó! =ÿn+’ª1­î«"ťù$òQRÒÃm¹G“³­ªL*å¿"Ï1²¹QŒ¢¹G¯b®c#®éöó`á,©·86¿ºHžíê u¿Ó*r ê›~y¥<mÛnHß»ùð÷òâZb~ó1"©”ÿÝ«xÔé~‚‘Á¹ –ŽäÖº”‰v;ˆäÑš ð<–k”’Ÿ#dòÖFÙéà^ï—ŠÔÒþÜ13³±3®@*å»Ò5 ‡/ÜÈ9C¡jsŽÛEGMtTd˜ƒ6LH!CïeÁ®WwYÒ(ÝçI¥û ÇxALeäŸ^8>àW]„òïãdòéŽT,“ûp•ö›=NÇ(ëTü1 ö÷÷¶û\m?µý¬Ìý<'‡£d\G‘ÜP3?Úr[½HÛj#îp‹¬Ýׂ[bË9úP(¬ó¦ÉäÍ— 0tm&’å¯àìÚŽëÝ×7.1²e«d>»ÏUÊ]€ì‰¯%²va<òýæp½ìC߬H^ßëƒÑU® 䯨n{‘‘ÅŸ 1{i±Š, ˆÂí–þùfR Òõã’I¥|§\|°ò™‹@øî­=ZräÄÃ4î„1y.7éÝ«ÈÕ¿ÜQÿ/]‘ìÿ·+~~+f¤Òýåo½Ñæú3Fê7õEc£j2YÇ-ù®K¤ÙÆT|·KHÅ÷+ÓpÄžž Ã]£ “´;‰ÔöSÛÏÊÜÏÆs0õ‘¡Hž¹îƒÐúµ¹ÓƒÃ06í§@vIÂøôîÉXuÀ0™Ôýž‡¯"¹ÄÙöÿNÈö+\aTÿ#çDÅÃ쩊 =·£–2R)Ť8¤¯é›B>ØËqÅCÈÅ^¿¿·%«ÈÌ›~xÖ¼'# çÀäk]‘ÜÕ$7M¸ÍT±0¾#˜ê‰ãa¹Jù”»aä :Yó‡ƪ-Ù 8Cö¤ÕÃ8Œ™ÉÝœ‡RQ&GŽËªíúŒTº_m«j}i “«Àò|K®kµh<U ¤©*[?9¨H¥ý}«ýþs»ä«"o‰n @jû©ígeîgË0Oœ´)ÝÑ?™»±z ŸÖãŽm]—drJí<4oÝ^ 󃬞r§ªÂ°qÖ4™\S- ïæ÷æÚ!¹ÛN ¤ÓXé#’JùGú©¡ó·Y2Y½Q6no%‘‡ÓB1°ð#_TÃkË!XmÝN$«Åø`—»@®>„S£ “ SöF*å†xbÙ¦'ŒLsó…sp[™ükw9ñc0J×à.¸’ˆ”ŒÜ™ÆÑh0:јTº'#«»Êd˰`< îÏ­j‡61öÆä³èx¸ï°H¥ýö]ƒ ÷4A íõ€N‹OCHm?µý¬Ìý|Sâ‚aµ÷ dñ|çßÏV‘Ù>á0iì*‘»»«a×ø ÷n«ht²¾!‹ß‡aÒœqÜOòpçZ/Fö¾–Ý·®yÖÆ¯2rûîTŒZʤRþõ"ÁÒ2%²cI’Bº2²Í„XÜÞS$‘ã­£ñn½•1©_⋤Ù™ê‚î‹9Ã6ŠôErç‰@,ì[$Jù²ó‚1µx„LæwRcåí}áÑ7] k–¹£}õ¿¹SP²¶–Š\z5oÖꈤÒý%)¡p^(ÈäÄá8i>‚ÛvQ(–šÝÈÀÁøðí¹ŠTÚ?£š}«%“NÇ`té\m?µý¬LýÔüÌu‚s›½)¤æ¼ÁÐx?™dLjÎ/ŸˆD™¯¾1©9oØ(纗 ¤æ<£J*^58$šs¥üwvFbÔڌԜ·çÊIÍùúÕjÔ4Œ‘šóÐËaЩS…‘šoÛ×D¸[N—IÍù¨3®xP+‘šó–#¼°È¨T ÿôþ×Qp +“šóWS¼0`X5™üÓý] rq®T_&ÿôym?µýüoîg»NçñŸlîœK^5EòÁÞ ä—·ä®ŠÂ[«†Ü>AX5BÛ(Ú ?Ù7WàŒ%cCŒIÇY(«™ÅHx ÛBW G5O…Oõ®Œ¬Ù"åM§«H¥üÊ$¬uy.q©IxøCO$#_DbR¢Šk^Ç´â>M€ë£2ÙyMŽ/ûÂÈþi‰ØczR+%Ë’ŒI¥|?e¢ùÞOYÓ\FîçÆ"9i§š@_&É!¹¤?·,7» ò»`|ýêÅUº¿}¶ b¿¨%2ê´ ‚µeä€!á¨1D"/E#kªH¥ý»6ÅÀ¬ûâòá¾(ôlÇH¥çµýÔöó¹Ÿne-ï-’?›¥ p¾Š»wŽ7®^Ȇ“ÝÐlënõO=÷™‘sÂU¡’Ém›ÓÑ/°ƒH^zFQ#¹Ï =–J™Õ1 †éDR)ÿÄ8 R·É&µÕð-ëÌõg®è5ÊG [>ÿýs£&‘Ó-ÎÁìC4wQã|$T¤ äÄ9±xñ×FÎxËwÅCH¥|ª…‰ht¥µH¶<]5Zpë8ÅÀ²x LZ/IAž™×ã®'ÇÈáï£0ù‰L*Ý¿]×lV3²g¢+å¡^«IŒÔœ+åýÀŸ…2FjÎ7ΊAá‘&*Rsþ©0 :j«$Rs=Ñëg_”È?Í'OTãÙ¹©9?y?æwo¥šó¯òàeÁÈ?½o¶9/ž 2©9¼7Î5ÞKäŸî¸" 潩È?}^ÛOm?ÿ›ûé=°ÿÉ [`ÒçŸòæÄxèt«›Lz5õÁë¡“9l—^ãZ<`°¬k¡"Îfèñ8V «?ó@LË*rW/oÜ\}‹‘O§ jÆ7¹ò},=Ñ—H¥ü­›Éx×9Á˜œ5XÂØyÁ*òÅ//ô¹çÀÕqòÃm¹/#ÿj˜ ÿ‹×%2x<,'µJ& ~ä¢÷Šcéà¥qýDR)ßÃ,wÌ8ÒL grÃä´Žùu_.ÜòüYgp>¶í? ‘1ʼn˜?ËCEÞ?‹y¦÷Ré~êZOXY7H‡õžˆÙ"«È±GpÙ`#íFE°Wc™TÚŸ9!¾C‚UäûÄhµ9.JÏkû©íçÿr?‡ôŠƒå²iÉä”>±(»¼;…tÓ Aýèf2¹è¥Bûrÿ²ŽÅ"YÞ;ƒZÔçÞ^ óƒenE*.÷2àööcø÷Ò:œQ%£BꊤR~½&ɨÝë­Šû-Í¿è¤þ»TßêÀÈ_×¢haÅ=Û6Fõ§IdÃâÌ÷7#ýÕ^°Ì,Bƭ§=nŒTÊç:Ùïmaäåj^èÖö:÷ÕÁ\¬Y‘CÚ22½a O™*‘¯z&áäÅ]Œtl™†tV—‹±¿¿7Ô¯%‘æµ"15Ì‘Jù‚6úÁܾ‘LÚµ Ág³ÜçvºýÈu7âpjPu‘üR²Ðþ2Y” ÿ…Í©tßü„¬ÇŸ’H7xcpþ{îû:Ø{¬¦H¦FáÉô¶\¥ý ù`Þö»Œ k¢Fa׿eRÛOm?+s?_dGãüŽ"‰ôû=F¦ßÊEö{o®“Ie]%r¿n ^ôk.“…³âÐèPGîk³xÌ>qA GÎŽD䩵ÜÎ^ q3Zp„Ç`mûê*R)ÿ”–±X¾±•мÔ>ïº\O&ÏêDÁdùåÒoh8œ??ÈIî™u³@6úýÞaõÛ]/ݦ†0Òyi&²½+R)ß<Ë(¾7H&ß݉ǓQ}¹3ÛEÁªü‰@>\†‹¿‚¹a“S0x½7 ‡Ûp•î÷kâƒÙß;1²q=_¤—›rUü1*ù¤@~0 Bœ[ŠD*í/¹“ŽêɤwiÞï÷†ƒWÛOm?+q?¹Åàì½>Œ<î‹A5§rkgå¢áÄå¹À?Ÿmns}Ra4­‘mº¤£,y•D¾–¢ S0SE^6ŒÃÄòîÂÛ)ðm8J ß~ ¹‰5DR)éXX C%²¬o<ôN:qÍ6…ãŸX{2#•òí²— —`$“ë¥cx믌<Þ6“OÈ 5àÓÉUE¦ އióù\o ë-’ÀU¸ŸÓÖ¶;·3òØDºžàæŒB§Üj\‡»QH¿óÙ˜TÚ¯ÿ)Æ×³ò{Z,Ú¤•sµýÔö³2÷³ðUl¬bä­ã «¶“{Ú=;;ÖÉãcñì©7Ý?-«¶H,ŒƒÁH®_Y ôKl¹â¥xMMâ¾Øænb #í¶J8gßT&•òoû˜€ÁŽÁ÷iü»[s Ý’2ü=#kÕÌ@§Û‰Ü«<ì)næn¨sÄG"ŸÅbÎ'FÞ “­­%R)ß…—™øìÝiTûû?ð$i"³$S%!”±Ú÷¾>„L™§Ì™ÉB!B’¤Ri Íj7ïæÚûÞ÷’™„dÊ !Sfñ÷p}ÎZû·Ö¹—gçÛÚûÉëÁµ>×õ>¼ÏªýäœwúI,šÚp2gt§š‘jiú¶è…™àéágƒö.Šƒí ’T–f7 YTè¾ÓÁ“áÁ¢Ãn$²3 Ô[’ÁóªFúÕ^‘ŠDÚ?êD*´VóeÐoÒàë·!"TÕOU?S?•?kæBŽ,ª<µM‡2£:Užÿ Ží}mQåytëhµé“Už çàòÅÁ ª<Êßô¢ ÞÅTËPå¹}R)ts+–£Êó9/cákÂ=UžÏ}š¹,ú·ù´§žîÓ¢mPåùÇø,áˆPåy›‚\˜äb­@ÿöþCS „lJgQå¹éÇè¿»½ýÛýcÙ,ÕDèß¾WõSÕÏÿr?÷o:ÿæã Qp1«¥ý¤á“öÊÑaÝOÃÜæ Ô™%àª/BW΄©gV1è¹û™À’Xºp|:d;iˆÑv¯c ªJ Ziàþ=”E¯ÖK¡Òb¤Êuâÿÿÿ ü¡ŽG”Þù2Ôß1s'Ø¢ ôÒ UßêÃ…°UÔ]VÖ†c#o°è†&¾ÇÕk‰Ð{V2°µò—£BùÞ,MÃËrÔ=Mõ¹ÞÔ¬YIයƒ]¸;ͽà—Há†Æ+zÿe"tèó*tHL„þ~'B›½Kƒ§WK©­O‚·†– ‰¥"*´ÿÜ<ð|l_„Þ¾Xiú·D¨Ð{U?Uýü_îç×þQðô¥9‹¿‚ ¯P/4=³’"Ô”ç`ûÇ&bT✭>¡£ŒrÁÛÙ‰A§=Œ„NÚMY´aÖIØ0QGÖüÌ€tãL5-:‰ÛfR…ò×v8&÷gÑrEˆÛGS÷•€ÚÁ49š[$ú: ¬+…Ýmäè¢&g`}m$ƒ^Ì–A3Ë'Ôì×qðYþ†*”oòiÔ¤GlÐT¬k*EhФ(X!šÎ s‡DÁàê+24©.Zõ8Í¢¯&Bzyw*tóôtx:6N„V–¦Ã»oû¨Ò¶ pùÆ}=s##~Q…ö_R+„ÚAÅ Ú®W:¬rÑ£ª~ªúÙ˜ûyj| oWÄ¢æVqàØîu÷똛c"F'”eB¨¼/u ›)öy Ú9ñ$eǨұ¹Pw¨‹½yŽƒê.RÝ ƒþ%]ÄèÈó±ðÔØ‹A…ò_q^Å¢GuO»!Z Ô­$ ÞOxÁ¢VÁ¹P–ª§@oùáï Úd”Î^{N}±,ê¾å²hêÄnöŽ*”/´ASW3蘋,8¼È£ŽüƒV±èùK'agªŒzëI)9§ÉQŸ 9XÈß3¨ÐýIö`yÜQ„ž½’à ‡Q'Êà©¿‹š6-OQ×"Th¿é8eq’AS?EÂl¶3‹ªú©êgcîg÷Ј9¡©@Ý—'†Oj¥º²?w£3uã Ì:DĊ~¶èúÙ±`µ§„EO^æÀ)MÛ5Ö-§…=Ô×5 ò¶y²hóŒLþ~&C…ò+†'C×Aèâsé`dÖƒzŒU@Uèw}ûº9H=® Fù2hý¢T`禋Ð_»Š@Íø¾ ê]–ëãå¨P¾oߊÀÅò=ƒÆÝ˃co[ŠQm©ì6‹–çJa•ÁeêÑ©p²ÏIÕLL‡ÀÒ[Tè~ë9¾²&"´éÓL¨Üß•ù>>DøÈћà àÇa;Ú?qZ"t]×Rïσ ÷ ¨ª~ªúÙ˜ûéãw­Œè°éà2 7uÈï(hQéÀ R .æjrTÃ0 ]í©@>öŸŸãhÈ¢x}.V„Ê—KdhDY:4 “³hà¹30à™'U(ÿ͉Ùv»›MöʇϗÛSaú9ȲŸ*BVÈ~rÍ’1NÛmÑÓ³àC›DjeÉÁð¼ 8!Úõn'F…òÝ¿$ßù†b4rb*0» ¨ÝsA=-ŽE¥Cò!²—#õõs)ø6dк£‰àü΂*tßÕY Ãö¡s^H!f˜˜š»žë%ßdhç[$Z•2¨Ðþ!íxh•΢µ¥ 0pé-BUýTõ³1÷sì„,ÈÝW&fåBáZsê‡q8v‹]£OfPO÷8šÑÔƒÏ@á„`zx· *}‚¨67‹`…i6ƒš–Cß)btV“xTêÏ Bù½{Ê ×ZC6ûÆÂÝ¢;,º¯PuÝÛˆQƒËYÐjXgêœðLX¦kÏ ‡_%Cà 'j_÷(ÐÚJu}êw`Q¡|}¾„¼-1úte,v¾Í _ÞåÁÜ„Z9úid. çÖPw΋‡ /³Y´éê|X¿ÛL Ý¿7' ÌÏ/¡‹äYð®Ö›ú´A Kô£ÇÔb!îZƒ íŸy.¶ßïÄ cæçÀÌŸ}E¨ªŸª~6¦~*®¾/€ºkݨò¼æI\ûÀ±¨ò|ÆG9‹bPåùÑ·™0£®Už7ìKƒ©§§³¨ò\(ÿÀ{4+ŽgQåùÅ}Éð]ü‰A•ç#îÇCKÿPª<*-‚‘•±,ú·ù¼»EÁIƇAÿO¾^ÙðnÆ9ª<ÿ9¹¾wiÇ¢{ÿ„v6HuãE¨ò|¥ô´kÿNŽþíþ¼§ùà2A]Žþí{U?Uýü/÷sDÅYø7×Ç@¹Ó·?ö9]¡á¹Ùp>é“õðÊ‚»@jê@)œ8,¡û¿¦A^b/Ít8 A+ž²è½lh.k¦@Ï? uæÙ,ºNM“¤U *”_vYCº92hÀ ´¾¦E]’Ÿyê#Q6Ô•=¡—§Aô£6bT]-ü_t¦žùósÎh­.ƒ»JàIŽžÊW9R¡ƒ4hüLÞ”³hOÿ\¸0ÕŸºno! ¥öí‘ýNˆÐQ²Áïä1ºza> W»`ƒv~fÙØ¢“×fÀ—=r´ûå øÚWC„ í¯Û• ¿üÛˆÑÏqÀF_`P¡÷ª~ªúù¿ÜÏÞWb Ìc-ºÈ?b.{ËÑè˜L¨\t„Z`šCöª±h¬Z¨%Ÿ¡îÏãÁìn7‹—AÄËÔ¨=20?1FŽE¥€åÚ- z×\ Z熊P¡üó‚“áÅôC"ô4ŸNk¢lÑ^¹9àY2V„®vÈóˆ£Ôu »éÝð%® ¬¡5]xxwXÁ¢›È!dóÊ7{Ú)XïëÇ¢;OCÅÒ¦Ô~oX¨Ÿ¿ƒœU 3>©SÕŽ$@€AŽŒ…}{¢XTè~‰[Ä<޵Eo¨‚¤^M„öœ˜ Å-ˆ m½YwŸD²¨ÐþY¦‰½ä¨OàIè]΢ª~ªúÙ˜û9CãkôXt4$€ßÎ TqÛ Ú²ˆú©éŸÞTÇQ÷w’€GäKjÑ×lØdg¨@Îä˸z[t¼Y.Ü6Ü'GH Ì²ƒVìI‚^Ç_²¨Pþ÷}Ҡغ‹ Ý‘j›R?„ÀØ{ ƒÎO˃g–)Ô&ŸãaDŽ ‹~NH¤›AÔUÙÙðé:+Bƒ†‚aq[Êç~ <%=lѺz9«1h{êG ¡WNC75wý±?D]hÛí§àfK{ºïäY_{€=ö±\ì£ÚWdCÐê×åÔª•°¨àï/yYpõÕ[]ø°V×zQUýTõ³1÷4AÛ݃¥Ö'Á¼çÔÍïrÀäx)uw¿?ß#z^¤FßæÀLñjò¬ “›Rå…ÐGúZŽžWç`m£3úóç0ä2‹N³T@~«<Ê/)Ï€bÚ'1ÜFìµE}þ|omñA{$K xz%uFÚÞÅ¢;B³áÑÅæÔsIé`?­ŽAƒ¢@btX† þýU°° gƒ^,(‚5×=© ½x|ýu`zV´£‹¶ÈÀßs ƒf®È »_"Tè~Ù¬"XV™!B&O¯‡ÔÔMgÁu`9µcŒ.—|fP¡ý±™à8sŠÕ–æÀØ;æTU?UýlÌý\ò)nµŠdÑ>oSÁ~i 5+˜ƒÅ¶ Ôí—Oà ; êí¶§açníØ«ªæTP{¸³P¬[L +Îóºvb´úT:䤎gÐÙÑÙ0×Bƒ*”ßþ}&”õ'BÅeÂÎŽéÔé7camÒý~ Ôò†¡™œ|æxÊÑ6­3 ­þ%µãçt¸;£½¾…ƒ*ÿq,*”ïjN>D}ßÇ ­s¡‡Ñzê¸Ò??W7tS¥©p¾¢uwd>„X…‹ÐV¹à±»˜A…î>,‚Œž: ªö»¸³fÔNßÒáé‚gÔ±¿%G{P…ößÛ— ™{LY”5‘ƒs°UÕOU?s?“/¦ƒñ‚(Þœ çC©»2JÀÖ§»­ 8­m¼t÷f)ìî/§:5Ié}BÕ]“G²_ROŒ‚”“sDèÌ׉TéΠ¶–'¡Mf ‹ å)Ë€ü¯DèÜÉé0ÐN›A3bÀôs‹5II”†íâ˜>~ûY´~e$•œ£¾9®ÚoDèDÏ|ˆ±Ó‘£BùÌ‚³!F—0hÐË,0pÒ¢¾Ë9 ö.ï©£ïÄÂxi(u@`è_̧f‡EÃÂLwº?O]¬îhõy^?½–QËŒÒÁ²;‘¡u2¡í*´ÎÊbpVŠPñŸ_ËãŸ1¨ªŸª~6¦~*¶Ý‘‚šÔ—E•ç7·ñàÙý%ƒ*ÏSßf€Ôr¯Užð‹‡ª!,ª<¯²çaìä,úÞ äŸ¸?¼‡ê2¨ò|ΟïEŽF Ty~1QN±¨òüs¥ ’˜pýÛ|R«lX?F*B•ç1©1‘Í‹På¹Í )\0ë¨@ÿöþ«´"h;o/ƒ*Ï×5K·ú ú·ûç‡fÂû¯Åèß¾WõSÕÏÿr?_,.³Å¤L9°‰A=žd@›¥ÔÁ‹882ê.5<°¬kµÄhFf&¨{{ËQIU:¨Þ(Bw?KÏsYÔuB6´ +£VÆÃÛÄT£´B¸¨±—A…òwIʃ˜·ÓY”õ.€¾«Pà 㠭ízmö8Ô~ÊÐŽ#SæT2è­uñðR;zÅ'Î7ÛÉ¢“€ïs-*”O£c&O´E§Ff‚‹æDjÝ4 µÛËÑèø°0aXT䛵ۚ*PÇhÔO«‘£B÷úÊ ¤ûãüçßÞ6ÔNò<ø¾|/ƒfW¦Ã¶Ô«T¡ý~c3amzk1q;j.1¨Ð{U?Uýü_îç‹Ò PjΠû>d€úh-*÷1 jµõŨÎíTÐ(üÅ #8~¾"C I$/ÎcP¡üz-‹a’d7ƒŽà þLµº6\tdÑf·3¡ìŽuyUèé·¡‹tòaÔ»ÝÔˆÝ2bª¦@×?æ¡ i±Ê×蟻&Gugž„ØA Ytd€ò7&ËQ»L˜ò‰š´. Öš=cÑüÛ…PlÔ…*tŸí\ NÞ=mÑ «‹¡p…½ 9” KÃFS_͇²ý52Thÿ¤¹ð¦ ‰E¯Ô)àãEUõSÕÏÆÜÏÜœlXÙAAŸ•dà QWª«C&0'rô‹&<oF¼=N´•2¨{a2¬¨IUO< ç™ÛTѦXpµÙ$B¯uK€w :Ýö$¸¬–³¨P~7[jå´Yÿ"èªÛTŒ&Tæ@ÈïlÝôµ\¶çPûäBŒÛ…BtÝ‘|˜æ$GǬÌ­& ôSX.¸åF‰P¡|ò¢D¨“Ê¢?–$øŠGÔü† Øi7Šj>Q m«ý¨.L.0>rôuM>L¯µdQÁß/ŠAcLží›P jÍôI?9\õZ„¯,ã®e *´ßçãiXæp@„v¯U€Cï‡ ªê§ªŸ©ŸÊŸ}‡³!k¤˜A•çÆó3@ÍèZª<Ÿ‘eZÚ¢Êó¨‰à䲉E•çã?Àïö!,ª<Ê¿;<.½n)F•çzy(‰XÍ¢Êsñ< ÷X]„*Ïý³Á1 Aÿ6_©q:4™®¡@•ç;ÇåÀç‚ UžÛ,æa¾çdú·÷õCôKUžÞ› ]­ÕÄèßî¸2 Žk-Fÿö½ªŸª~þ—ûI¾Èáß^‘¯ú±èš´øÙыک! –v?Ì  mRá¹Ùzêmýè¹É—ª'Î~Ï£©Zî´þ°¿Õý.ƒKt”]‘LX ‹lȇä/T¡ü .%µ¡ *”aÍÌä¨ÑÓ48CüôÊöx9nuòÇ|Ú}ˆ:Ø7¦[DSúÀ(·ý,Zq_“XËQ¡|ÆoóaZâ!]9«^¾ÚF}õ0 öuò£Î©Ï€˜ Ǩ]ÌÒ ãE/uè#‰é"Tè¾ÚŸ.—½‡XôášHºåMÝx# ¾~¹,Ck‚“!Ôn‹ í¿å•:{°è^Ãlð˜Kz¯ê§ªŸÿËýœæZþ§\YTË¢6]H8 ÷Ô‡0è±èõªZ„ö˜ 7gÐΡ©à?h'õA§BÈÕÞE…·9°¢á5ªLßÜÊQwë"¸4cƒ åOï)’åè«p T¯ÿI}sJ£$Ú êó$ tnXˆP«ÚL¨Å ¥AÉ[jPZܯp ¦­Ê†Ëf±T¡|ÚþE°sÅTÕ?,ÏJSêÁþYàû3†ÊnÌÂ5ÑÔæ #4¨“]ÓÁ@ýUèþ¦G`W¼‡EN)„E~nTÏ€tXsãudn6l¿CÚ->ŽögÑc=e0±Úšªê§ªŸ¹Ÿ®ÁD1,Ú°TZûQ>L‚œTcºìn´\©+G I 4Ì€AÇÜJ‚6žØ¢³3A·$ƒúÛ§Âý7›©v¬JfÄP-û&ƒV²1U(ÿëd8ãÒ“EŸ«¥€qØ8êú÷I0²í9ʼ‘ÞŽE³î'ÃïK#tÍ0 DÝ.¡!—S êÖrí% Ræ–¡Bùº;ÈÁ²ýs9*;+‡ó±;¨\L,¸~”EG€s²uÀ˜\¨æ"©WŠ`É5Bº?A^Kn¯eÑÝ‹ ÞÙ‰:-¦Ô¢½¨­šÈáÔAmªÐ~î|Ô†ÈÑ–ëä`æX&BUýTõ³1÷SüXc2[³èô±r8zè¥u],ç¯*©©ë’¡²‡%‹úN€Í‹órôÊĈi1“E×…H üì7:¾, ~12´$LýNý£“Ü2áev$‹ åÿT—¡¿V³¨þ¯TØ­¹êÁ¥@CûeÔ‡6éÀõ  ^¾žC, dèçB lÑœE§.L…X·­T›~9pE+†*”oþg9¸/k.Ccþ|¯úÒZ„>Ÿ^aqsXt¢D;¾u¥¾ú ‡þ;ŽÚ¢{ ¦˜Ï B÷k<‹àìäé,úõnÔަÕÉÁªé[ôt/,ñ´fP¡ý/^ÂÒÖ+ôýÓ\pûAUõSÕÏÆÜO»p9x»§ËÑѾ§„¹-¢Î}—ÕæÎ,:ãfÜÏ8Dm}06Ä¡ÞØ–ýıÔJãd0ºßýG§©‰7õó˜BîìN ¨—C¯°5¶¨PþÞ½ÓaS”?‹æ;gÀï€0ê`S)ŒþEý9,:ˆ¦ê\KÅ&+¨Í®¥Ã¬„`êK¤[ÚPEErˆˆö¡BùFºË¡¬ï9jrMõm:0¨ó!9 Ž(”£ßËáÁ™$úÿÿ»Þ³{Ä0hþ†4¨¸s€*tÿ^ܶÆ¢;e4ÍœÚÙ<Êz1¨X= lÏÆP…ö“‚Vïh][“ áÏ÷PUýTõ³1÷³ü­ 2ÏÈÐÈ??‡\¯ÖØ ¿3`fü1;- H÷Xê±myàXJµ¨.I÷UÔô )LhC­3Íi8uÔ¡h뽟AC3ÀÌ'œ*”_ÁfÂqé ­7È‚îÕ1Ô»}ó!G;ˆºª¨ìô×QÓgfµI,ua].¼¾Aí_“mÔýôÞå I‰  å3«.mñ(5` !‘_Kµ¯Cïä‰"”ØÊá«óª×H ¸§¦Zúó{ÔÀz9*t¿Õä½4`Ñwêr¤£Mdn–^ :—•À¡¾ZT¡ýj%°¹Kkt) LNm)DUýTõ³1õSùsä©XÎ"TyîžI£XTy.ê÷ç{”Ãw9ª<¿¬]Ö –²¨òüÑh œ8P"B•çBùkܲaŽA,‹*Ï/ÆË`Í—n,ª<ïa\Þ6,ªAm©Ì:C˜¼ú8uÛ°­ÄìF0Uè~Øû\nWÇÕ·æÞ¼8AµÈó³ãÂ8´-Ç}ÎïÀ£BûK;û‘`Ë®<ª¨ò%ëªÛr¨Ð{U?Uýü_îç ÅZÎ:é½ÍX´€³äÒ´®™>_¯v ÐñcÌøÂ–+¨ù?ÀÚ¦<ºîž é>ÃðKJ¯ªcÄXaK~9D1Þð2õWð]Å®±*”ÿ³Ù*rþðk@'.]Iªì4j?¦‚SkvЕñ?¸M'm©KÛÎ$95ÑÔák­ˆñÉ*ê‘59¤¤Z‡:/9‰Œk±” Bù,BN€ïõC½ý£L¹~„š¸$‘xáмrñ1‘3AFžmºK=<.‚¸ðA€ ÝßïÄy¶ãQµ_\ð“T·¡ÇçŠvÔ=aƒ`£±Uh¬ö<ò³¦) Š4òL4Œªê§ªŸ¹Ÿs€ãܨ¿q[.XnÄ¡-× ä‡_ü!Bµlûñ¯³6rh×#ˆŽ› VíXC^ 3¡.ïmÁZ8GVÕõãÃË9ôQ²-qè÷˜zø‹3yÓ;P¡ü­Ÿ.'6ÃÚôâÂ%DzS—˜|â´¾_bQÛ¥8±á@Ãdøèo€¦tmNæ:éÔ`m)üJ’Rg¼ç|–M¥ å{d}_ÔSç(AÉÑÁÜBãêˆf—IùÖ› tåÞ›$¥}@MåÖäinÕkxsòX§ ÝŸ­£Ø¿N£Å_JåV!)T‡hó¬Õ‚éJ@sªÐþ±ëôHnŸÞ€vÖç÷¯ ªú©êgcîgžÂ|îJ}Qr‡-W ¦„¶¼våGªÛ°‹Ükï~<^@ž yÍ¡ß?Æ~\Kê¨:îòìy w‡ö&èÎ' ïË©ÔZ½‡ÜMñC1Ê}ÞËmÓæP¡|Ÿœ\ƒ¦?A=äú|ýT¿Éåä÷èw€êï,!÷Kì*’ÎvU€†¬¬X|Àƒ*t¿Wññ½4{ºDTþc»'KÈfÿ•ýa6q]¸P¡ýnjèlè:s+ñÌ”ÝTU?UýlÌýÝÂR¼éZ*‡²Áuâ7Ôš]¹•½'ó¨ú¼§b«œ9ÔßâãD=ÀÐð~ëÈÖ‚>;Ö’,n¾GWöZGféE]Њ;m0A§ŽÝÅ/ËáP¡ü)ýùl¸‰ Áý͉lÙ~êÄ„‹œK_}ÕªÈáò¡š¯c˜­úb˵ÏZA5ø^¢˜YòƒCO™¶$>£ xT(ß•<†‰w"è“.Kø{Þ©-š§‘N{¨õÙ‡ˆçÁpªÑÌmŠ>ËR8T«‰1¾jÌ£B÷[¾Ÿs~¦ñh…{8xücù¦{`ž¥GP·5‡Ï–ÞThÿÉ-ý¹ŠÉ> TãÌ)…뜦<ªê§ªŸ¹Ÿì÷‘°«íͽx Ë®P?_‘C«q<º¾g[2'¤Uz]l4DPÍ+j°j’õÈW72´h‡Ž|2–¼è*Fý^áÒyô›—Y¾Ýƒ*”ÿø7bäHÐëw¯ÁÊÐêðƒ3¸¨É^<šp~»X'8æ#gv›4ÿövþܔ̀˜¹”è“r1jš ûžêT(ŸeýNþÓ⯀ΠØÏ?g÷PÓM"q/:B»9ñ^“H½ÿÆ—ðì&]ÛÍ›\=&TèþÌ´lð]’Ê£[îÝ„ðÜdêÎÀ£Ü#ñ>r²å‚–çs¨Ðþç"`²ñTíñh"Ùn;Žªê§ªŸ©ŸÊŸUµ©ð"à2‡*Ï*‡vŠ/ª Êó»ÓÜɱŽ<ª<Ê/ŽÚ ç£ ª<Ýú!Ìž•É£Ês/³]ü7~‡þŸý;{söÖú·ù–ßÞÏ·’ù2¨òÜã ?tºEPå¹á¸ñdˆk‚þíým·?ÁX$Už¿˜Yϼ‰oÊ£»?e~0 uÇ¡û^ÕOU?ÿËýÜ^YKþÍ.Ó]HŒ^7‚ú›85.TŸ…¹àYÉ£•횃‡©Æ:Ä.srÝkö^ì»~µhË òez®íïîK<[ž‘£ÏNãG”j¾À‚»3€G…òÇåàÍ®LáÑ™= ùSÝ©Zu:¤d@*AÏ-aûåãÿ¸ÿ\ñå9ôÊ#âY=‰Ú·ùw¸pù, ïz{s 5*”/Œ9Äý<3˜Gû\ÊRô´±¡ÞŸ3yö&(<_¬>Çê9a%³Ò׎G½.XyVUèþϻùBõí€Î/·âdŽÔÏÇÚÑ>ï9´lÏ<2ñMgªÐþP®üŒeF€~ 1çÕÒÌXT轪Ÿª~þ/÷3tÀ4R˜Û‰ £wÚÂöÔVMÈÝw-_5ˆdJPt¯‹t›h«}Ç™YC/ˆÑC[N’_=Žúul"ñsCÐeÒ\…ÆÆ5Œ˜xt£/vÎ&Wåw¨KŸf„ºñèê3ýÈ5ÝT¡|éç’ÁËLG—ô¾ G¸çº¡¡ZQü¬PŸžÜ¦kÿ¸õxq¬©¢¾žBüTèþ\­ŠbÔ¸0_áf6Ÿz¶j7~QW‚¶|ØœÐc BûuØŠqû½x´hþsè?g7UÕOU?s?[v@r u ªßºÉ^­M]{g‰Ý8”G×ùn&†³ûRïýu3 ¨!·×·žM©Kíí¸YÓãÚúñwn×…%Ô´ òUôИg·aÌë}T¡ü/Œ­àóÊ<õa³¡Î)—úÔ¥\±¤ê0 s÷=T„w\K½¼hºFPƒòöðènÝc^@ô›Ï£¦n÷Èç£Ê—¸÷1|P+âÐ~GžÃ%Û#Ô_Ü9ò·€æŸ‘sò7Ô;-6½]û ª¸º–ÆR…î¯y¶Wñbß-ÝÔ`¨Pc~Ù¢Ÿ³¾qìÛ©*ž,ãžœ9OÚßó‰#¹”Ü…GßÞ[B\ë[p¨ªŸª~6æ~îŽíIµÑ&¨£}rÓT‡:aËA2gu•U‡“‹žqèÔÄržéM}ß-™l~PÇq:¼n¤A÷_ÕäÛÉmlO öS'WèqÖ[8Êï}´)ížÉ£-gõ&¯''QùéݸA;úµ=§öå‡- æsɇÑ-ùü ~ÿŽ¡ž9êAf,K½›R :®­¨Bù^ï¯ý-VrhPçÇ:`uß·füŠ  @ÛD[ñ§ESk¼æÔ¯ '¨´Ã5nʾù,*t_m€ˆ•öŽ`ÑÀ ®Ìüm¨Ý|®×\]MþœÄ…;Q…ö×éö&××Êõ¾ ýYTU?UýlÌý”l7"^þz <ÝlïÜŠzâs<Ù6z/‡®R$KoµÿHV›$hHþt2­hµÊ¦¯é¨÷¸Á¼šãBº¯¦)î¾GýR²‰á„Ã*”?~û8¢™É£ZË—’Y{©{w˜pCù)ªv ?«÷®œúh¶3³k™„ ÆÇ+¹‘7PÃÇÆC¢Z- ]ó(R*R…ò59ñZoïÇ¡s›Ô@ˆ)uáÀ9üÖ¾:€Íuå]ºÞW +VGŠ«ïãPç¥!ðúQªÐý@^bV}¢5<ï->¿;U­k¨í»'ytéERS ©Bûs.ÅðV€ê €×cœ©ª~ªúÙ˜ú©ü¹’­O¦º·#¨ò|Êñ(2qÄz@•çw[‘ÑZ‚*Ïû4éɯþõC•çGD“˜§GUž å¯ÓØFΦïãQå¹Ö{O8­Ë£Êó÷ŠA¼Î‰IUž—Ùµd_­ãпͷ›|„}³8Ty^®¹“·XuC•ç-·^€¸39ôoï?Í*ëoóW ÊóSˆCgýÛývvÏÅ#Gú·ïUýTõó¿Üϸ×ïÉ¿ydtq|vDŒ²zÁäíóTêÎâ9¤†h”þ’l&§J‰=ÿݧCçoiÁÛMÊ£¾3GAÌ&{ß›LŽRÞ"ó·üÔ-×ü™VÊßé07hÞ‚^íQÌU5t .˜;™W›sÍ-9µŽwŽsãPxDjnÍ!ç>*ÐÞ?7‚¬k@ãZ_ëïòüG|sõ ?kêµ± ‰ýƒ:X>ŸnoÌ£1ãùí’eÔÓ«ÎoÝ€zî wŽ ¨Ðý*ã:î@y@yó·\v\sjˆôø[“»úòR{2§| ퟵ³5o46б’޼ñ„=T¡÷ª~ªúù¿ÜOë’2“=+FW­ #;–>¥žîÕ–¬‡æuÛ“›Š»Q]Ìý¹XÑ\u|UqR²€:mAGbÑ)…CK\ÉÂ^×h¤fóTë.uè5…xíSOʿ̵†ËÛùО3¸óÌiꇄ|ØüvÔNÃý‰†Ç5@6]L,L7”Ù·GL¬fð¨ã²wGU(m˜?í®? ¯sšñá[í¨^¾¿¹çž‘A/"^g—QËÙ(.åÑ »‹ˆí„mT¡|£‹¾Â˜'mzÇätðèD>»Xñ˽€Gæ.‚š…ÔªAjâ#‚~ÙÐ…?h6”*tfD-çôª‰Ý=ê9ç9ÞI„N~›NBªMêW³ƒ˜–/¡ í¿n¶œ7)æÑ !o¹¡õaTU?UýlÌý\|+ž˜ü hªLBJ¯ºRÝJžsÇQ7mîÀO«œ!FWKaÎèxöv0©Ø˜AõPü®c×Û<ô0µòè=r¢ã.ßABY Bùƒ}šòáƒÞˆÑ¿¹ðÇë©jw£àCR"KïNvÎô£Øß›l¼Œ þ7H¿g®Ô=Ñžd•÷h]>‘%ý3?r¨P¾â.‡áòzC‚ž:ßzÞhCu oB®œIãÑò© é;/ŒÚ,ö9§wç†ýYðN\¾ê8‡ Ý?¾ºšS{“Ë¢ìȇœ£ï0ZoEˆ¥Ór‚ÝÛ›8ÚAÚ¯åÓšçÇñ¨ÙS¢s٪ꧪŸ¹ŸÕ{Òɾƒa€ö)Ï"ŸýeÔiY]yýó8Ô°uK~hÁ/êæßaD²7ŒGG¥Ÿ"]µxÂ}nrâD‚>ùý…KÜÕŠ:mpÙ+kG-íѓʨBùÝV}å¦ú0ÔýõœÔq$‹Æìs ’ëSxôÈÆé¤ƒE ê¹[êdÊT‚®0<—jã©› H•_- %òÏÐÚg A…òFˆ~ª´¸¡^áð³P£½KȦn<šÛ}3É{8œúnV:8îmáaLø"ªÐýl½{œ½ÂKþ¬à´–I¨ëW=‚V¦'ÊÜÉVX$¥P…öŸO.þÐáÑõ_,ɉ+8TÕOU?s?í§ä‘¼ÉՀΎ/ v-‚v¬½Å}PÌ£cíàÖ-›I]÷¹„„EÎàÐúº²Âm2 ~Ï5y[õzêCùMç©öâ(®cŠ!A}úk°ÛFK8T(ÿ»Ï“ÙT®ûù‹;2Îê;w$ÙÐVÁ¡:–ÄF÷uIé{E¤$… ¯&éòõŽR¥·0ûÅ}­µÁ˜yº*”¯›Ö"Î¥n UOü¸ò¼1b´6d#©îØŠGu—‘ôK·9´¢Íj²ÓÊ–êbMªã¾*tý nJÇs ô[ç2nð”ÇÔâ><ôô$èŪ…ü·)€ íÏ[׉„Èí8ôÅQ3µ\ƒªê§ªŸ©ŸÊŸî…ĨºA•çù‰Çc‡-ãQåyUË Äå| Êó ®ü’ˆ›bTyntS‡øö;Ì¡Ês¡üg¶7ç[G)På¹ÅÜÞ$Ü}/‡*Ï{{ÍãL ¨òÜ絿8ep<‡þm¾d›@®I—®ª<ל8ƒŒLåÐÿ“¿É(2%òAÿöþPÉîj̪<ßcמ¯»è¡»?­5)­º-Fÿö½ªŸª~þ—ûYÃ~%ÿ昛™Ü£ûÞ Típ*WèÜ–C3 æð÷fò¨[1·|õÆšKâ… ºžÇœàM}Ÿø‘sË—0èê!Þ\å´6,z¢o7l²ž/‘+¦]Τ å{éäv‹P Ý{½¶~7µgB R`Ë£ÏæO%…Ô´Ü=äSGªYÃIÒ$Éj·‡XŸ÷ãP­¡ËIAÿ±Êh¦­P¬[Í£«{t€–;]©/-‘×?Õ94ûÆÒjÑz1Z3ê8Y±¡Ð’'Ȱyº¿pïPòÊg#A$"ýó×SOj Ãç™S[Ôesæ3©Bû[Ùh“„=Û j½Õ:·]Cz¯ê§ªŸÿËýܱ>†[µŠCÇgætˆ¡}p‰»À£á›­¹þÑÔfѺüK5-‚¼Éyf£¦m7s­4ƒX´Êrgª3•CÅû÷~ñ<º5ûq¹eÏ¡Bù~nŒ§ݶ"Ǧî(ÛL¾÷ÙÀ£a’D2Õ¡ïuR_}‡C.{KBL hY·!âtÿøc&wòh?ªP¾íl°˜´™G²[ÏæË¨lû‘d™‡9 HEúxjö­¤™ÕA‚FÅè‚ůªÐý_[­È£ék ZU>€º9S|ÜÄ«'¶¤ŽVDð•®ï¨Ð~½ÈâÁ&ÔHÑSbÔÚŽ|ñ-9½î ‘eWÉwãå­ûñ‚SK±§ àwÌIT(߈* Ó–Ùóèªú•į¬3õí‹O ùÈЭ7Àd[ÇÜvšÓk® &Œ0UTwr¨Ð}ÿ¸þÄ~Øj‚ö»ÐØF¯¢&tY˘Šy4žÜâ*B\¨Bûk;æfÉ÷ôEw¾ëÞ=TU?UýlÌýª¬ØÓ¼–m¼ÃLüaDq«)t¯ØDz¿5x{n ÖqÙpy¾‡®ÿV‘GPß.v''¢žPc¤±äV“Çb´_ÖWEè3}‚ÆYLâG->Å¡Bù»\4'_»ÍP ßlÅÄ)`‹†§„ô¥cxt™­”|?nMM. 'Õ—j>c4y·è0uò¥¼^° f?‹kÙͧ þýùí%¬×UUy¼Ú:ˆú³×˜kÜ Ð-£/¡gê#®†|ºÀ¡¥?›‘M ¨B÷MÌûoù*‚ÆF[ÍàÕÔý»Š›3¦ñhÐíq {n$Uh¿ïj^2̘ ;«ŽòÅ~úrTÕOU?s?GÕM—Þ<:¾µ-¶¢ZšAHswê¬èåàW³ŸÚ1ëØvæÐôÝ=ÉÕˆ¯ÔVŽG‘Z~äAµûܠغ)ˆGMê"Á|ÝvªPþIƒ§ÇyNb´4r9±µîè›+„לGG}Hf˜LâÐo›¾Ã"XCP5“R¸|(5pb”Î?Å¡sŸÀƱRªP¾ ŽIªÉj1µt¹¾ÑPé¿w)•ÑÈ…G“©ÎŒ97Í”GgRü!­©B÷­ô-Hó50«/ùê¶Ž:åU¸Øê‡3&Õ6?Ç“*´ŸÛæÈ›.×ãѳ ñ\…Ô–ªê§ªŸ¹Ÿ×ÎBÄš!<Ê©ê4˜ê» PÝÖi“³KŽR?ú ]ôÔ¨F® ò$z%‡Îð¿;öï$h•åÄ,£ž™ÕŽT.>Σ¾:,á–O£ å³ßBƯò4×t7ér¸Œš?öÙ4žÚ¦årIÔš K–žƒ‹“ ©_Êä°dSgê¾Τ¬õ&1úys[Ò9jA…ò¥0+ȦôW€Ž‹N¾3$èmn'41~hèË`qh| ‚žyó–Ô»ì4ãJ;Ï“ B÷ÌèC4w»ôÙ+srl€+µ•'aJ-x´Æ(ˆ ·QöÇ·kÇ£†ÜOs¨ªŸª~6¦~*|K{’»qýxTyÞ½aq->£Êóˆ²Ûd©{ ÊóÞž›áê'‚*Ï;ÄmÓUž åç<ÉX÷VUž_6 %^¥“ª<¯» , MªO"žê7et:úC9Ï…‹æT¡ü/VO…€kê-›¶Dü¬âgøðùÎë-_éÅ[dXr¨¥}™xó‰æ<êê0åM©S"÷µµS¨OøJÒ}°U(_qómÐkà>‚ÆØÏeæÎÛû#Óˆï†NÔ%cÒI­ã(ªK×+û< ÃnîæÚ½ÐçQ¡û‹u?CJ²‡ºÙþÓû©?׌áÂwÍ%è÷ÚYü“ÏvT¡ý¿ ê7ÚÔ«ÂGôî½  BïUýTõó¹Ÿò– Q9ÅATóvþpëÈ|ýµ± :}9@ýi½—XökMídJ\û5eЯ³cˆ[‹» 4dIٿلCcÏNâ\}—ÔÍ1ˆëžÏ¡BùÏ»¥hþ½Pói¸/UשC’'òû+uxt»]%×ðc4õ§Ÿ.¬5Ñ¢.f"®3´¢¶Xÿ€‰»h“[¶¤ÝQ;‚ å `ÇåÌØBÐãïs¹ÒˆyÔ¦È éVêYÝ@2»o0uþåÎäXÛÙ<*ú²ŸlrË¡B÷õ:¨“ëcöshLµ&q/êú31|òª!€¾.ðçÕÕxThb1ÄÔ0hÂæÁàóØ‹ªê§ªŸ¹Ÿå?½’œ ‚Îì>Œk÷1žªñ®±üqŒG¯Ôî Ï»ÄQ¥çvpQ+‚zÜmE.p¢Ú\¼Bº·US zÕHxé@ÛtÑ Ÿó胜öäjU(¿nÜM®oŸH@'èñ§÷¡wÔb¾›Ì£-_ƒ»GjñBqèü]Ô–cÁQNB]å{¸Ç[µj¹öž·¡ å¹áwûžˆ ghóŸ.Rƒu–õ Ôn’öÄ¥:™j4r/i“Ï¡¦Á)dÛ¨_T¡ûu®zdÖ§=z"· i˜¿“ºð‘_Wõ#c× ^ÛjEž¤ñ<êû=”üúK­YÊ ã´™ž3~/ åÛn«Ï‡'|te˜oXv‘ºì—/Ôq±U«5esëQ ­Þ½[+h›öwˆyÍ4‚ ÝŸy°™°t#‡ö©èN–[Bméaɹ¶ÑäÑîfœ|¦)UhÚû±Ð¢ÂG#5§ÈÏÉTU?UýlÌýlÿâëäBÐk>rëÆO¢fžª&šFóèx¿:’ѹŽC¿|ŽR˜;õ$¨Ññ±¦¦T$ƒ}œuxqŽÜ%¨ U«@BÊÎõ¢®5·![Î*”¿Ï{ÂgG–rhÿû ¿õ`­¯5#j>Z,ZXnD¦ÈÌÅè:ÃR²<î žØx‹L•Q³]‰c1µ¾|5Y;}+U(_÷QÍø´)é€Nùñû¥Gu'Î=hA¿L‘p'mjÙŠédå_êZ͸çlHºÿs»9”1™CÕgZ’!ó¬©Ó¸ÞvsytjêHrùí0ªÐþ“ã󈻕Æ}¼Eü~·§ªú©êgcîç= 5þ÷¬¾ý½° ¿k¿õ»[-)áûrè–>wI÷Gábtì։ܚäÑãù¹;© b7F³yk@ X@î ]º-±ÙÒ„ j!!\›¶¥€ 埾e?xÒ=vÖ€ŸÙe3õ™yObÝÝ™AÓVŽ#jIrt㘲°å4•zÆ’)½ÔØÁìÙêÀ£Q?“²žÖT¡|Oý?p¿&zü ç½$Êx¤p“7PùU;9§öY4· Vôƒzh;'›*t±&Š F:­ípò(â‹ i8JJ;–P›ßßGÊ2zThÿŤ³ä¦UÎ$‘Ë“,¨ªŸª~6¦~*lµp¦WUžo–œ&åÎëUž uã;Ty®2Õè+Påù|‡—\¨Í<Už å·øZÏ­}{ˆG•çNm÷óßĨòü{û½Ä[>HŒ*ÏH%?ªVú·ùô_hòŽçUž?|sUQâ½C•çW_§píÒãèßÞ÷KC]MS ÿgîõ ºÛKпÝß< š|5ÉW û^ÕOU?ÿËý<ä§>üß óºNú¯ßÉ£²v„×\@æÓ¨–7ZÃå)Ô ‹0òÕº9A«Û§‘ÝשòI§³åºôC<ÑÙ´…š°æ(ÑleèÐB²K} A…ò$–Cx4^=–ÿð¥z¤ÆÂÍG3 :©Ã)½·jÀ–‘ŒßÏo•Mœºö'¨¿FWR²PŸjDzptĪP¾óŽ\uçæ ÍânwiCMi>–èlÈ£Ö&j“¡1‰Ô¾3¡¤h0‡ªÏÓwâJº_¸D*öêr€G]ËÅš÷ý¨–R2å^…-=<‹ŒÒ´!¨Ð~5éSE«5€¾>^Æê=:¥@…Þ«ú©êçÿr?›ÔÜ&WmÄ<ºNë6ײ+õ…ö(¢YI=‹IÙɃԥâ(r ?ž 3\§Ëð Ô!;#ˆóö4Õ›(%q¯žQ-][ñß=nzò‹>oón å?÷>—œþ²GÝÝKÈè s¨Žý?ví<(Ç·%$IY“„’tG¶$[Jv©$K¡ÉÒ†¤]›$IB*•¤ºÏ³B’P„²%[²‡ðûÌ|¿Çù¹g~Ï5þ{žæ¾þyÍ8æ:Ž÷ðn¹g$›V1€VÄ=†¼Ïàj¿Œ “áÜY󠪟:wShm.w"èE•tZöpCÅò=[Ÿ@_>î hþöBÚ\Õ‡KFKÈÚÍÁܚВØÛ…«ñJž.šéÌÐóòŠ0O·„+veÇJa c¨žÝá{z4·Ðî‰ðuë,@wFRH®Ø~%Õ>‡Éo$èëQC¨Þôµ\Y?eýlÉý\–z B¶É1Ô¡ÛuøGRFQëUÎðkÃZ†-]zl¹Ûfû•½í¶zíVÎõÿq\—…Rt•Ñ ØQlMPå³èä}†^"ÇŽ”ºsÅòoU»«bhxh¨[uäÞ]nOK/ŸôÌëNìÚ¶cÜMq`Ø¡™ EÇài}@ßä·˶± }íÓ qàŠþü›úŽŽ×ÓÔlAG–h¥Ä­«‹ ó>šr—½L'Î/´¸ºKÒ¡µÞ†ŽŸ|ÊS丢ý\¦JvŒg(ÉDÚ»$qÕ‡SSÝx‚Nß¾‡6é¼äŠí׌íÀ²æí¢h­Òd¦òb Ceý”õ³%÷ó@B è<¥¨÷Y É/çµ5‡›f1ô¾Éü}7H;’ºÝÐôb“mZ5 SæA˜¥hÅæi`ºÚ+?ÎÚóY”_TâŠåÿnz>Þ¹CÑ[•0pq·êµ-«ê耻«XÇi­¹viŒ-×/%¾_ æ6OÖ„SQÑ u8–ëvpE¿~‚ú³#ïn´›Ó(¦´c+wääJòô’* sÞ<#'«q#|·Bâ¿ÝAÕò·Áú¨<®Øý’£–d®ÒI†¦tú‡L[’Î\Þ• ®è¹,÷îG‚ŠíŸ¶M…ÍÝšÎД啦6ê•\Y?eýlÉýl*Ë㣺õZ|âB^KP“½cÀ(|C‡Ïž«ßùqÕfÜšê ¨yþlb¨jÎm“ÓŸø¿ èøìöT'*€ æ75ÀY-Ž«¥¾ªG¹bù÷Ü+ˇS(:ñA¨È7KPÿ7SXó«,íª÷ææ¯¤¨|«Áàº8P—ë]I¼q÷À?i@}‡4Ü)L  ¨X>ïè1, èj]}6»b,E=î’ÔíZ€^èDäîÙF‹:Ô%±=x&/Tì~÷ÕAdÔœl†®¾™LzTçrÓ½’™ÏÖîUþ°ŠÅ/ÏPÑÏGÓºÀóµgÛm*l¸÷WÖOY?[r?Ã^ÄÃüµ§%è½ RÌÕˆt‡ ÈÐûû" +t77¥WŽ`°›š¢¥g_HÜMþý,òÝkúXÒdæ"AíG¬®Ìã~®R‡ñ¡€¾Ö¥ÆfrÅò'=*“%è—4ÐØ¿kþ¾-½ªY@Ñõ‰úä­O&÷{¡€nít‡=åHPmuÛq€(nˆÖN‰Rç^€ŠåËHUc«Ï]§hÕÆ«Ô¤L“¡'ã4I§è€n>·Y’×™ëþ;´|:7%ßžé¾MP±ûž‘E¤õ톪ïzDìŒ ¹¯n/ Ã ì¹õ¶ĦU{®h?‚üâî UwýI|‹Úreý”õ³%õSúÉ9±ˆr•žUJ‡Ÿd5C¥çåLŒÎPé¹×Š…ÔžÞ‡JÏçç-aŸó *=ËÿcA2„ M’ Òsê–Nz{QTz¾öL]÷È“¢ÒóÕ¿]ØÜúq€þm>Õ›F´Õ£‰ •ž÷P ¤uë®Tz^ÑU“Y<—¢{¿÷œ_¤ï‡b†JÏoœ;MœPôo÷ßÌî ‘]ú·ïËú)ëçs?‡ŒhmöŸì8…°¼ÚV =³n( »¤Ìµ1ìJLàE?÷K>1`¨™÷Á.CP“oB_óî܇ŽAÝú}}¹h7|h¯hóQÁùë,®ÉÓ®/rÅòËW ¸1Є|#ɸb_®‹,4·¹ AG9yAÒp‚þ>±Ÿñྭ=ʆjySTÑÚæ bè¶ûcaçÇPŠŠå+ìÑÅTó %CŒ6’׿k¹mÒ[Ãb÷u€ÎYÒöVq}od+wQ´ÆY‹æ¯c¨Øý›®wèÀ\@#ÝßКý]¸ÏfÞ€‹ª½Z}¥4œmTl«Ç{©ÜŸ*‚:n(‰~£&AÅÞ—õSÖÏÿå~†ôèÅ®¿Vah‘*»’­ÌUœòHÐq#·¾×rãK·ù‚»DÙM ÐIÎQÏ%¹ W[ ‡ÿüâú7ì…à®9Ü̙߳Îôž€nò0£Ç–d¨X~«ø§›½Ü5Îõ2ýÓ݉ûù |ýô› #^ÉÃkÿv€jZ„±èùF üs*ÛqÈ»]|+RÔé]¸:Tôë§ð߯]o†šÅXB£šWóô'rÐN ÄåZ>Wûº\0`hfQ(˜¶=$AÅî÷xù›>Q4ÀH‰ {IÐnm®BŽÉ®öž¨­ÊÛ¿6n$ÉIü"Aµû&íÖseý”õ³%÷³8Nžu;¯ÈPù¯ï©ãÚoÍzO†íÈe蹯“ ^þ27þ­"klmBÑ é™4¿j6Cʆön]ø¾Æ*.T)Ý&|Öeh‡’6p£ÀË?¼ñ ùa ÖCº‘ßçrVÜ%îç¾TaðO O¹1 +¨—’+CÝN £EΆ\Ëü± 8æ  £^¦ç¢2¹bùªT÷‚Æ© 5y÷]ÆUÞ-̾q Pÿ´Ô$¶ˆ;ùÜxZ{M‚æ¼É‚ᣨØý ÷;²ŒÂb‚6wc“öä’ûá8{ ÷ÇÃÓÐû† WlÊ`0Åž¢MíÙýÑCeý”õ³%÷sñgôRï'ÝñôæVÊÍ?»èû‹ õ5È€ÍÃ2¹‹µ%ïŸ'rFÁŸ4î½W 0!*PµÊÞ`!äqµ~æS ýòð%]þBQ±üëß-$ŽV‹Ý³z/Ù3Å™;ÒZVË+rU ­ÀÈ@àº~W£Wó_ST­`=§E¹ºkâéEoC@k=¼LgišsÅò……_€_{LšÐý-kÇuÝùŽF¾< è çÁŒìä&7ü„… ɶøÜ¸é ¨ØýÓ=Y+o'‚fíÍÎÎW妿>êcûzáêøV¸‹+¶é¬ËtA— †êÏèG¼¿Ï•õSÖÏ–ÜÏßÕOh¬S:E-'½¥ßDqKç_Äa µW¸—žÛr?_ÝuA¡ÜègávÜš’’d:sn &ÛQµ£¯ê»Ž®(ázD]&£¦´áŠå/uˆ%±ë<¯‰#3úrå/„A^{¹Ÿ.†ù•g¹»‡Óç=P´&¥-UŸbÌм÷?©š¶& õõtÿ°ž Ë÷ãÇcÔ?IÑ¡KŸ@õkneÞ$v-}" ¡Ƴ/‰ ê×Úäýhî ²Ñ# b÷•·h³ˆ‘¶Z§Í>î.Aõ=:Ò‹žö€>Ù4Œ5uñ$¨Øþà ¦0±ìC §o‚Ô.Þ\Y?eýlÉýt^ÿ‹šw÷¥è¨ÊÌëîjîU§r0»Ð–¡ùf¹0ÿFE7ûm…úÕƒêõ`:Ï0ãªo;*Ñ6¿Y€ê¶n¢¿õ¢h#¹InhµKÙÂýû—‘Á·ƒ½÷ÙŽ^Œâ úWȧÆ[\ƒö®«¹SµHÒfG†>3"¼gs' %a\Áa?xÏÄ˧m÷ä*Ÿ™¢§jo@Ò4 ‚ºêôe¦43ý.­¨7¦hBŽ©M«Ðê®miØÝ2‚ŠÝ·’ëÃnû(P”Æöd;èRîJϬSëóÜO#JèÛÛ ÛÿuÓ4H;Й¡zWÛ@qó[ŠÊú)ëgKê§ôsìLwVWº¢Òóîýã  ä½•žÇ[P°_ÏPéùJE+Aál”•ž{Ë¢ßýPé¹Xþ”eräªM< Òó7/ÑV} Òóé)`kù¢ÒówÕ¶°û¬9Cÿ6߈…EP•±„ Òóì›#¨ê»tŠJÏ¿ÛÂÜl¿ èßÞ¿¹²3pާ¨ô|×ä TÙ$‰¢»¿â"l/Paèß¾/맬ŸÿÍýLØÚÖì?9g¾„{`MP—É¢™2’[ø©´q;ÎÐT6ÀõÝ~Ünrfyù½Ú£.¾ÐXŸ_‚…{ ›Ó”Yo<®ªÝr·ch÷Öñ`}GP±üWìo“­·tzwÓ)¢ÛdÀ¥^$æT%ESµä ^èÁÐÕŸfÁâß—tw¾ 4»xs]޹ƒ“J†ž´¾öõáŠå‹2œ ºkOú&B &,8Ëý’Ø…ÕÐ\¸%°ÂKÎèú O .ûCr·x®Ø}7ñ³øC€V¸÷§ i Ü*Óxˆ³`h„\>Ô÷xnŠŠírU"×wCwµ-OrÅÞ—õSÖÏÿå~š¯»DælìCÐN몈ç¶Ü™;×€¡6ê ðÈ5–¢³ë€ëÝ#}W5ªŸÌåš·s`ŽZž€>Øl¦ M ¬ ;»¬ôP?sh*šÀËïXíONöŸÂPÓó È÷öNÜã©+á­épîà©ð´UWî¥Î[!P9„ Ù fzî@뮕Á‰é zàY6LÖ¯$¨X>÷2¢ ¨Â­¹¤xI&÷O¬û9Ú€¢UÕÌv Uccˆ÷n#@;hK.úÞP±ûso§ÿÜIÔùùIšª’Ä5U'í¸‘ºHV@4Wlÿ;³Ãþ;¡1ÃÒÁ·z(WÖOY?[r?ý÷Õ“æñº5¾ö‡ïÏ5}uêœ4¥S v;IÐÒwCAûUC>ÚáÝ$HXà%A ¡™rÑUú6T²ÊþwZ¤F:»ã+ÚÛÆ›+–ßûîDâQáÍÐÀáÖ$ëX·Í•§0îi0E§tú㢺Ô^QBÖžßèòÇT%m·ƒ¿/ÉzbÅÕ,2lðž bùÜŸ‡ ^S’•S¨Ì_<)’kùG™ÒÙ uy‘B÷ìÞÎ5x G–Y†ôcuvýK;@Åîo!wéÖéI€v“gyáG¹MkÕ$Å­¶q·Mvܸbû·ùì‡ÆKŸ)š2i˜NÐe¨¬Ÿ²~¶ä~æ)«€y‡õõ8Ø_Är?6®ên¿¸S@QKж Fíµ hšøW Tn2½sB•ûCÿ =Ùî$A7 »4³)úµ{72rP9W,Ã]bß!™¡¯^œ!ϼÏrõ£šàü™.€v¸Ñ!˸f½¬Ø—狸V'ÂØ‰Í š¹f¹à hþµÖ,ØàWôç{i~ÁÞÁ~€š½ 2­÷váVìj’è¹û1Ô¢‡ªP»q·äá V~0L‚æjû²w×]*v¿ãÈîÌîÙ@»×göp˜;iY Y¯¿‚[¸,™NáŠí ]Šž«zm^2¼bÏ•õSÖÏ–ÜOßÝ}¡ãÝKu¼¡YO›¸ê#a÷’fnúè10Õå÷âùT2)7 ÐÓ_ÓiæÇ¹Záêìé¾hŠÖ{QââÍP½…­ØU#îfÓ‰Mh5W,°Ù¢YÄжƒûÀ4å+\S•Ó°.u; ƒŠ|Àc#·÷‰p&÷|y>z,r%K-ÜMÑÿvÆÅ¡ A±Ó©â¶bŠŠåKÕ«Î_ž¨äÉ2Öa*7õãAïCk7%WçêÄèÑd}G®Æ•¡ +š¢b÷]›1švÐE{m˜í¸(nz;kÚÅ(‡»yê8Öí‹#WlÿÇò289+‡¢ÆÎ•õSÖÏ–ÜÏ!úÐŦ ¿ÕõÁ!Ó˜›9Ê^©&tÖLðwˆåÚš:0§U€Fö b•}úpϤÞ$%¡Ù •wðºCÉÜ#曀lóå–Oî nïJP±ü­u,aç‡2†ÞHñ€¨Âî®ÖsÁÖ­? WfÁËñå=§Á>^ö£èÙºJ=ÀC‚†.M!^«Í(Zø³tœ‰b†Šås+?N S,µ ¾GJëæq½–ÉK: fè1÷ tÑïþ\³Ë%ÝפPÔnçobsÛš¡b÷kü—³ò¡€ ò`öOöqoO3aröä£iŸ,©}6CÅöÛ™Îíô€>¯}"D<ñáÊú)ëgKê§ôT:š*=_}ø0XTzþ´f³[²€ Òóe®ñ`¥º’¡Òs“F0ÒìKQé¹X~ÝÛ!P³¨€¡ÒóÊ+`âb?‚JÏß-ª¥›µ´ *=·ÿ£MÃ/2ôoóyŒn"³\×*=·œ’IŸ–a¨ô<çBܜ׋¡{ß"É›eôôTz®ê—LÁпÝ?êÚVÚ¹t û¾¬Ÿ²~þ7÷Óyy;³ÿä£IöäwÞT†*4 !÷¿Ìç®u1óó€PÒÙË»»Dbu«•€N¼¿‚n¾ðB‚>\VMì?|ã.mŽ#…‡ý(úbüGP˜Ê=eXþ0P±ü]O©ÿû{¹: ×ušÈ=c¹cV¹õ;ÎP¥)G`[û¥Ü§3  ×ûn\ÿ_—AWÙG@«É}aÍ@ ô„)ã¸bù¦+D :¹þ 0Ó†|hçÅUQœ ¥–)ÜþÛ ÛÓn×ç"e Ü)¹©Ð{Z0Wì~bö p Ù¨s§Z¸7È«Y:Š”›'èL ‰.œÂÛïSj'÷)4±Û20>®Å{_ÖOY?ÿ—ûx¤épu5C势 ucwp•k‹É§ì €Î‰e¤÷ÄéÜùs)³PghP›¤cN÷îÄ ½t7òF(ª/æº(Ï€¼ÅmvÛAÃM¹bùï\=G¦,¶tÑÏ)ÄÏb·10rÿý\º¦‡ˆd‚zŽ<®º–C«´@®Z­LŸTµJPfwÆsÅòEk„g‘+ªu€èLçú‡xAÔÇ5Üš«¡êÒ®å×+Ò*Ø=½u6̽´P±û!÷`©â~@“ÞW‚¼Y·Ëì:Ágž2·|¨63‡>\±ýŠesafpw@ŸN;Lä4öreý”õ³%÷ÓÝM‘Üì´—¡NêºäsF×6ð+¹±c Ý]ãÞ\ëCV`nvš¡öëó!®î w²nÔÑä~U2›”ßÇÍÍ,!oíƒmSïÏ\ÎrÅòËÕøŽ5OôaÙÚ¥]7âì^ðì¥Àí¸m6øwêÃÝpå9I‚HnáŽi$kôNn¤¡ ÓÒ$AWFZ翦¨X¾:ÄmŸ!C#ö¶'v÷áî•üx2˜[gÞì†p7ŸXCnd›÷³ œ[Û…+v¿6è \)‰ô÷ŠB¸ã›À ñKbž·? ¨äy2«Ë×a¨ØþˆòL%=P…Pv)gWÖOY?[r?çø.$5K2tÇäpbµ?‘rÆÏ¨÷G(ìaÍ=»ÿ%T.šÌЩ;j`GÓо?þ²jÔõ°=£5Œ¸Gç–ZÖ¢«’Ò{W *–ߪû3ª˜¨Í«ÁLË2’_0h³2·ôìH(ýXHP“úM$bàD@ßž”ƒl5}î¥þt¦Ó^ŠÞ5–=Ïf¨X>³ÈÑù!MÝ:`Ho4¿7×åÍ5òdãDnuÙ1bÖÃ…;`ö*¸Ò èåcûHŽ~5WìþçH^y ÐS}SÁzÙ î¹úÑLiè.†šK“̽ \Ñïo6YX­Fs‹ƒW±?¿\¸IùD¢ûûp],-H«àv³Ê‡Z»ÚµYÞìØ9_ËßpÔ$M>ÂÐÇG¤Û¥£Üí7JÀbc;®Žy"¬´ ¤è™È@è>¼3C/ú‚ÚÎd z£x “;ñÇ}«úFP¼¥ÆP±|M®Š°täC‚x·b“Ì]à º& ÜGA«`k€U5P$¶w1´äD†Dƒ^ ¨ØýûŸËàQ¦.C=*®C÷“Êܶ[Ø–‡)ÚzõGºy·?CÅö'N²e¯%2´Ät?ÕÙWÍ{_ÖOY?ÿ—ûi™J?¿FÐ/—ïÑ¢&Êm33‰¼= ÐFÇt¢¶•û¸às¶‘Ôhy8+YÝ‹¢4͘ͷØèp‰6 (\LÐ¥Îãà€-:òj,©U5%¨X~R8 ‡&0tLä pɰq„ìŸGÑš,ˆ <Á]4R~t hô¤¾1º—€&u÷ÞúšSTa¿»ïfÃP±|g÷iÁÂkË ú¸óx0}Þ‹[æ»vþÜOÑ Oeý”;­‡õ¤C»Ü~%Ìyyˆ+vß8³BÎ>¦¨RØ-¨–?ÁÕÛû™¼U‹c¨UónHµšÍÛïvã6¹ý“1tª‹9¼ÊYÌ•õSÖÏ–ÜOEwuVÿ1‰  F³ø‘;¸Zó]Éãæ@åª6Œsì}„»FÁ„åtɧ¨Ó¶8ºjðnÝP#êh¾ë;y,Yzi÷¼…9(ÝZhé~úðÝ®XþÖBáÙ ?†žZw ¬ÛØsý«U ñM;î›[ÃAaëî×à½É‡¢>³üÀî­"C]o¤H² s'oÙ³ÖúrÅòOpƒ‹öJý)'Fpé…ðd†d‚QuµÔü÷3ÍÙu “Q Ÿ/G ¨Øý..U ¾rE+ÆUA™Î+ Yëež÷(ª°©ª2Tl”óð»¢ÏF§“èJ\Y?eýlÉýTº4—y>èIÐ…ÜYô¶Îšpñ|á  aC4XÜAoníþ´nôP‚:4§Sÿ3å\³‡™B£¡€ÎpŸÖ:DsoÏaG:èqChzòQ‚Šå¯p/‚†AC:âS%,Ô©¥hÞ@O8$?„¡!Q)5ïEÍV—ÀõÇÊ õñûï¢hlÓx¨õ¿nІ.íÙwü(*–o¶Z*Ü—´xVèxƱ®²]|(jìWr3Í´ß¼hØœx™ C?xÂÓ]»¯Òæxõ¸‘Ö_¯€éw¯ hÈ©7d]a C¯w°û'›+¶ßÙ?™Ò¿OÑÍ^³¡x¶)Ceý”õ³%÷Snên–’|Y‚¶žÊ"=·RÔ­Õ\V«5PÍ ¶bó.‚>UÈzGyqwì4gV÷ƒ(ÚqŒ;»»6“»{ ?+Rñcèº5ª™7› »}Ö0íØ% Ë[¿šg[PôzÍcPÙ0R@M/AêñÃÜQ*%`ÒKÐÅ•ÌöÞÛEPÏŠpùíx@ £n@T}.EG®‘3ó¿h¨X¾r× r`  G¡ †j8q?÷¾y‡ ÆÛÓÀµq ’Ž—àäÔ“½£y’6wTì¾$þ4šEPå]W ¡1–»<ò ¬lÞÉÐBj(æ%AÅörZ¯êËPÇØÖf]jÇKPY?eýlÉý47‹fc^£¨QáA¶ûEG†6¸l`‰qs(úæúto¤ÍP?yy{àn§_…Ï4‹»Ô¨{òý"Ww¢"ù8†q­ŽY ¹3îrs†Â·—=¹bù×{‘û÷4¬© ü%o¸› SÀ _ ½·¹ÀL§"®éˆÓàúÓ—û)U€ö=wp-]C‚e0wÝ‹¤çÏïý_Åò%œ¸ÓV„÷…ü½§¸ú+÷@è”`î^:¨Ñ)îï¥jÐÉø·áƪŸº–+vºïEª¯ôÒy ‡üàW}ôæqVYÀÛoâõÚêèü/Y´™+맬Ÿ-©ŸÒϣ̃,µÙ˜¡ÒóøÛí™âÆPéy§4kP™pž¡Òóí½úBÊw†JÏŸGé‡cÇ)*=Ëï©_ g¬{*=¿4çQö¿¨ô|eÄ2}ýp@¥çÖ‚TmÊ èßæÓL8Éö…€JÏ×.!ó4s•žÇm΢ÚaŒ {ßjt.(±N€JÏ“Ÿ!¿¯·ôo÷+*X@Æ3ôoß—õSÖÏÿæ~:÷W1ûOž®³ÙÝtNÛj¶A Ñ2„~“QAƒ fqoçÜ…»7/R´¨æÈÙö‘ Ã2ó‰OzCyjÁŒçv\SËÛ‚ò’¡õRèÅJÝÛ1T,¿sz;:¥ñ òé4m)WQW œÛŸ§èÅŽÈÉIí*§ïKô?É Ü)/ò /#¨ê±álðü½€šzìe¿UöPT,ßsåU¤¤m0A þi¾ü|Î-tßGGHPãº`hl,Ðú ¼S¬Íô‚9mó¸b÷?z †øñWºÌ~4Ç•qôMáÎ:mn×Á}Á˜ü’ bû¡9ÞÒÔ`°-ì(oÅý÷“õSÖÏÿá~ÎÛT*á» ºdïCø¡w[¥ü† hVºûRUKÐ÷Æ +A…dM|E§ï?J+pšÑ3ŽmºÐæâYò‚[à/xpÅòÿù©Ä_œtù4CÖ<þ7zSŠ0pÂr†jm×&ÖIÜÈó_i ro‚®³va ×\(*Wè}þ¤gC³¾> GöàŠåÓα‘D¨kj)Jwv0äZ]÷çrªaëŽwGŒ¤¤È™«£@%ÚJoTìþT¿%s¿”¡ºY´*ávVÛKò‚‚)úñQ¦¨¬Ÿ²~¶ä~64߇Ÿö€NȪ‚óÜàЬ°×R•ŸÀ\=®RÔÆb(Ì\Ñ¡‡ÃÃÁ:X•«ítƒ†Þ÷àZ) •Ô;ËZI²m,)ºà™&„,˜ÁPÑ¿Ÿ™³™sÞN@½ŒœX¿ „;©ò Q=XÂP»u+`xê®W}‹ÛnÅìkÏ\'¦quÞܢι;×ÐÏ\±|ÑE*ì}Ú8@ËLlXÂCîRÝ­ìËÕ»5 NrÓTòòÕU‚æ6^êTì¾ê|˜ SÌЇ:¡`.ávÝ´)ç¾ûXb¹bû‡ï}ZC$èŽý¯ÁN©  ²~ÊúÙ’û™–Q|皯%Óº-\eë]̤ã|†ý=5<È-kSÖÔPõ˜×ŒTyƒ¯uÎ14ñrqÜš?ÀéÚ# zj“ÆÙ„*–¿¢÷jfwæ+A¯h/cvs–sŸ\;o~gèâðüµwz¥ÕS+¹_;Ï#ŠGй¹}€]l7äÃ905èKP±|mlXÛ~]Ðî«yXNÐõ‚HšE1 {u¦ÂK¹ÎícùùÞQÌþn@*v¿Pû Hvcè “$ЯÏàŽ¾òÚh_¤hP›L¨’K'¨ØþdÇDÈ™h_Ã<’çyŠ+맬Ÿ-¹Ÿµf©`Üu? wü¢an·ƒ\§_tsy.C·Õô¡½[qk>¤Áˆé€ÖZ€Ü»ëÜÈ j¨'Aßf›þG‚öQúškÚÔ~½P±ü1VVLn÷òÔ e$s ·§è£â; ÑÝ«Û$rí0A?jÃêû ½¯!@×öÜ2©0Û´A³ᇎCÅò­ªÎaš!zugÖ0¡B‚..lˈE ×ç[3ë1 \åµ6Ôëu?†®¬Í#wBwSTì~ÞâT 9ÁP•Y~„› Öì¿ hç«Dçg©€ŠíïaBË7ºú)°–¶2©!¨¬Ÿ²~¶ä~^^ç ù[’-2´…»5i܈×]‰–q6CÕîf“‰­¢¸Þ~ÁdQß @ëO6 þ&‹¸§Gn„þʯêÒß ´]"Ç¡+´ÕAçæ @æ­gÝk¹bù¡¶#3Øz¢oÝ+hžé/îܸPø£ hïåÐ<ˈۦ·:X­Ó¡èÛg‘då±R :àõxkœ' Z“ü Ç' P±|+ç²µ•”¢3¤°¾ýú0´üì6ÖÎP³¨-LË«÷ÃØI´ts£}ûÁ€õ½¡b÷o<£UbêkM¡w·nçw6` ´B‚æ÷ʆŒÃw *¶ß80D¢fíCÐÒ¶1$I·' ²~ÊúÙ’ú)ý(-îÃ~f*=O5x@\*=oxz†¼® ¨ôüAçP¸QAQéùˆ«YÆè`•ž‹åãíEßÈ©2TzîpG žëª*=¯^¡KªFž§¨ô|ú“Átÿ @ÿ6߉uAlŸ‚ C¥çmFÚ²I y•žÏùÓSòdí †þíý\ÅbºîÏPé¹YÜyÐŒNôo÷_ïJ<Ü#ýÛ÷eý”õ󿹟٠ÿçÏÿÿl»àµ³ cè秨ê«dn±ƒ»Òµ” ­R˜\ØY½u4…˜)ÏåZÂÐévŽ :J„xfM¥¨Á ‰`¬7“¡:û á9ÕäN® ]÷T,¿Ñp|ÊÐÞ54°ŠK¾È1óàF‚~^³•ý^¸—»àh94û[S4Ùú'L ¨…ªÙ0ö×kã8¨xþE@Åò•ª¦kÏõ`¨«B-‹wàš/ƒÝr»Ý9 fÌâ†ØÐ?„p‹²ÉÍ•†»ÿóC«~¬ÆÐ%ÇÙü{ºÜ²ñO¨’ò­™µ‰u/Ö`¨ØþÓe–`:ÝŽ¢ÏÞäB†â{_ÖOY?ÿ—û¹Re-½sž¡vÏÛÐöeÜç˜oÿuú˜Îz»c¨{BëéhD§UD3UÂ}g¨‘G²úíÝQè½â7PiYêÜßûZ›yIQ±ü_ï'ÀÅ¡Œ¡ŽóÎ@Ó¯$.̜ۙ†v£è+çÓLÿˆC\üêN€†Ž.Â~©Ü-[CáüÝÃ\9¯À±y^ϸbùÞEyÑû³04ÚâŽÄ)*‡{¬m%¹_v Ð8ÍÁ‚ë¾§Ü‹6?%Þ+6r“³²£gqÅîxq˜5;a診HV°Ð‚›yt2sUÍå†Í3>Œ¯áŠíO’{ßoMÔó\>¬ŸQÀ•õSÖÏ–ÜO¹“©&9#ªšÔƒlÝÿ”«_þïÏüY»¸ c,Ù?¹÷§=¤Aþ×;¾±a‡¹Ÿ½½…Þ -¼ÿ4­(ú΂ڦ @k … {rÅòG¬È× »ZMArn÷Ò¼ öÚ"ˆ;×O—}»”ÆÕèåÕÇó·N"s§Çpu{`Ý+qkOâÆXRT,_Í¡GB²V%C5JÈC¥§\ë9§èÕÕ5€î¡Çö-ææù¤²•")zùp(=¯´”¡b÷Û¨ìgöÍcè¼™ãm'îˆ2+Øà ±~+ ¨Øþé±#A)ü6 ^¿#òŸl‹ãÊú)ëgKî§×Žxâ1øC÷ŒýDRÊÞp¯о÷¹j¶…äð\ÏIáì„F@¢æ1µ/Ï$hý·$ð:{• µŸt`ŽÃ[®ä× øìÜ%çl {†P±üMßòàöu†¦ªŸ;ÓÒ)Zk”#iØ–ÊPý›ÉîYG‡‚Æ£•Û=®Ðs A›º… ª;š¡É®g÷æŠåÛøs ømÉPM[7°*«å®¹”Ýl h´æz6ÞÊ’7©Ž”öË¥¨°}–D®øn>*vÿÞòµ,FÓ¡fæ²E·pOík ^ ÔMÑÀ§–¤JQÑþï]HÓk¾4Â(Sȸª  ²~ÊúÙ’ûÙM[Ll^34põ ð¸öœ;qwùlîÆUlèwnö㦠ûhW‹І N†ÌHäšÿÛ0¼* ]›€ŠéfŠíÀº{½*3VŽ“ ¢??_Ïs»QôÐã ðJú<Uj=JîaèË=ab׊F……«y¦š4H Üú84Þôë6€¡õ~ sgypÅò Þ ·w^g¨Ë…3pÓ.“kg²”míWJP·úÌ`Ñb½[gÀÒÞ[2Ôùûpjw«–+v_ØGب>^ ý}º?‹öòæ*?“‡Û{¸›>œ‚B³®Ø~Í#ßH®òx‚Ön"·ûMTÖOY?[r?ê¹Ãjç ½´Ƭ¹Î}¦¤ 6qýrâÑž%ÇM¾º”+ ¥H,+’ú¨2´KÕ(™Ñ½È‚®ÔÞIÌnU™¢·C̘a´ bùÓgn€V/´çp ˆ9ˆ Âí$dÈN zN·iò½Ë}o ú}Gð£-œÏKàεù÷w‘-扦´¶+†Šå³»Rr‚Z=ö2tØcÅU-¸B#ê†QôdF©dÖ®Ò7p6Ÿ¡¾ú@¾óaŠŠÝ/٣š/ø0tjAýöúÿ©«)oöë¼%÷µ÷7h[. bûK]}%†GO𓲀Mv‚+맬Ÿ-©ŸÒÏܘXб¢ •žëߊ&^5Ý$¨ôüeÞæÙ°ž Òs÷³ 38Péùë¸3ld;U†JÏÅò?ÒÖ†Z¦GP鹄KFTPTz®4³Ù´aêu@¥ç3“ :ªaèßæ[ö­òß*0Tznxº±²˜MQéyȦ6 çš  {km6ÝóÛ¡Òóø!Qày°/ »ßþø Vàmèß¾/맬ŸÿÍý4×è`öŸÜÑU Êëò½ßêñí}«[ÄÖÎ %hÛNÉìiÅ8ŠÆíRdní—Úó?+(èÌÍí"<Ó:ÎÐ{óVÀ}—+\¹Œ#篬[νÒ&⢸bùÍ ²‰\ì @ƒŒIDQ ·äf<è–%q~Ù@nÛ{ÜVºç˜ãÜCu“?ɪ¥èŽ¡alµ§A{ïcªC*–/#†ÁÁ¸q }4õ)ŒqéÎ=Ú:WV¸q y[QÍý¸íiõó7j³Óu»À»Ÿ4¬Ž&ôKÔ·_[–§yŒÛ¼û_q‹ûaµ{#çÆÛ»é=èÍ4• 7~ƒ9Çl{_ÖOY?ÿ—ûi9v ùú®ЊÇÝóIßËÜ×0vò­C]¢²ÈÁ^ÜI׎²ïv¯%ܶ¾¬¶Ø‘¡wòïÀ­w‡¸IF_àV„wøÉfèê~œ Õ§ö7ÌW.¦¨ãx%³„5õô¶ž-ÕØsÐèeÝØúu§¸cØ[Í ¿×•9Øõ`¨Øýɶ=XåÒX@ ý±wªÁÜgV±Æ½$he½m«%ÏP±ýÕ΀õÆs€žß¥‘æ\Y?eýlÉý,¸>Î?vÐÃí¯Ð§{ ¸_§}¥5Nq v¸(i{0‘[Ô¡Š†Y¦sAð&niÜ›ó‹ iFk‚ÖDÍïÅ»¹nNƒpy†š™>Å·Â*–ßÂñm|èËE—è·1^Ü…Ú1‡#Ü´qllG«{ä(qÞºœ¡nçÚBìÍ_U›G#˸…‡ MCÞf¨X>Øöf3œÇT¿XɬjÃgîŽáKس«[M~´“=JVçæŽXKö|¿FQc£’ÏÇK *vÿü1¬h² î«&²Tóµ\]{o’qÌ”¢!– Ës*¶ßžt!þLôÇd?b<÷½€Êú)ëgKîgcÎ,òç@cÂvrz·Úh5Q g¨ÜÔ‡¤›ÝJ®þhmXåàÆÕþÚ>–SÔÎö4I;ùL‚šìûHTzõbèÚÉ !³â U“ÇÒ¨µK¸bù]¢{Ê&ZÞ÷$M»ÕÛ­ÿ}bA«±¯°Êqè“6é¤>é‘­ iK _ª è„c§ !/“¡æGSÀ¾ì£)W$߀º?P´ó븽ØÄÍœ½­ 1$¨oŒ ËèÖ‘¢}G3»‡^í¨Ãn.`¨Ø}—wÓ™C°- ³»ÌaMɦÜÞ¯™ÒSCnÚ´töò¼!AE?tšnÔR‚¶¿=†> ¨¬Ÿ²~¶ä~NÊsd?^†ھ؋´ÞÅÝÖæ Q®Ô`¨æ‚<òžERôëAИP"Ak†}£Õž. ˆ †Êãú¾¦Nm‹£hôHC¨O& ß ˜]¨Xþ°Uki[.AÕª.JR“¬¸G—'…—›¢oË4áàC?‚ºüÓŸU… ¨[ùZv¥Û†~L¸JÌ4ÖhÚrGȹ½Š¡bù× è™Ñý0Šû¤og¦_|Œ¢Nv¡ô†N6wslqA]QC;g.ƒÁ^Ó¹b÷ÞÏfßèh̘E¬·e{®ÂÞÖéùX†®zPI×å$rÅöÍÚ"ê§AvìyÄ•õSÖÏ–ÜO¿à@Ößb! Jžì~;î¨æFÁË2D‚Þ_bE½䘢]ÏŒd—,Æ1tÔ„ÛÔ;ë8÷Â÷VfT¢hHÕm·?ÈÍ½Š¹ ?ÂÕ¡n,åýw *–¿ø¿`Ü«H@{wÜHÌviqësºÃÞÄ€.:Cü>xs}‚mØÄ’x†JZŸ¦FVr¿µ6Ó7|GÑ×eסà…= bùîWÛC—£U‘ë;MörÚ„x]IQ¹{ƒHmi²)ê=ì=™Þ¾#E›TÓ¨êϾ »¿bÜ"f›QGÐó™þ?¹ÜÞ+¾‘œ÷{Zì<ü+STl¿Oÿ¶ìÔÊë€6Ǭa0i WÖOY?[R?¥ŸßK£™ÕÔÖ€JÏ£óÚ°÷%¨ô<åM‰9{ƒ¡Òóäu«@ñs* Òói‘¤zÔ•ž‹å×?QI ^öPéyÇG³èB£C€Jϳ번uúm†JÏ]#BH›èßæÜñ,Iþ¾PéymÑ âõóA¥çÚ¿ÿä.½Êп½oå:‡-± $¨ô|þâiDÃ#[‚þíþ#†± å¡ýÛ÷eý”õ󿹟†“ÿÏÿkúÿ“oåëW:íd´©¾ÌÍþÇ^Ë$hƒO.;|e'Ei›v¬hâa†V,N1MšXËÕ8›NÜŠîp½>„(çn}‡·‚Nñ3nìŸ|X+–ÿkô}êx‹2ôù¯‰ôYE1w‡R"x—äq'õ~a‘û¹Žç?€eƒwè¯wàå°F@_ûá]ìzÏ#âôïT,ßÄÅûàɉL@³8@ØöÜÄW‡„ç§îp݃ÏQ«>7¸­»æ‘.ór7DY²Áß¹b÷Ÿ+߆BÇ@@M4®Bƒq×_ý¬{9›©a ‘\Ñý3[› ûR‚ŽIz£Z*ö¾¬Ÿ²~þ/÷³)î¨Ц>#ØaùniÉ4&®+ Ç­¿P5Ó¾­ŸÂJ^3‚†í_ÃF0†ŠÝŸY¶ÿœôðGØôüÿéö£~´ å>j%@꥛\±ýu™0pçqŠÎ-| ß›J *맬Ÿ-¹Ÿª7¿ÀÝ^ “ý,îÞ¥hLÇGÄ"P_‚ŽyKª.l"híç=‚… ahò©Ö õ'KTòa¸b×c¾‚ÙÌã÷(úöÌgx~+@‚n~ÉgË?jD_8Ó¶Ž¢on«€Wk/îçûHYµCÇ4¶‡øÌ9\G“[°ãÏ0îàßP6_‹¢†ïJai‡¯}ëγo*–/rûTÖsÑ2@¼œÆ†%tâêvø*©·ˆ¢vÉ ‰A•<7åùi¡dÛ,†¯&Uc†Tìþ = Ùé Ÿ-…qrǹNÂbºÙ'“ëÒ¾ËnÛ+¶ÿúã‹évPIÜLØüñWÖOY?[r?ÜûY¶®Usz+µÆKЄ£Ý…ÎÎUµuˆÆ>~Èw)´ŽŸaè·w ù؇kZ*g<ôA=zŸƒwÙ+M™Ô‰îMÙÊ}¿º;xååWèsŸ$T%KPÝœ=ÄìøµoXýġڊ—`FÖd®‰éO€ýí¹«b Œk6ô=ÀrïÞK¦^Ív¦¨X¾SêcÙ¶SAÍ­ìÌ´¶Зí޷“¥_7TÞ]Ý9òÛæBûê ÀP±ûצBïÜ£€Ö«Œ‚q懹fl%j%úìñ(ÈM[HP±ýöÎf‘•ÐéÖÞ´ëšÉ\Y?eýlIý”~–E=ƒ&ùß*=¯ÍÒcÙº¡•žO hm6Ÿ¥STz>õ÷(ÐP7TzîÓþdvߨô\,ÿä]iBã•/*=æô:ïʦ¨ô|‚;,YpPéù§ÙÃÁ¤âAÿ6Ÿyv^§NQéy¼NkH¨¨ô|KP!ý=¶”¡{¿âv螨ôÜcéV°Ÿè èßî·Ð±BnÝп}_ÖOY?ÿ›û™ÔNÙì?¹µË\–p9Ð{g¶³&ïÜès'X•• wQßS¬i@•€N9ž×ÿÄ2ôpý˜S1œëê=‚xÔr ÖxAêƒbî÷>õtÃóR®ÍYw¨0:ÀËïðí1ìÚ0ЪÆËàéÂ5·Ig tžQCåz›¦v0Ь]^ìüõ¡ÜŠ‘Û$vop?ÿxAãÝã *–/½ÑŸÿhÑ€D¶¶¹÷˜çp‰Ú­ÇÜ@TbZÁ=;eX‡ßåöÓþJ'%&sÅî—mɃܗGêw¼¼¢¹k/Íß%Ü9‘ÇÀ³ëX®Øþ±ÆŸ¡iù(†*~ÿº6ƒ*ö¾¬Ÿ²~þ/÷óíœÖ3c  s¿Ç³¿ÆpßWE1Ý·G(ªúЙMQèÏМ òf =£T£¡6 44çT>ÜÞiÍÐïÓS Úð¢ÍË|M¬ôêtGI+æéØ›¡bù7æ¥BF’ »¿ÃjínÀÕõìõ=w†ž’k .Gp¬_̧T èâ«Ô)fEç–ô€=§$(¹¿R|CËWY™Åö*í%(µÈcK†´“ ¯ËvÓ& P¹ëØ.¯Ü—ñ­Xù^7‚:ïûAÜ ,%¨è÷Ÿ¢KÐp5¡Ë$W Îß‹»½a%xuùZ€nŽ®ÌVl3EÅö'Øž€Â†€ÆßÒ‚”©]¹²~ÊúÙ’û™6ï$ ܤ 踞™,.ê"A‡vjÇäÒç0Ô.I‹Êm[À-þº²l6:Óî1q’Œâ~Ö ]6XToÜ~Á`q£½Ñ¦¤ {ÌÐßgoAü¹q\±üþaK»½€j:Ž€÷ñ^\ƒJSȽ u_L>ë™rŸ¿5$]Ê­(ê¼^v\DPe7¨æÝ @7v `\$¨X¾õÙLaóiŠ&ôOb ³û0t|GOæâÕеŎLåÉ8‚FëyŽÕ,@µépây½”+vÿJx9¬¬YÁPM³ë0sÂL®uÂz¡oo®^è1I"9ÃÛ2Ƈd?œ-A'æÊƒýB†Êú)ëgKîgNûfÞ`MÐoª9LmëSSÔÍt4™å>Š¡ÏËÈ65žµì~ OЬ9ˆÜ]GѦ5¿$Ùf3Ôãa92$‡òl0DŒˆ"hÂ;arš CÅò—|S€}–êT@RžŽå Q¿ˆ£º/Eig»ð[?®}èê†22ª<’ߣ\bvøEý‚• Ó½z‚Šå›°}/û5j1C}Rg²©Ã}¹}±•s•)š¬œE×¾=ÎMxáÅÜú¦Ûn–°ÚF‚ŠÝÏ,¼ç=G1´¶Ûu(¿¢ÉÕUüÿدרš¿íà!©$ IŠB¢Ôþ¬™$·$!$$$·„ø&I%I%I*E’rI…ÔÞk…\ IBr!÷ÜÿçÁ®3Æþq>ós{?y=˜cÍùVïï·šïʸ®+ >yWlÿ7=Ðq•C‹¿ç™ß)ªè§¢ŸM¹Ÿ¥}sXØšŽÍߘÉzNæžj¬'³g¡ã5¥d±ßv‚ήÞ3ß fhæÏ*¸4°ž¢‡{n—±gÚãZ-ä}Xϵ¿x¼&äÚmû Á•ßT,ÿ°áä÷tU@‹êŸŒ ”‘.ËøÁöêÜiè§)µaǸÇô'3“½»¸ù͙ꦽÜ/rØ’ðaË7(¢ÛoÅÐøã¨tl47eG7©Ä_JÑg£÷Ò¢M\Ãâ›R§˜rn keä AÅî×Ù_…Å ›1ôí´ËÐ¹× Šªz=ƒ#uc¹Î{â !ñ AÅö»½ofãÿ™´ôÒXo¨¢ŸŠ~6å~¾´õñš¢ƒE±hÓN õJU"ö·4ÒÑÖº Ü;ù¿Á_jFЫQ/`¥°P Ë¯Ðø¤E­TÎAžs3@…wÜ£ƒ¸+C*IsÉ7ŠŠå<¼‚”n)Ðêv{ˆ²ÆzHi!{_HÐgÝ¢Yf–7E:`÷tÄØc¬è*HQ¥™*ìÁôõ °m$Û:^¤¨X¾úqg×s¡ MnIêSq3Š[B ÊóBtá®àgrˆ *«ÛÑm"í^²}ÍÖáŠÝ7ù,k“JÑ´ÑE0ÿónEI1Ÿ7  n¥B÷£š Û_;Ê>>=h㟹ä·ïL®¢ŸŠ~6¥~ÊjßldëÚ a¨üÜ¿J©ÆÛ*?÷-L„|Ï<@åçe¯¡¬PPùyr»zØþëEåçbùÕƒoµãq•Ÿ¿PÛÎ.íud¨ü¼ÇÇPf|O“¡òóçsèŒÃ:€þm¾CaÍÀ¼ÇP†þŸ÷^íÀÅÊ Pù¹*KÝhÀп½ïj['5)*?/®î«*v2ôo÷ç/2#J7^ èß¾WôSÑÏÿæ~>2U±ùOÞ(©M§® õìüŒ²>Qô”f zãhàØlÿµßM©»j­í]“Kk÷rz §W?3´o¾LÊxÀß³ƒmêôˆ¢_Œ£î¥ ËqQ³<CЊ¡»™™Õ]lqjLZŸ¾]àVž¿OtVßehZÖfHò’r7U¬gïݶr/*ï&æ[ ¹bùÛ?¤#ÿ• =¤HW)ä&l¾&1¿è÷§…ôiÿ‹\c«S`~?†ëöè818s‡+vßpŸÓˆa¨Wïaì˵xnxÂ!vxb0AƒZn`Ê•*¶ÿÊQØÿ'—¡ÍëaÀ©\±÷Š~*úù¿ÜÏ—ßа"¢.Ím®>×|_:Û`Ÿ* gv±Æù‰õt‡'7U^rQè;ü÷Rh*|œs˜¡÷M ÁÀC“{éÒF˜ùÒœ«1² £@P±üÖiálÊnKŠNxëþØ3îÛª v®Ð@ÍÞ’!j\SãP´–¡÷ò2¡ÿ¢"ŠNl7¬÷Çru‡S÷ñ{*–O)¦²ðfc9CMŸEƲûÜ)‡2«¾‡håË¢Çÿí|ßž5ÙÈ-›HÝ'&IQ±ûªÅ]íœÌЉGUX¬Ë~îÃÞ„¦wŸÆµúŠŒ{›EQ±ý]:ÖÂUI˜€®òÚ ›^·TÑOE?›r?}ZØ2 jy¼¹W’#A»,d‘º ¸Òš•Hûó|ͨÑàæl”U+®qfäo}" ~¹wˆÕ±(‚&b“”Ç14u¯”,P­æŠåo·i$+9¦ÂЃmYÎèîÜ€6'heD Ï<¥q¥¸§c|¡Ö)I@[è^&Süç4õöTö=Ô‘¡sž‘²ª\±|ZAÚð%óCOOq©÷-0&ÝÝÐ6ÁLó Acÿ™å0_†Ž|º¶Û.Tìþ˜Á÷©Ñ†ƒ ÕݘI3‚q¯éàÓa:AÅò}ïÏ”¡Á-ƒßTnÕF7–¿ehv\&}’¢Sì'’•n7µý6‹WŠáŠÝO™¿‚6,Í`èIMºÒüßÎ~Ä ¶ú1,“…~ÚHQ±ý‹õÒHÖí9 =2whßÚÃUôSÑϦÜOã×õÐÜÂPÛ¤ ºëÂT,ŸcG qä*E‡çeCQë–ÜØþÕDwÂDî¶A=Áá>PkýI uQ PÓ­ß…Xc\±û¦ËÛ¨KahÚŸùdÃÌDnÃôv%ô仦æZTl¿“çCÐ*ë ¨ñ«`HnÀUôSÑϦÔOùOëûéÐ9.Pù¹±¶Ð:z òóp³ã4âÊU@åçV[ŽÀõ ¥•Ÿ×ö8&™ÿÝ™¡òs±ü‘-µaº’£€þŸ|BàœpPùy»?5ð¾È…¡òó1kH}’=Aÿ6Ÿû¡¨qZ@åçðÚ~6>%¨ü<HcFá€þíý€ê½DõfCåç=öXк™Z€þíþÚoÄÁßп}¯è§¢ŸÿÍýô¾ dóŸ<^ÊJÖú h~îü¬ ­Ý¨ÇW&ÚZÓ‡|´‘»aXKpS1ç¦O”._ÊÕ­~J{¬6eh‡-¨OßLnÎÜ`xÈ’ —­³”nÊ2T,¿>ˇ© K:ݶmåºõ8FW徊™ žS¤ÜþÏs ³Óv@/Ï_ 3ïŸå¾Àó–Cã _C 1– bù”&‚û£1 Óž‚­­ ÷É™i¤to. –¦Æt¹y>ww˹HÕ(™xþ‰› ¨Øý½…SYT^8 ‰úÌ,%ë|7|ü×qÇJ'C]Ñ%®Ø~åû“ÉÝàD†Îüè .=ò¸bïýTôó¹Ÿ×¦Å²““6StÞ‰]lì×;\u»˜ï@@OY¦2³-Ém9€òß(QÚÍ>׿>JìÕ=¯0ôr^:,îwˆkyì;MZ~‚;½Ý*¨>¸Ÿ+–ŸÞ¾ÍÌbèýÚ[ð~eOîâ—Põ.“«ä] ¶ý¹sÈ­ie€æ±¥z²¹Ã^ƒpƒd®ê´W‚WËm\±|fUƒ†ó/Šr}™Ú¡ÜÙWTY¿ÝY€LžÁ"æîàVë·‚MÝ®q‡¥·f®%û¸b÷ÏG/e?ú.´ åjæìÂõYn)›~ŸqMÛ²%›\¸¢ÿx|ì63ôÔ©·páw2EýTô³)÷Ó?w'û¨­ÃÐ/ #™Ým°‡E9ï’¡ËË6²ØVåULg 'ÈÐy7°íú2TkÀ Ȟݓû»!lÇ èLp¼‚«ùé)-š~HŠŠåOÜz²ònPôíÛR ÁÕíS®Ý¿sUZUCщ‘Ôàø7:1ÁP£Ý=g–DÐø]™Bí?m(Zš {\¥\±|~€MyGkô7ùíßDTË)Œ9éèÆ=;Y—œ'¥S°šžvܪfë‰ÕÕ}»¿yáz6¤=œ¹‘-ÐëËÕL¥ÔçÁ,m#+ 9%;)*¶ßeÑYX8þ4AkžŽýº€*ú©ègSîç‚w[Xàà §´š¥w ænjÆÆÌ2âÞ‘ì§Þm€›wím½œ~+tÞ0Žkêg ÉÂ9‚Ú\ß-Ä¿Ìõ–¦ÐŠã Êß¿UrÅò ’á( šÝ, Š“­ Zo” ×]áJ&/7m@ ³ d;RÔbÅSRýy÷‹émXÙÝÐ[`©úE®X¾q_>ÆM@µuŸ@€ÿn¢övæÓ.]@K¢–0ïÚåÕ …I»´œYƒf¿L@Åîg> dS:kÚÿA Ýÿ Aï•l­1^zsa*Œ{< P±ýn}õˆÑ6‚ºŸê.»÷­œ¢Š~*úÙ”ûùåòtövÉN†~±`:)ܪÏ+„ˆsn ýóäT¡ wM£!Ⱥì¡hï !I=• JSé¥ÄÇ”[™(­»ƒ¡ j1PtÏ]‚úFkרXþÊe±Ð¼ • ÉßVCÚºëÜö‚ü\þ“›=ý² k!p7D¯÷–÷講r„ 5 ;©õøž€ö›uƒØ?9IQ±|Æ—añ@@ÍždÀšÂhn¡}ÖïÛ+ŠVœŒ¦‘fZ Õq?O—?’º+o={Ú¿Wì¾Í–ìëÆ‚N1]ÏúEïå>ö±(*ôäv-j>§„+¶¿a\6•Ìб‘áà·;‰«è§¢ŸM¹Ÿ¿g·g9=3ûþíù!›Û¸¾3<Šñ¥¨ÿ8SHóx& ™»´ˆŸd Ï­š±ñ»gr_Zö†Co j-;#Ç¥qÝIk:Ýï WÏý¼ˆ=Ê˯ßÁnÞzDЪê𥶚[ÞÁI6Ý5œ¢Ùó­lZ7w?8/Z hPã2Púu’Ûоâü| j£Ñs¤€Š~}ûoHÕ@§þ34Ã㹋“…Ū íÝòé;õ,Eg %T=%“;sUdàŠÝ÷+òeëç´çø¥lv±:·Ç&õ¾P“rcV{O— bûïŸén íuø;<ªjAQE?ýlJý”ÿÄÆm¡v~y •ŸçêƒÿÈ÷•Ÿ/^Æ +U•Ÿ? yÏ'3T~®ï{¢ò† ¨ü\,î²£äˆnAåç$§€Ü‰b¨ü¼tgÙžy PùùtÉh:6# пÍgØê;y·f; òó>Ö=Aéí[*?×¶š1.€þí}«ÇsY¹ÌS@åçµvKŠ+Pôo÷;¬¹ •W5ýÛ÷Š~*úùßÜO•£à?Ù*‡Á¹·Õð?á+ª¸N÷û‘ŸÃ½énM¯nÛÉõµ]Ÿ¬æTÉ:—8Ì-àž{@dŸ¸3âeJ%¨Ò¬ 2Ê“14Ñö$ÄšÇËǧš¼|ÐÙ=g‘ ÕÿÖ.·T(+3àÞ ‚ó;z:cÍ/’×y×´y•»ûV߇ËÊ!èí »ifæ=ŠŠÝÜ{ŽfGú85êÆq uÏKt|&QôÖC5Xrn4CÅö}Í.\ÙLP§—Y²†·)*ö^ÑOE?ÿ—û¹id1L8« (¨žç®Ã¹ë—«1ï—Û¸ >ÓÙÄ…^ÜŒiÕ€_Z¹åõ[]FÑ#FZtÅŽ> íp-œ´ÔIàêU/…ŠÔD‚&ÆÒ`Z,CÅò+)Jr^3@wZÑŠª<®G™= .¿Mн™[˜¥º¶€FÿÊžŽf¨ÆÒflKR ×9b?ÉšÙ@ÐüœÉìË!®X¾›'}Á(ÎP«·Îòb;w®_ƒl[J<·Ã†×Ô÷þNîè@°FQôAë¹°Òm, ¢ßß>û “M" ¦° Ò.¦pWFdÀÝGö uœÿ\Ÿ¦ž¶Ù€v¨kÏšç$q_%³7Fm šÄ$/[0ô½ãdøé¸_@-†õ€Fa€ŠÝ7Ö²ìÀ֭î5û3¸y=<ˆË»?%þËÁÕÆ¡bû‹×F“›«Ôð†‡Ì¸ãcªè§¢ŸM¹Ÿ‹s¦BÑËB@cóÂ:Æ·È’ÇÙ1Ô@7‰D<ìÎÍŒ™ >_S ê¦áIbOãŽ1jA-UÖR4*n @ô24±VÆ!þ2t÷šm¤~´” bù•£4ØìÓêµð~@³+ÚËÐO7@mX%EC.àžÐN@%5ÚBJ+JÐ ®Ïi¹g+î—±½!pc CÇ~rÃ¥;%¨X>ÉS`;fª*uc»l¸îq-™ƒ±CíUz-›p[¶?Aí 2]™´™}yÑŽ+v?Ú¨#LHKt¦½ÜÎ?À¿{z;¾¦è}×·PÐe7AÅöO7x+¤ÞÃP? v‹c¹Š~*úÙ”ú)ÿùòAš¿,T~®éß ü/PT~ž£³•*u;[ˆÊσ×w…4é†ÊÏç‡-§Õ³'2T~.–¿¾W5ÒFQù¹UÈkÒÚò*Aåç!'‡³ÛPT~¤MM|îôoóõrõbÎóÕ•Ÿ/¶ëL2Š0T~Þµu_v¹gEÿö¾•ZYû| òóÁ0¡ »Ÿ,Ï…øc›ú·ïýTô󿹟×î=†ÿdV»Z:?WJÑ}žS_Ûf Õ³ [&ET’ßÌ-Ú*¼›­ñÜ©-ÁeN×!¢ F„0‚–NMæhíŒd°ÔœÂ­juM˜Óë#AÅòï¿ÝƒE¨»1ôœsk6Oi3wÖ6%jø®†¢9 ¤ìÝ®izU ° ªw“… áû)jâïFŒÞI¹gF'CÊðÙT,Ÿìkk¸rŒ ÚÇÈFúš»XÁ«áú³•!Ô¤\#hè€rbûÄÐsƒ›1õ ¹b÷+ÜO@»«ëMOƒªén¼äÜéULÑûoŽCȨ‚Ší×;{Â^wtñkØ{p>Wì½¢ŸŠ~þ/÷³€>¢)ý:ûÓMz}¼=wOÇ#Äûí@4_-ç*åN òáê×ö¦ +ïtÍ+CÈNp4eÊ;áŠíO‚^ØJº” cèÛ0)Æš+–ßÍ䨲‹¡ÎZá´ÞáW/ÏVª¬¡è2Ùrxš}S@• ´áꢫu¯ ›Ó¹‰ëâ€õÛès òûÇa®X¾„ÊÛ-Õ¿Òœ– *ÜÏìéq£W’ß›|°pâV½ cFqgtÌGsÖ§@`¨Øýò52°ÉÐßçÀïÓ8nì‹aðǧ5 N6ó÷ÁÛ¯îpš|еÙ0Eº`Ý4‚*ú©ègSîgcª_ãÉÐS5»é¤AÜ‹š1tÜG_@U[ë²ÙAnÜ¢#ýdú#¾ÉÐ…'(éŸú‡¢ã»É>j”¡¿}È#ƒA ­ -‡•åw j0â'›?P±üêJe¯†eè7YÂ¥ñ9Ü;¦›À/£% —= bê2®[U&tÈAаžÙp­ÌP³ý%ôxÆ%‚ÞU=E"×”QT,ß Óô’Ù[‚¦ÎÐbù/r¸¾:ÚÔ¬û\@©½ Þ™.Ü/g]Èð}Î ­°À„$w*v¿ôÚEpŽÎPó™¥PÒ‡ëå0‘žîx“¢åç„-+C*¶ßgñ$¦? ¨e¶2¤Œtg¨¢ŸŠ~6å~VwÖ¢@ÿ6ߘƎ¬åÈ­ •Ÿç[¨²†ü) •ŸÛ­ÜD^‡,ôoïIº 3ﶤ¨üÜ!tý±K‡¡»?ÊÇ^ætä“ýÛ÷Š~*úùßÜOÃmåðŸlðž~}´_ìHXË<ÞL¤CZük9ôš¢ÎeÓȰt´ï×jÕÆ‘«]î‡7•QÔiN*{J†¶¼ú›jùNæþÌW‡zÓlŠŠå?6=f¯Ö¥è¥=™òÒQŠ&›®›©Û}–ÒêoDr?-C²šÝ,zÎ^ÇmX³Y€ñ„k¹ÈÂÏ„rÅò-zú•ìûi èšÏñD·X“2L)m­úuƈœÞ@Qß]ÝÉÚN© m¼¾BûmáŠÝÚR™]ódè—å¿é§-«¸Å¹³¨R¢‘€j×ÒíÉí*¶?öò|ˆÉ{CQ»]Gapp®Ø{E?ýü_«(E½'¨NõhH¿©¨ÿ”ðeÉ Š^¯°€ízqÑövQG@ *–°kûÔÿÏIx•ö€ëÙ?ö×-4¡Ù6H3ìÈg‡‰OÆ®Xþ(\øc©IÐô€Óà5k÷•°ŽÌËòÔ&XMZ?М+8&‚v§ U S:w¤hæÛs0}WbZ–uËçþ²¹¾ñA Ë®HlWùpg¶wËê=¯>ŠW¦ 諨H¸52˜¢Æc[C7ó\‚ŠÝïsý95¿‘¡F¯ÑYÊ[¸†‹dŸ¦GrS—µ‚#‹ösÅögZÁP—R‚$î‡Ê¥óUôSÑϦÜO;kÈ·¨uM?H œÀU ÑÂgÿú»æÿ»ìÙR¨è‚7ö;=¢*‰êìôÞ> õyù¯Ÿùê»{àA<ºnå:nû@Kföá*e+è».¢¢ÿ}e†™õ•îË#ûn€û,«´9NP㜭²OGÛËÐ,µù üÁ™ ¿>|$ÞûÎqS.š uÞ¸f§Õé²a íçZYögUÿ!ûîjAÑ•® nò>‚Zd¢EŸ¹c’£h¼²+wçÏ.Ìü­*CÅî—tÍ¡æ•[Z°d­ÒŽäÚî «mÖpõ&@gv†¢bûO‚ÅN‘€ºžò%½„Õ\E?ýlÊý kÖ ¬4ª§§qKÖqCf7!Õøf5&4­’pýÇEÊŽ.±c¨{ÏR"Ý£ËõÙЂæ½õ]ÝŽš®=, «óCÁpnš }uc¸†¬T,ÿòmY0Ìs öA½ÓB®Íʶäó–Óõ®ª'ãj14^K¦mÊ ¨¶v-i¶U@­¯ôƒ(—†Žs9uJú\±|Z!ÝeYG/QÔ¦ÀPXh©ÃÐ~ëJ„ší ë>œ.íôoÿTt¡'~Æ14¨¶;Ì•lçŠÝ·¿îM[äF3ôx˜]ž˽?r¤œµ%èõÑQDp‘+¶ÌÏLÙÌ;ß ê>Ø\æ%äÈPE?ýlÊý¼6ú9Ýe  ùNqäÀöm\ŸAOi±Š)·ØÃ”ÅTá&\÷óì)º8Ñ œ|“šýÔŽ´˜x–¢'ÛK b•:CÝ»^z?ÞhÆ’8Zâ= bùÕ¯FÁ©››½6 ’"¸SÿqžYú 5·ŽÛSt‹³2k¶,‹[æû^0´c¨jbö‰ h¹´£ðmí¿Ë7_¯7)ø1‚¡¯ei¤ýÖùÜᮩôî'‚Öî4f5%û¸ÅþPzKJѪj°ÍõAÅî[=®•½h¹›¡)·¬eãÇs«UöÒbíOúê†:Ëeo(*¶Aÿl¢.Uf¨õi¡WÑOE?›r?ßZ’˜×;u)Ý,¬1Œáöé2ƒ 麗 ÷'-c·¾é hѱo¤Ã$èÏ’YBåF?nÞÎ8¨~œ¢+ßžã’f}ò©#´x[CÑ“ñ¹Ð]XGP±üÃöLƒèÝ;í¿Ó¾ûÆqíXHÜ×QÔíZ)L5¸( ´çR“÷ÛÚrÎ@ÒÕ~®úˆ£t¸±!×Ì,›ôÒØÏËçÛª9øß]ÏP:&ßÌ~Ä‘ÓÉОsX£EMßCÃ6”¸Ù²[% »Ÿýh¯d÷á= ýüèˆðêÿ-.L¡Ïüƒ¹qU~¤Ç¹ý\Ñ¿¿:å‚4í8EcF\rlAýTô³)õSþsêÙ4Y\@åç‰y^¬¢ß*ŠÊÏ;Ω¢Ëo,T~îérâŽôT~^¤¶ŠöE*?ËŸ¨Õvبü\rë¤%ÔT~¾ùªüªÛÃPùy~ÚNÈÞéÌпÍw*c4v b¨üü®û æ÷¢òóe÷èû †þí}»µIHø†ÊÏC9BŠÚ†þíþÂÇypz·) û^ÑOE?ÿ›û_/…ÿ¤RáAéñ;­´èè4iüÔ]2TûD–lQ|C÷™w"ÃÒƒ¹®Â!!àA½Î›@pzG@«cÍØ­ËaQcÒŒ>¤»ž”è3´k­ØG†T,çC‘ää¡']zA•ø‘ÜÈðDÒÜÕ‰¡ðIV?\ÆUœ Z[6´êòXüÁÐôE͘÷®[2tRT=áèÎP±|WÏ|¦ÀÚê5íϾBÐMÎÍ¡ýùí Õ·삸 FÛ«î€ú%·&}¶µãŠÝ?ô蕎ôÙ´{Tv…p—Mh o×D2ÔÕ}X9MáŠíÿlÛœÍuí¨çØÏ4°ä…€Š½WôSÑÏÿé~–5Zëz £¨SZ?!gÀî¬BF¨Ù:†Ú»jƒ½‹ wÞ©­$}ó#‚•™IÝõïhÒ?í¨u5EÉö$îÑ ®ÆŠC´K@AIY'Öû±6CÅò›ùͪúHП±†tÖŽç\²¢',bh‘û.Xb`Çm;X»/´ªzõ8„ë¾äÙÀP•ñSAÖo'EÅòe}¤&U ÚJï [! Òõî ¾{C¿'mWß~ÜÀà‹ÖgçèR´Àõiþt CÅîwˆø@ë‡:y€2³îoʽv)lÃS)ú¼ÌÌ/÷'¨ØþŒœ´¯÷fŠönv˜¤³¯áúC!±S}HP‹Wïhñç\®X>Áûhù›S2Tßx(-é=ƒ¢ze›¡H¿–«Ö6Š;:qÄÄùh†6ºIa·›RTì¾æ$Mvs— ©©Ømk=îŠ;‘dè±"‚ÆR¿{“¹bû=T Ãx8E?œ…˜9«ªè§¢ŸM¹ŸZkg‘%Þ­j¥±›TºpSZ9À§3úuó¶„/u§tÞ®2b•½–¡~êŽ0}MÀ¿õ ¿&Nô¾E) ·Üʵë}ˆŒ­iÆõWkÏŽ2‚ŠåoTmÉΪxÔ=CŽmÐÎ_ÓARÉuJ€L­$‚f:j±®C_psÛ5gÆótI¥9s~]JQ'—3²/[f3T,_Ô‹éš« •fö$›KOrã¶y‚áµ4LG€aÚc º€ì€Œ_j€~šp›Ì>Ò–+v?SËÕh ¨~t¶ûŸÜËmÔ˜®m E­Sn¶ ýûïRŠh …¾!\E?ýlÊý¼°òÙKnÓ†æâ ú3@F¦—¿à~ˆŒèÆñ í·&fÔ=¢è”Q޲@ãP@û\¤WýFrc³ ^(š3Ë’œÉ‘0Tô÷sI?f³Û² 1„õ«NÑe!ްÚô1Aó:|&+<ã¾M‰¤û;M¥¨I|¬ðÛUÊÕ=Ñ \븵íÀòCAÅòõŒÊ&TYJQ³VÜå(×Ü¿üZu .#v‘³Š¹î§»ËWëJQ‡*¤æÅP†ŠÝÓÉ”™­KÐ/ñCØÁæ·¹^YÍdއ0ôbâ2àî:®Ø~÷ÑÁ¤ŸA$ öF­èŸ·³¸Š~*úÙ”ûy+Zl.Û0´µ£ hVŽâ¦ÜU!á‹Õ=÷´AæÝ²5×ß)´ehð,²ÛNÐEW?Њy)ÜÉ+èïkú¼¹\?¦' ž»‘ƒí&*–?Ç{(³§èÊ3öãéu®{ó$÷ÚA‚†ÉÌó4]û 9Ò;‡¢vê@Ô…ëÑs,uª’Z:m$Ë;7ž bùzõ‡l¥hÐã áo̳ÍVø°ŠtñnK:~c,×ÝÛ¾/^ËРéPãÔ‘+vMKV{¦€ iˬ™Oé>nÊG¨ðoËPïyc °•ºÛ&uurª$è±o阎3d¨¢ŸŠ~6¥~ÊRFÛC²Ëh†ÊÏCfäѪï *?w?Þú¹Ü ¨ü<Ѫ™è“KQùyRÿÞìÓ%e@åçbù?ÍèÅ”ž¶b¨ü|þB‡­CQù¹ÆÇ@pè3ˆ òóӳ;[0ôoó]uYÙý?JQùyÐ--ˆëHPùùâÉ)à³(K@ÿöþï\UÎ&¨üü°e9ÑŸzƒ »ÿ: "¡NQôoß+ú©èçs?3"ò×8øÇ2”¢É[Ç@Á–!Ü}z´{—Cܵí÷Ñ*c†~ªƒ#Zºãò r'БûõQxq&” kB­ÈþËuÜ@ŸðÝÁÐsswË„+–¿aÉtÙú&uÚÛBV¾t¸€6Î[Ú—%hI¯Áà™<‹ -õ#àÉòzî‚çN ?½ «G"wÇwáv¹”MYësÅòÍ6¯¦sWk3ôÙöú^u ·a¬ 1:<„ûmÕ’¼Ð™7‹&Tž¤¨ï„ˤæÆi®Øý÷J0†2 Iw6@{i[î•Ïó¡Huƒµ UØ=Ûª¦/ôÐ"h{É+rϰ bïýTôó¹Ÿw_Œ—Þ¹2ôX´l²š¢O,§Þ†0ÔÆêžìÁiÜ]+Ûë)^€.²5£‰w's&Ì¥šéu~݂م à6û! )½'CCæ™@zRG†Šå·¹©-)1Ù-CUk· ÅÖEG6ÔÑ[´^Ï€,ë•Á½¸Q Jì4 XêAbú—”½ÐeVïÕtÀXMêgü”¢bùÖ”n u£ú¦m3*½;•«¶¸ŽT<›ÇÍ}Ñoyr {L³ êÑRý-*vßìž_ÖÔ¸½/¤­èÅMm,|ÐJ$èÛÛ‡©ûúŸTlÿà‡g…iC±ôË¡^\E?ýlÊýŒM ëè@¡7lY`HP“‰‹„'¼:ÿáÒ¾†;ð޺⛠ñ?k©tW(AçÄé±ßC€¢r^QÉe†º·:³¼(ÚI¶æÅ¼!¨XþÞÛuˆwl4Eûí]Mv÷•q?Ü—ÕüYJÐkë“iÖѪeßFH]÷Ž[ë±KºßlEÝÚ‚[‡-Üúoö¾¹† bùœ65X+7ÎbèÇ¢Ž¤ýù;1h"|é1“›øh5˜©ÙrËR6ÿËkííNÓ[[rÅîw õ†v‡z¼ ræpn”çê–YBÑà‰ÁôçÆ‘ ÛR¹ {|òÚŒçþœÓœ4×óbh†‰ hu‡+Öÿ~iÚdЦYһ깊~*úÙ”û¹»¼Žä5fÔðIi³±„›?Å Þ>ÄÐ e'¨ÒkÁ­ý–EBËŽP´àí@04žËõßÝÊà<÷{®)ÉN• 'ê~‘§½f2t°}Hi[®Xþ‡:ÀBCe†ÎÖŸ¬T¸J¿³è)cîÔq†4}¤ ×°W;p®šÉ%5®0¢ó$D?×N@UL“É·õ™Ë—cªé®& ­ZkVÍ5¹UcÀðR0E ·ñ²:[c´·ÛBÑÏGv@ØŸ‰»o¾x(}t45y Ø÷qåîê'À³ä ¼ÔV.ø" bûýM®’ë“‹(:ÎÜV®Ž—¡Š~*úÙ”ûYœGF5»GP­SÉ«:®és8::‰¢ß›;‚ÎÜR:)j9(. !AÐFåA¯¾Ô†1û«¸7«#H?ÝÁ€ºÿö?Ö'è‹“ÈÀ{¹bù='Á±ïÍ*Kñ%Ïuš($^÷bèWëƒd·›;wéôp &F\µ¥ñ0ä5£¨¡ß0t_,E[úÂŒc}Ë'ù5´ânSÔîî0Ø›ÆU¶ÛSÚç ¨iÆT¨úáAP½ˆõP›g èÿD³Ô“+v? 8ÅÐýÛ‡ÂÕ©žÜSÿú:,ÿø‚ ÍÕh—ßÍÛ¯ñl<è·:JÐOëzL²TÑOE?›R?å?ú­¿ ¿ßT~^ðt(Tl.PùùlÉRxîa¨üÜδJ¦Ó×Pùy¤gõ«;JQù¹X~ß”`X\MQù¹Çu(½"0T~¾©u,E¡òsÇÄëÇr@ÿ6_ï­ðåú*?·šÜt ’*?OqÝ/ò5ýÛûƒç÷‡a°Pù¹c›fl|}Aÿvÿ¾dße€þí{E?ýüoî§äànøO>?;žÆ¾¿EP-æLo<ÁõŸª ÷óöqYùñãßnôr„éfýJÌ—ÂúõM¡O$Ÿ¶õf¨†Úv2¨Á‰;l³øµ¹AÐÿ…_À P±ü!‹Ú·+& ý-ó!ŽõæN¸XF§Ì^Hн¾Ð³fCôãE2Ìð Aç¶%«ÖþáÖéùwYQtꣾ@ÏîæŠåsÐŒ%%Ê ³·„4ŒÑáê Ó#7’ j6-IˆUíÊ­‘u‚nIZÙ8–d¿—¡b÷Ã'u£³ÔêåÚ ò Ô¹ $cŸt‚:Û/Ë.âŠíOé+£š šò>A=LsÅÞ+ú©èçÿr?H&ÑÐ³Û êÛg4 Ÿ6…;e“#)óãú̵ _徊[3ûèPTheåmªt²±¤œÀÐÜ%ãÀƶ+·~|Ýñ6 »{¶¦×§ùPT,ÿˆAÉÁ`}†vº{¨†jr«N ƒÏèRt\‹ôÏõƒÜâIõ²[ß¾t¿4ƒö~\Ä%[áªÓEõ.ðïktË7ù…ôºÑ¡­·v…>¿{sËc êCƒ¤¨ÓTW¡ÓŠ.txLœWfÔ ¬d†/¹b÷‡º „^ƒÔÚøpx¯Æ]~ü$5·í- }nÓ;»â(*¶ßs[º}M_†®Ñ&5K,¹Š~*úÙ”ûaNó_´!¨ÇžÎôâê­ëHV=ëÿÝ„Vܾ$C+zü ‹b bMæHäú¹CŨ R JǬPÃoj0[i-Ecb< tôA‚Šå·ÖÿDlu¿S´¸A ‚TnpïG.£Ž›^pkI';·`¨í§×´M+BP×m_ivFúOð]"¹ÝP¥ã^BŠúd‚ŠåK­¶†¥y½ª2 þvåÚ|¨è\оª·'ù’ŸÜÉnÕ´$i"AÉÉ'´×¸»ŸôÕÞ¸¨2T5ÄN,Sá&¶  Y]Ûsk[Å ¾u&\±ýÊ.‰[³N }ÕÜV|ÜKQE?ýlÊý úFhøÊ5lÜ-+Ú°æ´C_¯‚¨?)ºúl ÑpÓch¿¬:’pÆ„›âFf©þ›¢Ÿß–·9—¹b÷»Þ ɵ-zãÙl¸«ÄUé}“$SçnÐF{EÅöç\7‚ÙñNzwéWÒr¨Œ Š~*úÙ”ûédrSêþ²V† x'iÜeDÑûîTã¹6C}ªÿ‘=?˜«UÅè çPŠ>±K¤Æ[[0´¡£*•é¬'èÊk§hù¯uÔ0ó„´è`Z!ºÉ_v>»DQ±üo^ÁÒöïeh‰Ao0žì(ECZ´…½Ë(Ú¹Î:'IѼdáüzŠÒ±{‰^ú npf4TDp7ì €T‡Ë7Ã)k®StŠÍxj±—[š«¿Úš3´v® ´Ð2ã V@]uˆ€¦½oOû«*vß+u!¬¨úFÑ€f>p¡ë{î®0e8A‡Y^%k'5Tô÷õÑ亟& §µ ©ÛrýTô³)÷Ó$ö´÷¯Ÿe¨$®+)I\Ç•V¬Àb CÜHùygn×Èw2;2»f…&GVÜF[tíÌ}jKu|ÔzýYTÄ<—¡ öCqÚ#‚ŠålÛìÎI4q±>¸ØT¥Ç@HãÊ­èßJÿ©áž±o6/)úªÁÊ—l•¢{fÜ#§†4ð4‘¹÷ù EÅòmk ö%£)»b{“¡'0Ó¡'OþIÕ¸?X“w ö€é9ºõÓçŠÝ7ü¹¬G¿ hièF`sîsv/lkì¨Sçp:@O+¶?öÏYºëÞ%‚V}}O/~* Š~*úÙ”ú)ÿé김؊ ¨ü‘½ÚF\±ûEÛÊ¥ç¾JP%0ÿ×ס¹5êÞ«d $¨öR6¹ bûŽÔ—µø¡ËЧÏ,H]e®Ø{E?ýü_îçŸk3À³ÕgЦ¥Í% k¸ßL#ke†:q¢rŠGj‘üåc¹…Ñ{‰Ï’‹Ü ÷–ÁO35n¿t8tF@ËCoï G(ê9­ÜJDP±ük'‡Â— A2ôû¯ÐúêoNÖ²'½–P4Ãà<©ØÉ=dç dÁ!‚ŽZ6f¦VrÝ¿õ‚SͲ(Úµ»ŽìEP±|ï´ìÈzÍUº›”+…r_ôò‚,×ó­í!ûq‡WšBÕtŠž´Ö‡£N ¨ØýìÖëdåHѧÃ÷Ê”žyÉÐCÃZ’È£]-¿óUöñŠ*Wlÿ2v“ø|èÌPÿóa_³ëUôSÑϦÜOGùàg|‘¢Ò÷ž |„ûkÃØ;.š{Îb&,ßkÀÍ}£ …–?¸mŸ ð¶ð;÷w^[(ÿg%A-ì#ȰB¸¯Ï\'¥º†€VÕ¶—e|íÄËŸ®µ®iF hï¸M°ÝT ±Ú ßJÑtC€—ë&r ƶzª† Ú'‰»”ûÕ®ѽò€Û­`+}úvW,Ÿwôu’ﲢɩÍaÕ®¬£ ðž ÇcÛŸ®åªh2U"åZÝ}!«(.àŠÝ÷ëyJÖ˜œ#Cǘ^“õ`﹋—n¡ß3´Js) »—¶{sO@µßî¤^õ»¯;®VY—¢ù‹ßÉ$o,ÿm‡f²Ò^C¹ ·“ñUá\±ýRÑó8KÐïQõ‚|ä*ú©ègSî§FWO0ŸgAÑ´$˜õI‰»ß« ¼3FЩ9È(K?.)‚:—=R4åú"003&è1»Åt¯I EVèÒ¹J Mß1ê«L)Zm°R.‡T,ÿ©„Y`—Kнë ém:w g¿Ü‹[VCEi>×}Í#‰Ò7{Š#Ü¿RƽÒù'Z»˜ëkiÓ.Í"¨X¾ŸÆA}?UŠúMuòZ°À…¤l'¨Ctsr`¡7L³£Ð¸©”¢ŸÔ²sQ\ÑïoØwYußÕ)S¢é³×qëjt!éÒ8nö«ÉP+ÄHP±ýEêT’ð€ W%I´W»m\E?ýlÊýô™÷?ÆÉÐÊ'.Pn-“¢Nž¤û±‚º… 'ù9۸ʖÐÁ.†[s÷#) NåÚ\î ÷íÎе­ÃI²³!÷]³P6 = D}&±K=OP±üƒ‡a_s ºã`/°›QÈMÓ™ Z]¾pOäXà M@ÃOòû…6C­*rˆLÛ„ëõ¬ ,êÚЄ“½Hçö¸bùºj,­‘;%¨ËUpþÐmM9¼K0{öJ‚–·'ð'2ôòµð¹Y¬€:ôê Uš *Ú—ætÁÉŠÖÝoF/Å%ryÂzó=ýeéSµTl¿JnÝ™&Ðý7ЈÉ)ªè§¢ŸM©ŸòŸq¡*뺕Ÿ;Ýî%”­$¨ü|aÉÿcßN£jìÛÿñW’DI¦ !™BQ µÏ÷'$s2Ïó<…„$TŠ"$IŠˆ$S’LµÏ³($IB’™dþ_ÿµ~ÇçZk׺ÏåÙ}[{?y=8Öyïk_ïÝÚûwDM_Ruž´¦1w¿J¤ê¼Ã¡«ÂìÃ%òÿä“ÉoPhȺ]S‚T×|nÄÖ¬1b¤êüÍšlH‚u:©:o½MtRù§ù¦/gNŸ{ƒTw«6OèÛÖW"Uç õΠh¬ #ÿô¾sî/±k¯£©:®iÄFülËÈ?Ý¿W¯@)½Š‘È?}^ÝOu?ÿ›ûyëáöŸÜ—r…eÕé?.·‹jrSoßÇàˆù c{ìAêî½ÖžèØnŠD¾ô~ ‹ižÜ+Ï”HešN.HÔe±£êpÃVBAµ†Œ|g|Ja?ê5H¹üßZ2ïÃÝ@j¶då|¸¾wõ…î3Hä =SÁÛ¹Bz)tìMG2Ê䡸zî‘\S¸€)}Ö)É÷=m؃Hwrù¾ûIºí¶ äÌ×~RÅõ׎¤v½ráÃ'g‘tžmË-%²iª1[jÐC$ß8½AÏ[›@Êݯ¼gÄ,^DäÊ CömV÷èº !²O´‚l’3Væ3$Rnû«¥wï<IÛs¤–Ù$Rîyu?Õýü_îç„D_4Ú]›‘ÊcPÏÚ€i×»Â'ìàŸ r¶ã>;Ð’õÂ$òzi°§HŽÛ0÷»itۆПÃõíz7mAéLX±q¾)—¿âS3Vc ÈUoLÙ¦:"7dñaDò‰Ü.µ@óëÕÓÉÙ)_ú#%rË®ÁP ñåæGD``Æ"pƒƒ•¾Wjˆ¤ìû;o©ôóÝ4‘´¹:Z ©àºsÆhÅ:‰\ê€ËNr5Ìz£î›2]FÇ‹N^ù\¹û½‡°õ«´@ößV‹ÕV4åÆää!«à¢DNicΚý¾Ç•Û?E±Xsv´D>ø‘(Œ1<ÇU÷SÝÏ¿¹ŸÃ7Û`î&F.Õ«òsú\ߤJíEiü9UTÖ0åyNaΖ;¹Mí§³£9î ·´b“Ýû)ÉL-ö2Á¤ÏÀìÇ7O‰ôLñ`}wx ¤\~ߺõYTÝ» •k±^5?p½²À¾7H'?Ÿ’ð&×”û!ô"† ‹%²j™ëü¤í¿~‚Xxò2wÇážhÞ¡~:)—//×JêÖÊJ"_Œx/–ùLänN?†Áw¸gÝÃÒ6ï¸æF#%o ƒú¿;<ŠI¹û—uÙäq]A:u¨Îž\vãžh=ŒvR"ØöxÁu½»£zGpoŸLJÆq[U‚[v„ë´;ÏšGpåò†k²R5F†T immîé³_påF½tÒß³>s;¯Éí¶¬3óR:‹äÝM}Ø!­ôiÿ Ã=—È•¦ìIþ‘”˧ø>Iô?ë/‘Æs6(F§†rCòª³ß~p_ŒoÊF/üÉoýNîÜø×ÚlР>\ÙÏo°&Ûæ>dûß±9Æ—ëà2•-JØ© ûugÚ:€”ÛÞ¹9«j;SA–×hÇL¦/©î§ºŸs?K;êƒFh3RcFsÇŠú¿@&÷²“z®Z*‘Û{7•ô;ä^û¬Ëšþ~ ²üa*f¯ÉçŽ?{[œÀmn1G2Hû%^C‡ ƒ4£D2¹L Ëõ¿H¤\~ÿØ[˜Ñ®#u,Óp)´¦­ÙÕ›¹i³ÆŠÍÒ æJ“]Ù ŸÒ{³]s¸YM ¸W$—}9)¬]÷”+—ÏØä‚$R"ËžtfÛ(î[[u÷=·üdo6Éþ·qvov6:[AZÆev®{@ÊÝyù C@.ªWŽ::;¸ Ç*1u•÷J­),Ê•Ûÿn° ‹«÷¤^ZvN2b¤ºŸê~þÍý4lpMù<è=Èw ú‹Ÿr¥«wÅ ½k9¾ÀLÔ:ó’;?` ²¬7t)ÒD§•é©ðò—^ÄwÉ1è&µrp•ÈßËžáRi£trX+6Òt¿DÊåï§‹/L9ðþ:Üšü¯1®˜CÙ7‘ì9´k~ꨂ ;fÍúZh0²}CVY¢Ï½i âBÃA¦”w–yÄ)IÙߟíqZc»Dö8-§`î:­áÌ«I÷p£ ¬ÓmÜaM-Y÷ÏZŒÜÈ®bÁí•»ßùÒ#”/Y\ó¾>Âmžª¡ðL*ßsEë+í$RnÑÚp¬33edî€êpÛ§ÃU÷SÝÏ¿©Ÿª¯Œƒ[Äan‚Tß·°™~—HÕy@lžXòT$UçE÷(÷7Ú%‘ÿgþ¾.KÝ],ªs¹üM6…ñņŒTû­Ðeš?AªÎÚázI-FªÎǶ­%GJäŸæ+¯U„vnî©:74™±hW‰To¸l _÷z¿ ,ÝR@ªÎ½ÂâE‡$òO÷ûž/U|Ð:òOŸW÷SÝÏÿæ~/Çþ“ñ½5p´ŽµH*'T l¸N¿.‹¯ž›J¤FÝ,1Ä~ wÆñ-Hú<›û¢öc¬wwü×b#ïRßS_ú§”ä»u1iO#,­f‰ÇIû$R.ÿˆVìéU‘ÌŠkÇš Á­1PH¯Ç “<2…ú¦%Ü”Ò:=ëˆDvØÒ”54Š[Ôr,kÖ[$÷÷gÓgµ)—ï£ÕNhþœ2Ê{>öõÁí]Ö‹l¿I"­^b}uæp'Nl;k dO;ToH¹ûîy—…Ú:¹¬ÕáØOC®éMmô1zÂÕé¶ ÃnsåöWf›²ñ¿ƒAö{¤ÇÚ7¹rÏ«û©îçÿr?këU J÷*‘üU¸‘×R"sE‹Ï=C¹g«5õìpö®Ïv…ä‰dAœóµðS9†Æ’wÿß"yI‘,',”ÈÕß2‘¥©‘Nº4e{_çH¤\~帖lrø¥4rÙ‰ÆÌ·WôwH±tÙ¡jˆq'þu¨)«j¾]$³LõY§Ó¾j§Ë"¯¬Y4s*JlÛråò•¶n‹¨“@Æ—_ZTÊýðró0´–ÈyA#Ødû‡"¹b¤ {­|ÒRÿVœøÈ•»¿ãŽ„Æ©kÙ S”\M—¸³&šû¶‘.ëûÏïHRnܽXZíHïº1¸µ^“‘ê~ªûù7÷Ókš&.oí/‘NùºÈµ]ÄuÕ[èšÎ¨¿Sp]u•ë¹CÁÖæ59ü‘‚L áÞ¦'¸DIdÀHgÔ›à cÎéœIál!Œâ†ƒ”˯H7byûòÛ~]¶zÖ®×üå¢éï• óõæ‹¶™rw5~ƒ†eÜ©E)0ÂÍ9¡˜êxN$ÝÖ—*6>+‘rùr#Û »ì Ì{*unÆ5Ê»‰dûþXów[dL=wìzpdÆ­:rÎH¹ûÏÚbÊ’ŽyÒ¥œÆÛqÖÓý´@æ¸i°ÐMa åö;˜ôFfËÇ KW¿¼FÆpÕýT÷óoîg—Fp³Û ‘ƉÍ0sy47δê=ÎãJ˜ í+Ü7Ý:±åºçA~³6a³~=â&9žÁ!C?‰<ž£Á<×Ôâê-;&¬ë ò}ß:Ò…ß ¸rù#k|‡ov žbK¥#74õ‚òÛ”QY4Õ\(ÝÜJ$-ÙLš˜²ñºáúìãÜæÞ£šûE"ÝÞ=Ä–÷¸rùún] Fd~ÈOÑYâ¨ôxîù³Œåˆæ&ŽïÄ–&k‚ äˆ{½ì$²èãq”fråîïú2®‹Tm)üõäz˂ͅbžc=¶ò Wn“8I¸6QdÂ÷Ú(Fg‘T÷SÝÏ¿¹Ÿëî[¡­tD"ûwBØËT®Û÷hûôwwußÄr/¬ÿ‚ÂÝe …žÃðg×¹ïF˜² Žd‡,kL/3%qÆ–\‘ ‰ ?›¹)ûßqú –ô R{Òqtš4»1­fV~ÉÔ\œ3ë&‘¾³?¤uÍŒéòáŒØÀÒŸ« V±+]$;]ÿ‚é烔ËwLCCx¤±@.¼SOJª©¥$#s›2Cãö —]ÐeWï÷áZÐg÷ŸJ¤Ãf{–Óó$Wî~hb –×î+‘s¿‡ÂsÃ@nÒ \Á©F)Èæ+~eÄsåö_7ð‚ÃåÁ÷×%L>±—«î§ºŸs?Óž CÙ½l‰ì2~^®¸Í Ì~ßjþܸ®ß1æš=·Í8¼ÌŽ)յà—¹ŠŠ¦ÌÕèwÕ¼šÌbx9·á4f”V)’cŸ¬¢iKrùûÌGñGo³C<0x¾?7±ü4Ö½'‘‹^|Ɔ¬iܽ;jJNE– Ó{éIŽ× ¤ë–u0¬_ ²jn•"xô5®ìM¥àSD²`[©ÅŒ|nvëRD- Òà)üÔü×|Æ4˜Jäæ3ÍYʧ”»ï9efZ¸I¤x÷ ^ÞÊÕˆsqœùÄ äè;û_Ï+JRn¿áÙÚ,÷æ‰9D—™ž—DRuŽy†Øz|²‚T·9to¼4©:?w¾-ë´ï:HÕ¹\þæG¸èRun_lÌî,‘ªó¶+ΉŠµERuž¤¿LôéòOó•Ï«+Å»4”HÕy›ë°l® HÕyvú%°“n ÿô~¿ 'Ñgÿp‰TG|œM“¬%òO÷»6ÀÖœ[#‘ú¼ºŸê~þ7÷sg—>ì?é`s áiŸ½>Y›¸AV·‘5ô7jà dGDr#.<@9 âÆ¯ýޝZpw]¯@‹ú û8œÂ²± ¹a¿°]?OK¤ÞæöLq¯/W.ÿWÏîlè6dÏì®ìƒÿhî=MG–eÓ—kщx.åúÍŠ†û÷£ÜÊòåÈÈÍ[ªÏN׸¿÷¾Gæó\¹|ß*{² ÏɆY`Z-rò¢›H/ßò—Ý)´êïÅí^«{sý»@¶>І}{º¤Üýšï±:ÿÈåè|û ÷SSæç:O oéš³6Ö@Êío~+-¿[IdʳB\kщ+÷¼ºŸê~þ/÷síqL(‘ïÆ¦Á¯†7×~iö\Êmj¢ÅÛp_®¬Îê…ÔIã¸ê,dâE¬›äŽ ÛÃAŠ‹ß {"£¸ FõXÙÛ:iyâ0ƯR.ÿ‰Zvlô€Å ótfã³ÖrĶ`E烹£­ê°àŒ-Üúý1óx+nß÷±mÊ@Ô!-éjÎPö+†+—ïÝ­nlÃ4pîÌž5¸Â×/:·\ANp] eln`”Ë.UrNåc\®Ü}îO‘ê{dò€ûȼ¹äÓ˜é~åž½ ËtS.qåö_ðªÃn}֕Ƚ&XǾ"©î§ºŸs?,/£ûÊ™YkSFÛ áÎŽªÎ~ì½!’ûÃtXË=O”¤ÃöH2¤óûG8§ØÇ­’.)¶ôÜÀ}”£)î*rå´Ú,X¯Ë5š)ê]9"rù]#;°°…›@>ªÖ†•­ÚÎÍ{û ëmäZ'_Áµ×+¹Uz6øzl½HæõëÞËì$Òb†1 ­bòë"˜ë(‘rù^ضeObdF¬²_}QÓbŒû%9ªÌüërÝF­Ãˆ­³Af‰paK™‚”»¿~Âm´¾¾ä2Õö)ÉÁGÓñú|ŽD¶èü Ã>pßžìÄ ^OàvkÑ’Uô:¬$åòÕ²}I#@ž(½½a Á?¯¥Dj·¸%äkO㞨)áè³bîåôŸx¯“É•»ÿªü2f…‚ld{í26ro¹ØàýQ$OÚLÂï+%Rnÿ·EÕ™ñò Íf#hê~þÍý4®É’ô÷‹¤U­êÌßlW9%“¯Eƒ4Ó<­%'¸³ƒï‚¶pƒFgvÜ:NÁ竽Dn]ô.#;sËý»°¦KA ·k²ë?÷qåò¶ÓdSÅ‚¼qâJíæ6Ÿ§ÂvÝɽCã—޽$RëE5–w+‡ûýV]öÀó$÷ÈÀgxwµ!È E?lè8Š+—oQëT<Üä”1èÖ‰{Öó¥pnöf‰´=§ 㨃\íÚuYHwn´±>«?ó“’”»²õ(ž_Y²²f´Û"‘ÿç@D;åù™ ÁýÃ|9ÖâÞjsªs·(¶UJäÿÙÿ°[´ù§÷+öìá§å UçG>k0»ç¹ùÇû2„_ã#EòOŸW÷SÝÏÿæ~ÎÜbÇþ“–Ãk°‘§b@–jj³Ê€½ÜM>Ö`»‚R1OÃ?þV¯Þ‡aV°/H} /,ûlÍôä6†vëÇ]m‹ÃÛšpõý>ÁºÉOŒÌˆºQ åò§}L†¶m;^÷!ì»Wy¢kß½×;çöf;soz6`^£Î)Èù÷´Ùá»ÑùíÃW 0B$“‡™°³™GR.ŸK¡;¹P O47b­OœçjÞ5aMú'‰äÙn-YŽ‘7ÿÓHìjuD"ç~ˆÆåZÿ*wÿä¦ìt“éP׌•n˜ÁÝÐÀÙ=»p}Òãákߊ+·ð­x°ÈMi·)NåÊ=¯î§ºŸÿËý<½Zƒimˆ¹fïW:ÿ¯Ó2_¯Wi¤žkò‰‰¤ÆæÑ˜dºG Ícû£ÆìóJRkýd%žÈ%óêÂís‚#9iÍZ´íwd•–0ÖöçÊå(ƒÞ 'Ë4×boD_÷pt®‚ë9â4Ê»´å*>F;ïJôÿu 5 ƒ¼XÜ„ù®œÍµ¨ËžYíäÊåë5§>3ªÔÙôHfv¸ ·à¸%=B[$= :±Nmë§‘Zuóà°ÖO"Ã}@ÛöERî¾S¼Ë>0A"#Íš±n‹Gp_·+ÃïDò£­ë6Þ–+·_ëá+|$’«=5ØÞU7Hu?Õýü›û™Tõí-cAj•£øÂ=…ró"ù¢±ÐÉç7·jÁ`thõ“»Éj&| ÇIdþ­sB¯N‡D²ÿ-BrsK‰T®mý äÌŸÁ8âi,qå>_çgÂßÅäþ)}ñÎo·Qª½Öåî442ÒU+ë6tùä•#âç[s“ÜsPRÄÕ-_ É®W.ß­g ØÀé}AÎlÚ€%GÍäZÚÚ2§O/dQ­.ly]lÓñ7¤ÑþÜw>ï`8u:H¹ûÏW4cmߔȒ3ÍXb;gnƒlì¤öiÈÚ±ÉzÕ@Êí·9 ÍJµ*²¡Û¼gHu?Õýü›û™\õÍCvƒTœ»‹U룸YFÂáŽÝY¨íÌÝ×d ãæíæå¶ž/tÉ]ÄíX%4NßÂÞò>&læ*£Ø›z¡\¹üÝ:·‡s¿¡ =}êàÙ~7®ùT ^rð2!Ã÷r9Üêá?¿…Z€œnßO°^¦Ã{ËIÓ‰–è×q¡DÊåk:¶«Wæ rF‚!³?­aà ÃC+¶àî+®û½«°mzäHÇݨ©{–+w¿KQ3öÑÌQ"O7cN·;s½µbu> ™gg̦z¬äÊí7òÿˆ ­w‚ì9ÿ.öuLæªû©îçßÜÏŽ“o èLȶ߲QÞ,Œ«Ýa‰Ð÷þ$‰l¾è°àQáÉÕýxÚ…g¸ï5n#Î5…ëùV!7wpõ]Öá‡þ®”ÓŒå·nÇíz£6‹Ô­© åò×6~%˜í²a¿4¡b¨ ·öÑÝÂê/‘œ¼{š`{¹ü_7VWô}“.pã»(/>ìó¯Ó3>r°¤Ëæ,‰á~ªÖŽªiÀiÝ‚MÜ‚›¼p’ã¹ »9@g¤=Wî¾Cz3öέ­DÆ6c^fÜZCµX“Ñç®ãzÞL®Üþ㩸þì Hø( <ÄU÷SÝÏ¿¹Ÿ™Û20þbÈ5ÝÏ"îØ:nή—Âo?‰Ló6EÍÃÜØÌ lïºkìû ¿:­åº¯JBäÖyÜÕŸï`BTnƆtÜ1mRÛÄ ›~åÊåO¬*œº#€¬h˜ðv°-×ga_!ál[‰œ¾oŽP Œà>Yš–aÕÊœì|@ÑjDrßðgp¹7A"/þø7×Ï*I¹|ÙIÕXÕ—ý çÖûÉrËw6b¹';q½××fm˺sí×÷‚§ÕdðËÓ­%Rî~qÇflÜáúYzÆŒuÚªÇ^´ËÌA¶Ô‰³é~)ûýáúj´ ù©ÍTxuæªû©îçßÔOÕWâ—㨓¾¤ê¼õogL+X+‘ªó¶5µÙäg¶©:÷®]…ó…ÓERu>¹áÅâm@ªÎåò{ ÇŸ´©:7û+là%‘ªó‘ûB„U)"©:÷ÝòÛ º‚üÓ|}sÞ"0ç HÕyfo ö¦¦Ruž˜~ õ7HäŸÞÏniÆv ÖHÕyý½õP’ÛC$ÿtãþØXm¥@þéóê~ªûùßÜO›˜Öì?¹`Q(V­•ÈŸ½ÃQÚ9ˆ;<¨.3?ßäâ)µØž‡®Üã­Ëpb¨7y£„|ÓjÜ3SÎ!+ ¿D*ÝDt5®—õŒy›%ïªÂem'rù»¬m†›×鶪-Ïsõ—¢WG‘ô?€ñã*¹Ù¡7Ð,ò–@>ŽÊ±5@†Eآ㕑ÔÍ2GÖÂþ)—Ïn€!{ñ¹¥‚l¡Ç²¾âj]tÄÙ¯]%Ò°û$|,ïÂõ¾[õÏo£&"i5÷Œ{¤ ¤Ü}Et}o4M"ù4BøâYÜ%•FÌ|v{nËuØœ“9")»_g¶ï y]Ø ×7ƒ¹rÏ«û©îçÿr?ÏõŒÄ)D"½§ì‚eJ(×¼Ž6{Ý ä•µ_pgÉ n€K=ÈZ‹&Cm7ãÂKtÌwI³_ñQ©/&?4±ó’=ȽáB\âk”Ë_£]G”ŸÝ&‡½ìQÿh.k³¾ZKäÔE{8l÷ΖM¨Õ¿1È‹Æb¸a+î¡Ú®8¬$‘qëð-ÅŸ+—Ï|“6ûUÌ2bãw\ê¼–;í¸?¬—t”È!G"PžÔ‚;÷P:ÝÒ$1 6ë¹r÷Å8S¤®š'‘+ëša“ÃBnľêl´ÁA%©ô /¶,H¹ýÓn¯Bçõ G/‡°)F©î§ºŸs?ßîÚ‹;úá¹ÿmÚgGpµ&?ÖëÎ ûµ-@-½®Üضfnã$ºY·’bGòðu ö â½@:œÔ`ž÷ù¡ÏgÁ¸ÞY±Õ»Ÿ§ˆ¤\þü±=àê}WAz¤Bó3ÉÃe‰™â+‘snJx>m·¶~ ì`2ëH©ð±— 7¬ÇE(óÛKdù‡7cO•¤\>ßßȱŠȽq%È^r[³É¬)©+‘Ó7¦Ã0ò‡HZœž€1÷6‚,óî Ý3¹r÷oÐx‰¼z²¦g,å^‰Hƒj ßwŠÄýæÝ¸rûo ž…ØîóER!.Æ9E‰T÷SÝÏ¿¹ŸZáë£H‰œ’u ©Ã¢¸™£$¸Í·é6ÿ0>MÒç>š»U0˜k(’ó,ŽìŠâ*Z|EÔáU [=|‰½C¹ú+ïâþçF9fˆ+Ñ€+—¿þšqèð:FIV…ÌÅiË"Y°<7»‡Kä…%(ûü¯»Úì:”jƒôµ[%Ĺ!ÖË4Y§M?¹§ß‡Gßå åòÍ?z bT‰@ö‘Õï3× à&_ÏI«ð2,™͵7é€kók€´Ö ŸNLVr÷…²vÈ\ç%‘®£¬TåÍíá<êŒiüÏ÷€VFråö=ÏÞS$·¶cЫ\u?Õýü›û©ë˜Œ¶-¢%Ò&ù4úüëÔ§ðº[*Ïìðvn×xÛ:A‘öD$ßÙ¥ -ô%òõÄÛ¸²1dQ‰ˆâ–ÿurKf>?Q$Øš0ß)—I¹üc.zã·çZ‘ZmмsC«^";r‹D¯ú„£Ó¸“w{ §kmH˸hájƒj\—]%ÌÃA~~˜ §¾\ÙÏOÊQ˜Eiƒ4k¶ ¥›jsß®BÊ{‘¬j§Í”Ób•dj“øDФŽC*z³)wß¾º-ꯒH“7]Ñ¥x5wïÜû‚ù€¼U! Î? ¸rû«ÛÆá;;%Òkm:ÆÖýWu?Õýü›ûÙÄ" /GI¤î?ßËkìÝÉõJ™‰ùƒò­» <­W °ÛÝL"“Ö øç{AK®Á½°²ãí-°®ß†+özƒ³‡O äÿÿïÜÜŽT)—zÃ-(˹+’©A‘8¨£)‘ÇÿÀæÃ³¸ÖvÌ»¯À]¦wOpº’™FŠÁÑ6ÔJ$œ6`YM{«¦ #:DIÊå»r~#ÖÏ6¹hÿrX˜6äžmY‹i|·Po ™Wu <ý‘“WK¤Ù<¶³ÅJ®ìßC,|%RcNOÇÿÕåÊ¡{=y±‰FÇi+H¹ýÍÞD­cÁù«÷S;(Qv%Ruþ¹®²{HÕù¬jеz%ªs¹ü —}üRO"Uç.Û4Ø*÷Z©:;d*îD‰¤êüÁæ 0ýÚQ"ÿ4_´ÎäF5©:ï¹ û<~³@ªÎÛÔaCô’È?½_^ÙUú~©:WT΀M«e"ù§û•Û^£$¸¶Dþéóê~ªûùßÜÏv¦ì?ùüøŒ7¸'ØE”žýÈþ£7æû‹díeÏQlК»uQ¬ŸüR’·"ãìš2ù-}Ò빂ÌÛŽ#:¦\ìÕbMBŒòJ÷,4{~–+—? â *ƒ|%ò{@|øq»ê°¸ÑQ"y²A}¶ÄÜko‡•·ô@þܲ ÑË͹v)ÕXáÌ…©3²3lªR.ßʘÙ{Èù;îáa#wî–ž ‡8ФmX';…rLߌ+Aj¥OÏÛÑ\¹ûƒlúb{ªÈž÷{bFV\G;Ö«eîZìwB_®Üþ®’PÅVKdU¹ˆ1 §såžW÷SÝÏÿå~6ÎÌ@ÿKÕA^eJ(º×å¼ü„Nσ•äõuÌðÊSGÒÌù>uKH§á1øŸßÿϯ 7À4ô¤@®²ðƒV@¸‚ôëäƒá£¿dÀU#¬)zÕËï89f·ý$2Ú<ýk·jÆ,eM‘\rÄ„5®¡Ãݦçí6 _ývEQ}îéö:ìgL7nõaå8#váÊåë}á½–¼¾õ&–%¯ä†tj…äãé"™2¾) ½âš7bÐ=árK,|^‡+wÿöv­@>¹Ø ÷ÚÿkÄÎÏØÐÁ›¶úâ{råöïÒ¸Š…öé[ˆ%g?‰¤ºŸê~þÍýt;w6–M@¦ÛžÄ¦Í­¹¯óª³ÚÞU rµµ»5´@šm=…®ßAVÿ}¬óDnâexµx”H¦”xÀ|ÈGnã{‚ÏÝi7µŠ:)H¹ü1—.áQñj‰üå’¯”\ Vø|µ’œ¡Û-:ßÀ‘l{Àc÷w·{ ›YqWežÅimn®I´ ‡¤\¾Ù?ó0xäjËäàIÛ½àl‚¸B]‰«m‚.SL¸ù!Cq|Ár›à‡•Ϫ‰¤Üý©º]`ûÑä _ktÜø¯Í]À|©9÷è­ýйô@ åö'†>Äò¦ƒE2ßçV¬=£ ÕýT÷óoî§ùÉD´ß ä ÇÃXÜ;o‹>k½C ‹ë°ižéÜîKw »Ò¤·´fŽ^\éÆ2$M³•Èøá~èí9‘«ß‰µ½DòÔÅ,$¾çÊåO醤,‰¬ÈÎÇFs¹9 YB‹xùè\M¦×¾±@6[g åsSvwM0{×o,j ×ó EòRýZˆq)ãÊåö, ßÚ¯ÙqE:¦ÎYÊ­ß¾[Kä%3 ¸5ḭ́1¿TŠäÀ™˜^£‡DÊ~>¬Û#ëcKS¶FÇi³‹Ú¸ äÈŸ£1ð|%)·ÿgÖs$ý’²yÛ2¬¸Ö ¤ºŸê~þÍý<3õ{‚L]‹-ûqëÕeÓ~?ÈͺL÷ŠȾ¾x(¸sc"< 1hwÝÓ X¾t¹DNòÛ²•>Üolðºº\›WuÙ½é?DR.×ó7¡-M’ÈÉk te7ºPƒ•yLȨz¯áùx3w¾F+ì­§ä¾píŽÍ‰ã¹ïט û¼ùQcZsåò}(9‡‹惴i‘‚¥§pÛµÆËò®yÀ8ØÙ‘ëòêLçÖîöÏr÷ƒí[⫽9ÈŠÐf(¨Û‚k÷É7š…‹äȵf~ù“+·ÿËáGØà=䨨;𲄫Ÿs?vÞ Ë*7æÚ;aâ6Š{¡ ³¬ÏÍ\©Ï–7äê]ž‹ˆ'V\ëszð›@zÇ\ÇÙÙã4è àÚ1d ý@‘|·ñ;ª6¯Wrù·Ôº‹n.Ùsä=ŸÛ•;pf!¦Õ>,“–K(šÁí|p|”n òé/ŒŸýAInwÞ©Zùnø=ÄéùФ\¾J·D˜òøÊ,÷rææ»¡¹ƒDN5ž—f]¹ß6ÿ„þ’NÜäÌ*Œv¸ ’r÷ƒZÿó›WlrP•1~:˜q÷Z˜áÃûVy¡· "G:råöºŠ†ûÖ€ìá,¢xÅ2®ºŸê~þMýT}åÇÇGãAªÎ{ÔÐcN½š‚T¯O]ˆ¬1ëRu^¸ç´.K¤ê\§[:ª »$ªs¹ü£'— Ê¶DªÎ¶ŒGù›|T¯lŠÙ«¶Š¤êßîwqtœ±‚\›QˆÝÝ?pM&h³“ÃcDÒ"[“5ñç.Ûê†[µÞq ίCë÷·¸r÷çÖù‚¾ÏdöÏ*̘\M -÷·½žDÆØÂü9ö\¹ýs¾øÂxdo‰Ì„€Z½¸ê~ªûù7÷óæþDßé$É¥X±R‹Û~ç*l(wùI×Ã+;q=RÁ·éKGrÁÁˆ{HIæÜA»•/nt8Zï à½yÖs§jïÇ¥ÝF åò|õñýEòLÖüî§àD=C~tM‰”ü/‡‹dÛïÚÌdÇo®ûeÖÁô#÷lGT(ã~XåÛ7¹²ïs[° —Î߀éàÍ\÷ž?á}ÚE$»ùŒI=´¸7Žá¾Ù!îЮw0°»;Wî~ÿßp"©ž@Nú«ÇZþ«Õ Œ*‘AgáñlWnÞm··•È7çcñ!£WÝOu?ÿæ~εZcïâäê5Aè{··Ûó•(3YYk}1äÿê‚™º‰äÃæ¸%ps›mÀÀƒäÝV¾(*6É{þAÛÅ äË^Èoìȕ˯Ùåf<ê(’:´XØñf\M£Çغy'W÷U ÜkÅ ŠÖaa—îqµ·i3«°sÜq'¢2÷ ·ù‚Å•Ëgâr&§N dí‚\t»Ç/z¤µS•¤A½gXt7Á‘òã;v†é‹ä¾zLym’’”»o¿äB‡Û dçz¬Ö¾žÜ%ÉÎ7"‘Ã~f!ÛÉ–+·ÿ×—dÄæ•‰¤øá"&>ãªû©îçßÜÏ5…ÁÈo‚4²¾ÍF<ŠZ¨$ï Xƒ³ª²Rᇖ 9\¥™)fOß/’cz5Aòï3\§f ábpœk°k*â2q£Îz#dâolsh¶>PrùÙ-mæ—RW$_¬Á¢_Vã:Ï+FU›i¤Zˆ…‹« d‡¬n­M"Ù:ð ÏZqõÓ«1ÃPm®u©“vR’rùœgç ¹ßoôÔËÆéPC–·îÁÍUiÔ¿s¿wÈ5ë°if‘ŽäÃAµ™]¯d)w?¦TƒÍÛ6D #Ži2íÖ“¹«ç_Ås¡ºDöo\€#]wˆ¤ìï£éwaù¬\IZä¾Âè×#¤ºŸê~þMýT}ý Áæ’;JRu¾¿ËZœk)ªóE“ÚáœE¦HªÎS6Gòµ&©:÷3 .µ‰äÿÙ/“_éS“=ÙT¡$Uçc¿ évˆ@ªÎµš¼ÂdסJRuÞç‘>sßÔJAþi¾©ç.¢wíæ Uçöû²1Þ&P Uç ÁÌlF üÓû7h±£ÜRu~ínj88)É?Ý_ÇwLÊ,ú¼ºŸê~þ7÷s܉ì?Ù0| ÊëKdi¯Õx>¿!·¡M^Õ·9øl(’ ,¹ßú_ELb‘œ9¡»žZs‡Db\û. ¯Ý‰â$+nó™Uî2J$K.k³—qåò›-Z„­&y«ÿ ”ý×›7"eýM¬é²Ãj~å6¹´ct%R¯‹7¼¿ýÉ&“¢Q¸Þ¤ñNĬ²åÊåëÜf#ºÜ9(’ÇX0lºŸç¾ÎÔff†ÁJru¸Kµˆ[¾óÆÙ±¿v%˜pwÿÞ“eA~zµý›Xq—¦¾Æ Á\$ýÇ~G@T3n¼2 aŸš‚ÔöŒ†™f5nûöZlLÊD‘¬Ýó5F6.T’rù;f¬@Ó÷ÿäþ)öÆ‹Y¸õzG¡xž>HÓ»Ûq2¶1wN³@\²x&’ž×·À(9›;'%Íž4™¨šF©)—/«ÿzØ,.É‘#Ùÿ57åàoŒ¶”dÝÖ_19³"ìÔrvghH¤ÍñMXíx]$åîãnÜì¯)ȤaøÆös}.D’ù5®†ÅXV H¹ýæg¼‘§g"‘Fÿ¼ï¹ÙÆ\u?Õýü›ûézd%¼ÂL%2oÈJÒiÌ- ÷§íAÆXmÅãôÖ\“Z¬A ‘|r :›¨´äž¾ë3é¹Ï&¬ëîÍ‹ynýTA.ê–£NR.ÿý>˜Ù¤Dnk±¬±÷Ò‡0¬žÚ¤Õ‚­_³#×ïö.$¶<"’Í’Ž çÊfî~Ëph”lRÏF¬Ç›¦ãDR.ŸÆâµ8½ó·H¾=䛘ézõ=&_º™J&_x‰³±‰Žd×Í1÷D„Hêͺ„×'\¹r÷_¿ ƒ†“‡‚,{³‹M¹ã¿&"Ô-A w߃)Ϲ²ûÇùa}¿:YŒ¼a?ERÝOu?ÿæ~N칑5Kät¿•èiÊ­­óf ÷NÚ†~/êqLÑa7R:ŠäÛ7ÚÌgBî‹•Û¿·Ÿ‚œÿ"³?—)Éá§ã°}E=¦î…Ù¼®\¹üáýàía ‘¿Ú­…Kg=®FÝ­ˆ4±¹:.¸Φ¡Fûy"¹½c.f<ïÁí¢íþg ¹½;NFj¤–DÊå[cè K}‰ü6kêqÇ:ÖÎl)’M_ÁüCî'‹™0ÏüÆÞ>¦†W¹rù’\V"ýhC‰Œ½»3s7ϹþO=¬ã{ 1÷¸gÎWg—TIlj:,t¾Wöý¹†3ÒÈGGÃðhC%¹vÕF¤/±ù%>õO[påö¿*<ﯞ"™00О«î§ºŸs?WãÅÕ†©w ¦ž¯Ïµü¸6»¯dH«\\´k¬Unƒú*Éy%ð½zÏ‘\Öo>Œº¿ÉŠóSa¹_S"'¶ØŽ¤Vi 2ñÜZض+’rù§þó¾j~Éà’ͨÔÉ=u;oÖ9lT"ãŸäþêUð ÐÉ·m4Yî mnÞö8¼êÆ]}<cV áÊå[è¸ÇM$Ò)`ÞuøW›{» $‚O¡oµ0®Nºû¸ç¦’Üu®nA÷ÓH¹ûÁax21@IÖš†­¹AÁ[°N29p}NG?H¹ý3&=AEÌs%9Ìíò^îåªû©îçßÔOÕWp¹/ÒÇI¤ê|ÅÃí°t^,ªsËëH=öQAªÎ«>OBïeÚ©:8oФê\.ÿ“a˜½ã¾HªÎ‹OF`ÅÖ©:7µYæç"©:Ÿ­÷W[‘üÓ|ƒ®xcͬF©:×Ê9†« ¤ê|DË›ðsCAþéýÐŽap×*R’ªóöáØ,ºJOMVUê£$ÿôyu?Õýüoîç‡ì?ùÂ>û¢ZˆÜ¡à<ÓˆûMkÂC‚ÒfÈHÄÞÆ]_µJD2»³;v™ÝåjÌB¢S ·ÝÕW¡Ñä‘G¹ˆW4ä~hu·ž ä%|,Ûy»Wï;¾îÃMó~‡.¾u¸r÷Ùæ÷X½øº’üìøµ§ärg¥>ÂÞEÒµí]ؤ=àÊí¿òàn=NɆ§*°.°+÷¼ºŸê~þ/÷sך‹ÜS[$ǟˆ…÷%™÷nîØE dàéq˜ºt/wôy°ž#’í^,„E` ÷Í×@LZ?ŽåŽš}ÁÝ÷jš8¹*ÈÛKö`ö»ïJR.ÿDaõÉ…JrÕ]Hû*r‡-P"åvW‘ÌÛ}É]¸Y½ŸbP÷.î¿Da›}\?w ƒÛjKdó4ämÊ•Ë÷þ×5è%Öy1;e?êp]ÌÅ0›4Ìl Ç ñÜO­K mûZAžìyg‚ë ¤ÜýÞà\@¶’Œõy’¢Ln—M7ð$Y$#ž_ÅÖ¹³¹rûË‚_bĈÇ"é¼ð)´¼ÏU÷SÝÏ¿¹Ÿ\E—‹…Jò‚óu¸®8Ïõ1VïÈܳñéæ^nƒRoì ŒIo§u0²–;ýI~¾îÊÕO=…Ýp‡”»’˜™ˆâÛµ$R.ÿýz{ð¢y5‘45‰Åõšm¹ŸÀö‚‚ÛVû>6¹r ϼÆÇ°3ÜEo°hßîÔŠ“8ei"‘é’°s÷;‘”Ë·×ú:D§Z [_ÇÄíÕ¹Ã\€Ý·0œ=F‰©k|¹¾‰˜­5ŸÛ3n.e¦påî?0¬@ÿ±’’,Û]Žî:g¹RÒÔÿÏÝ~?oRrû¿åßCÇâ,‘®¸…Äâh®ºŸê~þÍý ß\«‰J²èömÄ áöïµ>¦ÑipΣoå&”mÅËI"©»7¹pm§\ÄE÷eÜÀ¨¼¶“›oö[Í$òjV,ÖGÕâÊåßw{êÎ$’o\â[âÁ Ÿ\Šâ³¹nu^!Ë%€ûhêk|:|››zý%†.¸Ëµ8ƒþ!Ü“õ¯ ²á!%)—¯dNvù%1× –¾åêO=…#9“¸ëÿùœþÒ‹;"yg–Ï R’ê~ªûù7÷sCûH\ª$ÞÇôÜà‘þð¾ì/uƯGÆ¢É\¯¥‰Øî$’6ƒD´óvåξy‡O*¹ßuã‰ýn‘VâKÒ¸nW”hÕ«3W.ÿ¶yðÌ D$]{Å£ÿ¬®Ó·èa¿;.ú,¢p'*EÄÀ›Ü´è¸´ô×.óº¬sR[œ*¡=>‰+—ϲn.ê>È¢ª+è7-ŸÛ¢V<>ŽîÀÍ©ƒù³säEÂ߽Ȍ·{p¬R›+wß»yò?íQ’óv”âQ·î©:OáUã¢@:4y…„·7¸rû-&ä _ܤ F^Ã쨩Ÿs?»{>Äýï^JrHðô1Ÿ[è«I¶YœŠkC)ÈFWapÉ]$cr aš¶™{¬áüXRÅýöG«I¤¿m1œö÷W’®Û>!{ƒ&W.¿æõx8 JÉÃȎ8Ïí:þ#îœ=É=Øõ¦¿¹k'A§2ž;7®Ÿ”~Ü1û5زoÃdFŸŸ˜ì0(•”Ë7aÄeç¥ äàvYðotœõk;6grí:lÆVÏÚÜ®÷ã±g&7É;3ìqåîoÔ{Šû{7*ÉMÞQÑÁ—[Rð]¦æä䨷x—|’+·¿ƒÆMxîŒÈv.ÅËÌæªû©îçßÔOÕ—qÐ3Xì›®$UçÍ®…#är°‚T×V>„y÷Ã"©:\ V )‘ªsÿ±ìlf²’TËåŸ\ë 7.IÕùÖ™oví‚HªÎËôòð©A;‘TZñ ÍCÓÈ?ͧëÔ1©:”ëPÕÍ@ Uçå{vÀ7&À‘üÓûÍ_<Ää´YJRu~'ö%,B· äŸîwç×cüÓçÕýT÷󿹟›ÞWâ?©}, VûJÒàjÌvÞæ&÷y…c>)ÉÊee0³þÁÝaÝ3ó2üÀ(nFr‡L*„áS+¹)ÿ1–Õñåvö«ÄÞ;µÓÈ’¬røS’rù5ŽŸF~zM‰´º‘‚¾þµÆ‰ pŠ{«$¤fb}„À;&RÝ'ÜÉ)Çp̰‰Hvó¾ˆM&ÜáÎ"ÊÛëJ¤\>—)¨4ÒgZ¬óu¸¶Óá‘•¯$»ùg"¦ç|nµ¼7¨ðþ® «;V"Ùu=Wî~dà.lÙ§%‘ ŸFã~†&×$èZ8É„+gáÓªWnÿýçùðíêŸFþ¸v‘AŽ Rîyu?Õýü_îçr) cº_ÈwY8¿5‹{Üê1Ú}R’EûîÁbi wnÃ]h7NÈ¢¡Óá!7¥èƶ¯ CëÄìâdGòªöm¸iNQ’þΣèõQ)—¿Û§d¬p¨)‘ÌO@£°7tÂ%L|Ê‘ŒñÈÁ8¿c ruÔ)8–ÉFëÒ0iéFɰßþ››á| ÷G¥såò5ñ>~cê äÌs'àžÔŒ;èÖeìíº5lœ’ Cã ŽdÁàOXdu5•½â%ºLP’r÷úF£~¦†DºŸŠBÞ°ß"Ù¤è2î;ÞS’ƒfÂUùŠ+·H5G¾TÈ‚È3x2—«î§ºŸs?W6Ï‘i¹xS&Î$$s§Õ,DqÁ)%Y=2w}¸žÉ»Qa÷M 缈ÅÌ-ÕAάDX`V{ö3–WmS’öýÏ¡à\¨@Â3¯ŸråòÏžy?RªKdÖ•cøõÿ±_—QQímÀ± Q±»‹Ùû²[» ã(*b¡"Ò =4 9³7Ý%X`£¢r»ó¼~¹ÿg­y×zöòÛó°f¾ü>Ügß÷…ç‚™¹W›•p'£›ò¤ËÕ¨ÿi$3Ò0?ÇFˆdÄŠTDç]a6>¢@ŒÇ|¦…2¦)N)•Ïß(oódîÏ`øÅNbÎúVŠëÃFqd‹eø}’9¤á-äzðÙ´Q.zVÊH©ûé}Üѯú‡H6“»¡ÜôsˈG(ºÛ™¹öÂßx³fSjÿ¥†ñð½ē׻%·Ëu¦¦Ÿš~Öä~.LG‚­œ'ï¥Áa©³öÑ4ô¨"u>eCßw®Œ ›„7€ü\† Gê0mo½A“r}H–¶©'’S ¡{ò'O^}xžË˜Rù;-Ã}Å/‘\Ô*Ï7|b¦-¼ çÛ xR,}„·뙋§àf“—"ùl§€W‘µ’É‘oÃGßME®ìô…Ü2”Ê× {"üVðä¡Ä˸YeÅŒhp™m9rÅ#8FÆ2å@=Ã.<¹)6&y0%¿¿ rÃécŸE2&Ý­l?03_àWÞ9æIó*Tö fJퟮ'âÇø½Ï1-;f@©ØÎ‘ Z§¢ií{ÌuæÑ¸^ø–' ‰ÅÓW™ƒ×ßG¾ äèó×pïpÓ ×$\9Ç“ßݕ֛)•?H;ïªErý$9tv>bîÉ«Bô.sžt(~‰á—3ëJDðÒ†ÉdÆöT5bê~@ö¶æ"œÿ#{/fJå{™á…óæÉ¦ž(ëæÆ|yüú=åÈ+ªñ)?ˆyø{^òwy²J‘£þZ ¥î7[éŠKÞŠä¦ú®ØXûóÁ ‡hìÃŒ¸Z†wMäL©ýsëæ£Ð¬È:WŠq8õ Ojú©égMîçÃÚÉÐ{œ'Ǭð½Ã>æÞÏ".l0`Ž»"àÙÇELï}±hÛ?”¹ûA4êÖ;¬¯Èƒµ—­ŠìÕ/1c9²l³7º†´ãÉáÏN¡UÒ.¦TþŸ9ÁøÐ L$Kƒ‘RžÏœsø-ô›NáÉ=çߣYdæ—¼è[?™<‚ÊßDòx‹û¨£³ÙÑ †ÎR*ßüµîphÌ“k»Á²nóh£7H.?Ç‘Ãû½Ç½Î‹˜ßú"µèO¦..Åé®w˜R÷G&¹`Žõs‘|¼×o>ešØ#ÿÜi¦åÅÜ]jÄ”Úßß½½ž–òdSÛb B™š~júY“ú©þJûý>ú…3çIõ¹©—€Ý*[žTŸ[˜…!ÿé@žTŸÛT‹h¶m0OªÏ79†_ñJžTŸKåéM’ER}îZô£‹ò¤ú|¡,}tФú|ÀÄ „´æÈ?ÍØÏ‹.eñ¤ú¼³ùGä¹ÖáHõù7ÇbÌ©öãÉ?½uœ NÕ,’êó™31¼‹¥@þéþösra›iÓú¼¦Ÿš~þ7÷³]ÃWøO.9•†\y…HŽ‘Š;I•Ì'þ "r—³¶^Ì— ðáîs¤ó„h8´ìÇ“ÝÂ<`Q£Œ|ÜÞºû†dÖ=äÈõÜÐÖ·%OJå7yšŒ]ÞCxrôµÄÌÎ4îŽÔ‡F22¦ûEhM;™DN87Æ÷rRQ2Î5fÞ›xKö”1±Þ Á›V‹¤T¾k¹±ª~ Onጣ{o0ó0:¾X$ÏÌÍÀ?§ï0»·ÜŽ=¯opäŠ<+d_”ñ¤ÔýÒM–xd|–#—¹ïBÂlæÐÒÛ¸}=’''Ç?ÀÓW™Rû³R/ H–,’m;Ù£~§KL©ç5ýÔôó¹Ÿ?‹“ñͰJ$ÇÍ¡Uùœ©÷$Õ-/ äEÅèʼæŸï;yrúë0ÔK¸ÄŒ “£…ðI u½C±áÀ‘<8á,ÆeŸàÉiÉöhØ:›)•ƒv%™ñä»t˜·búÙAkƈ‘d›;Ç‘:iŒt"Joœúò òm˜6£Žù‘Œyl‰y­™RùžW¸âûóržTD{àÞøÛÌãëS¾ê‰HV7Vø‚Ùúà XýZΓÅNøÆ1¥îoyº©ÞrŽ\0k'ªÚ%2Íß=ÀÊŽ+y2¸ÃT¥¶aJíÏ´r€ñ‡"y]tÅ£vƒ™š~júY“ûY¯BÛ·Õ"¹g¸¹“_1k­¹†òê\Üw­ ]Þ3Û·‹„íŠdž4õU`“]3àŠ‘¾"™æµå fÝ»^Ø¿½Œ'u ƒ ¡L©ü§g¢z”-O–ŽÏ†»Áy&l­a—Ö™#Íõ÷aèÚ‹Ì«£ËP>ü±@v½s¾SÛ‹dCëÝð\å)ó¿Ûº^ð(R*ß"'/œÞq‹'ö7™n›’`pâ•HvHŒÃ«­/™÷ø ¤oGFžñÇZÝRêþ²;ð+6›#ìv iÝ æé¥´0‡i~/ ¿Â}˜RûOöôEð:"é»6ÇU ¤¦Ÿš~Öä~ŽòHÄ·ÊW"Ùúg_|0OD‹¤úÜíÝYܪ}F$ÿ4ߏޑÔà,OªÏÇ´Æ«ºçDR}¾¬Ã9hýØŸHþéýTèÜÞ‰'Õçý§Ç£äÝkžüÓý-LÃË*‘üÓç5ýÔô󿹟[’Ÿâ?ùjzÚÉs9òaÏ$XͨbN0‘cáúµ<9ô¬õŸÚ0›ò‚uR:sÀâ@Œ”Á<×â<–54æÈê/'QÔ&—Ù©g4Žˆ9"Ùëgt“í™Rù½7=€©R'¿×{€õgk1ïü£@p§hŽ´¼ŽÛâ˜yñ¿™À“kêª0²þefþÊ8Äœ/È“cRвÐT$¥òÍWåÁµ8R>'æuÞÊȪᨚ›À“ŸfcMeóN=ßßÿŽJÒÌ;VUr”ºÝ!½–OæÉúæk8“¹Iï(Æwa®Ñ=¨ÙL©ý Bð<·+O>‚¶-Â9RêyM?5ýü_îç¤!I0ùU‡'nJBeKæ”’ßwn;3ê‡cÛ€ ¦}t8–ù&0ïX(°Rq™yo–-Ê{1ß?ÏEÛ™7OÉaÐ¥•HfíK‚e{”Ê¿jê}øß¹Å‘WFÜöM1Ì®_‚ðÉñ:³Yµ/Šíêód‡6"îÈ3¿¤°+›Íôì)âÀø`‘œy$ AgŠ™RùFV¥ã‹a-Ž\à‚ÔÏí™SƒK°´™'O^î}®‡Ï0'Ï Ç§¥ãDrÕ®@<]hÏ”º?sR&Tççðd“þYxî±Ùµ#êæ¯`Æü} ÍvgJío>-– W)æ…†Gï©HM?5ý¬Éýü«4 'FðäµJØÏÂÔ2 özqLÏb+S˜Ã„xtµÊÇ\+-‘¬ÜZ‚A¾Ç˜RùU7î hçYެîuûû,d¶këŽ/ûñäçO˜ ]ÂD=%Ž¢#ÓÌ(‡ö<äHÇ7>y7S$k—¸`@ó L©|W× ˜·p"G.ÛŸᇙ.)W¡|¿‹'w™]Å(GS¦Ów7¼óñɼg‘ÿ‰)uß§ÃïÏ—ñ¤Y‹4?µŠyö?æÜ­Ç49íÖs8Rj¿ežf” ÈQv˜w¯ZEjú©égMîgŽ sÞŒáI{mßž›0U ¡l‘Ã4:# ñÄ|æõ±8w¨ SÖ2uÃc9rÂWOh!”'sw`ø@oæ­ 9xž"’÷ŸÄÁ|c.S*ÿ¦‘åð*hÏ‘«´oâ‹O‘Œ<6ÜÂÔãøv*ˆ#Kzº£bÈæ°Ñ‰Hß|W WU¥aÉB‘”Ê÷þQ Ò|~päO·h˜¤¶áIÃ×øàPÅ‘òY¹0™cëœGɺ|¹b³'¬Û®Q’R÷_éåãÎMpÓçÞeJíïÝüä…my2ö žY-ajú©égMîçíЬ«Þœà Ӡ5á3×:yÇTÌKU…¨0‰dÞûæaü9Ž,w½€¯»Ì~›#ð¡ãwfÃïr8šîb†˜'áꀋéÕ¤½¦–3¥ò'-†ÖˆQ¤PP­¶Ê$rœ\ŽvÅ<s8à¯1O/qB§£í™“ý0|Ï,¦É™ÔæCEROŒA'Û¦T>½›QX»sO~÷ˆÂ¥„™LßÁ™HNòæÈW)ÈìeÏlq'¾‡g ä £H¤l-’’ᅢ Q~j;O>_¿‘;˜Ë>ABê4Ø}Åd¤Ôþ¾ÏÏ`g[žt.pÄuvLM?5ý¬IýT½ML‡¿òOªÏ'Û]Áǹ><©>ÿxâ,lgwçIõyº“/NòÇe¤ú|ï§«8³s‹HªÏ¥ò'6̓uLb©>÷܇»U%<ùÿÌq@¡¾ ÏT{Ýé‡Y"ù§ùnVG¡Û=sžTŸkR!=ò,KùæL½ô“ÀŒ³q€ëm[¦Ôý¿úÁÚiGŽ˜î’ÔáÌŠp˜)ü2'5Z0¥ö¿é "Ñ,–'wÞËBK]/¦Ôóš~júù¿ÜÏ“»òáéÄ“¿¿WøTžc[žƒ 8²òW<·ÖåÉÎ9.è¿è„Hý鈟L˜vþ!0KräH³H4Š{È<~Ï]•×2¸í Œé~_IJ埰=ˆ°H…26£F0]laËà2rEâqôòõåÈnA*$Nï¬"‡ŽLB^ÓÚ¹By­J™“;x¡nãå<)•Ï=&¾“òìÀ(´Xý‚9\žMª ž´k]€'ñ—˜ã?ú`À;gæƒ_áøõñ(Sê~—Ƙ³¿;GþJ@Ø5]æŒrÝ6Ér h·m&’RûƒÖ`Y‹}<¹*¿Z^ý™š~júY“û¹ý[!ŽÅžàÉo?ŠÐr5óBh"zucn7QÁÈ€c®Zå„=]šŠäçpW¸l È¿.þož¼oå‹„Ó;™e}NÀ§V{æ„§N3°aJå_0= ›ÖȉmUèµ&AE¾Q? B´EòŸc‘ãaÀ4yQŒù;íyr·ÖU8œ°f:Å´a˜‘žÑ°}+çH©ûÜÆ@´Jù)#§4 ‚ÅškÌD“L„ä-IÿF©X³Ü‘)µ›n>:´½Æ‘\×LXÚÌÔôSÓÏšÜOei1î”Zðä½Wàñh³$;éL™ŸŸ¥C¯zóJ_/¸–ŒH£,èW4`ž™„7­ÝxÒÑ,oúD2³÷cÅÜÌÜ!ÑX5±)S*ÿ¦6"¢lS‘)| L¶eZ®rDNáPžÌßþ£˜cd©¨œÔ™¹pG6zœÌlø,úQ.L×´bX÷À”Ê·li¦¥ŒÉoaap_d 0ºŽÙk—óä7à|fÓ¦}0mºqävíK8=І)ußÃ#™b ŒLêŒÔwÛ™›€KÕ‘"yó^(â%2¥öŸ?'Àgb_ŽÜ>=†C·35ýÔô³&÷óEõ ¦<ÙpH Úï܆1†·•䨒 èëßgÞïïƒI}óäÖVHxܹ`_6æfÝzBQ'f¥n.êÖ;Ä‘±W”(üÔŽ)•ïÃÑL]%’:/‚=Ђ¹4æúNnÁ“k>_Ç e9G¦Ø8£oÇv<©>ï´(›7Фú<­ÑUtªnÇ“êó”ì ŸžÀ“ês©üs½ò¡ß0CEªÏ} Áˆ÷Æ©>ïåð{obGcR}Þ»G*L/ðäŸæK™ìËþ6"©>7Ë-BUn„ŒTŸ~HAú^žüÓû.Z!0ž{FEªÏ㿸Ãsú üÓýC^¦âÜ7WžüÓç5ýÔô󿹟Å;øOšË`wù„@6¹æ‡=ÛÎ3?>Š‡Ñ­ŽlÀÇáiÙf÷ÁqÍÑX$›ù^€ãô*ìÐ8󦈤4bNqUË›ðd³g>Ðý<…)•ÿcY)þ®% äËß¿m,^0;¦Ec‚þ,Ž\u1Y˜•!žh<­ól_7÷b–F&à´¾£Œ<'⸉À‘Rùn¸bÑ»ÃYû£3Ü"™›WÙ!L<"Ê©§Ñ±k¹Šló4Ƙ͓Çõ“Ðc»-SêþÒ^×ðê|#ž¼9õÜÜþáÈê陘±ÀY$ïöMÄ}Þ—)µÿ«N4žz8Šä´C°xïÆ”z^ÓOM?ÿ—ûi9߭䎩“ãó.̇1°Ì\Á‘ºu£°ªbóÎÄ‹hö÷Tt;ã­{cG‘ªFÁ°æ‚òX‡pÄ–›2cT蔢͓ÖÛ# ”p¤Tþm÷JqroS‘´4-…éüîLãKapšY‘F#ÂpjbSžœ­pÁ²Ïu™cc]q¢|4So{{‹rcÂ7+I©|u&8Þ9Ò££3Ž-jʓ׻ŸÂœ¤52Ò¤á)T­]Ë‘Ñssq(ï(Oѽ¥)FL©û.K¯aR÷jŽ–_ã’rf×ùahúÂE$—òƒöö=L©ýSïz ¥ÕY‘¬4ºˆnq«™š~júY“ûy Ÿ7ÞË“ig‹Ð±®6S*ïy0®wR$ݳ±¹½=s•* 6<¹}_..?ÿ‹yù«Õ—™×Î¥#ªú³Ïð`¬[(Õu£‘i9†)•ïNé%Ìi}Š'w<ñÁÅÁ™‹rœ¿Óœ¹ÚÚ‡M÷1§' ¢à¨@®j„’r…1)ùûk{¹Å{9²làU<¿=û1m§Èû»â ÿü†)µß{£7VŸàȈý8|ù4SÓOM?kr?Ë=Pœ8P Ÿ9âtU}fÕd_,¿ÕŸ'7Øû¡_Þ,f­vhV­%’[8/,¼Ýˆ™8ÒºûÏ1=»¹âu7kfj~6Úv»(# ó’šzߘ”Ê?6'ÁïErI^*>íôd:æÃýÜžÔízåU2楹˜`iÉÄ•´Þ"cúG(Ñ«_´@~X¦Âß ÇŠ¤T¾oKü±lŸOîè ÓÈË̸M>0nt„i>-æþëuebÓÚpdåÀBT“R÷ KñíÞŽŒYZ £×-˜Ðë5R$#®†â–ãF¦Ô~ƒ!09ÿUF¶ŠEñ#{©é§¦Ÿ5©Ÿê¯æÏp÷S´ŠTŸïz€­-xR}~±Ô›Ö|Hõyý†ö0¬5Q$Õç‰cá9DÉ‘ês©ü­‚Dücí#’êó9õËñúY#žTŸO/ÄÍAZ<©>¯ Ãwv"ù§ùv´Cd@OªÏó+卨|€'ÕçÃZ\…|ª z¿à~ ʧVÊHõù³¥—á[¼O$ÿt¿ât0 ÷ äŸ>¯é§¦ŸÿÍý\¾ ÿɘ)xÓ´HÖÞ%âå?­˜޼ˆF<éø1Ÿ0ƒg†áÝÛƒ"ùÄÜzþ5b5lÊ Ñq€mi´@x X7ÌF$¥†`Ù¼yL©üqÒ ᛌl»2%‘r&b0Áq€@ö‰ŒÅÏâ“Ìç·Ã±þV[YŽý9ý²ÚºO+O0¹ç8=[$¥ò ŽN°êóG>슱±:<9wB0’G™ý…çȚR7ú÷Td¯”X„UìH©û3ïâR‚©HÞúT€¿Î-bž|™‰º c8²Í§TÄ7ªÍ”Ú?+0‘ÛZóäçÜèöHáH©ç5ýÔôó¹Ÿõ¨Ð_ÞB$ËÖ(a×¥)Sûr4¢çÈx2¹{ì´M˜õ#Ü‘xÀL$︈øŠÌOÙgÑ/ÝDE8ÅAÿ‹y©i æîNÈ¿{GÀâÇw)•ßLHFÅòÅ2rb± ™×Mã`kŸ#ñ³cÑüB=‘4›ŠÉŽé×<wŸÌ­=T¸ÿÊ^$›ž F­úǘRùÜ/yàÓñNC_¸){óäóA8:â_>%¢eÓ™Ìï‹U¸×góöotÜc*’+n:`Ü ¦Ôýð:y(yµU$¼Ër²%3ãA4&Ùräñ½ éû–)µ?§i(\ zpä¶A”—˜š~júY“ûÙ94;Þ ¤Q\æó˜ÏÞ§ãIJ¥<Ù¿,˲L˜ŽsÎÃÒ 'G>éîˆsÌnƒüÀ•XñäCýßßC–ïa¾=è‡f—V‰äÕ XãÛœ)•ó÷¬š©##þЇiòjf¬~0ÒU3DrsS_ŒÒ6aNsÆ£0Œ2s€uÂÛ$rr`ÌŸÔR‘ vFÃéä&”Êçøûî#7Cž|šÓÉ™kJ8¬kÍ´X Âáw/Èev0£PE–Ÿ8Ù¬bŽ”º_ž ÏaûEò]\lb3u¼’0VÞ•'—ßIÇ Ë¾L©ý×F"N¬Ï“ufÉqùø8¦¦Ÿš~Öä~6 ×s%9o\,b>ˆLa[!ôÏâÉÌ}%¨ÜÑ‘ù¼Ê{eM™ë–ø¢ÛµÌúsâàú~>sÙ<%:tcGØ!Ï/WFîû肬€î<)•¿°o&´•‘;ubaóèsf©Ö‡ŽÉãuœq9Èiíg®O¶pd‰Õt[÷•iŽò MDRåƒFO{0¥ò­Ð Gp†O>ø}aµ™> ¸ÝZÅ‘õòU°¯8Ú㎠yž< Ûº3™R÷—žÉÄ–UGE2mq¢Bm˜íü Qõ³ Oöíp:kÎp¤Ôþ‰#¢ðîÑžlX–„;15ýÔô³&÷³_®ëU!i¼O§]˜O’¯aÖ˜·™Ùü&îš{3?t—£×]ž¼Ó-í†Vsä¡ã*¼©wY04 #U[˜}nÊ¡3’ãIŸ<Ü*Ê8R*¿·‘‰ïºsä+ïŒK]À<1ô‚»È»·Î Ξ¹á½#úfód‡lwDüÚʬøûÎO²ºÑY\Ÿ1‹#¥ò _…MžpäÓhLšÁü! ‰%3˜^~ Ð*hÇlö WtàI³‘qP5»Ì‘R÷?uM‡¯ÒV$oV¦¢¾ÝIæÓo7ñéBžŠ<†ëè|é£@Jí·^š‰‡öñä¤Ð"œ=1•©é§¦Ÿ5©Ÿê/ïù |ò>&êóçÓËpU3ŽTŸ;m/ŽTŸÿ8‹¶>Ú©>ï ëŒÇ2R}.•É¢ÔŽ=ΑêóUÓí ³]EªÏ/˜ú¢ë€“<©>ÏÊp‚à9š'ÿ4ße4Ä>©>÷j‡X§Z©>¯n…ƒ»ßËÈ?½oì—‚O‹¤ú¯é§¦ŸÿÍýìSï:þ“±é±dågŽŒ/tD~®6O¦œÉFýZÃDrf¯ ´ œÏ\óû}Fé;€#Ç=ŽF啹÷ˆ30S$wžCµžL«¥ö¸’ÕS /d;¹m-ž”Ê_h—Üyh¡#¦ |Â|d¯À›‹*Re­ç‡“Èníá³®=OV¶ÃqÏÌ]ûŠ-ê3=']Å¢.2R*ŸíÚpx%œÈ«ñ¡ÐëíÏŒ®ð­b+³nc%V]É<ÐÎGk-ÉM Ý0HyS ¥îw¼ “‚^"ùÍÉ ¦õ`F÷ŒÆ‡í7däÀïáø wˆ#¥ö/+T`Õó"9êP(g3¥ž×ôSÓÏÿå~F×wFÿ®x²é6L¾Ø¹æ^2Ì÷Yˆd·»I8þüs¶ލd…ŒLj‚CÙ}9²qm9~-®H›Ar$cºo Äþ‰óxrbj†)É^›Òq·ü«ŠTüþÜ1µÎJŽL¼ ý2rÞÂhü rÀžøË³T¤äç§r¤¶rî(_ØEÄ2?Ü‹,-Tä y,’W¯0&û Õû½9r}‹Hä?i¦"%û³ó"wÉÞÛ<¡°jÃ Û Âòý`ž¬}8º³Û3¥öW7v€Ù_ ì›r‡Ú&‘š~júY“ûyò¢'ô‡ÌãÉ}g½à•»‚ù Ó2‰dy¹ôúìaæô ƒÓ›~†+”¤Ôþ¬¸º)œ#ÓÂ~¿¯îÄ“š~júY“û¹é²êTmàÉ#ù¾àmg.á ý€e"9k³Fnæ˜-Ö*á±cOfµÉ€ygSfiU†Íú*qu£±×¼‡HšOÈ„ûÛ¡<ùañUdßÜÁ‘Rù6øa}ØžL· D¹Óa¦ø1ŽVíDòhH ÌuÇ3“Fa™ÿW9"ÝЉÖ9ËI‰®gÇ‹d‹lóÚõ¯R¿ÿI»H §E¸âà§Ì~y‰èÝmGNۨ€ž+˜în¨Ô÷’‘‡»aç¢Ú<)ußý˜,,ˆäP7WÌ6®Åv¤×£ŠÒ×0S†ŽI©ýÍó.ã鑉<©³>2çiLM?5ý¬Éý ¯€íæÉ¤ÉA¨{ws¬·+˜¶I—Îp/¿/% `°´7O6©.AHv9Gn#G'¹L$ÏõðÁ³­C˜S‚JhùX íë¤bpÜb‘”ÊÿL)G^øQž,þ†æŸŽ1*%VL^'’íŽÆ¢sãƒÌÝýü¡“y“#«àÙ£=OÎ.s…ÓÊ"9+Ížoº¤T>§º.È}Ø[E¶)q—óV2Òku2lÒWp¤íþ4èoÏ<4ÒfS7ð¤MA4Žœ8É”º¿%Á‡z~È–œñ²á3¦‡i ¾ì^+’ ž*›²†)µæ&úµÄ“ƒÇ%âI-¦¦Ÿš~Ö¤~ª¿_ã‹ãAžTŸë­t†õ볩>ï–v ÚÃês¤ú|¤Â+Ÿ7IõùÎᘳ‰Iõ¹T~ýQQXïwŒ'Õçÿ Çí¡GER}þ©QúÆMàIõù¾0gä÷+äÈ?Íw_ß žýr¤ú<Ö*æÛšp¤úÜ×! k·oâÉ?½ÿRÇ»– ¤úüjIvÆÈDòO÷—NLÂÞ&žù§Ïkú©éçs?ç7(Ų`C8JSëp¤‰QNš<•‘ «*V‘µ\DXÏâÒh|(n¿¯+’ɉÁˆø-‡jÀzA»$2òBJó|òñœ‹¨0.‘‘¹¡ÞH_Ú‹'¥òOiž½ŠnæÂî~ŠŠô;— ³Ø9™ß3mïíg>ý/SŸ0¥îë V`ŠðŘÙD#÷2rsW?øŒHÖÖuö½õ˜Rû¯OÆòg9çS82gœgjú©égMîgJE$-##¿ôŽB”I„1yº«çmµD²}P%w`nû­÷Ÿ•äæ1ð¿wBE4…ù0+‘,þŒän[™Þ­a±««ŒÔ¾}Ó;L©üõ;å ÂÛ“#ûäàQ×µLÝ›)0Œ_Á“†C³ÑóŒÙÒ4íËH;ûÝ)gºÜ Æöæ[D2a½+vno”Ê÷Wq¼’+9rÚ™lÈ›f1 .f ÐßI §ŒNE¿å fÌ Œ÷yË|*ºV+™R÷ßÄ`ÎeKyåa ⎹1‹–_ÄÓ½ø"Vû‹#¥ö¯ø…ùö=8208 ç6‘š~júY“ûY23 ¦÷"“H—éQÈð¬«"qñøzm°HÊÄ`YøXæ ßï;.#z dën/qaYàCy‘ôŸí†Ö‰U¹ðˆffrdv­4 éSΔÊÿàN6vnÒåÈÐÙðÜ{FF®hP€)³µy2ày0TΑ­Ã¡[6™cŽÅÆaÌ~³œ°'ÅFFÎøÛû÷ç”:<)•Ï÷W.N¿òäȲfø±{ óÍU‡[¤ dº]j-ý׺+œá°Ÿ#¿š_´åÃyRêþ‡û170IFVýþ»Ñá3gŽ;Ü»µãÉuPÿðL¦Ôþ“[Ã1¦Åhi™ ­Uµ˜š~júY“ûid…ªÑ;UdŸ‘x5,‚9žÇ€©"¹¼k0<¦0‡8GÁ|ê Íx2"9'§é0­â1_Û‹#%¿Ÿ¢|Ã]9=¢©&µ”ä—ß{ì‹ãÒÖ$sïz1ï„ýþ9vóä·µ"Ì®/gJÝ·ø¥ÎWy·³­7ks$—ûûÿ{9<¹w­«¬ú2¥öwöóGà *¹Uǧ7¸q¤¦Ÿš~Öä~Ú™Eâàâ Y¿(o4H¯_¾øQÁ‹$gà…—z3ÿ²÷ÇYï’¯¾„ųTÌ”´Œ‰äH›ê 4Ó½ÁLóP`¤£•H6mîˆC™Rù[ÊÓa÷¨JE~i•†>Ûôò›A>¦]ó`¾˜ÀJfB¶m3ôxÒfwæÇ7anø®À˜Feä£p|8ù”)•¯ªC1fÔÈý•E®8ʬ”Ç ÃÇš™Uµóx¦ãè\ìµþÉ‘¶còÜg°Œ”º¿t†×ÚuäÈ’ã \°êËÔ²KAØž2æOïè:õcJíß±0Í[6æIÓØP ªâ™š~júY“ú©þ*Ò‹À‡©>×»ã†Ò=Ú"©>Gº+†¬ì%êóŽÃ#оV*GªÏ‡qCDÛ©>—Êo±6S¦Hõù³a©(²m(’ÿo›ÿÝe©>Ï CïAù§ù´ëaüùtTŸª¢ásçŠTŸ7ý–†ÛÝO äŸÞ×-PàÜfcŽü?© Z§&’ºßcplýòäŸ>¯é§¦ŸÿÍý챫ÿɬòdèì†Hî(pÞ} sƳ|Šþ$²‘H/»Í´† t®0Ëû»`âÜùùk þ¾w“#s¶¤ÁÐ}3Æ= s7Œàɱ}ŠñÓý›Œ”Ê¿nG"î–…ªÈ¢¾ ˜RÏ|q*öOƉäÉe¡¨2ˆ¹À=—¢ã9²UÏL™èÀ4úêògÓx²x@ÌmM™RùVÍSâÃÜÖ"Ù ,ŠæÝ™óo|¹Ö—''ùcÏ´ÉÌÛgCÐÕ},GŠåáØŸVÁ”ºÿZÇ™ f dd}¼jÓ“¹v´†}Mf¾‹t†þ¶5JRjÿ$e Ön‘‘- ÓѰSµŠ”z^ÓOM?ÿ—û©ôQ±ßl‘ü°0u›/b6ËGÔºx ?†CgÏ2ëÖvøýã9²^/øZßb6“‹¤p%¹ü³ dþýl<¹ÞD$õ¿Ç`·±L©ü‹?Å¡î(Ùñ^, «Ü™¢èŽH6˜í‡ð­×2¿RǸÑÙ_©@¦¶RF¶ß’æé¶3 %§_¨H©|óÜb±,’¯GcÛ QÌ&‡åȲ„'ÛéGÂÚj sCïD<¹¢Ã\锉­)uOS\>RK ?z`S~¢Š,°u…ƒeGޏìË~ÃyRj¿õÀt¬¿œ,CEÌ=ÞI$5ýÔô³&÷óÚ—8œì³\$žRàÑ–UÌ-Ãð`òTÜ»7 ‰-ÿQ‘ѳð¶X‡'í´Ã1Â¥3gD<æEê d œÞ|IEºŽñGÊÊ;YWô‡õŠÙƤTþÆ/Ƚ¾UEöV _ŸfÌ?.ã´öa Þè‹ë3ÓUd·o¿¿— Ë0&¿›þîÅÏäQ¤M„‘|’@.^¯ãL©|.çüŽÉnWä°>lÌ|üû÷è°›OÞÛ¤Äæˆ…ÌýB¬¢Ésqã`-‘”ºe†¬O®T‘l~›à£$•‡A•»‚'eä?g3¥öÝ‹ãg&Ф¶¯×ÖMejú©égMî§²y4zå­É•ø1â_åÇáýñ”Ь¥ ý&_”äÝ« $hÄ“æV‰˜…# "cÐÖíµ1ùP7åâ3Ù;6ïjsäp­h쬠"¥òÇûÄ µiEÙrA ´“²ŒÉ³ÓýlYGF÷ BõØÌHŸ0˜öÉ̼‚Ô¥M8rö§(˜¤"ç?Ї}Øk%)•Ϫy æÌ*’+-|Q·¬óÆÁä›çÉ„Ò ä×ïÉœZ_…«³‰dXïjÿ«Ô}¡À©¯¶“Y]=áj7^FŽÍ@±OòSs¡]t˜#%?¿Lõ†«®¡HV>uÁéôxÔôSÓÏšÜO±MÜVˆää@9þž¾„Ù±8 Ö'”dåé¼éw—Ù£·ÌþâÈ…iIÆ•ÉÈÏó“ •ô‹iš!¢×a=¦Îw9æM¬/k^ú"=¢#GJåß;3Ëysi~2ÉåñÌs[B »Ñ#ßX‡ãˆ Ï´ Á¯Ç™¶aðžÏlµ: áƒír´m4^‹ER*ß”Þ(žÚZ$»èxâïÖ¿2yhüøº<™w0ÝF*9ÒÉÍÌj‹äþón3³PIJÝ×µõÄÁ8?ùó³'&-zÌìø0[ü‚UäñG©ðûæ#Rû“Lx&|iÚÇÊ4%Gjú©égMîç² A°ú5W$‡|ñÇ‚ûÓ™\íXTMÝ­"í¿Å"¸¸’Ù HuZkLF7Š…VáßIä©hìØB ç*Spii2óÁ›`Ì)íÀ“fe*¿æ˜Rù»ÖWàùŠO2òr†m82æ~$¢µ_ÉÈân¿?Çtˬí…êéµxòûôxðt˜ _d—h‰ä°î®0Úµ^EJåksÒ fóó2ÀÞég™ž®ùÈö˜Å‘Ïç#µ×@Ùh¯>$q䜇¡¸Xø…)u¿tÏ%ÖáÈkµ¼°ã¯þLÇÖI¸a›/¿ÿî¬;Ê”Ú?&Þzý y²Z;'šOfjú©égMê§ú+-ǯþ+’êóãpû‘‘@ªÏ«¶FC«Gw©>k¢‚n•žHªÏ£L ñ,щ#ÕçRùT±4#Õç΢aw)HIªÏ?°¶‰6OªÏMx"ýtžüÓ|ç8gLîÖ^ ÕçMä!¦Ç]©>ÿ‹µ8òOïrõÂÏÙS9R}>Â"ÝêLÈ?Ýÿ1(¿ZåÉ?}^ÓOM?ÿ›ûy]VˆÿdØÌ”l¨"؇£¤×u%ùäE$,:žeb{4¬gmEîÑ:w±@ž ŠGÑxsQôïÿÎìófäóˆX,ïuJ"'Œ°âg¼‘‘ÎíÓPÆÙµÖB÷é¾HŽyfFÞ“Þ¤Ü}m‚ ôö&}KB}_nßoÉðl´\$6›°tãM”Û¯ˆ‘0+¸#w\NEÄ@/®£ŸŽ~¤~ÚM™©Â¹ÛÛDÒ~ž8 ~Œ´Ÿ·’…¡ ½IûyŽá–+’öóú7 ð»õÆJÚÏåògöÖB?¶Ÿ7i?Ž5bk­ƒŒü_ó#i˜²u‹@ÚÏKW‰C5E5oòWóÝꇌ‰VÒ~þÃÇ„Z{ Œ´ŸKs0à´J$õþ8 ¬Ëú{“öóò Éèädó$u¿G~¯ïÍ+ÃÈ_}ÞÑOG?ÿ“ûy»ï ü;_)ð.ÓÙF.”SfQî®I:›ÃÈYËÕ¸ý,Ž»öA žN:/’WBüYÊ›ìøø žH`䨡¸¶È‹,5!æ–ÅÒPVÃDR.Æv &Ü*ïE™¢E§!·¸†%j¼jÙpÍ!äôp㞌Î@Š:‘?Ú1x”´‘›Iø^·%wm†—ÖáÊå{W+ed d‹šF;ñŽ[ÉjÁá.û¹åZ*ÞâþÙ\ UÍ‘lgLÅÂy\¹û§›YqqFYo2ÅSB— ¥¹„åàkë"ùâôQTi(rû«¼·áð_£ù¸¹½ÏäÊ=ïè§£ŸÿÍýì蔀2Cþfd™“ñHívƒ{bº7{†pÍKÐ=pך¦GÚgo’MLÃVó{‘l='•—¹!â0êDuoRÜh¡•mErZ!#¢'¬²’rùŸÐÁܤ–@zBÏ_šs¯eEá܉B6²ÁøúO#“âRðæ\#Ùù¢C;·ä–Ÿ…™½:0²±s5.ù‹ä„Ú¹¨6ý•DÊÝŸÛÁ#µ„7y­Ÿ æŠE¹Õ¢r0¨ûVF¾´1Ìýñ+·¿¸"E{îadëæ:„MíÂuôÓÑÏ‚ÜÏ1¹q¸`Êdä×7±(þ3»þiî~jÊÝP<Z®•Èqyîü®Ií|vUªÅ5ÕÕbäÔöÞä_3Ú¼oÂ]rTƒ’Ç&ä‚=VoÜ/’rù66à͆:Yæ‡úϼÈÊÝØ¥–ÈME»Ny‘,ûñº?¨i#U}£ðîâ#F¶Ž×aÀË ‘l÷"“çvçÊå3 ®MЉä[fÆâM÷òF1=^ŸÅÈâ·µ`Æ6Ü•còâ6ÒižkS=¹r÷Ÿ H‡ùâO‘ÜØç0BÖ~ü—}ø”UÌF6 M‚â\#åö›6i1¼É^‰l0F×.í¼HG?ý,Èýü1 §Öogäñ;1×a<×µx Šz,H¿Š1èö{w‘ôžÂÐiòI Ùd‰¦ç%ÒÜ<¥w½É¢o³QsÑ@î,¡ÆÒPn›ƒÙ¨¬»'‘rù»¦&ÃUÛÇ‹<Ò˧¿ªXÉ_"0ÁS'’*s,æÿäΆµ~ã™  Eõ/ÃòQ¯tªXÃJî쨇_øKOR.Ÿ{}+½£²÷p nÏJq7LÖâ˜õ•D ÔB?e-woáXT_VÁFvË Å€Ão%Rî~2°Õí…H6k–‰Ê×îr«Œœ¦@´¯·•”Û?{„å¼ÈÁêt°õ•HG?ý,Èý¬ƒlß&Œ¼r —=ïJä°.ùßÇç "Ùe<–js?IA¹„ꌜ¥KÆÍî_$ò´G6j ÝÌ6 ”Û~ÙŠ¡Õmän)­ ¸rù§¨Øq¨œDÆ]7­ÌFî¾C ´Ë®æMž*¦Ã3“+w–ù žŒɲ—£¡ù›7™¯ÇÁŠE2¯¸rùöwa(_+ÉJþ}žáá×$‰TõÓ¡Uô+ºTŒj£½H÷^øf:"’é3Tp]çîMÊÝw+“…g.Šdèƒ,{”{ª—yíÏ äýùz¸/?Ç•Û?àd ¦]oÉÈß“qo殣ŸŽ~ä~.b1Øzu†D†;ÇÂ)ð¸…|÷)o8{“µ5Ôëësw—3`¸Ð_"+N2ÀéÄW/òÀj3&|°22dD2Þ/Jæ& Ťҗ%ò÷wÑx>º´7)—ÿû#ÊæfJ¤Ó#êovb¤¹F b?Tõ&ËyYqîžWòRà™[k®G¦WÞys¿ùeÀiíD/rúðt,1Ÿg¤\¾›Üòj1rP,CÅ'Ó¸?îä¿Çm¼ìEfD«¦±ÜOœ®àêM–<‘…S¾ûDRî~€. §XD²Âüè6«¸a‡’ñ°VˆYLkÀ—– ‰”ݯנBÃÈ¿ç$bdz=\G?ý,Hý´ÿZ±=nÎ-Ò~ÞÃÕ€i©-½Iûy±¥FüsX-öóÞ´ˆoº–‘öó»›RÐÖ³½7i?—Ë¿¤°¯_Öa¤ý¼É–½NIûyŸx­kåMÚÏ{ºëQºHù«ùÖž‘pØ/„‘öóë1&øÝÑXHûùž&YévQ"õþ€z¹èr7L$íç³KjQßí‡Dþêþ¥CbqSóQ"õyG?ýüOîgÇÈcøwîÍN…[ä ‘x& ÙÇq;'"ñY wÄ|†wOçV¿gƒóª¿¹¬pê—ØÍ}6+Zoó"µçñsA‘Ìɶbß§j6r¦å4î.\¹üƒ±Õ⹪f2æ½×r¿öÉAF±KòþÜ,´JìÈȥκÝ\ÄÍz“†šÏ%îo‹U?s›~?Í2vH¤\¾nF˜<Jdå¡É¨êïÌÈä×Yð*ôZ$W}ÊÅo¯â¹‡n«'@ ½‡kðÏôÝ")w_`ÀŶA¹ænâvÍKD^oWY±PtaÉŒ”Ûï«Å‰Ì*"émÀ›ñ‹¹rÏ;úéèçs?;]2ã–iHþyÁ‚¡7¼¹Ýh°Çù·âlêŸÏå¶¹y®Ú~^äÙ}¹Ð«v2ÒÃYƒaswФªNþ÷¥ˆ?wI“ƒø9c¤¹uPF´«áMÊå7Fi)ŒÌ^¯E“}i܉WÓq½´’{¹ž–…W¸žŸ“ÑäðcîÈ¿Ôéô’;3L…ïkÆ‹¤ó—d„ƕ˷ÔÓ€cM2Òﱭζâ>—Ž¢æ·ú"Ù´Á1„Ôò´’‹Ï¤`ßäL‘œÜá0^–›Ä•»Û.þ= dè¢dtÛ2ŒÛUŽ~ Nz‘RÇØNYDRnÿµ­©èzÎ_$w(%„…{pýtô³ ÷ómþçêÝõEòfŒ„º—Js¯ÿiBàB#wÞÝT4~Ì}è•…ý‹ÛÈ·ÇÒЫeKî°±)ðÑ\$·O…Úš ªîM5¹8ÕÄW åò? Æ¶•&F>P©p°ªš«ùaB§¨¿¸]õ¨p;è\êÖ;ÎM|ƒÒ+—s ÕNÁŒg_¼ÈÕ yí*‘rùúÌÕ¢»© #«>Pcg”wgó£81Ôƒ»úyÎÖSrÛ,ÌB†¢ wó. w›±‘r÷ÿR&£dq¬u=WËWæ>‰V-ëM¶hž‚aµÊpåö·}™§Ç¬y &áç§3ÒÑOG? r?WÍ`˜Óâ²@«dÃŒvÜðíf”ÿ6Q$S,0+p«¿Ó`ÑÚ66rFf<.o«Ì]5ׄ÷®[ÉÇÿ翃k'‘~J|¹ÇȲcÔ8á~ƒ+—ßý”~£"ù¼T"ÙÎ=ÑXö S¸ÆµJÌ;¹–;½mª +.‘ÏçGáâ˜A"yÚªÅúÊ™™œÿÖfîB‘”Ë×m|ü¨ÀÈj_•èwá–D® ÌDÜgŒÌnƒGgÙ ¯¾E]¹ïïD¡jÕ FÊ~þ6bõÛ;^ä±F˜îãfÔ³áÇßL$¿¹fàz…+)·ÿt#+Vô½ÍÈ)“ѸWéè§£Ÿ¹ŸÝ"møÜ ˆ@KG§ŽÎV²Æ ü"7 ä£\3œŠ\6“/ëGbš"™‘£ÿ<ˆgë‡JdQ“Ënx’×épjÇ1LÞš€7[&Jäà²Üpë!’rùï-:„a‡§1ÒÕ%•ãZrcG+0ìv1îaƒåSXÉY§cÑùâ5‘ôx¦À4]Uo²Rm†;£Šˆ¤úŽ ÷]f2Röýè¤ûæøIdìÓDd¬|a!›ì2££{m¹'Àˆ«Õêsÿ9w³+¿Èímã±´RoRî~¼hD‡K¥½È¶ýÿçï53›Éc/lø©)Ãȃ͘ÑwWnÿS'%ê(j#}êÄ`q#ýtô³ ÷óN÷ttØé#‘›Ç¤cžËÛ•†‹c¸¦RÐkw2·OëÌÝ´V$?ÞÅùŠß¸Su~ãɽjIÁ« øÃïѯä}TµOÆ‚¼–)—?ðG,ô.×$ÒïXôÛ_‰{KP!oë|¿Mqs‹ˆäá›z\wnáMf² ÃÌFÜWz#j³¢6RèÅç)—¯ÐŒDøj¦ dáá‰ØUé'w]¬ͯԲ‘‡›¨°¤©37`Œ ]jµó&Í‚ôþ‘HÊÝï;ÌÅÁJžjÄWm’±Þ{'#»¶Ò¢Ýì¶\¹ýÁ #`Š.#‘Ï÷G¢ÈÒ‘tôÓÑÏ‚ÔOû¯á=ÓñÛ‚ Œ´ŸïdBìÂSi?ßë‘„Ó]\½Iû¹o +ÞuIûyv˜æ9Ò~.—¿ß©Ü]]G íçY.:zRE$íçóßg ìJoÒ~^zJ\g¥ 䝿kžˆÜ›=EÒ~61#¾b¤ý<¸ÅQTQ••È_½oékDkïÂi?/²S×Hä¯îo?õŠž¬àMþêóŽ~:úùŸÜÏe ŽâßéÕ.bÇúÞäà…fôʮŽ:5ɧ,Œj=Œ­.?¹Í6i¡w[ÉðS°kòà|ÿ<‘Ñ'ã0¡Û+¹sšN%žäÏßóhÇDR.ÿƒxÏ­d#Ûä$âzX)nq­¹ =$òÀUü†ýô$Mm“pªF'‘¼û^ƒá{¹Ï¾pûcmn¥ef|þpQ åò-öQ£hG«D&ù&!¯Ådn»õIxåv‡‘N'b’W6÷rÉ,?6\"˺%B×|›HÊÝšŽ'#¯ˆä?ccE±ãÜ›©¹8h+ÁÈGÓcpÀ#®ÜþB1 ›g:Ûȸ·l\䯕{ÞÑOG?ÿ›û´ÏŠ™½\¼É3û.?ÿ;ßIÂé"Õmäß{Rà;° 7õœ›&ÖÉ)û ¸ªÃíäw3¸%ã”Ðw¿Í-V(¾Â îˆœÃØ›¶‰‘rù»Ô8„§]?3ò¸%[c/s\Òƒì&Ú1É0/ÛÌ-×*µ^êDò~£4´{•ÄMð²bÇŒúŒ|Ⓦ ‹2¸rùl‘*Lè +yÀC³¦Ž@N9ý>y·U,Þ߸ûwèqUñ^$Ÿ¼c8ÓýWîþû øE$§d x¸ŠûÁfÂ×…UldÞ}%¾?*Ì•Û?íþ^ðSldÓ¦QHhÃHG?ý,Èý³0=Î9y“MêdÀÖí‰Hv¾¡ƒKŸ¦6òj/rŽÖá¦.OÁÇãÓEràl ‚&4çê.ðOPIoîK+4?‰äîR /g#ýçG#¡ÕFÊåO1F£H#S«Dá…f·P)œ*NÈ33Sð¦ëv/rû% û†mÉ ­cZ«'Ùf]þçêÙMŒ‘‹všˆ¤\¾…-UÈúœ./·«ðéH5‘œ$C3‡Iä€ÑXþ[š@^{•¼€Û^dÛ+‡á¯8ÇH¹ûEzf¢T½0‘¬|4-ØÆít2?732ăçû¤Üþ¦pìºúÕ‹ì2&n­­"éè§£Ÿ¹Ÿ'd¢}­S"u- Õwk¸Ã{BÝ%mäa¯´»ŸÇȾ-–ßwÈÙj†y¾I¤¢Yª©‚D²T›\Lè0ÈJnëƒâ‡m"Ù}‚ %›6ñ&åò缊À»› y¡["Ž®–ÈÉÕSñ¾¦;÷t^ ®z?â¶“Ó… n·kéXQ~ #{×ÐÀ’R›thCÕž‰¤\¾åÃ’Pnó ‘¼³Ebg×s׬ˆA1Ý®:.M•nÙöÉhåìj#súÇ¡¤Ë3FÊÝÿÐ. ‘Æ"é»; £îô玔€U#¸ÊZôl{…+·¿Ú:½wñ&ë\6âgÿú\G?ý,Èý<'ËíÉ™‰9¨U{8W?7áÊõŒl磡 9y’ºˆ½ŒLZ—‚Ý­Îq;œÏÂ}·éb ó²ÊÙÈ¡¹0|\#’[šf!}ï%FÊåßP'I äë5ø­~‘ÌYj‚¾f{FÞjœÿ}ΘÌÝ›lÅ¢>×¹9kL(S·¸üy-K¯xId½ †~Sm¤\¾Ž5µ(±ñ€H6+¬‡õs"÷ÑèƒÞqë–NBv7gor{^8üñ"C;ÆáöÎ")w¿hþ¿gzìl&’Wœ³qóQqn«aùnÆ=Ó2ÿ=xDO®Üþ©6¼+ääM·fãbÚ‘tôÓÑÏ‚ÜÏÁá\^$§ü™‹«‚2«gâ7grc¿GÂcÏ&‘,õ^‹Úg0r¶¨Àë™Ü®e h•ÙÈFìH@ùe¹ÿó}+¤qå›…WN)—?qb$.OX*’ñ9Q8ãœÄMø]‡’q ™XB’†áÜI=4øæYÒF²± è•{Ÿ‘Ó†*qüu ¹¶lfö)ÎH¹|M›%ãà7ƒH®XmBv˜‰{ð´ ×ð&ß0!`RMî—´dd-jÊ5?ó¢ïˆ¤Üý#­³Ñ¥Á¼Ö9ìÁjn^„„ŒÙîV²÷p3æÜiÀH¹ý»ç`bŽ^â&†Â+•‘Ž~:úYúiÿ5si.œF^ì@ÚÏ¿”‰Å‚MgEÒ~žZ"e|i?WŽÂˆÊaŒ´Ÿ=Nˆ¤ý\.ÿ‚Èü÷ª§—EÒ~^HPâÐ’ Œ´ŸW¨ƒÝ‰[i?? ÿ”Ç"ù«ùŠßI…ùHÚÏÇ^³ J¡ŠÞ¤ýÜèua¾­$òWï·í˜unz‘ösçMFܨ<ˆ‘¿ºÿ¥_výéd#õyG?ýüOî甋¹øwÖŠ>Œð–…²ÿ…ÃÈe%' gX²ð#=¾¦aLg'¹¹J Ôߪ ä­RñpÓlÉj¢á73ÁLŠ3ãÐÍg‡H.ojÂãù§­äãVz„w|(‘rù'4§y=F®ÙŸ Ԯ˭RC‹’g× ¤ÿ ŠÜ5ôËp÷«µøÜö2wûq"M¯y£bb§qåò%ßÇ®yoù¥q,ÆÅdq½?„arû4‹E‘õ9ÜkEÕ¸d› ’%Ûñ~û|®Üý,êïmrÔÉ\l­Óß“ô\ Ç)÷F¶Xž„'×ûråöÍÔaQÌFž+¬F‹W-¹rÏ;úéèçs??ç÷°Vß=ù5¿§=~pï÷1âf·â6²X’-c qWIÙÏ¿Ô8xŒ¸(’Éi èRõ;wÂSʪàM–ø¤®Uç>T¤"¼E°D¾ùÈ̾Œ”»¿ñB6Ìã·3rM‡lŒÉ‹àú 6 aÈv‰ü§­3joµ’²ï_õ’aî$‘e2t˜°x×ÑOG? r?¯^3ã¨î&#Ÿy§¡ÊÎgÜÇ#°6i©HñRâçöxn¥˯ìed¶‹ãôåf~U¡0€û4X‰7:Käôþ9˜5à‹@vªœŽu·Þ1R.ÿø!:DW²x‘Dzò{6½‰@¾ë©Á„þc­dÐ}5ÂOŽÈ9ÙÔY¶‹Ûì¹?VÉœ•©pŸUÁ›\v& Oo‘”Ë×±‘ Sw•ñ&_Ó ¬‰ ×Å%›»Våf=° Ê»RÜÖÛÔxÖt0#ÝŸ'Â)±•”»?, ­öêyîx&ZìËàŠt8½:L '…àWU$åö{ÖB¿oŽYøŠwë\HG?ý,Hý´ÿZ´9oF~`¤ý<2Bâ-ÓEÒ~~!H…¡>i?P> ÃÍi?×™”p3”·‘ös¹ü3éq¬ü$´Ÿ/Ü Áñ …EÒ~~ÚÇ€g+{‰¤ýüþàÃÑ)ž‘¿šï­Ê€:;«z“öóÀÒé˜Ré±Hþ¯5UпÜ$rñþñOh‘‘ÇHûùÈ )¸>ßE$uñe¸_i#’¿ú¼£ŸŽ~þ'÷ófRþ}\%$¹¸ˆäu^•¿!ÃrcQáÀD‘ܱÿ&nÐqWÏ6âj‡/yá§ WÞçŽõQCõù9#§ä?×7!ˆ{¤³SœSÒËœ‚¡BQ®\þNÛHsE$ü¥B3ñ5÷·;©˜RÌF Ãô°},Éý{g<*^Ô0RzAÙ…¸Y'cqªb!lé—„'DR.ßÉÛ ˆ -,’×[)Pus®Ï8=¾Öwñ&wïJÁÕ‘¹#×§áKèï"Y'ØŒ7ßZI¹û›Û¦¡ÖÈÃŒ¬39r¹ õIøçSIù ÿ÷YC}‘²¿?˜‘Qh#WT5âé‰m\¹çýtô󿹟ãS$lˆ ÈœL ËÏ?ñ"½'(±ïñ-‘œ\ƒ¡>swNÒ¡W—UQQ¿mË<É5ã0£ü[+9xö!”[<^$?N0a9«ÂÈv5˜êS“+—²Ÿ…=¼É6]õXÔ¦·…» 'N¾edヌp¤„ž[+<W>¾HÉ8DЋä£”}ÿ/ï¾±Á-)ÈJÊåKûK‰1kEòå³$<µûhžÑm‹z“»#Ò±ÙrE$×ì5!¸š+#g3+•»ÿ8 ÙÓO2òv’ sÊæq7ôŽÆ› VYã{¡…²%RnbæÖÉkÎ&\É{#Ž~:úYûY鈖BUYªÉ1­¹!Ù‡áúOS‘ܺ(¦Z%²Ã ÎŒX#‘îCŒˆhò‘;í<Ã}cw…“kv®adÞh5öŒÈå®ì×o)ž¤\þ±JŽ8.’m’cÜÉDî’vj<ªÿ€»ü©íf¼æÖùžOÏKqYƒ¸Vÿ)‘RPãpY‘4 ©øli!‘rùZƧb]ÂV‘D[3¤×“¹ N™¨ÅÈä6ÄJ×¹ïð5ûºD6:¤âD#®Ü}¯òzœz•‘#ëæ¿WݹÌý;UBö5#÷ÛÉd„T|Ä•Û|b ªŒ«)7ÓMЫÝ$®£ŸŽ~à~îkhFÿÞŒ,Õ> eÇŽåênFÅZÇ%òpÝtL_4–‘“éqàX®TF‹ðì§é1Òˆ÷O#Ùø¥û—mæÎ&¡ÏçG"Y´ ¶Å‡¹rùÓGfâÑa?‘ Ø“…ѵšsŸŒKØûW¹¶“îöç–KÈ@[«™‘ÚX1<¥¸¼yÏ€ðê7%²²‡êù ¬¤\¾µ-x\Ö]$ýZ±hÌ#,6Ø‚Õ ÛÈ ›L(¿¨ J ºº-ý{%cJÛÁ")wÏïZT}‘‘.]4˜v<;ÌS—v©Ü?WB’X”+·ÿNj2îÚ%òòR=Ü|·qýtô³ õÓþë³G*ʾŸÃHûù÷i|E1Ò~¾¥Š!OªK¤ýün{”e¤ý¼RÕ,L5v`¤ý\.ï!ÙX™!ÿëþþtŒÑ^$íçÕ+&Ã6¤Š´Ÿ;G0ËO)¿šO¿ÌŠn?|Ò~¾þ€µm¿ÙHûù«?Ì÷/)’¿z¿’¯n-N1Ò~þ´MÚ\+,’¿ºÿw/œv<±¿ú¼£ŸŽ~þ'÷sÖï ÿÎ[MØåÅÈÑmŒ¼5Žûº)CÑwJ+™´H¾½—ÈÍÇ%<Ý”" ÎK8ºC#‘£,pª:™‘^5bÖn¼ÖŒ#æeÜõ*5\ÜrQ‰xóÕ‹ô®¦ÂÀr¾"™ÝY±c%¹oï™a}½‘rùܶB×°žHÞ“ðWâœÿ9}ë~œ•þ›„‡çª02VaDæŒXWù º'Á÷çj‘R‰þîݹ·]”ÀÃæÜ×בyô€D¶8“„æ­7‰ä&'®õŠç´¢nvknÝ SŽn´’ëÖéá<5N${5”07¤•¹åEŽ›¬@«YN"Yxq*údíå6ÝgÅ’³­¸>-ÈÜß‘Î&É-ZÙ‚6ßgsW5Ð`ÐÔ=\¹üqs­hÔ¢#§ <«4;n²Û/zq‡e%bÇìåÙ÷µ{‡„ˆäˆkÉèÕ$žÛãwŠ6ŠâÖI´`³¿/W.Ÿ­šyãBù¥¨¿EoáŽê£Äõ6"ynbŒ¯ãž¯¢Â…ë#¸uuÉŸÏ•»ŸÎt8×3†‘N?ósôäz-Ób잃ÜsŸ8qªWnFu†"ÉnyÕׂ·Ý&3ÒÑOG? R?í¿&ÝKĽEQi?¯]S¾"i?¿šÿÞPvØ OÒ~~¹ñ2Ò~ÞÓ”ˆbK ¤ý\.Úñ4Ü©¶…‘ösíÅD\­µG íç†miøóì6‘´Ÿ·#!¥O!Fþj¾€Ž*< ËHûy± Z\l*’öóêÑVôZà.’¿zHw-ž/c¤ý|ƒ-/VÏÈ_Ýï•bÌïa,#õyG?ýüOîçì+þž÷Œ07gä÷=Éhõ3’»*VÂt¯õ©ÜdEô¶:ŒT]’оº·©Éÿ\·þQ“‘Õ>§âL‘mÜ e ¸ù/—QalwJI%Þ¯«È•Ëÿ=/!3÷ˆ¤jNŠÍòçvô7âõÜ>iè»lwn ËœÈÅ aºHdµüßW£¼‘V2øb Ž ÚÃH¹|¡;%´î"bþ¯«¸Ø“œj:›‘ͪ¥âÁ¬ÝÜâsRðéÓ>n§Uj¸þ½‰+wÿÅ¡Tìx³‘‹¾§ Ç#ˆ» š~­#¹©ó÷>ñçÊí÷ϵì{&‘›ÜÓ°üõfFÊ=ïè§£ŸÿÍýŒïlÀøŠQŒ¬ò·¯Æ„s[ì4c°y·Öó6qOžJÅ´ýÜÚ ¨?8Š;q~&—ÿ“«ë À“Òá©ÉVa¥Ëx‘L葊Źrùg\5£úƒÉ"ù¢„G:þÁ]õ‡#V¹s§˜$ì "oYp?ŒÌn˜ ×2»¹®ã’°ÿ…Üf]˜Ûè@Êå[vPB³yÛ$ræ-+ê&”bä€vF´þÁ]ÜZw—ùãu"þZa%÷¯IBçÀÅ")w¿eÿTÕ3²r¨ 퇇röW {„N"kS`Vߢ")·ÿK3 }ddÑyIèó|×ÑOG? r?Ý5Z8=ßÏȘ%|½¸‹;õ~2ï‰ä6ÉС¯ñ ÷óR5nÙÄ]øTÇ9¥¹ øVë‰@®œ¯Æóå›DòZº„ûZIdn¢õ.…3R.ÿ§7VŸST$Û%D L ó¶Ièâ.‘ÏÓ-XÒ•‘oçß!Šûóœ#únã®*¢ÇõÀp‘pׂ 5À•ËW$Ñ‚À‰=¹©¤ûµ+¹‡kzy'·ð#u™ÌíÑÑ„àsa"¹ ’„#Õ>¤ÜýFù?·}Éÿ¹œÒ؈¹.‘ÜÃj ‚WïÉ•;L¸¼+”+·ÿÏV Ì>´W"×X•öI ýtô³ ÷óî`5\:ú3²TÏ$¬6Ìã–¼¨ÆÆqÛ¸ƒg¨àzjWû1'¶½ó"[=R¢o(Dr}ßd|F÷ŸŸix±q·°«Š Õ™­Bæ¶Q")—ÿØy õÒC½ÈWÙ2FÔ–ÈÏÑ©ˆ®¹ƒ‘>©É°å¿—‘·'+1-£1·å›D\Zm%“R¬8»·#í1àÇ€(®\¾ðª©(;7#{½2bèçƒÜiU•8S¡ w^UF'Αȉ³¬¸5¢!#NÆ–äH®Üýk3“q±I#w¨ Pü—ê†V /ÞF$­G$M¨ ‘rûgæ¨!žØ*’õó’QHÉuôÓÑÏ‚ÜÏÜ‘ùï%iƒé½K‰?«üÎ °*Ðûú7‰ÿ!Çg=´’¾´è›½W$ë­2âãÀî¹JßKÈAý%”üpF"Ý«§bÀäÝ"9ODçe7ÓÈ»îzTpŽɘ¯&„¯8À ð5aÝœ0îŒx û³Ö¤\¾=4(=r'#ó&'aþÅ…Ü…³T¨WÝG$—[Õˆžµ•Û&GƒnA܇Sî¼+w×JD]#ãr´˜;ã·1K»+¹ÖÎÏ©¸Z"e?ß?[1claFV;Š„½\G?ý,Hý´ÿò©¢€SêL‰´ŸÇNHÂ¹Õ EÒ~® –v5P"íç+«©q3k=#íçkUp{2L$íçrù>HžœmŒ´ŸÏÏP`D§Ÿi?ßTÖ‚¼6cEÒ~>èY¼ÂÖ0òWó©®(qu ÈHûy©:èòÂDÒ~~=ÿ=/(±¶@þêýš•´øÞw/#íç­Z*q¥|M‘üÕý,zÜÉÈ_}ÞÑOG?ÿ“ûy`óÿýõÿŸ¦Ãñû9oýÿü>wso6K¾j÷£[2Vu ãžY ÇÙ°îÖ¤$lk5‡{½‘‹JH¤a•“ŸTÉ­ãÍ×y ×#Á c #åòçü°àT©&"¹!ÏŠOŠ'y|„>­Ó¼È­Ÿ¬øXá¬DjJ§äÿÄÈcãôh7/„”ŸÓç^U‘œ»7-^…råò¹¶bÜJŒ¼\ÉóšþÜu0æÖz¨ÆUü¹.“5X´U$×7NEÝI\¹ûF›ÐÉ-˜‘0pco(4µ¹H~ ÃÈÃû¸rûÓâUðvéÇÈëݸæÎJÊ=ïè§£ŸÿÍýÜ4Lƒau¶2²Ô˜´r%÷j]bïåž«†±ÛîA«­ZdKäõU ìÙº] Ÿ]Ñ Gó"ÙÊjÌÿsp€û8M°ö¡ŒÜ:]uï¦I¤\þ›C%TX*}ò½ÏŠ0Oòþ=3z÷ÊÈBa)ø:q·“s~~[Êu¯£Äªò…¸KïX1²ÓŒÌßÓ}ðPFÊåK𕆫Ùei ^ô äŠq*Œ¼Ó—;ÀY æóE"gM–0@1P /1#òäFÊÝß÷&o§†2rJ‡dˆaÜi…S´b»H^4Zá³ë‹@Ê횤DŸõDrT_-¾tä:úéègAî§¢StŸÀÈfUøT¹ ÷MSÚuðäV±)Q,W"3 %áÎn_‘,¾KçcÁÜ÷OÍæáÃ-¹HÂìêäÅžLSlɆføÆqåòWÏÿ¾5$‘k_XaU^æŽÓP±#;ŸÖ ÍÍíÜQØ4u¯Ù^©Äý]õD²Í=-ºjö0rÃ@»Ô”H¹|ÍÑ*<„‘Õ ˜t>”{/ÿçžI{JH¤ËlÖ¸,È”åzh5!ŒôªÄKÎ\¹ûæuôõcä£zÀ'”Û°¦„9uMY£x¦íÚÀH¹ý}L¸Õ3X$‡L³ jPg®£ŸŽ~ä~æÆ)Ñn‘+#/ý¦D½Ÿ$òA±ߘÉ$£ ØRqÜ„ÖMöŠä¦¸ùöážkE sEFnpIEÕ¿¸½¬x÷¼wÐ2=&«B¸rù·7¶‚]®ÃÈÕ,(ôW?îÚN”íË]¤@Ÿii¹°ˆ®6‰dKÊz†q·êÕØâ²+JÃ8뿔˷ç™%oïaä½ü÷žÄÚþÜW³•0®²H†´OBüûqܧPâmX)îÐ>z¼î•»_º‚‰û0Òe¬ÅìãžÍ¯½ÿ^K&íRaßÓ^\¹ýZ Ýûtñ"ÿ¾fA÷S-éè§£Ÿ¹ŸÎá œÙ¡’ÈÙÃÖ¯·l5¶·õIïîjô«±ŠûaŠeå¾9ê¼§7ý%‘?<ôø\$„‘㿨PsÝn«1 L¸ÙC"ý×k°pç6‘”ËßáD6¾ý“‘»z¥âÖÀmÜæý(Ö³¬@~ T¢ì€Z"ù¼F*œmÜÏ Æ=kǸDºÍ$2æo:õÛÃH¹|ïë'a¹q #ÕL‰&‹qc#4Ș ’Ïgé±rG×pÈŒœ¿GsïÌ’ÐpU‰”»/©µ¨¸;ˆ‘Ÿ´0 ÙÉý”ÿïÙ7·v ¯…ªðfK‘”ÛÏʦÀÇÄÈ9…u¨=b/×ÑOG? r?›‰ ̽ÁBföW`ÇÏJY½ˆkGíÉ¥“á?0Œ;6" =Z-gä­´d¨‡r+ŒP Ý*‰  ÄÙeEr—ÍŒˆ/¾Üós¬øø{9FÊåïm‚÷»½Œ¼s'{6„r7tT#sÿJ‘ùH‡¾%p+-à?²±@{nExÃ+éõJ…2å†3Òý¦­3¯ ¤\¾r¹ ̸zB"»öVüÏÿwšû|”£ýóûøÿ|Ô$õGp§JEórÛyi‡?ŸüK¹û™£4øØg+#‹Ij¸ømà²õzHY!"P9 1ßÖsåö[ªªP§‘#æ÷|ýÃHG?ý,Hý´ÿº À­‹ai?ß霂æ ‘´Ÿ7Ü«_ÍFÚÏ·oÐàDð6‘´ŸËÿÜŸÿ¹OÚÏåògU1 L^(#íçîÑæó~‘´Ÿéf†Gÿ錴Ÿ?©iÈï É_Íwm ßæÖHûù¨@3N¨Æ‰¤ý|hþÏ=“zõ”È_½¯¬¯†ñý2FÚÏ+»J¨TB/¿º¿j32à)’¿ú¼£ŸŽ~þ'÷s2þï¯ÿÿqT‰A]ª0²}%JÜy ‘Ñ›¨«éE¶o¤Äí÷W²õÚ\þ#@$Ëø[Ðc^KîöH ®«Üyå§ Åºîä>ùÂ=µ½¸õgi¡pßΕ˿&Ë‚¸;õùTmƧF¸å稰¤U{nf¦ø¾Y"³/h0ºÜ‘¬1*5b‚¹»iá2e;7`ŠmûŒàÊå3)µð0î`äÙ5]\Á=úD‰÷k‹äãIx4™û¥— î]çüj@îÙ`®Üý‡Nz\8º‡‘}éÐÅe7w£¯~^ÿò¯¢*L˫ϕÛïÚA¬F+¹ð¥¡}º3RîyG?ýüoîçîs ´/@"{+0÷gîÔ$м£Hy£F¥ÀµÜj^0*ЋL«iÅÆ¢å¹ ¹ufrkOQaû¶Üù;ø˜rÞ‹¼øC‡\Û‘”Ë_|TºýÉÈ#—R°ÿÔî>ƒÓù{;õµ¿õPá\†»HFÖµbKmgn1?3<õa¤\þ+-M8&1²ÙÂd$ïæ.©¯Á“&ëEòÎ =¼ÜÇ ô–Púà7OrC+ޏ—dä'“›<+r?Waª²µHÊåËz ÀÃ]‰é¼%ߣ³­¤òUïÉ·Ñ©8Ww=wKÔi»©ºŸ„­¦qåîשÁ®¹Ì¦ÆŸKWrÝ\’qçX°Hw0ãÑŠ1\¹ý?ö+ÁþÆÈ{q ¼ÝE ýtô³ ÷3ì‘iõ”yv˜Óªàê¥à¶Û6‘<×ÂŒÊçÇr5¡jì]¶Œ‘N•èÒí­D~øªEàâ]"™qÖK﩯Áù;¹;EÛU ¤\þÙ:=&…ìc䟟µX:}·U¶ ¶ïÉnf”ì:Ž«i‘׆ùùÿŸÛ®$ãEñ`®ë9#†ïÛ#’êbfì_/rùÊh^8F U>Jt|ü7·ÿ-3œööɦó­hfû[ oW`üî /rbm ~~òI¹ûS›ªñO¹ŒLHBcë8nßÖ~ºõ"'n´`þŽ–Œ”Ûßcvþ¸ï+’5Îèásq×ÑOG? r??¾TBÑ·ŽHöÌŠoÜ‘[²¬FߊÜàÊFß'‡Âóÿ¹?hÆ 5«Û°³Hú­· Ø½–ÜFùwÝj!—3`)‚ErÙ>+ÂÞH¹üÒh ޵ÞÈÈ3%áƒy×w±Ngßd¿üçšµkl!w÷Ð"¦G#sƒUȼ!pK®OÃÀ=󹑓5p^¶‘+—ïìÂüŽõTÞÉHûù“üŒ?# ¤ý\.ÿ—/ L ²H¤ý|ÇA¶ d¤ýüȲ$HcF‰¤ýÜo»#– É_Í7µ\2²N‹¤ýÜíV úoa¤ý|ëz%&T.ÎÈ_½ÿi•uµEi?Þ¢w’¿HþêþG5“1ùH0#õyG?ýüOîgÇïVü;翽xÖš‘Qß•XÝ¥7¯ºYÂ$2ù¸ÖU^dÐ=êµÜ-’6§`ÜšMÜÏ߬Øv)Гl;Ð×ÅnŒŒ ¶`Qïš"©Ê4#ú;)—_=ÀŠBÊ›ééo^W‡‘l¦ó¯á6TÁ·sKî¾ü÷ .ÃÝErry-¦÷ðçfä)0sçM/òýC-41ÛDR._Ñ =Fîfäp½µmå”W¢Ù“ ÙR…GãjŠdÉfJô‹SHä;½ Úˆ¤ÜýÖ©zø®ÚÍHï’z'îâ®Þ›‚І›¸šép¼Üv®Üþ */«*’_ßXQöÙC+)÷¼£ŸŽ~þ7÷S¹H‰Ò/nHäÛrJ„úqÃf*aéyY K?UA¹NÉc-˜áÚˆ›þÅ ¼áEþ“mB“–ŒÜ\SSο¼á¦Å¸ÏþÜŠ×ÐGWH¹üîKÌXÑ›‘ÎÍÓÀZÏåŠù?7Üî¾B"|Ràó<ô_–Œ²“‚D²Ÿ5QñK¹Í–šñØ»ëà(Òíü¸Ûâî¶Á :ݼq Ü-A!„@â„àî.Ó£ïé]—…ÅÅ]×ßܺßólÕ|êþºøïÞÔÌ?¯ª9ô9'3ïÉôSl±[tæëçF«m—Rm¿§ôx~>œ‘“ëëðfs{îÐìz,Ê=\$‡±ï·$îÎN}c—02Õ¨ÇÀ擸«çK(·«¶H¾¯f®O©Üêå](ݦ#7ô3 LêL®ÚþYౄ‘‹ÒÍ0„/æNzg†æÕb‘ ØkCl@*·õß2?ÏÁÈßÞ;0M箵Iøm¼¿H>ôäâ¤eWm¿ÕGwا‡›|ôr7¢ÅÁ93Í65¢E2³Ý…)'ZqOÞÒ¡DæÁŒ¼zn7.ox­!Õæ¹©GlûFÞ讇2%˜›»ž '[õÉý»±ãôa ©Öï³Ý8rº·@žsèÀ‚û‰¤/Ÿ¾|fä|ŽZ¦ÅÛ¬/²w Cj•Éä.TüÑ»ô¢ ÿåù¹J‚Ë…¬s{7ª6¬#ÃOÛ€wóD2¾¯ŒC}kpgÑbÁ_ñ9µS:†_"’jûßËiÂa©Œü#Ê€wwb¹ß={ÏÝO$SE7ö„=ȉû,¸˜} #ö6âF©$îð?ÜØ[h¤@îhà9w­ e¤Ú~Û=¯ëîš/ò¡$áÄ•"¹¦©WÚ>È%/Ý8yò†,1Á€ Ïf‰äƒßí8`‰çªÍŸwD‡ÊÝ0²zg&>ëÄ k,£ÐãÚÜÜç¬ø£ßB®Z¿/fLþ´X$?¬s`ß×é\_>}ùÌÈùlzKB뜑t;u9·7é¼×ÐO ý¸ñõI…Œy#áÔ=Q$—1¡ðáTîaÏ9ãÜ&E&K•—Qõd}Fjƺ™í”@¶àÄòAc©¶ÿЂzä.6‚‘õ¯yÎa!͹ù=®QÚ$™[ÕÉ%ß)äÖžÏatcF~²µÆ(䬡ˆ¹f1Òuf7†L8«!Õö‹¯®Ç©£DòÒkbFÄs'ŸpcBë‰ ¹ã´ 6  #O\ö|ÏÎî"g¹ \ïÊHµù“ÎK¨5¦#óO°òTuîJÏçøûXîûîZ4XwH!ÕúwêçÆƒ¿/ ävÉú—W+¤/Ÿ¾|f¤|z?žèõhRl’Hz×çvãhPfFz×?–¶ai»"é]3Õ†'îùŒô®wY¬ÇÑ´PFz×Õö/ý@‹®­ò1Ò»Þõª%Ÿf¤w}O -òTw ¤w=誫ƒç‰äÏî÷ôoÒG-Iïzý_\ðÿ½/#½ëíƒÍÜ·ˆ‘?;C 3Úe¤w}ëx-Nt½(?Û¿j”GË3òg¯÷åÓ—Ïÿæ|n9ðïçÿ“}ctH¸ÓŽ‘z‡„Üëjpë5•йcAnïZtë0^!/<Óc„>D$‹,HÇÑg ¸Ã˘â_€{ÔæFZ—×nR³'u2/É+n,œlHµýóʱ(ܱ÷­˜Ñ#•«6•ç¾bà–TF.ëfÂ_“¹S ‘W˜Ã]îþ×ÿwò¶Bªõ¯“âFòíY ù÷:'Òß`¤/Ÿ¾|fä|¶´èrj¶B®Ë£E¸á¬›\?Y‡¶ÑmE²„Í€ÅÛ¢¹×÷¸¡½ç&T•ÑkhFZ1N3—ë¨ê¹oh•—»k­›ÇÈ-)dÙ¶@äªìŸxÄ>×By«§!ÇqG Ñbæ¢My«¼§Ê4Éšè0’[û«Œ› d\3oÍ™8ÞŒ¿¤qÕöëZJ‡Ñ›2rÜ;-žÕÊÄ­ÿ݈—]“D²–1 ¿.àž/áÆˆ—²Á(Ï}ÒÞŒT›ß[gÄ“± Œ<˜ßˆùÕçp¯‡yÎEKŒùGorŠIµþc,(wl#'~ÔcÐöP®/Ÿ¾|fä|¾Î¢Å•­4dpU->eí$¡Af4È‘&’ÇZX±ªÿ?ÖbÇÛqŒ\bƇiÜÓ…´xQ-‡@V©CdHk‘\3Ïgû¢òeGÚê§1RmÿŠî§2rv1 ª}]À=SÀˆ%uçˆd#m:ŒŸp *nÌ7êÝä©þ2–)ÅÈò=µH7.WÈSõõ¨‘sHªíØR‹aB‚B>ñœƒÌçß9É/£l¸Û"E$K^s x³®0Ô‚õg02×+ 7~ÔçªÍ?iÀ·ÀiŒlO’Oà^nE×|‘|pÞiS}®ZÿÝñZä~ºG!ÇÓ¢z¬V }ùôå3#çs¸ç÷zÇFL¼¥Å­fï¸ýÞØ¡‰™)’ÏúôëÀ.«Ç’;ýù9D‹ý½õ y¶#&ÏÉ9»í0ÆÏá8®Ãn{wFîz¦Å–_RmËD3F–Mcä_çŒØß/‘;´›ÏòÄ‹äÃ.ä_؆›»¦-MgäèWéx“m!wGº˜#’až¹³òN–Iµý´Ð"ÛÐ8|ûZ ûæï\ÇUBmõDrr7^V9"}j1òùzn…—Fþ”(’jósÒ#§0Œ‘w.é0=¹Ÿ÷ºqiå`7Ù¨ íÛ3Rõü•®Çþ„`‘SÀ‚ßp}ùôå3#çóD¤ÇÃ2"9£®ÅFh¸£nÊè°åƒ@:n”ê\˜{­½•o§rÛuh\·«HfÕÉHꘋ;ÔsaK…4÷¶¢Xå4‘\fpãóºâ©¶h Î3Œt~Õ!ohn¶vn¶u duÙ¼•¹É’vl¶hFvˆ‘÷º,·BŒ)gær[eÕãuÕ¾\µýաׂ&"™}…»Žæ^÷䂵¡!×Dº1T^¢Å8P¶ì8‘Üàé»p~3Rm~ê0’~iÍHÇ ¦ãõ¹µ7Y°8i7çz=Î.ÃUë_q ý÷ Énl=¿L }ùôå3#åÓûÑ$‡¦÷Iïzâ*7 º*¤wýàÊLIïú¡î.¹×–‘Þõ¿–ÚQ§ê\Fz×Õöß{UÂèuué]°MƲ{¹é]ÿ’[‹—gŽ»IïúÊZ¼*³M v¿JÃè'’ÞõeCdô¾R’‘ÞõnùXy9˜‘?;?ç< ›æV`¤w}ac-òUPȟퟫ‚rü©?{½/Ÿ¾|þ7ç3(õßÏÿ'Ù,-Z™§w:h1Ñü¥sŸ4É–AüZr"w}Šû4ÐQA2êr3²b]Iñ\íY M¢+rY/-j½¬!ÅŒm扤Úþóï;PíñHF÷|_Ì5“;h¼#ZÈ$Ä„WÉá©t_5ž»iºŒ2ã²sç]–Q:ñŠBNÉeÁÔ´TFªíw5V‡¶#µÝ%äˆÉÁ-¾Å“ÛÉsDòÒq´a*÷·¶2n+ÄM:+£ÁŠ ©6ßïª ÿœËÈÖ0bMb ÷“çswé` @mÔ£pÕ!"W¥÷nV,0§02ñ²ÄH®Úõ¾|úòù¿œÏ Í´(ÿÊÞœôÔâÍŽÚöÝ„EHÉâÇ-¸yj7æ½£ÇOedýNéèÛ"•›âékNØÑœ¼E‡A'kŠä„2ùïä×h'®2Rmÿ±¯¬8¶:‰‘ÆÞ,ÊõÛn@c¸Hf­˜ŽOyR¹c<}nÌÚŒ\4SÆÂ¹Ù™=„ËAORÛK‡€GDRm¿Nó´X³l¹B®j®Åû¼Ýä„gV Ø$’©cHîÊݲҊ7—“ùí¾%B;qÕæïÛe@«áŒL,a@àË`îü8 ²5JÉÐb.äÕš«úóå’p%ê‰B–ŸàÉy® é˧/Ÿ9ŸÕWj¡5mHM& ¯K?àŽ›eG¶§1"Yñ'ßëÌÝ}TbÅF0Ò|I‹wSO+¤f– Kë%Šd…‡6ä¾6—»´œçuš4–‘±k´h3y«@ªíÿÜlFý6ó™©´ ßÏ-·×†2GãD²ßU'¦NÜÎKˆÍ=ž‘Çg[иi*÷ê`žüž ’±Qn|/$jû}­Åò¾ƒ²t B×Ç܆.T^ÚH$?_—qæþE|è¹ïYS“^ÀˆÑºé"©6üB=j®ÄÈ&ùõ(ñ½+÷[²Sçu×±]¨V¸.#ÕúO‚¥'öÉÎÃÒ‘«O*×—O_>3r>£I¨ZT$۶֡Ü" 2 ÊÂ5…¸±ñVSì1X‹;Úqfè‘¢IÿÊ2„–ànOrãÇë9nró×t,JÉÉÍ<Ï÷X!jû[oÀØJáŒÜÐÞó>4ä.{(Ã2óÜï9Ç…ös“;±*ßLF{-aâÍj܆!¸s…r·f2àJ– ®Ú~yJ蘻¶HîKÖcè›Ü"qnT¾»OC®îíFϪQ 9¹ ÏœSDòÙ,ÏçâÜ ©6ß°\‡ ÁȹÕtxP—»¾‡c¤nùNÔ¹:«š«4Á"Y¼°îÒ6ôåÓ—ÏŒœÏî…õ¸¿¤›Hîÿ®‡re wo˜©óê*ä‹4Y ™©O3aId¢HïgCÿ; Ü).øjÀÈÍžsÌãUIÜÇÓQ1„Û¦¼»sÕöVI‡ë{ê0R/HH,›…°Eƹ  ©äÄðSý)t×Â^´¬B.ù]‹ÙÇ äýAZœmËxÜ„¼ÑI"©¶ß“¶F”¼#’…üÍøýe2·L Œnëó3Ò½ËO÷à†-vbIÉ^ÜIGL–ÄU›ß¿„Qåù{„Ì‹òrÿ\¢…1ßz…\ŸWBK<Hµþ×J»1z•A!›wÀÙu #}ùôå3#çYŒ¸ü8J$3•4¡z£xîÕN´h׉‘KÚÑåY47x¸ ;Æ7É¡nT«ÔH ŸÎ3b¹i6#Ÿ…KxZ©÷]~ cž ä”V\º’"’jûç_§ÅUÛ6…<Ü^‹OQïÜäÒ6t|šÈÈ•Ǫ́nKážo®Ç´½DòK!3"g'sc,.¼zîÇž*ƒµÉÌHÕ|6·`KdªHþ%Ø2(‘[n€_2Å2Ò¸×éö©Å¹‚[òÎ W§•IûL-’ææSÈ)•õ¸ð ƒHªÍ?c6ãB±$FŽö|Þ·IàþÕ݆Ålî?4"[Ãi\µþ–È(~ä/Ì\ÃwAA ©v½/Ÿ¾|þ/çsâÏ}÷åƒ ùj»¡e¢¹Ž_%|H»)wµ:¬8ßT$÷jdœœƒ»©Ÿ Y%7Yí¨í>Ä3rQªïOçÖº£G«ÔAܰûZL~aHÕýÏ;Ñfg+FÖ^ë€ÿ±ÜÒñZ4)]E!‹ÈZTÛ¶X '¶¢UÑ$‘\ðÌ¥q¹©™dŒR˜{ð‡ šøÂŒTÛoÖC=N”ÌÈrSuèÖ±>÷Õ=¤ÍýErE~JtŸÍ-½ÕŠ[³¸GvʈÞ~Q Õæo›lB~ã\FF]0âwûLn|u E]WÈï'´ØÍ6 ¤Zÿ6ãˆÍёŧXжb2×—O_>3r>OMÕbÕߺÉ{!ZLè[QCvO¸'ßÝ8ò<ÑMÖÑ›!æMbäçZÜ _­jû5$ážíƒBVÐjÑ}Ô\î— é(04Y$odÃÙ&qÜeedèßåcd®i|*“ÌU›ÿŒ°¬ŸÊÈ ƒûʆpGE°²üX‘¬Ô׊I!‰ÿ¨ÒßÐÊ€ 'G1rûS-ºvr)¤/Ÿ¾|fä|ŽHÕââøæi9£E˜i·Eq+’ò&‰d7£]²N႙ѭ^#7DëaJëÉ}³Lÿ³D²ge3š$$p§ÂJÅÆ1²¸¿ONuâªíÿ ‚]†'3’¥šqUNä.XšŽ{ý’E²vA;Zçˆá, £é¶‚Œü‘×ο…pÏ÷ÓcÉÁ®"Y4ØŽ£ý§qÕö›>A‹?Žf’Ii‹ËËDä×¥T=R$sÍváዚÜ;½u¨Ó³#¹´83s±@ªÍoPÙ€›mG0òï z|¬Ô‡{r† o÷ý*’…<÷eg›µp“jý³×¢fÕMÙ÷±ÚNƒEÒ—O_>3r>Wi$L²Ü1 3Y‘|lj÷àæ‹’‘yÂ+,pU‹Ûu 9e‡ãëÄ䘡v”¿8M$÷¶”Ñ«MvîCÏÜçW ä¦ÊV½HIµý×d6áÍ¡XF,5 kÕñÜ1ßœè!ˆäßWddùÕ!rZ0v\2#×|2`vÉÜÍéóÉí ÷±Ã?d#Õö³HK|%ý‡ëP¡Q‘¬y\Æýkûòq7þÒ„jÈÎYL¸}2V$Ûº(ÑdWmþ¤Jz\=Ú‘ :”êÚŒû×W'%Ük,èð*‰«Ö¿Z. ŽKÉÞ%œœÜ—ë˧/Ÿ9Ÿ†é:T}]_$‹uÐãØó.ÜCmÝXS¿¨@~kèÆ´=r×3^DµÉý?Lxš'[¹³;g}w“Ù‹¸pe3F&·v£Lñ²Ùiºí»D2RmGg=ªõ `䦎¨ÎêåÆÌÓZ7™»‡Œ³™yô†„3[Jp×yÎmõ>Ÿu“r_=ÞnëÊÈ1µX|bµ@ªí·â”Ÿå"™gDÀ´éÜ҅ݲ.E!‹å’qâN!FöôiÚôG3²Û9š=ÌHµùCÛèð~MF–¼ó¯ÿn®$wÔU=¶<È=¾N‹×_‚R­Žntš–&U£&WH_>}ùÌHùô~¬©cÀ¸æ#EÒ»>¥ƒŒ#¿ee¤w½ôvgŸ#’ÞõêÓ­(–=‘‘ÞõÍËuõ¸#½ëjû—(Áá÷A!½ë780ÃÒŸ‘Þõ¸bžûš/çÒ»¾É`ÆâI"ù³û 6c}ÿD‘ô®Wì鄟¥#½ë#*š»ÐFþìü’©ê\ÏÁHïz© ³R³ŠäÏöÏÒÊ¿cù³×ûòéËçs>;Öþ÷óÿÉÊñFŒŒ›ÂÈ\)Ôú0’»ô¾KöÇsW 6bp«ÉÜRÅu¸Õ $÷Û-Fĸɹuô€¹•HÊÇ͈M瞸cG°qŒ\S]ò7[rÕö¿}JƱ¸õ©-ïFáB¯4d¯.Ü»–‹‘ë·9ð=p wtºM~LåŠ:Ì\Sƒ;éžEBE²ß7ç¾ UHµý&sÂ}¶=#^²C[c<׺Lþ9»rë>àߦ ·ÒF2»J‹äÙ=2Þ}Ü­jóó•²ârËxF¦G[Ø?;ò͞Njd»·NÌÔ7âªõ7yÎÅ»v' d.³-žuIµë}ùôåó9Ÿç7ëqd7FjÏê°·bcnO®lmÅ]±T±wŸ²¿Ç—? d¼Ý€ÅÂDò÷³vìKÏÝ=PF™˜Y;\Beå 7Ou+úTŒIµýõÕÝá¿ÖMn¾/£ñ±d…LšaE­&qŒÔä6ãòйÜ#L‹àŽrà{-†ÝÜ !û­°¶ËÈ׃u¸ú® Wm¿Cm0eÃÈ™~¼X—Àý׿£>µyªB¶Z­Å¥M5䈾6<ÅÈ"wõ~7«6úµtd1'0²£˜Ž±ÿ1O-7ê~.¤!;º°êkuFªõÿº4¹×$ˆä½å\7ˆë˧/Ÿ9Ÿšò:8¶–b$›'A<ñ^!/ÙµX2¶ ·¡A‹¬ëËd`1 žïHIk¯Ó†q?æqãTWA!ûoub{©–Œ¬\Ôó=¼­‚@6J¶cU¯0Fª¾ÿCehúÝSÈcË\h6·,#Ë>Ñ#<­wÍ WÎdæ¶.¬‡vl ‘̺؄;§b¹EfH¸žþB ?”µÀ¾5A$ÕöëÒÚ {¹8Fö 3ý¹9JHhÓ!™{êð-Oe‘üm¥S]ÍÉWžslÈ–A"©6?ûF3ŠUgäòÌfXZÏå>´"zg·ü&´ÎÑ\µþ7Ý2 ÕuÙô°Œ“+¶*¤/Ÿ¾|fä|^..a÷C»B®;¢EçäÑÜdÏûrñé ,ŸCq‘¬2\Æ… wòR7Ò£î»É¼y¬Ø0*ž‘?2QiÝnìMvŸoÂMÜ,aŽ>“Hªí¿s¡Ÿ'·fä°þT· ç&lÒâ|–ÃnòU6 *iòÍZ++’‡E'j/é­:SFÝŸÙ¬´ “Ãê1Rm¿ÅÇõ¸›Ú‹‘¡¡:¥Uç~¨f@ö×E2çD–ÌšÅ}¨·¡ãåÜNWdŒü{©@ªÍ¿4Ö„çg1RwÖˆnÛ¦qŸüÐbñÀ YY”ð^:%jý#,ÔÊ6€‘…œéÕ&p}ùôå3#çsÒ-Š1ºÉâk´Øúh†ÜãgDZ—‰"yÐdÆòîñ\m˜ =t•Y𤠳&L羞,aÔ†' 9Ö¦Åà 25ÕŠÝ?æŠdÁJnúzECªíoúhä–Ó™kŵjqÜÐC:déï/’Ù–q£}·Åj]R)çÆÍÒÝäú…&œÜËÈd“£:WQHµý6K(Üý¾BþêùÜoŠÅÝÿ"SM "Ùt’ ùgÌ亂]xóª #KÌKÇŸK¸jó·4¢Ìš0F޶ð¼I·[#Fg É_­È?`W­ÿ°“z¸½ùË=-_¨¾|úò™‘ó¹Í­Å“]mrVf 5æïäôÜ7?Ü'’õÏÙ1;v<÷G€Ñ-ãY¾;Ûr³ÑcØì"°ÓŒÆÍâ¹Sýìø±+‚‘oÖèðbX®êçë}:"Œ ŒŒ˜hF…mqܘ—éáyßɹv\;Îepá~æ’Œ¬ºÉŽQçC¹M[p¤Ô‘ô¯êÀo»GsÕö[è9×”[МwI‹‚3âr£ç{±{¦"y#Î…iC*pÝo=ïCÂŒ,VIÂÛß™@ªÍ¿[Û€®©ƒYÛ Çš­Ý¹%Ý.ÔpÉåÝÈ_ó­›Të]ÕsnüºW +L5àÍø"é˧/Ÿ)ŸÞ¡štz$ÞõnOœ@æ&"é]} ÅݨŠé]Gº™ô!"é]÷;/A4åIïºÚþ¯¸Rk:#½ëõ~qaª­¾HzׯéÓqtG#½ë£=÷s_t“?»_«$ û*¾HïúZ›Œß&ü? öšPµÑ‘û“óß5Ò#Ó’ÖŒô®ç‹vâËŸmù³ý/Ä[0®k‚Hþìõ¾|úòùßœÏÂYÿýü²’Ÿ{²ôaäû^z¶à¾èj@PžþÜ*tXq±·Ú+-Î.×òèѬVc‘ŒdÅ'³¹á‡èÙÒŸ›ë†)'ê*dt##rU'’jûoËâFÀ6msrðù:×VÈ…íð‹ÄÈ€=é¸T"Ž›'áóÛk ùõ qÂ5dñíN´Ô6e¤¦” §“§rÕöüȆv áŒìQÜŠCº9Üžscp·s ¹õ„ç>ËzÛM^ItâÑ‘ƒÍpÜÍU›ŸõW råŽcd«´täÌüÃ&Èè°ë7L^"£cŠV!Õú¯-fBäЩ"ÑƇgpÕ®÷åÓ—Ïÿå|Ž_¦Ãæ‘Uùøµ„ºrrûÕ—àŸG«K÷iñuuU™L¿kB±3ErwfTœÎõ-cI‰\¹&‡ŒA?*d%Ù‰˜/DÒ–Å“«&ŸRmÿq2êÜ·*d—k.d¶fgd›p#ò]á®Ü£ƒe‡·Ï; ÝWçÉZpkÀHn¾#ZTŽMw“Í.|u¤Hªí·Å?ûwÏed×A&¼íŽðH‹CÓº äÕß%ô}ù•Û*A•Û×òì1 iöï©6ÿãK3zˆsY°¯_£fs_¼°£Ëê‘ÜbÝÌøÜ~6W­ÿªñ. þRZ$ xžÏS8ÎEúòéËgFÎçÏ÷J•×g²ä/Öv_Ì\EB‰Ž›rN/.:K‰d`OnýÊmðXF£ Urð;v¿žÀÈc£ÍXyv6÷m+3>~ˆån9¦Ezø)7©¶ÿÑwN„ž®ÍHÿ«|Ü›«´„ÕV)¤æŒ[výСÛÓ±²@œH¶J¶ãÛâqÜl+¸Ù£wÇm‡âD…TÝo›ò c¤±†¿ŸiÆ}ÒMÜa-Dò×!F$®˜ÀþÍŒÍ}ærc^:1Ä\‡«6?ˆ™Ÿ-†‘—ª›°»j$wÄÎשÀ-ñ›µc"5¤ZÿkÃ](¾¾,ãV·ÁÿÆ ®/Ÿ¾|fä|ž¿ E¯ê%r‹ç{{Bõ2Y³–5J÷É÷³M˜x~:·û(ÒŽ*äó ØYî“;®6äùC‹a¯³+äd—¹NÉe¥]ð›Yƒ«¶ÿØ¥v$\ËÈßµ!ëÁÜJO$tÍ!’šPgt_nŸTNt+Æ}ùVÆžzo4döÇVdÿÃÈÐU:¾WåªíñYÂÂ͹Ùµ¶„M…w*d…}f8/ÍÉÁ-¬xØñGŸ•Q)ÿ(…¬VÈŽjÓ&1Rm~ËF¼½=‘‘ṌxÝ6ˆ+Ñ#÷&"Ù©U: Ës¹ªç¿&˜wG0òd.nNËÇõåÓ—ÏŒœÏ§µèÿù«†œUÂÇsñù¥°½‹Ä‰ä³Í6Üy4•›2É‚·§ç2RW܈*M‚¹ÙöJÈ2ò“@– 6¢V‘æra†’‘iV¢Ñ\µý/è-h8k.#ÿ¬™ŽókþÑÐß ¿”Ù"¹W¶"`Ò,®ÿd³íWÈgeœ8Ò¥##û-”Чî_y4“™Jʼn¤Ú~Gµ¸óÇ>7ùèO-vÍúE o®µÃØk¬H²N,êÓŒ›¶Ã€ zÃYúw-öäZã&Õæ7›k@ÅAƒ90“ôäþÊ(°§Hêþ”ñþúTëöUWhÈ‘ŠMûø‰¤/Ÿ¾|fä|"áCÎÃ9á’„vý²ˆdö‡¤h»r/j]X—P»è–}ÑlŒ|á¹/¸<ÔÔœÌ6ÚŠ®Ù"9ö¤Ç"rì×"¤îk™ÔÊ„ÖÓDRmÿæ:ΆD3²sk#Þ¿ÇÍ|ܸÈ"iÊ$£N¥/ù&É ×ùXF¾M3¡~ï\¥»Œ~wÿÈÐ,.ü–¥#Õö é.Á”¼G Û7ÐaO¢"YRÆ¢¯2ùžŒW;p·éõxð´£HN}aÅ»Ó1\µù‹’ôˆ¼Ö–‘©Eõ°k½àyJú¢É:ÞNœÊHµþ“ªš‘·i¬H.‚Ýó{+”ë˧/Ÿ)ŸÞÿ²ô¬ ’ÞõŠŒ]““Ò»¾#¿®±ùEÒ»ÞëŒì'² ¤w½ÅT×*%’ÞuµýW~Ð#uXFz×¥/2†O1iHïúÙJz”*ÖŒ‘Þu¶Þ„/f0òg÷k5Y‡#[‹¤wýàKú)ùÒ»^e†Œï]Üù³ó_K:Ôz^ƒ‘Þõžï8t #¶ÿåÌ2ú¬ý,?{½/Ÿ¾|þ7çÓyKÆÿŸºVØ~ÅÈÙÓ‘åølîú =‹ò{mªm-ÇÈ&šêCòÂN'¢¿ÖgäÉÓVŒh={û¥/.Nä–ùÕˆ a£¸Á?´è5ÆO Õöï¼ÓŽ"ËGŠdÚmh;r§Ùm°µšÄ½4à Yß„»à‚ŒDó Ù³£Œ^ì€BÎèB¡ÖŹ.ÒŒ—J Wm¿M§e\ü”U §yæ^wÒM–ëë€>¢#'Tµ¡ÒÀHn‡ãfœš0‹[઄ÀC+¤Úü“íø;#Ü·aÎPnù#&Œ>5»¨Íäªõßxʼn&åüÙu¾3ü£¹j×ûòéËçÿr>Ÿ>6£Ì‹YŒ4Ü2aÀÆ(nö>øÍïÃÝŸÛ†íݧq»¿²¢S(nÐfâÃ"¹~Õa£;·Â}-J]o­!›Íèÿn¦H&e—Qðæ}TÛ?þ¥ߪ‹d–ã.´“2qgv•7¯@joÊhÕMýîÀšeí9-ÁŠí#£¹¨#¡™#E!Ǭ×#dx+‘TÛ/%Ü“Ëu;²ènÞ˜s2RÚ›Ž‡fs‹ 1!öànÈN [Û_È„f”/#’jóoö³!Op#ç²bÐÒé܈—Z,x÷—†t…è1é†F$Õú¯[eDÛZã¹ñ‰„rm¾(¤/Ÿ¾|fä|N¾iD¾¡Œt=1 oÍaÜø”t¬]<›[º ­ƒ&q‡Ô¡A›Úܹ$¼9ÓV!·­Õabxy‘ìºË„oÆHn'^ŒlÌHcK#JÝÍUÛû8Iu™û°Œ¿—WâÚJȶæºBÖ-åDÁ£­Ùj· 眑Ü'=õx¢oÊ}œÙŽ‘BDrò C ´ÖjûiãX0­1#×q k wm#JÅ÷àî‹Ö¡cëRÜ]PŠˆ"™ëO–™wݤÚü¨zVT>“‘+V[ðép,wMr:ÍÉ¥§hÞ™«Öõk-:}§!ä×cwáz"é˧/Ÿ9ŸÇsp:"€‘ëE=ºÔmÌrËó{~\Gn`]¶/ÏË)áTÃtÌÿXsC;‰d\Š —º…sCC](öª0·÷-Î\.'†UfôŽš)’jûçñ8øÓY™¥Ìbd/ÁŒÂ=¢¹ÍwÚýÃ$î²2&h^ü£ZëkÒ¢^ ä©c2œýŠ)¤/Ÿ¾|fä|–)¡s‡ƒ ™«º„î¹ã¸k'Iè:Ã-ŠC‡m'*‹äåi.4,ô 7ôºŒ–yÆiÈ‹Á6DïÂÈF‰&ô7•Ôψ¢½‚¸ ?hQ¤ã/©¶ÿûÝN °×gä›4þ^ÓƒÛþ ¿ î¼¤´ŒžhÁ F³E2ª´Aõr/ßv¢\É_¹ë ãÉÇà ©¶ßÖ`p'‘e²ê1ÂU‡Ûí•·¶É»h¶<”[z‰®ÇrÝ6æWÊÎU›¿Ïsõ³F22°„ Ç”IÜÏ÷%ÄÕÿ¤]<¯Ëˆ¨.©Ö?ÿb'ê=ögäºJV¼Ý9“ë˧/Ÿ)ŸÞÝo´-üÝMzן¬5 NÍ"é]õÒ…?¾RHïú¥Z?/ÍHïúÆX3ú½ŒIïºÚþQì85–‘ÞõÕtÐ("’ÞõÏÇ]8;5“Hz×ßv° ÿ¬ÙŒüÙý¼ >üªÞõ#õÓ¡¹+’Þõ¬dTL>¨?;ÿJªƒÃÇ1Ò»¾ú­÷çtÉŸí®‰æÞ£ù³×ûòéËçs>;îÿ÷óÿɃí\˜U©#'mp¢LBmî›éhv;F$w¶á·ã“¸§²I(Ò´‹†ÜÕW‡Ë÷óŠdØ#r©ÊýòÒ„3ŦrHNŒ•ý¹™QîY0Wmÿì 8+vÉsŒ˜7š{´ßL¹y±œçœS¦ºBnÏ-a]O½†œÓBW»œ"¹0]FZÕ¿Ýd¬U'2Rm¿-Ø–#’_rÛP­éN´ÿ Üâi.ܽ•3O~ïš1²S> _-G5¤Úü|¥]èÞ±4#‡*Nl™R“[ò‚ÉM+‰d™f„ß‹âªõOÌ*aΘrƒV‡´ÈÒ"©v½/Ÿ¾|þ/ç³È V½kÃÈw9øX¤wå FÍé*’¡M]RŒ[d®yçæ¶ÞbÁkw47¯Þ†Z³C¸»Æ¸Pob~®. ¯ÕúÛ&|^!’jû³K&ü!’ß[¦cü/1Ü-$d:.«7èmnI‘\þ·9ÛâÖ^ø¯×?†»2\7Õ›0òÑ C_&jû™Œv ˜7D$÷Vòäp½Àõ›)£ã­¹yp¯ŒÚ˺ÉÇÇ H©Ó_$·µáG™ \µù[ú8ÑÁј‘Áøö£5wþq;j-$’ÚÉxùÅ jýËiMȽ;\$fÛPoþD®/Ÿ¾|fä|²òvžÄÈa5m˜Y*œÛ~€Œœ6 dmÏu7wWoNœïÀéÇ]Dò·Ê2ªµþM szþÜRS¬LiåÂWEÙf‚ Ñ£ó‰äA?úÏ/ÁHµý,È·Às®ÔÉ÷ûd”^$“¾|úò™‘óY°¦RêtFÖ©lÁÙ½1Ü"A2æ^Z¡'z¸ð¶Á/Œ¥—!ˆùòM&v <#ý&Ûàöãγ˜pùG875Ê„…û&qe‘ðCo“Iµý­ãìhTu¤H¶Ûä@‰\¹‹"‡Læþ’Ý vÜ›Ûd,YYS Û“1ÃBšî¸Àª?à&ŸJG½1ŒTÛ¯ÔNïTÈ\LÆöqnrà<r`¤{ª C2‡q³œNG_ÏÒ–ß‹qÕæ_˜jÇŸÃG0òÞ –ÖÇK5cQ£éÜ¡7uø¾º2W­ÿк.(¯‹3²­çuž9|2×—O_>3r>ë—IÇû]ÑŒ\VÂŒ«]"¹~ë¸7¡37<܆Õ¸ kl8=$”{/ÔŒ]󢸮}: }[Ž»%‡Ýê7Ù°² ·ó†Šä©7NœW‘«¶ÿMƒºù‰äÚù.¬û˜»ä½ CÍ×rŒçܱ³o] Y4È ÓŸ‘¥kÙðîûdî¯g$L¼¡ÒFrKФÚ~!!2žî\¬ÿúw|þ?qûW³`†ÃÈ»gLh‘3‚›·¯„: ä¥r&¿"’jó÷±!«9Œ‘5.YQëU·ƒç÷@«_Æ»ÈLtxì_I$Õú[Ƙ÷s#µ±:LïT„ë˧/Ÿ9Ÿ‡Ê˜ðpo# Ö6âDaÜf¹,ÎÇp›¯3AW%œË´z¼Z+rÏ?~µB«¡Ã°sYD²²lD/%˜ûd­ ÞÊÂÈ”ff$6ÆUÛ¿V–©n,¿FÆCm{îAÆ!£U! $81ó|=Ffó|ކ'Dq÷d6 ÚÉöÜ,ý­˜:7J$¯L‘Q£{ª@ªí—MïDØB?Fæíã€îDîz­þº>Üywux8¸ w`)ní#’nYFpß­RmþõVüq;Š‘;l$ÍŒæúÍ4cÝÝ(‘ 9nÇÜ•ƒ¸jýÛzrÝùï˜&¤8[‡¬£Šˆ¤/Ÿ¾|f¤|z?R»°ï\WFz×p¢lFz×Ï7•Pæ·`ô®·˜l…ãî4‘ô®WûEBÓÈ·nÒ»®¶NÏó×çÇ6'½ë ·lÞŽc¤wý¹EÂv§Ò»^©‘ƤæŒüÙý¾°áó¤ Œô®WÜ)¡ß×à é]O*ëÄø"#v~@% \ûbé]ÚÉ8œÍ(?ÛK _³NÉŸ½Þ—O_>ÿ›óùi뿟ÿOÞ_íÂ\á£BVÈíƒì%›ËŒæEÂEò]˜ —Gq­›$¼ždRȉpxp]\3P‚»CwÛ="³ "yz–ŒšZ d¦Svd®Õ‘jûGÅë×Ì_$ׯ7 ´unü=FnÅHöM‚äwO!×M”pìÔ<îŸ$h¦¶È™²]ëWÉ®…d\l©(¤Ú~Ÿ^›:všHnH·àÔ¥,cǑ˃¹c8ñ°R}î„tÄ¿Žbä“ÖLÌÇU›_¥ îäcä çN´˜R†ÛGpmr5l}Y§E‘TëyEBGçY… n#á\õæ©úúøòéËçÿp>—s"ïÐzŒ¬²Þžr;îÁ#6´k,’o/;Ðq+¸O³éQgueîý)&ì±…p3HÇåSÓ¸Ç7Û±¶Ç@îý«zŒ-Þ‚‘϶KØtÕ"jû¯¾bÄ£YcD2{}3¶ S¸Ó«JØ8e¾›l³HÂøjÛòôežd.+’IG(²l4wÓl j„F1ÒÑÊsßÿ%Wm?)؆ l‚H>ùlÇpkoîLÙ…ê  äÇXMDn·D Y®¯çNcBÒ!"©6¿ÃB' ×ý•‘!5œÈ‘¥ ·çétlöŸ.’MO9ãJK®Zÿì©z,ÉÔH$ç7ãùóH®/Ÿ¾|fä|VúÝŽC{ú1²á ò” âΙäÂv}6‘l8FF°!J S[q ó‘Œôœ#6Å4âN¿ìB³qW²À|·•RÈžûörŦ‹d‰N2:F-HµýµËÓÑ4çt‘<ûÅ‚/¦HîÖÚz„V¯Éýè2âç(îþ þXÅÝôÒó:, ä¦tÕ¡àȬÜ"+-XR7Š«¶ß¹Nœ|[O$ÓÂ]xÙ.·÷*N^t“#3ÉØÆ)d‚èÀ·¡ÝD²ïfÏtÕjóûÊÄ_oÍÈõíèbèÊM#ÃYª™@ÞÙáÂÍÄ7 ©Ö¿u‚¯[ Éá;²ÅS+^ÅLbdâ¦tŠLçþ}F‚˜zR!ÒÁÔð¹@ªÍÏò» é‚ÙI´a߬PnšF‡Êã(ä…¦Zî--jý ”±¢jÉHF 6"ìêP®/Ÿ¾|fä|^{h©ܓ9øWŽq+?ô|ošÄ-—ްçQÜa‹L¸q%”k,©ÇšàjÜÆU%<Ð%kÈ¡—uzUF$ç~qAšwD gû9¿³1#Õöï¹Î…“u?ä¸2*Eïà^ôœ#"+5s“宺´øŠBvíg…æD#7í4¡ôÞ‰Ü ˆn#’ç*{ÎFrÕöÛâB§ÉÙùw/'ò¿¨ÏÝ>ЄOÛ&pÛw3 Ã°NÜç.3ž‡EŠä“'êépÕæš­Ð gäÝJžsìÛ©ÜKGô¨š[Ém.Ï9ó×é\µþg3ëð¡â…œî/á}ó|é˧/Ÿ)ŸÞ ¹è¸±?#½ë¿V7¡LïqŒô®?h+afÁ é]§˜àž&’ÞõƒmH^;˜‘ÞuµýK¤ÊPLeÒ»ôÒQmDFþŸŸoº®5`¤wýæÏ9p]6 ù³ûÍ~mÇõ‰Œü?×ïÖÁЭ(#½ëÓ–É8*p“?;Ï J QŒô®b´[ØZ$¶ÿÄÎzüTK$öz_>}ùüoÎçÃÔ?ÿŸÌ¾Ü‰r¯ªˆdîz.8Çæå®ÞaA®OŒLèmƺb“¹B=ún«.’·˜Ð-d7Û!–(ÂjÂô6ã¸Ó׺°®åS…ì~ÁŒÃ§0Rmÿƒ øx¡=#;ôÕãD­_¹ë¦ãpÝ©"Y±¡ ¿±ñÜ£!f¼rNæâ«‡B¸¥BmX×s,#óØôèw¬Wm¿‹•õHëZ™‘¹ütH©úL!ëëõxyÇŸ‘Úëp{øG…<ÕØÚ]»ˆäø$©;4¤Úü|se˜Ò%=Ï(r§9Y!BÂë$…,ùBÂÑy§R­¿¹«k@A…LÊ¡CÇ&×Ríz_>}ùü_ÎçY“ SšÜÈEeôx»‹+T5bUž~ŒlÛ]&-kr o°býå0‘ìZщO®†ÜU/¬ˆlÊ=ëvâðˆòÜ­ã%LÌ>D!µ øG€Hªíp¦»ædäêKÞ¼? ?.;p ]#’û].œ¸x] o–pâ‰Í_$ë–;R+ù#=÷-³#¸9 š1¬N˜Hª¾¾ ìRR#HjOt“õ'Ixwp¬BÎÖHX¦ Ô¹s8QecFÞ\gFòép®ÚüRód,iZÓMŠõœ ÜæVK0"¥ç‘̾Ίsøjý‡57¢Æìþ"y~~;#¸¾|úò™‘óyxŒÅ/ú d­8¯ïº4äç/]½ +=¿ÇSÞà.j&£íê4|ÖGÆè¹c²æ¿ä?¥!“¶¸PéûC…ü³—#®vÉžs@…(…TÛÿà ÷ï SÈŽž÷uNd€›lëù{9IC®$ãzúF…œ4KFÏèûnrX¢/ådä‹N”ÐWÉåÿúÜîÝ¥ªßï=%ô²—ȵ‡$,ïà z 7ÓvrÝnΡ°H¶ûU‡±'ž*¤ø§ççš°O Õæç/cèÄÆ é×EÆ©û3¸3Ÿ9ñ\).’Ižë4IkdR­¿ûšŠäò@OºŒH_>}ùÌÈù<çù½ß¨N7¹.\FÌ岓çumö¥´@º<÷½c{\ãʘRöWFnÍaCà˜náP;v† äNøjFZr÷²Ý‚…ÿñG~Æ<¾©jûê(!&ñ†|ºXÂàvKòÂ'‚+Vaä÷Úv4:”Ûy‡uôå M-xüa*7ì½3÷Ná¶±êýK!®Ú~wëPçE‘üM§G· þÜ,= hv¼×ÙÁ͆`®pÍ„=³C¹':pxH{®ÚüÅžÏa÷ùrêK®îãV`NDÖ+ÏÈG=¬(ð×d®ZÿýÙd¬kaQHCe*> `¤/Ÿ¾|fä|>i.ãâÔù ¹û®çÜPõ0·Ðp=êNñÉTÏ}°_\ná¼f @#W.ÔãÑÈúÜ×õ¸ƒæÜ¬3$4(£/êq ­ªH~l¨<†«¶r1†/þK •?thx»˜Hæ-x÷j*#ïŠ&l÷ æ¦:ŒhÒi8wõ}4(ÅÝúQB¦ çòúüt?NIµý¢s‘÷{ H–_fBÅËã¹…¢ÒQùòTn˜ÙŠöaÜ‹žßjeÖ윻 ŒT›_ÒæÂÂJ·òø$œÇ¿q¯´4âÿc¿®££H×ýÑãînÁ‚»CR5_ x‚ H   Îà2¸¦Ú;m•ªÁœÁÝÝÝõö]û<ï^«ÏÝ¿:Üs׺{g½ýÏg­þRÏû|;]õË‹~*Yõo÷}ÈgU!µæÿá´ `•\<È€-¯;0y?y?3s?…h~ùã›BθåDƒ¤2*<ÖŒŠ¦h‘<ü§Ûâ˜!Ó%Ì)1S!+ÇKXjŒÈìÛ%´¼»‰Y¡¯ƒc;ˆäi÷»¬…drýl.|¥’ZûoSõÀþæ"y딵Þö`n򨂿MUòô ƒRHã0 ·–ù3ËO–Ð0jš@Otaâ¡Ŏ‘.ô‰ÿ®Zû%Ì ŒÉ+O¬¸ô,–»×ŽÂñÌâÓœ¬«Á¼ÑߌI­£Urn^†8¯*¤Öùw«º°dIA•®Æ^Èÿ¶ßs÷Þ_NäÏžÿ$Ú‰kæZ*ùßþÁ$+jÄŋ̟œ?¾°7@$özÞOÞÏç~ŒÿÇûÿÊïm,Xz,F$û6³âÈèXæà™NœüR…éØï‰ýGòU„ŒõÒ3Èæe\(÷#ŸJ¾qaßî×90VFiqB™_0a^Ÿ"91ɉA¼™ZûHp ò(?•,ÚÏë~Ìøf”{É =a@•M]™*ÚñmÄ fšË‚²Kb™j}M£fäX_'œ_ꪤÖ~%ò8‘%W•Ìæ°ãá°^Ì¿»ÊˆXì¯Ió]¨uŸ™ºÍÃ¥‘ *'£ÈÊ%©uþÞ"îçÜ^DòØ j”ý*ñËŒè=m H›“ŽW;ÙZóOy§ÃË/J$Ws¢š­SëzÞOÞÏÿä~­‘ŽÎM£D2 ¸ ï„1Ìu£d¼þÍ—t ”ñgo…lWÕŽ5©ä¨Þ4úüûЉeŠ3°aÛÈQÌ&N¶¼;¥Œ´ ¡r¬JjíÕˆMgG«d‡3VV£™ƒõ:Øåg$!ÿ@…Ü1Ȉ¦×úªäiE]±BÌGL8!„1½§Jèu`°Bjíw¢¹ ko‡ªäÜUVTÿ-†éòsâRѺ̓©v”¸ÄìíëÂ3S¦CgÅën1L­ó×­sá[÷ëyï¢ Çîbžþˉ)¿–ÉæîëìEŠùZóKv—ñÇŒö™ã¦ ŸZ( ÉûÉû™™ûòÁ†+kƒE2ÿßvØgô`~Ýâ„ÆWrKvÔRÉÛ HŸÜ™Ù³Œ¯–]TÈ*ïM­ŒUÉø–zÌî]…YÙ(¡Y• iù¢Ç†_|DRkÿ>) ŠUÉM̱8œyhŒ„WGª d‡‰:ÌX–U$+„J8w×K![n’péä¬pPøÄ¦"Y7À†ñYC™šýxkFSÑ*yz  édMJljLïžœ~Ã<óf#\ÎlÖÅ€©^~Ìõ6"ævÉäš6,ŽÍÔ:ÿ@/C;ZÆ„ë/|É)#FÆVÉŽu9ïŠBjÍGŒ‚Ò^%sî’PtÄ…äýäýÌÌý¼õØ ŸÅDRã°›¯²Q5 ücTòÎ+#Š;†2ï±=[?‘,VÊ´ÄXf³ìF|5÷d^ìhÅò€Xæ /éè^1L%÷>ÔÁ5§$SkÿÊzþÚX%³_ÐáeZ1æúêéÐ2J$•väXÛyv§j¾æðì´À\UÝ„o=‡«d ÷çì\Pn…ÔÚïËo:Ô:—C%‹‘ÐüºN!+ï×áÔÔÂ*éØ)aq½- ùü© 约 dîi.\^ðT!µÎO“Ñ{i_2‡ûý’-gȤœ„Ò[ýrú=¶mñI­ùö9¼ZÅ äòûz|þÔB$y?y?3s?ó]wÁ6RÈN d8_Ogê³°ë“Jî®Ã¡MÙ™m®ÚÑ;¥»H^žåBÿœ2lŒÙÖ¶Éò9eØ;mÈ=uH]ñš¹ó¥M-’ZûŸ¯ªƒxèšBÝ,¡NÊB¦­  -¯É–íeÔÌ=H _„¹p£Ñ{æÜ­wÌò%Ot5@ì'’½žÙйÞ¦Ö~s‡I¨õ)¿Bî%¡Ú®y¾dœû¾(õì ròT -g ¤WãtÜü©’SëÚ’©uþýHeÅ´ ²ùÂÊ(dÚ :ÌŒÉ^c˜Õ–©5ßùÃŒ{_¢Erãh;ÒNöeò~ò~f¦~z¾¼Cdt¼SH =ó±K%t/5C!=ó<î¹Ó÷M‘IÏ|…(#Çͱ é™þ"£~h˜@zæZû/$áø¤7¤gÞb°ŒÁ **¤gþð¶ AI²Bzæ¿FÈØÒÛåKþì~ÂZ q¶9é™_ï¢Ã—¶/Ò3¯ÝMÂé"Ï}ÈŸ=?‡ûs81~Bzæ±Íeøí›(?;ÀRô­îäÏ^ÏûÉûùïÜϼ£þñþ¿²ö9Ê®í"’¾ÛŒðñÀ,—bBWï‘Ì`«5B£™;†›0ïÜpæ¹éÈv6œùþ¸„ž¹6 dÉ`r4i#’“ÉØ0T`8 ®l«’Zû¯ª##gÇq yîO÷}J…ýÌMd´Ù<ˆ™˜Å…½ ùUòÖi'ÚxÉy!D·ÈÝa&¼¯6B%ÏN‘ðlYC…ÔÚoÿH)Ó|Ȧ­e”8û‹BîJuàQ£_Dò÷ïNô?šŸ9ÆÇ‚Íó£TrœE‡ÁSò0µÎ¿¿Ö£¯HËî„kGCæ²4®¥’…wè±àf=¦æüe:”œ]%K8Ó(ŇԺž÷“÷ó?¹ŸESM8Ùv¤Hú3ãØ­qÌÒ±|ñhæævœˆìÂl½ÕeÏ}˜?n¹0a¼Q cÃ,xš%Z${¯µ£‘¾'³Ú;šxuSI{¼„³/J¤ÖþÆ .¬’K%ß8QÔPƒ9æžÝZ0Û»Ÿ3ðW8szuEª'(äÓyN8ꥒ‹ràеÉãmè{,„©µß¤½.¼Ÿ¼G!‡½u"faA•|ÿÉ…m-Ö ä†á2â’Úø’ñÛ$¼n0C \6b©Á"©uþå'êòÉ¿d'æ—/ÍT£$Ü,—C![ סٞW©5¿T†Ý äI±© 9Ó†1y?y?3s?[õ· ïÕ(‘ìæoÅñ*Ñ̸?œh8»UbšÝŸ‹y[Š ¤Öù×¾ºìõ‡@~ò–ñ!ïDfÌ 'Ft¨ ’ƒÓÑ:cSk~‚ÕŽ Þ=T²×X ¶e‹fò~ò~fæ~¶^âÀ/{‘ìÔÞ‰‡RMæÚqv´©¤’g{§ãö·qÌ\#$ ]ù»/™g£AsŠdDZ.u¾–A®¯«CÅå2v·ËËtÉŠ=eä+¨ZûKÈñu°Jž9`@‘a™ã¶0lyG‘Ôu7ïú8fò=ŽûÔf4á@Ðæ@÷U³d¾h4n­’Zû•»dÀ´g]TòÛ2=²Hu˜U+›ð]Ê,ÓÁ€~~a>ÍiDÉáÝE2f¤ ¹7 gjþžÝ÷au·´ȇ}elµ?ô%])F¨·úªdXIJ5?®Zósð$*yo§]É ÉûÉû™™úéùÊqÓ‰%ߊˆ¤g¾±¬EêE©¤gž”d„5¼¯Hz漸?»§HzæÏíéP|ÂTÒ3×Ú¿ßn=öïm¨’žù÷sßK¸HzæïN[a[)’žyÃ×F¤ VÉŸÝ/åwfÌȦ’žyÈaª«TÒ3~éBìË-ù³çO.#o¶Ž¾¤g>v¾„¾A}ògç_Ý*!fÇtüÙëy?y?ÿûÙá–\.—Ëår¹\.—Ëår¹\.—Ëår¹\.—Ëårÿ¿ÕóÅsžóœç<ÿŸå\.—Ëår¹\.—Ëår¹\.—Ëår¹\.—Ëårÿ÷z¾xÎsžóœçÿ³œËår¹\.—Ëår¹\.—Ëår¹\.—Ëår¹\îÿ^ÏÏyÎsžóü–s¹\.—Ëår¹\.—Ëår¹\.—Ëår¹\.—Ëýßëùâ9ÏyÎsžÿÏr.—Ëår¹\.—Ëår¹\.—Ëår¹\.—Ëår¹ÿ{=_<ç9ÏyÎóÿYÎår¹\.—Ëår¹\.—Ëår¹\.—Ëår¹\.÷¯ç‹ç<ç9Ïyþ?˹\.—Ëår¹\.—Ëår¹\.—Ëår¹\.—Ëåþïõ|ñœç<ç9ÏÿŸóÚcdüŸ<“ÕŽ¥‰dÿ4;vùõdúl6aÃÓQ*é—LjÜÙ{0g)N ¥™µFÛz)ˆYð  [Ï옡2úti–AúÚ$èB~Èý#L(Ðu„Hjí¿5L³ü¯3ÈI%lnéåC®ÉjF´˜J–ºo@“°®ÌI“(à¬Ìì7ÐŽ æ>̬Ì(\šé—WÆÀ^Ërüw^øRI­ý¶$º°¹ÆC…”ö;q(O)•Ü*ã³PÆ—lç/ãXûÆ 9©·û­Uòû\ SZ PHÍó]P·|È¿f¹0yÍ-æãfTD¤Jvij@ñí"Skþ©RÌ;࣒Ÿ¾Hø«Ü…äýäýÌLýô|U-£ÈÇ/2é™?ÛfAãÝÑ"é™·íçDå}ÕEÒ3?"Ùà÷çP‘ôÌÛŒh>z JzæZûmA‹áîÏÅé™×ªëăáuEÒ3¿eCÑÙÃEÒ3øÜˆvk‚Uòg÷‹¼ëÀSS•ôÌ÷ráêÅ] é™ÇÖÔ!w›‹ù³çp¸pçÎaôÌß’WÉ¢?;T°„1±ý|ÉŸ½ž÷“÷óß¹Ÿ¹'ýãýå÷Ý:týPX%ë ~VÈÃFŽŸ£’Ãk¸Ÿ;2èq%guæÇC"'²þáOjäZ_ÛÛLTÈN}tøQáƒ@ ˜Ñëx”Hjíßè¯óÉ‘S\˜Ví¥@Œ”±ìÇ%_2,XƼ²µrK1.Œ¼(y—é1?´Hæ¹aÃ+†ªäÛr&äŠÎÔÚ/ Ò…Yú÷ iPÈ•­¼JΙ`Dz¯"¹ú½ ŸcþZÜŒEÂU²Ã'–,ËÔ:E²Î'ÙE²žS‡Yþ…˜Ç‹Ú°ùá•Ü0ÌŒäq‘L­ù¥õh¤óÉññ$9˜©u=ï'ïçr?æÑágÒ²W°@‰yû¨N6SÉ—&†ç(ÈÜî/aÔ$Á‡ì~Ùýw~ ,ïr"ö·r*y£‰ÅÿÈô:â€Kç#’¹Ü{o³ÇIjíŸõ± ºŽ.lë#£ÅßIÌÞ&Êš.*ä®X'šÌ«®’£,FÔ81X$‡Õ±`aµXæÞö]; »÷IèŸG'ZûU»ç@û½-TÒû™}ïtc†Ìµ`ö—X‘|玜—£˜a$¼ *“‹E ФÖùoÞèØ¶ŒHîk¦ÇÞsU˜cò`š'¨d­K Û] ©5¿øW z7‰ÉåÍí8µe “÷“÷33÷óÊ\ ß–NUÈë#%üX‹Ù䦄ç·UææP +{ÕgV ×#п¶H–ÛoÄß׃™ÏòYñ¾vœJ¿bÄ’šC™S8л–/Ó¹꣘Zû_“QåsÌâ~ÿpÐÆVä²=vüÖ¢—J¶Ý•ŽŽûÇ1+¹Ò¡û6N$ÇqÀ¯@³½Ùˆ¨?3‹ÅÙPïþH¦Ö~//Ûò÷P•L²§#£X8ÓÏhÃrÃEòTžèÂ\±Ñ„­Ac˜ó'ÛÐmâH¦Öùщz\ý\G$íÑc®)se/ 8sÀ—<´D‡M/r‹¤ÖüÇœ¸RF$«¶—ñ1a”@ò~ò~fæ~ê.!yìú rû{zËѲ>¤Wg ¿_óò%óÍ–ðçÀ$¬9ß‚\ßcEÒ{¸ Y·bæñÑ#1²ªJ>\'amõE éÓ[‡¿cz9%„Ý$Zû‡‡ËØýæY92öŒQÈóË,ϧ’ËbMØÊ,÷‰‹‹ä%/3– ¤ ¸Pfåi枢2–ž[«ZûÕOµbCp¬JîénA¬º:ЉÖ[j‹¤¾§ ãó2 ø»ðG¯ìÌ£ñ2R7¬É µÎßRЀg·‘<0Ø€á+Û1g÷£/3ì±s[Æ1µæo t?gîj«Ë>:±/©¨Jò~ò~fæ~.sÿ]¿ž~À—¼7NÂ|?A û{ë`[}“ùG=¼ÖUIŸ«tRZ2'nt¡Íôy®Ÿ„*‰_|ɃÑ:a¬„²KZ ¤gÚ=•ïGª¤g~9Àˆ³“ûŠäÏîê¾Ê»¼‚Jzæ¿/qaH¹û é™»Ü{Z äÏž_¼˜ Ç¿‡ˆ¤gþ5ÍwßE•üÙùV÷ç4ÿ¡E ù³×ó~ò~þ;÷óîâ¼ÿ¯Ü°ÐŠ1A%Çß°à\ÓñÌþ.L]‘G$ùdØ¥ äÁ;&H§#DÒe³âƒñÌ•·Óa·D¨ä“Á­â˜M‚dÄN¯å‚íX—:H%µö¿Ó؈•ú‹ä‹Â&¼(3ŠY¨›„ ³k+ä_ —·žò%ÅËh4¥®@ötÿ§=+­Þ 6Lÿ4Z$Wª.Ä ¾!Zû=íoÃ¥&a"¹"ÆŽM2C"Lˆ¦’g¾p:&ˆÙ5›»f´d^kÅ´b L­ó·7”°ãÆ7'ùª±„”/¿ø’Kq¡G_•̥úÐ\L­ù-Œv´)ÚW$wy¹0®q¦Öõ¼Ÿ¼ŸÿÉýìÑЂ"MTrù43ùÄ2w%Êè~¥‚@f[$#¦LŸ ²v˜?vɃ›((x3wX 8¿¸»JNÐáïò¹˜AùLÈwz$sýf ]&nSH­ýk~2Áùk¤H>{dÆ£6ñÌ#»%¼O•rÚYj—/#’OM.ÌOº§‹k9ñ¦bS•ÌÝVFê–¹ Ù]µc^¡>*©µß€xª¿ë ’Ÿ:Ñû}=fE—VµPÉ·i:\-Q”™ÿ¥Õ#™ƒŽHˆ Ø¥Zç7õ‘°Ì¾Ç—ÓAB©E²µûýMM÷fT Ö:Rk¾%\†v?œ:DF™Í! ÉûÉû™™ûi>kB×*ù£ž IÉ¡ÌÄ®2¢'OWHÑê‚:ì.SWZFX¤N íî¹Ogô•ÉþV.,«AqB_NÈ­Þ:ôþŒùgS3.uŒI­ýg_·àxÓñ"ÙðŒ;úÆ3ç3 p“?³NGVnÍœPØ]ƒa*9g Š$07–5£þÌ(¦.Z“3Sk¿W•\Èy²°HNß鋱×Òt]ÂÓ§GòÊ ó{öe:FI¸Xc”@6{¤Çó‚Hjÿ[_ ?mrv¸„c™ /ëáì+’kÜÿÏ\3£˜Zó3Þ9ñâF)•\gG墙¼Ÿ¼Ÿ™¹Ÿ—çá8¬’ ^ppD 3èŸx1£V9`‹iËì™CFð §B§:Q¿Q-•´>ÔáQ»ò"Ya´®õƒ˜KCÈÒÊŸ™ŽŒÊ“2|I­ýKíOÇMs¤HÎ0Øp¡ÿ(æC-¨¦K`V™‘Ž*«¢™MÝ¿‡ÇÓ£T²àêUîÉlÝC‚ÅÜP ?0{JG‘ÔÚïF®¯H!Iƶ~å™ÜÏ-~ÝËäú@ Å yu Ï7$ˆdF’¯÷`j?x–„OÝ~ÈÛ$|Û³ƒ9qW:òœɳŸPâ[1µæ7îjÅÓó *™PÔ„ÒõF1y?y?3s?6 œŸ¿J†ŸÖcé¯>̦vÜ+5„™º-Á+£˜ |ìpýÌ œjŪ1 ÌëS-Øu?A$SÓlX>¿H.+ c²Ó*{¸ŸSÇOUH­ó•p÷Ð.ÌñVÂÑÀó̇…dÌhiaÞ˜)ãM‡K¤Öü ýõxS¼®J¶ß á¸×…äýäýÌÌý¼Ñ[Ýꨤëºûœe™ïrX1£öxæ3VL‰aÖ5›Ð*K8sS=ênËÔõsbTH}‘ì_\†oO£@.y§Ã~/•ìã~Žòn[F µö·”uÂïJ3‘̿ωööŠÌØE.¬:÷F ’1náæ÷÷jÆÜÙ2™c¡„†Á+òÁ|yKÎË ›w"nus•ÔÚïx Ÿ/UÈÎû]x<ç óÈB÷ßGEÒz€§·z2ǘXàßF%'6¶bŦÖùAUt¨ôT ³êpæZ‘ÜÕÑ…¦§ó¨ä´åvDÉý˜Zó‡·”Ð|°Ñ—¬ÞXÛØ7ÉûÉû™™úéùº?^×ýÜ*é™—>iD££!*é™'èðqÓw…ôÌ×/”qìúð Ò3ï²Ó€ÃË{ˆ¤g®µ‰>.”/—K$=ó–¿Ë°M¬šAzæpéptjq‘ôÌ×ì²`{ñ*ù³ûEÔtáë´B*ùßrº-’žyÈRÆÔ쪒?{þãßtè€ü"é™_-bEB•ñ*ù³óG3àÙ:‘üÙëy?y?ÿû¹Øò÷ÿ•ŸzÛ0|J„HÞ{nÄZ¡ÌÁ¥œøm¬/3Ÿ· #÷—dþ½Â…¤÷<K–´a^Ýl‡µÁ •œ¹ÛŠM‰™Ç¸p¾þ#ÌÛWÆÝ9ËRkÿ½ùd¬{zP! lr!~áf¹ƒN´È^M%·uàQÞ̇óô8r±sW1N/|§éÓœ(¾¾¾J¦n±âá…‰L­ýöô‘Ñsÿ2…ÜÿÄŸ3™“ýMxppœJŽ|fÀ,S?滦2ÊŒ3*d\œ»wQI­óß­¶ ëíD•\õ‹Ï›üÓËFËG÷Ì «írÿ7fÚlÃã'a"Yâ†ô,ÊÔ:Ô#ÎŽ¥’ÉÑFôJÆÌx*áÆÍ;™ÓÛ€+ÖŽ"©5?>Ä‚Ý3URiD‡ýC™¼Ÿ¼Ÿ™¹Ÿ…rÈ(!ÈY~2ŽfßÊ ,fÇ/¾#UòëÊt\{ÇÒUÂêÇ1 y´œ„ø|ɨ›éè¤ÆˆäºU,lëÏÔu¶#wÖ•|ÛÅ„ÏÇ1µöß|ǂԼI*óÕŒ~£&2×7”0|lyTÂØç§™Ï:ª¶É7îÏé[¿|Lse+†?JTIŸ¬4 mÇÔÚ¯üt3kŽWÉEcMÛ)‚y혥‹x‰ä‰ zl¯Ñš¹Ùìþ\4{ëC~žæÂõ 9TRëüT/#𔤒[Ý÷1IBfÝŽfTOÉ9Í>Æ2µæ¿|£C³ª*™=FÂÍÒ«’÷“÷33õÓóõ FF¶À$ôÌÛ²bȧD•ô̧$<©w^ =ójk]ȳã›@zæÃÏH0¼ ž¹Öþ=ʘñ,ÁýœpÌ%Ã\¶Ý¼Ælð΄uÙ'¨äõߌð{ÊκpõØ\j“Ï-˜ÕVÊpý:Q!KváNZq•왡ǽ7~"ù¥­ŽvÉL­ý§_7caòd‘¼¼Ó‚„_“™ë1´N{•LOÐá}·RLý×4,·|Ë còJXX½Ž@&ÝrÁµ÷•BžüaCFݱ*©µß’=º^ðÉÃ] (þ®3ä¾ $3ãú¥£³O³ p^ê®’úéÊZv)¤Öù)z'ÆE4PÉáN$þÚ‚Ù=È »>Q$ŸÓÑòù¦Öü©£u8z±¸H>^jÀõ&}™¼Ÿ¼Ÿ™¹ŸësèPó`‘|7H‡ëÇ‹2‡ ZÖ]%Íeôˆ:Ø€yp—Œ ¯’ ¹á­ «t™ÇE~|RÉôÔtüQ6‘ys¹»ü‹d˜{/ÛêE>¤Öþ¬·"9fŠHLNGžŽ‰Ló4 }&ìTÈ1?Ò`è–—Y~„A·2O =*Ôn ’×÷˜ð20A%ŸEëÐvrI¦Ö~>¾F ð.’òå‹fVVmH\Á\–Çå0ã HÈ8ÒD ËeÑ#pB]‘Ô:¿S'µJ½çÀþÕ˜ñNÞ–í"’§î¸PUÿR µæÿ6ÔŒ‚oE²mH:Î|û§¼Ÿ¼Ÿ™¹ŸgÿÔáÍÃ*"¹¾¶û{ð{#fçº:L0åQIÛ$ ®þŠBîãDTö–*é×ÑŽl­B™Õ—˜±­ü$æ ¿ °929qiGfå¹VXtS˜ZûÇ÷³¡\‘X‘¬[ÛŽ–÷G3ºŸ³¦$Ýó%kDK(3] ‡Þ3À¯Ï`‘œYÐŒé»'0>Oô[­|Éúwu踠¦Hjí׫³ÇG%Šä!äMfþÝØ‰à°_˜oê»p&¦þ‘À,ßÀ†¦8¦Öù]×:ÐgQW•¬è€Ó+Yú”ŒG#fû’%ûÈЗ´*¤Öü*õØÚ«¯HfèBýÜ%˜¼Ÿ¼Ÿ™¹Ÿ;Öé±çD²Qfº˜m‹H^û…<õ4 áÝZ‘ÝZa}6Y%o64ã±<‘Ùuµ;‚+0‡úIhÔèw…ô}­ÇU£¿JÆe—sli…ÔÚóK;*<(’‘:† €ÙÖ}ŸòÐP”i×ë“܆99ØŠÈ’ÉÌ›l(r$’Yn¼5’’˜áÛìhZsSk¿ê¬È}ŠH&ÌKÇ»™;Ü÷AÑÛ dÿ©22ì‹™eï8‘p³†HVÝéž» œ@j¯ÏåÀ²ÖT²¢Ëޝ‡0/üêÄÇð¦Ì?í6èD0µæ7[#cÊ X<³GFbÎB ÉûÉû™™ûµÑ€¢Mú‰äÜúFœïÂÜQLÂÜÐv9Öýœñ"`'sá_T)hìÀ‘¢ƒURk¿öŸd|«°9ƒ¼uZÆ­}ò´¿Œß¥syêOÅö§0sl´c\L¨H–ÚíÂÅð|L­ó|wa[ýÏéWSƦk÷™Ã Ú¡VÉiv'Z^hÆÔšß¬­ »äÊ"ùäïÛ­H­ëy?y?ÿ“û9´L:.EMSÉ ÉVèf¥0ßìOÃõr¾dì¹4 ÙšO }ËK»·Œil ÃãóÅD²ë™4ì›M!¯¸{½ÿK¨@Ö:œ†‘Ë6fq½tøVF$µöŸœË¥¾»×ÿePc3®TNfþjD6u,3õ˜Æ]˜3,¹šÂ´M2á´ûsJÞìbAÏ…)Ì’ëô˜y®Sk¿Ä92ÊåÔ+¤_³¯=b^þ(#Þo‡/Ù샌¶“” rÎwµ»åõ%ƒsºç¶ü Zç÷é*Ã\ýŒ@‹”‘^hÓë~2»Ï¹üú¥LjÍïôÍýÿJòÏ +ô“ÑæË1…äýäýÌÌýü|Û‚$÷÷ yÎß‚sÿibM ÕlërœK³,ï˜sïèþ°«HšŠ¸¯¦8f©": )Syï«{úö`ÉbÆÉ?'3w,³£CýÑLÍû“a&ì¬5A%±Âˆ!3#˜ï&êÐd^%f@²„ù›.(d‰ øæ¨’sêÐ&¢.óõç4ý¥wB%ÜìyD µöû¶Ò…†÷‹ªä›7Nì ¯Ç|ä’‘9S!·–ñ¢àeæ–2¼Ê1D%?å°bÙ­¦ÖùaóÜ=Ü( d·í2î=XÆ<ÝNFë²ÞP'ê÷o­’ZóÇírbÙü¦*¹?ÌóÃ0&ï'ïgfîç\ƒŸ&LSÉ%Ì0…i˜¡C=¿*"ypºKo¶e¦¥XÐy_ sË—t|~žÄü-Å„Z±™—zX1Ò™ÂÌ5RFÙ€9~…Œ6)¤Öþç  QÉ¡U èhëÍ,÷8 Ÿ;)d¸û>Eº²/¹b¾„go(ä•›i¨ñ°!ÓËeÀýRCEòðV+¦÷Jaj>Ÿ4sâb‘ö*™¨¾ý™ ]ȱµ8óÞ‰/š3ç=0àÆ¯Ã™>7%tž©u¾y§Œ5§ ä÷sÙ’Ð~ÌÚkmX½!^%߆»ŸS­)LÍŸÿF+þè—¢’÷˜PfW“÷“÷33÷óC² YLTÉ!O¸ñ.†¹Æ`À•®CDrûg#Ò¶Å2_´u ¬k sKe:>®ÎÜWÄŽ"™'"¸9ëfçÒô©4D%wünFñ&Ó˜ZûWNÔcÅ ?•Éø­êz_rFe’ðX »½’±9÷[_²²A‡ˆ5Uò¡û÷`øíL+Rkÿ"ýtн.«’CÝß{ãÇfg.¼©CüÓ"¹»›ûWöe^ѹ¿ç*2»æqÿœÇôdÖî,£òÓ³ ~ÛË•Ôü~OLÇÅŒd•Lˆ²béÊæè*6l_—ȷߊ®•R˜ûn°¡áp‘œôÂO·S˜ZçÏü(£i{½/ù워ï ê0‹Ô‘bݨ9¤¡WO½/©5ßïKÒ£òÜé4ì^’E y?y?3S?=_Ezðìf_•ôÌø¥CÎ1M$=óN‡dT ŒTHÏüÊA }£Ò3ï±S‡Éåꈤg®µŽ)>>?¯žy¤û>äÅæX‘ôÌë|5¢¦+’ž¹òÜŒ.ÙSTòg÷Û4ÜÛŽ•ô̳àÖ¶•ôÌ_wàÇÐ!"ù³çÿßï×9á$=ó2f µŠ½ÈŸ/š$ßñR ÿßì÷Êy?y?ÿÿìçî†ø?ÙtB:òŽŸ.’19m86é#aÞð‡ 9ûqŽx¯`î>cBð¡i"Ù –‡Î¦2ýé0~5•Ü_D«¶6…œ¯šqró •ìñÍý=>nD…'ã™¶Fîæ=,ÉguøìÝB$µæ¸áD.¯V"ÙyŒŒ+Go ¤Öõ¼Ÿ¼ŸÿÉý,÷» ~$’3ËÚ±2w³Öæ4øKÙrÊÒ4èÞÔö%?Œ·¡ÞÚÉ"Yòw¢1Ç-IÃÇ'd²ÈË4“×䈭iHìQ@!—¤Ix²8»Hjí¿à#²½ŒÉl˜öf"s‰ûß-LìCîÊ‘«;ƒÒ’Å„#ErH~ ª ©Ì'õøõS3ä†ßÌ`jíÖ׈Àˆh•\2Ñ€“C˜uªgàô“3äÎG2zwž¥F÷œBóûóýdüžôV!µÎÿ£œ ¯g&‰äà1&ÌÚ7…©ïhDÿ)QÌcM-è>>•©5w·½çK⫌+Ç($ï'ïgfîgáuvô!’¥*:°wÝHæÓi84µŸ@Uq?‡ ÚÅLpáÙ¹Z"¹|†Œ>óN d3/b›•É·_ôðWû2ë˜9;žùéQ:]™ÆÔÚ¿µlBÀ€i"9cª« Î`¦$É8´ï¼BÖ_áB¹´ *¹Ã;CfˆdDM;6u‰gvÿʼnî%˜.Ê(øu¹@jíg–ôèõª»J¦½Ð¡¾É‡>IÆTœWÈWÝ{½)¬’y×9p¿ß@æê[V¬y1ƒ©ùýë4áti"¹(—eæ§0󷲡D¡©Ìä§D6 djͯúÁ…ÏJ>•|ßЉìI]™¼Ÿ¼Ÿ™¹ŸYV9zp HúrÂûafAY‚óQ.fŽé:4ÿµói¥ |™’U Û–È@§quÒw ¹ZOÉ]_-¸v7•y`¨ ¯ÕêÌFî½Úzï÷!5÷ïlAð¢T‘¼YÜŠ¢×ÿi½QN¬Þ×Q%S^Ûñ×¾ÑÌ+ï¨Ûµ·H¦Ä¸ÐãIUæá³2JžY¥¾Íœè•§«JjíçÝE‡ÎÇ«¨d‚*¡Ð¦ÜÌfÅ\¸Þ¢1ó—ÊN.Ýyì‘ãý'2nÔ¡Sý†L­óö5ÃkÕt‘\¶ÝŒIq3˜7ߺp³q~æ…üxµ¸­@jÍ¿`ú~Uò@}+Œ§R™¼Ÿ¼Ÿ™¹ŸÍ§8Q¦P{‘ ¿çÄ"ÿ–̳ãõ?·3óþ"rÕÁœ"#ûð» ù¦¤ #ÌT2n Õ‹OÉ…¯˜¹¶7³ê²})¯’IlIfjíßåºYÞÏÉÐméð¿œÂ¬ÙÓ†ôÉ*Ùñw+>Ħ2[M—ñ­ßiÜ\*±O«2WŸ±¢`¾T•ìåmÄo="˜Zû­ë$! Ì…lç~.ì{{ 3_An^¥’a7lHz79ùXn}KPȶçÓPârª@jù£7ʧŠä„.ä[üO[È@ÌB?…|ûÎ…­Ÿò©¤Öü“L˜1#Y%]ù 8P«?“÷“÷33÷sxGÞl®-’×»àlZž¹ýéÛÇ3í%ÍØ±/…ù4ÀäJ!*ù ¦ ã6OenÞï¾o^PB$ïÝ’Qcö<úÙˆ%U²ØU 5Kbjî?Ö† •¦ˆd|-;æ‹ñ̾GÌÈØ3C%ë1!µp³Wõ ´w:ƒiñgo§B¾6Hè7+‡J.\ž†iÞS|I­ý6ÙÒpó¥¨KÝ×µükJyz]:’ߦ¨d³@+Î;R™T6¾o"’7 ›P¤@SëüîXpâ\ªH¶~nA±ÿ´Êsúå TÉœ¾6Lø’ÌÔšÿ°’ë åTÒëYÞ¬RHÞOÞÏÌÔOÏן/]è§€Hzæ^Qlq¤Š¤g>ýœ ¤ª¤g^¼~Ð'ƒôÌÍ«ÓPÎÛéKzæZûß¾nGÑÊa"é™ç™`Àùš!*é™wr¸p·ai•ôÌ´Ò!àcE‘üÙý~u÷°ÚñI¾¤g¾/‡¥ª¤g~3o:*¬q?Çü—?{~Ý6Vœ8*’žù•æ|™˜ª’?;¿ÞÒ4\èQÖ—üÙëy?y?ÿûYsÌ?ÞÿW>p¤aj£µr7 ÏÞìd–IsâôòN"¹h‘ YFÕfþ½ÅeØoÌÁOÒ?1•™s Ñ=§«dœœl6Œ©\’Qüî…ì¸Îé—‡«¤Öþ#j9$A%ûM4 öì0¦i¡ ÑÓS˜g¯Y±dÆ,æÂ7<ÿM$+|IGˆO*óÞÐ4HïVû’OßI¨”PI$µö ùêĘñÉQë\ÿûõE߬ Üœ#`FsØÙþ *¢€¢1cΘ("Q‚(1°»ä¸ìÎ0kVÔÛ,æ0cVŒïV꾫öÔ{¦üö<ÔòåWe;Ý—x);0°GrZ7œÜñ’Gç(¡ÅÒݤO½PÂ÷•nÝ—ö“6 ¨¯ õöŒ%¥òÇ_I…©§Æ ¨¿m*´èçJn°Ì‚f%Á¤åü x¨ØHf<Ο‹‹8´ÝV5غ %ÛìÔ÷þÏò«,Îg‘RùÆuÒÀ·“u9tÅn ŒoñT†r‹•`ïß’GçŒSBÝè4tǪ4X¾o¶€NNWÁj¹ )u¿f 4vM†&wTÁñ¨rïýßûáŸ<ºn¾ú4l@Jí¿å¢†7Fp¨g= 4™jJûiìgyîg‡½*PûÔçÐ/oUP,ÈŸ2-d.'CKfh¡vG;ôÆ` ´üZC´Ñ—Í2ôä%XÔsÕ¢û•x%T†ÖÒ)áõ¹ýfÇ¡{RÁwührìÄHD¶û”Ç]Ç“R÷£>¨`þ} ¥OuxÚž Ù0søfòL›<˜Ø}-)µeh>ØÍš. 7[å‚ßîÒØOc?Ës??J…¡]&rhƒ±ip½õL2¢OÄýè' Ãäj˜iëIjÎæÁõ©KÈ»ûsàíº@2µ þô äÐíòáÛç@5÷Mj8_j®‚”wdèßæö«Š6PÃù¶š™PQÿï5œ'…j ¬j)þí}g] hbäú¿òé?w¤µ©/CÿvaŒþsaÚ(ýÛçý4öó?¹Ÿ©Éÿóëÿ?ã"ô¹fuàÑZ8ë8“ôöMÚrÆ¡£õ9ø±äÓ *è8¤)y¬8®)‡’ëªaש¤ÍÑÈÓlU Êú høÞ\Hò $¥òç'©à¾¯‡úÖK‹á=HŸ‡J¸~í™ £PAØúVz¶4.ýZIº¬SÃR/ò÷( 70ÐÓCòÁëÞR*ß·MZpJ$CûíÓBØ•úñU6 ™* 'ŽfAÛiadý–J>ÕQ‹>LWB•5§e¨Ô}Ç)YP¿K˜€Æ7ͱ8”œÐM âASšû^ RJI©ý^¶Yð°b˜€¾¸S.RÏûiìçs?ÛÕÂÞî1<Ü[ õ ›lî™ýLqhÆÌthöÚŸ¬£C–’+dÀ»ËA¤ÙW ÀÀ[2´sœ>ß­O®}œÂf̧€züR*ÿª@ý¿›{ý]-Âl<Èõ)ú÷‚iޤþ=ð^±ùª¨n¶êJ*OjÀô×'šÙ+Ї‡ èÊšé¾h-)•¯²ÞmÏojPmú÷À1µy´uB& .Ù" ¶+2`ì ²gb \9ˆCó£ÒaÓŸ ¤Ô}‹‹™ * ¥Á™Ð—ßBÆ.KLÆ¡¶qiÐøª/)µßE“ úNÐ?TÐscWÒØOc?Ës?ÏVÓ‚UîEmvE÷V¼%mÂ2@=6˜C-…L0ïJz®Ë‚;aäõ­9púDY¥… ƒ[ñè¹—ð˜ùl;I }BÈ~J°kpH†Jåï b'qh‡öip|ý<ÒÏ!úœñ#΀¦ý7“ÍôŸÛw(7ÉÐ÷‡´ðjÃS[Ôû” |n' £{(¡ß‹æ<*•Ïu¡ÔõVóè¨^ZøÇ2‡ì4)~eú èýÑiP2d9Æ$îO ãСy0¾¥)u¿å€Lò9D@ùoУֿZ´Ì„ÛA!z÷B6´K %¥ö›SB=»k<ÚÞZ ŸüÔ¢Æ~ûYžû¹s»«« h°—"Ž˜’{ßgߌ0½Ð>j¿ÚB¾σYÖ“W<Õ°ºÖR—^c&÷Ðëóa¢å 2Ø$öùàÐvc2á¤l )•¿ôHÌ{´‚Cƒ¦§ƒC±?ùÅ= úš‡‘š§ÙPÏ7”ë¯_ðàц_4pþÎ-ò×~%ð}ŽÈPëê©0\îÁ¡Rùj?ÐÀýE%Ùj8ä0‚”ÚÿÞF µ»ö—¡õïU#–’Æ~ûYžûiÒ@\-tÕÑX9¼yfT.|9Ä¡gWæw3òÑ9ýï;Ô…¼ªÿ{¸^±D†zÝÊ…‹M7 è¬lHªFîi®läÐ{· À{j'R*ÿÃ:°àÙ&}®É€*-6“U—äB³AAäÏçyðqÁ*rt= \L´Ð}WÔ°qípÒñL œ°™CÖÎ…V ÿU*߯ß`9¬€®ZR¹r;Ò¤HE¬H‡N*°=[•¬´G ý-OhÑËñXû¬ª€JÝÿj•[æ hã‡é°»4€>WC2êsèúùZxÿu •ÚßdP ¤…Øp¨ªIŽœKûiìgyîçïP·З àW°#é´%ŠßÌçÐjÓÚ‹4Ý …š#ÜdhÔ-dŽ›§Em»dB²:D@¼Mó³«É©‰Z¨¦zi‡Z\ÐÀû˜w<*•ßoN&hláÐ -³ ïn(¹Ô`#ó&Í,´ö̇ȚKtÀ¾\x72‹kjHu. ~Õ9b2Ù<] -ËNñè™ÑJÓŸŒwQÃÎ:4±i.¸Þ &¥îWÙ•íÝ6 èå±éêOºíÕB`až}û[ îñ¨Ôþ…C2ÀÙ<ˆC¤dÁ€ a¤±ŸÆ~–§~~mV«apЇ€Î§À[ŽC ç.ÎZn¶G ç­¤@Gnj8?š•1Çæ¨á\*ÿÉKY°]ÿÞ†ÎoŽÕ€“³)‡ηœÏŸ]¡j8_©…Uþíxôoóy-ȇî èÿÊg¥„[·îjQÃù‡Í™PP¸E@ÿö~\“tÈsY' †sÅöpÍï# »ßÊ4þ”sèß>oì§±ŸÿÉý<õî~ýÿç‰<(Ø»‰CæÁ;› d™wôuvP·uj‘9v· '· É›HÍj§†Žæ‘Rùž± ¸¨Ù¡3eÂÌ&d”Kl¹ïJ¶«­úÁ=É5ù*h3¥§€¾²RBËÓ <*u¿§U> ûÇO@ÕOó@ùnYA‘ûÖsè.yìLŒ"¥ö|¢ÿ¥›‰ ½¹^ nçn“Æ~ûYžûi’__V‡¾¨¦A}{‘~2àìê0=.;‚È•7´Ðeym8R ŠÎ—ÉÍsr!¼K¨€žh• {_F‘ëÛäÁ —`M{Vfû“RùïÎSÁ³í´Z=ìï`BjN©Á¿æòL`>düXI~0M‡ˆmäü ©°cÞl2è±l«Î¡½õwÕ—ÏÛ¢Rùê–fBÏ®QúðLLúò¯ÇõŸƒªO1%o÷ÒBÂù§2ôþC,^Tƒ\öI ãCêp¨dæA×ÛúlZÉ^sàãôpm:6”²µ¤Ôþq©*x¤íΡíôŸ Ü7Ï ý4ö³<÷sà Ä̶âÐÇñP[4%Ï,N§ËktÈÒT5ŸEò“4ÐâKòF­øæI¾¼˜K:†“ÙÒ ™•?)¿¤…éÛA†¾œ¬›ZE<*•ÿ¢V Ó=ñhÁR%Txx•le™Þ#ƒ´Í`}"È+íS`pw’3QÁ¼º¦äµ†Zp¨ÿG7¬PÃ`‡é*•ïÅ¡lh³8ŠC­7ä@ÊîÒ#N ÎvûdhâU}/ûô!‹û¤Â£zS9tа ø]¶…”ºŸ¡ÿs?s Ðô{¹0ðæfÒga¼í0„C¿hÀ®BeRjÿ›éépõB ‡žKÏMv$iì§±Ÿå©Ÿ†_Û®kà•w-5œ'†¥€ý<75œó3òáŒç5œÇéRàë²Qj8ž£†¸ÞÞj8—ÊßÞV 6=SxÔpþµY6T~% †ó§Ã•ðáÏ£†óé9°Õ4B@ÿ6ŸÊ+º„r¨á<¿T ×±C çNe7 ŠCÿöþ§„\Xn¿E@ çþÑÂØTú·ûÔÍD]$‡þíóÆ~ûùŸÜOA<ü_žRdÂà—± ¶™°äZ ÙâzŒî¼™Cïχo}ýI!POîjÑ_á ¸ïSS†vW¥ô ÐÕ¥)ðgå,2€ã¡8ðˆtH k[ó¨T~Û7 h¶:W†r–Jpð»IÖº–užG hµ•ÙPåié/jÁ)íÎ0ÓÂóþ&úòEœ»Í¡f{s`ht )•o¹Cd 9tå»4ÈsßLzø)àÐÇvhÝ8´³±’¡ƒòáÄžŸ…)1¤Ôý}b>d-ñçPŸOù0?g-i·^¿ù—jôÂiŒˆ•¡’ý© ÿ{õôçP›—é0È?‚”zÞØOc?ÿ›ûyðL´™- ¶3 uvy/I Žó9tØ‚˜ÜÖ‹Ü\ª€`×|ª>¤„:[ph÷õ*88ÚV@Íg)Áo`EÒz|Ä/K^?— %B)•ÿ¼·J¶ý’¡ªƒJ¸àÚ€C7É/eœ€^ç3Àµ_4ÚYîvrò¹þ½NŒJNi †AsWsh§, ”„´'¥òu<˜‰¿Â8ÔsR¬ä£ÈY'`ß.Z†^2Q€ïÈkk2¡f`¬€:§ÁÓ@Rêþ k5,ó\É¡½&ªa`ÑR2Kÿ}rÞ€üS-Ž:#¥ö?/Ë„6ëã8Ô¾Q(?Æ’Æ~ûYžûi÷>|Fèã th9$ŒLl§4àÐõÇ40´N[2+U·ʯ)À ŸM¦iÐèy Ðÿq|˜[€>MÍ„Ûb´jQ*Üs^KJåü¬„m=,9´êpŒžÞ“ÔÌM‡{»C4©[xTßH¶äó!3ÖŸìã˜Í«‡‘ÐŒ‡Ø•Se¨[îYÉ£Rù·Ì„1Úé~&xŽˆ#§¯RBלªdé7%T¼cI¾®“«M‡ èèJèPô…G¥î[E©á€ÉBmtT ‹zÎ!ƒ‹S¡ó¤u¤õ×t;AJíod™—KÂ8´Be5\Ü´†4öÓØÏòÜÏÅé" i0y_ùqˆ®Ê¡Ãs´Ð(ä*_“ãÒ7q軎ÐAIŠy ø0Í_†®Öÿ~8ûÙÿ±øÍ-ô{¨œ¼+ð¨Tþ³é*ð.À¡ š¤À¥enäôÚ©piÈ\P#¶ù#=Ô9PÍ%†œÚ<žXm#Äk F¿Îäò—ù Z¿Ž”Ê眔¡m·qèÕÙÐ÷KY©‚m9ò—o9OÒl³ÊfÜÔ¢#+`T(*u¿ìƒ‚ÚÏàТ6pöÃ$2Õ1 š=Œ#ŸîÉú÷>TjARÄ…»s¨ë+ t1kAûiìgyî§•gXŒPñC*Œó#ûUäÁêG˜ ÍêÉC³aÈôw™°ua‡Î;’ !ÿê±H]Zô'ëÜKn©>d—jJøÒû” ­`—!OFr¨TþÕÁ)ßk"‡.ûu~Ï&‹:© ¦¾WèáJˆv®JŠú÷ŽË›bÉ¢’tp^AÆ/͆&±ä… H•G“RùÖu̽gb9ôçó8Mþ•ãîÏ&/¤Âû3kÉ•T8¢-y½™þsOîÝ•ºËaäÚké`Õ ‚T=Ë‚äÛH©û>)`1ÏC£Š @µÛ™ì¼BÎïzÝ´0µú=*µxwN„Yðhdª’[\&ý4ö³<õÓðËìA Œ|ä# †ó õx`Ý×ñ¨á¼Š¨ÍŽ9j8žƒšmãPùx,~ðçPùTþ Ái0 i ‡Î3Ö+ ¤• A 絪`k³!j87Ù®€Gºóèßæ»Þ |q¨á¼U\Lܳ•C çö_sáðõpýÛûk+h`XÚ@5œ?éÁƒ¸¬© ýÛýS 4àÕÕJ@ÿöyc?ýüOî§Ý¾ÿùõÿŸ¦¾<äŸÇ£='òp,;ŠÜÐ7âŸ' hYÏ,Ðîü×O– PéiЗLÓ¯8ÈÐß¿sáØÅ «Ÿ>7ȸ˜ 8ϡߣr`ÉÁ¤Tþ‘uraL“íj±2d/vׯê{Ysí¨€=ªÃZtZf>LË Ðëƒó`8‹&+_I¸Ρí†fAMu")•/ÿD<ÿ™( ÚšYÐaο>éÏÃý 92tãR|Ï'—ŽRÀìÃëx4½›2W¡R÷W–¥ÃÃç±ú²wŒ®°Ô|Õ¿÷MÙ. IùYP–HJíÏ뤀÷“oØ¡ÃÃ`•'C¥ž7öÓØÏÿæ~æÛóP囊G3-y˜þò8éÕ#ª$íÐéú?×Û_qdÃ4xÜûG†–ÌPB¥ÛÍ84Æ4 üV$ ¨eë Øx ެ䍆IŸ6ph´©â?u$¥òÏ}” ×:% è©¡ÙðõÖ¿&Z) hr´ºÐ]M&,–¡Žç@·Z;TèŸ “JÉå×söÆrèù_j(i²””Ê÷ux&tù¼C@×oË€&{ãÉüm< |øÄm¯îëâÖ´šþ½îzQ™ ­®‚†Ç†q¨Ôý“ 2àV…x­¤Ê§—ÿzcEôh/ ÓÆ¥A‘õRjÿ/%¼ŽkÌ¡OüTPzo(iì§±Ÿå¹Ÿ]iaغ{<:àªÆøJÞ–§C¸6J@gŒHƒeÏBÈõÓU ¸æÄ¡‡ö¦@¥ ÉgÎiÐowˆ€N:ÃO/$§nàáZ¾£ m5‚û[‰<*•_—›?¿% èÖYP´é_} ¸rF†Ö좄Şu8ÔA‘ õ'èEý¿mrYà¡ ¹9‡Ö àáöj{*•oÂÃt¶ V@·õHßéQd~4®3ªñhø\<òýɪ7SáÍÎ@]¹;x1ž”ºû4V Û9Ô×2ÜWì ýפ@̹Zó³"®õ%¥ö»|OhÏ•Ú}H$ý4ö³<÷ó ÓBQÖZOÿ}¿Ö™ìÿTªô±d5;‚C_5Ï€{¡qä&PÂÖ+ ôÎ>ø¥œàÑÞB&4y•  Nú÷ÂåOCH©|²Ö)P«ÒݸK-¹“×]ôŸkÒ\H~Otñ˜BöÊß$š³[ÿ9`÷d-*u¿¥{æ9´^x$<ÿWÏ`%\­`I}©‚ʣƑRû—NSCV¾?‡6)-€+“½Hc?ý,Oý4üÚo¯YU\Ôp~µP³ÖÝäQÃùðúj°·ÞÄ¡†óÅÑJunË¡†ó– ðæU€Î¥ò÷ê‘Ût›Ôp~lF&TêžÀ¡†óoö 8gëÌ£ÿ+_  "“]ôoóM°RÁ2¹€Îç>VÃżej8¯W‡5ϯóèßÞ¯}& vþJäPÃyPZ*œ* àпÝÙE +¶ãп}ÞØOc?ÿ“ûVK€ÿ˾B2È*ÄÚ¡vÇ’!aÅ{r¹þ÷u¹ae‹&T`P§³ZôxåT¸pt“€ž»©³ósHÇqÙ0af‡Êêæ‚úònÒ£(Fdí!TQCUû0R*ÿÊÂdðÍRhQ§‚d~|‡];P »…phB¯8•²žþ3 :G' èŽý™ðdÂ>rZh&Léü¯É1iðÙ)†”Ê÷øC 8VÚ$ —¤À•ä5ä÷Ù°st‡6œëî#Uwsàìâ=d¼G>LdѤÔý0%صé. ú*aI¸5ÙBÿ9æBH52ëw2ÄÅmãQ©ý.ïxh>ʆG•[x8<£ˆ”Ìgì§±ŸÿÅýLü'Æ»u¡£o'ƒ{Ê(Ò¿„‡Ç'=yÔ[Ãð^ûHÿëJ˜ýÂQ@£*)¡[’993¢4Û8´¨¿îÞB®Rhà“iê£ÿsF?–¡Rùwk’¡ÚÅþvèÚ#ÉPµÚ=’ÕÕÀ‹1>zà¢V´J®uÏ€wn»ô}tˆ]·|[1Öm\H¶4UÂF§6¤ä÷ù« n¸-Ð?*x]s2©nš ¦ªÝºþy.ØöL$/­)Ëä -Ä ¶¤ÔýýÍ•ððY[¬ „ÖÝ-ÈŽÚdØUÛS‹Ö*N†óæƒe¨ÔþÕ·µPìÑT@?”h Ël'ÒØOc?Ës?}^'C«dèñ ˜§ÛNnYɃɅbõ·â¡lvUÍœ¤€©ê§<ºìC2üiBŽÜ^Šš¾úí”"}]ÉþúÓF²Eßlà¡û <*•ÿî…d8x¦» Í’ Ž–‹È«µÀíÊ¡mjð0Ò¥.Yx9b«„ ¨WN ¬¿¬þ9æÔãQë£É Õ=µC¥òm¢'î꣄vo_³óà”b‡]žéM·’ëçðp5þ µÿÍøŸv¨ÔýÙ% 8éÓT@û\PÀ¢ÒÝCŽ_nËДŸÐžC¥ö»« À;z‘€î¡†ê.!¤±ŸÆ~–ç~Îmª€Ïs3dèÈÞ è~ïyï°LZèÔ¡ZåÔtÖ¿—¬½}P‹ŽÑÿ\­1öŽj&j!­^+=´”‡ƒoÉÐ/w4p¡“³€–UÃÅðR*ÿ?Épìm¼ M°T@ÿ¹jrˆþßÕ×_Åä¸"Ö¤G‘ç3T0G9C@óÏ)á~‡Á¤­Ÿ„e24¿¹ ¦ôàP©|æ^JÕ¿‡€N®¬ÿ{¿hA÷RƒrËýýD =kÍ^ò`}Ö•G=»óÐéP%•ºÿA­þPMÖ¿ ʬFŽuWÁùÚc9ÔúH ÜWo ¥öσ”¾Ûô‚þsŠN¹›4öÓØÏòÜÏ¥úÿ×Ö»#CuKðõó{òõ ¬:á* «zjà”8…Üò;6ÌÛ&Cƒ–*àÊâÿú˜«…“É@ýÝýDTlž W’v è‰äL¨;c)•³Ž..–¡¡+ô?·Þ~"Ý¿óÐÚ¬!ªÎqy ­WO ÐÕR@{韋PâÑM Rá­,„CW Ì€ZÍv‘Rùü àt¶Ž€[®€ÓK>ñhiXœ²ŒC{[kàû…©dƒ[¸×Y@V(€¡Ç6R÷=ÞRA@èÿ\=zæÑvóÒàe³h]fŸ‹ì"¥öÛLȆg3’´,1²‡í#ý4ö³<÷³{¼ž¬Ì¡y¢ûÔ#ÛDÀÜÚË´å5L@jZ*Á7²‡N)S‚ó`W2°„‡?ùž<¼†‡iÊ«då±iðfãV¥‚>¦Rù“Spͧ&‡¦=U€ùï&ä2âÛ›òè¼Ó<¨²·’gk) Á‰$òð™dhö¢é;:¬ç$qhœwØgm'¥òÍ|wžGÕõ`º,™ÔhÀöÌp ²ÓBÜ8[ÒúH<ºM@#ìsà»Ù>RêþÞé úù9:ŒÐÿ¿³û™Ý= <{'qhP»8ï±”Úïó4VÌNÐÐÁiÐàYiì§±Ÿå©Ÿ†_ƒ^(`vqS5œç[©á\õP5œª˜«r¨á|W‘¼—™ ¨áüÞ¬ìUM@ çRù×X)!­w{5œ—,çábÙu5œ÷)HÓ‚[ÔpþË´Î\õçпÍwþe2T¾èÇ£†óü4-Äjá†óf)™°fî>ýÛûÏ(À¯çE5œ¯›rßýÛý'N§@°éFýÛçý4öó?¹Ÿ£Ý¶²ÿËe;°á[ÊDô‰®céUuèÐø`Vó¤ Ê÷kØñ{£HÇä–àR3WŽšŸŠ€ùQ±€Î\tZŒs`h›ýÙ—ÐŽ ª²|6T'cóê²;**•¿*¬î¯L@³+±ó7“Ÿ÷)¼o“FÎÌi.ÚÔ½EVX‘®­¾—lsy Œ;¸ˆüýFÿ^Ó{.yyP?6mÿR*_ôlóÂËÛkÊÑÂN½ä£º…‘§Ë,˜XdËЃ3Ó@F™¡ƒÏ^äÂÕÇÀæÞ?€JÝϽµ &DÑôî1`ù#‹¼[# ®~k§C3çgÀú=ö¤ÔþÏ3W1‹C 9˜õÐ|‘£RÏûiìçs?ÍeýØ2‡º:49HÎܧš’.s¦²}7úÛo0kþÀ„LÏUdèójú®LëBÞ©¬÷®Pç9ØìNf{,aM›äÐI—Ö² ÿh@¥òû´­Ì"üП˫²55ïÈÑõ«}Å ‘/íŸ+Vl›óûÄwv#ç?3cÍ-çüë©ÎìR6 vâ L+iJJå‹ô©–ƒ>ÈÑ‘kFƒõm@7ΨÍ_”1tƒò½ßìÒÛ”ß| AE=thㆵٲH©ý ê³vQn…è¢?·aVtˆûiìgyîgõåƒØÌÏMthÀ}ÖðUs²]hGöìÒN9Z¿¤Ë­[E@äE‚ç`hƒ›úÌÍ™F[;T³a(‹=l^ˆz{ËYÎñ}€¦ÅÕc3´Q©ü6Úš,>¬º]ð¢>;Ú¼^!z#^ì=?PÅíâ$/2$¤7[Òe/ù>Øþ±÷É‘¾ÅpãA$¹ÅÇ=Y°¦•Ê·uR4L|ëè¯ô\8ÒVAZÌx ™/š14Âá|~ÿ ÐÃØ—Šåh–ÉöÝ"P©ûKcÁ±1WEô„×TÈI¾O¶Ÿô^V‰'\ˆ††&]H©ýN ïÃŽ¦/D” ®Ï¶Ž7Ó¡Æ~ûYžûy{§;+³3סÏCG±µO[g*|… _Š Ña€ÝEO-±¸#wç1ôr?µÜwðM@ûOZÆî®“ËÑ3nAÌ%žºu 5ëUW‡ž'³ŽîET*ÇÒ&ìÜ´Ã…hÎ~Köv£‰ˆèÜSTWnhå¾…;’ä¨Pdž:Þ€¡µ¬ØýŽÖ¤ls;ÛýR!Úv?»òQŽJå _¶U¾hçuO@·å ¹Òþ2d{!㊮‡¬òÏ$WÖ%߇ŒŒ¨Ë¼}S Q©û÷Œ†‹ç^ŠèϳCá–ö39Zv–oøÔ’th»Q¼Üg)µ¿fŽ-±Ì\‡îÎÉžÄ×%ý4ö³<÷Ó$r,»ñ±™ÍŸÀ:¬lL6ž~’5»EôN¥×0ÈëyܳüãhÒUœŒu"«¸mbõm#K³g³·÷ãÉC³WîDôü•høô%˜”Ê_÷Ÿ.ìÞ[=ÖÊ–%6ö$+Ë#å£j/•£wÞƒD‡ód™}Vö¢;C×-ú9ö­Éè/6lùÑ®€®Ë¨È®&Q©|ƒ/•Bé›: 5ïú¶u1'KÇ>†6>ø7ô­9Jކmø ¶ßÑÇ.m™j\c*uÿÜÖàß«‚uœ)‡Õ“ª’ïdébÌ»,mvs‰xUq™”Úßo¿ [ÅÝÑÈõæljDÒØOc?Ës?9_oÖ4¼¡ ì3‰ŒªMV}T…ýhW&¢+¯4e#ù::ôôçº,»²  ¦:±7~³Éâ?v¬^LÒgL}vŬj!ÚîÖ\á×Åú:4ö…- éÓ‰”Ê?w€#{vpªˆþ<1’YûÎ yÿýPµÌÐ96WÀyO:éîü¶ÞÿBÚôú ££¶’‡ZUe÷ÓZêХ뻱 #:RùÚÞzÍ&tdh}í]\½'Ùv Q1T@ÃX3q^!àÅ"ÄŠ:4u»Z¬Q©ûÉÃla½w-:Ë¡?|µ®Oöß~SˆotTDo½h{Î/ ¥ö·\žoJ'ˆhØ{Q>øØDÒØOc?ËS? ¿’Nx³Â•u¨áÜNѵiÝD‡ÎW¬XÄÎ¥íÔp®ûóbö‰¨áüæÌ\‹EÔp.•ÿ7…Mœ=QD ç3ßCPÀo@ çc´uÙò»å¨á\ˆ±a½’Õ"ú·ùJ¹"¸ñª/C ç±Öƒ˜÷ÛÈBÔp¾;ùL2[Dÿö~7Ö‚©5œçºTÐlÓ¢»_Q9¨pý“½"ú·Ïûiìçr?›ogÿ—y~¬¤cd»¦là{™ ½+¦lјě∆È—¯'¶_ב¡>Õº‰¬I×’cìý Ðä†gáÍ•¾ mÒç§Ü«šJŽV3s’WœgÂP©üÅÛ_ Ÿ04cv+ñm)¹¨;caWj’Žï×¶9ølsÖÀ¥+ ¯ƒÝ˜õ]+²Ç¡oòßcÉ&Õ³)…{I©|‹«²Ýñ.:tÈâ†li©+Ù£g˜<"©¹zS]¹MAr·ïsXW%ZDK;_eV*u?+q,ë`õZD£—y³ —Þ‘%•8¦î!/DC-g»J‘Rû/-ÝÈ¢fí´Ô)ˆ™˜Þ&¥ž7öÓØÏÿæ~ž­dÂX|?†ŽX‡EõèNÚ!НjV%ž¹¨#­}œÄ-¿ë1´_«‡…m 9é»@ì=Œ¡ê½á‘þ] }æ4Il½ä ŠuÄ…'[“Rù·,š62aè¼s3Ä7ëi5€UyhC6¿Ú-.#‹,aaË}½wz-KRÉÎ-YƒNm:ë^lus ¥ò}±jÁj/rÖ¡;Ò,ÙÀaÈj½kɦ÷'S[È[Dž°kÄ‚¾ÊÈ7ÛûSV‰”ºÿºË4¶¦ï'Ukf²nn_H…b Û¹‡^ýÈJNTjŸŠ³YVßýq¿;~ã1iì§±Ÿå¹Ÿu«³w­ú+°ësº9ù~l7±Ý­ˆžxéZÞ÷ùZ ú±Ðêm"áÛÁ–¤·ŸJž×¥6C¯Ý‘S¤ZêQeëBÈÉÚ°²*•¿ý?q*W…¡•–nÍãÞjª+½Õ•¡ßû߃1UMIÇBöúì+@ƒ“ÉíXó–½t¨ýõ6lý…Vä³zŸå솧Áálòwv[öOÇM"jóÛú]šEJÝ·3›ÇÆ·þ*¢f}±W©_ÈŽ¦±›cêèÕ›mö›EJíçVaªntKîÐ&JŽûiìgyîçºÄ_Þ¡CkWùm+žWÉöÙ$¢çbÃ?a]HÍéOðö Ôé¦9«™©$-š…AMÍP2ï`MVm¢œ|Së(ô«oËÐ!Š©ðyðI@¥ò·N ÷øzöe¨øbM¹®L=Þ'/ö΃ÉËbÈÑOº±~v ZÃË”­Ëü è³@?vrôÜÖñ|. RùÜ?˜³î{jéÐýWMX`Y‰ˆ:ì ×kûèÐ{KÒ Ã¦9”½.üjy]DC»·7¯i¥C¥înä˺ÿø$¢}ÒW°[¦ÈîëLXkÛU…h•Þ ØçÔ³"*µÿƒ¶5«{ØBDÇÌÏB v“Æ~ûYžûÙsÅ'XúÐcæ¡W·cdv´3tû¶¶ýpÐò~N£çëX³‰Çj0t†okö6¬?ùìÄ0¶zz E+XñÉÛ䲨ö¬Ë®R9Z7Œ•ÌôT*ÿT‹ Q±a ~‹—‹Š ËÉCìxmIš/­Í’²GÉÑŸ«³ “íç\Ÿ]>pZ޶;ÎXÞ×3€ÞmЈ5êëTˆJå+|P…õ|©Ñ1“J`r§­dÆ–SàÓ«ª={ç2,êvYD=o“çÖj­Cc£`AÅ"•º¯+[Í>U|+¢vÖ±c—Ÿ“ý¸Î,ÌÁ\‡î4Á‹›“Rûã ƒ™×¬,µ5ÛÊÂÜ6“Æ~ûYžûÙÌäÌý™èšÙ_asÂFÒѧ3ô ïJÎðÐÉ“j†“ýrk±½¥Ã:éÔSp˜jO†,ZÁ,:Ö!žô`“¿˜‹£æ°²¦ç}_V“u¾;Ÿ”Ê/?2Z¼3]ûOW1»q=òÓÜþ¬µ˜È£ªÚ>ìu·9ºìyVÒì{!ú:À…Áõmó­s9ö<©ÿ99nú#R*_IíÃàÝw¼ˆráqðÀËŠœ^ç¬rJ$ϿȂsãF‘Ït‡$‡=…h¯6ž¢Ìæ–•üþ8û3MÜ#}3gsp›Œ™6‡u½IŽqðfÓÞO ¥ö?²‰Ù7¬(¢mƒ=ØÔ©!jì§±Ÿå©Ÿ†_9Ö¿ Æ¸Á€Îáæ+>õ«PÃy?ŸÓðÝÏŒ¡†óÔµ-Y•Óß5œ·ÚžýÓlƒˆÎ¥ò»Ö/+|¹å¢5œÿÄæ¬âPÃùŸüùÌ?]#¢†óe»–²U'«‰èßæ»7¾Lª"¢†ó.épëp 5œ÷ДŠ5GÌ“£{¿Ý„ìží5œÏ‹¶fßøª"ú·ûG¹›°íæiú·Ïûiìçr?ý~ìfÿ—©¦Nr×ótè'§¹û¡¤åá lé¯Ï"ºyÇJä_I‡*dUXªcÈeyÍØ™Ò¾¤ý&¶'ý*  ç´`KâLÈ—©ÃYƒƒMuèv·@öÓ¶XD¥ò?»×uåÄÐí‰íØ×‘ndL뵌s> ¨·b»n¥"GNø,ïSˇ\Ytç¤ÜÚqÏå }ýŠîY“Rù®Ç¸±'rEÔë³û°œdÂ̓߭Xëc"áW‘9ýXIÆ–µ_ˆV<9Ÿy´Ñ5k°Öª!…è{§kðqýi•ºÿ¨Z­Âx¯/€˜ µº>#>vfN'Ú1´]ûŠìpHsRj¿çƒç`k×WGëÀÎL–‘Æ~ûYžû9èRsà:I‡º~‘CǸÉä«6lዽ±™e»Û‘vò_ò>Èîs þ‘‘2÷Å̳þIòKÿp–c:‚TEÖd5+{éP«u¶ló3R*¿üÔ[Xã=Œ¡ÃþÄÖƒÈd] –é=DD i¶ð9äՃ؟Πu[s<L>,Šb9t€]4‰]STd¨T¾Úǎ³²5"úé{TµÙB 5™EYGr²G$s­ö¾=8ÎŽ­nÙZ‡Ú=ÝÈbžU#¥î_Üë%{gS è^³óœÏÞ“äñ›‡áÞ×c¤Íøj̳ês9*µÿ¡í6±£‰õ]3•9ßOQc?ý,Ïý\ê=t­&éÐÆ>pÿÃ8r‡ý¶ûFd!úD=‘íÝ8€CE³bŸêy"zLÕR,f¢C݉`ÚÞW9´V­ìåìÆ€Öo™i^æ"ÊŸ.„VxJJåo>,VvïÏP¥I ŒìÝì«êÏø^÷Dtq)¬BÌ2´h.4dCêív\¾iJOò¦þçp§ýõýÖ¯7{¹c¾ˆJåk¹¬1ìrØ+¢{§Ë·OI?ŸXæ›T³½ŸÃÜoÄÈÑ >“Øó›Eô×ùs°Är)u?¤š³\¬Ôsòym%iyy<»"4/D…ÑìàËÖ*µÿk¤-óºÆ‰hmÇ{°æÒØOc?Ës?Wˆƒ½=uèªé!æ ùPÁ±Õƒäè„sÖºÆ;mšï ø(ítèåN­aì\ ƒÚ2‡›äèë•Õ˜KK'MËé;Zס¿æ Ð~PR*–÷hùÓŒ¡^®“@¾¼"9åO‹òÏѵ‡£ØÚëÓÈOuöËŪ5j2ÒÚZhµË\Ÿü)DÛ gÂ]S@¥ò½.i"|¶~$¢¦múŽò®¢C_L g#ólý²q#k|"Œ¬ôâ»Ü–½Ñ–ývAîo¦C¥îŸ.ÉMI´x{58AÞï²¹Þq M¯®`6\)µ¿¸µ„¬¿(¢Qwì䣿uÔ¡Æ~ûYžûyôwû1í|ŽvîEÖV‰9çö)D]N>„IDtüÇ@¨u¢©±y54vÉÑeÇÞ‚ßù?äÔæ&lÒîž:41EÇ%-½! ßì'ÛET*ÿ­Nž.» èaã¡õ£\rÅ÷hÖµnn!Zy{k·¼‡*Ü€3^Ký½+ëôÖ™ìWǜ޵Ø5¹÷Ä‹÷DT*ß–±…–k›èÐC ¶÷;’õ¦Îfo›¥:«d(—KÞx×–ùõ•ëPϺ­Ùˆ’Ç"*u¯Â jžÝ¨ÙÕÁð‘WÏtd³Çí&·ý{3ä¨Ôþ ¹Õ¡ì‘»:#–Up!ý4ö³<õÓðëÉù"¸æÚV‡ÎO׸ܼ½"j8Ÿ¿è²üò›”BÔp~7v «> »5œÛº^.t„v:Ôp.•gíeÐóäV@ ç§Ìç³½ïÛËQÃù­â©ìÑó€þ¯?ßêÉlEÛú:ôoóýyTG®nÖ_‡Î÷`k=öj87 ç_NÑ¿½ßé¾L=PÃùÙ^-ØÊîž"ú·ûƒJ«1ï&Muèß>oì§±ŸÿÉý´}}ý_ëü­;š6=œý¹/#?…äËÏ[-D÷ÖöÚQPÇ‹¬¨muê3ò $)Dô«Ýd¶³k#@“*G1ïÛÉq;Y\ƒl”ÃXÎ#R*ÿ¦û^\¶Ùe]è!0;HþªkÉf ­C‡èÈ–—v%®ÂrÖ4²ƒ«$«Iž?w‡‘ƒ¾ìœ™)•¯H>‚ex™24¨·'K¿Õ´™ÒOn)ÚéÐÏÎäe­I›c…âþŽäjíxãMD¥î»ÇV(¼óÒ”C¤~—ÝÐ,ú³ŒÙ|­ÇÐ3ɘÃ.+R*_¶—92Ôqî öt¼é£ì ÊnŸDôÖ}[Ø{©#¥î{®´“»„8ËQoM²¼Ñ‹XrGóxH~õÐW]ËÍúX3Tj¿Õáe¬Û€©€ŽÙÓ†•M«Dûiìgyî'¾€½ýÝÐØ6ÞLákNlÞ þv¼IŠoÔâ‹ùEä£ÜbwûK"jæ³Ilz£‰­ÛÙ‚Õ9P¡Ëß>„Ié¡€ö9À®Ë³ QùºÝÌkÑ@¥ò?ãÃñ¢€>ÝS¶™m‘£›t«áôg¹ˆjß “;žîFzN[Ï:T©ÆÐo3ÜØ— äkëŸðÏ`w@wÎ `;o¢RùÞkû0çe“º³Bv$ñ_'Ø6ÿãUEÔ3/Rþó~‡šŸ»%Î]˜ ¢QozŠý¶UÓ¡’ý\õVžh[$G­4…+*Ú5pšxjDs†öÝ}TLxX¨ÔþÉ«J¡èã£BT•\Ÿµm©Qc?ý,Ïýì®ÆÚ•~–£ëvËY§í{I‡Ñ÷ÅÞ÷]VôN<4«¹¥Ö„ÂaƒûéP³®`Ó‹Œ{V‘…'fpè×CØ€†/ ÑÚ—º°²þýs¶>š·UD¥ò79{G~oWS@ñ—§ûL µÅÂðW"êÖLì«.&?¶nÊÖèÂÐÏÍÂchH6ƒÖt®7 ƒ­°joRùšËê1³C^ øç ¼²s$}»Ô+lÚë“ý0·¥ýÜ Ðµ[vÊ׺Ñç® /{%*ùù¢}8äaè7Ë‘ÐsºœRÿÛap–Ó„/ÏÞbǾN–ÉEoËYÌÀ¾ŒŒìêÎ4gL†¾x(MJÜéJ*Ý¿mÎm⬽} 8[mö)^£¯ BòV§mÅgŸµ*í[»#f^Y*“ñ'/€é¸)ŒT÷SÝϪÔOÕï-«ŽÃ»0Ruøu ¼¶ýHFªÎS§–±¸Š2©:ÏÙÕ]²[©:/w^ŠÅï58©:WʯÙ~;?{ªó³à´Ãž“ªó×¥ÉXÙü|©:/ýóö¬æ2ù·ùF9¡“ú©:/lqYÜÌe¤êü¼O¬T>BÉ¿½Æ3 V[ä©:w>u^îöõ»ÿÁéípñÖWFþí{u?Õýüoîç±ç¹øŸÌ0®Å“3d™tènÈ'kÂȯe0Êò|1ƒá {J¤ÅS(îÔÉ=¿R¡Ùè/@Öí/†qràæuøºn2#›¼x7{Üv»šáº¦†P)Ü™¥Ì¦µ9¾aMX~“ZÂÅ‹‡Á»†Úœ›à³ì·1rFàn¶\»1’Û¶²Jç‹@–t)€ûmÜ…ûýpïk¡R>§Ð‹àT{2’w$ÂÖÊIBÏK‹°òì}F_©ç„ ¥Nø­ 5'ݵGŸÛS©tÿ`£`YûXGNêxÊ3s]…£_e°¦O.Ó”/«#TÚ¯á‹ùM\Yd ƒÝ*í…JïÕýT÷ó¹Ÿ\Ä×5ÉÈ„Æ ù}Ë8áÞ6“ðl×Qù(ØVŽ(&ƒû_†«ºsÌHt@ŸÒÛùÐ! +º—ɈI-±f =FZj³£c<8y90ft*åïq×DvÐòÂñ­Ò÷Ÿ „ÌoÉdCù|0H’ÈÌC×äß9K€,œÚrÛϺ}ŽBŒè„äë´8¾IK¡R¾¨©^p{·?’‘F¸¨Ÿðxl~²™‘Ñ;°eHwaßA°k×&aâàêRÚø~œTº??«¿üÅ¢;'#v´”ôú bŽK)ÉΌܽ~“œê<H¥ý½šDAÇåŒü°nÌ(îÃIu?Õý¬Êý|ºÒ„[Ëa䫉Füvê aÍ]ÆxQ«®ÐhÀ'X3z³ð]šLl ¤wa ^Â|¡Ñ9ž™¥Œ´ÚQm®wæäðη¤¡ÑgdR÷R»/“‘Jù=C4¸iæ¿:Œ j;îc ï•H³š<ö´®+ù¦ètßsÈ ? ñä¬zHn¿b€ï¦p%!/ò$‰TÊ7hð{é于HÖ|©ûnÔ®õÚ†Õ/ÄˤAZš6zïJê탶MgqÒ$µÞRM¨øûãÕb£~ƒ9é SÜñòpá4ƒ“,#;È-vZ|Ÿ¥L*íÕö 8„‡rÒyS4hê%T÷SÝϪÜÏÒ7Ú|íÁÛLøø;³*y)ìÖå)\tü,||óàÜfœüÈÖà’ÚH~8ä‡?",…pÂǶõáäìúþ˜~²0ÿFŒt%ÇK3+âëía¤RþÝ P+ÈÚuσã÷ ÂÝŒyûfŒ´ºÿ‹æ ‹ç¼‡eÆÝ ;jŸñÂøÅøÉ´µ°ßJYK¨ØÏƒÝ`’å[ k~_gn5­vKdVù¼Às…/Ví›ùÚŒŒòÏ.>mvU¨tß¿ÎPW“HNV7‘½Æ 'öÙÅp×YFšÔŠ-žü\¨´ß8¨Ø¿`d;3™ÞV¨î§ºŸU¹ŸÛ/Ýg5¾0Ò9Ÿ±âòÂö•¦x³Ü“ßNvEóŽÂ»A†è÷PÉ!‰àrË @Ù…¡ÕåÝŒìºÈ¯Õ¸&“éï Ø©ÞîÂ7wÒ™k>'•ò×t^Å~ÒzÃ%hÎÉÙ=RسøwŒôl&_ü¡ÁÉìærõÛnHÎ82 ¬#¬-¡¶Þ™\€]3_&•òÙu¹ggÇùó‡6œ9FXÛÔÍ'—¸’ùLÇäÉdÜÞeÒŒ)“89qñ5ð9J¨t?Ðe™4¶õDNfÜ8)5®,œzRæÚ5gäE·XÙzaJûÛö̆!!í¹¨B>ü~+T÷SÝϪÜϦ'6²ÓÆßº#„Ý\ö^ø]ohß’“Õ­¢Ð j§o¨„îÍ#%òÝvÄǚ׊ÉôÐ;Ûˆ‘Ç¢·Á™&…e³c §¥'Ó+vÊþ=·‘JùÛ…ÁåHK${%¤€áÚŽÂþ‹ë@Åü[Œ, ž ¦K…ÙÖÈfŒÚ ä´óF®›5‚„Çmgà€§ºHFëaNP!Jùº%·Eû@†Üëƒ¼Æ ¯v~ Ò¢±Œ¬qà)ÌyHØ®Þyày;…ñ©ý‹7ÿ’I¥û¡+4!úÂN6Öµ†Âe!Âj o³õ= €\«kÊÇ-Ô*í¿ÙÐÇûprV\) 2ªû©îgUê§êw)¬%û8©œ‘ªóö>qè仇‘ªóàÈeسéN‰T;ŽØ«ï÷å¤êœŸlÀˆd¤ê\)ÿŒÝ~ uGBRu~ÂS‚gWȤêÜécÿ‰Ru>òìhl•éÉÈ¿Í×"f>¤ê\÷c-ìQPÉHÕyÛ`‰³¨ÅÉ¿½oþ³/;–ÉÆ›2elÛ™³F5všˆäðæúÔKç¿õ«Ù¨$ ôÍ‘TÊ·Ýh,iÇÈ/Ï[BÆê'2 ‰¿¥Yߦ0²ë„jur‘L®¶¬ËOÞÌÒ³Ïp¦ýú¾L*ÝÏy9[÷ád÷²0Ì5$<±þ¼hÏÈǶ1P½Ø“JûKr^Ã׆iUâ†Æ¥Õ9©ô^ÝOu?ÿ—ûYR‚mú6æd`ôdÜÞOO˜àÜ–ßñU"ó.Øs,+&“;7Äïý­\Éâô`x~šD¦»èa×ι2iìí×¾bäîÌJˆº:]&¾8bñµœTÊozh)[ûº'‡ß]ϸ4vø#¶ï¤qÍ7’}dá„u ï=$óºçC—Y@j¦sø1ÒÉÏÝtPÿk' ûñ"Ç5áå!™4sÑbõß?Nîç%aí×im\(_t_äO­áp7XW"íJæþüH¥ûWFàw§þœ´;»û¶ï%lxDw$MÚ„`Ä‚±B¥ý›®Gßø F®wÍÅL«®Bu?Õý¬ÊýŒ3¶}aä×>C±—ÞáØ&͸vBá«óïX`ÀáâÛ‰ø2/ÈC )XwlC$§Û€M+Î1Ò5c/f ï"œ4s;¦\ÏÎsuÁWëdR)ÿ±%ëX«ã8ùM3‚iey :솼Gìëü2ú7BrÓ¼¸È=‡¦aàóBϨd´sO¾™<Gø: ©”¯¿Xæ3¢ #»ÂYö{T¤°‹õræ7ú Ÿ/>beŽ »Ì2àëVGråÈ|Zù?©t_kãrô¯ëÆÉ”1Øá»“ð©Åüg£Ž°‹Iüà1“‘JûCO% ïH{‰Œ×Í׬r%ÕýT÷³*÷³\ÛÛëcdí}Ž“Ÿ'4ôŠe™Ë& íŽnkÆ5~¬·m¬]‘\?Õ ¹O3áÄ©XTc¯D¸3cµòg›àn݈“£®Æ`­G5…JùÇ=ò`..Ã8™´ú†lk5Vh}ÿ ·ŒdÇ€7š#<|= Óõõ„†¾q\9 §¬= ÍÞòüµŽØ¬ÛZWR)_¿âO¬ÇÙ"Fo®Å;k~ö{jÂsoe™9¤ sn„°< Nržb&‘óV®«ð€Tºÿâl,U³ãäêF«0®‡•ðØ¢vp¯ÝzF×Ђ;Í›rRñ߇)ß`cú2FÎqÖÂÓêrRÝOu?«r?nŽÖ›©•¨‡1ý…6r®diQLžój]ýÍ€|¼Xø¥ ]»â»~õ‹I¯ËÕq¤ûáÅúÕqÒƒTF.鶯-ÚXDØó´ô…‘Jù5>·.jÈÉ3…÷¥ÒÂç4aï,$5ÊçËu4û gY™ãl¡vü6ßÒ]X'b–5|¤A…/úï–TÊwþOuîêÑ“¾ßŸ³ËµÛ gíµá>ÄHdI¦1ß6!C&ÓäaÌâ“)’{M¸îÞY@*ÝÿçÖ*œSј“_j­Æ¦:FÂÕñ_áÁ¶Q·ÐxpO¡ÒþÒâÁX»Ü’“ ›­ÃÇ7/2RÝOu?«r?Ÿ­ýžë¹nña¸ð2Y˜|³-<}›d¯_ÒÉù…Õ÷²&ŒÜi±M‚z“ô¼±yâ FÞ=ºS“ó„¦^èkèÍI8°s[è0R)ÿŽUc`Üð¡œôÉÙ·öõfç[³}}$ã'±ß×œØ ´žê#©“ä Z£²€ò£:º.o \—6ÛYn”I¥|Ñ•‡ØÍq'ÝïÎa“ÁM¸zðm¶Šõbd±ýT¶¨n¤ð“¦kRLži1 fìRéþµ­«0åœ'eÅ᛬JF~_=í'¼†ÑÃ=Ïâ…JûO8mÄßÞ2¹aØü•ðU"ÕýT÷³*õSõëó'âjg2Ru^½Y¢|#÷ªóy-°ODªóZay¸îrw™T¿³ýÅ÷:2Ru®”?müødÔ“ªóˆEEŸëÖRuÎF?†©:é³ gTÔFòoóο'›—vá¤ê|U–…ܾM#UçüÆaiãR7$ÿö¾«i,¦ÇÝc¤êo,ûÚ+4¬  k„Á!å×Îû–k€qùbrls6ßý Jù]Ïß•»ìGN™À–O ¯‡U—î 7¤ÃD/a‹¶7áȫ߻ïGA÷•L]Ò [Ÿ[ïJúÌ_žj!©”ï¡9°ºee2 qË1ÍÈá;g`ÆëœŒh¹¹–°ÕùWÌ㎡p¾§«ìæ+Tº?a·9¸ÿÓI÷Ž  ÷D.Åû‡~H于u1|p/Ri¿góKR“6‘œìQW›­˜$T÷SÝϪÜÏ5ó±ÍÉîH.ÚÅrÓ%áÓ/0Ïñ#í?6Æ£cêqròžpä“L®Z/ÍO äæCòƸΜ̶X«’& M†4çO×%2RwRS¹`Í¡R~ÓûÙ×Kñ@¦Ì¯duäÕ¸€_0²Õ‰|sÞ³µ¬ ;hÄêþ$oxÁxßzB[˜}:@ØÊ :^k;H¥|ïô³XçÐcŒŒËÏf_Fk–[.2ß•‡$R÷|8¿ªûôCýµ¾H*åË^´†ÅÙtá¤COöaÉHa÷«3±uR‚LÞþÙOKÞ©õšµ1¼ÅHóA¹’v‹þœTºï2m) k{È1ÉpøB‰p²a®ñx$“»fàSÿ @*í·x ¯|‚9²“&øÆµà¤ºŸê~Vå~ò<=>ö~C$»Ì0惧ÕF-IÁÈôvœÔ8•…w®éë71äu’þaäž6…l¶F;NêëÔÁ¡ÇV0rúS+(Y|G8bñ)ÉÎ aaöbVm‘5Jùogwá_LŠ%2ø„Ä3{w*&ÄC3PXÝ ã— ›Þ=*Ÿ×)Èv;¡E¯@×Y  Ú#iÔÇšöR)߯—[å)frr˜é@énèBὕ¦¨]Û‘wç|„# „[†–@œÍká—uZ¬ë@G¡âŸ…yðfX¿‡.u3…?0äG²°gW+<¡&#•ö÷´Ø uC#9yXßíG ÕýT÷³*÷sK†|Û¦ç@šø›ð¡eÇ…u›d¡ßüýŒÜ1!‡Öé"L¸#û>mÄÉÍÓc$³GQŒ¼=ÁÏ5týN†>˜ îË„ã{wä›=B飹Oj3÷ŠP)åíÎ|Óà2¹¨—5’ÀH‹ {0rÔt$½n¥U¤°¦O$lÝÛDîqMºÑnpÅâ8ôßL¨uü ‰ó*åk|£œœ:›“N/VB-á›[Ðl³¡p‹æ+ËòjÖâcΓ?8 *ÝOØyLÚ'îZ ‹þ‰îûã‹—C3råÅdÌÈ9 TÚ¿«x .jËÉ{‰Æxhy#ÕýT÷³*õSõ³_‡gÎØ¤ê<Ãk®i!“ªs‹1ÓäÍ÷ÏJ¤ê¼E˜)ÚÃ$NªÎíö (–L/I¤ê\)¿}WC>ªófFªÎM×çƒãÆS@þŸüØ8©’ªóf df$›oï«Ðp—䥹%n J[F ©”ý"qg==$wn†AE¦ÂŸ ²a¤G>ÖÄ@·ÏH~6Fìk)|7;Rêü`¬Péþ®¤õgùj~©ô`t´0hv)ì*Ø'œ3Ñ×O다Òþîz…øG2òŸ¡ hÝ7וT÷SÝϪÜÏ’úÒËã8Y#觤 ¬Êb.ígäÞ‹/äycÎ ¿:¤ºÆNx.“kÝê±í€L|»]çzs²ïÄÍ8cü F¾^jŠW– ïú÷‚Œ6œTÊŸµ½Ûµã¤çô l5¼ž°ÈÏŸôɓȧÕqÙËá¸0*ÉIò°~ÿTaò½êà2¸…phåøö~$JùF~±ÅüâÆHv8«ƒõoÕ6xsW ïm&ÜÚå»\ÿŒ¥°éŠ,ók s;ÊúÓöH¤Òý†¯Áܳ @¶òìÆ›³„1»Ë Ë´x$5üÞ;—O*í^í¯“ɸÕÕqÓ€GŒT÷SÝϪÜO¿Ïžà»~'>FBÀ­‰ïL¥¾#·12©ÓU©Þ©¦ÂŸyålÙÖ¥@F,j̇;ÛÈäï“qÆ—a£³«ã­…]„ b°2ц“º¡ãÐqÏC™TÊßþB:þzûš‘ùy©h[q@ؤð,hvS&/¡6ö=šÃÈÍeŸcÑìÇ<õ#û`É^á­é-/*å;<÷1ôzøȦþס^ý½Bw—"ftª’ ¿ò¡™·Œ5Ý-ïÓEßY Tº¿pÓ È]_䆱pÊï²Ð±Ï,¹àÐ~¡C“å°<ÿ©D*í/Ü&áéí8i²i N~û…‘ê~ªûY•ûëŸG¬»r²«n l+¬Ó,HJÎ"‘ Ÿ—ÏÍÒ×;6ºÏKFv­õ@ngÔ“.-oÂûÝŒ\ñ¾;®í[Ÿ“­N<†÷û» ïYÆ ^d¡R~ƒì5XjÃÈ Ç#Ð<Æ[ý³7>ýCøfdþÚ—#\sGÆ(Ý2´m/Lp¹äþ²ï0|²+’É~Hß“"„êŽgÆý …Š?ŸÄwÄÁž½%R/Øçö¯t%mÓŒù÷ÇÏeRÿÌ%6bH[Föù2 ŽkrUúvÂÙI¥ûÕj€E]$t,“3„-¶,g‹æý/cÁ_ê •ö¿õ“ð Å4 kE;bÊ&Fªû©îgUê§êçµ2BçÝa¤êü¶½3/j¿HÕyú¥<9tLŠLªÎ÷tO¯uT›uÜ‹}u8©:WÊߨOcÌÔÆHÕ¹í·‰Á¥g%òLóãà dVÇÉò4»ŒÞÓ›m»eÁÉÁÕ±·‚‘LðÐá¶§Ê€Tº¿øÙJ¶¾ËUFÆMÝÂl¾NÞ}Q6~9IƒsgØèÆFB¥ýåñëàöoWNöŒªóŸmg¤Ò{u?Õýü_îçƒWìõò‘D–¶ë‹woµrOÍ)øÓ&‘ÚEmñĦXáôA8æpCá£#·à ß-œéªÅ'­nÉÉn›çÈŽ>32ýÕ2ÌûÐIסSÁý¡¿P)ÿD÷Il‘n[Fž¸žU{¶I8øš«1Ò“—zËíܧO•ÆÂÅH ¯Ž‡á‡ž u£#qÕ$ƒŽ™£O^@*å;x³ê]rs|öÏÒDò¾ýLÖ'|'{µ1f[ç,jx–"*â€üúe6ìnî¤ÒýC7þõgA‰&'ß_/f­ÖJN äxžäľWáú¸zH*íÿ§y&“ôémY›7LÔ㤺Ÿê~Vå~ÆûÅÔâ0 ­õý1UÚ#¼ü½ Y-) ZÓää£Ñ¯`kœ¥ðg¢v7ê$,|Ÿ&==TC&ï÷`—‡;¹×;ÞÌÖZýº 5uƒ‘TÊÿµ éæ¾gd­I¬ÆaKNÆ4Žë…Yªáµ‚uÂ)Jºå„d¾÷w–ÜØUˆwaû™¼–Ÿ„‰«ÌTÊ盃ñ>FHÖ]ãQ- „íêºJfúë9d½<Üâ…Ö§Øíæ6Hžñ+cck1Ré~{»Ö~‹'ÊçØÚ7öÂ6CV`8’›wg¥ŽB¥ý‡³ç±¸úã8 DzàZ¹·PÝOu?«r?cÐáÏ3 WL¯ÓŒô×^v–Žœ|Øí:Ä.!œx~_pž‘Ú¥;°°ÕV‰|Töž½» Ïȯ…ìÈ*{N~šµ“Ù”û yqÖvØ6a#Jù ¶Î`¯æöç$\1dN­f7×kͧ„{YáÂD3á´Èwfò´n;þ-ÁLÓý<Ä& _UŒA'“jB¥|)uíq•ï Ïm4ÀÜ#ù—Ý~@é˹œLj†óœ\„Ûv–\ú'ʤ÷™5¬æL-$ÿü ¼À·r㤵Õ9V^ÓK¸Rï<Û˜ä­ò›NeRiÿ³úðT2FöÓep§†‹PÝOu?«r?gFû îÂöHÆ´ì…¿tú ³õ[¢žïxNÖ´Bßý}… ÌVâÑØB Í5=qºîxáëߤfs²mÙièÖÜUXj#U£áHžN3æk gä­Ïߥ7C¶ ÷oÎe^¶‘k\õ¥Øqo$²0"ßM\‚ä.›,è;ÝC¨”owÅ'h#ò¡ögh0v‰D†þ²Ä¡ß2rqH .4ˆÂëGYñ¢˜%7XÃH¥û:'Yfo?N±-bwò' _§µçû]€4î¥Íi‰¤Ò~¯S‡\»^¨`d±[iì»…œT÷SÝϪÜϼÛ8íÖx$Û,7Ã. „'Waá†úœ¼’˜Šž÷72Òq­„¡¯œ„Ý—FàÆNß„Õm}ÿõsÕa›‹Q,+c£pZÎZ×î-€<}äË1ñBR)ÿëK`þöUœÌ˜Íauø aÓFÉòÒ«™¦ç!7‰ïÂÉ+•Žàuý2ë¹Æ70GrÔ/|gê ä“ï}põÁH*åk§Ç¾ÙȤæ¤6ØÎµP迳šL2dd­Œ°S»•°ÑŒ>ð ¥£+ybJ³iå¤Òý+ÇóØûês9©¡·‰[²DøÆØ–g6o†¤µ›Ä57RiÿK åÆFNnè†ñã‚„ê~ªûY•ú©úÕXÛE#©:·ªŸ‚l,“ªói‰»±[ƒ2FªÎ<ÿ¾–·à¤ê|Oô{–Ýo¹LªÎ•ò›e½Ó/Ã8©:OÕ¯»Læ¤ê¼VäNVkPm$Uç×F}—#ÎIHþm¾.âR½¥2©:¿ÝÕn_ÜÄHÕyhš7Üñž‘{ÿÙÀìÞ¿þÝCªÎ [Žg]c/Ëäßîo×ó_bäß¾W÷SÝÏÿæ~έQŠÿÉ›3­YÇçˆdïÜhãä$|Ô׌]÷¹ äãÕìñˆ2áØ¢\4Ì»\DZMKD»«·ìÜË žÄ.æäáæ82m„pô´ÓFxfþæm¤%TÊôân,]…Œ<×á_Ö_^DÖpf™ëv2òŒÝ"éã¯ÞÂÓ/±Ï‘´_žÅàÂ/ MÞÕdzó~K¤v=L|9I¥|‹¬—°àyŸ9ÕM—•}Ú/ÔïþœÕí`€äæFOÙ¼»€LuŒÃçvs Æ}ý¦!©t?ÔPk_ çäÍœæ¼TøôÈmh–æ(<Òb xÀH¥ýgÊ ðn݉29*õ D¼ßÉH¥÷ê~ªûù¿ÜÏ^M²y+[#é5û {èo,Ì>ïÄ~^ dÆ)~-t¨DÆNôÅ1 ž6¼ ~öº(‘æË"ñÄ?Ç™–Þ»Ì{/“Ï^jÀ¦¦k8Ù™£ÝŒ(F*åß»,í;i::.Þ#üÙÀÎ;¹Jd¦»¥´©ÑX ïŽwí[&¬\>ýS…¯ú$±»;‘4ðørçJùÞ¼L”×'¹0òtƒmòóÔ|2-$ÍÝïä­‡ÏäU#Ý„y½OJ uG#¹½¯·ürx/ •îoÚ ï\ÎI÷0'Üc¼R>ã–¼ác#O´ø*u?8’“Šý¼Ö3ç4àdçý»qO§“ŒT÷SÝϪÜϹï´xb‡@fFsÛkÂ-îc¸õ¸ŒõÖ…»÷?*læµ›G µËÇûKc„wÃ?ÀF»ÓB×µQ/"ˆ“Í4‚ù*cáíÓ+1âÁ"¡âÿÿ/+ñÈÕJ :ÌÀÃÁFHžïÆæ4 ªÎuŸʤkã2XxÉÉÔ;Õ §¦‡°™ÏK˜ôvŽðc¬Ìn]o*TÊgØ”ösÒ®m< Mô/”ÔL:Üóï§šHÖÙyjl|äƒeµ\ø°x$•î'´ôÄ>Á±œ N†[výÛѳzc«‹…Üv%.ŸÝP¨´ÿðæ£ø½ÿ}‰|Z´÷oÝ ¤ºŸê~Vå~Ú&˜ñ¶c€,ÜjÎÖË’ÈÓw~2ƒÏÿ02`d(˱Ú/´ér ÝjdHdãsØîñ[ G/ C÷‡Ë9Y1$¯Ô³>Þ«gÅ1rÏü› ÅûrR)¿ãñ>øNnŒ¤¥FkéÕHÛ¶Ïiþ‹‘C’³’[¾œ|Ýg'3±¶EòäÁÖ¼àY:S]^È -oɤ~à4fÖER)_“‰ÿ°Â¶ù@NŠ4á.Òÿíä©0xz7$W5¨½®Ìοù‹õ¼å* }¦%ïrRéþ·ˆ‰øåîJNvÜ>ÏüZ!ÜyÒï6ËcäÆhXTnÃI¥ý=Wx`¶¯q1yÆhÞåϩŸU¹Ÿ +ÌøÐEcd2s­1ŸtÔ…‘VÓeKsá»å.7]wIdh?,\f€d¶ã-¨¾È£¸˼ìZË›5÷ÉŽË·a«»QŒtë¡3Ç©”O¥¾‹×CòÍ®òÈ~Ý[³€¡ó8‘+y½õ>Ó±áß+¶Êä‡gYå—iŒôX>˜÷ã_€4_Ϊ]ªÇH¥| ç;ðåÎ>@>úäε•„+âäãË‘œt“t"„Ǻî…ë5 „þ¶gÙˆFB¥ûæ£"1¶É2NNà+p‘g¤ðñ§~q…ðšË|\²~„Piÿé­›ðØè»Œì’‡k4oJ¤ºŸê~Vå~ž[“ÇZ%3²Ý§g,¶×iá‹}q²e—5@Îp™Å~N9,\^í¿éI¤ÎÞ±84– 5^Ý€ðÂF>>ç£/ër2ï:<¸]OhªW€oÇÛ©”é OpïŸ}@j}ÓÄ›Ys„ëz6>^›“áõ,ŠüÚNcdíÙeó ö2i^¹DÖx³ÈùƒJî;…{£[ðóÆ eR)_‡¯7µ3gdÍW–üôøý Í^±ˆ+ƒÔxP‹7§/¼aÏ/4aä·ifÌÊ©¥Pé~îÈÕ˜Ø!Œ“M¯Ç¾ž3„õ^Æ až #µè„7 žK¤Òþn 0®V${Ûk¢Å ÆBu?Õý¬JýTý68ʬoËWŒTGyÜc/ÒÖ©:ßR{=ŽKÕBRu>ðå.,,]ÏHÕù‹Î•`Ö° ªs¥ü-Î5Æ´¡õTï)ZÇÊ}žÉ¤êüݶdvp“9’ªóOóV±‘_ûpòoó=JýÄ4šhqRunüû‹x¤ê|åíÖ²´:’{ÿåã$Œ­ÈIÕy·¢:\ÎÈ¿ÝÿÊI§\Šòoß«û©îçs?åËeøŸ\ÒyÞv.—È×Ö[Ñê|K ½kœcuÇisò–ÁiÏ]øeÆ Ü–äŒV Ø#s’w·ÏÅ®ÇÝ„Iw ÁéÞ"¡ßö.P|4“׋†°c§R©”ÿ¼mhçö‘îmQÚÒå•pÍHLy]"‘[Úxá˜p “òGã—ÜBÃCðEO$Sšm‘§é-àäŸwÕ±VMK¡R¾aRm~øÛ G›6á§"­…~ŽúåÖ%IB¥| 2ŒùÂ&2¹ìÂgöÅx:#ƒJ~ËñiBg¿RiÇö±Âk³/céå¡Edç?KPú¾H¥ûÜbSÃ…Hò®h.L:Ž|øc Ý=eŸF×â¤ÒþÚz÷å>u‘ŒíÈ·?IRÝOu?«r?/»à6-s mnôÀ %eiðý1´ýg'=ž:cåé•ûaú\S$ëÄÚ îÌd 7Íî Kwä k.ÜQ0 É(‹vPøñ1#¯ý‰Æ—sR)]'æ¦ãÁɧ§ë1÷¢±ÂwþúXÖ'ÉØ’áì±Q8àÎ5ö6·Èsãv²sƒô„¡»ââÁ)Œ¬¸”¹¯N•ò5i¼›î³‘Žó=™¦w¦°¸‹;ôÒH&ǽü%%×|-‘©mûc·×yŒÔüç–I¥ûþóíÐ0} ’£O›ã˜øëé>N4¶ääÖ‰ ‘ËH¥ýššÞܸ÷jFö®^Ê\‹Þ ÕýT÷³*÷³(ÝÓt %2æ¾9úÍA™,7´Õàdý±Ã±ñ×Fîø3m¼ äôÞÿú;ñ§’=ZæË' Ÿ†VãFã„¶‘qáþxFú·ÒÂ?²7'•ò÷zÓ_λ¸˜“!ù²4xì ¡nãÒÙm‘,Ȳ€¨AFÂi^mä”Z@îqÓ„Iöï„FåëñªíxN®ø¾ -×3R)_^BŽk„KvöB2ùL ì(l Tº?æ—>èŽdøMlqn°ë£éÌrG*#·l~ÃøÍ œTÚŸ_xWþPcL®¬—Ǽ†ïRÝOu?«r?Ÿø6D®¥Ãȹ£àÌc£…×_tBç鵄­×ÖÂÃñ„Úæ@Јp$ã’†³KÝ…1—XÝI@ZÜ,-Ü,‘nDz±šS¥Lâ‘há¾H¥üú=c 03–“«_½¿³Ë…iÇŸÂz'ìð£ Z4}!Ônó®Øa—’¿û”±6ú‹„׆ãy§÷ŒÜüd#=þ*TÊþzºÜ;û²Dî˜×šù¥ÇÙúÍ fñ̃‘wuµøçQšœÜc<o~.òR¿8ÌÙ…¤Ò}ëmï ©b’ÆßîÀë`a×›çœ×Dl⤶ó}¸ÿ°‡Piÿ '2Ö Èû§óï0RÝOu?«r?o6ÃމŒüZlƒ>_ö ¬K!ýýá»eWÁí5'¯Îgy[²€ÌÙ•"WÌ·Ö­¾ì–m$½‘ÛM®…¤×Ñ\œ;§##gÄgàWßVH*å7o…3äœ<õzNÞ;Lõ°Z÷è„ä±[ú¸êtaÿÆN¼[¾“0¢mn<' ÈèÅKðyf$§¬mއG©”ï¹ÍNæñøƒ>ió!…÷…ß°ê|'Ýg1»¡Ë„6ì€ä3áH–Y¬‚kfþ@*ݯåzŒÏAÒá4ƒ~f>Â×ü «íFŽÕ ŸÕ¼&TÚo¾Áº¨¬ +'O·¹ 4…ê~ªûY•ú©úÙ „'ÌK©:ï1×K>sRuþ¡U(˜ÿ HÕùí³íyNö UçuWÿ†ƒ%§%Ru®”ÿü·d?ÚqRu¯ÿM*}:Iչ݋V¹b¸LªÎw@1åÿúYäÿû·ù*÷9óÈeg€T½èàúÆ=ž“ªó½šá«>•@þíý áùdO$Uçáà[m"'ÿvE£) Ñr.#ÿö½ºŸê~þ7÷óÖ”{øŸ ~_êšþý-Æw `bÁ;¡Æª/,têSFÞ¿²‚¹e Ñm%+ü=Éûémyü‘ŽÂ‹Úá ©½ž“&† ‘Ç*áÝ>YRÚÆEd€÷SùzøN*ån¾·Ä„"y¬ó4ìk„<ŒðýÀŽÂ ï:8÷{6'r3\kO —È‹q=Y”ÿ ;Û0v§u3$§F,‘íUg¤R¾IÍ0h¾#'LÑÇ?þ ³Ó,Ðî’’£Ý¬ðÈ”ÅÂa°P£»Ðøw"Àä •îµÎdóÎÕFò®S+¸ÿHûçk‘Å›"yŲÖ Z"TÚÿΠ!IŸ‘?‡Da½–58©ô^ÝOu?ÿ—ûéù6 ìžüRóÉÖ¦6’-sš³sj0²Á¬þìÜ&] Ÿf¸pkmáÞî;˜}ƒF®M‡«·[sR2¹ Úk3r…ó8ìÜf,'ß”™Å+k¡Rþü¥qjýt$?™Ù` Û)ÌoÕ çv¯䯒…øÏÕ1ÂAÖü‡&’ºíÃùÎ!@^ÓIgV7 Ô¸„¿ûfäB*åû=°:ÇÞdäî&vØ#«'gªŽç¦lAòÊøe0pÈ.aÊN8ùî|á”& ¥‰}²…J÷ö2M—{@êÝÛÍÞqaÝêV¬×W$K¦¶bÍvx©´¿fÊ%ü¼9›‘ë÷\Æ&WF©î§ºŸU¹Ÿ‘OnÂÛòæHöxôÚFw¦Wœ`G®e™Ñ 9®¿Oè+ëÃлy"Öz®ˆ’ɯé:pjÃ0NŽªó\¾­®uwAãÐUÂEwÆáÁ‘©”¯ß`iœ‡¤ï¤\8ög›0cT8â =¡uŒúVLšÊ?¼ìÃÈ_;ªñCp]8:­Là6TØñð*vëZJùÂnÀQ‡ªs2µ[4.7¸ÁH/éIQ×£Û‘ÌO¶fĶ»!~ýHï¡`RQ‰Péþ±òìTƒL £¶°ßoV ;>a. Ç}®'™öŒDRiÿÊ1ØâÑ {¿·Có6£\Iu?Õý¬Êýì}ø¸ÛBÒÈï|cÁB“›ƒø¸c}9"€§Wß,“ºo‡±®£ºÞ¯›ñY Ž3räÕüòb'^6ÞÚŒœmâ†aËN wt¸ƒóß®—H¥üW&xÃÙ°$$O¨”¾ÎöÛ1z¯ù·­®öd~Œ^,š-¾ÅH«F+¥Ü"‘1!Yìô¶œ<ò¼:®¾Õ_¨”¯MÚFÜŸ4‹‘ºíÀi›¸’!+ÙÁ}Ý‘4XíÌn\{ äº~‡å‡]<,›Ð›_«°—H¥ûg×0Yc*¦m³àuý…kËëòÎó"‘ìdÁc‹O©´¿‘æx,ó]ÅHãc©8Ò»@"ÕýT÷³*÷³ç˧pF/ÉÏÇÁ=y¥EºqIã#;¹¼c¬YN6;݈—ÄLæ'8²Ü¤µÂ/ÝqÀØî@&¼l„MŸ–É’CCpWÖR »&¢ý [F*å¿¿»@:`1 ÉÄ·Ï¥i#ÚË3Ÿn&¦)Ñ,jån }>4eÝÆú >`†5‡ÉäèýœÐüINZvÈ•>.ß*TÊn—‹Ù×¹ùx&k 9múfל‚h /ø,„=0D˜üO*{±™‘¡_3Ùã Å@*ÝoâÃÇt²$¥5kUÝFx>ØFö.ΓHWg¸råJû¯o‰À‡6½ô^V Þu¢„ê~ªûY•ûy¿l4o±ÉÛ ãçzᕈGræšöœ<1n Ø› KRÁþÁaˆß-)lE#û¦D¡å§«Â¹±C€§ð`jG´µœ¤×öp­S%Jù3¶yÀøA5¼Ú6t3Ïé¿ô¡ëZ=Â]­—@êÆÆHÞÿWU6ääí±i2Ÿ%ŒÌé‰î0Rn•›X¢P)߸;k«¹#isn/ñîœtÒÖ$™rî2_ö^اÚDþb׉tÔØÀîtnÄI¥û…½+å=W­€ô7]'wøn+4i¹]¹òZØ0É‘¯>=œ‘Jû õôàú׎HJõ«ã‹U/€T÷SÝϪÔOÕO;Ä_ú½o’ªóä:Ç!´öuFªÎÛÞœÏ~ÛÍc¤êüÔÑËx­þ^ Uçé F¢»Õr$UçJùCsþQMrTžrß¹HªÎGî: »&ÄpRuî~$ ÿèõáäßæ3÷쎞šHªÎug/†ìN-Tw Í]çPFþí}¯Ì³ÅÕýHÕùôÓ§Ø+/Nþí~¿!øvr$ÿö½ºŸê~þ7÷ÓÿÂ#üO~}·ÜUÓÆ‚“ï^µw yÓCЇÖHÒÄÓ™Ç,Iÿ»ËðHà $~†Üaë…?ï†`ô;kN–ôÀ`ÛŒÌ_xN{FrržÇ~È[£ÃH¥ü6•ó0Ò¢'Ïå,B»ÀÝ~»6f\½„7OšHä&ÿ‹8?ð;éëñÔ–ÕHΚU ûo4*åÛ›yfwÙÇÉ’1æVw›pÁÀCð£ŽGgTìä¼[–0ÉW£ç³…[‡•îgß“0Ò|ë(Ñ,lá‘É4lbÔµ­Çïˆe¤Òþw?#ðû½Hš®tÅH›êBu?Õý¬Êý´ÕÆ®LâdO¯ت™)œs¤1 Úd 4o”-‹/12©×2¸6ê§DÞ ‹…¶/5¼zm^¹šÄÉî³ðÄâúÂMs½ð–×Fp‘qßUC¡Rþ-{<Ð1ª\&/lw-9dYSöøî™ìcT“_5]ÏÈyí°ÞQO$ߤ™á»%¶ÂÂqq˜T£¦0ªN5Jž'TÊWà€'±œìÚ ü…ãtqÖæ;@~»y&dÙ#i¹ãûðM‘ŸÁDúg D*ÝÝi<nÃÈôÙ³à`I5aé²ïű™39éß´%N^âóoöë&/ÇIÈ/»‹Ð¯ò¸PÝOu?«r?o¯)ÓÈ\N65¨}í>~³ &Í›Åȃµ·KEÛ £/ aöý† YÐÏ{ÜV¸©¶?muM&Ç9½‚f·‚¹'å¸>hŒ$ G‡ü=@*å//¨©®1Œ¬{Ô­‹n{ílÏ­Ü:sòú“š|öÀUÂþEpH7HŸÕñûÐL‰L6_δcï tM(.®ßI¥|oÝìÑïmkNºŽÖÃFµ« ô«þÑ\U{.;g*¬µ¸;¿vªHÿ¬éû¾ó¸L*åë}j>`d#Ëž°<ú¥ðX~}ÞÏi8’˧õåz¦fÂßÍBä=¦‡YÔ…ÙÌ«H¥ûKÎw„uµö“/6èB¸q rØñÅpý{'KõÐ`üZ¡ÒþiÃb¥†™Çìë± œÖ[ ÕýT÷³*÷sÙöÉxís'—†ãRóõÂqÓ²«Ý„Ÿld&m^­°`ç+žK䢎Ìõ‹’å÷â×u…‡?NÃùqS…å!0uÐ ¡³~Sþ!m7JùËšôÇs›Æq2µË k5_he»z.±¦6Ù)-¨±—‘¿:,‡…ß‘œÝ(šév_'LnòFî}ȬÇÕøåQÍ8©”ïôà½RÆÔ:\¸m­ô„=Kq3Ã< c·µãYµ„]Æmdk#;pÒ‰·Æg– •îç.É—¼‹¶º’ÉnšÒ*ÿ´գÑkE #G”@ô¸AB¥ýóÏ?óßfHêç;ÊÆ­W ÕýT÷³*õSõó퇟DqRuþÙ£º´f'Uç~wôø®ßHªÎ§Ž°ÂÂÉfHªÎÖ°((™T+åÙ,;xÿ?öë<ªæwûxJB"’ J’„sžeJ2% eÈ”JBÒÿ¼þØëÙû·%›*=Ÿ°NDýçSTzþÝþ=5h" Òóy©§‰Ñ«e ýÛ|‹Þë“Q‰Î •ž7L}L÷¹&‹QéyæFÐŽQdèßÞ/úþ¬bÿò—"Tz®ŸÚúŽVgèßî÷ikÌ .zú·ïeý”õ󿹟>÷_ÃòËÉpЦ#-z¾ ¶vᎊ*–<îa'A³6æR«_ btGÛXl½‚¡}-ò¡ÀmE7ýAƹïâÎÍ~ ^ðŒ›û.žæl|*Acâ¨÷Ù Ê_÷„LÈ×ttØS2rÛhnm¹5œ.NfèÂái°1Œ+ú¼˜6õø]žéó„’ø uOj/Îð£ŸÖ-dG_|¥¨P¾ÊÓé' ÎkQtÛ:y¢ o$hÝ¡5p$Ð6sÍá»è(÷öû- ¼†p›óLÄ?ç ä Ý7Ð{-Vi¢1=ÅÁÜŒ¬û¢¯¿ú3´¾ª¤×l®ÐþÔ‚ƒpæ…Mîã wWj*ô^ÖOY?ÿ—ûÝÞn©øZeßÎŒ áV‡ë3µ/ŨåÉ•Ìbaªõî±>l'hs˜töˆå¾zMìËе×Í€&ãêÜ[ÝBj(Z|¤#¼X1€¡Bùט1b¯2жoýÈŸE«¹7â !è½ CGnO†[K.P´!Üšå-9 FË׎`š·(Zd¨@ß_ d¨Bq­« ¢¨P¾I燒,««'Ñš«_Ån ä :9§Šì .´Jã•øvÜ^®Þí2ãã‚æYú’gSüûQ­"Þ ‰¦¨œùã‘j£R¸´ô@QïCO¸.‡q½¸Bû÷¥™Á²¹'ºÔ >F£²~ÊúÙšû©~ô! <¨Ý©mdç¡0îžÚpö8ñEãV®a_Û[0´Ò±x ¡½‹ã ~óM1šÝ+Ž2ôý…™àÕá+EM¯_ECÆ0´}š3X “ BùŸ^ çõ´SoUê`íÁí¶e&Èù¥he÷~`f©ÏÐ…›¼©s·ÜU‹«HnÙdn;C*1ÙïÏû ãâë)*˜/¨‡¤¯úJ‚UDÑʵ‘Ü"÷ 'à C éµD|Üx$wÙ”ÞÌO>”;½ÁŒ^|DP¡û^—$ççS4×¹ŸÄ(ŽqÍÇ}$+·ÚpK êH'† íwÖ¯‡¹ï z{w¬Ú¨¬Ÿ²~¶æ~*§jÈ€Öyéˆ7¯äÎüeÀVÍpgè,ÿLªf¹€»ju Ø'jâ¿Äïä^ÜsŠŒ¸Ô‰¡ž¹íádÚB®ÿ°màø‰RÔ,j tX¶P¡üµòéñöc5›ö‹6=íÆvn^ÅÐÍm¢aôn­ê yðEÍÛBdzQÜíW à\s÷mhüS8…¡Bù†4µeÚŽö¼Úš•Õv£/jBÉvÖÐÉÍ$(T‘ÛC,OÞÛ™r§_Ðf-'ý*t¿Má(É\Õ;Õ#I‰ÊW®¥|"¸_úÂ=4æì¿~ÏvThF’DôôRÈs¢6’TÖOY?[s?3U$ò΀š*m“ØÛZsýsU˜í2e¨}t©ø¸ëeŠ›õ…£I€6þ~G–¨ åŠOl‡¬çº U’êýèxŒŸØÐãa8ÛÏÊþ…&³³(&è5]YEŒÚéÁTƒ Í[[ 7KPѬ Ò"ßLÑ’Ì$²Q´™¡ ”@7‘&AÛlJ‘ÊÇ<—°¦5S(¡ìêU>q\Ñ·õz€îqê ${7+s W`ÏÐz}ˆFÚŠ Ý7PºYaáØž¡A¹é#¯hrGYÇÂÈb@/UÏ…’«™Ú?ç)@ÙG‚*ØXÂÖpS@eý”õ³5÷Ó³`‹dY/C@USä%îj\æ¢ùwô)zñ]g*÷®T‚¶?<ÈÊ‚fº„AQX_@5ÜAn€>÷í>mxïxŸ »ÎéÐâFK@êÈ®Å× *”ÿn~/æ|£#EÕÛ²f…Ü»Å[a­eA_=5ƒ)®Ÿ¸*OG‚éÆ|†N_—qO’¸þ—3aBÑN‚†ßX@Wè~ƒ¼žøMÄ@†~~Q&þñsw˦Cà©yXŒvoªôÚÿø[Åýâ €®?kÈt»reý”õ³5õSú+Sé(Övk¨ô)£ û-¨¶ÿK‚ íŸfmH¿Õ+ºLm*‹Oë)F…ÞËú)ëçÿr?/¯—cr?BUŒÏÑ·ï%èÑㄬêèSÝ×:s®j­"D¼så.Ú3’ž_Ã5Ý•AÜæõæþ  ž]¦s×9u%¯Þ¦¨CÌl¨YRÀP¡üï†uƒ”È2†ŽßMÿ6 !UâÜæüãâaSB¸ƒ¿‡_ß)zr”>tcî ¦WdËïR‚:ïV†’¼ € å›e´ÖŽÃÐ_CùÞþÜÕŸûƒŽz•ÝÛå©lZ$BË Ú’"E@K èüö\¡û‡uúÁÚ}«´Œ²ã¶Y£Ç¦ö™¨ƒg›ÞMP¡ý7M€•2g( ÞDÇMµáÊú)ëgkî§BB-øé@Ñ3éå¢ n\Â"ª½±ˆ Cu¡+¾ç‹ÑüLs6±n #›{²ÉÏ“ ê°_ú¼/ô±_ÉÉ+f‡¸Ç‚ %ãŠÎ*œ^ýý*”ŸD­ƒ³³ Ú±ç6¸?6ÛǶ¼êÍõž‹†Ûr«gÜ%wæ$Sô÷˜6pdˆ˜¡=Û`Û4äh*ýªá@Q¡|0h7÷EOï:j¿%hÓeX™«NQÿçÖ0½O0wÖ£zZø[Ÿ¡· × †sÿþN°„ËeSñÛ®uäÞwùC‡F”StõÕ®¤™>Wh‹/}œFÑo,Ù\¹h®¬Ÿ²~¶æ~VÝ:y41’¢ãΊ¯Íà>r,•M¡U~m‰‹k‚ú Ú¿·¿˜en™ÆÐøI»*κFqeý”õ³5÷³®l0I­žBÑb_ªÎUèš ^Úã AFÐyš€Ú7«³dÏÜ͢Šú'‚¦MË&Ê×rkÝúÒ¶r€º$Cbïµ\óÃÆä]P2W(ÿßd¸TØ•¡ooDBØ©Kµžêû:tÓ`K˜¦Ô›íwòž—RôÙ†ƒà¦2H‚¾ÕMšïL¡èötie† åËê ^?5={Ï®\À5>uÄ÷+Úàqά €ð2ý]! ý=q™¥WDQ¡û!ç|áÉ) ¥Š¡zl<7lB9©ŸÃ}•²*7p…öкhvcèíë?ˆ[ƒx0þ@:bWD»7Th¿­WŒ¼¹Ÿ¡=Ï‚éà ½—õSÖÏÿå~N0Ù •#“+И—aг¸N‚ÊiÞ7× ûh`î0îüþ~$ c7 “ž§Q^Üæçþtø‰­ °îÌÜfl‘ IìvÙ!1šÑäJúLf¨P~õŠtÐM†VÎŒ–ØŸ7àÖ-Ú Ñ¬¨&hÁÅi¤àc wcÔ1Z6Ù‚¡VG‰ñˆ¯­¿ºŠ\ê{]‚îXWL,Wd¨P>ëoG`БeÕí{‚Þn¡Iës á×@ÓvGAgÏ®Q×}š0™[ç ß7Tè¾}P4¬] hÿˆ¼,»G5Ž®ÙÄ}ïAn©ip…ö–:Yê½à8|Freý”õ³5÷3âe¼•›BÑ™ÁP39ûõ.˱W‚ÞziÇÆ‡þ¤èúaòìý ¨æå·’†–dŠN­ZH'¸Laè/­>`æÏýèцê˜ç¾ùÞBMf\¡üuûŠÄZ›0Ôɪ×ànöÐ$nÃÍÓt ß.üÛ˜kIãŠ|>'ò¶À5»Øþ¸14ç¡ø”æ åË»ŸÖ>C jô}+|¸²û>‡@Ù³ý€nYõˆXÙîáêYW@û«¹©_Éü !\¡û¾/ƒá¸b: çW¬ƒª·9Ü·ù9¤ÐQ‰ 3 O»=;*´È9sõ9E'Ú‰Ũ¬Ÿ²~¶æ~º…EÀ™ç):hÞ6°òšQgÉŽ¦-ahTS½=,™»Z2„ØYM¤è ²»’Ô’‚ 4oµ¸Ä)3ôä?aNÎsŠ® íAe{¸ÝÏ;@w¦ÅP¡üÕênÄÂýE3Þ[7ýjîà×XŽ_AÕû°»%‘­ÜýLäô[›¡e}áÂÛ0nºCG˜=r/·Úu7ø]¤¨P¾`®Ö— Ê“½`ªQWjF¢Ixßh@WV§1æ3¹ó¿M•VX´[Ñ¢Uú¯Úo)h5äª-r‚ðǹßÞP«­ÎÜ%Ua¬·­A…ö›gx€ÑÒ€f]7%~—¸²~ÊúÙšû©Ý.rRt¯é~(ÛøƒûôÝoÉ»ûi ]]#½ãV×b§ htsvfP$CõÖ÷†úË빩°À-ƒ{Y~'tO™ èÑæ‘pežW(Uá6±}à!ж¼Ë‘ŒË<ÈU!{èÞœ1 ]jðDüÑe1·ûÐXü-ûñd%äõíÈý~þ1U~)Fsgd@åÕZ‚ å‹« „Å)j‘UvrG»~':žC Yb ¾1#¹ÞVsìëÌ}¿u%ÓyŽ¢B÷¡ßX§]h¹Í Ø)_Ê•OÑaªt E:ž¢Jáã*´ß.0IœÜq  Zk’Zm[®¬Ÿ²~¶æ~ÖÏ:³žQÔ¦©Ú~-ãªlkCž=ÏР7*ößîÁÝeU%Yª}”;äë~R¹1†ûLáÉ â¦µÍd’%A¿™ÇCÅÅ=€Ve—›p…òë¼ö§›û_¡è¥Òs4ê½2CMÓ È m#¸ß¿Î"3 Ûq·-Øz“)júx#h~Lã>PõnÅ€¶ûöéÐq0W(_–Q¼“S´7©‡Ã¹—áMØ @ï¦ÙA§ÓÉÜí¹ôÜâ, J&Ncš• ¹B÷}t5 ÊÝð“´?SÊí?x.³ôÜJÐñû]XTåGŠ þ|µú*Ù(:hli ]¾ð(WÖOY?[S?¥¿KYaœh=E¥çK"éÁ& †JÏ»]²&.Û2Tz¾/jÌÝfFQé¹ï‰âç(¨ô\(Àš&ê´lC¥çú×^KBêz0ôÿýúÿõïnôc†JÏ'ÌR€6Ù€þm¾ ÷žÃbÉk *=¤£´*=ï8Ó’> _Êп½0üÑŠ8¨ô¼(¯øéë± ýÛýõ•Ê,¬Ý`@ÿö½¬Ÿ²~þ7÷óÃéðŸlH 4í÷!™Ô^Í=­´|• Zœ•_¿Mä6È€„µc úFÓf¿˜¨öäm⤔‚Þܽˆ:ÈÕqÃæú‘¹—»꪿šNŽ¢¨Pþ6‹ á^Ä2†Æû[Àž1ƒ¹ŸL†{ý ‚.¾Üb^àF8äÒØ¯ú€~zñ™ªUDˆÑr}ºvÃd@ýæD“C‘ó¸BùŽÍꦿBYF*—;sŸf²^fTãòbæ.&5»Û–ÙZÇÔ­¹øÖ×kº¿ÿ˜èòª4@³BI¾—eqoûÁ¬Ž¹}Š-È„±\¡ýßÔvBö 5 °„Cz/맬ŸÿËý4œ!!rqÃý6þÑ;„»C» .SK†Ž²¨…¼±·(êºP‰Þõt¬þ5ÚFUÛ¦F‰}½Š¢ÝÚçJ”ÒƒúM>–þnxFÑ G—³â K† å7i™_(ú¢‹&X^ÞÃ-xn/+Gtre$XuHã¦ÅùÓÅ9öê‡DÎòw9:­—û™œ讀Ôüš6A…ò¸6ÌêÞ ÐqU@b_1‚ÅÖt•g舩ÖW]®{ø6ZÓqEÛ¾×gÆÿlc¨Ðýýo$®ÇöºýéXªg™Ï]Z5Š\ó‹&hCèu’&w’+´i×vð'õ!E÷Ã`ób®¬Ÿ²~¶æ~z©AÈŸa€Æ¾6€°[6Üg¯€\݃rô€Ó!øü»’ ó4òiéñ\ë§7$ƒÎp}õ¥¡ »pÄö0ä~éxŽ 3¹ú?\ÉËØ]Ê¿íõw2ç±'E},«È¹åÎܦ¤ ””Ù‡<î趘²®#C-M¦'†wâ íÿâ1”~÷4èÃ[’e2„+맬Ÿ­¹Ÿ='9Àœ¨-€–Ï™'gep Úç‘®ÃÏÔûëN’²Ú…»ìÀN¶4Èž{%hKó‰¥èõ‹ñ ÞåC{Æí‡Êv:Ü=ÊAû¤&A=™kÞE*”߬ö$)9Ü@QMypuUbèJƒ¤ä• ýM¯ßSfÜo[²©q'†6;6Hk<¥èú¨dH`hco/°vžGQ¡|š‹ÈôE@?Û¶'>ܧ™ÚD·6›¢‹ë;’£™}ú­l"è;GÐU¢åPþb§º/š§Í.‹³õÕÌflO㺯ÖgÕ+(ªPfþÚÈP¡ýõÃÆ‚Óè­€¶Øì%rå縲~ÊúÙšûI¬@½G Õ}@O|‚;-î!¹ï0œ  w4 ›æ1Môíí<“~Ñ3 è…iòt EKZÔàðäë\ _JLõútó!j;ÂP¡ü}Œ‡‚]Kg†Þܾê?´ãÞnc †ˆÝ@ŒÀaÏf.“Iv[BÑcO>P£›Ÿ¸.Çí¡Ô7nf¯¸®b¨P¾ÍcuiN+@ßüúJ¿èqû?Ï!™+|šºÎªÛ§põûž¹Hg@ç¦ZÂÎu{¹B÷cK]™è³“Y–|4Wù…8+8¡aNjP­ÈÚ若—<ØhPÄ šáqƒ ²~ÊúÙšú)ý©Úý$éÞU€JÏw‹´àŸD3@¥çs¶ÿ¤•ãêE¨ôüqãFhÖª¢¨ô¼Ã.W–—rŒ¢Òs¡ü%Ê;À߲ޢÒó”†tr9~ ÿï×GúÒ[î •ž§¾/¸>пÍ×ó– ;ãÚLP鹤.äòw2TzîÐÖ5tôoïçtfÇU6*=oz¼ƒÔ*L¤èßîèAê‹¶ôoßËú)ëçs?“L[à?ùðFB…µB¼°y§¤Öê÷BE"ø..4è†ôôʸ~kžÒ€« êf<•¾YaÉ¥¶ƒåè~¤ÒØX/‚Æ}¼EöØzmÔyzfÅ *”ßù¡‰äÒàž êš0êÏ“6\És˜Ü—ž-¾1˜i?Óa襽À¿· 'uÛ‚œn÷Õ?ùtï‹v·;OSì*”oWK"i­‘ >ËcÉÃÀr®èMɾ3›¡í=^“ž÷Úr»Œ…¸$;@uü&ÿ!¨Ðý Ùé´õÀÐË«OÓ§v.Ü8µÓpÙnEgõú¯&0‚ íXúGrbþ>àþéÁHi7®Ð{Y?eýü_î§ýíï’w (z*·+­ÿÁ5yp„´ß• ¨•å±{ž'·ÏÀTz÷a'îAÓ…Ì2z·Q]| £zCc»0‚î¹³•E‡à&}yLgÛ¸0®@þW£ *B[îRôEñ?’1 ¥Ü‡´èÜÑË:Fñ¹ñÎûqþðŠÞÆU€Z6Ó©‡·pù‡À›Èw}¬2.œZ.A…òE9—’x×Ýý}ÿѾUÊM~ƈal"E•?y‘õK}¹Q?öÃÌé€Zgh²±mör…î/4;N;Üðeè[…Z82’›. ’׬TTeS/ý ¨Ð~KºÆ½'EÕÜ‘Œª$¨¬Ÿ²~¶æ~þÜž*\9KÑ»sŠ$Ö!mJÉÛ9j€êÆ%×çK šž“Ëœ>½£雨M·¯­º·.FZý´Ôî”p·x/¢“'¬£¨…¾¬Ïg¨PþgÃè:¯BŠ."wèù‹Üýcáuç»Üë)#!,¬E‚F¾§ ùÏõj­dõŒöܣϛßs :ëõ>˜µ~5 BùŽ/Õ†÷çÚš¾j |¬¶á¶<‘~ûNPôΓ\Òs`G†öí9•N($hÂ<êŸðž+tÿ„8’ZMNdh§[öt¶K&wg ¸³‚Î|Ójó&*´¿hõfɱ}çRDý‰Ceý”õ³5÷óÂñ˜Qãä Ú¾±= TÏÝ•ø”4'ž h¡û3rmœ ÷·×Ò`“ïmû'Œ:.û$A/©’ü¶Œ´ùŸ ¤n¶=wA¯D¿'“ ƒ¾Ÿ‹Íb *”?×¾+sÎjÃPw&lÛ^ ®ÁKuÈ[¬CQš¢[ã¸!KM‰£e(Aà ƒ%MÞEÜÓñQ°ó^w¨’=4ÒH@…òi´tƒÐØp@‡ï=O£*AÕ9˜¡§¯ÁŠSæTèþÝŸ>¤weh¥è 1Щä^ÜÇ–~¡hï¾z,©9‰¡Bû½5æÁyÝ} Ýü» žwšÊ•õSÖÏÖÔOéoèà-°'Ú…¡Òó{ Ù%Ö™¡Òså[Á &J‚JÏ“§’¶Ê*•ž÷{VL;?èJQé¹Pþ¾‹NIÊ‹Ï2Tz>øO4Ä÷Tz¾a±5(ÔL`¨ô¼êðúöi†þm¾%:ÃØþ%n€JÏ/†A®ê@¥ç“^FƒºwEÿö~]ÿ{¤»/c¨ô|iÄ!ñÝ–Ù ýÛý—»f€õÑFú·ïeý”õ󿹟Z=ä­ÿ“õû@€í†~z1Œ¼=¹#c 憼Ü/~ç†Ý˜EVL™h‹6).ÜÃm|ò™ì½×‰ æXBáµ01Úæê3ÚÓ5 ªùxSJLd¨P~—¾GIögŠ9DÎ.XË-Y¦ÈD–cU]¿ŠM{q -ŠW ²£"×+{üã8Ð%ãóiT=C竌€ {ݸBùJæÌ #‡ï$¨aOzûf ÷ÝÖ"ªèh èaEjYiÂÕŒ)iìdÁÐô_!$øe2Wè¾èd1±>»Œ¡~õ/Iñ±µÜ.ymÉ×åµ€6%”Óò¨p®Ð~ß¼nÌ,ÀFŒ¦öó—W¦¨Ð{Y?eýü_îg“ Ì{åÃÐu7ˆV\˽åV!G¾Ptëª,(íË-|D+†tÝÀ…lÊ wîó `–fܼ‡r°çîQnÚ RxÇŠ¡Ž¡ÅרW(¿éÈd¢6v/En$‰Ëns£ò½˜Ü;-î«;ÆlÂæ2®y“"(žèŸêb ò ™]Ϫüx†þn9 »‚¹Bù‚½Ñ7IOšw®‰Z+UpÝ®hH7´rRzŤ›Ü”[; ¿S.E/Ëõ…ùW *tÿçžÎpÔ7¡.¾ƒÀàQ0·p÷QÉòa€>.»RñðfWh¿ÚÃŽ¤rÆ!ûÚª}*¥¨¬Ÿ²~¶æ~®èï—34gl<˜ÝœÅ[°nÚÇPôè¦03}ÆuÈâžn$hÁ!mš¿¼+·úô"úöÊ@-×.£:/¸6e@ÐÐ3bÔ[Î ,ÎT(¿ÕE°²ÿCѹ á޹C·\ýBýTÝ)Zß“ÉÕ§@EFÃÄyC4ú9Ø‚tÜÅÕõ󆜋c)ÚÖ8öUäp…òܪÅÞ/³ ¨ßá~,*ÿ†UJOÓîÔFI‘ °›»Cä >¾ õÏ “B@…š9%! 5ÿ¸4K‚¹¯¢õ™}Y_@÷v¼K3=E…öo_3‰4WÄ3´Ô4JåÊú)ëgkî§èl&Ô4[1ts»PJÕ¸²àj³÷by”Ysë¼+%M«J êŸ|.ïÇMµ(ëx¯ô›Ö2z`W×Ý8¦Wes¿Xn«îßÊÊc£ØŒùBÑé)ýðwEòjVÐFI‚J^ogº³nP´ÿ ‰œ ¥‡†±¸^ÆÜ_újÐÍχ{óm4}¢ø– BùÔ7*³ßWoPtLÚ.ž>˜¡q.3™·F4 3‚ÙÎ î™Á)£Kò¸Š·sÈŠ­s¸‚ÿ) óK›º~ø6p(÷å.ìq„£¨Ù•EAn\Ë݈YÛ zpá.² à9Eã þHjÃ*”ÿ(œ†CÏtŒ¤¨¡> ññeâ‹C—2ÔçÓvò³Î†P5ƒ¡^Ÿ]a»!·8à8zЗ¢í²öH*N–JÐoÇæ“Ñ—ò)ZôæSEä¯t®Ðý§=ÀœŽZ½æl®µàêù­âYÜ‹“@ÚmŠ í/°ºn3cÄh½‚'LÌ,%¨¬Ÿ²~¶¦~J«Ä;`‚ÞY‚JÏ[eAfN@¥ç›ú;€nG†JÏM+•èÌ™‘•žG*$Àûû •ž å_ÖùÜquTzîý}Ñ—l¡èÿ[àdÆ,>(R®Ô×ߪ3Ü*weèßæ3,Z uêŸ)*=WíP)Šòó£Òóã¶ñä”^!Cÿö¾o—JHrÐa¨ôܨ*í†þíþ?{fÛbôoßËú)ëçs?¿ÞW°þOf5ì’XÎ0tB‡ ×*[®ËVuðmÚÅÐŒŸ .*ž+—¥ Q[Ær=Æ:û©ÅܦÌÙàÙÜ Ì<áB·ct¦Éob³9ЩIé&QW(Ç3¿éÿP‚¶l|IµVrS«5Yüò8Š‚¹*¥z#ê'ËÞ¥ip/±ŠU™Ü†”K·b8WMNF§WT(_¹r ± ±%èΩûHõdgnòâRòV.Ðó67Ȧ¼¹Ýì $rlª³¢œ:ʦ¨ÐýÌIpTÜ¡*Ò!è«2wÈs_Udèþ£aÌŒ– BûûE¢¡’›Íš®$©O°£¨Ð{Y?eýü_îçwÿú™üÜ|@_ ¦#߯ãÆì„é}ç3tiI Ô5÷ãÞÒÞ —QÔmÀ0òbmg{²~è‹Ñ›·€f‘ ŽËKêW­– j×'ÓäÊ?ñÉ'ztÒ ‚҆άKž* FUÈqÕ± p°é6m¸/ëoIìÏróhÒœËíã?:õ2$hEÂ(z³$ P¡|V)§ÉMQA¯¬=FbÚwyh;u·Jß’†Gs¯åYëÇkúd{T?OQ¡ûƒïeNÍ}ŠnpÌýןm.÷q¿îlæšÔL}ë´w·Úïi©E*öÔKÐ£Ž©T×jEeý”õ³5÷óàÄÓ4ïw í?(3qF47Ï&âæwbè}§UNƒ¸~ÊAI°P·ú«bÿJÜnÛ¶Acû½\/­!p|‰/÷Œ»'¨ŽLfhøöxh¯ÈÊßãþ0¦«Ù PË îìý§\ÛNb0œ˜MQ#ymhиÃ=×|„´Ä-gèb“&ñ q+¹.G²Åsô*—mË*^ç åsWu&F!Ö€úg• ñä^×î 3öqý§“y”{Áj(,üJÑ/å­_–ÆIP¡û1íò È{:E­ì§Ì êÆú›ô`h”Ášp)”+´hj¾"œ¡ï H~Ói®¬Ÿ²~¶æ~ªnÊä¢-|8©¬á¦h‚ߥ… µè—c¦„rÃr×Ë^Ïšä™HLg¸zwÄRh²ážIŸš&I\ÿ¬p¦bC×}ØjÃßT(\d4«Ñ•´Íà Vº5Ÿ s=­àkÿ1 ¹Ÿ‰™þ\ûè¥$Ñ0ƒ«à>´ówsë÷mcV›Ô¸rÓíD#V¿£¨P>Ç4éÏ:@Û5™³'“q•ûÉK~Ÿä>Ö¨¢{ï¤sEmCï$;î{“  2"› B÷?\ßµ¯ÿ¡7?îÛ%êýöæ1kyGQºp ±ˆ™ÉÚ_ýÞê·í`hÇnpëŒ;WÖOY?[s?ßÙ†°Y'WªÝ;5=±ã¶Q»ƒ}Zñì&àNô ¦?äsõ6þ×ÏðWB¸A=¾‰§¯Î亷?M—¨5tÊHØñˈ{ç®+¥ŽÊ?}tr[ŒzMØË6XhPtjò]XpÑŽ¡¿þõ„dŠ:ŸN‡àl+†fúl/õ ôþ’dÉ¥Ï]Ú®m.TE£¨P¾a¯ý˜±Ç`@3×ıÙ-µM’ëÈÆX:Zìñ‹ŽŒ=Mмb(˜¨Ó²hÚæE_®Ðý`‡4¸ð;ˆ –]a×®Ñô~à¦ÌPǦrP« “ Bûwt­™ =³š£þ­¬Ÿ²~¶æ~VéíbÓJúš¼1‡}ÚûŠ KL¿BÞ‹¹>ë.8÷0!è&ãp–¹ã4÷’·óÝ‘«ý+Zò¥ï ŠÆ^œDî}-A5Þ£UGÒ)ºÜ{=¨àÀÊÿ±:•½ÿq†¢ê^!Ì­°+C9<‹ï&½«rúÜNåN²5…:{¹sÊo“S¢ù\QÙZø©ÜЮëK€žéÀÊ7R!žmU’#èƒÃ!ì£Îy :dS½dÏĨµyѨ ¸ÚQh@Ø:ͤ˜¢.wwҠúŸá1º í•&µíÝgQÏ~»rü´‡Y‹¹Bû{T‚\ï»§ça%–•õSÖÏÖÔOé¯àr6³‰ˆ%¨ô¼‹v,±(%¨ôtC¥çBù}Í&±™Äœ¡Òs…ËéÐÖ,F¥çÏ34áœA¥ç7×L…¼.ú·ùÆMtau†.•žÇöÓ!fÉbTz~5ÚŽÙm cèßÞßQºÌj*= O¹‘€þíþ·žJ ÌÔÄèß¾—õSÖÏÿæ~¶óicýŸL“ì—Ô4hQôcS™dè˜hn˪&ÚëE&C»¾³¤=S¸ÓüÙl--@£²æC5bôœê"6æW-E†O`ÞE0´dÓ6hL/ãÇm†ÅÙƒ¹Bù«æ$:a(:nïiÒõ†›áUD=]š0a¤Û=£hQÞA°5” §¶eåhm@EYª`ÝØCŒ¾H2óW=ʧÐs¼)^ÆÐ»¿æÁ¢ÜûMÛa@ÐDŠF IãÜvÀ+í @ƒD^ïå Ýr”|C¹ï ÙÂÝ&׆Q˜ÊÕ[;™>ÉÇüý÷ÛK¼D}íÕÔÚr…ÞËú)ëçÿr?••‚%§Y E++ FͨêÀP³íöý+\×Î/%}öÍá>TÝÁ¢´¹Ëvf1%c¹ûÏÇ›esGÍÖowîÐ7ÁaÊ#Šn¿ßü¿¶T(ªâ£'ÏÐØ¥F "þCÑžõÆ =»P‚j»´ëíZÜÞŽ³ÌqôÃGs0½dÁ=Q'b#›Ì¹µC;³3kŨP¾®¡à’ÄPZ“ ró¦QãrŠ® §PÔ!_„v)]D.ŒÕ´DyÝ{vT¨zro•*Ñš­…ÜòakˆöÆM«¤´a1×|M±’‹â®= zm§STëUüzl è’—íØ­. ýÕy%­7…+”ß6? ”ª+а»ÐÝ%˜ §ŠO‚ó‡bT¿ñø¾¨r¤%,u¸ÎMïf)Εû·:³=$1Ï(zΠ-øàÂP¡|Ÿ_ƒ½—*C¢`õ™¹u˜1VusôéèU$¦ýx®hçRî¬Çе;2  ,“+tÿõˆ) bœh*5Å#©ÜZ«BÒè˜FÑ‘Ô Ü,Æ3Th¿z¨«ýšpkäYŸÊ.\Y?eýlÍý̧.ðVo)C=›"@dëµ:` ÉÅEÜŸË7ÁÖw¡\S¿µ`~¢÷ÖOØ”«NЇNš°Dœ¨AîpBâ6pÓM/’€ª†Šoî ò’ßÊŸ²#Žº¨Zn æ_ì¹ó;gÛ“Ê ·r€­Ór¹koí¡Ö- \aFÝ7ZqýgÇCØöw=3¿¼µVT(_@v ÔÇ„‹Ñ¾v… î´– W^$IDóíðÖzæ9´,ó-L·M¥¨’a6˜Åž– B÷Ufö€³ wªjü‹Ø/ŒãÚMy ©YÝ}&Éÿ$¨ÐþWRï[F ½pÀ ÚÌäÊú)ëgkî§Cý.=š¡±áEßO‡›70‡ip/œ_ëwͧ¨ïµÞpW)ž £?ކçyzÜÅÛO‘K«Mh"w犧CÀ§þbT»ç\bsi= Bù«U Ôn¾!˜›o㞌ë =®Dsï.6†,¥Ü¥FŽbëÚÜ…Õ$¯Ž¹r§6O…¾#Žs}cîRÏ9»¸Bù<ű`2:‘ õ}ª‡ž\¨8+ù¸$O„šÞ;ôÀs ^-\*Fýv«»+ºßV½š\~܉9Du@wBõ.X'ºFP_‹K 2úWhƒ4\˧èÒMKáÌÃH®¬Ÿ²~¶¦~Jƒ¿J`„V#E¥çÙ÷LÁv†»•žÏ¬H‚5Ö*=_x_­²ê\, Òs¡üécžÕÓ•ž[Ìr„˜×Q€JÏN(3ÉêÞ€JÏ r$VFŽ€þm¾N7l¡áëG1*=›~¥Â­L Ò󫟈ª<ú·÷wwÛD6F,Tz>«ç X§” èßî×ê½”†tgèß¾—õSÖÏÿæ~¾©kkýŸT—ïn%KºkµøyørêÔíÛ×mSž Ÿ‡ºçÏjÿ#’ Ý+(½k~“Ûtt Ù’ÎPØr‘(—øp4:ÀÖ¢AÜ´Ò gÝ•+”Ÿªô€Ïÿ„ê£ ¥]ÖsÕ³FSïËJ\‡_Ùã}1j6¸ ±7P´r‰:„x¹0ôwJ*ÞÎ]3û>¸u­@…òéBÜÞZT%;…¨Ù¨q×}ÊmËÐ »ýÈvÜã×ÃˆÅÆl®èD‹8É;˜+t_{NoÒýŸZ@ÕWzˆ»L¹ÎuM¸N•ÿ|£;sRéSww† íW¬íMeö€.êêC—\Ñå ½—õSÖÏÿå~ÎÛlγ:®q Ì•;?«‡šÜä|•¸KS§²-5jÜdÏ,Àæ•=è°ÜOJeèÊK£ <ªš›ÚÆŒÆ*wÙpÔô [®PþáK,`Æ·õ€V?³†NáÜÚÊ,]¹+CžLmެáš%ç“͹Aa·¡ zE-ÇšÃÏéCêvÈî+´T(ŸýƒÃ$¡³-A… ¥òyÜû³‰›ÑŠöq\EæÙßàÒösÀøX*C{Än¯G/E¨Ð}¯ÇÃ%«^]ÔA¯;Ÿw•›×÷IËÐ¥í¡~šWhá/FMÃ;4 ¶Ó²\Y?eýlÍýlÚâ ‘ý:s8$\^͵›^GŽÕ¦tÙ£wDÇl8÷Àt&b}šÜ¢Ã Tܸ±z Îà¶L?ón<¢¨Kpµ­<è0Q4Û°)€ Bùí FÃõ=‰€ŽU2ƒ ‰¹ÜNɦ¤Í¾5 ½ÿÅtº8™;)âXy,'¨Úœ}àº$‚›Ù<„ݞ̽]|š®ôPb¨P¾­’’û? *×X7âʾ€š-:%ÞÜM•¡KíÆŠ wÎâNª­"ó ¼Z³±+øüQTè~xfЏ ¨ÞšÃ´[òY®ÕÝvÐß0›¡º;ÂÌí%\¡ýªáÌ(£ˆ VrYž8›¢²~ÊúÙšûùqE´Ô91ôʽ˜u}(·ij¨?äOÐGWn¢XÏ=;x]؇¡1ÿP•÷jÜú› ©,˜¢í>‡Á“i\oŸ®¬lºA·ëùÒ‚6N Ê`°*TÜ> ¨¶úràw97ËÎG\»s.C+wè‹4î§q¯ï<_ìåzéàc¸zá 7Þʼn¹2+†Æ}Ü%n[u…+”ïUÂNÚ4Åдã}Ø9uî£Íö¤Ù9•¡–—U!ñY)÷)¹LÏ'گɇÕï‰#¨Ðý'6¨ÓG  VNŠÌ…–pýƒƒÁT †¾ì—+·£¨Ðþ} ˜õ™0†¾_šHý{seý”õ³5÷sö²ˆ÷Vfè?O‹ài^!EõÓäˆå›N€fì [—ËqßÉŸ 5)> ½ó"LêÏíºôà1úö”9xmh: .œì!A»yn¦Ë‡¹Bù †<ïßxд)í¨ý½n»'{ˆã•Ë ÕvYª½ë¸_mŸ‚ÊÖå€Nú˜ Ãl ¸7¼ë‰r͆]ë/Ÿùq…ò%I±P¹ã=Áê­\Åèû“›À¦à$CWä‘É9Üù뇰‹-]y5›m¹-¦¨Ð}«ÙÌsØ@mõ˜eR&×jn2ôTI¥hÍã# PlOP¡ý3ÆP¿_#rv/ýË•õSÖÏÖÜOÛí‡!êU'ŠzMÞÿ¯ž®£úý XrÏi}™; + hÞ°U°°o†æž^ãÞ‹ÑaætòK]@kR(UK.£Ù]²À¼û~@Ÿ¼T‚ò¯Á\¡ü×ãÒÚ³I€®6k¤s«Wr½­ÂÃSû:kQ ìpœÊ}t¦ü6OÔ¦ð<™m0Ž›¯àuOº!±7yì}†+”ïWu4Ë=EÑm‘عoíZþæ,Äö\Ãu’¯==îtË4äkwÐÇ.Ä%è:E…î+^Ì2õ]Z6œY[„s›ºBqÏ™\Ñì±tõÄ:‚ íOÔ¬#Ýsz»0jtãÊú)ëgkê§ôž_j½ *=ÿ =’¹lÂPéùí.ДDPéù«Nk¨×âÝbTz®yE,žú§ Pé¹Pþ×çÚ±är@¥çzÏ’áÚÉv •žÏ?Ü4¢4•žO ­Ø26 пͷ¸n2;wC¥çlA!è¶ü¢¨ôüaŽ7i8­Ëп½ÿãç(¶A× Péù®õÊ´du¤ýÛý–— Ü=w]Œþí{Y?eýüoîgC'%ëÿd®oz]Œ&G¤PÑAU‚n¼96‡U·^ísj¹:¢†Š€âPîù˜ú>l W³_¢9cC;-w‡Ó®STkûxPƒ1ê<`UÑñT(ÿ¤… °²R™¡†K_’)Qw)ª¤ê~½bzøi6¼Ëà *¦N®ªmüòŠNõT …ã³!´&¡ZC²Á6I…+”oçü‰0æ> sú÷ƒÌï†ÜûkhPb"EÆö—|_}‡«>h*œÈíÊП_7ÁMcC@…îÿzÒ}¾¥ÅPV:€ÑóC¹Õw÷kíy ªpÿ¼ït‰+´ÿççîÓ’ý€NOÅÆ¿Kà ½—õSÖÏÿå~®Z¹‡ú}4gH% (ôâ~»ü„¸ô­ÔêÉ q×Ó¥Üvs iyNÐÕç?ÐZç€F©;Ã}]1A§½·€Êß#¹™]ú2¹7ÏËщ);Y^ÉJŠ åzü,ùÙó Eoî."+/qLÃu“@†¶YõŽ? ¨Ïî¹Ì~ŸwÈõfýl"C³nþ†íò;(:fm÷+E¥î{ŠÍØr] =ò‰ÞKç<3"£kƒ­ê÷–ZÝ‹#\‰ýÖõ{HôYs†^i±lQTÙOe?r?/YÇ/>PÃH¦ÂžÉÑT«a2nCµ3t¥O™q 3½{°C…ñ54\Áµ^/½è%{#*ˆ»¸e7‘_ö}¸}ýÂXËf\©ü©î°3#‡¢´uôÉ7àžT‡%; jtc#Ìgèîkˆ£Í.†>Ù;òdq§/Ó‚a5rngvŽúãJå³”·…_r}si;Ùÿñ ÷ãý5à|s(Cw ?^Ûqí:N&·M—q÷Žk Þ^ETòç·µW©þºc µµÜM£Ïœá\›-?PјûÓàùô˜¢Rû?¿<=v´ýé¸î¦¨²ŸÊ~6ä~†ë&²ë·ì(š3/™Åº_â½ýœF7¿Iо«ÕYÀÞÙr45¹VìÔu;Cßšt…Äö¡Ü!Å㘣N ŽÃ6±Fo!hÿ³ÃXÇñFýy'Œ­`ÏP©üEpÛªP@žÝ¥](AßîU…-sf–,:õãž3Þ ¿ëV24dL ]h‘ /†VrSdåÓîA%?_OLÏ^(ô½ó9ºïN!×/´ §¸S4©¢×N$(±ï Ÿ.ZQ´Sttï` ¨ÔýÆ!.t™Ûy†.V}'‚ÍeLb£§ÍMj6n[oãJío?(vE5ôz´›ÄUöSÙφÔOÅGóÒ.–wR‡¡ŠóGU™´Íø—UœoU€Ž×r)ª8ŸÖì;5úž@QÅùSu‘Ìq^ÅPŹTþ„°õ\k¨â¼kÁYšå1€ Šó¼Ú9'L ¨â|{Ùy2îž š¯<ÄÉöfª8¼ò|¨n¨â¼ÿÎÁäAÙA@ÿô¾á3A^8µ„¡Šó ƒ*a‘óh†þéþ-«¦A¬a ú¾²ŸÊ~þ'÷³¼ƒ†Õÿf”‰;)œ¹]D×›-&­¹À€–n>ÅËä¢î“JŠþ~V{? èÓûàÆ½y€†]Ôb*ÑU2t»Ï(šzÛ˜¡g—M3ƒ§Í2m §ì“£RùÉË#PÞ#ÐRyÌV_ϵ\X,³0®ƒù>Ñg«×üS|ºŸÄmé8ª¯mæÚÖŠ4 2–ëvò0;¥AP©|*vdjnÀ k/S<†›bû Ÿœ$èÉvo¡zòh@ Îþ>=8˜Û, ™k—Í•¼.L¸Û´'CÕ{ÜÎ ÜLŽôè 8[£ ¨Ôþ!y dlÐ6@}ÛJl]÷s¥ÞWöSÙÏÿæ~jæÍ ÁñÍDîëþŵ!·I‘-ɪÁPƒjc(?ïÎýbúÓ“=°ÂLšlá¦¾ë Æ›Œj^¾*ovÑYCšAêã0@› b¯vs¥ò/ˆ‹€ÖM½ÝñW(èìsæš_¹D#Þöã6ÓìÀÖh-&h^îL¸‘´Е-ÆÀZ³\îáUí™uH*E5_u`r—A •Ê—û8F¨(èhÆ´Îd¬ž&÷”áEHJáV^J†½"¹­&ïd5 ¹u;õXbúŠJÝ÷ O>—NfhæoG²íÍ|n驺uÇÐ}÷#áä›\®Ôþæ Ò©oÚ1@,f2ŸÇæ\e?•ýlÈý¬Kþ"8C¬ˆªoÛ,oZR4Ìs x\ÉÐkŽÀÒ×/)ºúŠ ´è|smøÚíwóGÈœ9” 3Û„ÃÜ]ŸÔc´Ë<£¨c‡-ìv¼C%ÿ~âB!÷úD@§D‡ƒýÄÉ\­º2ª9*‹¢?Ûe æî=ÚvJYœzÐÌÎ虉¹\ßh¨ÍÀÐÛ¾-!E>“ RùæÞ±'v'µõ°6%}®täžÿ7ý¹C>ûƒûÌ%ÜcuTsŠESß—ÉNyNf¨Ô}+ãmd–Û†6Y%’ÝC¸õÿüΘR薇鴇íV®Ô~yŒ*+0tÑÊ'ËåŽM£¹Ê~*ûÙûY]*SàMQ•/†»¤âšWŸ„²³ßEtPe&¸%f è·e?D=µ[€~YЇ9iEpÙ%®ôය ¡SNs¿íD ^3T=©šÜÛ±Ž+•? ¢ÀÇu å‰‘p¬y÷Ôƒöð@¦ÅÐ5©ö0lÞ6Š–îíÄ&‚  ßªäÔâ[Ae¿áƒS@×8›Ñ§ǸRù4.ÜNttÒUZqV“Û\%2·úp‡…‡Â÷=QÜ$ôÓÚQtáÇ^ðæ]A¥î'å¾#i#úù‹ŸÃ=±t$½ßy! ³BtÙ™K¸Rû¯¦û‰¥3'‰hïž•Bï%ͪ짲Ÿ ¹ŸVö9²7sUz»}’°¼¼wjy øO¯Ð»öu}÷Û„vÌ3ê9AÅòZù4‰;Y8Jöv>hÖ¼2!ëÙvî Ã`X³Û•¿e$ÃòW*¿ïÊ«‰Ôç¤'$ÎÜÇ­Kù_:Rô±½=6sÛª5¥î’Z2Á›Ž,NæfßêÈl¾wT=.’½2ÕçJåóŽI *ó ºbáñR@ÇÈ<¡}³Ã€öÑ™Ë9n˜æJøöÙˆëÁˆ )áJÝ7 ë ÚÏbºð«-8ïŠçþøË“ÝþñV@ë¢méVçf •Úßb„7t“3t´GhL Pe?•ýlÈýÌõéKÎWÙ34¹n'™Òe17ìí˜Û7N@#¢@E›0‚–-K¦ÃdqëG¡Þé£E4î£* ù± Ðù%EÔlj67²ªSsJâÆîóeªû—ÊP©ü]‚ÚÚ#€^yGz·Èå¾Ý Å=K(Ú=àäŽôæ®iy‘ª¬œ-¢ƒ?Ý{wëÀЩ{–3?’n£ª8×HªŸwÆ€*ÎÝîÕˆ{|º1Tq^utÛn¨â\{x›šæ@QŹTþ¿×L$3|ª8_"Tƒ¾M Açsjƒº«5Cçz’É™Sú§ùô*Õ„¬&2†*ÎYi)Íš2PŹͧ8¦ÓóAÿô~Á¾(Ø•ÍPÅù¸ê™P1ô)EÿtÿßAépw—  ú¾²ŸÊ~þ'÷sºF3«ÿÍ“-K ÿôR†Æö,7;®q—5´Çò®€\6 §Ví"¨FâGº§éR@'îðg–/ôqaô,Õ<–å'qOU•CDÅo;°vÿßÿøÿW*ÿžt ¨íȾ, ½ C½:µe?oÛTç„sBÑ…W÷ý±Ûuñ‹!Óoìäžê· B× çn{«F6Í*áJåKSK¨Þ2†¶V±‚¼ã¸V Ÿg®Ç¢ñ°è¥À£^NOŸ<@PÛª©ä¥VO†JÝ7}»C¸ùbC­õÕHþ”1ܽ—ÿbâú#ÜB{ ßþ0WjäÚXxëCQ›Û ~nWê}e?•ýü¯îgæeÐ(6chÁóó`Ô–ëÐæ‚Ìü`'‚>õoI4vpæz°¶ÛL)ªŸ<„[Ep›~-%ºr@o7Í“ï»p÷® &c.r[Ÿ3fNW¸Rù ï6cÑd&E'ý:Lõgp…+YF;†¾½ªÏÜÝ÷p;Ø'vÝšè„9†L“næž•EµÛ®æò eŽAŽ\©|3V®€K/Z2ô· ¿xQT{È0ëçÎP­›ñª·ž«=y*GPtvÖFhÚåWêþÜ‘äïjÖ%„4õ›Ì]Þj7ÑHXÅ])ÿI²#¹Rû¡_F(ª¾½Z%¨²ŸÊ~6ä~^pÁ²“*CŸ‚óOíݤBèd8„ Þ›bäe? 4ñ÷\µ*Ÿ¢ÍÓƒ˜Ú€ }û(Jh–YKÐÊq+d‰ªyëmqö3AŸ -£÷6f¨Tþ0×÷¢ÇéjŠ^¨¿$øö}Çm½¥1e·0´¾S{q'…Ûåðdv¢ã5‚^yZOU¢:Q4¿»£›14|“:sð8E¥ò9•m„ÒšÃzÓöŸïM1› ƒ_]„ýÿ|î£Y-4wííyÎçW4={'3;•ºï°"‹$ Sj¾ä.Yn?…k¿ "FÚpË›EÁBK‚Jí?òK[B÷Zpo$dN8ÄUöSÙφÜÏÃ‡ŽƒíÇ7µ8s ¶U½åö¿yW9\e?•ýlÈý¬êq:­û@Ñaó!ïÙ;nþå8’›aÇК÷2°xìÆ••éAŸaQÜÅÙ-AÅi×uåh8Ñb(·"bx„xPÔwÌ82æ( ³¬7ÑŠ©á\©ü9bIKu¥è»v¯Èíi:ÜE§šÙô"†ú-‡ß¥…\ó• BŽÃPŠ#:.õܾ½ƒÈñû&€&îÔcŠwr%þXKLU´|¤Izô˜ aÓ«!7BÐz‡K0p¸)w¡ë–|Ù—û¼Eõ˶!¨Ô}xÛZ] üp|l?€›w|• Ñ¢Š 3ª†³sn{¸Rû¿´ÜFï¾Îô°av*¸Ê~*ûÙûyÎCô©§}j[þô2wXÛXè9Ð’¡}ö‡iS‹)jñn ´HXÅÐ+Îq0ÔYàƒM ×ËTDÿê–ý¯¦qkÌÆ1 Y$ ©Ö±[K.ST*ÿÀˆÃ¨o"j»èñ+iOÑ¡•…0³f+Což½*͹s‰¼»Û”ûýËaèÿr¤ˆÚØìfU–õ­ÒœÌÒ˜QT*Ÿé;w’¶æAw‘þ–O¸«}³áô¨€Ž_–Žo ¸Ö;si₃zú’-9aÁP©û]§‚ßhC†/󃄖ÜV»þùݬ{7®ÑÖãÔïeWjøS´ ©ˆN©Ù*+h(G•ýTö³!õSñqH¼ã|R)ª8¿óõÈíQŹqÓPúú­Uœ‰öïêª8×ÛK/ UœKåWöˆh%Π¨âÜÏõX½iJQÅyl‹ƒRÙPÅù„•ìZow†þi¾j—ÍÄÜRPÅù!Ÿ,`í{ª8ÿÐi¤^Z!¢zÿú£ÐþÔ[Š*Îã›ØÐ±$¡º?]u¡hÚ垈þéûÊ~*ûùŸÜO×;ÿïëÿ“síâw:Å>W´î<”ki‰š¶Ü=c»Ai²#÷Ê”ËôÊ­ ÍÖ}%jE\äF‡o‘¿•Æ-¬õ'ö…>\Ëbkè{¦P-ÕÖb´ZW*Ï<8t… +¾18ÚÙÐæ‡'’»ƒ¼›Z$تys߇S£‡ß)ê–4…DožÂÐòùIÐîènV© ´²‡ ¨T¾Ï××PY?MŠF·¼JCe+¹Cºõg¡f1t˜a}ÊVqYxmlŒ€šy&*û* *u¿oä&™¢Ö×Aod$÷Яa̰÷3}dÄ6ºž£¨Ôþ}ư°Iv½Ûe9«÷ëÃP©÷•ýTö󿹟^¿æÑ±¶Æ€ÖÈ ÇZ4çª $Ëçôá†-Y&pŽ #_vƒSî1Ô×~´ìÇ-.Ë&ûö®ç’–³áâ;nä‚06åÒ*@S^ua{îTPT*¿ñ/‚³Üíe²ÇlãVº\+µÇÈPk×$qS÷ ŠêŒÒ…˜!ÀÐ.KMáÖøîÜÔ}} XÛ ÅRÿ^Å\©|N+›°ÞíŸR´å©¶¬m—¡ =÷!‰>î·’ÛòïHš={ ·×60ÃÍP‡õ¯é ”=\©ûïoCËyÞU«)ÿx÷áÎy,Ç)¡•l§°*¡”+µ_¿i-mµƒ¡†ú·ó»l‰á*û©ìgCîçMy1mVú€ ;N<¦â^®_³‚Æþ µÞgH¼_—£ŽÅ©àµ¿)CËçÂòƒ ËJ†oC–s{þÕ/nÔ«&–Æ/nÌPƒDhëWAQ©üŽ'þÎl@S®†ê‹ç¸!‡v“‡ºòôl¨Þ?8At÷DîÂ/ñ ývwcÏÃ4ôõþôU¹W*ŸÕLfÌÐy_wÒvVÜ™Wb©¼.’»VœE¯>Ìí•ÂfÝM èSÓÅ̹ตºÌý¤ýú%¢öÞ€JÁ,îÂoº`TÇP‡¿zÃç á\©ýe½ö •Â!†:õ_Ç{\e?•ýlÈý̪héLw‚¬ _úks·=ŸJJç>—£Æc5ˆ•YsîO•-ð¥ùvŠ.8³¬V~ÐÓì éœIÐÓ&mHqˆ6÷œH^ š€~ õ¿'s¥òWÿ5„ÛÅ€F¼#&gþeñ•4È»§ÊÐ:—lоøMDÕ?‡Ç ƒúi„šUå§‹r4(ÍÞv*chÎÎ"h9»#W*_À8'ùÌÑ 5;´‡ -â&¬z(ËÝx›rõ1é¶õ)wÉö6©Ü•[š#ëà—Ï•ºtpx—RúÔ௯PÔÝàáÃи™g@KK&¢Rû-Ξ‡/Ëö14®þ,"O)ªì§²Ÿ ¹ŸÍ^Ý¢¶)T@Õ_]¦o7q2uÄÜù%"ZéÓZjœ¡èùX˜·×Pïyñ‚~ 7àã5y𻃽ßåxôæî}ÀÜUnp¿M´f>«1T*]þzR1£Ðø­§„ü'¹‹÷BD]ˆ€ÞؽJÕp?ä?Çš€ºŸ=¾åC¹nì$œÚBQÕÚt ú+•ÊWïØÆ®<ËÐ;G°yx„Û¿©'DN{À]S‘I£NqãW|%ÁݸêƒÇAPØ ®äïg§_€jáX‚š=}¸ýsœ£Þp›7É!Öæ\Éï¿yéÐüF3е> aÌDTÙOe?r?O^¼@ W h\ÖUZhÅ5œÞ„F­! ¾ÙŠLí¾…[¢LÝžZ hÕ¢qK§h¥ +ømÊÐmwÓ#ûS¸®Y¬þÉymDväJå/}ÐZÞ5*PóŬc»¸CwžS{‚^4(‡â¾€êdÁ®Æ#¹>K³à‡g(w¼w‘©ý&èÛËúDí×5®T¾Aá«ae}CmšÁüo›¹c:‹PäÊi/‡…n\SûíP°ñ»€Ò0 Ú£j *uŸú=ƒ›— j´è1$EþàÞAzûÎåÒRjqÚ‡+µÕ¬ƒð»HP£ À¬‰;WÙOe?R?ŸÝ+¨ËPŹžÐö®ÙÍPÅy€,íLñ¹lŠ*Îk¯’·ú Uœ?¾J5Mª8—Ê_÷=IÜö<PÅùî®ÅÐyI" Šó¶àË3 ¨âýÝv•zpåì&¹°.˜ ýIÔ;m@>ÜÌ6w‹àjÿb‹ÇePT*_¥vüœîR†œµ“Móôæê™§‚¡Ã^®M£Ó°t€+÷ÕÙ]BS]Ô)YF®×Tê¾ÛåD ÖªC¡W@wp×M¤hì8nË3›iáO•ÚŸk}†¼µÏ!¨õäýÄÏ£J@•ýTö³!÷óç@;:~– •u«iŸCnͯ2rz^‰>þ8›ûíñæ¿¶9E RlØ©³º µô}û¼QmøLR»¨T¾4£‹$ß%š¡3FÁ®³\aPÐ?Yä¦úBNòVn[—c4Q-‘¡¹éâ%9\©ûtâá`N µËbaP×8î½@ÛW‹¡qWvÁ*ûQ\©ý&ë©ÞþÙ Í`udR}WÙOe?r?C¯ô¦n%ZsAÿ÷3µÈ{³:é1ô¡5Œ!ó)zËâ9¶0Ÿ¡VÁs`MÕBîªÏGA%k4×0á:T…‹è‘³Œ,zRÁÐ˯7@‘ÁF®T~K“,Ñ'ðA¿Ý™O÷°pn‚¹>{¶ÅÐвQ2ˆ»µ¾%«ØlÉЕš,óÁ ®eM‚V”% YOg1[¹E¥òù÷p‡ðÉ«ÚôÓfˆ3œÛþÐr¨ôÞ ¢»|7@êç.z,0†1tuw9tzúÏç.*qÿèh³ Ð=!Qðêë>néÇp4õѨ’«ll¨ÔþÏ–‚î/Æ5 …ÅžN\e?•ýlÈý,½-;4^@˪Zûãd¨Ú“‡àæ`ÀÕ-.…Ãß赂ñðàŒ*CëG‚nÝ_S´‰W¤,i ¨V÷äpïwõ™WÖºG)êè4ôNµT*¿CjuH7ФŸÛ¨å“¿(:Jï({á¨OP=‹4¦:ÿE7ÝRc¯_îa¨~×9¢a9÷|¢ƒ¬pû~XMã#.s¥ò銃OŽ(jå‹U¸‘qkÁ¿õz‚GL„Üß¹ Ol¿k¯Eô—G8äk\&¨Ôý¯7ƒj“,@½ZÃzË\®v·äîÅÓ¢À§bWjÿº…°;Ì¡ýCšZ]‹[EQe?•ýlHýT|¬2k…TG=ª8ï=ð¤NAPÅù¥Îk {ÙŠ*Î{Ý8J^¬7%¨âü½C%9¯¾Ÿ Šs©ür‡)4Ǽ˜¢ŠóÄ.˘ê;†*Î[»ƒ¢' UœÇÄ&C£MÙ ýÓ|–[×ùXu‚*Î7ª€­ Šó‚ ó…@ß¶€þéýº¼ÕÐs’PÅùü÷Edý¹§€þéþb×/pêMo@ÿô}e?•ýüOîç£Vÿ›Úfm¡4·Cgˆz`50·îVÐí{* U%shlM"×°ý3ñôª`Š;'‰ çqÇ ß@¥:]@Gœ­³.Ë]r¯8·ûAÑõí5¡¬õ|‚Jå|]@†Dtd¨Ê7](ÉÒæÎ»µ ’N]'hZɘfåèÏMîįk7 óAV¬Í;ÆÔí¼¹5ánBá[ƕʷɠ€˜š_´ÉÁD¡Lv™+ÏHfYžd裵ªø“rSÝfÈÚ½Ÿ¨õÅ* ƒÌ¸R÷í:ÕP‹ðv]ý‘ê­ÍÐÖo€.Ö%hÉéøñ:P©ýÙ/ËH⨞uyu—¼Ò¹&¢Rï+û©ìçs?eƒáË`k†:¶ ¦æ“¹ 6´dßß/ôÚ­¡Ì®_ nÎ"/²8­C µ¬a²EEƒ§G‚Îå@wÉ© >Èu 3†§†ÜØcX¥ö®TþêU£¡íšzŠþ(]n¦'¸.¿ÚC›-û5œ.[ó(‹k1£é\VHÐ)½ã…²´3Üèë)¬UcS@um|Ùû ŠJåÓœ³Ž>H €~nÒŒ5Ù¼Ÿ;ÀdXdahuÌ6k³ëër…N›tš¢ï‹5@î“Æ•ºïèúª\,CÃ6USÃåùrtàœ¾ï2½ýáoáÎÒÉ\©ýe͆C¡ÕwÍM 3ç˪짲Ÿ ¹ŸQ5ãáï7†ZäyAÊã…\/˾̯쭀Ž0zNÕ¡hmðbèÚq)A/´P‡`èè=£N$úð&n­f çÊõõve·µEtX¢;³€¡RùgWùÔ>ã)ú½6R©—ˆ>H+¦M:Fúº^‹šÌíܺÕ— .‰z}c(Új½ ›§ÏPÝ ~`*W*ßûb3™¶Ð>“صôþ\³á{ qŽ7C½|2 äçîÍyP$Pë6³á¤u,A¥î§{FݾoÑ/Ò¹ñ·¸…©¥‚̱  ë·ûÑ™÷žTjÿhëN0.6P«Ùêôöñp®²ŸÊ~6ä~ÊMÂaf”C½IÓ¿pû ×£½ïì¡è°ª ÁºK7o_ˆPhÔ ÐŒÀ@1¾ËO‚Έ3ùç瀾€þÞgŠÔ>ˆèÈ¡°tä †Ž¸™ ƒ´Ör%ÿ~>ùCͲÙZ]±Â[©Ôýq7fù¸ ñ=§³ÏÓ:scmN›6 Y» ÛUS´q‚ÔRÔoú$ ŽÿR-“§r/ô‰ceEr´¨C›øe"C7ö9MjnÑ€Mzp¬yŠþœ«Nο;KP½ÓÉuV •Êß)`/¶%è§YAÑ|)÷þý¬RÅž¡Çv8²õ7S¹§Ã€zÐ+‚FZöO;Z:ÖIô}Ó•¡Æ£ýÀÒ/+•oHÿ]4u¾‹ˆÎ|IÝlÖqs—hX5BÐc‹™îh@=W·`:GÎ2´÷Ý«äk׃\©ûo¶3ºæ÷8оŸW@Ý`:÷[ï°ëç-‚Ê·:AYV  RûžÅ´ÆîehÔ /⥲t®²ŸÊ~6¤~*>ë›~ÀºŠ*ÎËëÛAk›LŠ*ÎËþ¦²ö»úoïo_Ãæn$¨â|tp,,ÜFQŹT~ýaQТ(• ŠóÀ´}trñ†*Î5ÝWC^P( Šó/§oƒêÌ-ýÓ|Ÿl£žjETq>ÞãÖoTqn¿Ü ú%ìgèŸÞom^Hó‡»QTq®ñm‡YèŸî÷|¬CòL3ú§ï+û©ìçr?3öÿ¿¯ÿO¾p9NŠ×RÔg÷R1±Žû—~SHµ˜$¢zÇßèñi2T#0“y;k2T³õT6-7•[+ ²Ò ª¿!Š˜ïÓ´}~.ÙÖéEß7žzÕ•Ê_Tñ7 lÏЄ•êV)“)úþÂh˜|$Cé·9 »Þ{`ã#0K"héêkpPÓвFñ ¶¡.U7¡ÿ¡—•Ê·i6Œ;›DQÏ"](ÉÞýuþ¤|šhf§xÙv·³ë)ÐúæÄÐFõÐã`0E¥î«ŒÛ<|ZÛƒZpùª,øÅ*×' ~V´FËDh»v7A¥ö7êÓ:\¸ÂP'“°æs¥ÞWöSÙÏÿæ~ÞÞw•<ÐúFѼÙU$gg=×1ì%yUv\††În UýœT+*E<[yŒ¡—䓯ӶsU®7–ÞºGP«ÙÕâóÝÜI½CÉ´YÃ]¶·?‹?®TþMu+yZ>*Dý“›‘]©O\Lšò(*L¹—U·ÀŒ³i€^?}•,›²—›ü ¼}&p§"v¿ßT*_~µ4~/RÔÿ¸Óƒ*¹Á/wCÒ°³€ž?m Eoq =/ÃÜVÜífpuôF®ÔýÒY…¢Þér´rëa¸Ýj Ðú& ˜6œrýénæJíßîðNÌîÂÐÏÃÓÁm ªì§²Ÿ ¹Ÿ“Taþ…¯µþÒî¼á6yÞJÓm Z³È†¦½à9Ñ ú”24-ÚŠýü¹*!íØ'×2‚†¦±¹]'QTub8;f¥ÅÐS9*,¸Ë®T~ú±^Ô©º¨Ï-x2··ôPüª =Öô'|1&U©×?SÓÁ ¸îË·[ipËò® m×e4¼§ÑÍÓb¨T¾ëíBõÕOý{A øÐ î^tÒøý@ÖÏ”w¾•ý/û¡NÜîÏ¯Òøw *uß”¼|Ò™ˆäé5­Z®U—²¿º2tųŽÕÚ?²K>¸w¿EÐóÛ³à˲€*û©ìgCîç`3°×¼MQ}íŽp^¼$w †ê8ô…ñ`÷c¯PúwG†Öª¦ƒ_ò͘:Š9 a¨ÝèJ1ñþAàðð{Üû àÁ9E¥òŸ9 [®ÚêüzŒz6ës¥ž<Àý0ñíâªD¹ç—¾)'hŽ•-=šÎß};X´mDÑsö[ Tû0A¥òå,Ì‹¾ <áDßöÑmÍGѯ#­7YKƒÇÎå­™+øl}LÑNš¦Pc’*¢R÷½›XÇʵí2XïÐå:39kjÇж¾×ÁM'PD¥öÓi ÿr' *›.ˇq•ýTö³!÷ó…ýx¸2y Eµ^8 µ*Ü9gã¨ßH'@ 2»0ùÐÎÜv‹Æmô† ¥'‰æ~nhl8ñ}•Îм¿Ì`Ô/‘Û¶ä,,OÏ'hŽwÙ´ÙP©üE¦Q0°‘# ž†`wídî–kÀ{`wÞLs˜Vvˆ;ý¾?-nËÑ|ïT™­ÁŠê}Yf›:qHc–SœÎ•ÊçT â‚È)†+šížNß$ØpêŽR‹ëƒ¸7'ô…تP-:¿Æ-í¨Ô}k>dÛÑN× !Ë\»p ›^†SÕ1]ùøhiz*ùù~s}þ©˜ Zc‹d¯ÖlPe?•ýlÈý¬mæ •k5dè† Ó /B— ŽŽa“>GsÛnÃrLîÈÑmjÛˆÕ½³ú¥IQ¹p™›×ü T7=ÆP¦jëÜ›»É–„>Û/ QKÌHóE‡)*•?`Þ"8°ÕPŸ}.Po=‰›ó‹ä­;Á9¸Y?§ˆk®5 ^ð7EŸýô‚þ¸±I‹˜é}@#3çK-*•ÏœœKM\µœ’ é¹›¹Ý~Qg÷ŽÜÁ©=XóñWjußUîÑk >}YÕúî\©ûßûE‘•¿:QÔmW,éÔ¸#·Ör#”×ÝÔz«§h4ùWjšfŽ rn¤€fÍy)XGQe?•ýlHýT|Üކ9-cª8·œ?ˆy,¡è¿-¼ùLô†0WáÑó{/ ý ª8?n•¯’~ˆ¨â\*ÄXÖr& ŠóÈF§éÜßE€þ[ü’y`ž²‹ Šs?ýdñ½ûQ†þi¾ŽáE×€*Îë7a–ªñª8WŸÀš§GôOïÛŒO$NuºUœÿµò]á€þéþ,×npàaEÿô}e?•ýüOîg©vK«ÿÍrÕzx0b* ·ZVCTP×óóGRsM@­Z¿'c>qß €¾¦ù=:x:lèȸÍÇ耼ßVŠ^Ú Í/ˆhS9#U‘m¯Ò‚ºŸP©ü>£ˆöŽQµ4s$òÃ-¸†;Võ÷áÜ·eÃÀúåXnA·–PvPŸ¡ÍwM¹Ë ŠîJ,'=Þ´ri²èkЙ+•ÏæÉGp…YÕ²S±z{ñ¬€^tŠcßš,Ôiêa&tùHЇï*@Õ9„¡Ž3Õ¬:HP©ûó.ù³±Áª€Ê²ýÙb³ëý¾ôôÞ=š¡{ÊoAÙµá•Ú¿ò×I¦™ýAoÝËVŽc¨ÔûÊ~*ûùßÜOÍ1÷àçÎm€žy|·§r÷¹N WÙt¯¥?1pÜÀíûÞLTšÚ¶|'±Ö2âþ\âe­o‰èÝ xp±!èÌíŒn®Íн§@Òƒp®TþNŽö¤hÛE]5†$þÈàî_×Ê—÷&èãK vò*n@+OXüj5EÃiԞěù›z¯$¢ÎsÐooö0T*_þ FVUZ¯:sËgMŸ¨Žn ˜l'Gm­b™§æÊ¸Œ³ýóÝdâ¹R÷Íä«ÙI•]Þ×ieNâ(çss>/»C@¥öguÕb×^îghŽæÑëP&WÙOe?r?÷þ> Ž“2]Ÿ·ȤnNÙnÙ4ÛQ­ô1§¥š×å¨eù=qëì‚þó ¨u:˜¢£ÎDÁ‰ú€Ž6,'™Üìö'¡¾¾#C{LJëQ€Jå> ¤¦Œ 躉ˆÕö:n¥ÏKÒ.ç AÍ’Ûn窃 5¶¥ˆ:ëBu»:‚¦X/…3Týç |8+•oÚú‡ W¨üt,~˜ÉuŽœÍNWôb܈NÌ<Á–;¤ Šn«ŽTÇB…Užž$ R÷÷˜/f*oôû@wVС×pögòd¾; êõÝ(Ø”+µÿÑ??à Ëgèc(ðÚyŒ«ì§²Ÿ ¹Ÿº¯BÁ{Èq@Ï[,†&Uÿ²eóùTkÑ ŠFÏ?$^iÖ¡mR®‘¼ðo½’2Ö&sµ×ÚÑmObÝÛó+Ý0—[äÙ  71¤ŒzäãJå7?rTxu¶AûkL•—Òåz÷ªçÖ,М° ñî"FQµ{éð¹6 Ð%o-ÁËFÎùNÕjæ·¾\6 ºdds¥òÑÞÛÁxr> ïŽ»Aç”eŸï9‰¡G-’hã÷.\C{Á9ÅŒ¢s+d¤ây Wê¾wØ4}Ã\D]ãÆ°"xÂ5ê-æé÷§èç镤|}Wjÿæe_ÒŸ¡Ù¯ÏCåè÷ªì§²Ÿ ¹Ÿ9ê éÄ〦}M¼ŽqsÖ9’Wç1ô—c+xÑÕ‹»à,NiGÑÐ ^àg߈ûÎps«±”n:Â,ZÞÐv]“™cWgõ¸‹n1˜¡RùoMmNM÷?ÐoÞ!´ 2_†–°'#nögè*/}xÿ|(÷iHrñÄ @3ë:¦6™ëüm” 7=ŸÛ£$ˆíïÓœ+•¯‰­ ôy$Ô/ò3)P=ÍuTF} W14 ò÷ðA—·pߌ‡W×w‰è·ý¯Éâê€JÝ/:?œ¥öêEÑãþ}˜÷Tgn’Ø*gËEôŽ÷ð¬nKQÉþ/ÞV÷δÿÒE°iÍ+®²ŸÊ~6ä~Ö©‚§ê1@Çßhú±ÿ²í»Ép£ã$†z¬ƒ÷¢)—™îoöF@[ ;4…ºµk*»1ß•¡:ÅMØ‹g¹ÜO¯ï‘Ñ.ë¹#½æAißL•Ê??ø 5~^'¢Ý<¤«œ“)*.u£Õ­zêÉjx<ñ,E îTÓì©Û™0ÏØÀ­ßx&=»KQ·àé¢E­>C¥ò “³ˆªÅ)@ç,´&'h—T$N§âZ[` ý«¢¸=l¨Ñ‘&€¯TeQA{ *uß§{–70¢í Ú°¤ï1Ü…q°pnAuú9Ƭ̀Jí?ìkuí0u›ðýFW®²ŸÊ~6¤~*>Þ“>íÓrUœGœ ׫ Uœ¼à ì2ý·÷[gçZœ`¨âÜrhÉkH@çRù÷6~DÓŽ4f¨â|ȯPx¡“BÑ˯›ÍŽ©ª8ד9€ÉÏóýÓ|*cÝeŸÎWá)ië Ãæ{0Tq~¤qk|áEÿô¾U®ù<™¢ÿ6ÿ{¼À ýã?ß‚®ô[Ì!÷ßWöSÙÏÿä~ªùü¿¯ÿOºýÖ‡Ž¿ç+i²h*7ñzd¯?NÑÐW¡{¿4ýü>ŠÄ­—£þ}ãYÿL+†jªm'ïŽj{M<Þ4š+xEÀÐÐm\Ë€·Ô¯Ù5®T~[SCr¼§C{ÌM$%u«¹Ž{L`lã$‚Ú¾H!=&?ãF¨‹â¶~½:¬ò‰XbĽíCÌWWP´ôÁhXíIP©|_<™Þ9†É›Ïnxºr£ƒÉI€ëÚÒõ¿¸¶F2p]{Ž{4GNoîæJÝÏiÞB0+¾ÄÐÂ0bõ¶„»¿vÛ×2Ÿk¡{œôˆ|Æ•ÚïWüÂ'bè•}"E@¥ÞWöSÙÏÿæ~vš oV}‚ê#–€žhÞ›9R†úL6!ŽåÜÙSØ’“«í16“iÙ‚îý;™~+ U½Ø‡> •ÊèçwbÆÐ{?ÃHÇHnÀ#§á?õtÿ€·bYù-HìçÂ)ªk© ÷Ç ¨]è#’èò‘ ÇËE“¯®€Jå x9€eOehëIßiê“XnÔŽÖlaÛ €ö6Ž%¤à|¾„™ßÁý|é$³i—CQ©û&ƒ=HË·º8ø Yïukæ± Šâ÷sõ/œÞ“¸Rûõ&3 Ãëú)y”ž¨²ŸÊ~6ä~f”•  Vƒ"äM™p“ZßÍ£Û¹w[¤À¹=g¸šM„µêŒ¡9‰°×6‰{Ó$š±ä¶ Òf³ã¸¯5,aàÄ®šf6´t¬£\‰üae¾ z}3C û'‚åȵÜÊWÄõe"ê? J,+ùÁ]¸þqþ»A½²w“ª\ŸÆXÚ…ã"Zsä:­t\ÃP©|Ÿ§î¤»:¥0ÔwU{Zð~?÷äcöe %èËÒùÌøN±€f•ª³â>çê®Ò N^ËåJÝÿû)IñÀÐU­Aeh9×C5¦¨žç#º2P©ýݼzÃÉF€æ¯jE“æGq•ýTö³!÷3Ïá¾87· aõfôÕ±»õ4'Üå  UëãÉÄÁŒûmÈUȶõf¨¾LÅ*îûÊ}Úž†w>ÎPÆÕDÕå·Û‡ õÏg'úm×H¢½ÎP©üÖ÷BqÂd†&õ¼S.ro6u£V+)3*Y3†ÎþG´´§ÔÑ ]°H<ÊMŽÉ#zùE wú5g¼¸RùT,† /!GºÌº雟ÏãÎ<‹*D´wÜöðU"E›/ܪᱠ}1åL‰:)¢R÷/Žï Úaå õß3:O{È]¨ã}ìwZçèBŠf.ãJíÿáuŸ^ôc®µôPe?•ýlÈýìß–ö,‰!¨ã¬¿èà}nMå}¹ß¾4@ŸÕM¥¹G6qcBY%¹~ ¨ÁI,Äê7&Rº04¸ô'léÅ«­EôL? ¨É¥$µÌ—¢’ß_n@¿=¥ý|TÆ·øN;:èù2†’ç±dÍönʪÝÔÂÚ‡ Öò–ldÄ"Š.-zå=ŸôfÍxóñ Rùœþù¹ÁpþE†Z™‚˰k\á‚'ÓÍÔà¶°ugÛVÍà–‡mƒÂCN€þžÑ ÚŽæJÝŸÒx ¨'–1te“P¸b›k5c±Lý„9 /‡¥R뵕Úÿ;æ—X#k'¢CÄŸy;(ªì§²Ÿ ¹ŸaŒhΞ2Ô9þ²˜·'MD×§Òr/w@Ã*ïÐ5ïGpǘۃöÌ}Ü‘r2|I wñ¼wàtº©S˜ é ¨ÆÞu±[‡ †š›‰oÿ€Jå·ª¿nódhï%×àEÔ‚Öžw„ŠUÉ /Ù;ª¼¹#3};þ/n–l§p9-˜»ht:íMÎzeÞ&º— Rù|Ï­…{+K:·i&dºp›±²6»¸}Ý£§Ù%nÁ[QfpÌÐྩJæ×3¨Ôý”îqáw¡íƒW}/qe6…ô•†Eo×M'ç~q¥ö§æ_ YsßRôÕÖÑ ]¹JD•ýTö³!õSñÑZ“ 7t1¥¨âÜyª«Ðï ¨â<‹î†µ Tq~ñj $~]¨â¼@'>|n¨â\*¿fKÐv÷A‚*΃ƒE8ž¤ÅÐûó•} ¿înd¨âüÁ5sÖ¾OEÿ4Ÿéˆ[à’²Ÿ¡ÿö,k+¸i>c\…gL³³äÀ¯ Šþéý£°`e¨âüÙÐ*}#¢ºßè¶&t0yMÐ?}_ÙOe?ÿ“û&ÿÿŸãÿƒ§-ÂØ˜&w :äp[Ü=…»=¬+èv hª}>©ŒêÎ};+¾³®Ü{zÿ÷ßD¸½÷ìUŽºStÊíµä]Äî«™ãHþçB@«5æ1w¯c•Êï<¹½Òt6C »ôèørs‘ƒr=nϰäâýÎ\›áþùP@sü‹ÈÆžm¹¶Ãg—C Úê8íøDQ©|ËŠÚ2¹ýcŠöÙžÖžïÌÐóÉìFmW@5âØògºžîÃbw@Ý"“Ž9w¸R÷suÖ‚u°Cw؃C)áF'ïÕJZ 3^T [= •Úoµ˜Š«­ÒZl1þžs€+õ¾²ŸÊ~þ7÷sœe ó¨µ&¨ö’8–¤, Þ¿ç‘ßgkêùÎŽ¨ïã:~.>„Ôañ;ZjÄ¢Óì¬'SÔ¤Îô—$t–£œ^×»MÑsÅGH`ù†JåOz“Lzfƽ¹AÊÕl¹;\ˆÎš«M ]EBbŽs›îº¶C&ê´ž|³Ü•ʗ– M×}µdhQËK‚Ó‚áÜyç÷±‚½seèµÑiìöþÛ]h5ŸÙjÆz¿oK´,P©û£âÿÉh$cèÉYÛ!x %·üéb2"£Ž¢I&Ùä&WjÿœOq0zÂj†Nнëzq•ýTö³!÷³Ï‘D6òY²ˆö™ÄX”EËE²\Ç ÖÓÈÔJWîZa8›ùÃÐ=|™Q} Aý§Û¡ý_r_øŠ…ƒ×rw ²†ÄKÛ4S?ˆ[r¥ò/¬¯!vSBzr–¼±–;'ÃjºNãïÉ‹¿M¸cS³í¯u+Ü#/¸ÔV†:ÝmN½<[êqž^Q©|5v’+wLšãüœ|ß«Áýýr+ëÕ×™ž2–o•Ì}t` 3Û>‘ëø:IL]þ”+uÿwìNgæ µ5Hçͽ¸Ã…›¶P´äòV°¾ÙŽ+µ¿*½ ý‰è¹µ0 æªì§²Ÿ ¹Ÿ)³w°Ó%=æÇV˜j34£°#éÝn8á>>$XEÐØi ,5j.Eç…‡1MŸ í5¥Íéó]@Ï\- †"ªÒÕQþMe3×Ê÷+ÙàÁP©üv9Óa±µ+CK|ׂúîAÜA*÷!xÆ8жIi7)AŸï6"¹w“)zÌE lrNs{h¾&3²nqŸ„"É~Ò¢JZ¤%•TRç¼^!E¶$I¶d!Ù~Ÿ™ûw½î™3sßã¿ûnÎûŸÇ̹æ}]Oyšê/†_’(*qˇôr;@GlÚÇB¸=Áòd° }Ößz‡!hÓÇPrôhWÔï]ÜàÃÊWá§-§Ü+Èû`mî…ôGdê_'hÜ´‰äšÔßIÏK=9nÜÞuP¡û޶îÒ`¨nRÄnýDQ%.ãîÞŽ4}Ø'ún£Whÿ­´pêý.PýYLãz0WÑOE?›s?K÷-e÷O‡0tÒ+³rˆ;æÃ]’GÑ &à Yûwp„H—ç0tã¯ÐvæÚ‰ÆÁšÆxnJöAXñf!7àö$‘S 芗ëXòî¡\Áï_»Á{KŠÆhn±G„êôbTˆôÝb˜ÁµÝ×rT¸–ï‘å4¹õ•1TùšCÝ[¨Ãµ™¾\¡|~åoˆþ“ÖMÛò…Œü]†=:†—0*½`Û©…\ÍÞdtø$@g]Õ¢³7%¨Ðýê0Ðé&EŸ•Þ¨ªd®{ÃFöe^7@#²ÓÙý]áÚ¿´E&óüuŽ g¤‡˜M¤CýTô³9õSþÙÙ¿#Û=:ƒ¡òs½%àêU)CåçÑ‹^ÀT«á •ŸÑfà¶H‚¯9z`ÁÌc€ å[²Dz,o¤(-èûÏ¿Üõ`³ ƒ¡ƒÒ`Zi(WóÛ-èº.˜û­ÿCðnaGP¡û SW’}“gPÔÍÅL/›ÊÕë –šp-Ž öJâ í;~v¥]&¨îp?ø¤î¨ÐûŠ~*úù¿ÜÏ©aÉœû mÖúþjõ˛Ìç®è™B—ŸÊãv÷ì£üúpƒÀqçmŠ®þÁ–.rä~jÜÆ$glºú3%’ v\5¨óu ¨Pþ ñ`p^™KЗç»Ã’o?¸ë?¶òÔá®>ÿ >ô¨‹½”Ø+ï¡‘+)ѵ˜ûÍ£»ðp; ™GXÒ­÷"T(ßÍ¿ÞRtªî??CìUÏG#ß\±‘ C쯀ê9¹·FÁ†ˆé€fÅ¿'c¸B÷sÚ­'U]¦P4ºÓ&2ýë¿­zr›úôÝ ¨ûã lk–*Whÿ¾¬d–? KMûÒV5ݸŠ~*úÙœûyíÉ Ðš¡ÆÐµŸ&Ã5FŠT«ÃÊPû7*ŸšÉ­È‰ƒ„—ñ"4"-OzGÐ}è¨K2†>}&ù÷¹VF»Èž”‚Ò+‰ë€Ê#ôYÓ² !ݲɓÍF\‡B)HuŽp%c× QY¼ka!Wyl#¬ÕrTéLGé~§ƒõ >@êÆ}â ÝWŸDÔÏÚStñã½d=[Èü5A¶enCß.[ ÷?^à í‹d&Ã;QtÎÁE¤&SÆUôSÑÏæÜO‹!; p—Ec_€Í 5nÿÑ¡0×–¡¡oßÜŠ}[‰IM, MË^Ò–7#¹¿‚dÈ84aù9pøü… ;ÆXzd# ¡Õ¨ó¨\¡ü’}©}}@oŸ¡MÃT¹…¾{h¹R:W·Æ”…ÜÍMR¾H%ôMTftuSKQ¡ýß{‚™NÑPOp›áCPE?ýlÎýÜó&”ÜßJÑö¾`ÓmÍ:xîg>¤èAßB,û!CßFø2ƒç½Õ×cKÅÏdhyû ¸¯²БCSHùHný‹ƒ¬mCÏ:O;œÏ`¨Pþ¼kT;¦˜ w¿¢YÝ·p‹’w± q}=¿2šíN7C_}ïAOªveh©d‹¨C± wycÓ œ!BÎO¦Ë3"*”Ï=j"‘t5¡YÇÇÍ3ÆÜ>#D°ìîc@Ÿ¾ô#ã^=ä*_&õ>P&CÎgˆ®ßЦ¨Ðý<DêéNÑÐüsÄø¢×¼©…8x! ¶üó;¤á®Ðþk{g‘ù®Ô2ÖHæšbÎUôSÑÏæÔOùgá \÷ÏÏëÿ_ùyŠéÐ*Ý@Pù¹iàR6ņÊÏ7ÝÙDß-š¨ü¼ºÕr˜z)¡òs¡ünŸ©ë˜**?w«ÞÈVÌhÃPù¹aë ÄÎÚŽ¡òs—3ð\4“¡šïËûNÄæé*?bß–^m¸¨ü<¨MII1ôOï/+&õ×PT~Þq¥)ñ°½èŸî·¾tR– ùÚ ýÓ÷ýTô󿹟*]Ú‰ÿ/]óÀðfC½m®€Ú6PqŸyÒØb)W(ŸWSÙW³— ï‰^ÑDnÖxG™Í *C%,o\…#E§®¿HCRn1txâ¨O:Ǻ¿u×[xØŸ¡Á†õp$° ·çøHª4Ð&­ ¿BóTSThÿŽ·!¤&û‹ Y<‚¸¶Ì”¡Bï+ú©èçÿr?s¢îÁí‡+Z6å9,]>…û«t(³^°ÐYq[X¦“&wÇŽZú#s CÅOÌ¾ÆøsG­ §Ÿ6R´â\£LçV+†Çe¥F‰€n‘aÇuOÈP¡ü&-%¬F÷¹-ظžéwØHQà ©¬{y4Ck#/’2ƒc\é€6ôË(snáš“DEÕš;º[0Ótœ¨«<Û2ƃ¡Bù~½$äÂj\NlÄq\o³Ã¢NSÂ(ZÚ¶±µMæÚ®º öß^sS†ÝƒwÎÊ€ ݯNþý5k(Õó#mHã&d‹|¯1ôË•PÕÍ—+´ßuÎ$’¶©€¢t`úóö UôSÑÏæÜÏ áAf†¶›ýú¤~¤¨‘C8{³¡^„V‡²Y¡y­]N´[1ÔE{ŒRñæFÞ‰&׿à–xêüŠ¢#NäSû÷º¤ÝEH=ÅÊ¿bÂækXMQ'kfîÕ¡É7—ÙC‘Ü ÄdÐ9`Åíóz(H¶æî5AßÓStê‡ d`S%Cçw¿ <|¹Bùì"â‰ÃÇ­"4ûl‰¦;¸}7ê“qK/PTÚÍšd\üȵˆµ¦·§};½˜®|îǺóO/ÝLWQ4Ì©´$ÜN»2azô[nìmH‚R¤¨eKqÿ·OZ×} |È(T(ßô«QDï¾õhHœ¸m-ö÷´ºëâ7ÒØgw–Ìñ–5A‹Ó[0·O·(*tÿÞ‚w;ÝP†n/z )»&ˆÐä¶¹ÐÃN h@îLh<žÏÚßé  8h:ú\pP1ç*ú©ègsîgæO51õù-BOø«‰›FfÔàv4ê›ÄÐÐ×?ÍÊ¿grkFúÃÈs€VÍî Ct¹gó2‰»çvÚÿq±Ù¯Â tÄô7,t£—³£¨Pþg鳯ÛºþBUg{¹?-Cà—%èYÿe`bØÄ]˺ƒ{y¸í±¡%,%è¢ö¬zR ölÓsy`† åÓ5]O$—ä£1_£¨à÷Ï’eôÓÂC m|ì"Û#ÊàzÞZ¥ ËF¶ƒåšFÜ­w“;*éwTä•8G„šþ8Hír1ôé?½–”,ã å¿ÜKžéP´l˜ŒÄîqåzö>¿SnÏ”,ÐKµ•¡™qfà|{²5ýд¬ *tßfÉcHyNÐÛ¶a‡á9îË0¦Þcš n¾™UÍf¨ÐþAç70·:#‚VæÏ`iʽªè§¢ŸÍ©ŸòmâЯ–*?ÿû•?´öÊd¨ü\¬Snv¹W1Aåç ÷Ô‘šoO)*?È—€Ù‹­•Ÿ åŸwH‹ÔtÌg¨ü|ÅÌ`òÕx òóg—;ٱ핟«Ó8ð«_MÑ?Í¡ò“ÌñßNQùù_±'` Ó^‚ÊÏßM'ï\ôOïßìZ jw«*?õ0‹~‘Þ`èŸîÏér„>ÈÐ?}_ÑOE?ÿ›û¡ÿ¯Ïÿ“]IŒ èèù†P˜å>k¹ÍÿÏ­·fú–Ü ÀÆ‹ |×ZìœBQåÕß@uõ†:Nm#Îu 'hÕ1 FžËzÕ:”óq…ò[wéNôÚªt‡æçh‰¹ûÓ»€–¶;C·%ºAÚ’éÜŒð•0Bo;×cm ¼š5”Ú†¶=KQƒdUÏT®P¾÷v#v(E•~eÔ´%CkìcIñ¹V+%ÌkõV®ù‹²/Þϵ fã¸B÷6©‰§OÿDÐü=­Äç>誳¾=t|¡í$ƒÅ p®Ð~-ó®dðQÆÐìWs¡£y2Wè}E?ýü_î§Ö_ÐÚàèê®àçãÆ5%²3×Ô¿<3„:škÓR|3¼ !+ò ”ãvv+‡H•ÃܽÇÂ緹k¼…‚œ;RôB¥<¬ËT(ÿå5ª´¼BPO5'ZZö• EÓG+1ô¹f2$ô‹–¡ßº]ë!Z]<ó$«?$èÝÅ·‰’ÎL):Jù&©o´¡BùÚ÷:À®681´Óßfãý‘>šÍ?˜ÅuèA%ßåê'ïfí]¿Q4ƪœ.ÐÍf¨Ð}ãUÊbûIã])ú õs¸k•žÁòÂo øû8¸7Th¿¶ô$Ìmµ’¡ÓÇ>‰“7EýTô³9÷ódÝTÈ¿¹ЬîC@æé½lcýŠžNZÇ|e3*µ0ƒäÐb@mnDŠnè_â-Ù){y¯˜»éž˜¥Žá¶i{J·Ïà:_4e)kQT(¿»¦/ýh‘FÐiýwPË…ã¹zá0*3‰{c¿\ðhhöÑ õÚ’›¥Ü2›ÍÕn Eç[ÊСý¾“WÅJ Ê—¢7†)Õ0†ÖçÒcpsO¸‘quܪ¦‰ðlÉn~ÖF°>r‘{ýñ3pØx‘¢B÷­ÀäÑ@gÎ|:7‚¸ÁA‘ðüF7vt/0ãWhí²Ûp¼/ Ãj=ÁêᮢŸŠ~6ç~jB‘¹ !§êˆçn«;c™ƒÎ^†¾/§îC³¹ÙêY“,P×ááìþv ®èK2kûT™ûæa›é¯ÂP·µKȾ>Üäªå°Úë0E…ò»YH³öø‹ÐCŸûPç!MRÔ䬋î¨ïYJ2ÿÔ`Ó*Òm’1 †óV‰Äþ¹3Þ.š9¤hdœ=< Bù Ÿ &¿dhùKÒó÷ÝæZ|‰—qÌ|på‚ñí^€gãÁ~|$Wè~¢é#¨8 ¨çØÛÐiín£ò:åãrn¬ƒˆ ¾w† Bû‰Ê,RŸèåUÔÙ,®¢ŸŠ~6ç~Î:E܆š2`;)ùìϧäÿÚv•¡Žûɶ…·¹²,iÆU*yìÃÒÂÖ04¾“)}ÈäÞv$W¯q_tíFíkäAT®Õs¿>­e²ŠŸ…24Té4i@kŠî.ÜMrº–´±/ØÎç¹s–6ÈZ-©á&§†Ò‚¹“¸Uç“çS6úÛe,3v!¨P>Õü¹Ð°ç!C S#!dwWäüvN°æ†*}ƒ¶W(ó3…¦ Ù¨o7sVªa%Bÿ~=ŠÀ²Ã @UÏÁº g¸þV,öÞaЦtH¢k†la¨Ðþ!½~ÓÞtÌ þLó´® UôSÑÏæÜOG¥ndêX@Ý­'‹¼•ü¸j&[Áв¡©ÒÓíÏ%­ÐÒe™ÜŸ¹¹Ä¨.‹;Àf=ä*çZ]·½—r-öÏ&î›?ËÐË“¯‘„Uê"T(ýúZ3˪ž5™"³º?×Rs¥¨ª!ž  E e!•]¹¯Û旅ÓûR´àq±´è{5÷½ç':èé`†–}H'Úóq…òíx” ww¦04lq„Ü à®~þlF|ÌG}¿Àéª|‚Nß,¡c/udhÍñ%D{ˆ+t_E÷8h&Q@=«bAyÿ%nì£`·'€¡G/n€¶õ¹‚¾=å´¤¡7CÇ<1'ÅùŽ\E?ýlNý”$ž‹eùƒv*?¦{ZŸÉPù¹câ"è˜ÍPùùÇu`¿Ú¢òóµ×”@™t%¨ü\(ÿ˜Ú‹¢¶F•ŸëÎ6“e=!Cåç£Ö%’ev/(*?ßÓöô 2fèŸæ³\ð fWLf¨üÜCöbv@åç½ß9Ág¿Ž ýÓûÛ—î ««€ÊÏl‡7QzýÓýGb Áë­Cÿô}E?ýüoîçý®ÿúü?¹Âò%”ì~OÑ=õõ0im×[*íŠóc¨f¯BvqmÈèèYëý>mûtôAm‡ôNXPO.‰&p;¦0¥Žóóуm‡ÓÓ‘—*”ß질ÌÑDѼ–aä™c{††:íÕL hid•Y~“)Þ 7CµpQÛÁqÜ—þY`£¨¦ÔT{߇¡BùTƒD,îkC’&ÜGÊ­mX˳Թ*óÀñÇQŠ~ß4Ò/æ1Ty]5ôǺ¯÷€¸®í¨É©Ð4€»oô ˆ,>εÜêJ¶%Üæ íŸÖSF<Û¶'èथÄÙå­*ô¾¢ŸŠ~þ/÷sãçÏàš[.Cw>ÿ =®2ú`¹>Tm a¨ûõµ¤âÏ >•ié÷£h»†®Ì¢so†JºzŠ~|j ¨»t–¬ãò@‚~ë–}#ÂÚÞOYüXµ BùŸ¼I²uc(éÖ mºs®‘Õ‡8‹P ÚŠžO“¡m%¢Øðý€–ô¢Ô¦«7wuÃ>ðÒ–0Ô‘TCýÙÊg÷’ÐÝ2ÆÕ7$ö5ÜymvCMF }e “߈ДØ7PNêg?ÒÎs…î_Žøm è¾®ÇHÕˡܑգY¥×nnÙ_f°WBP¡ýƒ¯‘ÜÞGeèÌ1H`z4WÑOE?›s?»8}ãNÑwúã\•}Û&ÞŒvfhVX1ÔWuániD·¯pú¯'e$‰;îÕnYã<Zïž#ÓëÝ$C­|Šˆ~ù3@çÝÃ\Œç åž? #:3tìä™°öw ®÷„n4Á<‰¢¦úÒ]gº2ÔAC̾ÿî h/7&Þ?Ý ukY&)€v=Ø’=ë±›+”ïrÇv°¶äCÏ¥¬ƒµ·ª¸úµ>ð~A ÌÖ€WÁnñŽ7t[q0 ÷=ŠêKmÖ]a¨P¾ôvÇávn1Cë[Ý…è1Éܶb{xf¢ èS¸ mµš7šiD,fèŠ_oÉ]Õ4®ÐýèßsˆAï‘€ZŸìOfmŵ\¯›ŸehŸ»Ù ­¾œ+´ßò¦¸VùSôÆóÅP3¬^„*ú©ègsî§R‹X¶?Ð'¯OÃC“Xnïƒ×¡áÞXî„àcPÕ)˜ÊÞ€dõW)jsæ&¸í¨Ô½7¼ÊèNÑ3*àj"Bõ~ük-gŠ. ÐÐ6 P¡üå#wBH«v½’£±ãМ»P¼\¡“ƒóÍ ][@|&24júè»”kS’ÝûGs»>û i³+D¨P>½V_!cÁz†:ßJ\àß‘»¸k˜>. í‰·“×þðQ¸²¦'Cë¶¥BÝÏ!ºoó÷{Q¬õhàv\òoçY”€Ä®@ІÀa0©×ThFæ`Xq«Ž ê«ó‰ä¦* Š~*úÙœû9AëlÙ‘èœþpNí×aã*(VIæzýP…ì YÜ•S÷B†e×õ£:ìQ;ÅuÚ~ŠhVû‰Ð€ÚS¤ Çb)š«Í\GúÚ)†©îR“=äËmb!PÓç…:möN<Ö— …sVƒé8†¾y·¶›SôÞ‚]PשÐ5ÍXÌ¡t®P>j­!ÖôïGÑ¡ªâ×jn­ù´ßÌc )Schi''îå#«at/nf¹ÔQÇ‹+t_R¶ÕìGÜ(@¼ueÊwL¸Çˬ`œÇzîžG­IDS0WhŸäáÛŸ•ÎéDÇh<à*ú©ègsê§ücyk. Ý|Pù¹4¬;™ñ1Pù¹Ñž÷²€É9€ÊÏ7ÆH‰$'Ê •ŸKÆ7ɪöå0T~.”_]ì[ç!¨ü¼Ôk hý• òóå-âÀY¾ •Ÿo˜u”½YT!Cÿ4_è»pÉ­  òse;-v&© òóòeM´1|4Aÿô~tû ²¢§Ã•ŸçÕj0ûÎCýÓý&S_Q#Û "ôOßWôSÑÏÿæ~>ýô¯ÿWî?YP+“Jœv´Ôê€,zÁ<®§é$ÈðlÁÐ…·ÃÆ=—(jf ÅÃ1´ß9húMÑô‡]iÁx ÿª¤½ž$h}á(°ØnÌ5üô„ØŒ:ÌÊ_÷¨ >ÈP»IÕYTJÑ}ù'@)a CŸyUÁ“Mjqâ1ô]‹°:ìHÑjŸ§`¼ÜÐQ;{‘÷F×¹Bù†RŸÖy@Ñ!jâÅgÇËÐMª ¤sÞ=,#C :ïD"|P»ÏETûòm®ÐýF—hÐÔÈ¡½§GÁ˜ÔÖÝòm6ˆ—TjÝt–_-ä íŸè!«u_=š t?Ìz_ÑOE?ÿ—ûi“õRælÖ‘ eêíi£­¯u0ß ¯<(ºö\Ô¿"COJ|@ïC šõÂT÷…Ôд%3&J†ÎμG{†u`¨’ó0Ó ‚^çJ>~ ¤¨PþéYOàFgІ­³5/Dha^$ÌìDÐÖ“n€k½P£€·zýAõ~BÙØ@+"ØŠ>ºÜ€¡æmîÎP¡|ÃÔÄK%è°9JâÜ€¦¤î†¬ìŸÝ6jìi×Ð{J©,õÅI‚îüèÆîëlb¨ÐýS_"À¦!¨’Ó~Ø/öà^N b½'šZÚ”Àì~/§¨Ð~‡q=Y¿ Þ€Î}áÏ"'TÑOE?›s?[¿ïJ%º!Rô‰CZ‘zY†Ú¿ €klš ÇDhï!1dßm‚Š#2‰Í.-Š åŸÔV]œ'j晵óVsŸ}îH´ Ò¹;/$Òž¹QÜ·Æìnû\Ã^QìÅ9C®}(ƒ;&¡\ñ²Ä {\¡|ï…#*ÝbkO^p ܵl®åÁ“"Ý;îØ[¿¡êé+‚JÇ»ƒÝœ|@…î»°&\"èÕŠ`Èøˆ«.Ƀ뙩 =|° ô)*´êH50'2´wf hõå*ú©ègsîçeŸv²é¡)Zß&É,vhW5ÊŸHÚÔ9ÝXZÚ…ëØb+uŸÛ’¢6¾-dñ!ܰw‘Ðq¿. Õ‹&À¹ü Ü‹_^¬M¹"tm% _#¹BùK‚#`Øá @+Zo…•Cp­%cÙŽ‘K¹³âýسCÏÚ6+žõXÓ‚¢‹§­gçÜ'3´ý\V’š@Ж û±~ϳ*”ïÃA_Ò~âK@;êö £†?ä¶½j@XW¹òõݬÉÝm’H-NerÏÅd/ÖTè~¶ßN˜žÛHPo©/äªkræKÇrÕ“w@ª,®ÐþJ‡jÈ^_DÑt½'=RPE?ýlÎý üqXTZDÑŒšöĸºŒÛøK&Ûýä6A0-êÝÖƒÛ¨ÓŸü¼}ƒ¢ÉÄ»ñ1·Ç¶Ñ¤bL( —×¶d¶÷–qÓ«ôˆ4Ïš c6•‹ª½c(*”ÿÊå©ð¹è( Åƒµ@Ú+“Û´(‚¥oOQÍÁ¾,'kC#Céû-±\½’ë¢Á+ó¹É+·Âß} ¹¡¶êâQ*³)*”ïqtKf&»¨åÙìtÎQné æâ~€ 9Cº0±ë@)º¢“ S· ahû50øPĺ/}· Æíì(¿Z±Þ\›™¡f —¹]—ob÷‹·s…ö“â½pÓá8 ¡©Äô å*ú©ègsê§ü£²ÀŠl9QIQù¹´_{š¥–)Båç6ÎmA¼°†¢òs‡…k™‘Ã$‚ÊÏ_©µ…»W¶PT~.”ÒÞl2øH òóCe]Yp÷c •Ÿïï7 ²òò*?°>f» èŸæ³ºçÇÞnT~îÝ‚Ù墨ü\mtèTLfèŸÞŸŸ³Ú‡T~®é›Ä¨êQôO÷ψíÎ.µ ôOßWôSÑÏÿæ~>Ëþ×çÿÉmŽ‘°ªÈ з'üàQˆ·B#RkÒíßvK_{EÐk[ÁÉG通¿|J¶ªä¶µ ­øÍ‹êÈÜtE\ÉŠ%àAÑí²1ppG+@…ò7Îþ):µv$CmÞï&'O›qÕæÿcöAF=ƒ†osMwʾɸšÁûý®{çN¢¸3ã¹FXuA…ò9?#â'“e¨‰M%Ñód ã å«jsƒÔ —¢wŸ“‚¹³Y,ÝRëNÑ‹ß-¨C§nÕŒ'°ãlCÉéVâ„åíº?Jéß’ Y»†­?ÎíoGįÍÐè¯{‰öšO2Th¿Á£v´6f Û >S£}ÑUôSÑÏæÜÏù­ÁÍ=З¥y$wMwݯjZ1ò¾ íµð&}£t–¢ãfo~~”¡?×°eÎ Mù«=wWÕø>)- äꟓšŽã¹¡| aäFŠ åo`K ;C5}¦ƒZ^ ®¡íTكˀvÞƒ]ûûwñáÃL}º2÷èä¬ý?Š5Ü FGωÐ]†Áì€ å‹«¿Bô¾÷—¡Nßo“¬Ç“¹ ß×Ȫ[)3ôrBŠhÂήܵ–ÀÀ–·5pmËjìÎp…îºH@·> Ð) \`ÍšÓÜ ×j2þ‰ õä)PŠ¡Bû½jÕíAÑÀc­©Éû) UôSÑÏæÜÏ1C-ˆÓ›@%ãî˜ÖŽà^ß½—nxÓ¡5Ò¥2ß0îŠß4Ý&ƒûâÎ ráñ%®öç èXãÀ=Ÿé?œ)úêº-x~}Nкi;D®æ€ åŸõÕõ¯PÔíÞJ¸6=„7h 37›h;‡íéLÐÅ3w±àÏP§Ç´×Ì2n@ª ëhèÊ,óxE…òÙ|Bô¡[fÔ%Uw)*9¾šŒ²ïÇЪÚäI‹¡\—MÙÌçÝ|‚î¶”e„e0Tè¾³‚ßr…ÃaPd>×Ï7èù™¡r’ˆM@œ Úï7÷ybçÈД.0:½=WÑOE?›s?5(S§I!€.íH·>’p-k7‘Vš«jÜ &¦üÍÕùyôþÒázÏ™Ú=ŸpÓ3¿Â*½\¡û£Wtûi2@ÕK¿’}ý)wPe ÉZ'E[ŸN&¥¹Af¨ÐþþËÀ}lŠÎØêK‰#AýTô³9÷Óñô%úEêèX×oôn%W{ÚíšÄP‹„`¨,hÃ-ûðÜŶ-Ì»jö;Ôo™fæÁ¥3\©I”)·$Ù7P´ÅÓÖT(ÿ©+èE ¡ê©Î çö›ûüÛ7š~=‡¡o+û뀻ܾ_Àí°=7cr+±Í![)úWÛ­àÐ+œ ¿IìàÞ€ å»ñ„<ûô[„©-&{{Ôj´´^\KQÃá 2z7wì[¨\4 з¶Ã!$ëWè~ÈØ‹dà h”ñ^Ò´LÆX™L;æÊÐÑľW2WhÿÍ›s ïÔn€^ýû,q4“«è§¢ŸÍ©ŸòÏŽuY¯~†€ÊÏUFƒÉÊ]•ŸÇØÌš€ÊÏ_ïÀîJ"¨üÿOŽ•Œd-7ôFœK)Wæöé·jò3¤h'¥=@íý ª·h‰4-± P]²€¹ºÆq5ÓD¬ra;†þ~¿‰ªà6È>’Ʀƒ"´hãN¢­³Ï Ê÷æJˆ·‹!è;›Õ÷wÐÉS°âㆆÜ}‘½ºpï¥$Â’ÈӀЭÔaÁÂJ®‡Ñ_̽[C7=qƒh®P¾ÁFédÊ­¹íÚk©6]È]â…–qIø\Ø¥Ÿ[+ÞÍÊtÊ(ZPº†¾ÑÊg¨Ðý [‘Ÿ~E€Š—,4s?TÈ}zŸ¨¯lÏЩö#àšo.E…ö§IXMÍ‚^ègîµb¨ÐûŠ~*úù¿ÜOÑÈù¬~rA+n-auó ¸mn{AʵŸÜÙÛÇÝÜÐ F™,%UJPw¥$V“؇¡O%/Hÿ>q\ŸÑoâÅÍí{ƒH6ïÈG»KcˆR‰—*”?ÄÁ^ýý ÷üGBT›Î€6”5BUÎ-º¬òÐK@{x‡Óm÷nr¯øî`²Ã¹µ‡o€}l‚ õ:,íû"Ê—óÝ…D‡Ù4ºÄäôÉa7ÖÌÖÔ#·–lKîÏ“&BF~&Cïvh! ±¡B÷³LÊd&QçUÕYHƒ—˸vö»±X†¾s²ñ¹‚ íLð£EûzC_TÎ$qýTô³9÷S–1‰hŸ¥Á YÅtKŠÞh£ ÌËPåähRýo“5ú1MY6C[ßÞAfv)ç~*Ǿ›):­Š‚…Nw@¾,!b£‡fè–)6Diì]®PþìËÝ`Nn_@%fŸHéðáÜâ„#`¯Ì8Z4rÏ :ÇZÙeôG~6K˜Ô¡6¨²§&€ÞíáȪ×QT(_ac(¥m“-LÕXOæÚDº+Ÿ¾€JÆ4 ;ÖŽ«êJ+NpÕ &Ð-ƒó¹B÷ã¤Ò¼v¹€n]þ„.:ÉØ<›:p5‹¬D¢?*´Ÿù‡Oí¦1Ô``1ˆ½K¤¨¢ŸŠ~6ç~6´œÍ¬=ò)ºßDÌ”–«2ôrÛ "ŸÁ€–NK—µHëÌ]ø"b<Î0tüÎJ¸0—{ÜÜ4BÃU:Y ªþ+œ[cRDîç ¡è4å:"9¶Ê ÊÿÆ$ú9 ÐÀ݈F½ ×yíjic åšøw`顸…ýYÁ‰,†z_ˆ’¶:ý˜[8·3IÁ]ªœ Îb1Wðç¬#¢ÒF†R¯JQMhW½²V¶Çø6A;¸YÒíܲÈ(vªõ7îsåíìy¬C…î_ÉmËNW¥j>°?Ëi{˜;xHozYû±-©Ë —9PThAùQpÖ1´f¯9hn à*ú©ègsî§nE¶mÈ †:ÜlÍ2­­¸Æ3×ÓËÙ jk’OÝNús[Û4A]ÿ12Ôù÷S(?eèÔªô‰ÇDn·çbÖöŸî Îáï‰å’jÑgé;Þ BùZŽ.¨Íš,Y›Uq+nx°Äë„k~?Œý8?ž Q­!F÷Cg[óٱÜîCò`ÑE@s·%JU¸Bù\f÷!U/FR´¡ÙVçÄmÛïox9C„êø,¡†çwËÐÎ*Jð.éCUý_€$ߊ+t?b¦˜Íêè®Æ¹,¼_—¬A¶Î¢h×ÌËDí×®Ð~ñ$Ñ” {ÜÔ™ùt·à*ú©ègsîgtßrêiæÂСvSÏ›¹öþO©M"´ë¨ZjÔäJQ“Ÿa°o"4© ˆ·ë}î  cVù™¢Ó \èë&†V(Õˆ¬«PÔmÎmâÓzW(Êykš’þ “.í£KgærWnßËž,§¨ÆG¦îº¡ñ>ß¡w¦·ä€²øÈÜ@‚:5Žfojb¹÷žÐÇ-*”/yxy–¿œ¢yÇ ˆ…†-wào1­)wäF·jAsþ>ËM²|ßÏ ÔÜΊö_å Ýïõj‹ë/4㛄ÍöpáNÜ=t$):N<jšÞ›¡Bû}7-bJ¡f¨•U76+kCýTô³9õSþyó¸pÑŸ¡òóÄ­givê/ŠÊÏkeŤ*?_³¨øŠ0T~¾´‡Øxß‘¡òs¡ü·gÐ¥oœ*?Wi8G-Žæ2T~^;«ö¬ðT~Þ4m,ñÝÍÐ?ͧ6ú!݆PT~®gV&ÍôÿIQùyãµ) º'ú§÷u>±¯•S•ŸßÓ…üG¹ýÓýcÌïˆbÏ…1ôOßWôSÑÏÿæ~î¨i#þ¿¬geÔúi ‰’¿XÚ¹ Öô(M†zµŠe…ôš7WøÓG†ê]/jøêEÑ,ÇŸP4ö.Aû< R}"©­¾±Ý0è i™rP¡üÅï‚À±S< i‰3!öÅ .SŸ#¶r÷Ö%“²Íû¸Þ[Éê%”¡öÝ-A³þ,·ÀtëyWnyêN¸;tW(_Ø›ë$av¦µUF TF›¡gVg2ý•¢¿íf«¶14; ƒÝ¼mOÑôÉYÔal C…îŸ=àM4÷¿”¡#>ûóM\ÏÙK@b;È ]d+ÏæThÿííß ~®)E_ª†Z“ù€ ½¯è§¢ŸÿËýÜó`Ztî:mbh¬Þ}øp2š¢»mMÐÐ|3¢5ÀP¡|'úÞ$Fm Eè¡vÅD{wFÍHÖ8õ$CûvZJ{„”r5þÞtîà”OÝe³ºÿàmQ/W¦èf8r­k+îð^¯‰Ý)@Ýü¥õõà í2n)¸-¹ h}í&Z×p‰«è§¢ŸÍ¹Ÿ—Å~쇪 o΄±³ºqk6 7E÷:C?k©ƒ$¨”kfbñ³Qt`¿mÐë3wÓà 6cî‚>šË.¿1T·O*¼V]½ü2 ºœ ¨P~½•&²ñOúäÌaZp„k=.³vôáÔi¬¤â$E·eß„Žm.tAh|ô[èÝ`*õiOP%÷_RVùœ¢Bùæ=”’ŠÀƒ"ÔZçÉÊ;Ê}6–ì|ÀP¯–¶°c|÷¢qüî~Ð7ß7ѬˆS\¡û+[§÷Á*ýq6›¨ P⾩­¡!+lšZTJã¿} ¨ÐþˆA¬$d2 êc£XS‡(Š*ú©ègsîçõ·íQBPÓˆ#¬Ç.î{ó¨½BÚ˜XJ6ÑÜàKhœEÐy£!?c ‰›/ѦsçT0>*á*h¦² вaÙ¸r£ ÊUÒžµy¨n¿ñlß±ÅÜê»­Ùwk†ÖV£E!\ÝÛ&`±é  Ú‘ÇDê­ã¹!ëûÂÏ«MÕw™K÷&¨à¿ÿžÇÉçÇDèÀsI$kF*wö´h›qš¡yA èÀNîím‘Lš·š zy±P£† ÝÏz % ‹>ÊP7ó"’û®š{m‹¹1âo†¦­wçn–\¡ý}×鲕³Bú4e'ù’y†«è§¢ŸÍ¹Ÿ%ËŽ2íƒÓe¨i¿$ö¼{(E-mßÀ©áÀÐ[ˆôfP4Vߟ|Vš¨ñ6=2ò/î¸_üêÞø^Yµ¡hr‹-ÄßvCõ#÷Cúý%ʰԅ¥m×ôãÙ-L#üAwŒ>M”¼bê9Í~ØËMò“v³\¨Òh'ÖcÀU‚nÊN$ÙI‰Ü¶£ˆäLšÊçãO®d‰ÐŠë$ð¹ŒûmÔoP×èÊЉNjâ71Z2ôá´™à|þCcF]ƒ['ŽRTè~¬ìIØR$C ûÞ%’c©\¯­ pÙ{ E§Æ„%k+*´?E3Ò:G2TïÔ=¸ÓúEýTô³9÷³çñxv°wK†ú?ßÏF?1ãnPS;«]$¨Í©'ðä… %»¯R¥« :üýCš>(Zm|æ?š hÅýe-ãþœ1Üzvá:_ ’mݪÌÊ_PäÔf¦ŠÐM“7³9¦(š<5–ùØ3Ô'èhÜ-¤hü²%¬hg·E÷Ö¬]æ†ö÷žC”"ÊP·³eÄíÕS®P¾9ï‰&”ˆPÓ”U¤¾ö wÅ¢–âúl @]kaí˜ î—UÇ@2nW’>‹\ÛœÀº?wv©Ð ’¡Û†=&FvV\í€ñ`åä ¨e¯®²I)\¡ýW@à¶&‚êOÞ ãFƪ觢ŸÍ©ŸòOׇŒ|_ÇPùy–iDZžT~^Õ#€ÆOÓf¨ü\{yJÏ*?/› ´|bEåçBùoé/c ÊŸRT~Þ)ó>ôUµ!¨ü<áð‘Ñ®ý •Ÿ—jœ!—w=¡š¯õU1©?”!Båç{´²`T~ÞþÁT¶Á¥ zsÒ3¢9HI†ÊÏßHÆ3ãÀV€þé~÷]§DAå€þéûŠ~*úùßÜO]Ë}þŸÔ:ξøçQtYLK¤ÅÐ~…!L¿ÉŽ ~{X’m(Eg¼I„eǺjcè®Ðâ¿Ý¼?GÈÐÕvÁHúF„ꕳiÅý-]ÛšUD%1T(¿óëí {é0E¯Í‚zfÁu6þl¶íÕJ@Ó/ÓÌó¦ÜÓBÀµóLŠªÔÁÒ‰¦µ^R ß&ïb(<ϵ%€ å[TïÁ|÷Æêž€-Éwâ*9`—·î¡³. dzký(j×”öÆ#¹ºÇAýòÀ¸¿¦Âf ìh ’ äLêÉ­}hÈBÞô`hMvhpÇ ®Ðþ þÛÀ"Å›¢T}Á¯t3A…ÞWôSÑÏÿå~Ú~ôb§6Z2t¨h6‹½ìÅÍz¼]ñ´àF/5d;¶GsßíPÏ÷{@PÒYV–i€^½ü†d] ¨gù^2uÛQnÄæS`Q)a¨CÝihЍPþಽPSj%CýÎïƒèU"Ôy©1ó”'è%_KVqP4ãë0½­ èÈ6­ éæ0îÉ{-¨è~5·lX›ב Bùrߥ3!Ï šõå$ |;Ë •LèÈrý»2ÔüÚ㷘›òc8ñLhé,:'̉ B÷';@ÌAC@ks§Á¦#¸[•—ÀÝœ9 ­Æ@Íï9üúKDP³¦ ^*Ádq„WÑOE?›s?Ýt°÷s÷2tê†OtfÒ!nCûÙ´ÁƒqO…f£¥Ü… ZÀ¨ðG€Þ(mÉd?ÎsM׊^Ù†tÐ…öT¯µ£Uúø·ìùõ0@ǘÉ*õ¡¨P~I¨/ !hÞþ]tã ÷÷FæâmÂP × ôBr0W²Z•$¯ï¨Ûž¿èë jïTNC|Ï3´¸¦Û%r…òý€T6àé-ŠÆ¶ÙÏæÜ±b¨Xg´Ìüùn®Ûª,b®ËíÛdOE[ìª'^¾q…îw^,†þ23@ÇÌ0†$‰ÿÖ{'´ß~‘ VýºC®úh@…öŸ ‘PÇÀ>€¾èÖ“]ë£NPE?ýlÎý<ô<„†5gè–þþ27­n²û 8²¼˜{¬g.¤ßIäæG`Ž˜:Ø4e¶s¦¨ã-úbb Wòi8ùuj Cw­>I<ûs³ãÀÈ·3E…ò÷.@ÔG@¯öY CÄÜÕrÈû± õß² ôcvs•ãçSMËŽ"´èE”̺p Eï]ù u1ÃMšaJŸÝæ å;íìÊTz‡24šýżä:̰‹ÖÑÜ¥¦p`‹„øy UN”¡#—Ö='‚ ÝWÞÝ~ ™è&C-Z;›[swº¨Çõ\›´ƒ´þÖJ‚ íßs­-“˜tfhÍÆ6¢´7!\E?ýlÎýœÙ¾©¨?ËИËçHSä¿­vy=gsg‹•Åæ|)Ü´–…mÜÍÐÖ¡šTí[×HÉÔ ávJÛ¥áí jÚփͱH«”T(ÿågc¡çÀ…€Mm ÚíÖs#Ÿ¤ÂóϪf|‚¦ï¥h¯$»[>÷iÚyRîyŒÛqd8»Qx‘ Tcs»F1T(_‡+©×ñb†Vºô%Ze\¥r ÊmòDï÷§èô÷{‰–A´ í–Bj¶&HQ¡û£†©€êžy€ê/|AŒ»-àZ?˜JHRÔÓgQ]®ÂP¡ýÑ—œ`K§`†"·¢FŠ*ú©ègsîç£w?ÅÐ%#,`¨êqîý-Ä—ÔOTùõshtwôîÄù0ßú.C}:TÔ/{¹™êáPx<œ c{O‡Z%4AËOvÚðE#¯ëƒË•h®P~‰òq¢ÔIèÒ"ñ€í\ok7o'Aß?Ά_K@U7þ$+í)Ɡ YÊdhïa¡÷9Ž¡®ì!êž"¨P¾³K:€ôó-†ê]^ Yæ—¸ëu*añ‚þŠ(†¤Ž€öœSJœ;ùHÑNKêHϨ–º_êq‰ŒÚ¼Ф³GHȱ%\‡ÅÚÚ;•¢[þêî¾2Th²[!´šMP·º@ˆÜê ¨¢ŸŠ~6§~Ê?kSÖBÝêC •Ÿï¿ž óž¦*?oß±µx`›v•ŸKzú‘Ã[—*?·¾­ Zí *?ÊoæP(³ŽöT~ž£»ZìŒT~>+K”¦ÌGåçÑOï‹@ÿ4_ë–Ç °Ó †ÊÏÏ_ISÇ€ÊÏ3ÖO#? vôOï;¦n#—–*?‘¢ †­_‰Ð?Ý:8Œ¼:|Ð?}_ÑOE?ÿ›ûù.´µøÿÒ|á HdèÍ_þ{wUóûþ?I¦$$‰H’$„zÝ×F¦„BH¦Þ™2eJ*M*IE‘4IJ©ö}gJ* Iʘ$™Cø}þký¯û`×ú¼–³Ï»µ÷Éãàr_×sµžËÎð€Ûä:VáÁíîŠÍÝg̽Qº—}sK¦¨ŠÐ›Ml bh|ÃUr¦~÷äé ?cÏU¸”­P}+­=ïNœ»þ¦¨X~+0„Ÿ/@ƒ¥ÕÄ7Õ›ûѱ?Ùñ8×ÈÀ‡-~­ÏÝÔ!Šå˜Ídh„uæ{MÊõŽºLÛ^´Öæ"Kܺž¢¢ùºH§@·N·†ùÜFsÖ¿r;·dêÖ0£;×®bZVIÐcµæDQe" b÷Ç> çÏWêÖª3h|Âý,܆õ™N]¬Å ¼B¨Ø~Mß3¤$ç:Cÿé~ü…h®Ø{y?åýü7÷óræ{rRe )—Òˆ}—9ÜÆã™¬F3B@/(œcå ÕªY¿”Øo¿Î]8ð¬4NæfÄCݪÑ:šÁcw3@Ãz€‘Ê>жzn —k­ËïRè@Vò4HÒF¸Ób#W;$„EÏ+ÏAuoakîôgèØçÉ]Ûrî¢á0®&ëtg¦œùœ;¢ÓgØõh&W,ŸaÇ;$wà=@sÛœÊyßâ6·Ê&€ÿ­HÐhG?Qx…¢ Ú«±‘§ÔÆ«šNtžÄP±û´±œØWT¢±Ÿ{VsgêÀôܬ–-Xy\,WlÿIüîpŠþzV õ“W*ï§¼ŸÍ¹Ÿº-Ö’g¥óMë|Oô|.·f”/»ëw¡VÙY½Z>W=ì œž¬ÇíRõ •Å4ð€~q2 ç–ï¡ZݸëÕXdPneŸFz¿b CÅò7…·¥‘UÖ€Ž<âG3[ãÎÉoÅ.tbhœö*Áds2wÒÜ*(ð›È­ð™¾€~”Ü­P@J#hM^>W,_Æü‡ŽçºüÞ$6~O72Õži&-bhù€6ìVÑ1n¦ú0¸`Êýu¿&¤RTìþ±-ˆª æ.‰9vosý£Ù„3Gúu¸ Z¿ƒ¡bûƒWÛñ–Uq‘f\ºÍ•÷SÞÏæÜO«§å9%/gšŸÙ‹zE›sOÓô^0Ôºq(Î|Æ]Ñ”šóúS_ +йÛ]w³¢'ý¹yzA̬.‰¢ö>~p»C_xJ/;*úýÒ@éÆÍí ŸPMë¬r ÊÖ¤x†Jzg‹<¹¥¥ßÕ Ðß>°©.‘[ŸÈj×HÑš’79ƒ×3T,ß»Bof¡èhð¬cÌu²27—zK· –2tŒ^ y5ç:·ç£˜ïï ¨BŠËèËR¸b÷ Òð3 -=FÇ\¿ÇÛ|M(r+cè–ßIàU—ÄÛÅò–˜ú¡_Ó4zHQy?åýlÎýlû*nÿ>PûYôÄom®ò¸pJ¸ÊÐS*aü¢ýÜa‹ÎR…·—}=ÒŸíz¾‚›Ô“]9ÉÐÚ~©D£s!7ô‚te»›€ÖŒgó§÷¥¨Xþ¡%hâƒ9Zõ*Üë/ ÷o@yik†^y¦l;, e³|HI·Ë€î¿þ›nYÅýñ³ ´`C‹l.@þÍË×Ç|¯?P!ö4»4"Š¢®‹¡8æ"C÷]€=!܃³°é­_tøÌÁ4ˆ¡b÷“?¾¤ë§:lpWöÉò*w¿Y IüdoЦ©=…6;Û¿ú²³I>ÏÐIšOIÙšJ®¼Ÿò~6ç~žÉþC‹Þ(j}ZƒŸ˜GУ$91ª =W¨$©UÔÐúĶ~f°múæÃV†û24pZ ôª8É-©WÜ×L¥¨‘Âb¢{õCÏ}úßO|¥¨Xþòÿ|;%ÖKQµWhnE{äf@¼_@ ÷€NÕ^®±+›ðIƒñÎ‡ÅØÌ¦¨$ëºtæÌ@¿[Àrƒvå bùÖbó×›0” ÝÌ|ý}¸¹CÃk¸’/Ÿ`çL/Šî\> Êz•24Ƽ…ä{hEEÿ¹2‚©”fª¹ÉšÙ~Mä¾yf¥%¹FôeeËr¸bûUÏ_‚6§úãL+‰ÛÕÕ•÷SÞÏæÔOÙOh¦ °› ²ó5gáãm@eç1ÿL£MN*;÷XûÌì­•_ºu–ÍTv.–¿vH8-ŽÌ£¨ì|Rõ3°>Ðÿ“¯Ó0꽜¡²sßJ#>ù0ôoó©5é3ßìD†ÊÎYÊpÉó$¨ì¼|@>Lïп½¿ì·K+8¨ìõ-}†=ä¶›SBfä3ôô»©äöèJîà+ÕtÃa î&ƒÎ µøWì¾Ëò}äó»~ý9.‚ÙpsfÆ0O“<­²˜Ì>nõg¨Øþ¾œ`ᜊné™oÇTì½¼Ÿò~þ›ûYTóMØ»f& ¹•Ž91¦rïn[ ž#Ú24~ùN°*  héÏ/0%{4 n“ÂÀ~_w³r$ ¾²Ÿ¢Ñ¿•š›RtI»ýpôx2C=üJA-½ bù縙ÇÜôÛƒT¨}Œ æw½¼Ý ݵæÛšp·Õ,‚!¿c¹Ý:åC‡Þ ¹ìˆÞ]kŠ.YÜFßiEP±|VÒ®P0±# Kº—}èÏ=gkÖï2Tz- „KIÜÙ eÐuÊyŠ&o> /.Tìþà¶g‰M°>EÍwe’ÄÓ:ÜÙ…EDkl.CÝÓ3aØœÕ\±ýy6+ `îB@ך<ަråý”÷³9÷Ó¢QƒVU €šøÐÁ›¸‘o·ƒÛ˜Y9híæ% óÇš ¦—B„^'ªU>µ¥õŠâ.íWFò¾Ðªb¶ AíNO^¨’·>Ë9q¢bùuËâa–©* GGýCS¹›#€…zEc´ü¡Í«sZÿ l&÷”¢{<Áȶ€ºUù޵ñ»@P/;º±×kŠŠå›1p!éòÓP…¹™ÌÊ»jS”~]ÁйaßáEùŠ®kO¦Kí<=i²þ*vÿñÂÄ*^ƒ¢7ÖÜ'&×Ûpn¾ _Ô4c+DUº*¶Ÿ>Ê –´R'³o¿‘¢ò~ÊûÙœû™˜Ou\º:,ëmé#AÍKLÓ¹tâ/ò¡g ×b¦³ÐœkdÄ®œXÁP…»ûÇú^ñ‘¢g.n©e ž¤ sG3tÒ»q`¹¨AÅòrñ„›wú|ÊL˜8,„+؃~Ðe‚:ÞÑ„M-5¼¾¼µ¯pÁ$R˜7¥Œ[¶Ù,ûÍcè¼ÛY0¸õ‚ŠåÓö™Eu“;ºöò*™XFЃ¾_Aéƒ'wÆËrèýy  NBÍlu™¡ÅÑq`f—λ½ù‘̪—rÇÔÓþ׸vƒÍL^ï´lÁ[:ðq AÅö›ŽmÁút fhb´tlA¹ò~ÊûÙœûùD¿+s69OЊ…ú¬Òe&×¹}éÚ%›«7ĘÌX½‡›®âM ÆÞ`¨ÅÆ©Ð6þ:·~Ç/r'\‡{ý 4>¢h•ÅlÒn{‰€^¤% €¢bùUû©@çkÑ€îPö&–Ær•c£Iad#AËÝjýùqÜÄ>V,ý÷ @g_:Ë>æ?"hȦbb·"P›ØxµÇýþ›¯È<[/ èìÀ.Ìêd )úc`,têìéÐ4:•›¬ÖRòêÒ,‚æôÞN» »?, ‘xž ‘¢ÕGÀ v ÷ÅE/š·<ž¢k®»“NÚ-*¶d·Ë:Ì“¡nÒ`õÃË •÷SÞÏæÜÏúü¡¬a-hûª‘ÌîÜ)êVÕ £ª6·ÑÑt–¢©1ùÐéö†.Yúr ³Qÿ­'!H ú*z;l<¶PÍ$¨ìVDPÓ?®Ôh§" bùÕÌZJ'®: ¨ïåú2+Œ«c»N3Ï`†~½4–iŽNäZ·ûJýt³¸¶6w`SW,_ _gÖnÆaŠnñn¢Êû;0T¹þ© Lô³¾9M±?Îuz;…­ Šàî›Ìân}¤¨Øý„6JPå¹/í±Cª>¬4Cõïe½(zÿƒ Ø}\! bû®æÃ$¥ƒ€&ÎüNÄUpåý”÷³9õSö£?|û<%„¢²óÕ™^Ä\HQÙyYŸLøgÚ @eç#O ¾G*;÷¸^DmjÌ*;Ëßbƒ:+¢{•?éHÌ–¡¨ì|ÞX}"9TÁPÙyIÔkP¿пÍçä˜LëJ¦2Tvn²X•ÝPÛ¨ì|ÛÔ>Ä-6¡{ÿZekøü]PÙy©S$Ù¾¤Q@ÿvåõ±ìk  û^ÞOy?ÿ—û9)¡µä¿ùXAŸ…–žtg×…lNÚn‹ûžL×¾׿җEï¤hŠq™ÿ«?Cßy‡yó)ê}d h¹Üf躈WP=|·R:‹¨^kÇ »¨ ¹S× ¨Xþß"hÕ8Ðͱ)pk½·n„#uؘLÐØ=ó©ÂÀÆlt{ËX¦*yKP¯cÌòz †Z] ÜZú‰ ‰†×…kÏFST,_PnOé]Ÿx@í.fÑ[ÝB¸‘W³ ףܲûØÌÞ–Üá-@ëL2÷pî@¦zS‹+vßóöIè¹öGçqç/\Ä®}lÇ]s7‘ÆošÅP÷»ÙzKÑþ—zÙ‡ Ëß`°WZ{ì8 ºOÐS›¸Å: däü–UU$SÃ]˜ã —õ« ~‡è©ýÃPÛµ0$V»8£¥¤ìÓT‚je¹¨j@E>Š­XYÝJ@C²Îá¸K-/ 5­Íª&l”*ìí1Õy%h¾Ãq¡"Û›«ü=œm²"è— ô¨ýe†Šå³^N¦,`hnMHÎÅÊÍܸÃF ¸¥+J áKwFühÚíIвa$ü‹ b÷?íµ…ígÝÝ\86¶óå.¼ž9¡-ê‘6 vĘ*¶ÿ«Wé6±= ¿Ÿ«ÐžO•¹ò~ÊûÙœûyØÖƒõ4ßÎÐÕ‹lØúU‡¸×f@à/‚NÜmî>sS¦iB@õ2@%ºÿéêfnòT':×V@ÇŸé j‰ý°ð+÷s5C„[½’B†Šå×4Ï<Èm‚f¤Oai%¹áô¤´_·}m¨8(ô kËÐ¸ê›´Ï }@­5Z3ßùª?²bt|jU]Ù >€Šå³·ÜGT«·1Ô e[_ºŠ«¨Þ–Õ¸¯.µ’Ö–¢‹Né2Eׂ&ŒSa߬`¨èß?.ƒ fI  ý º@qP7Òs¡Ò¿wJX–Ô­ë%Ûÿ}t[æv.Ä -u(¢7Œa¨¼Ÿò~6§~Ê~Æ&è±~—ã*;·_¬å;Ô•ooÄ.¶TvÞÎ1X-;@PÙù@ýj¨¶›¢²s±ü‘ÿ3}ýVŠÊÎS^!usM*;÷ÛrŠÆZåRTv~ðs&m—wпÍw&b<ôÉšÀPÙùbÏ&ÈXÒPÙ¹ý±à_›Äп½ïñè± TvÞCo<éÙ´š¢»¿×¤xr~T8Cÿö½¼Ÿò~þ/÷Ó{‹²ä¿i¶l8I¤èG•à–öÞ ý|s ìÉüJQ•5p¥àwÕŒ:Ÿ±Y@mì®CU­ Ûç@ТLîˆo§iAí% L÷½A/šjûÕ·*–Ùó$˜“ÌУîÀÒãû¹Ae· ÷fˆM{˜ "­N¶&ÜhË.äÓ®‹Vµ-E¯¨(B\EÅò5æEB‹ƒî€Ž»²v=Ä•<;J×ðåZuÐdyUøõôˆyÏ;}5L$ÉE9¨ØýÃ`S×;€šnve\…ÞÌäÄU½×’õX³’¡bûú ”ÅeÚyè\ÛɈ+ö^ÞOy?ÿÍýt­w…- Ú­·s…Y«ƒ¹Jé`j|OŠê>…GVôw°!,ß\èçµÅôlåº=g_­zssó±Ã{14 ñ9¬b«)ª| =Ø T,¿O:°­°`¨ÅÀ_ܯš¢¿×mw“ííõö1¹"ÝÂ5™”I_án’(0Í+MjšìMf¯¾MÐÉžWiCº EÅòœÝêüªó¥/y•ÂõÙ;‚½5ßOÐqõ™ÍÅV•$‡-!Õ¹5–f¥PTìþ7%-xõà! £—¼øÏžGܰaïIX¼C]^ùA½óŠŠí8cňREo¹iÐ-ºŽ •÷SÞÏæÜOµ&g¸¨ù€ :‘ßµ •·öýJuî˜sÁ(v9·“eÛd4šëFÏ0“÷RŠZoϧŸzß`èÇOv𶺄k¼²%Ë\Z% K{^!Ön¿(*–ß4¯…„]Ø'EÝ~ü†§ÖG *yÒ5çäÒ‰€ª~ÙKã»—tÃÚ`ª/9OQ«1¿õ#mjz÷#‰N÷å~MyU=WKQ±|. ;Ð1Ù{=–EÛ9Näv©~M“2ÞP4ø¤>±Ø€¡/*ÉÅ\õéð¹ç6ŠŠÝ/%ê#Z¨ÒžÔô|ÈuÞ™ úÄçé3§–+¶ßp«6D$X1ÔjÍVèÝÍ¢ò~ÊûÙœûÙzÑØçhh‹Ó£ Ñh ×oI= ·òç–܈2”ƒ¸šoG³„?± Íîq”£ß LÅìAïžRa çl¸¡´79½aC-6<#*'õ¸oç€ go@É&FMÂNqÅîë¼ß›3rø}@c hèÞÛÜè]B”­1A/dy‘ð‰C)*¶?rõDxñbAkT3Èø÷‘\y?åýlÎýôÖÕ›.K´ë)©û¶«µ¨’ÝÁÝ:ýÿûì¸+ÍãÀ(<–¡.Ûßßô;m¹è ”Tb°Ú,-,•;ØzdÑch¯‘pûõ@Åòkd¥Câ›(@u !5ú"wä¢ñD95¢‡gUk?nâ‚V`r؈ Š“IP«-Ü‘;û0Ÿ =Sz¼GáŠåË·nÁ‚l6KѮъ=!µ“öƒxýÜ~ó$NßÃÍ#Ñ,éRKî · ´¡]!CÅî§iFÑDí|@w½xFµº2®ÎؾPõZŠŽº1 j¥Tl¹JW¢ån†:oˆ#O7PTÞOy?›s?#‡úÅg;50Ê>wrá’½Ìûb‚ž“z±/E×(zmÂ#Ø­ ¨_¸T ‰âz/Ç^¬ÊÕ%“Ø‹Ïnu?Ò•ÙÞð´¢r%+jre¨XþõeæPï}ÐŒã÷HZÓMnz|º·MЦöüIÊëÔmöõœ -U{8TúbÃYŠN[WÏ4ü´MËËUbM©¡¨B¥ZÖ«òö ÝÐ} H®¶”¢_uzAÍêÖ­œzB‡ahtÅ78е' b÷w¬êÌŒÜÓ«;”Å»Äq+¶²Ò\IÏw´¥ö ‚Ší—Ô›‚Ýâ=}s {-#¨¼Ÿò~6§~Ê~4MOI×·uTvî°t>{}Ï‘¡²óÜ{•›Å'•[» ¡Ñ÷­*;ï«—ë÷Ÿc¨ì\,¿]–»ùý* ²óí¦Á *;¯|¸ƒdž×g¨ìÜ%m*³²Ÿ@Ñ¿Í;c>)ÉèÈPÙyÜR'OPÙy»'•ÄmÄm@ÿöþ£Ó™g¿#€Ê΃Z³âûýÛýŽËuAõ‡ û^ÞOy?ÿ—û9³O+ɳa÷\aɨ»€N9»€šßàjXf@‰]'†®n}lÂJ¤è¸n RRBÑnîUP^§¨íì±ôÕ×+\÷rw¦eÈü|Wì~Í—BºØËÐßî/hN6÷ýç#¬Sør‚ùº˜9e1TlÔ; ÔÜ|# e†*Ðrf7@åý”÷³9÷óކ-³»â ¨g®ûY0Ÿ;rü¸•”̽ïuŸXÌÌçz%9³Ÿ—ü¸Ýi2[«j`†>ö4]†.º 3vçP4÷nxw9è¯%>lÛÏÝ\±üßgháìý µÎ\«·ûp×¹|„†luý|»®ÅÎ4}è*Vå`ÁõYéɬ†í“¢OÎ}'ž{³úÙþdŽÉ“¢bùd¸=(ÐW+ƒ‰çûÜÌuºäÅ…4Š.nC‰´Ë nì‚ïpÂ4’û´[(Œ½w P±ûß>}£G)ºS»%[yà1A}æ½% ¥å ïúŠïìâŠí_è˜ ÝÝa ’­,ñÔ‚Êû)ïgsîgFÇ@ÖªWw@Û$b­ÏÔ:r!5˸ ¨}å¶bF$wïú¬†ù2ÔdÒ3ßÉ%ÜÐÏ.Ы1 Ã?“ºé߸‘÷ÔØ·8†Þ]*…ûã¹bù;çš } C'·þm0ØÜÊôÍôhÒ¹ø!· vûÜT†m¸i¦óm?·Æe4Xt-ÔÃ4œ©(öåŠå JÒ ×weêÞØ’µùÁí9¬ ô4¥¨îN`Uu},j}…ªéxzÒ±;徕¢b÷‹â[³‡’ú,®-SÝÅÓê)¹y{wƒRI bûýöfÏmG0Ôx“!1¹“È•÷SÞÏæÜÏ.[ØBPœ€nsaÊû)ú.)œEê´É8™¥½QÐ_Ÿü!»]:CU¦=…aêÜš¤‘.!hôÉ$í{oŠF¦-‡Zv€ª;üC­Ò÷T,¿’J!üÞþ›¢Î¾·%wzJ;æ`$tÌìClKö|îä‚¡0a°3ãnö­Çß(ºñÚ{Z×&–¡ß+â¡Ãå%\±|YW¦²ˆ«K º½‘%jý$¨ùã°ê¹„p •,?ÎõýHôgeh¬9¨Õý–¢b÷ûé·eG7hÔÈT™eîß+ '“¢¨Ã½X@]o`;7TlÒü“@3´{T#è?Ö§¨¼Ÿò~6ç~ztñc¦¥ ¹ÕþÖe¨dC4›²º/÷ô {¶xÕnŠr9´*½OP¥Ažp~ï>@+k;“ÆRŠ>÷ŸJ§º4zL>ÑèêMÑÒ ÇH@ÿË˯¿§L_)´~Óc0Œ­çV®La+rrÐE†Ø×IKj²s(¬tP›ã gx1A¯­G÷€ÚÝÝ)½ä¥ÊËW§º“Y¹f hYúröé×AЦ§¶!Êf Ó-^Z4wöœh’ßj·jI¹0ÈõEÅî¯û©À$Ó.ä ý>Ñ)¥R´Â¾ ëídÊPõݹ$j‚Wlÿ‰9wÀºx Y_ÈÊS¸ò~ÊûÙœú)û!ö³˜O¿¹ •/[p‚ºô)b¨ì\oÝ82>+PÙySn7¨ýÓPÙyÕÎKt³ÊŠÊÎÅò[¥CÏiÓý? íG:¾àãÊ|jŠÒ‰Û…r‚ÊÎ OÖ³»ì¥èßæsý3Œ©{j1TvžÖàLU˜¡²sC›i°0í'Eÿö~íô*ZDM)*;X³ Z>,¦èßî_Õm Ÿ èß¾—÷SÞÏÿå~N<ßRòßÌýÇ›ÆææÔbÑÙ÷®Í®Xr=°€:L™ì˜¾™k4~„³"†. ¯÷»k¸ .›H¿‡ j0²Zi_Šî8 Ê£)ºsîqâÖþ)AÅòÿ‘˜ÁöéÅ ML÷õlîƒý^tØé‚ÞSëÀBâtØ ø¥ÃP-÷ûàª9»qgkÛm$·è¥xºpÅòéô3Θѓ¢nïgOIJâž:¡ ñÈð'¨sÊs¸å±PK#=(˜}¡úíÁØã§)*vßÍ>3ÇÍþT6ª}}ŒÂñRTCe Äu› ¨Â9…±ƒzrÅö¿)¼H–¯Ëgh—^ypëgWì½¼Ÿò~þ›ûéa¾fÞÉP³XGݽ­UˆÚ#ØÅ´3C+ÓÚ“’ÝK(zaå78µ¦A·oN‡À³û=Üñ©w™¢ÏÎ:‚ïË>Ü88Ø´Ÿ¹º·Ê#¨Xþ»5©àJ£Zàz8qCMº°êvµu~êG½é6†Ùÿ Jú¼Páe ôï¿ PÓzP´›KPçÌ Ä(û  bùª” ÎWªÔ7ˆÌ¾Ø—kø+~dǪ1u(\,àn7rÛÆ¤3«›u–+v?úmK!Zñ‰U¸_Ðëß“¢%ƒßK£sšrÐì?)DÓ÷0EÅö;(JîNOPµ¼lÈëwPy?åýlÎý ô1¤iƒ;StÊð$iÍÖuÜÏ’—ÙܤOÝ`}á5î¸ÕȪ[i€šGÒÈ»>ÜÝý÷@í½÷]¬òšËmz!  ©>ÄíH_†Šå³Y©낇34TiD© æ–\lM>*ôì?•TÛ$‰«æãÇ '&R´öòX25ð8CÅî7y.¨÷Ÿ@Q£>¤|õr®á¾Aàù4R@>»D Ö¶Tl¶ñ$b¯]¨m¤#»´É+ï§¼ŸÍ¹Ÿj;ÁLku8EKë…ÀvIÜúpYîÆ}åU^žf¨Å®‰lŒ]AýjÇ,w¾¤è ÕJzþÍJ@Ãû»1›m!:ñu¥y2ôáº4Xÿä, bùÄÔÁ– ÷š»þ)Ôݯ!¨ÆŽÿ|W.†6®‡”w/sÐÂmtˆð• Æë^Qƒ‰µRô†—H 3TÒ¥TÒ©Î Ë·³‹;¨/êĸwŽ€j^E!Ȇ½õ\¨ù[o¶1íASEÁ“iÏ(úeœ%ç**vßp51YãBQÕ¡^äØkoîúg§¨}Ù-‚ö?݉¥9> ¨Øþ>ú!̬`>ECo¾ w^`¨¼Ÿò~6ç~6(¬"ƒ‡§Q´þF9¬–ÌÕù´æ\"¨Çø¥P|ÑÐ)?æ½&2´§—d¯éÆMìÎU7îã%‡ÈÄc…\“Œ-̱«+ ³•¾R«·‡*–?õ@>dõ‘êTvŠïºp«”ƒbü®–kT(¥r³&ÇJO~úBQå°l°þ×|à;’³_`hEý%ȽùE@ÅòE~L‚ýRôÇú4°ZNÐRðd&™(:~Ëtr C£c6 ¥~î=¨FÔ[¥¨ØýomO¦3þ}L¤¤ÍŠ®Z÷"ÁÿcC{ëGÃv'o®Øþ)óºƒeVCMDUäÊû)ïgsî§â˜ 2ÁéEó+‚Öª]\W[(Ÿê ¨]»—Bt?®_ªlú¤JÑÆ] ÍÌÅs¤ ×åCw*µ”äÔŒ–¢ã·ž†Î}Õº8¤'$½ë¨X~Ï}^ð3)ЛÍ@ýë1î«]—ɘø{Ü×ÞR—Ùܪ»:ð4p*EÓ{u—’îðI#ªóÓíñó8{}Ëž bùW$AéU@g-·–vÜg>÷iÕ¶ Ó!T·=Àm3`9ìIÐ %ûIS;;@E›þœÒÜGÑí©Ÿ‰å]wî…÷Д( £\NCÌå3€Ší÷I¥yM>® ¾»;*ï§¼ŸÍ©Ÿ²Ÿ å.0lÇ ŠÊÎu¦ÓÙû6*;ß:•mXDPÙyÐë›0rÎa@eçÑK{ ù×Ò¥¨ì\,ÿ`‹ ²¼C4 ÿ'¿3r\ ¨ì<ðœ'1zv” ²óÒ¸_Ò3Ùwú·ùjËׂÔë  ²sÝŠ&2Ë͇¡²óo,˜ÖR_‚þíýÄm­¡tÁ:ŠÊέœ‹¤{nú·û­ö Oƒú·ïåý”÷󹟇û(Jþ›³NÇAÂS†¶ ¿qÓÃõAcdOu¾­ÕßôcM̿ك¡’ÉÁhõ‚w !Ea¥ÕÞ?¬ì ¹ž£5H‡‹o¸ «OAÕóL)*–?Èh¹0=' ×æÒå÷×q½¯×È€ÞÜÃsÏB_÷“Ü&·$°ìÕ¡!…wÁÓ¤A{¾:;òjÖ5\ÏÌT,߉7õ$xç †þ ¾Åúܘ-«ØÎç~Ü„שäl×zà!öë€&W/,Ø«^æŠÝ?­º V|œÆÐ/›} ø8pm~ KkO®Å‹|8Þ¹œ¢bû;ÞùÛotcè¤50dÃp@ÅÞËû)ï翹ŸªJ¥Ð!rCÍÇ>‡ÌÎUÈÈ­„:‚þ¾BPÔmhƒr4„~JåÆkG,-ïsWÍ ƒœ©‚®Î°…d‹õ€ÚIäˆÙYnÖ‰£L«:[ŠŠå·Õ®£O&Í´qÕ`öXO›ÛÓµ-¿Î}ñ~]µ?™»va*|«ÜÎ%ÝúBÆ]Æýõííp‰«5>’­XÙš¡bù®ØÍ†ÏÅÏ)Zi°T;Ûqã.®"ê Š×Âú??G)÷·æM(˜XCÑOËÜÀaÏ4@Åî¿~ #:á`h—÷âêôɇ¬ý°ì5ñ}]ÀÛ?d¼58ÆJÍü|žºÅpåý”÷³9÷ó‡é;x:М¢Q{?@åH-qZAO™^!h¾Ó 4=O@Û1[Í @œ;ÁJ÷ ¨ùƒ^Ò?Â=rÞ‘ÕîÉX¨G{—2t|ð0H™AP±üwn.`72.ô°…)\óÐ[Ó§2õS³]·k³³ú  cŸ¥– —}3*€™ÎŸÎ½¶¤/˜ e µ¬¸ ‰ . ¨X¾åÏç‚ÛÆÙèú–c óA‚vÎ= g†0Ô(÷Ü8€ë$8K v´!¢…à4ÀŽ¢b÷}7Ÿåèv íl›iº5ý²‚Y/ÚhÔÃXéAQ±ý ƒØkËíÔ…Õ¯ïÅPy?åýlÎý,½û$³â ZÙ¹:·Ðt”‰cÓQÔqw>ús8Cµ·Ù²Í㜸§úmôïŸçÖ?òcËOµâÖ™ÅОÙÜèŒ7‚êÀ³€~^©ÁÒ:{PT,ÿ?Ov²quµÔZË9ue¨nOS¦ Qâ:Ǭ¤cß,ç¾XdžޟJÑb‰=ûRÄÐ%3/’èÞN€>3ŸD%ñJRT,ßÑ .}A·øç‘µï¸áO+ÁåÑ)zͲŽ<,!¨Vy_Š4”¢·»ï'çwwTô¾A&üÃ.R4á…¢wî”ôRj–‰¡!Ç] uYWl¿áÊ-$1d%C‡-°†oô3Eåý”÷³9÷³~eĦÏôhßÖq7\ÿ´`ªCãGÜ$ýp£¯o‡Cʸ“¶^€¥mcí¹Jךûî|xnÀupÕ†‹#^STµYum?AEÿ}ôh2û¤¿’¡i;²r÷CÜ­,‘Q´VT,ŸÎË%$­ƒ Aógœ ö(ro¬‰íðé€þ1šZK<¸•U™qìͧ?¤µ*v¿Õ§« Ô»3EUrn€$ðqú$ì\Ýv’¢WNÂÑASÛïfk™½êtR,YïAPy?åýlÎýœT– Çc  ±Ü£ÏÖÀÛ=‡Z×7 ޽]É-ȉ„ÒÁ9•\êú±CuY÷–LéÎU9³nÏJã„y no4:~Ê0Ú€¢bù5'кÆ†F÷ó¶<½Âõ5YùÔ™¢à5<” ºªœÓCÛ=5óþuìÔžµ[AQ»dh¹ø8CEûy ØÌà‘ŽUèUfö§‹E¯¸æ“-³=UÜÂlÌÒ%Üâ©°éÆ.†¼ðÖj Tì~O…¨¬• hIrØùÛtû[’“²Pµ Gé«IÛ¯`7Ò¬æîj)ýЃ؞Tf¨¼Ÿò~6§~Ê~,,V@–à @eçÙQOáXç•Û½¬ºÿ$¨ìÜR˜KAÿ4 ²ó¾åÚðçs* ²s±üú5•DåâM†Êί}i 6©Ç*;|sjÒí•ëxÔƒcÄx‚þm>S§·‚_r1Eeç¹3wÒÔ.€ÊÎÚÙ“^gïú·÷-snB—GI•˜ÙI×ÚSôo÷wµ_ ‘Ê ýÛ÷ò~Êûù¿ÜÏê ’ÿæ{¶"5² Zµo«>ãÐWp§d:WCû˜˜èNI0Sˆ‘…Þ|9Ø»Š¡ŽÅ^Lkw§l=–¢% VWÚ0“|{ŠêÕ·e¿'ºî´|M[ŠÖ ;Å*ZºTóâ#ú3#¡b÷W.ü®†W(Zý¬œWr˦ĀSäe)jj¯ž¬TlÿŸ“a`Ú«@@§˜¬…ü®€Š½—÷SÞÏs?» éÝî© Mws«[‡aA±€ººêƒkÏBî‘h2Ó>œ¡ ~lˆ™\­š Äi²/÷ÉÈЗ{4ã'„õžFÐÌ1û„è„T@Åòño ƽ¼)Z´©? nêÆM7žÀ»mfh“C;jö1‹{tŽ5‹Ý¸“«àõ+§ÏÝ‹3ÉÔÄÿ%EgåäIwëÊÐ|ƒyôãèbŠZ¶¿)¸IZ2´}ÕYèŸÀ=|<z|%¨Øý$»&x¶eŸ­,ý û,34×äqötÝ@Ïmñcþú¸bûmîúæ<´: ¨£Ùf¶ÅX›+ï§¼ŸÍ¹Ÿ¡dÕ—Ï­½H`m%·&'0çѦ"@OöÕe¯ÜÏrãÖCÕ¤OR4k’%XÚätå‚-»ìÕZð‘ŒÊ&è =f˜GQý¡Æ RZÃË?íÐhPèš3=å"ÀÆè!]vz\ø˜ÇÐr`ÙùÃÜÚ» ¡ëenÕŸ*ÐךÏUI7f}mýu>¿‰é^ÛÃP±|É+ôHQ 6C'~»IÞíÀ};+Ÿ” ­¥h»š ææÇ•X¯#õê+Mé2Jœ5sP±ûãÇ}‡›wôEòg°¢…ÜÝ!^,†-`hmµ1×½ÇÛÌÄŸ]¬ÁPËb[j ½Ì•÷SÞÏæÜOÉS2nDAլʄÀç>Üš~Laò,@+¼cX¤ý0‚ΰÙOž'qô*Ílnl‹6ôö<œÐP+RÛë.EÚZ“Š[Ñmtº,ÄÆb¨Xþym‡Qù9‚Vè’¸¾“¾‚^«*Š~¼÷´W4lôXÚ—[Õ+öýöôçø0ðýšÌÐ’ÍÅPÓØP±|{£;ƒôí=Š:˜§íj®Á(ȼ雃N›Ñ f¬$¨é]%H4÷¤h›ŒаïAÅî7ª‡É±í}YüB‹†p #¯òš†úÕB‰{AÅöÝèFôCíö¼‡Ã?STÞOy?›s?«:¯¾þÑ%hƒ·º°Yo¬€ö™|˜YlþJQO·Y¬T/CÇŸÞKÒ’oStÛ0ÉÀÝš> ªÞ6q絎M) †åÑ[ŠvÉ*!?^T,¿bÒSÒD/@Íno#Ê߇pƒ>fBç‡ÁÜÃó‡@ÑoÊÕ§„ÇgrC¥;iå‹#\'«ER‹G»¹Y¾›¨‡{ EÅòùv%ʹ9hô½ÒY— Úiäõ…\®Þ‚ÁûÓunæƒJzoi%÷Þ¾ÌØq1CÅî»{Û?Ìtܻۓµ‘ël¹ 4pOYЪÁ»¹bûý¾à :Ð 5aqèY®¼Ÿò~6ç~Fëáh²¿5^sNȬNÑÒ–AÔøRCߊ%ÇÎ]äZ¦†BPŸ€úr‡Ø6“U2p‡ÌþáÆ»Ÿ¼Ù)®ù?gXÞH )šñ‡èùæ3T,Šâ3½ïC­¹:Âi]®AF•Ô…¤pß¼Ìb¿,åzäš±ë#ÚsO$ f6æStp]œüRŠ–ÿI»=èAÅò©µ}GjÆ…´C”?q9šÌ-ûÔ­aWµW:vŸ,E¾v­/± ÝÜ4z:AÅî‡'\«Ž>€vNƒÔ]¡Ü$5e–+uPó¥m„xí– Ûï=5‡Î¤Ž€9i²’‰TÞOy?›S?e?’•=HÁ4?ŠÊÎ/±ˆ<ÆPÙù ¿Ãäy$ ²óÑæ«Ù #ã•{ìº :Ç6Tv.–è¹Ûô÷ë€ÊÎ'º.ey— ²ó£MÓé– A •»\;KCý‡3ôoósPèõð(Aeçªìù.Eeç¿G@Û1E€þíýI–§À÷ýq@eçk?ö†á9;)ú·ûó÷^“N›×ž¡û^ÞOy?ÿ—ûyÍý'ü73»«±–ªÆ ½õšžý¶”kØ øp=\= \a'÷‹™üÔ®£h Û8»œ ƒ7ÔA7mŠ®ñ)€¬î€&ëÛÁê¥3¸V¿¥Û5ìT,ÿ¹y~à>0ÐŽ³FÂÈ´›\EÕÒ/r©ƒ óܽ’ëvEŸå_ÞÇ}»Ã…) «ªhÕ ,¾]¢è›Åp«ÌP±|‡#ãáaòq@—ø­;ú¹\£‚¹0ÿ^·¨ƒ/W’È3Ff«ÿ èø5‡@ïX2Wì¾ó²ë`ûê>Aùæ@Üe@ "èÛ÷ù5³½Dº{2Tlÿ£5·I­Ö‚æN:gæú3L@ÅÞËû)ï翹ŸµóÐ÷3¼š2ÿ«´ôÏan·¯—àÃÞþ܆À\[¢ †ÞÐtËкÍÚDi_ ׺Õkr8¸˜{þ€Àt{‡p F˜ƒZßÕRÔéI}Î ½Ù€Šåÿ<õqI.´\ÙWêP˜Ç \ÃÔƒ4ÎÀ‡Ù(NbèÄç#YU@®Jê\éSº•;F}›l@í·µ7k·+œ¡bùž“%‹µd$m¬Îç†/t¤M‚¸^Y“›·~…<ÉÜÉÕ½¡È o…sÅîO[œ ¥Ž:€~js¬FŒâ^,Jƒ ZCZ<ë!ˆo ¨èßooÂH/‹[Ý2ضl?Ä•÷SÞÏæÜÏü°,!fÐ)†nŠ;MÆöIäæ:þ?öë5*Çnûx”$•„$•$ IN‰î{Í$ä”$!!ç$§*!•J%"IRIB×ZÉ)E’SrŠè!grúïÿ¹~cÜ{Œ} ïönÜכϋ9֜ߡïóÄC˜bUOPÝ»g¡åí@@—F90:_î÷ñû™ÕÞúÒ7Š5¾‰/Do¨±s:g*aõI¶Ü¦+Ùp`fW,Ö•jêž™ hÌóaìÞhîšI÷©4ë8Cž*%_‚÷Õ®pïÇ$îæOÈïBQê}ÆäF0Ô¯T€‰“·*–ïNËôŽJ6 +í\Yÿ”íÜNöæ,¯E(A3²´Y5¥è‰{Ùs÷÷Ü”â8âøêCÅî9¯Û4$v˜ßžÉµûe ³>s_5g¿…\±ý[6G€óîû}íÓLÙ¨¼Ÿò~6ç~ŽoS³ªä4¶dpõ“×BÇi§½0&|ù§ŒÛøt*³QÜÅÐòÄ‘ä†ý5n;‹&:/š{Qs7 ?Ó‚«bz:øEêl ,xúL®XþËëV³@Ã€ÎÆ:×ý&èO«Hx3(–¡&.€ã”6Üw‘c!{½A;®ÈϯYÜÑŸnÒµ°PÛ?±tB|+†ŠåÛWʶl5ôíÓ½¬›©9AsÓÒiÐþ¯µŠ”жÝZåð$ý¹‘ a îç ¨Øý“Y[à×%€Æ»Ì‡ˆÈU\ÓµûÙ}ïSÚpû5ývæCÅö§wiÉì¶úÚ¾c$ËÖÜ'Aåý”÷³9÷Ó²llOfhøŸCœÉ5„£t‚Ã9@=¾ÌfnzÁ\«ÔdH›ÌÐß+žÀåýuÙR64¤ú2™0ì%·]“ }?‹¡··M‡ÚÍF*–ÿ‡N«ž®D¸§CÙ`ËåU;V>§zÔµ}¼ 7´h¿äáæ»RTaý”swÒNRÔ¦ýx<—p#¯TI2ìT,ߣ”HæµcE 5V°Øgƒšzé49ðL›»únW—QTíñDê{c0 ÁŠY‚ZÑ^ŠŠÝ·ß9`Ù@eô†W ›¸{GN…íÁI 5Þ—?úPTl¿ÿ-K¶*'†¡ä^ Ø|ÿ:WÞOy?›s?û1ÐÉ^ËÐ~w cœ-·R;Ž%&¾'èÝyI¬8ÉŽ¢>ED]4´Â¨?°ºnQû×Ͻ—5ÐßCÜ j(jxaŠÐd#E£WÛÁï,;†Šåÿ­¼šÕÎnÇа¹R69r*7b‡&z¹j³ã¤ô÷ ®ZäUâØ`ÎÐ]ü `´.×¢V*˜åŽãšôe¸U“+–ÏNÍyÅ2Ôõ—/ý‘ȵ‹jó~ hI¾1ܿ׉ 1zƒ÷ & ÎË=ˆý¡N€ŠÝ_d j=6ºyûSâg²‘ûýùR0øªÍµú¦E*¿þ!¨ØþçënB’ŽCmº¼ ƒTÞOy?›S?e?ÓupæU+†ÊΟÞöeŸ’Ç3Tv®•,´ˆž ¨ì|ö% Q+ ¨ìü¨ADÇ9*;ËŸ¿µ‘æ_ b¨ìÜluKj¢@Ð{¿:¼;-PÙù”•á°a| ›O˺y×&•¡²óð˜Zâ1# ²óµú,îÜ~ýÛû:¯3É#Ý5€ÊÎm”ÚKz)Qôo÷o× ‚Y÷ýÛ÷ò~ÊûùßÜϪ[_á?™–=”9Å2tŠðƒæªã~;ëºÙç)jpÎ j7õP»~ÓØõ§æ€>ËÞŪn¤h¶ª7³™\%EnޣϽG2´²1ŠýijÅíIü`VtW,¿ÙIJ†m±´6c±¨ìÌde2Rª2?$ Üâ!{ ·_wll1Œ1H¢h˜Õ#¨» -ƒÃjGËgü5…Ìñ Áú­ˆSq×ùù/j¿>€¡É÷û NqǸ‘ÚHŸ5”Qôç×­0•:CÅîÏ™ãÊÊ úg€#œ}öœ›¼äü‰Ù ¨E ‘Þ[ÏÛo²û¨.üJÐ85[È/ÎTì½¼Ÿò~þ/÷ó«ÍZêh|–¡¹}H)æÖî ÕÝ4kñ%2îB×'¶=kÙx¡[*[À¢É¥Ü£ÛžáŠÓ¹Ò>ó zi0EÓlÃì5Æ€ÚÏÒ¶_ *–?{–ÔzË ‚*ô"<½Ê•Ž®ƒ_Eg(ZÕXoÍßôgÅa04«á*Ö ‚u%£ý9š=¸3tŇvF3T,Ÿ¡UŽÄ¤w‚*hv’¨.ü×ß#þ¿…R“}†¡" ê[×vh:è[@û›÷¦«—œàŠÝoøn KQTl¿Ââ:Ú5à0 ‹ß&°Æm¡•÷SÞÏæÜϳÙñ$.1µÇ–ç¹™o‹¥µéa­0«µ-¤¨´Æfï`hùø(ºx°½©vÁ‘uw]/Õ¹”û­Ï?Ä<ñ0E¿:—‘«_-ËŸ¼}³u%OŠ*œÝV¸@å„€’[Ù¨Û—.ðaM ·VM‘DÌ1ø? ® jõ ª¹Áö¥M£hõçÃR·ë!Ëç¯Î¤5϶RÔ¶û6Òïj9WÃà:ì.ZÊБËÞÃÌÁ§(º3c[Z¿E@/Ûç ª[CÅî‡nÔ¿³-ýèÉñÿ?56©AÐÖŠ.²Ô…$o‚ŠþýJ}ëÓk#CTn'n[Ïsåý”÷³9÷ÓiÉxÏPϘ iÜ춺4Ó"Š¢þo[÷÷Òchùžxç¾ÐXò‘´¼—ÄUY—-HæRÔݯž¨u›ÀÐÁ–aäKÑcãg@ܘ<†Šå×ÜuBjÙm)E%žDâ“Æ}}ʪ=/tpÃVm<Ÿ[¢üTêdµ¢·µ5@Ãù÷P·^äÁw[†jŸº_ŽÏ§¨X>¯ë_Ⱥ>ï(Ú¶í0°šõëó ò'DômÎhh?Ðä"0óËÐ'ƒ¡ãK@Åîïüv‹$kÿ!èµÏÇIԇܸŸO ÕòopÛ<¡IÝ"(*¶¿ã²cäËДٗáÕÎÇRTÞOy?›s?C¶„;Ÿ"šq,Œ;­àÞ®?FŒ¹=÷ØžÍLnæ×=ôá˜)€nºó™FêRtøŽdø\Њ¡u |V5AuëžÁ±¡æ€Žª.£y-“¸¢?ÿÇ×I£öYŠ.¶íå&ǹ-¼B˜ÿ“ÛR4¬«3+ÛÙ¡.çB²Y£͹?jfµô›dÜ´>ý3f»§ñ• bùLµWÂà©{(J.…æ…­º(7Ò”ºÝ̺'qýk&ϾÜäŽ'$_ý)*v?ß7„¿|FÐ’Τ¥Û nº™™x$„¡Ÿ¶¤ÂØA«¸bû .—“mü~\ºð9WÞOy?›s?;ι ]ºY2TyPå| èÒq0b•)C_Ï(‚\³@Šž¸/¦çîqÛe¾!vʹ‘nsái^: NRèé;Ù\åàz:q¥9C•®LƒrÓ ŠŠåO¹$…uŠòž Vwï ¨Køf*qßÎÐKo‘•ŠÁ\XErú{z¢ï]j.™ÀíE]¨íl†)•þ-¸bùºï…”…UÑŠ€ôñmUð—Hzn-àÖùM=nîæZ²ƒdí§…hÄõ=drã(@ÅîÛ0]rûr>AtnIýO$pÛæ½‚‰+¹çŒv€®N bûÝ+UiÑB)úiO©z˜EQy?åýlNý”ý}¿vg<)*;¯ôo÷G÷šŽB/ýÛ÷ò~ÊûùßÜφ+á?¹«ÿUòjïUŠÞVV„ÞÜŠ$i–ôAk ¶ºwP’¢i›5@çÜ`@ßFéSõÎN\Dz ádºWͬ‘nVEÐöí¬Ýšèr{sö>IQ±üû»º°d³†6ši°®)\_íÐ;ù0 î †Æpû .G0îaÜ>»’È„“… -8v\ëK)*v¿,¡žv’ ™^_©ïŸt}Ÿ:TŸ§hòå"(²P±ýö<Ù ÿ¶€^œ7Œ¹¬hÃPy?åýlÎý Z2Fåo%èëµ=Àtõnúðý[¢â]± úT¨Óõ)Üù݃¡W×A§§oÚ6Q‰}<”;$S‘YåZ14¤dÀ•@ wî`N+/T,ÿŒIñ°¿*ž¡.Ë `ïW/îÛL-ÐiY@ѸƒAïÝ% ªpñÒ0‡“€º,Ȇ^Ì­ëéŽ?ì šnŸ"ø~l! bù¶ÂnÕMk¹ â╹¤(Œyd´4{\ØÒ^@µ4§€›ÄÐàîL8¹4Œ b÷ëþÐçõÃ)š½QåMŠâªì"þ .Úãœ7«7+¶ÿv­. ìåÃÐÕå.ðìWÞOy?›s?¼Û¾Š&‚ž®®&o;vtR­+ìXÉœYN\ìå&î„Ìq¸6ÛwKU¥rýëÓɺ!Gj™þk7qÔç÷UaÜRðÇj‚Šå_ûý5é3ÔýÞÈNѬՊ°íZ1AcN^•ZõùÍ}âêÌ’?¢†F·©A›ž Mlao€[Ú”¯š*–oò•Pýó—õù:<®î#èÛ‡,#¿CµãõÙË£¡Ü×ÿÜ'¯ƒ )ª{Ê Ê_ž'¨Øý[F?élµŠÖl¤kkq?xobÚ­jèè%5QJâŠíwÊŽÚoG2šÒ¸ò~ÊûÙœû9³,‚\ÒP¿A¿¤}M¸I&Ztͽܓ̔ rôâvËÑf¶½¹¯´4™gcŠ𼇷ñ5½w »ï´vÃáb6hôà@0¶iÇP±üãê*Á#g…ýôæ"Ü<~Ž É-ÓhÔ¤Pî¬Ö­˜Æ eý½o5Ù|vCG'XýõEϯºLϦt£év¶t‰ CÅò™uV…EËÚdºë_àÚ»§ 'eh•ù²Yù÷öÈó¬¾uhòg&8STìþëƒ5ôÅæ. 5þu‰æŒÄôس­†q-í‚À°f4AÅö»¿W£•z£(ª²i¡wG3TÞOy?›S?ÿíË7ʶWæ“42u•;P)ÜC—¡²ó†Ë¥i­ ý·ó×@ñô=€þÛ{‘üºÑÇ!§º ²óEY4h˜Ceç32ì V}ˆ•ßß3 _<ÀпÍçhY+5•î$¨ì¼»™ëŸñFbi\LQÏ?¤Ï~ µš˜J.n ã–| öO]T,_Œi4}÷ôKønHû–ÊÍLÉfôñehh@Ýà™Å.ùCÙ‘8îý—áP¹Ó—+vßÍ'Œù?¶(DM>…1’Ü¢ÅkÂX¿®Ü—»ÛÐ¥{R*¶ÿ”Ó Hˆ ¾O‚œÑþ€Š½—÷SÞÏÿå~jíÜ ÞÝÒMVZNq%6…âvvÜÒú—4&«#wƒ‚Ë\žIÑy«C©ÛûÝ ý~0 bï%è• mгØ¨óQw6Òk1÷·Õaêp)¡bùßmœ ÷Ò'ú ² x¨mä~œ¬Á\ïØqoT­föcÍúRØjï¿Stzàiè¨Ý“ >ö_‰ú‡ó€®lÀ‚Ó bù”2Að‡Ó€.ˆ÷#ÕÛ ¸IâÉ¡˜¡÷ý= rW7T5 âtKZmtUêÓþ>Wìþ›Å¡¬Ø5‰¢í¯ï`A–/¹ÅO@Ãt9CMº%@ð»t)*¶?ni PdIUˆõ¤ _לCåý”÷³9÷󅪘-f€†¯>CîÏ)æV¦=…ÔâÄ6ýc&EÝÔCE¯†.¾vL¶8qä>¤»´¶ºò~³Í,•¢ãn¥Àɨ=ÛäH¤6*–ÿíµ£dRY  sZH7”ï⺗{±ítúÖî%µj<À©y¦Þ€ÎVC’ÚårçížGœ…M 5<WçIQ±|z;Ñ—èn¢Äž´ âN˜+¶3tQËû°{£2·…ÇN²±â&E¥ê‘àýc„»ß¦O «¦ÃÐÍ:kXl´5w¡ëoÒág@‹®*ÔV÷”¢bû½òãIð·bŠÖ%Œ‡"“ès¨¼Ÿò~6ç~Ò–Têw‚ZÒ`J³¶åq³ÙíM³jôÞ~ß–Ê,h€ê²u]92r¿E,a‰;:dmÑÛužû¡WK¸9^•{¢g ôp¨Xþ­©aôüÞm€®NmÇZOZÀýµ3ƒ,u.fhÊÇݰ¿ò0wÎäTbš¨É›0–á×@ÐWýÏ ‘ê~RtÝ©îðzCÅò=Ë“2ç~€õuf*¿" ª÷¬?o’¢ÛO_€ÐPm@÷I>ÐŒæ6>]Ç^ŒPáŠþÿ§Ý<æ`9‹¡5+Æ3÷S~ܘ¾Œì¾¢ÈÍ< î63)*¶ßrSGÐ:Ü Ðó×R½+#¹ò~ÊûÙœûâq*L;hÌ¢®,án8÷uJú¬<ËУS6C]«ãܰ£›Éàô€wSg±Û¦puFž…EÆKšdzŒ[9Tû¬=;h·Ðc}khý´Ã Ëã1‘Ýýb è¢ÏëÙg_#6öfè”_µ°®k{‚þ™²“¹Õ*1´ðæ=ZÓb?7`C ôÓ@Ð_ZÒpûÓ€ŠåëOưÆËeª<¨+£­ïQô$žŽ^¨ö\#(íÊU9¦Ïî<ŠehÐÞ(°šÀ»¿tÃæ{'Œ¡Ê›:²ü‡ñ\»ÍP?ÆPÛô`iðø4®Øþ/]™ÂJ;kTùc6¼zCåý”÷³9÷óü¢ÑìÛ‹%€®ºˆöç~:ttO¬gh]W`ÕþEµ«Ì˜«®€ŽŒìK3õº2ô¨ËB(ó\¨úO‰N´-wòóÕþ7E-W¾"!{@Åò[¯ fÞŽù´¸r ½IÑôŸÙðå«# î—‡ƒþœHn¢úObä›ÉPeЮœÉýÌÞ8^¢èºŽÝaQ@4CÅòUî̦û z148U[°k$×-»7ɬÙhã°!TÿÖî{ÿ"hÔ;CP÷åۈ߸逊Ýw=PO—>ÌP“#‡é—²¸å?Ø€Oím0óf÷¿;2Tlç.: é\ÈPÝ‚J°³Ï•÷SÞÏæÔOÙÏùÞzÖöõ‚ÊÎsǾ† §U•¿›ü•\zЖ¡²óWYSiã¹5RTv¾º´ñOQÙ¹XþJ¯5Lp³`è¿ÝIúÍ÷Tv® îS˜€ÊÎöÝjgü ú·ù·‡µá6 •ßo™NËg%追_-hæV èßÞŸÅš&çå3Tv~ðá.òB©¡»ðŸÇàüœ û^ÞOy?ÿ›û9=î5ü'³ómÁk­ i[5 ÖpýŸxÒË[.ôÓ,Wª³j8ºäæ XbéEÑÊâRh¬˜ hêÊHf@ÚPtüï&ÚÂ=Š¡.ÕT©p w…–ºpÅòO¤ŽPÒá F·ˆöàl®Òідk:C7í ƒÄ–C¸ªjãé¡3« ¯˜ÊÌ¿?%èò•ψƒJ)׿zIáö)MËç^iB•J¨‡µ:Ýq‚Qtmã2訨‡mH¾=ì¸Ö÷>JÑ¥ãó‰‰Âe‚ŠÝ÷PÜÁN柦è +¶±þ×bð9êøsCWæ9­5i\±ý vCËVíõ·/&–µñ\±÷ò~Êûù¿ÜÏoýϳvËþ\BŽU¹rºm?ëv<’¢þ©D¹ì"wû8 PHËÔª$‘~µŽáN F0¢Û††?H_½ î¶$’À¾ÌÇÔ§—T,^ÿOÒÊVG]»Ë†>åª5œÏm„¢'.·W@“²Ö1 RCÑö;ëéÀÖI éŸKçÔStóg/ð’¤*–O!IË:Ò°C_¯'³Ê'r½5·E±€jÔ{ÓÞ¸ösŸÕóŠ(j:j|ì0–¡b÷ÌW°¹} àÊì;×~ú3xsˆQ4çõY¨¼P±ýJsZèw‡],'o1Eåý”÷³9÷³Ñ^Yjú­–ä d²×x«Œ»¸‹¢›Aᵓ5úFq»ºøA‹ ~PCµ·õ+†Ò"‚š]Ï&‘‰j€ÎÍÏ&ûL†0TiÆsb>º˜ bùú>¦}v¬tÞÓ®l×q ®YûpxêË5ò) k~žå¾Îm$Ùë†Þs Ìßsú²ðµ}M[BŸkïf¨X¾òïêå7¡G.€ÐÓ¸¶+̘qñ @ýØjÖÅ› 5.ÁWm}l«AÒ–œåŠÝ?”oË̦Ídh`OSö(q5· gy— P»„¾ÌgŽ"WlçŒ4Óè C5jÇÀöw\y?åýlÎýìèoN÷•¿#¨Â‹™t™æ~nw-gH0;Ç}k¥¡Š­|ã@:éZ2Tâ6zøeQÔÆ´½ð4ó‹-Ÿ’NÔW~¥hîÈýtk¢*CkòO²Ý#¹bùc•2òâ*A­c°Õ¯J¤è ÅÇt–ƒ€ZõßÀ´|Œ¹}Ã@§èéýæ\¯OvÔ¬k4”®*¦èUŸƒ$Öû1AÅòå¿H€ßû24dÛYð^WLÑ5IþìÜ… îè^ãYx§y 5Uef¾³ ªáiF[‡ïf¨Øýsæm™ùÄm µYSM?…„s/tèãá)Ú£ýg"ü£ÊP±ý*:ç!upE}ZV­•÷SÞÏæÜO›sîÔËZ‡ á„ú·rí£8¨Ìt¬£™ƒ'sè™Bź휫LìÔïq“à}-êžæí}º¦|LçV”.e]ú>"¨X~C-=«3€¢þIõ´õ×Rnж`Ö[Ä«SnÁöžŸÎа1Ãa‘V †ïã%Ú]§s%+H§›ª }µ'ܽHP±|—Û^ÿ[÷Ï¡;^ƒÐ1ýðýÕ»u˜¡!'’ìµ·¤p'(Páv\o«<$¨Ø}‡{éüw{Ús¾)õY’È-i 4 ÚËv½tïŽz®Øþ§+KÉÜz[@‹ÚU]7, ¨¼Ÿò~6ç~*:ŠD–åR4ýðT0ýéÉ•÷SÞÏæÔOÙÏâLªíp„¢²óß ³Xæe†ÊÎç¯~Dú¯ÚÍPÙyS}³êÕ©•7ÞœHËÃOQTv.–ÁŽäéuc†ÊοkCÎ0-†ÊÎm­$ªgÏSTvþ£s<™¿ç(Cÿ6_x½=|ÉTv>Zÿ&tÚ¤ÁPÙùÒG§`“Ù‚þíý·CVË.É ý·|SöAÏ"†þíþ$çõ°cxK@ÿö½¼Ÿò~þ7÷óçìðŸ´:vI:¸.‘  ûúzG»pcòӜȆžhB  ¸¦U Ù’hvÒkÙi/wóö‹dqZCóŠvÃØ:Ü…à=s< V ¯Ò‰³qÅòw{qÖ<'¨¦&Ùªu’ë²P¤ñ…M™+…’^×%hn=£óË)šìy[òÅ{=CW·•@ý³”<òO.T,ߤóì¶æŠº¯ÇüØl†UO“Íí]òp ´ ›ÊMÐqaeñ:\³ÛG¨íá± »ÿÇv,SÛáJQÍÃÖ,4([‘ íÖ3´âœ¹s¦TlÿÖiÔ©(@k§‡²‡]*ö^ÞOy?ÿ—ûYÑûŒ`oqJŠ^KQ£*û§¨¡•;·Ï`胷Ç!mÑnÑ_¶)=Ž åß°q+º1T£!®]]IP÷Œ©üµ³ö'ÙÛ~QtW—wD%º5 bùý·¸K†'¨f¿‰‚¡î š=\ út©!è?©Šä“« “sÇÃýá xtÇsç±ð#*€ÎYJ¥1T,Ÿ’û+ZÃн7† ¦Ïҹþ½#ýÜõØ^ Íû4„{ñÃDpûõš¢%Â=²`W1AÅîÛµéË./}@Ñu´3ÓNoÅÐËZ=¡É}* :ã/5*úç¿ÖŒY(­gèÒ®IiÝ!®¼Ÿò~6ç~*iu¦:‰¢%? ò¹_ÒË¡4·š¶ô8åw&¨Ý‰‹Ò¤ÌÍ m×ûpkÙ0ÁÌykÔÊ2‚˜·lÇÐ÷û²ŠzíYk0T,¿E[s!ö3PÔ{”ŽôGŸ"niÖYjf]EКBcæìY! é‰5`£ïMÐé§ÓáÄ£`@ó» "t`è媠à?P±|‹.× ͼ0ê.Äsd«PËÝ× úíRwZ]6JŠª}ð§•šë)Zi׿½öc¨Øý ½–ìÛM]†:µ¾C%_̸¾??J¤P´Ó±)p¦sÅö×.;Ÿß2´aWÖ4¹òmἡA\±ýÓ»> †Ëµm|ÜIPÔ*’¢ò~ÊûÙœûYÑDˆé) C—Ìý×ï¸IÜßgªÈT'O@“u+%ˆqkÌ…‰ AÓ· ™moR.ƒ¦5q€¾M;LOwÜÇýÇÖ„lhjËк•­@}üS‚Šå¯4í U¦ –3 öÓãÞ—@[ÿ#\Ýù0!À‹d5•-°Ì'¨©]ïr—¢ Ö‡ÈÇŽÛ=±+Ý% bùjbN€NèVŠjœ=‡Ê ¤hëB}¨¥Ç(º2Ñ ’wV è@çÌåP@×ß§*=*v_úç± IžÄТÑýÎi::s 7y±«_£ z½å@f겞¡bû³Þ„ä_ï)“¿Æþ>Í•÷SÞÏæÜÏpÒ–Kf0´îÇPP¶›ÎõÕA÷Í+&hõÀáÔÿm¦5zY§Œ¤lÉÐn±Ð§§7äñf®žJP§ìqŒvbh‘mW~Ù¢ZŽ«!¯CÅò¥`Hœþ¢[€Nc¸ÆÎÏ öµ+÷WÌ-øQØPÇv— £öghCgmPï`ÈMŽž9ÍoS þq? Q±|_îsŒ 7>Ïçˆv€æyúÀ¸ÒuÑÆwÖT[ɬ\:1tä Hêu’ b÷ƒZÜ–ÚFLehÜ g²lå®_‚´Ž9Å•\¿вWl‹ }À"^t´›&Ü·ŠTÞOy?›S?e¿Ü·°iÒ†Êν?çJÜgDPTvn1éØü¹#Eeçåf+HׂT†ÊÎ3³‹áZgG@eçbùŸeAä =•{Õî‚ öQ€ÊΧ›üëfË7*;¯Ž‹#';:ú·ùöÑ è¨ì¼Xª‡ç*;×+C7lV¤èßÞ×ݰ,èìÀPÙù²mé°af'@ÿv¿ÎÚ6,ûÁ6@ÿö½¼Ÿò~þW÷3í1ü'«6:±S7ÛZÑËu:ŸCÐUoVBqöV†ö¼• ›?YqmŽž\^DPÿSUçv’R4Ùô4L>ÕЀ;ZÓ9‹ë]¹R°RÎPi(xŽŠåo?‘ 9¶€>2öeÝlê šñ¯_S÷÷Gq™ÉcòÁØÐoóËiY»½ Íøþ…DuËæö©ÜFÂkÕx¯ó¹ñ¢bùèÌ0â9§C—¥j@ñ—aÜv&ýÒr­0—NößÇ=5q¸Ý}LÑQ"ÁºãX@Åî{šѵÕFMsKhym!wáÀHHœh’³ íò'‘+¶¿àÙ>aäûMú­ 7:Le¨Ø{y?åýü_îgCìf–Ö¡NŠ~\À>­T¡èºùw £Î|®^môÓ9KоºoÈÔ™m::,,æí¡hÔÁÖ,àE ç]Ö³K¤¨·Ù!Üg Õ îÌvb_†Šåª¹Åý~#EuþÙÊJw¯ èÏî焵ÿ ÔEÚ‘µ8^DÐß’!¡C/@¨†.Em2—ÁåÀ†êyÞ€?Öª€ŠåËS›—{š3tÍÞ(öTäªöèΨº5u +w† ½[¦S£ê@O|÷gï|QT쾇±`—AÑÚ˜÷Ý~G¸æÖÀ¢<–4¸½+-Ñ5b¨Ø~×?90Ù°˜¢mÎÂ÷㽕÷SÞÏæÜÏ‹g7°ÇSŠ)ªo¹ˆ••é3Ô5ãŒÓ^èõƒ+¡zìAîØõÀ>!èæhkÈy¨ÕÍôÀrW†¶núNú~óâ® Û^S¸ÊÝÝ _DO@Åò›æ,cWÃ5j©6œyþ3ƒÛØ~4µg-E‡mWe[,b¨ÕíÈTӴĽÌj5‘·2UXÝ;˜›‘°ƒvˆ>IQ±|‡SŽƒáÞUu«= ÎËŸIÑ ycØô6)êQ܊ݨÎÐÁA»ÉVý\ƒ£û@o犊ÝÏ}GZÛã E—>N2m.q[>Ó‡Ag?qUU{áe?‚ŠíW_£iª'Í7Tcáýƒ¹ò~ÊûÙœûÛi4³Þ2•¡Y{»2§Ü ΋w³Í2M WÇs«Ý¨Á›ÿÓ&y pjÕü\_7ER4Îן%¨aÓ¥Bßm(ê}; l:“BT,âÈ·t ÕN†Îv1¢G¹Õ;¥Ö‡Ó¹µw¨áÚÍ”¬ªk¨ÕÚ¡h…¤];U ›—hèESš²µ bùÝNC½Î‚ª-O·Ô€ê›+Sƒù£ºÄ2š ¨Ã=£øQ>¯¨Âöþ…w?¯¡¨ØýÚÈíÄÅ¥Œ¢ß àôƼ *?—T¿ú15ªaíÏÜÕÌÿúykÔEŸÒTý9 •÷SÞÏæÜÏÅO+hhB,Cç=N›*qU÷é°g:“ý½r$ó.‰'¨CãöpõŠÚ>¢—æÄ24ùQªäÔ´@‚z÷kA»Þ¿FQwE+‰aËX@;¾õdÑ];1T,‚™y.=È‰\µgÁÃf÷UïûpçôW ÝàOvï»DÑWÉvàæ¿X\K]^Íc¨In(Ù¬âŠåSÍØ–ZQ€Jô»Â û£\%-Cø4¦3CW×O€¦£»(ZÑÓ†)2´!i8T[*vÕÙjâ'¹DÑcíàòÐ3ÜG™D±Ín†zíÊ’LG®Øþ³'ÛÁ„m³ºt‚¼Ü¢ò~ÊûÙœûy¹Õ*éÍÎÇZ´qdx‚;ú†sÓGÑÍó´`„&Cÿ ì·†Fqïf%Á±ß(z6ü'ÑÓ™ÃPÏ×`x\™[«ßT¶r+;º@—YËÿkÖh˜•ÊÐkõþ7`!÷S-…¤¾:€žZ¿}·rÓ4·Azô‚^óžë£Õ4:‡xr߾̥Åý¸bùFê'1¿²u/^ES”R¸uÆ@úÔ RôvRx¢šJЉ·¦Ñ½Ò€v0Üʾ5¢¨Ø}ÿ-¨ò?BÑSzÀÚå‘ÜÉnw¡îÂm‚’¹#ÀâÓa@Åößðm -(A³5 v“ ¸ò~ÊûÙœú)ûMQ‡y¯3*;_PÑBÚáËP†ÊÎ=„@½ÝV‚ÊÎßv» úë?ôß8>¶ç$P®Ì'–ßuóÐô5a¨ì<ùm0)½¨ìÜÅé·T×(PÙ¹òªväÔÛ·ýÛ|ª†ºLùèV@eç¶êÁ¤öäm‚ÊΗ¯ïMnØÄ1ôoïo–ó*ŠÊÎ# nЖÿ¬ôo÷ûì†^Rgèß¾—÷SÞÏÿæ~{݃ÿdvT6UY¦ h¿Ý×鹄›=Ò#´>s/ï…# 3u¹HÒu0Ô³dĘÆ]¼¬?½ÕŸ¢ÙÓé܈p†F¾ž)¬µìh©ñÒjÜ%ŠŠå·9k.˜NPÿÙk$MšÛ)ª~¬†(¯ÔØ`!ûip… É}¥àªÖC@U»«€–y A¥CJ¡å¡ýdcíÖlT,_Ö¼¶D_Ï’¡Öãî‚g=¸®I›™Ç*M‚Zå®`µCoRÔ7â’à_>ªõWR‡¥­Ç2Tì¾b—çäés†–©‚Ö‡\U.ì‹z®á¾\â»Ü‘+¶ßHÓÌž~‘¢UG«É¥ûÃ{/ï§¼ŸÿËý<:ñ}ã±” ®3K¨©Ú|)ºåôHhy*P'»YäŠC:WçÛ>((ÐC?ÆÃ§ƒŸ :cõLˆœ¿Ÿ¡†KËÁ}ÙSо27†Ë- _ÂÜ5´ *úßW¸9>àE¥ŸvmjQ±Š=OÌ¡¨þªöÌá¢CK·O.Ôž©¨±j æz±A%A[è„URÔýî0úîEQ±|Wwí÷ÕYoí¯â~ÑíÉ®eÌahL-P £®žSDžß/Eí¾ÂÁ¹G»¯ùÜÔ2Të¥5(¾0ç.1•ô(J–§ eÛŸ¼¦Š®U7T[ ûy5€¢ò~ÊûÙœûyej:µ‰¿) Ÿ¢WÐýÎ>}°ì­b½Ó×–ùÅ}^%% Ë« Ú¨4P(ûU& &©§¡é «ÛBÍåíÜÆEçɵ/Gm› %§*–ßIh ú»fø=VçžVz,uˆùÀzöÞÍí´¸+|߆›½l°mÈn.«Ë'Jgú.t«Þ3D@Åò}Zn ‘F«$è`WSp²Ž û2Èe ŒÛ}¸ên冸Žg­&úèz-¶c¨ØýùEÎ0ÜÛ”¡m”–Ciž7õÝ6ªpÀA‚º;V¨àÌP±ý3¾ï¤»ïaè¤òiàšÌ•÷SÞÏæÜÏl‡Wµ”,ŠNoº&é;¯’[W·šõJõ&¨Í²ù¬›ÎaŠÞy|ˆ¤ïkÇP;³ p(÷3E“ºÒ‰Ïõ-¾æHí¬5(ªàQ?l…%¨ßƒ!±¶š¢bùsu}á‚{Eß”DA ×]\áC2L{dÈÐð¯i `8í:+Ͳ,ö3´M?¦J¸“ÜN·›3¹‡rTln/ bù”N½%Þ\#è¸6sÉžo¸)mÃá|ï¾ }¢œ¤¯EÝ%öÐ~³*C-*À²ùg *v_@¨¼Ôž¡-Úï‚ò -¸wVÀ¢«¸á~·aúóóTlMû+ ràE“òûòN€Êû)ïgsî§Å Rÿá)E[Œ8DLês‡Çudq£úZï³ðdc·°ö¤½NÐÄ«[ažt= {W–“>ó®RT{ûTððÌ”¢-o€óÏ6º`ŶcXV\å~šäóªcýý^ÜUÈ~VAЦíÛhø Ü^1aðP0æþXä§ÜçߤÉßô)Z¢ª}63T쾟Î^°)xDQ¶ÉP™Ë½¾, Š×GºúW£ð¾M,Wlÿ„ÕÄöó@% i겕÷SÞÏæÜÏ ûßúº«51ì¥Q)ÜÒ.ÿú»ÎŸ¡)ëíÀ9w×nyORîtP/ýAÌd…+÷Ö %0/ÆU˜JË®U©/!–ûº 8 ¾«T,–Æ!¸a«¨ES TßµãªDK”Ÿ#¨£V/!rÍ}¼]~¿Ô1½?=Ø4‰ÛZ0Té, 袶/„s•;Ëg5°‚y½:‡^ÜóŒúØ$S4Só¹à¡höŒî$àg1A×¼Y-²Ô5à ~øTìþszÊ)«–•ïô¸[ã°ˆíÓãoHUH†ŠíW;!õ0‰§hÐ+¸`¶›+ï§¼ŸÍ©Ÿ²ŸG_spQœAQÙùîœõðÆý:Eeç¦ÚëÙà¢d•x0ƒ_Ÿ* ÿÀýB¡›ž2áÊ|bùw?])·*;/*·'Éj(*;÷{QBíI *;ðʃ9œÿAѿ͗uï4H2d¨ì<¹ßûÂqG_HQÙù‡1úìÀa€þíýa·rÁ1ßR@eç‘G»ƒËÝrŠþí~›N>pêpAÿö½¼Ÿò~þ7÷Ó3£þ“þ}ý­nÞ¤hµí5©Ž C;Þ*&¹“ïT3û§ÄÈnö§qð4m4×ólÙ¾¬˜[“¯ Gµ}º¹_ ¸­ ¦èžVã`Á@î]¶NbKQ±üž++éZŸ †NÿІ~¨ŠçZ\¹M=¾ºp‹Ý:“l·Dîó#iÛeÕ%›TØ„yÕÚËé)ÿ2›¡Çþ\åývËW•xt§7höš«ðáBA/8^#?z;3´k¢4¦8qûÞ›‹¬õG§ ÞÉ»W]Šo„0ôú:kx¥´“k­ F¿77Ë_x˜¾+¶?$÷)wg3 êM˜ãã·Tôç'ï§¼ŸÿËý°˜¼ÑîÎП«‹Èþ¹×7†Ðš’ M!whÁ¦þý9­µYçVˆºÇ>(¼fdÄОEf`Ù'‡ K»<âžSÛ ?¤¨S¸b! ¨Xþ/n–äËÅC UÚ¥?'ÿŸÆ]\ÿõo±½\¿î§ ø¨„«Úþ¥=Çq—ÿ‰&»ÚÇsu­Ç’ź©€yoÎìw<P±|5g@sL/@+= *Ç—ÛíR"ì|Ù…¡ó: Ð>kE}Ÿ,dnrU{§’§ »*v?,~zocè{Åup®³?Wyêt]‘CМ)o©æO †Ší¯ß¹…®‰ÎP¿ÔvPÔÓ„+ï§¼ŸÍºŸuªp»Ãp†.I %¯†r¯Í?A-Õáº÷·*UVq—ÖƒÙQîÃwÇ¡K‰"×ðøi¡*ã"E#FµÍ'†.UIJ»¨q³÷-ƒu?ÇrÅò'dM‚#1q }?9f¦r;Î*‡ÊÈIzÛ"Šuuõ> æµA y^u ÎR4ÍH2j0ôzœ@¦]ª!¨X¾ ×fAƒN ú H«å©ÜÁG.@«åq5²Ì„ŸÇú[+æhQ´Ä †i7 *v¿úÐNèªîÃP?…½àxÀ»öúÒ/t,7qíL°IvP±ý¾:ã!ÙÕJ‚ºýŠ&Ã…[•÷SÞÏæÜOwÝ0bZ_†Ú}ßv¹õ·ˆÉ—®Ö]Opœ·ƒ›gN”åÔô~ŒÞè'µtØ1ë E ŠÏA‰a @UFµ€)fuµšÿVzÄX•¡bù;*†Òz{†Z|:Ó×´àÞû64ÓÖêý}i ˹³kÎÃ÷#ïúÜr˜:Mç>u6¸FÑ÷I>pýʆŠå3~*ª¿P‡?ÁôñÌíÜ"ß5Ò¸›{Ï¨Ž¸úæú'”Zð„¢K‡BÉºŽ »3/ˆÎ†š&eyþ`îåUq¤WÖU‚Æ~  vûËTlÿ·»»©Â'3)zûAº¤Ÿ”¡ò~ÊûÙœûyÚ'\Ã_Q´Ð;æ/ÞËíâžS€¡Å^å`0|E¨H¥OöêrC—=õì˽xv8\Üϵ{€îáú¹åƒ»ÁR ݾIjš ¨X~­v…Ðt™¢Ap ŸgHQ‰õhZZßPÇÓ~T%¼¿½¶ÒŒìÝ¿ÐüA‰´WÛÜêãÌvhsý~UxqÅò‘|eæÔg .ù}XtÁ‚&{Åsm½¨ò‹~[Ä•–¢»ãSà¼Éw‚ú\9N‹Pô}Ï ²ábG@Åòu=d ïÜ&h‡I Ä*ê5—Ú{ÀDçšà´%h¶bG¨B*a«s´¹b÷slçCaE8CnjÉîìäP:Ÿ¢}º|&uµµÛo:Ü´l$¨¿E“ä·OWÞOy?›s?CÚO!3‚¡FŽpÙ4ˆ[X±Pˆ;Õ–ë{;™Ì5Rçf-ó‡ým&êõ|+y4y÷ܳ\ˆlÛB@ïèDÅ̹€úߘ%^º ~Nƒ {¡>CÅò[ì†-ò(Z·ØL½¸•OçÐÒþ€>³ÎN|Ôâê¶s€HÅ\÷{’À¦½ÜàŠvÄî¾@Ñ(»8Ⱦ}R‚Šå‹T^H¯4»ª·´åØÜ“ð&û·lÚ:9”q•3_ÓUÕ µX?¦çåŠÝz}À¡Å­âažÅb®E÷hz¢îAÃÞ5ѲÃ* Ûj|­òÿعó¨ÿîø™*!• I*I’$Sr®½3BH’TÊ<$™’JÒ$sEædªs}®Ì™3e&!¾É”)„ç~ÖóìϽÖù­u_Ë÷Ý:×?¯?ö÷ÚûÍ÷Íéüƒ‘z¿[ó5Ó%RÝOu?kr?u§¤ba¿IÙêÞ~</pµi1[N22¹SO¬Zê#’·ë7–*´F!éUæ'9Ü;ß“œ9n „–oA²êK+Éòž÷øÑüðhwÓvkiAv7®\þ$;Ä{÷M2>Ó-¿òàˉ’Fôq‘ô›a)MêÞC"S»XI_ï H*W”üj=V’3¯%Bz­õHnÚ2^ò>¥$eûY0J,j ²d¡µ¾z:#÷D›‹Q^~@¦½ëǼÃÒDòv¹3Þ¼ÈààËbœnk”»ûö¾~¨Dæ:@¿3]¹Qk÷CXÈzî!«ƒødzw®ì÷/Óxtÿ9N" >\D “y¤ºŸê~Öä~ž.ÈC壆¹6ú,^[w‘ñæmÑ·á m /CÿÜ4Çjv¥A‰ÜÔo¬\Ìme'uI‰b¤yÛzŠ~}%Ò·ÝH1§«÷sëÁ˜=~—@Ê寨…b½\ MšW¿qý‡'‰Rýx‰ùUß<æYñ‘={éÀu9à9AÜ„F'¡ÿ‹0îÝÞ>èß H¹|áÕé‘ yçù(Oj)‘~ÕÖÌpõCF~8?G¸4J!‘:ŸoAØÞÁÜT›<ÔߤÃH¹û{›E‰TŽÌÇm¾3Ò1({}¿dë…M°ã“ùHÊí·»²;”NC²ì°\“¸ê~ªûY“ú©úØ) ±èM•HªÎcú·îêi!©:Ÿ•€l˜DªÎGlsÀE_©:¿1ƒù$’ªs¹ü›'7‡SŸ©:÷v[Šº–©:o.¸üXs‰Tûy'ú ÉU›¯QctøØA"Uç¬Ý#·™$‘ªó¥^8_¹ É¿½oýT® 2Ru~ï’û¥] äßîïWôŽl|äß¾¯î§ºŸÿÍýtõ:ƒÿÉuW†KNyN¤ö¸ARô‚Œl0Ï ë<ò]ê!xîßɘ:3p¿–•DÎ1]ŠÙ=’¤eØff>0–‘A{ÁrÊî¢&uÐBÿ wâœ×P߯/’rù#M[ ·1R9È Ü£ã“ØëûÝ‘l1ï)Ër¤ƒuÞ:Å}õ¥žlˆäÚ>›að­³@ÆmÉŠÊô%R.Ÿ§Ël^Ø‘™Câðõóa·e]¶ÿµ•D¦^wߢîÕü;வŠË~(ÑaÜJFÊÝÿv¬»8t™Djü2-?­à¦Û5@£ žÜ 9S0ýøS‘”ÛïÑo Ú7EòÖë\Ø’ëÆ•{_ÝOu?ÿ—ûY°·‡´.ÿ*#‹ÖšI§K›Hä¼`]ñµdƒäæóYZ@ŽY®Çš\çfÏòÒ·ÌæZüêÐEINîðú4ÕFr”§«Tkk¾Hú52Ä­S’$R.ÿþ܃ðësc‰¬­Ûý*šs|²s!±ŒüSm…©åܯ¿ •6@¦nSvw>&’ÙÉ{ÑàÏ:F^ôí‰ÖiÉHÊåå ÆgyìÍBL¯2CrÉf˜{7\"W†Fa†7·“©ö½‘Ô3±dÛC ”»ÿÃ,Uðú”(‘¯ÌzÁ¥Éܵe·`úÎ ]²³ôÂD%)·?®]-–ø<H— ÀâS:#ÕýT÷³&÷3üáW6¾©³D¾<µƒmú1ŠëµÍ\ Öz O#l$‡S9Œü`Ö[lÖl#×kìe(YÜY"½]ήro´©TÒk>#Ë'Çbý˜@¦Û)‹^NH¹üùÃú`±D6›2[/ÿÀÈ=õ°®u&7{¹€…C|òLúJØ^ð™‘zM=0}Ö'®F=S)¦°ÈúszÃ`W3‰”Ëwá+~ ‹dû?Õ°50ŠëbxÃ>iJäù´Sø®ÿ~‘¯$Ó¬WáïÁHzn0DzƱ@Æ\biIGR.ß“–pbs’vm™á–nîå6"šjÛH"UçN¾k…ï‹c©:÷ˆ^Ö‹œ$Ru.—ÓŽ~8ñe,’ªó‹ÍÊ`t—õ©:ÿê¼–¹v‘Èÿóë÷pÃEº/Dòoó )·“ÜwוHÕ¹–÷–1rÿçý“mÐm‚±Dþíý{W¢1°ªƒDªÎ/n¨3øÈ¿Ý?3Ùßte@þíûê~ªûùßÜOÝ›yøŸL?›¤˜Ö´’Á÷"EMå Ǭª€® ŽYì„›}^3ÒütÑgU¤HL=Fk/3²W¼.ŒŽ·C2³ª¶´hÔv kW Æ5îÆ¹eñ.8”¤\þê}%Ÿòc@ž=ß[ú}L)‘6à¢üH¶ïÜÝ|¹¯·èØH"/v·À™©Œ\ÛJ!†zs³ì+aYÉR‰”Ë·ÐÌ¥; “È‘Cw q1ÜŸ—c zù õì; º¬äŸÛ›!9vT#МÉ•»òV;0é#R°°mŸ¹fyšÒo›»@¦®k!]û¬-‘rûÛè~Mé5#ŸÌ[†Ù?ËERî}u?Õýü_î§÷ÝRqÏ: ?DÿõÞ¶àæe Æý³4©ü숎™í,:<íZú12?-/uT9¶¡‘d»µ–DÚûg z¶ñ\‡¸Bù+îÔ ‡±ÍŒŒ”Ëo|¤ÒÎÝ)6²‘>uÂ5iÑ>g¸"™9¢)Ë6,2xZ4)iÇe]wA•AÕ¿Í[Š3‡ÈlW{áCÿ@Êå[äul’ÇIäÖK´ÒRp#W*]ö÷S’%‹EáÌÕÕŒ4úy••ä)ÈU-/ÃéÖw)w±ír¦®-‘¡ã¬¬¶!·í?¨ˆOà¶Ì^¿­¹²ýÌŽÄž¬üé–¶/çªû©îgMî§Ë¤»¢Ëþ8×bœhèöH$ÊÀ»"Ȱia§ó1îöç-1"!ÉfÎÕbnX(÷Z§™èðe¢DøºÇØÖfäüÂbø1)I3‘¥Ž¸"’rùÝjHv-ÛJäÕa©¬ÿœaÜ8ߦL£Óq%yz¤Ø'í`äëQ æcqÈéw¾³ôÓÙ"¹´ƒ>¨g%‘¥wãê°, åòåìö@›NZùÇ$;Œ4›YFŽÚÀ5=R‡E,p²[ØrVG¡ÉÈËÕöÒ—ìÛ@.Ò¶“ó7úrõò 1uß®†a;eËq±"Yðâ\ÉûÀH)(› [9T"ƒ]Òpìšæ\¹ü?†RÛI)vnƒFiƒ¸C½Ð¸Á% ãµÑ2¢7’?4'£×ÃP‰<¹ün¼ÁÈóÛ›`H»G\—Ž¡°ó åòÅÏ3Âl›í@:×ÎÝ-y\·7kÐhe×ñÑBdq’¡31õU’@ž ¹χš!)w?æÃ ÂÖØ} ÞfÆ µ_‰Wöcdã¬DìÚI¹ý³†ÃÈqAù¤ïÙXY¤ºŸê~Öä~>­…7Ž3îb Œk™Á½ó&Ì›6["½üF¡âV W7/wxÄ0òü`G<15 È'G—âØ&Œ8$ ׫ƒd·¦{Ûš;³h­ÐcW(#åòw ŠÇÎXJä……“1S»”‘N(""Æ ™¨WÉâ’ZpëÎ߃Ü€tnìƒËº!ùæéæ2JddáµAh{e‰DÊå;¯ažëÒL]¬#nzâÀµI²ÆVC— 999Z¸Æs#wR\˜¦ËHqçܘ½—+w¿ÙÒ«Ðó±Dn{úÌ4äNº8 f$"y5«©4ó£6Wnÿ½cmYJ Ȫöqb‚ÔJ"ÕýT÷³&õSõ1ê8{È\FªÎ_›lÆ÷¬$Ru>u_O˜c·HÕyrÈg94IÕyªc<ºï» ªs¹üaq¡èóc#Uç:ËÒþQ‡Ru^~4zßvDRu>oÕ^̾x ȿ͗»F—ͬÔIÕùÚ⡌í CRun”Н]í‘üÛû;'ÔÇ ;4$Ru~ß÷7 >x‘»ßkº Þ™$‘û¾ºŸê~þ7÷3@<ŒÿIÓì qòµx ëîî̼¶tá.躊]ù=B"!ç±pÌ)’{ëñhÒÌÉíÓÙJãË@Þm©•‘K%Òié*´½÷›‘ó·á§:HÆÕd%·-¸rù†]‚_Bm$C¯‡@–Û ½„cjé‰ì˜°—½Íg¤Ë‹ÚŠó+LµX+lêáËÈ/ÉnðdÇ-î6˜æºH¹|œzŠ·}Ÿ0ò„ðL~ÛJ"7Z†óûôFÒüˆÎqŸÁÕþd"™Ï½dÄø©¬N[‰”½ÿ¤\ -ÎIÏGEes=F¶ÝÖ.¬à¶Ån©¶@ÊíìîÞó£îB$­¿J“;)÷¾ºŸê~þ/÷ÓÑj +Y8K _÷ÜÀÜ>nɧ´Ð ù‰däÕ:}¹®—Iµ=Y”Ê>4)‘ºâ0êý ý—®õ|Ø' Ü'‹Œ³§úÔ®ƒ¤\þtñ’pcë6 5f*Ö]iÅmn¾ gülÃÝ·`*Þ8ÛɧÞCHècFþ0m£pDz=]/¹8zóÿ1A"åò…vÍ‚Œ (‘áq&˜´Ó•µo%8èMG2²Ç±©U/®òG7¼QÂHpÿWÎ…»”»Ÿ}å¥òRŠ#^ÞP¸ÏçN_.|(¹dâ“|6zrFÊíÏÈxÎò^`dpÕ¶´—Hu?Õý¬Éý|¡¿ýöðgä¦fë™U¸ÈÍl™€]ë>ä~ÔMÂðmcD2îí?°×#F"½—¬@û×¶ÜôfåŠç®@,i“SN0òåtc©¢t<7ž …‡×"%R.¿F3ë¼Ð°…J²ÈÒLèª'#º}„üîVHÆÍ=/<¹Q¤éô›·Œ«;Øø@2fm"¾xÓH"·Åé)—o™¶Öï&‘ïÖ&£if®Ã¬eìô›r gOeMª2Êz óÙ-’GÞî½)%Rî¾¢è€àX‘ÂÈSmšlMçfï1V×ò—H¯A3Я‡W¶G »aƹF–ïÔǰ·+€T÷SÝÏšÜϘáÌöK‰,<Õš°æþÚ81'È5«mÐ(ú17yÄfÔˆožGîŸ0C6WÙ/ 1ïçF2§uØ÷}’@Îܾ#3SdöîBëg€”ËŸZ«>ä%ïadóo‰ ôÊu9PK\Ùw™@Öݧ»B“éé;|×G"ùæþ –îч낇¶º&ŒÔ=1u<såòù.ÊĦ7722-é –tߟOz/v`¿Ï802R°±Óîƒ{qžC÷Z¯PêÖI¹ûþc¡,~?#¯ä­‚ݱ‡¹æw I™žH^Ÿ3m ’²ßÞÉË3Ød ÝlÆ‹±ŒT÷SÝÏšÜÏÈúýż}%²™e®Úb,·*þ0”(YÐêŠpûÚ2î÷VITÍÕH‹Ï÷wY)p#§bÔQ7$‡ü4…«ÙQÜóií°Ä¨©Dd¯Ä_Ž–HÊå÷|ðnH?yب5~]õ«+þ‚ƒõ‹¸…oÇáÀª9\çöÖÒìgN@Ž[ð™¹ü¹ÇÈEß½Ðïè8$[/ÿ‡7áÊå«g³—ÏÛ ¤å’MX>ÑIî'àV´ÈHíi-Ðaí n¤Î.Q³ÐIí¼E‚AÊJFÊÝ»ø(,]ÃȺµîƒoÀAîÌ—×­ZM‘,˜8T }+’rûý¶ …½½%²ÀHlÀU÷SÝÏšÜÏÃçƒÎÊ©ÙãúM˜t$”«1ùw¾CY%é1d–°jã&F†ýþ+%î‹é=Ñ%ï>wö´ZÒÖ$uâkKóÌaäómY¬Vt$ «þéi—ª)‘rù‹´‡ã¥#wiðbŽÑMå®ÖŒE§fí€ôÈš‚Ÿ›#ù»N‰ÂïV‰tè×Ï\mÊW•)ú½ù·&+»c\E]‘”Ë7·ÝbÌKŠdƒÒ®Þ}67w§€©± rPawþn 7¢Ë×nJò`R.L®×I¹ûUÛ~BÃQ;©9VÍßÀeØóe·é÷é¸ê rû“gÀö 0r^Ø*ôèØIu?Õý¬IýT}Œ_áÍos$Run2}8(ëH¤êük@2fÏŠSªó®?Ö¢©•DªÎ3Ë,qBöX Uçrù§mODé—HªÎÝŽÔF»63‘Tw¨ß_ǵa¤êü³ðH¬Zšäßæû“X‘ ü?¿~ßúXtRIÕ¹ESéÉÉ•@þíýÅ-1S{#Uçfiºx»ÜɿݟíX k;DòoßW÷SÝÏÿæ~¶³Ýƒÿɵ/ „ÀÊHæ…‰¯-q]–y(‚ç ¤†UW…Ù¨ Œ<ÖÚ½Œä>òÂË!1@µÉdí¬‘<ð¾‚-š0P$ÏŸŒ4ã$Óg´f.Ö.Œ”˯ßo„¿4BrÄ$W!õê¿õx¸H8û¬¥D:¤T‚˲ܙÕó0·Ý) ñšî¼ÕI½Uø9 M$s5»‚ñ?k”Ëçsë2‹S˜ ¹¿¦dYrÈOî=°À¤Ž@Bd'|Yo •>.ø®ª;#5ꀳ—µGRî~¶— ´GF–è¤ ƒâGýÛü{Jï¢h%¹'o!ì²2’H¹ý÷~¬@#E+‰¼td3ÚGþ#rï«û©îçÿr?ÏÁ|‡8#éÛæ»]ß’»µ¤xxÇÈ{¶e°c]3‰t ÑÄ×»"™’ž!jäôæ:ø·UjxÕ‘Hû7 =`ŒÜ÷´¾øÝT »/6c‘uô‘”Ë¿ºN#6e›&’c–ofå­o¹µ%Ò©ËC˜\qH‡ΰáO)#åò¿È×–ü/Ø3²[H5«­WÈÜBígŽFÒ=±X¸ÿHÁõÎ^Ž%YdÔÁÔ³n€äk6ÿ #'‰?`üØ2 åòixÝú¬4—Èy­@àÙfÜßGHV9/².6–lîD3ÒoÚ<¡Ì}.Ÿ+®WëK¤Üý}³w­{éi}L÷¿ä–xd°’ùǤ)€}—+Œ”ýþ> o;%‰d˜vV¦"©î§ºŸ5¹ŸÊ¦R÷oDrâU}Élx#·vŒÂ:"y¼«ZûLâ.×›€S¶¬adêåøzì ?vZ ¡ÓfI¤MY8Ƭsæ&]߈_õ”ä¨ )Àf…")ûý(¨•ê7“ȹγYÑÜ>Ü©zö¬äÀE û¶e~ó†*É¡ö`$®tM:ûsãtß³MW_ ä²ZPa?K"åòÝcÚhm^ÅH½¶¨¿{õŸë¬ces‰<›V!vôæN_€ú§ÛpŒLû“¯)wÿI@9”U½ad½ÁZè¹î-÷Øøè·iHÞºóFŒú¤Ü~¯KKáÆî)HÖÙü€yÒ„«î§ºŸ5¹ŸYÃþ°Nï3²½«¬ý]‰ òN…íëg#ùyl”¨ã7”[=5L&ì2®4l(.èÈÈ+‡‰GNÙJ¤ºŸê~Ö¤~ª>Ê?É,®¡­DªÎÏocïÖk!ùÔÝ«©ó›qUž2“™p=¯’ªs½ûzVõªRu.—ÿñ™›b:G"Uç{¢z §Þ‘TÿiÍ^9iK¤êÜEYšYïfäßæëݼ1Fo‰Ru^Ïd"ÚZH¤êü‘Wæ·–‘{_/RÀQiw©:O¹§œÑ&P"ÿvhkc¼UG"ÿö}u?Õýüoîgr»­øŸÜç€ûtFrÑÈ>¸OáÆýt¸œNæ&Öͼ>Žâjôø*:ý®Ç=/õg‘“dø…txY$5FãŸÃºHÎÌZ+ùY>ßœ–’H¹ün×W`·CÏÌÌ Ã7£Íôúô ïîÄ}Ô÷‘pZ¨Ïí- ö‹í ‘V«±ôÂ^FÙ¼vwz d֧묨õFÊåsÉ·E÷q¥@^ñž<Ãõ[œ O|%òꀎ8½öp¯°øx+$ÿä\b•.×”»_ÞX4sìÊHsã ÊÓǸ“ ÛÐmÜnË Ðçèl åö·Ì¸Àtò*²¢ûVüEG"åÞW÷SÝÏÿå~Ö7o…›žŽD®[%œ5Ëë>…ÍœmÇ-2|ÂÌüOÙ£°>x¾È`d÷qKÑdîý½w ²±’‹æ ìÎkc®ËðM8×­#;ëgC]ÍæHÊå¿6|ÞOï…dÁ¡–Øysï}[*n_7ÈÈG'Nj2rrÕ&̾5ÈøGAhWÛIû}¿aþüñùvÝrt ¤\¾³zñP¸ã Úø[Þ>›Žûf›KäMÕh¬¿—‘V›Ó™AË&yϸÚuhÇ•»)ìÌ\¸™‘é‘'¹Óó¸Áý²zcÏùˆaî›0Rnÿ¤Ik h¢¿DÞ.ÁµšÍ¹ê~ªûY“ûyóÃNpÉñAÒý¦?Æ ÈûÂöTi¤…I«Z¶†‘ó¼ºcnŸž@ž›ñ Þ½ÒGòÛšulæ¯%¹zÌ!Aÿ<#Cœá‡wí§©xäx rù×8Ý€+×' YgÈ`Xm;–[ëÛfе‘«,1øû|nCûBXhìŠd¶Â@Ô Ñþ·‡FAÖ@>;¹-n1R._É@'!ùðeLï,ÌÚvT$‹Š×bzidOÒêt úßÞä׺}±®[]n—%›…´ Þ\ÙÏ÷GÓ„¾ï22»Ãu!ôd%7òG½ž:¾®¹>¡Z™þ[¹ý»¢¢ÑÉOO 6ÇêÕwT÷SÝÏšÜÏ=wú Î {#ybÒ q‰±·lÄ7±þ°ZÙñˆôºbÀ} < z5hŠä½OìÁ‚Ž@š¦aHoFj m‰Ž?“€Ìk°E\¼³’Zɻ؜{)—ß/º®âLˆ’Y¶-Y¬Ÿ ·Ù€Qø¨ÀQ }¬‡àÔV÷€ôoÕ‚]Ê<'ÙÇ6 ^¯–0r@n<úúíÈße{ÁÝ}"’²Ÿ¯^ßËË™:v9Ìm\Èm ¹ãè $¿è×ÁãÍô¸mëAÆØùük$6½SÅH¹û›šK¨'‘îë§·Ç3O ü?ÿA¿£p®ÍQàª<ÇÌݰPá ¤êüfW|âÐM"Uç½&iâÙ[íTËå/ä˧a¤ê\ô{æ¥1RuÞ  [ñ¾’ªs½º“¡ãéñù·ù~t‰Gï 7TÏ9™ Óƒ²©:×_kq‡Ê€üÛûž5Â]æ6©:OœŽxä»3’»ÿÓHöõ˜9’û¾ºŸê~þ7÷3þúüOöƒëNF0²¼Q,Mú*’ÓžŸǃÍ%2-Ù÷–2ò‰€§îIä‘II¸s¢'#ã'Ü‚Ö9ç™rëJî…ؾaS‰4¾Ô¿¯{¤\þB+3œîà$‘%f}qúsSnØÇÎ8ýŸƒŒü7?TÉé‘­pr­@VÍ^ ùËR¹C×⭨Œ<×û´ER._‹]öXXû¥‚ îß·ŽÒØÁÜ´ôÔpì•?kP>ÆéÃq–×en— ¡BÈŒžHÊÝgûÛ3?«Žé¾ 7ÓvOT’]VG£u2d°VøU)·?=z8T÷Žfdäx[;fWî}u?Õýü_î§÷›h,ˆÜß“l8h1*jÿÈ?QN8Òk#½;a] +ì=0ûyd™3ÚˇtB2|±ëa»‹‘™1`™Õ]"Súé …ErÁîþøll#åòßNðCïSÙ>hvÌÚÊ-ì´#Æ­Òãœ+z²DÒüI›}7ÑRµOiA7)‡‘N?ˆëÌ% Ÿ \ Š4FÊå›Ù•î­ßÀé[H–L G4ø)é{“!·EÒdü9Œå‘³­Àÿt‰”»oR4ŒÅþÞ!’ï†fñY†Œ,™°GèþÊÈÓn# ÿ…uŒ”Û_šíɾKŒ,û·vBRÝOu?kr? šMÃ#]‚Ô5÷FÓÄtî»Ý:ØÅ4˜ëw;¼¤\Ý«­aò‡ÖHžˆ®]Œ–ùáÆhüÅÌ$’­Bñú‘´.kŒå}HÆ%]bï'¹)—ßÁ¹h—ؘ‘–NCm—ã Ò/è*Ø8 F2ò›¡²êsw®ß³*ÐsÖ’H«“Xtž‘!Õv3o Ž£¬å¶ß\¹|Ø4®½1@rΦBüÒ»`oØÅÈ%e·¡dÕ:îƒÄŽxÌü¾H®Š³OíRîþ®SÞÌ8Í›‘†2dníQzYoçöx½/<œ ¤Ü~ûóD±N[$_è—2·û?RÝOu?kr? ç!*[Lñ È‚K•=_÷oËHß–…P½l-÷ìò‰xÄêŠn‡Á0RÉ‚;™àäó‘ž?ù…Õ@Êåbèƒ ÏÚ©»^Àíû¸»,׳þXÌ5¯<ÌœH rîª$Œ´=Å}µn!;ýÈâYñ¬ ¹¶DÞÛ[$ÖáÊ囨ÅöŽþ䢞©lç˜nž±:Þÿ×÷ÿoïÄ~‰AJÒÿ³+Û¨\ÄÈMY%P¼½ŸDÊÝÓFÖ»]6#«´a¹opÓ Ñ=©’ÍöÞ÷\lË•Û_¦ôe]_0rÙê}à*H¤ºŸê~Öä~ÙjãáùÏ€Œ_vŠôžp£ë²û3²Ðs„جÃ[n£¾]Ð/ÍL$•µÇºçÔ˜P[á±}÷Q–% hÄHççñìðrmî«cMÐJ£¿DÊåÏùc‚9eóÐÖ<É=WË–ÎÜÂHÃ>ëWto‚ëoöAÒ|ö%aî‚ÎÜ ë£Í­x ]>ë‹];1R.ŸKðuVy¹·Ÿ±‡9"™úÚG¸Ì2ε3j¾þÄm:.ëÄÈn!ðn¶&’r÷“õë1ŒŒÌpR,uÔ’Èêˆ\¶§ówùÀ´·ÐÍè1#e>ÜÚÓ ¼yÉHm·†©î§ºŸ5¹Ÿ_ëfBç×€ 'Ãý‹G¸¦;­ažv;‰´mQ“#Ü­b2ìÓI÷ÂѬùŠZ\éq ,O‰gäí~8zc ÷xÏÇé!ܪ˜|aÑð@®\þÞ~; Qé~ s‹ÝÀ.+Žkòë#Ü+:ÂHÇ8KÐé€HnÍqe72%¡ÓéºZ$÷§uŦg[Hä×GñøàÜN åò5Ür‰.ÛÂÈôëILq³¶DXùÞü°Fòó´ápïv7îÌ~ŽBì> ‹OÂÙu»)wßfWÑç«¡DFê5RYpnjŒíM—3Ò5Âyþs”ÛßìÖjÁñB,÷::³+©î§ºŸ5©ŸªOÌΖðOêJ Uç;fõÁ®þ¶©:¿~íÖõÙ-©:ïb…šZ–HªÎ‹Á‡=gŒTË寸~KwY©:ÏŽ6ÀãÆ©:€å;©:÷úd Þíÿßçáÿóoóí /G·“HÕyÎñ,qAk+$UçE;CpvY®@þíý¾ýWeÃì$Ruþε‹ ¹æ,»ß}Ìq¡ãz‰üÛ÷ÕýT÷󿹟ƒ;­ÀÿdÀ4éZÊÈõóVÁµ}¹–v ¥aI@vQ\·Ü©Åm¡¨‡3Na¤Ëa 4}_ÈÂUƒðEÖnõŽ#p»ëYnmå XY7Ž‘0©%¶õüH¹üY?¶Š~È‚A½D½•¡JÒaö,ì;u£’íñÔÉg\ƒQ̽ä<÷¦/cšgÊ”äç¡“1X³/#õÛœƒ‚û{”Ëçù꫘Ë^32ûY¬âzhs‰Ìêè€Ç*Z2òÿ~Ÿ Ü­²çë½ÌÃh˜HæLþ-ÔÚd/‘r÷«"í·}òó†žbç×ÜYéø"é##ú/Ć]j)·YzG“Ûœ‘¥³aî·|®Üûê~ªûù¿ÜσÓNA‡‰×Yõç tô9ʵ¼Ñ“Õa7Eòà#¶éÕ1Fº¸ ‹ž9sÒe»#¥\òLan‹xÌÜ;~ÌßÊHûêTötC…³„“9ú)—ß?S4)ŠRî¾Ï×Ëâ¢.÷€ÌMjÎ"ªÎr}Zëàç åܔDž¬ñþ\¹ý7ã‚FZŒ|l¡…lÑJ ÕýT÷³&÷³yºöª^ÃÈW ѵ½7×åçʼnÓ©±|,òpä¾Û˜Îîô/Ȉ݉løá“ŒÜñ§=ú‡á~¿†‘¡µrü?öx¯éJ%iþ(?vU] åò›v˜ %§b¹|ÿIH¶Nà¦8Ç ‘ýâòÉG.ÃáåÂ…Œ1¢fÌ,áÚ9àÑ—oEÒó\¬;ÉÉlÏl18£H¹|Íö˜àµ1$ò¬¶j¶3à.]W Ïê¶Fòûi¾ª7àbŒP×}¶@êVî„Í ¿)w?ú©3füä2‡9®ãVŒê Ï#§2ò¼f[Œ›±‰+·¿R,,¬ï$Ƴƒ}o¤ºŸê~Öä~vù`ŠÇ¿üIçœVèU^Ÿ;¢q|~«“D.z7]Îëqó¯ž<´Ús÷+ôð|ç–\ÿãCñî9-$'ÜØ ¶“¸Óïn“ m%²åëXl?@I¹üµ›VÁO‹¹ŒœnÔçwíÀµ\ð ¶ÿÞÇ žå‚çþŒç^XÔ“Ç#k¦Ï…˜•ÉÜ©ãÚAí«Ù²¿ ¾yh¤\¾ókBðTÆ-F6X²µ}¹¹WºŠ'Sš#™œ²]¯¸ d—ç‹ÐbÛTFÆ-ñD³ZÚHÊÝßU?‘Í¿ ä‚ÀͬþÕÜ›¦âgsS®óÖø¡—5’rûïgø²­GŒ|𠛣»I¤ºŸê~Öä~~únŠenÁùõ½!ÆÌвÕÃ0ô68ÀÈÙ#—bHlŒHîø5›\Šfdê+glݸ‰ãMXÌ“gÜ«ÝØ£)ÓEvî„·¾i¸v„ÅÌa¤\þ¯Îí1sZ¼H¾óí† dåàIè>4‚;çýh¬\sÈðŸÅ+N dœ÷}ñìšXFfóTžÛöÈÕ¡kÙTÌf¤\¾XExïÏ]%Yyr þ¾käWǶDhË-ÎÙËÆßɘ¶ Î ´Ôp-unÖ\ƒ‘r÷φïf®'¾ dv“l¶¯2˜ž×óÊ{ $ƒ¯bñÏRn¿ey/tÍ«%q÷û¢Ÿa˜’T÷SÝÏšÜÏ~ÕZÚj<]¢ßB¿„•ÜÙ·¦áŽÇ}¸ ÎOf\ë^ç¡xÌZ®‡iº0uÅuì6Àn¥d3²Òª!önПû«b&nÓòÍáÏÂÁßöHÊå×ëØ×­ì ä‹^ÿúnñ#“;ëhs|oäÀÀ¹Ù¤;7ýâ{áÆ&#‰4{Rë>´ã^]Ôƒgës5ò[£VqW åòMî5»ßMÒ·m4u:ÏÕ1” ¯÷1²àŸâ¶Yiˆ¦­ì¹¿&à]Íç@ÊÝ_ip”½þÖ@Iö÷=ÆNDlÉ™±>U¹Œ4Öú ›¦påö[Üo€¶Ùp†!îÁU÷SÝÏšÔOÕGËî, û² HÕy~ßÚ½«HÕyêÆ¡•v#UçÃúcÌÖÑ@ªÎïùÍfõúŽb¤ê\.ÿjóæXð HÕyèÏ3â“k‘T ¹ïùØ>쩱ý]î_æë;¬Þ<¸ ¤êü‡iÜ8TK"Uç¸OŸ%¾ýäßÞß_q„•½Ôc¤êüÁCCLƒö@þíþ©ír„á‰Óù·ï«û©îçs?›‹‹ð?zN8ž¯È”¢ëÂÍ©º"y²»XÒº–Dö7r‚Y:&Üê qPímÀ¿Æ·$>eäôs#0Þó÷Jù,löö¾‚,Ýa€uk7C2¥~¸mN åò//…Yu7 "£Öq{Ï3ïm²¹¹[òÄ+ž#¹U»_&oÞd³¡®B¦]ùfe”ÞOá–tòBoO+ åò5q À]#œnÑÎ-àþ,?#´F²È·%¼`ÂõH‰eΗ‘‘›–¬‡úJ‰”»_¨°@ï M$Cû¡{‚.7Çuk;b‡@ê¥þ³[ž‘ÉçJÁR»H¹üæ»'ÂÆ}›€ÜÐç¡àÄõÔÅf–‡‰d‰oSÖØø$#-?M‚‹•O¹[~ëâÞÈZ9âØ|¸îc¤ÛµÌÜùYOR._ny+ì´ÿŵÞBÿ&•Ü{Z–bá¥\Íïf¬¼y×å¦=†Ž¼.’Šo`fÀ åî;icäx#$‡oüá—L¸%½ Àu{‰4 êYÊ0FÊí¯(>!F´ïÃHÍê5ÐwÀ~®ºŸê~Öä~ž.›ŸZ†32òÞÐÝ·†Ûê&`†Ö*î#÷>h>1A$#ÞL„!H=Íz¿Ù¢$S“óÛößä>i5tšÄH¿VÉl±ÿy¼–ÂÙ“$R.¿Ë¦=o dÅ”=bú¸?YbµKø2ÓP"w§\ŸƒÜhµu'#Ç„-ªŒr¹wý)ðpÙÉÈÈœjˆo ¤\>Ÿþ[¡Û²§@îªr†âÄ#ÜêKÌñáüúMãCôy¯½“˜p¦=×Yqzïn*‘r÷û÷¾‹N˜!yµ•î›Xp%£A”#ƒŠºÃ 7®Üþ¯š “Ï)‘ôÝüü–¯RÝOu?kr?¤æC´ÁF.ò+ÃÉ{¹Ý¦w@”/ùV¯.ê_ d¤Åla²«’‘ýÏÁ¨/zÙ¡Ô‹ÇG1R1ppÖÖ–HÙþÔ'˜^8ä|S/ö*æ¤@ªû©îgMîgd‹ú8Ïi#OÆ›aÎÌÜòø9&HŸ-`Õî?kú£Û±rFjX€S®ŸÉœÑ}i]$+ŽO…à|sî-w ª;I¤Ž%{ü¤\þ!ûØ“#‘Œ4Ï=%Þ\xŠëºv0.²!á¥-q‡Q$Ð¥7XUýÃõ3Z¥XÒSÁ-lÿìb¤fÖ/øó.H¹|…‡ó…#DòÄn[08ґ뵟ÀóXnÄŠÆxìmmî®išð{ÁR #§/‡Ä³Œ”»?¹¹+´ø×ç:y3»<;mÂ}¥QºW™{"-§?H¹ýzmØÝOŒ‘×.„ä­$RÝOu?kr?§G8â½YÑŒì®Ù;iäœ$h¤Nƒ†£I‡_"©:¿mj+ƒÏ0Ruî9ÇšyRu~%u ,|”ÉHÕyÔOŒ[vC Uçrùwm K·µ’HÕù ³‚æø#"©:_ûÔ kš0RuÞýÄüì½ È¿Í•ÔwŸ\ÊHÕyaØM˜í’äÿY`[ª4j#÷/ïû/RZ6ÒDRužµzƒ»ÛY$ÿvñɺxòPû¾ºŸê~þ7÷³êaþ'Çlß ƒÞ$9dSÌËYÍ-îb€/g2²ß*Ôó/’…-ÍÛpFŽ ?C?qãÞwž oÅíb ·ãK¹Á=am¿H í¦_€Ÿ…qŒ”Ë?"ê7´ýq‘¯Ì[à^/%÷GµXŸü¤ß³ÊóÓ¹!“ o3m$]fìPžÎ<dQµÐÆà·å[V˜¬Trù Ÿ ²É®mêÂçঌԘ{%?xòni“ž.ôš]‹‘>Ýz³ÆÖ:@~¨5TXÙRb¤ÜýòØÌm=O$õu‚0¯-*I“Zmð´í[‘d·:£cH"rûkçuCÅ—9"9äe7ü*mRî}u?Õýü_îg‹S=ààÔ•@ö]Z"¸›Äp;-iˆ: d¢é=h ãäÁ&˜Õò#£¦n›ÄmÓþä~z8«½ ¹ÕÁ°Þ“1@îhX<Ð@R.Ï—]ÐcX#­ DcÅ,î?ñæˆD,pvVýÐgä‹ÛEï-£ÒaY”0+éæõ›Vr£Ào.ž*’rù âgCš®'#},Ã’þÜ‚ÁãaÈûÑ\öþ´oéÏí:IÍʹz_‚No åîW;¡–Os 3ij¹mÌOÂÒJm$o‰º­ €”Û?qËeÀ$K~k*ž8?RÝOu?kr?sŸuê´ñò„c¤r÷Ÿܶã¢a£"œ[ûÐ)Aß^Á ¾èƒ~# d—Öf¸½Ý +Ž ÁcƒçqÛý®„Î#os5úkä_kF®ÒÑÃ[fˆ¤\~Í–>¨ÑD‡‘G"ý1U3SIZk,…ªCsÜå-<¼Ü‹[ZkÄå,ån,ÔÆ÷¿ˆdÊ™žpôý@ þ™$¸†å2R._^›OPÖ¢7#=b ÐkX…HÎ;\ökÎÈb»–h3%FI¦ì¦ }z_ ÍÇtVŒ¹ËH¹û»6úc¯GõüsÇ-Ê{pï>³(’†ûmàÒ—ÝŒ”Û?G¿ ³+¡ƒ{ /£©î§ºŸ5¹Ÿ‰¿—Šþ²dèEñJž.×eÀQ¥Õ Wn\P„x½T‹‘†±›¡s`ž«ª„3+²¸c[Ðjº H§ˆÚàß|±Hn8äÍÞœ2;Úœé­ f¤\~³×þøäÍUŒì:d²Øm,Q’: Œ°VÐ '¬5ÂÇ~õ¸#V?†•‡s¸;bêà‡Œt.ë©Þ~@ÊåS¼°Ä‚”d»>6xsÝ{L]hgO52é±>ú±…{æ¼)¾Üø•‘î!îx"ÃV åî;ÜùzÁ@~MN¥+¸OMê ÅÈ^ŒÔ]?­i¤ìßo ¡Ï‘F]ÕÇóOy¤ºŸê~Öä~æFVŠ¥'ºˆdÌ ŸbZ~-F–ôB¥í„Ü€­ß…ÚCßsm¦ üJkÅÍùýü;%põº[)’«ÙÞ»×âNS·?äš­ƒºVÝ”Ëÿ¬b¾~ºÈÌvÝpO×›ÜnŸB¥x†ÛЧ7¬^ò„ëUmŸ^ãÚ\-6ŽäúýÓlúþÛÝûaû?ÉŒ”Ë«,ðX“…@îtiŒÅߎs¸>‚¾»îs½#!¯áGn¯¨©¨_ ë¯h YØEóKû€ô;Þ‹xðT G̲ÇõIß^DcƒŒœÖizo¼ª ûú<†FZ ‘”Ë7sf‡=r CL4þÁ™)è.zÌõ°<%›ÁÕpùدó¨œö÷ü!„„Ì !tŒ!¤ö~îÌÃÉL2ϳCæ)T’$$JRi*iNÝ{ïÒ$!•$s†cLHÆÂïüÖú\¯÷Z÷w­³—ÿÞoëÞÿ<þ¸Ö¾®gz¶nw}Aos‚Lõž'8ÖqçI¥û>BdÀE#RL…’þùÌ Ì%ŸÁ29´C*FúÕO%•öÏ8ÝY^¶¨G~é½\ÚYë¢ÌÔôSÓÏߨŸêO¾yi@»™TŸï+ØM)‘ÉÿgAT[Á=ð-˜jõÞ—|K]-TŸ×§½V ©>WÊŸ–Šm9Z©>w9f!ϯî$“êóc%ƒÑØú›LªÏß=ô’Þ´oòWóYÌí‰ï+¾€TŸŸ(µ’?/lR}6^[¸s©–@þêýyÚ]“Ý÷@ªÏs^ Š’É_ݬMl?褒¿ú¾¦Ÿš~þ7÷3ïýáß´Ø]˜àá$‘šõÒB=Tdé\/êý’kß•]'1Ýz€u·˜=ø{üÕÅNÌÒdN¸çÔŒ9æC%®Ž<Áœ²ò mU$‡ÿh"íKH¥ü±Áûøž± ç•™ˆ†íŽ0=J¦!U×ÙìZ;D—E‹d‡%Ñ Â ä­‰+°#»3Ó;( ¾_ˤþâQBŠÛ ‰TÊ×ä•–ð~Ym}ÓâÓ'¦å“:ª·$ò`lKaËGŽlUùÓ/ødô²NÒöãŽ2©t¿ýÆÏÈ9ñ·LîXS[è:ù3ÀäÍMÙwW a*í·Jýd©ã1dåØc|Îýb‰Tz_ÓOM?ÿ—ûY}·¿pm#OŽœ6@ØÙ¢&H“EåÜgù1OÚ}n(ûÖ‘Hó6‘¼–~ð ²b°#ªžõ‘É Wæ`õœ‘ Ÿ퇵Iddz7pfêM¢³$ÏÓ¯xR)ÿ…æ‡$‹¥S@θ+M9úŠ';͇íè2y^ÈÄ¥^Ì/ˆåµj¯P‘=,®óoü%“ºÕ’Kï€\ÖÒ‘o[¿+S)ç§ {@žZ^ç0öo%ÜxoÄÌŽ«+<9|„¹!è2jîx$“»ëý!¬ž-‘Š÷ãô„œÆ7e2eEKáê»ËL7~\÷¾”Èœ&Ÿ!/°–I¥ý¶F¢lô$™<Ê5 o´–HM?5ýüûye]?ÁJ5d<×S¨žáÌ4ºpG´H)“£ÎÏæ{‹b=ù •— ™¾QFB±Û(žÜ²O·ÁQ&í,¬ â˜û¦ÀˆY'˜–æZBHôžTʯûW¥T¹…#k†”H?DH䑬f‚ùÑý2)}á„óÌ.îøËë*³á?Ÿ³Ù3‹˜-†Å`Œþ0fPƒ¶Âˆ¢Þ •òé?ÑFÇG&-ëñQÍF0çÖ,DjL:Óùæn„Ý`m“½“#™ÎÉù·.©t?ľ½Ð­¹$“Q]„Q§¢˜ ¶}…-S»òäÅ—Í„1!H¥ý‹DmAGw ÈîÃwa‡E:SÓOM?ç~æ<í$ø%ƒtKi)Ô-1—­ŽzWJeÒ@?O¼evl,¬Œ?òAu(Œ<`^m2LX]ÿ*OÚ?7®f‚t8¦£ª(¨Åôªêˆ ×îʤRþ¾cܤûÉd©Ïg•ó2Gæ¨÷c„^×½xÒxŸ¹0t›È®œÐ¿tL+T.é(’:ë;¢Ø¯¤è˜$}©8!‘JùVíh/ ³+àIsã@Écr‘L7Íç»ÌIi|WÌkr€i^‡²'×drñÀá‚YÐX¦Òý²)=…m×d²Ne_a³|€Ýl1\ퟃ\¶ “Ä7ÃTÚ¯³º“>qH—O¤€‹ ©é§¦Ÿ¿s?[u«'èý(Y¸æ=¶~Ì|äQG8ùô–L:”vÌʘG9¾åÒD­l¯Iµ¯—ðäéfÞnóä;ÉœoméÄ<“ÞSð(ì)“é³"P.©øs,>ȯ™R&}ÏtÀÕ1Ì ‚FÅ ¯ÿyë\0u ú †Ïgö:VŠÏ!Ì ßTX¼í˜Löüò ö>: •òMò’jÕ‘ÉVÆÒÓ~;™ì ¥šµkƒA˜áñÑXt~ÓíakÁz‡Si}í*Ôp¾Ê“}Ö!ïŠýÓôSÓÏÿá~V¿rVQ¸Lî ÂÀ ̉gÃÆ<†Ùðb j…e0÷ž0ÄƒÏ AjÙvã¼i/‘[lÛ !c_òdÅž*¶8 òØæyx÷ù!OˆèÔý„L*åÿ¢2n*‘֢ݫ92º³½ÂeÒÞãôG\`.ÒzÄ¿»å òÝͽb—Ü*ž,¾‡ƒý2é±|  7<,‘JùÂ/ø¡‹¯LFßÏ…Ë«0æ‹6™ü³n›@=¯ÇuvjÁœgƒé‹e²øQwáY‡,‰Tº/òoù—ß½eÒ©_3˜? `–™ö>.xÍ“³:×–l‰©´Ipï­'€ÔY•)š~/‘š~júù;÷sc–„nýRdÒBuýõþc;ÇOó6‰9újKAoºsØ-‰×àÄŒ2öÄ☋L9o-öÔAnéÒ’ßTk,³|U_¡öâ2¹'ÿ+N¥;‚TÊ?MßTضû&O~¹j,$tYõ©µ ®="“¦É}…ë1'ô²uŽGHäÌÚåü¾)dr÷¸Z°A kšw€X®ÅTÊ÷fg‡Èd#U3¡þf«õ¤È™¹Ìï¼8aém‰´èÖN¾dýX±ùSé¾u×¾pNɤKljØùó,³Ÿ´«Nƒ ­Ç×6>Ï“Jûõ³øÇýŽÈäyƒxliÃÔôSÓÏß¹Ÿ£¦ÆÑÃñ29vl=¡àï³Lþƒ‰0£^7f Gwá»O‰Û£–pŽ”Éœ^…¬ì¶Ìy³õøVc&KäÐ깸>vœL>>;fù"Ù¿@Oýn&“Jùß¿k.È·fƒ¼|B[Øjº“™ÿ¥¯`×€'oÛ´:é™ÌªðC ÿ~™Ü0ï õg6Z*#ÑífV#áÕô2žTÊ×åjW!Â{žL|j&Ä|¬Ç|w¾'ÿªã|fàöæè|ë36¬ŠwØÛÅ’Ùæ*­ dRéþª\;¤¶:'“]绢Ô9й³r>êL­Ç¼?±¿¿H¥ý‡ú •I›]õ­.½xRÓOM?ç~nÈh.X-ö•I#ÁÛÇ…9Ë»½½ä,O¾ÐÓ¼Oš€4þi(ß²ãIOß§ððèr™Ç¬ªh*“V®5„§+¶ñäà/mƒ¼ í‚ÂíS)Dï§H™³ä罩x¸Û‘éuï%¦¶ÜÀ4Ô÷Cüù Ìã5×ø2ùEû.^'6Yý§ôž_dÚÚqfZð¤R>°z¢¡DÞœh.änâI¯Ÿû2÷”L–¬¾„‹+Ž1']m*\øYÆ“[BÐÑ4 ¤Òý6>ð%“}ZœAÌÔHfÉX=Á}Øgžlh’¥a •ögYÇ€[Û¤M§æˆšºŠ'5ýÔôówîçÔí«ΑÉ$]S¡òŸÏqòÑ«"”ÝÒ»ñ ywc>8´%áÚLݸž˜ÛG"ÏXÉè¸äêNõðÙp3³p'b§¶’IãÖzBÑTžTÊ_þÁ#›/ùå¥#&Lå˜sîNƹæI<9ÂíOÜh'‘Mö9ÃT2»Tâûj¿ãIƒ±§à‘?Z&³WcÕ²<©øýhz/á[~k KÚ +2Ýì>çßB™lë©#Ô¼ýN"µôÇY„»wéá\Äw‹)“J÷¯¬KB³KgdÒYȆwhsÎölÞLç0HóQeââÛû9Ri¿Îä×<>šÉdÉ_ÇP²7ž©é§¦Ÿ¿S?ÕŸc­Í'í§©>Ÿ2}.&Y%ñ¤úoöG)|w÷–É_½ï2±%Zþ2ùÿÜ/¨àƒG¬É_Ýo£' Û*“¿ú¾¦Ÿš~þ7÷suXáßôy5 6žKAn|ÝKçOaŽ3)Cåh7™4^ÛPp›¶9ùz5ÞΫ’ÈQ:­»GN–dè×9°]4¤Õ,/~e°7OZ/y‹AÏ$rÏè<Ì\ëR)ÿ2ÝÅüÅŒ <9aî ÞAË-™öú2?1¸T"§5é½¥Ëdò¸‹±°îtWŽ,]o ؾìÒ%Ï‹ûÊd—?ë}of*åû²^WH¦rh¸¶`¶s 3à\ ?¢r§DޏywlßA&_^| ¹W[}ÃßcSé¾÷R¾Ûºç<Ù×ô¿Q+‰9­[+!ö“HŸ´[X7Ô›©´ßªípySe²åmžuõa*½¯é§¦ŸÿËý ÞûÒϤÇò‹üìÒÖÌ«¡m…\#™t8ÞIˆù`$‘§67J¯š‚ì?ó Îö<ÂÜÙă7Yñ]" fCú´[&+nàíÞð¤Àfœy8X&•ò'yó×BVJä¢K…|—¼ûÌݱä·LžŒ/€sÞQæÕ?>âE‚3Èîîaˆ~¿›É0Ê;…ò¤Þé 襘€TÊ×0à-²y ‡äÁÃØ“Ùò\'¬°Z'“…“íñ`˜'sÊÄAüÐè"ib·«ídRé~æ'Þ§xOžÉÞÃ×ÿfÂl?{/­9sf´¯¡Ë“Jû[E5ê§Î•ÉÈÉm½7"©é§¦Ÿ¿s?[æß–½àÉÈ+sy­ÞÌâ?Û N›™Óê uìn·-u3M¼šáÀû®ÌÌVù¸(¬—ÉÁ¡5„ˉ\±±Ï6\åÈ)Ÿ,›Õ¤Rþ‡“›bÁÓV2Ù·x8D‹Ì_´…ºçg3Û|h">̑Ȗm¦cUW~Vƒá.æpä¨nn0õT"/6ìß¿a*ås8z«Þû€Ü6Ï w Ž0מ†õ¡c2yÕ±æC]™±†ïÐk ‰ìãÈw<©tßÃo?©âG.ñämMsUä­ 74òJ"·Ø¿Ä žL¥ýuÛÖn¼® rKƒ4cjú©éçïÜO÷ ~Ö‡*Ž4 ëÏo³š,’Ï>¿GUrLÆED¶´b^Z©-1Úœl<Å“tŸKä8Ãg84Ø—' 8‚%ç¾0mû?â/dZÉdðØ·ˆ÷˜ËTÊÿjô 0.“1÷|±¡|Ó¤»®ý!ƒ#Ýß•¡í÷y2¶ ¬ß%Iä")íª˜Õ' d[Wv®Åð¿W0•ò•h ¸1h7È^ŸëÀ'j"Ó1¨¦0äÃ8™Ì›¦+Ôë^.‘Î}ZüówîΓzÛbuäD™Tºï«ÈXÿF${ŠåµçMÈ3j«Û¼ãI—ÞI¨?s/H¥ý1/Au×Ay;€ˆœv2©é§¦Ÿ¿s?Åøš‹%2¬ÓÞ ñ%湕Çúª%È3k6ðğ'‡è_¾ç#‰Œ1üŽÀU–äÌ“mðÂb&GZy·ƒ5zɤ¾ö=Xúäòä›aQ¦ß]"•òöH@þÃþ2Yy÷*–MoÍ„M,šë×9<Ú×¼xò/ÃZBé?©‘`zÙŸ'Os ç.‘¯ô´„!Õ=˜Jùœ‡gò«/éL«y†o¿`O¶zÖ@M$Rj¡WfóäJ—bèt>*“»&7¸kk$Ré~øÇþh§D:e>áò)Ì•£š`HÉ<ù¦3ø²s³9Rið׿qWo¦L‡ uLîK¤¦Ÿš~þÎý pŠæ·®/“.9/øuƒ{0§‹ËÐð‹HšÝv–ÑiYº÷/è²FU4–ŒÞÍœ`ä‡ðS^2YÔKKa±‡ùfy.æèx1/-j/Ønª)’JùmZ?Ä£)O$òæÏWèys73ìÆ(˜»U‹¤êêl8ÑR&«òk.÷gƒ4(]c–t{›vNÌØËùf«y¦R¾Ø*þÝ0?¹úî7~M„»D&ö.Å€þwx2Õ&5¿1SzäÁ`´6È)£' ð~c‘TºßÒ´vû<È@3}¸MøÉ´LèQM{ˤá3M›ôa*íï¯ÕIØ"ˆ<Ùs­– »p3HM?5ýüú©þüüÜõ–’IõymƒPôÚˤú¼R× ¡ßׂTŸ{öj#4O—Hõù0£4êÛ¤ú\)ÿˆ”·0é'“£} Î=»ðdYèW¾¸Ñ[‘\1½ >ø}•È2£Td,X.“†ø Û—¹Õ¬_VNgÞ9¬#D?Ï´$•ò-îÔßæ§‹äîìÈéæ/‘Cö§¢fçÅ29Ëõ ÊMm˜ã—?€O«ñÌ|¹‰9¤·D*Ý÷[‡ì¸ ӽÑ8c.óÖVOLªõ½ ¸á—yRi¿áì&BJ¨ Oæ«Ä×A •Þ×ôSÓÏÿå~š4h$xšùód’»®ÐÑæ³UƒZº÷ÍA^ðŠôO™I‚7&{.–I«ÉŰü8‘Ù&]OØdt\$¿Ýû‚aþ9<Ù>ó4~ZÔY€Yž©”ÿÕǺBóÉþ<ùÞZKØjr›YÛs Ây™f³±Ì•[´…^Ù uòÅ2Žlé‰å'²xÒÚÝ mÖ^–H¥|9†&8x­¦LŒ×ÜÞLûgZ¤N=˜Q§ê 6ïIdãÏoà´«6Èv{<áÈ“J÷Õ Æ½13@Þº~iG&3o™‡°A"[N@¯ÞqL¥ý.DÀ6}ȻśáU$ò¤¦Ÿš~þÎýtu„¿z ¿Éµ„à˜·ËRqþ…óÚ·ôêÚžÉõþ=IdÕ篈žýÂ’œ2& ÞxË“§ïÙZLN§`6@"ýõGǃTÊ?}L9ÎÛ×ùwÞK—ÿ!®µLdÒÓêôL.Hdíà|4ácy²Å®Œºp‚ùÌù+v¹µ—É×eûö><©”ïä†È5œ(“ÕM½°×y!Óî¡`³Û@"?èú«Þrd[?¸üuY"Ó…j ¿ô7Séþ.-Oìý'H«ónØç<˜¹2ýæý4àHob—´©´¿UżLm(‘;ŠŽ¶*¦¦Ÿš~þÎýl·º U.æ Wż¸}4Ó´j/Þêó¤ÅÄ3£ sÇÑ<Ü e¾zâ÷dörÍ{«$2¢q.úYÉ$j.BíTž4ožŒò]ç%R)ÁÍ,¬í¬ Ò}],BŽÔ`޽ù¦S¶«È¸¤<ø8ódb“mÈoÑÞ’ìù÷Vt}~Y"?ÚÅ óGýŠ]!n¬I¥|£nGcÎÈÅ2™6ã ÊgÛ0׫Ƅ˧yòæÈh´ù)3Pl$T7aÎþt®A6 •îW”ïÆòù@ž ÚŽZݘ‹÷£ÂÙšù¶}”&ó¤Ò~UÉWôèY["ÇÔvþq'5ýÔôówî§]Ê<µ™ rYë Îd¾|¿Eö¢HVÄúâF—Dvâ–æþ1‘ô¸° FëÊ$—U[XrûŸÏÿÿóòô¦Býœ(ެ}£ © Kx²8Ê #.Ú‚TÊ_{I&]¼Ã“ÝJŽ`]Ÿ`f¡aby³YêìµÝÇü–pƳÊdÀ“bL)µþ¾oñîA´D:.®-\Èש”o|ƒÇX¿Á\&÷eT`·¬Çœî(aÈâ<9¢C zÍŒg­<¯1³jû`òòC©ØŸ; Ùc²ëꥈj¡ÇÔÓiŒ6ªeé3Ï×[´”I¥ý¦½Kà^a ÒhÐÄÏejú©éçïÜÏ7­R1o×,K7ŸÇ®SÓ˜?ë$£Æü+ézZ2˜fvQ8Ñg’LŽ›UŽÑ™#˜%ñµ„Úm@~ÌIGá¼Ìúûzâž^=‰Œ©s/3•òwŒwCÜhKž ðqAÒùÆ9z¿Œ“RȶCͱá (‘'×R^´’ÉŠ‘ú¼ A"C–ÅA-[쟇é)¤R¾ußþù»¦’Ȳ7ÕÔÿƒ™¾¾Ž]xòÂÏÝžÉUÍ+ðã³¾DÙ÷s+Œ@*ÝÏ<3ºâwžüê6n 0}®^ÃÒNé‰ÈL(îÍ“Jûwî ã÷/yÒ¤§€3ë2DRÓOM?§~ª?«CQ¹h,Hõùc_0fî.‰TŸ/8ÐPèríºDªÏKÛ¹ÂùÉžTŸ?|œ‚€Ú† ÕçJùG^Û‹×ËDR}xs¬²úɤú|{‰®Ð0m7OªÏ—'_ÃXíù«ù|?â]Eª%©>å†y]OI¤ú¼‰ßA ™òWï»uœŠžöOªÏ÷ŸDhŽ6È_Ýpö1˜œË’È_}_ÓOM?ÿ›û)lÑþMù‚¸óudrÎO7¼ëÔœùmå3´›R$o‘‡ Fí™MŠ~b®ûž<=ò ^ùgŽºT…ŠÑIÌåÓn zS+»NÆ9•'W7uBQÌI‰TÊo1WM,É!ºcÖ;I${¥ÎDcÃÛ<ùc®€/SÌ™ù“ÆàP;gfå´ ¨¾1B$ŽG£Ÿíd²*êÞi§ˆ¤R¾^ÝÞcõQ"í}Ç”÷ÎÌñckWÏmfžÿS[ø¢?F$YG"[ì ²ÛÅ%˜4'”'•î_ÑúôïÍ“oB~ jÎ ¦ñN„­.gÞ¾Õq¤ÒþWàP¾!È´¤ÙX±ûO*½¯é§¦ŸÿËý¬cwkNÉäÉ‹xh ïÇûí@–>öCãªz̼S™°¼kÌ4拆Ó27g㾋ó‰Í> œ²”'Ç\ÅÄ·“E²¦Óq8òOyR)ÿ“’Y6´’Èj­uÈßåÊ\–1g…‹¤EÙj|v="‘•‹a°Äic} Ýæ…0»8žÄ^ãI<¹õàfìT…I¤R¾5j jjIäÊÚB§@Ù’ ™USˆvhΓf1Ÿñ‡u ³òo7xÚ ”H÷!¹èü]$•îõ©B®#OzŒúŒ^=2›¦ùcÅý¹ákÀÃG"©´¿Vèu Iy` vK¤¦Ÿš~þÎýT…œúõ”IÓ‹ñð~ýûLuGìÜË<ùbâT_²eæNØ Ÿhwf‡Cn°>ÔD$Ͷyƒ{z—4>OªVIäý­azë¤H=Œ] dR)ÿ„C{0åJ¨D®qçÇ1Mæy¡·ÓêPZ-c¸“ÓKƒ˜N+®£YÇÙ°~&‚›v“I«³_p¦»¾D*å›[^Sø”Ù›'{|GŸ¯~̆ª'Xûá6sw¯ë¾eúÏÉGäö æ‰ÄƒÀÄf ¿•X0ðOš .‡ß’x¦˜p\÷3'`|Íæ •öÛo¿ Û¨/"Ùúxn„ÎçIM?5ýüûù"=fwºÉd¤\·2fêÜsE_fIÆÖðƃ+ŸDrAP8~¾³‘Èøçùp 7d6iú ­üæ‰äÄñå¸wÑŸ'S¶À!. ™œ˜¾CÙð¤Rþ¾ƒƒPÇ#N"{‹‚Ç)¦ÛЬZÓž>î>9zsä^¯tD”‡óäþ=Á˜èPÁ”»¥":x³¶òzõ–H¥|ÎgßÁìUO.YT .ù3Ë4EMï3/nF—ag˜«½98g6eÎÙî€áb%Réþ>‹—øî}‘'Û—?Áë³W™~ÛᜢòÓ2?LÚð¤Ò~I¸µ§H§‰.ˆÙ^©é§¦Ÿ¿s?Ú¡àak™4¾}¼³.³U½p{8E"}ß§!ñälæáÕÏ6ï6Gº—âñë»<éß;Sµ9¬ØÅ'M˜;'Ùa†T)‘isnbû%]™TÊE n:¸IäÙÏø6йædÚŽõäÉÙ£Âp,¤€iµc+®O¼ÃÌ?5=7M`ú˜äa©§®LÎh -äÝZ'’JùâÆçA÷Xm}ºˆØêY“i`u î ûóä/D÷ · õC.¢×ÌéÝ0¿Ùó¤Ò}ý°Ìå&OÖ—J0¶õ=¦ëÓ¥¸rÒI"ç´9¬ ‰L¥ý1C0õñnžœÓdZÍh&‘š~júù;÷ó¤ÕSÌxõE"Ý _¡¾]³wŸbú«)søþ¿¡??…ô9(Ãöª1ÈÆ¹‡ñøVæ³~«ÑÒ­€'µ+–"³wެÝåšÖ×âɧŸ\¡U}3™TÊ?* #Åb‘,,¿«¼ù)d3gT·ºÆ“ŽÙàOëÝL®j dÇû"Ù­ÿäɔȢ>…ØùäO¿ãëÎÁ©”oqH<Ã_òäXÛ”?R1WýôCóƒí%²òF$^»`ºnrÂäŽ2Oæ±Àí3¥"©tßàS!œ§–ò¤—ñ5›=f>ºíë "i ·Žò¤âÏ7h/´^—ÈG½±­FSÓOM?§~ª?7—Ã:'R"ÕçCn¿Æ›‚<©>ï;~>î,¿Â“êó·³"4m¾DªÏõžG¢[N[™TŸ+å/: OC®s¤ú·¨›Žõ#$R}^VíÕëF2ù«÷ŸL»„=ñxR}~°Ÿ&í¹Ã“¿º^à5xÇ-É_}_ÓOM?ÿ›ûÉ݇s{ÿ4ìôš'ï¼ Áêÿè_!£¸Í'æ7Çs°Þ[ÉÔŸ¸ ‡¶`z¿^Šþ"™»+Ö~åÉ(kw|M’™wfœ……ÇW‰Ü³o g*å×}þ]¾4‘ÈîÓ^`{°³HίÂp힬:ˆW=>3‡•ÃèÛéA¤ß®Ç¸+YðäŸÖ¢þÑg¹°Æ\š"‘Jùb^ÀU”ò¤ëHL‰~ìúR†ÊiF̧Õ#zÀ¦qŸ%húª©HF›xà@v”D*Ý÷ô»‚ á·$òGrÖþqÙ8% [ËJyòÛmÌ{<©´ç®X½±Š#×emÁÀ©ô¾¦Ÿš~þ/÷³<ÎC¾<çÉŒx/8öxÈl›æ»%Ìmžx·4Œ©}Ä7Û‘ÈoŸ}‘í‘Åüs“#N\©Í“{NÂÎ[q"yîx¬jð¤Ó½#Ð^=W"•ò¯Ýù7¿Ð·$_,…énŽÜÙ4göÜãÉ~ýÁ”s® `˜Ó¯8›ƒÖ0údàݤši¾1cæEó¤R¾B trËxòX—£èaŸYxç6ŠìTLiU.êj1LPw»Džj'!"æG*Ýy¸ÓèT‹']®ÀïJ7¦Ýãp›Û˜YU}v_{¨È kCá³2#¯”†àÖ»x‘<ï°µnsdýÝû±üñq‰TÊwËo&Խȓînލzpœ©¯ÿOÜo0÷‹À„àæ@Û#°žiάWo¢FiI¤Ò}KÛb$;-‘:\ –m8δžQ±ÏLŽœr/óä3<©´_Ïç"òVyX’.Vqxª¿„'5ýÔôówîç#g›FðäÜ÷»ðã»'3í–|¼µDÒh}0ÂÚ0 ®§áŒ×0Ž”¯G ÿŒWÊ¿ÿÓI\•U¤ú<·M ìàIõù˜Zéøê–Ë“êóÑåGàœ{Š#5Ÿd„Gí†H¤úün›+ø´®ŽDªÏõ¿¢Oÿ‰üÕû§ÚÜůDR}>2P…áñ<ù«ûu2`ræ´%ù«ïkú©éçs?}—?Ä¿i²ü*Æ ß%‘ÇóqÒm53yAž]ˆãI£æ‘0kÏ é‹´î™ãŸ‰M |˜Ö§N`@ÿƒ"R$ï…ÞGØÍ£ÌIöp^;–'•òÕäŽgá&"™­·£jß`~‰¹«ž³x²¨ý5ˆ‹<˜Qµ#D¿¿DnûxÍŽÇ0•îNuG£À>ÙÍü –m°bÞö¸ϧF<ù()Q“ö1•öïߺ¾å…–ä˜÷®øóm3‰Tz_ÓOM?ÿ—ûº¢®Ûm%rhÚ-<ˆà™Oa†{O®t;½pgfÍ¡71zü"‰ ;xe5#Eòq¸ ž†7³$×nþç{SP-‰4È@üça<¹zF8òp*R)bö! éoÈ“×΂MŸBŽ|<ïJcN§¾—KP¿ÃUŽ,‰;‚Åù$2änJÂÆ1¯WFá¯Ûy²‹A †˜e‹¤R¾¸îî°;ý‡D>Úy?­3OÞÍÀÔ¯ajsŸ§g1¿„– 4<©”OïÒQtËÜ/‘|N ¼m³yDÂf…ñä§ÔShéqˆ™Ò>V's¤õ©ãè]C"•î/TEÿð…yµý1tü¾’Ù07N ΋dV˘46›©´ßàC ¢¶¦Šä³Dèº-áHM?5ýüûy[ûboD‹dtÄ\Ðý“ùÚ^¡–ä¬(Il*’Öىش±OÆ´Š@f§'–䱘 ¸Ô*˜#÷¥F cÝQ<9ÜKÆb­½ÌÖÝÏ@kCŽTü9¡tüysŸŒôˆ¤ãŠ885ÓçÉœèx^Ë‘ý]q»"€yû}8îftäÉ"$"Æô³kS˜2m!G*å+^‚]ÏHd-·ts–¹6ö$]gòä³)þx8Z—¹´ø,þzrF"Ø\F—½Ç˜J÷O;ŽFöë$Ò¢­ölfŽ˜P‚t±+G (°Øå<©´¦}(¦´«É“ãÌcö…ƒ©é§¦Ÿ¿s?_|“ cUdÑùûpÏJ·$ _ŸÅÙŽ"Yô-Í<–3/}G‹ªÛÌcQÃÙF"7 Ǿk'‹Lôí£%‘êsÃN©uÔR$Õç/æÂw™—DþêýúÒ4Û¿V"Õç¯÷§C ˜ ’¿º㦋ûQ*’¿ú¾¦Ÿš~þ7÷óëÂø7…¼c0>Ë‘¯Nya¬ýa¦Mm>ýXÆ“oãñ%Å‘éá)¢ÇÜ}*Rß& ÎöâHËõá¨#|g®mí[ƒZ<9>øì]pä#_Îùˆ¤R~ï¶ îæ,‘aí“ñØwsb·ãpùì(’GêG‡[õ%Ò²Þe,òiÅt»—ÝY "9Éæ<ÒwìçH1ÛGŽ0•òéD%"|P?‰<´Q…£†ƒ™Kfaó›6<é⑎ÎÆ2?TF¢ÔºµiÔ4 Nù $Réþ¥£Xöc6O¶šŒ›Â´ø#“=m%rç•LliÃTü÷—ýQ}‚#­j…UÖ¢dRé}M?5ýü_îçñâ#ßÒ†#3Vybîi-fæÊsXrÊ™'ß­ ƒx`+ÓÈîš,ªÇ|yñ"žžË<à‰%ûWq¤³þa,n6^$»äúښ#ÇT¤ÁÃoO*åÿ’"â€Å*‰ÜœžŠ­¦2ßÎòÇó¡c™½ÿ8Ó´ÍL+Ó¸0Û>…Ü?è::—ûq$·èl~ ’p=ƒó†K¤R¾*¯4\)*‘úg³P=É’Yì"aý§u˜ËÓÎÌþŽá(ß¿¹£Ãq¼Ü¢'‘Ïׇáïz™¯¬";¡Œ#ÓÄ$la.’Jù}§ãEƒ$rÚ¾ ¤¸0C\¢àQ²‹Ù²{"ó2ùyÙXòJ‹' ÒTÐÿR“ùýd"ž=Y ’=Nž†»ÝAŽTÊ<:Ýv•È­mò0é{}ft§h×ÚÓÁg¡g´…ð÷eŒ‹úçûßÿ¹vF2Üý»r¤ÒýZ;üð¥°/OÊ—OÀ}W¦Ýæ4p›<8Ò|C,äà¦L¥ý×FE@ž5A"÷' ¾u¦¦Ÿš~þÎý¬ÿÄò^C‘ t—v0JN æ÷;Ys©?òÍbN Æ€¨n<¹¬K0ZvðáÈ£11HPÝɺçâ±*BWE®u¸Š>7rä¸5©˜³uO*å÷h“‰f•"Yq1Ãã˜;ªDl¨ß]"#Ê´.G$ýšÇáøp޼¸ì<&'Õ·$Ç\< ƒZ"9ÑÏ•M”H¥|ªÂ|hÇ\É¿"Qµ…™+„âK­©”0æaÙ«Ž–ärãÓ˜õùˆŠ<Óè,ÒžÕ° ?ÇãËu™_ðÏ÷ª{~iR;ã¿ä2§u=ƒf— xÒ¤q,¬žE«H¥üØŽ~«DòåÚ4æýP‘Nýó9=¡ŽHö¬•€´2KKòR­X$ÜrÉáÕÉ6}b.ø;uÌJäÁ (}–B*å«=  Uäãœ|tÜdI~yv‘¥).BƒÌuÌÝ p³›-‘KÆÿó½íc¤H*ÝpÀ¯ýàÈOk|Öã)³E·L¬è;X"#FåC¿²T$?ßM‚ÐWg4Göÿãlo¤©HM?5ýüûùt²¬çhIäàuÇ`™Õ„Ù5çÊøb¹}h<æØ2Í–±lþyK²mã H»‹9²¾½Ÿš1W¦Æ«sE²êå%ü|±“#g™HHº¿'•ò›EH°­lª"OOMQ·wdàúHôè›fI> ­"§$òížL lXS"'¼¹†çnQ"™`ˆ 3n[’ãgøÀ8L_"•òÝjœ‡a«çpäâ¹hò%ŒÉw9…èÖ-Érpx,3Q‘)²P¼u¿*RòHžTŸgVƒõΩ>ŠýyR}®”¿ÕÉDlXånIªÏçìÀ«%3DR}¾åI!†Ìª¯"Õç÷§D¢¨ƒ½Dþj¾¬¤,ßx…#ÕçÎ¥Ñp¬"ÕçûÏû>÷EòWï»üðE€¡GªÏ‹î&bIeGþêþC)çÐÊe•Dþêûš~júùßÜÏ3C¯àß °È„V¿õÉäßÛ²àÞ¾·%Ùø‚ vÏ38òÍ—˜Î«Ã“wkDÀÄ-P$ƒ&D£k¿úÌ]b`käË|Ý%)ä»=YxpxG¶ê!㨞)O*å_ìq†myòÙŽTlѹïö?ßÓÞð™PRK™9ÿ¯„ëýÍΡK—2fÛ—1Ìaó»» 1¾¯™JùÌUpm·Ê’4-#Òî;sçt"8qdlËv_b>t̆w£<‘|õ0sê¦J÷k¸}›kió"±ßî0dÇáÞ„ƒ*ÒÍ-i?[’Jû׌‹CÒò^"ù³áy”:øY’Jïkú©éçÿr?ÏØg£EÏtKÒmÐ%XpdÕ‰ è1¸ O¦UÄcIožÙ¸i –µ¸kI^û‰‡O9òN‹Hôž.0—Ú†àkÈrfÁà(´×®Å“¬X¤naA*åÙBB¯þ­~ŽäɧE¸Ù{8Ó8âZõÉãÈŽMpû}óÞ³X6à¸%9Éû8>%’=ÓQ²ûŠ%©k}÷šñ¤RþÉiI˜öšãÉÑàà?ˆyiÎi ¹ÑY|.¦Ǫ̃@_¸¶ âÈÐIÇQÃ&Ã’Lœ|Ê¢U¤óíläiçp¤R¾õ.bŒí ŽüûQ¬ ´y2öJêŒæ˜5=cPÖ|óûêxÔßÝN"ïú^DšO[¦Òý•Ë31oéwŽôØœ÷ýkñdZÇP|9ÖM"OG!w¬SiÿŽ,_t:1[$+ú£¤ÅW¦¦Ÿš~þÎý¬wî<-Ã9òÂÿÿ¹.ç0»œÆæýy²ÃÊP¼¯cÀŒxqzéÛè8Ö.U‘æ'ü>åo‘ÜYv ÜJdÖÌh¤¯íÄ“þ“¢²ù‹%©”ÿîÞX|¼Ö‡'{ F‹{™ïþ¹kZð†#Û´öƒnÑ!f+-?LÊx¬" sƒ’±^$gy%ávP)G>Ú £z™É¤R¾3R±ÝÜ'Û6“Ðm¤)óS‹sèµj$ÓáÕ¼cÁlyô ¦ìÉOS‘iaI*ݼ-«ÔåÉ¿Ö_ÄÕõ™+Æ%ÂÕº¾D.’„ؼå"©´_§ê¦”·“H½¼(ì Ôejú©éçïÜÏMÿ|/Òºö7Gfo͂٠-ž¼Œ}Þr$w<‹;b^9ãó„“"y¹û),HÌföëZ¯™-ìcÑ÷ç,¦ëü,È{||¾ÖæI¥üíF¡Ú¶OV}Ž„¿–6ó¬½?š•e[’wí‚`wµ¿ŠœRpÖö"9!? ë×cvIEºÙ^fî’\<[¼Ë’TÊ7i` Ì Á“­¾&‚»:‚éãy 9G:2¹m!Ö°ó‡qòCöZÎç±.EI¥û-§Aÿ¨.OŽ›˜ŠN9Õ/Zc›“±ÁapxÓ1™TÚ¿+*\NŠd?ÝXhë«HM?5ýüûyo`&ú,ÕåÉÍr:ze´`VÅ^#+Žì7ú4Œ‹NY’û½#Q¿a€H¾‹Æ÷éwTä*Ûh¤ý=Þ’ìYã,¢Çsd“}qXô× žœê^Ž.©”w³HÔXð#õ3"àŸÏ|Xç4zL}¢"w”GÂdrS*Ž©¹ƒ%Ùçe4^åäÈÊîúÙ`xxŒž©ù®4µ¿m`–´N†qPg‰LÜ–…W?µ˜J÷{[ÈêÔˆ'ïv€‘ÿñMÝ@dwÚ(’kû!ï}C‰TÚÿ4/GÖæÈô;A°¾œaIjú©éçïÔOõ'ªÅE,êÕž'ÕççG Mû€©>ßgºSp¤úü<ˆÚŒ9R}^æ” ­>ž)¤ú\)·º‘8ÔÖ#ÕçÛ3c jAªÏ[LŠDRÈŽTŸëÇãêÌ *òWóý=ùÆõ2ãIõyüø0Ø/ûaIªÏ{^§^é*òWïoª/bÃi=žTŸÏÚ|žñù«ûo~? û¾ªÈ_}_ÓOM?ÿ›û¹¨( ÿ&úŸge…ŠÜÓ°ìÚR$Uÿü^“[’Çk$ ðü-æÙ°h˜wm/‘Ýcq®snàI<ªÚ.’ùŒis‰9r£ ãàÏYakíJ¦R~kÇ@8|Ø™B®Ž:‰ÄC{Tdµ]"„qäô^ñXg0™¹ç¤„3†Z‡Ò.@ÜÑÜ’Ôw>ƒ{ˆ¤^÷hôÙzÈ’TÊçµê„•Éi¶*³T™Jûo D×õGDr_U(LøkL¥÷5ýÔô󹟃W‡ae‰¹HæåœÂ+³™í“pnuO޼gœŒe*'¦SÏd\oõV$å´dx›ú2¯è…ñÿÇŽ}EÑ7ïâ#æ¬ˆŠŠ9aF`‡n#˜Åœ0£˜Š˜ F@D " ŠÂ²KXÒ†aA³b@Ì9çœÓŸSçt[µ§þ¿)ë¼yj÷ͧʾçÛpÁÌÜí®² abâa¶Ýñ¨sò©Œ ù"Âùލ%¥òk¬÷õ¼:ò‹Å>Pì*’Ráû„y²B:,«4DGªei v¹«%+NJëÙEòKf¤ßÍÈšÛ£!q¿’•ʱ# Bëÿ”±Gá̹~©ÊOƒÈfÕÙ‹3`üëiì¾¶p}C¹,òE¼œ*|I©ýƒ-Eטf‘féÅŽ-Ç.™{g:iIב Pa§#%¿ÿÕàÃ’|‘üáž© Ö²Æ~ûY’û2"ÎîöÉ÷•ba{ÕPö­N{W§ ¤EO |Ò?b·6Ï„uŠä¶ž*xØ(DGFz'õ uEòmH" ˜ßJF6Tæ‚÷ÉÝùâšf9”²'¥ò¹îƒaåç‰äêûa¤ÝvÛ÷LËj³¶uð±Z/¶Ý«dðZ²—=mš ýú>`Ç•ÙWç¼´#;¬‰‡Ò6wt¤T¾½+”pÈÙK +iàê›p6mT&œ9¿ nœ ‰ÖçÙäñG ìËL‘4‘é Ç*RKJí7…,ð 4É"‹Y ½ù]$+˜§€õÞ™0]¡½#Rêü7¥“a¿u??KÝg¿‘‘Æ~ûY’ûÙãóA8V˜ ’!µc¡ÇŠ6Wž ‘MíIaH:´ÙRŸ­‘¡€FöY:²ôŽ8Èÿá$’¶3ãaÞÝú9ÅéýS–}]Ÿ‡¬bm·©áP¡Ž”ʯ« ¢‰¤ƒS $tÔ²¿¿dCÀçÎìéÑy°w`¡Žü^.Ø”Ï"]“tpL÷×2ÕSà÷Û 9o½lßb¥òù+â¡[¨\ ÍÜÃa*öÔ‹t˜hù“=þ0v¨nOîµU¯éAÔ: <ú§Š¤Ôþ¹Vz€7ïD²~ˆÞÏyÊ–Ù? äøû‡!ªBY{RêüãA`ëNé|d?ôY®“‘Æ~ûY’ûÙjF,Ì÷(ÉwbaÙü'l¯ù*¨òÍž¼ãšµÂ³º† ¾:‘üZ#l”Í"ï·ÙMGôÑK £ ü{‘Œí’¾æ2òÓ¨PW½ Rùû]:]þœÉ^±`7÷"{Ç1¦}ñ³%-çæÃGËÒÙ`o6 },’&QG@Ÿ{€µ ¬?È5­¢¡Qùý2R*ßþ®±&OÈ5À€›±ì.Çhw¦=ê’c4b‡¥Aà½êYäX! Ü/™°Rûg½ÔC×I·E2Á:F\½Ä¦î‹±uOä·ÑÐ4SÆJÿ}DTñsÎy¬ó~ÝÐG$ý4ö³$÷óæ¶C0âÞW‘ü0é0èíÊf‘î‡RÛžlê#‡—K³'f)¡ÖʆYä?´bÆ ËbÀ»AœH.Ï4ì±Öép¯r†@º¤ÁÛ+µ¬DþÅCçï—E²}½x˜èy‰•¥æAž››@&nÌ1ûØ¥5€‡MW‘ìUO[—ÏÑ‘š|9äÌÁ–ê§ï6}R*vmr°@i÷u¹¥lïp¾_Ïž¼ß!¶\­ÈîÈóÖkErJ[ t¶ë¯#¥öOZ– K>œÉŸQÙ0%'‹]ä~NüRØ‘Ö*!'*–”:ÿñÂXˆ_¤Éçî 0¡jkì§±Ÿ%©Ÿ†ŸÁ#â Ü“JY¤á¼C<$¶¾"†ó]gýS$ çkö(¡]ì6‘4œ°Í*ý/êHùTþÆ…r(]å´Hίù‰Ðäi8ÏôÒ€ç7g i8÷ Qœ;_ò_ó]Ί‡®i8Oò8 Ÿ¯Òpþ´k2ÌžþNGþëþžb6¸R‰¤á¼ÇG¼÷¯'ÿzþµ±I°Ñ±‘HþëõÆ~ûùŸÜω±:øŸL×÷d»)Ð÷ó_ý5*˜žð×À¯*øàðדuÐòWˆŽüÞKö£’X]H¼ú¤g/,ƒÍÊ»ë`ÜVŒÌY¦ƒ[²Õ¬Tþ²›uPgXùvƒÚ­Ç®¼›Õ’BDrtp"4(»“œ¦…¡9ÿ]¤´ g˘§Áæ6a"yRa½:‚•Ê6=Ö¸Fˆä’îÉàù6œdžAf¹vd"¬ÙĶڙoŠ$>Lƒ‚!¬ÔþÅ;â¡b|s‘To‰‡£u,Ùw …P»fYn£&½©ÉJ?¨q,kmmK†5‹ƒï³CìH©ëý4ö󿹟g¤ÀŸª‘i¹. Ù_g÷L…²'#زî©p¸Ë_Í›êàþ¨“:Rý] óßßg/ŽƒKöš}sÈ.ÓÁ ¿ìÈ Å¹£ëØRùG­ÒÁàÀ¹:Òª¸×îQkØÆý¡~ç ‘|úU ¾Ýv°Ö#’á}„@F¦$C§{ØSÓTпj¤HóJV-þ*•oQ…d± \$C®'Á·ëal+¿Dð½,NGa›y[£Kl<*’á«S! e+µ¿ŽgI»ÞmÕ9 ê¿7ë¦A»«»Ù!K´ï]O$“mµ°ÖÉ’u-~n.òÑW-âàÆ§v䜞:蔩#çÝ×ÂûOïX©üÁtªÓ‘NÅßלýÑlÛ†J˜ã´U$ .)@‘¹‰ýY˜7{E äxkth÷×±o¡S©]"y×" ­Y©|ÍÆ$çÕÝ"Y£mT­ý×5Ë’`ÜèPt*îñ8»06ih „üØ#’ÃB’á÷ÍVj¿ÃŒâûxs‘,;)ê¯3cë/ÕA;ëeäém:8hnËJÿ³ø½7¯M¶Œ\¤ƒ.…7Yc?ý,ÉýT™¨à»}¤@öh¤[ë¿Î‘§Áʼ]ìÇiÐ~w[ætÉé90ùfÃÎkM¢‹ìȆÝã ÇÝ62r¥»F5¨+’Ñ5à¥ëÄJåßæ ƒ÷ä:R&¿WIeomW@ø7o‘œ3PŸêy²;CUp­h@nùª‚2Ž­=F )޾"颀ëàÃJå“ß·u½v‰ä¡ ‰9=„]ý4 VW È·Í“aß‘¿»™³ï‡‰d£6IUm7+µ¿pT<¼¹]O$5Câa!Öe“"tÐ@óØŽÌ< mÙ¬Ôù'_ÅAÚÛo2ò­e4ˆí>Eá{ˆdRšÚyNe¥òÏí¢÷f¢Ž¬ÕJgïe½Ë+ ^³Õ"Ù+?æŸsc«OH…{«#²œ&r—…³gn'Àà1î"9´GÔèéÂJåu(òVìÉk¡ÿº`6nj2ÈgDäž=ɰñå_V&ÂáYÁ"9å´2_ng¥ö§ô‡¨Ú")B<ÌùY“½~PvŸkÈQ:¨>F¯%¥ÎÏžÌd¨xèÞŠ5öÓØÏ’ÔOÃj~ñ}½Lñýýÿh8ožªµi8ÿñJ ŸûIÃù ¯8X*$ÉHÃyî·L¸Úf¹HÎ¥òÛ˜ë GøYù}}›@Ùs¡HÎûš¥AµNai8w —Ã+G'‘ü×|[œÁÚ#H$ çú+É`6u@ÎíZ*¡µ|«Hþëþ›=ãáÕø"i8÷Ø¡ƒ1[êÈ=_÷ ü”Ýò_¯7öÓØÏÿä~ŽTkáÒÛ'¦¿õɱÓÒÁå©[þq:„^#o­3 ‡¯;ër'l?gïWÃâj}Ø]ãÁbÝ(?1Ðk÷ vÕ™+’çãÓ¡«|+•?b¢žqÈø ðx:æ'´òfã®'‚òÑF¶è¬Â]E2ÏYIe&³v!ZH0PF>ÈÐÂjûfv¤T¾M.Zˆß¦#•#´0iEëâšKoH{1 Ìzncg$(A¯óÉè|(ïÎf¥ö7ï¡„ÈæKr‰£\ü—²g^$òå6‘|Ü* "¶mf¥Îÿ–íË8 ¤ÂBÛve¥®7öÓØÏÿæ~>Òá\‘·HʤC Î‡­°9Þ¥/È6—2 ×ÚyìpµnwëÆv6Ó@ãUÍÙŸñ72GFj ã!õÕ3¶Ã¸4xwm‹Hº~TAoV*ÿš$¬~è"•o(à~ö¶×$8Ý ›šûmc»¾K› "¹Ý/’‡ög!A Ë¿¿×’[—jÁo¶¿Ž”Ê÷MÐÂ3™ZG.j¥…k׎³Ïk&CöR_4'^¹û±ïà•z„Hv/~/{dËJí¿:A ¡o– ä¤J°ºœMqM„Õ¼ER›¤„‚ò«X©ó)vÄE öWB¹%KYc?ý,Éýñ) BwoÉŽÒ ÐuÛÝ"rîÌÈ*®™`ÿÀ™­´X7ÞÕb—‰ÈS™°Ë[Êao…ЬÜ[‚¬»Û7ÆnÉ_–Ép&Þ—•ü:*(¡Ùê9¨»Tµ–°ŠîÉà{Ï—½&O†]æþ¬]‡ÓßF$C/ɡզvìÛJZˆ´º¦#ƒVi [n ‘”ÊW½štƒ¯èȇ4Ð"à1+ËN{Ü[/¦Üùk¯@9¼É³ÉZåå`‘bÂJþ~¬WBÓØ™¤„9_ÜY¯RJ˜ð|¾HºMR@½ûάÔùŠGJê·V Kû'‚O)ÖØOc?Kr?/(Ò g§Í"Ù{k¨^þu‡2â¶Èèw™P¿h([®”.ÅÜ‘‘‹mµ0ñ„–=úBšeVi>-R‘½x,&/Ù(’¿‹¿[bW°Rù_NWBõmnÙ5P âMwöa³hñÆŸ]žQS¶³¼äpÓ¼‘Hæô”Õ:UÙ9jpùÜ…íò nmÎJå{{Eº½Ÿt¤L«]²Ò"9tQ Ôë³] ÛK\ô×è°xˆÙ’¯#7 Œ‡‰×7²Rû½*á[_œŸ©„󾫨§G`bÕ!"Ù¬ø¹Á~‡-+ù|Ø3 RînH|šÚ;ÛXc?ý,Éý\?3 ¬÷oIHƒŒ[Ù–ÝÔpætü³T mÙ³óæh¡GÙ]2R㯅•'³on'€×ªáé µ§°OG) vSg‘´šq_V*½l%`ÕÕôZ ÍÍ×±½kª mÎvöÉVL­ú×ÖŸâ¡mÙ:r…6„‡×YïÇ ¼èÈ¢Ýñ0uäQVêü1¦*p›¿] óõ*ø•ëÏûiìgIê§á'ý\*®è+’†óÀƨ·ÉR çM‹Ïí÷s¡†4œOìš­B<ÒpÞpD<8nðב†s©üý<aQª·@ÎU/RábÔ64œ?—ÅC»Ëóu¤á<·µ §nÉÍçTE‡ü[Фá|ášTèÔÀO çÍ"ãaßÍã2ò_÷»ÔO„ï»6¤áiÅ.Ý ì¼Gh Ótd‹L Lxº‡ÍÞ—ÍíÇ d“k™`Úµ;+•ïWÓ ¸ºš@~˜“ó_Od},UPÇÖ‡­µSÊóÞl©ErhSõ¦Œ|g–Çj ¤Ô~ë èñ²”@†ÕÑ€õ…22îs2˜¬ôÈY?S`½ƒ+uþùºéP.c©@öyš}ó]Xc?ý,Éý´ü®O¿‘ù½™¢Ûe{¬Ô@Z÷2²L´LSìý©°böŒ÷KƒK£=Øš…I#[ϾIO†¡ï½Øé‚2èeäÚ 0¢¨–@Jå¯y# j¯X/’哭ײ}[d‚åšì]y|…1ì‡U8¶õ˜Žb¥nIoØóEj8Ù¨²@> ЀogŒ”Êg’”WîŒÈŠ2 vå0¶I™T8]Á›³,JßñdûH€'ëz°¾(À©÷HVj|3 <Ñ’‘sŠ¿®ìè7lFñß‘—•¼Ò·tñ}9x+Ù#e,ú>Z -]2áÍ–>¬±ŸÆ~–ä~n¤„°„ 禄gU'³¡G5°ýP Œ,¼§½ƒç°u¤ƒçûE9ýg:<3“uÝ“©Ö>l¦— ¿Sz) KudíS•ða:+•?l\ÜR­É›õ’`ï wö]» x~{*k› ‡Ìeõ…j8°¹2ûq¡Êu°`»×Àõ¢¶2rs-d««%¥ò½éž ~jG ]ž Sc{±.wSaRâzöÛ4¸q5Û¤q"d;Îe·NN‚ÑoW±Rû í4P~õ3ùÊA°÷¿§¼‘›@Þyœ½n»°’ï¦jˆ‰ë(wÃÕÐr_=ÖØOc?Kr?—îUBƒÓ²ð¸’—Ïd|ÕÀÂV2ò­©nljGÞ=”còWÿLÐÞëÏšÍN… e½Ø3ýÓÀ©íÖyb¬bk7K'Ñ›•Ê¿(zÝsI]h"ì YÂv4O‡ˆÕKÙs¾i€S<Ø”ï™Ðë`'öâšL¨uÙË750ºª›Ž6Fßcn°Rù§d‚yo[Ì}™ ׺°Ç2Ó`ÄwvuÓt¨Ùb)ë” cx±—+ª B_Vj¿j¤ÞV¾%#[LÑ@³cElQn¼©>J 'úeBÜW{VêüÕu5ðeîYµ†»cý4ö³$÷óô{%ôö˜-Ã-aò¤¹lr--<ö™mG^+¾®ú¡ðLÒîj&¬pê.°Îj¸R£ki‘û-e=_¦C‡ Ûá ü¬¼Ùß©ipÚוʿjB"•_(’%BC›¹ì©/ÅÏý¯Ö±/¦§B™š^¬ãû PùcëOÍ€:úI¬½&ì2g‹e‚ÍðÞ¬T¾Ú-Õp3¨­@™¨†uC›±§6§Ã·žóÙ¹ÏÓaV” [ef*ÄTõbÅ”4èëÎJíï=WWÝÏËÈŠË5àlqŠb¯†å/[ äÓËjh5¸2+uþŠc_f‡ŒŒþ¦Ÿ»Ú³Æ~ûY’úiø™4("/HÃùüâ^Î7_¢% çyçÕPàTE ÿ¯ë3ÀùÕh4œo•›Òp.•ÿÉ%ôi7K$ ç¿Nª`öjo‘4œOÌK‡ÚÃ]EÒpþØ2ά™&’ÿš/}»zæ› ¤á¼úà èê–°íST ÞZÉJ?|«rFÛêÈÚ½4ð»^4kì§±Ÿ%¹ŸÂÑLXÓ©¹HŽÌmv“£`ë¹´žæ]Ùü¼$˜~c±@ÞQ%ÃÂqîlÀÝL8iÒ„õôVÃëã¥YÕj Ìî=QFN;¬ÔË´¤Tþ 54 lÊ‘‘qV¨;BÉ“¿š ¤÷™8›Þ=Ö&ž­`kýN…ô{‹Ù›^³rkú2VöiÌJ嫾U é¹Ãòäm%ØÅa”Ï€F²+3ÀcÎ@Ö9H -—ü‘‘_Šî×ïg¥öûš¦Ã³{3Eò÷³4(½m[zE ̽áÁî”%CÛÝn¬Ôùç.¨Á¹ß}©C5ÔYZ]$ý4ö³$÷3ß-vYX‰dÔÀL3©[ê¦\»ÜÒ‘žajè±à'»ùP 5^%5ö¨àysvM l¬pBFþœ¦ŸÈÍìðžØ7å°ŽÑM ¢jФTþ¼(8.#÷N×ÀŸ*›Ù ¸cÞQ ãÏ* ¡‰={»ø>RÉ•M NÍ‚I¬ò£²ûŸ—‘ ý4°èdV*Ÿ½M"Lÿ9I ­Bá†v:›{?v¼öc&TÚѸWÏÜžÙ‘Ëc4ÐÖR£%¥öËN§®‰«HöU¤Á—Ø9lv•$xÝ鯭ß*¡åû±¬Ôù'ý3áôv"¹[Ì€ë…}Xc?ý,Iý4üØ[dÂÀÅ6"i80D :UIÃù9ÿTX´ÐM çÝÃ50 M9i8”Îõú‰¤á\*{w ìmä"# çñS•0¶ÿ`4œ·š÷ÛHÃyQ¬N;jÉÍ7õ}"äÜ™-†ó”C™à1±µ@Î+;¿—>Ñ‘ÿºßl{D4œ'’†ó÷/psp/‘ü×óG]Jÿ»cEò_¯7öÓØÏÿä~šÿ¿ÿýÿOÙd9,¿ª%+NÃ}ÓVlô'5<Œ•‘‘­5á´Ž}TJÎ6KE2äQ2]³gÍ•P/ÙN §´HŸlÑ89¨këmÉå…rxòQ.#¥òŠ’ƒLôÒ‘·6Ë¡Q…1lÀ„žX";¥BÓ¨…ì « XÒÌA$»ŒI‡l÷±ì¯âçÇmŽkÉRj(ê®×‘RùÖ¸&âÎL‘4ÿš)~SØ‹³“`øÕ™ùäWLœËž¹%‡¼?É2²×ñØ–ZU ¥ö¿ý’¡Úº"ÙÿnÜzV“mœš*KÙC­’!èâº%¬ÔþR{‹Ÿš”Éë[`FÝÒìØ (u°5»?0®/ÃJï[üwk÷Îö2Ò¶øß¿9™Ø’Æ~ûY’ûùeŽ—•–‘^nr8âÓ‚í;RGŸšëȧ5ð4`>;ÜR:7ÉŸ«àI­?:², lf äó™éàê5†­:)4uDz•§eBßö-Y©ü÷ÿ×sɵ|[2³¸§G[Ù‘—žeÀÛ=Ý2Æ+^„4eOnLÿ EòK@dn™Åv—¡­g²“Æ+A­íÅJåûüK!Êž"«½¼#û»J*¸ŒZ$g§‚ÏÊyì.! ´Å¾_üÞf6˜•Ú?Ñ-Nþø¡#}¦&ÀwÕvÌc9´‘Áî]+‡2_VêürŽ˜ÛPGÊM4Pfñ^ÖØOc?Kr?=<åÐ~ªƒŒ¼ºC'¾¹²/‹÷¼†½:òP¾lƒòغ_äP®O»,\Í‚W³‘nоR1#¶\hÁö9¦†ª¶y2Ò¡ø¹áÂà0;Ròë˜-‡5+ßÚ‘!ÅïI}Â[ËÈ  j¸µ£²@~[U¼gß3éz ^LÉ6Ç”ðvÌ vCnÔ¼W…¬•ƒÛ®0)•oüH´þm)’[Ê*TõÙ`Lƒ•f ä€Ki°¸Îvžk&h¬Z°ó½ÕàŸ÷HFJí7$:x¡#¯Ø&@¿+÷ÙÁÅ¿×Óº¨ÉÌ•r¸Ô«³Œ”:õZ5 ]ûTGŽ<– f®õDÒØOc?Kr?#£å0üàF©O—즻ØÍÅ{ï»?Õ‘u[«!ðr9‘läRüuÌ»­%mÆËÁÕý£-¹¨&v/'#‹{½?'OFZ¶×Àr½»Žœc¡—eDR*À9ÔØ>VF¶ßW|__ëÍ.¼¢† 6ö¨…&nÚÌVý¡€fSzŠdh š±1Å?·ÃÃ5ä¢r8`»YFJå[*&@µ‹•EÒ¹ø¹Çõâoùaz:¸ÅŒÈ}ÏÓ!"w0[£«õž+#íþ×¹¹_Õ¤Ôþòm ¾Ãuù¸^øF]`ÓoÊaô°d¹Ï;¢ª™¤Ôù+f@“ÝErÅ­txzkì§±Ÿ%©Ÿ†›3Å?Çù±2Òpþ>?Z»×Iù©»Ä·—‘†órŠŸ›ËHÃùüÉ xÑ[$ çRù'éä2-\FÎ ×@ûa-d¤áüI\ti*’†ó+»àÞç²ù¯ù¶Aì­ðHGΧ/È€³›z ¤á|h; lî¡#ÿu¿GùØp4œ¯œ©€6CZ 俞ÿD£Ý¦Šä¿^oì§±ŸÿÉý\gsÿ'OhpyÓ@.ñOÂ7 ØÙ5qò»«lBK7ë$ûzËf¸{ã»@ÆœÛÎØÈØhÜxºžÌ9· ¿,8ÄZ8EÃô˜mìäȈ°ö`¥ò½³ø÷²°ÑT96`߇Ùc:¹ì óIxü²’ýlÑ Çíù©'}žÕŤ)U²É~#^ÄO«€¼®?fÝóa¥ò)1çì¹äéÆyØO|Àfô³ÁAÏôä¨vØäGÛiD8ª·”B2§Óa¬5Û’•Ú?XÝ Õ6Û“›KwÃc“Øy—p׉ ·¦¡Ë®=¬ä×·|&^Û¦'ã&ÚâýجÔõÆ~ûùßÜÏ;µS1!¶Hï¡8ý[¾²5/Ò²swÁ0;%ÿ Îˆ½íIq“):¨Ø¹p­ãO=9%Ï]š6Ë&ko7çë³ÈWÁÃðYZ0Rù+oŽÃïŠÛödßJ‰héÈÞW­Á÷“¢€´:€Wkîbþ¨ŠW×ÕÍ&/ªŠQ+ê³N#ða3-#&(qâç2HJå;ÿ9m6½Ò÷Ìq\çöŽ­4ÏWV Ñ“ckØâúÑØYÇãðÕ¦vHŽéo»Ug¥ö±µÆ­®Ù“ï‹{Sþ¨ ½LÏ㼬Ÿg.ú¿pd%ûSª.ÏMË"¯æÀþçuYc?ý,Éý\i­AçO€ô?£ÃoÏÙC³ðzN «}´íÒö°× :bÑ^ V5 ¾_ÇLóE³žÙ¤o?tý)°§ÎÌÂ_c"Y±ù,\°M`¥ò?ÚŠw›Ô°'W›i0gÓHéà¾?¯ríDô¨¾›Û^ ýÄÙä qmr)6xW&F,1Grü®ìeV•Ê×ìð ,]å9!'ÐãÎ_åz`÷¶§³H¿§q™Ž´RFã‘]ñ@ª‚a5Us{RjÿýYnè^ÈCã=®w`•Wáò1ãíÉ©bpUb%=)u¾cåh³/X$-¢|à·Mkì§±Ÿ%¹Ÿƒ«é±¥ðÈOß³1Sû‰í·Õs=ÂØ¡Ó±–E8Û¡¼;Æåe²W¿oÅð9ØZ3}qù¤®Ù䛉›Ñ|YÖ%l!θÝÉzý¶cñ@Jå/½FûÍ Dr˜¼ø½cËÐ,ò›•ˆ‰k£€T§ïañ안ˆs ôdÅœhæÀÊœãðê_ k+có5qY¤T¾ZñÇñûöâïÃÿq×Ï|{ê»Ç±Zöר“êÁ–¸§¿5ÌS±ë‘ùzrúFE\`¥ö?ìÒÓú9ãm?TíÏVw ÅÛõ¤mp&ë+d“Rç×3 „^Ér‘<ñR„¦bt¤±ŸÆ~–ä~n>‚oâ¾™|9“^ýfëÄ¢bÊ^vµ"ýd—‚ñEÓºHFÏÃÒÐŽuè¾aæ+=¹¸h*BÂ>6¬y*Ž·ñµ'3:\Æyõe‘Rù§ŸÀë³È#MÎb£ulãOg10# È1âÜúXdW'wA—µåôäÝá]q¯Ý ì»5ßêNêIÇdµ®šMJå;‘‡Ù¿(í[^aí¦4ÀëY>¬I ô1;̺çŸÄÙoõdVnM¾ÅJí/³ÓÆ.ò´Ù ÜâìÍ:, Dçkí²É‘k‚03ÈŽ•:߯¯ zìíbOÖ<ÜK»–ÒØOc?Kr?¯åç¡‹ ’ufÅÈí¥Ùv3µ8¬V"׿åbj š}é—≠Žž­ÿê«°Û‹vzrÁ‡º8dLß,røµ[ØþÎV6¦Ù1|<#‰•Êÿ/`®ï¥,2â^!^¾öŒ=\ÿ"V ä¼qh9NÃΞm…¡SlÙ‹q­°…ùAö„¹+4Í&Å:±(࢞”ʧۛ…‹òI -~Â6=UËÏ?Ê^Vþ„ë=o°k»(‹ï³zÒ¾³/6ºÉJí¿í9ÏÞ¤÷©a¸^4>ïžîžM^?åí~4d¥ÎÿÝi4¾-ò²A]¼<ó$kì§±Ÿ%©Ÿ†Ÿ&GQ=¿ ’†ó´À¨¼“ ¤áü¢0|þ 5’†óŠ»ïCàáªö¤á¼m¯=X4u–ž4œKå¯þé .{ó-‹4œ({ çt‹Òp¾e_#ü™}HÃùÕ î8Ò`8ÖuÈÎÊQ±¹’OÎÄá§õäF=¾JËaý:Eáúz.@6öŠÃ6UíÉ´¥‡±pdlR|KuîÈJå7[å×èÉ÷ÅïêòY뛩x÷c†H>²Ö#Ö¹—EæÄ®ÄÓ ’¾sÖàÙEö¬üí!_/È&‡±îŽÛ")•/pzìÔÔ5‹¼9¶ †,Û$uܚೄûz2A^½Sß°ù_ã᥼»øÈ/PǼI©ýõ,\±àì3 —ÙÌDZ÷ÙqÁ"þìá 'מ>ïú`¥Î·Z…¹Y{õäÁþñ¹"‚ý}þ4öÓØÏÿä~zvÛŒí÷êI‹ú^ø¸Ô!öÔ‘±hUI‹ªÐn+vçˆDœ·J¥'çw‹@«X¶¬iZµªÆz˜gc̽ìÀ×»Ñã]ÕlÒæì\\¾_OJåW>«Çªêõdh3ô¨Ǿ\=ävìÁÊØ÷²ëYÝ¿;tG2Àß/þhÆn-TàÛ!¢ž<Ý9Ï×®›MJå+8ߟœUÛ“qY¶Xw@ ž•EWü®'#v}Ô”Ê&O,뉫^˜ù>eº×±Rû_/YŒÕæÜÒå˜62+d W Cããz2»ó9,õþ<+u~uÏQ8?ŸžœØCÃXc?ý,ÉýœÓÊ'VKÒ“v“¡S5;ïÞDì´¾ ’{q@ [ ñA÷Z‘zrëœ øöf›'Ç“óX·§qiöÖ»^L6IË"={„7:Ø“Rùsû6ÂÈnzÒyKS\=b![©o!–.À~-B?ˆ`{&lÅǦ¦HîNÜÙoÎÙíX2¾>o‘M:Cž¬Wš•Ê÷¼–=Vëæ䎳ˆõÊE±5#^A–Î&­?…²MØíÍüqöñšHP†bD½®¬ÔþÝ==ð³æ òÖâÂùl 1ß=®'¿¯Kƨé¬Ôù ×þ€æ9=YºÌiv25öÓØÏ’ÜÏ¢+.h¾ûˆžTš„ÏßœbóMÁ«eHNœ9Ëô´có+[¡ÙÚ=zòñMSÌmÞp ž{Ìn9s½ûßbkûûÁü®Z{²i··ð.»*Rù«fµÀŽ=õäè m°Êê_YdÆõËØçÂ>=ù ín³ŸUîÃü€<û5”¦lÙ>+qä¬Ãz²kbstR·a¥ò½×näîןí¾ÇÎñ šzþГËMÞÀû ¯YïŒ=èq¾+’¹9‘øpI VjÿÃTOìw? È÷5}°ë’ v™ûn|üð€ž|÷h)Z$…³’ï_QjˆÉÕ“îosÁéIkì§±Ÿ%¹ŸQFbÞ…zÒkú 4Yy‹]ßm{ÔÉäùópÚÉö¬é´kpye¼žŒÊʀѓ”lF¨ ß&Ÿdg©öbýš©¬Xv†€7K†bµÈ›¬Tþ›ªè?ck™5²3ž8TZ${ÛÂôؽz²ð@6îKgGJDÏ{Iv¤gœüx•Ex"ÂØí¬¹ ʸ!’RùŽ*úáEÝ O/ìI ª"éðó+töº¡'Z—ÁÍ[rÙ¬„}øxì ô<ŒiÃ’Rjÿ—Õ›°ðw"-^oAuÐa¶Î³žxö@¸žÜ|³<¾žÍJŸþ!ØÜÝ¢'cºVÆß5ë°Æ~ûY’ûé5¥î^ÿXO^+°ÃýÞ°í‡/Åf}› Ù½òj¬ZPÝÖ-šLŠ×“[õé`Î:—Ú„Ó–îcÁd,Šú`öãÂT,Ý 6’3Aƒ±Ok°Rùu»ap»QöäŠ}ÖøÐæ!;t’ ÏtÞ­'ï~ŒAU{¡ÆQ,å0‘=µü z, fÓž'ÃSçŒ>m†Y¿íI©|#_öGg‡HŽ䀋z7g=­+cƹzÒ§bÌ ÙÀºnVáÜPv‰]64yÆJíœç‹£;ìrí,u1ŒÝlv 7ëÉRåýW©ógŒižÁÐíí ¤±ŸÆ~–¤~~nêºa\ê=i8×NðÆÌŸ?€4œÏëz ’CÜõ¤á|ÊÃ8ªF€ž4œÿðHÆÄÚ)@Î¥òolñ¤u' ç_[ãOa·ž4œ×¹rC¾Éõ¤á\ç¢ý¥ ÿ5_+¥6°o¤áHćϮ³É̓°Fç(vÁËxwm(+µ×Õ³èstCÏ`mKd;Z)1GûIF~n…ç{ Ö“Rç×ZZü´ÃΞœ×M‡UÌ"ÿ_Ÿ?ý4öó?¹ŸòÁßaä({R5¡Ž>}˜=Ñó(ÜÎæŒx ¦£.³_ÞÌĪ鿀”Çx¡Ïãls3¼5g'»jg,þ²[ÊžË/ˆªþì)Ùe,³Èš•üù 7qʈ‘Yd[å-ÜRm3›–¼»^éšMÛ‰}—taǶNDÛßßôä÷[ûpâ‹{lnÏ68ÄÜ‹íà” çn g¥òÅŠÅófm²É*bpöìì³Xô\ 'g9D÷&—]²¸îÌ®»˜…[ÿ*±ÿNßÓØ¯V7 ­nÄ Ý­XYö¬yFO¶žáƒvUke“Rç7ïw§Vi©'ïìÀ¬±ŸÆ~–ä~>xYK‡žµ'ÿÖÃÄÙ]¥*à„%•¬Ü¸1¶ýdŶþŒ;+û±?ïÄY[²¥¶‰x¼CoŽŸÃý/ê±i4Xcä ‘Ìº„ÍtzR*ÿ¯E·ñj³è,7ÜÆþÑZ6·S(:jÚf“c†â‡FìÉò;p³éQ=¹ã¹¾úÃ&Oò…³ŠÚ¬û±th1Û>‹”Êwj}Þ-Û$›Ý ;Õ1c_Æ`@^ªž¼;ßG=Èæ®Ý[GMg+Yk ÆqVjÿf—¨LndÔõc¸}FöºaF£ÎÙäd¹ –1X©ó_.ZŽMOŸÕ“CgÃØX“lÒØOc?Kr?GThŠsVòG[üU£9ÛõEw<ÿn4»¸`(y´–… 8ç]c{òtÍ|¬Ry]yyÇulùöŽ=yáÔm|yaÛ.hžkR!›tÒw¼hÅJå/ºy \Ì"ûm½‰æŸ±‡JmEáyÅlÒæòZ<èøDOš;ĽØVK:`[¡7».Ï £:ß´'g»ÍÅ/n@Jå;ÚaþQWÏ&ŸßGß4,Ïv81g†íÒ“µŽÄØèÍì–q€ë^Äsý1õH ¥ö÷è•ç_öd¥ÝyX¿ã=víþ XþY·l2£«úÔ3c¥ÎwYÒUª›MÆ™5Â/ï³Æ~ûY’û™oÑ Ww´2ö•€¯R†²Îí\qk¥ì9+OÜzâ0Û=åúa‹Ÿ',a÷OµDíÝÑl½/t²? dÀ¨=x/ª:’RûÞ<‚«eGíIߺ9¨Ø~è¯X>úŠž¬!CÛ‹#X©óëEÖćfæÙlå8b½)kì§±Ÿ%¹Ÿ¯;à¿™@¦_sÂʇV±ýæø£Mc‘ô=GÏ.`Om¾†§<¼ôdƒM—1.y›÷DïNe‘SGÆãÜ96zÒ¼«5F]ø`O:Ôí‡#þR*­5…8ìC]=iãsOkÊ>w³ÅˆÉµYËE–¸vzë,²Î÷xÈñ«Ì~Ÿºõ;d¤ç=~lR Iëûè}[ ¤T¾î'Vá¥Çôä«Ûó± (Ùk«w]Æ*î½….&ÍY‡÷ ص~ $W\Qaéc Y©ý'{éñqÛ­ö䢩"ætÉšMë„Ö¶OÒ±{s4mæ ¤ÔùvÖŇKz²ÔÓ&xaÚzÖØOc?KR? ?{îNÄÝŸ¶i8;ƒ3>ÞÒp^ùÜéâçìP=i8·Ü„çÛøêIùiù‘xem$ çRùÃ?œF›³íõ¤á|Úé*xT5Óž4œo9èuÆ5¶' çE‚ý:Û“ÿš¯Ñ–Éè}%XOÎ󺜃 ÛªèIÃyõ‚Lhõ ÈÝ?d™=2kÙ“†ó·›à”:w€ü×ó}:`VŸÉYä¿^oì§±ŸÿMý4ôm½8½‡3 ¢n`÷ ÃØY­íp`•Êz²Cµ.˜Ü»|9qær î.d“iCfc»VìPË=0/¶‹ž,ÛRóÏ$e‘?ÖtÆÛŽm²Iý¢Vø#»POJå—ŸíŠa½®Šä—Ò]q¡c}{²Z eN§lröìžØï‚%»|K ªTÇžÜÚ2JíMggµ(‹«Îšf“GgVÆ›5¢õ¤T¾ö—¢°‰í +ÆÆÛ†²ÂöXT}»à´.@Þ é‚óæ²MwDÕŽT=YÐØ ÉÞö’áÝw GuÁ1÷‚غǽ±ÅކH–Ëß…¯ôd¥ò­i•ƒßßݲ'Ue£¢Ò>¶IÙ}¸{Yílò•{$ªgY²Êȼ٭HO¾~ƒ½Ž³RûkVCË]²ÉA…»Qu¶3;ë€ný¥'[YÂv¶±RçØ‚?/fÙ“ÊY±Xép~iì§±Ÿ%¹Ÿj^ç-JÙËï \|Óž<³«¶¹vȈ}µQ,_ É»­pì´{²ƒK[¬2ÚÈÝ…»pìókl•Nr|~õ+{bêN¼_£:’q_¢ñ½ù ¥òŸïÚ‡ø+ìפ?Éæv쌗Û'f‘?Ýðžûl{rX•-øµq,cíÁ¼O©ì!C›#™yí¦æ)•/Àâ,n,ÓÇžœ>í"Þ-$í}Ãñme«lÒvãÃöÖ¬ÇÙÕøpë>=ù±µV ub¥öGîÂ%>²Iè‚ïÊZ±&ÖÂÁw&éIï—§Á¶[#Vê|ûí¡8xž‰ÛPåû”5öÓØÏ’ÜÏ®ªËhÒ'Óž Ûz }/²ëÂêàÝèjHF†ÕG÷o Ø{ã[cQç‹@®¯ÔKk+!¹i¢í–—aØåcM)vÜõÜí4EOö²WâÅØ*Ù¤T~çîØ?ì}^öÀŒ?Ù7FÆÞß{ –‡$f‘ÉŸ‚ÁÿÕS”Ú?ùM0ÞÙ!›ì„¶cç× ¥ƒ2‹¼¹ÜÊ/)’Rç/oº›•«ŸMäNÁ¼øŽÿûuUcß¾vú?eäN‹ÑHn®r£Ãeø&ç0²Ó•ëðïqŒë^„ŽçErð1OèÊd\©|›«ÐùÅ>9éu©fæp/õñÄ‹16"9é ‚c{rój0{—úŒœÙº˜½¾<‘+uÿ“…ʦ˜‹äcñ 6h™rËn‡1«fÎ2rÔìJ6ÑAƒ‘’ÿ‰#ð ¬·HZyÙaðœî\e?•ý¬KýT|e *FMY{Rq®§ß {>˜‚Tœ?¾> Ú;­@*Î}3ΣÃÊõŒTœwé4k"Šs©üòH{<=cRq¾þÁdÁ@*Î'+‡ËæPF*Λ,Ù„Köf"ù§ù<5îÁ´ó9©8_í‡f"©8ß6ݶº3òOïéSDRqnû¢-öšÈÈ?Ý_x´'ÆD·É?}¿²ŸÊ~þ/õSÑnmQp E —½ÕAb÷ûBMÜ\ûš›ã÷4^pÝ'…ÇÝö ‹öœFëEí¸ñýÁiºHš…hו^ßK2²õÎÓØšKJå_hy¾zßedãn·Q žË-]YÍ6³ƒi9ú#ccl¹i çñtŒ ÈÚ¬|lîzƒ‘Ú‚F©…sgtWÃôkš ¥ò½‹b^Kf dîðxV:­JÁ¦Ü`dÒ£k¸hwŽÛz¹—Ìä0ÇÔ쿹R÷s#âÙ€”Ñw8š¹~ÌÍ~‹-îzÜV©ax‘Ç•Ú_à5 •n2Òtözdeë€üOŸ?•ýTö󿹟§ Õàl˜ µ-T`þ9†‹Ñ•ì¡k ×л}×¼Á-ïž‹oAZ '¾àrý5#[ íŽa)]Eòð?ß³4ýÛq¬.¡»s¼@:M*€Í„†")•Ûž›ØY«ŒüÜûúW¼³'»QCiÎ99ó Êšß·'eCÏ¡Æ;”‘zkr0{Y/î‰æxíܤìí\´º¢Å•Ê7ðW:›hÔT _†å²W‹ ädµÍ Xö8ÊÈ-U×ñ;ê0W[Ã%]@çOƒ—ª Wê~ħ06XÅ^ ƒXVÜC>€¶Hê‰{±¸—%WòûQð>ÜZl²Å\?ä=ïËUöSÙϺÜÏÜüw¬e×(ü²º†…r/a§žçqõ‡ž`w¦p…ƒrÌ.Keäë^gá?»|} l2©/’3G¶À‰grÊŒ$èÙ¨‹äÔ–;à89O ¥òß}*7;ä'®]Äk×&rò{Wkôº§ÊÈRÿ1øÒh ·Ÿ[ò~ÉÈõ±òt±œTÛ냅}Ùàa*ÞïRµ'¥òmýû ë8hšœ yøi‡µÈ%Ÿ••a¢ñrFÎlÿÏÏÑÁ‚›Õr®·6yò£/º»T2Rê¾£ª?k´¦³@&åø° r#n½î«ð¾Ü^$/~™„8Áš+µßzy$¬m{Vœ Á«ì§²Ÿu¹Ÿ·m®³iŹ¡G!Û!óá&tË`džý«–S6ËL]Ìmhs Ƴ?Û“¬cñú½ª@†[`a·ÞܸiÝ`y+ÒžL½ÕçGdËÉЮOY£– )•¿çå|\þ¾ONj àz¥ˆ;âÒr”g'02¢j/&]¹Ê}ë‚ò¬ÕwƷΜæªÎ–£¦Q÷ú9–öÒI©|+õ~°iöÍd¤igM|Ñ8Âõ~p-4oq't;‡o õ¹Ÿ¾Æàe”#ÍüRQ0ÇJ ¥î¿zèÍŠš´È&;°ÝºM¸%‹ÀHÖîµÆVí ”Úÿ³ô(žoýÆÈÓMÒ0kWÙOe?ër?ÏÍbL<²ÿ­tÖÈe/×âí%¶4¬Wf_þºäËIëOþôÉW w-rÃë­ÜÚC 6“‘®* p4ð"·µîmæ0é%w_«V¨¬V)•ÿ¯þg¡s¤¾@:ß:žŒ¹?M}ѦëFîΎ‡¢† GU8atæK´¸5 ª‹¤JI**ßsû½?„UM®T¾ÉFú°,ù&#肌w¦ŒT7Ï‚ƒéÇ\òÁcX;¯PN.{‘†üqϲ]R<uI©û%ÞûÙ¥÷õ2¯¹›0üœœ’Ü)»G ¤0¶¼æô‘Rû¿®NÅÃk22ß:¿º?‘“Ê~*ûY—ûéÏfmÈãg#غ˸/î×2í¬ª\Ò?´,æt“‘MôVàôU‘üþpnÞ×çfº»àãuï¡yž W÷Ì"x×0Òü`$êÇáJåÿ¡qgÛ1<ð6½ßMçê´L…{ý& “ò2±üwS®Þ_ö(kÜZ$W&wÄæ%Ü骳Q19J £t:ÀÅÔÑŽ”Ê÷xžrâÆ2ò¥Ãh\hâÄýQ³arùçP4<¾ˆÛT//>ôÉÔG®Øåщ+u^Sw¶Þ±RNÖîØÇìQÈUk¨ñaäÐñXuç;Wjü«P|4‹H³…‡ðån-WÙOe?ëR?_û±Ê™ÓRqþÁ®f}(#ç‹ ­p/¡ƒH*ν‹€ÏŽî çBn<¼’“Šs©üUÞáȱÜ(ŠóÌ©"%j€Tœû[éàqh‘Tœ›Öƒ–çFþi¾¨‚¹ø\éÅHÅy÷CX]vH çO¬çã‚á üÓû¹‹ö1­ÊcrRq~ïÇRlÿÖäŸîO݃Æ"ù§ïWöSÙÏÿ¥~*šQ8Ñ÷2r_Û1H*ÙÏ}ß/vÝZŠd¤dŒ¿©Ëí¥×»úæ $š7ÆÌ5g¹a"Q~Õ•‘ú%à÷äq2²âC•Q@šöºÎÌ}R¹RùÝ/5EgÿUŒÔûª)“|¹{ ¿3[Ë–\•®úhñy÷䤧ÉDRã¥3†Í°ån‹yÌ–þuYFvŒ„ð¯{)•O{Aœ2 äöZ?tOpGým‹¥îŒ>L@ú•R.êGbó¼…¹ø«;âáJÝwë=«Ù/F¦Üy½† 3³ï!Þa¡Œ\»¨ ÑûÈI©ý¹3 ï¡)’-§F\kA ÿÓçOe?•ýüoîç_ÉÓ‘çtˆ‘¯-Æè‘þÜ‹‘Qx]¦-’Gfࢅ:7ÿf1[=çˆ@–~‰b‡î;s7g$ãÎ^sIøØ=—«=Ï›Éà ¹Ý+.3ƒ˜)•¿¸:Ž¿Ke¤í~5LXPÄ]ÜÖ›ìáþ~:SÌý¸.[cç] ‘t¶ŸÍ×-¹±‰~piÅÈÐey¸uc W*ßâî‘6ýš@–iz ¾ý[î(¿¨LyÙ|0ý(7öýLLqé ’z]-à{²3W꾇×$džUY;m*B6åZ{\EÕ}LˆËB™;Wjÿyß>˜ðÔL ŸÌ°»¾NF*û©ìg]î§~ÿu(ÌHí;P3'ŒÛüŠ'XóZ,´sµý}n—ÛجöÆ\—6‡Ù{órrh½Ìéª!’oßaúŒ.ÜCv8tq#ã¶  'Œ+•ßëµöm¼ÇÈm³4ÐþÃgn­öX6 çÊ’üñ}À¿n÷µÈ791ª/Zn‹áÞí~}·'ÊHAUx¿ç©œ”Êg•²ƒjˆä„þNpøK‹»eÜAT õeäÚø½\ÆõœÞ?Ÿ6ÉÁZû9M ¥îßï7o"t@þî6C.µäžû¸\æ² ªa\©ýK mðþ†7#C;CÍæ÷\e?•ý¬Ëý<âëŠQŒlrÀ?ŸGróÊW`Ȇ\é biÕ"Fú˜É‘YdÁÝ·¬F…p·³)x=Uäžu¾Ä |b¸&_¯³V‰w¹TàÕãxnñíuÈÛoR*Ÿ|õpä›ôy¸Ï\Œú×[{z!ðÍF–:Ù ýX7;£%Ì“{ÊHDzÉÑ{#¥î§Ž ÁËTFnXŽ6ÁNÜßm}aró)w’,õm7r¥öß”‹Á+²ò—ÁY¬m *’ÿé󧲟Ê~þ7÷³íÓr¬,'=gÜ„åñR®ka<¦|ã^¯ƒsýiù¸,Óç0òrÂ)Œ[÷@FnºÓ W'«‚”»ôCF5®“å,igÁír26Í )•?Ô5O>—‘gÙ$y¬å^¿¿}>¥1rý”ƒhõÕ‡ëRô–-Jnra—¦Xÿª·¼8Ÿ{s{]Ì€ÝÁÖ\©|Mf-Äb¡ÈGÖ¢£Kîóý1Hó#›Å€AÕ_ܧ|n(çšî: 3YWêþ”-Q굂‘¶[bðÎr.7¡I"|’ksÈ«¾ñx½)ÙÏ­ihب“HnÔˆÄe^\e?•ý¬Ëýüµîš‡ÔÈ¢¥h'ès¿Ÿ9ŒÖ‹¸mCö¡Æ2‹kÿ(ѳCsÉyNW`¥½@þ6[‚3šŸùòÖ~h™gq?6OÞA!—ô ñ@•æy”üüšy*³ìÈš‡éÐk>TNžðŒÄ¥Ž9¬<×ÍžÊÈ’†ñ¦¯>ÈIE¸XЊ;Ó6Wb"éz6¯ [ÊI©|½.íÀìwæ mGºAÇȘ[1t*Zì×âÖÏ[ŒÒC®–ÑMôÛÍH·ô{èÞæ°Œ”üÿXõ€ Œ‡'Á`ñ®Ü:#–|Èö1^°®ßA$¥öOS÷½–"iòlœo´å*û©ìg]îghÐe<‰´ÈØ´"üÐÎý± †åÜ 3gc„ðš;ïJÎ_ äêV_ÁȈ›\ýT–ŽaäùG¡òÂÊž,rë ù M‘ìw±šåÇ|H©ücª“¡u½XN®×Fz¹(â,¢Ó.Ú‘ÍÏ!ûÕ]9¹Us9dkÕAæZ¹aå'Œœ6×ë½’rÞwn ûÄ•Ê7tÉíÒä=ƒÃˆì­ÆÝ² ¾þ]¹ËÌ÷bÖLkn¸Ó-„Nø)'÷ÌËFéO”º?¢ÍQ¤&ôeäñò4¬›Û™«ÕaêM·ÉåI£`nß—+µ?zý$œò~+{È`|ß‹«ì§²Ÿu¹ŸKΣUíl¼þEŽ!V븭 †`¼Ü>¾Ý`Û¶žHÞ7.ÄL£úÜÞgP0N›«=æ$ú|,‹GžB¸j÷fS̳õ"î÷W'#¥ò›OÄë…Ã2ò]¶nà–T¡ÿå…\Óê‹° Lá.È÷ø7‰Œœ#;îò}OØö=5™°#œÚè•ʧ5!o}däàw¡Èùvû+í’õ™ÐÊ;ÿ5t¯oŽHû©‘ÐuWêþ7—ãp ÐgdÏÖ'Qo¬&÷I¬2mErB½6hšôF ¥ö_6ï…ÛÊÉ1ɽѷcwF*û©ìg]u´GíÈ×½Na“Ú~®ÚšÖˆŽQɬ™ PYòM ¿nK@¼i ‘¼éæ‡ýwÔ¹BÓÑ}hS®ãÐXd¬ëÊu°^‚ç­ý™Ñü(¦L çJå7ívÓ  ¤¾‘'¬¶DsÛ/»€^º÷¸33ÎãaqC‘¼2 Îéöd‡©ˆÖR U¼ÖG[;ÒªÄg1R*_ÂýHXNg¤“N T{yp¯¶ ‡sIo=·ÆbŠvwî“åM`âS,'«»ç³Ä!µ2Rêþ¦àÓ0íôUF>mpåWʹA-Qóx·@®>ßÎå’Rû7‚ýÏÎ1rp—¹ÐölRÙOe?ëR?_šµéXzî@*Îw¾xÄÖ }!Šóq‹\à9ý…@*ÎÖóG‚¹­H*Î×ÜÀ¼õM©8—ÊæÖØŸ9.Šsµæ9ˆÖR$ç5³Ó€˜HTœ_´SÃù§ùÆ:ÅáõáþŒTœ—½M†‰› HÅyÖØL–^qœ‘z?mZ6ê'œ–‘Šsk¯ÁvÆÈ?Ýßg¤ n¼ì òO߯짲ŸÿKýT´å‚‡Lëì´rºÎ~wŸÊÕIMÄ£#C¹Uó$6ÌjÀW?½uDÒÁ2 ÓÍõ¹»Ž†¢AÞn•½ó?‡s—: ¯“–Œ<ùâfWH©ü•™Y,îH¥ŒôݘÁT×3R£x1Ì\~sí¦ïÁ·¥ÕÜ7f‰ožÀU³1+›ëÿ%’mw«–‘ûfg1ïç)•Ïèõr„ ȇÅSëÊ-Ý£¥&ß¹sF¾eÉ©/¸ºåÕøliO^v-Eÿ•)uæ×¸‚úŒ|Øý®Y\“‘‡îÆô¸ FnöŒ‚Me Wj’YKœèðˆ‘õR†!vÕîúü©ì§²ŸÿÍý\ð(Ÿ9Ú ä$y&‹8jÌ Í̅ʧÉ2rDz<¶®''¯.òG`»–"y+Ë5³êsŶC¿-X û>×Á /˹X ºh<æ> ItZ‰¤Tþ~«Ó™í≌ÜŸÆjŠöpUæù`ÄÉ4nZÏpô輘»þÍeœðÎuô*ÇLAF^hÑ¿HÕõèiúŠ‘Rùzä ÁÊe³r„vo|7ç–L»Â–¬¾Î5ð$›?ý4·‡J:½Kä~Íw…ùþ$®äïÇá8Ü~GÈÈ„AǰÞ` ×íc–úå22§ä¬ê»s¥öŸÒÚŽ ÙŒ¬¾›¨½\e?•ý¬ËýÌly”åé6HõÚh&_yQNš~¸„_ ¾p­º]AÕÊ>ÿÿÏy›æ`ߘ"î¾9ƒ°öÕnáÕlûè'r2C-–ÙÈõd¤ã—MHw~)ëÒú@(º '¥ò_k~œY˜„12%þ4Óøç³tí­òéw3æÇÇÈÉò’*L©w-— V‘¡­rNûHùvD:Ìì'Rùjk;agzµœ\%êap¸g_D³/?´ÿÈŠ‚gq5ÛÚá8ˆ»0ª!¦u/#¥îooŸŽo¹ìɶSáÒz˜y&¶wÌîËÈ¡‡ªñÆÂUNJí¿87 ¦7ãdäÊ 'Ѱ×o9©ì§²Ÿu¹Ÿa;‚Y?9¹4þ0óoªÅ½¥]ŠïF!Ù=¶ z\Ý*+ìœßIN¦nèŽ Û i½9%è ç>ŒMgM¿¥s³Y¡²þ¿Ž›°º˜ƒ”Ê0X`q«/22üÊ%–r¡Š;öm*F6tÈn«‚ G¹V* :Îû{ûhzœæ:Ãôaj"é9Ù¾-m¹Rùºy4…å” {rÁΆˆzBFþ}#€M{ÙT ½"˜yðf9ÙÕ÷ s:’ÄHƒU$×4)uÿ‹ÛQ$ýLË%½š¦ÀfX?9¹Ïí“ dí¬ȧ¥p%?ßž†áÖ|®w/7Ie?•ý¬ËýÔÝîŢϫåeªûYàóZ{ò㯋x¤ùJ Çç#@c‘4™Ý?¥3²ÉÎáð­UéòŒUÆÈ—™xPQäÍÀ0LÔ‡{Y÷,â¾3R*j« fÝï#ÕÞ>g!{~sßîI@âè7ã •˜æ"9ò€ý.·2fn*t‚žr‹„áï×S$¯7À½G)•Ï~ç–ñÈ‘ײ>²ã'–pSƧ³ŠLW{²bã–±û´ŒÌûÖÏœ»\èµý+L¸R÷WlMB ¡·œ´¸œÇ»¹ÜI»"¡.¤ýÉØþwWj#XÜéa.’Guc©žWÙOe?ër?çŒrgÅ*{e¤ÚHwfì_Ä-¨ÈÁŒÐ"YÿaäÚqE÷Åø²Ëäèн;¦wWü(tŸó›‘ÝWî€ONw®ãr¾## Ć>;R*ÿgýZtª1HócjØ3« ÷pÌ™i.’“5ŽHÃÛwëeä‚ ñ¨í[.'û¥Û॥HÎÜø›ÝH|'RùZ·¹ŠúéŒÔ̹‰‡«ü¹¡ëoboŒ@–ÿ.AµJ×U· ÔÂ̸“¶^gú¤ÉH©ûÓ½¡(A]$íSQÏ[ƒkìuŸ<܉‘‹Üc•Nç¹Rû;WÇÇչ˥- óœ¸ÿé󧲟Ê~þ7÷³âØZ”y…2²ªÙìkÆ­ßh7·Z‚lé…Ì+½¹áZ#¡]©Ï=Y° ?ƒ­¸UåÈ(ÐáF8]†»ê]F®´·»~=‘\g•Çú7ÌH©ü—“±äË F~k’‚ T¸iþ‘˜T?Ÿ»Ô<)‚3·iEÒƒvdÃaû±ì wœñÖþí99¢£ ’ b¤T¾ew0êÔF¾›\…ÅÍ­¸}sóQýñž@:՞ŵþ¹Ãì/1ËE9ŒÌo­ý‹[‚”ºŸ˜™  M‘´•' Èõ_ß§7FUu3'¯ZâŠWj¿—óæûHNnôHgß7n–‘Ê~*ûY—û™tÆ7¾‡0ò`µ'¶êqýú£úVo‹WÅ"8Ú’»v›'Rí¸MnF`ÒvkîÇ’R˜ºïddüÀKxí½AFªLñfç—¶“3oc7îg¤TþMÿ<'x…®“‘±M“ÐÿÉ“25/™·/ÈHg³´\6ENzZ/Âñzwrp[[h=ãþNô¨”xF¾X‘ ösW*_¶é=Ôœ{(#üïaØ2{î‡öi¸£úS eAѨ=ñ‹;^s$ÔJÌA m=pqE3®ÔýàÜX4ÐÉ¥™Ñx:ë_37.Bô± ­>qU®Ôþ‹«ãXÉ‚mŒ\í˜ÏšŸºÁUöSÙϺÜOã÷>ëæËȆOa3È‹›˜œ§Õ¦ íÍÄ´c­¹k×§ãi±!×¢K6œ/þÍÈqȆÚñ‡r2Õ=ª× ä\Gh{œb¤kEfƒ-*Z\©ü•0_'\ -aÁåS\ý‡“aÙ£™HÅX#׳9·ÉEW¦âQ’EÞ‹b‰ÏÔ¹¦Ý0ÛMépÁÇw2R*_ƒ•%°¾»K {â©K ÷×ÞÑpòsá=‘Á5Ñ„û+à :Ó[‰dí“rf\üZ ¥îÛî<Œ’úEÒëAôÓkȽ4È>â,û4 ††š")µ?«AÒ‚yx]<Ô=Re¤²ŸÊ~Ö¥~*¾‚KO Õš®ŒTœ¯¿|ó:7e¤â¼[¡7^Ï>,ŠócÂy¦S\##çiÝŒ0ð¹‘@*Î¥ò_é²)5—Rq߬%^õh*’Šó#Íϳ­¹.ŒTœ1ú¬>È?Í·^MŽåÉ©8wµ³‚KãUrRqþŠÅ²šŒù§÷ÿv>€˜§õDRq¾n²6úÕk,’º?hjVé 䟾_ÙOe?ÿ—ú©hã¡U°˜c&'ï¦Tâ—j"wZe,{T³_ '™bû÷ôæîÇ¿›ƒÔݘ‚NE匄V(¬ˆäÞ©.¨yØ”[±«í5æ ä)ïcXø’+•ßqG+üÒpOó?íÌ5nŠﺀœ ˆ+}t¹M&Åâ×ns‘œÛæ~®ýW·!UP9a'ÑòspzÕÊ×e F éh¯‡ŠÚ®××}h§«ÒÏí0Tv©p›úÈ~GÝcd´æPdo¯R꾋PÉ:%~H“+×YŠßsîfWìœ×S$m»ŒÅ€>\ÉïGi™x¾ýŸ–›Š1hèSFþ§ÏŸÊ~*ûùßÜOK¿ ¤j|““…õË‘¯ÚA ïwc)ËŽÈÉ‚]åq{Ò£Çq¬3]Ãȃ¡i¨Œ2”‘–“q1¨\ îôýŽÜ.Ï×ãb`>÷üµx|04—”Ê?µô[7L §Eú³Yz:܉©Ç1Aõ #wiebÕ½D®eƒÍ8yM_$WË&C·P…û}²²sq;èN€ËÛh”ÊÔ©5*MHsÍ6hñ´×hU(®%ÝfäªqG`šÈµkè g•ËÜlŸX< ù!#¥î?œQÌÂõî ¤×Êfn‹Ô¡-&‰dä˜÷lŠÍc”Úo¸ý6>nôbä^¯ œ[ #•ýTö³.÷¿®c¾­ƒ@š¸Š²±+¸_¾ø3›òO2ròâ4·| ##ïÇ`þKm¼buý}¹s‡þóÜÿ3%›ÜÚ¡?6™ËÈáÛ¬G”sƒvì‡ÍЮ ¥ò¶?À~T““o·º±ÝŸç’¶¹™K³cäkß ÌšÒ^FÖs²ÇàÉY9qrŒðÆüa‰kçî… A^°ÏÆ”£­¸ïRËàÞp3#¥òÛäìe#vÿeO~éÆê<%#ý.'C·B”“ÙÂáup¢@>Ðþcå’ãÙcºF;FÏòÃÐÝÆ {^¡Ù]“+•ïIgK¸x–ÈȆ=e‘Ñ…‘NÛ1Nk‹áÿŸårÒlËl¤è$’Ï6G½+z\©ûg°Ü¨Óy¾G:ûµ+…»+Çw’‡3Òvç2ÜZÈ•ÚjQ’f¥ d©Í”Ø's•ýTö³.÷sDõy|õŽH—/r¬ôHçôi…%ÉÅŒôZÖ/ƒ*¸£§ë¢V®@.]VÁÂb=¹ãÏÆ §«Èv-rü¸×q{.οÖÈ_2<ºÁ•Ê?VÅ‹…—é2Rãq+X0šÛõ‘æ;d‹•›P»ï÷FÙd„:†2òs̼ ¿ÁÍÛT޼!Û¸×ÿº íà›rR*_ö¶Q°_½„‘îggcV°7÷ÇíHŒ\?I {7‚­owR²È\ó¹ã¾ŸdyãRå¤äçoóöÖ8J L`Õûý¸{OàúªÃŒt¸˜±Ew®Ôþö‘c0/m­@Ž¹Õ •—½ä¤²ŸÊ~Öå~>¿p%åg²í̓زàwÅÍÑX3§’‘!×cøù2î_÷¢YÙóé!z°¢AÆ2ò½J|ô 9ßò. 'Må¾ÚÖ £ Z ¤åï7,Í| #¥òk¯‹eC¼w3²fã)véP(wÈıˆ¹•)s º æe ÷Íe¸}ÿÌHב øóƒ«}ÿæÙÈ1)³aõГ+•ïçZÜ\ÃÈ{CöàÄ\uæ¸çrxÍnlùÛ|gC4[eËÈíIãaív„+u}b,xÞ] _Ï9Â&Änæ.›T„èûçsÉà%èóv@Jí·\¨ µÇÚŒl2BO¿œå*û©ìg]ê§âË,; –zRqžeš5Îe¤â|ê_¶lÂTF*Îÿ>}K÷:Ú“Šs{ŸvHPmRq.•èñ‹,tâ F*ÎßOSÃΧ^©8’–òÒgŒTœ[|ÐE^3;{òOóíNôBÉ®F*Î[å.ÇÆ¶ ¤âüX’"UÜù§÷33£Ø–]sRq®õ£†G? äŸîo}Î 1Ï›‚üÓ÷+û©ìçÿR?}{·)lâ½²ue ­¿ƒ«¿ç,ªZ‰¤ŸÛQX½oÏýÞÆmØgÜtj¢×ꈤá¤YhçÃÈ7æ°r~0·°S NB_ gí8„>Q")•¿§¥ –ý|ÌÈ©Éîxóñ×¹G,œZ‚tO>‰mîF\q« ^oÝ#p;²$î‘£piqW™áYjÕÊw©_Öº¨dê© èMáZn쀲"59ù¹¦æXøÈÈü”p o1Ÿ‘½öebãæ‰rRêþŒÚØUÂÈ ;›ÃbÞnø› Ëq‹?ÝAÍ-\©ýmF.Gk»"™5ÒóNiqÿÓçOe?•ýüoî§Z›¿Øïá³R£écVàhËm¹/Qf"ÐãÞ¿7åŽ=~žoÅZÔ¹{Ô¹o×'à½ÉFŽœƒQ{ÜddÈ]+ôýd"’Ï5²˜ÕŽ<”Êás©CEF–]ò‡ÚˆDîZÕsØv¯Ȉn—Ѹ~1#k>Ű)ê2ÒS5’ùŸÏÈ«OäÈ(ú›ë²µÆ¶¥2R*ßµ}'Ðnw€@êe¥ã»§À­Ú)CÈK3F~Žq}¶r§_ÊÄï™…Ù#?^LERê¾½]KÈ+ž0ÒÔAïT>rÙ’j¨Œý;›ºêJ5W ¤Ôþ’^†pØ‘%›Óô`b¬ÁUöSÙϺÜÏ]'o²qº©oqK©“m¬v£¡Š±H–®_ŠKÚ-¸ª¯ŠÙþÅ·²`(k2|/×¢~òÏhsßÜÿç9Ü8‹»äA{­;YF®Ôꆧ+Ž1R*¿§](þžéÅÈà«‘hvu6×dj)¶™ìáúvº‚ΦU2òe~&»—ËÈ`¼d™®õ@ö°øçÿÊj¾@®¸àˆì˜®T¾Ì9)hdñD ³üãг_C‘,ܶ …3rù¯ÝXt‚û©xò¯Û‹äóîF¸v³Wêþ‹˜¶8Qö“‘]†¡UtC7äg‘Ôÿ‚@6¨‚`ý\©ýgk»âsQ²Œüv~>{¸0RÙOe?ër?ûæ°•Ñ{åd½ß'XÿƒGsÉ«—ÆÃ÷¬ŠHÚºÃoäóå^Lç§œœ™Ërß|‘W{ž…ýx-‘,œƒ>Û,¹ƒ,át?FZÔÕ rR*ÿä 1è]`ÈÈo­ã1Â4KFú'Áko=97+ÉûÚ ä´‘mÐæWÛ/ Bó—¹{jcçÛ9™kòžåþóÙOJ图;çóZˆä— ¸XlÌ}ÛÀNç0²_ïH\·Éæör~ÉÌ=Ž ä£¿™Ëë9¤Ôýy6ð¤ƒÈË«Ì`°¶×ZmÒïå äÔŸ±Ôe0Wjÿ««nxá”ÆÈ8‡XhtKæ*û©ìg]îçN׿a™oOv¶Šg¶Ie¤Á+¬"òÁÄ®ð:<‚ûɼšµ/òb¤“»LcÏs­rÇÞö"Ù/yJí{rí·Ï¾˶æJ忚‘çŽçíÉÕ²Dœ›f-'›µO…õ¬­©eŠ’v!ÜŽÅköÅäЮ^¨ÞªÅýëp¬›Ö‘ÛæŽ+ªæÛs¥òýb^è ÞU$\öÀ¹Ÿ×äëQ\·Kcäø“g>#€kÖÌËÖsÿî»MÁ\©ûÛžvEßݺ gGY¢rbîÃ{ø¼n¢ŒôŠé ûጔڟ±TŽw¡Ë9jåUÔ$L”‘Ê~*ûY—û¹¦y K|òFFjw=Âzß5edèê.˜á('sn÷@¥o#9êùph(gä·Û`PÀ-7°ÀZ¿f"é¿V­³rÒ»íOæÜK]$c¦ãkå¤TþÂÓ ¨,},'»Æ#:¤@¹¹s£¹s— î¾/7iy8,®ÝcdJqÚäž58“)ê S7ç@·«#¥òuK_‹–}DÒP}’nÿkþ½|Œš´„‘7—`ÆôÜË‹Ž!$AŸ[/<%-Rê~ç€Þ¸ñ¸È¶ÐïiÂ}¨3 ¸wM=¡7¢=Wjÿøæå(×Y ô-Câ†|®²ŸÊ~Ö¥~*¾ôwݨø)ŒTœÏœ`‡ínÍ©8ÿrÓ•1Rq~4¥)FgµHÅùG#X©:1Rq.•ÒŽ”‡mHŹóúÁ˜e5_ 皆)ÐzuUF*Η÷ö•!ù§ùŒƒ‡awWk‘Tœ{Ÿ¼mÕ2RqÞˆI‡ß äŸÞOìÜå½MA*ÎW¿MÆ¸ÑÆ ÿtÿì¦"×i‰äŸ¾_ÙOe?ÿ—ú©hÕa=ìn4Ž‘Þ'MÏVs稧a’sK[c2aXò‘‹Ý P^K‘LsùÅJ÷_ÈMªá¨ßüW§µæœÑÉe#Oáq##GúÞ„ëÚ匔ÊoV6« EÒ祶qzûc­k+¦{’ׂ;…kúd:àeý29éªë„[î dʼn¦xô°„+•ÏÄÏ•mpГ‘N®lQìonu° \"Òßmâ’£¹×ŸÝBÿçrɹ®Áw(R÷ûÜ‹ÕñŒyiì¹÷¶G0—ªã9äÇh6dÊ>FJíw=«G•3ò×ÉPYrûŸ>*û©ìçs?gïꆘž®ŒŒ¾kƒímq ’£Éæ|n\p,oºsKê¿dMÏLÈ%Ž¿˜v^»\ÒaÐ8,ô0Éþ-¡ª£Íí¶ù.øLH£ïžhp†+•ÿe›¾XªÑN$ÃÒ:Á&F{yHvômòC¯b|}XÁÈÓz y?™n62æ0òíäÖýt”œ·#ù·Nc¤T>¯D?VÐn*#‹ŽÅ³š2îæ!˜®(£Êº¡yäNîÔ½'pw‹±H^~áŠiÇ,¸R÷çò„‰g#«¯yÃ2ç_-þ~Ì:úÉMlÕ •& %÷Ç!ÜÅ‘¶Žÿÿ{ê©ì§²Ÿu¹ŸÓÒãS~#*ÆaTëHîÞ­y˜õ¥%7ãU.f…«ÈÈr#nÂȽ‡¡þ~nfÛbfep[ õ›ù±ˆ«S¹¥½Q¾òœÔ±€Æ‡dFJåõIS¿ëˆ¤[ïFÓCûãY9F×2rý¬jÜï¬Ë-›°sÇp?y„׮͛.0 Йþx?¶jp¥ò ¬[@6# ÆU±Í•w¹&kZá`†­@ÒÁ#/ÈÉŒÏöˆÓü-{ÛÁ^½7Wê¾ÑÔÃ05Ob¤ÇL»–À͸!…@fhÁæ‚*Wjÿ²æI˜¸ÚR × ‡ÿóB®²ŸÊ~Öå~¶é?öª1Œ¬(Zµ±Ü¾&™Ø8)HN~vNE§gfY›µ'…3²âYž4ÝÇýRáÍ|]±'ÇLËej63rúªÝ(OérÑ—¼°ÑáJå/Øø†m+­Èt×lmÌ#î¯z÷wif.©]RV_: äçxXµHeä½lŒ´ÝÍ}”„ó¦s½ŒD’½@J~?Zó›É…¯ŒÔÜ9çƒÜäþ’õ/з#k{WþÓ¥'2rüq4^ÞŠ‘Ÿ¹ ;&‰+uÛ”`=ÇÈ9Åa8Xu„»´y t?EsO¨¦aÁŒ2Rjÿ…énxÿP]$=^ŽE`®WÙOe?ër?qBXT #WÎÞý⸅[£ñý d–e=|­ äJ>? 5ÇŒ1Z ƒ-¼Ç5çÞX^Îò//aäÚù÷™–a:÷ÁÌT”|õãÊMŠanÛUFJÝè…#™æÿ—!Ü>C£0`·@NXꊒˆd®Ô~Ÿ6ºðìßD$±“®WRÙOe?ër?7;¹£ùFš/òF£-‡¹;›¸ÂmI¨@ª \ŽrË î¸E¹XÁ–roYf¡àWˆÙ'•Œ Ï Dù¡ .r½QíuU í^ÙcO“¶\©üµá,XÓ] çiû2ë}\‹o©0 ~Ç]ä‚Ò[så§n`§Ý®–{ ذ›Ü޽ÂXïûûeä“ öªò#¥ò½š3 #Ž79aÐrT¬jÆ-ÉúÌ\ »9ƒÇ2}‘”ºß¤&Sîø3rpÛd¼Ô9Ä}—5'ÎäÀê8°qWj¿ËØC¬ã9&û~z±í¹2RÙOe?ëR?_kôü×Þ‘Šóâãa3nŸ@*Î_Å%ÀvEk‘Tœ-CíÆ"©8qè‹­aAŒTœKå?tÜ‹eת¤â\÷ºl\_ ¤â|Š–€ejÚ"©8ÿå;ÏÆvù§ùjìBYŒHÅù]c¼­g RqîŸã…†«mDòOï¿еÁŒTœKËc.Y}åäŸîïÕ%5ñ>ÀÈ?}¿²ŸÊ~þ/õSÑŠxÕË™‘0» ýê!Ücαq7RíɘËULS]ƒ‘y†}1÷Ñ5}~~¼ôd¤ýßi¬ãé¡ròʦ6éæFÆ·Å>Ï>,'ƒÖ Àóé>Œ”Ê_•ò“=í¹˜‘†;›à”ì·[Òd˜¬:Ä}?Ç ¥W³¹™ƒc™ëâaÜw¯òØFí"®õ­¦xñZƒ+_¶û¯Çp%¿?ìÊÆi{GFØ– §bM®ûõãˆ|¦Î=ôê,*šÉÈ—žwáøÄ‘{®¡êØ ”ºoè„à ŒT­ Áí×¹®†ñy¿@Z[ŠØ²ûWj¿kZ"î­®•“ñ/|`2ë€@þ§ÏŸÊ~*ûùßÜÏŒöÕØx¦#8ÝÃlã2Ònb}Ⱥ­bä@Y[V…s'Ý Û+ç¹Ú†àÀãkÿjRƺÄ<àîîÖ¯Ö‚ì¾#:£×02Éÿ$ªtH©ü,ª-*OE1RãUW\Ò²òsÉð¤¾8qã4##ŸOÆÛ>ÇÈaZëP)ç~¿_†óñ…2²cX9ùï•“aí#Q5¯5ÈéHßô„‘–×ΰÊÃyékŧúËH©|c–Dáö®Åit7¥>îÜ7‰Çq{r#‘tòˆG3÷¶Ü×7V!°Ã F¾˜ ÿŸ¸R÷?¤Å¢]”#hÇcÑ“Ü÷Z·Y«áK2.è=Ël'#%¾¼¿™ÎRFŠ];âüµ¶ •ýTö³.÷3°Ë]tÀrŒOù¬ç^þå‚Ob…Ú>6Ü:¥ÈÕ».[Få ‘JK‘|8ç ,Lä2²[ÄQXtø"'uû ÁÏyëZ0ÞwôáJå_ç¹Užbä°ýk“ÈmûýÖY-ã¾–ÂõÅ%nnø¤õddÙÑ3ðZž.#5¦èAç÷UF®¸¿&U·¸RùöyÀ~Ã<~Íχrcè ‘¬jµÆÖ}¹‹«Šq}U#íWáLÎ9)u?«VÖ[ÄÈÖ,jÉ܇k;aìÒÜ ãõ×D®Ô~Cǹ˜ñºH7£ÃXÙÌŽ«ì§²Ÿu¹Ÿ93oÁq¸¯@¾ñ¿†ÃÏ“¹£'àù H9ñE2úÔ•“QGÐbv‘ÔÿéŠe­­¹ù·ý¡–²O üÖ"Ð œ;xB0äÝ넞->q¥òÏÓ9ŒþýYS„öÝvpcÌÏ#ÕKM$uOGy÷ܧÇàx)]NÎÿ¾srƒcV|-#ãF†cÕ߇R*ŸYí 86ÈS7¦`7qëY±Öv"9Þj&÷µáj?»‚Q‹î¤ÑºxDUšˆ¤Ôý<Õ$ŒûdÁH³ˆ$Ä}Õæn¹å «MîÜ€ɰm%#%ŸgÃñ׆ ‡êàâÈÛŒTöSÙϺÔOÅ×¢!Wðæ(Šó •ÉáÒN ç4&¡ÑI‘TœÏºmƒa*ëRq®—ü“éä\HŹTþ3ð6œ‘Šóoñeƒ•H*Îg.ôÄËm©8ïž2•£~äŸæ‹½9™Ï· ¤âÜhu/$Ìê.’Šs¿+ÁÜõEòOï?ü™„Þa/d¤âü–ÇqTÞî#ºßÄ´-ó{2òO߯짲ŸÿKýTôa˜""òò÷XÞ5Ž[•qg>r+w…áü$‘Œþ²ßþ2ù8ïŸçâA–Ü¿š®Eñu[î9‹0tíÃí72§¯uÈUv³P2-ˆ+•?dÐYäîÞ$‹u2‘²$Š;ÉW5Ÿp£ µ0)⑜¼«Ñ –#²¹¦+ÿÉ®Ÿ&#/l½JÕÉŒÜ?¢§H©|C:býÁ 22Ø­ÌÚ7e¤m·¦Øð|w©zKŒ~‚ëš”ïÁ߸ý¯Šèþ]FJÝoR ŸáõääÁ3wðFeWo´Æ©O³'e«ÑÎÞ‡‘RûµgZÑ]²ýO $î›!#ÿÓçOe?•ýüoîçú-ëáY/Q •/FE“½Çþûu•c»¾<D™’1B‰hФç:Èœ™ /™çyžò¾ÆJET(4QJB*I¥çºS"!J2O™2gúï/çµÿëùr/kÙ»õÜ_~ÎuŸçÁ:Özî¶ÁsÂ/a£»«1mx…ðæ–ÐÍäƒ~yhµã #§Ž°¶…ËV7C‹ÈÎ ²³Y7´rµdd×Uƒ1%p¶Ð.j""…çÒŽ¢b]K‰Õ} |b›åòéÄuÀ›³3ù´² ì”áªڦˆ²!t\ïˆ7J}ÖSâšñ@Nþ4ÞŒºB¹ûçÂKÑvt¶’Ì|.§58ùøE캢 r\–òG·Êí—ž8Âîc#_NLJKBu?Õý¬Îý´tü ?âä® ƒ±T!lôÝ/~_¬é7R¤ÐèÅM×ÜÎÈñ˰‹ž‚tÌ)Âïtaòì<ôùÜŠ“;ëûÃo¼=È*Ã|ÌËzÅH¹ü_“ãÔÄ9*äþþXOøÚh8:—z22k÷J´*Ù'Ü:"/‹=Œž;ü„(iŽ·Wqò¯èîˆ 2a¤\¾Ïݱ1>—‘Z€QßÂãÜÐ"¢5HÝËqΨ³pí ;X´5åäÕÖÆ‘Ù‰‘r÷ïåÞÀ!ÿÖœ\^§Kö(„/marAsó–^Æö­eŒ”Ûoß#L_ä›¶IxROC¨î§ºŸÕ¹Ÿ c{bÙò`N¶®²ÅË Û„zèÑÂÌÁVXr/KIîM½W©?'7èÅ™#Yˆ€$<®yD˜öuòB. M¼s±^JòªGV–Þæ¤\þ«^{ÑÂ×X"›ÆìÄä[í„æ;póñ.FÚ|ŠÄ±>³…%WÓÔD8îá9YÞÈ$ÝmýðÒ0œ‘³f¥bÊÔÓ R.ßÍžãñ0F¤~½Åh4Ú@XØß§[ õ†„"Ǹ«Ð»E/Ümý‹‘£úcO½ åî·mQ„¦§Çs2ñÇU”­ºŒ¸ >¢#Gµ½ÏN8)·ÿùñËÈt8ÎÈ~Š;¸89]Aªû©îguîçÍ'æ˜òc'-¶µÆ&wáóWVð|œæLšÎtÂî9µà~ú­>sÒnÂ6ÜÙüCø4y!´–% ntÁ°Û}…»O@Û¡'„÷&ÛâiÈ>)—¿[o\Jè$‘Ý­¿o)üf’×Â÷ ²¦öi¬i®Ì$;:͇39yl¶‡^7…Þ·Ï`ÅÍáõkAȈì,‘rùZ|ØŒË=›‚§·–FÍ…/…vïöÂiåÐÒHøŽs|ì«+\ð£ OŒú0Rî~½„+¸ùÙ‡“#­/áö«=Âçºç0äE‰P;î ¬ÇèK¤ì¿ï{V¸p25÷úÌ| T÷SÝÏêÜÏÌÚMyÖž“wÖ×Å@g¡c{WX,b¤yû)ˆv< lÖusrO·ž˜8r«pÆÝnXr*Õ™Lî§À;§uŒ|级 'VA“cB¹ü§/OÇ áÝ$ò‡ù0TZ «6¤coƒœÔn} j$ m¿% ¿f‰œŸãÖ°–0Î\8§a«:¾—“rùü—„bÖQcÙY‡ÑçucaÓ¼ ¨0®bäÒ…È™vVØ¿çM`ÒDNŽZzžþÊþÿlËÃW—(NFfæ"­ò¸páplY¢-‘7fº`Rð)NÊíépÏ'6—ÈVW·ãcpG¡ºŸê~V§~ª>´uÐèk®’TÛUy o|&#UçW›XÁ«,PIªÎWYÌÄ«Ó%ŒT߸‰Ò-NªÎåò×¹èç¼N©:ÿi³«8©:O3X‹¶­©:lsŽÅÕ±fäŸæ¾ÿ$Ž~Щ:wò*Å_61Ru¾éËfm€ÖþP¡ìïUÇpÖÅÈôg· û¤TAÚDÄàoﯜ|f/\CHdLaoÌOøÉȪhs­X(w_çk¦(ºJä.i;¾>°Ž>ë…«7³ÙÜ0þuG åöÇ\tC§C›y¿‘7&-<*üO¿?ÕýT÷󿹟Ï=“YÀkWNNú~‚m¨SWç ̽ÁH^çüÒÂ…¶Á1ˆýª rËÃTlŽNd伸b$ysr×ÒL °RØ¿(%NŸdys˜r÷'åòÏ~¢«Üœìíõ…%t¹&<Ü󻤧#‘;/g…½Ò8\v‹¹{ÍH/S#Dh Ò»³9ÜgŸS’þíÛ£it #åòµ›}fÒÈR¿;ÐìwOI¾œì‹1›ÛHä[i Öî(lÔ9¦UŸdÆó0Ô<œ“r÷7,óAi][‰toä‰xÞ]¸iW,ôò»)Iå©}Ø¿““rû×܉FÚ¡]Œ¼ßí jNø¦ ÕýT÷³:÷³sp"K¾¡$¥0{2ɶ;1­¬'#×%¢Ñ®­ rÊ·3Ðnö[¸Ï;n?ã”ä¦=¡xÅ %²©Á xg×Ö}w{›ÌÓÆý€ºB¹ü­ Ÿ²ovG89¿²Œ•¥o.®‘À m„ãO²%¶œÉ­5†!`¾ÈÐÏžˆµrjmDdíÂ%J8QS(—Ïcðm´˜8‘“¯Òo`L\´Ð÷¯!è9º£D~ñî„Ak[ ûù-@‡%©œ¨=,Æuf Ibf•Ñ™¤ÂÞ6kó)—¢þM¦ß¥+'w·)e>¥Õvˆ¸Ãr²_qÒ/ä6³||_ANrÑÇW½ßŒ”»ï¸d!Îßµ—È+f `×ÿóÔKÖwÚ|NHd—Ç.Prû»\‰…þÈæi×Ç%aöBu?Õý¬ÎýÔ7+d?_6eäÇrvýž«°ÿÔý¸Þz!'ç[ø#+#P˜ã6 Ë»í޾è€{ß: c6[ 1`‚‚<µv(F¥-fä¹O¨pL¶þƒ{F”¤\~sƒû¬Í÷LrZÈ+æ¶aµ‚ÔÚe‰c?®22Œ7~6ôß ¯D.Çìr íÓ“`8°”‘rùÌÿ`•zIœŒ¹PÁZuù[8®n>m¨²ÎÏóØñ #û™ÅC7º†’20n…§9)wÿnœš8ôåä eC´ÜÚE¨°5Br“¦ÂÑ…føøø¥‚”Ûÿ{Ø6Ì~û‘“3GaK§Z©î§ºŸÕ¹ŸïSO".´„‘çkg@Ï3WØt¬ì­Ž\=¦#÷ OþS>^ãd|qüJþ·ÝÃk K?°¹éL[\©;™‘÷\Î`™§ÈÐù×0¥²g&)—ÿ1;† z1Ò a2jÏÒúֹ̌Û'qòãÐPv·'¡·½2+¹íE$•®æîIÇšc5•¤UQ vEpR.ßHåSf¦Óˆ“m׿dFAÝ•äïûEØ ÇÈU·‘s­—ðü„Ix2¨†DvýÁŽMHá¤Üýª®˜°ª'£µô°Ñ飒\ÖÞ¿ì`äŒÝ;°ôP PnÿÛ¡vê'× Í`k–Mªû©îguî§Ëk %û“y~H.†† ‹ZÌãaƒ[ðkÌáÇz Êɸźi=W’[âÇa’ÿaF®òEÀÞpa‘Æ!”m»ÎÉ‹†#+ê P.ÿ€ÌTš¹SAžlœ¶Èiá¾lÉv#9gÉ>6màZFöÚ›ˆÊ¤— rÑ‹4\ûí­$g®êH—¾œ 5殌”˧ó…mw® ¿eèÀû‘>#ûçßC˜y+YÿžöªP’GJ"Ø3– òNÃGÌ©Õ'FÊÝoÑ«¬Í•JÒþ¥&**¶OÔ=‚kwuY’–Œ´Ù¿•¤Üþ†éÁl{ÂQéÑ0‹¹8ÏHu?Õý¬ÎýìÚ,]Ûofdà’ÅP¡Oz ¦$û Ÿõ‡¹ñ`¡Ñª,f§-Ì=}Ÿõë–#|Ûà¢{Z ÇÁ”‚.J’}¶GôÔÇ ²Óe_˜d2R.ÿ¬š™¸=h’l½(g/qòÜú‹,Ãó#³×Áʱ@¶~œŠíÙ[9Ù¿<ü ŸK¬C= ëô2°Ï¿„‘rùö,k ñŒLÞl—¦ãVN®ž~/í/ Wœƒ¡Ž C/Æ Ø¸£Pî¾»V ¬YÓ@IêuüÉÚüÈÊÙœT{\q„ÿÑÎ Uçæ{wÃ.¾±DªÎ­3n¡ÓцJòOóYô@rL4#U祃²1 »¦DªÎÛôɇ[ÇFþéýÕvßXâê|gRu>1Y­á%òO÷·»½ÿ´ë òOßW÷SÝÏÿ¥~ªºP3¯.0rÛ¯“»—"l¼2†.G…‡·æcQ,„§ÚnÄ`³Dáò5û¡ßôˆPšŒDß6yüšü: uMFO뙤ޅ­¨¿Ÿ“rùÙ²R|5šÏÉÁã‹þè0ûÄ8Üú‚‘‹mF¾w¹Ð¥hRsò\Zg,ÙòBØuêE¼Ìº*ü±ÅW·´–H¹|^ÍÝ0©q"#ë­›‡Â•§„—µ7Ãÿª±D®ú0 =ß¶¾Ï¹‚ó]9¹¯É!||öI(wß(¢;r‡¬à¤÷k4?ü—ÐþÃSæZ!Ü<(šÕ¯°p&åöÿŽKYÛùòöalZºPAþ§ßŸê~ªûùßÜσ]NcIÔ>FñMGûJ!ßžŠª òÔ©\”h^S’OŽ$áížñŒôÈ‘°Äw3Yw¤ ž~í&‘½ïübQZÂì,K”ŒÏW’ ,OIe¤\þnpÞQÈɇ¶๽J8¦`7ê[f02¡cÂ.¬õþÎL?ÇrrÔêLö(¼†p†};4Ÿ™-|TË3ÎlSrùîõ݈IŒ¼ï‡7ÓHaÞî3)—oŸùa„­×Ï$ïLŽEþý3J2ÈßÝ61b¤qƒvùN¨pU»}¨;O["SÃû¢Qž‘Pîþ\ ¸ëÄg’›ý:£Í¤ë=Èrpyk0',?“õï…rûmÆ[ÁG¡rdÔj¤v5ªû©îguî统ɽ¥&'K›œ@½–BÝÕƒáU‡°æŽXhé(t[ÄÌ!lèâÍXþ ù!ñÚ.Ë.ÿÖ‹489þù4d­ûÂÈcpØnžP.ÿ¦ÑK°"ÚI"·<‹û‹» õÝ»AQw9'ÍnŒ¸-O•d’Çž"=s®áÎø£ŒÔ˜cƒÁ óm¡NpW¡ìßGŠhh³nœLzŽiåë„w_°¿12otꃴ¸~žÍôIàäÞÛ™õÚŒ”»@Ë täìo]a6}¹°Íý•Xsì 'ÿì »×!B¹ý!³asýv ?ž½€×_1RÝOu?«S?UŸÂ}ñr~4'Uçµ?Y#Ø*@IªÎKcóâN3Ruþ4j'ÖXíä¤êüøÓ0d=ÆIÕ¹\þÚA=±ãRg‰TOò¨…9÷Qªsφ7ñÜõ´‚T—g£óšMŒüÓ|±ƒ±1:‚“ªsûáa±²HÕyB^MüêdòOïWδÆÇÊã Ru>æ‡9JÓÌ”äŸî·™r—Œ:3òOßW÷SÝÏÿ¥~ªú|ì~ìˆm)‘O{!y¡©0Ø9=·öä¤s^<p=SIÆÚš#»îkF>ü=ÃÛ4Ù>;ûMÍ$²ñÜe(rî!´N¹‚Å5l99¼*ƒÔ”H¹ü+5ÖÂýúgNn«3 g>^ZÂêžë/|~ËU ÓUcFÝÁ˜Ö;…ïÞ–Àˆõãä²±±êÍȵoaô”úœ”Ë7>4ÙÍ“9iøÞÜâ²°OÝÕxùW‹v ¿¾±pߦb6xÉF–¹L@÷Ë– åîÿÕýË/ÿ¡ ÷{ž`-òL™±ª ~/ž¥$ò{áÁB'FÊí_Ðã8œšf0òܵÓpïtGAþ§ßŸê~ªûùßÜÏç|»Ð\"ßýØÝ€ÎÂÛN²áf»¤ÇÒBöðÞtFfèìFÊ›¶ ßÍO…ö¨:ÂWó[Â,¦­Dv¸šÉ}¿ÀI7—n¸œç/∊VŒ”ËŸ]è{ËœtÍí ë¹™Âè„PÆ{12)§„½ó~-ìÔ+Q‡‹9Yú¯ïœø‰“º«öqrŸ{;h8¸*I¹|>å›1$ç.'GI ÑýêẨƒ¸WÖäÖ'ѽDSèÚìt}ê ?ÎEáøNŒ”»Ÿõ)•MjåÊÈ1$–6l‘°ö¦5˜'E–„Áª¹¯Pn˜Ó ÒãdÙœ/Þ-T÷SÝÏêÜOï:Kà}®‹DZ}˜ˆN5þíñÈøÌÈ›Q­¡ŸýHXqõ¦ùVºßÅ?ùü´{ñb¹’¼ôô{íµ‘Úñë¿ð´ñ ¼˜')I¹üQ½á0'’“Ý÷vEc»õÂS#txØ䯇C1ë‡p‹´ É>-$ò¼‘;rõë —ÞrDª!g¤ii0\¦ë‚”Ë7ô">⤙£3ªJ„é990^vƒ‘¹ƒ®áÒ•]²ï©xùu'ÙÈmhuQ(wŸ7¹È´Â}9.ñ:3iŸÐïI2Rµ½äç)¸Ói*'åö¿¸Ÿ##89}d<×W¨î§ºŸÕ¹Ÿ/*aµK'‰|ÜÍ ¶µ2íÞH;¬òIÍ9¸ÚN[øÊèVømæ¤Õžs€–D–ý4€Çmg‡OÄå¨6Âßo}±äûNút«Ÿ×Î åòOùf†zŽœ4ÝÚ¹/*Éë!žÐ¹brAÛˆXÜA8é}g˜>)ãdÇ% Ð}Ë*á<Ø?•‘ãà`Ú)NÊþýPfЧµ³8¹åºb—F ›m)….ê2òÂù2Ô×)É$M‡÷ƒÞâ¿9YPf‹QÙ! RîþFÜgCÜ0rwê+6À'M¨IÓ+9Ù-lÛ7‘H¹ý=ƒ,Q>i…3ùò¤3&XÁHu?Õý¬ÎýÌøb†OZIä±5F8:µ‰ßñ{ö‹‘ïÒö àg¹°Cq4ÒžXJäÊbÜ÷q–³=¸¼Ád܆$Dç0ò~—dö½ÅH¡¿ö4mgR.ÿ$×öHöê $clÍ0Üô¡39òu*&ŒÓÙÆã<<~œ`dóºè¹K_I–hšÑÍ9ç] jZ:I¤åi=´hÓX(—ïW·ìÙÚœœ¦ý€<¡'œuá´ŸõZí,‚ORŠðãá˜: Š‘óµ£ðæIPî¾q­o,¨ø<#ïhb¥m±pâL#оOêH¤ê\.Òï^xb2”‘ªó=ÚY¸›¸”“ªsƒ <²‡‘ªóÜÚÛ`l§òOó=ns…-âý©:wÑF˜ž•DªÎ:¬CÍ«&ù§÷+ÎcÒøÏŒT¯çŠÑév ÿt¿x=Û¯ã䟾¯î§ºŸÿKýTõŽÿ<4œ‘ÏÉák&ýëï.ÜçT‚¦æ»¹<¿ .EdÑ‘,Ã<€“ƒmüXçgÉ' “0áz'­>n‡Ý£Yõò.¸ÿv"%ŸÄÄÔÖ)—?[¿?6ì¾ÃHÃÄq(]þA¸=2 ?ÚsòƒcBü|…CܧÂgì!Fnº©)„Cœ& .>L˜=4 g·ÊåK+c,KÏmÅ1«6Ÿ¤™ÁÄXëH¤Ý-°o(tyT³CÛ89bh,Fh[H¤Üý’¶cÐFêÒ1z ê‡Ùûl‰f¶ØÅÈ—7Go#rûÏ<;‰è­z ‹7]ˆJOFþ§ßŸê~ªûùßÜÏùÓGaÞ?'8ÙsÍ@|m!ìjQ†EÍv+É”-Åpë´Ž“ Ž3í£Œ7AW"åòM›’Âvt_ÇÈáå—Ù;Ã3Âe¦À_ÙH"³=‘sN_ø½ao|Ÿj+œ·9…û¥pRîþÉQóq¼°;È æ«ðÔÝ^8Š­ÅÒkẩG‘ØÝP(·?îòe´-óV’ßÒñ¯o5Nªû©îguîçÇÛ=QåëÇI‹xG\bó„óå¡oU¾Ð¬â44?jJäÜ^nXxÜ¤í‹ Xœ¶ bÎc;rrwA +Špe¤Çí¶èe[!|6qbµž åò›^ÜOƒÚ ÔÁͺµ„×+`Ü4œ“™V-qy¤«ð„},â²”dA‡0œï¹‘“[!3®±Dvi½…Å×à¤\¾I Þ³5½>02þ¾Fh2gfcYâäÀ.¯™æ.\² ‚Å7éÎÈ릦x;±HÙß÷í1¥‡È@}o¬ÿo/­¿€žû¶1r•O†‡T’rûokîÅÚ›ï99<{.?ªû©îguîgÜ/xýãÌɧ/»Â=\_ØzØ!|°1’ȵ­w⮡‰0âa* 7¹õT®…Å1ÒÃô5+NЉ:ý mµêZ%£çæ7gÈ'F¾¸ý*‚“rùK›D¡Ò­ÈmgŽ!âÙ[F׬ •äz-m4¬ÇÈ[.¾xpŠ“ïMñÙÂéDzU†ß¹àÂŒ;¢)—o´:zµy÷º+æøØ+®sæÝq '‹WG³%gC•ä¥f0i òçÓ ,ˆÜËH¹ûãwlC¿ {»ôª´nMD†Y 'ƒºlƒIÜ3¡Ü~zvü#ˆ“uÇšÂÉm¼’T÷SÝÏêÜÏ]ÐþIª’|u²3JÝ: ù—åðÜJ"¾Œ‹žõ…†woày=]yâà%¼9ò' `Ãî •[Ò=áÎÜîp¯}]I޽2 v)—¿iT*ju,aä¢M +N ubÎéhaÛ<+œ×ú ¼Â-à乓õÀ÷w|zAßn1Ò˧gÚqR.ßW×p›Õ¤ÃÌ­ÉuîÝÎ2Ü$ûù8«³Ê›‘kÏg àý[%i{cÆÙ¤sRî~ئ`h•Û‚,Š|7kaìŠáØÔ;“E>hgXK(·ÿÜ{ücÎÈñ›fcìñÝBu?Õý¬ÎýÜ·» ºõ­èA–4ꆫ&*È[?-Pµ°’“Ÿf4„Žù)¡qדXÿ\蔄7ú¹sÿUð”MŒ\~íªÂ”¤ÕËCˆŸÎÈ÷óÏc–ÉK%)—?¨É|¬ØÉÈõ®`ž¾«ðöŒI¨ZÚdL’/ô¬[ wm¯båËZ*È®ZpÎðfd×0¼I«'‘Zaö(9x‡“rùînÙ‹®;ìA^»‡½/-„þ‹Ùö&7yá›,šÕ™î†ìÒ=œ ×ùW·ÚÏîAÊÝ¿îxïZvùsi,¬™…0ËËÛU!SgÀé^#åöþº{ì`ddB fVÕªû©îguê§êó³½-6‡æ+HÕùd-|9‘“ªs;30DS"Uç5¡Äƒw)œTçëîÇ0´”HÕ¹\þë ‘ö¾TAªÎ[Ö‰AàÆF UçZN¦8ùš‘ªói-â]î0gòOóÝ+:nýŒAªÎ}žY`Ü&3ªóà‹`Ûp'#ÿôþo¿£¨‘Þ¤êücR †7ØÉÈ?ÝoÒé.ŸÊT’ú¾ºŸê~þ/õSÕà’C¬Îï¹ÍIVÙ?F¨qï ûmRÎIûÚá,ÜÜWøÆãL›µ”H‡ü11ÐI¸,7 ™yæJrVÖÏ ådÑ _”Ùä3rÈH%öΓ¤\þ Rð÷‰ ]M%ìZGè²äë0º˜‘HhŽe† 4{ÄÖØîR’kûßcsʬî´ Ó’È1Í4X/Š“rùzmÁ:Sg÷ßíÆõvÂ-§:"èH"#™OOø<º×Ã'oçdÙ ¸j(H¹û=7G¢÷€£Œt{…†'£…ƒ§Øc_W¡•ýbR,”ÛÀ&wÖ:¼ÿY‰×ûê ÿÓïOu?Õýüoîçè…ç¿p‘‘<î²§¿_ ÷+¼XÆŽJò¤«yù‚\Þ¨?Ìîu“ÈØèhô¶¦Ð&!—ÂYéSá9Ê@¸ïóQ Ýó’“ß:bç‘Æ)—iH¶¼`äùn×0É.]XðØ æ-Ì@&öA`YGá«%ºèçVÈȦ}РWAŽ—X`è&Fz¯‹¿êv)—¯ðP |´;t=‚âM…nC§`ÅmMa™›ÑQ©/Lûi‹m_«Ù#dNŃ”»oê‹.F¡Œ¬šr…ÝÖ†#«êšpJv&ö>%”Ûÿ—÷-è:x02þCÒ×夺Ÿê~Vç~÷•­ÉÔi1N® ›½Œaƒžîgdpn[7íðöÅd㵘“ã±kÂdĽÆÐÅN¾Ð<Ç,3¡]ßýìù­QJÒ§ttoR.Nö ¸þôdde—[p^ÔBø-8•Ÿ AFžKÃ!ÃÛŒl¶îo4гi˜vº‘í„›j+ñ¶=¡Å·{È:;FIÊ勨}çÏþb䉳—àý3WX³‹‡ëÔ_‡k1’Å_‚«^¬Pßö& «sRî~Ÿ)Çà÷Ñ›‘¡Ö'0¯ÆjáÒèóX4ç›’|¿<ùûžrRng\Ìßöž“Y- {ßL"ÕýT÷³:÷s“A $  ÈaÍ-QûH'¡E±!rj =^1ùw^p~ÍŠnœd¤Ým+\ñ¬rgûD¶ GF–„|g¦? —}ˆƒÃ ašwÿk‚”ËvêmÜó·VÝoan /%ùÔ=åÁ‹™d’‡wF%Îäýfg‘uú#m6• ?Û"l|2µÏµ’Èæ÷–N2ÊåûY^„Çw½91µºB7·tüõ³Pø«â<"ù.á —ãh´ÁT"o𠯣¶öB¹ûW¯'¡tê$F>D f´ï#¼úÆ æšJdñ+øœh.”ÛŸøzŽí(‘#rëÃi÷Nªû©îguîg&záŸoÖ _;Æš†ŽÂœEØ8ÑNØkÂN¬Ð°–œXƒÓÙ …#4Ãá0à#›nvÅ;Ón óNïFy{áױɸ¸©DîÚé€*»+—¿[— 7µäd¯·×`{ÞWè 8‹ þ…­“áz5Fèxá.Z^ÔR’çFÜ€Ù»Cœ¬¿ô4“k)Ì ¯‡Åç)ûûj~ýÉ }‹ñý·&'“š^ÃÀMŒlþë:̺^É$½7§²äZg89"~?›Ñ±#åî7ZžŠqsÍù|nšÑž?˜Ã¬O&r2öæv6Är¹‚”Ûöô&3xÜ‘“Oæ¾b6Û[0RÝOu?«s??^‹fœAfœ\?7&\¶;öuM„õç¥!°‘¶ÐhíI ÜÆÈ}mÏ`o× ²Å sÆêƒ|ü÷mÜi°‘KØ#Vþmq&ùåî@¸;ßd¤\þuW.BË(“×~e¡üq¹p}D8.+n ã®x#Øí‰0iDZL‰Üü3 Ÿí» ;ûmEÏ©µ@öÌLA˜>Œ”Ë7Ú© ^8Ùq\.vðTá…NE¨Ñl€0zW>æ>-ÌY×yYÍ@Úu †or¡Üýö&hóø–‚¼¿ç 2?}“-ñŠ‘¶ôŵÝAÊö¿SGç2rpÇ…pèóN¨î§ºŸÕ©ŸªÏÏk¾è2GRu¾Êó<~~Ïb¤ê|ðöjÉIÕ¹õÃ;(þÖ˜“ªs—ÉQ”ÈHÕ¹\þv¹©ÈÛWC"Uç9ë§!­û5NªÎÜ_ˆã%RuþÍ7‘XÈÉ?Í·O?9wsRu>;L‰¥g5$Ru¾vjô|ƒù§÷Wåfb½­‚T›ìƒß<;º¿l}¼N\`䟾¯î§ºŸÿKýT5ÿ”ìßÎÈm¥SqyÃam“#¨9¹HÇsJ˜=®!,°ŒFf­öÂãrðøû=Fº+Vàk«}ž»wƒ¿ó®·ð…é}‰¼Ò£ ¼'nä¤\þHcxºþ¸’üÔ)ÛsÒó‰ô½ zÎÆLÍwÂñîânú^%ù[»:g8yÖ¡ ¦vÔ9Á1%CÚ åòù4ÈÔ¥Îä¬á¹λ¤$—õJAý3Ém'N"stmNNéÑü—’ÔÎëc/†0Rîþ‘¦»¡í”ÎHŸž!Hy‘({ÿs¿‘ÃÉŸU ì«÷ngRn¿÷àf¨lRÊÈÞ¡S¡?Æäúý©î§ºŸÿÍý,˜° ìBù¢ÙZ j È$]èp¦}Î: ßÜ/‚Ö?ÂÞ¯ ܺ ''½‰…–^±‚l¸èâ®U(ɯÏ:£´‘‘ë´`ò0F(—ñ×(,ÉÌIetî{ŸÌvÀ¥o7„×GAǽÂðß§Ðùrc‰\Ù6­?Z £JJÑ1­ #?GUúwNÊå{-áNÂN^šqV¥!Â#`ys³Í=„z»Ò…ŸÆxâPb#=nG#íÝ;)û÷Fýðä‡9C3 .„ óh`ùÕl¡y’ºÏlRnІp¼{Ô dä„sxÒà*#ÕýT÷³:÷Óváf<¸ÆÈ Ö>è£Ü+¼pò²VtÍ$=öÞB”ÃhNZÎMÇû%“KöâÖÓºY·ù~ôYÉÉÒë°ÄâªpÛÃX¼>ZI.Yº/'…sR.ÿþy{0æÒNæúã×øýÂN¯Y¿â/JÒ®Ã=æÑ4YAÎò‡[ºHdÇwMà9´Ž0bÆlܸá,œt‚Ùþ:ÊI¹|Mu£‰.ç¤U³ƒˆzsK882ûO?Ú|ñBÿÚ?…?n@‹N„‰sÖ _Ài¡ÜýáÁ‘˜Þh#ÞKÝ*¬´Ú‹oµAêô8à¿w2RnçRøy+ÈNâòÊ\¨î§ºŸÕ¸Ÿ½ÎúcÔ?Œ|õ+{· YàU,þ–ÇÉšc%lÞ¬/‘évs±“ÿÛ±]à×ì'ûìèÍÆ'…Åkô¡xÔBöÕ ×¼V+I#÷¸u/ƒ‘rù§yŽäd¿‘Þ!4Xø•³ÙËHûn-QióIØÂ¿„µüÉÉ!éìrj%Ùín&{•{‚‘C~.BÔEKrù¾YïÆÝà—œþÕ1{ß ×¯šIƒkHdÞ9̯ý“5;àŠÕ¡ALlз`¤Ü}npCÿYÍÈzïcP¹a’ÍDùĶJr‚nâ&ïå¤Ü~G) ßž·È¡›Ðy‘³PÝOu?«s?§…`Šë F®7 õßn¾íã1§C'‰ô?ˆ5{;Kzh5€“.uðµÉ@¹¾XF«k2r÷6èÑüŠðdÌØjœ½ÍF+{NÊå_0un8…pòœç¼ñÚuŠâPc% =QUÒA˜”v‹ÚNg¤ç­&øÕà¶0鈄¼Ø«ÂÓÐè'åòm0Z‡æ/8éÛÈ:…7…×µÁ™ÓÂZès&D¸ e4ο¬bäãó‡Põì»Pîþ¶ Ã¿¿#×TÆaãsaA¨ô}/p²¯Ç ÄMÚ#”ÛßãykL{ÕN"?†&²kS8©î§ºŸÕ¹Ÿ£Ê#1 “#}ˆB•µ…wg/†ïi…D:Ã@ÏZ˜õ¦.ëofdÚõ±0\œ)|sv:Ì\ AN©Å˳ÿ6ÔÎz­ZJdQD9k=DÉI¹ü¬ÏP¬™¾ˆ“îÍz#j„‡¢p8µȉ›”Øò‘CzÀÁæµAޏ¿öµþ­­iò“kJdëæÚÍI¹|~õÃçŠLN^Î鎨û…ã"°¾í„ Z°,Å‚3¤Îâ+øb«ÁÈ÷ÛÏáJã\NÊÝ_m‹“õéû<šOä˜Ü.³UI^)U`úî.Œ”Û_ÒÔ‹O3QΚùìáìbFªû©îguê§êÓaX ÷P*HÕùÃøñÊH"Uç ·B«i&#UçEuÏ"îw#Uç÷ŠÏ²:NÉŒTËåw˜ïïl]NªÎ/v(„Ëî=ŒTϺÅ]FªÎ³o¶Æà[5ù§ùÞ´2‡þêiœT(dA&¦ŒT¿¹ ÃÌ»IäŸÞ¯óÏQ|5HPªó6«—" ã^FþéþtW[¸s3ú¾ºŸê~þ/õSÕç‘ixWiÒaÈYŒÙZG8÷f n.¯+‘iVkQéÐXعê;÷<–“åy,îÜÝLrgO+\1TròõçúÈ´þ¥$ÛÅxâèßA–¾¼Ž A>Œ”Ë¿üdkTÖ|ÊÉßfúèyöœpn»¬hËrFîð2„WA‰°áóÚÈZl¬ 'lƒ1Ã2òûô‹(^›%ÌøV„Ù¿B8)—¯ q&¿=ÈÉ€;0¾,P˜«3×…-,]0-r¥ðVU‹+¶c¤V%<¶)wß¾q!ÒâC¹Óõ: ]½„žkFáý_ùœŒHkŒ¿ŒÆ åö·tŽ.=vsa=nšÿÓïOu?Õýüoîgƒz1Ôå##×ç^ņª«Âoˇ`ˆE}‰|<ÂWƒp²Õ¡Jö2Ô“‘¯ã» ui…Ð;µÌ–ê ¯Ôí v+Z8Þ(ýjüàdÉŒ¸¬a'‘rùíÚ}a¯„q²×—;ìû«IÂF û`ÿX]n«Ö¡¯K}a¬Ç@<:xš‘«®CVÛ|áòˆíØwÜQ"[åße×û}â¤\¾²Ãƒñi'++ÒCl…ÓöwECí6B㦘³rŒ’¬ñ9k·ùdée¬Ü;‰‘r÷w·¸1¥îŒôõ-ÁK7K¡¥¦&,o)È‘1f˜eXÈH¹ýc<ÌÑsÉy¥°'2ù1RÝOu?«s?׌¿ÇÈi·à†õBë­5»é,'?¸Æt›Í° w×™8~? <Þ1²xÚFäf ï¼Ü‡Ý· Ç/ôaݺ¿Q’­æ6†NCc²ß'iùlý—ÏJ²[³³lŠfP&9©ý^þRäÃÅÇÑùõyF6KÝ oÇ„£ôbÐúŸQ„C»ÙÛi džÂ6¾;H¹|ÇæÛ!-¢TIîð´FZ`e&©4ÁLí 2¶Ž^ÿèÎH÷ŸÙØj³‡“©:;ÕG_"åî[ÜÄsï_ ’w-EƒôH¡þI\]¤æ1$MøÊH¹ýËBWáxíŠòˆ röçˆÐáä6=ޝ9µ@JÃn¢ßÏïJR.ŸÖb¼ßÐ[AV9B{å7á¯eöXÀȵ)CqkÕaUÃ.Øîu…“æ£kA£ÅùtRîþ‡Ù¥Ø=½†‚¬éXЬŠ%™¤U˨|FöU€Â/Û©$±—ùmT& 6U’ê~ªûYûiúëÂ&÷È$C îáÙû|%Y2¹˜=™ÍHÿƒÚxþù£°õÏdø§Úp²ÃÄô~T8pT <ÿÞ&\…EèU,ìÔ= Ç•F9¦¼ÊGqR.ÿ'2673˜‘FaŸ˜béaß~iX<´'›éÅã—N˜0nB†/Û,œå´næaÂMN‘>¶Èaº&pª÷‹“rùvé Û=Ù§ùl¾V¸æÛ|÷¾)ds½11·\èSîŒàÐcÿvº?*/*…r÷c¢oâ±Ï%©}¤ÓGÝf—Ÿ@ø»zé:~5îèX åöÿ :Ž¿Ïp2¸ãv´j§+‘ê~ªûYûÙûR.†)8iíTŠf }…OêuÆŽfÆ -1]„É· r9yÍÐ SûžïÔ )öne ‡±¦Î¤á€šW6„‘ÿú;ªÞÌ÷B¹üuÌõÑ{ôF&´5A€·>ÈÈÁÁî›ÇÉ“oÄ€œBaÊÖ‰@‘Ÿ°Ùd†„ï~—±wï)È¿¿ À§)—oªÑ4Ô\ÈÈœn+‘å.lß=óÆ–+Äâ»Aš°«C<ö}z¢ ÍjÀÅòÁœ”»Õõ,2æd»m×qÖ©¿pí}xlÓ—È,Ó}¬Õi;NÊíÿ|´/zNÖ–Èq¿ÙãøNªû©îguê§ê3oÞuðª$NªÎ—þãI6 Uç¥Æ6ø¾Ë‚“ªó‡oÐé;‰‘ªó~“°Ñ©.#Uçrù‡tvÄ÷“ÍAªÎדּ4NªÎ¿Ö°BÀ·Š3¤ê|ÄÇh¤ÊbäŸæÛ牧©:_øë$bó¶2Ru¾cÁJØÏÉ?½¿gA!VÝ^ÆIÕù¹WÑìa[Fþéþq5³™Æ©zú¾ºŸê~þ/õSÕ>_c‘hö—3ý&Á½~d’5Ó¡­ª ]g¶Ãa!Œ,>–ƒN¿,„Uº9hÔð¹’ÔÙx »V¥ ?íÉAUó§œÜ¢8‚ªŒlõ;&MVrR.ÿð1ìÁÍQœ<¹ŸÅµ>¥$gW¤CïÛaNêLEæÃoBc«ø|ß[x?p;Ü·¶^Š9ÚÂÆ&‘Øá×E"åò¥½·BkýmŒ >è‚JçáÄŽ=`>»Ðï«+öœ&\8ä5sz¿YøbÑ_Ë6)wÏe¬ˆˆá¤UüE|(É~6¹‚²W‡9íh1þS¦$e¿+â4£ˆ‘Nå™xr¬‰ð?ýþT÷SÝÏÿæ~†Çb尿JrÚ†Cˆ[öVxò{w$däàïáXž/œñ#ƒ“8Ù}è.L¯êžÛ‹™_:Hä–þà9¾0¸ëhlÞëÍÉ8{Œu+S²ßÓeûرÖ5döžhöQ§#¼Þ†lIäóÇîX<·±01bŽ–œã¤Cw'Œû.ìý¡ž¬/‘GÆú²+¤\¾W]Ç¢×苌|z`v»-47/§ù ¿¯EýèýÂ6ÖI¨Q© òÖë¨ïÑYAÊÝß¼ð<ì{]åäËÙ(È~(üý" .K> mż&)·ÿÈ•t8µlÂÉ+‹¢`y5]¨î§ºŸÕ¹Ÿ¦QhÓÌ„“ú #q> ¿ÐZc¼F>bäèŸ~˜)=vŪˈ7œ,½ßqqñÂw50­¸DXßœ3­®aJòf/oŒÙÆÈ:nñ(÷=åLÊåO\tšÕ®çÍÈ5§‹ÙòFg„ì©)ôŒ~srøœ÷l×´xá×fhnÙ\x=¨´Ö¸+È‹œ°£¿ȰÉè·¿±Pö÷ïéØ4zÀÈàÔ xQ&ükŠ/‚6„WŽÛs+Oá%^.ÒHÓ> ÐJi+”»ï÷ŒcœË'N~ŽÍÀµQšyɯtçÿÛw°Š.œ”Û?/u#^†–qR/¬7ÖŪû©îguîgàøPÜ™8“yz!5ÛSh˜†È ëŒ<¹îöÇ nƒá_”äõ½F¸Üöž‚ Ö~Ê>ØÎÈ\©­Òí÷4ÉLääæ tý´T(—‡G»Z÷%#ƒÏ6DH¡.HÓVû¨| $O½8Ãþý:ªñmßN¦(©„H™š$Õ¾’™džç)3Éð%$IR©PiRI…T*$íëF$$óXæyNÈÌÿyñ?¯g­ý{q/ëyó<­½ß|^œkŸç±vGkß[C¡ÍHý7ƨ^ÃOè4 7®>½%¼Òø.LO(Éë$tÚÑA"åòýj‹-Î3²áôTŒpÉÚ*v¡›vo¡_dœzpCA6Ï)fwû_äddópömÅ@FÊÝïÜô0,Ð’Èð^‘øD_x"àûRèÌÈöGíÐ^ïPn¡akxÕû¨$?†·C¸~¥‚T÷SÝϪÜχ0ìiÊÉþ¿1Ý8ZøpW.¶L›ÌÈ€Û®”Æ(ÈÚË»C[;#Ÿ^ž‡ÅM%a˜Ÿ/â,‚t«¹O1²¯_kXÕõ2ýP¸þø(”˿ݦ®M0iw¶˜¢°õ‘—¬¶#g¤ñòVÐ: ²Úg/¼Ýõ‡‘Ö[b\ôN¸¨m#| Ò•Èã¶É¬ÞÈp)—ÏøÇAœZµ™‘ƒFÅê»vÂ1?RѲ‘¿#Ù8/mO+ÉrK¼™fò®c*îmý·r÷?uLC«ô¦9óV2ìO´×õǯ€ Ž>€Ôg錔Ûï=p:WngdEÖ ²W¨î§ºŸU¹ŸÕmBQËDNÎKYâÉB«þ'pi~¼’TDä#äÖNþÖ Ágç F¦¬ÚбB÷¤ãXìe)ìÓI‹u9iqê 4†(È—ß ˆ­)‘rù¯î‰@ÎYµ½µÌ…+ƒ'b¤ù¿}f‚}/-„ÚÆ”æ)Œ4ìu.­o*Hi‘R AÆigÀ·Q5¡\¾µ8ZÖ‰Uþ¹f,«©$ JÄ;ý9YÖf'*5ƒ…c²Ká´«?#ë5/Á‚×8)wÌ$4j+‘¾ÑÐHê \ã} óï7Qƒ Ò1¬µ'åöO´A·þóé:*}‹Œ¤ºŸê~V¥~ª¾<ýVàëþdNªÎSÿDQ®’“ÿg~z^ì/u"Uç q©ðáœT‡$bCâNªÎåò²ñÛ‚T/¬—‰öïT¯^oóæœT›®9)ÁJòoómîÃa¢ÏIÕù 4꺓ªó¬[›¡|Û]"ÿöþÃ#;ðif'‰TÏ„Wå8ù·ûÅÃd¯'ÿöýê~ªûù¿ÔOUÍó/#w–’“:çÁï<>Z½5öG1ÒáPJžož4ÒÑ«Ó8ùЦ"ýp"w øÃ\UÙ%d" Ø Yãò§kœ?f^iv“H¹ü#V9ã껑Œ¼îêŠ]Ò:á¹YŽèsë#'óÚ6„å–¡ïïtö¦ÂŠ‘MeÁ/…†ÝÆ\Å,%yÅa² Ì$R._¯ÃQx4Âä¾aûatS[蟄€ÂlFæŠÂòsB·7ó±Øv'í­q¶G…’”ýû.Ý £€pNî ÇíMqÂkŽÎ8r$’‘÷ÊÖãlX±Pnˆ{Ь'[µOfã#¢ùŸ>ªû©îçs?K- Ðt`M‰|â’‡Ÿ5 „G׆£Û/Fv¸´ ¡ël„ý[ ýþÂ}Cüâ¢0aIì7Yor />ÏÈ«ýY¹â¦’<¶¥t·)—¿Ãñ8wv+#oE£„çþ|g½Öæ¤í®ïì†Æ.égÄð̾-ȵ…›P~ÓZøðZ¤h(‘GŠÙ–Ãe R.Ÿ½fn{ÇHÃBì|D˜Þ( †K&uZíƒÕØ· Òm4ƒ©‰#Kc֣ߥ¡Üý#aÛ”¼‡“YóB0øÎ~á¡~I¨=*†‘-ŽàŽ Rnÿ½Æ(5jÒÚe#¶Ìvªû©îgUîçòE™ðÓJ"çÜÞƒgúÂf‰{ÐûÞ>ù|` žÔÞ™O®Øæ‡GY¥Œ o’€iRª°Ñç»ð½ç”Ož º€Ónr²bÑ1¬ÛaÒÄ÷.šÄI¹ü¶ÕW!#=Ž‘’/¬lc…ƒ^è!x¿¿°õ€n(7¾$,öËÀÖ&Í@ê§]@Ðr‰‘¥ ó¬@nîÌ[ „rùº>)éùŒ¼ÿñ*4tódÇŠýð«íDv[¶¼ë%ÙÏ3Ë›3òZD"îô®ÏI¹û5Í‚°Äð 'ÇnFÎèCÂæº™°›ì-ÌÏ Á§ÂgB¹ý«;…ëì† ý{Ü“)ŒT÷SÝϪÜÏØô(DhÚJ¤³ñV¸6ë.|¸# û~V’—¤XÜZ2Š“#Ò21i€#[L;Ã~ÑJr¹o"ôÖ[JdÑýQPnì.´½ Ú9 Ó[g3Û‰œ”ËŸ@´½¶ƒ‘eŽÛÐᮿpÚ½Yè´ò£0bp N÷y',ÝR ‹°µ Òøýu4-öçäÙVwq%ÕDØáŸhüþÒU"åòÅüº†rí JòŸ——q²Á@NúýIÄ«º½„•½wâÔ ?áë7pR?V8-«v/(”»8Ôö!¹œÜ^ìƒÈcyBóœÁxÛñ‰0k€6nkÍÊí\rþFqòîŠ\t Ó‘Hu?Õý¬Êý4¹%¶Ž9²þ"X uÖX½ [îq2ô–’<„Ô»ñ|¿¯°dv Î8,l¶ù ;4¡†DæíõgY>Õ99êâG–þú3#5uã±êg[rù—xìDbâF&ïCðÉÂ9ñîÒIá²Øƒˆn¸T¨sI‰-ÚZù¢s$jìµbõV«Uáöáìá–þŒ”ËWãê9˜,ÞÏɹšÇQøR¸j@¦ô‰F9{cÂ÷ÝÂ’í]ßò‚‚|Ó`6Bcö2Rî¾ñ?kQqí'Ç ^w·þíõ´Ì}ú'¹r”^œ-e¤ÜþÒû‘ºÕF"o&˜âr¡PÝOu?«r?5NF©§Df[öDèÁ.šã=ð`çnNŽ»?ß ¶ wN› ÷ùÂè}Nè}Ò]X}z›×#š‘ÍtS¿È[q7Ð#UË‘¼¶=&£šK¤\~Óƒ‰(>¯ÍH>}ÆMMQ…n¹x ëõo½¡¶×{%ù¥î8ôùÝM"}M´pJÃ@x÷òHð!¶ ß– Àý##åòÕ­“…NGt$2}Y⟴æTÌÂk«(Nž™ä†lÓõÂçXœH aä‹o™04¿”OÊÝÝr1 Oçq2ë§;úEç Ëâ}phŒÈ>ë3¡ó«”‘rûû¹g7…p2×w?ó~óEAªû©îgUê§êëtZG|75“HÕ¹dÜ9±³8©:/œjŽKý•¤ê¼ïG¬S€T{¬ÐDÀø;œTËå?žšŒÙ¡ùN¤ê|Îï½8¼b3'Uçsö²¡Ãã9©:ßàz û-ääßæËi¾ó;I¤êüÚW†’Ý8©:o4=õô29ù·÷¿¼™Š{ãqRu>dÒ9ØÅŸR»ÿMR<èö‘û~u?Õýü_ꧪ™|ž»Ÿ“ûýç^iB¿^ 4žÂÈ“ÈÆÎ§äÛ®ÑHðäŒÜî˜õ•C„3·ïEãû8y³ÏfL¿ý^˜e¸'(ÈÌ#Ûá}2”“rùÿ|Ù‹º µAž ?„À‰¯¹®«ôÜ@6:‘ã;õ„÷ߦá`+aø»ó¨9ð4#Ç=ÍA÷KZ ÷ι‰%¾*I¹|}N_CN9f`œpn¾©„éñJ²†ÛQÜìeÅIãwqlso%é˜ögšI¤Ü}E6z‡¬å¤ëîL|/ö¸x1W¹pÃÅ8¼è,‘rûý[žEŒO%9vK.LÇœàäúü©î§ºŸÿÍý¼f½zq{9Ù `”—v ÿù“ O{-%ùØ/ã¾¶âdty¦ôŽÉ'íG›s8Ù9µ¼JË„ßêèaƒþ|áÛâ>îë.”†9 ss3FÊå_¶Q‚ˆŒŒ[pûê„ 'e+ñdp¡Ðçv12š› wf߯¹öA ²·ëUÔ0ÙÈI]ë(¸ÑQ"G=ÿήŒøÉI¹|›ºÜdz×ANäˆAwammÈI×Ì 4Þ!Ly€j—ÏttAäÝιú÷n6iãNÊÝ?suWGs2#}^ UhÑ»bÏZHdÊ“LÕNÊí¯~'|•¾DŽIêƒ%[ê ÕýT÷³*÷sïÙÑ8ù!Š“«£•ÙVaÓ)ÉðvÚ$tÚ²V÷Ó…³ì@½” òÕ¤–ðj‘ÉH/lFê HaÃâd$Ôq"åòÏ«{Ã*ìYÏì& P^!Ñt†’|Õý4æJáä•ñy8Õ¸ŽDÆÆm‡î á4ë=ÌmÆFŠ› ãwv åò=;zOzÄr²°ø<Îlx&¼=4íÚ?>¿·š½Þ óL®°e7Î3òó¨eêk Rî~£‰)ð¯—ÃÉÃ7wcðy.üþ+’½Ðù¤ ·›Õã¾uAÊ>öûÎ̯$qÒÑÿ +;×TAªû©îgUîçMœ‹÷ædy¼#|zMÖš§„]>xâdŸÓÂ%z"¤x§p¢‹)òWÚŽš#»ê —ÏŽBÚãJFÚ›nAðŒíœô»­@R‚©P.ÿü=—ཚ)ɹëJÐxKNMÈ@ßš¹w^(ÊÐRȆĻù…ËnkÁ£¸†pðçóÈé{“‘/ê_Å­±8)—/EO‰1Mõ%²OŸý¨ØÙAøðòxìÊ¿ÏÉ©NiÏ…E¼÷ ^2R?ûÞ Ñã¤Üýü6ñhîYÄÉ >1Xõõ’0ªÉ¬O·ÙûéŒÐÖÊíÏr«…H×"F…û©Í@ªû©îgUîçüù¶x÷Òž“í¶v†î#aâµQ¸å™%Œ^ª€FEÐî@kd­MRûÓàÞh#­tó¬±U¸¬éi„\««$Ý[ôAß”QŒ¬á†åÁB¹ü­OžEûY›9™zî·ó…:S°H³µD®œÜ“PWØrHóŒñâdµ‹ÉlRP¸‚|»Åcœ$Ò¯i:Ë4ÚÇI¹|w†DÀw‡½D®ëâƒó™°ÔÌ“žoádâ|°D#áÕÂ=˜af"‘í4­0ξ™PîþéÞ‘|‹“í¶Ã|î=áž“7 {A›‘#/@wV2'eŸWÅ 3¢ È·õNáÆ•CŒT÷SÝϪÜÏûf戼Q $÷ h‡ùC o èÕÝ8ÙT¿5NîõU’eÅs0eJ6#ÛêãyYš°üÒa”•fpÒ¨dô¾iJäý©xÐÔOI&šn@ýœ”ËËì(4Ü_q²àÂütєȈãOY`ßlN¶.Ëf?5o(ɨ£Ø#[‰‘uB‘Ncæ+.°Ó§î3òãõ`ü¹åR.Ÿ÷«±hìé ‘Ù3¬ð®G'á’XVD瓟|·ú énšÁì¯V(I›¹5p¤ÅFÊÝÏ(ߊj}srTê,¸÷Lø9,ÿ¬i%‘žÎá=ÌT(·ÿuÜè^ÉÈ'ÇYÄŽ•ç9©î§ºŸU©Ÿª/¿zfèÑ×ø©:ŸÖeþ¤ê|[Dìû3“‘ªócnèªÔ”HÕ¹þºNÐð¬T’ªs¹üÇ£ì[c‰Tß-:ÂÚé¶e¤ê|åöÕpéaRuåq-v-`äßæ{yC›H¤êüE£Ö¨µn!#Uçáþ›àü´-È¿½›eñ’“ªóŠÙ÷Y÷ñG8ù·ûûÎŽ„Õ°Vù·ïW÷SÝÏÿ¥~ªÚ>ÝËs»IäÑšs°¶‡ðò’<t:ÃÉÌW)˜ÿ[C"w,Ç‚9iœÔ±þ^ÓO_ßvÂú彄æfPí‹fÙtJ4ŽÇZœ«uR­H¹üáG0¦^=­àÔÊgŒlzsÏìº}QÂÿ“ŽpúÙ¸±”“ý+vBgÀ aje>;8Ú™‘ÇêÎBƒVí@Êå{5úæÎöS’¶.ÁÑv<'{ìÚ—_ù$qŠÆ4.7tAçnÙ¯{ rþ<ÊÝÑ5 ЏN™¿'}}­…+í<ÿ‡‚œê=ž±[)·ÿüê‰ÙÈɵ#p.BGøŸ>ªû©îçs?KEr˜•DFüîŽþ§:s†b©C#á³u¾ÖPØ¿ /Lb9©kÕ!— i(`t°'#¯õš…vÙ1B£8䎳”Hˉ×YLÁ}NÊå÷8TŒ.:9jýU$ºÎ.8+ÁÏ;@I¶=y —úqacoL»~G8"mj$º|9©I#ÅžE^§}œ”Ë·òäüäÙœì~ •£Ë….±ƒ°~•žD®œÛc—UròÊ6 µe+ȵéö3›“r÷“^‡ãÀ|;‰´Ñ Á³¡Ý…#pêÕNF6‘€ ×-…rûg¿·Fȇ¡ Òµã t›ÆHu?Õý¬ÊýÔ25Åã¯&™ö²fLÕúíèëµÚÂù›áÞ«'œþ»%ÜÝÜdÊm;D¥­`dõ­p9#L7L@˜³³Ðk 3µ¸)œ ­AÊå7\v3:ÜVE oÁ©vz>ì™×úW9ùªV Þ¯ù!œÐö_¿‹<„w?4„}ŽJÒà´Š~uÈ2½ì¤ÇrNÊåËèŸs#]‰4L‹Ây-…ë®j ßØlN¾M-e=AxÍd!êjT—ÈWÚø\¹œ“r÷Û´ÜŒû]%rhk|¯á$,>–‚#Ú)Éäð(Ø_\ÌI¹ýC¥å(/bä‹eÛ`k°I¨î§ºŸU¹ŸíFþ`c«Kdá’R·ë.'­ùÊ’´£„ÿ”^gqé”d­Óºí#¯ÔØ‚– {NÅqû® ˆŠ`œÌˆ¹ ÃNdÇ8ÌZh%‘rùçͺÎM8é°ð2ÖœÚ,Lî3u¾ ßì°ÁVE±°Ú;#Ì=Ò–‘ÇW;âšVŠp¹s ôkòZ8¬]bÒÛ€”Ë—½¼\ÚKäô0wØ)Ú Û(fbÞ#×¹Ãâ­¹óP]h¬!<äáŽÅÕAÊÝÝk9t.8Jd_7wxnqNèï ×j œlä9N¯<…rûsOï†[ïJYÿæTt U’ê~ªûY•û¹/÷8kö$‡“Ïö3»ŸþÂß-/3´õ òÆ‘Wl{ü*F6NÀÛ`?áI½t¼78¨ +Kp1Š“Ýg#ªs´ðÚ¦«Ìòþ¡ë¯bìw‹‘rù+žE\§Nz.“õò³ÐKWžçÿNö¯Žæ\”dM­yx:ã*#-S¶¢Åî Ââá×ñ|b¾‚|°-n-t%RöûÏ 6Í2–È€-ÛWK¨Û­:\w¤1r[ýVæU.œrôÞeKB§÷'Ð<(PIÊÝwY7ß{ØKä«ÇƒðºÜVó½ Lú9*ÉgçQ£WFÊí¯{7}:-â¤s-œï-T÷SÝϪÜÏ61ÌwBWN>ÈÛÆºwÚ¯$»VÓÅÅ.0Òû|WŒñ¬ 2òu^¸¾R’Û6ÆA?t+'£RÜjöÎ.ïŒ^ÇÈÜâhäÄÙ€´7¼‡¢ÁíœH¹ü=õ³Qþ¯žŸ’<¥¥0hkTû`ÂȆZ­Q–²W8øóDL‹v·ÊÆöüJéuÏ aó $òG2ëSì£ åòÕN®åé9Ù¦ñf~7IèÖåÚ @–ÕZû‰†Âa·w¡ÙWœ<1¦ÌQ!”» T·”HhkÌÑQ8ÞežiÆ0²žA(¿Ú&”Û?¢dj­çä?ÅvˆÈ+Q’ê~ªûY•ú©úš22”E†88‘ªó˜÷ÓxǤ꼎õ&NÞŠ™‹ ?›K¤\¾>=àûÐÍœ¼4" £jf 7ÔÀøí¿„é¹§Ø«Ä=žSó0¥Ï`]-Å÷rRîþÚÝÑ÷¯¿×ÿwäì~ø™©/ŒÙ]ˆ“-ÿ0²U‹ûH?÷Ü‘”Ûïuë-{£ÜÃÈoM]ðöB#ÿé󧺟ê~þ7÷sIŸ6ص¯.'óJZ Z¯t%™ás•56Q’õ“1_S)\y¡Z}©#‘+ž”³½Ó9©Ú AiS©spN^Ý#\¿õ›>݆“M“šáw«Œ”Ë×tÊNôN¸ÀIåÇ@è¯z |0?ž ‹ø¬$›ŸŒe·ŽRcÞÌÄÕwo9ék¢ƒö%½„²ŸÞÖ—H—š6¨3^S{â îß8¹|B "Ç8I¤ÜþÜi;ðBÙäAåqTTža¤ºŸê~Vå~êk›@wq}%y°† Ú—mt"ç2ÍÂp—D¶õÜF"©+Ò…¯ç&àÉÖU´‡…8×q''3Qt@_"ëÛ&!½¡?'–ÀŸ\¡\þQÓvãT7Nêûí‚nßÞÂþ'™Kôq%yÑ¢˜ÙGtd¤ó (”ÙÂK=#ðº[¤på¸]pX¤oá9T+w åòµX¸=çdVþ TÖ|$¬4Q2]ïhFj$ý`s†|>×3@´—вÃrü<ûG(ûwþÙg54$Ò÷ŸØ^ö™“QÇ᩽±DjÝ a+;qRn×ÑWp¼Ö}GòSÊIŒÔ9ÍIu?Õý¬ÊýÔÈn3ËüäémÐnåaIh›Y÷#Çk†â¨æ C\ÀwÚNyÐ6 oFQ’¡×§`÷ä–y¥i”hÜçäÈ6†p £ ›ŸðЫŒ”Ëï…z‹9y¬x;R·mÞ;[ 6 Î3òY ÜÚ뀼R¹Ú7ly0w?â{„9‘ÍÆ!ðB-‰t˜Ü ®ýJ9)ûûÁy ö̸ÄÉãSmá>=[è{¤bÊŒAÎ4˜„ šVÂÁ5²à9%…‘NmŽ£8²'eŸ/67F’Û3NšØë`Ä?×…é:,78‘–VŽ(ªa RnÿŽø¤ýi!‘•w\°ÒÈX¨î§ºŸU¹Ÿ=5ÛÃùa[FîßЉc† {ÜŠ7ïlA®úy²æqxíÆÉë7üÐ8U)|Qq˜%žS’>#ï³¢ùɌܗŸ‰úÏ\…Ë&§Â#+Ž“rù;÷ FxZ8'çõÜ„Í÷ã„Ï߬‚æhcZ§bqè®®°ë¬ØÌÕãä¤ÎQçí/ܵî[öÄ‘›7ÌC?z åò­›Ð w=9™ëÜß­…5?À¿— ÈÔ×{Щ©ÐõGbÒprCS[x†<ÊÝßV pr^Ÿ?¬¦[¦0ps<>X|üì ZnÈb¤Üþò˜“{']æ`-?MWê~ªûY•û9Í5¬bdÌ:@gÆV!&&a¦‡È=59Ö,Ðîü3çgq2²ß¿¾zÌöqÂæuMAÖòÃÞ0SáöîÁKJ8¹í`=\ÊÙ¥ åòo{½õ&pr½ó¤…ìîõÏ…bî FŽn{î~WlBs£TNý:K¿Ä 'Gʼnzጴ08 ò­œ”Ë×åq=ëíQ’;%M,s"ÍÆ(1sq-ý>—`ÀˆlF^œýŠOÜOÞ®×w0Rî~«ìN×Nƽ`)Ø$¬·¼Û´,„N†‡áÓ¢©DÊí¿s½ìŸ02W1%iíAªû©îgUê§êë[F(Å1Ruþ—2ê#Uç››#uÕë|RunýGBëÃ÷©:o|fü>0Ru.—ÿ¾ÆtÌÿÁIÕù³’b<ø2TIªÎÛh»àÇõ¹œT¯8¶KúÖÈ¿ÍWX³>™Öe¤ê¼Dë6= ©:ß·mö Ðù·÷ë”ßc³›Nç¤ê||À<<Ñ]"ÿvA­dtkiòo߯ŸÿKýTuË8 Z¢'‘—«iaí{ ¡ç‡Ö7Ћ“7>mbOjä“9ËF`M­Œl:s5f½¶îs+Ê µëÜAnÖRN®=±¾¶yþÔ-$ 6ã¤\þÝÝO¢h­’“µ Žàk¿×Âfóüqêü¿5Û7 ±_ ‡öyDz´®0r‚±¼‹šƒÔæ3é—¨$/ô°Ùð_Œ”Ë—¹è¸ìŒæ$»äŽW{· ·ømd?7Ìw"—)¢X¦ÿ*Fêî}ÆÙýæd¿Ã¡,ÀÕŽ‘r÷{]\‚^å;9ùtþ|ÄL‹nýžˆ®Ý; —^§‰»„rû‡Ý/ÀØ?ŒlЫ Ç£·(ÈÿôùSÝOu?ÿ›ûù%ü#»yä1''·¹ÁFè(…ç£Yü£yŒ40¾Á S ç,þaÂÖ‰Ðs²Îïsý+›KäïnËaôÉI¨¨¾‰F6B·™ÌÕÍ•“rùëOÞZš9Ü/†# gn²Ä•ÇùœìöHÛÇŒFŒöGÛíAvHÉÀ¢Mu…k+Sq=ý%#ëô)€~ØU%)—oøÄ±XyÙ“•EQ¸b¦]½ÂŽ<ÆÈ«7  u½9ÈÄJàž«¥ðØåÃx3·±PîþZ³ÙøÑx;'WiLÃ7½ahi¨Ø—(ìwËÏ.ØåöÛ¸€Ê¤Óœ .ŠCN# ‰T÷SÝϪÜÏg{¢Â875u:@èniˆ¡mŒ@.vuèw6‡öaþI?'rqX2vüjÉI‡àÚ˜ë¬+‘±CÙ:3N¶w3E£ïº µæâ˹JFÊ忦 F ž±D^i³ûê´Þë_ ñ.“óÉú-4¡¥èÇÈévçð´_˜¸Œ[ŽòÉ•§‚q n5‰ÌmÕ‘ý"9)—ï{zhæXsrW°=êOx¯$? D`„ȳc7 † ^jS†xÜÉ'»¶ËDÍgÍ$Rî~Ó:QoìNNrƒå…÷ZµEY;OéÓÙ›N„0RnÿrŸ¾8æl!‘É-n0kͳœT÷SÝϪÜOwñì°ë %y«M «6Í0Ÿœï‡ãî ]­’°·‡¹0/0WÚDp²qõpšyXØ`ê~ö§n#¯·i‡…ýÚ€œÐ³]µ¶q²âéøKM%R.¿~ë©Øek$‘õƒ\þõ;HOXq©-V¾>ÎHÍ⸢û[X+õ¢‡íã¤ô*zkI¤ÝäÆÈñœÊÈâŽpxÞc¡\¾™§m0~Â%é²Å&MœÈà²x Ž·©h~ ÿôh$ü4×n3M%Òß6‘íÓú¬$åî&Gйuœ¼Økb<<„æ ÖÁåÜ)Fn9µ×£…rû‹îÆ0‡CN Ò ê+‹¾ý’‘ê~ªûY•ûY¿U+›wPAj?If5÷wgdr²#×Éz_‚{f#~ ¯k‰œ\cùGÂð¼ gÙlq]ûU~~… ·V’óÛ¸¢«¯&HÙïϹæhc[C"»¤6DÍœtØæ‹”: íþIÄöÊ÷ŒÔâÔeÆé1º+ZÙj g!q\kFεÓL8)—Ï¥ÜJcä‘RŒì©ÃÈôeQ»ì²pÙÝRd¤ê|ÍŒKŸÊIÕù’–Ãàñç(#Uço:îFÊ&S‰TŸY6 ÷½æäÿ¹/“ÿûØëÌ­\‹“ªóÓ ñkpRužõ‘9ü³Ž‘ªó&q0?y #ÿ6_°ß$x•1Ru~p×YœûVÎIÕyrëõpÔo%‘{?e­ZpRuÞÎÂZÝ8ù·ûä ¯ûoNþíûÕýT÷󩟪²6Ú2MI. ^ÇÉAG XïQŒ‘}aE$a̳@¬º&lz6Ÿ\†+½²°jáCNÖöCïÁúé¿ùNí•äÅQ[P6ê'åòÏÈ¿ŠÍf]9¹¦K ¶¬‰:·.foG oßÏ"‡ìÏ'7—ׄåéÚ ·MQ6BV2î_õ„K›Eá)Œ”Ë7œ›ã”ƒ’“B Ð6X8`R«Ñå#«å×Ã݆ KõÐýüVFÄ&xo9,”ý~ß¼©îÚ’°YVWèW Œ—­…ò YÓùœ”Ûo³ë cÛqÒ³[,:ç ÿÓçOu?ÕýüoîgïT¤ÖöS/Þëc•סýÌ6°ùPä¹¶#0ò›¡°[ñœÛ°UA.Ûƒg+“•ä[ÜïSÎIó‘Xç.½„v›£$dšðÝív˜ñ'€‘rù½ÃOc õuNú*ò° KM‰ÜÚý<ë>Ñ—‘.»ôPýd¥°¯F4>„Xœö¸–3« [9 Ÿ?9YkŠ.¼û,”Ëç­C­8iïZþ½•dj<ð¹%È…«—î Ì=½iIæŒl=2ÃÞ*I¹ûGC|ñ{·®D¶{±ñ+õ„5Ìv1mFμÙOtAÊíŸâ¸~I—99ª;ðÝv§PÝOu?«r?/7l ;­®Œ xÓ§ÿÌÖìì‹çç›CØ,×6®Ý¦?™“ oÁ¬Ç1BýÃÕpc¤#×Zc¹Ù9¡k“ptp}"üUë$,tU’rùߦaÊwC‰Lª‹¸í…3¹!·Zs>Š-xsÞD8hP)bÍ­ù¥àfýëwéaÙ’‰®°Ö-;´NŽd¤\¾—‡5ᵯ§‚ôöÑEûßÍ™ÿ+ –_­@ö<„…ÕšsïB't']ƒ¯7 åî‹ò@ëz¹nàl$®ÓF'oÁ ˳O`þÂ[Œ”ÛªMüv?¦$ÛF˜cWF Fªû©îgUîgÎ@sh> `¤þ/[ü¸'´2ËBDÓgÂŽf'`Ò$\8hÝüòOâäé½ñì­·°WÖÜ‹þÄÈȼø—I£AÐ~¢%‘z7ß±ÕÙ8)—´S Ž·”È)Ë0ÍÔFxîÞ><X¤‹Ï tÖØÃÈ–'¡“TG"}ßG@ÿy7áê%!¨ít„‘w×§aelo)—¯u,Ëú‡‘aC-±æÝn¡ÕÈsx²á¶ðh›0l9YøÈËý§)ÉÕzâed?FÊÝ_¥5‰ÿúôÿ=þÆ õk C*¯àÍ*C%Ú#Ç_pRnÿ°Û®(peäTx#þ`ªPÝOu?«r?žé£ ŒT,Œö•ùÂáû‹ {ì¡‚Œ2+‚ó·x%ir·Ö*Ê„Î=M±¯Ën©ex7Sõùkj6zîú¢$皢íãŒü=5aíë‚”Ëïðvž,ï,‘ƒ¬ªÛFh6òþrœ‘å3&aý…+£ J‘ÚÚ Ÿ _uµ./à¤ë¦µ¨•œÌÈÇGðqÅt¡Ü}£ê}`·@S";sÄj5…Ͼ­Gwkca­Fxqå)'åöª“ûüYv; ÷Bß*Hu?Õý¬Êýü3ᇠÙr¬;îœ+¼—ÐrÊzNµ>ˆ{‰g…»¶ÙãGüFF–^˜Ž£YBMÏ]x—ÀÉ´)>pO*F-¹ŒñOV ¯Ks`xÚZ"åòÏÔmˆ†Ãõ$2ín9ó?þ‘“‡£NjpB«XÙÔHz¡lÒÑœ4‹ÞÊî,udä„N6X s%ixÑ·’<)—ï•× 4¬¸ÅÈb]].ìú¯ÿ¯RN®¹}ÓH¤sÓt8D(Éè';yˆ“r÷]ëZAáõ‹“kZ´CÃÑ„ÎKKY Ý&%ù"I¹÷0Rn'çd”¬Y©$koŒFˆñ8Nªû©îgUê§ê Í—££õEFªÎC bõò'Uçòƒpò|1#Uç‹×»Âðe'Uçv“CÙÉ>”¤ê\.\äiVÐþ'UçsC¼ÐßOK"UçÓ-ß³œ½5@ªÎ õ‚ѾI$#ÿ6_‚ß6ܘQÀHÕùŽÃ¨`Œï 9©:ïðj ×ù·û›5óÇáŠNþíûÕýT÷󩟪^7kˆeCßsòI€âÏ_VD4B€¾#'ZÃíèQá}Ã]p[¶Xxóc  2䤸['ˆ“çxã·‚ÐóãA\×J"m"?1#o ¡\þöãá‘fÎÉþa18ć Oî W‡¡ƒ[[–Oþ0°GâÏŒ|V9 ® ‡Û†: O.-Dî´0)—oØ×&ø9V_"Ýn–3Åœçœ\m¬……oj\êÇ ÖÂøFµ0|í$FŽªéŽKÇt@ÊÝ×uŽíîy?É®7l…»>¬ÀàÏÇ8ÙÀ¿;¬º† åößY}=’Ú(Èà;û°«Àž“ÿé󧺟ê~þ7÷3Õó3‰ÙÏÉó /3§E„å%#0²f)#ëeþƒ­žÖLFa°’|¹7!rÒ©ú¤~óÆE;"-FO¸påE¶çÍiFÆ}Å÷à6 åòŸ®‰.ï—q2óVRs6 ÇXéÃÍ}™’Ü]G+K~*ȶ÷ì!#Ã4SqÙø°ÐJ+­»>àäÕûޏÁŽ eŸ4+`?Œb9ù³û^Ö¡m#áuoO´^Ó ä—ß; UËL¨—‹aÇ.3RQP‚…1Æœ”»_8њ˺HdFX{¼ýÐ^^Í·rº(ÉìÃ-¡Ûl##åöߎô:yœÌw\€11·…ê~ªûY•ûi¶Ab¶ -9¹¯kóÖÛ«$Ógãò:#§Æ"·0[xnb0¶>ÙÊÉß]WâNY”ðš•5êýt"Ç{2 |Ù•‘ßK.£Ñ¼8%ÙÏh3Âb %R.ÿî&Ahd³ƒ“?ù¡a¬ðš¶)êWb䢂þhÓâpøšcðŸÖIxÕꦬHT’ín4Á¨Í[ä•)Óá`›ÃH¹| &1ËZ™N¤CT‘êÄÈ›¹éX°¨>Hýç'·÷#ºE#ðMK‰tôÖçyu…r÷³Æ¡ù˜Vùì¼Br …#|§áø³rFºUÆA7ä…Pn¿»qGì}ŸÀÉÓÁ5Ñ2b¦’T÷SÝϪÜÏk3˜å'2+è Û銂´öIEZ¸#êfÀæ¡®ðÛÈø3З“ÏììkÚ\øsàdÉHMÓMöp¿P÷ØWÖ©ó NÎcÓn)—_{ü¿~g5Làd~K‘ó4^Øó”öÿ©òªU ÷~fäå–‡Q•Êɵ{£¶5$Ò¿2«¯bä¡ɘÕt‘’”Ë9ü›6e#‡¬¯)i¯„yðîÖ8á톗1åhs'2ïw8«½f·‚ìß« > jRî¾ã TkÜ@"s¿¾gWk u·œÀÒP'Fþ‰*@ßiœ”Û£WøØû1òê”Áø2é_ŸóÿWÝOu?«r?3ç±Qïû2rRƒsÌÿt¨°ÏDµúyNä‹çót|oŽj^ Ž’wk[cZ›NŒL³ Å}7 Óó3ÐlÔA9£A"Ò²3²ç½ Ïþ¦$åòŸ(ž »I1œ¼5flnÖÏF¤×QF¾»_€oÅæB›ñ‹po’¾Df¥˜a–]%'{žFJ×­ÂÄÔaHšè+”Ë×|s;´:WBÃÁhc¡[b4Ó““³FJp9›+ 0ÎBî© ¯é߇÷ÌIGI¹û:¥ìÑÈ N>Z~–yÖ¹#ì{cZ¦kI䬃a˜ßD(·ßjõvœN®²"@‰ÑéŒT÷SÝϪÜÏÎ0]3%#ãçüd#;>*­RàZœ4 ŠEÓé…?ÛÁþùñŒÔ¨\}‡BáÏLhvjÆÉÒŽq°3È-öÁϺYÂÝ íQô½¡Pöói0]lädŸ€¨ðq–¥aÄØ%y3J°© œ H{Äì]/ÊaÕîX2Rnÿ¡%Hʨ¥$S•ùˆÊ½ÉIu?Õý¬JýT}•·l‡Õ5AªÎ³u¹1–“ªó“§vâZÿÃŒT4X…I:¯9©:Íè‰]ì©:—Ë¿nöT8qRuÞ2z/¦ ÖHÕyëyØÓ %#Uç=gÄcEóŒüÛ|<ã14R¤êÜæ€ÂkH¤êü•O {㓨$ÿö~‘Ù.¦ç»™“ÿ'_RÌ­Þäßî_l û¶ù·ïW÷SÝÏÿ¥~þŸï÷ñï˜ÅäíJòÀœ÷¬ÚºzŽä7? ØÿÈUÇöØÃÜÆ’‘úìÆ m»…cNg` Auaá¡l\j\žOžºk³‚8ñOôº_†¢ìB¹ü ÁÓý'KZ€K']‰ÌzYÄ~ºžáä‹s;ذµ”äÇ¥X=ð#Ó‹CñÛ€ w\´œ Û׿0)Ì`¤\¾áõ&c}x7‰Œ±²ÆžÍæÂ•? zè#W5sF…©.ÈÀ?‡Yó1JFs]Gr÷çΰ‚öéGœt^Òm‡^&&9è½…DZ\>ξeÄrRnÿŽÅze,#­V_††ËNþÇÏŸê~ªûù_Üϊß™}b¤‚쾺*JõYQ·?¼J -,& ßØ`áÛtô™ª$% 9¦s²Õú­hÝð¥ðÏ"78(Ê…Î3­a^í­’œPÇ ]‡û1R.ÿͱ0Úh,‘ÛfÁv”©ð@ó46vÙFš{j nþWáŽú»a¾}½Ðªé~–(ÈÎ5òç´“:…|ÊåÓºU£‚ô%’ ºÌZtxÄIË­KáqÎäÖ7¡Øæ¨'ìZŒï ydìEúÎI¹ûSÌZcûÙSœ 4BÞ aÒ­#lÀo7F꼴İ>z åöïluÉÚIdó—Ó°-ÎV¨î§ºŸU¹ŸŸ§jbåúŒì5\y ü…?²Ã/o§ðõqä–E }‚¶Â¡ Š“W"Wâ÷8á ïz(³UøºÙ=f¿m™‚bîcÎ3òÖw :}6+I¹üî•ÿ`ÉS‰ n7Óº™_•öFH‹6 ?ÞóǬ•í…óÍöáEçÁJR_7ß~Ys²ÿŒ¦Èù]®$g&1Ô·öe¤\¾I‰ ,pA'Í^lb:î×”dËÊd<šVÉȹ÷ãE~¶°Üc3&t7“ÈmKk!·áiNÊݳ¿¾}Kää1]¬ý"|2y;ZN3©é!!sD,#åö{[¾`.%Ÿ9iScûtþ`>©î§ºŸU¹Ÿ–õL°or"#»8vĽâ\¡þ•@Û† CZ„#ÏÉ[8ÿæ(ŒkéËIm†tû6ÂÔžzp^p‘ã:ÍÁ§Ï& »èoÆ©L]‰Üýá ‹|9•“rù_ívÂìzy" 5îv¨&üÕ{?~ÞÐY{Còëe0òtßÈ,óåä.]?˜ôÞ)Ì Ù ËÞñŒ¼§“„¨À' R¶O6²mWRä§ Ñ,2Ú‘CÃõnoaö™“ðW^s"ƒõ_³¬ðnŒ´½6=‹ª”»¿ ƒ6¾¦xq2+D÷}G _:×w7”äůÉxsé2'åö§¹_c¯¼däÑΣñ¶†Hu?Õý¬ÊýÔïŽÚçÉÏôã7·„Ïíã0íÓáô„$ðþµ„Õ†ZáÞ¨ù¤ÛQ¸6ù¨ GÜÝ‹±qº g-º„'G×0²Â½šêèô}‘NÕ…rù›•hCzz•“Éÿ²gã…Ú‹. s[’‚<Ô .öœ\é;ý¦† ßÕ‚y+…³¢pé‹…ðíoºÊåÓì[Ä Ëî02»{Md%è‚|äÑk¤ÉÉ­+rP»ûfáì É(Þ–ËȰüCˆ×y›OÊÝ_x¼ Žuá¤ÙѬ)Õ{Z<,zè[âšO”Pn¿iÀ~ô+nR?çNelc¤ºŸê~Vå~žñƒ¢‘?ÍÃû>敤¢ú*ÈÉÖûà{¥³’œ-õEËýÙCkWäw5¾Žñ«Ü8y}b6¼KäËc%˜»²„“{vÇ«û–)—ÿ‰Ã#æúÙ“ç/ßdÏ¢s•dið!<[ÈɨQp˜®!‘–wCy CN~fŽçóIkíàª;“ãKL`ž°ž‘rù* ,”dr¼ÉX(é.l×2Ë]ó9¹·÷¬-½-L¾…Ç!q¸žs0tç¡ÜýÚ«4ð{ÕI%ù®šÆ´q–¬k†úoóÉÌV60IÆH¹ý>]ncÌóᜬû= áûšH¤ºŸê~V¥~ª¾ŽwX‰o·ž3Ru>lp*ìÓësRuT ©z>#UçÕ³—ÀX·»DªÎ­ç2× ÚŒTËåbx›¥68áDªÎ+þÁ·Æu$Ruþ£¯†ìªÇHÕù"­-X½\äßæ»oë‹8KHչǩ Шþ“ªóòœîÈß“{å ¬ôØ™OªÎ¿]Qµv3òo÷§ZˆKYVù·ïW÷SÝÏÿ¥~ªj3ÂõæýædZÖð8ûNx«™eÖ]„Y!6¹XIúW[Ù}´@6«‘„KÓn3²³õqØžnw¸Í·aùä³FÁ(ÙÕdêæR\ú‘ëDÊþ>šw š5O9‘†±—pïÁe%éîâ'q1œÌë0ö[…;´9[UÆùæ};̪Ódä?±lÂ$CN>35Cž¾H¹|Ú¢ðÕ¯šDZT Ĺ¾pœ]møý d¤±²-Ò4Ï ólêàù躜´ 뀚ÆþŒ”»ÿúÍz'Çq²^µuÈ>´Kh²I¶ÂB·0èJÙB¹ý¯O£¬à#kÕ¹‹3'j*ÈÿôùSÝOu?ÿ›ûyÿHKØäÝædå ]ô]' »fš#þÁŠ|²h|[')H{ç#»Ú—‘-öæ¢IJ²á‰“¸õõ'ç ñ+‰ôIÜŽ&§í…‰‰ìÔ圔Ëëuæ&s9éÜà 󲄋ƒØR2IئuT÷y«$Í6nÀÑÓ¶ çþÈÀ$7aÍL¤´øÂÈcebÌI¹|ž{àÄ+=‰|ÕgÒê w_ErF6yºW~>¹-jJ¾Ÿâð½Çt¡ÜýýU¸Ó#ž“ —!Üùß–4› Çß%Âøp#xðÊíï8±«FÞå¤ýæmX¾ÆB"ÕýT÷³*÷s@‘š{ÅsòÜõ·ì½û*¡·^äo³gä|' Ú$Üó, ½8¹aIË3„QíPëTk‰%’“ÛO±3k[ KSa³%œ‘º;2àÓ×^8fæ:ÜI9ÀÉðD[8Å®ÊÝ~&rnÆp24y úzE Û¼ÚMA?‘€üÛŒ”Ûo¦y“•»™2²ÕœŽ¸´ã•PÝOu?«r?ÝÚžaÞcòÈíÑ…,p÷A9ø©?ôCï1Ò¢z4NJ—…ƒ~9á`—¶œL©kƒ–=µ”äcƒÍø–èRñê¦T¯%t®_‹›·S^g£vÁiFÊå?>ØeaV™µ{¶fvj_óƒ­ãF*‚PkûCa/ŸeØÓWO"Û­³ALÂSNæ" ig0rÕ®4üü`¬$åòÍaÕÜ%+c޲I Í©uû ú¯SC®e`E/%é_ÑkÛQ v Dø›·Œ”»?|ýx´ñŠàdå”QXye›ðà¡ëÈmq#Ÿ´¥De^u‰”Ûí¦šÎ© rè­|oǩŸU¹ŸQ™Å¬³adââÛ,¼¶ŸÐ´q~÷I¾9ŠG÷' Û.í ¯ñçäÎ ½qÂc6#+Ç”¢aL´‚ô©{Oœädå ¤õÁÈ:ŽIP¬)S’rùmVvGÔ\3‰ôÚmˆq…CïçâhÏFëw65¹‚4®ùŽ 8ioYF;‘?]vÀañINšÞìŒ2;¥P.ßÈ#WYŸœÝŒ,«SF3Þ“þ»öÕÖ­ Š9"&‚PèùbΘPQ1'Ìn#"•,I²ˆ€¢¨ô\˜PP ¨ ˆ9gÌé?çbÌ¿ªÏÅ*kß|Õëæ©rìãEߪ^–{R *7áä[çXt¹4U˜×$µ§U2òâÄËÐ8µþ)w?mè¼^çËIíþÑ»ÌKø¡ßLúÚS"o¸~`·Š« eÿþg} –¯‹ä§âCh\܆“ê~ªûY•ú©úlÊ}Å\ö¥3Ruž;ï4ülRªó¸`¢N"#Uçn%aèëÚQ"Uç¡;P°,–“ªs¹üï{a6­~sRuîsÞí:rRu1¨,ÏÏ`¤êüBØ[6·{WFþm¾Ô¶ØðµHÕù›'!x¸““ªóGSÑúWc‰üÛû½Æ “Ã[9©:Öt+[n¦iGþõÏ×8 ÏÛGpòo?¯î§ºŸÿMýTÕ-X;Ö^ä¤û¼Lãt´°]ül}EØÿŸ‰x6¹Ph9òë{p#÷×Á­†„}Ǹ µ© È f°;Ñ^è]x޽=–“Z3¬0E÷#åòÝ— ×'ÎJr½A":ë '9†[Iäøé¾XX`+lsÄ#¾ròØ·7lùalÆmÔuÞ(Üô?߃®ëí$R.ߩ轸¸~ 'ëfû"È'P¸Ò£Òµ{+ÉA[ hÐ9¹¦4¦S•ä«!(j³Ÿ“r÷‡½Â¦F%2Ë6 ãJ[×-B÷¨[ŒôÒ¿ƒiSksRnð˜ÈkÒ äõ¡Qèwñ #ÿíû§ºŸê~þ'÷ó›^);±{'—­»Âôš ›þ²Â®‡…Ëë`g³-“—ãéçÖ £ßÄbµ³†0°ä6²'­cäY§[Xå¹…“S¤ ¬æ!Fn¹‘в¶m9)—ߥ~¦¿hÌÉŠÑX÷ÈJè÷z.¾¶’ȶißYÉR-¡£ÉIæ9=ÏŽ¬xø€ÕŒ‘Ÿ+Xã9Žœ´HoƒþÁš åòÅ|òÄíќ¬;ÉÆK’…šéáÅ'FæhöD§ñ!ÂÙ{Qº£˜“š5mY_(wÿDß=ð?ÞZ"[5ôÁ›(]á²c‡aÔ[[˜qØúKM…rû-ãaº##7·Ì†Y@†’T÷SÝϪÜOËՅ̳å<%9êÕ–«ûÌŽ´¾\éý*•¤Q÷lé¶> Ò©ü$’z0rÃ>޾ •äšÑihm¦/‘³Óg​¹p¬ÇÔnÃI³©ý°D1D(—_Ç?®˜ÀÉ=cCÐØr¡fD$ûRé.lšëà =÷+È÷qæØ ÝägÛq¸¥ðÕ«$Ü×ÿÉȇñ’"O)I¹|Ñ—á@ǃœœør: \R„cšŽEœO#ìaÜ™ãÂlÿÆ(ÊÞXàUú åî['íÀE‰¬ÓÖÃÆwÖuÇ:¬•89xD!Ó˜')H¹ý[œ÷ùåNÆÌñÄÖAG„ê~ªûY•ûÙÈ«”Ù)ï+ÈmåÏÙ¼@0²ò÷o–Ýc®°4¸&T\NÈLƒÉµmœÌë±ujJÂBÏŸ,5á‘p]ïcì]³{9äŸ]‘^ç§‚ì´Üê)#åò?Ê„“á6NÚýðÁȾB‹™,îëYF¦‡t€›V+1³°r¯¶pY“ø\ÝËÈûçwÃÐû '7YõŪ曄rù>9@³?Qœœhh‹;uw ;m…ïaFæ<Ü ÷9±ÂQšÅX0|›ð妋˜Ò°œ“r÷k¼pƒ{ÓŽYsã\¬:ÜA¨Òz¿î3Rwú6ôiP ¤Üþ„޳pË2‚“^ûã ÿ¡ºŸê~Vå~ÞuüÍ&madLr=´¿¹_øþ„ Š·ÖytÕ´iÛVØ*h5ý~Ïû]T’í­òÑ/ì 'çµ@ÈÔš 2·ÇX­ØÆH¹|kÆcd‡™œãçfÏIÓÝ~æºW(·ÿAó>ˆÎÝÈÈQÖ pkСºŸê~V¥~ª>16Ðt{ÊHÕy§9gÐxò5FªÎGV·Â!‡4©:ïÙì(’žŸÍ!Uç{S`—aªs¹ü'»Àµq'UçnÕó±ýUŠ’T÷þRϲµ$Ru~¯~<&}ÆÉ¿Í—v²nd6g¤êüÓÓ8›Ö[hZï œôj\>é:îŒOã¤\¾˜>¨y¨œ“¡ÉлY&œ]j„üý„Õ:áÈŒ JrÇ˳ˆ´¸ËHéB ¼ô·sRîþ ç-xÛÕN"õZ®†v†­0æÌV,ÁH¿ßaØãá)”Û?#ݶ4cdúì±XòrƒPÝOu?«r?ÇMÇŒ¢gœ,y;ƒÜ çwì ­Üy~˜ÞÍyÀÉù©Mð¥q “¯/AØ%aýßjÜ?ÈÈn?òà=µµ’tr½ÃtôeäÕÛ^ØY¿+H¹üóÞÞ@ˆÝTN -DSëtáM T®Šš-€ë¨½ÂHo ”V’Ís» óàz ²åê™XqÜR"¿šg²[î*I¹|·¦/ĵiEœ4: E)ÙÂìÝ]0xì};²éÿþ*‹ä¶ >ÿóûj'‘s”7ÙŒ /8)w½ß\¬¾×["÷ „YÜZèxùz¨ ' α5&œ”ÛßÚwnÆ1²ý+?äM8(T÷SÝϪÜOÓ†Ïö·9ù6¼fhç s†æ³Ð:žÂh«xöÀÁBIvë‡334AöÁ‘k´‘ûÒm”ÌÉ»±¹^…0bî]¸4Q½šì´l$R.¿Y·ÓØòŒ“óÁ©º’ð§Ü¶qòµ{a¬Pg`W|Ó¶eäãSý°'é€ð×Nk8=®²Ö¢¸=>ÇH¹|-]†`÷à}œìõB›#þ~h‡(w#FòŃÁz9 ¿¤²²²íÂDÃ9(~Þ¤Üýñ3‡ p¼…Dæ|´eº©pÄ•}êøŒ“Þý‘º¼¾DÊí_\š‚9«9êÌIØ{mµ%ÕýT÷³*÷Ór…J?gq²±{Ø×Þ'Ô¿™Ââç a¤YùVÇó–ðYb.š?ÔU’%‡àçìÅÉ^šSÐËŽ WWëz§ãš§°Ï×wÞ5Äð†@Êå¾/u’ZKäÇw¿£ƒðMº nx~T’}JtánaKZF.AeÔ#F®ôØ ×ð×Â_û2ЭµŸ’´ˆñ‡ÍÎ=œ”Ëç×ËOŽ*8¹îš9ž<ý¥$S/Œ‡ç'FþuÁ`ƒ@aଓèÕí¼PïQ.½Ôç¤ÜýØV]±£´“Dnz­ÍGí„Ç–¿a^;ösòwìaö CŸ‘rûõã³0§Y'{÷ ĸk $RÝOu?«r?=ºÔ‚—þ&NúÆ}bŸÚ —jƒâÕ­A^ š€]]¬…³Möb Û)N:ÿ\Þës„ÔݱõÁ8%YýœfÛV±+Ž nP>#Û¶<Œ?g–pR.Óú[°Ü¾“DVΜ Þ@_X:[Ù' ù{zw,­³_X|Ó\K;]€Æ‡— Ò]4'ûsòÎΨµú¾’”ËÒÞ öu¶+ɽ>fÈbšCFº­ÄóÑŒ pÝŠÎʼnÂ3ïý[\ÊÉ2ãÐLÊÝWìhýòæùçgmô»SW˜¯Ù[Ä Ø4³Êí/Ä:7‘ÈØÃ—Ù”öAœT÷SÝϪÔOÕgÈÇÌvÛC%©:oîäõ¿l@ªÎåŒÃú~œTZ;£+&0Ruþõö ô7?ÆIÕ¹\þ”΃¶¾¹DªÎGxCc›§ŒTçŒÌGEì NªÎûèãkµ`Fþm¾úá¸4ÓHAªÎ]ÆùâêÃFªÎã´Ìñýa¨’üÛû?Ï}gsžþá¤êÜ6ªyO1òo÷ë~ˆg†+Lù·ŸW÷SÝÏÿ¦~þŸïÇ›>Xj%‘Ï{{àQ¤µp—tóÛ{1ò[Ôe ö W».ä áãwF^zÖÖºš‹ÅnO89þ7>mÖ—Hnuõ›­Qævû_ %‘rùMš‹M¾ï¹°éFL:úGxÞÚ«FÆ}› ßÑí@Nš¿;¾`äÓ2l=yTA"w7ܧ02òâ1,móXAÊå+ß~ %s§22ï CûÔRk7ìä;IœôÐôƒÖÀ,áA½Ûxô£µÐß>aW,%Rî~ÝRO¬&‘§K¶ Á) ¡Ý óhx=‰“fí‚ðÄÓD"åö×{SÕ’¤±s ¦Û¢$ÿíû§ºŸê~þ'÷svÌ\<ú§‡D.,£ÓL…fzň­Ñ‚“KÚ^Ó;)¼>æÆýÎŽ7³;H¤Ÿ5F;UnÎ¬å£Æq²rÚ}Ñç¬ðò䬢Fmrù+Vìº%ÕAæ- vÑFV‡`š‡HEÅI žô“‘}mŠáåÏÉ•‡Ò1¶c}‰|s6Ïf–sr‘ :è4’H¹|™SnÁ° %'+.cè”há³Â-p|XØêår^–(4îð“ý@]‰Ô»ÎŽ]6e¤ÜýSïþÁj»ßœÌõvÜ-ß„óõaøTW"{5ñe+™qRnÿÌnñ87k3'«_óÁ`ƒ;Bu?Õý¬Êý¬–c…e[ $2®Hñš õjdaýÌÂÇ·B‘3ª«0oúRÜÈRû^Ö„îòÆÂ¨ÕõмÍmyþ}/œÐ eä¼ÎÇÑtKKy{.ãÒþDNÊåo³<î'Þ2RûFžº\ºÍ½ŽÂO«„AeØ_¯$-Ó¶¡¯‹DÞȵCWµ…ÙÃØÑ¾­yÎmÆ[(@Êåë2ÿöý~ÂÉ/¹‡”Þ@"Ÿ5 Íß{93ÆR™‡°þÎQp˜×¤¶îaÄÜg¤Üý> P¹ä#'ûÖ˜…Æí^ -ƒ8k¾à#‡Û8"à‚5H¹ýÕ­ÇàE«œLŒªá:{…ê~ªûY•û™ì]šU—ÈÍG¾²Ž%w8ySÓ óõ”H§°š`,\3:œíôµãäáÀ öjò*FÆY„G÷2„§Ê½°}\„°àëhüª¦#‘~þ?ØàÌßv¤\þƒ•9èÒ9‘cºå![cƒ0)¢~p2îÈ!¤ýì ‘«æÔClb'7\úÃÜÞvS’K&– lO #<9Žô5%R.ß +á¸2¹DÆì@÷ßÂúÞƒ6b'¯ÝR`ʬÎB{Ç\ü¥$3†âú NÊö§Ò mr²lÞ8Ä™” —DdÀóQ Ó^\ÅÒÖšŒ”Û¿.øs®qDAº–7ÀÃÍ¿©î§ºŸU¹Ÿ‹ëÜc]328Y°¡€í¿·EøüãgöÏ>M‰LwKd7ý£9¹a‹.jêÕ9ªûFœ9¦'ý>óÿ,dd¡_‚ïÖVŸ .<­ÅD¿B¹üM_À­Q=Fz¾‚¥§Ú‘NnC?&‘/?Z"~r7áð÷ÍÑi›5#¿N´C|—(á̶=][" Ý~³5Úž R.ß¹ysû¹D~Ëí‹gKê §|êJáðú]…Ÿ›Î†kX<'Ÿuâ& åî/Žï.sàò¾³ÂÃýOáÎðdá©]~à‘µ%Rnÿ£òu¸u®ÈUGNàLzc¡ºŸê~Vå~<Å4;táä»ÙJq3PI: ßÊjžX"ÔØÀFÖÃÈñ’p¯ä‹ðüU%v r¾è{îƒúæ×ÀÚ¼œ¬õ"¹ƒÚ’ßw„áY‹\NÊåìXŒ9w_)I³zEH8¾ž“GŒ*˜óÎ÷B£!lž§±Ðâø¼0>Âȵ¶!Ý2I¸äû|´Ï}°–+w åò-î„‚å/8¹êu#xf_·2Ä”adë¦YŸ‘®«M¡QqÎŽ\xÝ«B)w¿ÜÈ//ds²ø© .¸¦ ŸñHtSxÍW} ;åöë»ÝEzFMF¦Gcã€3œT÷SÝϪÔOÕgHj.ëUaGªÎÏ4½Â,úU0RuÞÎ"ÝêìT’ªó®ƒ‘Ö«Œ“ªó‚azH^ÀIÕ¹\þºÓÏãù-%'Uç÷ûŰÓ©:o^#_R¼ù~¾Ôt{ᯠÿ6_»oÕaW4‹“ªs‹/¦púµš‘ªó6λÐýþ'Fþíýƒ°Fßð(NªÎu=:ãìÀ`ù·û]FìÊvù·ŸW÷SÝÏÿ¦~ªúø`,´ÌZr²ö’Øg› ß~ßtA}±CW˜ õ.¶€Í厜|^ öçRdBsˬrµï.ø\m,´jžUW}9ùxúX,2—H¹ü çâa ]‡oBdæwF†íLg&ÎæäÔ5i,jH†‚œ2´ NhÃÉ“í±·Bdƒ×Ç°ÊØ“¡£W£õ¡\¾#µ¡}¹ÈÂf›qwb7açG±w¼#û.HCÎ?ùbÁÖ¦¤®D¾º°‡,èÁH¹ûû±óTKNþ8 …·‘p}[Ü÷or±/jd´ÊíOÔkjrr˧†xÒýž‚ü·ïŸê~ªûùŸÜÏÌwQ˜£ÁÉ›º‘¨wdž0zM,=é²ã@Žè—5…qÎ-prs.#>OB#½Þ¥¡díF.»w S¼d°õv¶bzŽÐÙtÎ ´)—ÿCÌs^1rjÃ=øçÁMaŒíÖÿg¦ðœ‘\jÔ¹çQT³;ÄÈ]½û£É§F ;14ÍíÅÉü¨nX“ÕLAÊå³ A ]VãÒ0ùláÓÊÐàC>º•Ý™J² ñh kФô> ;®>b¤Ü}÷?1Ð:Ö““0‹™ý`aÚÏl´×ÙÏÈÏ9xän¯$åö?´2ÆÄ…Œ\½eL¤ºŸê~Vå~¦= GøÒ-œìÖ- M„-¯áÄ´xFîõ¿‹Sw#äò”˜ùê‚|í_2rØŽTLܧÃɃ‹ÂðüÄfᮘB„5®ÍÈ«3"PýÁNÊåç%áØX3ò}Y †ÿŒÖX´Í'4ÙÞ^šû…‘WwáÙŽ WÃwÇ:Âeõcì›ÃŒü0}?.œ|.”Ëw=êv¾Ñ)=½‚ßë²éc°ršpòé˜HhXöú=ÏV\IÚû‡Àæ§ÄI¹û+úECaàÈÉQVQx:r¾pîù8XDy §–ï„×€D¡ÜþWs3ñÙ®:È8Ëx-mÆHu?Õý¬ÊýÜö|/<¤hNîªå¹4¡ÿí2D¸1¡Ë›B¸– –ܽF_[òó”B\r›“µ»{ÁûÏ^¡‹×J\Ø)ìTÙ“žù 7x[ài«¶¤\þÈÎ hì·™‘î'aû`áçà$ÔwHXd˜Q³-…5‹oàøÊUÂÄä2°Á:œ|–p×K*ÉØ€Ý°q·”H¹|ûï—bäE{FòåÐp Í&ŒEöÊEœ¬ˆè¯ÞÂåÊÙ¨vr¿°]hÜ9ÝS(w¿iõH|É^ÏÉ6 è¿]8éòB,Íœ†-ö«„rûíëcÚõtN–5ŒA 7s‰T÷SÝϪÜÏ&›w£ú…“œ¬7Î ìÏ “g!­¢­DfÆá…cOá«+Qx¶±‡0sÐp¸ž¶>Š‰Šœ Njwlƛ턟÷GÛ.þŒ|ô-£ÇßÊå×d)YÕ‘c6§`Bï …01_oŸ±#þϯŸ¹©T’÷šžCøèꙓ‚Å=…“ê¥0OíDNvh®—µAÊåëÿ  úq²KÊUXÕQ 7NðFÊ‹Xáék›±ûT–Ðû­42+lÈãó{ ¿e4#åîW× Ç7_N&Í Å›áé÷F8nöMI¦4oM¿+HÙ÷÷vxßÖB"ƒjeŸ28©î§ºŸU¹ŸÿlÚŒ­QW8ik¸ãÆÞîÙ4Ïçõ–Èz^¦_ÑAØÉ¨‚Úö”“9ý÷±Î?z*Éß4ÑÜp©‚Ô,o Ï9œ‘™3 aЀ“®M{+‰”Ëïõ)k´ôä“Ò$Œþî~’\Ô3NÃÍ8©¼Š›\…ÏL‡âx¤¥D^˜ÿši}æäÊ#IHùÕ¤Oó =;YIÊåëyè ,’jI¤k‹t<ÝAø(xgærÒbÈVçÂç]X£ r¹ÛE\¯¿ž‘r÷GýŒ¾O£9™2}/¢g' ·öDåÆÛŒìf» žR3rû{_Ždí÷µedeËVh7³Hu?Õý¬JýT}Æö›J9©:·?ò…Y|ùÀIÕùÉ”{ìkôAFªÎ/iºâF¢HÕyÙ©mìAhª’TËå?2?FwU’ªsCøúïâ¤êüö¶«FuNªÎw÷Üw9ù·ùšOE©“•DªÎSšôÀÏ5IœTg‡Çù=ÅœüÛûŸê" :ƒ“ªó´¢SˆÎ¸Àȿݿéθíiòo?¯î§ºŸÿMýTõÞŸ;c­ÉÈ#ùåp|‘jGü9Ç*¼|99sK»;IIúÕJ@ëu]8Ywc(´œ— 79EÎÄgB_çí8\»¡DƸ…\@;²§ƒ?6¾±‘H¹ü6÷cX½9ièêÏ&Ýo©$—'íG­7Zœ¬<»ÆË íV%£ÄìÐmívT7©!‘ß–”AÇá¶’uÀ£›ØI¤\¾I»P‘uÈŽÜÒn£2dz»¡ð‰:ÁȦs`/ _-ÚÏþ:4_„Ñ%– åîG+€÷Ó] r‘^ŸL°#?/OBÁd_%éó ýØrRnÿ“ÈTLéÄÈ„´3H²o­ ÿíû§ºŸê~þ'÷óüär´X‘­$ã3˰kÊpNZÏŠfAnCYøíÓw¹+lìƒ$CNFMõÀñ§)ÂÔkÀ¯Ó×…—"õáyÊJ¸tá~fe¼B°Î q;€”Ë¿ §?=ꉂl›ÃjõßÆÈ ÑÁX㸙“7Ÿû äTœÐe˜|Ìn GβÄЧ^«O°1?¢…QÉ áºì>#eûQÒ=c;12%¤ Îk®L܈GIG…yâN“á°1§1îõiá—1§0Îh'åî_ª~~³Þäa“/ÃË|¿’¼ˆü?ÁœÌKY ­Æ…B¹ý7J°1Låä×Ô`ü>` ‘ê~ªûY•ûÙü^ ¶¿çä>+°¬^$Ly£ƒuƒuAžÍ§ÔÞÂOW0Ýò('³=;Ãìx€Ð¯Ø!‹Ù‘ÕŸôÆ“=Myo`w§ î¦à릚œ”Ëuš½aŒ¬9çë°¤È}=ð´ÇNnl;faEB}G´VV*ɀݡoj|’¬ãE‹7Œì¼ü¢;ÏRrù}î·©IŒÚWf Û—ùbM Ð7j/uñΨã‹ìš·8é»s<®® ÊÝ¿ßñ2NuÖää=§"d7µ,2Å"c†løÕJRnTª5Oï(‘Ù‘©Ì­E'ÕýT÷³*÷3rÀyÔoùƒ“—ŸçàK­&yräntv·y|ôAðá†Bçn¿ÙÑ”d@¿Ìø… #­ë G¿u „ƒ ]ðC'QXþÄÑŸ|9Ùcá(8ge¿?—µÄ×÷º §í±ÁÚ æÂ‘–ݱdäQNîh¢‰¥³Ð!Í ¶Ái RßÏ.W»32-1 v9=8µÒ «ø!¡ìïïîɈüù‘‘gº¬öçZ ×Õ ‡fCgF:ž‹Æ²\aJL®¬ÏÉØpC¤Þ¢ åî·ûQ€¶Ï—prSÍKÈo Üjü‹ÕœÇÈö®˜œÚ¤Ü~§§þìÜü1Œ,žÜ l¥.Hu?Õý¬Êýœj™éŸô%2yeœ¾™ ý/æAËì#ÇÙ•`özwá¾@#d›i‚¬ãç2S¡½ûN ¼{‰‘KtSÐøsºpð׬ !@Aöú±A]@Êå¾ëŒ9mAž6ß ó5 aÚ2¦aÖçÙëæCVÑg;#•³‡à­þnaG»ÙØm}V8é–)&uÏ䤎í]ö=r8#åòÍßâ× uAjiEb®¦°P#}]ê2²¿îAÌ;¸[A¾ÐµC½´FÞˆ‚ݹ: åî]}5 pÒlü9|8wVhcwoF7¹$¢žS¤Üþ loôe rch.ì1RÝOu?«r?Ö ÆË"K‰\j·6c{ ·”߆ûZs2¨¤–w—r²þ”,<¨Ý äî·°:p#3»œÃ…=—¤ÕÃs0yáÇÉ‹_K±µ±‚‘Ç âhØU"åòŸ F¿tkí’“g$ŒœÔ£¿Wæå;¢÷scá¾N^P,ÄÈÀ+‰8ëvYx«±;x Æ‹JQÙd6#åòq:ÓÄ÷Œ4¯-!æáiá¢Û¸b˜C:n?Œkks²Åüb´ÐcdH W¦Õ”H¹û£cN!ðêNörV¢äøG¡÷œÛþNØeâÄN±”H¹ýÛ_Æ`;òŒs6\œS8©î§ºŸU©ŸªÇûU²©§DªÎ·ÉE…·9©:`\÷ZNœTŸ›‹¯ÚI¤êܯÿ=ÖñI6'Uçrù[{GXxªó×öbŽ«HÕùºÅ§ñZÓ•‘ªóøÅYðŠl%‘›/?òÂ"63Ru¾vm:ª·ßÆIÕùáσ‘ÓÙZ"ÿö~Ep6übkJ¤êÜþtS,­®!‘»ÿy tžò’“ûyu?Õýüoꧪk¯@¯#Yà—ŠX}aÞ™\T‹yÌÉe³Sð%»DÆÎJcsê02£¨1lb ‡wMcöÝ•¶ä¤ÁZð¾”ËȽ£— Ñxc {—ákÝŒ”Ë¿¯p2¦µ÷æ¤{Èx¬iµY¨?õ-vÙ‘#5uq¦~-}C®0Ï™=¹u[}ü“ý]x¼{tnŸPKç$áó±§JR.ß¶ñ­1ÿÊ^N–j×L˜çÞnáYÂ%' pÕ}‘ð{¯g̸c5¥;÷¢y¨¥Pû]X<¯ÏÈE>åøõ>LAÖ¼è…m98Ùé½%7þ.”ý^N\Wî¯8™»Ä®• ÿíû§ºŸê~þ'÷Ó$&Ã^)ȩƩ`!‘•‡¼1¶¥µD®[2 /ÿ¿ûW»`ÊÑú 'vBΡ—Œüt}:º5¨2óC v?¸ÎÈßâQ2ÍX"÷ì|Çž:ÊI¹ü«FáÐÓeœüôa0»8 }>;Ã<Öä”±¡˜¾¦›ðsùd|47:EaáŒîÂFŸü1$é>'<¢sÃËB¹|C5š!]3SIZÖÑÆ»½É¬h]\‰z£$÷Ôï÷Éq9äƒ1ÅðºØ—‘&+Â2€sRî~üÓr˜V:Ú‘Ûw•£pñ`%ypÍ)ÆãÌ8s0•nHg¤Üþöù…L§i N&;^dnÁÁŒT÷SÝϪÜÏ!cÀÒÐ\A¾é•‚ÜÄÑväÇ“:ØøUW"×%²‘Ãpò’C2Œ‡ìb$6§¢¼Åqim¯%Œü¬ŸˆÍ•‰ r×m}(F&0rö•Þ5U(ûç®j5ÁIŸÛ½æd-ܽY“ßu@Z¾¹o‹­Œücžw^ýg–ãªN¶‚ ;Ã>}JbdúX87´)—¯4^wg(ȃú&èV‘§™`÷dÙy½%îß6n±Ñ“qrÖ†qÙ®B¹ûQëïâ´ó%©² KZuçä6 Ó_X¼\’Žf£;e¾Bk¸®ê 2d^LÏ[ ÕýT÷³*÷sƒF2ômæ§N%âAŒ®’<œÀ® ÍÉŠj;Yÿ¬e ’µJÂÄ£õ”d«ðýh[ó‹pöÅô¯}>‡¬ý1cZrrØÁḧíÍÈ¢VYètª!'åò9š#kO+N–¦tÆÌÏ•dçàr&ŒÜnkÝ¢!BÛ=vøìÙZ¸Ç³Ô 4ÛÕ³\g*ÉÞ»`û±–Œ”»_6ò6ê_ÀI E tˇ”B:cÌÈ.Ï.ÂÆò8'åöO×,ÆW§{QoÕ!a#­(X¯‹:ØŠyöŸ…Æ6âú–y~É>Ö$¤6'åò_]¨­q»”¤k+Tô²Ï!{[§a|M}‰ü6u–N±–vÞŠz¦Âü+–°Óo(Ü2« ŠæžâdØýöX¾e­‚”ËWÏWß $NÄg»çB¯³©°žõE(w¿šÏUh-Hã¤×Ð",» ,]ÝÑ­$rÏB®yÃI¹ýYFÙx;_S"]ª¹Ã.A_¨î§ºŸU¹Ÿ»×ÄaäÜ+JÒ `?B´¿ Ó_õÄý‘z [»®Àþ½FÂsÁÿ`BÍÇœuË/îÞ.èl…ïû*…ý¯² §YÂ>ÞÀ‹ §¥áõËTFÊå?áÑm äØ˜¦HNªÅÈÎ9 Øvl'‘ÞæµqÌë';Ö€Ñõ¡Î㯬WÙϲäôt~¹€‘!{Ýa]ë”P.ßòÖŽ°[±ƒ‘†Î‹Ý#¬hãÓ;G„}Ö!¿¬Hèµä&ôWLU’ Û%!¶¿±DÊÝwúç"5¸ÏIc‹³ÈÙûEèo®%c; W3Ä‚b¤Üþá-p¼œ“½4êâÙ—JRÝOu?«R?UŸGcÑëjSNªÎ³çïEÑ¬Ö Uç–7?²&9[8©:_ºê0KY0•‘ªód·tüt}ÌIÕ¹\þÒÈæøQîÈÈÿóXßg³Ûs¡Êsù] |xÖ›‘ªójw²á\g#ÿ6_§V  Ým?#Uç]:xc~¯»ŒT·ºÞ }ZKäßÞÏ^ÉáTSS"UçÉ}Y9€‘»ßÚ@Z ¾+È¿ý¼ºŸê~þ7õSÕˆZúhúá3#Ìð¶S= Æ{À9ÛH˜g…Õ{;§\KÄ-…Þ1Wàæ˜ÄÈ%GüüÆ dSåiÔzs‘ËßLÂcåONÚ¤²šŽõ¤\~Ï Xý“úuNàÂÊfÙèlÒZtžßì†eÖB÷´4ÌsÎà¤ÆÌ@äØ ÔƒÆýÆ 'Ž9‚ãgÛ åòm>|IN9i±,­ÛýnªuG›•äo÷gŽœÜr4’åù5däg×þ(Ý` Rî~=—s˜a8˜‘Y•ç Ý²µÐY™ÌŒw1Nn´:Á9ÁH¹ý¿Ö¶ƒëÌ· rº²l_)ùoß?ÕýT÷ó?¹ŸEOúá× ÷;"än{¡Í‚“¨ZG¸`i1´u29}î]xÎS’ /œƒ©O5‰¬×ó2œgL¶#gC×b?'‡:ÌÃé¶ ‡]¸…ávC)—ÿ¼E2¢çJdbeæÏ0ÎiÞùN:Âů˜ùµãœ´ZŠ<.´+˜=WÄ»|®aCë^B?è(¨/‘rù¼WÆ›×m%òàÄ}hÜ]Xôå >±hNþ‰>q'„ñÇ9JžkìWû:/mÀI¹ûÉšyÈÝ[A^:™B»àѰwèÒ©v::ÐÊí_ºg%BXSM¿¥aá·ÚBu?Õý¬Êý\3bœßt¹ÂxlwÖžñ?½;zKA¶øvN 8ùÖ>õ/ô”ÈÃ'{áüÛN”µ(x}‡“ëû/F\?¥ÐûŸýHS_"m4ºá‹~,'åò¼,%rÖ(èÄwn|\Èæ/ÍR’S]Þ1ã5zŒÌŸ1¯.­ãäÒß}ª9J8|™)N¬],|±Ã{ì~Ú‘rù¶~Ù†³Ë¬$ÒcÙ4Lê&¼W⋳ù'uµ·‚Ÿ:*ÜØ1‹¦Õ’HåôAП›ÃI¹ûæçÏÂ^‘nGÆm=¿ïA9ä¥òkøgM{F^¹–᜔Û_km †0FV~¾ŽƒîÑœT÷SÝϪÜÏ]õ›h ÒõC$ê<5Ôº‚öA…œ,ò̆uÍ–¹mÙ}vBû'3’.±[‹}ìÈ3Yc1(o;'}û¢Îu{¡ž¯:;Z(È_ÛúbiÙVFÊåÛvù)ýì„/> …§CuRºŸ‘¹k†!kòyáì‘&H «ÇÉÛZ-à~oÕI2ï© *ƒÖ3²¨|'o«”ËçtÌ ¡öm%²KTsœŽÕ¾ã«0Q"'?kÏǘ!!ÂÖ—Ìqýø%yÍà ÍõTr÷Ë*NÁSs®’4z,Á½ GxÃdR ¿pòa§©hm{^(·ÿsBì»Id̓±ÌÙB¨î§ºŸU¹Ÿ¿¶@çjz ŸºƒaӦšÖaˆûf.‘%GV ÈÃRX·Z#ø†2²½ã8&_š_0ÁJÛÛJ2ôE ´oî­ wüÜ‹W¿12ïÂudZº»9©:?—‚z¿3Ru>=<¿¾µ©:ïÊpú}Cªs¹üîv™ó¹•9¤êüh£H´qf¤ê<ãåyT¼¾ÉHÕyƒ:îh?±È¿Íw§¬:|÷˜0Ruž_Ðέ8©:?¾v-ö gù×÷-3¡q1'UçWNL„ó@ »¿ßÂ@ŒÎÈbäß~^ÝOu?ÿ›ú©j²Îc6ôq #›Ö€Ëúao¯.xÕâ'ßßúÊ5<#œ^¸ ƒî®ž¿ùC£…»‡—ÁmxOF§ñœ|郚ÞþŒ¼µ7' ”ýþ:à·‰—‚ûE)ÂÒ¤¹è}ì—°¸Ì ß• 'öjßAEœµTbë½Û ·Në‡Ø¨'¨4CLZå¨$åòÅŸç?yg²;^׹ܰ´{=eäœû–hîÜä÷ƒps×NŽ}vš}å—”¤Ü}½±¾0¸ÀÈóüp¢Ž¯pÍ VdõJXùs0¬7H¹ýõïBã¸`F–$GÀHsðß¾ªû©îçr?õ–µ€cÊSF®Ó¹z?…ùs2EÆ NæÚ„± mdÎŹ°_q„“zö‚¡éAái“˜iw‘È °±˜Ù«³PiyëG&q²ÎtÔúÒQ"åòçyFÃùK#;~KœN!¾?B0Ö,E¸f@†Ü^$ìž—ÊtІ ýÇÕÀÚñZ 5ëö„Ãó_ òÛhŒZ[ÈH¹|F=ý¡ÿ Èö£éÞ:¼Š9X2°¹°Gu/ pª+ü–¢…£íê õˆÆ²^=„r÷­ñÇ€]Œ<±%]Wy ÝŽâÊÒÚ Ý ³CŒ‚”Ûÿp\öX3ò­ËY¸¿½§$ÕýT÷³*÷ód/4Õ™2g ZÍ©#üü+•e^ögdPŸ·lÅáj SªÕÄC¥='\g§ŸÞSM¼«áÒ‡œìªýŽiœÈ´!nÚÍ‚º1rv«mèõº+H¹üw=2°°ÉFöð9Ž1õ† ³ŽÄÀäv{a]¯8\¹W¦ }ZL‹« È&Ccpè¹0j~:®&½d¤”uçÃqR._\ƒ,ðÇïÙêÕYLkÀ…ÝýÂëðL¸áAÌÛ¶øyoÊþ(È , L|ÏI¹û;‘}p#ÃRö`b·-‚Œ,,±ñá¤A­D—KB¹ý.ŽG`~ÿ#'ç{àÑw3‰T÷SÝϪÜO³±2¯ÈÛ~«qk„¦Ðw™)"ò…¿º-B¾“B8,O©N/Y~h2BÛ™CX·=Ö%¬adß[ãp¹4J¸rûi±Yð4 vqR.¿åš\ ¨öUA†CÅâWv¤Wn2æÙPïOBÇnæJòd니NzÌÈ1½nÁ¨ã!ÙzÉ”`ylèA¦X8†“rùtúãVéFº…’“¾ rv·T¬®9‘Û‹SṪºÐáÀ <ú“³“»áÀW{¡Üý±Å{qý¹;#WÔ Û÷pqƒÕغt?'#®LÃ3_¡Üþ\ ß›5‘ÈluÿZœT÷SÝϪÜÏ]c·¢ºc5ø² ]+™ù6—ÖYƒTtÌ‚Bj)ì×/?‹M…gŠÔå<#ß¹¬@ËaB£ò­ñ‰f†mÆÚc±œ +î€ÜKû„rùﻀŸS#”dõ°<®/'§üÉÀÏéLè {Ù‘ÙB³68é´Ukv•_ß 5ßu™Ó¹ºe ^}ÎÊåëÖµ][ÞS’+ìo õý@NŽ-LÁèz#dD§$8Ki'ÉÃþxVøE84Í'6te¤Üý:SCàê·–‘öCQ}ÓJáª}½qaôzNýŸ¿‡U&ìV’rû½ßqæÎÈžqv¨!Hu?Õý¬Êý  €Á쇌|Ò5w/ ý¿^ÄûôBá‡;×à4Ø\X÷Ÿ»èwÛVItÊƒŽ²œ“Ãç#år2#kÞÏ„síužS^0ó­éÂúŸbñöf7rùoE`í}œÜ—¤„׫¡ö£`d ª%‘oî.G¿&m„w³vb踚ÂÊUS¿³Œ“/OÇ`—[+F®™ŽÜ-”¤\¾‹µóÑ&õ'ÝÇẨ¥DÆ M€ó¹>J2õp<uÈšÎr‚¦C%#?ÔÌ€ÙÎ_B¹ûsƆã¬õ2FžûûÍ …=Õ@ÜÑ ¡Kƒ±>¤Üþê‰áØÓ§Èøé'9<€‘ê~ªûY•ú©úl?”'©:›t¿¿ÎR’ªó¡‡CPž®+‘ªsíöç²öy©:¿»®·GêqRu.—ÿ“Óaœ9SK"UçÉ&ýÒX"U美w9©:wÉŒBÝ£Ÿ8ù·ù*Œâ`nb&‘ªóœÓñ×¶1'UçÙ‰¥ˆÚ¼_Iþíýx)ºÌe¤ê¼Ï·˜¹Jäßî?ð=‡ürÈ¿ý¼ºŸê~þ7õSÕÙΈXÍÉÞ¦ŽÐû#lq'3BéqgZY —Û„2⡜¬;0ƒ¹|žÉÈkÎbTÓ œ|ås þ4”Hosø˜XÆ…»c{ø¡\~ÛÞ­pcVcïõ†ö—vÂÖ¿c¶»9'ó´¢q­|¸Ðíi, >4Éìr°4&•‘—lQÐ?H8¢U¶W<ÊåkRÚK§&(H‹9FØÕY‹‘üI*Îè$‘»²}ðÈJ(™‡Áj¤''í_ìÄc³p¡Üý˜}ú8Ž‘]º@Ó$KøùYSgê€ÜPÇ;lº åö›Ï„gúµÒåMúOoÊÉûþ©î§ºŸÿÉý¼5f4NhÇròþŒ˜Ý:Z¸À¢¹3¼mIÃÎ…ØÐg4'#¶›­2Í~*Æ6^ÝîŠxwK‰<;â%«µ»š06Ëß;*ɯ_,QïeSFÊå0ÍÊ;€|µb&¶šÇE€w ä¤Ii Ú\¶ÓËEÄc_Y=ôZºip²‰ÃU„´¬ ³ô°ì¼‰DÊåk¸³2rÈC ­ú 'hÌ@å×î¹±ÒZ5´…z1“Ñ¡['½xËŽ† ÊÝý«+ÊÆŸcdô¢ÈìyC˜òÙÆ9«Z¹ð«'‡”Û_9~vÝÉɃ ‚1êA¤PÝOu?«r?l³…W¿pN®ýÝÛŸç‡ýˆëµA‡ðócK‰¼å #û;ŒœœŸ€©Ÿ6ÞÙÆÊY)IÛN¥l` FZc>ùýzÕ*„±]P.Ƃݘ®ÓdÎÁ0øZ×± M¿qò‰Ç`ðìj™±! wÌàä<Û|³Ü$œùõ%ë¦óCX}ÕöÀ-ˆ‘rù¾Ý€^îãy7v´Fº­}Ê8™Ôü9Õl­pÕÊ'ìâì"F>ë±¶®v åîÏ8ÖVÑùâ¶=”S? wŸNÄ—+9YZ'CSv åönÜŒ'Sžpr’§5y+T÷SÝϪÜÏI:"}ývNVLÕA„ñaµ!˜Qb)‘cfã@–ðgßDôªP¦[Pm²–ð’ŽîüÔÙsf8N&Ön“²a˜¢#‘?kafÍêB¹ü›Üã1G·’‘ÏÂbÖð|áéö `ip“e¥JòbˆÐ±­?ð¦DïDQH²ð]¬曼ed‹ëû0QÏY(—ïJ·Ñ¨Sg=#¿}rÀãI»…?–¿Úµ…Ó:y"$g'Gt˜ü³B¹ýék®0åAWNöýu€uüåÌHu?Õý¬Êý,ìÔÇ¿€“Å?ª£À¡ž°kp'ø¬1’È#7°l«œtš•k%9Èý <‡÷çäúÒLüÓz#“ï¦c©þ{;rð·ZxÓe#S<6!î×Q¡\þï2¨ÉÈõ Y˜ïâ"<ó}{UpEAnŠ*dßj\cdû>+±hX!'½7õÃÑ—×…&'SØ6SA.z¥ÍÃ9)—ÏëàdLˆŒaäij±é`¶ðÕm z?VØØÇ ùýw ¹£im‰|ù¤Ò¬<9)wߨÃd\½\äÝ9³ñé^á®4üs0“Ó›æ3ÏõgsH¹ýÏVµƒ"Sd§[>XsÒV¨î§ºŸU¹ŸKÎW²ô1áJrRÎS}åÛIòÕý6xýN23¶tÞØ2ÿü~lx_ÀIã¸m(<ÛD"³xÖe)É‘ïb°Õ×€“:ƒ‚°9Ö‹‘Sor¤æ·Qrùuš…ýœ¦Œ|žxf+dãËÐõY'!ÓWãî…°Þg-D§Fp²¹É¦Sâ $«š ãNé<Ê“ ‰ÎP’rùNVºaæ¢ËŒœº“ø}á™Eƒ‘b*¬6c2Úü&Ô1±‚²ý~%ùóZ<{é£ å[ŠþÚ Mo®‡ÇÆVÂ’Y_˜ŽöSF†ìXŽSö¶ åö—$ŸƒOÿ·Œ4)»Ž¥öv¤ºŸê~V¥~ª>oÏÞcÎww*HÕùÉèt¦ì8‡‘ªó 4Óh"‘ªóü1‘˜0Å—“ªó^‘x®+‘ªs¹ü3?g`i!v¤êÜ)/wz˜€TŸØ•ÇÎZ2Ru¾5À5Œ@þm>·NþÐnñ’‘ªó¢«®8ø>˜‘ªs³Ö¸ôù#ÿö¾ë›-èû¨ HÕùÄ-)ÝüÛý³æŸD×À,NþíçÕýT÷ó¿©Ÿªö=ŸƒP»F™^#ù«ô…%û¡Q\“Ϋ[aÕ_%™9¼#Ön¬ÍÈ’Æ>)YXìàƒ«ß}9yÖn\ \NƤޱÂßµìð8\["åòÇ}]†—¼Œ“[O‡±ò¡°b@G„jWMøÍŽl'ÜÙ5ζglI­™éðž{[IšëŸÁÊ…E r˾  j,‘rù<…òÞu%²Òó ¦:wÖ×6A­à ÷á–9SaùÉZº3ÏO”3Rîþ¦ÆûÙÊ»ƒÙøT23™ç!<↥ô¿$üpb4~ƒ”ÛßäQ1JkØqrRå\òNøoß?ÕýT÷ó?¹Ÿ/ßÄ£¸m7‰Ô»‚µ§z ;w®³6­d“jš¸ÙÜ–‘n6C ¤srbÇ´ޝ1 λ¯srúOmäµÌ^½°›Y_ïÀȶûÿ{w•cÛ¶< I©„DQ’t›Yæ9ãMæyÎ"D*•T¨4*%ê:Î2„$!©$ãÍmΔ™¿wýßýxÖºÞ粞/ÏÓºÎ/¿ûºö}[Ú¬Îë [P3¿3H¹ü_‚âmÃÇœ´Èo3J…Éi7™×QO9*ê1ÛÇȲµÇávן“ÕãÂþù‡p£Ž25Œ%Òôuë¢9”‘rùª5 DòR;‰Ü‘µ ûº gùâô9µÊcU/s¡KO Cž^T’wÆîËI¹û šœ`©/BYP_b†)§…w:Gaý-Ó—žAãÈŽŒ”Ûßxöf\^YÉÉMïFa¢÷1¡ºŸê~Vå~îjç Í& ‰l¼n:|ÿ—n¾õ1'3…‘þ±ÂÞ~ÕAºÝ:‡ÉûŸ0Ò®Ë] =¤ Y9gã‡õQ’ R‹Ø¥«ùùË9Ä7Ôê¥BüH7NÊåïñÐ æprñ-¤4Œêÿf4ià8eº –¬VÛ™lj†;} Cn.ÅGº CÏG V׌”Ë÷îÒhLîb-‘oÓÚc@MC¡î‹(ÄFîØy ¿~nžÊÞ‰¼'œü0a&– »"”»ßÊï*«·¶‘õ¼ËY·_¯„ßuÃïâ%i;árÍÍ9)·ÿJƒþøp~"'çNs€¢™PÝOu?«r?_NAÂ<‰læÚ†Ûm…‡Cuó– ýòÄé6ÂÇn¢¾2•“оq_×F"Ϥ À‡{AVl‰À»ŽÂ×^Þèý"’“Ã_Yáy¡\þ‡ÒçÊÉþçØƒ-ÚÂØÏ¡8ùû=üzï,|‡Ö®žÎ*æÅrò’õ6æ±Ö^AÖ^…>ü…}ËN`ÇõAœ”Ëe«‡­ßsriÄ6",Q¸ ­eg^;’Ãf]‡V㤫áO6óÀfaïƒù¬ÛÓxFÊÝgÅoÙ“êš 'Äi"˲ž°×›T>\ÍɹGö ÞÇB¹ýÚÍ?ÐGI>èQJÛ0RÝOu?«r?ssÛaHp‰ÜÔ£õ …¯Da‹ikìg6æùi wFÌCW³Ùa¶!î>}ÏÉ‚Æ8’ÄÈ—o`GT¹’œ30ƒ¥”ìbäðØ%u)—ßmAÓ³b™ÂeñìæJù~f1ì6­dd”G ÆfÄ+É¥¯rغà"Fö>o‹6{,@»³O¾·È™NÑìÚ¸½œ”Ë·÷ÝS¶©t('í ž1]?/%9år6Z÷Mã¤cÿŒ*)œÑvΧ(@åãÀêFÊÝo4¿>‚Æìß°/m)|2`æ ҔȔ%wYÏÑaœ”Ûß½‹%š_­dä¼°|Ó¤ºŸê~Vå~–.¬Ž'‹µ$òŠ¿Ybt9'm+o!\ã#®—ãbÙVyøÐ–u«-'k<ÔÅ@锂½![rnr²ÙJ/dd¿ŽßW»ž™¤¹£/6^û‡“rù­sS™Ÿ#÷¼ºÆ~†æ+³/"nb'ç ShJ¤ÿ¡Ø4®=ÈU¦‰è S]¸õ€&‡Öư£8bZO(—/%ï›¬å¤ Ýütñ¤aF68ú?ÿΡ†DÃVÔ¬ý™“g›\ÆÒNÞÂÞÓwaª¢R(wŒg[¬šÞ¤s|7œ1ê(Œ‰c½W€‘ÿ¬4…A 9H¹ý]8Z†×¹xl9j–å+Hu?Õý¬JýT}Ú¹ÊZÝIâ¤ê|I£;˜bЗ“ªs½~÷™ÄHÕù„3àj“ÆIÕù|Ý ¨˜íÅIÕ¹\þÒ~_×®Ruþ-Æ ]§×‘HÕù' ´×1Ru>ããidÅ´ìAþi¾×ýM°¢ÞXFªÎïE͇ÃåRNªÎmóÆÀÅè 'ÿôþê)ý¡áѤêümD 4œí@þéþÚçóñ±ËENþéçÕýT÷ó¿©Ÿªõ–`Yü9“’•—>ñJ2Ó/}f’K›GaUõéJr÷O\Oé òø˜“8œ]C8ç’.n%‘×ÚáÛÄgœÔ—Âqi‰µDz×DÞÅdNÊå__™‹5çqÒÉT‚_»2áÚ®®Hp6ùÎ, sf´ò­¾øñ©£D.¯× ¶Â%S£×QK˜gVûG5ÊåË7tÆœ9Œ“5‹£Ÿo$Ä#=ô.'ø„fÛú9uM$Ì>™¤ö×xöqœ\>+„Í)ÁH¹ýVbîõŒ<îWÓë«8ùᆰû©îçr?[L:…!šœ¬q( 6Z '÷BÙD%¹]#ß³ksò½ Ü0˜‘¡§8*=R”äF¯¦kÒ›“Y–0JóÈ$Ó=,±*Ü‘No†¡öA¡\þ©ÉðôøÉɃ½"PÏ·¶Dn[’ G?鱤ï—,žÕ¾Ïðó÷÷ÿõç¸}ìX篖ÁlzÑlFv¿ }Ûµ)—/óç´vÒÙÉg7ÎFÕ†‡nE\Ks‰|,ÂÒT aÿYó)¹œ|¥ÎJuý)wJP «ùÉTAÆ eÖ>ÙBÃíqm½Èà¥{±lu=¡Ü~/d¬[ÙR"÷^ž…ê~ªûY•ûɯ$`מœœc {Åá÷1Xa?QÈ…íÚB§†q0i³T˜QíNÕðf&wFŸ|g¹ÚÂíë22ÏÓÑž¿„SkÃßyš‚”Ë?¼Æ~ÚÕ•È6 ž0¸¦#4˜QŽÝ¿.)Içeù8œw“‹¯ž`u¼—22×Û–ñ…}û'Áòüja“Ñ0ªôÎ$åòåY…¡™n-›Ãu×Fn™Þ#H䘴ÛLÞCNæ´ž¶§ ÈŽégð1©€‘r÷³ÚF1‹ †Œìû2‰Ån,t³<†Ç3ü….^qxî¢ åö7‰ÐÅÜßïQÂìJV°ÛOIªû©îgUîç¥3‘°ý5›“Ö×ãñò%Âó«B1ns¹Ðwýnè·ª-‘©mö¡YwoNNªæ Etœðä5[¤hÅ0ÒæÈ,<­òØå`D î*‘îmËYiðaNÊ埱mçhJä³3QáSÁÉ e‰¸mßL"‡ÖÞãÉVBß÷.˜z´È•owÃý×KFö™‡¡C8ÙòìV¼¦/‘rùÒ*$<PÀÈgÒUè ð.ëÄœÚ,áä.æÕLÏ‘\Yû î{ù)IMÓ0¹áÂI¹ûVW”Œßvc¤KP ظ_ØùU–VËË û¶I@Ùß•JRnÀHtºbÆÈò—}PM{ŽPÝOu?«r?º†b_;WNÎ9Œ•«„±­cöQC‰¼}£;¦®Ð61[‰Ç'J8ae çÂó?ð©1È“/ °:î(#ó-ã³O8£ÑØt Êåw-ƒ¥é·8ùîã@œ49-ìpw0FÞÕ—ÈI·Z ¿Î5N¾¾×ÄHFözQæc…:§˜ËœpNFG½a__V)Û1·qðQF¾ž{ï¦6P’ÝCØ.ÛMŒu½œ¥?î‰òÇê­û9y°ùÔ7/”»?·å]–t-Ž‘&ß°VŽ\¨»<ÅYœLظ|VS‰”ÛÆs4jŽÙÌÈ|‡é8#ªû©îgUîç· Œª¾–“®¹Hïå&,Uß·|¾ht†ÅinKxÆ Mw S^e) ½¤Ö÷2„(8ÙþiŒ–7HÝüÔw¿ÊÈM o `S\&)—ÿ›A8 åä•¥€Ñ€­ÂgAzè>z”°åQC¸Mî¢$cWAófŒœp>j¾Q–_Á&ɤÂ2ÍútPrù®9– ÆËIœÌJ,€Í® ÂÐø¨6ѤéUg¼\ÕF˜>ë;ÎI_ËTvõa4#åîŸëW k£¯1r€[|/~ðÓÁ» 5$2¼h;h¸RIÊí7 ؉CxËH[Ðì^*T÷SÝϪÔOÕÇÙm?¢^¸sRunæ¿‹MwŽQ’ªs—.˜Wé FªÎ¬ßŒÁÇ:I¤ê<óÅ>ÔýËA"Uçrùuµ‚.Má¤ê||…*úŒSªsý:'ðÏ«ޤêü³Í”ͱáäŸæk6à,rƒµ%Ru¾[;õn Ružè³ [•Ž ÿôþ²æÖî%#UçS—3˜¼eäŸî/Ú[ˆŽäŸ~^ÝOu?ÿ›ú©ê¥ÜXÞ%‘½p.ì¼pûüwLcÅgrù]Xei1òË‘m0ŸpPXØ~/žvHêà ºDoáä‚DZÍD"W[¦ ëNÎÍ€•þF¡\þ‚Çý°U3‘‘ÃWƒ{ó+´úE°iãÀɺ?r ÿà­e0׊¡Âó>…,eØc9®ænxùýäd֣׬•Ým¡\¾¤s?Øû作l{ù+hÚBhoåŒ.IÛ9iþD,¾uPèŽ,v'H.LXíø– åîçï4ÄG8Ù³_c$%î®È=†ÏëI¤M‹¿0÷@W¡Ü~» 4øîÎÈo{c‘~t¡ðß}ÿT÷SÝÏÿä~Õ„s&eŒœûx R¢Þ ‡†˜£Ó…QÿÒÇ&¯º”D!Ø6Q<å,ª s6÷›¯=$²Úõ¬"ø''ƒ·®Â猳S+„rùÏ,™„ËÏŸ2Ò¸Ö"Øçj€d)Q(¿Ð^"¹ú=Ââc†ðw cäPÝA0x”!fšÎ6f 5:ú`ùÈv åò]©U I'Ú+I›Ou°ðûÙQk&šuÈ`¤ßýUø0á®0²unL>‹ÉÏ¡ eï5Bž•'Ã;4ÂB³‰ÂYÏ.³ì™e¾0§yù Rn?3ÏFEÔ ùùú¤7ÙÏIu?Õý¬Êý¼Ýk:Nxi‚®½é­t…ŽáÒœµŒìsjÎöp¾ ,@ÅágJR»<wzääaÎÎdÌU’Æ[¿°×cÃyÞÀæm@žî¢Ä,½ûŒ”Ë?Ó}3:ïÑyn´ìKê 'õ´F›@3‰ôlðŠý¥}‹“ß»/„ÝŠcŒì1n=ÌWÞQÂk±±°ZÄQè¥Õå¤\¾?š bÓ Ùn¥%>]5eäK{/X×û$|²&CZ|†O‹ƒ‘½’ 9•ëG±œ”»ŸËa¸owN.|ÛõÓ…£cvýJFŽ°ßˆšª”ÛüL$F·’È7‹GÀR»›PÝOu?«r?·}ߌ—»‚¼Wꑃ „ ®9cúEOFÞÞ4 'Ç… ×éÀ3¾›Dj5d¨]ËVøòÐ tØó„‘–ÿü¾3û‚0ØäêíÆÉºƒöÃíëf¡\~«!øæZ¤~ÍxÔØ¡ý/‡_boüqòpþßlòÊ‹ Ò3w ŠOE0²W†ÞvÏ^«wŸxp²"sRÆÔ•HÙïoíº`î—nŒœ–ΰæzoáúŒ{òPøÏÒÄv‹šÞ\ÕŸÍ$òþÌt¶øK*'åîwíÜúþß”$m€³.5fý~Šadáèôîç,”Û?øA>ã}îq’•döZ RÝOu?«r?/O À€W Á‘úÂÞ3gã«›ÄHEüzø|( žWÌt>qrEûp\OI–LÞ…lƒ@FºÞñ…Ñú}ÂéVâAË7œ<•Æ–›C(—ß§ïiùÈÈÝsðÏ«+ÂÛôýáWG°À#Â~"r’ð¨d³ÐàN,Ûa6“[µ0·²H¹|Ö±}ñ¢Í F¾ô6ËY˜‘Zˆ×6FÂ2‡RÄWß§$?\>Å2÷¦2òDè Ü™gRî~YÝ&0 öW’ÍÖ5…é‰áÂIáGáQÖ…‘ÃãÏ`͉¦ Röïß¼Zèß<‘'ëLÁȃ߅ê~ªûY•û9ÚáâŽÕyç~ Ü5…Z>8ô…‘iÕ#0¹úgaIØwfÕp—ð”¡úÑW…=†ÅR캒ݪžpþ•VðªÙä¬ãJè^ÄH¹üžß °¤E#×eãø$káù~P|Hè·a.nX£F_‡ûåç䫎Wñnh'Ë£±H·ÈÝÞgÔNÏ‘”Ë×,Ï zš‹9jÑ ›y ßÂs³`NÖê}µ/W“Èüï X³5Ž‘!q(;ÚZAÊÝ7ln½¤Ÿ™äß÷M¡×ÔEhâ!aR@'ÇÀñ…)·_ñɯ".0rì<ÈÝ)T÷SÝϪÔOÕçîý,œ˜ð‚‘ªó…õN£HºÉHÕù³%È«“ÍHÕùø)WàÙr'UçŸÄŒM=9©:—Ëïó¤ £¶¾w$U瞆Kñäy0#Uç&sŽ#ouK‰TG^:Œ³¯­8ù§ùÆËþ‘ŒTçfƒÁO ‰Tk7ˆÅ¥ê•äŸÞ_•d÷i×HÕyqT;¬ò·”È?ÝŸ6:­Ú¬bäŸ~^ÝOu?ÿ›ú©êÜÓ>ì|LNn©±yè)Énea÷Öš G|½†ƒÝ#9Æp3B º€œ•xÝ› kÕ?õ› ”ä”WáXp¸'7%Ï€7ûyq¿7ÖÖy$”Ë?d`.sÛ|“mÎ'³Ž.¯½G!`´­DšÎ©…ç½ 4ÛàêqsF†2˜š÷>z¸ƒœ9™}å/hO+Êå:\‰ŸeŒ,»yùG…ã{Â4ê#'ÃVlÇ×#O…>Cáµz#$G¢¬}}¡ÜýðBñõ@N>؉À—Â-£bÈ䃌uµ\„rû×ÝŒÕÕlAÞŽ?Ž©NŸùᆰû©îçr?óy°UM¾9’.ÎÞ¬rEmFZYˆê;/*ÈäuùÈzSª$Ínãµ±#—DcÝÌŸJR·æ!¼\4“^¡¸'B¸hêMÔŒÒe$3ŠÃ¹ +‰”Ëß:=†M›÷SI®òOdîíVõ }{ưU5Â9ùÞf7Óu=CfzöÇòòьܜ0Ãfí6¯du4M9Y²¶ì”ËgéSûØîŒüܼ N!ëI×Õ+ñØö 'µ†Î…Uæ1áí+(.¨§$o¶IB/×ê)wN­°N àä"ϰQ í;æ#¯³–°¬AúY7”H¹ý±Ë2ÑÙ·5#®LBÖé¡JRÝOu?«r?û58ȲM—0ò冖–% ’²°×o;'ÚÇhí´9%Zw„µ›ìG³}5$Ò´¾¬Ì« ƒ¶0ìpÒ³ÕÄ÷:%œüø©)jîmÇH¹üëÎe³·*䣢{,¡x#÷ŒÏb)ãý„þjŒ»?-ò1úí5¡}àf$¯¨Ò¶S" O ßžEŽ™’”ˇð;آߚ“K ñbCœðüÆéXÜ.@Ði*ôFïnqí ¿ØŽÙó½+/ìÈI¹û5êîÀR¯ .ôñDþá`á)×ùð±RHdÒ¼§,àŸ×œ”Ûß-0øøXIZ^„'ÕýT÷³*÷Sïl+j|‘;.`îó? ƒ^„þþÞ“ÃÉÚÚ{±ÛLï] ýœûBûšÎ˜»=LèÓâ+_}\8Â`'«Ûø¶‚,;ZËùw¬;œ§h€”Ëÿóau\9x€‘ÚÍŒpöJšð¡îhT»YäƒIp]ü•‘£|"aa\dß>—ÐÎì#'¼ îøUœÔ(õCƯ£B¹|5;_BiÏÏœ´:“·­Æ¹×nœ¥ÝœDâÌé'ìÛ´Ž•2ÒðÈf^ýV(w_¡á‰VB9;a;új¹žb—j{;’ÒÅ&ýñ#åößz|‹N6óÝ…Se:©î§ºŸU¹ŸÃcë¡rumÖÍqÂ[_üm;Ö'pÒtà¤úö‰¥Ù«…ïuúâxò a‘ñWžQ dë `´³ƒp_áM89.aä &'ñb]s‰”ËÿùY{Ôßy‰‘{sz£eá5¡î[_L¿r^ø2=.z¾Â;ËТÿj¹nhxæäÿ(OÓ’Èq¾ìXƒg™¤\¾œ_G0òu{‰<üÈF»oεG¾«/'kÏ4Áî‰îÂ=o÷#>Å‹‘=•‡à´J(w?bï6Øüu”“F![Áq‰ùS‘¾QÉÈÄŸëÑiIˆPnÿ³îÐg(‘톦³¨1œT÷SÝϪÜÏÏV=09¿)ȵŸFbGŸÂvÉ›à:à 'K¶­Å¨‘Â6ZÂdò0áFÝ̨«–’ü¦íƵ@yy &ó1rd¨1†Æ¾àäª.¦è•»EAÊåO[1‡Öæ3rbãY¸Tœ#wù¾Å,žŒ AnÂá’Áih;·‰DjïD§¬NB-×Îà—ÛAØ'Û>c¤\>ë ѧfG‰,ן›˜ ïyh¢˜[qÒëN3rý–Iîû‘…— ;¤E'%²‹_rRîþÉØrœ“ ô¶Ànrª°þªm8e˜ÈÈšálå%¡ìûÕj/6þ™># îj£¯u]ê~ªûY•ú©ú,í<=µ©:úûÏ53?“ªó»G,âŽ#U烓 ñf#©:;Ô _ïÀHÕ¹\þf½–avh#UçÖÅtîÌHÕ9.ô„¡«DªÎã–ŸÄÒó•äŸæ+Ûa­µ$Ru¾#ý sM3`¤ê|Ͻ¿ps‰ƒDþéý;óÝa:!ƒ“ªósÉ9˜¿x*#ÿt¿mÖh²ù§ŸW÷SÝÏÿ¦~ªZyd6üúÉI³þÂÏë/„çvÁ•pc‰¬÷Þ«²; ï´öÆ…S-NÇâAÁFjI.ßdC›Xü2°†.Û€®î9¹jTÌzuY(—ö…hhÌýÜ]ø&z'¦d’ÞcØB‹4%áÈÏNgd£lýn£ ¿´n ‹\MFL¾ãÌcœlo±óÏ— åòe½l «µµ©Ì²B«Ëm…Wv³Þ΄•­³ØbÛóÂvmR±|NáË”ì²ßÍI¹ûºÝN¡lÅ™3ÿ4:®7jæ6ÂÊフ|,†öî8¡Üþñ·ÌPßt'ÎìOÆf’ÿîû§ºŸê~þ'÷ssÌts¼Íɕ׆bò”³Âköh;‰ô40†I^ aÅÝ$ØÅc¤´6vÆ;d‹{…ÇÈäâb¤<Ñà¤Ï¹¶uÈ2yÏý/üsŤ\þCY`%ÙÂ0 k¾ /º?a½ªi€\´°¦Nh'ÜrÑq×2²þ+GhN×ylÍL̰“·ÞµÁ»ƒB¹|nWºÂ§v7Fšç2d6u¾~^>q: ÿ2R Ú.Sá½R4‹ï&‘O·¾bGiåîûnÌÀx'wGòÔ,%:&(2É´óÕz?#›×^ñ§…rûÏzÂŽ8$3²CW†B'kê~ªûY•û9Öj ÈÉI úÃúìA¡í?·ØÂœ—B“°P¦³N˜…wJIÿ×ÇÐñÀ†LòNô),ñ„“ö_v¡yjþ)ŸûÙÐ9;‡sR.¿åß)iªÁÉ3N¢ÚÂÞÂ^[Ö¤[P8.6ŽêéŽðY–Bë¤x„ö7Td²ÓŒ|Ðk>ÊZ+@ÊåÛîÝaEŽŒú¦7&õtή¿ ›ü[ìõÃ|}#á“àlqV“‘á/û`CDžPîþ®FŠo·U’ñë³0Ù)P¨ïˆ£Í_12jây|8-”Û¿¶ô º6í r¹ùÌ~“‘ê~ªûY•ûùæ&xs²Gõ~0šç&¼×Ù™UÏ$›ÖIe“5œÙ» µW˜qrtIXçk“ɳÑóËY¡ÙωèäºKXù5#|¶—l^Ç…u%R.¿ÇŽ“Ø=x'{µHÏàÂÚëS18ä,#M"NãØ{aM«¬KÏêo.Ç.Ý ò|~.r<1²_f*–¥»rR.ß¼Ó½új:#3zöB§GžBý®‡ôá­pD—d.?)ôú²…8,œþj;ÖdŸÊÝoå›ésË•äÉÁgaý>'Ó,Já?á•’¼Û(K¤Üþ›î70÷AX&™x .椺Ÿê~Vå~*çõƒeâN:>ë‡ÃoÇ _ùÌ–O‘yQÃó¥7³ýP—µ”È–¦pº}{áÓ#Ñ*Û‹“;·Cù¼ÂŸ¯|YÒ,m¡]'Ü:hR.bå1¯¹ÇÉÓ÷"Ñ슖D¾ö?—Õê)Èéwâ`\ÔTI&¯ºÇCœÜ¼9 Ÿ†Õ’ÈåG|áTêÅɧ=1¹âšP.ßÌ}áœÅÈ6þ5®Ù•8¬x¼Z¸«s,Œ› í¥ é¦RxÅø6¬;\W’r÷†žÅ¬UŽœ<¹é,̽f ×®Àõ¢.ùOZ}<9ò“rû/uDu7%'Ç9yâ@ô!¡ºŸê~Vå~¾Õé£ Ã90».<$Œð‰Æ¾1Ò'Á¿†¼ÞÈ(a߯VpRWÓ‹M?«$]\^²k¦×„w4¯²m­=™Üø8ú{g ý[Å Ïò˜#)—ßëÅ$65”ÈàE»P¡ÕZ8[' ½ï­S’X†% BûöÞHžD>þ9û¾àäû¤zHìqO¸»2Œu¶ÛÎH¹|±Ë'áAŸ Fîm½EÎÚ ßOˆÂ¸Ç rÖ§HL«wFxeX,ìo'‘',,Ô«‰PîþÙ²1å¡''×ßÈÂò©¡Â§Ÿª£lc€’×ÞŠþVrûíÚ{@Û%œ“;¥ßßË® ÕýT÷³*õSõ îÛ9©:ßh±ûü¯0Ru^ì”Í3Ruîö°%¬ÃL@ªÎƒ/¦à/^““ªs¹üoÖb“~{‰T}ŽÇ%לT·6·™áœTïé°• ì@þi¾ñÅžXiÚ¤ê<îÜï^ž‹Tªó²áÏ™[ÃJòOï§w“ðSë'UçÆî=pË·#ÿt?nZ`Üõ›œüÓÏ«û©îçS?U}hŽK…i¶, sƸ OÙâó­ ²]¤B0òqÑ@è?Ü*Ì\?—l6 ç4¯`]gõêî°ÃÆG…e7Ö dàsa²•FíÊå·Úû“u¼TäãcŒÈ3¦êiàáÆ{Œœ9ÖÞÌAª²/í”dÃÍ•lb·¦Œ|·¬>Õù ,NHÂÇcrù–o,A‡è# òE嬷KV’EEápÿŸÿßçÕXžŠ¯)„ýF䡯ÏG%ÙNƒ³c-$Rî¾AõDÜ ×ÙG:Qù5„GÓn£w®'·w=¹^¦)·¿yX0–V³•Èt瘾ÁDøï¾ªû©îçr?MˆÇÃ+y8- É·§ § 5†•ÌúçY¢f¿daø¥1ȸ!\›°u¿®»3 Ë‚„/NÌ€ï?a·g'±®¿pÐþ<ÌœÅI¹üoB6ºÈF.¡t®ƒÐu™Œj(„ƒüŽâJ¼°í?µ¿¬ˆ‘5ö„ÿ†6 ƒÊ`«,P’å¡òI=‰”Ë÷qm1Îÿ½…“÷\ƒãÉ›ÂFÝ.¢Î°=Œ4ª6Ÿ+Èf/ÌP<ÛL"Ýd±™±‡”¤Ü}‹É§áœú‹‘/qì |+,>#l:Kä×A:Øåt“rû÷ö«N#£9¹•7Æ“ó[•¤ºŸê~Vå~êKC¯NŒìT–6ÍZ ëüþ¹¯=õAXyp2´ý A–ïGŒC]áÔíJ†¾`ääÍKPßù¢püç}ØÚTd£¢¡Ø£è,‘† þfIgJR.?Ö»ãhh¦Iûp¥Qá—™çѹßWFÆ}¼a&S…û†î‚µ—Hã.é¨9»‰pÊËQÈZÅIo7',ÐÜ,”Ëg²< vYu$ÒèaÆw7~ZX‚1Š %ÙdG!n¬ ãdÛmp&Ƈ‘–|l«R.ßÖF°ln'‰\þÜmÓm…ýv\ÀeՄ׆%â~õ6¿"¦ÁÜAÉÈV?¢½ž6H¹û_Æ\ÅÃg¹jþuÌ ‰&Öê†ÜZo¤kY;X%­e¤Ü~û&¿¿‹vaäÉïVȪvO¨î§ºŸU¹ŸúÓ/Ã}ç  ²âþôÕ V’ dbSÿº -ºŽg©1ï4®†4È]^x²±­°¶ÁM¸œ:ÍÉdÛ(° –yV/žÕƒØÅéñ!œTÇhÌÉ·Zù§ù†hꡦ›''UçZ jàØÛÜT7›Öß߯R’z¿žF9æïÜ® UçãO¤£à²†Dþéþ9S’uæ'ÿôóê~ªûùßÔOUËn}d¦ÕÞ2r¬_mp©rõãË(lÞ”‘÷Vœ‡‹¦~&nÒã½örÒámcìè¿AØ»81Nø6ëj\NÑ+GΠ» ra×£8xBG"åò»…"ý«œôO‚ÊÕÉÂÈ·ã¯Èg¶Â¼I3áå7µñÎk$'Ãj属zVd×ÓzÒL["ªi!}Ñ$)—oèrK|î7œ‘㢻âGô8á ¾#qhÈ9NþÜ1ZƒÿåÃ| \«$£j¦Ài ‰DÊÝ/Þ;s-A>>¾_*„awÙnc¹{_¬økZLª Rn³}±¸÷c5#7l<‡”ÔB…PÝOu?«p?‘Òc–Ù\¯9gFöÖÏˆÄØ~œtLJAø¾BaW›€üÊ]±:[!Ï´£8Ù÷án¶¿ÛÙ|OûÞ¥…’4ijËa6 åòÿõi0”Q«8ÙµåP¤Ï] Œ;Y —ÅÓyºz9ÎJP’KÛÌÄ왎 ô<„/†„'Ïúbº¹™ðþ¾2,žbËH¹|îãû!æÈP&lÞ“\¦=¿“-®œÌÚ4ï¸Y8u²)F̵fäØs½~gé.”»¯sП#{€Lqß—öÂ5Õâ1ož0æ|1b·0Rn¿2û**O¬àä´ó)°ÖT"ÕýT÷³*÷sdç5ØØŸ¬unvtUî‡aÎOÆÉgã‡ñ$áû­q˜Pd 2mçüh‘ÀÈמ5PÿÒ;!÷uAçQõ@wâÐÚø‘öÏR`¸ÿ¹’”Ëo{c¬ çsr^­AȵLØçr>zÅžš$¥àÔïï9ä»Eàú‚‘ÍG\Æž[„Ï;¤Áfo5‰ôj>•?Ë8)—oAôïß›»63rü¡ž°»*´XÝQa9ùýI¤w-< lO2òdz°2)wÿ‡mrÞtލq6Âqoã0÷àäËy±ˆŸÕ@"åöw³Y ãG ‰´úýN0żŽPÝOu?«r?¶ØÓö _ß< Ç„LÃùÕ‡“w|Aù9ë_^=­ Mdõù'P’V‡“ÿøú@¯¸„‘ýæí‡²Å>¡Á¹4=Ж“éöûÑÌQG"åòç7í‹ðfë8©W怕Ò&áÊ>~˜Õ¾‘DÖŠXì•Յ쀄 …®JÒß"Ll8YfèŒV¶‡¼6‚òâV¡\> ߿׋²9¹ËHô´{&¬áô–UØ>Q’‘뮲ÈÄ ²Qz’¶KŒ”>bGþ>NÊÝ›ñ C7'Â^ÃT¸½Ö2Üb5$RgÓ`ÜßÎI¹ý®3®³+%í8Ùö}Ml~úKAªû©îgUîg­ÝñhÖÖ ¤UõT8V6Æ- @ÝN®+Û†èÞú©YË~9é~ý‡Û['í‡yÀ6F¼ˆ@@…¿ðë÷R–9ñ9'Ç͸͆$d3R.ÿ{$¥»qÒém Lh¸P8¢é8ôX}Y8wåpüî¾ptö˜•;[fB«ÈîÂå˳˜óê]Œü´µS åòõ8:,kܸ O75bÒf°q#õº=`• =ü1çCC‰\Úa ‚­38)wÿøíð]`òyz:ôkÕŽŠÖkpr`j7Ä~wÊí?Û¨ bò'0ÒE§?샆 ÕýT÷³*õSõáŸ3qgBuªóI†SpwW ‰Tߘ ½ÑnœTgH°Ü‘ªsÓ€í(ëýš‘ªs¹ü·ÖÃßïrRu~môH¼é·Ž“ªó=ÃQ(¹qRu¾ªE¦$„2òOó¥Û‹…éÍ@þŸûf ÐéE#ªóù#1èÍZNþéýÍ”¨ÿZ¤êüªæeT—“ºÿHAOXòfäŸ~^ÝOu?ÿ›ú©jžå+vîP!'×MºË†ê¦Dœ„˾ºÙmF®l±¶wHÄ¥!©œ<¹)7èKdùgä®3yëïCðôï œ­†Wû's²ÙoØÆÖ•H¹üSÏnG-· Ÿçìø½u„ ]wÃû`(#ŸdïÇ“ÔT¡^^°íUÈÉ1Fµña|ªpæÛô‹ÜÏH·uaˆ®á&”Ëç~.ÁVŒ,0+ÁäŽyŽä•=‡ÙÅç>ŒÌYÂrTÇh$áÕ¼á7Q}Õ!%)wqác6ìø-FÖïü™Møü·p%¿Œyý’„–kîbÉÀÛJRnÀñ>ÐÈ|ÉÉv±ÏØÌ¡‰Â÷ýSÝOu?ÿ“ûYâVÈZµôà¤Ç€¬°OWa¡ëèÏQHä̵ƒàÐU¨õf:fwè(\ý«&&4fj¹^¥Œüô±ÿ,¨æHNáç™åœÖ‘È´ò­ØÕ±Pn¿N·=ìüÔ—Ž¤ó¬›Ì}ý3Fªû©îgUîgÇÎEì¼ñ-%yÝí[zC_Ø\Ó=*%2RçovdÇ=NXÂ"–» ·¿‰dߦh2òÆ‹,X/ ç¤ö›PLÿqM¸ÿÞvø:ç˜y한4i}‰Û7PI®цÉñ󌜵i4«=6ÌØ ÏÅÇ8Ùdš't¶škd âH#=~\Eð”Ë?gDœ[Æ0òçÆbL3œ"|uá*v5*ää½8|ÀT"¥I;aÌ­A†ö‰A·ê»Ê.( ¯T¹ÏÚÀ4y#åò-ùÝvm$òË›@„më$´Z Ã핌40?EdªÐWßþ A Òd[Sè×Ia¤ÜýÝI ©© rS¨#V”ÔZØ(°nR NFûµÄØø¦B¹ýîÃèì8FÖ‹ŠBu?Õý¬ÊýÌ?¥- /ä»k Ñxµ#õ*_°!Ñ7¤½‰ê'eä[W|]& VÛˆ ^!”A˜šÃÉ¿lÇ¢ãÏïBÿgÔM´–ÈeÇë"gà%)—?cÃ]D=8£ Û?*ÇWûvJ²íÄí&‘V­Çáí϶ÂNàr ?#'”‡Ö»Ÿ Ò°Õ4ènÒgéU¼^p—‘rùZ_óÀшN;¯gZ¯ I€gújFº÷‹Ã_)­…±9n(žßdËö¹^ðš‘r÷íÆÀêãõ@šæŒDô¾9»ëSV°´Ÿ‚ƒoÌ»å#FÊíOl‰÷õŸ(È=ÃwvPÝOu?«r?Ýl†¹ï­9UÑÝ“„]Ü­pßlP{Ã@ÍùMcìÇ uGx£Ì»D˜ùE/íosòÖZ_fþ>CI²E°4|¶#Ù+¾>*‡3R.ÿqÏ2ø×ïÌÉ;‹ðùÖ¡Ó4sD·¨&‘W¢ë¡øÒAN.¸ £ö+dŸѸ[°\Ø:ô Æt äd§.Þ8ª(ÊåÛø´¦æêKä,§öh4ü 'g¶B³=ŸdÁ÷¬]ö·°^×›XúÁ˜“‡Ñß±H(wÿtþ$ìÚSä¹I³àœQGʧâä<a‘O"ªïl)”ÛÿñB6†­Ü¢$"²™}™“ê~ªûY•ú©úœ ¶AÍj½©:ï“ë„ð¤ÙŒT_ΊÆ÷ Ï©:ŸÜ.‘½Š<ÎHÕù Ä=Ðzؤê\.¿ÝÀ«Èºxƒ“ªóܸ†0mS›“ªó»+’Ðl{mùø¸at@þa¾ {ZàÐ’óœTßI8ŠÔö Ru~-0û0'ÿô~Ì„ÅØÓ¶6HÕy¯‡y8ð÷2FþéþÊÚ!HJî ‘úyu?Õýüoꧪ©ý¢à±c'w–„ÃnÃ_Âá;âÌ¿”äò“áÒu‰pey4†-zÌÈÑ%—àšxH<0jÙY"{Ï+e›ësrÖ³p”9XIdpÑ7¶âóWNÊ域Æ:,Ná¤G@+œ+,»Š‘{Ú3rX“D¤ùíROªûàY“Sœ¼¾W¾}îjîÂÊ­Bý olêöI(—¯úš·lÓÕ RT£¦ô`ä?8éî>½+çáäŠÝpè{P8}Ò>lns\(wG?°Oëpri½/ì¾Å=%é>¤9>­­)‘æZ(ôïÂI¹ý?ŒÏcÊòJFn™\Žöm.)È÷ýSÝOu?ÿ“û9mÔaìïÌÉ7§Â0Ýa˜ÐGiü›•ä›ã͡ץ½pÆÏ;аˆ÷^Æí–— `² 'åîŸ<ù“=¶=¬$'œ¬Ž™Õ† ?„›Aï`KGòû¥NòX#”Û~ 6!œ¬µ5ýµjI¤ºŸê~Vå~®©†ùŽ8¹Þ cúÏ^®½ì,G²Iòwæ’Y‘Á{1È£…D®ý¹£–[ã {Àá²#G¾rÂ>Ó‰ÂþÚÀݘ×Â7Z©p{`R.¿N» ¦Û烂ô--f‡Veä©¡Çaú¤‘‚»ì –öê«$g¶9ʬ†oåä™zÛX’ï 7‹­êì9lÎ/Žb¤\¾‘ç±cø\FNhÞ{*'ƒ-8¦WóŽk‡Tã&B‡±¨îe)‘eŽ ñ¤PîþX·šøgÛåL2:HîÓªe-FÐÿ¡ ?Œ©‡‰…©Œ”ÛÿÚe ²l?pR'grïÄ ÕýT÷³*÷³UAwïËÉ•ß!|à`á•_X\Z#½ãê xÐ+¡ËË>x1ô' ;bèÖ@¡[9°²Ì‡‘cZGù·Âà#EÀ‡œì9Ëy+Ë…rùTûÁj6õa¤¡uC8/ˆþó6 5&qrûÒ Ìq¸#<\žÃ7¾ÈÈ-ãÛÀ¦FCvÕüÀn&0r©éE™ºI(—OÛ´?ÒF cdŸ}ðÐd¸pcã[¸¸"“lÚ¼¯Ãœ9éüÎeþNÂA9]‘ã{MIÊÝø¦‡ís$f7‚㫺 òí›á¸^Ò䣙‘¨ÙA(·ÿ\ï…¸˜ÈÉ8“IèÀ…ê~ªûY•ûÙzò4\3“w‡#H±DhvɬEsCMsë*4ÚÚ·juãdT(CÁÔNB~u'¼¦´ùlùì¸QW8x–ÖÝ ã¤ËiåW¡\~ë¼Öxøâ#ÿþ»äŸÚ]ŒBqn ‰´² 9u{÷/Z„êõAö9çƒwn¯ivߦ•pòɤÅÓj/‘rù’£€œ3ét£úWnž½}ïø5N6UfâÞü†ùγ.bL(Éïß³7yœ‘r÷­âüš ròˆfp)]) R€­dFvè–ÓÉJRnÿûƦØÐ/‡“«¶œaOÒ: ÕýT÷³*÷³,9cœ¶sò´I$>- ïõÂQéþ}ýÿo9‹ÂŒ¢ö§Ç,äN.ºõ‹ôP*É]ïÀEk"#Ó"‹ð)|,'Gdì`‘Û¸#Y¿/ô_š”ËÏœ CÓŒ´µvã…­žõBM-;‰´3ÓBƒau„ ¥Ì©ÃÈOÏüñ¡Û6á>“fHŽ^ÏÉú:]ÑJR*I¹|Y{à[z#Ÿuí ¿ïW…úŽ¢®«DÎ[â…w/» oç.EÒGGÖFÙpY÷‰‘r÷ýý~¿ÜÚ¥ »™Ã¤ô€°Õ˜DÌ¿ÈÉ.ÅX®ë-”Ûï5%”ݵ‘ñ3ëÁä€!Hu?Õý¬JýT}š¦GàÜþNªÎµ~d⬵.HÕùü%y¬Ö°–ŒTûn> 'ÞpRu~y̼ÈLa¤ê\.¿‡õ$ì}˜‘ªóu)çÙŽÁœT»)ö£Òñ÷÷·ÿUuÞqsuìH q$ÿ4ßsŸ!~|b¤êÜçÚ4X¯¶‘HÕ¹]» °¿^™Iþéý¨A–ÈZ® Uç÷¿¢á¥íœüÓý7ï®Æµ  ÿôóê~ªûùßÔOU‹üû£Õ—xFZôwBÚEIØë› ~¶³ÙâôLT¾·îœV–G)ÈD¿÷,mh#CBby½5Èdë ¸¹-‘‘ï߆ÀÞº½Džºiƒ+ë8'åòë™$CÛ³¥’œ6éÆ|ž ô:›0²ºóMX•¸ Ín‚ý œÌ*=ŒÛSí…yûmq6w’Ü ÕCÒ›[™B™|¦ÍcTX#wì‹‡Í ÂÜÎg íÛ@"£Çs¡Ýˆ}l OTeÈLxÅHÙß—£Vb|‹ NþÓ̹™'„ºÓú"ÙÊ“‘û’"+wŽPn=ÿ àRC"m ±óÐáä¿ûþ©î§ºŸÿÉýßtfͽÅÈZGAçî ᣠ»PÚ ä!ßpäÚ6ú„4Ç6ýF¥ߣÕPG¡å€lXþj¦ þ~ïž—©$]üû5:ròãì·¬”'+I¹ü²b`\{š’\”…™>…ÅúסÑßó49pÿY8½µçdÛô(ÜÙ·Ix°ùQ\*».¼aP õŽ}`d+—@ )ëR._ä¨tÔLe䢊óBü…{O{Á|M‰Œ²QîFBE³5è;\ÉÈgã×@‘)”»°dÕOàd7ûèk-|þ®Þ¾LcdŸzKZѤÜþŽÃï3cÙ¶Ì~qBu?Õý¬Êý4µš„ÏZÕ@6ó›Âiu„ãž&¡óø—Œô,KƒÝì#­£à9ߤ´ïÎé1rú°£¸ë¥$W$à]P‘Pß­.tЬ0®5Œ…rù,ŒBÅØ¶JrÔíh¸—›]=Ž¢´íœ4x{ FüËÃ|1>É@"Ç÷›Qí…k*.c´Y_Fþ:!ojpR._üíkØ4¬#/î¼÷Õv*ÈÞ_†Án„†D.uFmºÌIm?_,6þÎȘ¨<Ø,MÊÝמ±oG‡s2¦tÞ­ æÇc£ È3Ÿ‹«{‘rûÓCÆ q™/#ì ‡íBu?Õý¬Êý¼·Î‹õAÞ‚í!M…¿¥Â®¼#=j%!|ß-¹zS.zvz$|Ó*ás”äÄ©ŸáÇÉs GÑd¤žD&–§¡§í_ rê­Xîq¦)—ÿSr\>´Q’í=‘ç?_è̃ñýúvÞÉt컣úTF6*Ëbçç ätd¶a¤TþÅm.`{“I™ñæÎqãÖôð‚ýU5‘´sÝÛÈn\“B=Tä_d¤qã‰Èks‰ÛíloèiDpm³mÐaÆ^®T>ó–)xÔQW$­‡cÿ–žÜÔ£V˜×g—@ÎidŠ«M¹Ãd€oë3ÜeC®²’0#®Ôý§NÛ`e$«nC|ÃPn«^ÎÈÔHæpXŠoþp¥ö¿ª)‡‡ß 9i¶3GV ¤²ŸÊ~Ö¥~*>£^&âÓ7-Šó–°þ2RqžfŽM_‹¤â¼íÞNpÑpg¤â| i4ðžÍHŹTþY‹Ó`b”.ŠóqÓLQñ£‹H*Î ¿-EÎHÅù‚àùpÝþ‹‘›ïë/Ô,4IÅy¢W;”°HÅy|ë6<ú*#ÿö¾Zå6L*?)Šó;5jð4~/»ÿAß,~ äß~^ÙOe?ÿ—ú©hƒ'QÒB$WµÀã˜FÜÇQ9¿†‘#¦a›àÌ-|î9Ú~Ùü°Žß段_V£ÌDrÉà.ˆÞ{-¬ s6䕯îX|ù1W*îH-ì—-ȳT±6Üæ_÷Ãùqcg‚w£5W­“›»Œ¼ÁÒairCFžï:C#ê 9ìˆôk!Œ”ÊW¼\ %GÛ‹dG|b›åòË·P¿cww³©Ø;:ž÷þBgXpŒDÔŸ\©ûMNA÷S`&™ ¹C÷q¯h€ßÜFVOö†5{Ì•Ú_âá…ƒAîí…£s«ùŸ¾*û©ìçs?'Ô°1¾ äv­»¬ñÇ2îuï\èÃH³#ç·Ô“{÷Òn$k%’v¡àÕI—{¸~sü3t“@ºxvõWžr2/ÛWo† ¤0¿_4I©üë-^±â²^p!Sy¶QNê_@üUµ;n!±æ,#5>Ÿe¥ž¹àRl=¦pŸ|:ˆÁ2²pO¼6\æJåÓŸ{ƒ°òëkýz·;C•®ÀMìÒÚ+3¹iݧbØS‘ÜØ!…íL?#R÷DCEkQ&¹''j3¹bÓÑ÷`)#ößG›G—e¤ÔþÏóp& #9&c£ÿa9©ì§²Ÿu¹Ÿç‚3X̲ é:!‚÷æ>ñίßËÙöÛu”?–‘lB1S{]!ë\Ø™ãrrÉä?{€«r¨3º&ŸäÎg.,nÛ-nYý®¸gܤäûÉÔ 6ð·ùÏãóìZ릌œáZ=’dä.Ã2|1*:KOcóg9hašÌ¾žIÖ<ºƒóù®Y6Ìé+´DR*_éÂl6mô=9¹ög1 Ó©5'k­Tpny˜@6Ò¸ÄFŸÄí½ë!{oÆHÛñ–õðæJÝwëvaCVf’Iqè«êÆdR +÷d926SÛ‹¤Ô~­Qøñ=FNØ‹êd'®²ŸÊ~Öå~мX˜©y¸‹ Û<﫜\ùü6T¢—s›9Þ†ûJ|Yz†­V]ËÈK›4q{Ý#®ÎÉ/l‚óri¹æ5»p?•‘ c\ >%“ÛÓ§N\ÉßG“X¥æNFÆ5¾È|ÿõÝG^™p¢®äòê8tf*"ylJÄ#cÒ©‹ûžpŸÕ_ˆ—+¢¸ƒ"¢hM*W*ßÝÕìbtžŒLÜ jÍ»02!0˜©ëËIíl¦5R—‘©7FÃÈi-÷pÁX¸–Vq¥î¿^°¨ ™¤£ì\:}ç>‘¯Ç´ÑMDrpÓiдH©ýoǦâ|¼•@înšˆ‡OÞý[e?•ý¬Ãý|7ȉ]4j 'Ï<Ș,“‘_¯"3ï±@®r3zêˆä§™SpmÝkFÞ Û„âÄ nÙâ>Hªêræâð³·àŽ¶Í…J 9#Ó5Šaäš"RùÃ+Ø‘…o©ªÒEV-@æ×÷íÜ?™¼?&ßãNÿŽÖù†"ÙÅf›1®Í•*¦¦-Ñó#YÎÚ`FJå{´¤¾é cäý¯ÆèU6šûÏ?gÙ¶Kg¹g,Þ°€.õAZt;Ž[‹ ¹ú¥x ÄH©û×:'Àåžœ¬×4 *úÿp?þ˜‡]WÌ Àf©\©ý“Ýq¨^‘¼Üy0zT÷á*û©ìg]î§OGOVá¨ÁÈëLoÍb®»æ ¨nï#’c„æ™þ\Mç-˜–ÀÈ“-ÖCóÒ9îëɈÖm2øÙEL5ZÁÈÜ>ܧ)’9{g¡f•«@JåŸx¢=îu¹äÅ`D$÷ᾚ· éÅñ¹jïèþäØ áÝ DòVÃj–˜P*Ôm‘¯­ò\ã`¼û}š‘Rù4> ÆxC#C&Èpª_/î·ËмH­u“q·m/nÅç«Ði¹G ÛTǼƒ¹R÷_¼JFù,79Ù(5›Ç&qãz6Á¹±Ùdâqæ d’RûÏN¸ÃŒf d’ñör™•œTöSÙϺÔOÅg¿I*¹Æ‘Šs—äxx½»H*ÎÛ%ìÃyíŒTœÏ NEÖ] sRqþø÷hµRq.•ÿ5›ËAƒA*Î Û‡g¦±©8·ÎxÈ‚ZvHŹëÒcxrû#ÿ6ßu| õ©8WÕØ 'Šó6Ñîx’qT ÿö~ñ’ `·ÊIʝ_.3Ýt›‘»_UWc»ôbäß~^ÙOe?ÿ—ú©è¢_wa“µO Ý«Š1¹]4·ÄêV|ÜÏY} Ë6Wp÷tÇFû`F¦Gá¾ýbnäæŸ,ô¡Hû€YÞÏ”;'Òpµà–ê]ƒÎÕ#Œ”Êo7©Ö¶ 3Èò’ðíö]N¶9ˆ?wn1r–ì8*¸1ãÙ5çX®õ¨¦0Òm ÒEk"zì3å&͹€À·j\©|sEèÛ‰ŒüévjQÜOÓO3ÍÁ±ÜÝ«^0c§ïÜ6OÆ¡y×A ƒÍâP¼¦WêþC—p¼p‘OÙA,Ž¹Í 5‹FÊÄþ\›EÙ(Jº##¥ö{ÎÊÂåÏj"9sŒô¢qÿÓ÷Oe?•ýüoî§›ûuøl.È×móùåwha–k)’ݾœ@ë—¹b÷@”ïp`¤îç(œ<Î5róC[ãT£!œ½ÀÈFgà³ì»œ<›ê²Ë\©ü¢aLS7 ¤ñîbüÎr-üP`·‘½zøà~€+wɳ)˜ªb òœÃÈm¢Çmg+`ýñSä¦-QðZKNJås~놨C~ŒŒðu…ÅQ/®Þ(=h=Ötr T¯uæž_&Ç µ>Œ<½*¿ì-H©û—&§5¹,ã—>ãV!kæ,œ­wgoh‹¤ÔþãðxžH†¼ïŠÙSÓRÙOe?ër?ß^‘£âÓ ¬JÂy•¦"öúX…÷ÙúÍh]«Îý\)"Tp`¤F§ÛÐmÚPFF=8 ËCÓ¹ûYj4gsgTÆI¯"¹q[W|´ÒçJåo,+@©O…@Þª'"÷j=‘̽€fÏ¢iÜ33¼Ò¸¦Á8]ýœku óëp f¢h]'æŽë÷ DR*ßvõ#ˆ `¤Õž#˜Ø7†kž´±ýÚƒ,¾å‚.åM¹þÏcÚðù22jÑeh¼Ý R÷5Üàuî #==±úúGî¯Aûñ¨Ù‘lõÉEOs¥ö/Ó1ÁùÖƒRv„Áæh®²ŸÊ~Öå~n¸uQßZˆä·ì“pxÑŠ; í|,ôF WMÇdr#o#i‚/7H?ƒÞjŠä •<´ra䤊‹ØÜÝ’ÛûúS¶ >“dúƒp!¦DFJåß㜀ˆ"9ãX$ò]ÚpÓÇ ¸eÆH߈›0ñ3äú$bÙ 5\ÿ!þØ3À‰ëªÑÙ_Û‹dRÂ'&h’“Rùü.ºa¾¾œ‘Á̃‡qgóBЪJî÷N~xÆ]9#WFõÉ®_a»—Wê~ßF¾P¯ýÊÈÜLØhÿääê¢WÕV¼‘ÝÆ?ÉI©ýÆeFøut–@ú>úÈì5à*û©ìg]îç¼F`5­DràO¼[Û’ûÞf&>vÈ/_çaîiWnÉ4o>3É=[çáiîên¥¸õ!GNÞD*Ý_ ¤û+ d^ËÈ4‡•ð8¤TþU*X®-’&öGÑÌS“û\ç>öt–“ž·Që!ù—OàÉ0Fv{•‚—Žs뻚bœý"YoE ¤ßzÌ•Ê׬ >M0rdF,8VpïWûÀr»?÷}’'Œ½p'žSìÉR/ô„Æþ¹™¤Ôýü” t}÷›‘[ß„¡í¼?\‹¤þÈÎé.ž áÝ®VNJþý_HdŸ¾ucä·­ŸØª ª •ýTö³.÷ÓÏí0ZΉd°™îê©r¿Û-FôzŒL˜ïäXîÔîCq{i!7´ÌIÌÛmø øø™ˆä 5[tØë|1 O^cdA žnû('¥òg‡ïGÚd5‘tÈ݆÷j²Åèl\ÛÚD$Åüp4íл÷Õu4ëmÈH­÷Ð}õE9¹Ê¿tÔÙöõ)dq¥òÙ}K…ÁÏ"F:è\‚“O÷ÒwÔíÅí÷Äõ3£¸:Ý¡ºcœì4ó%ó™Õ‘R÷ŸZGBoäFv)A•ËoîW‘ýÙkjÞ3:©ƒ”ÚïUnƒü½‹‘ý üa}® WÙOe?ëR?Ÿ.v£±w@*λ-›Œz³/ ¤â|jØ8Ls\(ŠsÙySÄ»/ŠóŒSÑq+YN*Î¥ò_¾k‡Ïê•©8ï›p#º‰¤â¼{lTêå ¤â|V~NÅ©0òoóM .‚±ÿ~F*ί¹Ö5#çk³»£`W{Õ¾xø´úÁHŹFÁ*¦CAþí~Ïñ;(—‘ûye?•ýü_ê§¢Ë3öàç¾ £9câ½6Ü´Ñ›à07U £çn…ÇÈPnÓÅ5Éî±ÍN2V½ÚánŒ\x½>ëšÉÉÊC1q·«@–ûÄ1ŸÖmä¤ä÷ËåÇlv+F~í¬Š_m¸™þ5,³æ7ئ @êµu†ŸW¦@Ö¿¸®½k¸KG»¡fæyF½>ˆÙ é\©|wíÒ0t«©@.™½û¹Óê{c}Â)Fö¨rÇõÞÜ¥­–ÃA_O$gµTCCRê¾úêúÐݸ˜‘ûgÔÃÃ}ÜÍÿzϱÉi(.Úçá«1+µ‰åwæÑ1] Í÷d³_DËÉÿôýSÙOe?ÿ›ûyö¸+ž«ƒÌØé «iõ¹–ìÆÐÚãÙ§Ð-Õâ¹3Ž` ;Eš“϶wÀËGwdd´n’_¤ùå$½m'’^•Ú{³=ȧþ((©e¤T~³ŸmQ•ÍÈøßF°™p™;üÄR¬Ð³Ù¯‹F|ïÏ ª?²©-ErhªXp}®£a*FžyÉÈhQì.'¥ò}ø–Ž]R²ÑÅ<žù–Ûm­+ò}¼ÙÄîª7GróVMÅÒÀ·¢+úz&r¥îOÿ®Ï—žŒl´¸:N çý;? ä•݇`ª1H$¥öÛ6`›Žû3²¿E[Ä^mRÙOe?ër?7ëùâeó÷Œt:p Ž÷¸Q­wãðãBœ<Þ™1ÕܤGý±µo¬Œœ=¶4VŽá¹8"·Y+‘,]µ“n^Èú›|ñì·#wjœƒú$9W*ÿÃrslš—ËHﱈªH罋«í5AnY“/32Êþ43Y&«¿8±Å»s3É͇OÃN½©Hn± FÙ)•/d~<îCC$59‰Å+ ¸Û¹cá½,FÚ-òG Q÷Ê—ÖY¿œôèRÆŠÖ¾g¤äý úH˜ÌÈ= ª°Z~™;qM´¨î!’‘[o2™ÿ)”Ú¿øÈ ȧi€ütû.l,f¤²ŸÊ~Öå~ê_ñ‡sãKŒlÜ$9 Ü6± E2yoOÄ7â^KԀLJjY;OߎE0rº-ˆ¾Ù@s-~7=ÃÕ[UŽ2¹ ·À!%þDR*ÿû-SqwòF~1aëC¸ö¿R0ñÚnÑÇxDú"#›œg‹~ìcäêÍpnÿ=îÙ^N·8Y ÷˜cC®ºHJå+wðB붃Dòú{d,”q[žŒ‚ÍÈJFö½žŠ­»ïrm=W!mK'÷6ûÂi\6#¥î[Ä6†GÒ=Fî:Ø Ï^¿áf<­béCÌÉ# P÷HFJíßuÜzk©ÕÉ WÓÏs•ýTö³.÷3j­?r*O2òé1$œôãŽY^zÚUyWKÎæUŸå&ï»MV+×ÉœÛÒÉmÛV äÊ­Íq»áCnþÙ9Øv{nòr\¶MåJå¯ß{.Õz0râÅÙx´å 7iCFœÍ•‘¿ûEàòÚzŒœø}¢—¾äö-_Šèι\q…+[¿¡›@n2ª‡Í³n3R*_ —y¨c.’º Íqâa®kY>âZedÿ¥è÷y÷¤ëQøùpküB‘vªŒ+uÿé8 ÈK3²uuŒ8ÙdVº)Vœ^+#uvk¡vù-®Ôþ…¼0èÙ¿~ÿãv%CßõWÙOe?ër?_yúâŸ>nŒl{Í^釸E)îlçî~ù¼b?«ºgAÎÚ‚k»MAÞh%‡“ÙwFêN e}t' dVñQ¶V­/#Åvï™Mû,,7Žb]­ü)•Ý×é8X¼Ÿ‘'¾Yã犃܈A§á6¾+×cíĨªså—Vcy—ãÜsÁ‹ ~ýwP»UXÿ3—»cÈbØv‹äJåÓèÕK-õDò„E#\[SŸTü.šš2òÙoÝ^ ÷v¿‰\O'Fü¸ƒ¯î)u?|›âU4@-î†Òwm¹›§7BÆ´tFY3¼]lRjÿ†w`⺋‘Çf?@ÇæMRÙOe?ëR?Ÿª®^˜fvˆ‘Šó[IÇY¥É F*ÎÏdæb‘õF*Î÷ßûÎæ·­f¤â<ïæ2„þi Rq.•_¥ãd¤{1®Âcçš0ç¤â¤T¾Ûú.lºñR9¹pû6jH„Œ,„„qwùâM.ö¾Jå–ŽYV!߸GŽîAaIWêþùÆM0~Ûcøº–Uì¹Ë<ì‚3G‹¹þúñ¢ÚŸ+ùû/i)> `dÕ…Xp®ŒûŸ¾*û©ìçs?7µ„æjÖù¡Ÿ9ÒÒGüÛ 1ùSC‘´Z~‰½/ÈFGnÀà¡ÈH݃ˆÃd¤NœÊÛˆ¤Æ³*¼¹R {wñB¦×x¹ zœh-’Rùm>†Ã(è§œìº#=sJ¸Ìaߥ #?ö3ÁüT î«Ãg°yò.ü<ì6ÛLá¶7¹¶ï-#/uτƩ¶™¤T>¿<f¼k#ÇD'±»b¹…“ŠÑ¤b'wÒÖr컕,#W•mFBè]FÚ;‰#)Z ¥îûªbŽ·® d‹îLÓ>›[±Â #~gp+íöÀëss‘”Ú¿âÌ<¬i ríÌxtÔå*û©ìg]îçg@ÕÃÒ0m8ôgŒä–o9ƶmÛÂúòs~º@N~Ò»Ÿ¶9Ò0'ÿ)âKädË©1Xï¿7“”Êßîw:½ H×[Pzë2WÏ6qñj ﻌZËxFžÉî ¡¤«HÚ~xÉÚ½»#pcj˜S=î69k}ê#¥ò%ï@â—ÛŒ\Þp/¢ßdp=*ÂyÂwœ­î€ü.·¹.VMa¯%’éZ§Ù³wwå¤Ô}•¨£lt¿&¹b¾ 3{&'>„SjîŒ,š¨VÕ)µßë^4Œ Ï ä¬~Ahw©µH*û©ìg]ê§â³ÆÛn»üRq®¥;/ƒŽ0Rqn:g0žìé!’ŠsÇC!ø:ºHÅùÉ‹PÛë%Šs©ü%ú)pì^-Šó;ó°]M›‘ŠóšÇ÷XeÒ`TœÒ^ ËÒ> ÿ6ß;—=ˆï}Š‘ÿçÏ/î‚ö˜DTœ?x ‰M7¬ù·÷/½sb6“ŠóÞÇœÐû®)ȿݚº[1H$ÿöóÊ~*ûù¿ÔOEWlˆÄx»é¿7:¥"wioo–¯·$“ôïsž¹ëk02Wl¯¾a¹«u\dÌ}¿ò3Ó™x™‘׎™£*ë17lº>[pkËôd˜.#¥ò‡­3Àä¯ädË=íñçÓ;îØV#¡¨!’]|4Ñø{nô¦XezDF:–¼eûØ12ml0{£Œ|Ô I ´AJå;7/®Þ§¹É+åz«¹Vžù¨:ûR ÷üLA­q3‘ì9?Ó|Î äžÁh4Ι+uß`utöÉ=*A(u5á ¢?ÆÕôãâø\äÍÐáJíO¹V„,;s9¹{ZlÊfÜÿðýSÙOe?ÿ›û©îU£Ç2rϱhñ­åƯÆÒSö1Rg´L‡qÇ[ : ä³ÇC±0 ·ôöÔ•;òóhiÅ1²wÍTôœµ1†M™½/}“¾ËH©üâ½—,ËñM&ù©G1ËÚÑLFšípcÂÁ_$jä¬ÑÍŒôݺËŸZ€ìÕÜZSzs?ººaçÄwŒÌX| „›\©| 4SaémAv~*ÖÍÈõã`Æêz"i×sz„— ä–Ðð+,’sÖ?dá;n ¤Ôý‰¢k÷¡"©ÓÜö ,¸Óòg¡Ê%V WAÖøWj¿vH"ULrÒë4è½ÌUöSÙϺÜOûðÄ}µ`äøuiè§Ê=™4Úe[¹‹æBN›Éܯ¹ÇYr¿hYX›Ê–˲ùz¨ Ý6ùy`ïÌ}]s«š1ÒÜãFEV¤T~¯›™ªóSi}?“¹ùŽgdJ³FØ<ë÷Û»Þ°º—Éüý ”Ô9wQ"JÂÃy>î¸÷Èðý¸~òW*ŸnT,Tjš™“ÍúŸBÚ±X ’µÚ†íëÒÒ¬pú;‡q½î|c}ýIŽÓì‹‚õF2Ròß7Á.ÈDr©×NØ÷aÜ¡û.²ªÍ^Yn{€E­ò•‘Rû/¿ G–jw‘Ü™ºße\e?•ý¬Ëýì¼^Ž}~Ïd¤îŠÄN9ÃõQµ€lû(Fuèƒ^CVqÓ<[ãWž6ÈnÏçÀ1±+wÂíX¸M`¤…ÏM”¸.–‘SÜÐ ´›H>²Aç_)•¿(Id¿où2®Îu¶mb×rÖXô=ÉÍq˜‚ª¬ýÜ_N#ÏÂëQvWvtãFvóFõEgFJÝŸ’¾†‚L$>/Å:[ ®ºövèO)#;¶·Ä€@Jí÷vè¿Êv"™4Ú%Ê~*ûY—ûéÕö2œ:Œ–‘pa[Íɵ­ åÌÈIqÈ+åNuARcÏ~y ÷v#O—cç- |’ó6¸qÞóÀœ¯s»¼¼ÊRÏq¥òýõŒí\÷™‘'F5@Vn MåcðÙj#_-‚qל¸/ÄPtm:—뛋Î/q­ŽMÅMýD2é{k•)RùÆÜ:ƒš’edÓ›IxÝ:ž{G_ òkkGL.¨æz0B.=AþóÌvw¸R÷GÏžƒ£½†Šä˜?“±°õ`nyÒ&TÇÜ`d‹¾ë±=ùWj‡šp»õHN²6ýðl²WÙOe?ëR?Ÿ¦Q…pÙ5JN*Î˼`ÍI5ŠóKìªký©8ŸT‰ÜÃÑrRq>Ý2‘ðõg¤â\*¿C…v©87œÛi÷2©8ßEÄÉÛ©8¼á_¿«J~ÈÈ¿ÍWz%®»VÉHŹju o&’Šó=ÖñXåÃÈ¿½ßñ÷HÌ5è/’Šs›ÊH›˜ÍÈ¿Ý?´^?¸lpœ;îÀjtþ'„;íûpÍtáëá*#“®t†Jmy&)uND2ïùÊɇºðVyÉÕ}å„ê.2‘Œïh‚æ»õ¹RûF¸áÜos‘”σOn&\e?•ý¬Ëý¬ï¦‚^šiâõ%ÌÏmØ ný†q» qd·Üí&6ìÁUuÈCMLN&™Z 3ãµ22ðˆÏÃZ ¤ëã¹0®Jåö˜· Z*—¸RùM¾–ÁÃp‚iUŽ&BŒœÄêÙ0 ÷ÈV —cŒ©/WíËd<ÊÜǵ>)÷½¹¦´†¯ù`F6Zeæ® R*ßÖ-°ü~¸@¦¥Ĭ¶©Ü÷ý÷ÀfJ ×yÖ^ÄwËçr­SkYK§BnšU{'láï<'ô™Y ¿¤ƒ\©|‘Ãìñ2°±HúX ›òÖÜgUÃq~¯·«ggXËZpƒ-ñ}ÅFF^ëcû4®Ô}ë™·Ñ?/@ ó÷Ü€u±\­[Øà·½¹¿F„±6×re¤Ôþ;Á5Ó] {šßbo×Zq•ýTö³.÷3,¤Ì¿1Òãˆ: ­Ô@¶vßãeÜÏ>Їp ‡_†Wý)½„ßÅw¸º?2q²Åwî禡> ¿H è ‹aŸY¡r ]sÌ@JåŸú- êïUD2kR 6×6ãNÊÉfƒF% ¤Á;W–0¦;7½ï~v0¿¹ùð@óçÄȨëeØ÷A.'›Ú%Ãñˆ¦HJå n<ÛõÛ‹dgÈ)ø·†°å{~ dª³À2/q÷͆I±ÀÈ–?+0ùgH&)uÓ¬PÿpA ¯¾½Š½ò¹]G±×·+é~Ô Ûww)µß0Æ‹¹þŽ“‘9ç²Ûxƒ‘Ê~*ûY—ú©ø˜–´ÅŸÐ¶ ç/ÆGãCYŠóCýι—¾H*Î_MYÿOƒERq®ÕóÞ:ÈHŹTþ»f§1©³¦H*ÎO×;ÄTt¶ÉHÅy´® úõ¿ÇHÅyÓàÿúõU ÿ6ŸZÇvȬÐIŹü;²U ç÷ä ™áCüÛûÍF棼ÉmTœ[owÄ—Þ-@þõþ”îp™¨ òo?¯ì§²ŸÿKýT´`áìóÏÈÔOqØ»ñ·XÅž£د3rЏ-ß‚øÝH$SÔ— ÿ*u®s1ZÞù%'÷ÍI‡óÌÃ9lµ9F â^ þ1#Ïr¥òç?¿ÄÆé¤ä6r‹'WG7Îé«äd”æ)¨äµ6'íºy"ÁÇ‘¶\м2˜;Ž¥Âfõ9ÙÎ[ÀŒ¶5)•¯´¡ J<-Ègï»A%Èx(Ù"Ü_/‡ äü ËðÖ_à.έ»#©Ÿ77ÊAJÝw BKÏ ÷9­L¹ÇE¡Od‘løò0š'” ¤ÔþÃ5ñøãs¼³Z£÷®àþ§ïŸÊ~*ûùßÜÏÀu§?«Z û_ŽÆýä†"yP»öü<ÏÈ_ªp)­Šô{,<òRsëì|Œû%/šVÛ¹5ïNàùJmnûGßÙ׆¥\ûƒnì`Ø')•¿[¶7{â>Q ƒ¦d£ÉÉWíâaè(“‘we «Y}nÍ»ƒi_ÈÈå—}ðÜ¡ÈOÓÁ¶ɘ¡½q×ÿŽ@J廢GiþæäÚvM5LUFI›vÒ°`8 +¿qG´óC¼oÖoÎÂëx#¥îòŽÄè{½A_ŒAzµ!÷ç²ÃØ|ü˜@²aÇPhÍ•ü=s~Vwl+––V×{WÙOe?ër?½k"àù¤•H²æakÙÛýOo8>n²‘ÙxTº™p¯¼Þ‚z‹£òì¥ pò„[¶í,Ž_jÁu>ŸVÏü¸µkúbVõ5Fê³] ¹RùìubšÆ»2É»‹Ž°I³CdäÎß—1aã.9©Sˆ3Ç–äûì8<ÙÚäB\ÆŸ‡•Œìn? w¬È9ËÌ¡æv‹+•Ïhø¦U'#—?b9]¹m¢¾ªÈéq ¡#Vq¿Ì ÅÝ—iü, [b¸R÷USÎàõÊn ç,9ô ¸g|𭼑HªE­‚ч+µ¿…v®>.çŸU°Ùn®²ŸÊ~Öå~Þmˆ‹{ˆär™7žUöá¶7Û€ã ,@œx/—ýÛª/ãÑzC3‘<ã¤[¯s?Y&¡]±÷Ì_´ˆ6ã®xo„Ö]>12õ¼vž R*ÒâãÌÙ«#gLNdyý·pÛFà*ŠrLÿ DÄh‰äÿ{èšèÀÈ®åpèóÈœ Ã&ž´HC×DægêÅHÉ|7JYèbFN¾w=wHâú¯*d/. äõ0_~»(³NfäóÒ |ße"'¥îGæ&!.VäâTœñiÉ=Ó[ íè‰Ü^yLun”@Jö瀳Î.É sêG±¯-‚©ì§²Ÿu¹ŸÝÂŽàõ–A"Yµa?ì ‡rƒóFÏ"·SîÇ f}'] 5?ÊÖ¯+’“­·ÏÅ›ð"™#èc€KC®6®Ï9ÁÈѵQ(T½ #¥ò/]s“™dd‡àO,)æ<×Ú9wÌLD²MÄ!\›Ø—ûñÍ Ü¾>A »Å§#flWø3 vê·i5w2Û•Ê×`âöÅò#u·4Ĥu¦õ]Øç[ää+ÕÃì@¨¶Œ›–áä,Üᵑ¹R÷3ޤã‘^Ó_fÂv† ××í+éÐVN®oв—ÉH©ý_ª#®…*Èñõ¬1mf#®²ŸÊ~Öå~Öm›h!’N;—¢Ôäßj§¡µýkF®·Ì€Ãî¨5IÌçDnñ¨Æð+sã.ÚÜÕ-üÒãË X{ƒÛûP Þn±edœvÞ4ë%Rù7g7ƒm—,F†Îé€ù—¸ƒg®€ðDOäMÆ8ÙÜ1él§øsç. AåÃ%Ü}eýpWý#Ëô\ 8)•oþúöXÜ® È5õ!ײ·‰J$ûX4€‘½&3ŸÑžÜ5'=áüç@îìy¯Çfs¥î‡=0(ö#Óœ²3çw÷Ë~¨6»šI6ÓDõ/9)µ¿oõFœYUÀÈ_WáÍ_®²ŸÊ~Ö¥~*>›fLE…ΑTœo)K…sÙ2F*Î ë‡ êG©8ßÕe&ô^'Šó¬ Ñ]ÜN$çRù+¶ôFÔœ‹ŒTœ[NKÈHŹ'±uXWTœwŒÏÆš“Ïù·ùiO…ûøA çß7„KÐyFþŸü+£I_#‘üÛû³³ÿâ#ÿÏ<ó*˹˜$#ÿvÛAÓðbÑFþíç•ýTöó©ŸŠ^—9 ½F¤ÁpghlèÆ=—ÞêGU¸eC`ë«Çužf‰k÷†1Rsx?\½¨ÂÕ®ïu-¶¤ÿýHX‡åv\ýƒYlÒ/c#:|c¤Tþ[ Ñuè=FÝ,F#ïóÜ ççâˆå3nzÇÕh^¢rûåeˆïÚX$kŒ‡ Æ­ רø4¶×¿ÌÈóU~»ƒ+•Ou­'Š2_0òýw ©ýÅõ{«Ã9éT¦‚e©ÜT# 9zE¬CÃd¤Ô}c–Ëv„_gä„Elæ}îûçpjõy¹ãçe¸Ø8XRûN‚°ÇC²ë?íà<É‘ûŸ¾*û©ìçs?›ÿqE~CÕ£|`XmÄ]R6º–¦ÜJû]hÙKÆu2ÓBÿ }Yx¥2†Åq…±¨ùýR s\ü‘én(’O»-ÅçÑþŒ\»j%âgꀔÊ_oÒD83òåá»Ð°Èu½á ¹ª!Èëú!>Ô›» ¹*´ß6Iµ¼$¦Ú,S }Ï€×öGŒü–P3®T¾%OOBåƒ*ÈE‹ã11¥!÷þ¦?ÑO Û­ e+Ÿ€«¹?6ëç2Ò-ëæ¿ÞD)uRÌæ7ï#]N¿a³×UsÓW£Eÿ}©›…€Äf")µ?3ë*KûÖW ·”¹³23©ì§²Ÿu¹ŸŽ¯£Æ¹HûäpÌ-êÉÍ3óDØŸ¡\ÝúሮîÅ]Ⱦ—Í“YvÎl{£,iظf¿O äN«ÓÈüÖJ$¥îwYðƒÝð‹‘EªpŸ\d'| é!’n–›1u|S®ÔþÍ•ÉLsp:#®Ñ†AjÊ~*ûY—û°á4š_ê2èeÒ×wâ2ÿs(¹ É5˜™†^0r›¿öµ r—S(ܺ¶çfÎjˆÆ³¯äÃË­°{Ú0nu£S°{TcAöû%Âpo=FJå¹xê[&ÈI÷sWpÓ¾˜ksäX;?F®9~ ¿ÒÈÈó~1ÍéŒÔÜÞŽõÇq/fž@p\?ÎR1Ts¥òµÐ/ºMÙŒ´’ßÁP-/nXm0+Ñ[Ã]“…5r<ËÝfî ›·Oò¥¥+Ì.{s¥îTGØÍ† /ׂQpîœ «0ã[’@[áˆQB¹RûŸ»LCk¿ ÏÙ®‡ÇÈlF*û©ìg]îçÔçòI¤ëÐ,ÌÞ§Î͸qOœc)”'ãØåu\×ѱ(Ù[Ê]š›#ÜÏÃLqrŠª@îô·ÄçUÓ¸ãK0Ç:ŠÕ<+ÖtI©ü·nä 䑆@.ŽÏ€kZî–cÙÈ2³3'«Êa´»œLx:Z)ÙvD_ä?^##÷nóÂÂýCDrA¨1Zi¶æJ~ÿ ,ÇÎÍfŒ¼Öÿšö‘ ºÕƒ…q #TuDGùcnÉ:¬p{&®î›ÑG$¥îï ÓÃõsj -ÃôÁš«s¿V9#ªÉKô}¼»NvI©ýö;—ÀÔØƒ‘󇫈ã*û©ìg]îçÓôKPY rƈègW2²×†xÈÞ÷á~0ŠÂf=î°%þx_¸™krÜ æ‰'¸.G{À´,@ [Ù¿`ój]¸³#·asf2ת¿;2¬‹¸Rù‡—%áÚ9lr7[öàîXÿ¾›3ÉS)Qpu##µú´…G•9×vÚW–k­ÆÈŒDcÏ_!ÑÙ“°µÉ1®T¾e«§!'GõºÂäéãqð‡‘H*Î¥òwj‹àÕZ©8_¦{£–Ü•‘Šó¨+lGt(#ç²K ðaYž@þm¾Ó¦×ÑÊÄZ çýMÇ…ô“ŒTœoÚ×Çûg{ß6Ï£~7©8ÏéÉš|i''ÿv¿li8žÎï òo?¯ì§²ŸÿKýTÔnNs¬u©ÈNUlò™ÛoÀf4îeRWÕ-&Yp­^ÆÙFˆ9¸5<Š{½É´=¡-’&7¬qáLo®qŒ¦YŸadµd`N=RùO®­aºˆd‚û3v×ù©@ºÙF³Ï/{ÊÉ—v^¬kjFþY'`\'5×ýo"gÉEFÝlƒ1g. ¤ sˆ~½,'¥ò}tNÅå 7R«I2fUþâ~M gÒ+3ÉFñ^ìSzžŒŒ ÌÀöý{åd“–°èš"R÷K†|fmTDr½ê[ö9ð£@Æ^½ˆ_¹_9Ë´ ú—l¹Rû³“Øÿr ÒmÈ)öXôaäúþ©ì§²ŸÿÍýœü‚->S)w1+«bîÝâ`d5òÇ÷8ÌöïÌ}sì"–š1rè— ¼\?KÎuo¤o:"y-¹…M²IU9šÆß”‘æµiЯÝ-Rù_½Ëäær<;ñ6«þæÇdE+Cy­ª‚- yŽ÷ê&ÜîËm3±Y-vf’-ê=`ãŠO3Rgûp,zw‚+•OÅY<™×Z$<ˆ‚ñ©îÜ_™Çؼ‰k©k–Ȇ¥r»ëÄ¢³¾‘H¶Ÿ³ C‡uâJÝÕ­d~OÒúÀCÖiX1÷IM1òÂ\åääé0—MH©ý•Î5lWDC^f2lp×á*û©ìg]îçÕW,h¢\ ƒöÄ269‚[¼:S¬‚|×,ÇÝ®3òÕ–Œ›é.Ëäxùó&wôˆ{,Dz ×ÜGaS~f™Õ‘¨ Ž—“µ²<<±š Rù['ßcã}– dEûJvð;w²o[ônÛdpK­ÿmÌU!›¾ËÉ;=ÎâY×zÙç[tºØ‹‘‹ÃšÃòÂC®T>wÇqÔ»¿HªÿãŽ'ÅæÜE.Yѫ׌LÝŸc‚|wrîgäýNpu‹äJÝ·{Ÿu–_H·wÙ“$î¨-Qå¯ÅõØ‹s+Tä¤Ô~WaælTÙSc'\©짲Ÿu¹ŸÃ3ƒXåc¡êÍÖ®žÉô$óå>ŒìÖ.öñ6Üó1‘ð;Y$?oùã‚O(wÜ©¾ØXÝ^NÎØÑ‡'i dÔx\ýÐ]$÷Ý^„™&Z\©ü[{|eq§ÊI†óØ{Þx>Ö>ùÀÈ£õVãÑò,îÄÔ(œŒ²•“ic¡z7Ï‚ÌÞµö¶ “M"aw½ W*Ÿe{GDËD²×ÜUèøÍŒëi ý5 óÇà•cCî¼cÛ¡Ù]M$3ãºÀ®®äÿG¥¬}Z„@ÖÆ”²•»}¹KVŠÈ²VNþZu 7õÝRjñ»ÅhÚˇ‘jÇãÑí\®²ŸÊ~Öå~n÷=ÊÔ´Ú dÙ!WV)'í…8ŒÔ‘‰3#ñ§#·bÅqÔÖÌÈ{S¢½{2÷Ñ55rÆï‘Î.–“5GW ª—¿@›îÄ%[‘”ÊßE­Œ3—“‹wèA¿qk®Ó¯ezcäí÷ÓÐÏÆ…k®—ƒV2R{`>ŠŒd’΂P“éÈÈ =p[µ€+•¯Jg*nŽ3É]K\¹Ü‘›i2SF`ä}ߏտ:·T=‰5+ˆÈç˜åød RêþúÌ;lD˜³@vT+c×s=0ÛÙB$kˆèä>\©ý+çÍ„Î\M¾¶Î(Þ4€«ì§²Ÿu¹Ÿn·Ý™u¨šœ\<ú+‰M° 3þ A¿HKFI BŽ×NnÞãD ÈÙ9 ˆ.ýÃ]1Í—¥,2fä×,Âõ.w·cSd>ÖÉØçYÞ­ 9)•N¹®%w““GÆôAî§eÜÕêV¬ bääf0|“;V¸ í»#rûØBèt»Ê ™œóFµŒ¼WRŽUÞÊH©|GüL±]M]$Œ{¡ñ·÷ùq%R6Ç2R+`1,›{rW½4‚vŸE2²Í×®XS”ºoíø€Q™.][=aOª‡rÙü ø’ÕV$g<›‡>ÿús“RûŸ†ÅÀȼH‡ÊlìÔýÀHe?•ý¬KýT|‚Ÿd?ìýd¤â¼rVº¾þ×o§ÿ¯â¼ät<íŒERq¾òv'ø­nRqnsª;j—'ÈHŹTþ©? tÉ)9©8ïR$SùÿدӨ÷÷àdj"B“2$‘BBûóvÈ”ñ3ÏŽ!¤“ÙIš…æR)M4©$•TûÞ©$EH¨džç9q ÿóà}ÎZû÷à^Ö÷É÷ÛÚûÉëÁµîëz[Þkußòó°žR”yv‘òóÑcd¨™²I 5_Þõ¾°·¯.#Åòw)9†]u@f™@mc[®¦ûZ4˜Ž‘†ÀÌ­Üzi,pÓQ´rIdû—)qõínà»k÷Ö’dä~äŠåk“[Êf¶Ù%5™¹Ï0®óÎ ìÞÇ«ŒLhö–íëÓ d÷?|P0TF_‚×¥äû©Àú–#Ò[£;¦ ¼X@ Û±ñÖʲëv}¼/È~Ûfc·†W,¡äNæz2òèûTü^¸†ë3wÕú dûe6¸ä1–ûÚ¾Îw(–’dzZãÛ·'\¥™fè¶ö‚@þ}`!NÕøpÅòEø$3t÷fä`ÙyVçÂÍKÝY” Èx{7ÝÎÈÁc"Ù³'niÒ&‰éL|,!Åîÿ\u“³ºËÈ?UÂðáCWî“ëEh­ÿ†‘Åk‘þ×v®Øþ«uóqwîF–Ýݽøž ýTô³)÷3fw5.†F ä±/—0þä .‹ŠÃªÂîŒl|…𹓹ûrÍqrKˆ@¾³S÷$îãÆS¬ã{Fj:Õ±=Gßr?ZêahDƒ@šd`ËvšpÅò©$á¶ç Fnø‡F§‰ÜO&¢Ï¬½¹aÖ(¨;es}[•³šÖ¤dÑÓXö¬“#ƒÛá³îO¬ø‚EÿÂË7òÖ–u ˆ‘},Ô±áG%·o‚v})æ–.ßJ‡n7©6ÆíäVZâõûóRìþ”kÁx¬‹ŒT»åÇZ\óŸ×a¯¡'%cGÊÐrÄXÛ_yàÆåZ€Œõ:Ám:qýTô³)÷s͵rXG äèág1¤á ÷Ô‡(¸û{1òKxäü–ÈÍé!°I:»2MÝ›Ùÿ\!%Wµ5Äù³z ïZ„gϺs꿲q3–3òNkK¼_tZBŠåß-9Œ?FþÎÈÉc" œ²œëkŒô†*|kÛ ¡W¸ícý22ºè KŠÿÊuð‰eK‡KÉí¥à^¡ÎH±|IUúUs‰‘:˜Ã}d)·ysG4[Æ]Ýr9ÒnGr{¥‚Œ# tžYÎ c¤èû…’ÖAFNÞ¸%mÛqÌ“qº»@¾\—„Ër H±ý/O•Âtp#Ç]Ðï€WÑOE?›R?å£fÈ0Ö¹^ åçywSQTÀHùùæÔVø¹?#åç).jôž‘òóëίØ4` )?ËßeËA„Npa¤ü´¹dÌýOß?ýTô󿹟ºå¬·[# ¾ÎšKŸq‡®/EóÙ—¹)™¥X—}kpÔèh òcÁraÈ==V–3k¹ßwôʥܯ­W`Ò¼¶t ºÝP‘‘bù5ONAÒþ.2Råä8l¾ªÂ}¶¼/\3rúê<ôîYÎäXŒ§IŽR2ó[&|o„r×MÅ<F&X`¾T ¤X¾úh§ar¸e¾~Ìm¼¿ýfXq7¨ìAÞps®¥ª”·÷•߯7¥RRìþOã«h¼Ž‘gdUh0šÆõ=¹ïg¤p\œ€øê-\±ýå•aÐÒùÊÈ•B¤Ã1®¢ŸŠ~6å~f|ÄêzþdäìçŸYZi¦EØøh2#£zçáVUnmW|]Ð ä¤îh=¡˜‘íT' £çfnÃÕAXžÚ…û6¢*]Ôed›s,`v+Ëo9Ðï? äX}Œ^Ïý²'¶Vg¸íZÄ ¦>Š«¬•„ËAdº4»&æJÈu}]°ÇŒ´{ E'U¸bùvU&Bo³)È> ì¢Ïýéºþ÷à¶hŒ¢ùm¹anÊÝn'óõÃØ‘• ¤Øý\*oÂÈúîWк¢;wÚ…Þø4;‘«ÒØ –“UAŠí7Ñ 1²í÷Mȼt•«è§¢ŸM¹ŸK´Æ3ó /H; uFî^¯ ˜êÔJÈøí‰0i|Éݘö'ZÛ†32'iþ|}”»øƒfh2î–ŸÙ|¥lîóž¦P^ø¸€Üµ³TFîH±üsÙ8¼9Z,f]'£jàqîLç(Œ¸„ë÷# ë;qÃì21"´7#/8c†QOî†êèå¹s‡íà gW,_å\tÿÚ¤Q@<:|bä÷‡¡`o^qS†… tÊYî²W©L'-†ècɃÇ\±û•O.¡tb ÆÝZ‰‘v5òsïYl òð½ý¸³þ_Eû³Ì–ï;‚,ÐÇ«Ð~\E?ýlÊýÔ©Ô…°^äYkÎÔ纵ŠE铌 =y“–qk¦N@³¸‡\‡”Ù°ÊîÒÒCÊÃÚsË‚Ô,¸“ù³i;™”¬]PÏæ)–CûY0i#^ËÁÅà wHËtäé}•’mlóðskoÌ=qžjž²ºö&¬¿\’ï“pµ°¸Wñ÷ÃLFŠå?_@»² ŒüËQ CÇ~²¹ÎÓ½a®Á5·š‚ªêƒÜï§Ì°§Ð„+v?óò¬2O“uŸËñíOˆ‡Ê5¿ÇAÉÿ8#Åö;øæ`q ±eøár‘Š~*úÙ”ûy0Çö @.: Ê~úÜ›X˜ë3²™_ŠË2¸lün<»h r†et:厽2ûswöC«Å*ܯ–6°r‰ad Ž*7¶ãŠå/´^7‚r«° =Îu×*F˞ܰ£%hõù"×óå œO à«,ƒÃÃ:çqy]tùØ5 5sZrÅòEåa™¹#Ì‚­ÍnÞ.øºá0×WÝ ßîœä.™Þ7's=/Àñtb÷õ>”áò¥Q²¨¬1¡J\­Wþ~µŸ‘ 7ÜñþI1Wl¿»¬C~Nfä”sÙ¨ô;&!ýTô³)õSþwdÏøüÔ)?7õ8‰òl#åçAs3päyWòóˆ~Pî!c¤ü\ýz#»Òù#åçbùߵ߱oãR~þz™j2R~óâ$ŽÈR~~á`6Ф–ŒüÕ|êáép:”‘òs‹øõؼì:#åçßB·"ñò ¿zÿÝ%bMÊϳ’}Ñ×¼-È_Ý_}k'¼”¿ú¼¢ŸŠ~þ/õSÞéƒÒñveWéë”ÙlCn„r!¿SYXT†ÜÅ/™þà,ôÂÎr7¥Þ€¹‰÷ÔÎë¬,ã÷þúލkÛdÈ'%¨¾P—‘†—ΰ]‡¤Xþ]É“à ßäT­Xv°×î€NeXs;~÷Áî#¸ª»ãX‰š§@ª½;Æ\朓’ëWÂÔ2Òò£=dG[qÅòMpñêZ9lü50«¥Üèïp32y 'º©0®[„k6­mùðMsÚÂH±ûñv“ reÈi·§£aÈ¿?‰b¾é dj»fBGBŠí×r÷„o£d›—ÑpÈïÍýOß?ýTô󿹟ÉÊ 1ØDFQ‰…ö¹\ƒ–Qø*‡‘Ê\ÀÁ {¸7nÝBÝ× iùñnÄ”JÉ Æ£|·.Èï½7 äHsîßé}Po¨#%3mûB=ÇW EßÏ?®AA¶ ›ñ»¹6×aÁ!DëÇýrú(´Vhp;Õ³ñ–™ÖäÛ’v¸pr«„”œpÃêÝ©9=y£_pÅòVc•y¸@V&\Á~¡û,&鯆‚¬Õ‹Æõã}¹q á:(“‘YyøìË»?ò·¹øÖñŸïÓÿï§Å°ÞØ;øK7¼ê»_Bê ‚ތߥ¤Øþ«q)¨­}ËÈz‹xÔæ*ú©ègSîgßàCH½7DF~)Á¤¥Ã¸ÛffõfdCƒåk2¸_ &'ƒìOàÐIkna†#Êg3òÌ ;,;ìÍu:ÊÆ†ÏÈ2ÿ¦½n #ÅòÇ%8£ni[;&»bÚªæÜ”IhÓx‡‘ÇÆ¡ÙÒcÜËÝL |z5Yb?ì{¢”|±Ò‚ƒ•Œ\5·7f•wãŠåÔ÷²¦=ÈSÇJ›ÐLFv—Õ @¶q ?~cä郚p<ê.!5ƒÊYP­ #ÅîGÙ¯„Úä¾ ûë®Ãó“½¹û M ìp‡ÎÃÛ¿ïnB}¹vŒ¦LÍç*ú©ègSîgØ?Ø™‘‹.îÅ“ßþuý,4 ”Ó–Æð#ÜI¯0&9GJ.=‘ ëÖä•Kdœ:ÌÈ£Qûš{±Ð»µ÷pw4¢YŸ¿¬I±üºÇÜ mõœ‘W×»aIó*®íž0¨öðㆿØ)ÆÜÕ?áÔ{¸@>¾«† çí¹—žõB¬R0wì×Å~þ_Åò©Ëò‘}]UFfwÌ„mçöÜ~Å)Øu¥’‘ïç$ èeWwéw6»R dþƒ‰øa«Ç»¿s§63¹à¯Í€µ7mV›¦|µ€tʦ-a¤Øþþ•Þx“Ö d‹ Qø¨aÀUôSÑϦÜÏ·ÜÐ9r˜Œî´ /&[r³¾Çcšo£„LŸƒ/­i¢™‹i·3%ä¿r ×v­5²ºöÙ{Éz´Þ&ájGŸf`äù¢¸–§R,¿Ùñ=ØaSÀHoöºLá*­sGŒ$“Ûn‰+ìãr«¢/³5é¶¹ ,ŒÅüL”’妠ü¥ŠŒŒÛùœµú»Wôûdm"-ÚËÈ®Ñh´Wãzµ‹FóžþŒük}(â.yq϶݊¹-긯UæBzb/WìþÞ®;ðx£Èãœqeš6÷î ×¬nH #ƒ~ôÂ+¶ßj^.zë2Û¼¯ê©è§¢ŸM¹Ÿ>6¡þOs15>éÇ­ G%7FžLÀÓçñ܆¿nàf€ %}ÎÜ@FšŸ@ˆÀ“– wÆbUUîðÆÅ˜Úü3#÷ôŸŒ.^[¹bùµØŒÆ—‘Œ¬Ÿ¹æ…pÇLò†o:H£å!0þCŸëÒÓMZ}LBêLŒeKRw1r„n«[ÕU g Ð…KhL)–OM9 åNJ22Â+™Ç_ d‚™/ŠüyÅ%q=Ôúãkt÷iÅoØåd Rìþ¤f{`ö¼#HµhW\§ÁÝÕm4Fö´g䆩 ·³FBŠíÏèuî?3Rò©ÕFG%¤¢ŸŠ~6¥~Êÿ¦·[‚˜R#)?ªžNÏN3R~~kR9NÜHùy•,=õ¤Œ”ŸGfvÅïóî3R~.–¿ù›%(nÌHùycqÂgõ)?¿êw—Õ·Ía¤ü<înmš&¿šïÔB?ô­¾,ÿ'ß@ÜVÁHùù¢?¼‘e4ä¯Þ?üÝ Ý¶¨‚”Ÿ×,m3*­ù«û b“ÑÓ㨄üÕçýTôó©Ÿòν¹ë:õù·‹+JXp—(pTå^yW†ŠÄŒŒºëÎßrëÙ¼yo(#?×ëãi× ç,†îúqߎ鈘ף²j™?àÚZJŠå^[„‡5Íddc@>l¢Zs£Î°)—‚ÒÙ?ˆ™ƒ»5ëº=©g¤Öâëk}ûÞËîµ Ëî8ÂOEÂË'£àUÑ^FŽS¶@ûâN\¿®þ([bRut$ºý4çÖÐÙ!Œ´r-‚ÞÇ(îŽYA¶õänyC,”@Šå»íPÀ:ÍõÈ1‹ŠYÇIó¸«ÍNbLµŒ\>$ƒ×Läæ>sGY÷ × DŸi)v`Œ”Ì¿lMnùq–¥Þ6’V“ŒÑú±Œ,Ój ÚFýþòÃÒN …žùôº=WÑOE?›r?ͳÓqs§.Hã3'ð P‹{W,wêÅHÙìX\¹‰Ûj¥.´“‘l¦ÂN·×¹Ÿ?ôï Ùql"6®êÃí÷nZ¿ïǵžá §Õ\±ü1!xwP KµbÛ3W®Ý™Î¸íöPJvuk`Æú ܈žWáßl#×zÞB†þs YØpJÆÝ@–x_„Fx2#EÿþUV±(}ÜmùŒÝMJš>‰ÎdFºT‡¡MáznãDg~Áý¸:r¿ÔrÅîûÜ­dYì$äLóV®ìÆÕ©o Ç9Aétt0<ÖMæŠ~ÿµ¹ˆ²å5Œ‰w‚Ÿo*#_îYŠ^šï¸½Ú @½© Hëéè|Ô—‘bùïÛù0×Cíù5>ˆÕÇæ–$Ÿ€“ÚIt>½^<åí`†•ÍedTÿ¶˜ÛЊk×j-FuÏÈ1+Bp!3ƒ+–/å¥&rfîeäô5*p/åÆÍ=„ KUÏNAã׎Ü^uîxu†‘]¯Ì†Ì»_4`®: —‘³wzÃ?|wÃN)ÒçyIÈŽ*Wáb¥[@Šíìê‰c*•ŒÔ¹ãƒ­5@þ§ïŸŠ~*úùßÜOõøÞˆ3Ð’‘»µu±°Ç¿^×<‡ CÞ3rîÚKP¾s…û{Ü{vWv|ù§ØZmABöf4«½1è¤WHè³å=¹öÃVžyŸ+úÿÿ:–òÚÄÈF¯,ö¦Û~îP¥“iÐAFÚI†}º17)¨ŽE*½È#«O²^^™Ü]!p®Œüc­¾upÅòÙÑzË“ÒíFÝáúo?Ž×éA·Î‹¸s  téSFžÍƒ»ÃÌ@ŠÝvÍåS­d¤óonXjÉ=÷±wÇ dà”KH™/pÅö¿«½Ajž<‰–Çõ¹Š~*úÙ”û™ÚÉiš2²Ù\U„Vhpí«aSŸÈÈæ¯¡ìÔŸÜ*IJMÊNÜ/ùLIõ,×,õ0R$ƒ@¾®HÃÎóš\!Ë ú\ƒä5Óxž‘bù¼ËÙô¯Œ ½uŸ-ŽpÝT#±Ê{ˆŒ4úº“rs—`«·Èý®²ýjg¥¤ï ²'´’‘ÛbW@uý!Ë—kÞ·e?yøƒ*6èj€||¤Õ­¸)îå¼î)#ÛïsÃk+ù‡bgb«¹ßé¬ Úo,#·ßׇæÜ‰§ ÷¦N û]‹ÇÚŠ®Øþ.E“ô…‘Szá±V,WÑOE?›r?š㚌|Þõ³hÑš«¼¨ _ ÃÈKùp˜•"!U¾}eýŒ?0²E¼ºéükŒk:$)—¹¦ÄÂic0·>Ò–r?j‰"ö’+–ßyÎ76ªe2#oª¢&=‘Ëš»àkÄ?Xÿ¿ö]u³÷nó69ÌPJöºÛ ê•븸î +û:ŒsEdü@)–ïšK'x ×¹ZßÕ¿õæNjQ‰=cFZ\Ä~ï@îÖÇÞ;é(×´`OÎæŠÝ9aÌÈȶZN°KíÇÝõ=+Œrì_0u…”Ûß,ô$ÜßN`dÍÅh,2ÿWE?ýlÊýÜsñ![›úC ·©a-íÞp—ú¡ä”¾„ü ÉEÒ­+Ö¤ªëH¼×>ÇHßÐñð¯uã^5õ…–©?7ÇÖºOJ¸]\GAúŠ丶{04æ#Eÿ~Ýë “§1Œ{´¦ë‡p­#֣ӧǶ ŸOåpw9@ÈØöÜyß›&îܮí°úH?ùøjoÜM='bùLk-`¿Ãd‚ïh´Û2€ÛPq7œf1ré…"œÍìž>†~Z uŠwcýX Wì~™~´5–‘’#ö¨2ìÉú¹:JÉõæÕP}½N ÅöÇ „Y\#S†cîæW\E?ýlÊýÜõ´’}\u[ í®žcû~»Àý6;õ¹ƒ%d×y HäqÃG™#휺"gZž„üñ}7„uª ¿ÙúbÛé¾Ü‰÷gCUÕ‡‘gÛè£eÚ ®XþÂ84*y0rñZsLy·Œûñ¹'òú† äÒ d\á¶õ2‡ç|®Cˆ^Þ¨åÞk·‘ü¸3%®p¸ÛLFŠå3n;A]MAÎѽ¸?e§±¤è±„\ôæ8^ϩ喺FþÂ^ [ŽƒÑëKŒ»ßqÛèЗ‘ß3V¡Ø«;·ëÔJ¼ó«ȵGr1bFk)¶îÊ#l5A>.ÍòÄö\E?ýlJý”ÿm:PÌ~Î9-òs¶;÷7d¤üüwó7Lg… #åç‰S ,wHù¹ç»ixàbR~.–ß8yne¤üÜÅ!ÎUi)?oùˆj_Hùù„¾“a¢k.#5ߊs±( ;Hùyi¼Î¶`¤ü\õÂ^¼Ëeä¯Þ9c:?ï,#åçi«c”ô^ u— ޹ÏÈ_}^ÑOE?ÿ—ú)oÔ±S8;ž‘é×ò¡ž•Ä]³2ïҹϕÐÌò ·øv1l^ûq½—ç`ýûNŠOb¸‚J:þ,XÊY» ˹¡zánÛ\±üºvWѪS #­Çß@çN¾\ËýÑØðm÷ÊÝp¨jìãÆeFà¹×2`ø JåvKƒÙ ]@î¼ñ¶€qÅòmÛ^s9ºÛQH|ör¥c êæÍµÚ§é\ûõÙuHýqX¯?€+v¿MÕmõY*!ŸdÜÆã§¹÷6dãΙŒ\Õ!û-âŠíWÝíì욈sê–ÜÿôýSÑOE?ÿ›û9´G!R^¥0ÒÖ©Íwÿëz»tDú?ãNŸ‹^?r=dGš2‚{P‚²nÜïšèwÝž›xz5¦sçÝíŸCAŽNó†IC'®Xþç&u¨±udä¦Û7ñc‹ W}k.-äFöÅèä7Ü q¡áþœ»eV8ªƒ<—šÜ-}° 6‘‘bù®7KÆ…ññŒ|¶)L)—ûû‰d¼{{;tp&޼ǽ±"#ÌÞpw_CÍ݃\±û³çÝÆåC¾Ödcå-ܽêšG¶Ñ9€Ã¾ Œ<ûÜWg7)¶ÿZEޏ1rÚ?´ê~…«è§¢ŸM¹Ÿ•Yeز.‘‘]Î_À‚ÒX®‰s®7{Ë]Yy“oqw˜øá³íY®·[ú×¶Ùk“.'ÿ`äÏ•¡@g}ö [y˜‘§—õÇ`É®XþûSêáN‹‘?¶ÜDÿëå’9¦Bozk•gNãûie®±ç h:kq§;"ì­*7¬ã4Twia?›ÛËg¤–Ç6¥Œœ=«ž/rW<ÏÇ%û§Üðسèxþ÷ŒÏ6¸¿8ÉU×Z‹eÅš ÅîWkÜ‚°úP9K¯ëhIÉ—Â1ÊÐä’aøÊŒ¸bûk\bÞ¥¶ /}HFäf=®¢ŸŠ~6å~z„]†sf8#µÔ«1{©×gm”rnŸ×à¹0Œ;±C,œvyÄ;O5u¹·J’Q Ý‡[ó0–Û;rw‚¶jý¸¾ú.˜}Á„+–ÿ%µØ`¾LBÚ]Çøó{¬ÉvZ¥HÒúÎÈ'Q•he^ÍÕ]_iÜ#n›™0{E"wÔy7Üìb ò‡nN­oÅËw÷B1ô>W0òÕórLmSÌÝÖ§Óõ*¹:«qøöq®Ñ1¬Ê²úòQì²êÅ»?ú[ÆØÏ•’çÕbä÷{¥ ™š-@Zù X¼.•‘¢ï†Rdýì2§û9Ä?¸ÅHE?ýlÊý|Óî®wpcä}ßZ4è®å¦ß¾‡s‹¸Õ« ir{w;ƒÉ×U@~îtÚÍ«ùpØ”¨ÕsÕN£¢$kh掤΅\·B†‹æQ\±ü]ÁêÔd»PP2@Jl¸‚ëZ1ŒÜ~÷"ö™Ëýî}5“rcŒrñpE7®§ªºL:Ì}k6I®pÅò5 ¸‚¤§™Œ,ïsg"¹†™7ZæÃUÞ{½ŽåþnŒ©×¹‘ÈÖ æŠÝ_bVƒÂëǤäí½×á4®Œ›mž÷™+t"ýe;¹bûíÒŠ°ó­/#Ñ1¿ëŒä*ú©ègSîgõ®›ø2"#;_¬G|˜ WP®@nVª„œRt-«Æs­ Š0(Ï—‘Fݳ°Fg<÷÷„D¬è¿˜{Û6Ñ]ݹuÞ=±±y'ýºþÇ–\Ñï»Aç0IÏQJʬÏàþ=On[i lUFV·ÏE–ÛE ig›ýîŒú$Î[sW9O†y¦)H³ Þ¸k;”+–Ïsg-æ¹2òÉ«:¤?œÀ­ý|7ë¿JÈÓë/¢™;·âîzœïUÎHAëñ,T¤ØýeB5ìîKÉ™—«Àj¿p{NÝ‹ºç¹y×<ú¢RlÿËËQ0h#Mò‡kUWÑOE?›R?åÙ[oás„2#åçÍNárS )?״ùéc)?l½“J)?ßýÖ fËÞ0R~.–_{h>êÌ]¤¤ü|ZV ÔŸ?—òó7úÁØ`’ÌHùy¢Y(~zÇÈ_Íç¹¢_Û1R~>ôT .×>²&åçVþH×òWï/.¾ ¿ÉmR~¾jW :+ƒüÕýßýÑzj##õyE?ýü_ê§¼öÊñô޲”t¸ÙãkníˆcP>h,!s¶Áì…7¹/$z¶y+ö§3¡“Œ¬¸—ƒ*ïFÞž^†–×_p'8n‡,æ5×áØè>¤è÷‡Ý0,ÔeäÓCfX«§ÉÍs³Àe‰ 6H¸ûŠ‘šú™F^š@´@‹ê\±kÚÑZFJŠÇã¸T™+–ovÈFh-Kȯ™á^A4WåC:L¶^’’3þùÚ"Uç B1ÖÍaäÀC ˜×¯?Wìþ³µ[0|¹¤“çœë¸¡ƒ_1åœBFšç7²ýÛ€Û¯i•±Vj [”Bmq3îúþ©è§¢ŸÿÍýÌ\Û ÖJÉÒ³_ØÇÃë¸ãcÀÞ2FššÄÀp—wÔãT´«î%#oF!JψTT…òÁ9ŒTþ~å×—rz¤ Í«Èçc#oB*#Åò¯2ë…Ó£:ÉÈ‘>Ý`Ö±3×(i2jûæ3²zÎDä nܘCx b+÷úÿ†§³¹Î‹ÿÄëqÜk½£ðrh!W,ßÚ½ž°©ÈðÕ¾(Ò ç€à iã‘€’ʟܨ þÈÜ–ÊȤíÁØbߤØýs©Îh\Ë@¾_»#ükq?]d½ìÎÝô~4>UipÅöþyŸ1ÒíB2/mç*ú©ègSîç®ûOYO³UR²½G {{2·X=‹ôO2RùAN?®æY€…kÛÊHõÞpxò ŒžTŒ†v_$¤‹Ýq¼´¸Ë]ånŒ‡ÙŒÜ=5ÓŒ@ŠåOïÕþZ2rζÖ8ZÕž»¸Nßz2òYl”l$d» Ð冟@vN³Çö½ç¹>gâÑeŒÌ_¶ÝZçŠå뫊1ëc2éQ$n-Oç–'£wãdFªŸ9}//nxY*ªÝ{‚4m%ÃéJ\±ûv¹!ç…äHoèvûט!vpív…‘Q“&Áu=Wlÿ项`_{2²xEËÀk“—Œìß%5{¸ºµPú í?Å@3f4÷fhÚÜÇH»!ˆ˜Á}äº&¡ÃA>lŽEõ ŒË_~ÿoöXUMFÖ>yÅJ[pc^©ã®Ï^ iví›Ùæ×ü”=&ìh)#Çœ]'=®§Ô;•¹_Úí…åæ0Ë72=wUв¿J"´:ÕqŸåžÂÃ%‡¹ì|!:¯Mæî¹ƒv¢¸æ—ÑW³‹+v¿ã^/Ô?Òyç^(ÍÎšÑ µÍ›1rÒoYþZ\±ýj›‚ ½&ˆqMü°úWÑOE?›r?íëÒØÈ¯òɬÂ8Ö¾çkòØÈbÌø‘Û†Uàå‡ûÜÀÏ™ø4ÓT Ÿ~-”æË¹EZXUÿƒ‘=²’ÛäïÛ¿7œ‘Ú Ø÷+–?ÿèöGÛOipèk§u›:NÊ}µYð>‘õ?µ–ëxÝíULed碾Ú—;¹>ÏmÒºE¶Íì #ÅòµÌOFéû韂reYöì<Ò«ù›FîàzïAåßÜìd7Tù냻/¿#¬@ÿóÞue€%wå§nøâhø iëüÑ;{ ·Ò²%:OjÆM42EÞó§ŒÛß§!»úhƒ´SˆäÞo©è§¢ŸM©Ÿò¿à†쫾 !åç‘W± ß•‘òóO…ؤ§"#åçdgWt‚‘òóÖ۱ú[#åçbù_·{Âú«Å ¤ü¼wÏçLùÊ=FÊÏonêÛˆ"”ŸwlEýíù«ùR»Ä#=ÄTFÊÏ/­©‡®jKFÊÏçnOÄÃd‘¿zêÝLöìR~>~ä(8·‹`ä¯î×_œ‡ñg’ù«Ï+ú©èçÿR?åMÕŒÄÈŠù1˜ßc"÷7çk0xïfM.,½ãÙÞdkC$®•ãaIÂ2)¹0­›‡0Ì{™ŠŒ/IRr¤$Vá‡i¸&#ZƒË?,z'¬ÛU1²*i lO=åjÚDFNW…UOrÏ7„ÞMM7ÂÐW½¸ÚÑøäeÊM)>Œ-×ï1R,_/ïìÛuFÎߨ6c½¹¯n^Ĩc ¹¹ü,T¼S¸k»%¡ý[k²²Mz=÷`¤Øý¸¹¬&çK¸ë{™Ê5M­b%y$äéQS¶ØšÛ?rj> \ ?OMÂÃûÇ%äúþ©è§¢ŸÿÍýÌ>|“þ”ärdT¼ÄmXS‡ã/ Èúùy9=€[6ÄK7NÈÉ%}0àx*7ºô(toÇHÈ©¯’ñ™¡½Î[¼×µ-2ü”e¤ØþÍ ‰XëÑäŠe§ð·´WÑOE?›r?cürPÿm @ çóÀ~·ân‹f};3òÍôÃh·Ž[§—ÃÆK2< ™½óÛÏÕÒ»†î‘‰ŒÔ¶ªCýb®aÿøê YûÏwÓŸŒË僚 ŽÇû‚Ô¼Ç5͹–Â;[*‘ã±_%–ë= =-rùüÃaÌ]~û¸…36ã#Wçà4Da;W,ß’æø{g­5i2Ãi]¥ä¢³ xv(˜»W;&§ÙD£6y8#M!äUqÅ»³Ä£$¤ýjw–µƒûYv‹ù„5döeöGX6Wlÿ„e%°ÐäƒÏçðéÝEF*ú©ègSîgp‰ÿ¿F ¤ýÆ"Ì<Û­( «^Ä2ÒÖ8jÓ*¸ÑÑg˜û“Áü²‘ ^þBJΉ¼ûÏ%¤rG¦Ë÷sslýptÈ!FØ= SënpEßOž»£}Š%HïV{6f8w¶ÁJXïȶš{àçÃýöà2rvbšm wÁÂ6П¼‡;¾í-vÒüW,ßëføÛᢔÛþ)†¹¬o ÐLµ)ÇQ‘ß›‘] Ã=¼;Èæ³P¾RŸ+v¿åtö,LB~éɪ:fq3#0ÍŽòê©~øbcÃÛ_½B†™>ŒÜÔ: ßš/ã*ú©ègSîg»ü³˜Øy…@vŒ(ô¬mܰ^GqvÐkF.iŸŠkB -L aûJJVßú çÌ™@ºJ†cÒ] ¹à`þ¼fÏHµq“qz´H£¥{ðb€W,¿åô8wnȽQõþ×òCpÛK*3B¡>ë)7Áp9¶Ô0rUw;¸.SÙp¡ ö Ðà6ž³Ámi)#Åò)é÷ÀNsò‹Sg´˜»‡›«q ®w2òJ˜ ¯Í]¸O# ôè5·âÌ1 ˜Î»?zð^öõ`¥„ÜÞ{?óúí)·Ú~.ª¥^ÙÐÁ _7×rÅöÛÏ:ˆÞv1rEoODÄäpýTô³)õSþ§Û¼²2/”ŸW´ÉBž¯*Hù9“Øaþ(”Ÿ/‚.‹Î3R~^Üß¿‡Ô2R~.–_%+¡öV åçq#¡.#åç ¶Ì‡ß÷ž åçéסôÞ ù«ùÒmTñrL€@Êχ¬.C×>žŒ”Ÿ‡¾ß‡Ak‚ù«÷µ?vxC3FÊÏÕœ7¡[¸¶ŒüÕýZiÎèß òWŸWôSÑÏÿ¥~Ê»kó~¤o9ÍÈfÞ˜fsœ»=ô"O áš­¨FðŠ\·3buKSû»¢Í§ÜyÈ𔦲JdØÛp$&büê¿Ò¥2 6kŠ%¤XþA]ðL/N «R]qû÷šG<"“zp+[&´ÚDJ®8¡Icò†Ã”-wæÎÙÏ`rÐBF¦¼í‹>·Ô¹bù¾ëÄãqKsƒÎ&àÑjSî©ó—P¹T c®bÛto®iõQ(Õˤä“l¬ôWJHÑï—~n˜Uo,#ýgºÂª£)w´·+4úH‡ëa° «àŠ~ÿga¢e0#ÿøX ›¹ÜÿôýSÑOE?ÿ›ûùÌÀ…Þ Œ¼ßo7œÿˆæ–YÕ¢Îa÷‡ÏM8<žÂ]=f?2þÒõR&?2áÖÕÔ!áa¤”#ÔcñlÖ»Öe2rºÅ-¼Œª•bù½¶{á€~¸@jóE³ÿú5 #›m‘}:œFÎ>UFÞ[ñ;žf¸ ¤éihƒ[Ö¦ŒË_ð,×l dŸ¡‡ñå(·ön ÎÌÈŽú—¡½cwöexå]ÈÑÇöcñ˜V2rçþcð; Äý+ä(ÆÖøW‘|­²ŽaѪ ërRPSØŠ›qîì]Ëòµi9JöVq7F\Àz?s®ãÒLì–.%Eû¹ËCËÈÁº»à¼Ä’Ûª8ÕùC¹úý|0ÅÓ˜+ú÷}ñUø\ÈÈgËK°¤ô¾„TôSÑϦÜOcÛeH fdè^;|ˆû×ÞùWÐùB°„4 ?ÿÏßÁ‰Ü°†¾CÒRià°k^àîí“…qá½²r}Vœö. §zD@síwFvÞy¯\ @Šö³M,Œ³2²qh´´nË/5ÐX§ËÈv· |÷ª„ èyÅÏ dä>o¤/Â5è€g úÜ;|ñæS®@Šå›0: ¯Ž~`ä»EGPã]ÏÝ7¾õ«¯ ¤×•|\îYÎ}¼8_öue¤ÿÃdX><Ï»(j;œGZÉHë[QxuW¿§£”¸VùûÑÜ!E Åö{HÃú' ÿñ:Ïf¤¢ŸŠ~6å~Ny3c½C9õÐX ‰à¾ø"ƒµ•„”ËÆÝÈmÜRË!ØpÒ‘Ã.iAÝ+»íÄQX%qÃCR0;ž›t<¿}{Åu)Dê¥`®X~ÿäô=tQ ·Ú¤cï©ÛÜlÜÆÌ«æ²}f-.G¢€œºi7:`2Òéüz„F ãHÁ÷¯*é3ó">VãŠå³4ŒFžý9Fªœ Cï-ÙÜȽYOÌÈkýÒ±¥(œÛb ÇqÍ@Fœ¼+§zFŠÝß3à µGÈȆ M0hkÍ]ü$úŸo[[ìÜ,c×ërÅöÏÔöÃϤSŒ´ýs?¾mkRÑOE?›r?×§ZC;8š‘1¼¸’½ÇàÄÎIÈ®kâ0èˆ#«Ÿ7Ç×½õÜ~°ã“Zƒ<Õ6›´¯3²~g†ßáêL^U—p­ûOÄ Ã^ ÅòÇ~ÎÀ¾²·v:m›ËÈÊ—ñ4ßFJ>šu—p}'/EëýÆ2RV»AWÛqw¬¾¸7¡iô´G?çpÅòŸ€×‘‰Œœ9n/VEæÊæ'CwèZLMÀ§ö¹yeg¡ÖÁŸ‘SlSpäÒ®Ø}ëò ¸”a-#[[‡§Jn«›Hï;C 7iWã©N WlÿÐ)Aˆ]Ö ä «h´Ï7ç*ú©ègSê§üo­ƒ)ªž§3R~>§å!œrZÉHùùï7Ô ¼¹3Hù¹®ì +‹)??]µ #þR~.–ÿœ÷I´óU•‘òó½Á¹˜–P[@ÊÏÿ½:R~>}n âvÈ_Í·ý† #)?w5NÀ2G))?ïýÅCs¢ù«÷•ÍÖ€µÈHù¹nX%^T ä¯îÿ MÆØŠ> õyE?ýü_ê§¼­k ÄV&šžPý4›kü$ïÇçÚ'ÂtN°„?, ÕÇZÈHUûXTåTä·YØ`¯'#7Ý:†iFÜ…S`?¼×*<#ózpÅòÆ;bÊ‹Ž ‡½wÄk]n ³æâÞ^+®þtlÃuU=€5=¸£1ìH#¯Ï8 ëñ¦ Åò9-Á§sjùøE9‚“´¹P ÀÍýÜÑ8„ãÝì¹í®xáÌ_QÜÄÝÉx6Ñ+v_§G ÚEŒ ËF•cäßÉÜÕžÅÐ<à%%»n¼·Cå\±ýŽ·ñaUœ”T[[‡fñò?}ÿTôSÑÏÿæ~*;è`Ëø4ü©Þ#CŽrû­HÆ—>Œ¬LLçÜÜþߢ±¼î/Þö(&\É“’÷JbqÚµ“Œ\õ4Ú­_äý/Áhæ|‚;J)VWçII±ü¬|Fõÿ›‘íµ7"Ýò×rO(ÃMAŽ‘ÄëÑݸù?Q¸Ú‚;Ö0¯,¹£NÃÛ ܈“±øì‹‘bù~¸Œ-z¦9|t5ÜwØpçh'`EìTîÛ—°ËÍl~3ßâ2‹¸£r“+v¿jú,yphÙmj%F&-å®^G¥ƒR2ú:&':qÅö缈É^}2zd\&¿‘’Š~*úÙ”û¹ÕÖ) ‡2ªãìð çîßt[ÚÅ2r§yNûæsÏ=އ†C$äï.,¼ÔyZïºN ÈUEñxÑ(%“æW£Ùû­#ÈæïêýNJŠå©^‹WOË9¨ÕJø^ËäFõÆLó– _}Ûׯ:Ff½Œ†ß3¬K£u¹e“v¢Nñÿع ¨úí{øÁ„ *"*&DD@ú» ˜朳¢bÎ ŠDDDDÉHVÀôiD "æœ1g¾÷ìýTõpʺ“û£º'ŸÁ®³÷ª®& òÖ…ŽXa˜ÁÊå‹©q#®L—ȉËo!E{ë­ŸŽ}/°³küç=`ù$¶pÆY Úô‰õ:“„¥ÓX¹û~Ý á\~Ä–XµZ ï§‘ò.àMÝù.ä8f¥Õgeû_­'çmɸea8¼©³ 5ýÔô³"÷3r¤@·Íû%R÷õDßôeµÄY¤:ä ÒéÉEd4½ÆZ ÎGt Öñò-øw2a-©˜4ÈŽtu;Ógb¤þÆ,4F-‰,n §¯¤\þîéSëyPy Ç"Û{›ÐlƬMa]Z€hC?öeÝ辬²êp4·¿#È“ãjãÉÚZ -‡##Ý€•ýûdÝ-$nÝ%‘{Ínâ×°¬õeÄ \Ç®î_ˆq'½ÙìcQ8:⣒ôÎIB`©ž åîO>s ½¶%§“µG_‡Ö^£ ²í<ûeGêÇD }Õ‚”Û|k¢•©‚TF¡Ã›?¬¦Ÿš~Vä~ÖˉÇöJä·üÉpjèË]š¶Î…‚œ×ÿ* ^œg `Ã×§ rÝÕ»0øg4»cÅu,-JdƒG=@aP}VÿYÌ^²ãÇX>ºH¹ü¿­†àÁ™Y‚ô× +³;oÆ`ŒÙ¹Õ²#VU߯ÖwrÇ(ÿcl]»¥¸ãÏj¹õ…yÍ€Z±rùöDC{e¨D¾­yÚõ¢ÙÈŸEhΞr úãN°Zc²1zd  ¬¼ë<Vöï—+7 e½:ƒ|Üé çDz—ÅaÎÌë‚ì1ñŒs«‚”Ûÿdè1è›7Y<3 ë;4f5ýÔô³"÷sÇù0®æ'‘³ž,Ã÷íXŸ·E˜1ö„ ;7-©¡aìs£"d6jhG©{Zç”=I+çû°VþÎ “,®ÁiM ’¼34Ök> 2ûáa˜ 8ÄÊåwK°„ù«V‚Œ_ßÁRuvo›ú(ôfWÍþ.jýŠb+9ByŸ½lù‘¶ØTÂÞ˜«U£”dð»`1ýþmV._¾<8/9"‘!ë/Bªɶšp[³ÙmŽù8R–Ïf.»›#ý9Þæ,»ÿTr÷o#áè½ Ráq¹?j(É)vç`P·:H­‹¸t¢DrûuÃ%ìüR äÜ3øZv[š~júY‘ú©þqN_ÓnÁ©>·8R€[>^‚TŸ—\N†eòt©>ß¿4 -¶+IõùûJ³pèÒqAªÏåò¯¬Ò…Ý^(Hõ¹äð@œm{NêóAu0ØL¤úÜñ·—0¿êדüÛ|Êò³0w9(‘ês“þÓÓÛW$R}ž—†Ž-‚üÛû-]ï¢tb[%ùÿÜ–í“"È¿Ýïö$z-ù·Ïkú©éçÿ¥~ªë‹›¨µb» Gô¼e7°M·ÞDéÖ-ùþt:%¹°{ªÙb¤K ÖÞ¦z,<ÀfkYáãЦ*²za?´l÷]"×<¯–»³Å"³Ø®ˆœÿBI¾m+`ØV"=¼aª“•Šìñ{>¬·vdÇ]®…QË$rÖÅA°)ÜÃÊÝß7~'òûޤΠ¨wå9k³. _Jž²•ç±%û;+û~¸á‚6UV‘Ek‘²¦H"ÿÛ÷OM?5ýü_îçƒÜbŒý±TOÞÁaÄlö×Ì<$4Ü ‘q¿²11y[X©ÆyÊ֋™º*2nú\Ä×É”H×ñîxz€MŸ°úoê«Èç}v#¡—+—?ä|6\æÿ–ȰÁYHlù‹uóèˆ5Së³kOáKo¶´C\ Hc‹ò,ÑD8«ug.°Ý §aÚö¶*R._›‘ƒ°"s†D&ß…6w¼Ù† º·R‘†ž‹±un]vÉw~HHûƒá(ùÎÊÝo>Îÿ y'Èâ-Ûžòœ‹J›JØš³áúAbåö¯9¬Í.ÙM+ë"•¤¦Ÿš~Vä~* Q¿ïAVÑ.À“álP3%ž-·’È3COàeì7%y=¤-²:´R‘e šc|˜›Ü!ÒRw‰œvþ,}÷±ÖéÛ¡×ÃHEþ¨³Ǽb%R.¿î*,K¾I䘩˜ñžõT4ĪÌ$¶š}èØ°Ò'`‚é¶ÿ÷-x9)˜?ÿøwW‘1ë†àÅe]V.ߤý“0tA’D¦Mš6í X›½+ð²æ/6mÐfl¶¾Å†¾‰ƒã=#i6&öY¹ûFç¶!à·Èãm(õ®ÆîŸ‚ÈîA‚Rˆi[v³rûû N€¯Å/iPr ‘®ž‚ÔôSÓÏŠÜÏÕ•/£©è'Ȳ¨‹øz]ÁÂ8 7¦ V’Öz1(H¬¡ ;:6Æÿ&*R¤´À…UÙyvç¡·*Y"ŸYå!½Z1›Ù;£l)ɃÛnÀúþ(V.ÿSã3ø8¨D"7=>ao±wv•ŠQ°Á[®Šá‹±AûöâCV2[ŠƒEרɗ6Ãs÷Q֪込_ÎÊåÛd<Ã-ßH¤ï÷¹87²šŠŒ>» fžÈ§s½QuÙ~6gA¦îÔU‘Ýâºv„DÊÝw=ä‚5j‚¼ná‚! uÙuS6cU„ GÞY5o²rû7ØŸÇ”Õ'éðé NwÌd5ýÔô³"÷3íû9\ë`-ÈU2‘¼Î’]é¯å rwt(Ný3]m¾wÁ–w÷%rNï¸þý$[Ë#EÅe¬§ut®¾`õUwÑ.°&û°ÁYìwSIÊå_t"AÛ/IdΙ£h1õkrò¼Ø\÷›=ö’(w8Ç& ˆÇöž¿ØÅg¡ñŽú*2elÞûId¥ß…°hy‰•ýû­|.¤ü*rÛ¢90‰3`m{`UÍUs-‹ò²3òcj´JIöN8‹)ÍK¤ÜýÎOœq'®È'qÎX•Û˜MÝa‰ç)µØ$“ÎH*kÃÊíÿî,£“yëFÞòa5ýÔô³"÷sDA:^¾±ä‡Ù§à¼»=0;cCX»¡è19ýXy-†­<,‘÷ßú`uI ›úä$/:ÇVONÀŒ´Íl¡KJš 2½Kæú”³rù¯i'¢s|ˆD*þÿÿ÷5Íu~vGô»—ÀÚ7ù-²b°'$#LÛPEN”ˆf¦ìl—L8ygHd¸2cŒ²rùê»Î„§O;i>szš³W%âÖ3‰\2ô4ªÎÐgöÞÄŠÜ‚<~r¤Ü}S/gøeëƒl°Ã×÷°CÊ{Ààwg¶Ö…È|fÈÊퟑ‰Ê¹%5 çg°š~júY‘ú©þ™W‚û¯­©>o¸!¯|¤úüûÃHè„ÇJ¤ú<¸íxëû(Iõy§ûغ¤Hõ¹\þ·bP”?Y"Õ祋Ãs¤»DªÏÏv‹ÁŒ£F*R}¾âO4´Cìù·ù˜NDìºî*R}£,ìêÙD"Õç̯àÒ^ òoïGû9㣭1Hõy’ûH„ëýäßî?fäÑa§ ÿöyM?5ýü¿ÔOuƒê‰ Ëg)É~¾¢ìØ6?'1aúlóÞép¾=ÇŽ4x¹kVÔYÏn:¤J&ì ÇObÊø"%9±khn,‘в¸tò§‚¼èöJ$Lÿ®$åòǼy.ìE%%™ÿÏU1ðM¶ú/ltï "W¦{AçFvÚÞVÕ䯇±d÷Ö¨ÏI8[9Ú‘~×pC«† åò-ÎÙ€>ÿùÛƒl`#6]èÈn=º†Kdå‹ÛÐÞ²ŠŠÓÜOܯrIIzÞ%fMóbåî/lƒ¯þ‚ìa ÷uÙ?–+á0ÌäBå¸ÜÖš•Ûé¼ uÖW‘ <ðlV±Dþ·ïŸš~júù¿ÜÏÌ]^âSÿQJòhuw¬gÏnr̆ý€‰ Ò¯(÷·†³%]§BëQw7ŸÎ€}nOVïÞpo»\"Ëw¬F·§X¯f{…ÊÓœÕï“*ʆUbåòQ‰ì®Ó”ä¤GŸ.«ÙïCwA¯¦BE^æ-.¶lA’’Cys¼3RZÜfûÄ݇ÃÉï 2öq rÒX¹|ϧm„J§5ÈÕ%ëÑЫ n½ÂFOEº)ÜaÙ¥5»wîáê5WI~»dŒõoµ$RîþsÐdùÊhWÿ™‚<}- V€L,؈k?;°rû¯¶=€üã.y½u,úWy®$5ýÔô³"÷SÔß!¶Tï¦$cS·‹°ú†lùŸk0?£RnÞ·1ßý"k6i^Ø[‚´˜¶KŸ·büvÇ¡_¿$rÚsŒl "ïí0ÁrS‰tÎ_åÁ§Y¹ü“mÂÄœ¹[”dIì~ᵑ-‹wCÁ@sÙß žÍ[±Ó—á¸Rä­›s‘´ØˆÝžƒïª¶‚ôYçŠàôtV.ß“?kðõ`Mv­@a³ùñ?ßË©‰]TdÊIw´jnÍ–èÏ€OÌ~‰½w`ã¹J*Rîþ)•)Þ¦_R1U›Ävعkít@.™…v¦')·_ëaê9uÏ Mâ PÙFAjú©égEîçšÛ…§ym%9§`‡èžù>ƒLèp£H ²ðÐxÄű—ÆÏà ÛJ ÕšŒÐäÓ‚œYâ‹\…¥Š|üÒ›¬z²W¦ûaöÁ–ìæp?¨vÙü«Ü÷sÛKô×Y¦$>î7Nbû»ã•ƒŽŠ||Ù ÖNw%²¯ÙpŸÛ d蹈êlǦޘ¿šÍÙë§!δ'+—ïÐZG ^yWîÙs°XKÉV;ᆠßv*ÒwäNÔNS°‡ã½ð9©3;£¹'ö=´ceß/úµDÞÓý ÒsgSÌy¾‰-ÙÓ §ouäÚÒF°g¬ åö÷þ|ý]ú)ÈL£‡(7ýlGjú©égEîç4{ñÕúré7Ì[4͈eK/ÞÇWk_¹,þ2Þ­eß6€„e9¥AG ¤ £óvãøKs¹5Ø 5Ö6fMÏìÁ䜬ñë ¤¯“H¹üÞ\…A\o%Ù`´«˜ZÉ”]9n6\ —ÈEƒ1°é8öÌðEø1ݤ}÷¥8þ ›÷Ï|~oÃ~™1ã/ R.Ÿ»Ëddn>,Èö° wf#Ê\amg«"lGíH ¶ê×8dcÂZFx¡dð]‰”»6§!¦9(ÈS êBod[vœÙ-lh¤$‡g‰ìÄU¬Üþš=ïÀÙÌÐŽ|Ö&™vcXM?5ý¬Èýt~è'6uôÎ u^‹ï)«Ø‰ ®£×…) ²sÏôòƦ ªóU•¤îÔâÆŸ"¶Fî¸Nx$‘Ç\àX߃µkpC‡/QËn`îÐî‚”Ëo“ä&æyÕT’Ÿ›{‹Ú÷ŸfçLŽ@oë#%iÜ>ÖnÅd票ô¤ ÈÊå ´û‹ Wt0ǘOé òSÁSfqEIÊå«zÛþ9éA<þрݲ};î÷m«"›º"ã`SöíÏÃ0lf+‘¦ÏàØ6=;Rî~ØœšÈHÖR©YÕuÆŽÔJÛ.ìzwT’áEí¹!¤Ü~iiê%-VZ±Q(ßÝLš~júY‘ú©þ‰˜- ?LÏ ÕçãÖŸ‡Å¬í R}~O;Q=«)‘êó7c¢áTz]IªÏï·¹ƒIõ R}.—ßvûQu™2ƒTŸŸ©Â˜1ú R}®ë:VËS©>ÿZÃGd×­'‘›¯‡©†;¯ Õç=µv¢ë(m©>wŠ.ÂÛzY òoïšWÂ1·ev¤úüšÁ-qìÕÍ òo÷ŸÜã‹:™>‚üÛç5ýÔôóÿR?Õ=ìο5•ÈE験fÀ øÕj¡"&Ç!S¯>;¿’ ÎÝÿ‘ÈÚWÎ!öy¹ÔÑmz¤rJJGÔ<Ô½²j~Oög}ÎÆçïuAÊå÷àV69¦$Ô?‚u»ïf6g#émœDÎÃ76z¹vdµv¥¿³«‚,áád²Cï.О•Ë×¹êEÌò*W‡æafãæ‚4~F˜v×–ÈE½nŠç÷k±Ÿ3\à¤LdDqOüº¾€•»ï”Öë>U—ÈÛ;,ÑJÕˆ½l‚å YcïgQÇ?•Û¯]d £àç¹Àvlô3ÙÿöýSÓOM?ÿ—ûÙ³]&î?n"‘Áý³á2¦k­Œ¥}eÙø[4š¿x(‘çüÏa©¨¢".PÁ§ó‰tϰÀ“ßÉäÁ6¸Rk¦D&šŒ€e¥ }VÄ>F¬\þÏÞÑøb¤­ ÇLŒF÷Jö¶ã øž·È´D¼éæ¡$¯¬Q@ù¹ŽŠwËÃ÷³ß ¬QVé s^[ã…^OV.Ÿ÷·Bt6î*ÈåÏŠ±=±7û«ü‡ø¨×X"û'ׇËoS6§Ù'¡Õ·‡ ‡„ˆßk¬Y¹ûCl°è‹‰D®ÖØæcÉn‰<'n69kBšÐÏbåöw¬¼ßç…J¤M¿X—{±š~júY‘û¹µí%¼ù]G"/f_†ËùšlNX† <ÁÎû Ï;XçìÓè5öûml2Otc—¦w@¬q.k=Üwª«Èíaqr´ Oý6Á$[{%)—¿²S4Ú­i.ÈÞ…ÑÐ Çú&c')Èð{«µ ÷gZáóáN*Òx¦´‹;³KuLð}P{‰œh^þ…Ŭ\>éëM4o3LŸݹþ¶hw[dØC"oÎHh45¹O^ÝS¶—ÅomVîþ´>0õ´—ÈÐpäîVûÏ51|v’ +ﮌi«Y¹ý—#ŽÁò•›DþúÏ{Tß%Þ¬¦Ÿš~Vä~7½‚\o‰ÜbP„G6ÚìÀQè–eÊiRŸõJR'!·[=aG}ÂጠÒ^W·æê«ÈFAMTbÀ^±})Ê#B%Ò¢W†˜3á+—_õtzà&Èmãc`jÁF79„£Õ±¢‚püým6îŒ-†•šªÈƒû!pi¶×ïç"øç‰´[¥ƒÌ§¬\¾uïÂB1^-îÂwü¿F.‹75¶Kä¬ZŽ8È~=ÝžÜJ'woŒmÆs%Rî¾õ·!ø¶y®DÆor@ÄÂ5ì‘­51BAúêÙàÔö”¤Ü~—Wñåx D& »Ý{BYM?5ý¬Èý´¼ZŒÌˆêù©ãMþ×1_ŽàÔ‚¶ rç‘8äÆè òèµôª¾œÍ³‰Ä£\vÛ;l©ÑXE.\gãÛªü«ïQ61I"'7‡Îû°²ßÏÒX»ž!È‘GãpáX1[œÿ³Ú moÂqrKvéíqxRYEX'ãÙ¦lºxc7˜úB;ÚŸ•Ë÷¤é<´+HßG71ëÕHÖ+˳rR$Ò5ÒÝVå³Z­–£ÛìwìÚo.°8Ù^EÊÝÿøv œ.o—Hã¡‘»Å‹{y0Ú&ų£ŒÂ¨}ÕU¤Üþ‚ž×P3"P"O¤æ ÍƬ¦Ÿš~Vä~Nï|'ôµ%RUtÿ\ÿ×KU’ Uæ(È*ÅÉhñ:” _¬ ÕAþž‹EKvÖ¼Qˆ\qK"W9oBöê86|Ç!t¬ÌÎ)Uá¦N)+—?¶Nâ6” òíÆD\Ñ*gÏ„AÿpGíçÄbKöð…˜-EKäÂ’pq\ËV+J×¾Qìä…¨WÉÊåóØ] GËá‚nVˆ˜ªCX%ûð+¸T"¾ ÂlJ*ò`ñv\µìwW<óïÄÊÝŸå;у÷KäWÕLD™f­—ŒF‹ö*ò¨ÇHXö°fåö3È@cC‰¼><Îéž¶¤¦Ÿš~V¤~ªj4½‡Q‰:©>w->ÞûÎ R}~Ç1õz¶©>ŸÞæîHõyå  {ê»DªÏåòoy•„ßýε¿ý#‘{ßæÚ\ Ò?"‘êóæ‡FCib¡"ÿvÿØÒlêf)È¿}^ÓOM?ÿ/õS]£¥;ñ{o¤ [–;#dqÛË1¯­|$rf(”ïbRö‰ÚÑF‚Ôßâ)Ö¸×eµ—ìEnz ×4qE»¯Ùnƒ'¦ø*ÆìÍb}Z˜ãõq¥DN3uÅ@ƒ V.ßµö˜î½V"Û‡êËÝØ—“¾‹•[œÙ]QτìÜùYˆ»óNãF'@ïu!+w_;f'Ne¿•Hƒºî±è9©ƒ*îMØ5Z÷DͳެÜ~e^kôðì+‘žOÇ`ÃlWVÓOM?+r?w<™§€½‚<,ífí¼ÏãÈE7‰\:3gCÿu…×ÿ¼¯³%Cë¶À…©IJò¶¢)ŽÍ£ [~-MÍJ²§óE%º òŸ?ñÖt+—ßÑø0·U(ÉwŽŸÄÛTWöÛ°nØbùSÏÜ Ñ>OäÏwyŸqNi×Ïa×%Ûlz2»yH¤Å«B´Îqcåòi´VÍü$òõ”è¸?Œz¾X8ÎJfoíÍ>M³Xûåžø±-F€î;wVî¾äâ•ÏïJdÜO,°-b·UùÏ;ìSì²KJoX¹ý†M·ãÔä$‰ìÛ/N™wXM?5ý¬Èýÿa0²ß{2Ö¬Ìíd‹f\ÃRm7‰ìºä&(v°õ‡ôÂÖq½ÙfsðqÙA6üm²0xüYI6è±Où´—ÈžƒÐÉ$IcTB£Æ7Y¹üMÚTC¯GIJòƒ¢>tŒï³Nàúþz L©‰‰?²v‡NB§ U­ßFaûXöóäÛ¨ýpµDÖß•‹–—k²rùVîDó¤£ùªãT5–Ø—ΉrU>»¢R¶ØYÌ~®[›Æ¯dçï±"Éu.+w¿»Ö-¹$‘]Zxãã6‰­oõTô™}޽aЋzž`åö·‡Ì~•Tdöõ“˜ýR—ÕôSÓÏŠÜÏrÿ]³ûv@ôg¶òºÛXµÍE"n¿Xî^º ÚÜdsZùମ¶ŠŒ­í+>Uê+‘ŽÝŽ ÛU¬oP®è¼þž Çôþ*Z'ícåòK"Uß®!áíö¤[†wo©"{l Ãø¡ØGû_ éð‰ŠV–çľ 5ƒSÙ¬ rÂñqhež-‘rùóÖˆw…DV:чJ¦²£¶×ÂÀU?yÛ®<«³§û†»Q »1¾#÷9ÈþÄb$ù49tðh;؃•Ëw)? 3\K%òMõשZ o…R}>sÔlZßäßæ{¸1 l´T¤ú|yfCÔëpN"ÕçO"6cVÑ=‰üÛû¦>þ¨³K"Õ篥Ëp!‘»ÿ×›xì(­.‘û¼¦Ÿš~þ_ꧺaEÚæd‰|¨…g}ÿõÉÏI0Œ²U‘™µgâØ“íTé´ÆÍ¤ßÕ[ølïÁæW¹ s“±¬YY>´^­`;Í>WØ·¥%"¦c7)—ß*äb›=d‘çiôµüÀ6ü©…Eº±~™…¢wvÖß%Z¬Kì«toѨº«w" ÝMªªÈ/Ê X4ü‡DÊå3®ÑÁåÆ µSZáóàÖl÷Üö8ºÓ˜ý³-rŸ5aÍë8áÏ'k9²+Bf4eåîw™s¤“O¿\ƒóÃ[R×㮕)ÈvCµ7s-+·¿¨y¦XÔ¯@»Â•"iR*ûß¾jú©éçÿr?':êÀéä1‰¬y¶Ý?ÊVý>IÍz¨Èã×—ÁÞ© ûêÅ5,n Èj³s°,%„5o“§\Y ES¼Ù‰½²Í^"Mf{bÊðǬ\~©ÓˆV•@nw9ƒ)>:쪭qâÊ÷ ò~ž¨÷9žýlì)êõûÁ¦3ÂY÷×™°|%‘²“0°{'%)—ïµ[s˜]0YìÒ•ç°Æ´ÅþÊ5Y×#mánóQGÒö!".]"§ÄEa”Íq%)û󻬙õÚ‘Y……pèÉN ÒÇ ™pÃõÛjK¤Üþþ_ŠE´îNAºdiÁĤ.«é§¦Ÿ¹Ÿ5}š¡Wf’DæãǼDöÜñupmc¢"}†º{±c®Á´—a‚¬c3GBYì‡ÃXÝ™XÊfdÇ¢óX}ùuæ)ÔðhÂÊå7p>ƒ™ÏtAꙜÁ[==öÐIWÑçÞ4Ù+y§Øµ6ÓŽT½8-Þ¯|ÈÞ:Y*bþÔR’ ¡†Ÿ õý·ãv¿† åò]ñmŒSfÍ@®‹k„N°ÙçÚá…öeA>Ú`†ãÝCد©'Ñœ-»nc&|¼ÄÊÝoßü |6çÙ‘Fäc•žŽ‚ì”?U®ÆI䡆káº²ŠŠ”Ûïéf€zƒd½z¡×¨®©é§¦Ÿ¹ŸÖ:âñ­x‰ŒÜf…F-ãXCåNX7©ª"k‡yÁuÙ#‰TìÁŠ=‡¹y¡œ¤@öзÎ81†íQ"$Í :޾G3%rÖ’,ÈÕÎ åòï^p3 š‚tt ÅôÙB_±.Á(ƒÌÜŸ*ª %鶬¨®³–¹–˜®²‘ÈsÆ q$Ñäý±ÈqëÂÊåÛx¢jw­Òàpclkõ[ÞaQÝa:[¿ Ö~Q Dz¡x_dÒ¦4TÕÒaåîÛ,¹ŒÓ‘f rèš8¿Æêe¸¡Ò«6*ò²Þ^¸}îÁÊíÏX9 Æ#%r_]¬¶þÁjú©égEîç뇽0Í6F"7º ~hvR‰/ž¾;Κ¯ „Ö•ìçF}à½_ÍŸ4Á/­½lO«Ë"èp{{vŒ(ÂMÙѺ޸۶#H¹ü³Ï@­ô «§bÖ˜–läì{b\rŠ’ŒÞ§nݵ%²Ã„‰¸ ØÉ¶(ߌ_ËŽ²2ú!²ãkAn™i×o¤\¾’¡M‘áûL†±úHû~‰Í‹ïŠÚÑþväÖ –44AIÚ%E`d¥w‚œ§ØŽ63RX¹ûõ_„Éûå rðÐóH½‹mrù¦‡X©H·ÁkÄÊ퟾Ã=}›«Èš?àâÔ™ÕôSÓÏŠÜÏñKÇ¡K³H‰ìà1µJÂØNBñvagÖ¤$>m)IÕÊ—bÖOAîøpB¬ºêÂ>;.ôÝÙZ¹§ÅÕÊØ»ŽÛÐUÏdøËùø2@—•Ëÿp}2޵¹åxÚ 9»©Èåï­%òb¼=´"–°Ó|àóù:ûûeFÖýÆzh‹Q·²¿ïà¥ŽŽŠ”ËçØAq‚|]Õ'ò<Øê5z ¾L!‘Ù§l°¦Ù^öŸ» VäÀEßEIBŒ‚”»IÿÂu+ÈÉ73Ñ®Q2kÒ"ÑÑŸ%òOÿdøïbåö‡ ÅØ–ÝTdYF$FMiÇjú©égEê§úgZž#¼ÂC$R}^}c"œFn°#Õç·WˆSR}Þ®ý q=þ¼‚TŸß2ìƒ_‹Æ R}.—ßÙ5¢š‚TŸ¯¾4‰¥{%R}û:mµtT¤úü}‚)¦›·S‘›/è??Ýï¤úÜxÌæeJ¤úümAˆ(,ŽÏ ÿöþƒmÚ­ÍVêói‹£±¥u+%ù·û“^F㣫®ŠüÛç5ýÔôóÿR?ÕÝ¥¸ƒ”{AnÙ{Í&e'4Ř%åJòGZg4Øh)‘Ÿ¦é`Ѧ7l{QÒ ]yÅP‰Ý-?Hd­ 9põ}É>ß…G­: ²YÖ!\«R.Ó"3<óIS²ÊîhV»¶DÈ?/º<8ÅêÇ^ï~\cµÅ£º*òÖÑ“˜Óœ]ìš»ùùdw ÒVk”ËwçS$„ém%ù.*v1Ï2ȯM=ñ «¿ mޝGnÁa¶Õ±¦Ð­VÆ^ºb£â!¬Üýàì-8L€,ŒÛ„zïl˜]‰¸7/YIzÖ9%Ž-™"‘rû·^‚ίt@V>?»`Èþ·ïŸš~júù¿ÜÏÝwPf:W~]ï Jg; ¸?†,X ‘ž¦#å›;ªgC ÕBEî±k0d‡]ÁëW$²afÊÂ"Ø#Ù{Ðb±-Hg× (zÚ‘•ËŸ¤Ó7¶ÚJdÔá¡0º€-øZ$z}Ážó.-6|bo,I‡V¿ú*ÒÇN‰O³Ë%rÔœxŒ²oÒífš™°rù:4Á¸WÃä¹~±ø™XYÕ×LGc¿(6íO?¼ÇîìÝ7²®+ɣǠkJ®DÊÝï¹w&OP€lµn¦ì´ck¤ÇŠcÂ$ÒìS¦¸Þ,‹•Û¸ ;öm0yêYkÄô3e5ýÔô³"÷sÒæÛxòËYC—ß´ n¬ëàõèú'B"çØ{ÀúÜiV:j·vú*2x×X¬ºX“Ýþó"¾g9Hl~:¬¦îT’ÖcG¡†O¾ û^ëçÄ!=I¹üûLÀa/O‰¬í0·„²#«cÕ¶¯¬Ùüx3áû¤s’Gä± 6œÆro¶æÁHÿJÅz~]X¹|Íâ°¦™ƒ /dÆaœãnöVq'„¿H`»-oòŸß‰¬©Ñ*|s4P‘­»{ÀÔÛ–•»Ÿ5} Æmè rÑÀUµ´eKÇÿy3®Häгíq¼8‡•ÛÓ ‹½‚Ôòi‚bÓ‚ÔôSÓÏŠÜO}Ý›X³a· ¯Ž¹Ž1?}Ùm;÷Ãôl®Dú´ æ-·Ù̉«ÑóF {2r®‹gí–Ä£ó²$yar¶Î^!È®kZâýÆl‰Ì×7_º‘Š”ËŸ#œÙñ„DÞ0ØU½6ÖÅ )»Ÿ±½oÆ£7…ì )è[^—½à­>÷$u·êâÑ»¹n\™˜å“ÏÊåË®†:É‚\ò%õÖ±3úWAÏOql抧¢µe4»¨Š?t¯tS‘s6‡`EµZ¬ì12-×=—Hõù©ÜË({XêóÚ?u‘ÛµHõyZ›ó¸±¤Lês¹ü®%p«[[EªÏ-~žÂ§ %©>¯Ÿ²îÏAªÏ§8 »äßæó…þî¦ Õçfï±5FR}Þwâ>h­°ù·÷Üš‹Ôv@ªÏ4»‡}F‚üÛý“úîÄqV*òoŸ×ôSÓÏÿKýTWœhˆÜ%ä³Z˜°>½Û­.n‡÷•ÈqctàÖfëuã~ÏÎdå.þèQ«6È®A¶¨2§‰Dî.Ž’UÇØz¶a¢ÅHGö’ñ‘Yÿ"+—ÿËæ<.dîÑËPïÛ:ÂõÂ-UdžîXL¶û×pæœÈ[:¾hÿÀšÅq濸,‘‹‘[i<Û®¯+vLO¤å½¶¨T›•ËñF,œ^ ²|qâG½`¯UƧ©®JÒT‡î=¤Y'+ôØYY­WtEXJ'%Ùß. ×GºK¤î®˜wlÊÊåÓ¹ÌNù)‘[ÝG ‘é{Öæp0Â*ëôN܇ÒNµØ¨†zȘ $ÍþŒÇ¸¯®)wßýÅ.qzá%Ùºî.qìÜv÷ÙD¬zÙJEZJÅ‹?$Rn¿É;Ä[F ×ävÅDËß‚ÔôSÓÏŠÜOûû b»BO"ËÇsßwdSºôAnC¹¨òh䫚²G†Y`SußtrA í'‘Æb õØ3L8‚—Vù/ü²8ײ²D&öLÉ·RY¹ü~R0lû<ìÝýøµò濞ODæ©ñl¥_Ih—Èf7íŠÒŽã%ò‰qw|½–Î>:~ŽGØ‘½ìn¡xAAÊå[t}:pæ±DÎÝè„Çû Yƒ)žSZ.ÈÔ»ÎÚú {ð÷^ +>'‘çŽGÒÉ»¬Ü}»O±saU‰|-y ½KuÙãMSñT±Ùº‰Ï"”¤Üþé×:`ö´PAš„w‚Ⱦ® 5ýÔô³"õSýS£S„Ðým/‘ês¿Ks0¹–®ŠTŸgwï‡õg3$R}^íË>DM½(Hõù¢)U‘yì¥DªÏåò/.ôÆàïy‚TŸ„z_K©>¯2×w(Üë±V/ƒ¡ß{û½êX89ä²rù'&Nìä'°{> gý`WÇÁ¥aUÙ·J‚ÎHä‚Ò+BÙþ:{§îsát³œù !r2š€¬ñº6†¤\¾!Q‹±êÈAA–èŒGùlèÖT|Ì͓ȾÁ©¸Þ$„]’?UÏÆ°®ü åÎÊÝÿÔw#T%UTd'×­p9®ÍzÏÛÚõW’§§íµ× ‰”Ûÿ~‡ ÒêuR‘ÝÛû ŠC=VÓOM?+r?í·8ð_¼DVþ¶®…±­|oˆynñ‚[vO pe †¡ŸEu9ìt jØÔdÛ=°Âö¶ù rù#3ø–*É”ãŸÄ›Ë‚|ë/†ÿZÆÊå¿|Ç kþÑV‘V[6àZp}6¢8 UrvKdN‰8fPm[V³‡ÖQ‘OηD¢ACvH !œ»)ìH“‰ÓpA;P"åò–öÿ4?AîÙÑ‘^ì‚Q)èSwªDF„Å‚»ï•d¿òÓ(ªó“]q¾COíQr÷CK\°d-™©ëŠÇºlVv¼Xwò D†y-DH6+·ÿ@Ë`\Ϲ,‘ ß#àþ«?«é§¦Ÿ¹Ÿ¥ÓwáôªÙ"ÕÏ»z±}O•‰E;» òqÖá0?SAê9‹†UTä…ý—1<ú…DÎÝߣ^D°“²-`ÛOKE~ZpF8ÜÑÏ h0£Ù)—¿®ù6ÔÑW‘_»¹á}G#6°^,´ÖÏK#ƒÃÂÑØ¼¶ /쉑öõT¤Õ³ÉèU•½î«Ë%rLjê?ÑV‘²?ÿ-["dÒ6A^/«…ô ‹Ù‡þñ¸à—AöìK"d‘ÿM ]ä&È”À<œZÄÊݱى)_ê«ÈYóÜQÿ‡»%« –?-’È÷]§áYy+·Ba4:Í~aGÞ‰ê![©é§¦Ÿ¹ŸÏÊöc\g‰Œ«t_fÏc-¯Õó­Nv¤E‰FmõQ’·’ 1 ë”DFÕ.Äé=ëXûZý°%ºµŠWc>žµf‡øì„a‡%R·T‚îʬ\þžÂ 癪Ȗ}öâý’lm£ ¤¸ RëÕèËf«›l†ùüÇáåЂ8ö‚¸‚Oeùìš;y(¿Ø–•Ë·hðwQpx˜ ‡þ¾+fTíÈÆg†À,µ;Á*qÜX£ô´ÿú޽òyJÒ®³r÷;úy¢eniœå…ä9úì´ñ^¸ç»_"ýšAìVnÿ°ÛÁ(›ñL ïÅzú 5ýÔô³"÷Sçcb[ ‘Èó'#q{kg¶zšvi5aKï ħÓÙ´½9h<ù¥’l¼W ÃIv¤wäøìU¨Èáɻື+Õþ;)ÉÌË·p®… eÿ>²ðG¿õU¤k•ƒÖ7gï­pÅÒ”o‚œ´É ŸÔÙ÷U0Æd;HäÖ›1ðš²TIê½NÁRÏ<éÓÃ÷V R.ŸñÀs¢GcmA–­KOïK òZÁ„uH¤nÆNÔºs“mvw<öÝØñ¯î-PôÕŽ”»?÷7êæ´P‘šï…÷Wv«Mœ¬gØ‘gçàÞÂ~‚”ÛS;½»,}0AcíXM?5ý¬HýTÿ<‹Í‘õ$R}¾*{¶ÜÚ#‘êó+9 èm®+Hõù³¹øœßHEªÏŽ?‰‰›¢©>—˯<‚‡æ*R}n®]§Z‚TŸ›Å$#ãY–‚TŸçÿèŠümY‚üÛ|  ‡¢q R}>mÑŒ2ú.HõyÏÀOb‡áB‰üÛûcì÷!ൡŠTŸW˜?›N òo÷¯ð‚g)Ý@þíóš~júù©Ÿê.¼ÿ—YPšÛe7Ø®g“±~G)[ž÷Gÿ:xÓu|ÿ¼LI¾x‹Ÿw(H»Ngàþ¦DIžLÍsÜo;ò’îÌëÓdy÷ñ8r¼Drù[Ç\A#sAöÏEãY#Y7 þD6ß} 7+űzãgl$;o`*Ì/ú±wm%Ô*¨òUj*ߪÎÊå›wu×ê©HÃõöŒÒe÷lßž9'r Ž™`ß¾ÍGcÏ‹‚ŒZ+Ájyr÷-s‚00i¦D~\pKÛ:°Ϲa„äo‡xµ¡+·ä/ÜIß,Èky>ßSAþ·ïŸš~júù¿ÜÏ%9˜iòH£.Àû\)û¦E N®~Ì–÷öB~ÿ"ö¶g2ÃÌÙ´=QØøÁ™=²²©ËÁšäÁ3+žÝеLX(ȣʾø|ý¼DÊå/©w^Í– Ò´D‚c¢+{1ú^Oœ!‘ï-΢‡÷U%Yûl6&ê:J¤Ý"´ØÜ˜-.œ ­/ñ‚ìj­»U{’rùb·ÃÍþÕU䶤‹¨úó»DzŸtE½‡Xû}¾Ðr f¯z†ÃöNŶáÅÌŸ‚”»¿ú[vÔ™»2NØŒšýQ¿Ysºz]wKrûûE4Á½ö5$2rE=<éÏjú©égEîgN¥sH´{/H×v*X¹|eínEŠ_:{!d~Þ?ÄöÚ¡ؼF áÐ!Yå2Œª–±­‡g`Ó''|¶b§ÒLE&oˆ@sVöû·9 «{骟Œ' Ù>Oã½O;2kiŒ'TÞµî¢òñÞJRoÝL{³JA^h‰’Ú¹¤‹¤ð6*R.ߪ¹pöD"Ÿ5χî‡Klú¡88c?ë7æÜY- Åç;™ÔµfW(I¹û:÷#qzxS‰iq»Rª²n_Úân—þ‚|aã²1JRnÿû¦¨÷¸²ŠtìÓ Y&¬¦Ÿš~Vä~VnáúSµ²Ob—ívÊÍhxj%»jBOä|5dû°FÑœ¬îÃ:q©7;È?ëCtAæôÅ÷ªìQ3Ø4©L"#æGËîÑJR.Ý X6 䙜0”äF²/?˜zØP×ݨÞÓ—½0óú¾kÇ®ûVŒ-]Ù-FkÐc¨BEÖÞßf-X¹|íDÆMN”Èlû\ªáǟƤm‹Ø‘ÍÏ¡÷.[vRJ;ìÜt†¶­/Îo¡"åî筈Ŧ•dâ¡x¸9Íþl7Éq{$²wæàêkVnÂç!hÚµ‡Šìš¾ W.þ«¦Ÿš~Vä~FÛ§`L¾H½ D¬^‰Ý˜Ø[æïR6ÇéE÷2È’Ë7EæÓd¹ðDœ°jU[I¦ë­ƒ^¥‚lWè€åÜÙ ¦ûÐàë.V|³Çëe¬\þÇéÈi{DK·ïEç–ÿšqi-ô–$²ûÏŽÆ£Í츬KH:Ǿ8xŽαƱˆ3 ”ÈÑÿDâÙðÓ R.ß>³|ü.Ÿ%‘eosÑk§ ;´zRu*³á¯Áÿè%f¸ÓvÚ©Èæ™¾°;dÉÊÝï’’Ÿ`o%tô(üf d/u݇ñýªÈ·ãðkTsVnÿ´t00U‘©¯ðãfVÓOM?+r?³scp­e%CïG`•»È¥.èjIdºO œy;‡­{@l™Ù†"Œ>ì€&ÝpÛâ‚´ønëáÕ%2óÓ=QÉçš ‹¬üDÆD;V.x7¬‰ŠdÉCgÔÎ aóÂͱö—ĺ6ª‡â'Ø=‡£°Æñ2»­þ^d·Ìbó—zaqÃ{lé  þÖ¤\¾àýÿ{÷ßTÕ¿<@[Ê®²‡Ù³¤»½?J[ H‡md¿–Ц´ÒE›ÊÊ’ ²d/Ùd÷%²‘¡ (ˆ n@þI›s‡BZð߃ðœÏû}ŸÛÞÜ›œû$Ír?½•þQËßë~@iÓ«óÜ{–ÿ–ÞŒåå±çHõÀrãìÅTú3Kõ™Õ¤¯ß_eéèòiÊ:êôÖ×;Xöï±æýuf;Ëzo«t~Ti3ËE—Rã“T–ŽÖ×}! ÿy©Êò“KhÁžKY,1?1?Ÿ¤ù)ŽîgçÐá–·–âò¯'¸Ð¼9 U–âòVív)»Ç} ²—/üº:%iVª,Åå§]ÓY,Å厶?Я7½úút…¥¸¼aÄ%eê­¹ Kqyü¾äÛb‰ÂR\¾æÍRT½^Eb™ßí[´&‹–vy¯KqyôÌód /¥°—÷ÿû=ÒmÀ2¿—¿®Âû¤)•Ù‚¥¸¼îªÃ4çÜÛ*Ëü®ÿæû‹èRíoXæ÷÷1?1?ÿKóóžûŸ¦¿+}#ëg±Lò¸¬Ô¾6Ÿç¡Øe4W}…§ûK‹iÜk½XêLYJ§.kkñ|ýò&2]OUX&¿°˜_ij´nò7<ÿ˜ãE{g]ã©»6Vq ˜Ô‚å: ”ä¡*KGÛ§­ìIšŸgoe©+æCú/Ëb™>§-H.dfÙ3 MÝ–Ž.¿Ã5Ô½î{,N]E}Oæ9]¿Kiÿ­{Ë¡µ–+ÇÊôVY:Z¿ád ÍmµRa¹£QýÐï9ž˜Ÿ˜ŸOòü,úÜÇJlÚz•åÌ 3J…o÷ðŒÉlCj×2Äò÷MéZ“gxÖo?š"¾Ø©²¬¸f&Õ?ß‹ç2GIÓ±]ˤMÓW»+,‹|êGGMáy£T3Úè^Ueéhû7WI ¦þf–-eP·“Íy†{w£†·Æ(,W„¾@…ÒþÇÓíëJl¯Ù<; ž Ô¬Ô–gø•¹4ÚÕSe¹`éFªÖ±…ÂÒÑöíØÝŽ-œ§²l÷ùË´Ùýžõ·¦Î¯ù˜YN£ =àϳû†7é«ù›–šï|i}‘/y:ºü×_\A; `ÙéÒRúâr)…åÀ£»•–-N¨,£æ¦ñ»]Í,­å Tek¿,–îNõèV£ *KÌOÌÏ'y~vº}NyqÕi•åðà‹Šné%ž¿ª•èd‡ÒÄRWƉN–.Ê3øïÅ4jV×,–úº+iÑ©’ ËþUÓ¾»y~Qf/•y¡±ü¤Ó2³¼6hmý½OGÛ¿|Û :s0ÀÌòàgÃh‹›Âóð¦*ôG–«ÂrL‘Âtsn‘–oŒVŸŸÄ³e‘¹J’o§,–]Z¬¦ K‹˵ç&’îÇx:Ú>×é=hw—‹*Ë©ëÓ(kÕ-ž[RhG¦¯™å7݇ÐÔçu<Ÿ|Q¹¹IóSePÊmûCe).oûµùê Kq¹Ó™åtbíb…¥¸¼Næz:9ª,±—Oø5{ók•¥¸ÜÑö{œM§µ0³—R¿PÔYK²XŠËK¤Pf×n ²—z¹ýÝÞƒXæwûz?ˆ®Å7³—_¾2ŠôÆ:f–âò3¿OPŽeDe±Ìïå_i:Ÿ2ö),Åå^‹ÆPı#*Ëü®ß÷¯>4h”¿™e~óóó¿4?Åtÿ-ž"öPY{'6ÕÝÍ³ÇæùTïÐdžþU×ÐÛ æÙÌõušóÑ<+_›D‹ÇsÇÂE´¤ÙW,GÖy—Jž¯°|®MùG¹™YvpCí&6ãéhû§6ØOoœº™Å2åµ=Ô¡Õž¿z~N¡ N°üªþ'4»ìÿ–·ŠŒ§/¿j@,«èGGŠUã9Ò8Žö¬Y®²ü{ç2ZûÝÍY:Ú¾c©ûhl® Ë×÷Qó¶ïð¬\&•~™åK,—µêDWûñVm<Õ8Q×Ìò†q ýÐæŒÊÒÑå·2“Þ‰®G,«ÎšNãKÜÉ¢c†Ñî¸*ËÏÖͦF·åéhýuö_R~]¸ ‹å€±ÏÑÕ *ËG}ü‰ù‰ùù8ÏÏÝÿ¼A¦ý[U–Γ†Òçë×ð¼Qu;5×µåy9l/õØp;‹eé éËóN*ËÏ[þŽø,,€¥SÑáÔè£_–·ÃºÑ/ó݈åŸm¤OJ–3³ìXmÕü=Zeéhûƒ¾ÛIǾk¸ƒeÌÈ”6ß=€eÄG‰ªÏWXÎ(ûy'àY£ogª<¦0±|³Q ™>W–õª/!Ó›ñ<ÔìmJXÕX:Ú¾ƒ÷ѯ¶(,C¾ý€œ}ÏñìSª½wÌ‹X¶nBËuxF­_IÛžû(‹å‘àYÔ«l…¥£Ë¿½q ÝîS—X®8<‰.M¯ÃÓõÛt¢¿[Ëg?\Eƒ÷OTX:ZÉÑÑ4ýÆ4•åÀF#èrðtž˜Ÿ˜ŸOòüúçÛô¿?ª,]Ž'ýâ©<‹u y:ZÿÏŸ, Î=T–ÕþÜBÅÌËb‰ù‰ùù$ÏÏg«O¦¿OŽPY.<4ÞtOå9æú)ºvâ…åþÖ§)²RO×ùÔ}VYb¹9ku.Ý”gÿO·)ë»îSXÖLŸ­¼Ò †§×¯×•m7ÛðÜæù®R|ï3*KGÛ®È ºð›ŸÂóÊBJyö<øM ]ñ_ð<5ÿ ª¸y/Ï_ÿyj•tSY¾¾&œtæó\]õ7åŸ/ŸUX®ëAòVY:Ú¾R•7Ñ‹*ËÎýÖ1®6Ï3×o+[ ÿ¢°,î{M:vÏ·Ý÷)%/­æ9|×p%©ô‰–Ž.¿½QôÿóIJcäªÙ´ÏÉý{Ñ‘.ÍxÞXàO5¿lÊÓÑúûŽÚG½¢Ï°œxéC*6o¶ÂóóóIžŸžî³)¼l{•徿ó©ê¸º<èÐ¥ Kï*G)¸æIžk;ô§rçšË÷›DQÏMþ<»¥/Uö´(Àr^»sÊó.ÅT–3#.(Y æY¡ã‹T¥ú3f–޶x·YÔ»ÿ…åþØÉTûâ(žGk½Bm¿›ÁsÜÕ š´/˜çþíI´ÿø'*˹ëFÒÐNEÌ,gÿ5’¾xwÊrLüVšÒÞÀÓÑö5.¿”橉eßJó¨ü[ž<]Wþ¨ìÞöŽÂR·ýgåâ{­x¶¸º@©¢©£²\ݶFlàéèòû Jþç«ËÁ;†`žoýíF=JñÌØú·oZ¯°t´þi}n(,g5ß@ýþ§%–˜Ÿ˜ŸOòüüóÊ"ª¹éVË’­–ÒdÓržmÛOÍþTXf}¬Rɛʼne˜·;5«Q—çñÚeix¤ Ï.G*Ò¨w†ª,ÿ(Fû«,â¹Àe2õZ¾™gõAK(Þéõ–޶ÿRµ1t5l²Â²Ùü·è[à ž5ëhÏ×XI¨Cgg±¼>w…[ÊÌrЋk©Š¦$Ï–á§H›1(€eÅØãtóú…¥£íó]?†Žó!–™eÇÓ‚îäô´[Ê?Ëv°TZ'M¥Ÿ·±ŒV:ÓŠËT–kJO¦C›‡óttùõ]R«˜ŠÄrÚª¾ô®_9ž ¦—v^ `Yõõçiðû²X:Z‘³iÁKÄòƒiÃèP·&<1?1?Ÿ¤ù)Ž+MWЖ¬zY,Åå.Õ6’i@yb).?²Þ‰ÜÿÙª°—'?ÿ9œ­²¼çòŸ›B­ƒ~QXŠËmÿÔò¯‘¦Äl…¥¸üòÑštúõD•¥¸|íÙ]T3ã¦ÊR\þ{©uä;»±ÌïöE¦ §Ñ^^ÄR\Þ£v*©y?‹¥¸¼î†Õ¤9®mÎ2¿—ŸžžAË?r#–÷ìÿ=ö¦Ê2¿ëÿîWhÖØrÄ2¿¿ù‰ùù_šŸ÷Ü?f¥± *,}‡hS™/yV7T¦°ñçT–_—­K—;¸˜YšßÛL3ËË/f¾Gç»ó<´ê„²mû •åÕ˜›JœÑÙ̲í ¦î*ËÞï¼CIû&ñt´ýŸþJ1tVYÖéö¹Rt|Ïs·OÑØ¿û(,ŧƒ‘*Ï}¥W*‡z7 `9nÒt¥Ñð²XŽ:SZºžÚβwzOª°fŠÊÒÑöÕúð=e‹Ù7‹åw6+/]âùj¥3¤nxMe¹uÁzþ¬Ϙ½³)½É:…åοRÍòÏKG—¿rêbåÚž Ëáç)>…†óün½Ú¤GKÏ´HZûë$•¥£õŸn?¶¼?Raùú׃hÐÊ£<õñ'æ'æçã@M]SX†ÜCŸ¼Ís² x¹ª™eÂŒö4àóF<÷ŸOËÜš˽kÓ©d=ožŸÎ¯Mƒ+Ö3³¬uøeò}·§¦ë.rÛÿÛ–õ³лó>PX:ÚþõgÏ+g®¼©²ÜØî‚òº€ç”–hc¹?–Í"¶Ó1Ï˨¯æ)%ÃG«,WŸú@)|ò$Ï3Óï,ã9wã:âömKGÛ×ÅxD™3Å[eY9íåX¡žúž¢oÏ6Îb9nÙ1³®cË+7¥‘)ÏË–=>Q~‹¼ª°ttùÓoÌPžÿ6Sa©fª²<(†çþׇÛù¯U–%ZϣŠ™Y:ZVm)ÌxYaùl•ghuÙs<1?1?Ÿäù©+·“ZfºË£QÛ)µAIžoψ£öŠŸ™å )êïOGÛŸ4ÿehÖ.•e\ÖÏÊ›x¶œ¼‚ʼ¢%–imfдOjó<0ì–²±†‹™eé µhz­Š<¿s?K'†÷UXn«j¦ãï#–޶ï“gz6a¼Êr4å›<ß5|Hû•VXþôµ™²ÞïÂs‰vŽRûrkžñ¯íSV¿q<‹¥£Ë?»{¢r³A[…ç³ã”ŽKu<§S©[áoU–?¦›]§ót´þc÷+•¾˜«°<ýûxEÿJIž˜Ÿ˜ŸOòü<¶‰\ÿv#–Ýʬ£°óåx6CO·0³ü§È»TëŒ/Ï€±Õ¨îÛS–¾Öÿà€ý,çM]Fõ»U–“ÓVÓ\¥.ÏYµëÒ¼–K– |¨[ä—Y,mÿµ´[J|Ê •¥±eQr›_ÜÌrIÖHjÕ¹±¬<7¾Öºñ\F­bfé½ú5ð“Ï[ƒ&RãNU‰åÊ_"ÈÇtDaéhûo6¤7Öª,Û &ú¥ýVžÆm¤âëG+,ËZB·dz¦+2KeY·V_jÙk9O‡ŠŒVZ©¤°ì2m¤Òc…†ç·Ó>§™'Ve±¼ÔåSºÕ¥ŒÂÒÑúw~>TQ; ËbYm©Rrá[*KÌOÌÏ'y~:/XA÷W$–[Ý“vcežN'Q»VÌ,?¨±†žŸY…礕MéÛ²XÖYIoü™©²ü¡õRj¡5€å…+Ó¨P…åŒ#¨^û½*ËÊTêÒo9OGÛ?8êÒ‡T0³,²·myMËó›Az*ÝéªÂò`¢w|×óL=1–º-¹®²ÜVw}ûöž™«=hBèš,›ìD#6mPY:Ú¾îSô”1{»ÊÒuo"iOmæy¡ò Jð^©°œsk4-úîN6i¿€t§›ª,»œÜDýWÞ`éèòãW¢ç `YdÕ0Åkê\žÓß>@±ë+<ϯ¤êÓÏñt´þ_g_W"&QY.nÖ„Îüs'æ'æç“eú@:v{•ʲFØÛT3lÏÆ…ÿ –‘ ô͆q<“¾]Oç¿âyüÂtº>® ±ttùë†)´¹[Ë ]‡)'è×,kŽ¥BÉŸ(,›iºSÍJsy:Zÿþ‘Fú¨Íe•åô‰c¨ÿøÍ<1?1?Ÿ¤ù)ŽË¿M¦]+Kqyí™;iöùE*KqùíqkH¹¸Ae).ûô3”nVXŠË÷ŸGEÿ.N,Å厶ÿíŒZÿ½·™¥¸¼¿/5[»#‹¥¸<¹Þêÿ¡Na).ï8ú3r°¨Ê2¿Û7yÕ$2”y[e)._^$”æn3(,ÅåKâ©™¹±Ìïå7l2\Ix!«9Kqùœ’~Ôù•ÏXæwý=Ï¥Eƒ›«,óûû˜Ÿ˜Ÿÿ¥ù)f£-åéXµY,º”¡„÷Üx¾÷Quú«wbYs«-¿OÏQ”ru…²¾‡?íßù<ÏõÅNS¹–U–Q†4øð¯ÍY”ûFé`þZeÙå«vôÍR3KGÛ_xÐRòªzSeÙáÜJŠ_þ9Ïf£> Ô‹-–¿ôÚM¯žZÎ3¦·/Ei/e±ˆ§ÓþèºËžÍŽ|BÉsbyfÝxŽ>þr"Ï7Æ{“çë7x~Þ¬}º¦®™e‹¨ÑÔ³^MžŽ¶ß3z5iS×´`yü¥åô믋XŽàMß7«I,gö¯BW–¹ð\–ø)½ûÓó ËÊ)𕱄çÏÝ6SéZ3xJ «mâéhûªµiL®õO(,/ÿV‘¼‹ïåßû=ZVcYKeÿRÚÙåt –“ªo§.8Kç2ïÒê!~<]¾R² ½R¬ª™¥þX ÕÚ_gãÒ#iìø†<ËV[@-*8ñt´þÎWQ‘~Ý–þ®“Hë9ƒ'æ'æç“£º©—XŽüþ$½[}ƒÂòQb~b~>ÎóóùBÍéÕ)U–¡ÛÛІªßð|kÚIJˆß °<õç‡ôfÒ7<ŸMA ^0³ m±€Š÷òàÙÖìNCþ¬Àò•-iê÷¾*Ë¢oiúSyþº’Ö–žÍÓÑöÏo_˜|µ#–6ü œÜø O÷VÓévÄ3¥ßrZüæJž‘ÓéÆÈf–5—Ž'§ù•îä÷(%ùʲñ'È­²g –޶oß!_šTº®™eåíh”âÁsÝK-©°å~ŸeB _:ŸVŒgت3»¦Êòƒé¨n×tpùQ¥GÓî£ÓT–…ßG)>Sxö XGn¾óvð쿉V/4),­ÿÅ,•|7%– ŽÌ§ÞïWç‰ù‰ùù$ÏÏ1oè©hýoT–½èâö‹/™Þùü3•eÈÞ~0û4ÏV æÑ†–Õˆå‰ Éwá «yœRvSYþè0]PrËr‡WÑ¢µûT–'¯ì£í£‡ñü蕱4k³±ìûw*bræéhûOµÝ ì¸^)€e•îËíõ›;X.é:¦õ¨M,?+ߎÜnÀó—òk©TËâ*ËC•WÐ×NõX{ÿ¨ÔÎ|/‹åµIE-û|SeéhûJš‡Ó¦Kf–W˜DÝ?hƳI#…¼mT– UÂèÆ‰É< ÿq–ÊýÔRa¹´ô^êþsabéèò5ïN§??©²,~j&}úÏv »Ñá?|ˆeÙ¥•i÷4-OGëï´XGCB&*,»UoH;jèXb~b~>ÉóÓ7ëMZZü°ÊÒ9j$U¢]MßµíÅòõï?¡Ê}–Þ_>K¿˜À²TÑntÓ}”ÊÒÑö»>¿Hy¥ËÑ,–[/T^]©SYF­mBfÖ"–lq¥ŸáyuКº#HaYâûÑ4{ó4žGÝ äܯ¹™edéÅte|]žŽ¶¯©ËlÒÞö2³Ü¸„ ðL÷5PR=Ue©íG)3?ã™ðûLê4¢±ìëCš=•x:ºü©7çÐóÿôSYú–Z@ï¸$ñœQû¤2}ÿ9…e¥OÖ(;×7äéhý©•ˆÚ¿Ô@eù¹K/Zðî"ž˜Ÿ˜ŸOòüÜòÎXªÒiƒÊ3sùõ]ȳel$M~åG…åÊU?h;ÏíÇ&Q‘ky~¸;¶}>çŃGiRâQžWÚl£U ËK×Khȇ¯«,¿^´ŸzO.ª°t´ýC_¢¬Õ÷SY~v{­â¦›ÇÓåÂÇJ·?*,¯þ¶\qݺˆgÅÄ^ôÞÒ5<Ã_  êÃó\9ýZv{ºÊ²ú¼ÍT-éDKGÛçþÖZú*©š™åŸ£¶ÐÎà’<ÛõE®S®©,Ïü8Þsý‹ç¼­uéÄä% ËFëÒ±•¯ï`éèò':½G½7wSYvûb 5¬Öž§òÁqeùwæ,–´¶" 5LRY:Zÿév“hr½=*Ëf‡×P³f+yb~b~>IóS.O¥%û&ª,Å僟ñ ¯' UXŠËýBÛPìF½ÂR\î>i6½ôO}b).¯um#MoP–XŠËmÿâYªR®ín•¥¸|SçiJ¶ KqyõÕe©çÐþ Kq¹ó;£(¢ÂL…e~·¯kM3Íq]e).ï1~ ¹¿®²—oû …µ«²ÌïåÇÎYN囩,Ååñßt¤嶪,ó»þ‰‡÷Óò·Z©,óûû˜Ÿ˜Ÿóü|Ú‡IŸ˜dŒ 6˜ Ѧ0CJŒ)9Í=9£©{ŒÉÐ#É螘ŸúÈ—¡Óé|½½µÖôóõÉNgﳇ—§ÖÃò#ž:?/o/­ÎSçáå©Ñêþ…ýs823L†t˦Ä%ÓŒ)÷ÿ9ËÅÇ?`=9»¢åù_Ίj k4a†XmD´¶“Ö6¬çiŠYxZ,´°þûTÞV¨×GÙNZcº…ø¾ÍBwÎ/›šìnHK³Ì¶Þ™†tCŠ)1ŨéÝT§ó÷hîë©óò÷5ú7o˜nŒOíÛÜ£uë /¿ÖAM"ª³pv‰ˆVÑ!Qs¹H×ȈÐp}hxáü’‘í£B»äö;Å"£"‚B¢£#¢„e¢#C‚ôQíc:††Gt79Z¨/Ȳ}HpŒ>°Uû\—ÅE´9,<ÚòïJÏY7 425#ÑúK“3 ݶ Û/jiÛi÷¦îMïÜå¬ÿÌåúwµžÏ®÷ìÓw®ïbÙÿ¶]ÏÙë»sý–±þS¸^­gÙ]Ÿn9ÿ¾ëz,•}žÝõWÖzƽ×[öE³ë+{EÂõôŠueéé†~-¢Mé–‡£Ö§ÝŠØ®g×ì Ξ0–ô°žØ9çÌ’üÉù·“>4̲v×%š\‘^šÜŽë%Umky(ž›š”™œcédCö¿ƒ²ÿmY\Ù¢J®¿ÌFMûdĦL± Á±žjw­'±gr¾×PÃ~ ‰É‰=Lù]É]×G|’¡g~W`½ºcs½4ksýE'«”¸ÄdKºØVâd½%òzà8Ǧæ\Fž~ºpbž´mfúJ^¸ï¾8™,}¬¿d»à"QÆx»f_”åÖ°þd¨>ª5›&¹Þ°­RS“²Ÿû±MQÛ£_}‚Qk=p´ÖÇZµëïiSãµ=,¿‘s‹g’ÚŒCšQkÈÐÚ³2ÙO.D[Ïe·l™{¿²QØvÍÞs3°á“û•sÿ[‚Í”<íî=ZSë~[wYk™Ä=SÓû5Ö†¿fý‘œkBo™ïFCl‚u3“ï³Ã÷>@e£ˆ£û¿˜ë[—¸Úm~ž«(O…rÿ«1>)Õ`ºs5ÚÝùæ<º¿s-¥¥&Òûg?{¯ÍH³ôEúý®5·\þº(j·)9O„ä3Z„d˜“³_£HOÎЦ¤&f³w66Á’bL²U¡¥dúi{Xú-Í‘¡M7f¤¥¦d»Ý®k°Ü.û!<úq éýr”õÃêÖ}0ÆÇ[Š-ñ5cöꙞÓv–¢0æi\…=˜œûDäºÙWìÃ<¸±þ»¡& eØ]Ay¾±ùÓcU³ïR–ÆÌ¾M³ÛÒ®V‹8·m÷|ð¶{>¦ÛnÿeUË„èi™•qÙ[n²¾ Ÿ–šh}µ­çã²ùÂ#²œ§ç£²Ÿ³m•Ú'çÁX“œ0$%åô…¥¬³w&Ýò‰ñZ}zfÞŽ4qüÛwŸbQ„ZæJT‡Àö–Óå­SÉòˆ9-ÉúV‡ì»š× IyÚê'²JÛ=ן3[½,÷f|šÚ½é£±íà³Üèv¿“s?‘§ëOÂ=E"g×PÈîrìŸn¿ûü;O^³¹•ýTxfØ~™ý³­öçó'ï9ÓS\ƒÝSbwŸ繦»Î·{ÚÆþü{Ÿ²¸k„¿ùí— 5ßuavÚŸÏþ°³?ïž?"îúÛƒ“»në»þ±©‘³7÷<¬¹{ñ= ²V³Å¹Þm²‹Î~4ó {$û9š{»³ifý µÉ¶Ëú£î 6Ñ«ç¼:»$Š„F‡Ù¶¦hbF²íUl½í'"­‹¢³¢ð“ÖãιURjl/ûë[£i©yÀÂÇgÜ÷ó?wÞÝûÈÃú¾Ÿ|}þÇS‡ÏÿÌÀçžêqŸãßvô7ýW.ÃÑño=^„ã_çå£Ñúü+—î`<åÇ¿ƒÏÆ{Çè£Ã<í2âû¼<¼Ñÿ2ÐÿOõxàç?ÿ•£ßÁñïíéå#ö¿§ÎC‡ÏÐLO¯ÿö0铌qÁ“!ÚfH‰1%§¹'g4u1z$Ýã}bôÑav:Î×Û[kM?_ŸìÔyæü;{xyúh=,?â©óóòñöÒê­îßÙÅÌ “!ݲ)q‰Æ4cÊýÎòcññXOήhyþW†s…¢šÂM˜!V­í¤µ ëyšbž -¬ÿ>•·UêõQ¶“Öߘn üH¡;ç—MMv7¤¥Yf[ïLCº!Å”˜bÔônªÓù{4÷µL_£óÖ‰éÆøÔ¾Í=Z·òòkÔÄ3Äß«‰·¯®I —“ Àà ïÖžþþþžÞÿµòÔŒûÿÿâÑÿàãßÃOçíã­/ÿ3dß@ÁbÇýãžðôÝ7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷ z D²û½"Ù}ƒ^‘ì¾A/€Hvß —@$»oÐK ’Ý7è%Éîôˆd÷M^òñ&}b’1.Ø`2D›Â )1¦ä4÷䌦î1­¢C¢:êC#Âõ2t:Ÿ6;}sRçé“Öáåé£õÐùz{êü¼|¼½´:??ŸF«û7vÐÑÈÌ0Ò-›—hL3¦Üÿç,?ÿ€õä슖çe8W(ª)¬Ñ„bµÑÚNZÛ°ž§)fái±ÐÂúïSy[e ^e;iýéºs~ùØÔdwCZZ’ѽw¦!ÝbJL1jz7Õéü=šûzê¼ü}þÍ['¦ãSû6÷hÝ:È˯uPϯ&Þ>¾º&>^þM‚ƒƒ¼[{úûû{zÿ ×ÊS3îsüÛýMù2ÿÖãE8þ}¼½5ZŸaÿާüøÏÃíïc2ô°ñÛÇ–ëÃ×Û;ýïéáé‡þ/˜þªGŽÿG<úÿž^:oñø×ùzâø/ˆ¡ZFkK–²ˆ6Râ éqÙ3¡ˆu©SIÆz¢°í§o[F!Ûi—œójrgŒÿâ¸mÖynù·«EttXhJœ±/»Uµšœ»‚únÖå‰É–¶ŽH3¦ã iö·¾uyy g‹VI©±½ØÂ\ÏÄÀÀÀÀ;¬íß_soû[i5ùkÿÊ¡èÙäº÷RGg}е@K.éž“OÍ`Ïp¹IÝ Œ§oꃲw:ÉÞ§{XîþJ|m}= 042ºQ#mFbrf’Á”š.{Ã0000000000000mäãýßÖ—íê2âýß~žxÿgÁ ¼ÿû©y~ÿ÷CýŽÝ=Ç¿å,ÿ6¬ï€³ä NýRb5wÞžfmÁ¶ÓÙïake;ØÎ}ÿ‰‘ûÿÄ”øÔ‡ºŒ‡ºÿÇñ_0÷ÿOõÈóýÿCýŽë?î¹ÿ÷Æñ_Cß/ͨ ÐÎìÁN—½M7òqÿg0=Üe<Äý¿7þþ/˜ûÿ§zäùþÿ¡~‡Ç¿øýž^xü_0#çïÞ-,'õÖZsçïYëAZ<2ɘb[R,ÚrN±ì3b5÷þékýÊ–Ù?eŒMM³­¥ŠuM9ç¿Xèa©˜-+¦§ú¥&e&§X¬E\j¦eM–³í€>4,$&*0¼Mˆå_Õ¢-·¶IkH‰Ó-R㵩=2Œé¯L‰©Ö¿Ô]ŸÏí ëGäœmë~κ¡‘©‰¶_ž(T7÷={#×=³*ùR¦¥3“_NI4eX÷î¾—b}“–íêp Œ o¡Éy‚âþW»òZ÷,ûŠkmJOLé©hr>èoÿ±¼BÖ ˆË}^¸ï8™,#­¿d»¸"QÆx»ZÙÙ˜–›`]ö²>Èv^î·eÎÆñÛ²Hûˆ6Ö-ȾͬK’R{æéV³¿øÊ¹]Ÿl<Ô\Ìó¸Fµ ~¹½u*>{g/2bŒq™ÙÇÞc³+•£c I†t»}i•šš”ýÐÀ¶/­Û¶‰‰Šèh=•ÚGŸdÈÛ!Ž{CÅ+=çÎ,Äú˜¤l¸!Ùxç°7¦×Ïh¯kýÑ·9¯›]42*¢]HÞrò¹ÈôÔW±&mbœÑòð(>16»¯´Ù¿ñ¸l¿P¼%£BÚ[z)$&8Poïu¢ŒIFC†Qky$aÔöI0¦XO´=,—–lÌЦY~;16O;ãz÷¾ fó^³ù™¥X[Åè;GZoŠ÷V–Ö¶Å ¶¿´Þ2£ƒ""CbÂì; Õ[æ`FljšQ›]õî=ݵ££ôµÚ· ,è.°L®„órmKë/Þý@†×߃XÙî4ù­o=Ïþ~èîv¥^Èþ|»µ?ß®¤ìÏ}ûe÷Ì*û…÷ÞdléTÛ+4“¬»Æ×&\]ÖCæ®WhŠÝY{‘Á9Ýœ4—¡–½²d1‹âCð‹Oö¸ÏßáúððÀGÿî?ëÈÿó?:?þþ+çžêáàøÿŽ~‡ßÿçí}Ï÷ÿé¼|qüÈpðý¥îÿýÃñýOÀÈë÷ÿYÏÇ÷ÿa````ü»ÃÑ÷æçÞ'ׯt¾ßÜa``````zv»À¸® .\{µí®sNVg9ßü·ÿÐ/Þ9¿õm× î–ó·ÕûjÏ¡ö!±¶ó«m8bs¨]_žùþsCøóÇœÛýÛËù¦7—Tü&üÎz ]§mõ¨]_´ù¡^PôŸ—}}`<]ƒ=»YÒvº¤ÝéJv§kÛÖÙna;ñ(÷eþòÉíõM»h4.xãá†õû=49OXO{Úö²;ímwÚÇî´¯í4Æx´µÜ“­´¾Ô&*âåðà&­£C‚å´ìÂÀÀÀÀÀÀÀÀÀÀÀÀÈy|ÿÿ#|ûÛC}ÿƒ§‡Þÿ[ ïÿªGžÞÿÿHGÿÃ|ÿ£ÎÏÏÇ?Fû}ÿ£õ%뛟¶ïïÿáÛßòþÇÁ Üÿ?Õ#O÷ÿtô?Ì÷?ZîÿñýO2ðýO÷Èãýÿ#|ûÛCÝÿë¼ð÷Á Üÿ?Õ#O÷ÿtô;þþGo¯xÿïë‰ã¿@†íû­Gw¾ÿÑú¹Çäû]5yûþG—ˆÖ­£C¬_PÔ8°¯1C›Ÿa4Y¿.953Ť5¥j[‡„k£BZ‡D…„…hÓRS¬óý_ú.È"¶möÎ}/¥I™u»îû%eEØéä»þ/!÷}y¤¯+sM»³WN¡ú¨Öš}±¢p+»FFD‡Z¿­Örºn`ŠÉ˜’bÐvjܹqmZ‚õKç,÷ÆtcJ¬Qkw1¸…Ÿ[Øá·¹9Ù¾.¬»y­hû·èÈÀ œ7>6_ç(ÌÐRÁ¡Ñmc‚CÃBôÙ_£V92¡_F¢å·´q‰†d£É˜n-¥¸ÄŒ„<ÏLû19÷=ˆÈu =ìäËû·&Û]A÷¿’ð¢Ï´N2ôÔÆ§¦kM ‰ÚôÔ>yº^Äñÿüå¢Îa/‡[ï]ê‡åÜX'eöœ4$™šú7Ö-jM©é‰†$ËiS¬{žö¢à¿,ÑÉö}{ÕÙá•b™”sv¥Cû@OÏÆÚ @×cºõE£õ¶ú¯bÙšìoG­o°íIš!®AöîðÆççÛïxÿFÁû}þË:Øiø¾Ÿÿú/?›•Çûÿÿü—Žÿ‚¸ÿªGžîÿ üó_–ûOÿ1ðù¯§{äñþ¿ ?ÿåá¿ÿ fàþÿ©yºÿÿþü—Îûž÷ÿøúáþ¿@FÎ߽ūiòòù/Wë»}“ϱ¿ÊïùdðÎðâ‘‘1‘QaÑ–U´MblÎÄÓ,m“ýÁ ëÇòþÆpö<À=ï²~¤+áþuv¨DPûИ ˆ°°Àpë·k—³üSk©ÐdËÆj3Œ½3­rzœöÇá{÷KXn¡ö¡Aì-ânö7‘„wíçu³‹†…DG¶±¾Ù½DûÔžÚdcF†¡çc³µ¡)&­Ý'kŠE´j¤ Íž3é‰=S,W±e"z¼jŒ5eŸïxÃÅÿÄÁÿÿ†—ŽhÕ!{väl}£ˆÆô×r&Hbœ¶~¢õÍjÚÄ­Ýj³ï¹òöa°Ø)aî¸DD…¶ µÎvÏúÑ©™é±FË!gl M;a´ñé©ÉÚ> ‰± lZÙ–LƸÇe† ûäjÙ-}gËi·0ÛV§¥'Z6ÜÔïqÙfáCyNúÐìO«”Ó'Z®g“!9-ûƒÁù8’…–ª›û6Kÿ hÞ>´gÝ€¸Ü÷à‘>ùélLKµ~®±ÈËú »)_‘ºûžœ¯ÂºH¸O¼{ÙÝw/w}0éN…ÛŸ}WWÚ/¸·‹ì—Þ9¨ïúô”Ýaq×¥lS7Õö Ä$̓?e½îz¢˜íêœs{: Îùd”‹%‹jr>•ýŒÎý~ãi÷ùûÏú•ÿ·¿4õù/ËIüýW Ïÿ<ÕãAÇÿ¿sô;:þ=½tâç¿<ü,?‡ã¿ †ƒÏ•¼ó¹që°ÿü—“KÎù5ÿíw@<Ý#¯Ÿÿ²þ÷Mñù/ Œ'kX»¿¿æÞî·Òjò×ýÙoÔØÕ¼Õs÷[€û Œ‚n¶Éΰ=$aü^µe‚í)/lÙÉö¥¯ÇlÙÖ)'wÚÒßö¢òZ[ÖwÉÉy¶¬T4''ØÒÕöBâ`[þiË4Û«4ßÙ²»íݹgmi{™í€-[–ÌÉ-¶Ô•ÊÉ¥¥4¹öÌNÛé v§Ûnewº“Ýé»ÓCíNϰ;½Öîô>»ÓçíNÿbwÚµÐÓÕíN{Ûngwº»Ýé×ìN±;½Àîô»ÓGìNmwúo»Ón…kwº…Ýé(»Ó ÿÇÞ}€ÅQ-|߉ ÖÁŽ4Ñ &B:Ö`LM’X°E’ES†k°cìØ±cìXƒë`ÇŽë`Çú½g÷Ÿ1–h®¹7ßý>Ïyžß}3¹Îll™™]˜þ|„þ¼°1T½CÍÓýíj5[ß sUG÷·Ô„îo«‘ºŸîwoª»é~ª;è~7O¢û[ öÖýíu Ýß|µ‡îosÕoUO÷»Ô}t¿{i!÷?Oý@ÝGó~Iu5ïGÕ¡š÷jžæ{µš­ùž«:šï jBó=XÔý4ï7ÕÝ4ïPÝAóž§Ñ|µ·æ{‰º†æë«=VH,òHþ.`©?ÐõtûÿMcþüæÿû—Ôß™ñáÉ—”l;âŸÒß~íÿè~µ$Ì?ÿ/…ßÿ‰£Ë™‰DÆžúºÌ¨¤žzÌk}²Äþ­ýÛÿÕ¿µÃ;ì°Ã;ì°ãß4åó¿‹ùëŸþÖõúÛŸÿ\2Ã~þ÷=þúó¿‹ûèÿ;¿ÿ-o@¾ýü¯Kn,ì÷¿­›Hüiþ‰?üýo]þx›ÿ'Æ¢ýüOùbÝÆßxýï?Èþþ—%3ìëÿ?z,ÊÏÿ,Þ£ÿ/ÿƒÿà÷¿ä`ÿKjd/ðÞô‚ïÑÝ~ü5' £b›…õ·ÿ}"1w›?¾…_þ~a¯—r»÷ývù?|»¿»½%4ßûÖÿÔí.ÊXß_{¿Z2·kïWKævíýê?t»‹2ìýêßv»ö~µÐÿÞÞ¯ãvíýj¡ÿ½½_-ÆíÚûÕBÿû¿»vüÿ‹rþw1ýÿß{ÿ×¾ÿ³d†=ÿû}þwqýçúyòíùß%2ìõ?þÙcQ^ÿó×ÿÿ×ÿöú?KhØ×ÿôøë×ÿÅ}ôÿÕã?ŸÇÿàßýþ×öóKd¤?÷ä<›X”ë¬`~CàÉõ?–Sw¹ŒßüŠ÷î˜qEúÝßæÿJuó[Þ§”•MΞY6µtvùeÙ³gÌÿ‹)e3Í%4²½_~ýú¢ýønºÅ?þíí‰ôÏ›?øãùþ¯ÿŽxóïZèÿæÉi¿úŸýþx.‹õÛâ{,ð¥ïZ4~ìýý"}¿—ÝvxᨠcFŒ7Ü\­f‹mËJ§ÅßK¾ïSf•ÍÎîÍgP™=qÎlsݾ¸eÓ§—þò½Ÿb. ³h˜€.‰?¿ŠJr!÷òÿõïzÿ?û®Ïÿ·,5³tò/Ùÿ?ðMϘÌ+ü$ͪێýxýÓoûo®>‘éŽY8¶h÷ôïæ_âšß­?8uHÃcÝ›1µtfù!éK†ðPO_Z£4{_úÓ³Ç6Ü?f,þYÞŒé“ÿ»/4l†ù |ÏŸørL|ÂØáãÜ1£Ç™9o²]Ÿi¥|mÎ.ï[Ö7{jYéæBËfTÆ\g4ƒiN*ófÏXÄËÍ¿LÀ_ÝÁÿÝóüÍãz…ùߥ …£·ifÚsü~e <_—Nß—ÿ|ÊŒ™¿ún/Ò{,ðúÓ)žýÇSó‡SL}wþÎcØ,›ß½ó—×ëÐÃ1þþåÅt–)=~øèÑ…é‹W¬U´ypèÙÏ<ÎÞ¯|Vv©¹ÕEúº-ù«uO=»§þõ™E“Sßmó7ÙÓfL.›úßúo6;Óés÷f£|Ñ.á³þ¥¿ÝW*âî1v—‘æÖ˜…¹ÊÒÔÔ9ý„™º{˜×Oó”_<-ÛÜg&Mš3³töߺfNâÌ?žØàQµèWÁYðQ¿HßååGïÙ›Q>=uwž]>­ì?~O·W‡úÏ]jÁã¼øñOÅÌZ¿9ZˆoÙü¸Kù«ÿâ·û^¿ú?¿Ãò«ÿû×/ÅÉþŸ^ã~õ׿¼Œ,ø× >g/ø÷¿{>ZðÿüãGô‚ÿÅo/Ouƒ~@êšÄŸ_žÊ¬ó«ÒïC›ÿ{·ºÍÕ¥©æ¦æòTfÓ¼é°Ì\Ý/¶;þícaçÿFn?aبÏ5 þÆùÿÁyöüß’öüÿ?züÕãÿßq ¨¿xü”÷û÷ÿÙë¿-‘ñç×:¦û/§Í0;™ó÷zhùÀį–ìø¿4æÿ>Ì¿ºþ“ù{{ý';ì°Ã;ìøÿ1õøq~ Ô¿~üŸß¿_»ÿ¿D†=þÿGE;þ_¼ß÷Wÿßÿþ·|íãߎ%6öûßÌè‘øå}£?üýoÿ—fõõq~ èo½þÛÇÿ’öõÿ=íõñ~ð¯ÿ¿ÿù?^ÿíÏÿ.‘aþïŸ=õõq~ èo¼þç°ÇÿKfØ×ÿôX´×ÿÅûÀ¿zÿà ßýüßàöç—ÈH÷f˜vþõÏÿu'ñ_óóóÊÿqßmg̘ºàÇï ]wdÑpóÇMÆÏœS–]>%{ÊÔÒ}³÷+•=ÑüLŒyò)/›l~0hZ©ù)}þú󽿋÷#0¿ŸÊo~È©û°1£FŽ6SqF0…}ÍÿÉÓç4þU‹ôï]6ñë±d¤`ãñæCÖå ù¹‚Ô÷„ò-âHü¿ü9‚n#‡ï2Ü|µÖ6ßáì©e–MÍî“=³ìÀòYæ§×Ò±H_Ÿ%ðÑúßÜA—;¼p\ê#ØË¦þõ3ËJg-âÏ`-»æo¾Ò=Æñ•[4Þ\_rÕq|Yg–Ï®äá4¹,»w^Ÿü¼Üÿš¯òB~€!Á`ø=¬ì-ük?´°Ðïâo+]õëÌÿ!ÕÔwª·Ùó2? ´óhó§Eû‘ ßcç_ù‰‹^Xõs ¿¼H-ÊÏ)ÄÏw þå/Ï#¿ÚÄØ?ûY…. |uçÿÝ©:ÁbâÏ~Á|é~u‚þ£¡+‹Ç°–âÏ]i7ºt×?[é¿,dÿß÷«hôö¿|þ?ÿï߯¿~üÏìïÿZ2Ãÿÿ£Ç_=þÿÑÿÿ~yùæñò›ÇÿÀ|ûó?Kdüùçÿsý[‡üüסéå#ÿ·ßÿgEùü¿ùógûù;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ìøÏ ó³ÿ‡$~ÿ³ÿ©ŸÒÏþ×~ö?u„Ä?æo¼˜±ÿÃþ;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì˜?ÌOáwÁRèšHÿ˜þÒèžHÿN€ ,ƒe±–Ç X1‘þ™ÿL¬„•± VÅjXk$Ò?ú¿&ÖÂÚXëb½Dê× $r°>6@Oô†Ø(‘þ½¹Ø›`SôA_l†<ä£úcbc °9¶À–Ø [c E!¶Å0l‡áí±а#vÂHŒÂhŒ‹bŒÅ8ŒÇÎØ»b7”`wì=±öÆìƒRLÄ$LF¦`_ì‡rì0Ó03à¡31 ³1â ŒJ˜ßïp(Ãá8Gb.ªpŽÆ18Çáxœ€jœˆ“p2NÁ©8 >jp:ÎÀ™8 g㜋Zœ‡óq.ÄE¸— —â2\Ž+p%®ÂÕ¨Ç5¸×ázÜ€ 7áfÜ‚[qnÇhĸ wãÜ‹û0M¸àA<„‡ñE3ÃãxOâ)<-xÏâ9<ð"^B+^Æ+x¯áu¼7ц·ð6ÞÁ»xïã´ãC|„ñ >ÅgˆÐÏñ¾ÄWøßà[tâ;|ð#~ÂÏ0þ$º`)tE7,îè ,ƒe±–Ç X2±VÆ*X«au¬,¬‰µ°6ÖÁºXÙÈÁúØ=Ñ b#ôF.6Æ&Ø}Л!ùè‡þ€„Á‚lŽ-°%¶ÂÖØCQˆm1 Ûa8F`{ì€"ìˆ0£0cà¢c1ã±3vÁ®Ø %Ø{`Oì…½1û 1 “Q†)Øû¡ûÃüÚ”©˜†é˜ó X*0³0sp ‚ùE.•8‡â0Ž#p$æ¢ GáhƒcqŽÇ ¨Æ‰8 '㜊Óࣧ㠜‰³p6ÎÁ¹¨Åy8àB\„‹q êp).Ãå¸Wâ*\z\ƒkq®Ç ¸pnÆ-¸·ávÜF܉»p7îÁ½¸óЄûñÄCxàQ4ã1<Ž'ð$žÂÓÑ‚gð,žÃóx/â%´âe¼‚Wñ^Çxmx o㼋÷ð>>@;>ÄGøŸàS|†ø_àK|…¯ñ ¾E'¾Ã÷ø?â'ü óŸD,…®è†¥Ñ=e°,–ÃòX+ÂA&VÂÊX«b5¬Ž5…5±ÖÆ:Xë!9X 'zaCl„ÞÈÅÆØ›¢úb3ä!ýÐ0ƒ0CP€Í±¶ÄVØÛ`( ±-†a; ÇlP„±FbFc \c,ÆaÌCîÇxáa<‚GÑŒÇð8žÀ“x O#D žÁ³xÏ㼈—Њ—ñ ^ÅkxoàM´á-¼wð.ÞÃûøíøác|‚Oñ"tàs|/ñ¾Æ7øøßãüˆŸð3ÌN]°º¢–Fwô@–Á²XËc¬™X +c¬ŠÕ°:Ö@ÖÄZXë`]¬‡lä`}l€žè… ±z#clŠ>è‹Í‡|ôC À@ Â` A6ÇØ[aklƒ¡(͆í0#°=v@vÄN‰Q1pQŒ±‡ñØ»`Wì†ìŽ=°'öÂÞ˜€}PЉ˜„É(Ãì‹ýPŽýq¦b¦cjp:ÎÀ™8 g㜋Zœ‡óq.ÄE¸— —â2\Ž+p%®ÂÕ¨Ç5¸×ázÜ€ 7áfÜ‚[qnÇhĸ wãÜ‹û0M¸àA<„‡ñE3ÃãxOâ)<-xÏâ9<ð"^B+^Æ+x¯áu¼7ц·ð6ÞÁ»xïã´ãC|„ñ >ÅgˆÐÏñ¾ÄWøßà[tâ;|ð#~ÂÏ0üItÁRèŠnXÝÑXËb9,°"db%¬ŒU°*VÃêXYXkam¬ƒu±²‘ƒõ±z¢6ÄFè\lŒM°)ú /6CòÑý11ƒ1Ø[`Kl…­± †¢Ûb¶ÃpŒÀöØEØ;a$Fa4ÆÀE1ÆbÆcgì‚]±J°;öÀžØ {cöA)&b&£ S°/öC9öǘŠi˜ŽðP™˜…Ù˜ƒqF%Á¡8 ‡ã‰¹¨ÂQ8ÇàX‡ãqªq"NÂÉ8§â4ø¨Áé8gâ,œsp.jqÎǸáb\‚:\ŠËp9®À•¸ W£×àZ\‡ëqnD€Ü„›q nÅm¸w wâ.Ü{p/îÃ<4á~<€ñÆ#xÍx ã <‰§ð4B´à<‹çð<^À‹x ­x¯àU¼†×ñÞDÞÂÛxïâ=¼ÐŽñ>Æ'øŸ!B>Çø_ák|ƒoщïð=~Àø ?ÜìK¢ –BWtÃÒèŽÈÀ2XËay¬€á +ae¬‚U±VÇÈšX kc¬‹õ¬ н°!6Boäbcl‚MÑ}±ò~èˆAŒ!(ÀæØ[b+lm0…Øð†c¶Ç(ÂŽØ #1 £1.Š1ã0;cìŠÝP‚ݱöÄ^ذJ1“0e˜‚}±ʱ?ÀTLÃtÌ€‡ ÌÄ,ÌÆˆƒp0*qÅa8GàHÌEŽÂÑ8Çâ8PqNÆ)8§ÁG NÇ8gálœƒsQ‹óp>.À…¸ãÔáR\†Ëq®ÄU¸õ¸×â:\p#4à&ÜŒ[p+nÃí¸¸wán܃{qæ¡ ÷ã<ˆ‡ð0Á£hÆcxOàI<…§¢ÏàY<‡çñ^ÄKhÅËx¯â5¼Ž7ð&ÚðÞÆ;xïá}|€v|ˆð1>Á§ø :ð9¾À—ø _ã|‹N|‡ïñ~ÄOøæD]°º¢–Fwô@–Á²XËc¬™X +c¬ŠÕ°:Ö@ÖÄZXë`]¬‡lä`}l€žè… ±z#clŠ>è‹Í‡|ôC À@ Â` A6ÇØ[aklƒ¡(͆í0#°=v@vÄN‰Q1pQŒ±‡ñØ»`Wì†ìŽ=°'öÂÞ˜€}PЉ˜„É(Ãì‹ýPŽýq¦b¦cjp:ÎÀ™8 g㜋Zœ‡óq.ÄE¸— —â2\Ž+p%®ÂÕ¨Ç5¸×ázÜ€ 7áfÜ‚[qnÇhĸ wãÜ‹û0M¸àA<„‡ñE3ÃãxOâ)<-xÏâ9<ð"^B+^Æ+x¯áu¼7ц·ð6ÞÁ»xïã´ãC|„ñ >ÅgˆÐÏñ¾ÄWøßà[tâ;|ð#~ÂÏ0oò%ÑK¡+ºaitGd`,‹å°>@;>ÄGøŸàS|†ø_àK|…¯ñ ¾E'¾Ã÷ø?â'ü ó]°º¢–Fwô@–Á²XËc¬™X +c¬ŠÕ°:Ö@ÖÄZXë`]¬‡lä`}l€žè… ±z#clŠ>è‹Í‡|ôC À@ Â` A6ÇØ[aklƒ¡(͆í0#°=v@vÄN‰Q1pQŒ±‡ñØ»`Wì†ìŽ=°'öÂÞ˜€}PЉ˜„É(Ãì‹ýPŽýq¦b¦cjp:ÎÀ™8 g㜋Zœ‡óq.ÄE¸— —â2\Ž+p%®ÂÕ¨Ç5¸×ázÜ€ 7áfÜ‚[qnÇhĸ wãÜ‹û0M¸àA<„‡ñE3ÃãxOâ)<-xÏâ9<ð"^B+^Æ+x¯áu¼7ц·ð6ÞÁ»xïã´ãC|„ñ >ÅgˆÐÏñ¾ÄWøßà[tâ;|ð#~ÂÏ0îI¢ –BWtÃÒèŽÈÀ2XËay¬€á +ae¬‚U±VÇæÒXkam¬ƒu±²‘ƒõ±z¢6ÄFè\lŒM°)ú /6CòÑý11ƒ1Ø[`Kl…­± †¢Ûb¶ÃpŒÀöØEØ;a$Fa4ÆÀE1ÆbÆcgì‚]±J°;öÀžØ {cöA)&b&£ S°/öC9öǘŠi˜ŽðP™˜…Ù˜ƒqF%Á¡8 ‡ã‰¹¨ÂQ8ÇàX‡ãqªq"NÂÉ8§â4ø¨Áé8gâ,œsp.jqÎǸáb\‚:\ŠËp9®À•¸ W£×àZ\‡ëqnD€Ü„›q nÅm¸w wâ.Ü{p/îÃ<4á~<€ñÆ#xÍx ã <‰§ð4B´à<‹çð<^ÈH_öÆ;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ì°Ã;ìøçÙã˧–MÞ®tvé¸Ù£J§O˜=Íë;mÖfÑã‹Fo¿Yß ³K'N-ë;%ïïßF^^Þ ²M˜j^¿ôrjôï70;Ÿÿ¤_ÞàþôÏÎ3èŸÈ^Œ›\ô1gÖìÒ™üS&——yeÓþßñŸM™ò'ÛIO%;îÿ•ÑmµîæУJ'e—½[¶†ù;ó»áýpY"uèÔ…íaŽ?V4kœ‹­~óŸ$ùûU'ַ͘Ôó¸“UÌ)Y:}vùô²DÅfyyCò·Ô/¯ÿAeC¶Q>³lÊŒƒ·È1bXÿÁ#†õé7|Hÿ>ÊëS8°ÿ>à ·6`D¿!C†ôðoøªücÆ_=þÿÑÿÿöÜï·ÿýÙÇÿ’÷2REÓ'Í,›V6}véÔÔÁ\.ÑóìÔ5bºV|jüÌø_ú÷ÚñïgõJïã1ï(sIÜÔXàñW2~XGáü&]ý­§új †j¤:ImPuUOõÕ 9ÿöæan(Ôr¤:º橮ꩾ¨¡©æbå©õ—Š'v¹¹W˞ꫪ‘êè+”§ºª§új0ÿ+™˜Ûœš–#Õé¦í¨®ê©¾¨¡©æâ‹©õ—ŽçóYj>ZöT_ ÔPTs·ÔvTWõT_ ºÇóYeg3-Gª¹Tj;ª«zª¯j¨Fª£ fäýráŒÍÍí¸ZöT_ ÔPTgmGuUOõÕ`™x>RóÑr¤:Ëj;ª«zª¯j¨Fª³œÖ_.žÏ±©ùhÙS}5PC5RåµÕU=ÕWƒåãù\“š–#ÕYAÛQ]ÕS}5PC5RµþŠñ|ÂÔ|´ì©¾¨¡©ŽžqòTWõT_ âg¦¹_¥æ£åHu2µÕU=ÕW5T#ÕYIë¯Ïg­]Ì|´ì©¾¨¡©ÎÊڎꪞê«ÁÊñ|†šÛ µ©Î*ڎꪞꫪ‘ꬪõWç3%5-{ª¯j¨Fª³š¶£ºª§új°Z<Ÿ“RóÑr¤:«k;ª«zª¯j¨Fª³†Ö_#žOCj>ZöT_ ÔPT'KÛQ]ÕS}5ÈŠçóBj>ZŽTgMmGuUOõÕ@ ÕHuÖÒúkÅóù!5-{ª¯j¨Fª³¶¶£ºª§új°v<Ÿœ]Í|´©Î:ڎꪞꫪ‘ꬫõ×ç³½¹W˞ꫪ‘ê¬§í¨®ê©¾¬Ïgjj>ZŽT'[ÛQ]ÕS}5PC5R­ŸÏçôÔ|´ì©¾¨¡©Îúڎꪞê«Áúñ|nOÍGË‘êl í¨®ê©¾¨¡©NO­ß3žÏk©ùhÙS}5PC5R^ڎꪞê«A¯x>]v3óÑr¤:j;ª«zª¯j¨Fª³‘Öß(žOos;®–=ÕW5T#Õé­í¨®ê©¾ôŽç3*5-Gª“«í¨®ê©¾¨¡©ÎÆZãx>³RóѲ§új †j¤:›h;ª«zª¯›Äó975-Gª³©¶£ºª§új †j¤:}´~Ÿx>÷¦æ£eOõÕ@ ÕHuúj;ª«zª¯}ãù¼š–#ÕÙLÛQ]ÕS}5PC5Råýr$”Qbæ£eOõÕ@ ÕHuòµÕU=ÕWƒüx>}Íí„ZŽT§Ÿ¶£ºª§új †j¤:ýµ~ÿx>ãSóѲ§új †j¤::Å–§ºª§újŸŠ›[™š–#Õ¨í¨®ê©¾¨¡©Î ­?(žÏÅ©ùhÙS}5PC5RÁڎꪞê«Áàx>¦æ£åHu†h;ª«zª¯j¨FªS õ âù|˜š–=ÕW5T#ÕÙ\ÛQ]ÕS}5Ø<žÏŠ»›ùh9R-´ÕU=ÕW5T#ÕÙRëoÏg ¹W˞ꫪ‘êèôqžêªžê«A|šyîî©ùh9R­µÕU=ÕW5T#ÕÙFëoÏgnj>ZöT_ ÔPTg¨¶£ºª§új04¾™+RóÑr¤:…ڎꪞꫪ‘êl«õ·çóxj>ZöT_ ÔPTg˜¶£ºª§új0,žO”š–#ÕÙNÛQ]ÕS}5PC5RáZx<ŸÕö0óѲ§új †j¤:#´ÕU=ÕWƒñ|¶0·j9RíµÕU=ÕW5T#ÕÙAëïϧ45-{ª¯j¨FªS¤í¨®ê©¾Åó9.5-Gª³£¶£ºª§új †j¤:;iýâù\—š–=ÕW5T#Õ©í¨®ê©¾ŒŒçÓ’š–#Õ¥í¨®ê©¾¨¡©Îh­?:žÏ7©ùhÙS}5PC5R1ڎꪞê«Á˜x>kïiæ£åHutb6OuUOõÕ@ ÕHuе~q<ŸmÍí¸ZöT_ ÔPTGoÿ婮ꩾÌ›01wßÔ|´©Î8mGuUOõÕ@ ÕHuÆkýññ|NIÍG˞ꫪ‘êì¬í¨®ê©¾ìÏç¦Ô|´©Î.ڎꪞꫪ‘êìªõwçóRj>ZöT_ ÔPTg7mGuUOõÕ`·x>?¦æ£åHuJ´ÕU=ÕW5T#ÕÙ]ëïÏgƒ½Ì|´ì©¾¨¡©Îڎꪞê«Áñ|v0·j9R=µÕU=ÕW5T#ÕÙKëïÏgzj>ZöT_ ÔPTgomGuUOõÕ`ïx>g¤æ£åHu&h;ª«zª¯j¨Fª³Öß'žOcj>ZöT_ ÔPT§TÛQ]ÕS}5(çózj>ZŽTg¢¶£ºª§új †j¤:“´þ¤x>]÷6óѲ§új †j¤:“µÕU=ÕWƒÉñ|rÍí„ZŽT§LÛQ]ÕS}5PC5R½!÷ËÓcRóѲ§új †j¤:ûj;ª«zª¯ûÆó™š–#ÕÙOÛQ]ÕS}5PC5Rr­_Ïç¼Ô|´ì©¾¨¡©Îþڎꪞê«Áþñ|îKÍGË‘ê í¨®ê©¾¨¡©ÎT­?5žÏ»©ùhÙS}5PC5Riڎꪞê«Á´x>ËL0óÑr¤:ú|Cžêªžê«ª‘êÌÐú3âùä™Ûqµì©¾¨¡©ŽÞ8ÎS]ÕS}5˜ÿsbîΩùh9R mGuUOõÕ@ ÕHufjý™ñ|MÍG˞ꫪ‘êÌÒvTWõT_ fÅó¹$5-Gª3[ÛQ]ÕS}5PC5R9ZN<Ÿ‡SóѲ§új †j¤:j;ª«zª¯Æóù(5-Gªs¶£ºª§új †j¤:kýƒãùdîcæ£eOõÕ@ ÕHu*µÕU=ÕWƒÊx>ƒÌí„ZŽTçmGuUOõÕ@ ÕHuÕú‡ÆóÙ35-{ª¯j¨Fªs˜¶£ºª§újpX<ŸªÔ|´©Îáڎꪞꫪ‘ê¡õˆçsUj>ZöT_ ÔPTçHmGuUOõÕàÈx>O¤æ£åHuæj;ª«zª¯j¨FªS¥õ«âù|žš–=ÕW5T#Õ9JÛQ]ÕS}58*žÏê¥f>ZŽTçhmGuUOõÕ@ ÕHuŽÑúÇÄóÙÊÜŽ«eOõÕ@ ÕHuŽÕvTWõT_ Žç315-Gªsœ¶£ºª§új †j¤:Çkýããùœš–=ÕW5T#Õ9AÛQ]ÕS}58!žÏõ©ùh9RjmGuUOõÕ@ ÕHuNÔú'ÆóyÖÜN®>¯4ÿ3wæcGæ#Cæc?æ#;æã6æ#3æã.æ£*æã&æ£"æcæ£æcæ#æcæ# æãæ#æí}óÖ¼y{ݼ5nÞÖ6oM›·•Í[Âæm]ó–¬y;5æíLóV¤y;ѼhÞÆ3oÅ™·ÑÌ[`æm,ó”yûȼdÞ¾1o½˜·OÌ[æm óÖƒyÛÀœò7§íÍ)wsºÜœò6§«Í©fsºØœê5§iÍ©Vsšt(ÌiJsŠÑœ4§øÌé9sjÍœ3§¶Ìi)sjÉœ2§tÌisJÅœ1§4Ìés*Áœ0‡òæ0ÜJ›Ã`skCÍ!¤9ü3‡pæðË:™ÃŸ}`;Ì¡ƒÙí7»ìf·Ûì2›Ý]³Ëjv7Í®¢ÙÝ3»jf7Ëì*™Ý³‹bv3Ì.‚yy7/ÑæåÕ¼4š—7óÒd^VÌKƒyZ7OÉæiÕ<%š§³¹0O'æ©À<œÍCÑ<ŒÌCÁÜÍ]ÐÜÌ]ç$œŒSp*NƒùL^ NÇ8gálœ“Húºçá|\€ q.Æ%¨Ã¥‰ôç¿/ǸWájÔã\‹ëp=nÀ0ŸlÀM¸·àV܆Ûqq'îÂݸ÷â>ÌCîÇxáa<‚GÑŒÇð8žÀ“x O'ÒŸAlÁ3xÏ%ÒŸF/â%´âe¼‚Wñ^Çxmx o㼋÷ð>>@;>ÄGøŸàS|óÙÇ|Ž/ð%¾Â×øߢßá{ü€ñR®åÁŸD,…®è†¥Ñ=e°,–ÃòX+Â|æ2+ae¬‚U±VÇÈšX kc¬‹õ¬ н°!6Boäbcl‚MÑ}±Ìg=óÑý11ƒ1Ø[`Kl…­± †¢Ûb¶ÃpŒÀöØEØ;a$Fa4Æ$ÓŸ1-ÆXŒÃxìŒ]°+vC vÇØ{aoLÀ>(ÅDLÂd”a öÅ~(Çþ8S1 Ó1æ³­˜‰Y˜98á`TâŠÃp8ŽÀ‘˜‹*…£q ŽÅq8' 'â$œŒSp*NƒùLm NÇ8gálœƒsQ‹óp>.À…¸ãÔáR\†Ëq®ÄU¸õ¸×â:\p#ÌgypnÆ-¸·ávÜF܉»p7îÁ½¸óЄûñÄCxàQ4ã1<Ž'ð$žÂÓ0ŸnÁ3xÏáy¼€ñZñ2^Á«x ¯ã ¼‰6¼…·ñÞÅ{x â#|ŒOð)>ƒùÌr>Çø_ák|ƒoщïð=~Àø ?üð'ÑK¡+ºaitGd`,‹å° Q‚rT¢µ¨GZІ$ÙùÍD. P„”£5¨CšÐ‚6t"ƒæ,ä¢E(EªPƒ:4 ­hG'2ØÙÎB> QŒRT  µ¨G#šÑŠv$ÙAÏDòQˆb”£Õ¨E=Ñ‚6t ÉŽ}&rP€"” •¨FЄ´¡d!(B *P…Ô¡MhE;:‘ÁDrQˆb”¢U¨A=ÑŒV´£™€ä …(F)*QZÔ£ÍhC’´d"ù(B ÊQ‰jÔ¢MhA:ä`' ¹(@JPŽ*Ô  hB Úщ ’²‹£¨B êЈf´¢Èàà*ù(D1JQjÔ¢hF+:ä€,9ÈG!JPŽJT£õhB ÚÐ$r™ÈEŠP‚rT¢uh@ZІNdp˜…\ ¥¨@jP‡4£íèDŽYÈG!ŠQŠ T¡õhD3ZÑŽ$›™ÈA> QŒrT¢µ¨G#ZІ$9HÍD P„”£Õ¨CšÐ‚6t ƒƒÛ,ä¢E(AªPƒ:4  ­hG'28(ÎB. QŒRT  5¨G#šÑŠvt"“ƒéä£Å(E%ªQ‹z4¢mè@2›u‘ƒ|¡å¨D5jÑ€&´  HæðïE. P„”£ 5¨CšÐ‚vt"ƒþ,ä¢Å(EªPƒ:4¢­hG'26àß‹|¢¥¨@5jQF4£Höd®ÈA> Q‚rT¢µ¨GZІ${±.rQ€"” •½ÒçPÌè‘øåÏÎ6çEìù{¾enžoiJØó-ö|‹=ßbÏ·Øó-ÿç[Ì>„y 7¯ÃæµÔ¼š×´j½v4è9¼EÏ¥æùÐ QŒrT¢µ¨G#ZІ$9hÌD P„”£Õ¨CšÐ‚6t ƒƒÍ,ä¢E(AªPƒ:4  ­hG'28HÍB. QŒRT  5¨G#šÑŠvtšó4Üæ …(F)*QZÔ£ÍhC’æœ r"” •¨F-Є´¡I¤³‹¡å¨B êЀ&´ ÈÈf]ä¢Å(EªPƒ:4¢­hG'2rø÷"…(F)*PZÔ£ÍhE’ìg"ù(D ÊQ‰jÔ¢MhA:Ü€u‘‹¡å¨D êЀ&´  ÈèÉ\‘‹¡¨B êЀf´¢ÈèźÈG!ŠQŠ TõJÓ›aΛ˜s$æ|ˆÙo1ç9Ì9 sþœ«0ç%Ì9s¾Áœ[0çÌ9s~`ŸDú¸ßã›ãysìnŽÓÍ1¹9þ6ÇÚæ¸ÚC›ãesllŽƒÍ1¯9¾—H·>¡ãÑ—tœùŽŽ#šc@s¼gŽíÌqœ9f3ÇgæXÌw™c,sZŽTç|mGuUOõÕ@ ÕHu.ÐúÄó9+5-{ª¯j¨Fªs¡¶£ºª§újpa<Ÿ;SóÑr¤:i;ª«zª¯j¨Fªs±Ö¿8žÏ›©ùhÙS}5PC5RK´ÕU=ÕWƒKâùt›læ£åHuê´ÕU=ÕW5T#Õ¹Të_Ïgs;®–=ÕW5T#Õ¹LÛQ]ÕS}5¸,ž›š–#Õ¹\ÛQ]ÕS}5PC5R+´þñ|LÍG˞ꫪ‘ê\©í¨®ê©¾\ÏçüÔ|´©ÎUڎꪞꫪ‘ê\­õ¯ŽçÓ”š–=ÕW5T#Õ©×vTWõT_ êãù¼—š–#Õ¹FÛQ]ÕS}5PC5Rkµþµñ|–+3óѲ§új †j¤:×i;ª«zª¯×ÅóÉ7·j9RëµÕU=ÕW5T#Õ¹AëßÏg×Ô|´ì©¾¨¡©Îڎꪞê«Áñ|KÍGË‘êڎꪞꫪ‘ê4hý†x>—¦æ£eOõÕ@ ÕHunÒvTWõT_ nŠçóHj>ZŽTçfmGuUOõÕ@ ÕHunÑú·Äóù$5-{ª¯j¨Fªs«¶£ºª§újpk<Ÿ•¦˜ùh9RÛ´ÕU=ÕW5T#Õ¹]ëßÏgˆ¹W˞ꫪ‘êÜ¡í¨®ê©¾ÜÏg¯Ô|´©N£¶£ºª§új †j¤:wjý;ãùš–=ÕW5T#Õ¹KÛQ]ÕS}5¸+žÏÕ©ùh9R»µÕU=ÕW5T#Õ¹GëßÏç©Ô|´ì©¾¨¡©Î½ÚŽêªžê«Á½ñ|¾HÍGË‘êÜ§í¨®ê©¾¨¡©Î<­?/žOÖ¾f>ZöT_ ÔPT§IÛQ]ÕS}5hŠç³µ¹PË‘êÜ¯í¨®ê©¾¨¡©ÎZÿx>“SóѲ§új †j¤:j;ª«zª¯Æó©NÍGË‘ê<¤í¨®ê©¾¨¡©ÎÃZÿáx>7¦æ£eOõÕ@ ÕHuÑvTWõT_ ‰çó\j>ZŽTçQmGuUOõÕ@ ÕHušµ~s<ŸïRóѲ§új †j¤:i;ª«zª¯ÅóYo?3-Gªó¸¶£ºª§új †j¤:Ohý'âù 7·ãjÙS}5PC5R'µÕU=ÕWƒ'ãù쟚–#ÕyJÛQ]ÕS}5PC5R§µþÓñ|üÔ|´ì©¾¨¡©N¨í¨®ê©¾„ñ|nMÍGË‘ê´h;ª«zª¯j¨FªóŒÖ&žÏ+©ùhÙS}5PC5RgµÕU=ÕWƒgãù$ÊÍ|´©Îsڎꪞꫪ‘ê<¯õŸç³¡¹W˞ꫪ‘ê¼ í¨®ê©¾¼Ïg§Ô|´©Î‹ÚŽêªžê«ª‘꼤õ_ŠçS‘š–=ÕW5T#ÕiÕvTWõT_ Zãùœš–#ÕyYÛQ]ÕS}5PC5RW´þ+ñ|îNÍG˞ꫪ‘ê¼ªí¨®ê©¾¼ϧ-5-Gªóš¶£ºª§új †j¤:¯ký×ãùtßßÌG˞ꫪ‘ê¼¡í¨®ê©¾¼ÏgSs;¡–#ÕySÛQ]ÕS}5PC5R6­ßÏglj>ZöT_ ÔPTç-mGuUOõÕà­x>¥æ£åHuÞÖvTWõT_ ÔPTç­ÿN<Ÿ SóѲ§új †j¤:ïj;ª«zª¯ïÆó¹?5-Gªóž¶£ºª§új †j¤:ïký÷ãù|š–=ÕW5T#Õù@ÛQ]ÕS}5ø žÏò˜ùh9RvmGuUOõÕ@ ÕHu>ÔúÆóéonÇÕ²§új †j¤:i;ª«zª¯ÅóÙ-5-Gªó±¶£ºª§új †j¤:ŸhýOâù‘š–=ÕW5T#ÕùTÛQ]ÕS}5ø4žÏe©ùh9RÏ´ÕU=ÕW5T#Õ‰´~ϧ95-{ª¯j¨FªÓ¡í¨®ê©¾tÄóù45-Gªó¹¶£ºª§új †j¤:_hý/âù¬2ÕÌG˞ꫪ‘ê|©í¨®ê©¾|ϧÀÜN¨åHu¾ÒvTWõT_ ÔPTçk­ÿu<Ÿ ©ùhÙS}5PC5Ro´ÕU=ÕWƒoâù“š–#ÕùVÛQ]ÕS}5PC5RN­ßÏçšÔ|´ì©¾¨¡©Îwڎꪞê«Áwñ|žNÍGË‘ê|¯í¨®ê©¾¨¡©ÎZÿ‡x>_¥æ£eOõÕ@ ÕHu~ÔvTWõT_ ~Œç³æ43-Gªó“¶£ºª§új †j¤:?kýŸãù 5·ãjÙS}5PC5R]á*OuUOõÕ ¾ÖܲÔ|´©NRÛQ]ÕS}5PC5R.Z¿ËüÛIœ”š–=ÕW5T#ÕYJÛQ]ÕS}5X*žOš–#Õéªí¨®ê©¾¨¡©N7­ß-žÏ ©ùhÙS}5PC5R¥µÕU=ÕWƒ¥ãù|Ÿš–#Õé®í¨®ê©¾¨¡©N­ß#žOÎt3-{ª¯j¨Fª“¡í¨®ê©¾dÄóan'Ôr¤:Ëh;ª«zª¯j¨Fª³¬Ö_6žÏÔÔ|´ì©¾¨¡©Îrڎꪞê«Árñ|jRóÑr¤:Ëk;ª«zª¯j¨Fª³‚Ö_!žÏí©ùhÙS}5PC5RµÕU=ÕWƒãù¼šš–#Õq´ÕU=ÕW5T#ÕÉÔú™ñ|ºÌ0óѲ§új †j¤:+i;ª«zª¯+ÅóÙÈÜN¨åHuVÖvTWõT_ ÔPTg­¿J<ŸQ©ùhÙSÍ5¹Í0KæKÁ|(«["}íÍîè ,ƒeéëq.°¢¹-d&Ò×è\«`U¬†ÕéëvfaM¬…µ±N"}-ÏõékNæ`}lH_ß³6ÄF‰ôusék~n‚MÑ}±™™#òÑý1ékƒÆDúzç›c‹Dúz¡[aklƒ¡(L¤¯!: Ûa8F`ûDúº¢æZé;b'Œ4_ÌDúZ£cÌ÷Å‹q‰ôõGwÆ.Ø»%Ò×Y7×$Ý{&Ò×7Ýéë”–b"&a2Êék—î‹ýék´ïéë™NÃtÌ0ßKT$Ò×8…Ù˜ƒqP"}ÝSs}÷Cp(Ãá‰ôµPL˜3r‰DŽÂщôõQÅq8'$Ò׆7¹3—3y3W3533õ2Ó2µ2“2q2S2122ñ1Ï1­11m1K1-1 ¹$‘¾î¼ùH…ù8„ùHƒù8‚ù(ù8€y+ß¼ oÞJ7oƒ›·°ÍÛÐæ-dóö¯y ×¼ýjÞ:5oš·.ÍÛŽæ­Có¶ŸyËμífÞ23ow™·¬ÌÛMæ­"óvϼDúz÷æ­ó6‡y‹Â¼Í`Þ"0§÷Í)zszÝœ7§·Í©isZÙœ6§uÍ)YsZÕœ5§3Í)Is:Ñœ 4§óÌ©8sÍœ 3§±Ì)(sÉœ2§oÞDÌ©súœz0§ Ì¡¿9l7‡Üæ°ÙòšÃUsÈiÍ¡ž9\3‡Zæ0Éê˜ÃsˆaÌ.¾Ù=7»Øf÷ØìÚšÝS³kiv Í®]j·Œ¿Ù-2»4fwÄìR˜ÝóRn^ŽÍK©y4/eæeȼ„˜—ónž~ÍS¨yú3O]æéÇ<µ¬ŠÕ°:Ö@V2}Mܵ°6ÖÁºX/™¾NnÖÇè‰^Éôµs7Boäbcl’L_O·úb³¤ÙäñŸL_c·?` ap2}ÝÝlŽ-°%¶J¦¯Å» †¢ÛbX2}}Þáí±Š’éköî„‘…Ñ“L_Ç·c1ã±s2}mß]±J°;öH¦¯÷»öÆì“L_k×\x&£ S°o2}]àrì0Ó’ékÏHš}^ÿ˜‰YÉôõƒçà@„ƒQ™L_SøP†ÃqŽL¦¯3\…£p4ŽÁ±Éôµ‡Ç ¨Æ‰8)™¾ñ)8§%;6ÿdúÅgàLœ…³qN2}ÝâZœ‡óq.L¦¯e|1.A.ÅeÉôõ¯À•¸ W£>™¾æñµ¸×ãܘL¤®ƒÜ€›p3nÁ­Éôµ‘oÇhĸ+™¾^ò=¸÷aš’ék(?€ñÆ#Éôu•ÍuÃãxO&Ó×Z~:iŽ-xüã<›L_ùy¼€ñZ“ék2¿‚Wñ^ÇÉôušÛðÞÆ;x7™¾vóûøíø%Ó×sþŸâ³¤9¦áñŸL_ãù |‰¯ð5¾I¦¯û܉ïð=~ÀÉôµ †yáO¢ –ê’¾>t7,îè ]3zY,‡å±VÔu¤3±VÆ*XU×–^k kb-]oz¬‹õÌ5§‘£kPo€žè… ±‘®Km†y/L¤_kKé×¼j½¶Ôë9¾YϵæùÐ<§™ç%óÜbžÌcÜ<ÍcÉ<ªu¿¬×÷¿EßóµLjNfÿ¨E(A9*Qƒ:4  -hC§ùZ±•…\sl¡¨B êЀf´¢È`ç+ ù(D1JQ*Ô¢hF+Ú‘d‡-9È7×âF1ÊQ‰jÔ¢hA:4×ìF P„”£Õ¨CšÐ‚6t ƒÄ,ä¢À\ï%¨@jP‡4¡íèD†¹.8rQˆb”¢U¨A=ÑŒV´£™ìæ …æšâ(E%ªQ‹z4¢mè@’ØLsíqä£%(G%ªQ‹4¡mè@’ß,ä¢Eæºå(GjP‡4¡íèD;ÍYæúæ(@1JQ*Ô hF+Úщ v¶sB›k££Õ¨E=ÑŒVt Éz&rÌ5ÔQˆ”£Õ¨E=šÐ‚6t ÉŽ}&rQ€"” •¨AЄ´¡d!×\§E(EªPƒ:4 ­hG'28ÈB> QŒRT  µ¨G#šÑŠv$9øÈDò͵àñ?ìÝg´W}ï}ɦX3‡bsl – Xcdªè¢‹:SD„"ê]T‹.ꈮ@ ˆ–- €M´ êˆ@@´ €e‹<ß¿Î/ë¾½ëY뾘Yë³¾ž›;ûhÛçHÓÄ4ÑAŒ0Á K¬±EÑž2jh …z`Œ)æXb-J좂š=o-tÑÇcL1Ç ìP²çÒ£‚:šh£‹>†˜`†VØ`‡®Ê¨¢nÏ´G= 0Â3,°ÆEÈPF ´ÐAŒ0ÅK¬±E‘¹CTPC-tÐÇcL1ÇìPâðÔÐD]ô1Ä3,°Â;”8p,£Š:šh£‹F˜`†VØ¢ÈÁæʨ¢Ž:èa€&˜c‰5¶(rz€ jh …zbŒ)æXbJÜ¢‚h£‹>†cŠVØ`‡Ҩ¢Ž&Úè¢&˜a6(r }€2ª¨£‰z`„ fXb-Ч²-ʨ¡:èa€1¦˜c‰5¶(•y½¨ †Zè¢!ƘbŽ6Ø¡Äÿ!*¨£‰6ºècˆ fX`… v88׋*êh¢a‚Xc‹âl‹2ªh …z`„)æXb-ŠgòzQA ´ÐAÿÌBvÆú¸Â_þ9ú«¶ó"ùù–ü|Ë…ü|˼ŸoÉÏ·äç[òó-ùù–ÿÏ·Ø>„}†Ûç°}–ÚçaOŸ;c½ÿÏõ>¼Öû½gÙû޽wØß¿ý Ûß™ý­ôõ;;ÖïÎBÿ6úwUÒk®¢Ž&Úè¢&˜a6ö”QEMtÐÃ#L0ÃklQdÇëeÔÐ@ ô0ÀS̱Ä[”Øa;D54ÐB} 1Æs¬°Á%vôQAM´ÑECL0Ã+l°³ó4ì –QEM´ÑÃ#L0ÃklQ´s&(£ŠZè ‡F˜bŽ%ÖØ¢ÈÎè!*¨¡:ècˆ1¦˜c‰ v(±{ˆ jh¢.úbŒXaƒJìü–QEM´ÑÅ#L0Ã+lQd‡ùeTQG ô0À̱Ä[ÙÑ>@54ÐB= 1Æs,±Æ%vÐQA ´ÑECŒ1Å+l°C‰ûCTQGmtÑÇ̰À 98@UÔÑD= 0Â3,±ÆE"PF ´ÐAŒ1ÅK¬±E‰ƒCTPC-tÑÇcL1Ç ìPâ åÔÑD]ô1Ä3,°Â;;OÃÁNUÔÑD= 0Â3,°ÆE;g‚2ªh …z`„)æXb-ŠX¢‚h¡ƒ>†cŠ9–Ø`‡d‡¨ †&Úè¢!Ƙa6ءěpUÔÑD] 0Â3,°ÂEþPFu´ÐAŒ0ÁK¬±E‘ƒÆTPC-tÐÃcL1ÇkìPâ`óÔÐ@]ô1ÄS,°Â;”8H=Du4ÑF}Œ0Á ¬°A‘Û”QEMtÐÃ#L0ÃklQ䀸eÔÐ@ ô0ÀS̱Ä[”8>D54ÐB} 1Æs¬°Á¥SÙÔÑD]ô1Ä3,°Â;”y½¨¢Ž&Úèa€&˜a5¶(Ú9”QE-tÐÃ#L1ÇklQ<׋ jh …úbŒ)æXbƒJg°-*¨¡‰6ºècˆ1fX`… v(ÉëEu4ÑFƒ3Žém±s'vžÄΉØù;×aç5솯°sv¢^8:¿`çî¢ý;`ÇþvœoÇôvünÇêv\nÇàv¼mÇÖ¯Õ~Û±°÷Ú1®Ï~ZÇ©K~GÇ•/þVÇvÌgÇwv,wí+Ùñ˜{Ùq–SÙñ“+Ý\ûOv¼cÇ6÷Ò¾”ŸØ±ˆwØ1†Oرà µeûÿ¶¯oûõ¶oûë¶onûásí_QûÍ+íÿ‡ös·Úµ}Õcµz)í[ž¬}FÛ?ä×pþÂægÇa¶¯oÿ·¶vŒ}«¹]ÿb?iˆ]Ãâ?Ò®M±3B‘}’–v݈}Œ1Jì/t±²k:|þOìZ ÿa{XÛõ>Ÿ§v…ÏÚ>6v-„ÏΙ]ãà³p€­]§à³mn×ø…bg×øÜYص>CF(ò™ÐÁÒÎÛó?F‰÷ë.VvN_¤‰+çý´‡µïæýqjç±y¯ëccç¢yïšÙ9fÞ‹ØÚyb~çvþ—÷‰!vv—¿û…›å_ÞEþ&;X¢ÆßØ%~i»XÙ9M~ÿ'v®’ßóÞ_[<«ð—¾F!_ò%_ò%_ò%_ò%_ò%_ò%_ò%_ò%_ò%_ò%_ò%_ò%_ò%_ò%_ò%_ò%_ò%_ò%_ò%_ò%_ò%_ò%_ò%_ò%_ò%_ò%_ò%_ò%_ò%_ò%_ò%_ò%_ò%_ò%_ò%_ò%_òåÿjyå™…Bò—G æM/¤ôÿÇ£ ië|ZLõjt¹£^CU§&ª¿ÜÿxÁcöÏ`ÔzP£4Ž«NMT¯¦jP£µý‰Ù ÍþŒZwj¢z5Uƒ]^㨱êÔDõ—Ïæó‘ý|´ÔèP㨱êÔDõjª5:IÛŸ”ÍçûùhÝ©‰êÕT jt²ÆQcÕ©‰êOÎæsœ³ùh=¨Ñ4Ž«NMT¯¦jP£S´ý)Ù|®n?'ÖºSÕ«©ÔèŠGU§&ª¿b6Ÿ»ìç£õ FWÒ8j¬:5Q½šªANÕö§fóéíç£u§&ªWS5¨QY㨱êÔDõål>¯ßÏGëANÓ8j¬:5Q½šªAN×ö§góùÔ~>Zwj¢z5Uƒ¡qÔXuj¢ú3²ùlöóÑzP£35Ž«NMT¯¦jP£+kû+gó¹T׿£u§&ªWS5¨ÑU4Ž«NMT•l>ײŸ“j=¨ÑYGU§&ªWS5¨QEÛW²ùÜs?­;5Q½šªAÎÖ8j¬:5QýÙÙ|ž±ŸÖƒ]U㨱êÔDõjª5ºš¶¿Z6Ÿ·ìç£u§&ªWS5¨Ñ9GU§&ª?'›ÏçöóÑzP£«k5Vš¨^MÕ Fçjûs³ù„ý|´îÔDõjª5º†ÆQcÕ©‰ê¯‘Íçr±ùh=¨QU㨱êÔDõjª5:OÛŸ—Íçösb­;5Q½šªA®©qÔXuj¢úkfó¹ÿ~>Zjt-£ÆªSÕ«©ÔèÚÚþÚÙ|ž»ŸÖš¨^MÕ F×Ñ8j¬:5Qýu²ù¼c?­5º®ÆQcÕ©‰êÕT jt=m½l>Ëý|´îÔDõjª5ªi5Vš¨¾–Íçwûùh=¨Ñõ5Ž«NMT¯¦jP£hûdó¹Âcm>Zwj¢z5UƒÝP㨱êÔDõ7ÌæS·Ÿ“j=¨Ñ4Ž«NMT¯¦jP£kûgóyÈ~>Zwj¢z5UƒÝD㨱êÔDõ7Éæó¢ý|´Ôè¦GU§&ªWS5¨Q]Û׳ù¼g?­;5Q½šªAn¦qÔXuj¢ú›eóùú~>Zjts£ÆªSÕ«©ÔèÚþÙ|þg?­;5Q½šªAn©qÔXuj¢ú[fó9íq6­5º•ÆQcÕ©‰êÕT jtkmël>·±ŸkÝ©‰êÕT jt£ÆªSÕß&›Ï#÷óÑzP£†ÆQcÕ©‰êÕT jt[mÛl>/ßÏGëNMT¯¦jP£Ûi5Vš¨þvÙ|>¸ŸÖƒÝ^㨱êÔDõjª5ºƒ¶¿C6Ÿïîç£u§&ªWS5¨Ñ5Ž«NMTÇl>Ç>Þæ£õ FwÒ8j¬:5Q½šªAbmgó©ØÏ‰µîÔDõjª5jj5Vš¨¾™ÍçŽûùh=¨Ñ5Ž«NMT¯¦jP£»hû»dóyÜ~>Zwj¢z5Uƒ¯qÔXuj¢úó³ùŒöóÑzP£»j5Vš¨^MÕ FwÓöwËæó±ý|´îÔDõjª5º»ÆQcÕ©‰êïžÍç‡ûùh=¨Ñ=4Ž«NMT¯¦jP£–¶oeó¹Øl>Zwj¢z5UƒÝS㨱êÔDõ÷Ìæs®ýœTëAî¥qÔXuj¢z5UƒÝ[Ûß;›Ï]÷óѺSÕ«©Ôè>GU§&ª¿O6Ÿ'ïç£õ F÷Õ8j¬:5Q½šªAî§íï—ÍçûùhÝ©‰êÕT jt£ÆªSÕß?›Ï§÷óÑzP£¶ÆQcÕ©‰êÕT jômÿ€l>ÿ¹ŸÖš¨^MÕ FÔ8j¬:5Qý³ùDO´ùh=¨Ñßh5Vš¨^MÕ FÒöÊæsû9±Öš¨^MÕ FÖ8j¬:5Qýƒ³ùÜk?­5zˆÆQcÕ©‰êÕT jôPmÿÐl>ýý|´îÔDõjª5êh5Vš¨¾“Íç­ûùh=¨ÑÃ4Ž«NMT¯¦jP£‡kû‡góùÂ~>Zwj¢z5Uƒ=B㨱êÔDõÈæ³ÝÏGëA©qÔXuj¢z5Uƒ=JÛ?*›Ï‰O²ùhÝ©‰êÕT jôh£ÆªSÕ?:›Ï íç¤Zjä4Ž«NMT¯¦jP£®¶ïfóyÀ~>Zwj¢z5Uƒ=F㨱êÔDõÉæó¼ý|´Ôè±GU§&ªWS5¨Ñã´ýã²ù¼k?­;5Q½šªA¯qÔXuj¢úÇgóùÊ~>Zjô£ÆªSÕ«©Ôè‰Úþ‰Ù|~¿ŸÖš¨^MÕ FOÒ8j¬:5Qý“²ùœÒ³ùh=¨QO㨱êÔDõjª5z²¶r6Ÿ›ÛωµîÔDõjª5zŠÆQcÕ©‰êŸ’Íç¡ûùh=¨ÑS5Ž«NMT¯¦jP£§iû§eóyÉ~>Zwj¢z5Uƒ=]㨱êÔDõOÏæóÞý|´ÔèGU§&ªWS5¨ÑÚþ‚l>«ý|´îÔDõjª5êk5Vš¨¾ŸÍçOûùh=¨Ñ35Ž«NMT¯¦jP£giûgeó9ãÉ6­;5Q½šªAž­qÔXuj¢úggóiØÏIµÔè9GU§&ªWS5¨Ñsµýs³ùZjô|£ÆªSÕ«©Ôh íÙ|>´ŸÖš¨^MÕ F/Ð8j¬:5Qý ²ùüû~>ZjôB£ÆªSÕ«©ÔèEÚþEÙ|.ü›Öš¨^MÕ F/Ö8j¬:5Qý‹³ùœm?'ÕzP£—h5Vš¨^MÕ F/Õö/Íæïç£u§&ªWS5¨Q¢qÔXuj¢ú$›Ïã÷óÑzP£¡ÆQcÕ©‰êÕT jô2mÿ²l>¯ÝÏGëNMT¯¦jP£—k5Vš¨þåÙ|>¾ŸÖƒ½B㨱êÔDõjª5z¥¶e6Ÿíç£u§&ªWS5¨Ñ«4Ž«NMTÿªl>ªÍGëA^­qÔXuj¢z5Uƒ½FÛ¿&›OÕ~N¬u§&ªWS5¨ÑH㨱êÔDõ£l>wÛÏGëAþV㨱êÔDõjª5z­¶m6Ÿ§îç£u§&ªWS5¨Ñë4Ž«NMTÿºl>ãý|´Ôèï4Ž«NMT¯¦jP£×kû×góù—ý|´îÔDõj嘣ÿ϶fÿx,ì¦,{îæEpQ‡.V8zç%pIKÙÏ.=ŸóÒ¸ .‹Ëá„ÂÑ3;íyã‡8 'ã …£çx^W*=s²ŒÓ GÏö<gâʸJá蹈ö¼Ï³qU\ çàê…£g€Ú³«8×ĵ GϽ®‹ëŽžw~ýÂѳBoˆáƸ nZ8z~èÍpsÜ·Ä­ GϽMáèYé·ÅípûÂÑsFïˆ;Ù+4qçÂѳGÏÇ]q7Ü÷(=ôž¸î]8z¾é} GÏ(½?Úxˆ¿)=·ôÁxZ8zFûà GÏ2}‰GáÑöß¶pô|ÓÇà±x'Žžyú¤ÂÑóÝŸŒ§à©…£ç >ÏÀ…£g~>³pôlÔgã9x.ž‡çŽž—ú¼/‹ñ’ÂÑ3T“Âѳã_†—ㅣ窾 ¯Æk0Âߎžµú:ü^7à…£ç¯¾ 7ã-xká虬oÃoÇ;ðÎÂÑsZß„/=³þ=…£g·¾ÿ„÷ãø`áèy®Â?ãÃø>Z8zÆëÇ GÏ»ÿ>‰OŽžûúü >‹>W8zìðE| _FZ8z>ìWð¯ø*¾†+=3öXá›ø¾]8zŽìwñïøÖø~áèÙ²?ÄàGø1~R8zëOñŸø~Ž_ŽžA°Å¯ðkü¦pô\Úßá¿ð{ìðß…£gÕþÿƒ?áϰ?~{~í18Â…q‘âÑ3mC ÃÅq‰âÑsnÇ¥á—.=ûÖnÕ´Û,íVI»ÍÑnQ´Û íA»½ÏnѳÛëìÖ8»½ÍnM³ÛÊìÖ0»­Ënɲ۪ì–(»ÉnI²Û‰ìV »ÇnűÛhìV»ÅnA±ÛHì»}ÃnÁ°Û'ìÖ»}Án=°ÛìÒ¿]¶·KîvÙÜ.yÛåj»äl—‹íR¯]®µK­v™Ô.uÚeJ»Äh— íŸ]ž³KlvyÌ.mÙå)»´d—…ìÒŽ]–±K*vYÄ.iØå»¤`—ìT¾Ž·SévÜNeÛih;…l§í®~µS¨vúÓN]ÚéG;uh§ýìÔv³SfvÚËNYÙé&;ed§{ìTn±S%všÃNUØi;E`‡ùvˆn‡×vˆl‡·vhj‡—vhh‡uvhf‡UvHd‡5vHb‡vH`»ó¶+n»Ó¶+l»±¶+j»‘¶ h»q¶ f»O¶ d»/¶ëa»öÑoÛöÑk›ö‘g[öô¼c¼©xôlã7ã-x+þo+=ïøíxÞ‰wáÝÅ£g {Lñ¼ï+=ùýø>X‚âcøxñèùÉŸÀ'ñ)|Ÿ)=Sù³Xàsø<¾P §ã œ‰+ë™ÔgiÅ>ãë…£ÏÚváè3m Ï–‰Þãz/µ÷C{O³÷%{o±÷û¶¿Cû[²¿‡~/§úï¿ÔûwYÔk¶ý£öìktÐÇcL1Çììß;Q‡öŒlÔÐD]ô1Ä3,°Â;”Øù*£Š:šö|mt1À̰À [Ùa;@ÙžÃ:Zè ‡F˜`Ž%ÖØ¢ÈŽÞ*¨¡:èaˆ1¦˜c‰5v(±ƒxˆŠ=ë ´ÑECŒ1Å+l°C‰ËCTQGmtÑÇ̰À Ù=@U{ž8šè ‡F˜`†%ÖØ¢hÏG54ÐB= 0Æs,±Æ%v~QAÍžYŽºècˆ1¦˜c… v(Ù³ÍQAM´ÑECL0Ã+l°Ã;ÛeTQ·ç¢£a‚Xc‹";èöütTÑ@ ô0ÀS̱Ä[Ù±?D54ìÙëè !ƘbŽ%6Ø¡ÄÁ¡=£54ÑF} 1Æ ¬°Á%$ʨ¢Ž¦=ß] 0Â3,°ÂE>P¶çÀ£Ž:èa€&˜c‰5¶(rÐr€ jh …zbŒ)æXbJì¢bÏšGmtÑÇcL±À ìPâ éUÔÑD]ô1Â3,°ÂE¬PFÕžg&:èa€&˜a‰5¶(ÚsïQF ´ÐAŒ1ÅK¬±E‰¹CTPC-tÑÇcL1Ç ìPâðÔÑD]ô1Ä3,°Â;pàXFu4ÑFŒ0Á ¬±E‘ƒÍ”QE-tÐÃ#L1ÇklQä õÔÐ@ ô1ÄS̱Ä;”8¸=D54ÑF} 1Æ ¬°Á%ŠË¨¢Ž&Úèb€&˜a¶(r }€2ª¨£…z`„ æXb-Ч²-*¨¡:èaˆ1¦˜c‰5v(•™+*¨¡6ºècˆ1¦X`… v(qÀˆ*êh¢.úa‚Xaƒâé¼^”QEMtÐÃ#L0ÃklQ<ƒmQF ´ÐAŒ1ÅK¬±EéL^/*¨¡ºèŸytÅ–ã ùçè¯þÙ΋äç[òó-òó-óB~¾%?ß’ŸoÉÏ·äç[þ_<ßbûönŸÃöYjŸ‡}}îŒõþ?×ûðFïwöžeï;öÞaÿö7jgö·Ò×ïìX¿ ý7ÚèßUI¯©Š:šh£‹F˜`†VØÚ¿Gv PFu´ÐAŒ0ÁK¬±E‘¯TPC-tÐÃcL1ÇkìPb‡íÔÐ@]ô1ÄS,°Â;”ØÑ;Du4ÑF}Œ0Á ¬°A‘ÔQEMtÐÃ#L0ÃklQd§òeÔÐ@ ô0ÀS̱Ä[”Ø=D54ÐB} 1Æs¬°Á%vbQAM´ÑECL0Ã+l°³ó4ìü–QEM´ÑÃ#L0ÃklQ´s&(£ŠZè ‡F˜bŽ%ÖØ¢ÈŽö!*¨¡:ècˆ1¦˜c‰ v(±ƒ~ˆ jh¢.úbŒXaƒJìØ—QEM´ÑÅ#L0Ã+lQä`àeTQG ô0À̱Ä[9ˆ8@54ÐB= 1Æs,±Æ%>QA ´ÑECŒ1Å+l°C‰ƒ–CTQGmtÑÇ̰À 9Ð9@UÔÑD= 0Â3,±ÆEPF ´ÐAŒ1ÅK¬±E‰«CTPC-tÑÇcL1Ç ìPâ€ìÔÑD]ô1Ä3,°Â;;OÛpUÔÑD= 0Â3,°ÆE;g‚2ªh …z`„)æXb-Š4¢‚h¡ƒ>†cŠ9–Ø`‡›‡¨ †&Úè¢!Ƙa6Ø¡ÄAjUÔÑD] 0Â3,°ÂElPFu´ÐAŒ0ÁK¬±E‘âTPC-tÐÃcL1ÇkìPâ@úÔÐ@]ô1ÄS,°Â;”Ne[TQGmtÑÇ̰À ˼^”QEMtÐÃ#L0ÃklQä`ÿeÔÐ@ ô0ÀS̱Ä[”Nçõ¢‚h¡‹>†cŠ9VØ`‡Òl‹ êh¢.úb‚Xaƒ§9“׋*êh¢gÓÛbçOì\‰±s v¾ÃÎmØy ;gaç'ì\ÄM Gçì|‚;°óvNÀŽÿíXߎëíÞŽ×íØÜŽÃí˜ÛޝíXú…£cd;¶c_;εcÚOéX5Õ1è·ulù3þFÇ‚vÜgÇxvö´ïü íÿPûºAû°¶¿zŒöC×þåIÚo´}D~÷ç?ìÛŽãìXÁöG{XÛµ2ö­¦v Œÿ0}lì:û=3»>Å~Ì[»ÆÄ~ÉÜ®±1Äήÿ°Ï°°ë:ü¡ÈçyK»æÂçó%>k»XÙõ>;'vƒÏÂÖv­‚_„©]ƒàsª]Gàsgf×ø`kçøù\˜Û¹{Þã‡ØÙùw~v^÷ßмŸv°´sÞ¼?ŽQâ½®‹•æ½kbç™ù¥ëamçŠyo™Ú9`Þ'úØØy\þîgv~–ylÑâïrŽ ¿¬Cììü'3 ;¯ÉïÿE~Ï;unñ¬Â_þù…|É—|É—|É—|É—|É—|É—|É—|É—|É—|É—|É—|É—|É—|É—|É—|É—|É—|É—|É—|É—|É—|É—|É—|É—|É—|É—|É—|É—|É—|É—|É—|É—|É—|É—|É—|É—|É—ÿ«å‘×/Ò¿<°Pøø3oz!ýcø?mH[çÓbô†£ÿÃ5ÔXuj¢ú7üïülÿ F­5z£ÆQcÕ©‰êÕT j4Ööãì…^úiö F­;5Q½šªAÞ¤qÔXuj¢ú7eó¹®ýœTëAþ^㨱êÔDõjª5z³¶s6Ÿûìç£u§&ªWS5¨Ñ[4Ž«NMTÿ–l>ÏÜÏGëAÞªqÔXuj¢z5Uƒýƒ¶ÿ‡l>oÛÏGëNMT¯¦jP£·i5Vš¨þmÙ|¾¸ŸÖƒM4Ž«NMT¯¦jP£·kû·góùõ~>Zwj¢z5Uƒ½C㨱êÔDõïÈæsù§Û|´ÔèGU§&ªWS5¨Ñ»´ý»²ùÜØ~N¬u§&ªWS5¨Ñ»5Ž«NMTÿîl>ÜÏGëAþQ㨱êÔDõjª5òÚÞgóìç£u§&ªWS5¨ÑT㨱êÔDõÓl>ïÞÏGëAÞ£qÔXuj¢z5Uƒ½WÛ¿7›ÏW÷óѺSÕ«©Ôè}GU§&ª_6ŸÝ~>ZjôOGU§&ªWS5¨Ñûµýû³ù\é6­;5Q½šªA> qÔXuj¢údó¹…ýœTëA>¨qÔXuj¢z5UƒÍ´ý,›ÏÃöóѺSÕ«©ÔèCGU§&ªÿP6Ÿ—îç£õ Fÿ¬qÔXuj¢z5Uƒ}XÛ8›Ï?íç£u§&ªWS5¨ÑG4Ž«NMTÿ‘l>ßÜÏGëA>ªqÔXuj¢z5Uƒ}LÛ,›Oá›Öš¨^MÕ F×8j¬:5Qýdzùœi?'ÕzP£¹ÆQcÕ©‰êÕT jô mÿ‰l>·ÛÏGëNMT¯¦jP£Oj5Vš¨þ“Ù|Ü~>Zjô)£ÆªSÕ«©ÔèÓÚþÓÙ|^µŸÖš¨^MÕ FŸÑ8j¬:5Qýg²ùüó~>Zjô/GU§&ªWS5¨Ñgµýg³ù¬÷óѺSÕ«©Ôh¡qÔXuj¢úE6Ÿ‹ôm>Zjô9£ÆªSÕ«©ÔèóÚþóÙ|®f?'ÖºSÕ«©Ôè GU§&ªÿB6Ÿæ~>ZjôE£ÆªSÕ«©ÔèKÚþKÙ|ž¸ŸÖš¨^MÕ F_Ö8j¬:5Qý—³ù¼n?­5J5Ž«NMT¯¦jP£¥¶_fóùÄ~>Zwj¢z5Uƒ}E㨱êÔDõ_Éæóãý|´Ôè_5Ž«NMT¯¦jP£¯jû¯fó¹ä3m>Zwj¢z5Uƒ}M㨱êÔDõ_ËæsžýœTëAþM㨱êÔDõjª5úº¶ÿz6Ÿ{ìç£u§&ªWS5¨Ñ74Ž«NMTÿl>OÛÏGëAVGU§&ªWS5¨Ñ7µý7³ùüý~>Zwj¢z5Uƒ}K㨱êÔDõßÊæóÙý|´ÔèÛGU§&ªWS5¨Ñw´ýw²ùüb?­;5Q½šªA¾«qÔXuj¢úïfó¹Ì³l>ZjôïGU§&ªWS5¨Ñ÷´ý÷²ùÔìçÄZwj¢z5Uƒ­5Ž«NMT¿Îæsßý|´ÔèûGU§&ªWS5¨Ñ´ý²ù<{?­;5Q½šªA~¨qÔXuj¢úfó™ìç£õ Fÿ¡qÔXuj¢z5UƒýHÛÿ(›Ï—÷óѺSÕ«©ÔèÇGU§&ªÿq6Ÿßìç£õ F?Ñ8j¬:5Q½šªA6Ú~“Íç¤gÛ|´îÔDõjª5ú©ÆQcÕ©‰êšÍç&ösR­5úO£ÆªSÕ«©ÔègÚþgÙ|´ŸÖš¨^MÕ F?×8j¬:5Qýϳù¼`?­5ú…ÆQcÕ©‰êÕT jôKmÿËl>~?­;5Q½šªA‚ÆQcÕ©‰êC6Ÿ¯íç£õ F[£ÆªSÕ«©ÔèWÚþWÙ|þ°ŸÖš¨^MÕ F¿Ö8j¬:5Qý¯³ùœú›ÖƒýF㨱êÔDõjª5ú­¶ÿm6Ÿ[ÙωµîÔDõjª5úÆQcÕ©‰ê—Íçáûùh=¨Ñi5Vš¨^MÕ F¿×ö¿Ïæ3ÜÏGëNMT¯¦jP£ÆQcÕ©‰êwÙ|Þ¿ŸÖƒý·ÆQcÕ©‰êÕT jômÿ‡l>ßÞÏGëNMT¯¦jP£?j5Vš¨þÙ|ŠÏµùh=¨Ñÿh5Vš¨^MÕ FÒöÊæsû9±Öš¨^MÕ FÖ8j¬:5QýŸ³ùÜ~?­5*=œùj¬:5Q½šªAŠÚ^eyÌ~>Zwj¢z5Uƒ£qÔXuj¢úcþ÷ç\ðêý|´ÔèX£ÆªSÕ«©ÔèBÚþBÙ|>²ŸÖš¨^MÕ FÖ8j¬:5Qý…³ù|?­5ºˆÆQcÕ©‰êÕT jtQmÑl>Ç=Ïæ£u§&ªWS5¨ÑqGU§&ª?.›Ï9ösR­5*i5Vš¨^MÕ FÓöËæs—ý|´îÔDõjª5º¸ÆQcÕ©‰ê/žÍçIûùh=¨Ñ%4Ž«NMT¯¦jP£KjûKfóyý~>Zwj¢z5Uƒ¯qÔXuj¢úã³ù|r?­5º”ÆQcÕ©‰êÕT jiû(›Ïf?­;5Q½šªA4Ž«NMTÍçøçÛ|´ÔèÒGU§&ªWS5¨Ñe´ýe²ù\Ë~N¬u§&ªWS5¨Ñe5Ž«NMTÙl>­ý|´ÔèrGU§&ªWS5¨Ñ Úþ„l>ÏØÏGëNMT¯¦jP£5Ž«NMTb6Ÿ7ïç£õ F—×8j¬:5Q½šªAµýa6ŸÏíç£u§&ªWS5¨ÑIGU§&ª?)›Ï/÷óÑzP£“5Ž«NMT¯¦jP£+hû+dó¹ÜÀæ£u§&ªWS5¨Ñ)GU§&ª?%›Ïõíç¤ZjtE£ÆªSÕ«©ÔèJÚþJÙ|֚¨^MÕ F§j5Vš¨þÔl>ÏÙÏGëAÊGU§&ªWS5¨ÑiÚþ´l>ïØÏGëNMT¯¦jP£Ó5Ž«NMTz6Ÿt?­5:C㨱êÔDõjª5:SÛŸ™ÍçwûùhÝ©‰êÕT jte£ÆªSÕ_9›ÏÉ/°ùh=¨ÑU4Ž«NMT¯¦jP£³´ýYÙ|êösb­;5Q½šªA*GUWÉ^ÿƒmÜDë^MÕ Fgk5Vš¨^MÕpvöú_d?'ºªÆQcÕ©‰êÕT jt5m¯Æª»Z6Ÿé~>Z÷jª5:G㨱êÔDõjª†s²ù|}?Ÿ«k5Vš¨^MÕ Fçj{5VݹÙ|þ¸ŸÖ½šªA®¡qÔXuj¢z5UÃ5²ùœöB›OU㨱êÔDõjª5:OÛ«±êÎËæskû9‰Ö½šªA®©qÔXuj¢z5UÃ5³ùZ÷jª5ºŽÆQcÕ©‰êÕT ×ÉæóÁý|®«qÔXuj¢z5Uƒ]OÛ«±ê®—Íç;ûùhÝ«©Ô¨¦qÔXuj¢z5UC-›Ï±/²ù\_㨱êÔDõjª5º¶WcÕÝ ›ÏYös­{5UƒÝP㨱êÔDõjª†fó¹ã~>7Ò8j¬:5Q½šªAn¬íÕXu7ÎæóØý|´îÕT jt£ÆªSÕ«©n’Íg´ŸÏM5Ž«NMT¯¦jP£º¶WcÕÕ³ù|t?­{5UƒÝL㨱êÔDõjª†›eóùá~>7×8j¬:5Q½šªAn¡íÕXu·ÈæSz±ÍGë^MÕ F·Ô8j¬:5Q½šªá–Ù|εŸÝJ㨱êÔDõjª5ºµ¶WcÕÝ:›ÏùûùhÝ«©Ôè6GU§&ªWS5Ü&›Ï“÷óih5Vš¨^MÕ F·Õöj¬ºÛfóyÃ~>Z÷jª5ºÆQcÕ©‰êÕT ·Ëæóéý|n¯qÔXuj¢z5UƒÝAÛ«±êîÍç§ûùhÝ«©ÔèŽGU§&ªWS5Ü1›Oô›Ï4Ž«NMT¯¦jP£XÛ«±êâl>×¶Ÿ“hÝ«©Ô¨©qÔXuj¢z5UC3›Ï½ö󹳯QcÕ©‰êÕT jtm¯Æª»K6Ÿ öóѺWS5¨ÑùGU§&ªWS5œŸÍç­ûùÜU㨱êÔDõjª5º›¶WcÕÝ-›Ïç÷óѺWS5¨ÑÝ5Ž«NMT¯¦j¸{6Ÿí~>÷Ð8j¬:5Q½šªAZÚ^U×ÊæsÂKm>Z÷jª5º§ÆQcÕ©‰êÕT ÷ÌæsCû9ѽ4Ž«NMT¯¦jP£{k{5Vݽ³ù´÷óѺWS5¨Ñ}4Ž«NMT¯¦j¸O6Ÿçíçs_£ÆªSÕ«©Ôè~Ú^Uw¿l>ïÜÏGë^MÕ F÷×8j¬:5Q½šªáþÙ|¾²ŸO[㨱êÔDõjª5z€¶WcÕ= ›Ïíç£u¯¦jP£j5Vš¨^MÕðÀl>§$6Ÿ¿Ñ8j¬:5Q½šªA¤íÕXuÊæs3û9‰Ö½šªA¬qÔXuj¢z5UóùZ÷jª5êh5Vš¨^MÕÐÉæóÞý|¦qÔXuj¢z5Uƒ=\Û«±êžÍçûùhÝ«©ÔèGU§&ªWS5<"›ÏŸöóy¤ÆQcÕ©‰êÕT jô(m¯Æª{T6ŸÓ‡6­{5Uƒ=Z㨱êÔDõjª†GgóiØÏ‰œÆQcÕ©‰êÕT jÔÕöj¬ºn6ŸGíç£u¯¦jP£Çh5Vš¨^MÕð˜l>¯ØÏç±GU§&ªWS5¨Ñã´½«îqÙ|fûùhÝ«©ÔèñGU§&ªWS5¨÷9öè§ÙÕû±Uû²é…q\Ç¡„‹áâ¸.‰ãq){ 8À¥q\—à 8—Ç!NÂɸNÁq%œŠ2NÃé8gâʸ ÎBg㪸ÎÁÕq®ÍUœ‡kâZ¸6®ƒëâz¨áú¸nˆáƸ nŠ:n†›ã¸%n…[ã6hමn;àŽ¸“ý;GwÆ]p>áî¸Z¸'î…{Û¿wÜ÷ÃýÑÆð@ü „ã!x(:xŽGà‘x]°{3 ….ƒÇâqx<ž€'âIèáÉx žŠ§áéx.@Ïijðl<ÏÅóð| ð¼/‹ñ¼´`÷„ C¼ /Ç+ðJ¼ ¯Æk0Âßâµxþ¯ÇðFŒñ&ü=ÞŒ·à­ø¼ ¼ïÀ;ñ.¼ÿX°{Q …)Þƒ÷â}ø'¼À1ÇðÏø0>‚âcø8æø>‰OáÓø þŸÅŸÃçñ|_— vL¡°ÄWð¯ø*¾†Ã×ñ ¬ðM| ߯wð]ü;¾‡5¾à‡øü?ÆO°ÁOñŸø~Ž_à—»÷†|ü ¿Æoð[üÿ…ßc‡ÿÆðGüþ„?Ãþø‹8ÇâB¸0.‚‹â8”p1\—À%q<.U´{~øûÇ¥q\—à 8—Ç!NÂɸNÁq%œŠ2NÃé8gâʸ ÎBg㪸ÎÁÕqnÑî5âïçᚸ®ëຸj¸>n€âF¸1n‚›¢Ž›áæ¸n‰[áÖ¸ ¸-n‡Ûã¸#î„MÜwÁù¸+ãhីîûྸî6€âoð <ÁCÑÁÃðp<Ä£ðè¢ÝSÅß?ƒÇâqx<ž€'âIèáÉx žŠ§áéxFÑîlãïÏijðl<ÏÅóð| ð¼/‹ñ¼´h÷rñ÷—áåx^‰WáÕx Fø[¼¯ÃßÁþ· ìvÀþ§ìkÿö•}ûÚ½}eÞ¾în_Y·¯›ÛWÅíëÞöUmûšµ}UÚ¾æl_Q¶¯ÛW„íë½ö]ûz­}5Ö¾Þj_Mµ¯•ÚWCíkö•LûZ¥}%Ò¾Îh_I´¯ÚWíë|öU<û}ξÆf_A³¯‘ÙWÀìë[ö,ûú”}õɾ¾d_=²¯ ÙWìk;ö•ûÚŒ}åž®b_9±¯‹ØW=ìëöU ûš„}ÕÁ¾¦`_1°¯ Ø-þv{¾Ýbo·ÇÛ­ív{ºÝZn·…Û­Ýv[¶ÝRm·EÛ-Ív;²ÝRl·Û­¼v;®ÝJk·ÁÚ­¬vªÝBj·Ú-œvû¥ÝBi·?Ú­‹vû¡Ý:¸¿í~»íÎn™³ÛÞì–5»ÝÌn³Û½ìV-»ÝÊn•²ÛœìV%»ÍÈn²Û|컽Æn‘±Û[ìÖ»½Än ±Û:ìÖ »­Ân‰°Ûì–»Àn °Ëùv)Þ.§Û¥p»Œm—¢í2²]¶Ë¸v Ö.ŸÚ%P»|i—íò¡]ú³ËvvéÍ.›Ù%/»le—œìr‘]ò±Ë5v©Å.—Ø¥»La—ì2â·ÓôvŠÝNÛ)n;=m§–íô°ÚµÓ²vjÕN‹Ú)M;-i§ít Ò³Óqv*ÍN‡Ù©,; e§’ì4±Ó0v ÅNØ) ;ý`§ìðßÝí°Ûí°×Yí°ÓípÏÙìpË•ìpÇUì0Ãl7ßvÑm7Ûv‘m÷ÖvQm÷Òv m÷î x"ž„žŒ§à©xžŽgàôñL< ÏÆsð\<ÏÇ/À ñ"¼/ÁK±{1ùûÇËðr¼¯Ä«ðj¼#ü-^‹×áïðz¼oÄoÂßãÍx ÞŠÀÛ0ÁÛñ¼ï»ñÇØ= üýã=x/Þ‡Âûñ|3|ÿŒã#ø(>†cŽOà“ø>Ïà_ðY,ð9|_Àñ%|)–ø þ_Å×ðoø:¾¾‰oáÛø¾‹Ç÷°Æ÷ñüÿáÇø 6ø)þ?ÃÏñ üò»×•¿ü ¿Æoð[üÿ…ßc‡ÿÆðGüþ„?Ãvú‹8ÇâB¸0.‚‹â8”p1\—À%q<.u¬ÝcËß?.ËಸNÀ‰¸<qNÆp ®ˆ+áT”qNÇ8WÆUp*8WÅÕp®Žsµ{{ùûÇy¸&®…kã:¸.®‡®à†¸nŒ›à¦¨ãf¸9n[âV¸5nƒn‹Ûáö¸îˆ;k÷ó÷;ã.8wÅÝpwÜ-Ü÷½uÌeÇ,õÂѱƒíßÛ>ú@ûÊí³.µohûw¶fûY¶¯dû3¶Obû¶o`Ÿï}ŽNõy¶ÔçŠ}6Øû·½Ûû¨½Úû™½'ÙûF_¿cýÍõûºÑïMIÿý*ú÷dsm£‹>†˜`†VØ`g¿L–QEM´ÑÃ#L0ÃklQäôeTÑ@ ô0ÀS̱Ä[9p=D54ÐB} 1Æs,±Á%xQA M´ÑECŒ1Ã+l°C‰å2ª¨£‰6º`„ fX`…-Š\ Œ*êh¡ƒa‚9–Xc‹"娠†Zè ‡!ƘbŽ%ÖØ¡ÄÁü!*¨¡6ºècˆ1¦X`… v(]‚mQEM´ÑE#L0Ã+lP¼$¯eTQGô0À̰Ä[g[”QC-tÐÃcL1ÇklQº¯ÔÐ@ ]ô1ÄS̱Â;”"¶Eu4ÑF} 1Á ¬°Á¼^TQGmô0À̰À[/Ͷ(£ŠZè ‡F˜bŽ%ÖØ¢x^/*¨¡:ècˆ1¦˜c‰ v(]–mQA M´ÑECŒ1Ã+l°Cér¼^TQGmt1À̰À [O`®(£Š:Zè ‡F˜`Ž%ÖØ¢x"Û¢‚h¡ƒ†cŠ9–Xc‡Òå™+*¨¡6ºècˆ1¦X`… v(²-ª¨£‰6ºèc„ fX`… Š'ñzQFu4ÑAŒ0Á K¬±Eñd¶E54ÐB= 0Æs,±Æ¥+ðzQA ´ÐECŒ1Å+l°Cé¶Eu4ÑF} 1Á ¬°ÁWäõ¢Š:šh£‡F˜`†ÖØ¢x%¶EU4ÐB= 0Âs,±ÆÅSy½¨ †Zè !ƘbŽ%6Ø¡Tf[TPCmtÑÇc̰À ìP:׋*êh¢.a‚Xa‹âéÌeTQG ô0À̱Ä[Ï`[TPC-tÐÃcL1ÇkìP:“¹¢‚h£‹>†c†VØ`‡Ò•y½¨¢Ž&Úèb€&˜a¶(^…׋2ª¨£…z`„ æXb-Šg±-*¨¡:èaˆ1¦˜c‰5v(U˜+*¨¡6ºècˆ1¦X`… v(Ͷ¨¢Ž&Úè¢&˜a6(^•׋2ª¨£‰z`„ fXb-ŠWc[”QC-tÐÃcL1ÇklQ:‡×‹ jh ….úbŒ)æXaƒJWg[TPGmtÑÇ̰À ìpp.¯UÔÑD= 0Â3,°ÆÅk°-ʨ¢:èa€¦˜c‰5¶(²}ˆ jh …úbŒ)æXbƒJç±-*¨¡‰6ºècˆ1fX`… v(]“׋*êh¢.a‚Xa‹âµ˜+ʨ¢Ž:èa€&˜c‰5¶(^›mQA ´ÐACŒ1ÅK¬±Cé:ÌÔÐ@]ô1ÄS,°Â;”®Ë¶¨¢Ž&Úè¢&˜a6(^׋2ª¨£‰z`„ fXb-Š5¶E54ÐB= 0Æs,±Æ¥ëózQA ´®tË–ã ùçè¯þÙ®[å×ÃòëaòëaóB~=,¿–_˯‡å×Ãòëaùõ°üzX~=,¿öÿßõ0;&²c;®°cÛïk?z¬ýÙ¹ö7Ú³}0Û²}!Û_±}Ûo°Ïþ¾>ƒ'ú¬[è3g£÷~{¶÷X{Ÿ´÷:{¿²÷œžþ¶Gú›éw}­ß©¢þÛ–õ︡¹tÐÃ#L1ÇklíwŠÉCTPC-tÐÇcL1ÇìPâôÔÐD]ô1Ä3,°Â;”8p-£Š:šh£‹F˜`†VØ¢ÈÁîʨ¢Ž:èa€&˜c‰5¶(r|€ jh …zbŒ)æXbJ\¢‚h£‹>†cŠVØ`‡凨¢Ž&Úè¢&˜a6(r €2ª¨£‰z`„ fXb-Š—`[”QC-tÐÃcL1ÇklQº$¯ÔÐ@ ]ô1ÄS̱Â;”Žg[TPGmtÑÇ̰À ìì:Ú¥x½¨¢Ž&Úèa€&˜a5¶(Ú5-”QE-tÐÃ#L1ÇklQ<àõ¢‚h¡ƒ>†cŠ9–Ø`‡Ò¥ÙÔÐD]ô1Ä3,°Â;”.ÃëEu4ÑFŒ0Á ¬°Eñ²ÌeTQG ô0À̱Ä[/Ƕ¨ †Zè ‡!ƘbŽ%ÖØ¡tsE54ÐF} 1Æ ¬°Á¥ÙUÔÑD]ô1Â3,°ÂEÞ€PFu4ÑAŒ0Á K¬±EñmQF ´ÐAŒ1ÅK¬±Eé$^/*¨¡ºècˆ1¦˜c… v(̶¨ Ž&Úè¢!&˜a6ØÙu´+ðzQEM´ÑÃ#L0ÃklQ´kZ(£ŠZè ‡F˜bŽ%ÖØ¢xE^/*¨¡:ècˆ1¦˜c‰ v(]‰mQA M´ÑECŒ1Ã+l°CéT^/ª¨£‰6º`„ fX`…-ŠeæŠ2ª¨£…z`„ æXb-Ч±-*¨¡:èaˆ1¦˜c‰5v(Î\QA ´ÑECŒ1Å+l°Cé ¶Eu4ÑF}Œ0Á ¬°AñL^/ʨ¢Ž&:èa€&˜c‰5¶(^™mQA ´ÐACŒ1ÅK¬±Cé*¼^TPCmtÑÇcL±À ìP:‹mQEM´ÑE#L0Ã+lP¬ðzQFu4ÑAŒ0Á K¬±Eñl¶E54ÐB= 0Æs,±Æ¥«òzQA ´ÐECŒ1Å+l°Céjl‹ êh¢.úb‚Xaƒ]G;‡×‹*êh¢a‚Xc‹¢]ÓBU4ÐB= 0Âs,±ÆÅsy½¨ †Zè !ƘbŽ%6Ø¡t ¶E54ÑF} 1Æ ¬°Á¥*¯UÔÑD] 0Â3,°ÂÅó˜+ʨ¢Ž:èa€&˜c‰5¶(^“mQA ´ÐACŒ1ÅK¬±CéZÌÔÐ@]ô1ÄS,°Â;”®Í¶¨¢Ž&Úè¢&˜a6(^‡×‹2ª¨£‰z`„ fXb-Š×e[”QC-tÐÃcL1ÇklQº¯ÔÐ@ ]ô1ÄS̱Â;”88:DuüìÝ Ü$g~ô·ª««ºŽîº&$LŽ„x×3ºÉ&ijÒhWñJÚ™ub’ f¥‘=D×J³&K8b‡¹‚¹ÍmÂeÂeÂe æ &@ሠL¸L¸Ìm.ñ­y_­fF׬v3»=ÏG½ïÛ¿~{úíêoO=õôçÔ5õEõýêÕ¨S?¡~Rý´úÙõ<Ú/õ|ÕuQ}N]û¥§ç)NNwoÏZÏ]­ç©ÖsRVá·Î5­ç•<;6[Ï ­çÖs>ëùõ\ÎzÞæù“Óó1ë¹—õ<Ëo<;v[Ï•¬çEÖs ëùŽõÜÆzã·Ï­ç"ÖóÿòÙù„ß{vžàžõÿÿ×gýúÿËÙñÞÚ7¿öï}îkÿúÚ—¾ö›ÿ³cÀµï{íçþÄÙñàÚW½öK¯}ÐkóÚ·¼ö#ÿú³cĵ/xí÷]ûx×þܵïví§]ûd×þ×µ¯uíWýígÇ¿ë¬ô÷œõoþ‡gý–øì¸ò<ëg\û×þõ¯pí\û×þ¾µooíÇû%gÇk_ÜÚï¶ö±­ýikßÙÚOökÎŽE×¾®µ_ë³ãÒµojí‡ZûœÖþ¥µ/ií7ú­gǪkßÏÚÏó;Îúo~÷Y¿Ìï?ëoùCgý(äìXöÿ8ë÷ØœõgÔgý?çìøöO9ëWxð¬¿àWœõ|çÙñýz,o7áÖ¹ºuù­ç~èìo¿qÖ´þÞ¯ïö‹ê'×q ŽIdßéóËê§Ö1šŽt{éøïûÕO¯ã'½)léøìÔϬcoýø:fѱÓªŸ]Ç:ú‰u<¡ãšR‘7ò õûÖ±~Ž;~XåŽ!¾¨~r‡ç˜àGÖñuöñ¿¬~j#gŸýG×±o|¿úéuüšýé[Ç¥Ù?þõ3ëØ2û»?¾Ž³ïúƒêg×q_öEbÏΩÈ~â õûÖ±Vöû~Xåöᾨ~reŸìGÖñM^¼/«ŸRO÷£êhÿçûÕO«ÏÙŸù1õóíŸü€ú™ulý]ÇíØwø~õÓëØPlScÛþêgÖq1¶Õ?¾Žw±ÝýAõ³ë˜ÛÑŸXÇ¢Ø&þŠà¾¡~ß:NÄ6ë‡UnûóEõ“ëÛ“YÇfØ>|YýÔ:¾ÂúþG×qV ߯~zû`]ücë˜ëÖP?³ŽK°®üñu¼õÞªŸ]Ç XýÄ:ÀÊä‡TtÛ9ù_tòö÷çOB -´ÐB -´ÐB -´ÐB -´ÐB -´ÐB -´ÐB -´ÐB -´Ðþ˜o¿S[/‡_§"xêÊÓO½òÂõõªÖ[S¬S¬Ó|ò¥WŸ_‡:Ýúamë5ƒ—Ýã7¦'' ?SàŽ;&gw<»òÖÀ°ÐÖvóê—®¿ðĵ›×®Ü|úÚ+ÏÝ|ùµ¿üÆ·öÙ§ž¹úÔ3Ÿúö?wóÚ^ºþñõ•ü°ÿÆùóçyè¡së×GyøÖ×óœþ|«=øÀÃç.¸Ëç}ðá‡üÈɹû2\æKoܼöº§òÂë¯]å½ïçn/¾ø>sú§œûÊ×o•¶²•ÄÓמ?÷ì•s¿úÜY»Åd;¹Îµñ[ONÙüû÷ö¯^½|öíú묿ü®»Doß>>ÿêË¿öÚkÞd_üҵׯ½róÆ+×O¾øíçÏ?váüàc{üâ?ôä=öØ}^•Lû ÿ_»þöÿð… wû¿ð`ðÚýk×ýŽ“ÓõTòÆ—_YG~[GåëŒ.ëZ0>ýùÖlFÛOžqxkoâ­úVl÷ºýñüýo|ˆíÿ…óÿ÷¥…íÿGºÝÛöÿkÑÿþzôÁóœ¿ËÿùG}8ø¿m…Ø|_|ÇÏomÏ¢Ûî÷#ãÿºÿô¿û#¿+ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡<ä!yÈCò‡ü£žŸ„ö‘j7¯Þxéú O\»yíÊͧ¯½òÜÍ—_ûøËo|ûgŸ}ꙫO=ó©oÿøs7¯}á¥ë¿ñÊ‹¯~Øãüùó<ôйõë£<|ëëùN¾Õ|àásÜåó>øðCž;ÿÀ…¹prîü×ó}¯ö¥7n^{ÝSyáÆõ×®¿òÞ÷s·_|ŸÇ9ýSÎ}åë·JÛNÙº2xúÚóçž½rîWŸ;k·V¹z@ýÖ“ÓÆ¿oyñêÕËgß®¿ñ·¨_~×]¢·oŸõå_{í5o²/~éÚë×^¹yã•ë'_üöóç»ð‰G8ÿàc\ìOÞxýú‹¯þ†O\xòÉÇ|ôÉÇ?öÀ¥ÇüØC?rþc~ð±=~ñ‰ÇzòÇ{쇾¯ÊG¦}ÿ¯]ÿû_¸Ëÿ…‡Îÿ÷£]ýòk×ÏýòsÅ•/}á­o‹oôs íþµ{Ýþ¿píæ‡þ7>Äöÿ£ÿ÷¥…íÿGºÝÛöÿkÑÿþyø¡ówùw¿àÿ¾´ß©œì­o·W×e}rJ}m+Òâ³/]»ñÊYRþ½nÉoýðÄõ7ž¿í®oµ‡Uyë—¯?ÿêë/œ=Ê·>Òé-wýbôa)?ûº\|ýõk_~üÕ—¾ôò+ëÝ~Ù ¯~É#YƒÝ._züêSÏ>ã‡ó_¹yý•W®{íÕëzæ{ν`½òüͯ¾rîÚn}é˯¼úòk/»ñʹ›7^^_ý•›×^yáÚë/Üòn7ìΞÖúôŽë?ùÔg_}ãÆú 'wt²Dïñ‡þùïú‡®U}îKÖ‡_zùó¯Ü¸ùÆúǪŸûnÿBô¦vöÏìž¾tñÊSÏ<ùìÙÓyï×ñ­×òÁõ¯ºõ:þ²+7_÷²üénØœ…o=—Íë×^xûËŸýîÊŸöžJrÓîåúKgÿîæòõoûñÖ¢úÊÂðÃöW>ÀÑ[/àú¿ã•篽tíõÛõS¯Ü<ÝX½W/>sõÒ3Ï\|î©'nýt¶¬Ÿzáž–âöäÎö¡Þ•Ñmß¿óùÞõÖÜYÇ^ºü]?³>«ÞmÞu7¯¿þ}×^úªÞvoµ¿áÝŸð³ïú„£ûîZþSßïýò•zãäŽwÿ{¼&§ð•×$yæâÓ—|>û–Ð×Ξ˹W®Ý£ÇòäÎöGaIÞõÎÛ=óù§Ÿû쳟ùîõU½rýõ×ß8ça¯¿~OÏ÷ëþÎû ubzõâåO]ºê»?ÙÆï{®ßüú­ÏÞ)a5xWƒw-ÝäêS§„îX¥œ{ùÆ ·¶z÷¾,o{uþäoÒEvïk¢þ(,©íõ×^}þ{×ìóW?9¹‡µÝ]‹ª\ÕsÏ^~êSO­»'Ç[KìÕ×o|w/¾úú¹Ûß a¡}£Ú'_}õ¥ÛWöW/_|ü;ùþ^}ýÚó¿~ÝL½øÒµï9÷±sW_ÿs/ž³&}íí×샗ÝÝíCn,–ÿï솷^’æ©WžýúË×_¹yí¥Ûÿµw<…·Örwì1åÕY“;w°¢Û~çö]™è¶ßxks~ûm·o,o¿ýí­Òÿè[«³Ûïz—›;ý¶¥óÖí¿àô(çäç­Ký©+OŸœ¾ÝJß¾µ›áÇ«g÷úì_¹u¯[ÿÖ•;ŷŸ|éÕçým/ÁÉûßÔí=ŽÿŸ|êÒgžøJçß‹_Û‘øWßÿç¦GÃñÿ}i¡ÿï#ÝÞ×ÿ×Eÿù¿páü…wôÿ]8ÿ`ð?ÚºÍ{Ò×½ºc‡`Ý@ž$»·÷#Ö¶îö½µYK“ÓŸÿ‚“o‘M]hïÒÞÿÀ#aüÇ}k/ÝöýÍ óµæ¡}sµ{ÚþmÓ?|¸ãÿ°ÿZØþ¤Û=lÿ¿FýfþÇÿÿ÷£…ù_>Úíž¶ÿ_ÛôêøÿÑÐÿZØþ¤Û=lÿ¿Fý<ÿË#xÇõ_ÿ÷¥Íÿò—žÜÓü/?qò-8ÿË¥Ï\üîçžxj]%ýéO|ežƒW_<÷Âõ—®}ùÜó××+äÏý¢ëÿžŸ»|ñÛÎ=qéñ_ü®³ |ü$ÌóM3 Â.÷Ï~úâ•KgËýß±Ü_ûÞko\÷åñ·Ð"Þ_¾ôä¥Ë—žyü­Åüø‹ù+é{,ê`ü›ý ðÁ³=þìëÜ¿ôÊkן_—âóßkòy ûÆ7o<ÿÆú6xñÆõ—^ø¶s·–ý'-Æ×®½ñÆ9{ã ¯_»ùê½ÍÐsfº{’‰'?sñSÏ]~öW­ß_~õÏ9÷äK׾瞞ëÝíÃ?×{[g3ûöàÖ„Më_°®MÎ=ë÷߸§¿á>Lv×ß_yöó—­÷nMÄ‘_yõK¯?ýÜoÖ‰ÎÞšÂã¼çü6ç<Ås<~uî½õÂD7_ÕD7·MóÖ“x×µÈéd0·í>ß1/Ë;Xw$ïØ.ß9ŸËÙ:ûŽI[n[ÛÝ~ûW3}Ì$îx»&ù›ÎÆúüu'wÌóòŽ—`}ñïë“¿ýšÅg 2ùMê7Ÿ^ Ÿúš%ïó‹ßŒí=Žÿ¯\½xõÒ7rþ—GBÿÿýi¡ÿï#ÝÞ×ÿ}™ÿåüƒç/¼ãüß…‡ÂùÿûÒÞþ—*¹s„ëíó¿ìÎ~þ¾“påä·n»×ù_²“0ÿKh¡…ÚK힎ÿîûõŸÜ3ìÿÝ—Žÿ>ÒíŽÿ¾!׆ñ_¡ÝÇö^×®-;y»Ÿü]¯ÿüV>š¹§íÿ}¿þãÂù Áÿýiaûÿ‘n÷°ýÿ†\ÿñhøüïûÒÂõívOÛÿûýÇ#áó?îS Ûÿt»‡íÿíë?|ôŸÿûÐ…ðù¿÷¥÷fÿñɽ\ÿ‘~ñ䛿ú·ŽÊ?pðáæñ[ŸJösŸyõÆ:êÿlàï:æðæõ—_»îû/½þ¡†~Ó}àêwžÜÙñá¿øÍ2ÐøîA¤Ÿyöâ:´¯ùÌ«×^øˆ.»»Æ^ïžýä•çž>Nù žýÂ×_ÿ¾5~ùÕ®Ÿxÿ¶sÏ>ùäsW>{éñ«—?ÿô=½P÷¨ûæò¥uÊéG'®£‚¯³­¿þúõWž¿~îÕ[Õµûý9Š÷üܯ<µ~ÐàÏ½í¹¿q:û›í‰ß=þÊç?ùÜ•Ç/®Ÿø‹ü{ãùk¯œ{åK/áúëßæõÉ3ÿ¾ëçn¾z{pOÈ×oxùW3Nùl-OƒŠÏV$wÜ÷6I·ß~öÞ¼ã¦ÓE~Çoßöb¾uû_zÖ£ü¼ÿ(ã 'wõ(g·ýʼn_R›ß¬þrõW½Ï/|¸ö^ûÏ^¾zé‰ç®^üäg¾önà¯þøoÝ û÷¥…ã¿t»ÿ_k7ðùàÑ»û/„þŸûÔÎ:}Ÿ¾~í/~Jô¹+×oÞÖüÆ—¿ôÒz¡dQ\]¯ìóßµÛïîÞçnmÝÏ}ï«/½pk÷óíðs/¾þêË~áêõ—lä_}íú½>ÈÙ?{ý…s×Þ¸ùú«ë%zϯ×è½½WõFè§þ:´¯fûÿa;‚>ÄùßGý?÷§…íÿGºÝûöÿÃw¿¯ÿ n{èÑ»ý?ôH˜ÿû¾´Ó£Ôß² ¿½ÿ÷¼µ¬w—¯¿xvûoù îüùÖ*ápç–Â1êßëÖæîKDNÞ>0þy'§W‰ì.>sõÒ3Ï\¼ðß?ðŽï/_¾øÝ§—÷Þþýáñg/_¾ôøú$ž¸xõâ»Þ’ÜõµZ¿>÷Ä¥+Ÿ>ÆÝ?··~þôwöÒåÇ?ÿÉKïsãîÒ¯þì³W>ùÒ]ßoŸ¼té‰ ïøæÛ¿ÙÝšeëìqnû>Y;.nûº¿Õ‘ñøÅ«—>õìåï~·þ¸;n¸û9¾zGðmßžzúâ§žzæSÏýªKO}êÓWßõ–Ÿsç-w?ôÄ»§,ÝËßu«ãæö#æ3o-»;:<ûÉ+ëý®>õì3gËÿ·TŸ½üìã—®xg¾ûÏåÚUóÜ3Ÿú“—.¿óÇã•Ç/_¼úø§Ÿ{·Åý¾áöÊSŸzúâíßt·¾¹û~ï~ëîÖ9·w~ÿÖåëo}ݯ_Ÿ{üÒ3W/?{ëï¸aóùïúUoI¿²´¾ò]ô×'y«Oúäí¾èŸwVg¿÷žï¾·ð+ç{û}ÛéÝÆÞ[Ë;mÜö½í}òÁKþ®Eûn/ë;^½äÝßwï\˼Cû® Þ÷óîï„`ó¾œo­¸¿²ö¾cª‚“Ó¾Ùôä´Ëp=#²îà­‡Qk§urzèAÕgëèv}ŽªWÃÉéE|“šONO€¼5 ÆÏQüÉé ‘õ-ô'œœ^5øó×'£~¡úÕŸ¤ÖÉ:þ”“Ó« ñÉéù‘_¢¾M}L­30}ûÉé§²®o‰õݰNW´îÖ­}ɨGÕc'§Ãu©ú„úe'§»™ëé•_¡¾ãäô“=?©Öé/Ö—n]àkîú¾þ´zJýJµž}YßQk/íÚ‰»žZ?õsj}ã\Qëþ¼ú.µ¾µZ9üêר_«~ú3ÕsêÏR×ÔÔÚ½½v|¯[Íu'b=Á¶Î̱~°Â:¯ÒÚ•»ÎÂÿ²Z÷AÖŽ–u›¹žo]{Ý×Në2ý’Z¯·ýsÔoP+ßõs»£úóNNg½&Qüïøß}fý„õã%Ö«Áÿ»Ï­ÞÖq#Šÿÿ;þwüïøßñ¿ãÇÿŽÿÿ;þwüïøßñ¿ãÇÿŽÿÿ;þwüïøßñ¿ãÇÿŽÿÿ;þwüïøßñ¿ãÇÿŽÿÿ;þwüïøßñ¿ãÇÿŽÿÿ;þwüïøßñ¿ãÇÿŽÿÿ;þwüïøßñ¿ãÇÿŽÿÿ;þwüïøßñ¿ãÇÿŽÿÿ;þwüïøßñ¿ãÇÿŽÿÿ;þwüïøßñ¿ãÇÿŽÿÿ;þwüïøßñ¿ãÇÿŽÿÿ;þwüïøßñ¿ãÇÿŽÿÿ;þwüïøßñ¿ãÇÿŽÿÿ;þwüïøßñ¿ãÇÿŽÿÿ;þwüïøßñ¿ãÇÿŽÿÿ;þwüïøßñ¿ãÇÿŽÿÿ;þwüïøßñ¿ãÇÿŽÿÿ;þwüïøßñ¿ãÇÿŽÿÿ;þwüïøßñ¿ãÇÿŽÿÿ;þwüïøßñ¿ãÇÿŽÿÿ;þwüïøßñ¿ãÇÿŽÿÿ;þwožíÉùÏùÏùÏùÏùÏùÏùÏùÏÝ)ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?ç?|ý ÅÎÎþéõÚmÅÎÎÎÎþìúñ6ŠÿüòúÑ2ë´Šÿœÿœÿœÿœÿœÿœÿœÿœÿœÿœÿœÿœÿœÿüùõ£Îÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿ9ÿù›§Ãú þ þ þ þ þ þ þ þ þ×é¾ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ þ‹Ï¬C>ÿųëÄ'êsë5êŠÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿ‚ÿâÍÓ!½%ÿ%ÿ%ÿ%ÿ%ÿ%ÿ%ÿ%ÿ%ÿ%ÿ¥;—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü—ü——×aäëŠÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿ’ÿõóÌKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþKþË7O‡óWüWüW›Órªø¯ø¯ø¯ø¯ø¯ø¯ø¯üBÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅõøzù‰â¿â¿â¿â¿â¿â¿â¿â¿â¿â¿zv½”dóIñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_ñ_½yz)Ïžÿ=ÿ{þ÷üïùßó¿çÏÿžÿ=ÿ{þ÷üïýÒžÿ=ÿ{þ÷üïùßó¿çÏÿžÿ=ÿ{þ÷üïùßó¿çÏÿžÿ=ÿ{þ÷üïùßó¿çÏÿžÿ=ÿ{þ÷üïùßó¿çÏÿžÿ=ÿ{þ÷üïùßó¿çÏÿžÿ=ÿ{þ÷üïùßó¿çÏÿžÿ=ÿûïX§]Süïùßó¿¿´^Φøßó¿çÏÿžÿ=ÿ{þ÷üïùßó¿ÿÜúa¶ë¤MëubŠÿ=ÿ{þ÷üïùßó¿çÏÿžÿ=ÿ{þ÷üïùßó¿çÏÿžÿ=ÿ{þ÷üïùßó¿çÏÿžÿ=ÿ{þ÷üïùßó¿çÏÿžÿ=ÿ{þ÷üïùßó¿çÏÿžÿ=ÿ{þ÷üïùßó¿çÏÿžÿ=ÿ{þ÷üïùßó¿çÏÿžÿ=ÿ{þ÷üïùßó¿çÏÿžÿ=ÿ{þ÷üïùßó¿çÏÿžÿ=ÿ{þ÷üïùßó¿çÏÿžÿ=ÿ{þ÷üïùßó¿çÏÿžÿ=ÿ{þ÷üïùßó¿çÏÿžÿ=ÿ{þ÷üïùßó¿çÏÿžÿ=ÿ{þ÷üïùßó¿çÏÿžÿ=ÿ{þ÷üïùßó¿çÏÿžÿ=ÿ{þ÷üïùßó¿çÏÿžÿ=ÿ{þ÷üïùßó¿çÏÿžÿ=ÿ{þ÷üïùßó¿çÏÿžÿ=ÿ{þ÷üïùßó¿çÏÿžÿ=ÿ{þ÷üïùßó¿çÏÿžÿ=ÿ{þ÷üïùßó¿çÏÿžÿ=ÿû7O/ã;ðàÿÀÿÿÿþüø?ðàÿÀÿÿÿ¿xàÿÀÿÿÿþüø?ðàÿÀÿÿÿþüø?ðàÿÀÿÿÿþüø?ðàÿÀÿÿÿþüø?ðàÿÀÿÿÿþüø?ðàÿÀÿÿÿþüø?ðàÿÀÿÿÃ'׋7ÿþüø?|z½„SñàÿÀÿÿÃ3ë…¥Šÿÿ‡ËëEîŠÿÿþüø?ðàÿÀÿÿÿþüø?ðàÿÀÿÿÿþüø?ðàÿÀÿÿÿþüø?ðàÿÀÿÿÿþüø?ðàÿÀÿÿÿþüø?ðàÿÀÿÿÿþüø?ðàÿÀÿÿÿþüø?ðàÿÀÿÿÿþüø?ðàÿÀÿÿÿþüø?ðàÿÀÿÿÿþüø?ðàÿÀÿÿÿþüø?ðàÿÀÿÿÿþüø?ðàÿÀÿÿÿþüø?ðàÿÀÿÿÿþüø?ðàÿÀÿÿÿþüø?ðàÿÀÿÿÿþüø?ðàÿÀÿÿÿþüø?ðàÿÀÿÿÿþüø?ðàÿÀÿÿÿþüø?ðàÿÀÿÿÿþüø?ðàÿÀÿÿÿþož^Â[ó_ó_ó_ó_ó_ó_ó_ó_ó_ó_ó_ó_ó_ó_ûåšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿšÿúÍÓË÷þþþþþþþþþþþþþþþÐðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðßðß|ç:­´â¿á¿á¿á¿á¿¹¼Ng£øoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoøoÞ<º£å¿å¿å¿å¿å¿å¿å¿å¿å¿å¿å¿å¿å¿å¿å¿å¿õ -ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿ-ÿíãëä,Šÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿ–ÿöÍÓi{:þ;þ;þ;þ;þ;þ;þ;þ;þ;þ;þ;þ;þ;þ;þ;þ;þ;Ôñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñßñß]^§iRüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwüwožNÙÕóßóßóßóßóßóßóßóßóßóßóßóßóßóßóßóßóßóß{°žÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿžÿþÍÓéúþþþþþþþþþþþþþþþþþþþ8ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?ð?¼y:UçÈÿÈÿÈÿÈÿÈÿÈÿÈÿÈÿÈÿÈÿÈÿÈÿÈÿÈÿÈÿÈÿÈÿÈÿÈÿÈÿèAGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþÇ/¬SÜ+þGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþGþÇ7O§éøŸøŸøŸøŸøŸøŸøŸøŸøŸøŸøŸøŸøŸøŸøŸøŸøŸøŸøŸøŸøŸ<ðÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿÄÿôæéÝ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ3ÿ³ŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸùŸß<žáááááááááááááááááááááááñ,ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ü/ož~4Ç‘ÿ#ÿGþüù?òäÿÈÿ‘ÿ#ÿGþüù?òäÿÈÿ‘ÿ#ÿGþüù?òäÿÈÿÑ?räÿÈÿ‘ÿ#ÿGþüù?òäÿÈÿ‘ÿ#ÿGþüù?òäÿÈÿ‘ÿ#ÿGþüù?òäÿÈÿ‘ÿ#ÿGþüù?òäÿÈÿ‘ÿ#ÿGþüù?òäÿÈÿ‘ÿ#ÿGþüù?òäÿÈÿ‘ÿ#ÿGþüù?ò¼¼~†‹âÿÈÿ‘ÿ#ÿGþüù?òäÿÈÿ‘ÿ#ÿGþüù?òäÿÈÿ‘ÿ#ÿGþüù?òäÿÈÿ‘ÿ#ÿGþüù?òäÿÈÿ‘ÿ#ÿGþüù?òäÿÈÿ‘ÿ#ÿGþüù?òäÿÈÿ‘ÿ#ÿGþüù?òäÿÈÿ‘ÿ#ÿGþüù?òäÿÈÿ‘ÿ#ÿGþüù?òäÿÈÿ‘ÿ#ÿGþüù?òäÿÈÿ‘ÿ#ÿGþüù?òäÿÈÿ‘ÿ#ÿGþüù?òäÿÈÿ‘ÿ#ÿGþüù?òäÿÈÿ‘ÿ#ÿGþüù?òäÿÈÿ‘ÿ#ÿGþüù?òäÿÈÿ‘ÿ#ÿGþüù?òäÿÈÿ‘ÿ#ÿGþüù?òüÿÛ»—ß:®<1À”-O»Ýívf™ ÏûT0‹AÒn‰zØ»'õèÖÀ–<’ÜÉF¡%ÊæŒLª)ª§{€$ÄÈ?d—U€üYÌ2È2 ÒYÙÌ.ËÈ6Q¾Ë‡EQ¤H^µIÙ¬pQ÷Ö­{ëÔã«sªêœSüÏò?Ëÿ,ÿ³üÏò?Ëÿ,ÿ³üÏò?Ëÿ,ÿ³üÏò?Ëÿ,ÿ³üÏò?Ëÿ,ÿ³üÏò?Ëÿ,ÿ³üÏò?Ëÿ,ÿ³üÏò?Ëÿ,ÿ³üÏò?ËÿìãõÇòþÿÿÀà?ðøüþÿÿÀà?ðøüþÿÿÀà?ðøüþƒþÿÿÀà?ðøüþÿÿÀà?ðøüþÿÿÀà?ðøüþÿÿÀà?ðøüþÿÿÀà?ðøüþÿáòäéI^üþÿÿÀà?ðøüþÿÿÀà?ðøüþÿÿÀà?ðøüþÿÿÀà?ðøüþÿÿÀà?ðøüþÿÿÀà?ðøüþÿÿÀà?ðøüþÿÿÀà?ðøüþÿÿÀà?ðøüþÿÿÀà?ðøüþÿÿÀà?ðøüþÿÿÀà?ðøüþÿÿÀà?ðøüþÿÿÀà?ðøüþÿÿÀà?ðøüþÿÿÀà?ðøüþÿÿÀà?ðøüþÿÿÀà?ðøüþÿÿÀà?ðøüþÿÿÀà?ðøüþÿÿÀà?ðøüþÿÿÀà?ðøüþÿÿÀà?ðø×ÉùüGþ#ÿ‘ÿÈä?òùüGþ#ÿ‘ÿÈä?òùüGþ#ÿ‘ÿÈä?òùüG3‹üGþ#ÿ‘ÿÈä?òùüGþ#ÿ‘ÿÈä?òùüGþ#ÿ‘ÿÈä?òùüGþ#ÿ‘ÿÈä?òùüGþ#ÿ‘ÿÈä?òùüGþ#ÿ‘ÿø“ɃּøüGþ#ÿ‘ÿÈä?òùüGþ#ÿ‘ÿÈä?òùüGþ#ÿ‘ÿÈä?òùüGþ#ÿ‘ÿÈä?òùüGþ#ÿ‘ÿÈä?òùüGþ#ÿ‘ÿÈä?òùüGþ#ÿ‘ÿÈä?òùüGþ#ÿ‘ÿÈä?òùüGþ#ÿ‘ÿÈä?òùüGþ#ÿ‘ÿÈä?òùüGþ#ÿ‘ÿÈä?òùüGþ#ÿ‘ÿÈä?òùüGþ#ÿ‘ÿÈä?òùüGþ#ÿ‘ÿÈä?òùüGþ#ÿ‘ÿÈä?òùüGþ#ÿ‘ÿÈä?òùüGþ#ÿ‘ÿÈä?òùüGþ#ÿ‘ÿÈä?òùüGþ#ÿ‘ÿÈä?òùüGþ#ÿ‘ÿÈä?òùüGþ#ÿ‘ÿÈä?òùüGþ#ÿ‘ÿÈä?òùüGþ#ÿ‘ÿøxýq|‰ÿÄâ?ñŸøOü'þÿ‰ÿÄâ?ñŸøOü'þÿ‰ÿÄâ?ñŸøOü'þÿ‰ÿÄâ?™aâ?ñŸøOü'þÿ‰ÿÄâ?ñŸøOü'þÿ‰ÿÄâ?ñŸøOü'þÿ‰ÿÄâ?ñŸøOü'þÿ‰ÿÄâ?ñŸøOü'þÿ‰ÿÄâ?ñŸøOü'þÿ‰ÿÄâ?ñŸøOü'þÿ‰ÿÄâ?ñŸøOü'þÿ‰ÿÄâ?ñŸøOü'þÿ‰ÿÄâ?ñŸøOü'þÿ‰ÿÄâ?ñŸøOü'þÿ‰ÿÄâ?ñŸøOü'þÿ‰ÿÄâ?ñŸøOü'þÿ‰ÿÄâ?ñŸøOü'þÿ‰ÿÄâ?ñŸøOü'þÿ‰ÿÄâ?ñŸøOü'þÿ‰ÿÄâ?ñŸøOü'þÿ‰ÿÄâ?ñŸøOü'þÿ‰ÿÄâ?ñŸøOü'þÿ‰ÿÄâ?ñŸøOü'þÿ‰ÿÄâ?ñŸøOü'þÿ‰ÿÄâ?ñŸøOü'þÿ‰ÿÄâ?ñŸøOü'þÿ‰ÿÄâ?ñŸøOü'þÿ‰ÿÄâ?ñŸøOü'þÿ‰ÿÄâ?ñŸøOü'þÿ‰ÿÄâ?ñŸøOü'þÿ‰ÿÄâ?ñŸøOü'þÿ‰ÿÄâ?ñŸ¯?Š3óŸùÏügþ3ÿ™ÿÌæ?óŸùÏügþ3ÿ™ÿÌæ?óŸùÏügþ3ÿ™ÿÌæ?óŸùÏügþ³™fþ3ÿ™ÿÌæ?óŸùÏügþ3ÿ™ÿÌæ?óŸùÏügþ3ÿ™ÿÌæ?óŸùÏügþ3ÿ™ÿÌæ?óŸùÏügþ3ÿ™ÿÌæ?óŸùÏügþ3ÿ™ÿÌæ?óŸùÏügþ3ÿ™ÿÌæ?óŸùÏügþ3ÿ™ÿÌæ?óŸùÏügþ3ÿ™ÿÌæ?óŸùÏügþ3ÿ™ÿÌæ?óŸùÏügþ3ÿ™ÿÌæ?óŸùÏügþ3ÿ™ÿÌæ?óŸùÏügþ3ÿ™ÿÌæ?óŸùÏügþ3ÿ™ÿÌæ?óŸùÏügþ3ÿ™ÿÌæ?óŸùÏügþ3ÿ™ÿÌæ?óŸùÏügþ3ÿ™ÿÌæ?óŸùÏügþ3ÿ™ÿÌæ?óŸùÏügþ3ÿ™ÿÌæ?óŸùÏügþ3ÿ™ÿÌæ?óŸùÏügþ3ÿ™ÿÌæ?óŸùÏügþ3ÿ™ÿÌæ?óŸùÏügþ3ÿ™ÿÌæ?óŸùÏügþ3ÿ™ÿÌæ?óŸùÏügþ3ÿ™ÿÌæ?óŸùÏügþ3ÿ™ÿÌæ?óŸùÏügþ3ÿ™ÿÌæ?óŸùÏügþ3ÿ™ÿÌæ?óŸùÏügþóã§9ó_ø/üþ ÿ…ÿÂá¿ð_ø/üþ ÿ…ÿÂá¿ð_ø/üþ ÿ…ÿÂá¿ð_ø/üþ ÿÅŒ ÿ…ÿÂá¿ð_ø/üþ ÿ…ÿÂá¿ð_ø/üþ ÿ…ÿÂá¿ð_ø/üþ ÿ…ÿÂá¿ð_ø/üþ ÿ…ÿÂá¿ð_ø/üþ ÿ…ÿÂá¿ð_ø/üþ ÿ…ÿÂá¿ð_ø/üþ ÿ…ÿÂá¿ð_ø/üþ ÿ…ÿÂá¿ð_ø/üþ ÿ…ÿÂá¿ð_ø/üþ ÿ…ÿÂá¿ð_ø/üþ ÿ…ÿÂá¿ð_ø/üþ ÿ…ÿÂá¿ð_ø/üþ ÿ…ÿÂá¿ð_ø/üþ ÿ…ÿÂá¿ð_ø/üþ ÿ…ÿÂá¿ð_ø/üþ ÿ…ÿÂá¿ð_ø/üþ ÿ…ÿÂá¿ð_ø/üþ ÿ…ÿÂá¿ð_ø/üþ ÿ…ÿÂá¿ð_ø/üþ ÿ…ÿÂá¿ð_ø/üþ ÿ…ÿÂá¿ð_ø/üþ ÿ…ÿÂá¿ð_ø/üþ ÿ…ÿÂá¿ð_ø/üþ ÿ…ÿÂá¿ð_ø/üþ ÿ…ÿÂá¿ð_ø/üþ ÿ…ÿÂá¿ð_ø/üþ ÿ…ÿÂá¿ð_ø/üþËãõGpWþ+ÿ•ÿÊå¿ò_ù¯üWþ+ÿ•ÿÊå¿ò_ù¯üWþ+ÿ•ÿÊå¿ò_ù¯üWþ+ÿ•ÿÊå¿ò_ͼò_ù¯üWþ+ÿ•ÿÊå¿ò_ù¯üWþ+ÿ•ÿÊå¿ò_ù¯üWþ+ÿ•ÿÊå¿ò_ù¯üWþ+ÿ•ÿÊå¿ò_ù¯üWþ+ÿ•ÿÊå¿ò_ù¯üWþ+ÿ•ÿÊå¿ò_ù¯üWþ+ÿ•ÿÊå¿ò_ù¯üWþ+ÿ•ÿÊå¿ò_ù¯üWþ+ÿ•ÿÊå¿ò_ù¯üWþ+ÿ•ÿÊå¿ò_ù¯üWþ+ÿ•ÿÊå¿ò_ù¯üWþ+ÿ•ÿÊå¿ò_ù¯üWþ+ÿ•ÿÊå¿ò_ù¯üWþ+ÿ•ÿÊå¿ò_ù¯üWþ+ÿ•ÿÊå¿ò_ù¯üWþ+ÿ•ÿÊå¿ò_ù¯üWþ+ÿ•ÿÊå¿ò_ù¯üWþ+ÿ•ÿÊå¿ò_ù¯üWþ+ÿ•ÿÊå¿ò_ù¯üWþ+ÿ•ÿÊå¿ò_ù¯üWþ+ÿ•ÿÊå¿ò_ù¯üWþ+ÿ•ÿÊå¿ò_ù¯üWþ+ÿ•ÿÊå¿ò_ù¯üWþ+ÿ•ÿÊå¿ò_ù¯üWþ+ÿ•ÿÊå¿ò_ù¯üWþ+ÿ•ÿÊå¿ò_ù¯üWþ+ÿ•ÿÊå¿ò_ù¯üWþ+ÿ•ÿÊå¿™Æã¿ñßøoü7þÿÿÆã¿ñßøoü7þÿÿÆã¿ñßøoü7þÿÿÆã¿ñßøoü7þÿMÿÿÆã¿ñßøoü7þÿÿÆã¿ñßøoü7þÿÿÆã¿ñßøoü7þÿÿÆã¿ñßøoü7þÿÿÆã¿ñßøoü7þÿÿÆã¿ñßøoü7þÿÿÆã¿ñßøoü7þÿÿÆã¿ñßøoü7þÿÿÆã¿ñßøoü7þÿÿÆã¿ñßøoü7þÿÿÆã¿ñßøoü7þÿÿÆã¿ñßøoü7þÿÿÆã¿ñßøoü7þÿÿÆã¿ñßøoü7þÿÿÆã¿ñßøoü7þÿÿÆã¿ñßøoü7þÿÿÆã¿ñßøoü7þÿÿÆã¿ñßøoü7þÿÿÆã¿ñßøoü7þÿÿÆã¿ñßøoü7þÿÿÆã¿ñßøoü7þÿÿÆã¿ñßøoü7þÿÿÆã¿ñßøoü7þÿÿÆã¿ñßøoü7þÿÿÆã¿ñßøoü7þÿÿÆã¿ñßøoü7þÿÿÆã¿ñßøoü7þÿÿÆã¿ñßøoü£7Óùïüwþ;ÿÿÎç¿óßùïüwþ;ÿÿÎç¿óßùïüwþ;ÿÿÎç¿óßùïüwþ;ÿÿÎç¿óß%¢óßùïüwþ;ÿÿÎç¿óßùïüwþ;ÿÿÎç¿óßùïüwþ;ÿÿÎç¿óßùïüwþ;ÿÿÎç¿óßùïüwþ;ÿÿÎç¿óßùïüwþ;ÿÿÎç¿óßùïüwþ;ÿÿÎç¿óßùïüwþ;ÿÿÎç¿óßùïüwþ;ÿÿÎç¿óßùïüwþ;ÿÿÎç¿óßùïüwþ;ÿÿÎç¿óßùïüwþ;ÿÿÎç¿óßùïüwþ;ÿÿÎç¿óßùïüwþ;ÿÿÎç¿óßùïüwþ;ÿÿÎç¿óßùïüwþ;ÿÿÎç¿óßùïüwþ;ÿÿÎç¿óßùïüwþ;ÿÿÎç¿óßùïüwþ;ÿÿÎç¿óßùïüwþ;ÿÿÎç¿óßùïüwþ;ÿÿÎç¿óßùïüwþ;ÿÿÎç¿óßùïüwþ;ÿÿÎç¿óßùïüwþ;ÿÿÎç¿óßùïüwþ;ÿÿÎç¿óßùïüwþ;ÿÿÎç¿óßùïüwþ;ÿÿÎç¿óßùïüwþ;ÿìfþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþ øøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøøÿŠbõÖâý…»—æWço®~8¿t{õóoþðݸ~ñòÍ›×oüàíÛ«óŸÜ_xûÞ©©çqêÔ©óg϶ÉðóçÖ†§æÖ?¯Å™¹sí´IæN½sæÜÙ3íÔœOgÖJ€_}ÉîÝ{Îÿ¬/Jûrøu‰×~ï[3Îßi×o¶Ô6b2níBøÍ8|¯}þõþþò­[76ÞN~!£˜ùѶIN<ÿ»w–?{þÁûØÏͯÌ/­..-Ìüü§N½{ú½ós§Î¼{~áÝ÷®,®,Ü[þå{§¯\¹xæ+¿?wùÝ3ß?{îü©ï_8wæÝï_¼péâÙ+sï¾ûîÜÙßÀZ96±§ÿÖ¿§ÿ3§NÝæß‡Ñÿ¡Ä_ 9âZwá7Wç—îίÜ]Û^|{òä:ÕW6¦~,Nl¼}ã³òè̉gþwŒ¯G<ÞˆÉ~ ¤±¶UoÞüðêÒÝ…_nnÕæ¥ä8£Ä¶Ö­äÍÅÏ­¯¯Ü]XY¸ûáüƒ­[òýïnLÿþýå;¾ùåŽ#ÇcŒ1Æ8ªØ÷ùßä°=å<¦8ÿ;wêüXþ;”ÏÿŽuìóüïôïíÿÜ©güϽszô?Æ¡Å&ç?†ÿÀëäÃ_-Ý™yrRûÚÆûÉkÒªöµ÷74œØòúºÆ¾óÿÅ¥{ËSÎcšüÿôèÿpbÌÿuì3ÿý{ûŸ|x&ÿ?5ú?Œ¸õ« íGí›>Ù|ûÆQ§iŒÃ‹}çÿwçW§Çùÿ™¹ñüÿpbÌÿuì3ÿý{úŸ;ÇË6ÿ§Çë‡ë罯ýÐÛ×nM6õÌ“»½¤o|t~qiã›Éíà™o¯}¸´ððΖI7ãœ×wÖ&¸±pgyåîÆ¿L:£yc}̶ž˜öG›gå³7ïÌߟ_¹¸|ÿÑçK“é~øþòòýµCØú¯_ùàÂo߸þ³ÉûËÑîÝŸÿÔû7ŸºÝ½Óˆí1U:Ol¾y6¡W—VÛ–„~ëÃë—.ß¾zÉÛßþheùÎÂÇË+íóå» mñî¾üÚo,Á»¦ùæêÊâÒ§_¦ùä­?ýè²á÷ž$xU1r_©ýÎWŸÚíkx’ÚgÖð$Á/ï~ýæÇïßÞXË¿ó$Ñ}rkúñãÇÿoÛ¸ÙL~¸Þ‰-ã·ìç[GoîJOMúdƒm½ulŽÿÅÆ¼I%†WoÞüpcÒgR÷‡3Û®àmnÒɱí ?2|å‹çMøŠÝòÿë\¸qõ_¸uõúµ®:EùÿìéwÆüÿPb,ÿëØÿ­º‡ÿ3gO?SþŸ;5^ÿ;”x~ýÏ/^Y‡¿SýÏ+'Ö?ÿ³™¯÷°ãû­ÿ9)õ?w‹ÉÚûË™g×ÞäÕf¶öÖn1ÏlYQ›ìøÅ7aMþ­£NÀG;ê¼±ýBÒ‡9ÿ›¶ØÁÏÿæNˇãùß±ŽýŸÿM_t/ÿ;Ôÿ[h–ïϯ,þåüêâòÒzåÿ{Ë+maþÎgͼVî¯ó`eùî£;«¿Í?lóíæêòŸ/{æ -6ï|ûêGË'ÿ<óôÕ“kU²×âwmñ?ºqýÒÇoùüÞÕ¥»‹w,É]¯,~2ij°²pgáÁêòÊö|¯Ý[X¸Û>]žŒ_\Z]ÞºJ´Ü¯|µËýÍiöóúµ?¼=ÙJÞ—k>ÿdae²vØh¼xòÒØã)f_næWvØŸúr·6"[WÊæø¥{t6óü0³íÝÉ'˰v)óäs&:n±Kþó£ËoݸðÁíŸ]½véúÏ^¬ È4×ÿN÷ÿ'Æòÿ±Ž}ú¡& Ï÷¯¬ÿlûsïŒýJìÑþã;»·ÿø¯í?¾þ±ßöߛی1Æc¼|±Wû³ƒ½_û³1ÆcŒ£Š£îX渷¿uúóÏÿÕÿø¯Ž:GuÔ 8âø_G€#Ž2âŽ<~¼²üèA;=ó­£NÈG¼?¿4¹5{ÔeãG}|óˆç¿v¥à¿ý‹c[:®qÀûÿSu0Åýÿ³gÆû‡ãýÿcºÿ?e {ù¶ÿ¹¹Ócýß1/ž×ÿÇäæÉqëÿc÷úSt0Eþúüøü·Ã‰1ÿ?ÖqÀúSu²—ÿwæž©ÿ÷ÎÙ±ühñŸ¶\üX«ä?óäžÈdøÿçúðÿþŸõá܉_¯ ?}s}øofׇÿùÖ‡¿5·>|ïÖ‡þx}øï~º>üßZþõ…Íù­%áÏ~rá0†ßäùíg{oæÿSu0ÕùÿXþ?œóÿcÊÿ§ìd/ÿÏöÿá`ìÿçPbìÿãxÇóÿ©:˜"ÿŸ;7–ÿ'ÆüÿXÇòÿ)»y¾ÿ‰üócÿGë×½ûßÎì§ÿ·~æ¥éÿã{3ûì_áÍ/_¸yûÊËrûÆåISÇxeeáç–îüª}¸0ÿðÑÊBslYX1fÓü?ô™N<î.?òo[ú0ùÉ…kk‹àÃù‹ K« +íÞÆR,.<ÜÒÉgóKK ÷ÛâR[ýl¡;ß>Ÿ_]Yüå¾–ìõ-‰z^ç'ÞÛy‘ÿ鎋¼Ö ÄŸ½õ¤Äó餞ù¾R|ø:ëI¢o_»ðá¤'©¿½-åmiþó—õéòo^½rûâõk?½íhu"­L¤]½Òî,/ýbaåá¤'/¨Í–ÖzùzI·ÂÉÍUóCÄÊü$ŸZºk_‰Wýw¯]¾uûæÕK—ß¿píÒÚç…ÕöpñîÂ'ëµê_Šõ¼c¿o ÞÇ'ý¾=Ü\ëÇßÃîôm×äoËTÞºuýÖ…nOVøfÁæïNö÷ÕåU‰:?YýlñáÆ^´¯¥ÙVT}‰sI¤›¼]‹’ë}èm9Íûòï×~¶ýL`ëÏž.]>õ»íå§§¾|ºXðÔW»uË÷t¶²õ›ŽÝ[¿~æØ¸õË“;ü`»è­ßm•óTžÝ%7¿þëú}ÿ~æù}N¾xª~ß÷6þà¯|ôzÅëU¯“^¯M^_ÌÌüÖÉ™™o¾nømÃ7 ßøâyöÅ.×ÿ.]¸uáö¥Ë7/Þ¸úѶg€|S\ÿ?u~ìÿïpb¼þ¬c¿þ_¤À=üO:û|¦ÿÿ3ãó?%žßÿßÉ»÷ÿ÷/gÆþÿ¾þ±ßþÿ&»ÃW×ÿß^ýWdîcÿUcŒ±ß8hùš&€S”ÿÏëÿNŒåÿc+ÿO×x/ÿ;µÿ=?Öÿãðâyí'…ÏãÖþw×üš&Såÿ£ÿÉ1ÿ?Öq°üº@{ùß©ýÏù±þï¡ÄØþçxÇAóÿišL‘ÿŸ™Ïÿ'ÆüÿXÇÁòÿéíáÿô;gŸíÿglÿ{8±~ÞûêßÌì§ýÏ+ÿeæ¥iÿ³Y)ë@uvß¼rþÓõêh+û¬‹¶=¾âŠ»om}òöí«“ªOù£åŵ?«ËÛž]¼±½^ŠêÛ–ãw¶µ \_”ºeQ6+:nÔ.=Š…9HͽݪÄí°Á¶~½ózØœâÞÆe§Ofž_-­Ïl»ìôêÆLžZûÅó&cŒ1ÆØ-þ?>lö¢xcasacore-3.7.1/tables/DataMan/test/tTiledDataStMan.out000066400000000000000000000007271476623553700226030ustar00rootroot00000000000000>>> TSMCube cache statistics: cubeShape: [12, 20, 30, 42] tileShape: [4, 5, 6, 7] maxCacheSz:0 cacheSize: 60 (*6720) #accesses: 60480 #reads: 360 #inits: 360 #writes: 360 hit-rate: 98.8095% <<< >>> TSMCube cache statistics: cubeShape: [12, 20, 30, 42] tileShape: [4, 5, 6, 7] maxCacheSz:0 cacheSize: 60 (*6720) #accesses: 30240 #reads: 360 #inits: 0 #writes: 0 hit-rate: 98.8095% <<< Test with version 1 table tTiledDataStMan_tmp.ms contains 8820 rows casacore-3.7.1/tables/DataMan/test/tTiledDataStMan.run000066400000000000000000000002241476623553700225700ustar00rootroot00000000000000#!/bin/sh # Script to run tTiledDataStMan with an older dataset in the tgz file tar zxf tTiledDataStMan.in_tgz $casa_checktool ./tTiledDataStMan casacore-3.7.1/tables/DataMan/test/tTiledEmpty.cc000066400000000000000000000071711476623553700216430ustar00rootroot00000000000000//# tTiledEmpty.cc: Test creation of a tiled array without write or flush //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include // This program tests if the cration of a tiled array succeeds if the // array is written nor flushed. void writeTable(const TSMOption&, bool writeFlush); void readTable(const TSMOption&, bool written); int main() { try { for (uInt i=0; i<2; ++i) { writeTable(TSMOption::Cache, i==0); readTable(TSMOption::Cache, i==0); readTable(TSMOption::Buffer, i==0); readTable(TSMOption::MMap, i==0); writeTable(TSMOption::Buffer, i==0); readTable(TSMOption::Cache, i==0); readTable(TSMOption::Buffer, i==0); readTable(TSMOption::MMap, i==0); writeTable(TSMOption::MMap, i==0); readTable(TSMOption::Cache, i==0); readTable(TSMOption::Buffer, i==0); readTable(TSMOption::MMap, i==0); } } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } // First build a description. void writeTable (const TSMOption& tsmOpt, bool write) { IPosition tileShape(2,64,64); IPosition cubeShape(2,256,256); // Build the table description. TableDesc td ("", "1", TableDesc::Scratch); td.addColumn (ArrayColumnDesc ("Data", 2)); // Now create a new table from the description. SetupNewTable newtab("tTiledEmpty_tmp.data", td, Table::New); // Create a storage manager for it. TiledCellStMan sm1 ("TSMExample", tileShape); Table table(newtab, 0, False, Table::LittleEndian, tsmOpt); table.addRow(); ArrayColumn col(table, "Data"); col.setShape (0, cubeShape); if (write) { Array arr(cubeShape); arr = 0.; col.put (0, arr); } } void readTable(const TSMOption& tsmOpt, bool written) { Table table("tTiledEmpty_tmp.data", Table::Old, tsmOpt); ArrayColumn col(table, "Data"); Array arr; col.getColumn (arr); AlwaysAssertExit (arr.shape() == IPosition(3,256,256,1)); if (written) { AlwaysAssertExit (allEQ(arr, float(0.))); } } casacore-3.7.1/tables/DataMan/test/tTiledFileAccess.cc000066400000000000000000000243251476623553700225460ustar00rootroot00000000000000//# tTiledFileAccess.h: Test program for class TiledFileAccess //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main() { // Test for a Float array written in big endian canonical format starting // at offset 0. { IPosition shape(2,16,32); Array arr(shape); indgen(arr); { Bool deleteIt; const Float* dataPtr = arr.getStorage (deleteIt); std::shared_ptr fios (new RegularFileIO(RegularFile("tTiledFileAccess_tmp.dat"), ByteIO::New)); CanonicalIO ios (fios); ios.write (shape.product(), dataPtr); arr.freeStorage (dataPtr, deleteIt); } try { TiledFileAccess tfa ("tTiledFileAccess_tmp.dat", 0, shape, IPosition(2,16,1), TpFloat, TSMOption::Cache, False, True); AlwaysAssertExit (tfa.shape() == shape); AlwaysAssertExit (tfa.tileShape() == IPosition(2,16,1)); AlwaysAssertExit (! tfa.isWritable()); AlwaysAssertExit (tfa.maximumCacheSize() == 0); cout << tfa.cacheSize() << endl; tfa.setMaximumCacheSize (100000); AlwaysAssertExit (tfa.maximumCacheSize() == 100000); AlwaysAssertExit (allEQ (arr, tfa.getFloat (Slicer(IPosition(2,0,0), shape)))); tfa.showCacheStatistics (cout); cout << tfa.cacheSize() << endl; } catch (std::exception& x) { cout << "Exception: " << x.what() << endl; return 1; } } // Test for a Float array written in local format starting // at offset 1. The tile size is half the length of the first axis. // This assumes local format is the same as local canonical format // which is true for all data types except long. { IPosition shape(2,32,16); Array arr(shape); indgen(arr); uInt off2; { Bool deleteIt; const Float* dataPtr = arr.getStorage (deleteIt); std::shared_ptr fios (new RegularFileIO(RegularFile("tTiledFileAccess_tmp.dat"), ByteIO::New)); RawIO ios (fios); uChar nr = 0; off2 = ios.write (1, &nr); ios.write (shape.product(), dataPtr); arr.freeStorage (dataPtr, deleteIt); } try { // The array starts at offset off2. TiledFileAccess tfa ("tTiledFileAccess_tmp.dat", off2, shape, IPosition(2,16,1), TpFloat, TSMOption(TSMOption::MMap, 0, 1)); AlwaysAssertExit (tfa.shape() == shape); AlwaysAssertExit (tfa.tileShape() == IPosition(2,16,1)); AlwaysAssertExit (! tfa.isWritable()); AlwaysAssertExit (tfa.maximumCacheSize() == 1024*1024); cout << tfa.cacheSize() << endl; tfa.setMaximumCacheSize (100000); AlwaysAssertExit (tfa.maximumCacheSize() == 100000); AlwaysAssertExit (allEQ (arr, tfa.getFloat (Slicer(IPosition(2,0,0), shape)))); tfa.showCacheStatistics (cout); cout << tfa.cacheSize() << endl; } catch (std::exception& x) { cout << "Exception: " << x.what() << endl; return 1; } } // Test for a DComplex array written in canonical and in local format. // Open it writable and update the values. // Check it after writing by iterating through the data. { IPosition shape(2,17,40); Array arr(shape); indgen(arr); uInt off2; { Bool deleteIt; const DComplex* dataPtr = arr.getStorage (deleteIt); std::shared_ptr fios (new RegularFileIO(RegularFile("tTiledFileAccess_tmp.dat"), ByteIO::New)); CanonicalIO ios (fios); off2 = ios.write (shape.product(), dataPtr); RawIO cios (fios); cios.write (shape.product(), dataPtr); arr.freeStorage (dataPtr, deleteIt); } try { Slicer slicer (IPosition(2,0,0), shape); TiledFileAccess tfac ("tTiledFileAccess_tmp.dat", 0, shape, IPosition(2,17,1), TpDComplex, TSMOption::Cache, True, True); AlwaysAssertExit (tfac.isWritable()); AlwaysAssertExit (allEQ (arr, tfac.getDComplex (slicer))); AlwaysAssertExit (tfac.shape() == shape); AlwaysAssertExit (tfac.tileShape() == IPosition(2,17,1)); TiledFileAccess tfal ("tTiledFileAccess_tmp.dat", off2, shape, IPosition(2,17,1), TpDComplex, TSMOption::Cache, True); AlwaysAssertExit (allEQ (arr, tfal.getDComplex (slicer))); tfac.put (tfac.getDComplex(slicer) + DComplex(1,2), slicer); tfal.put (tfal.getDComplex(slicer) + DComplex(3,5), slicer); } catch (std::exception& x) { cout << "Exception: " << x.what() << endl; return 1; } try { TiledFileAccess tfac ("tTiledFileAccess_tmp.dat", 0, shape, IPosition(2,17,1), TpDComplex, TSMOption::Buffer, True, True); AlwaysAssertExit (tfac.shape() == shape); AlwaysAssertExit (tfac.tileShape() == IPosition(2,17,1)); TiledFileAccess tfal ("tTiledFileAccess_tmp.dat", off2, shape, IPosition(2,17,1), TpDComplex, TSMOption::Buffer, True); IPosition st(2,0,0); IPosition end(2,15,0); IPosition leng(2,16,1); for (Int i=0; i arrs(shape); Array arrf(shape); Float scale = 2; Float offset = 2; indgen(arrs); indgen(arrf, float(2), float(2)); { Bool deleteIt; const uChar* dataPtr = arrs.getStorage (deleteIt); std::shared_ptr fios (new RegularFileIO(RegularFile("tTiledFileAccess_tmp.dat"), ByteIO::New)); CanonicalIO ios (fios); ios.write (shape.product(), dataPtr); arrs.freeStorage (dataPtr, deleteIt); } try { Slicer slicer (IPosition(2,0,0), shape); TiledFileAccess tfac ("tTiledFileAccess_tmp.dat", 0, shape, IPosition(2,10,5), TpUChar, TSMOption::Cache, True, True); AlwaysAssertExit (allEQ (arrs, tfac.getUChar (slicer))); AlwaysAssertExit (allEQ (arrf, tfac.getFloat (slicer, scale, offset, uChar(255)))); AlwaysAssertExit (tfac.shape() == shape); AlwaysAssertExit (tfac.tileShape() == IPosition(2,10,5)); } catch (std::exception& x) { cout << "Exception: " << x.what() << endl; return 1; } } // Test for a Short array written in canonical format. // Read it also back as Float with a scale and offset. { IPosition shape(2,17,40); Array arrs(shape); Array arrf(shape); Float scale = 2; Float offset = -10; indgen(arrs); indgen(arrf, float(-10), float(2)); { Bool deleteIt; const Short* dataPtr = arrs.getStorage (deleteIt); std::shared_ptr fios (new RegularFileIO(RegularFile("tTiledFileAccess_tmp.dat"), ByteIO::New)); CanonicalIO ios (fios); ios.write (shape.product(), dataPtr); arrs.freeStorage (dataPtr, deleteIt); } try { Slicer slicer (IPosition(2,0,0), shape); TiledFileAccess tfac ("tTiledFileAccess_tmp.dat", 0, shape, IPosition(2,17,4), TpShort, TSMOption::Cache, True, True); AlwaysAssertExit (allEQ (arrs, tfac.getShort (slicer))); AlwaysAssertExit (allEQ (arrf, tfac.getFloat (slicer, scale, offset, short(-32768)))); AlwaysAssertExit (tfac.shape() == shape); AlwaysAssertExit (tfac.tileShape() == IPosition(2,17,4)); } catch (std::exception& x) { cout << "Exception: " << x.what() << endl; return 1; } } // Test the tileShape function in various ways. { try { cout << TiledFileAccess::makeTileShape (IPosition(2,17,40)) << endl; cout << TiledFileAccess::makeTileShape (IPosition(2,17,40), 17) << endl; cout << TiledFileAccess::makeTileShape (IPosition(2,17,40), 34) << endl; cout << TiledFileAccess::makeTileShape (IPosition(2,17,40), 33) << endl; cout << TiledFileAccess::makeTileShape (IPosition(2,17,40), 15) << endl; cout << TiledFileAccess::makeTileShape (IPosition(2,17,40), 3) << endl; } catch (std::exception& x) { cout << "Exception: " << x.what() << endl; return 1; } } return 0; } casacore-3.7.1/tables/DataMan/test/tTiledFileAccess.out000066400000000000000000000006331476623553700227640ustar00rootroot000000000000000 >>> TSMCube cache statistics: cubeShape: [16, 32] tileShape: [16, 1] maxCacheSz:100000 cacheSize: 1 (*64) #buckets: 32 #reads: 32 #accesses: 32 hit-rate: 0% <<< 1 0 >>> TSMCube cache statistics: cubeShape: [32, 16] tileShape: [16, 1] maxCacheSz:100000 cacheSize: 1 (*64) #buckets: 32 #reads: 32 #accesses: 32 hit-rate: 0% <<< 0 [15, 16] [17, 40] [17, 1] [17, 2] [17, 2] [17, 1] [1, 1] casacore-3.7.1/tables/DataMan/test/tTiledShapeStM_1.cc000066400000000000000000000326211476623553700224470ustar00rootroot00000000000000//# tTiledShapeStM_1.cc: Test program for performance of the TiledShapeStMan classes //# Copyright (C) 2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //

        // Test program for performance of the TiledShapeStMan class. // // This program tests the class TiledShapeStMan and related classes. // The results are written to stdout. The script executing this program, // compares the results with the reference output file. // Outcomment and uncomment the correct typedef and define. //typedef Int Type; typedef Double Type; #define ARRINIT indgen(array) #define ARRINCR array += (Type)1 //typedef Bool Type; //#define ARRINIT array = False //#define ARRINCR array = !array TSMOption makeAcc (int acc, Bool read=True) { if (!read) { acc = acc>>2; } if ((acc&3) == 1) { cout << "use mmapped TSM access" << endl; return TSMOption (TSMOption::MMap, 0, 0); } else if ((acc&3) == 2) { cout << "use buffered TSM access" << endl; return TSMOption (TSMOption::Buffer, 0, 0); } cout << "use cached TSM access" << endl; return TSMOption (TSMOption::Cache, 0, 0); } Bool readTable (int acc, Bool chk, const IPosition& shape, uInt nrrow) { Bool ok = True; Table table("tTiledShapeStM_1_tmp.data", Table::Old, makeAcc(acc)); if (table.nrow() != nrrow) { cout << "Table has " << table.nrow() << " rows; expected " << nrrow << endl; return False; } ArrayColumn data (table, "Data"); Array result; Array array(shape); ARRINIT; Timer timer; for (uInt i=0; i data (table, "Data"); Array result; Array array(shape); ARRINIT; Slicer slicer(blc, trc, inc, Slicer::endIsLast); Array arraySlice(array(blc, trc, inc)); Timer timer; for (uInt i=0; i data (table, "Data"); Array result; uInt lastAxis = shape.nelements(); IPosition shpa = shape.concatenate (IPosition(1,1)); IPosition blca = blc.concatenate (IPosition(1,0)); IPosition trca = trc.concatenate (IPosition(1,0)); IPosition inca = inc.concatenate (IPosition(1,1)); Array array(shpa); ARRINIT; Slicer slicer(blc, trc, inc, Slicer::endIsLast); IPosition bl, tr, ic; IPosition resShp = slicer.inferShapeFromSource (shape, bl, tr, ic); Timer timer; for (Int i=0; i arr (result(st, end)); Array arraySlice(array(blca, trca, inca)); if (! allEQ (arraySlice, arr)) { cout << "mismatch in data row " << j << ", x " << i << endl; ok = False; } ARRINCR; } ARRINIT; } } timer.show("Read col-x"); if (chk && ok) { cout << " readColX checks successfull" << endl; } return ok; } Bool readColY (int acc, Bool chk, const IPosition& shape, const IPosition& blc, const IPosition& trc, const IPosition& inc, uInt nrrow) { Bool ok = True; Table table("tTiledShapeStM_1_tmp.data", Table::Old, makeAcc(acc)); if (table.nrow() != nrrow) { cout << "Table has " << table.nrow() << " rows; expected " << nrrow << endl; return False; } ArrayColumn data (table, "Data"); Array result; uInt lastAxis = shape.nelements(); IPosition shpa = shape.concatenate (IPosition(1,1)); IPosition blca = blc.concatenate (IPosition(1,0)); IPosition trca = trc.concatenate (IPosition(1,0)); IPosition inca = inc.concatenate (IPosition(1,1)); Array array(shpa); ARRINIT; Slicer slicer(blc, trc, inc, Slicer::endIsLast); IPosition bl, tr, ic; IPosition resShp = slicer.inferShapeFromSource (shape, bl, tr, ic); Timer timer; for (Int i=0; i arr (result(st, end)); Array arraySlice(array(blca, trca, inca)); if (! allEQ (arraySlice, arr)) { cout << "mismatch in data row " << j << ", x " << i << endl; ok = False; } ARRINCR; } ARRINIT; } } timer.show("Read col-y"); if (chk && ok) { cout << " readColY checks successfull" << endl; } return ok; } Bool readCol (int acc, Bool chk, const IPosition& shape, const IPosition& blc, const IPosition& trc, const IPosition& inc, uInt nrrow) { Bool ok = True; Table table("tTiledShapeStM_1_tmp.data", Table::Old, makeAcc(acc)); if (table.nrow() != nrrow) { cout << "Table has " << table.nrow() << " rows; expected " << nrrow << endl; return False; } ArrayColumn data (table, "Data"); Array result; uInt lastAxis = shape.nelements(); IPosition shpa = shape.concatenate (IPosition(1,1)); IPosition blca = blc.concatenate (IPosition(1,0)); IPosition trca = trc.concatenate (IPosition(1,0)); IPosition inca = inc.concatenate (IPosition(1,1)); Array array(shpa); ARRINIT; Slicer slicer(blc, trc, inc, Slicer::endIsLast); Array arraySlice(array(blca, trca, inca)); Timer timer; data.getColumn (slicer, result); if (chk) { IPosition end = result.shape() - 1; end(lastAxis) = 0; IPosition st = IPosition(end.nelements(), 0); for (uInt i=0; i arr (result(st, end)); if (! allEQ (arraySlice, arr)) { cout << "mismatch in data row " << i << endl; ok = False; } ARRINCR; } } timer.show("Read col "); if (chk && ok) { cout << " readCol checks successfull" << endl; } return ok; } void writeVar (int acc, Bool chk, const IPosition& shape, const IPosition& tileShape, uInt nrrow) { // Build the table description. TableDesc td ("", "1", TableDesc::Scratch); td.addColumn (ArrayColumnDesc ("Data", shape.nelements())); td.defineHypercolumn ("TSMExample", shape.nelements()+1, stringToVector ("Data")); // Now create a new table from the description. SetupNewTable newtab("tTiledShapeStM_1_tmp.data", td, Table::New); // Create a storage manager for it. TiledShapeStMan sm1 ("TSMExample", tileShape); newtab.bindAll (sm1); Table table(newtab, 0, False, Table::AipsrcEndian, makeAcc(acc, False)); ArrayColumn data (table, "Data"); Array array(shape); uInt i; ARRINIT; Timer timer; try { for (i=0; i> acc; } { istringstream istr(argv[2]); istr >> mode; } { istringstream istr(argv[3]); istr >> nrow; } { istringstream istr(argv[4]); istr >> nx; } { istringstream istr(argv[5]); istr >> ny; } uInt tx = nx; if (argc >= 7) { istringstream istr(argv[6]); istr >> tx; } uInt ty = ny; if (argc >= 8) { istringstream istr(argv[7]); istr >> ty; } uInt tz = 1; if (argc >= 9) { istringstream istr(argv[8]); istr >> tz; } uInt sx = 0; if (argc >= 10) { istringstream istr(argv[9]); istr >> sx; } uInt sy = 0; if (argc >= 11) { istringstream istr(argv[10]); istr >> sy; } uInt ex = nx-1; if (argc >= 12) { istringstream istr(argv[11]); istr >> ex; } uInt ey = ny-1; if (argc >= 13) { istringstream istr(argv[12]); istr >> ey; } uInt ix = 1; if (argc >= 14) { istringstream istr(argv[13]); istr >> ix; } uInt iy = 1; if (argc >= 15) { istringstream istr(argv[14]); istr >> iy; } IPosition shape(2,nx,ny); IPosition tileShape(3,tx,ty,tz); IPosition blc(2,sx,sy); IPosition trc(2,ex,ey); IPosition inc(2,ix,iy); cout << "acc: " << acc << endl; cout << "mode: " << mode << endl; cout << "nrow: " << nrow << endl; cout << "shape: " << shape << endl; cout << "tileShape: " << tileShape << endl; cout << "sei: " << blc << trc << inc << endl; cout << ">>>" << endl; writeVar (acc, mode%2==1, shape, tileShape, nrow); if (! readTable (acc, mode%2==1, shape, nrow)) { ok = False; } if ((mode&2) == 2) { if (! readSlices (acc, mode%2==1, shape, blc, trc, inc, nrow)) { ok = False; } } if ((mode&4) == 4) { if (! readColX (acc, mode%2==1, shape, blc, trc, inc, nrow)) { ok = False; } } if ((mode&8) == 8) { if (! readColY (acc, mode%2==1, shape, blc, trc, inc, nrow)) { ok = False; } } if ((mode&16) == 16) { if (! readCol (acc, mode%2==1, shape, blc, trc, inc, nrow)) { ok = False; } } } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } cout << "<<<" << endl; if (!ok) { return 1; } return 0; // exit with success status } casacore-3.7.1/tables/DataMan/test/tTiledShapeStMan.cc000066400000000000000000000530051476623553700225450ustar00rootroot00000000000000//# tTiledShapeStMan.cc: Test program for the TiledShapeStMan classes //# Copyright (C) 1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Test program for the TiledShapeStMan class. // // This program tests the class TiledShapeStMan and related classes. // The results are written to stdout. The script executing this program, // compares the results with the reference output file. // First build a description. void writeFixed (const TSMOption& tsmOpt) { cout << "WriteFixed ..." << endl; // Build the table description. TableDesc td ("", "1", TableDesc::Scratch); td.addColumn (ArrayColumnDesc ("Pol", IPosition(1,16), ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc ("Freq", 1, ColumnDesc::FixedShape)); td.addColumn (ScalarColumnDesc ("Time")); td.addColumn (ArrayColumnDesc("Data", 2, ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc ("Flag", 2, ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc ("Weight", IPosition(2,16,25), ColumnDesc::FixedShape)); td.defineHypercolumn ("TSMExample", 3, stringToVector ("Data,Flag,Weight"), stringToVector ("Pol,Freq,Time")); // Now create a new table from the description. SetupNewTable newtab("tTiledShapeStMan_tmp.data", td, Table::New); // Create a storage manager for it. // Let the tile shape not fit integrally in the cube shape. TiledShapeStMan sm1 ("TSMExample", IPosition(2,5,6)); newtab.setShapeColumn ("Freq", IPosition(1,25)); newtab.setShapeColumn ("Data", IPosition(2,16,25)); newtab.setShapeColumn ("Flag", IPosition(2,16,25)); newtab.bindAll (sm1); Table table(newtab, 0, False, Table::LittleEndian, tsmOpt); Vector freqValues(25); Vector polValues(16); indgen (freqValues, float(200)); indgen (polValues, float(300)); float timeValue; timeValue = 34; ArrayColumn freq (table, "Freq"); ArrayColumn pol (table, "Pol"); ArrayColumn data (table, "Data"); ArrayColumn flag (table, "Flag"); ArrayColumn weight (table, "Weight"); ScalarColumn time (table, "Time"); Matrix darray(IPosition(2,16,25)); Matrix farray(IPosition(2,16,25)); Matrix warray(IPosition(2,16,25)); Matrix dresult(IPosition(2,16,25)); Matrix fresult(IPosition(2,16,25)); Matrix wresult(IPosition(2,16,25)); indgen (darray); indgen (warray); for (uInt i=0; i<101; i++) { for (uInt j=0; j freq (table, "Freq"); ArrayColumn pol (table, "Pol"); ArrayColumn data (table, "Data"); ArrayColumn flag (table, "Flag"); ArrayColumn weight (table, "Weight"); ScalarColumn time (table, "Time"); float timeValue; timeValue = 34; for (uInt i=0; i dresult(dwShape); Array fresult(dwShape); Array wresult(dwShape); data.get (i, dresult); flag.get (i, fresult); weight.get (i, wresult); Array darray(dresult.shape()); Array farray(fresult.shape()); Array warray(wresult.shape()); indgen (darray, float(i)*Complex(100,10)); for (uInt j=0; j freqValues (dresult.shape()(1)); Vector polValues (dresult.shape()(0)); indgen (freqValues, float(200)); indgen (polValues, float(300)); if (! allEQ (darray, dresult)) { cout << "mismatch in data row " << i << endl; cout << dresult; } if (! allEQ (farray, fresult)) { cout << "mismatch in flag row " << i << endl; } if (! allEQ (warray, wresult)) { cout << "mismatch in weight row " << i << endl; } if (! allEQ (freq(i), freqValues)) { cout << "mismatch in freq row " << i << endl; } if (! allEQ (pol(i), polValues)) { cout << "mismatch in pol row " << i << endl; } if (time(i) != timeValue) { cout << "mismatch in time row " << i << endl; } timeValue += 5; if (i < 10) { cout << "tileshape=" << data.tileShape(i) << endl; } } } void writeVar(const TSMOption& tsmOpt) { cout << "WriteVar ..." << endl; // Build the table description. TableDesc td ("", "1", TableDesc::Scratch); td.addColumn (ArrayColumnDesc ("Pol", 1)); td.addColumn (ArrayColumnDesc ("Freq", 1)); td.addColumn (ScalarColumnDesc ("Time")); td.addColumn (ArrayColumnDesc("Data", 2)); td.addColumn (ArrayColumnDesc ("Flag", 2)); td.addColumn (ArrayColumnDesc ("Weight", 2)); td.defineHypercolumn ("TSMExample", 3, stringToVector ("Data,Flag,Weight"), stringToVector ("Pol,Freq,Time")); // Now create a new table from the description. SetupNewTable newtab("tTiledShapeStMan_tmp.data", td, Table::New); // Create a storage manager for it. // Let the tile shape fit integrally in the cube shape. TiledShapeStMan sm1 ("TSMExample", IPosition(2,4,5)); newtab.bindAll (sm1); Table table(newtab, 0, False, Table::BigEndian, tsmOpt); Vector freqValues(25); Vector polValues(16); indgen (freqValues, float(200)); indgen (polValues, float(300)); float timeValue; timeValue = 34; ArrayColumn data (table, "Data"); ArrayColumn flag (table, "Flag"); ArrayColumn weight (table, "Weight"); ScalarColumn time (table, "Time"); ArrayColumn freq (table, "Freq"); ArrayColumn pol (table, "Pol"); Matrix darray(IPosition(2,16,25)); Matrix farray(IPosition(2,16,25)); Matrix warray(IPosition(2,16,25)); indgen (darray); indgen (warray); for (uInt i=0; i<5; i++) { table.addRow(); cout << " pol.isDefined=" << pol.isDefined(i) << endl; pol.setShape (i, IPosition(1,16), IPosition(1,1)); cout << " pol.isDefined=" << pol.isDefined(i) << endl; cout << "data.isDefined=" << data.isDefined(i) << endl; data.setShape (i, IPosition(2,16,25), IPosition(2,12,10)); cout << "weig.isDefined=" << weight.isDefined(i) << endl; cout << "freq.isDefined=" << freq.isDefined(i) << endl; cout << pol.shape(i) << freq.shape(i) << data.shape(i) << weight.shape(i) << endl; for (uInt j=0; j ("Pol", 1)); td.addColumn (ArrayColumnDesc ("Freq", 1)); td.addColumn (ScalarColumnDesc ("Time")); td.addColumn (ArrayColumnDesc("Data", 2)); td.addColumn (ArrayColumnDesc ("Flag", IPosition(2,16,25), ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc ("Weight", IPosition(2,16,25), ColumnDesc::FixedShape)); td.defineHypercolumn ("TSMExample", 3, stringToVector ("Data,Flag,Weight"), stringToVector ("Pol,Freq,Time")); // Now create a new table from the description. SetupNewTable newtab("tTiledShapeStMan_tmp.data", td, Table::New); // Create a storage manager for it. // Let the tile shape match the cube shape. TiledShapeStMan sm1 ("TSMExample", IPosition(2,16,25)); newtab.bindAll (sm1); Table table(newtab, 0, False, Table::LocalEndian, tsmOpt); Vector freqValues(25); Vector polValues(16); indgen (freqValues, float(200)); indgen (polValues, float(300)); float timeValue; timeValue = 34; ArrayColumn data (table, "Data"); ArrayColumn flag (table, "Flag"); ArrayColumn weight (table, "Weight"); ScalarColumn time (table, "Time"); ArrayColumn freq (table, "Freq"); ArrayColumn pol (table, "Pol"); Matrix darray(IPosition(2,16,25)); Matrix farray(IPosition(2,16,25)); Matrix warray(IPosition(2,16,25)); indgen (darray); indgen (warray); for (uInt i=0; i<5; i++) { table.addRow(); cout << " pol.isDefined=" << pol.isDefined(i) << endl; pol.setShape (i, IPosition(1,16), IPosition(1,1)); cout << " pol.isDefined=" << pol.isDefined(i) << endl; cout << "data.isDefined=" << data.isDefined(i) << endl; data.setShape (i, IPosition(2,16,25), IPosition(2,12,10)); cout << "weig.isDefined=" << weight.isDefined(i) << endl; cout << "freq.isDefined=" << freq.isDefined(i) << endl; cout << pol.shape(i) << freq.shape(i) << data.shape(i) << weight.shape(i) << endl; for (uInt j=0; j ("Pol", 1)); td.addColumn (ArrayColumnDesc ("Freq", 1)); td.addColumn (ScalarColumnDesc ("Time")); td.addColumn (ArrayColumnDesc("Data", 2)); td.addColumn (ArrayColumnDesc ("Flag", 2)); td.addColumn (ArrayColumnDesc ("Weight", 2)); td.defineHypercolumn ("TSMExample", 3, stringToVector ("Data,Flag,Weight"), stringToVector ("Pol,Freq,Time")); // Now create a new table from the description. SetupNewTable newtab("tTiledShapeStMan_tmp.data", td, Table::New); // Create a storage manager for it. TiledShapeStMan sm1 ("TSMExample", IPosition(2,5,6)); newtab.bindAll (sm1); Table table(newtab, 0, False, Table::LittleEndian, tsmOpt); Vector polValues(16); indgen (polValues, float(300)); float timeValue; timeValue = 34; ArrayColumn data (table, "Data"); ArrayColumn flag (table, "Flag"); ArrayColumn weight (table, "Weight"); ScalarColumn time (table, "Time"); ArrayColumn freq (table, "Freq"); ArrayColumn pol (table, "Pol"); for (uInt i=0; i<10; i++) { uInt n2 = 10 + i%3; table.addRow(); cout << " pol.isDefined=" << pol.isDefined(i) << endl; pol.setShape (i, IPosition(1,16), IPosition(1,1)); cout << " pol.isDefined=" << pol.isDefined(i) << endl; cout << "data.isDefined=" << data.isDefined(i) << endl; data.setShape (i, IPosition(2,16,n2), IPosition(2,12,10)); cout << "weig.isDefined=" << weight.isDefined(i) << endl; cout << "freq.isDefined=" << freq.isDefined(i) << endl; cout << pol.shape(i) << freq.shape(i) << data.shape(i) << weight.shape(i) << endl; Matrix darray(IPosition(2,16,n2)); Matrix farray(IPosition(2,16,n2)); Matrix warray(IPosition(2,16,n2)); indgen (darray, float(i)*Complex(100, 10)); for (uInt j=0; j freqValues(n2); indgen (freqValues, float(200)); data.put (i, darray); flag.put (i, farray); weight.put (i, warray); freq.put (i, freqValues); pol.put (i, polValues); time.put (i, timeValue); timeValue += 5; } } void writeNoHyper(const TSMOption& tsmOpt) { cout << "WriteNoHyper ..." << endl; // Build the table description. TableDesc td ("", "1", TableDesc::Scratch); td.addColumn (ArrayColumnDesc ("Pol", IPosition(1,16), ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc ("Freq", 1, ColumnDesc::FixedShape)); td.addColumn (ScalarColumnDesc ("Time")); td.addColumn (ArrayColumnDesc("Data", 2, ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc ("Flag", 2, ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc ("Weight", IPosition(2,16,25), ColumnDesc::FixedShape)); // Now create a new table from the description. SetupNewTable newtab("tTiledShapeStMan_tmp.data", td, Table::New); // Create a storage manager for it. TiledShapeStMan sm1 ("TSMExample", IPosition(2,5,6)); newtab.setShapeColumn ("Freq", IPosition(1,25)); newtab.setShapeColumn ("Data", IPosition(2,16,25)); newtab.setShapeColumn ("Flag", IPosition(2,16,25)); newtab.bindColumn ("Data", sm1); newtab.bindColumn ("Flag", sm1); newtab.bindColumn ("Weight", sm1); Table table(newtab, 0, False, Table::BigEndian, tsmOpt); Vector freqValues(25); Vector polValues(16); indgen (freqValues, float(200)); indgen (polValues, float(300)); float timeValue; timeValue = 34; ArrayColumn data (table, "Data"); ArrayColumn flag (table, "Flag"); ArrayColumn weight (table, "Weight"); ScalarColumn time (table, "Time"); ArrayColumn freq (table, "Freq"); ArrayColumn pol (table, "Pol"); Matrix darray(IPosition(2,16,25)); Matrix farray(IPosition(2,16,25)); Matrix warray(IPosition(2,16,25)); Matrix dresult(IPosition(2,16,25)); Matrix fresult(IPosition(2,16,25)); Matrix wresult(IPosition(2,16,25)); indgen (darray); indgen (warray); for (uInt i=0; i<101; i++) { for (uInt j=0; j ("Flag", 2, ColumnDesc::FixedShape)); // Now create a new table from the description. SetupNewTable newtab("tTiledShapeStMan_tmp.data", td, Table::New); // Create a storage manager for it. TiledShapeStMan sm1 ("TSMExample", IPosition(3,1,7,2)); newtab.setShapeColumn ("Flag", IPosition(2,16,25)); newtab.bindColumn ("Flag", sm1); Table table(newtab, 0, False, Table::BigEndian, tsmOpt); ArrayColumn flag (table, "Flag"); Matrix ones(IPosition(2,16,25)); Matrix zeros(IPosition(2,16,25)); Matrix fresult(IPosition(2,16,25)); table.addRow(); table.addRow(); table.addRow(); table.addRow(); for (uInt j=0; j < fresult.nelements(); ++j) { zeros.data()[j] = 0; ones.data()[j] = 1; } flag.put (3, zeros); flag.get (3, fresult); if (! allEQ (fresult, zeros)) { cout << "Problem writing row 3" << endl; return; } flag.put (0, ones); flag.put (1, ones); flag.put (2, ones); flag.get (3, fresult); if (! allEQ (fresult, zeros)) { cout << "Row 3 has changed since it was written!" << endl; } return; } int main () { try { writeFixed (TSMOption::MMap); readTable (IPosition(2,16,25), TSMOption::Buffer); writeVar (TSMOption::Buffer); readTable (IPosition(2,16,25), TSMOption::Cache); writeFixVar (TSMOption::Cache); readTable (IPosition(2,16,25), TSMOption::MMap); writeVarShaped (TSMOption::Default); testCacheSizing (); readTable (IPosition(), TSMOption::Aipsrc); writeNoHyper (TSMOption::Aipsrc); readTable (IPosition(2,16,25), TSMOption::Default); writeFlags(); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } casacore-3.7.1/tables/DataMan/test/tTiledShapeStMan.out000066400000000000000000000065071476623553700227740ustar00rootroot00000000000000WriteFixed ... Checking 101 rows tileshape=[5, 6, 1092] tileshape=[5, 6, 1092] tileshape=[5, 6, 1092] tileshape=[5, 6, 1092] tileshape=[5, 6, 1092] tileshape=[5, 6, 1092] tileshape=[5, 6, 1092] tileshape=[5, 6, 1092] tileshape=[5, 6, 1092] tileshape=[5, 6, 1092] WriteVar ... pol.isDefined=0 pol.isDefined=1 data.isDefined=0 weig.isDefined=1 freq.isDefined=1 [16][25][16, 25][16, 25] pol.isDefined=0 pol.isDefined=1 data.isDefined=0 weig.isDefined=1 freq.isDefined=1 [16][25][16, 25][16, 25] pol.isDefined=0 pol.isDefined=1 data.isDefined=0 weig.isDefined=1 freq.isDefined=1 [16][25][16, 25][16, 25] pol.isDefined=0 pol.isDefined=1 data.isDefined=0 weig.isDefined=1 freq.isDefined=1 [16][25][16, 25][16, 25] pol.isDefined=0 pol.isDefined=1 data.isDefined=0 weig.isDefined=1 freq.isDefined=1 [16][25][16, 25][16, 25] Checking 5 rows tileshape=[12, 10, 273] tileshape=[12, 10, 273] tileshape=[12, 10, 273] tileshape=[12, 10, 273] tileshape=[12, 10, 273] WriteFixVar ... pol.isDefined=1 pol.isDefined=1 data.isDefined=1 weig.isDefined=1 freq.isDefined=1 [16][25][16, 25][16, 25] pol.isDefined=1 pol.isDefined=1 data.isDefined=1 weig.isDefined=1 freq.isDefined=1 [16][25][16, 25][16, 25] pol.isDefined=1 pol.isDefined=1 data.isDefined=1 weig.isDefined=1 freq.isDefined=1 [16][25][16, 25][16, 25] pol.isDefined=1 pol.isDefined=1 data.isDefined=1 weig.isDefined=1 freq.isDefined=1 [16][25][16, 25][16, 25] pol.isDefined=1 pol.isDefined=1 data.isDefined=1 weig.isDefined=1 freq.isDefined=1 [16][25][16, 25][16, 25] Checking 5 rows tileshape=[16, 25, 82] tileshape=[16, 25, 82] tileshape=[16, 25, 82] tileshape=[16, 25, 82] tileshape=[16, 25, 82] WriteVarShaped ... pol.isDefined=0 pol.isDefined=1 data.isDefined=0 weig.isDefined=1 freq.isDefined=1 [16][10][16, 10][16, 10] pol.isDefined=0 pol.isDefined=1 data.isDefined=0 weig.isDefined=1 freq.isDefined=1 [16][11][16, 11][16, 11] pol.isDefined=0 pol.isDefined=1 data.isDefined=0 weig.isDefined=1 freq.isDefined=1 [16][12][16, 12][16, 12] pol.isDefined=0 pol.isDefined=1 data.isDefined=0 weig.isDefined=1 freq.isDefined=1 [16][10][16, 10][16, 10] pol.isDefined=0 pol.isDefined=1 data.isDefined=0 weig.isDefined=1 freq.isDefined=1 [16][11][16, 11][16, 11] pol.isDefined=0 pol.isDefined=1 data.isDefined=0 weig.isDefined=1 freq.isDefined=1 [16][12][16, 12][16, 12] pol.isDefined=0 pol.isDefined=1 data.isDefined=0 weig.isDefined=1 freq.isDefined=1 [16][10][16, 10][16, 10] pol.isDefined=0 pol.isDefined=1 data.isDefined=0 weig.isDefined=1 freq.isDefined=1 [16][11][16, 11][16, 11] pol.isDefined=0 pol.isDefined=1 data.isDefined=0 weig.isDefined=1 freq.isDefined=1 [16][12][16, 12][16, 12] pol.isDefined=0 pol.isDefined=1 data.isDefined=0 weig.isDefined=1 freq.isDefined=1 [16][10][16, 10][16, 10] Testing cache sizing via accessor ... completed testing cache sizing via accessor; status: passed Checking 10 rows tileshape=[12, 10, 273] tileshape=[12, 10, 273] tileshape=[12, 10, 273] tileshape=[12, 10, 273] tileshape=[12, 10, 273] tileshape=[12, 10, 273] tileshape=[12, 10, 273] tileshape=[12, 10, 273] tileshape=[12, 10, 273] tileshape=[12, 10, 273] WriteNoHyper ... Checking 101 rows tileshape=[5, 6, 1092] tileshape=[5, 6, 1092] tileshape=[5, 6, 1092] tileshape=[5, 6, 1092] tileshape=[5, 6, 1092] tileshape=[5, 6, 1092] tileshape=[5, 6, 1092] tileshape=[5, 6, 1092] tileshape=[5, 6, 1092] tileshape=[5, 6, 1092] casacore-3.7.1/tables/DataMan/test/tTiledStMan.cc000066400000000000000000000066571476623553700215770ustar00rootroot00000000000000//# tTiledStMan.cc: Test program of TiledStMan class //# Copyright (C) 1997,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include // // Test program for TiledStMan class. // // This program tests the class TiledCellStMan and related classes. // The results are written to stdout. The script executing this program, // compares the results with the reference output file. void doIt (uInt tileSize); IPosition getVec (uInt nrdim, const String& prompt); int main (int argc, const char* argv[]) { // Get the command line arguments as cube shape, tile shape. if (argc < 2) { cout << ">>>" << endl; cout << "tTiledStMan tests the function makeTileShape." << endl; cout << "Invoke as tTiledStMan tileSize (in bytes)" << endl; cout << " Eg. tTiledStMan 32768" << endl; cout << "<<<" << endl; return 0; } try { uInt tileSize; istringstream istr1(argv[1]); istr1 >> tileSize; doIt (tileSize); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } // First build a description. void doIt (uInt tileSize) { // Convert the command line argument to shape. while (True) { IPosition shape = getVec (10, "cube shape (end means stop): "); if (shape.nelements() == 0) { break; } cout << "tileshape = " << TiledStMan::makeTileShape (shape, 0.5, tileSize) << endl; } } IPosition getVec (uInt nrdim, const String& prompt) { while (True) { cout << prompt; String str; cin >> str; if (str == "end") { return IPosition(); } Vector vec = strToVector (str); if (vec.nelements() > nrdim) { cout << "value can contain max. " << nrdim << " values" << endl; }else{ Bool error = False; IPosition pos(vec.nelements()); for (uInt i=0; i> pos(i); if (pos(i) < 0) { cout << "Value " << pos(i) << " must be >= 0" << endl; error = True; break; } } if (!error) { return pos; } } } return IPosition(); } casacore-3.7.1/tables/DataMan/test/tVACEngine.cc000066400000000000000000000132031476623553700213130ustar00rootroot00000000000000//# tVACEngine.cc: Test program for class VACEngine //# Copyright (C) 1994,1995,1996,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include "dVACEngine.h" #include "dVACEngine.cc" #include #include #include #include #include #include #include #include #include #include #include // Test program for class VACEngine // This program tests the virtual column engine VACEngine. // It is using the example class VACExampleVACEngine for that purpose // (note that BaseColumnDesc adds VACEngine to the default datamanager type). // The results are written to stdout. The script executing this program, // compares the results with the reference output file. void a(); void b(); int main () { try { a(); b(); } catch (const AipsError& x) { cout << "Caught an exception: " << x.getMesg() << endl; return 1; } return 0; // exit with success status } // First build a description. void a() { // First register the virtual column engine. VACExampleVACEngine::registerClass(); // Add ArrayColumnDesc to column type map. ArrayColumnDesc("x").registerClass(); // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.comment() = "A test of class TableDesc"; td.addColumn (ArrayColumnDesc ("x")); td.addColumn (ArrayColumnDesc ("y")); td.addColumn (ArrayColumnDesc ("z")); td.addColumn (ArrayColumnDesc ("colA")); // Now create a new table from the description. SetupNewTable newtab("tVACEngine_tmp.data", td, Table::New); // Create the virtual column engine with the target columns x and y. VACExampleVACEngine engine ("colA", "x", "y", "z"); newtab.bindColumn ("colA", engine); Table tab(newtab, 10); // Fill the table via the virtual columns. ArrayColumn colA (tab,"colA"); for (int i=0; i<10; i++) { Vector vac(i); for (int j=0; j& x, const Array& y, const Array& z) { uInt sz = len; AlwaysAssertExit(x.size()==sz && y.size()==sz && z.size()==sz); AlwaysAssertExit(x.ndim()==1 && y.ndim()==1 && z.ndim()==1); for (int j=0; j& vac) { AlwaysAssertExit(vac.size()==uInt(len)); AlwaysAssertExit(vac.ndim()==1); for (int j=0; j colx (tab, "x"); ArrayColumn coly (tab, "y"); ArrayColumn colz (tab, "z"); ArrayColumn colA(tab, "colA"); for (int i=0; i<10; i++) { cout << "get row " << i << endl; Array x = colx.get(i); Array y = coly.get(i); Array z = colz.get(i); Array vac = colA.get(i); checkArrs (i, i, x, y, z); checkVac (i, i, vac); } // Read a slice from a range of rows. cout << "get row range" << endl; Array vecA = colA.getColumnRange (Slicer(IPosition(1,3), IPosition(1,7)), // rows Slicer(IPosition(1,1), IPosition(1,2))); // array slice AlwaysAssertExit (vecA.ndim() == 2 && vecA.size() == 14); for (int i=0; i<7; i++) { checkVac (i+4, 2, vecA[i]); } } casacore-3.7.1/tables/DataMan/test/tVACEngine.out000066400000000000000000000003721476623553700215400ustar00rootroot00000000000000Expected exception: Table DataManager error: Invalid operation: VACEngine with source column colA bound to column x; must be the same x get row 0 get row 1 get row 2 get row 3 get row 4 get row 5 get row 6 get row 7 get row 8 get row 9 get row range casacore-3.7.1/tables/DataMan/test/tVSCEngine.cc000066400000000000000000000121071476623553700213370ustar00rootroot00000000000000//# tVSCEngine.cc: Test program for class VSCEngine //# Copyright (C) 1994,1995,1996,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Define the variable to exclude the main from dVSCEngine.cc #define DVSCENGINE_MAIN //# Includes #include "dVSCEngine.h" #include "dVSCEngine.cc" #include #include #include #include #include #include #include #include #include #include #include // Test program for class VSCEngine // This program tests the virtual column engine VSCEngine. // It is using the example class VSCExampleVSCEngine for that purpose. // The results are written to stdout. The script executing this program, // compares the results with the reference output file. void a(); void b(); int main () { try { a(); b(); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } // First build a description. void a() { // First register the virtual column engine. VSCExampleVSCEngine::registerClass(); // Add ScalarColumnDesc to column type map. ScalarColumnDesc("x").registerClass(); // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.comment() = "A test of class TableDesc"; td.addColumn (ScalarColumnDesc ("x")); td.addColumn (ScalarColumnDesc ("y")); td.addColumn (ScalarColumnDesc ("z")); td.addColumn (ScalarColumnDesc ("colA")); // Now create a new table from the description. SetupNewTable newtab("tVSCEngine_tmp.data", td, Table::New); // Create the virtual column engine with the target columns x and y. VSCExampleVSCEngine engine ("colA", "x", "y", "z"); newtab.bindColumn ("colA", engine); Table tab(newtab, 10); // Fill the table via the virtual columns. ScalarColumn colA (tab,"colA"); uInt i; for (i=0; i<10; i++) { colA.put (i, VSCExample(i,i+1, String::toString(i+2))); } //# Do an erroneous thing. SetupNewTable newtab2("tVSCEngine_tmp.dat2", td, Table::Scratch); newtab2.bindColumn ("x", engine); try { Table tab2(newtab2, 10); // bound to incorrect column } catch (std::exception& x) { cout << "Expected exception: " << x.what() << endl; } } void b() { // Read back the table. Table tab("tVSCEngine_tmp.data"); ScalarColumn colx (tab, "x"); ScalarColumn coly (tab, "y"); ScalarColumn colz (tab, "z"); ScalarColumn colA(tab, "colA"); Int valx; float valy; String valz; VSCExample valA; Int i; for (i=0; i<10; i++) { cout << "get row " << i << endl; colx.get (i, valx); coly.get (i, valy); colz.get (i, valz); colA.get (i, valA); String expz = String::toString(i+2); if (valx!=i || valy!=i+1 || valz!=expz || !(valA == VSCExample(i,i+1,expz))) { cout << "error: " << valx << " " << valy << " " << valz << " " << valA.x() << " " << valA.y() << " " << valA.z() << endl; } } Vector vecA = colA.getColumn(); for (i=0; i<10; i++) { if (!(vecA(i) == VSCExample(i,i+1,String::toString(i+2)))) { cout << "error in vecA(" << i << "): " << vecA(i).x() << " " << vecA(i).y() << vecA(i).z() << endl; } } // Read a few rows. Vector rows(2); rows[0] = 2; rows[1] = 5; Vector vecRF = colA.getColumnCells(RefRows(rows)); AlwaysAssertExit (vecRF.size() == 2); AlwaysAssertExit (vecRF[0] == VSCExample(2,3,String::toString(4))); AlwaysAssertExit (vecRF[1] == VSCExample(5,6,String::toString(7))); } casacore-3.7.1/tables/DataMan/test/tVSCEngine.out000066400000000000000000000003521476623553700215600ustar00rootroot00000000000000Expected exception: Table DataManager error: Invalid operation: VSCEngine with source column colA bound to column x; must be the same get row 0 get row 1 get row 2 get row 3 get row 4 get row 5 get row 6 get row 7 get row 8 get row 9 casacore-3.7.1/tables/DataMan/test/tVirtColEng.cc000066400000000000000000000140751476623553700216000ustar00rootroot00000000000000//# tVirtColEng.cc: Test program for virtual column engine //# Copyright (C) 1994,1995,1996,1997,1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Define the variable to exclude the main from dVirtColEng.cc #define DVIRTCOLENG_MAIN //# This test program is in fact also a demo program. //# A virtual column engine is declared in dVirtColEng.h and //# implemented in dVirtColEng.cc. //# Both files are included here; the .h file for the definitions //# and the .cc file to get compiled. //# Includes #include "dVirtColEng.h" #include "dVirtColEng.cc" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Test program for virtual column engine // This program tests the class DummyVirtualEngine and related classes. // The results are written to stdout. The script executing this program, // compares the results with the reference output file. // The Dummy* classes are declared and implemented in dVirtColEng.h // and dVirtColEng.cc, resp.. void a(); void b(); int main() { try { a(); b(); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } // First build a description. void a() { // First register the virtual column engine. DummyVirtualEngine::registerClass(); // Build the table description. // Define a group name Engine for the columns intended to be virtual. TableDesc td("tTableDesc","1",TableDesc::Scratch); td.comment() = "A test of class TableDesc"; td.addColumn (ScalarColumnDesc ("DATA1")); td.addColumn (ScalarColumnDesc ("DOUB1", "", "", "Engine")); td.addColumn (ArrayColumnDesc ("DATA2")); td.addColumn (ArrayColumnDesc ("DOUB2", "", "", "Engine")); // Now create a new table from the description. SetupNewTable newtab("tVirtColEng_tmp.data", td, Table::New); // Create the virtual column engine with the scale factors // and bind the columns to them. DummyVirtualEngine engine(2.0, 3.0); newtab.bindGroup ("Engine", engine); Table tab(newtab, 10); // Fill the table via the virtual columns. ScalarColumn doub1(tab,"DOUB1"); ArrayColumn doub2(tab,"DOUB2"); Cube arrd(IPosition(3,2,3,4)); uInt i; i=0; for (uInt i2=0; i2<4; i2++) for (uInt i1=0; i1<3; i1++) for (uInt i0=0; i0<2; i0++) { arrd(i0,i1,i2) = i; i += 3; } for (i=0; i<10; i++) { doub1.put (i, i*2); doub2.put (i,arrd); arrd += (double)(3*arrd.nelements()); } } void b() { // Read back the table. Table tab("tVirtColEng_tmp.data"); ScalarColumn doub1(tab,"DOUB1"); ArrayColumn doub2(tab,"DOUB2"); ScalarColumn data1(tab,"DATA1"); ArrayColumn data2(tab,"DATA2"); uInt i; double dval; Int ival; Cube arri(IPosition(3,2,3,4)); Cube arrvali(IPosition(3,2,3,4)); Cube arrd(IPosition(3,2,3,4)); Cube arrval(IPosition(3,2,3,4)); Cube arrvalslice(arrval(Slice(0,1),Slice(0,1,2),Slice(0,2,2))); Slice tmp; Slicer nslice (tmp, tmp, tmp, Slicer::endIsLength); Slicer nslice2(Slice(0,1), Slice(0,1,2), Slice(0,2,2), Slicer::endIsLength); i=0; for (uInt i2=0; i2<4; i2++) for (uInt i1=0; i1<3; i1++) for (uInt i0=0; i0<2; i0++) { arri(i0,i1,i2) = i; arrd(i0,i1,i2) = 3*i; i++; } for (i=0; i<10; i++) { cout << "get scalar row " << i << endl; ival = data1(i); dval = doub1(i); if (ival != Int(i) || dval != 2*i) { cout << "error in row " << i << ": " << ival << " " << dval << endl; } data2.get (i, arrvali); if (!allEQ (arrvali, arri)) { cout << "error in DATA2 in row " << i << endl; } doub2.get (i, arrval); if (!allEQ (arrval, arrd)) { cout << "error in DOUB2 in row " << i << endl; } doub2.getSlice (i, nslice, arrval); if (!allEQ (arrval, arrd)) { cout << "error in DOUB2 (entire slice) in row " << i << endl; } doub2.getSlice (i, nslice2, arrvalslice); if (!allEQ (arrval, arrd)) { cout << "error in DOUB2 (partial slice) in row " << i << endl; } arrd += (double)(3*arrd.nelements()); arri += (Int)(arrd.nelements()); } Vector vec = doub1.getColumn(); cout << tab.nrow() << " " << vec.nelements() << endl; for (i=0; i<10; i++) { if (vec(i) != 2*i) { cout << "error in getColumn " << i << ": " << vec(i) << endl; } } } casacore-3.7.1/tables/DataMan/test/tVirtColEng.out000066400000000000000000000002601476623553700220110ustar00rootroot00000000000000get scalar row 0 get scalar row 1 get scalar row 2 get scalar row 3 get scalar row 4 get scalar row 5 get scalar row 6 get scalar row 7 get scalar row 8 get scalar row 9 10 10 casacore-3.7.1/tables/DataMan/test/tVirtualTaQLColumn.cc000066400000000000000000000324211476623553700231050ustar00rootroot00000000000000//# tVirtualTaQLColumn.cc: Test program for class VirtualTaQLColumn //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Test program for class VirtualTaQLColumn // This program tests the virtual column engine VirtualTaQLColumn. // The results are written to stdout. The script executing this program, // compares the results with the reference output file. TableDesc makeDesc(); void a (const TableDesc&); void check(const Table& table, Bool showname); void testSelect(); void testPerf(); int main () { try { { TableDesc td = makeDesc(); a (td); Table table("tVirtualTaQLColumn_tmp.data0"); check(table, True); table.deepCopy ("tVirtualTaQLColumn_tmp.data1", Table::New, True); check (Table("tVirtualTaQLColumn_tmp.data1"), True); Table tab2 = table.copyToMemoryTable ("tVirtualTaQLColumn_tmp.data2"); check (tab2, True); } testSelect(); testPerf(); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } // First build a description. TableDesc makeDesc() { // First register the virtual column engine. VirtualTaQLColumn::registerClass(); // Build the table description. TableDesc td("tTableDesc","1",TableDesc::Scratch); td.comment() = "A test of class tVirtualTaQLColumn"; td.addColumn (ScalarColumnDesc("ab","Comment for column ab")); td.addColumn (ScalarColumnDesc("ac")); td.addColumn (ScalarColumnDesc("ad","comment for ad")); td.addColumn (ScalarColumnDesc("ae")); td.addColumn (ScalarColumnDesc("af")); td.addColumn (ScalarColumnDesc("ag")); td.addColumn (ScalarColumnDesc("acalcc")); td.addColumn (ScalarColumnDesc("acalc")); td.addColumn (ScalarColumnDesc("acalc2")); td.addColumn (ScalarColumnDesc("acalc3")); ScalarColumnDesc acalcaf("acalcaf"); acalcaf.setMaxLength (4); td.addColumn (acalcaf); td.addColumn (ArrayColumnDesc("arr1",3,ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc("arr2",0)); td.addColumn (ArrayColumnDesc("arr3",0,ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc("arrcalc",0)); td.addColumn (ArrayColumnDesc("arrcalcc",IPosition(1,3))); return td; } void a (const TableDesc& td) { // Now create a new table from the description. SetupNewTable newtab("tVirtualTaQLColumn_tmp.data0", td, Table::New); newtab.setShapeColumn("arr1",IPosition(3,2,3,4)); newtab.setShapeColumn("arr3",IPosition(3,2,3,4)); VirtualTaQLColumn vtcc("10"); VirtualTaQLColumn vtc("ab+10."); VirtualTaQLColumn vtc2("ag+max(arr3)"); VirtualTaQLColumn vtc3("ab*ac"); VirtualTaQLColumn vtcaf("af + '1234'"); VirtualTaQLColumn vtac("ab*arr3"); VirtualTaQLColumn vtacc("[1,2,3]"); newtab.bindColumn ("acalcc", vtcc); newtab.bindColumn ("acalc", vtc); newtab.bindColumn ("acalc2", vtc2); newtab.bindColumn ("acalc3", vtc3); newtab.bindColumn ("acalcaf", vtcaf); newtab.bindColumn ("arrcalc", vtac); newtab.bindColumn ("arrcalcc", vtacc); Table tab(newtab, 10); ScalarColumn ab1(tab,"ab"); ScalarColumn ab2(tab,"ab"); ScalarColumn ac (tab,"ac"); ScalarColumn ad(tab,"ad"); ScalarColumn ae(tab,"ae"); ScalarColumn af(tab,"af"); TableColumn ag1(tab,"ag"); ScalarColumn ag(tab,"ag"); ArrayColumn arr1(tab,"arr1"); ArrayColumn arr2(tab,"arr2"); ArrayColumn arr3(tab,"arr3"); Cube arrf(IPosition(3,2,3,4)); uInt i; char str[8]; indgen (arrf); for (i=0; i<10; i++) { ab1.put (i, i); ac.put (i, i+1); ad.put (i, i+2); ae.put (i, i+3); snprintf (str, sizeof(str), "V%i_", i); af.put (i, str); arr1.put(i,arrf); arr2.put(i,arrf); arr3.put(i,arrf); arrf += (float)(arrf.nelements()); } ag1.putColumn (ad); VirtualTaQLColumn vtcm("acalc+acalc3+mean(arrcalc)"); tab.addColumn (ScalarColumnDesc("acalc4"), vtcm); } void check(const Table& tab, Bool showname) { if (!showname) cout << ">>>" << endl; cout << "Checking table " << tab.tableName() << endl; if (!showname) cout << "<<<" << endl; ScalarColumn ab2(tab,"ab"); ScalarColumn ac (tab,"ac"); ScalarColumn ad(tab,"ad"); ScalarColumn ae(tab,"ae"); ScalarColumn af(tab,"af"); ScalarColumn ag(tab,"ag"); ScalarColumn acalcc(tab,"acalcc"); ScalarColumn acalc(tab,"acalc"); ScalarColumn acalc2(tab,"acalc2"); ScalarColumn acalc3(tab,"acalc3"); ScalarColumn acalc4(tab,"acalc4"); ScalarColumn acalcaf(tab,"acalcaf"); ArrayColumn arr1(tab,"arr1"); ArrayColumn arr2(tab,"arr2"); ArrayColumn arr3(tab,"arr3"); ArrayColumn arrcalc(tab,"arrcalc"); ArrayColumn arrcalcc(tab,"arrcalcc"); Int i; Short acalc3val; Int abval, acval; uInt adval; float aeval, acalcval, acalc4val; Int acalccval; String afval, acalcafval; DComplex agval; Complex acalc2val; char str[8]; Cube arrf(IPosition(3,2,3,4)); Cube arrval(IPosition(3,2,3,4)); Cube arrvalslice(arrval(Slice(0,1),Slice(0,1,2),Slice(0,2,2))); Vector arrcexp(3); indgen (arrcexp, 1u); Slice tmp; Slicer nslice (tmp, tmp, tmp, Slicer::endIsLength); Slicer nslice2(Slice(0,1), Slice(0,1,2), Slice(0,2,2), Slicer::endIsLength); indgen (arrf); for (i=0; i<10; i++) { ab2.get (i, abval); ac.get (i, acval); ad.get (i, adval); ae.get (i, aeval); af.get (i, afval); ag.get (i, agval); acalcc.get (i, acalccval); acalc.get (i, acalcval); acalc2.get (i, acalc2val); acalc3.get (i, acalc3val); acalc4.get (i, acalc4val); acalcaf.get (i, acalcafval); snprintf (str, sizeof(str), "V%i_", i); if (abval != i || acval != i+1 || Int(adval) != i+2 || aeval != i+3 || afval != str || agval != DComplex(i+2) || acalccval != 10 || acalcafval != (afval+"1234").substr(0,4) || acalcval != abval+10 || acalc3val != abval*acval) { cout << "error in row " << i << ": " << abval << ", " << acval << ", " << adval << ", " << aeval << ", " << afval << ", " << agval << ", " << acalcval << ", " << acalc3val << endl; } if (!allEQ (arrcalcc(i), arrcexp)) { cout << "error in arrcalcc in row " << i << endl; } arr1.get (i, arrval); if (!allEQ (arrval, arrf)) { cout << "error in arr1 in row " << i << endl; } arr2.get (i, arrval); if (!allEQ (arrval, arrf)) { cout << "error in arr2 in row " << i << endl; } arr3.get (i, arrval); if (!allEQ (arrval, arrf)) { cout << "error in arr3 in row " << i << endl; } if (acalc2val != Complex(agval) + max(arrval)) { cout << "error in acalc2val in row " << i << ": " << acalc2val << endl; } arrcalc.get (i, arrval); if (!allEQ (arrval, float(abval)*arrf)) { cout << "error in arrcalc in row " << i << endl; } if (acalc4val != acalcval + acalc3val + mean(arrval)) { cout << "error in acalc4val in row " << i << ": " << acalc4val << endl; } arr2.getSlice (i, nslice, arrval); if (!allEQ (arrval, arrf)) { cout << "error in arr2 (entire slice) in row " << i << endl; } arr2.getSlice (i, nslice2, arrvalslice); if (!allEQ (arrval, arrf)) { cout << "error in arr2 (partial slice) in row " << i << endl; } arrf += (float)(arrf.nelements()); } Vector abvec = ab2.getColumn(); for (i=0; i<10; i++) { if (abvec(i) != i) { cout << "error in ab getColumn " << i << ": " << abvec(i) << endl; } } Vector acalc3vec = acalc3.getColumn(); for (i=0; i<10; i++) { if (acalc3vec(i) != i*(i+1)) { cout << "error in acalc3 getColumn " << i << ": " << acalc3vec(i) << endl; } } Array arr1a = arr1.getColumn(); if (arr1a.ndim() != 4) { cout << "arr1a not 4-dim" << endl; } i=0; uInt j0,j1,j2,j3; for (j3=0; j3<10; j3++) for (j2=0; j2<4; j2++) for (j1=0; j1<3; j1++) for (j0=0; j0<2; j0++) { if (arr1a(IPosition(4,j0,j1,j2,j3)) != i++) { cout <<"arr1a error at " < arr1b = arr1.getColumn(nslice); if (arr1b.ndim() != 4) { cout << "arr1b not 4-dim" << endl; } i=0; for (j3=0; j3<10; j3++) for (j2=0; j2<4; j2++) for (j1=0; j1<3; j1++) for (j0=0; j0<2; j0++) { if (arr1b(IPosition(4,j0,j1,j2,j3)) != i++) { cout <<"arr1b error at " < ab (iter.table(), "ab"); if (ab(0) != 9-i) { cout << "Invalid value " << ab(0) << " in ad TableIterator " << i << endl; } iter.next(); i++; } } { Int i = 0; TableIterator iter (tab, "acalc", TableIterator::Descending); while (! iter.pastEnd()) { if (iter.table().nrow() != 1) { cout << "More than 1 row in acalc TableIterator " << i << endl; } ScalarColumn ab (iter.table(), "ab"); if (ab(0) != 9-i) { cout << "Invalid value " << ab(0) << " in acalc TableIterator " << i << endl; } iter.next(); i++; } } } // Test if a tableCommand on a table containing a TaQL column works fine. // Note it requires recursive parsing. void testSelect() { // Select all rows. Table subset = tableCommand("select from tVirtualTaQLColumn_tmp.data0 " "where acalc > -1000").table(); check (subset, False); } // Test how getting a column performs. void testPerf() { { TableDesc td; td.addColumn (ScalarColumnDesc("sca")); td.addColumn (ScalarColumnDesc("row")); td.addColumn (ArrayColumnDesc("arr")); SetupNewTable newtab("tVirtualTaQLColumn_tmp.dataperf", td, Table::New); VirtualTaQLColumn sca("0"); VirtualTaQLColumn row("rownumber()", "python"); // python -> 0-based VirtualTaQLColumn arr("[1.,2,3,4]"); newtab.bindColumn ("sca", sca); newtab.bindColumn ("row", row); newtab.bindColumn ("arr", arr); Table tab(newtab, 1000000); } { Table tab("tVirtualTaQLColumn_tmp.dataperf"); ScalarColumn scacol(tab, "sca"); ScalarColumn rowcol(tab, "row"); ArrayColumn arrcol(tab, "arr"); PrecTimer timer; timer.start(); Vector vec(scacol.getColumn()); timer.stop(); timer.show (cout, "scacol"); timer.reset(); timer.start(); Vector vec2(rowcol.getColumn()); timer.stop(); timer.show (cout, "rowcol"); timer.reset(); timer.start(); Array arr(arrcol.getColumn()); timer.stop(); timer.show (cout, "arrcol"); AlwaysAssertExit (vec.size() == tab.nrow()); AlwaysAssertExit (allEQ (vec, 0)); AlwaysAssertExit (vec2.size() == tab.nrow()); for (size_t i=0; i
        «interface»
        DataManagerColumn
        + read()
        + write()
        «interface»...
        «interface»
        DataManager
        + open()
        + close()
        «interface»...
        StandardStManMSMBaseAdios2StManTiledColumnStManIncrementalStManStManAipsIOTiledShapeStManVirtualTaQLColumnBaseMappedArrayEngine<V,S>ForwardColumnEngineTiledDataStManVirtualColumnEngineMemoryStManTiledCellStManSSMColumnISMColumnMSMColumnAdios2StManColumnTSMColumn
        1
        1
        1
        1
        1
        1
        1
        1
        1
        1
        StManColumnBaseDyscoStManSSMBase- SSMIndex- BucketCache- StManArrayFileISMBase- ISMIndex- BucketCache- StManArrayFileTiledStMan- PtrBlock<TSMFile>- PtrBlock<TSMCube>DyscoStManColumn
        1
        1
        0..n
        0..n
        0..n
        0..n
        0..n
        0..n
        0..n
        0..n
        0..n
        0..n
        0..n
        0..n
        StManColumn
        For backward compatibility
        with external data managers
        For backward compatibility...
        Various derived classes, such asCompressComplexCompressFloatBitFlagsEngine<S>RetypedArrayEngine<V,S>RetypedArrayEngine
        1
        1
        0..n
        0..n
        ForwardColumnTableRODataManAccessorROTiledStManAccessor
        Text is not SVG - cannot display
        casacore-3.7.1/tables/Dysco/000077500000000000000000000000001476623553700156575ustar00rootroot00000000000000casacore-3.7.1/tables/Dysco/CMakeLists.txt000066400000000000000000000032561476623553700204250ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.6) find_package(GSL REQUIRED) find_package(Threads REQUIRED) include_directories(${GSL_INCLUDE_DIRS}) add_compile_options(-O3 -Wall -DNDEBUG) option(PORTABLE "Generate portable code" ON) option(BUILD_PACKAGES "Build Debian packages" OFF) if(NOT PORTABLE) if(USE_AVX512F) add_compile_options(-mavx512f) elseif(USE_AVX2) add_compile_options(-mavx2) elseif(USE_AVX) add_compile_options(-mavx) else() add_compile_options(-march=native) endif() else() if(USE_AVX512F OR USE_AVX2 OR USE_AVX) message(FATAL_ERROR "Cannot enable AVX features when building portable code.") endif() endif() add_library(dyscostman-object OBJECT aftimeblockencoder.cc dyscostman.cc dyscodatacolumn.cc dyscoweightcolumn.cc stochasticencoder.cc threadeddyscocolumn.cc rftimeblockencoder.cc rowtimeblockencoder.cc) set_property(TARGET dyscostman-object PROPERTY POSITION_INDEPENDENT_CODE 1) set(DYSCOSTMAN_SOURCES $ PARENT_SCOPE) set(DYSCOSTMAN_LIBRARIES ${GSL_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} PARENT_SCOPE) if(BUILD_TESTING) find_package(Boost 1.72 COMPONENTS system filesystem unit_test_framework) if(Boost_FOUND) include_directories(${Boost_INCLUDE_DIR}) add_executable(tDysco $ tests/runtests.cc tests/testbytepacking.cc tests/testdyscostman.cc tests/testtimeblockencoder.cc ) target_link_libraries(tDysco ${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${GSL_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} casa_tables casa_casa) add_test(tDysco tDysco) else() message("Boost testing framework not found.") endif() endif() casacore-3.7.1/tables/Dysco/LICENSE000066400000000000000000001045051476623553700166710ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. {one line to give the program's name and a brief idea of what it does.} Copyright (C) {year} {name of author} 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 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: {project} Copyright (C) {year} {fullname} This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . casacore-3.7.1/tables/Dysco/README.md000066400000000000000000000013241476623553700171360ustar00rootroot00000000000000The Dysco compression technique is explained in the article "Compression of interferometric radio-astronomical data", A. R. Offringa (2016; http://arxiv.org/abs/1609.02019). If you use this software, please cite the paper. # dysco A compressing storage manager for Casacore mearement sets To install: mkdir build cd build cmake ../ make -j 4 make install To be able to open compressed measurement sets, the dysco library ("libdyscostman.so") must be in your path. The Dysco compression technique is explained in [the article](http://arxiv.org/abs/1609.02019). Further documentation for the storage manager, including documentation of the `dscompress` tool: https://github.com/aroffringa/dysco/wiki casacore-3.7.1/tables/Dysco/aftimeblockencoder.cc000066400000000000000000000417401476623553700220140ustar00rootroot00000000000000#include "aftimeblockencoder.h" #include namespace { void changeChannelFactor(std::vector &data, float *metaBuffer, size_t visIndex, double factor) { metaBuffer[visIndex] /= factor; for (AFTimeBlockEncoder::DBufferRow &row : data) row.visibilities[visIndex] *= factor; } } AFTimeBlockEncoder::AFTimeBlockEncoder(size_t nPol, size_t nChannels, bool fitToMaximum) : _nPol(nPol), _nChannels(nChannels), _fitToMaximum(fitToMaximum), _rmsPerChannel(_nChannels * nPol), _ditherDist( dyscostman::StochasticEncoder::GetDitherDistribution()) {} AFTimeBlockEncoder::~AFTimeBlockEncoder() {} void AFTimeBlockEncoder::Normalize( const dyscostman::StochasticEncoder &gausEncoder, TimeBlockBuffer> &buffer, size_t antennaCount) { if (_rmsPerAntenna.size() < antennaCount) _rmsPerAntenna.resize(antennaCount); std::vector data; buffer.ConvertVector>(data); const size_t visPerRow = _nPol * _nChannels; // Normalize the channels std::vector channelRMSes(_nChannels * _nPol); for (const DBufferRow &row : data) { for (size_t i = 0; i != visPerRow; ++i) { channelRMSes[i].Include(row.visibilities[i]); } } for (DBufferRow &row : data) { for (size_t i = 0; i != visPerRow; ++i) { double rms = channelRMSes[i].RMS(); row.visibilities[i] /= rms; } } for (size_t p = 0; p != _nPol; ++p) { // Normalize the antennae calculateAntennaeRMS(data, p, antennaCount); for (DBufferRow &row : data) { double mul = (_rmsPerAntenna[row.antenna1] * _rmsPerAntenna[row.antenna2]); double fac = (mul == 0.0) ? 0.0 : 1.0 / mul; for (size_t ch = 0; ch != _nChannels; ++ch) { row.visibilities[ch * _nPol + p] *= fac; } } } if (_fitToMaximum) { for (size_t visIndex = 0; visIndex != visPerRow; ++visIndex) { double factor = 1.0; for (const DBufferRow &row : data) { if (row.antenna1 != row.antenna2) { const std::complex *ptr = &row.visibilities[visIndex]; double complMax = std::max(ptr->real(), ptr->imag()); double complMin = std::min(ptr->real(), ptr->imag()); if (complMax * factor > gausEncoder.MaxQuantity()) { factor = complMax / gausEncoder.MaxQuantity(); } else if (complMin * factor < gausEncoder.MinQuantity()) { factor = complMin / gausEncoder.MinQuantity(); } } } for (DBufferRow &row : data) row.visibilities[visIndex] *= factor; } } } void AFTimeBlockEncoder::changeAntennaFactor(std::vector &data, float *metaBuffer, size_t antennaIndex, size_t antennaCount, size_t polIndex, double factor) { const size_t visPerRow = _nPol * _nChannels; size_t metaIndex = visPerRow + antennaCount * polIndex; metaBuffer[metaIndex + antennaIndex] /= factor; for (DBufferRow &row : data) { unsigned count = 0; if (row.antenna1 == antennaIndex) ++count; if (row.antenna2 == antennaIndex) ++count; for (unsigned repeat = 0; repeat != count; ++repeat) { for (size_t i = 0; i != _nChannels; ++i) row.visibilities[i * _nPol + polIndex] *= factor; } } } // // There are 3 axes to maximize over; antenna1, antenna2, channel // We want to maximize all values (average abs value as large as possible) // Example: (10=max) // ch1 ch2 // a1a2a3 a1a2a3 // ------ ------ // 1 1 1 1 1 1 // 10 1 1 1 1 1 // 1 1 1 1 1 1 // ant3 * 10 -> sum = (3 * 1 + 10 + 5 * 10) + (4 * 1 + 5 * 10) = 117 // ch2 * 10 -> sum = 18 + 90 = 108 // both * s10 -> sum = (13 + 5*s10) + (s10*4 + 10*5) = 91.5 // ch1 ch2 // 1 1 1 1 1 1 // 1 1 1 1 1 1 // 1 2 1 1 1.5 1 // ant3 * 10 -> sum = (3 * 1 + 10 + 5 * 10) + (4 * 1 + 5 * 10) = 117 // ch2 * 10 -> sum = 18 + 90 = 108 // both * s10 -> sum = (13 + 5*s10) + (s10*4 + 10*5) = 91.5 // // Approach: iterate over all antenna and channels, and find the antenna/channel // that can increase the sum the most. void AFTimeBlockEncoder::fitToMaximum( std::vector &data, float *metaBuffer, const dyscostman::StochasticEncoder &gausEncoder, size_t antennaCount) { // First, the channels and polarizations are scaled such that the maximum // value equals the maximum encodable value const size_t visPerRow = _nPol * _nChannels; for (size_t visIndex = 0; visIndex != visPerRow; ++visIndex) { double largest_component = 0.0; for (const DBufferRow &row : data) { if (row.antenna1 != row.antenna2) { const std::complex *ptr = &row.visibilities[visIndex]; double local_max = std::max(std::max(ptr->real(), ptr->imag()), -std::min(ptr->real(), ptr->imag())); if (std::isfinite(local_max) && local_max > largest_component) largest_component = local_max; } } const double factor = (gausEncoder.MaxQuantity() == 0.0 || largest_component == 0.0) ? 1.0 : gausEncoder.MaxQuantity() / largest_component; changeChannelFactor(data, metaBuffer, visIndex, factor); } for (size_t polIndex = 0; polIndex != _nPol; ++polIndex) { bool isProgressing; do { // Find the factor that increasest the sum of absolute values the most double bestChannelIncrease = 0.0, channelFactor = 1.0; size_t bestChannel = 0; for (size_t channel = 0; channel != _nChannels; ++channel) { // By how much can we increase this channel? double largest_component = 0.0; for (const DBufferRow &row : data) { if (row.antenna1 != row.antenna2) { const std::complex *ptr = &row.visibilities[channel * _nPol + polIndex]; double local_max = std::max(std::max(ptr->real(), ptr->imag()), -std::min(ptr->real(), ptr->imag())); if (std::isfinite(local_max) && local_max > largest_component) largest_component = local_max; } } double factor = (largest_component == 0.0) ? 0.0 : (gausEncoder.MaxQuantity() / largest_component - 1.0); // How much does this increase the total? double thisIncrease = 0.0; for (DBufferRow &row : data) { if (row.antenna1 != row.antenna2) { std::complex v = row.visibilities[channel * _nPol + polIndex] * double(factor); const double absoluteValue = std::fabs(v.real()) + std::fabs(v.imag()); if (std::isfinite(absoluteValue)) thisIncrease += absoluteValue; } } if (thisIncrease > bestChannelIncrease) { bestChannelIncrease = thisIncrease; bestChannel = channel; channelFactor = factor + 1.0; } } ao::uvector maxCompPerAntenna(antennaCount, 0.0); for (const DBufferRow &row : data) { if (row.antenna1 != row.antenna2) { for (size_t channel = 0; channel != _nChannels; ++channel) { const std::complex *ptr = &row.visibilities[channel * _nPol + polIndex]; double complMax = std::max(std::max(ptr->real(), ptr->imag()), -std::min(ptr->real(), ptr->imag())); if (std::isfinite(complMax)) { if (complMax > maxCompPerAntenna[row.antenna1]) maxCompPerAntenna[row.antenna1] = complMax; if (complMax > maxCompPerAntenna[row.antenna2]) maxCompPerAntenna[row.antenna2] = complMax; } } } } ao::uvector increasePerAntenna(antennaCount, 0.0); for (const DBufferRow &row : data) { if (row.antenna1 != row.antenna2) { double factor1 = (maxCompPerAntenna[row.antenna1] == 0.0) ? 0.0 : (gausEncoder.MaxQuantity() / maxCompPerAntenna[row.antenna1] - 1.0); double factor2 = (maxCompPerAntenna[row.antenna2] == 0.0) ? 0.0 : (gausEncoder.MaxQuantity() / maxCompPerAntenna[row.antenna2] - 1.0); for (size_t channel = 0; channel != _nChannels; ++channel) { std::complex v1 = row.visibilities[channel * _nPol + polIndex] * factor1; double av1 = std::fabs(v1.real()) + std::fabs(v1.imag()); if (std::isfinite(av1)) increasePerAntenna[row.antenna1] += av1; std::complex v2 = row.visibilities[channel * _nPol + polIndex] * factor2; double av2 = std::fabs(v2.real()) + std::fabs(v2.imag()); if (std::isfinite(av2)) increasePerAntenna[row.antenna2] += av2; } } } size_t bestAntenna = 0; double bestAntennaIncrease = 0.0; for (size_t a = 0; a != antennaCount; ++a) { if (increasePerAntenna[a] > bestAntennaIncrease) { bestAntennaIncrease = increasePerAntenna[a]; bestAntenna = a; } } // The benefit was calculated for increasing an antenna and increasing a // channel. Select which of those two has the largest benefit and apply: if (bestAntennaIncrease > bestChannelIncrease) { double factor = (maxCompPerAntenna[bestAntenna] == 0.0) ? 1.0 : (gausEncoder.MaxQuantity() / maxCompPerAntenna[bestAntenna]); if (factor < 1.0) isProgressing = false; else { isProgressing = factor > 1.01; changeAntennaFactor(data, metaBuffer, bestAntenna, antennaCount, polIndex, factor); } } else { if (channelFactor < 1.0) { isProgressing = false; } else { isProgressing = channelFactor > 1.001; changeChannelFactor(data, metaBuffer, bestChannel * _nPol + polIndex, channelFactor); } } } while (isProgressing); } } template void AFTimeBlockEncoder::encode( const dyscostman::StochasticEncoder &gausEncoder, const TimeBlockBuffer> &buffer, float *metaBuffer, symbol_t *symbolBuffer, size_t antennaCount, std::mt19937 *rnd) { if (_rmsPerAntenna.size() < antennaCount) _rmsPerAntenna.resize(antennaCount); // Note that encoding is performed with doubles std::vector data; buffer.ConvertVector>(data); const size_t visPerRow = _nPol * _nChannels; // Normalize the RMS of the channels std::vector channelRMSes(_nChannels * _nPol); for (const DBufferRow &row : data) { for (size_t i = 0; i != visPerRow; ++i) { channelRMSes[i].Include(row.visibilities[i]); } } for (DBufferRow &row : data) { for (size_t i = 0; i != visPerRow; ++i) { double rms = channelRMSes[i].RMS(); if (rms != 0.0) { row.visibilities[i] /= rms; } metaBuffer[i] = rms; } } for (size_t p = 0; p != _nPol; ++p) { // Normalize the RMS of the antennae calculateAntennaeRMS(data, p, antennaCount); for (DBufferRow &row : data) { double mul = (_rmsPerAntenna[row.antenna1] * _rmsPerAntenna[row.antenna2]); double fac = (mul == 0.0) ? 0.0 : 1.0 / mul; for (size_t ch = 0; ch != _nChannels; ++ch) { row.visibilities[ch * _nPol + p] *= fac; } } size_t metaIndex = visPerRow + antennaCount * p; for (size_t a = 0; a != antennaCount; ++a) metaBuffer[metaIndex + a] = _rmsPerAntenna[a]; } if (_fitToMaximum) { fitToMaximum(data, metaBuffer, gausEncoder, antennaCount); } symbol_t *symbolBufferPtr = symbolBuffer; for (const DBufferRow &row : data) { for (size_t i = 0; i != visPerRow; ++i) { if (UseDithering) { symbolBufferPtr[i * 2] = gausEncoder.EncodeWithDithering( row.visibilities[i].real(), _ditherDist(*rnd)); symbolBufferPtr[i * 2 + 1] = gausEncoder.EncodeWithDithering( row.visibilities[i].imag(), _ditherDist(*rnd)); } else { symbolBufferPtr[i * 2] = gausEncoder.Encode(row.visibilities[i].real()); symbolBufferPtr[i * 2 + 1] = gausEncoder.Encode(row.visibilities[i].imag()); } } symbolBufferPtr += visPerRow * 2; } } template void AFTimeBlockEncoder::encode( const dyscostman::StochasticEncoder &gausEncoder, const TimeBlockBuffer> &buffer, float *metaBuffer, symbol_t *symbolBuffer, size_t antennaCount, std::mt19937 *rnd); template void AFTimeBlockEncoder::encode( const dyscostman::StochasticEncoder &gausEncoder, const TimeBlockBuffer> &buffer, float *metaBuffer, symbol_t *symbolBuffer, size_t antennaCount, std::mt19937 *rnd); void AFTimeBlockEncoder::calculateAntennaeRMS( const std::vector &data, size_t polIndex, size_t antennaCount) { std::vector matrixMeas(antennaCount * antennaCount); for (const DBufferRow &row : data) { size_t a1 = row.antenna1, a2 = row.antenna2; if (a1 != a2) { if (a1 > a2) std::swap(a1, a2); const size_t index = a1 * antennaCount + a2; for (size_t ch = 0; ch != _nChannels; ++ch) matrixMeas[index].Include(row.visibilities[ch * _nPol + polIndex]); } } ao::uvector matrix(matrixMeas.size()); for (size_t i = 0; i != antennaCount; ++i) { for (size_t j = i; j != antennaCount; ++j) { matrix[i * antennaCount + j] = matrixMeas[i * antennaCount + j].RMS(); matrix[j * antennaCount + i] = matrixMeas[i * antennaCount + j].RMS(); } } // (note that _rmsPerAntenna is larger, so don't use assign()) for (size_t i = 0; i != antennaCount; ++i) _rmsPerAntenna[i] = 1.0; double precision = 1.0; for (size_t iteration = 0; iteration != 100 && precision > 1e-6; ++iteration) { ao::uvector nextRMS(antennaCount, 0.0); for (size_t i = 0; i != antennaCount; ++i) { double weightSum = 0.0; for (size_t j = 0; j != antennaCount; ++j) { if (i != j && _rmsPerAntenna[j] != 0.0) { double w = _rmsPerAntenna[j]; if (std::isfinite(matrix[i * antennaCount + j])) { // matrix / estVec, but since we weight, just: nextRMS[i] += matrix[i * antennaCount + j]; weightSum += w; } } } if (weightSum == 0.0) nextRMS[i] = 0.0; else nextRMS[i] /= weightSum; } double maxVal = 0.0; for (size_t i = 0; i != antennaCount; ++i) { _rmsPerAntenna[i] = nextRMS[i] * 0.8 + _rmsPerAntenna[i] * 0.2; maxVal = std::max(_rmsPerAntenna[i], maxVal); } precision = 0.0; for (size_t i = 0; i != antennaCount; ++i) { if (_rmsPerAntenna[i] < maxVal * 1e-5) _rmsPerAntenna[i] = 0.0; precision = std::max(precision, std::fabs(_rmsPerAntenna[i] - nextRMS[i]) / maxVal); } } } void AFTimeBlockEncoder::InitializeDecode(const float *metaBuffer, size_t /*nRow*/, size_t nAntennae) { if (_rmsPerAntenna.size() < nAntennae * _nPol) _rmsPerAntenna.resize(nAntennae * _nPol); const size_t visPerRow = _nPol * _nChannels; for (size_t i = 0; i != visPerRow; ++i) _rmsPerChannel[i] = metaBuffer[i]; metaBuffer += visPerRow; for (size_t p = 0; p != _nPol; ++p) { for (size_t a = 0; a != nAntennae; ++a) _rmsPerAntenna[a * _nPol + p] = metaBuffer[a + p * nAntennae]; } } void AFTimeBlockEncoder::Decode( const dyscostman::StochasticEncoder &gausEncoder, FBuffer &buffer, const AFTimeBlockEncoder::symbol_t *symbolBuffer, size_t blockRow, size_t antenna1, size_t antenna2) { ao::uvector antFactors(_nPol); for (size_t p = 0; p != _nPol; ++p) antFactors[p] = _rmsPerAntenna[antenna1 * _nPol + p] * _rmsPerAntenna[antenna2 * _nPol + p]; FBufferRow &row = buffer[blockRow]; row.antenna1 = antenna1; row.antenna2 = antenna2; row.visibilities.resize(_nChannels * _nPol); std::complex *destination = row.visibilities.data(); const symbol_t *srcRowPtr = symbolBuffer + blockRow * SymbolsPerRow(); for (size_t ch = 0; ch != _nChannels; ++ch) { for (size_t p = 0; p != _nPol; ++p) { double chRMS = _rmsPerChannel[ch * _nPol + p]; double factor = chRMS * antFactors[p]; destination->real(double(gausEncoder.Decode(*srcRowPtr)) * factor); ++srcRowPtr; destination->imag(double(gausEncoder.Decode(*srcRowPtr)) * factor); ++srcRowPtr; ++destination; } } } casacore-3.7.1/tables/Dysco/aftimeblockencoder.h000066400000000000000000000062131476623553700216520ustar00rootroot00000000000000#ifndef DYSCO_AFTIME_BLOCK_ENCODER_H #define DYSCO_AFTIME_BLOCK_ENCODER_H #include "stochasticencoder.h" #include "timeblockbuffer.h" #include "uvector.h" #include #include #include #include "timeblockencoder.h" class AFTimeBlockEncoder : public TimeBlockEncoder { public: AFTimeBlockEncoder(size_t nPol, size_t nChannels, bool fitToMaximum); virtual ~AFTimeBlockEncoder() override; virtual void EncodeWithDithering( const dyscostman::StochasticEncoder &gausEncoder, FBuffer &buffer, float *metaBuffer, symbol_t *symbolBuffer, size_t antennaCount, std::mt19937 &rnd) final override { encode(gausEncoder, buffer, metaBuffer, symbolBuffer, antennaCount, &rnd); } virtual void EncodeWithoutDithering( const dyscostman::StochasticEncoder &gausEncoder, FBuffer &buffer, float *metaBuffer, symbol_t *symbolBuffer, size_t antennaCount) final override { encode(gausEncoder, buffer, metaBuffer, symbolBuffer, antennaCount, 0); } virtual void InitializeDecode(const float *metaBuffer, size_t nRow, size_t nAntennae) final override; virtual void Decode(const dyscostman::StochasticEncoder &gausEncoder, FBuffer &buffer, const symbol_t *symbolBuffer, size_t blockRow, size_t antenna1, size_t antenna2) final override; virtual size_t SymbolCount(size_t nRow, size_t nPol, size_t nChannels) const final override { return nRow * nChannels * nPol * 2 /*complex*/; } virtual size_t SymbolCount(size_t nRow) const final override { return nRow * _nChannels * _nPol * 2 /*complex*/; } virtual size_t SymbolsPerRow() const final override { return _nChannels * _nPol * 2 /*complex*/; } virtual size_t MetaDataCount(size_t /*nRow*/, size_t nPol, size_t nChannels, size_t nAntennae) const final override { return nPol * (nChannels + nAntennae); } void Normalize(const dyscostman::StochasticEncoder &gausEncoder, TimeBlockBuffer> &buffer, size_t antennaCount); private: void calculateAntennaeRMS(const std::vector &data, size_t polIndex, size_t antennaCount); template void encode(const dyscostman::StochasticEncoder &gausEncoder, const FBuffer &buffer, float *metaBuffer, symbol_t *symbolBuffer, size_t antennaCount, std::mt19937 *rnd); void changeAntennaFactor(std::vector &data, float *metaBuffer, size_t antennaIndex, size_t antennaCount, size_t polIndex, double factor); void fitToMaximum(std::vector &data, float *metaBuffer, const dyscostman::StochasticEncoder &gausEncoder, size_t antennaCount); size_t _nPol, _nChannels; bool _fitToMaximum; ao::uvector _rmsPerChannel, _rmsPerAntenna; std::uniform_int_distribution _ditherDist; }; #endif casacore-3.7.1/tables/Dysco/bytepacker.h000066400000000000000000000572251476623553700201740ustar00rootroot00000000000000#ifndef DYSCO_BYTE_PACKER_H #define DYSCO_BYTE_PACKER_H #include #include namespace dyscostman { /** * Class for bit packing of values into bytes. * * Contains several methods that can pack and unpack an array of unsigned * values into a bit-packed array, using as few bytes as possible. * The number of bits used is fixed for all values in the array. All methods * assume that the specified output array has at least enough space to store * the packed / unpacked data. When packing, the amount of space needed is * ceil(symbolCount / bitCount). * * The @ref pack() and @ref unpack() methods can call the method with given * bitcount at runtime. If the bitcount is known at compile time, one of the * other methods can be used. For each of these calls, the input symbols are * assumed to occupy at most the given number of bits. The number of bytes * written during pack operations is ceil(symbolCount * bitCount / 8). * unpack operations will write symbolCount symbols into the output buffer. */ class BytePacker { public: /** * Call a pack..() function for a given bit count. Will forward the pack * operation to the one for the given bit count. * @param bitCount the number of bits to use per symbol * @param dest output buffer (see class desc for size) * @param symbolBuffer the input buffer * @param symbolCount number of symbols in @p symbolBuffer. */ static void pack(unsigned bitCount, unsigned char *dest, const unsigned *symbolBuffer, size_t symbolCount); /** * Call an unpack..() function for a given bit count. Will forward the unpack * operation to the one for the given bit count. * @param bitCount the number of bits used per symbol * @param symbolBuffer output buffer * @param packedBuffer the input buffer with the packed symbols * @param symbolCount number of symbols that will be unpacked into @p * symbolBuffer. */ static void unpack(unsigned bitCount, unsigned *symbolBuffer, unsigned char *packedBuffer, size_t symbolCount); /** * Pack the symbols from symbolBuffer into the destination array using * bitCount=2. */ static void pack2(unsigned char *dest, const unsigned *symbolBuffer, size_t symbolCount); /** * Reverse of pack2(). Will write symbolCount items into the symbolBuffer. */ static void unpack2(unsigned *symbolBuffer, unsigned char *packedBuffer, size_t symbolCount); /** * Pack the symbols from symbolBuffer into the destination array using * bitCount=3. */ static void pack3(unsigned char *dest, const unsigned *symbolBuffer, size_t symbolCount); /** * Reverse of pack3(). Will write symbolCount items into the symbolBuffer. */ static void unpack3(unsigned *symbolBuffer, unsigned char *packedBuffer, size_t symbolCount); /** * Pack the symbols from symbolBuffer into the destination array using * bitCount=4. */ static void pack4(unsigned char *dest, const unsigned *symbolBuffer, size_t symbolCount); /** * Reverse of pack4(). Will write symbolCount items into the symbolBuffer. */ static void unpack4(unsigned *symbolBuffer, unsigned char *packedBuffer, size_t symbolCount); /** * Pack the symbols from symbolBuffer into the destination array using * bitCount=6. */ static void pack6(unsigned char *dest, const unsigned *symbolBuffer, size_t symbolCount); /** * Reverse of pack6(). Will write symbolCount items into the symbolBuffer. */ static void unpack6(unsigned *symbolBuffer, unsigned char *packedBuffer, size_t symbolCount); /** * Pack the symbols from symbolBuffer into the destination array using * bitCount=8. */ static void pack8(unsigned char *dest, const unsigned *symbolBuffer, size_t symbolCount); /** * Reverse of pack8(). Will write symbolCount items into the symbolBuffer. */ static void unpack8(unsigned *symbolBuffer, unsigned char *packedBuffer, size_t symbolCount); /** * Pack the symbols from symbolBuffer into the destination array using * bitCount=10. */ static void pack10(unsigned char *dest, const unsigned *symbolBuffer, size_t symbolCount); /** * Reverse of pack10(). Will write symbolCount items into the symbolBuffer. */ static void unpack10(unsigned *symbolBuffer, unsigned char *packedBuffer, size_t symbolCount); /** * Pack the symbols from symbolBuffer into the destination array using * bitCount=12. */ static void pack12(unsigned char *dest, const unsigned *symbolBuffer, size_t symbolCount); /** * Reverse of pack12(). Will write symbolCount items into the symbolBuffer. */ static void unpack12(unsigned *symbolBuffer, unsigned char *packedBuffer, size_t symbolCount); /** * Pack the symbols from symbolBuffer into the destination array using * bitCount=16. */ static void pack16(unsigned char *dest, const unsigned *symbolBuffer, size_t symbolCount); /** * Reverse of pack16(). Will write symbolCount items into the symbolBuffer. */ static void unpack16(unsigned *symbolBuffer, unsigned char *packedBuffer, size_t symbolCount); static size_t bufferSize(size_t nSymbols, size_t nBits) { return (nSymbols * nBits + 7) / 8; } }; inline void BytePacker::pack(unsigned int bitCount, unsigned char *dest, const unsigned int *symbolBuffer, size_t symbolCount) { switch (bitCount) { case 2: pack2(dest, symbolBuffer, symbolCount); break; case 3: pack3(dest, symbolBuffer, symbolCount); break; case 4: pack4(dest, symbolBuffer, symbolCount); break; case 6: pack6(dest, symbolBuffer, symbolCount); break; case 8: pack8(dest, symbolBuffer, symbolCount); break; case 10: pack10(dest, symbolBuffer, symbolCount); break; case 12: pack12(dest, symbolBuffer, symbolCount); break; case 16: pack16(dest, symbolBuffer, symbolCount); break; default: throw std::runtime_error("Unsupported packing size"); } } inline void BytePacker::unpack(unsigned int bitCount, unsigned int *symbolBuffer, unsigned char *packedBuffer, size_t symbolCount) { switch (bitCount) { case 2: unpack2(symbolBuffer, packedBuffer, symbolCount); break; case 3: unpack3(symbolBuffer, packedBuffer, symbolCount); break; case 4: unpack4(symbolBuffer, packedBuffer, symbolCount); break; case 6: unpack6(symbolBuffer, packedBuffer, symbolCount); break; case 8: unpack8(symbolBuffer, packedBuffer, symbolCount); break; case 10: unpack10(symbolBuffer, packedBuffer, symbolCount); break; case 12: unpack12(symbolBuffer, packedBuffer, symbolCount); break; case 16: unpack16(symbolBuffer, packedBuffer, symbolCount); break; default: throw std::runtime_error("Unsupported unpacking size"); } } inline void BytePacker::pack2(unsigned char *dest, const unsigned int *symbolBuffer, size_t symbolCount) { const size_t limit = symbolCount / 4; for (size_t i = 0; i != limit; i++) { *dest = (*symbolBuffer); // bits 1-2 into 1-2 ++symbolBuffer; *dest |= (*symbolBuffer) << 2; // bits 1-2 into 3-4 ++symbolBuffer; *dest |= (*symbolBuffer) << 4; // bits 1-2 into 5-6 ++symbolBuffer; *dest |= (*symbolBuffer) << 6; // bits 1-2 into 7-8 ++symbolBuffer; ++dest; } size_t pos = limit * 4; if (pos != symbolCount) { *dest = (*symbolBuffer); // bits 1-2 into 1-2 ++pos; if (pos != symbolCount) { ++symbolBuffer; *dest |= (*symbolBuffer) << 2; // bits 1-2 into 3-4 ++pos; if (pos != symbolCount) { ++symbolBuffer; *dest |= (*symbolBuffer) << 4; // bits 1-2 into 5-6 } } } } inline void BytePacker::unpack2(unsigned *symbolBuffer, unsigned char *packedBuffer, size_t symbolCount) { const size_t limit = symbolCount / 4; for (size_t i = 0; i != limit; i++) { *symbolBuffer = *packedBuffer & 0x03; // bits 1-2 into 1-2 ++symbolBuffer; *symbolBuffer = ((*packedBuffer) & 0x0C) >> 2; // bits 3-4 into 1-2 ++symbolBuffer; *symbolBuffer = ((*packedBuffer) & 0x30) >> 4; // bits 5-6 into 1-2 ++symbolBuffer; *symbolBuffer = ((*packedBuffer) & 0xC0) >> 6; // bits 7-8 into 1-2 ++symbolBuffer; ++packedBuffer; } size_t pos = limit * 4; if (pos != symbolCount) { *symbolBuffer = *packedBuffer & 0x03; // bits 1-2 into 1-2 ++pos; if (pos != symbolCount) { ++symbolBuffer; *symbolBuffer = ((*packedBuffer) & 0x0C) >> 2; // bits 3-4 into 1-2 ++pos; if (pos != symbolCount) { ++symbolBuffer; *symbolBuffer = ((*packedBuffer) & 0x30) >> 4; // bits 5-6 into 1-2 } } } } inline void BytePacker::pack3(unsigned char *dest, const unsigned *symbolBuffer, size_t symbolCount) { const size_t limit = symbolCount / 8; for (size_t i = 0; i != limit; i++) { *dest = *symbolBuffer; // 1. Bit 1-3 into 1-3 ++symbolBuffer; *dest |= (*symbolBuffer) << 3; // 2. Bits 1-3 into 4-6 ++symbolBuffer; *dest |= ((*symbolBuffer) & 0x3) << 6; // 3. Bits 1-2 into 7-8 ++dest; *dest = ((*symbolBuffer) & 0x4) >> 2; // 3b. Bit 3 into 1 ++symbolBuffer; *dest |= (*symbolBuffer) << 1; // 4. Bits 1-3 into 2-4 ++symbolBuffer; *dest |= (*symbolBuffer) << 4; // 5. Bits 1-3 into 5-7 ++symbolBuffer; *dest |= ((*symbolBuffer) & 0x1) << 7; // 6. Bit 1 into 8 ++dest; *dest = ((*symbolBuffer) & 0x6) >> 1; // 6b. Bits 2-3 into 1-2 ++symbolBuffer; *dest |= (*symbolBuffer) << 2; // 7. Bits 1-3 into bits 3-5 ++symbolBuffer; *dest |= (*symbolBuffer) << 5; // 8. Bits 1-3 into bits 6-8 ++symbolBuffer; ++dest; } size_t pos = limit * 8; if (pos != symbolCount) { *dest = *symbolBuffer; // 1. Bit 1-3 into 1-3 ++pos; if (pos != symbolCount) { ++symbolBuffer; *dest |= (*symbolBuffer) << 3; // 2. Bits 1-3 into 4-6 ++pos; if (pos != symbolCount) { ++symbolBuffer; *dest |= ((*symbolBuffer) & 0x3) << 6; // 3. Bits 1-2 into 7-8 ++dest; *dest = ((*symbolBuffer) & 0x4) >> 2; // Bit 3 into 1 ++pos; if (pos != symbolCount) { ++symbolBuffer; *dest |= (*symbolBuffer) << 1; // 4. Bits 1-3 into 2-4 ++pos; if (pos != symbolCount) { ++symbolBuffer; *dest |= (*symbolBuffer) << 4; // 5. Bits 1-3 into 5-7 ++pos; if (pos != symbolCount) { ++symbolBuffer; *dest |= ((*symbolBuffer) & 0x1) << 7; // 6. Bit 1 into 8 ++dest; *dest = ((*symbolBuffer) & 0x6) >> 1; // Bits 2-3 into 1-2 ++pos; if (pos != symbolCount) { ++symbolBuffer; *dest |= (*symbolBuffer) << 2; // 7. Bits 1-3 into bits 3-5 } } } } } } } } inline void BytePacker::unpack3(unsigned *symbolBuffer, unsigned char *packedBuffer, size_t symbolCount) { const size_t limit = symbolCount / 8; for (size_t i = 0; i != limit; i++) { *symbolBuffer = (*packedBuffer) & 0x07; // 1. Bits 1-3 into 1-3 ++symbolBuffer; *symbolBuffer = ((*packedBuffer) & 0x38) >> 3; // 2. Bits 4-6 into 1-3 ++symbolBuffer; *symbolBuffer = ((*packedBuffer) & 0xC0) >> 6; // 3. Bits 7-8 into 1-2 ++packedBuffer; *symbolBuffer |= ((*packedBuffer) & 0x01) << 2; // 3b. Bit 1 into 3 ++symbolBuffer; *symbolBuffer = ((*packedBuffer) & 0x0e) >> 1; // 4. Bit 2-4 into 1-3 ++symbolBuffer; *symbolBuffer = ((*packedBuffer) & 0x70) >> 4; // 5. Bit 5-7 into 1-3 ++symbolBuffer; *symbolBuffer = ((*packedBuffer) & 0x80) >> 7; // 6. Bit 8 into 1 ++packedBuffer; *symbolBuffer |= ((*packedBuffer) & 0x03) << 1; // 6b. Bit 1-2 into 2-3 ++symbolBuffer; *symbolBuffer = ((*packedBuffer) & 0x1c) >> 2; // 7. Bit 3-5 into 1-3 ++symbolBuffer; *symbolBuffer = ((*packedBuffer) & 0xe0) >> 5; // 8. Bit 6-8 into 1-3 ++symbolBuffer; ++packedBuffer; } size_t pos = limit * 8; if (pos != symbolCount) { *symbolBuffer = (*packedBuffer) & 0x07; // 1. Bits 1-3 into 1-3 ++pos; if (pos != symbolCount) { ++symbolBuffer; *symbolBuffer = ((*packedBuffer) & 0x38) >> 3; // 2. Bits 4-6 into 1-3 ++pos; if (pos != symbolCount) { ++symbolBuffer; *symbolBuffer = ((*packedBuffer) & 0xC0) >> 6; // 3. Bits 7-8 into 1-2 ++packedBuffer; *symbolBuffer |= ((*packedBuffer) & 0x01) << 2; // 3b. Bit 1 into 3 ++pos; if (pos != symbolCount) { ++symbolBuffer; *symbolBuffer = ((*packedBuffer) & 0x0e) >> 1; // 4. Bit 2-4 into 1-3 ++pos; if (pos != symbolCount) { ++symbolBuffer; *symbolBuffer = ((*packedBuffer) & 0x70) >> 4; // 5. Bit 5-7 into 1-3 ++pos; if (pos != symbolCount) { ++symbolBuffer; *symbolBuffer = ((*packedBuffer) & 0x80) >> 7; // 6. Bit 8 into 1 ++packedBuffer; *symbolBuffer |= ((*packedBuffer) & 0x03) << 1; // 6b. Bit 1-2 into 2-3 ++pos; if (pos != symbolCount) { ++symbolBuffer; *symbolBuffer = ((*packedBuffer) & 0x1c) >> 2; // 7. Bit 3-5 into 1-3 } } } } } } } } inline void BytePacker::pack4(unsigned char *dest, const unsigned int *symbolBuffer, size_t symbolCount) { const size_t limit = symbolCount / 2; for (size_t i = 0; i != limit; i++) { *dest = (*symbolBuffer); // bits 1-4 into 1-4 ++symbolBuffer; *dest |= (*symbolBuffer) << 4; // bits 1-4 into 5-8 ++symbolBuffer; ++dest; } if (limit * 2 != symbolCount) *dest = (*symbolBuffer); // bits 1-4 into 1-4 } inline void BytePacker::unpack4(unsigned *symbolBuffer, unsigned char *packedBuffer, size_t symbolCount) { const size_t limit = symbolCount / 2; for (size_t i = 0; i != limit; i++) { *symbolBuffer = *packedBuffer & 0x0F; // bits 1-4 into 1-4 ++symbolBuffer; *symbolBuffer = ((*packedBuffer) & 0xF0) >> 4; // bits 5-8 into 1-4 ++symbolBuffer; ++packedBuffer; } if (limit * 2 != symbolCount) *symbolBuffer = *packedBuffer & 0x0F; // bits 1-4 into 1-4 } inline void BytePacker::pack6(unsigned char *dest, const unsigned *symbolBuffer, size_t symbolCount) { const size_t limit = symbolCount / 4; for (size_t i = 0; i != limit; i++) { *dest = *symbolBuffer; // Bit 1-6 into 1-6 ++symbolBuffer; *dest |= (*symbolBuffer & 3) << 6; // Bits 1-2 into 7-8 ++dest; *dest = (*symbolBuffer & 60) >> 2; // Bits 3-6 into 1-4 ++symbolBuffer; *dest |= (*symbolBuffer & 15) << 4; // Bits 1-4 into 5-8 ++dest; *dest = (*symbolBuffer & 48) >> 4; // Bits 5-6 into 1-2 ++symbolBuffer; *dest |= *symbolBuffer << 2; // Bits 1-6 into bits 3-8 ++symbolBuffer; ++dest; } size_t pos = limit * 4; if (pos != symbolCount) { *dest = *symbolBuffer; // Bit 1-6 into 1-6 ++pos; if (pos != symbolCount) { ++symbolBuffer; *dest |= (*symbolBuffer & 3) << 6; // Bits 1-2 into 7-8 ++dest; *dest = (*symbolBuffer & 60) >> 2; // Bits 3-6 into 1-4 ++pos; if (pos != symbolCount) { ++symbolBuffer; *dest |= (*symbolBuffer & 15) << 4; // Bits 1-4 into 5-8 ++dest; *dest = (*symbolBuffer & 48) >> 4; // Bits 5-6 into 1-2 //++symbolBuffer; ++pos; } } } } inline void BytePacker::unpack6(unsigned *symbolBuffer, unsigned char *packedBuffer, size_t symbolCount) { const size_t limit = symbolCount / 4; for (size_t i = 0; i != limit; i++) { *symbolBuffer = (*packedBuffer) & 63; // Bits 1-6 into 1-6 ++symbolBuffer; *symbolBuffer = ((*packedBuffer) & 192) >> 6; // Bits 7-8 into 1-2 ++packedBuffer; *symbolBuffer |= ((*packedBuffer) & 15) << 2; // Bits 1-4 into 3-6 ++symbolBuffer; *symbolBuffer = ((*packedBuffer) & 240) >> 4; // Bits 5-8 into 1-4 ++packedBuffer; *symbolBuffer |= ((*packedBuffer) & 3) << 4; // Bits 1-2 into 5-6 ++symbolBuffer; *symbolBuffer = ((*packedBuffer) & 252) >> 2; // Bits 3-8 into 1-6 ++packedBuffer; ++symbolBuffer; } size_t pos = limit * 4; if (pos != symbolCount) { *symbolBuffer = (*packedBuffer) & 63; // Bits 1-6 into 1-6 ++pos; if (pos != symbolCount) { ++symbolBuffer; *symbolBuffer = ((*packedBuffer) & 192) >> 6; // Bits 7-8 into 1-2 ++packedBuffer; *symbolBuffer |= ((*packedBuffer) & 15) << 2; // Bits 1-4 into 3-6 ++pos; if (pos != symbolCount) { ++symbolBuffer; *symbolBuffer = ((*packedBuffer) & 240) >> 4; // Bits 5-8 into 1-4 ++packedBuffer; *symbolBuffer |= ((*packedBuffer) & 3) << 4; // Bits 1-2 into 5-6 } } } } inline void BytePacker::pack8(unsigned char *dest, const unsigned *symbolBuffer, size_t symbolCount) { for (size_t i = 0; i != symbolCount; ++i) dest[i] = symbolBuffer[i]; } inline void BytePacker::unpack8(unsigned *symbolBuffer, unsigned char *packedBuffer, size_t symbolCount) { for (size_t i = 0; i != symbolCount; ++i) symbolBuffer[i] = packedBuffer[i]; } inline void BytePacker::pack10(unsigned char *dest, const unsigned int *symbolBuffer, size_t symbolCount) { const size_t limit = symbolCount / 4; for (size_t i = 0; i != limit; i++) { *dest = (*symbolBuffer & 0x0FF); // Bit 1-8 into 1-8 ++dest; *dest = (*symbolBuffer & 0x300) >> 8; // Bits 9-10 into 1-2 ++symbolBuffer; *dest |= (*symbolBuffer & 0x03F) << 2; // Bits 1-6 into 3-8 ++dest; *dest = (*symbolBuffer & 0x3C0) >> 6; // Bits 7-10 into 1-4 ++symbolBuffer; *dest |= (*symbolBuffer & 0x00F) << 4; // Bits 1-4 into 5-8 ++dest; *dest = (*symbolBuffer & 0x3F0) >> 4; // Bits 5-10 into bits 1-6 ++symbolBuffer; *dest |= (*symbolBuffer & 0x003) << 6; // Bits 1-2 into 7-8 ++dest; *dest = (*symbolBuffer & 0x3FC) >> 2; // Bits 3-10 into bits 1-8 ++symbolBuffer; ++dest; } size_t pos = limit * 4; if (pos != symbolCount) { *dest = (*symbolBuffer & 0x0FF); // Bit 1-8 into 1-8 ++dest; *dest = (*symbolBuffer & 0x300) >> 8; // Bits 9-10 into 1-2 ++pos; if (pos != symbolCount) { ++symbolBuffer; *dest |= (*symbolBuffer & 0x03F) << 2; // Bits 1-6 into 3-8 ++dest; *dest = (*symbolBuffer & 0x3C0) >> 6; // Bits 7-10 into 1-4 ++pos; if (pos != symbolCount) { ++symbolBuffer; *dest |= (*symbolBuffer & 0x00F) << 4; // Bits 1-4 into 5-8 ++dest; *dest = (*symbolBuffer & 0x3F0) >> 4; // Bits 5-10 into bits 1-6 //++symbolBuffer; ++pos; } } } } inline void BytePacker::unpack10(unsigned int *symbolBuffer, unsigned char *packedBuffer, size_t symbolCount) { const size_t limit = symbolCount / 4; for (size_t i = 0; i != limit; i++) { *symbolBuffer = *packedBuffer; // Bits 1-8 into 1-8 ++packedBuffer; *symbolBuffer |= ((*packedBuffer) & 0x03) << 8; // Bits 1-2 into 9-10 ++symbolBuffer; *symbolBuffer = ((*packedBuffer) & 0xFC) >> 2; // Bits 3-8 into 1-6 ++packedBuffer; *symbolBuffer |= ((*packedBuffer) & 0x0F) << 6; // Bits 1-4 into 7-10 ++symbolBuffer; *symbolBuffer = ((*packedBuffer) & 0xF0) >> 4; // Bits 5-8 into 1-4 ++packedBuffer; *symbolBuffer |= ((*packedBuffer) & 0x3F) << 4; // Bits 1-6 into 5-10 ++symbolBuffer; *symbolBuffer = ((*packedBuffer) & 0xC0) >> 6; // Bits 7-8 into 1-2 ++packedBuffer; *symbolBuffer |= (*packedBuffer) << 2; // Bits 1-8 into 3-10 ++packedBuffer; ++symbolBuffer; } size_t pos = limit * 4; if (pos != symbolCount) { *symbolBuffer = *packedBuffer; // Bits 1-8 into 1-8 ++packedBuffer; *symbolBuffer |= ((*packedBuffer) & 0x03) << 8; // Bits 1-2 into 9-10 ++pos; if (pos != symbolCount) { ++symbolBuffer; *symbolBuffer = ((*packedBuffer) & 0xFC) >> 2; // Bits 3-8 into 1-6 ++packedBuffer; *symbolBuffer |= ((*packedBuffer) & 0x0F) << 6; // Bits 1-4 into 7-10 ++pos; if (pos != symbolCount) { ++symbolBuffer; *symbolBuffer = ((*packedBuffer) & 0xF0) >> 4; // Bits 5-8 into 1-4 ++packedBuffer; *symbolBuffer |= ((*packedBuffer) & 0x3F) << 4; // Bits 1-6 into 5-10 } } } } inline void BytePacker::pack12(unsigned char *dest, const unsigned int *symbolBuffer, size_t symbolCount) { const size_t limit = symbolCount / 2; for (size_t i = 0; i != limit; i++) { *dest = (*symbolBuffer) & 0x0FF; // bits 1-8 into 1-8 ++dest; *dest = ((*symbolBuffer) & 0xF00) >> 8; // bits 9-12 into 1-4 ++symbolBuffer; *dest |= ((*symbolBuffer) & 0x00F) << 4; // bits 1-4 into 5-8 ++dest; *dest = ((*symbolBuffer) & 0xFF0) >> 4; // bits 5-12 into 1-8 ++symbolBuffer; ++dest; } if (limit * 2 != symbolCount) { *dest = (*symbolBuffer) & 0x0FF; // bits 1-8 into 1-8 ++dest; *dest = ((*symbolBuffer) & 0xF00) >> 8; // bits 9-12 into 1-4 } } inline void BytePacker::unpack12(unsigned int *symbolBuffer, unsigned char *packedBuffer, size_t symbolCount) { const size_t limit = symbolCount / 2; for (size_t i = 0; i != limit; i++) { *symbolBuffer = *packedBuffer; // bits 1-8 into 1-8 ++packedBuffer; *symbolBuffer |= ((*packedBuffer) & 0x0F) << 8; // bits 1-4 into 9-12 ++symbolBuffer; *symbolBuffer = ((*packedBuffer) & 0xF0) >> 4; // bits 5-8 into 1-4 ++packedBuffer; *symbolBuffer |= ((*packedBuffer) & 0xFF) << 4; // bits 1-8 into 5-12 ++packedBuffer; ++symbolBuffer; } if (limit * 2 != symbolCount) { *symbolBuffer = *packedBuffer; // bits 1-8 into 1-8 ++packedBuffer; *symbolBuffer |= ((*packedBuffer) & 0x0F) << 8; // bits 1-4 into 9-12 } } inline void BytePacker::pack16(unsigned char *dest, const unsigned *symbolBuffer, size_t symbolCount) { for (size_t i = 0; i != symbolCount; ++i) reinterpret_cast(dest)[i] = symbolBuffer[i]; } inline void BytePacker::unpack16(unsigned *symbolBuffer, unsigned char *packedBuffer, size_t symbolCount) { for (size_t i = 0; i != symbolCount; ++i) symbolBuffer[i] = reinterpret_cast(packedBuffer)[i]; } } // namespace dyscostman #endif casacore-3.7.1/tables/Dysco/dyscodatacolumn.cc000066400000000000000000000105711476623553700213630ustar00rootroot00000000000000#include "dyscodatacolumn.h" #include "aftimeblockencoder.h" #include "rftimeblockencoder.h" #include "rowtimeblockencoder.h" namespace dyscostman { void DyscoDataColumn::Prepare(DyscoDistribution distribution, Normalization normalization, double studentsTNu, double distributionTruncation) { _distribution = distribution; _studentsTNu = studentsTNu; _normalization = normalization; ThreadedDyscoColumn::Prepare(distribution, normalization, studentsTNu, distributionTruncation); const size_t nPolarizations = shape()[0], nChannels = shape()[1]; switch (normalization) { case Normalization::kAF: _decoder.reset(new AFTimeBlockEncoder(nPolarizations, nChannels, true)); break; case Normalization::kRF: _decoder.reset(new RFTimeBlockEncoder(nPolarizations, nChannels)); break; case Normalization::kRow: _decoder.reset(new RowTimeBlockEncoder(nPolarizations, nChannels)); break; } switch (distribution) { case GaussianDistribution: _gausEncoder.reset( new StochasticEncoder(1 << getBitsPerSymbol(), 1.0, true)); break; case UniformDistribution: _gausEncoder.reset( new StochasticEncoder(1 << getBitsPerSymbol(), 1.0, false)); break; case StudentsTDistribution: _gausEncoder.reset(new StochasticEncoder( StochasticEncoder::StudentTEncoder(1 << getBitsPerSymbol(), studentsTNu, 1.0))); break; case TruncatedGaussianDistribution: _gausEncoder.reset(new StochasticEncoder( StochasticEncoder::TruncatedGausEncoder( 1 << getBitsPerSymbol(), distributionTruncation, 1.0))); break; } } void DyscoDataColumn::initializeDecode(TimeBlockBuffer * /*buffer*/, const float *metaBuffer, size_t nRow, size_t nAntennae) { _decoder->InitializeDecode(metaBuffer, nRow, nAntennae); } void DyscoDataColumn::decode(TimeBlockBuffer *buffer, const unsigned int *data, size_t blockRow, size_t a1, size_t a2) { _decoder->Decode(*_gausEncoder, *buffer, data, blockRow, a1, a2); } std::unique_ptr>::ThreadDataBase> DyscoDataColumn::initializeEncodeThread() { const size_t nPolarizations = shape()[0], nChannels = shape()[1]; std::unique_ptr encoder; switch (_normalization) { case Normalization::kAF: encoder.reset(new AFTimeBlockEncoder(nPolarizations, nChannels, true)); break; case Normalization::kRF: encoder.reset(new RFTimeBlockEncoder(nPolarizations, nChannels)); break; case Normalization::kRow: encoder.reset(new RowTimeBlockEncoder(nPolarizations, nChannels)); break; } std::unique_ptr newThreadData(new ThreadData(std::move(encoder))); // Seed every thread from a random number if (_randomize) newThreadData->rnd.seed(_rnd()); else std::cout << "Warning: New thread NOT seeded.\n"; return newThreadData; } void DyscoDataColumn::encode(ThreadDataBase *threadData, TimeBlockBuffer *buffer, float *metaBuffer, symbol_t *symbolBuffer, size_t nAntennae) { ThreadData &data = static_cast(*threadData); data.encoder->EncodeWithDithering(*_gausEncoder, *buffer, metaBuffer, symbolBuffer, nAntennae, data.rnd); } size_t DyscoDataColumn::metaDataFloatCount(size_t nRows, size_t nPolarizations, size_t nChannels, size_t nAntennae) const { return _decoder->MetaDataCount(nRows, nPolarizations, nChannels, nAntennae); } size_t DyscoDataColumn::symbolCount(size_t nRowsInBlock, size_t nPolarizations, size_t nChannels) const { return _decoder->SymbolCount(nRowsInBlock, nPolarizations, nChannels); } size_t DyscoDataColumn::defaultThreadCount() const { if (!_randomize) { std::cout << "Warning: using only one thread to avoid randomizing the results.\n"; return 1; } else { return ThreadedDyscoColumn::defaultThreadCount(); } } } // namespace dyscostman casacore-3.7.1/tables/Dysco/dyscodatacolumn.h000066400000000000000000000055341476623553700212300ustar00rootroot00000000000000#ifndef DYSCO_DATA_COLUMN_H #define DYSCO_DATA_COLUMN_H #include "threadeddyscocolumn.h" #include "stochasticencoder.h" #include "timeblockencoder.h" namespace dyscostman { class DyscoStMan; /** * A column for storing compressed complex values with an approximate Gaussian * distribution. * @author André Offringa */ class DyscoDataColumn final : public ThreadedDyscoColumn> { public: /** * Create a new column. Internally called by DyscoStMan when creating a * new column. */ DyscoDataColumn(DyscoStMan *parent, int dtype) : ThreadedDyscoColumn(parent, dtype), _rnd(std::random_device{}()), _gausEncoder(), _distribution(GaussianDistribution), _normalization(Normalization::kRF), _randomize(true) {} DyscoDataColumn(const DyscoDataColumn &source) = delete; void operator=(const DyscoDataColumn &source) = delete; /** Destructor. */ virtual ~DyscoDataColumn() { shutdown(); } virtual void Prepare(DyscoDistribution distribution, Normalization normalization, double studentsTNu, double distributionTruncation) override; void SetStaticRandomizationSeed() { std::cout << "Warning: Initializing random number generator with static seed!\n"; _rnd = std::mt19937(); _randomize = false; } protected: virtual void initializeDecode(TimeBlockBuffer *buffer, const float *metaBuffer, size_t nRow, size_t nAntennae) override; virtual void decode(TimeBlockBuffer *buffer, const symbol_t *data, size_t blockRow, size_t a1, size_t a2) override; virtual std::unique_ptr initializeEncodeThread() override; virtual void encode(ThreadDataBase *threadData, TimeBlockBuffer *buffer, float *metaBuffer, symbol_t *symbolBuffer, size_t nAntennae) override; virtual size_t metaDataFloatCount(size_t nRow, size_t nPolarizations, size_t nChannels, size_t nAntennae) const override; virtual size_t symbolCount(size_t nRowsInBlock, size_t nPolarizations, size_t nChannels) const override; virtual size_t defaultThreadCount() const override; private: struct ThreadData final : public ThreadDataBase { ThreadData(std::unique_ptr timeBlockEncoder) : encoder(std::move(timeBlockEncoder)) {} std::unique_ptr encoder; std::mt19937 rnd; }; std::mt19937 _rnd; std::unique_ptr> _gausEncoder; std::unique_ptr _decoder; DyscoDistribution _distribution; Normalization _normalization; double _studentsTNu; bool _randomize; }; } // namespace dyscostman #endif casacore-3.7.1/tables/Dysco/dyscodistribution.h000066400000000000000000000003401476623553700216060ustar00rootroot00000000000000#ifndef DYSCO_DISTRIBUTION_H #define DYSCO_DISTRIBUTION_H namespace dyscostman { enum DyscoDistribution { GaussianDistribution, UniformDistribution, StudentsTDistribution, TruncatedGaussianDistribution }; } #endif casacore-3.7.1/tables/Dysco/dysconormalization.h000066400000000000000000000002131476623553700217540ustar00rootroot00000000000000#ifndef DYSCO_NORMALIZATION_H #define DYSCO_NORMALIZATION_H namespace dyscostman { enum class Normalization { kAF, kRF, kRow }; } #endif casacore-3.7.1/tables/Dysco/dyscostman.cc000066400000000000000000000355651476623553700203700ustar00rootroot00000000000000#include "dyscostman.h" #include "dyscodatacolumn.h" #include "dyscostmancol.h" #include "dyscostmanerror.h" #include "dyscoweightcolumn.h" #include "header.h" void register_dyscostman() { dyscostman::DyscoStMan::registerClass(); } namespace dyscostman { const unsigned short DyscoStMan::VERSION_MAJOR = 1, DyscoStMan::VERSION_MINOR = 0; DyscoStMan::DyscoStMan(unsigned dataBitCount, unsigned weightBitCount, const casacore::String &name) : DataManager(), _nRow(0), _nBlocksInFile(0), _rowsPerBlock(0), _antennaCount(0), _blockSize(0), _headerSize(0), _name(name), _dataBitCount(dataBitCount), _weightBitCount(weightBitCount), _distribution(TruncatedGaussianDistribution), _normalization(Normalization::kAF), _studentTNu(0.0), _distributionTruncation(2.5), _staticSeed(false) {} DyscoStMan::DyscoStMan(const casacore::String &name, const casacore::Record &spec) : DataManager(), _nRow(0), _nBlocksInFile(0), _rowsPerBlock(0), _antennaCount(0), _blockSize(0), _headerSize(0), _name(name), _dataBitCount(0), _weightBitCount(0), _distribution(GaussianDistribution), _normalization(Normalization::kAF), _studentTNu(0.0), _distributionTruncation(0.0), _staticSeed(false) { setFromSpec(spec); } DyscoStMan::DyscoStMan(const DyscoStMan &source) : DataManager(), _nRow(0), _nBlocksInFile(0), _rowsPerBlock(0), _antennaCount(0), _blockSize(0), _headerSize(0), _name(source._name), _dataBitCount(source._dataBitCount), _weightBitCount(source._weightBitCount), _distribution(source._distribution), _normalization(source._normalization), _studentTNu(source._studentTNu), _distributionTruncation(source._distributionTruncation), _staticSeed(source._staticSeed) {} void DyscoStMan::setFromSpec(const casacore::Record &spec) { // Here we need to load from _spec int i = spec.description().fieldNumber("dataBitCount"); if (i >= 0) { _dataBitCount = spec.asInt("dataBitCount"); if (_dataBitCount == 0) throw DyscoStManError("Invalid error for data bit rate"); _weightBitCount = spec.asInt("weightBitCount"); if (_weightBitCount == 0) throw DyscoStManError("Invalid error for weight bit rate"); std::string str = spec.asString("distribution"); if (str == "Uniform") _distribution = UniformDistribution; else if (str == "Gaussian") _distribution = GaussianDistribution; else if (str == "StudentT") _distribution = StudentsTDistribution; else if (str == "TruncatedGaussian") _distribution = TruncatedGaussianDistribution; else throw DyscoStManError("Unsupported distribution specified"); str = spec.asString("normalization"); if (str == "RF") _normalization = Normalization::kRF; else if (str == "AF") _normalization = Normalization::kAF; else if (str == "Row") _normalization = Normalization::kRow; else throw DyscoStManError("Unsupported normalization specified"); if (spec.description().fieldNumber("studentTNu") >= 0) _studentTNu = spec.asDouble("studentTNu"); else _studentTNu = 0.0; _distributionTruncation = spec.asDouble("distributionTruncation"); } } void DyscoStMan::makeEmpty() { for (std::unique_ptr &col : _columns) { col->shutdown(); } _columns.clear(); } DyscoStMan::~DyscoStMan() { makeEmpty(); } casacore::Record DyscoStMan::dataManagerSpec() const { casacore::Record spec; spec.define("dataBitCount", _dataBitCount); spec.define("weightBitCount", _weightBitCount); std::string distStr; switch (_distribution) { case GaussianDistribution: distStr = "Gaussian"; break; case UniformDistribution: distStr = "Uniform"; break; case StudentsTDistribution: distStr = "StudentsT"; break; case TruncatedGaussianDistribution: distStr = "TruncatedGaussian"; break; } spec.define("distribution", distStr); std::string normStr; switch (_normalization) { case Normalization::kAF: normStr = "AF"; break; case Normalization::kRF: normStr = "RF"; break; case Normalization::kRow: normStr = "Row"; break; } spec.define("normalization", normStr); spec.define("studentTNu", _studentTNu); spec.define("distributionTruncation", _distributionTruncation); return spec; } void DyscoStMan::registerClass() { DataManager::registerCtor("DyscoStMan", makeObject); } casacore::Bool DyscoStMan::flush(casacore::AipsIO &, casacore::Bool /*doFsync*/) { return false; } void DyscoStMan::create64(casacore::rownr_t nRow) { _nRow = nRow; _fStream.reset(new std::fstream( fileName().c_str(), std::ios_base::in | std::ios_base::out | std::ios_base::trunc)); if (_fStream->fail()) throw DyscoStManError("I/O error: could not create new file '" + fileName() + "'"); _nBlocksInFile = 0; } void DyscoStMan::writeHeader() { _fStream->seekp(0, std::ios_base::beg); Header header; header.columnCount = _columns.size(); header.storageManagerName = _name; header.rowsPerBlock = _rowsPerBlock; header.antennaCount = _antennaCount; header.blockSize = _blockSize; header.versionMajor = VERSION_MAJOR; header.versionMinor = VERSION_MINOR; header.dataBitCount = _dataBitCount; header.weightBitCount = _weightBitCount; header.distribution = _distribution; header.normalization = static_cast(_normalization); header.studentTNu = _studentTNu; header.distributionTruncation = _distributionTruncation; header.columnHeaderOffset = header.calculateColumnHeaderOffset(); _headerSize = header.columnHeaderOffset; for (std::unique_ptr &col : _columns) _headerSize += sizeof(GenericColumnHeader) + col->ExtraHeaderSize(); header.headerSize = _headerSize; header.Serialize(*_fStream); for (std::unique_ptr &col : _columns) { GenericColumnHeader cHeader; cHeader.columnHeaderSize = cHeader.calculateSize() + col->ExtraHeaderSize(); cHeader.Serialize(*_fStream); col->SerializeExtraHeader(*_fStream); } if (_fStream->fail()) throw DyscoStManError("I/O error: could not write to file"); } void DyscoStMan::readHeader() { Header header; _fStream->seekg(0, std::ios_base::beg); header.Unserialize(*_fStream); if (_fStream->fail()) throw DyscoStManError("I/O error: could not read file '" + fileName() + "' -- is the file corrupted?"); _headerSize = header.headerSize; size_t curColumnHeaderOffset = header.columnHeaderOffset; size_t columnCount = header.columnCount; _name = header.storageManagerName; _dataBitCount = header.dataBitCount; _weightBitCount = header.weightBitCount; _distribution = (enum DyscoDistribution)header.distribution; _normalization = (enum Normalization)header.normalization; _studentTNu = header.studentTNu; _distributionTruncation = header.distributionTruncation; _rowsPerBlock = header.rowsPerBlock; _antennaCount = header.antennaCount; _blockSize = header.blockSize; if (header.versionMajor != 1 || header.versionMinor != 0) { std::stringstream s; s << "The compressed file has file format version " << header.versionMajor << "." << header.versionMinor << ", but this version of Dysco can only open file format version 1.0. " "Upgrade Dysco.\n"; throw DyscoStManError(s.str()); } if (columnCount != _columns.size()) { std::stringstream s; s << "The column count in the DyscoStMan file (" << columnCount << ") does not match with the measurement set (" << _columns.size() << ")"; throw DyscoStManError(s.str()); } for (size_t i = 0; i != _columns.size(); ++i) { DyscoStManColumn &col = *_columns[i]; GenericColumnHeader cHeader; _fStream->seekg(curColumnHeaderOffset, std::ios_base::beg); cHeader.Unserialize(*_fStream); col.UnserializeExtraHeader(*_fStream); curColumnHeaderOffset += cHeader.columnHeaderSize; } } void DyscoStMan::initializeRowsPerBlock(size_t rowsPerBlock, size_t antennaCount, bool writeToHeader) { if (areOffsetsInitialized() && (rowsPerBlock != _rowsPerBlock || antennaCount != _antennaCount)) throw DyscoStManError( "initializeRowsPerBlock() called with two different " "values; something is wrong"); _rowsPerBlock = rowsPerBlock; _antennaCount = antennaCount; _blockSize = 0; for (std::unique_ptr &col : _columns) { size_t columnBlockSize = col->CalculateBlockSize(rowsPerBlock, antennaCount); col->SetOffsetInBlock(_blockSize); _blockSize += columnBlockSize; col->InitializeAfterNRowsPerBlockIsKnown(); } if (writeToHeader) writeHeader(); } casacore::rownr_t DyscoStMan::open64(casacore::rownr_t nRow, casacore::AipsIO &) { _nRow = nRow; _fStream.reset(new std::fstream(fileName().c_str(), std::ios_base::in | std::ios_base::out)); if (_fStream->fail()) { _fStream.reset(new std::fstream(fileName().c_str(), std::ios_base::in)); if (_fStream->fail()) throw DyscoStManError("I/O error: could not open file '" + fileName() + "', which should be an existing file"); } readHeader(); _fStream->seekg(0, std::ios_base::end); if (_fStream->fail()) throw DyscoStManError("I/O error: error reading file '" + fileName()); std::streampos size = _fStream->tellg(); if (size > _headerSize) _nBlocksInFile = (size_t(size) - _headerSize) / _blockSize; else _nBlocksInFile = 0; return nRow; } casacore::DataManagerColumn *DyscoStMan::makeScalarColumn( const casacore::String & /*name*/, int dataType, const casacore::String &dataTypeID) { std::ostringstream s; s << "Can not create scalar columns with DyscoStMan! (requested datatype: '" << dataTypeID << "' (" << dataType << ")"; throw DyscoStManError(s.str()); } casacore::DataManagerColumn *DyscoStMan::makeDirArrColumn( const casacore::String &name, int dataType, const casacore::String & /*dataTypeID*/) { std::unique_ptr col; if (name == "WEIGHT_SPECTRUM") { if (dataType == casacore::TpFloat) col.reset(new DyscoWeightColumn(this, dataType)); else throw DyscoStManError( "Trying to create a Dysco weight column with wrong type"); } else if (dataType == casacore::TpComplex) { col.reset(new DyscoDataColumn(this, dataType)); if (_staticSeed) static_cast(*col).SetStaticRandomizationSeed(); } else throw DyscoStManError( "Trying to create a Dysco data column with wrong type"); _columns.push_back(std::move(col)); return _columns.back().get(); } casacore::DataManagerColumn *DyscoStMan::makeIndArrColumn( const casacore::String & /*name*/, int /*dataType*/, const casacore::String & /*dataTypeID*/) { throw DyscoStManError( "makeIndArrColumn() called on DyscoStMan. DyscoStMan can only created " "direct columns!\nUse casacore::ColumnDesc::Direct as option in your " "column desc constructor"); } casacore::rownr_t DyscoStMan::resync64(casacore::rownr_t nRow) { return nRow; } void DyscoStMan::deleteManager() { unlink(fileName().c_str()); } void DyscoStMan::prepare() { std::lock_guard lock(_mutex); if (_dataBitCount == 0 || _weightBitCount == 0) throw DyscoStManError( "One of the required parameters of the DyscoStMan was not " "set!\nDyscoStMan was not correctly initialized by your program."); for (std::unique_ptr &col : _columns) { DyscoDataColumn *dataCol = dynamic_cast(col.get()); if (dataCol) dataCol->SetBitsPerSymbol(_dataBitCount); else { DyscoWeightColumn *wghtCol = dynamic_cast(col.get()); if (wghtCol) wghtCol->SetBitsPerSymbol(_weightBitCount); } col->Prepare(_distribution, _normalization, _studentTNu, _distributionTruncation); } // In case this is a new measurement set, we do not know the rowsPerBlock yet // If this measurement set is opened, we do know it, and we have to call // initializeRowsPerBlock() to let the columns know this value. if (areOffsetsInitialized()) initializeRowsPerBlock(_rowsPerBlock, _antennaCount, false); } void DyscoStMan::reopenRW() {} void DyscoStMan::addRow64(casacore::rownr_t nrrow) { _nRow += nrrow; } void DyscoStMan::removeRow64(casacore::rownr_t rowNr) { if (rowNr != _nRow - 1) throw DyscoStManError( "Trying to remove a row in the middle of the file: " "the DyscoStMan does not support this"); _nRow--; } void DyscoStMan::addColumn(casacore::DataManagerColumn * /*column*/) { if (_nBlocksInFile != 0) throw DyscoStManError( "Can't add columns while data has been committed to table"); prepare(); writeHeader(); } void DyscoStMan::removeColumn(casacore::DataManagerColumn *column) { for (std::vector>::iterator i = _columns.begin(); i != _columns.end(); ++i) { if (i->get() == column) { _columns.erase(i); writeHeader(); return; } } throw DyscoStManError( "Trying to remove column that was not part of the storage manager"); } void DyscoStMan::readCompressedData(size_t blockIndex, const DyscoStManColumn *column, unsigned char *dest, size_t size) { std::lock_guard lock(_mutex); size_t fileOffset = getFileOffset(blockIndex); size_t columnOffset = column->OffsetInBlock(); _fStream->seekg(fileOffset + columnOffset, std::ios_base::beg); _fStream->read(reinterpret_cast(dest), size); if (_fStream->fail()) { // This can be sort of ok ; row exists because other columns have written // here, but no data had been written yet for this column if (blockIndex + 1 != _nBlocksInFile) throw DyscoStManError("I/O error: error while reading file '" + fileName() + "'"); _fStream->clear(); // reset fail bit } } void DyscoStMan::writeCompressedData(size_t blockIndex, const DyscoStManColumn *column, const unsigned char *data, size_t size) { std::lock_guard lock(_mutex); if (_nBlocksInFile <= blockIndex) { _nBlocksInFile = blockIndex + 1; } size_t fileOffset = getFileOffset(blockIndex); _fStream->seekp(fileOffset + column->OffsetInBlock(), std::ios_base::beg); _fStream->write(reinterpret_cast(data), size); if (_fStream->fail()) throw DyscoStManError("I/O error: error while writing file '" + fileName() + "'"); } } // namespace dyscostman casacore-3.7.1/tables/Dysco/dyscostman.h000066400000000000000000000335701476623553700202240ustar00rootroot00000000000000#ifndef DYSCO_STORAGE_MANAGER_H #define DYSCO_STORAGE_MANAGER_H #include #include #include #include #include #include #include #include "dyscodistribution.h" #include "dysconormalization.h" #include "threadgroup.h" #include "uvector.h" /** * @file * Contains DyscoStMan and its global register function * register_dyscostman(). * * @defgroup Globals Global functions * Contains the register_dyscostman() function. */ #ifndef DOXYGEN_SHOULD_SKIP_THIS extern "C" { #endif void register_dyscostman(); #ifndef DOXYGEN_SHOULD_SKIP_THIS } #endif /** * @author André Offringa */ namespace dyscostman { class DyscoStManColumn; /** * The main class for the Dysco storage manager. */ class DyscoStMan : public casacore::DataManager { public: /** * Convenience constructor to create a new storage manager with some settings * without having to fill a 'spec' Record. The storage manager will be * initialized to AF normalization with a truncated Gaussian distribution for * the quantization, and a truncation of sigma = 2.5. To change the settings, * use one of the Set...Distribution() methods and SetNormalization(). * @param dataBitRate The number of bits per float used for visibilities. * @param weightBitRate The number of bits per float used for the weight * column. * @param name Storage manager name. */ DyscoStMan(unsigned dataBitRate, unsigned weightBitRate, const casacore::String &name = "DyscoStMan"); /** * Initialize the storage manager to use a Gaussian distribution for the * quantization. This method should only be called directly after creating * DyscoStMan, before adding columns, and reading/writing data. * * In tests with MWA and LOFAR data, the Gaussian distribution showed lesser * compression accuracy compared to the truncated Gaussian and uniform * distributions. * @see SetUniformDistribution(), SetTruncatedGaussianDistribution() */ void SetGaussianDistribution() { _distribution = GaussianDistribution; } /** * Initialize the storage manager to use a Uniform distribution for the * quantization (i.e., use a linear quantizer). This method should only be * called directly after creating DyscoStMan, before adding columns, and * reading/writing data. * * In tests with MWA and LOFAR data, the Uniform distribution showed very good * results, only the truncated Gaussian distribution showed better results for * some cases. */ void SetUniformDistribution() { _distribution = UniformDistribution; } /** * Initialize the storage manager to use a Student T distribution for the * quantization (i.e., use a linear quantizer). This method should only be * called directly after creating DyscoStMan, before adding columns, and * reading/writing data. * * The Student T distribution performed not very well on test sets, and was * mainly added for testing. */ void SetStudentsTDistribution(double nu) { _distribution = StudentsTDistribution; _studentTNu = nu; } /** * Initialize the storage manager to use a Uniform distribution for the * quantization (i.e., use a linear quantizer). This method should only be * called directly after creating DyscoStMan, before adding columns, and * reading/writing data. * * In tests with MWA and LOFAR data, the truncated Gaussian distribution with * a sigma of 1.5 to 2.5 is the recommended distribution. * @param truncationSigma At which point the distribution is truncated. Good * values are 1.5 to 2.5. */ void SetTruncatedGaussianDistribution(double truncationSigma) { _distribution = TruncatedGaussianDistribution; _distributionTruncation = truncationSigma; } /** * Set the type of normalization. * This method should only be called directly after creating DyscoStMan, * before adding columns, and reading/writing data. */ void SetNormalization(Normalization normalization) { _normalization = normalization; } void SetStaticSeed(bool staticSeed) { _staticSeed = staticSeed; } /** * This constructor is called by Casa when it needs to create a DyscoStMan. * Casa will call makeObject() that will call this constructor. * When it loads an DyscoStMan for an existing MS, the "spec" parameter * will be empty, thus the class should initialize its properties * by reading them from the file. * The @p spec is used to make a new storage manager with specs similar to * another one. * @param name Name of this storage manager. * @param spec Specs to initialize this class with. */ DyscoStMan(const casacore::String &name, const casacore::Record &spec); /** * Copy constructor that initializes a storage manager with similar specs. * The columns are not copied: the new manager will be empty. */ DyscoStMan(const DyscoStMan &source); /** Destructor. */ ~DyscoStMan(); /** Assignment -- new dyscostman takes the settings of the source (but not the * columns and/or data). * @param source Source manager. */ DyscoStMan &operator=(const DyscoStMan &source) = delete; /** Polymorphical copy constructor, equal to DyscoStMan(const DyscoStMan&). * @returns Empty manager with specs as the source. */ virtual casacore::DataManager *clone() const final override { return new DyscoStMan(*this); } /** Type of manager * @returns "DyscoStMan". */ virtual casacore::String dataManagerType() const final override { return "DyscoStMan"; } /** Returns the name of this manager as specified during construction. */ virtual casacore::String dataManagerName() const final override { return _name; } /** Get manager specifications. Includes method settings, etc. Can be used * to make a second storage manager with * @returns Record containing data manager specifications. */ virtual casacore::Record dataManagerSpec() const final override; /** * Get the number of rows in the measurement set. * @returns Number of rows in the measurement set. */ uint getNRow() const { return _nRow; } /** * Whether rows can be added. * @returns @c true */ virtual casacore::Bool canAddRow() const final override { return true; } /** * Whether rows can be removed. * @returns @c true (but only rows at the end can actually be removed) */ virtual casacore::Bool canRemoveRow() const final override { return true; } /** * Whether columns can be added. * @returns @c true (but restrictions apply; columns can only be added as long * as no writes have been performed on the set). */ virtual casacore::Bool canAddColumn() const final override { return true; } /** * Whether columns can be removed. * @return @c true (but restrictions apply -- still to be checked) * @todo Describe restrictons */ virtual casacore::Bool canRemoveColumn() const final override { return true; } /** * Create an object with given name and spec. * This methods gets registered in the DataManager "constructor" map. * The caller has to delete the object. New class will be * initialized via @ref DyscoStMan(const casacore::String& name, const * casacore::Record& spec). * @returns A DyscoStMan with given specs. */ static casacore::DataManager *makeObject(const casacore::String &name, const casacore::Record &spec) { return new DyscoStMan(name, spec); } /** * This function makes the DyscoStMan known to casacore. The function * is necessary for loading the storage manager from a shared library. It * should have this specific name ("register_" + storage manager's name in * lowercase) to be able to be automatically called when the library is * loaded. That function will forward the * call here. */ static void registerClass(); protected: /** * The number of rows that are actually stored in the file. * This method is synchronized (i.e., thread-safe). */ uint64_t nBlocksInFile() const { std::lock_guard lock(_mutex); return _nBlocksInFile; } /** * Number of rows in one "time-block", i.e. a sequence of rows that * belong to the same timestep, spw and field. * This value is only available after a first time block was written * (see areOffsetsInitialized()). * @returns Number of measurement set rows in one time block. */ size_t nRowsInBlock() const { return _rowsPerBlock; } /** * Number of antennae used in a time block. This does not have to be equal * to the number of antennae stored in the measurement set. * This value is only available after a first time block was written * (see areOffsetsInitialized()). * @returns Number of antennae. */ size_t nAntennae() const { return _antennaCount; } /** * Return index of block that contains the given measurement set row. * This can only be calculated after a first time block was written * (see areOffsetsInitialized()). * @param row A measurement set row. * @returns Block index. */ size_t getBlockIndex(uint64_t row) const { return row / _rowsPerBlock; } /** * Return the offset of the row within the block. * This can only be calculated after a first time block was written * (see areOffsetsInitialized()). * @see getBlockIndex(). * @param row A measurement set row. * @returns offset of row within block. */ size_t getRowWithinBlock(uint64_t row) const { return row % _rowsPerBlock; } /** * Calculate first measurement set row index of a given block index. * @param block A block index * @returns First measurement set row index of given block. */ uint64_t getRowIndex(size_t block) const { return uint64_t(block) * uint64_t(_rowsPerBlock); } /** * This method returns @c true when the number of rows per block and the * number of antennae per block are known. This is only the case once the * first time- block was written to the file. * @returns True when the nr of rows per block and antennae are available. */ bool areOffsetsInitialized() const { return _rowsPerBlock != 0; } /** * To be called by a column once it determines rowsPerBlock and antennaCount. * @param rowsPerBlock Number of measurement set rows in one time block. * @param antennaCount Highest antenna index+1 used in a time block. * @param writeToHeader Write the header? */ void initializeRowsPerBlock(size_t rowsPerBlock, size_t antennaCount, bool writeToHeader); private: friend class DyscoStManColumn; const static unsigned short VERSION_MAJOR, VERSION_MINOR; void readCompressedData(size_t blockIndex, const DyscoStManColumn *column, unsigned char *dest, size_t size); void writeCompressedData(size_t blockIndex, const DyscoStManColumn *column, const unsigned char *data, size_t size); void readHeader(); void writeHeader(); void makeEmpty(); void setFromSpec(const casacore::Record &spec); size_t getFileOffset(size_t blockIndex) const { return _blockSize * blockIndex + _headerSize; } // Flush and optionally fsync the data. // The AipsIO stream represents the main table file and can be // used by virtual column engines to store SMALL amounts of data. virtual casacore::Bool flush(casacore::AipsIO &, casacore::Bool doFsync) final override; // Let the storage manager create files as needed for a new table. // This allows a column with an indirect array to create its file. virtual void create64(casacore::rownr_t nRow) final override; // Open the storage manager file for an existing table. // Return the number of rows in the data file. virtual casacore::rownr_t open64(casacore::rownr_t nRow, casacore::AipsIO &) final override; // Create a column in the storage manager on behalf of a table column. // The caller will NOT delete the newly created object. // Create a scalar column. virtual casacore::DataManagerColumn *makeScalarColumn( const casacore::String &name, int dataType, const casacore::String &dataTypeID) final override; // Create a direct array column. virtual casacore::DataManagerColumn *makeDirArrColumn( const casacore::String &name, int dataType, const casacore::String &dataTypeID) final override; // Create an indirect array column. virtual casacore::DataManagerColumn *makeIndArrColumn( const casacore::String &name, int dataType, const casacore::String &dataTypeID) final override; virtual casacore::rownr_t resync64(casacore::rownr_t nRow) final override; virtual void deleteManager() final override; // Prepare the columns, let the data manager initialize itself further. // Prepare is called after create/open has been called for all // columns. In this way one can be sure that referenced columns // are read back and partly initialized. virtual void prepare() final override; // Reopen the storage manager files for read/write. virtual void reopenRW() final override; // Add rows to the storage manager. virtual void addRow64(casacore::rownr_t nrrow) final override; // Delete a row from all columns. virtual void removeRow64(casacore::rownr_t rowNr) final override; // Do the final addition of a column. virtual void addColumn(casacore::DataManagerColumn *) final override; // Remove a column from the data file. virtual void removeColumn(casacore::DataManagerColumn *) final override; uint64_t _nRow; uint64_t _nBlocksInFile; uint32_t _rowsPerBlock; uint32_t _antennaCount; uint32_t _blockSize; unsigned _headerSize; mutable std::mutex _mutex; std::unique_ptr _fStream; std::string _name; unsigned _dataBitCount; unsigned _weightBitCount; DyscoDistribution _distribution; Normalization _normalization; double _studentTNu, _distributionTruncation; bool _staticSeed; std::vector> _columns; }; } // namespace dyscostman #endif casacore-3.7.1/tables/Dysco/dyscostmancol.h000066400000000000000000000116651476623553700207230ustar00rootroot00000000000000#ifndef DYSCO_STORAGE_MAN_COLUMN_H #define DYSCO_STORAGE_MAN_COLUMN_H #include "dyscodistribution.h" #include "dysconormalization.h" #include #include #include #include namespace dyscostman { class DyscoStMan; /** * Base class for columns of the DyscoStMan. * @author André Dysco */ class DyscoStManColumn : public casacore::StManColumnBase { public: /** * Constructor, to be overloaded by subclass. * @param parent The parent stman to which this column belongs. * @param dtype The column's type as defined by Casacore. */ explicit DyscoStManColumn(DyscoStMan *parent, int dtype) : casacore::StManColumnBase(dtype), _offsetInBlock(0), _storageManager(parent) {} /** Destructor */ virtual ~DyscoStManColumn() {} /** To be called before destructing the class. */ virtual void shutdown() = 0; /** * Whether this column is writable * @returns @c true */ virtual casacore::Bool isWritable() const override { return true; } virtual void Prepare(DyscoDistribution distribution, Normalization normalization, double studentsTNu, double distributionTruncation) = 0; virtual void InitializeAfterNRowsPerBlockIsKnown() = 0; virtual size_t CalculateBlockSize(size_t nRowsInBlock, size_t nAntennae) const = 0; /** * Get number of bytes needed for column header of this column. This is * excluding the generic column header. * @returns Size of column header */ virtual size_t ExtraHeaderSize() const { return 0; } virtual void SerializeExtraHeader(std::ostream &stream) const = 0; virtual void UnserializeExtraHeader(std::istream &stream) = 0; size_t OffsetInBlock() const { return _offsetInBlock; } void SetOffsetInBlock(size_t offsetInBlock) { _offsetInBlock = offsetInBlock; } protected: /** Get the storage manager for this column */ DyscoStMan &storageManager() const { return *_storageManager; } /** * Read a row of compressed data from the stman file. * @param blockIndex The block index of the row to read. * @param dest The destination buffer, should be at least of size Stride(). * @param size The nr of bytes to be read. */ void readCompressedData(size_t blockIndex, unsigned char *dest, size_t size); /** * Write a row of compressed data to the stman file. * @param blockIndex The block index of the row to write. * @param data The data buffer containing Stride() bytes. * @param size The nr of bytes to be written. */ void writeCompressedData(size_t blockIndex, const unsigned char *data, size_t size); /** * Get the actual number of blocks in the file. */ uint64_t nBlocksInFile() const; size_t getBlockIndex(uint64_t row) const; size_t getRowWithinBlock(uint64_t row) const; size_t nRowsInBlock() const; size_t nAntennae() const; uint64_t getRowIndex(size_t block) const; bool areOffsetsInitialized() const; void initializeRowsPerBlock(size_t rowsPerBlock, size_t antennaCount); private: DyscoStManColumn(const DyscoStManColumn &source) = delete; void operator=(const DyscoStManColumn &source) = delete; size_t _offsetInBlock; DyscoStMan *_storageManager; }; } // namespace dyscostman #include "dyscostman.h" namespace dyscostman { inline void DyscoStManColumn::readCompressedData(size_t blockIndex, unsigned char *dest, size_t size) { _storageManager->readCompressedData(blockIndex, this, dest, size); } inline void DyscoStManColumn::writeCompressedData(size_t blockIndex, const unsigned char *data, size_t size) { _storageManager->writeCompressedData(blockIndex, this, data, size); } inline uint64_t DyscoStManColumn::nBlocksInFile() const { return _storageManager->nBlocksInFile(); } inline size_t DyscoStManColumn::getBlockIndex(uint64_t row) const { return _storageManager->getBlockIndex(row); } inline size_t DyscoStManColumn::nRowsInBlock() const { return _storageManager->nRowsInBlock(); } inline size_t DyscoStManColumn::nAntennae() const { return _storageManager->nAntennae(); } inline uint64_t DyscoStManColumn::getRowIndex(size_t block) const { return _storageManager->getRowIndex(block); } inline size_t DyscoStManColumn::getRowWithinBlock(uint64_t rowIndex) const { return _storageManager->getRowWithinBlock(rowIndex); } inline bool DyscoStManColumn::areOffsetsInitialized() const { return _storageManager->areOffsetsInitialized(); } inline void DyscoStManColumn::initializeRowsPerBlock(size_t rowsPerBlock, size_t antennaCount) { _storageManager->initializeRowsPerBlock(rowsPerBlock, antennaCount, true); } } // namespace dyscostman #endif casacore-3.7.1/tables/Dysco/dyscostmanerror.h000066400000000000000000000011631476623553700212670ustar00rootroot00000000000000#ifndef DYSCO_STMAN_ERROR_H #define DYSCO_STMAN_ERROR_H #include namespace dyscostman { /** * Represents a runtime exception that occured within the DyscoStMan. */ class DyscoStManError : public casacore::DataManError { public: /** Constructor */ DyscoStManError() : casacore::DataManError() {} /** Construct with message. * @param message The exception message. */ explicit DyscoStManError(const std::string &message) : casacore::DataManError( message + " -- Error occured inside the Dysco Storage Manager") {} }; } // namespace dyscostman #endif casacore-3.7.1/tables/Dysco/dyscoweightcolumn.cc000066400000000000000000000027401476623553700217400ustar00rootroot00000000000000#include "dyscoweightcolumn.h" namespace dyscostman { void DyscoWeightColumn::Prepare(DyscoDistribution distribution, Normalization normalization, double studentsTNu, double distributionTruncation) { ThreadedDyscoColumn::Prepare(distribution, normalization, studentsTNu, distributionTruncation); const size_t nPolarizations = shape()[0], nChannels = shape()[1]; _encoder.reset(new WeightBlockEncoder(nPolarizations, nChannels, 1 << getBitsPerSymbol())); } void DyscoWeightColumn::initializeDecode(TimeBlockBuffer * /*buffer*/, const float *metaBuffer, size_t /*nRow*/, size_t /*nAntennae*/) { _encoder->InitializeDecode(metaBuffer); } void DyscoWeightColumn::decode(TimeBlockBuffer *buffer, const unsigned int *data, size_t blockRow, size_t /*a1*/, size_t /*a2*/) { _encoder->Decode(*buffer, data, blockRow); } void DyscoWeightColumn::encode(ThreadDataBase * /*threadData*/, TimeBlockBuffer *buffer, float *metaBuffer, symbol_t *symbolBuffer, size_t /*nAntennae*/) { _encoder->Encode(*buffer, metaBuffer, symbolBuffer); } } // namespace dyscostman casacore-3.7.1/tables/Dysco/dyscoweightcolumn.h000066400000000000000000000042441476623553700216030ustar00rootroot00000000000000#ifndef DYSCO_WEIGHT_COLUMN_H #define DYSCO_WEIGHT_COLUMN_H #include "stochasticencoder.h" #include "threadeddyscocolumn.h" #include "weightblockencoder.h" namespace dyscostman { class DyscoStMan; /** * A column for storing compressed complex values with an approximate Gaussian * distribution. * @author André Offringa */ class DyscoWeightColumn final : public ThreadedDyscoColumn { public: /** * Create a new column. Internally called by DyscoStMan when creating a * new column. */ DyscoWeightColumn(DyscoStMan *parent, int dtype) : ThreadedDyscoColumn(parent, dtype) {} DyscoWeightColumn(const DyscoWeightColumn &source) = delete; void operator=(const DyscoWeightColumn &source) = delete; /** Destructor. */ virtual ~DyscoWeightColumn() { shutdown(); } virtual void Prepare(DyscoDistribution distribution, Normalization normalization, double studentsTNu, double distributionTruncation) override; protected: virtual void initializeDecode(TimeBlockBuffer *buffer, const float *metaBuffer, size_t nRow, size_t nAntennae) override; virtual void decode(TimeBlockBuffer *buffer, const symbol_t *data, size_t blockRow, size_t a1, size_t a2) override; virtual std::unique_ptr initializeEncodeThread() override { return nullptr; } virtual void encode(ThreadDataBase *threadData, TimeBlockBuffer *buffer, float *metaBuffer, symbol_t *symbolBuffer, size_t nAntennae) override; virtual size_t metaDataFloatCount(size_t /*nRows*/, size_t /*nPolarizations*/, size_t /*nChannels*/, size_t /*nAntennae*/) const override { return _encoder->MetaDataFloatCount(); } virtual size_t symbolCount(size_t nRowsInBlock, size_t /*nPolarizations*/, size_t /*nChannels*/) const override { return _encoder->SymbolCount(nRowsInBlock); } private: std::unique_ptr _encoder; }; } // namespace dyscostman #endif casacore-3.7.1/tables/Dysco/header.h000066400000000000000000000061551476623553700172670ustar00rootroot00000000000000#ifndef DYSCO_HEADER_H #define DYSCO_HEADER_H #include "serializable.h" #include namespace dyscostman { #ifndef DOXYGEN_SHOULD_SKIP_THIS struct Header : public Serializable { /** Size of the total header, including column subheaders */ uint32_t headerSize; /** Start offset of the column headers */ uint32_t columnHeaderOffset; /** Number of columns and column headers */ uint32_t columnCount; std::string storageManagerName; uint32_t rowsPerBlock; uint32_t antennaCount; uint32_t blockSize; /** File version number */ uint16_t versionMajor, versionMinor; uint8_t dataBitCount; uint8_t weightBitCount; uint8_t distribution; uint8_t normalization; double studentTNu, distributionTruncation; uint32_t calculateColumnHeaderOffset() const { return 7 * 4 + // 6 x uint32 + string length storageManagerName.size() + 2 * 2 + // 2 x uint16 4 * 1 + // 4 x uint8 2 * 8; // 2 x double } virtual void Serialize(std::ostream &stream) const final override { SerializeToUInt32(stream, headerSize); SerializeToUInt32(stream, columnHeaderOffset); SerializeToUInt32(stream, columnCount); SerializeTo32bString(stream, storageManagerName); SerializeToUInt32(stream, rowsPerBlock); SerializeToUInt32(stream, antennaCount); SerializeToUInt32(stream, blockSize); SerializeToUInt16(stream, versionMajor); SerializeToUInt16(stream, versionMinor); SerializeToUInt8(stream, dataBitCount); SerializeToUInt8(stream, weightBitCount); SerializeToUInt8(stream, distribution); SerializeToUInt8(stream, normalization); SerializeToDouble(stream, studentTNu); SerializeToDouble(stream, distributionTruncation); } virtual void Unserialize(std::istream &stream) final override { headerSize = UnserializeUInt32(stream); columnHeaderOffset = UnserializeUInt32(stream); columnCount = UnserializeUInt32(stream); Unserialize32bString(stream, storageManagerName); rowsPerBlock = UnserializeUInt32(stream); antennaCount = UnserializeUInt32(stream); blockSize = UnserializeUInt32(stream); /** File version number */ versionMajor = UnserializeUInt16(stream); versionMinor = UnserializeUInt16(stream); dataBitCount = UnserializeUInt8(stream); weightBitCount = UnserializeUInt8(stream); distribution = UnserializeUInt8(stream); normalization = UnserializeUInt8(stream); studentTNu = UnserializeDouble(stream); distributionTruncation = UnserializeDouble(stream); } // the column headers start here (first generic header, then column specific // header) }; struct GenericColumnHeader : public Serializable { /** size of generic header + column specific header */ uint32_t columnHeaderSize; virtual void Serialize(std::ostream &stream) const override { SerializeToUInt32(stream, columnHeaderSize); } virtual void Unserialize(std::istream &stream) override { columnHeaderSize = UnserializeUInt32(stream); } virtual uint32_t calculateSize() const { return 4; } }; } #endif #endif casacore-3.7.1/tables/Dysco/rftimeblockencoder.cc000066400000000000000000000125731476623553700220370ustar00rootroot00000000000000#include "rftimeblockencoder.h" #include "stochasticencoder.h" #include RFTimeBlockEncoder::RFTimeBlockEncoder(size_t nPol, size_t nChannels) : _nPol(nPol), _nChannels(nChannels), _channelFactors(_nChannels * nPol), _rowFactors(), _ditherDist( dyscostman::StochasticEncoder::GetDitherDistribution()) {} RFTimeBlockEncoder::~RFTimeBlockEncoder() = default; void RFTimeBlockEncoder::maximizeRows(std::vector &data, float *metaBuffer, const dyscostman::StochasticEncoder &gausEncoder) { // Scale rows: Scale every row maximum to the max level. // Polarizations are processed separately: every polarization // has its own row-scaling factor. const double maxLevel = gausEncoder.MaxQuantity(); const size_t visPerRow = _nPol * _nChannels; for (size_t rowIndex = 0; rowIndex != data.size(); ++rowIndex) { DBufferRow &row = data[rowIndex]; for (size_t polIndex = 0; polIndex != _nPol; ++polIndex) { double max_val = 0.0; for (size_t channel = 0; channel != _nChannels; ++channel) { std::complex v = row.visibilities[channel * _nPol + polIndex]; double m = std::max(std::fabs(v.real()), std::fabs(v.imag())); if (std::isfinite(m)) max_val = std::max(max_val, m); } const double factor = max_val == 0.0 ? 1.0 : maxLevel / max_val; for (size_t channel = 0; channel != _nChannels; ++channel) { row.visibilities[channel * _nPol + polIndex] *= factor; } metaBuffer[visPerRow + rowIndex * _nPol + polIndex] = (maxLevel == 0.0) ? 1.0 : max_val / maxLevel; } } } void RFTimeBlockEncoder::maximizeChannels(std::vector &data, float *metaBuffer, const dyscostman::StochasticEncoder &gausEncoder) { const size_t visPerRow = _nPol * _nChannels; // Scale channels: channels are scaled such that the maximum // value equals the maximum encodable value for (size_t visIndex = 0; visIndex != visPerRow; ++visIndex) { double largest_component = 0.0; for (const DBufferRow &row : data) { const std::complex *ptr = &row.visibilities[visIndex]; const double local_max = std::max(std::fabs(ptr->real()), std::fabs(ptr->imag())); if (std::isfinite(local_max) && local_max > largest_component) largest_component = local_max; } const double factor = (gausEncoder.MaxQuantity() == 0.0 || largest_component == 0.0) ? 1.0 : gausEncoder.MaxQuantity() / largest_component; metaBuffer[visIndex] = 1.0 / factor; for (RFTimeBlockEncoder::DBufferRow &row : data) { row.visibilities[visIndex] *= factor; } } } template void RFTimeBlockEncoder::encode( const dyscostman::StochasticEncoder &gausEncoder, const TimeBlockEncoder::FBuffer &buffer, float *metaBuffer, TimeBlockEncoder::symbol_t *symbolBuffer, size_t /*antennaCount*/, std::mt19937 *rnd) { // Note that encoding is performed with doubles std::vector data; buffer.ConvertVector>(data); const size_t visPerRow = _nPol * _nChannels; // Rows are processed before // channels, because auto-correlations might have much // higher values compared to cross-correlations. By first // scaling the rows, the auto-correlations and cross-correlations // are brought to the same level. maximizeRows(data, metaBuffer, gausEncoder); maximizeChannels(data, metaBuffer, gausEncoder); symbol_t *symbolBufferPtr = symbolBuffer; for (const DBufferRow &row : data) { for (size_t i = 0; i != visPerRow; ++i) { if (UseDithering) { symbolBufferPtr[i * 2] = gausEncoder.EncodeWithDithering( row.visibilities[i].real(), _ditherDist(*rnd)); symbolBufferPtr[i * 2 + 1] = gausEncoder.EncodeWithDithering( row.visibilities[i].imag(), _ditherDist(*rnd)); } else { symbolBufferPtr[i * 2] = gausEncoder.Encode(row.visibilities[i].real()); symbolBufferPtr[i * 2 + 1] = gausEncoder.Encode(row.visibilities[i].imag()); } } symbolBufferPtr += visPerRow * 2; } } void RFTimeBlockEncoder::InitializeDecode(const float *metaBuffer, size_t nRow, size_t /*nAntennae*/) { _channelFactors.assign(metaBuffer, metaBuffer + _nPol * _nChannels); metaBuffer += _nPol * _nChannels; _rowFactors.assign(metaBuffer, metaBuffer + _nPol * nRow); } void RFTimeBlockEncoder::Decode( const dyscostman::StochasticEncoder &gausEncoder, TimeBlockEncoder::FBuffer &buffer, const TimeBlockEncoder::symbol_t *symbolBuffer, size_t blockRow, size_t antenna1, size_t antenna2) { FBufferRow &row = buffer[blockRow]; row.antenna1 = antenna1; row.antenna2 = antenna2; row.visibilities.resize(_nChannels * _nPol); std::complex *destination = row.visibilities.data(); const symbol_t *srcRowPtr = symbolBuffer + blockRow * SymbolsPerRow(); const size_t visPerRow = _nPol * _nChannels; for (size_t i = 0; i != visPerRow; ++i) { double chFactor = _channelFactors[i]; double factor = chFactor * _rowFactors[blockRow * _nPol + i % _nPol]; destination->real(double(gausEncoder.Decode(*srcRowPtr)) * factor); ++srcRowPtr; destination->imag(double(gausEncoder.Decode(*srcRowPtr)) * factor); ++srcRowPtr; ++destination; } } casacore-3.7.1/tables/Dysco/rftimeblockencoder.h000066400000000000000000000055571476623553700217050ustar00rootroot00000000000000#ifndef DYSCO_RFTIME_BLOCK_ENCODER_H #define DYSCO_RFTIME_BLOCK_ENCODER_H #include "stochasticencoder.h" #include "timeblockbuffer.h" #include "uvector.h" #include #include #include #include "timeblockencoder.h" class RFTimeBlockEncoder : public TimeBlockEncoder { public: RFTimeBlockEncoder(size_t nPol, size_t nChannels); virtual ~RFTimeBlockEncoder() override; virtual void EncodeWithDithering( const dyscostman::StochasticEncoder &gausEncoder, FBuffer &buffer, float *metaBuffer, symbol_t *symbolBuffer, size_t antennaCount, std::mt19937 &rnd) final override { encode(gausEncoder, buffer, metaBuffer, symbolBuffer, antennaCount, &rnd); } virtual void EncodeWithoutDithering( const dyscostman::StochasticEncoder &gausEncoder, FBuffer &buffer, float *metaBuffer, symbol_t *symbolBuffer, size_t antennaCount) final override { encode(gausEncoder, buffer, metaBuffer, symbolBuffer, antennaCount, 0); } virtual void InitializeDecode(const float *metaBuffer, size_t nRow, size_t nAntennae) final override; virtual void Decode(const dyscostman::StochasticEncoder &gausEncoder, FBuffer &buffer, const symbol_t *symbolBuffer, size_t blockRow, size_t antenna1, size_t antenna2) final override; virtual size_t SymbolCount(size_t nRow, size_t nPol, size_t nChannels) const final override { return nRow * nChannels * nPol * 2 /*complex*/; } virtual size_t SymbolCount(size_t nRow) const final override { return nRow * _nChannels * _nPol * 2 /*complex*/; } virtual size_t SymbolsPerRow() const final override { return _nChannels * _nPol * 2 /*complex*/; } virtual size_t MetaDataCount(size_t nRow, size_t nPol, size_t nChannels, size_t /*nAntennae*/) const final override { return nPol * (nChannels + nRow); } void Normalize(const dyscostman::StochasticEncoder &gausEncoder, TimeBlockBuffer> &buffer, size_t antennaCount); private: void maximizeRows(std::vector &data, float *metaBuffer, const dyscostman::StochasticEncoder &gausEncoder); void maximizeChannels( std::vector &data, float *metaBuffer, const dyscostman::StochasticEncoder &gausEncoder); template void encode(const dyscostman::StochasticEncoder &gausEncoder, const FBuffer &buffer, float *metaBuffer, symbol_t *symbolBuffer, size_t antennaCount, std::mt19937 *rnd); size_t _nPol, _nChannels; ao::uvector _channelFactors, _rowFactors; std::uniform_int_distribution _ditherDist; }; #endif casacore-3.7.1/tables/Dysco/rowtimeblockencoder.cc000066400000000000000000000062261476623553700222350ustar00rootroot00000000000000#include "rowtimeblockencoder.h" #include "stochasticencoder.h" using namespace dyscostman; RowTimeBlockEncoder::RowTimeBlockEncoder(size_t nPol, size_t nChannels) : _nPol(nPol), _nChannels(nChannels), _ditherDist(StochasticEncoder::GetDitherDistribution()), _rowFactors() {} void RowTimeBlockEncoder::InitializeDecode(const float *metaBuffer, size_t nRow, size_t /*nAntennae*/) { _rowFactors.assign(metaBuffer, metaBuffer + nRow); } void RowTimeBlockEncoder::Decode(const StochasticEncoder &gausEncoder, FBuffer &buffer, const symbol_t *symbolBuffer, size_t blockRow, size_t antenna1, size_t antenna2) { FBufferRow &row = buffer[blockRow]; row.antenna1 = antenna1; row.antenna2 = antenna2; row.visibilities.resize(_nChannels * _nPol); std::complex *destination = row.visibilities.data(); const symbol_t *srcRowPtr = symbolBuffer + blockRow * SymbolsPerRow(); const size_t visPerRow = _nPol * _nChannels; for (size_t i = 0; i != visPerRow; ++i) { double factor = _rowFactors[blockRow]; destination->real(double(gausEncoder.Decode(*srcRowPtr)) * factor); ++srcRowPtr; destination->imag(double(gausEncoder.Decode(*srcRowPtr)) * factor); ++srcRowPtr; ++destination; } } template void RowTimeBlockEncoder::encode(const StochasticEncoder &gausEncoder, const FBuffer &buffer, float *metaBuffer, symbol_t *symbolBuffer, size_t /*antennaCount*/, std::mt19937 *rnd) { // Note that encoding is performed with doubles std::vector data; buffer.ConvertVector>(data); const size_t visPerRow = _nPol * _nChannels; // Scale every maximum per row to the max level const double maxLevel = gausEncoder.MaxQuantity(); for (size_t rowIndex = 0; rowIndex != data.size(); ++rowIndex) { DBufferRow &row = data[rowIndex]; double maxVal = 0.0; for (size_t i = 0; i != visPerRow; ++i) { std::complex v = row.visibilities[i]; double m = std::max(std::fabs(v.real()), std::fabs(v.imag())); if (std::isfinite(m)) maxVal = std::max(maxVal, m); } const double factor = (maxVal == 0.0) ? 1.0 : maxLevel / maxVal; for (size_t i = 0; i != visPerRow; ++i) row.visibilities[i] *= factor; metaBuffer[rowIndex] = maxVal / maxLevel; } symbol_t *symbolBufferPtr = symbolBuffer; for (const DBufferRow &row : data) { for (size_t i = 0; i != visPerRow; ++i) { if (UseDithering) { symbolBufferPtr[i * 2] = gausEncoder.EncodeWithDithering( row.visibilities[i].real(), _ditherDist(*rnd)); symbolBufferPtr[i * 2 + 1] = gausEncoder.EncodeWithDithering( row.visibilities[i].imag(), _ditherDist(*rnd)); } else { symbolBufferPtr[i * 2] = gausEncoder.Encode(row.visibilities[i].real()); symbolBufferPtr[i * 2 + 1] = gausEncoder.Encode(row.visibilities[i].imag()); } } symbolBufferPtr += visPerRow * 2; } } casacore-3.7.1/tables/Dysco/rowtimeblockencoder.h000066400000000000000000000046421476623553700220770ustar00rootroot00000000000000#ifndef DYSCO_ROW_TIME_BLOCK_ENCODER_H #define DYSCO_ROW_TIME_BLOCK_ENCODER_H #include "stochasticencoder.h" #include "timeblockbuffer.h" #include "uvector.h" #include #include #include #include "timeblockencoder.h" class RowTimeBlockEncoder : public TimeBlockEncoder { public: RowTimeBlockEncoder(size_t nPol, size_t nChannels); virtual ~RowTimeBlockEncoder() override {} virtual void EncodeWithDithering( const dyscostman::StochasticEncoder &gausEncoder, FBuffer &buffer, float *metaBuffer, symbol_t *symbolBuffer, size_t antennaCount, std::mt19937 &rnd) final override { encode(gausEncoder, buffer, metaBuffer, symbolBuffer, antennaCount, &rnd); } virtual void EncodeWithoutDithering( const dyscostman::StochasticEncoder &gausEncoder, FBuffer &buffer, float *metaBuffer, symbol_t *symbolBuffer, size_t antennaCount) final override { encode(gausEncoder, buffer, metaBuffer, symbolBuffer, antennaCount, 0); } virtual void InitializeDecode(const float *metaBuffer, size_t nRow, size_t nAntennae) final override; virtual void Decode(const dyscostman::StochasticEncoder &gausEncoder, FBuffer &buffer, const symbol_t *symbolBuffer, size_t blockRow, size_t antenna1, size_t antenna2) final override; virtual size_t SymbolCount(size_t nRow, size_t nPol, size_t nChannels) const final override { return nRow * nChannels * nPol * 2 /*complex*/; } virtual size_t SymbolCount(size_t nRow) const final override { return nRow * _nChannels * _nPol * 2 /*complex*/; } virtual size_t SymbolsPerRow() const final override { return _nChannels * _nPol * 2 /*complex*/; } virtual size_t MetaDataCount(size_t nRow, size_t /*nPol*/, size_t /*nChannels*/, size_t /*nAntennae*/) const final override { return nRow; } private: template void encode(const dyscostman::StochasticEncoder &gausEncoder, const FBuffer &buffer, float *metaBuffer, symbol_t *symbolBuffer, size_t antennaCount, std::mt19937 *rnd); size_t _nPol, _nChannels; std::uniform_int_distribution _ditherDist; ao::uvector _rowFactors; }; #endif casacore-3.7.1/tables/Dysco/serializable.h000066400000000000000000000074711476623553700205070ustar00rootroot00000000000000#ifndef DYSCO_SERIALIZABLE_H #define DYSCO_SERIALIZABLE_H #include #include #include class Serializable { public: virtual ~Serializable() {} virtual void Serialize(std::ostream &stream) const = 0; virtual void Unserialize(std::istream &stream) = 0; template static void SerializeToUInt64(std::ostream &stream, T value) { uint64_t val64t = value; stream.write(reinterpret_cast(&val64t), sizeof(val64t)); } template static void SerializeToUInt32(std::ostream &stream, T value) { uint32_t val32t = value; stream.write(reinterpret_cast(&val32t), sizeof(val32t)); } template static void SerializeToUInt16(std::ostream &stream, T value) { uint16_t val16t = value; stream.write(reinterpret_cast(&val16t), sizeof(val16t)); } template static void SerializeToUInt8(std::ostream &stream, T value) { uint8_t val8t = value; stream.write(reinterpret_cast(&val8t), sizeof(val8t)); } static void SerializeToBool8(std::ostream &stream, bool value) { uint8_t val8t = value; stream.write(reinterpret_cast(&val8t), sizeof(val8t)); } static void SerializeToFloat(std::ostream &stream, float value) { stream.write(reinterpret_cast(&value), sizeof(value)); } static void SerializeToDouble(std::ostream &stream, double value) { stream.write(reinterpret_cast(&value), sizeof(value)); } static void SerializeToLDouble(std::ostream &stream, long double value) { stream.write(reinterpret_cast(&value), sizeof(value)); } static void SerializeToLDoubleC(std::ostream &stream, std::complex value) { stream.write(reinterpret_cast(&value), sizeof(value)); } static void SerializeToString(std::ostream &stream, const std::string &str) { SerializeToUInt64(stream, str.size()); stream.write(str.c_str(), str.size()); } static void SerializeTo32bString(std::ostream &stream, const std::string &str) { SerializeToUInt32(stream, str.size()); stream.write(str.c_str(), str.size()); } static uint64_t UnserializeUInt64(std::istream &stream) { return Unserialize(stream); } static uint32_t UnserializeUInt32(std::istream &stream) { return Unserialize(stream); } static uint16_t UnserializeUInt16(std::istream &stream) { return Unserialize(stream); } static uint8_t UnserializeUInt8(std::istream &stream) { return Unserialize(stream); } static bool UnserializeBool8(std::istream &stream) { return (bool)Unserialize(stream); } static double UnserializeFloat(std::istream &stream) { return Unserialize(stream); } static double UnserializeDouble(std::istream &stream) { return Unserialize(stream); } static long double UnserializeLDouble(std::istream &stream) { return Unserialize(stream); } static std::complex UnserializeLDoubleC(std::istream &stream) { return Unserialize>(stream); } static void UnserializeString(std::istream &stream, std::string &destStr) { size_t size = UnserializeUInt64(stream); char *str = new char[size]; stream.read(str, size); destStr = std::string(str, size); delete[] str; } static void Unserialize32bString(std::istream &stream, std::string &destStr) { size_t size = UnserializeUInt32(stream); char *str = new char[size]; stream.read(str, size); destStr = std::string(str, size); delete[] str; } private: template static T Unserialize(std::istream &stream) { T val; stream.read(reinterpret_cast(&val), sizeof(val)); return val; } }; #endif casacore-3.7.1/tables/Dysco/stochasticencoder.cc000066400000000000000000000130431476623553700216730ustar00rootroot00000000000000#include "stochasticencoder.h" #include #include #include #include #ifndef M_SQRT2l #define M_SQRT2l 1.4142135623730950488016887242096981L #endif namespace dyscostman { template inline typename StochasticEncoder::num_t StochasticEncoder::cumulative(num_t x) { return num_t(0.5) + num_t(0.5) * gsl_sf_erf(x / num_t(M_SQRT2l)); } template typename StochasticEncoder::num_t StochasticEncoder::invCumulative(num_t c, num_t err) { if (c < 0.5) return (-invCumulative(1.0 - c, err)); else if (c == 0.5) return 0.0; else if (c == 1.0) return std::numeric_limits::infinity(); else if (c > 1.0) return std::numeric_limits::quiet_NaN(); num_t x = 1.0; num_t fx = cumulative(x); num_t xLow, xHi; if (fx < c) { do { x *= 2.0; fx = cumulative(x); } while (fx < c); xLow = x * 0.5; xHi = x; } else { xLow = 0.0; xHi = 1.0; } num_t error = xHi; int notConverging = 0; do { x = (xLow + xHi) * 0.5; fx = cumulative(x); if (fx > c) xHi = x; else xLow = x; num_t currErr = std::fabs(fx - c); if (currErr >= error) { ++notConverging; // not converging anymore; stop. if (notConverging > 10) return x; } else notConverging = 0; error = currErr; } while (error > err); return x; } template StochasticEncoder::StochasticEncoder(size_t quantCount, ValueType stddev, bool gaussianMapping) : _encDictionary(quantCount - 1), _decDictionary(quantCount - 1) { // The minimum squared error is reached when each quantity gets an equal share // of error The error of a single quantity is \int _-dist ^dist P(x) x^2 dx // The integral of x^2 yields a third order term in the solutions. // The consequence is that it is optimal to quantize to value as given by // uniformly gridding the inverse cumulative normal distribution with a sigma // of sqrt(3). stddev = stddev * std::sqrt(3); _decDictionary.reserve(quantCount); typename Dictionary::iterator encItem = _encDictionary.begin(); typename Dictionary::iterator decItem = _decDictionary.begin(); if (gaussianMapping) { for (size_t i = 0; i != quantCount - 1; ++i) { if (i != 0) { num_t rightBoundary = ((num_t)i) / (num_t)(quantCount - 1); *encItem = stddev * invCumulative(rightBoundary); ++encItem; } num_t val = ((num_t)i + num_t(0.5)) / (num_t)(quantCount - 1); *decItem = stddev * invCumulative(val); ++decItem; } } else { for (size_t i = 0; i != quantCount - 1; ++i) { if (i != 0) { num_t rightBoundary = -1.0 + 2.0 * ((num_t)i) / (num_t)(quantCount - 1); *encItem = stddev * rightBoundary; ++encItem; } num_t val = -1.0 + 2.0 * ((num_t)i + num_t(0.5)) / (num_t)(quantCount - 1); *decItem = stddev * val; // item.symbol = i; ++decItem; } } // Bounding element so that lower_bound always returns a valid symbol // Note that if the input is max, it will be encoded as an inf. *encItem = std::numeric_limits::max(); // The last encoding quantity will be used for representing non-finite // values. This value is reserved, but outside of the size of the // vector -- somewhat dirty but OK since a uvector is used, for which // we know it behaves OK in this case. This will make sure that // lower_bound() never sees the NaN. *decItem = std::numeric_limits::quiet_NaN(); } template void StochasticEncoder::initializeStudentT(double nu, double rms) { size_t quantCount = _encDictionary.size() + 1; _decDictionary.reserve(quantCount); typename Dictionary::iterator encItem = _encDictionary.begin(); typename Dictionary::iterator decItem = _decDictionary.begin(); for (size_t i = 0; i != quantCount - 1; ++i) { if (i != 0) { num_t rightBoundary = ((num_t)i) / (num_t)(quantCount - 1); *encItem = gsl_cdf_tdist_Pinv(rightBoundary, nu) * rms; ++encItem; } num_t val = ((num_t)i + num_t(0.5)) / (num_t)(quantCount - 1); *decItem = gsl_cdf_tdist_Pinv(val, nu) * rms; ++decItem; } *encItem = std::numeric_limits::max(); *decItem = std::numeric_limits::quiet_NaN(); } template void StochasticEncoder::initializeTruncatedGaussian( double truncationValue, double rms) { size_t quantCount = _encDictionary.size() + 1; _decDictionary.reserve(quantCount); typename Dictionary::iterator encItem = _encDictionary.begin(); typename Dictionary::iterator decItem = _decDictionary.begin(); double cdfValueAtTrunc = gsl_cdf_gaussian_P(-truncationValue, 1.0); double factor = 1.0 - 2.0 * cdfValueAtTrunc; for (size_t i = 0; i != quantCount - 1; ++i) { if (i != 0) { double rightBoundary = ((num_t)i) / (num_t)(quantCount - 1); double cdfVal = rightBoundary * factor + cdfValueAtTrunc; *encItem = gsl_cdf_gaussian_Pinv(cdfVal, rms); ++encItem; } double val = ((num_t)i + num_t(0.5)) / (num_t)(quantCount - 1); double cdfVal = val * factor + cdfValueAtTrunc; *decItem = gsl_cdf_gaussian_Pinv(cdfVal, rms); ++decItem; } *encItem = std::numeric_limits::max(); *decItem = std::numeric_limits::quiet_NaN(); } template class StochasticEncoder; } // namespace dyscostman casacore-3.7.1/tables/Dysco/stochasticencoder.h000066400000000000000000000221071476623553700215360ustar00rootroot00000000000000#ifndef DYSCO_STOCHASTIC_ENCODER_H #define DYSCO_STOCHASTIC_ENCODER_H #include "uvector.h" #include #include #include namespace dyscostman { /** * Lossy encoder for stochastic values. * * This encoder can encode a numeric value represented by a floating point * number (float, double, ...) into an integer value with a given limit. * It can be made to be the least-square error quantization for some * distributions. * * Encoding and decoding have asymetric time complexity / speeds, as decoding * is easier than encoding. Decoding is a single indexing into an array, thus * extremely fast and with constant time complexity. Encoding is a binary * search through the quantizaton values, thus takes O(log quantcount). * Typical performance of encoding is 100 MB/s. * * If the values are encoded into a number of bits which are not divisible by * eight, the BytePacker class can be used to pack the values. * * @author André Offringa (offringa@gmail.com) */ template class StochasticEncoder { public: /** * Construct encoder for given dictionary size and Gaussian stddev. * This constructor initializes the lookup table, and is therefore * quite slow. * @param quantCount The number of quantization levels, i.e., the dictionary * size. Normally this is 2^bitcount. One quantity will be saved for storing * non-finite values (NaN and infinites). * @param stddev The standard deviation of the data. The closer this value is * to the real stddev, the more accurate the encoder will be. * @param gaussianMapping Used for testing with non-gaussian distributions. */ StochasticEncoder(size_t quantCount, ValueType stddev, bool gaussianMapping = true); static StochasticEncoder StudentTEncoder(size_t quantCount, double nu, double rms) { StochasticEncoder encoder(quantCount); encoder.initializeStudentT(nu, rms); return encoder; } static StochasticEncoder TruncatedGausEncoder(size_t quantCount, double trunc, double rms) { StochasticEncoder encoder(quantCount); encoder.initializeTruncatedGaussian(trunc, rms); return encoder; } /** * Unsigned integer type used for representing the encoded symbols. */ typedef unsigned symbol_t; /** * Template type used for representing floating point values that * are to be encoded. */ typedef ValueType value_t; /** * Get the quantized symbol for the given floating point value. * This method is implemented with a binary search, so takes * O(log N), with N the dictionary size (2^bitcount). * Use Decode() on the returned symbol to get * the decoded value. * @param value Floating point value to be encoded. */ symbol_t Encode(ValueType value) const { if (std::isfinite(value)) return _encDictionary.symbol(_encDictionary.lower_bound(value)); else return QuantizationCount() - 1; } static std::uniform_int_distribution GetDitherDistribution() { return std::uniform_int_distribution(0, ((1u << 31) - 1)); } /** * Get the quantized symbol for the given floating point value. * Dithering is applied, which will cause the average error to * converge to zero, assuming the error is uniformly distributed. * This method is implemented with a binary search, so takes * O(log N), with N the dictionary size (2^bitcount). * Use Decode() on the returned symbol to get * the decoded value. * @param value Floating point value to be encoded. * @param ditherValue The dithering value to apply. */ symbol_t EncodeWithDithering(ValueType value, unsigned ditherValue) const { if (std::isfinite(value)) { const typename Dictionary::const_iterator lowerBound = _decDictionary.lower_bound(value); if (lowerBound == _decDictionary.begin()) return _decDictionary.symbol(lowerBound); if (lowerBound == _decDictionary.end()) return _decDictionary.symbol(lowerBound - 1); const ValueType rightValue = _decDictionary.value(lowerBound); const ValueType leftValue = _decDictionary.value(lowerBound - 1); ValueType ditherMark = ValueType(1u << 31) * (value - leftValue) / (rightValue - leftValue); if (ditherMark > ditherValue) return _decDictionary.symbol(lowerBound); else return _decDictionary.symbol(lowerBound - 1); } else { return _encDictionary.size(); } } /** * Will return the right boundary of the given symbol. * The right boundary is the smallest value that would not be * quantized to the given symbol anymore. If no such boundary * exists, 0.0 is returned. */ value_t RightBoundary(symbol_t symbol) const { if (symbol != _encDictionary.size()) return _encDictionary.value(symbol); else return 0.0; } /** * Get the centroid value that belongs to the given symbol. * @param symbol Symbol to be decoded * @returns The best estimate of the original value. */ ValueType Decode(symbol_t symbol) const { return _decDictionary.value(symbol); } size_t QuantizationCount() const { return _decDictionary.size() + 1; } ValueType MaxQuantity() const { return _decDictionary.largest_value(); } ValueType MinQuantity() const { return _decDictionary.smallest_value(); } private: explicit StochasticEncoder(size_t quantCount) : _encDictionary(quantCount - 1), _decDictionary(quantCount - 1) {} void initializeStudentT(double nu, double rms); void initializeTruncatedGaussian(double truncationValue, double rms); class Dictionary { public: typedef value_t *iterator; typedef const value_t *const_iterator; Dictionary() : _values() {} explicit Dictionary(size_t size) : _values(size) {} void reserve(size_t size) { _values.reserve(size); } void resize(size_t size) { _values.resize(size); } /** * Returns an iterator pointing to the first element in the dictionary * that is not less than (i.e. greater or equal to) value. * * This implementation is like @ref lower_bound_fast(), but additionally * assumes the dictionary has at least two elements, avoiding one * comparison. */ const_iterator lower_bound(value_t val) const { size_t p = 0, q = _values.size(); size_t m = (p + q) / 2; if (_values[m] <= val) p = m; else q = m; while (p + 1 != q) { size_t m = (p + q) / 2; if (_values[m] <= val) p = m; else q = m; } return (_values[p] < val) ? (&_values[q]) : (&_values[p]); } /** * Returns an iterator pointing to the first element in the dictionary * that is not less than (i.e. greater or equal to) value. * * This implementation turns out to be slightly faster than the * STL implementation. It performs 10.7 MB/s, vs. 9.0 MB/s for the * STL. 18% faster. Using "unsigned" instead of "size_t" is 5% slower. * (It's not a fair STL comparison, because this implementation * does not check for empty vector). */ const_iterator lower_bound_fast(value_t val) const { size_t p = 0, q = _values.size(); while (p + 1 != q) { size_t m = (p + q) / 2; if (_values[m] <= val) p = m; else q = m; } return (_values[p] < val) ? (&_values[q]) : (&_values[p]); } /** * Below is the first failed result of an attempt to beat the STL in * performance. It turns out to be 13% slower for larger dictionaries, * compared to the STL implementation that is used in the class * above. It performs 7.9 MB/s. 26% compared to the 'fastest' lower_bound. */ const_iterator lower_bound_slow(value_t val) const { const value_t *p = &*_values.begin(), *q = p + _values.size(); while (p + 1 != q) { // This is a bit inefficient, but (p + q)/2 was not allowed, because // operator+(ptr,ptr) is not allowed. const value_t *m = p + (q - p) / 2; if (*m <= val) p = m; else q = m; } return p; } iterator begin() { return &*_values.begin(); } const_iterator begin() const { return &*_values.begin(); } const_iterator end() const { return &*_values.end(); } symbol_t symbol(const_iterator iter) const { return (iter - begin()); } symbol_t largest_symbol() const { return _values.size() - 1; } value_t value(const_iterator iter) const { return *iter; } value_t value(symbol_t sym) const { return _values[sym]; } value_t largest_value() const { return _values.back(); } value_t smallest_value() const { return _values.front(); } size_t size() const { return _values.size(); } size_t capacity(size_t) const { return _values.capacity(); } private: ao::uvector _values; }; typedef long double num_t; static num_t cumulative(num_t x); static num_t invCumulative(num_t c, num_t err = num_t(1e-13)); Dictionary _encDictionary; Dictionary _decDictionary; }; } // namespace dyscostman #endif casacore-3.7.1/tables/Dysco/tests/000077500000000000000000000000001476623553700170215ustar00rootroot00000000000000casacore-3.7.1/tables/Dysco/tests/runtests.cc000066400000000000000000000001541476623553700212170ustar00rootroot00000000000000#define BOOST_TEST_MODULE dysco //#define BOOST_TEST_DYN_LINK #include casacore-3.7.1/tables/Dysco/tests/testbytepacking.cc000066400000000000000000000114251476623553700225330ustar00rootroot00000000000000#include "../bytepacker.h" #include "../uvector.h" #include #include using namespace dyscostman; BOOST_AUTO_TEST_SUITE(bytepacking) template void assertEqualArray(const T* expected, const T* actual, size_t size, const std::string& msg) { for (size_t i = 0; i != size; ++i) { if (expected[i] != actual[i]) { std::ostringstream str; str << "Failed assertEqualArray() for test " << msg << ": Expected: {" << (int)expected[0]; for (size_t j = 1; j != size; ++j) str << ", " << (int)expected[j]; str << "} Actual: {" << (int)actual[0]; for (size_t j = 1; j != size; ++j) str << ", " << (int)actual[j]; str << "}"; BOOST_REQUIRE_MESSAGE(expected[i] == actual[i], str.str()); } } BOOST_CHECK(expected[0] == actual[0]); } BOOST_AUTO_TEST_CASE(under_and_overflow) { const size_t NBITSIZES = 8; size_t bitSizes[NBITSIZES] = {2, 3, 4, 6, 8, 10, 12, 16}; for (size_t i = 0; i != NBITSIZES; ++i) { for (size_t s = 0; s != 12; ++s) { unsigned arr[12] = {1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31}; unsigned expected[13] = {37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37}; for (size_t x = 0; x != 12; ++x) arr[x] &= (1 << bitSizes[i]) - 1; unsigned result[13] = {37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37}; unsigned char packed[24], packedOr[24]; memset(packed, 39, 24); memset(packedOr, 39, 24); BytePacker::pack(bitSizes[i], packed, arr, s); BytePacker::unpack(bitSizes[i], result, packed, s); for (size_t x = 0; x != s; ++x) expected[x] = arr[x]; std::stringstream msg; msg << "result of pack+unpack (l=" << s << ",bits=" << bitSizes[i] << ')'; assertEqualArray(expected, result, 13, msg.str()); size_t packedSize = BytePacker::bufferSize(s, bitSizes[i]); assertEqualArray(packedOr, &packed[packedSize], 24 - packedSize, "packed not overwritten past length"); } unsigned arr2[15]; for (size_t x = 0; x != 15; ++x) arr2[x] = (1 << bitSizes[i]) - 1; unsigned char packed[30]; memset(packed, 0, 30); unsigned result[15]; memset(result, 0, 15 * sizeof(unsigned)); BytePacker::pack(bitSizes[i], packed, arr2, 15); BytePacker::unpack(bitSizes[i], result, packed, 15); std::stringstream msg; msg << "all bits set (bits=" << bitSizes[i] << ')'; assertEqualArray(arr2, result, 15, msg.str()); } } void testSingle(const ao::uvector& data, int bitCount) { int limit = (1 << bitCount); ao::uvector trimmedData(data.size()), restoredData(data.size()); for (size_t i = 0; i != data.size(); ++i) trimmedData[i] = data[i] % limit; ao::uvector buffer( BytePacker::bufferSize(trimmedData.size(), bitCount), 0); // std::cout << "Tested array: [" << trimmedData[0]; // for(size_t i=1; i!=std::min(trimmedData.size(), 32); ++i) // std::cout << ", " << trimmedData[i]; // if(trimmedData.size() > 32) std::cout << ", ..."; // std::cout << "]\n"; BytePacker::pack(bitCount, buffer.data(), trimmedData.data(), trimmedData.size()); BytePacker::unpack(bitCount, restoredData.data(), buffer.data(), restoredData.size()); for (size_t i = 0; i != trimmedData.size(); ++i) { BOOST_REQUIRE_MESSAGE(restoredData[i] == trimmedData[i], "data[" << i << "] was incorrectly unpacked: was " << restoredData[i] << ", should be " << trimmedData[i]); } } void testCombinations(const ao::uvector& data, int bitCount) { for (size_t dataSize = 1; dataSize != std::min(32u, data.size()); ++dataSize) { ao::uvector resizedData(data.begin(), data.begin() + dataSize); testSingle(resizedData, bitCount); } testSingle(data, bitCount); for (size_t dataSize = 1; dataSize != std::min(32u, data.size() - 1); ++dataSize) { ao::uvector resizedData(data.begin() + 1, data.begin() + dataSize + 1); testSingle(resizedData, bitCount); } ao::uvector resizedData(data.begin() + 1, data.end()); testSingle(resizedData, bitCount); } const int bitrates[] = {2, 3, 4, 6, 8, 10, 12, 16}; BOOST_AUTO_TEST_CASE(pack_unpack) { for (int sample : bitrates) { ao::uvector testArray{1337, 2, 100, 0}; for (int i = 0; i != 1000; ++i) { testArray.push_back(i); testArray.push_back(i * 37); testArray.push_back(i * 20000); } testCombinations(testArray, sample); } } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/tables/Dysco/tests/testdyscostman.cc000066400000000000000000000143251476623553700224210ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include "../dyscostman.h" using namespace casacore; using namespace dyscostman; BOOST_AUTO_TEST_SUITE(dyscostman) casacore::Record GetDyscoSpec() { casacore::Record dyscoSpec; dyscoSpec.define("distribution", "TruncatedGaussian"); dyscoSpec.define("normalization", "AF"); dyscoSpec.define("distributionTruncation", 2.0); dyscoSpec.define("dataBitCount", 10); dyscoSpec.define("weightBitCount", 12); return dyscoSpec; } struct TestTableFixture { explicit TestTableFixture(size_t nAnt) { casacore::TableDesc tableDesc; IPosition shape(2, 1, 1); casacore::ArrayColumnDesc columnDesc( "DATA", "", "DyscoStMan", "", shape); columnDesc.setOptions(casacore::ColumnDesc::Direct | casacore::ColumnDesc::FixedShape); casacore::ScalarColumnDesc ant1Desc("ANTENNA1"), ant2Desc("ANTENNA2"), fieldDesc("FIELD_ID"), dataDescIdDesc("DATA_DESC_ID"); casacore::ScalarColumnDesc timeDesc("TIME"); tableDesc.addColumn(columnDesc); tableDesc.addColumn(ant1Desc); tableDesc.addColumn(ant2Desc); tableDesc.addColumn(fieldDesc); tableDesc.addColumn(dataDescIdDesc); tableDesc.addColumn(timeDesc); casacore::SetupNewTable setupNewTable("TestTable", tableDesc, casacore::Table::New); register_dyscostman(); DataManagerCtor dyscoConstructor = DataManager::getCtor("DyscoStMan"); std::unique_ptr dysco( dyscoConstructor("DATA_dm", GetDyscoSpec())); setupNewTable.bindColumn("DATA", *dysco); casacore::Table newTable(setupNewTable); size_t a1 = 0, a2 = 1; double time = 10.0; const size_t nRow = 2 * nAnt * (nAnt - 1) / 2; newTable.addRow(nRow); casacore::ScalarColumn a1Col(newTable, "ANTENNA1"), a2Col(newTable, "ANTENNA2"), fieldCol(newTable, "FIELD_ID"), dataDescIdCol(newTable, "DATA_DESC_ID"); casacore::ScalarColumn timeCol(newTable, "TIME"); for (size_t i = 0; i != nRow; ++i) { a1Col.put(i, a1); a2Col.put(i, a2); fieldCol.put(i, 0); dataDescIdCol.put(i, 0); timeCol.put(i, time); a2++; if (a2 == nAnt) { ++a1; a2 = a1 + 1; if (a2 == nAnt) { a1 = 0; a2 = 1; ++time; } } } casacore::ArrayColumn dataCol(newTable, "DATA"); for (size_t i = 0; i != nRow; ++i) { casacore::Array arr(shape); *arr.cbegin() = i; dataCol.put(i, arr); } } ~TestTableFixture() { boost::filesystem::remove_all("TestTable"); } }; BOOST_AUTO_TEST_CASE(spec) { DyscoStMan dysco(8, 12); Record spec = dysco.dataManagerSpec(); BOOST_CHECK_EQUAL(spec.asInt("dataBitCount"), 8); BOOST_CHECK_EQUAL(spec.asInt("weightBitCount"), 12); } BOOST_AUTO_TEST_CASE(name) { DyscoStMan dysco1(8, 12, "withparameters"); BOOST_CHECK_EQUAL(dysco1.dataManagerType(), "DyscoStMan"); BOOST_CHECK_EQUAL(dysco1.dataManagerName(), "withparameters"); DyscoStMan dysco2("testname", GetDyscoSpec()); BOOST_CHECK_EQUAL(dysco2.dataManagerType(), "DyscoStMan"); BOOST_CHECK_EQUAL(dysco2.dataManagerName(), "testname"); std::unique_ptr dysco3(dysco2.clone()); BOOST_CHECK_EQUAL(dysco3->dataManagerType(), "DyscoStMan"); BOOST_CHECK_EQUAL(dysco3->dataManagerName(), "testname"); register_dyscostman(); DataManagerCtor dyscoConstructor = DataManager::getCtor("DyscoStMan"); std::unique_ptr dysco4( dyscoConstructor("Constructed", GetDyscoSpec())); BOOST_CHECK_EQUAL(dysco4->dataManagerName(), "Constructed"); TestTableFixture fixture(3); casacore::Table table("TestTable"); casacore::ArrayColumn dataCol(table, "DATA"); DataManager* dm = table.findDataManager("DATA", true); BOOST_CHECK_EQUAL(dm->dataManagerName(), "DATA_dm"); } BOOST_AUTO_TEST_CASE(makecolumn) { DyscoStMan dysco(8, 12, "mydysco"); dysco.createDirArrColumn("DATA", casacore::DataType::TpComplex, ""); dysco.createDirArrColumn("WEIGHT_SPECTRUM", casacore::DataType::TpFloat, ""); dysco.createDirArrColumn("CORRECTED_DATA", casacore::DataType::TpComplex, ""); dysco.createDirArrColumn("ANYTHING", casacore::DataType::TpComplex, ""); BOOST_CHECK(true); } BOOST_AUTO_TEST_CASE(maketable) { size_t nAnt = 3; TestTableFixture fixture(nAnt); casacore::Table table("TestTable"); casacore::ArrayColumn dataCol(table, "DATA"); for (size_t i = 0; i != table.nrow(); ++i) { BOOST_CHECK_CLOSE_FRACTION((*dataCol(i).cbegin()).real(), float(i), 1e-4); } } BOOST_AUTO_TEST_CASE(read_past_end) { /** * While reading past the end of a file might seem wrong in any case, it can * happen that a user reads a line that was not stored yet in the particular * column, but which does exist in the table. This is valid (and DPPP does * this). */ size_t nAnt = 3; TestTableFixture fixture(nAnt); casacore::Table table("TestTable"); casacore::ArrayColumn dataCol(table, "DATA"); for (size_t i = table.nrow(); i != table.nrow() * 2; ++i) { BOOST_CHECK_CLOSE_FRACTION((*dataCol(i).cbegin()).real(), 0.0, 1e-4); } } BOOST_AUTO_TEST_CASE(readonly) { size_t nAnt = 3; TestTableFixture fixture(nAnt); boost::filesystem::directory_iterator end_itr; for (boost::filesystem::directory_iterator itr("TestTable/"); itr != end_itr; ++itr) { if (boost::filesystem::is_regular_file(itr->path())) { boost::filesystem::permissions( itr->path(), boost::filesystem::others_read | boost::filesystem::owner_read); } } casacore::Table table("TestTable"); casacore::ArrayColumn dataCol(table, "DATA"); for (size_t i = 0; i != table.nrow(); ++i) { BOOST_CHECK_CLOSE_FRACTION((*dataCol(i).cbegin()).real(), float(i), 1e-4); } } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/tables/Dysco/tests/testtimeblockencoder.cc000066400000000000000000000325771476623553700235570ustar00rootroot00000000000000#include "../aftimeblockencoder.h" #include "../dysconormalization.h" #include "../rftimeblockencoder.h" #include "../rowtimeblockencoder.h" #include "../stochasticencoder.h" #include #include using namespace dyscostman; BOOST_AUTO_TEST_SUITE(timeblock_encoder) namespace { std::unique_ptr CreateEncoder( Normalization blockNormalization, size_t nPol, size_t nChan) { switch (blockNormalization) { default: case Normalization::kRF: return std::unique_ptr( new RFTimeBlockEncoder(nPol, nChan)); case Normalization::kAF: return std::unique_ptr( new AFTimeBlockEncoder(nPol, nChan, true)); case Normalization::kRow: return std::unique_ptr( new RowTimeBlockEncoder(nPol, nChan)); } } TimeBlockBuffer> Decode( Normalization blockNormalization, StochasticEncoder& gausEncoder, size_t nAnt, size_t nChan, size_t nPol, size_t nRow, const float* metaBuffer, const TimeBlockEncoder::symbol_t* symbolBuffer) { TimeBlockBuffer> out(nPol, nChan); out.resize(nRow); std::unique_ptr decoder = CreateEncoder(blockNormalization, nPol, nChan); decoder->InitializeDecode(metaBuffer, nRow, nAnt); size_t rIndex = 0; for (size_t ant1 = 0; ant1 != nAnt; ++ant1) { for (size_t ant2 = ant1; ant2 != nAnt; ++ant2) { decoder->Decode(gausEncoder, out, symbolBuffer, rIndex, ant1, ant2); ++rIndex; } } return out; } void TestSimpleExample(Normalization blockNormalization) { const size_t nAnt = 4, nChan = 1, nPol = 2, nRow = (nAnt * (nAnt + 1) / 2); TimeBlockBuffer> buffer(nPol, nChan); std::complex data[nChan * nPol]; data[0] = 99.0; data[1] = 99.0; buffer.SetData(0, 0, 0, data); data[0] = 10.0; data[1] = std::complex(9.0, 1.0); buffer.SetData(1, 0, 1, data); data[0] = 8.0; data[1] = std::complex(7.0, 2.0); buffer.SetData(2, 0, 2, data); data[0] = 6.0; data[1] = std::complex(5.0, 3.0); buffer.SetData(3, 0, 3, data); data[0] = 99.0; data[1] = 99.0; buffer.SetData(4, 1, 1, data); data[0] = 4.0; data[1] = std::complex(3.0, 4.0); buffer.SetData(5, 1, 2, data); data[0] = 2.0; data[1] = std::complex(1.0, 5.0); buffer.SetData(6, 1, 3, data); data[0] = 99.0; data[1] = 99.0; buffer.SetData(7, 2, 2, data); data[0] = 0.0; data[1] = std::numeric_limits::quiet_NaN(); buffer.SetData(8, 2, 3, data); data[0] = 99.0; data[1] = 99.0; buffer.SetData(9, 3, 3, data); const TimeBlockBuffer> input(buffer); StochasticEncoder gausEncoder(256, 1.0, false); std::unique_ptr encoder = CreateEncoder(blockNormalization, nPol, nChan); size_t metaDataCount = encoder->MetaDataCount(nRow, nPol, nChan, nAnt); size_t symbolCount = encoder->SymbolCount(nRow); ao::uvector metaBuffer(metaDataCount); ao::uvector symbolBuffer(symbolCount); encoder->EncodeWithoutDithering(gausEncoder, buffer, metaBuffer.data(), symbolBuffer.data(), nAnt); TimeBlockBuffer> out = Decode(blockNormalization, gausEncoder, nAnt, nChan, nPol, nRow, metaBuffer.data(), symbolBuffer.data()); std::complex dataFromOut[nChan * nPol], dataFromIn[nChan * nPol]; for (size_t row = 0; row != nRow; row++) { // skip auto-correlations of AF, since these are not saved. out.GetData(row, dataFromOut); input.GetData(row, dataFromIn); if (blockNormalization != Normalization::kAF || (row != 0 && row != 4 && row != 7 && row != 9)) { for (size_t ch = 0; ch != nChan; ++ch) { BOOST_CHECK_MESSAGE( std::norm(dataFromOut[ch] - dataFromIn[ch]) < 0.1, "Output{" << dataFromOut[ch] << "} is close to input{" << dataFromIn[ch] << "} of row " << row << " with normalization " << int(blockNormalization)); } } } } void TestTimeBlockEncoder(Normalization blockNormalization) { const size_t nAnt = 50, nChan = 64, nPol = 4, nRow = (nAnt * (nAnt + 1) / 2); TimeBlockBuffer> buffer(nPol, nChan); std::mt19937 rnd; std::normal_distribution dist; StochasticEncoder gausEncoder(256, 1.0, false); std::uniform_int_distribution dither = gausEncoder.GetDitherDistribution(); dist(rnd); ao::uvector> data(nChan * nPol); std::vector>> allData; RMSMeasurement unscaledRMS; double factorSum = 0.0; size_t factorCount = 0; size_t blockRow = 0; for (size_t a1 = 0; a1 != nAnt; ++a1) { for (size_t a2 = a1; a2 != nAnt; ++a2) { for (size_t ch = 0; ch != nChan; ++ch) { for (size_t p = 0; p != nPol; ++p) { std::complex rndVal(dist(rnd), dist(rnd)); // std::complex rndVal(1.0, 0.0); float f = 1.0; // float f = float(a1+1) * float(a2+1) * float(p+1); factorSum += f; factorCount++; data[p + ch * nPol] = rndVal * f; std::complex encodedVal( gausEncoder.Decode( gausEncoder.EncodeWithDithering(rndVal.real(), dither(rnd))), gausEncoder.Decode( gausEncoder.EncodeWithDithering(rndVal.imag(), dither(rnd)))); unscaledRMS.Include(encodedVal - rndVal); } } buffer.SetData(blockRow, a1, a2, data.data()); allData.push_back(data); ++blockRow; } } std::unique_ptr encoder = CreateEncoder(blockNormalization, nPol, nChan); const size_t nIter = 25; ao::uvector metaBuffer( encoder->MetaDataCount(nRow, nPol, nChan, nAnt)); ao::uvector symbolBuffer( encoder->SymbolCount(nAnt * (nAnt + 1) / 2)); for (size_t i = 0; i != nIter; ++i) encoder->EncodeWithDithering(gausEncoder, buffer, metaBuffer.data(), symbolBuffer.data(), nAnt, rnd); for (size_t i = 0; i != nIter; ++i) { encoder->InitializeDecode(metaBuffer.data(), nRow, nAnt); blockRow = 0; for (size_t a1 = 0; a1 != nAnt; ++a1) { for (size_t a2 = a1; a2 != nAnt; ++a2) { ao::uvector> dataOut(nChan * nPol); encoder->Decode(gausEncoder, buffer, symbolBuffer.data(), blockRow, a1, a2); buffer.GetData(blockRow, dataOut.data()); ++blockRow; } } } RMSMeasurement mEncodingError, mData; size_t index = 0; encoder->InitializeDecode(metaBuffer.data(), nRow, nAnt); blockRow = 0; for (size_t a1 = 0; a1 != nAnt; ++a1) { for (size_t a2 = a1; a2 != nAnt; ++a2) { ao::uvector> dataOut(nChan * nPol); encoder->Decode(gausEncoder, buffer, symbolBuffer.data(), blockRow, a1, a2); buffer.GetData(blockRow, dataOut.data()); ao::uvector>& dataIn = allData[index]; for (size_t i = 0; i != nPol * nChan; ++i) { mEncodingError.Include(dataOut[i] - dataIn[i]); mData.Include(dataIn[i]); } ++index; ++blockRow; } } BOOST_CHECK_LT(mEncodingError.RMS(), mData.RMS() * 0.1); /*std::cout << "Gaussian encoding error for unscaled values: " << unscaledRMS.RMS() << '\n'; std::cout << "Average factor: " << factorSum / factorCount << '\n'; std::cout << "Effective RMS of error: " << mEncoded.RMS() / (factorSum / factorCount) << " ( " << (mEncoded.RMS() / (factorSum / factorCount)) / unscaledRMS.RMS() << " x theoretical)\n";*/ } TimeBlockBuffer> EncodeDecode(Normalization block_normalization, TimeBlockBuffer> &buffer, size_t n_pol, size_t n_chan, size_t n_ant) { const size_t n_row = buffer.NRows(); StochasticEncoder gausEncoder(256, 1.0, false); std::unique_ptr encoder = CreateEncoder(block_normalization, n_pol, n_chan); size_t metaDataCount = encoder->MetaDataCount(n_row, n_pol, n_chan, n_ant); size_t symbolCount = encoder->SymbolCount(n_row); ao::uvector metaBuffer(metaDataCount); ao::uvector symbolBuffer(symbolCount); std::mt19937 mt; encoder->EncodeWithDithering(gausEncoder, buffer, metaBuffer.data(), symbolBuffer.data(), n_ant, mt); return Decode(block_normalization, gausEncoder, n_ant, n_chan, n_pol, n_row, metaBuffer.data(), symbolBuffer.data()); } } BOOST_AUTO_TEST_CASE(row_normalization_per_row_accuracy) { TestSimpleExample(Normalization::kRow); } BOOST_AUTO_TEST_CASE(row_normalization_global_rms_accuracy) { TestTimeBlockEncoder(Normalization::kRow); } BOOST_AUTO_TEST_CASE(af_normalization_per_row_accuracy) { TestSimpleExample(Normalization::kAF); } BOOST_AUTO_TEST_CASE(af_normalization_global_rms_accuracy) { TestTimeBlockEncoder(Normalization::kAF); } BOOST_AUTO_TEST_CASE(rf_normalization_per_row_accuracy) { TestSimpleExample(Normalization::kRF); } BOOST_AUTO_TEST_CASE(rf_normalization_global_rms_accuracy) { TestTimeBlockEncoder(Normalization::kRF); } void TestZeroEncoding(Normalization block_normalization) { const size_t n_ant = 4, n_chan = 1, n_pol = 2; TimeBlockBuffer> buffer(n_pol, n_chan); std::vector> data(n_chan * n_pol, 0.0); size_t index = 0; for (size_t a1 = 0; a1 != n_ant; ++a1) { for (size_t a2 = a1; a2 != n_ant; ++a2) { buffer.SetData(index, a1, a2, data.data()); ++index; } } const TimeBlockBuffer> out = EncodeDecode(block_normalization, buffer, n_pol, n_chan, n_ant); std::complex dataFromOut[n_chan * n_pol]; for (size_t row = 0; row != out.NRows(); row++) { // skip auto-correlations of AF, since these are not saved. out.GetData(row, dataFromOut); if (block_normalization != Normalization::kAF || (row != 0 && row != 4 && row != 7 && row != 9)) { for (size_t ch = 0; ch != n_chan; ++ch) { BOOST_CHECK_MESSAGE(std::isfinite(dataFromOut[ch].real()), "Real output{" << dataFromOut[ch] << "} is finite, row " << row << " with normalization " << int(block_normalization)); BOOST_CHECK_MESSAGE(std::isfinite(dataFromOut[ch].imag()), "Imaginary output{" << dataFromOut[ch] << "} is finite, row " << row << " with normalization " << int(block_normalization)); BOOST_CHECK_EQUAL(dataFromOut[ch].real(), 0.0); BOOST_CHECK_EQUAL(dataFromOut[ch].imag(), 0.0); } } } } BOOST_AUTO_TEST_CASE(af_normalization_with_zeros) { TestZeroEncoding(Normalization::kAF); } BOOST_AUTO_TEST_CASE(rf_normalization_with_zeros) { TestZeroEncoding(Normalization::kRF); } BOOST_AUTO_TEST_CASE(row_normalization_with_zeros) { TestZeroEncoding(Normalization::kRow); } BOOST_AUTO_TEST_CASE(rf_dynamic_range) { constexpr size_t n_ant = 3; constexpr size_t n_chan = 3; constexpr size_t n_pol = 1; constexpr Normalization block_normalization = Normalization::kRF; TimeBlockBuffer> buffer(n_pol, n_chan); // row, ant1, ant2 constexpr std::complex normal_row_values[n_chan] = {{0.1, 0.1}, {1.0, 1.0}, {1.0, 1.0}}; constexpr std::complex weird_row_values[n_chan] = {{1.0, 1.0}, {1e8, 1e8}, {1e8, 1e8}}; constexpr size_t weird_antenna = 1; size_t row = 0; for(size_t a1=0; a1!=n_ant; ++a1) { for(size_t a2=a1; a2!=n_ant; ++a2) { if(a1 == weird_antenna && a2 == weird_antenna) buffer.SetData(row, a1, a2, weird_row_values); else buffer.SetData(row, a1, a2, normal_row_values); ++row; } } const TimeBlockBuffer> out = EncodeDecode(block_normalization, buffer, n_pol, n_chan, n_ant); row = 0; for(size_t a1=0; a1!=n_ant; ++a1) { for(size_t a2=a1; a2!=n_ant; ++a2) { // skip auto-correlations of AF, since these are not saved. std::complex out_data[n_chan * n_pol]; out.GetData(row, out_data); for (size_t ch = 0; ch != n_chan; ++ch) { BOOST_CHECK_MESSAGE(std::isfinite(out_data[ch].real()), "Real output{" << out_data[ch] << "} is finite, row " << row); BOOST_CHECK_MESSAGE(std::isfinite(out_data[ch].imag()), "Imaginary output{" << out_data[ch] << "} is finite, row " << row); if(a1 != weird_antenna || a2 != weird_antenna) { const double expected = (ch == 0) ? 0.1 : 1.0; BOOST_CHECK_CLOSE_FRACTION(out_data[ch].real(), expected, 1e-4); BOOST_CHECK_CLOSE_FRACTION(out_data[ch].imag(), expected, 1e-4); } else if(ch != 0) { // We don't test channel 0 because it can (and may) have a large error, because // it is the only low value in this row. BOOST_CHECK_CLOSE_FRACTION(out_data[ch].real(), 1e8, 1e-4); BOOST_CHECK_CLOSE_FRACTION(out_data[ch].imag(), 1e8, 1e-4); } } ++row; } } } BOOST_AUTO_TEST_SUITE_END() casacore-3.7.1/tables/Dysco/threadeddyscocolumn.cc000066400000000000000000000331111476623553700222250ustar00rootroot00000000000000#include "threadeddyscocolumn.h" #include "dyscostman.h" #include "dyscostmanerror.h" #include "bytepacker.h" #include "threadgroup.h" #include #include #include #include namespace dyscostman { template ThreadedDyscoColumn::ThreadedDyscoColumn(DyscoStMan *parent, int dtype) : DyscoStManColumn(parent, dtype), _bitsPerSymbol(0), _ant1Col(), _ant2Col(), _fieldCol(), _packedBlockReadBuffer(), _unpackedSymbolReadBuffer(), _stopThreads(false), _currentBlock(std::numeric_limits::max()), _isCurrentBlockChanged(false), _blockSize(0), _antennaCount(0), _timeBlockBuffer() {} // prepare the class for destruction when the derived class is destructed. // this is necessary because the virtual function of the derived class might get // called to empty the cache. template void ThreadedDyscoColumn::shutdown() { if (_isCurrentBlockChanged) storeBlock(); stopThreads(); } template ThreadedDyscoColumn::~ThreadedDyscoColumn() { shutdown(); } template void ThreadedDyscoColumn::stopThreads() { std::unique_lock lock(_mutex); if (_threadGroup.empty()) { if (!_cache.empty()) throw DyscoStManError( "DyscoStMan is flushed before at least two timeblocks were stored. " "DyscoStMan can not handle this situation."); } else { // Don't stop threads before cache is empty while (!_cache.empty()) _cacheChangedCondition.wait(lock); // Signal threads to stop _stopThreads = true; _cacheChangedCondition.notify_all(); // Wait for threads to end lock.unlock(); _threadGroup.join_all(); } } template void ThreadedDyscoColumn::setShapeColumn( const casacore::IPosition &shape) { _shape = shape; } template void ThreadedDyscoColumn::loadBlock(size_t blockIndex) { if (blockIndex < nBlocksInFile()) { readCompressedData(blockIndex, _packedBlockReadBuffer.data(), _blockSize); const size_t nPolarizations = _shape[0], nChannels = _shape[1], nRows = nRowsInBlock(), nMetaFloats = metaDataFloatCount(nRows, nPolarizations, nChannels, _antennaCount); unsigned char *symbolStart = _packedBlockReadBuffer.data() + nMetaFloats * sizeof(float); BytePacker::unpack(_bitsPerSymbol, _unpackedSymbolReadBuffer.data(), symbolStart, symbolCount(nRows, nPolarizations, nChannels)); float *metaData = reinterpret_cast(_packedBlockReadBuffer.data()); initializeDecode(_timeBlockBuffer.get(), metaData, nRows, _antennaCount); uint64_t startRow = getRowIndex(blockIndex); _timeBlockBuffer->resize(nRows); for (size_t blockRow = 0; blockRow != nRows; ++blockRow) { int a1 = (*_ant1Col)(startRow + blockRow), a2 = (*_ant2Col)(startRow + blockRow); decode(_timeBlockBuffer.get(), _unpackedSymbolReadBuffer.data(), blockRow, a1, a2); } } _currentBlock = blockIndex; _isCurrentBlockChanged = false; } template void ThreadedDyscoColumn::getValues( casacore::rownr_t rowNr, casacore::Array *dataArr) { if (!areOffsetsInitialized()) { // Trying to read before first block was written -- return zero // TODO if a few rows were written of the first block, those are // incorrectly returned. This is a rare case but can be fixed. *dataArr = DataType(); } else { size_t blockIndex = getBlockIndex(rowNr); if (blockIndex >= nBlocksInFile()) { // Trying to read a row that was not stored yet -- return zero *dataArr = DataType(); } else { // Make sure array storage is contiguous. casacore::Bool deleteIt; DataType* dataPtr = dataArr->getStorage (deleteIt); std::unique_lock lock(_mutex); // Wait until the block to be read is not in the write cache typename cache_t::const_iterator cacheItemPtr = _cache.find(blockIndex); while (cacheItemPtr != _cache.end()) { _cacheChangedCondition.wait(lock); cacheItemPtr = _cache.find(blockIndex); } lock.unlock(); if (_currentBlock != blockIndex) { if (_isCurrentBlockChanged) storeBlock(); loadBlock(blockIndex); } // The time block encoder is now initialized and contains the unpacked // block. _timeBlockBuffer->GetData(getRowWithinBlock(rowNr), dataPtr); dataArr->putStorage (dataPtr, deleteIt); } } } template void ThreadedDyscoColumn::storeBlock() { // Put the data of the current block into the cache so that the parallell // threads can write them std::unique_lock lock(_mutex); CacheItem *item = new CacheItem(std::move(_timeBlockBuffer)); // Wait until there is space available AND the row to be written is not in the // cache typename cache_t::iterator cacheItemPtr = _cache.find(_currentBlock); while (_cache.size() >= maxCacheSize() || cacheItemPtr != _cache.end()) { _cacheChangedCondition.wait(lock); cacheItemPtr = _cache.find(_currentBlock); } _cache.insert(typename cache_t::value_type(_currentBlock, item)); _cacheChangedCondition.notify_all(); lock.unlock(); _isCurrentBlockChanged = false; const size_t nPolarizations = _shape[0], nChannels = _shape[1]; _timeBlockBuffer.reset( new TimeBlockBuffer(nPolarizations, nChannels)); //_timeBlockBuffer->SetNAntennae(_antennaCount); } template void ThreadedDyscoColumn::putValues( casacore::rownr_t rowNr, const casacore::Array *dataArr) { // Make sure array storage is contiguous. casacore::Bool deleteIt; const DataType* dataPtr = dataArr->getStorage (deleteIt); if (!areOffsetsInitialized()) { // If the manager did not initialize its offsets yet, then it is determined // from the first "time block" (a block with the same time, field and spw) // that is written into the measurement set. // // This only happens when a new measurement // set was created; if an existing measurement set is opened with at least // one block, then the offsets will be read from the headers. // // A consequence of this is that the first blocks in a new measurement set // are required to be written consecutively. double time = (*_timeCol)(rowNr); int fieldId = (*_fieldCol)(rowNr); int dataDescId = (*_dataDescIdCol)(rowNr); if (_timeBlockBuffer->Empty()) { // This is the first row written _currentBlock = 0; _lastWrittenTime = time; _lastWrittenField = fieldId; _lastWrittenDataDescId = dataDescId; } else if (time != _lastWrittenTime || fieldId != _lastWrittenField || dataDescId != _lastWrittenDataDescId) { initializeRowsPerBlock(rowNr, _timeBlockBuffer->MaxAntennaIndex() + 1); } } const int ant1 = (*_ant1Col)(rowNr), ant2 = (*_ant2Col)(rowNr); if (areOffsetsInitialized()) { const size_t blockIndex = getBlockIndex(rowNr), blockRow = getRowWithinBlock(rowNr); // Is this the first row of a new block? if (blockIndex != _currentBlock) { if (_isCurrentBlockChanged) storeBlock(); // Load new block loadBlock(blockIndex); } _timeBlockBuffer->SetData(blockRow, ant1, ant2, dataPtr); } else { _timeBlockBuffer->SetData(rowNr, ant1, ant2, dataPtr); } _isCurrentBlockChanged = true; dataArr->freeStorage (dataPtr, deleteIt); } template void ThreadedDyscoColumn::Prepare(DyscoDistribution, Normalization, double /*studentsTNu*/, double /*distributionTruncation*/) { stopThreads(); casacore::Table &table = storageManager().table(); _ant1Col.reset(new casacore::ScalarColumn(table, "ANTENNA1")); _ant2Col.reset(new casacore::ScalarColumn(table, "ANTENNA2")); _fieldCol.reset(new casacore::ScalarColumn(table, "FIELD_ID")); _dataDescIdCol.reset(new casacore::ScalarColumn(table, "DATA_DESC_ID")); _timeCol.reset(new casacore::ScalarColumn(table, "TIME")); size_t nPolarizations = _shape[0], nChannels = _shape[1]; _timeBlockBuffer.reset( new TimeBlockBuffer(nPolarizations, nChannels)); if (_antennaCount != 0) { // TODO _timeBlockEncoder->SetNAntennae(_antennaCount); } _currentBlock = std::numeric_limits::max(); } template size_t ThreadedDyscoColumn::defaultThreadCount() const { // Don't spawn more than 8 threads; it causes problems in NDPPP return std::min(8l, sysconf(_SC_NPROCESSORS_ONLN)); } template void ThreadedDyscoColumn::InitializeAfterNRowsPerBlockIsKnown() { stopThreads(); if (_bitsPerSymbol == 0) throw DyscoStManError( "bitsPerSymbol not initialized in ThreadedDyscoColumn"); _antennaCount = nAntennae(); _blockSize = CalculateBlockSize(nRowsInBlock(), _antennaCount); _packedBlockReadBuffer.resize(_blockSize); const size_t nPolarizations = _shape[0], nChannels = _shape[1]; _unpackedSymbolReadBuffer.resize( symbolCount(nRowsInBlock(), nPolarizations, nChannels)); // TODO _timeBlockEncoder->SetNAntennae(_antennaCount); // start the threads size_t threadCount = defaultThreadCount(); EncodingThreadFunctor functor; functor.parent = this; _stopThreads = false; for (size_t i = 0; i != threadCount; ++i) _threadGroup.create_thread(functor); } template void ThreadedDyscoColumn::encodeAndWrite( size_t blockIndex, const CacheItem &item, unsigned char *packedSymbolBuffer, unsigned int *unpackedSymbolBuffer, ThreadDataBase *threadUserData) { const size_t nPolarizations = _shape[0], nChannels = _shape[1]; const size_t metaDataSize = sizeof(float) * metaDataFloatCount(nRowsInBlock(), nPolarizations, nChannels, _antennaCount); const size_t nSymbols = symbolCount(nRowsInBlock(), nPolarizations, nChannels); float *metaBuffer = reinterpret_cast(packedSymbolBuffer); unsigned char *binaryBuffer = packedSymbolBuffer + metaDataSize; encode(threadUserData, item.encoder.get(), metaBuffer, unpackedSymbolBuffer, _antennaCount); BytePacker::pack(_bitsPerSymbol, binaryBuffer, unpackedSymbolBuffer, nSymbols); const size_t binarySize = BytePacker::bufferSize(nSymbols, _bitsPerSymbol); writeCompressedData(blockIndex, packedSymbolBuffer, metaDataSize + binarySize); } // Continuously write items from the cache into the measurement // set untill asked to quit. template void ThreadedDyscoColumn::EncodingThreadFunctor::operator()() { const size_t nPolarizations = parent->_shape[0], nChannels = parent->_shape[1]; const size_t nSymbols = parent->symbolCount(parent->nRowsInBlock(), nPolarizations, nChannels); std::unique_lock lock(parent->_mutex); ao::uvector packedSymbolBuffer(parent->_blockSize); ao::uvector unpackedSymbolBuffer(nSymbols); cache_t &cache = parent->_cache; std::unique_ptr threadUserData = parent->initializeEncodeThread(); while (!parent->_stopThreads) { typename cache_t::iterator i; bool isItemAvailable = parent->isWriteItemAvailable(i); while (!isItemAvailable && !parent->_stopThreads) { parent->_cacheChangedCondition.wait(lock); isItemAvailable = parent->isWriteItemAvailable(i); } if (isItemAvailable) { size_t blockIndex = i->first; CacheItem &item = *i->second; item.isBeingWritten = true; lock.unlock(); parent->encodeAndWrite(blockIndex, item, &packedSymbolBuffer[0], &unpackedSymbolBuffer[0], threadUserData.get()); lock.lock(); delete &item; cache.erase(i); parent->_cacheChangedCondition.notify_all(); } } } // This function should only be called with a locked mutex template bool ThreadedDyscoColumn::isWriteItemAvailable( typename cache_t::iterator &i) { i = _cache.begin(); while (i != _cache.end() && i->second->isBeingWritten) ++i; return (i != _cache.end()); } template size_t ThreadedDyscoColumn::CalculateBlockSize( size_t nRowsInBlock, size_t nAntennae) const { size_t nPolarizations = _shape[0], nChannels = _shape[1]; const size_t metaDataSize = sizeof(float) * metaDataFloatCount(nRowsInBlock, nPolarizations, nChannels, nAntennae); const size_t nSymbols = symbolCount(nRowsInBlock, nPolarizations, nChannels); const size_t binarySize = BytePacker::bufferSize(nSymbols, _bitsPerSymbol); return metaDataSize + binarySize; } template void ThreadedDyscoColumn::SerializeExtraHeader( std::ostream &stream) const { Header header; header.antennaCount = _antennaCount; header.blockSize = _blockSize; header.Serialize(stream); } template void ThreadedDyscoColumn::UnserializeExtraHeader( std::istream &stream) { Header header; header.Unserialize(stream); _antennaCount = header.antennaCount; _blockSize = header.blockSize; } template class ThreadedDyscoColumn>; template class ThreadedDyscoColumn; } // namespace dyscostman casacore-3.7.1/tables/Dysco/threadeddyscocolumn.h000066400000000000000000000173421476623553700220770ustar00rootroot00000000000000#ifndef DYSCO_THREADED_DYSCO_COLUMN_H #define DYSCO_THREADED_DYSCO_COLUMN_H #include #include #include #include #include #include #include #include #include #include "dyscostmancol.h" #include "serializable.h" #include "stochasticencoder.h" #include "threadgroup.h" #include "timeblockbuffer.h" namespace dyscostman { class DyscoStMan; /** * A column for storing compressed values in a threaded way, tailored for the * data and weight columns that use a threaded approach for encoding. * @author André Offringa */ template class ThreadedDyscoColumn : public DyscoStManColumn { public: typedef DataType data_t; /** * Create a new column. Internally called by DyscoStMan when creating a * new column. */ ThreadedDyscoColumn(DyscoStMan *parent, int dtype); ThreadedDyscoColumn(const ThreadedDyscoColumn &source) = delete; void operator=(const ThreadedDyscoColumn &source) = delete; /** Destructor. */ virtual ~ThreadedDyscoColumn(); /** Set the dimensions of values in this column. */ virtual void setShapeColumn(const casacore::IPosition &shape) override; /** Get the dimensions of the values in a particular row. * The rownr parameter is not used as the shape is the same for all rows. */ virtual casacore::IPosition shape(casacore::rownr_t /*rownr*/) override { return _shape; } /** * Read the values for a particular row. This will read the required * data and decode it. * @param rowNr The row number to get the values for. * @param dataPtr The array of values, which should be a contiguous array. */ virtual void getArrayV( casacore::rownr_t rowNr, casacore::ArrayBase &dataPtr) override { return DyscoStManColumn::getArrayV(rowNr, dataPtr); } /** * Write values into a particular row. This will add the values into the cache * and returns immediately afterwards. A pool of threads will encode the items * in the cache and write them to disk. * @param rowNr The row number to write the values to. * @param dataPtr The data pointer, which should be a contiguous array. */ virtual void putArrayV( casacore::rownr_t rowNr, const casacore::ArrayBase &dataPtr) override { return DyscoStManColumn::putArrayV(rowNr, dataPtr); } virtual void Prepare(DyscoDistribution distribution, Normalization normalization, double studentsTNu, double distributionTruncation) override; /** * Prepare this column for reading/writing. Used internally by the stman. */ virtual void InitializeAfterNRowsPerBlockIsKnown() override; /** * Set the bits per symbol. Should only be called by DyscoStMan. * @param bitsPerSymbol New number of bits per symbol. */ void SetBitsPerSymbol(unsigned bitsPerSymbol) { _bitsPerSymbol = bitsPerSymbol; } virtual size_t CalculateBlockSize(size_t nRowsInBlock, size_t nAntennae) const final override; virtual size_t ExtraHeaderSize() const override { return Header::Size(); } virtual void SerializeExtraHeader(std::ostream &stream) const final override; virtual void UnserializeExtraHeader(std::istream &stream) final override; protected: class ThreadDataBase { public: virtual ~ThreadDataBase(){}; }; typedef typename TimeBlockBuffer::symbol_t symbol_t; virtual void initializeDecode(TimeBlockBuffer *buffer, const float *metaBuffer, size_t nRow, size_t nAntennae) = 0; virtual void decode(TimeBlockBuffer *buffer, const symbol_t *data, size_t blockRow, size_t a1, size_t a2) = 0; virtual std::unique_ptr initializeEncodeThread() = 0; virtual void encode(ThreadDataBase *threadData, TimeBlockBuffer *buffer, float *metaBuffer, symbol_t *symbolBuffer, size_t nAntennae) = 0; virtual size_t metaDataFloatCount(size_t nRow, size_t nPolarizations, size_t nChannels, size_t nAntennae) const = 0; virtual size_t symbolCount(size_t nRowsInBlock, size_t nPolarizations, size_t nChannels) const = 0; virtual void shutdown() override final; virtual size_t defaultThreadCount() const; size_t getBitsPerSymbol() const { return _bitsPerSymbol; } const casacore::IPosition &shape() const { return _shape; } private: struct CacheItem { CacheItem(std::unique_ptr> &&encoder_) : encoder(std::move(encoder_)), isBeingWritten(false) {} std::unique_ptr> encoder; bool isBeingWritten; }; struct EncodingThreadFunctor { void operator()(); ThreadedDyscoColumn *parent; }; struct Header : public Serializable { uint32_t blockSize; uint32_t antennaCount; static uint32_t Size() { return 8; } virtual void Serialize(std::ostream &stream) const override { SerializeToUInt32(stream, blockSize); SerializeToUInt32(stream, antennaCount); } virtual void Unserialize(std::istream &stream) override { blockSize = UnserializeUInt32(stream); antennaCount = UnserializeUInt32(stream); } }; typedef std::map cache_t; void getValues(casacore::rownr_t rowNr, casacore::Array *dataPtr); void putValues(casacore::rownr_t rowNr, const casacore::Array *dataPtr); void stopThreads(); void encodeAndWrite(size_t blockIndex, const CacheItem &item, unsigned char *packedSymbolBuffer, unsigned int *unpackedSymbolBuffer, ThreadDataBase *threadUserData); bool isWriteItemAvailable(typename cache_t::iterator &i); void loadBlock(size_t blockIndex); void storeBlock(); size_t maxCacheSize() const { return ThreadedDyscoColumn::defaultThreadCount() * 12 / 10 + 1; } unsigned _bitsPerSymbol; casacore::IPosition _shape; std::unique_ptr> _ant1Col, _ant2Col, _fieldCol, _dataDescIdCol; std::unique_ptr> _timeCol; double _lastWrittenTime; int _lastWrittenField, _lastWrittenDataDescId; ao::uvector _packedBlockReadBuffer; ao::uvector _unpackedSymbolReadBuffer; cache_t _cache; bool _stopThreads; std::mutex _mutex; threadgroup _threadGroup; std::condition_variable _cacheChangedCondition; size_t _currentBlock; bool _isCurrentBlockChanged; size_t _blockSize; size_t _antennaCount; std::unique_ptr> _timeBlockBuffer; }; template <> inline void ThreadedDyscoColumn>::getArrayV( casacore::rownr_t rowNr, casacore::ArrayBase &dataPtr) { getValues(rowNr, static_cast>*>(&dataPtr)); } template <> inline void ThreadedDyscoColumn>::putArrayV( casacore::rownr_t rowNr, const casacore::ArrayBase &dataPtr) { putValues(rowNr, static_cast>*>(&dataPtr)); } template <> inline void ThreadedDyscoColumn::getArrayV( casacore::rownr_t rowNr, casacore::ArrayBase &dataPtr) { getValues(rowNr, static_cast*>(&dataPtr)); } template <> inline void ThreadedDyscoColumn::putArrayV( casacore::rownr_t rowNr, const casacore::ArrayBase &dataPtr) { putValues(rowNr, static_cast*>(&dataPtr)); } extern template class ThreadedDyscoColumn>; extern template class ThreadedDyscoColumn; } // namespace dyscostman #endif casacore-3.7.1/tables/Dysco/threadgroup.h000066400000000000000000000021421476623553700203530ustar00rootroot00000000000000#ifndef DYSCO_THREADGROUP_H #define DYSCO_THREADGROUP_H #include #include #include namespace dyscostman { /** * Group of threads. */ class threadgroup { public: /** Constructor */ threadgroup() {} /** Destructor. Will join all threads that have not been joined yet. */ ~threadgroup() { join_all(); } /** * Create a new thread that will execute the given functor. The new thread * will be added to the group. * @param threadFunc The functor to be called. */ template void create_thread(T threadFunc) { _threads.emplace_back(threadFunc); } /** * Join all threads in the group that have not yet been joined. */ void join_all() { for (std::thread &t : _threads) { t.join(); } _threads.clear(); } /** * Get state of thread group. * @returns true when there are unjoined threads in the group. Not * synchronized -- caller has to make sure that thread is safe. */ bool empty() const { return _threads.empty(); } private: std::vector _threads; }; } // namespace dyscostman #endif casacore-3.7.1/tables/Dysco/timeblockbuffer.h000066400000000000000000000044511476623553700211770ustar00rootroot00000000000000#ifndef DYSCO_TIME_BLOCK_BUFFER_H #define DYSCO_TIME_BLOCK_BUFFER_H #include "uvector.h" #include #include template class TimeBlockBuffer { public: typedef unsigned symbol_t; TimeBlockBuffer(size_t nPol, size_t nChannels) : _nPol(nPol), _nChannels(nChannels) {} bool Empty() const { return _data.empty(); } void resize(size_t nRows) { _data.resize(nRows); } struct DataRow { size_t antenna1, antenna2; std::vector visibilities; }; DataRow &operator[](size_t rowIndex) { return _data[rowIndex]; } void ResetData() { _data.clear(); } void SetData(size_t blockRow, size_t antenna1, size_t antenna2, const data_t *data) { if (_data.size() <= blockRow) _data.resize(blockRow + 1); DataRow &newRow = _data[blockRow]; newRow.antenna1 = antenna1; newRow.antenna2 = antenna2; newRow.visibilities.resize(_nPol * _nChannels); for (size_t i = 0; i != _nPol * _nChannels; ++i) newRow.visibilities[i] = data[i]; } void GetData(size_t blockRow, data_t *destination) const { const data_t *srcPtr = _data[blockRow].visibilities.data(); memcpy(destination, srcPtr, sizeof(data_t) * _data[blockRow].visibilities.size()); } size_t NRows() const { return _data.size(); } size_t MaxAntennaIndex() const { size_t maxAntennaIndex = 0; for (const DataRow &row : _data) { maxAntennaIndex = std::max(maxAntennaIndex, std::max(row.antenna1, row.antenna2)); } return maxAntennaIndex; } const std::vector &GetVector() const { return _data; } std::vector &GetVector() { return _data; } template void ConvertVector( std::vector::DataRow> &vector) const { vector.resize(_data.size()); for (size_t i = 0; i != _data.size(); ++i) { const DataRow &rowIn = _data[i]; typename TimeBlockBuffer::DataRow &rowOut = vector[i]; rowOut.antenna1 = rowIn.antenna1; rowOut.antenna2 = rowIn.antenna2; rowOut.visibilities.assign(rowIn.visibilities.begin(), rowIn.visibilities.end()); } } private: size_t _nPol, _nChannels; std::vector _data; }; template class TimeBlockBuffer>; #endif casacore-3.7.1/tables/Dysco/timeblockencoder.h000066400000000000000000000042621476623553700213450ustar00rootroot00000000000000#ifndef DYSCO_TIME_BLOCK_ENCODER_H #define DYSCO_TIME_BLOCK_ENCODER_H #include "stochasticencoder.h" #include "timeblockbuffer.h" #include "uvector.h" #include #include #include class RMSMeasurement { public: RMSMeasurement() : _count(0), _value(0.0) {} void Include(const std::complex &val) { if (isfinite(val)) { _count++; _value += val.real() * val.real() + val.imag() * val.imag(); } } double RMS() const { return sqrt(_value / (_count * 2)); } private: static bool isfinite(const std::complex &val) { return std::isfinite(val.real()) && std::isfinite(val.imag()); } size_t _count; double _value; }; class TimeBlockEncoder { public: typedef TimeBlockBuffer> FBuffer; typedef typename TimeBlockBuffer>::DataRow FBufferRow; typedef TimeBlockBuffer> DBuffer; typedef typename TimeBlockBuffer>::DataRow DBufferRow; typedef unsigned symbol_t; virtual ~TimeBlockEncoder() {} virtual void EncodeWithDithering( const dyscostman::StochasticEncoder &gausEncoder, FBuffer &buffer, float *metaBuffer, symbol_t *symbolBuffer, size_t antennaCount, std::mt19937 &rnd) = 0; virtual void EncodeWithoutDithering( const dyscostman::StochasticEncoder &gausEncoder, FBuffer &buffer, float *metaBuffer, symbol_t *symbolBuffer, size_t antennaCount) = 0; virtual void InitializeDecode(const float *metaBuffer, size_t nRow, size_t nAntennae) = 0; virtual void Decode(const dyscostman::StochasticEncoder &gausEncoder, FBuffer &buffer, const symbol_t *symbolBuffer, size_t blockRow, size_t antenna1, size_t antenna2) = 0; virtual size_t SymbolCount(size_t nRow, size_t nPol, size_t nChannels) const = 0; virtual size_t SymbolCount(size_t nRow) const = 0; virtual size_t SymbolsPerRow() const = 0; virtual size_t MetaDataCount(size_t nRow, size_t nPol, size_t nChannels, size_t nAntennae) const = 0; protected: TimeBlockEncoder() {} }; #endif casacore-3.7.1/tables/Dysco/uvector.h000066400000000000000000001213261476623553700175240ustar00rootroot00000000000000#ifndef AO_UVECTOR_H #define AO_UVECTOR_H #include #include #include #include #include #include /** * @file uvector.h * Header file for uvector and its relational and swap functions. * @author André Offringa * @copyright André Offringa, 2013, distributed under the GPL license version 3. */ namespace ao { /** * @defgroup uvector Class uvector and related functions. * @{ */ /** * @brief A container similar to std::vector, but one that allows construction without initializing its elements. * @details This container is similar to a std::vector, except that it can be constructed without * initializing its elements. This saves the overhead of initialization, hence the * constructor @ref uvector(size_t) is significantly faster than the corresponding std::vector * constructor, and has no overhead compared to a manually allocated array. * * Probably its greatest strength lies in the construction of containers with a number of elements * that is runtime defined, but that will be initialized later. For example: * * @code * // Open a file * ifstream file("myfile.bin"); * * // Construct a buffer for this file * uvector buffer(buffer_size); * * // Read some data into the buffer * file.read(&buffer[0], buffer_size); * @endcode * * However, it has a few more use-cases with improved performance over std::vector. This is * possible because of more strengent requirements on the element's type. * * The container will behave correctly with any trivial type, but will not work for almost * all non-trivial types. * * The methods with different semantics compared to std::vector are: * * @ref uvector(size_t n) * * @ref resize(size_t n) * * Also the following new members are introduced: * * @ref insert_uninitialized(const_iterator position, size_t n) * * @ref push_back(InputIterator first, InputIterator last) * * @ref push_back(size_t n, const Tp& val) * * @ref push_back(std::initializer_list initlist) * * @ref push_back_uninitialized(size_t n) * * All other members work exactly like std::vector's members, although some are slightly faster because of * the stricter requirements on the element type. * * @tparam Tp Container's element type * @tparam Alloc Allocator type. Default is to use the std::allocator. * * @author André Offringa * @copyright André Offringa, 2013, distributed under the GPL license version 3. */ template > class uvector : private Alloc { static_assert(std::is_standard_layout(), "A uvector can only hold classes with standard layout"); public: /// Element type typedef Tp value_type; /// Type of allocator used to allocate and deallocate space typedef Alloc allocator_type; /// Reference to element type typedef Tp& reference; /// Constant reference to element type typedef const Tp& const_reference; /// Pointer to element type typedef Tp* pointer; /// Pointer to constant element type typedef const Tp* const_pointer; /// Iterator type typedef Tp* iterator; /// Iterator type of constant elements typedef const Tp* const_iterator; /// Reverse iterator type typedef std::reverse_iterator reverse_iterator; /// Reverse iterator of constant elements typedef std::reverse_iterator const_reverse_iterator; /// Difference between to iterators typedef std::ptrdiff_t difference_type; /// Type used for indexing elements typedef std::size_t size_t; /// Type used for indexing elements typedef std::size_t size_type; private: #if __cplusplus > 201402L typedef typename std::allocator_traits::is_always_equal allocator_is_always_equal; #else typedef std::false_type allocator_is_always_equal; #endif pointer _begin, _end, _endOfStorage; public: /** @brief Construct an empty uvector. * @param allocator Allocator used for allocating and deallocating memory. */ explicit uvector(const allocator_type& allocator = Alloc()) noexcept : Alloc(allocator), _begin(nullptr), _end(nullptr), _endOfStorage(nullptr) { } /** @brief Construct a vector with given amount of elements, without initializing these. * @details This constructor deviates from std::vector's behaviour, because it will not * value construct its elements. It is therefore faster than the corresponding constructor * of std::vector. * @param n Number of elements that the uvector will be initialized with. */ explicit uvector(size_t n) : _begin(allocate(n)), _end(_begin + n), _endOfStorage(_end) { } /** @brief Construct a vector with given amount of elements and set these to a specific value. * @details This constructor will initialize its members with the given value. * @param n Number of elements that the uvector will be initialized with. * @param val Value to initialize all elements with * @param allocator Allocator used for allocating and deallocating memory. */ uvector(size_t n, const value_type& val, const allocator_type& allocator = Alloc()) : Alloc(allocator), _begin(allocate(n)), _end(_begin + n), _endOfStorage(_end) { std::uninitialized_fill_n(_begin, n, val); } /** @brief Construct a vector by copying elements from a range. * @param first Iterator to range start * @param last Iterator to range end * @param allocator Allocator used for allocating and deallocating memory. */ template uvector(InputIterator first, InputIterator last, const allocator_type& allocator = Alloc()) : Alloc(allocator) { construct_from_range(first, last, std::is_integral()); } /** @brief Copy construct a uvector. * @details The allocator of the new uvector will be initialized from * @c std::allocator_traits::select_on_container_copy_construction(other). * @param other Source uvector to be copied from. */ uvector(const uvector& other) : Alloc(std::allocator_traits::select_on_container_copy_construction(static_cast(other))), _begin(allocate(other.size())), _end(_begin + other.size()), _endOfStorage(_end) { std::copy(other._begin, other._end, _begin); } /** @brief Copy construct a uvector with custom allocator. * @param other Source uvector to be copied from. * @param allocator Allocator used for allocating and deallocating memory. */ uvector(const uvector& other, const allocator_type& allocator) : Alloc(allocator), _begin(allocate(other.size())), _end(_begin + other.size()), _endOfStorage(_end) { std::copy(other._begin, other._end, _begin); } /** @brief Move construct a uvector. * @param other Source uvector to be moved from. */ uvector(uvector&& other) noexcept : Alloc(std::move(other)), _begin(other._begin), _end(other._end), _endOfStorage(other._endOfStorage) { other._begin = nullptr; other._end = nullptr; other._endOfStorage = nullptr; } /** @brief Move construct a uvector with custom allocator. * @param other Source uvector to be moved from. * @param allocator Allocator used for allocating and deallocating memory. */ uvector(uvector&& other, const allocator_type& allocator) noexcept : Alloc(allocator), _begin(other._begin), _end(other._end), _endOfStorage(other._endOfStorage) { other._begin = nullptr; other._end = nullptr; other._endOfStorage = nullptr; } /** @brief Construct a uvector from a initializer list. * @param initlist Initializer list used for initializing the new uvector. * @param allocator Allocator used for allocating and deallocating memory. */ uvector(std::initializer_list initlist, const allocator_type& allocator = Alloc()) : Alloc(allocator), _begin(allocate(initlist.size())), _end(_begin + initlist.size()), _endOfStorage(_end) { iterator destIter = _begin; for(typename std::initializer_list::const_iterator i=initlist.begin(); i!=initlist.end(); ++i) { *destIter = *i; ++destIter; } } /** @brief Destructor. */ ~uvector() noexcept { deallocate(); } /** @brief Assign another uvector to this uvector. * @details The allocator of the uvector will be assigned to @p other when * std::allocator_traits::propagate_on_container_copy_assignment() is of true_type. */ uvector& operator=(const uvector& other) { return assign_copy_from(other, typename std::allocator_traits::propagate_on_container_copy_assignment()); } /** @brief Assign another uvector to this uvector. * @details The allocator of the uvector will be assigned to @p other when * std::allocator_traits::propagate_on_container_move_assignment() is of true_type. */ uvector& operator=(uvector&& other) noexcept( std::allocator_traits::propagate_on_container_move_assignment::value|| allocator_is_always_equal::value) { return assign_move_from(std::move(other), typename std::allocator_traits::propagate_on_container_move_assignment()); } /** @brief Get iterator to first element. */ iterator begin() noexcept { return _begin; } /** @brief Get constant iterator to first element. */ const_iterator begin() const noexcept { return _begin; } /** @brief Get iterator to element past last element. */ iterator end() noexcept { return _end; } /** @brief Get constant iterator to element past last element. */ const_iterator end() const noexcept { return _end; } /** @brief Get reverse iterator to last element. */ reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } /** @brief Get constant reverse iterator to last element. */ const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); } /** @brief Get reverse iterator to element before first element. */ reverse_iterator rend() noexcept { return reverse_iterator(begin()); } /** @brief Get constant reverse iterator to element before first element. */ const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } /** @brief Get constant iterator to first element. */ const_iterator cbegin() const noexcept { return _begin; } /** @brief Get constant iterator to element past last element. */ const_iterator cend() const noexcept { return _end; } /** @brief Get constant reverse iterator to last element. */ const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(end()); } /** @brief Get constant reverse iterator to element before first element. */ const_reverse_iterator crend() const noexcept { return const_reverse_iterator(begin()); } /** @brief Get number of elements in container. */ size_t size() const noexcept { return _end - _begin; } /** @brief Get maximum number of elements that this container can hold. */ size_t max_size() const noexcept { return Alloc::max_size(); } /** @brief Change the number of elements in the container. * @details If the new size is larger than the current size, new values will be * left uninitialized. Therefore, it is more efficient than @c resize(size_t) in * @c std::vector, as well as @ref resize(size_t, const Tp&). * If the new size is smaller than the current size, the container will be * truncated and elements past the new size will be removed. No destructor of the * removed elements will be called. * @param n The new size of the container. */ void resize(size_t n) { if(capacity() < n) { size_t newSize = enlarge_size(n); pointer newStorage = allocate(newSize); std::move(_begin, _end, newStorage); deallocate(); _begin = newStorage; _endOfStorage = _begin + newSize; } _end = _begin + n; } /** @brief Change the number of elements in the container. * @details If the new size is larger than the current size, new values will be * initialized by the given value. * If the new size is smaller than the current size, the container will be * truncated and elements past the new size will be removed. No destructor of the * removed elements will be called. * @param n The new size of the container. * @param val New value of elements that get added to the container. */ void resize(size_t n, const Tp& val) { size_t oldSize = size(); if(capacity() < n) { pointer newStorage = allocate(n); std::move(_begin, _end, newStorage); deallocate(); _begin = newStorage; _endOfStorage = _begin + n; } _end = _begin + n; if(oldSize < n) std::uninitialized_fill(_begin + oldSize, _end, val); } /** @brief Get the number of elements the container can currently hold without reallocating storage. */ size_t capacity() const noexcept { return _endOfStorage - _begin; } /** @brief Determine if the container is currently empty. * @returns @c true if @ref size() == 0. */ bool empty() const noexcept { return _begin == _end; } /** @brief Reserve space for a number of elements, to prevent the overhead of extra * reallocations. * @details This has no effect on the working of the uvector, except that it might change * the current capacity. This can enhance performance when a large number of elements are added, * and an approximate size is known a priori. * * This method might cause a reallocation, causing iterators to be invalidated. * @param n Number of elements to reserve space for. */ void reserve(size_t n) { if(capacity() < n) { const size_t curSize = size(); pointer newStorage = allocate(n); std::move(_begin, _begin + curSize, newStorage); deallocate(); _begin = newStorage; _end = newStorage + curSize; _endOfStorage = _begin + n; } } /** @brief Change the capacity of the container such that no extra space is hold. * @details This has no effect on the working of the uvector, except that it might change * the current capacity. This can reduce the current memory usage of the container. * * This method might cause a reallocation, causing iterators to be invalidated. */ void shrink_to_fit() { const size_t curSize = size(); if(curSize == 0) { deallocate(); _begin = nullptr; _end = nullptr; _endOfStorage = nullptr; } else if(curSize < capacity()) { pointer newStorage = allocate(curSize); std::move(_begin, _begin + curSize, newStorage); deallocate(); _begin = newStorage; _end = newStorage + curSize; _endOfStorage = _begin + curSize; } } /** @brief Get a reference to the element at the given index. */ Tp& operator[](size_t index) noexcept { return _begin[index]; } /** @brief Get a constant reference to the element at the given index. */ const Tp& operator[](size_t index) const noexcept { return _begin[index]; } /** @brief Get a reference to the element at the given index with bounds checking. * @throws std::out_of_range when given index is past the last element. */ Tp& at(size_t index) { check_bounds(index); return _begin[index]; } /** @brief Get a constant reference to the element at the given index with bounds checking. * @throws std::out_of_range when given index is past the last element. */ const Tp& at(size_t index) const { check_bounds(index); return _begin[index]; } /** @brief Get reference to first element in container. */ Tp& front() noexcept { return *_begin; } /** @brief Get constant reference to first element in container. */ const Tp& front() const noexcept { return *_begin; } /** @brief Get reference to last element in container. */ Tp& back() noexcept { return *(_end - 1); } /** @brief Get constant reference to last element in container. */ const Tp& back() const noexcept { return *(_end - 1); } /** @brief Get pointer to internal storage. */ Tp* data() noexcept { return _begin; } /** @brief Get constant pointer to internal storage. */ const Tp* data() const noexcept { return _begin; } /** @brief Assign this container to be equal to the given range. * @details The container will be resized to fit the length of the given * range. Iterators are invalidated. * @param first Iterator to the beginning of the range. * @param last Iterator past the end of the range. */ template void assign(InputIterator first, InputIterator last) { assign_from_range(first, last, std::is_integral()); } /** @brief Resize the container and assign the given value to all elements. * @details Iterators are invalidated. * @param n New size of container * @param val Value to be assigned to all elements. */ void assign(size_t n, const Tp& val) { if(n > capacity()) { iterator newStorage = allocate(n); deallocate(); _begin = newStorage; _endOfStorage = _begin + n; } _end = _begin + n; std::uninitialized_fill_n(_begin, n, val); } /** @brief Assign this container to an initializer list. * @details The container will be resized to fit the length of the given * initializer list. Iterators are invalidated. * @param initlist List of values to assign to the container. */ void assign(std::initializer_list initlist) { if(initlist.size() > capacity()) { iterator newStorage = allocate(initlist.size()); deallocate(); _begin = newStorage; _endOfStorage = _begin + initlist.size(); } _end = _begin + initlist.size(); iterator destIter = _begin; for(typename std::initializer_list::const_iterator i=initlist.begin(); i!=initlist.end(); ++i) { *destIter = *i; ++destIter; } } /** @brief Add the given value to the end of the container. * @details Iterators are invalidated. * @param item Value of new element. */ void push_back(const Tp& item) { if(_end == _endOfStorage) enlarge(enlarge_size(1)); *_end = item; ++_end; } /** @brief Add the given value to the end of the container by moving it in. * @details Iterators are invalidated. * * Note that this container can only hold simple types that do not perform allocations. Therefore, * there is probably no benefit in moving the new item in over copying it in with @ref push_back(const Tp&). * @param item Value of new element. */ void push_back(Tp&& item) { if(_end == _endOfStorage) enlarge(enlarge_size(1)); *_end = std::move(item); ++_end; } /** @brief Remove the last element from the container. */ void pop_back() { --_end; } /** @brief Insert an element at a given position. * @details All iterators will be invalidated. This operation needs to move all elements after * the new element, and can therefore be expensive. * @param position Position of the new element. The new element will be added before the old element * at that position. * @param item Value of the new item. * @return Position of the new element. */ iterator insert(const_iterator position, const Tp& item) { if(_end == _endOfStorage) { size_t index = position - _begin; enlarge_for_insert(enlarge_size(1), index, 1); position = _begin + index; } else { std::move_backward(const_cast(position), _end, _end+1); ++_end; } *const_cast(position) = item; return const_cast(position); } /** @brief Insert elements at a given position and initialize them with a value. * @details All iterators will be invalidated. This operation needs to move all elements after * the new element, and can therefore be expensive. * @param position Position of the new elements. The new elements will be added before the old element * at that position. * @param n Number of elements to add. * @param val Value of the new item. * @return Position of the first new element. */ iterator insert(const_iterator position, size_t n, const Tp& val) { if(capacity() < size() + n) { size_t index = position - _begin; enlarge_for_insert(enlarge_size(n), index, n); position = _begin + index; } else { std::move_backward(const_cast(position), _end, _end+n); _end += n; } std::uninitialized_fill_n(const_cast(position), n, val); return const_cast(position); } /** @brief Insert elements at a given position and initialize them from a range. * @details All iterators will be invalidated. This operation needs to move all elements after * the new element, and can therefore be expensive. * @param position Position of the new elements. The new elements will be added before the old element * at that position. * @param first Iterator to the beginning of the range. * @param last Iterator past the end of the range. * @return Position of the first new element. */ template iterator insert(const_iterator position, InputIterator first, InputIterator last) { return insert_from_range(position, first, last, std::is_integral()); } /** @brief Insert an element at a given position by moving it in. * @details All iterators will be invalidated. This operation needs to move all elements after * the new element, and can therefore be expensive. * * Note that this container can only hold simple types that do not perform allocations. Therefore, * there is probably no benefit in moving the new item in over copying it in with * @ref insert(const_iterator, const Tp&). * @param position Position of the new element. The new element will be added before the old element * at that position. * @param item Value of the new item. * @return Position of the new element. */ iterator insert(const_iterator position, Tp&& item) { if(_end == _endOfStorage) { size_t index = position - _begin; enlarge_for_insert(enlarge_size(1), index, 1); position = _begin + index; } else { std::move_backward(const_cast(position), _end, _end+1); ++_end; } *const_cast(position) = std::move(item); return const_cast(position); } /** @brief Insert elements at a given position and initialize them from a initializer list. * @details All iterators will be invalidated. This operation needs to move all elements after * the new element, and can therefore be expensive. * @param position Position of the new elements. The new elements will be added before the old element * at that position. * @param initlist List of items to insert. * @return Position of the first new element. */ iterator insert(const_iterator position, std::initializer_list initlist) { if(capacity() < size() + initlist.size()) { size_t index = position - _begin; enlarge_for_insert(enlarge_size(initlist.size()), index, initlist.size()); position = _begin + index; } else { std::move_backward(const_cast(position), _end, _end+initlist.size()); _end += initlist.size(); } iterator destIter = const_cast(position); for(typename std::initializer_list::const_iterator i=initlist.begin(); i!=initlist.end(); ++i) { *destIter = *i; ++destIter; } return const_cast(position); } /** @brief Delete an element from the container. * @details This operation moves all elements past the removed element, and can therefore be * expensive. * @param position Position of element to be removed. * @return Iterator pointing to the first element past the delete element. */ iterator erase(const_iterator position) { std::move(const_cast(position)+1, _end, const_cast(position)); --_end; return const_cast(position); } /** @brief Delete a range of elements from the container. * @details This operation moves all elements past the removed elements, and can therefore be * expensive. * @param first Position of first element to be removed. * @param last Position past last element to be removed. * @return Iterator pointing to the first element past the delete element. */ iterator erase(const_iterator first, const_iterator last) { std::move(const_cast(last), _end, const_cast(first)); _end -= last - first; return const_cast(first); } /** @brief Swap the contents of this uvector with the given uvector. * @details Iterators to both vectors will remain valid and will point into * to the swapped container afterwards. This function will never reallocate * space. * * The allocator will be swapped when the @c propagate_on_container_swap * of the respective @c allocator_trait is @c true_type. * Its behaviour is undefined when the allocators do not compare equal and * @c propagate_on_container_swap is false. * @param other Other uvector whose contents it to be swapped with this. */ void swap(uvector& other) noexcept { swap(other, typename std::allocator_traits::propagate_on_container_swap()); } /** @brief Remove all elements from the container. */ void clear() { _end = _begin; } /** @brief Insert an element at a given position by constructing it in place. * @details All iterators will be invalidated. This operation needs to move all elements after * the new element, and can therefore be expensive. * @param position Position of the new element. The new element will be added before the old element * at that position. * @param args List of arguments to be forwarded to construct the new element. * @return Position of the new element. */ template iterator emplace(const_iterator position, Args&&... args) { if(_end == _endOfStorage) { size_t index = position - _begin; enlarge_for_insert(enlarge_size(1), index, 1); position = _begin + index; } else { std::move_backward(const_cast(position), _end, _end+1); ++_end; } *const_cast(position) = Tp(std::forward(args)...); return const_cast(position); } /** @brief Add the given value to the end of the container by constructing it in place. * @details Iterators are invalidated. * @param args List of arguments to be forwarded to construct the new element. */ template void emplace_back(Args&&... args) { if(_end == _endOfStorage) enlarge(enlarge_size(1)); *_end = Tp(std::forward(args)...); ++_end; } /** @brief Get a copy of the allocator. */ allocator_type get_allocator() const noexcept { return *this; } // --- NON STANDARD METHODS --- /** @brief Insert elements at a given position without initializing them. * @details All iterators will be invalidated. This operation needs to move all elements after * the new element, and can therefore be expensive. It will not initialize the new elements, * and is therefore faster than @ref insert(const_iterator, size_t, const Tp&). * * This method is non-standard: it is not present in std::vector. * @param position Position of the new elements. The new elements will be added before the old element * at that position. * @param n Number of elements to add. */ iterator insert_uninitialized(const_iterator position, size_t n) { if(capacity() < size() + n) { size_t index = position - _begin; enlarge_for_insert(enlarge_size(n), index, n); position = _begin + index; } else { std::move_backward(const_cast(position), _end, _end+n); _end += n; } return const_cast(position); } /** @brief Add a range of items to the end of the container. * @details All iterators will be invalidated. * * This method is non-standard: it is not present in std::vector. * @param first Iterator to the beginning of the range. * @param last Iterator past the end of the range. */ template void push_back(InputIterator first, InputIterator last) { push_back_range(first, last, std::is_integral()); } /** @brief Add elements at the end and initialize them with a value. * @details All iterators will be invalidated. * * This method is non-standard: it is not present in std::vector. * @param n Number of elements to add. * @param val Value of the new items. */ void push_back(size_t n, const Tp& val) { if(capacity() - size() < n) { enlarge(enlarge_size(n)); } std::uninitialized_fill_n(_end, n, val); _end += n; } /** @brief Add elements from an initializer list to the end of the container. * @details All iterators will be invalidated. * * This method is non-standard: it is not present in std::vector. * @param initlist The list with values to add. */ void push_back(std::initializer_list initlist) { if(capacity() - size() < initlist.size()) { enlarge(enlarge_size(initlist.size())); } for(typename std::initializer_list::iterator i = initlist.begin(); i != initlist.end(); ++i) { *_end = *i; ++_end; } } /** @brief Add elements at the end without initializing them. * @details All iterators will be invalidated. * * This method is non-standard: it is not present in std::vector. * @param n Number of elements to add. */ void push_back_uninitialized(size_t n) { resize(size() + n); } private: pointer allocate(size_t n) { return Alloc::allocate(n); } void deallocate() noexcept { deallocate(_begin, capacity()); } void deallocate(pointer begin, size_t n) noexcept { if(begin != nullptr) Alloc::deallocate(begin, n); } template void construct_from_range(InputIterator first, InputIterator last, std::false_type) { construct_from_range(first, last, typename std::iterator_traits::iterator_category()); } template void construct_from_range(Integral n, Integral val, std::true_type) { _begin = allocate(n); _end = _begin + n; _endOfStorage = _end; std::uninitialized_fill_n(_begin, n, val); } template void construct_from_range(InputIterator first, InputIterator last, std::forward_iterator_tag) { size_t n = std::distance(first, last); _begin = allocate(n); _end = _begin + n; _endOfStorage = _begin + n; Tp* destIter = _begin; while(first != last) { *destIter = *first; ++destIter; ++first; } } template void assign_from_range(InputIterator first, InputIterator last, std::false_type) { assign_from_range(first, last, typename std::iterator_traits::iterator_category()); } // This function is called from assign(iter,iter) when Tp is an integral. In that case, // the user tried to call assign(n, &val), but it got caught by the wrong overload. template void assign_from_range(Integral n, Integral val, std::true_type) { if(size_t(n) > capacity()) { iterator newStorage = allocate(n); deallocate(); _begin = newStorage; _endOfStorage = _begin + n; } _end = _begin + n; std::uninitialized_fill_n(_begin, n, val); } template void assign_from_range(InputIterator first, InputIterator last, std::forward_iterator_tag) { size_t n = std::distance(first, last); if(n > capacity()) { iterator newStorage = allocate(n); deallocate(); _begin = newStorage; _endOfStorage = _begin + n; } _end = _begin + n; Tp* destIter = _begin; while(first != last) { *destIter = *first; ++destIter; ++first; } } template iterator insert_from_range(const_iterator position, InputIterator first, InputIterator last, std::false_type) { return insert_from_range(position, first, last, typename std::iterator_traits::iterator_category()); } template iterator insert_from_range(const_iterator position, Integral n, Integral val, std::true_type) { if(capacity() < size() + n) { size_t index = position - _begin; enlarge_for_insert(enlarge_size(n), index, n); position = _begin + index; } else { std::move_backward(const_cast(position), _end, _end+n); _end += n; } std::uninitialized_fill_n(const_cast(position), n, val); return const_cast(position); } template iterator insert_from_range(const_iterator position, InputIterator first, InputIterator last, std::forward_iterator_tag) { size_t n = std::distance(first, last); if(capacity() < size() + n) { size_t index = position - _begin; enlarge_for_insert(enlarge_size(n), index, n); position = _begin + index; } else { std::move_backward(const_cast(position), _end, _end+n); _end += n; } Tp* destIter = const_cast(position); while(first != last) { *destIter = *first; ++destIter; ++first; } return const_cast(position); } void check_bounds(size_t index) const { if(index >= size()) throw std::out_of_range("Access to element in uvector past end"); } size_t enlarge_size(size_t extra_space_needed) const noexcept { return size() + std::max(size(), extra_space_needed); } void enlarge(size_t newSize) { pointer newStorage = allocate(newSize); std::copy(_begin, _end, newStorage); deallocate(); _end = newStorage + size(); _begin = newStorage; _endOfStorage = _begin + newSize; } void enlarge_for_insert(size_t newSize, size_t insert_position, size_t insert_count) { pointer newStorage = allocate(newSize); std::copy(_begin, _begin + insert_position, newStorage); std::copy(_begin + insert_position, _end, newStorage + insert_position + insert_count); deallocate(); _end = newStorage + size() + insert_count; _begin = newStorage; _endOfStorage = _begin + newSize; } // implementation of operator=(const&) without propagate_on_container_copy_assignment uvector& assign_copy_from(const uvector& other, std::false_type) { const size_t n = other.size(); if(n > capacity()) { iterator newStorage = allocate(n); deallocate(); _begin = newStorage; _end = _begin + n; _endOfStorage = _end; } std::copy(other._begin, other._begin + n, _begin); return *this; } // implementation of operator=(const&) with propagate_on_container_copy_assignment uvector& assign_copy_from(const uvector& other, std::true_type) { if(allocator_is_always_equal() || static_cast(other) == static_cast(*this)) { assign_copy_from(other, std::false_type()); } else { const size_t n = other.size(); iterator newStorage = static_cast(other).allocate(n); deallocate(); _begin = newStorage; _end = _begin + n; _endOfStorage = _end; std::copy(other._begin, other._begin + n, _begin); Alloc::operator=(static_cast(other)); } return *this; } // implementation of operator=() without propagate_on_container_move_assignment uvector& assign_move_from(uvector&& other, std::false_type) noexcept(allocator_is_always_equal::value) { if(allocator_is_always_equal::value || static_cast(other) == static_cast(*this)) { deallocate(); _begin = other._begin; _end = other._end; _endOfStorage = other._endOfStorage; other._begin = nullptr; other._end = nullptr; other._endOfStorage = nullptr; } else { // We should not propagate the allocator and the allocators are different. // This means we can not swap the allocated space, since then we would // deallocate the space with a different allocator type. Therefore, we // need to copy: assign_copy_from(other, std::false_type()); } return *this; } // implementation of operator=() with propagate_on_container_move_assignment uvector& assign_move_from(uvector&& other, std::true_type) noexcept { deallocate(); Alloc::operator=(std::move(static_cast(other))); _begin = other._begin; _end = other._end; _endOfStorage = other._endOfStorage; other._begin = nullptr; other._end = nullptr; other._endOfStorage = nullptr; return *this; } // implementation of swap with propagate_on_container_swap void swap(uvector& other, std::true_type) noexcept { std::swap(_begin, other._begin); std::swap(_end, other._end); std::swap(_endOfStorage, other._endOfStorage); std::swap(static_cast(other), static_cast(*this)); } // implementation of swap without propagate_on_container_swap void swap(uvector& other, std::false_type) noexcept { std::swap(_begin, other._begin); std::swap(_end, other._end); std::swap(_endOfStorage, other._endOfStorage); /** * We have two choices here: * - Do not swap the allocators. For stateful allocators, we would need to * reallocate memory, and iterators would not be valid UNLESS * they were stored as indices. However, containers with stateful allocators * are not allowed to be swapped unless the allocators are equal, in which case swapping * is not necessary. * - Swap the allocators. This would not reallocate memory and * iterators remain valid, but the trait ignores propagate_on_container_swap. * * The standard says: * "Allocator replacement is performed by copy assignment, move assignment, or * swapping of the allocator only if allocator_traits:: * propagate_on_container_copy_assignment::value, * allocator_traits::propagate_on_container_move_assignment::value, * or allocator_traits::propagate_on_container_swap::value is true * within the implementation of the corresponding container operation. The behavior * of a call to a container’s swap function is undefined unless the objects being * swapped have allocators that compare equal or * allocator_traits::propagate_on_container_swap::value is true." */ } template void push_back_range(InputIterator first, InputIterator last, std::false_type) { push_back_range(first, last, typename std::iterator_traits::iterator_category()); } // This function is called from push_back(iter,iter) when Tp is an integral. In that case, // the user tried to call push_back(n, &val), but it got caught by the wrong overload. template void push_back_range(Integral n, Integral val, std::true_type) { if(capacity() - size() < size_t(n)) { enlarge(enlarge_size(n)); } std::uninitialized_fill_n(_end, n, val); _end += n; } template void push_back_range(InputIterator first, InputIterator last, std::forward_iterator_tag) { size_t n = std::distance(first, last); if(n > capacity() - size()) { enlarge(enlarge_size(n)); } while(first != last) { *_end = *first; ++_end; ++first; } } }; /** @brief Compare two uvectors for equality. */ template inline bool operator==(const uvector& lhs, const uvector& rhs) noexcept { return lhs.size()==rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); } /** @brief Compare two uvectors for inequality. */ template inline bool operator!=(const uvector& lhs, const uvector& rhs) noexcept { return !(lhs == rhs); } /** @brief Compare two uvectors for smaller than. * @details If two uvectors compare equal up to the length of one, the uvector with * the smallest size is consider to be smaller. */ template inline bool operator<(const uvector& lhs, const uvector& rhs) noexcept { const size_t minSize = std::min(lhs.size(), rhs.size()); for(size_t i=0; i!=minSize; ++i) { if(lhs[i] < rhs[i]) return true; else if(lhs[i] > rhs[i]) return false; } return lhs.size() < rhs.size(); } /** @brief Compare two uvectors for smaller than or equal. * @details If two uvectors compare equal up to the length of one, the uvector with * the smallest size is consider to be smaller. */ template inline bool operator<=(const uvector& lhs, const uvector& rhs) noexcept { const size_t minSize = std::min(lhs.size(), rhs.size()); for(size_t i=0; i!=minSize; ++i) { if(lhs[i] < rhs[i]) return true; else if(lhs[i] > rhs[i]) return false; } return lhs.size() <= rhs.size(); } /** @brief Compare two uvectors for larger than. * @details If two uvectors compare equal up to the length of one, the uvector with * the smallest size is consider to be smaller. */ template inline bool operator>(const uvector& lhs, const uvector& rhs) noexcept { return rhs < lhs; } /** @brief Compare two uvectors for larger than or equal. * @details If two uvectors compare equal up to the length of one, the uvector with * the smallest size is consider to be smaller. */ template inline bool operator>=(const uvector& lhs, const uvector& rhs) noexcept { return rhs <= lhs; } /** @brief Swap the contents of the two uvectors. * @details Iterators to both vectors will remain valid and will point into * to the swapped container afterwards. This function will never reallocate * space. * * The allocator will be swapped when the @c propagate_on_container_swap * of the respective @c allocator_trait is @c true_type. * Its behaviour is undefined when the allocators do not compare equal and * @c propagate_on_container_swap is false. */ template inline void swap(uvector& x, uvector& y) { x.swap(y); } /** @} */ } // end of namespace ao #endif // AO_UVECTOR_H casacore-3.7.1/tables/Dysco/weightblockencoder.h000066400000000000000000000047051476623553700217000ustar00rootroot00000000000000#ifndef DYSCO_WEIGHT_BLOCK_ENCODER_H #define DYSCO_WEIGHT_BLOCK_ENCODER_H #include #include #include "timeblockbuffer.h" class WeightBlockEncoder { public: WeightBlockEncoder(size_t nPolarizations, size_t nChannels, size_t quantCount) : _nPolarizations(nPolarizations), _nChannels(nChannels), _quantCount(quantCount) {} size_t MetaDataFloatCount() const { return 1.0; } size_t SymbolCount(size_t nRowsInBlock) const { return nRowsInBlock * _nChannels; } void InitializeDecode(const float *metaBuffer) { _decodeMaxValue = metaBuffer[0]; } void Decode(TimeBlockBuffer &buffer, const unsigned int *symbolBuffer, size_t blockRow) const { double scaleValue = _decodeMaxValue / (double(_quantCount - 1)); TimeBlockBuffer::DataRow &row = buffer[blockRow]; const unsigned int *rowBuffer = &symbolBuffer[blockRow * _nChannels]; for (size_t ch = 0; ch != _nChannels; ++ch) { float value = *rowBuffer * scaleValue; row.visibilities.resize(_nChannels * _nPolarizations); float *chPtr = &row.visibilities[ch * _nPolarizations]; for (size_t p = 0; p != _nPolarizations; ++p) chPtr[p] = value; ++rowBuffer; } } void Encode(TimeBlockBuffer &buffer, float *metaBuffer, unsigned int *symbolBuffer) const { float maxValue = 0.0; for (const TimeBlockBuffer::DataRow &row : buffer.GetVector()) { for (size_t ch = 0; ch != _nChannels; ++ch) { const float *visPtr = &row.visibilities[ch * _nPolarizations]; float weight = *visPtr; for (size_t p = 1; p != _nPolarizations; ++p) if (visPtr[p] < weight) weight = visPtr[p]; if (weight > maxValue) maxValue = weight; } } if (maxValue == 0.0) maxValue = 1.0; metaBuffer[0] = maxValue; double scaleValue = double(_quantCount - 1) / maxValue; for (const TimeBlockBuffer::DataRow &row : buffer.GetVector()) { for (size_t ch = 0; ch != _nChannels; ++ch) { const float *visPtr = &row.visibilities[ch * _nPolarizations]; float weight = *visPtr; for (size_t p = 1; p != _nPolarizations; ++p) if (visPtr[p] < weight) weight = visPtr[p]; *symbolBuffer = roundf(double(weight) * scaleValue); ++symbolBuffer; } } } private: const size_t _nPolarizations; const size_t _nChannels; const size_t _quantCount; float _decodeMaxValue; }; #endif casacore-3.7.1/tables/Dysco/weightencoder.cc000066400000000000000000000000331476623553700210110ustar00rootroot00000000000000#include "weightencoder.h" casacore-3.7.1/tables/Dysco/weightencoder.h000066400000000000000000000051311476623553700206570ustar00rootroot00000000000000#ifndef DYSCO_WEIGHT_ENCODER_H #define DYSCO_WEIGHT_ENCODER_H #include #include namespace dyscostman { /** * Encoder for visibility weights. It's a linear quantizer for * non-negative values, with a single scaling factor. The scaling * factor will be such that the max value will still fit. * @author André Offringa */ template class WeightEncoder { public: /** Value type of the original weights (a floating point value).*/ typedef T value_t; /** Value type of the symbols to which the weights are encoded. */ typedef unsigned symbol_t; /** Construct a new encoder with the given quantization count. * @param quantCount The number of quantization levels. */ explicit WeightEncoder(unsigned quantCount) : _quantCount(quantCount) {} /** * Encodes a vector of values. Will return a vector of symbols and a * scaling value. Together, these can be used to get the original values * back (with some loss due to quantization), by using @ref Decode(). * @param scaleVal Will receive the scale value for the vector of values * @param dest The destinal vector with symbols. It is assumed that it * is already of the right size, i.e., equal to input.size(). * @param input The input array of values to be encoded. */ void Encode(value_t &scaleVal, std::vector &dest, const std::vector &input) const { // Find max value (implicit assumption input is not empty) typename std::vector::const_iterator i = input.begin(); scaleVal = *i; ++i; while (i != input.end()) { if (*i > scaleVal) scaleVal = *i; ++i; } i = input.begin(); typename std::vector::iterator d = dest.begin(); const value_t scaleFact = value_t(_quantCount - 1) / scaleVal; while (i != input.end()) { *d = roundf(scaleFact * (*i)); ++i; ++d; } } /** * Decode a previously encoded value. * @param dest The destination for the decoded weight values. * @param scaleVal The scale value for the vector of values. * @param input The input symbols to be decoded. * @see Encode() */ void Decode(std::vector &dest, value_t scaleVal, const std::vector &input) const { typename std::vector::const_iterator i = input.begin(); typename std::vector::iterator d = dest.begin(); const value_t scaleFact = scaleVal / value_t(_quantCount - 1); while (i != input.end()) { *d = (*i) * scaleFact; ++i; ++d; } } private: unsigned _quantCount; }; } // namespace dyscostman #endif casacore-3.7.1/tables/LogTables.h000066400000000000000000000033261476623553700166270ustar00rootroot00000000000000//# LogTables.h: a module for log tables //# Copyright (C) 2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #if !defined (AIPS_MODULE_LOGTABLES_H) #define AIPS_MODULE_LOGTABLES_H #include // // // // Logging in Casacore tables // // //
      • Logging //
      • Tables // // // // // // // // // // // // // // // // #endif casacore-3.7.1/tables/LogTables/000077500000000000000000000000001476623553700164525ustar00rootroot00000000000000casacore-3.7.1/tables/LogTables/LogFilterExpr.cc000066400000000000000000000071551476623553700215170ustar00rootroot00000000000000//# LogFilterExpr.cc: Class to deal with a TaQL expression to filter messages //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LogFilterExpr::LogFilterExpr (const String& expr) : itsExpr (0) { // Make a description for the parser. RecordDesc desc; desc.addField ("TIME", TpDouble); desc.addField ("PRIORITY", TpString); desc.addField ("MESSAGE", TpString); desc.addField ("LOCATION", TpString); desc.addField ("OBJECT_ID", TpString); itsExpr = new TableExprNode (RecordGram::parse (Record(desc), expr)); } LogFilterExpr::LogFilterExpr (const LogFilterExpr& that) : TableExprData(), itsExpr (0) { if (that.itsExpr != 0) { itsExpr = new TableExprNode (*that.itsExpr); } } LogFilterExpr::~LogFilterExpr() { delete itsExpr; } LogFilterExpr& LogFilterExpr::operator= (const LogFilterExpr& that) { if (this != &that) { delete itsExpr; itsExpr = 0; if (that.itsExpr != 0) { itsExpr = new TableExprNode (*that.itsExpr); } } return *this; } Bool LogFilterExpr::matches (const LogMessage& message) { // Evaluate the expression for this message. itsMessage = &message; Bool valb; // This class contains the functions to get the values. itsExpr->get (*this, valb); return valb; } Double LogFilterExpr::getDouble (const Block& fieldNrs) const { switch (fieldNrs[0]) { case 0: return itsMessage->messageTime().modifiedJulianDay()*24.0*3600.0; default: throw (AipsError("LogFilterExpr::getDouble")); } } String LogFilterExpr::getString (const Block& fieldNrs) const { switch (fieldNrs[0]) { case 1: return itsMessage->LogMessage::toString(itsMessage->priority()); case 2: return itsMessage->message(); case 3: return itsMessage->origin().location(); case 4: { String tmp; itsMessage->origin().objectID().toString(tmp); return tmp; } default: throw (AipsError("LogFilterExpr::getString")); } } DataType LogFilterExpr::dataType (const Block& fieldNrs) const { switch (fieldNrs[0]) { case 0: return TpDouble; case 1: case 2: case 3: case 4: return TpString; default: throw (AipsError("LogFilterExpr::dataType")); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/LogTables/LogFilterExpr.h000066400000000000000000000051311476623553700213510ustar00rootroot00000000000000//# LogFilterExpr.h: Class to deal with a TaQL expression to filter messages //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_LOGFILTEREXPR_H #define TABLES_LOGFILTEREXPR_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableExprNode; class LogMessage; // // Class to deal with a TaQL expression to filter messages. // // // // This program tests the class TableExprData. // This example shows how a data set consisting of two vectors // of scalars can be used. class LogFilterExpr : public TableExprData { public: // Construct it from an expression which gets parsed. LogFilterExpr (const String& expr); // Copy constructor (copy semantics). LogFilterExpr (const LogFilterExpr&); virtual ~LogFilterExpr(); // Assignment (copy semantics). LogFilterExpr& operator= (const LogFilterExpr&); // Does this message match the expression? Bool matches (const LogMessage& message); // Get the data. // virtual Double getDouble (const Block& fieldNrs) const; virtual String getString (const Block& fieldNrs) const; // // Get the data type of the various values. virtual DataType dataType (const Block& fieldNrs) const; private: TableExprNode* itsExpr; const LogMessage* itsMessage; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/LogTables/LogFilterTaql.cc000066400000000000000000000042261476623553700214760ustar00rootroot00000000000000//# LogFilterTaql.cc: Filter LogMessages using a TaQL expression //# Copyright (C) 1996,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LogFilterTaql::LogFilterTaql (const String& expr) : expr_p (0) { expr_p = new LogFilterExpr(expr); } LogFilterTaql::LogFilterTaql (const LogFilterTaql& other) : LogFilterInterface(), expr_p (0) { if (other.expr_p != 0) { expr_p = new LogFilterExpr (*other.expr_p); } } LogFilterTaql& LogFilterTaql::operator= (const LogFilterTaql& other) { if (this != &other) { delete expr_p; expr_p = 0; if (other.expr_p != 0) { expr_p = new LogFilterExpr (*other.expr_p); } } return *this; } LogFilterTaql::~LogFilterTaql() { delete expr_p; } LogFilterTaql* LogFilterTaql::clone() const { return new LogFilterTaql(*this); } Bool LogFilterTaql::pass (const LogMessage& message) const { return expr_p->matches (message); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/LogTables/LogFilterTaql.h000066400000000000000000000071611476623553700213410ustar00rootroot00000000000000//# LogFilterTaql.h: Filter LogMessages using a TaQL expression //# Copyright (C) 1996,2000,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_LOGFILTERTAQL_H #define TABLES_LOGFILTERTAQL_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class LogFilterExpr; // // Filter LogMessages using a TaQL expression. // // // // // //
      • LogMessage // // // // Log[Message] Filter. // // // // The LogFilter class is used by the various log sink classes, // typically accessed through LogSink, to // decide whether a particular LogMessage // should be accepted or rejected. // // Simple filtering is based on the messages priority. // In particular, you typically will choose to only pass messages greater // than or equal to NORMAL in priority, but you might choose // DEBUGGING to see all messages, or SEVERE to only see // messages that report serious problems. // // // // Suppose we wanted to change the global sink so that it prints all messages, // including debugging messages: // // LogFilter all(LogMessage::DEBUGGING); // LogSink::globalSink().filter(all); // // // // // // //# //# class LogFilterTaql : public LogFilterInterface { public: // Construct a filter from a TaQL expression. Only messages matching // the expression pass the filter. // The field names that can be used in the expression are: // TIME, PRIORITY, MESSAGE, LOCATION, OBJECT_ID. // All fields are strings, expect for TIME which is a double (MJD in sec.). explicit LogFilterTaql (const String& expr); // Copy other to this. // LogFilterTaql (const LogFilterTaql& other); LogFilterTaql& operator= (const LogFilterTaql& other); // virtual ~LogFilterTaql(); // Clone the object. virtual LogFilterTaql* clone() const; // Return True if message passes this filter. virtual Bool pass (const LogMessage& message) const; private: LogFilterExpr* expr_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/LogTables/LoggerHolder.cc000066400000000000000000000177621476623553700213530ustar00rootroot00000000000000//# LoggerHolder.cc: Class to handle a hierarchy of loggers //# Copyright (C) 2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN LoggerHolder::LoggerHolder (Bool nullSink) : itsRep (new LoggerHolderRep (nullSink)) {} LoggerHolder::LoggerHolder (const String& logTableName, Bool isWritable) : itsRep (new LoggerHolderRep (logTableName, isWritable)) {} LoggerHolder::LoggerHolder (const LoggerHolder& that) : itsRep (that.itsRep) {} LoggerHolder::~LoggerHolder() { // Close the possible log table to avoid waste in case the logger // is not really used anymore. itsRep->tempClose(); } LoggerHolder& LoggerHolder::operator= (const LoggerHolder& that) { if (this != &that) { itsRep = that.itsRep; } return *this; } void LoggerHolder::append (const LoggerHolder& other) { itsRep->append (other); } void LoggerHolder::reopenRW() { itsRep->reopenRW(); } void LoggerHolder::addParent (const LoggerHolder& logger) { itsRep->addParent (logger); } void LoggerHolder::tempClose (Bool closeParents) const { itsRep->tempClose (closeParents); } void LoggerHolder::unlock() { itsRep->unlock(); } void LoggerHolder::flush() { itsRep->flush(); } void LoggerHolder::resync() { itsRep->resync(); } void LoggerHolder::removeParents() { itsRep->removeParents(); } void LoggerHolder::clear() { itsRep->clear(); } LoggerHolderRep::LoggerHolderRep (Bool nullSink) : itsSink (LogFilter(), nullSink), itsTablePtr (0), itsIsWritable (True), itsIsClosed (False) { itsLogger = LogIO(itsSink); } LoggerHolderRep::LoggerHolderRep (const String& logTableName, Bool isWritable) : itsTableName (logTableName), itsTablePtr (0), itsIsWritable (isWritable), itsIsClosed (True) { // Open the log table. doReopen(); } LoggerHolderRep::LoggerHolderRep (const LoggerHolderRep& that) : itsParents (that.itsParents), itsSink (that.itsSink), itsLogger (that.itsLogger), itsTableName (that.itsTableName), itsTablePtr (that.itsTablePtr), itsIsWritable (that.itsIsWritable), itsIsClosed (that.itsIsClosed) {} LoggerHolderRep::~LoggerHolderRep() { removeParents(); } LoggerHolderRep& LoggerHolderRep::operator= (const LoggerHolderRep& that) { if (this != &that) { itsParents = that.itsParents; itsSink = that.itsSink; itsLogger = that.itsLogger; itsTableName = that.itsTableName; itsTablePtr = that.itsTablePtr; itsIsWritable = that.itsIsWritable; itsIsClosed = that.itsIsClosed; } return *this; } void LoggerHolderRep::append (const LoggerHolder& other) { reopenRW(); LogSinkInterface& logsink = sink().localSink(); for (LoggerHolder::const_iterator iter = other.begin(); iter != other.end(); iter++) { logsink.writeLocally (iter->time(), iter->message(), iter->priority(), iter->location(), iter->objectID()); } } void LoggerHolderRep::reopenRW() { // Only needed if a table is used and if not already open for rw. if (!itsTableName.empty()) { if (itsTablePtr == 0 || !itsIsWritable) { // Temporarily close table possibly opened for readonly. tempClose (False); // Reopen temporarily closed table for rw (if possible). if (!itsIsWritable) { itsIsWritable = Table::isWritable (itsTableName); } reopen(); } } } void LoggerHolderRep::doReopen() { if (itsIsClosed && itsTablePtr == 0 && !itsTableName.empty()) { if (itsIsWritable) { itsTablePtr = new TableLogSink(LogFilter(), itsTableName); } else { itsTablePtr = new TableLogSink(itsTableName); } // Make it the local sink. Because that clears the pointer, // we pass it a copy. LogSinkInterface* ptr = itsTablePtr; itsSink.localSink (ptr); itsLogger = LogIO(itsSink); itsIsClosed = False; } } void LoggerHolderRep::addParent (const LoggerHolder& logger) { uInt nr = itsParents.nelements(); itsParents.resize (nr + 1); itsParents[nr] = logger; } void LoggerHolderRep::tempClose (Bool closeParents) { if (itsTablePtr != 0) { itsTablePtr->table().unlock(); itsSink = LogSink(); itsLogger = LogIO(); itsTablePtr = 0; itsIsClosed = True; } if (closeParents) { for (uInt i=0; itable().unlock(); } } void LoggerHolderRep::flush() { if (itsTablePtr != 0) { itsTablePtr->table().flush(); } } void LoggerHolderRep::resync() { if (itsTablePtr != 0 && !itsTablePtr->table().hasLock (FileLocker::Read)) { itsTablePtr->table().unlock(); } } LogIO& LoggerHolderRep::logio() { reopenRW(); return itsLogger; } LogSink& LoggerHolderRep::sink() { if (itsIsClosed) { reopen(); } return itsSink; } void LoggerHolderRep::removeParents() { itsParents.resize (0, True, True); } void LoggerHolderRep::clear() { reopenRW(); removeParents(); itsSink.clearLocally(); } LogHolderIter::LogHolderIter (const LoggerHolder* logger) : itsLogger (logger), itsTempClosed (logger->isTempClosed()), itsParentIter (0), itsCounter (0) { const Block& par = itsLogger->parents(); if (par.nelements() > 0) { itsParentIter = new LogHolderIter (&par[0]); itsCounter++; } } LogHolderIter::~LogHolderIter() { delete itsParentIter; if (itsTempClosed) { itsLogger->tempClose(); } } Bool LogHolderIter::next() { while (itsParentIter != 0 && !itsParentIter->next()) { delete itsParentIter; itsParentIter = 0; const Block& par = itsLogger->parents(); if (par.nelements() > itsCounter) { itsParentIter = new LogHolderIter (&par[itsCounter]); itsCounter++; } else { itsCounter = 0; } } if (itsParentIter != 0) { itsEntry = itsParentIter->getEntry(); } else { const LogSink& sink = itsLogger->sink(); if (itsCounter < sink.nelements()) { itsEntry = LogHolderIterEntry (&sink, itsCounter); itsCounter++; } else { return False; } } return True; } LoggerHolderIterator::LoggerHolderIterator (const LoggerHolder* logger) : itsIter (0) { itsIter = new LogHolderIter (logger); next(); } LoggerHolderIterator::LoggerHolderIterator (const LoggerHolderIterator& that) : itsIter (0) { if (that.itsIter != 0) { itsIter = new LogHolderIter (&(that.logger())); next(); } } LoggerHolderIterator& LoggerHolderIterator::operator= (const LoggerHolderIterator& that) { if (this != &that) { delete itsIter; itsIter = 0; if (that.itsIter != 0) { itsIter = new LogHolderIter (&(that.logger())); next(); } } return *this; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/LogTables/LoggerHolder.h000066400000000000000000000370001476623553700212000ustar00rootroot00000000000000//# LoggerHolder.h: Class holding a hierarchy of loggers //# Copyright (C) 2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_LOGGERHOLDER_H #define TABLES_LOGGERHOLDER_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class LoggerHolderRep; class LoggerHolderIterator; class TableLogSink; // // Class holding a hierarchy of loggers. // // // // // //
      • LogIO
      • // // // The LoggerHolder class implements a hierarchy of loggers. // It has a log sink of its own and can have multiple parent LoggerHolder // objects representing the log info of parent objects. // It is used by class // ImageInterface, but could also // be used elsewhere. // // The sink of a LoggerHolder can be different depending on the type of image. // E.g. for a transient image it can be a // MemoryLogSink, while for a persistent // image it will be a TableLogSink. //
        An important feature is that an LoggerHolder can have zero or more // parent LoggerHolder objects. In that way the log of the parent object // of an image object can be made part of the log of the image object itself, // without having to copy the log. // // To iterate through all messages in a LoggerHolder (including all parents), // the LoggerHolderIterator can // be used. This is an STL-style const_iterator object. // // LoggerHolder uses reference counting // (of class LoggerHolderRep) // to be able to retain // the object after the (ImageInterface) object containing it is gone. // Otherwise classes like SubImage would lose their log info. //
        // // // LoggerHolder logger ("tLoggerHolder_tmp.log", True); // logger.logio() << "test1" << LogIO::POST; // logger.logio() << "test2" << LogIO::POST; // for (LoggerHolder::const_iterator iter = logger.begin(); // iter != logger.end(); // iter++) { // cout << iter->time() << ' ' << iter->message() << endl; // } // // This example shows the construction of an LoggerHolder with a // TableLogSink sink. Thereafter some messages are written. // The latter part shows how to iterate through all messages. // // // LoggerHolder logger (False); // logger.addParent (parent.logger()); // logger.logio() << "test1" << LogIO::POST; // logger.logio() << "test2" << LogIO::POST; // // This example shows the construction of an LoggerHolder with a // MemoryLogSink sink (e.g. for a SubImage). Thereafter the logger of // the parent image is added to it. // Finally some messages are written. // // // This class simplifies and unifies all Image logging activities. // //# //# class LoggerHolder { public: // Create with a NullSink or MemoryLogSink (default). explicit LoggerHolder (Bool nullSink = False); // Create with a TableLogSink. LoggerHolder (const String& logTableName, Bool isWritable); // Copy constructor (reference semantics). LoggerHolder (const LoggerHolder&); ~LoggerHolder(); // Assignment (reference semantics). LoggerHolder& operator= (const LoggerHolder&); // Add a logger from a parent. void addParent (const LoggerHolder&); // Append the entries of the other logger to this one. void append (const LoggerHolder& other); // Reopen a readonly logtable for read/write (if needed). void reopenRW(); // Reopen the log table if needed (after a tempClose). void reopen(); // Temporarily close all log tables. // By default the possible parent log tables are also closed. void tempClose (Bool closeParents = True) const; // Unlock the log table. void unlock(); // Flush the log table. void flush(); // Resync the log table (if needed). void resync(); // Is the log table temporarily closed? Bool isTempClosed() const; // Get access to the logger. // It assumes that it will be used to post a message, so it reopens // the log table for read/write if needed). LogIO& logio(); // Get access to the log sink (reopen the log table if needed). // It is not assumed you want to write. If you want to do that, // you should first call reopenRW() to ensure you can write. // LogSink& sink(); const LogSink& sink() const; // // Clear the log. // It removes the parents and removes all messages from the sink. void clear(); // Remove all parents. void removeParents(); // Return the block of parents. const Block& parents() const; // Define the STL-style iterators. // Only a const forward iterator is available. // It makes it possible to iterate through all messages in the logger. // // LoggerHolder logger("log.name", False) // for (LoggerHolder::const_iterator iter=arr.begin(); // iter!=arr.end(); iter++) { // cout << iter.message() << endl; // } // // // STL-style typedefs. typedef LoggerHolderIterator const_iterator; // Get the begin and end iterator object. const_iterator begin() const; const_iterator end() const; // private: std::shared_ptr itsRep; }; // // Representation of the class holding a hierarchy of loggers. // // // // // //
      • LogIO
      • // // // The LoggerHolderRep class is the reference counted implementation // of LoggerHolder. // See that class for more information. // // // Reference counting was needed to be able to keep a LoggerHolder // object after the (ImageInterface) object containing it is gone. // //# //# class LoggerHolderRep { public: // Create with a NullSink or MemoryLogSink (default). LoggerHolderRep (Bool nullSink); // Create with a TableLogSink. LoggerHolderRep (const String& logTableName, Bool isWritable); // Copy constructor. LoggerHolderRep (const LoggerHolderRep&); ~LoggerHolderRep(); // Assignment. // It removes the current parents. LoggerHolderRep& operator= (const LoggerHolderRep&); // Add a logger from a parent. void addParent (const LoggerHolder&); // Append the entries of the other logger to this one. void append (const LoggerHolder& other); // Reopen a readonly logtable for read/write (if needed). void reopenRW(); // Reopen the log table if needed (after a tempClose). void reopen() { if (itsIsClosed) doReopen(); } // Temporarily close all log tables. // By default the possible parent log tables are also closed. void tempClose (Bool closeParents = True); // Unlock the log table. void unlock(); // Flush the log table. void flush(); // Resync the log table (if needed). void resync(); // Is the log table temporarily closed? Bool isTempClosed() const { return itsIsClosed; } // Get access to the logger. // It assumes that it will be used to post a message, so it reopens // the log table for read/write if needed). LogIO& logio(); // Get access to the log sink (reopen the log table if needed). // It is not assumed you want to write. If you want to do that, // you should first call reopenRW() to ensure you can write. LogSink& sink(); // Clear the log. // It removes the parents and removes all messages from the sink. void clear(); // Remove all parents. void removeParents(); // Return the block of parents. const Block& parents() const { return itsParents; } // Define the STL-style iterators. // Only a const forward iterator is available. // It makes it possible to iterate through all messages in the logger. // // LoggerHolder logger("log.name", False) // for (LoggerHolder::const_iterator iter=arr.begin(); // iter!=arr.end(); iter++) { // cout << iter.message() << endl; // } // // // STL-style typedefs. typedef LoggerHolderIterator const_iterator; // Get the begin and end iterator object. const_iterator begin() const; const_iterator end() const; // private: // Do the actual reopen. void doReopen(); Block itsParents; LogSink itsSink; LogIO itsLogger; String itsTableName; TableLogSink* itsTablePtr; Bool itsIsWritable; Bool itsIsClosed; }; // // Class representing an entry in a LoggerHolder. // // // // // //
      • LoggerHolder
      • // // // This class makes it possible to use the iterator in the STL-style. // It only contains a 'pointer' to the current entry in the current logger. // Function like time() can be used to retrieve the message parts. // class LogHolderIterEntry { public: LogHolderIterEntry() : itsSink(0), itsIndex(0) {} LogHolderIterEntry (const LogSink* sink, uInt index) : itsSink(sink), itsIndex(index) {} LogHolderIterEntry (const LogHolderIterEntry& that) : itsSink(that.itsSink), itsIndex(that.itsIndex) {} ~LogHolderIterEntry() {} LogHolderIterEntry& operator= (const LogHolderIterEntry& that) { itsSink=that.itsSink; itsIndex=that.itsIndex; return *this; } // Get the message parts. // Double time() const { return itsSink->getTime(itsIndex); } String message() const { return itsSink->getMessage(itsIndex); } String priority() const { return itsSink->getPriority(itsIndex); } String location() const { return itsSink->getLocation(itsIndex); } String objectID() const { return itsSink->getObjectID(itsIndex); } // private: const LogSink* itsSink; uInt itsIndex; }; // // Class doing the actual iteration through an LoggerHolder. // // // // // //
      • LoggerHolder
      • // // // This class makes it possible to use the iterator in the STL-style. // It is used by //LoggerHolderIterator // which is the class as seen by the user. // LogHolderIter makes it easier to make the first entry available on // construction of an LoggerHolderIterator. // class LogHolderIter { public: // Construct the iterator on the given LoggerHolderRep. LogHolderIter (const LoggerHolder*); ~LogHolderIter(); // Copy constructor is not needed, thus forbidden. LogHolderIter (const LogHolderIter&) = delete; // Assignment is not needed, thus forbidden. LogHolderIter& operator= (const LogHolderIter&) = delete; // Increment to next message. // Returns False if at the end. Bool next(); // Get the entry. const LogHolderIterEntry& getEntry() const { return itsEntry; } const LoggerHolder& logger() const { return *itsLogger; } private: const LoggerHolder* itsLogger; Bool itsTempClosed; LogHolderIter* itsParentIter; uInt itsCounter; LogHolderIterEntry itsEntry; }; // // Class to iterate through an LoggerHolder. // // // // // //
      • LoggerHolder
      • // // // This class makes it possible to iterate in the STL-style through all // entries of an LoggerHolder object. If the logger has parent LoggerHolder // objects, it first iterates through all parents (recursively) and // finally through all entries in the LoggerHolder object itself. // // // // LoggerHolder logger ("tLoggerHolder_tmp.log", True); // logger.logio() << "test1" << LogIO::POST; // logger.logio() << "test2" << LogIO::POST; // for (LoggerHolder::const_iterator iter = logger.begin(); // iter != logger.end(); // iter++) { // cout << iter->time() << ' ' << iter->message() << endl; // } // // class LoggerHolderIterator { public: LoggerHolderIterator() : itsIter(0), itsNotAtEnd(False) {} LoggerHolderIterator (const LoggerHolder*); LoggerHolderIterator (const LoggerHolderIterator&); ~LoggerHolderIterator() { delete itsIter; } LoggerHolderIterator& operator= (const LoggerHolderIterator&); // Increment to next message. // void operator++() { next(); } void operator++ (int) { next(); } // // Is the iterator not at the end yet? Bool operator!= (const LoggerHolderIterator&) { return itsNotAtEnd; } // Get the entry. // const LogHolderIterEntry& operator*() const { return itsIter->getEntry(); } const LogHolderIterEntry* operator->() const { return &(itsIter->getEntry()); } // const LoggerHolder& logger() const { return itsIter->logger(); } private: // Get the next entry (if available). void next() { itsNotAtEnd = itsIter->next(); } LogHolderIter* itsIter; Bool itsNotAtEnd; }; inline void LoggerHolder::reopen() { itsRep->reopen(); } inline Bool LoggerHolder::isTempClosed() const { return itsRep->isTempClosed(); } inline LogIO& LoggerHolder::logio() { return itsRep->logio(); } inline LogSink& LoggerHolder::sink() { return itsRep->sink(); } inline const LogSink& LoggerHolder::sink() const { return itsRep->sink(); } inline const Block& LoggerHolder::parents() const { return itsRep->parents(); } inline LoggerHolder::const_iterator LoggerHolder::begin() const { return LoggerHolderIterator (this); } inline LoggerHolder::const_iterator LoggerHolder::end() const { return LoggerHolderIterator(); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/LogTables/NewFile.cc000066400000000000000000000102511476623553700203110ustar00rootroot00000000000000//# NewFile.cc: Constrain a string to be a new (non-existent) file //# Copyright (C) 1996,1997,1999,2000,2001,2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN NewFile::NewFile(Bool deleteIfExists) : delete_p(deleteIfExists) { // Nothing } NewFile::NewFile(const NewFile &other) : delete_p(other.delete_p) { // Nothing } NewFile &NewFile::operator=(const NewFile &other) { if (this != &other) { delete_p = other.delete_p; } return *this; } NewFile::~NewFile() { // Nothing } Bool NewFile::valueOK(const String &value, String &error) const { LogOrigin OR("NewFile", "valueOK(const String &value, String &error) const", WHERE); LogMessage msg(OR); error = ""; Bool retval = False; // if (value.empty()) { error = "File string is empty"; return False; } // File thefile(value); if (thefile.exists()) { String text = String("File '") + value + "' already exists. Remove it?"; Vector choices(2); choices(0) = "no"; choices(1) = "yes"; String remove = Choice::choice(text, choices); if (remove == "yes") { Bool removed = False; String extra_error = ""; try { if (thefile.isRegular()) { RegularFile rfile = thefile; rfile.remove(); removed = True; } else if (thefile.isDirectory()) { // Assume that directories are tables. if (! Table::isWritable(value)) { removed = False; extra_error = "Table is not writable!"; } else { removed = False; if (TableUtil::canDeleteTable(extra_error, value)) { try { TableUtil::deleteTable(value); removed = True; } catch (std::exception& xxx) { removed = False; extra_error = String("Error deleting table ") + value + ":" + xxx.what(); } } } } else if (thefile.isSymLink()) { SymLink sfile = thefile; sfile.remove(); removed = True; } } catch (std::exception& x) { extra_error = x.what(); removed = False; } if (!removed) { retval = False; error = String("Could not remove file ") + value; if (extra_error != "") { error += String("(") + extra_error + ")"; } error += "."; } else { retval = True; msg.message(String("Removed file ") + value + " at users request").line(__LINE__). priority(LogMessage::NORMAL); LogSink::postGlobally(msg); } } else { retval = False; error = String("File ") + value + " exists, and the user does not want to remove it."; } } else { retval = True; } return retval; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/LogTables/NewFile.h000066400000000000000000000055771476623553700201720ustar00rootroot00000000000000//# NewFile: Do checks for a new (non-existent) file //# Copyright (C) 1996,1999,2002,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_NEWFILE_H #define TABLES_NEWFILE_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Do checks for a new (non-existent) file. // // // // // // // Use this if you want a New File. // // // // NewFile is a class checking if a new file already exists. // If the file exists, then the user is asked via Choice::choice whether or not // he or she wants to delete the file before using it. // // // // NewFile validFile; // String newFileName("bigone"), error; // Bool ok = validFile.valueOK(newFileName, error); // if (!ok) { // cout << error << endl; // } // // // // Output file names are fairly common parameters, this consolidates the error // checking and "remove if it already exists" logic. // // // //
      • We should probably make sure that the file is writable // class NewFile { public: // Currently the deleteIfExists argument has no affect NewFile(Bool deleteIfExists = True); // Copy constructor (copy semantics) NewFile(const NewFile &other); // Assignment (copy semantics) NewFile &operator=(const NewFile &other); // Destructor ~NewFile(); // Indicates whether the specified string is a valid new file, // invoking the choice GUI. If it returns False, an error // message is returned. Bool valueOK(const String &value, String &error) const; private: Bool delete_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/LogTables/TableLogSink.cc000066400000000000000000000222711476623553700213030ustar00rootroot00000000000000//# TableLogSink.h: save log messages in a Casacore Table //# Copyright (C) 1996,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN String TableLogSink::localId( ) { return String("TableLogSink"); } String TableLogSink::id( ) const { return String("TableLogSink"); } TableLogSink::TableLogSink (LogMessage::Priority filter, const String& fileName) : LogSinkInterface(LogFilter(filter)) { init (fileName); } TableLogSink::TableLogSink (const LogFilterInterface& filter, const String& fileName) : LogSinkInterface(filter) { init (fileName); } void TableLogSink::init (const String& fileName) { LogMessage logMessage(LogOrigin("TableLogSink", "TableLogSink", WHERE)); if (fileName.empty()) { // Create temporary table logMessage.priority(LogMessage::DEBUGGING).line(__LINE__). message("Creating temporary log table"); LogSink::postGlobally(logMessage); SetupNewTable setup (fileName, logTableDescription(), Table::Scratch); makeTable (setup); } else if (Table::isWritable(fileName)) { log_table_p = Table(fileName, Table::Update); logMessage.priority(LogMessage::DEBUGGING).line(__LINE__).message( String("Opening existing file ") + fileName); LogSink::postGlobally(logMessage); } else if (Table::isReadable(fileName)) { // We can read it, but not write it! logMessage.priority(LogMessage::SEVERE).line(__LINE__).message( fileName + " exists, but is not writable"); LogSink::postGloballyThenThrow(logMessage); } else { // Table does not exist - create logMessage.priority(LogMessage::DEBUGGING).line(__LINE__). message("Creating " + fileName); LogSink::postGlobally(logMessage); SetupNewTable setup (fileName, logTableDescription(), Table::New); makeTable (setup); } attachCols(); } TableLogSink::TableLogSink (const String& fileName) : LogSinkInterface() { LogMessage logMessage(LogOrigin("TableLogSink", "TableLogSink", WHERE)); if (! Table::isReadable (fileName)) { // Table does not exist. logMessage.priority(LogMessage::SEVERE).line(__LINE__).message( fileName + " does not exist or is not readable"); LogSink::postGloballyThenThrow(logMessage); } else { log_table_p = Table(fileName); logMessage.priority(LogMessage::DEBUGGING).line(__LINE__).message( String("Opening readonly ") + fileName); LogSink::postGlobally(logMessage); } attachCols(); } TableLogSink::TableLogSink (const TableLogSink& other) : LogSinkInterface() { copy_other (other); } TableLogSink& TableLogSink::operator= (const TableLogSink& other) { if (this != &other) { copy_other(other); } return *this; } void TableLogSink::copy_other (const TableLogSink& other) { LogSinkInterface::operator= (other); log_table_p = other.log_table_p; time_p.reference (other.time_p); priority_p.reference (other.priority_p); message_p.reference (other.message_p); location_p.reference (other.location_p); id_p.reference (other.id_p); } TableLogSink::~TableLogSink() { TableLogSink::flush(); } void TableLogSink::makeTable (SetupNewTable& setup) { // Bind all to the SSM. StandardStMan stman("SSM", 32768); setup.bindAll(stman); log_table_p = Table(setup); log_table_p.tableInfo() = TableInfo(TableInfo::LOG); log_table_p.tableInfo(). readmeAddLine("Repository for software-generated logging messages"); } void TableLogSink::attachCols() { time_p.attach (log_table_p, columnName(TIME)); priority_p.attach (log_table_p, columnName(PRIORITY)); message_p.attach (log_table_p, columnName(MESSAGE)); location_p.attach (log_table_p, columnName(LOCATION)); id_p.attach (log_table_p, columnName(OBJECT_ID)); // If writable, define the time keywords when not defined yet. // In this way the table browser can interpret the times. if (log_table_p.isWritable()) { TableRecord& keySet = time_p.rwKeywordSet(); if (! keySet.isDefined ("UNIT")) { keySet.define ("UNIT", "s"); keySet.define ("MEASURE_TYPE", "EPOCH"); keySet.define ("MEASURE_REFERENCE", "UTC"); } } } void TableLogSink::reopenRW (const LogFilterInterface& aFilter) { log_table_p.reopenRW(); attachCols(); filter (aFilter); } Bool TableLogSink::postLocally (const LogMessage& message) { if (log_table_p.isWritable()) { log_table_p.reopenRW(); attachCols(); } Bool posted = False; if (filter().pass(message)) { String tmp; message.origin().objectID().toString(tmp); writeLocally (message.messageTime().modifiedJulianDay()*C::day, message.message(), LogMessage::toString(message.priority()), message.origin().location(), tmp); posted = True; } return posted; } uInt TableLogSink::nelements() const { return table().nrow(); } Double TableLogSink::getTime (uInt i) const { AlwaysAssert (i < table().nrow(), AipsError); return roTime()(i); } String TableLogSink::getPriority (uInt i) const { AlwaysAssert (i < table().nrow(), AipsError); return roPriority()(i); } String TableLogSink::getMessage (uInt i) const { AlwaysAssert (i < table().nrow(), AipsError); return roMessage()(i); } String TableLogSink::getLocation (uInt i) const { AlwaysAssert (i < table().nrow(), AipsError); return roLocation()(i); } String TableLogSink::getObjectID (uInt i) const { AlwaysAssert (i < table().nrow(), AipsError); return roObjectID()(i); } String TableLogSink::columnName (TableLogSink::Columns which) { switch (which) { case TIME: return "TIME"; case PRIORITY: return "PRIORITY"; case MESSAGE: return "MESSAGE"; case LOCATION: return "LOCATION"; case OBJECT_ID: return "OBJECT_ID"; default: AlwaysAssert(! "REACHED", AipsError); } return ""; } TableDesc TableLogSink::logTableDescription() { TableDesc desc; desc.comment() = "Log message table"; desc.addColumn (ScalarColumnDesc(columnName(TIME), "MJD in seconds")); ScalarColumnDesc pdesc (columnName(PRIORITY)); pdesc.setMaxLength (9); // Longest is DEBUGGING desc.addColumn (pdesc); desc.addColumn (ScalarColumnDesc(columnName(MESSAGE))); desc.addColumn (ScalarColumnDesc(columnName(LOCATION))); desc.addColumn (ScalarColumnDesc(columnName(OBJECT_ID))); return desc; } void TableLogSink::flush(Bool) { log_table_p.flush(); } void TableLogSink::writeLocally (Double mtime, const String& mmessage, const String& mpriority, const String& mlocation, const String& mobjectID) { uInt offset = table().nrow(); // cout << "writing " << mmessage << " at row " << offset << endl; table().addRow(1); time().put (offset, mtime); message().put (offset, mmessage); priority().put (offset, mpriority); location().put (offset, mlocation); objectID().put (offset, mobjectID); } void TableLogSink::clearLocally() { String fileName = log_table_p.tableName(); // Delete current log table. log_table_p.markForDelete(); log_table_p = Table(); // Create new log table. SetupNewTable setup (fileName, logTableDescription(), Table::New); makeTable (setup); attachCols(); } LogSink TableLogSink::makeSink (const LogFilterInterface &filter, const String &fileName) { LogSinkInterface* sink = new TableLogSink (LogFilter(LogMessage::DEBUGGING), fileName); return LogSink (filter, std::shared_ptr(sink)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/LogTables/TableLogSink.h000066400000000000000000000217511476623553700211470ustar00rootroot00000000000000//# TableLogSink.h: Save log messages in a Casacore Table //# Copyright (C) 1996,1997,1998,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABLELOGSINK_H #define TABLES_TABLELOGSINK_H //# Includes #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableDesc; class SetupNewTable; // // Save log messages in a Casacore Table // // // // // //
      • LogSinkInterface //
      • Tables // // // // Log to a Casacore Table. // // // // Unlike the other classes derived from // LogSinkInterface, there are utility // functions in this class which might be of some modest interest. In // particular, the member functions which define the structure of the table // and define the column names might be of interest. // // This class posts messages which pass the filter to a Casacore // Table. It puts every field of the // LogMessage into its own column. // // // // See Logging.h. // // // // "Persistent" log messages must be stored in a Table. // // // //
      • Allow a subset of the columns to be written? e.g., only time, // message, and priority. //
      • Allow time sorting in concatenate? // class TableLogSink : public LogSinkInterface { public: // If fileName exists, attach and append to it, otherwise create // a table with that name. If the table exists, it must have all the // required columns defined by logTableDescription(). // TableLogSink (LogMessage::Priority filter, const String& fileName); TableLogSink (const LogFilterInterface& filter, const String& fileName); // // Open the log table for readonly. // If needed, reopenRW can be used later to define a filter and // to open the logtable for writing. explicit TableLogSink (const String& fileName); // After copying, both sinks will write to the same Table. // TableLogSink (const TableLogSink& other); TableLogSink& operator= (const TableLogSink& other); // ~TableLogSink(); // Reopen the logtable for read/write (if needed). // When it actually reopens, the given filter will be used. void reopenRW (const LogFilterInterface& filter); // If the message passes the filter, write it to the log table. virtual Bool postLocally (const LogMessage& message); // Get number of messages in sink. virtual uInt nelements() const; // Get given part of the i-th message from the sink. // virtual Double getTime (uInt i) const; virtual String getPriority (uInt i) const; virtual String getMessage (uInt i) const; virtual String getLocation (uInt i) const; virtual String getObjectID (uInt i) const; // // Access to the actual log table and its columns. // // Functions time, priority, message, location, objectID // return a null ScalarColumn object when the logtable is // not writable. Using it may result in using a null pointer // causing a core dump. In debug mode it is checked if the object // is not null. // // const Table& table() const; Table& table(); const ScalarColumn& roTime() const; ScalarColumn& time(); const ScalarColumn& roPriority() const; ScalarColumn& priority(); const ScalarColumn& roMessage() const; ScalarColumn& message(); const ScalarColumn& roLocation() const; ScalarColumn& location(); const ScalarColumn& roObjectID() const; ScalarColumn& objectID(); // // Defines the minimal set of columns in the table (more may exist, but // are ignored. enum Columns { // MJD in seconds, UT. (Double.) TIME, // Message importance. (String). PRIORITY, // Informational message. (String). MESSAGE, // Source code origin of the log message. Usually a combination of // class name, method name, file name and line number, but any String // is legal. LOCATION, // ObjectID of distributed object that created the message (String). // If empty, no OBJECT_ID was set. OBJECT_ID }; // Turn the Columns enum into a String which is the actual // column name in the Table. static String columnName(Columns which); // Description of the log table. You can use this if, e.g., you do not // want to use the storage managers that this class creates by default // (currently Miriad). static TableDesc logTableDescription(); // Write out any pending output to the table. virtual void flush (Bool global=True); // Write a message (usually from another logsink) into the local one. virtual void writeLocally (Double time, const String& message, const String& priority, const String& location, const String& objectID); // Clear the local sink (i.e. remove all messages from it). virtual void clearLocally(); // Returns the id for this class. static String localId( ); // Returns the id of the LogSink in use. String id( ) const; // Make a LogSink for a TableLogSink with a new table. // Default filter is NORMAL. // static LogSink makeSink (const String& fileName); static LogSink makeSink (LogMessage::Priority filter, const String& fileName); static LogSink makeSink (const LogFilterInterface& filter, const String& fileName); // private: // Avoid duplicating code in copy ctor and assignment operator void copy_other(const TableLogSink& other); // Make a new log table. void makeTable (SetupNewTable&); // Attach the column objects and create unit keywor if needed. void attachCols(); // Initialize the object. void init (const String& fileName); Table log_table_p; ScalarColumn time_p; ScalarColumn priority_p; ScalarColumn message_p; // Origin ScalarColumn location_p; // ObjectID ScalarColumn id_p; }; //# Inlines inline const Table& TableLogSink::table() const {return log_table_p;} inline Table& TableLogSink::table() {return log_table_p;} inline const ScalarColumn& TableLogSink::roTime() const {return time_p;} inline ScalarColumn& TableLogSink::time() {return time_p;} inline const ScalarColumn& TableLogSink::roPriority() const {return priority_p;} inline ScalarColumn& TableLogSink::priority() {return priority_p;} inline const ScalarColumn& TableLogSink::roLocation() const {return location_p;} inline ScalarColumn& TableLogSink::location() {return location_p;} inline const ScalarColumn& TableLogSink::roObjectID() const {return id_p;} inline ScalarColumn& TableLogSink::objectID() {return id_p;} inline const ScalarColumn& TableLogSink::roMessage() const {return message_p;} inline ScalarColumn& TableLogSink::message() {return message_p;} inline LogSink TableLogSink::makeSink (const String& fileName) { return makeSink (LogFilter(), fileName); } inline LogSink TableLogSink::makeSink (LogMessage::Priority filter, const String& fileName) { return makeSink (LogFilter(filter), fileName); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/LogTables/test/000077500000000000000000000000001476623553700174315ustar00rootroot00000000000000casacore-3.7.1/tables/LogTables/test/CMakeLists.txt000066400000000000000000000005021476623553700221660ustar00rootroot00000000000000set (tests dLogging dLogging2 tLoggerHolder tLogging ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_tables) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/tables/LogTables/test/dLogging.cc000066400000000000000000000140351476623553700214750ustar00rootroot00000000000000//# dLogging.cc: This program demonstrates the logging system. //# Copyright (C) 1996,1997,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! //#!! //#!! If you modify this file, change casa/Logging.h to reflect the changes. //#!! //#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! //##### This file is documented in casa/Logging.h. #include #include #include #include #include #include #include class DataClass { public: DataClass(const IPosition &shape, const LogSink &sink); // 1 void set(Int toWhat); // 2 LogIO &sink() {return os_p;} // 3 Array &data() {return data_p;} // 4 const Array &data() const {return data_p;} // 5 private: // 6 Vector data_p; // 7 LogSink log_sink_p; // 8 LogIO os_p; // 9 }; DataClass::DataClass(const IPosition &shape, const LogSink &sink) : log_sink_p(sink), os_p(log_sink_p) // 1 { // 2 os_p << LogOrigin("DataClass", // 3 "DataClass(const IPosition &shape, const LogSink &sink)"); // 4 // 5 if (shape.nelements() != 1) { // 6 os_p << LogIO::SEVERE << WHERE << // 7 "Illegal Shape! Must be one dimensional." << LogIO::EXCEPTION; // 8 } // 9 // 10 data_p.resize(shape(0)); // 11 os_p << "Inital shape " << shape << "and value 2" << // 12 LogIO::NORMAL << LogIO::POST; // 13 // 14 set(2); // 15 } void DataClass::set(Int toWhat) { os_p << LogIO::NORMAL << LogOrigin("DataClass", "set(Int toWhat)"); // 1 os_p << "Setting data values to " << toWhat << WHERE << LogIO::POST; // 2 uInt n = data_p.nelements(); // 3 for (uInt i=0; i < n; i++) { // 4 #ifdef AIPS_DEBUG // 5 os_p << LogIO::DEBUGGING << WHERE << // 6 "Setting element " << i << " to " << toWhat << LogIO::POST; // 7 #endif // 8 data_p(i) = toWhat; // 9 } } void square(DataClass &object) { object.sink() << LogIO::NORMAL << WHERE << // 1 LogOrigin("square(DataClass &object)") << "Squaring data elements" // 2 << LogIO::POST; // 3 object.data() *= object.data(); // 4 } float sum(const DataClass &object) { LogIO global(LogOrigin("sum(const DataClass &object)")); // 1 float theSum = sum(object.data()); // 2 global << WHERE << "Sum of object is: " << theSum; // 3 return theSum; // 4 } int main() { LogSink::globalSink().filter(LogFilter(LogMessage::DEBUGGING));// 1 LogSink logger = TableLogSink::makeSink ("dLogging_tmp_messages"); // 2 // 3 IPosition legalShape(1, 10); // 4 DataClass dc(legalShape, logger); // 5 // 6 square(dc); // 7 // 8 Float total = sum(dc); // 9 if (total != 40) { // 10 cout << "sum is incorrect" << endl; // 11 return 1; // 12 } // 13 return 0; // 14 } casacore-3.7.1/tables/LogTables/test/dLogging2.cc000066400000000000000000000156651476623553700215710ustar00rootroot00000000000000//# dLogging.cc: This program demonstrates the logging system. //# Copyright (C) 1996,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! //#!! //#!! If you modify this file, change casa/Logging.h to reflect the changes. //#!! //#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! //##### This file is documented in casa/Logging.h. #include #include #include #include #include #include class DataClass { public: DataClass(const IPosition &shape, const LogSink &sink); // 1 void set(Int toWhat); // 2 LogSink &sink() {return log_sink_p;} // 3 Array &data() {return data_p;} // 4 const Array &data() const {return data_p;} // 5 private: // 6 Vector data_p; // 7 LogSink log_sink_p; // 8 }; DataClass::DataClass(const IPosition &shape, const LogSink &sink) : log_sink_p(sink) // 1 { // 2 LogOrigin where("DataClass", // 3 "DataClass(const IPosition &shape, const LogSink &sink)", // 4 WHERE); // 5 LogMessage logMessage(where); // 6 // 7 if (shape.nelements() != 1) { // 8 logMessage.priority(LogMessage::SEVERE).line(__LINE__).message( // 9 "Illegal Shape! Must be one dimensional."); // 10 log_sink_p.postThenThrow(logMessage, AipsError()); // 11 } // 12 // 13 data_p.resize(shape(0)); // 14 ostringstream buffer; // 15 buffer << "Inital shape " << shape << "and value 2"; // 16 log_sink_p.post( // 17 logMessage.priority(LogMessage::NORMAL).line(__LINE__). // 19 message(buffer)); // 19 // 20 set(2); // 21 } void DataClass::set(Int toWhat) { LogOrigin where("DataClass", "set(Int toWhat)", WHERE); // 1 LogMessage logMessage(where); // 2 ostringstream buffer; // 3 buffer << "Setting data values to " << toWhat; // 4 log_sink_p.post(logMessage.priority(LogMessage::NORMAL).line(__LINE__). // 5 message(buffer)); // 6 uInt n = data_p.nelements(); // 7 for (uInt i=0; i < n; i++) { // 8 #ifdef AIPS_DEBUG // 9 ostringstream buffer; // 10 buffer << "Setting element " << i << " to " << toWhat; // 11 logMessage.priority(LogMessage::DEBUGGING).line(__LINE__). // 12 message(buffer); // 13 log_sink_p.post(logMessage); // 14 #endif // 15 data_p(i) = toWhat; // 16 } } void square(DataClass &object) { object.sink().post(LogMessage("Squaring object.data elements", // 1 LogOrigin("square(DataClass &object)", // 2 WHERE))); // 3 object.data() *= object.data(); // 4 } float sum(const DataClass &object) { float theSum = sum(object.data()); // 1 ostringstream buffer; // 2 buffer << "Sum of object is: " << theSum; // 3 LogSink::postGlobally(LogMessage(buffer, // 4 LogOrigin("sum(const DataClass &object)", WHERE))); // 5 return theSum; // 6 } int main() { LogSink::globalSink().filter(LogFilter(LogMessage::DEBUGGING));// 1 LogSink logger = TableLogSink::makeSink ("dLogging2_tmp_messages"); // 2 // 3 IPosition legalShape(1, 10); // 4 DataClass dc(legalShape, logger); // 5 // 6 square(dc); // 7 // 8 Float total = sum(dc); // 9 if (total != 40) { // 10 cout << "sum is incorrect" << endl; // 11 return 1; // 12 } // 13 return 0; // 14 } casacore-3.7.1/tables/LogTables/test/tLoggerHolder.cc000066400000000000000000000072461476623553700225120ustar00rootroot00000000000000//# tLoggerHolder.cc: Test program for class LoggerHolder //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include void doIt (Bool tempClose) { LoggerHolder sublogger (False); LoggerHolder sublogger1 (False); LoggerHolder logger (False); logger.addParent (sublogger); logger.addParent (sublogger1); sublogger.logio() << "subtest1" << LogIO::POST; logger.logio() << "test1" << LogIO::POST; // Make sure we have a new table. if (Table::isReadable ("tLoggerHolder_tmp.log")) { TableUtil::deleteTable ("tLoggerHolder_tmp.log"); } // Create with a TableLogSink. // Test copy ctor and assignment. LoggerHolder logger2a ("tLoggerHolder_tmp.log", True); logger2a.addParent (logger); LoggerHolder logger2 (logger); logger2 = logger2a; logger2.addParent (sublogger); logger2.logio() << "testtable" << LogIO::POST; logger.logio() << "test2" << LogIO::POST; AlwaysAssertExit (logger.logio().localSink().nelements() == 2); AlwaysAssertExit (logger2.logio().localSink().nelements() == 1); AlwaysAssertExit (logger.sink().nelements() == 2); AlwaysAssertExit (logger2.sink().nelements() == 1); if (tempClose) { logger2.tempClose(); } AlwaysAssertExit (logger2.isTempClosed() == tempClose); { Table tab("tLoggerHolder_tmp.log"); AlwaysAssertExit (tab.nrow() == 1); } uInt nmsg=0; for (LoggerHolder::const_iterator iter = logger2.begin(); iter != logger2.end(); iter++) { cout << iter->time() << ' ' << (*iter).message() << endl; nmsg++; } AlwaysAssertExit (nmsg == 5); AlwaysAssertExit (logger2.isTempClosed() == tempClose); nmsg = 0; for (LoggerHolder::const_iterator iter = sublogger.begin(); iter != sublogger.end(); iter++) { cout << iter->time() << ' ' << (*iter).message() << endl; nmsg++; } AlwaysAssertExit (nmsg == 1); nmsg = 0; for (LoggerHolder::const_iterator iter = sublogger1.begin(); iter != sublogger1.end(); iter++) { cout << iter->time() << ' ' << (*iter).message() << endl; nmsg++; } AlwaysAssertExit (nmsg == 0); } int main() { try { doIt (False); doIt (True); } catch (std::exception& x) { cout << "Unexpected exception: " << x.what() << endl; return 1; } catch (...) { cout << "Unexpected unknown exception" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/tables/LogTables/test/tLogging.cc000066400000000000000000000453111476623553700215160ustar00rootroot00000000000000//# tLogging.cc: Test the logging classes //# Copyright (C) 1996,1997,1998,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void testLogFilter() { // LogFilter(LogMessage::Priority lowest=LogMessage::DEBUGGING); LogFilter low(LogMessage::DEBUGGING); LogFilter normal(LogMessage::NORMAL); LogFilter warn(LogMessage::WARN); LogFilter severe(LogMessage::SEVERE); LogFilter tmp; LogMessage message; // LogMessage::Priority lowestPriority() const; AlwaysAssertExit(low.lowestPriority() == LogMessage::DEBUGGING); AlwaysAssertExit(normal.lowestPriority() == LogMessage::NORMAL); AlwaysAssertExit(severe.lowestPriority() == LogMessage::SEVERE); AlwaysAssertExit(warn.lowestPriority() == LogMessage::WARN); AlwaysAssertExit(tmp.lowestPriority() == LogMessage::NORMAL); // LogFilter(const LogFilter &other); // LogFilter &operator=(const LogFilter &other); tmp = severe; LogFilter copy(severe); AlwaysAssertExit((tmp.lowestPriority() == copy.lowestPriority()) && (tmp.lowestPriority() == LogMessage::SEVERE)); // Bool pass(const LogMessage &message) const; message.priority(LogMessage::DEBUGGING); AlwaysAssertExit(low.pass(message) && !normal.pass(message) && !warn.pass(message) && !severe.pass(message)); message.priority(LogMessage::NORMAL); AlwaysAssertExit(low.pass(message) && normal.pass(message) && !warn.pass(message) && !severe.pass(message)); message.priority(LogMessage::SEVERE); AlwaysAssertExit(low.pass(message) && normal.pass(message) && warn.pass(message) && severe.pass(message)); message.priority(LogMessage::WARN); AlwaysAssertExit(low.pass(message) && normal.pass(message) && warn.pass(message) && !severe.pass(message)); // LogFilter(const String& expr); LogFilterTaql expr1 ("LOCATION=='::abc' && !(PRIORITY in ['INFO','DEBUGGING'])"); LogFilterTaql expr2 ("LOCATION=='::abc' && PRIORITY in ['INFO','DEBUGGING']"); LogFilterTaql expr(expr2); expr = expr1; AlwaysAssertExit (!expr.pass(LogMessage(String("abc"), LogMessage::NORMAL))); AlwaysAssertExit (!expr.pass(LogMessage(String("abc"), LogMessage::DEBUGGING))); AlwaysAssertExit (expr.pass(LogMessage(String("abc"), LogMessage::WARN))); AlwaysAssertExit (expr.pass(LogMessage(String("abc"), LogMessage::SEVERE))); AlwaysAssertExit (!expr.pass(LogMessage(String("abcd"), LogMessage::WARN))); AlwaysAssertExit (!expr.pass(LogMessage(String("abcd"), LogMessage::SEVERE))); // ~LogFilter() at end of block } void testLogMessage() { // LogMessage(Priority=NORMAL); // Priority priority() const; // const String &message() const; { LogMessage m1; AlwaysAssertExit(m1.priority() == LogMessage::NORMAL); LogMessage m2(LogMessage::SEVERE); AlwaysAssertExit(m2.priority() == LogMessage::SEVERE); AlwaysAssertExit(m2.message() == ""); } // LogMessage(const LogOrigin &sourceLocation, Priority=NORMAL); // LogMessage(const String &message, const LogOrigin &sourceLocation, // Priority=NORMAL); // const LogOrigin &origin() const; // LogMessage(const LogMessage &other); // LogMessage &operator=(const LogMessage &other); { LogMessage m1(LogOrigin("test"), LogMessage::SEVERE); AlwaysAssertExit(m1.priority() == LogMessage::SEVERE && m1.message()==""); AlwaysAssertExit(m1.origin().functionName() == "test"); LogMessage m2("message", LogOrigin("test"), LogMessage::SEVERE); AlwaysAssertExit(m2.priority() == LogMessage::SEVERE && m2.message()=="message" && m2.origin().functionName() == "test"); LogMessage m3(m2); AlwaysAssertExit(m3.priority() == m2.priority() && m3.message() == m2.message() && m3.origin().functionName() == m2.origin().functionName()); LogMessage m4; m4 = m2; AlwaysAssertExit(m4.priority() == m2.priority() && m4.message() == m2.message() && m4.origin().functionName() == m2.origin().functionName()); } // LogMessage &message(const String &message, Bool keepLastTime = False); // const Time &messageTime() const; // uInt line() const; // LogMessage &line(uInt which); // LogMessage &origin(const LogOrigin &origin); // LogMessage &priority(Priority which); // static const String &toString(Priority which); // String toString() const; // global operator<< { LogMessage m; m.message("hello"); AlwaysAssertExit(m.message() == "hello"); Time now; // 100 msec should be bigger than clock tick? AlwaysAssertExit(now - m.messageTime() >= 0 && now - m.messageTime() < 0.1); m.line(100); AlwaysAssertExit(m.line() == 100); m.origin(LogOrigin("test")); AlwaysAssertExit(m.origin().functionName() == "test"); m.priority(LogMessage::SEVERE); AlwaysAssertExit(m.priority() == LogMessage::SEVERE); AlwaysAssertExit(LogMessage::toString(m.priority()) == "SEVERE"); String message = m.toString(); AlwaysAssertExit(message.contains(String("hello")) && message.contains(String("test")) && message.contains(String("SEVERE"))); ostringstream os; os << m; String cached(os); cached = cached(0, cached.length()-1); // get rid of trailing nl AlwaysAssertExit(cached == message); } // ~LogMessage(); - implicit at end of blocks } void testLogOrigin() { // LogOrigin(); // const String &functionName() const; // const String &className() const; // const ObjectID &objectID() const; // uInt line() const; // LogOrigin &fileName(const String &fileName); { LogOrigin empty; AlwaysAssertExit(empty.functionName() == "" && empty.className() == "" && empty.objectID().isNull() && empty.line() == 0 && empty.fileName() == ""); } // Takes the place of WHERE, which would be used in user code SourceLocation location; location.fileName = "file"; location.lineNumber = 10; // LogOrigin(const String &globalFunctionName, const char *fileName=0, // uInt lineNumber = 0); { LogOrigin global("global", &location); AlwaysAssertExit(global.functionName() == "global" && global.className() == "" && global.objectID().isNull() && global.line() == 10 && global.fileName() == "file"); } // LogOrigin(const String &className, const String &memberFuncName, // const char *fileName=0, uInt lineNumber = 0); { LogOrigin member("class", "member", &location); AlwaysAssertExit(member.functionName() == "member" && member.className() == "class" && member.objectID().isNull() && member.line() == 10 && member.fileName() == "file"); } // LogOrigin(const String &className, const String &memberFuncName, // const ObjectID &id, const char *fileName=0, uInt lineNumber = 0); // LogOrigin(const LogOrigin &other); // LogOrigin &operator=(const LogOrigin &other); // String fullName() const; // LogOrigin &functionName(const String &funcName); // LogOrigin &className(const String &className); // LogOrigin &objectID(const ObjectID &id); // LogOrigin &line(uInt which); // const String &fileName() const; // String toString() const; // global ostream &operator<<(ostream &os, const LogOrigin &origin); { ObjectID id; LogOrigin distributed("class", "member", id, &location); AlwaysAssertExit(distributed.functionName() == "member" && distributed.className() == "class" && distributed.objectID() == id && distributed.line() == 10 && distributed.fileName() == "file" && distributed.fullName() == "class::member"); LogOrigin t1(distributed), t2; t2 = distributed; AlwaysAssertExit(distributed.functionName() == t1.functionName() && distributed.className() == t1.className() && distributed.objectID() == t1.objectID() && distributed.line() == t1.line() && distributed.fileName() == t1.fileName()); AlwaysAssertExit(distributed.functionName() == t2.functionName() && distributed.className() == t2.className() && distributed.objectID() == t2.objectID() && distributed.line() == t2.line() && distributed.fileName() == t2.fileName()); LogOrigin t3; t3.functionName(t1.functionName()).className(t1.className()). objectID(t1.objectID()).line(t1.line()).fileName(t1.fileName()); AlwaysAssertExit(distributed.functionName() == t3.functionName() && distributed.className() == t3.className() && distributed.objectID() == t3.objectID() && distributed.line() == t3.line() && distributed.fileName() == t3.fileName()); String s = t3.toString(); AlwaysAssertExit(s.contains(String("class")) && s.contains(String("member")) && s.contains(String("file"))); ostringstream buffer; buffer << t3; String s2(buffer); AlwaysAssertExit(s2 == s); } // ~LogOrigin(); - implicit at end of blocks } const char *tableNames[] = { "tLogging_tmp", "tLogging_tmp2", 0}; void cleanup() { int i = 0; while (tableNames[i]) { Directory deleteme(tableNames[i]); if (deleteme.exists()) { deleteme.removeRecursive(); } i++; } } void testLogSink() { cleanup(); // LogSink(const LogFilter &filter); LogSink sink1(LogMessage::SEVERE); ostringstream os; // LogSink(const LogFilter &filter, ostream *os); LogSink sink2(LogMessage::SEVERE, &os); // LogSink(const LogFilter &filter, const String &fileName); LogSink sink3 = TableLogSink::makeSink (LogMessage::SEVERE, tableNames[0]); LogSinkInterface *newGlobal = new TableLogSink(LogMessage::SEVERE, tableNames[1]); LogSinkInterface *copy = newGlobal; AlwaysAssertExit(newGlobal); // static void globalSink(LogSinkInterface *&fromNew); LogSink::globalSink(newGlobal); AlwaysAssertExit(!newGlobal); // static LogSinkInterface &globalSink(); AlwaysAssertExit(copy == &LogSink::globalSink()); LogMessage message; message.message("test"); // Bool post(const LogMessage &message); AlwaysAssertExit(! sink1.post(message) && ! sink2.post(message) && ! sink3.post(message)); message.priority(LogMessage::SEVERE); AlwaysAssertExit(sink1.post(message) && sink2.post(message) && sink3.post(message)); String fromos(os); AlwaysAssertExit(fromos.contains("test")); Table logTable(tableNames[0]); Table logTable2(tableNames[1]); ScalarColumn messageColumn(logTable, TableLogSink::columnName(TableLogSink::MESSAGE)); ScalarColumn messageColumn2(logTable2, TableLogSink::columnName(TableLogSink::MESSAGE)); AlwaysAssertExit(messageColumn(0) == "test"); // LogSink(const LogSink &other); LogSink sink4(sink3); // LogSink &operator=(const LogSink &other); LogSink sink5(LogMessage::SEVERE); sink5 = sink3; sink4.post(message); sink5.post(message); AlwaysAssertExit(logTable.nrow() == 3 && messageColumn(1) == "test" && messageColumn(2) == "test"); AlwaysAssertExit(logTable2.nrow() == 5 && messageColumn2(4) == "test"); // static Bool postGlobally(const LogMessage &message); sink5.postGlobally(message); AlwaysAssertExit(logTable2.nrow() == 6); // virtual Bool postLocally(const LogMessage &message); sink5.postLocally(message); AlwaysAssertExit(logTable.nrow() == 4); // const LogSinkInterface &localSink() const; AlwaysAssertExit(&sink3.localSink() == &sink4.localSink()); // virtual const LogFilter &filter() const; AlwaysAssertExit(dynamic_cast(sink3.filter()). lowestPriority() == LogMessage::SEVERE); // virtual LogSinkInterface &filter(const LogFilter &filter); sink3.filter(LogFilter(LogMessage::NORMAL)); AlwaysAssertExit(dynamic_cast(sink3.filter()). lowestPriority() == LogMessage::NORMAL); // void postThenThrow(const LogMessage &message); Bool caught = False; try { sink5.postThenThrow(message, AipsError()); } catch (std::exception& x) { caught = True; AlwaysAssertExit(String(x.what()).contains("test")); AlwaysAssertExit(logTable.nrow() == 5 && logTable2.nrow() == 7); } AlwaysAssertExit(caught); // static void postGloballyThenThrow(const LogMessage &message); caught = False; try { sink5.postGloballyThenThrow(message); } catch (std::exception& x) { caught = True; AlwaysAssertExit(String(x.what()).contains("test")); AlwaysAssertExit(logTable.nrow() == 5 && logTable2.nrow() == 8); } AlwaysAssertExit(caught); // LogSink &localSink(LogSinkInterface *&fromNew); LogSinkInterface *newLocal = new NullLogSink(LogMessage::SEVERE); AlwaysAssertExit(newLocal); copy = newLocal; sink5.localSink(newLocal); AlwaysAssertExit(!newLocal); AlwaysAssertExit(copy == &sink5.localSink()); AlwaysAssertExit(&sink5.localSink() != &sink4.localSink()); // LogSink &localSink(LogSinkInterface *&fromNew); LogSinkInterface *newLocalm = new MemoryLogSink(LogMessage::SEVERE); AlwaysAssertExit(newLocalm); copy = newLocalm; sink5.localSink(newLocalm); AlwaysAssertExit(!newLocalm); AlwaysAssertExit(copy == &sink5.localSink()); AlwaysAssertExit(&sink5.localSink() != &sink4.localSink()); // ~LogSink(); - implicit at end of block } void testLogIO() { { ostringstream ostr; LogSink sls(LogMessage::NORMAL, &ostr); // LogIO(LogSink &sink); LogIO os(sls); // ostream& output(); // void post(); os << "This SHOULD post" << LogIO::POST; String s(ostr); AlwaysAssert(s.contains("SHOULD"), AipsError); // ~LogIO(); } { ostringstream ostr; LogSink sls(LogMessage::NORMAL, &ostr); // LogIO(const LogOrigin &or, LogSink &sink); LogIO os(LogOrigin("HELLO"), sls); os << "This SHOULD post" << LogIO::POST; String s(ostr); AlwaysAssert(s.contains("HELLO"), AipsError); } { ostringstream ostr; LogSink sls(LogMessage::NORMAL, &ostr); LogIO os(sls); // void priority(LogMessage::Priority which); os << LogIO::DEBUGGING << "This SHOULD NOT post" << LogIO::POST; String s(ostr); AlwaysAssert(s == "", AipsError); } { ostringstream ostr; LogSinkInterface *sls = new StreamLogSink(LogMessage::NORMAL, &ostr); LogSink::globalSink(sls); // LogIO(); LogIO os; os << "This SHOULD post" << LogIO::POST; String s(ostr); AlwaysAssert(s.contains("SHOULD"), AipsError); // Put back the null log sink since ostr is now frozen! sls = new NullLogSink; LogSink::globalSink(sls); } { ostringstream ostr; LogSink sls(LogMessage::NORMAL, &ostr); LogIO os(sls); Bool caught = False; try { // void postThenThrow(); os << "This SHOULD post" << LogIO::EXCEPTION; } catch (std::exception& x) { caught = True; } AlwaysAssert(caught, AipsError); String s(ostr); AlwaysAssert(s.contains("SHOULD"), AipsError); // ~LogIO(); } { ostringstream ostr; LogSink sls(LogMessage::NORMAL, &ostr); LogIO os(sls); Bool caught = False; try { // void postThenThrow(); os << "This SHOULD post"; os.postThenThrow (DuplError("duplicate")); } catch (DuplError& x) { caught = True; } AlwaysAssert(caught, AipsError); String s(ostr); AlwaysAssert(s.contains("SHOULD"), AipsError); AlwaysAssert(s.contains("duplicate"), AipsError); // ~LogIO(); } } void testLogAny (LogSink& sink) { LogIO lio(sink); lio << "test " << LogIO::WARN << "message" << LogIO::SEVERE << LogIO::POST; AlwaysAssertExit (sink.nelements() == 1); lio << "test2 message" << LogIO::POST; AlwaysAssertExit (sink.nelements() == 2); AlwaysAssertExit (lio.localSink().nelements() == 2); AlwaysAssertExit (sink.getMessage(0) == "test message"); AlwaysAssertExit (sink.getPriority(0) == "SEVERE"); AlwaysAssertExit (sink.getMessage(1) == "test2 message"); AlwaysAssertExit (sink.getPriority(1) == "INFO"); sink.localSink().writeLocally (sink.getTime(0), sink.getMessage(1), sink.getPriority(0), sink.getLocation(0), sink.getObjectID(0)); AlwaysAssertExit (sink.nelements() == 3); AlwaysAssertExit (sink.getMessage(2) == "test2 message"); AlwaysAssertExit (sink.getPriority(2) == "SEVERE"); sink.clearLocally(); AlwaysAssertExit (sink.nelements() == 0); lio << "test " << LogIO::WARN << "message" << LogIO::SEVERE << LogIO::POST; AlwaysAssertExit (sink.nelements() == 1); AlwaysAssertExit (sink.getMessage(0) == "test message"); AlwaysAssertExit (sink.getPriority(0) == "SEVERE"); } void testLogMemory() { LogFilter tmp; LogSink sink(tmp, False); testLogAny (sink); } void testLogTable() { LogFilter tmp; cleanup(); LogSink sink = TableLogSink::makeSink (tmp, tableNames[0]); testLogAny (sink); } int main() { try { testLogFilter(); testLogMessage(); testLogOrigin(); // Also tests other classes derived from LogSinkInterface testLogSink(); testLogIO(); testLogMemory(); testLogTable(); } catch (std::exception& x) { cout << "Caught an exception : " << x.what() << endl; exit(1); } cerr << "OK (nothing else should have been printed by this program)\n"; return 0; } casacore-3.7.1/tables/PlainTable.drawio.svg000066400000000000000000001242321476623553700206220ustar00rootroot00000000000000 TableBaseTable- nrrow_p: rownr_t- name_p: StringPlainTable- tsmOption_p: TSMOptionTableInfo- type_p: String- subType_p: String- readme_p: StringTableDescColumnDescSetTableLockData- itsLock: LockFile*- itsReleaseCallback- itsReleaseParent: void*TableCache- tableMap_p: std::mapTableSyncData- itsModifyCounter: uInt- itsMemIO: MemoryIO- itsAipsIO: AipsIOMultiFileBaseDataManagerBaseColumnColumnSet- StorageOption- nrrow_p: rownr_tBaseColumnDescDataManagerColumnPlainColumn- originalName_p: String- rtraceColumn_p: Bool- wtraceColumn_p: BoolScalarColumnData<T>- undefVal_p: TArrayColumnData<T>- shapeCol_p: IPosition- checkValueLength_p: BoolScalarColumnDesc<T>ArrayColumnDescBaseTableColumn- canChangeShape_p: Bool- isColWritable_p: BoolScalarColumn<T>+ get functions+ put functionsArrayColumn<T>+ get functions+ put functions
        0..n
        0..n
        0..n
        0..n
        0..n
        0..n
        Use
        Use
        ArrayColumnDesc<T>ColumnCache- itsStart: rownr_t- itsEnd: rownr_t- itsIncr: rownr_t
        0..n
        0..n
        ArrayColumnBase- shape_p: IPosition- itsData: void*
        Text is not SVG - cannot display
        casacore-3.7.1/tables/PlainTable.drawio.svg.extrahtml000066400000000000000000000003101476623553700226170ustar00rootroot00000000000000
        The TableDesc diagram and DataManager diagram show the class structure of table description and data managers.
        casacore-3.7.1/tables/TaQL.h000066400000000000000000000110051476623553700155450ustar00rootroot00000000000000//# TaQL.h: The TaQL module - Casacore data querying //# Copyright (C) 1994-2010 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TAQL_H #define TABLES_TAQL_H //# Includes //# table expressions (for selection of rows) #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // // TaQL is the query language for Casacore tables // // // // // //
      • Tables module // // // "TaQL" is the Table Query Language. Its pronounciation rhymes with bagel. // // // TaQL is an SQL-like language to query a Casacore table. // Amongst its options are row select, sort, update, and delete. //
        Some more information is given in the description of the // Tables module. // A detailed description is given in note 199. // // The high-level interface is using a TaQL command as described in // note 199. Such a command can be given // in C++ (using TableParse.h), Python or the shell program 'taql'. // The code for parsing and executing TaQL commands is quite complex. // Processing a command consists of two steps. //
          //
        1. // First a command is parsed using 'flex' and 'bison'. The file TableGram.ll // is used by 'flex' to recognize the tokens in the command. The file // TableGram.yy defines the grammar which is used by bison to invoke // actions on the recognized parts of the command. These actions // consist of building a parse tree by means of class TaQLNode and // associated classes. In this way the command is syntactically checked. //
        2. // If the parsing is done successfully, the command is executed // by walking through the parse tree using class TaQLNodeHandler. // In its turn that class invokes functions in class TableParseQuery // to check (semantically) and execute commands such as SELECT, UPDATE, etc.. // Note that subqueries are executed before the entire parse tree has // been walked through, thus before possible later semantic errors are // detected. //
          Expressions in the parse tree are converted to expression trees // using the various TableExprNode classes. Expression trees are evaluated // for each row in a table. Note that expressions can be used in many // parts of a TaQL command. // Functions in a command are handled by TableParseFunc. //
        // // Expression trees can also be generated directly in C++ using class // TableExprNode which is overloaded for many operators and functions // (such as sin, max, etc.). In fact, TaQLNodeHandler uses this code. // For example: // // // Table tab("my.ms"); // Table selection (tab(tab.col("ANTENNA1") == tab.col("ANTENNA2") && // tab.col("SPECTRAL_WINDOW_ID") == 0)); // // creates a (reference) table containing the autocorrelations of the // first spectral window in "my.ms". // //
        // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/000077500000000000000000000000001476623553700153775ustar00rootroot00000000000000casacore-3.7.1/tables/TaQL/ExprAggrNode.cc000066400000000000000000000422661476623553700202450ustar00rootroot00000000000000//# ExprAggrNode.cc: TaQL node representing an aggregate function //# Copyright (C) 2013 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableExprAggrNode::TableExprAggrNode (FunctionType ftype, NodeDataType dtype, ValueType vtype, const TableExprNodeSet& source, const vector& nodes, const Block& dtypeOper) : TableExprFuncNode (ftype, dtype, vtype, source, nodes, dtypeOper) { // Always treat an aggregate as a variable expression. // Otherwise it might be treated as constant and evaluated immediately // which cannot be done. exprtype_p = Variable; } Bool TableExprAggrNode::isAggregate() const { return True; } TableExprFuncNode::NodeDataType TableExprAggrNode::checkOperands (Block& dtypeOper, ValueType& resVT, FunctionType ftype, vector& nodes) { if (ftype >= FirstAggrArrayFunc && ftype < LastAggrArrayFunc && nodes.size() > 0 && nodes[0]->valueType() != VTArray) { throw TableInvExpr ("Argument of GxxxS functions has to be an array"); } for (const TENShPtr& n : nodes) { if (! TableExprNodeUtil::getAggrNodes(n.get()).empty()) { throw TableInvExpr ("The argument of an aggregate function cannot use " "an aggregate function"); } } resVT = VTScalar; switch (ftype) { case countallFUNC: checkNumOfArg (0, 0, nodes); return NTInt; case gcountFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTAny, NTInt, nodes); case gfirstFUNC: case glastFUNC: checkNumOfArg (1, 1, nodes); resVT = nodes[0]->valueType(); return checkDT (dtypeOper, NTAny, NTAny, nodes); case gexpridFUNC: checkNumOfArg (0, 0, nodes); return NTInt; case gaggrFUNC: checkNumOfArg (1, 1, nodes); resVT = VTArray; return checkDT (dtypeOper, NTAny, NTAny, nodes); case ghistFUNC: checkNumOfArg (4, 4, nodes); if (nodes[1]->dataType() != NTInt) { throw TableInvExpr ("2nd argument of function GHIST " "has to be a constant integer scalar"); } for (int i=1; i<4; ++i) { if (nodes[i]->valueType() != VTScalar || ! nodes[i]->isConstant()) { throw TableInvExpr ("2nd, 3rd and 4th argument of function GHIST " "have to be constant scalars"); } } resVT = VTArray; return checkDT (dtypeOper, NTReal, NTInt, nodes); case growidFUNC: checkNumOfArg (0, 0, nodes); resVT = VTArray; return checkDT (dtypeOper, NTAny, NTInt, nodes); case gminsFUNC: case gmaxsFUNC: resVT = VTArray; CASACORE_FALLTHROUGH; case gminFUNC: case gmaxFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTReal, NTReal, nodes); case gsumsFUNC: case gproductsFUNC: case gsumsqrsFUNC: resVT = VTArray; CASACORE_FALLTHROUGH; case gsumFUNC: case gproductFUNC: case gsumsqrFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTNumeric, NTNumeric, nodes); case gmeansFUNC: resVT = VTArray; CASACORE_FALLTHROUGH; case gmeanFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTNumeric, NTDouCom, nodes); case gvariances0FUNC: case gvariances1FUNC: case gstddevs0FUNC: case gstddevs1FUNC: resVT = VTArray; CASACORE_FALLTHROUGH; case gvariance0FUNC: case gvariance1FUNC: case gstddev0FUNC: case gstddev1FUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTNumeric, NTDouble, nodes); case grmssFUNC: resVT = VTArray; CASACORE_FALLTHROUGH; case grmsFUNC: case gmedianFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTReal, NTDouble, nodes); case gfractileFUNC: checkNumOfArg (2, 2, nodes); if (nodes[1]->valueType() != VTScalar || ! nodes[1]->isConstant()) { throw TableInvExpr ("2nd argument of function GFRACTILE " "has to be a constant scalar"); } return checkDT (dtypeOper, NTReal, NTDouble, nodes); case ganysFUNC: case gallsFUNC: resVT = VTArray; CASACORE_FALLTHROUGH; case ganyFUNC: case gallFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTBool, NTBool, nodes); case gntruesFUNC: case gnfalsesFUNC: resVT = VTArray; CASACORE_FALLTHROUGH; case gntrueFUNC: case gnfalseFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTBool, NTInt, nodes); default: throw TableInvExpr ("Unhandled aggregate function " + String::toString(ftype)); } } std::shared_ptr TableExprAggrNode::makeGroupAggrFunc() { // Create a new function object because each FuncSet needs its own one. itsFunc.reset (doMakeGroupAggrFunc()); return itsFunc; } Bool TableExprAggrNode::isLazyAggregate() const { return itsFunc->isLazy(); } TableExprGroupFuncBase* TableExprAggrNode::doMakeGroupAggrFunc() { if (funcType() == countallFUNC) { return new TableExprGroupCountAll(this); } else if (funcType() == gcountFUNC) { return new TableExprGroupCount(this); } else if (funcType() == gfirstFUNC) { return new TableExprGroupFirst(this); } else if (funcType() == glastFUNC) { return new TableExprGroupLast(this); } else if (funcType() == gexpridFUNC) { return new TableExprGroupExprId(this); } else if (funcType() == gaggrFUNC) { return new TableExprGroupAggr(this); } else if (funcType() == growidFUNC) { return new TableExprGroupRowid(this); } if (operands()[0]->valueType() == VTScalar) { switch (operands()[0]->dataType()) { case NTBool: switch (funcType()) { case ganyFUNC: return new TableExprGroupAny(this); case gallFUNC: return new TableExprGroupAll(this); case gntrueFUNC: return new TableExprGroupNTrue(this); case gnfalseFUNC: return new TableExprGroupNFalse(this); default: throw TableInvExpr ("Aggregate function " + String::toString(funcType()) + " cannot be used with a bool argument"); } case NTInt: switch (funcType()) { case gminFUNC: return new TableExprGroupMinInt(this); case gmaxFUNC: return new TableExprGroupMaxInt(this); case gsumFUNC: return new TableExprGroupSumInt(this); case gproductFUNC: return new TableExprGroupProductInt(this); case gsumsqrFUNC: return new TableExprGroupSumSqrInt(this); default: break; } // Fall through, so e.g. mean of ints can be done CASACORE_FALLTHROUGH; case NTDouble: switch (funcType()) { case gminFUNC: return new TableExprGroupMinDouble(this); case gmaxFUNC: return new TableExprGroupMaxDouble(this); case gsumFUNC: return new TableExprGroupSumDouble(this); case gproductFUNC: return new TableExprGroupProductDouble(this); case gsumsqrFUNC: return new TableExprGroupSumSqrDouble(this); case gmeanFUNC: return new TableExprGroupMeanDouble(this); case gvariance0FUNC: return new TableExprGroupVarianceDouble(this, 0); case gvariance1FUNC: return new TableExprGroupVarianceDouble(this, 1); case gstddev0FUNC: return new TableExprGroupStdDevDouble(this, 0); case gstddev1FUNC: return new TableExprGroupStdDevDouble(this, 1); case grmsFUNC: return new TableExprGroupRmsDouble(this); case gmedianFUNC: return new TableExprGroupFractileDouble(this, 0.5); case gfractileFUNC: return new TableExprGroupFractileDouble (this, operands()[1]->getDouble(0)); case ghistFUNC: return new TableExprGroupHistDouble (this, operands()[1]->getInt(0), operands()[2]->getDouble(0), operands()[3]->getDouble(0)); default: throw TableInvExpr ("Aggregate function " + String::toString(funcType()) + " cannot be used with an integer/double" " argument"); } case NTComplex: switch (funcType()) { case gsumFUNC: return new TableExprGroupSumDComplex(this); case gproductFUNC: return new TableExprGroupProductDComplex(this); case gsumsqrFUNC: return new TableExprGroupSumSqrDComplex(this); case gmeanFUNC: return new TableExprGroupMeanDComplex(this); case gvariance0FUNC: return new TableExprGroupVarianceDComplex(this, 0); case gvariance1FUNC: return new TableExprGroupVarianceDComplex(this, 1); case gstddev0FUNC: return new TableExprGroupStdDevDComplex(this, 0); case gstddev1FUNC: return new TableExprGroupStdDevDComplex(this, 1); default: throw TableInvExpr ("Aggregate function " + String::toString(funcType()) + " cannot be used with a dcomplex argument"); } default: break; } throw TableInvExpr ("Aggregate function " + String::toString(funcType()) + " is unknown for scalar data type " + String::toString(operands()[0]->dataType())); } // The operand is an array. switch (operands()[0]->dataType()) { case NTBool: switch (funcType()) { case ganyFUNC: return new TableExprGroupArrayAny(this); case gallFUNC: return new TableExprGroupArrayAll(this); case gntrueFUNC: return new TableExprGroupArrayNTrue(this); case gnfalseFUNC: return new TableExprGroupArrayNFalse(this); default: throw TableInvExpr ("Aggregate function " + String::toString(funcType()) + " cannot be used with a bool argument"); } case NTInt: switch (funcType()) { case gminFUNC: return new TableExprGroupMinArrayInt(this); case gmaxFUNC: return new TableExprGroupMaxArrayInt(this); case gsumFUNC: return new TableExprGroupSumArrayInt(this); case gproductFUNC: return new TableExprGroupProductArrayInt(this); case gsumsqrFUNC: return new TableExprGroupSumSqrArrayInt(this); default: break; } // Fall through, so e.g. mean of ints can be done CASACORE_FALLTHROUGH; case NTDouble: switch (funcType()) { case gminFUNC: return new TableExprGroupMinArrayDouble(this); case gmaxFUNC: return new TableExprGroupMaxArrayDouble(this); case gsumFUNC: return new TableExprGroupSumArrayDouble(this); case gproductFUNC: return new TableExprGroupProductArrayDouble(this); case gsumsqrFUNC: return new TableExprGroupSumSqrArrayDouble(this); case gmeanFUNC: return new TableExprGroupMeanArrayDouble(this); case gvariance0FUNC: return new TableExprGroupVarianceArrayDouble(this, 0); case gvariance1FUNC: return new TableExprGroupVarianceArrayDouble(this, 1); case gstddev0FUNC: return new TableExprGroupStdDevArrayDouble(this, 0); case gstddev1FUNC: return new TableExprGroupStdDevArrayDouble(this, 1); case grmsFUNC: return new TableExprGroupRmsArrayDouble(this); case gmedianFUNC: return new TableExprGroupFractileArrayDouble(this, 0.5); case gfractileFUNC: return new TableExprGroupFractileArrayDouble (this, operands()[1]->getDouble(0)); default: throw TableInvExpr ("Aggregate function " + String::toString(funcType()) + " cannot be used with an integer/double argument"); } case NTComplex: switch (funcType()) { case gsumFUNC: return new TableExprGroupSumArrayDComplex(this); case gproductFUNC: return new TableExprGroupProductArrayDComplex(this); case gsumsqrFUNC: return new TableExprGroupSumSqrArrayDComplex(this); case gmeanFUNC: return new TableExprGroupMeanArrayDComplex(this); case gvariance0FUNC: return new TableExprGroupVarianceArrayDComplex(this, 0); case gvariance1FUNC: return new TableExprGroupVarianceArrayDComplex(this, 1); case gstddev0FUNC: return new TableExprGroupStdDevArrayDComplex(this, 0); case gstddev1FUNC: return new TableExprGroupStdDevArrayDComplex(this, 1); default: throw TableInvExpr ("Aggregate function " + String::toString(funcType()) + " cannot be used with a dcomplex argument"); } default: break; } throw TableInvExpr ("Aggregate function " + String::toString(funcType()) + " is unknown for array data type " + String::toString(operands()[0]->dataType())); } Bool TableExprAggrNode::getBool (const TableExprId& id) { const TableExprIdAggr& aid = TableExprIdAggr::cast (id); if (itsFunc->isLazy()) { return itsFunc->getBool (aid.result().ids(id.rownr())); } TableExprGroupFuncSet& set = aid.result().funcSet(id.rownr()); return set.getFuncs()[itsFunc->seqnr()]->getBool(); } Int64 TableExprAggrNode::getInt (const TableExprId& id) { const TableExprIdAggr& aid = TableExprIdAggr::cast (id); if (itsFunc->isLazy()) { return itsFunc->getInt (aid.result().ids(id.rownr())); } TableExprGroupFuncSet& set = aid.result().funcSet(id.rownr()); return set.getFuncs()[itsFunc->seqnr()]->getInt(); } Double TableExprAggrNode::getDouble (const TableExprId& id) { if (dataType() != NTDouble) { return TableExprNodeRep::getDouble (id); } const TableExprIdAggr& aid = TableExprIdAggr::cast (id); if (itsFunc->isLazy()) { return itsFunc->getDouble (aid.result().ids(id.rownr())); } TableExprGroupFuncSet& set = aid.result().funcSet(id.rownr()); return set.getFuncs()[itsFunc->seqnr()]->getDouble(); } DComplex TableExprAggrNode::getDComplex (const TableExprId& id) { if (dataType() != NTComplex) { return TableExprNodeRep::getDComplex (id); } const TableExprIdAggr& aid = TableExprIdAggr::cast (id); if (itsFunc->isLazy()) { return itsFunc->getDComplex (aid.result().ids(id.rownr())); } TableExprGroupFuncSet& set = aid.result().funcSet(id.rownr()); return set.getFuncs()[itsFunc->seqnr()]->getDComplex(); } String TableExprAggrNode::getString (const TableExprId& id) { const TableExprIdAggr& aid = TableExprIdAggr::cast (id); if (itsFunc->isLazy()) { return itsFunc->getString (aid.result().ids(id.rownr())); } TableExprGroupFuncSet& set = aid.result().funcSet(id.rownr()); return set.getFuncs()[itsFunc->seqnr()]->getString(); } MVTime TableExprAggrNode::getDate (const TableExprId& id) { const TableExprIdAggr& aid = TableExprIdAggr::cast (id); if (itsFunc->isLazy()) { return itsFunc->getDate (aid.result().ids(id.rownr())); } TableExprGroupFuncSet& set = aid.result().funcSet(id.rownr()); return set.getFuncs()[itsFunc->seqnr()]->getDate(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/ExprAggrNode.h000066400000000000000000000120431476623553700200750ustar00rootroot00000000000000//# ExprAggrNode.h: TaQL node representing a scalar aggregate function //# Copyright (C) 2013 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_EXPRAGGRNODE_H #define TABLES_EXPRAGGRNODE_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations. class TableExprGroupFuncBase; class TableExprGroupFuncSet; // // TaQL node representing a scalar aggregate function // // // // // // A TableExprAggrNode object is a special TableExprFuncNode object. // Instead of operating on a single row, it operates on a group of table // rows, usually formed by means of the GROUPBY clause. // It aggregates the values in the rows in the group by means of an // aggregation function derived from TableExprGroupFuncBase. // Several standard aggregation functions (e.g., gmean, gmin, gsum) are // defined in TaQL and implemented this way. // // There are two types of aggregate function implementations: //
          //
        • Immediate aggregate functions calculate the results while the // groups are being formed. In this way they step sequentially through // the data. // This is only possible for functions that do not have to keep // to many data in memory. //
        • Lazy aggregate functions calculate the results after the groups are // formed using the vector of TableExprIds they get per group. // In this way only data for a single group might need to be kept in // memory. It is used, for instance, to calculate the median. //
        // Note that this class handles operands that are a scalar or array. // If array, all values in the array are used as individual values. // Class TableExprAggrNodeArray handles aggregate functions giving an // array result (e.g., function gaggr). // // It is also possible to define an aggregate function in a UDF derived // from class UDFBase. Such an aggregate function is instantiated as a // TableExprUDFNode(Array) object, not as TabeExprAggrNode(Array). // These functions are always lazy. //
        class TableExprAggrNode: public TableExprFuncNode { public: // Constructor. TableExprAggrNode (FunctionType, NodeDataType, ValueType, const TableExprNodeSet& source, const vector& nodes, const Block& dtypeOper); // This node does aggregation. virtual Bool isAggregate() const; // Check the operands of the aggregate function and return the // result's data type. static NodeDataType checkOperands (Block& dtypeOper, ValueType& resVT, FunctionType ftype, std::vector& nodes); // Get the operand node. TENShPtr operand() { return (operands().empty() ? TENShPtr() : operands()[0]); } // Create the correct aggregate function object. // It is also kept in case it is a lazy aggregate function. virtual std::shared_ptr makeGroupAggrFunc(); // Is the aggregate function a lazy or an immediate one? virtual Bool isLazyAggregate() const; // Functions to get the result of an aggregate function. // virtual Bool getBool (const TableExprId& id); virtual Int64 getInt (const TableExprId& id); virtual Double getDouble (const TableExprId& id); virtual DComplex getDComplex (const TableExprId& id); virtual String getString (const TableExprId& id); virtual MVTime getDate (const TableExprId& id); // private: // Do the actual creation of the correct aggregate function object. TableExprGroupFuncBase* doMakeGroupAggrFunc(); //# Data members. std::shared_ptr itsFunc; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/ExprAggrNodeArray.cc000066400000000000000000000251221476623553700212340ustar00rootroot00000000000000//# ExprAggrNodeArray.cc: TaQL node representing an aggregate function //# Copyright (C) 2013 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableExprAggrNodeArray::TableExprAggrNodeArray (TableExprFuncNode::FunctionType ftype, NodeDataType dtype, ValueType vtype, const TableExprNodeSet& source, const vector& nodes, const Block& dtypeOper, const TaQLStyle& style) : TableExprFuncNodeArray (ftype, dtype, vtype, source, nodes, dtypeOper, style) { // Always treat an aggregate as a variable expression. // Otherwise if might be treated as constant and evaluated immediately // which cannot be done. exprtype_p = Variable; } std::shared_ptr TableExprAggrNodeArray::makeGroupAggrFunc() { // Create a new function object because each FuncSet needs its own one. itsFunc = doMakeGroupAggrFunc(); return itsFunc; } Bool TableExprAggrNodeArray::isAggregate() const { return True; } Bool TableExprAggrNodeArray::isLazyAggregate() const { return itsFunc->isLazy(); } std::shared_ptr TableExprAggrNodeArray::doMakeGroupAggrFunc() { if (funcType() == TableExprFuncNode::gexpridFUNC) { return std::make_shared(this); } else if (funcType() == TableExprFuncNode::gaggrFUNC) { return std::make_shared(this); } else if (funcType() == TableExprFuncNode::growidFUNC) { return std::make_shared(this); } else if (funcType() == TableExprFuncNode::ghistFUNC) { Int64 nbin = operands()[1]->getInt(0); Double start = operands()[2]->getDouble(0); Double end = operands()[3]->getDouble(0); if (operands()[0]->valueType() == VTScalar) { return std::make_shared(this, nbin, start, end); } if (operands()[0]->dataType() == NTInt) { return std::make_shared(this, nbin, start, end); } return std::make_shared(this, nbin, start, end); } if (operands()[0]->valueType() == VTScalar) { throw TableInvExpr ("Aggregate function " + String::toString(funcType()) + " is unknown for scalar data type " + String::toString(operands()[0]->dataType())); } // The operand is an array. switch (operands()[0]->dataType()) { case NTBool: switch (funcType()) { case TableExprFuncNode::ganysFUNC: return std::make_shared(this); case TableExprFuncNode::gallsFUNC: return std::make_shared(this); case TableExprFuncNode::gntruesFUNC: return std::make_shared(this); case TableExprFuncNode::gnfalsesFUNC: return std::make_shared(this); default: throw TableInvExpr ("Aggregate function " + String::toString(funcType()) + " cannot be used with a bool argument"); } case NTInt: switch (funcType()) { case TableExprFuncNode::gminsFUNC: return std::make_shared(this); case TableExprFuncNode::gmaxsFUNC: return std::make_shared(this); case TableExprFuncNode::gsumsFUNC: return std::make_shared(this); case TableExprFuncNode::gproductsFUNC: return std::make_shared(this); case TableExprFuncNode::gsumsqrsFUNC: return std::make_shared(this); default: break; } // Fall through, so e.g. mean of ints can be done CASACORE_FALLTHROUGH; case NTDouble: switch (funcType()) { case TableExprFuncNode::gminsFUNC: return std::make_shared(this); case TableExprFuncNode::gmaxsFUNC: return std::make_shared(this); case TableExprFuncNode::gsumsFUNC: return std::make_shared(this); case TableExprFuncNode::gproductsFUNC: return std::make_shared(this); case TableExprFuncNode::gsumsqrsFUNC: return std::make_shared(this); case TableExprFuncNode::gmeansFUNC: return std::make_shared(this); case TableExprFuncNode::gvariances0FUNC: return std::make_shared(this, 0); case TableExprFuncNode::gvariances1FUNC: return std::make_shared(this, 1); case TableExprFuncNode::gstddevs0FUNC: return std::make_shared(this, 0); case TableExprFuncNode::gstddevs1FUNC: return std::make_shared(this, 1); case TableExprFuncNode::grmssFUNC: return std::make_shared(this); default: throw TableInvExpr ("Aggregate function " + String::toString(funcType()) + " cannot be used with an integer/double argument"); } case NTComplex: switch (funcType()) { case TableExprFuncNode::gsumsFUNC: return std::make_shared(this); case TableExprFuncNode::gproductsFUNC: return std::make_shared(this); case TableExprFuncNode::gsumsqrsFUNC: return std::make_shared(this); case TableExprFuncNode::gmeansFUNC: return std::make_shared(this); case TableExprFuncNode::gvariances0FUNC: return std::make_shared(this, 0); case TableExprFuncNode::gvariances1FUNC: return std::make_shared(this, 1); case TableExprFuncNode::gstddevs0FUNC: return std::make_shared(this, 0); case TableExprFuncNode::gstddevs1FUNC: return std::make_shared(this, 1); default: throw TableInvExpr ("Aggregate function " + String::toString(funcType()) + " cannot be used with a dcomplex argument"); } default: break; } throw TableInvExpr ("Array aggregate function " + String::toString(funcType()) + " is unknown"); } MArray TableExprAggrNodeArray::getArrayBool (const TableExprId& id) { const TableExprIdAggr& aid = TableExprIdAggr::cast (id); if (itsFunc->isLazy()) { return itsFunc->getArrayBool (aid.result().ids(id.rownr())); } TableExprGroupFuncSet& set = aid.result().funcSet(id.rownr()); return set.getFuncs()[itsFunc->seqnr()]->getArrayBool(); } MArray TableExprAggrNodeArray::getArrayInt (const TableExprId& id) { const TableExprIdAggr& aid = TableExprIdAggr::cast (id); if (itsFunc->isLazy()) { return itsFunc->getArrayInt (aid.result().ids(id.rownr())); } TableExprGroupFuncSet& set = aid.result().funcSet(id.rownr()); return set.getFuncs()[itsFunc->seqnr()]->getArrayInt(); } MArray TableExprAggrNodeArray::getArrayDouble (const TableExprId& id) { if (dataType() != NTDouble) { return TableExprNodeArray::getArrayDouble (id); } const TableExprIdAggr& aid = TableExprIdAggr::cast (id); if (itsFunc->isLazy()) { return itsFunc->getArrayDouble (aid.result().ids(id.rownr())); } TableExprGroupFuncSet& set = aid.result().funcSet(id.rownr()); return set.getFuncs()[itsFunc->seqnr()]->getArrayDouble(); } MArray TableExprAggrNodeArray::getArrayDComplex (const TableExprId& id) { if (dataType() != NTComplex) { return TableExprNodeArray::getArrayDComplex (id); } const TableExprIdAggr& aid = TableExprIdAggr::cast (id); if (itsFunc->isLazy()) { return itsFunc->getArrayDComplex (aid.result().ids(id.rownr())); } TableExprGroupFuncSet& set = aid.result().funcSet(id.rownr()); return set.getFuncs()[itsFunc->seqnr()]->getArrayDComplex(); } MArray TableExprAggrNodeArray::getArrayString (const TableExprId& id) { const TableExprIdAggr& aid = TableExprIdAggr::cast (id); if (itsFunc->isLazy()) { return itsFunc->getArrayString (aid.result().ids(id.rownr())); } TableExprGroupFuncSet& set = aid.result().funcSet(id.rownr()); return set.getFuncs()[itsFunc->seqnr()]->getArrayString(); } MArray TableExprAggrNodeArray::getArrayDate (const TableExprId& id) { const TableExprIdAggr& aid = TableExprIdAggr::cast (id); if (itsFunc->isLazy()) { return itsFunc->getArrayDate (aid.result().ids(id.rownr())); } TableExprGroupFuncSet& set = aid.result().funcSet(id.rownr()); return set.getFuncs()[itsFunc->seqnr()]->getArrayDate(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/ExprAggrNodeArray.h000066400000000000000000000074171476623553700211050ustar00rootroot00000000000000//# ExprAggrNodeArray.h: TaQL node representing an array aggregate function //# Copyright (C) 2013 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_EXPRAGGRNODEARRAY_H #define TABLES_EXPRAGGRNODEARRAY_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations. class TableExprGroupFuncBase; class TableExprGroupFuncSet; // // TaQL node representing an array aggregate function // // // // // // This class is similar to TableExprAggrNode, but its result is an array // instead of a scalar value. // There are few aggregate functions resulting in an array. An example // is gaggr, which aggregates the non-empty arrays in a group // into a single array. Other functions (like medians, runningmean, etc.) // can be applied to its result making it quite versatile. // // Most array aggregate functions are lazy to avoid using too much memory. // class TableExprAggrNodeArray: public TableExprFuncNodeArray { public: // Constructor. TableExprAggrNodeArray (TableExprFuncNode::FunctionType, NodeDataType, ValueType, const TableExprNodeSet& source, const vector& nodes, const Block& dtypeOper, const TaQLStyle& style); // This node does aggregation. virtual Bool isAggregate() const; // Get the operand node. TENShPtr operand() { return (operands().empty() ? TENShPtr() : operands()[0]); } // Create the correct aggregate function object. virtual std::shared_ptr makeGroupAggrFunc(); // Is the array aggregate function lazy? virtual Bool isLazyAggregate() const; // Functions to get the result of an aggregate function. // virtual MArray getArrayBool (const TableExprId& id); virtual MArray getArrayInt (const TableExprId& id); virtual MArray getArrayDouble (const TableExprId& id); virtual MArray getArrayDComplex (const TableExprId& id); virtual MArray getArrayString (const TableExprId& id); virtual MArray getArrayDate (const TableExprId& id); // private: // Create the correct aggregate function object. std::shared_ptr doMakeGroupAggrFunc(); //# Data members. std::shared_ptr itsFunc; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/ExprConeNode.cc000066400000000000000000000536651476623553700202560ustar00rootroot00000000000000//# ExprConeNode.cc: Class representing a cone search in table select expression //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableExprConeNode::TableExprConeNode (FunctionType ftype, NodeDataType dtype, const TableExprNodeSet& source, const vector& nodes, const Block& dtypeOper, uInt origin) : TableExprFuncNode (ftype, dtype, VTScalar, source, nodes, dtypeOper), origin_p (origin) {} TableExprConeNode::~TableExprConeNode() {} Bool TableExprConeNode::getBool (const TableExprId& id) { switch (funcType()) { case TableExprFuncNode::anyconeFUNC: { Array srcArr = operands()[0]->getArrayDouble(id).array(); if (srcArr.nelements() != 2) { throw TableInvExpr("First ANYCONE argument must have multiple of 2 values"); } Array coneArr = operands()[1]->getArrayDouble(id).array(); if (coneArr.nelements() % 3 != 0) { throw TableInvExpr("Second ANYCONE argument " "must have multiple of 3 values"); } Bool deleteSrc, deleteCone; const double* src = srcArr.getStorage (deleteSrc); const double* cone = coneArr.getStorage (deleteCone); const double ra = src[0]; const double dec = src[1]; Bool res = False; for (size_t i=0; i srcArr = operands_p[0]->getArrayDouble(id).array(); if (srcArr.nelements() != 2) { throw TableInvExpr("First CONES argument must have multiple of 2 values"); } Array coneArr = operands_p[1]->getArrayDouble(id).array(); if (coneArr.nelements() != 3) { throw TableInvExpr("Second CONES argument " "must have multiple of 3 values"); } Bool deleteSrc, deleteCone; const double* src = srcArr.getStorage (deleteSrc); const double* cone = coneArr.getStorage (deleteCone); const double ra = src[0]; const double dec = src[1]; const double raCone = cone[0]; const double decCone = cone[1]; const double radius = cone[2]; Bool res = (cos(radius) <= (sin(decCone) * sin(dec) + cos(decCone) * cos(dec) * cos(raCone - ra))); srcArr.freeStorage (src, deleteSrc); coneArr.freeStorage (cone, deleteCone); return res; } case TableExprFuncNode::anycone3FUNC: { Array srcArr = operands()[0]->getArrayDouble(id).array(); if (srcArr.nelements() != 2) { throw TableInvExpr("First ANYCONE argument must have multiple of 2 values"); } Array coneArr = operands()[1]->getArrayDouble(id).array(); if (coneArr.nelements() % 2 != 0) { throw TableInvExpr("Second ANYCONE3 argument " "must have multiple of 2 values"); } // Radius can be a single value or an array. int nrrad = 1; double radval; const double* rad = 0; Array radArr; if ( operands()[2]->valueType() == VTArray) { radArr = operands()[2]->getArrayDouble(id).array(); nrrad = radArr.nelements(); } else { radval = operands()[2]->getDouble(id); rad = &radval; } Bool deleteSrc, deleteCone, deleteRad; const double* src = srcArr.getStorage (deleteSrc); const double* cone = coneArr.getStorage (deleteCone); if (rad != &radval) { rad = radArr.getStorage (deleteRad); } const double ra = src[0]; const double dec = src[1]; Bool res = False; for (size_t i=0; i srcArr = operands()[0]->getArrayDouble(id).array(); if (srcArr.nelements() != 2) { throw TableInvExpr("First CONES argument " "must have multiple of 2 values"); } Array coneArr = operands()[1]->getArrayDouble(id).array(); if (coneArr.nelements() % 2 != 0) { throw TableInvExpr("Second CONES3 argument " "must have multiple of 2 values"); } Bool deleteSrc, deleteCone; const double* src = srcArr.getStorage (deleteSrc); const double* cone = coneArr.getStorage (deleteCone); const double ra = src[0]; const double dec = src[1]; const double raCone = cone[0]; const double decCone = cone[1]; const double radius = operands()[2]->getDouble(id); Bool res = (cos(radius) <= (sin(decCone) * sin(dec) + cos(decCone) * cos(dec) * cos(raCone - ra))); srcArr.freeStorage (src, deleteSrc); coneArr.freeStorage (cone, deleteCone); return res; } default: throw (TableInvExpr ("TableExprConeNode::getBool, " "unknown function")); } return True; } Int64 TableExprConeNode::getInt (const TableExprId& id) { switch (funcType()) { case TableExprFuncNode::findconeFUNC: { Array srcArr = operands()[0]->getArrayDouble(id).array(); if (srcArr.nelements() != 2) { throw TableInvExpr("First FINDCONE argument " "must have multiple of 2 values"); } Array coneArr = operands()[1]->getArrayDouble(id).array(); if (coneArr.nelements() % 3 != 0) { throw TableInvExpr("Second FINDCONE argument " "must have multiple of 3 values"); } Bool deleteSrc, deleteCone; const double* src = srcArr.getStorage (deleteSrc); const double* cone = coneArr.getStorage (deleteCone); const double ra = src[0]; const double dec = src[1]; Int64 res = -1; for (size_t i=0; i srcArr = operands()[0]->getArrayDouble(id).array(); if (srcArr.nelements() != 2) { throw TableInvExpr("First FINDCONE argument " "must have multiple 2 values"); } Array coneArr = operands()[1]->getArrayDouble(id).array(); if (coneArr.nelements() % 2 != 0) { throw TableInvExpr("Second FINDCONE3 argument " "must have multiple of 2 values"); } // Radius can be a single value or an array. int nrrad = 1; double radval; const double* rad = 0; Array radArr; if ( operands()[2]->valueType() == VTArray) { radArr = operands()[2]->getArrayDouble(id).array(); nrrad = radArr.nelements(); } else { radval = operands()[2]->getDouble(id); rad = &radval; } Bool deleteSrc, deleteCone, deleteRad; const double* src = srcArr.getStorage (deleteSrc); const double* cone = coneArr.getStorage (deleteCone); if (rad != &radval) { rad = radArr.getStorage (deleteRad); } const double ra = src[0]; const double dec = src[1]; Int64 res = -1; for (size_t i=0; i= 0) break; } srcArr.freeStorage (src, deleteSrc); coneArr.freeStorage (cone, deleteCone); if (rad != &radval) { radArr.freeStorage (rad, deleteRad); } return res; } default: throw (TableInvExpr ("TableExprConeNode::getInt, " "unknown function")); } return 0; } TableExprNodeRep::NodeDataType TableExprConeNode::checkOperands (Block& dtypeOper, ValueType& resVT, Block&, FunctionType fType, const vector& nodes) { int nrarg = 3; switch (fType) { // The 2 argument cone functions accept arrays only. // The result is a Bool scalar or array. case TableExprFuncNode::conesFUNC: case TableExprFuncNode::anyconeFUNC: case TableExprFuncNode::findconeFUNC: nrarg = 2; // fall through // The 3 argument cone functions accept a scalar or array as the 3rd argument. // The result is a Bool scalar or array. case TableExprFuncNode::cones3FUNC: case TableExprFuncNode::anycone3FUNC: case TableExprFuncNode::findcone3FUNC: { checkNumOfArg (nrarg, nrarg, nodes); for (Int i=0; i<2; i++) { if (nodes[i]->valueType() != VTArray) { throw TableInvExpr ("First 2 arguments of CONE functions must be double arrays"); } } // Result is a scalar or array. resVT = VTScalar; // Check the number of elements in the position node. Int nvalPos = findNelem (nodes[0]); Int nvalCone = findNelem (nodes[1]); // findcone returns an index value as integer. // This is a scalar if there is one source. if (fType == findconeFUNC || fType == findcone3FUNC) { if (nvalPos != 2) { resVT = VTArray; } return checkDT (dtypeOper, NTReal, NTInt, nodes); } // cones returns an array if there is more than one cone or radius. if (fType == conesFUNC) { if (nvalCone != 3) { resVT = VTArray; } } else if (fType == cones3FUNC) { if (nvalCone != 2 || nodes[2]->valueType() != VTScalar) { resVT = VTArray; } } return checkDT (dtypeOper, NTReal, NTBool, nodes); } default: throw (TableInvExpr ("TableExprConeNode::checkOperands, " "function not contained in switch statement")); } } Int TableExprConeNode::findNelem (const TENShPtr& node) { Int64 nelem = -1; if (node->valueType() == VTSet) { const TableExprNodeSet* set = dynamic_cast(node.get()); AlwaysAssert (set, AipsError); TENShPtr arr = set->setOrArray(); if (arr->valueType() != VTArray) { throw TableInvExpr ("CONES argument is a non-array set"); } nelem = arr->shape().product(); } else { nelem = node->shape().product(); } return nelem; } TableExprConeNodeArray::TableExprConeNodeArray (TableExprFuncNode::FunctionType ftype, NodeDataType dtype, const TableExprNodeSet& source, const vector& nodes, const Block& dtypeOper, uInt origin) : TableExprFuncNodeArray (ftype, dtype, VTArray, source, nodes, dtypeOper, TaQLStyle()), origin_p (origin) { ndim_p = -1; } TableExprConeNodeArray::~TableExprConeNodeArray() {} MArray TableExprConeNodeArray::getArrayBool (const TableExprId& id) { switch (funcType()) { case TableExprFuncNode::conesFUNC: { Array srcArr = operands()[0]->getArrayDouble(id).array(); if (srcArr.nelements() % 2 != 0) { throw TableInvExpr("First CONES argument " "must have multiple of 2 values"); } Array coneArr = operands()[1]->getArrayDouble(id).array(); if (coneArr.nelements() % 3 != 0) { throw TableInvExpr("Second CONES argument " "must have multiple of 3 values"); } // The result shape is a matrix (#cones, #sources). Int nsrc = srcArr.nelements() / 2; Int ncone = coneArr.nelements() / 3; Array resArr(IPosition(2,ncone,nsrc)); Bool deleteSrc, deleteCone; const double* src = srcArr.getStorage (deleteSrc); const double* cone = coneArr.getStorage (deleteCone); Bool* res = resArr.data(); for (size_t j=0; j(resArr); } case TableExprFuncNode::cones3FUNC: { Array srcArr = operands()[0]->getArrayDouble(id).array(); if (srcArr.nelements() % 2 != 0) { throw TableInvExpr("First CONES3 argument " "must have multiple of 2 values"); } Array coneArr = operands()[1]->getArrayDouble(id).array(); if (coneArr.nelements() % 2 != 0) { throw TableInvExpr("Second CONES3 argument " "must have multiple of 2 values"); } // Radius can be a single value or an array. int nrrad = 1; double radval; const double* rad = 0; Array radArr; if ( operands()[2]->valueType() == VTArray) { radArr = operands()[2]->getArrayDouble(id).array(); nrrad = radArr.nelements(); } else { radval = operands()[2]->getDouble(id); rad = &radval; } // The result shape is a cube (#radii, #cones, #sources). Int nsrc = srcArr.nelements() / 2; Int ncone = coneArr.nelements() / 2; Bool deleteSrc, deleteCone, deleteRad; const double* src = srcArr.getStorage (deleteSrc); const double* cone = coneArr.getStorage (deleteCone); if (rad != &radval) { rad = radArr.getStorage (deleteRad); } Array resArr(IPosition(3,nrrad,ncone,nsrc)); Bool* res = resArr.data(); for (size_t j=0; j(resArr); } default: throw (TableInvExpr ("TableExprConeNodeArray::getArrayBool, " "unknown function")); } } MArray TableExprConeNodeArray::getArrayInt (const TableExprId& id) { switch (funcType()) { case TableExprFuncNode::findconeFUNC: { Array srcArr = operands()[0]->getArrayDouble(id).array(); if (srcArr.nelements() % 2 != 0) { throw TableInvExpr("First FINDCONE argument " "must have multiple of 2 values"); } Array coneArr = operands()[1]->getArrayDouble(id).array(); if (coneArr.nelements() % 3 != 0) { throw TableInvExpr("Second FINDCONE argument " "must have multiple of 3 values"); } // The result shape is the source array shape. IPosition shpc = srcArr.shape(); IPosition shp; if (shpc.nelements() > 1 && shpc[0] == 2) { shp = shpc.getLast (shpc.nelements() - 1); } else { shp = shpc; shp[0] = shp[0] / 2; } Array resArr(shp); Bool deleteSrc, deleteCone; const double* src = srcArr.getStorage (deleteSrc); const double* cone = coneArr.getStorage (deleteCone); Int64* res = resArr.data(); for (size_t j=0; j(resArr); } case TableExprFuncNode::findcone3FUNC: { Array srcArr = operands()[0]->getArrayDouble(id).array(); if (srcArr.nelements() % 2 != 0) { throw TableInvExpr("First FINDCONE argument " "must have multiple of 2 values"); } Array coneArr = operands()[1]->getArrayDouble(id).array(); if (coneArr.nelements() % 2 != 0) { throw TableInvExpr("Second FINDCONE3 argument " "must have multiple of 2 values"); } // Radius can be a single value or an array. int nrrad = 1; Array radArr; if ( operands()[2]->valueType() == VTArray) { radArr = operands()[2]->getArrayDouble(id).array(); nrrad = radArr.nelements(); } else { radArr.resize(IPosition(1,1)); radArr.data()[0] = operands()[2]->getDouble(id); nrrad = 1; } // The result shape is the source array shape. IPosition shpc = srcArr.shape(); IPosition shp; if (shpc.nelements() > 1 && shpc[0] == 2) { shp = shpc.getLast (shpc.nelements() - 1); } else { shp = shpc; shp[0] = shp[0] / 2; } Bool deleteSrc, deleteCone, deleteRad; const double* src = srcArr.getStorage (deleteSrc); const double* cone = coneArr.getStorage (deleteCone); const double* rad = radArr.getStorage (deleteRad); Array resArr(shp); Int64* res = resArr.data(); for (size_t j=0; j= 0) break; } res++; } srcArr.freeStorage (src, deleteSrc); coneArr.freeStorage (cone, deleteCone); radArr.freeStorage (rad, deleteRad); return MArray(resArr); } default: throw (TableInvExpr ("TableExprConeNodeArray::getArrayInt, " "unknown function")); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/ExprConeNode.h000066400000000000000000000104261476623553700201040ustar00rootroot00000000000000//# ExprConeNode.h: Class representing a cone search in table select expression //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_EXPRCONENODE_H #define TABLES_EXPRCONENODE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Class representing a cone search in table select expression // // // // // //# Classes you should understand before using this one. //
      • TableExprFuncNode // // // The class represents a cone search. // It is a specialization of the TableExprFuncNode class. // Currently the implementation is straightforward, but in the future // it can do smarter things. // For instance: //
          //
        • If the cone positions and radii are constant, one can use // an integer zone number (e.g. floor(dec)) to avoid the much // more expensive sine/cosine calculations. Each cone will get a // minzone and maxzone value (derived from cone position and radius). //
        • Multiple cones can be ordered on minzone and maxzone. //
        //
        class TableExprConeNode : public TableExprFuncNode { public: // Constructor TableExprConeNode (FunctionType, NodeDataType, const TableExprNodeSet& source, const vector& nodes, const Block& dtypeOper, uInt origin); // Destructor ~TableExprConeNode(); // 'get' Functions to get the desired result of a function. // Bool getBool (const TableExprId& id); Int64 getInt (const TableExprId& id); // // Check the data and value types of the operands. // It sets the exptected data and value types of the operands. // Set the value type of the function result and returns // the data type of the function result. static NodeDataType checkOperands (Block& dtypeOper, ValueType& resVT, Block& vtypeOper, FunctionType, const std::vector&); private: // Find the number of elements in an argument. // It returns -1 if unknown. static Int findNelem (const TENShPtr& node); uInt origin_p; }; class TableExprConeNodeArray : public TableExprFuncNodeArray { public: // Constructor TableExprConeNodeArray (TableExprFuncNode::FunctionType, NodeDataType, const TableExprNodeSet& source, const vector& nodes, const Block& dtypeOper, uInt origin); // Destructor ~TableExprConeNodeArray(); // 'get' Functions to get the desired result of a function. // MArray getArrayBool (const TableExprId& id); MArray getArrayInt (const TableExprId& id); // private: uInt origin_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/ExprDerNode.cc000066400000000000000000000265651476623553700201030ustar00rootroot00000000000000//# ExprDerNode.cc: Nodes representing scalar operators in table select expression tree //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Implement the constants for each data type. TableExprNodeConstBool::TableExprNodeConstBool (const Bool& val) : TableExprNodeBinary (NTBool, VTScalar, OtLiteral, Constant), value_p (val) {} Bool TableExprNodeConstBool::getBool (const TableExprId&) { return value_p; } TableExprNodeConstInt::TableExprNodeConstInt (const Int64& val) : TableExprNodeBinary (NTInt, VTScalar, OtLiteral, Constant), value_p (val) {} Int64 TableExprNodeConstInt::getInt (const TableExprId&) { return value_p; } Double TableExprNodeConstInt::getDouble (const TableExprId&) { return value_p; } DComplex TableExprNodeConstInt::getDComplex (const TableExprId&) { return double(value_p); } TableExprNodeConstDouble::TableExprNodeConstDouble (const Double& val) : TableExprNodeBinary (NTDouble, VTScalar, OtLiteral, Constant), value_p (val) {} Double TableExprNodeConstDouble::getDouble (const TableExprId&) { return value_p; } DComplex TableExprNodeConstDouble::getDComplex (const TableExprId&) { return value_p; } TableExprNodeConstDComplex::TableExprNodeConstDComplex (const DComplex& val) : TableExprNodeBinary (NTComplex, VTScalar, OtLiteral, Constant), value_p (val) {} DComplex TableExprNodeConstDComplex::getDComplex (const TableExprId&) { return value_p; } TableExprNodeConstString::TableExprNodeConstString (const String& val) : TableExprNodeBinary (NTString, VTScalar, OtLiteral, Constant), value_p (val) {} String TableExprNodeConstString::getString (const TableExprId&) { return value_p; } TableExprNodeConstRegex::TableExprNodeConstRegex (const TaqlRegex& val) : TableExprNodeBinary (NTRegex, VTScalar, OtLiteral, Constant), value_p (val) {} TaqlRegex TableExprNodeConstRegex::getRegex (const TableExprId&) { return value_p; } TableExprNodeConstDate::TableExprNodeConstDate (const MVTime& val) : TableExprNodeBinary (NTDate, VTScalar, OtLiteral, Constant), value_p (val) {} Double TableExprNodeConstDate::getDouble (const TableExprId&) { return value_p; } MVTime TableExprNodeConstDate::getDate (const TableExprId&) { return value_p; } // //
      • TableInvExpr // //# Create a table expression node for a column. //# First use a "dummy" data type and fill it in later. //# Similarly for the value type. TableExprNodeColumn::TableExprNodeColumn (const TableExprInfo& tableInfo, const String& name) : TableExprNodeBinary (NTNumeric, VTScalar, OtColumn, Variable), tableInfo_p (tableInfo), tabCol_p (tableInfo.table(), name), applySelection_p (True) { //# Check if the column is a scalar. if (! tabCol_p.columnDesc().isScalar()) { throw (TableInvExpr (name, " is no scalar column")); } //# Fill in the real data type and the base table pointer. switch (tabCol_p.columnDesc().dataType()) { case TpBool: dtype_p = NTBool; break; case TpString: dtype_p = NTString; break; case TpComplex: case TpDComplex: dtype_p = NTComplex; break; case TpFloat: case TpDouble: dtype_p = NTDouble; break; default: dtype_p = NTInt; } setUnit (getColumnUnit(tabCol_p)); } TableExprInfo TableExprNodeColumn::getTableInfo() const { return tableInfo_p; } Unit TableExprNodeColumn::getColumnUnit (const TableColumn& tabcol) { Unit unit; //# Get the unit (if defined). const TableRecord& keyset = tabcol.keywordSet(); if (keyset.isDefined ("QuantumUnits")) { const Array& units = keyset.asArrayString("QuantumUnits"); if (units.size() > 0) { unit = *(units.data()); } } else if (keyset.isDefined ("UNIT")) { unit = keyset.asString("UNIT"); } return unit; } void TableExprNodeColumn::disableApplySelection() { applySelection_p = False; } void TableExprNodeColumn::applySelection (const Vector& rownrs) { if (applySelection_p) { // Attach the column to the selection of the table. // Get column name before doing selection!!!! String name = tabCol_p.columnDesc().name(); tableInfo_p.apply (rownrs); tabCol_p = TableColumn(tableInfo_p.table(), name); // Reset switch, because the column object can be used multiple times. // when a select expression is used as e.g. sort key. applySelection_p = False; } } //# Return the TableColumn. const TableColumn& TableExprNodeColumn::getColumn() const { return tabCol_p; } Bool TableExprNodeColumn::getBool (const TableExprId& id) { Bool val; tabCol_p.getScalar (id.rownr(), val); return val; } Int64 TableExprNodeColumn::getInt (const TableExprId& id) { Int64 val; tabCol_p.getScalar (id.rownr(), val); return val; } Double TableExprNodeColumn::getDouble (const TableExprId& id) { Double val; tabCol_p.getScalar (id.rownr(), val); return val; } DComplex TableExprNodeColumn::getDComplex (const TableExprId& id) { DComplex val; tabCol_p.getScalar (id.rownr(), val); return val; } String TableExprNodeColumn::getString (const TableExprId& id) { String val; tabCol_p.getScalar (id.rownr(), val); return val; } Bool TableExprNodeColumn::getColumnDataType (DataType& dt) const { dt = tabCol_p.columnDesc().dataType(); return True; } Array TableExprNodeColumn::getColumnBool (const Vector& rownrs) { ScalarColumn col (tabCol_p); return col.getColumnCells (rownrs); } Array TableExprNodeColumn::getColumnuChar (const Vector& rownrs) { ScalarColumn col (tabCol_p); return col.getColumnCells (rownrs); } Array TableExprNodeColumn::getColumnShort (const Vector& rownrs) { ScalarColumn col (tabCol_p); return col.getColumnCells (rownrs); } Array TableExprNodeColumn::getColumnuShort (const Vector& rownrs) { ScalarColumn col (tabCol_p); return col.getColumnCells (rownrs); } Array TableExprNodeColumn::getColumnInt (const Vector& rownrs) { ScalarColumn col (tabCol_p); return col.getColumnCells (rownrs); } Array TableExprNodeColumn::getColumnuInt (const Vector& rownrs) { ScalarColumn col (tabCol_p); return col.getColumnCells (rownrs); } Array TableExprNodeColumn::getColumnInt64 (const Vector& rownrs) { ScalarColumn col (tabCol_p); return col.getColumnCells (rownrs); } Array TableExprNodeColumn::getColumnFloat (const Vector& rownrs) { ScalarColumn col (tabCol_p); return col.getColumnCells (rownrs); } Array TableExprNodeColumn::getColumnDouble (const Vector& rownrs) { ScalarColumn col (tabCol_p); return col.getColumnCells (rownrs); } Array TableExprNodeColumn::getColumnComplex (const Vector& rownrs) { ScalarColumn col (tabCol_p); return col.getColumnCells (rownrs); } Array TableExprNodeColumn::getColumnDComplex (const Vector& rownrs) { ScalarColumn col (tabCol_p); return col.getColumnCells (rownrs); } Array TableExprNodeColumn::getColumnString (const Vector& rownrs) { ScalarColumn col (tabCol_p); return col.getColumnCells (rownrs); } TableExprNodeRownr::TableExprNodeRownr (const TableExprInfo& tableInfo, uInt origin) : TableExprNodeBinary (NTInt, VTScalar, OtRownr, Variable), tableInfo_p (tableInfo), origin_p (origin) {} TableExprInfo TableExprNodeRownr::getTableInfo() const { return tableInfo_p; } Int64 TableExprNodeRownr::getInt (const TableExprId& id) { AlwaysAssert (id.byRow(), AipsError); return id.rownr() + origin_p; } TableExprNodeRowid::TableExprNodeRowid (const TableExprInfo& tableInfo) : TableExprNodeBinary (NTInt, VTScalar, OtRownr, Variable), tableInfo_p (tableInfo), rownrs_p (tableInfo.table().nrow()) { indgen (rownrs_p); } TableExprInfo TableExprNodeRowid::getTableInfo() const { return tableInfo_p; } void TableExprNodeRowid::applySelection (const Vector& rownrs) { // Append rows for an insert. if (rownrs.size() == 1 && rownrs[0] >= rownrs_p.size()) { rownr_t sz = rownrs_p.size(); rownrs_p.resize (rownrs[0], True); for (rownr_t i=sz; i newRows(rownrs.size()); for (rownr_t i=0; i #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableColumn; class Table; //# This file defines classes derived from TableExprNode representing //# the data type and operator in a table expression. //# //# Data types Bool, Int64, Double, DComplex and String are used. //# Char, uChar, Short, uShort, Int, and uInt are converted to Int64, //# Float to Double, and Complex to DComplex. //# Binary operators +, -, *, /, ==, >=, >, <, <= and != are recognized. //# Also &&, ||, parentheses and unary +, - and ! are recognized. // // Constant Bool in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents a constant in a table select expression tree. // This is also used to hold the value of a table keyword, which is // constant over the entire table. // class TableExprNodeConstBool : public TableExprNodeBinary { public: TableExprNodeConstBool (const Bool& value); ~TableExprNodeConstBool() override = default; Bool getBool (const TableExprId& id) override; private: Bool value_p; }; // // Constant Int64 in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents a constant in a table select expression tree. // This is also used to hold the value of a table keyword, which is // constant over the entire table. // class TableExprNodeConstInt : public TableExprNodeBinary { public: TableExprNodeConstInt (const Int64& value); ~TableExprNodeConstInt() override = default; Int64 getInt (const TableExprId& id) override; Double getDouble (const TableExprId& id) override; DComplex getDComplex (const TableExprId& id) override; private: Int64 value_p; }; // // Constant Double in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents a constant in a table select expression tree. // This is also used to hold the value of a table keyword, which is // constant over the entire table. // class TableExprNodeConstDouble : public TableExprNodeBinary { public: TableExprNodeConstDouble (const Double& value); ~TableExprNodeConstDouble() override = default; Double getDouble (const TableExprId& id) override; DComplex getDComplex (const TableExprId& id) override; private: Double value_p; }; // // Constant DComplex in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents a constant in a table select expression tree. // This is also used to hold the value of a table keyword, which is // constant over the entire table. // class TableExprNodeConstDComplex : public TableExprNodeBinary { public: TableExprNodeConstDComplex (const DComplex& value); ~TableExprNodeConstDComplex() override = default; DComplex getDComplex (const TableExprId& id) override; private: DComplex value_p; }; // // Constant String in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents a constant in a table select expression tree. // This is also used to hold the value of a table keyword, which is // constant over the entire table. // class TableExprNodeConstString : public TableExprNodeBinary { public: TableExprNodeConstString (const String& value); ~TableExprNodeConstString() override = default; String getString (const TableExprId& id) override; private: String value_p; }; // // Constant Regex or StringDistance in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents a constant in a table select expression tree. // This is also used to hold the value of a table keyword, which is // constant over the entire table. // class TableExprNodeConstRegex : public TableExprNodeBinary { public: TableExprNodeConstRegex (const TaqlRegex& value); ~TableExprNodeConstRegex() override = default; TaqlRegex getRegex (const TableExprId& id) override; private: TaqlRegex value_p; StringDistance dist_p; }; // // Constant Date in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents a constant in a table select expression tree. // This is also used to hold the value of a table keyword, which is // constant over the entire table. // class TableExprNodeConstDate : public TableExprNodeBinary { public: TableExprNodeConstDate (const MVTime& value); ~TableExprNodeConstDate() override = default; Double getDouble(const TableExprId& id) override; MVTime getDate (const TableExprId& id) override; private: MVTime value_p; }; // // Scalar column in table select expression tree // // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents a scalar column in a table select expression tree. // When the select expression gets evaluated, the value of the // given row in the column is used. // class TableExprNodeColumn : public TableExprNodeBinary { public: TableExprNodeColumn (const TableExprInfo&, const String& columnName); ~TableExprNodeColumn() override = default; // Get the table info for this column. TableExprInfo getTableInfo() const override; // Do not apply the selection. void disableApplySelection() override; // Re-create the column object for a selection of rows. void applySelection (const Vector& rownrs) override; // Get the data type of this scalar column. Bool getColumnDataType (DataType&) const override; // Get the data for the given id. Bool getBool (const TableExprId& id) override; Int64 getInt (const TableExprId& id) override; Double getDouble (const TableExprId& id) override; DComplex getDComplex (const TableExprId& id) override; String getString (const TableExprId& id) override; const TableColumn& getColumn() const; // Get the data for the given rows. Array getColumnBool (const Vector& rownrs) override; Array getColumnuChar (const Vector& rownrs) override; Array getColumnShort (const Vector& rownrs) override; Array getColumnuShort (const Vector& rownrs) override; Array getColumnInt (const Vector& rownrs) override; Array getColumnuInt (const Vector& rownrs) override; Array getColumnInt64 (const Vector& rownrs) override; Array getColumnFloat (const Vector& rownrs) override; Array getColumnDouble (const Vector& rownrs) override; Array getColumnComplex (const Vector& rownrs) override; Array getColumnDComplex (const Vector& rownrs) override; Array getColumnString (const Vector& rownrs) override; // Get the column unit (can be empty). static Unit getColumnUnit (const TableColumn&); protected: TableExprInfo tableInfo_p; TableColumn tabCol_p; Bool applySelection_p; }; // // Rownumber in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents the rownumber() function in a table // select expression tree. // The origin is stored to indicate whether the first rownumber // should be zero (in C++) or another value (1 in TaQL). // class TableExprNodeRownr : public TableExprNodeBinary { public: TableExprNodeRownr (const TableExprInfo&, uInt origin); ~TableExprNodeRownr() override = default; TableExprInfo getTableInfo() const override; Int64 getInt (const TableExprId& id) override; private: TableExprInfo tableInfo_p; uInt origin_p; }; // // Rowid in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents the rowid() function in a table // select expression tree. // It is meant to get the original row number in a GIVING clause, // but, of course, it can also be used in the SELECT clause. // The row number returned is 0-based. // class TableExprNodeRowid : public TableExprNodeBinary { public: TableExprNodeRowid (const TableExprInfo&); ~TableExprNodeRowid() override = default; TableExprInfo getTableInfo() const override; void applySelection (const Vector& rownrs) override; Int64 getInt (const TableExprId& id) override; private: TableExprInfo tableInfo_p; Vector rownrs_p; }; // // Random number in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents the rand() function in a table // select expression tree. // class TableExprNodeRandom : public TableExprNodeBinary { public: TableExprNodeRandom (const TableExprInfo&); ~TableExprNodeRandom() override = default; TableExprInfo getTableInfo() const override; Double getDouble (const TableExprId& id) override; private: TableExprInfo tableInfo_p; MLCG generator_p; Uniform random_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/ExprDerNodeArray.cc000066400000000000000000000253621476623553700210740ustar00rootroot00000000000000//# ExprDerArrayNode.cc: Nodes representing constant arrays in table select expression tree //# Copyright (C) 1997,1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableExprNodeArrayConstBool::TableExprNodeArrayConstBool (const Array& val) : TableExprNodeArray (NTBool, OtLiteral, val.shape()), value_p (val) {} TableExprNodeArrayConstBool::TableExprNodeArrayConstBool (const MArray& val) : TableExprNodeArray (NTBool, OtLiteral, val.shape()), value_p (val) {} TableExprNodeArrayConstBool::~TableExprNodeArrayConstBool() {} MArray TableExprNodeArrayConstBool::getArrayBool (const TableExprId&) { return value_p; } TableExprNodeArrayConstInt::TableExprNodeArrayConstInt (const Array& val) : TableExprNodeArray (NTInt, OtLiteral, val.shape()) { value_p.fill (val); } TableExprNodeArrayConstInt::TableExprNodeArrayConstInt (const Array& val) : TableExprNodeArray (NTInt, OtLiteral, val.shape()), value_p (val) {} TableExprNodeArrayConstInt::TableExprNodeArrayConstInt (const Array& val) : TableExprNodeArray (NTInt, OtLiteral, val.shape()) { value_p.fill (val); } TableExprNodeArrayConstInt::TableExprNodeArrayConstInt (const Array& val) : TableExprNodeArray (NTInt, OtLiteral, val.shape()) { value_p.fill (val); } TableExprNodeArrayConstInt::TableExprNodeArrayConstInt (const Array& val) : TableExprNodeArray (NTInt, OtLiteral, val.shape()) { value_p.fill (val); } TableExprNodeArrayConstInt::TableExprNodeArrayConstInt (const Array& val) : TableExprNodeArray (NTInt, OtLiteral, val.shape()) { value_p.fill (val); } TableExprNodeArrayConstInt::TableExprNodeArrayConstInt (const Array& val) : TableExprNodeArray (NTInt, OtLiteral, val.shape()) { value_p.fill (val); } TableExprNodeArrayConstInt::TableExprNodeArrayConstInt (const MArray& val) : TableExprNodeArray (NTInt, OtLiteral, val.shape()) { value_p.fill (val); } TableExprNodeArrayConstInt::TableExprNodeArrayConstInt (const MArray& val) : TableExprNodeArray (NTInt, OtLiteral, val.shape()), value_p (val) {} TableExprNodeArrayConstInt::TableExprNodeArrayConstInt (const MArray& val) : TableExprNodeArray (NTInt, OtLiteral, val.shape()) { value_p.fill (val); } TableExprNodeArrayConstInt::TableExprNodeArrayConstInt (const MArray& val) : TableExprNodeArray (NTInt, OtLiteral, val.shape()) { value_p.fill (val); } TableExprNodeArrayConstInt::TableExprNodeArrayConstInt (const MArray& val) : TableExprNodeArray (NTInt, OtLiteral, val.shape()) { value_p.fill (val); } TableExprNodeArrayConstInt::TableExprNodeArrayConstInt (const MArray& val) : TableExprNodeArray (NTInt, OtLiteral, val.shape()) { value_p.fill (val); } TableExprNodeArrayConstInt::TableExprNodeArrayConstInt (const MArray& val) : TableExprNodeArray (NTInt, OtLiteral, val.shape()) { value_p.fill (val); } TableExprNodeArrayConstInt::~TableExprNodeArrayConstInt() {} MArray TableExprNodeArrayConstInt::getArrayInt (const TableExprId&) { return value_p; } MArray TableExprNodeArrayConstInt::getArrayDouble (const TableExprId&) { MArray arr; arr.fill (value_p); return arr; } MArray TableExprNodeArrayConstInt::getArrayDComplex (const TableExprId&) { MArray arr; arr.fill (value_p); return arr; } TableExprNodeArrayConstDouble::TableExprNodeArrayConstDouble (const Array& val) : TableExprNodeArray (NTDouble, OtLiteral, val.shape()), value_p (val) {} TableExprNodeArrayConstDouble::TableExprNodeArrayConstDouble (const Array& val) : TableExprNodeArray (NTDouble, OtLiteral, val.shape()) { value_p.fill (val); } TableExprNodeArrayConstDouble::TableExprNodeArrayConstDouble (const Array& val) : TableExprNodeArray (NTDouble, OtLiteral, val.shape()) { value_p.fill (val); } TableExprNodeArrayConstDouble::TableExprNodeArrayConstDouble (const MArray& val) : TableExprNodeArray (NTDouble, OtLiteral, val.shape()), value_p (val) {} TableExprNodeArrayConstDouble::TableExprNodeArrayConstDouble (const MArray& val) : TableExprNodeArray (NTDouble, OtLiteral, val.shape()) { value_p.fill (val); } TableExprNodeArrayConstDouble::TableExprNodeArrayConstDouble (const MArray& val) : TableExprNodeArray (NTDouble, OtLiteral, val.shape()) { value_p.fill (val); } TableExprNodeArrayConstDouble::~TableExprNodeArrayConstDouble() {} MArray TableExprNodeArrayConstDouble::getArrayDouble (const TableExprId&) { return value_p; } MArray TableExprNodeArrayConstDouble::getArrayDComplex (const TableExprId&) { MArray arr; arr.fill (value_p); return arr; } TableExprNodeArrayConstDComplex::TableExprNodeArrayConstDComplex (const Array& val) : TableExprNodeArray (NTComplex, OtLiteral, val.shape()), value_p (val) {} TableExprNodeArrayConstDComplex::TableExprNodeArrayConstDComplex (const Array& val) : TableExprNodeArray (NTComplex, OtLiteral, val.shape()) { value_p.fill (val); } TableExprNodeArrayConstDComplex::TableExprNodeArrayConstDComplex (const Array& val) : TableExprNodeArray (NTComplex, OtLiteral, val.shape()) { value_p.fill (val); } TableExprNodeArrayConstDComplex::TableExprNodeArrayConstDComplex (const Array& val) : TableExprNodeArray (NTComplex, OtLiteral, val.shape()) { value_p.fill (val); } TableExprNodeArrayConstDComplex::TableExprNodeArrayConstDComplex (const MArray& val) : TableExprNodeArray (NTComplex, OtLiteral, val.shape()), value_p (val) {} TableExprNodeArrayConstDComplex::TableExprNodeArrayConstDComplex (const MArray& val) : TableExprNodeArray (NTComplex, OtLiteral, val.shape()) { value_p.fill (val); } TableExprNodeArrayConstDComplex::TableExprNodeArrayConstDComplex (const MArray& val) : TableExprNodeArray (NTComplex, OtLiteral, val.shape()) { value_p.fill (val); } TableExprNodeArrayConstDComplex::TableExprNodeArrayConstDComplex (const MArray& val) : TableExprNodeArray (NTComplex, OtLiteral, val.shape()) { value_p.fill (val); } TableExprNodeArrayConstDComplex::~TableExprNodeArrayConstDComplex() {} MArray TableExprNodeArrayConstDComplex::getArrayDComplex (const TableExprId&) { return value_p; } TableExprNodeArrayConstString::TableExprNodeArrayConstString (const Array& val) : TableExprNodeArray (NTString, OtLiteral, val.shape()), value_p (val) {} TableExprNodeArrayConstString::TableExprNodeArrayConstString (const MArray& val) : TableExprNodeArray (NTString, OtLiteral, val.shape()), value_p (val) {} TableExprNodeArrayConstString::~TableExprNodeArrayConstString() {} MArray TableExprNodeArrayConstString::getArrayString (const TableExprId&) { return value_p; } TableExprNodeArrayConstDate::TableExprNodeArrayConstDate (const Array& val) : TableExprNodeArray (NTDate, OtLiteral, val.shape()), value_p (val) {} TableExprNodeArrayConstDate::TableExprNodeArrayConstDate (const MArray& val) : TableExprNodeArray (NTDate, OtLiteral, val.shape()), value_p (val) {} TableExprNodeArrayConstDate::~TableExprNodeArrayConstDate() {} MArray TableExprNodeArrayConstDate::getArrayDouble (const TableExprId&) { MArray arr; arr.fill (value_p); return arr; } MArray TableExprNodeArrayConstDate::getArrayDate (const TableExprId&) { return value_p; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/ExprDerNodeArray.h000066400000000000000000000203111476623553700207230ustar00rootroot00000000000000//# ExprDerArrayNode.h: Nodes representing constant arrays in table select expression tree //# Copyright (C) 1997,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_EXPRDERNODEARRAY_H #define TABLES_EXPRDERNODEARRAY_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // Bool Array constant in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeArray // // // This class represents a constant in a table select expression tree. // This is also used to hold the value of a table keyword, which is // constant over the entire table. // class TableExprNodeArrayConstBool : public TableExprNodeArray { public: TableExprNodeArrayConstBool (const Array& value); TableExprNodeArrayConstBool (const MArray& value); ~TableExprNodeArrayConstBool(); MArray getArrayBool (const TableExprId& id); private: MArray value_p; }; // // Int Array constant in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeArray // // // This class represents a constant in a table select expression tree. // This is also used to hold the value of a table keyword, which is // constant over the entire table. // class TableExprNodeArrayConstInt : public TableExprNodeArray { public: TableExprNodeArrayConstInt (const Array& value); TableExprNodeArrayConstInt (const Array& value); TableExprNodeArrayConstInt (const Array& value); TableExprNodeArrayConstInt (const Array& value); TableExprNodeArrayConstInt (const Array& value); TableExprNodeArrayConstInt (const Array& value); TableExprNodeArrayConstInt (const Array& value); TableExprNodeArrayConstInt (const MArray& value); TableExprNodeArrayConstInt (const MArray& value); TableExprNodeArrayConstInt (const MArray& value); TableExprNodeArrayConstInt (const MArray& value); TableExprNodeArrayConstInt (const MArray& value); TableExprNodeArrayConstInt (const MArray& value); TableExprNodeArrayConstInt (const MArray& value); ~TableExprNodeArrayConstInt(); MArray getArrayInt (const TableExprId& id); MArray getArrayDouble (const TableExprId& id); MArray getArrayDComplex (const TableExprId& id); private: MArray value_p; }; // // Double Array constant in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeArray // // // This class represents a constant in a table select expression tree. // This is also used to hold the value of a table keyword, which is // constant over the entire table. // class TableExprNodeArrayConstDouble : public TableExprNodeArray { public: TableExprNodeArrayConstDouble (const Array& value); TableExprNodeArrayConstDouble (const Array& value); TableExprNodeArrayConstDouble (const Array& value); TableExprNodeArrayConstDouble (const MArray& value); TableExprNodeArrayConstDouble (const MArray& value); TableExprNodeArrayConstDouble (const MArray& value); ~TableExprNodeArrayConstDouble(); MArray getArrayDouble (const TableExprId& id); MArray getArrayDComplex (const TableExprId& id); private: MArray value_p; }; // // DComplex Array constant in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeArray // // // This class represents a constant in a table select expression tree. // This is also used to hold the value of a table keyword, which is // constant over the entire table. // class TableExprNodeArrayConstDComplex : public TableExprNodeArray { public: TableExprNodeArrayConstDComplex (const Array& value); TableExprNodeArrayConstDComplex (const Array& value); TableExprNodeArrayConstDComplex (const Array& value); TableExprNodeArrayConstDComplex (const Array& value); TableExprNodeArrayConstDComplex (const MArray& value); TableExprNodeArrayConstDComplex (const MArray& value); TableExprNodeArrayConstDComplex (const MArray& value); TableExprNodeArrayConstDComplex (const MArray& value); ~TableExprNodeArrayConstDComplex(); MArray getArrayDComplex (const TableExprId& id); private: MArray value_p; }; // // String Array constant in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeArray // // // This class represents a constant in a table select expression tree. // This is also used to hold the value of a table keyword, which is // constant over the entire table. // class TableExprNodeArrayConstString : public TableExprNodeArray { public: TableExprNodeArrayConstString (const Array& value); TableExprNodeArrayConstString (const MArray& value); ~TableExprNodeArrayConstString(); MArray getArrayString (const TableExprId& id); private: MArray value_p; }; // // Date Array constant in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeArray // // // This class represents a constant in a table select expression tree. // This is also used to hold the value of a table keyword, which is // constant over the entire table. // class TableExprNodeArrayConstDate : public TableExprNodeArray { public: TableExprNodeArrayConstDate (const Array& value); TableExprNodeArrayConstDate (const MArray& value); ~TableExprNodeArrayConstDate(); MArray getArrayDouble(const TableExprId& id); MArray getArrayDate (const TableExprId& id); private: MArray value_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/ExprFuncNode.cc000066400000000000000000001775351476623553700202700ustar00rootroot00000000000000//# ExprFuncNode.cc: Class representing a function in table select expression //# Copyright (C) 1994,1995,1996,1997,1998,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableExprFuncNode::TableExprFuncNode (FunctionType ftype, NodeDataType dtype, ValueType vtype, const TableExprNodeSet& source, const vector& nodes, const Block& dtypeOper, const TableExprInfo& tabInfo) : TableExprNodeMulti (dtype, vtype, OtFunc, source), funcType_p (ftype), argDataType_p (dtype), scale_p (1), table_p (tabInfo.table()) { // Fill child nodes as needed. It also fills operands_p. fillChildNodes (nodes, dtypeOper); // Set the unit for some functions. fillUnits(); // Some functions on a variable can already give a constant result. tryToConst(); } TableExprFuncNode::~TableExprFuncNode() {} void TableExprFuncNode::fillUnits() { if (funcType_p == cFUNC) { setUnit ("m/s"); } if (operands_p.size() > 0) { const Unit& childUnit = operands_p[0]->unit(); switch (funcType_p) { case asinFUNC: case acosFUNC: case atanFUNC: case atan2FUNC: case timeFUNC: // These functions return radians (if operand is not complex). if (operands_p[0]->dataType() != NTComplex) { setUnit ("rad"); } break; case mjdFUNC: // These functions return days. setUnit ("d"); break; case mjdtodateFUNC: // These functions require days. if (! childUnit.empty()) { TableExprNodeUnit::adaptUnit (operands_p[0], "d"); } break; case argFUNC: setUnit ("rad"); break; case absFUNC: case realFUNC: case imagFUNC: case conjFUNC: case roundFUNC: case floorFUNC: case ceilFUNC: case arrsumFUNC: case arrsumsFUNC: case runsumFUNC: case boxsumFUNC: case arrminFUNC: case arrminsFUNC: case runminFUNC: case boxminFUNC: case arrmaxFUNC: case arrmaxsFUNC: case runmaxFUNC: case boxmaxFUNC: case arrmeanFUNC: case arrmeansFUNC: case runmeanFUNC: case boxmeanFUNC: case arrstddev0FUNC: case arrstddevs0FUNC: case runstddev0FUNC: case boxstddev0FUNC: case arrstddev1FUNC: case arrstddevs1FUNC: case runstddev1FUNC: case boxstddev1FUNC: case arravdevFUNC: case arravdevsFUNC: case runavdevFUNC: case boxavdevFUNC: case boxrmsFUNC: case arrrmsFUNC: case arrrmssFUNC: case runrmsFUNC: case arrmedianFUNC: case arrmediansFUNC: case runmedianFUNC: case boxmedianFUNC: case arrfractileFUNC: case arrfractilesFUNC: case runfractileFUNC: case boxfractileFUNC: case arrayFUNC: case transposeFUNC: case areverseFUNC: case resizeFUNC: case diagonalFUNC: case marrayFUNC: case arrdataFUNC: case negatemaskFUNC: case replmaskedFUNC: case replunmaskedFUNC: case arrflatFUNC: case gminFUNC: case gmaxFUNC: case gsumFUNC: case gmeanFUNC: case gstddev0FUNC: case gstddev1FUNC: case grmsFUNC: case gminsFUNC: case gmaxsFUNC: case gsumsFUNC: case gmeansFUNC: case gstddevs0FUNC: case gstddevs1FUNC: case grmssFUNC: case gaggrFUNC: case gmedianFUNC: case gfractileFUNC: // These functions return the same unit as their child. setUnit (childUnit); break; case normFUNC: case squareFUNC: case arrsumsqrFUNC: case arrsumsqrsFUNC: case runsumsqrFUNC: case boxsumsqrFUNC: case gsumsqrFUNC: case gsumsqrsFUNC: case arrvariance0FUNC: case arrvariances0FUNC: case runvariance0FUNC: case boxvariance0FUNC: case gvariance0FUNC: case gvariances0FUNC: case arrvariance1FUNC: case arrvariances1FUNC: case runvariance1FUNC: case boxvariance1FUNC: case gvariance1FUNC: case gvariances1FUNC: // These functions return the square of their child. if (! childUnit.empty()) { Quantity q(1., childUnit); setUnit ((q*q).getFullUnit()); } break; case cubeFUNC: if (! childUnit.empty()) { Quantity q(1., childUnit); setUnit ((q*q*q).getFullUnit()); } break; case sqrtFUNC: // These functions return the sqrt of their child. if (! childUnit.empty()) { Quantity q(1., childUnit); Quantity qs(sqrt(q)); // sqrt result is always in SI units, so scaling might be involved. scale_p = qs.getValue(); setUnit (qs.getFullUnit()); } break; case normangleFUNC: setUnit ("rad"); // fall through case sinFUNC: case cosFUNC: case tanFUNC: case hmsFUNC: case dmsFUNC: case hdmsFUNC: // These functions return no unit, but their child must be in radians. // Note that in case of DComplex argument there is no unit. if (! childUnit.empty()) { TableExprNodeUnit::adaptUnit (operands_p[0], "rad"); } break; case iifFUNC: setUnit (makeEqualUnits (operands_p, 1, operands_p.size())); break; case complexFUNC: case minFUNC: case maxFUNC: case fmodFUNC: setUnit (makeEqualUnits (operands_p, 0, operands_p.size())); break; case near2FUNC: case nearabs2FUNC: case near3FUNC: makeEqualUnits (operands_p, 0, 2); break; case nearabs3FUNC: makeEqualUnits (operands_p, 0, 3); break; case angdistFUNC: case angdistxFUNC: setUnit ("rad"); // fall through case conesFUNC: case cones3FUNC: case anyconeFUNC: case anycone3FUNC: case findconeFUNC: case findcone3FUNC: for (uInt i=0; i& nodes, uInt starg, uInt endarg) { // These functions have multiple children, which must have the same unit. // The first real unit is chosen as the result unit. const Unit* unit = &(nodes[starg]->unit()); for (uInt i=starg; iunit().empty()) { unit = &(nodes[i]->unit()); break; } } if (! unit->empty()) { for (uInt i=starg; i& nodes, const Block& dtypeOper) { // Copy block of children. // Determine if common argument type is Int, Double or Complex. // (this is used by some functions like near and norm). operands_p.resize (nodes.size()); argDataType_p = NTInt; for (uInt i=0; idataType() == NTDouble && argDataType_p != NTComplex) { argDataType_p = NTDouble; } else if (nodes[i]->dataType() == NTComplex) { argDataType_p = NTComplex; } } // Convert String to Date if needed for (uInt i=0; idataType() == NTString) { TableExprNode dNode = datetime (operands_p[i]); operands_p[i] = dNode.getRep(); } else if (nodes[i]->dataType() == NTDouble || nodes[i]->dataType() == NTInt) { TableExprNode dNode = mjdtodate (operands_p[i]); operands_p[i] = dNode.getRep(); } } } } void TableExprFuncNode::tryToConst() { switch (funcType_p) { case ndimFUNC: if (operands_p[0]->ndim() >= 0) { exprtype_p = Constant; } break; case nelemFUNC: case isdefFUNC: case isnullFUNC: if (operands_p[0]->ndim() == 0 || operands_p[0]->shape().size() > 0 ) { exprtype_p = Constant; } break; case iscolFUNC: case iskeyFUNC: exprtype_p = Constant; break; default: break; } } Bool TableExprFuncNode::getBool (const TableExprId& id) { switch (funcType_p) { case boolFUNC: if (operands_p[0]->dataType() == NTBool) { return operands_p[0]->getBool(id); } else if (operands_p[0]->dataType() == NTInt) { return (operands_p[0]->getInt(id) != 0); } else if (operands_p[0]->dataType() == NTDouble) { return (operands_p[0]->getDouble(id) != 0); } else if (operands_p[0]->dataType() == NTComplex) { return (operands_p[0]->getDComplex(id) != DComplex()); } else if (operands_p[0]->dataType() == NTDate) { return (operands_p[0]->getDouble(id) != 0); } return string2Bool (operands_p[0]->getString(id)); case arranyFUNC: if (operands_p[0]->valueType() == VTArray) { return anyTrue (operands_p[0]->getArrayBool(id)); } return operands_p[0]->getBool (id); case arrallFUNC: if (operands_p[0]->valueType() == VTArray) { return allTrue (operands_p[0]->getArrayBool(id)); } return operands_p[0]->getBool (id); case isnanFUNC: if (argDataType_p == NTComplex) { return isNaN(operands_p[0]->getDComplex(id)); } return isNaN(operands_p[0]->getDouble(id)); case isinfFUNC: if (argDataType_p == NTComplex) { return isInf(operands_p[0]->getDComplex(id)); } return isInf(operands_p[0]->getDouble(id)); case isfiniteFUNC: if (argDataType_p == NTComplex) { return isFinite(operands_p[0]->getDComplex(id)); } return isFinite(operands_p[0]->getDouble(id)); case isdefFUNC: return operands_p[0]->isDefined (id); case isnullFUNC: if (operands_p[0]->valueType() == VTArray) { switch (operands()[0]->dataType()) { case NTBool: return (operands()[0]->getArrayBool(id).isNull()); case NTInt: return (operands()[0]->getArrayInt(id).isNull()); case NTDouble: return (operands()[0]->getArrayDouble(id).isNull()); case NTComplex: return (operands()[0]->getArrayDComplex(id).isNull()); case NTString: return (operands()[0]->getArrayString(id).isNull()); case NTDate: return (operands()[0]->getArrayDate(id).isNull()); default: throw TableInvExpr ("TableExprFuncNode::getBool, " "unknown datatype in isNull function"); } } return False; case iscolFUNC: return table_p.tableDesc().isColumn (operands_p[0]->getString (id)); case iskeyFUNC: { String name = operands_p[0]->getString (id); String shand, columnName; Vector fieldNames; TableParseUtil::splitName (shand, columnName, fieldNames, name, True, True, False); if (! shand.empty()) { return False; } const TableRecord* rec; String fullName; try { if (columnName.empty()) { rec = TableExprNode::findLastKeyRec (table_p.keywordSet(), fieldNames, fullName); } else { TableColumn column(table_p, columnName); const TableRecord& colkeys = column.keywordSet(); rec = TableExprNode::findLastKeyRec (colkeys, fieldNames, fullName); } } catch (const std::exception&) { return False; } String keyName = fieldNames[fieldNames.size() -1 ]; return rec->isDefined (keyName); } case near2FUNC: if (argDataType_p == NTDouble) { return near (operands_p[0]->getDouble(id), operands_p[1]->getDouble(id), 1.0e-13); } return near (operands_p[0]->getDComplex(id), operands_p[1]->getDComplex(id), 1.0e-13); case near3FUNC: if (argDataType_p == NTDouble) { return near (operands_p[0]->getDouble(id), operands_p[1]->getDouble(id), operands_p[2]->getDouble(id)); } return near (operands_p[0]->getDComplex(id), operands_p[1]->getDComplex(id), operands_p[2]->getDouble(id)); case nearabs2FUNC: if (argDataType_p == NTDouble) { return nearAbs (operands_p[0]->getDouble(id), operands_p[1]->getDouble(id), 1.0e-13); } return nearAbs (operands_p[0]->getDComplex(id), operands_p[1]->getDComplex(id), 1.0e-13); case nearabs3FUNC: if (argDataType_p == NTDouble) { return nearAbs (operands_p[0]->getDouble(id), operands_p[1]->getDouble(id), operands_p[2]->getDouble(id)); } return nearAbs (operands_p[0]->getDComplex(id), operands_p[1]->getDComplex(id), operands_p[2]->getDouble(id)); case iifFUNC: return operands_p[0]->getBool(id) ? operands_p[1]->getBool(id) : operands_p[2]->getBool(id); default: throw (TableInvExpr ("TableExprFuncNode::getBool, " "unknown function " + String::toString(funcType_p))); } return True; } Int64 TableExprFuncNode::getInt (const TableExprId& id) { switch(funcType_p) { case powFUNC: { Double val = pow (operands_p[0]->getDouble(id), operands_p[1]->getDouble(id)); return Int64(val<0 ? ceil(val-0.5) : floor(val+0.5)); } case squareFUNC: { Int64 val = operands_p[0]->getInt(id); return val * val; } case cubeFUNC: { Int64 val = operands_p[0]->getInt(id); return val * val * val; } case minFUNC: return std::min (operands_p[0]->getInt(id), operands_p[1]->getInt(id)); case maxFUNC: return std::max (operands_p[0]->getInt(id), operands_p[1]->getInt(id)); case normFUNC: { Int64 val = operands_p[0]->getInt(id); return val * val; } case absFUNC: return abs (operands_p[0]->getInt(id)); case intFUNC: if (operands_p[0]->dataType() == NTString) { return string2Int (operands_p[0]->getString(id)); } else if (operands_p[0]->dataType() == NTBool) { return operands_p[0]->getBool(id) ? 1:0; } else if (argDataType_p == NTDouble) { return Int64 (operands_p[0]->getDouble(id)); } return operands_p[0]->getInt(id); case signFUNC: { Int64 val = operands_p[0]->getInt(id); if (val > 0) { return 1; } if (val < 0) { return -1; } return 0; } case roundFUNC: return operands_p[0]->getInt(id); case floorFUNC: return operands_p[0]->getInt(id); case ceilFUNC: return operands_p[0]->getInt(id); case fmodFUNC: return operands_p[0]->getInt(id) % operands_p[1]->getInt(id); case strlengthFUNC: return operands_p[0]->getString (id).length(); case yearFUNC: return operands_p[0]->getDate(id).year(); case monthFUNC: return operands_p[0]->getDate(id).month(); case dayFUNC: return operands_p[0]->getDate(id).monthday(); case weekdayFUNC: return operands_p[0]->getDate(id).weekday(); case weekFUNC: return operands_p[0]->getDate(id).yearweek(); case arrminFUNC: if (operands_p[0]->valueType() == VTArray) { MArray tmp = operands_p[0]->getArrayInt (id); return min(tmp); } return operands_p[0]->getInt (id); case arrmaxFUNC: if (operands_p[0]->valueType() == VTArray) { MArray tmp = operands_p[0]->getArrayInt (id); return max(tmp); } return operands_p[0]->getInt (id); case arrsumFUNC: if (operands_p[0]->valueType() == VTArray) { return sum (operands_p[0]->getArrayInt (id)); } return operands_p[0]->getInt (id); case arrproductFUNC: if (operands_p[0]->valueType() == VTArray) { return product (operands_p[0]->getArrayInt (id)); } return operands_p[0]->getInt (id); case arrsumsqrFUNC: if (operands_p[0]->valueType() == VTArray) { return sumsqr (operands_p[0]->getArrayInt (id)); } else { Int64 val = operands_p[0]->getInt(id); return val * val; } case arrntrueFUNC: if (operands_p[0]->valueType() == VTArray) { return ntrue (operands_p[0]->getArrayBool (id)); } return (operands_p[0]->getBool(id) ? 1 : 0); case arrnfalseFUNC: if (operands_p[0]->valueType() == VTArray) { return nfalse (operands_p[0]->getArrayBool (id)); } return (operands_p[0]->getBool(id) ? 0 : 1); case ndimFUNC: { // Return fixed dimensionality if available. Int64 nrdim = operands_p[0]->ndim(); return (nrdim >= 0 ? nrdim : operands_p[0]->shape(id).size()); } case nelemFUNC: return (operands_p[0]->valueType() == VTScalar ? 1 : operands_p[0]->shape(id).product()); case iifFUNC: return operands_p[0]->getBool(id) ? operands_p[1]->getInt(id) : operands_p[2]->getInt(id); default: throw (TableInvExpr ("TableExprFuncNode::getInt, " "unknown function " + String::toString(funcType_p))); } return 0; } Double TableExprFuncNode::getDouble (const TableExprId& id) { if (dataType() == NTInt) { return TableExprFuncNode::getInt (id); } // Delta degrees of freedom for variance/stddev. uInt ddof = 1; switch(funcType_p) { case piFUNC: return M_PI; case eFUNC: return M_E; case cFUNC: return C::c; case sinFUNC: return sin (operands_p[0]->getDouble(id)); case sinhFUNC: return sinh (operands_p[0]->getDouble(id)); case cosFUNC: return cos (operands_p[0]->getDouble(id)); case coshFUNC: return cosh (operands_p[0]->getDouble(id)); case expFUNC: return exp (operands_p[0]->getDouble(id)); case logFUNC: return log (operands_p[0]->getDouble(id)); case log10FUNC: return log10 (operands_p[0]->getDouble(id)); case powFUNC: return pow (operands_p[0]->getDouble(id), operands_p[1]->getDouble(id)); case squareFUNC: { Double val = operands_p[0]->getDouble(id); return val * val; } case cubeFUNC: { Double val = operands_p[0]->getDouble(id); return val * val * val; } case sqrtFUNC: return sqrt (operands_p[0]->getDouble(id)) * scale_p; case conjFUNC: return operands_p[0]->getDouble(id); case minFUNC: return min (operands_p[0]->getDouble(id), operands_p[1]->getDouble(id)); case maxFUNC: return max (operands_p[0]->getDouble(id), operands_p[1]->getDouble(id)); case normFUNC: if (argDataType_p == NTDouble) { Double val = operands_p[0]->getDouble(id); return val * val; } return norm (operands_p[0]->getDComplex(id)); case absFUNC: if (argDataType_p == NTDouble) { return abs (operands_p[0]->getDouble(id)); } return abs (operands_p[0]->getDComplex(id)); case argFUNC: if (argDataType_p == NTDouble) { if (operands_p[0]->getDouble(id) >= 0) { return 0; } return atan2 (Double(0), Double(-1)); // results in pi } return arg (operands_p[0]->getDComplex(id)); case realFUNC: if (operands_p[0]->dataType() == NTString) { return string2Real (operands_p[0]->getString(id)); } else if (operands_p[0]->dataType() == NTBool) { return operands_p[0]->getBool(id) ? 1:0; } else if (argDataType_p == NTInt) { return operands_p[0]->getInt(id); } else if (argDataType_p == NTDouble) { return operands_p[0]->getDouble(id); } return operands_p[0]->getDComplex(id).real(); case imagFUNC: if (argDataType_p == NTDouble) { return 0; } return operands_p[0]->getDComplex(id).imag(); case asinFUNC: return asin (operands_p[0]->getDouble(id)); case acosFUNC: return acos (operands_p[0]->getDouble(id)); case atanFUNC: return atan (operands_p[0]->getDouble(id)); case tanFUNC: return tan (operands_p[0]->getDouble(id)); case tanhFUNC: return tanh (operands_p[0]->getDouble(id)); case atan2FUNC: return atan2 (operands_p[0]->getDouble(id), operands_p[1]->getDouble(id)); case signFUNC: { Double val = operands_p[0]->getDouble(id); if (val > 0) { return 1; } if (val < 0) { return -1; } return 0; } case roundFUNC: { Double val = operands_p[0]->getDouble(id); if (val < 0) { return ceil (val - 0.5); } return floor (val + 0.5); } case floorFUNC: return floor (operands_p[0]->getDouble(id)); case ceilFUNC: return ceil (operands_p[0]->getDouble(id)); case fmodFUNC: return fmod (operands_p[0]->getDouble(id), operands_p[1]->getDouble(id)); case mjdFUNC: return operands_p[0]->getDate(id).day(); case timeFUNC: //# return in radians return fmod (Double(operands_p[0]->getDate(id)), 1.) * 2.0 * M_PI; case arrminFUNC: if (operands_p[0]->valueType() == VTArray) { return min (operands_p[0]->getArrayDouble (id)); } return operands_p[0]->getDouble (id); case arrmaxFUNC: if (operands_p[0]->valueType() == VTArray) { return max (operands_p[0]->getArrayDouble (id)); } return operands_p[0]->getDouble (id); case arrsumFUNC: if (operands_p[0]->valueType() == VTArray) { return sum (operands_p[0]->getArrayDouble (id)); } return operands_p[0]->getDouble (id); case arrproductFUNC: if (operands_p[0]->valueType() == VTArray) { return product (operands_p[0]->getArrayDouble (id)); } return operands_p[0]->getDouble (id); case arrsumsqrFUNC: if (operands_p[0]->valueType() == VTArray) { return sumsqr (operands_p[0]->getArrayDouble (id)); } else { Double val = operands_p[0]->getDouble(id); return val * val; } case arrmeanFUNC: if (operands_p[0]->valueType() == VTArray) { return mean (operands_p[0]->getArrayDouble (id)); } return operands_p[0]->getDouble (id); case arrvariance0FUNC: ddof = 0; // fall through case arrvariance1FUNC: if (operands_p[0]->valueType() == VTArray) { if (operands_p[0]->dataType() == NTComplex) { return real(variance (operands_p[0]->getArrayDComplex(id), ddof)); } return variance (operands_p[0]->getArrayDouble(id), ddof); } return 0; case arrstddev0FUNC: ddof = 0; // fall through case arrstddev1FUNC: if (operands_p[0]->valueType() == VTArray) { if (operands_p[0]->dataType() == NTComplex) { return real(stddev (operands_p[0]->getArrayDComplex(id), ddof)); } return stddev (operands_p[0]->getArrayDouble(id), ddof); } return 0; case arravdevFUNC: if (operands_p[0]->valueType() == VTArray) { if (operands_p[0]->dataType() == NTComplex) { return real(avdev (operands_p[0]->getArrayDComplex (id))); } return avdev (operands_p[0]->getArrayDouble (id)); } return 0; case arrrmsFUNC: if (operands_p[0]->valueType() == VTArray) { return rms (operands_p[0]->getArrayDouble (id)); } return operands_p[0]->getDouble (id); case arrmedianFUNC: if (operands_p[0]->valueType() == VTArray) { return median (operands_p[0]->getArrayDouble (id)); } return operands_p[0]->getDouble (id); case arrfractileFUNC: if (operands_p[0]->valueType() == VTArray) { return fractile (operands_p[0]->getArrayDouble (id), operands_p[1]->getDouble (id)); } return operands_p[0]->getDouble (id); case iifFUNC: return operands_p[0]->getBool(id) ? operands_p[1]->getDouble(id) : operands_p[2]->getDouble(id); case angdistFUNC: case angdistxFUNC: { MArray a1 = operands_p[0]->getArrayDouble(id); MArray a2 = operands_p[1]->getArrayDouble(id); if (!(a1.size() == 2 && a1.array().contiguousStorage() && a2.size() == 2 && a2.array().contiguousStorage())) { throw TableInvExpr ("Arguments of function ANGDIST[x] must have a " "multiple of 2 values"); } const double* d1 = a1.array().data(); const double* d2 = a2.array().data(); return angdist (d1[0], d1[1], d2[0], d2[1]); } case normangleFUNC: { double v = fmod(operands_p[0]->getDouble(id), 2.0 * M_PI); if (v < -M_PI) v += 2.0 * M_PI; return (v <= M_PI ? v : v-(2.0 * M_PI)); } case datetimeFUNC: case mjdtodateFUNC: case dateFUNC: return getDate(id); // automatic conversion of MVTime to double default: // Functions like YEAR are implemented as Int. return getInt(id); } return 0; } DComplex TableExprFuncNode::getDComplex (const TableExprId& id) { if (dataType() == NTDouble) { return TableExprFuncNode::getDouble (id); } switch (funcType_p) { case sinFUNC: return sin (operands_p[0]->getDComplex(id)); case sinhFUNC: return sinh (operands_p[0]->getDComplex(id)); case cosFUNC: return cos (operands_p[0]->getDComplex(id)); case coshFUNC: return cosh (operands_p[0]->getDComplex(id)); case expFUNC: return exp (operands_p[0]->getDComplex(id)); case logFUNC: return log (operands_p[0]->getDComplex(id)); case log10FUNC: return log10 (operands_p[0]->getDComplex(id)); case powFUNC: return pow (operands_p[0]->getDComplex(id), operands_p[1]->getDComplex(id)); case squareFUNC: { DComplex val = operands_p[0]->getDComplex(id); return val * val; } case cubeFUNC: { DComplex val = operands_p[0]->getDComplex(id); return val * val * val; } case sqrtFUNC: return sqrt (operands_p[0]->getDComplex(id)) * scale_p; case conjFUNC: return conj (operands_p[0]->getDComplex(id)); case minFUNC: { DComplex val0(operands_p[0]->getDComplex (id)); DComplex val1(operands_p[1]->getDComplex (id)); if (val0 > val1) { return val1; } return val0; } case maxFUNC: { DComplex val0(operands_p[0]->getDComplex (id)); DComplex val1(operands_p[1]->getDComplex (id)); if (val0 < val1) { return val1; } return val0; } case asinFUNC: return asin (operands_p[0]->getDComplex(id)); case acosFUNC: return acos (operands_p[0]->getDComplex(id)); case atanFUNC: return atan (operands_p[0]->getDComplex(id)); case tanFUNC: return tan (operands_p[0]->getDComplex(id)); case tanhFUNC: return tanh (operands_p[0]->getDComplex(id)); case complexFUNC: // A single argument is always a string. if (operands_p.size() == 1) { return string2Complex (operands_p[0]->getString(id)); } return DComplex (operands_p[0]->getDouble (id), operands_p[1]->getDouble (id)); case arrsumFUNC: if (operands_p[0]->valueType() == VTArray) { return sum (operands_p[0]->getArrayDComplex (id)); } return operands_p[0]->getDComplex (id); case arrproductFUNC: if (operands_p[0]->valueType() == VTArray) { return product (operands_p[0]->getArrayDComplex (id)); } return operands_p[0]->getDComplex (id); case arrsumsqrFUNC: if (operands_p[0]->valueType() == VTArray) { return sumsqr (operands_p[0]->getArrayDComplex (id)); } else { DComplex val = operands_p[0]->getDComplex (id); return val * val; } case arrmeanFUNC: if (operands_p[0]->valueType() == VTArray) { return mean (operands_p[0]->getArrayDComplex (id)); } return operands_p[0]->getDComplex (id); case iifFUNC: return operands_p[0]->getBool(id) ? operands_p[1]->getDComplex(id) : operands_p[2]->getDComplex(id); default: throw (TableInvExpr ("TableExprFuncNode::getDComplex, " "unknown function " + String::toString(funcType_p))); } return DComplex(0., 0.); } String TableExprFuncNode::getString (const TableExprId& id) { static Regex leadingWS("^[ \t]*"); static Regex trailingWS("[ \t]*$"); switch (funcType_p) { case upcaseFUNC: { String str = operands_p[0]->getString (id); str.upcase(); return str; } case downcaseFUNC: { String str = operands_p[0]->getString (id); str.downcase(); return str; } case capitalizeFUNC: { String str = operands_p[0]->getString (id); str.capitalize(); return str; } case sreverseFUNC: { String str = operands_p[0]->getString (id); str.reverse(); return str; } case trimFUNC: { String str = operands_p[0]->getString (id); str.trim(); return str; } case ltrimFUNC: { String str = operands_p[0]->getString (id); str.gsub (leadingWS, String()); return str; } case rtrimFUNC: { String str = operands_p[0]->getString (id); str.gsub (trailingWS, String()); return str; } case substrFUNC: { String str = operands_p[0]->getString (id); Int64 st = operands_p[1]->getInt (id); if (st < 0) st += str.size(); if (st < 0) st = 0; if (st > Int64(str.size())) st = str.size(); Int64 sz = String::npos; if (operands_p.size() > 2) { sz = std::max (Int64(0), operands_p[2]->getInt (id)); } return str.substr (st, sz); } case replaceFUNC: { String str = operands_p[0]->getString (id); String repl; if (operands_p.size() > 2) { repl = operands_p[2]->getString (id); } if (operands_p[1]->dataType() == NTString) { str.gsub (operands_p[1]->getString(id), repl); } else { str.gsub (operands_p[1]->getRegex(id).regex(), repl); } return str; } case cmonthFUNC: return operands_p[0]->getDate(id).monthName(); case cdowFUNC: return operands_p[0]->getDate(id).dayName(); case ctodFUNC: return stringDateTime (operands_p[0]->getDate(id), 9); case cdateFUNC: return stringDate (operands_p[0]->getDate(id)); case ctimeFUNC: return stringTime (operands_p[0]->getDate(id), 9); case stringFUNC: { String fmt; Int width, prec; getPrintFormat (fmt, width, prec, operands_p, id); if (operands_p[0]->dataType() == NTBool) { return stringValue (operands_p[0]->getBool(id), fmt, width); } else if (operands_p[0]->dataType() == NTInt) { return stringValue (operands_p[0]->getInt(id), fmt, width); } else if (operands_p[0]->dataType() == NTDouble) { return stringValue (operands_p[0]->getDouble(id), fmt, width, prec, getMVFormat(fmt), operands_p[0]->unit()); } else if (operands_p[0]->dataType() == NTComplex) { return stringValue (operands_p[0]->getDComplex(id), fmt, width, prec); } else if (operands_p[0]->dataType() == NTDate) { return stringValue (operands_p[0]->getDate(id), fmt, width, getMVFormat(fmt)); } return stringValue (operands_p[0]->getString(id), fmt, width); } case hmsFUNC: return stringHMS (operands_p[0]->getDouble(id), 9); case dmsFUNC: return stringDMS (operands_p[0]->getDouble(id), 9); case iifFUNC: return operands_p[0]->getBool(id) ? operands_p[1]->getString(id) : operands_p[2]->getString(id); default: throw (TableInvExpr ("TableExprFuncNode::getString, " "unknown function " + String::toString(funcType_p))); } return ""; } TaqlRegex TableExprFuncNode::getRegex (const TableExprId& id) { switch (funcType_p) { case regexFUNC: return TaqlRegex(Regex(operands_p[0]->getString (id), True)); case patternFUNC: return TaqlRegex(Regex(Regex::fromPattern(operands_p[0]->getString (id)), True)); case sqlpatternFUNC: return TaqlRegex(Regex(Regex::fromSQLPattern(operands_p[0]->getString (id)), True)); case iifFUNC: return operands_p[0]->getBool(id) ? operands_p[1]->getRegex(id) : operands_p[2]->getRegex(id); default: break; } throw (TableInvExpr ("TableExprFuncNode::getRegex, " "unknown function " + String::toString(funcType_p))); } MVTime TableExprFuncNode::getDate (const TableExprId& id) { switch (funcType_p) { case datetimeFUNC: { Quantity quant; if (MVTime::read (quant, operands_p[0]->getString(id))) { return quant; } throw (TableInvExpr ("invalid date string " + operands_p[0]->getString(id))); } case mjdtodateFUNC: return MVTime (operands_p[0]->getDouble(id)); case dateFUNC: return MVTime (floor (Double (operands_p[0]->getDate(id)))); case iifFUNC: return operands_p[0]->getBool(id) ? operands_p[1]->getDate(id) : operands_p[2]->getDate(id); default: throw (TableInvExpr ("TableExprFuncNode::getDate, " "unknown function " + String::toString(funcType_p))); } return MVTime(); } void TableExprFuncNode::getPrintFormat (String& fmt, Int& width, Int& prec, const vector& operands, const TableExprId& id) { width = 0; prec = 0; if (operands.size() > 1) { if (operands[1]->dataType() == NTString) { fmt = operands[1]->getString(id); } else { // Format can be given as a double like w.p (e.g. 10.5). // Add small value for numerical inaccuracy double w = operands[1]->getDouble(id) + 1e-10; width = w; w -= width; w *= 10; if (w - int(w) > 1e-5) { w *= 10; } prec = w; } } } std::pair TableExprFuncNode::getMVFormat (const String& fmt) { int mvFormat = 0; int prec = 6; if (! fmt.empty()) { // The format can consist of the various MVTime/Angle format specifiers // (separated by vertical bars or commas with optional spaces). char separator = ','; if (fmt.find('|') != String::npos) { separator = '|'; } Vector fmts = stringToVector(fmt, separator); Bool ok = True; for (uInt i=0; i 0) os << std::setw(width); os << val; return os.str(); } return String::format (fmt.c_str(), val); } String TableExprFuncNode::stringValue (Double val, const String& fmt, Int width, Int prec, const std::pair& mvFormat, const Unit& unit) { if (fmt.empty()) { ostringstream os; if (width > 0) os << std::setw(width); if (prec > 0) os << std::setprecision(prec); os << val; return os.str(); } if (mvFormat.first >= 0) { // If formatted as angle, convert to radians if possible. if (! (unit.empty() || unit.getName() == "rad")) { val = Quantity(val, unit).getValue("rad"); } return stringAngle (val, mvFormat.second, MVAngle::formatTypes(mvFormat.first)); } return String::format (fmt.c_str(), val); } String TableExprFuncNode::stringValue (const DComplex& val, const String& fmt, Int width, Int prec) { if (fmt.empty()) { ostringstream os; if (width <=0 && prec <= 0) { os << val; } else { os << '('; if (width > 0) os << std::setw(width); if (prec > 0) os << std::setprecision(prec); os << val.real() << ','; if (width > 0) os << std::setw(width); if (prec > 0) os << std::setprecision(prec); os << val.imag() << ')'; } return os.str(); } return String::format (fmt.c_str(), val.real(), val.imag()); } String TableExprFuncNode::stringValue (const String& val, const String& fmt, Int width) { if (fmt.empty()) { if (width <= 0) return val; ostringstream os; // Take substr because operator<< does not truncate value if > width. os << std::setw(width) << val.substr(0,width); return os.str(); } return String::format (fmt.c_str(), val.c_str()); } String TableExprFuncNode::stringValue (const MVTime& val, const String& fmt, Int width, const std::pair& mvFormat) { if (fmt.empty()) { if (width <= 0) width = 6; return stringDateTime (val, width); } if (mvFormat.first >= 0) { return stringDT (val, mvFormat.second, MVTime::formatTypes(mvFormat.first)); } return String::format (fmt.c_str(), val.day()); } String TableExprFuncNode::stringHMS (double val, Int prec) { // Replace : by h and m. String s = stringAngle (val, prec, MVAngle::TIME); char r = 'h'; for (uInt i=0; i& dtypeOper, ValueType& resVT, Block&, FunctionType fType, vector& nodes) { // The default returned value type is a scalar. resVT = VTScalar; // The default datatype is NTDouble. NodeDataType dtin = NTDouble; NodeDataType dtout = NTDouble; // The following functions accept a single scalar or array argument. // They result in a scalar. switch (fType) { case arrminFUNC: case arrmaxFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTReal, NTReal, nodes); case arrmeanFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTNumeric, NTDouCom, nodes); case arrvariance0FUNC: case arrvariance1FUNC: case arrstddev0FUNC: case arrstddev1FUNC: case arravdevFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTNumeric, NTDouble, nodes); case arrrmsFUNC: case arrmedianFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTReal, NTDouble, nodes); case arrfractileFUNC: checkNumOfArg (2, 2, nodes); if (nodes[1]->valueType() != VTScalar) { throw TableInvExpr ("2nd argument of function FRACTILE " "has to be a scalar"); } return checkDT (dtypeOper, NTReal, NTDouble, nodes); case arrsumFUNC: case arrproductFUNC: case arrsumsqrFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTNumeric, NTNumeric, nodes); case arranyFUNC: case arrallFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTBool, NTBool, nodes); case arrntrueFUNC: case arrnfalseFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTBool, NTInt, nodes); case nelemFUNC: case ndimFUNC: case shapeFUNC: checkNumOfArg (1, 1, nodes); if (fType == shapeFUNC) { resVT = VTArray; } return checkDT (dtypeOper, NTAny, NTInt, nodes); case isdefFUNC: case isnullFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTAny, NTBool, nodes); case iscolFUNC: case iskeyFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTString, NTBool, nodes); case angdistFUNC: case angdistxFUNC: checkNumOfArg (2, 2, nodes); if (nodes[0]->valueType() != VTArray || nodes[1]->valueType() != VTArray) { throw TableInvExpr ("Arguments of function ANGDIST[x] " "have to be arrays"); } if (nodes[0]->shape().product() != 2 || nodes[1]->shape().product() != 2) { resVT = VTArray; // result is scalar if both arg have 2 values } return checkDT (dtypeOper, NTReal, NTDouble, nodes); case marrayFUNC: checkNumOfArg (2, 2, nodes); resVT = VTArray; if (nodes[1]->dataType() != NTBool) { throw TableInvExpr("Second argument of marray function must be bool"); } checkDT (dtypeOper, NTAny, NTBool, nodes); return nodes[0]->dataType(); break; case nullarrayFUNC: case arrdataFUNC: case negatemaskFUNC: case arrflatFUNC: checkNumOfArg (1, 1, nodes); resVT = VTArray; return checkDT (dtypeOper, NTAny, NTAny, nodes); break; case arrmaskFUNC: checkNumOfArg (1, 1, nodes); resVT = VTArray; return checkDT (dtypeOper, NTAny, NTBool, nodes); break; case replmaskedFUNC: case replunmaskedFUNC: checkNumOfArg (2, 2, nodes); resVT = VTArray; checkDT (dtypeOper, NTAny, NTAny, nodes); return nodes[0]->dataType(); break; default: break; } // The following functions accept one array and a set of // one or more scalars. // They return an array. switch (fType) { case arrsumsFUNC: case arrproductsFUNC: case arrsumsqrsFUNC: case arrminsFUNC: case arrmaxsFUNC: case arrmeansFUNC: case arrvariances0FUNC: case arrvariances1FUNC: case arrstddevs0FUNC: case arrstddevs1FUNC: case arravdevsFUNC: case arrrmssFUNC: case arrmediansFUNC: case arrfractilesFUNC: case arranysFUNC: case arrallsFUNC: case arrntruesFUNC: case arrnfalsesFUNC: case runsumFUNC: case runproductFUNC: case runsumsqrFUNC: case runminFUNC: case runmaxFUNC: case runmeanFUNC: case runvariance0FUNC: case runvariance1FUNC: case runstddev0FUNC: case runstddev1FUNC: case runavdevFUNC: case runrmsFUNC: case runmedianFUNC: case runfractileFUNC: case runanyFUNC: case runallFUNC: case runntrueFUNC: case runnfalseFUNC: case boxsumFUNC: case boxproductFUNC: case boxsumsqrFUNC: case boxminFUNC: case boxmaxFUNC: case boxmeanFUNC: case boxvariance0FUNC: case boxvariance1FUNC: case boxstddev0FUNC: case boxstddev1FUNC: case boxavdevFUNC: case boxrmsFUNC: case boxmedianFUNC: case boxfractileFUNC: case boxanyFUNC: case boxallFUNC: case boxntrueFUNC: case boxnfalseFUNC: case arrayFUNC: case transposeFUNC: case areverseFUNC: case resizeFUNC: case diagonalFUNC: { // Most functions can have Int or Double in and result in Double. dtin = NTReal; dtout = NTDouble; uInt axarg = 1; uInt optarg = 0; switch (fType) { case arrsumsFUNC: case arrproductsFUNC: case arrsumsqrsFUNC: case runsumFUNC: case runproductFUNC: case runsumsqrFUNC: case boxsumFUNC: case boxproductFUNC: case boxsumsqrFUNC: dtin = dtout = NTNumeric; break; case arrminsFUNC: case arrmaxsFUNC: case runminFUNC: case runmaxFUNC: case boxminFUNC: case boxmaxFUNC: dtin = dtout = NTReal; break; case arrmeansFUNC: case runmeanFUNC: case boxmeanFUNC: dtin = NTNumeric; dtout = NTDouCom; break; case arrvariances0FUNC: case arrvariances1FUNC: case arrstddevs0FUNC: case arrstddevs1FUNC: case arravdevsFUNC: case runvariance0FUNC: case runvariance1FUNC: case runstddev0FUNC: case runstddev1FUNC: case runavdevFUNC: case boxvariance0FUNC: case boxvariance1FUNC: case boxstddev0FUNC: case boxstddev1FUNC: case boxavdevFUNC: dtin = NTNumeric; break; case arrfractilesFUNC: case runfractileFUNC: case boxfractileFUNC: axarg = 2; break; case arranysFUNC: case arrallsFUNC: case runanyFUNC: case runallFUNC: case boxanyFUNC: case boxallFUNC: dtin = dtout = NTBool; break; case arrntruesFUNC: case arrnfalsesFUNC: case runntrueFUNC: case runnfalseFUNC: case boxntrueFUNC: case boxnfalseFUNC: dtin = NTBool; dtout = NTInt; break; case resizeFUNC: optarg = 1; CASACORE_FALLTHROUGH; case arrayFUNC: case transposeFUNC: case areverseFUNC: case diagonalFUNC: dtin = dtout = NTAny; break; default: break; } // The result is an array. // All arguments (except possibly first) must be integers. resVT = VTArray; checkNumOfArg (axarg+1, axarg+optarg+1, nodes); dtypeOper.resize(axarg+1); dtypeOper = NTReal; // Check if first argument is array. if (fType != arrayFUNC) { if (nodes[0]->valueType() != VTArray) { throw TableInvExpr ("1st argument of function nr " + String::toString(fType) + " has to be an array"); } } // Check if first argument has correct type. vector nodeTmp(1); nodeTmp[0] = nodes[0]; Block dtypeTmp; // Gets filled in by checkDT dtout = checkDT (dtypeTmp, dtin, dtout, nodeTmp); dtypeOper[0] = dtypeTmp[0]; // If more arguments are needed, they have to be Real scalars. if (axarg > 1) { for (uInt i=1; ivalueType() != VTScalar || (nodes[i]->dataType() != NTInt && nodes[i]->dataType() != NTDouble)) { throw TableInvExpr ("2nd argument of function FRACTILE " "has to be a real scalar"); } } } if (nodes[axarg]->dataType() != NTInt) { throw TableInvExpr ("The axes arguments of RUNNINGxxx, BOXEDxxx, " "or XXXs function " + String::toString(fType) + " have to be integers"); } // The last argument forms the axes as an array object. AlwaysAssert (nodes[axarg]->valueType() == VTArray, AipsError); // Check if an optional arg (voor expand) is an Int. if (nodes.size() == axarg+optarg+1) { if (nodes[axarg+optarg]->dataType() != NTInt) { throw TableInvExpr ("3rd argument of function RESIZE" " has to be integer"); } } return dtout; } default: break; } // The following functions accept scalars and arrays. // They return an array if one of the input arguments is an array. // If a function has no arguments, it results in a scalar. for (uInt i=0; i< nodes.size(); i++) { ValueType vt = nodes[i]->valueType(); if (vt == VTArray) { resVT = vt; } else if (vt != VTScalar) { throw TableInvExpr ("Function nr " + String::toString(fType) + " has to have a scalar or array argument"); } } switch (fType) { case strlengthFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTString, NTInt, nodes); case upcaseFUNC: case downcaseFUNC: case capitalizeFUNC: case sreverseFUNC: case trimFUNC: case ltrimFUNC: case rtrimFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTString, NTString, nodes); case substrFUNC: checkNumOfArg (2, 3, nodes); if (nodes[0]->dataType() != NTString) { throw TableInvExpr ("1st argument of function SUBSTR " "has to be a string"); } for (uInt i=1; ivalueType() != VTScalar || nodes[i]->dataType() != NTInt) { throw TableInvExpr ("2nd and optional 3rd argument of function " "SUBSTR have to be integer scalars"); } } dtypeOper.resize (nodes.size()); dtypeOper = NTInt; dtypeOper[0] = NTString; return NTString; case replaceFUNC: checkNumOfArg (2, 3, nodes); if (nodes[0]->dataType() != NTString) { throw TableInvExpr ("1st argument of function REPLACE " "has to be a string"); } if (nodes[1]->valueType() != VTScalar || (nodes[1]->dataType() != NTString && nodes[1]->dataType() != NTRegex)) { throw TableInvExpr ("2nd argument of function REPLACE " "has to be a string or regex"); } if (nodes.size() == 3) { if (nodes[2]->valueType() != VTScalar || nodes[2]->dataType() != NTString) { throw TableInvExpr ("Optional 3rd argument of function REPLACE " "has to be a string scalar"); } } dtypeOper.resize (nodes.size()); dtypeOper = NTString; dtypeOper[1] = nodes[1]->dataType(); return NTString; case datetimeFUNC: if (checkNumOfArg (0, 1, nodes) == 1) { return checkDT (dtypeOper, NTString, NTDate, nodes); } dtypeOper.resize (1); dtypeOper[0] = NTString; nodes.resize(1); nodes[0] = std::make_shared("today"); return NTDate; case mjdtodateFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTReal, NTDate, nodes); case dateFUNC: if (checkNumOfArg (0, 1, nodes) == 1) { return checkDT (dtypeOper, NTDate, NTDate, nodes); } dtypeOper.resize (1); dtypeOper[0] = NTDate; nodes.resize (1); nodes[0] = std::make_shared(MVTime(Time())); return NTDate; case yearFUNC: case monthFUNC: case dayFUNC: case weekdayFUNC: case weekFUNC: dtout = NTInt; CASACORE_FALLTHROUGH; case mjdFUNC: case timeFUNC: if (checkNumOfArg (0, 1, nodes) == 1) { return checkDT (dtypeOper, NTDate, dtout, nodes); } dtypeOper.resize (1); dtypeOper[0] = NTDate; nodes.resize (1); nodes[0] = std::make_shared(MVTime(Time())); return dtout; case cmonthFUNC: case cdowFUNC: case ctodFUNC: case cdateFUNC: case ctimeFUNC: if (checkNumOfArg (0, 1, nodes) == 1) { return checkDT (dtypeOper, NTDate, NTString, nodes); } dtypeOper.resize (1); dtypeOper[0] = NTDate; nodes.resize (1); nodes[0] = std::make_shared(MVTime(Time())); return NTString; case stringFUNC: if (checkNumOfArg (1, 2, nodes) == 2) { if ((nodes[1]->dataType() != NTString && nodes[1]->dataType() != NTDouble && nodes[1]->dataType() != NTInt) || nodes[1]->valueType() != VTScalar) { throw TableInvExpr ("2nd argument of function STRING " "has to be a scalar string or real value"); } } dtypeOper.resize (nodes.size()); dtypeOper[0] = nodes[0]->dataType(); if (nodes.size() > 1) { dtypeOper[1] = nodes[1]->dataType(); } return NTString; case hmsFUNC: case dmsFUNC: case hdmsFUNC: checkNumOfArg (1, 1, nodes); if (fType == hdmsFUNC && nodes[0]->valueType() != VTArray) { throw TableInvExpr("Argument of function HDMS has to be an array"); } return checkDT (dtypeOper, NTReal, NTString, nodes); case sinFUNC: case sinhFUNC: case cosFUNC: case coshFUNC: case expFUNC: case logFUNC: case log10FUNC: case sqrtFUNC: case conjFUNC: case asinFUNC: case acosFUNC: case atanFUNC: case tanFUNC: case tanhFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTNumeric, NTDouCom, nodes); case normangleFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTReal, NTDouble, nodes); case squareFUNC: case cubeFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTNumeric, NTNumeric, nodes); case normFUNC: case absFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTNumeric, NTReal, nodes); case realFUNC: checkNumOfArg (1, 1, nodes); if (nodes[0]->dataType() == NTString) { return checkDT (dtypeOper, NTString, NTDouble, nodes); } else if (nodes[0]->dataType() == NTBool) { return checkDT (dtypeOper, NTBool, NTDouble, nodes); } return checkDT (dtypeOper, NTNumeric, NTDouble, nodes); case argFUNC: case imagFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTNumeric, NTDouble, nodes); case signFUNC: case roundFUNC: case floorFUNC: case ceilFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTReal, NTReal, nodes); case intFUNC: checkNumOfArg (1, 1, nodes); if (nodes[0]->dataType() == NTString) { return checkDT (dtypeOper, NTString, NTInt, nodes); } else if (nodes[0]->dataType() == NTBool) { return checkDT (dtypeOper, NTBool, NTInt, nodes); } return checkDT (dtypeOper, NTReal, NTInt, nodes); case boolFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTAny, NTBool, nodes); case near2FUNC: case nearabs2FUNC: checkNumOfArg (2, 2, nodes); return checkDT (dtypeOper, NTNumeric, NTBool, nodes); case near3FUNC: case nearabs3FUNC: { checkNumOfArg (3, 3, nodes); // Check if tolerance has a Real value. vector nodeTol(1); nodeTol[0] = nodes[2]; checkDT (dtypeOper, NTReal, NTBool, nodeTol); if (nodes[2]->valueType() != VTScalar) { throw TableInvExpr ("3rd argument of function NEAR and NEARABS " "has to be a scalar"); } return checkDT (dtypeOper, NTNumeric, NTBool, nodes); } case powFUNC: checkNumOfArg (2, 2, nodes); return checkDT (dtypeOper, NTNumeric, NTDouCom, nodes); case minFUNC: case maxFUNC: checkNumOfArg (2, 2, nodes); return checkDT (dtypeOper, NTNumeric, NTNumeric, nodes); case atan2FUNC: checkNumOfArg (2, 2, nodes); return checkDT (dtypeOper, NTReal, NTDouble, nodes); case fmodFUNC: checkNumOfArg (2, 2, nodes); return checkDT (dtypeOper, NTReal, NTReal, nodes); case complexFUNC: if (nodes.size() == 1 && nodes[0]->dataType() == NTString) { return checkDT (dtypeOper, NTString, NTComplex, nodes); } checkNumOfArg (2, 2, nodes); return checkDT (dtypeOper, NTReal, NTComplex, nodes); case isnanFUNC: case isinfFUNC: case isfiniteFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTNumeric, NTBool, nodes); case iifFUNC: { checkNumOfArg (3, 3, nodes); vector nodeCond(1); nodeCond[0] = nodes[0]; vector nodeArg(2); nodeArg[0] = nodes[1]; nodeArg[1] = nodes[2]; Block dtypeTmp; checkDT (dtypeTmp, NTBool, NTBool, nodeCond); dtypeOper.resize(3); dtypeOper[0] = dtypeTmp[0]; // Do not allow automatic conversion to date/time. NodeDataType dt = checkDT (dtypeTmp, NTAny, NTAny, nodeArg, False); dtypeOper[1] = dtypeTmp[0]; dtypeOper[2] = dtypeTmp[1]; return dt; } default: break; } // The following functions accept scalars only (or no arguments). for (uInt i=0; i< nodes.size(); i++) { if (nodes[i]->valueType() != VTScalar) { throw TableInvExpr ("Function nr " + String::toString(fType) + " has to have a scalar argument"); } } switch (fType) { case rownrFUNC: case rowidFUNC: checkNumOfArg (0, 0, nodes); return NTInt; case randFUNC: case piFUNC: case eFUNC: case cFUNC: checkNumOfArg (0, 0, nodes); return NTDouble; case regexFUNC: case patternFUNC: case sqlpatternFUNC: checkNumOfArg (1, 1, nodes); return checkDT (dtypeOper, NTString, NTRegex, nodes); default: throw (TableInvExpr ("TableExprFuncNode::checkOperands, " "function nr " + String::toString(fType) + " not contained in switch statement")); } return NTNumeric; } Int64 TableExprFuncNode::string2Int (const String& str) { istringstream istr(str); // Initialize to 0 to make sure an empty string is handled correctly. Int64 v=0; istr >> v; return v; } Double TableExprFuncNode::string2Real (const String& str) { istringstream istr(str); Double v=0; istr >> v; return v; } DComplex TableExprFuncNode::string2Complex (const String& str) { istringstream istr(str); Double r=0, i=0; char c=' '; istr >> c; if (c == '(') { // Like (12.3, 45.6) istr >> r >> c >> i; } else { // Like 12.3, 45.6 or 12.3 + 45.6i istringstream istr2(str); istr2 >> r >> c >> i; } if (c == '-') i = -i; return DComplex(r,i); } Bool TableExprFuncNode::string2Bool (const String& str) { String s(str); s.trim(); s.downcase(); if (s.empty() || s == "f" || s == "false" || s == "0" || s == "-" || s == "n" || s == "no") { return False; } return True; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/ExprFuncNode.h000066400000000000000000000446131476623553700201200ustar00rootroot00000000000000//# ExprFuncNode.h: Class representing a function in table select expression //# Copyright (C) 1994,1995,1996,1997,1998,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_EXPRFUNCNODE_H #define TABLES_EXPRFUNCNODE_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableExprNodeSet; // // Class representing a function in table select expression // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeMulti // // // This class represents a function in a table select tree. // The rownumber function is represented by class // TableExprNodeRownr. // The rowid function is represented by class // TableExprNodeRowid. // The rand function is represented by class // TableExprNodeRandom. //

        // When one wants to add a function to the table selection grammar, // the following has to be done: //

          //
        • Add the function to the enum below. //
        • Implement the function in the get functions in ExprFuncNode(Array).cc. //
        • Implement the function in the checkOperands in ExprFuncNode.cc. //
        • Declare and define the function in ExprNode.h (for C++ binding). //
        • Add the function to findFunc in TableParse.cc (for TaQL). //
        //
        class TableExprFuncNode : public TableExprNodeMulti { public: //# Define the function types. enum FunctionType { piFUNC, //# 0 eFUNC, //# 1 cFUNC, //# 2 // for Int, or Double or Complex returning Bool // (2 is with default tolerance) near2FUNC, //# 3 near3FUNC, //# 4 nearabs2FUNC, //# 5 nearabs3FUNC, //# 6 // for Int, Double or DComplex returning Double or Complex sinFUNC, //# 7 sinhFUNC, //# 8 cosFUNC, //# 9 coshFUNC, //# 10 expFUNC, //# 11 logFUNC, //# 12 log10FUNC, //# 13 sqrtFUNC, //# 14 powFUNC, //# 15 conjFUNC, //# 16 // for Int, Double or DComplex returning Int, Double or Complex squareFUNC, //# 17 cubeFUNC, //# 18 minFUNC, //# 19 maxFUNC, //# 20 // for Int, Double or DComplex returning Int or Double normFUNC, //# 21 absFUNC, //# 22 // for Int, Double or DComplex returning Double argFUNC, //# 23 // for Int, Double, DComplex, Bool or String returning Double realFUNC, //# 24 // for Double or DComplex returning Double imagFUNC, //# 25 // for Int, Double, Bool or String returning Int (using floor) intFUNC, //# 26 // for Int, Double or Complex returning Double or Complex asinFUNC, //# 27 acosFUNC, //# 28 atanFUNC, //# 29 atan2FUNC, //# 30 tanFUNC, //# 31 tanhFUNC, //# 32 // for Int or Double returning Int or Double signFUNC, //# 33 roundFUNC, //# 34 floorFUNC, //# 35 ceilFUNC, //# 36 fmodFUNC, //# 37 // for DComplex or String returning DComplex complexFUNC, //# 38 // for Int, Double or Complex array returning the same arrsumFUNC, //# 39 arrsumsFUNC, //# 40 runsumFUNC, //# 41 boxsumFUNC, //# 42 arrproductFUNC, //# 43 arrproductsFUNC, //# 44 runproductFUNC, //# 45 boxproductFUNC, //# 46 arrsumsqrFUNC, //# 47 arrsumsqrsFUNC, //# 48 runsumsqrFUNC, //# 49 boxsumsqrFUNC, //# 50 // for Int or Double array returning Int or Double arrminFUNC, //# 51 arrminsFUNC, //# 52 runminFUNC, //# 53 boxminFUNC, //# 54 arrmaxFUNC, //# 55 arrmaxsFUNC, //# 56 runmaxFUNC, //# 57 boxmaxFUNC, //# 58 // for Int or Double array returning Double arrmeanFUNC, //# 59 arrmeansFUNC, //# 60 runmeanFUNC, //# 61 boxmeanFUNC, //# 62 arrvariance0FUNC, //# 63 arrvariances0FUNC,//# 64 runvariance0FUNC, //# 65 boxvariance0FUNC, //# 66 arrvariance1FUNC, //# 67 arrvariances1FUNC,//# 68 runvariance1FUNC, //# 69 boxvariance1FUNC, //# 70 arrstddev0FUNC, //# 71 arrstddevs0FUNC, //# 72 runstddev0FUNC, //# 73 boxstddev0FUNC, //# 74 arrstddev1FUNC, //# 75 arrstddevs1FUNC, //# 76 runstddev1FUNC, //# 77 boxstddev1FUNC, //# 78 arravdevFUNC, //# 79 arravdevsFUNC, //# 80 runavdevFUNC, //# 81 boxavdevFUNC, //# 82 arrrmsFUNC, //# 83 arrrmssFUNC, //# 84 runrmsFUNC, //# 85 boxrmsFUNC, //# 86 arrmedianFUNC, //# 87 arrmediansFUNC, //# 88 runmedianFUNC, //# 89 boxmedianFUNC, //# 90 arrfractileFUNC, //# 91 arrfractilesFUNC, //# 92 runfractileFUNC, //# 93 boxfractileFUNC, //# 94 // for Bool array returning Bool arranyFUNC, //# 95 arranysFUNC, //# 96 runanyFUNC, //# 97 boxanyFUNC, //# 98 arrallFUNC, //# 99 arrallsFUNC, //# 100 runallFUNC, //# 101 boxallFUNC, //# 102 // for Bool array returning Int scalar arrntrueFUNC, //# 103 arrntruesFUNC, //# 104 runntrueFUNC, //# 105 boxntrueFUNC, //# 106 arrnfalseFUNC, //# 107 arrnfalsesFUNC, //# 108 runnfalseFUNC, //# 109 boxnfalseFUNC, //# 110 // for any type returning array of that type arrayFUNC, //# 111 transposeFUNC, //# 112 areverseFUNC, //# 113 resizeFUNC, //# 114 diagonalFUNC, //# 115 // for Int, Double or DComplex array returning Bool isnanFUNC, //# 116 isinfFUNC, //# 117 isfiniteFUNC, //# 118 // for any array returning Bool scalar isdefFUNC, //# 119 isnullFUNC, //# 120 iscolFUNC, //# 121 iskeyFUNC, //# 122 // for any array returning Int scalar ndimFUNC, //# 123 nelemFUNC, //# 124 // for any array returning Int array shapeFUNC, //# 125 // for String strlengthFUNC, //# 126 returning Int upcaseFUNC, //# 127 returning String downcaseFUNC, //# 128 returning String capitalizeFUNC, //# 129 returning String sreverseFUNC, //# 130 trimFUNC, //# 131 returning String ltrimFUNC, //# 132 returning String rtrimFUNC, //# 133 returning String substrFUNC, //# 134 returning String replaceFUNC, //# 135 returning String regexFUNC, //# 136 returning TaqlRegex patternFUNC, //# 137 returning TaqlRegex sqlpatternFUNC, //# 138 returning TaqlRegex // for Date datetimeFUNC, //# 139 returning Date mjdtodateFUNC, //# 140 returning Date mjdFUNC, //# 141 returning Double dateFUNC, //# 142 returning Date timeFUNC, //# 143 returning Double (in radians) yearFUNC, //# 144 returning Int monthFUNC, //# 145 returning Int dayFUNC, //# 146 returning Int cmonthFUNC, //# 147 returning String weekdayFUNC, //# 148 returning Int cdowFUNC, //# 149 returning String weekFUNC, //# 150 returning Int ctodFUNC, //# 151 returning String cdateFUNC, //# 152 returning String ctimeFUNC, //# 153 returning String // return values as strings stringFUNC, //# 154 // return angles as hms strings hmsFUNC, //# 155 // return angles as dms strings dmsFUNC, //# 156 // return angles as hms/dms strings hdmsFUNC, //# 157 // special function returning a random Double number randFUNC, //# 158 // special function returning Int row number rownrFUNC, //# 159 // special function returning Int row id (meant for GIVING) rowidFUNC, //# 160 // special function resembling if statement iifFUNC, //# 161 // angular distance returning radians angdistFUNC, //# 162 angdistxFUNC, //# 163 // cone search functions, implemented in derived class conesFUNC, //# 164 cones3FUNC, //# 165 anyconeFUNC, //# 166 anycone3FUNC, //# 167 findconeFUNC, //# 168 findcone3FUNC, //# 169 // normalize angle between -pi and pi normangleFUNC, //# 170 // for Int, Double, Complex or String returning Bool boolFUNC, //# 171 // masked array functions nullarrayFUNC, //# 172 marrayFUNC, //# 173 arrdataFUNC, //# 174 arrmaskFUNC, //# 175 negatemaskFUNC, //# 176 replmaskedFUNC, //# 177 replunmaskedFUNC, //# 178 arrflatFUNC, //# 179 //# AGGREGATE functions must be the last ones. FirstAggrFunc, //# 180 countallFUNC = FirstAggrFunc, gcountFUNC, gfirstFUNC, glastFUNC, //# Grouping doing aggregation on the fly; reducing to a scalar per group gminFUNC, //# 184 gmaxFUNC, gsumFUNC, gproductFUNC, gsumsqrFUNC, gmeanFUNC, gvariance0FUNC, gvariance1FUNC, gstddev0FUNC, gstddev1FUNC, grmsFUNC, ganyFUNC, gallFUNC, gntrueFUNC, gnfalseFUNC, //# Grouping doing aggregation on the fly; reducing to an array per group FirstAggrArrayFunc,//# 199 gminsFUNC = FirstAggrArrayFunc, gmaxsFUNC, gsumsFUNC, gproductsFUNC, gsumsqrsFUNC, gmeansFUNC, gvariances0FUNC, gvariances1FUNC, gstddevs0FUNC, gstddevs1FUNC, grmssFUNC, ganysFUNC, gallsFUNC, gntruesFUNC, gnfalsesFUNC, LastAggrArrayFunc, //# 214 ghistFUNC = LastAggrArrayFunc, //# Grouping requiring aggregation of rows when getting result gaggrFUNC, //# 215 growidFUNC, gmedianFUNC, gfractileFUNC, gexpridFUNC, //# special function (can be inserted by TableParse) NRFUNC //# 220 should be last }; // Constructor TableExprFuncNode (FunctionType, NodeDataType, ValueType, const TableExprNodeSet& source, const vector& nodes, const Block& dtypeOper, const TableExprInfo& = TableExprInfo()); // Destructor ~TableExprFuncNode (); // 'get' Functions to get the desired result of a function // Bool getBool (const TableExprId& id); Int64 getInt (const TableExprId& id); Double getDouble (const TableExprId& id); DComplex getDComplex (const TableExprId& id); String getString (const TableExprId& id); TaqlRegex getRegex (const TableExprId& id); MVTime getDate (const TableExprId& id); // // Check the data and value types of the operands. // It sets the exptected data and value types of the operands. // Set the value type of the function result and returns // the data type of the function result. static NodeDataType checkOperands (Block& dtypeOper, ValueType& resVT, Block& vtypeOper, FunctionType, std::vector&); // Fill the result unit in the node. // Adapt the children nodes if their units need to be converted. // It returns a possible scale factor in case result unit is SI (for sqrt). void fillUnits(); // Link the children to the node and convert the children // to constants if possible. void fillChildNodes (const vector& nodes, const Block& dtypeOper); // Get possible unit scale factor (needed for sqrt). Double getScale() const { return scale_p; } // Some functions to be used by TableExprNodeFuncArray. // const std::vector& operands() const { return operands_p; } std::vector& rwOperands() { return operands_p; } FunctionType funcType() const { return funcType_p; } NodeDataType argDataType() const { return argDataType_p; } // // Get the possible print format, width, and/or precision. static void getPrintFormat (String& fmt, Int& width, Int& prec, const std::vector& operands, const TableExprId& id); // Convert the date and/or time to a string. // static String stringDT (const MVTime& dt, Int prec, MVTime::formatTypes); static String stringDateTime (const MVTime& dt, Int prec); static String stringDate (const MVTime& dt); static String stringTime (const MVTime& dt, Int prec); // // Convert a value to a string. // If fmt is empty, ostringstream is used. // Otherwise the printf-like format is used. // If possible, a double value is converted to radians if formatted as angle. // static String stringValue (Bool val, const String& fmt, Int width); static String stringValue (Int64 val, const String& fmt, Int width); static String stringValue (Double val, const String& fmt, Int width, Int prec, const std::pair& mvFormat, const Unit& unit); static String stringValue (const DComplex& val, const String& fmt, Int width, Int prec); static String stringValue (const String& val, const String& fmt, Int width); static String stringValue (const MVTime& val, const String& fmt, Int width, const std::pair& mvFormat); // Convert angle to a string (hms or dms). // static String stringAngle (double val, Int prec, MVAngle::formatTypes type); static String stringHMS (double val, Int prec); static String stringDMS (double val, Int prec); // // Get the MVTime/Angle format and optional precision. // 0,0 is returned if empty or unknown format. static std::pair getMVFormat (const String& fmt); // Get the angular distance between two positions on a sphere. static double angdist (double ra1, double dec1, double ra2, double dec2) { return acos (sin(dec1)*sin(dec2) + cos(dec1)*cos(dec2)*cos(ra1-ra2)); } // Read a string as an integer, double, complex or bool. static Int64 string2Int (const String&); static Double string2Real (const String&); static DComplex string2Complex (const String&); static Bool string2Bool (const String&); private: // Try if the function gives a constant result. // If so, set the expression type to Constant. void tryToConst(); // Make the units of nodes from starg till endarg // equal. Return the unit found. static const Unit& makeEqualUnits (std::vector& nodes, uInt starg, uInt endarg); //# Data members. FunctionType funcType_p; // which function NodeDataType argDataType_p; // common argument data type Double scale_p; // possible scaling for unit conversion // (needed for sqrt) Table table_p; // table (for iscolumn and iskeyword) }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/ExprFuncNodeArray.cc000066400000000000000000003075371476623553700212640ustar00rootroot00000000000000//# ExprFuncNodeArray.cc: Class representing an array function in table select expression //# Copyright (C) 2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Helper function to fill an array from another one. template void TEFNAFillArray (Array& res, Array arr) { Bool delRes, delArr; T* resd = res.getStorage (delRes); const T* arrd = arr.getStorage (delArr); size_t j=0; size_t arrsz = arr.size(); size_t n = res.size(); for (size_t i=0; i= arrsz) { j = 0; } } res.putStorage (resd, delRes); arr.freeStorage (arrd, delArr); } template void TEFNAiifExec (const Bool* cond, const T* left, int incrLeft, const T* right, int incrRight, T* result, size_t nr) { size_t p1 = 0; size_t p2 = 0; for (size_t i=0; i MArray TEFNAiifAS (Bool useArray, const MArray& arr, const TENShPtr& node, const TableExprId& id) { if (useArray || arr.isNull()) return arr; // Use the scalar by expanding it to an (unmasked) array. Array res(arr.shape()); T val; node->get(id, val); res = val; return MArray(res); } // Result mask is True if cond.mask=True, otherwise mask1 or mask2. // Result is null if one of the operands is null. Only if condition // is scalar and operands are arrays, the result might be non-null. template MArray TEFNAiif (const vector& operands, const TableExprId& id) { // If the condition is a scalar, one or both operands is an array. if (operands[0]->valueType() == TableExprNodeRep::VTScalar) { Bool valc = operands[0]->getBool(id); MArray values; // If an operand is a scalar, return array or scalar expanded to array. // Note that the evaluation of the scalar operand is only done if needed. if (operands[1]->valueType() == TableExprNodeRep::VTScalar) { operands[2]->get(id, values); return TEFNAiifAS (!valc, values, operands[1], id); } else if (operands[2]->valueType() == TableExprNodeRep::VTScalar) { operands[1]->get(id, values); return TEFNAiifAS (valc, values, operands[2], id); } else if (valc) { operands[1]->get(id, values); return values; } else { operands[2]->get(id, values); return values; } } // The condition is an array. The operands can be scalar or array. // Arrays can have masks. // First get the condition array and a pointer to its data. MArray arrc (operands[0]->getArrayBool(id)); if (arrc.isNull()) { return MArray(); } Bool deleteArrc, deleteArr1, deleteArr2, deleteRes; const Bool* datac = arrc.array().getStorage (deleteArrc); IPosition shp (arrc.shape()); size_t nr = arrc.size(); // The operands can be array or scalar. // So use a pointer and increment that can deal with either of them. MArray arr1, arr2; T val1, val2; const T* data1 = &val1; const T* data2 = &val2; size_t incr1 = 1; size_t incr2 = 1; // Set initially that the operands do not have a mask. Bool hasMask = False; Bool isNull = False; // Get the values. If array, check if shape matches. if (operands[1]->valueType() == TableExprNodeRep::VTScalar) { operands[1]->get(id, val1); incr1 = 0; } else { operands[1]->get(id, arr1); if (arr1.isNull()) isNull = True; if (! shp.isEqual (arr1.shape())) { throw TableInvExpr ("TableExprFuncNodeArray::get, " "array shapes mismatch in function IIF"); } data1 = arr1.array().getStorage (deleteArr1); hasMask = hasMask || arr1.hasMask(); } if (operands[2]->valueType() == TableExprNodeRep::VTScalar) { operands[2]->get(id, val2); incr2 = 0; } else { operands[2]->get(id, arr2); if (arr2.isNull()) isNull = True; if (! shp.isEqual (arr2.shape())) { throw TableInvExpr ("TableExprFuncNodeArray::get, " "array shapes mismatch in function IIF"); } data2 = arr2.array().getStorage (deleteArr2); hasMask = hasMask || arr2.hasMask(); } if (isNull) { return MArray(); } Array result(shp); T* res = result.getStorage (deleteRes); TEFNAiifExec (datac, data1, incr1, data2, incr2, res, nr); arrc.array().freeStorage (datac, deleteArrc); if (data1 != &val1) arr1.array().freeStorage (data1, deleteArr1); if (data2 != &val2) arr2.array().freeStorage (data2, deleteArr2); result.putStorage (res, deleteRes); if (!hasMask) { return MArray(result, arrc.mask()); } // The operands have a mask, so combine them in the same way as the values. Bool valMask1 = False; Bool valMask2 = False; const Bool* valm1 = &valMask1; const Bool* valm2 = &valMask2; incr1 = 0; incr2 = 0; if (arr1.hasMask()) { valm1 = arr1.mask().getStorage (deleteArr1); incr1 = 1; } if (arr2.hasMask()) { valm2 = arr2.mask().getStorage (deleteArr2); incr2 = 1; } Array resMask(shp); Bool* resm = resMask.getStorage (deleteRes); TEFNAiifExec (datac, valm1, incr1, valm2, incr2, resm, arrc.size()); if (valm1 != &valMask1) arr1.mask().freeStorage (valm1, deleteArr1); if (valm2 != &valMask2) arr2.mask().freeStorage (valm2, deleteArr2); result.putStorage (res, deleteRes); MArray mares (result, resMask); return MArray (result, mares.combineMask (arrc)); } template MArray TEFMASKneg (const MArray& arr) { if (arr.isNull()) { return arr; } else if (!arr.hasMask()) { return MArray (arr.array(), Array(arr.shape(), True)); } return MArray (arr.array(), !arr.mask()); } template MArray TEFMASKrepl (const MArray& arr, const TENShPtr& operand2, const TableExprId& id, Bool maskValue) { MArray res(arr); MArray arr2; T val2; T* data1; const T* data2 = &val2; const Bool* mask1; size_t incr2 = 0; Bool del1, del2, delm; if (operand2->valueType() == TableExprNodeRep::VTScalar) { operand2->get(id, val2); } else { operand2->get(id, arr2); if (arr2.isNull()) { return MArray(); } if (! arr.shape().isEqual (arr2.shape())) { throw TableInvExpr ("TableExprFuncNodeArray::get, array shapes " "mismatch in function REPLACE(UN)MASKED"); } data2 = arr2.array().getStorage (del2); incr2 = 1; } if (!arr.hasMask()) { if (!maskValue) { if (incr2 == 0) { res.array() = val2; } else { res.array() = arr2.array(); } } } else { data1 = res.array().getStorage (del1); mask1 = arr.mask().getStorage (delm); for (size_t i=0; i 0) { arr2.array().freeStorage (data2, del2); } } return res; } // Helper function to resize an array. template MArray TableExprFuncNodeArray::TEFResize (const MArray& arr, const TableExprId& id) { IPosition shp = adjustShape (getArrayShape(id), arr.shape()); const IPosition& alt = getAlternate(id); if (alt.empty()) { Array res(shp, T()); res.copyMatchingPart (arr.array()); if (arr.hasMask()) { Array resm(shp, False); resm.copyMatchingPart (arr.mask()); return MArray(res, resm); } return MArray(res); } Array res(shp); expandArray (res, arr.array(), alt); if (arr.hasMask()) { Array resm(shp); expandArray (resm, arr.mask(), alt); return MArray(res, resm); } return MArray(res); } TableExprFuncNodeArray::TableExprFuncNodeArray (TableExprFuncNode::FunctionType ftype, NodeDataType dtype, ValueType vtype, const TableExprNodeSet& source, const vector& nodes, const Block& dtypeOper, const TaQLStyle& style) : TableExprNodeArray (dtype, OtFunc), node_p (ftype, dtype, vtype, source, nodes, dtypeOper), origin_p (style.origin()), isCOrder_p (style.isCOrder()), constAxes_p (False), constAlt_p (False) { exprtype_p = node_p.exprType(); unit_p = node_p.unit(); tryToConst(); } TableExprFuncNodeArray::~TableExprFuncNodeArray() {} void TableExprFuncNodeArray::flattenTree (std::vector& nodes) { nodes.push_back (this); node_p.flattenTree (nodes); } void TableExprFuncNodeArray::tryToConst() { Int axarg = 1; switch (funcType()) { case TableExprFuncNode::shapeFUNC: if (operands()[0]->ndim() == 0 || operands()[0]->shape().size() > 0 ) { exprtype_p = Constant; } break; case TableExprFuncNode::arrfractilesFUNC: axarg = 2; CASACORE_FALLTHROUGH; case TableExprFuncNode::arrsumsFUNC: case TableExprFuncNode::arrproductsFUNC: case TableExprFuncNode::arrsumsqrsFUNC: case TableExprFuncNode::arrminsFUNC: case TableExprFuncNode::arrmaxsFUNC: case TableExprFuncNode::arrmeansFUNC: case TableExprFuncNode::arrvariances0FUNC: case TableExprFuncNode::arrvariances1FUNC: case TableExprFuncNode::arrstddevs0FUNC: case TableExprFuncNode::arrstddevs1FUNC: case TableExprFuncNode::arravdevsFUNC: case TableExprFuncNode::arrrmssFUNC: case TableExprFuncNode::arrmediansFUNC: case TableExprFuncNode::arranysFUNC: case TableExprFuncNode::arrallsFUNC: case TableExprFuncNode::arrntruesFUNC: case TableExprFuncNode::arrnfalsesFUNC: case TableExprFuncNode::areverseFUNC: if (operands()[axarg]->isConstant()) { ipos_p = getAxes (0, -1, axarg); constAxes_p = True; } break; case TableExprFuncNode::diagonalFUNC: if (operands()[axarg]->isConstant()) { getDiagonalArg (0, IPosition()); constAxes_p = True; } break; case TableExprFuncNode::resizeFUNC: if (operands().size() < 3 || operands()[2]->isConstant()) { getAlternate (0); constAlt_p = True; } // fall through case TableExprFuncNode::arrayFUNC: if (operands()[axarg]->isConstant()) { getArrayShape (0, axarg); // fills ipos_p constAxes_p = True; } break; case TableExprFuncNode::transposeFUNC: if (operands()[axarg]->isConstant()) { ipos_p = getAxes (0, -1, axarg, False); constAxes_p = True; } break; case TableExprFuncNode::nullarrayFUNC: exprtype_p = Constant; break; case TableExprFuncNode::marrayFUNC: case TableExprFuncNode::arrdataFUNC: case TableExprFuncNode::arrmaskFUNC: case TableExprFuncNode::negatemaskFUNC: case TableExprFuncNode::replmaskedFUNC: case TableExprFuncNode::replunmaskedFUNC: case TableExprFuncNode::arrflatFUNC: if (operands()[0]->valueType() == VTScalar) { ipos_p = IPosition(1,1); constAxes_p = True; } break; default: break; } } IPosition TableExprFuncNodeArray::getAxes (const TableExprId& id, Int ndim, uInt axarg, bool swapRemove) { // Get the axes if not constant (or not known). if (!constAxes_p) { Array ax(operands()[axarg]->getArrayInt(id).array()); AlwaysAssert (ax.ndim() == 1, AipsError); AlwaysAssert (ax.contiguousStorage(), AipsError); ipos_p.resize (ax.size()); for (uInt i=0; i ax(operands()[axarg]->getArrayInt(id).array()); AlwaysAssert (ax.ndim() == 1, AipsError); AlwaysAssert (ax.contiguousStorage(), AipsError); uInt ndim = ax.size(); ipos_p.resize (ndim); if (isCOrder_p) { for (uInt i=0; i ax(operands()[1]->getArrayInt(id).array()); AlwaysAssert (ax.ndim() == 1, AipsError); AlwaysAssert (ax.contiguousStorage(), AipsError); if (ax.size() > 0) { ipos_p.resize (2); ipos_p[0] = ax.data()[0] - origin_p; // firstAxis ipos_p[1] = 0; if (ax.size() > 1) { ipos_p[1] = ax.data()[1]; // diag } iposN_p = ipos_p; } } // If there is a real array, check the arguments. if (shp.size() > 0) { // Check the arguments and reverse C-order if needed. // The diagonals are taken for two consecutive axes. // If the axes are given in C-order, the user has given the last axis, // so we have to subtract one extra. // Use defaults if no arguments given. if (iposN_p.empty()) { ipos_p.resize (2); ipos_p[0] = ipos_p[1] = 0; } else if (isCOrder_p) { ipos_p[0] = shp.size() - iposN_p[0] - 2; } if (ipos_p[0] < 0 || ipos_p[0] >= Int(shp.size())-1) { throw TableInvExpr ("Diagonals axes outside array with ndim=" + String::toString(shp.size())); } if (shp[ipos_p[0]] != shp[ipos_p[0]+1]) { throw TableInvExpr ("Diagonals axis " + String::toString(ipos_p[0]) + " and " + String::toString(ipos_p[0]+1) + " should have equal length"); } // Set offset to last one if exceeding. if (abs(ipos_p[1]) > shp[ipos_p[0]] - 1) { ipos_p[1] = shp[ipos_p[0]] - 1; if (iposN_p[1] < 0) { ipos_p[1] = -ipos_p[1]; } } } return ipos_p; } const IPosition& TableExprFuncNodeArray::getAlternate (const TableExprId& id) { // Only do it if not constant or known. if (! constAlt_p) { if (operands().size() < 3) { expandAlt_p = IPosition(); // normal resize } else { if (operands()[2]->valueType() == VTScalar) { // A scalar is true for all axes. // The dimensionality is unknown, so make it very large to cover all. expandAlt_p = IPosition(20, operands()[2]->getInt(id)); } else { Array arr(operands()[2]->getArrayInt(id).array()); expandAlt_p.resize (arr.size()); if (isCOrder_p) { for (uInt i=0; i TableExprFuncNodeArray::angdistx (const MArray& a1, const MArray& a2) const { Array::const_iterator end1 = a1.array().end(); Array::const_iterator end2 = a2.array().end(); Array result(IPosition(2, a1.size()/2, a2.size()/2)); Double* res = result.data(); for (Array::const_iterator p2 = a2.array().begin(); p2!=end2; ++p2) { Double ra2 = *p2; ++p2; Double sindec2 = sin(*p2); Double cosdec2 = cos(*p2); for (Array::const_iterator p1 = a1.array().begin(); p1!=end1; ++p1) { Double ra1 = *p1; ++p1; *res++ = acos (sin(*p1)*sindec2 + cos(*p1)*cosdec2*cos(ra1-ra2)); } } return MArray(result, a1.combineMask(a2)); } MArray TableExprFuncNodeArray::getArrayBool (const TableExprId& id) { switch (funcType()) { case TableExprFuncNode::boolFUNC: if (operands()[0]->dataType() == NTBool) { return operands()[0]->getArrayBool(id); } else if (operands()[0]->dataType() == NTInt) { return (operands()[0]->getArrayInt(id) != Int64(0)); } else if (operands()[0]->dataType() == NTDouble) { return (operands()[0]->getArrayDouble(id) != 0.); } else if (operands()[0]->dataType() == NTComplex) { return (operands()[0]->getArrayDComplex(id) != DComplex()); } else if (operands()[0]->dataType() == NTDate) { return (operands()[0]->getArrayDouble(id) != 0.); } else { MArray values = operands()[0]->getArrayString(id); Array res(values.shape()); Array::const_iterator in = values.array().begin(); for (Array::contiter out=res.cbegin(); out!=res.cend(); ++out, ++in) { *out = TableExprFuncNode::string2Bool (*in); } return MArray (res, values); } case TableExprFuncNode::near2FUNC: if (argDataType() == NTDouble) { if (operands()[0]->valueType() == VTScalar) { return near (operands()[0]->getDouble(id), operands()[1]->getArrayDouble(id), 1.0e-13); } else if (operands()[1]->valueType() == VTScalar) { return near (operands()[0]->getArrayDouble(id), operands()[1]->getDouble(id), 1.0e-13); } else { return near (operands()[0]->getArrayDouble(id), operands()[1]->getArrayDouble(id), 1.0e-13); } } if (operands()[0]->valueType() == VTScalar) { return near (operands()[0]->getDComplex(id), operands()[1]->getArrayDComplex(id), 1.0e-13); } else if (operands()[1]->valueType() == VTScalar) { return near (operands()[0]->getArrayDComplex(id), operands()[1]->getDComplex(id), 1.0e-13); } else { return near (operands()[0]->getArrayDComplex(id), operands()[1]->getArrayDComplex(id), 1.0e-13); } case TableExprFuncNode::near3FUNC: if (argDataType() == NTDouble) { if (operands()[0]->valueType() == VTScalar) { return near (operands()[0]->getDouble(id), operands()[1]->getArrayDouble(id), operands()[2]->getDouble(id)); } else if (operands()[1]->valueType() == VTScalar) { return near (operands()[0]->getArrayDouble(id), operands()[1]->getDouble(id), operands()[2]->getDouble(id)); } else { return near (operands()[0]->getArrayDouble(id), operands()[1]->getArrayDouble(id), operands()[2]->getDouble(id)); } } if (operands()[0]->valueType() == VTScalar) { return near (operands()[0]->getDComplex(id), operands()[1]->getArrayDComplex(id), operands()[2]->getDouble(id)); } else if (operands()[1]->valueType() == VTScalar) { return near (operands()[0]->getArrayDComplex(id), operands()[1]->getDComplex(id), operands()[2]->getDouble(id)); } else { return near (operands()[0]->getArrayDComplex(id), operands()[1]->getArrayDComplex(id), operands()[2]->getDouble(id)); } case TableExprFuncNode::nearabs2FUNC: if (argDataType() == NTDouble) { if (operands()[0]->valueType() == VTScalar) { return nearAbs (operands()[0]->getDouble(id), operands()[1]->getArrayDouble(id), 1.0e-13); } else if (operands()[1]->valueType() == VTScalar) { return nearAbs (operands()[0]->getArrayDouble(id), operands()[1]->getDouble(id), 1.0e-13); } else { return nearAbs (operands()[0]->getArrayDouble(id), operands()[1]->getArrayDouble(id), 1.0e-13); } } if (operands()[0]->valueType() == VTScalar) { return nearAbs (operands()[0]->getDComplex(id), operands()[1]->getArrayDComplex(id), 1.0e-13); } else if (operands()[1]->valueType() == VTScalar) { return nearAbs (operands()[0]->getArrayDComplex(id), operands()[1]->getDComplex(id), 1.0e-13); } else { return nearAbs (operands()[0]->getArrayDComplex(id), operands()[1]->getArrayDComplex(id), 1.0e-13); } case TableExprFuncNode::nearabs3FUNC: if (argDataType() == NTDouble) { if (operands()[0]->valueType() == VTScalar) { return nearAbs (operands()[0]->getDouble(id), operands()[1]->getArrayDouble(id), operands()[2]->getDouble(id)); } else if (operands()[1]->valueType() == VTScalar) { return nearAbs (operands()[0]->getArrayDouble(id), operands()[1]->getDouble(id), operands()[2]->getDouble(id)); } else { return nearAbs (operands()[0]->getArrayDouble(id), operands()[1]->getArrayDouble(id), operands()[2]->getDouble(id)); } } if (operands()[0]->valueType() == VTScalar) { return nearAbs (operands()[0]->getDComplex(id), operands()[1]->getArrayDComplex(id), operands()[2]->getDouble(id)); } else if (operands()[1]->valueType() == VTScalar) { return nearAbs (operands()[0]->getArrayDComplex(id), operands()[1]->getDComplex(id), operands()[2]->getDouble(id)); } else { return nearAbs (operands()[0]->getArrayDComplex(id), operands()[1]->getArrayDComplex(id), operands()[2]->getDouble(id)); } case TableExprFuncNode::arranysFUNC: { MArray arr (operands()[0]->getArrayBool(id)); return partialAnys (arr, getAxes(id, arr.ndim())); } case TableExprFuncNode::arrallsFUNC: { MArray arr (operands()[0]->getArrayBool(id)); return partialAlls (arr, getAxes(id, arr.ndim())); } case TableExprFuncNode::runallFUNC: { MArray arr (operands()[0]->getArrayBool(id)); return slidingAlls (arr, getArrayShape(id)); } case TableExprFuncNode::runanyFUNC: { MArray arr (operands()[0]->getArrayBool(id)); return slidingAnys (arr, getArrayShape(id)); } case TableExprFuncNode::boxallFUNC: { MArray arr (operands()[0]->getArrayBool(id)); return boxedAlls (arr, getArrayShape(id)); } case TableExprFuncNode::boxanyFUNC: { MArray arr (operands()[0]->getArrayBool(id)); return boxedAnys (arr, getArrayShape(id)); } case TableExprFuncNode::arrayFUNC: { IPosition shp (getArrayShape(id)); Array res(shp); Array mask; if (operands()[0]->valueType() == VTScalar) { res = operands()[0]->getBool(id); } else { MArray arr (operands()[0]->getArrayBool(id)); if (arr.isNull()) { return arr; } TEFNAFillArray (res, arr.array()); if (arr.hasMask()) { mask.resize (shp); TEFNAFillArray (mask, arr.mask()); } } return MArray (res, mask); } case TableExprFuncNode::transposeFUNC: { MArray arr (operands()[0]->getArrayBool(id)); return reorderArray (arr, getOrder(id, arr.ndim()), False); } case TableExprFuncNode::areverseFUNC: { MArray arr (operands()[0]->getArrayBool(id)); return reverseArray (arr, getReverseAxes(id, arr.ndim())); } case TableExprFuncNode::diagonalFUNC: { MArray arr (operands()[0]->getArrayBool(id)); if (arr.isNull()) { return arr; } const IPosition parms = getDiagonalArg (id, arr.shape()); if (arr.hasMask()) { return MArray(arr.array().diagonals(parms[0], parms[1]), arr.mask().diagonals(parms[0], parms[1])); } return MArray(arr.array().diagonals(parms[0], parms[1])); } case TableExprFuncNode::resizeFUNC: return TEFResize (operands()[0]->getArrayBool(id), id); case TableExprFuncNode::isnanFUNC: if (argDataType() == NTComplex) { return isNaN (operands()[0]->getArrayDComplex(id)); } else { return isNaN (operands()[0]->getArrayDouble(id)); } case TableExprFuncNode::isinfFUNC: if (argDataType() == NTComplex) { return isInf (operands()[0]->getArrayDComplex(id)); } else { return isInf (operands()[0]->getArrayDouble(id)); } case TableExprFuncNode::isfiniteFUNC: if (argDataType() == NTComplex) { return isFinite (operands()[0]->getArrayDComplex(id)); } else { return isFinite (operands()[0]->getArrayDouble(id)); } case TableExprFuncNode::iifFUNC: return TEFNAiif (operands(), id); case TableExprFuncNode::nullarrayFUNC: return MArray(); case TableExprFuncNode::marrayFUNC: return MArray (operands()[0]->getBoolAS(id), operands()[1]->getBoolAS(id)); case TableExprFuncNode::arrdataFUNC: { MArray arr(operands()[0]->getBoolAS(id).array()); return arr.isNull() ? arr : MArray (arr.array()); } case TableExprFuncNode::negatemaskFUNC: return TEFMASKneg (operands()[0]->getBoolAS(id)); case TableExprFuncNode::replmaskedFUNC: return TEFMASKrepl (operands()[0]->getBoolAS(id), operands()[1], id, True); case TableExprFuncNode::replunmaskedFUNC: return TEFMASKrepl (operands()[0]->getBoolAS(id), operands()[1], id, False); case TableExprFuncNode::arrflatFUNC: return MArray (operands()[0]->getBoolAS(id).flatten()); case TableExprFuncNode::arrmaskFUNC: { IPosition shp; Bool isNull = False; switch (operands()[0]->dataType()) { case NTBool: { MArray arr (operands()[0]->getBoolAS(id).mask()); if (arr.hasMask()) { return MArray (arr.mask()); } shp = arr.shape(); isNull = arr.isNull(); break; } case NTInt: { MArray arr (operands()[0]->getIntAS(id)); if (arr.hasMask()) { return MArray (arr.mask()); } shp = arr.shape(); isNull = arr.isNull(); break; } case NTDouble: { MArray arr (operands()[0]->getDoubleAS(id)); if (arr.hasMask()) { return MArray (arr.mask()); } shp = arr.shape(); isNull = arr.isNull(); break; } case NTComplex: { MArray arr (operands()[0]->getDComplexAS(id)); if (arr.hasMask()) { return MArray (arr.mask()); } shp = arr.shape(); isNull = arr.isNull(); break; } case NTString: { MArray arr (operands()[0]->getStringAS(id)); if (arr.hasMask()) { return MArray (arr.mask()); } shp = arr.shape(); isNull = arr.isNull(); break; } case NTDate: { MArray arr (operands()[0]->getDateAS(id)); if (arr.hasMask()) { return MArray (arr.mask()); } shp = arr.shape(); isNull = arr.isNull(); break; } default: throw TableInvExpr ("TableExprFuncNodeArray::getArrayBool, " "unknown datatype in mask function"); } if (isNull) { return MArray(); } Array mask(shp); mask = False; return MArray (mask); } default: break; } throw TableInvExpr ("TableExprFuncNodeArray::getArrayBool, " "unknown function " + String::toString(funcType())); } MArray TableExprFuncNodeArray::getArrayInt (const TableExprId& id) { switch (funcType()) { case TableExprFuncNode::squareFUNC: case TableExprFuncNode::normFUNC: return square (operands()[0]->getArrayInt(id)); case TableExprFuncNode::cubeFUNC: return cube (operands()[0]->getArrayInt(id)); case TableExprFuncNode::absFUNC: return abs (operands()[0]->getArrayInt(id)); case TableExprFuncNode::intFUNC: if (operands()[0]->dataType() == NTString) { MArray values (operands()[0]->getArrayString(id)); Array res(values.shape()); Array::const_iterator in = values.array().begin(); for (Array::contiter out=res.cbegin(); out!=res.cend(); ++out, ++in) { *out = TableExprFuncNode::string2Int (*in); } return MArray (res, values); } else if (operands()[0]->dataType() == NTBool) { MArray values (operands()[0]->getArrayBool(id)); Array res(values.shape()); Array::const_iterator in = values.array().begin(); for (Array::contiter out=res.cbegin(); out!=res.cend(); ++out, ++in) { *out = *in ? 1:0; } return MArray (res, values); } else if (argDataType() == NTDouble) { MArray val (operands()[0]->getArrayDouble(id)); Array arr(val.shape()); convertArray (arr, val.array()); return MArray (arr, val); } return operands()[0]->getArrayInt(id); case TableExprFuncNode::signFUNC: return sign(operands()[0]->getArrayInt(id)); case TableExprFuncNode::roundFUNC: case TableExprFuncNode::floorFUNC: case TableExprFuncNode::ceilFUNC: return operands()[0]->getArrayInt(id); case TableExprFuncNode::shapeFUNC: { IPosition shp (operands()[0]->shape(id)); Int n = shp.size(); Array result(IPosition(1,n)); Int64* res = result.data(); if (isCOrder_p) { for (Int i=0; i(result); } case TableExprFuncNode::strlengthFUNC: { MArray values (operands()[0]->getArrayString(id)); Array res(values.shape()); Bool deleteVal, deleteRes; const String* val = values.array().getStorage (deleteVal); Int64* resp = res.getStorage (deleteRes); size_t n = values.size(); for (size_t i=0; i (res, values); } case TableExprFuncNode::yearFUNC: case TableExprFuncNode::monthFUNC: case TableExprFuncNode::dayFUNC: case TableExprFuncNode::weekdayFUNC: case TableExprFuncNode::weekFUNC: { MArray values (operands()[0]->getArrayDate(id)); Array res(values.shape()); Bool deleteVal, deleteRes; const MVTime* val = values.array().getStorage (deleteVal); Int64* resp = res.getStorage (deleteRes); size_t n = values.size(); switch (funcType()) { case TableExprFuncNode::yearFUNC: for (size_t i=0; i (res, values); } case TableExprFuncNode::minFUNC: if (operands()[0]->valueType() == VTScalar) { return min (operands()[1]->getArrayInt(id), operands()[0]->getInt(id)); } else if (operands()[1]->valueType() == VTScalar) { return min (operands()[0]->getArrayInt(id), operands()[1]->getInt(id)); } else { return min (operands()[0]->getArrayInt(id), operands()[1]->getArrayInt(id)); } case TableExprFuncNode::maxFUNC: if (operands()[0]->valueType() == VTScalar) { return max (operands()[1]->getArrayInt(id), operands()[0]->getInt(id)); } else if (operands()[1]->valueType() == VTScalar) { return max (operands()[0]->getArrayInt(id), operands()[1]->getInt(id)); } else { return max (operands()[0]->getArrayInt(id), operands()[1]->getArrayInt(id)); } case TableExprFuncNode::fmodFUNC: if (operands()[0]->valueType() == VTScalar) { return operands()[0]->getInt(id) % operands()[1]->getArrayInt(id); } else if (operands()[1]->valueType() == VTScalar) { return operands()[0]->getArrayInt(id) % operands()[1]->getInt(id); } else { return operands()[0]->getArrayInt(id) % operands()[1]->getArrayInt(id); } case TableExprFuncNode::arrsumsFUNC: { MArray arr (operands()[0]->getArrayInt(id)); return partialSums (arr, getAxes(id, arr.ndim())); } case TableExprFuncNode::arrproductsFUNC: { MArray arr (operands()[0]->getArrayInt(id)); return partialProducts (arr, getAxes(id, arr.ndim())); } case TableExprFuncNode::arrsumsqrsFUNC: { MArray arr (operands()[0]->getArrayInt(id)); return partialSums (arr*arr, getAxes(id, arr.ndim())); } case TableExprFuncNode::arrminsFUNC: { MArray arr (operands()[0]->getArrayInt(id)); return partialMins (arr, getAxes(id, arr.ndim())); } case TableExprFuncNode::arrmaxsFUNC: { MArray arr (operands()[0]->getArrayInt(id)); return partialMaxs (arr, getAxes(id, arr.ndim())); } case TableExprFuncNode::runsumFUNC: { MArray arr (operands()[0]->getArrayInt(id)); return slidingSums (arr, getArrayShape(id)); } case TableExprFuncNode::runproductFUNC: { MArray arr (operands()[0]->getArrayInt(id)); return slidingProducts (arr, getArrayShape(id)); } case TableExprFuncNode::runsumsqrFUNC: { MArray arr (operands()[0]->getArrayInt(id)); return slidingSumSqrs (arr, getArrayShape(id)); } case TableExprFuncNode::runminFUNC: { MArray arr (operands()[0]->getArrayInt(id)); return slidingMins (arr, getArrayShape(id)); } case TableExprFuncNode::runmaxFUNC: { MArray arr (operands()[0]->getArrayInt(id)); return slidingMaxs (arr, getArrayShape(id)); } case TableExprFuncNode::boxsumFUNC: { MArray arr (operands()[0]->getArrayInt(id)); return boxedSums (arr, getArrayShape(id)); } case TableExprFuncNode::boxproductFUNC: { MArray arr (operands()[0]->getArrayInt(id)); return boxedProducts (arr, getArrayShape(id)); } case TableExprFuncNode::boxsumsqrFUNC: { MArray arr (operands()[0]->getArrayInt(id)); return boxedSumSqrs (arr, getArrayShape(id)); } case TableExprFuncNode::boxminFUNC: { MArray arr (operands()[0]->getArrayInt(id)); return boxedMins (arr, getArrayShape(id)); } case TableExprFuncNode::boxmaxFUNC: { MArray arr (operands()[0]->getArrayInt(id)); return boxedMaxs (arr, getArrayShape(id)); } case TableExprFuncNode::arrntruesFUNC: { MArray arr (operands()[0]->getArrayBool(id)); MArray res(partialNTrue (arr, getAxes(id, arr.ndim()))); Array resd(res.shape()); convertArray (resd, res.array()); return MArray (resd, res); } case TableExprFuncNode::runntrueFUNC: { MArray arr (operands()[0]->getArrayBool(id)); MArray res(slidingNTrue (arr, getArrayShape(id))); Array resd(res.shape()); convertArray (resd, res.array()); return MArray (resd, res); } case TableExprFuncNode::boxntrueFUNC: { MArray arr (operands()[0]->getArrayBool(id)); MArray res(boxedNTrue (arr, getArrayShape(id))); Array resd(res.shape()); convertArray (resd, res.array()); return MArray (resd, res); } case TableExprFuncNode::arrnfalsesFUNC: { MArray arr (operands()[0]->getArrayBool(id)); MArray res(partialNFalse (arr, getAxes(id, arr.ndim()))); Array resd(res.shape()); convertArray (resd, res.array()); return MArray (resd, res); } case TableExprFuncNode::runnfalseFUNC: { MArray arr (operands()[0]->getArrayBool(id)); MArray res(slidingNFalse (arr, getArrayShape(id))); Array resd(res.shape()); convertArray (resd, res.array()); return MArray (resd, res); } case TableExprFuncNode::boxnfalseFUNC: { MArray arr (operands()[0]->getArrayBool(id)); MArray res(boxedNFalse (arr, getArrayShape(id))); Array resd(res.shape()); convertArray (resd, res.array()); return MArray (resd, res); } case TableExprFuncNode::arrayFUNC: { IPosition shp (getArrayShape(id)); Array res(shp); Array mask; if (operands()[0]->valueType() == VTScalar) { res = operands()[0]->getInt(id); } else { MArray arr (operands()[0]->getArrayInt(id)); if (arr.isNull()) { return arr; } TEFNAFillArray (res, arr.array()); if (arr.hasMask()) { mask.resize (shp); TEFNAFillArray (mask, arr.mask()); } } return MArray (res, mask); } case TableExprFuncNode::transposeFUNC: { MArray arr (operands()[0]->getArrayInt(id)); return reorderArray (arr, getOrder(id, arr.ndim()), False); } case TableExprFuncNode::areverseFUNC: { MArray arr (operands()[0]->getArrayInt(id)); return reverseArray (arr, getReverseAxes(id, arr.ndim())); } case TableExprFuncNode::diagonalFUNC: { MArray arr (operands()[0]->getArrayInt(id)); if (arr.isNull()) { return arr; } const IPosition parms = getDiagonalArg (id, arr.shape()); if (arr.hasMask()) { return MArray(arr.array().diagonals(parms[0], parms[1]), arr.mask().diagonals(parms[0], parms[1])); } return MArray(arr.array().diagonals(parms[0], parms[1])); } case TableExprFuncNode::resizeFUNC: return TEFResize (operands()[0]->getArrayInt(id), id); case TableExprFuncNode::iifFUNC: return TEFNAiif (operands(), id); case TableExprFuncNode::nullarrayFUNC: return MArray(); case TableExprFuncNode::marrayFUNC: return MArray (operands()[0]->getIntAS(id), operands()[1]->getBoolAS(id)); case TableExprFuncNode::arrdataFUNC: { MArray arr(operands()[0]->getIntAS(id).array()); return arr.isNull() ? arr : MArray (arr.array()); } case TableExprFuncNode::negatemaskFUNC: return TEFMASKneg (operands()[0]->getIntAS(id)); case TableExprFuncNode::replmaskedFUNC: return TEFMASKrepl (operands()[0]->getIntAS(id), operands()[1], id, True); case TableExprFuncNode::replunmaskedFUNC: return TEFMASKrepl (operands()[0]->getIntAS(id), operands()[1], id, False); case TableExprFuncNode::arrflatFUNC: return MArray (operands()[0]->getIntAS(id).flatten()); default: break; } throw TableInvExpr ("TableExprFuncNodeArray::getArrayInt, " "unknown function " + String::toString(funcType())); } MArray TableExprFuncNodeArray::getArrayDouble (const TableExprId& id) { if (dataType() == NTInt) { return TableExprNodeArray::getArrayDouble (id); } // Delta degrees of freedom for variance/stddev. uInt ddof = 1; switch (funcType()) { case TableExprFuncNode::sinFUNC: return sin (operands()[0]->getArrayDouble(id)); case TableExprFuncNode::sinhFUNC: return sinh (operands()[0]->getArrayDouble(id)); case TableExprFuncNode::cosFUNC: return cos (operands()[0]->getArrayDouble(id)); case TableExprFuncNode::coshFUNC: return cosh (operands()[0]->getArrayDouble(id)); case TableExprFuncNode::expFUNC: return exp (operands()[0]->getArrayDouble(id)); case TableExprFuncNode::logFUNC: return log (operands()[0]->getArrayDouble(id)); case TableExprFuncNode::log10FUNC: return log10 (operands()[0]->getArrayDouble(id)); case TableExprFuncNode::squareFUNC: return square (operands()[0]->getArrayDouble(id)); case TableExprFuncNode::cubeFUNC: return cube (operands()[0]->getArrayDouble(id)); case TableExprFuncNode::sqrtFUNC: { MArray res = sqrt (operands()[0]->getArrayDouble(id)); if (node_p.getScale() != 1.) { // Note: in this way arr references the array in res. Array arr(res.array()); arrayTransformInPlace (arr, node_p.getScale(), casacore::Multiplies()); } return res; } case TableExprFuncNode::conjFUNC: return operands()[0]->getArrayDouble(id); case TableExprFuncNode::normFUNC: if (argDataType() == NTDouble) { return square (operands()[0]->getArrayDouble(id)); } else { MArray arr (operands()[0]->getArrayDComplex(id)); Array result(arr.shape()); Bool deleteArr, deleteRes; const DComplex* data = arr.array().getStorage (deleteArr); Double* res = result.getStorage (deleteRes); size_t nr = arr.size(); for (size_t i=0; i (result, arr); } case TableExprFuncNode::absFUNC: if (argDataType() == NTDouble) { return abs (operands()[0]->getArrayDouble(id)); } return amplitude (operands()[0]->getArrayDComplex(id)); case TableExprFuncNode::argFUNC: if (argDataType() == NTDouble) { MArray marr (operands()[0]->getArrayDouble(id)); Array arr(marr.array().copy()); Bool deleteIt; Double* data = arr.getStorage (deleteIt); size_t nr = arr.size(); for (size_t i=0; i= 0) { data[i] = 0; } else { data[i] = M_PI; } } arr.putStorage (data, deleteIt); return MArray (arr, marr); } return phase (operands()[0]->getArrayDComplex(id)); case TableExprFuncNode::realFUNC: if (operands()[0]->dataType() == NTString) { MArray values (operands()[0]->getArrayString(id)); Array res(values.shape()); Array::const_iterator in = values.array().begin(); for (Array::contiter out=res.cbegin(); out!=res.cend(); ++out, ++in) { *out = TableExprFuncNode::string2Real (*in); } return MArray (res, values); } else if (operands()[0]->dataType() == NTBool) { MArray values (operands()[0]->getArrayBool(id)); Array res(values.shape()); Array::const_iterator in = values.array().begin(); for (Array::contiter out=res.cbegin(); out!=res.cend(); ++out, ++in) { *out = *in ? 1:0; } return MArray (res, values); } else if (argDataType() == NTDouble) { return operands()[0]->getArrayDouble(id); } return real (operands()[0]->getArrayDComplex(id)); case TableExprFuncNode::imagFUNC: if (argDataType() == NTDouble) { MArray arr (operands()[0]->getArrayDouble(id)); Array result(arr.shape()); result = 0.; return MArray (result, arr); } return imag (operands()[0]->getArrayDComplex(id)); case TableExprFuncNode::asinFUNC: return asin (operands()[0]->getArrayDouble(id)); case TableExprFuncNode::acosFUNC: return acos (operands()[0]->getArrayDouble(id)); case TableExprFuncNode::atanFUNC: return atan (operands()[0]->getArrayDouble(id)); case TableExprFuncNode::tanFUNC: return tan (operands()[0]->getArrayDouble(id)); case TableExprFuncNode::tanhFUNC: return tanh (operands()[0]->getArrayDouble(id)); case TableExprFuncNode::signFUNC: return sign (operands()[0]->getArrayDouble(id)); case TableExprFuncNode::roundFUNC: return round (operands()[0]->getArrayDouble(id)); case TableExprFuncNode::floorFUNC: return floor (operands()[0]->getArrayDouble(id)); case TableExprFuncNode::ceilFUNC: return ceil (operands()[0]->getArrayDouble(id)); case TableExprFuncNode::mjdFUNC: case TableExprFuncNode::timeFUNC: { MArray values (operands()[0]->getArrayDate(id)); Array doubles(values.shape()); Bool deleteVal, deleteDoub; const MVTime* val = values.array().getStorage (deleteVal); Double* doub = doubles.getStorage (deleteDoub); size_t n = values.size(); if (funcType() == TableExprFuncNode::mjdFUNC) { for (size_t i=0; i (doubles, values); } case TableExprFuncNode::powFUNC: if (operands()[0]->valueType() == VTScalar) { return pow (operands()[0]->getDouble(id), operands()[1]->getArrayDouble(id)); } else if (operands()[1]->valueType() == VTScalar) { return pow (operands()[0]->getArrayDouble(id), operands()[1]->getDouble(id)); } else { return pow (operands()[0]->getArrayDouble(id), operands()[1]->getArrayDouble(id)); } case TableExprFuncNode::minFUNC: if (operands()[0]->valueType() == VTScalar) { return min (operands()[1]->getArrayDouble(id), operands()[0]->getDouble(id)); } else if (operands()[1]->valueType() == VTScalar) { return min (operands()[0]->getArrayDouble(id), operands()[1]->getDouble(id)); } else { return min (operands()[0]->getArrayDouble(id), operands()[1]->getArrayDouble(id)); } case TableExprFuncNode::maxFUNC: if (operands()[0]->valueType() == VTScalar) { return max (operands()[1]->getArrayDouble(id), operands()[0]->getDouble(id)); } else if (operands()[1]->valueType() == VTScalar) { return max (operands()[0]->getArrayDouble(id), operands()[1]->getDouble(id)); } else { return max (operands()[0]->getArrayDouble(id), operands()[1]->getArrayDouble(id)); } case TableExprFuncNode::atan2FUNC: if (operands()[0]->valueType() == VTScalar) { return atan2 (operands()[0]->getDouble(id), operands()[1]->getArrayDouble(id)); } else if (operands()[1]->valueType() == VTScalar) { return atan2 (operands()[0]->getArrayDouble(id), operands()[1]->getDouble(id)); } else { return atan2 (operands()[0]->getArrayDouble(id), operands()[1]->getArrayDouble(id)); } case TableExprFuncNode::fmodFUNC: if (operands()[0]->valueType() == VTScalar) { return fmod (operands()[0]->getDouble(id), operands()[1]->getArrayDouble(id)); } else if (operands()[1]->valueType() == VTScalar) { return fmod (operands()[0]->getArrayDouble(id), operands()[1]->getDouble(id)); } else { return fmod (operands()[0]->getArrayDouble(id), operands()[1]->getArrayDouble(id)); } case TableExprFuncNode::arrsumsFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return partialSums (arr, getAxes(id, arr.ndim())); } case TableExprFuncNode::arrproductsFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return partialProducts (arr, getAxes(id, arr.ndim())); } case TableExprFuncNode::arrsumsqrsFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return partialSums (arr*arr, getAxes(id, arr.ndim())); } case TableExprFuncNode::arrminsFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return partialMins (arr, getAxes(id, arr.ndim())); } case TableExprFuncNode::arrmaxsFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return partialMaxs (arr, getAxes(id, arr.ndim())); } case TableExprFuncNode::arrmeansFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return partialMeans (arr, getAxes(id, arr.ndim())); } case TableExprFuncNode::arrvariances0FUNC: ddof = 0; // fall through case TableExprFuncNode::arrvariances1FUNC: { if (operands()[0]->dataType() == NTComplex) { MArray arr (operands()[0]->getArrayDComplex(id)); return real(partialVariances (arr, getAxes(id, arr.ndim()), ddof)); } MArray arr (operands()[0]->getArrayDouble(id)); return partialVariances (arr, getAxes(id, arr.ndim()), ddof); } case TableExprFuncNode::arrstddevs0FUNC: ddof = 0; // fall through case TableExprFuncNode::arrstddevs1FUNC: { if (operands()[0]->dataType() == NTComplex) { MArray arr (operands()[0]->getArrayDComplex(id)); return real(partialStddevs (arr, getAxes(id, arr.ndim()), ddof)); } MArray arr (operands()[0]->getArrayDouble(id)); return partialStddevs (arr, getAxes(id, arr.ndim()), ddof); } case TableExprFuncNode::arravdevsFUNC: { if (operands()[0]->dataType() == NTComplex) { MArray arr (operands()[0]->getArrayDComplex(id)); return real(partialAvdevs (arr, getAxes(id, arr.ndim()))); } MArray arr (operands()[0]->getArrayDouble(id)); return partialAvdevs (arr, getAxes(id, arr.ndim())); } case TableExprFuncNode::arrrmssFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return partialRmss (arr, getAxes(id, arr.ndim())); } case TableExprFuncNode::arrmediansFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return partialMedians (arr, getAxes(id, arr.ndim())); } case TableExprFuncNode::arrfractilesFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return partialFractiles (arr, getAxes(id, arr.ndim(), 2), operands()[1]->getDouble(id)); } case TableExprFuncNode::runsumFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return slidingSums (arr, getArrayShape(id)); } case TableExprFuncNode::runproductFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return slidingProducts (arr, getArrayShape(id)); } case TableExprFuncNode::runsumsqrFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return slidingSumSqrs (arr, getArrayShape(id)); } case TableExprFuncNode::runminFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return slidingMins (arr, getArrayShape(id)); } case TableExprFuncNode::runmaxFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return slidingMaxs (arr, getArrayShape(id)); } case TableExprFuncNode::runmeanFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return slidingMeans (arr, getArrayShape(id)); } case TableExprFuncNode::runvariance0FUNC: ddof = 0; // fall through case TableExprFuncNode::runvariance1FUNC: { if (operands()[0]->dataType() == NTComplex) { MArray arr (operands()[0]->getArrayDComplex(id)); return real(slidingVariances (arr, getArrayShape(id), ddof)); } MArray arr (operands()[0]->getArrayDouble(id)); return slidingVariances (arr, getArrayShape(id), ddof); } case TableExprFuncNode::runstddev0FUNC: ddof = 0; // fall through case TableExprFuncNode::runstddev1FUNC: { if (operands()[0]->dataType() == NTComplex) { MArray arr (operands()[0]->getArrayDComplex(id)); return real(slidingStddevs (arr, getArrayShape(id), ddof)); } MArray arr (operands()[0]->getArrayDouble(id)); return slidingStddevs (arr, getArrayShape(id), ddof); } case TableExprFuncNode::runavdevFUNC: { if (operands()[0]->dataType() == NTComplex) { MArray arr (operands()[0]->getArrayDComplex(id)); return real(slidingAvdevs (arr, getArrayShape(id))); } MArray arr (operands()[0]->getArrayDouble(id)); return slidingAvdevs (arr, getArrayShape(id)); } case TableExprFuncNode::runrmsFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return slidingRmss (arr, getArrayShape(id)); } case TableExprFuncNode::runmedianFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return slidingMedians (arr, getArrayShape(id)); } case TableExprFuncNode::runfractileFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return slidingFractiles (arr, getArrayShape(id, 2), operands()[1]->getDouble(id)); } case TableExprFuncNode::boxsumFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return boxedSums (arr, getArrayShape(id)); } case TableExprFuncNode::boxproductFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return boxedProducts (arr, getArrayShape(id)); } case TableExprFuncNode::boxsumsqrFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return boxedSumSqrs (arr, getArrayShape(id)); } case TableExprFuncNode::boxminFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return boxedMins (arr, getArrayShape(id)); } case TableExprFuncNode::boxmaxFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return boxedMaxs (arr, getArrayShape(id)); } case TableExprFuncNode::boxmeanFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return boxedMeans (arr, getArrayShape(id)); } case TableExprFuncNode::boxvariance0FUNC: ddof = 0; // fall through case TableExprFuncNode::boxvariance1FUNC: { if (operands()[0]->dataType() == NTComplex) { MArray arr (operands()[0]->getArrayDComplex(id)); return real(boxedVariances (arr, getArrayShape(id), ddof)); } MArray arr (operands()[0]->getArrayDouble(id)); return boxedVariances (arr, getArrayShape(id), ddof); } case TableExprFuncNode::boxstddev0FUNC: ddof = 0; // fall through case TableExprFuncNode::boxstddev1FUNC: { if (operands()[0]->dataType() == NTComplex) { MArray arr (operands()[0]->getArrayDComplex(id)); return real(boxedStddevs (arr, getArrayShape(id), ddof)); } MArray arr (operands()[0]->getArrayDouble(id)); return boxedStddevs (arr, getArrayShape(id), ddof); } case TableExprFuncNode::boxavdevFUNC: { if (operands()[0]->dataType() == NTComplex) { MArray arr (operands()[0]->getArrayDComplex(id)); return real(boxedAvdevs (arr, getArrayShape(id))); } MArray arr (operands()[0]->getArrayDouble(id)); return boxedAvdevs (arr, getArrayShape(id)); } case TableExprFuncNode::boxrmsFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return boxedRmss (arr, getArrayShape(id)); } case TableExprFuncNode::boxmedianFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return boxedMedians (arr, getArrayShape(id)); } case TableExprFuncNode::boxfractileFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return boxedFractiles (arr, getArrayShape(id, 2), operands()[1]->getDouble(id)); } case TableExprFuncNode::arrayFUNC: { IPosition shp (getArrayShape(id)); Array res(shp); Array mask; if (operands()[0]->valueType() == VTScalar) { res = operands()[0]->getDouble(id); } else { MArray arr (operands()[0]->getArrayDouble(id)); if (arr.isNull()) { return arr; } TEFNAFillArray (res, arr.array()); if (arr.hasMask()) { mask.resize (shp); TEFNAFillArray (mask, arr.mask()); } } return MArray (res, mask); } case TableExprFuncNode::transposeFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return reorderArray (arr, getOrder(id, arr.ndim()), False); } case TableExprFuncNode::areverseFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); return reverseArray (arr, getReverseAxes(id, arr.ndim())); } case TableExprFuncNode::diagonalFUNC: { MArray arr (operands()[0]->getArrayDouble(id)); if (arr.isNull()) { return arr; } const IPosition parms = getDiagonalArg (id, arr.shape()); if (arr.hasMask()) { return MArray(arr.array().diagonals(parms[0], parms[1]), arr.mask().diagonals(parms[0], parms[1])); } return MArray(arr.array().diagonals(parms[0], parms[1])); } case TableExprFuncNode::resizeFUNC: return TEFResize (operands()[0]->getArrayDouble(id), id); case TableExprFuncNode::iifFUNC: return TEFNAiif (operands(), id); case TableExprFuncNode::nullarrayFUNC: return MArray(); case TableExprFuncNode::marrayFUNC: return MArray (operands()[0]->getDoubleAS(id), operands()[1]->getBoolAS(id)); case TableExprFuncNode::arrdataFUNC: { MArray arr(operands()[0]->getDoubleAS(id).array()); return arr.isNull() ? arr : MArray (arr.array()); } case TableExprFuncNode::negatemaskFUNC: return TEFMASKneg (operands()[0]->getDoubleAS(id)); case TableExprFuncNode::replmaskedFUNC: return TEFMASKrepl (operands()[0]->getDoubleAS(id), operands()[1], id, True); case TableExprFuncNode::replunmaskedFUNC: return TEFMASKrepl (operands()[0]->getDoubleAS(id), operands()[1], id, False); case TableExprFuncNode::arrflatFUNC: return MArray (operands()[0]->getDoubleAS(id).flatten()); case TableExprFuncNode::angdistFUNC: { MArray a1 = operands()[0]->getArrayDouble(id); MArray a2 = operands()[1]->getArrayDouble(id); if (!(a1.size() %2 == 0 && a2.size() %2 == 0)) { throw TableInvExpr ("Arguments of angdist function must have a " "multiple of 2 values"); } // Treat an array of size 2 as scalar, so allow scalar-array operations // which is handled by angdistxFUNC. if (a1.size() == 2 || a2.size() == 2) { return angdistx (a1, a2); } if (a1.size() != a2.size()) { throw TableInvExpr ("Arguments of angdist function must have " "equal length"); } Array result(IPosition(1, a1.size()/2)); Double* res = result.data(); Array::const_iterator p2 = a2.array().begin(); Array::const_iterator end1 = a1.array().end(); for (Array::const_iterator p1 = a1.array().begin(); p1!=end1; ++p1) { Double ra1 = *p1; ++p1; Double ra2 = *p2; ++p2; *res++ = acos (sin(*p1)*sin(*p2) + cos(*p1)*cos(*p2)*cos(ra1-ra2)); ++p2; } // Reduce possible masks by combining every 2 values. Array mask; if (a1.hasMask()) { partialArrayMath (mask, a1.mask().reform(IPosition(2, 2, a1.size()/2)), IPosition(1,0), AnyFunc()); } if (a2.hasMask()) { Array mask2; partialArrayMath (mask2, a2.mask().reform(IPosition(2, 2, a2.size()/2)), IPosition(1,0), AnyFunc()); if (mask.empty()) { mask.reference (mask2); } else { mask.reference (mask || mask2); } } return MArray (result, mask); } case TableExprFuncNode::angdistxFUNC: { MArray a1 = operands()[0]->getArrayDouble(id); MArray a2 = operands()[1]->getArrayDouble(id); if (!(a1.size() %2 == 0 && a2.size() %2 == 0)) { throw TableInvExpr ("Arguments of angdistx function must have a " "multiple of 2 values"); } return angdistx (a1, a2); } case TableExprFuncNode::normangleFUNC: { MArray values (operands()[0]->getArrayDouble(id)); Array res(values.shape()); Bool deleteVal, deleteRes; const Double* val = values.array().getStorage (deleteVal); Double* resp = res.getStorage (deleteRes); size_t n = values.size(); for (size_t i=0; i (res, values); } case TableExprFuncNode::datetimeFUNC: case TableExprFuncNode::mjdtodateFUNC: case TableExprFuncNode::dateFUNC: { MArray arr (getArrayDate(id)); Array res(arr.shape()); convertArray (res, arr.array()); return MArray (res, arr); } default: { // Functions like YEAR are implemented as Int only. MArray arr (getArrayInt(id)); Array res(arr.shape()); convertArray (res, arr.array()); return MArray (res, arr); } } return MArray(); } MArray TableExprFuncNodeArray::getArrayDComplex (const TableExprId& id) { if (dataType() == NTDouble) { return TableExprNodeArray::getArrayDComplex (id); } switch (funcType()) { case TableExprFuncNode::sinFUNC: return sin (operands()[0]->getArrayDComplex(id)); case TableExprFuncNode::sinhFUNC: return sinh (operands()[0]->getArrayDComplex(id)); case TableExprFuncNode::cosFUNC: return cos (operands()[0]->getArrayDComplex(id)); case TableExprFuncNode::coshFUNC: return cosh (operands()[0]->getArrayDComplex(id)); case TableExprFuncNode::expFUNC: return exp (operands()[0]->getArrayDComplex(id)); case TableExprFuncNode::logFUNC: return log (operands()[0]->getArrayDComplex(id)); case TableExprFuncNode::log10FUNC: return log10 (operands()[0]->getArrayDComplex(id)); case TableExprFuncNode::squareFUNC: return square ( operands()[0]->getArrayDComplex(id)); case TableExprFuncNode::cubeFUNC: return cube ( operands()[0]->getArrayDComplex(id)); case TableExprFuncNode::sqrtFUNC: { MArray res = sqrt (operands()[0]->getArrayDComplex(id)); if (node_p.getScale() != 1.) { arrayTransformInPlace (res.array(), node_p.getScale(), casacore::Multiplies()); } return res; } case TableExprFuncNode::conjFUNC: return conj (operands()[0]->getArrayDComplex(id)); case TableExprFuncNode::powFUNC: if (operands()[0]->valueType() == VTScalar) { return casacore::pow (operands()[0]->getDComplex(id), operands()[1]->getArrayDComplex(id)); } else if (operands()[1]->valueType() == VTScalar) { if (operands()[1]->dataType() == NTDouble) { return casacore::pow (operands()[0]->getArrayDComplex(id), operands()[1]->getDouble(id)); } else { return casacore::pow (operands()[0]->getArrayDComplex(id), operands()[1]->getDComplex(id)); } } else { return pow (operands()[0]->getArrayDComplex(id), operands()[1]->getArrayDComplex(id)); } case TableExprFuncNode::minFUNC: if (operands()[0]->valueType() == VTScalar) { return min (operands()[0]->getDComplex(id), operands()[1]->getArrayDComplex(id)); } else if (operands()[1]->valueType() == VTScalar) { return min (operands()[0]->getArrayDComplex(id), operands()[1]->getDComplex(id)); } else { return min (operands()[0]->getArrayDComplex(id), operands()[1]->getArrayDComplex(id)); } case TableExprFuncNode::maxFUNC: if (operands()[0]->valueType() == VTScalar) { return max (operands()[0]->getDComplex(id), operands()[1]->getArrayDComplex(id)); } else if (operands()[1]->valueType() == VTScalar) { return max (operands()[0]->getArrayDComplex(id), operands()[1]->getDComplex(id)); } else { return max (operands()[0]->getArrayDComplex(id), operands()[1]->getArrayDComplex(id)); } case TableExprFuncNode::asinFUNC: return asin (operands()[0]->getArrayDComplex(id)); case TableExprFuncNode::acosFUNC: return acos (operands()[0]->getArrayDComplex(id)); case TableExprFuncNode::atanFUNC: return atan (operands()[0]->getArrayDComplex(id)); case TableExprFuncNode::tanFUNC: return tan (operands()[0]->getArrayDComplex(id)); case TableExprFuncNode::tanhFUNC: return tanh (operands()[0]->getArrayDComplex(id)); case TableExprFuncNode::arrsumsFUNC: { MArray arr (operands()[0]->getArrayDComplex(id)); return partialSums (arr, getAxes(id, arr.ndim())); } case TableExprFuncNode::runsumFUNC: { MArray arr (operands()[0]->getArrayDComplex(id)); return slidingSums (arr, getArrayShape(id)); } case TableExprFuncNode::boxsumFUNC: { MArray arr (operands()[0]->getArrayDComplex(id)); return boxedSums (arr, getArrayShape(id)); } case TableExprFuncNode::arrproductsFUNC: { MArray arr (operands()[0]->getArrayDComplex(id)); return partialProducts (arr, getAxes(id, arr.ndim())); } case TableExprFuncNode::runproductFUNC: { MArray arr (operands()[0]->getArrayDComplex(id)); return slidingProducts (arr, getArrayShape(id)); } case TableExprFuncNode::boxproductFUNC: { MArray arr (operands()[0]->getArrayDComplex(id)); return boxedProducts (arr, getArrayShape(id)); } case TableExprFuncNode::arrsumsqrsFUNC: { MArray arr (operands()[0]->getArrayDComplex(id)); return partialSums (arr*arr, getAxes(id, arr.ndim())); } case TableExprFuncNode::runsumsqrFUNC: { MArray arr (operands()[0]->getArrayDComplex(id)); return slidingSumSqrs (arr, getArrayShape(id)); } case TableExprFuncNode::boxsumsqrFUNC: { MArray arr (operands()[0]->getArrayDComplex(id)); return boxedSumSqrs (arr, getArrayShape(id)); } case TableExprFuncNode::arrmeansFUNC: { MArray arr (operands()[0]->getArrayDComplex(id)); return partialMeans (arr, getAxes(id, arr.ndim())); } case TableExprFuncNode::runmeanFUNC: { MArray arr (operands()[0]->getArrayDComplex(id)); return slidingMeans (arr, getArrayShape(id)); } case TableExprFuncNode::boxmeanFUNC: { MArray arr (operands()[0]->getArrayDComplex(id)); return boxedMeans (arr, getArrayShape(id)); } case TableExprFuncNode::arrayFUNC: { IPosition shp (getArrayShape(id)); Array res(shp); Array mask; if (operands()[0]->valueType() == VTScalar) { res = operands()[0]->getDComplex(id); } else { MArray arr (operands()[0]->getArrayDComplex(id)); if (arr.isNull()) { return arr; } TEFNAFillArray (res, arr.array()); if (arr.hasMask()) { mask.resize (shp); TEFNAFillArray (mask, arr.mask()); } } return MArray (res, mask); } case TableExprFuncNode::transposeFUNC: { MArray arr (operands()[0]->getArrayDComplex(id)); return reorderArray (arr, getOrder(id, arr.ndim()), False); } case TableExprFuncNode::areverseFUNC: { MArray arr (operands()[0]->getArrayDComplex(id)); return reverseArray (arr, getReverseAxes(id, arr.ndim())); } case TableExprFuncNode::diagonalFUNC: { MArray arr (operands()[0]->getArrayDComplex(id)); if (arr.isNull()) { return arr; } const IPosition parms = getDiagonalArg (id, arr.shape()); if (arr.hasMask()) { return MArray(arr.array().diagonals(parms[0], parms[1]), arr.mask().diagonals(parms[0], parms[1])); } return MArray(arr.array().diagonals(parms[0], parms[1])); } case TableExprFuncNode::resizeFUNC: return TEFResize (operands()[0]->getArrayDComplex(id), id); case TableExprFuncNode::complexFUNC: { if (operands().size() == 1) { MArray values (operands()[0]->getArrayString(id)); Array res(values.shape()); Array::const_iterator in = values.array().begin(); for (Array::contiter out=res.cbegin(); out!=res.cend(); ++out, ++in) { *out = TableExprFuncNode::string2Complex (*in); } return MArray (res, values); } if (operands()[0]->valueType() == VTScalar) { Double val = operands()[0]->getDouble(id); MArray arr (operands()[1]->getArrayDouble(id)); return MArray (makeComplex(val,arr.array()), arr); } else if (operands()[1]->valueType() == VTScalar) { MArray arr (operands()[0]->getArrayDouble(id)); Double val = operands()[1]->getDouble(id); return MArray (makeComplex(arr.array(), val), arr); } MArray arr1 (operands()[0]->getArrayDouble(id)); MArray arr2 (operands()[1]->getArrayDouble(id)); if (arr1.isNull() || arr2.isNull()) { return MArray(); } return MArray (makeComplex(arr1.array(), arr2.array()), arr1.combineMask(arr2)); } case TableExprFuncNode::iifFUNC: return TEFNAiif (operands(), id); case TableExprFuncNode::nullarrayFUNC: return MArray(); case TableExprFuncNode::marrayFUNC: return MArray (operands()[0]->getDComplexAS(id), operands()[1]->getBoolAS(id)); case TableExprFuncNode::arrdataFUNC: { MArray arr(operands()[0]->getDComplexAS(id).array()); return arr.isNull() ? arr : MArray (arr.array()); } case TableExprFuncNode::negatemaskFUNC: return TEFMASKneg (operands()[0]->getDComplexAS(id)); case TableExprFuncNode::replmaskedFUNC: return TEFMASKrepl (operands()[0]->getDComplexAS(id), operands()[1], id, True); case TableExprFuncNode::replunmaskedFUNC: return TEFMASKrepl (operands()[0]->getDComplexAS(id), operands()[1], id, False); case TableExprFuncNode::arrflatFUNC: return MArray (operands()[0]->getDComplexAS(id).flatten()); default: break; } throw TableInvExpr ("TableExprFuncNodeArray::getArrayDComplex, " "unknown function " + String::toString(funcType())); } MArray TableExprFuncNodeArray::getArrayString (const TableExprId& id) { switch (funcType()) { case TableExprFuncNode::upcaseFUNC: case TableExprFuncNode::downcaseFUNC: case TableExprFuncNode::capitalizeFUNC: case TableExprFuncNode::sreverseFUNC: case TableExprFuncNode::trimFUNC: case TableExprFuncNode::ltrimFUNC: case TableExprFuncNode::rtrimFUNC: case TableExprFuncNode::substrFUNC: case TableExprFuncNode::replaceFUNC: { static Regex leadingWS("^[ \t]*"); static Regex trailingWS("[ \t]*$"); MArray mstrings (operands()[0]->getArrayString(id)); Array strings (mstrings.array().copy()); Bool deleteStr; String* str = strings.getStorage (deleteStr); size_t n = strings.size(); switch (funcType()) { case TableExprFuncNode::upcaseFUNC: for (size_t i=0; igetInt (id); Int64 sz = String::npos; if (operands().size() > 2) { sz = std::max (Int64(0), operands()[2]->getInt (id)); } for (size_t i=0; i 2) { repl = operands()[2]->getString (id); } if (operands()[1]->dataType() == TableExprNodeRep::NTString) { String patt = operands()[1]->getString(id); for (size_t i=0; igetRegex(id).regex(); for (size_t i=0; i (strings, mstrings); break; } case TableExprFuncNode::cmonthFUNC: case TableExprFuncNode::cdowFUNC: case TableExprFuncNode::ctodFUNC: case TableExprFuncNode::cdateFUNC: case TableExprFuncNode::ctimeFUNC: { MArray values (operands()[0]->getArrayDate(id)); Array strings(values.shape()); Bool deleteVal, deleteStr; const MVTime* val = values.array().getStorage (deleteVal); String* str = strings.getStorage (deleteStr); size_t n = values.size(); switch (funcType()) { case TableExprFuncNode::cmonthFUNC: for (size_t i=0; i (strings, values); break; } case TableExprFuncNode::stringFUNC: { String fmt; Int width, prec; TableExprFuncNode::getPrintFormat (fmt, width, prec, operands(), id); Array res; if (operands()[0]->dataType() == NTBool) { MArray arr (operands()[0]->getArrayBool(id)); res.resize (arr.shape()); Array::const_iterator arrIter = arr.array().begin(); Array::iterator iterEnd = res.end(); for (Array::iterator resIter = res.begin(); resIter != iterEnd; ++resIter, ++arrIter) { *resIter = TableExprFuncNode::stringValue (*arrIter, fmt, width); } return MArray(res, arr); } else if (operands()[0]->dataType() == NTInt) { MArray arr (operands()[0]->getArrayInt(id)); res.resize (arr.shape()); Array::const_iterator arrIter = arr.array().begin(); Array::iterator iterEnd = res.end(); for (Array::iterator resIter = res.begin(); resIter != iterEnd; ++resIter, ++arrIter) { *resIter = TableExprFuncNode::stringValue (*arrIter, fmt, width); } return MArray(res, arr); } else if (operands()[0]->dataType() == NTDouble) { std::pair mvFormat = TableExprFuncNode::getMVFormat(fmt); MArray arr (operands()[0]->getArrayDouble(id)); res.resize (arr.shape()); Array::const_iterator arrIter = arr.array().begin(); Array::iterator iterEnd = res.end(); for (Array::iterator resIter = res.begin(); resIter != iterEnd; ++resIter, ++arrIter) { *resIter = TableExprFuncNode::stringValue (*arrIter, fmt, width, prec, mvFormat, operands()[0]->unit()); } return MArray(res, arr); } else if (operands()[0]->dataType() == NTComplex) { MArray arr (operands()[0]->getArrayDComplex(id)); res.resize (arr.shape()); Array::const_iterator arrIter = arr.array().begin(); Array::iterator iterEnd = res.end(); for (Array::iterator resIter = res.begin(); resIter != iterEnd; ++resIter, ++arrIter) { *resIter = TableExprFuncNode::stringValue (*arrIter, fmt, width, prec); } return MArray(res, arr); } else if (operands()[0]->dataType() == NTDate) { std::pair mvFormat = TableExprFuncNode::getMVFormat(fmt); MArray arr (operands()[0]->getArrayDate(id)); res.resize (arr.shape()); Array::const_iterator arrIter = arr.array().begin(); Array::iterator iterEnd = res.end(); for (Array::iterator resIter = res.begin(); resIter != iterEnd; ++resIter, ++arrIter) { *resIter = TableExprFuncNode::stringValue (*arrIter, fmt, width, mvFormat); } return MArray(res, arr); } else { MArray arr (operands()[0]->getArrayString(id)); Array res(arr.shape()); Array::const_iterator arrIter = arr.array().begin(); Array::iterator iterEnd = res.end(); for (Array::iterator resIter = res.begin(); resIter != iterEnd; ++resIter, ++arrIter) { *resIter = TableExprFuncNode::stringValue (*arrIter, fmt, width); } return MArray(res, arr); } } case TableExprFuncNode::hmsFUNC: case TableExprFuncNode::dmsFUNC: case TableExprFuncNode::hdmsFUNC: { MArray values (operands()[0]->getArrayDouble(id)); Array strings(values.shape()); Bool deleteVal, deleteStr; const Double* val = values.array().getStorage (deleteVal); String* str = strings.getStorage (deleteStr); size_t n = values.size(); switch (funcType()) { case TableExprFuncNode::hmsFUNC: for (size_t i=0; i (strings, values); break; } case TableExprFuncNode::arrayFUNC: { IPosition shp (getArrayShape(id)); Array res(shp); Array mask; if (operands()[0]->valueType() == VTScalar) { res = operands()[0]->getString(id); } else { MArray arr (operands()[0]->getArrayString(id)); if (arr.isNull()) { return arr; } TEFNAFillArray (res, arr.array()); if (arr.hasMask()) { mask.resize (shp); TEFNAFillArray (mask, arr.mask()); } } return MArray (res, mask); } case TableExprFuncNode::transposeFUNC: { MArray arr (operands()[0]->getArrayString(id)); return reorderArray (arr, getOrder(id, arr.ndim()), False); } case TableExprFuncNode::areverseFUNC: { MArray arr (operands()[0]->getArrayString(id)); return reverseArray (arr, getReverseAxes(id, arr.ndim())); } case TableExprFuncNode::diagonalFUNC: { MArray arr (operands()[0]->getArrayString(id)); if (arr.isNull()) { return arr; } const IPosition parms = getDiagonalArg (id, arr.shape()); if (arr.hasMask()) { return MArray(arr.array().diagonals(parms[0], parms[1]), arr.mask().diagonals(parms[0], parms[1])); } return MArray(arr.array().diagonals(parms[0], parms[1])); } case TableExprFuncNode::resizeFUNC: return TEFResize (operands()[0]->getArrayString(id), id); case TableExprFuncNode::iifFUNC: return TEFNAiif (operands(), id); case TableExprFuncNode::nullarrayFUNC: return MArray(); case TableExprFuncNode::marrayFUNC: return MArray (operands()[0]->getStringAS(id), operands()[1]->getBoolAS(id)); case TableExprFuncNode::arrdataFUNC: { MArray arr(operands()[0]->getStringAS(id).array()); return arr.isNull() ? arr : MArray (arr.array()); } case TableExprFuncNode::negatemaskFUNC: return TEFMASKneg (operands()[0]->getStringAS(id)); case TableExprFuncNode::replmaskedFUNC: return TEFMASKrepl (operands()[0]->getStringAS(id), operands()[1], id, True); case TableExprFuncNode::replunmaskedFUNC: return TEFMASKrepl (operands()[0]->getStringAS(id), operands()[1], id, False); case TableExprFuncNode::arrflatFUNC: return MArray (operands()[0]->getStringAS(id).flatten()); default: break; } throw TableInvExpr ("TableExprFuncNodeArray::getArrayString, " "unknown function " + String::toString(funcType())); } MArray TableExprFuncNodeArray::getArrayDate (const TableExprId& id) { switch (funcType()) { case TableExprFuncNode::datetimeFUNC: { MArray values (operands()[0]->getArrayString(id)); Array dates(values.shape()); Bool deleteVal, deleteDat; const String* val = values.array().getStorage (deleteVal); MVTime* dat = dates.getStorage (deleteDat); Quantity quant; size_t n = values.size(); for (size_t i=0; i (dates, values); } case TableExprFuncNode::mjdtodateFUNC: { MArray values (operands()[0]->getArrayDouble(id)); Array dates(values.shape()); Bool deleteVal, deleteDat; const Double* val = values.array().getStorage (deleteVal); MVTime* dat = dates.getStorage (deleteDat); size_t n = values.size(); for (size_t i=0; i (dates, values); } case TableExprFuncNode::dateFUNC: { MArray values (operands()[0]->getArrayDate(id)); Array dates(values.shape()); Bool deleteVal, deleteDat; const MVTime* val = values.array().getStorage (deleteVal); MVTime* dat = dates.getStorage (deleteDat); size_t n = values.size(); for (size_t i=0; i (dates, values); } case TableExprFuncNode::arrayFUNC: { IPosition shp (getArrayShape(id)); Array res(shp); Array mask; if (operands()[0]->valueType() == VTScalar) { res = operands()[0]->getDate(id); } else { MArray arr (operands()[0]->getArrayDate(id)); if (arr.isNull()) { return arr; } TEFNAFillArray (res, arr.array()); if (arr.hasMask()) { mask.resize (shp); TEFNAFillArray (mask, arr.mask()); } } return MArray (res, mask); } case TableExprFuncNode::transposeFUNC: { MArray arr (operands()[0]->getArrayDate(id)); return reorderArray (arr, getOrder(id, arr.ndim()), False); } case TableExprFuncNode::areverseFUNC: { MArray arr (operands()[0]->getArrayDate(id)); return reverseArray (arr, getReverseAxes(id, arr.ndim())); } case TableExprFuncNode::diagonalFUNC: { MArray arr (operands()[0]->getArrayDate(id)); if (arr.isNull()) { return arr; } const IPosition parms = getDiagonalArg (id, arr.shape()); if (arr.hasMask()) { return MArray(arr.array().diagonals(parms[0], parms[1]), arr.mask().diagonals(parms[0], parms[1])); } return MArray(arr.array().diagonals(parms[0], parms[1])); } case TableExprFuncNode::resizeFUNC: return TEFResize (operands()[0]->getArrayDate(id), id); case TableExprFuncNode::iifFUNC: return TEFNAiif (operands(), id); case TableExprFuncNode::nullarrayFUNC: return MArray(); case TableExprFuncNode::marrayFUNC: return MArray (operands()[0]->getDateAS(id), operands()[1]->getBoolAS(id)); case TableExprFuncNode::arrdataFUNC: { MArray arr(operands()[0]->getDateAS(id).array()); return arr.isNull() ? arr : MArray (arr.array()); } case TableExprFuncNode::negatemaskFUNC: return TEFMASKneg (operands()[0]->getDateAS(id)); case TableExprFuncNode::replmaskedFUNC: return TEFMASKrepl (operands()[0]->getDateAS(id), operands()[1], id, True); case TableExprFuncNode::replunmaskedFUNC: return TEFMASKrepl (operands()[0]->getDateAS(id), operands()[1], id, False); case TableExprFuncNode::arrflatFUNC: return MArray (operands()[0]->getDateAS(id).flatten()); default: break; } throw TableInvExpr ("TableExprFuncNodeArray::getArrayDate, " "unknown function " + String::toString(funcType())); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/ExprFuncNodeArray.h000066400000000000000000000154531476623553700211170ustar00rootroot00000000000000//# ExprFuncNodeArray.h: Class representing an array function in table select expression //# Copyright (C) 2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_EXPRFUNCNODEARRAY_H #define TABLES_EXPRFUNCNODEARRAY_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // Class representing an array function in table select expression // // // // // //# Classes you should understand before using this one. //
      • TableExprFuncNode //
      • TableExprNodeArray // // // This class can be seen as a specialization of TableExprFuncNode // for functions returning arrays. // However, it is derived from TableExprNodeArray to make it possible // that the ExprNode classes use all array functionality offered by // that base class. //
        Internally an TableExprFuncNode object is used. //

        // When a TaQL function is used, TableExprFuncNode::checkOperands // determines whether the result is a scalar or an array. // Thereafter TableExprNode::newFunctionNode creates a TableExprFuncNode // for scalars or a TableExprFuncNodeArray for arrays. // class TableExprFuncNodeArray : public TableExprNodeArray { public: // Constructor TableExprFuncNodeArray (TableExprFuncNode::FunctionType, NodeDataType, ValueType, const TableExprNodeSet& source, const vector& nodes, const Block& dtypeOper, const TaQLStyle&); // Destructor ~TableExprFuncNodeArray(); // Flatten the node tree by adding the node and its children to the vector. virtual void flattenTree (std::vector&); // 'get' Functions to get the desired result of a function // virtual MArray getArrayBool (const TableExprId& id); virtual MArray getArrayInt (const TableExprId& id); virtual MArray getArrayDouble (const TableExprId& id); virtual MArray getArrayDComplex (const TableExprId& id); virtual MArray getArrayString (const TableExprId& id); virtual MArray getArrayDate (const TableExprId& id); // // Get the function node. TableExprFuncNode* getChild() { return &node_p; } const TableExprFuncNode* getChild() const { return &node_p; } protected: // Try if the function gives a constant result. // If so, set the expression type to Constant. // Get possible constant arguments like axes. void tryToConst(); // Some functions to be used by TableExprNodeFuncArray. // const std::vector& operands() const { return node_p.operands(); } std::vector& rwOperands() { return node_p.rwOperands(); } TableExprFuncNode::FunctionType funcType() const { return node_p.funcType(); } NodeDataType argDataType() const { return node_p.argDataType(); } // private: // Get the collapse axes for the partial functions. // It compares the values with the #dim and removes them if too high. // axarg gives the argument nr of the axes. IPosition getAxes (const TableExprId& id, Int ndim, uInt axarg=1, Bool swapRemove=True); // Remove axes exceeding ndim. IPosition removeAxes (const IPosition& axes, Int ndim) const; // Get the shape for the array, boxed and running functions. // If an axis length < 0, the corresponding main shape axis (if present) // is used. // axarg gives the argument nr of the shape. const IPosition& getArrayShape (const TableExprId& id, uInt axarg=1); // Get the transpose order of the array axes. IPosition getOrder (const TableExprId& id, Int ndim); // Get the axes for the reverse function. IPosition getReverseAxes (const TableExprId& id, uInt ndim); // Get the arguments for the diagonals function. // They are checked and if needed adapted if the shape is not empty. const IPosition& getDiagonalArg (const TableExprId& id, const IPosition& shp); // Set the alternate value expandAlt_p for array expand and return it. const IPosition& getAlternate (const TableExprId& id); // Adjust the resize shape by replacing negative axes with the // original axis (if present) or 1. IPosition adjustShape (const IPosition& shape, const IPosition& origShape) const; // Templated function to resize/expand an array. template MArray TEFResize (const MArray& arr, const TableExprId& id); // The angular distance between each pair of the arguments. MArray angdistx (const MArray& a1, const MArray& a2) const; //# Data members TableExprFuncNode node_p; Int origin_p; //# axes origin Bool isCOrder_p; //# axes order Bool constAxes_p; //# True = collapse axes are constant Bool constAlt_p; //# True = expandAlt_p is constant IPosition ipos_p; //# the (maybe constant) axes or shape IPosition iposN_p; //# the non-reversed axes or shape IPosition expandAlt_p; //# alternate for expand/resize }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/ExprGroup.cc000066400000000000000000000435171476623553700176530ustar00rootroot00000000000000//# ExprGroup.cc: Classes for TaQL's GROUPBY clause //# Copyright (C) 2013 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN bool TableExprGroupKey::operator== (const TableExprGroupKey& that) const { switch (itsDT) { case TableExprNodeRep::NTBool: return itsBool == that.itsBool; case TableExprNodeRep::NTInt: return itsInt64 == that.itsInt64; case TableExprNodeRep::NTDouble: return itsDouble == that.itsDouble; default: return itsString == that.itsString; } } bool TableExprGroupKey::operator< (const TableExprGroupKey& that) const { switch (itsDT) { case TableExprNodeRep::NTBool: return itsBool < that.itsBool; case TableExprNodeRep::NTInt: return itsInt64 < that.itsInt64; case TableExprNodeRep::NTDouble: return itsDouble < that.itsDouble; default: return itsString < that.itsString; } } TableExprGroupKeySet::TableExprGroupKeySet (const vector& nodes) { itsKeys.reserve (nodes.size()); for (uInt i=0; idataType()); } } void TableExprGroupKeySet::fill (const vector& nodes, const TableExprId& id) { AlwaysAssert (nodes.size() == itsKeys.size(), AipsError); for (uInt i=0; i>& funcSets) { itsFuncSets = funcSets; } TableExprGroupResult::TableExprGroupResult (const vector>& funcSets, const vector>>& ids) { AlwaysAssert (ids.size() == funcSets.size() || ids.empty(), AipsError); itsFuncSets = funcSets; itsIds = ids; } TableExprGroupFuncBase::TableExprGroupFuncBase (TableExprNodeRep* node) : itsNode (node), itsOperand (0), itsSeqnr (0) { if (node) { TableExprAggrNode* snode = dynamic_cast(node); if (snode) { itsOperand = snode->operand().get(); } else { TableExprAggrNodeArray* anode = dynamic_cast(node); if (anode) { itsOperand = anode->operand().get(); } else { TableExprUDFNode* unode = dynamic_cast(node); AlwaysAssert (unode && unode->isAggregate(), AipsError); } } } } TableExprGroupFuncBase::~TableExprGroupFuncBase() {} Bool TableExprGroupFuncBase::isLazy() const { return False; } void TableExprGroupFuncBase::finish() {} std::shared_ptr> TableExprGroupFuncBase::getIds() const { throw TableInvExpr ("TableExprGroupFuncBase::getIds not implemented"); } Bool TableExprGroupFuncBase::getBool (const vector&) { throw TableInvExpr ("TableExprGroupFuncBase::getBool not implemented"); } Int64 TableExprGroupFuncBase::getInt (const vector&) { throw TableInvExpr ("TableExprGroupFuncBase::getInt not implemented"); } Double TableExprGroupFuncBase::getDouble (const vector&) { throw TableInvExpr ("TableExprGroupFuncBase::getDouble not implemented"); } DComplex TableExprGroupFuncBase::getDComplex (const vector&) { throw TableInvExpr ("TableExprGroupFuncBase::getDComplex not implemented"); } MVTime TableExprGroupFuncBase::getDate (const vector&) { throw TableInvExpr ("TableExprGroupFuncBase::getDate not implemented"); } String TableExprGroupFuncBase::getString (const vector&) { throw TableInvExpr ("TableExprGroupFuncBase::getString not implemented"); } MArray TableExprGroupFuncBase::getArrayBool (const vector&) { throw TableInvExpr ("TableExprGroupFuncBase::getArrayBool not implemented"); } MArray TableExprGroupFuncBase::getArrayInt (const vector&) { throw TableInvExpr ("TableExprGroupFuncBase::getArrayInt not implemented"); } MArray TableExprGroupFuncBase::getArrayDouble (const vector&) { throw TableInvExpr ("TableExprGroupFuncBase::getArrayDouble not implemented"); } MArray TableExprGroupFuncBase::getArrayDComplex (const vector&) { throw TableInvExpr ("TableExprGroupFuncBase::getArrayDComplex not implemented"); } MArray TableExprGroupFuncBase::getArrayDate (const vector&) { throw TableInvExpr ("TableExprGroupFuncBase::getArrayDate not implemented"); } MArray TableExprGroupFuncBase::getArrayString (const vector&) { throw TableInvExpr ("TableExprGroupFuncBase::getArrayString not implemented"); } TableExprGroupNull::TableExprGroupNull (TableExprNodeRep* node) : TableExprGroupFuncBase (node) {} TableExprGroupNull::~TableExprGroupNull() {} Bool TableExprGroupNull::isLazy() const { return True; } void TableExprGroupNull::apply (const TableExprId&) { throw TableInvExpr ("TableExprGroupFunc::apply should not be called for " " lazy aggregation"); } TableExprGroupFirst::TableExprGroupFirst (TableExprNodeRep* node) : TableExprGroupFuncBase (node) {} TableExprGroupFirst::~TableExprGroupFirst() {} void TableExprGroupFirst::apply (const TableExprId& id) { // Keep first one. if (itsId.rownr() < 0) { itsId = id; } } Bool TableExprGroupFirst::getBool (const vector&) { return itsOperand->getBool (itsId); } Int64 TableExprGroupFirst::getInt (const vector&) { return itsOperand->getInt (itsId); } Double TableExprGroupFirst::getDouble (const vector&) { return itsOperand->getDouble (itsId); } DComplex TableExprGroupFirst::getDComplex (const vector&) { return itsOperand->getDComplex (itsId); } MVTime TableExprGroupFirst::getDate (const vector&) { return itsOperand->getDate (itsId); } String TableExprGroupFirst::getString (const vector&) { return itsOperand->getString (itsId); } MArray TableExprGroupFirst::getArrayBool (const vector&) { return itsOperand->getArrayBool (itsId); } MArray TableExprGroupFirst::getArrayInt (const vector&) { return itsOperand->getArrayInt (itsId); } MArray TableExprGroupFirst::getArrayDouble (const vector&) { return itsOperand->getArrayDouble (itsId); } MArray TableExprGroupFirst:: getArrayDComplex (const vector&) { return itsOperand->getArrayDComplex (itsId); } MArray TableExprGroupFirst::getArrayDate (const vector&) { return itsOperand->getArrayDate (itsId); } MArray TableExprGroupFirst::getArrayString (const vector&) { return itsOperand->getArrayString (itsId); } TableExprGroupLast::TableExprGroupLast (TableExprNodeRep* node) : TableExprGroupFirst (node) {} TableExprGroupLast::~TableExprGroupLast() {} void TableExprGroupLast::apply (const TableExprId& id) { itsId = id; } TableExprGroupExprId::TableExprGroupExprId (TableExprNodeRep* node) : TableExprGroupFuncBase (node) { itsIds = std::make_shared>(); } TableExprGroupExprId::~TableExprGroupExprId() {} Bool TableExprGroupExprId::isLazy() const { return True; } void TableExprGroupExprId::apply (const TableExprId& id) { itsIds->push_back (id); } std::shared_ptr> TableExprGroupExprId::getIds() const { return itsIds; } TableExprGroupRowid::TableExprGroupRowid (TableExprNodeRep* node) : TableExprGroupFuncBase (node) {} TableExprGroupRowid::~TableExprGroupRowid() {} Bool TableExprGroupRowid::isLazy() const { return True; } void TableExprGroupRowid::apply (const TableExprId&) { throw TableInvExpr ("TableExprGroupRowid::apply should not be called"); } MArray TableExprGroupRowid::getArrayInt (const vector& ids) { Vector rowIds(ids.size()); for (size_t i=0; i(rowIds); } TableExprGroupAggr::TableExprGroupAggr (TableExprNodeRep* node) : TableExprGroupFuncBase (node) {} TableExprGroupAggr::~TableExprGroupAggr() {} Bool TableExprGroupAggr::isLazy() const { return True; } void TableExprGroupAggr::apply (const TableExprId&) { throw TableInvExpr ("TableExprGroupAggr::apply should not be called"); } MArray TableExprGroupAggr::getArrayBool (const vector& ids) { return getArray(ids); } MArray TableExprGroupAggr::getArrayInt (const vector& ids) { return getArray(ids); } MArray TableExprGroupAggr::getArrayDouble (const vector& ids) { return getArray(ids); } MArray TableExprGroupAggr::getArrayDComplex (const vector& ids) { return getArray(ids); } MArray TableExprGroupAggr::getArrayDate (const vector& ids) { return getArray(ids); } MArray TableExprGroupAggr::getArrayString (const vector& ids) { return getArray(ids); } TableExprGroupFuncBool::~TableExprGroupFuncBool() {} Bool TableExprGroupFuncBool::getBool (const vector&) { return itsValue; } TableExprGroupFuncInt::~TableExprGroupFuncInt() {} Int64 TableExprGroupFuncInt::getInt (const vector&) { return itsValue; } Double TableExprGroupFuncInt::getDouble (const vector&) { return itsValue; } TableExprGroupFuncDouble::~TableExprGroupFuncDouble() {} Double TableExprGroupFuncDouble::getDouble (const vector&) { return itsValue; } TableExprGroupFuncDComplex::~TableExprGroupFuncDComplex() {} DComplex TableExprGroupFuncDComplex::getDComplex (const vector&) { return itsValue; } TableExprGroupFuncString::~TableExprGroupFuncString() {} String TableExprGroupFuncString::getString (const vector&) { return itsValue; } TableExprGroupFuncArrayBool::~TableExprGroupFuncArrayBool() {} MArray TableExprGroupFuncArrayBool::getArrayBool (const vector&) { return MArray(itsValue); } Bool TableExprGroupFuncArrayBool::checkShape (const MArrayBase& arr, const String& func) { if (itsValue.empty()) { itsValue.resize (arr.shape(), arr.hasMask()); return True; // first time itsValue is used } if (! itsValue.shape().isEqual (arr.shape())) { throw TableInvExpr ("Mismatching array shapes in aggregate function " + func); } AlwaysAssert (arr.hasMask() == itsValue.hasMask(), AipsError); return False; } TableExprGroupFuncArrayInt::~TableExprGroupFuncArrayInt() {} MArray TableExprGroupFuncArrayInt::getArrayInt (const vector&) { return MArray(itsValue); } Bool TableExprGroupFuncArrayInt::checkShape (const MArrayBase& arr, const String& func) { if (itsValue.empty()) { itsValue.resize (arr.shape(), arr.hasMask()); return True; // first time itsValue is used } if (! itsValue.shape().isEqual (arr.shape())) { throw TableInvExpr ("Mismatching array shapes in aggregate function " + func); } AlwaysAssert (arr.hasMask() == itsValue.hasMask(), AipsError); return False; } TableExprGroupFuncArrayDouble::~TableExprGroupFuncArrayDouble() {} MArray TableExprGroupFuncArrayDouble::getArrayDouble (const vector&) { return MArray(itsValue); } Bool TableExprGroupFuncArrayDouble::checkShape (const MArrayBase& arr, const String& func) { if (itsValue.empty()) { itsValue.resize (arr.shape(), arr.hasMask()); return True; // first time itsValue is used } if (! itsValue.shape().isEqual (arr.shape())) { throw TableInvExpr ("Mismatching array shapes in aggregate function " + func); } AlwaysAssert (arr.hasMask() == itsValue.hasMask(), AipsError); return False; } TableExprGroupFuncArrayDComplex::~TableExprGroupFuncArrayDComplex() {} MArray TableExprGroupFuncArrayDComplex::getArrayDComplex (const vector&) { return MArray(itsValue); } Bool TableExprGroupFuncArrayDComplex::checkShape (const MArrayBase& arr, const String& func) { if (itsValue.empty()) { itsValue.resize (arr.shape(), arr.hasMask()); return True; // first time itsValue is used } if (! itsValue.shape().isEqual (arr.shape())) { throw TableInvExpr ("Mismatching array shapes in aggregate function " + func); } AlwaysAssert (arr.hasMask() == itsValue.hasMask(), AipsError); return False; } TableExprGroupFuncArrayDate::~TableExprGroupFuncArrayDate() {} MArray TableExprGroupFuncArrayDate::getArrayDate (const vector&) { return MArray(itsValue); } Bool TableExprGroupFuncArrayDate::checkShape (const MArrayBase& arr, const String& func) { if (itsValue.empty()) { itsValue.resize (arr.shape(), arr.hasMask()); return True; // first time itsValue is used } if (! itsValue.shape().isEqual (arr.shape())) { throw TableInvExpr ("Mismatching array shapes in aggregate function " + func); } AlwaysAssert (arr.hasMask() == itsValue.hasMask(), AipsError); return False; } TableExprGroupFuncArrayString::~TableExprGroupFuncArrayString() {} MArray TableExprGroupFuncArrayString::getArrayString (const vector&) { return MArray(itsValue); } Bool TableExprGroupFuncArrayString::checkShape (const MArrayBase& arr, const String& func) { if (itsValue.empty()) { itsValue.resize (arr.shape(), arr.hasMask()); return True; // first time itsValue is used } if (! itsValue.shape().isEqual (arr.shape())) { throw TableInvExpr ("Mismatching array shapes in aggregate function " + func); } AlwaysAssert (arr.hasMask() == itsValue.hasMask(), AipsError); return False; } TableExprGroupFuncSet::TableExprGroupFuncSet (const vector& aggrNodes) : itsId (0) { itsFuncs.reserve (aggrNodes.size()); for (uInt i=0; imakeGroupAggrFunc()); itsFuncs[i]->setSeqnr (i); } } void TableExprGroupFuncSet::add (const std::shared_ptr& func) { size_t seqnr = itsFuncs.size(); itsFuncs.push_back (func); itsFuncs[seqnr]->setSeqnr (seqnr); } void TableExprGroupFuncSet::apply (const TableExprId& id) { itsId = id; for (uInt i=0; iapply (id); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/ExprGroup.h000066400000000000000000000755101476623553700175130ustar00rootroot00000000000000//# ExprGroup.h: Classes handling TaQL's GROUPBY functionality //# Copyright (C) 2013 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_EXPRGROUP_H #define TABLES_EXPRGROUP_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //

        // Class representing a key in the groupby clause. // // // // // // The GROUPBY clause consists of one or more keys, each being a scalar // TaQL expression with an arbitrary data type. // This class contains the value of a key for a particular table row. // It is part of a TableExprGroupKeySet object. // class TableExprGroupKey { public: // Construct for a given data type. explicit TableExprGroupKey (TableExprNodeRep::NodeDataType dtype) : itsDT (dtype) {} // Get the data type. TableExprNodeRep::NodeDataType dataType() const { return itsDT; } // Set the key's value. // void set (Bool v) { itsBool = v; } void set (Int64 v) { itsInt64 = v; } void set (Double v) { itsDouble = v; } void set (const String& v) { itsString = v; } // // Compare this and that key. // bool operator== (const TableExprGroupKey&) const; bool operator< (const TableExprGroupKey&) const; // private: TableExprNodeRep::NodeDataType itsDT; Bool itsBool = false; Int64 itsInt64 = 0; Double itsDouble = 0.0; String itsString; }; // // Class representing all keys in the groupby clause. // // // // // // The GROUPBY clause consists of one or more keys, each being a scalar // TaQL expression with an arbitrary data type. // This class contains a set of TableExprGroupKey objects, each containing // the value of a key for a particular table row. //
        It contains comparison functions to make it possible to use them // in a std::map object to map the groupby keyset to a group. //
        class TableExprGroupKeySet { public: // Form the object from the given groupby nodes. TableExprGroupKeySet (const vector& nodes); // Add a key to end the set. void addKey (TableExprNodeRep::NodeDataType dtype) { itsKeys.push_back (TableExprGroupKey(dtype)); } // Fill the keys with the values from the nodes for this rowid. void fill (const vector& nodes, const TableExprId& id); // Compare all keys in the set. // The keyset is compared in order of key, thus the first key defines // the major ordering. bool operator== (const TableExprGroupKeySet&) const; bool operator< (const TableExprGroupKeySet&) const; private: vector itsKeys; }; // // Class holding the results of groupby and aggregation // // // // // // The SELECT (and HAVING) clause can contain aggregate functions // of which the results can be grouped using the GROUPBY clause. // This class holds the results of the (immediate) aggregate functions // and, if needed, the TableExprId ids of all rows belonging to each group. // These ids are used to evaluate the lazy aggregate functions. //
        An object of this class is part of the TableExprIdAggr object // used to get the aggregated values of each group. //
        class TableExprGroupResult { public: // Create from the possible set of immediate aggregate functions. // No immediate functions were used, thus no TableExprIds needed. explicit TableExprGroupResult (const vector>& funcSets); // Create from the possible set of immediate aggregate functions // and the set of TableExprIds per group for lazy aggregate functions. TableExprGroupResult (const vector>& funcSets, const vector>>& ids); // Get the nr of groups. uInt ngroup() const { return itsFuncSets.size(); } // Get the set of functions (and their results) for the given group. TableExprGroupFuncSet& funcSet (uInt group) const { return *itsFuncSets[group]; } // Get the set of TableExprIds for the given group. const vector& ids (uInt group) const { return *itsIds[group]; } private: vector> itsFuncSets; vector>> itsIds; }; // // Abstract base class for classes calculating an aggregated group result. // // // // // // The GROUPBY clause divides a table into groups for which aggregated // results can be calculated like the mean or minimum. These results are // calculated in classes derived from this abstract base class. //
        There is one such function object per aggregation per group. All // aggregation objects of a group are combined in a std::vector. // This vector is mapped to a TableExprGroupKeySet object to keep track // of all groups and aggregations. //
        There are two types of aggregation function classes. //
          //
        • Immediate classes implement the 'apply' function to immediately // apply the operand's value in the aggregation. // Such classes do not keep the operand's values. //
        • Lazy classes do not need the 'apply' function. Instead they // read all values of the group in the 'getXXX' function and do the // aggregation. Such classes are meant for aggregation functions // like 'median' that need to keep all values. When applying it // immediately, all groups need to keep their values which might need // too much memory. Lazy classes need the values of only one group // at a time, but have the disadvantage that reading the values from // the table might be done in a non-sequential order. //
        // Most derived classes are immediate classes. //
        class TableExprGroupFuncBase { public: // Construct from the TaQL aggregation node. It keeps the operand // of the aggregation node. explicit TableExprGroupFuncBase (TableExprNodeRep* node); virtual ~TableExprGroupFuncBase(); // Copying is not needed, thus not allowed. TableExprGroupFuncBase (const TableExprGroupFuncBase&) = delete; TableExprGroupFuncBase& operator= (const TableExprGroupFuncBase&) = delete; // Does the aggregate function use lazy semantics? // The default implementation returns False. virtual Bool isLazy() const; // Get the function's sequence nr. uInt seqnr() const { return itsSeqnr; } // Set the function's sequence nr. void setSeqnr (uInt seqnr) { itsSeqnr = seqnr; } // Get the operand's value for the given row and apply it to the aggregation. // This function should not be called for lazy classes. virtual void apply (const TableExprId& id) = 0; // If needed, finish the aggregation. // By default nothing is done. virtual void finish(); // Get the assembled TableExprIds of a group. It is specifically meant // for TableExprGroupExprId used for lazy aggregation. virtual std::shared_ptr> getIds() const; // Get the aggregated value. // Immediate classes can return the already calculated value, while // lazy classes will get the values of all rows given by the TableExprIds // and do the aggregation. // virtual Bool getBool (const vector& = vector()); virtual Int64 getInt (const vector& = vector()); virtual Double getDouble (const vector& = vector()); virtual DComplex getDComplex (const vector& = vector()); virtual MVTime getDate (const vector& = vector()); virtual String getString (const vector& = vector()); virtual MArray getArrayBool (const vector& = vector()); virtual MArray getArrayInt (const vector& = vector()); virtual MArray getArrayDouble (const vector& = vector()); virtual MArray getArrayDComplex (const vector& = vector()); virtual MArray getArrayDate (const vector& = vector()); virtual MArray getArrayString (const vector& = vector()); // protected: //# Data member TableExprNodeRep* itsNode; // refers the node (not owned) TableExprNodeRep* itsOperand; // refers the operand (not owned) uInt itsSeqnr; }; // // Class derived from TableExprGroupFuncBase representing a no function // // // // // // This class represents a null aggregate function which is meant for // possible aggregate functionality in UDFs. // class TableExprGroupNull: public TableExprGroupFuncBase { public: explicit TableExprGroupNull (TableExprNodeRep* node); virtual ~TableExprGroupNull(); virtual Bool isLazy() const; virtual void apply (const TableExprId& id); }; // // Class derived from TableExprGroupFuncBase for the first value in a group // // // // // // This class keeps the TableExprId of the first value in a group. // The 'getXXX' functions get the value for that TableExprId. // class TableExprGroupFirst: public TableExprGroupFuncBase { public: explicit TableExprGroupFirst (TableExprNodeRep* node); virtual ~TableExprGroupFirst(); virtual void apply (const TableExprId& id); virtual Bool getBool (const vector&); virtual Int64 getInt (const vector&); virtual Double getDouble (const vector&); virtual DComplex getDComplex (const vector&); virtual MVTime getDate (const vector&); virtual String getString (const vector&); virtual MArray getArrayBool (const vector&); virtual MArray getArrayInt (const vector&); virtual MArray getArrayDouble (const vector&); virtual MArray getArrayDComplex (const vector&); virtual MArray getArrayDate (const vector&); virtual MArray getArrayString (const vector&); protected: TableExprId itsId; }; // // Class derived from TableExprGroupFuncBase for the first value in a group // // // // // // This class keeps the TableExprId of the last value in a group. // The 'getXXX' functions get the value for that TableExprId. //
        For ease of use this class is derived from TableExprGroupFirst. //
        class TableExprGroupLast: public TableExprGroupFirst { public: explicit TableExprGroupLast (TableExprNodeRep* node); virtual ~TableExprGroupLast(); virtual void apply (const TableExprId& id); }; // // Class derived from TableExprGroupFuncBase collecting the ids in a group // // // // // // This class keeps all TableExprIds in a group. // It is meant for lazy aggregation classes which use the collected // TableExprIds in their 'getXXX' functions. // class TableExprGroupExprId: public TableExprGroupFuncBase { public: explicit TableExprGroupExprId (TableExprNodeRep* node); virtual ~TableExprGroupExprId(); virtual Bool isLazy() const; virtual void apply (const TableExprId& id); virtual std::shared_ptr> getIds() const; private: std::shared_ptr> itsIds; }; // // Class collecting the rowids of entries in a group. // // // // // // This class collects the row numbers of the rows in a group. // class TableExprGroupRowid: public TableExprGroupFuncBase { public: explicit TableExprGroupRowid (TableExprNodeRep* node); virtual ~TableExprGroupRowid(); virtual Bool isLazy() const; virtual void apply (const TableExprId& id); virtual MArray getArrayInt (const vector&); }; // // Class collecting the arrays in a group. // // // // // // This class collects the non-empty arrays in a group into an array with // one more axis. All arrays (if not empty) must have the same shape. // class TableExprGroupAggr: public TableExprGroupFuncBase { public: explicit TableExprGroupAggr (TableExprNodeRep* node); virtual ~TableExprGroupAggr(); virtual Bool isLazy() const; virtual void apply (const TableExprId& id); virtual MArray getArrayBool (const vector&); virtual MArray getArrayInt (const vector&); virtual MArray getArrayDouble (const vector&); virtual MArray getArrayDComplex (const vector&); virtual MArray getArrayDate (const vector&); virtual MArray getArrayString (const vector&); protected: template MArray getArray (const vector& ids) { // Return scalar values as a Vector. if (itsOperand->valueType() == TableExprNodeRep::VTScalar) { Vector result(ids.size()); for (size_t i=0; iget (ids[i], result[i]); } return MArray(result); } // Array values are returned as an array with one more axis. // Use the first non-null value to determine the shape and if masked. MArray arr; size_t id; Bool hasMask = False; IPosition shp; for (id=0; idget (ids[id], arr); if (! arr.isNull()) { hasMask = arr.hasMask(); shp = arr.shape(); shp.append (IPosition (1, ids.size())); break; } } size_t ndef = 0; if (id == ids.size()) { // All arrays are null. return MArray(); } Array result(shp); ArrayIterator iter (result, arr.ndim()); Array mask; std::shared_ptr> miter; if (hasMask) { mask.resize (shp); miter.reset (new ArrayIterator (mask, arr.ndim())); } for (; id values; itsOperand->get (ids[id], values); if (! values.isNull()) { ndef++; iter.array() = values.array(); iter.next(); if (hasMask) { miter->array() = values.mask(); miter->next(); } } } if (ndef < ids.size()) { shp[shp.size() - 1] = ndef; result.resize (shp, True); if (hasMask) { mask.resize (shp, True); } } return MArray(result, mask); } }; // // Abstract base class for aggregate functions giving a bool scalar. // // // // // // This class is derived from TableExprGroupFuncBase and acts as the // abstract base class for aggregate functions resulting in a bool scalar. //
        Derived classes can use itsValue to contain the // aggregated value. It that case they do not need to implement the // get function. //
        class TableExprGroupFuncBool: public TableExprGroupFuncBase { public: explicit TableExprGroupFuncBool (TableExprNodeRep* node) : TableExprGroupFuncBase (node) {} TableExprGroupFuncBool (TableExprNodeRep* node, Bool initValue) : TableExprGroupFuncBase (node), itsValue (initValue) {} virtual ~TableExprGroupFuncBool(); virtual Bool getBool (const vector&); protected: Bool itsValue; }; // // Abstract base class for aggregate functions giving an integer scalar. // // // // // // This class is derived from TableExprGroupFuncBase and acts as the // abstract base class for aggregate functions resulting in an integer scalar. //
        Derived classes can use itsValue to contain the // aggregated value. It that case they do not need to implement the // get function. //
        class TableExprGroupFuncInt: public TableExprGroupFuncBase { public: explicit TableExprGroupFuncInt (TableExprNodeRep* node, Int64 initValue=0) : TableExprGroupFuncBase (node), itsValue (initValue) {} virtual ~TableExprGroupFuncInt(); virtual Int64 getInt (const vector&); virtual Double getDouble (const vector&); protected: Int64 itsValue; }; // // Abstract base class for aggregate functions giving a double scalar. // // // // // // This class is derived from TableExprGroupFuncBase and acts as the // abstract base class for aggregate functions resulting in a double scalar. //
        Derived classes can use itsValue to contain the // aggregated value. It that case they do not need to implement the // get function. //
        class TableExprGroupFuncDouble: public TableExprGroupFuncBase { public: explicit TableExprGroupFuncDouble (TableExprNodeRep* node, Double initValue = 0) : TableExprGroupFuncBase (node), itsValue (initValue) {} virtual ~TableExprGroupFuncDouble(); virtual Double getDouble (const vector&); protected: Double itsValue; }; // // Abstract base class for aggregate functions giving a dcomplex scalar. // // // // // // This class is derived from TableExprGroupFuncBase and acts as the // abstract base class for aggregate functions resulting in a dcomplex scalar. //
        Derived classes can use itsValue to contain the // aggregated value. It that case they do not need to implement the // get function. //
        class TableExprGroupFuncDComplex: public TableExprGroupFuncBase { public: explicit TableExprGroupFuncDComplex (TableExprNodeRep* node, const DComplex& initValue = DComplex()) : TableExprGroupFuncBase (node), itsValue (initValue) {} virtual ~TableExprGroupFuncDComplex(); virtual DComplex getDComplex (const vector&); protected: DComplex itsValue; }; // // Abstract base class for aggregate functions giving a date/time scalar. // // // // // // This class is derived from TableExprGroupFuncBase and acts as the // abstract base class for aggregate functions resulting in a date/time scalar. //
        Derived classes can use itsValue to contain the // aggregated value. It that case they do not need to implement the // get function. //
        class TableExprGroupFuncDate: public TableExprGroupFuncBase { public: explicit TableExprGroupFuncDate (TableExprNodeRep* node, const MVTime& initValue = MVTime()) : TableExprGroupFuncBase (node), itsValue (initValue) {} virtual ~TableExprGroupFuncDate(); virtual MVTime getDate (const vector&); protected: MVTime itsValue; }; // // Abstract base class for aggregate functions giving a string scalar. // // // // // // This class is derived from TableExprGroupFuncBase and acts as the // abstract base class for aggregate functions resulting in a string scalar. //
        Derived classes can use itsValue to contain the // aggregated value. It that case they do not need to implement the // get function. //
        class TableExprGroupFuncString: public TableExprGroupFuncBase { public: explicit TableExprGroupFuncString (TableExprNodeRep* node, const String& initValue = String()) : TableExprGroupFuncBase (node), itsValue (initValue) {} virtual ~TableExprGroupFuncString(); virtual String getString (const vector&); protected: String itsValue; }; // // Abstract base class for aggregate functions giving a bool array. // // // // // // This class is derived from TableExprGroupFuncBase and acts as the // abstract base class for aggregate functions resulting in a bool array. //
        Derived classes can use itsValue to contain the // aggregated value. It that case they do not need to implement the // get function. //
        class TableExprGroupFuncArrayBool: public TableExprGroupFuncBase { public: explicit TableExprGroupFuncArrayBool (TableExprNodeRep* node) : TableExprGroupFuncBase (node) {} virtual ~TableExprGroupFuncArrayBool(); virtual MArray getArrayBool (const vector&); protected: // If not empty, check if the shape matches that of itsValue. // If itsValue is still empty, it is sized. Bool checkShape (const MArrayBase& arr, const String& func); MArray itsValue; }; // // Abstract base class for aggregate functions giving an integer array. // // // // // // This class is derived from TableExprGroupFuncBase and acts as the // abstract base class for aggregate functions resulting in an integer array. //
        Derived classes can use itsValue to contain the // aggregated value. It that case they do not need to implement the // get function. //
        class TableExprGroupFuncArrayInt: public TableExprGroupFuncBase { public: explicit TableExprGroupFuncArrayInt (TableExprNodeRep* node) : TableExprGroupFuncBase (node) {} virtual ~TableExprGroupFuncArrayInt(); virtual MArray getArrayInt (const vector&); protected: // If not empty, check if the shape matches that of itsValue. // If itsValue is still empty, it is sized. Bool checkShape (const MArrayBase& arr, const String& func); MArray itsValue; }; // // Abstract base class for aggregate functions giving a double array. // // // // // // This class is derived from TableExprGroupFuncBase and acts as the // abstract base class for aggregate functions resulting in a double array. //
        Derived classes can use itsValue to contain the // aggregated value. It that case they do not need to implement the // get function. //
        class TableExprGroupFuncArrayDouble: public TableExprGroupFuncBase { public: explicit TableExprGroupFuncArrayDouble (TableExprNodeRep* node) : TableExprGroupFuncBase (node) {} virtual ~TableExprGroupFuncArrayDouble(); virtual MArray getArrayDouble (const vector&); protected: // If not empty, check if the shape matches that of itsValue. // If itsValue is still empty, it is sized. Bool checkShape (const MArrayBase& arr, const String& func); MArray itsValue; }; // // Abstract base class for aggregate functions giving a dcomplex array. // // // // // // This class is derived from TableExprGroupFuncBase and acts as the // abstract base class for aggregate functions resulting in a dcomplex array. //
        Derived classes can use itsValue to contain the // aggregated value. It that case they do not need to implement the // get function. //
        class TableExprGroupFuncArrayDComplex: public TableExprGroupFuncBase { public: explicit TableExprGroupFuncArrayDComplex (TableExprNodeRep* node) : TableExprGroupFuncBase (node) {} virtual ~TableExprGroupFuncArrayDComplex(); virtual MArray getArrayDComplex (const vector&); protected: // If not empty, check if the shape matches that of itsValue. // If itsValue is still empty, it is sized. Bool checkShape (const MArrayBase& arr, const String& func); MArray itsValue; }; // // Abstract base class for aggregate functions giving a date/time array. // // // // // // This class is derived from TableExprGroupFuncBase and acts as the // abstract base class for aggregate functions resulting in a date/time array. //
        Derived classes can use itsValue to contain the // aggregated value. It that case they do not need to implement the // get function. //
        class TableExprGroupFuncArrayDate: public TableExprGroupFuncBase { public: explicit TableExprGroupFuncArrayDate (TableExprNodeRep* node) : TableExprGroupFuncBase (node) {} virtual ~TableExprGroupFuncArrayDate(); virtual MArray getArrayDate (const vector&); protected: // If not empty, check if the shape matches that of itsValue. // If itsValue is still empty, it is sized. Bool checkShape (const MArrayBase& arr, const String& func); MArray itsValue; }; // // Abstract base class for aggregate functions giving a string array. // // // // // // This class is derived from TableExprGroupFuncBase and acts as the // abstract base class for aggregate functions resulting in a string array. //
        Derived classes can use itsValue to contain the // aggregated value. It that case they do not need to implement the // get function. //
        class TableExprGroupFuncArrayString: public TableExprGroupFuncBase { public: explicit TableExprGroupFuncArrayString (TableExprNodeRep* node) : TableExprGroupFuncBase (node) {} virtual ~TableExprGroupFuncArrayString(); virtual MArray getArrayString (const vector&); protected: // If not empty, check if the shape matches that of itsValue. // If itsValue is still empty, it is sized. Bool checkShape (const MArrayBase& arr, const String& func); MArray itsValue; }; // // Class containing the results of aggregated values in a group. // // // // // // This class contains the set of aggregate function objects containing // all aggregate results of a particular GROUPBY group. // It also contains the TableExprId of the last row in the group. // It is used for possible non-aggregate expressions. // class TableExprGroupFuncSet { public: TableExprGroupFuncSet() : itsId (0) {} // Let the aggregate node objects construct the function set. TableExprGroupFuncSet (const vector& aggrNodes); // Copying is not needed, thus not allowed. TableExprGroupFuncSet (const TableExprGroupFuncSet&) = delete; TableExprGroupFuncSet& operator= (const TableExprGroupFuncSet&) = delete; // Add a function object. void add (const std::shared_ptr& func); // Apply the functions to the given row. void apply (const TableExprId& id); // Get the vector of functions. const vector>& getFuncs() const { return itsFuncs; } // Get the TableExprId. const TableExprId& getId() const { return itsId; } private: //# Data members. vector> itsFuncs; TableExprId itsId; //# row containing the non-aggregate variables }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/ExprGroupAggrFunc.cc000066400000000000000000000306761476623553700212720ustar00rootroot00000000000000//# ExprGroupAggrFunc.cc: Classes for TaQL's GROUPBY clause //# Copyright (C) 2013 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableExprGroupCountAll::TableExprGroupCountAll (TableExprNodeRep* node) : TableExprGroupFuncInt (node) {} TableExprGroupCountAll::~TableExprGroupCountAll() {} void TableExprGroupCountAll::apply (const TableExprId&) { itsValue++; } TableExprGroupCount::TableExprGroupCount (TableExprNodeRep* node) : TableExprGroupFuncInt (node), itsColumn (0) { // Get the TableColumn object from the argument of the gcount node. itsColumn = dynamic_cast(itsOperand); if (!itsColumn) { TableExprNodeColumn* col = dynamic_cast(itsOperand); if (!col) { throw TableInvExpr("Argument of GCOUNT function must be a column"); } } } TableExprGroupCount::~TableExprGroupCount() {} void TableExprGroupCount::apply (const TableExprId& id) { // Add if this row contains a value. if (!itsColumn || itsColumn->isDefined(id.rownr())) { itsValue++; } } TableExprGroupAny::TableExprGroupAny (TableExprNodeRep* node) : TableExprGroupFuncBool (node, False) {} TableExprGroupAny::~TableExprGroupAny() {} void TableExprGroupAny::apply (const TableExprId& id) { Bool v = itsOperand->getBool(id); if (v) itsValue = True; } TableExprGroupAll::TableExprGroupAll (TableExprNodeRep* node) : TableExprGroupFuncBool (node, True) {} TableExprGroupAll::~TableExprGroupAll() {} void TableExprGroupAll::apply (const TableExprId& id) { Bool v = itsOperand->getBool(id); if (!v) itsValue = False; } TableExprGroupNTrue::TableExprGroupNTrue (TableExprNodeRep* node) : TableExprGroupFuncInt (node) {} TableExprGroupNTrue::~TableExprGroupNTrue() {} void TableExprGroupNTrue::apply (const TableExprId& id) { Bool v = itsOperand->getBool(id); if (v) itsValue++; } TableExprGroupNFalse::TableExprGroupNFalse (TableExprNodeRep* node) : TableExprGroupFuncInt (node) {} TableExprGroupNFalse::~TableExprGroupNFalse() {} void TableExprGroupNFalse::apply (const TableExprId& id) { Bool v = itsOperand->getBool(id); if (!v) itsValue++; } TableExprGroupMinInt::TableExprGroupMinInt (TableExprNodeRep* node) : TableExprGroupFuncInt (node, std::numeric_limits::max()) {} TableExprGroupMinInt::~TableExprGroupMinInt() {} void TableExprGroupMinInt::apply (const TableExprId& id) { Int64 v = itsOperand->getInt(id); if (v::min()) {} TableExprGroupMaxInt::~TableExprGroupMaxInt() {} void TableExprGroupMaxInt::apply (const TableExprId& id) { Int64 v = itsOperand->getInt(id); if (v>itsValue) itsValue = v; } TableExprGroupSumInt::TableExprGroupSumInt(TableExprNodeRep* node) : TableExprGroupFuncInt (node) {} TableExprGroupSumInt::~TableExprGroupSumInt() {} void TableExprGroupSumInt::apply (const TableExprId& id) { itsValue += itsOperand->getInt(id); } TableExprGroupProductInt::TableExprGroupProductInt(TableExprNodeRep* node) : TableExprGroupFuncInt (node, 1) {} TableExprGroupProductInt::~TableExprGroupProductInt() {} void TableExprGroupProductInt::apply (const TableExprId& id) { itsValue *= itsOperand->getInt(id); } TableExprGroupSumSqrInt::TableExprGroupSumSqrInt(TableExprNodeRep* node) : TableExprGroupFuncInt (node) {} TableExprGroupSumSqrInt::~TableExprGroupSumSqrInt() {} void TableExprGroupSumSqrInt::apply (const TableExprId& id) { Int64 v = itsOperand->getInt(id); itsValue += v*v; } TableExprGroupMinDouble::TableExprGroupMinDouble(TableExprNodeRep* node) : TableExprGroupFuncDouble (node, std::numeric_limits::max()) {} TableExprGroupMinDouble::~TableExprGroupMinDouble() {} void TableExprGroupMinDouble::apply (const TableExprId& id) { Double v = itsOperand->getDouble(id); if (v::min()) {} TableExprGroupMaxDouble::~TableExprGroupMaxDouble() {} void TableExprGroupMaxDouble::apply (const TableExprId& id) { Double v = itsOperand->getDouble(id); if (v>itsValue) itsValue = v; } TableExprGroupSumDouble::TableExprGroupSumDouble(TableExprNodeRep* node) : TableExprGroupFuncDouble (node) {} TableExprGroupSumDouble::~TableExprGroupSumDouble() {} void TableExprGroupSumDouble::apply (const TableExprId& id) { itsValue += itsOperand->getDouble(id); } TableExprGroupProductDouble::TableExprGroupProductDouble(TableExprNodeRep* node) : TableExprGroupFuncDouble (node, 1) {} TableExprGroupProductDouble::~TableExprGroupProductDouble() {} void TableExprGroupProductDouble::apply (const TableExprId& id) { itsValue *= itsOperand->getDouble(id); } TableExprGroupSumSqrDouble::TableExprGroupSumSqrDouble(TableExprNodeRep* node) : TableExprGroupFuncDouble (node) {} TableExprGroupSumSqrDouble::~TableExprGroupSumSqrDouble() {} void TableExprGroupSumSqrDouble::apply (const TableExprId& id) { Double v = itsOperand->getDouble(id); itsValue += v*v; } TableExprGroupMeanDouble::TableExprGroupMeanDouble(TableExprNodeRep* node) : TableExprGroupFuncDouble (node), itsNr (0) {} TableExprGroupMeanDouble::~TableExprGroupMeanDouble() {} void TableExprGroupMeanDouble::apply (const TableExprId& id) { itsValue += itsOperand->getDouble(id); itsNr++; } void TableExprGroupMeanDouble::finish() { if (itsNr > 0) { itsValue /= itsNr; } } TableExprGroupVarianceDouble::TableExprGroupVarianceDouble(TableExprNodeRep* node, uInt ddof) : TableExprGroupFuncDouble (node), itsDdof (ddof), itsNr (0), itsCurMean (0) {} TableExprGroupVarianceDouble::~TableExprGroupVarianceDouble() {} void TableExprGroupVarianceDouble::apply (const TableExprId& id) { // Calculate mean and variance in a running way using a // numerically stable algorithm // See en.wikipedia.org/wiki/Algorithms_for_calculating_variance itsNr++; Double v = itsOperand->getDouble(id); Double delta = v - itsCurMean; itsCurMean += delta/itsNr; itsValue += delta*(v-itsCurMean); // itsValue contains the M2 value } void TableExprGroupVarianceDouble::finish() { if (itsNr > itsDdof) { itsValue /= itsNr-itsDdof; } else { itsValue = 0; } } TableExprGroupStdDevDouble::TableExprGroupStdDevDouble(TableExprNodeRep* node, uInt ddof) : TableExprGroupVarianceDouble (node, ddof) {} TableExprGroupStdDevDouble::~TableExprGroupStdDevDouble() {} void TableExprGroupStdDevDouble::finish() { TableExprGroupVarianceDouble::finish(); itsValue = sqrt(itsValue); } TableExprGroupRmsDouble::TableExprGroupRmsDouble(TableExprNodeRep* node) : TableExprGroupFuncDouble (node), itsNr (0) {} TableExprGroupRmsDouble::~TableExprGroupRmsDouble() {} void TableExprGroupRmsDouble::apply (const TableExprId& id) { Double v = itsOperand->getDouble(id); itsValue += v*v; itsNr++; } void TableExprGroupRmsDouble::finish() { if (itsNr > 0) { itsValue = sqrt(itsValue / itsNr); } } TableExprGroupFractileDouble::TableExprGroupFractileDouble(TableExprNodeRep* node, Double fraction) : TableExprGroupFuncDouble (node), itsFrac (fraction) {} TableExprGroupFractileDouble::~TableExprGroupFractileDouble() {} Bool TableExprGroupFractileDouble::isLazy() const { return True; } void TableExprGroupFractileDouble::apply (const TableExprId&) {} Double TableExprGroupFractileDouble::getDouble (const vector& ids) { vector values; values.reserve (ids.size()); for (size_t i=0; igetDouble (ids[i])); } if (! values.empty()) { return GenSort::kthLargest (&(values[0]), values.size(), static_cast((values.size() - 1.)*itsFrac + 0.001)); } return 0; } TableExprGroupSumDComplex::TableExprGroupSumDComplex(TableExprNodeRep* node) : TableExprGroupFuncDComplex (node) {} TableExprGroupSumDComplex::~TableExprGroupSumDComplex() {} void TableExprGroupSumDComplex::apply (const TableExprId& id) { itsValue += itsOperand->getDComplex(id); } TableExprGroupProductDComplex::TableExprGroupProductDComplex(TableExprNodeRep* node) : TableExprGroupFuncDComplex (node, DComplex(1,0)) {} TableExprGroupProductDComplex::~TableExprGroupProductDComplex() {} void TableExprGroupProductDComplex::apply (const TableExprId& id) { itsValue *= itsOperand->getDComplex(id); } TableExprGroupSumSqrDComplex::TableExprGroupSumSqrDComplex(TableExprNodeRep* node) : TableExprGroupFuncDComplex (node) {} TableExprGroupSumSqrDComplex::~TableExprGroupSumSqrDComplex() {} void TableExprGroupSumSqrDComplex::apply (const TableExprId& id) { DComplex v = itsOperand->getDComplex(id); itsValue += v*v; } TableExprGroupMeanDComplex::TableExprGroupMeanDComplex(TableExprNodeRep* node) : TableExprGroupFuncDComplex (node), itsNr (0) {} TableExprGroupMeanDComplex::~TableExprGroupMeanDComplex() {} void TableExprGroupMeanDComplex::apply (const TableExprId& id) { itsValue += itsOperand->getDComplex(id); itsNr++; } void TableExprGroupMeanDComplex::finish() { if (itsNr > 0) { itsValue /= double(itsNr); } } TableExprGroupVarianceDComplex::TableExprGroupVarianceDComplex(TableExprNodeRep* node, uInt ddof) : TableExprGroupFuncDouble (node), itsDdof (ddof), itsNr (0) {} TableExprGroupVarianceDComplex::~TableExprGroupVarianceDComplex() {} void TableExprGroupVarianceDComplex::apply (const TableExprId& id) { // Calculate mean and variance in a running way using a // numerically stable algorithm // See en.wikipedia.org/wiki/Algorithms_for_calculating_variance itsNr++; DComplex v = itsOperand->getDComplex(id); DComplex delta = v - itsCurMean; itsCurMean += delta / Double(itsNr); DComplex d = v - itsCurMean; itsValue += real(delta)*real(d) + imag(delta)*imag(d); } void TableExprGroupVarianceDComplex::finish() { if (itsNr > itsDdof) { itsValue /= (itsNr-itsDdof); } else { itsValue = 0; } } TableExprGroupStdDevDComplex::TableExprGroupStdDevDComplex(TableExprNodeRep* node, uInt ddof) : TableExprGroupVarianceDComplex (node, ddof) {} TableExprGroupStdDevDComplex::~TableExprGroupStdDevDComplex() {} void TableExprGroupStdDevDComplex::finish() { TableExprGroupVarianceDComplex::finish(); itsValue = sqrt(itsValue); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/ExprGroupAggrFunc.h000066400000000000000000000422471476623553700211310ustar00rootroot00000000000000//# ExprGroupAggrFunc.h: The various scalar aggregation functions //# Copyright (C) 2013 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_EXPRGROUPAGGRFUNC_H #define TABLES_EXPRGROUPAGGRFUNC_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declaration class TableExprNodeArrayColumn; // // Aggregate class counting number of rows in a group // // // // // // Aggregate class counting number of rows in a group. // class TableExprGroupCountAll: public TableExprGroupFuncInt { public: explicit TableExprGroupCountAll (TableExprNodeRep* node); virtual ~TableExprGroupCountAll(); virtual void apply (const TableExprId& id); // Set result in case it is known directly. void setResult (Int64 cnt) { itsValue = cnt; } }; // // Aggregate class counting number of rows in a group containing a value // // // // // // Aggregate class counting number of rows in a group containing a value. // class TableExprGroupCount: public TableExprGroupFuncInt { public: explicit TableExprGroupCount (TableExprNodeRep* node); virtual ~TableExprGroupCount(); virtual void apply (const TableExprId& id); private: TableExprNodeArrayColumn* itsColumn; }; // // Aggregate class counting if any value in a group is true // // // // // // Aggregate class counting if any value in a group is true. // class TableExprGroupAny: public TableExprGroupFuncBool { public: explicit TableExprGroupAny (TableExprNodeRep* node); virtual ~TableExprGroupAny(); virtual void apply (const TableExprId& id); }; // // Aggregate class counting if all values in a group are true // // // // // // Aggregate class counting if all values in a group are true. // class TableExprGroupAll: public TableExprGroupFuncBool { public: explicit TableExprGroupAll (TableExprNodeRep* node); virtual ~TableExprGroupAll(); virtual void apply (const TableExprId& id); }; // // Aggregate class counting the number of true values in a group // // // // // // Aggregate class counting the number of true values in a group. // class TableExprGroupNTrue: public TableExprGroupFuncInt { public: explicit TableExprGroupNTrue (TableExprNodeRep* node); virtual ~TableExprGroupNTrue(); virtual void apply (const TableExprId& id); }; // // Aggregate class counting the number of false values in a group // // // // // // Aggregate class counting the number of false values in a group. // class TableExprGroupNFalse: public TableExprGroupFuncInt { public: explicit TableExprGroupNFalse (TableExprNodeRep* node); virtual ~TableExprGroupNFalse(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the minimum integer value in a group // // // // // // Aggregate class determining the minimum integer value in a group. // class TableExprGroupMinInt: public TableExprGroupFuncInt { public: explicit TableExprGroupMinInt (TableExprNodeRep* node); virtual ~TableExprGroupMinInt(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the maximum integer value in a group // // // // // // Aggregate class determining the maximum integer value in a group. // class TableExprGroupMaxInt: public TableExprGroupFuncInt { public: explicit TableExprGroupMaxInt (TableExprNodeRep* node); virtual ~TableExprGroupMaxInt(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the sum of integer values in a group // // // // // // Aggregate class determining the sum of integer values in a group. // class TableExprGroupSumInt: public TableExprGroupFuncInt { public: explicit TableExprGroupSumInt (TableExprNodeRep* node); virtual ~TableExprGroupSumInt(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the product of integer values in a group // // // // // // Aggregate class determining the product of integer values in a group. // class TableExprGroupProductInt: public TableExprGroupFuncInt { public: explicit TableExprGroupProductInt (TableExprNodeRep* node); virtual ~TableExprGroupProductInt(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the sum of squares of integer values in a group // // // // // // Aggregate class determining the sum of squares of integer values in a group. // class TableExprGroupSumSqrInt: public TableExprGroupFuncInt { public: explicit TableExprGroupSumSqrInt (TableExprNodeRep* node); virtual ~TableExprGroupSumSqrInt(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the minimum double value in a group // // // // // // Aggregate class determining the minimum double value in a group. // class TableExprGroupMinDouble: public TableExprGroupFuncDouble { public: explicit TableExprGroupMinDouble (TableExprNodeRep* node); virtual ~TableExprGroupMinDouble(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the maximum double value in a group // // // // // // Aggregate class determining the maximum double value in a group. // class TableExprGroupMaxDouble: public TableExprGroupFuncDouble { public: explicit TableExprGroupMaxDouble (TableExprNodeRep* node); virtual ~TableExprGroupMaxDouble(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the sum of double values in a group // // // // // // Aggregate class determining the sum of double values in a group. // class TableExprGroupSumDouble: public TableExprGroupFuncDouble { public: explicit TableExprGroupSumDouble (TableExprNodeRep* node); virtual ~TableExprGroupSumDouble(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the product of double values in a group // // // // // // Aggregate class determining the product of double values in a group. // class TableExprGroupProductDouble: public TableExprGroupFuncDouble { public: explicit TableExprGroupProductDouble (TableExprNodeRep* node); virtual ~TableExprGroupProductDouble(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the sum of squares of double values in a group // // // // // // Aggregate class determining the sum of squares of double values in a group. // class TableExprGroupSumSqrDouble: public TableExprGroupFuncDouble { public: explicit TableExprGroupSumSqrDouble (TableExprNodeRep* node); virtual ~TableExprGroupSumSqrDouble(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the mean of values in a group // // // // // // Aggregate class determining the mean of values in a group. // class TableExprGroupMeanDouble: public TableExprGroupFuncDouble { public: explicit TableExprGroupMeanDouble (TableExprNodeRep* node); virtual ~TableExprGroupMeanDouble(); virtual void apply (const TableExprId& id); virtual void finish(); private: Int64 itsNr; }; // // Aggregate class determining the variance of values in a group // // // // // // Aggregate class determining the variance of values in a group. // It uses a running algorithm // (see en.wikipedia.org/wiki/Algorithms_for_calculating_variance) // class TableExprGroupVarianceDouble: public TableExprGroupFuncDouble { public: explicit TableExprGroupVarianceDouble (TableExprNodeRep* node, uInt ddof); virtual ~TableExprGroupVarianceDouble(); virtual void apply (const TableExprId& id); virtual void finish(); protected: uInt itsDdof; Int64 itsNr; Double itsCurMean; }; // // Aggregate class determining the standard deviation of values in a group // // // // // // Aggregate class determining the standard deviation of values in a group. // It uses a running algorithm // (see en.wikipedia.org/wiki/Algorithms_for_calculating_variance) // class TableExprGroupStdDevDouble: public TableExprGroupVarianceDouble { public: explicit TableExprGroupStdDevDouble (TableExprNodeRep* node, uInt ddof); virtual ~TableExprGroupStdDevDouble(); virtual void finish(); }; // // Aggregate class determining the RMS of values in a group // // // // // // Aggregate class determining the RMS of values in a group. // class TableExprGroupRmsDouble: public TableExprGroupFuncDouble { public: explicit TableExprGroupRmsDouble (TableExprNodeRep* node); virtual ~TableExprGroupRmsDouble(); virtual void apply (const TableExprId& id); virtual void finish(); private: Int64 itsNr; }; // // Aggregate class determining the fractile of values in a group // // // // // // Aggregate class determining the fractile of values in a group. //
        It is a lazy aggregate class, thus apply does nothing. // Instead, getDouble assembles the values and determines the // fractile. //
        class TableExprGroupFractileDouble: public TableExprGroupFuncDouble { public: explicit TableExprGroupFractileDouble (TableExprNodeRep* node, Double fractile); virtual ~TableExprGroupFractileDouble(); virtual Bool isLazy() const; virtual void apply (const TableExprId& id); virtual Double getDouble (const vector& ids); private: Double itsFrac; }; // // Aggregate class determining the sum of complex values in a group // // // // // // Aggregate class determining the sum of complex values in a group. // class TableExprGroupSumDComplex: public TableExprGroupFuncDComplex { public: explicit TableExprGroupSumDComplex (TableExprNodeRep* node); virtual ~TableExprGroupSumDComplex(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the product of complex values in a group // // // // // // Aggregate class determining the product of complex values in a group. // class TableExprGroupProductDComplex: public TableExprGroupFuncDComplex { public: explicit TableExprGroupProductDComplex (TableExprNodeRep* node); virtual ~TableExprGroupProductDComplex(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the sum of squares of complex values in a group // // // // // // Aggregate class determining the sum of squares of complex values in a group. // class TableExprGroupSumSqrDComplex: public TableExprGroupFuncDComplex { public: explicit TableExprGroupSumSqrDComplex (TableExprNodeRep* node); virtual ~TableExprGroupSumSqrDComplex(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the mean of complex values in a group // // // // // // Aggregate class determining the mean of complex values in a group. // class TableExprGroupMeanDComplex: public TableExprGroupFuncDComplex { public: explicit TableExprGroupMeanDComplex (TableExprNodeRep* node); virtual ~TableExprGroupMeanDComplex(); virtual void apply (const TableExprId& id); virtual void finish(); private: Int64 itsNr; }; // // Aggregate class determining the variance of values in a group // // // // // // Aggregate class determining the variance of values in a group. // It uses a running algorithm // (see en.wikipedia.org/wiki/Algorithms_for_calculating_variance) // Note that the result is a Double value (not DComplex). // class TableExprGroupVarianceDComplex: public TableExprGroupFuncDouble { public: explicit TableExprGroupVarianceDComplex (TableExprNodeRep* node, uInt ddof); virtual ~TableExprGroupVarianceDComplex(); virtual void apply (const TableExprId& id); virtual void finish(); protected: uInt itsDdof; Int64 itsNr; DComplex itsCurMean; }; // // Aggregate class determining the standard deviation of values in a group // // // // // // Aggregate class determining the standard deviation of values in a group. // It uses a running algorithm // (see en.wikipedia.org/wiki/Algorithms_for_calculating_variance) // class TableExprGroupStdDevDComplex: public TableExprGroupVarianceDComplex { public: explicit TableExprGroupStdDevDComplex (TableExprNodeRep* node, uInt ddof); virtual ~TableExprGroupStdDevDComplex(); virtual void finish(); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/ExprGroupAggrFuncArray.cc000066400000000000000000001272741476623553700222720ustar00rootroot00000000000000//# ExprGroupAggrFuncArray.cc: The various array reduction aggregation functions //# Copyright (C) 2013 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Clear value is masked off. template void TEGClearMasked (MArray& arr) { if (arr.hasMask()) { Array::const_contiter m = arr.mask().cbegin(); for (typename Array::contiter p = arr.array().cbegin(); p != arr.array().cend(); ++p, ++m) { if (*m) *p = T(); } } } template void TEGMin (const MArray& src, MArray& dst) { typename Array::const_iterator in = src.array().begin(); if (src.hasMask()) { typename Array::const_iterator min = src.mask().begin(); typename Array::contiter mout = dst.wmask().cbegin(); for (typename Array::contiter out = dst.array().cbegin(); out != dst.array().cend(); ++in, ++min, ++out, ++mout) { if (! *min) { *mout = False; if (*in < *out) *out = *in; } } } else { for (typename Array::contiter out = dst.array().cbegin(); out != dst.array().cend(); ++in, ++out) { if (*in < *out) *out = *in; } } } template void TEGMax (const MArray& src, MArray& dst) { typename Array::const_iterator in = src.array().begin(); if (src.hasMask()) { typename Array::const_iterator min = src.mask().begin(); typename Array::contiter mout = dst.wmask().cbegin(); for (typename Array::contiter out = dst.array().cbegin(); out != dst.array().cend(); ++in, ++min, ++out, ++mout) { if (! *min) { *mout = False; if (*in > *out) *out = *in; } } } else { for (typename Array::contiter out = dst.array().cbegin(); out != dst.array().cend(); ++in, ++out) { if (*in > *out) *out = *in; } } } template void TEGSum (const MArray& src, MArray& dst) { if (src.hasMask()) { typename Array::const_iterator in = src.array().begin(); typename Array::const_iterator min = src.mask().begin(); typename Array::contiter mout = dst.wmask().cbegin(); for (typename Array::contiter out = dst.array().cbegin(); out != dst.array().cend(); ++in, ++min, ++out, ++mout) { if (! *min) { *mout = False; *out += *in; } } } else { dst.array() += src.array(); } } template void TEGProduct (const MArray& src, MArray& dst) { if (src.hasMask()) { typename Array::const_iterator in = src.array().begin(); typename Array::const_iterator min = src.mask().begin(); typename Array::contiter mout = dst.wmask().cbegin(); for (typename Array::contiter out = dst.array().cbegin(); out != dst.array().cend(); ++in, ++min, ++out, ++mout) { if (! *min) { *mout = False; *out *= *in; } } } else { dst.array() *= src.array(); } } template void TEGSumSqr (const MArray& src, MArray& dst) { if (src.hasMask()) { typename Array::const_iterator in = src.array().begin(); typename Array::const_iterator min = src.mask().begin(); typename Array::contiter mout = dst.wmask().cbegin(); for (typename Array::contiter out = dst.array().cbegin(); out != dst.array().cend(); ++in, ++min, ++out, ++mout) { if (! *min) { *mout = False; *out += *in * *in; } } } else { typename Array::const_iterator in = src.array().begin(); for (typename Array::contiter out = dst.array().cbegin(); out != dst.array().cend(); ++in, ++out) { *out += *in * *in; } } } template void TEGMeanAdd (const MArray& src, Array& dst, Array& nr) { typename Array::contiter itn = nr.cbegin(); if (src.hasMask()) { typename Array::const_iterator in = src.array().begin(); typename Array::const_iterator min = src.mask().begin(); for (typename Array::contiter out = dst.cbegin(); out != dst.cend(); ++in, ++min, ++out, ++itn) { if (! *min) { *out += *in; (*itn)++; } } } else { typename Array::const_iterator in = src.array().begin(); for (typename Array::contiter out = dst.cbegin(); out != dst.cend(); ++in, ++out, ++itn) { *out += *in; (*itn)++; } } } template void TEGMeanFinish (MArray& val, const Array& nr) { DebugAssert (nr.contiguousStorage() && val.array().contiguousStorage(), AipsError); typename Array::contiter itv = val.array().cbegin(); typename Array::contiter itm = val.wmask().cbegin(); for (Array::const_contiter itn = nr.cbegin(); itn != nr.cend(); ++itn, ++itv, ++itm) { if (*itn > 0) { *itv /= *itn; } else if (val.hasMask()) { *itm = True; } } } TableExprGroupArrayAny::TableExprGroupArrayAny(TableExprNodeRep* node) : TableExprGroupFuncBool (node, False) {} TableExprGroupArrayAny::~TableExprGroupArrayAny() {} void TableExprGroupArrayAny::apply (const TableExprId& id) { if (!itsValue) { Bool v = anyTrue (itsOperand->getArrayBool(id)); if (v) itsValue = True; } } TableExprGroupArrayAll::TableExprGroupArrayAll(TableExprNodeRep* node) : TableExprGroupFuncBool (node, True) {} TableExprGroupArrayAll::~TableExprGroupArrayAll() {} void TableExprGroupArrayAll::apply (const TableExprId& id) { if (itsValue) { Bool v = allTrue (itsOperand->getArrayBool(id)); if (!v) itsValue = False; } } TableExprGroupArrayNTrue::TableExprGroupArrayNTrue(TableExprNodeRep* node) : TableExprGroupFuncInt (node) {} TableExprGroupArrayNTrue::~TableExprGroupArrayNTrue() {} void TableExprGroupArrayNTrue::apply (const TableExprId& id) { itsValue += ntrue (itsOperand->getArrayBool(id)); } TableExprGroupArrayNFalse::TableExprGroupArrayNFalse(TableExprNodeRep* node) : TableExprGroupFuncInt (node) {} TableExprGroupArrayNFalse::~TableExprGroupArrayNFalse() {} void TableExprGroupArrayNFalse::apply (const TableExprId& id) { itsValue += nfalse (itsOperand->getArrayBool(id)); } TableExprGroupMinArrayInt::TableExprGroupMinArrayInt(TableExprNodeRep* node) : TableExprGroupFuncInt (node, std::numeric_limits::max()) {} TableExprGroupMinArrayInt::~TableExprGroupMinArrayInt() {} void TableExprGroupMinArrayInt::apply (const TableExprId& id) { MArray arr = itsOperand->getArrayInt(id); if (! arr.empty()) { Int64 v = min(arr); if (v::min()) {} TableExprGroupMaxArrayInt::~TableExprGroupMaxArrayInt() {} void TableExprGroupMaxArrayInt::apply (const TableExprId& id) { MArray arr = itsOperand->getArrayInt(id); if (! arr.empty()) { Int64 v = max(arr); if (v>itsValue) itsValue = v; } } TableExprGroupSumArrayInt::TableExprGroupSumArrayInt(TableExprNodeRep* node) : TableExprGroupFuncInt (node) {} TableExprGroupSumArrayInt::~TableExprGroupSumArrayInt() {} void TableExprGroupSumArrayInt::apply (const TableExprId& id) { itsValue += sum(itsOperand->getArrayInt(id)); } TableExprGroupProductArrayInt::TableExprGroupProductArrayInt(TableExprNodeRep* node) : TableExprGroupFuncInt (node, 1) {} TableExprGroupProductArrayInt::~TableExprGroupProductArrayInt() {} void TableExprGroupProductArrayInt::apply (const TableExprId& id) { MArray arr = itsOperand->getArrayInt(id); if (! arr.empty()) { itsValue *= product(arr); } } TableExprGroupSumSqrArrayInt::TableExprGroupSumSqrArrayInt(TableExprNodeRep* node) : TableExprGroupFuncInt (node) {} TableExprGroupSumSqrArrayInt::~TableExprGroupSumSqrArrayInt() {} void TableExprGroupSumSqrArrayInt::apply (const TableExprId& id) { MArray arr = itsOperand->getArrayInt(id); itsValue += sum(arr*arr); } TableExprGroupMinArrayDouble::TableExprGroupMinArrayDouble(TableExprNodeRep* node) : TableExprGroupFuncDouble (node, std::numeric_limits::max()) {} TableExprGroupMinArrayDouble::~TableExprGroupMinArrayDouble() {} void TableExprGroupMinArrayDouble::apply (const TableExprId& id) { MArray arr = itsOperand->getArrayDouble(id); if (! arr.empty()) { Double v = min(arr); if (v::min()) {} TableExprGroupMaxArrayDouble::~TableExprGroupMaxArrayDouble() {} void TableExprGroupMaxArrayDouble::apply (const TableExprId& id) { MArray arr = itsOperand->getArrayDouble(id); if (! arr.empty()) { Double v = max(arr); if (v>itsValue) itsValue = v; } } TableExprGroupSumArrayDouble::TableExprGroupSumArrayDouble(TableExprNodeRep* node) : TableExprGroupFuncDouble (node) {} TableExprGroupSumArrayDouble::~TableExprGroupSumArrayDouble() {} void TableExprGroupSumArrayDouble::apply (const TableExprId& id) { itsValue += sum(itsOperand->getArrayDouble(id)); } TableExprGroupProductArrayDouble::TableExprGroupProductArrayDouble(TableExprNodeRep* node) : TableExprGroupFuncDouble (node, 1) {} TableExprGroupProductArrayDouble::~TableExprGroupProductArrayDouble() {} void TableExprGroupProductArrayDouble::apply (const TableExprId& id) { MArray arr = itsOperand->getArrayDouble(id); if (! arr.empty()) { itsValue *= product(arr); } } TableExprGroupSumSqrArrayDouble::TableExprGroupSumSqrArrayDouble(TableExprNodeRep* node) : TableExprGroupFuncDouble (node) {} TableExprGroupSumSqrArrayDouble::~TableExprGroupSumSqrArrayDouble() {} void TableExprGroupSumSqrArrayDouble::apply (const TableExprId& id) { MArray arr = itsOperand->getArrayDouble(id); itsValue += sum(arr*arr); } TableExprGroupMeanArrayDouble::TableExprGroupMeanArrayDouble(TableExprNodeRep* node) : TableExprGroupFuncDouble (node), itsNr (0) {} TableExprGroupMeanArrayDouble::~TableExprGroupMeanArrayDouble() {} void TableExprGroupMeanArrayDouble::apply (const TableExprId& id) { MArray arr = itsOperand->getArrayDouble(id); itsValue += sum(arr); if (arr.hasMask()) { itsNr += nfalse(arr.mask()); } else { itsNr += arr.size(); } } void TableExprGroupMeanArrayDouble::finish() { if (itsNr > 0) { itsValue /= itsNr; } } TableExprGroupVarianceArrayDouble::TableExprGroupVarianceArrayDouble(TableExprNodeRep* node, uInt ddof) : TableExprGroupFuncDouble (node), itsDdof (ddof), itsNr (0), itsCurMean (0) {} TableExprGroupVarianceArrayDouble::~TableExprGroupVarianceArrayDouble() {} void TableExprGroupVarianceArrayDouble::apply (const TableExprId& id) { // Calculate mean and variance in a running way using a // numerically stable algorithm // See en.wikipedia.org/wiki/Algorithms_for_calculating_variance MArray arr = itsOperand->getArrayDouble(id); if (! arr.empty()) { Array::const_iterator in = arr.array().begin(); if (arr.hasMask()) { Array::const_iterator min = arr.mask().begin(); for (size_t i=0; i itsDdof) { itsValue /= itsNr-itsDdof; } else { itsValue = 0; } } TableExprGroupStdDevArrayDouble::TableExprGroupStdDevArrayDouble(TableExprNodeRep* node, uInt ddof) : TableExprGroupVarianceArrayDouble (node, ddof) {} TableExprGroupStdDevArrayDouble::~TableExprGroupStdDevArrayDouble() {} void TableExprGroupStdDevArrayDouble::finish() { TableExprGroupVarianceArrayDouble::finish(); itsValue = sqrt(itsValue); } TableExprGroupRmsArrayDouble::TableExprGroupRmsArrayDouble(TableExprNodeRep* node) : TableExprGroupFuncDouble (node), itsNr (0) {} TableExprGroupRmsArrayDouble::~TableExprGroupRmsArrayDouble() {} void TableExprGroupRmsArrayDouble::apply (const TableExprId& id) { MArray arr = itsOperand->getArrayDouble(id); itsValue += sum(arr*arr); if (arr.hasMask()) { itsNr += nfalse(arr.mask()); } else { itsNr += arr.size(); } } void TableExprGroupRmsArrayDouble::finish() { if (itsNr > 0) { itsValue = sqrt(itsValue / itsNr); } } TableExprGroupFractileArrayDouble::TableExprGroupFractileArrayDouble(TableExprNodeRep* node, Double fraction) : TableExprGroupFuncDouble (node), itsFrac (fraction) {} TableExprGroupFractileArrayDouble::~TableExprGroupFractileArrayDouble() {} Bool TableExprGroupFractileArrayDouble::isLazy() const { return True; } void TableExprGroupFractileArrayDouble::apply (const TableExprId&) {} Double TableExprGroupFractileArrayDouble::getDouble (const vector& ids) { try { if (ids.empty()) { return 0; } // All arrays have to be combined in a single vector. // Get first array to estimate the total size. size_t nr = 0; MArray arr0 = itsOperand->getArrayDouble(ids[0]); std::vector values(ids.size() * arr0.size()); nr += arr0.flatten (&(values[0]), values.size()); for (size_t i=1; i arr = itsOperand->getArrayDouble(ids[i]); if (arr.size() > values.size()-nr) { values.resize (values.size() + arr.size()); } nr += arr.flatten (&(values[0]) + nr, values.size()-nr); } return GenSort::kthLargest (&(values[0]), nr, static_cast((nr - 1.)*itsFrac + 0.001)); } catch (const std::exception& x) { throw TableInvExpr ("Cannot compute gfractile; " "probably too many data - " + String(x.what())); } } TableExprGroupSumArrayDComplex::TableExprGroupSumArrayDComplex(TableExprNodeRep* node) : TableExprGroupFuncDComplex (node) {} TableExprGroupSumArrayDComplex::~TableExprGroupSumArrayDComplex() {} void TableExprGroupSumArrayDComplex::apply (const TableExprId& id) { itsValue += sum(itsOperand->getArrayDComplex(id)); } TableExprGroupProductArrayDComplex::TableExprGroupProductArrayDComplex(TableExprNodeRep* node) : TableExprGroupFuncDComplex (node, DComplex(1,0)) {} TableExprGroupProductArrayDComplex::~TableExprGroupProductArrayDComplex() {} void TableExprGroupProductArrayDComplex::apply (const TableExprId& id) { MArray arr = itsOperand->getArrayDComplex(id); if (! arr.empty()) { itsValue *= product(arr); } } TableExprGroupSumSqrArrayDComplex::TableExprGroupSumSqrArrayDComplex(TableExprNodeRep* node) : TableExprGroupFuncDComplex (node) {} TableExprGroupSumSqrArrayDComplex::~TableExprGroupSumSqrArrayDComplex() {} void TableExprGroupSumSqrArrayDComplex::apply (const TableExprId& id) { MArray arr = itsOperand->getArrayDComplex(id); itsValue += sum(arr*arr); } TableExprGroupMeanArrayDComplex::TableExprGroupMeanArrayDComplex(TableExprNodeRep* node) : TableExprGroupFuncDComplex (node), itsNr (0) {} TableExprGroupMeanArrayDComplex::~TableExprGroupMeanArrayDComplex() {} void TableExprGroupMeanArrayDComplex::apply (const TableExprId& id) { MArray arr = itsOperand->getArrayDComplex(id); itsValue += sum(arr); if (arr.hasMask()) { itsNr += nfalse(arr.mask()); } else { itsNr += arr.size(); } } void TableExprGroupMeanArrayDComplex::finish() { if (itsNr > 0) { itsValue /= double(itsNr); } } TableExprGroupVarianceArrayDComplex::TableExprGroupVarianceArrayDComplex(TableExprNodeRep* node, uInt ddof) : TableExprGroupFuncDouble (node), itsDdof (ddof), itsNr (0) {} TableExprGroupVarianceArrayDComplex::~TableExprGroupVarianceArrayDComplex() {} void TableExprGroupVarianceArrayDComplex::apply (const TableExprId& id) { // Calculate mean and variance in a running way using a // numerically stable algorithm // See en.wikipedia.org/wiki/Algorithms_for_calculating_variance MArray arr = itsOperand->getArrayDComplex(id); if (! arr.empty()) { Array::const_iterator in = arr.array().begin(); if (arr.hasMask()) { Array::const_iterator min = arr.mask().begin(); for (size_t i=0; i itsDdof) { itsValue /= itsNr-itsDdof; } else { itsValue = 0; } } TableExprGroupStdDevArrayDComplex::TableExprGroupStdDevArrayDComplex(TableExprNodeRep* node, uInt ddof) : TableExprGroupVarianceArrayDComplex (node, ddof) {} TableExprGroupStdDevArrayDComplex::~TableExprGroupStdDevArrayDComplex() {} void TableExprGroupStdDevArrayDComplex::finish() { TableExprGroupVarianceArrayDComplex::finish(); itsValue = sqrt(itsValue); } TableExprGroupArrayAnys::TableExprGroupArrayAnys(TableExprNodeRep* node) : TableExprGroupFuncArrayBool (node) {} TableExprGroupArrayAnys::~TableExprGroupArrayAnys() {} void TableExprGroupArrayAnys::apply (const TableExprId& id) { MArray arr(itsOperand->getArrayBool(id)); if (! arr.empty()) { if (checkShape (arr, "GANYS")) { itsValue.array() = arr.array(); itsValue.wmask() = arr.mask(); } else if (arr.hasMask()) { Array::const_iterator in = arr.array().begin(); Array::const_iterator min = arr.mask().begin(); Array::contiter mout = itsValue.wmask().cbegin(); for (Array::contiter out = itsValue.array().cbegin(); out != itsValue.array().cend(); ++in, ++min, ++out, ++mout) { if (! *min) { *mout = False; *out = *out || *in; } } } else { Array::const_iterator in = arr.array().begin(); for (Array::contiter out = itsValue.array().cbegin(); out != itsValue.array().cend(); ++in, ++out) { *out = *out || *in; } } } } TableExprGroupArrayAlls::TableExprGroupArrayAlls(TableExprNodeRep* node) : TableExprGroupFuncArrayBool (node) {} TableExprGroupArrayAlls::~TableExprGroupArrayAlls() {} void TableExprGroupArrayAlls::apply (const TableExprId& id) { MArray arr(itsOperand->getArrayBool(id)); if (! arr.empty()) { if (checkShape (arr, "GALLS")) { itsValue.array() = arr.array(); itsValue.wmask() = arr.mask(); } else if (arr.hasMask()) { Array::const_iterator in = arr.array().begin(); Array::const_iterator min = arr.mask().begin(); Array::contiter mout = itsValue.wmask().cbegin(); for (Array::contiter out = itsValue.array().cbegin(); out != itsValue.array().cend(); ++in, ++min, ++out, ++mout) { if (! *min) { *mout = False; *out = *out && *in; } } } else { Array::const_iterator in = arr.array().begin(); for (Array::contiter out = itsValue.array().cbegin(); out != itsValue.array().cend(); ++in, ++out) { *out = *out && *in; } } } } TableExprGroupArrayNTrues::TableExprGroupArrayNTrues(TableExprNodeRep* node) : TableExprGroupFuncArrayInt (node) {} TableExprGroupArrayNTrues::~TableExprGroupArrayNTrues() {} void TableExprGroupArrayNTrues::apply (const TableExprId& id) { MArray arr(itsOperand->getArrayBool(id)); if (! arr.empty()) { if (checkShape (arr, "GNTRUES")) { itsValue.array() = 0; itsValue.wmask() = True; } if (arr.hasMask()) { Array::const_iterator in = arr.array().begin(); Array::const_iterator min = arr.mask().begin(); Array::contiter mout = itsValue.wmask().cbegin(); for (Array::contiter out = itsValue.array().cbegin(); out != itsValue.array().cend(); ++in, ++min, ++out, ++mout) { if (! *min) { *mout = False; if (*in) { (*out)++; } } } } else { Array::const_iterator in = arr.array().begin(); for (Array::contiter out = itsValue.array().cbegin(); out != itsValue.array().cend(); ++in, ++out) { if (*in) { (*out)++; } } } } } TableExprGroupArrayNFalses::TableExprGroupArrayNFalses(TableExprNodeRep* node) : TableExprGroupFuncArrayInt (node) {} TableExprGroupArrayNFalses::~TableExprGroupArrayNFalses() {} void TableExprGroupArrayNFalses::apply (const TableExprId& id) { MArray arr(itsOperand->getArrayBool(id)); if (! arr.empty()) { if (checkShape (arr, "GNFALSES")) { itsValue.array() = 0; itsValue.wmask() = True; } if (arr.hasMask()) { Array::const_iterator in = arr.array().begin(); Array::const_iterator min = arr.mask().begin(); Array::contiter mout = itsValue.wmask().cbegin(); for (Array::contiter out = itsValue.array().cbegin(); out != itsValue.array().cend(); ++in, ++min, ++out, ++mout) { if (! *min) { *mout = False; if (! *in) { (*out)++; } } } } else { Array::const_iterator in = arr.array().begin(); for (Array::contiter out = itsValue.array().cbegin(); out != itsValue.array().cend(); ++in, ++out) { if (! *in) { (*out)++; } } } } } TableExprGroupMinsArrayInt::TableExprGroupMinsArrayInt(TableExprNodeRep* node) : TableExprGroupFuncArrayInt (node) {} TableExprGroupMinsArrayInt::~TableExprGroupMinsArrayInt() {} void TableExprGroupMinsArrayInt::apply (const TableExprId& id) { MArray arr(itsOperand->getArrayInt(id)); if (! arr.empty()) { if (checkShape (arr, "GMINS")) { itsValue.array() = std::numeric_limits::max(); itsValue.wmask() = True; } TEGMin (arr, itsValue); } } void TableExprGroupMinsArrayInt::finish() { TEGClearMasked (itsValue); } TableExprGroupMaxsArrayInt::TableExprGroupMaxsArrayInt(TableExprNodeRep* node) : TableExprGroupFuncArrayInt (node) {} TableExprGroupMaxsArrayInt::~TableExprGroupMaxsArrayInt() {} void TableExprGroupMaxsArrayInt::apply (const TableExprId& id) { MArray arr(itsOperand->getArrayInt(id)); if (! arr.empty()) { if (checkShape (arr, "GMAXS")) { itsValue.array() = std::numeric_limits::min(); itsValue.wmask() = True; } TEGMax (arr, itsValue); } } void TableExprGroupMaxsArrayInt::finish() { TEGClearMasked (itsValue); } TableExprGroupSumsArrayInt::TableExprGroupSumsArrayInt(TableExprNodeRep* node) : TableExprGroupFuncArrayInt (node) {} TableExprGroupSumsArrayInt::~TableExprGroupSumsArrayInt() {} void TableExprGroupSumsArrayInt::apply (const TableExprId& id) { MArray arr = itsOperand->getArrayInt(id); if (! arr.empty()) { if (checkShape (arr, "GSUMS")) { itsValue.array() = 0; itsValue.wmask() = True; } TEGSum (arr, itsValue); } } TableExprGroupProductsArrayInt::TableExprGroupProductsArrayInt(TableExprNodeRep* node) : TableExprGroupFuncArrayInt (node) {} TableExprGroupProductsArrayInt::~TableExprGroupProductsArrayInt() {} void TableExprGroupProductsArrayInt::apply (const TableExprId& id) { MArray arr = itsOperand->getArrayInt(id); if (! arr.empty()) { if (checkShape (arr, "GPRODUCTS")) { itsValue.array() = 1; itsValue.wmask() = True; } TEGProduct (arr, itsValue); } } void TableExprGroupProductsArrayInt::finish() { TEGClearMasked (itsValue); } TableExprGroupSumSqrsArrayInt::TableExprGroupSumSqrsArrayInt(TableExprNodeRep* node) : TableExprGroupFuncArrayInt (node) {} TableExprGroupSumSqrsArrayInt::~TableExprGroupSumSqrsArrayInt() {} void TableExprGroupSumSqrsArrayInt::apply (const TableExprId& id) { MArray arr = itsOperand->getArrayInt(id); if (! arr.empty()) { if (checkShape (arr, "GSUMSQRS")) { itsValue.array() = 0; itsValue.wmask() = True; } TEGSumSqr (arr, itsValue); } } TableExprGroupMinsArrayDouble::TableExprGroupMinsArrayDouble(TableExprNodeRep* node) : TableExprGroupFuncArrayDouble (node) {} TableExprGroupMinsArrayDouble::~TableExprGroupMinsArrayDouble() {} void TableExprGroupMinsArrayDouble::apply (const TableExprId& id) { MArray arr(itsOperand->getArrayDouble(id)); if (! arr.empty()) { if (checkShape (arr, "GMINS")) { itsValue.array() = std::numeric_limits::max(); itsValue.wmask() = True; } TEGMin (arr, itsValue); } } void TableExprGroupMinsArrayDouble::finish() { TEGClearMasked (itsValue); } TableExprGroupMaxsArrayDouble::TableExprGroupMaxsArrayDouble(TableExprNodeRep* node) : TableExprGroupFuncArrayDouble (node) {} TableExprGroupMaxsArrayDouble::~TableExprGroupMaxsArrayDouble() {} void TableExprGroupMaxsArrayDouble::apply (const TableExprId& id) { MArray arr = itsOperand->getArrayDouble(id); if (! arr.empty()) { if (checkShape (arr, "GMAXS")) { itsValue.array() = std::numeric_limits::min(); itsValue.wmask() = True; } TEGMax (arr, itsValue); } } void TableExprGroupMaxsArrayDouble::finish() { TEGClearMasked (itsValue); } TableExprGroupSumsArrayDouble::TableExprGroupSumsArrayDouble(TableExprNodeRep* node) : TableExprGroupFuncArrayDouble (node) {} TableExprGroupSumsArrayDouble::~TableExprGroupSumsArrayDouble() {} void TableExprGroupSumsArrayDouble::apply (const TableExprId& id) { MArray arr = itsOperand->getArrayDouble(id); if (! arr.empty()) { if (checkShape (arr, "GSUMS")) { itsValue.array() = 0; itsValue.wmask() = True; } TEGSum (arr, itsValue); } } TableExprGroupProductsArrayDouble::TableExprGroupProductsArrayDouble(TableExprNodeRep* node) : TableExprGroupFuncArrayDouble (node) {} TableExprGroupProductsArrayDouble::~TableExprGroupProductsArrayDouble() {} void TableExprGroupProductsArrayDouble::apply (const TableExprId& id) { MArray arr = itsOperand->getArrayDouble(id); if (! arr.empty()) { if (checkShape (arr, "GPRODUCTS")) { itsValue.array() = 1; itsValue.wmask() = True; } TEGProduct (arr, itsValue); } } void TableExprGroupProductsArrayDouble::finish() { TEGClearMasked (itsValue); } TableExprGroupSumSqrsArrayDouble::TableExprGroupSumSqrsArrayDouble(TableExprNodeRep* node) : TableExprGroupFuncArrayDouble (node) {} TableExprGroupSumSqrsArrayDouble::~TableExprGroupSumSqrsArrayDouble() {} void TableExprGroupSumSqrsArrayDouble::apply (const TableExprId& id) { MArray arr = itsOperand->getArrayDouble(id); if (! arr.empty()) { if (checkShape (arr, "GSUMSQRS")) { itsValue.array() = 0; itsValue.wmask() = True; } TEGSumSqr (arr, itsValue); } } TableExprGroupMeansArrayDouble::TableExprGroupMeansArrayDouble(TableExprNodeRep* node) : TableExprGroupFuncArrayDouble (node) {} TableExprGroupMeansArrayDouble::~TableExprGroupMeansArrayDouble() {} void TableExprGroupMeansArrayDouble::apply (const TableExprId& id) { MArray arr = itsOperand->getArrayDouble(id); if (! arr.empty()) { if (checkShape (arr, "GMEANS")) { itsValue.array() = 0; itsValue.wmask() = False; itsNr.resize (arr.shape()); itsNr = 0; } TEGMeanAdd (arr, itsValue.array(), itsNr); } } void TableExprGroupMeansArrayDouble::finish() { TEGMeanFinish (itsValue, itsNr); } TableExprGroupVariancesArrayDouble::TableExprGroupVariancesArrayDouble(TableExprNodeRep* node, uInt ddof) : TableExprGroupFuncArrayDouble (node), itsDdof (ddof) {} TableExprGroupVariancesArrayDouble::~TableExprGroupVariancesArrayDouble() {} void TableExprGroupVariancesArrayDouble::apply (const TableExprId& id) { // Calculate mean and variance in a running way using a // numerically stable algorithm. // See en.wikipedia.org/wiki/Algorithms_for_calculating_variance MArray arr = itsOperand->getArrayDouble(id); if (! arr.empty()) { if (checkShape (arr, "GVARIANCES")) { itsValue.array() = 0; itsValue.wmask() = False; itsCurMean.resize (arr.shape()); itsCurMean = 0; itsNr.resize (arr.shape()); itsNr = 0; } Array::contiter itm = itsCurMean.cbegin(); Array::contiter itn = itsNr.cbegin(); Array::const_iterator in = arr.array().begin(); if (arr.hasMask()) { Array::const_iterator min = arr.mask().begin(); for (Array::contiter out = itsValue.array().cbegin(); out != itsValue.array().cend(); ++in, ++min, ++out, ++itm, ++itn) { if (! *min) { (*itn)++; Double delta = *in - *itm; *itm += delta / *itn; delta *= *in - *itm; *out += delta; } } } else { for (Array::contiter out = itsValue.array().cbegin(); out != itsValue.array().cend(); ++in, ++out, ++itm, ++itn) { (*itn)++; Double delta = *in - *itm; *itm += delta / *itn; delta *= *in - *itm; *out += delta; } } } } void TableExprGroupVariancesArrayDouble::finish() { DebugAssert (itsNr.contiguousStorage() && itsValue.array().contiguousStorage(), AipsError); Array::contiter itv = itsValue.array().cbegin(); Array::contiter itm = itsValue.wmask().cbegin(); for (Array::const_contiter itn = itsNr.cbegin(); itn != itsNr.cend(); ++itn, ++itv, ++itm) { if (*itn > itsDdof) { *itv /= *itn - itsDdof; } else { *itv = 0; *itm = True; } } } TableExprGroupStdDevsArrayDouble::TableExprGroupStdDevsArrayDouble(TableExprNodeRep* node, uInt ddof) : TableExprGroupVariancesArrayDouble (node, ddof) {} TableExprGroupStdDevsArrayDouble::~TableExprGroupStdDevsArrayDouble() {} void TableExprGroupStdDevsArrayDouble::finish() { TableExprGroupVariancesArrayDouble::finish(); itsValue = sqrt(itsValue); } TableExprGroupRmssArrayDouble::TableExprGroupRmssArrayDouble(TableExprNodeRep* node) : TableExprGroupFuncArrayDouble (node) {} TableExprGroupRmssArrayDouble::~TableExprGroupRmssArrayDouble() {} void TableExprGroupRmssArrayDouble::apply (const TableExprId& id) { MArray arr = itsOperand->getArrayDouble(id); if (! arr.empty()) { if (checkShape (arr, "GRMSS")) { itsValue.array() = 0; itsValue.wmask() = False; itsNr.resize (arr.shape()); itsNr = 0; } Array::contiter itn = itsNr.cbegin(); Array::const_iterator in = arr.array().begin(); if (arr.hasMask()) { Array::const_iterator min = arr.mask().begin(); for (Array::contiter out = itsValue.array().cbegin(); out != itsValue.array().cend(); ++in, ++min, ++out, ++itn) { if (! *min) { *out += *in * *in; (*itn)++; } } } else { for (Array::contiter out = itsValue.array().cbegin(); out != itsValue.array().cend(); ++in, ++out, ++itn) { *out += *in * *in; (*itn)++; } } } } void TableExprGroupRmssArrayDouble::finish() { DebugAssert (itsNr.contiguousStorage() && itsValue.array().contiguousStorage(), AipsError); Array::contiter itv = itsValue.array().cbegin(); Array::contiter itm = itsValue.wmask().cbegin(); for (Array::const_contiter itn = itsNr.cbegin(); itn != itsNr.cend(); ++itn, ++itv, ++itm) { if (*itn > 0) { *itv = sqrt(*itv / *itn); } else if (itsValue.hasMask()) { *itm = True; } } } TableExprGroupSumsArrayDComplex::TableExprGroupSumsArrayDComplex(TableExprNodeRep* node) : TableExprGroupFuncArrayDComplex (node) {} TableExprGroupSumsArrayDComplex::~TableExprGroupSumsArrayDComplex() {} void TableExprGroupSumsArrayDComplex::apply (const TableExprId& id) { MArray arr = itsOperand->getArrayDComplex(id); if (! arr.empty()) { if (checkShape (arr, "GSUMS")) { itsValue.array() = DComplex(); itsValue.wmask() = True; } TEGSum (arr, itsValue); } } TableExprGroupProductsArrayDComplex::TableExprGroupProductsArrayDComplex(TableExprNodeRep* node) : TableExprGroupFuncArrayDComplex (node) {} TableExprGroupProductsArrayDComplex::~TableExprGroupProductsArrayDComplex() {} void TableExprGroupProductsArrayDComplex::apply (const TableExprId& id) { MArray arr = itsOperand->getArrayDComplex(id); if (checkShape (arr, "GPRODUCTS")) { itsValue.array() = DComplex(1,0); itsValue.wmask() = True; } TEGProduct (arr, itsValue); } void TableExprGroupProductsArrayDComplex::finish() { TEGClearMasked (itsValue); } TableExprGroupSumSqrsArrayDComplex::TableExprGroupSumSqrsArrayDComplex(TableExprNodeRep* node) : TableExprGroupFuncArrayDComplex (node) {} TableExprGroupSumSqrsArrayDComplex::~TableExprGroupSumSqrsArrayDComplex() {} void TableExprGroupSumSqrsArrayDComplex::apply (const TableExprId& id) { MArray arr = itsOperand->getArrayDComplex(id); if (! arr.empty()) { if (checkShape (arr, "GSUMSQRS")) { itsValue.array() = DComplex(); itsValue.wmask() = True; } TEGSumSqr (arr, itsValue); } } TableExprGroupMeansArrayDComplex::TableExprGroupMeansArrayDComplex(TableExprNodeRep* node) : TableExprGroupFuncArrayDComplex (node) {} TableExprGroupMeansArrayDComplex::~TableExprGroupMeansArrayDComplex() {} void TableExprGroupMeansArrayDComplex::apply (const TableExprId& id) { MArray arr = itsOperand->getArrayDComplex(id); if (! arr.empty()) { if (checkShape (arr, "GMEANS")) { itsValue.array() = DComplex(); itsValue.wmask() = False; itsNr.resize (arr.shape()); itsNr = 0; } TEGMeanAdd (arr, itsValue.array(), itsNr); } } void TableExprGroupMeansArrayDComplex::finish() { TEGMeanFinish (itsValue, itsNr); } TableExprGroupVariancesArrayDComplex::TableExprGroupVariancesArrayDComplex(TableExprNodeRep* node, uInt ddof) : TableExprGroupFuncArrayDouble (node), itsDdof (ddof) {} TableExprGroupVariancesArrayDComplex::~TableExprGroupVariancesArrayDComplex() {} void TableExprGroupVariancesArrayDComplex::apply (const TableExprId& id) { // Calculate mean and variance in a running way using a // numerically stable algorithm. // See en.wikipedia.org/wiki/Algorithms_for_calculating_variance MArray arr = itsOperand->getArrayDComplex(id); if (! arr.empty()) { if (checkShape (arr, "GVARIANCES")) { itsValue.array() = 0; itsValue.wmask() = False; itsCurMean.resize (arr.shape()); itsNr.resize (arr.shape()); itsNr = 0; } Array::contiter itm = itsCurMean.cbegin(); Array::contiter itn = itsNr.cbegin(); Array::const_iterator in = arr.array().begin(); if (arr.hasMask()) { Array::const_iterator min = arr.mask().begin(); for (Array::contiter out = itsValue.array().cbegin(); out != itsValue.array().cend(); ++in, ++min, ++out, ++itm, ++itn) { if (! *min) { (*itn)++; DComplex delta = *in - *itm; *itm += delta / Double(*itn); DComplex d = *in - *itm; *out += real(d)*real(delta) + imag(d)*imag(delta); } } } else { for (Array::contiter out = itsValue.array().cbegin(); out != itsValue.array().cend(); ++in, ++out, ++itm, ++itn) { (*itn)++; DComplex delta = *in - *itm; *itm += delta / Double(*itn); DComplex d = *in - *itm; *out += real(d)*real(delta) + imag(d)*imag(delta); } } } } void TableExprGroupVariancesArrayDComplex::finish() { DebugAssert (itsNr.contiguousStorage() && itsValue.array().contiguousStorage(), AipsError); Array::contiter itv = itsValue.array().cbegin(); Array::contiter itm = itsValue.wmask().cbegin(); for (Array::const_contiter itn = itsNr.cbegin(); itn != itsNr.cend(); ++itn, ++itv, ++itm) { if (*itn > itsDdof) { *itv /= *itn - itsDdof; } else { *itv = 0; *itm = True; } } } TableExprGroupStdDevsArrayDComplex::TableExprGroupStdDevsArrayDComplex(TableExprNodeRep* node, uInt ddof) : TableExprGroupVariancesArrayDComplex (node, ddof) {} TableExprGroupStdDevsArrayDComplex::~TableExprGroupStdDevsArrayDComplex() {} void TableExprGroupStdDevsArrayDComplex::finish() { TableExprGroupVariancesArrayDComplex::finish(); itsValue = sqrt(itsValue); } TableExprGroupHistBase::TableExprGroupHistBase (TableExprNodeRep* node, Int64 nbin, Double start, Double end) : TableExprGroupFuncBase (node), itsHist (nbin+2, 0), itsStart (start) { AlwaysAssert (nbin > 0 && end > start, AipsError); itsWidth = (end-start) / nbin; } TableExprGroupHistBase::~TableExprGroupHistBase() {} void TableExprGroupHistBase::add (Double val) { size_t bin = size_t(std::max(0., (val - itsStart) / itsWidth + 1.)); if (bin >= itsHist.size()) { bin = itsHist.size() - 1; } itsHist[bin]++; } MArray TableExprGroupHistBase::getArrayInt (const vector&) { return MArray(itsHist); } TableExprGroupHistScalar::TableExprGroupHistScalar (TableExprNodeRep* node, Int64 nbin, Double start, Double end) : TableExprGroupHistBase (node, nbin, start, end) {} TableExprGroupHistScalar::~TableExprGroupHistScalar() {} void TableExprGroupHistScalar::apply (const TableExprId& id) { add (itsOperand->getDouble (id)); } TableExprGroupHistInt::TableExprGroupHistInt (TableExprNodeRep* node, Int64 nbin, Double start, Double end) : TableExprGroupHistBase (node, nbin, start, end) {} TableExprGroupHistInt::~TableExprGroupHistInt() {} void TableExprGroupHistInt::apply (const TableExprId& id) { MArray arr = itsOperand->getArrayInt (id); // Array does not need to be contiguous, so use iterator. if (! arr.hasMask()) { Array::const_iterator iterEnd = arr.array().end(); for (Array::const_iterator iter = arr.array().begin(); iter!=iterEnd; ++iter) { add (*iter); } } else { Array::const_iterator iterEnd = arr.array().end(); Array::const_iterator miter = arr.mask().begin(); for (Array::const_iterator iter = arr.array().begin(); iter!=iterEnd; ++iter, ++miter) { if (!*miter) add (*iter); } } } TableExprGroupHistDouble::TableExprGroupHistDouble (TableExprNodeRep* node, Int64 nbin, Double start, Double end) : TableExprGroupHistBase (node, nbin, start, end) {} TableExprGroupHistDouble::~TableExprGroupHistDouble() {} void TableExprGroupHistDouble::apply (const TableExprId& id) { MArray arr = itsOperand->getArrayDouble (id); // Array does not need to be contiguous, so use iterator. if (! arr.hasMask()) { Array::const_iterator iterEnd = arr.array().end(); for (Array::const_iterator iter = arr.array().begin(); iter!=iterEnd; ++iter) { add (*iter); } } else { Array::const_iterator iterEnd = arr.array().end(); Array::const_iterator miter = arr.mask().begin(); for (Array::const_iterator iter = arr.array().begin(); iter!=iterEnd; ++iter, ++miter) { if (!*miter) add (*iter); } } } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/ExprGroupAggrFuncArray.h000066400000000000000000001031631476623553700221230ustar00rootroot00000000000000//# ExprGroupAggrFuncArray.h: The various array reduction aggregation functions //# Copyright (C) 2013 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_EXPRGROUPAGGRFUNCARRAY_H #define TABLES_EXPRGROUPAGGRFUNCARRAY_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Aggregate class counting if any array value in a group is true // // // // // // Aggregate class counting if any array value in a group is true. // class TableExprGroupArrayAny: public TableExprGroupFuncBool { public: TableExprGroupArrayAny (TableExprNodeRep* node); virtual ~TableExprGroupArrayAny(); virtual void apply (const TableExprId& id); }; // // Aggregate class counting if all array values in a group are true // // // // // // Aggregate class counting if all array values in a group are true. // class TableExprGroupArrayAll: public TableExprGroupFuncBool { public: TableExprGroupArrayAll (TableExprNodeRep* node); virtual ~TableExprGroupArrayAll(); virtual void apply (const TableExprId& id); }; // // Aggregate class counting the number of true array values in a group // // // // // // Aggregate class counting the number of true array values in a group. // class TableExprGroupArrayNTrue: public TableExprGroupFuncInt { public: TableExprGroupArrayNTrue (TableExprNodeRep* node); virtual ~TableExprGroupArrayNTrue(); virtual void apply (const TableExprId& id); }; // // Aggregate class counting the number of false array values in a group // // // // // // Aggregate class counting the number of false array values in a group. // class TableExprGroupArrayNFalse: public TableExprGroupFuncInt { public: TableExprGroupArrayNFalse (TableExprNodeRep* node); virtual ~TableExprGroupArrayNFalse(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the minimum integer array value in a group // // // // // // Aggregate class determining the minimum integer array value in a group. // class TableExprGroupMinArrayInt: public TableExprGroupFuncInt { public: TableExprGroupMinArrayInt (TableExprNodeRep* node); virtual ~TableExprGroupMinArrayInt(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the maximum integer array value in a group // // // // // // Aggregate class determining the maximum integer array value in a group. // class TableExprGroupMaxArrayInt: public TableExprGroupFuncInt { public: TableExprGroupMaxArrayInt (TableExprNodeRep* node); virtual ~TableExprGroupMaxArrayInt(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the sum of integer array values in a group // // // // // // Aggregate class determining the sum of integer array values in a group. // class TableExprGroupSumArrayInt: public TableExprGroupFuncInt { public: TableExprGroupSumArrayInt (TableExprNodeRep* node); virtual ~TableExprGroupSumArrayInt(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the product of integer array values in a group // // // // // // Aggregate class determining the product of integer array values in a group. // class TableExprGroupProductArrayInt: public TableExprGroupFuncInt { public: TableExprGroupProductArrayInt (TableExprNodeRep* node); virtual ~TableExprGroupProductArrayInt(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the sum of squares of integer array values // in a group // // // // // // Aggregate class determining the sum of squares of integer array values // in a group. // class TableExprGroupSumSqrArrayInt: public TableExprGroupFuncInt { public: TableExprGroupSumSqrArrayInt (TableExprNodeRep* node); virtual ~TableExprGroupSumSqrArrayInt(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the minimum double array value in a group // // // // // // Aggregate class determining the minimum double array value in a group. // class TableExprGroupMinArrayDouble: public TableExprGroupFuncDouble { public: TableExprGroupMinArrayDouble (TableExprNodeRep* node); virtual ~TableExprGroupMinArrayDouble(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the maximum double array value in a group // // // // // // Aggregate class determining the maximum double array value in a group. // class TableExprGroupMaxArrayDouble: public TableExprGroupFuncDouble { public: TableExprGroupMaxArrayDouble (TableExprNodeRep* node); virtual ~TableExprGroupMaxArrayDouble(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the sum of double array values in a group // // // // // // Aggregate class determining the sum of double array values in a group. // class TableExprGroupSumArrayDouble: public TableExprGroupFuncDouble { public: TableExprGroupSumArrayDouble (TableExprNodeRep* node); virtual ~TableExprGroupSumArrayDouble(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the product of double array values in a group // // // // // // Aggregate class determining the product of double array values in a group. // class TableExprGroupProductArrayDouble: public TableExprGroupFuncDouble { public: TableExprGroupProductArrayDouble (TableExprNodeRep* node); virtual ~TableExprGroupProductArrayDouble(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the sum of squares of double array values // in a group // // // // // // Aggregate class determining the sum of squares of double array values // in a group. // class TableExprGroupSumSqrArrayDouble: public TableExprGroupFuncDouble { public: TableExprGroupSumSqrArrayDouble (TableExprNodeRep* node); virtual ~TableExprGroupSumSqrArrayDouble(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the mean of array values in a group // // // // // // Aggregate class determining the mean of array values in a group. // class TableExprGroupMeanArrayDouble: public TableExprGroupFuncDouble { public: TableExprGroupMeanArrayDouble (TableExprNodeRep* node); virtual ~TableExprGroupMeanArrayDouble(); virtual void apply (const TableExprId& id); virtual void finish(); private: Int64 itsNr; }; // // Aggregate class determining the variance of array values in a group // // // // // // Aggregate class determining the variance of array values in a group. // It uses a running algorithm // (see en.wikipedia.org/wiki/Algorithms_for_calculating_variance) // class TableExprGroupVarianceArrayDouble: public TableExprGroupFuncDouble { public: TableExprGroupVarianceArrayDouble (TableExprNodeRep* node, uInt ddof); virtual ~TableExprGroupVarianceArrayDouble(); virtual void apply (const TableExprId& id); virtual void finish(); protected: uInt itsDdof; Int64 itsNr; Double itsCurMean; }; // // Aggregate class determining the standard devation of array values // in a group // // // // // // Aggregate class determining the standard deviation of array values // in a group. It uses a running algorithm // (see en.wikipedia.org/wiki/Algorithms_for_calculating_variance) // class TableExprGroupStdDevArrayDouble: public TableExprGroupVarianceArrayDouble { public: TableExprGroupStdDevArrayDouble (TableExprNodeRep* node, uInt ddof); virtual ~TableExprGroupStdDevArrayDouble(); virtual void finish(); }; // // Aggregate class determining the RMS of array values in a group // // // // // // Aggregate class determining the RMS of array values in a group. // class TableExprGroupRmsArrayDouble: public TableExprGroupFuncDouble { public: TableExprGroupRmsArrayDouble (TableExprNodeRep* node); virtual ~TableExprGroupRmsArrayDouble(); virtual void apply (const TableExprId& id); virtual void finish(); private: Int64 itsNr; }; // // Aggregate class determining the fractile of array values in a group // // // // // // Aggregate class determining the fractile of array values in a group. //
        It is a lazy aggregate class, thus apply does nothing. // Instead, getDouble assembles the values and determines the // fractile. //
        class TableExprGroupFractileArrayDouble: public TableExprGroupFuncDouble { public: explicit TableExprGroupFractileArrayDouble (TableExprNodeRep* node, Double fractile); virtual ~TableExprGroupFractileArrayDouble(); virtual Bool isLazy() const; virtual void apply (const TableExprId& id); virtual Double getDouble (const vector& ids); private: Double itsFrac; }; // // Aggregate class determining the sum of complex array values in a group // // // // // // Aggregate class determining the sum of complex array values in a group. // class TableExprGroupSumArrayDComplex: public TableExprGroupFuncDComplex { public: TableExprGroupSumArrayDComplex (TableExprNodeRep* node); virtual ~TableExprGroupSumArrayDComplex(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the product of complex array values in a group // // // // // // Aggregate class determining the product of complex array values in a group. // class TableExprGroupProductArrayDComplex: public TableExprGroupFuncDComplex { public: TableExprGroupProductArrayDComplex (TableExprNodeRep* node); virtual ~TableExprGroupProductArrayDComplex(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the sum of squares of complex array values // in a group // // // // // // Aggregate class determining the sum of squares of complex array values // in a group. // class TableExprGroupSumSqrArrayDComplex: public TableExprGroupFuncDComplex { public: TableExprGroupSumSqrArrayDComplex (TableExprNodeRep* node); virtual ~TableExprGroupSumSqrArrayDComplex(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the mean of complex array values in a group // // // // // // Aggregate class determining the mean of complex array values in a group. // class TableExprGroupMeanArrayDComplex: public TableExprGroupFuncDComplex { public: TableExprGroupMeanArrayDComplex (TableExprNodeRep* node); virtual ~TableExprGroupMeanArrayDComplex(); virtual void apply (const TableExprId& id); virtual void finish(); private: Int64 itsNr; }; // // Aggregate class determining the variance of array values in a group // // // // // // Aggregate class determining the variance of array values in a group. // It uses a running algorithm // (see en.wikipedia.org/wiki/Algorithms_for_calculating_variance) // Note that the result is a Double value (not DComplex). // class TableExprGroupVarianceArrayDComplex: public TableExprGroupFuncDouble { public: TableExprGroupVarianceArrayDComplex (TableExprNodeRep* node, uInt ddof); virtual ~TableExprGroupVarianceArrayDComplex(); virtual void apply (const TableExprId& id); virtual void finish(); protected: uInt itsDdof; Int64 itsNr; DComplex itsCurMean; }; // // Aggregate class determining the standard devation of array values // in a group // // // // // // Aggregate class determining the standard deviation of array values // in a group. It uses a running algorithm // (see en.wikipedia.org/wiki/Algorithms_for_calculating_variance) // class TableExprGroupStdDevArrayDComplex: public TableExprGroupVarianceArrayDComplex { public: TableExprGroupStdDevArrayDComplex (TableExprNodeRep* node, uInt ddof); virtual ~TableExprGroupStdDevArrayDComplex(); virtual void finish(); }; // // Aggregate class counting per array index in a group if any is true // // // // // // Aggregate class counting per array index in a group if any is true. // class TableExprGroupArrayAnys: public TableExprGroupFuncArrayBool { public: TableExprGroupArrayAnys (TableExprNodeRep* node); virtual ~TableExprGroupArrayAnys(); virtual void apply (const TableExprId& id); }; // // Aggregate class counting per array index in a group if all are true // // // // // // Aggregate class counting per array index in a group if all are true. // class TableExprGroupArrayAlls: public TableExprGroupFuncArrayBool { public: TableExprGroupArrayAlls (TableExprNodeRep* node); virtual ~TableExprGroupArrayAlls(); virtual void apply (const TableExprId& id); }; // // Aggregate class counting per array index in a group the nr of true values // // // // // // Aggregate class counting per array index in a group the nr of true values. // class TableExprGroupArrayNTrues: public TableExprGroupFuncArrayInt { public: TableExprGroupArrayNTrues (TableExprNodeRep* node); virtual ~TableExprGroupArrayNTrues(); virtual void apply (const TableExprId& id); }; // // Aggregate class counting per array index in a group the nr of false values // // // // // // Aggregate class counting per array index in a group the nr of false values. // class TableExprGroupArrayNFalses: public TableExprGroupFuncArrayInt { public: TableExprGroupArrayNFalses (TableExprNodeRep* node); virtual ~TableExprGroupArrayNFalses(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining per array index in a group the minimum value // // // // // // Aggregate class determining per array index in a group the minimum value. // class TableExprGroupMinsArrayInt: public TableExprGroupFuncArrayInt { public: TableExprGroupMinsArrayInt (TableExprNodeRep* node); virtual ~TableExprGroupMinsArrayInt(); virtual void apply (const TableExprId& id); virtual void finish(); }; // // Aggregate class determining per array index in a group the maximum value // // // // // // Aggregate class determining per array index in a group the maximum value. // class TableExprGroupMaxsArrayInt: public TableExprGroupFuncArrayInt { public: TableExprGroupMaxsArrayInt (TableExprNodeRep* node); virtual ~TableExprGroupMaxsArrayInt(); virtual void apply (const TableExprId& id); virtual void finish(); }; // // Aggregate class determining per array index in a group the sum of values // // // // // // Aggregate class determining per array index in a group the sum of values. // class TableExprGroupSumsArrayInt: public TableExprGroupFuncArrayInt { public: TableExprGroupSumsArrayInt (TableExprNodeRep* node); virtual ~TableExprGroupSumsArrayInt(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining per array index in a group the product of values // // // // // // Aggregate class determining per array index in a group the product of values. // class TableExprGroupProductsArrayInt: public TableExprGroupFuncArrayInt { public: TableExprGroupProductsArrayInt (TableExprNodeRep* node); virtual ~TableExprGroupProductsArrayInt(); virtual void apply (const TableExprId& id); virtual void finish(); }; // // Aggregate class determining per array index in a group the sum of value squares // in a group // // // // // // Aggregate class determining per array index in a group the sum of value squares. // class TableExprGroupSumSqrsArrayInt: public TableExprGroupFuncArrayInt { public: TableExprGroupSumSqrsArrayInt (TableExprNodeRep* node); virtual ~TableExprGroupSumSqrsArrayInt(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the minimum double array value in a group // // // // // // Aggregate class determining the minimum double array value in a group. // class TableExprGroupMinsArrayDouble: public TableExprGroupFuncArrayDouble { public: TableExprGroupMinsArrayDouble (TableExprNodeRep* node); virtual ~TableExprGroupMinsArrayDouble(); virtual void apply (const TableExprId& id); virtual void finish(); }; // // Aggregate class determining the maximum double array value in a group // // // // // // Aggregate class determining the maximum double array value in a group. // class TableExprGroupMaxsArrayDouble: public TableExprGroupFuncArrayDouble { public: TableExprGroupMaxsArrayDouble (TableExprNodeRep* node); virtual ~TableExprGroupMaxsArrayDouble(); virtual void apply (const TableExprId& id); virtual void finish(); }; // // Aggregate class determining the sum of double array values in a group // // // // // // Aggregate class determining the sum of double array values in a group. // class TableExprGroupSumsArrayDouble: public TableExprGroupFuncArrayDouble { public: TableExprGroupSumsArrayDouble (TableExprNodeRep* node); virtual ~TableExprGroupSumsArrayDouble(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the product of double array values in a group // // // // // // Aggregate class determining the product of double array values in a group. // class TableExprGroupProductsArrayDouble: public TableExprGroupFuncArrayDouble { public: TableExprGroupProductsArrayDouble (TableExprNodeRep* node); virtual ~TableExprGroupProductsArrayDouble(); virtual void apply (const TableExprId& id); virtual void finish(); }; // // Aggregate class determining the sum of squares of double array values // in a group // // // // // // Aggregate class determining the sum of squares of double array values // in a group. // class TableExprGroupSumSqrsArrayDouble: public TableExprGroupFuncArrayDouble { public: TableExprGroupSumSqrsArrayDouble (TableExprNodeRep* node); virtual ~TableExprGroupSumSqrsArrayDouble(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the mean of array values in a group // // // // // // Aggregate class determining the mean of array values in a group. // class TableExprGroupMeansArrayDouble: public TableExprGroupFuncArrayDouble { public: TableExprGroupMeansArrayDouble (TableExprNodeRep* node); virtual ~TableExprGroupMeansArrayDouble(); virtual void apply (const TableExprId& id); virtual void finish(); private: Array itsNr; }; // // Aggregate class determining the variance of array values in a group // // // // // // Aggregate class determining the variance of array values in a group. // It uses a running algorithm // (see en.wikipedia.org/wiki/Algorithms_for_calculating_variance) // class TableExprGroupVariancesArrayDouble: public TableExprGroupFuncArrayDouble { public: TableExprGroupVariancesArrayDouble (TableExprNodeRep* node, uInt ddof); virtual ~TableExprGroupVariancesArrayDouble(); virtual void apply (const TableExprId& id); virtual void finish(); protected: uInt itsDdof; Array itsNr; Array itsCurMean; }; // // Aggregate class determining the standard devation of array values // in a group // // // // // // Aggregate class determining the standard deviation of array values // in a group. It uses a running algorithm // (see en.wikipedia.org/wiki/Algorithms_for_calculating_variance) // class TableExprGroupStdDevsArrayDouble: public TableExprGroupVariancesArrayDouble { public: TableExprGroupStdDevsArrayDouble (TableExprNodeRep* node, uInt ddof); virtual ~TableExprGroupStdDevsArrayDouble(); virtual void finish(); }; // // Aggregate class determining the RMS of array values in a group // // // // // // Aggregate class determining the RMS of array values in a group. // class TableExprGroupRmssArrayDouble: public TableExprGroupFuncArrayDouble { public: TableExprGroupRmssArrayDouble (TableExprNodeRep* node); virtual ~TableExprGroupRmssArrayDouble(); virtual void apply (const TableExprId& id); virtual void finish(); private: Array itsNr; }; // // Aggregate class determining the sum of complex array values in a group // // // // // // Aggregate class determining the sum of complex array values in a group. // class TableExprGroupSumsArrayDComplex: public TableExprGroupFuncArrayDComplex { public: TableExprGroupSumsArrayDComplex (TableExprNodeRep* node); virtual ~TableExprGroupSumsArrayDComplex(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the product of complex array values in a group // // // // // // Aggregate class determining the product of complex array values in a group. // class TableExprGroupProductsArrayDComplex: public TableExprGroupFuncArrayDComplex { public: TableExprGroupProductsArrayDComplex (TableExprNodeRep* node); virtual ~TableExprGroupProductsArrayDComplex(); virtual void apply (const TableExprId& id); virtual void finish(); }; // // Aggregate class determining the sum of squares of complex array values // in a group // // // // // // Aggregate class determining the sum of squares of complex array values // in a group. // class TableExprGroupSumSqrsArrayDComplex: public TableExprGroupFuncArrayDComplex { public: TableExprGroupSumSqrsArrayDComplex (TableExprNodeRep* node); virtual ~TableExprGroupSumSqrsArrayDComplex(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the mean of complex array values in a group // // // // // // Aggregate class determining the mean of complex array values in a group. // class TableExprGroupMeansArrayDComplex: public TableExprGroupFuncArrayDComplex { public: TableExprGroupMeansArrayDComplex (TableExprNodeRep* node); virtual ~TableExprGroupMeansArrayDComplex(); virtual void apply (const TableExprId& id); virtual void finish(); private: Array itsNr; }; // // Aggregate class determining the variance of array values in a group // // // // // // Aggregate class determining the variance of array values in a group. // It uses a running algorithm // (see en.wikipedia.org/wiki/Algorithms_for_calculating_variance) // Note that result is a Double value (npot DComplex). // class TableExprGroupVariancesArrayDComplex: public TableExprGroupFuncArrayDouble { public: TableExprGroupVariancesArrayDComplex (TableExprNodeRep* node, uInt ddof); virtual ~TableExprGroupVariancesArrayDComplex(); virtual void apply (const TableExprId& id); virtual void finish(); protected: uInt itsDdof; Array itsNr; Array itsCurMean; }; // // Aggregate class determining the standard devation of array values // in a group // // // // // // Aggregate class determining the standard deviation of array values // in a group. It uses a running algorithm // (see en.wikipedia.org/wiki/Algorithms_for_calculating_variance) // class TableExprGroupStdDevsArrayDComplex: public TableExprGroupVariancesArrayDComplex { public: TableExprGroupStdDevsArrayDComplex (TableExprNodeRep* node, uInt ddof); virtual ~TableExprGroupStdDevsArrayDComplex(); virtual void finish(); }; // // Base aggregate class determining the histogram of values in a group // // // // // // Base aggregate class determining the histogram of values in a group // class TableExprGroupHistBase: public TableExprGroupFuncBase { public: explicit TableExprGroupHistBase (TableExprNodeRep* node, Int64 nbin, Double start, Double end); virtual ~TableExprGroupHistBase(); virtual MArray getArrayInt (const vector&); protected: // Add the value to the histogram. void add (Double value); private: Vector itsHist; Double itsStart; Double itsWidth; }; // // Aggregate class determining the histogram of scalar values in a group // // // // // // Aggregate class determining the histogram of scalar values in a group // class TableExprGroupHistScalar: public TableExprGroupHistBase { public: explicit TableExprGroupHistScalar (TableExprNodeRep* node, Int64 nbin, Double start, Double end); virtual ~TableExprGroupHistScalar(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the histogram of integer array values in a group // // // // // // Aggregate class determining the histogram of integer array values in a group // class TableExprGroupHistInt: public TableExprGroupHistBase { public: explicit TableExprGroupHistInt (TableExprNodeRep* node, Int64 nbin, Double start, Double end); virtual ~TableExprGroupHistInt(); virtual void apply (const TableExprId& id); }; // // Aggregate class determining the histogram of double array values in a group // // // // // // Aggregate class determining the histogram of double array values in a group // class TableExprGroupHistDouble: public TableExprGroupHistBase { public: explicit TableExprGroupHistDouble (TableExprNodeRep* node, Int64 nbin, Double start, Double end); virtual ~TableExprGroupHistDouble(); virtual void apply (const TableExprId& id); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/ExprLogicNode.cc000066400000000000000000000417521476623553700204210ustar00rootroot00000000000000//# ExprLogicNode.cc: Nodes representing scalar logical operators in table select expression tree //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include // for DBL_MAX #include // for DBL_MAX namespace casacore { //# NAMESPACE CASACORE - BEGIN // Implement the comparison operators for each data type. TableExprNodeEQBool::TableExprNodeEQBool (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtEQ) {} Bool TableExprNodeEQBool::getBool (const TableExprId& id) { return lnode_p->getBool(id) == rnode_p->getBool(id); } TableExprNodeEQInt::TableExprNodeEQInt (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtEQ) {} Bool TableExprNodeEQInt::getBool (const TableExprId& id) { return lnode_p->getInt(id) == rnode_p->getInt(id); } TableExprNodeEQDouble::TableExprNodeEQDouble (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtEQ) {} Bool TableExprNodeEQDouble::getBool (const TableExprId& id) { return lnode_p->getDouble(id) == rnode_p->getDouble(id); } TableExprNodeEQDComplex::TableExprNodeEQDComplex (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtEQ) {} Bool TableExprNodeEQDComplex::getBool (const TableExprId& id) { return lnode_p->getDComplex(id) == rnode_p->getDComplex(id); } TableExprNodeEQString::TableExprNodeEQString (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtEQ) {} Bool TableExprNodeEQString::getBool (const TableExprId& id) { return lnode_p->getString(id) == rnode_p->getString(id); } TableExprNodeEQRegex::TableExprNodeEQRegex (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtEQ) {} Bool TableExprNodeEQRegex::getBool (const TableExprId& id) { return rnode_p->getRegex(id).match (lnode_p->getString(id)); } TableExprNodeEQDate::TableExprNodeEQDate (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtEQ) {} Bool TableExprNodeEQDate::getBool (const TableExprId& id) { return lnode_p->getDate(id) == rnode_p->getDate(id); } TableExprNodeNEBool::TableExprNodeNEBool (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtNE) {} Bool TableExprNodeNEBool::getBool (const TableExprId& id) { return lnode_p->getBool(id) != rnode_p->getBool(id); } TableExprNodeNEInt::TableExprNodeNEInt (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtNE) {} Bool TableExprNodeNEInt::getBool (const TableExprId& id) { return lnode_p->getInt(id) != rnode_p->getInt(id); } TableExprNodeNEDouble::TableExprNodeNEDouble (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtNE) {} Bool TableExprNodeNEDouble::getBool (const TableExprId& id) { return lnode_p->getDouble(id) != rnode_p->getDouble(id); } TableExprNodeNEDComplex::TableExprNodeNEDComplex (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtNE) {} Bool TableExprNodeNEDComplex::getBool (const TableExprId& id) { return lnode_p->getDComplex(id) != rnode_p->getDComplex(id); } TableExprNodeNEString::TableExprNodeNEString (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtNE) {} Bool TableExprNodeNEString::getBool (const TableExprId& id) { return lnode_p->getString(id) != rnode_p->getString(id); } TableExprNodeNERegex::TableExprNodeNERegex (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtNE) {} Bool TableExprNodeNERegex::getBool (const TableExprId& id) { return ! rnode_p->getRegex(id).match (lnode_p->getString(id)); } TableExprNodeNEDate::TableExprNodeNEDate (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtNE) {} Bool TableExprNodeNEDate::getBool (const TableExprId& id) { return lnode_p->getDate(id) != rnode_p->getDate(id); } TableExprNodeGTInt::TableExprNodeGTInt (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtGT) {} Bool TableExprNodeGTInt::getBool (const TableExprId& id) { return lnode_p->getInt(id) > rnode_p->getInt(id); } TableExprNodeGTDouble::TableExprNodeGTDouble (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtGT) {} Bool TableExprNodeGTDouble::getBool (const TableExprId& id) { return lnode_p->getDouble(id) > rnode_p->getDouble(id); } TableExprNodeGTDComplex::TableExprNodeGTDComplex (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtGT) {} Bool TableExprNodeGTDComplex::getBool (const TableExprId& id) { return lnode_p->getDComplex(id) > rnode_p->getDComplex(id); } TableExprNodeGTString::TableExprNodeGTString (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtGT) {} Bool TableExprNodeGTString::getBool (const TableExprId& id) { return lnode_p->getString(id) > rnode_p->getString(id); } TableExprNodeGTDate::TableExprNodeGTDate (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtGT) {} Bool TableExprNodeGTDate::getBool (const TableExprId& id) { return lnode_p->getDate(id) > rnode_p->getDate(id); } TableExprNodeGEInt::TableExprNodeGEInt (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtGE) {} Bool TableExprNodeGEInt::getBool (const TableExprId& id) { return lnode_p->getInt(id) >= rnode_p->getInt(id); } TableExprNodeGEDouble::TableExprNodeGEDouble (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtGE) {} Bool TableExprNodeGEDouble::getBool (const TableExprId& id) { return lnode_p->getDouble(id) >= rnode_p->getDouble(id); } TableExprNodeGEDComplex::TableExprNodeGEDComplex (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtGE) {} Bool TableExprNodeGEDComplex::getBool (const TableExprId& id) { return lnode_p->getDComplex(id) >= rnode_p->getDComplex(id); } TableExprNodeGEString::TableExprNodeGEString (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtGE) {} Bool TableExprNodeGEString::getBool (const TableExprId& id) { return lnode_p->getString(id) >= rnode_p->getString(id); } TableExprNodeGEDate::TableExprNodeGEDate (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtGE) {} Bool TableExprNodeGEDate::getBool (const TableExprId& id) { return lnode_p->getDate(id) >= rnode_p->getDate(id); } TableExprNodeINInt::TableExprNodeINInt (const TableExprNodeRep& node, Bool) : TableExprNodeBinary (NTBool, node, OtIN) {} void TableExprNodeINInt::optimize() { doOptimize (rnode_p); } void TableExprNodeINInt::doOptimize (TENShPtr& rnode) { if (rnode->isConstant() && rnode->valueType() == VTArray) { // Convert a constant array for faster lookup. MArray values = rnode->getArrayInt(0); Array arr(values.array()); if (values.hasMask()) { // Remove masked elements. arr.reference (values.flatten()); } // Use an unordered_map for fast lookup. rnode = std::make_shared>(*rnode, arr); } } Bool TableExprNodeINInt::getBool (const TableExprId& id) { Int64 val = lnode_p->getInt (id); return rnode_p->contains (id, val); } TableExprNodeINDouble::TableExprNodeINDouble (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtIN) {} void TableExprNodeINDouble::optimize() { doOptimize (rnode_p); } void TableExprNodeINDouble::doOptimize (TENShPtr& rnode) { if (rnode->isConstant() && rnode->valueType() == VTSet) { TableExprNodeSet& set = dynamic_cast(*rnode); if (!set.isSingle() && !set.isDiscrete()) { rnode = TableExprNodeSetOptContSetBase::transform (set); } } } Bool TableExprNodeINDouble::getBool (const TableExprId& id) { return rnode_p->contains (id, lnode_p->getDouble (id)); } TableExprNodeINDComplex::TableExprNodeINDComplex (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtIN) {} Bool TableExprNodeINDComplex::getBool (const TableExprId& id) { return rnode_p->contains (id, lnode_p->getDComplex (id)); } TableExprNodeINString::TableExprNodeINString (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtIN) {} void TableExprNodeINString::optimize() { doOptimize (rnode_p); } void TableExprNodeINString::doOptimize (TENShPtr& rnode) { if (rnode->isConstant()) { if (rnode->valueType() == VTSet) { // A constant set with continuous intervals can be made faster. TableExprNodeSet& set = dynamic_cast(*rnode); if (!set.isSingle() && !set.isDiscrete()) { rnode = TableExprNodeSetOptContSetBase::transform (set); } } else if (rnode->valueType() == VTArray) { // Convert a constant array to an unordered_set for faster lookup. MArray values = rnode->getArrayString(0); Array arr(values.array()); if (values.hasMask()) { // Remove masked elements. arr.reference (values.flatten()); } rnode = std::make_shared>(*rnode, arr); } } } Bool TableExprNodeINString::getBool (const TableExprId& id) { return rnode_p->contains (id, lnode_p->getString (id)); } TableExprNodeINDate::TableExprNodeINDate (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtIN) {} void TableExprNodeINDate::optimize() { doOptimize (rnode_p); } void TableExprNodeINDate::doOptimize (TENShPtr& rnode) { if (rnode->isConstant() && rnode->valueType() == VTSet) { TableExprNodeSet& set = dynamic_cast(*rnode); if (!set.isSingle() && !set.isDiscrete()) { rnode = TableExprNodeSetOptContSetBase::transform (set); } } } Bool TableExprNodeINDate::getBool (const TableExprId& id) { return rnode_p->contains (id, lnode_p->getDate (id)); } TableExprNodeOR::TableExprNodeOR (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtOR) {} Bool TableExprNodeOR::getBool (const TableExprId& id) { return lnode_p->getBool(id) || rnode_p->getBool(id); } TableExprNodeAND::TableExprNodeAND (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtAND) {} Bool TableExprNodeAND::getBool (const TableExprId& id) { return lnode_p->getBool(id) && rnode_p->getBool(id); } TableExprNodeNOT::TableExprNodeNOT (const TableExprNodeRep& node) : TableExprNodeBinary (NTBool, node, OtNOT) {} Bool TableExprNodeNOT::getBool (const TableExprId& id) { return ! lnode_p->getBool(id); } void TableExprNodeEQDouble::ranges (Block& blrange) { Double dval = 0; TENShPtr tsncol = 0; //# We can store a range if there is a scalar column and constant //# (left or right). if (lnode_p->operType() == TableExprNodeRep::OtColumn && lnode_p->valueType() == TableExprNodeRep::VTScalar && rnode_p->operType() == TableExprNodeRep::OtLiteral) { tsncol = lnode_p; dval = rnode_p->getDouble (0); }else{ if (rnode_p->operType() == TableExprNodeRep::OtColumn && rnode_p->valueType() == TableExprNodeRep::VTScalar && lnode_p->operType() == TableExprNodeRep::OtLiteral) { tsncol = rnode_p; dval = lnode_p->getDouble (0); } } //# Now create a range (if possible). //# The cast is harmless, since it is surely that object type. TableExprNodeRep::createRange (blrange, dynamic_cast(tsncol.get()), dval, dval); } void TableExprNodeGEDouble::ranges (Block& blrange) { Double st = 0; Double end = 0; TENShPtr tsncol = 0; //# We can store a range if there is a scalar column and constant //# (left or right). if (lnode_p->operType() == TableExprNodeRep::OtColumn && lnode_p->valueType() == TableExprNodeRep::VTScalar && rnode_p->operType() == TableExprNodeRep::OtLiteral) { tsncol = lnode_p; st = rnode_p->getDouble (0); end = DBL_MAX; }else{ if (rnode_p->operType() == TableExprNodeRep::OtColumn && rnode_p->valueType() == TableExprNodeRep::VTScalar && lnode_p->operType() == TableExprNodeRep::OtLiteral) { tsncol = rnode_p; end = lnode_p->getDouble (0); st = -DBL_MAX; } } //# Now create a range (if possible). //# The cast is harmless, since it is surely that object type. TableExprNodeRep::createRange (blrange, dynamic_cast(tsncol.get()), st, end); } void TableExprNodeGTDouble::ranges (Block& blrange) { Double st = 0; Double end = 0; TENShPtr tsncol = 0; //# We can store a range if there is a scalar column and constant //# (left or right). if (lnode_p->operType() == TableExprNodeRep::OtColumn && lnode_p->valueType() == TableExprNodeRep::VTScalar && rnode_p->operType() == TableExprNodeRep::OtLiteral) { tsncol = lnode_p; st = rnode_p->getDouble (0); end = DBL_MAX; }else{ if (rnode_p->operType() == TableExprNodeRep::OtColumn && lnode_p->valueType() == TableExprNodeRep::VTScalar && lnode_p->operType() == TableExprNodeRep::OtLiteral) { tsncol = rnode_p; end = lnode_p->getDouble (0); st = -DBL_MAX; } } //# Now create a range (if possible). //# The cast is harmless, since it is surely that object type. TableExprNodeRep::createRange (blrange, dynamic_cast(tsncol.get()), st, end); } //# Or two blocks of ranges. void TableExprNodeOR::ranges (Block& blrange) { //# Get the ranges of the children. Block left,right; lnode_p->ranges (left); rnode_p->ranges (right); //# Now or the ranges. //# If a column appears in one, but not in the other it needs //# to be removed. Only equal columns can be combined and what //# gets created is a superset of the original blocks. blrange.resize(0,True); size_t nr=0; for (size_t i=0; i& blrange) { //# Get the ranges of the children. Block other; lnode_p->ranges (blrange); rnode_p->ranges (other); //# If one of them is empty (which means all), return the other. if (other.nelements() == 0) { return; } if (blrange.nelements() == 0) { blrange = other; return; } //# Now and the ranges. //# First handle one and intersect its ranges with matching //# column names in the other one. //# Keep a vector with flags for non-processed other ones. Vector vec(other.nelements()); vec = 0; for (size_t i=0; i #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# This file defines classes derived from TableExprNode representing //# the data type and operator in a table expression. //# //# Data types Bool, Int64, Double, DComplex and String are used. //# Char, uChar, Short, uShort, Int, and uInt are converted to Int64, //# Float to Double, and Complex to DComplex. //# Binary operators ==, >=, >, <, <=, !=, and IN are recognized. //# Also &&, ||, and unary ! are recognized. // // Bool comparison == in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an == comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeEQBool : public TableExprNodeBinary { public: TableExprNodeEQBool (const TableExprNodeRep&); ~TableExprNodeEQBool() = default; Bool getBool (const TableExprId& id) override; }; // // Int comparison == in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an == comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeEQInt : public TableExprNodeBinary { public: TableExprNodeEQInt (const TableExprNodeRep&); ~TableExprNodeEQInt() = default; Bool getBool (const TableExprId& id) override; }; // // Double comparison == in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an == comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeEQDouble : public TableExprNodeBinary { public: TableExprNodeEQDouble (const TableExprNodeRep&); ~TableExprNodeEQDouble() = default; Bool getBool (const TableExprId& id) override; void ranges (Block&) override; }; // // DComplex comparison == in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an == comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeEQDComplex : public TableExprNodeBinary { public: TableExprNodeEQDComplex (const TableExprNodeRep&); ~TableExprNodeEQDComplex() = default; Bool getBool (const TableExprId& id) override; }; // // String comparison == in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an == comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeEQString : public TableExprNodeBinary { public: TableExprNodeEQString (const TableExprNodeRep&); ~TableExprNodeEQString() = default; Bool getBool (const TableExprId& id) override; }; // // Regex comparison == in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an == comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeEQRegex : public TableExprNodeBinary { public: TableExprNodeEQRegex (const TableExprNodeRep&); ~TableExprNodeEQRegex() = default; Bool getBool (const TableExprId& id) override; }; // // Date comparison == in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an == comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeEQDate : public TableExprNodeBinary { public: TableExprNodeEQDate (const TableExprNodeRep&); ~TableExprNodeEQDate() = default; Bool getBool (const TableExprId& id) override; }; // // Bool comparison != in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an != comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeNEBool : public TableExprNodeBinary { public: TableExprNodeNEBool (const TableExprNodeRep&); ~TableExprNodeNEBool() = default; Bool getBool (const TableExprId& id) override; }; // // Int comparison != in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an != comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeNEInt : public TableExprNodeBinary { public: TableExprNodeNEInt (const TableExprNodeRep&); ~TableExprNodeNEInt() = default; Bool getBool (const TableExprId& id) override; }; // // Double comparison != in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an != comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeNEDouble : public TableExprNodeBinary { public: TableExprNodeNEDouble (const TableExprNodeRep&); ~TableExprNodeNEDouble() = default; Bool getBool (const TableExprId& id) override; }; // // DComplex comparison != in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an != comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeNEDComplex : public TableExprNodeBinary { public: TableExprNodeNEDComplex (const TableExprNodeRep&); ~TableExprNodeNEDComplex() = default; Bool getBool (const TableExprId& id) override; }; // // String comparison != in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an != comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeNEString : public TableExprNodeBinary { public: TableExprNodeNEString (const TableExprNodeRep&); ~TableExprNodeNEString() = default; Bool getBool (const TableExprId& id) override; }; // // Regex comparison != in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an != comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeNERegex : public TableExprNodeBinary { public: TableExprNodeNERegex (const TableExprNodeRep&); ~TableExprNodeNERegex() = default; Bool getBool (const TableExprId& id) override; }; // // Date comparison != in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an != comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeNEDate : public TableExprNodeBinary { public: TableExprNodeNEDate (const TableExprNodeRep&); ~TableExprNodeNEDate() = default; Bool getBool (const TableExprId& id) override; }; // // Int comparison > in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an > comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeGTInt : public TableExprNodeBinary { public: TableExprNodeGTInt (const TableExprNodeRep&); ~TableExprNodeGTInt() = default; Bool getBool (const TableExprId& id) override; }; // // Double comparison > in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an > comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeGTDouble : public TableExprNodeBinary { public: TableExprNodeGTDouble (const TableExprNodeRep&); ~TableExprNodeGTDouble() = default; Bool getBool (const TableExprId& id) override; void ranges (Block&) override; }; // // DComplex comparison > in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an > comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeGTDComplex : public TableExprNodeBinary { public: TableExprNodeGTDComplex (const TableExprNodeRep&); ~TableExprNodeGTDComplex() = default; Bool getBool (const TableExprId& id) override; }; // // String comparison > in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an > comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeGTString : public TableExprNodeBinary { public: TableExprNodeGTString (const TableExprNodeRep&); ~TableExprNodeGTString() = default; Bool getBool (const TableExprId& id) override; }; // // Date comparison > in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an > comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeGTDate : public TableExprNodeBinary { public: TableExprNodeGTDate (const TableExprNodeRep&); ~TableExprNodeGTDate() = default; Bool getBool (const TableExprId& id) override; }; // // Int comparison >= in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an >= comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeGEInt : public TableExprNodeBinary { public: TableExprNodeGEInt (const TableExprNodeRep&); ~TableExprNodeGEInt() = default; Bool getBool (const TableExprId& id) override; }; // // Double comparison >= in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an >= comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeGEDouble : public TableExprNodeBinary { public: TableExprNodeGEDouble (const TableExprNodeRep&); ~TableExprNodeGEDouble() = default; Bool getBool (const TableExprId& id) override; void ranges (Block&) override; }; // // DComplex comparison >= in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an >= comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeGEDComplex : public TableExprNodeBinary { public: TableExprNodeGEDComplex (const TableExprNodeRep&); ~TableExprNodeGEDComplex() = default; Bool getBool (const TableExprId& id) override; }; // // String comparison >= in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an >= comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeGEString : public TableExprNodeBinary { public: TableExprNodeGEString (const TableExprNodeRep&); ~TableExprNodeGEString() = default; Bool getBool (const TableExprId& id) override; }; // // Date comparison >= in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an >= comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeGEDate : public TableExprNodeBinary { public: TableExprNodeGEDate (const TableExprNodeRep&); ~TableExprNodeGEDate() = default; Bool getBool (const TableExprId& id) override; }; // // Int comparison IN in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an IN comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // The right hand side can be optimized if it contains a constant array which // can be replaced by an std::unordered_set or a Block. // class TableExprNodeINInt : public TableExprNodeBinary { public: // doTracing is not used. TableExprNodeINInt (const TableExprNodeRep&, Bool doTracing=False); ~TableExprNodeINInt() = default; void optimize() override; static void doOptimize (TENShPtr& rnode); Bool getBool (const TableExprId& id) override; private: }; // // Double comparison IN in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an IN comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // The right hand side can be optimized if it contains a constant set with // bounded intervals. // class TableExprNodeINDouble : public TableExprNodeBinary { public: TableExprNodeINDouble (const TableExprNodeRep&); ~TableExprNodeINDouble() = default; void optimize() override; static void doOptimize (TENShPtr& rnode); Bool getBool (const TableExprId& id) override; }; // // DComplex comparison IN in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an IN comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeINDComplex : public TableExprNodeBinary { public: TableExprNodeINDComplex (const TableExprNodeRep&); ~TableExprNodeINDComplex() = default; Bool getBool (const TableExprId& id) override; }; // // String comparison IN in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an IN comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // The right hand side can be optimized if it contains a constant array which // can be replaced by an std::unordered_set // class TableExprNodeINString : public TableExprNodeBinary { public: TableExprNodeINString (const TableExprNodeRep&); ~TableExprNodeINString() = default; void optimize() override; static void doOptimize (TENShPtr& rnode); Bool getBool (const TableExprId& id) override; }; // // Date comparison IN in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an IN comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeINDate : public TableExprNodeBinary { public: TableExprNodeINDate (const TableExprNodeRep&); ~TableExprNodeINDate() = default; void optimize() override; static void doOptimize (TENShPtr& rnode); Bool getBool (const TableExprId& id) override; }; // // Logical or in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents a logical or in a table select expression tree. // This is defined for Bool only. // class TableExprNodeOR : public TableExprNodeBinary { public: TableExprNodeOR (const TableExprNodeRep&); ~TableExprNodeOR() = default; Bool getBool (const TableExprId& id) override; void ranges (Block&) override; }; // // Logical and in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents a logical and in a table select expression tree. // This is defined for Bool only. // class TableExprNodeAND: public TableExprNodeBinary { public: TableExprNodeAND (const TableExprNodeRep&); ~TableExprNodeAND() = default; Bool getBool (const TableExprId& id) override; void ranges (Block&) override; }; // // Logical not in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents a logical not in a table select expression tree. // This is defined for Bool only. // class TableExprNodeNOT: public TableExprNodeBinary { public: TableExprNodeNOT (const TableExprNodeRep&); ~TableExprNodeNOT() = default; Bool getBool (const TableExprId& id) override; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/ExprLogicNodeArray.cc000066400000000000000000000477301476623553700214220ustar00rootroot00000000000000//# ExprLogicNodeArray.cc: Nodes representing logical array operators in table select expression tree //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableExprNodeArrayEQBool::TableExprNodeArrayEQBool (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtEQ) {} TableExprNodeArrayEQBool::~TableExprNodeArrayEQBool() {} MArray TableExprNodeArrayEQBool::getArrayBool (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayBool (id) == rnode_p->getBool (id); case ScaArr: return lnode_p->getBool (id) == rnode_p->getArrayBool (id); default: break; } return lnode_p->getArrayBool (id) == rnode_p->getArrayBool (id); } TableExprNodeArrayEQInt::TableExprNodeArrayEQInt (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtEQ) {} TableExprNodeArrayEQInt::~TableExprNodeArrayEQInt() {} MArray TableExprNodeArrayEQInt::getArrayBool (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayInt (id) == rnode_p->getInt (id); case ScaArr: return lnode_p->getInt (id) == rnode_p->getArrayInt (id); default: break; } return lnode_p->getArrayInt (id) == rnode_p->getArrayInt (id); } TableExprNodeArrayEQDouble::TableExprNodeArrayEQDouble (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtEQ) {} TableExprNodeArrayEQDouble::~TableExprNodeArrayEQDouble() {} MArray TableExprNodeArrayEQDouble::getArrayBool (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayDouble (id) == rnode_p->getDouble (id); case ScaArr: return lnode_p->getDouble (id) == rnode_p->getArrayDouble (id); default: break; } return lnode_p->getArrayDouble (id) == rnode_p->getArrayDouble (id); } TableExprNodeArrayEQDComplex::TableExprNodeArrayEQDComplex (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtEQ) {} TableExprNodeArrayEQDComplex::~TableExprNodeArrayEQDComplex() {} MArray TableExprNodeArrayEQDComplex::getArrayBool (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayDComplex(id) == rnode_p->getDComplex(id); case ScaArr: return lnode_p->getDComplex(id) == rnode_p->getArrayDComplex(id); default: break; } return lnode_p->getArrayDComplex(id) == rnode_p->getArrayDComplex(id); } TableExprNodeArrayEQString::TableExprNodeArrayEQString (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtEQ) {} TableExprNodeArrayEQString::~TableExprNodeArrayEQString() {} MArray TableExprNodeArrayEQString::getArrayBool (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayString (id) == rnode_p->getString (id); case ScaArr: return lnode_p->getString (id) == rnode_p->getArrayString (id); default: break; } return lnode_p->getArrayString (id) == rnode_p->getArrayString (id); } TableExprNodeArrayEQRegex::TableExprNodeArrayEQRegex (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtEQ) {} TableExprNodeArrayEQRegex::~TableExprNodeArrayEQRegex() {} MArray TableExprNodeArrayEQRegex::getArrayBool (const TableExprId& id) { MArray left = lnode_p->getArrayString(id); Array result(left.shape()); TaqlRegex regex = rnode_p->getRegex(id); Array::const_iterator liter = left.array().begin(); Array::contiter riterend = result.cend(); for (Array::contiter riter = result.cbegin(); riter != riterend; ++riter, ++liter) { *riter = regex.match (*liter); } return MArray (result, left.mask()); } TableExprNodeArrayEQDate::TableExprNodeArrayEQDate (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtEQ) {} TableExprNodeArrayEQDate::~TableExprNodeArrayEQDate() {} MArray TableExprNodeArrayEQDate::getArrayBool (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayDate(id) != rnode_p->getDate(id); case ScaArr: return lnode_p->getDate(id) != rnode_p->getArrayDate(id); default: break; } return lnode_p->getArrayDate(id) != rnode_p->getArrayDate(id); } TableExprNodeArrayNEBool::TableExprNodeArrayNEBool (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtNE) {} TableExprNodeArrayNEBool::~TableExprNodeArrayNEBool() {} MArray TableExprNodeArrayNEBool::getArrayBool (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayBool (id) != rnode_p->getBool (id); case ScaArr: return lnode_p->getBool (id) != rnode_p->getArrayBool (id); default: break; } return lnode_p->getArrayBool (id) != rnode_p->getArrayBool (id); } TableExprNodeArrayNEInt::TableExprNodeArrayNEInt (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtNE) {} TableExprNodeArrayNEInt::~TableExprNodeArrayNEInt() {} MArray TableExprNodeArrayNEInt::getArrayBool (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayInt (id) != rnode_p->getInt (id); case ScaArr: return lnode_p->getInt (id) != rnode_p->getArrayInt (id); default: break; } return lnode_p->getArrayInt (id) != rnode_p->getArrayInt (id); } TableExprNodeArrayNEDouble::TableExprNodeArrayNEDouble (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtNE) {} TableExprNodeArrayNEDouble::~TableExprNodeArrayNEDouble() {} MArray TableExprNodeArrayNEDouble::getArrayBool (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayDouble (id) != rnode_p->getDouble (id); case ScaArr: return lnode_p->getDouble (id) != rnode_p->getArrayDouble (id); default: break; } return lnode_p->getArrayDouble (id) != rnode_p->getArrayDouble (id); } TableExprNodeArrayNEDComplex::TableExprNodeArrayNEDComplex (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtNE) {} TableExprNodeArrayNEDComplex::~TableExprNodeArrayNEDComplex() {} MArray TableExprNodeArrayNEDComplex::getArrayBool (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayDComplex(id) != rnode_p->getDComplex(id); case ScaArr: return lnode_p->getDComplex(id) != rnode_p->getArrayDComplex(id); default: break; } return lnode_p->getArrayDComplex(id) != rnode_p->getArrayDComplex(id); } TableExprNodeArrayNEString::TableExprNodeArrayNEString (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtNE) {} TableExprNodeArrayNEString::~TableExprNodeArrayNEString() {} MArray TableExprNodeArrayNEString::getArrayBool (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayString (id) != rnode_p->getString (id); case ScaArr: return lnode_p->getString (id) != rnode_p->getArrayString (id); default: break; } return lnode_p->getArrayString (id) != rnode_p->getArrayString (id); } TableExprNodeArrayNERegex::TableExprNodeArrayNERegex (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtNE) {} TableExprNodeArrayNERegex::~TableExprNodeArrayNERegex() {} MArray TableExprNodeArrayNERegex::getArrayBool (const TableExprId& id) { MArray left = lnode_p->getArrayString(id); Array result(left.shape()); TaqlRegex regex = rnode_p->getRegex(id); Array::const_iterator liter = left.array().begin(); Array::contiter riterend = result.cend(); for (Array::contiter riter = result.cbegin(); riter != riterend; ++riter, ++liter) { *riter = !regex.match (*liter); } return MArray (result, left.mask()); } TableExprNodeArrayNEDate::TableExprNodeArrayNEDate (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtNE) {} TableExprNodeArrayNEDate::~TableExprNodeArrayNEDate() {} MArray TableExprNodeArrayNEDate::getArrayBool (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayDate(id) != rnode_p->getDate(id); case ScaArr: return lnode_p->getDate(id) != rnode_p->getArrayDate(id); default: break; } return lnode_p->getArrayDate(id) != rnode_p->getArrayDate(id); } TableExprNodeArrayGTInt::TableExprNodeArrayGTInt (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtGT) {} TableExprNodeArrayGTInt::~TableExprNodeArrayGTInt() {} MArray TableExprNodeArrayGTInt::getArrayBool (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayInt(id) > rnode_p->getInt(id); case ScaArr: return lnode_p->getInt(id) > rnode_p->getArrayInt(id); default: break; } return lnode_p->getArrayInt(id) > rnode_p->getArrayInt(id); } TableExprNodeArrayGTDouble::TableExprNodeArrayGTDouble (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtGT) {} TableExprNodeArrayGTDouble::~TableExprNodeArrayGTDouble() {} MArray TableExprNodeArrayGTDouble::getArrayBool (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayDouble(id) > rnode_p->getDouble(id); case ScaArr: return lnode_p->getDouble(id) > rnode_p->getArrayDouble(id); default: break; } return lnode_p->getArrayDouble(id) > rnode_p->getArrayDouble(id); } TableExprNodeArrayGTDComplex::TableExprNodeArrayGTDComplex (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtGT) {} TableExprNodeArrayGTDComplex::~TableExprNodeArrayGTDComplex() {} MArray TableExprNodeArrayGTDComplex::getArrayBool (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayDComplex(id) > rnode_p->getDComplex(id); case ScaArr: return lnode_p->getDComplex(id) > rnode_p->getArrayDComplex(id); default: break; } return lnode_p->getArrayDComplex(id) > rnode_p->getArrayDComplex(id); } TableExprNodeArrayGTString::TableExprNodeArrayGTString (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtGT) {} TableExprNodeArrayGTString::~TableExprNodeArrayGTString() {} MArray TableExprNodeArrayGTString::getArrayBool (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayString(id) > rnode_p->getString(id); case ScaArr: return lnode_p->getString(id) > rnode_p->getArrayString(id); default: break; } return lnode_p->getArrayString(id) > rnode_p->getArrayString(id); } TableExprNodeArrayGTDate::TableExprNodeArrayGTDate (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtGT) {} TableExprNodeArrayGTDate::~TableExprNodeArrayGTDate() {} MArray TableExprNodeArrayGTDate::getArrayBool (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayDate(id) > rnode_p->getDate(id); case ScaArr: return lnode_p->getDate(id) > rnode_p->getArrayDate(id); default: break; } return lnode_p->getArrayDate(id) > rnode_p->getArrayDate(id); } TableExprNodeArrayGEInt::TableExprNodeArrayGEInt (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtGE) {} TableExprNodeArrayGEInt::~TableExprNodeArrayGEInt() {} MArray TableExprNodeArrayGEInt::getArrayBool (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayInt(id) >= rnode_p->getInt(id); case ScaArr: return lnode_p->getInt(id) >= rnode_p->getArrayInt(id); default: break; } return lnode_p->getArrayInt(id) >= rnode_p->getArrayInt(id); } TableExprNodeArrayGEDouble::TableExprNodeArrayGEDouble (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtGE) {} TableExprNodeArrayGEDouble::~TableExprNodeArrayGEDouble() {} MArray TableExprNodeArrayGEDouble::getArrayBool (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayDouble(id) >= rnode_p->getDouble(id); case ScaArr: return lnode_p->getDouble(id) >= rnode_p->getArrayDouble(id); default: break; } return lnode_p->getArrayDouble(id) >= rnode_p->getArrayDouble(id); } TableExprNodeArrayGEDComplex::TableExprNodeArrayGEDComplex (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtGE) {} TableExprNodeArrayGEDComplex::~TableExprNodeArrayGEDComplex() {} MArray TableExprNodeArrayGEDComplex::getArrayBool (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayDComplex(id) >= rnode_p->getDComplex(id); case ScaArr: return lnode_p->getDComplex(id) >= rnode_p->getArrayDComplex(id); default: break; } return lnode_p->getArrayDComplex(id) >= rnode_p->getArrayDComplex(id); } TableExprNodeArrayGEString::TableExprNodeArrayGEString (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtGE) {} TableExprNodeArrayGEString::~TableExprNodeArrayGEString() {} MArray TableExprNodeArrayGEString::getArrayBool (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayString(id) >= rnode_p->getString(id); case ScaArr: return lnode_p->getString(id) >= rnode_p->getArrayString(id); default: break; } return lnode_p->getArrayString(id) >= rnode_p->getArrayString(id); } TableExprNodeArrayGEDate::TableExprNodeArrayGEDate (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtGE) {} TableExprNodeArrayGEDate::~TableExprNodeArrayGEDate() {} MArray TableExprNodeArrayGEDate::getArrayBool (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayDate(id) >= rnode_p->getDate(id); case ScaArr: return lnode_p->getDate(id) >= rnode_p->getArrayDate(id); default: break; } return lnode_p->getArrayDate(id) >= rnode_p->getArrayDate(id); } TableExprNodeArrayINInt::TableExprNodeArrayINInt (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtIN) {} TableExprNodeArrayINInt::~TableExprNodeArrayINInt() {} void TableExprNodeArrayINInt::optimize() { TableExprNodeINInt::doOptimize (rnode_p); } MArray TableExprNodeArrayINInt::getArrayBool (const TableExprId& id) { return rnode_p->contains (id, lnode_p->getArrayInt (id)); } TableExprNodeArrayINDouble::TableExprNodeArrayINDouble (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtIN) {} TableExprNodeArrayINDouble::~TableExprNodeArrayINDouble() {} void TableExprNodeArrayINDouble::optimize() { TableExprNodeINDouble::doOptimize (rnode_p); } MArray TableExprNodeArrayINDouble::getArrayBool (const TableExprId& id) { return rnode_p->contains (id, lnode_p->getArrayDouble (id)); } TableExprNodeArrayINDComplex::TableExprNodeArrayINDComplex (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtIN) {} TableExprNodeArrayINDComplex::~TableExprNodeArrayINDComplex() {} MArray TableExprNodeArrayINDComplex::getArrayBool (const TableExprId& id) { return rnode_p->contains (id, lnode_p->getArrayDComplex (id)); } TableExprNodeArrayINString::TableExprNodeArrayINString (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtIN) {} TableExprNodeArrayINString::~TableExprNodeArrayINString() {} void TableExprNodeArrayINString::optimize() { TableExprNodeINString::doOptimize (rnode_p); } MArray TableExprNodeArrayINString::getArrayBool (const TableExprId& id) { return rnode_p->contains (id, lnode_p->getArrayString (id)); } TableExprNodeArrayINDate::TableExprNodeArrayINDate (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtIN) {} TableExprNodeArrayINDate::~TableExprNodeArrayINDate() {} void TableExprNodeArrayINDate::optimize() { TableExprNodeINDate::doOptimize (rnode_p); } MArray TableExprNodeArrayINDate::getArrayBool (const TableExprId& id) { return rnode_p->contains (id, lnode_p->getArrayDate (id)); } TableExprNodeArrayOR::TableExprNodeArrayOR (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtOR) {} TableExprNodeArrayOR::~TableExprNodeArrayOR() {} MArray TableExprNodeArrayOR::getArrayBool (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayBool(id) || rnode_p->getBool(id); case ScaArr: return lnode_p->getBool(id) || rnode_p->getArrayBool(id); default: break; } return lnode_p->getArrayBool(id) || rnode_p->getArrayBool(id); } TableExprNodeArrayAND::TableExprNodeArrayAND (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtAND) {} TableExprNodeArrayAND::~TableExprNodeArrayAND() {} MArray TableExprNodeArrayAND::getArrayBool (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayBool(id) && rnode_p->getBool(id); case ScaArr: return lnode_p->getBool(id) && rnode_p->getArrayBool(id); default: break; } return lnode_p->getArrayBool(id) && rnode_p->getArrayBool(id); } TableExprNodeArrayNOT::TableExprNodeArrayNOT (const TableExprNodeRep& node) : TableExprNodeArray (node, NTBool, OtNOT) {} TableExprNodeArrayNOT::~TableExprNodeArrayNOT() {} MArray TableExprNodeArrayNOT::getArrayBool (const TableExprId& id) { return !(lnode_p->getArrayBool(id)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/ExprLogicNodeArray.h000066400000000000000000000650031476623553700212550ustar00rootroot00000000000000//# ExprLogicArrayNode.h: Nodes representing logical array operators in table select expression tree //# Copyright (C) 1997,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_EXPRLOGICNODEARRAY_H #define TABLES_EXPRLOGICNODEARRAY_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations //# This file defines classes derived from TableExprNode representing //# the data type and operator in a table expression. //# //# Data types Bool, Int64, Double, DComplex and String are used. //# Char, uChar, Short, uShort, Int, and uInt are converted to Int64, //# Float to Double, and Complex to DComplex. //# Binary operators ==, >=, >, <, <= and != are recognized. //# Also &&, ||, and unary ! are recognized. // // Bool Array comparison == in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an == comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeArrayEQBool : public TableExprNodeArray { public: TableExprNodeArrayEQBool (const TableExprNodeRep&); ~TableExprNodeArrayEQBool(); MArray getArrayBool (const TableExprId& id); }; // // Int Array comparison == in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an == comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeArrayEQInt : public TableExprNodeArray { public: TableExprNodeArrayEQInt (const TableExprNodeRep&); ~TableExprNodeArrayEQInt(); MArray getArrayBool (const TableExprId& id); }; // // Double Array comparison == in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an == comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeArrayEQDouble : public TableExprNodeArray { public: TableExprNodeArrayEQDouble (const TableExprNodeRep&); ~TableExprNodeArrayEQDouble(); MArray getArrayBool (const TableExprId& id); }; // // DComplex Array comparison == in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an == comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeArrayEQDComplex : public TableExprNodeArray { public: TableExprNodeArrayEQDComplex (const TableExprNodeRep&); ~TableExprNodeArrayEQDComplex(); MArray getArrayBool (const TableExprId& id); }; // // String Array comparison == in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an == comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeArrayEQString : public TableExprNodeArray { public: TableExprNodeArrayEQString (const TableExprNodeRep&); ~TableExprNodeArrayEQString(); MArray getArrayBool (const TableExprId& id); }; // // Regex Array comparison == in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an == comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeArrayEQRegex : public TableExprNodeArray { public: TableExprNodeArrayEQRegex (const TableExprNodeRep&); ~TableExprNodeArrayEQRegex(); MArray getArrayBool (const TableExprId& id); }; // // Date Array comparison == in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an == comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeArrayEQDate : public TableExprNodeArray { public: TableExprNodeArrayEQDate (const TableExprNodeRep&); ~TableExprNodeArrayEQDate(); MArray getArrayBool (const TableExprId& id); }; // // Bool Array comparison != in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an != comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeArrayNEBool : public TableExprNodeArray { public: TableExprNodeArrayNEBool (const TableExprNodeRep&); ~TableExprNodeArrayNEBool(); MArray getArrayBool (const TableExprId& id); }; // // Int Array comparison != in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an != comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeArrayNEInt : public TableExprNodeArray { public: TableExprNodeArrayNEInt (const TableExprNodeRep&); ~TableExprNodeArrayNEInt(); MArray getArrayBool (const TableExprId& id); }; // // Double Array comparison != in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an != comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeArrayNEDouble : public TableExprNodeArray { public: TableExprNodeArrayNEDouble (const TableExprNodeRep&); ~TableExprNodeArrayNEDouble(); MArray getArrayBool (const TableExprId& id); }; // // DComplex Array comparison != in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an != comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeArrayNEDComplex : public TableExprNodeArray { public: TableExprNodeArrayNEDComplex (const TableExprNodeRep&); ~TableExprNodeArrayNEDComplex(); MArray getArrayBool (const TableExprId& id); }; // // String Array comparison != in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an != comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeArrayNEString : public TableExprNodeArray { public: TableExprNodeArrayNEString (const TableExprNodeRep&); ~TableExprNodeArrayNEString(); MArray getArrayBool (const TableExprId& id); }; // // Regex Array comparison != in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an != comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeArrayNERegex : public TableExprNodeArray { public: TableExprNodeArrayNERegex (const TableExprNodeRep&); ~TableExprNodeArrayNERegex(); MArray getArrayBool (const TableExprId& id); }; // // Date Array comparison != in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an != comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeArrayNEDate : public TableExprNodeArray { public: TableExprNodeArrayNEDate (const TableExprNodeRep&); ~TableExprNodeArrayNEDate(); MArray getArrayBool (const TableExprId& id); }; // // Int Array comparison > in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an > comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeArrayGTInt : public TableExprNodeArray { public: TableExprNodeArrayGTInt (const TableExprNodeRep&); ~TableExprNodeArrayGTInt(); MArray getArrayBool (const TableExprId& id); }; // // Double Array comparison > in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an > comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeArrayGTDouble : public TableExprNodeArray { public: TableExprNodeArrayGTDouble (const TableExprNodeRep&); ~TableExprNodeArrayGTDouble(); MArray getArrayBool (const TableExprId& id); }; // // DComplex Array comparison > in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an > comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeArrayGTDComplex : public TableExprNodeArray { public: TableExprNodeArrayGTDComplex (const TableExprNodeRep&); ~TableExprNodeArrayGTDComplex(); MArray getArrayBool (const TableExprId& id); }; // // String Array comparison > in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an > comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeArrayGTString : public TableExprNodeArray { public: TableExprNodeArrayGTString (const TableExprNodeRep&); ~TableExprNodeArrayGTString(); MArray getArrayBool (const TableExprId& id); }; // // Date Array comparison > in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an > comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeArrayGTDate : public TableExprNodeArray { public: TableExprNodeArrayGTDate (const TableExprNodeRep&); ~TableExprNodeArrayGTDate(); MArray getArrayBool (const TableExprId& id); }; // // Int Array comparison >= in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an >= comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeArrayGEInt : public TableExprNodeArray { public: TableExprNodeArrayGEInt (const TableExprNodeRep&); ~TableExprNodeArrayGEInt(); MArray getArrayBool (const TableExprId& id); }; // // Double Array comparison >= in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an >= comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeArrayGEDouble : public TableExprNodeArray { public: TableExprNodeArrayGEDouble (const TableExprNodeRep&); ~TableExprNodeArrayGEDouble(); MArray getArrayBool (const TableExprId& id); }; // // DComplex Array comparison >= in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an >= comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeArrayGEDComplex : public TableExprNodeArray { public: TableExprNodeArrayGEDComplex (const TableExprNodeRep&); ~TableExprNodeArrayGEDComplex(); MArray getArrayBool (const TableExprId& id); }; // // String Array comparison >= in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an >= comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeArrayGEString : public TableExprNodeArray { public: TableExprNodeArrayGEString (const TableExprNodeRep&); ~TableExprNodeArrayGEString(); MArray getArrayBool (const TableExprId& id); }; // // Date Array comparison >= in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an >= comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeArrayGEDate : public TableExprNodeArray { public: TableExprNodeArrayGEDate (const TableExprNodeRep&); ~TableExprNodeArrayGEDate(); MArray getArrayBool (const TableExprId& id); }; // // Int Array comparison IN in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an IN comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // The right hand side can be optimized if it contains a constant array which // can be replaced by an std::unordered_set or a Block. // class TableExprNodeArrayINInt : public TableExprNodeArray { public: TableExprNodeArrayINInt (const TableExprNodeRep&); ~TableExprNodeArrayINInt(); virtual void optimize() override; virtual MArray getArrayBool (const TableExprId& id) override; }; // // Double Array comparison IN in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an IN comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeArrayINDouble : public TableExprNodeArray { public: TableExprNodeArrayINDouble (const TableExprNodeRep&); ~TableExprNodeArrayINDouble(); virtual void optimize() override; MArray getArrayBool (const TableExprId& id) override; }; // // DComplex Array comparison IN in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an IN comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeArrayINDComplex : public TableExprNodeArray { public: TableExprNodeArrayINDComplex (const TableExprNodeRep&); ~TableExprNodeArrayINDComplex(); MArray getArrayBool (const TableExprId& id); }; // // String Array comparison IN in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an IN comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // The right hand side can be optimized if it contains a constant array which // can be replaced by an std::unordered_set // class TableExprNodeArrayINString : public TableExprNodeArray { public: TableExprNodeArrayINString (const TableExprNodeRep&); ~TableExprNodeArrayINString(); virtual void optimize() override; virtual MArray getArrayBool (const TableExprId& id) override; }; // // Date Array comparison IN in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents an IN comparison in a table select expression tree. // This is defined for all data types. // Only the Bool get function is defined, because the result of a // compare is always a Bool. // class TableExprNodeArrayINDate : public TableExprNodeArray { public: TableExprNodeArrayINDate (const TableExprNodeRep&); ~TableExprNodeArrayINDate(); virtual void optimize() override; MArray getArrayBool (const TableExprId& id) override; }; // // Logical or in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents a logical or in a table select expression tree. // This is defined for Bool only. // class TableExprNodeArrayOR : public TableExprNodeArray { public: TableExprNodeArrayOR (const TableExprNodeRep&); ~TableExprNodeArrayOR(); MArray getArrayBool (const TableExprId& id); }; // // Logical and in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents a logical and in a table select expression tree. // This is defined for Bool only. // class TableExprNodeArrayAND: public TableExprNodeArray { public: TableExprNodeArrayAND (const TableExprNodeRep&); ~TableExprNodeArrayAND(); MArray getArrayBool (const TableExprId& id); }; // // Logical not in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeArray // // // This class represents a logical not in a table select expression tree. // This is defined for Bool only. // class TableExprNodeArrayNOT: public TableExprNodeArray { public: TableExprNodeArrayNOT (const TableExprNodeRep&); ~TableExprNodeArrayNOT(); MArray getArrayBool (const TableExprId& id); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/ExprMathNode.cc000066400000000000000000000343141476623553700202510ustar00rootroot00000000000000//# ExprMathNode.cc: Nodes representing scalar mathematical operators in table select expression tree //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Implement the arithmetic operators for each data type. TableExprNodePlus::TableExprNodePlus (NodeDataType dt, const TableExprNodeRep& node) : TableExprNodeBinary (dt, node, OtPlus) {} TableExprNodePlus::~TableExprNodePlus() {} TableExprNodePlusInt::TableExprNodePlusInt (const TableExprNodeRep& node) : TableExprNodePlus (NTInt, node) {} TableExprNodePlusInt::~TableExprNodePlusInt() {} Int64 TableExprNodePlusInt::getInt (const TableExprId& id) { return lnode_p->getInt(id) + rnode_p->getInt(id); } Double TableExprNodePlusInt::getDouble (const TableExprId& id) { return lnode_p->getInt(id) + rnode_p->getInt(id); } DComplex TableExprNodePlusInt::getDComplex (const TableExprId& id) { return double(lnode_p->getInt(id) + rnode_p->getInt(id)); } TableExprNodePlusDouble::TableExprNodePlusDouble (const TableExprNodeRep& node) : TableExprNodePlus (NTDouble, node) {} TableExprNodePlusDouble::~TableExprNodePlusDouble() {} Double TableExprNodePlusDouble::getDouble (const TableExprId& id) { return lnode_p->getDouble(id) + rnode_p->getDouble(id); } DComplex TableExprNodePlusDouble::getDComplex (const TableExprId& id) { return lnode_p->getDouble(id) + rnode_p->getDouble(id); } TableExprNodePlusDComplex::TableExprNodePlusDComplex (const TableExprNodeRep& node) : TableExprNodePlus (NTComplex, node) {} TableExprNodePlusDComplex::~TableExprNodePlusDComplex() {} DComplex TableExprNodePlusDComplex::getDComplex (const TableExprId& id) { return lnode_p->getDComplex(id) + rnode_p->getDComplex(id); } TableExprNodePlusString::TableExprNodePlusString (const TableExprNodeRep& node) : TableExprNodePlus (NTString, node) {} TableExprNodePlusString::~TableExprNodePlusString() {} String TableExprNodePlusString::getString (const TableExprId& id) { return lnode_p->getString(id) + rnode_p->getString(id); } TableExprNodePlusDate::TableExprNodePlusDate (const TableExprNodeRep& node) : TableExprNodePlus (NTDate, node) {} TableExprNodePlusDate::~TableExprNodePlusDate() {} void TableExprNodePlusDate::handleUnits() { if (lnode_p->dataType() == NTDouble) { TableExprNodeUnit::adaptUnit (lnode_p, "d"); } else if (rnode_p->dataType() == NTDouble) { TableExprNodeUnit::adaptUnit (rnode_p, "d"); } } MVTime TableExprNodePlusDate::getDate(const TableExprId& id) { return lnode_p->getDouble(id) + rnode_p->getDouble(id); } Double TableExprNodePlusDate::getDouble(const TableExprId& id) { return lnode_p->getDouble(id) + rnode_p->getDouble(id); } TableExprNodeMinus::TableExprNodeMinus (NodeDataType dt, const TableExprNodeRep& node) : TableExprNodeBinary (dt, node, OtMinus) {} TableExprNodeMinus::~TableExprNodeMinus() {} TableExprNodeMinusInt::TableExprNodeMinusInt (const TableExprNodeRep& node) : TableExprNodeMinus (NTInt, node) {} TableExprNodeMinusInt::~TableExprNodeMinusInt() {} void TableExprNodeMinusInt::handleUnits() { if (lnode_p->dataType() == NTDate && rnode_p->dataType() == NTDate) { setUnit("d"); //# date-date results in days } else { TableExprNodeBinary::handleUnits(); } } Int64 TableExprNodeMinusInt::getInt (const TableExprId& id) { return lnode_p->getInt(id) - rnode_p->getInt(id); } Double TableExprNodeMinusInt::getDouble (const TableExprId& id) { return lnode_p->getInt(id) - rnode_p->getInt(id); } DComplex TableExprNodeMinusInt::getDComplex (const TableExprId& id) { return double(lnode_p->getInt(id) - rnode_p->getInt(id)); } TableExprNodeMinusDouble::TableExprNodeMinusDouble (const TableExprNodeRep& node) : TableExprNodeMinus (NTDouble, node) {} TableExprNodeMinusDouble::~TableExprNodeMinusDouble() {} void TableExprNodeMinusDouble::handleUnits() { if (lnode_p->dataType() == NTDate && rnode_p->dataType() == NTDate) { setUnit("d"); //# date-date results in days } else { TableExprNodeBinary::handleUnits(); } } Double TableExprNodeMinusDouble::getDouble (const TableExprId& id) { return lnode_p->getDouble(id) - rnode_p->getDouble(id); } DComplex TableExprNodeMinusDouble::getDComplex (const TableExprId& id) { return lnode_p->getDouble(id) - rnode_p->getDouble(id); } TableExprNodeMinusDComplex::TableExprNodeMinusDComplex (const TableExprNodeRep& node) : TableExprNodeMinus (NTComplex, node) {} TableExprNodeMinusDComplex::~TableExprNodeMinusDComplex() {} DComplex TableExprNodeMinusDComplex::getDComplex (const TableExprId& id) { return lnode_p->getDComplex(id) - rnode_p->getDComplex(id); } TableExprNodeMinusDate::TableExprNodeMinusDate (const TableExprNodeRep& node) : TableExprNodeMinus (NTDate, node) {} TableExprNodeMinusDate::~TableExprNodeMinusDate() {} void TableExprNodeMinusDate::handleUnits() { // Right hand side must be in days. TableExprNodeUnit::adaptUnit (rnode_p, "d"); } MVTime TableExprNodeMinusDate::getDate(const TableExprId& id) { return lnode_p->getDouble(id) - rnode_p->getDouble(id); } Double TableExprNodeMinusDate::getDouble(const TableExprId& id) { return lnode_p->getDouble(id) - rnode_p->getDouble(id); } TableExprNodeTimes::TableExprNodeTimes (NodeDataType dt, const TableExprNodeRep& node) : TableExprNodeBinary (dt, node, OtTimes) {} TableExprNodeTimes::~TableExprNodeTimes() {} void TableExprNodeTimes::handleUnits() { if (lnode_p->unit().empty()) { setUnit (rnode_p->unit()); } else if (rnode_p->unit().empty()) { setUnit (lnode_p->unit()); } else { Quantity q1 (1, lnode_p->unit()); Quantity q2 (1, rnode_p->unit()); setUnit ((q1*q2).getFullUnit()); } } TableExprNodeTimesInt::TableExprNodeTimesInt (const TableExprNodeRep& node) : TableExprNodeTimes (NTInt, node) {} TableExprNodeTimesInt::~TableExprNodeTimesInt() {} Int64 TableExprNodeTimesInt::getInt (const TableExprId& id) { return lnode_p->getInt(id) * rnode_p->getInt(id); } Double TableExprNodeTimesInt::getDouble (const TableExprId& id) { return lnode_p->getInt(id) * rnode_p->getInt(id); } DComplex TableExprNodeTimesInt::getDComplex (const TableExprId& id) { return double(lnode_p->getInt(id) * rnode_p->getInt(id)); } TableExprNodeTimesDouble::TableExprNodeTimesDouble (const TableExprNodeRep& node) : TableExprNodeTimes (NTDouble, node) {} TableExprNodeTimesDouble::~TableExprNodeTimesDouble() {} Double TableExprNodeTimesDouble::getDouble (const TableExprId& id) { return lnode_p->getDouble(id) * rnode_p->getDouble(id); } DComplex TableExprNodeTimesDouble::getDComplex (const TableExprId& id) { return lnode_p->getDouble(id) * rnode_p->getDouble(id); } TableExprNodeTimesDComplex::TableExprNodeTimesDComplex (const TableExprNodeRep& node) : TableExprNodeTimes (NTComplex, node) {} TableExprNodeTimesDComplex::~TableExprNodeTimesDComplex() {} DComplex TableExprNodeTimesDComplex::getDComplex (const TableExprId& id) { return lnode_p->getDComplex(id) * rnode_p->getDComplex(id); } TableExprNodeDivide::TableExprNodeDivide (NodeDataType dt, const TableExprNodeRep& node) : TableExprNodeBinary (dt, node, OtDivide) {} TableExprNodeDivide::~TableExprNodeDivide() {} void TableExprNodeDivide::handleUnits() { if (lnode_p->unit().empty()) { if (! rnode_p->unit().empty()) { Quantity q1 (1); Quantity q2 (1, rnode_p->unit()); setUnit ((q1/q2).getFullUnit()); } } else if (rnode_p->unit().empty()) { // For backward compatibility dividing seconds by 86400 is a // conversion to days. if (rnode_p->isConstant() && (rnode_p->dataType() == NTDouble || rnode_p->dataType() == NTInt) && rnode_p->getDouble(0) == 86400. && lnode_p->unit().getName() == "s") { setUnit ("d"); } else { setUnit (lnode_p->unit()); } } else { Quantity q1 (1, lnode_p->unit()); Quantity q2 (1, rnode_p->unit()); // If same unit kinds, result is no unit. if (q1.isConform (q2)) { makeEqualUnits (lnode_p, rnode_p); } else { setUnit ((q1/q2).getFullUnit()); } } } TableExprNodeDivideDouble::TableExprNodeDivideDouble (const TableExprNodeRep& node) : TableExprNodeDivide (NTDouble, node) {} TableExprNodeDivideDouble::~TableExprNodeDivideDouble() {} Double TableExprNodeDivideDouble::getDouble (const TableExprId& id) { return lnode_p->getDouble(id) / rnode_p->getDouble(id); } DComplex TableExprNodeDivideDouble::getDComplex (const TableExprId& id) { return lnode_p->getDouble(id) / rnode_p->getDouble(id); } TableExprNodeDivideDComplex::TableExprNodeDivideDComplex (const TableExprNodeRep& node) : TableExprNodeDivide (NTComplex, node) {} TableExprNodeDivideDComplex::~TableExprNodeDivideDComplex() {} DComplex TableExprNodeDivideDComplex::getDComplex (const TableExprId& id) { return lnode_p->getDComplex(id) / rnode_p->getDComplex(id); } TableExprNodeModulo::TableExprNodeModulo (NodeDataType dt, const TableExprNodeRep& node) : TableExprNodeBinary (dt, node, OtModulo) {} TableExprNodeModulo::~TableExprNodeModulo() {} void TableExprNodeModulo::handleUnits() { TableExprNodeBinary::handleUnits(); } TableExprNodeModuloInt::TableExprNodeModuloInt (const TableExprNodeRep& node) : TableExprNodeModulo (NTInt, node) {} TableExprNodeModuloInt::~TableExprNodeModuloInt() {} Int64 TableExprNodeModuloInt::getInt (const TableExprId& id) { return floormod (lnode_p->getInt(id), rnode_p->getInt(id)); } Double TableExprNodeModuloInt::getDouble (const TableExprId& id) { return getInt (id); } DComplex TableExprNodeModuloInt::getDComplex (const TableExprId& id) { return getInt (id); } TableExprNodeModuloDouble::TableExprNodeModuloDouble (const TableExprNodeRep& node) : TableExprNodeModulo (NTDouble, node) {} TableExprNodeModuloDouble::~TableExprNodeModuloDouble() {} Double TableExprNodeModuloDouble::getDouble (const TableExprId& id) { return floormod (lnode_p->getDouble(id), rnode_p->getDouble(id)); } DComplex TableExprNodeModuloDouble::getDComplex (const TableExprId& id) { return getDouble (id); } TableExprNodeBitAndInt::TableExprNodeBitAndInt (const TableExprNodeRep& node) : TableExprNodeBinary (NTInt, node, OtBitAnd) {} TableExprNodeBitAndInt::~TableExprNodeBitAndInt() {} Int64 TableExprNodeBitAndInt::getInt (const TableExprId& id) { return lnode_p->getInt(id) & rnode_p->getInt(id); } Double TableExprNodeBitAndInt::getDouble (const TableExprId& id) { return lnode_p->getInt(id) & rnode_p->getInt(id); } DComplex TableExprNodeBitAndInt::getDComplex (const TableExprId& id) { return double(lnode_p->getInt(id) & rnode_p->getInt(id)); } TableExprNodeBitOrInt::TableExprNodeBitOrInt (const TableExprNodeRep& node) : TableExprNodeBinary (NTInt, node, OtBitOr) {} TableExprNodeBitOrInt::~TableExprNodeBitOrInt() {} Int64 TableExprNodeBitOrInt::getInt (const TableExprId& id) { return lnode_p->getInt(id) | rnode_p->getInt(id); } Double TableExprNodeBitOrInt::getDouble (const TableExprId& id) { return lnode_p->getInt(id) | rnode_p->getInt(id); } DComplex TableExprNodeBitOrInt::getDComplex (const TableExprId& id) { return double(lnode_p->getInt(id) | rnode_p->getInt(id)); } TableExprNodeBitXorInt::TableExprNodeBitXorInt (const TableExprNodeRep& node) : TableExprNodeBinary (NTInt, node, OtBitXor) {} TableExprNodeBitXorInt::~TableExprNodeBitXorInt() {} Int64 TableExprNodeBitXorInt::getInt (const TableExprId& id) { return lnode_p->getInt(id) ^ rnode_p->getInt(id); } Double TableExprNodeBitXorInt::getDouble (const TableExprId& id) { return lnode_p->getInt(id) ^ rnode_p->getInt(id); } DComplex TableExprNodeBitXorInt::getDComplex (const TableExprId& id) { return double(lnode_p->getInt(id) ^ rnode_p->getInt(id)); } TableExprNodeMIN::TableExprNodeMIN (const TableExprNodeRep& node) : TableExprNodeBinary (node.dataType(), node, OtMIN) {} TableExprNodeMIN::~TableExprNodeMIN() {} Int64 TableExprNodeMIN::getInt (const TableExprId& id) { return -(lnode_p->getInt(id)); } Double TableExprNodeMIN::getDouble (const TableExprId& id) { return -(lnode_p->getDouble(id)); } DComplex TableExprNodeMIN::getDComplex (const TableExprId& id) { return -(lnode_p->getDComplex(id)); } TableExprNodeBitNegate::TableExprNodeBitNegate (const TableExprNodeRep& node) : TableExprNodeBinary (node.dataType(), node, OtBitNegate) {} TableExprNodeBitNegate::~TableExprNodeBitNegate() {} Int64 TableExprNodeBitNegate::getInt (const TableExprId& id) { return ~(lnode_p->getInt(id)); } Double TableExprNodeBitNegate::getDouble (const TableExprId& id) { return double(~(lnode_p->getInt(id))); } DComplex TableExprNodeBitNegate::getDComplex (const TableExprId& id) { return double(~(lnode_p->getInt(id))); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/ExprMathNode.h000066400000000000000000000533111476623553700201110ustar00rootroot00000000000000//# ExprMathNode.h: Nodes representing scalar mathematical operators in table select expression tree //# Copyright (C) 1994,1995,1996,1997,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_EXPRMATHNODE_H #define TABLES_EXPRMATHNODE_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# This file defines classes derived from TableExprNode representing //# the data type and operator in a table expression. //# //# Data types Bool, Int64, Double, DComplex and String are used. //# Char, uChar, Short, uShort, Int, and uInt are converted to Int64, //# Float to Double, and Complex to DComplex. //# Binary operators +, -, *, /, ==, >=, >, <, <= and != are recognized. //# Also &&, ||, parentheses and unary +, - and ! are recognized. // // Addition in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This abstract class represents an addition in a table expression tree. // class TableExprNodePlus : public TableExprNodeBinary { public: TableExprNodePlus (NodeDataType, const TableExprNodeRep&); ~TableExprNodePlus(); }; // // Int addition in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents an addition in a table select expression tree. // Strings can also be added (ie. concatenated). // Numeric data types will be promoted if possible, so for instance // an addition of Int and Complex is possible. // class TableExprNodePlusInt : public TableExprNodePlus { public: TableExprNodePlusInt (const TableExprNodeRep&); ~TableExprNodePlusInt(); Int64 getInt (const TableExprId& id); Double getDouble (const TableExprId& id); DComplex getDComplex (const TableExprId& id); }; // // Double addition in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents an addition in a table select expression tree. // Strings can also be added (ie. concatenated). // Numeric data types will be promoted if possible, so for instance // an addition of Int and Complex is possible. // class TableExprNodePlusDouble : public TableExprNodePlus { public: TableExprNodePlusDouble (const TableExprNodeRep&); ~TableExprNodePlusDouble(); Double getDouble (const TableExprId& id); DComplex getDComplex (const TableExprId& id); }; // // DComplex addition in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents an addition in a table select expression tree. // Strings can also be added (ie. concatenated). // Numeric data types will be promoted if possible, so for instance // an addition of Int and Complex is possible. // class TableExprNodePlusDComplex : public TableExprNodePlus { public: TableExprNodePlusDComplex (const TableExprNodeRep&); ~TableExprNodePlusDComplex(); DComplex getDComplex (const TableExprId& id); }; // // String addition in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents an addition in a table select expression tree. // Strings can also be added (ie. concatenated). // Numeric data types will be promoted if possible, so for instance // an addition of Int and Complex is possible. // class TableExprNodePlusString : public TableExprNodePlus { public: TableExprNodePlusString (const TableExprNodeRep&); ~TableExprNodePlusString(); String getString (const TableExprId& id); }; // // Date addition in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents an addition in a table select expression tree. // Strings can also be added (ie. concatenated). // Numeric data types will be promoted if possible, so for instance // an addition of Int and Complex is possible. // class TableExprNodePlusDate : public TableExprNodePlus { public: TableExprNodePlusDate (const TableExprNodeRep&); ~TableExprNodePlusDate(); virtual void handleUnits(); Double getDouble (const TableExprId& id); MVTime getDate (const TableExprId& id); }; // // Subtraction in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This abstract class represents a subtraction in a table expression tree. // class TableExprNodeMinus : public TableExprNodeBinary { public: TableExprNodeMinus (NodeDataType, const TableExprNodeRep&); ~TableExprNodeMinus(); }; // // Int subtraction in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents a subtraction in a table select expression tree. // Numeric data types will be promoted if possible, so for instance // a subtraction of Int and Complex is possible. // class TableExprNodeMinusInt : public TableExprNodeMinus { public: TableExprNodeMinusInt (const TableExprNodeRep&); ~TableExprNodeMinusInt(); virtual void handleUnits(); Int64 getInt (const TableExprId& id); Double getDouble (const TableExprId& id); DComplex getDComplex (const TableExprId& id); }; // // Double subtraction in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents a subtraction in a table select expression tree. // Numeric data types will be promoted if possible, so for instance // a subtraction of Int and Complex is possible. // class TableExprNodeMinusDouble : public TableExprNodeMinus { public: TableExprNodeMinusDouble (const TableExprNodeRep&); ~TableExprNodeMinusDouble(); virtual void handleUnits(); Double getDouble (const TableExprId& id); DComplex getDComplex (const TableExprId& id); }; // // DComplex subtraction in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents a subtraction in a table select expression tree. // Numeric data types will be promoted if possible, so for instance // a subtraction of Int and Complex is possible. // class TableExprNodeMinusDComplex : public TableExprNodeMinus { public: TableExprNodeMinusDComplex (const TableExprNodeRep&); ~TableExprNodeMinusDComplex(); DComplex getDComplex (const TableExprId& id); }; // // Date subtraction in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents a subtraction in a table select expression tree. // Numeric data types will be promoted if possible, so for instance // a subtraction of Int and Complex is possible. // class TableExprNodeMinusDate : public TableExprNodeMinus { public: TableExprNodeMinusDate (const TableExprNodeRep&); ~TableExprNodeMinusDate(); virtual void handleUnits(); MVTime getDate (const TableExprId& id); Double getDouble (const TableExprId& id); }; // // Multiplication in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This abstract class represents a multiplication in a table expression tree. // class TableExprNodeTimes : public TableExprNodeBinary { public: TableExprNodeTimes (NodeDataType, const TableExprNodeRep&); ~TableExprNodeTimes(); virtual void handleUnits(); }; // // Int multiplication in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents a multiplication in a table select expression tree. // Numeric data types will be promoted if possible, so for instance // a multiplication of Int and Complex is possible. // class TableExprNodeTimesInt : public TableExprNodeTimes { public: TableExprNodeTimesInt (const TableExprNodeRep&); ~TableExprNodeTimesInt(); Int64 getInt (const TableExprId& id); Double getDouble (const TableExprId& id); DComplex getDComplex (const TableExprId& id); }; // // Double multiplication in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents a multiplication in a table select expression tree. // Numeric data types will be promoted if possible, so for instance // a multiplication of Int and Complex is possible. // class TableExprNodeTimesDouble : public TableExprNodeTimes { public: TableExprNodeTimesDouble (const TableExprNodeRep&); ~TableExprNodeTimesDouble(); Double getDouble (const TableExprId& id); DComplex getDComplex (const TableExprId& id); }; // // DComplex multiplication in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents a multiplication in a table select expression tree. // Numeric data types will be promoted if possible, so for instance // a multiplication of Int and Complex is possible. // class TableExprNodeTimesDComplex : public TableExprNodeTimes { public: TableExprNodeTimesDComplex (const TableExprNodeRep&); ~TableExprNodeTimesDComplex(); DComplex getDComplex (const TableExprId& id); }; // // Division in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This abstract class represents a division in a table expression tree. // class TableExprNodeDivide : public TableExprNodeBinary { public: TableExprNodeDivide (NodeDataType, const TableExprNodeRep&); ~TableExprNodeDivide(); virtual void handleUnits(); }; // // Double division in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents a division in a table select expression tree. // Numeric data types will be promoted if possible, so for instance // a division of Int and Complex is possible. // class TableExprNodeDivideDouble : public TableExprNodeDivide { public: TableExprNodeDivideDouble (const TableExprNodeRep&); ~TableExprNodeDivideDouble(); Double getDouble (const TableExprId& id); DComplex getDComplex (const TableExprId& id); }; // // DComplex division in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents a division in a table select expression tree. // Numeric data types will be promoted if possible, so for instance // a division of Int and Complex is possible. // class TableExprNodeDivideDComplex : public TableExprNodeDivide { public: TableExprNodeDivideDComplex (const TableExprNodeRep&); ~TableExprNodeDivideDComplex(); DComplex getDComplex (const TableExprId& id); }; // // Modulo in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This abstract class represents a modulo in a table expression tree. // class TableExprNodeModulo : public TableExprNodeBinary { public: TableExprNodeModulo (NodeDataType, const TableExprNodeRep&); ~TableExprNodeModulo(); virtual void handleUnits(); }; // // Int modulo in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents a modulo operation in a table select expression tree. // It is only possible for datatype Int. // class TableExprNodeModuloInt : public TableExprNodeModulo { public: TableExprNodeModuloInt (const TableExprNodeRep&); ~TableExprNodeModuloInt(); Int64 getInt (const TableExprId& id); Double getDouble (const TableExprId& id); DComplex getDComplex (const TableExprId& id); }; // // Double modulo in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents a modulo operation in a table select expression tree. // It is only possible for datatype Double. // class TableExprNodeModuloDouble : public TableExprNodeModulo { public: TableExprNodeModuloDouble (const TableExprNodeRep&); ~TableExprNodeModuloDouble(); Double getDouble (const TableExprId& id); DComplex getDComplex (const TableExprId& id); }; // // Bitwise and in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents a bitwise and operation in a table select expression // tree. It is only possible for datatype Int. // class TableExprNodeBitAndInt : public TableExprNodeBinary { public: TableExprNodeBitAndInt (const TableExprNodeRep&); ~TableExprNodeBitAndInt(); Int64 getInt (const TableExprId& id); Double getDouble (const TableExprId& id); DComplex getDComplex (const TableExprId& id); }; // // Bitwise or in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents a bitwise or operation in a table select expression // tree. It is only possible for datatype Int. // class TableExprNodeBitOrInt : public TableExprNodeBinary { public: TableExprNodeBitOrInt (const TableExprNodeRep&); ~TableExprNodeBitOrInt(); Int64 getInt (const TableExprId& id); Double getDouble (const TableExprId& id); DComplex getDComplex (const TableExprId& id); }; // // Bitwise xor in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents a bitwise xor operation in a table select expression // tree. It is only possible for datatype Int. // class TableExprNodeBitXorInt : public TableExprNodeBinary { public: TableExprNodeBitXorInt (const TableExprNodeRep&); ~TableExprNodeBitXorInt(); Int64 getInt (const TableExprId& id); Double getDouble (const TableExprId& id); DComplex getDComplex (const TableExprId& id); }; // // Unary minus in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents a unary minus in a table select expression tree. // This is defined for numeric data types only. // class TableExprNodeMIN : public TableExprNodeBinary { public: TableExprNodeMIN (const TableExprNodeRep&); ~TableExprNodeMIN(); Int64 getInt (const TableExprId& id); Double getDouble (const TableExprId& id); DComplex getDComplex (const TableExprId& id); }; // // Bitwise negate in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents a bitwise negate in a table select expression tree. // This is defined for integer data types only. // class TableExprNodeBitNegate : public TableExprNodeBinary { public: TableExprNodeBitNegate (const TableExprNodeRep&); ~TableExprNodeBitNegate(); Int64 getInt (const TableExprId& id); Double getDouble (const TableExprId& id); DComplex getDComplex (const TableExprId& id); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/ExprMathNodeArray.cc000066400000000000000000000444701476623553700212540ustar00rootroot00000000000000//# ExprMathNodeArray.cc: Nodes representing mathematical array operators in table select expression tree //# Copyright (C) 1997,1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Implement the arithmetic operators for each data type. TableExprNodeArrayPlus::TableExprNodeArrayPlus (NodeDataType dt, const TableExprNodeRep& node) : TableExprNodeArray (node, dt, OtPlus) {} TableExprNodeArrayPlus::~TableExprNodeArrayPlus() {} TableExprNodeArrayPlusInt::TableExprNodeArrayPlusInt (const TableExprNodeRep& node) : TableExprNodeArrayPlus (NTInt, node) {} TableExprNodeArrayPlusInt::~TableExprNodeArrayPlusInt() {} MArray TableExprNodeArrayPlusInt::getArrayInt (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayInt (id) + rnode_p->getInt (id); case ScaArr: return lnode_p->getInt (id) + rnode_p->getArrayInt (id); default: break; } return lnode_p->getArrayInt (id) + rnode_p->getArrayInt (id); } TableExprNodeArrayPlusDouble::TableExprNodeArrayPlusDouble (const TableExprNodeRep& node) : TableExprNodeArrayPlus (NTDouble, node) {} TableExprNodeArrayPlusDouble::~TableExprNodeArrayPlusDouble() {} MArray TableExprNodeArrayPlusDouble::getArrayDouble (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayDouble (id) + rnode_p->getDouble (id); case ScaArr: return lnode_p->getDouble (id) + rnode_p->getArrayDouble (id); default: break; } return lnode_p->getArrayDouble (id) + rnode_p->getArrayDouble (id); } TableExprNodeArrayPlusDComplex::TableExprNodeArrayPlusDComplex (const TableExprNodeRep& node) : TableExprNodeArrayPlus (NTComplex, node) {} TableExprNodeArrayPlusDComplex::~TableExprNodeArrayPlusDComplex() {} MArray TableExprNodeArrayPlusDComplex::getArrayDComplex (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayDComplex (id) + rnode_p->getDComplex (id); case ScaArr: return lnode_p->getDComplex (id) + rnode_p->getArrayDComplex (id); default: break; } return lnode_p->getArrayDComplex (id) + rnode_p->getArrayDComplex (id); } TableExprNodeArrayPlusString::TableExprNodeArrayPlusString (const TableExprNodeRep& node) : TableExprNodeArrayPlus (NTString, node) {} TableExprNodeArrayPlusString::~TableExprNodeArrayPlusString() {} MArray TableExprNodeArrayPlusString::getArrayString (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayString (id) + rnode_p->getString (id); case ScaArr: return lnode_p->getString (id) + rnode_p->getArrayString (id); default: break; } return lnode_p->getArrayString (id) + rnode_p->getArrayString (id); } TableExprNodeArrayPlusDate::TableExprNodeArrayPlusDate (const TableExprNodeRep& node) : TableExprNodeArrayPlus (NTDate, node) {} TableExprNodeArrayPlusDate::~TableExprNodeArrayPlusDate() {} void TableExprNodeArrayPlusDate::handleUnits() { if (lnode_p->dataType() == NTDouble) { TableExprNodeUnit::adaptUnit (lnode_p, "d"); } else if (rnode_p->dataType() == NTDouble) { TableExprNodeUnit::adaptUnit (rnode_p, "d"); } } MArray TableExprNodeArrayPlusDate::getArrayDouble (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayDouble(id) + rnode_p->getDouble(id); case ScaArr: return lnode_p->getDouble(id) + rnode_p->getArrayDouble(id); default: break; } return lnode_p->getArrayDouble(id) + rnode_p->getArrayDouble(id); } MArray TableExprNodeArrayPlusDate::getArrayDate (const TableExprId& id) { MArray tmp(getArrayDouble(id)); Array res(tmp.shape()); convertArray (res, tmp.array()); return MArray (res, tmp.mask()); } TableExprNodeArrayMinus::TableExprNodeArrayMinus (NodeDataType dt, const TableExprNodeRep& node) : TableExprNodeArray (node, dt, OtMinus) {} TableExprNodeArrayMinus::~TableExprNodeArrayMinus() {} TableExprNodeArrayMinusInt::TableExprNodeArrayMinusInt (const TableExprNodeRep& node) : TableExprNodeArrayMinus (NTInt, node) {} TableExprNodeArrayMinusInt::~TableExprNodeArrayMinusInt() {} MArray TableExprNodeArrayMinusInt::getArrayInt (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayInt (id) - rnode_p->getInt (id); case ScaArr: return lnode_p->getInt (id) - rnode_p->getArrayInt (id); default: break; } return lnode_p->getArrayInt (id) - rnode_p->getArrayInt (id); } TableExprNodeArrayMinusDouble::TableExprNodeArrayMinusDouble (const TableExprNodeRep& node) : TableExprNodeArrayMinus (NTDouble, node) {} TableExprNodeArrayMinusDouble::~TableExprNodeArrayMinusDouble() {} MArray TableExprNodeArrayMinusDouble::getArrayDouble (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayDouble (id) - rnode_p->getDouble (id); case ScaArr: return lnode_p->getDouble (id) - rnode_p->getArrayDouble (id); default: break; } return lnode_p->getArrayDouble (id) - rnode_p->getArrayDouble (id); } TableExprNodeArrayMinusDComplex::TableExprNodeArrayMinusDComplex (const TableExprNodeRep& node) : TableExprNodeArrayMinus (NTComplex, node) {} TableExprNodeArrayMinusDComplex::~TableExprNodeArrayMinusDComplex() {} MArray TableExprNodeArrayMinusDComplex::getArrayDComplex (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayDComplex (id) - rnode_p->getDComplex (id); case ScaArr: return lnode_p->getDComplex (id) - rnode_p->getArrayDComplex (id); default: break; } return lnode_p->getArrayDComplex (id) - rnode_p->getArrayDComplex (id); } TableExprNodeArrayMinusDate::TableExprNodeArrayMinusDate (const TableExprNodeRep& node) : TableExprNodeArrayMinus (NTDate, node) {} TableExprNodeArrayMinusDate::~TableExprNodeArrayMinusDate() {} void TableExprNodeArrayMinusDate::handleUnits() { // Right hand side must be in days. TableExprNodeUnit::adaptUnit (rnode_p, "d"); } MArray TableExprNodeArrayMinusDate::getArrayDouble (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayDouble(id) - rnode_p->getDouble(id); case ScaArr: return lnode_p->getDouble(id) - rnode_p->getArrayDouble(id); default: break; } return lnode_p->getArrayDouble(id) - rnode_p->getArrayDouble(id); } MArray TableExprNodeArrayMinusDate::getArrayDate (const TableExprId& id) { MArray tmp(getArrayDouble(id)); Array res(tmp.shape()); convertArray (res, tmp.array()); return MArray (res, tmp.mask()); } TableExprNodeArrayTimes::TableExprNodeArrayTimes (NodeDataType dt, const TableExprNodeRep& node) : TableExprNodeArray (node, dt, OtTimes) {} TableExprNodeArrayTimes::~TableExprNodeArrayTimes() {} void TableExprNodeArrayTimes::handleUnits() { if (lnode_p->unit().empty()) { setUnit (rnode_p->unit()); } else if (rnode_p->unit().empty()) { setUnit (lnode_p->unit()); } else { Quantity q1 (1, lnode_p->unit()); Quantity q2 (1, rnode_p->unit()); setUnit ((q1*q2).getFullUnit()); } } TableExprNodeArrayTimesInt::TableExprNodeArrayTimesInt (const TableExprNodeRep& node) : TableExprNodeArrayTimes (NTInt, node) {} TableExprNodeArrayTimesInt::~TableExprNodeArrayTimesInt() {} MArray TableExprNodeArrayTimesInt::getArrayInt (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayInt (id) * rnode_p->getInt (id); case ScaArr: return lnode_p->getInt (id) * rnode_p->getArrayInt (id); default: break; } return lnode_p->getArrayInt (id) * rnode_p->getArrayInt (id); } TableExprNodeArrayTimesDouble::TableExprNodeArrayTimesDouble (const TableExprNodeRep& node) : TableExprNodeArrayTimes (NTDouble, node) {} TableExprNodeArrayTimesDouble::~TableExprNodeArrayTimesDouble() {} MArray TableExprNodeArrayTimesDouble::getArrayDouble (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayDouble (id) * rnode_p->getDouble (id); case ScaArr: return lnode_p->getDouble (id) * rnode_p->getArrayDouble (id); default: break; } return lnode_p->getArrayDouble (id) * rnode_p->getArrayDouble (id); } TableExprNodeArrayTimesDComplex::TableExprNodeArrayTimesDComplex (const TableExprNodeRep& node) : TableExprNodeArrayTimes (NTComplex, node) {} TableExprNodeArrayTimesDComplex::~TableExprNodeArrayTimesDComplex() {} MArray TableExprNodeArrayTimesDComplex::getArrayDComplex (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayDComplex (id) * rnode_p->getDComplex (id); case ScaArr: return lnode_p->getDComplex (id) * rnode_p->getArrayDComplex (id); default: break; } return lnode_p->getArrayDComplex (id) * rnode_p->getArrayDComplex (id); } TableExprNodeArrayDivide::TableExprNodeArrayDivide (NodeDataType dt, const TableExprNodeRep& node) : TableExprNodeArray (node, dt, OtDivide) {} TableExprNodeArrayDivide::~TableExprNodeArrayDivide() {} void TableExprNodeArrayDivide::handleUnits() { if (lnode_p->unit().empty()) { setUnit (rnode_p->unit()); } else if (rnode_p->unit().empty()) { setUnit (lnode_p->unit()); } else { Quantity q1 (1, lnode_p->unit()); Quantity q2 (1, rnode_p->unit()); // If same unit kinds, result is no unit. if (q1.isConform (q2)) { makeEqualUnits (lnode_p, rnode_p); } else { setUnit ((q1/q2).getFullUnit()); } } } TableExprNodeArrayDivideDouble::TableExprNodeArrayDivideDouble (const TableExprNodeRep& node) : TableExprNodeArrayDivide (NTDouble, node) {} TableExprNodeArrayDivideDouble::~TableExprNodeArrayDivideDouble() {} MArray TableExprNodeArrayDivideDouble::getArrayDouble (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayDouble (id) / rnode_p->getDouble (id); case ScaArr: return lnode_p->getDouble (id) / rnode_p->getArrayDouble (id); default: break; } return lnode_p->getArrayDouble (id) / rnode_p->getArrayDouble (id); } TableExprNodeArrayDivideDComplex::TableExprNodeArrayDivideDComplex (const TableExprNodeRep& node) : TableExprNodeArrayDivide (NTComplex, node) {} TableExprNodeArrayDivideDComplex::~TableExprNodeArrayDivideDComplex() {} MArray TableExprNodeArrayDivideDComplex::getArrayDComplex (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayDComplex (id) / rnode_p->getDComplex (id); case ScaArr: return lnode_p->getDComplex (id) / rnode_p->getArrayDComplex (id); default: break; } return lnode_p->getArrayDComplex (id) / rnode_p->getArrayDComplex (id); } TableExprNodeArrayModulo::TableExprNodeArrayModulo (NodeDataType dt, const TableExprNodeRep& node) : TableExprNodeArray (node, dt, OtModulo) {} TableExprNodeArrayModulo::~TableExprNodeArrayModulo() {} void TableExprNodeArrayModulo::handleUnits() { TableExprNodeBinary::handleUnits(); } TableExprNodeArrayModuloInt::TableExprNodeArrayModuloInt (const TableExprNodeRep& node) : TableExprNodeArrayModulo (NTInt, node) {} TableExprNodeArrayModuloInt::~TableExprNodeArrayModuloInt() {} MArray TableExprNodeArrayModuloInt::getArrayInt (const TableExprId& id) { switch (argtype_p) { case ArrSca: return floormod (lnode_p->getArrayInt(id), rnode_p->getInt(id)); case ScaArr: return floormod (lnode_p->getInt(id), rnode_p->getArrayInt(id)); default: break; } return floormod (lnode_p->getArrayInt(id), rnode_p->getArrayInt(id)); } TableExprNodeArrayModuloDouble::TableExprNodeArrayModuloDouble (const TableExprNodeRep& node) : TableExprNodeArrayModulo (NTDouble, node) {} TableExprNodeArrayModuloDouble::~TableExprNodeArrayModuloDouble() {} MArray TableExprNodeArrayModuloDouble::getArrayDouble (const TableExprId& id) { switch (argtype_p) { case ArrSca: return floormod (lnode_p->getArrayDouble(id), rnode_p->getDouble(id)); case ScaArr: return floormod (lnode_p->getDouble(id), rnode_p->getArrayDouble(id)); default: break; } return floormod (lnode_p->getArrayDouble(id), rnode_p->getArrayDouble(id)); } TableExprNodeArrayBitAndInt::TableExprNodeArrayBitAndInt (const TableExprNodeRep& node) : TableExprNodeArray (node, NTInt, OtBitAnd) {} TableExprNodeArrayBitAndInt::~TableExprNodeArrayBitAndInt() {} MArray TableExprNodeArrayBitAndInt::getArrayInt (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayInt(id) & rnode_p->getInt(id); case ScaArr: return lnode_p->getInt(id) & rnode_p->getArrayInt(id); default: break; } return lnode_p->getArrayInt(id) & rnode_p->getArrayInt(id); } TableExprNodeArrayBitOrInt::TableExprNodeArrayBitOrInt (const TableExprNodeRep& node) : TableExprNodeArray (node, NTInt, OtBitOr) {} TableExprNodeArrayBitOrInt::~TableExprNodeArrayBitOrInt() {} MArray TableExprNodeArrayBitOrInt::getArrayInt (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayInt(id) | rnode_p->getInt(id); case ScaArr: return lnode_p->getInt(id) | rnode_p->getArrayInt(id); default: break; } return lnode_p->getArrayInt(id) | rnode_p->getArrayInt(id); } TableExprNodeArrayBitXorInt::TableExprNodeArrayBitXorInt (const TableExprNodeRep& node) : TableExprNodeArray (node, NTInt, OtBitXor) {} TableExprNodeArrayBitXorInt::~TableExprNodeArrayBitXorInt() {} MArray TableExprNodeArrayBitXorInt::getArrayInt (const TableExprId& id) { switch (argtype_p) { case ArrSca: return lnode_p->getArrayInt(id) ^ rnode_p->getInt(id); case ScaArr: return lnode_p->getInt(id) ^ rnode_p->getArrayInt(id); default: break; } return lnode_p->getArrayInt(id) ^ rnode_p->getArrayInt(id); } TableExprNodeArrayMIN::TableExprNodeArrayMIN (const TableExprNodeRep& node) : TableExprNodeArray (node, node.dataType(), OtMIN) {} TableExprNodeArrayMIN::~TableExprNodeArrayMIN() {} MArray TableExprNodeArrayMIN::getArrayInt (const TableExprId& id) { return -(lnode_p->getArrayInt(id)); } MArray TableExprNodeArrayMIN::getArrayDouble (const TableExprId& id) { return -(lnode_p->getArrayDouble(id)); } MArray TableExprNodeArrayMIN::getArrayDComplex (const TableExprId& id) { return -(lnode_p->getArrayDComplex(id)); } TableExprNodeArrayBitNegate::TableExprNodeArrayBitNegate (const TableExprNodeRep& node) : TableExprNodeArray (node, node.dataType(), OtBitNegate) {} TableExprNodeArrayBitNegate::~TableExprNodeArrayBitNegate() {} MArray TableExprNodeArrayBitNegate::getArrayInt (const TableExprId& id) { return ~(lnode_p->getArrayInt(id)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/ExprMathNodeArray.h000066400000000000000000000527261476623553700211210ustar00rootroot00000000000000//# ExprMathArrayNode.h: Nodes representing mathematical array operators in table select expression tree //# Copyright (C) 1997,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_EXPRMATHNODEARRAY_H #define TABLES_EXPRMATHNODEARRAY_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations //# This file defines classes derived from TableExprNode representing //# the data type and operator in a table expression. //# //# Data types Bool, Int64, Double, DComplex and String are used. //# Char, uChar, Short, uShort, Int, and uInt are converted to Int64, //# Float to Double, and Complex to DComplex. //# Binary operators +, -, *, /, and % are recognized. //# Also unary + and - are recognized. // // Array addition in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This abstract class represents an addition in a table expression tree. // class TableExprNodeArrayPlus : public TableExprNodeArray { public: TableExprNodeArrayPlus (NodeDataType, const TableExprNodeRep&); ~TableExprNodeArrayPlus(); }; // // Int Array addition in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents an addition in a table select expression tree. // Strings can also be added (ie. concatenated). // Numeric data types will be promoted if possible, so for instance // an addition of Int and Complex is possible. // class TableExprNodeArrayPlusInt : public TableExprNodeArrayPlus { public: TableExprNodeArrayPlusInt (const TableExprNodeRep&); ~TableExprNodeArrayPlusInt(); MArray getArrayInt (const TableExprId& id); }; // // Double Array addition in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents an addition in a table select expression tree. // Strings can also be added (ie. concatenated). // Numeric data types will be promoted if possible, so for instance // an addition of Int and Complex is possible. // class TableExprNodeArrayPlusDouble : public TableExprNodeArrayPlus { public: TableExprNodeArrayPlusDouble (const TableExprNodeRep&); ~TableExprNodeArrayPlusDouble(); MArray getArrayDouble (const TableExprId& id); }; // // DComplex Array addition in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents an addition in a table select expression tree. // Strings can also be added (ie. concatenated). // Numeric data types will be promoted if possible, so for instance // an addition of Int and Complex is possible. // class TableExprNodeArrayPlusDComplex : public TableExprNodeArrayPlus { public: TableExprNodeArrayPlusDComplex (const TableExprNodeRep&); ~TableExprNodeArrayPlusDComplex(); MArray getArrayDComplex (const TableExprId& id); }; // // String Array addition in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents an addition in a table select expression tree. // Strings can also be added (ie. concatenated). // Numeric data types will be promoted if possible, so for instance // an addition of Int and Complex is possible. // class TableExprNodeArrayPlusString : public TableExprNodeArrayPlus { public: TableExprNodeArrayPlusString (const TableExprNodeRep&); ~TableExprNodeArrayPlusString(); MArray getArrayString (const TableExprId& id); }; // // Date Array addition in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents an addition in a table select expression tree. // Strings can also be added (ie. concatenated). // Numeric data types will be promoted if possible, so for instance // an addition of Int and Complex is possible. // class TableExprNodeArrayPlusDate : public TableExprNodeArrayPlus { public: TableExprNodeArrayPlusDate (const TableExprNodeRep&); ~TableExprNodeArrayPlusDate(); virtual void handleUnits(); MArray getArrayDouble (const TableExprId& id); MArray getArrayDate (const TableExprId& id); }; // // Array addition in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This abstract class represents an addition in a table expression tree. // class TableExprNodeArrayMinus : public TableExprNodeArray { public: TableExprNodeArrayMinus (NodeDataType, const TableExprNodeRep&); ~TableExprNodeArrayMinus(); }; // // Int Array subtraction in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents a subtraction in a table select expression tree. // Numeric data types will be promoted if possible, so for instance // a subtraction of Int and Complex is possible. // class TableExprNodeArrayMinusInt : public TableExprNodeArrayMinus { public: TableExprNodeArrayMinusInt (const TableExprNodeRep&); ~TableExprNodeArrayMinusInt(); MArray getArrayInt (const TableExprId& id); }; // // Double Array subtraction in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents a subtraction in a table select expression tree. // Numeric data types will be promoted if possible, so for instance // a subtraction of Int and Complex is possible. // class TableExprNodeArrayMinusDouble : public TableExprNodeArrayMinus { public: TableExprNodeArrayMinusDouble (const TableExprNodeRep&); ~TableExprNodeArrayMinusDouble(); MArray getArrayDouble (const TableExprId& id); }; // // DComplex Array subtraction in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents a subtraction in a table select expression tree. // Numeric data types will be promoted if possible, so for instance // a subtraction of Int and Complex is possible. // class TableExprNodeArrayMinusDComplex : public TableExprNodeArrayMinus { public: TableExprNodeArrayMinusDComplex (const TableExprNodeRep&); ~TableExprNodeArrayMinusDComplex(); MArray getArrayDComplex (const TableExprId& id); }; // // Date Array subtraction in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents a subtraction in a table select expression tree. // Numeric data types will be promoted if possible, so for instance // a subtraction of Int and Complex is possible. // class TableExprNodeArrayMinusDate : public TableExprNodeArrayMinus { public: TableExprNodeArrayMinusDate (const TableExprNodeRep&); ~TableExprNodeArrayMinusDate(); virtual void handleUnits(); MArray getArrayDouble (const TableExprId& id); MArray getArrayDate (const TableExprId& id); }; // // Array addition in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This abstract class represents an addition in a table expression tree. // class TableExprNodeArrayTimes : public TableExprNodeArray { public: TableExprNodeArrayTimes (NodeDataType, const TableExprNodeRep&); ~TableExprNodeArrayTimes(); virtual void handleUnits(); }; // // Int Array multiplication in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents a multiplication in a table select expression tree. // Numeric data types will be promoted if possible, so for instance // a multiplication of Int and Complex is possible. // class TableExprNodeArrayTimesInt : public TableExprNodeArrayTimes { public: TableExprNodeArrayTimesInt (const TableExprNodeRep&); ~TableExprNodeArrayTimesInt(); MArray getArrayInt (const TableExprId& id); }; // // Double Array multiplication in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents a multiplication in a table select expression tree. // Numeric data types will be promoted if possible, so for instance // a multiplication of Int and Complex is possible. // class TableExprNodeArrayTimesDouble : public TableExprNodeArrayTimes { public: TableExprNodeArrayTimesDouble (const TableExprNodeRep&); ~TableExprNodeArrayTimesDouble(); MArray getArrayDouble (const TableExprId& id); }; // // DComplex Array multiplication in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents a multiplication in a table select expression tree. // Numeric data types will be promoted if possible, so for instance // a multiplication of Int and Complex is possible. // class TableExprNodeArrayTimesDComplex : public TableExprNodeArrayTimes { public: TableExprNodeArrayTimesDComplex (const TableExprNodeRep&); ~TableExprNodeArrayTimesDComplex(); MArray getArrayDComplex (const TableExprId& id); }; // // Array addition in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This abstract class represents an addition in a table expression tree. // class TableExprNodeArrayDivide : public TableExprNodeArray { public: TableExprNodeArrayDivide (NodeDataType, const TableExprNodeRep&); ~TableExprNodeArrayDivide(); virtual void handleUnits(); }; // // Double Array division in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents a division in a table select expression tree. // Numeric data types will be promoted if possible, so for instance // a division of Int and Complex is possible. // class TableExprNodeArrayDivideDouble : public TableExprNodeArrayDivide { public: TableExprNodeArrayDivideDouble (const TableExprNodeRep&); ~TableExprNodeArrayDivideDouble(); MArray getArrayDouble (const TableExprId& id); }; // // DComplex Array division in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents a division in a table select expression tree. // Numeric data types will be promoted if possible, so for instance // a division of Int and Complex is possible. // class TableExprNodeArrayDivideDComplex : public TableExprNodeArrayDivide { public: TableExprNodeArrayDivideDComplex (const TableExprNodeRep&); ~TableExprNodeArrayDivideDComplex(); MArray getArrayDComplex (const TableExprId& id); }; // // Array addition in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This abstract class represents an addition in a table expression tree. // class TableExprNodeArrayModulo : public TableExprNodeArray { public: TableExprNodeArrayModulo (NodeDataType, const TableExprNodeRep&); ~TableExprNodeArrayModulo(); virtual void handleUnits(); }; // // Int Array modulo in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents a modulo operation in a table select expression tree. // It is only possible for datatype Int. // class TableExprNodeArrayModuloInt : public TableExprNodeArrayModulo { public: TableExprNodeArrayModuloInt (const TableExprNodeRep&); ~TableExprNodeArrayModuloInt(); MArray getArrayInt (const TableExprId& id); }; // // Double Array modulo in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents a modulo operation in a table select expression tree. // It is only possible for datatype Double. // class TableExprNodeArrayModuloDouble : public TableExprNodeArrayModulo { public: TableExprNodeArrayModuloDouble (const TableExprNodeRep&); ~TableExprNodeArrayModuloDouble(); MArray getArrayDouble (const TableExprId& id); }; // // Int Array bitwise and in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents a bitwise and operation in a table select expression // tree. It is only possible for datatype Int. // class TableExprNodeArrayBitAndInt : public TableExprNodeArray { public: TableExprNodeArrayBitAndInt (const TableExprNodeRep&); ~TableExprNodeArrayBitAndInt(); MArray getArrayInt (const TableExprId& id); }; // // Int Array bitwise or in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents a bitwise or operation in a table select expression // tree. It is only possible for datatype Int. // class TableExprNodeArrayBitOrInt : public TableExprNodeArray { public: TableExprNodeArrayBitOrInt (const TableExprNodeRep&); ~TableExprNodeArrayBitOrInt(); MArray getArrayInt (const TableExprId& id); }; // // Int Array bitwise xor in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep // // // This class represents a bitwise xor operation in a table select expression // tree. It is only possible for datatype Int. // class TableExprNodeArrayBitXorInt : public TableExprNodeArray { public: TableExprNodeArrayBitXorInt (const TableExprNodeRep&); ~TableExprNodeArrayBitXorInt(); MArray getArrayInt (const TableExprId& id); }; // // Unary minus in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents a unary minus in a table select expression tree. // This is defined for numeric data types only. // class TableExprNodeArrayMIN : public TableExprNodeArray { public: TableExprNodeArrayMIN (const TableExprNodeRep&); ~TableExprNodeArrayMIN(); MArray getArrayInt (const TableExprId& id); MArray getArrayDouble (const TableExprId& id); MArray getArrayDComplex (const TableExprId& id); }; // // Bitwise negate in table select expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents a bitwise negate in a table select expression tree. // This is defined for Int data types only. // class TableExprNodeArrayBitNegate : public TableExprNodeArray { public: TableExprNodeArrayBitNegate (const TableExprNodeRep&); ~TableExprNodeArrayBitNegate(); MArray getArrayInt (const TableExprId& id); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/ExprNode.cc000066400000000000000000001543631476623553700174460ustar00rootroot00000000000000//# ExprNode.cc: Handle class for a table column expression tree //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableExprNode::TableExprNode() : node_p(0) {} //# Constructors for the various constants. //# These objects are created as temporaries by the compiler. TableExprNode::TableExprNode (const Bool& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const Int& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const uInt& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const Int64& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const uInt64& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const Float& val) { node_p = std::make_shared(Double(val)); } TableExprNode::TableExprNode (const Double& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const Complex& val) { node_p = std::make_shared(DComplex(val)); } TableExprNode::TableExprNode (const DComplex& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const String& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const std::string& val) { node_p = std::make_shared(String(val)); } TableExprNode::TableExprNode (const char* val) { node_p = std::make_shared(String(val)); } TableExprNode::TableExprNode (const Regex& val) { node_p = std::make_shared(TaqlRegex(val)); } TableExprNode::TableExprNode (const StringDistance& val) { node_p = std::make_shared(TaqlRegex(val)); } TableExprNode::TableExprNode (const TaqlRegex& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const MVTime& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const Array& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const Array& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const Array& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const Array& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const Array& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const Array& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const Array& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const Array& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const Array& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const Array& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const Array& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const Array& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const Array& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const Array& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const MArray& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const MArray& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const MArray& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const MArray& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const MArray& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const MArray& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const MArray& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const MArray& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const MArray& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const MArray& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const MArray& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const MArray& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const MArray& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const MArray& val) { node_p = std::make_shared(val); } TableExprNode::TableExprNode (const TENShPtr& node) : node_p (node) {} TableExprNode::TableExprNode (const TableExprNode& node) : node_p (node.node_p) {} TableExprNode& TableExprNode::operator= (const TableExprNode& that) { node_p = that.node_p; return *this; } //# Destructor. TableExprNode::~TableExprNode () {} TableExprNode operator&& (const TableExprNode& left, const TableExprNode& right) { if (left.isNull()) return right; if (right.isNull()) return left; return left.newAND (right.getRep()); } TableExprNode operator|| (const TableExprNode& left, const TableExprNode& right) { if (left.isNull()) return right; if (right.isNull()) return left; return left.newOR (right.getRep()); } TableExprNode TableExprNode::in (const TableExprNodeSet& set, const TaQLStyle& style) const { // An empty set never matches. // Note it makes it possible to use an empty set that has // no data type yet. if (set.size() == 0) { return TableExprNode(False); } set.checkEqualDataTypes(); TableExprNodeSet setcp = set; return newIN (setcp.setOrArray(), style); } TableExprNode TableExprNode::useUnit (const Unit& unit) const { if (node_p->dataType() != TableExprNodeRep::NTInt && node_p->dataType() != TableExprNodeRep::NTDouble && node_p->dataType() != TableExprNodeRep::NTComplex && node_p->dataType() != TableExprNodeRep::NTDate) { throwInvDT("units can only be used with numeric values"); } return TableExprNodeUnit::useUnit (node_p, unit); } DataType TableExprNode::getColumnDataType() const { DataType dt; if (node_p->getColumnDataType (dt)) { return dt; } return dataType(); } Bool TableExprNode::checkTableSize (const Table& table, Bool canBeConst) const { // Always correct if no original table. if (table.isNull()) { return True; } std::vector
      • tables(TableExprNodeUtil::getNodeTables (node_p.get(), True)); if (tables.empty()) { return canBeConst; } return (table.nrow() == TableExprNodeUtil::getCheckNRow (tables)); } void TableExprNode::throwInvDT (const String& message) { throw (TableInvExpr ("invalid operand data type; " + message)); } TENShPtr TableExprNode::setBinaryNodeInfo (TableExprNodeBinary* tsnptr, const TENShPtr& right) const { TENShPtr shPtr(tsnptr); tsnptr->setChildren (node_p, right); return TableExprNodeRep::replaceConstNode (shPtr); } TENShPtr TableExprNode::newPlus (const TENShPtr& right) const { TableExprNodeRep node = TableExprNodeBinary::getCommonTypes (node_p, right, TableExprNodeRep::OtPlus); TableExprNodeBinary* tsnptr=0; if (node.valueType() == TableExprNodeRep::VTScalar) { switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr = new TableExprNodePlusInt (node); break; case TableExprNodeRep::NTDouble: tsnptr = new TableExprNodePlusDouble (node); break; case TableExprNodeRep::NTComplex: tsnptr = new TableExprNodePlusDComplex (node); break; case TableExprNodeRep::NTString: tsnptr = new TableExprNodePlusString (node); break; case TableExprNodeRep::NTDate: tsnptr = new TableExprNodePlusDate (node); break; default: throwInvDT("in scalar operator+"); } }else{ switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr = new TableExprNodeArrayPlusInt (node); break; case TableExprNodeRep::NTDouble: tsnptr = new TableExprNodeArrayPlusDouble (node); break; case TableExprNodeRep::NTComplex: tsnptr = new TableExprNodeArrayPlusDComplex (node); break; case TableExprNodeRep::NTString: tsnptr = new TableExprNodeArrayPlusString (node); break; case TableExprNodeRep::NTDate: tsnptr = new TableExprNodeArrayPlusDate (node); break; default: throwInvDT("in array operator+"); } } return setBinaryNodeInfo (tsnptr, right); } TENShPtr TableExprNode::newMinus (const TENShPtr& right) const { TableExprNodeRep node = TableExprNodeBinary::getCommonTypes (node_p, right, TableExprNodeRep::OtMinus); TableExprNodeBinary* tsnptr=0; if (node.valueType() == TableExprNodeRep::VTScalar) { switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr = new TableExprNodeMinusInt (node); break; case TableExprNodeRep::NTDouble: tsnptr = new TableExprNodeMinusDouble (node); break; case TableExprNodeRep::NTComplex: tsnptr = new TableExprNodeMinusDComplex (node); break; case TableExprNodeRep::NTDate: tsnptr = new TableExprNodeMinusDate (node); break; default: throwInvDT("in scalar operator-"); } }else{ switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr = new TableExprNodeArrayMinusInt (node); break; case TableExprNodeRep::NTDouble: tsnptr = new TableExprNodeArrayMinusDouble (node); break; case TableExprNodeRep::NTComplex: tsnptr = new TableExprNodeArrayMinusDComplex (node); break; case TableExprNodeRep::NTDate: tsnptr = new TableExprNodeArrayMinusDate (node); break; default: throwInvDT("in array operator-"); } } return setBinaryNodeInfo (tsnptr, right); } TENShPtr TableExprNode::newTimes (const TENShPtr& right) const { TableExprNodeRep node = TableExprNodeBinary::getCommonTypes (node_p, right, TableExprNodeRep::OtTimes); TableExprNodeBinary* tsnptr=0; if (node.valueType() == TableExprNodeRep::VTScalar) { switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr = new TableExprNodeTimesInt (node); break; case TableExprNodeRep::NTDouble: tsnptr = new TableExprNodeTimesDouble (node); break; case TableExprNodeRep::NTComplex: tsnptr = new TableExprNodeTimesDComplex (node); break; default: throwInvDT("in scalar operator*"); } }else{ switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr = new TableExprNodeArrayTimesInt (node); break; case TableExprNodeRep::NTDouble: tsnptr = new TableExprNodeArrayTimesDouble (node); break; case TableExprNodeRep::NTComplex: tsnptr = new TableExprNodeArrayTimesDComplex (node); break; default: throwInvDT("in array operator*"); } } return setBinaryNodeInfo (tsnptr, right); } TENShPtr TableExprNode::newDivide (const TENShPtr& right) const { // Note that (as in python3) integer division is exact and results in // a double. TableExprNodeRep node = TableExprNodeBinary::getCommonTypes (node_p, right, TableExprNodeRep::OtDivide); TableExprNodeBinary* tsnptr=0; if (node.valueType() == TableExprNodeRep::VTScalar) { switch (node.dataType()) { case TableExprNodeRep::NTDouble: tsnptr = new TableExprNodeDivideDouble (node); break; case TableExprNodeRep::NTComplex: tsnptr = new TableExprNodeDivideDComplex (node); break; default: throwInvDT("in scalar operator/"); } }else{ switch (node.dataType()) { case TableExprNodeRep::NTDouble: tsnptr = new TableExprNodeArrayDivideDouble (node); break; case TableExprNodeRep::NTComplex: tsnptr = new TableExprNodeArrayDivideDComplex (node); break; default: throwInvDT("in array operator/"); } } return setBinaryNodeInfo (tsnptr, right); } TENShPtr TableExprNode::newModulo (const TENShPtr& right) const { TableExprNodeRep node = TableExprNodeBinary::getCommonTypes (node_p, right, TableExprNodeRep::OtModulo); TableExprNodeBinary* tsnptr=0; if (node.valueType() == TableExprNodeRep::VTScalar) { switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr = new TableExprNodeModuloInt (node); break; case TableExprNodeRep::NTDouble: tsnptr = new TableExprNodeModuloDouble (node); break; default: throwInvDT("no real operands in modulo (%)"); } }else{ switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr = new TableExprNodeArrayModuloInt (node); break; case TableExprNodeRep::NTDouble: tsnptr = new TableExprNodeArrayModuloDouble (node); break; default: throwInvDT("no real operands in modulo (%)"); } } return setBinaryNodeInfo (tsnptr, right); } TENShPtr TableExprNode::newBitAnd (const TENShPtr& right) const { TableExprNodeRep node = TableExprNodeBinary::getCommonTypes (node_p, right, TableExprNodeRep::OtBitAnd); TableExprNodeBinary* tsnptr=0; if (node.valueType() == TableExprNodeRep::VTScalar) { switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr = new TableExprNodeBitAndInt (node); break; default: throwInvDT("no integer operands in bitand (&)"); } }else{ switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr = new TableExprNodeArrayBitAndInt (node); break; default: throwInvDT("no integer operands in bitand (&)"); } } return setBinaryNodeInfo (tsnptr, right); } TENShPtr TableExprNode::newBitOr (const TENShPtr& right) const { TableExprNodeRep node = TableExprNodeBinary::getCommonTypes (node_p, right, TableExprNodeRep::OtBitOr); TableExprNodeBinary* tsnptr=0; if (node.valueType() == TableExprNodeRep::VTScalar) { switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr = new TableExprNodeBitOrInt (node); break; default: throwInvDT("no integer operands in bitor (|)"); } }else{ switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr = new TableExprNodeArrayBitOrInt (node); break; default: throwInvDT("no integer operands in bitor (|)"); } } return setBinaryNodeInfo (tsnptr, right); } TENShPtr TableExprNode::newBitXor (const TENShPtr& right) const { TableExprNodeRep node = TableExprNodeBinary::getCommonTypes (node_p, right, TableExprNodeRep::OtBitXor); TableExprNodeBinary* tsnptr=0; if (node.valueType() == TableExprNodeRep::VTScalar) { switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr = new TableExprNodeBitXorInt (node); break; default: throwInvDT("no integer operands in bitxor (^)"); } }else{ switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr = new TableExprNodeArrayBitXorInt (node); break; default: throwInvDT("no integer operands in bitxor (^)"); } } return setBinaryNodeInfo (tsnptr, right); } TENShPtr TableExprNode::newEQ (const TENShPtr& right) const { TableExprNodeRep node = TableExprNodeBinary::getCommonTypes (node_p, right, TableExprNodeRep::OtEQ); TableExprNodeBinary* tsnptr=0; if (node.valueType() == TableExprNodeRep::VTScalar) { switch (node.dataType()) { case TableExprNodeRep::NTBool: tsnptr = new TableExprNodeEQBool (node); break; case TableExprNodeRep::NTInt: tsnptr = new TableExprNodeEQInt (node); break; case TableExprNodeRep::NTDouble: tsnptr = new TableExprNodeEQDouble (node); break; case TableExprNodeRep::NTComplex: tsnptr = new TableExprNodeEQDComplex (node); break; case TableExprNodeRep::NTString: tsnptr = new TableExprNodeEQString (node); break; case TableExprNodeRep::NTRegex: tsnptr = new TableExprNodeEQRegex (node); break; case TableExprNodeRep::NTDate: tsnptr = new TableExprNodeEQDate (node); break; default: throwInvDT("in scalar operator=="); } }else{ switch (node.dataType()) { case TableExprNodeRep::NTBool: tsnptr = new TableExprNodeArrayEQBool (node); break; case TableExprNodeRep::NTInt: tsnptr = new TableExprNodeArrayEQInt (node); break; case TableExprNodeRep::NTDouble: tsnptr = new TableExprNodeArrayEQDouble (node); break; case TableExprNodeRep::NTComplex: tsnptr = new TableExprNodeArrayEQDComplex (node); break; case TableExprNodeRep::NTString: tsnptr = new TableExprNodeArrayEQString (node); break; case TableExprNodeRep::NTRegex: tsnptr = new TableExprNodeArrayEQRegex (node); break; case TableExprNodeRep::NTDate: tsnptr = new TableExprNodeArrayEQDate (node); break; default: throwInvDT("in array operator=="); } } return setBinaryNodeInfo (tsnptr, right); } TENShPtr TableExprNode::newNE (const TENShPtr& right) const { TableExprNodeRep node = TableExprNodeBinary::getCommonTypes (node_p, right, TableExprNodeRep::OtNE); TableExprNodeBinary* tsnptr=0; if (node.valueType() == TableExprNodeRep::VTScalar) { switch (node.dataType()) { case TableExprNodeRep::NTBool: tsnptr = new TableExprNodeNEBool (node); break; case TableExprNodeRep::NTInt: tsnptr = new TableExprNodeNEInt (node); break; case TableExprNodeRep::NTDouble: tsnptr = new TableExprNodeNEDouble (node); break; case TableExprNodeRep::NTComplex: tsnptr = new TableExprNodeNEDComplex (node); break; case TableExprNodeRep::NTString: tsnptr = new TableExprNodeNEString (node); break; case TableExprNodeRep::NTRegex: tsnptr = new TableExprNodeNERegex (node); break; case TableExprNodeRep::NTDate: tsnptr = new TableExprNodeNEDate (node); break; default: throwInvDT("in scalar operator<> (!=)"); } }else{ switch (node.dataType()) { case TableExprNodeRep::NTBool: tsnptr = new TableExprNodeArrayNEBool (node); break; case TableExprNodeRep::NTInt: tsnptr = new TableExprNodeArrayNEInt (node); break; case TableExprNodeRep::NTDouble: tsnptr = new TableExprNodeArrayNEDouble (node); break; case TableExprNodeRep::NTComplex: tsnptr = new TableExprNodeArrayNEDComplex (node); break; case TableExprNodeRep::NTString: tsnptr = new TableExprNodeArrayNEString (node); break; case TableExprNodeRep::NTRegex: tsnptr = new TableExprNodeArrayNERegex (node); break; case TableExprNodeRep::NTDate: tsnptr = new TableExprNodeArrayNEDate (node); break; default: throwInvDT("in array operator<> (!=)"); } } return setBinaryNodeInfo (tsnptr, right); } TENShPtr TableExprNode::newGT (const TENShPtr& right) const { TableExprNodeRep node = TableExprNodeBinary::getCommonTypes (node_p, right, TableExprNodeRep::OtGT); TableExprNodeBinary* tsnptr=0; if (node.valueType() == TableExprNodeRep::VTScalar) { switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr = new TableExprNodeGTInt (node); break; case TableExprNodeRep::NTDouble: tsnptr = new TableExprNodeGTDouble (node); break; case TableExprNodeRep::NTComplex: tsnptr = new TableExprNodeGTDComplex (node); break; case TableExprNodeRep::NTString: tsnptr = new TableExprNodeGTString (node); break; case TableExprNodeRep::NTDate: tsnptr = new TableExprNodeGTDate (node); break; default: throwInvDT("in scalar operator>"); } }else{ switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr = new TableExprNodeArrayGTInt (node); break; case TableExprNodeRep::NTDouble: tsnptr = new TableExprNodeArrayGTDouble (node); break; case TableExprNodeRep::NTComplex: tsnptr = new TableExprNodeArrayGTDComplex (node); break; case TableExprNodeRep::NTString: tsnptr = new TableExprNodeArrayGTString (node); break; case TableExprNodeRep::NTDate: tsnptr = new TableExprNodeArrayGTDate (node); break; default: throwInvDT("in array operator>"); } } return setBinaryNodeInfo (tsnptr, right); } TENShPtr TableExprNode::newGE (const TENShPtr& right) const { TableExprNodeRep node = TableExprNodeBinary::getCommonTypes (node_p, right, TableExprNodeRep::OtGE); TableExprNodeBinary* tsnptr=0; if (node.valueType() == TableExprNodeRep::VTScalar) { switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr = new TableExprNodeGEInt (node); break; case TableExprNodeRep::NTDouble: tsnptr = new TableExprNodeGEDouble (node); break; case TableExprNodeRep::NTComplex: tsnptr = new TableExprNodeGEDComplex (node); break; case TableExprNodeRep::NTString: tsnptr = new TableExprNodeGEString (node); break; case TableExprNodeRep::NTDate: tsnptr = new TableExprNodeGEDate (node); break; default: throwInvDT("in scalar operator>="); } }else{ switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr = new TableExprNodeArrayGEInt (node); break; case TableExprNodeRep::NTDouble: tsnptr = new TableExprNodeArrayGEDouble (node); break; case TableExprNodeRep::NTComplex: tsnptr = new TableExprNodeArrayGEDComplex (node); break; case TableExprNodeRep::NTString: tsnptr = new TableExprNodeArrayGEString (node); break; case TableExprNodeRep::NTDate: tsnptr = new TableExprNodeArrayGEDate (node); break; default: throwInvDT("in array operator>="); } } return setBinaryNodeInfo (tsnptr, right); } TENShPtr TableExprNode::newIN (const TENShPtr& right, const TaQLStyle& style) const { // Is the right operand a scalar or an array with a single element? // If so, the IN operator can be replaced by the EQ operator. // Note that an array can also be represented by a set with single values. TableExprNodeRep::ValueType vtRight = right->valueType(); if (vtRight == TableExprNodeRep::VTScalar) { return newEQ (right); } else if (vtRight == TableExprNodeRep::VTArray) { TableExprNodeSet* set = dynamic_cast(right.get()); if (set) { if (set->isSingle() && set->size() == 1 && ! set->hasArrays()) { TENShPtr snode = (*set)[0]->start(); return newEQ (snode); } } else { TableExprNodeArray* arr = dynamic_cast(right.get()); if (arr) { TENShPtr sca = arr->makeConstantScalar(); if (sca) { return newEQ (sca); } } } } else if (vtRight != TableExprNodeRep::VTSet) { throw (TableInvExpr ("Right operand of IN has to be a scalar, array or set")); } // A mix of Int and Double operands means Double. // Otherwise the operands should have equal data types. TableExprNodeRep::NodeDataType dtype = node_p->dataType(); TableExprNodeRep::NodeDataType rdtype = right->dataType(); if (dtype != rdtype) { if ((dtype==TableExprNodeRep::NTInt && rdtype==TableExprNodeRep::NTDouble) || (dtype==TableExprNodeRep::NTDouble && rdtype==TableExprNodeRep::NTInt)) { dtype = TableExprNodeRep::NTDouble; } else { throwInvDT ("mismatching operand types for IN-operator"); } } // If both operands are constant, the result is constant as well. TableExprNodeRep::ExprType extype = TableExprNodeRep::Variable; if (right->isConstant() && node_p->isConstant()) { extype = TableExprNodeRep::Constant; } TableExprNodeRep node (dtype, node_p->valueType(), TableExprNodeRep::OtIN, TableExprNodeRep::NoArr, extype, node_p->ndim(), node_p->shape()); // Create the correct IN object depending on data type // and if the left hand operand is scalar or array. TableExprNodeBinary* tsnptr = 0; if (node.valueType() == TableExprNodeRep::VTScalar) { switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr = new TableExprNodeINInt (node, style.doTracing()); break; case TableExprNodeRep::NTDouble: tsnptr = new TableExprNodeINDouble (node); break; case TableExprNodeRep::NTComplex: tsnptr = new TableExprNodeINDComplex (node); break; case TableExprNodeRep::NTString: tsnptr = new TableExprNodeINString (node); break; case TableExprNodeRep::NTDate: tsnptr = new TableExprNodeINDate (node); break; default: throwInvDT("in scalar IN-operator"); } }else{ switch (node.dataType()) { case TableExprNodeRep::NTInt: tsnptr = new TableExprNodeArrayINInt (node); break; case TableExprNodeRep::NTDouble: tsnptr = new TableExprNodeArrayINDouble (node); break; case TableExprNodeRep::NTComplex: tsnptr = new TableExprNodeArrayINDComplex (node); break; case TableExprNodeRep::NTString: tsnptr = new TableExprNodeArrayINString (node); break; case TableExprNodeRep::NTDate: tsnptr = new TableExprNodeArrayINDate (node); break; default: throwInvDT("in array IN-operator"); } } return setBinaryNodeInfo (tsnptr, right); } TENShPtr TableExprNode::newOR (const TENShPtr& right) const { TableExprNodeRep node = TableExprNodeBinary::getCommonTypes (node_p, right, TableExprNodeRep::OtOR); TableExprNodeBinary* tsnptr = 0; if (node.valueType() == TableExprNodeRep::VTScalar) { switch (node.dataType()) { case TableExprNodeRep::NTBool: tsnptr = new TableExprNodeOR (node); break; default: throwInvDT("no Bool operands in logical OR (||)"); } }else{ switch (node.dataType()) { case TableExprNodeRep::NTBool: tsnptr = new TableExprNodeArrayOR (node); break; default: throwInvDT("no Bool operands in logical OR (||)"); } } return setBinaryNodeInfo (tsnptr, right); } TENShPtr TableExprNode::newAND (const TENShPtr& right) const { TableExprNodeRep node = TableExprNodeBinary::getCommonTypes (node_p, right, TableExprNodeRep::OtAND); TableExprNodeBinary* tsnptr = 0; if (node.valueType() == TableExprNodeRep::VTScalar) { switch (node.dataType()) { case TableExprNodeRep::NTBool: tsnptr = new TableExprNodeAND (node); break; default: throwInvDT("no Bool operators in logical AND (&&)"); } }else{ switch (node.dataType()) { case TableExprNodeRep::NTBool: tsnptr = new TableExprNodeArrayAND (node); break; default: throwInvDT("no Bool operators in logical AND (&&)"); } } return setBinaryNodeInfo (tsnptr, right); } TableExprNode TableExprNode::operator+ () const { return *this; } TableExprNode TableExprNode::operator- () const { if (node_p->dataType() != TableExprNodeRep::NTInt && node_p->dataType() != TableExprNodeRep::NTDouble && node_p->dataType() != TableExprNodeRep::NTComplex) { throwInvDT("no numeric operand in unary -"); } TableExprNodeBinary* tsnptr; if (node_p->valueType() == TableExprNodeRep::VTScalar) { tsnptr = new TableExprNodeMIN (*node_p); }else{ tsnptr = new TableExprNodeArrayMIN (*node_p); } return setBinaryNodeInfo (tsnptr); } TableExprNode TableExprNode::operator! () const { if (node_p->dataType() != TableExprNodeRep::NTBool) { throwInvDT("no numeric operand in unary NOT (!)"); } TableExprNodeBinary* tsnptr; if (node_p->valueType() == TableExprNodeRep::VTScalar) { tsnptr = new TableExprNodeNOT (*node_p); }else{ tsnptr = new TableExprNodeArrayNOT (*node_p); } return setBinaryNodeInfo (tsnptr); } TableExprNode TableExprNode::operator~ () const { if (node_p->dataType() != TableExprNodeRep::NTInt) { throwInvDT ("no integer operand in unary bitnegate (~)"); } TableExprNodeBinary* tsnptr; if (node_p->valueType() == TableExprNodeRep::VTScalar) { tsnptr = new TableExprNodeBitNegate (*node_p); }else{ tsnptr = new TableExprNodeArrayBitNegate (*node_p); } return setBinaryNodeInfo (tsnptr); } //# Create an expression node for either a keyword or column. TableExprNode TableExprNode::keyCol (const TableExprInfo& tabInfo, const String& name, const Vector& fieldNames) { if (tabInfo.table().tableDesc().isColumn (name)) { return newColumnNode (tabInfo, name, fieldNames); } else { uInt nr = fieldNames.nelements(); Vector names (nr + 1); names (Slice(1,nr)) = fieldNames; names(0) = name; return newKeyConst (tabInfo.table().keywordSet(), names); } } //# Create a column node on behalf of the Table class. //# For builtin data types another type of node is created than //# for other data types. TableExprNode TableExprNode::newColumnNode (const TableExprInfo& tableInfo, const String& name, const Vector& fieldNames) { //# Get the column description. This throws an exception if //# the name is not a column. const Table& table = tableInfo.table(); TENShPtr tsnptr; const ColumnDesc& coldes = table.tableDesc().columnDesc (name); TableColumn col(table, name); if (fieldNames.size() > 0 && coldes.dataType() != TpRecord) { throw (TableInvExpr ("Column " + name + " does not contain records, " "so no subfields can be given for it")); } if (coldes.isArray()) { switch(coldes.dataType()) { case TpBool: tsnptr = std::make_shared(col, tableInfo); break; case TpUChar: tsnptr = std::make_shared(col, tableInfo); break; case TpShort: tsnptr = std::make_shared(col, tableInfo); break; case TpUShort: tsnptr = std::make_shared(col, tableInfo); break; case TpInt: tsnptr = std::make_shared(col, tableInfo); break; case TpUInt: tsnptr = std::make_shared(col, tableInfo); break; case TpInt64: tsnptr = std::make_shared(col, tableInfo); break; case TpFloat: tsnptr = std::make_shared(col, tableInfo); break; case TpDouble: tsnptr = std::make_shared(col, tableInfo); break; case TpComplex: tsnptr = std::make_shared(col, tableInfo); break; case TpDComplex: tsnptr = std::make_shared(col, tableInfo); break; case TpString: tsnptr = std::make_shared(col, tableInfo); break; default: throw (TableInvExpr (name, "unknown data type")); } } else if (coldes.isScalar()) { if (coldes.dataType() == TpRecord && fieldNames.size() == 0) { throw (TableInvExpr ("Column " + name + " contains records, " "so subfields have to be given for it")); } if (coldes.dataType() == TpRecord) { throw (TableInvExpr ("Sorry, column " + name + " contains records, " "which is not supported yet")); } tsnptr = std::make_shared(tableInfo, name); } else { throw (TableInvExpr (name, " must be a Scalar or Array column")); } return tsnptr; } // Find the last TableRecord of the field names of a keyword. TableRecord* TableExprNode::findLastKeyRec (const TableRecord& keyset, const Vector& fieldNames, String& fullName) { const TableRecord* ksPtr = &keyset; // All field names, except last one, should be records. uInt last = fieldNames.size() - 1; fullName.clear(); Int fieldnr = 0; for (uInt i=0; i 0) { fullName += '.'; } fullName += fieldNames(i); fieldnr = ksPtr->fieldNumber (fieldNames(i)); if (fieldnr < 0) { throw (TableInvExpr ("Keyword " + fullName + " does not exist")); } if (ksPtr->dataType(fieldnr) != TpRecord) { throw (TableInvExpr ("Keyword " + fullName + " is no record, " "so no subfields can be given for it")); } ksPtr = &(ksPtr->subRecord(fieldnr)); } return const_cast(ksPtr); } //# Create a constant node for a keyword on behalf of the Table class. //# The constructor reads in the value and stores it as a constant. TableExprNode TableExprNode::newKeyConst (const TableRecord& keyset, const Vector& fieldNames) { TENShPtr tsnptr; String fullName; const TableRecord* ks = findLastKeyRec (keyset, fieldNames, fullName); String name = fieldNames[fieldNames.size() - 1]; fullName += '.' + name; Int fieldnr = ks->fieldNumber (name); if (fieldnr < 0) { throw (TableInvExpr ("Keyword " + fullName + " does not exist")); } switch (ks->dataType (fieldnr)) { case TpBool: tsnptr = std::make_shared(ks->asBool (name)); break; case TpString: tsnptr = std::make_shared(ks->asString (name)); break; case TpComplex: case TpDComplex: tsnptr = std::make_shared(ks->asDComplex (name)); break; case TpFloat: case TpDouble: tsnptr = std::make_shared(ks->asDouble (name)); break; case TpChar: case TpUChar: case TpShort: case TpUShort: case TpInt: case TpUInt: case TpInt64: tsnptr = std::make_shared(ks->asInt64 (name)); break; case TpArrayBool: tsnptr = std::make_shared(ks->asArrayBool (name)); break; case TpArrayString: tsnptr = std::make_shared (ks->asArrayString (name)); break; case TpArrayComplex: tsnptr = std::make_shared (ks->asArrayComplex (name)); break; case TpArrayDComplex: tsnptr = std::make_shared (ks->asArrayDComplex (name)); break; case TpArrayUChar: tsnptr = std::make_shared (ks->asArrayuChar (name)); break; case TpArrayShort: tsnptr = std::make_shared (ks->asArrayShort (name)); break; case TpArrayInt: tsnptr = std::make_shared (ks->asArrayInt (name)); break; case TpArrayUInt: tsnptr = std::make_shared (ks->asArrayuInt (name)); break; case TpArrayInt64: tsnptr = std::make_shared (ks->asArrayInt64 (name)); break; case TpArrayFloat: tsnptr = std::make_shared (ks->asArrayFloat (name)); break; case TpArrayDouble: tsnptr = std::make_shared (ks->asArrayDouble (name)); break; case TpRecord: throw (TableInvExpr ("Keyword " + fullName + " contains records, " "so subfields have to be given for it")); break; case TpTable: throw (TableInvExpr ("Keyword " + name + " is a table")); break; default: throw (TableInvExpr ("keyword " + fullName + " has unknown data type")); } return tsnptr; } TableExprNode diagonal (const TableExprNode& array, const TableExprNode& firstAxis) { TableExprNodeSet set; set.add (TableExprNodeSetElem(firstAxis)); return TableExprNode::newFunctionNode (TableExprFuncNode::diagonalFUNC, array, set); } TableExprNode diagonal (const TableExprNode& array, const TableExprNode& firstAxis, const TableExprNode& diag) { TableExprNodeSet set; set.add (TableExprNodeSetElem(firstAxis)); set.add (TableExprNodeSetElem(diag)); return TableExprNode::newFunctionNode (TableExprFuncNode::diagonalFUNC, array, set); } TableExprNode TableExprNode::newFunctionNode (TableExprFuncNode::FunctionType ftype, const TableExprNode& node) { TableExprNodeSet set; set.add (TableExprNodeSetElem(node)); return newFunctionNode (ftype, set, TableExprInfo()); } TableExprNode TableExprNode::newFunctionNode (TableExprFuncNode::FunctionType ftype, const TableExprNode& node1, const TableExprNode& node2) { TableExprNodeSet set; set.add (TableExprNodeSetElem(node1)); set.add (TableExprNodeSetElem(node2)); return newFunctionNode (ftype, set, TableExprInfo()); } TableExprNode TableExprNode::newFunctionNode (TableExprFuncNode::FunctionType ftype, const TableExprNode& node1, const TableExprNode& node2, const TableExprNode& node3) { TableExprNodeSet set; set.add (TableExprNodeSetElem(node1)); set.add (TableExprNodeSetElem(node2)); set.add (TableExprNodeSetElem(node3)); return newFunctionNode (ftype, set, TableExprInfo()); } TableExprNode TableExprNode::newFunctionNode (TableExprFuncNode::FunctionType ftype, const TableExprNode& array, const TableExprNodeSet& axes) { TableExprNodeSet set; set.add (TableExprNodeSetElem(array)); // Turn the axes set into an array. set.add (TableExprNodeSetElem(axes.setOrArray())); return newFunctionNode (ftype, set, TableExprInfo()); } TableExprNode TableExprNode::newFunctionNode (TableExprFuncNode::FunctionType ftype, const TableExprNode& array, const TableExprNode& node, const TableExprNodeSet& axes) { TableExprNodeSet set; set.add (TableExprNodeSetElem(array)); set.add (TableExprNodeSetElem(node)); // Turn the axes set into an array. set.add (TableExprNodeSetElem(axes.setOrArray())); return newFunctionNode (ftype, set, TableExprInfo()); } TableExprNode TableExprNode::newFunctionNode (TableExprFuncNode::FunctionType ftype, const TableExprNodeSet& set, const TableExprInfo& tabInfo, const TaQLStyle& style) { // Convert the set to a vector of the values in the set elements. // This requires that the set has single values. if (! set.isSingle()) { throw (TableInvExpr ("A function parameter cannot be an interval")); } uInt npar = set.size(); vector par(npar); for (uInt i=0; istart(); } // rownrFUNC, rowidFUNC and randomFUNC are special, because they // need their own objects and the table. if (ftype == TableExprFuncNode::rownrFUNC) { TableExprNodeMulti::checkNumOfArg (0, 0, par); // First rownr is 0 or 1. return newRownrNode (tabInfo, style.origin()); } if (ftype == TableExprFuncNode::rowidFUNC) { TableExprNodeMulti::checkNumOfArg (0, 0, par); return newRowidNode (tabInfo); } if (ftype == TableExprFuncNode::randFUNC) { TableExprNodeMulti::checkNumOfArg (0, 0, par); return newRandomNode (tabInfo); } // Check all the operands and get the resulting data type and value type // of the function. // It also fills the expected data and value type of the operands. TableExprNodeRep::ValueType resVT; TableExprNodeRep::NodeDataType resDT; Block dtypeOper; Block vtypeOper; TENShPtr fnode; // Create new function node depending on the type. if (ftype >= TableExprFuncNode::FirstAggrFunc) { resDT = TableExprAggrNode::checkOperands (dtypeOper, resVT, ftype, par); // Create new aggregate function node and fill it. if (resVT == TableExprNodeRep::VTScalar) { fnode = std::make_shared(ftype, resDT, resVT, set, par, dtypeOper); } else { fnode = std::make_shared(ftype, resDT, resVT, set, par, dtypeOper, style); } } else { resDT = TableExprFuncNode::checkOperands (dtypeOper, resVT, vtypeOper, ftype, par); if (resVT == TableExprNodeRep::VTScalar) { auto node = std::make_shared(ftype, resDT, resVT, set, par, dtypeOper, tabInfo); fnode = node; // If the condition of IIF is a constant scalar, evaluate and replace. // Do it only if the data type matches. // Take the operands from the function node created, // because the units might be adapted. if (ftype == TableExprFuncNode::iifFUNC) { const std::vector& oper = node->operands(); if (oper[0]->isConstant()) { if (oper[0]->getBool(TableExprId(0))) { if (resDT == oper[1]->dataType()) { fnode = oper[1]; } } else { if (resDT == oper[2]->dataType()) { fnode = oper[2]; } } } } } else { fnode = std::make_shared(ftype, resDT, resVT, set, par, dtypeOper, style); } } return TableExprNodeRep::replaceConstNode (fnode); } TableExprNode TableExprNode::newUDFNode (const String& name, const TableExprNodeSet& set, const TableExprInfo& tableInfo, const TaQLStyle& style) { // Create the correct UDF object. An exception is thrown if unknown. std::shared_ptr udf(UDFBase::createUDF (name, style)); // Convert the set to a vector of the values in the set elements. // This requires that the set has single values. if (! set.isSingle()) { throw (TableInvExpr ("A function parameter cannot be an interval")); } uInt npar = set.size(); vector par(npar); for (uInt i=0; istart(); } udf->init (par, tableInfo, style); if (udf->ndim() == 0) { return new TableExprUDFNode (udf, tableInfo, set); } return new TableExprUDFNodeArray (udf, tableInfo, set); } TableExprNode TableExprNode::newConeNode (TableExprFuncNode::FunctionType ftype, const TableExprNode& node1, const TableExprNode& node2) { TableExprNodeSet set; set.add (TableExprNodeSetElem(node1)); set.add (TableExprNodeSetElem(node2)); return newConeNode (ftype, set); } TableExprNode TableExprNode::newConeNode (TableExprFuncNode::FunctionType ftype, const TableExprNode& node1, const TableExprNode& node2, const TableExprNode& node3) { TableExprNodeSet set; set.add (TableExprNodeSetElem(node1)); set.add (TableExprNodeSetElem(node2)); set.add (TableExprNodeSetElem(node3)); return newConeNode (ftype, set); } TableExprNode TableExprNode::newConeNode (TableExprFuncNode::FunctionType ftype, const TableExprNodeSet& set, uInt origin) { // Convert the set to a vector of the values in the set elements. // This requires that the set has single values. if (! set.isSingle()) { throw (TableInvExpr ("A function parameter cannot be an interval")); } uInt npar = set.size(); vector par(npar); for (uInt i=0; istart(); } // Check all the operands and get the resulting data type and value type // of the function. // It also fills the expected data and value type of the operands. Block dtypeOper; Block vtypeOper; TableExprNodeRep::ValueType resVT; TableExprNodeRep::NodeDataType resDT; resDT = TableExprConeNode::checkOperands (dtypeOper, resVT, vtypeOper, ftype, par); // Create new function node and fill it. TENShPtr fnode; if (resVT == TableExprNodeRep::VTScalar) { fnode = std::make_shared(ftype, resDT, set, par, dtypeOper, origin); } else { fnode = std::make_shared(ftype, resDT, set, par, dtypeOper, origin); } return TableExprNodeRep::replaceConstNode (fnode); } TableExprNode TableExprNode::newArrayPartNode (const TableExprNode& arrayNode, const TableExprNodeSet& indices, const TaQLStyle& style) { // Check if the node is an array. if (arrayNode.node_p->valueType() != TableExprNodeRep::VTArray) { throw (TableInvExpr ("Indexing can only be done on arrays")); } // Create new Index node and fill it. TENShPtr inode(new TableExprNodeIndex (indices, style)); TENShPtr anode(new TableExprNodeArrayPart (arrayNode.node_p, inode)); return anode; } void TableExprNode::adaptUnit (const Unit& unit) { TableExprNodeUnit::adaptUnit (node_p, unit); } TableExprNode TableExprNode::newRownrNode (const TableExprInfo& tableInfo, uInt origin) { return TENShPtr(new TableExprNodeRownr (tableInfo, origin)); } TableExprNode TableExprNode::newRowidNode (const TableExprInfo& tableInfo) { return TENShPtr(new TableExprNodeRowid (tableInfo)); } TableExprNode TableExprNode::newRandomNode (const TableExprInfo& tableInfo) { return TENShPtr(new TableExprNodeRandom (tableInfo)); } DataType TableExprNode::dataType() const { if (node_p->valueType() == TableExprNodeRep::VTScalar || node_p->valueType() == TableExprNodeRep::VTArray) { switch(node_p->dataType()) { case TableExprNodeRep::NTBool: return TpBool; case TableExprNodeRep::NTInt: return TpInt64; case TableExprNodeRep::NTDouble: return TpDouble; case TableExprNodeRep::NTComplex: return TpDComplex; case TableExprNodeRep::NTString: return TpString; case TableExprNodeRep::NTDate: return TpQuantity; default: return TpOther; } } return TpOther; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/ExprNode.h000066400000000000000000002523411476623553700173030ustar00rootroot00000000000000//# ExprNode.h: Handle class for a table column expression tree //# Copyright (C) 1994,1995,1996,1997,1998,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_EXPRNODE_H #define TABLES_EXPRNODE_H //# Includes #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class Table; class String; class Regex; class StringDistance; class Unit; class Record; class TableRecord; class TableExprNodeBinary; class TableExprNodeSet; template class Block; template class MArray; // // Handle class for a table column expression tree // // // // // //# Classes you should understand before using this one. //
      • Table //
      • Note 199 describing // // TaQL // // // TableExprNode represents a node in the tree reflecting a // table select expression. // // // TableExprNode is the class to store a table select expression, // making it possible to select rows from the table. The selected // rows form a table which is a view of the original table. //

        // TableExprNode is a handle class for the counted referenced class // TableExprNodeRep. // Classes (like TableExprNodePlusXX) derived from TableExprNodeRep // hold the individual // nodes in the expression, i.e. the operators and operands. The nodes // form a binary tree reflecting the expression. // E.g. the expression 2*COLUMN results in the node TableExprNodeTimes // with its children TableExprNodeConst and TableExprNodeColumn. // Constant subexpressions (like 2*3) are evaluated immediately and // only the result is stored as a node. //

        // There are a few TableExprNode constructors taking a constant scalar or array. // In this way constant value are automatically converted to the // appropriate TableExprNodeConst object. //

        // The derived classes also reflect the data type of the node. // Data types Bool, Int64, Double, DComplex and String are used. // Char, uChar, Short, uShort, Int and uInt are converted to Int64, // float to Double, and Complex to DComplex. // Binary operators +, -, *, /, %, &, }, ^, ==, >=, >, <, <= and != are // recognized. Also &&, ||, parentheses and unary +, -, ~ and ! are recognized. // For strings the binary operator + can also be used. // The operators have the normal C++ precedence. // Furthermore functions (such as sin, max, ceil) can be used in an expression. //
        Operator() can be used to take a slice from an array. //

        // The Table function col has to be used to create a TableExprNode // object for a column in the table. The Table // operator() can be used // the do the actual selection from the top TableExprNode object. // // // // // Select from table X all rows where column RA<5 and where column // // SWITCH is true. // Table table("X"); // Table subtable = table(table.col("RA") < 5 && table.col("SWITCH")); // // // Select from that result all rows where the concatenation of // // the strings in columns STR1 and STR2 is equal to the string // // in keyword STRKEY. // Table subsub = subtable(subtable.col("STR1") + subtable.col("STR2") // == subtable.key("STRKEY")); // // // // Having TableExprNode as a handle class makes it possible to // handle temporary objects created by the compiler in a smooth way. // TableExprNode and its derivations allow to store an expression // before actually evaluating it. This also allows the classes to // be used by the table expression parser defined in TableParse and // TableGram. // // For each operator a special derived class is implemented. // Another approach could have been to store the operator as // a flag and switch on that. However, that causes extra overhead // and the C++ virtual function mechanism is the designed for // these purposes. // // //# A List of bugs, limitations, extensions or planned refinements. //

      • add operations on arrays //
      • add selection by comparing with a set of values // class TableExprNode { public: TableExprNode (); // Unary operators on numeric TableExprNode's. // TableExprNode operator+ () const; TableExprNode operator- () const; // // Unary NOT-operator on boolean TableExprNode's. TableExprNode operator! () const; // Unary bitwise negate-operator on integer TableExprNode's. TableExprNode operator~ () const; // Slicing in a node containing an array. It is possible to // address a single pixel or an n-dimensional subarray. // In case of a single pixel the result is a scalar node. // Otherwise the result is an array node with the same dimensionality // as the source. //
        Note that there exist TableExprNodeSet constructors to // convert an IPosition or Slicer object // automatically to a TableExprNodeSet. // An IPosition addresses a single element and results in // a scalar node, while a Slicer can address multiple // elements and always results in an array node. TableExprNode operator() (const TableExprNodeSet& indices); // The IN operator to test if a value is contained in an array or set. // The array can also be a scalar. // TableExprNode in (const TableExprNode& array, const TaQLStyle& = TaQLStyle(0)) const; TableExprNode in (const TableExprNodeSet& set, const TaQLStyle& = TaQLStyle(0)) const; // // Use a unit for the given TableExprNode. // Note that if a column has a unit, it is automatically set. In that case // this can be used to convert units. TableExprNode useUnit (const Unit& unit) const; // Constructors to convert a constant value to a TableExprNode. // The constructor for char* is also supported to convert a // character-array to a string, since a two step conversion // is not done automatically. // TableExprNode (const Bool& value); TableExprNode (const Int& value); TableExprNode (const uInt& value); TableExprNode (const Int64& value); TableExprNode (const uInt64& value); TableExprNode (const Float& value); TableExprNode (const Double& value); TableExprNode (const Complex& value); TableExprNode (const DComplex& value); TableExprNode (const String& value); TableExprNode (const std::string& value); TableExprNode (const char*); TableExprNode (const Regex& value); TableExprNode (const StringDistance& value); TableExprNode (const TaqlRegex& value); TableExprNode (const MVTime& value); TableExprNode (const Array& value); TableExprNode (const Array& value); TableExprNode (const Array& value); TableExprNode (const Array& value); TableExprNode (const Array& value); TableExprNode (const Array& value); TableExprNode (const Array& value); //# The following constructor has to be explicit, othwerwise //# Table(Vector) //# gives an ambiguous error as the preferred class RowNumbers //# has a similar constructor. explicit TableExprNode (const Array& value); TableExprNode (const Array& value); TableExprNode (const Array& value); TableExprNode (const Array& value); TableExprNode (const Array& value); TableExprNode (const Array& value); TableExprNode (const Array& value); TableExprNode (const MArray& value); TableExprNode (const MArray& value); TableExprNode (const MArray& value); TableExprNode (const MArray& value); TableExprNode (const MArray& value); TableExprNode (const MArray& value); TableExprNode (const MArray& value); TableExprNode (const MArray& value); TableExprNode (const MArray& value); TableExprNode (const MArray& value); TableExprNode (const MArray& value); TableExprNode (const MArray& value); TableExprNode (const MArray& value); TableExprNode (const MArray& value); // // Construct a node from a node representation shared pointer // which increments the reference count. TableExprNode (const TENShPtr&); // Construct from a node representation. It takes over the pointer, so the // object gets deleted automatically. TableExprNode (TableExprNodeRep* rep) : node_p(TENShPtr(rep)) {} // copy constructor (reference semantics). TableExprNode (const TableExprNode&); // Assignment (reference semantics). TableExprNode& operator= (const TableExprNode&); // The destructor deletes all the underlying TableExprNode objects, ~TableExprNode (); // Does the node contain no actual node? Bool isNull() const { return !node_p; } // Do not apply the selection. void disableApplySelection() { node_p->disableApplySelection(); } // Re-create the column object for a selection of rows. // Nothing is done if the node does not represent a column object. void applySelection (const Vector& rownrs) { node_p->applySelection (rownrs); } // Get the table info of the expression node. TableExprInfo getTableInfo() const { return node_p->getTableInfo(); } // Get the table to which the expression node belongs. //# [[deprecated ("Use getTableInfo().table() instead")]] Table table() const __attribute__ ((deprecated ("Use getTableInfo().table() instead"))) { return getTableInfo().table(); } // Get the unit of the expression. const Unit& unit() const { return node_p->unit(); } // Get the attributes of the expression. const Record& attributes() const { return node_p->attributes(); } // Get the data type of the expression. // Currently the only possible values are TpBool, TpInt, TpDouble, // TpDComplex, TpString, and TpOther. // The latter is returned for a date or regex. DataType dataType() const; // Is the expression a scalar? Bool isScalar() const { return (node_p->valueType() == TableExprNodeRep::VTScalar); } // Get the number of rows in the table associated with this expression. // One is returned if the expression is a constant or if no table is // associated with it. rownr_t nrow() const { return node_p->nrow(); } // Get a value for this node in the given row. // These functions are implemented in the derived classes and // will usually invoke the get in their children and apply the // operator on the resulting values. // void get (const TableExprId& id, Bool& value) const; void get (const TableExprId& id, Int64& value) const; void get (const TableExprId& id, Double& value) const; void get (const TableExprId& id, DComplex& value) const; void get (const TableExprId& id, String& value) const; void get (const TableExprId& id, TaqlRegex& value) const; void get (const TableExprId& id, MVTime& value) const; void get (const TableExprId& id, MArray& value) const; void get (const TableExprId& id, MArray& value) const; void get (const TableExprId& id, MArray& value) const; void get (const TableExprId& id, MArray& value) const; void get (const TableExprId& id, MArray& value) const; void get (const TableExprId& id, MArray& value) const; void get (const TableExprId& id, Array& value) const; void get (const TableExprId& id, Array& value) const; void get (const TableExprId& id, Array& value) const; void get (const TableExprId& id, Array& value) const; void get (const TableExprId& id, Array& value) const; void get (const TableExprId& id, Array& value) const; Bool getBool (const TableExprId& id) const; Int64 getInt (const TableExprId& id) const; Double getDouble (const TableExprId& id) const; DComplex getDComplex (const TableExprId& id) const; MVTime getDate (const TableExprId& id) const; String getString (const TableExprId& id) const; Array getArrayBool (const TableExprId& id) const; Array getArrayInt (const TableExprId& id) const; Array getArrayDouble (const TableExprId& id) const; Array getArrayDComplex (const TableExprId& id) const; Array getArrayString (const TableExprId& id) const; Array getArrayDate (const TableExprId& id) const; // Get a value as an array, even it it is a scalar. // This is useful in case one can give an argument as scalar or array. // MArray getBoolAS (const TableExprId& id) const; MArray getIntAS (const TableExprId& id) const; MArray getDoubleAS (const TableExprId& id) const; MArray getDComplexAS (const TableExprId& id) const; MArray getStringAS (const TableExprId& id) const; MArray getDateAS (const TableExprId& id) const; // // // Get the data type for doing a getColumn on the expression. // This is the data type of the column if the expression // consists of a single column only. // Otherwise it is the expression data type as returned by // function dataType. DataType getColumnDataType() const; // Get the value of the expression evaluated for the entire column. // The data of function called should match the data type as // returned by function getColumnDataType. // Array getColumnBool (const RowNumbers& rownrs) const; Array getColumnuChar (const RowNumbers& rownrs) const; Array getColumnShort (const RowNumbers& rownrs) const; Array getColumnuShort (const RowNumbers& rownrs) const; Array getColumnInt (const RowNumbers& rownrs) const; Array getColumnuInt (const RowNumbers& rownrs) const; Array getColumnInt64 (const RowNumbers& rownrs) const; Array getColumnFloat (const RowNumbers& rownrs) const; Array getColumnDouble (const RowNumbers& rownrs) const; Array getColumnComplex (const RowNumbers& rownrs) const; Array getColumnDComplex (const RowNumbers& rownrs) const; Array getColumnString (const RowNumbers& rownrs) const; // // The same functions as above for the old Vector. // They are primarily meant for CASA's SplatalogueTable class. // Normally they should not be used, hence declared private // unless told otherwise. #ifndef IMPLICIT_CTDS_32BIT private: #endif // Array getColumnBool (const Vector& rownrs) const { return getColumnBool (RowNumbers(rownrs)); } Array getColumnuChar (const Vector& rownrs) const { return getColumnuChar (RowNumbers(rownrs)); } Array getColumnShort (const Vector& rownrs) const { return getColumnShort (RowNumbers(rownrs)); } Array getColumnuShort (const Vector& rownrs) const { return getColumnuShort (RowNumbers(rownrs)); } Array getColumnInt (const Vector& rownrs) const { return getColumnInt (RowNumbers(rownrs)); } Array getColumnuInt (const Vector& rownrs) const { return getColumnuInt (RowNumbers(rownrs)); } Array getColumnInt64 (const Vector& rownrs) const { return getColumnInt64 (RowNumbers(rownrs)); } Array getColumnFloat (const Vector& rownrs) const { return getColumnFloat (RowNumbers(rownrs)); } Array getColumnDouble (const Vector& rownrs) const { return getColumnDouble (RowNumbers(rownrs)); } Array getColumnComplex (const Vector& rownrs) const { return getColumnComplex (RowNumbers(rownrs)); } Array getColumnDComplex (const Vector& rownrs) const { return getColumnDComplex (RowNumbers(rownrs)); } Array getColumnString (const Vector& rownrs) const { return getColumnString (RowNumbers(rownrs)); } // public: // Show the tree. void show (ostream&) const; // Convert the tree to a number of range vectors which at least // select the same things. // This function is very useful to convert the expression to // some intervals covering the select expression. This can // be used to do a rough fast selection via an index and do the // the slower final selection on that much smaller subset. // The function can only convert direct comparisons of columns // with constants (via ==, !=, >, >=, < or <=) and their combinations // using && or ||. void ranges (Block&); // Check if tables used in expression have the same number of // rows as the given table. Bool checkTableSize (const Table& table, Bool canBeConst) const; // Create a column node or constant keyword node. static TableExprNode keyCol (const TableExprInfo& tabInfo, const String& name, const Vector& fieldNames); // Create a column node on behalf of the Table class. // fieldNames indicate a possible field in a column of Records. // For builtin data types another type of node is created than // for other data types. static TableExprNode newColumnNode (const TableExprInfo&, const String& colName, const Vector& fieldNames); // Create a TableExprNodeConst for a table keyword // (which is handled as a constant). // fieldNames tells the name of the keyword and possible subrecords. static TableExprNode newKeyConst (const TableRecord&, const Vector& fieldNames); // Handle all field names except the last one. ALl of them must // be records. The last record is returned. // fullName is filled with the full keyword name separated by dots. static TableRecord* findLastKeyRec (const TableRecord& keyset, const Vector& fieldNames, String& fullName); // Throw invalid data type exception. static void throwInvDT (const String& message); // Create function node of the given type with the given arguments. // static TableExprNode newFunctionNode (TableExprFuncNode::FunctionType, const TableExprNodeSet& set, const TableExprInfo& tabInfo, const TaQLStyle& = TaQLStyle(0)); static TableExprNode newFunctionNode (TableExprFuncNode::FunctionType, const TableExprNode& node); static TableExprNode newFunctionNode (TableExprFuncNode::FunctionType, const TableExprNode& node1, const TableExprNode& node2); static TableExprNode newFunctionNode (TableExprFuncNode::FunctionType, const TableExprNode& node1, const TableExprNode& node2, const TableExprNode& node3); static TableExprNode newFunctionNode (TableExprFuncNode::FunctionType, const TableExprNode& array, const TableExprNodeSet& axes); static TableExprNode newFunctionNode (TableExprFuncNode::FunctionType, const TableExprNode& array, const TableExprNode& node, const TableExprNodeSet& axes); // // Create a user defined function node. static TableExprNode newUDFNode (const String& name, const TableExprNodeSet& set, const TableExprInfo& tableInfo, const TaQLStyle& = TaQLStyle(0)); // Create cone function node of the given type with the given arguments. // static TableExprNode newConeNode (TableExprFuncNode::FunctionType, const TableExprNodeSet& set, uInt origin = 0); static TableExprNode newConeNode (TableExprFuncNode::FunctionType, const TableExprNode& node1, const TableExprNode& node2); static TableExprNode newConeNode (TableExprFuncNode::FunctionType, const TableExprNode& node1, const TableExprNode& node2, const TableExprNode& node3); // // Create rownumber() function node. // Origin indicates whether the first row should be zero (for C++ binding) // or an other value (one for TaQL binding). static TableExprNode newRownrNode (const TableExprInfo&, uInt origin); // Create rowid() function node. // Origin is always 0. static TableExprNode newRowidNode (const TableExprInfo&); // Create rand() function node. static TableExprNode newRandomNode (const TableExprInfo&); // Create ArrayElement node for the given array with the given index. // The origin is 0 for C++ and 1 for TaQL. static TableExprNode newArrayPartNode (const TableExprNode& arrayNode, const TableExprNodeSet& indices, const TaQLStyle& = TaQLStyle(0)); // returns const pointer to the underlying TableExprNodeRep object. const TENShPtr& getRep() const; const TableExprNodeRep* getNodeRep() const; // Adapt the unit of the expression to the given unit (if not empty). void adaptUnit (const Unit&); // Construct a new node for the given operation. // TENShPtr newPlus (const TENShPtr& right) const; TENShPtr newMinus (const TENShPtr& right) const; TENShPtr newTimes (const TENShPtr& right) const; TENShPtr newDivide (const TENShPtr& right) const; TENShPtr newModulo (const TENShPtr& right) const; TENShPtr newBitAnd (const TENShPtr& right) const; TENShPtr newBitOr (const TENShPtr& right) const; TENShPtr newBitXor (const TENShPtr& right) const; TENShPtr newEQ (const TENShPtr& right) const; TENShPtr newNE (const TENShPtr& right) const; TENShPtr newGE (const TENShPtr& right) const; TENShPtr newGT (const TENShPtr& right) const; TENShPtr newIN (const TENShPtr& right, const TaQLStyle&) const; TENShPtr newOR (const TENShPtr& right) const; TENShPtr newAND (const TENShPtr& right) const; // private: // Put the new binary node object in a shared pointer. // Set the node's info and adapt the children if needed. // If the node is constant, it is evaluated and returned as result. TENShPtr setBinaryNodeInfo (TableExprNodeBinary* tsnptr, const TENShPtr& right=TENShPtr()) const; // convert Block of TableExprNode to vector of TENShPtr. static std::vector convertBlockTEN (Block& nodes); // The actual (counted referenced) representation of a node. TENShPtr node_p; }; inline void TableExprNode::ranges (Block& blrange) { node_p->ranges (blrange); } //# Get the value of an expression. inline void TableExprNode::get (const TableExprId& id, Bool& value) const { value = node_p->getBool (id); } inline void TableExprNode::get (const TableExprId& id, Int64& value) const { value = node_p->getInt (id); } inline void TableExprNode::get (const TableExprId& id, Double& value) const { value = node_p->getDouble (id); } inline void TableExprNode::get (const TableExprId& id, DComplex& value) const { value = node_p->getDComplex (id); } inline void TableExprNode::get (const TableExprId& id, String& value) const { value = node_p->getString (id); } inline void TableExprNode::get (const TableExprId& id, TaqlRegex& value) const { value = node_p->getRegex (id); } inline void TableExprNode::get (const TableExprId& id, MVTime& value) const { value = node_p->getDate (id); } inline void TableExprNode::get (const TableExprId& id, MArray& value) const { value = node_p->getArrayBool (id); } inline void TableExprNode::get (const TableExprId& id, MArray& value) const { value = node_p->getArrayInt (id); } inline void TableExprNode::get (const TableExprId& id, MArray& value) const { value = node_p->getArrayDouble (id); } inline void TableExprNode::get (const TableExprId& id, MArray& value) const { value = node_p->getArrayDComplex (id); } inline void TableExprNode::get (const TableExprId& id, MArray& value) const { value = node_p->getArrayString (id); } inline void TableExprNode::get (const TableExprId& id, MArray& value) const { value = node_p->getArrayDate (id); } inline void TableExprNode::get (const TableExprId& id, Array& value) const { value = node_p->getArrayBool (id).array(); } inline void TableExprNode::get (const TableExprId& id, Array& value) const { value = node_p->getArrayInt (id).array(); } inline void TableExprNode::get (const TableExprId& id, Array& value) const { value = node_p->getArrayDouble (id).array(); } inline void TableExprNode::get (const TableExprId& id, Array& value) const { value = node_p->getArrayDComplex (id).array(); } inline void TableExprNode::get (const TableExprId& id, Array& value) const { value = node_p->getArrayString (id).array(); } inline void TableExprNode::get (const TableExprId& id, Array& value) const { value = node_p->getArrayDate (id).array(); } inline Bool TableExprNode::getBool (const TableExprId& id) const { return node_p->getBool (id); } inline Int64 TableExprNode::getInt (const TableExprId& id) const { return node_p->getInt (id); } inline Double TableExprNode::getDouble (const TableExprId& id) const { return node_p->getDouble (id); } inline DComplex TableExprNode::getDComplex (const TableExprId& id) const { return node_p->getDComplex (id); } inline MVTime TableExprNode::getDate (const TableExprId& id) const { return node_p->getDate (id); } inline String TableExprNode::getString (const TableExprId& id) const { return node_p->getString (id); } inline Array TableExprNode::getArrayBool (const TableExprId& id) const { return node_p->getArrayBool (id).array(); } inline Array TableExprNode::getArrayInt (const TableExprId& id) const { return node_p->getArrayInt (id).array(); } inline Array TableExprNode::getArrayDouble (const TableExprId& id) const { return node_p->getArrayDouble (id).array(); } inline Array TableExprNode::getArrayDComplex (const TableExprId& id) const { return node_p->getArrayDComplex (id).array(); } inline Array TableExprNode::getArrayString (const TableExprId& id) const { return node_p->getArrayString (id).array(); } inline Array TableExprNode::getArrayDate (const TableExprId& id) const { return node_p->getArrayDate (id).array(); } inline MArray TableExprNode::getBoolAS (const TableExprId& id) const { return node_p->getBoolAS (id); } inline MArray TableExprNode::getIntAS (const TableExprId& id) const { return node_p->getIntAS (id); } inline MArray TableExprNode::getDoubleAS (const TableExprId& id) const { return node_p->getDoubleAS (id); } inline MArray TableExprNode::getDComplexAS (const TableExprId& id) const { return node_p->getDComplexAS (id); } inline MArray TableExprNode::getStringAS (const TableExprId& id) const { return node_p->getStringAS (id); } inline MArray TableExprNode::getDateAS (const TableExprId& id) const { return node_p->getDateAS (id); } inline Array TableExprNode::getColumnBool (const RowNumbers& rownrs) const { return node_p->getColumnBool (rownrs); } inline Array TableExprNode::getColumnuChar (const RowNumbers& rownrs) const { return node_p->getColumnuChar (rownrs); } inline Array TableExprNode::getColumnShort (const RowNumbers& rownrs) const { return node_p->getColumnShort (rownrs); } inline Array TableExprNode::getColumnuShort (const RowNumbers& rownrs) const { return node_p->getColumnuShort (rownrs); } inline Array TableExprNode::getColumnInt (const RowNumbers& rownrs) const { return node_p->getColumnInt (rownrs); } inline Array TableExprNode::getColumnuInt (const RowNumbers& rownrs) const { return node_p->getColumnuInt (rownrs); } inline Array TableExprNode::getColumnInt64 (const RowNumbers& rownrs) const { return node_p->getColumnInt64 (rownrs); } inline Array TableExprNode::getColumnFloat (const RowNumbers& rownrs) const { return node_p->getColumnFloat (rownrs); } inline Array TableExprNode::getColumnDouble (const RowNumbers& rownrs) const { return node_p->getColumnDouble (rownrs); } inline Array TableExprNode::getColumnComplex (const RowNumbers& rownrs) const { return node_p->getColumnComplex (rownrs); } inline Array TableExprNode::getColumnDComplex (const RowNumbers& rownrs) const { return node_p->getColumnDComplex (rownrs); } inline Array TableExprNode::getColumnString (const RowNumbers& rownrs) const { return node_p->getColumnString (rownrs); } inline void TableExprNode::show (ostream& os) const { node_p->show (os, 0); } inline const TENShPtr& TableExprNode::getRep() const { return node_p; } inline const TableExprNodeRep* TableExprNode::getNodeRep() const { return node_p.get(); } // Define all global functions operating on a TableExprNode. // //# Define the operations we allow. //# Note that the arguments are defined as const. This is necessary //# because the compiler generates temporaries when converting a constant //# to a TableExprNode using the constructors. Temporaries has to be const. //# However, we have to delete created nodes, so lnode_p and rnode_p //# cannot be const. The const arguments are casted to a non-const in //# the function fill which calls the non-const function simplify. // Arithmetic operators for numeric TableExprNode's. // // + is also defined for strings (means concatenation). TableExprNode operator+ (const TableExprNode& left, const TableExprNode& right); TableExprNode operator- (const TableExprNode& left, const TableExprNode& right); TableExprNode operator* (const TableExprNode& left, const TableExprNode& right); TableExprNode operator/ (const TableExprNode& left, const TableExprNode& right); TableExprNode operator% (const TableExprNode& left, const TableExprNode& right); TableExprNode operator& (const TableExprNode& left, const TableExprNode& right); TableExprNode operator| (const TableExprNode& left, const TableExprNode& right); TableExprNode operator^ (const TableExprNode& left, const TableExprNode& right); // // Comparison operators. // TableExprNode operator== (const TableExprNode& left, const TableExprNode& right); TableExprNode operator!= (const TableExprNode& left, const TableExprNode& right); // Not defined for Bool. // TableExprNode operator>= (const TableExprNode& left, const TableExprNode& right); TableExprNode operator> (const TableExprNode& left, const TableExprNode& right); TableExprNode operator<= (const TableExprNode& left, const TableExprNode& right); TableExprNode operator< (const TableExprNode& left, const TableExprNode& right); // // // Logical operators to combine boolean TableExprNode's. // A null TableExprNode object is ignored, so it is possible to // build up a full expression gradually. // TableExprNode operator&& (const TableExprNode& left, const TableExprNode& right); TableExprNode operator|| (const TableExprNode& left, const TableExprNode& right); // // Functions to return whether a value is "relatively" near another. // Returns tol > abs(val2 - val1)/max(abs(val1),(val2)). // If tol <= 0, returns val1 == val2. If either val is 0.0, takes // care of area around the minimum number that can be represented. //
        The nearAbs functions return whether a value is "absolutely" near // another. Returns tol > abs(val2 - val1). // Default tolerance is 1.0e-13. // They operate on scalars and arrays. // TableExprNode near (const TableExprNode& left, const TableExprNode& right); TableExprNode near (const TableExprNode& left, const TableExprNode& right, const TableExprNode& tolerance); TableExprNode nearAbs (const TableExprNode& left, const TableExprNode& right); TableExprNode nearAbs (const TableExprNode& left, const TableExprNode& right, const TableExprNode& tolerance); // // Angular distance between positions. // Both arguments have to be arrays. If both arrays contain 2 values // (ra and dec), the result is a scalar. // Otherwise the arrays have to contain a multiple of 2 values and the // result is a 2-dim array giving the distance of each position in the // first array to each position in the second array. TableExprNode angdist (const TableExprNode& pos1, const TableExprNode& pos2); // Angular distance as above, but only pair-wise enties are used if // both arguments are arrays. TableExprNode angdistx (const TableExprNode& pos1, const TableExprNode& pos2); // Cone search; test if the position of a source is inside a cone. //
        Argument sourcePos must be a double array // containing two values (ra and dec of source) in radians. //
        Argument cones must be a double array // specifying the position of the cone centers and radii in radians. // So the array must contain three values (ra,dec,radius) // or a multiple of it. // // The result is a bool array telling for each cone if it contains the // source. If there is only one cone, the result is a scalar. TableExprNode cones (const TableExprNode& sourcePos, const TableExprNode& cones); // The result is always a Bool scalar telling if any cone contains // the source. TableExprNode anyCone (const TableExprNode& sourcePos, const TableExprNode& cones); // The sourcePos can contain multiple sources. // The result is a double array giving the index of the first // cone containing the corresponding source. // If there is one source, the result is a double scalar. TableExprNode findCone (const TableExprNode& sourcePos, const TableExprNode& cones); // // Cone search as above. // However, the cone positions and radii are specified separately // and (virtually) a larger array containing every combination of // position/radius is formed. // TableExprNode cones (const TableExprNode& sourcePos, const TableExprNode& conePos, const TableExprNode& radii); TableExprNode anyCone (const TableExprNode& sourcePos, const TableExprNode& conePos, const TableExprNode& radii); TableExprNode findCone (const TableExprNode& sourcePos, const TableExprNode& conePos, const TableExprNode& radii); // // Transcendental functions that can be applied to essentially all numeric // nodes containing scalars or arrays. // TableExprNode sin (const TableExprNode& node); TableExprNode sinh (const TableExprNode& node); TableExprNode cos (const TableExprNode& node); TableExprNode cosh (const TableExprNode& node); TableExprNode exp (const TableExprNode& node); TableExprNode log (const TableExprNode& node); TableExprNode log10 (const TableExprNode& node); TableExprNode pow (const TableExprNode& x, const TableExprNode& exp); TableExprNode square (const TableExprNode& node); TableExprNode cube (const TableExprNode& node); TableExprNode sqrt (const TableExprNode& node); TableExprNode norm (const TableExprNode& node); // // Transcendental functions applied to to nodes containing scalars or // arrays with double values. // They are invalid for Complex nodes. // TableExprNode asin (const TableExprNode& node); TableExprNode acos (const TableExprNode& node); TableExprNode atan (const TableExprNode& node); TableExprNode atan2 (const TableExprNode& y, const TableExprNode& x); TableExprNode tan (const TableExprNode& node); TableExprNode tanh (const TableExprNode& node); TableExprNode sign (const TableExprNode& node); TableExprNode round (const TableExprNode& node); TableExprNode ceil (const TableExprNode& node); TableExprNode abs (const TableExprNode& node); TableExprNode floor (const TableExprNode& node); TableExprNode fmod (const TableExprNode& x, const TableExprNode& y); // // String functions on scalars or arrays. // TableExprNode strlength (const TableExprNode& node); TableExprNode upcase (const TableExprNode& node); TableExprNode downcase (const TableExprNode& node); TableExprNode capitalize(const TableExprNode& node); TableExprNode trim (const TableExprNode& node); TableExprNode ltrim (const TableExprNode& node); TableExprNode rtrim (const TableExprNode& node); TableExprNode substr (const TableExprNode& str, const TableExprNode& pos); TableExprNode substr (const TableExprNode& str, const TableExprNode& pos, const TableExprNode& npos); TableExprNode replace (const TableExprNode& str, const TableExprNode& patt); TableExprNode replace (const TableExprNode& str, const TableExprNode& patt, const TableExprNode& repl); // // Functions for regular expression matching and // pattern matching. Defined for scalars and arrays. //
        pattern is for a file name like pattern. //
        sqlpattern is for an SQL like pattern. // TableExprNode regex (const TableExprNode& node); TableExprNode pattern (const TableExprNode& node); TableExprNode sqlpattern (const TableExprNode& node); // // Functions for date-values. Defined for scalars and arrays. //# Note, ctod is called ctodt, because Mac OS-X defines a macro //# ctod in param.h // TableExprNode datetime (const TableExprNode& node); TableExprNode mjdtodate (const TableExprNode& node); TableExprNode mjd (const TableExprNode& node); TableExprNode date (const TableExprNode& node); TableExprNode year (const TableExprNode& node); TableExprNode month (const TableExprNode& node); TableExprNode day (const TableExprNode& node); TableExprNode cmonth (const TableExprNode& node); TableExprNode weekday (const TableExprNode& node); TableExprNode cdow (const TableExprNode& node); TableExprNode ctodt (const TableExprNode& node); TableExprNode cdate (const TableExprNode& node); TableExprNode ctime (const TableExprNode& node); TableExprNode week (const TableExprNode& node); TableExprNode time (const TableExprNode& node); // // Functions for angle-values. Defined for scalars and arrays. // dhms converts pairs of values to hms and dms and only works for arrays. // TableExprNode hms (const TableExprNode& node); TableExprNode dms (const TableExprNode& node); TableExprNode hdms (const TableExprNode& node); // // Function to convert any value to a string. // See TaQL note 199 for possible format values. // TableExprNode toString (const TableExprNode& node); TableExprNode toString (const TableExprNode& node, const TableExprNode& format); // // Function to test if a scalar or array is NaN (not-a-number). // It results in a Bool scalar or array. TableExprNode isNaN (const TableExprNode& node); // Function to test if a scalar or array is finite. // It results in a Bool scalar or array. TableExprNode isFinite (const TableExprNode& node); // Minimum or maximum of 2 nodes. // Makes sense for numeric and String values. For Complex values // the norm is compared. // One or both arguments can be scalar or array. // TableExprNode min (const TableExprNode& a, const TableExprNode& b); TableExprNode max (const TableExprNode& a, const TableExprNode& b); // // The complex conjugate of a complex node. // Defined for scalars and arrays. TableExprNode conj (const TableExprNode& node); // The real part of a complex node. // Defined for scalars and arrays. TableExprNode real (const TableExprNode& node); // The imaginary part of a complex node. // Defined for scalars and arrays. TableExprNode imag (const TableExprNode& node); // Convert double, bool, or string to int (using floor). TableExprNode integer (const TableExprNode& node); // Convert numeric or string value to bool (0, no, false, - means false) TableExprNode boolean (const TableExprNode& node); // The amplitude (i.e. sqrt(re*re + im*im)) of a complex node. // This is a synonym for function abs. // Defined for scalars and arrays. TableExprNode amplitude (const TableExprNode& node); // The phase (i.e. atan2(im, re)) of a complex node. // This is a synonym for function arg. // Defined for scalars and arrays. TableExprNode phase (const TableExprNode& node); // The arg (i.e. atan2(im, re)) of a complex node. // Defined for scalars and arrays. TableExprNode arg (const TableExprNode& node); // Form a complex number from two Doubles. // One or both arguments can be scalar or array. TableExprNode formComplex (const TableExprNode& real, const TableExprNode& imag); // Form a complex number from a string. // Defined for scalars and arrays. TableExprNode formComplex (const TableExprNode& node); // Functions operating on a Double or Complex scalar or array resulting in // a scalar with the same data type. // TableExprNode sum (const TableExprNode& array); TableExprNode product (const TableExprNode& array); TableExprNode sumSquare (const TableExprNode& array); // // Functions operating on a Double scalar or array resulting in // a Double scalar. // TableExprNode min (const TableExprNode& array); TableExprNode max (const TableExprNode& array); TableExprNode mean (const TableExprNode& array); TableExprNode variance (const TableExprNode& array); TableExprNode stddev (const TableExprNode& array); TableExprNode avdev (const TableExprNode& array); TableExprNode rms (const TableExprNode& array); TableExprNode median (const TableExprNode& array); TableExprNode fractile (const TableExprNode& array, const TableExprNode& fraction); // // TableExprNode any (const TableExprNode& array); TableExprNode all (const TableExprNode& array); TableExprNode ntrue (const TableExprNode& array); TableExprNode nfalse (const TableExprNode& array); // // The partial version of the functions above. // They are applied to the array subsets defined by the axes in the set // using the partialXXX functions in ArrayMath. // The axes must be 0-relative. // TableExprNode sums (const TableExprNode& array, const TableExprNodeSet& collapseAxes); TableExprNode products (const TableExprNode& array, const TableExprNodeSet& collapseAxes); TableExprNode sumSquares (const TableExprNode& array, const TableExprNodeSet& collapseAxes); TableExprNode mins (const TableExprNode& array, const TableExprNodeSet& collapseAxes); TableExprNode maxs (const TableExprNode& array, const TableExprNodeSet& collapseAxes); TableExprNode means (const TableExprNode& array, const TableExprNodeSet& collapseAxes); TableExprNode variances (const TableExprNode& array, const TableExprNodeSet& collapseAxes); TableExprNode stddevs (const TableExprNode& array, const TableExprNodeSet& collapseAxes); TableExprNode avdevs (const TableExprNode& array, const TableExprNodeSet& collapseAxes); TableExprNode rmss (const TableExprNode& array, const TableExprNodeSet& collapseAxes); TableExprNode medians (const TableExprNode& array, const TableExprNodeSet& collapseAxes); TableExprNode fractiles (const TableExprNode& array, const TableExprNode& fraction, const TableExprNodeSet& collapseAxes); TableExprNode anys (const TableExprNode& array, const TableExprNodeSet& collapseAxes); TableExprNode alls (const TableExprNode& array, const TableExprNodeSet& collapseAxes); TableExprNode ntrues (const TableExprNode& array, const TableExprNodeSet& collapseAxes); TableExprNode nfalses (const TableExprNode& array, const TableExprNodeSet& collapseAxes); // // Functions operating for each element on a box around that element. // The elements at the edges (where no full box can be made) are set to 0. // TableExprNode runningMin (const TableExprNode& array, const TableExprNodeSet& halfBoxWidth); TableExprNode runningMax (const TableExprNode& array, const TableExprNodeSet& halfBoxWidth); TableExprNode runningMean (const TableExprNode& array, const TableExprNodeSet& halfBoxWidth); TableExprNode runningVariance (const TableExprNode& array, const TableExprNodeSet& halfBoxWidth); TableExprNode runningStddev (const TableExprNode& array, const TableExprNodeSet& halfBoxWidth); TableExprNode runningAvdev (const TableExprNode& array, const TableExprNodeSet& halfBoxWidth); TableExprNode runningRms (const TableExprNode& array, const TableExprNodeSet& halfBoxWidth); TableExprNode runningMedian (const TableExprNode& array, const TableExprNodeSet& halfBoxWidth); TableExprNode runningAny (const TableExprNode& array, const TableExprNodeSet& halfBoxWidth); TableExprNode runningAll (const TableExprNode& array, const TableExprNodeSet& halfBoxWidth); // // Create an array of the given shape and fill it with the values. // The values array is rewound as needed. TableExprNode array (const TableExprNode& values, const TableExprNodeSet& shape); // Form a masked array. TableExprNode marray (const TableExprNode& array, const TableExprNode& mask); // Get the data array of a masked array. TableExprNode arrayData (const TableExprNode& array); // Flatten a masked array (get unmasked elements). TableExprNode arrayFlatten (const TableExprNode& array); // Get the mask of a masked array. // If the array has no mask, it return an array with all False values. TableExprNode arrayMask (const TableExprNode& array); // Get the diagonal of a (masked) array; // If the array is not a Matrix, it will take the diagonals of the // subarrays given by the two axes in the axes argument. Those // axes have to have the same length (thus each subarray is a Matrix). // If no axes are given, they default to the first two axes. //
        The diag argument tells which diagonal to take. // 0 is the main diagonal, >0 is above main diagonal, <0 is below. TableExprNode diagonal (const TableExprNode& array); TableExprNode diagonal (const TableExprNode& array, const TableExprNode& firstAxis); TableExprNode diagonal (const TableExprNode& array, const TableExprNode& firstAxis, const TableExprNode& diag); // Transpose all axes of a (masked) array. TableExprNode transpose (const TableExprNode& array); // Transpose a (masked) array by making the given axes the first axes. TableExprNode transpose (const TableExprNode& array, const TableExprNode& axes); // Function operating on a field resulting in a bool scalar. // It can be used to test if a column has an array in the current row. // It can also be used to test if a record contains a field. TableExprNode isdefined (const TableExprNode& array); // Functions operating on any scalar or array resulting in a Double scalar. // A scalar has 1 element and dimensionality 0. // TableExprNode nelements (const TableExprNode& array); TableExprNode ndim (const TableExprNode& array); // // Function operating on any scalar or array resulting in a Double array // containing the shape. A scalar has shape [1]. TableExprNode shape (const TableExprNode& array); // Function resembling the ternary ?: construct in C++. // The argument "condition" has to be a Bool value. // If an element in "condition" is True, the corresponding element from // "arg1" is taken, otherwise it is taken from "arg2". // The arguments can be scalars or array or any combination. TableExprNode iif (const TableExprNode& condition, const TableExprNode& arg1, const TableExprNode& arg2); //
        inline TableExprNode operator+ (const TableExprNode& left, const TableExprNode& right) { return left.newPlus (right.getRep()); } inline TableExprNode operator- (const TableExprNode& left, const TableExprNode& right) { return left.newMinus (right.getRep()); } inline TableExprNode operator* (const TableExprNode& left, const TableExprNode& right) { return left.newTimes (right.getRep()); } inline TableExprNode operator/ (const TableExprNode& left, const TableExprNode& right) { return left.newDivide (right.getRep()); } inline TableExprNode operator% (const TableExprNode& left, const TableExprNode& right) { return left.newModulo (right.getRep()); } inline TableExprNode operator& (const TableExprNode& left, const TableExprNode& right) { return left.newBitAnd (right.getRep()); } inline TableExprNode operator| (const TableExprNode& left, const TableExprNode& right) { return left.newBitOr (right.getRep()); } inline TableExprNode operator^ (const TableExprNode& left, const TableExprNode& right) { return left.newBitXor (right.getRep()); } inline TableExprNode operator== (const TableExprNode& left, const TableExprNode& right) { return left.newEQ (right.getRep()); } inline TableExprNode operator!= (const TableExprNode& left, const TableExprNode& right) { return left.newNE (right.getRep()); } inline TableExprNode operator> (const TableExprNode& left, const TableExprNode& right) { return left.newGT (right.getRep()); } inline TableExprNode operator>= (const TableExprNode& left, const TableExprNode& right) { return left.newGE (right.getRep()); } inline TableExprNode operator<= (const TableExprNode& left, const TableExprNode& right) { return right.newGE (left.getRep()); } inline TableExprNode operator< (const TableExprNode& left, const TableExprNode& right) { return right.newGT (left.getRep()); } inline TableExprNode TableExprNode::in (const TableExprNode& right, const TaQLStyle& style) const { return newIN (right.getRep(), style); } inline TableExprNode TableExprNode::operator() (const TableExprNodeSet& indices) { // C++ indexing is 0-based. return newArrayPartNode (*this, indices, TaQLStyle(0)); } inline TableExprNode near (const TableExprNode& left, const TableExprNode& right) { return TableExprNode::newFunctionNode (TableExprFuncNode::near2FUNC, left, right); } inline TableExprNode near (const TableExprNode& left, const TableExprNode& right, const TableExprNode& tolerance) { return TableExprNode::newFunctionNode (TableExprFuncNode::near3FUNC, left, right, tolerance); } inline TableExprNode nearAbs (const TableExprNode& left, const TableExprNode& right) { return TableExprNode::newFunctionNode (TableExprFuncNode::nearabs2FUNC, left, right); } inline TableExprNode nearAbs (const TableExprNode& left, const TableExprNode& right, const TableExprNode& tolerance) { return TableExprNode::newFunctionNode (TableExprFuncNode::nearabs3FUNC, left, right, tolerance); } inline TableExprNode angdist (const TableExprNode& pos1, const TableExprNode& pos2) { return TableExprNode::newFunctionNode (TableExprFuncNode::angdistFUNC, pos1, pos2); } inline TableExprNode angdistx (const TableExprNode& pos1, const TableExprNode& pos2) { return TableExprNode::newFunctionNode (TableExprFuncNode::angdistxFUNC, pos1, pos2); } inline TableExprNode cones (const TableExprNode& sourcePos, const TableExprNode& cones) { return TableExprNode::newConeNode (TableExprFuncNode::conesFUNC, sourcePos, cones); } inline TableExprNode anyCone (const TableExprNode& sourcePos, const TableExprNode& cones) { return TableExprNode::newConeNode (TableExprFuncNode::anyconeFUNC, sourcePos, cones); } inline TableExprNode findCone (const TableExprNode& sourcePos, const TableExprNode& cones) { return TableExprNode::newConeNode (TableExprFuncNode::findconeFUNC, sourcePos, cones); } inline TableExprNode cones (const TableExprNode& sourcePos, const TableExprNode& conePos, const TableExprNode& radii) { return TableExprNode::newConeNode (TableExprFuncNode::cones3FUNC, sourcePos, conePos, radii); } inline TableExprNode anyCone (const TableExprNode& sourcePos, const TableExprNode& conePos, const TableExprNode& radii) { return TableExprNode::newConeNode (TableExprFuncNode::anycone3FUNC, sourcePos, conePos, radii); } inline TableExprNode findCone (const TableExprNode& sourcePos, const TableExprNode& conePos, const TableExprNode& radii) { return TableExprNode::newConeNode (TableExprFuncNode::findcone3FUNC, sourcePos, conePos, radii); } inline TableExprNode cos (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::cosFUNC, node); } inline TableExprNode cosh (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::coshFUNC, node); } inline TableExprNode exp (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::expFUNC, node); } inline TableExprNode log (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::logFUNC, node); } inline TableExprNode log10 (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::log10FUNC, node); } inline TableExprNode pow (const TableExprNode& x, const TableExprNode& y) { return TableExprNode::newFunctionNode (TableExprFuncNode::powFUNC, x, y); } inline TableExprNode sin (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::sinFUNC, node); } inline TableExprNode sinh (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::sinhFUNC, node); } inline TableExprNode square (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::squareFUNC, node); } inline TableExprNode cube (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::cubeFUNC, node); } inline TableExprNode sqrt (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::sqrtFUNC, node); } inline TableExprNode norm (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::normFUNC, node); } inline TableExprNode acos (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::acosFUNC, node); } inline TableExprNode asin (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::asinFUNC, node); } inline TableExprNode atan (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::atanFUNC, node); } inline TableExprNode atan2 (const TableExprNode& y, const TableExprNode& x) { return TableExprNode::newFunctionNode (TableExprFuncNode::atan2FUNC, y, x); } inline TableExprNode sign (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::signFUNC, node); } inline TableExprNode round (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::roundFUNC, node); } inline TableExprNode ceil (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::ceilFUNC, node); } inline TableExprNode abs (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::absFUNC, node); } inline TableExprNode floor (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::floorFUNC, node); } inline TableExprNode fmod (const TableExprNode& x, const TableExprNode& y) { return TableExprNode::newFunctionNode (TableExprFuncNode::fmodFUNC, x, y); } inline TableExprNode tan (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::tanFUNC, node); } inline TableExprNode tanh (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::tanhFUNC, node); } inline TableExprNode min (const TableExprNode& a, const TableExprNode& b) { return TableExprNode::newFunctionNode (TableExprFuncNode::minFUNC, a, b); } inline TableExprNode max (const TableExprNode& a, const TableExprNode& b) { return TableExprNode::newFunctionNode (TableExprFuncNode::maxFUNC, a, b); } inline TableExprNode real (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::realFUNC, node); } inline TableExprNode imag (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::imagFUNC, node); } inline TableExprNode integer (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::intFUNC, node); } inline TableExprNode boolean (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::boolFUNC, node); } inline TableExprNode conj (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::conjFUNC, node); } inline TableExprNode amplitude (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::absFUNC, node); } inline TableExprNode arg (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::argFUNC, node); } inline TableExprNode phase (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::argFUNC, node); } inline TableExprNode formComplex (const TableExprNode& real, const TableExprNode& imag) { return TableExprNode::newFunctionNode (TableExprFuncNode::complexFUNC, real, imag); } inline TableExprNode formComplex (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::complexFUNC, node); } inline TableExprNode strlength (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::strlengthFUNC, node); } inline TableExprNode upcase (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::upcaseFUNC, node); } inline TableExprNode downcase (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::downcaseFUNC, node); } inline TableExprNode capitalize (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::capitalizeFUNC, node); } inline TableExprNode regex (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::regexFUNC, node); } inline TableExprNode pattern (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::patternFUNC, node); } inline TableExprNode sqlpattern (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::sqlpatternFUNC, node); } inline TableExprNode datetime (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::datetimeFUNC, node); } inline TableExprNode mjdtodate (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::mjdtodateFUNC, node); } inline TableExprNode mjd (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::mjdFUNC, node); } inline TableExprNode date (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::dateFUNC, node); } inline TableExprNode year (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::yearFUNC, node); } inline TableExprNode month (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::monthFUNC, node); } inline TableExprNode day (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::dayFUNC, node); } inline TableExprNode cmonth (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::cmonthFUNC, node); } inline TableExprNode weekday (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::weekdayFUNC, node); } inline TableExprNode cdow (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::cdowFUNC, node); } inline TableExprNode ctodt (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::ctodFUNC, node); } inline TableExprNode cdate (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::cdateFUNC, node); } inline TableExprNode ctime (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::ctimeFUNC, node); } inline TableExprNode hms (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::hmsFUNC, node); } inline TableExprNode dms (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::dmsFUNC, node); } inline TableExprNode hdms (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::hdmsFUNC, node); } inline TableExprNode toString (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::stringFUNC, node); } inline TableExprNode toString (const TableExprNode& node, const TableExprNode& format) { return TableExprNode::newFunctionNode (TableExprFuncNode::stringFUNC, node, format); } inline TableExprNode week (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::weekFUNC, node); } inline TableExprNode time (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::timeFUNC, node); } inline TableExprNode trim (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::trimFUNC, node); } inline TableExprNode ltrim (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::ltrimFUNC, node); } inline TableExprNode rtrim (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::rtrimFUNC, node); } inline TableExprNode substr (const TableExprNode& node, const TableExprNode& pos) { return TableExprNode::newFunctionNode (TableExprFuncNode::substrFUNC, node, pos); } inline TableExprNode substr (const TableExprNode& node, const TableExprNode& pos, const TableExprNode& npos) { return TableExprNode::newFunctionNode (TableExprFuncNode::substrFUNC, node, pos, npos); } inline TableExprNode replace (const TableExprNode& node, const TableExprNode& patt) { return TableExprNode::newFunctionNode (TableExprFuncNode::replaceFUNC, node, patt); } inline TableExprNode replace (const TableExprNode& node, const TableExprNode& patt, const TableExprNode& repl) { return TableExprNode::newFunctionNode (TableExprFuncNode::replaceFUNC, node, patt, repl); } inline TableExprNode isNaN (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::isnanFUNC, node); } inline TableExprNode isInf (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::isinfFUNC, node); } inline TableExprNode isFinite (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::isfiniteFUNC, node); } inline TableExprNode min (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrminFUNC, node); } inline TableExprNode max (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrmaxFUNC, node); } inline TableExprNode sum (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrsumFUNC, node); } inline TableExprNode product (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrproductFUNC, node); } inline TableExprNode sumSquare (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrsumsqrFUNC, node); } inline TableExprNode mean (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrmeanFUNC, node); } inline TableExprNode variance (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrvariance1FUNC, node); } inline TableExprNode stddev (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrstddev1FUNC, node); } inline TableExprNode avdev (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::arravdevFUNC, node); } inline TableExprNode rms (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrrmsFUNC, node); } inline TableExprNode median (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrmedianFUNC, node); } inline TableExprNode fractile (const TableExprNode& node, const TableExprNode& fraction) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrfractileFUNC, node, fraction); } inline TableExprNode any (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::arranyFUNC, node); } inline TableExprNode all (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrallFUNC, node); } inline TableExprNode ntrue (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrntrueFUNC, node); } inline TableExprNode nfalse (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrnfalseFUNC, node); } inline TableExprNode sums (const TableExprNode& array, const TableExprNodeSet& axes) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrsumsFUNC, array, axes); } inline TableExprNode products (const TableExprNode& array, const TableExprNodeSet& axes) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrproductsFUNC, array, axes); } inline TableExprNode sumSquares (const TableExprNode& array, const TableExprNodeSet& axes) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrsumsqrsFUNC, array, axes); } inline TableExprNode mins (const TableExprNode& array, const TableExprNodeSet& axes) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrminsFUNC, array, axes); } inline TableExprNode maxs (const TableExprNode& array, const TableExprNodeSet& axes) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrmaxsFUNC, array, axes); } inline TableExprNode means (const TableExprNode& array, const TableExprNodeSet& axes) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrmeansFUNC, array, axes); } inline TableExprNode variances (const TableExprNode& array, const TableExprNodeSet& axes) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrvariances1FUNC, array, axes); } inline TableExprNode stddevs (const TableExprNode& array, const TableExprNodeSet& axes) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrstddevs1FUNC, array, axes); } inline TableExprNode avdevs (const TableExprNode& array, const TableExprNodeSet& axes) { return TableExprNode::newFunctionNode (TableExprFuncNode::arravdevsFUNC, array, axes); } inline TableExprNode rmss (const TableExprNode& array, const TableExprNodeSet& axes) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrrmssFUNC, array, axes); } inline TableExprNode medians (const TableExprNode& array, const TableExprNodeSet& axes) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrmediansFUNC, array, axes); } inline TableExprNode fractiles (const TableExprNode& array, const TableExprNode& fraction, const TableExprNodeSet& axes) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrfractilesFUNC, array, fraction, axes); } inline TableExprNode anys (const TableExprNode& array, const TableExprNodeSet& axes) { return TableExprNode::newFunctionNode (TableExprFuncNode::arranysFUNC, array, axes); } inline TableExprNode alls (const TableExprNode& array, const TableExprNodeSet& axes) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrallsFUNC, array, axes); } inline TableExprNode ntrues (const TableExprNode& array, const TableExprNodeSet& axes) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrntruesFUNC, array, axes); } inline TableExprNode nfalses (const TableExprNode& array, const TableExprNodeSet& axes) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrnfalsesFUNC, array, axes); } inline TableExprNode runningMin (const TableExprNode& node, const TableExprNodeSet& halfBoxWidth) { return TableExprNode::newFunctionNode (TableExprFuncNode::runminFUNC, node, halfBoxWidth); } inline TableExprNode runningMax (const TableExprNode& node, const TableExprNodeSet& halfBoxWidth) { return TableExprNode::newFunctionNode (TableExprFuncNode::runmaxFUNC, node, halfBoxWidth); } inline TableExprNode runningMean (const TableExprNode& node, const TableExprNodeSet& halfBoxWidth) { return TableExprNode::newFunctionNode (TableExprFuncNode::runmeanFUNC, node, halfBoxWidth); } inline TableExprNode runningVariance (const TableExprNode& node, const TableExprNodeSet& halfBoxWidth) { return TableExprNode::newFunctionNode (TableExprFuncNode::runvariance1FUNC, node, halfBoxWidth); } inline TableExprNode runningStddev (const TableExprNode& node, const TableExprNodeSet& halfBoxWidth) { return TableExprNode::newFunctionNode (TableExprFuncNode::runstddev1FUNC, node, halfBoxWidth); } inline TableExprNode runningAvdev (const TableExprNode& node, const TableExprNodeSet& halfBoxWidth) { return TableExprNode::newFunctionNode (TableExprFuncNode::runavdevFUNC, node, halfBoxWidth); } inline TableExprNode runningRms (const TableExprNode& node, const TableExprNodeSet& halfBoxWidth) { return TableExprNode::newFunctionNode (TableExprFuncNode::runrmsFUNC, node, halfBoxWidth); } inline TableExprNode runningMedian (const TableExprNode& node, const TableExprNodeSet& halfBoxWidth) { return TableExprNode::newFunctionNode (TableExprFuncNode::runmedianFUNC, node, halfBoxWidth); } inline TableExprNode runningAny (const TableExprNode& node, const TableExprNodeSet& halfBoxWidth) { return TableExprNode::newFunctionNode (TableExprFuncNode::runanyFUNC, node, halfBoxWidth); } inline TableExprNode runningAll (const TableExprNode& node, const TableExprNodeSet& halfBoxWidth) { return TableExprNode::newFunctionNode (TableExprFuncNode::runallFUNC, node, halfBoxWidth); } inline TableExprNode boxedMin (const TableExprNode& node, const TableExprNodeSet& halfBoxWidth) { return TableExprNode::newFunctionNode (TableExprFuncNode::boxminFUNC, node, halfBoxWidth); } inline TableExprNode boxedMax (const TableExprNode& node, const TableExprNodeSet& halfBoxWidth) { return TableExprNode::newFunctionNode (TableExprFuncNode::boxmaxFUNC, node, halfBoxWidth); } inline TableExprNode boxedMean (const TableExprNode& node, const TableExprNodeSet& halfBoxWidth) { return TableExprNode::newFunctionNode (TableExprFuncNode::boxmeanFUNC, node, halfBoxWidth); } inline TableExprNode boxedVariance (const TableExprNode& node, const TableExprNodeSet& halfBoxWidth) { return TableExprNode::newFunctionNode (TableExprFuncNode::boxvariance1FUNC, node, halfBoxWidth); } inline TableExprNode boxedStddev (const TableExprNode& node, const TableExprNodeSet& halfBoxWidth) { return TableExprNode::newFunctionNode (TableExprFuncNode::boxstddev1FUNC, node, halfBoxWidth); } inline TableExprNode boxedAvdev (const TableExprNode& node, const TableExprNodeSet& halfBoxWidth) { return TableExprNode::newFunctionNode (TableExprFuncNode::boxavdevFUNC, node, halfBoxWidth); } inline TableExprNode boxedRms (const TableExprNode& node, const TableExprNodeSet& halfBoxWidth) { return TableExprNode::newFunctionNode (TableExprFuncNode::boxrmsFUNC, node, halfBoxWidth); } inline TableExprNode boxedMedian (const TableExprNode& node, const TableExprNodeSet& halfBoxWidth) { return TableExprNode::newFunctionNode (TableExprFuncNode::boxmedianFUNC, node, halfBoxWidth); } inline TableExprNode boxedAny (const TableExprNode& node, const TableExprNodeSet& halfBoxWidth) { return TableExprNode::newFunctionNode (TableExprFuncNode::boxanyFUNC, node, halfBoxWidth); } inline TableExprNode boxedAll (const TableExprNode& node, const TableExprNodeSet& halfBoxWidth) { return TableExprNode::newFunctionNode (TableExprFuncNode::boxallFUNC, node, halfBoxWidth); } inline TableExprNode array (const TableExprNode& values, const TableExprNodeSet& shape) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrayFUNC, values, shape); } inline TableExprNode marray (const TableExprNode& array, const TableExprNode& mask) { return TableExprNode::newFunctionNode (TableExprFuncNode::marrayFUNC, array, mask); } inline TableExprNode arrayData (const TableExprNode& array) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrdataFUNC, array); } inline TableExprNode arrayMask (const TableExprNode& array) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrmaskFUNC, array); } inline TableExprNode arrayFlatten (const TableExprNode& array) { return TableExprNode::newFunctionNode (TableExprFuncNode::arrflatFUNC, array); } inline TableExprNode transpose (const TableExprNode& array) { // Needs an empty axes argument. return TableExprNode::newFunctionNode (TableExprFuncNode::transposeFUNC, array, TableExprNode(Vector())); } inline TableExprNode transpose (const TableExprNode& array, const TableExprNodeSet& axes) { return TableExprNode::newFunctionNode (TableExprFuncNode::transposeFUNC, array, axes); } inline TableExprNode diagonal (const TableExprNode& array) { return TableExprNode::newFunctionNode (TableExprFuncNode::diagonalFUNC, array, TableExprNode(Vector())); } inline TableExprNode isdefined (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::isdefFUNC, node); } inline TableExprNode nelements (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::nelemFUNC, node); } inline TableExprNode ndim (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::ndimFUNC, node); } inline TableExprNode shape (const TableExprNode& node) { return TableExprNode::newFunctionNode (TableExprFuncNode::shapeFUNC, node); } inline TableExprNode iif (const TableExprNode& condition, const TableExprNode& arg1, const TableExprNode& arg2) { return TableExprNode::newFunctionNode (TableExprFuncNode::iifFUNC, condition, arg1, arg2); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/ExprNodeArray.cc000066400000000000000000001514631476623553700204430ustar00rootroot00000000000000//# ExprNodeArray.cc: Classes representing an array in table select expression //# Copyright (C) 1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableExprNodeArray::TableExprNodeArray (NodeDataType dtype, OperType otype) : TableExprNodeBinary (dtype, VTArray, otype, Constant) { ndim_p = -1; } TableExprNodeArray::TableExprNodeArray (const TableExprNodeRep& node, NodeDataType dtype, OperType otype) : TableExprNodeBinary (dtype, node, otype) {} TableExprNodeArray::TableExprNodeArray (NodeDataType dtype, OperType otype, const IPosition& shape) : TableExprNodeBinary (dtype, VTArray, otype, Constant) { shape_p = shape; ndim_p = shape.size(); if (ndim_p == 0) { ndim_p = -1; } } TENShPtr TableExprNodeArray::makeConstantScalar() { if (isConstant()) { switch (dataType()) { case NTBool: { MArray arr = getArrayBool(0); if (arr.size() == 1) { return TENShPtr(new TableExprNodeConstBool (arr.array().data()[0])); } } break; case NTInt: { MArray arr = getArrayInt(0); if (arr.size() == 1) { return TENShPtr(new TableExprNodeConstInt (arr.array().data()[0])); } } break; case NTDouble: { MArray arr = getArrayDouble(0); if (arr.size() == 1) { return TENShPtr(new TableExprNodeConstDouble (arr.array().data()[0])); } } break; case NTComplex: { MArray arr = getArrayDComplex(0); if (arr.size() == 1) { return TENShPtr(new TableExprNodeConstDComplex (arr.array().data()[0])); } } break; case NTString: { MArray arr = getArrayString(0); if (arr.size() == 1) { return TENShPtr(new TableExprNodeConstString (arr.array().data()[0])); } } break; case NTDate: { MArray arr = getArrayDate(0); if (arr.size() == 1) { return TENShPtr(new TableExprNodeConstDate (arr.array().data()[0])); } } break; default: break; } } return 0; } IPosition TableExprNodeArray::validateIndex (const IPosition& index, const ArrayBase& arr) const { if (index.size() != arr.ndim()) { throw TableInvExpr("index size does not match the array dimensionality"); } IPosition inx(index); for (uInt i=0; i TableExprNodeArray::getArrayDouble (const TableExprId& id) { MArray arr = getArrayInt (id); Array result (arr.shape()); convertArray (result, arr.array()); return MArray (result, arr.mask()); } MArray TableExprNodeArray::getArrayDComplex (const TableExprId& id) { MArray arr = getArrayDouble (id); Array result (arr.shape()); convertArray (result, arr.array()); return MArray (result, arr.mask()); } Bool TableExprNodeArray::contains (const TableExprId& id, Bool value) { return anyEQ (value, getArrayBool (id)); } Bool TableExprNodeArray::contains (const TableExprId& id, Int64 value) { return anyEQ (value, getArrayInt (id)); } Bool TableExprNodeArray::contains (const TableExprId& id, Double value) { return anyEQ (value, getArrayDouble (id)); } Bool TableExprNodeArray::contains (const TableExprId& id, DComplex value) { return anyEQ (value, getArrayDComplex (id)); } Bool TableExprNodeArray::contains (const TableExprId& id, String value) { return anyEQ (value, getArrayString (id)); } Bool TableExprNodeArray::contains (const TableExprId& id, MVTime value) { return anyEQ (value, getArrayDate (id)); } MArray TableExprNodeArray::contains (const TableExprId& id, const MArray& value) { MArray set = getArrayBool (id); Array result(value.shape()); Bool deleteIn, deleteOut; const Bool* in = value.array().getStorage (deleteIn); Bool* out = result.getStorage (deleteOut); size_t nval = value.size(); for (size_t i=0; i (result, value.mask()); } MArray TableExprNodeArray::contains (const TableExprId& id, const MArray& value) { MArray set = getArrayInt (id); Array result(value.shape()); Bool deleteIn, deleteOut; const Int64* in = value.array().getStorage (deleteIn); Bool* out = result.getStorage (deleteOut); size_t nval = value.size(); for (size_t i=0; i (result, value.mask()); } MArray TableExprNodeArray::contains (const TableExprId& id, const MArray& value) { MArray set = getArrayDouble (id); Array result(value.shape()); Bool deleteIn, deleteOut; const Double* in = value.array().getStorage (deleteIn); Bool* out = result.getStorage (deleteOut); size_t nval = value.size(); for (size_t i=0; i (result, value.mask()); } MArray TableExprNodeArray::contains (const TableExprId& id, const MArray& value) { MArray set = getArrayDComplex (id); Array result(value.shape()); Bool deleteIn, deleteOut; const DComplex* in = value.array().getStorage (deleteIn); Bool* out = result.getStorage (deleteOut); size_t nval = value.size(); for (size_t i=0; i (result, value.mask()); } MArray TableExprNodeArray::contains (const TableExprId& id, const MArray& value) { MArray set = getArrayString (id); Array result(value.shape()); Bool deleteIn, deleteOut; const String* in = value.array().getStorage (deleteIn); Bool* out = result.getStorage (deleteOut); size_t nval = value.size(); for (size_t i=0; i (result, value.mask()); } MArray TableExprNodeArray::contains (const TableExprId& id, const MArray& value) { MArray set = getArrayDate (id); Array result(value.shape()); Bool deleteIn, deleteOut; const MVTime* in = value.array().getStorage (deleteIn); Bool* out = result.getStorage (deleteOut); size_t nval = value.size(); for (size_t i=0; i (result, value.mask()); } Bool TableExprNodeArray::getElemBool (const TableExprId& id, const Slicer& slicer) { MArray arr = getArrayBool (id); return arr.array()(validateIndex(slicer.start(), arr.array())); } Int64 TableExprNodeArray::getElemInt (const TableExprId& id, const Slicer& slicer) { MArray arr = getArrayInt (id); return arr.array()(validateIndex(slicer.start(), arr.array())); } Double TableExprNodeArray::getElemDouble (const TableExprId& id, const Slicer& slicer) { MArray arr = getArrayDouble (id); return arr.array()(validateIndex(slicer.start(), arr.array())); } DComplex TableExprNodeArray::getElemDComplex (const TableExprId& id, const Slicer& slicer) { MArray arr = getArrayDComplex (id); return arr.array()(validateIndex(slicer.start(), arr.array())); } String TableExprNodeArray::getElemString (const TableExprId& id, const Slicer& slicer) { MArray arr = getArrayString (id); return arr.array()(validateIndex(slicer.start(), arr.array())); } MVTime TableExprNodeArray::getElemDate (const TableExprId& id, const Slicer& slicer) { MArray arr = getArrayDate (id); return arr.array()(validateIndex(slicer.start(), arr.array())); } MArray TableExprNodeArray::getSliceBool (const TableExprId& id, const Slicer& slicer) { MArray arr = getArrayBool (id); if (arr.isNull()) { return arr; } IPosition start, end, incr; slicer.inferShapeFromSource (arr.array().shape(), start, end, incr); return arr(start, end, incr); } MArray TableExprNodeArray::getSliceInt (const TableExprId& id, const Slicer& slicer) { MArray arr = getArrayInt (id); if (arr.isNull()) { return arr; } IPosition start, end, incr; slicer.inferShapeFromSource (arr.shape(), start, end, incr); return arr(start, end, incr); } MArray TableExprNodeArray::getSliceDouble (const TableExprId& id, const Slicer& slicer) { MArray arr = getArrayDouble (id); if (arr.isNull()) { return arr; } IPosition start, end, incr; slicer.inferShapeFromSource (arr.array().shape(), start, end, incr); return arr(start, end, incr); } MArray TableExprNodeArray::getSliceDComplex (const TableExprId& id, const Slicer& slicer) { MArray arr = getArrayDComplex (id); if (arr.isNull()) { return arr; } IPosition start, end, incr; slicer.inferShapeFromSource (arr.array().shape(), start, end, incr); return arr(start, end, incr); } MArray TableExprNodeArray::getSliceString (const TableExprId& id, const Slicer& slicer) { MArray arr = getArrayString (id); if (arr.isNull()) { return arr; } IPosition start, end, incr; slicer.inferShapeFromSource (arr.array().shape(), start, end, incr); return arr(start, end, incr); } MArray TableExprNodeArray::getSliceDate (const TableExprId& id, const Slicer& slicer) { MArray arr = getArrayDate (id); if (arr.isNull()) { return arr; } IPosition start, end, incr; slicer.inferShapeFromSource (arr.array().shape(), start, end, incr); return arr(start, end, incr); } Array TableExprNodeArray::getElemColumnBool (const Vector&, const Slicer&) { TableExprNode::throwInvDT ("(getElemColumnBool(Slicer) not implemented)"); return Array(); } Array TableExprNodeArray::getElemColumnuChar (const Vector&, const Slicer&) { TableExprNode::throwInvDT ("(getElemColumnuChar(Slicer) not implemented)"); return Array(); } Array TableExprNodeArray::getElemColumnShort (const Vector&, const Slicer&) { TableExprNode::throwInvDT ("(getElemColumnShort(Slicer) not implemented)"); return Array(); } Array TableExprNodeArray::getElemColumnuShort (const Vector&, const Slicer&) { TableExprNode::throwInvDT ("(getElemColumnuShort(Slicer) not implemented)"); return Array(); } Array TableExprNodeArray::getElemColumnInt (const Vector&, const Slicer&) { TableExprNode::throwInvDT ("(getElemColumnInt(Slicer) not implemented)"); return Array(); } Array TableExprNodeArray::getElemColumnuInt (const Vector&, const Slicer&) { TableExprNode::throwInvDT ("(getElemColumnuInt(Slicer) not implemented)"); return Array(); } Array TableExprNodeArray::getElemColumnInt64 (const Vector&, const Slicer&) { TableExprNode::throwInvDT ("(getElemColumnInt64(Slicer) not implemented)"); return Array(); } Array TableExprNodeArray::getElemColumnFloat (const Vector&, const Slicer&) { TableExprNode::throwInvDT ("(getElemColumnFloat(Slicer) not implemented)"); return Array(); } Array TableExprNodeArray::getElemColumnDouble (const Vector&, const Slicer&) { TableExprNode::throwInvDT ("(getElemColumnDouble(Slicer) not implemented)"); return Array(); } Array TableExprNodeArray::getElemColumnComplex (const Vector&, const Slicer&) { TableExprNode::throwInvDT ("(getElemColumnComplex(Slicer) not implemented)"); return Array(); } Array TableExprNodeArray::getElemColumnDComplex (const Vector&, const Slicer&) { TableExprNode::throwInvDT ("(getElemColumnDComplex(Slicer) not implemented)"); return Array(); } Array TableExprNodeArray::getElemColumnString (const Vector&, const Slicer&) { TableExprNode::throwInvDT ("(getElemColumnString(Slicer) not implemented)"); return Array(); } MArray TableExprNodeArray::makeArray (const IPosition& shape, Int64 value) { Array arr(shape); arr.set (value); return MArray(arr); } MArray TableExprNodeArray::makeArray (const IPosition& shape, Double value) { Array arr(shape); arr.set (value); return MArray(arr); } MArray TableExprNodeArray::makeArray (const IPosition& shape, const DComplex& value) { Array arr(shape); arr.set (value); return MArray(arr); } // ---------------------------------- // TableExprNodeArrayColumn functions // ---------------------------------- TableExprNodeArrayColumn::TableExprNodeArrayColumn (const TableColumn& tablecol, const TableExprInfo& tableInfo) : TableExprNodeArray (NTNumeric, OtColumn), tableInfo_p (tableInfo), tabCol_p (tablecol), applySelection_p (True) { //# Fill in the real data type and the base table pointer. switch (tabCol_p.columnDesc().dataType()) { case TpBool: dtype_p = NTBool; break; case TpString: dtype_p = NTString; break; case TpComplex: case TpDComplex: dtype_p = NTComplex; break; case TpChar: case TpUChar: case TpShort: case TpUShort: case TpInt: case TpUInt: case TpInt64: dtype_p = NTInt; break; case TpFloat: case TpDouble: dtype_p = NTDouble; break; default: throw (TableInvExpr (tabCol_p.columnDesc().name(), "unknown data type")); } exprtype_p = Variable; // Set the fixed shape and dimensionality (if known). ndim_p = tabCol_p.ndimColumn(); if (ndim_p == 0) { ndim_p = -1; // unknown dimensionality } shape_p = tabCol_p.shapeColumn(); setUnit (TableExprNodeColumn::getColumnUnit(tabCol_p)); } TableExprInfo TableExprNodeArrayColumn::getTableInfo() const { return tableInfo_p; } void TableExprNodeArrayColumn::disableApplySelection() { applySelection_p = False; } void TableExprNodeArrayColumn::applySelection (const Vector& rownrs) { if (applySelection_p) { // Attach the column to the selection of the table. // Get column name before doing selection!!!! String name = tabCol_p.columnDesc().name(); tableInfo_p.apply (rownrs); tabCol_p = TableColumn(tableInfo_p.table(), name); // Reset switch, because the column object can be used multiple times. // when a select expression is used as e.g. sort key. applySelection_p = False; } } const IPosition& TableExprNodeArrayColumn::getShape (const TableExprId& id) { varShape_p.resize (0); if (tabCol_p.isDefined (id.rownr())) { varShape_p = tabCol_p.shape (id.rownr()); } return varShape_p; } Bool TableExprNodeArrayColumn::isDefined (const TableExprId& id) { return tabCol_p.isDefined (id.rownr()); } Bool TableExprNodeArrayColumn::getColumnDataType (DataType& dt) const { dt = tabCol_p.columnDesc().dataType(); return True; } TableExprNodeArrayColumnBool::TableExprNodeArrayColumnBool (const TableColumn& col, const TableExprInfo& tableInfo) : TableExprNodeArrayColumn (col, tableInfo), col_p (col) {} void TableExprNodeArrayColumnBool::applySelection (const Vector& rownrs) { TableExprNodeArrayColumn::applySelection (rownrs); col_p = ArrayColumn(tabCol_p); } Bool TableExprNodeArrayColumnBool::getElemBool (const TableExprId& id, const Slicer& index) { Array arr = col_p.getSlice (id.rownr(), index); return *arr.data(); } MArray TableExprNodeArrayColumnBool::getArrayBool (const TableExprId& id) { if (tabCol_p.isDefined (id.rownr())) { return MArray (col_p(id.rownr())); } return MArray(); } MArray TableExprNodeArrayColumnBool::getSliceBool (const TableExprId& id, const Slicer& index) { if (tabCol_p.isDefined (id.rownr())) { return MArray (col_p.getSlice (id.rownr(), index)); } return MArray(); } Array TableExprNodeArrayColumnBool::getElemColumnBool (const Vector& rownrs, const Slicer& index) { return col_p.getColumnCells (rownrs, index); } TableExprNodeArrayColumnuChar::TableExprNodeArrayColumnuChar (const TableColumn& col, const TableExprInfo& tableInfo) : TableExprNodeArrayColumn (col, tableInfo), col_p (col) {} void TableExprNodeArrayColumnuChar::applySelection (const Vector& rownrs) { TableExprNodeArrayColumn::applySelection (rownrs); col_p = ArrayColumn(tabCol_p); } Int64 TableExprNodeArrayColumnuChar::getElemInt (const TableExprId& id, const Slicer& index) { Array arr = col_p.getSlice (id.rownr(), index); return *arr.data(); } MArray TableExprNodeArrayColumnuChar::getArrayInt (const TableExprId& id) { if (tabCol_p.isDefined (id.rownr())) { Array arr = col_p (id.rownr()); Array out (arr.shape()); convertArray (out, arr); return MArray (out); } return MArray(); } MArray TableExprNodeArrayColumnuChar::getSliceInt (const TableExprId& id, const Slicer& index) { if (tabCol_p.isDefined (id.rownr())) { Array arr = col_p.getSlice (id.rownr(), index); Array out (arr.shape()); convertArray (out, arr); return MArray (out); } return MArray(); } Array TableExprNodeArrayColumnuChar::getElemColumnuChar (const Vector& rownrs, const Slicer& index) { return col_p.getColumnCells (rownrs, index); } TableExprNodeArrayColumnShort::TableExprNodeArrayColumnShort (const TableColumn& col, const TableExprInfo& tableInfo) : TableExprNodeArrayColumn (col, tableInfo), col_p (col) {} void TableExprNodeArrayColumnShort::applySelection (const Vector& rownrs) { TableExprNodeArrayColumn::applySelection (rownrs); col_p = ArrayColumn(tabCol_p); } Int64 TableExprNodeArrayColumnShort::getElemInt (const TableExprId& id, const Slicer& index) { Array arr = col_p.getSlice (id.rownr(), index); return *arr.data(); } MArray TableExprNodeArrayColumnShort::getArrayInt (const TableExprId& id) { if (tabCol_p.isDefined (id.rownr())) { Array arr = col_p (id.rownr()); Array out (arr.shape()); convertArray (out, arr); return MArray (out); } return MArray(); } MArray TableExprNodeArrayColumnShort::getSliceInt (const TableExprId& id, const Slicer& index) { if (tabCol_p.isDefined (id.rownr())) { Array arr = col_p.getSlice (id.rownr(), index); Array out (arr.shape()); convertArray (out, arr); return MArray (out); } return MArray(); } Array TableExprNodeArrayColumnShort::getElemColumnShort (const Vector& rownrs, const Slicer& index) { return col_p.getColumnCells (rownrs, index); } TableExprNodeArrayColumnuShort::TableExprNodeArrayColumnuShort (const TableColumn& col, const TableExprInfo& tableInfo) : TableExprNodeArrayColumn (col, tableInfo), col_p (col) {} void TableExprNodeArrayColumnuShort::applySelection (const Vector& rownrs) { TableExprNodeArrayColumn::applySelection (rownrs); col_p = ArrayColumn(tabCol_p); } Int64 TableExprNodeArrayColumnuShort::getElemInt (const TableExprId& id, const Slicer& index) { Array arr = col_p.getSlice (id.rownr(), index); return *arr.data(); } MArray TableExprNodeArrayColumnuShort::getArrayInt (const TableExprId& id) { if (tabCol_p.isDefined (id.rownr())) { Array arr = col_p (id.rownr()); Array out (arr.shape()); convertArray (out, arr); return MArray (out); } return MArray(); } MArray TableExprNodeArrayColumnuShort::getSliceInt (const TableExprId& id, const Slicer& index) { if (tabCol_p.isDefined (id.rownr())) { Array arr = col_p.getSlice (id.rownr(), index); Array out (arr.shape()); convertArray (out, arr); return MArray (out); } return MArray(); } Array TableExprNodeArrayColumnuShort::getElemColumnuShort (const Vector& rownrs, const Slicer& index) { return col_p.getColumnCells (rownrs, index); } TableExprNodeArrayColumnInt::TableExprNodeArrayColumnInt (const TableColumn& col, const TableExprInfo& tableInfo) : TableExprNodeArrayColumn (col, tableInfo), col_p (col) {} void TableExprNodeArrayColumnInt::applySelection (const Vector& rownrs) { TableExprNodeArrayColumn::applySelection (rownrs); col_p = ArrayColumn(tabCol_p); } Int64 TableExprNodeArrayColumnInt::getElemInt (const TableExprId& id, const Slicer& index) { Array arr = col_p.getSlice (id.rownr(), index); return *arr.data(); } MArray TableExprNodeArrayColumnInt::getArrayInt (const TableExprId& id) { if (tabCol_p.isDefined (id.rownr())) { Array arr = col_p (id.rownr()); Array out (arr.shape()); convertArray (out, arr); return MArray (out); } return MArray(); } MArray TableExprNodeArrayColumnInt::getSliceInt (const TableExprId& id, const Slicer& index) { if (tabCol_p.isDefined (id.rownr())) { Array arr = col_p.getSlice (id.rownr(), index); Array out (arr.shape()); convertArray (out, arr); return MArray (out); } return MArray(); } Array TableExprNodeArrayColumnInt::getElemColumnInt (const Vector& rownrs, const Slicer& index) { return col_p.getColumnCells (rownrs, index); } TableExprNodeArrayColumnuInt::TableExprNodeArrayColumnuInt (const TableColumn& col, const TableExprInfo& tableInfo) : TableExprNodeArrayColumn (col, tableInfo), col_p (col) {} void TableExprNodeArrayColumnuInt::applySelection (const Vector& rownrs) { TableExprNodeArrayColumn::applySelection (rownrs); col_p = ArrayColumn(tabCol_p); } Int64 TableExprNodeArrayColumnuInt::getElemInt (const TableExprId& id, const Slicer& index) { Array arr = col_p.getSlice (id.rownr(), index); return *arr.data(); } MArray TableExprNodeArrayColumnuInt::getArrayInt (const TableExprId& id) { if (tabCol_p.isDefined (id.rownr())) { Array arr = col_p (id.rownr()); Array out (arr.shape()); convertArray (out, arr); return MArray (out); } return MArray(); } MArray TableExprNodeArrayColumnuInt::getSliceInt (const TableExprId& id, const Slicer& index) { if (tabCol_p.isDefined (id.rownr())) { Array arr = col_p.getSlice (id.rownr(), index); Array out (arr.shape()); convertArray (out, arr); return MArray (out); } return MArray(); } Array TableExprNodeArrayColumnuInt::getElemColumnuInt (const Vector& rownrs, const Slicer& index) { return col_p.getColumnCells (rownrs, index); } TableExprNodeArrayColumnInt64::TableExprNodeArrayColumnInt64 (const TableColumn& col, const TableExprInfo& tableInfo) : TableExprNodeArrayColumn (col, tableInfo), col_p (col) {} void TableExprNodeArrayColumnInt64::applySelection (const Vector& rownrs) { TableExprNodeArrayColumn::applySelection (rownrs); col_p = ArrayColumn(tabCol_p); } Int64 TableExprNodeArrayColumnInt64::getElemInt (const TableExprId& id, const Slicer& index) { Array arr = col_p.getSlice (id.rownr(), index); return *arr.data(); } MArray TableExprNodeArrayColumnInt64::getArrayInt (const TableExprId& id) { if (tabCol_p.isDefined (id.rownr())) { return MArray (col_p (id.rownr())); } return MArray(); } MArray TableExprNodeArrayColumnInt64::getSliceInt (const TableExprId& id, const Slicer& index) { if (tabCol_p.isDefined (id.rownr())) { return MArray (col_p.getSlice (id.rownr(), index)); } return MArray(); } Array TableExprNodeArrayColumnInt64::getElemColumnInt64 (const Vector& rownrs, const Slicer& index) { return col_p.getColumnCells (rownrs, index); } TableExprNodeArrayColumnFloat::TableExprNodeArrayColumnFloat (const TableColumn& col, const TableExprInfo& tableInfo) : TableExprNodeArrayColumn (col, tableInfo), col_p (col) {} void TableExprNodeArrayColumnFloat::applySelection (const Vector& rownrs) { TableExprNodeArrayColumn::applySelection (rownrs); col_p = ArrayColumn(tabCol_p); } Double TableExprNodeArrayColumnFloat::getElemDouble (const TableExprId& id, const Slicer& index) { Array arr = col_p.getSlice (id.rownr(), index); return *arr.data(); } MArray TableExprNodeArrayColumnFloat::getArrayDouble (const TableExprId& id) { if (tabCol_p.isDefined (id.rownr())) { Array arr = col_p (id.rownr()); Array out (arr.shape()); convertArray (out, arr); return MArray(out); } return MArray(); } MArray TableExprNodeArrayColumnFloat::getSliceDouble (const TableExprId& id, const Slicer& index) { if (tabCol_p.isDefined (id.rownr())) { Array arr = col_p.getSlice (id.rownr(), index); Array out (arr.shape()); convertArray (out, arr); return MArray(out); } return MArray(); } Array TableExprNodeArrayColumnFloat::getElemColumnFloat (const Vector& rownrs, const Slicer& index) { return col_p.getColumnCells (rownrs, index); } TableExprNodeArrayColumnDouble::TableExprNodeArrayColumnDouble (const TableColumn& col, const TableExprInfo& tableInfo) : TableExprNodeArrayColumn (col, tableInfo), col_p (col) {} void TableExprNodeArrayColumnDouble::applySelection (const Vector& rownrs) { TableExprNodeArrayColumn::applySelection (rownrs); col_p = ArrayColumn(tabCol_p); } Double TableExprNodeArrayColumnDouble::getElemDouble (const TableExprId& id, const Slicer& index) { Array arr = col_p.getSlice (id.rownr(), index); return *arr.data(); } MArray TableExprNodeArrayColumnDouble::getArrayDouble (const TableExprId& id) { if (tabCol_p.isDefined (id.rownr())) { return MArray (col_p (id.rownr())); } return MArray(); } MArray TableExprNodeArrayColumnDouble::getSliceDouble (const TableExprId& id, const Slicer& index) { if (tabCol_p.isDefined (id.rownr())) { return MArray (col_p.getSlice (id.rownr(), index)); } return MArray(); } Array TableExprNodeArrayColumnDouble::getElemColumnDouble (const Vector& rownrs, const Slicer& index) { return col_p.getColumnCells (rownrs, index); } TableExprNodeArrayColumnComplex::TableExprNodeArrayColumnComplex (const TableColumn& col, const TableExprInfo& tableInfo) : TableExprNodeArrayColumn (col, tableInfo), col_p (col) {} void TableExprNodeArrayColumnComplex::applySelection (const Vector& rownrs) { TableExprNodeArrayColumn::applySelection (rownrs); col_p = ArrayColumn(tabCol_p); } DComplex TableExprNodeArrayColumnComplex::getElemDComplex (const TableExprId& id, const Slicer& index) { Array arr = col_p.getSlice (id.rownr(), index); return *arr.data(); } MArray TableExprNodeArrayColumnComplex::getArrayDComplex (const TableExprId& id) { if (tabCol_p.isDefined (id.rownr())) { Array arr = col_p (id.rownr()); Array out (arr.shape()); convertArray (out, arr); return MArray (out); } return MArray(); } MArray TableExprNodeArrayColumnComplex::getSliceDComplex (const TableExprId& id, const Slicer& index) { if (tabCol_p.isDefined (id.rownr())) { Array arr = col_p.getSlice (id.rownr(), index); Array out (arr.shape()); convertArray (out, arr); return MArray (out); } return MArray(); } Array TableExprNodeArrayColumnComplex::getElemColumnComplex (const Vector& rownrs, const Slicer& index) { return col_p.getColumnCells (rownrs, index); } TableExprNodeArrayColumnDComplex::TableExprNodeArrayColumnDComplex (const TableColumn& col, const TableExprInfo& tableInfo) : TableExprNodeArrayColumn (col, tableInfo), col_p (col) {} void TableExprNodeArrayColumnDComplex::applySelection (const Vector& rownrs) { TableExprNodeArrayColumn::applySelection (rownrs); col_p = ArrayColumn(tabCol_p); } DComplex TableExprNodeArrayColumnDComplex::getElemDComplex (const TableExprId& id, const Slicer& index) { Array arr = col_p.getSlice (id.rownr(), index); return *arr.data(); } MArray TableExprNodeArrayColumnDComplex::getArrayDComplex (const TableExprId& id) { if (tabCol_p.isDefined (id.rownr())) { return MArray (col_p (id.rownr())); } return MArray(); } MArray TableExprNodeArrayColumnDComplex::getSliceDComplex (const TableExprId& id, const Slicer& index) { if (tabCol_p.isDefined (id.rownr())) { return MArray (col_p.getSlice (id.rownr(), index)); } return MArray(); } Array TableExprNodeArrayColumnDComplex::getElemColumnDComplex (const Vector& rownrs, const Slicer& index) { return col_p.getColumnCells (rownrs, index); } TableExprNodeArrayColumnString::TableExprNodeArrayColumnString (const TableColumn& col, const TableExprInfo& tableInfo) : TableExprNodeArrayColumn (col, tableInfo), col_p (col) {} void TableExprNodeArrayColumnString::applySelection (const Vector& rownrs) { TableExprNodeArrayColumn::applySelection (rownrs); col_p = ArrayColumn(tabCol_p); } String TableExprNodeArrayColumnString::getElemString (const TableExprId& id, const Slicer& index) { Array arr = col_p.getSlice (id.rownr(), index); return *arr.data(); } MArray TableExprNodeArrayColumnString::getArrayString (const TableExprId& id) { if (tabCol_p.isDefined (id.rownr())) { return MArray (col_p (id.rownr())); } return MArray(); } MArray TableExprNodeArrayColumnString::getSliceString (const TableExprId& id, const Slicer& index) { if (tabCol_p.isDefined (id.rownr())) { return MArray (col_p.getSlice (id.rownr(), index)); } return MArray(); } Array TableExprNodeArrayColumnString::getElemColumnString (const Vector& rownrs, const Slicer& index) { return col_p.getColumnCells (rownrs, index); } // ---------------------------- // TableExprNodeIndex functions // ---------------------------- TableExprNodeIndex::TableExprNodeIndex (const TableExprNodeSet& indices, const TaQLStyle& style) : TableExprNodeMulti (NTInt, VTIndex, OtColumn, indices), origin_p (style.origin()), endMinus_p (0), isCOrder_p (style.isCOrder()), isSingle_p (True) { if (style.isEndExcl()) endMinus_p = 1; fillIndex (indices); } void TableExprNodeIndex::checkIndexValues (const TENShPtr& arrayNode) { uInt i; Int ndim = arrayNode->ndim(); uInt n = start_p.size(); // Check against dimensionality (if fixed). if (ndim >= 0 && ndim != Int(n)) { throw (TableInvExpr ("#indices mismatches array dimensionality")); } // Check start and increment values. for (i=0; ishape(); if (shape.size() > 0) { for (i=0; i= shape(i)) { throw (TableInvExpr("index value exceeds array shape")); } } if (!varIndex_p[3*i + 1]) { if (end_p(i) >= shape(i)) { throw (TableInvExpr("index end value exceeds array shape")); } } } } } void TableExprNodeIndex::fillSlicer (const TableExprId& id) { uInt n = varIndex_p.size(); uInt i = 0; uInt j = 0; while (j < n) { if (varIndex_p[j]) { Int64 val = operands_p[j]->getInt (id); if (val < 0) { start_p(i) = val; }else{ start_p(i) = val - origin_p; } } j++; if (varIndex_p[j]) { if (operands_p[j] == 0) { end_p(i) = start_p(i); }else{ Int64 val = operands_p[j]->getInt (id); if (val < 0) { end_p(i) = val - endMinus_p; }else{ end_p(i) = val - origin_p - endMinus_p; } } } j++; if (varIndex_p[j]) { incr_p(i) = operands_p[j]->getInt(id); } j++; i++; } slicer_p = Slicer (start_p, end_p, incr_p, Slicer::endIsLast); } // Fill the children pointers of a node. // Also reduce the tree if possible by combining constants. void TableExprNodeIndex::fillIndex (const TableExprNodeSet& indices) { // Check that the set elements have equal data types. indices.checkEqualDataTypes(); // Check that the set contains discrete values. if (! indices.isDiscrete()) { throw (TableInvExpr ("Index values must be discrete (with possible :")); } TENShPtr rep; // Copy block of start, end, and increment. // Determine if single element subscripting is done. // That is true if all starts are given and no end and increment values. // Check if all indices have data type Int and are scalars. uInt n = indices.size(); operands_p.resize (3 * n); uInt j = 0; for (uInt i=0; istart(); if (rep) { operands_p[j] = rep; }else{ isSingle_p = False; } j++; rep = indices[inx]->end(); if (rep) { operands_p[j] = rep; isSingle_p = False; } j++; rep = indices[inx]->increment(); if (rep) { operands_p[j] = rep; isSingle_p = False; } j++; } // Check if all indices have data type Int, are scalars, and don't // use aggregate functions. for (uInt i=0; idataType() != NTInt || operands_p[i]->valueType() != VTScalar) { throw (TableInvExpr ("Index value must be an integer scalar")); } TableExprNodeUtil::checkAggrFuncs (operands_p[i].get()); } } convertConstIndex(); if (isConstant()) { slicer_p = Slicer (start_p, end_p, incr_p, Slicer::endIsLast); } } void TableExprNodeIndex::convertConstIndex() { TENShPtr rep; uInt n = operands_p.size() / 3; start_p.resize (n); end_p.resize (n); incr_p.resize (n); varIndex_p.resize (3*n); varIndex_p.set (False); uInt j = 0; for (uInt i=0; iisConstant()) { Int64 val = rep->getInt(0); if (val < 0) { start_p(i) = val; }else{ start_p(i) = val - origin_p; } }else{ varIndex_p[j] = True; } } j++; // If no end value is given, it is initially set to the end. // If a start is given, it is set to start. // A negative end means till the end. rep = operands_p[j]; end_p(i) = Slicer::MimicSource; if (rep != 0) { if (rep->isConstant()) { Int64 val = rep->getInt(0); if (val != Slicer::MimicSource) { if (val < 0) { end_p(i) = val - endMinus_p; }else{ end_p(i) = val - origin_p - endMinus_p; } } }else{ varIndex_p[j] = True; } }else{ if (operands_p[j-1] != 0) { end_p(i) = start_p(i); varIndex_p[j] = varIndex_p[j-1]; } } j++; // If no increment value is given, it is 1. rep = operands_p[j]; incr_p(i) = 1; if (rep != 0) { if (rep->isConstant()) { incr_p(i) = rep->getInt(0); }else{ varIndex_p[j] = True; } } j++; } } // ---------------------- // TableExprNodeArrayPart // ---------------------- TableExprNodeArrayPart::TableExprNodeArrayPart (const TENShPtr& arrayNode, const TENShPtr& indexNode) : TableExprNodeArray (arrayNode->dataType(), OtSlice), colNode_p (0) { // Keep nodes and cast them to the array and index node. lnode_p = arrayNode; rnode_p = indexNode; arrNode_p = dynamic_cast(arrayNode.get()); AlwaysAssert (arrNode_p, AipsError); inxNode_p = dynamic_cast(indexNode.get()); AlwaysAssert (inxNode_p, AipsError); // Check the index bounds as far as possible. inxNode_p->checkIndexValues (arrayNode); fillExprType (indexNode.get()); fillExprType (arrayNode.get()); // If indexing a single element, the result is a scalar. if (inxNode_p->isSingle()) { vtype_p = VTScalar; ndim_p = 0; } else if (inxNode_p->isConstant()) { // Otherwise if the index node is constant, it may be possible // to determine the resulting shape. const Slicer& slicer = inxNode_p->getSlicer(0); // If all slicer lengths are defined, that is the resulting shape. if (slicer.isFixed()) { shape_p = slicer.length(); ndim_p = shape_p.size(); }else{ // If some are depending on array shape, the resulting // shape can be determined if the array shape is fixed. IPosition arrshp = arrayNode->shape(); if (arrshp.size() > 0) { IPosition blc,trc,inc; shape_p = slicer.inferShapeFromSource (arrshp, blc, trc, inc); ndim_p = shape_p.size(); } } } if (inxNode_p->isConstant()) { // If the constant child is an ArrayColumn, things can be // improved in getColumnXXX. colNode_p = dynamic_cast(arrayNode.get()); } setUnit (arrayNode->unit()); } void TableExprNodeArrayPart::show (ostream& os, uInt indent) const { TableExprNodeRep::show (os, indent); os << "array: "; arrNode_p->show (os, indent+2); os << "index: "; inxNode_p->show (os, indent+2); } Bool TableExprNodeArrayPart::getColumnDataType (DataType& dt) const { //# Return data type of column if constant index. if (inxNode_p->isConstant()) { return arrNode_p->getColumnDataType (dt); } return False; } Bool TableExprNodeArrayPart::getBool (const TableExprId& id) { DebugAssert (valueType() == VTScalar, AipsError); return arrNode_p->getElemBool (id, inxNode_p->getSlicer(id)); } Int64 TableExprNodeArrayPart::getInt (const TableExprId& id) { DebugAssert (valueType() == VTScalar, AipsError); return arrNode_p->getElemInt (id, inxNode_p->getSlicer(id)); } Double TableExprNodeArrayPart::getDouble (const TableExprId& id) { DebugAssert (valueType() == VTScalar, AipsError); return arrNode_p->getElemDouble (id, inxNode_p->getSlicer(id)); } DComplex TableExprNodeArrayPart::getDComplex (const TableExprId& id) { DebugAssert (valueType() == VTScalar, AipsError); return arrNode_p->getElemDComplex (id, inxNode_p->getSlicer(id)); } String TableExprNodeArrayPart::getString (const TableExprId& id) { DebugAssert (valueType() == VTScalar, AipsError); return arrNode_p->getElemString (id, inxNode_p->getSlicer(id)); } MVTime TableExprNodeArrayPart::getDate (const TableExprId& id) { DebugAssert (valueType() == VTScalar, AipsError); return arrNode_p->getElemDate (id, inxNode_p->getSlicer(id)); } MArray TableExprNodeArrayPart::getArrayBool (const TableExprId& id) { DebugAssert (valueType() == VTArray, AipsError); return arrNode_p->getSliceBool (id, inxNode_p->getSlicer(id)); } MArray TableExprNodeArrayPart::getArrayInt (const TableExprId& id) { DebugAssert (valueType() == VTArray, AipsError); return arrNode_p->getSliceInt (id, inxNode_p->getSlicer(id)); } MArray TableExprNodeArrayPart::getArrayDouble (const TableExprId& id) { DebugAssert (valueType() == VTArray, AipsError); return arrNode_p->getSliceDouble (id, inxNode_p->getSlicer(id)); } MArray TableExprNodeArrayPart::getArrayDComplex (const TableExprId& id) { DebugAssert (valueType() == VTArray, AipsError); return arrNode_p->getSliceDComplex (id, inxNode_p->getSlicer(id)); } MArray TableExprNodeArrayPart::getArrayString (const TableExprId& id) { DebugAssert (valueType() == VTArray, AipsError); return arrNode_p->getSliceString (id, inxNode_p->getSlicer(id)); } MArray TableExprNodeArrayPart::getArrayDate (const TableExprId& id) { DebugAssert (valueType() == VTArray, AipsError); return arrNode_p->getSliceDate (id, inxNode_p->getSlicer(id)); } Array TableExprNodeArrayPart::getColumnBool (const Vector& rownrs) { if (colNode_p == 0) { return TableExprNodeRep::getColumnBool (rownrs); } return colNode_p->getElemColumnBool (rownrs, inxNode_p->getSlicer(0)); } Array TableExprNodeArrayPart::getColumnuChar (const Vector& rownrs) { if (colNode_p == 0) { return TableExprNodeRep::getColumnuChar (rownrs); } return colNode_p->getElemColumnuChar (rownrs, inxNode_p->getSlicer(0)); } Array TableExprNodeArrayPart::getColumnShort (const Vector& rownrs) { if (colNode_p == 0) { return TableExprNodeRep::getColumnShort (rownrs); } return colNode_p->getElemColumnShort (rownrs, inxNode_p->getSlicer(0)); } Array TableExprNodeArrayPart::getColumnuShort (const Vector& rownrs) { if (colNode_p == 0) { return TableExprNodeRep::getColumnuShort (rownrs); } return colNode_p->getElemColumnuShort (rownrs, inxNode_p->getSlicer(0)); } Array TableExprNodeArrayPart::getColumnInt (const Vector& rownrs) { if (colNode_p == 0) { return TableExprNodeRep::getColumnInt (rownrs); } return colNode_p->getElemColumnInt (rownrs, inxNode_p->getSlicer(0)); } Array TableExprNodeArrayPart::getColumnuInt (const Vector& rownrs) { if (colNode_p == 0) { return TableExprNodeRep::getColumnuInt (rownrs); } return colNode_p->getElemColumnuInt (rownrs, inxNode_p->getSlicer(0)); } Array TableExprNodeArrayPart::getColumnInt64 (const Vector& rownrs) { if (colNode_p == 0) { return TableExprNodeRep::getColumnInt64 (rownrs); } return colNode_p->getElemColumnInt64 (rownrs, inxNode_p->getSlicer(0)); } Array TableExprNodeArrayPart::getColumnFloat (const Vector& rownrs) { if (colNode_p == 0) { return TableExprNodeRep::getColumnFloat (rownrs); } return colNode_p->getElemColumnFloat (rownrs, inxNode_p->getSlicer(0)); } Array TableExprNodeArrayPart::getColumnDouble (const Vector& rownrs) { if (colNode_p == 0) { return TableExprNodeRep::getColumnDouble (rownrs); } return colNode_p->getElemColumnDouble (rownrs, inxNode_p->getSlicer(0)); } Array TableExprNodeArrayPart::getColumnComplex (const Vector& rownrs) { if (colNode_p == 0) { return TableExprNodeRep::getColumnComplex (rownrs); } return colNode_p->getElemColumnComplex (rownrs, inxNode_p->getSlicer(0)); } Array TableExprNodeArrayPart::getColumnDComplex (const Vector& rownrs) { if (colNode_p == 0) { return TableExprNodeRep::getColumnDComplex (rownrs); } return colNode_p->getElemColumnDComplex (rownrs, inxNode_p->getSlicer(0)); } Array TableExprNodeArrayPart::getColumnString (const Vector& rownrs) { if (colNode_p == 0) { return TableExprNodeRep::getColumnString (rownrs); } return colNode_p->getElemColumnString (rownrs, inxNode_p->getSlicer(0)); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/ExprNodeArray.h000066400000000000000000000752721476623553700203100ustar00rootroot00000000000000//# ExprNodeArray.h: Classes representing an array in table select expression //# Copyright (C) 1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_EXPRNODEARRAY_H #define TABLES_EXPRNODEARRAY_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableExprNodeSet; // // Base class for arrays in table select expression // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep //
      • TableExprNodeBinary // // // This class is the base class to represent an array. // The actual storing of the array column is done by its derivations. // class TableExprNodeArray : public TableExprNodeBinary { public: // Create the object. // TableExprNodeArray (NodeDataType, OperType); TableExprNodeArray (const TableExprNodeRep& node, NodeDataType, OperType); TableExprNodeArray (NodeDataType, OperType, const IPosition& shape); // ~TableExprNodeArray() override = default; // Turn a constant array with one element into a scalar. // It returns a zero pointer if not possible. // The default implementation returns 0. virtual TENShPtr makeConstantScalar(); // Validate the given index against the array's shape. // Treat a negative as an index from the end (a la python) and replace it. IPosition validateIndex (const IPosition& index, const ArrayBase& arr) const; // Get the shape of the array in the given row. // This default implementation evaluates the value and returns its shape. const IPosition& getShape (const TableExprId& id) override; // The default implementation of getArrayDouble does // getArrayInt and converts the result. MArray getArrayDouble (const TableExprId& id) override; // The default implementation of getArrayDComplex does // getArrayDouble and converts the result. MArray getArrayDComplex (const TableExprId& id) override; // Does a value occur in the set? // Bool contains (const TableExprId& id, Bool value) override; Bool contains (const TableExprId& id, Int64 value) override; Bool contains (const TableExprId& id, Double value) override; Bool contains (const TableExprId& id, DComplex value) override; Bool contains (const TableExprId& id, String value) override; Bool contains (const TableExprId& id, MVTime value) override; MArray contains (const TableExprId& id, const MArray& value) override; MArray contains (const TableExprId& id, const MArray& value) override; MArray contains (const TableExprId& id, const MArray& value) override; MArray contains (const TableExprId& id, const MArray& value) override; MArray contains (const TableExprId& id, const MArray& value) override; MArray contains (const TableExprId& id, const MArray& value) override; // // Get a single element from the array in the given row. // virtual Bool getElemBool (const TableExprId& id, const Slicer& index); virtual Int64 getElemInt (const TableExprId& id, const Slicer& index); virtual Double getElemDouble (const TableExprId& id, const Slicer& index); virtual DComplex getElemDComplex (const TableExprId& id, const Slicer& index); virtual String getElemString (const TableExprId& id, const Slicer& index); virtual MVTime getElemDate (const TableExprId& id, const Slicer& index); // // Get a slice of the array in the given row. // virtual MArray getSliceBool (const TableExprId& id, const Slicer&); virtual MArray getSliceInt (const TableExprId& id, const Slicer&); virtual MArray getSliceDouble (const TableExprId& id, const Slicer&); virtual MArray getSliceDComplex (const TableExprId& id, const Slicer&); virtual MArray getSliceString (const TableExprId& id, const Slicer&); virtual MArray getSliceDate (const TableExprId& id, const Slicer&); // // Get a single element for the entire column (used by sort). // virtual Array getElemColumnBool (const Vector& rownrs, const Slicer&); virtual Array getElemColumnuChar (const Vector& rownrs, const Slicer&); virtual Array getElemColumnShort (const Vector& rownrs, const Slicer&); virtual Array getElemColumnuShort (const Vector& rownrs, const Slicer&); virtual Array getElemColumnInt (const Vector& rownrs, const Slicer&); virtual Array getElemColumnuInt (const Vector& rownrs, const Slicer&); virtual Array getElemColumnInt64 (const Vector& rownrs, const Slicer&); virtual Array getElemColumnFloat (const Vector& rownrs, const Slicer&); virtual Array getElemColumnDouble (const Vector& rownrs, const Slicer&); virtual Array getElemColumnComplex (const Vector& rownrs, const Slicer&); virtual Array getElemColumnDComplex (const Vector& rownrs, const Slicer&); virtual Array getElemColumnString (const Vector& rownrs, const Slicer&); // // Make an array with the given shape and fill it with the value. static MArray makeArray (const IPosition& shape, Int64 value); static MArray makeArray (const IPosition& shape, Double value); static MArray makeArray (const IPosition& shape, const DComplex& value); protected: IPosition varShape_p; }; // // Base class for Array column in table select expression // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeArray // // // This class is the base class to store an array column. // The actual storing of the array column is done by its derivations. // class TableExprNodeArrayColumn : public TableExprNodeArray { public: // Create the object for the given column and table. TableExprNodeArrayColumn (const TableColumn& tablecol, const TableExprInfo&); ~TableExprNodeArrayColumn() override = default; // Get the table info for this column. TableExprInfo getTableInfo() const override; // Do not apply the selection. void disableApplySelection() override; // Re-create the column object for a selection of rows. void applySelection (const Vector& rownrs) override; // Get the TableColumn object. const TableColumn& getColumn() const; // Get the shape of the array in the given row. const IPosition& getShape (const TableExprId& id) override; // Is the value in the given row defined? Bool isDefined (const TableExprId& id) override; // Get the data type of this column. // It returns with a True status. Bool getColumnDataType (DataType&) const override; protected: TableExprInfo tableInfo_p; TableColumn tabCol_p; Bool applySelection_p; }; // // Bool array column in table select expression // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeArrayColumn // // // These classes store an array column of type X. // class TableExprNodeArrayColumnBool : public TableExprNodeArrayColumn { public: TableExprNodeArrayColumnBool (const TableColumn&, const TableExprInfo&); ~TableExprNodeArrayColumnBool() override = default; // Re-create the column object for a selection of rows. void applySelection (const Vector& rownrs) override; Bool getElemBool (const TableExprId& id, const Slicer& index) override; MArray getArrayBool (const TableExprId& id) override; MArray getSliceBool (const TableExprId& id, const Slicer&) override; Array getElemColumnBool (const Vector& rownrs, const Slicer&) override; protected: ArrayColumn col_p; }; // // uChar array column in table select expression // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeArrayColumn // // // These classes store an array column of type X. // class TableExprNodeArrayColumnuChar : public TableExprNodeArrayColumn { public: TableExprNodeArrayColumnuChar (const TableColumn&, const TableExprInfo&); ~TableExprNodeArrayColumnuChar() override = default; // Re-create the column object for a selection of rows. void applySelection (const Vector& rownrs) override; Int64 getElemInt (const TableExprId& id, const Slicer& index) override; MArray getArrayInt (const TableExprId& id) override; MArray getSliceInt (const TableExprId& id, const Slicer&) override; Array getElemColumnuChar (const Vector& rownrs, const Slicer&) override; protected: ArrayColumn col_p; }; // // Short array column in table select expression // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeArrayColumn // // // These classes store an array column of type X. // class TableExprNodeArrayColumnShort : public TableExprNodeArrayColumn { public: TableExprNodeArrayColumnShort (const TableColumn&, const TableExprInfo&); ~TableExprNodeArrayColumnShort() override = default; // Re-create the column object for a selection of rows. void applySelection (const Vector& rownrs) override; Int64 getElemInt (const TableExprId& id, const Slicer& index) override; MArray getArrayInt (const TableExprId& id) override; MArray getSliceInt (const TableExprId& id, const Slicer&) override; Array getElemColumnShort (const Vector& rownrs, const Slicer&) override; protected: ArrayColumn col_p; }; // // uShort array column in table select expression // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeArrayColumn // // // These classes store an array column of type X. // class TableExprNodeArrayColumnuShort : public TableExprNodeArrayColumn { public: TableExprNodeArrayColumnuShort (const TableColumn&, const TableExprInfo&); ~TableExprNodeArrayColumnuShort() override = default; // Re-create the column object for a selection of rows. void applySelection (const Vector& rownrs) override; Int64 getElemInt (const TableExprId& id, const Slicer& index) override; MArray getArrayInt (const TableExprId& id) override; MArray getSliceInt (const TableExprId& id, const Slicer&) override; Array getElemColumnuShort (const Vector& rownrs, const Slicer&) override; protected: ArrayColumn col_p; }; // // Int array column in table select expression // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeArrayColumn // // // These classes store an array column of type X. // class TableExprNodeArrayColumnInt : public TableExprNodeArrayColumn { public: TableExprNodeArrayColumnInt (const TableColumn&, const TableExprInfo&); ~TableExprNodeArrayColumnInt() override = default; // Re-create the column object for a selection of rows. void applySelection (const Vector& rownrs) override; Int64 getElemInt (const TableExprId& id, const Slicer& index) override; MArray getArrayInt (const TableExprId& id) override; MArray getSliceInt (const TableExprId& id, const Slicer&) override; Array getElemColumnInt (const Vector& rownrs, const Slicer&) override; protected: ArrayColumn col_p; }; // // uInt array column in table select expression // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeArrayColumn // // // These classes store an array column of type X. // class TableExprNodeArrayColumnuInt : public TableExprNodeArrayColumn { public: TableExprNodeArrayColumnuInt (const TableColumn&, const TableExprInfo&); ~TableExprNodeArrayColumnuInt() override = default; // Re-create the column object for a selection of rows. void applySelection (const Vector& rownrs) override; Int64 getElemInt (const TableExprId& id, const Slicer& index) override; MArray getArrayInt (const TableExprId& id) override; MArray getSliceInt (const TableExprId& id, const Slicer&) override; Array getElemColumnuInt (const Vector& rownrs, const Slicer&) override; protected: ArrayColumn col_p; }; // // Int64 array column in table select expression // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeArrayColumn // // // These classes store an array column of type X. // class TableExprNodeArrayColumnInt64 : public TableExprNodeArrayColumn { public: TableExprNodeArrayColumnInt64 (const TableColumn&, const TableExprInfo&); ~TableExprNodeArrayColumnInt64() override = default; // Re-create the column object for a selection of rows. void applySelection (const Vector& rownrs) override; Int64 getElemInt (const TableExprId& id, const Slicer& index) override; MArray getArrayInt (const TableExprId& id) override; MArray getSliceInt (const TableExprId& id, const Slicer&) override; Array getElemColumnInt64 (const Vector& rownrs, const Slicer&) override; protected: ArrayColumn col_p; }; // // Float array column in table select expression // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeArrayColumn // // // These classes store an array column of type X. // class TableExprNodeArrayColumnFloat : public TableExprNodeArrayColumn { public: TableExprNodeArrayColumnFloat (const TableColumn&, const TableExprInfo&); ~TableExprNodeArrayColumnFloat() override = default; // Re-create the column object for a selection of rows. void applySelection (const Vector& rownrs) override; Double getElemDouble (const TableExprId& id, const Slicer& index) override; MArray getArrayDouble (const TableExprId& id) override; MArray getSliceDouble (const TableExprId& id, const Slicer&) override; Array getElemColumnFloat (const Vector& rownrs, const Slicer&) override; protected: ArrayColumn col_p; }; // // Double array column in table select expression // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeArrayColumn // // // These classes store an array column of type X. // class TableExprNodeArrayColumnDouble : public TableExprNodeArrayColumn { public: TableExprNodeArrayColumnDouble (const TableColumn&, const TableExprInfo&); ~TableExprNodeArrayColumnDouble() override = default; // Re-create the column object for a selection of rows. void applySelection (const Vector& rownrs) override; Double getElemDouble (const TableExprId& id, const Slicer& index) override; MArray getArrayDouble (const TableExprId& id) override; MArray getSliceDouble (const TableExprId& id, const Slicer&) override; Array getElemColumnDouble (const Vector& rownrs, const Slicer&) override; protected: ArrayColumn col_p; }; // // Complex array column in table select expression // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeArrayColumn // // // These classes store an array column of type X. // class TableExprNodeArrayColumnComplex : public TableExprNodeArrayColumn { public: TableExprNodeArrayColumnComplex (const TableColumn&, const TableExprInfo&); ~TableExprNodeArrayColumnComplex() override = default; // Re-create the column object for a selection of rows. void applySelection (const Vector& rownrs) override; DComplex getElemDComplex (const TableExprId& id, const Slicer& index) override; MArray getArrayDComplex (const TableExprId& id) override; MArray getSliceDComplex (const TableExprId& id, const Slicer&) override; Array getElemColumnComplex (const Vector& rownrs, const Slicer&) override; protected: ArrayColumn col_p; }; // // DComplex array column in table select expression // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeArrayColumn // // // These classes store an array column of type X. // class TableExprNodeArrayColumnDComplex : public TableExprNodeArrayColumn { public: TableExprNodeArrayColumnDComplex (const TableColumn&, const TableExprInfo&); ~TableExprNodeArrayColumnDComplex() override = default; // Re-create the column object for a selection of rows. void applySelection (const Vector& rownrs) override; DComplex getElemDComplex (const TableExprId& id, const Slicer& index) override; MArray getArrayDComplex (const TableExprId& id) override; MArray getSliceDComplex (const TableExprId& id, const Slicer&) override; Array getElemColumnDComplex (const Vector& rownrs, const Slicer&) override; protected: ArrayColumn col_p; }; // // String array column in table select expression // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeArrayColumn // // // These classes store an array column of type X. // class TableExprNodeArrayColumnString : public TableExprNodeArrayColumn { public: TableExprNodeArrayColumnString (const TableColumn&, const TableExprInfo&); ~TableExprNodeArrayColumnString() override = default; // Re-create the column object for a selection of rows. void applySelection (const Vector& rownrs) override; String getElemString (const TableExprId& id, const Slicer& index) override; MArray getArrayString (const TableExprId& id) override; MArray getSliceString (const TableExprId& id, const Slicer&) override; Array getElemColumnString (const Vector& rownrs, const Slicer&) override; protected: ArrayColumn col_p; }; // // The index of an array element in a table select expression // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeMulti // // // TableExprNodeIndex is used to store an index. // All the operands must be Int. // // // TableExprNodeIndex is a derivation of TableExprNodeMulti // expression tree that represents an index. // // // All operands of TableExprNodeIndex must be Int, // therefore it is a derivation of TableExprNodeMulti. // // //# A List of bugs, limitations, extensions or planned refinements. //
      • to be filled in // class TableExprNodeIndex : public TableExprNodeMulti { public: // Constructor explicit TableExprNodeIndex (const TableExprNodeSet& indices, const TaQLStyle& = TaQLStyle(0)); // Destructor ~TableExprNodeIndex() override = default; // Link all the operands and check datatype. // Calculate the IPosition values for the const operands. void fillIndex (const TableExprNodeSet& indices); // Check if the index values match the dimensionality and shape // of fixed-shaped array. void checkIndexValues (const TENShPtr& arrayNode); // Get the Slicer value for a constant index. const Slicer& getConstantSlicer() const; // Get the Slicer value for the slice. const Slicer& getSlicer (const TableExprId& id); // Does it index a single element? Bool isSingle() const; protected: Int origin_p; //# origin 0 for C++/Python; 1 for Glish Int endMinus_p; //# subtract from end (origin and endExcl) Bool isCOrder_p; //# True for Python IPosition start_p; //# precalculated start values IPosition end_p; //# precalculated end values (<0 = till end) IPosition incr_p; //# precalculated increment values Slicer slicer_p; //# combined start, end, and incr Block varIndex_p; //# is the start for the axes variable? Bool isSingle_p; //# Index a single value? // Precalculate the constant indices and store them. void convertConstIndex(); // Fill the slicer for this row. void fillSlicer (const TableExprId& id); // Get the shape of the node involved. Reverse axes if needed. IPosition getNodeShape (const TENShPtr& arrayNode) const; }; // // Array column part in table select expression // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep //
      • TableExprNodeBinary // // // This class handles a part of an array. // It uses a TableExprNodeArray to handle the array // and a TableExprNodeIndex to store the index. // class TableExprNodeArrayPart : public TableExprNodeArray { public: TableExprNodeArrayPart (const TENShPtr& arrayNode, const TENShPtr& indexNode); ~TableExprNodeArrayPart() override = default; // Show the node. void show (ostream& os, uInt indent) const override; Bool getBool (const TableExprId& id) override; Int64 getInt (const TableExprId& id) override; Double getDouble (const TableExprId& id) override; DComplex getDComplex (const TableExprId& id) override; String getString (const TableExprId& id) override; MVTime getDate (const TableExprId& id) override; MArray getArrayBool (const TableExprId& id) override; MArray getArrayInt (const TableExprId& id) override; MArray getArrayDouble (const TableExprId& id) override; MArray getArrayDComplex (const TableExprId& id) override; MArray getArrayString (const TableExprId& id) override; MArray getArrayDate (const TableExprId& id) override; // Get the data type of this column (if possible). // It returns with a False status when the index is not constant // (that means that the index can vary with row number). Bool getColumnDataType (DataType&) const override; Array getColumnBool (const Vector& rownrs) override; Array getColumnuChar (const Vector& rownrs) override; Array getColumnShort (const Vector& rownrs) override; Array getColumnuShort (const Vector& rownrs) override; Array getColumnInt (const Vector& rownrs) override; Array getColumnuInt (const Vector& rownrs) override; Array getColumnInt64 (const Vector& rownrs) override; Array getColumnFloat (const Vector& rownrs) override; Array getColumnDouble (const Vector& rownrs) override; Array getColumnComplex (const Vector& rownrs) override; Array getColumnDComplex (const Vector& rownrs) override; Array getColumnString (const Vector& rownrs) override; // Get the index node. const TableExprNodeIndex* getIndexNode() const; // Get the array column node. // It returns 0 if the parent object is no array column. const TableExprNodeArrayColumn* getColumnNode() const; private: TableExprNodeIndex* inxNode_p; TableExprNodeArray* arrNode_p; TableExprNodeArrayColumn* colNode_p; //# 0 if arrNode is no arraycolumn }; inline Bool TableExprNodeIndex::isSingle() const { return isSingle_p; } inline const Slicer& TableExprNodeIndex::getConstantSlicer() const { return slicer_p; } inline const Slicer& TableExprNodeIndex::getSlicer (const TableExprId& id) { if (!isConstant()) { fillSlicer (id); } return slicer_p; } inline const TableColumn& TableExprNodeArrayColumn::getColumn() const { return tabCol_p; } inline const TableExprNodeIndex* TableExprNodeArrayPart::getIndexNode() const { return inxNode_p; } inline const TableExprNodeArrayColumn* TableExprNodeArrayPart::getColumnNode() const { return colNode_p; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/ExprNodeRecord.cc000066400000000000000000000240441476623553700205750ustar00rootroot00000000000000//# ExprNodeRecord.cc: Nodes representing fields in record select expression tree //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableExprNodeRecordField::TableExprNodeRecordField (DataType dtype, const Block& fieldNumbers) : TableExprNodeBinary (NTNumeric, VTScalar, OtField, Constant), fieldNrs_p (fieldNumbers), lastEntry_p (fieldNumbers.nelements() - 1) { //# Fill in the real data type. switch (dtype) { case TpBool: dtype_p = NTBool; break; case TpString: dtype_p = NTString; break; case TpComplex: case TpDComplex: dtype_p = NTComplex; break; case TpChar: case TpUChar: case TpShort: case TpUShort: case TpInt: case TpUInt: case TpInt64: dtype_p = NTInt; break; case TpFloat: case TpDouble: dtype_p = NTDouble; break; default: TableExprNode::throwInvDT ("TableExprNodeRecordField: invalid data type"); } exprtype_p = Variable; // Make sure the variable is not a constant for isDefined. ndim_p = -1; } TableExprNodeRecordField::~TableExprNodeRecordField() {} const IPosition& TableExprNodeRecordField::getShape (const TableExprId&) { return shape_p; } Bool TableExprNodeRecordField::isDefined (const TableExprId& id) { DataType dtype=TpOther; if (id.byData()) { dtype = id.data().dataType (fieldNrs_p); } else { const RecordInterface* recPtr = &(id.record()); for (uInt i=0; idescription(); if (fieldNrs_p[i] >= Int(desc.nfields()) || !desc.isSubRecord(fieldNrs_p[i])) { return False; } recPtr = &(recPtr->asRecord (fieldNrs_p[i])); } RecordDesc desc = recPtr->description(); if (fieldNrs_p[lastEntry_p] >= Int(desc.nfields())) { return False; } dtype = desc.type(fieldNrs_p[lastEntry_p]); } switch (dtype_p) { case NTBool: return dtype == TpBool; case NTInt: return dtype == TpUChar || dtype == TpShort || dtype == TpInt || dtype == TpUInt || dtype == TpInt64; case NTDouble: return dtype == TpUChar || dtype == TpShort || dtype == TpInt || dtype == TpUInt || dtype == TpInt64 || dtype == TpFloat || dtype == TpDouble; case NTComplex: return dtype == TpUChar || dtype == TpShort || dtype == TpInt || dtype == TpUInt || dtype == TpInt64 || dtype == TpFloat || dtype == TpDouble || dtype == TpComplex || dtype == TpDComplex; case NTString: return dtype == TpString; default: return False; } return False; } Bool TableExprNodeRecordField::getBool (const TableExprId& id) { if (id.byData()) { return id.data().getBool (fieldNrs_p); } return getRecord(id).asBool (fieldNrs_p[lastEntry_p]); } Int64 TableExprNodeRecordField::getInt (const TableExprId& id) { if (id.byData()) { return id.data().getInt (fieldNrs_p); } return getRecord(id).asInt64 (fieldNrs_p[lastEntry_p]); } Double TableExprNodeRecordField::getDouble (const TableExprId& id) { if (id.byData()) { return id.data().getDouble (fieldNrs_p); } return getRecord(id).asDouble (fieldNrs_p[lastEntry_p]); } DComplex TableExprNodeRecordField::getDComplex (const TableExprId& id) { if (id.byData()) { return id.data().getDComplex (fieldNrs_p); } return getRecord(id).asDComplex (fieldNrs_p[lastEntry_p]); } String TableExprNodeRecordField::getString (const TableExprId& id) { if (id.byData()) { return id.data().getString (fieldNrs_p); } return getRecord(id).asString (fieldNrs_p[lastEntry_p]); } const RecordInterface& TableExprNodeRecordField::getRecord (const TableExprId& id) const { const RecordInterface* recPtr = &(id.record()); for (uInt i=0; iasRecord (fieldNrs_p[i])); } return *recPtr; } TableExprNodeRecordFieldArray::TableExprNodeRecordFieldArray (DataType dtype, const Block& fieldNumbers) : TableExprNodeArray (NTNumeric, OtField), fieldNrs_p (fieldNumbers), lastEntry_p (fieldNumbers.nelements() - 1) { //# Fill in the real data type. switch (dtype) { case TpArrayBool: dtype_p = NTBool; break; case TpArrayString: dtype_p = NTString; break; case TpArrayComplex: case TpArrayDComplex: dtype_p = NTComplex; break; case TpArrayUChar: case TpArrayShort: case TpArrayInt: case TpArrayUInt: case TpArrayInt64: dtype_p = NTInt; break; case TpArrayFloat: case TpArrayDouble: dtype_p = NTDouble; break; default: TableExprNode::throwInvDT ("TableExprNodeRecordFieldArray: invalid data type"); } exprtype_p = Variable; } TableExprNodeRecordFieldArray::~TableExprNodeRecordFieldArray() {} const IPosition& TableExprNodeRecordFieldArray::getShape (const TableExprId& id) { varShape_p.resize (0); if (id.byData()) { varShape_p = id.data().shape (fieldNrs_p); } else { varShape_p = getRecord(id).shape (fieldNrs_p[lastEntry_p]); } return varShape_p; } Bool TableExprNodeRecordFieldArray::isDefined (const TableExprId& id) { DataType dtype=TpOther; if (id.byData()) { dtype = id.data().dataType (fieldNrs_p); } else { const RecordInterface* recPtr = &(id.record()); for (uInt i=0; idescription(); if (fieldNrs_p[i] >= Int(desc.nfields()) || !desc.isSubRecord(fieldNrs_p[i])) { return False; } recPtr = &(recPtr->asRecord (fieldNrs_p[i])); } RecordDesc desc = recPtr->description(); if (fieldNrs_p[lastEntry_p] >= Int(desc.nfields())) { return False; } dtype = desc.type(fieldNrs_p[lastEntry_p]); } switch (dtype_p) { case NTBool: return dtype == TpArrayBool; case NTInt: return dtype == TpArrayUChar || dtype == TpArrayShort || dtype == TpArrayInt || dtype == TpArrayUInt || dtype == TpArrayInt64; case NTDouble: return dtype == TpArrayUChar || dtype == TpArrayShort || dtype == TpArrayInt || dtype == TpArrayUInt || dtype == TpArrayInt64 || dtype == TpArrayFloat || dtype == TpArrayDouble; case NTComplex: return dtype == TpArrayUChar || dtype == TpArrayShort || dtype == TpArrayInt || dtype == TpArrayUInt || dtype == TpArrayInt64 || dtype == TpArrayFloat || dtype == TpArrayDouble || dtype == TpArrayComplex || dtype == TpArrayDComplex; case NTString: return dtype == TpArrayString; default: return False; } return False; } MArray TableExprNodeRecordFieldArray::getArrayBool (const TableExprId& id) { if (id.byData()) { return MArray (id.data().getArrayBool (fieldNrs_p)); } return MArray (getRecord(id).asArrayBool (fieldNrs_p[lastEntry_p])); } MArray TableExprNodeRecordFieldArray::getArrayInt (const TableExprId& id) { if (id.byData()) { return MArray (id.data().getArrayInt (fieldNrs_p)); } return MArray (getRecord(id).toArrayInt64 (fieldNrs_p[lastEntry_p])); } MArray TableExprNodeRecordFieldArray::getArrayDouble (const TableExprId& id) { if (id.byData()) { return MArray (id.data().getArrayDouble (fieldNrs_p)); } return MArray (getRecord(id).toArrayDouble (fieldNrs_p[lastEntry_p])); } MArray TableExprNodeRecordFieldArray::getArrayDComplex (const TableExprId& id) { if (id.byData()) { return MArray (id.data().getArrayDComplex (fieldNrs_p)); } return MArray (getRecord(id).toArrayDComplex (fieldNrs_p[lastEntry_p])); } MArray TableExprNodeRecordFieldArray::getArrayString (const TableExprId& id) { if (id.byData()) { return MArray (id.data().getArrayString (fieldNrs_p)); } return MArray (getRecord(id).asArrayString (fieldNrs_p[lastEntry_p])); } const RecordInterface& TableExprNodeRecordFieldArray::getRecord (const TableExprId& id) const { const RecordInterface* recPtr = &(id.record()); for (uInt i=0; iasRecord (fieldNrs_p[i])); } return *recPtr; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/ExprNodeRecord.h000066400000000000000000000115631476623553700204410ustar00rootroot00000000000000//# ExprNodeRecord.h: Nodes representing fields in record select expression tree //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_EXPRNODERECORD_H #define TABLES_EXPRNODERECORD_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class RecordDesc; class RecordInterface; //# This file defines classes derived from TableExprNode representing //# fields in a record select expression. //# //# Data types Bool, Double, DComplex and String are used. //# Char, uChar, Short, uShort, Int and uInt are converted to Int64, //# float to Double, and Complex to DComplex. //# Binary operators +, -, *, /, ==, >=, >, <, <= and != are recognized. //# Also &&, ||, parentheses and unary +, - and ! are recognized. // // Scalar field in record select expression tree // // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents a scalar column in a table select expression tree. // When the select expression gets evaluated, the value of the // given row in the column is used. // class TableExprNodeRecordField : public TableExprNodeBinary { public: TableExprNodeRecordField (DataType dtype, const Block& fieldNumbers); ~TableExprNodeRecordField(); virtual const IPosition& getShape (const TableExprId& id); virtual Bool isDefined (const TableExprId& id); virtual Bool getBool (const TableExprId& id); virtual Int64 getInt (const TableExprId& id); virtual Double getDouble (const TableExprId& id); virtual DComplex getDComplex (const TableExprId& id); virtual String getString (const TableExprId& id); protected: Block fieldNrs_p; uInt lastEntry_p; // Get the record for the last field number, thus going through // all subrecords for the other field numbers. const RecordInterface& getRecord (const TableExprId& id) const; }; // // Array field in record select expression tree // // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This class represents a scalar column in a table select expression tree. // When the select expression gets evaluated, the value of the // given row in the column is used. // class TableExprNodeRecordFieldArray : public TableExprNodeArray { public: TableExprNodeRecordFieldArray (DataType dtype, const Block& fieldNumbers); ~TableExprNodeRecordFieldArray(); virtual Bool isDefined (const TableExprId& id); virtual const IPosition& getShape (const TableExprId& id); virtual MArray getArrayBool (const TableExprId& id); virtual MArray getArrayInt (const TableExprId& id); virtual MArray getArrayDouble (const TableExprId& id); virtual MArray getArrayDComplex (const TableExprId& id); virtual MArray getArrayString (const TableExprId& id); protected: Block fieldNrs_p; uInt lastEntry_p; // Get the record for the last field number, thus going through // all subrecords for the other field numbers. const RecordInterface& getRecord (const TableExprId& id) const; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/ExprNodeRep.cc000066400000000000000000001037341476623553700201110ustar00rootroot00000000000000//# ExprNodeRep.cc: Representation class for a table column expression tree //# Copyright (C) 1994,1995,1996,1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableExprInfo::TableExprInfo (const Table& table, const String& alias, Bool isJoinTable) : itsTable (table), itsAlias (alias), itsIsJoinTable (isJoinTable) {} void TableExprInfo::apply (const Vector& rownrs) { itsTable = itsTable(rownrs); } // The constructor to be used by the derived classes. TableExprNodeRep::TableExprNodeRep (NodeDataType dtype, ValueType vtype, OperType optype, ExprType exprtype) : dtype_p (dtype), vtype_p (vtype), optype_p (optype), argtype_p (NoArr), exprtype_p (exprtype), ndim_p (0) {} TableExprNodeRep::TableExprNodeRep (NodeDataType dtype, ValueType vtype, OperType optype, ArgType argtype, ExprType exprtype, Int ndim, const IPosition& shape) : dtype_p (dtype), vtype_p (vtype), optype_p (optype), argtype_p (argtype), exprtype_p (exprtype), ndim_p (ndim), shape_p (shape) {} TableExprInfo TableExprNodeRep::getTableInfo() const { return TableExprInfo(); } Bool TableExprNodeRep::isAggregate() const { return False; } void TableExprNodeRep::optimize() {} void TableExprNodeRep::show (ostream& os, uInt indent) const { for (uInt i=0; i&) {} void TableExprNodeRep::flattenTree (std::vector& nodes) { nodes.push_back (this); } void TableExprNodeRep::setUnit (const Unit& unit) { unit_p = unit; if (!unit.empty() && dtype_p == NTInt) { dtype_p = NTDouble; } } Double TableExprNodeRep::getUnitFactor() const { return 1.; } void TableExprNodeRep::adaptSetUnits (const Unit&) {} //# Determine the number of rows in the main table used in the expression. rownr_t TableExprNodeRep::nrow() { if (exprtype_p == Constant) { return 1; } vector
      • tables = TableExprNodeUtil::getNodeTables (this, True); if (tables.empty()) { return 1; // for calc expressions } return tables[0].nrow(); } void TableExprNodeRep::fillExprType (const TableExprNodeRep* node) { if (node != 0 && !node->isConstant()) { exprtype_p = Variable; } } // The getColumn data type is unknown. Bool TableExprNodeRep::getColumnDataType (DataType&) const { return False; } // Convert the tree to a number of range vectors which at least // select the same things. // By default a not possible is returned (an empty block). void TableExprNodeRep::ranges (Block& blrange) { blrange.resize (0, True); } // Create a range. void TableExprNodeRep::createRange (Block& blrange) { blrange.resize (0, True); } void TableExprNodeRep::createRange (Block& blrange, TableExprNodeColumn* tsn, Double st, Double end) { if (tsn == 0) { blrange.resize (0, True); } else { blrange.resize (1, True); blrange[0] = TableExprRange (tsn->getColumn(), st, end); } } const IPosition& TableExprNodeRep::shape (const TableExprId& id) { if (ndim_p == 0 || shape_p.size() != 0) { return shape_p; } return getShape (id); } const IPosition& TableExprNodeRep::getShape (const TableExprId&) { throw (TableInvExpr ("getShape not implemented")); return shape_p; } Bool TableExprNodeRep::isDefined (const TableExprId&) { return True; } //# Supply the default functions for the get functions. Bool TableExprNodeRep::getBool (const TableExprId&) { TableExprNode::throwInvDT ("(getBool not implemented)"); return False; } Int64 TableExprNodeRep::getInt (const TableExprId&) { TableExprNode::throwInvDT ("(getInt not implemented)"); return 0; } Double TableExprNodeRep::getDouble (const TableExprId& id) { return getInt (id); } DComplex TableExprNodeRep::getDComplex (const TableExprId& id) { return getDouble (id); } String TableExprNodeRep::getString (const TableExprId&) { TableExprNode::throwInvDT ("(getString not implemented)"); return ""; } TaqlRegex TableExprNodeRep::getRegex (const TableExprId&) { TableExprNode::throwInvDT ("(getRegex not implemented)"); return TaqlRegex(Regex(String())); } MVTime TableExprNodeRep::getDate (const TableExprId&) { TableExprNode::throwInvDT ("(getDate not implemented)"); return MVTime(0.); } MArray TableExprNodeRep::getArrayBool (const TableExprId&) { TableExprNode::throwInvDT ("(getArrayBool not implemented)"); return MArray(); } MArray TableExprNodeRep::getArrayInt (const TableExprId&) { TableExprNode::throwInvDT ("(getArrayInt not implemented)"); return MArray(); } MArray TableExprNodeRep::getArrayDouble (const TableExprId& id) { MArray tmp(getArrayInt(id)); MArray res; res.fill (tmp); return res; } MArray TableExprNodeRep::getArrayDComplex (const TableExprId&) { TableExprNode::throwInvDT ("(getArrayDComplex not implemented)"); return MArray(); } MArray TableExprNodeRep::getArrayString (const TableExprId&) { TableExprNode::throwInvDT ("(getArrayString not implemented)"); return MArray(); } MArray TableExprNodeRep::getArrayDate (const TableExprId&) { TableExprNode::throwInvDT ("(getArrayDate not implemented)"); return MArray(); } MArray TableExprNodeRep::getBoolAS (const TableExprId& id) { if (valueType() == VTArray) { return getArrayBool(id); } Vector res(1); res[0] = getBool(id); return MArray(res); } MArray TableExprNodeRep::getIntAS (const TableExprId& id) { if (valueType() == VTArray) { return getArrayInt(id); } Vector res(1); res[0] = getInt(id); return MArray(res); } MArray TableExprNodeRep::getDoubleAS (const TableExprId& id) { if (valueType() == VTArray) { return getArrayDouble(id); } Vector res(1); res[0] = getDouble(id); return MArray(res); } MArray TableExprNodeRep::getDComplexAS (const TableExprId& id) { if (valueType() == VTArray) { return getArrayDComplex(id); } Vector res(1); res[0] = getDComplex(id); return MArray(res); } MArray TableExprNodeRep::getStringAS (const TableExprId& id) { if (valueType() == VTArray) { return getArrayString(id); } Vector res(1); res[0] = getString(id); return MArray(res); } MArray TableExprNodeRep::getDateAS (const TableExprId& id) { if (valueType() == VTArray) { return getArrayDate(id); } Vector res(1); res[0] = getDate(id); return MArray(res); } Bool TableExprNodeRep::contains (const TableExprId& id, Bool value) { return (value == getBool(id)); } Bool TableExprNodeRep::contains (const TableExprId& id, Int64 value) { return (value == getInt(id)); } Bool TableExprNodeRep::contains (const TableExprId& id, Double value) { return (value == getDouble(id)); } Bool TableExprNodeRep::contains (const TableExprId& id, DComplex value) { return (value == getDComplex(id)); } Bool TableExprNodeRep::contains (const TableExprId& id, String value) { return (value == getString(id)); } Bool TableExprNodeRep::contains (const TableExprId& id, MVTime value) { return (value == getDate(id)); } MArray TableExprNodeRep::contains (const TableExprId& id, const MArray& value) { return (getBool(id) == value); } MArray TableExprNodeRep::contains (const TableExprId& id, const MArray& value) { return (getInt(id) == value); } MArray TableExprNodeRep::contains (const TableExprId& id, const MArray& value) { return (getDouble(id) == value); } MArray TableExprNodeRep::contains (const TableExprId& id, const MArray& value) { return (getDComplex(id) == value); } MArray TableExprNodeRep::contains (const TableExprId& id, const MArray& value) { return (getString(id) == value); } MArray TableExprNodeRep::contains (const TableExprId& id, const MArray& value) { return (getDate(id) == value); } Array TableExprNodeRep::getColumnBool (const Vector& rownrs) { TableExprId id; rownr_t nrrow = rownrs.size(); Array arr (IPosition(1,nrrow)); Bool* vec = arr.data(); for (rownr_t i=0; i TableExprNodeRep::getColumnuChar (const Vector&) { TableExprNode::throwInvDT ("(getColumnuChar not implemented)"); return Array(); } Array TableExprNodeRep::getColumnShort (const Vector&) { TableExprNode::throwInvDT ("(getColumnShort not implemented)"); return Array(); } Array TableExprNodeRep::getColumnuShort (const Vector&) { TableExprNode::throwInvDT ("(getColumnuShort not implemented)"); return Array(); } Array TableExprNodeRep::getColumnInt (const Vector& rownrs) { TableExprId id; rownr_t nrrow = rownrs.size(); Array arr (IPosition(1,nrrow)); Int* vec = arr.data(); for (rownr_t i=0; i TableExprNodeRep::getColumnuInt (const Vector&) { TableExprNode::throwInvDT ("(getColumnuInt not implemented)"); return Array(); } Array TableExprNodeRep::getColumnInt64 (const Vector& rownrs) { TableExprId id; rownr_t nrrow = rownrs.size(); Array arr (IPosition(1,nrrow)); Int64* vec = arr.data(); for (rownr_t i=0; i TableExprNodeRep::getColumnFloat (const Vector&) { TableExprNode::throwInvDT ("(getColumnFloat not implemented)"); return Array(); } Array TableExprNodeRep::getColumnDouble (const Vector& rownrs) { TableExprId id; rownr_t nrrow = rownrs.size(); Array arr (IPosition(1,nrrow)); Double* vec = arr.data(); for (rownr_t i=0; i TableExprNodeRep::getColumnComplex (const Vector&) { TableExprNode::throwInvDT ("(getColumnComplex not implemented)"); return Array(); } Array TableExprNodeRep::getColumnDComplex (const Vector& rownrs) { TableExprId id; rownr_t nrrow = rownrs.size(); Array arr (IPosition(1,nrrow)); DComplex* vec = arr.data(); for (rownr_t i=0; i TableExprNodeRep::getColumnString (const Vector& rownrs) { TableExprId id; rownr_t nrrow = rownrs.size(); Array arr (IPosition(1,nrrow)); String* vec = arr.data(); for (rownr_t i=0; ioperType() != OtOR && thisNode->operType() != OtAND) { return this; } // Determine if and which node is a constant. // Exit if no constant. TENShPtr* constNode = &lnode_p; TENShPtr* otherNode = &rnode_p; if (! lnode_p->isConstant()) { if (! rnode_p->isConstant()) { return this; } constNode = &rnode_p; otherNode = &lnode_p; } // Only a constant Scalar can be handled, since arrays can be varying // in size and the result can be important. if ((**constNode).valueType() != VTScalar) { return this; } Bool value = (**constNode)->getBool (0); // For an AND a true constant means the other node determines the result. // So we can replace the AND by that node. // A false results in a constant false when the other operand is a scalar. // So in that case the constant is the result. // For OR the same can be done. if (thisNode->operType() == OtAND) { if (value) { return *otherNode; } else { if ((**otherNode).valueType() != VTScalar) { return *constNode; } } } else { if (value) { if ((**otherNode).valueType() != VTScalar) { return this; } } else { return *otherNode; } } return *constNode; } #endif TENShPtr TableExprNodeRep::replaceConstNode (const TENShPtr& node) { // It might be possible to optimize a node by // replacing a constant right hand by a faster implementation // (in particular for the IN operator). node->optimize(); if (! node->isConstant()) { return node; } // Evaluate the constant subexpression and replace the node. TENShPtr newNode; if (node->valueType() == VTScalar) { switch (node->dataType()) { case NTBool: newNode = std::make_shared(node->getBool(0)); break; case NTInt: newNode = std::make_shared(node->getInt(0)); break; case NTDouble: newNode = std::make_shared(node->getDouble(0)); break; case NTComplex: newNode = std::make_shared(node->getDComplex(0)); break; case NTString: newNode = std::make_shared(node->getString(0)); break; case NTRegex: newNode = std::make_shared(node->getRegex(0)); break; case NTDate: newNode = std::make_shared(node->getDate(0)); break; default: TableExprNode::throwInvDT ("in replaceConstNode"); // should never occur } } else { switch (node->dataType()) { case NTBool: newNode = std::make_shared(node->getArrayBool(0)); break; case NTInt: newNode = std::make_shared(node->getArrayInt(0)); break; case NTDouble: newNode = std::make_shared(node->getArrayDouble(0)); break; case NTComplex: newNode = std::make_shared(node->getArrayDComplex(0)); break; case NTString: newNode = std::make_shared(node->getArrayString(0)); break; case NTDate: newNode = std::make_shared(node->getArrayDate(0)); break; default: TableExprNode::throwInvDT ("in replaceConstNode"); // should never occur } } newNode->setUnit (node->unit()); return newNode; } // ------------------------------ // TableExprNodeBinary functions // ------------------------------ TableExprNodeBinary::TableExprNodeBinary (NodeDataType tp, ValueType vtype, OperType optype, ExprType exprtype) : TableExprNodeRep (tp, vtype, optype, exprtype) {} TableExprNodeBinary::TableExprNodeBinary (NodeDataType tp, const TableExprNodeRep& that, OperType oper) : TableExprNodeRep (that) { dtype_p = tp; optype_p = oper; } void TableExprNodeBinary::show (ostream& os, uInt indent) const { TableExprNodeRep::show (os, indent); if (lnode_p != 0) { lnode_p->show (os, indent+2); } if (rnode_p != 0) { rnode_p->show (os, indent+2); } } void TableExprNodeBinary::flattenTree (std::vector& nodes) { nodes.push_back (this); if (lnode_p) { lnode_p->flattenTree (nodes); } if (rnode_p) { rnode_p->flattenTree (nodes); } } // Check the datatypes and get the common one. // For use with operands. TableExprNodeRep::NodeDataType TableExprNodeBinary::getDT (NodeDataType leftDtype, NodeDataType rightDtype, OperType opt) { // Equal types is mostly fine. if (leftDtype == rightDtype) { if (leftDtype==NTBool || leftDtype==NTDouble || leftDtype==NTComplex || leftDtype==NTString || (leftDtype==NTInt && opt!=OtDivide)) { return leftDtype; } } // If one is an Int, try as Double. if (leftDtype == NTInt) leftDtype = NTDouble; if (rightDtype == NTInt) rightDtype = NTDouble; // Double matches Int and Double. if (leftDtype == NTDouble && rightDtype == NTDouble) { return NTDouble; } // Complex matches Double and Complex. if ((leftDtype == NTComplex && rightDtype == NTDouble) || (leftDtype == NTDouble && rightDtype == NTComplex)) { return NTComplex; } // String and Regex will get Regex if ((leftDtype == NTString && rightDtype == NTRegex) || (leftDtype == NTRegex && rightDtype == NTString)) { return NTRegex; } // A String will be promoted to Date when used with a Date. if (leftDtype == NTDate && rightDtype == NTString) { rightDtype = NTDate; } if (leftDtype == NTString && rightDtype == NTDate) { leftDtype = NTDate; } // A double will be promoted to Date when used with a Date in a comparison. if (opt >= OtEQ && opt <= OtIN) { if (leftDtype == NTDate && rightDtype == NTDouble) { rightDtype = NTDate; } if (leftDtype == NTDouble && rightDtype == NTDate) { leftDtype = NTDate; } } // Date - Date will get Double if (leftDtype == NTDate && rightDtype == NTDate && opt == OtMinus) { return NTDouble; } // Date matches Date; Date+Date is not allowed // Note that date/date or date*date has been catched earlier. if (leftDtype == NTDate && rightDtype == NTDate && opt != OtPlus) { return NTDate; } // Date+Double and Date-Double is allowed if (opt == OtPlus || opt == OtMinus) { if (leftDtype == NTDate && rightDtype == NTDouble) { return NTDate; } } // Double+Date is allowed if (opt == OtPlus) { if (leftDtype == NTDouble && rightDtype == NTDate) { return NTDate; } } TableExprNode::throwInvDT("TableExprNodeBinary::getDT cannot combine " "arguments with data type " + typeString(leftDtype) + " and " + typeString(rightDtype)); return NTComplex; // compiler satisfaction } TableExprNodeRep TableExprNodeBinary::getCommonTypes (const TENShPtr& left, const TENShPtr& right, OperType opt) { ValueType leftVtype = left->valueType(); ValueType rightVtype = right->valueType(); // Check that the value type is VTScalar and/or VTArray. if ((leftVtype != VTArray && leftVtype != VTScalar) || (rightVtype != VTArray && rightVtype != VTScalar)) { throw (TableInvExpr ("Operand has to be a scalar or an array")); } // The resulting value type is Array if one of the operands is array. // Otherwise it is scalar. ValueType vtype; if (leftVtype == VTArray || rightVtype == VTArray) { vtype = VTArray; } else { vtype = VTScalar; } NodeDataType leftDtype = left->dataType(); NodeDataType rightDtype = right->dataType(); NodeDataType dtype = getDT (leftDtype, rightDtype, opt); ArgType atype = ArrArr; // Set the argument type in case arrays are involved. // Its setting is not important if 2 scalars are involved. if (leftVtype == VTScalar) { atype = ScaArr; } if (rightVtype == VTScalar) { atype = ArrSca; } // Get dimensionality and shape of result. IPosition shape; Int ndim = -1; if (leftVtype == VTScalar && rightVtype == VTScalar) { ndim = 0; } else { // Check if the 2 operands have matching dimensionality and shape. // This can only be done if they are fixed. // Also determine the resulting dimensionality and shape. Int leftNdim = left->ndim(); Int rightNdim = right->ndim(); if (leftNdim > 0) { ndim = leftNdim; if (rightNdim > 0 && leftNdim != rightNdim) { throw (TableInvExpr ("Mismatching dimensionality of operands")); } } else if (rightNdim > 0) { ndim = rightNdim; } IPosition leftShape = left->shape(); IPosition rightShape = right->shape(); leftNdim = leftShape.size(); rightNdim = rightShape.size(); if (leftNdim > 0) { shape = leftShape; if (rightNdim > 0 && !leftShape.isEqual (rightShape)) { throw (TableInvExpr ("Mismatching shape of operands")); } } else if (rightNdim > 0) { shape = rightShape; } } // The result is constant when both operands are constant. ExprType extype = Variable; if (left->isConstant() && right->isConstant()) { extype = Constant; } // Determine from which table the expression is coming // and whether the tables match. return TableExprNodeRep (dtype, vtype, opt, atype, extype, ndim, shape); } void TableExprNodeBinary::setChildren (const TENShPtr& left, const TENShPtr& right, Bool adapt) { lnode_p = left; rnode_p = right; if (right) { // NTRegex will always be placed in the right node. if (left->dataType() == NTRegex) { lnode_p = right; rnode_p = left; } else { // If expression with date and double or string, convert to date. if (left->dataType() == NTDate) { if (right->dataType() == NTString) { TableExprNode dNode = datetime (right); rnode_p = dNode.getRep(); } else if (right->isReal()) { TableExprNode dNode = mjdtodate (right); rnode_p = dNode.getRep(); } } if (right->dataType() == NTDate) { if (left->dataType() == NTString) { TableExprNode dNode = datetime (left); lnode_p = dNode.getRep(); } else if (left->isReal()) { TableExprNode dNode = mjdtodate (left); lnode_p = dNode.getRep(); } } // date-date results in double, so convert if needed. if (dataType() == NTDouble) { if (left->dataType() == NTDate) { TableExprNode dNode = mjd (left); lnode_p = dNode.getRep(); } if (right->dataType() == NTDate) { TableExprNode dNode = mjd (right); rnode_p = dNode.getRep(); } } } } if (adapt) { // Check and adapt units. handleUnits(); // Adapt data types as needed. adaptDataTypes(); } } const Unit& TableExprNodeBinary::makeEqualUnits (const TENShPtr& left, TENShPtr& right) { // The first real unit is chosen as the result unit. const Unit* unit = &(left->unit()); if (right) { if (unit->empty()) { unit = &(right->unit()); } else if (! right->unit().empty()) { TableExprNodeUnit::adaptUnit (right, *unit); } } return *unit; } void TableExprNodeBinary::handleUnits() { const Unit& resUnit = makeEqualUnits (lnode_p, rnode_p); // A comparison has no units, so only set result unit if not bool. if (dataType() != NTBool) { setUnit (resUnit); } } void TableExprNodeBinary::adaptDataTypes() { // Convert data type of a constant // from Double to DComplex if: // - there are 2 nodes // - data types are not equal // - conversion from Int or Double can be done if (!rnode_p || lnode_p->dataType() == rnode_p->dataType()) { return; } // Determine if and which node is a constant. TENShPtr* constNode = &lnode_p; TENShPtr* otherNode = &rnode_p; if (! lnode_p->isConstant()) { if (! rnode_p->isConstant()) { return; } constNode = &rnode_p; otherNode = &lnode_p; } // Only scalars and arrays can be converted. ValueType vtype = (*constNode)->valueType(); if (vtype != VTScalar && vtype != VTArray) { return; } // The only possible conversion is from Int or Double to Double or DComplex. NodeDataType newType = NTDouble; if ((*otherNode)->dataType() == NTDouble) { if ((*constNode)->dataType() != NTInt) { return; } } else if ((*otherNode)->dataType() == NTComplex) { newType = NTComplex; if (((*constNode)->dataType() != NTInt) && ((*constNode)->dataType() != NTDouble)) { return; } } else { return; } // Yeah, we have something to convert. #if defined(AIPS_TRACE) cout << "constant converted" << endl; #endif TENShPtr newNode; if (vtype == VTScalar) { if (newType == NTDouble) { newNode = std::make_shared((*constNode)->getDouble(0)); } else { newNode = std::make_shared((*constNode)->getDouble(0)); } } else { if (newType == NTDouble) { newNode = std::make_shared ((*constNode)->getArrayDouble(0)); } else { newNode = std::make_shared ((*constNode)->getArrayDouble(0)); } } newNode->setUnit ((*constNode)->unit()); *constNode = newNode; } // ---------------------------- // TableExprNodeMulti functions // ---------------------------- TableExprNodeMulti::TableExprNodeMulti (NodeDataType tp, ValueType vtype, OperType oper, const TableExprNodeRep& source) : TableExprNodeRep (tp, vtype, oper, source.exprType()) {} void TableExprNodeMulti::show (ostream& os, uInt indent) const { TableExprNodeRep::show (os, indent); for (uInt j=0; jshow (os, indent+2); } } } void TableExprNodeMulti::flattenTree (std::vector& nodes) { nodes.push_back (this); for (uInt j=0; jflattenTree (nodes); } } } std::shared_ptr TableExprNodeRep::makeGroupAggrFunc() { throw AipsError ("TableExprNodeRep::makeGroupAggrFunc should not be called"); } Bool TableExprNodeRep::isLazyAggregate() const { return True; } uInt TableExprNodeMulti::checkNumOfArg (uInt low, uInt high, const vector& nodes) { if (nodes.size() < low) { throw (TableInvExpr("too few function arguments")); } else if (nodes.size() > high) { throw (TableInvExpr("too many function arguments")); } return nodes.size(); } TableExprNodeRep::NodeDataType TableExprNodeMulti::checkDT (Block& dtypeOper, NodeDataType dtIn, NodeDataType dtOut, const vector& nodes, Bool dateConv) { uInt nelem = nodes.size(); dtypeOper.resize (nelem); dtypeOper.set (dtIn); // NTAny means that it can be any type. // An output of NTAny means that the types have to match. if (dtIn == NTAny) { if (dtOut != NTAny) { // Make sure output type is not generic. AlwaysAssert (dtOut!=NTNumeric && dtOut!=NTReal && dtOut!=NTDouCom, AipsError); return dtOut; } // Input data type is first one. Set to NTNumeric if numeric, so // numeric data types can be mixed. dtIn = nodes[0]->dataType(); if (dtIn == NTInt || dtIn == NTDouble || dtIn == NTComplex) { dtIn = NTNumeric; } } NodeDataType resultType = dtIn; if (dtIn == NTNumeric) { // NTNumeric -> dtIn must be NTComplex or NTDouble or NTInt // and set resultType to the highest type of dtIn resultType = (dtOut==NTDouCom ? NTDouble : NTInt); for (uInt i=0; idataType() == NTComplex) { resultType = NTComplex; } else if (nodes[i]->dataType() == NTDouble) { if (resultType != NTComplex) { resultType = NTDouble; } } else if (nodes[i]->dataType() != NTInt) { TableExprNode::throwInvDT("function argument is not numeric"); } } } else if (dtIn == NTReal) { // NTReal -> dtIn must be NTDouble or NTInt // and set resultType to the highest type of dtIn resultType = (dtOut==NTDouCom ? NTDouble : NTInt); for (uInt i=0; idataType() == NTDouble) { resultType = NTDouble; } else if (nodes[i]->dataType() != NTInt) { TableExprNode::throwInvDT("function argument is not real"); } } } else { // Data types of the nodes must match dtIn for (uInt i=0; idataType() != dtIn) { if (dateConv && dtIn == NTDate) { if (nodes[i]->dataType() != NTString && nodes[i]->dataType() != NTDouble && nodes[i]->dataType() != NTInt) { TableExprNode::throwInvDT("function argument is not " "date, string or real"); } } else { TableExprNode::throwInvDT("function argument is not " + typeString(dtIn)); } } } } if (dtOut == NTReal) { if (resultType == NTComplex) { resultType = NTDouble; } } else if (dtOut == NTDouCom) { if (resultType == NTInt) { resultType = NTDouble; } } else if (dtOut != NTNumeric && dtOut != NTAny) { resultType = dtOut; } return resultType; } String TableExprNodeRep::typeString (NodeDataType type) { switch (type) { case NTBool: return "Bool"; case NTInt: return "Integer"; case NTDouble: return "Double"; case NTComplex: return "Complex"; case NTString: return "String"; case NTRegex: return "Regex"; case NTDate: return "DateTime"; case NTReal: return "Real"; case NTDouCom: return "Double/Complex"; case NTNumeric: return "Numeric"; case NTAny: return "Any"; } throw AipsError("TableExprNodeRep::typeString NodeDataType"); } String TableExprNodeRep::typeString (ValueType type) { switch (type) { case VTScalar: return "Scalar"; case VTArray: return "Array"; case VTRecord: return "Record"; case VTSetElem: return "SetElement"; case VTSet: return "Set"; case VTIndex: return "Index"; } throw AipsError("TableExprNodeRep::typeString ValueType"); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/ExprNodeRep.h000066400000000000000000000645501476623553700177550ustar00rootroot00000000000000//# ExprNodeRep.h: Abstract base class for a node in a table column expression tree //# Copyright (C) 1994,1995,1996,1997,1998,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_EXPRNODEREP_H #define TABLES_EXPRNODEREP_H //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableExprNode; class TableExprNodeColumn; class TableExprGroupFuncBase; template class Block; //# Define a shared pointer to the Rep class. class TableExprNodeRep; typedef std::shared_ptr TENShPtr; // // Class to handle a Regex or StringDistance. // // // // // //# Classes you should understand before using this one. //
      • Regex //
      • StringDistance // // // A StringDistance (Levensthein distance) in TaQL is given in the same way // as a Regex. This class is needed to have a single object in the parse tree // objects containing them (in class TableExprNodeConstRegex). // class TaqlRegex { public: // Construct from a regex. explicit TaqlRegex (const Regex& regex) : itsRegex(regex) {} // Construct from a StringDistance. explicit TaqlRegex (const StringDistance& dist) : itsDist(dist) {} // Does the regex or maximum string distance match? Bool match (const String& str) const { return itsRegex.regexp().empty() ? itsDist.match(str) : str.matches(itsRegex); } // Return the regular expression. const Regex& regex() const { return itsRegex; } private: Regex itsRegex; StringDistance itsDist; }; // // Class to connect a Table and its alias name // // // // // // This class connects a Table object to its alias name used in a TaQL command. // If no alias is given, the table name is used as such. // It also tells if the Table is used as a join table. // class TableExprInfo { public: // Construct from a table and its alias. explicit TableExprInfo (const Table& table = Table(), const String& alias = String(), Bool isJoinTable = False); // Get the Table object. const Table& table() const { return itsTable; } // Get the alias. const String& alias() const { return itsAlias; } // Is the table a join table? Bool isJoinTable() const { return itsIsJoinTable; } // Apply a selection of row numbers to the Table. void apply (const Vector& rownrs); private: Table itsTable; String itsAlias; Bool itsIsJoinTable; }; // // Abstract base class for a node in a table column expression tree // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // TableExprNodeRep is the (abstract) REPresentation of a node in a table // expression tree. // // // TableExprNodeRep is the base class for all nodes in a table // expression tree. It is used by the handle class TableExprNode. //

        // The objects of this class are reference-counted to make it possible // that the same object is reused. // // // TableExprNodeRep and its derivations store a table select expression // before actually evaluating it. It is also possible that the classes // are used by the table expression parser defined in TableParse and // TableGram. //
        // For each operator a special derived class is implemented. // Another approach could have been to store the operator as // a flag and switch on that. However, that causes extra overhead // and the C++ virtual function mechanism is designed for // these purposes. //
        // //# A List of bugs, limitations, extensions or planned refinements. //

      • add selection by comparing with a set of values // class TableExprNodeRep { public: // Define the data types of a node. enum NodeDataType { NTBool, NTInt, NTDouble, NTComplex, NTString, NTRegex, NTDate, NTReal, //# NTInt or NTDouble NTDouCom, //# NTDouble or NTComplex NTNumeric, //# NTInt, NTDouble, or NTComplex NTAny //# Any data type }; // Define the value types. enum ValueType { VTScalar, VTArray, VTRecord, VTSetElem, VTSet, VTIndex }; // Define the operator types. // LE and LT are handled as GE and GT with swapped operands. enum OperType {OtPlus, OtMinus, OtTimes, OtDivide, OtModulo, OtBitAnd, OtBitOr, OtBitXor, OtBitNegate, OtEQ, OtGE, OtGT, OtNE, OtIN, OtAND, OtOR, OtNOT, OtMIN, OtColumn, OtField, OtLiteral, OtFunc, OtSlice, OtUndef, OtRownr, OtRandom }; // Define the value types of the 2 arguments when arrays are involved. enum ArgType { NoArr, ArrArr, ArrSca, ScaArr }; // Define (sub-)expression type enum ExprType { // A constant subexpression which can be evaluated immediately. Constant, // A variable (i.e. row dependent) subexpression which // has to be evaluated for each table row. Variable // An expensive constant subexpression which should only be // evaluated when needed (e.g. a subquery). // Lazy }; // Construct a node. TableExprNodeRep (NodeDataType, ValueType, OperType, ArgType, ExprType, Int ndim, const IPosition& shape); // This constructor is called from the derived TableExprNodeRep. TableExprNodeRep (NodeDataType, ValueType, OperType, ExprType); // Copy constructor. TableExprNodeRep (const TableExprNodeRep&) = default; // Assign to a TableExprNodeRep cannot be done. TableExprNodeRep& operator= (const TableExprNodeRep&) = delete; // The destructor deletes all the underlying TableExprNode objects. virtual ~TableExprNodeRep() = default; // Is the node an aggegation node. // The default implementation returns False. virtual Bool isAggregate() const; // Get the table info. // The default implementation returns an info object with a null table. virtual TableExprInfo getTableInfo() const; // Try to optimize the node (meant for the right hand of the IN operator). // The default implementation does nothing. virtual void optimize(); // Do not apply the selection. virtual void disableApplySelection(); // Re-create the column object for a selection of rows. // The default implementation does nothing. virtual void applySelection (const Vector& rownrs); // Get the unit conversion factor. // Default 1 is returned. virtual Double getUnitFactor() const; // Flatten the node tree by adding the node and its children to the vector. virtual void flattenTree (std::vector&); // Create the correct immediate aggregate function object. // The default implementation throws an exception, because it should // only be called for TableExprAggrNode(Array). virtual std::shared_ptr makeGroupAggrFunc(); // Is the aggregate function a lazy or an immediate one? // The default implementation returns True // (because all UDF aggregate functions have to be lazy). virtual Bool isLazyAggregate() const; // Get a scalar value for this node in the given row. // The appropriate functions are implemented in the derived classes and // will usually invoke the get in their children and apply the // operator on the resulting values. // virtual Bool getBool (const TableExprId& id); virtual Int64 getInt (const TableExprId& id); virtual Double getDouble (const TableExprId& id); virtual DComplex getDComplex (const TableExprId& id); virtual String getString (const TableExprId& id); virtual TaqlRegex getRegex (const TableExprId& id); virtual MVTime getDate (const TableExprId& id); // // Get an array value for this node in the given row. // The appropriate functions are implemented in the derived classes and // will usually invoke the get in their children and apply the // operator on the resulting values. // virtual MArray getArrayBool (const TableExprId& id); virtual MArray getArrayInt (const TableExprId& id); virtual MArray getArrayDouble (const TableExprId& id); virtual MArray getArrayDComplex (const TableExprId& id); virtual MArray getArrayString (const TableExprId& id); virtual MArray getArrayDate (const TableExprId& id); // // General get functions for template purposes. // void get (const TableExprId& id, Bool& value) { value = getBool (id); } void get (const TableExprId& id, Int64& value) { value = getInt (id); } void get (const TableExprId& id, Double& value) { value = getDouble (id); } void get (const TableExprId& id, DComplex& value) { value = getDComplex (id); } void get (const TableExprId& id, MVTime& value) { value = getDate (id); } void get (const TableExprId& id, String& value) { value = getString (id); } void get (const TableExprId& id, MArray& value) { value = getArrayBool (id); } void get (const TableExprId& id, MArray& value) { value = getArrayInt (id); } void get (const TableExprId& id, MArray& value) { value = getArrayDouble (id); } void get (const TableExprId& id, MArray& value) { value = getArrayDComplex (id); } void get (const TableExprId& id, MArray& value) { value = getArrayDate (id); } void get (const TableExprId& id, MArray& value) { value = getArrayString (id); } // // Get a value as an array, even it it is a scalar. // This is useful if one could give an argument as scalar or array. // MArray getBoolAS (const TableExprId& id); MArray getIntAS (const TableExprId& id); MArray getDoubleAS (const TableExprId& id); MArray getDComplexAS (const TableExprId& id); MArray getStringAS (const TableExprId& id); MArray getDateAS (const TableExprId& id); // // Does a set or array contain the value? // The default implementation assumes the set is a single scalar, // thus tests if it is equal to the given value. // virtual Bool contains (const TableExprId& id, Bool value); virtual Bool contains (const TableExprId& id, Int64 value); virtual Bool contains (const TableExprId& id, Double value); virtual Bool contains (const TableExprId& id, DComplex value); virtual Bool contains (const TableExprId& id, String value); virtual Bool contains (const TableExprId& id, MVTime value); virtual MArray contains (const TableExprId& id, const MArray& value); virtual MArray contains (const TableExprId& id, const MArray& value); virtual MArray contains (const TableExprId& id, const MArray& value); virtual MArray contains (const TableExprId& id, const MArray& value); virtual MArray contains (const TableExprId& id, const MArray& value); virtual MArray contains (const TableExprId& id, const MArray& value); // // Get the number of rows in the table associated with this expression. // One is returned if the expression is a constant or no table is // associated with it. rownr_t nrow(); // Get the data type of the column. // It returns True when it could set the data type (which it can // if the expression is a scalar column or a constant array column pixel). // Otherwise it returns False. virtual Bool getColumnDataType (DataType&) const; // Get the value of the expression evaluated for the entire column. // The data of function called should match the data type as // returned by function getColumnDataType. // virtual Array getColumnBool (const Vector& rownrs); virtual Array getColumnuChar (const Vector& rownrs); virtual Array getColumnShort (const Vector& rownrs); virtual Array getColumnuShort (const Vector& rownrs); virtual Array getColumnInt (const Vector& rownrs); virtual Array getColumnuInt (const Vector& rownrs); virtual Array getColumnInt64 (const Vector& rownrs); virtual Array getColumnFloat (const Vector& rownrs); virtual Array getColumnDouble (const Vector& rownrs); virtual Array getColumnComplex (const Vector& rownrs); virtual Array getColumnDComplex (const Vector& rownrs); virtual Array getColumnString (const Vector& rownrs); // // Convert the tree to a number of range vectors which at least // select the same things. // This function is very useful to convert the expression to // some intervals covering the select expression. This can // be used to do a rough fast selection via an index and do the // the slower final selection on that much smaller subset. // The function can only convert direct comparisons of columns // with constants (via ==, !=, >, >=, < or <=) and their combinations // using && or ||. virtual void ranges (Block&); // Get the data type of the derived TableExprNode object. // This is the data type of the resulting value. E.g. a compare // of 2 numeric values results in a Bool, thus the data type // of, say, TableExprNodeEQ is always Bool. // Function getInternalDT gives the internal data type, thus in // the example above the data type of T. NodeDataType dataType() const; // Is the data type real (i.e., integer or double)? Bool isReal() const; // Get the value type. ValueType valueType() const; // Set the value type. void setValueType (ValueType vtype); // Get the operator type. OperType operType() const; // Get the expression type. ExprType exprType() const; // Is the expression a constant? Bool isConstant() const; // Get the unit. const Unit& unit() const; // Set the unit. // It also sets the datatype to NTDouble if it is NTInt. void setUnit (const Unit& unit); // Get the attributes. const Record& attributes() const; // Set the attributes. void setAttributes (const Record&); // Get the fixed dimensionality (same for all rows). Int ndim() const; // Get the fixed shape (same for all rows). const IPosition& shape() const; // Get the shape for the given row. // It returns the fixed shape if defined, otherwise getShape(id). const IPosition& shape (const TableExprId& id); // Is the value in the given row defined? // The default implementation returns True. virtual Bool isDefined (const TableExprId& id); // Show the expression tree. virtual void show (ostream&, uInt indent) const; // Replace a node with a constant expression by node with its value. static TENShPtr replaceConstNode (const TENShPtr& node); // Let a set node convert itself to the given unit. // The default implementation does nothing. virtual void adaptSetUnits (const Unit&); // Create a range object from a column and an interval. static void createRange (Block&, TableExprNodeColumn*, Double start, Double end); // Create a empty range object. static void createRange (Block&); // Convert a NodeDataType to a string. static String typeString (NodeDataType); // Convert a ValueType to a string. static String typeString (ValueType); protected: NodeDataType dtype_p; //# data type of the operation ValueType vtype_p; //# value type of the result OperType optype_p; //# operator type ArgType argtype_p; //# argument types ExprType exprtype_p; //# Constant or Variable Int ndim_p; //# Fixed dimensionality of node values //# -1 = variable dimensionality IPosition shape_p; //# Fixed shape of node values Unit unit_p; //# Unit of the values Record attributes_p; //# Possible attributes (for UDFs) // Get the shape for the given row. virtual const IPosition& getShape (const TableExprId& id); // Set expression type to Variable if node is Variable. void fillExprType (const TableExprNodeRep* node); // If the node is constant, it is evaluated and replaced by // the appropriate TableExprNodeConst object. // If not constant, it calls the virtual ConvertConstChild function // which can convert a constant child if appropriate. static TENShPtr convertNode (const TENShPtr& thisNode, Bool convertConstType); }; // // Abstract base class for a node having 0, 1, or 2 child nodes. // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeRep // // // TableExprNodeBinary is a node in the table expression tree // representing a binary node (i.e. having up to 2 operands). // // // TableExprNodeBinary is the abstract base class for all nodes in a table // expression tree using up to 2 operands. // It is used as the base class for the node classes representing // operator +, -, etc.. // // // This class contains the common functionality for the classes // representing a binary (or unary) operator. // //# //# A List of bugs, limitations, extensions or planned refinements. //#
      • to be filled in //# class TableExprNodeBinary : public TableExprNodeRep { public: // Constructor TableExprNodeBinary (NodeDataType, ValueType, OperType, ExprType); TableExprNodeBinary (NodeDataType, const TableExprNodeRep&, OperType); // Destructor ~TableExprNodeBinary() override = default; // Show the expression tree. void show (ostream&, uInt indent) const override; // Flatten the node tree by adding the node and its children to the vector. void flattenTree (std::vector&) override; // Check the data types and get the common one. static NodeDataType getDT (NodeDataType leftDtype, NodeDataType rightDype, OperType operType); // Check the data and value types and get the common one. static TableExprNodeRep getCommonTypes (const TENShPtr& left, const TENShPtr& right, OperType operType); // Set the children. // If needed, their properties like data type and unit are adapted. void setChildren (const TENShPtr& left, const TENShPtr& right, Bool adapt=True); // Handle the units of the children and possibly set the parent's unit. // The default implementation make the units of the children equal and // set the parent unit to that unit if the parent is not a Bool value. virtual void handleUnits(); // If one of the children is a constant, convert its data type // to that of the other operand. This avoids that conversions are // done for each get. void adaptDataTypes(); // Get the child nodes. // const TENShPtr& getLeftChild() const { return lnode_p; } const TENShPtr& getRightChild() const { return rnode_p; } // protected: // Make the units equal. // Replace the right node if needed. static const Unit& makeEqualUnits (const TENShPtr& left, TENShPtr& right); TENShPtr lnode_p; //# left operand TENShPtr rnode_p; //# right operand }; // // Abstract base class for a node having multiple child nodes. // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeRep // // // TableExprNodeMulti is a node in the table expression tree // which can have MULTIple child nodes. // // // TableExprNodeMulti is the abstract base class for all nodes in a table // expression tree using multiple operands. // It is used as the base class for the node classes representing // functions, sets, indices, etc.. // // // This class contains the common functionality for the classes // representing a node with multiple operands. // //# //# A List of bugs, limitations, extensions or planned refinements. //#
      • to be filled in //# class TableExprNodeMulti : public TableExprNodeRep { public: // Constructor TableExprNodeMulti (NodeDataType, ValueType, OperType, const TableExprNodeRep& source); // Destructor ~TableExprNodeMulti() override = default; // Show the expression tree. void show (ostream&, uInt indent) const override; // Flatten the node tree by adding the node and its children to the vector. void flattenTree (std::vector&) override; // Check number of arguments // low <= number_of_args <= high // It throws an exception if wrong number of arguments. static uInt checkNumOfArg (uInt low, uInt high, const std::vector& nodes); // Get the child nodes. const std::vector& getChildren() const { return operands_p; } // Check datatype of nodes and return output type. // It also sets the expected data type of the operands (from dtIn). // Conversion of Int,Double.String to Date is by default possible. static NodeDataType checkDT (Block& dtypeOper, NodeDataType dtIn, NodeDataType dtOut, const std::vector& nodes, Bool dateConv=True); protected: std::vector operands_p; }; //# Get the data type of the node. inline TableExprNodeRep::NodeDataType TableExprNodeRep::dataType() const { return dtype_p; } inline Bool TableExprNodeRep::isReal() const { return dtype_p==NTInt || dtype_p==NTDouble; } //# Get the value type of the node. inline TableExprNodeRep::ValueType TableExprNodeRep::valueType() const { return vtype_p; } //# Set the value type of the node. inline void TableExprNodeRep::setValueType (TableExprNodeRep::ValueType vtype) { vtype_p = vtype; } //# Get the operator type of the node. inline TableExprNodeRep::OperType TableExprNodeRep::operType() const { return optype_p; } //# Get the expression type of the node. inline TableExprNodeRep::ExprType TableExprNodeRep::exprType() const { return exprtype_p; } //# Is the expression a constant? inline Bool TableExprNodeRep::isConstant() const { return (exprtype_p == Constant); } //# Get the unit of the node. inline const Unit& TableExprNodeRep::unit() const { return unit_p; } inline const Record& TableExprNodeRep::attributes() const { return attributes_p; } inline void TableExprNodeRep::setAttributes (const Record& attributes) { attributes_p = attributes; } //# Get the fixed dimensionality of the node. inline Int TableExprNodeRep::ndim() const { return ndim_p; } //# Get the fixed shape of the node. inline const IPosition& TableExprNodeRep::shape() const { return shape_p; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/ExprNodeSet.cc000066400000000000000000000375501476623553700201200ustar00rootroot00000000000000//# ExprNodeSet.cc: Classes representing a set in table select expression //# Copyright (C) 1997,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableExprNodeSet::TableExprNodeSet() : TableExprNodeRep (NTNumeric, VTSet, OtUndef, Constant), itsSingle (True), itsDiscrete (True), itsBounded (True), itsCheckTypes (True) {} TableExprNodeSet::TableExprNodeSet (const IPosition& indices) : TableExprNodeRep (NTInt, VTSet, OtUndef, Constant), itsSingle (True), itsDiscrete (True), itsBounded (True), itsCheckTypes (False) { uInt n = indices.size(); itsElems.resize (n); for (uInt i=0; i (TableExprNode (Int64(indices(i)))); } } TableExprNodeSet::TableExprNodeSet (const Slicer& indices) : TableExprNodeRep (NTInt, VTSet, OtUndef, Constant), itsSingle (False), itsDiscrete (True), itsBounded (True), itsCheckTypes (False) { uInt n = indices.ndim(); itsElems.resize (n); for (uInt i=0; i(start, end, incr); } } TableExprNodeSet::TableExprNodeSet (const Vector& rownrs, const TableExprNodeSet& set) : TableExprNodeRep (set.dataType(), VTSet, OtUndef, Constant), itsElems (rownrs.size() * set.size()), itsSingle (set.isSingle()), itsDiscrete (set.isDiscrete()), itsBounded (set.isBounded()), itsCheckTypes (False) { // Fill in all values. size_t nrel = set.size(); for (rownr_t i=0; ievaluate (rownrs[i]); } } setUnit (set.unit()); } TableExprNodeSet::TableExprNodeSet (const TableExprNodeSet& that) : TableExprNodeRep (that), itsElems (that.itsElems), itsSingle (that.itsSingle), itsDiscrete (that.itsDiscrete), itsBounded (that.itsBounded), itsCheckTypes (that.itsCheckTypes) {} TableExprNodeSet::~TableExprNodeSet() {} void TableExprNodeSet::add (const TENSEBShPtr& elemIn, Bool adaptType) { // Convert a constant mid-width interval to a normal interval. TENSEBShPtr elem (elemIn); if (elem->isConstant() && elem->isMidWidth()) { elem = elem->evaluate (TableExprId(0)); } size_t n = itsElems.size(); itsElems.resize (n+1); itsElems[n] = elem; // Set and adapt unit as needed. if (unit().empty()) { setUnit (elem->unit()); } // See if the set properties change. if (! elem->isSingle()) { itsSingle = False; if (! elem->isDiscrete()) { itsDiscrete = False; itsBounded = False; } else { if (elem->end() == 0) { // Note that an undefined start defaults to 0, this is bounded. itsBounded = False; } } } if (n == 0) { dtype_p = elem->dataType(); } else if (adaptType) { // Determine the highest data type. // Note: using OtEQ works well for all types (including dates). dtype_p = TableExprNodeBinary::getDT (dtype_p, elem->dataType(), OtEQ); } fillExprType (itsElems[n].get()); } void TableExprNodeSet::adaptSetUnits (const Unit& unit) { if (! unit.empty()) { for (size_t i=0; iadaptSetUnits (unit); } setUnit (unit); } } void TableExprNodeSet::checkEqualDataTypes() const { if (itsCheckTypes) { for (size_t i=0; idataType() != dtype_p) { throw TableInvExpr ("Set elements must have equal data types"); } } } } void TableExprNodeSet::show (ostream& os, uInt indent) const { TableExprNodeRep::show (os, indent); for (size_t j=0; jshow (os, indent+2); } } void TableExprNodeSet::flattenTree (std::vector& nodes) { nodes.push_back (this); for (size_t j=0; jflattenTree (nodes); } } Bool TableExprNodeSet::hasArrays() const { //# Check if a value is an array? size_t n = itsElems.size(); for (size_t i=0; ivalueType() == VTArray) { return True; } if (elem.end() && elem.end()->valueType() == VTArray) { return True; } if (elem.increment() && elem.increment()->valueType() == VTArray) { return True; } } return False; } TENShPtr TableExprNodeSet::setOrArray() const { // A set where elements have different unit types cannot be turned // into an array. if (! unit().empty()) { Quantity q(1., unit()); size_t n = size(); for (size_t i=0; iunit().empty()) { if (! q.isConform (itsElems[i]->unit())) { return TENShPtr(new TableExprNodeSet (*this)); } } } // No different units, so adapt elements to the unit. for (size_t i=0; iadaptSetUnits (unit()); } } // If discrete, all start values should be filled in. if (itsDiscrete) { size_t n = size(); for (size_t i=0; istart() == 0) { throw (TableInvExpr ("no start value in discrete interval")); } } } // If the set is bounded, it can be converted to an array. if (itsBounded) { // If it is const, that can be done immediately. if (isConstant()) { return toConstArray(); } // Set ndim and shape if those are constant. } auto set = std::make_shared(*this); if (itsBounded) { // Set the type to VTArray; the getArray functions // will convert the set to an array for each row. // Set the shape and/or dimensionaly if constant. set->setValueType (VTArray); set->setShape(); } return set; } void TableExprNodeSet::setShape() { // Only sets with single elements can have a size. if (!itsSingle) { return; } // Scalar elements form a vector. if (!hasArrays()) { ndim_p = 1; shape_p = IPosition (1, size()); } // See if all elements have the same shape. // If not, leave the shape empty. // Do the same for dimensionality. IPosition shp (itsElems[0]->shape()); uInt ndim = shp.size(); for (size_t i=1; ishape()); if (!shp2.isEqual(shp)) { shp.resize(0); } if (shp2.size() != ndim) { ndim = 0; } } // The set has one more axis. if (ndim > 0) { ndim_p = ndim+1; } if (!shp.empty()) { shape_p = shp; shape_p.append (IPosition(1,size())); } } TENShPtr TableExprNodeSet::toConstArray() const { // Construct the correct const array object. TENShPtr tsnptr; switch (dataType()) { case NTBool: tsnptr = std::make_shared(toArray(0)); break; case NTInt: tsnptr = std::make_shared(toArray(0)); break; case NTDouble: tsnptr = std::make_shared(toArray(0)); break; case NTComplex: tsnptr = std::make_shared(toArray(0)); break; case NTString: tsnptr = std::make_shared(toArray(0)); break; case NTDate: tsnptr = std::make_shared(toArray(0)); break; default: TableExprNode::throwInvDT ("TableExprNodeSet::toConstArray"); } tsnptr->setUnit (unit()); return tsnptr; } MArray TableExprNodeSet::getArrayBool (const TableExprId& id) { return toArray (id); } MArray TableExprNodeSet::getArrayInt (const TableExprId& id) { return toArray (id); } MArray TableExprNodeSet::getArrayDouble (const TableExprId& id) { return toArray (id); } MArray TableExprNodeSet::getArrayDComplex (const TableExprId& id) { return toArray (id); } MArray TableExprNodeSet::getArrayString (const TableExprId& id) { return toArray (id); } MArray TableExprNodeSet::getArrayDate (const TableExprId& id) { return toArray (id); } Bool TableExprNodeSet::contains (const TableExprId& id, Bool value) { Bool result = False; size_t n = itsElems.size(); for (size_t i=0; imatchBool (&result, &value, 1, id); if (result) break; } return result; } Bool TableExprNodeSet::contains (const TableExprId& id, Int64 value) { Bool result = False; size_t n = itsElems.size(); for (size_t i=0; imatchInt (&result, &value, 1, id); if (result) break; } return result; } Bool TableExprNodeSet::contains (const TableExprId& id, Double value) { Bool result = False; size_t n = itsElems.size(); for (size_t i=0; imatchDouble (&result, &value, 1, id); if (result) break; } return result; } Bool TableExprNodeSet::contains (const TableExprId& id, DComplex value) { Bool result = False; size_t n = itsElems.size(); for (size_t i=0; imatchDComplex (&result, &value, 1, id); if (result) break; } return result; } Bool TableExprNodeSet::contains (const TableExprId& id, String value) { Bool result = False; size_t n = itsElems.size(); for (size_t i=0; imatchString (&result, &value, 1, id); if (result) break; } return result; } Bool TableExprNodeSet::contains (const TableExprId& id, MVTime value) { Bool result = False; size_t n = itsElems.size(); for (size_t i=0; imatchDate (&result, &value, 1, id); if (result) break; } return result; } MArray TableExprNodeSet::contains (const TableExprId& id, const MArray& value) { Array result(value.shape()); result.set (False); Bool deleteIn, deleteOut; const Bool* in = value.array().getStorage (deleteIn); Bool* out = result.getStorage (deleteOut); size_t nval = value.size(); size_t n = itsElems.size(); for (size_t i=0; imatchBool (out, in, nval, id); } value.array().freeStorage (in, deleteIn); result.putStorage (out, deleteOut); return MArray (result, value.mask()); } MArray TableExprNodeSet::contains (const TableExprId& id, const MArray& value) { Array result(value.shape()); result.set (False); Bool deleteIn, deleteOut; const Int64* in = value.array().getStorage (deleteIn); Bool* out = result.getStorage (deleteOut); size_t nval = value.size(); size_t n = itsElems.size(); for (size_t i=0; imatchInt (out, in, nval, id); } value.array().freeStorage (in, deleteIn); result.putStorage (out, deleteOut); return MArray (result, value.mask()); } MArray TableExprNodeSet::contains (const TableExprId& id, const MArray& value) { Array result(value.shape()); result.set (False); Bool deleteIn, deleteOut; const Double* in = value.array().getStorage (deleteIn); Bool* out = result.getStorage (deleteOut); size_t nval = value.size(); size_t n = itsElems.size(); for (size_t i=0; imatchDouble (out, in, nval, id); } value.array().freeStorage (in, deleteIn); result.putStorage (out, deleteOut); return MArray (result, value.mask()); } MArray TableExprNodeSet::contains (const TableExprId& id, const MArray& value) { Array result(value.shape()); result.set (False); Bool deleteIn, deleteOut; const DComplex* in = value.array().getStorage (deleteIn); Bool* out = result.getStorage (deleteOut); size_t nval = value.size(); size_t n = itsElems.size(); for (size_t i=0; imatchDComplex (out, in, nval, id); } value.array().freeStorage (in, deleteIn); result.putStorage (out, deleteOut); return MArray (result, value.mask()); } MArray TableExprNodeSet::contains (const TableExprId& id, const MArray& value) { Array result(value.shape()); result.set (False); Bool deleteIn, deleteOut; const String* in = value.array().getStorage (deleteIn); Bool* out = result.getStorage (deleteOut); size_t nval = value.size(); size_t n = itsElems.size(); for (size_t i=0; imatchString (out, in, nval, id); } value.array().freeStorage (in, deleteIn); result.putStorage (out, deleteOut); return MArray (result, value.mask()); } MArray TableExprNodeSet::contains (const TableExprId& id, const MArray& value) { Array result(value.shape()); result.set (False); Bool deleteIn, deleteOut; const MVTime* in = value.array().getStorage (deleteIn); Bool* out = result.getStorage (deleteOut); size_t nval = value.size(); size_t n = itsElems.size(); for (size_t i=0; imatchDate (out, in, nval, id); } value.array().freeStorage (in, deleteIn); result.putStorage (out, deleteOut); return MArray (result, value.mask()); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/ExprNodeSet.h000066400000000000000000000306501476623553700177540ustar00rootroot00000000000000//# ExprNodeSet.h: Classes representing a set in table select expression //# Copyright (C) 1997,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_EXPRNODESET_H #define TABLES_EXPRNODESET_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableExprNode; class IPosition; class Slicer; // // Class to hold multiple table expression nodes. // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • TableExprNodeRep //
      • TableExprNodeBinary // // // This class is used to assemble several table expression nodes. // It is used for 3 purposes: //
          //
        1. To hold the arguments of a function. // All set elements must be single. //
        2. To hold the variables of an index for an array slice. // All set elements must be of type int scalar and they must // represent a discrete interval (which includes single). //
        3. To hold the elements of a set used with the IN operator. // All set elements must be scalars of any type. //
        // The type of all set elements has to be the same. // The set consists of // TableExprNodeSetElem // elements. The add function has to be used to // add an element to the set. //

        // It is possible to construct the object directly from an // IPosition object. // In that case all elements are single. // Furthermore it is possible to construct it directly from a // Slicer object. // In that case all elements represent a discrete interval. // class TableExprNodeSet : public TableExprNodeRep { public: // Construct an empty set. TableExprNodeSet(); // Construct from an IPosition. // The number of elements in the set is the number of elements // in the IPosition. All set elements are single values. TableExprNodeSet (const IPosition&); // Construct from a Slicer. // The number of elements in the set is the dimensionality // of the Slicer. All set elements are discrete intervals. // Their start and/or end is undefined if it is was not defined // (i.e. Slicer::MimicSource used) in the Slicer object. TableExprNodeSet (const Slicer&); // Construct a set with n*set.size() elements where n is the number // of rows. // Element i is constructed by evaluating the input element // for row rownr[i]. TableExprNodeSet (const Vector& rownrs, const TableExprNodeSet&); TableExprNodeSet(const TableExprNodeSet&); ~TableExprNodeSet(); // A copy of a TableExprNodeSet cannot be assigned. TableExprNodeSet& operator= (const TableExprNodeSet&) = delete; // Add an element to the set. // If adaptType=True, the data type is the highest of the elements added. // Otherwise it is that of the first element. // True is meant for a set of values, False for function arguments. //
        A constant mid-width interval is added as a normal interval. // In this way constant intervals can never be mid-width which makes // optimization easier. void add (const TENSEBShPtr&, Bool adaptType=False); void add (const TableExprNodeSetElem& elem, Bool adaptType=False) { add (elem.getElem(), adaptType); } // Show the node. void show (ostream& os, uInt indent) const override; // Flatten the node tree by adding the node and its children to the vector. virtual void flattenTree (std::vector&) override; // Check if the data type of the set elements are the same. // If not, an exception is thrown. //# Note that if itsCheckTypes is set, the data types are already //# known to be equal. void checkEqualDataTypes() const; // Contains the set only single elements? // Single means that only single values are given (thus no end nor incr). Bool isSingle() const; // Contains the set only discrete elements? // Discrete means that no continuous ranges are given, but discrete // ranges (using :) are possible. Bool isDiscrete() const; // Is the set fully bounded (discrete and no undefined end values)? Bool isBounded() const; // Get the number of elements. size_t size() const; // For backward compatibility. size_t nelements() const {return size();} // Get the i-th element. const TENSEBShPtr& operator[] (size_t index) const; // Contains the set array values? Bool hasArrays() const; // Try to convert the set to an array. // If not possible, a copy of the set is returned. TENShPtr setOrArray() const; template MArray toArray (const TableExprId& id) const; // Get an array value for this bounded set in the given row. // MArray getArrayBool (const TableExprId& id) override; MArray getArrayInt (const TableExprId& id) override; MArray getArrayDouble (const TableExprId& id) override; MArray getArrayDComplex (const TableExprId& id) override; MArray getArrayString (const TableExprId& id) override; MArray getArrayDate (const TableExprId& id) override; // // Does a value occur in the set? // Bool contains (const TableExprId& id, Bool value) override; Bool contains (const TableExprId& id, Int64 value) override; Bool contains (const TableExprId& id, Double value) override; Bool contains (const TableExprId& id, DComplex value) override; Bool contains (const TableExprId& id, String value) override; Bool contains (const TableExprId& id, MVTime value) override; MArray contains (const TableExprId& id, const MArray& value) override; MArray contains (const TableExprId& id, const MArray& value) override; MArray contains (const TableExprId& id, const MArray& value) override; MArray contains (const TableExprId& id, const MArray& value) override; MArray contains (const TableExprId& id, const MArray& value) override; MArray contains (const TableExprId& id, const MArray& value) override; // // Useful to make overloading clearer (mainly for test programs). Bool contains (const TableExprId& id, int value) { return contains (id, Int64(value)); } Bool contains (const TableExprId& id, const char* value) { return contains (id, String(value)); } // Let a set node convert itself to the given unit. void adaptSetUnits (const Unit&) override; // Try to set the set's shape for a bounded set with single elements. void setShape(); private: // Convert the const set to an array. TENShPtr toConstArray() const; // Get the array in a templated way. // void getArray (MArray& marr, const TENShPtr& node, const TableExprId& id) const { marr.reference (node->getArrayBool (id)); } void getArray (MArray& marr, const TENShPtr& node, const TableExprId& id) const { marr.reference (node->getArrayInt (id)); } void getArray (MArray& marr, const TENShPtr& node, const TableExprId& id) const { marr.reference (node->getArrayDouble (id)); } void getArray (MArray& marr, const TENShPtr& node, const TableExprId& id) const { marr.reference (node->getArrayDComplex (id)); } void getArray (MArray& marr, const TENShPtr& node, const TableExprId& id) const { marr.reference (node->getArrayString (id)); } void getArray (MArray& marr, const TENShPtr& node, const TableExprId& id) const { marr.reference (node->getArrayDate (id)); } // //# Data members std::vector itsElems; Bool itsSingle; Bool itsDiscrete; Bool itsBounded; //# Set is discrete and all starts/ends are defined Bool itsCheckTypes; //# True = checking data types is not needed }; inline Bool TableExprNodeSet::isSingle() const { return itsSingle; } inline Bool TableExprNodeSet::isDiscrete() const { return itsDiscrete; } inline Bool TableExprNodeSet::isBounded() const { return itsBounded; } inline size_t TableExprNodeSet::size() const { return itsElems.size(); } inline const TENSEBShPtr& TableExprNodeSet::operator[] (size_t index) const { return itsElems[index]; } template MArray TableExprNodeSet::toArray (const TableExprId& id) const { /// TODO: align possible units DebugAssert (itsBounded, AipsError); Int64 n = size(); if (hasArrays()) { if (itsElems[0]->start()->valueType() != VTArray) { throw TableInvExpr("scalar value cannot be given in a nested array"); } // Handle a nested array; this is done recursively. MArray marr; getArray (marr, itsElems[0]->start(), id); if (marr.isNull()) { return marr; } Array result (marr.array()); Array mask (marr.mask()); IPosition shp = result.shape(); uInt naxes = shp.size(); shp.append (IPosition(1,n)); IPosition maskShp(shp); maskShp[maskShp.size()-1] = 1; result.resize (shp, True); if (! mask.empty()) { mask.resize (shp, True); } // Iterate through the remaining arrays. ArrayIterator iter(result, shp.size()-1); IPosition s(shp.size(), 0); IPosition e(shp-1); e[naxes] = 0; for (Int64 i=1; istart()->valueType() != VTArray) { throw TableInvExpr("scalar value cannot be given in a nested array"); } iter.next(); s[naxes]++; e[naxes]++; MArray marr; getArray (marr, itsElems[i]->start(), id); if (marr.isNull()) { return marr; } if (! marr.shape().isEqual (iter.array().shape())) { throw TableInvExpr("Shapes of nested arrays do not match"); } iter.array() = marr.array(); if (marr.hasMask()) { if (mask.empty()) { // The first time a mask was found, so create the resulting mask. mask.resize (shp); mask = False; } mask(s,e) = marr.mask().reform(maskShp); } else if (! mask.empty()) { // This array has no mask, so set to False in resulting mask. mask(s,e) = False; } } return MArray(result, mask); } else { // Combine scalars. Int64 n = size(); Int64 cnt = 0; Vector result (n); for (Int64 i=0; ifillVector (result, cnt, id); } result.resize (cnt, True); return MArray(result); } } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/ExprNodeSetElem.cc000066400000000000000000001045631476623553700207220ustar00rootroot00000000000000//# ExprNodeSetElem.cc: Classes representing a set element in table select expression //# Copyright (C) 1997,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableExprNodeSetElemBase::TableExprNodeSetElemBase (NodeDataType dt) : TableExprNodeRep (dt, VTSetElem, OtUndef, Constant) {} Bool TableExprNodeSetElemBase::isDiscrete() const { return False; } Bool TableExprNodeSetElemBase::isSingle() const { return False; } Bool TableExprNodeSetElemBase::isLeftClosed() const { return False; } Bool TableExprNodeSetElemBase::isRightClosed() const { return False; } Bool TableExprNodeSetElemBase::isMidWidth() const { return False; } void TableExprNodeSetElemBase::adaptSetUnits (const Unit& unit) { if (! unit.empty()) { if (itsStart) TableExprNodeUnit::adaptUnit (itsStart, unit); if (itsEnd) TableExprNodeUnit::adaptUnit (itsEnd, unit); if (itsIncr) TableExprNodeUnit::adaptUnit (itsIncr, unit); setUnit (unit); } } void TableExprNodeSetElemBase::show (ostream& os, uInt indent) const { TableExprNodeRep::show (os, indent); if (itsStart) { os << "start: "; itsStart->show (os, indent+2); } if (itsEnd) { os << "end: "; itsEnd->show (os, indent+2); } if (itsIncr) { os << "incr: "; itsIncr->show (os, indent+2); } } void TableExprNodeSetElemBase::flattenTree (std::vector& nodes) { nodes.push_back (this); if (itsStart) { itsStart->flattenTree (nodes); } if (itsEnd) { itsEnd->flattenTree (nodes); } if (itsIncr) { itsIncr->flattenTree (nodes); } } void TableExprNodeSetElemBase::fillVector (Vector&, Int64&, const TableExprId&) const { throw TableInvExpr ("TableExprNodeSetElem::fillVector"); } void TableExprNodeSetElemBase::fillVector (Vector&, Int64&, const TableExprId&) const { throw TableInvExpr ("TableExprNodeSetElem::fillVector"); } void TableExprNodeSetElemBase::fillVector (Vector&, Int64&, const TableExprId&) const { throw TableInvExpr ("TableExprNodeSetElem::fillVector"); } void TableExprNodeSetElemBase::fillVector (Vector&, Int64&, const TableExprId&) const { throw TableInvExpr ("TableExprNodeSetElem::fillVector"); } void TableExprNodeSetElemBase::fillVector (Vector&, Int64&, const TableExprId&) const { throw TableInvExpr ("TableExprNodeSetElem::fillVector"); } void TableExprNodeSetElemBase::fillVector (Vector&, Int64&, const TableExprId&) const { throw TableInvExpr ("TableExprNodeSetElem::fillVector"); } void TableExprNodeSetElemBase::matchBool (Bool*, const Bool*, size_t, const TableExprId&) const { throw TableInvExpr ("TableExprNodeSetElem::matchBool"); } void TableExprNodeSetElemBase::matchInt (Bool*, const Int64*, size_t, const TableExprId&) const { throw TableInvExpr ("TableExprNodeSetElem::matchInt"); } void TableExprNodeSetElemBase::matchDouble (Bool*, const Double*, size_t, const TableExprId&) const { throw TableInvExpr ("TableExprNodeSetElem::matchDouble"); } void TableExprNodeSetElemBase::matchDComplex (Bool*, const DComplex*, size_t, const TableExprId&) const { throw TableInvExpr ("TableExprNodeSetElem::matchDComplex"); } void TableExprNodeSetElemBase::matchString (Bool*, const String*, size_t, const TableExprId&) const { throw TableInvExpr ("TableExprNodeSetElem::matchString"); } void TableExprNodeSetElemBase::matchDate (Bool*, const MVTime*, size_t, const TableExprId&) const { throw TableInvExpr ("TableExprNodeSetElem::matchDate"); } void TableExprNodeSetElemBase::setExprType() { exprtype_p = Constant; fillExprType (itsStart.get()); fillExprType (itsEnd.get()); fillExprType (itsIncr.get()); } TENShPtr TableExprNodeSetElemBase::evalExpr (const TENShPtr& expr, const TableExprId& id) const { if (!expr) { return TENShPtr(); } TENShPtr res; switch (dataType()) { case NTBool: res = std::make_shared(expr->getBool(id)); break; case NTInt: res = std::make_shared(expr->getInt(id)); break; case NTDouble: res = std::make_shared(expr->getDouble(id)); break; case NTComplex: res = std::make_shared(expr->getDComplex(id)); break; case NTString: res = std::make_shared(expr->getString(id)); break; case NTDate: // Note that the increment or width for a DateTime is double. if (expr->dataType() == NTDate) { res = std::make_shared(expr->getDate(id)); } else { res = std::make_shared(expr->getDouble(id)); } break; default: TableExprNode::throwInvDT ("TableExprNodeSetElem::evaluate"); } res->setUnit (expr->unit()); return res; } void TableExprNodeSetElemBase::getStart (const TableExprId& id, Double& v) const { if (itsStart->dataType() == NTDate) { v = itsStart->getDate (id); // gets converted to days } else { v = itsStart->getDouble (id); } } void TableExprNodeSetElemBase::getEnd (const TableExprId& id, Double& v) const { if (itsEnd->dataType() == NTDate) { v = itsEnd->getDate (id); // gets converted to days } else { v = itsEnd->getDouble (id); } } void TableExprNodeSetElemBase::getStart (const TableExprId& id, String& v) const { v = itsStart->getString (id); } void TableExprNodeSetElemBase::getEnd (const TableExprId& id, String& v) const { v = itsEnd->getString (id); } TableExprNodeSetElemSingle::TableExprNodeSetElemSingle (const TableExprNode& value) : TableExprNodeSetElemBase() { itsStart = value.getRep(); dtype_p = itsStart->dataType(); setUnit (itsStart->unit()); setExprType(); ndim_p = value.getNodeRep()->ndim(); shape_p = value.getNodeRep()->shape(); } TableExprNodeSetElemSingle::TableExprNodeSetElemSingle (const TableExprNodeSetElemSingle& that, const TENShPtr& value) : TableExprNodeSetElemBase(that.dataType()) { itsStart = value; setUnit (itsStart->unit()); } TENSEBShPtr TableExprNodeSetElemSingle::evaluate (const TableExprId& id) const { return TENSEBShPtr(new TableExprNodeSetElemSingle (*this, evalExpr(itsStart, id))); } Bool TableExprNodeSetElemSingle::isDiscrete() const { return True; } Bool TableExprNodeSetElemSingle::isSingle() const { return True; } void TableExprNodeSetElemSingle::fillVector (Vector& vec, Int64& cnt, const TableExprId& id) const { Int64 n = vec.size(); if (n < cnt+1) { vec.resize (cnt+64, True); } vec(cnt++) = itsStart->getBool (id); } void TableExprNodeSetElemSingle::fillVector (Vector& vec, Int64& cnt, const TableExprId& id) const { Int64 n = vec.size(); if (n < cnt+1) { vec.resize (cnt+64, True); } vec(cnt++) = itsStart->getInt (id); } void TableExprNodeSetElemSingle::fillVector (Vector& vec, Int64& cnt, const TableExprId& id) const { Int64 n = vec.size(); if (n < cnt+1) { vec.resize (cnt+64, True); } vec(cnt++) = itsStart->getDouble (id); } void TableExprNodeSetElemSingle::fillVector (Vector& vec, Int64& cnt, const TableExprId& id) const { Int64 n = vec.size(); if (n < cnt+1) { vec.resize (cnt+64, True); } vec(cnt++) = itsStart->getDComplex (id); } void TableExprNodeSetElemSingle::fillVector (Vector& vec, Int64& cnt, const TableExprId& id) const { Int64 n = vec.size(); if (n < cnt+1) { vec.resize (cnt+64, True); } vec(cnt++) = itsStart->getString (id); } void TableExprNodeSetElemSingle::fillVector (Vector& vec, Int64& cnt, const TableExprId& id) const { Int64 n = vec.size(); if (n < cnt+1) { vec.resize (cnt+64, True); } vec(cnt++) = itsStart->getDate (id); } void TableExprNodeSetElemSingle::matchBool (Bool* match, const Bool* value, size_t nval, const TableExprId& id) const { Bool* lastVal = match + nval; if (itsStart->valueType() == VTArray) { TableExprNodeArrayConstBool start (itsStart->getArrayBool(id)); while (match < lastVal) { if (start.contains (id, *value)) { *match = True; } value++; match++; } } else { Bool start = itsStart->getBool (id); while (match < lastVal) { if (*value == start) { *match = True; } value++; match++; } } } void TableExprNodeSetElemSingle::matchInt (Bool* match, const Int64* value, size_t nval, const TableExprId& id) const { Bool* lastVal = match + nval; if (itsStart->valueType() == VTArray) { TableExprNodeArrayConstInt start (itsStart->getArrayInt(id)); while (match < lastVal) { if (start.contains (id, *value)) { *match = True; } value++; match++; } } else { Int64 start = itsStart->getInt (id); while (match < lastVal) { if (*value == start) { *match = True; } value++; match++; } } } void TableExprNodeSetElemSingle::matchDouble (Bool* match, const Double* value, size_t nval, const TableExprId& id) const { Bool* lastVal = match + nval; if (itsStart->valueType() == VTArray) { TableExprNodeArrayConstDouble start (itsStart->getArrayDouble(id)); while (match < lastVal) { if (start.contains (id, *value)) { *match = True; } value++; match++; } } else { Double start = itsStart->getDouble (id); while (match < lastVal) { if (*value == start) { *match = True; } value++; match++; } } } void TableExprNodeSetElemSingle::matchDComplex (Bool* match, const DComplex* value, size_t nval, const TableExprId& id) const { Bool* lastVal = match + nval; if (itsStart->valueType() == VTArray) { TableExprNodeArrayConstDComplex start (itsStart->getArrayDComplex(id)); while (match < lastVal) { if (start.contains (id, *value)) { *match = True; } value++; match++; } } else { DComplex start = itsStart->getDComplex (id); while (match < lastVal) { if (*value == start) { *match = True; } value++; match++; } } } void TableExprNodeSetElemSingle::matchString (Bool* match, const String* value, size_t nval, const TableExprId& id) const { Bool* lastVal = match + nval; if (itsStart->valueType() == VTArray) { TableExprNodeArrayConstString start (itsStart->getArrayString(id)); while (match < lastVal) { if (start.contains (id, *value)) { *match = True; } value++; match++; } } else { String start = itsStart->getString (id); while (match < lastVal) { if (*value == start) { *match = True; } value++; match++; } } } void TableExprNodeSetElemSingle::matchDate (Bool* match, const MVTime* value, size_t nval, const TableExprId& id) const { Bool* lastVal = match + nval; if (itsStart->valueType() == VTArray) { TableExprNodeArrayConstDate start (itsStart->getArrayDate(id)); while (match < lastVal) { if (start.contains (id, *value)) { *match = True; } value++; match++; } } else { MVTime start = itsStart->getDate (id); while (match < lastVal) { if (*value == start) { *match = True; } value++; match++; } } } TableExprNodeSetElemDiscrete::TableExprNodeSetElemDiscrete (const TableExprNode& start, const TableExprNode& end, const TableExprNode& incr, Bool isEndExcl) : TableExprNodeSetElemBase(), itsEndExcl (isEndExcl) { // Start, end and increment are all optional. // Get the overall data type and test if they are scalar. Bool isScalar = True; NodeDataType dts = NTInt; if (! start.isNull()) { itsStart = start.getRep(); dts = itsStart->dataType(); isScalar = isScalar && start.isScalar(); } NodeDataType dte = dts; if (! end.isNull()) { itsEnd = end.getRep(); dte = itsEnd->dataType(); isScalar = isScalar && end.isScalar(); } NodeDataType dti = NTInt; if (! incr.isNull()) { itsIncr = incr.getRep(); dti = itsIncr->dataType(); isScalar = isScalar && incr.isScalar(); } if (!isScalar) { throw TableInvExpr("Scalar values must be used in start:incr:end"); } if (dts == NTInt && (dte == NTDouble || dti == NTDouble)) dts = NTDouble; if (dte == NTInt && (dts == NTDouble || dti == NTDouble)) dte = NTDouble; if ((dts != NTInt && dts != NTDouble && dts != NTDate) || dte != dts || (dti != NTInt && dti != NTDouble)) { throw TableInvExpr("start:end should have equal data types (Int, Double" " or Date) and incr should have Int or Double"); } // Find unit and adapt units if needed. setUnit (TableExprNodeUnit::adaptUnits (itsStart, itsEnd, itsIncr)); dtype_p = dts; setExprType(); } TableExprNodeSetElemDiscrete::TableExprNodeSetElemDiscrete (const TableExprNodeSetElemDiscrete& that, const TENShPtr& start, const TENShPtr& end, const TENShPtr& incr) : TableExprNodeSetElemBase(that.dataType()), itsEndExcl (that.itsEndExcl) { itsStart = start; itsEnd = end; itsIncr = incr; setUnit (that.unit()); } TENSEBShPtr TableExprNodeSetElemDiscrete::evaluate (const TableExprId& id) const { return TENSEBShPtr(new TableExprNodeSetElemDiscrete (*this, evalExpr(itsStart, id), evalExpr(itsEnd, id), evalExpr(itsIncr, id))); } Bool TableExprNodeSetElemDiscrete::isDiscrete() const { return True; } void TableExprNodeSetElemDiscrete::fillVector (Vector& vec, Int64& cnt, const TableExprId& id) const { Int64 start = !itsStart ? 0 : itsStart->getInt (id); Int64 end = !itsEnd ? start : itsEnd->getInt (id); Int64 incr = !itsIncr ? 1 : itsIncr->getInt (id); if (incr == 0) { throw TableInvExpr("Increment in a range must be non-zero"); } Int64 nval = std::max(Int64(0), 1 + (end - start) / incr); if (itsEndExcl && nval > 0) { Int64 rngend = start + (nval-1)*incr; if (rngend == end) { nval -= 1; } } Int64 n = vec.size(); if (n < cnt+nval) { vec.resize (cnt+max(64,nval), True); } for (Int64 i=0; i& vec, Int64& cnt, const TableExprId& id) const { Double start = !itsStart ? 0 : itsStart->getDouble (id); Double end = !itsEnd ? start : itsEnd->getDouble (id); Double incr = !itsIncr ? 1 : itsIncr->getDouble (id); if (incr == 0) { throw TableInvExpr("Increment in a range must be non-zero"); } Int64 nval = std::max(Int64(0), Int64(1 + (end - start) / incr + 1e-10)); if (itsEndExcl && nval > 0) { Double rngend = start + (nval-1)*incr; if (near(rngend, end) || (end == 0 && nearAbs(rngend, end))) { nval -= 1; } } Int64 n = vec.size(); if (n < cnt+nval) { vec.resize (cnt+max(64,nval), True); } for (Int64 i=0; i& vec, Int64& cnt, const TableExprId& id) const { Double start = !itsStart ? 0 : Double(itsStart->getDate (id)); Double end = !itsEnd ? start : Double(itsEnd->getDate (id)); Double incr = !itsIncr ? 1 : itsIncr->getDouble (id); if (incr == 0) { throw TableInvExpr("Increment in a range must be non-zero"); } Int64 nval = std::max(Int64(0), Int64(1 + (end - start) / incr + 1e-10)); if (itsEndExcl && nval > 0) { Double rngend = start + (nval-1)*incr; if (near(rngend, end) || (end == 0 && nearAbs(rngend, end))) { nval -= 1; } } Int64 n = vec.size(); if (n < cnt+nval) { vec.resize (cnt+max(64,nval), True); } for (Int64 i=0; igetInt (id); Int64 end = !itsEnd ? start : itsEnd->getInt (id); Int64 incr = !itsIncr ? 1 : itsIncr->getInt (id); if (incr == 0) { throw TableInvExpr("Increment in a range must be non-zero"); } Bool* lastVal = match + nval; end -= start; if (itsEndExcl) { end -= 1; } while (match < lastVal) { Int64 tmp = *value - start; if (incr > 0) { if (tmp >= 0 && (!itsEnd || tmp <= end)) { if (tmp%incr == 0) { *match = True; } } } else { if (tmp <= 0 && (!itsEnd || tmp >= end)) { if (tmp%incr == 0) { *match = True; } } } value++; match++; } } void TableExprNodeSetElemDiscrete::matchDouble (Bool* match, const Double* value, size_t nval, const TableExprId& id) const { Double start = !itsStart ? 0 : itsStart->getDouble (id); Double end = !itsEnd ? start : itsEnd->getDouble (id); Double incr = !itsIncr ? 1 : itsIncr->getDouble (id); if (incr == 0) { throw TableInvExpr("Increment in a range must be non-zero"); } Bool* lastVal = match + nval; end -= start; while (match < lastVal) { Double tmp = *value - start; if (incr > 0) { if (tmp >= 0 && (!itsEnd || tmp < end || (!itsEndExcl && tmp==end))) { if (near(tmp, incr*Int64(tmp/incr + 0.5))) { *match = True; } } } else { if (tmp <= 0 && (!itsEnd || tmp > end || (!itsEndExcl && tmp==end))) { if (near(tmp, incr*Int64(tmp/incr + 0.5))) { *match = True; } } } value++; match++; } } void TableExprNodeSetElemDiscrete::matchDate (Bool* match, const MVTime* value, size_t nval, const TableExprId& id) const { Double start = !itsStart ? 0 : Double(itsStart->getDate (id)); Double end = !itsEnd ? start : Double(itsEnd->getDate (id)); Double incr = !itsIncr ? 1 : itsIncr->getDouble (id); if (incr == 0) { throw TableInvExpr("Increment in a range must be non-zero"); } Bool* lastVal = match + nval; end -= start; while (match < lastVal) { Double tmp = Double(*value) - start; if (incr > 0) { if (tmp >= 0 && (!itsEnd || tmp < end || (!itsEndExcl && tmp==end))) { if (near(tmp, incr*Int64(tmp/incr + 0.5))) { *match = True; } } } else { if (tmp <= 0 && (!itsEnd || tmp > end || (!itsEndExcl && tmp==end))) { if (near(tmp, incr*Int64(tmp/incr + 0.5))) { *match = True; } } } value++; match++; } } TableExprNodeSetElemCont::TableExprNodeSetElemCont (Bool isLeftClosed, const TableExprNode& start, const TableExprNode& end, Bool isRightClosed) { setup (isLeftClosed, &start, &end, isRightClosed); } TableExprNodeSetElemCont::TableExprNodeSetElemCont (Bool isLeftClosed, const TableExprNode& start) { setup (isLeftClosed, &start, 0, False); } TableExprNodeSetElemCont::TableExprNodeSetElemCont (const TableExprNode& end, Bool isRightClosed) : TableExprNodeSetElemBase() { setup (False, 0, &end, isRightClosed); } TableExprNodeSetElemCont::TableExprNodeSetElemCont (const TableExprNode& mid, const TableExprNode& width) : TableExprNodeSetElemBase() { AlwaysAssert (!mid.isNull(), AipsError); AlwaysAssert (!width.isNull(), AipsError); itsStart = mid.getRep(); itsEnd = width.getRep(); itsLeftClosed = True; itsRightClosed = True; } TableExprNodeSetElemCont::TableExprNodeSetElemCont (const TableExprNodeSetElemCont& that, const TENShPtr& start, const TENShPtr& end) : TableExprNodeSetElemBase(that.dataType()), itsLeftClosed (that.itsLeftClosed), itsRightClosed (that.itsRightClosed) { itsStart = start; itsEnd = end; setUnit (that.unit()); } void TableExprNodeSetElemCont::setup (Bool isLeftClosed, const TableExprNode* start, const TableExprNode* end, Bool isRightClosed) { // Setup for a continuous interval given as start,end. // Start or end are optional. itsLeftClosed = isLeftClosed; itsRightClosed = isRightClosed; Bool isScalar = True; if (start) { itsStart = start->getRep(); isScalar = isScalar && start->isScalar(); // Get data type. dtype_p = itsStart->dataType(); // Integer is handled as Double. if (dtype_p == NTInt) { dtype_p = NTDouble; } } if (end) { itsEnd = end->getRep(); isScalar = isScalar && end->isScalar(); NodeDataType etype = itsEnd->dataType(); if (etype == NTInt) { etype = NTDouble; } if (start && etype != dtype_p) { throw TableInvExpr ("start=:=end must have equal data types"); } dtype_p = etype; } if (!isScalar) { throw TableInvExpr("Scalar values must be used in start=:=end"); } NodeDataType dt = dataType(); if (dt != NTDouble && dt != NTString && dt != NTDate) { throw TableInvExpr ("start=:=end only valid for " "Int, Double, String or Date"); } // Find unit and adapt units if needed. setUnit (TableExprNodeUnit::adaptUnits (itsStart, itsEnd, itsIncr)); setExprType(); } TENSEBShPtr TableExprNodeSetElemCont::evaluate (const TableExprId& id) const { return TENSEBShPtr(new TableExprNodeSetElemCont (*this, evalExpr(itsStart, id), evalExpr(itsEnd, id))); } Bool TableExprNodeSetElemCont::isLeftClosed() const { return itsLeftClosed; } Bool TableExprNodeSetElemCont::isRightClosed() const { return itsRightClosed; } void TableExprNodeSetElemCont::matchDouble (Bool* match, const Double* value, size_t nval, const TableExprId& id) const { Double start = !itsStart ? 0 : itsStart->getDouble (id); Double end = !itsEnd ? start : itsEnd->getDouble (id); Bool* lastVal = match + nval; while (match < lastVal) { Double tmp = *value; if ((!itsStart || tmp > start || (itsLeftClosed && tmp == start)) && (!itsEnd || tmp < end || (itsRightClosed && tmp == end))) { *match = True; } value++; match++; } } void TableExprNodeSetElemCont::matchString (Bool* match, const String* value, size_t nval, const TableExprId& id) const { String start; if (itsStart) { start = itsStart->getString (id); } String end; if (itsEnd) { end = itsEnd->getString (id); } Bool* lastVal = match + nval; while (match < lastVal) { if ((!itsStart || *value > start || (itsLeftClosed && *value == start)) && (!itsEnd || *value < end || (itsRightClosed && *value == end))) { *match = True; } value++; match++; } } void TableExprNodeSetElemCont::matchDate (Bool* match, const MVTime* value, size_t nval, const TableExprId& id) const { Double start = !itsStart ? 0 : Double(itsStart->getDate (id)); Double end = !itsEnd ? start : Double(itsEnd->getDate (id)); Bool* lastVal = match + nval; while (match < lastVal) { Double tmp = *value; if ((!itsStart || tmp > start || (itsLeftClosed && tmp == start)) && (!itsEnd || tmp < end || (itsRightClosed && tmp == end))) { *match = True; } value++; match++; } } TableExprNodeSetElemMidWidth::TableExprNodeSetElemMidWidth (const TableExprNode& mid, const TableExprNode& width) : TableExprNodeSetElemCont (mid, width) { Bool isScalar = mid.isScalar() && width.isScalar(); // Get data type. dtype_p = itsStart->dataType(); // Integer is handled as Double. if (dtype_p == NTInt) { dtype_p = NTDouble; } if (dtype_p != NTDouble && dtype_p != NTDate) { throw TableInvExpr ("mid<:>width must have an Int, Double or Datetime mid value"); } if (itsEnd->dataType() != NTInt && itsEnd->dataType() != NTDouble) { throw TableInvExpr ("mid<:>width must have an Int or Double width value"); } if (!isScalar) { throw TableInvExpr("Scalar values must be used in mid<:>width"); } // Find unit and adapt units if needed. NodeDataType dt = dataType(); if (dt == NTDouble) { setUnit (TableExprNodeUnit::adaptUnits (itsStart, itsEnd, itsIncr)); } else if (dt == NTDate) { TableExprNodeUnit::adaptUnit (itsEnd, "d"); } else { throw TableInvExpr ("mid<:>width only valid for Int, Double or Date"); } setExprType(); } TENSEBShPtr TableExprNodeSetElemMidWidth::evaluate (const TableExprId& id) const { Double start, end, mid, width; getEnd(id, width); if (width == 0) { start = std::numeric_limits::lowest(); end = std::numeric_limits::max(); } else { getStart(id, mid); start = mid - width*0.5; end = mid + width*0.5; } TENShPtr startp; TENShPtr endp; if (dataType() == NTDouble) { startp = std::make_shared(start); endp = std::make_shared(end); } else{ startp = std::make_shared(MVTime(start)); endp = std::make_shared(MVTime(end)); } // Create a closed-closed interval. return TENSEBShPtr(new TableExprNodeSetElemCont (*this, startp, endp)); } Bool TableExprNodeSetElemMidWidth::isMidWidth() const { return True; } void TableExprNodeSetElemMidWidth::matchDouble (Bool* match, const Double* value, size_t nval, const TableExprId& id) const { Double width = itsEnd->getDouble (id); Double start, end; if (width == 0) { start = std::numeric_limits::lowest(); end = std::numeric_limits::max(); } else { Double mid = itsStart->getDouble (id); start = mid - width*0.5; end = mid + width*0.5; } Bool* lastVal = match + nval; while (match < lastVal) { Double tmp = *value; if (tmp >= start && tmp <= end) { *match = True; } value++; match++; } } void TableExprNodeSetElemMidWidth::matchDate (Bool* match, const MVTime* value, size_t nval, const TableExprId& id) const { Double mid = Double(itsStart->getDate (id)); Double width = Double(itsEnd->getDouble (id)); Double start = mid - width*0.5; Double end = mid + width*0.5; Bool* lastVal = match + nval; while (match < lastVal) { Double tmp = *value; if (tmp >= start && tmp <= end) { *match = True; } value++; match++; } } TableExprNodeSetElem::TableExprNodeSetElem (const TENSEBShPtr& elem) : TableExprNodeRep (NTBool, VTSetElem, OtUndef, Constant), itsElem (elem) { init(); } TableExprNodeSetElem::TableExprNodeSetElem (const TableExprNode& node) : TableExprNodeRep (NTBool, VTSetElem, OtUndef, Constant), itsElem (new TableExprNodeSetElemSingle (node)) { init(); } TableExprNodeSetElem::TableExprNodeSetElem (const TableExprNode* start, const TableExprNode* end, const TableExprNode* incr, Bool isEndExcl) : TableExprNodeRep (NTBool, VTSetElem, OtUndef, Constant) { TableExprNode s (start ? *start : TableExprNode()); TableExprNode e (end ? *end : TableExprNode()); TableExprNode i (incr ? *incr : TableExprNode()); itsElem = std::make_shared(s, e, i, isEndExcl); init(); } TableExprNodeSetElem::TableExprNodeSetElem (Bool isLeftClosed, const TableExprNode& start, const TableExprNode& end, Bool isRightClosed) : TableExprNodeRep (NTBool, VTSetElem, OtUndef, Constant), itsElem (new TableExprNodeSetElemCont (isLeftClosed, start, end, isRightClosed)) { init(); } TableExprNodeSetElem::TableExprNodeSetElem (Bool isLeftClosed, const TableExprNode& start) : TableExprNodeRep (NTBool, VTSetElem, OtUndef, Constant), itsElem (new TableExprNodeSetElemCont (isLeftClosed, start)) { init(); } TableExprNodeSetElem::TableExprNodeSetElem (const TableExprNode& end, Bool isRightClosed) : TableExprNodeRep (NTBool, VTSetElem, OtUndef, Constant), itsElem (new TableExprNodeSetElemCont (end, isRightClosed)) { init(); } TableExprNodeSetElem::TableExprNodeSetElem (const TableExprNode& mid, const TableExprNode& width) : TableExprNodeRep (NTBool, VTSetElem, OtUndef, Constant), itsElem (new TableExprNodeSetElemMidWidth (mid, width)) { init(); } void TableExprNodeSetElem::init() { dtype_p = itsElem->dataType(); exprtype_p = itsElem->exprType(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/ExprNodeSetElem.h000066400000000000000000000622631476623553700205640ustar00rootroot00000000000000//# ExprNodeSetElem.h: Classes representing a set element in table select expression //# Copyright (C) 1997,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_EXPRNODESETELEM_H #define TABLES_EXPRNODESETELEM_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableExprNode; // Define a shorthand for a shared pointer to TableExprNodeSetElemBase. // It will be used at several places. class TableExprNodeSetElemBase; typedef std::shared_ptr TENSEBShPtr; //

        // Base class for the classes defining set element // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeSet //
      • TableExprNodeRep // // // This class is used to assemble the table expression nodes // representing an element in a set. There are various types of elements // such as a single value, discrete range and continuous interval. //
        The base class holds the possible start, end and increment value of // an element. It also has functions telling the overall expression which // columns and aggregate functions are used by an element. //
        class TableExprNodeSetElemBase: public TableExprNodeRep { public: // Constructor to initialize the parent. explicit TableExprNodeSetElemBase (NodeDataType = NTDouble); virtual ~TableExprNodeSetElemBase() = default; // A copy of a TableExprNodeSetElem cannot be made. TableExprNodeSetElemBase& operator= (const TableExprNodeSetElemBase&) = delete; // Show the node. void show (ostream& os, uInt indent) const override; // Flatten the node tree by adding the node and its children to the vector. virtual void flattenTree (std::vector&) override; // Is it a discrete set element. // Default implementation returns False. virtual Bool isDiscrete() const; // Is a single value given? // Default implementation returns False. virtual Bool isSingle() const; // Is the interval left or right closed? // Default implementation returns False. // virtual Bool isLeftClosed() const; virtual Bool isRightClosed() const; // // Is the interval given as mid-width? // Default implementation returns False. virtual Bool isMidWidth() const; // Get the start, end or increment expression. // Note that the shared pointer returned can be null indicating that a // value was not given. // const TENShPtr& start() const { return itsStart; } const TENShPtr& end() const { return itsEnd; } const TENShPtr& increment() const { return itsIncr; } // // Fill a vector with the value(s) from this element by appending them // at the end of the vector; the end is given by argument cnt // which gets incremented with the number of values appended. // This is used by the system to convert a set to a vector. // virtual void fillVector (Vector& vec, Int64& cnt, const TableExprId& id) const; virtual void fillVector (Vector& vec, Int64& cnt, const TableExprId& id) const; virtual void fillVector (Vector& vec, Int64& cnt, const TableExprId& id) const; virtual void fillVector (Vector& vec, Int64& cnt, const TableExprId& id) const; virtual void fillVector (Vector& vec, Int64& cnt, const TableExprId& id) const; virtual void fillVector (Vector& vec, Int64& cnt, const TableExprId& id) const; // // Set a flag in the match output array if the corresponding element // in the value array is included in this set element. // This is used by the system to implement the IN operator. //
        Note that it does NOT set match values to False; it is assumed they // are initialized that way. // virtual void matchBool (Bool* match, const Bool* value, size_t nval, const TableExprId& id) const; virtual void matchInt (Bool* match, const Int64* value, size_t nval, const TableExprId& id) const; virtual void matchDouble (Bool* match, const Double* value, size_t nval, const TableExprId& id) const; virtual void matchDComplex (Bool* match, const DComplex* value, size_t nval, const TableExprId& id) const; virtual void matchString (Bool* match, const String* value, size_t nval, const TableExprId& id) const; virtual void matchDate (Bool* match, const MVTime* value, size_t nval, const TableExprId& id) const; // // Evaluate the element for the given row and construct a new // (constant) element from it. // This is used by the system to implement a set in a GIVING clause. virtual TENSEBShPtr evaluate (const TableExprId& id) const = 0; // Set the expression type (Variable or Constant) depending on the children. void setExprType(); // Let a set node convert itself to the given unit. void adaptSetUnits (const Unit&) override; // Get the start or end value of a Double or DateTime interval. // void getStart (const TableExprId& id, Double&) const; void getEnd (const TableExprId& id, Double& ) const; // // Get the start or end value of a String interval. // void getStart (const TableExprId& id, String&) const; void getEnd (const TableExprId& id, String&) const; // protected: // Evaluate the expression for the given row id. TENShPtr evalExpr (const TENShPtr& expr, const TableExprId& id) const; //# Data members. //# They are put here, so function start(), etc. can be inlined. //# Also getColumnNodes(), etc. make use of them. TENShPtr itsStart; TENShPtr itsEnd; TENShPtr itsIncr; }; // // Class defining a set element containing a single value. // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeSet //
      • TableExprNodeRep // // // This class defines a set element containing a single (discrete) value // which can be of any data type. The value can be a scalar or an array. // It can be used for 3 purposes: //
        - A function argument. //
        - A single index in an array indexing operation. //
        - A single value in a set (used with the IN operator). // This is in fact a bounded discrete ramge (see TableExprNodeSetDiscrete). //
        class TableExprNodeSetElemSingle: public TableExprNodeSetElemBase { public: // Create the object for a single expression node. explicit TableExprNodeSetElemSingle (const TableExprNode& node); ~TableExprNodeSetElemSingle() override = default; // It is a discrete set element. Bool isDiscrete() const override; // A single value is given (which can be an array). Bool isSingle() const override; // Fill a vector with the value(s) from this element by appending them // at the end of the vector; the end is given by argument cnt // which gets incremented with the number of values appended. // This is used by the system to convert a set to a vector. // void fillVector (Vector& vec, Int64& cnt, const TableExprId& id) const override; void fillVector (Vector& vec, Int64& cnt, const TableExprId& id) const override; void fillVector (Vector& vec, Int64& cnt, const TableExprId& id) const override; void fillVector (Vector& vec, Int64& cnt, const TableExprId& id) const override; void fillVector (Vector& vec, Int64& cnt, const TableExprId& id) const override; void fillVector (Vector& vec, Int64& cnt, const TableExprId& id) const override; // // Set a flag in the match output array if the corresponding element // in the value array is included in this set element. // This is used by the system to implement the IN operator. //
        Note that it does NOT set match values to False; it is assumed they // are initialized that way. // void matchBool (Bool* match, const Bool* value, size_t nval, const TableExprId& id) const override; void matchInt (Bool* match, const Int64* value, size_t nval, const TableExprId& id) const override; void matchDouble (Bool* match, const Double* value, size_t nval, const TableExprId& id) const override; void matchDComplex (Bool* match, const DComplex* value, size_t nval, const TableExprId& id) const override; void matchString (Bool* match, const String* value, size_t nval, const TableExprId& id) const override; void matchDate (Bool* match, const MVTime* value, size_t nval, const TableExprId& id) const override; // // Evaluate the element for the given row and construct a new // (constant) element from it. // This is used by the system to implement a set in a GIVING clause. TENSEBShPtr evaluate (const TableExprId& id) const override; private: // Construct an element from the given parts and take over their pointers. // It is used by evaluate to construct an element in a rather cheap way. TableExprNodeSetElemSingle (const TableExprNodeSetElemSingle& that, const TENShPtr& start); }; // // Class defining a set element containing a discrete range. // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeSet //
      • TableExprNodeRep // // // Class defining a set element containing a discrete range (start:end:incr) // which can be of data type Int, Double and Datetime. They have to be scalars. // A range consists of a start, end and increment value, each of them optional. // Increment defaults to 1. The end value can be inclusive or exclusive // (as in Python). // It can be used for 2 purposes: //
        - A slice in an array indexing operation which requires data // type Int. In that case start default to the beginning // of the dimension and end defaults to the end. //
        - A discrete range in a set. Start has to be given. // If end is not given, the result is an unbounded discrete range. //
        For a discrete range, the type of start and end can also be // a datetime scalar. //
        A bounded discrete range is automatically // converted to a vector, which makes it possible to apply array // functions to it (e.g., date() + [0:31]). //
        class TableExprNodeSetElemDiscrete: public TableExprNodeSetElemBase { public: // Create the object for a discrete range. // Each of the start, end, and incr pointers can be null meaning // that they are not given (see the synopsis for an explanation). // Optionally the end is inclusive (C++ and Glish style) or exclusive // (Python style). TableExprNodeSetElemDiscrete (const TableExprNode& start, const TableExprNode& end, const TableExprNode& incr, Bool isEndExcl = False); ~TableExprNodeSetElemDiscrete() override = default; // It is a discrete set element. Bool isDiscrete() const override; // Fill a vector with the value(s) from this element by appending them // at the end of the vector; the end is given by argument cnt // which gets incremented with the number of values appended. // This is used by the system to convert a set to a vector. // void fillVector (Vector& vec, Int64& cnt, const TableExprId& id) const override; void fillVector (Vector& vec, Int64& cnt, const TableExprId& id) const override; void fillVector (Vector& vec, Int64& cnt, const TableExprId& id) const override; // // Set a flag in the match output array if the corresponding element // in the value array is included in this set element. // This is used by the system to implement the IN operator. //
        Note that it does NOT set match values to False; it is assumed they // are initialized that way. // void matchInt (Bool* match, const Int64* value, size_t nval, const TableExprId& id) const override; void matchDouble (Bool* match, const Double* value, size_t nval, const TableExprId& id) const override; void matchDate (Bool* match, const MVTime* value, size_t nval, const TableExprId& id) const override; // // Evaluate the element for the given row and construct a new // (constant) element from it. // This is used by the system to implement a set in a GIVING clause. TENSEBShPtr evaluate (const TableExprId& id) const override; private: // Construct an element from the given parts and take over their pointers. // It is used by evaluate to construct an element in a rather cheap way. TableExprNodeSetElemDiscrete (const TableExprNodeSetElemDiscrete& that, const TENShPtr& start, const TENShPtr& end, const TENShPtr& incr); //# Data members Bool itsEndExcl; }; // // Class defining a set element containing a continuous interval. // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeSet //
      • TableExprNodeRep // // // Class defining a set element containing a continuous interval. // It consists of a start and/or an end scalar value of type int, double, // datetime, or string. Data type int will be converted to double. // The interval can be open (exclusive) or closed (inclusive) on either side. // The interval can be unbounded by leaving out start or end. //
        Note that a continuous interval can also be given as mid-width // using class TableExprNodeSetElemMidWidth. //
        class TableExprNodeSetElemCont: public TableExprNodeSetElemBase { public: // Create the object for a continuous bounded interval. It can be // open or closed on either side. TableExprNodeSetElemCont (Bool isLeftClosed, const TableExprNode& start, const TableExprNode& end, Bool isRightClosed); // Create the object for a continuous left-bounded interval. TableExprNodeSetElemCont (Bool isLeftClosed, const TableExprNode& start); // Create the object for a continuous right-bounded interval. TableExprNodeSetElemCont (const TableExprNode& end, Bool isRightClosed); // Construct an element from the given parts and take over their pointers. // It is used by evaluate to construct an element in a rather cheap way. TableExprNodeSetElemCont (const TableExprNodeSetElemCont& that, const TENShPtr& start, const TENShPtr& end); ~TableExprNodeSetElemCont() override = default; // Is the interval left or right closed? // Bool isLeftClosed() const override; Bool isRightClosed() const override; // // Set a flag in the match output array if the corresponding element // in the value array is included in this set element. // This is used by the system to implement the IN operator. //
        Note that it does NOT set match values to False; it is assumed they // are initialized that way. // void matchDouble (Bool* match, const Double* value, size_t nval, const TableExprId& id) const override; void matchString (Bool* match, const String* value, size_t nval, const TableExprId& id) const override; void matchDate (Bool* match, const MVTime* value, size_t nval, const TableExprId& id) const override; // // Evaluate the element for the given row and construct a new // (constant) element from it. // This is used by the system to implement a set in a GIVING clause. TENSEBShPtr evaluate (const TableExprId& id) const override; protected: // Constructor used by the derived class TableExprNodeSetElemMidWidth. TableExprNodeSetElemCont (const TableExprNode& mid, const TableExprNode& width); private: // Setup the object for a continuous interval. void setup (Bool isLeftClosed, const TableExprNode* start, const TableExprNode* end, Bool isRightClosed); //# Data members Bool itsLeftClosed; Bool itsRightClosed; }; // // Class defining a set element containing a continuous mid/width interval. // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeSet //
      • TableExprNodeRep // // // Class, derived from TableExprNodeSetElemCont, defining a set element // containing a continuous interval given its midpoint and width. // It is defined as [mid-width/2,mid+width/2]. It is closed on both sides. // It can only be used for data type double and datetime. // In case of datetime the width must be a double with default unit d (days). //
        Following the definition of intervals in the MeasurementSet (note 223), // the interval is infinite on both sides if the width is zero. //
        class TableExprNodeSetElemMidWidth: public TableExprNodeSetElemCont { public: // Create the object for a continuous bounded interval given as mid-width. // It is closed on both sides. TableExprNodeSetElemMidWidth (const TableExprNode& mid, const TableExprNode& width); ~TableExprNodeSetElemMidWidth() override = default; // The interval is given as mid-width. Bool isMidWidth() const override; // Set a flag in the match output array if the corresponding element // in the value array is included in this set element. // This is used by the system to implement the IN operator. //
        Note that it does NOT set match values to False; it is assumed they // are initialized that way. // void matchDouble (Bool* match, const Double* value, size_t nval, const TableExprId& id) const override; void matchDate (Bool* match, const MVTime* value, size_t nval, const TableExprId& id) const override; // // Evaluate the set element for the given row and construct a new // (constant) TableExprNodeSetElemCont element from it. // This is used by the system to implement a set in a GIVING clause. TENSEBShPtr evaluate (const TableExprId& id) const override; }; // // Class to hold the table expression nodes for an element in a set. // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeSet //
      • TableExprNodeRep // // // This class hold a TableExprNodeSetElemBase pointer, thus points // to an object of one its derived classes. //
        At the end of 2022 the old TableExprSetElem was split into several // classes, one per element type. This class still exists for backward // compatibilty, but is also useful in its own right. Its constructors create // one of the derived TableExprNodeSetElemBase objects which mirror the // TaQL specification, but can also be used directly in C++. The types are: //
          //
        • A single value; in TaQL: max(array) or [2] //
        • A discrete range; in TaQL: [1:101:3] //
        • A continuous start/end interval; in TaQL: between 3 and 11 //
        • A continuous mid/width interval; in TaQL: around 7 in 8 //
        //
        Note the difference between a discrete range and a continuous interval. // The discrete range 2,6 consists of the values 1,4,7...100 // The continuous intervals consists of all values between 3 and 11. //
        class TableExprNodeSetElem: public TableExprNodeRep { public: // Create from a Base element. explicit TableExprNodeSetElem (const TENSEBShPtr& elem); ~TableExprNodeSetElem() override = default; // Create the object for a single expression node. explicit TableExprNodeSetElem (const TableExprNode& node); // Create the object for a discrete range. // Each of the start, end, and incr pointers can be zero meaning // that they are not given (see the synopsis for an explanation). // Optionally the end is inclusive (C++ and Glish style) or exclusive // (Python style). TableExprNodeSetElem (const TableExprNode* start, const TableExprNode* end, const TableExprNode* incr, Bool isEndExcl = False); // Create the object for a continuous bounded interval. It can be // open or closed on either side. TableExprNodeSetElem (Bool isLeftClosed, const TableExprNode& start, const TableExprNode& end, Bool isRightClosed); // Create the object for a continuous left-bounded interval. TableExprNodeSetElem (Bool isLeftClosed, const TableExprNode& start); // Create the object for a continuous right-bounded interval. TableExprNodeSetElem (const TableExprNode& end, Bool isRightClosed); // Create the object for a mid-width interval (closed on both sides). TableExprNodeSetElem (const TableExprNode& mid, const TableExprNode& width); // Get the internal pointer to the underlying TableExprNodeSetElemBase. const TENSEBShPtr& getElem() const { return itsElem; } // Show the node. void show (ostream& os, uInt indent) const override { itsElem->show (os, indent); } // Is it a discrete set element. Bool isDiscrete() const { return itsElem->isDiscrete(); } // Is a single value given? Bool isSingle() const { return itsElem->isSingle(); } // Is the interval left or right closed? // Bool isLeftClosed() const { return itsElem->isLeftClosed(); } Bool isRightClosed() const { return itsElem->isRightClosed(); } // // Is the interval given as mid-width? Bool isMidWidth() const { return itsElem->isMidWidth(); } // Get the start, end or increment expression. // Note that the shared pointer returned can be null indicating that a // value was not given. // const TENShPtr& start() const { return itsElem->start(); } const TENShPtr& end() const { return itsElem->end(); } const TENShPtr& increment() const { return itsElem->increment(); } // private: // Set the data and expression type in the superclass. void init(); //# Data members TENSEBShPtr itsElem; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/ExprNodeSetOpt.cc000066400000000000000000000340301476623553700205710ustar00rootroot00000000000000//# ExprNodeSetOpt.cc: Classes representing an optimized set in table select expression //# Copyright (C) 2022 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableExprNodeSetOptBase::TableExprNodeSetOptBase (const TableExprNodeRep& orig) : TableExprNodeRep (orig) {} Bool TableExprNodeSetOptBase::contains (const TableExprId&, Int64 value) { return (find(value) >= 0); } Bool TableExprNodeSetOptBase::contains (const TableExprId&, Double value) { return (find(value) >= 0); } Bool TableExprNodeSetOptBase::contains (const TableExprId&, String value) { return (find(value) >= 0); } MArray TableExprNodeSetOptBase::contains (const TableExprId&, const MArray& value) { Array result(value.shape()); Bool deleteIn, deleteOut; const Int64* in = value.array().getStorage (deleteIn); Bool* out = result.getStorage (deleteOut); for (size_t i=0; i= 0); } value.array().freeStorage (in, deleteIn); result.putStorage (out, deleteOut); return MArray (result, value.mask()); } MArray TableExprNodeSetOptBase::contains (const TableExprId&, const MArray& value) { Array result(value.shape()); Bool deleteIn, deleteOut; const Double* in = value.array().getStorage (deleteIn); Bool* out = result.getStorage (deleteOut); for (size_t i=0; i= 0); } value.array().freeStorage (in, deleteIn); result.putStorage (out, deleteOut); return MArray (result, value.mask()); } MArray TableExprNodeSetOptBase::contains (const TableExprId&, const MArray& value) { Array result(value.shape()); Bool deleteIn, deleteOut; const String* in = value.array().getStorage (deleteIn); Bool* out = result.getStorage (deleteOut); for (size_t i=0; i= 0); } value.array().freeStorage (in, deleteIn); result.putStorage (out, deleteOut); return MArray (result, value.mask()); } Int64 TableExprNodeSetOptBase::find (Int64) const { return -1; } Int64 TableExprNodeSetOptBase::find (Double) const { return -1; } Int64 TableExprNodeSetOptBase::find (String) const { return -1; } template TableExprNodeSetOptUSet::TableExprNodeSetOptUSet (const TableExprNodeRep& orig, const Array& arr) : TableExprNodeSetOptBase (orig) { itsMap.clear(); auto iter = arr.begin(); for (size_t i=0; i void TableExprNodeSetOptUSet::show (ostream& os, uInt indent) const { TableExprNodeRep::show (os, indent); os << "Int set as std::unordered_map" << endl; } template Int64 TableExprNodeSetOptUSet::find (T value) const { auto iter = itsMap.find(value); if (iter == itsMap.end()) { return -1; } return iter->second; } template TableExprNodeSetOptContSetBase::TableExprNodeSetOptContSetBase (const TableExprNodeSet& orig, const std::vector& starts, const std::vector& ends) : TableExprNodeSetOptBase (orig), itsStarts (starts), itsEnds (ends) { AlwaysAssert (starts.size() == ends.size(), AipsError); } template void TableExprNodeSetOptContSetBase::show (ostream& os, uInt indent) const { TableExprNodeRep::show (os, indent); os << " TableExprNodeSetOptContSet with " << itsStarts.size() << " intervals" << endl << " start = " << itsStarts << endl << " end = " << itsEnds << endl; } template TENShPtr TableExprNodeSetOptContSetBase::transform (const TableExprNodeSet& set, Bool combine) { DebugAssert (set.size() > 0, AipsError); // Get all start values and sort them (indirectly) in ascending order. // Use lowest value if no start given. // Similar for all end values. Block stvals (set.size(), std::numeric_limits::lowest()); Block endvals(set.size(), std::numeric_limits::max()); // Make an id for the gets. // The values are constant, hence use an use arbitrary row number. TableExprId id(0); for (size_t i=0; istart()) { set[i]->getStart (id, stvals[i]); } if (set[i]->end()) { set[i]->getEnd (id, endvals[i]); } } // Sort the start values indirectly. Vector index; GenSortIndirect::sort (index, stvals, stvals.size()); std::vector newStart; std::vector newEnd; std::vector newLeftC; std::vector newRightC; if (!combine) { for (size_t i=0; iisLeftClosed()); newRightC.push_back (set[inx]->isRightClosed()); } } else { // Get the start and end value of first interval in sorted list. T stval = stvals[index[0]]; T endval = endvals[index[0]]; Bool leftC = set[index[0]]->isLeftClosed(); Bool rightC = set[index[0]]->isRightClosed(); // Loop through the next intervals and combine if possible. for (size_t i=1; iisLeftClosed() || set[index[i-1]]->isRightClosed()))) { // Overlap; update end if higher. if (end2 > endval) { endval = end2; rightC = set[inx]->isRightClosed(); } } else { // No overlap, so create the interval found and start a new one. newStart.push_back (stval); newEnd.push_back (endval); newLeftC.push_back (leftC); newRightC.push_back (rightC); stval = st2; endval = end2; leftC = set[inx]->isLeftClosed(); rightC = set[inx]->isRightClosed(); } } // Create the last interval. newStart.push_back (stval); newEnd.push_back (endval); newLeftC.push_back (leftC); newRightC.push_back (rightC); } // Now create the correct object. return createOptSet (set, newStart, newEnd, newLeftC, newRightC); } template TENShPtr TableExprNodeSetOptContSetBase::createOptSet (const TableExprNodeSet& set, const std::vector& start, const std::vector& end, const std::vector& leftC, const std::vector& rightC) { AlwaysAssert (start.size() == end.size(), AipsError); AlwaysAssert (leftC.size() == rightC.size(), AipsError); // See if all intervals have the same left and right closedness. // If so, a better version can be used that does not need to test on it // for each compare. Bool same = True; for (size_t i=1; i* optSet; if (same) { if (leftC[0]) { if (rightC[0]) { optSet = new TableExprNodeSetOptContSet,std::less_equal> (set, start, end, std::less_equal(), std::less_equal(), "CC"); } else { optSet = new TableExprNodeSetOptContSet,std::less> (set, start, end, std::less_equal(), std::less(), "CO"); } } else { if (rightC[0]) { optSet = new TableExprNodeSetOptContSet,std::less_equal> (set, start, end, std::less(), std::less_equal(), "OC"); } else { optSet = new TableExprNodeSetOptContSet,std::less> (set, start, end, std::less(), std::less(), "OO"); } } } else { optSet = new TableExprNodeSetOptContSetMixOC (set, start, end, leftC, rightC); } return TENShPtr(optSet); } template TableExprNodeSetOptContSetMixOC::TableExprNodeSetOptContSetMixOC (const TableExprNodeSet& orig, const std::vector& starts, const std::vector& ends, const std::vector& leftC, const std::vector& rightC) : TableExprNodeSetOptContSetBase (orig, starts, ends), itsLeftC (leftC), itsRightC (rightC) { AlwaysAssert (starts.size() == leftC.size(), AipsError); AlwaysAssert (starts.size() == rightC.size(), AipsError); } template void TableExprNodeSetOptContSetMixOC::show (ostream& os, uInt indent) const { TableExprNodeSetOptContSetBase::show (os, indent); os << " leftC = " << itsLeftC << endl << " rightC = " << itsRightC << endl; } template Int64 TableExprNodeSetOptContSetMixOC::find (T value) const { for (size_t i=0; iitsStarts.size(); ++i) { if ((value > this->itsStarts[i] && value < this->itsEnds[i]) || (value == this->itsStarts[i] && this->itsLeftC[i]) || (value == this->itsEnds[i] && this->itsRightC[i])) { return i; } } return -1; } template TableExprNodeSetOptContSet::TableExprNodeSetOptContSet (const TableExprNodeSet& orig, const std::vector& starts, const std::vector& ends, LeftComp leftCmp, RightComp rightCmp, const String& cmpType) : TableExprNodeSetOptContSetBase (orig, starts, ends), itsLeftCmp (leftCmp), itsRightCmp (rightCmp), itsCmpType (cmpType) {} template void TableExprNodeSetOptContSet::show (ostream& os, uInt indent) const { TableExprNodeSetOptContSetBase::show (os, indent); os << " as TableExprNodeSetOptContSet" << itsCmpType << endl; } template Int64 TableExprNodeSetOptContSet::find (T value) const { auto iter = std::upper_bound (this->itsEnds.begin(), this->itsEnds.end(), value, itsRightCmp); if (iter != this->itsEnds.end()) { size_t index = std::distance (this->itsEnds.begin(), iter); if (itsLeftCmp (this->itsStarts[index], value)) { return index; } } return -1; } // Instantiate as needed for Int64, Double and String. // Only the instantiated types are used in the TaQL code for the // IN and join operator. Datetime is handled as Double. // Bool, DComplex and Regex are not used in these operators. // std::less is for an open interval side, std::less_equal for a closed side. template class TableExprNodeSetOptUSet; template class TableExprNodeSetOptUSet; template class TableExprNodeSetOptContSetBase; template class TableExprNodeSetOptContSetBase; template class TableExprNodeSetOptContSetMixOC; template class TableExprNodeSetOptContSetMixOC; template class TableExprNodeSetOptContSet,std::less_equal>; template class TableExprNodeSetOptContSet,std::less>; template class TableExprNodeSetOptContSet,std::less_equal>; template class TableExprNodeSetOptContSet,std::less>; template class TableExprNodeSetOptContSet,std::less_equal>; template class TableExprNodeSetOptContSet,std::less>; template class TableExprNodeSetOptContSet,std::less_equal>; template class TableExprNodeSetOptContSet,std::less>; } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/ExprNodeSetOpt.h000066400000000000000000000241341476623553700204370ustar00rootroot00000000000000//# ExprNodeSetOpt.h: Classes representing an optimized set in table select expression //# Copyright (C) 2022 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_EXPRNODESETOPT_H #define TABLES_EXPRNODESETOPT_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Forward Declarations class TableExprNodeSet; // // Abstract base class for optimized set representations // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeSet // // // This class is the abstract base class for the optimized representation of // constant value or interval sets used by the IN operator or join operator. // // The find function can operate on integer, double and string values. // Note that datetimes are handled as doubles. It returns the index of the // value or interval matching the value searched for. // class TableExprNodeSetOptBase : public TableExprNodeRep { public: explicit TableExprNodeSetOptBase (const TableExprNodeRep& orig); // Does the set contain the given value? // They call the find function. // Bool contains (const TableExprId& id, Int64 value) override; Bool contains (const TableExprId& id, Double value) override; Bool contains (const TableExprId& id, String value) override; // // Tell for each array value if the set contains that value. // It calls the scalar contains function for each value. // MArray contains (const TableExprId& id, const MArray& value) override; MArray contains (const TableExprId& id, const MArray& value) override; MArray contains (const TableExprId& id, const MArray& value) override; // // Tell which key matches a value. -1 = no match. // The default implementations throw a 'not implemented' exception. //# The String version is passed by value to use the same mechanism //# as used for the other types to make templates possible. // virtual Int64 find (Int64 value) const; virtual Int64 find (Double value) const; virtual Int64 find (String value) const; // }; // // An optimized representation of a discrete selection set. // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeSet // // // This templated class is an optimized representation of an constant // integer or string array set used by the IN operator. // If applicable, TableExprLogicNode instantiates an object of this class. //
        The representation is a std::unordered_map containing the array values // and the index in the array. //
        Note that a std::unordered_map is used instead of std::map because its // hashing mechanism makes it faster. //
        template class TableExprNodeSetOptUSet: public TableExprNodeSetOptBase { public: // Construct an empty set. TableExprNodeSetOptUSet (const TableExprNodeRep& orig, const Array&); // Show the node. void show (ostream& os, uInt indent) const override; // Where does a value occur in the set? -1 is no match. Int64 find (T value) const override; private: std::unordered_map itsMap; }; // // An optimized representation of a selection set with continuous intervals. // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeSet // // // This class is the base class for the optimized representations of a // constant selection set with continuous intervals. // It holds the start and end values of the intervals. // template class TableExprNodeSetOptContSetBase: public TableExprNodeSetOptBase { public: // Construct from the original set and the start and end values of the // intervals. The vectors must have the same length. explicit TableExprNodeSetOptContSetBase (const TableExprNodeSet& orig, const std::vector& starts, const std::vector& ends); // Get the size (nr of intervals). size_t size() const { return itsStarts.size(); } // Show the node. void show (ostream& os, uInt indent) const override; // Transform a set into an optimized one by ordering the intervals // and optionally combining adjacent intervals. // If not possible, an empty TENShPtr is returned. static TENShPtr transform (const TableExprNodeSet& set, Bool combine=True); // Create the appropriate optimized OptContSet object. // Note that leftC and rightC do not need to have the same length as start/end. // If it is known that all intervals have the same leftC/rightC, // a single value suffices. static TENShPtr createOptSet (const TableExprNodeSet& set, const std::vector& start, const std::vector& end, const std::vector& leftC, const std::vector& rightC); protected: std::vector itsStarts; std::vector itsEnds; }; // // An optimized representation of a selection set with continuous intervals. // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeSet // // // This class is an optimized representation of a constant selection set // with continuous intervals using a mix of open and closed start and end. // If applicable, TableExprLogicNode instantiates an object of this class. //
        The representation has std::vector objects containing the start // and end values. A lookup using std::upper_bound on the end values is done // to determine if a value is contained in one of the intervals. //
        This templated class is instantiated for Double and String. //
        template class TableExprNodeSetOptContSetMixOC: public TableExprNodeSetOptContSetBase { public: TableExprNodeSetOptContSetMixOC (const TableExprNodeSet& orig, const std::vector& starts, const std::vector& ends, const std::vector& leftC, const std::vector& rightC); // Show the node. void show (ostream& os, uInt indent) const override; // Tell which interval contains a value. -1 = no match. Int64 find (T value) const override; protected: std::vector itsLeftC; std::vector itsRightC; }; // // An optimized representation of a selection set with similar intervals. // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeSetOptContSet // // // This class is a further optimized version of TableExprNodeSetOptContSet // for continuous intervals all using the same open/closed interval types. // It reduces the number of comparisons required. // The left and right comparison functors tell if an interval side is // open (uses std::less) or closed (uses std::less_equal). // template class TableExprNodeSetOptContSet: public TableExprNodeSetOptContSetBase { public: TableExprNodeSetOptContSet (const TableExprNodeSet& orig, const std::vector& starts, const std::vector& ends, LeftComp leftCmp, RightComp rightCmp, const String& cmpType); // Show the node. void show (ostream& os, uInt indent) const override; // Tell which interval contains a value. -1 = no match. Int64 find (T value) const override; private: LeftComp itsLeftCmp; RightComp itsRightCmp; String itsCmpType; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/ExprNodeUtil.cc000066400000000000000000000102261476623553700202710ustar00rootroot00000000000000//# ExprNodeUtil.cc: Utility functions for TableExprNodeRep objects //# Copyright (C) 2022 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN namespace TableExprNodeUtil { void checkAggrFuncs (TableExprNodeRep* node) { if (! getAggrNodes(node). empty()) { throw TableInvExpr("Invalid use of an aggregate function " "(only use in SELECT or HAVING clause)"); } } std::vector getAggrNodes (TableExprNodeRep* node) { std::vector allNodes; node->flattenTree (allNodes); std::vector aggrNodes; for (auto nodeP : allNodes) { if (nodeP->isAggregate()) { aggrNodes.push_back (nodeP); } } return aggrNodes; } std::vector getColumnNodes (TableExprNodeRep* node) { std::vector allNodes; node->flattenTree (allNodes); std::vector colNodes; for (auto nodeP : allNodes) { if (nodeP->operType() == TableExprNodeRep::OtColumn) { colNodes.push_back (nodeP); } } return colNodes; } std::vector
      • getNodeTables (TableExprNodeRep* node, Bool properMain) { std::vector allNodes; node->flattenTree (allNodes); std::vector
        tables; std::vector aliases; for (auto nodeP : allNodes) { TableExprInfo tabInfo = nodeP->getTableInfo(); if (! tabInfo.table().isNull()) { // A proper Table object. // Handle a join table only if needed. // Add the table if its alias is not processed before. if (!properMain || !tabInfo.isJoinTable()) { auto iter = std::find(aliases.begin(), aliases.end(), tabInfo.alias()); if (iter == aliases.end()) { tables.push_back (tabInfo.table()); aliases.push_back (tabInfo.alias()); } else { // Check that the same alias is the same table. size_t index = std::distance (aliases.begin(), iter); AlwaysAssert (tabInfo.table().isSameTable (tables[index]), AipsError); } } } } return tables; } rownr_t getCheckNRow (const std::vector
        & tables) { rownr_t nrow = 0; Bool first = True; for (const Table& tab : tables) { if (first) { nrow = tab.nrow(); first = False; } else { if (tab.nrow() != nrow) { throw TableInvExpr("Table " + tab.tableName() + " has " + String::toString(tab.nrow()) + " rows, " + "but previous tables have " + String::toString(nrow) + " rows"); } } } return nrow; } } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/ExprNodeUtil.h000066400000000000000000000060311476623553700201320ustar00rootroot00000000000000//# ExprNodeUtil.h: Utility functions for TableExprNodeRep objects //# Copyright (C) 2022 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_EXPRNODEUTIL_H #define TABLES_EXPRNODEUTIL_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Class to handle a Regex or StringDistance. // // // // // //# Classes you should understand before using this one. //
      • Regex //
      • StringDistance // // // A StringDistance (Levensthein distance) in TaQL is given in the same way // as a Regex. This class is needed to have a single object in the parse tree // objects containing them (in class TableExprNodeConstRegex). // namespace TableExprNodeUtil { // Throw an exception if an aggregate function is used in // the expression node or its children. void checkAggrFuncs (TableExprNodeRep* node); // Get the aggregate function nodes used in the node and its children. std::vector getAggrNodes (TableExprNodeRep* node); // Get the column nodes used in the node and its children. std::vector getColumnNodes (TableExprNodeRep* node); // Get the (unique) tables used in the node and its children. // If properMain only proper main tables (i.e., tables // specified in the FROM clause) are returned. std::vector
      • getNodeTables (TableExprNodeRep* node, Bool properMain); // Get the nr of rows in the tables used. // An exception is thrown if the tables differ in the nr of rows. rownr_t getCheckNRow (const std::vector
        &); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/ExprRange.cc000066400000000000000000000135231476623553700176050ustar00rootroot00000000000000//# ExprRange.cc: Select range of a column in an select expression //# Copyright (C) 1994,1995,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableExprRange::TableExprRange() : tabColPtr_p(0) {} TableExprRange::TableExprRange(const TableColumn& col, double stval, double endval) : sval_p (1), eval_p (1), tabColPtr_p(0) { tabColPtr_p = new TableColumn(col); sval_p(0) = stval; eval_p(0) = endval; } TableExprRange::TableExprRange (const TableExprRange& that) : sval_p (that.sval_p), eval_p (that.eval_p), tabColPtr_p(0) { if (that.tabColPtr_p != 0) { tabColPtr_p = new TableColumn (*(that.tabColPtr_p)); } } TableExprRange::~TableExprRange() { delete tabColPtr_p; } TableExprRange& TableExprRange::operator= (const TableExprRange& that) { if (this != &that) { sval_p = that.sval_p; eval_p = that.eval_p; delete tabColPtr_p; if (that.tabColPtr_p != 0) { tabColPtr_p = new TableColumn (*(that.tabColPtr_p)); } } return *this; } const TableColumn& TableExprRange::getColumn() const { return *tabColPtr_p; } //# And two vectors of ranges. //# This means taking the intersection of all ranges in both vectors. //# Note that the start values are kept in ascending order, so that //# makes searching easier. void TableExprRange::mixAnd (const TableExprRange& that) { //# Allocate vectors (long enough) to hold the result. size_t nrres=0; Vector stres (sval_p.nelements() + that.sval_p.nelements()); Vector endres(sval_p.nelements() + that.sval_p.nelements()); //# Loop through all intervals of this. for (size_t i=0; i eval_p(i)) { break; // that past this; next this } if (that.eval_p(j) >= sval_p(i)) { // overlap stres(nrres) = max (sval_p(i), that.sval_p(j)); endres(nrres) = min (eval_p(i), that.eval_p(j)); nrres++; } } } //# Now copy the result (nrres elements of course) into this. sval_p.resize(nrres); eval_p.resize(nrres); if (nrres > 0) { sval_p = stres (Slice(0,nrres)); eval_p = endres(Slice(0,nrres)); } } void TableExprRange::mixOr (const TableExprRange& that) { //# Allocate vectors (long enough) to hold the result. size_t nrres=0; Vector stres (sval_p.nelements() + that.sval_p.nelements()); Vector endres(sval_p.nelements() + that.sval_p.nelements()); size_t j=0; //# Loop through all intervals of this. //# Store in the result, while inserting the that intervals, //# in order of start-value. for (size_t i=0; i stmp(nrres); Vector etmp(nrres); j = 0; stmp(0) = stres(0); // first interval etmp(0) = endres(0); for (size_t i=1; i etmp(j)) { etmp(j) = endres(i); // higher end-value } }else{ j++; // no overlap, stmp(j) = stres(i); // so insert interval etmp(j) = endres(i); } } nrres = j+1; //# Now set vectors to their final length and store values in them. //# Note that (opposite to Block) the Vector resize function does not //# preserve the values in the vector, so therefore we could not use //# sval_p/eval_p, but had to use temporaries. sval_p.resize(nrres); eval_p.resize(nrres); if (nrres > 0) { sval_p = stmp(Slice(0,nrres)); eval_p = etmp(Slice(0,nrres)); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/ExprRange.h000066400000000000000000000107671476623553700174560ustar00rootroot00000000000000//# ExprRange.h: Select range of a column in an select expression //# Copyright (C) 1994,1995,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_EXPRRANGE_H #define TABLES_EXPRRANGE_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableColumn; // // Select range of a column in an select expression // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // TableExprRange represents the ranges of a column as specified in // a table select expression. // // // TableExprRange holds the ranges of values for a column as specified // in a table select expression. // It traverses the expression tree and composes the hull of the values. // Only double values are taken into account. // It can handle operators &&, ||, ==, >, >=, <, <=, !. // It can handle a comparison operator only for a column with a constant. // Other operators and expressions are non-convertable. // // The ranges function in class TableExprNode returns a Block // of TableExprRange objects which contains the ranges for each // (applicable) column used in the expression. // // // TableExprRange gives great possibilities in optimizing a table // selection. It allows to get a rough estimate of the values needed // for a column which can be used to do a fast preselect using an index. // // //# A List of bugs, limitations, extensions or planned refinements. //
      • Support other data types than double //
      • Recognize that 2*COL<3 is equal to COL<3/2 // class TableExprRange { public: // Default constructor (needed for Block). TableExprRange(); // Construct from a column and a single constant range. TableExprRange (const TableColumn&, double stval, double endval); // Copy constructor. TableExprRange (const TableExprRange&); ~TableExprRange (); // Assignment operator (copy semantics). TableExprRange& operator= (const TableExprRange&); // Return the vector of start values. // Together with the equally sized vector of end values, this forms // the ranges for the column (which can be acquired using getColumn). const Vector& start() const; // Return the vector of end values. // Together with the equally sized vector of start values, this forms // the ranges for the column (which can be acquired using getColumn). const Vector& end() const; // Return the column object. const TableColumn& getColumn() const; //*display 4 // Mix with another range for an AND expression. void mixAnd (const TableExprRange&); //*display 4 // Mix with another range for an OR expression. void mixOr (const TableExprRange&); private: Vector sval_p; //# start values Vector eval_p; //# end values TableColumn* tabColPtr_p; //# pointer to column }; inline const Vector& TableExprRange::start() const { return sval_p; } inline const Vector& TableExprRange::end() const { return eval_p; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/ExprUDFNode.cc000066400000000000000000000066601476623553700200010ustar00rootroot00000000000000//# ExprUDFNode.cc: Class representing a scalar UDF in select expression //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableExprUDFNode::TableExprUDFNode (const std::shared_ptr& udf, const TableExprInfo& tabInfo, const TableExprNodeSet& source) : TableExprNodeMulti (udf->dataType(), VTScalar, OtFunc, source), itsTableInfo (tabInfo), itsUDF (udf) { // The source may be empty which causes the expression type // to be made constant. Force it to be variable if needed. if (udf->isConstant()) { exprtype_p = Constant; } else { exprtype_p = Variable; } // Set the unit and attributes (is also fine if undefined). setUnit (Unit(udf->getUnit())); setAttributes (udf->getAttributes()); } void TableExprUDFNode::flattenTree (vector& nodes) { nodes.push_back (this); itsUDF->flattenTree (nodes); } TableExprInfo TableExprUDFNode::getTableInfo() const { return itsTableInfo; } void TableExprUDFNode::disableApplySelection() { itsUDF->disableApplySelection(); } void TableExprUDFNode::applySelection (const Vector& rownrs) { itsUDF->applySelection (rownrs); } std::shared_ptr TableExprUDFNode::makeGroupAggrFunc() { return std::make_shared(this); } Bool TableExprUDFNode::getBool (const TableExprId& id) { return itsUDF->getBool (id); } Int64 TableExprUDFNode::getInt (const TableExprId& id) { return itsUDF->getInt (id); } Double TableExprUDFNode::getDouble (const TableExprId& id) { return itsUDF->getDouble (id); } DComplex TableExprUDFNode::getDComplex (const TableExprId& id) { return itsUDF->getDComplex (id); } String TableExprUDFNode::getString (const TableExprId& id) { return itsUDF->getString (id); } TaqlRegex TableExprUDFNode::getRegex (const TableExprId& id) { return itsUDF->getRegex (id); } MVTime TableExprUDFNode::getDate (const TableExprId& id) { return itsUDF->getDate (id); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/ExprUDFNode.h000066400000000000000000000110261476623553700176330ustar00rootroot00000000000000//# ExprUDFNode.h: Class representing a scalar UDF in select expression //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_EXPRUDFNODE_H #define TABLES_EXPRUDFNODE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableExprNodeSet; // // Class representing a scalar UDF in select expression // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeMulti // // // This class represents a function in a table select tree. // The rownumber function is represented by class // TableExprNodeRownr. // The rowid function is represented by class // TableExprNodeRowid. // The rand function is represented by class // TableExprNodeRandom. //

        // When one wants to add a function to the table selection grammar, // the following has to be done: //

          //
        • Add the function to the enum below. //
        • Implement the function in the get functions in ExprFuncNode(Array).cc. //
        • Implement the function in the checkOperands in ExprFuncNode.cc. //
        • Declare and define the function in ExprNode.h (for C++ binding). //
        • Add the function to findFunc in TableParse.cc (for TaQL). //
        //
        class TableExprUDFNode: public TableExprNodeMulti { public: // Constructor TableExprUDFNode (const std::shared_ptr& udf, const TableExprInfo&, const TableExprNodeSet& source); // Destructor ~TableExprUDFNode() override = default; // Is the UDF an aggregate function? Bool isAggregate() const override { return itsUDF->isAggregate(); } // Get the table info. TableExprInfo getTableInfo() const override; // Flatten the node tree by adding the node and its children to the vector. void flattenTree (std::vector&) override; // Do not apply the selection. void disableApplySelection() override; // If needed, let the UDF re-create column objects for a selection of rows. // It calls the function recreateColumnObjects. void applySelection (const Vector& rownrs) override; // UDFs do not need a TableExprGroupFuncBase, // so TableExprGroupNull is returned. std::shared_ptr makeGroupAggrFunc() override; // Functions to get the desired result of a function // Bool getBool (const TableExprId& id) override; Int64 getInt (const TableExprId& id) override; Double getDouble (const TableExprId& id) override; DComplex getDComplex (const TableExprId& id) override; String getString (const TableExprId& id) override; TaqlRegex getRegex (const TableExprId& id) override; MVTime getDate (const TableExprId& id) override; // private: TableExprInfo itsTableInfo; std::shared_ptr itsUDF; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/ExprUDFNodeArray.cc000066400000000000000000000067001476623553700207730ustar00rootroot00000000000000//# ExprUDFNodeArray.cc: Class representing an array UDF in select expression //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableExprUDFNodeArray::TableExprUDFNodeArray (const std::shared_ptr& udf, const TableExprInfo& tabInfo, const TableExprNodeSet&) : TableExprNodeArray (udf->dataType(), OtFunc), itsTableInfo (tabInfo), itsUDF (udf) { // The source may be empty which causes the expression type // to be made constant. Force it to be variable if needed. if (udf->isConstant()) { exprtype_p = Constant; } else { exprtype_p = Variable; } // Set the unit and attributes (is also fine if undefined). setUnit (Unit(udf->getUnit())); setAttributes (udf->getAttributes()); } void TableExprUDFNodeArray::flattenTree (vector& nodes) { nodes.push_back (this); itsUDF->flattenTree (nodes); } TableExprInfo TableExprUDFNodeArray::getTableInfo() const { return itsTableInfo; } void TableExprUDFNodeArray::disableApplySelection() { itsUDF->disableApplySelection(); } void TableExprUDFNodeArray::applySelection (const Vector& rownrs) { itsUDF->applySelection (rownrs); } std::shared_ptr TableExprUDFNodeArray::makeGroupAggrFunc() { return std::make_shared(this); } MArray TableExprUDFNodeArray::getArrayBool (const TableExprId& id) { return itsUDF->getArrayBool (id); } MArray TableExprUDFNodeArray::getArrayInt (const TableExprId& id) { return itsUDF->getArrayInt (id); } MArray TableExprUDFNodeArray::getArrayDouble (const TableExprId& id) { return itsUDF->getArrayDouble (id); } MArray TableExprUDFNodeArray::getArrayDComplex(const TableExprId& id) { return itsUDF->getArrayDComplex (id); } MArray TableExprUDFNodeArray::getArrayString (const TableExprId& id) { return itsUDF->getArrayString (id); } MArray TableExprUDFNodeArray::getArrayDate (const TableExprId& id) { return itsUDF->getArrayDate (id); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/ExprUDFNodeArray.h000066400000000000000000000106371476623553700206410ustar00rootroot00000000000000//# ExprUDFNodeArray.h: Class representing an array UDF in select expression //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_EXPRUDFNODEARRAY_H #define TABLES_EXPRUDFNODEARRAY_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableExprNodeSet; // // Class representing an array UDF in select expression // // // // // //# Classes you should understand before using this one. //
      • TableExprNodeMulti // // // This class represents a function in a table select tree. // The rownumber function is represented by class // TableExprNodeRownr. // The rowid function is represented by class // TableExprNodeRowid. // The rand function is represented by class // TableExprNodeRandom. //

        // When one wants to add a function to the table selection grammar, // the following has to be done: //

          //
        • Add the function to the enum below. //
        • Implement the function in the get functions in ExprFuncNode(Array).cc. //
        • Implement the function in the checkOperands in ExprFuncNode.cc. //
        • Declare and define the function in ExprNode.h (for C++ binding). //
        • Add the function to findFunc in TableParse.cc (for TaQL). //
        //
        class TableExprUDFNodeArray: public TableExprNodeArray { public: // Constructor TableExprUDFNodeArray (const std::shared_ptr& udf, const TableExprInfo&, const TableExprNodeSet& source); // Destructor ~TableExprUDFNodeArray() override = default; // Flatten the node tree by adding the node and its children to the vector. void flattenTree (std::vector&) override; // Get the table info. TableExprInfo getTableInfo() const override; // Do not apply the selection. void disableApplySelection() override; // If needed, let the UDF re-create column objects for a selection of rows. // It calls the function recreateColumnObjects. void applySelection (const Vector& rownrs) override; // UDFs do not need a TableExprGroupFuncBase, so null is returned. std::shared_ptr makeGroupAggrFunc() override; // Functions to get the desired result of a function // MArray getArrayBool (const TableExprId& id) override; MArray getArrayInt (const TableExprId& id) override; MArray getArrayDouble (const TableExprId& id) override; MArray getArrayDComplex (const TableExprId& id) override; MArray getArrayString (const TableExprId& id) override; MArray getArrayDate (const TableExprId& id) override; // private: TableExprInfo itsTableInfo; std::shared_ptr itsUDF; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/ExprUnitNode.cc000066400000000000000000000133331476623553700202750ustar00rootroot00000000000000//# ExprUnitNode.cc: Nodes representing unit handling in table select expression tree //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableExprNodeUnit::TableExprNodeUnit (const TENShPtr& child, const Unit& unit) : TableExprNodeBinary (child->dataType(), *child, OtUndef) { // Units imply conversion, thus result cannot be integer. if (dtype_p == NTInt) { dtype_p = NTDouble; } lnode_p = child; factor_p = set (*this, child, unit); } TableExprNodeUnit::~TableExprNodeUnit() {} Double TableExprNodeUnit::set (TableExprNodeRep& parent, const TENShPtr& child, const Unit& unit) { Double factor = 1; if (unit.empty()) { parent.setUnit (child->unit()); } else { if (! child->unit().empty()) { // Conversion is only possible between units of the same type // and between time/angle. UnitVal type1 = unit.getValue(); UnitVal type2 = child->unit().getValue(); if (! (type1 == type2 || (type1 == UnitVal::ANGLE && type2 == UnitVal::TIME) || (type2 == UnitVal::ANGLE && type1 == UnitVal::TIME))) { throw TableInvExpr ("Units " + unit.getName() + " and " + child->unit().getName() + " do not conform"); } // Get conversion factor. Quantity q(1., child->unit()); factor = q.getValue (unit); } parent.setUnit (unit); } return factor; } TENShPtr TableExprNodeUnit::useUnit (const TENShPtr& node, const Unit& unit) { // No conversion needed if a unit is empty. // However, always set the node's unit to the new unit. if (unit.empty() || node->unit().empty()) { node->setUnit (unit); return node; } // A conversion might be needed. // A set has to create conversion nodes for its elements. if (node->valueType() == VTSet || node->valueType() == VTSetElem) { node->adaptSetUnits(unit); return node; } // Create a unit conversion node for a scalar or array. TENShPtr tsnptr; if (node->valueType() == VTScalar) { tsnptr = std::make_shared(node, unit); } else { tsnptr = std::make_shared(node, unit); } if (tsnptr->getUnitFactor() == 1.) { // Units are the same, so no conversion needed. return node; } return tsnptr; } void TableExprNodeUnit::adaptUnit (TENShPtr& node, const Unit& unit) { // See if a conversion is needed. // If so, adapt the reference counts and replace it. node = useUnit (node, unit); } Unit TableExprNodeUnit::adaptUnits (TENShPtr& node1, TENShPtr& node2, TENShPtr& node3) { // Find unit to be used. Unit unit; if (unit.empty() && node1) unit = node1->unit(); if (unit.empty() && node2) unit = node2->unit(); if (unit.empty() && node3) unit = node3->unit(); if (! unit.empty()) { if (node1) adaptUnit (node1, unit); if (node2) adaptUnit (node2, unit); if (node3) adaptUnit (node3, unit); } return unit; } Double TableExprNodeUnit::getUnitFactor() const { return factor_p; } Double TableExprNodeUnit::getDouble (const TableExprId& id) { return factor_p * lnode_p->getDouble(id); } DComplex TableExprNodeUnit::getDComplex (const TableExprId& id) { return factor_p * lnode_p->getDComplex(id); } TableExprNodeArrayUnit::TableExprNodeArrayUnit (const TENShPtr& child, const Unit& unit) : TableExprNodeArray (*child, child->dataType(), OtUndef) { // Units imply conversion, thus result cannot be integer. if (dtype_p == NTInt) { dtype_p = NTDouble; } lnode_p = child; factor_p = TableExprNodeUnit::set (*this, child, unit); } TableExprNodeArrayUnit::~TableExprNodeArrayUnit() {} Double TableExprNodeArrayUnit::getUnitFactor() const { return factor_p; } MArray TableExprNodeArrayUnit::getArrayDouble (const TableExprId& id) { MArray arr = lnode_p->getArrayDouble(id); return MArray (factor_p * arr.array(), arr.mask()); } MArray TableExprNodeArrayUnit::getArrayDComplex(const TableExprId& id) { MArray arr = lnode_p->getArrayDComplex(id); return MArray (DComplex(factor_p) * arr.array(), arr.mask()); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/ExprUnitNode.h000066400000000000000000000102411476623553700201320ustar00rootroot00000000000000//# ExprUnitNode.h: Nodes representing unit handling in table select expression tree //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_EXPRUNITNODE_H #define TABLES_EXPRUNITNODE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Unit for scalar values in a table select expression tree // // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • Unit // // // This class represents a unit in a table select expression tree. // It contains a unit conversion factor to convert the child to this unit. // The factor is 1 if the child has no unit. // class TableExprNodeUnit : public TableExprNodeBinary { public: // Construct from the given child node and unit. TableExprNodeUnit (const TENShPtr& child, const Unit& unit); ~TableExprNodeUnit(); // Calculate the conversion factor and return it. // It is static to be useful for TableExprNodeArrayFunc as well. static Double set (TableExprNodeRep& parent, const TENShPtr& child, const Unit& unit); // Create a new node if unit conversion is needed. // Otherwise return the current node. static TENShPtr useUnit (const TENShPtr& node, const Unit& unit); // Use useUnit to see if a conversion is needed. // If so, adapt the reference counts and replace the node. static void adaptUnit (TENShPtr& node, const Unit& unit); // Find the unit to be used and adapt the nodes to it. static Unit adaptUnits (TENShPtr& node1, TENShPtr& node2, TENShPtr& node3); // Get the unit factor. virtual Double getUnitFactor() const; virtual Double getDouble (const TableExprId& id); virtual DComplex getDComplex (const TableExprId& id); private: Double factor_p; }; // // Unit for array values in a table select expression tree // // // // // // //# Classes you should understand before using this one. //
      • TableExprNode //
      • Unit // // // This class represents a unit in a table select expression tree. // It contains a unit conversion factor to convert the child to this unit. // The factor is 1 if the child has no unit. // class TableExprNodeArrayUnit : public TableExprNodeArray { public: TableExprNodeArrayUnit (const TENShPtr& child, const Unit& unit); ~TableExprNodeArrayUnit(); virtual Double getUnitFactor() const; virtual MArray getArrayDouble (const TableExprId& id); virtual MArray getArrayDComplex (const TableExprId& id); private: Double factor_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/MArray.h000066400000000000000000000165301476623553700167500ustar00rootroot00000000000000//# MArray.h: Class to handle an Array with an optional mask //# Copyright (C) 2012 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MARRAY_H #define CASA_MARRAY_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Class to handle an Array with an optional mask // // // // // //# Classes you should understand before using this one. //
      • Array //
      • MArrayBase // // // This class makes it easier to handle arrays with ot without mask. // The array is always present, but the mask is optional. The mask is // contained in the non-templated base class MArrayBase and functions // to operate on the mask are defined there. //
        The class is primarily developed for TaQL masked arrays, but // could be used elsewhere as well. // // A mask value True means that the corresponding value is masked off, thus // not taken into account in reduction functions like sum. This // is the same as the numpy masked array. // // MArrayMath.h contains many functions to operate on MArray objects // (addition, sin, etc.). //
        template class MArray: public MArrayBase { public: // Default constructor creates a null array. MArray() : MArrayBase (True) {} // Construct from an array without a mask. // It references the given array. explicit MArray (const Array& array) : MArrayBase (False), itsArray (array) { resizeBase (array, False); } // Construct from an array and a mask. // It references the given arrays. // isNull=True requires the arrays to be empty. MArray (const Array& array, const Array& mask, Bool isNull=False) : MArrayBase (array, mask, isNull), itsArray (array) {} // Construct from an array with the mask and null from another MArray. // It references the given arrays. // The shapes of both arrays must match. MArray (const Array& array, const MArrayBase& marray) : MArrayBase (array, marray), itsArray (array) {} // Construct from two MArrays, one the array, the other the mask. // If one of them is null, the constructed MArray is null. MArray (const MArray& array, const MArray& mask) : MArrayBase (array.isNull() || mask.isNull()) { if (! isNull()) { itsArray.reference (array.array()); setBase (itsArray, mask.array()); } } // Reference another array. void reference (const MArray& other) { itsArray.reference (other.itsArray); referenceBase (other); } // Resize the array and optionally the mask. // It always sets the MArray to non-null. void resize (const IPosition& shape, Bool useMask) { itsArray.resize (shape); resizeBase (itsArray, useMask); } // Copy the array data and possible mask from another one. // The shapes do not need to match. // The array data is copied, but the new mask references the possible // mask in from. template void fill (const MArray& from) { itsArray.resize (from.shape()); convertArray (itsArray, from.array()); setBase (itsArray, from.mask()); } // Copy the array from a normal Array. The possible mask is removed. // The shapes do not need to match. // The array data is always copied. template void fill (const Array& from) { itsArray.resize (from.shape()); convertArray (itsArray, from); resizeBase (itsArray, False); } // Get access to the array. // const Array& array() const { return itsArray; } Array& array() { return itsArray; } // // Flatten the unmasked elements of the array to a vector. Vector flatten() const; // Copy the unmasked elements to the out. The argument size // gives the size of the output buffer which should be at least the // size of the array. It returns the nr of unmasked elements. size_t flatten (T* out, size_t size) const; // Get a subset of the array. MArray operator() (const IPosition& start, const IPosition& end, const IPosition& stride) { if (hasMask()) { return MArray (itsArray(start, end, stride), mask()(start, end, stride)); } return MArray (itsArray(start, end, stride)); } private: Array itsArray; }; //# Implement functions. template Vector MArray::flatten() const { Vector vec(nvalid()); // We lie about the size, because we know the buffer has the right size. flatten (vec.data(), itsArray.size()); return vec; } template size_t MArray::flatten (T* out, size_t size) const { if (size < itsArray.size()) { throw ArrayError ("MArray::flatten - size " + std::to_string(size) + " of output buffer is too small"); } size_t nr = 0; if (!hasMask()) { // No mask, so copy all elements. Array arr(itsArray.shape(), out, SHARE); arr = itsArray; nr = arr.size(); } else { // Copy only the valid elements. if (itsArray.contiguousStorage() && mask().contiguousStorage()) { typename Array::const_contiter miter = mask().cbegin(); typename Array::const_contiter iterEnd = itsArray.cend(); for (typename Array::const_contiter iter=itsArray.cbegin(); iter!=iterEnd; ++iter, ++miter) { if (!*miter) out[nr++] = *iter; } } else { typename Array::const_iterator miter = mask().begin(); typename Array::const_iterator iterEnd = itsArray.end(); for (typename Array::const_iterator iter=itsArray.begin(); iter!=iterEnd; ++iter, ++miter) { if (!*miter) out[nr++] = *iter; } } } return nr; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/MArrayBase.cc000066400000000000000000000077361476623553700177110ustar00rootroot00000000000000//# MArrayBase.cc: Base class for array used in a TableExprNode with an optional mask //# Copyright (C) 2012 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include //# needed for correct build #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Construct from a mask. MArrayBase::MArrayBase (const ArrayBase& arr, const Array& mask, Bool isNull) : itsMask (mask), itsShape (arr.shape()), itsSize (arr.size()), itsNValid (arr.size()), itsNull (isNull) { init(); } MArrayBase::MArrayBase (const ArrayBase& arr, const MArrayBase& marray) : itsMask (marray.mask()), itsShape (arr.shape()), itsSize (arr.size()), itsNValid (arr.size()), itsNull (marray.isNull()) { init(); } void MArrayBase::init() { if (itsNull) { AlwaysAssert (itsShape.empty() && itsMask.empty(), AipsError); } else if (! itsMask.empty()) { itsNValid = -1; if (! itsShape.isEqual (itsMask.shape())) { std::ostringstream os; os << "MArrayBase - array shape " << itsShape << " and mask shape " << itsMask.shape() << " mismatch"; throw ArrayError (os.str()); } } } void MArrayBase::resizeBase (const ArrayBase& arr, Bool useMask) { itsShape.resize (arr.ndim()); itsShape = arr.shape(); itsSize = arr.size(); itsNull = False; if (useMask) { itsMask.resize (arr.shape()); itsNValid = -1; } else { removeMask(); } } void MArrayBase::referenceBase (const MArrayBase& other) { itsMask.reference (other.itsMask); itsShape.resize (other.itsShape.size()); itsShape = other.itsShape; itsSize = other.itsSize; itsNValid = other.itsNValid; itsNull = other.itsNull; } void MArrayBase::setBase (const ArrayBase& arr, const Array& mask) { itsShape.resize (arr.ndim()); itsShape = arr.shape(); itsSize = arr.size(); itsNull = False; setMask (mask); } void MArrayBase::setMask (const Array& mask) { if (mask.empty()) { removeMask(); } else { AlwaysAssert (itsShape.isEqual (mask.shape()), AipsError); itsMask.reference (mask); itsNValid = -1; } } Array MArrayBase::combineMask (const MArrayBase& other) const { if (itsMask.empty()) { return other.itsMask; } else if (other.itsMask.empty()) { return itsMask; } // Combine the flags of masked-off values. return itsMask || other.itsMask; } void MArrayBase::fillNValid() const { if (hasMask()) { itsNValid = nfalse(itsMask); } else { itsNValid = itsSize; } } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/MArrayBase.h000066400000000000000000000134121476623553700175370ustar00rootroot00000000000000//# MArrayBase.h: Base class for an array with an optional mask //# Copyright (C) 2012 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MARRAYBASE_H #define CASA_MARRAYBASE_H //# Includes #include #include #include //# Define the mask value indicating a valid value. //# In this way it is easy to change it to another value (if ever needed). //# The current setting is the same as used in numpy's masked_array and //# in the MeasurementSet's FLAG column. //# But the opposite value sounds somewhat better (same as MaskedArray) //# because something like DATA[isnan(DATA)] = 0 is much more intuitive. //# #define MArrayValid False //# #define MArrayInvalid True namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Base class for an array with an optional mask // // // // // //# Classes you should understand before using this one. //
      • Array // // // This class is the base class of the templated class MArray. It contains // the functions that are not template dependent. // // MArray is developed to make it easier to handle arrays with an // optional mask. The array is always present, but the mask is optional. // MArrayMath contains functions to operate on such arrays. // // Similar to numpy.masked_array and the MeasurementSet FLAG definition, // a mask value True means that the corresponding value is masked off, // thus is not taken into account in reduction functions like sum. // on a masked array. In operations like addition, masked off values are // processed because testing the mask value is more expensive than an // addition (even if the value is a NaN). For an operation with multiple // operands, the mask of the result is the OR of the operand masks. // // MArray can be null meaning that the array is a null value. It can be // used to indicate that a table cell does not contain an array. // A null MArray has an empty array and mask. Operations where an operand // is a null MArray, result in a null MArray. // class MArrayBase { protected: // The default constructor creates an empty mask. explicit MArrayBase (Bool isNull) : itsSize (0), itsNValid (0), itsNull (isNull) {} // Construct from a given array shape and mask. MArrayBase (const ArrayBase& arr, const Array& mask, Bool isNull); // Construct from a given array shape and mask from another MArray. MArrayBase (const ArrayBase& arr, const MArrayBase& marray); // Reference the mask and set the shape. void setBase (const ArrayBase& arr, const Array& mask); // Reference another MArray. void referenceBase (const MArrayBase& other); // Set the array shape and resize the mask. void resizeBase (const ArrayBase& arr, Bool useMask); public: // Is the array null? Bool isNull() const { return itsNull; } // Remove the mask. void removeMask() { itsMask.resize(); itsNValid = itsSize; } // Is there a mask? Bool hasMask() const { return !itsMask.empty(); } // Set the mask. It checks if it matches the array shape. void setMask (const Array& mask); // Get the mask. The returned array is empty if there is no mask. const Array& mask() const { return itsMask; } Array& wmask() { return itsMask; } // Return the number of valid array values, thus unflagged elements. Int64 nvalid() const { if (itsNValid < 0) fillNValid(); return itsNValid; } // Is the array empty? Bool empty() const { return itsSize == 0; } // Get the dimensionality. uInt ndim() const { return itsShape.size(); } // Get the shape. const IPosition& shape() const { return itsShape; } // Get the size. // size_t size() const { return itsSize; } size_t nelements() const { return itsSize; } // // Combine this and the other mask. // One or both MArray-s can be unmasked. Array combineMask (const MArrayBase& other) const; private: // Initialize and check. void init(); // Fill the number of valid values. void fillNValid() const; //# Data members. Array itsMask; IPosition itsShape; size_t itsSize; mutable Int64 itsNValid; Bool itsNull; // True = array is null, thus undefined in a column }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/MArrayLogical.h000066400000000000000000000533251476623553700202460ustar00rootroot00000000000000//# MArrayLogical.h: Logical operations on MArray objects //# Copyright (C) 2012 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MARRAYLOGICAL_H #define CASA_MARRAYLOGICAL_H //# Includes #include #include #include #include #include namespace casacore { // // Logical operations for MArray objects. // // // // // //
      • MArray // // // // These functions perform element by element logical operations on // optionally masked arrays and/or scalars. // If two arrays are used, the arrays must conform, except for allEQ which // returns False if the arrays do not conform. // // The functions in this file can be divided in 3 groups: //
          //
        • Full array operations like ==, near, etc. // They are defined for array-array and array-scalar operations. Arrays // shapes have to be conformant. They operate on all elements // (also the masked ones). The result is an MArray with the same // shape as the input array(s). It will have a mask if one of the // operands has a mask. If both operands have a mask, the resulting // mask is the OR of both masks. //
        • Full reduction functions like ntrue, all, allEQ, etc. // They operate on the unmasked elements only. If there are no unmasked // elements, the results is 0 or True. //
        • Reduction functions working on unmasked elements in parts of the // input array. The result is an MArray that has a mask if the input // array has a mask. An output element is masked off if its input // part has no unmasked elements. // The functors defined at the beginning of this file are used to // operate on each part. // There are 3 flavours: //
            //
          • partialXXX reduces one or more axes. E.g. one can count the // number of True elements for particular array axes. // The result is an array with a lower dimensionality. // They can be seen as a special versions of the boxedXXX functions. //
          • slidingXXX operates in a sliding window over the array. So the // result is an array with the same shape as the input, although // the output array is smaller if the edge is not filled. //
          • boxedXXX divides the input array in boxes with the given size // and operates on each box. The result is an array with the same // dimensionality, but with a smaller size. // If the box size does not fit integrally, the edge box is smaller. //
          //
        //
        // // // Define functors to perform a reduction function on an MArray object. // template class MNTrueFunc : public MArrayFunctorBase { public: virtual ~MNTrueFunc() {} RES operator() (const MArray& arr) const { return ntrue(arr); } }; template class MNFalseFunc : public MArrayFunctorBase { public: virtual ~MNFalseFunc() {} RES operator() (const MArray& arr) const { return nfalse(arr); } }; template class MAllFunc : public MArrayFunctorBase { public: virtual ~MAllFunc() {} Bool operator() (const MArray& arr) const { return allTrue(arr); } }; template class MAnyFunc : public MArrayFunctorBase { public: virtual ~MAnyFunc() {} Bool operator() (const MArray& arr) const { return anyTrue(arr); } }; // // Define comparison functions between 2 MArray objects and // between MArray object and scalar. // template MArray operator== (const MArray& left, const MArray& right) { return (left.isNull() || right.isNull() ? MArray() : MArray (left.array() == right.array(), left.combineMask(right))); } template MArray operator<= (const MArray& left, const MArray& right) { return (left.isNull() || right.isNull() ? MArray() : MArray (left.array() <= right.array(), left.combineMask(right))); } template MArray operator< (const MArray& left, const MArray& right) { return (left.isNull() || right.isNull() ? MArray() : MArray (left.array() < right.array(), left.combineMask(right))); } template MArray operator>= (const MArray& left, const MArray& right) { return (left.isNull() || right.isNull() ? MArray() : MArray (left.array() >= right.array(), left.combineMask(right))); } template MArray operator> (const MArray& left, const MArray& right) { return (left.isNull() || right.isNull() ? MArray() : MArray (left.array() > right.array(), left.combineMask(right))); } template MArray operator!= (const MArray& left, const MArray& right) { return (left.isNull() || right.isNull() ? MArray() : MArray (left.array() != right.array(), left.combineMask(right))); } template MArray operator|| (const MArray& left, const MArray& right) { return (left.isNull() || right.isNull() ? MArray() : MArray (left.array() || right.array(), left.combineMask(right))); } template MArray operator&& (const MArray& left, const MArray& right) { return (left.isNull() || right.isNull() ? MArray() : MArray (left.array() && right.array(), left.combineMask(right))); } template MArray operator== (const MArray& left, const T& right) { return MArray (left.array() == right, left); } template MArray operator<= (const MArray& left, const T& right) { return MArray (left.array() <= right, left); } template MArray operator< (const MArray& left, const T& right) { return MArray (left.array() < right, left); } template MArray operator>= (const MArray& left, const T& right) { return MArray (left.array() >= right, left); } template MArray operator> (const MArray& left, const T& right) { return MArray (left.array() > right, left); } template MArray operator!= (const MArray& left, const T& right) { return MArray (left.array() != right, left); } template MArray operator|| (const MArray& left, const T& right) { return MArray (left.array() || right, left); } template MArray operator&& (const MArray& left, const T& right) { return MArray (left.array() && right, left); } template MArray operator== (const T& left, const MArray& right) { return MArray (left == right.array(), right); } template MArray operator<= (const T& left, const MArray& right) { return MArray (left <= right.array(), right); } template MArray operator< (const T& left, const MArray& right) { return MArray (left < right.array(), right); } template MArray operator>= (const T& left, const MArray& right) { return MArray (left >= right.array(), right); } template MArray operator> (const T& left, const MArray& right) { return MArray (left > right.array(), right); } template MArray operator!= (const T& left, const MArray& right) { return MArray (left != right.array(), right); } // // The logical OR of 2 MArray objects (normally Bool type) template MArray operator|| (const T& left, const MArray& right) { return MArray (left || right.array(), right); } // The logical AND of 2 MArray objects (normally Bool type). template MArray operator&& (const T& left, const MArray& right) { return MArray (left && right.array(), right); } // The logical NOT of an MArray object (normally Bool type). template MArray operator! (const MArray& a) { return MArray (!a.array(), a); } // Compare with a given relative or absolute tolerance. // template MArray near (const MArray& left, const MArray& right, Double tol) { return (left.isNull() || right.isNull() ? MArray() : MArray (near(left.array(), right.array(), tol), left.combineMask(right))); } template MArray nearAbs (const MArray& left, const MArray& right, Double tol) { return (left.isNull() || right.isNull() ? MArray() : MArray (nearAbs(left.array(), right.array(), tol), left.combineMask(right))); } template MArray near (const MArray& left, const T& right, Double tol) { return MArray (near(left.array(), right, tol), left); } template MArray nearAbs (const MArray& left, const T& right, Double tol) { return MArray (nearAbs(left.array(), right, tol), left); } template MArray near (const T& left, const MArray& right, Double tol) { return MArray (near(left, right.array(), tol), right); } template MArray nearAbs (const T& left, const MArray& right, Double tol) { return MArray (nearAbs(left, right.array(), tol), right); } // // Test which elements are NaN. template MArray isNaN (const MArray& arr) { return MArray (isNaN(arr.array()), arr); } // Test which elements are infinite. template MArray isInf (const MArray& arr) { return MArray (isInf(arr.array()), arr); } // Test which elements have a finite value. template MArray isFinite (const MArray& arr) { return MArray (isFinite(arr.array()), arr); } // Are all unmasked elements equal? // The result is True if there are no unmasked elements. // template Bool allEQ (const MArray& left, const MArray& right) { if (left.isNull() || right.isNull()) { return False; } else if (left.hasMask()) { if (right.hasMask()) { return compareAllMasked (left.array().begin(), left.array().end(), right.array.begin(), left.mask().begin(), right.mask().begin(), std::equal_to()); } else { return compareAllMasked (left.array().begin(), left.array().end(), right.array.begin(), left.mask().begin(), std::equal_to()); } } else if (right.hasMask()) { return compareAllMasked (left.array().begin(), left.array().end(), right.array.begin(), right.mask().begin(), std::equal_to()); } return allEQ (left.array(), right.array()); } template Bool allEQ (const MArray& array, const T& value) { return array.isNull() ? False : array.hasMask() ? compareAllRightMasked (array.array().begin(), array.array().end(), value, array.mask().begin(), std::equal_to()) : allEQ (array.array(), value); } template inline Bool allEQ (const T& value, const MArray& array) { return allEQ (array, value); } // // Is any unmasked element equal? // The result is False if there are no unmasked elements. // template Bool anyEQ (const MArray& left, const MArray& right) { if (left.isNull() || right.isNull()) { return False; } else if (left.hasMask()) { if (right.hasMask()) { return compareAnyMasked (left.array().begin(), left.array().end(), right.array.begin(), left.mask().begin(), right.mask().begin(), std::equal_to()); } else { return compareAnyMasked (left.array().begin(), left.array().end(), right.array.begin(), left.mask().begin(), std::equal_to()); } } else if (right.hasMask()) { return compareAnyMasked (left.array().begin(), left.array().end(), right.array.begin(), right.mask().begin(), std::equal_to()); } return anyEQ (left.array(), right.array()); } template Bool anyEQ (const MArray& array, const T& value) { return array.isNull() ? False : array.hasMask() ? compareAnyRightMasked (array.array().begin(), array.array().end(), value, array.mask().begin(), std::equal_to()) : anyEQ (array.array(), value); } template inline Bool anyEQ (const T& value, const MArray& array) { return anyEQ (array, value); } // // Are all unmasked elements true? inline Bool allTrue (const MArray& array) { return allEQ (array, True); } // Is any unmasked element true? inline Bool anyTrue (const MArray& array) { return anyEQ (array, True); } // Count the number of unmasked elements that are True. template size_t ntrue(const MArray& a) { if (a.hasMask()) { return a.array().contiguousStorage() && a.mask().contiguousStorage() ? countNEMasked(a.array().cbegin(), a.array().cend(), a.mask().cbegin(), T()) : countNEMasked(a.array().begin(), a.array().end(), a.mask().begin(), T()); } return ntrue(a.array()); } // Count the number of unmasked elements that are False. template size_t nfalse(const MArray& a) { if (a.hasMask()) { return a.array().contiguousStorage() && a.mask().contiguousStorage() ? countMasked(a.array().cbegin(), a.array().cend(), a.mask().cbegin(), T()) : countMasked(a.array().begin(), a.array().end(), a.mask().begin(), T()); } return nfalse(a.array()); } // Get partial ntrues. template MArray partialNTrue (const MArray& a, const IPosition& collapseAxes) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(partialNTrue (a.array(), collapseAxes)); } MArray res; partialArrayMath (res, a, collapseAxes, MNTrueFunc()); return res; } // Get partial nfalses. template MArray partialNFalse (const MArray& a, const IPosition& collapseAxes) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(partialNFalse (a.array(), collapseAxes)); } MArray res; partialArrayMath (res, a, collapseAxes, MNFalseFunc()); return res; } // Get partial all. template MArray partialAlls (const MArray& a, const IPosition& collapseAxes) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { Array res; partialArrayMath (res, a.array(), collapseAxes, AllFunc()); return MArray(res); } MArray res; partialArrayMath (res, a, collapseAxes, MAllFunc()); return res; } // Get partial any. template MArray partialAnys (const MArray& a, const IPosition& collapseAxes) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { Array res; partialArrayMath (res, a.array(), collapseAxes, AnyFunc()); return MArray(res); } MArray res; partialArrayMath (res, a, collapseAxes, MAnyFunc()); return res; } // Get sliding ntrues. template MArray slidingNTrue (const MArray& a, const IPosition& halfBoxSize, Bool fillEdge=True) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { Array res; slidingArrayMath (res, a.array(), halfBoxSize, NTrueFunc(), fillEdge); return MArray(res); } MArray res; slidingArrayMath (res, a, halfBoxSize, MNTrueFunc(), fillEdge); return res; } // Get sliding nfalses. template MArray slidingNFalse (const MArray& a, const IPosition& halfBoxSize, Bool fillEdge=True) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { Array res; slidingArrayMath (res, a.array(), halfBoxSize, NFalseFunc(), fillEdge); return MArray(res); } MArray res; slidingArrayMath (res, a, halfBoxSize, MNFalseFunc(), fillEdge); return res; } // Get sliding all. template MArray slidingAlls (const MArray& a, const IPosition& halfBoxSize, Bool fillEdge=True) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { Array res; slidingArrayMath (res, a.array(), halfBoxSize, AllFunc(), fillEdge); return MArray(res); } MArray res; slidingArrayMath (res, a, halfBoxSize, MAllFunc(), fillEdge); return res; } // Get sliding any. template MArray slidingAnys (const MArray& a, const IPosition& halfBoxSize, Bool fillEdge=True) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { Array res; slidingArrayMath (res, a.array(), halfBoxSize, AnyFunc(), fillEdge); return MArray(res); } MArray res; slidingArrayMath (res, a, halfBoxSize, MAnyFunc(), fillEdge); return res; } // Get boxed ntrues. template MArray boxedNTrue (const MArray& a, const IPosition& boxSize) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { Array res; boxedArrayMath (res, a.array(), boxSize, NTrueFunc()); return MArray(res); } MArray res; boxedArrayMath (res, a, boxSize, MNTrueFunc()); return res; } // Get boxed nfalses. template MArray boxedNFalse (const MArray& a, const IPosition& boxSize) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { Array res; boxedArrayMath (res, a.array(), boxSize, NFalseFunc()); return MArray(res); } MArray res; boxedArrayMath (res, a, boxSize, MNFalseFunc()); return res; } // Get boxed all. template MArray boxedAlls (const MArray& a, const IPosition& boxSize) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { Array res; boxedArrayMath (res, a.array(), boxSize, AllFunc()); return MArray(res); } MArray res; boxedArrayMath (res, a, boxSize, MAllFunc()); return res; } // Get boxed any. template MArray boxedAnys (const MArray& a, const IPosition& boxSize) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { Array res; boxedArrayMath (res, a.array(), boxSize, AnyFunc()); return MArray(res); } MArray res; boxedArrayMath (res, a, boxSize, MAnyFunc()); return res; } // } //# end namespace #endif casacore-3.7.1/tables/TaQL/MArrayMath.h000066400000000000000000001360331476623553700175630ustar00rootroot00000000000000//# MArrayMath.h: Mathematical operations on MArray objects //# Copyright (C) 2012 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MARRAYMATH_H #define CASA_MARRAYMATH_H //# Includes #include #include #include #include #include #include #include #include #include namespace casacore { // // Mathematical operations for MArray objects. // // // // // //
      • MArray // // // // These functions perform element by element mathematical operations on // optionally masked arrays and/or scalars. // If two arrays are used, the arrays must conform, except for allEQ which // returns False if the arrays do not conform. // // The functions in this file can be divided in 3 groups: //
          //
        • Full array operations like ==, near, etc. // They are defined for array-array and array-scalar operations. Arrays // shapes have to be conformant. They operate on all elements // (also the masked ones). The result is an MArray with the same // shape as the input array(s). It will have a mask if one of the // operands has a mask. If both operands have a mask, the resulting // mask is the OR of both masks. //
        • Full reduction functions like ntrue, all, allEQ, etc. // They operate on the unmasked elements only. If there are no unmasked // elements, the results is 0 or True. //
        • Reduction functions working on unmasked elements in parts of the // input array. The result is an MArray that has a mask if the input // array has a mask. An output element is masked off if its input // part has no unmasked elements. // The functors defined at the beginning of this file are used to // operate on each part. // There are 3 flavours: //
            //
          • partialXXX reduces one or more axes. E.g. one can count the // number of True elements for particular array axes. // The result is an array with a lower dimensionality. // They can be seen as a special versions of the boxedXXX functions. //
          • slidingXXX operates in a sliding window over the array. So the // result is an array with the same shape as the input, although // the output array is smaller if the edge is not filled. //
          • boxedXXX divides the input array in boxes with the given size // and operates on each box. The result is an array with the same // dimensionality, but with a smaller size. // If the box size does not fit integrally, the edge box is smaller. //
          //
        //
        // // // Define functors to perform a reduction function on an MArray object. // template class MSumFunc : public MArrayFunctorBase { public: virtual ~MSumFunc() {} T operator() (const MArray& arr) const { return sum(arr); } }; template class MSumSqrFunc : public MArrayFunctorBase { public: virtual ~MSumSqrFunc() {} T operator() (const MArray& arr) const { return sumsqr(arr); } }; template class MProductFunc : public MArrayFunctorBase { public: virtual ~MProductFunc() {} T operator() (const MArray& arr) const { return product(arr); } }; template class MMinFunc : public MArrayFunctorBase { public: virtual ~MMinFunc() {} T operator() (const MArray& arr) const { return min(arr); } }; template class MMaxFunc : public MArrayFunctorBase { public: virtual ~MMaxFunc() {} T operator() (const MArray& arr) const { return max(arr); } }; template class MMeanFunc : public MArrayFunctorBase { public: virtual ~MMeanFunc() {} T operator() (const MArray& arr) const { return mean(arr); } }; template class MVarianceFunc : public MArrayFunctorBase { public: explicit MVarianceFunc(uInt ddof=0) : itsDdof(ddof) {} virtual ~MVarianceFunc() {} T operator() (const MArray& arr) const { return variance(arr, itsDdof); } private: uInt itsDdof; }; template class MStddevFunc : public MArrayFunctorBase { public: explicit MStddevFunc(uInt ddof=0) : itsDdof(ddof) {} ~MStddevFunc() {} T operator() (const MArray& arr) const { return stddev(arr, itsDdof); } private: uInt itsDdof; }; template class MAvdevFunc : public MArrayFunctorBase { public: virtual ~MAvdevFunc() {} T operator() (const MArray& arr) const { return avdev(arr); } }; template class MRmsFunc : public MArrayFunctorBase { public: virtual ~MRmsFunc() {} T operator() (const MArray& arr) const { return rms(arr); } }; template class MMedianFunc : public MArrayFunctorBase { public: explicit MMedianFunc (Bool sorted=False, Bool takeEvenMean=True, Bool inPlace = False) : itsSorted(sorted), itsTakeEvenMean(takeEvenMean), itsInPlace(inPlace) {} virtual ~MMedianFunc() {} T operator() (const MArray& arr) const { return median(arr, itsSorted, itsTakeEvenMean, itsInPlace); } private: Bool itsSorted; Bool itsTakeEvenMean; Bool itsInPlace; }; template class MFractileFunc : public MArrayFunctorBase { public: explicit MFractileFunc (Float fraction, Bool sorted = False, Bool inPlace = False) : itsFraction(fraction), itsSorted(sorted), itsInPlace(inPlace) {} virtual ~MFractileFunc() {} T operator() (const MArray& arr) const { return fractile(arr, itsFraction, itsSorted, itsInPlace); } private: float itsFraction; Bool itsSorted; Bool itsInPlace; }; // Do partial reduction of an MArray object. I.e., perform the operation // on a subset of the array axes (the collapse axes). template inline MArray partialArrayMath (const MArray& a, const IPosition& collapseAxes, const MArrayFunctorBase& funcObj) { MArray res; partialArrayMath (res, a, collapseAxes, funcObj); return res; } template void partialArrayMath (MArray& res, const MArray& a, const IPosition& collapseAxes, const MArrayFunctorBase& funcObj) { AlwaysAssert (a.hasMask(), AipsError); // This can also be done as boxedArrayMath with a removeDegenerate thereafter. // // It should be possible to parallelize this loop. // Determine nr of iteration steps and iterate over that as an int. // Do not use Array slicing, because that is not thread-safe. // Instead create ArraySTLIterator directly from Array and blc,trc, // so funcObj should accept iterators instead of Array. // However, ArraySTLIterator needs the sliced array, not original. // Maybe keep ref of itsSteps in iterator instead of array. // Hmm, tricky for median and fractile. // Better to make Array copy ctor thread-safe (thus use boost shared_ptr). ReadOnlyArrayIterator aiter(a.array(), collapseAxes); ReadOnlyArrayIterator miter(a.mask(), collapseAxes); IPosition shape(a.array().shape().removeAxes (collapseAxes)); /* Int64 nr = 1; for (uInt i=0; i(a.array()(pos,endPos), a.mask()(pos,endpos))); } */ ///IPosition shape(a.array().shape().removeAxes (collapseAxes)); res.resize (shape, False); Array resMask(shape); RES* data = res.array().data(); Bool* mask = resMask.data(); while (!aiter.pastEnd()) { if (allTrue(miter.array())) { *mask++ = True; *data++ = RES(); } else { *mask++ = False; *data++ = funcObj(MArray (aiter.array(), miter.array())); } aiter.next(); miter.next(); } res.setMask (resMask); } // template inline MArray boxedArrayMath (const MArray& a, const IPosition& boxShape, const MArrayFunctorBase& funcObj) { MArray res; boxedArrayMath (res, a, boxShape, funcObj); return res; } template void boxedArrayMath (MArray& res, const MArray& array, const IPosition& boxShape, const MArrayFunctorBase& funcObj) { AlwaysAssert (array.hasMask(), AipsError); const IPosition& shape = array.shape(); uInt ndim = shape.size(); IPosition fullBoxShape, resShape; fillBoxedShape (shape, boxShape, fullBoxShape, resShape); res.resize (resShape, False); Array resMask(resShape); RES* data = res.array().data(); Bool* mask = resMask.data(); // Loop through all data and assemble as needed. IPosition blc(ndim, 0); IPosition trc(fullBoxShape-1); while (True) { Array subMask (array.mask()(blc,trc)); if (allTrue(subMask)) { *data++ = RES(); *mask++ = True; } else { *data++ = funcObj (MArray(array.array()(blc,trc), subMask)); *mask++ = False; } uInt ax; for (ax=0; ax= shape[ax]) { trc[ax] = shape[ax]-1; } break; } blc[ax] = 0; trc[ax] = fullBoxShape[ax]-1; } if (ax == ndim) { break; } } res.setMask (resMask); } template inline MArray slidingArrayMath (const MArray& array, const IPosition& halfBoxShape, const MArrayFunctorBase& funcObj, Bool fillEdge=True) { MArray res; slidingArrayMath (res, array, halfBoxShape, funcObj, fillEdge); return res; } template void slidingArrayMath (MArray& res, const MArray& array, const IPosition& halfBoxShape, const MArrayFunctorBase& funcObj, Bool fillEdge=True) { AlwaysAssert (array.hasMask(), AipsError); const IPosition& shape = array.shape(); uInt ndim = shape.size(); IPosition boxEnd, resShape; Bool empty = fillSlidingShape (shape, halfBoxShape, boxEnd, resShape); if (fillEdge) { res.resize (shape, False); res.array() = RES(); Array mask(shape, True); res.setMask (mask); } else { res.resize (resShape, True); } if (!empty) { Array resa (res.array()); Array resm (res.mask()); if (fillEdge) { IPosition boxEnd2 (boxEnd/2); resa.reference (resa(boxEnd2, resShape+boxEnd2-1)); resm.reference (resm(boxEnd2, resShape+boxEnd2-1)); } typename Array::iterator iterarr(resa.begin()); typename Array::iterator itermask(resm.begin()); // Loop through all data and assemble as needed. IPosition blc(ndim, 0); IPosition trc(boxEnd); IPosition pos(ndim, 0); while (True) { Array subMask (array.mask()(blc,trc)); if (allTrue(subMask)) { *iterarr = RES(); *itermask = True; } else { *iterarr = funcObj (MArray(array.array()(blc,trc), subMask)); *itermask = False; } ++iterarr; ++itermask; uInt ax; for (ax=0; ax template MArray operator+ (const MArray& left, const MArray& right) { return (left.isNull() || right.isNull() ? MArray() : MArray (left.array() + right.array(), left.combineMask(right))); } template MArray operator- (const MArray& left, const MArray& right) { return (left.isNull() || right.isNull() ? MArray() : MArray (left.array() - right.array(), left.combineMask(right))); } template MArray operator* (const MArray& left, const MArray& right) { return (left.isNull() || right.isNull() ? MArray() : MArray (left.array() * right.array(), left.combineMask(right))); } template MArray operator/ (const MArray& left, const MArray& right) { return (left.isNull() || right.isNull() ? MArray() : MArray (left.array() / right.array(), left.combineMask(right))); } template MArray operator% (const MArray& left, const MArray& right) { return (left.isNull() || right.isNull() ? MArray() : MArray (left.array() % right.array(), left.combineMask(right))); } template MArray operator& (const MArray& left, const MArray& right) { return (left.isNull() || right.isNull() ? MArray() : MArray (left.array() & right.array(), left.combineMask(right))); } template MArray operator| (const MArray& left, const MArray& right) { return (left.isNull() || right.isNull() ? MArray() : MArray (left.array() | right.array(), left.combineMask(right))); } template MArray operator^ (const MArray& left, const MArray& right) { return (left.isNull() || right.isNull() ? MArray() : MArray (left.array() ^ right.array(), left.combineMask(right))); } template MArray operator+ (const MArray& left, const T& right) { return MArray (left.array() + right, left); } template MArray operator- (const MArray& left, const T& right) { return MArray (left.array() - right, left); } template MArray operator* (const MArray& left, const T& right) { return MArray (left.array() * right, left); } template MArray operator/ (const MArray& left, const T& right) { return MArray (left.array() / right, left); } template MArray operator% (const MArray& left, const T& right) { return MArray (left.array() % right, left); } template MArray operator& (const MArray& left, const T& right) { return MArray (left.array() & right, left); } template MArray operator| (const MArray& left, const T& right) { return MArray (left.array() | right, left); } template MArray operator^ (const MArray& left, const T& right) { return MArray (left.array() ^ right, left); } template MArray operator+ (const T& left, const MArray& right) { return MArray (left + right.array(), right); } template MArray operator- (const T& left, const MArray& right) { return MArray (left - right.array(), right); } template MArray operator* (const T& left, const MArray& right) { return MArray (left * right.array(), right); } template MArray operator/ (const T& left, const MArray& right) { return MArray (left / right.array(), right); } template MArray operator% (const T& left, const MArray& right) { return MArray (left % right.array(), right); } template MArray operator& (const T& left, const MArray& right) { return MArray (left & right.array(), right); } template MArray operator| (const T& left, const MArray& right) { return MArray (left | right.array(), right); } template MArray operator^ (const T& left, const MArray& right) { return MArray (left ^ right.array(), right); } // // Negate the elements in an array. template MArray operator- (const MArray& a) { return MArray (-a.array(), a); } // Take the complement of the elements in an array. template MArray operator~ (const MArray& a) { return MArray (~a.array(), a); } // Perform mathematical function on each element in an array. // template MArray sin(const MArray& a) { return MArray (sin(a.array()), a); } template MArray cos(const MArray& a) { return MArray (cos(a.array()), a); } template MArray tan(const MArray& a) { return MArray (tan(a.array()), a); } template MArray sinh(const MArray& a) { return MArray (sinh(a.array()), a); } template MArray cosh(const MArray& a) { return MArray (cosh(a.array()), a); } template MArray tanh(const MArray& a) { return MArray (tanh(a.array()), a); } template MArray asin(const MArray& a) { return MArray (asin(a.array()), a); } template MArray acos(const MArray& a) { return MArray (acos(a.array()), a); } template MArray atan(const MArray& a) { return MArray (atan(a.array()), a); } template MArray atan2(const MArray& left, const MArray& right) { return (left.isNull() || right.isNull() ? MArray() : MArray (atan2(left.array(), right.array()), left.combineMask(right))); } template MArray atan2(const MArray& left, const T& right) { return MArray (atan2(left.array(), right), left); } template MArray atan2(const T& left, const MArray& right) { return MArray (atan2(left, right.array()), right); } template MArray exp(const MArray& a) { return MArray (exp(a.array()), a); } template MArray log(const MArray& a) { return MArray (log(a.array()), a); } template MArray log10(const MArray& a) { return MArray (log10(a.array()), a); } template MArray sqrt(const MArray& a) { return MArray (sqrt(a.array()), a); } template MArray square(const MArray& a) { return MArray (square(a.array()), a); } template MArray cube(const MArray& a) { return MArray (cube(a.array()), a); } template MArray pow(const MArray& a, const MArray& exp) { return (a.isNull() || exp.isNull() ? MArray() : MArray (pow(a.array(), exp.array()), a.combineMask(exp))); } template MArray pow(const T& a, const MArray& exp) { return MArray (pow(a, exp.array()), exp); } template MArray pow(const MArray& a, const T& exp) { return MArray (pow(a.array(), exp), a); } template MArray> pow(const MArray>& a, const T& exp) { return MArray> (pow(a.array(), exp), a); } template MArray min(const MArray& left, const MArray& right) { return (left.isNull() || right.isNull() ? MArray() : MArray (min(left.array(), right.array()), left.combineMask(right))); } template MArray min(const MArray& left, const T& right) { return MArray (min(left.array(), right), left); } template MArray min(const T& left, const MArray& right) { return MArray (min(left, right.array()), right); } template MArray max(const MArray& left, const MArray& right) { return (left.isNull() || right.isNull() ? MArray() : MArray (max(left.array(), right.array()), left.combineMask(right))); } template MArray max(const MArray& left, const T& right) { return MArray (max(left.array(), right), left); } template MArray max(const T& left, const MArray& right) { return MArray (max(left, right.array()), right); } template MArray ceil(const MArray& a) { return MArray (ceil(a.array()), a); } template MArray floor(const MArray& a) { return MArray (floor(a.array()), a); } template MArray round(const MArray& a) { return MArray (round(a.array()), a); } template MArray sign(const MArray& a) { return MArray (sign(a.array()), a); } template MArray abs(const MArray& a) { return MArray (abs(a.array()), a); } template MArray fabs(const MArray& a) { return MArray (fabs(a.array()), a); } template MArray fmod(const MArray& left, const MArray& right) { return (left.isNull() || right.isNull() ? MArray() : MArray (fmod(left.array(), right.array()), left.combineMask(right))); } template MArray fmod(const MArray& left, const T& right) { return MArray (fmod(left.array(), right), left); } template MArray fmod(const T& left, const MArray& right) { return MArray (fmod(left, right.array()), right); } template MArray floormod(const MArray& left, const MArray& right) { return (left.isNull() || right.isNull() ? MArray() : MArray (floormod(left.array(), right.array()), left.combineMask(right))); } template MArray floormod(const MArray& left, const T& right) { return MArray (floormod(left.array(), right), left); } template MArray floormod(const T& left, const MArray& right) { return MArray (floormod(left, right.array()), right); } template MArray conj(const MArray& arr) { return MArray (conj(arr.array()), arr); } inline MArray real(const MArray &arr) { return MArray (real(arr.array()), arr); } inline MArray imag(const MArray &arr) { return MArray (imag(arr.array()), arr); } inline MArray amplitude(const MArray &arr) { return MArray (amplitude(arr.array()), arr); } inline MArray phase(const MArray &arr) { return MArray (phase(arr.array()), arr); } inline MArray real(const MArray &arr) { return MArray (real(arr.array()), arr); } inline MArray imag(const MArray &arr) { return MArray (imag(arr.array()), arr); } inline MArray amplitude(const MArray &arr) { return MArray (amplitude(arr.array()), arr); } inline MArray phase(const MArray &arr) { return MArray (phase(arr.array()), arr); } // // Reduce an array to a scalar using the unmasked elements only. // The result is 0 if there are no unmasked elements. // template T sum(const MArray& a) { if (a.hasMask()) { return a.array().contiguousStorage() && a.mask().contiguousStorage() ? accumulateMasked(a.array().cbegin(), a.array().cend(), a.mask().cbegin(), T(), std::plus()) : accumulateMasked(a.array().begin(), a.array().end(), a.mask().begin(), T(), std::plus()); } return sum(a.array()); } template T sumsqr(const MArray& a) { if (a.hasMask()) { return a.array().contiguousStorage() && a.mask().contiguousStorage() ? accumulateMasked(a.array().cbegin(), a.array().cend(), a.mask().cbegin(), T(), SumSqr()) : accumulateMasked(a.array().begin(), a.array().end(), a.mask().begin(), T(), SumSqr()); } return sumsqr(a.array()); } template T product(const MArray& a) { if (a.hasMask()) { return a.array().contiguousStorage() && a.mask().contiguousStorage() ? accumulateMasked(a.array().cbegin(), a.array().cend(), a.mask().cbegin(), std::multiplies()) : accumulateMasked(a.array().begin(), a.array().end(), a.mask().begin(), std::multiplies()); } return product(a.array()); } template T min(const MArray& a) { if (a.hasMask()) { return a.array().contiguousStorage() && a.mask().contiguousStorage() ? accumulateMasked(a.array().cbegin(), a.array().cend(), a.mask().cbegin(), Min()) : accumulateMasked(a.array().begin(), a.array().end(), a.mask().begin(), Min()); } return min(a.array()); } template T max(const MArray& a) { if (a.hasMask()) { return a.array().contiguousStorage() && a.mask().contiguousStorage() ? accumulateMasked(a.array().cbegin(), a.array().cend(), a.mask().cbegin(), Max()) : accumulateMasked(a.array().begin(), a.array().end(), a.mask().begin(), Max()); } return max(a.array()); } template T mean(const MArray& a) { Int64 nv = a.nvalid(); if (nv == 0) return T(); if (! a.hasMask()) return mean(a.array()); return T(sum(a) / (1.0*nv)); } template T variance(const MArray& a, T mean, uInt ddof) { Int64 nv = a.nvalid(); if (nv < ddof+1) return T(); if (! a.hasMask()) return pvariance(a.array(), mean, ddof); T sum = a.array().contiguousStorage() && a.mask().contiguousStorage() ? accumulateMasked(a.array().cbegin(), a.array().cend(), a.mask().cbegin(), T(), SumSqrDiff(mean)) : accumulateMasked(a.array().begin(), a.array().end(), a.mask().begin(), T(), SumSqrDiff(mean)); return T(sum / (1.0*nv - ddof)); } template T variance(const MArray& a, uInt ddof) { return variance(a, mean(a), ddof); } template T stddev(const MArray& a, uInt ddof) { return sqrt(variance(a, ddof)); } template T stddev(const MArray& a, T mean, uInt ddof) { return sqrt(variance(a, mean, ddof)); } template T avdev(const MArray& a, T mean) { Int64 nv = a.nvalid(); if (nv == 0) return T(); if (! a.hasMask()) return avdev(a.array(), mean); T sum = a.array().contiguousStorage() && a.mask().contiguousStorage() ? accumulateMasked(a.array().cbegin(), a.array().cend(), a.mask().cbegin(), T(), SumAbsDiff(mean)) : accumulateMasked(a.array().begin(), a.array().end(), a.mask().begin(), T(), SumAbsDiff(mean)); return T(sum / (1.0*nv)); } template T avdev(const MArray& a) { return avdev(a, mean(a)); } template T rms(const MArray& a) { Int64 nv = a.nvalid(); if (nv == 0) return T(); if (! a.hasMask()) return rms(a.array()); T sum = a.array().contiguousStorage() && a.mask().contiguousStorage() ? accumulateMasked(a.array().cbegin(), a.array().cend(), a.mask().cbegin(), T(), SumSqr()) : accumulateMasked(a.array().begin(), a.array().end(), a.mask().begin(), T(), SumSqr()); return T(sqrt(sum / (1.0*nv))); } template T median(const MArray &a, Bool sorted, Bool takeEvenMean, Bool inPlace=False) { // The normal median function needs at least one element, so shortcut. if (a.empty()) return T(); if (! a.hasMask()) return median(a.array(), sorted, takeEvenMean, inPlace); Block buf(a.size()); Int64 nv = a.flatten (buf.storage(), buf.size()); if (nv == 0) return T(); Array arr(IPosition(1, nv), buf.storage(), SHARE); // Median can be taken in place. return median (arr, sorted, takeEvenMean, True); } template inline T median(const MArray &a) { return median (a, False, (a.size() <= 100), False); } template inline T median(const MArray &a, Bool sorted) { return median (a, sorted, (a.nelements() <= 100), False); } template inline T medianInPlace(const MArray &a, Bool sorted = False) { return median (a, sorted, (a.nelements() <= 100), True); } // Return the fractile of an array. // It returns the value at the given fraction of the array. // A fraction of 0.5 is the same as the median, be it that no mean of // the two middle elements is taken if the array has an even nr of elements. // It uses kthLargest if the array is not sorted yet. template T fractile(const MArray &a, Float fraction, Bool sorted=False, Bool inPlace=False) { // The normal fractile function needs at least one element, so shortcut. if (a.empty()) return T(); if (! a.hasMask()) return fractile(a.array(), fraction, sorted, inPlace); Block buf(a.size()); Int64 nv = a.flatten (buf.storage(), a.size()); if (nv == 0) return T(); Array arr(IPosition(1, nv), buf.storage(), SHARE); return fractile (arr, fraction, sorted, True); } // // Get partial sums, etc. // template MArray partialSums (const MArray& a, const IPosition& collapseAxes) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(partialSums (a.array(), collapseAxes)); } return partialArrayMath (a, collapseAxes, MSumFunc()); } template MArray partialSumSqrs (const MArray& a, const IPosition& collapseAxes) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(partialArrayMath (a.array(), collapseAxes, SumSqrFunc())); } return partialArrayMath (a, collapseAxes, MSumSqrFunc()); } template MArray partialProducts (const MArray& a, const IPosition& collapseAxes) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(partialProducts (a.array(), collapseAxes)); } return partialArrayMath (a, collapseAxes, MProductFunc()); } template MArray partialMins (const MArray& a, const IPosition& collapseAxes) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(partialMins (a.array(), collapseAxes)); } return partialArrayMath (a, collapseAxes, MMinFunc()); } template MArray partialMaxs (const MArray& a, const IPosition& collapseAxes) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(partialMaxs (a.array(), collapseAxes)); } return partialArrayMath (a, collapseAxes, MMaxFunc()); } template MArray partialMeans (const MArray& a, const IPosition& collapseAxes) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(partialMeans (a.array(), collapseAxes)); } return partialArrayMath (a, collapseAxes, MMeanFunc()); } template MArray partialVariances (const MArray& a, const IPosition& collapseAxes, uInt ddof) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(partialVariances (a.array(), collapseAxes, ddof)); } return partialArrayMath (a, collapseAxes, MVarianceFunc(ddof)); } template MArray partialStddevs (const MArray& a, const IPosition& collapseAxes, uInt ddof) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(partialStddevs (a.array(), collapseAxes, ddof)); } return partialArrayMath (a, collapseAxes, MStddevFunc(ddof)); } template MArray partialAvdevs (const MArray& a, const IPosition& collapseAxes) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(partialAvdevs (a.array(), collapseAxes)); } return partialArrayMath (a, collapseAxes, MAvdevFunc()); } template MArray partialRmss (const MArray& a, const IPosition& collapseAxes) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(partialRmss (a.array(), collapseAxes)); } return partialArrayMath (a, collapseAxes, MRmsFunc()); } template MArray partialMedians (const MArray& a, const IPosition& collapseAxes, Bool takeEvenMean=False, Bool inPlace=False) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(partialMedians (a.array(), collapseAxes, takeEvenMean, inPlace)); } return partialArrayMath (a, collapseAxes, MMedianFunc(False, takeEvenMean, inPlace)); } template MArray partialFractiles (const MArray& a, const IPosition& collapseAxes, Float fraction, Bool inPlace=False) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(partialFractiles (a.array(), collapseAxes, fraction, inPlace)); } return partialArrayMath (a, collapseAxes, MFractileFunc(fraction, False, inPlace)); } // // Get sliding sums. // template MArray slidingSums (const MArray& a, const IPosition& halfBoxSize, Bool fillEdge=True) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(slidingArrayMath (a.array(), halfBoxSize, SumFunc(), fillEdge)); } return slidingArrayMath (a, halfBoxSize, MSumFunc(), fillEdge); } template MArray slidingSumSqrs (const MArray& a, const IPosition& halfBoxSize, Bool fillEdge=True) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(slidingArrayMath (a.array(), halfBoxSize, SumSqrFunc(), fillEdge)); } return slidingArrayMath (a, halfBoxSize, MSumSqrFunc(), fillEdge); } template MArray slidingProducts (const MArray& a, const IPosition& halfBoxSize, Bool fillEdge=True) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(slidingArrayMath (a.array(), halfBoxSize, ProductFunc(), fillEdge)); } return slidingArrayMath (a, halfBoxSize, MProductFunc(), fillEdge); } template MArray slidingMins (const MArray& a, const IPosition& halfBoxSize, Bool fillEdge=True) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(slidingArrayMath (a.array(), halfBoxSize, MinFunc(), fillEdge)); } return slidingArrayMath (a, halfBoxSize, MMinFunc(), fillEdge); } template MArray slidingMaxs (const MArray& a, const IPosition& halfBoxSize, Bool fillEdge=True) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(slidingArrayMath (a.array(), halfBoxSize, MaxFunc(), fillEdge)); } return slidingArrayMath (a, halfBoxSize, MMaxFunc(), fillEdge); } template MArray slidingMeans (const MArray& a, const IPosition& halfBoxSize, Bool fillEdge=True) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(slidingArrayMath (a.array(), halfBoxSize, MeanFunc(), fillEdge)); } return slidingArrayMath (a, halfBoxSize, MMeanFunc(), fillEdge); } template MArray slidingVariances (const MArray& a, const IPosition& halfBoxSize, uInt ddof, Bool fillEdge=True) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(slidingArrayMath (a.array(), halfBoxSize, VarianceFunc(ddof), fillEdge)); } return slidingArrayMath (a, halfBoxSize, MVarianceFunc(ddof), fillEdge); } template MArray slidingStddevs (const MArray& a, const IPosition& halfBoxSize, uInt ddof, Bool fillEdge=True) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(slidingArrayMath (a.array(), halfBoxSize, StddevFunc(ddof), fillEdge)); } return slidingArrayMath (a, halfBoxSize, MStddevFunc(ddof), fillEdge); } template MArray slidingAvdevs (const MArray& a, const IPosition& halfBoxSize, Bool fillEdge=True) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(slidingArrayMath (a.array(), halfBoxSize, AvdevFunc(), fillEdge)); } return slidingArrayMath (a, halfBoxSize, MAvdevFunc(), fillEdge); } template MArray slidingRmss (const MArray& a, const IPosition& halfBoxSize, Bool fillEdge=True) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(slidingArrayMath (a.array(), halfBoxSize, RmsFunc(), fillEdge)); } return slidingArrayMath (a, halfBoxSize, MRmsFunc(), fillEdge); } template MArray slidingMedians (const MArray& a, const IPosition& halfBoxSize, Bool takeEvenMean=False, Bool inPlace=False, Bool fillEdge=True) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(slidingArrayMath (a.array(), halfBoxSize, MedianFunc(False, takeEvenMean, inPlace), fillEdge)); } return slidingArrayMath (a, halfBoxSize, MMedianFunc(False, takeEvenMean, inPlace), fillEdge); } template MArray slidingFractiles (const MArray& a, const IPosition& halfBoxSize, Float fraction, Bool inPlace=False, Bool fillEdge=True) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(slidingArrayMath (a.array(), halfBoxSize, FractileFunc(fraction, False, inPlace), fillEdge)); } return slidingArrayMath (a, halfBoxSize, MFractileFunc(fraction, False, inPlace), fillEdge); } // // Get boxed sums. // template MArray boxedSums (const MArray& a, const IPosition& boxSize) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(boxedArrayMath (a.array(), boxSize, SumFunc())); } return boxedArrayMath (a, boxSize, MSumFunc()); } template MArray boxedSumSqrs (const MArray& a, const IPosition& boxSize) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(boxedArrayMath (a.array(), boxSize, SumSqrFunc())); } return boxedArrayMath (a, boxSize, MSumSqrFunc()); } template MArray boxedProducts (const MArray& a, const IPosition& boxSize) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(boxedArrayMath (a.array(), boxSize, ProductFunc())); } return boxedArrayMath (a, boxSize, MProductFunc()); } template MArray boxedMins (const MArray& a, const IPosition& boxSize) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(boxedArrayMath (a.array(), boxSize, MinFunc())); } return boxedArrayMath (a, boxSize, MMinFunc()); } template MArray boxedMaxs (const MArray& a, const IPosition& boxSize) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(boxedArrayMath (a.array(), boxSize, MaxFunc())); } return boxedArrayMath (a, boxSize, MMaxFunc()); } template MArray boxedMeans (const MArray& a, const IPosition& boxSize) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(boxedArrayMath (a.array(), boxSize, MeanFunc())); } return boxedArrayMath (a, boxSize, MMeanFunc()); } template MArray boxedVariances (const MArray& a, const IPosition& boxSize, uInt ddof) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(boxedArrayMath (a.array(), boxSize, VarianceFunc(ddof))); } return boxedArrayMath (a, boxSize, MVarianceFunc(ddof)); } template MArray boxedStddevs (const MArray& a, const IPosition& boxSize, uInt ddof) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(boxedArrayMath (a.array(), boxSize, StddevFunc(ddof))); } return boxedArrayMath (a, boxSize, MStddevFunc(ddof)); } template MArray boxedAvdevs (const MArray& a, const IPosition& boxSize) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(boxedArrayMath (a.array(), boxSize, AvdevFunc())); } return boxedArrayMath (a, boxSize, MAvdevFunc()); } template MArray boxedRmss (const MArray& a, const IPosition& boxSize) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(boxedArrayMath (a.array(), boxSize, RmsFunc())); } return boxedArrayMath (a, boxSize, MRmsFunc()); } template MArray boxedMedians (const MArray& a, const IPosition& boxSize, Bool takeEvenMean=False, Bool inPlace=False) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(boxedArrayMath (a.array(), boxSize, MedianFunc(False, takeEvenMean, inPlace))); } return boxedArrayMath (a, boxSize, MMedianFunc(False, takeEvenMean, inPlace)); } template MArray boxedFractiles (const MArray& a, const IPosition& boxSize, Float fraction, Bool inPlace=False) { if (a.isNull()) { return MArray(); } else if (! a.hasMask()) { return MArray(boxedArrayMath (a.array(), boxSize, FractileFunc(fraction, False, inPlace))); } return boxedArrayMath (a, boxSize, MFractileFunc(fraction, False, inPlace)); } // // } //# end namespace #endif casacore-3.7.1/tables/TaQL/MArrayMathBase.h000066400000000000000000000233161476623553700203550ustar00rootroot00000000000000//# MArrayMathBase.h: Basic functions and classes for math on MArray objects //# Copyright (C) 2012 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MARRAYMATHBASE_H #define CASA_MARRAYMATHBASE_H #include #include namespace casacore { //# Forward declarations. template class MArray; // // Basic functions and classes for math on MArray objects // // // // // //
      • MArray // // // // This header file defines several STL-like functions to work on // iterators with a mask. // // Furthermore, abstract base classes are defined for functors to be used // in functions like slidingXXX. // Virtual functions instead of templated functions are used to avoid // code bloat when used in functions like partialArrayMath. Because a // reduction operation usually takes much more time than the call, using // virtual functions hardly imposes a performance penalty. // // // // Define STL-like accumulate function operating on arrays with masks. // A mask value True means masked-off, thus is not taken into account. // //
        The first function initializes the accumulator to the first // unmasked value. This is useful if it is not possible to initialize // it externally (e.g. for a function like min). template T accumulateMasked (ARRAYITER abegin, ARRAYITER aend, MASKITER mbegin, OPER oper) { T accum = T(); for (; abegin!=aend; ++abegin, ++mbegin) { if (!*mbegin) { accum = *abegin; ++abegin; ++mbegin; break; } } for (; abegin!=aend; ++abegin, ++mbegin) { if (!*mbegin) accum = oper(accum, *abegin); } return accum; } // The second function uses an externally initialized accumulator // (e.g. needed for sum). template T accumulateMasked (ARRAYITER abegin, ARRAYITER aend, MASKITER mbegin, T accum, OPER oper) { for (; abegin!=aend; ++abegin, ++mbegin) { if (!*mbegin) accum = oper(accum, *abegin); } return accum; } //
        // Count the number of unmasked values matching the given value. // It is similar to std::count, but a mask is applied. template size_t countMasked (ARRAYITER abegin, ARRAYITER aend, MASKITER mbegin, const T& value) { size_t n = 0; for (; abegin!=aend; ++abegin, ++mbegin) { if (!*mbegin && *abegin == value) ++n; } return n; } // Count the number of unmasked values not matching the given value. // It is similar to std::count, but a mask is applied. template size_t countNEMasked (ARRAYITER abegin, ARRAYITER aend, MASKITER mbegin, const T& value) { size_t n = 0; for (; abegin!=aend; ++abegin, ++mbegin) { if (!*mbegin && *abegin != value) ++n; } return n; } // Define a function to compare the unmasked elements of two sequences. // It returns true if all unmasked elements compare true or if there are // no unmasked elements. // An example compare operator is std::equal_to. // template inline bool compareAllMasked (InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, MaskIterator mask1, MaskIterator mask2, CompareOperator op) { for (; first1!=last1; ++first1, ++first2, ++mask1, ++mask2) { if (!*mask1 && !*mask2) { if (!op(*first1, *first2)) return False; } } return true; } template inline bool compareAllMasked (InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, MaskIterator mask1, CompareOperator op) { for (; first1!=last1; ++first1, ++first2, ++mask1) { if (!*mask1) { if (!op(*first1, *first2)) return False; } } return true; } // For use with a constant left value. // This avoids use of bind1st or bind2nd which can fail for gcc-4.3. // (see ArrayMath.h). template inline bool compareAllLeftMasked (InputIterator1 first1, InputIterator1 last1, T left, MaskIterator mask1, CompareOperator op) { for (; first1!=last1; ++first1, ++mask1) { if (!*mask1) { if (!op(left, *first1)) return False; } } return true; } // For use with a constant right value. // This avoids use of bind1st or bind2nd which can fail for gcc-4.3. // (see ArrayMath.h). template inline bool compareAllRightMasked(InputIterator1 first1, InputIterator1 last1, T right, MaskIterator mask1, CompareOperator op) { for (; first1!=last1; ++first1, ++mask1) { if (!*mask1) { if (!op(*first1, right)) return False; } } return true; } // // Define a function to compare the unmasked elements of two sequences. // It returns true if any element compares true. // If there are no unmasked elements, it returns False. // An example compare operator is std::equal_to. // template inline bool compareAnyMasked (InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, MaskIterator mask1, MaskIterator mask2, CompareOperator op) { for (; first1!=last1; ++first1, ++first2, ++mask1, ++mask2) { if (!*mask1 && !*mask2) { if (op(*first1, *first2)) return true; } } return False; } template inline bool compareAnyMasked (InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, MaskIterator mask1, CompareOperator op) { for (; first1!=last1; ++first1, ++first2, ++mask1) { if (!*mask1) { if (op(*first1, *first2)) return true; } } return False; } // For use with a constant left value. // This avoids use of bind1st or bind2nd which can fail for gcc-4.3. // (see ArrayMath.h). template inline bool compareAnyLeftMasked (InputIterator1 first1, InputIterator1 last1, T left, MaskIterator mask1, CompareOperator op) { for (; first1!=last1; ++first1, ++mask1) { if (!*mask1) { if (op(left, *first1)) return true; } } return False; } // For use with a constant right value. // This avoids use of bind1st or bind2nd which can fail for gcc-4.3. // (see ArrayMath.h). template inline bool compareAnyRightMasked(InputIterator1 first1, InputIterator1 last1, T right, MaskIterator mask1, CompareOperator op) { for (; first1!=last1; ++first1, ++mask1) { if (!*mask1) { if (op(*first1, right)) return true; } } return False; } // // Define the base class for functors to perform a reduction function on an // MArray object. The functors themselves are defined elsewhere. template class MArrayFunctorBase { public: virtual ~MArrayFunctorBase() {} virtual RES operator() (const MArray&) const = 0; }; //
        } //# end namespace #endif casacore-3.7.1/tables/TaQL/MArrayUtil.h000066400000000000000000000113671476623553700176110ustar00rootroot00000000000000//# MArrayUtil.h: Utility functions for MArrays //# Copyright (C) 2012 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef CASA_MARRAYUTIL_H #define CASA_MARRAYUTIL_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Reorder the axes of the data in an MArray object // // // // // This function makes it possible to reorder the axes of an MArray. // Both the data and the optional mask are reordered. // The resulting array is a copy of the input array with its data // moved around according to the new array order. // If the order does not change, a copy is returned if the // alwaysCopy is true. Otherwise a reference of the // input array is returned. //

        // The newAxisOrder defines the new axes order. // Its length can be less than the dimensionality of the input array. // It is appended with the non-specified axes in their natural order. // newAxisOrder(i) gives the axis in the original array // which will now get axis i. // // // // MArray result = reorderArray (someArray, IPosition(2,1,3)); // // Say that someArray is a 4D array with shape [3,4,5,6]. // The non-specified axes get appended to the axis order // specification [1,3] resulting in [1,3,0,2]. //
        This means that axis 1 gets axis 0, axis 3 gets axis 1, axis 0 gets // axis 2, and axis 2 gets axis 3. // Thus the resulting shape is [4,6,3,5] and the data are moved accordingly. //
        // template MArray reorderArray (const MArray& array, const IPosition& newAxisOrder, Bool alwaysCopy = True) { return (array.isNull() ? MArray() : (array.hasMask() ? MArray (reorderArray(array.array(), newAxisOrder, alwaysCopy), reorderArray(array.mask(), newAxisOrder, alwaysCopy)) : MArray (reorderArray(array.array(), newAxisOrder, alwaysCopy)))); } // //

        // Reverse the order of one or more axes of an MArray. // // // // // This function makes it possible to reverse one or more axes of an MArray by // swapping around the elements of each axis. // Both the data and the optional mask are reversed. // The resulting array is a copy of the input array with its data // moved around according to the new order. // If the order does not change, a copy is returned if the // alwaysCopy is true. Otherwise a reference of the // input array is returned. // // // Reversing axis 0 of a Vector means that the Vector is reversed. // Reversing axis 1 of a Matrix means that its rows are reversed. // Reversing axis 0 of an N-dim array means that the elements of each Vector // in that array are reversed. // // template MArray reverseArray (const MArray& array, const IPosition& reversedAxes, Bool alwaysCopy = True) { return (array.isNull() ? MArray() : (array.hasMask() ? MArray (reverseArray(array.array(), reversedAxes, alwaysCopy), reverseArray(array.mask(), reversedAxes, alwaysCopy)) : MArray (reverseArray(array.array(), reversedAxes, alwaysCopy)))); } // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/RecordExpr.cc000066400000000000000000000073071476623553700177720ustar00rootroot00000000000000//# RecordExpr.cc: Global functions to make a expression node for a record field //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableExprNode makeRecordExpr (const RecordDesc& desc, Int fieldNumber) { if (fieldNumber < 0 || fieldNumber >= Int(desc.nfields())) { throw TableInvExpr ("makeRecordExpr: invalid field number given"); } Block fieldNrs (1, fieldNumber); if (desc.isArray (fieldNumber)) { return new TableExprNodeRecordFieldArray (desc.type(fieldNumber), fieldNrs); } return new TableExprNodeRecordField (desc.type(fieldNumber), fieldNrs); } TableExprNode makeRecordExpr (const RecordDesc& desc, const String& fieldName) { Int fld = desc.fieldNumber (fieldName); if (fld < 0) { throw TableInvExpr ("makeRecordExpr: field name " + fieldName + " is unknown"); } return makeRecordExpr (desc, fld); } TableExprNode makeRecordExpr (const RecordInterface& record, const String& fieldName) { Vector names (stringToVector (fieldName, '.')); if (names.nelements() == 0) { throw TableInvExpr ("makeRecordExpr: empty field name given"); } Block fieldNrs (names.nelements()); String name; Int fld=0; const RecordInterface* recPtr = &record; RecordDesc desc(record.description()); for (uInt i=0; iasRecord(fld)); desc = recPtr->description(); } } fieldNrs[i] = fld; } if (desc.isArray (fld)) { return new TableExprNodeRecordFieldArray (desc.type(fld), fieldNrs); } return new TableExprNodeRecordField (desc.type(fld), fieldNrs); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/RecordExpr.h000066400000000000000000000062101476623553700176240ustar00rootroot00000000000000//# RecordExpr.h: Global functions to make a expression node for a record field //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_RECORDEXPR_H #define TABLES_RECORDEXPR_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Global functions to make a expression node for a record field. // // // // // // //# Classes you should understand before using this one. //
      • TableExprNode // // // This file contains a few global functions to construct an expression // node for a field in a record. // // // Make a record expression node for the given field in the record description. // TableExprNode makeRecordExpr (const RecordDesc& desc, Int fieldNumber); TableExprNode makeRecordExpr (const RecordDesc& desc, const String& fieldName); // // Make a record expression node for the given field in the record. inline TableExprNode makeRecordExpr (const RecordInterface& record, Int fieldNumber) { return makeRecordExpr (record.description(), fieldNumber); } // Make a record expression node for the given field in the record. // The field can be a field in the record itself, but it can also be // a field in a subrecord (or subsubrecord, etc.). If it is not a field // in the record itself, the name must define the 'path' to the field // in the subrecord by preceeding the field name with the name(s) of the // subrecord(s) separated by dots. E.g. sub1.sub2.fld TableExprNode makeRecordExpr (const RecordInterface& record, const String& fieldName); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/RecordGram.cc000066400000000000000000000352751476623553700177470ustar00rootroot00000000000000//# RecordGram.cc: Grammar for record command lines //# Copyright (C) 2000,2001,2003,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // RecordGram; grammar for record command lines // This file includes the output files of bison and flex for // parsing command lines operating on records. #include #include #include #include #include #include #include #include #include #include #include //# stdlib.h is needed for bison 1.28 and needs to be included here //# (before the flex/bison files). //# Bison defines WHERE, which is also defined in LogOrigin.h (which //# is included in auto-template mode). //# So undefine WHERE first. #undef WHERE #include //# Let clang and gcc ignore some warnings in bison/flex code. //# Undef YY_NULL because flex can redefine it slightly differently (giving a warning). #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpragmas" #pragma GCC diagnostic ignored "-Wdeprecated-register" #pragma GCC diagnostic ignored "-Wsign-compare" #include "RecordGram.ycc" // bison output #ifdef YY_NULL # undef YY_NULL #endif #include "RecordGram.lcc" // flex output #pragma GCC diagnostic pop // Define the yywrap function for flex. int RecordGramwrap() { return 1; } namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Declare a file global pointer to a char* for the input string. static const char* strpRecordGram = 0; static Int posRecordGram = 0; //# Static pointer to the record when parsing the fields. //# Static pointer to the node holding the final expression tree. const RecordInterface* RecordGram::theirRecPtr = 0; TableExprNode* RecordGram::theirNodePtr = 0; const Table* RecordGram::theirTabPtr = 0; TaQLStyle RecordGram::theirTaQLStyle; std::mutex RecordGram::theirMutex; //# The list of nodes to delete (usually in case of exception). std::map RecordGram::theirTokens; void RecordGram::addToken (TableExprNode* ptr) { addToken (ptr, RecordGram::Node); } void RecordGram::addToken (RecordGramVal* ptr) { addToken (ptr, RecordGram::Val); } void RecordGram::addToken (TableExprNodeSet* ptr) { addToken (ptr, RecordGram::Set); } void RecordGram::addToken (TableExprNodeSetElem* ptr) { addToken (ptr, RecordGram::Elem); } void RecordGram::deleteToken (TableExprNode* ptr) { removeToken (ptr); delete ptr; } void RecordGram::deleteToken (RecordGramVal* ptr) { removeToken (ptr); delete ptr; } void RecordGram::deleteToken (TableExprNodeSet* ptr) { removeToken (ptr); delete ptr; } void RecordGram::deleteToken (TableExprNodeSetElem* ptr) { removeToken (ptr); delete ptr; } void RecordGram::deleteTokenStorage() { for (std::map::const_iterator iter=theirTokens.begin(); iter!=theirTokens.end(); ++iter) { switch (iter->second) { case RecordGram::Node: delete static_cast(iter->first); break; case RecordGram::Val: delete static_cast(iter->first); break; case RecordGram::Elem: delete static_cast(iter->first); break; case RecordGram::Set: delete static_cast(iter->first); break; } } theirTokens.clear(); } //# Parse the command. //# Do a yyrestart(yyin) first to make the flex scanner reentrant. int recordGramParseCommand (const String& command) { RecordGramrestart (RecordGramin); yy_start = 1; strpRecordGram = command.chars(); // get pointer to command string posRecordGram = 0; // initialize string position return RecordGramparse(); // parse command string } //# Give the string position. Int& recordGramPosition() { return posRecordGram; } //# Get the next input characters for flex. int recordGramInput (char* buf, int max_size) { int nr=0; while (*strpRecordGram != 0) { if (nr >= max_size) { break; // get max. max_size char. } buf[nr++] = *strpRecordGram++; } return nr; } void RecordGramerror (const char*) { throw (TableInvExpr ("Parse error at or near '" + String(RecordGramtext) + "'")); } Bool RecordGram::expr2Bool (const String& expr, const Record& vars) { // Convert expression to tree. TableExprNode node (RecordGram::parse(vars, expr)); // Evaluate. Bool result; node.get (vars, result); return result; } Int64 RecordGram::expr2Int (const String& expr, const Record& vars) { return Int64(expr2Double (expr, vars) + 0.0001); } double RecordGram::expr2Double (const String& expr, const Record& vars, const String& unit) { String ex = expr; if (! unit.empty()) { // Convert to the given unit (e.g., 1.3 GHz to Hz). ex = "(" + ex + ")" + unit; } // Convert expression to tree. TableExprNode node (RecordGram::parse(vars, ex)); // Evaluate. double result; node.get (vars, result); return result; } DComplex RecordGram::expr2Complex (const String& expr, const Record& vars) { // Convert expression to tree. TableExprNode node (RecordGram::parse(vars, expr)); // Evaluate. DComplex result; node.get (vars, result); return result; } String RecordGram::expr2String (const String& expr, const Record& vars) { // Convert expression to tree. TableExprNode node (RecordGram::parse(vars, expr)); // Evaluate. String result; node.get (vars, result); return result; } MVTime RecordGram::expr2Date (const String& expr, const Record& vars) { // Convert expression to tree. TableExprNode node (RecordGram::parse(vars, expr)); // Evaluate. MVTime result; node.get (vars, result); return result; } Array RecordGram::expr2ArrayBool (const String& expr, const Record& vars) { String ex = expr; // Convert expression to tree. TableExprNode node (RecordGram::parse(vars, expr)); // Evaluate. Array result; if (node.isScalar()) { result.resize (IPosition(1,1)); node.get (vars, result.data()[0]); } else { node.get (vars, result); } return result; } Array RecordGram::expr2ArrayInt (const String& expr, const Record& vars) { // Convert expression to tree. TableExprNode node (RecordGram::parse(vars, expr)); // Evaluate. Array result; if (node.isScalar()) { result.resize (IPosition(1,1)); node.get (vars, result.data()[0]); } else { node.get (vars, result); } return result; } Array RecordGram::expr2ArrayDouble (const String& expr, const Record& vars, const String& unit) { String ex = expr; if (! unit.empty()) { // Convert to the given unit (e.g., 1.3 GHz to Hz). ex = "(" + ex + ")" + unit; } // Convert expression to tree. TableExprNode node (RecordGram::parse(vars, ex)); // Evaluate. Array result; if (node.isScalar()) { result.resize (IPosition(1,1)); node.get (vars, result.data()[0]); } else { node.get (vars, result); } return result; } Array RecordGram::expr2ArrayComplex (const String& expr, const Record& vars) { // Convert expression to tree. TableExprNode node (RecordGram::parse(vars, expr)); // Evaluate. Array result; if (node.isScalar()) { result.resize (IPosition(1,1)); node.get (vars, result.data()[0]); } else { node.get (vars, result); } return result; } Array RecordGram::expr2ArrayString (const String& expr, const Record& vars) { String ex = expr; // Convert expression to tree. TableExprNode node (RecordGram::parse(vars, ex)); // Evaluate. Array result; if (node.isScalar()) { result.resize (IPosition(1,1)); node.get (vars, result.data()[0]); } else { node.get (vars, result); } return result; } Array RecordGram::expr2ArrayDate (const String& expr, const Record& vars) { String ex = expr; // Convert expression to tree. TableExprNode node (RecordGram::parse(vars, ex)); // Evaluate. Array result; if (node.isScalar()) { result.resize (IPosition(1,1)); node.get (vars, result.data()[0]); } else { node.get (vars, result); } return result; } TableExprNode RecordGram::parse (const RecordInterface& record, const String& expression) { std::lock_guard lock(theirMutex); theirRecPtr = &record; theirTabPtr = 0; return doParse (expression); } TableExprNode RecordGram::parse (const Table& table, const String& expression) { std::lock_guard lock(theirMutex); theirRecPtr = 0; theirTabPtr = &table; return doParse (expression); } TableExprNode RecordGram::doParse (const String& expression) { theirTokens.clear(); String message; String command = expression + '\n'; Bool error = False; TableExprNode result; try { // Parse and execute the command. if (recordGramParseCommand(command) != 0) { throw (TableParseError(expression)); // throw exception if error } // Make this copy before deleteTokenStorage is done, // otherwise it will be deleted. result = *theirNodePtr; } catch (const std::exception& x) { message = x.what(); error = True; } // Delete possibly non-deleted tokens (usually in case of exception). deleteTokenStorage(); //# If an exception was thrown; throw it again with the message. if (error) { throw AipsError(message + '\n' + "Scanned so far: " + command.before(recordGramPosition())); } return result; } //# Convert a constant to a TableExprNode object. //# The leading and trailing " is removed from a string. TableExprNode RecordGram::handleLiteral (RecordGramVal* val) { TableExprNode expr; switch (val->type) { case 'b': expr = TableExprNode (val->bval); break; case 'i': expr = TableExprNode (val->ival); break; case 'f': expr = TableExprNode (val->dval[0]); if (! val->str.empty()) { expr = expr.useUnit (val->str); } break; case 'c': expr= TableExprNode (DComplex (val->dval[0], val->dval[1])); break; case 's': expr = TableExprNode (val->str); break; case 'd': { MUString str (val->str); Quantity res; if (! MVTime::read (res, str)) { throw (TableInvExpr ("invalid date string " + val->str)); } expr = TableExprNode (MVTime(res)); } break; case 't': { Quantity res; //# Skip a possible leading / which acts as an escape character. if (val->str.length() > 0 && val->str[0] == '/') { val->str = val->str.after(0); } if (! MVAngle::read (res, val->str)) { throw (TableInvExpr ("invalid time/pos string " + val->str)); } expr = TableExprNode (MVAngle(res).radian()); expr = expr.useUnit ("rad"); } break; default: throw (TableInvExpr ("RecordGram: unhandled literal type")); } return expr; } TableExprNode RecordGram::handleField (const String& name) { if (theirTabPtr == 0) { return makeRecordExpr (*theirRecPtr, name); } return TableExprNode::keyCol (TableExprInfo(*theirTabPtr), name, Vector()); } TableExprNode RecordGram::handleFunc (const String& name, const TableExprNodeSet& arguments) { // The ROWNR function can only be used with tables. if (theirTabPtr == 0) { Vector ignoreFuncs (1, TableExprFuncNode::rownrFUNC); return TableParseFunc::makeFuncNode (0, name, arguments, ignoreFuncs, TableExprInfo(), theirTaQLStyle); } return TableParseFunc::makeFuncNode (0, name, arguments, Vector(), TableExprInfo(*theirTabPtr), theirTaQLStyle); } TableExprNode RecordGram::handleRegex (const TableExprNode& left, const String& regex) { Bool caseInsensitive = False; Bool negate = False; Int sz = regex.size(); if (sz > 0 && regex[sz-1] == 'i') { caseInsensitive = True; --sz; } AlwaysAssert (sz >= 4 && regex[sz-1] != ' ', AipsError); Int inx = 0; if (regex[0] == '!') { negate = True; ++inx; } AlwaysAssert (regex[inx] == '~', AipsError); while (regex[++inx] == ' ') {} AlwaysAssert (regex.size()-inx >= 3, AipsError); // Remove delimiters. String str = regex.substr(inx+2, sz-inx-3); if (regex[inx] == 'p') { str = Regex::fromPattern (str); } else if (regex[inx] == 'm') { str = ".*(" + str + ").*"; } TableExprNode lnode(left); if (caseInsensitive) { str = Regex::makeCaseInsensitive (str); } TableExprNode rnode((Regex(str, True))); if (negate) { lnode = (lnode != rnode); } else { lnode = (lnode == rnode); } return lnode; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/RecordGram.h000066400000000000000000000244741476623553700176100ustar00rootroot00000000000000//# RecordGram.h: Grammar for record command lines //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_RECORDGRAM_H #define TABLES_RECORDGRAM_H //# Includes #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableExprNode; class TableExprNodeSet; class TableExprNodeSetElem; class Table; // // Global functions for flex/bison scanner/parser for RecordGram // // // // // //# Classes you should understand before using this one. //
      • RecordGram.l and .y (flex and bison grammar) // // // Global functions are needed to define the input of the flex scanner // and to start the bison parser. // The input is taken from a string. // // // It is necessary to be able to give a record select command in ASCII. // This can be used in a CLI or in the record browser to get a subset // of a record or to sort a record. // // //# A List of bugs, limitations, extensions or planned refinements. // // // Declare the bison parser (is implemented by bison command). int recordGramParseCommand (const String& command); // The yyerror function for the parser. // It throws an exception with the current token. void RecordGramerror (const char*); // Give the current position in the string. // This can be used when parse errors occur. Int& recordGramPosition(); // Declare the input routine for flex/bison. int recordGramInput (char* buf, int max_size); // A function to remove escaped characters. inline String recordGramRemoveEscapes (const String& in) { return tableGramRemoveEscapes (in); } // A function to remove quotes from a quoted string. inline String recordGramRemoveQuotes (const String& in) { return tableGramRemoveQuotes (in); } // // // Helper class for values in RecordGram // // // // // // A record selection command is lexically analyzed via flex. // An object of this class is used to hold a value (like a name // or a literal) for later use in the parser code. // class RecordGramVal { public: Int type; //# i=Int, f=Double, c=DComplex, s=String r=Regex String str; //# string literal; table name; field name; unit Bool bval; //# bool literal Int64 ival; //# integer literal Double dval[2]; //# Double/DComplex literal }; // // Select-class for flex/bison scanner/parser for RecordGram // // // // // //# Classes you should understand before using this one. //
      • RecordGram.l and .y (flex and bison grammar) // // // This class is needed for the the actions in the flex scanner // and bison parser. // This stores the information by constructing RecordGram objects // as needed and storing them in a List. // // An expression can be given as a string and parsed by the parse // function. // The grammar used is as much as possible the same as that for the // WHERE clause in TaQL (see Note 199). // It is possible to set the TaQL style to use by setting // theirTaQLStyle before calling the parse functions. // A better way is to define the style with the 'USING STYLE' part of // the command (similar to TaQL). // // // It is necessary to be able to give a record select command in ASCII. // It is used by the ACSIS people. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class RecordGram { public: // Define the types of tokens in the grammar. enum Token {Node, Val, Elem, Set}; // Convert an expression string to an expression tree. // The expression will operate on a series of Record objects. // The given record is needed to know the type of the fields used in // the expression. //# The record will be put into the static variable to be used by //# the other functions. static TableExprNode parse (const RecordInterface& record, const String& expression); // Convert an expression string to an expression tree. // The expression will operate on the given table. //# The record will be put into the static variable to be used by //# the other functions. static TableExprNode parse (const Table& table, const String& expression); // Evaluate an expression to the given type. // The expression can contain variables; their names and values must be // defined in the record. // For double values it is possible to specify the desired unit. // If the expression is a scalar value, the expr2Array functions will // return an array with length 1. // static Bool expr2Bool (const String& expr, const Record& vars=Record()); static Int64 expr2Int (const String& expr, const Record& vars=Record()); static double expr2Double (const String& expr, const Record& vars=Record(), const String& unit=String()); static DComplex expr2Complex (const String& expr, const Record& vars=Record()); static String expr2String (const String& expr, const Record& vars=Record()); static MVTime expr2Date (const String& expr, const Record& vars=Record()); static Array expr2ArrayBool (const String& expr, const Record& vars=Record()); static Array expr2ArrayInt (const String& expr, const Record& vars=Record()); static Array expr2ArrayDouble (const String& expr, const Record& vars=Record(), const String& unit=String()); static Array expr2ArrayComplex (const String& expr, const Record& vars=Record()); static Array expr2ArrayString (const String& expr, const Record& vars=Record()); static Array expr2ArrayDate (const String& expr, const Record& vars=Record()); // // Create a TableExprNode from a literal. static TableExprNode handleLiteral (RecordGramVal*); // Find the field name and create a TableExprNode from it. // To be called only by the yy parser (under theirMutex). static TableExprNode handleField (const String& name); // Handle a function. // To be called only by the yy parser (under theirMutex). static TableExprNode handleFunc (const String& name, const TableExprNodeSet& arguments); // Handle a regex. static TableExprNode handleRegex (const TableExprNode& left, const String& regex); // Set the final node pointer. static void setNodePtr (TableExprNode* nodePtr) { theirNodePtr = nodePtr; } // Define the global TaQLStyle to use. // By default it is glish style. static TaQLStyle theirTaQLStyle; // Add a token to the list of tokens to be deleted // for the possible tokens in the RecordGram.yy union. // The addToken() functions are to be called only by the yy parser (under theirMutex). static void addToken (TableExprNode* ptr); static void addToken (RecordGramVal* ptr); static void addToken (TableExprNodeSet* ptr); static void addToken (TableExprNodeSetElem* ptr); // Delete a token and remove from the list. // The deleteToken() functions are to be called only by the yy parser (under theirMutex). static void deleteToken (TableExprNode* ptr); static void deleteToken (RecordGramVal* ptr); static void deleteToken (TableExprNodeSet* ptr); static void deleteToken (TableExprNodeSetElem* ptr); private: // Delete all tokens not deleted yet. static void deleteTokenStorage(); // Do the conversion of an expression string to an expression tree. static TableExprNode doParse (const String& expression); // Add a token to the list of tokens to be deleted. static void addToken (void* ptr, Token type) { theirTokens[ptr] = type; } // Remove a token from the list of tokens to be deleted. static void removeToken (void* ptr) { theirTokens.erase (ptr); } static std::map theirTokens; static const RecordInterface* theirRecPtr; static const Table* theirTabPtr; static TableExprNode* theirNodePtr; static std::mutex theirMutex; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/RecordGram.ll000066400000000000000000000267531476623553700177720ustar00rootroot00000000000000/* RecordGram.l: Lexical analyzer for table commands Copyright (C) 2000,2003 Associated Universities, Inc. Washington DC, USA. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. Correspondence concerning AIPS++ should be addressed as follows: Internet email: casa-feedback@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA */ /* yy_unput is not used, so let flex not generate it, otherwise picky compilers will issue warnings. */ %option nounput %{ #undef YY_INPUT #define YY_INPUT(buf,result,max_size) result=recordGramInput(buf,max_size) #undef YY_DECL #define YY_DECL int RecordGramlex (YYSTYPE* lvalp) %} /* The order in the following list is important, since, for example, the word "giving" must be recognized as GIVING and not as NAME. Similarly, an alphanumeric string must be recognized as NAME and not as NAMETAB or NAMEFLD. Complex values can be given as: FLOATi where i is the letter i (in lowercase only). In a NAME the backslash can be used to escape special characters like -. In that way a name like DATE-OBS can be given as DATE\-OBS. */ WHITE1 [ \t\n] WHITE {WHITE1}* DIGIT [0-9] INT {DIGIT}+ INT2 {DIGIT}{DIGIT} INT4 {INT2}{INT2} HEXINT 0[xX][0-9a-fA-F]+ EXP [DdEe][+-]?{INT} FLOAT {INT}{EXP}|{INT}"."{DIGIT}*({EXP})?|{DIGIT}*"."{INT}({EXP})? FLINT {FLOAT}|{INT} COMPLEX {FLINT}[ij] TRUE T|([Tt][Rr][Uu][Ee]) FALSE F|([Ff][Aa][Ll][Ss][Ee]) FLINTUNIT {FLINT}[a-zA-Z]+ MONTH [A-Za-z]+ DATEA {INT}{MONTH}{INT}|{INT}"-"{MONTH}"-"{INT} DATEH ({INT2}"-"{INT2}"-"{INT4})|({INT4}"-"{INT2}"-"{INT2}) DATES {INT4}"/"{INT2}"/"{INT2} DATE {DATEA}|{DATEH}|{DATES} DTIMEH {INT}[hH]({INT}?([mM]({FLINT})?)?)? DTIMEC {INT}":"({INT}?(":"({FLINT})?)?)? DTIME {DTIMEH}|{DTIMEC} DATETIME {DATE}([-/ T]{DTIME}(Z?))? POSHM {INT}[hH]{INT}[mM]{FLINT}? POSDM {INT}[dD]{INT}[mM]{FLINT}? POSD {INT}"."{INT}"."{FLINT} TIME {POSHM}|{POSDM}|{POSD} /* positions/times with colons cannot be allowed, because they interfere with the interval syntax. It is only possible when preceeded by a date. Furthermore, a colon is sometimes also used for degrees (in declinations), so it's better to stick to hms and dms. */ QSTRING \"[^\"\n]*\" ASTRING \'[^\'\n]*\' UQSTRING \"[^\"\n]*\n UASTRING \'[^\'\n]*\n STRING ({QSTRING}|{ASTRING})+ USTRING ({UQSTRING}|{UASTRING})+ STYLE [Uu][Ss][Ii][Nn][Gg]{WHITE}[Ss][Tt][Yy][Ll][Ee]{WHITE1} BETWEEN [Bb][Ee][Tt][Ww][Ee][Ee][Nn] LIKE [Ll][Ii][Kk][Ee] IN [Ii][Nn] INCONE [Ii][Nn]{WHITE}[Cc][Oo][Nn][Ee]{WHITE1} AND [Aa][Nn][Dd] OR [Oo][Rr] NOT [Nn][Oo][Tt] NAME \\?[A-Za-z_]([A-Za-z_0-9]|(\\.))* NAMEFLD {NAME}("."{NAME})* REGEX1 m"/"[^/]+"/" REGEX2 m%[^%]+% REGEX3 m#[^#]+# REGEX {REGEX1}|{REGEX2}|{REGEX3} FREGEX1 f"/"[^/]+"/" FREGEX2 f%[^%]+% FREGEX3 f#[^#]+# FREGEX {FREGEX1}|{FREGEX2}|{FREGEX3} PATT1 p\/[^/]+\/ PATT2 p%[^%]+% PATT3 p#[^#]+# PATT {PATT1}|{PATT2}|{PATT3} PATTEX ({REGEX}|{FREGEX}|{PATT})i? DIST1 d\/[^/]+\/ DIST2 d%[^%]+% DIST3 d#[^#]+# DISTOPT [bi]*{INT}?[bi]* DISTEX ({DIST1}|{DIST2}|{DIST3}){DISTOPT} OPERREX "!"?"~" PATTREX {OPERREX}{WHITE}({PATTEX}|{DISTEX}) %% /* This grammar is used for selection of records to be used in C++. It is the same as the WHERE part of TableGram. */ {IN} { recordGramPosition() += yyleng; return IN; } {INCONE} { recordGramPosition() += yyleng; return INCONE; } "[" { recordGramPosition() += yyleng; return LBRACKET; } "]" { recordGramPosition() += yyleng; return RBRACKET; } /* regular expression and pattern handling */ {PATTREX} { recordGramPosition() += yyleng; lvalp->val = new RecordGramVal(); RecordGram::addToken (lvalp->val); lvalp->val->type = 'r'; lvalp->val->str = String(RecordGramtext,yyleng); return REGEX; } /* operators */ "<:>" { recordGramPosition() += yyleng; return MIDWIDTH; } "<:<" { recordGramPosition() += yyleng; return OPENOPEN; } "<:=" { recordGramPosition() += yyleng; return OPENCLOSED; } "=:<" { recordGramPosition() += yyleng; return CLOSEDOPEN; } "=:=" { recordGramPosition() += yyleng; return CLOSEDCLOSED; } "<:" { recordGramPosition() += yyleng; return OPENEMPTY; } ":<" { recordGramPosition() += yyleng; return EMPTYOPEN; } "=:" { recordGramPosition() += yyleng; return CLOSEDEMPTY; } ":=" { recordGramPosition() += yyleng; return EMPTYCLOSED; } ":" { recordGramPosition() += yyleng; return COLON; } "==" { recordGramPosition() += yyleng; return EQ; } "=" { recordGramPosition() += yyleng; return EQ; } "!=" { recordGramPosition() += yyleng; return NE; } "<>" { recordGramPosition() += yyleng; return NE; } ">=" { recordGramPosition() += yyleng; return GE; } ">" { recordGramPosition() += yyleng; return GT; } "<=" { recordGramPosition() += yyleng; return LE; } "<" { recordGramPosition() += yyleng; return LT; } {STYLE} { recordGramPosition() += yyleng; return STYLE; } {BETWEEN} { recordGramPosition() += yyleng; return BETWEEN; } {LIKE} { recordGramPosition() += yyleng; return LIKE; } "&&" { recordGramPosition() += yyleng; return AND; } {AND} { recordGramPosition() += yyleng; return AND; } "||" { recordGramPosition() += yyleng; return OR; } {OR} { recordGramPosition() += yyleng; return OR; } "!" { recordGramPosition() += yyleng; return NOT; } {NOT} { recordGramPosition() += yyleng; return NOT; } "^" { recordGramPosition() += yyleng; return BITXOR; } "**" { recordGramPosition() += yyleng; return POWER; } "*" { recordGramPosition() += yyleng; return TIMES; } "/" { recordGramPosition() += yyleng; return DIVIDE; } "//" { recordGramPosition() += yyleng; return DIVIDETRUNC; } "%" { recordGramPosition() += yyleng; return MODULO; } "+" { recordGramPosition() += yyleng; return PLUS; } "-" { recordGramPosition() += yyleng; return MINUS; } "|" { tableGramPosition() += yyleng; return BITOR; } "&" { tableGramPosition() += yyleng; return BITAND; } "~" { tableGramPosition() += yyleng; return BITNOT; } "(" { recordGramPosition() += yyleng; return LPAREN; } ")" { recordGramPosition() += yyleng; return RPAREN; } "{" { recordGramPosition() += yyleng; return LBRACE; } "}" { recordGramPosition() += yyleng; return RBRACE; } "," { recordGramPosition() += yyleng; return COMMA; } /* Literals */ /* TIME must be done before FLINTUNIT, otherwise something like 2d1m is recognized as FLINTUNIT instead of TIME. Similarly COMPLEX must be done before FLINTUNIT. */ {COMPLEX} { recordGramPosition() += yyleng; lvalp->val = new RecordGramVal(); RecordGram::addToken (lvalp->val); lvalp->val->type = 'c'; sscanf (RecordGramtext, "%lf%*c", &(lvalp->val->dval[1])); lvalp->val->dval[0] = 0; return LITERAL; } {FLOAT} { recordGramPosition() += yyleng; lvalp->val = new RecordGramVal(); RecordGram::addToken (lvalp->val); lvalp->val->type = 'f'; lvalp->val->dval[0] = atof(RecordGramtext); return LITERAL; } {INT} { recordGramPosition() += yyleng; char* endPtr; Int64 v = strtol(RecordGramtext, &endPtr, 10); if (endPtr != RecordGramtext+yyleng) { throw TableInvExpr ("Integer number not fully parsed"); } lvalp->val = new RecordGramVal(); RecordGram::addToken (lvalp->val); lvalp->val->type = 'i'; lvalp->val->ival = v; return LITERAL; } {HEXINT} { recordGramPosition() += yyleng; char* endPtr; Int64 v = strtol(RecordGramtext, &endPtr, 0); if (endPtr != RecordGramtext+yyleng) { throw TableInvExpr ("Hex number not fully parsed"); } lvalp->val = new RecordGramVal(); RecordGram::addToken (lvalp->val); lvalp->val->type = 'i'; lvalp->val->ival = v; return LITERAL; } {TRUE} { recordGramPosition() += yyleng; lvalp->val = new RecordGramVal(); RecordGram::addToken (lvalp->val); lvalp->val->type = 'b'; lvalp->val->bval = True; return LITERAL; } {FALSE} { recordGramPosition() += yyleng; lvalp->val = new RecordGramVal(); RecordGram::addToken (lvalp->val); lvalp->val->type = 'b'; lvalp->val->bval = False; return LITERAL; } {STRING} { recordGramPosition() += yyleng; lvalp->val = new RecordGramVal(); RecordGram::addToken (lvalp->val); lvalp->val->type = 's'; lvalp->val->str = recordGramRemoveQuotes (RecordGramtext); return STRINGLITERAL; } {DATETIME} { recordGramPosition() += yyleng; lvalp->val = new RecordGramVal(); RecordGram::addToken (lvalp->val); lvalp->val->type = 'd'; lvalp->val->str = RecordGramtext; return LITERAL; } {TIME} { recordGramPosition() += yyleng; lvalp->val = new RecordGramVal(); RecordGram::addToken (lvalp->val); lvalp->val->type = 't'; lvalp->val->str = RecordGramtext; return LITERAL; } {FLINTUNIT} { recordGramPosition() += yyleng; double v; char unit[32]; sscanf (RecordGramtext, "%lf%31s", &v, unit); lvalp->val = new RecordGramVal(); RecordGram::addToken (lvalp->val); lvalp->val->type = 'f'; lvalp->val->dval[0] = v; lvalp->val->str = unit; return LITERAL; } {NAME} { recordGramPosition() += yyleng; lvalp->val = new RecordGramVal(); RecordGram::addToken (lvalp->val); lvalp->val->type = 's'; lvalp->val->str = recordGramRemoveEscapes (RecordGramtext); return NAME; } {NAMEFLD} { recordGramPosition() += yyleng; lvalp->val = new RecordGramVal(); RecordGram::addToken (lvalp->val); lvalp->val->type = 's'; lvalp->val->str = recordGramRemoveEscapes (RecordGramtext); return FLDNAME; } /* Whitespace is skipped */ {WHITE} { recordGramPosition() += yyleng; } /* An unterminated string is an error */ {USTRING} { throw (TableInvExpr ("Unterminated string")); } /* terminate on EOF */ <> { yyterminate(); } /* Any other character is invalid */ . { return YYERRCODE; } %% casacore-3.7.1/tables/TaQL/RecordGram.yy000066400000000000000000000534161476623553700200200ustar00rootroot00000000000000/* RecordGram.y: Parser for table commands Copyright (C) 2000-2002,2003 Associated Universities, Inc. Washington DC, USA. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. Correspondence concerning AIPS++ should be addressed as follows: Internet email: casa-feedback@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA */ /* The grammar has 1 shift/reduce conflict which is resolved in a correct way. */ %{ using namespace casacore; %} %pure-parser /* make parser re-entrant */ %expect 1 /* do not report 1 shift/reduce conflict */ %union { TableExprNode* node; RecordGramVal* val; TableExprNodeSetElem* elem; TableExprNodeSet* settp; } %token NAME /* name of function or shorthand for table */ %token FLDNAME /* name of field or shorthand for table */ %token LITERAL %token STRINGLITERAL %token REGEX %token STYLE %token IN %token INCONE %token BETWEEN %token AROUND %token LIKE %token LPAREN %token RPAREN %token COMMA %token LBRACKET %token RBRACKET %token LBRACE %token RBRACE %token COLON %token MIDWIDTH %token OPENOPEN %token OPENCLOSED %token CLOSEDOPEN %token CLOSEDCLOSED %token OPENEMPTY %token EMPTYOPEN %token CLOSEDEMPTY %token EMPTYCLOSED %type unit %type orexpr %type andexpr %type relexpr %type arithexpr %type inxexpr %type simexpr %type simbexpr %type set %type singlerange %type subscripts %type elemlist %type elems %type elem %type subsrange %type colonrange %type range %left OR %left AND %nonassoc EQ GT GE LT LE NE %left BITOR %left BITXOR %left BITAND %left PLUS MINUS %left TIMES DIVIDE DIVIDETRUNC MODULO %nonassoc UNARY BITNOT %nonassoc NOT %right POWER %{ namespace casacore { //# NAMESPACE CASACORE - BEGIN } //# NAMESPACE CASACORE - END int RecordGramlex (YYSTYPE*); %} %% topcomm: whexpr | stylecoms whexpr ; stylecoms: stylecoms stylecomm | stylecomm ; stylecomm: STYLE stylelist ; stylelist: stylelist COMMA NAME { RecordGram::theirTaQLStyle.set ($3->str); RecordGram::deleteToken ($3); } | NAME { RecordGram::theirTaQLStyle.set ($1->str); RecordGram::deleteToken ($1); } ; whexpr: orexpr /* set the final result */ { RecordGram::setNodePtr ($1); } ; orexpr: andexpr | orexpr OR andexpr { $$ = new TableExprNode (*$1 || *$3); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); } ; andexpr: relexpr | andexpr AND relexpr { $$ = new TableExprNode (*$1 && *$3); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); } ; relexpr: arithexpr | arithexpr EQ arithexpr { $$ = new TableExprNode (*$1 == *$3); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); } | arithexpr GT arithexpr { $$ = new TableExprNode (*$1 > *$3); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); } | arithexpr GE arithexpr { $$ = new TableExprNode (*$1 >= *$3); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); } | arithexpr LT arithexpr { $$ = new TableExprNode (*$1 < *$3); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); } | arithexpr LE arithexpr { $$ = new TableExprNode (*$1 <= *$3); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); } | arithexpr NE arithexpr { $$ = new TableExprNode (*$1 != *$3); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); } | arithexpr REGEX { $$ = new TableExprNode (RecordGram::handleRegex (*$1, $2->str)); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($2); } | arithexpr LIKE arithexpr { $$ = new TableExprNode (*$1 == sqlpattern(*$3)); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); } | arithexpr NOT LIKE arithexpr { $$ = new TableExprNode (*$1 != sqlpattern(*$4)); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($4); } | arithexpr IN arithexpr { $$ = new TableExprNode ($1->in (*$3)); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); } | arithexpr NOT IN arithexpr { TableExprNode node ($1->in (*$4)); $$ = new TableExprNode (!node); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($4); } | arithexpr IN singlerange { $$ = new TableExprNode ($1->in (*$3)); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); } | arithexpr NOT IN singlerange { TableExprNode node ($1->in (*$4)); $$ = new TableExprNode (!node); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($4); } | arithexpr BETWEEN arithexpr AND arithexpr { TableExprNodeSet set; set.add (TableExprNodeSetElem(True, *$3, *$5, True)); $$ = new TableExprNode ($1->in (set)); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); RecordGram::deleteToken ($5); } | arithexpr NOT BETWEEN arithexpr AND arithexpr { TableExprNodeSet set; set.add (TableExprNodeSetElem(True, *$4, *$6, True)); TableExprNode node ($1->in (set)); $$ = new TableExprNode (!node); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($4); RecordGram::deleteToken ($6); } | arithexpr AROUND arithexpr IN arithexpr { TableExprNodeSet set; set.add (TableExprNodeSetElem(*$3, *$5)); $$ = new TableExprNode ($1->in (set)); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); RecordGram::deleteToken ($5); } | arithexpr NOT AROUND arithexpr IN arithexpr { TableExprNodeSet set; set.add (TableExprNodeSetElem(*$4, *$6)); TableExprNode node ($1->in (set)); $$ = new TableExprNode (!node); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($4); RecordGram::deleteToken ($6); } | arithexpr INCONE arithexpr { $$ = new TableExprNode (anyCone (*$1, *$3)); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); } | arithexpr NOT INCONE arithexpr { TableExprNode node(anyCone (*$1, *$4)); $$ = new TableExprNode (!node); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($4); } ; arithexpr: inxexpr | arithexpr PLUS arithexpr { $$ = new TableExprNode (*$1 + *$3); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); } | arithexpr MINUS arithexpr { $$ = new TableExprNode (*$1 - *$3); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); } | arithexpr TIMES arithexpr { $$ = new TableExprNode (*$1 * *$3); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); } | arithexpr DIVIDE arithexpr { $$ = new TableExprNode (*$1 / *$3); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); } | arithexpr DIVIDETRUNC arithexpr { $$ = new TableExprNode (floor(*$1 / *$3)); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); } | arithexpr MODULO arithexpr { $$ = new TableExprNode (*$1 % *$3); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); } | arithexpr BITAND arithexpr { $$ = new TableExprNode (*$1 & *$3); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); } | arithexpr BITXOR arithexpr { $$ = new TableExprNode (*$1 ^ *$3); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); } | arithexpr BITOR arithexpr { $$ = new TableExprNode (*$1 | *$3); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); } | MINUS arithexpr %prec UNARY { $$ = new TableExprNode (-*$2); RecordGram::addToken ($$); RecordGram::deleteToken ($2); } | PLUS arithexpr %prec UNARY { $$ = $2; } | BITNOT arithexpr { $$ = new TableExprNode (~*$2); RecordGram::addToken ($$); RecordGram::deleteToken ($2); } | NOT arithexpr { $$ = new TableExprNode (!*$2); RecordGram::addToken ($$); RecordGram::deleteToken ($2); } | arithexpr POWER arithexpr { $$ = new TableExprNode (pow (*$1, *$3)); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); } ; inxexpr: simexpr | simexpr LBRACKET subscripts RBRACKET { $$ = new TableExprNode (TableParseQuery::handleSlice (*$1, *$3, RecordGram::theirTaQLStyle)); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); } ; simexpr: simbexpr { $$ = $1; } | simbexpr unit { $$ = new TableExprNode ($1->useUnit ($2->str)); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($2); } ; simbexpr: LPAREN orexpr RPAREN { $$ = $2; } | NAME LPAREN elemlist RPAREN { $$ = new TableExprNode (RecordGram::handleFunc ($1->str, *$3)); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); } | FLDNAME LPAREN elemlist RPAREN { $$ = new TableExprNode (RecordGram::handleFunc ($1->str, *$3)); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); } | NAME { $$ = new TableExprNode (RecordGram::handleField ($1->str)); RecordGram::addToken ($$); RecordGram::deleteToken ($1); } | FLDNAME { $$ = new TableExprNode (RecordGram::handleField ($1->str)); RecordGram::addToken ($$); RecordGram::deleteToken ($1); } | LITERAL { $$ = new TableExprNode (RecordGram::handleLiteral ($1)); RecordGram::addToken ($$); RecordGram::deleteToken ($1); } | STRINGLITERAL { $$ = new TableExprNode (RecordGram::handleLiteral ($1)); RecordGram::addToken ($$); RecordGram::deleteToken ($1); } | set { $$ = $1; } ; unit: NAME /* simple unit */ { $$ = $1; } | FLDNAME /* unit with . */ { $$ = $1; } | STRINGLITERAL /* compound unit (with special characters) */ { $$ = $1; } ; set: LBRACKET elems RBRACKET { $$ = new TableExprNode ($2->setOrArray()); RecordGram::addToken ($$); RecordGram::deleteToken ($2); } | LPAREN elems RPAREN { $$ = new TableExprNode ($2->setOrArray()); RecordGram::addToken ($$); RecordGram::deleteToken ($2); } ; elemlist: elems { $$ = $1; } | { $$ = new TableExprNodeSet; /* no elements */ RecordGram::addToken ($$); } ; elems: elems COMMA elem { $$ = $1; $$->add (*$3); RecordGram::deleteToken ($3); } | elem { $$ = new TableExprNodeSet; RecordGram::addToken ($$); $$->add (*$1); RecordGram::deleteToken ($1); } ; elem: orexpr { $$ = new TableExprNodeSetElem(*$1); RecordGram::addToken ($$); RecordGram::deleteToken ($1); } | range { $$ = $1; } ; singlerange: range { TableExprNodeSet set; set.add (*$1); RecordGram::deleteToken ($1); $$ = new TableExprNode (set.setOrArray()); RecordGram::addToken ($$); } ; range: colonrange { $$ = $1; } | LT arithexpr COMMA arithexpr GT { $$ = new TableExprNodeSetElem (False, *$2, *$4, False); RecordGram::addToken ($$); RecordGram::deleteToken ($2); RecordGram::deleteToken ($4); } | LT arithexpr COMMA arithexpr RBRACE { $$ = new TableExprNodeSetElem (False, *$2, *$4, True); RecordGram::addToken ($$); RecordGram::deleteToken ($2); RecordGram::deleteToken ($4); } | LBRACE arithexpr COMMA arithexpr GT { $$ = new TableExprNodeSetElem (True, *$2, *$4, False); RecordGram::addToken ($$); RecordGram::deleteToken ($2); RecordGram::deleteToken ($4); } | LBRACE arithexpr COMMA arithexpr RBRACE { $$ = new TableExprNodeSetElem (True, *$2, *$4, True); RecordGram::addToken ($$); RecordGram::deleteToken ($2); RecordGram::deleteToken ($4); } | LBRACE COMMA arithexpr GT { $$ = new TableExprNodeSetElem (*$3, False); RecordGram::addToken ($$); RecordGram::deleteToken ($3); } | LT COMMA arithexpr GT { $$ = new TableExprNodeSetElem (*$3, False); RecordGram::addToken ($$); RecordGram::deleteToken ($3); } | LBRACE COMMA arithexpr RBRACE { $$ = new TableExprNodeSetElem (*$3, True); RecordGram::addToken ($$); RecordGram::deleteToken ($3); } | LT COMMA arithexpr RBRACE { $$ = new TableExprNodeSetElem (*$3, True); RecordGram::addToken ($$); RecordGram::deleteToken ($3); } | LT arithexpr COMMA RBRACE { $$ = new TableExprNodeSetElem (False, *$2); RecordGram::addToken ($$); RecordGram::deleteToken ($2); } | LT arithexpr COMMA GT { $$ = new TableExprNodeSetElem (False, *$2); RecordGram::addToken ($$); RecordGram::deleteToken ($2); } | LBRACE arithexpr COMMA RBRACE { $$ = new TableExprNodeSetElem (True, *$2); RecordGram::addToken ($$); RecordGram::deleteToken ($2); } | LBRACE arithexpr COMMA GT { $$ = new TableExprNodeSetElem (True, *$2); RecordGram::addToken ($$); RecordGram::deleteToken ($2); } | arithexpr MIDWIDTH arithexpr { $$ = new TableExprNodeSetElem (*$1, *$3); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); } | arithexpr OPENOPEN arithexpr { $$ = new TableExprNodeSetElem (False, *$1, *$3, False); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); } | arithexpr OPENCLOSED arithexpr { $$ = new TableExprNodeSetElem (False, *$1, *$3, True); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); } | arithexpr CLOSEDOPEN arithexpr { $$ = new TableExprNodeSetElem (True, *$1, *$3, False); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); } | arithexpr CLOSEDCLOSED arithexpr { $$ = new TableExprNodeSetElem (True, *$1, *$3, True); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); } | EMPTYOPEN arithexpr { $$ = new TableExprNodeSetElem (*$2, False); RecordGram::addToken ($$); RecordGram::deleteToken ($2); } | EMPTYCLOSED arithexpr { $$ = new TableExprNodeSetElem (*$2, True); RecordGram::addToken ($$); RecordGram::deleteToken ($2); } | arithexpr OPENEMPTY { $$ = new TableExprNodeSetElem (False, *$1); RecordGram::addToken ($$); RecordGram::deleteToken ($1); } | arithexpr CLOSEDEMPTY { $$ = new TableExprNodeSetElem (True, *$1); RecordGram::addToken ($$); RecordGram::deleteToken ($1); } ; subscripts: subscripts COMMA subsrange { $$ = $1; $$->add (*$3); RecordGram::deleteToken ($3); } | subscripts COMMA { $$ = $1; $$->add (TableExprNodeSetElem (0, 0, 0)); } | COMMA { $$ = new TableExprNodeSet; RecordGram::addToken ($$); $$->add (TableExprNodeSetElem (0, 0, 0)); $$->add (TableExprNodeSetElem (0, 0, 0)); } | COMMA subsrange { $$ = new TableExprNodeSet; RecordGram::addToken ($$); $$->add (TableExprNodeSetElem (0, 0, 0)); $$->add (*$2); RecordGram::deleteToken ($2); } | subsrange { $$ = new TableExprNodeSet; RecordGram::addToken ($$); $$->add (*$1); RecordGram::deleteToken ($1); } ; subsrange: arithexpr { $$ = new TableExprNodeSetElem (*$1); RecordGram::addToken ($$); RecordGram::deleteToken ($1); } | colonrange { $$ = $1; } ; colonrange: arithexpr COLON arithexpr { $$ = new TableExprNodeSetElem ($1, $3, 0); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); } | arithexpr COLON arithexpr COLON arithexpr { $$ = new TableExprNodeSetElem ($1, $3, $5); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($3); RecordGram::deleteToken ($5); } | arithexpr COLON { TableExprNode incr(1); $$ = new TableExprNodeSetElem ($1, 0, &incr); RecordGram::addToken ($$); RecordGram::deleteToken ($1); } | arithexpr COLON COLON arithexpr { $$ = new TableExprNodeSetElem ($1, 0, $4); RecordGram::addToken ($$); RecordGram::deleteToken ($1); RecordGram::deleteToken ($4); } | COLON arithexpr { $$ = new TableExprNodeSetElem (0, $2, 0); RecordGram::addToken ($$); RecordGram::deleteToken ($2); } | COLON arithexpr COLON arithexpr { $$ = new TableExprNodeSetElem (0, $2, $4); RecordGram::addToken ($$); RecordGram::deleteToken ($2); RecordGram::deleteToken ($4); } | COLON COLON arithexpr { $$ = new TableExprNodeSetElem (0, 0, $3); RecordGram::addToken ($$); RecordGram::deleteToken ($3); } ; %% casacore-3.7.1/tables/TaQL/TaQLJoin.cc000066400000000000000000000430641476623553700173360ustar00rootroot00000000000000//# TaQLJoin.cc: Class handling the condition of the join clause //# Copyright (C) 2022 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Int64 TaQLJoinRow::findRow (const TableExprId&) { return itsRow; } TaQLJoin::TaQLJoin (const TENShPtr& mainNode, const TENShPtr& joinNode, const std::vector>& children) : itsMainNode (mainNode), itsJoinNode (joinNode), itsChildren (children) { itsOptSet = dynamic_cast(itsJoinNode.get()); AlwaysAssert (itsOptSet, AipsError); } Int64 TaQLJoin::findRow (const TableExprId& id) { Int64 index; switch (itsMainNode->dataType()) { case TableExprNodeRep::NTInt: index = itsOptSet->find (itsMainNode->getInt(id)); break; case TableExprNodeRep::NTDouble: index = itsOptSet->find (itsMainNode->getDouble(id)); break; case TableExprNodeRep::NTString: index = itsOptSet->find (itsMainNode->getString(id)); break; default: index = -1; } if (index < 0) { return -1; } return itsChildren[index]->findRow (id); } template std::shared_ptr TaQLJoin::makeOptDiscrete (TableExprNodeRep& node, const std::vector& mainNodes, const std::vector& joinNodes, const std::vector& rows, size_t level) { Vector vec (rows.size()); for (size_t i=0; i> children; Vector newVec(vec); if (level == mainNodes.size() - 1) { // For the lowest level, the children are TaQLJoinRow objects // containing the row number only. for (size_t j=0; j(new TaQLJoinRow(rows[j]))); } } else { // For other levels a TaQLJoin object is created for each unique value. // It contains all row numbers per value. // Sort the values and get the unique ones. Vector index; GenSortIndirect::sort (index, vec.data(), vec.size()); std::vector vals; T val = vec[index[0]]; std::vector srows; srows.push_back (index[0]); for (size_t j=1; j(vals)); } TENShPtr optSet (new TableExprNodeSetOptUSet (node, newVec)); return std::shared_ptr (new TaQLJoin (mainNodes[level].getRep(), optSet, children)); } template std::shared_ptr TaQLJoin::makeOptInterval (const TableExprNodeSet& set, const std::vector& mainNodes, const std::vector& joinNodes, const std::vector& rows, size_t level) { const TableExprNodeSetElemCont& elem = dynamic_cast(*(set[0].get())); // Get all start-end interval values. Block stvals(rows.size()); Block endvals(rows.size()); for (size_t i=0; igetStart (0, stvals[i]); newElem->getEnd (0, endvals[i]); } // Sort the intervals in order of start value. Vector index; GenSortIndirect::sort (index, stvals, stvals.size()); std::vector starts; std::vector ends; std::vector> children; if (level == mainNodes.size() - 1) { // For the lowest level, the children are TaQLJoinRow objects // containing the row number only. for (size_t j=0; j(new TaQLJoinRow(row))); } } else { // For other levels a TaQLJoin object is created for each unique interval. // It contains all row numbers for per interval. T st = stvals[index[0]]; T end = endvals[index[0]]; std::vector srows; srows.push_back (index[0]); for (size_t j=1; j::createOptSet (set, starts, ends, std::vector({elem.isLeftClosed()}), std::vector({elem.isRightClosed()}))); return std::shared_ptr (new TaQLJoin (mainNodes[level].getRep(), optSet, children)); } std::shared_ptr TaQLJoin::createRecursive (const std::vector& mainNodes, const std::vector& joinNodes, const std::vector& rows, size_t level) { // TaQLJoin objects are created in a recursive way and stored as a tree. // Each level represents an EQ or IN comparison in the join condition. // The top level contains one object. // The next level contains an object per unique value or interval. // In that way finding the matching row for multiple comparisons means // finding the right value at each level. // First see if a value or an interval is given. TableExprNodeRep* node = joinNodes[level].getRep().get(); TableExprNodeRep* mainNode = mainNodes[level].getRep().get(); const TableExprNodeSet* set = dynamic_cast(node); std::shared_ptr joinTree; if (!set) { AlwaysAssert (node->valueType() == TableExprNodeRep::VTScalar, AipsError); if (node->dataType() == TableExprNodeRep::NTInt && mainNode->dataType() == TableExprNodeRep::NTInt) { joinTree = makeOptDiscrete (*node, mainNodes, joinNodes, rows, level); } else if (node->dataType() == TableExprNodeRep::NTString && mainNode->dataType() == TableExprNodeRep::NTString) { joinTree = makeOptDiscrete (*node, mainNodes, joinNodes, rows, level); } else { throw TableInvExpr ("In a equality join condition only Int and String " "data types are possible"); } } else { // Check if a correct interval is given in the set. // The data type checking is superfluous because the IN operator does that. // But you never know. AlwaysAssert (set->size() == 1, AipsError); AlwaysAssert (!set->isDiscrete() && !set->hasArrays(), AipsError); const TENSEBShPtr& elem = (*set)[0]; if ((elem->dataType() == TableExprNodeRep::NTInt || elem->dataType() == TableExprNodeRep::NTDouble || elem->dataType() == TableExprNodeRep::NTDate) && (mainNode->dataType() == TableExprNodeRep::NTInt || mainNode->dataType() == TableExprNodeRep::NTDouble || mainNode->dataType() == TableExprNodeRep::NTDate)) { joinTree = makeOptInterval (*set, mainNodes, joinNodes, rows, level); } else if (elem->dataType() == TableExprNodeRep::NTString && mainNode->dataType() == TableExprNodeRep::NTString) { joinTree = makeOptInterval (*set, mainNodes, joinNodes, rows, level); } else { throw TableInvExpr ("In an interval join condition only Int, Double, Date " "and String data types are possible"); } } return joinTree; } TaQLJoinColumn::TaQLJoinColumn (const TENShPtr& columnNode, const TableParseJoin& join) : TableExprNodeRep (*columnNode), itsColumn (columnNode), itsJoin (join) { setUnit (columnNode->unit()); } // Get the table info for this column. TableExprInfo TaQLJoinColumn::getTableInfo() const { return itsColumn->getTableInfo(); } void TaQLJoinColumn::clear() {} MArray TaQLJoinColumn::getArrayBool (const TableExprId& id) { Int64 rownr = itsJoin.findRow(id); if (rownr < 0) { return MArray(); } return itsColumn->getArrayBool (rownr); } MArray TaQLJoinColumn::getArrayInt (const TableExprId& id) { Int64 rownr = itsJoin.findRow(id); if (rownr < 0) { return MArray(); } return itsColumn->getArrayInt (rownr); } MArray TaQLJoinColumn::getArrayDouble (const TableExprId& id) { Int64 rownr = itsJoin.findRow(id); if (rownr < 0) { return MArray(); } return itsColumn->getArrayDouble (rownr); } MArray TaQLJoinColumn::getArrayDComplex (const TableExprId& id) { Int64 rownr = itsJoin.findRow(id); if (rownr < 0) { return MArray(); } return itsColumn->getArrayDComplex (rownr); } MArray TaQLJoinColumn::getArrayString (const TableExprId& id) { Int64 rownr = itsJoin.findRow(id); if (rownr < 0) { return MArray(); } return itsColumn->getArrayString (rownr); } MArray TaQLJoinColumn::getArrayDate (const TableExprId& id) { Int64 rownr = itsJoin.findRow(id); if (rownr < 0) { return MArray(); } return itsColumn->getArrayDate (rownr); } TableExprNode TaQLJoinColumn::makeColumnNode (const TENShPtr& columnNode, const TableParseJoin& join) { DebugAssert (columnNode->operType() == OtColumn, AipsError); if (columnNode->valueType() == VTScalar) { switch (columnNode->dataType()) { case NTBool: return new TaQLJoinColumnBool (columnNode, join); case NTInt: return new TaQLJoinColumnInt (columnNode, join); case NTDouble: return new TaQLJoinColumnDouble (columnNode, join); case NTComplex: return new TaQLJoinColumnDComplex (columnNode, join); case NTString: return new TaQLJoinColumnString (columnNode, join); case NTDate: return new TaQLJoinColumnDate (columnNode, join); default: throw TableInvDT(); } } return new TaQLJoinColumn (columnNode, join); } TaQLJoinColumnBool::TaQLJoinColumnBool (const TENShPtr& columnNode, const TableParseJoin& join) : TaQLJoinColumn (columnNode, join) { rownr_t nrow = itsColumn->getTableInfo().table().nrow(); itsData.resize (nrow); for (rownr_t row=0; rowgetBool(row); } } Bool TaQLJoinColumnBool::getBool (const TableExprId& id) { Int64 rownr = itsJoin.findRow(id); if (rownr < 0) { return False; } DebugAssert (rownr < static_cast(itsData.size()), AipsError); return itsData[rownr]; } void TaQLJoinColumnBool::clear() { itsData.resize(); } TaQLJoinColumnInt::TaQLJoinColumnInt (const TENShPtr& columnNode, const TableParseJoin& join) : TaQLJoinColumn (columnNode, join) { rownr_t nrow = itsColumn->getTableInfo().table().nrow(); itsData.resize (nrow); for (rownr_t row=0; rowgetInt(row); } } Int64 TaQLJoinColumnInt::getInt (const TableExprId& id) { Int64 rownr = itsJoin.findRow(id); if (rownr < 0) { return std::numeric_limits::max(); } DebugAssert (rownr < static_cast(itsData.size()), AipsError); return itsData[rownr]; } void TaQLJoinColumnInt::clear() { itsData.resize(); } TaQLJoinColumnDouble::TaQLJoinColumnDouble (const TENShPtr& columnNode, const TableParseJoin& join) : TaQLJoinColumn (columnNode, join) { rownr_t nrow = itsColumn->getTableInfo().table().nrow(); itsData.resize (nrow); for (rownr_t row=0; rowgetDouble(row); } } Double TaQLJoinColumnDouble::getDouble (const TableExprId& id) { Int64 rownr = itsJoin.findRow(id); if (rownr < 0) { return std::numeric_limits::quiet_NaN(); } DebugAssert (rownr < static_cast(itsData.size()), AipsError); return itsData[rownr]; } void TaQLJoinColumnDouble::clear() { itsData.resize(); } TaQLJoinColumnDComplex::TaQLJoinColumnDComplex (const TENShPtr& columnNode, const TableParseJoin& join) : TaQLJoinColumn (columnNode, join) { rownr_t nrow = itsColumn->getTableInfo().table().nrow(); itsData.resize (nrow); for (rownr_t row=0; rowgetDComplex(row); } } DComplex TaQLJoinColumnDComplex::getDComplex (const TableExprId& id) { Int64 rownr = itsJoin.findRow(id); if (rownr < 0) { return DComplex(std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()); } DebugAssert (rownr < static_cast(itsData.size()), AipsError); return itsData[rownr]; } void TaQLJoinColumnDComplex::clear() { itsData.resize(); } TaQLJoinColumnString::TaQLJoinColumnString (const TENShPtr& columnNode, const TableParseJoin& join) : TaQLJoinColumn (columnNode, join) { rownr_t nrow = itsColumn->getTableInfo().table().nrow(); itsData.resize (nrow); for (rownr_t row=0; rowgetString(row); } } String TaQLJoinColumnString::getString (const TableExprId& id) { Int64 rownr = itsJoin.findRow(id); if (rownr < 0) { return "none"; } DebugAssert (rownr < static_cast(itsData.size()), AipsError); return itsData[rownr]; } void TaQLJoinColumnString::clear() { itsData.resize(); } TaQLJoinColumnDate::TaQLJoinColumnDate (const TENShPtr& columnNode, const TableParseJoin& join) : TaQLJoinColumn (columnNode, join) { rownr_t nrow = itsColumn->getTableInfo().table().nrow(); itsData.resize (nrow); for (rownr_t row=0; rowgetDate(row); } } MVTime TaQLJoinColumnDate::getDate (const TableExprId& id) { Int64 rownr = itsJoin.findRow(id); if (rownr < 0) { return MVTime(); } DebugAssert (rownr < static_cast(itsData.size()), AipsError); return itsData[rownr]; } void TaQLJoinColumnDate::clear() { itsData.resize(); } TaQLJoinRowid::TaQLJoinRowid (const TableExprInfo& tabInfo, const TableParseJoin& join) : TableExprNodeRep (NTInt, VTScalar, OtRownr, Variable), itsTabInfo(tabInfo), itsJoin (join) {} TableExprInfo TaQLJoinRowid::getTableInfo() const { return itsTabInfo; } Int64 TaQLJoinRowid::getInt (const TableExprId& id) { return itsJoin.findRow(id); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/TaQLJoin.h000066400000000000000000000314441476623553700171770ustar00rootroot00000000000000//# TaQLJoin.h: Class handling the condition of the join clause //# Copyright (C) 2022 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TAQLJOIN_H #define TABLES_TAQLJOIN_H #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations. class TableExprNode; class TableExprNodeSetElemCont; class TableParseJoin; // // Base class for handling a comparison in the join clause in a TaQL command // // // // // // class TaQLJoinBase { public: virtual ~TaQLJoinBase() = default; // Find the row number. <0 means not found. virtual Int64 findRow (const TableExprId&) = 0; }; // // Class holding the row number as the final level in the comparison tree // // // // // // Objects of this class form the lowest level in a TaQLJoin tree. It only // contains a row number telling which row in the join table contains the // values at the higher levels in the tree. // class TaQLJoinRow : public TaQLJoinBase { public: TaQLJoinRow (Int64 row) : itsRow (row) {} ~TaQLJoinRow() override = default; // Return the row number. Int64 findRow (const TableExprId&) override; private: Int64 itsRow; }; // // Class holding a comparison part of a join condition // // // // // // TaQLJoin holds a vector of nested TaQLJoinBase objects, one for each // unique value or interval in the join table. It uses TableExprNodeSetOptBase // to hold the values and the index in the vector of TaQLJoinBase objects. // The data types that can be used in a join condition are the same as those // supported by TableExprNodeSetOptBase; that is: integer and string for a // discrete value and int, double, datetime and string for an interval. // Note that in the interval case int and datetime are handled as double. // // Note that at the lowest level the vector of TaQLJoinBase objects contains // TaQLJoinRow objects holding the join table row number. The TaQLJoin objects // at the higher levels contain the index in the vector at the next level. // // The class contains static functions to build the tree given the left and // right part of a comparison, where left is an expression using the main table // and right using the join table. The right expression gives the values to // build the tree as sketched above. // // // // SELECT t1.COL1, t2.NAME FROM maintab t1 JOIN jointab t2 ON // t1.COL1 = t2.IDCOL // AND t1.TIME AROUND t2.TIME IN t2.INTERVAL // // In this example the main and join table are joined on 2 condition parts. // First the equality match of COL1 and IDCOL, second the interval match on TIME. // Note that the NAME column from the join table are selected, thus // the join is used to find the name for each row in the main table. //
        The join table columns may contain something like below. // Thus 4 unique IDCOL values, each having 2 time intervals. // // IDCOL TIME INTERVAL NAME // 0 tm1 d1 name01 // 1 tm1 d1 name11 // 2 tm1 d1 name21 // 3 tm1 d1 name31 // 0 tm2 d2 name02 // 1 tm2 d2 name12 // 2 tm2 d2 name22 // 3 tm2 d2 name32 // // The TaQLJoin tree will consist of 3 levels. The first level handles the // equality match. It consists of a single TaQLJoin object containing a vector // of second level TaQLJoin objects, one for each unique IDCOL value. // The second level handles the TIME match. The third level contains the row // number for each IDCOL/TIME pair. Schematically: // // level 1 TaQLJoin, IDCOL: 0 1 2 3 // level 2 TaQLJoin, TIME: tm1 tm2 tm1 tm2 tm1 tm2 tm1 tm2 // level 3 TaQLJoinRow: 0 1 2 3 4 5 6 7 // // To find the matching join table row means that first the matching // IDCOL is found at level 1, thereafter at level 2 the matching interval // for that IDCOL.Level 3 gives the correct row number in the join table. //
        //
        class TaQLJoin : public TaQLJoinBase { public: TaQLJoin (const TENShPtr& mainNode, const TENShPtr& joinNode, const std::vector>& children); ~TaQLJoin() override = default; // Find the row number in the join table for the given row in the main table. Int64 findRow (const TableExprId&) override; // From the given level on create nested TaQLJoin nodes. // It use makeOptDiscrete or makeOptInterval to create the appropriate // TableExprNodeSetOptBase object. static std::shared_ptr createRecursive (const std::vector& mainNodes, const std::vector& joinNodes, const std::vector& rows, size_t level); // Create nested TaQLJoin nodes for a join expression part at the given level // using discrete values. // Thereafter createRecursive is called for the next level. template static std::shared_ptr makeOptDiscrete (TableExprNodeRep& node, const std::vector& mainNodes, const std::vector& joinNodes, const std::vector& rows, size_t level); // Create nested TaQLJoin nodes for a join expression part at the given level // using intervals. // Thereafter createRecursive is called for the next level. template static std::shared_ptr makeOptInterval (const TableExprNodeSet& set, const std::vector& mainNodes, const std::vector& joinNodes, const std::vector& rows, size_t level); private: TENShPtr itsMainNode; TENShPtr itsJoinNode; // only used for automatic deletion TableExprNodeSetOptBase* itsOptSet; // same ptr as itsJoinNode std::vector> itsChildren; }; // // A column in a join table // // // // // // TaQLJoinColumn contains the TableExprNodeColumn object for a // column in a join table. It is used to find a value in the join column // for a row in the main table. It uses the TableParseJoin object to find // the row in the join table given the row in the main table. // class TaQLJoinColumn : public TableExprNodeRep { public: TaQLJoinColumn (const TENShPtr& columnNode, const TableParseJoin&); ~TaQLJoinColumn() override = default; // Get the table info for this column. TableExprInfo getTableInfo() const override; // Get the data for the given id. // Using the Join object it maps the row number in the main table // to the row number in the join table. // MArray getArrayBool (const TableExprId& id) override; MArray getArrayInt (const TableExprId& id) override; MArray getArrayDouble (const TableExprId& id) override; MArray getArrayDComplex (const TableExprId& id) override; MArray getArrayString (const TableExprId& id) override; MArray getArrayDate (const TableExprId& id) override; // // Clear the internal data vector (in derived classes). virtual void clear(); // Make the appropriate TaQLJoinColumn object. static TableExprNode makeColumnNode (const TENShPtr& columnNode, const TableParseJoin&); protected: TENShPtr itsColumn; const TableParseJoin& itsJoin; }; // // A scalar column of the given type in a join table // // // // // // TaQLJoinArrayColumn contains the TableExprNodeColumn object for an array // column in a join table. It is used to find a value in the join column // for a row in the main table. It uses the TableParseJoin object to find // the row in the join table given the row in the main table. // class TaQLJoinColumnBool : public TaQLJoinColumn { public: TaQLJoinColumnBool (const TENShPtr& columnNode, const TableParseJoin&); ~TaQLJoinColumnBool() override = default; Bool getBool (const TableExprId& id) override; void clear() override; private: Vector itsData; }; class TaQLJoinColumnInt : public TaQLJoinColumn { public: TaQLJoinColumnInt (const TENShPtr& columnNode, const TableParseJoin&); ~TaQLJoinColumnInt() override = default; Int64 getInt (const TableExprId& id) override; void clear() override; private: Vector itsData; }; class TaQLJoinColumnDouble : public TaQLJoinColumn { public: TaQLJoinColumnDouble (const TENShPtr& columnNode, const TableParseJoin&); ~TaQLJoinColumnDouble() override = default; Double getDouble (const TableExprId& id) override; void clear() override; private: Vector itsData; }; class TaQLJoinColumnDComplex : public TaQLJoinColumn { public: TaQLJoinColumnDComplex (const TENShPtr& columnNode, const TableParseJoin&); ~TaQLJoinColumnDComplex() override = default; DComplex getDComplex (const TableExprId& id) override; void clear() override; private: Vector itsData; }; class TaQLJoinColumnString : public TaQLJoinColumn { public: TaQLJoinColumnString (const TENShPtr& columnNode, const TableParseJoin&); ~TaQLJoinColumnString() override = default; String getString (const TableExprId& id) override; void clear() override; private: Vector itsData; }; class TaQLJoinColumnDate : public TaQLJoinColumn { public: TaQLJoinColumnDate (const TENShPtr& columnNode, const TableParseJoin&); ~TaQLJoinColumnDate() override = default; MVTime getDate (const TableExprId& id) override; void clear() override; private: Vector itsData; }; // // The rowid in a join table // // // // // // TaQLJoinRowid contains row sequence numbers of the matching rows // in the join table. // class TaQLJoinRowid : public TableExprNodeRep { public: TaQLJoinRowid (const TableExprInfo&, const TableParseJoin&); ~TaQLJoinRowid() override = default; TableExprInfo getTableInfo() const override; // Get the data (rowid in join table) for the given id. // Using the Join object it maps the row number in the main table // to the row number in the join table. Int64 getInt (const TableExprId& id) override; private: TableExprInfo itsTabInfo; const TableParseJoin& itsJoin; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/TaQLNode.cc000066400000000000000000000175601476623553700173260ustar00rootroot00000000000000//# TaQLNode.cc: Representation of entities in the TaQL parse tree //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Initialize the static getting the result from the parser. TaQLNode TaQLNode::theirNode; std::vector TaQLNode::theirNodesCreated; // Initialize the TaQL style. TaQLStyle TaQLNode::theirStyle; std::mutex TaQLNode::theirMutex; TaQLNode TaQLNode::parse (const String& command) { // Add a newline if not present. String str(command); if (str.length() == 0 || str[str.length()-1] != '\n') { str += '\n'; } std::lock_guard lock(theirMutex); // Reset to default TaQL style and no timings. theirStyle.reset(); try { tableGramParseCommand (str); } catch (const TableGramError& x) { // Keep erroneous position and token in new exception clearNodesCreated(); throw TableParseError (str + " " + x.what(), x.pos(), x.token()); } catch (const std::exception& x) { // Parse error, so delete all nodes and rethrow. clearNodesCreated(); throw TableParseError (str + " " + x.what()); } TaQLNode node = theirNode; clearNodesCreated(); return node; } void TaQLNode::clearNodesCreated() { for (uInt i=0; isave (aio); } else { aio << TaQLNode_Null; } } TaQLNode TaQLNode::restore (AipsIO& aio) { aio.getstart ("TaQLNode"); TaQLNode node = restoreNode (aio); aio.getend(); return node; } TaQLNode TaQLNode::restoreNode (AipsIO& aio) { char nodeType; aio >> nodeType; switch (nodeType) { case TaQLNode_Null: return 0; case TaQLNode_Const: return TaQLConstNodeRep::restore (aio); case TaQLNode_Unary: return TaQLUnaryNodeRep::restore (aio); case TaQLNode_Binary: return TaQLBinaryNodeRep::restore (aio); case TaQLNode_Multi: return TaQLMultiNodeRep::restore (aio); case TaQLNode_Func: return TaQLFuncNodeRep::restore (aio); case TaQLNode_Range: return TaQLRangeNodeRep::restore (aio); case TaQLNode_Index: return TaQLIndexNodeRep::restore (aio); case TaQLNode_KeyCol: return TaQLKeyColNodeRep::restore (aio); case TaQLNode_Table: return TaQLTableNodeRep::restore (aio); case TaQLNode_Col: return TaQLColNodeRep::restore (aio); case TaQLNode_Columns: return TaQLColumnsNodeRep::restore (aio); case TaQLNode_Join: return TaQLJoinNodeRep::restore (aio); case TaQLNode_SortKey: return TaQLSortKeyNodeRep::restore (aio); case TaQLNode_Sort: return TaQLSortNodeRep::restore (aio); case TaQLNode_LimitOff: return TaQLLimitOffNodeRep::restore (aio); case TaQLNode_Giving: return TaQLGivingNodeRep::restore (aio); case TaQLNode_UpdExpr: return TaQLUpdExprNodeRep::restore (aio); case TaQLNode_Select: return TaQLSelectNodeRep::restore (aio); case TaQLNode_Update: return TaQLUpdateNodeRep::restore (aio); case TaQLNode_Insert: return TaQLInsertNodeRep::restore (aio); case TaQLNode_Delete: return TaQLDeleteNodeRep::restore (aio); case TaQLNode_Calc: return TaQLCalcNodeRep::restore (aio); case TaQLNode_CreTab: return TaQLCreTabNodeRep::restore (aio); case TaQLNode_ColSpec: return TaQLColSpecNodeRep::restore (aio); case TaQLNode_RecFld: return TaQLRecFldNodeRep::restore (aio); case TaQLNode_Unit: return TaQLUnitNodeRep::restore (aio); case TaQLNode_Regex: return TaQLRegexNodeRep::restore (aio); case TaQLNode_Count: return TaQLCountNodeRep::restore (aio); case TaQLNode_Groupby: return TaQLGroupNodeRep::restore (aio); case TaQLNode_AltTab: return TaQLAltTabNodeRep::restore (aio); case TaQLNode_AddCol: return TaQLAddColNodeRep::restore (aio); case TaQLNode_SetKey: return TaQLSetKeyNodeRep::restore (aio); case TaQLNode_RenDrop: return TaQLRenDropNodeRep::restore (aio); case TaQLNode_AddRow: return TaQLAddRowNodeRep::restore (aio); case TaQLNode_ConcTab: return TaQLConcTabNodeRep::restore (aio); case TaQLNode_Show: return TaQLShowNodeRep::restore (aio); case TaQLNode_CopyCol: return TaQLCopyColNodeRep::restore (aio); case TaQLNode_DropTab: return TaQLDropTabNodeRep::restore (aio); default: throw AipsError ("TaQLNode::restoreNode - unknown node type"); } } TaQLMultiNode TaQLNode::restoreMultiNode (AipsIO& aio) { char nodeType; aio >> nodeType; switch (nodeType) { case TaQLNode_Null: return 0; case TaQLNode_Multi: return TaQLMultiNodeRep::restore (aio); default: throw AipsError ("TaQLNode::restoreMultiNode - unknown node type"); } } TaQLConstNode::TaQLConstNode (TaQLConstNodeRep* rep) : TaQLNode(rep), itsNRep(rep) {} void TaQLConstNode::setIsTableName() { itsNRep->setIsTableName(); } const String& TaQLConstNode::getString() const { return itsNRep->getString(); } TaQLRegexNode::TaQLRegexNode (TaQLRegexNodeRep* rep) : TaQLNode(rep), itsNRep(rep) {} const String& TaQLRegexNode::getString() const { return itsNRep->itsValue; } Bool TaQLRegexNode::caseInsensitive() const { return itsNRep->itsCaseInsensitive; } Bool TaQLRegexNode::negate() const { return itsNRep->itsNegate; } TaQLMultiNode::TaQLMultiNode() : TaQLNode(0), itsNRep (0) {} TaQLMultiNode::TaQLMultiNode (Bool isSetOrArray) : TaQLNode(new TaQLMultiNodeRep(isSetOrArray)) { itsNRep = (TaQLMultiNodeRep*)(TaQLNode::itsRep.get()); } TaQLMultiNode::TaQLMultiNode (TaQLMultiNodeRep* rep) : TaQLNode(rep), itsNRep (rep) {} void TaQLMultiNode::add (const TaQLNode& node) { itsNRep->add (node); } void TaQLMultiNode::add (TaQLNodeRep* noderep) { itsNRep->add (TaQLNode(noderep)); } void TaQLMultiNode::setIsSetOrArray() { itsNRep->setIsSetOrArray(); } void TaQLMultiNode::setPPFix (const String& prefix, const String& postfix) { itsNRep->setPPFix (prefix, postfix); } void TaQLMultiNode::setSeparator (const String& sep) { itsNRep->setSeparator (sep); } void TaQLMultiNode::setSeparator (uInt incr, const String& sep) { itsNRep->setSeparator (incr, sep); } TaQLQueryNode::TaQLQueryNode (TaQLQueryNodeRep* rep) : TaQLNode(rep), itsNRep (rep) {} void TaQLQueryNode::setBrackets() { itsNRep->setBrackets(); } void TaQLQueryNode::setNoExecute() { itsNRep->setNoExecute(); } void TaQLQueryNode::setFromExecute() { itsNRep->setFromExecute(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/TaQLNode.h000066400000000000000000000166771476623553700172000ustar00rootroot00000000000000//# TaQLNode.h: Envelope class for a node in the raw TaQL parse tree //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TAQLNODE_H #define TABLES_TAQLNODE_H //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declaration. class AipsIO; class TaQLNodeVisitor; class TaQLMultiNode; class TaQLConstNodeRep; class TaQLRegexNodeRep; class TaQLMultiNodeRep; class TaQLQueryNodeRep; // // Envelope class for a node in the raw TaQL parse tree. // // // // // //# Classes you should understand before using this one. //
      • TableGram //
      • Note 199 describing // // TaQL // // // The result of parsing a TaQL command is stored in TaQLNode objects. // Each part of the command can have its own specialized // TaQLNodeRep object, which forms // the letter in the TaQLNode envelope. //
        The actual scanning/parsing of the command is done using flex/bison // as defined in the TableGram files. //
        // // The letter-envelope idiom (counted pointer) makes if much easier // to keep track of memory, especially in the case of exceptions. // class TaQLNode { public: // Default constructor. TaQLNode() {} // Construct for given letter. It takes over the pointer. TaQLNode (TaQLNodeRep* rep) { itsRep.reset (rep); } // Copy constructor (reference semantics). TaQLNode (const TaQLNode& that) { itsRep = that.itsRep; } // Assignment (reference semantics). TaQLNode& operator= (const TaQLNode& that) { if (this != &that) { itsRep = that.itsRep; } return *this; } // Get the TaQL style. const TaQLStyle& style() const { return itsRep->style(); } virtual ~TaQLNode() noexcept = default; // Parse a TaQL command and return the result. // An exception is thrown in case of parse errors. // The parse tree is deleted by function clearNodeCreated. static TaQLNode parse (const String& command); // Does the envelope contain a letter? Bool isValid() const { return Bool(itsRep); } // Return the type of letter. char nodeType() const { return itsRep->nodeType(); } // Get read access to the letter. const TaQLNodeRep* getRep() const { return itsRep.get(); } // Let the visitor visit the node. // If no node, return an empty result. TaQLNodeResult visit (TaQLNodeVisitor& visitor) const { return (itsRep ? itsRep->visit (visitor) : TaQLNodeResult()); } // Print the node (recursively) in the given stream. void show (std::ostream& os) const { if (itsRep) itsRep->show (os); } // Save and restore the entire parse tree. // void save (AipsIO& aio) const; static TaQLNode restore (AipsIO& aio); // protected: std::shared_ptr itsRep; private: // Delete all nodes that were created by the parser. static void clearNodesCreated(); public: // Helper functions for save/restore of tree. // void saveNode (AipsIO& aio) const; static TaQLNode restoreNode (AipsIO& aio); static TaQLMultiNode restoreMultiNode (AipsIO& aio); // // The object getting the final tree. static TaQLNode theirNode; // A list of objects created by the parser and deleted at the end. static std::vector theirNodesCreated; // Keep the TaQL style to use. static TaQLStyle theirStyle; // Use a mutex to guard the statics. static std::mutex theirMutex; }; // // Envelope class for a node containing a constant value. // // // // // // This is a specialization of the envelope class // TaQLNode for a node containing // a constant value. // class TaQLConstNode: public TaQLNode { public: explicit TaQLConstNode (TaQLConstNodeRep* rep); void setIsTableName(); const String& getString() const; private: TaQLConstNodeRep* itsNRep; }; // // Envelope class for a node containing a constant regex value. // // // // // // This is a specialization of the envelope class // TaQLNode for a node containing // a constant regex or pattern value. // class TaQLRegexNode: public TaQLNode { public: explicit TaQLRegexNode (TaQLRegexNodeRep* rep); const String& getString() const; Bool caseInsensitive() const; Bool negate() const; private: TaQLRegexNodeRep* itsNRep; }; // // Envelope class for a node containing a list of nodes. // // // // // // This is a specialization of the envelope class // TaQLNode for a node containing // a list of nodes. // class TaQLMultiNode: public TaQLNode { public: TaQLMultiNode(); explicit TaQLMultiNode (Bool isSetOrArray); TaQLMultiNode (TaQLMultiNodeRep* rep); void add (const TaQLNode& node); void add (TaQLNodeRep* noderep); void setIsSetOrArray(); void setPPFix (const String& prefix, const String& postfix); void setSeparator (const String& sep); void setSeparator (uInt incr, const String& sep); const TaQLMultiNodeRep* getMultiRep() const { return itsNRep; } private: TaQLMultiNodeRep* itsNRep; }; // // Envelope class for a node containing a selection command. // // // // // // This is a specialization of the envelope class // TaQLNode for a node containing // a selection command. // class TaQLQueryNode: public TaQLNode { public: TaQLQueryNode (TaQLQueryNodeRep* rep); void setBrackets(); void setNoExecute(); void setFromExecute(); private: TaQLQueryNodeRep* itsNRep; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/TaQLNodeDer.cc000066400000000000000000001551261476623553700177620ustar00rootroot00000000000000//# TaQLNodeDer.cc: Representation of entities in the TaQL parse tree //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundatcd ion; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // A global function to show the tables in a WITH clause (if used). void showWithTables (ostream& os, const TaQLMultiNode& with) { if (with.isValid()) { os << "WITH "; with.show (os); os << ' '; } } TaQLConstNodeRep::TaQLConstNodeRep (Bool value) : TaQLNodeRep (TaQLNode_Const), itsType (CTBool), itsIsTableName (False), itsBValue (value) {} TaQLConstNodeRep::TaQLConstNodeRep (Int64 value) : TaQLNodeRep (TaQLNode_Const), itsType (CTInt), itsIsTableName (False), itsIValue (value), itsRValue (value), itsCValue (value, 0.) {} TaQLConstNodeRep::TaQLConstNodeRep (Double value) : TaQLNodeRep (TaQLNode_Const), itsType (CTReal), itsIsTableName (False), itsRValue (value), itsCValue (value, 0.) {} TaQLConstNodeRep::TaQLConstNodeRep (Double value, const String& unit) : TaQLNodeRep (TaQLNode_Const), itsType (CTReal), itsIsTableName (False), itsRValue (value), itsCValue (value, 0.), itsUnit (unit) {} TaQLConstNodeRep::TaQLConstNodeRep (DComplex value) : TaQLNodeRep (TaQLNode_Const), itsType (CTComplex), itsIsTableName (False), itsCValue (value) {} TaQLConstNodeRep::TaQLConstNodeRep (const String& value, Bool isTableName) : TaQLNodeRep (TaQLNode_Const), itsType (CTString), itsIsTableName (isTableName), itsSValue (value) {} TaQLConstNodeRep::TaQLConstNodeRep (const MVTime& value) : TaQLNodeRep (TaQLNode_Const), itsType (CTTime), itsIsTableName (False), itsRValue (value), itsCValue (value, 0.), itsTValue (value) {} TaQLConstNodeRep::TaQLConstNodeRep (Int64 value, const String& subTableName) : TaQLNodeRep (TaQLNode_Const), itsType (CTInt), itsIsTableName (True), itsIValue (value), itsRValue (value), itsCValue (value, 0.), itsSValue (subTableName) {} const String& TaQLConstNodeRep::getString() const { AlwaysAssert (itsType == CTString, AipsError); return itsSValue; } TaQLNodeResult TaQLConstNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitConstNode (*this); } void TaQLConstNodeRep::show (std::ostream& os) const { // Output the possible unit in the same way as TaQLUnitNodeRep is doing. if (! itsUnit.empty()) { os << '('; } switch (itsType) { case CTBool: if (itsBValue) { os << 'T'; } else { os << 'F'; } break; case CTInt: if (itsIsTableName) { os << itsSValue; // tablename (including $i) } else { os << itsIValue; } break; case CTReal: os << std::setprecision(16) << itsRValue; break; case CTComplex: if (itsCValue.real() != 0) { os << std::setprecision(16) << itsCValue.real() << '+'; } os << std::setprecision(16) << itsCValue.imag() << 'i'; break; case CTString: if (itsIsTableName) { /// NOTE: possible special characters in the string should be handled. os << addEscape(itsSValue); } else { /// NOTE: possible quotes in the string should be handled. os << "'" << itsSValue << "'"; } break; case CTTime: // 10 digits precision in the time os << MVTime::Format(MVTime::YMD, 10) << itsTValue; break; } if (! itsUnit.empty()) { os << ")'" << itsUnit << "'"; } } void TaQLConstNodeRep::save (AipsIO& aio) const { aio << char(itsType) << itsIsTableName << itsUnit; switch (itsType) { case CTBool: aio << itsBValue; break; case CTInt: aio << itsIValue; if (itsIsTableName) { aio << itsSValue; } break; case CTReal: aio << itsRValue; break; case CTComplex: aio << itsCValue; break; case CTString: aio << itsSValue; break; case CTTime: aio << (double)itsTValue; break; } } TaQLNode TaQLConstNodeRep::restore (AipsIO& aio) { char type; Bool isTableName; String unit; aio >> type >> isTableName >> unit; switch (type) { case CTBool: { Bool value; aio >> value; return new TaQLConstNodeRep (value); } case CTInt: { Int64 value; aio >> value; if (isTableName) { String name; aio >> name; return new TaQLConstNodeRep (value, name); } else { return new TaQLConstNodeRep (value); } } case CTReal: { Double value; aio >> value; return new TaQLConstNodeRep (value, unit); } case CTComplex: { DComplex value; aio >> value; return new TaQLConstNodeRep (value); } case CTString: { String value; aio >> value; return new TaQLConstNodeRep (value, isTableName); } case CTTime: { double v; aio >> v; return new TaQLConstNodeRep (MVTime(v)); } } return 0; } TaQLRegexNodeRep::TaQLRegexNodeRep (const String& regex) : TaQLNodeRep (TaQLNode_Regex), itsCaseInsensitive (False), itsNegate (False), itsIgnoreBlanks (False), itsMaxDistance (-1) { Int sz = regex.size(); AlwaysAssert (sz >= 4 && regex[sz-1] != ' ', AipsError); Int inx = 0; if (regex[0] == '!') { itsNegate = True; ++inx; } AlwaysAssert (regex[inx] == '~', AipsError); // Skip blanks. while (regex[++inx] == ' ') {} // Find regex qualifiers. while (--sz > inx) { if (regex[sz] == 'i') { itsCaseInsensitive = True; } else if (regex[sz] == 'b') { itsIgnoreBlanks = True; } else if (isdigit(regex[sz])) { int numend = sz; while (isdigit(regex[--sz])) {} ++sz; istringstream istr(regex.substr(sz, numend)); istr >> itsMaxDistance; } else { break; } } ++sz; AlwaysAssert (sz-inx >= 3, AipsError); itsValue = regex.substr(inx, sz-inx); if (itsCaseInsensitive) { itsValue.downcase(); } } TaQLRegexNodeRep::TaQLRegexNodeRep (const String& value, Bool caseInsensitive, Bool negate, Bool ignoreBlanks, Int maxDistance) : TaQLNodeRep (TaQLNode_Regex), itsValue (value), itsCaseInsensitive (caseInsensitive), itsNegate (negate), itsIgnoreBlanks (ignoreBlanks), itsMaxDistance (maxDistance) {} TaQLNodeResult TaQLRegexNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitRegexNode (*this); } void TaQLRegexNodeRep::show (std::ostream& os) const { if (itsNegate) { os << '!'; } os << '~'; os << itsValue; if (itsCaseInsensitive) { os << 'i'; } if (itsIgnoreBlanks) { os << 'b'; } if (itsMaxDistance >= 0) { os << itsMaxDistance; } } void TaQLRegexNodeRep::save (AipsIO& aio) const { aio << itsValue << itsCaseInsensitive << itsNegate << itsIgnoreBlanks << itsMaxDistance; } TaQLNode TaQLRegexNodeRep::restore (AipsIO& aio) { String value; Bool caseInsensitive, negate, ignoreBlanks; Int maxDistance; aio >> value >> caseInsensitive >> negate >> ignoreBlanks >> maxDistance; return new TaQLRegexNodeRep (value, caseInsensitive, negate, ignoreBlanks, maxDistance); } TaQLUnaryNodeRep::TaQLUnaryNodeRep (Type type, const TaQLNode& child) : TaQLNodeRep (TaQLNode_Unary), itsType (type), itsChild (child) {} TaQLNodeResult TaQLUnaryNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitUnaryNode (*this); } void TaQLUnaryNodeRep::show (std::ostream& os) const { switch (itsType) { case U_MINUS: os << "-("; itsChild.show(os); os << ')'; break; case U_NOT: os << "NOT("; itsChild.show(os); os << ')'; break; case U_EXISTS: os << "EXISTS "; itsChild.show(os); break; case U_NOTEXISTS: os << "NOT EXISTS "; itsChild.show(os); break; case U_BITNOT: os << "~("; itsChild.show(os); os << ')'; break; } } void TaQLUnaryNodeRep::save (AipsIO& aio) const { aio << char(itsType); itsChild.saveNode (aio); } TaQLNode TaQLUnaryNodeRep::restore (AipsIO& aio) { char ctype; aio >> ctype; TaQLUnaryNodeRep::Type type = (TaQLUnaryNodeRep::Type)ctype; TaQLNode node = TaQLNode::restoreNode (aio); return new TaQLUnaryNodeRep (type, node); } TaQLBinaryNodeRep::TaQLBinaryNodeRep (Type type, const TaQLNode& left, const TaQLNode& right) : TaQLNodeRep (TaQLNode_Binary), itsType (type), itsLeft (left), itsRight (right) {} TaQLBinaryNodeRep* TaQLBinaryNodeRep::handleRegex (const TaQLNode& left, const TaQLRegexNode& right) { Type oper; if (right.negate()) { oper = B_NEREGEX; } else { oper = B_EQREGEX; } return new TaQLBinaryNodeRep (oper, left, right); } TaQLNodeResult TaQLBinaryNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitBinaryNode (*this); } void TaQLBinaryNodeRep::show (std::ostream& os) const { os << '('; itsLeft.show (os); os << ')'; Bool paren = True; switch (itsType) { case B_PLUS: os << '+'; break; case B_MINUS: os << '-'; break; case B_TIMES: os << '*'; break; case B_DIVIDE: os << '/'; break; case B_DIVIDETRUNC: os << "//"; break; case B_MODULO: os << '%'; break; case B_POWER: os << "**"; break; case B_OR: os << "||"; break; case B_AND: os << "&&"; break; case B_EQ: os << '='; break; case B_NE: os << "<>"; break; case B_GT: os << '>'; break; case B_GE: os << ">="; break; case B_LT: os << '<'; break; case B_LE: os << "<="; break; case B_IN: paren = False; os << " IN "; break; case B_INDEX: paren = False; break; case B_EQREGEX: case B_NEREGEX: paren = False; break; case B_BITAND: os << '&'; break; case B_BITXOR: os << '^'; break; case B_BITOR: os << '|'; break; } if (paren) { os << '('; itsRight.show (os); os << ')'; } else { itsRight.show (os); } } void TaQLBinaryNodeRep::save (AipsIO& aio) const { aio << char(itsType); itsLeft.saveNode (aio); itsRight.saveNode (aio); } TaQLNode TaQLBinaryNodeRep::restore (AipsIO& aio) { char ctype; aio >> ctype; TaQLBinaryNodeRep::Type type = (TaQLBinaryNodeRep::Type)ctype; TaQLNode left = TaQLNode::restoreNode (aio); TaQLNode right = TaQLNode::restoreNode (aio); return new TaQLBinaryNodeRep (type, left, right); } TaQLMultiNodeRep::TaQLMultiNodeRep (Bool isSetOrArray) : TaQLNodeRep (TaQLNode_Multi), itsIsSetOrArray (isSetOrArray), itsSep (","), itsIncr (1) {} TaQLMultiNodeRep::TaQLMultiNodeRep(const String& prefix, const String& postfix, Bool isSetOrArray) : TaQLNodeRep (TaQLNode_Multi), itsIsSetOrArray (isSetOrArray), itsPrefix (prefix), itsPostfix (postfix), itsSep (","), itsIncr (1) {} TaQLNodeResult TaQLMultiNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitMultiNode (*this); } void TaQLMultiNodeRep::show (std::ostream& os) const { os << itsPrefix; for (uInt i=0; i> isSetOrArray >> prefix >> postfix >> sep >> sep2 >> incr; aio >> size; std::unique_ptr node (new TaQLMultiNodeRep(prefix, postfix, isSetOrArray)); node->setSeparator (sep); node->setSeparator (incr, sep2); for (uInt i=0; iadd (TaQLNode::restoreNode (aio)); } return node.release(); } TaQLFuncNodeRep::TaQLFuncNodeRep (const String& name) : TaQLNodeRep (TaQLNode_Func), itsName (name), itsArgs (False) {} TaQLFuncNodeRep::TaQLFuncNodeRep (const String& name, const TaQLMultiNode& args) : TaQLNodeRep (TaQLNode_Func), itsName (name), itsArgs (args) {} TaQLNodeResult TaQLFuncNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitFuncNode (*this); } void TaQLFuncNodeRep::show (std::ostream& os) const { os << itsName << '('; itsArgs.show (os); os << ')'; } void TaQLFuncNodeRep::save (AipsIO& aio) const { aio << itsName; itsArgs.saveNode (aio); } TaQLNode TaQLFuncNodeRep::restore (AipsIO& aio) { String name; aio >> name; return new TaQLFuncNodeRep (name, TaQLNode::restoreMultiNode (aio)); } TaQLRangeNodeRep::TaQLRangeNodeRep (Bool leftClosed, const TaQLNode& start, const TaQLNode& end, Bool rightClosed, Bool asMidWidth) : TaQLNodeRep (TaQLNode_Range), itsStart (start), itsEnd (end), itsLeftClosed (leftClosed), itsRightClosed(rightClosed), itsAsMidWidth (asMidWidth) {} TaQLRangeNodeRep::TaQLRangeNodeRep (Bool leftClosed, const TaQLNode& start) : TaQLNodeRep (TaQLNode_Range), itsStart (start), itsEnd (), itsLeftClosed (leftClosed), itsRightClosed(False), itsAsMidWidth (False) {} TaQLRangeNodeRep::TaQLRangeNodeRep (const TaQLNode& end, Bool rightClosed) : TaQLNodeRep (TaQLNode_Range), itsStart (), itsEnd (end), itsLeftClosed (False), itsRightClosed(rightClosed), itsAsMidWidth (False) {} TaQLRangeNodeRep::TaQLRangeNodeRep (const TaQLNode& mid, const TaQLNode& width) : TaQLNodeRep (TaQLNode_Range), itsStart (mid), itsEnd (width), itsLeftClosed (True), itsRightClosed(True), itsAsMidWidth (True) {} TaQLNodeResult TaQLRangeNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitRangeNode (*this); } void TaQLRangeNodeRep::show (std::ostream& os) const { if (itsAsMidWidth) { os << '('; itsStart.show (os); os << ")<:>("; itsEnd.show (os); os << ')'; } else { if (itsLeftClosed) { os << '{'; } else { os << '<'; } itsStart.show (os); os << ','; itsEnd.show (os); if (itsRightClosed) { os << '}'; } else { os << '>'; } } } void TaQLRangeNodeRep::save (AipsIO& aio) const { aio << itsLeftClosed << itsRightClosed << itsAsMidWidth; itsStart.saveNode (aio); itsEnd.saveNode (aio); } TaQLNode TaQLRangeNodeRep::restore (AipsIO& aio) { Bool leftClosed, rightClosed, asMidWidth; aio >> leftClosed >> rightClosed >> asMidWidth; TaQLNode start = TaQLNode::restoreNode (aio); TaQLNode end = TaQLNode::restoreNode (aio); return new TaQLRangeNodeRep (leftClosed, start, end, rightClosed, asMidWidth); } TaQLIndexNodeRep::TaQLIndexNodeRep (const TaQLNode& start, const TaQLNode& end, const TaQLNode& incr) : TaQLNodeRep (TaQLNode_Index), itsStart (start), itsEnd (end), itsIncr (incr) {} TaQLNodeResult TaQLIndexNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitIndexNode (*this); } void TaQLIndexNodeRep::show (std::ostream& os) const { itsStart.show (os); if (itsEnd.isValid()) { os << ':'; itsEnd.show (os); } else if (itsIncr.isValid()) { os << ':'; } if (itsIncr.isValid()) { os << ':'; itsIncr.show (os); } } void TaQLIndexNodeRep::save (AipsIO& aio) const { itsStart.saveNode (aio); itsEnd.saveNode (aio); itsIncr.saveNode (aio); } TaQLNode TaQLIndexNodeRep::restore (AipsIO& aio) { TaQLNode start = TaQLNode::restoreNode (aio); TaQLNode end = TaQLNode::restoreNode (aio); TaQLNode incr = TaQLNode::restoreNode (aio); return new TaQLIndexNodeRep (start, end, incr); } TaQLJoinNodeRep::TaQLJoinNodeRep (const TaQLMultiNode& tables, const TaQLNode& condition) : TaQLNodeRep (TaQLNode_Join), itsTables (tables), itsCondition (condition) {} TaQLNodeResult TaQLJoinNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitJoinNode (*this); } void TaQLJoinNodeRep::show (std::ostream& os) const { os << " JOIN "; if (itsTables.isValid()) { itsTables.show (os); os << ' '; } os << "ON "; itsCondition.show (os); } void TaQLJoinNodeRep::save (AipsIO& aio) const { itsTables.saveNode (aio); itsCondition.saveNode (aio); } TaQLNode TaQLJoinNodeRep::restore (AipsIO& aio) { TaQLMultiNode tables = TaQLNode::restoreMultiNode (aio); TaQLNode condition = TaQLNode::restoreNode (aio); return new TaQLJoinNodeRep (tables, condition); } TaQLKeyColNodeRep::TaQLKeyColNodeRep (const String& name, const String& nameMask) : TaQLNodeRep (TaQLNode_KeyCol), itsName (name), itsNameMask (nameMask) {} TaQLNodeResult TaQLKeyColNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitKeyColNode (*this); } void TaQLKeyColNodeRep::show (std::ostream& os) const { if (itsNameMask.empty()) { os << itsName; } else { os << '(' << itsName << ',' << itsNameMask << ')'; } } void TaQLKeyColNodeRep::save (AipsIO& aio) const { aio << itsName << itsNameMask; } TaQLNode TaQLKeyColNodeRep::restore (AipsIO& aio) { String name, nameMask; aio >> name >> nameMask; return new TaQLKeyColNodeRep (name, nameMask); } TaQLTableNodeRep::TaQLTableNodeRep (const TaQLNode& table, const String& alias) : TaQLNodeRep (TaQLNode_Table), itsTable (table), itsAlias (alias) {} TaQLNodeResult TaQLTableNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitTableNode (*this); } void TaQLTableNodeRep::show (std::ostream& os) const { itsTable.show (os); if (! itsAlias.empty()) { os << " AS " << itsAlias; } } void TaQLTableNodeRep::save (AipsIO& aio) const { aio << itsAlias; itsTable.saveNode (aio); } TaQLNode TaQLTableNodeRep::restore (AipsIO& aio) { String alias; aio >> alias; return new TaQLTableNodeRep (TaQLNode::restoreNode(aio), alias); } TaQLColNodeRep::TaQLColNodeRep (const TaQLNode& expr, const String& name, const String& nameMask, const String& dtype) : TaQLNodeRep (TaQLNode_Col), itsExpr (expr), itsName (name), itsNameMask (nameMask), itsDtype (checkDataType(dtype)) {} TaQLNodeResult TaQLColNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitColNode (*this); } void TaQLColNodeRep::show (std::ostream& os) const { itsExpr.show (os); if (! itsName.empty()) { os << " AS " ; if (itsNameMask.empty()) { os << itsName; } else { os << '(' << itsName << ',' << itsNameMask << ')'; } if (! itsDtype.empty()) { os << ' ' << itsDtype; } } } void TaQLColNodeRep::save (AipsIO& aio) const { aio << itsName << itsNameMask << itsDtype; itsExpr.saveNode (aio); } TaQLNode TaQLColNodeRep::restore (AipsIO& aio) { String name, nameMask, dtype; aio >> name >> nameMask >> dtype; return new TaQLColNodeRep (TaQLNode::restoreNode(aio), name, nameMask, dtype); } TaQLColumnsNodeRep::TaQLColumnsNodeRep (Bool distinct, const TaQLMultiNode& nodes) : TaQLNodeRep (TaQLNode_Columns), itsDistinct (distinct), itsNodes (nodes) {} TaQLNodeResult TaQLColumnsNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitColumnsNode (*this); } void TaQLColumnsNodeRep::show (std::ostream& os) const { if (itsDistinct) { os << " DISTINCT"; } if (itsNodes.isValid()) { os << ' '; itsNodes.show (os); } } void TaQLColumnsNodeRep::save (AipsIO& aio) const { aio << itsDistinct; itsNodes.saveNode (aio); } TaQLNode TaQLColumnsNodeRep::restore (AipsIO& aio) { Bool distinct; aio >> distinct; return new TaQLColumnsNodeRep (distinct, TaQLNode::restoreMultiNode(aio)); } TaQLGroupNodeRep::TaQLGroupNodeRep (Type type, const TaQLMultiNode& nodes) : TaQLNodeRep (TaQLNode_Groupby), itsType (type), itsNodes (nodes) {} TaQLNodeResult TaQLGroupNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitGroupNode (*this); } void TaQLGroupNodeRep::show (std::ostream& os) const { os << " GROUPBY"; if (itsType == Rollup) { os << " ROLLUP"; } os << ' '; itsNodes.show (os); } void TaQLGroupNodeRep::save (AipsIO& aio) const { aio << char(itsType); itsNodes.saveNode (aio); } TaQLNode TaQLGroupNodeRep::restore (AipsIO& aio) { char ctype; aio >> ctype; TaQLGroupNodeRep::Type type = (TaQLGroupNodeRep::Type)ctype; return new TaQLGroupNodeRep (type, TaQLNode::restoreMultiNode(aio)); } TaQLSortKeyNodeRep::TaQLSortKeyNodeRep (Type type, const TaQLNode& child) : TaQLNodeRep (TaQLNode_SortKey), itsType (type), itsChild (child) {} TaQLNodeResult TaQLSortKeyNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitSortKeyNode (*this); } void TaQLSortKeyNodeRep::show (std::ostream& os) const { itsChild.show (os); switch (itsType) { case Ascending: os << " ASC"; break; case Descending: os << " DESC"; break; case None: break; } } void TaQLSortKeyNodeRep::save (AipsIO& aio) const { aio << char(itsType); itsChild.saveNode (aio); } TaQLNode TaQLSortKeyNodeRep::restore (AipsIO& aio) { char ctype; aio >> ctype; TaQLSortKeyNodeRep::Type type = (TaQLSortKeyNodeRep::Type)ctype; return new TaQLSortKeyNodeRep (type, TaQLNode::restoreNode(aio)); } TaQLSortNodeRep::TaQLSortNodeRep (Bool unique, Type type, const TaQLMultiNode& keys) : TaQLNodeRep (TaQLNode_Sort), itsUnique (unique), itsType (type), itsKeys (keys) {} TaQLNodeResult TaQLSortNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitSortNode (*this); } void TaQLSortNodeRep::show (std::ostream& os) const { os << " ORDERBY"; if (itsUnique) { os << " UNIQUE"; } if (itsType == Descending) { os << " DESC"; } os << ' '; itsKeys.show (os); } void TaQLSortNodeRep::save (AipsIO& aio) const { aio << itsUnique << char(itsType); itsKeys.saveNode (aio); } TaQLNode TaQLSortNodeRep::restore (AipsIO& aio) { Bool unique; char ctype; aio >> unique >> ctype; TaQLSortNodeRep::Type type = (TaQLSortNodeRep::Type)ctype; return new TaQLSortNodeRep (unique, type, TaQLNode::restoreMultiNode(aio)); } TaQLLimitOffNodeRep::TaQLLimitOffNodeRep (const TaQLNode& limit, const TaQLNode& offset) : TaQLNodeRep (TaQLNode_LimitOff), itsLimit (limit), itsOffset (offset) {} TaQLNodeResult TaQLLimitOffNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitLimitOffNode (*this); } void TaQLLimitOffNodeRep::show (std::ostream& os) const { if (itsLimit.isValid()) { os << " LIMIT "; itsLimit.show (os); } if (itsOffset.isValid()) { os << " OFFSET "; itsOffset.show (os); } } void TaQLLimitOffNodeRep::save (AipsIO& aio) const { itsLimit.saveNode (aio); itsOffset.saveNode (aio); } TaQLNode TaQLLimitOffNodeRep::restore (AipsIO& aio) { TaQLNode limit = TaQLNode::restoreNode (aio); TaQLNode offset = TaQLNode::restoreNode (aio); return new TaQLLimitOffNodeRep (limit, offset); } TaQLGivingNodeRep::TaQLGivingNodeRep (const String& name, const TaQLMultiNode& type) : TaQLNodeRep (TaQLNode_Giving), itsName (name), itsType (type) {} TaQLGivingNodeRep::TaQLGivingNodeRep (const TaQLMultiNode& exprlist) : TaQLNodeRep (TaQLNode_Giving), itsExprList (exprlist) {} TaQLNodeResult TaQLGivingNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitGivingNode (*this); } void TaQLGivingNodeRep::show (std::ostream& os) const { if (itsExprList.isValid()) { itsExprList.show (os); } else { os << addEscape(itsName); if (itsType.isValid()) { os << " AS "; itsType.show (os); } } } void TaQLGivingNodeRep::save (AipsIO& aio) const { itsExprList.saveNode (aio); if (! itsExprList.isValid()) { aio << itsName; itsType.saveNode (aio); } } TaQLNode TaQLGivingNodeRep::restore (AipsIO& aio) { TaQLMultiNode node = TaQLNode::restoreMultiNode(aio); if (node.isValid()) { return new TaQLGivingNodeRep (node); } String name; aio >> name; TaQLMultiNode type = TaQLNode::restoreMultiNode (aio); return new TaQLGivingNodeRep (name, type); } TaQLUpdExprNodeRep::TaQLUpdExprNodeRep (const String& name, const String& nameMask, const TaQLNode& expr) : TaQLNodeRep (TaQLNode_UpdExpr), itsName (name), itsNameMask (nameMask), itsExpr (expr) {} TaQLUpdExprNodeRep::TaQLUpdExprNodeRep (const String& name, const String& nameMask, const TaQLMultiNode& indices, const TaQLNode& expr) : TaQLNodeRep (TaQLNode_UpdExpr), itsName (name), itsNameMask (nameMask), itsIndices1 (indices), itsExpr (expr) {} TaQLUpdExprNodeRep::TaQLUpdExprNodeRep (const String& name, const String& nameMask, const TaQLMultiNode& indices1, const TaQLMultiNode& indices2, const TaQLNode& expr) : TaQLNodeRep (TaQLNode_UpdExpr), itsName (name), itsNameMask (nameMask), itsIndices1 (indices1), itsIndices2 (indices2), itsExpr (expr) {} TaQLNodeResult TaQLUpdExprNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitUpdExprNode (*this); } void TaQLUpdExprNodeRep::show (std::ostream& os) const { if (itsNameMask.empty()) { os << itsName; } else { os << '(' << itsName << ',' << itsNameMask << ')'; } itsIndices1.show (os); itsIndices2.show (os); os << '='; itsExpr.show (os); } void TaQLUpdExprNodeRep::save (AipsIO& aio) const { aio << itsName << itsNameMask; itsIndices1.saveNode (aio); itsIndices2.saveNode (aio); itsExpr.saveNode (aio); } TaQLNode TaQLUpdExprNodeRep::restore (AipsIO& aio) { String name, nameMask; aio >> name >> nameMask; TaQLMultiNode indices1 = TaQLNode::restoreMultiNode (aio); TaQLMultiNode indices2 = TaQLNode::restoreMultiNode (aio); TaQLNode expr = TaQLNode::restoreNode (aio); return new TaQLUpdExprNodeRep (name, nameMask, indices1, indices2, expr); } TaQLQueryNodeRep::TaQLQueryNodeRep (int nodeType) : TaQLNodeRep (nodeType), itsBrackets (False), itsNoExecute (False), itsFromExecute (False) {} void TaQLQueryNodeRep::show (std::ostream& os) const { if (itsBrackets) { os << '['; } showDerived (os); if (itsBrackets) { os << ']'; } } void TaQLQueryNodeRep::saveSuper (AipsIO& aio) const { aio << itsBrackets << itsNoExecute << itsFromExecute; } void TaQLQueryNodeRep::restoreSuper (AipsIO& aio) { aio >> itsBrackets >> itsNoExecute >> itsFromExecute; } TaQLSelectNodeRep::TaQLSelectNodeRep (const TaQLNode& columns, const TaQLMultiNode& with, const TaQLNode& where, const TaQLNode& groupby, const TaQLNode& having, const TaQLNode& sort, const TaQLNode& limitoff, const TaQLNode& giving, const TaQLMultiNode& dminfo) : TaQLQueryNodeRep (TaQLNode_Select), itsColumns(columns), itsWith(with), itsWhere(where), itsGroupby(groupby), itsHaving(having), itsSort(sort), itsLimitOff(limitoff), itsGiving(giving), itsDMInfo(dminfo) {} TaQLSelectNodeRep::TaQLSelectNodeRep (const TaQLNode& columns, const TaQLMultiNode& with, const TaQLMultiNode& tables, const TaQLMultiNode& joins, const TaQLNode& where, const TaQLNode& groupby, const TaQLNode& having, const TaQLNode& sort, const TaQLNode& limitoff, const TaQLNode& giving, const TaQLMultiNode& dminfo) : TaQLQueryNodeRep (TaQLNode_Select), itsColumns(columns), itsWith(with), itsTables(tables), itsJoins(joins), itsWhere(where), itsGroupby(groupby), itsHaving(having), itsSort(sort), itsLimitOff(limitoff), itsGiving(giving), itsDMInfo(dminfo) {} TaQLNodeResult TaQLSelectNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitSelectNode (*this); } void TaQLSelectNodeRep::showDerived (std::ostream& os) const { showWithTables (os, itsWith); os << "SELECT"; itsColumns.show (os); if (itsTables.isValid()) { os << " FROM "; itsTables.show (os); } itsJoins.show (os); if (itsWhere.isValid()) { os << " WHERE "; itsWhere.show (os); } if (itsGroupby.isValid()) { itsGroupby.show (os); } if (itsHaving.isValid()) { os << " HAVING "; itsHaving.show (os); } itsSort.show (os); itsLimitOff.show (os); if (itsGiving.isValid()) { os << " GIVING "; itsGiving.show (os); } if (itsDMInfo.isValid()) { os << " DMINFO "; itsDMInfo.show (os); } } void TaQLSelectNodeRep::save (AipsIO& aio) const { itsColumns.saveNode (aio); itsWith.saveNode (aio); itsTables.saveNode (aio); itsJoins.saveNode (aio); itsWhere.saveNode (aio); itsGroupby.saveNode (aio); itsHaving.saveNode (aio); itsSort.saveNode (aio); itsLimitOff.saveNode (aio); itsGiving.saveNode (aio); itsDMInfo.saveNode (aio); saveSuper (aio); } TaQLNode TaQLSelectNodeRep::restore (AipsIO& aio) { TaQLNode columns = TaQLNode::restoreNode (aio); TaQLMultiNode with = TaQLNode::restoreMultiNode (aio); TaQLMultiNode tables = TaQLNode::restoreMultiNode (aio); TaQLMultiNode joins = TaQLNode::restoreMultiNode (aio); TaQLNode where = TaQLNode::restoreNode (aio); TaQLNode groupby = TaQLNode::restoreNode (aio); TaQLNode having = TaQLNode::restoreNode (aio); TaQLNode sort = TaQLNode::restoreNode (aio); TaQLNode limitoff = TaQLNode::restoreNode (aio); TaQLNode giving = TaQLNode::restoreNode (aio); TaQLMultiNode dminfo = TaQLNode::restoreMultiNode (aio); std::unique_ptr node (new TaQLSelectNodeRep (columns, with, tables, joins, where, groupby, having, sort, limitoff, giving, dminfo)); node->restoreSuper (aio); return node.release(); } TaQLCountNodeRep::TaQLCountNodeRep (const TaQLMultiNode& with, const TaQLNode& columns, const TaQLMultiNode& tables, const TaQLNode& where) : TaQLQueryNodeRep (TaQLNode_Count), itsWith(with), itsColumns(columns), itsTables(tables), itsWhere(where) {} TaQLNodeResult TaQLCountNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitCountNode (*this); } void TaQLCountNodeRep::showDerived (std::ostream& os) const { showWithTables (os, itsWith); os << "COUNT "; itsColumns.show (os); os << " FROM "; itsTables.show (os); if (itsWhere.isValid()) { os << " WHERE "; itsWhere.show (os); } } void TaQLCountNodeRep::save (AipsIO& aio) const { itsWith.saveNode (aio); itsColumns.saveNode (aio); itsTables.saveNode (aio); itsWhere.saveNode (aio); saveSuper (aio); } TaQLNode TaQLCountNodeRep::restore (AipsIO& aio) { TaQLMultiNode with = TaQLNode::restoreMultiNode (aio); TaQLNode columns = TaQLNode::restoreNode (aio); TaQLMultiNode tables = TaQLNode::restoreMultiNode (aio); TaQLNode where = TaQLNode::restoreNode (aio); std::unique_ptr node (new TaQLCountNodeRep (with, columns, tables, where)); node->restoreSuper (aio); return node.release(); } TaQLUpdateNodeRep::TaQLUpdateNodeRep (const TaQLMultiNode& with, const TaQLMultiNode& tables, const TaQLMultiNode& update, const TaQLMultiNode& from, const TaQLNode& where, const TaQLNode& sort, const TaQLNode& limitoff) : TaQLNodeRep (TaQLNode_Update), itsWith (with), itsTables (tables), itsUpdate (update), itsFrom (from), itsWhere (where), itsSort (sort), itsLimitOff (limitoff) {} TaQLNodeResult TaQLUpdateNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitUpdateNode (*this); } void TaQLUpdateNodeRep::show (std::ostream& os) const { showWithTables (os, itsWith); os << "UPDATE "; itsTables.show (os); os << " SET "; itsUpdate.show (os); if (itsFrom.isValid()) { os << " FROM "; itsFrom.show (os); } if (itsWhere.isValid()) { os << " WHERE "; itsWhere.show (os); } itsSort.show (os); itsLimitOff.show (os); } void TaQLUpdateNodeRep::save (AipsIO& aio) const { itsWith.saveNode (aio); itsTables.saveNode (aio); itsUpdate.saveNode (aio); itsFrom.saveNode (aio); itsWhere.saveNode (aio); itsSort.saveNode (aio); itsLimitOff.saveNode (aio); } TaQLNode TaQLUpdateNodeRep::restore (AipsIO& aio) { TaQLMultiNode with = TaQLNode::restoreMultiNode (aio); TaQLMultiNode tables = TaQLNode::restoreMultiNode (aio); TaQLMultiNode update = TaQLNode::restoreMultiNode (aio); TaQLMultiNode from = TaQLNode::restoreMultiNode (aio); TaQLNode where = TaQLNode::restoreNode (aio); TaQLNode sort = TaQLNode::restoreNode (aio); TaQLNode limitoff = TaQLNode::restoreNode (aio); return new TaQLUpdateNodeRep (with, tables, update, from, where, sort, limitoff); } TaQLInsertNodeRep::TaQLInsertNodeRep (const TaQLMultiNode& with, const TaQLMultiNode& tables, const TaQLMultiNode& columns, const TaQLNode& values, const TaQLNode& limit) : TaQLNodeRep (TaQLNode_Insert), itsWith (with), itsTables (tables), itsColumns (columns), itsValues (values), itsLimit (limit) {} TaQLInsertNodeRep::TaQLInsertNodeRep (const TaQLMultiNode& with, const TaQLMultiNode& tables, const TaQLMultiNode& insert) : TaQLNodeRep (TaQLNode_Insert), itsWith (with), itsTables (tables), itsColumns (False) { // Convert the list of column=value expressions like // SET col1=val1, col2=val2 // to a list of columns and a list of values like // [col1,col2] VALUES [val1,val2]. TaQLMultiNode values(False); values.setPPFix ("VALUES [", "]"); // The nodes in the list are of type TaQLUpdExprNodeRep. const std::vector& nodes = insert.getMultiRep()->getNodes(); for (uInt i=0; i (nodes[i].getRep()); AlwaysAssert (rep, AipsError); if (rep->itsIndices1.isValid() || rep->itsIndices2.isValid()) { throw TableInvExpr ("Column indices or masks cannot be given in an " "INSERT command"); } // Add the column name and value expression. itsColumns.add (new TaQLKeyColNodeRep (rep->itsName)); values.add (rep->itsExpr); } TaQLMultiNode valuesList(False); valuesList.add (values); itsValues = valuesList; } TaQLNodeResult TaQLInsertNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitInsertNode (*this); } void TaQLInsertNodeRep::show (std::ostream& os) const { showWithTables (os, itsWith); os << "INSERT"; if (itsLimit.isValid()) { os << " LIMIT "; itsLimit.show (os); } os << " INTO "; itsTables.show (os); if (itsColumns.isValid()) { os << " ["; itsColumns.show (os); os << ']'; } os << ' '; itsValues.show (os); } void TaQLInsertNodeRep::save (AipsIO& aio) const { itsWith.saveNode (aio); itsTables.saveNode (aio); itsColumns.saveNode (aio); itsValues.saveNode (aio); itsLimit.saveNode (aio); } TaQLNode TaQLInsertNodeRep::restore (AipsIO& aio) { TaQLMultiNode with = TaQLNode::restoreMultiNode (aio); TaQLMultiNode tables = TaQLNode::restoreMultiNode (aio); TaQLMultiNode columns = TaQLNode::restoreMultiNode (aio); TaQLNode values = TaQLNode::restoreNode (aio); TaQLNode limit = TaQLNode::restoreNode (aio); return new TaQLInsertNodeRep (with, tables, columns, values, limit); } TaQLDeleteNodeRep::TaQLDeleteNodeRep (const TaQLMultiNode& with, const TaQLMultiNode& tables, const TaQLNode& where, const TaQLNode& sort, const TaQLNode& limitoff) : TaQLNodeRep (TaQLNode_Delete), itsWith (with), itsTables (tables), itsWhere (where), itsSort (sort), itsLimitOff (limitoff) {} TaQLNodeResult TaQLDeleteNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitDeleteNode (*this); } void TaQLDeleteNodeRep::show (std::ostream& os) const { showWithTables (os, itsWith); os << "DELETE FROM "; itsTables.show (os); if (itsWhere.isValid()) { os << " WHERE "; itsWhere.show (os); } itsSort.show (os); itsLimitOff.show (os); } void TaQLDeleteNodeRep::save (AipsIO& aio) const { itsWith.saveNode (aio); itsTables.saveNode (aio); itsWhere.saveNode (aio); itsSort.saveNode (aio); itsLimitOff.saveNode (aio); } TaQLNode TaQLDeleteNodeRep::restore (AipsIO& aio) { TaQLMultiNode with = TaQLNode::restoreMultiNode (aio); TaQLMultiNode tables = TaQLNode::restoreMultiNode (aio); TaQLNode where = TaQLNode::restoreNode (aio); TaQLNode sort = TaQLNode::restoreNode (aio); TaQLNode limitoff = TaQLNode::restoreNode (aio); return new TaQLDeleteNodeRep (with, tables, where, sort, limitoff); } TaQLCalcNodeRep::TaQLCalcNodeRep (const TaQLMultiNode& with, const TaQLMultiNode& tables, const TaQLNode& expr, const TaQLNode& where, const TaQLNode& sort, const TaQLNode& limitoff) : TaQLNodeRep (TaQLNode_Calc), itsWith (with), itsTables (tables), itsExpr (expr), itsWhere (where), itsSort (sort), itsLimitOff (limitoff) {} TaQLNodeResult TaQLCalcNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitCalcNode (*this); } void TaQLCalcNodeRep::show (std::ostream& os) const { showWithTables (os, itsWith); os << "CALC "; itsExpr.show (os); if (itsTables.isValid()) { os << " FROM "; itsTables.show (os); } if (itsWhere.isValid()) { os << " WHERE "; itsWhere.show (os); } itsSort.show (os); itsLimitOff.show (os); } void TaQLCalcNodeRep::save (AipsIO& aio) const { itsWith.saveNode (aio); itsTables.saveNode (aio); itsExpr.saveNode (aio); itsWhere.saveNode (aio); itsSort.saveNode (aio); itsLimitOff.saveNode (aio); } TaQLNode TaQLCalcNodeRep::restore (AipsIO& aio) { TaQLMultiNode with = TaQLNode::restoreMultiNode (aio); TaQLMultiNode tables = TaQLNode::restoreMultiNode (aio); TaQLNode expr = TaQLNode::restoreNode (aio); TaQLNode where = TaQLNode::restoreNode (aio); TaQLNode sort = TaQLNode::restoreNode (aio); TaQLNode limitoff = TaQLNode::restoreNode (aio); return new TaQLCalcNodeRep (with, tables, expr, where, sort, limitoff); } TaQLCreTabNodeRep::TaQLCreTabNodeRep (const TaQLMultiNode& with, const TaQLNode& giving, const TaQLMultiNode& likeDrop, const TaQLMultiNode& cols, const TaQLNode& limit, const TaQLMultiNode& dminfo) : TaQLQueryNodeRep (TaQLNode_CreTab), itsWith (with), itsGiving (giving), itsLikeDrop(likeDrop), itsColumns (cols), itsLimit (limit), itsDMInfo (dminfo) {} TaQLNodeResult TaQLCreTabNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitCreTabNode (*this); } void TaQLCreTabNodeRep::showDerived (std::ostream& os) const { showWithTables (os, itsWith); os << "CREATE TABLE "; itsGiving.show (os); if (itsLikeDrop.isValid()) { os << " LIKE "; const std::vector& nodes = itsLikeDrop.getMultiRep()->getNodes(); nodes[0].show (os); if (nodes.size() == 2) { os << " DROP COLUMN "; nodes[1].show (os); } } // If a column-list is given, it must be preceded by ADD COLUMN if // the LIKE clause is given as well. if (itsColumns.isValid() && !itsColumns.getMultiRep()->getNodes().empty()) { if (itsLikeDrop.isValid()) { os << " ADD COLUMN "; } else { os << ' '; } itsColumns.show (os); } if (itsLimit.isValid()) { os << " LIMIT "; itsLimit.show (os); } if (itsDMInfo.isValid()) { os << " DMINFO "; itsDMInfo.show (os); } } void TaQLCreTabNodeRep::save (AipsIO& aio) const { itsWith.saveNode (aio); itsGiving.saveNode (aio); itsLikeDrop.saveNode (aio); itsColumns.saveNode (aio); itsLimit.saveNode (aio); itsDMInfo.saveNode (aio); saveSuper (aio); } TaQLNode TaQLCreTabNodeRep::restore (AipsIO& aio) { TaQLMultiNode with = TaQLNode::restoreMultiNode (aio); TaQLNode giving = TaQLNode::restoreNode (aio); TaQLMultiNode likeDrop = TaQLNode::restoreMultiNode (aio); TaQLMultiNode columns = TaQLNode::restoreMultiNode (aio); TaQLNode limit = TaQLNode::restoreNode (aio); TaQLMultiNode dminfo = TaQLNode::restoreMultiNode (aio); std::unique_ptr node (new TaQLCreTabNodeRep (with, giving, likeDrop, columns, limit, dminfo)); node->restoreSuper (aio); return node.release(); } TaQLColSpecNodeRep::TaQLColSpecNodeRep (const String& name, const String& likeCol, const String& dtype, const TaQLMultiNode& spec) : TaQLNodeRep (TaQLNode_ColSpec), itsName (name), itsLikeCol (likeCol), itsDtype (checkDataType(dtype)), itsSpec (spec) {} TaQLNodeResult TaQLColSpecNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitColSpecNode (*this); } void TaQLColSpecNodeRep::show (std::ostream& os) const { os << itsName; if (! itsLikeCol.empty()) { os << " LIKE " << itsLikeCol; } if (! itsDtype.empty()) { os << ' ' << itsDtype; } if (itsSpec.isValid()) { os << ' '; itsSpec.show (os); } } void TaQLColSpecNodeRep::save (AipsIO& aio) const { aio << itsName << itsLikeCol << itsDtype; itsSpec.saveNode (aio); } TaQLNode TaQLColSpecNodeRep::restore (AipsIO& aio) { String name, likeCol, dtype; aio >> name >> likeCol >> dtype; TaQLMultiNode spec = TaQLNode::restoreMultiNode (aio); return new TaQLColSpecNodeRep (name, likeCol, dtype, spec); } TaQLRecFldNodeRep::TaQLRecFldNodeRep (const String& name, const TaQLNode& values, const String& dtype) : TaQLNodeRep (TaQLNode_RecFld), itsName (name), itsDtype (checkDataType(dtype)), itsValues(values) {} TaQLRecFldNodeRep::TaQLRecFldNodeRep (const String& name, const TaQLRecFldNodeRep& node) : TaQLNodeRep (TaQLNode_RecFld), itsName (name), itsDtype (node.itsDtype), itsValues(node.itsValues) {} TaQLRecFldNodeRep::TaQLRecFldNodeRep (const String& name, const String& fromName, const String& dtype) : TaQLNodeRep (TaQLNode_RecFld), itsName (name), itsFromName (fromName), itsDtype (checkDataType(dtype)) {} TaQLNodeResult TaQLRecFldNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitRecFldNode (*this); } void TaQLRecFldNodeRep::show (std::ostream& os) const { if (! itsName.empty()) { os << itsName << '='; } if (! itsFromName.empty()) { os << itsFromName; } else if (itsValues.isValid()) { if (itsValues.nodeType() == TaQLNode_Multi && dynamic_cast(*(itsValues.getRep())).itsNodes.empty()) { os << "[=]"; } else { itsValues.show (os); } } else { os << "[]"; } if (! itsDtype.empty()) { os << " AS " << itsDtype; } } void TaQLRecFldNodeRep::save (AipsIO& aio) const { aio << itsName << itsFromName << itsDtype; itsValues.saveNode (aio); } TaQLNode TaQLRecFldNodeRep::restore (AipsIO& aio) { String name, fromName, dtype; aio >> name >> fromName >> dtype; TaQLNode values = TaQLNode::restoreNode (aio); if (fromName.empty()) { return new TaQLRecFldNodeRep (name, values, dtype); } else { return new TaQLRecFldNodeRep (name, fromName, dtype); } } TaQLUnitNodeRep::TaQLUnitNodeRep (const String& unit, const TaQLNode& child) : TaQLNodeRep (TaQLNode_Unit), itsUnit (unit), itsChild (child) {} TaQLNodeResult TaQLUnitNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitUnitNode (*this); } void TaQLUnitNodeRep::show (std::ostream& os) const { os << '('; itsChild.show(os); os << ")'" << itsUnit << "'"; } void TaQLUnitNodeRep::save (AipsIO& aio) const { aio << itsUnit; itsChild.saveNode (aio); } TaQLNode TaQLUnitNodeRep::restore (AipsIO& aio) { String unit; aio >> unit; TaQLNode node = TaQLNode::restoreNode (aio); return new TaQLUnitNodeRep (unit, node); } TaQLAltTabNodeRep::TaQLAltTabNodeRep (const TaQLMultiNode& with, const TaQLNode& table, const TaQLMultiNode& from, const TaQLMultiNode& commands) : TaQLQueryNodeRep (TaQLNode_AltTab), itsWith (with), itsTable (table), itsFrom (from), itsCommands (commands) {} TaQLNodeResult TaQLAltTabNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitAltTabNode (*this); } void TaQLAltTabNodeRep::showDerived (std::ostream& os) const { showWithTables (os, itsWith); os << "ALTER TABLE "; itsTable.show (os); if (itsFrom.isValid()) { os << " FROM "; itsFrom.show (os); } os << ' '; itsCommands.show (os); } void TaQLAltTabNodeRep::save (AipsIO& aio) const { itsWith.saveNode (aio); itsTable.saveNode (aio); itsFrom.saveNode (aio); itsCommands.saveNode (aio); } TaQLNode TaQLAltTabNodeRep::restore (AipsIO& aio) { TaQLMultiNode with = TaQLNode::restoreMultiNode (aio); TaQLNode table = TaQLNode::restoreNode (aio); TaQLMultiNode from = TaQLNode::restoreMultiNode (aio); TaQLMultiNode commands = TaQLNode::restoreMultiNode (aio); return new TaQLAltTabNodeRep (with, table, from, commands); } TaQLAddColNodeRep::TaQLAddColNodeRep (const TaQLMultiNode& cols, const TaQLMultiNode& dminfo) : TaQLNodeRep (TaQLNode_AddCol), itsColumns (cols), itsDMInfo (dminfo) {} TaQLNodeResult TaQLAddColNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitAddColNode (*this); } void TaQLAddColNodeRep::show (std::ostream& os) const { os << "ADD COLUMN "; itsColumns.show (os); if (itsDMInfo.isValid()) { os << " DMINFO "; itsDMInfo.show (os); } } void TaQLAddColNodeRep::save (AipsIO& aio) const { itsColumns.saveNode(aio); itsDMInfo.saveNode(aio); } TaQLNode TaQLAddColNodeRep::restore (AipsIO& aio) { TaQLMultiNode cols = TaQLNode::restoreMultiNode (aio); TaQLMultiNode dminfo = TaQLNode::restoreMultiNode (aio); return new TaQLAddColNodeRep (cols, dminfo); } TaQLRenDropNodeRep::TaQLRenDropNodeRep (Int type, const TaQLMultiNode& names) : TaQLNodeRep (TaQLNode_RenDrop), itsType (type), itsNames (names) {} TaQLNodeResult TaQLRenDropNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitRenDropNode (*this); } void TaQLRenDropNodeRep::show (std::ostream& os) const { if (itsType == 0) { os << "RENAME COLUMN "; } else if (itsType == 1) { os << "DROP COLUMN "; } else if (itsType == 2) { os << "RENAME KEYWORD "; } else { os << "DROP KEYWORD "; } itsNames.show (os); } void TaQLRenDropNodeRep::save (AipsIO& aio) const { aio << itsType; itsNames.saveNode (aio); } TaQLNode TaQLRenDropNodeRep::restore (AipsIO& aio) { Int type; aio >> type; TaQLMultiNode names = TaQLNode::restoreMultiNode (aio); return new TaQLRenDropNodeRep (type, names); } TaQLSetKeyNodeRep::TaQLSetKeyNodeRep (const TaQLMultiNode& keyvals) : TaQLNodeRep (TaQLNode_SetKey), itsKeyVals (keyvals) {} TaQLNodeResult TaQLSetKeyNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitSetKeyNode (*this); } void TaQLSetKeyNodeRep::show (std::ostream& os) const { os << "SET KEYWORD "; itsKeyVals.show (os); } void TaQLSetKeyNodeRep::save (AipsIO& aio) const { itsKeyVals.saveNode (aio); } TaQLNode TaQLSetKeyNodeRep::restore (AipsIO& aio) { TaQLMultiNode keyvals = TaQLNode::restoreMultiNode (aio); return new TaQLSetKeyNodeRep (keyvals); } TaQLAddRowNodeRep::TaQLAddRowNodeRep (const TaQLNode& nrow) : TaQLNodeRep (TaQLNode_AddRow), itsNRow(nrow) {} TaQLNodeResult TaQLAddRowNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitAddRowNode (*this); } void TaQLAddRowNodeRep::show (std::ostream& os) const { os << "ADD ROW "; itsNRow.show (os); } void TaQLAddRowNodeRep::save (AipsIO& aio) const { itsNRow.saveNode (aio); } TaQLNode TaQLAddRowNodeRep::restore (AipsIO& aio) { TaQLNode nrow = TaQLNode::restoreNode (aio); return new TaQLAddRowNodeRep (nrow); } TaQLConcTabNodeRep::TaQLConcTabNodeRep (const String& tableName, const TaQLMultiNode& tables, const TaQLMultiNode& subtableNames) : TaQLQueryNodeRep (TaQLNode_ConcTab), itsTableName (tableName), itsTables (tables), itsSubTables (subtableNames) {} TaQLNodeResult TaQLConcTabNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitConcTabNode (*this); } void TaQLConcTabNodeRep::showDerived (std::ostream& os) const { os << "["; itsTables.show (os); if (itsSubTables.isValid()) { os << " SUBTABLES "; itsSubTables.show (os); } if (! itsTableName.empty()) { os << " GIVING " << addEscape(itsTableName); } os << ']'; } void TaQLConcTabNodeRep::save (AipsIO& aio) const { aio << itsTableName; itsTables.saveNode (aio); itsSubTables.saveNode (aio); } TaQLNode TaQLConcTabNodeRep::restore (AipsIO& aio) { String tableName; aio >> tableName; TaQLMultiNode tables = TaQLNode::restoreMultiNode (aio); TaQLMultiNode subtables = TaQLNode::restoreMultiNode (aio); return new TaQLConcTabNodeRep (tableName, tables, subtables); } TaQLShowNodeRep::TaQLShowNodeRep (const TaQLMultiNode& names) : TaQLNodeRep (TaQLNode_Show), itsNames (names) {} TaQLNodeResult TaQLShowNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitShowNode (*this); } void TaQLShowNodeRep::show (std::ostream& os) const { os << "SHOW "; if (itsNames.isValid()) { itsNames.show (os); } } void TaQLShowNodeRep::save (AipsIO& aio) const { itsNames.saveNode (aio); } TaQLNode TaQLShowNodeRep::restore (AipsIO& aio) { TaQLMultiNode names = TaQLNode::restoreMultiNode (aio); return new TaQLShowNodeRep (names); } TaQLCopyColNodeRep::TaQLCopyColNodeRep (const TaQLMultiNode& names, const TaQLMultiNode& dminfo) : TaQLNodeRep (TaQLNode_CopyCol), itsNames (names), itsDMInfo (dminfo) {} TaQLNodeResult TaQLCopyColNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitCopyColNode (*this); } void TaQLCopyColNodeRep::show (std::ostream& os) const { os << "COPY COLUMN "; itsNames.show (os); if (itsDMInfo.isValid()) { os << " DMINFO "; itsDMInfo.show (os); } } void TaQLCopyColNodeRep::save (AipsIO& aio) const { itsNames.saveNode(aio); itsDMInfo.saveNode(aio); } TaQLNode TaQLCopyColNodeRep::restore (AipsIO& aio) { TaQLMultiNode names = TaQLNode::restoreMultiNode (aio); TaQLMultiNode dminfo = TaQLNode::restoreMultiNode (aio); return new TaQLCopyColNodeRep (names, dminfo); } TaQLDropTabNodeRep::TaQLDropTabNodeRep (const TaQLMultiNode& with, const TaQLMultiNode& tables) : TaQLNodeRep (TaQLNode_DropTab), itsWith (with), itsTables (tables) {} TaQLNodeResult TaQLDropTabNodeRep::visit (TaQLNodeVisitor& visitor) const { return visitor.visitDropTabNode (*this); } void TaQLDropTabNodeRep::show (std::ostream& os) const { showWithTables (os, itsWith); os << "DROP TABLE "; itsTables.show (os); } void TaQLDropTabNodeRep::save (AipsIO& aio) const { itsWith.saveNode(aio); itsTables.saveNode(aio); } TaQLNode TaQLDropTabNodeRep::restore (AipsIO& aio) { TaQLMultiNode with = TaQLNode::restoreMultiNode (aio); TaQLMultiNode tables = TaQLNode::restoreMultiNode (aio); return new TaQLDropTabNodeRep (with, tables); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/TaQLNodeDer.h000066400000000000000000001260731476623553700176230ustar00rootroot00000000000000//# TaQLNodeDer.h: Specialized nodes in the raw TaQL parse tree //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TAQLNODEDER_H #define TABLES_TAQLNODEDER_H //# Includes #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Raw TaQL parse tree node defining a constant value. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding a constant expression or a table name. // The types supported are Bool, Int, Double, DComplex, String, and MVTime. // Note that a keyword or column name is represented by TaQLKeyColNodeRep. // class TaQLConstNodeRep: public TaQLNodeRep { public: // Do not change the values of this enum, as objects might be persistent. enum Type {CTBool =0, CTInt =1, CTReal =2, CTComplex=3, CTString =4, CTTime =5}; explicit TaQLConstNodeRep (Bool value); explicit TaQLConstNodeRep (Int64 value); explicit TaQLConstNodeRep (Double value); explicit TaQLConstNodeRep (Double value, const String& unit); explicit TaQLConstNodeRep (DComplex value); explicit TaQLConstNodeRep (const String& value, Bool isTableName=False); explicit TaQLConstNodeRep (const MVTime& value); explicit TaQLConstNodeRep (Int64 value, const String& subTableName); void setIsTableName() { itsIsTableName = True; } const String& getString() const; const String& getUnit() const { return itsUnit; } virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void show (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); Type itsType; Bool itsIsTableName; Bool itsBValue; Int64 itsIValue; Double itsRValue; DComplex itsCValue; String itsSValue; MVTime itsTValue; String itsUnit; }; // // Raw TaQL parse tree node defining a constant regex value. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding a constant regex/pattern value. // Part of the regex are the delimiters (like p//). // It also holds if the regex is case-insensitive and if a match or no match // operator is given. // class TaQLRegexNodeRep: public TaQLNodeRep { public: explicit TaQLRegexNodeRep (const String& value); TaQLRegexNodeRep (const String& value, Bool caseInsensitive, Bool negate, Bool ignoreBlanks, Int maxDistance); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void show (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); String itsValue; Bool itsCaseInsensitive; Bool itsNegate; //# True means !~ //# The following members are only used for distance. Bool itsIgnoreBlanks; Int itsMaxDistance; }; // // Raw TaQL parse tree node defining a unary operator. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding a unary operator and operand. // The operators supported are -, ~, NOT, EXISTS, and NOT EXISTS. // Note the unary operator + is superfluous and is ignored by the parser. // class TaQLUnaryNodeRep: public TaQLNodeRep { public: // Do not change the values of this enum, as objects might be persistent. enum Type {U_MINUS =0, U_NOT =1, U_EXISTS =2, U_NOTEXISTS=3, U_BITNOT =4}; TaQLUnaryNodeRep (Type type, const TaQLNode& child); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void show (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); Type itsType; TaQLNode itsChild; }; // // Raw TaQL parse tree node defining a binary operator. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding a binary operator and operands. // All standard mathematical (including % and ^), relational, bit, and logical // operators are supported. Furthermore operator IN and the INDEX operator // (for indexing in an array) are supported. // class TaQLBinaryNodeRep: public TaQLNodeRep { public: // Do not change the values of this enum, as objects might be persistent. enum Type {B_PLUS =0, B_MINUS =1, B_TIMES =2, B_DIVIDE=3, B_MODULO=4, B_POWER =5, B_EQ =6, B_NE =7, B_GT =8, B_GE =9, B_LT =10, B_LE =11, B_OR =12, B_AND =13, B_IN =14, B_INDEX =15, B_DIVIDETRUNC=16, B_EQREGEX =17, B_NEREGEX =18, B_BITAND =19, B_BITXOR =20, B_BITOR =21}; TaQLBinaryNodeRep (Type type, const TaQLNode& left, const TaQLNode& right); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void show (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); // Handle a comparison wih a regex. The operator (~ or !~) is extracted // from the regex. static TaQLBinaryNodeRep* handleRegex (const TaQLNode& left, const TaQLRegexNode& regex); Type itsType; TaQLNode itsLeft; TaQLNode itsRight; }; // // Raw TaQL parse tree node defining a list of nodes. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding a list of heterogeneous nodes. // class TaQLMultiNodeRep: public TaQLNodeRep { public: explicit TaQLMultiNodeRep (Bool isSetOrArray=False); TaQLMultiNodeRep(const String& prefix, const String& postfix, Bool isSetOrArray=False); void setIsSetOrArray() { itsIsSetOrArray = True; } void setPPFix (const String& prefix, const String& postfix) { itsPrefix = prefix; itsPostfix = postfix; } void setSeparator (const String& sep) { itsSep = sep; } void setSeparator (uInt incr, const String& sep) { itsIncr = incr; itsSep2 = sep; } void add (const TaQLNode& node) { itsNodes.push_back (node); } const std::vector& getNodes() const { return itsNodes; } virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void show (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLMultiNode restore (AipsIO& aio); std::vector itsNodes; Bool itsIsSetOrArray; String itsPrefix; String itsPostfix; String itsSep; String itsSep2; uInt itsIncr; }; // // Raw TaQL parse tree node defining a function. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding a function name and its arguments. // class TaQLFuncNodeRep: public TaQLNodeRep { public: TaQLFuncNodeRep (const String& name); TaQLFuncNodeRep (const String& name, const TaQLMultiNode& args); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void show (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); String itsName; TaQLMultiNode itsArgs; }; // // Raw TaQL parse tree node defining a range. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding the optional start and end values // of a range (i.e. an interval) and flags if the range is open or closed. // class TaQLRangeNodeRep: public TaQLNodeRep { public: TaQLRangeNodeRep (Bool leftClosed, const TaQLNode& start, const TaQLNode& end, Bool rightClosed, Bool asMidWidth=False); TaQLRangeNodeRep (Bool leftClosed, const TaQLNode& start); TaQLRangeNodeRep (const TaQLNode& end, Bool rightClosed); TaQLRangeNodeRep (const TaQLNode& mid, const TaQLNode& width); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void show (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); TaQLNode itsStart; TaQLNode itsEnd; Bool itsLeftClosed; Bool itsRightClosed; Bool itsAsMidWidth; }; // // Raw TaQL parse tree node defining an index in a array. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding the optional start, end, and incr // values of an index in an array. // class TaQLIndexNodeRep: public TaQLNodeRep { public: TaQLIndexNodeRep (const TaQLNode& start, const TaQLNode& end, const TaQLNode& incr); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void show (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); TaQLNode itsStart; TaQLNode itsEnd; TaQLNode itsIncr; }; // // Raw TaQL parse tree node defining a join operation. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding the expressions of a join operation. // This is, however, a placeholder and not implemented yet. // class TaQLJoinNodeRep: public TaQLNodeRep { public: TaQLJoinNodeRep (const TaQLMultiNode& tables, const TaQLNode& condition); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void show (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); TaQLMultiNode itsTables; TaQLNode itsCondition; }; // // Raw TaQL parse tree node defining a keyword or column name. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding the name of a keyword or column. // The name can contain . and :: delimiters for scoping. // class TaQLKeyColNodeRep: public TaQLNodeRep { public: TaQLKeyColNodeRep (const String& name, const String& nameMask = String()); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void show (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); String itsName; String itsNameMask; }; // // Raw TaQL parse tree node defining a table. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding the info defining a table. // It can be a constant value holding a name or it can be a subquery. // Furthermore the alias of the table is defined (which can be empty). // class TaQLTableNodeRep: public TaQLNodeRep { public: TaQLTableNodeRep (const TaQLNode& table, const String& alias); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void show (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); TaQLNode itsTable; String itsAlias; }; // // Raw TaQL parse tree node defining a select column expression. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding a column expression in the // column list of the select clause. // A new column name and data type can be defined for the column (expression). // The expression can be a wildcarded column name (a regex) preceeded by // ~ or !~ (meaning include or exclude). // class TaQLColNodeRep: public TaQLNodeRep { public: TaQLColNodeRep (const TaQLNode& expr, const String& name, const String& nameMask, const String& dtype); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void show (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); TaQLNode itsExpr; String itsName; String itsNameMask; String itsDtype; }; // // Raw TaQL parse tree node defining a select column list. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding a select column list. // It also defines if the result must be distinct (unique) // class TaQLColumnsNodeRep: public TaQLNodeRep { public: TaQLColumnsNodeRep (Bool distinct, const TaQLMultiNode& nodes); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void show (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); Bool itsDistinct; TaQLMultiNode itsNodes; }; // // Raw TaQL parse tree node defining a groupby list. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding a groupby list with the optional // ROLLUP qualifier. // class TaQLGroupNodeRep: public TaQLNodeRep { public: // Do not change the values of this enum, as objects might be persistent. enum Type {Normal=0, Rollup=1}; //# in the future type Cube could be added TaQLGroupNodeRep (Type type, const TaQLMultiNode& nodes); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void show (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); Type itsType; TaQLMultiNode itsNodes; }; // // Raw TaQL parse tree node defining a sort key. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding a sort key and the optional order // in which this key must be sorted. // class TaQLSortKeyNodeRep: public TaQLNodeRep { public: // Do not change the values of this enum, as objects might be persistent. enum Type {Ascending =0, Descending=1, None =2}; TaQLSortKeyNodeRep (Type type, const TaQLNode& child); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void show (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); Type itsType; TaQLNode itsChild; }; // // Raw TaQL parse tree node defining a sort list. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding a sort list and the default order // for each individual sort key. // class TaQLSortNodeRep: public TaQLNodeRep { public: // Do not change the values of this enum, as objects might be persistent. enum Type {Ascending =0, Descending=1}; TaQLSortNodeRep (Bool unique, Type type, const TaQLMultiNode& keys); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void show (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); Bool itsUnique; Type itsType; TaQLMultiNode itsKeys; }; // // Raw TaQL parse tree node defining a limit/offset expression. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding the optional expressions for the // LIMIT and OFFSET clause. // class TaQLLimitOffNodeRep: public TaQLNodeRep { public: TaQLLimitOffNodeRep (const TaQLNode& limit, const TaQLNode& offset); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void show (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); TaQLNode itsLimit; TaQLNode itsOffset; }; // // Raw TaQL parse tree node defining a giving expression list. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding the values for a GIVING clause. // The value can be a table name or a list of expressions. // class TaQLGivingNodeRep: public TaQLNodeRep { public: explicit TaQLGivingNodeRep (const String& name, const TaQLMultiNode& type); explicit TaQLGivingNodeRep (const TaQLMultiNode& exprlist); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void show (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); String itsName; TaQLMultiNode itsType; TaQLMultiNode itsExprList; }; // // Raw TaQL parse tree node defining a column update expression. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding the values for an update expression. // It defines the column name and the expression for the new value. // Optionally an index can be defined in case the column contains array // values for which only some values need to be updated. // class TaQLUpdExprNodeRep: public TaQLNodeRep { public: TaQLUpdExprNodeRep (const String& name, const String& nameMask, const TaQLNode& expr); TaQLUpdExprNodeRep (const String& name, const String& nameMask, const TaQLMultiNode& indices, const TaQLNode& expr); TaQLUpdExprNodeRep (const String& name, const String& nameMask, const TaQLMultiNode& indices1, const TaQLMultiNode& indices2, const TaQLNode& expr); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void show (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); String itsName; String itsNameMask; TaQLMultiNode itsIndices1; //# indices or mask TaQLMultiNode itsIndices2; //# mask or indices TaQLNode itsExpr; }; // // Raw TaQL parse tree node defining a selection command. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is an abstract TaQLNodeRep for a selection command that can // also be used as a subquery. // It holds flags telling if and how the select command must be // executed when the node is visited for TaQLNodeHandler. // class TaQLQueryNodeRep: public TaQLNodeRep { public: TaQLQueryNodeRep (int nodeType); void setBrackets() { itsBrackets = True; } void setNoExecute() { itsNoExecute = True; } void setFromExecute() { itsFromExecute = True; } Bool getBrackets() const { return itsBrackets; } Bool getNoExecute() const { return itsNoExecute; } Bool getFromExecute() const { return itsFromExecute; } virtual void show (std::ostream& os) const override; protected: void saveSuper (AipsIO& aio) const; void restoreSuper (AipsIO& aio); private: virtual void showDerived (std::ostream& os) const = 0; Bool itsBrackets; Bool itsNoExecute; //# no execute in EXISTS operator Bool itsFromExecute; //# special execute in FROM }; // // Raw TaQL parse tree node defining a select command. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding the different parts of a // select expression. // It also holds flags telling if and how the select command must be // executed when the node is visited for TaQLNodeHandler. // class TaQLSelectNodeRep: public TaQLQueryNodeRep { public: TaQLSelectNodeRep (const TaQLNode& columns, const TaQLMultiNode& withTables, const TaQLNode& where, const TaQLNode& groupby, const TaQLNode& having, const TaQLNode& sort, const TaQLNode& limitoff, const TaQLNode& giving, const TaQLMultiNode& dminfo); TaQLSelectNodeRep (const TaQLNode& columns, const TaQLMultiNode& withTables, const TaQLMultiNode& fromTables, const TaQLMultiNode& joins, const TaQLNode& where, const TaQLNode& groupby, const TaQLNode& having, const TaQLNode& sort, const TaQLNode& limitoff, const TaQLNode& giving, const TaQLMultiNode& dminfo); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void showDerived (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); TaQLNode itsColumns; TaQLMultiNode itsWith; TaQLMultiNode itsTables; TaQLMultiNode itsJoins; TaQLNode itsWhere; TaQLNode itsGroupby; TaQLNode itsHaving; TaQLNode itsSort; TaQLNode itsLimitOff; TaQLNode itsGiving; TaQLMultiNode itsDMInfo; }; // // Raw TaQL parse tree node defining a count command. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding the parts for a count command. // class TaQLCountNodeRep: public TaQLQueryNodeRep { public: TaQLCountNodeRep (const TaQLMultiNode& with, const TaQLNode& columns, const TaQLMultiNode& tables, const TaQLNode& where); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void showDerived (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); TaQLMultiNode itsWith; TaQLNode itsColumns; TaQLMultiNode itsTables; TaQLNode itsWhere; }; // // Raw TaQL parse tree node defining an update command. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding the parts for an update command. // The tables to be used can be defined in two parts: the main one in // the UPDATE clause, possible other ones in the FROM command. // class TaQLUpdateNodeRep: public TaQLNodeRep { public: TaQLUpdateNodeRep (const TaQLMultiNode& with, const TaQLMultiNode& tables, const TaQLMultiNode& update, const TaQLMultiNode& from, const TaQLNode& where, const TaQLNode& sort, const TaQLNode& limitoff); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void show (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); TaQLMultiNode itsWith; TaQLMultiNode itsTables; TaQLMultiNode itsUpdate; TaQLMultiNode itsFrom; TaQLNode itsWhere; TaQLNode itsSort; TaQLNode itsLimitOff; }; // // Raw TaQL parse tree node defining an insert command. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding the parts for an insert command. // The values cvan be a list of expressions or a subquery. // class TaQLInsertNodeRep: public TaQLNodeRep { public: TaQLInsertNodeRep (const TaQLMultiNode& with, const TaQLMultiNode& tables, const TaQLMultiNode& columns, const TaQLNode& values, const TaQLNode& limit); TaQLInsertNodeRep (const TaQLMultiNode& with, const TaQLMultiNode& tables, const TaQLMultiNode& insert); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void show (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); TaQLMultiNode itsWith; TaQLMultiNode itsTables; TaQLMultiNode itsColumns; TaQLNode itsValues; TaQLNode itsLimit; }; // // Raw TaQL parse tree node defining a delete command. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding the parts for a delete command. // class TaQLDeleteNodeRep: public TaQLNodeRep { public: TaQLDeleteNodeRep (const TaQLMultiNode& with, const TaQLMultiNode& tables, const TaQLNode& where, const TaQLNode& sort, const TaQLNode& limitoff); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void show (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); TaQLMultiNode itsWith; TaQLMultiNode itsTables; TaQLNode itsWhere; TaQLNode itsSort; TaQLNode itsLimitOff; }; // // Raw TaQL parse tree node defining a calc command. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding the parts of the calc command. // class TaQLCalcNodeRep: public TaQLNodeRep { public: TaQLCalcNodeRep (const TaQLMultiNode& withTables, const TaQLMultiNode& fromTables, const TaQLNode& expr, const TaQLNode& where, const TaQLNode& sort, const TaQLNode& limitoff); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void show (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); TaQLMultiNode itsWith; TaQLMultiNode itsTables; TaQLNode itsExpr; TaQLNode itsWhere; TaQLNode itsSort; TaQLNode itsLimitOff; }; // // Raw TaQL parse tree node defining a create table command. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding the parts of the create table command. // class TaQLCreTabNodeRep: public TaQLQueryNodeRep { public: TaQLCreTabNodeRep (const TaQLMultiNode& with, const TaQLNode& giving, const TaQLMultiNode& likeDrop, const TaQLMultiNode& cols, const TaQLNode& limit, const TaQLMultiNode& dminfo); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void showDerived (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); TaQLMultiNode itsWith; TaQLNode itsGiving; TaQLMultiNode itsLikeDrop; TaQLMultiNode itsColumns; TaQLNode itsLimit; TaQLMultiNode itsDMInfo; }; // // Raw TaQL parse tree node defining a create column specification. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding the parts of a column specification // in the create table command. // class TaQLColSpecNodeRep: public TaQLNodeRep { public: TaQLColSpecNodeRep (const String& name, const String& likeCol, const String& dtype, const TaQLMultiNode& spec); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void show (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); String itsName; String itsLikeCol; String itsDtype; TaQLMultiNode itsSpec; }; // // Raw TaQL parse tree node defining a record field. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding the parts of a record field. // class TaQLRecFldNodeRep: public TaQLNodeRep { public: TaQLRecFldNodeRep (const String& name, const TaQLNode& values, const String& dtype); TaQLRecFldNodeRep (const String& name, const TaQLRecFldNodeRep&); TaQLRecFldNodeRep (const String& name, const String& fromName, const String& dtype); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void show (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); String itsName; String itsFromName; String itsDtype; TaQLNode itsValues; }; // // Raw TaQL parse tree node defining a unit. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding the parts of a record field. // class TaQLUnitNodeRep: public TaQLNodeRep { public: TaQLUnitNodeRep (const String& unit, const TaQLNode& child); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void show (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); String itsUnit; TaQLNode itsChild; }; // // Raw TaQL parse tree node defining an alter table command. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding the parts of the alter table command. // class TaQLAltTabNodeRep: public TaQLQueryNodeRep { public: TaQLAltTabNodeRep (const TaQLMultiNode& with, const TaQLNode& table, const TaQLMultiNode& from, const TaQLMultiNode& commands); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void showDerived (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); TaQLMultiNode itsWith; TaQLNode itsTable; TaQLMultiNode itsFrom; TaQLMultiNode itsCommands; }; // // Raw TaQL parse tree node defining an alter table add column command. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding the parts of the add column subcommand. // class TaQLAddColNodeRep: public TaQLNodeRep { public: TaQLAddColNodeRep (const TaQLMultiNode& cols, const TaQLMultiNode& dminfo); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void show (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); TaQLMultiNode itsColumns; TaQLMultiNode itsDMInfo; }; // // Raw TaQL parse tree node defining an alter table rename or drop command. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding the parts of the rename or drop subcommand. // class TaQLRenDropNodeRep: public TaQLNodeRep { public: TaQLRenDropNodeRep (Int type, const TaQLMultiNode& cols); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void show (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); Int itsType; TaQLMultiNode itsNames; }; // // Raw TaQL parse tree node defining an alter table set keyword command. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding the parts of the set keyword subcommand. // class TaQLSetKeyNodeRep: public TaQLNodeRep { public: TaQLSetKeyNodeRep (const TaQLMultiNode& keyvals); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void show (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); TaQLMultiNode itsKeyVals; }; // // Raw TaQL parse tree node defining an alter table add rows command. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding the parts of the add rows subcommand. // class TaQLAddRowNodeRep: public TaQLNodeRep { public: TaQLAddRowNodeRep (const TaQLNode& nrow); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void show (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); TaQLNode itsNRow; }; // // Raw TaQL parse tree node defining an alter table command. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding the parts of the alter table command. // class TaQLConcTabNodeRep: public TaQLQueryNodeRep { public: TaQLConcTabNodeRep (const String& tableName, const TaQLMultiNode& tables, const TaQLMultiNode& subtableNames); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void showDerived (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); String itsTableName; TaQLMultiNode itsTables; TaQLMultiNode itsSubTables; }; // // Raw TaQL parse tree node defining a show command. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding the parts of the show command. // class TaQLShowNodeRep: public TaQLNodeRep { public: TaQLShowNodeRep (const TaQLMultiNode& names); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void show (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); TaQLMultiNode itsNames; }; // // Raw TaQL parse tree node defining an alter table copy column command. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding the parts of the copy column subcommand. // class TaQLCopyColNodeRep: public TaQLNodeRep { public: TaQLCopyColNodeRep (const TaQLMultiNode& names, const TaQLMultiNode& dminfo); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void show (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); TaQLMultiNode itsNames; TaQLMultiNode itsDMInfo; }; // // Raw TaQL parse tree node defining a DROP TABLE command. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeRep // // // This class is a TaQLNodeRep holding the tables of a drop table command. // class TaQLDropTabNodeRep: public TaQLNodeRep { public: TaQLDropTabNodeRep (const TaQLMultiNode& with, const TaQLMultiNode& tables); virtual TaQLNodeResult visit (TaQLNodeVisitor&) const override; virtual void show (std::ostream& os) const override; virtual void save (AipsIO& aio) const override; static TaQLNode restore (AipsIO& aio); TaQLMultiNode itsWith; TaQLMultiNode itsTables; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/TaQLNodeHandler.cc000066400000000000000000001367331476623553700206300ustar00rootroot00000000000000//# TaQLNodeHandler.cc: Class to handle the nodes in the raw TaQL parse tree //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TaQLNodeHandler::~TaQLNodeHandler() { clearStack(); } TaQLNodeResult TaQLNodeHandler::handleTree (const TaQLNode& node, const std::vector& tempTables) { clearStack(); itsTempTables = tempTables; return node.visit (*this); } TableParseQuery* TaQLNodeHandler::pushStack (TableParseQuery::CommandType type) { TableParseQuery* sel = new TableParseQuery(type); itsStack.push_back (sel); return sel; } TableParseQuery* TaQLNodeHandler::topStack() const { AlwaysAssert (itsStack.size() > 0, AipsError); return itsStack[itsStack.size()-1]; } void TaQLNodeHandler::popStack() { delete topStack(); itsStack.resize (itsStack.size()-1); } void TaQLNodeHandler::clearStack() { for (Int i=itsStack.size()-1; i>=0; --i) { delete itsStack[i]; } itsStack.resize (0); } TaQLNodeResult TaQLNodeHandler::visitConstNode (const TaQLConstNodeRep& node) { TableExprNode expr; switch (node.itsType) { case TaQLConstNodeRep::CTBool: expr = TableExprNode(node.itsBValue); break; case TaQLConstNodeRep::CTInt: expr = TableExprNode(node.itsIValue); break; case TaQLConstNodeRep::CTReal: expr = TableExprNode(node.itsRValue); break; case TaQLConstNodeRep::CTComplex: expr = TableExprNode(node.itsCValue); break; case TaQLConstNodeRep::CTString: expr = TableExprNode(node.itsSValue); break; case TaQLConstNodeRep::CTTime: expr = TableExprNode(node.itsTValue); break; } if (! node.getUnit().empty()) { expr = expr.useUnit (node.getUnit()); } return new TaQLNodeHRValue (expr); } TaQLNodeResult TaQLNodeHandler::visitRegexNode (const TaQLRegexNodeRep& node) { // Remove delimiters. String str = node.itsValue.substr(2, node.itsValue.size()-3); if (node.itsValue[0] == 'd') { return new TaQLNodeHRValue (TableExprNode(TaqlRegex( StringDistance(str, node.itsMaxDistance, True, node.itsIgnoreBlanks, node.itsCaseInsensitive)))); } else if (node.itsValue[0] == 'p') { str = Regex::fromPattern (str); } else if (node.itsValue[0] == 'm') { str = ".*(" + str + ").*"; } if (node.itsCaseInsensitive) { str = Regex::makeCaseInsensitive(str); } return new TaQLNodeHRValue (TableExprNode(TaqlRegex(Regex(str, True)))); } TaQLNodeResult TaQLNodeHandler::visitUnaryNode (const TaQLUnaryNodeRep& node) { Bool notexists = True; TaQLNodeResult res = visitNode (node.itsChild); TableExprNode expr = getHR(res).getExpr(); switch (node.itsType) { case TaQLUnaryNodeRep::U_MINUS: return new TaQLNodeHRValue (-expr); case TaQLUnaryNodeRep::U_NOT: return new TaQLNodeHRValue (!expr); case TaQLUnaryNodeRep::U_EXISTS: notexists= False; break; case TaQLUnaryNodeRep::U_NOTEXISTS: break; case TaQLUnaryNodeRep::U_BITNOT: return new TaQLNodeHRValue (~expr); } TableExprNode exres(topStack()->doExists (notexists, node.style().doTiming())); popStack(); return new TaQLNodeHRValue(exres); } TaQLNodeResult TaQLNodeHandler::visitBinaryNode (const TaQLBinaryNodeRep& node) { TaQLNodeResult resl = visitNode (node.itsLeft); TableExprNode left = getHR(resl).getExpr(); TaQLNodeResult resr = visitNode (node.itsRight); if (node.itsType == TaQLBinaryNodeRep::B_INDEX) { const TableExprNodeSet& right = getHR(resr).getExprSet(); return new TaQLNodeHRValue (TableParseQuery::handleSlice(left, right, node.itsRight.style())); } TableExprNode right = getHR(resr).getExpr(); switch (node.itsType) { case TaQLBinaryNodeRep::B_PLUS: return new TaQLNodeHRValue (left + right); case TaQLBinaryNodeRep::B_MINUS: return new TaQLNodeHRValue (left - right); case TaQLBinaryNodeRep::B_TIMES: return new TaQLNodeHRValue (left * right); case TaQLBinaryNodeRep::B_DIVIDE: return new TaQLNodeHRValue (left / right); case TaQLBinaryNodeRep::B_DIVIDETRUNC: return new TaQLNodeHRValue (floor(left / right)); case TaQLBinaryNodeRep::B_MODULO: return new TaQLNodeHRValue (left % right); case TaQLBinaryNodeRep::B_POWER: return new TaQLNodeHRValue (pow(left, right)); case TaQLBinaryNodeRep::B_OR: return new TaQLNodeHRValue (left || right); case TaQLBinaryNodeRep::B_AND: return new TaQLNodeHRValue (left && right); case TaQLBinaryNodeRep::B_EQ: return new TaQLNodeHRValue (left == right); case TaQLBinaryNodeRep::B_NE: return new TaQLNodeHRValue (left != right); case TaQLBinaryNodeRep::B_GT: return new TaQLNodeHRValue (left > right); case TaQLBinaryNodeRep::B_GE: return new TaQLNodeHRValue (left >= right); case TaQLBinaryNodeRep::B_LT: return new TaQLNodeHRValue (left < right); case TaQLBinaryNodeRep::B_LE: return new TaQLNodeHRValue (left <= right); case TaQLBinaryNodeRep::B_IN: return new TaQLNodeHRValue (left.in (right, node.style())); case TaQLBinaryNodeRep::B_INDEX: break; case TaQLBinaryNodeRep::B_EQREGEX: return new TaQLNodeHRValue (left == right); case TaQLBinaryNodeRep::B_NEREGEX: return new TaQLNodeHRValue (left != right); case TaQLBinaryNodeRep::B_BITAND: return new TaQLNodeHRValue (left & right); case TaQLBinaryNodeRep::B_BITXOR: return new TaQLNodeHRValue (left ^ right); case TaQLBinaryNodeRep::B_BITOR: return new TaQLNodeHRValue (left | right); } return TaQLNodeResult(); } TaQLNodeResult TaQLNodeHandler::visitMultiNode (const TaQLMultiNodeRep& node) { TaQLNodeHRValue* hrval = new TaQLNodeHRValue(); TaQLNodeResult res(hrval); // takes care of deletion TableExprNodeSet* set = new TableExprNodeSet(); hrval->setExprSet (set); hrval->setExpr (TableExprNode(set)); // takes care of deletion for (uInt i=0; iadd (*(vhr.getElem()), node.itsIsSetOrArray); } else { set->add (TableExprNodeSetElem(vhr.getExpr()), node.itsIsSetOrArray); } } if (node.itsIsSetOrArray) { hrval->setExpr (TableExprNode(set->setOrArray())); } return res; } TaQLNodeResult TaQLNodeHandler::visitFuncNode (const TaQLFuncNodeRep& node) { // The MSID function is handled immediately, because its column might not exist. // If not existing, the rowid function is used instead. In this way an MSv2, // which uses rowid as an implicit id, can easily be used in a join. // It can be preceded by a table shorthand. TableExprNode funcRes; String funcName (downcase(node.itsName)); Vector fncParts = stringToVector (funcName, '.'); if (fncParts[fncParts.size()-1] == "msid") { funcRes = handleIdFunc(node); } else { TaQLNodeResult result = visitNode (node.itsArgs); funcRes = topStack()->handleFunc (node.itsName, getHR(result).getExprSet(), node.style()); } TaQLNodeHRValue* hrval = new TaQLNodeHRValue(); TaQLNodeResult res(hrval); hrval->setExpr (funcRes); return res; } TableExprNode TaQLNodeHandler::handleIdFunc (const TaQLFuncNodeRep& node) { // Get the column name from the function argument. const std::vector& nodes = node.itsArgs.getMultiRep()->itsNodes; if (nodes.size() != 1 || !nodes[0].isValid()) { throw TableInvExpr("The MSID function should have 1 argument (column name)"); } const TaQLKeyColNodeRep* colRep = dynamic_cast(nodes[0].getRep()); if (! colRep) { throw TableInvExpr("The MSID function requires an unquoted column name"); } // See if the function or column name is prefixed with a shorthand. Vector fncParts = stringToVector (node.itsName, '.'); Vector colParts = stringToVector (colRep->itsName, '.'); if (fncParts.size() + colParts.size() > 3) { throw TableInvExpr ("The MSID function or its column name can only " "be prefixed by the shorthand of a table"); } String colName = colParts[colParts.size() - 1]; String shand; if (fncParts.size() == 2) { shand = fncParts[0]; } else if (colParts.size() == 2) { shand = colParts[0]; } TableExprInfo tabInfo = topStack()->tableList().findTable (shand, False).getTableInfo(); // Check that a table is given. if (tabInfo.table().isNull()) { throw TableInvExpr ("No table found for the shorthand in the MSID function"); } // Use the column if it exists. Otherwise use rowid(). if (tabInfo.table().tableDesc().isColumn(colName)) { if (! shand.empty()) { colName = shand + '.' + colName; } return topStack()->handleKeyCol (colName, False); } return TableExprNode::newRowidNode (tabInfo); } TaQLNodeResult TaQLNodeHandler::visitRangeNode (const TaQLRangeNodeRep& node) { TaQLNodeHRValue* hrval = new TaQLNodeHRValue(); TaQLNodeResult res(hrval); TaQLNodeResult start = visitNode (node.itsStart); TaQLNodeResult end = visitNode (node.itsEnd); TableExprNodeSetElem* elem; if (node.itsAsMidWidth) { elem = new TableExprNodeSetElem (getHR(start).getExpr(), getHR(end).getExpr()); } else { if (start.isValid()) { if (end.isValid()) { elem = new TableExprNodeSetElem (node.itsLeftClosed, getHR(start).getExpr(), getHR(end).getExpr(), node.itsRightClosed); } else { elem = new TableExprNodeSetElem (node.itsLeftClosed, getHR(start).getExpr()); } } else { elem = new TableExprNodeSetElem (getHR(end).getExpr(), node.itsRightClosed); } } hrval->setElem (elem); hrval->setExpr (TableExprNode(elem)); return res; } TaQLNodeResult TaQLNodeHandler::visitIndexNode (const TaQLIndexNodeRep& node) { TaQLNodeHRValue* hrval = new TaQLNodeHRValue(); TaQLNodeResult res(hrval); TaQLNodeResult start = visitNode (node.itsStart); TaQLNodeResult end = visitNode (node.itsEnd); TaQLNodeResult incr = visitNode (node.itsIncr); const TableExprNode* se = start.isValid() ? &(getHR(start).getExpr()) : 0; const TableExprNode* ee = end.isValid() ? &(getHR( end).getExpr()) : 0; const TableExprNode* ie = incr.isValid() ? &(getHR( incr).getExpr()) : 0; TableExprNodeSetElem* elem = 0; // A single boolean node indicates a mask. if (se && !ee && !ie && se->dataType() == TpBool) { elem = new TableExprNodeSetElem (*se); } else { elem = new TableExprNodeSetElem (se, ee, ie, node.style().isEndExcl()); } hrval->setElem (elem); hrval->setExpr (TableExprNode(elem)); // Takes care of deleting elem return res; } TaQLNodeResult TaQLNodeHandler::visitKeyColNode (const TaQLKeyColNodeRep& node) { TaQLNodeHRValue* hrval = new TaQLNodeHRValue(); TaQLNodeResult res(hrval); hrval->setExpr (topStack()->handleKeyCol (node.itsName, True)); return res; } TaQLNodeResult TaQLNodeHandler::visitTableNode (const TaQLTableNodeRep& node) { TaQLNodeHRValue* hrval = new TaQLNodeHRValue; TaQLNodeResult res(hrval); if (node.itsTable.nodeType() == TaQLNode_Const) { handleTableName (hrval, node.itsTable); } else { TaQLNodeResult res = visitNode (node.itsTable); hrval->setTable (getHR(res).getTable()); } hrval->setAlias (node.itsAlias); return res; } TaQLNodeResult TaQLNodeHandler::visitColNode (const TaQLColNodeRep& node) { TaQLNodeHRValue* hrval = new TaQLNodeHRValue; TaQLNodeResult res(hrval); if (node.itsExpr.nodeType() == TaQLNode_KeyCol) { // A single column name. TaQLKeyColNodeRep* keyNode = (TaQLKeyColNodeRep*)(node.itsExpr.getRep()); hrval->setString (keyNode->itsName); } else if (node.itsExpr.nodeType() == TaQLNode_Regex) { // A wildcarded column name has an int value >= 0. TaQLRegexNodeRep* regexNode = (TaQLRegexNodeRep*)(node.itsExpr.getRep()); Int val = 0; if (regexNode->itsCaseInsensitive) val = val|1; if (regexNode->itsNegate) val = val|2; hrval->setInt (val); hrval->setString (regexNode->itsValue); } else { // An expression. TaQLNodeResult result = visitNode(node.itsExpr); hrval->setExpr (getHR(result).getExpr()); } if (hrval->getExpr().isNull() && ! node.itsNameMask.empty()) { throw TableInvExpr("value AS (col,mask) can only be given if value " "is an expression"); } hrval->setAlias (node.itsName); hrval->setNameMask (node.itsNameMask); hrval->setDtype (node.itsDtype); return res; } TaQLNodeResult TaQLNodeHandler::visitColumnsNode (const TaQLColumnsNodeRep& node) { if (node.itsNodes.isValid()) { const TaQLMultiNodeRep* columns = node.itsNodes.getMultiRep(); const std::vector& nodes = columns->itsNodes; for (uInt i=0; ihandleColumn (res.getInt(), res.getString(), res.getExpr(), res.getAlias(), res.getNameMask(), res.getDtype()); } } topStack()->handleColumnFinish (node.itsDistinct); return TaQLNodeResult(); } TaQLNodeResult TaQLNodeHandler::visitJoinNode (const TaQLJoinNodeRep& node) { // Add a TableParseJoin object. TableParseJoin& joinObj = topStack()->addJoin(); AlwaysAssert (node.itsTables.isValid(), AipsError); const std::vector& nodes = node.itsTables.getMultiRep()->itsNodes; for (uInt i=0; i& nodes = keys->itsNodes; std::vector outnodes(nodes.size()); for (uInt i=0; ihandleGroupby (outnodes, node.itsType==TaQLGroupNodeRep::Rollup); return TaQLNodeResult(); } TaQLNodeResult TaQLNodeHandler::visitSortKeyNode (const TaQLSortKeyNodeRep&) { // This function cannot be called, because visitSortNode handles // the keys. throw AipsError("TaQLNodeHandler::visitSortKeyNode should not be called"); } TaQLNodeResult TaQLNodeHandler::visitSortNode (const TaQLSortNodeRep& node) { const TaQLMultiNodeRep* keys = node.itsKeys.getMultiRep(); const std::vector& nodes = keys->itsNodes; std::vector outkeys(nodes.size()); for (uInt i=0; iitsChild); const TaQLNodeHRValue& res = getHR(result); if (keyNode->itsType == TaQLSortKeyNodeRep::None) { outkeys[i] = TableParseSortKey (res.getExpr()); } else { Sort::Order sortOrder = Sort::Ascending; if (keyNode->itsType == TaQLSortKeyNodeRep::Descending) { sortOrder = Sort::Descending; } outkeys[i] = TableParseSortKey (res.getExpr(), sortOrder); } } Sort::Order defaultSortOrder = Sort::Ascending; if (node.itsType == TaQLSortNodeRep::Descending) { defaultSortOrder = Sort::Descending; } topStack()->handleSort (outkeys, node.itsUnique, defaultSortOrder); return TaQLNodeResult(); } TaQLNodeResult TaQLNodeHandler::visitLimitOffNode (const TaQLLimitOffNodeRep& node) { if (node.itsLimit.isValid()) { TaQLNodeResult result = visitNode (node.itsLimit); const TaQLNodeHRValue& res = getHR(result); // If start:end:incr is given, the result is a set element. // Otherwise the result is an expression (for a single limit value). if (res.getElem()) { topStack()->handleLimit (*res.getElem()); } else { topStack()->handleLimit (res.getExpr()); } } if (node.itsOffset.isValid()) { TaQLNodeResult result = visitNode (node.itsOffset); const TaQLNodeHRValue& res = getHR(result); topStack()->handleOffset (res.getExpr()); } return TaQLNodeResult(); } TaQLNodeResult TaQLNodeHandler::visitGivingNode (const TaQLGivingNodeRep& node) { if (node.itsExprList.isValid()) { // Expressions in Giving clause. TaQLNodeResult result = visitNode (node.itsExprList); const TaQLNodeHRValue& res = getHR(result); topStack()->handleGiving (res.getExprSet()); } else { // Table in Giving clause. Record type = handleMultiRecFld (node.itsType); topStack()->handleGiving (node.itsName, type); } return TaQLNodeResult(); } TaQLNodeResult TaQLNodeHandler::visitUpdExprNode (const TaQLUpdExprNodeRep& node) { TaQLNodeResult eres = visitNode (node.itsExpr); TableExprNode expr = getHR(eres).getExpr(); if (node.itsIndices1.isValid()) { TaQLNodeResult ires1 = visitNode (node.itsIndices1); if (node.itsIndices2.isValid()) { TaQLNodeResult ires2 = visitNode (node.itsIndices2); topStack()->addUpdate (std::make_shared (node.itsName, node.itsNameMask, getHR(ires1).getExprSet(), getHR(ires2).getExprSet(), expr, node.itsIndices1.style())); } else { topStack()->addUpdate (std::make_shared (node.itsName, node.itsNameMask, getHR(ires1).getExprSet(), expr, node.itsIndices1.style())); } } else { topStack()->addUpdate (std::make_shared (node.itsName, node.itsNameMask, expr)); } return TaQLNodeResult(); } TaQLNodeResult TaQLNodeHandler::visitSelectNode (const TaQLSelectNodeRep& node) { // Add an entry to the stack. Bool outer = itsStack.empty(); TableParseQuery* curSel = pushStack (TableParseQuery::PSELECT); // First handle LIMIT/OFFSET, because limit is needed when creating // a temp table for a select without a FROM. // In its turn limit/offset might use WITH tables, so do them very first. handleTables (node.itsWith, False); visitNode (node.itsLimitOff); if (node.itsTables.isValid()) { handleTables (node.itsTables); } else { // A select without a FROM means that a temp table must be created. topStack()->handleTableNoFrom(); } curSel->setDMInfo (handleMultiRecFld (node.itsDMInfo)); // Handle WHERE before SELECT because WHERE cannot use columns in a // table resulting from SELECT, while the other clauses can. // The reason is that selection has to be done before projection. // Furthermore, handle GIVING first, because projection needs to know // the resulting table name. visitNode (node.itsGiving); handleJoins (node.itsJoins); handleWhere (node.itsWhere); visitNode (node.itsGroupby); visitNode (node.itsColumns); handleHaving (node.itsHaving); visitNode (node.itsSort); TaQLNodeHRValue* hrval = new TaQLNodeHRValue(); TaQLNodeResult res(hrval); if (! node.getNoExecute()) { if (outer) { curSel->execute (node.style().doTiming(), False, False, 0, node.style().doTracing(), itsTempTables, itsStack); hrval->setTable (curSel->getTable()); Block block = curSel->getColumnNames(); hrval->setNames (Vector(block.begin(), block.end())); hrval->setString ("select"); } else { if (node.getFromExecute()) { hrval->setTable (curSel->doFromQuery(node.style().doTiming())); } else { hrval->setExpr (curSel->doSubQuery(node.style().doTiming())); } } popStack(); } return res; } TaQLNodeResult TaQLNodeHandler::visitUpdateNode (const TaQLUpdateNodeRep& node) { TableParseQuery* curSel = pushStack (TableParseQuery::PUPDATE); // First handle LIMIT/OFFSET, because limit is needed when creating // a temp table for a select without a FROM. // In its turn limit/offset might use WITH tables, so do them very first. handleTables (node.itsWith, False); handleTables (node.itsTables); handleTables (node.itsFrom); handleUpdate (node.itsUpdate); handleWhere (node.itsWhere); visitNode (node.itsSort); visitNode (node.itsLimitOff); curSel->execute (node.style().doTiming(), False, True, 0); TaQLNodeHRValue* hrval = new TaQLNodeHRValue(); TaQLNodeResult res(hrval); hrval->setTable (curSel->getTable()); Block block = curSel->getColumnNames(); hrval->setNames (Vector(block.begin(), block.end())); hrval->setString ("update"); popStack(); return res; } TaQLNodeResult TaQLNodeHandler::visitInsertNode (const TaQLInsertNodeRep& node) { TableParseQuery* curSel = pushStack (TableParseQuery::PINSERT); handleTables (node.itsWith, False); handleTables (node.itsTables); handleInsCol (node.itsColumns); if (node.itsLimit.isValid()) { TaQLNodeResult res = visitNode (node.itsLimit); curSel->handleLimit (getHR(res).getExpr()); } Bool addedSel = False; if (node.itsValues.nodeType() == TaQLNode_Multi) { // Individual value expressions given. handleInsVal (node.itsValues); curSel->handleInsert(); } else { // A subquery is given. AlwaysAssert (node.itsValues.nodeType() == TaQLNode_Select, AipsError); visitNode (node.itsValues); curSel->handleInsert (topStack()); addedSel = True; } curSel->execute (node.style().doTiming(), False, True, 0); if (addedSel) { popStack(); // remove insert subquery } TaQLNodeHRValue* hrval = new TaQLNodeHRValue(); TaQLNodeResult res(hrval); hrval->setTable (curSel->getTable()); Block block = curSel->getColumnNames(); hrval->setNames (Vector(block.begin(), block.end())); hrval->setString ("insert"); popStack(); return res; } TaQLNodeResult TaQLNodeHandler::visitDeleteNode (const TaQLDeleteNodeRep& node) { TableParseQuery* curSel = pushStack (TableParseQuery::PDELETE); handleTables (node.itsWith, False); handleTables (node.itsTables); handleWhere (node.itsWhere); visitNode (node.itsSort); visitNode (node.itsLimitOff); curSel->execute (node.style().doTiming(), False, True, 0); TaQLNodeHRValue* hrval = new TaQLNodeHRValue(); TaQLNodeResult res(hrval); hrval->setTable (curSel->getTable()); hrval->setString ("delete"); popStack(); return res; } TaQLNodeResult TaQLNodeHandler::visitCountNode (const TaQLCountNodeRep& node) { Bool outer = itsStack.empty(); TableParseQuery* curSel = pushStack (TableParseQuery::PCOUNT); handleTables (node.itsWith, False); handleTables (node.itsTables); visitNode (node.itsColumns); handleWhere (node.itsWhere); curSel->handleCount(); TaQLNodeHRValue* hrval = new TaQLNodeHRValue(); TaQLNodeResult res(hrval); AlwaysAssert (! node.getNoExecute(), AipsError); if (outer) { curSel->execute (node.style().doTiming(), False, True, 0); hrval->setTable (curSel->getTable()); Block block = curSel->getColumnNames(); hrval->setNames (Vector(block.begin(), block.end())); hrval->setString ("count"); } else { AlwaysAssert (node.getFromExecute(), AipsError); hrval->setTable (curSel->doFromQuery(node.style().doTiming())); } popStack(); return res; } TaQLNodeResult TaQLNodeHandler::visitCalcNode (const TaQLCalcNodeRep& node) { TableParseQuery* curSel = pushStack (TableParseQuery::PCALC); handleTables (node.itsWith, False); handleTables (node.itsTables); // If where, orderby, limit and/or offset is given, handle as FROM query. if (node.itsWhere.isValid() || node.itsSort.isValid() || node.itsLimitOff.isValid()) { handleWhere (node.itsWhere); visitNode (node.itsSort); visitNode (node.itsLimitOff); Table tab = curSel->doFromQuery(node.style().doTiming()); // Replace with the resulting table. curSel->replaceTable (tab); } TaQLNodeResult eres = visitNode (node.itsExpr); curSel->handleCalcComm (getHR(eres).getExpr()); TaQLNodeHRValue* hrval = new TaQLNodeHRValue(); TaQLNodeResult res(hrval); hrval->setExpr (curSel->getNode()); hrval->setString ("calc"); popStack(); return res; } TaQLNodeResult TaQLNodeHandler::visitCreTabNode (const TaQLCreTabNodeRep& node) { TableParseQuery* curSel = pushStack (TableParseQuery::PCRETAB); handleTables (node.itsWith, False); visitNode (node.itsGiving); topStack()->initDescriptions (TableDesc(), Record()); handleLikeDrop (node.itsLikeDrop); handleColSpecs (node.itsColumns); Record dminfo = handleMultiRecFld (node.itsDMInfo); if (node.itsLimit.isValid()) { TaQLNodeResult res = visitNode (node.itsLimit); curSel->handleLimit (getHR(res).getExpr()); } curSel->handleCreTab (dminfo, itsTempTables, itsStack); TaQLNodeHRValue* hrval = new TaQLNodeHRValue(); TaQLNodeResult res(hrval); hrval->setTable (curSel->getTable()); Block block = curSel->getColumnNames(); hrval->setNames (Vector(block.begin(), block.end())); hrval->setString ("cretab"); popStack(); return res; } TaQLNodeResult TaQLNodeHandler::visitColSpecNode (const TaQLColSpecNodeRep& node) { Record spec = handleMultiRecFld (node.itsSpec); topStack()->handleColSpec (node.itsName, node.itsLikeCol, node.itsDtype, spec, node.style().isCOrder()); return TaQLNodeResult(); } TaQLNodeResult TaQLNodeHandler::visitRecFldNode (const TaQLRecFldNodeRep& node) { std::string error; ValueHolder vh; if (! node.itsFromName.empty()) { vh = topStack()->getRecFld (node.itsFromName); } else if (! node.itsValues.isValid()) { // Invalid node means an empty vector. vh = ValueHolder (1, True); } else if (node.itsValues.nodeType() == TaQLNode_Multi && node.itsValues.getRep() != 0 && ! ((const TaQLMultiNodeRep*)(node.itsValues.getRep()))->itsIsSetOrArray) { vh = ValueHolder (handleMultiRecFld (node.itsValues)); } else { handleWhere (node.itsValues); TableExprNode expr = topStack()->getNode(); if (! expr.getRep()->isConstant()) { error = "must be constant"; } else { switch (expr.dataType()) { case TpBool: if (expr.isScalar()) { vh = ValueHolder(expr.getBool (0)); } else { vh = ValueHolder(expr.getArrayBool(0)); } break; case TpInt64: if (expr.isScalar()) { vh = ValueHolder(expr.getInt (0)); } else { vh = ValueHolder(expr.getArrayInt(0)); } break; case TpDouble: if (expr.isScalar()) { vh = ValueHolder(expr.getDouble (0)); } else { vh = ValueHolder(expr.getArrayDouble(0)); } break; case TpDComplex: if (expr.isScalar()) { vh = ValueHolder(expr.getDComplex (0)); } else { vh = ValueHolder(expr.getArrayDComplex(0)); } break; case TpString: if (expr.isScalar()) { vh = ValueHolder(expr.getString (0)); } else { vh = ValueHolder(expr.getArrayString(0)); } break; default: error = "has an unknown data type"; } } } if (! error.empty()) { ostringstream os; node.itsValues.show (os); throw TableInvExpr("Expression " + os.str() + ' ' + error); } TaQLNodeHRValue* hrval = new TaQLNodeHRValue(); TaQLNodeResult res(hrval); hrval->setString (node.itsName); hrval->setDtype (node.itsDtype); hrval->setValueHolder (vh); return res; } Record TaQLNodeHandler::handleMultiRecFld (const TaQLNode& node) { if (! node.isValid()) { return Record(); } AlwaysAssert (node.nodeType() == TaQLNode_Multi, AipsError); const TaQLMultiNodeRep* mnode = (const TaQLMultiNodeRep*)(node.getRep()); const std::vector& vals = mnode->itsNodes; for (uInt i=0; ihandleAltTab(); const TaQLMultiNodeRep& clist = *(node.itsCommands.getMultiRep()); const std::vector& commands = clist.itsNodes; for (uInt i=0; isetTable (curSel->getTable()); hrval->setString ("alttab"); popStack(); return res; } TaQLNodeResult TaQLNodeHandler::visitAddColNode (const TaQLAddColNodeRep& node) { topStack()->initDescriptions (TableDesc(), Record()); handleColSpecs (node.itsColumns); Record dminfo = handleMultiRecFld (node.itsDMInfo); topStack()->handleAddCol (dminfo); return TaQLNodeResult(); } TaQLNodeResult TaQLNodeHandler::visitSetKeyNode (const TaQLSetKeyNodeRep& node) { // Get the value. const TaQLMultiNodeRep& nodelist = *(node.itsKeyVals.getMultiRep()); const std::vector& nodes = nodelist.itsNodes; for (uInt i=0; ihandleSetKey (res.getString(), res.getDtype(), res.getValueHolder()); } return TaQLNodeResult(); } TaQLNodeResult TaQLNodeHandler::visitRenDropNode (const TaQLRenDropNodeRep& node) { // Get the column/keyword names. const TaQLMultiNodeRep& nodelist = *(node.itsNames.getMultiRep()); const std::vector& nodes = nodelist.itsNodes; Vector names(nodes.size()); for (uInt i=0; iitsName; } // Get the table to operate on. Table tab (topStack()->getTable()); if (node.itsType == 0) { // Rename columns. AlwaysAssert (names.size() % 2 == 0, AipsError); for (uInt i=0; ihandleRenameKey (names[i], names[i+1]); } } else if (node.itsType == 3) { // Remove keywords for (uInt i=0; ihandleRemoveKey (names[i]); } } else { throw AipsError("TaQLNodeHandler::vistRenDrop - unhandled type"); } return TaQLNodeResult(); } TaQLNodeResult TaQLNodeHandler::visitAddRowNode (const TaQLAddRowNodeRep& node) { TaQLNodeResult result = visitNode (node.itsNRow); const TaQLNodeHRValue& res = getHR(result); topStack()->handleAddRow (res.getExpr()); return TaQLNodeResult(); } void TaQLNodeHandler::handleTableName (TaQLNodeHRValue* hrval, const TaQLNode& node) { AlwaysAssert (node.nodeType() == TaQLNode_Const, AipsError); TaQLConstNodeRep* tabnm = (TaQLConstNodeRep*)(node.getRep()); if (tabnm->itsType == TaQLConstNodeRep::CTInt) { // A temptable has type Int, but the string can contain a subtable name. hrval->setInt (tabnm->itsIValue); hrval->setString (tabnm->itsSValue); } else { hrval->setString (tabnm->getString()); } } void TaQLNodeHandler::handleTables (const TaQLMultiNode& node, Bool addToFromList) { if (! node.isValid()) { return; } const std::vector& nodes = node.getMultiRep()->itsNodes; for (uInt i=0; itableList().addTable (res.getInt(), res.getString(), res.getTable(), res.getAlias(), addToFromList, itsTempTables, itsStack); } } void TaQLNodeHandler::handleJoins (const TaQLMultiNode& node) { if (! node.isValid()) { return; // no joins } const std::vector& nodes = node.getMultiRep()->itsNodes; for (uInt i=0; i& nodes = node.itsTables.getMultiRep()->itsNodes; std::vector
      • tables; for (uInt i=0; i nms = Directory::shellExpand(Vector(1, name)); if (nms.empty()) { throw TableInvExpr("No matching tables found for " + name); } for (uInt j=0; j tabs(tables.size()); for (uInt i=0; i subtables; if (node.itsSubTables.isValid()) { const std::vector& names = node.itsSubTables.getMultiRep()->itsNodes; subtables.resize (names.size()); for (uInt i=0; isetTable (conctab); return hr; } TaQLNodeResult TaQLNodeHandler::visitShowNode (const TaQLShowNodeRep& node) { String info; Bool doInfo = True; Vector parts; if (node.itsNames.isValid()) { const std::vector& nodes = node.itsNames.getMultiRep()->itsNodes; parts.resize (nodes.size()); TaQLNodeResult result = visitNode (nodes[0]); const TaQLNodeHRValue& res = getHR(result); parts[0] = res.getExpr().getString(0); parts[0].downcase(); if (parts[0] == "table" && nodes.size() > 1) { TableParseQuery* curSel = pushStack (TableParseQuery::PSHOW); TaQLNodeHRValue res; handleTableName (&res, nodes[1]); curSel->tableList().addTable (res.getInt(), res.getString(), res.getTable(), res.getAlias(), True, itsTempTables, itsStack); parts[1] = res.getString(); for (uInt i=2; igetTableStructure (parts, node.style()); doInfo = False; popStack(); } else { for (uInt i=1; isetString ("show"); hr->setExpr (TableExprNode(info)); return hr; } TaQLNodeResult TaQLNodeHandler::visitCopyColNode (const TaQLCopyColNodeRep& node) { Record dminfo = handleMultiRecFld (node.itsDMInfo); topStack()->initDescriptions (TableDesc(), dminfo); const TaQLMultiNodeRep& nodelist = *(node.itsNames.getMultiRep()); const std::vector& nodes = nodelist.itsNodes; std::vector names(nodes.size()); // Get the names of the new and old columns. for (uInt i=0; iitsName; } // Get the table to operate on. Table tab (topStack()->getTable()); // Add entries for each column copy. AlwaysAssert (names.size() % 2 == 0, AipsError); for (uInt i=0; ihandleColSpec (names[i], names[i+1], String(), Record()); // Add an update command. topStack()->addUpdate (std::make_shared (names[i], String(), tab.col(names[i+1]))); } // Execute it all. topStack()->handleCopyCol (node.style().doTiming()); return TaQLNodeResult(); } TaQLNodeResult TaQLNodeHandler::visitDropTabNode (const TaQLDropTabNodeRep& node) { TableParseQuery* curSel = pushStack (TableParseQuery::PDROPTAB); handleTables (node.itsWith, False); handleTables (node.itsTables); curSel->handleDropTab (itsTempTables, itsStack); return TaQLNodeResult (new TaQLNodeHRValue()); } void TaQLNodeHandler::handleWhere (const TaQLNode& node) { if (node.isValid()) { TaQLNodeResult result = visitNode (node); const TaQLNodeHRValue& res = getHR(result); topStack()->handleWhere (res.getExpr()); } } void TaQLNodeHandler::handleHaving (const TaQLNode& node) { if (node.isValid()) { TaQLNodeResult result = visitNode (node); const TaQLNodeHRValue& res = getHR(result); topStack()->handleHaving (res.getExpr()); } } void TaQLNodeHandler::handleUpdate (const TaQLMultiNode& node) { const TaQLMultiNodeRep& updates = *(node.getMultiRep()); const std::vector& nodes = updates.itsNodes; for (uInt i=0; ihandleUpdate(); } void TaQLNodeHandler::handleInsCol (const TaQLMultiNode& node) { if (! node.isValid()) { return; } const TaQLMultiNodeRep& cols = *(node.getMultiRep()); const std::vector& nodes = cols.itsNodes; for (uInt i=0; ihandleColumn (-1, colNode->itsName, TableExprNode(), "", colNode->itsNameMask, ""); } } void TaQLNodeHandler::handleInsVal (const TaQLNode& node) { AlwaysAssert (node.nodeType() == TaQLNode_Multi, AipsError); const TaQLMultiNodeRep& avals = dynamic_cast(*node.getRep()); const std::vector& anodes = avals.itsNodes; std::vector exprs; AlwaysAssert (anodes.size() > 0, AipsError); uInt nval = 0; for (uInt i=0; i(*anodes[i].getRep()); const std::vector& nodes = vals.itsNodes; if (i == 0) { nval = nodes.size(); exprs.reserve (nval*anodes.size()); } else { if (nodes.size() != nval) { throw TableInvExpr("Different nr of values given in INSERT"); } } for (uInt j=0; jaddUpdate (std::make_shared ("", "", expr)); } } } topStack()->setInsertExprs (exprs); } void TaQLNodeHandler::handleLikeDrop (const TaQLMultiNode& node) { if (! node.isValid()) { return; } const TaQLMultiNodeRep& mrep = *(node.getMultiRep()); const std::vector& nodes = mrep.itsNodes; AlwaysAssert (nodes.size() > 0, AipsError); // Handle the LIKE table; add it to the FROM list. TaQLNodeResult result = visitNode (nodes[0]); const TaQLNodeHRValue& res = getHR(result); Table tab(topStack()->tableList().addTable (res.getInt(), res.getString(), res.getTable(), res.getAlias(), True, itsTempTables, itsStack)); // An exception should be thrown if the table does not exist, but be defensive. AlwaysAssert (! tab.isNull(), AipsError); TableDesc desc(tab.tableDesc()); if (nodes.size() == 2) { // Handle the columns to remove. AlwaysAssert (nodes[1].nodeType() == TaQLNode_Multi, AipsError); const TaQLMultiNodeRep& nodeList = *(const TaQLMultiNodeRep*)(nodes[1].getRep()); const std::vector& colNodes = nodeList.itsNodes; for (uInt i=0; iitsName)) { desc.removeColumn (colNode->itsName); } else { throw TableInvExpr("Column " + colNode->itsName + " to be dropped does not exist in the LIKE table"); } } } // Ensure no dropped columns are present in the dminfo. Record dminfo (DataManInfo::finalizeMerge (desc, tab.dataManagerInfo())); topStack()->initDescriptions (desc, dminfo); } void TaQLNodeHandler::handleColSpecs (const TaQLMultiNode& node) { if (! node.isValid()) { return; } const TaQLMultiNodeRep& cols = *(node.getMultiRep()); const std::vector& nodes = cols.itsNodes; for (uInt i=0; i #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TaQLNodeHRValue; // // Class to handle the nodes in the raw TaQL parse tree. // // // // // //# Classes you should understand before using this one. //
      • TaQLNode //
      • Note 199 describing // // TaQL // // // TaQLNodeHandler is a specialization of class // TaQLNodeVisitor. // It processes the raw TaQL parse tree generated by TableGram. // The processing is done in a recursive way. It starts at the top // (which is a SELECT, UPDATE, etc. expression) and the processing // results of a query are stored in a TableParseQuery object. // These objects are kept in a stack for possible nested queries. // After a query is fully processed, it is executed. Usually the result // is a table; only a CALC command gives a TableExprNode as result. // // // Separating the raw query parsing from the query processing has // several advantages compared to the old situation where parsing // and processing were combined. //
          //
        • The full command is parsed before any processing is done. // So in case of a parse error, no possibly expensive processing // has been done yet. //
        • In the future query optimization can be done in an easier way. //
        • Nested parsing is not possible. In case a Table is opened // with a virtual TaQL column, the parsing of that TaQL string // does not interfere with parsing the TaQL command. //
        • It is possible to use expressions in the column list. // That could not be done before, because the column list was // parsed/processed before the table list. //
        //
        class TaQLNodeHandler : public TaQLNodeVisitor { public: virtual ~TaQLNodeHandler(); // Handle and process the raw parse tree. // The result contains a Table or TableExprNode object. TaQLNodeResult handleTree (const TaQLNode& tree, const std::vector&); // Define the functions to visit each node type. // virtual TaQLNodeResult visitConstNode (const TaQLConstNodeRep& node); virtual TaQLNodeResult visitRegexNode (const TaQLRegexNodeRep& node); virtual TaQLNodeResult visitUnaryNode (const TaQLUnaryNodeRep& node); virtual TaQLNodeResult visitBinaryNode (const TaQLBinaryNodeRep& node); virtual TaQLNodeResult visitMultiNode (const TaQLMultiNodeRep& node); virtual TaQLNodeResult visitFuncNode (const TaQLFuncNodeRep& node); virtual TaQLNodeResult visitRangeNode (const TaQLRangeNodeRep& node); virtual TaQLNodeResult visitIndexNode (const TaQLIndexNodeRep& node); virtual TaQLNodeResult visitKeyColNode (const TaQLKeyColNodeRep& node); virtual TaQLNodeResult visitTableNode (const TaQLTableNodeRep& node); virtual TaQLNodeResult visitColNode (const TaQLColNodeRep& node); virtual TaQLNodeResult visitColumnsNode (const TaQLColumnsNodeRep& node); virtual TaQLNodeResult visitJoinNode (const TaQLJoinNodeRep& node); virtual TaQLNodeResult visitGroupNode (const TaQLGroupNodeRep& node); virtual TaQLNodeResult visitSortKeyNode (const TaQLSortKeyNodeRep& node); virtual TaQLNodeResult visitSortNode (const TaQLSortNodeRep& node); virtual TaQLNodeResult visitLimitOffNode (const TaQLLimitOffNodeRep& node); virtual TaQLNodeResult visitGivingNode (const TaQLGivingNodeRep& node); virtual TaQLNodeResult visitUpdExprNode (const TaQLUpdExprNodeRep& node); virtual TaQLNodeResult visitSelectNode (const TaQLSelectNodeRep& node); virtual TaQLNodeResult visitUpdateNode (const TaQLUpdateNodeRep& node); virtual TaQLNodeResult visitInsertNode (const TaQLInsertNodeRep& node); virtual TaQLNodeResult visitDeleteNode (const TaQLDeleteNodeRep& node); virtual TaQLNodeResult visitCountNode (const TaQLCountNodeRep& node); virtual TaQLNodeResult visitCalcNode (const TaQLCalcNodeRep& node); virtual TaQLNodeResult visitCreTabNode (const TaQLCreTabNodeRep& node); virtual TaQLNodeResult visitColSpecNode (const TaQLColSpecNodeRep& node); virtual TaQLNodeResult visitRecFldNode (const TaQLRecFldNodeRep& node); virtual TaQLNodeResult visitUnitNode (const TaQLUnitNodeRep& node); virtual TaQLNodeResult visitAltTabNode (const TaQLAltTabNodeRep& node); virtual TaQLNodeResult visitAddColNode (const TaQLAddColNodeRep& node); virtual TaQLNodeResult visitSetKeyNode (const TaQLSetKeyNodeRep& node); virtual TaQLNodeResult visitRenDropNode (const TaQLRenDropNodeRep& node); virtual TaQLNodeResult visitAddRowNode (const TaQLAddRowNodeRep& node); virtual TaQLNodeResult visitConcTabNode (const TaQLConcTabNodeRep& node); virtual TaQLNodeResult visitShowNode (const TaQLShowNodeRep& node); virtual TaQLNodeResult visitCopyColNode (const TaQLCopyColNodeRep& node); virtual TaQLNodeResult visitDropTabNode (const TaQLDropTabNodeRep& node); // // Get the actual result object from the result. static const TaQLNodeHRValue& getHR (const TaQLNodeResult&); private: // Push a new TableParseQuery on the stack. TableParseQuery* pushStack (TableParseQuery::CommandType); // Get the top of the TableParseQuery stack. TableParseQuery* topStack() const; // Pop the top from the TableParseQuery stack. void popStack(); // Clear the select stack. void clearStack(); // Handle the select command. // Optionally the command is not executed (needed for the EXISTS operator). TaQLNodeResult handleSelect (const TaQLSelectNodeRep& node, Bool doExec); // Handle a table name or temptable number in the given node // and put it in the value result. void handleTableName (TaQLNodeHRValue* hrval, const TaQLNode& node); // Handle a MultiNode containing table info. void handleTables (const TaQLMultiNode&, Bool addToFromList=True); // Handle a MultiNoide containing joins. void handleJoins (const TaQLMultiNode& node); // Make a ConcatTable from a nested set of tables. Table makeConcatTable (const TaQLMultiNodeRep& node); // Handle the WHERE clause. void handleWhere (const TaQLNode&); // Handle the HAVING clause. void handleHaving (const TaQLNode&); // Handle the UPDATE SET clause. void handleUpdate (const TaQLMultiNode&); // Handle the INSERT columns. void handleInsCol (const TaQLMultiNode&); // Handle the INSERT values. void handleInsVal (const TaQLNode&); // Handle the possible LIKE table DROP COLUMN part. void handleLikeDrop (const TaQLMultiNode& node); // Handle a column specification in a create table or add column. void handleColSpecs (const TaQLMultiNode&); // Handle a Multi RecFld representing a Record. Record handleMultiRecFld (const TaQLNode& node); // Handle the MSID function. TableExprNode handleIdFunc (const TaQLFuncNodeRep& node); //# Data members //# Use vector instead of stack because random access is needed. std::vector itsStack; //# The temporary tables referred to by $i in the TaQL string. std::vector itsTempTables; }; // // Class containing the result value of the handling of a TaQLNode. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeResult //
      • TaQLNodeHandler //
      • Note 199 describing // // TaQL // // // TaQLNodeHRValue is a specialization of class // TaQLNodeResultRep. // It contains the values resulting from handling a particular node. // The object is effectively a collection of all possible values that // need to be returned. Which values are filled in, depends on which node // has been processed. // The getHR function in TaQLNodeHandler is very useful to // extract/cast the TaQLNodeHRValue object from the general // TaQLNodeResult object. // // class TaQLNodeHRValue: public TaQLNodeResultRep { public: TaQLNodeHRValue() : itsInt(-1), itsElem(0), itsSet(0), itsNames(0) {} TaQLNodeHRValue (const TableExprNode& expr) : itsInt(-1), itsExpr(expr), itsElem(0), itsSet(0), itsNames(0) {} ~TaQLNodeHRValue() override = default; // Get the values. // Int getInt() const { return itsInt; } const String& getString() const { return itsString; } const String& getAlias() const { return itsAlias; } const String& getNameMask() const { return itsNameMask; } const String& getDtype() const { return itsDtype; } const Record& getRecord() const { return itsRecord; } const ValueHolder& getValueHolder() const { return itsVH; } const Table& getTable() const { return itsTable; } const TableExprNode& getExpr() const { return itsExpr; } const TableExprNodeSetElem* getElem() const { return itsElem; } const TableExprNodeSet& getExprSet() const { return *itsSet; } const Vector& getNames() const { return itsNames; } // // Set the values. // If a pointer is given, it takes over the pointer. // void setInt (Int ival) { itsInt = ival; } void setString (const String& str) { itsString = str; } void setAlias (const String& alias) { itsAlias = alias; } void setNameMask (const String& nameMask) { itsNameMask = nameMask; } void setDtype (const String& dtype) { itsDtype = dtype; } void setRecord (const Record& record) { itsRecord = record; } void setValueHolder (const ValueHolder& vh) { itsVH = vh; } void setTable (const Table& table) { itsTable = table; } void setExpr (const TableExprNode& expr) { itsExpr = expr; } void setElem (TableExprNodeSetElem* elem) { itsElem = elem; } void setExprSet (TableExprNodeSet* set) { itsSet = set; } void setNames (const Vector& names) { itsNames = names; } // private: Int itsInt; String itsString; String itsAlias; String itsNameMask; String itsDtype; Record itsRecord; ValueHolder itsVH; Table itsTable; TableExprNode itsExpr; TableExprNodeSetElem* itsElem; //# is counted in itsExpr TableExprNodeSet* itsSet; //# is counted in itsExpr Vector itsNames; }; //# This function can only be implemented after TaQLNodeHRValue is declared. inline const TaQLNodeHRValue& TaQLNodeHandler::getHR (const TaQLNodeResult& res) { return dynamic_cast(res.getRep()); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/TaQLNodeRep.cc000066400000000000000000000067401476623553700177730ustar00rootroot00000000000000//# TaQLNodeRep.cc: Representation of entities in the TaQL parse tree //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TaQLNodeRep::TaQLNodeRep (int nodeType) : itsNodeType (nodeType), itsStyle (TaQLNode::theirStyle) {} TaQLNodeRep::~TaQLNodeRep() {} String TaQLNodeRep::checkDataType (const String& dtype) { String dtstr(dtype); if (! dtstr.empty()) { dtstr.upcase(); if (dtstr == "B" || dtstr == "BOOL" || dtstr == "BOOLEAN") { dtstr = "B"; } else if (dtstr == "U1" || dtstr == "UC" || dtstr == "UCHAR" || dtstr == "BYTE") { dtstr = "U1"; } else if (dtstr == "I2" || dtstr == "SHORT" || dtstr == "SMALLINT") { dtstr = "I2"; } else if (dtstr == "U2" || dtstr == "UI2" || dtstr == "USHORT" || dtstr == "USMALLINT") { dtstr = "U2"; } else if (dtstr == "I4" || dtstr == "INT" || dtstr == "INTEGER") { dtstr = "I4"; } else if (dtstr == "U4" || dtstr == "UI4" || dtstr == "UINT" || dtstr == "UINTEGER") { dtstr = "U4"; } else if (dtstr == "I8" || dtstr == "LONG" || dtstr == "BIGINT") { dtstr = "I8"; } else if (dtstr == "FLT" || dtstr == "R4" || dtstr == "FLOAT") { dtstr = "R4"; } else if (dtstr == "DBL" || dtstr == "R8" || dtstr == "DOUBLE") { dtstr = "R8"; } else if (dtstr == "FC" || dtstr == "C4" || dtstr == "FCOMPLEX" || dtstr == "COMPLEX") { dtstr = "C4"; } else if (dtstr == "DC" || dtstr == "C8" || dtstr == "DCOMPLEX") { dtstr = "C8"; } else if (dtstr == "S" || dtstr == "STRING") { dtstr = "S"; } else if (dtstr == "TIME" || dtstr == "DATE" || dtstr == "EPOCH") { dtstr = "EPOCH"; } else { throw TableError ("Datatype '" + dtype + "' is invalid"); } } return dtstr; } String TaQLNodeRep::addEscape (const String& str) const { // This Regex contains the NAMETABC characters in TableGram.ll. static Regex re("[A-Za-z0-9_./+\\-~$@:]"); String out; for (size_t i=0; i #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declaration. class AipsIO; class TaQLNodeVisitor; // // Representation of a node in the raw TaQL parse tree. // // // // // //# Classes you should understand before using this one. //
      • TaQLNode //
      • Note 199 describing // // TaQL // // // TaQLNode/TaQLNodeRep form an envelope/letter pair. // TaQLNodeRep is the abstract base class for all classes used in the // raw TaQL parse tree // (e.g. TaQLConstNodeRep). // // // The envelope/letter idiom (aka counted referencing) is a nice means // to pass an object around by value, so to ensure that an object is deleted // in case of an exception. // Furthermore it makes copying an object very cheap and memory // management straightforward. // class TaQLNodeRep { public: // Define the various derived types (to be stored with AipsIO). //# They are easier to use than an enum. //# Do not change these definitions, since these values are stored in files. // #define TaQLNode_Null char(0) #define TaQLNode_Const char(1) #define TaQLNode_Unary char(2) #define TaQLNode_Binary char(3) #define TaQLNode_Multi char(4) #define TaQLNode_Func char(5) #define TaQLNode_Range char(6) #define TaQLNode_Index char(7) #define TaQLNode_KeyCol char(8) #define TaQLNode_Table char(9) #define TaQLNode_Col char(10) #define TaQLNode_Columns char(11) #define TaQLNode_Join char(12) #define TaQLNode_SortKey char(13) #define TaQLNode_Sort char(14) #define TaQLNode_LimitOff char(15) #define TaQLNode_Giving char(16) #define TaQLNode_UpdExpr char(17) #define TaQLNode_Select char(18) #define TaQLNode_Update char(19) #define TaQLNode_Insert char(20) #define TaQLNode_Delete char(21) #define TaQLNode_Calc char(22) #define TaQLNode_CreTab char(23) #define TaQLNode_ColSpec char(24) #define TaQLNode_RecFld char(25) #define TaQLNode_Unit char(26) #define TaQLNode_Regex char(27) #define TaQLNode_Count char(28) #define TaQLNode_Groupby char(29) #define TaQLNode_AltTab char(30) #define TaQLNode_AddCol char(31) #define TaQLNode_SetKey char(32) #define TaQLNode_RenDrop char(33) #define TaQLNode_AddRow char(34) #define TaQLNode_ConcTab char(35) #define TaQLNode_Show char(36) #define TaQLNode_CopyCol char(37) #define TaQLNode_DropTab char(38) // // Constructor for derived classes specifying the type. explicit TaQLNodeRep (int nodeType); virtual ~TaQLNodeRep(); // Letter objects cannot be copied. // TaQLNodeRep (const TaQLNodeRep&) = delete; TaQLNodeRep& operator= (const TaQLNodeRep&) = delete; // // Get the node type of the derived class. char nodeType() const { return itsNodeType; } // Get the TaQL style. const TaQLStyle& style() const { return itsStyle; } // Visit a node for tree traversal. virtual TaQLNodeResult visit (TaQLNodeVisitor&) const = 0; // Print the object in an ostream. virtual void show (std::ostream& os) const = 0; // Save the object. virtual void save (AipsIO& aio) const = 0; // Check the data type string and return its standard form. static String checkDataType (const String&); // Add escape characters to a table name where needed. String addEscape (const String& str) const; private: char itsNodeType; TaQLStyle itsStyle; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/TaQLNodeResult.h000066400000000000000000000076141476623553700203660ustar00rootroot00000000000000//# TaQLNodeResult.h: Classes holding the result of a node tree visit //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TAQLNODERESULT_H #define TABLES_TAQLNODERESULT_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Abstract base class to hold the result of a visit to the node tree. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeVisitor //
      • Note 199 describing // // TaQL // // // TaQLNodeResultRep is the abstract base class for classes holding // values filled by visitors to the raw TaQL parse tree. Visitors are // classes derived from TaQLNodeVisitor // which traverse the parse tree. // TaQLNodeResultRep is the counted referenced letter class in the envelope // class TaQLNodeResult. // class TaQLNodeResultRep { public: // Default constructor. TaQLNodeResultRep() {} // Destructor. virtual ~TaQLNodeResultRep() = default; // Letter objects cannot be copied. // TaQLNodeResultRep (const TaQLNodeResultRep&) = delete; TaQLNodeResultRep& operator= (const TaQLNodeResultRep&) = delete; // }; // // Envelope class to hold the result of a visit to the node tree. // // // // // //# Classes you should understand before using this one. //
      • TaQLNodeVisitor //
      • Note 199 describing // // TaQL // // // TaQLNodeResult is the envelope class for classes holding // values filled by visitors to the raw TaQL parse tree. Visitors are // classes derived from TaQLNodeVisitor // which traverse the parse tree. // The counted referenced letter base class for the envelope is // class TaQLNodeResultRep. // class TaQLNodeResult { public: // Default constructor has no letter. TaQLNodeResult() {} // Wrap the given pointer in a shared_ptr. TaQLNodeResult (TaQLNodeResultRep* rep) : itsRep (rep) {} // Does the envelope hold a letter? Bool isValid() const { return itsRep.get(); } // Get the actual underlying object. const TaQLNodeResultRep& getRep() const { return *(itsRep.get()); } private: std::shared_ptr itsRep; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/TaQLNodeVisitor.cc000066400000000000000000000026201476623553700206750ustar00rootroot00000000000000//# TaQLNodeVisitor.cc: Class to visit the nodes in the raw TaQL parse tree //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TaQLNodeVisitor::~TaQLNodeVisitor() {} } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/TaQLNodeVisitor.h000066400000000000000000000136251476623553700205460ustar00rootroot00000000000000//# TaQLNodeVisitor.h: Class to visit the nodes in the raw TaQL parse tree //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TAQLNODEVISITOR_H #define TABLES_TAQLNODEVISITOR_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Class to visit the nodes in the raw TaQL parse tree. // // // // // //# Classes you should understand before using this one. //
      • TaQLNode //
      • Note 199 describing // // TaQL // // // TaQLNodeVisitor is the abstract base class for classes that want to // visit a TaQLNode tree, i.e. traverse the tree. // Each visit results in a TaQLNodeResult object which acts as the basis // for the actual result object. //
        // A specialization of TaQLNodeVisitor (e.g. class // TaQLNodeHandler needs to implement // the various visitXXNode functions. A visit function will process a node // which usually means visiting its children, etc.. //
        // // The visitor design pattern separates the tree from the way it is processed. // In this way any handler can be created. For instance, a query optimizer // could be a future other handler. // class TaQLNodeVisitor { public: virtual ~TaQLNodeVisitor(); // Define the functions to visit each node type. // virtual TaQLNodeResult visitConstNode (const TaQLConstNodeRep& node) = 0; virtual TaQLNodeResult visitRegexNode (const TaQLRegexNodeRep& node) = 0; virtual TaQLNodeResult visitUnaryNode (const TaQLUnaryNodeRep& node) = 0; virtual TaQLNodeResult visitBinaryNode (const TaQLBinaryNodeRep& node) = 0; virtual TaQLNodeResult visitMultiNode (const TaQLMultiNodeRep& node) = 0; virtual TaQLNodeResult visitFuncNode (const TaQLFuncNodeRep& node) = 0; virtual TaQLNodeResult visitRangeNode (const TaQLRangeNodeRep& node) = 0; virtual TaQLNodeResult visitIndexNode (const TaQLIndexNodeRep& node) = 0; virtual TaQLNodeResult visitKeyColNode (const TaQLKeyColNodeRep& node) = 0; virtual TaQLNodeResult visitTableNode (const TaQLTableNodeRep& node) = 0; virtual TaQLNodeResult visitColNode (const TaQLColNodeRep& node) = 0; virtual TaQLNodeResult visitColumnsNode (const TaQLColumnsNodeRep& node) = 0; virtual TaQLNodeResult visitJoinNode (const TaQLJoinNodeRep& node) = 0; virtual TaQLNodeResult visitGroupNode (const TaQLGroupNodeRep& node) = 0; virtual TaQLNodeResult visitSortKeyNode (const TaQLSortKeyNodeRep& node) = 0; virtual TaQLNodeResult visitSortNode (const TaQLSortNodeRep& node) = 0; virtual TaQLNodeResult visitLimitOffNode (const TaQLLimitOffNodeRep& node) = 0; virtual TaQLNodeResult visitGivingNode (const TaQLGivingNodeRep& node) = 0; virtual TaQLNodeResult visitUpdExprNode (const TaQLUpdExprNodeRep& node) = 0; virtual TaQLNodeResult visitSelectNode (const TaQLSelectNodeRep& node) = 0; virtual TaQLNodeResult visitUpdateNode (const TaQLUpdateNodeRep& node) = 0; virtual TaQLNodeResult visitInsertNode (const TaQLInsertNodeRep& node) = 0; virtual TaQLNodeResult visitDeleteNode (const TaQLDeleteNodeRep& node) = 0; virtual TaQLNodeResult visitCountNode (const TaQLCountNodeRep& node) = 0; virtual TaQLNodeResult visitCalcNode (const TaQLCalcNodeRep& node) = 0; virtual TaQLNodeResult visitCreTabNode (const TaQLCreTabNodeRep& node) = 0; virtual TaQLNodeResult visitColSpecNode (const TaQLColSpecNodeRep& node) = 0; virtual TaQLNodeResult visitRecFldNode (const TaQLRecFldNodeRep& node) = 0; virtual TaQLNodeResult visitUnitNode (const TaQLUnitNodeRep& node) = 0; virtual TaQLNodeResult visitAltTabNode (const TaQLAltTabNodeRep& node) = 0; virtual TaQLNodeResult visitAddColNode (const TaQLAddColNodeRep& node) = 0; virtual TaQLNodeResult visitSetKeyNode (const TaQLSetKeyNodeRep& node) = 0; virtual TaQLNodeResult visitRenDropNode (const TaQLRenDropNodeRep& node) = 0; virtual TaQLNodeResult visitAddRowNode (const TaQLAddRowNodeRep& node) = 0; virtual TaQLNodeResult visitConcTabNode (const TaQLConcTabNodeRep& node) = 0; virtual TaQLNodeResult visitShowNode (const TaQLShowNodeRep& node) = 0; virtual TaQLNodeResult visitCopyColNode (const TaQLCopyColNodeRep& node) = 0; virtual TaQLNodeResult visitDropTabNode (const TaQLDropTabNodeRep& node) = 0; // protected: // A convenience function to visit the given node using this visitor. TaQLNodeResult visitNode (const TaQLNode& node) { return node.visit (*this); } }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/TaQLResult.cc000066400000000000000000000033611476623553700177110ustar00rootroot00000000000000//# TaQLResult.cc: Class to hold the result of a TaQL command //# Copyright (C) 2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include namespace casacore { TaQLResult::TaQLResult (const Table& table) : itsTable (table) {} TaQLResult::TaQLResult (const TableExprNode& node) : itsNode (node) {} const Table& TaQLResult::table() const { AlwaysAssert (isTable(), AipsError); return itsTable; } TableExprNode TaQLResult::node() const { AlwaysAssert (!isTable(), AipsError); return itsNode; } } //#NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/TaQLResult.h000066400000000000000000000051761476623553700175610ustar00rootroot00000000000000//# TaQLResult.h: Class to hold the result of a TaQL command //# Copyright (C) 2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TAQLRESULT_H #define TABLES_TAQLRESULT_H //# Includes #include #include namespace casacore { // // Class to hold the result of a TaQL command. // // // // // //# Classes you should understand before using this one. // // // The result of a TaQL command can be a Table or a TableExprNode. // This class holds the actual result. // // // It is possible to give a TaQL command resulting in a TableExprNode // to make it possible to make expressions of columns. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class TaQLResult { public: // Construct from a Table. explicit TaQLResult (const Table& = Table()); // Construct from a TableExprNode. explicit TaQLResult (const TableExprNode&); // Is the result a Table? Bool isTable() const { return itsNode.isNull(); } // Return the result as a TableExprInfo. // It throws an exception if it is not a table. const Table& table() const; // Return the result as a TableExprNode. // It throws an exception if it is not a TableExprNode. TableExprNode node() const; private: Table itsTable; TableExprNode itsNode; }; } #endif casacore-3.7.1/tables/TaQL/TaQLShow.cc000066400000000000000000001726751476623553700173720ustar00rootroot00000000000000//# TaQLShow.cc: Class to get various TaQL-related info //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include using namespace std; namespace casacore { //# NAMESPACE CASACORE - BEGIN // This function concatenates the help info below to a single string. String concHelp (const char* str[], size_t n) { std::string s; for (size_t i=0; i' for more information about a command.", " 'show expr(essions)' for more information about forming expressions.", "See http://casacore.github.io/casacore-notes/199.html for full info.", }; const char* selectHelp[] = { " [WITH table_list]", "SELECT", " [[DISTINCT] expression_list]", " [INTO table [AS options]]", " [FROM table_list]", " [JOIN table_list ON expression [JOIN table_list ON expression ...]]", " [WHERE expression]", " [GROUPBY expression_list]", " [HAVING expression]", " [ORDERBY [DISTINCT] [ASC|DESC] sort_list]", " [LIMIT expression] [OFFSET expression]", " [GIVING table [AS options] | set]", " [DMINFO datamanagers]", "", "WITH table_list", " Optional list of temporary tables to factor out multiply used subqueries.", " E.g., WITH [select from some.tab WHERE expr] AS t1", " creates a temporary table that can be used as table 't1' further down.", "", "[DISTINCT] expression-list", " Optional list of expressions (possibly containing aggregate functions).", " An expression can be followed by 'AS newname [datatype]' to define a name for", " the resulting column and its data type (defaults to the expression type).", " Possible data types: B, U1, I2, U2, I4, U4, I8, R4, R8, C4, C8, S, EPOCH", " Use 'show datatypes' to get more information about the possible data types.", " A masked array expression can be stored in 2 columns like", " expression AS (datacolumn,maskcolumn) [datatype]", " where datatype applies to the datacolumn (the maskcolumn is always Bool).", " A regex (see 'show constants') can be used at any place in the expression-list", " to include or exclude columns.", " For example: !~p/*DATA/ to exclude all columns ending in DATA", " DISTINCT (or UNIQUE) removes output rows with equal values.", " If no expression-list is given, all columns of the first table are selected.", "", "INTO table [AS options]", " Name of resulting table; if not given, no output table is created.", " Possible options:", " TYPE = PLAIN,SCRATCH,MEMORY", " If TYPE is not given, a reference table is made if no expressions", " are given in the SELECT clause, otherwise a plain table is made.", " ENDIAN = BIG,LITTLE,LOCAL,AIPSRC default AIPSRC", " STORAGE = SEPFILE,MULTIFILE,MULTIHDF5,DEFAULT,AIPSRC default AIPSRC", " BLOCKSIZE = ", " OVERWRITE = T|F default T", " Use 'show tableoptions' to get more information about the possible options.", "", "FROM table_list", " One or more input tables to use. Each table can be followed by a", " shorthand to be used to qualify a column or keyword in an expression.", " A table can be one of the following:", " - a tablename proper, possibly containing ~ and $", " Use :: (not /) to name a subtable (e.g., my.ms::ANTENNA)", " - a subquery enclosed in parentheses or brackets", " - concatenated tables; separated by commas enclosed in brackets", " - backreference to an earlier created table using its shorthand", " If FROM is not given, an empty table with LIMIT (default 1) rows is used.", "", "JOIN table_list ON expression ...", " Zero or more joins can be done to connect tables. For instance, in a MeasurementSet", " the main table can be joined with the ANTENNA subtable to find antenna names.", " The tables in the list are the join tables, while the tables in the FROM table_list", " or in a previous join are the main tables. The join tables can be specified in the", " same way as described in the FROM clause above.", " The expression must be a boolean scalar expression doing one or more comparisons", " separated by AND. Each comparison matches a column in the main and join table.", " A comparison can be the equality operator = or the interval operator IN,", " where the interval must be in the join table. For example:", " SELECT t1.ANTENNA1, t2.NAME FROM my.ms t1", " JOIN ::ANTENNA t2 on t1.ANTENNA1=t2.rownumber()", " selects the antenna number and its name by joining the antenna number with the row", " number in the ANTENNA subtable (where the row number is the implicit key).", " A more complicated join is given below. It finds information in the SYSCAL table", " based on antenna, spectral window and time.", " JOIN ::DATA_DESC t2 ON t1.DATA_DESC_ID==t2.rownumber() JOIN ::SYSCAL t3 ON", " t1.ANTENNA1==t3.ANTENNA_ID and t2.SPECTRAL_WINDOW_ID==t3.SPECTRAL_WINDOW_ID and", " t1.TIME AROUND t3.TIME IN t3.INTERVAL", "", "WHERE expression", " An expression resulting in a boolean scalar telling if a row is selected.", " If not given, all rows will be selected.", "", "GROUPBY expression_list", " It can be used to group rows with equal values for the given", " expressions (which must result in scalar values).", " Often aggregate functions (such as GSUM) are used in the SELECT and/or", " HAVING clause to calculate an aggregate value, but in something like", " select from my.ms groupby ANTENNA1,ANTENNA2", " GROUPBY is used to get the number of unique baselines in the MS.", "", "HAVING expression", " Similar to WHERE, but is a filter on the result of GROUPBY.", " A column in the expression can be a column created in the SELECT clause.", "", "ORDERBY [DISTINCT] [ASC|DESC] sort_list", " It specifies how the output of the SELECT clause has to be ordered.", " The sort list is a list of expressions (resulting in scalar values),", " optionally followed by ASC or DESC to define the order for that sort key.", " A column in an expression can be a column created in the SELECT clause.", " ASC (default) or DESC given before the sort list defines the default order.", " DISTINCT (or UNIQUE) removes all rows with equal sort keys.", "", "LIMIT expression OFFSET expression", "LIMIT start:end:step", " can be used to limit the number of output rows.", " The first form is in fact the same as 'offset:offset+limit:1'", " start first row to use; defaults to 0", " end last row (exclusive); defaults to all rows", " step take every step-th row; defaults to 1", " start and end can be negative to count from the end (a la python).", " This is also true for the expressions in the first form.", "", "GIVING table [AS options] | set", " Defines the output table, which is an alternative for the INTO clause.", " It is also possible to specify a set containing a column or interval,", " which can be used to define the result of a nested query.", "", "DMINFO datamanagers", " can be used by expert users to define data managers for the output columns.", " Use 'show dminfo' for more information." }; const char* calcHelp[] = { " [WITH table_list]", "CALC expression [FROM table_list]", "", "This command evaluates the given expression, which can contain columns from", "the given tables. It is basically the same as", " SELECT expression [FROM table_list]", "but does not create an output table as SELECT always does." }; const char* updateHelp[] = { " [WITH table_list]", "UPDATE", " table_list", " SET update_list", " [FROM table_list]", " [WHERE expression]", " [ORDERBY [DISTINCT] [ASC|DESC] sort_list]", " [LIMIT expression] [OFFSET expression]", "", "table_list", " The first table in the table_list is updated. More tables can be given to be", " used in expressions, but preferably these tables are given in the FROM part.", "", "SET update_list", " The update_list looks like:", " COLUMN=expression, COLUMN=expression, ...", " COLUMN is the name of the column to be updated with the expression value.", " If the column contains scalars, the expression must be a scalar.", " Arrays in columns can be set in various ways:", " - If set to a scalar, all array elements in the column are set to the scalar.", " - If set to an array, each array is replaced by the new one, possibly with a", " different shape.", " It is possible to update part of an array by slicing and/or masking it like:", " COLUMN[start:end:step] or COLUMN[mask]", " If a mask is used, only array values with a True mask value will be updated.", " It is possible to apply slicing and masking in succession (in any order).", " Array and mask can be updated jointly like", " (DATACOLUMN,MASKCOLUMN)=expression", " if expression is a masked array.", " Slicing and masking can only be applied to both of them like:", " (COLUMN,MASKCOLUMN)[start:end:step] or (COLUMN,MASKCOLUMN)[mask]", "", "FROM table_list", " Extra tables used in expressions (can also be given in the first table_list).", " A table can be any type (as in the FROM clause of the SELECT command).", "", "WHERE, ORDERBY, LIMIT, OFFSET", " Using these clauses (similar to SELECT) a subset of the table can be updated." }; const char* insertHelp[] = { " [WITH table_list]", "INSERT INTO table_list SET column=expr, column=expr, ...", " Add one row.", "INSERT INTO table_list [(column_list)] VALUES (expr_list),(expr_list),... [LIMIT n]", " Add n rows.", "INSERT INTO table_list [(column_list)] SELECT_command", " Add selected rows from another (or same) table.", "", "table_list", " Rows are added to the first table in the list. More tables can be given", " to be used in expressions. Any table form can be used as in SELECT FROM.", "", "SET column=expr, column=expr, ...", " Specify the column values in the new row in the same way as for UPDATE.", "", "(column_list)", " Specify the column names (enclosed in parentheses).", " The form (DATACOLUMN,MASKCOLUMN) can be used to store a masked array.", " If (column_list) is omitted, it defaults to all stored columns in the table.", "", "(expr_list)", " List of expressions separated by commas enclosed in parentheses.", " Each expr_list is a row to be added.", "", "LIMIT expression", " defines the number of rows to add. It defaults to the number of (expr_list)", " given. If different, it iterates through the expr_lists as needed.", "", "SELECT_command", " The rows resulting from the SELECT command are added.", " The resulting column names and data types have to match the column_list,", " but their order can differ." }; const char* deleteHelp[] = { " [WITH table_list]", "DELETE FROM table_list", " [WHERE ...] [ORDERBY ...] [LIMIT ...] [OFFSET ...]", "", "table_list", " Rows matching the selection criteria are removed from the first table", " in the table_list. More tables can be given to be used in expressions.", "", "WHERE, ORDERBY, LIMIT, OFFSET", " Only rows matching these selection criteria are removed.", " See 'help command select' for a brief description of these clauses." }; const char* createHelp[] = { " [WITH table_list]", "CREATE TABLE table [AS options]", " [LIKE other_table [DROP COLUMN column_list]]", " [[ADD COLUMN] (column_specs)]", " [LIMIT expression]", " [DMINFO datamanagers]", "", "table [AS options]", " The name of the table to create followed by optional table options (enclosed in", " square brackets) controlling how the table is created.", " Possible options:", " ENDIAN = BIG,LITTLE,LOCAL,AIPSRC default AIPSRC", " STORAGE = SEPFILE,MULTIFILE,MULTIHDF5,DEFAULT,AIPSRC default AIPSRC", " BLOCKSIZE = ", " OVERWRITE = T|F default T", " Use 'show tableoptions' to get more information about the possible options.", "", "LIKE other_table DROP COLUMN column_list", " The new table is created with the same description and dminfo as other_table.", " other_table can be followed by a shorthand to be used in other command parts.", " Optionally DROP COLUMN column_list can be given to omit the given columns.", " It is possible to add columns in the column_specs.", "", "[ADD COLUMN] (column_specs)", " A list of column specifications separated by commas and enclosed in parentheses (or", " square brackets) and preceded by ADD COLUMN. Both can be left out if LIKE", " is not used. Each spec looks like:", " name [LIKE other_name] [datatype] [prop_list]", " name Name of the column.", " other_name Name of the column whose description is used for the new column.", " It can be from another table using the 't.column' syntax.", " The description can be modified using the datatype and prop_list arguments.", " datatype Data type (B, U1, I2, U2, I4, U4, I8, R4, R8, C4, C8, S, EPOCH).", " Use 'show datatypes' for more information about data types.", " It must be given if 'LIKE other_name' is not given.", " prop_list Optional key=value list of other properties. Enclose in square", " brackets if multiple key=value pairs are given.", " NDIM=n dimensionality", " SHAPE=[n1,n2,...] fixed shape; NDIM must match if also given", " UNIT='string' unit of values in the column", " COMMENT='string' comment string describing the column", "", "LIMIT expression", " Number of rows in the new table. No rows if omitted.", "", "DMINFO datamanagers", " can be used by expert users to define data managers for the columns.", " Use 'show dminfo' for more information." }; const char* alterHelp[] = { " [WITH table_list]", "ALTER TABLE table [FROM table_list] subcommand1 subcommand2 ...", " Alter the table with the given name.", " The FROM part can be given to use other tables in the subcommands.", " One or more of the following subcommands can be given.", "", " ADD COLUMN [column_specs] [DMINFO datamanagers]", " Add one or more columns to the table.", " Use 'show command create' to see the syntax for the arguments.", "", " COPY COLUMN newcol=other, newcol=other, ... [DMINFO datamanagers]", " Copy the data of a column to a new column, which gets the same description.", " By default it gets the same data manager, but can be overwritten using DMINFO.", " Use 'show dminfo' for the syntax of the DMINFO argument.", "", " RENAME COLUMN old TO new, old TO new, ...", " Rename one or more columns.", "", " DROP COLUMN col, col, ...", " Remove one or more columns.", "", " SET KEYWORD key=value [AS dtype], key=value [AS dtype], ...", " Add or reset one or more table and/or column keyword values.", " A column keyword name must be preceded by the column name like col::key.", " Nested keyword names can be used (e.g., col::key.subkey.fld)", " The value can be any expression, also an empty array given as [].", " A data type can be given, which defaults to the expression data type.", " Use 'show datatypes' to see the possible data types.", " The value cannot be a record, but an empty record can be given as [=].", "", " COPY KEYWORD key=other, key=other, ...", " Copy the value of a table and/or column keyword to a new keyword.", " The keyword can be a nested one, thus a field in a record value.", " The value can be anything, also a record.", "", " RENAME KEYWORD old TO new, old TO new, ...", " Rename one or more table and/or column keywords, possibly nested ones.", " The new name cannot be qualified with a column or nested keyword name,", " thus renaming cannot be done across columns or so.", "", " DROP KEYWORD key, key, ...", " Remove one or more table and/or column keywords, possibly nested ones.", "", " ADD ROW expression", " The expression gives the number of rows to be added to the table." }; const char* countHelp[] = { " [WITH table_list]", "COUNT [column_list] FROM table_list [WHERE expression]", "", "After having done the WHERE selection, it counts the number of rows", "in the first table for each group formed by the columns in the list.", "It creates a table with the columns in the column list and the column", "_COUNT_ containing the number of rows in each group.", "", " COUNT col1,col2 FROM my.ms WHERE expression", "is the same as", " SELECT col1,col2,gcount() as _COUNT_ FROM my.ms", " WHERE expression GROUPBY col1,col2", "", "SELECT/GROUPBY is much more powerful, but the COUNT command can still be used." }; const char* exprHelp[] = { "A TaQL expression can use scalar and/or arrays (as in numpy).", "The following elements can be used:", " operators see 'show oper(ators)'", " functions see 'show func(tions)'", " constants see 'show const(ants)'", " (scalar and arrays of various data types)", " sets/intervals see 'show sets'", " units see 'show units'", " columns see 'show table '", " keywords see 'show table tabkey colkey'", "", "- A unit can be given after each (sub)expression; if needed conversion is done.", " For example: (1e9Hz + 1GHz)MHz results in 2000 MHz", "- Columns and keywords can be taken from any table given in a TaQL command", " by preceding their names with a table shorthand (e.g., t0.DATA)", "- A keyword can be a table keyword or a column keyword (e.g., col::key)", " Nested keywords can be used (e.g., col::key.subkey.fld)", "- In the HAVING and ORDERBY clause an expression can use a column", " created in the SELECT clause.", "- Aggregate functions are only possible in the SELECT and HAVING clause." }; const char* operHelp[] = { "Available TaQL operators in order of precedence (high to low):", " **", " ! ~ + - (unary operators)", " * / // %", " + -", " &", " ^", " |", " == != > >= < <= ~= !~= IN INCONE BETWEEN AROUND EXISTS LIKE ~ !~", " &&", " ||", "", "Some operators have a synonym:", " == =", " != <>", " && AND", " || OR", " ! NOT", " ^ XOR", "", "Description of some operators:", " ** power, right-associative, thus 2**1**2 = 2", " ~ if unary, bit-wise complement", " / real division, thus 1/2 = 0.5", " // integer division, thus 1//2 = 0", " % modulo as in Python; sign of divisor", " + also string concat; also add days to datetime", " - also for datetime (results in unit d)", " & bit-wise and", " ^ bit-wise xor", " | bit-wise or", " ~= !~= (not) about equal (relative to 1e-5)", " ~ !~ (I)LIKE pattern match", " IN is left hand an element in the right hand?", " INCONE cone searching", " EXISTS does subquery have at least 1 match?", " x BETWEEN a AND b is x in bounded interval ?", " x AROUND m IN w is x in bounded interval ?" }; const char* constHelp[] = { "Scalar constants of following data types:", " bool TRUE or FALSE (case-insensitive), T or F", " int integer; also hexadecimal like 0xffff", " double 12. 12e5 3.2e-5 etc.", " value followed by a unit like 10m or 10.5sec", " position/time in HMS or DMS like 2h13m44.5 or 30d13m44.4", " results in double with unit rad", " complex add imaginary part like 1+2i or 3 - 5j", " string enclose in single or double quotes; concatenation like 'ab'\"cd\"", " datetime date/time like 3Mar16/12:14:23.3 or 2016-03-02/1:4:23", " - or space can be used instead of /", " regex p/globpattern/ or f/regex/ or m/regex/ (same as f/.*regex.*/)", " used with operator ~ or !~", " % or @ can also be used as regex delimiter", "", "N-dim array of those data types (except regex) like:", " [1,2,3] (1-dim) or [[1,2,3],[4,5e3,6]] (2-dim)", " or using function ARRAY", "", "Masked array (True value means bad (as in numpy)):", " array[mask] such as [1,2,3][[T,F,T]]", " or using function MARRAY" }; const char* dtypeHelp[] = { "Internally TaQL supports the data types bool, int64, double, dcomplex,", "string, regex and datetime.", "'show constants' shows how to define constants for those types.", "", "There is more choice for the data type of a table column.", "When creating a table (using e.g. the SELECT or CREATE TABLE command), the", "data types of the columns can be defined using one of the following words.", "", " B BOOL BOOLEAN", " U1 UCHAR BYTE", " I2 SHORT SMALLINT", " U2 UI2 USHORT USMALLINT", " I4 INT INTEGER", " U4 UI4 UINT UINTEGER", " I8 LONG BIGINT", " R4 FLT FLOAT", " R8 DBL DOUBLE", " C4 FC FCOMPLEX COMPLEX", " C8 DC DCOMPLEX", " S STRING", " EPOCH", "", "EPOCH can be used for a datetime value. It uses a column with data type", "double and sets the column keywords defining the Measure type MEpoch." }; const char* taboptHelp[] = { "One or more of the following key=value options can be used to specify", "how a table has to be created. If multiple options are given, they have", "to be enclosed in square brackets separated by commas.", "", " TYPE='value' specifies the table type.", " PLAIN = make a persistent table, thus a true copy of all", " selected rows/columns.", " SCRATCH = as plain, but only as a temporary table.", " MEMORY = as plain, but keep everything in memory.", "", " ENDIAN='value' specifies the endianness.", " BIG = big endian", " LITTLE = little endian", " LOCAL = native endianness of the machine being used", " AIPSRC = as defined in the .casarc file (usually defaults to LOCAL)", " If ENDIAN is not given, it defaults to AIPSRC.", "", " STORAGE='value' specifies the storage type.", " SEPFILE = store as separate files (the old Casacore table format)", " MULTIFILE = combine all storage manager files into a single file", " MULTIHDF5 = as MULTIFILE, but use HDF5 instead of a regular file", " DEFAULT = use SEPFILE (might change in a future Casacore version)", " AIPSRC = as defined in the .casarc file (usually defaults to DEFAULT)", " If STORAGE is not given, it defaults to AIPSRC.", "", " BLOCKSIZE=n the blocksize (in bytes) to use for MULTIFILE or MULTIHDF5", "", " OVERWRITE=T|F overwrite an existing table? Default is T" }; const char* dminfoHelp[] = { "DMINFO [NAME=name, TYPE=type, SPEC=[...] COLUMNS=[col1, col2, ...]], ...", " defines the data managers to be used by columns. It is a comma separated", " list of key=value definitions enclosed in square brackets.", " NAME=name defines the unique data manager name.", " TYPE=type defines the type; StandardStMan, TiledShapeStMan, etc.", " COLUMNS=[...] is a comma separated list of the names of the columns to", " be stored with this data manager.", " SPEC=[...] is a datamanager-specific key=value list defining the", " data manager parameters. If not given, defaults are used.", " Tiled storage managers:", " DEFAULTTILESHAPE default tile shape (in Fortran order!!)", " MAXCACHESIZE maximum tile cache size (0=no limit)", " IncrementalStMan:", " BUCKETSIZE size of bucket (in bytes)", " StandardStMan:", " BUCKETSIZE size of bucket in bytes", " or BUCKETROWS size of bucket in rows", " VirtualTaQLColumn:", " TAQLCALCEXPR expression (using other columns)", " BitFlagsEngine:", " SOURCENAME name of the boolean source column", " TARGETNAME name of the integer target column", " ReadMask integer defining flags to use on read", " or ReadMaskKeys vector with flag names to use on read", " WriteMask integer defining flags to use on write", " or WriteMaskKeys vector with flag names to use on write", " There are several more data managers, also external ones such as LofarStMan.", " For example:", " dminfo [NAME='ISM1',TYPE='IncrementalStMan',COLUMNS=['col1']],", " [NAME='SSM1',TYPE='StandardStMan',", " SPEC=[BUCKETSIZE=1000],COLUMNS=['col2','col3']]", " defines 2 data managers (one for col1 and the other for col2 and col3)." }; const char* setHelp[] = { "A set is a series of values, ranges and/or intervals enclosed in brackets.", "Often the IN operator is used on a set, but a set can also be used as an array.", "", "A value can be of type integer, double, complex, datetime or string.", "Numeric data types can be mixed; the 'highest' type is used.", "", "A range is a series of values written as start:end:step", " 'start' can be omitted and defaults to 0", " 'end' can be omitted making it unbounded (till infinity)", " ':step' can be omitted and defaults to 1", "start and end can be integer, double or datetime", "step must be integer or double and can be negative (as in Python)", "", "An interval is a continuous set of real values with optional bounds.", "If a bound is given, it can be open or closed.", "An interval can be given in various ways:", " as start-end using curly braces (closed side) and angle brackets (open side)", " bounded: {1,2} <1,2> {1,2> <1,2}", " unbounded: {1,} <1,> {,2> <,2}", " as start-end using a=:=b (closed sides) and a<:width or AROUND mid IN width", "", "A set consisting of values and/or bounded ranges is a 1-dim array.", " For example: [1,2,3,4,5] [1:6] [1,2:5,5] are all the same", "Multi-dimensional arrays can be given as nested sets.", " For example: [[1,2,3],[4:7]] defines an array with shape [2,3]", "", "A set can be created from a subquery as used in:", " ANTENNA1 IN [select rowid() from ::ANTENNA where NAME~p/CS*/]", }; const char* allFuncHelp[] = { "About all TaQL functions operate on scalars and arrays (and mixed)", "math functions:", " pi e c rand", " sin sinh asin cos cosh acos", " tan tanh atan atan2", " exp log log10", " pow sqrt sqr cube", " norm abs arg fmod", " sign round floor ceil", "logical functions:", " near nearabs isnan isinf isfinite", " isdef isnull iscol iskey", " min max iif", "conversion functions:", " bool int real imag complex conj", " string hms dms hdms", "datetime functions:", " datetime mjdtodate mjd date time", " year month day weekday week", " cmonth cweekday cdatetime cdate ctime", "string functions:", " strlength upcase downcase capitalize sreverse", " trim ltrim rtrim substr replace", " regex pattern sqlpattern", "array functions:", " array ndim nelem shape", " transpose areverse resize diagonal", " nullarray marray arraydata mask", " flatten negatemask replacemasked replaceunmasked", "reduce functions:", " sum product sumsqr min max mean", " variance samplevariance stddev samplestddev avdev rms", " median fractile any all ntrue nfalse", " plural, running and boxed forms of above reduce functions", "astro functions:", " angdist angdistx normangle cones anycone findcone", " see also 'show func meas' and 'show func mscal'", "misc functions:", " msid rownr rowid", "aggregate functions:", " gmin gmax gsum gproduct gsumsqr gmean", " gvariance gsamplevariance gstddev gsamplestddev grms", " gany gall gntrue gnfalse", " plural forms of above aggregate functions (e.g., gmins)", " gmedian gfractile ghist gstack", " countall gcount gfirst glast " }; const char* mathFuncHelp[] = { "Mathematical functions", "", " double PI ()", " double E ()", " double C () m/s", " double RAND ()", " numeric SIN (numeric)", " numeric SINH (numeric)", " double ASIN (real) rad", " numeric COS (numeric)", " numeric COSH (numeric)", " double ACOS (real) rad", " double TAN (real)", " double TANH (real)", " double ATAN (real) rad", " double ATAN2 (real y, real x) rad", " numeric EXP (numeric)", " numeric LOG (numeric)", " numeric LOG10 (numeric)", " numeric POW (numeric, numeric exp)", " numeric SQRT (numeric)", " numeric SQR (numeric) aka SQUARE", " numeric CUBE (numeric)", " real NORM (numeric)", " real ABS (numeric) aka AMPLITUDE", " double ARG (numeric) aka PHASE", " real FMOD (real, real) modulo as in C; sign of dividend", " real SIGN (real)", " real ROUND (real) round(-1.6) = -2", " real FLOOR (real) floor(-2.2) = -3", " real CEIL (real) ceil (-2.2) = -2" }; const char* convFuncHelp[] = { "Conversion functions", "", " string HMS (real RAD) convert angles to e.g. 12h34m56.789", " string DMS (real RAD) convert angles to e.g. 12d34m56.789", " string HDMS (realarray) convert angles alternately to HMS and DMS", " string STR (string, int WIDTH) make string WIDTH long", " string STR (numeric, ) make string WIDTH long", " string STR (string, int WIDTH) make string WIDTH long" }; const char* logicalFuncHelp[] = { "Logical functions", "", " bool NEAR (numeric, numeric, double tol) relative near", " bool NEARABS (numeric, numeric, double tol) absolute near", "", " bool ISNAN (numeric val) is value Not-a-Number?", " bool ISINF (numeric val) is value infinite?", " bool ISFINITE (numeric val) is value finite?", " bool ISNULL (anytype) is array a null array?", " bool ISDEFINED (anytype) contains row a value?", " bool t.ISCOLUMN (string) does keyword exist in table?", " bool t.ISKEYWORD (string) does keyword exist in table?", "", " numeric MIN (numeric, numeric)", " numeric MAX (numeric, numeric)", " anytype IIF (bool cond, arg1, arg2) arg1 if cond is True, else arg2" }; const char* dateTimeFuncHelp[] = { "Date/time functions", "Functions taking a datetime, use current UTC date/time if not given", "", " datetime DATETIME (string) convert string to datetime", " datetime MJDTODATE (real) convert MJD to datetime", " double MJD (datetime) convert datetime to MJD", " datetime DATE (datetime) get date, thus remove time part", " double TIME (datetime) get time (in rad), thus remove date part", " int YEAR (datetime) get year", " int MONTH (datetime) get month (1..12)", " int DAY (datetime) get day (1..31)", " int WEEK (datetime) get week number (0..53)", " int WEEKDAY (datetime) aka DOW weekday (1=Monday .. 7=Sunday)", " string CDATETIME (datetime) aka CTOD YYYY/MM/DD/HH:MM:SS.SSS", " string CDATE (datetime) DD-MMM-YYYY", " string CTIME (datetime) HH:MM:SS.SSS", " string CMONTH (datetime) Jan..Dec", " string CWEEKDAY (datetime) aka CDOW Mon..Sun" }; const char* stringFuncHelp[] = { "String functions", "", " int LEN (string) aka STRLENGTH", " string UPCASE (string) aka UPPER", " string DOWNCASE (string) aka LOWER", " string CAPITALIZE (string)", " string SREVERSE (string) reverse the string (aka REVERSESTRING)", " string TRIM (string) remove leading/trailing whitespace", " string LTRIM (string) remove leading whitespace", " string RTRIM (string) remove trailing whitespace", " string SUBSTR (string, int START, int N) START<0 means from the end", " string REPLACE (string SRC, string TOREPLACE[, string REPLACEMENT])", " string REPLACE (string SRC, regex TOREPLACE[, string REPLACEMENT])", "", " regex REGEX (string) make regex from regular expression string", " regex PATTERN (string) make regex from glob pattern string", " regex SQLPATTERN (string) make regex from SQL-style pattern string", "", "'show func conversion' for functions converting values to string", "'show func datetime' for functions converting date/time to string" }; const char* arrayFuncHelp[] = { "Array creation/manipulation functions", "", " array ARRAY (value, shape) create array and fill with value", " int NELEMENTS (any) aka COUNT size of array (1 for scalar)", " int NDIM (any) dimensionality of array (0 for scalar)", " array SHAPE (any) shape of array (empty for scalar)", " array TRANSPOSE (array [,axes]) transpose array", " if axes are given, only those axes are transposed", " array AREVERSE (array [,axes]) reverse array elements of given axes", " if no axes are given, elements of all axes are reversed", " array RESIZE (array, shape [,mode]) resize an array", " no mode: copy corresponding elements", " mode=0 : upsampling; copy values evenly if axis gets larger: 1,2 -> 1,1,2,2", " mode=1 : repeat values if axis gets larger: 1,2 -> 1,2,1,2", " array DIAGONAL (array [,firstaxis [,diag]])", " diagonal of each 2-dim subarray (at axis firstaxis)", " diag=0 main diagonal; <0 below main diagonal; >0 above", "", " array NULLARRAY (value) create null array", " array MARRAY (array, boolarray) create masked array", " same as 'array[boolarray]'", " array ARRAYDATA (array) array without possible mask", " bool ARRAYMASK (array) aka MASK mask of masked array", " array FLATTEN (array) remove masked elements", " array NEGATEMASK(array) negate mask in masked array", " array REPLACEMASKED (arr1, arr2)", " replace masked elements in arr1 by corresponding value in arr2", " array REPLACEUNMASKED (arr1, arr2)", " replace unmasked elements in arr1 by corresponding value in arr2" }; const char* reduceFuncHelp[] = { "Array reduce functions (use unmasked elements only)", " XXX (array) reduces to a scalar", " XXXS (array, reduceAxes) reduces to a (N-M)-dim array", " RUNNINGXXX (array, windowSize) calculates XXX in sliding window", " BOXEDXXX (array, boxSize) calculates XXX for each box", "", "XXX can be one of the following functions:", " bool ANY (bool) is any element true?", " bool ALL (bool) are all elements true?", " int NTRUE (bool) number of true elements", " int NFALSE (bool) number of false elements", " numeric SUM (numeric) sum of all elements", " numeric SUMSQR (numeric) sum of all squared elements aka SUMSQUARE", " numeric PRODUCT (numeric) product of all elements", " real MIN (real) minimum of all elements", " real MAX (real) maximum of all elements", " numeric MEAN (numeric) mean of all elements aka AVG", " double VARIANCE (numeric) population variance complex variance", " double SAMPLEVARIANCE(numeric) sample variance uses absolute value", " double STDDEV (numeric) population standard deviation (is sum of real and", " double SAMPLESTDDEV(numeric) sample standard deviation imaginary variances)", " double AVDEV (numeric) average deviation", " double RMS (real) root-mean-square", " double MEDIAN (real) median (the middle element)", " double FRACTILE (real, fraction) element at given fraction" }; const char* astroFuncHelp[] = { "Astronomical functions", "", " double ANGDIST (arg1,arg2) aka ANGULARDISTANCE", " angular distance (in rad) between corrersponding positions in arg1 and arg2", " arg1 and arg2 must be arrays containing ra/dec or lon/lat pairs", " double ANGDISTX (arg1,arg2) aka ANGULARDISTANCEX", " same as ANGDIST, but between all positions in arg1 and arg2", " double NORMANGLE (arg1)", " normalize an angle between -pi and pi radians", " bool ANYCONE (source, cones)", " True if source in at least one of the cones", " synonym for operator INCONE", " bool ANYCONE (source, conepos, radii)", " same as above, but cone centers and radii are given separately", " each radius is applied to each cone", " int FINDCONE (sources, cones)", " index of the first cone containing a source", " if a single source is given, the result is a scalar, otherwise an array", " int FINDCONE (sources, conepos, radii)", " same as above, but cone centers and radii are given separately", " each radius is applied to each cone", " bool CONES (sources, cones)", " 2-dim bool array telling for each source if in each cone", " bool CONES (sources, conepos, radii)", " 3-dim bool array telling for each source if in each cone and radius", "", "'show func meas' for measures functions converting between reference frames", "'show func mscal' for mscal functions handling measures in MeasurementSets", }; const char* miscFuncHelp[] = { "Miscellaneous functions", "", " int ROWNR() aka ROWNUMBER return row number in current table", " int ROWID() return row number in input table", " MSID(column) use column if existing, otherwise ROWID()", }; const char* aggrFuncHelp[] = { "Aggregate functions operating per group (using GROUPBY)", "", "The following functions result in a scalar value", " int GCOUNT() number of rows aka GCOUNT(*)", " int GCOUNT (columnname) number of rows for which the column has a value", " anytype GFIRST (anytype) first value in the group", " anytype GLAST (anytype) last value of the group", " bool GANY (bool) is any element true?", " bool GALL (bool) are all elements true?", " bool GNTRUE (bool) number of true elements", " int GNFALSE (bool) number of false elements", " numeric GSUM (numeric) sum of all elements", " numeric GSUMSQR (numeric) sum of all squared elements aka GSUMSQUARE", " numeric GPRODUCT (numeric) product of all elements", " real GMIN (real) minimum of all elements", " real GMAX (real) maximum of all elements", " numeric GMEAN (numeric) mean of all elements aka GAVG", " double GVARIANCE (numeric) population variance complex variance", " double GSAMPLEVARIANCE(numeric) sample variance uses absolute value", " double GSTDDEV (numeric) population standard deviation (is sum of real and", " double GSAMPLESTDDEV(numeric) sample standard deviation imaginary variances)", " double GRMS (real) root-mean-square", " double GMEDIAN (real) median (the middle element)", " double GFRACTILE (real, fraction) element at given fraction", "", "The following functions result in an array and operate element by element", " GANYS GALLS GNTRUES GNFALSES", " GSUMS GSUMSQRS GPRODUCTS GMINS GMAXS GMEANS", " GVARIANCES GSAMPLEVARIANCES GSTDDEVS GSAMPLESTDDEVS GRMSS", "", "The following functions result in an array", " double GHIST (data, nbin, start, end) histogram of the data", " anytype GSTACK (anytype) stack the data to an array aka GAGGR" }; const char* positionHelp[] = { "Position types:", " ITRF", " WGS84" }; const char* epochHelp[] = { "Epoch types:", " LAST Local Apparent Sidereal Time", " LMST Local Mean Sidereal Time", " GMST1, GMST Greenwich Mean ST1", " GAST Greenwich Apparent ST", " UT1, UT Universal Time", " UT2 Universal Time", " UTC Coordinated Universal Time", " TAI, IAT International Atomic Time", " TDT, TT, ET Terrestrial Dynamical Time", " TCG Geocentric Coordinate Time", " TDB Barycentric Dynamical Time", " TCB Barycentric Coordinate Time" }; const char* directionHelp[] = { "Direction types:", " J2000 mean equator and equinox at J2000.0 (FK5)", " JNAT geocentric natural frame", " JMEAN mean equator and equinox at frame epoch", " JTRUE true equator and equinox at frame epoch", " APP apparent geocentric position", " B1950 mean epoch and ecliptic at B1950.0", " B1950_VLA mean epoch(1979.9)) and ecliptic at B1950.0", " BMEAN mean equator and equinox at frame epoch", " BTRUE true equator and equinox at frame epoch", " HADEC topocentric hourangle and declination", " AZEL topocentric Azimuth and Elevation (N through E)", " AZELNE topocentric Azimuth and Elevation (N through E)", " AZELSW topocentric Azimuth and Elevation (S through W)", " AZELGEO geodetic Azimuth and Elevation (N through E)", " AZELNEGEO geodetic Azimuth and Elevation (N through E)", " AZELSWGEO geodetic Azimuth and Elevation (S through W)", " ECLIPTIC ecliptic for J2000 equator and equinox", " MECLIPTIC ecliptic for mean equator of date", " TECLIPTIC ecliptic for true equator of date", " GALACTIC galactic coordinates", " SUPERGAL supergalactic coordinates", " ITRF coordinates wrt ITRF Earth frame", " TOPO apparent topocentric position", " ICRS International Celestial Reference System" }; const char* earthMagneticHelp[] = { "EarthMagnetic types:", " IGRF IGRF model", " J2000 mean equator and equinox at J2000.0 (FK5)", " JNAT geocentric natural frame", " JMEAN mean equator and equinox at frame epoch", " JTRUE true equator and equinox at frame epoch", " APP apparent geocentric position", " B1950 mean epoch and ecliptic at B1950.0", " B1950_VLA mean epoch(1979.9)) and ecliptic at B1950.0", " BMEAN mean equator and equinox at frame epoch", " BTRUE true equator and equinox at frame epoch", " HADEC topocentric hourangle and declination", " AZEL topocentric Azimuth and Elevation (N through E)", " AZELNE topocentric Azimuth and Elevation (N through E)", " AZELSW topocentric Azimuth and Elevation (S through W)", " AZELGEO geodetic Azimuth and Elevation (N through E)", " AZELNEGEO geodetic Azimuth and Elevation (N through E)", " AZELSWGEO geodetic Azimuth and Elevation (S through W)", " ECLIPTIC ecliptic for J2000 equator and equinox", " MECLIPTIC ecliptic for mean equator of date", " TECLIPTIC ecliptic for true equator of date", " GALACTIC galactic coordinates", " SUPERGAL supergalactic coordinates", " ITRF coordinates wrt ITRF Earth frame", " TOPO apparent topocentric position", " ICRS International Celestial Reference System" }; const char* frequencyHelp[] = { "Frequency types", " REST Rest frequency", " LSRD Local Standard of Rest (J2000) - dynamical definition (IAU, [9,12,7] km/s in galactic coordinates)", " LSRK LSR as kinematical (radio) definition - 20.0 km/s in direction ra,dec = [270,+30] deg (B1900.0)", " BARY Barycentric (J2000)", " GEO Geocentric", " TOPO Topocentric", " GALACTO Galacto centric (with rotation of 220 km/s in direction l,b = [90,0] deg", " LGROUP Local group velocity -- 308km/s towards l,b = [105,-7] deg (F. Ghigo)", " CMB CMB velocity -- 369.5km/s towards l,b = [264.4, 48.4] deg (F. Ghigo)" }; const char* radialVelocityHelp[] = { "RadialVelocity types", " LSRD Local Standard of Rest (J2000) - dynamical definition (IAU, [9,12,7] km/s in galactic coordinates)", " LSRK LSR as kinematical (radio) definition - 20.0 km/s in direction ra,dec = [270,+30] deg (B1900.0)", " BARY Barycentric (J2000)", " GEO Geocentric", " TOPO Topocentric", " GALACTO Galacto centric (with rotation of 220 km/s in direction l,b = [90,0] deg", " LGROUP Local group velocity -- 308km/s towards l,b = [105,-7] deg (F. Ghigo)", " CMB CMB velocity -- 369.5km/s towards l,b = [264.4, 48.4] deg (F. Ghigo)" }; const char* dopplerHelp[] = { "Doppler types (with F = f/f0, the frequency ratio)", " Z, OPTICAL -1 + 1/F", " RATIO F", " RADIO 1 - F", " BETA, TRUE (1 - F**2)/(1 + F**2)", " RELATIVISTIC = BETA (= v/c)", " GAMMA (1 + F**2)/2F" }; String TaQLShow::getInfo (const Vector& parts, const TaQLStyle& style) { // parts contains the possible command, type and subtypes. if (parts.empty()) { return getHelp (infoHelp); } String cmd(parts[0]); cmd.downcase(); String type; if (parts.size() > 1) type = parts[1]; String origType(type); type.downcase(); if (cmd == "table") { return showTable (parts); } else if (cmd == "command" || cmd == "commands") { return showCommand (type); } else if (cmd == "expr" || cmd == "expression") { return getHelp (exprHelp); } else if (cmd == "oper" || cmd == "operator" || cmd == "operators") { return getHelp (operHelp); } else if (cmd == "const" || cmd == "constant" || cmd == "constants") { return getHelp (constHelp); } else if (cmd == "dtype" || cmd == "datatype" || cmd == "datatypes") { return getHelp (dtypeHelp); } else if (cmd == "tabopt" || cmd == "tableoption" || cmd == "tableoptions") { return getHelp (taboptHelp); } else if (cmd == "dminfo") { return getHelp (dminfoHelp); } else if (cmd == "set" || cmd == "interval" || cmd == "sets" || cmd == "intervals") { return getHelp (setHelp); } else if (cmd == "func" || cmd == "function" || cmd == "functions") { return showFuncs (type, parts, style); } else if (cmd == "meastype" || cmd == "meastypes") { return showMeasTypes (type); } else if (cmd == "unit" || cmd == "units") { return showUnits (origType); } throw TableInvExpr (cmd + " is an unknown SHOW command"); } String TaQLShow::showTable (const Vector& parts) { AlwaysAssert (parts.size() < 2, AipsError); return getHelp (tableHelp); } String TaQLShow::showCommand (const String& cmd) { if (cmd.empty()) { return getHelp (commandHelp); } else if (cmd == "select") { return getHelp (selectHelp); } else if (cmd == "calc") { return getHelp (calcHelp); } else if (cmd == "update") { return getHelp (updateHelp); } else if (cmd == "insert") { return getHelp (insertHelp); } else if (cmd == "delete") { return getHelp (deleteHelp); } else if (cmd == "create") { return getHelp (createHelp); } else if (cmd == "alter") { return getHelp (alterHelp); } else if (cmd == "count") { return getHelp (countHelp); } throw TableInvExpr (cmd + " is an unknown command for 'show command '\n" " use select, calc, update, insert, delete, create," " alter or count\n"); } String TaQLShow::showFuncs (const String& type, const Vector& parts, const TaQLStyle& style) { if (type.empty() || type == "all") { return getHelp (allFuncHelp); } else if (type == "math") { return getHelp (mathFuncHelp); } else if (type == "conversion" || type == "conv") { return getHelp (convFuncHelp); } else if (type == "logical") { return getHelp (logicalFuncHelp); } else if (type == "datetime") { return getHelp (dateTimeFuncHelp); } else if (type == "string") { return getHelp (stringFuncHelp); } else if (type == "array") { return getHelp (arrayFuncHelp); } else if (type == "reduce") { return getHelp (reduceFuncHelp); } else if (type == "astro") { return getHelp (astroFuncHelp); } else if (type == "misc") { return getHelp (miscFuncHelp); } else if (type == "aggr") { return getHelp (aggrFuncHelp); } try { TableExprNodeSet operands; String ftype; if (parts.size() > 2) ftype = parts[2]; operands.add (TableExprNodeSetElem(ftype)); // Make a node for the UDF library given by type to use its help function. // It takes an argument (which can be empty). TableExprNode node = TableExprNode::newUDFNode (type+".help", operands, TableExprInfo(), style); return node.getString(0); // get the UDF help info as a string } catch (const std::exception&) { return type + " is an unknown type in 'show functions '\n" " (maybe an unknown UDF library)\n"; } } void TaQLShow::showUnitKind (ostream& os, const UnitVal& kind, const map& units) { for (map::const_iterator iter = units.begin(); iter != units.end(); ++iter) { if (Unit(iter->first).getValue() == kind) { os << " " << iter->second << endl; } } } String TaQLShow::showUnits (const String& type) { ostringstream os; if (type.empty()) { UnitMap::list (os); } else if (type == "prefix") { UnitMap::listPref (os); } else { UnitVal kind; if (type == "length") { kind = UnitVal::LENGTH; } else if (type == "mass") { kind = UnitVal::MASS; } else if (type == "time") { kind = UnitVal::TIME; } else if (type == "current") { kind = UnitVal::CURRENT; } else if (type == "temperature") { kind = UnitVal::TEMPERATURE; } else if (type == "intensity") { kind = UnitVal::INTENSITY; } else if (type == "molar") { kind = UnitVal::MOLAR; } else if (type == "angle") { kind = UnitVal::ANGLE; } else if (type == "solidangle") { kind = UnitVal::SOLIDANGLE; } else { try { Unit unit(type); kind = unit.getValue(); } catch (const AipsError&) { throw TableInvExpr ("Unknown kind or unit given in command " "'show units " + type + "'\nUse 'show' to " "show the valid kinds"); } } showUnitKind (os, kind, UnitMap::giveDef()); showUnitKind (os, kind, UnitMap::giveSI()); showUnitKind (os, kind, UnitMap::giveCust()); showUnitKind (os, kind, UnitMap::giveUser()); } return os.str(); } String TaQLShow::showMeasTypes (const String& type) { // Because libtables cannot be dependent on libmeasures, // no Measures functions can be used to show the types. if (type.empty()) { return getHelp (positionHelp) + getHelp (epochHelp) + getHelp (directionHelp) + getHelp (earthMagneticHelp) + getHelp (frequencyHelp) + getHelp (radialVelocityHelp) + getHelp (dopplerHelp) + "\nSee also 'show functions meas " "pos|epoch|dir|em|freq|radvel|doppler\n"; } else if (type == "pos" || type == "position") { return getHelp (positionHelp); } else if (type == "epoch") { return getHelp (epochHelp); } else if (type == "dir" || type == "direction") { return getHelp (directionHelp); } else if (type == "em" || type == "earthmagnetic") { return getHelp (earthMagneticHelp); } else if (type == "freq" || type == "frequency") { return getHelp (frequencyHelp); } else if (type == "rv" || type == "radvel" || type == "radialvelocity") { return getHelp (radialVelocityHelp); } else if (type == "doppler") { return getHelp (dopplerHelp); } throw TableInvExpr (type + " is an unknown type for command " "'show meastypes '"); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/TaQLShow.h000066400000000000000000000057221476623553700172200ustar00rootroot00000000000000//# TaQLShow.h: Class to show various TaQL-related info //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TAQLSHOW_H #define TABLES_TAQLSHOW_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Class to show various TaQL-related info // // // // // //# Classes you should understand before using this one. //
      • TableGram //
      • Note 199 describing // // TaQL // // // TaQLShow::getInfo is called by the TaQL help (or show) command. // It returns a string (with newlines) containing the requested help info. // // Note that a command like 'help func mscal' command calls the getInfo function in // the dynamically loaded mscal UDF library. // // // It is nice if the user can get online TaQL help. // class TaQLShow { public: static String getInfo (const Vector& parts, const TaQLStyle& style); static String showTable (const Vector& parts); static String showCommand (const String& cmd); static String showFuncs (const String& type, const Vector& parts, const TaQLStyle& style); static void showUnitKind (std::ostream& os, const UnitVal& kind, const std::map& units); static String showUnits (const String& type); static String showMeasTypes (const String& type); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/TaQLStyle.cc000066400000000000000000000065201476623553700175330ustar00rootroot00000000000000//# TaQLStyle.cc: Class with static members defining the TaQL style //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TaQLStyle::TaQLStyle (uInt origin) : itsOrigin (origin), itsEndExcl (False), itsCOrder (False), itsDoTiming (False), itsDoTracing (False) { // Define mscal as a synonym for derivedmscal. defineSynonym ("mscal", "derivedmscal"); defineSynonym ("py", "pytaql"); } void TaQLStyle::set (const String& value) { String val = upcase(value); if (val == "GLISH") { itsOrigin = 1; itsEndExcl = False; itsCOrder = False; } else if (val == "PYTHON") { itsOrigin = 0; itsEndExcl = True; itsCOrder = True; } else if (val == "BASE1") { itsOrigin = 1; } else if (val == "BASE0") { itsOrigin = 0; } else if (val == "FORTRANORDER") { itsCOrder = False; } else if (val == "CORDER") { itsCOrder = True; } else if (val == "ENDINCL") { itsEndExcl = False; } else if (val == "ENDEXCL") { itsEndExcl = True; } else if (val == "TIME") { itsDoTiming = True; } else if (val == "NOTIME") { itsDoTiming = False; } else if (val == "TRACE") { itsDoTracing = True; } else if (val == "NOTRACE") { itsDoTracing = False; } else { throw TableError(value + " is an invalid TaQL STYLE value"); } } void TaQLStyle::reset() { set ("GLISH"); itsDoTiming = False; itsDoTracing = False; } void TaQLStyle::defineSynonym (const String& synonym, const String& udfLibName) { itsUDFLibNameMap[downcase(synonym)] = udfLibName; } void TaQLStyle::defineSynonym (const String& command) { String cmd(command); // to make it non-const String::size_type pos = cmd.find ('='); AlwaysAssert (pos != String::npos, AipsError); defineSynonym (trim(String(cmd.before(pos))), trim(String(cmd.after(pos)))); } String TaQLStyle::findSynonym (const String& synonym) const { map::const_iterator it = itsUDFLibNameMap.find (synonym); if (it == itsUDFLibNameMap.end()) { return synonym; } return it->second; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/TaQLStyle.h000066400000000000000000000101101476623553700173630ustar00rootroot00000000000000//# TaQLStyle.h: Class with static members defining the TaQL style //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TAQLSTYLE_H #define TABLES_TAQLSTYLE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Class with static members defining the TaQL style. // // // // // // Originally TaQL was developed to use the Glish style of indexing. // This meant 1-based indices, axes in Fortran order, and end is inclusive // in start:end. // On the other hand the Python style is the opposite. // In order to let the user choose between styles, one can define the // style in a TaQL command. // The default style is Glish. // // The class is also used to tell the TaQL execution engine if timings // or tracing of the various parts of the TaQL command need to be done. // // Finally it is possible to define synonyms for UDF library names. // For example, 'derivedmscal' is a lot to type, so a synonym 'mscal' // (or even 'mc') can be defined for it. // class TaQLStyle { public: // Default style is Glish and no timing/tracing. explicit TaQLStyle (uInt origin=1); // Reset to the default Glish style and no timing/tracing. void reset(); // Set the style according to the (case-insensitive) value. // Possible values are Glish, Python, Base0, Base1, FortranOrder, Corder, // InclEnd, and ExclEnd. void set (const String& value); // Define a UDF library name synonym. // The synonym name is always converted to lowercase because TaQL always // uses lowercase to lookup functions. The library name kept as is making // it possible to use a library containing uppercase characters. // If the synonym already exists, it is redefined. void defineSynonym (const String& synonym, const String& udfLibName); // Set a synonym using a command like 'synonym = udflibname'. void defineSynonym (const String& command); // Find the UDF library name belonging to a synonym. // If undefined, the synonym itself is returned. String findSynonym (const String& synonym) const; // Get the various style values. // uInt origin() const { return itsOrigin; } Bool isEndExcl() const { return itsEndExcl; } Bool isCOrder() const { return itsCOrder; } // // Set if timing needs to be done. void setTiming (Bool doTiming) { itsDoTiming = doTiming; } // Should timing be done? Bool doTiming() const { return itsDoTiming; } // Set if tracing needs to be done. void setTracing (Bool doTracing) { itsDoTracing = doTracing; } // Should tracing be done? Bool doTracing() const { return itsDoTracing; } private: uInt itsOrigin; Bool itsEndExcl; Bool itsCOrder; Bool itsDoTiming; Bool itsDoTracing; std::map itsUDFLibNameMap; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/TableExprData.cc000066400000000000000000000061341476623553700203720ustar00rootroot00000000000000//# TableExprData.cc: Abstract base class for data object in a TaQL expression //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableExprData::~TableExprData() {} IPosition TableExprData::shape (const Block&) const { return IPosition(); } Bool TableExprData::getBool (const Block&) const { throw (AipsError ("TableExprData::getBool not implemented")); } Int64 TableExprData::getInt (const Block&) const { throw (AipsError ("TableExprData::getInt not implemented")); } Double TableExprData::getDouble (const Block& fieldNrs) const { return getInt (fieldNrs); } DComplex TableExprData::getDComplex (const Block& fieldNrs) const { return getDouble (fieldNrs); } String TableExprData::getString (const Block&) const { throw (AipsError ("TableExprData::getString not implemented")); } Array TableExprData::getArrayBool (const Block&) const { throw (AipsError ("TableExprData::getArrayBool not implemented")); } Array TableExprData::getArrayInt (const Block&) const { throw (AipsError ("TableExprData::getArrayInt not implemented")); } Array TableExprData::getArrayDouble (const Block& fieldNrs) const { Array tmp = getArrayInt (fieldNrs); Array result(tmp.shape()); convertArray (result, tmp); return result; } Array TableExprData::getArrayDComplex (const Block& fieldNrs) const { Array tmp = getArrayDouble (fieldNrs); Array result(tmp.shape()); convertArray (result, tmp); return result; } Array TableExprData::getArrayString (const Block&) const { throw (AipsError ("TableExprData::getArrayString not implemented")); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/TableExprData.h000066400000000000000000000227661476623553700202450ustar00rootroot00000000000000//# TableExprData.h: Abstract base class for data object in a TaQL expression //# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABLEEXPRDATA_H #define TABLES_TABLEEXPRDATA_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class String; class IPosition; template class Block; // // Abstract base class for data object in a TaQL expression. // // // // // //
      • TableExprNode. // // // The Table Query Language (TaQL) is implemented by means of the // TableExprNode classes. It is primarily meant to do // selection on tables. However, it is also possible to use it for // selection on any other set of data resembling tabular data. //
        An example of such a data set is a set of // Record objects. TaQL can be used // to select some of those records based on the contents of one or more // fields in the records. Note that this example is already directly // supported by TaQL. //
        Another example is when a user has several equally long vectors // with data. The vectors can be seen as fields and TaQL can be used // to select entries from the vectors. This example requires that // this class TableExprData is used. //

        // The TableExprNodeRecordField // and TableExprId classes form // the means by which TaQL can deal with any set of data. //
        First the TaQL expression has to be setup. This is done by // constructing a TableExprNodeRecordField object for each // 'field' to be used in the expression. TableExprNodeRecordField // uses a RecordInterface object // to make the data type of a field in the data set known and to // map a field name to a field index (the index is the sequence number // of the field in the record description). //
        When evaluating the expression for each member in the data set, // a TableExprData> needs to be passed (which is automatically // converted to TableExprId). // So a class needs to be written to access the data in the data set. // It needs to be derived from the abstract base class TableExprData // defined in this file. An example is given below. //

        // It is also possible that the data set contains records and that // the selection is based on fields in those records. In such a case // the record passed to TableExprNodeRecordField should contain // subrecords representing those records. The field index in the various // functions as passed as a Block to denote the fields // in the subrecords (and possibly subsubrecords, etc.. // However, normally records won't be used and fieldNrs[0] // gives the field index. // // // This example shows how a data set consisting of two vectors // of scalars can be used. // // // Write a class derived from TableExprData to handle the vectors. // class MyTestClass : public TableExprData // { // public: // // Constructor checks if both vectors have equal length. // MyTestClass (const Vector& fld1, const Vector& fld2) // : itsFld1(fld1), itsFld2(fld2), itsEntry(0) // { AlwaysAssert (fld1.nelements() == fld2.nelements(), AipsError); } // virtual ~MyTestClass() // {} // void next() // { itsEntry++; } // // Note that only the get functions for the possible types are needed. // // Also note that all numeric types are handled by TaQL as Double. // // The exception should never be thrown unless things are screwed up. // virtual Double getDouble (const Block& fieldNrs) const // { switch (fieldNrs[0]) { // case 0: // return itsFld1(itsEntry); // default: // throw AipsError(); // } // } // virtual String getString (const Block& fieldNrs) const // { switch (fieldNrs[0]) { // case 1: // return itsFld2(itsEntry); // default: // throw AipsError(); // } // } // virtual DataType dataType (const Block& fieldNrs) const // { switch (fieldNrs[0]) { // case 0: // return TpInt; // case 1: // return TpString; // default: // throw AipsError(); // } // } // // Make a Record to give to vectors a name. // // The order in which the fields are defined determines the fieldnrs // // passed to the get functions. // static Record makeRecord() // { RecordDesc desc; // desc.addField ("fld1", TpInt); // desc.addField ("fld2", TpString); // return Record(desc); // } // private: // Vector itsFld1; // Vector itsFld2; // uInt itsEntry; // }; // // Vector findMatches (const Vector& fld1, // const Vector& fld2) // { // // Make some expression. // // First create a Record to make the names and types known. // Record rec(MyTestClass::makeRecord()); // TableExprNode expr (makeRecordExpr(rec,"fld1") > 10 && // makeRecordExpr(rec,"fld2") != pattern("*xxx*")); // // Now evaluate the expression for each entry in the vector. // // Make a MyTestClass object to handle the vectors and put it in // // a TableExprId object for the TaQL evaluator. // // Note that TableExprId holds a pointer to the original MyTestClass // // object, so the TaQL evaluator 'sees' the changes we make by // // using the its next() function. // MyTestClass subj(fld1, fld2); // TableExprId eid(subj); // // The matching entry numbers are stored in a vector. // Vector result(fld1.nelements()); // uInt nr=0; // Bool valb; // for (uInt i=0; i // // // This class makes it possible that TaQL can be used in a very versatile way. // //# //# class TableExprData { public: // Construct it from a row number. TableExprData() {;} virtual ~TableExprData(); // Get the shape of the given field. // Need only be implemented if there are arrays in the data. // The default implementation returns an empty IPosition. virtual IPosition shape (const Block& fieldNrs) const; // Get the data type of the given field. // Note that TpArray types have to be returned for arrays. // If the field is unknown, TpOther should be returned. // It is used for the isdefined function to check if the field // is really defined. virtual DataType dataType (const Block& fieldNrs) const = 0; // Get a scalar in the given type. // This might involve converting for Double and DComplex. // Most default implementations throws an "not possible" exception. // The default getDouble invokes getInt. // The default getDComplex invokes getDouble. // virtual Bool getBool (const Block& fieldNrs) const; virtual Int64 getInt (const Block& fieldNrs) const; virtual Double getDouble (const Block& fieldNrs) const; virtual DComplex getDComplex (const Block& fieldNrs) const; virtual String getString (const Block& fieldNrs) const; // // Get an array in the given type. // This might involve converting for Double and DComplex. // Most default implementations throws an "not possible" exception. // The default getArrayDComplex invokes // getArrayDouble. // virtual Array getArrayBool (const Block& fieldNrs) const; virtual Array getArrayInt (const Block& fieldNrs) const; virtual Array getArrayDouble (const Block& fieldNrs) const; virtual Array getArrayDComplex (const Block& fieldNrs) const; virtual Array getArrayString (const Block& fieldNrs) const; // }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/TableExprId.cc000066400000000000000000000025641476623553700200600ustar00rootroot00000000000000//# TableExprId.cc: The identification of a TaQL selection subject //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Everything is inlined } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/TableExprId.h000066400000000000000000000125231476623553700177160ustar00rootroot00000000000000//# TableExprId.h: The identification of a TaQL selection subject //# Copyright (C) 2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABLEEXPRID_H #define TABLES_TABLEEXPRID_H //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class RecordInterface; class TableExprData; //

        // The identification of a TaQL selection subject. // // // // // //
      • TableExprNode. // // // This class provides the user the ability to identify the data objects // to test in a TaQL expression. In this way a TaQL expression is not // limited to tables, but can be used for any set of data. // Three types are available: //
          //
        1. A row number giving the row to test in a table. // It also contains a sequence number (0..n) which is used to get the // result calculated by aggregate functions. //
        2. A RecordInterface // object giving the record to test. //
        3. A TableExprData // object giving the abstract base class of an object holding // the data to test. In this way any data can be used. //
        // The TaQL expression must be setup with this in mind by constructing // the appropriate TableExprNode // leaf objects. //
        // When used for tables, the function Table::col // should be used to create the TableExprNode objects for the // table columns to be used in the expression. //
        // For the other cases class // TableExprNodeRecordField // has to be used to know the index of the fields in the expression. // It uses a record (description) for this purpose. //
        // // // // // // This class makes it possible that TaQL can be used in a very versatile way. // //# //# class TableExprId { public: // Default constructor sets rownr to -1. TableExprId(); // Construct it from a row number. TableExprId (rownr_t rowNumber); // Construct it from a Record object. TableExprId (const RecordInterface&); // Construct it from pointers to data. TableExprId (const TableExprData& data); ~TableExprId() {} // Is the id given by row number? Bool byRow() const; // Is the id given as a RecordInterface? Bool byRecord() const; // Is the id given as a TableExprData? Bool byData() const; // Get the row number. Int64 rownr() const; // Get the Record reference. const RecordInterface& record() const; // Get the data reference. const TableExprData& data() const; // Set the row number. void setRownr (rownr_t rownr); // Set the record. void setRecord (const RecordInterface&); private: Int type_p; union { Int64 row_p; const RecordInterface* record_p; const TableExprData* data_p; }; }; inline TableExprId::TableExprId() : type_p (0), row_p (-1) {} inline TableExprId::TableExprId (rownr_t rowNumber) : type_p (0), row_p (rowNumber) {} inline TableExprId::TableExprId (const RecordInterface& record) : type_p (-1), record_p (&record) {} inline TableExprId::TableExprId (const TableExprData& data) : type_p (-2), data_p (&data) {} inline Int64 TableExprId::rownr() const { return row_p; } inline const RecordInterface& TableExprId::record() const { return *record_p; } inline const TableExprData& TableExprId::data() const { return *data_p; } inline void TableExprId::setRownr (rownr_t rownr) { row_p = rownr; } inline void TableExprId::setRecord (const RecordInterface& record) { record_p = &record; } inline Bool TableExprId::byRow() const { return type_p >= 0; } inline Bool TableExprId::byRecord() const { return type_p == -1; } inline Bool TableExprId::byData() const { return type_p == -2; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/TableExprIdAggr.h000066400000000000000000000110021476623553700205060ustar00rootroot00000000000000//# TableExprIdAggr.h: The Table Expression Selection id used with aggregation //# Copyright (C) 2013 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABLEEXPRIDAGGR_H #define TABLES_TABLEEXPRIDAGGR_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // The Table Expression Selection id used with aggregation // // // // // // This class provides the user the ability to identify the data objects // to test in a TaQL expression using aggregate functions. //
        // It adds a TableExprGroupResult object to the TableExprId class which // is used by the various classes derived from TableExprGroupFuncBase // to get or evaluate the aggregated value. // An aggregated value can be determined in two ways: //
          //
        • An immediate aggregate function has calculated its value in its // apply function. // Each group has a TableExprGroupFuncSet containing the values // of all immediate aggregate functions of that group. // A vector of these objects is part of of TableExprGroupResult // and is used by the TableExprAggrNode(Array) get functions // to return the correct value. //
        • A lazy aggregate function calculates the value as part of its // get function. It uses the vector of TableExprId objects // in TableExprGroupResult to know which rows (or records) belong to // which group. Note that UDF aggregate functions are always lazy. //
        // TableExprIdAggr contains a static function to (statically) cast a // TableExprId object to TableExprIdAggr. A magic value is used to check // that the cast is valid. //
        No dynamic_cast is used, because it requires TableExprId to contain // virtual functions making the object larger. TaQL can hold quite large // vectors of TableExprId objects, so such overhead is unacceptably large. //
        // // This class makes it possible to retrieve aggregated values using the // standard get functions taking an TableExprId. // class TableExprIdAggr: public TableExprId { public: // Construct it from the aggregation results. explicit TableExprIdAggr (const std::shared_ptr& result) : itsMagicValue (0xabababab), itsResult (result) {} // Get out the aggregation result object. const TableExprGroupResult& result() const { return *itsResult; } // Get the magic value (to check if correct). uInt getMagicValue() const { return itsMagicValue; } // Cast a TableExprId object to TableExprIdAggr. It checks if the cast // is correct by checking the magic value. static const TableExprIdAggr& cast (const TableExprId& id) { const TableExprIdAggr& idAggr = reinterpret_cast(id); AlwaysAssert (idAggr.getMagicValue() == 0xabababab, AipsError); return idAggr; } private: uInt itsMagicValue; std::shared_ptr itsResult; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/TableGram.cc000066400000000000000000000151741476623553700175540ustar00rootroot00000000000000//# TableGram.cc: Grammar for table command lines //# Copyright (C) 1993,1994,1995,1997,1999,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA // TableGram; grammar for table command lines // This file includes the output files of bison and flex for // parsing command lines operating on tables. #include #include #include #include #include #include #include //# stdlib.h is needed for bison 1.28 and needs to be included here //# (before the flex/bison files). //# Bison defines WHERE, which is also defined in LogOrigin.h (which //# is included in auto-template mode). //# So undefine WHERE first. #undef WHERE #include //# Let clang and gcc ignore some warnings in bison/flex code. //# Undef YY_NULL because flex can redefine it slightly differently (giving a warning). #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpragmas" #pragma GCC diagnostic ignored "-Wdeprecated-register" #pragma GCC diagnostic ignored "-Wsign-compare" #include "TableGram.ycc" // bison output #ifdef YY_NULL # undef YY_NULL #endif #include "TableGram.lcc" // flex output #pragma GCC diagnostic pop // Define the yywrap function for flex. int TableGramwrap() { return 1; } namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Declare a file global pointer to a char* for the input string. static const char* strpTableGram = 0; static Int posTableGram = 0; // Define a class to delete the yy_buffer in case of an exception. class TableGramState { public: TableGramState (YY_BUFFER_STATE state) : itsState (state) {} ~TableGramState() { clear(); } TableGramState (const TableGramState&) = delete; TableGramState& operator= (const TableGramState&) = delete; void clear() { if (itsState) { TableGram_delete_buffer(itsState); itsState=0; } } YY_BUFFER_STATE state() const { return itsState; } private: YY_BUFFER_STATE itsState; //# this is a pointer to yy_buffer_state }; //# Parse the command. int tableGramParseCommand (const String& command) { // Save current state for re-entrancy. int sav_yy_start = yy_start; const char* savStrpTableGram = strpTableGram; Int savPosTableGram= posTableGram; YY_BUFFER_STATE sav_state = YY_CURRENT_BUFFER; // Create a new state buffer for new expression. TableGramState next (TableGram_create_buffer (TableGramin, YY_BUF_SIZE)); TableGram_switch_to_buffer (next.state()); yy_start = 1; strpTableGram = command.chars(); // get pointer to command string posTableGram = 0; // initialize string position int sts = TableGramparse(); // parse command string // The current state has to be deleted before switching back to previous. next.clear(); // Restore previous state. yy_start = sav_yy_start; strpTableGram = savStrpTableGram; posTableGram= savPosTableGram; TableGram_switch_to_buffer (sav_state); return sts; } //# Give the string position. Int& tableGramPosition() { return posTableGram; } //# Get the next input characters for flex. int tableGramInput (char* buf, int max_size) { int nr=0; while (*strpTableGram != 0) { if (nr >= max_size) { break; // get max. max_size char. } buf[nr++] = *strpTableGram++; } return nr; } void TableGramerror (const char*) { throw TableGramError (tableGramPosition(), String(TableGramtext)); } String tableGramRemoveEscapes (const String& in) { String out; int leng = in.length(); for (int i=0; i 0 && val[0] == '/') { val = val.after(0); } if (! MVAngle::read (res, val)) { throw TableInvExpr ("invalid time/pos string " + val); } return MVAngle(res).radian(); } MVTime tableGramParseDateTime (const String& in) { MUString str (in); Quantity res; if (! MVTime::read (res, str)) { throw TableInvExpr ("invalid date string " + in); } return MVTime(res); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/TableGram.h000066400000000000000000000065351476623553700174170ustar00rootroot00000000000000//# TableGram.h: Grammar for table command lines //# Copyright (C) 1993,1994,1995,1997,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABLEGRAM_H #define TABLES_TABLEGRAM_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations // // Global functions for flex/bison scanner/parser for TableGram // // // // // //# Classes you should understand before using this one. //
      • TableGram.l and .y (flex and bison grammar) // // // Global functions are needed to define the input of the flex scanner // and to start the bison parser. // The input is taken from a string. // // // It is necessary to be able to give a table select command in ASCII. // This can be used in a CLI or in the table browser to get a subset // of a table or to sort a table. // // //# A List of bugs, limitations, extensions or planned refinements. // // // Declare the bison parser (is implemented by bison command). int tableGramParseCommand (const String& command); // The yyerror function for the parser. // It throws an exception with the current token. void TableGramerror (const char*); // Give the current position in the string. // This can be used when parse errors occur. Int& tableGramPosition(); // Declare the input routine for flex/bison. int tableGramInput (char* buf, int max_size); // A function to remove escape characters. String tableGramRemoveEscapes (const String& in); // A function to remove escape characters and quotes. String tableGramRemoveEscapesQuotes (const String& in); // A function to remove quotes from a quoted string. String tableGramRemoveQuotes (const String& in); // A function to parse a date/time string. MVTime tableGramParseDateTime (const String& in); // A function to parse a time/position string. // The value is returned in radians. Double tableGramParseTime (const String& in); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/TableGram.ll000066400000000000000000000672021476623553700175750ustar00rootroot00000000000000/* TableGram.ll: Lexical analyzer for table commands Copyright (C) 1994,1995,1996,1997,1998,2001,2003 Associated Universities, Inc. Washington DC,a USA. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. Correspondence concerning AIPS++ should be addressed as follows: Internet email: casa-feedback@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA */ /* yy_unput is not used, so let flex not generate it, otherwise picky compilers will issue warnings. */ %option nounput %{ #undef YY_INPUT #define YY_INPUT(buf,result,max_size) result=tableGramInput(buf,max_size) #undef YY_DECL #define YY_DECL int TableGramlex (YYSTYPE* lvalp) %} /* States to distinguish how some tokens are recognized */ %s STYLEstate %s EXPRstate %s TABLENAMEstate /* Exclusive state meaning that tokens are only recognized in that state */ %x SHOWstate /* Define functions to set the EXPRstate or TABLENAMEstate. Unfortunately the symbolic state names above cannot be used because Flex defines them just before %% below. So ensure that the values below are indeed the 2nd and 3rd state above (note: INITIALstate=0). */ %{ void setEXPRstate() { BEGIN(2); } void setTABLENAMEstate() { BEGIN(3); } %} /* Define all tokens recognized by flex. Imaginary part of a Complex value can be given as: FLOATi where i is the letter i or j (in lowercase only). In a NAME the backslash can be used to escape special characters like -. In that way a name like DATE-OBS can be given as DATE\-OBS. Comment is from # till the end of the command. */ WHITE1 [ \t\n] WHITE {WHITE1}* NONWHITE [^ \t\n]+ COMMENT "#".* DIGIT [0-9] INT {DIGIT}+ INT2 {DIGIT}{DIGIT} INT4 {INT2}{INT2} HEXINT 0[xX][0-9a-fA-F]+ EXP [Ee][+-]?{INT} FLOAT {INT}{EXP}|{INT}"."{DIGIT}*({EXP})?|{DIGIT}*"."{INT}({EXP})? FLINT {FLOAT}|{INT} COMPLEX {FLINT}[ij] TRUE T|([Tt][Rr][Uu][Ee]) FALSE F|([Ff][Aa][Ll][Ss][Ee]) FLINTUNIT {FLINT}[a-zA-Z]+ MONTH [A-Za-z]+ /* Date with numeric month must have 2 digits in day and month and 4 in year. Can be DMY or YMD with - or / as delimiter. */ DATEH ({INT2}"-"{INT2}"-"{INT4})|({INT4}"-"{INT2}"-"{INT2}) DATES {INT4}"/"{INT2}"/"{INT2} /* Date with alphabetic month has more freedom in day and year */ DATEA {INT}{MONTH}{INT}|{INT}"-"{MONTH}"-"{INT} DATE {DATEA}|{DATEH}|{DATES} /* Time part of a datetime can use h/m or : */ DTIMEH {INT}[hH]({INT}?([mM]({FLINT})?)?)? DTIMEC {INT}":"({INT}?(":"({FLINT})?)?)? DTIME {DTIMEH}|{DTIMEC} /* - / space or T can be used as separator between date and time; optional ISO Z */ DATETIME {DATE}([-/ T]{DTIME}(Z?))? /* Sky position as HMS or DMS; dots can be used instead of DMS (as MVAngle allows). Positions/times with colons cannot be allowed, because they interfere with the interval syntax. It is only possible when preceeded by a date. */ POSHM {INT}[hH]{INT}[mM]{FLINT}? POSDM {INT}[dD]{INT}[mM]{FLINT}? POSD {INT}"."{INT}"."{FLINT} TIME {POSHM}|{POSDM}|{POSD} /* Strings can be quoted using single or double quotes */ QSTRING \"[^\"\n]*\" ASTRING \'[^\'\n]*\' /* Recognize a quoted string without an end-quote (to give an error message) */ UQSTRING \"[^\"\n]*\n UASTRING \'[^\'\n]*\n STRING ({QSTRING}|{ASTRING})+ USTRING ({UQSTRING}|{UASTRING})+ /* Recognize all reserved words (can be a mix of lowercase and uppercase) */ UNION [Uu][Nn][Ii][Oo][Nn] INTERSECT [Ii][Nn][Tt][Ee][Rr][Ss][Ee][Cc][Tt] EXCEPT ([Ee][Xx][Cc][Ee][Pp][Tt])|([Mm][Ii][Nn][Uu][Ss]) STYLE [Uu][Ss][Ii][Nn][Gg]{WHITE}[Ss][Tt][Yy][Ll][Ee]{WHITE1} TIMEWORD [Tt][Ii][Mm][Ee] SHOW ([Ss][Hh][Oo][Ww])|([Hh][Ee][Ll][Pp]) WITH [Ww][Ii][Tt][Hh] TABLE [Tt][Aa][Bb][Ll][Ee] SELECT [Ss][Ee][Ll][Ee][Cc][Tt] UPDATE [Uu][Pp][Dd][Aa][Tt][Ee] INSERT [Ii][Nn][Ss][Ee][Rr][Tt] DELETE [Dd][Ee][Ll][Ee][Tt][Ee] DROP [Dd][Rr][Oo][Pp] DROPDEL {DROP}|{DELETE} ADD [Aa][Dd][Dd] RENAME [Rr][Ee][Nn][Aa][Mm][Ee] SET [Ss][Ee][Tt] COPY [Cc][Oo][Pp][Yy] COLUMN [Cc][Oo][Ll][Uu][Mm][Nn]([Ss])? KEYWORD [Kk][Ee][Yy][Ww][Oo][Rr][Dd]([Ss])? ROW [Rr][Oo][Ww]([Ss])? COUNT [Cc][Oo][Uu][Nn][Tt] COUNTALL [Gg]{COUNT}{WHITE}"("{WHITE}"*"?{WHITE}")" CALC [Cc][Aa][Ll][Cc] CREATETAB [Cc][Rr][Ee][Aa][Tt][Ee]{WHITE}{TABLE}{WHITE1} ALTERTAB [Aa][Ll][Tt][Ee][Rr]{WHITE}{TABLE}{WHITE1} DROPTAB {DROP}{WHITE}{TABLE}{WHITE1} /* Optionally the ALTER TABLE subcommands can be separated by commas; they need a space after the subcommand name. */ ADDCOL ,?{WHITE}{ADD}{WHITE}{COLUMN}{WHITE1} COPYCOL ,?{WHITE}{COPY}{WHITE}{COLUMN}{WHITE1} RENAMECOL ,?{WHITE}{RENAME}{WHITE}{COLUMN}{WHITE1} DROPCOL ,?{WHITE}{DROPDEL}{WHITE}{COLUMN}{WHITE1} SETKEY ,?{WHITE}{SET}{WHITE}{KEYWORD}{WHITE1} COPYKEY ,?{WHITE}{COPY}{WHITE}{KEYWORD}{WHITE1} RENAMEKEY ,?{WHITE}{RENAME}{WHITE}{KEYWORD}{WHITE1} DROPKEY ,?{WHITE}{DROPDEL}{WHITE}{KEYWORD}{WHITE1} ADDROW ,?{WHITE}{ADD}{WHITE}{ROW}{WHITE1} DMINFO [Dd][Mm][Ii][Nn][Ff][Oo] VALUES [Vv][Aa][Ll][Uu][Ee][Ss] FROM [Ff][Rr][Oo][Mm] WHERE [Ww][Hh][Ee][Rr][Ee] ORDERBY [Oo][Rr][Dd][Ee][Rr]{WHITE}[Bb][Yy]{WHITE1} NODUPL1 [Nn][Oo][Dd][Uu][Pp][Ll][Ii][Cc][Aa][Tt][Ee][Ss] DISTINCT [Dd][Ii][Ss][Tt][Ii][Nn][Cc][Tt] UNIQUE [Uu][Nn][Ii][Qq][Uu][Ee] NODUPL {NODUPL1}|{DISTINCT}|{UNIQUE} GIVING1 [Gg][Ii][Vv][Ii][Nn][Gg] SAVETO [Ss][Aa][Vv][Ee]{WHITE}[Tt][Oo]{WHITE1} GIVING {GIVING1}|{SAVETO} INTO [Ii][Nn][Tt][Oo] SUBTABLES [Ss][Uu][Bb][Tt][Aa][Bb][Ll][Ee][Ss] GROUPBY [Gg][Rr][Oo][Uu][Pp]{WHITE}[Bb][Yy]{WHITE1} GROUPROLL {GROUPBY}{WHITE}[Rr][Oo][Ll][Ll][Uu][Pp]{WHITE1} HAVING [Hh][Aa][Vv][Ii][Nn][Gg] JOIN [Jj][Oo][Ii][Nn] ON [Oo][Nn] ASC [Aa][Ss][Cc] DESC [Dd][Ee][Ss][Cc] LIMIT ([Ll][Ii][Mm][Ii][Tt])|([Tt][Oo][Pp]) OFFSET [Oo][Ff][Ff][Ss][Ee][Tt] BETWEEN [Bb][Ee][Tt][Ww][Ee][Ee][Nn] AROUND [Aa][Rr][Oo][Uu][Nn][Dd] EXISTS [Ee][Xx][Ii][Ss][Tt][Ss] LIKE [Ll][Ii][Kk][Ee] ILIKE [Ii][Ll][Ii][Kk][Ee] IN [Ii][Nn] INCONE [Ii][Nn]{WHITE}[Cc][Oo][Nn][Ee]{WHITE1} AS [Aa][Ss] TO [Tt][Oo] AND [Aa][Nn][Dd] OR [Oo][Rr] XOR [Xx][Oo][Rr] NOT [Nn][Oo][Tt] ALL [Aa][Ll][Ll] /* To distinguish keyword ALL from function ALL, the latter has a parenthesis */ ALLFUNC {ALL}{WHITE}"(" /* A basic name is alphanumeric (and underscores) or is escaped with a backslash */ NAME \\?[A-Za-z_]([A-Za-z_0-9]|(\\.))* /* A field name is a name with dots or double colons */ NAMEFLD ({NAME}".")?{NAME}?("::")?{NAME}("."{NAME})* /* A temporary table name can be followed by field names */ TEMPTAB [$]{INT}(("."{NAME})?("::"{NAME}("."{NAME})*)*) /* A table name can contain about every character (but is recognized in specific states only). It can be a mix of quoted and unquoted strings (with escaped characters). NOTE: when changing NAMETABC, also change TaQLNodeRep::addEscape. */ NAMETABC ([A-Za-z0-9_./+\-~$@:]|(\\.))+ NAMETAB {NAMETABC}|(({STRING}|{NAMETABC})+) /* A UDFlib synonym */ UDFLIBSYN {NAME}{WHITE}"="{WHITE}{NAME} /* A regular expression can be delimited by / % or @ optionall=y followed by i to indicate case-insensitive matching. m is a partial match (match if part of string matches the regex) f is a full match p is a pattern match (glob-style pattern) */ REGEX1 m"/"[^/]+"/" REGEX2 m%[^%]+% REGEX3 m@[^@]+@ REGEX {REGEX1}|{REGEX2}|{REGEX3} FREGEX1 f"/"[^/]+"/" FREGEX2 f%[^%]+% FREGEX3 f@[^@]+@ FREGEX {FREGEX1}|{FREGEX2}|{FREGEX3} PATT1 p\/[^/]+\/ PATT2 p%[^%]+% PATT3 p@[^@]+@ PATT {PATT1}|{PATT2}|{PATT3} PATTEX ({REGEX}|{FREGEX}|{PATT})i? /* String distance is similar; has options b i and nn (distance) in any order */ DIST1 d\/[^/]+\/ DIST2 d%[^%]+% DIST3 d@[^@]+@ DISTOPT [bi]*{INT}?[bi]* DISTEX ({DIST1}|{DIST2}|{DIST3}){DISTOPT} /* Part of the pattern is the operator */ OPERREX "!"?"~" PATTREX {OPERREX}{WHITE}({PATTEX}|{DISTEX}) %% /* The command to be analyzed is something like: SELECT column-list FROM table-list WHERE expression ORDER BY column-list GIVING table The WHERE, ORDER BY, and GIVING parts are optional. Elements in a list are separated by commas. A table name can be only a table file name or a table file name followed by whitespace and an alphanumeric name. That 2nd name serves as a shorthand for possible later use in the command. A table name can be given in a few parts of the command. These are indicated by the TABLENAMEstate, because a table name can contain special characters such as ~. In TableGram.yy care is taken that the state is switched forth and back to between TABLENAMEstate and EXPRstate. A table name can be $nnn indicating a temporary table. It can optionally be followed by :: and the name of a subtable of that temporary table. The order in the following list is important, since, for example, the word "giving" must be recognized as GIVING and not as NAME. Similarly, an alphanumeric string must be recognized as NAME and not as NAMETAB or NAMEFLD. TableGramText is the char* pointer giving the start of the token recognized. yyleng gives the length of the token recognized by flex. tableGramPosition() is an Int& keeping track of the position in the command string for error reporting in TableGram.cc. Note that lvalp is defined at the beginning of this file as the argument to TableGramlex (in the YY_DECL definition). The possible lvalp fields (such as lvalp->val) are defined in the union in TableGram.yy. */ /* In the SHOW command any word (such as SELECT) is allowed */ {NAME} { tableGramPosition() += yyleng; lvalp->val = new TaQLConstNode( new TaQLConstNodeRep (tableGramRemoveEscapes (TableGramtext))); TaQLNode::theirNodesCreated.push_back (lvalp->val); return NAME; } {UNION} { tableGramPosition() += yyleng; throw (TableInvExpr ("UNION is not supported yet")); } {INTERSECT} { tableGramPosition() += yyleng; throw (TableInvExpr ("INTERSECT is not supported yet")); } {EXCEPT} { tableGramPosition() += yyleng; return EXCEPT; } {STYLE} { tableGramPosition() += yyleng; BEGIN(STYLEstate); return STYLE; } {SELECT} { tableGramPosition() += yyleng; BEGIN(EXPRstate); return SELECT; } {UPDATE} { tableGramPosition() += yyleng; BEGIN(TABLENAMEstate); return UPDATE; } {SET} { tableGramPosition() += yyleng; BEGIN(EXPRstate); return UPDSET; } {INSERT} { tableGramPosition() += yyleng; return INSERT; } {VALUES} { tableGramPosition() += yyleng; BEGIN(EXPRstate); return VALUES; } {DELETE} { tableGramPosition() += yyleng; return DELETE; } {COUNT} { tableGramPosition() += yyleng; BEGIN(EXPRstate); return COUNT; } {COUNTALL} { tableGramPosition() += yyleng; BEGIN(EXPRstate); return COUNTALL; } {CALC} { tableGramPosition() += yyleng; BEGIN(EXPRstate); return CALC; } {CREATETAB} { tableGramPosition() += yyleng; BEGIN(TABLENAMEstate); return CREATETAB; } {ALTERTAB} { tableGramPosition() += yyleng; BEGIN(TABLENAMEstate); return ALTERTAB; } {DROPTAB} { tableGramPosition() += yyleng; BEGIN(TABLENAMEstate); return DROPTAB; } {ADDCOL} { tableGramPosition() += yyleng; BEGIN(EXPRstate); return ADDCOL; } {COPYCOL} { tableGramPosition() += yyleng; BEGIN(EXPRstate); return COPYCOL; } {RENAMECOL} { tableGramPosition() += yyleng; BEGIN(EXPRstate); return RENAMECOL; } {DROPCOL} { tableGramPosition() += yyleng; BEGIN(EXPRstate); return DROPCOL; } {SETKEY} { tableGramPosition() += yyleng; BEGIN(EXPRstate); return SETKEY; } {COPYKEY} { tableGramPosition() += yyleng; BEGIN(EXPRstate); return COPYKEY; } {RENAMEKEY} { tableGramPosition() += yyleng; BEGIN(EXPRstate); return RENAMEKEY; } {DROPKEY} { tableGramPosition() += yyleng; BEGIN(EXPRstate); return DROPKEY; } {ADDROW} { tableGramPosition() += yyleng; BEGIN(EXPRstate); return ADDROW; } {DMINFO} { tableGramPosition() += yyleng; BEGIN(EXPRstate); return DMINFO; } {WITH} { tableGramPosition() += yyleng; BEGIN(TABLENAMEstate); return WITH; } {FROM} { tableGramPosition() += yyleng; BEGIN(TABLENAMEstate); return FROM; } {WHERE} { tableGramPosition() += yyleng; BEGIN(EXPRstate); return WHERE; } {ORDERBY} { tableGramPosition() += yyleng; BEGIN(EXPRstate); return ORDERBY; } {NODUPL} { tableGramPosition() += yyleng; return NODUPL; } {DESC} { tableGramPosition() += yyleng; return SORTDESC; } {ASC} { tableGramPosition() += yyleng; return SORTASC; } {GIVING} { tableGramPosition() += yyleng; BEGIN(TABLENAMEstate); return GIVING; } {INTO} { tableGramPosition() += yyleng; BEGIN(TABLENAMEstate); return INTO; } {SUBTABLES} { tableGramPosition() += yyleng; BEGIN(EXPRstate); return SUBTABLES; } {LIMIT} { tableGramPosition() += yyleng; BEGIN(EXPRstate); return LIMIT; } {OFFSET} { tableGramPosition() += yyleng; BEGIN(EXPRstate); return OFFSET; } {GROUPBY} { tableGramPosition() += yyleng; BEGIN(EXPRstate); return GROUPBY; } {GROUPROLL} { tableGramPosition() += yyleng; BEGIN(EXPRstate); return GROUPROLL; } {HAVING} { tableGramPosition() += yyleng; BEGIN(EXPRstate); return HAVING; } {JOIN} { tableGramPosition() += yyleng; BEGIN(TABLENAMEstate); return JOIN; } {ON} { tableGramPosition() += yyleng; BEGIN(EXPRstate); return ON; } {AS} { tableGramPosition() += yyleng; return AS; } {TO} { tableGramPosition() += yyleng; return TO; } {IN} { tableGramPosition() += yyleng; return IN; } {INCONE} { tableGramPosition() += yyleng; return INCONE; } "[" { tableGramPosition() += yyleng; return LBRACKET; } "(" { tableGramPosition() += yyleng; return LPAREN; } "]" { tableGramPosition() += yyleng; return RBRACKET; } ")" { tableGramPosition() += yyleng; return RPAREN; } ";" { tableGramPosition() += yyleng; return SEMICOL; } /* UDF libname synonym definition */ {UDFLIBSYN} { tableGramPosition() += yyleng; lvalp->val = new TaQLConstNode( new TaQLConstNodeRep (tableGramRemoveEscapes (TableGramtext))); TaQLNode::theirNodesCreated.push_back (lvalp->val); return UDFLIBSYN; } /* regular expression and pattern handling */ {PATTREX} { tableGramPosition() += yyleng; lvalp->valre = new TaQLRegexNode( new TaQLRegexNodeRep (String(TableGramtext,yyleng))); TaQLNode::theirNodesCreated.push_back (lvalp->valre); return REGEX; } /* operators */ "<:>" { tableGramPosition() += yyleng; return MIDWIDTH; } "<:<" { tableGramPosition() += yyleng; return OPENOPEN; } "<:=" { tableGramPosition() += yyleng; return OPENCLOSED; } "=:<" { tableGramPosition() += yyleng; return CLOSEDOPEN; } "=:=" { tableGramPosition() += yyleng; return CLOSEDCLOSED; } "<:" { tableGramPosition() += yyleng; return OPENEMPTY; } ":<" { tableGramPosition() += yyleng; return EMPTYOPEN; } "=:" { tableGramPosition() += yyleng; return CLOSEDEMPTY; } ":=" { tableGramPosition() += yyleng; return EMPTYCLOSED; } ":" { tableGramPosition() += yyleng; return COLON; } "==" { tableGramPosition() += yyleng; return EQ; } "=" { tableGramPosition() += yyleng; return EQASS; } "!=" { tableGramPosition() += yyleng; return NE; } "<>" { tableGramPosition() += yyleng; return NE; } ">=" { tableGramPosition() += yyleng; return GE; } ">" { tableGramPosition() += yyleng; return GT; } "<=" { tableGramPosition() += yyleng; return LE; } "<" { tableGramPosition() += yyleng; return LT; } "~=" { tableGramPosition() += yyleng; return EQNEAR; } "!~=" { tableGramPosition() += yyleng; return NENEAR; } {BETWEEN} { tableGramPosition() += yyleng; return BETWEEN; } {AROUND} { tableGramPosition() += yyleng; return AROUND; } {EXISTS} { tableGramPosition() += yyleng; return EXISTS; } {LIKE} { tableGramPosition() += yyleng; return LIKE; } {ILIKE} { tableGramPosition() += yyleng; return ILIKE; } "&&" { tableGramPosition() += yyleng; return AND; } {AND} { tableGramPosition() += yyleng; return AND; } "||" { tableGramPosition() += yyleng; return OR; } {OR} { tableGramPosition() += yyleng; return OR; } "!" { tableGramPosition() += yyleng; return NOT; } {NOT} { tableGramPosition() += yyleng; return NOT; } "^" { tableGramPosition() += yyleng; return BITXOR; } {XOR} { tableGramPosition() += yyleng; return BITXOR; } "**" { tableGramPosition() += yyleng; return POWER; } "*" { tableGramPosition() += yyleng; return TIMES; } "//" { tableGramPosition() += yyleng; return DIVIDETRUNC; } "/" { tableGramPosition() += yyleng; return DIVIDE; } "%" { tableGramPosition() += yyleng; return MODULO; } "+" { tableGramPosition() += yyleng; return PLUS; } "-" { tableGramPosition() += yyleng; return MINUS; } "|" { tableGramPosition() += yyleng; return BITOR; } "&" { tableGramPosition() += yyleng; return BITAND; } "~" { tableGramPosition() += yyleng; return BITNOT; } "{" { tableGramPosition() += yyleng; return LBRACE; } "}" { tableGramPosition() += yyleng; return RBRACE; } "," { tableGramPosition() += yyleng; return COMMA; } /* Literals */ /* TIME must be done before FLINTUNIT, otherwise something like 2d1m is recognized as FLINTUNIT instead of TIME. Similarly COMPLEX must be done before FLINTUNIT. */ {COMPLEX} { tableGramPosition() += yyleng; double v; sscanf (TableGramtext, "%lf%*c", &v); lvalp->val = new TaQLConstNode( new TaQLConstNodeRep (DComplex(0, v))); TaQLNode::theirNodesCreated.push_back (lvalp->val); return LITERAL; } {FLOAT} { tableGramPosition() += yyleng; double v = atof(TableGramtext); lvalp->val = new TaQLConstNode(new TaQLConstNodeRep (v)); TaQLNode::theirNodesCreated.push_back (lvalp->val); return LITERAL; } {INT} { tableGramPosition() += yyleng; char* endPtr; Int64 v = strtoll(TableGramtext, &endPtr, 10); if (endPtr != TableGramtext+yyleng) { throw TableInvExpr ("Integer number not fully parsed"); } lvalp->val = new TaQLConstNode(new TaQLConstNodeRep (v)); TaQLNode::theirNodesCreated.push_back (lvalp->val); return LITERAL; } {HEXINT} { tableGramPosition() += yyleng; char* endPtr; Int64 v = strtoll(TableGramtext, &endPtr, 0); if (endPtr != TableGramtext+yyleng) { throw TableInvExpr ("Hex number not fully parsed"); } lvalp->val = new TaQLConstNode(new TaQLConstNodeRep (v)); TaQLNode::theirNodesCreated.push_back (lvalp->val); return LITERAL; } {TRUE} { tableGramPosition() += yyleng; lvalp->val = new TaQLConstNode(new TaQLConstNodeRep (True)); TaQLNode::theirNodesCreated.push_back (lvalp->val); return LITERAL; } {FALSE} { tableGramPosition() += yyleng; lvalp->val = new TaQLConstNode(new TaQLConstNodeRep (False)); TaQLNode::theirNodesCreated.push_back (lvalp->val); return LITERAL; } {STRING} { tableGramPosition() += yyleng; lvalp->val = new TaQLConstNode( new TaQLConstNodeRep (tableGramRemoveQuotes (TableGramtext))); TaQLNode::theirNodesCreated.push_back (lvalp->val); return STRINGLITERAL; } {DATETIME} { tableGramPosition() += yyleng; lvalp->val = new TaQLConstNode( new TaQLConstNodeRep (tableGramParseDateTime (TableGramtext))); TaQLNode::theirNodesCreated.push_back (lvalp->val); return LITERAL; } {TIME} { tableGramPosition() += yyleng; double v = tableGramParseTime (TableGramtext); lvalp->val = new TaQLConstNode( new TaQLConstNodeRep (v, String("rad"))); TaQLNode::theirNodesCreated.push_back (lvalp->val); return LITERAL; } {FLINTUNIT} { tableGramPosition() += yyleng; double v; char unit[32]; sscanf (TableGramtext, "%lf%31s", &v, unit); lvalp->val = new TaQLConstNode( new TaQLConstNodeRep (v, String(unit))); TaQLNode::theirNodesCreated.push_back (lvalp->val); return LITERAL; } /* In most states the word TIME is a normal column or function name. Otherwise it is the TIME keyword (to show timings). The same for SHOW. */ {TIMEWORD} { tableGramPosition() += yyleng; lvalp->val = new TaQLConstNode( new TaQLConstNodeRep (tableGramRemoveEscapes (TableGramtext))); TaQLNode::theirNodesCreated.push_back (lvalp->val); return NAME; } {TIMEWORD} { tableGramPosition() += yyleng; return TIMING; } {SHOW} { tableGramPosition() += yyleng; lvalp->val = new TaQLConstNode( new TaQLConstNodeRep (tableGramRemoveEscapes (TableGramtext))); TaQLNode::theirNodesCreated.push_back (lvalp->val); return NAME; } {SHOW} { tableGramPosition() += yyleng; BEGIN(SHOWstate); return SHOW; } /* In the FROM clause a shorthand (for a table) can be given. In the WHERE and ORDERBY clause a function name can be given. Note that this rule could also be done by NAMEFLD. However, in the future :: and . might be be operators instead of parts of the name. ALL is a special name, because it can also be used instead of DISTINCT in the SELECT clause (note that ALL is also a function name). So recognize ALL followed by a parenthesis as a function name. */ {ALLFUNC} { /* will not work for e.g. select all (1+2)*3, but nothing to do about it */ yyless(3); /* unput everything but ALL */ tableGramPosition() += yyleng; lvalp->val = new TaQLConstNode( new TaQLConstNodeRep (String("ALL"))); TaQLNode::theirNodesCreated.push_back (lvalp->val); return NAME; } {ALL} { tableGramPosition() += yyleng; return ALL; } {NAME} { tableGramPosition() += yyleng; lvalp->val = new TaQLConstNode( new TaQLConstNodeRep (tableGramRemoveEscapes (TableGramtext))); TaQLNode::theirNodesCreated.push_back (lvalp->val); return NAME; } /* Field names can be used in the SELECT, FROM, WHERE, and ORDERBY clause */ {NAMEFLD} { tableGramPosition() += yyleng; lvalp->val = new TaQLConstNode( new TaQLConstNodeRep (tableGramRemoveEscapes (TableGramtext))); TaQLNode::theirNodesCreated.push_back (lvalp->val); return FLDNAME; } /* A temporary table number possibly followed by a subtable name*/ {TEMPTAB} { tableGramPosition() += yyleng; Int64 ival = atoi(TableGramtext+1); lvalp->val = new TaQLConstNode( new TaQLConstNodeRep (ival, tableGramRemoveEscapes (TableGramtext))); TaQLNode::theirNodesCreated.push_back (lvalp->val); return TABNAME; } {TEMPTAB} { tableGramPosition() += yyleng; Int64 ival = atoi(TableGramtext+1); lvalp->val = new TaQLConstNode( new TaQLConstNodeRep (ival, tableGramRemoveEscapes (TableGramtext))); TaQLNode::theirNodesCreated.push_back (lvalp->val); return TABNAME; } /* An unquoted table file name can be given at several places */ {NAMETAB} { tableGramPosition() += yyleng; lvalp->val = new TaQLConstNode( new TaQLConstNodeRep (tableGramRemoveEscapesQuotes (TableGramtext))); TaQLNode::theirNodesCreated.push_back (lvalp->val); return TABNAME; } /* Whitespace is skipped */ {WHITE} { tableGramPosition() += yyleng; } {WHITE} { tableGramPosition() += yyleng; } /* Comment is skipped */ {COMMENT} { tableGramPosition() += yyleng; } {COMMENT} { tableGramPosition() += yyleng; } /* Any other non-white character is an error for SHOW */ {NONWHITE} { throw TableInvExpr ("Invalid character used in SHOW command"); } /* An unterminated string is an error */ {USTRING} { throw (TableInvExpr ("Unterminated string")); } /* terminate on EOF */ <> { yyterminate(); } /* Any other character is invalid */ . { return YYERRCODE; } %% casacore-3.7.1/tables/TaQL/TableGram.yy000066400000000000000000002256231476623553700176320ustar00rootroot00000000000000/* TableGram.yy: Parser for table commands Copyright (C) 1994,1995,1997,1998,1999,2001,2002,2003 Associated Universities, Inc. Washington DC, USA. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. Correspondence concerning AIPS++ should be addressed as follows: Internet email: casa-feedback@nrao.edu. Postal address: AIPS++ Project Office National Radio Astronomy Observatory 520 Edgemont Road Charlottesville, VA 22903-2475 USA */ %{ using namespace casacore; %} %pure-parser /* make parser re-entrant */ /* The grammar has 2 shift/reduce conflicts which are resolved in a correct way. - '(orexpr' can be the start of a set or be a subexpression. - '[name' can be the start of a set or subquery or be a record value. Expect them, so bison does not generate an error message. */ %expect 2 /* Define the terminals (tokens returned by flex), if needed with their type */ %token STYLE %token TIMING %token SHOW %token SELECT %token UPDATE %token UPDSET %token INSERT %token VALUES %token DELETE %token COUNT %token COUNTALL %token CALC %token CREATETAB %token ALTERTAB %token DROPTAB %token WITH %token FROM %token JOIN %token ON %token WHERE %token GROUPBY %token GROUPROLL %token HAVING %token ORDERBY %token NODUPL %token GIVING %token INTO %token SUBTABLES %token EXCEPT %token SORTASC %token SORTDESC %token LIMIT %token OFFSET %token ADDCOL %token COPYCOL %token RENAMECOL %token DROPCOL %token SETKEY %token COPYKEY %token RENAMEKEY %token DROPKEY %token ADDROW %token DMINFO %token ALL /* ALL (in SELECT ALL) */ %token NAME /* name of function, field, table, or alias */ %token UDFLIBSYN /* UDF library name synonym definition */ %token FLDNAME /* name of field or table */ %token TABNAME /* table name */ %token LITERAL %token STRINGLITERAL %token REGEX %token AS %token TO %token IN %token INCONE %token BETWEEN %token AROUND %token EXISTS %token LIKE %token ILIKE %token LPAREN %token RPAREN %token COMMA %token LBRACKET %token RBRACKET %token LBRACE %token RBRACE %token COLON %token SEMICOL %token MIDWIDTH %token OPENOPEN %token OPENCLOSED %token CLOSEDOPEN %token CLOSEDCLOSED %token OPENEMPTY %token EMPTYOPEN %token CLOSEDEMPTY %token EMPTYCLOSED /* Define all non-terminals with their '$$ return type' */ %type literal %type asdtype %type tabname %type stabname %type namefld %type unit %type tabalias %type tfnamen %type tfname %type showcomm %type selcomm %type updcomm %type inscomm %type delcomm %type dropcomm %type calccomm %type nestedcomm %type countcomm %type cretabcomm %type alttabcomm %type tfcommand %type subquery %type selcol %type normcol %type withpart %type fromtabs %type tables %type tablist %type likedrop %type likedropac %type concsub %type concslist %type concinto %type joins %type joinlist %type join %type whexpr %type groupby %type exprlist %type having %type order %type limitoff %type tabnmopts %type tabnmtyp %type given %type into %type colexpr %type wildcol %type nrowspec %type colspec %type columns %type collist %type nmcolumns %type colspecs %type colspecl %type showlist %type showflds %type updlist %type updexpr %type insclist %type insvalue %type insparts %type inspart %type insvlist %type altcomm %type altlist %type copycols %type rencols %type namelist %type renkeys %type dropkeys %type setkeys %type copykeys %type setkey %type copykey %type orexpr %type andexpr %type relexpr %type arithexpr %type inxexpr %type simexpr %type simbexpr %type set %type singlerange %type subscripts %type elemlist %type elems %type elem %type subsingle %type subsrange %type colonrange %type colonrangeinterval %type colonrangeindex %type range %type sortexpr %type sortlist %type dminfo %type dmlist %type dmelem %type recexpr %type recfield %type srecfield %type rrecfield %type brackval %type keyval %type srecval /* This defines the precedence order of the operators (low to high) */ %left OR %left AND %nonassoc EQ EQASS GT GE LT LE NE EQNEAR NENEAR %left BITOR %left BITXOR %left BITAND %left PLUS MINUS %left TIMES DIVIDE DIVIDETRUNC MODULO %nonassoc UNARY BITNOT %nonassoc NOT %right POWER /* Define the possible $$ value types. Alas you cannot use objects in a union, so pointers have to be used. They are not deleted automatically. Hence a vector (in TaQLNode) is used to keep track of the nodes created. They are deleted at the end of the parsing, also in case everything goes well since copies are used in the parse tree. */ %union { TaQLConstNode* val; TaQLRegexNode* valre; TaQLNode* node; TaQLConstNode* nodename; TaQLMultiNode* nodelist; TaQLQueryNode* nodeselect; TaQLRecFldNodeRep* noderecfldrep; } %{ int TableGramlex (YYSTYPE*); /* Define the functions in TableGram.ll to set EXPRstate or TABLENAMEstate from bison */ void setEXPRstate(); void setTABLENAMEstate(); %} /* Now define the parser by defining all non-terminal rules. The code belonging to a rule is executed if the entire rule is reconized by bison. The code builds a parse tree using the classes defined in TaQLNodeDer.h. That tree is traversed and execuited by TaQLNodeHandler. Note that $$ is the 'return value'. $1, $2, etc. are the rule arguments. */ %% /* A command can optionally be ended with a semicolon */ topcomm: topcomm1 | topcomm1 SEMICOL ; /* A command can be preceded by the TIME keyword and style arguments */ topcomm1: command | sttimcoms command ; sttimcoms: TIMING { TaQLNode::theirStyle.setTiming (True); } | stylecoms | stylecoms TIMING { TaQLNode::theirStyle.setTiming (True); } | TIMING stylecoms { TaQLNode::theirStyle.setTiming (True); } | stylecoms TIMING stylecoms { TaQLNode::theirStyle.setTiming (True); } ; /* Multiple STYLE commands can be given */ stylecoms: stylecoms stylecomm | stylecomm ; stylecomm: STYLE stylelist ; /* A style can consist of multiple keywords and UDFLIB synonyms */ stylelist: stylelist COMMA NAME { TaQLNode::theirStyle.set ($3->getString()); } | NAME { TaQLNode::theirStyle.set ($1->getString()); } | stylelist COMMA UDFLIBSYN { TaQLNode::theirStyle.defineSynonym ($3->getString()); } | UDFLIBSYN { TaQLNode::theirStyle.defineSynonym ($1->getString()); } ; /* The possible TaQL commands; nestedcomm can be used in a nested FROM */ command: selcomm { TaQLNode::theirNode = *$1; } | updcomm { TaQLNode::theirNode = *$1; } | inscomm { TaQLNode::theirNode = *$1; } | delcomm { TaQLNode::theirNode = *$1; } | dropcomm { TaQLNode::theirNode = *$1; } | calccomm { TaQLNode::theirNode = *$1; } | nestedcomm { TaQLNode::theirNode = *$1; } | showcomm { TaQLNode::theirNode = *$1; } ; /* The SHOW (or HELP) can have a list of names */ showcomm: SHOW showlist { $$ = new TaQLNode(new TaQLShowNodeRep (*$2)); TaQLNode::theirNodesCreated.push_back ($$); } ; showlist: { /* no list */ $$ = new TaQLMultiNode(); TaQLNode::theirNodesCreated.push_back ($$); } | showflds { $$ = $1; } ; /* This is the standard Bison way to define a list. First the addition to a list; thereafter the initial list. */ showflds: showflds tabname { $$ = $1; $$->add (*$2); } | tabname { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->setSeparator (" "); $$->add (*$1); } ; /* The commands (besides SELECT) that can be used in a nested FROM */ nestedcomm: countcomm { $$ = $1; } | cretabcomm { $$ = $1; } | alttabcomm { $$ = $1; } ; /* A nested FROM command is a subquery or one of the other commands enclosed in parentheses or square brackets */ tfcommand: subquery { $$ = $1; } | LPAREN nestedcomm RPAREN { $$ = $2; $$->setBrackets(); } | LBRACKET nestedcomm RBRACKET { $$ = $2; $$->setBrackets(); } ; /* A subquery must be enclosed in parentheses or square brackets */ subquery: LPAREN selcomm RPAREN { $$ = $2; $$->setBrackets(); } | LBRACKET selcomm RBRACKET { $$ = $2; $$->setBrackets(); } ; /* WITH table-list is optional */ withpart: { /* no WITH part */ $$ = new TaQLMultiNode(); TaQLNode::theirNodesCreated.push_back ($$); } | WITH tables { $$ = $2; } ; /* The SELECT command; note that many parts are optional which is handled in the rule of that part. The FROM part being optional is handled here because a join might be used. */ selcomm: withpart SELECT selcol FROM tables joins whexpr groupby having order limitoff given dminfo { $$ = new TaQLQueryNode( new TaQLSelectNodeRep (*$3, *$1, *$5, *$6, *$7, *$8, *$9, *$10, *$11, *$12, *$13)); TaQLNode::theirNodesCreated.push_back ($$); } | withpart SELECT selcol into FROM tables joins whexpr groupby having order limitoff dminfo { $$ = new TaQLQueryNode( new TaQLSelectNodeRep (*$3, *$1, *$6, *$7, *$8, *$9, *$10, *$11, *$12, *$4, *$13)); TaQLNode::theirNodesCreated.push_back ($$); } | withpart SELECT selcol whexpr groupby having order limitoff given dminfo { $$ = new TaQLQueryNode( new TaQLSelectNodeRep (*$3, *$1, *$4, *$5, *$6, *$7, *$8, *$9, *$10)); TaQLNode::theirNodesCreated.push_back ($$); } | withpart SELECT selcol into whexpr groupby having order limitoff dminfo { $$ = new TaQLQueryNode( new TaQLSelectNodeRep (*$3, *$1, *$5, *$6, *$7, *$8, *$9, *$4, *$10)); TaQLNode::theirNodesCreated.push_back ($$); } ; /* The column list can be preceded by ALL or DISTINCT */ selcol: normcol { $$ = $1; } | ALL columns { $$ = new TaQLNode( new TaQLColumnsNodeRep (False, *$2)); TaQLNode::theirNodesCreated.push_back ($$); } | NODUPL columns { $$ = new TaQLNode( new TaQLColumnsNodeRep (True, *$2)); TaQLNode::theirNodesCreated.push_back ($$); } ; normcol: columns { $$ = new TaQLNode( new TaQLColumnsNodeRep (False, *$1)); TaQLNode::theirNodesCreated.push_back ($$); } ; /* The COUNT command */ countcomm: withpart COUNT normcol FROM tables whexpr { $$ = new TaQLQueryNode( new TaQLCountNodeRep (*$1, *$3, *$5, *$6)); TaQLNode::theirNodesCreated.push_back ($$); } ; /* The UPDATE command */ updcomm: withpart UPDATE tables UPDSET updlist fromtabs whexpr order limitoff { $$ = new TaQLNode( new TaQLUpdateNodeRep (*$1, *$3, *$5, *$6, *$7, *$8, *$9)); TaQLNode::theirNodesCreated.push_back ($$); } ; /* The list of columns to be updated with their value expressions */ updlist: updlist COMMA updexpr { $$ = $1; $$->add (*$3); } | updexpr { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->add (*$1); } ; /* An array column to be updated can have a range and mask. Furthermore, a value and mask column can be assigned (from a masked array) */ updexpr: NAME EQASS orexpr { $$ = new TaQLNode( new TaQLUpdExprNodeRep ($1->getString(), "", *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | NAME LBRACKET subscripts RBRACKET EQASS orexpr { /* array slice or mask */ $$ = new TaQLNode( new TaQLUpdExprNodeRep ($1->getString(), "", *$3, *$6)); TaQLNode::theirNodesCreated.push_back ($$); } | NAME LBRACKET subscripts RBRACKET LBRACKET subscripts RBRACKET EQASS orexpr { /* array slice and mask (in any order) */ $$ = new TaQLNode( new TaQLUpdExprNodeRep ($1->getString(), "", *$3, *$6, *$9)); TaQLNode::theirNodesCreated.push_back ($$); } | LPAREN NAME COMMA NAME RPAREN EQASS orexpr { $$ = new TaQLNode( new TaQLUpdExprNodeRep ($2->getString(), $4->getString(), *$7)); TaQLNode::theirNodesCreated.push_back ($$); } | LPAREN NAME COMMA NAME RPAREN LBRACKET subscripts RBRACKET EQASS orexpr { $$ = new TaQLNode( new TaQLUpdExprNodeRep ($2->getString(), $4->getString(), *$7, *$10)); TaQLNode::theirNodesCreated.push_back ($$); } ; /* INSERT can be used in multiple ways */ inscomm: withpart INSERT INTO tables insclist selcomm { /* insert with SELECT command */ $6->setNoExecute(); $$ = new TaQLNode( new TaQLInsertNodeRep (*$1, *$4, *$5, *$6, TaQLNode())); TaQLNode::theirNodesCreated.push_back ($$); } | withpart INSERT INTO tables insclist insvalue { /* insert in SQL style */ $$ = new TaQLNode( new TaQLInsertNodeRep (*$1, *$4, *$5, *$6, TaQLNode())); TaQLNode::theirNodesCreated.push_back ($$); } | withpart INSERT LIMIT orexpr INTO tables insclist insvalue { $$ = new TaQLNode( new TaQLInsertNodeRep (*$1, *$6, *$7, *$8, *$4)); TaQLNode::theirNodesCreated.push_back ($$); } | withpart INSERT INTO tables insclist insvalue LIMIT orexpr { $$ = new TaQLNode( new TaQLInsertNodeRep (*$1, *$4, *$5, *$6, *$8)); TaQLNode::theirNodesCreated.push_back ($$); } | withpart INSERT INTO tables UPDSET updlist { /* insert in update style */ $$ = new TaQLNode( new TaQLInsertNodeRep (*$1, *$4, *$6)); TaQLNode::theirNodesCreated.push_back ($$); } ; /* The optional INSERT column list must be enclosed in parentheses or square brackets */ insclist: { /* no insert column-list */ $$ = new TaQLMultiNode(); TaQLNode::theirNodesCreated.push_back ($$); } | LBRACKET nmcolumns RBRACKET { $$ = $2; } | LPAREN nmcolumns RPAREN { $$ = $2; } ; insvalue: VALUES insparts { $2->setPPFix ("VALUES ", ""); $$ = $2; } ; /* The value list for the columns to be inserted can have multiple values, one for each row to be added */ insparts: insparts COMMA inspart { $$ = $1; $$->add (*$3); } | inspart { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->add (*$1); } ; /* Each row to be inserted has a list of values whose size should match the columns to be inserted */ inspart: LBRACKET insvlist RBRACKET { $$ = $2; } | LPAREN insvlist RPAREN { $$ = $2; } ; insvlist: insvlist COMMA orexpr { $$ = $1; $$->add (*$3); } | orexpr { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->setPPFix ("[", "]"); $$->add (*$1); } ; /* The DELETE command */ delcomm: withpart DELETE FROM tables whexpr order limitoff { $$ = new TaQLNode( new TaQLDeleteNodeRep (*$1, *$4, *$5, *$6, *$7)); TaQLNode::theirNodesCreated.push_back ($$); } ; /* The DROP TABLE command */ dropcomm: withpart DROPTAB tables { $$ = new TaQLNode( new TaQLDropTabNodeRep (*$1, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } ; /* The CALC command can calculate a single expression */ calccomm: withpart CALC FROM tables CALC orexpr { $$ = new TaQLNode( new TaQLCalcNodeRep (*$1, *$4, *$6, TaQLNode(), TaQLNode(), TaQLNode())); TaQLNode::theirNodesCreated.push_back ($$); } | withpart CALC orexpr fromtabs whexpr order limitoff { $$ = new TaQLNode( new TaQLCalcNodeRep (*$1, *$4, *$3, *$5, *$6, *$7)); TaQLNode::theirNodesCreated.push_back ($$); } ; /* The CREATE TABLE command has a few flavours. Optionally LIKE-DROP can be given, followed by ADDCOLUMN. Note that the first rule requires a non-empty column list, otherwise there is a reduce conflict with the 2nd rule. */ cretabcomm: withpart CREATETAB tabnmtyp colspecl nrowspec dminfo { $$ = new TaQLQueryNode( new TaQLCreTabNodeRep (*$1, *$3, TaQLMultiNode(), *$4, *$5, *$6)); TaQLNode::theirNodesCreated.push_back ($$); } | withpart CREATETAB tabnmtyp likedrop nrowspec dminfo { $$ = new TaQLQueryNode( new TaQLCreTabNodeRep (*$1, *$3, *$4, TaQLMultiNode(False), *$5, *$6)); TaQLNode::theirNodesCreated.push_back ($$); } | withpart CREATETAB tabnmtyp likedropac LPAREN colspecs RPAREN nrowspec dminfo { $6->setPPFix ("(", ")"); $$ = new TaQLQueryNode( new TaQLCreTabNodeRep (*$1, *$3, *$4, *$6, *$8, *$9)); TaQLNode::theirNodesCreated.push_back ($$); } | withpart CREATETAB tabnmtyp likedropac LBRACKET colspecs RBRACKET nrowspec dminfo { $6->setPPFix ("[", "]"); $$ = new TaQLQueryNode( new TaQLCreTabNodeRep (*$1, *$3, *$4, *$6, *$8, *$9)); TaQLNode::theirNodesCreated.push_back ($$); } ; /* The ALTER TABLE command */ alttabcomm: withpart ALTERTAB tabalias fromtabs altlist { $$ = new TaQLQueryNode( new TaQLAltTabNodeRep (*$1, *$3, *$4, *$5)); TaQLNode::theirNodesCreated.push_back ($$); } ; /* The ALTER TABLE commands consists of one or more subcommands */ altlist: altlist altcomm { $$ = $1; $$->add (*$2); } | altcomm { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->setSeparator (" "); $$->add (*$1); } ; /* The ALTER TABLE subcommands */ altcomm: ADDCOL colspecl dminfo { $$ = new TaQLNode ( new TaQLAddColNodeRep(*$2, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | COPYCOL copycols dminfo { $$ = new TaQLNode( new TaQLCopyColNodeRep(*$2, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | RENAMECOL rencols { $$ = new TaQLNode( new TaQLRenDropNodeRep(0, *$2)); TaQLNode::theirNodesCreated.push_back ($$); } | DROPCOL namelist { $$ = new TaQLNode( new TaQLRenDropNodeRep(1, *$2)); TaQLNode::theirNodesCreated.push_back ($$); } | SETKEY setkeys { $$ = new TaQLNode( new TaQLSetKeyNodeRep (*$2)); TaQLNode::theirNodesCreated.push_back ($$); } | COPYKEY copykeys { $$ = new TaQLNode( new TaQLSetKeyNodeRep (*$2)); TaQLNode::theirNodesCreated.push_back ($$); } | RENAMEKEY renkeys { $$ = new TaQLNode( new TaQLRenDropNodeRep(2, *$2)); TaQLNode::theirNodesCreated.push_back ($$); } | DROPKEY dropkeys { $$ = new TaQLNode( new TaQLRenDropNodeRep(3, *$2)); TaQLNode::theirNodesCreated.push_back ($$); } | ADDROW orexpr { $$ = new TaQLNode( new TaQLAddRowNodeRep(*$2)); TaQLNode::theirNodesCreated.push_back ($$); } ; /* COPY COLUMN subcommand can copy multiple columns */ copycols: copycols COMMA NAME EQASS namefld { $$ = $1; $$->add (new TaQLKeyColNodeRep ($3->getString())); $$->add (new TaQLKeyColNodeRep ($5->getString())); } | NAME EQASS namefld { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->setSeparator (2, "="); $$->add (new TaQLKeyColNodeRep ($1->getString())); $$->add (new TaQLKeyColNodeRep ($3->getString())); } ; /* RENAME COLUMN subcommand can rename multiple columns */ rencols: rencols COMMA NAME TO NAME { $$ = $1; $$->add (new TaQLKeyColNodeRep ($3->getString())); $$->add (new TaQLKeyColNodeRep ($5->getString())); } | NAME TO NAME { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->setSeparator (2, " TO "); $$->add (new TaQLKeyColNodeRep ($1->getString())); $$->add (new TaQLKeyColNodeRep ($3->getString())); } ; /* A comma separated liost of names */ namelist: namelist COMMA NAME { $$ = $1; $$->add (new TaQLKeyColNodeRep ($3->getString())); } | NAME { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->add (new TaQLKeyColNodeRep ($1->getString())); } ; /* RENAME COLUMN subcommand can rename multiple columns */ renkeys: renkeys COMMA namefld TO NAME { $$ = $1; $$->add (new TaQLKeyColNodeRep ($3->getString())); $$->add (new TaQLKeyColNodeRep ($5->getString())); } | namefld TO NAME { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->setSeparator (2, " TO "); $$->add (new TaQLKeyColNodeRep ($1->getString())); $$->add (new TaQLKeyColNodeRep ($3->getString())); } ; /* DROP KEYWORD subcommand can remove multiple keywords */ dropkeys: dropkeys COMMA namefld { $$ = $1; $$->add (new TaQLKeyColNodeRep ($3->getString())); } | namefld { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->add (new TaQLKeyColNodeRep ($1->getString())); } ; /* SET KEYWORD subcommand can set multiple keywords */ setkeys: setkeys COMMA setkey { $$ = $1; $$->add (*$3); } | setkey { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->add (*$1); } setkey: namefld EQASS keyval { $$ = new TaQLNode( new TaQLRecFldNodeRep($1->getString(), *$3)); TaQLNode::theirNodesCreated.push_back ($$); delete $3; } ; /* COPY KEYWORD subcommand can copy multiple keywords */ copykeys: copykeys COMMA copykey { $$ = $1; $$->add (*$3); } | copykey { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->add (*$1); } ; /* A copied keyword can get another data type */ copykey: namefld EQASS namefld asdtype { $$ = new TaQLNode( new TaQLRecFldNodeRep($1->getString(), $3->getString(), $4->getString())); TaQLNode::theirNodesCreated.push_back ($$); } ; keyval: srecval { $$ = $1; } | brackval { $$ = $1; } ; /* Keyword values using square brackets */ brackval: LBRACKET recexpr RBRACKET { $$ = new TaQLRecFldNodeRep ("", *$2, ""); } | LBRACKET EQASS RBRACKET { /* Like in glish [=] is the syntax for an empty 'record' */ TaQLMultiNode empty(False); empty.setPPFix ("[", "]"); $$ = new TaQLRecFldNodeRep ("", empty, ""); } | LBRACKET RBRACKET AS NAME { /* empty vector of the datatype given by NAME */ $$ = new TaQLRecFldNodeRep ("", TaQLNode(), $4->getString()); } ; /* The DataManager info (used by various commands) */ dminfo: { /* no datamans */ $$ = new TaQLMultiNode(); TaQLNode::theirNodesCreated.push_back ($$); } | DMINFO dmlist { $$ = $2; } ; /* A (non-empty) list of expressions */ exprlist: exprlist COMMA orexpr { $$ = $1; $$->add (*$3); } | orexpr { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->add (*$1); } ; /* There does not need to be a GROUPBY clause. GROUPBY ROLLUP is not implemented (yet) */ groupby: { /* no groupby */ $$ = new TaQLNode(); TaQLNode::theirNodesCreated.push_back ($$); } | GROUPBY exprlist { $$ = new TaQLNode( new TaQLGroupNodeRep (TaQLGroupNodeRep::Normal, *$2)); TaQLNode::theirNodesCreated.push_back ($$); } | GROUPROLL exprlist { $$ = new TaQLNode( new TaQLGroupNodeRep (TaQLGroupNodeRep::Rollup, *$2)); TaQLNode::theirNodesCreated.push_back ($$); } ; /* There does not need to be a HAVING clause. */ having: { /* no having */ $$ = new TaQLNode(); TaQLNode::theirNodesCreated.push_back ($$); } | HAVING orexpr { $$ = $2; } ; /* There does not need to be an ORDERBY clause. If there, the default sort order can be given first, which can be given per expression as well. ASCENDING (or DESCENDING) and DISTINCT can be given in either order. */ order: { /* no sort */ $$ = new TaQLNode(); TaQLNode::theirNodesCreated.push_back ($$); } | ORDERBY sortlist { $$ = new TaQLNode( new TaQLSortNodeRep (False, TaQLSortNodeRep::Ascending, *$2)); TaQLNode::theirNodesCreated.push_back ($$); } | ORDERBY SORTASC sortlist { $$ = new TaQLNode( new TaQLSortNodeRep (False, TaQLSortNodeRep::Ascending, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | ORDERBY SORTDESC sortlist { $$ = new TaQLNode( new TaQLSortNodeRep (False, TaQLSortNodeRep::Descending, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | ORDERBY NODUPL sortlist { $$ = new TaQLNode( new TaQLSortNodeRep (True, TaQLSortNodeRep::Ascending, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | ORDERBY NODUPL SORTASC sortlist { $$ = new TaQLNode( new TaQLSortNodeRep (True, TaQLSortNodeRep::Ascending, *$4)); TaQLNode::theirNodesCreated.push_back ($$); } | ORDERBY NODUPL SORTDESC sortlist { $$ = new TaQLNode( new TaQLSortNodeRep (True, TaQLSortNodeRep::Descending, *$4)); TaQLNode::theirNodesCreated.push_back ($$); } | ORDERBY SORTASC NODUPL sortlist { $$ = new TaQLNode( new TaQLSortNodeRep (True, TaQLSortNodeRep::Ascending, *$4)); TaQLNode::theirNodesCreated.push_back ($$); } | ORDERBY SORTDESC NODUPL sortlist { $$ = new TaQLNode( new TaQLSortNodeRep (True, TaQLSortNodeRep::Descending, *$4)); TaQLNode::theirNodesCreated.push_back ($$); } ; /* There does not need to be a LIMIT/OFFSET clause. LIMIT can be given as a start:end:step range in which case OFFSET cannot be given. Otherwise LIMIT and/or OFFSET take a single value and can be given in either order. */ limitoff: { /* no limit,offset */ $$ = new TaQLNode(); TaQLNode::theirNodesCreated.push_back ($$); } | LIMIT colonrangeinterval { $$ = new TaQLNode( new TaQLLimitOffNodeRep (*$2, 0)); TaQLNode::theirNodesCreated.push_back ($$); } | LIMIT orexpr { $$ = new TaQLNode( new TaQLLimitOffNodeRep (*$2, 0)); TaQLNode::theirNodesCreated.push_back ($$); } | OFFSET orexpr { $$ = new TaQLNode( new TaQLLimitOffNodeRep (0, *$2)); TaQLNode::theirNodesCreated.push_back ($$); } | LIMIT orexpr OFFSET orexpr { $$ = new TaQLNode( new TaQLLimitOffNodeRep (*$2, *$4)); TaQLNode::theirNodesCreated.push_back ($$); } | OFFSET orexpr LIMIT orexpr { $$ = new TaQLNode( new TaQLLimitOffNodeRep (*$4, *$2)); TaQLNode::theirNodesCreated.push_back ($$); } ; /* The optional table name and options when creating a table */ tabnmtyp: tabname { $$ = new TaQLNode( new TaQLGivingNodeRep ($1->getString(), TaQLMultiNode())); TaQLNode::theirNodesCreated.push_back ($$); } | tabname AS tabnmopts { $$ = new TaQLNode( new TaQLGivingNodeRep ($1->getString(), *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | AS tabnmopts { $$ = new TaQLNode( new TaQLGivingNodeRep ("", *$2)); TaQLNode::theirNodesCreated.push_back ($$); } ; /* The table creation options specified as a bracketed keyword=value list */ tabnmopts: NAME { /* PLAIN_BIG, etc. for backward compatibility */ TaQLNode val(new TaQLConstNodeRep (True)); $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->setPPFix ("[", "]"); $$->add (new TaQLRecFldNodeRep ($1->getString(), val, "")); } | LBRACKET recexpr RBRACKET { $$ = $2; } ; /* The optional GIVING clause can result in a table or a set */ givenlb: LBRACKET { setEXPRstate(); } given: { /* no result */ $$ = new TaQLNode(); TaQLNode::theirNodesCreated.push_back ($$); } | GIVING tabnmtyp { $$ = $2; } | GIVING givenlb elems RBRACKET { $$ = new TaQLNode( new TaQLGivingNodeRep (*$3)); TaQLNode::theirNodesCreated.push_back ($$); } ; /* If INTO instead of GIVING is used, the only result can be a table */ into: INTO tabnmtyp { $$ = $2; } ; /* The optional list of columns in the SELECT clause */ columns: { /* no columns given (thus take all) */ $$ = new TaQLMultiNode(); TaQLNode::theirNodesCreated.push_back ($$); } | collist { $$ = $1; } ; /* List of columns */ collist: colexpr { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->add (*$1); } | collist COMMA colexpr { $$ = $1; $$->add (*$3); } ; /* A selected column is an expression optionally followed by its name and possibly data type. To handle a masked array, two column names can be given (one for the value and one for the mask). Note that the data type applies to the value column (since the mask is Bool). It is also possible to use wildcards in the column list. */ colexpr: orexpr { $$ = new TaQLNode( new TaQLColNodeRep (*$1, "", "", "")); TaQLNode::theirNodesCreated.push_back ($$); } | orexpr AS NAME { $$ = new TaQLNode( new TaQLColNodeRep (*$1, $3->getString(), "", "")); TaQLNode::theirNodesCreated.push_back ($$); } | orexpr AS LPAREN NAME COMMA NAME RPAREN { $$ = new TaQLNode( new TaQLColNodeRep (*$1, $4->getString(), $6->getString(), "")); TaQLNode::theirNodesCreated.push_back ($$); } | orexpr AS NAME NAME { /* name and data type */ $$ = new TaQLNode( new TaQLColNodeRep (*$1, $3->getString(), "", $4->getString())); TaQLNode::theirNodesCreated.push_back ($$); } | orexpr AS LPAREN NAME COMMA NAME RPAREN NAME { $$ = new TaQLNode( new TaQLColNodeRep (*$1, $4->getString(), $6->getString(), $8->getString())); TaQLNode::theirNodesCreated.push_back ($$); } | wildcol { $$= $1; } ; /* A wildcard in a SELECT clause can be * or a regex. * means all columns of the input table. A regex (with includes ~ or !~) can be used to exclude (or include) columns. */ wildcol: TIMES { /* SELECT * FROM ... */ TaQLRegexNode p (new TaQLRegexNodeRep ("~p/*/")); $$ = new TaQLNode (new TaQLColNodeRep (p, "", "", "")); TaQLNode::theirNodesCreated.push_back ($$); } | REGEX { $$ = new TaQLNode (new TaQLColNodeRep (*$1, "", "", "")); TaQLNode::theirNodesCreated.push_back ($$); } ; /* The column list for an INSERT command. Similar to the SELECT clause, two names can be given for a masked array. Note that a data type cannot be given, since the specified columns must exist, thus already have a data type. */ nmcolumns: NAME { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->add (new TaQLKeyColNodeRep ($1->getString())); } | LPAREN NAME COMMA NAME RPAREN { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->add (new TaQLKeyColNodeRep ($2->getString(), $4->getString())); } | nmcolumns COMMA NAME { $$ = $1; $$->add (new TaQLKeyColNodeRep ($3->getString())); } | nmcolumns COMMA LPAREN NAME COMMA NAME RPAREN { $$ = $1; $$->add (new TaQLKeyColNodeRep ($4->getString(), $6->getString())); } ; /* In CREATE TABLE the LIMIT clause can be used to specify #rows */ nrowspec: { /* no nrows given */ $$ = new TaQLNode(); TaQLNode::theirNodesCreated.push_back ($$); } | LIMIT orexpr { $$ = $2; } ; /* Optional column specifications can be given in CREATE TABLE */ colspecs: { /* no column specifications given */ $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); } | colspecl { $$ = $1; } ; /* A non-empty list of column specifications (also for ADD COLUMN) */ colspecl: colspec { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->add (*$1); } | colspecl COMMA colspec { $$ = $1; $$->add (*$3); } ; /* The specification of a column consists of a name, data type and possibly a bracketed key=value list for properties such as NDIM, etc. A single property can be given as a non-bracketed key=value. */ colspec: NAME NAME { $$ = new TaQLNode( new TaQLColSpecNodeRep($1->getString(), String(), $2->getString(), TaQLMultiNode())); TaQLNode::theirNodesCreated.push_back ($$); } | NAME NAME srecfield { TaQLMultiNode re(False); re.add (*$3); $$ = new TaQLNode( new TaQLColSpecNodeRep($1->getString(), String(), $2->getString(), re)); TaQLNode::theirNodesCreated.push_back ($$); } | NAME NAME LBRACKET recexpr RBRACKET { $$ = new TaQLNode( new TaQLColSpecNodeRep($1->getString(), String(), $2->getString(), *$4)); TaQLNode::theirNodesCreated.push_back ($$); } | NAME LIKE namefld { $$ = new TaQLNode( new TaQLColSpecNodeRep($1->getString(), $3->getString(), String(), TaQLMultiNode())); TaQLNode::theirNodesCreated.push_back ($$); } | NAME LIKE namefld srecfield { TaQLMultiNode re(False); re.add (*$4); $$ = new TaQLNode( new TaQLColSpecNodeRep($1->getString(), $3->getString(), String(), re)); TaQLNode::theirNodesCreated.push_back ($$); } | NAME LIKE namefld LBRACKET recexpr RBRACKET { $$ = new TaQLNode( new TaQLColSpecNodeRep($1->getString(), $3->getString(), String(), *$5)); TaQLNode::theirNodesCreated.push_back ($$); } | NAME LIKE namefld NAME { $$ = new TaQLNode( new TaQLColSpecNodeRep($1->getString(), $3->getString(), $4->getString(), TaQLMultiNode())); TaQLNode::theirNodesCreated.push_back ($$); } | NAME LIKE namefld NAME srecfield { TaQLMultiNode re(False); re.add (*$4); $$ = new TaQLNode( new TaQLColSpecNodeRep($1->getString(), $3->getString(), $4->getString(), re)); TaQLNode::theirNodesCreated.push_back ($$); } | NAME LIKE namefld NAME LBRACKET recexpr RBRACKET { $$ = new TaQLNode( new TaQLColSpecNodeRep($1->getString(), $3->getString(), $4->getString(), *$6)); TaQLNode::theirNodesCreated.push_back ($$); } ; /* Optional 'LIKE tab' with an optional DROP COLUMN columname-list */ likedrop: { /* no LIKE table given */ $$ = new TaQLMultiNode(); TaQLNode::theirNodesCreated.push_back ($$); } | LIKE tabalias { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->add (*$2); } | LIKE tabalias DROPCOL namelist { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->add (*$2); $$->add (*$4); } ; /* Optional 'LIKE tab DROP COLUMN' where ADDCOLUMN must be used. */ likedropac: { /* no LIKE table given */ $$ = new TaQLMultiNode(); TaQLNode::theirNodesCreated.push_back ($$); } | ADDCOL { /* A superfluous ADDCOLUMN is possible */ $$ = new TaQLMultiNode(); TaQLNode::theirNodesCreated.push_back ($$); } | LIKE tabalias ADDCOL { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->add (*$2); } | LIKE tabalias DROPCOL namelist ADDCOL { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->add (*$2); $$->add (*$4); } ; /* An optional list of FROM tables */ fromtabs: { /* no FROM tables given */ $$ = new TaQLMultiNode(); TaQLNode::theirNodesCreated.push_back ($$); } | FROM tables { $$ = $2; } ; /* A list of tables with optional aliases. */ tables: tablist { $$ = $1; /* All table names are processed, thus expressions hereafter */ setEXPRstate(); } ; tablist: tabalias { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->add (*$1); /* Another table name can be expected */ setTABLENAMEstate(); } | tablist COMMA tabalias { $$ = $1; $$->add (*$3); /* Another table name can be expected */ setTABLENAMEstate(); } ; /* If NAME is given, it is purely alphanumeric, so it can be used as alias. This is not the case if another type or name is given, so in that case there is no alias. Hence the 2 cases have to be handled differently. Note that the alias can be given with or without AS. It can also be given in a reversed way using IN (which is OQL syntax). */ tabalias: NAME { /* table name is also alias */ $1->setIsTableName(); $$ = new TaQLNode( new TaQLTableNodeRep(*$1, $1->getString())); TaQLNode::theirNodesCreated.push_back ($$); } | tfname { /* no alias */ $$ = new TaQLNode( new TaQLTableNodeRep(*$1, "")); TaQLNode::theirNodesCreated.push_back ($$); } | tfnamen NAME { /* table name and alias */ $2->setIsTableName(); $$ = new TaQLNode( new TaQLTableNodeRep(*$1, $2->getString())); TaQLNode::theirNodesCreated.push_back ($$); } | tfnamen AS NAME { $$ = new TaQLNode( new TaQLTableNodeRep(*$1, $3->getString())); TaQLNode::theirNodesCreated.push_back ($$); } | NAME IN tfnamen { $$ = new TaQLNode( new TaQLTableNodeRep(*$3, $1->getString())); TaQLNode::theirNodesCreated.push_back ($$); } ; /* General table specification */ tfnamen: tfname { $$ = $1; } | NAME { $1->setIsTableName(); $$ = $1; } ; /* Slightly more specific table specification. The tabalias rule above needs a separate line for NAME, therefore this rule does not include it. The table can be a subquery, table name, or table concatenation. With table concatenation it is possible to specify the subtables to concatenate and the GIVING/INTO to make the concat table persistent. */ tfname: tfcommand { $1->setFromExecute(); $$ = $1; } | stabname { $$ = $1; } | LBRACKET tables concsub concinto RBRACKET { $$ = new TaQLNode( new TaQLConcTabNodeRep($4->getString(), *$2, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } ; /* Subtable concatenation is optional */ concsub: { /* no SUBTABLES */ $$ = new TaQLMultiNode(); TaQLNode::theirNodesCreated.push_back ($$); } | SUBTABLES concslist { $$ = $2; } ; /* A list of subtables to concatenate */ concslist: NAME { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $1->setIsTableName(); $$->add (*$1); } | concslist COMMA NAME { $$ = $1; $3->setIsTableName(); $$->add (*$3); } ; /* Concat table persistency using GIVING or INTO is optional */ concgiven: GIVING { setTABLENAMEstate(); } | INTO { setTABLENAMEstate(); } concinto: { /* no GIVING */ $$ = new TaQLConstNode(new TaQLConstNodeRep(String())); TaQLNode::theirNodesCreated.push_back ($$); } | concgiven tabname { $$ = $2; } ; /* A table name can contain various characters, possibly using a quoted literal */ stabname: TABNAME { $1->setIsTableName(); $$ = $1; } | FLDNAME { $1->setIsTableName(); $$ = $1; } | STRINGLITERAL { $1->setIsTableName(); $$ = $1; } ; /* A general table name also includes an aplhanumeric name */ tabname: NAME { $1->setIsTableName(); $$ = $1; } | stabname { $$ = $1; } ; /* JOIN ON is optional */ joins: { /* no joins */ $$ = new TaQLMultiNode(); TaQLNode::theirNodesCreated.push_back ($$); } | joinlist { $$ = $1; } ; joinlist: joinlist join { $$ = $1; $$->add (*$2); } | join { $$ = new TaQLMultiNode(False); $$->setSeparator (String()); TaQLNode::theirNodesCreated.push_back ($$); $$->add (*$1); } ; join: JOIN tablist ON orexpr { $$ = new TaQLNode (new TaQLJoinNodeRep (*$2, *$4)); TaQLNode::theirNodesCreated.push_back ($$); } ; /* WHERE is optional */ whexpr: { /* no selection */ $$ = new TaQLNode(); TaQLNode::theirNodesCreated.push_back ($$); } | WHERE orexpr { $$ = $2; } ; /* Multiple ORs can be used (OR has lowest precedence) */ orexpr: andexpr { $$ = $1; } | orexpr OR andexpr { $$ = new TaQLNode( new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_OR, *$1, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } ; /* Multiple ANDs can be used (AND has higher precedence) */ andexpr: relexpr { $$ = $1; } | andexpr AND relexpr { $$ = new TaQLNode( new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_AND, *$1, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } ; /* All possible logical expressions */ relexpr: arithexpr { $$ = $1; } | arithexpr EQ arithexpr { $$ = new TaQLNode( new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_EQ, *$1, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr EQASS arithexpr { $$ = new TaQLNode( new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_EQ, *$1, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr GT arithexpr { $$ = new TaQLNode( new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_GT, *$1, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr GE arithexpr { $$ = new TaQLNode( new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_GE, *$1, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr LT arithexpr { $$ = new TaQLNode( new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_LT, *$1, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr LE arithexpr { $$ = new TaQLNode( new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_LE, *$1, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr NE arithexpr { $$ = new TaQLNode( new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_NE, *$1, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr EQNEAR arithexpr { /* ~= means function NEAR */ TaQLMultiNode set(False); set.add (*$1); set.add (*$3); set.add (TaQLConstNode(new TaQLConstNodeRep(1e-5))); $$ = new TaQLNode (new TaQLFuncNodeRep("NEAR", set)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr NENEAR arithexpr { /* !~= means NOT function NEAR */ TaQLMultiNode set(False); set.add (*$1); set.add (*$3); set.add (TaQLConstNode(new TaQLConstNodeRep(1e-5))); TaQLNode ref (new TaQLFuncNodeRep("NEAR", set)); $$ = new TaQLNode( new TaQLUnaryNodeRep (TaQLUnaryNodeRep::U_NOT, ref)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr REGEX { /* REGEX also contains operator ~ or !~ */ $$ = new TaQLNode(TaQLBinaryNodeRep::handleRegex (*$1, *$2)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr LIKE arithexpr { TaQLMultiNode re(False); re.add (*$3); TaQLNode ref (new TaQLFuncNodeRep("SQLPATTERN", re)); $$ = new TaQLNode( new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_EQ, *$1, ref)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr ILIKE arithexpr { /* case-insensitive LIKE */ TaQLMultiNode mn1(False); mn1.add (*$1); TaQLNode tn1 (new TaQLFuncNodeRep("LOWER", mn1)); TaQLMultiNode mn2(False); mn2.add (*$3); TaQLNode tn2 (new TaQLFuncNodeRep("LOWER", mn2)); TaQLMultiNode re(False); re.add (tn2); TaQLNode ref (new TaQLFuncNodeRep("SQLPATTERN", re)); $$ = new TaQLNode( new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_EQ, tn1, ref)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr NOT LIKE arithexpr { TaQLMultiNode re(False); re.add (*$4); TaQLNode ref (new TaQLFuncNodeRep("SQLPATTERN", re)); $$ = new TaQLNode( new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_NE, *$1, ref)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr NOT ILIKE arithexpr { TaQLMultiNode mn1(False); mn1.add (*$1); TaQLNode tn1 (new TaQLFuncNodeRep("LOWER", mn1)); TaQLMultiNode mn2(False); mn2.add (*$4); TaQLNode tn2 (new TaQLFuncNodeRep("LOWER", mn2)); TaQLMultiNode re(False); re.add (tn2); TaQLNode ref (new TaQLFuncNodeRep("SQLPATTERN", re)); $$ = new TaQLNode( new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_NE, tn1, ref)); TaQLNode::theirNodesCreated.push_back ($$); } | EXISTS subquery { /* is subquery result non-empty */ $2->setNoExecute(); $$ = new TaQLNode( new TaQLUnaryNodeRep (TaQLUnaryNodeRep::U_EXISTS, *$2)); TaQLNode::theirNodesCreated.push_back ($$); } | NOT EXISTS subquery { $3->setNoExecute(); $$ = new TaQLNode( new TaQLUnaryNodeRep (TaQLUnaryNodeRep::U_NOTEXISTS, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr IN arithexpr { $$ = new TaQLNode( new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_IN, *$1, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr NOT IN arithexpr { TaQLNode p( new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_IN, *$1, *$4)); $$ = new TaQLNode( new TaQLUnaryNodeRep (TaQLUnaryNodeRep::U_NOT, p)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr IN singlerange { $$ = new TaQLNode( new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_IN, *$1, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr NOT IN singlerange { TaQLNode p (new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_IN, *$1, *$4)); $$ = new TaQLNode( new TaQLUnaryNodeRep (TaQLUnaryNodeRep::U_NOT, p)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr BETWEEN arithexpr AND arithexpr { TaQLMultiNode pr(False); pr.add (new TaQLRangeNodeRep (True, *$3, *$5, True)); $$ = new TaQLNode( new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_IN, *$1, pr)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr NOT BETWEEN arithexpr AND arithexpr { TaQLMultiNode pr(False); pr.add (new TaQLRangeNodeRep (True, *$4, *$6, True)); TaQLNode p (new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_IN, *$1, pr)); $$ = new TaQLNode( new TaQLUnaryNodeRep (TaQLUnaryNodeRep::U_NOT, p)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr AROUND arithexpr IN arithexpr { TaQLMultiNode pr(False); pr.add (new TaQLRangeNodeRep (*$3, *$5)); $$ = new TaQLNode( new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_IN, *$1, pr)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr NOT AROUND arithexpr IN arithexpr { TaQLMultiNode pr(False); pr.add (new TaQLRangeNodeRep (*$4, *$6)); TaQLNode p (new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_IN, *$1, pr)); $$ = new TaQLNode( new TaQLUnaryNodeRep (TaQLUnaryNodeRep::U_NOT, p)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr INCONE arithexpr { TaQLMultiNode pr(False); pr.add (*$1); pr.add (*$3); $$ = new TaQLNode( new TaQLFuncNodeRep ("anyCone", pr)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr NOT INCONE arithexpr { TaQLMultiNode pr(False); pr.add (*$1); pr.add (*$4); TaQLNode p (new TaQLFuncNodeRep ("anyCone", pr)); $$ = new TaQLNode( new TaQLUnaryNodeRep (TaQLUnaryNodeRep::U_NOT, p)); TaQLNode::theirNodesCreated.push_back ($$); } ; /* All possible numeric expressions */ arithexpr: simexpr { $$= $1; } | arithexpr PLUS arithexpr { $$ = new TaQLNode( new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_PLUS, *$1, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr MINUS arithexpr { $$ = new TaQLNode( new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_MINUS, *$1, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr TIMES arithexpr { $$ = new TaQLNode( new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_TIMES, *$1, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr DIVIDE arithexpr { $$ = new TaQLNode( new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_DIVIDE, *$1, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr DIVIDETRUNC arithexpr { $$ = new TaQLNode( new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_DIVIDETRUNC, *$1, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr MODULO arithexpr { $$ = new TaQLNode( new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_MODULO, *$1, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr BITAND arithexpr { $$ = new TaQLNode( new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_BITAND, *$1, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr BITXOR arithexpr { $$ = new TaQLNode( new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_BITXOR, *$1, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr BITOR arithexpr { $$ = new TaQLNode( new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_BITOR, *$1, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | MINUS arithexpr %prec UNARY { $$ = new TaQLNode( new TaQLUnaryNodeRep (TaQLUnaryNodeRep::U_MINUS, *$2)); TaQLNode::theirNodesCreated.push_back ($$); } | PLUS arithexpr %prec UNARY { $$ = $2; } | BITNOT arithexpr { $$ = new TaQLNode( new TaQLUnaryNodeRep (TaQLUnaryNodeRep::U_BITNOT, *$2)); TaQLNode::theirNodesCreated.push_back ($$); } | NOT arithexpr { $$ = new TaQLNode( new TaQLUnaryNodeRep (TaQLUnaryNodeRep::U_NOT, *$2)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr POWER arithexpr { $$ = new TaQLNode( new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_POWER, *$1, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } ; /* A (sub)expression can be followed by a unit Note that in the second rule using inxexpr instead of simexpr has the effect that units cannot be chained (such as 3 km m) */ simexpr: inxexpr { $$ = $1; } | simexpr unit { $$ = new TaQLNode( new TaQLUnitNodeRep ($2->getString(), *$1)); TaQLNode::theirNodesCreated.push_back ($$); } ; /* An array can be indexed with subscripts or mask Note that in the second rule using simbexpr instead of inxexpr has the effect that brackets operators cannot be chained (such as DATA[FLAG][,0]) */ inxexpr: simbexpr { $$ = $1; } | inxexpr LBRACKET subscripts RBRACKET { $$ = new TaQLNode( new TaQLBinaryNodeRep (TaQLBinaryNodeRep::B_INDEX, *$1, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } ; /* A subexpression, function, column, literal or set. Note that the COUNT function has a separate line because COUNT is also a keyword. COUNTALL has a somewhat special syntax which is recognized in flex. */ simbexpr: LPAREN orexpr RPAREN /* subexpression in parentheses */ { $$ = $2; } | namefld LPAREN elemlist RPAREN { /* function */ $$ = new TaQLNode( new TaQLFuncNodeRep ($1->getString(), *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | COUNT LPAREN elemlist RPAREN { /* COUNT function */ $$ = new TaQLNode( new TaQLFuncNodeRep ("COUNT", *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | COUNTALL { $$ = new TaQLNode( /* COUNT(*) function */ new TaQLFuncNodeRep ("COUNTALL")); TaQLNode::theirNodesCreated.push_back ($$); } | namefld { $$ = new TaQLNode( /* column name */ new TaQLKeyColNodeRep ($1->getString())); TaQLNode::theirNodesCreated.push_back ($$); } | literal { $$ = $1; } | set { $$ = $1; } ; /* Column name or keyword name (possibly with alias) */ namefld: NAME { /* simple name */ $$ = $1; } | FLDNAME { /* name with . or :: */ $$ = $1; } ; /* Simple unit or compound unit enclosed in quotes */ unit: namefld { $$ = $1; } | STRINGLITERAL { /* compound unit (with special characters) */ $$ = $1; } ; /* A numeric or boolean literal or a string literal (in quotes) */ literal: LITERAL { $$ = $1; } | STRINGLITERAL { $$ = $1; } ; /* A set is is a series of values enclosed in brackets or parentheses. It can also be the result of a subquery. */ set: LBRACKET elems RBRACKET { $2->setIsSetOrArray(); $$ = $2; } | LPAREN elems RPAREN { $2->setIsSetOrArray(); $$ = $2; } | subquery { $$ = $1; } ; /* A possibly empty list of values */ elemlist: elems { $$ = $1; $$->setPPFix("", ""); } | { $$ = new TaQLMultiNode(False); /* no elements */ TaQLNode::theirNodesCreated.push_back ($$); } ; /* A non-empty list of values */ elems: elems COMMA elem { $$ = $1; $$->add (*$3); } | elem { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->setPPFix ("[", "]"); $$->add (*$1); } ; /* A value in a set can be an expression or a range specification */ elem: orexpr { $$ = $1; } | range { $$ = $1; } ; /* A range in a set requires an extra MultiNode */ singlerange: range { $$ = new TaQLMultiNode(True); TaQLNode::theirNodesCreated.push_back ($$); $$->add (*$1); } ; /* A range can be a discrete strt:end:step range or a continuous interval. The latter can be specified in two ways: using angle brackets and braces or using the =:= notation (where = can also be <). Angle brackets indicate an open side, braces a closed side. It is possible to leave out the start or end value (meaning - or +infinity). */ range: colonrangeinterval { $$ = $1; } | BETWEEN arithexpr AND arithexpr { $$ = new TaQLNode( new TaQLRangeNodeRep (False, *$2, *$4, False)); TaQLNode::theirNodesCreated.push_back ($$); } | AROUND arithexpr IN arithexpr { $$ = new TaQLNode( new TaQLRangeNodeRep (*$2, *$4)); TaQLNode::theirNodesCreated.push_back ($$); } | LT arithexpr COMMA arithexpr GT { $$ = new TaQLNode( new TaQLRangeNodeRep (False, *$2, *$4, False)); TaQLNode::theirNodesCreated.push_back ($$); } | LT arithexpr COMMA arithexpr RBRACE { $$ = new TaQLNode( new TaQLRangeNodeRep (False, *$2, *$4, True)); TaQLNode::theirNodesCreated.push_back ($$); } | LBRACE arithexpr COMMA arithexpr GT { $$ = new TaQLNode( new TaQLRangeNodeRep (True, *$2, *$4, False)); TaQLNode::theirNodesCreated.push_back ($$); } | LBRACE arithexpr COMMA arithexpr RBRACE { $$ = new TaQLNode( new TaQLRangeNodeRep (True, *$2, *$4, True)); TaQLNode::theirNodesCreated.push_back ($$); } | LBRACE COMMA arithexpr GT { $$ = new TaQLNode( new TaQLRangeNodeRep (*$3, False)); TaQLNode::theirNodesCreated.push_back ($$); } | LT COMMA arithexpr GT { $$ = new TaQLNode( new TaQLRangeNodeRep (*$3, False)); TaQLNode::theirNodesCreated.push_back ($$); } | LBRACE COMMA arithexpr RBRACE { $$ = new TaQLNode( new TaQLRangeNodeRep (*$3, True)); TaQLNode::theirNodesCreated.push_back ($$); } | LT COMMA arithexpr RBRACE { $$ = new TaQLNode( new TaQLRangeNodeRep (*$3, True)); TaQLNode::theirNodesCreated.push_back ($$); } | LT arithexpr COMMA RBRACE { $$ = new TaQLNode( new TaQLRangeNodeRep (False, *$2)); TaQLNode::theirNodesCreated.push_back ($$); } | LT arithexpr COMMA GT { $$ = new TaQLNode( new TaQLRangeNodeRep (False, *$2)); TaQLNode::theirNodesCreated.push_back ($$); } | LBRACE arithexpr COMMA RBRACE { $$ = new TaQLNode( new TaQLRangeNodeRep (True, *$2)); TaQLNode::theirNodesCreated.push_back ($$); } | LBRACE arithexpr COMMA GT { $$ = new TaQLNode( new TaQLRangeNodeRep (True, *$2)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr MIDWIDTH arithexpr { $$ = new TaQLNode( new TaQLRangeNodeRep (*$1, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr OPENOPEN arithexpr { $$ = new TaQLNode( new TaQLRangeNodeRep (False, *$1, *$3, False)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr OPENCLOSED arithexpr { $$ = new TaQLNode( new TaQLRangeNodeRep (False, *$1, *$3, True)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr CLOSEDOPEN arithexpr { $$ = new TaQLNode( new TaQLRangeNodeRep (True, *$1, *$3, False)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr CLOSEDCLOSED arithexpr { $$ = new TaQLNode( new TaQLRangeNodeRep (True, *$1, *$3, True)); TaQLNode::theirNodesCreated.push_back ($$); } | EMPTYOPEN arithexpr { $$ = new TaQLNode( new TaQLRangeNodeRep (*$2, False)); TaQLNode::theirNodesCreated.push_back ($$); } | EMPTYCLOSED arithexpr { $$ = new TaQLNode( new TaQLRangeNodeRep (*$2, True)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr OPENEMPTY { $$ = new TaQLNode( new TaQLRangeNodeRep (False, *$1)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr CLOSEDEMPTY { $$ = new TaQLNode( new TaQLRangeNodeRep (True, *$1)); TaQLNode::theirNodesCreated.push_back ($$); } ; /* Array subscripts indicate a single value or a range of values per axis. An array subscript can be left out (indicating entire axis), but the comma has to be present. A single subscript can be a mask for a masked array. */ subscripts: subscripts COMMA subsrange { $$ = $1; $$->add (*$3); } | subscripts COMMA { $$ = $1; $$->add (new TaQLIndexNodeRep(0, 0, 0)); } | COMMA { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->setPPFix ("[", "]"); $$->add (new TaQLIndexNodeRep(0, 0, 0)); $$->add (new TaQLIndexNodeRep(0, 0, 0)); } | COMMA subsrange { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->setPPFix ("[", "]"); $$->add (new TaQLIndexNodeRep(0, 0, 0)); $$->add (*$2); } | subsingle { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->setPPFix ("[", "]"); $$->add (*$1); } ; /* A single subscript can be a single element in a vector, but also an array giving a boolean mask. Hence it accepts an orexpr instead of arithexpr. */ subsingle: orexpr { $$ = new TaQLNode( new TaQLIndexNodeRep (*$1, 0, 0)); TaQLNode::theirNodesCreated.push_back ($$); } | colonrangeindex { $$ = $1; } ; /* An array axis subscript is a single value or a range */ subsrange: arithexpr { $$ = new TaQLNode( new TaQLIndexNodeRep (*$1, 0, 0)); TaQLNode::theirNodesCreated.push_back ($$); } | colonrangeindex { $$ = $1; } ; /* A range interval is a start:end:step specification where all parts are optional. An array index slice is similar, but requires a different representation of an unspecified end. Hence they share the range specifications except those with an unspefied end. */ colonrangeinterval: colonrange { $$ = $1; } | arithexpr COLON { $$ = new TaQLNode( new TaQLIndexNodeRep (*$1, 0, 0)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr COLON COLON { $$ = new TaQLNode( new TaQLIndexNodeRep (*$1, 0, 0)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr COLON COLON arithexpr { $$ = new TaQLNode( new TaQLIndexNodeRep (*$1, 0, *$4)); TaQLNode::theirNodesCreated.push_back ($$); } ; /* An array axis slice is the colonrange below extended with the possibility. of an unspecified end which is represented as Slicer::MimicSource. */ colonrangeindex: colonrange { $$ = $1; } | arithexpr COLON { $$ = new TaQLNode (new TaQLIndexNodeRep (*$1, TaQLConstNode(new TaQLConstNodeRep(Int64(Slicer::MimicSource))), 0)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr COLON COLON { $$ = new TaQLNode (new TaQLIndexNodeRep (*$1, TaQLConstNode(new TaQLConstNodeRep(Int64(Slicer::MimicSource))), 0)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr COLON COLON arithexpr { $$ = new TaQLNode (new TaQLIndexNodeRep (*$1, TaQLConstNode(new TaQLConstNodeRep(Int64(Slicer::MimicSource))), *$4)); TaQLNode::theirNodesCreated.push_back ($$); } ; /* Each part in a start:end:step range is optional as well as the last colon if end and/or step is not given. */ colonrange: arithexpr COLON arithexpr { $$ = new TaQLNode( new TaQLIndexNodeRep (*$1, *$3, 0)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr COLON arithexpr COLON { $$ = new TaQLNode( new TaQLIndexNodeRep (*$1, *$3, 0)); TaQLNode::theirNodesCreated.push_back ($$); } | arithexpr COLON arithexpr COLON arithexpr { $$ = new TaQLNode( new TaQLIndexNodeRep (*$1, *$3, *$5)); TaQLNode::theirNodesCreated.push_back ($$); } | COLON arithexpr { $$ = new TaQLNode( new TaQLIndexNodeRep (0, *$2, 0)); TaQLNode::theirNodesCreated.push_back ($$); } | COLON arithexpr COLON { $$ = new TaQLNode( new TaQLIndexNodeRep (0, *$2, 0)); TaQLNode::theirNodesCreated.push_back ($$); } | COLON arithexpr COLON arithexpr { $$ = new TaQLNode( new TaQLIndexNodeRep (0, *$2, *$4)); TaQLNode::theirNodesCreated.push_back ($$); } | COLON COLON arithexpr { $$ = new TaQLNode( new TaQLIndexNodeRep (0, 0, *$3)); TaQLNode::theirNodesCreated.push_back ($$); } ; /* A list of expressions to sort on (each with optional ASC or DESC) */ sortlist : sortlist COMMA sortexpr { $$ = $1; $$->add (*$3); } | sortexpr { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->add (*$1); } ; /* A sort expression can have ASCENDING or DESCENDING */ sortexpr : orexpr { $$ = new TaQLNode( new TaQLSortKeyNodeRep (TaQLSortKeyNodeRep::None, *$1)); TaQLNode::theirNodesCreated.push_back ($$); } | orexpr SORTASC { $$ = new TaQLNode( new TaQLSortKeyNodeRep (TaQLSortKeyNodeRep::Ascending, *$1)); TaQLNode::theirNodesCreated.push_back ($$); } | orexpr SORTDESC { $$ = new TaQLNode( new TaQLSortKeyNodeRep (TaQLSortKeyNodeRep::Descending, *$1)); TaQLNode::theirNodesCreated.push_back ($$); } ; /* A list of DataManager specifications */ dmlist: dmelem { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->add (*$1); } | dmlist COMMA dmelem { $$ = $1; $$->add (*$3); } ; /* A DataManager specification is a record (list of key=value) */ dmelem: LBRACKET recexpr RBRACKET { $$ = new TaQLNode( new TaQLRecFldNodeRep ("", *$2, "")); TaQLNode::theirNodesCreated.push_back ($$); } ; /* A list of general record field definitions (key=value) */ recexpr: recexpr COMMA recfield { $$->add (*$3); } | recfield { $$ = new TaQLMultiNode(False); TaQLNode::theirNodesCreated.push_back ($$); $$->setPPFix ("[", "]"); $$->add (*$1); } ; /* A general key=value can have a simple or (nested) record value */ recfield: srecfield { $$ = $1; } | rrecfield { $$ = $1; } ; /* A simple key=value (having an optional data type specification) */ srecfield: NAME EQASS srecval { $$ = new TaQLNode( new TaQLRecFldNodeRep ($1->getString(), *$3)); TaQLNode::theirNodesCreated.push_back ($$); delete $3; } ; /* A record value has an optional data type */ srecval: orexpr asdtype { $$ = new TaQLRecFldNodeRep ("", *$1, $2->getString()); } /* A record value being a record in itself */ rrecfield: NAME EQASS brackval { $$ = new TaQLNode( new TaQLRecFldNodeRep ($1->getString(), *$3)); TaQLNode::theirNodesCreated.push_back ($$); delete $3; } ; /* An optional data type */ asdtype: { /* no datatype */ $$ = new TaQLConstNode(new TaQLConstNodeRep(String())); TaQLNode::theirNodesCreated.push_back ($$); } | AS NAME { $$ = $2; } ; %% casacore-3.7.1/tables/TaQL/TableParse.cc000066400000000000000000000075321476623553700177370ustar00rootroot00000000000000//# TableParse.cc: Functions to parse and execute a TaQL command //# Copyright (C) 1994-2022 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Simplified forms of general tableCommand function. TaQLResult tableCommand (const String& str) { Vector cols; return tableCommand (str, cols); } TaQLResult tableCommand (const String& str, const Table& tempTable) { std::vector tmp(1); tmp[0] = &tempTable; return tableCommand (str, tmp); } TaQLResult tableCommand (const String& str, const std::vector& tempTables) { Vector cols; return tableCommand (str, tempTables, cols); } TaQLResult tableCommand (const String& str, Vector& cols) { std::vector tmp; return tableCommand (str, tmp, cols); } TaQLResult tableCommand (const String& str, Vector& cols, String& commandType) { std::vector tmp; return tableCommand (str, tmp, cols, commandType); } TaQLResult tableCommand (const String& str, const std::vector& tempTables, Vector& cols) { String commandType; return tableCommand (str, tempTables, cols, commandType); } //# Do the actual parsing of a command and execute it. TaQLResult tableCommand (const String& str, const std::vector& tempTables, Vector& cols, String& commandType) { commandType = "error"; // Do the first parse step. It returns a raw parse tree // (or throws an exception). Timer timer; TaQLNode tree = TaQLNode::parse(str); // Now process the raw tree and get the final ParseSelect object. try { TaQLNodeHandler treeHandler; TaQLNodeResult res = treeHandler.handleTree (tree, tempTables); const TaQLNodeHRValue& hrval = TaQLNodeHandler::getHR(res); commandType = hrval.getString(); TableExprNode expr = hrval.getExpr(); if (tree.style().doTiming()) { timer.show (" Total time "); } if (! expr.isNull()) { return TaQLResult(expr); // result of CALC command } //# Copy the possibly selected column names. cols.reference (hrval.getNames()); return TaQLResult(hrval.getTable()); } catch (std::exception& x) { throw TableParseError ("'" + str + "'\n " + x.what()); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/TableParse.h000066400000000000000000000055641476623553700176040ustar00rootroot00000000000000//# TableParse.h: Functions to parse and execute a TaQL command //# Copyright (C) 1994-2022 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABLEPARSE_H #define TABLES_TABLEPARSE_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class Table; class String; // // Parse and execute the given TaQL command. // It will open (and close) all tables needed. // It returns the resulting table. // The command type and the selected or updated // column names can be returned. // Zero or more temporary tables can be used in the command // using the $nnn syntax. // // TaQLResult tableCommand (const String& command); TaQLResult tableCommand (const String& command, const Table& tempTable); TaQLResult tableCommand (const String& command, const std::vector& tempTables); TaQLResult tableCommand (const String& command, Vector& columnNames); TaQLResult tableCommand (const String& command, Vector& columnNames, String& commandType); TaQLResult tableCommand (const String& command, const std::vector& tempTables, Vector& columnNames); TaQLResult tableCommand (const String& command, const std::vector& tempTables, Vector& columnNames, String& commandType); // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/TableParseFunc.cc000066400000000000000000000762101476623553700205520ustar00rootroot00000000000000//# TableParseFunc.cc: Class handling TaQL functions //# Copyright (C) 1994-2022 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableExprNode TableParseFunc::makeFuncNode (TableParseQuery* tpq, const String& fname, const TableExprNodeSet& arguments, const Vector& ignoreFuncs, const TableExprInfo& tabin, const TaQLStyle& style) { TableExprInfo tabInfo(tabin); String name = fname; // See if something like xx.func is given. // xx can be a shorthand or a user defined function library. Vector parts = stringToVector (name, '.'); TableParsePair tabPair; if (tpq && parts.size() == 2) { // See if xx is a shorthand. If so, use that table. tabPair = tpq->tableList().findTable (parts[0], False); if (! tabPair.table().isNull()) { tabInfo = tabPair.getTableInfo(); name = parts[1]; } } //# Determine the function type. TableExprFuncNode::FunctionType ftype = findFunc (name, arguments.size(), ignoreFuncs); if (ftype == TableExprFuncNode::NRFUNC) { // The function can be a user defined one (or unknown). return makeUDFNode (tpq, name, arguments, tabInfo, style); } if (tabInfo.isJoinTable()) { if (ftype == TableExprFuncNode::rowidFUNC) { if (arguments.size() > 0) { throw TableInvExpr("Function rowid cannot have arguments") ; } return new TaQLJoinRowid (tabInfo, tpq->joins()[tabPair.joinIndex()]); } else if (ftype == TableExprFuncNode::rownrFUNC) { throw TableInvExpr("Function rownr cannot be used on a join table"); } } try { // The axes of reduction functions such as SUMS can be given as a set or as // individual values. Turn it into an Array object. uInt axarg = 1; switch (ftype) { case TableExprFuncNode::arrfractilesFUNC: case TableExprFuncNode::runfractileFUNC: case TableExprFuncNode::boxfractileFUNC: axarg = 2; // fall through!! case TableExprFuncNode::arrsumsFUNC: case TableExprFuncNode::arrproductsFUNC: case TableExprFuncNode::arrsumsqrsFUNC: case TableExprFuncNode::arrminsFUNC: case TableExprFuncNode::arrmaxsFUNC: case TableExprFuncNode::arrmeansFUNC: case TableExprFuncNode::arrvariances0FUNC: case TableExprFuncNode::arrvariances1FUNC: case TableExprFuncNode::arrstddevs0FUNC: case TableExprFuncNode::arrstddevs1FUNC: case TableExprFuncNode::arravdevsFUNC: case TableExprFuncNode::arrrmssFUNC: case TableExprFuncNode::arrmediansFUNC: case TableExprFuncNode::arranysFUNC: case TableExprFuncNode::arrallsFUNC: case TableExprFuncNode::arrntruesFUNC: case TableExprFuncNode::arrnfalsesFUNC: case TableExprFuncNode::runsumFUNC: case TableExprFuncNode::runproductFUNC: case TableExprFuncNode::runsumsqrFUNC: case TableExprFuncNode::runminFUNC: case TableExprFuncNode::runmaxFUNC: case TableExprFuncNode::runmeanFUNC: case TableExprFuncNode::runvariance0FUNC: case TableExprFuncNode::runvariance1FUNC: case TableExprFuncNode::runstddev0FUNC: case TableExprFuncNode::runstddev1FUNC: case TableExprFuncNode::runavdevFUNC: case TableExprFuncNode::runrmsFUNC: case TableExprFuncNode::runmedianFUNC: case TableExprFuncNode::runanyFUNC: case TableExprFuncNode::runallFUNC: case TableExprFuncNode::runntrueFUNC: case TableExprFuncNode::runnfalseFUNC: case TableExprFuncNode::boxsumFUNC: case TableExprFuncNode::boxproductFUNC: case TableExprFuncNode::boxsumsqrFUNC: case TableExprFuncNode::boxminFUNC: case TableExprFuncNode::boxmaxFUNC: case TableExprFuncNode::boxmeanFUNC: case TableExprFuncNode::boxvariance0FUNC: case TableExprFuncNode::boxvariance1FUNC: case TableExprFuncNode::boxstddev0FUNC: case TableExprFuncNode::boxstddev1FUNC: case TableExprFuncNode::boxavdevFUNC: case TableExprFuncNode::boxrmsFUNC: case TableExprFuncNode::boxmedianFUNC: case TableExprFuncNode::boxanyFUNC: case TableExprFuncNode::boxallFUNC: case TableExprFuncNode::boxntrueFUNC: case TableExprFuncNode::boxnfalseFUNC: case TableExprFuncNode::arrayFUNC: case TableExprFuncNode::transposeFUNC: case TableExprFuncNode::areverseFUNC: case TableExprFuncNode::diagonalFUNC: if (arguments.size() >= axarg) { TableExprNodeSet parms; // Add first argument(s) to the parms. for (uInt i=0; i()))); parms.add (arg); } } else if (arguments.size() == axarg+1 && arguments[axarg]->isSingle()) { // A single set given; see if it is an array. const TENSEBShPtr& arg = arguments[axarg]; if (arg->start()->valueType() == TableExprNodeRep::VTArray) { parms.add (arg); axesIsArray = True; } } if (!axesIsArray) { // Combine all axes in a single set and add to parms. TableExprNodeSet axes; for (uInt i=axarg; istart(); if (!rep || !arg->isSingle() || rep->valueType() != TableExprNodeRep::VTScalar || (rep->dataType() != TableExprNodeRep::NTInt && rep->dataType() != TableExprNodeRep::NTDouble)) { throw TableInvExpr ("Axes/shape arguments " + String::toString(i+1) + " are not one or more scalars" " or a single bounded range"); } axes.add (arg); } parms.add (TableExprNodeSetElem(axes.setOrArray())); } return TableExprNode::newFunctionNode (ftype, parms, tabInfo, style); } break; case TableExprFuncNode::conesFUNC: case TableExprFuncNode::anyconeFUNC: case TableExprFuncNode::findconeFUNC: case TableExprFuncNode::cones3FUNC: case TableExprFuncNode::anycone3FUNC: case TableExprFuncNode::findcone3FUNC: return TableExprNode::newConeNode (ftype, arguments, style.origin()); default: break; } return TableExprNode::newFunctionNode (ftype, arguments, tabInfo, style); } catch (const std::exception& x) { String err (x.what()); if (err.size() > 28 && err.before(28) == "Error in select expression: ") { err = err.from(28); } throw TableInvExpr ("Erroneous use of function " + name + " - " + err); } } TableExprNode TableParseFunc::makeUDFNode (TableParseQuery* sel, const String& name, const TableExprNodeSet& arguments, const TableExprInfo& tabInfo, const TaQLStyle& style) { Vector parts = stringToVector (name, '.'); if (parts.size() == 1) { // No ., thus no UDF but a builtin function. throw TableInvExpr ("TaQL function " + name + " is unknown; " "use 'show func' to see all functions"); } TableExprNode udf; if (sel) { if (parts.size() > 2) { // At least 3 parts; see if the first part is a table shorthand. TableParsePair tabPair = sel->tableList().findTable (parts[0], False); if (! tabPair.table().isNull()) { udf = TableExprNode::newUDFNode (name.substr(parts[0].size() + 1), arguments, tabPair.getTableInfo(), style); } } } // If not created, use the full name and given (i.e. first) table. if (udf.isNull()) { udf = TableExprNode::newUDFNode (name, arguments, tabInfo, style); } // A UDF might create table column nodes, so add it to applySelNodes_p. if (sel) { sel->addApplySelNode (udf); } return udf; } TableExprFuncNode::FunctionType TableParseFunc::findFunc (const String& name, uInt narguments, const Vector& ignoreFuncs) { //# Determine the function type. //# Use the function name in lower case. //# Error if functype in ignoreFuncs or if ignoreFuncs is not empty and //# the function is an aggregate one. TableExprFuncNode::FunctionType ftype = TableExprFuncNode::piFUNC; String funcName (name); funcName.downcase(); if (funcName == "pi") { ftype = TableExprFuncNode::piFUNC; } else if (funcName == "e") { ftype = TableExprFuncNode::eFUNC; } else if (funcName == "c") { ftype = TableExprFuncNode::cFUNC; } else if (funcName == "near") { ftype = TableExprFuncNode::near2FUNC; if (narguments == 3) { ftype = TableExprFuncNode::near3FUNC; } } else if (funcName == "nearabs") { ftype = TableExprFuncNode::nearabs2FUNC; if (narguments == 3) { ftype = TableExprFuncNode::nearabs3FUNC; } } else if (funcName == "sin") { ftype = TableExprFuncNode::sinFUNC; } else if (funcName == "sinh") { ftype = TableExprFuncNode::sinhFUNC; } else if (funcName == "cos") { ftype = TableExprFuncNode::cosFUNC; } else if (funcName == "cosh") { ftype = TableExprFuncNode::coshFUNC; } else if (funcName == "exp") { ftype = TableExprFuncNode::expFUNC; } else if (funcName == "log" || funcName == "ln") { ftype = TableExprFuncNode::logFUNC; } else if (funcName == "log10") { ftype = TableExprFuncNode::log10FUNC; } else if (funcName == "sqrt") { ftype = TableExprFuncNode::sqrtFUNC; } else if (funcName == "pow") { ftype = TableExprFuncNode::powFUNC; } else if (funcName == "conj") { ftype = TableExprFuncNode::conjFUNC; } else if (funcName == "square" || funcName == "sqr") { ftype = TableExprFuncNode::squareFUNC; } else if (funcName == "cube") { ftype = TableExprFuncNode::cubeFUNC; } else if (funcName == "min") { ftype = TableExprFuncNode::minFUNC; if (narguments == 1) { ftype = TableExprFuncNode::arrminFUNC; } } else if (funcName == "max") { ftype = TableExprFuncNode::maxFUNC; if (narguments == 1) { ftype = TableExprFuncNode::arrmaxFUNC; } } else if (funcName == "norm") { ftype = TableExprFuncNode::normFUNC; } else if (funcName == "abs" || funcName == "amplitude" || funcName == "ampl") { ftype = TableExprFuncNode::absFUNC; } else if (funcName == "arg" || funcName == "phase") { ftype = TableExprFuncNode::argFUNC; } else if (funcName == "real") { ftype = TableExprFuncNode::realFUNC; } else if (funcName == "imag") { ftype = TableExprFuncNode::imagFUNC; } else if (funcName == "int" || funcName == "integer") { ftype = TableExprFuncNode::intFUNC; } else if (funcName == "asin") { ftype = TableExprFuncNode::asinFUNC; } else if (funcName == "acos") { ftype = TableExprFuncNode::acosFUNC; } else if (funcName == "atan") { ftype = TableExprFuncNode::atanFUNC; } else if (funcName == "atan2") { ftype = TableExprFuncNode::atan2FUNC; } else if (funcName == "tan") { ftype = TableExprFuncNode::tanFUNC; } else if (funcName == "tanh") { ftype = TableExprFuncNode::tanhFUNC; } else if (funcName == "sign") { ftype = TableExprFuncNode::signFUNC; } else if (funcName == "round") { ftype = TableExprFuncNode::roundFUNC; } else if (funcName == "floor") { ftype = TableExprFuncNode::floorFUNC; } else if (funcName == "ceil") { ftype = TableExprFuncNode::ceilFUNC; } else if (funcName == "fmod") { ftype = TableExprFuncNode::fmodFUNC; } else if (funcName == "complex" || funcName == "formcomplex") { ftype = TableExprFuncNode::complexFUNC; } else if (funcName == "sum") { ftype = TableExprFuncNode::arrsumFUNC; } else if (funcName == "sums") { ftype = TableExprFuncNode::arrsumsFUNC; } else if (funcName == "runningsum") { ftype = TableExprFuncNode::runsumFUNC; } else if (funcName == "boxedsum") { ftype = TableExprFuncNode::boxsumFUNC; } else if (funcName == "product") { ftype = TableExprFuncNode::arrproductFUNC; } else if (funcName == "products") { ftype = TableExprFuncNode::arrproductsFUNC; } else if (funcName == "runningproduct") { ftype = TableExprFuncNode::runproductFUNC; } else if (funcName == "boxedproduct") { ftype = TableExprFuncNode::boxproductFUNC; } else if (funcName == "sumsqr" || funcName == "sumsquare") { ftype = TableExprFuncNode::arrsumsqrFUNC; } else if (funcName == "sumsqrs" || funcName == "sumsquares") { ftype = TableExprFuncNode::arrsumsqrsFUNC; } else if (funcName == "runningsumsqr" || funcName == "runningsumsquare") { ftype = TableExprFuncNode::runsumsqrFUNC; } else if (funcName == "boxedsumsqr" || funcName == "boxedsumsquare") { ftype = TableExprFuncNode::boxsumsqrFUNC; } else if (funcName == "mins") { ftype = TableExprFuncNode::arrminsFUNC; } else if (funcName == "runningmin") { ftype = TableExprFuncNode::runminFUNC; } else if (funcName == "boxedmin") { ftype = TableExprFuncNode::boxminFUNC; } else if (funcName == "maxs") { ftype = TableExprFuncNode::arrmaxsFUNC; } else if (funcName == "runningmax") { ftype = TableExprFuncNode::runmaxFUNC; } else if (funcName == "boxedmax") { ftype = TableExprFuncNode::boxmaxFUNC; } else if (funcName == "mean" || funcName == "avg") { ftype = TableExprFuncNode::arrmeanFUNC; } else if (funcName == "means" || funcName == "avgs") { ftype = TableExprFuncNode::arrmeansFUNC; } else if (funcName == "runningmean" || funcName == "runningavg") { ftype = TableExprFuncNode::runmeanFUNC; } else if (funcName == "boxedmean" || funcName == "boxedavg") { ftype = TableExprFuncNode::boxmeanFUNC; } else if (funcName == "variance") { ftype = TableExprFuncNode::arrvariance0FUNC; } else if (funcName == "variances") { ftype = TableExprFuncNode::arrvariances0FUNC; } else if (funcName == "runningvariance") { ftype = TableExprFuncNode::runvariance0FUNC; } else if (funcName == "boxedvariance") { ftype = TableExprFuncNode::boxvariance0FUNC; } else if (funcName == "samplevariance") { ftype = TableExprFuncNode::arrvariance1FUNC; } else if (funcName == "samplevariances") { ftype = TableExprFuncNode::arrvariances1FUNC; } else if (funcName == "runningsamplevariance") { ftype = TableExprFuncNode::runvariance1FUNC; } else if (funcName == "boxedsamplevariance") { ftype = TableExprFuncNode::boxvariance1FUNC; } else if (funcName == "stddev") { ftype = TableExprFuncNode::arrstddev0FUNC; } else if (funcName == "stddevs") { ftype = TableExprFuncNode::arrstddevs0FUNC; } else if (funcName == "runningstddev") { ftype = TableExprFuncNode::runstddev0FUNC; } else if (funcName == "boxedstddev") { ftype = TableExprFuncNode::boxstddev0FUNC; } else if (funcName == "samplestddev") { ftype = TableExprFuncNode::arrstddev1FUNC; } else if (funcName == "samplestddevs") { ftype = TableExprFuncNode::arrstddevs1FUNC; } else if (funcName == "runningsamplestddev") { ftype = TableExprFuncNode::runstddev1FUNC; } else if (funcName == "boxedsamplestddev") { ftype = TableExprFuncNode::boxstddev1FUNC; } else if (funcName == "avdev") { ftype = TableExprFuncNode::arravdevFUNC; } else if (funcName == "avdevs") { ftype = TableExprFuncNode::arravdevsFUNC; } else if (funcName == "runningavdev") { ftype = TableExprFuncNode::runavdevFUNC; } else if (funcName == "boxedavdev") { ftype = TableExprFuncNode::boxavdevFUNC; } else if (funcName == "rms") { ftype = TableExprFuncNode::arrrmsFUNC; } else if (funcName == "rmss") { ftype = TableExprFuncNode::arrrmssFUNC; } else if (funcName == "runningrms") { ftype = TableExprFuncNode::runrmsFUNC; } else if (funcName == "boxedrms") { ftype = TableExprFuncNode::boxrmsFUNC; } else if (funcName == "median") { ftype = TableExprFuncNode::arrmedianFUNC; } else if (funcName == "medians") { ftype = TableExprFuncNode::arrmediansFUNC; } else if (funcName == "runningmedian") { ftype = TableExprFuncNode::runmedianFUNC; } else if (funcName == "boxedmedian") { ftype = TableExprFuncNode::boxmedianFUNC; } else if (funcName == "fractile") { ftype = TableExprFuncNode::arrfractileFUNC; } else if (funcName == "fractiles") { ftype = TableExprFuncNode::arrfractilesFUNC; } else if (funcName == "runningfractile") { ftype = TableExprFuncNode::runfractileFUNC; } else if (funcName == "boxedfractile") { ftype = TableExprFuncNode::boxfractileFUNC; } else if (funcName == "any") { ftype = TableExprFuncNode::arranyFUNC; } else if (funcName == "anys") { ftype = TableExprFuncNode::arranysFUNC; } else if (funcName == "runningany") { ftype = TableExprFuncNode::runanyFUNC; } else if (funcName == "boxedany") { ftype = TableExprFuncNode::boxanyFUNC; } else if (funcName == "all") { ftype = TableExprFuncNode::arrallFUNC; } else if (funcName == "alls") { ftype = TableExprFuncNode::arrallsFUNC; } else if (funcName == "runningall") { ftype = TableExprFuncNode::runallFUNC; } else if (funcName == "boxedall") { ftype = TableExprFuncNode::boxallFUNC; } else if (funcName == "ntrue") { ftype = TableExprFuncNode::arrntrueFUNC; } else if (funcName == "ntrues") { ftype = TableExprFuncNode::arrntruesFUNC; } else if (funcName == "runningntrue") { ftype = TableExprFuncNode::runntrueFUNC; } else if (funcName == "boxedntrue") { ftype = TableExprFuncNode::boxntrueFUNC; } else if (funcName == "nfalse") { ftype = TableExprFuncNode::arrnfalseFUNC; } else if (funcName == "nfalses") { ftype = TableExprFuncNode::arrnfalsesFUNC; } else if (funcName == "runningnfalse") { ftype = TableExprFuncNode::runnfalseFUNC; } else if (funcName == "boxednfalse") { ftype = TableExprFuncNode::boxnfalseFUNC; } else if (funcName == "array") { ftype = TableExprFuncNode::arrayFUNC; } else if (funcName == "transpose") { ftype = TableExprFuncNode::transposeFUNC; } else if (funcName == "reversearray" || funcName == "areverse") { ftype = TableExprFuncNode::areverseFUNC; } else if (funcName == "diagonal" || funcName == "diagonals") { ftype = TableExprFuncNode::diagonalFUNC; } else if (funcName == "resize") { ftype = TableExprFuncNode::resizeFUNC; } else if (funcName == "isnan") { ftype = TableExprFuncNode::isnanFUNC; } else if (funcName == "isinf") { ftype = TableExprFuncNode::isinfFUNC; } else if (funcName == "isfinite") { ftype = TableExprFuncNode::isfiniteFUNC; } else if (funcName == "isdefined") { ftype = TableExprFuncNode::isdefFUNC; } else if (funcName == "isnull") { ftype = TableExprFuncNode::isnullFUNC; } else if (funcName == "iscolumn") { ftype = TableExprFuncNode::iscolFUNC; } else if (funcName == "iskeyword") { ftype = TableExprFuncNode::iskeyFUNC; } else if (funcName == "ndim") { ftype = TableExprFuncNode::ndimFUNC; } else if (funcName == "nelements" || funcName == "count") { ftype = TableExprFuncNode::nelemFUNC; } else if (funcName == "shape") { ftype = TableExprFuncNode::shapeFUNC; } else if (funcName == "strlength" || funcName == "len") { ftype = TableExprFuncNode::strlengthFUNC; } else if (funcName == "upcase" || funcName == "upper" || funcName == "toupper" || funcName == "to_upper") { ftype = TableExprFuncNode::upcaseFUNC; } else if (funcName == "downcase" || funcName == "lower" || funcName == "tolower" || funcName == "to_lower") { ftype = TableExprFuncNode::downcaseFUNC; } else if (funcName == "capitalize") { ftype = TableExprFuncNode::capitalizeFUNC; } else if (funcName == "reversestring" || funcName == "sreverse") { ftype = TableExprFuncNode::sreverseFUNC; } else if (funcName == "trim") { ftype = TableExprFuncNode::trimFUNC; } else if (funcName == "ltrim") { ftype = TableExprFuncNode::ltrimFUNC; } else if (funcName == "rtrim") { ftype = TableExprFuncNode::rtrimFUNC; } else if (funcName == "substr" || funcName == "substring") { ftype = TableExprFuncNode::substrFUNC; } else if (funcName == "replace") { ftype = TableExprFuncNode::replaceFUNC; } else if (funcName == "regex") { ftype = TableExprFuncNode::regexFUNC; } else if (funcName == "pattern") { ftype = TableExprFuncNode::patternFUNC; } else if (funcName == "sqlpattern") { ftype = TableExprFuncNode::sqlpatternFUNC; } else if (funcName == "datetime") { ftype = TableExprFuncNode::datetimeFUNC; } else if (funcName == "mjdtodate") { ftype = TableExprFuncNode::mjdtodateFUNC; } else if (funcName == "mjd") { ftype = TableExprFuncNode::mjdFUNC; } else if (funcName == "date") { ftype = TableExprFuncNode::dateFUNC; } else if (funcName == "time") { ftype = TableExprFuncNode::timeFUNC; } else if (funcName == "year") { ftype = TableExprFuncNode::yearFUNC; } else if (funcName == "month") { ftype = TableExprFuncNode::monthFUNC; } else if (funcName == "day") { ftype = TableExprFuncNode::dayFUNC; } else if (funcName == "cmonth") { ftype = TableExprFuncNode::cmonthFUNC; } else if (funcName == "weekday" || funcName == "dow") { ftype = TableExprFuncNode::weekdayFUNC; } else if (funcName == "cweekday" || funcName == "cdow") { ftype = TableExprFuncNode::cdowFUNC; } else if (funcName == "week") { ftype = TableExprFuncNode::weekFUNC; } else if (funcName == "cdatetime" || funcName == "ctod") { ftype = TableExprFuncNode::ctodFUNC; } else if (funcName == "cdate") { ftype = TableExprFuncNode::cdateFUNC; } else if (funcName == "ctime") { ftype = TableExprFuncNode::ctimeFUNC; } else if (funcName == "string" || funcName == "str") { ftype = TableExprFuncNode::stringFUNC; } else if (funcName == "hms") { ftype = TableExprFuncNode::hmsFUNC; } else if (funcName == "dms") { ftype = TableExprFuncNode::dmsFUNC; } else if (funcName == "hdms") { ftype = TableExprFuncNode::hdmsFUNC; } else if (funcName == "rand") { ftype = TableExprFuncNode::randFUNC; } else if (funcName == "rownumber" || funcName == "rownr") { ftype = TableExprFuncNode::rownrFUNC; } else if (funcName == "rowid") { ftype = TableExprFuncNode::rowidFUNC; } else if (funcName == "iif") { ftype = TableExprFuncNode::iifFUNC; } else if (funcName == "angdist" || funcName == "angulardistance") { ftype = TableExprFuncNode::angdistFUNC; } else if (funcName == "angdistx" || funcName == "angulardistancex") { ftype = TableExprFuncNode::angdistxFUNC; } else if (funcName == "normangle") { ftype = TableExprFuncNode::normangleFUNC; } else if (funcName == "cones") { ftype = TableExprConeNode::conesFUNC; if (narguments == 3) { ftype = TableExprConeNode::cones3FUNC; } } else if (funcName == "anycone") { ftype = TableExprConeNode::anyconeFUNC; if (narguments == 3) { ftype = TableExprConeNode::anycone3FUNC; } } else if (funcName == "findcone") { ftype = TableExprConeNode::findconeFUNC; if (narguments == 3) { ftype = TableExprConeNode::findcone3FUNC; } } else if (funcName == "bool" || funcName == "boolean") { ftype = TableExprFuncNode::boolFUNC; } else if (funcName == "nullarray") { ftype = TableExprFuncNode::nullarrayFUNC; } else if (funcName == "marray") { ftype = TableExprFuncNode::marrayFUNC; } else if (funcName == "arraydata") { ftype = TableExprFuncNode::arrdataFUNC; } else if (funcName == "mask" || funcName == "arraymask") { ftype = TableExprFuncNode::arrmaskFUNC; } else if (funcName == "negatemask") { ftype = TableExprFuncNode::negatemaskFUNC; } else if (funcName == "replacemasked") { ftype = TableExprFuncNode::replmaskedFUNC; } else if (funcName == "replaceunmasked") { ftype = TableExprFuncNode::replunmaskedFUNC; } else if (funcName == "flatten" || funcName == "arrayflatten") { ftype = TableExprFuncNode::arrflatFUNC; } else if (funcName == "countall") { ftype = TableExprFuncNode::countallFUNC; } else if (funcName == "gcount") { ftype = TableExprFuncNode::gcountFUNC; } else if (funcName == "gfirst") { ftype = TableExprFuncNode::gfirstFUNC; } else if (funcName == "glast") { ftype = TableExprFuncNode::glastFUNC; } else if (funcName == "gmin") { ftype = TableExprFuncNode::gminFUNC; } else if (funcName == "gmins") { ftype = TableExprFuncNode::gminsFUNC; } else if (funcName == "gmax") { ftype = TableExprFuncNode::gmaxFUNC; } else if (funcName == "gmaxs") { ftype = TableExprFuncNode::gmaxsFUNC; } else if (funcName == "gsum") { ftype = TableExprFuncNode::gsumFUNC; } else if (funcName == "gsums") { ftype = TableExprFuncNode::gsumsFUNC; } else if (funcName == "gproduct") { ftype = TableExprFuncNode::gproductFUNC; } else if (funcName == "gproducts") { ftype = TableExprFuncNode::gproductsFUNC; } else if (funcName == "gsumsqr" || funcName == "gsumsquare") { ftype = TableExprFuncNode::gsumsqrFUNC; } else if (funcName == "gsumsqrs" || funcName == "gsumsquares") { ftype = TableExprFuncNode::gsumsqrsFUNC; } else if (funcName == "gmean" || funcName == "gavg") { ftype = TableExprFuncNode::gmeanFUNC; } else if (funcName == "gmeans" || funcName == "gavgs") { ftype = TableExprFuncNode::gmeansFUNC; } else if (funcName == "gvariance") { ftype = TableExprFuncNode::gvariance0FUNC; } else if (funcName == "gvariances") { ftype = TableExprFuncNode::gvariances0FUNC; } else if (funcName == "gsamplevariance") { ftype = TableExprFuncNode::gvariance1FUNC; } else if (funcName == "gsamplevariances") { ftype = TableExprFuncNode::gvariances1FUNC; } else if (funcName == "gstddev") { ftype = TableExprFuncNode::gstddev0FUNC; } else if (funcName == "gstddevs") { ftype = TableExprFuncNode::gstddevs0FUNC; } else if (funcName == "gsamplestddev") { ftype = TableExprFuncNode::gstddev1FUNC; } else if (funcName == "gsamplestddevs") { ftype = TableExprFuncNode::gstddevs1FUNC; } else if (funcName == "grms") { ftype = TableExprFuncNode::grmsFUNC; } else if (funcName == "grmss") { ftype = TableExprFuncNode::grmssFUNC; } else if (funcName == "gany") { ftype = TableExprFuncNode::ganyFUNC; } else if (funcName == "ganys") { ftype = TableExprFuncNode::ganysFUNC; } else if (funcName == "gall") { ftype = TableExprFuncNode::gallFUNC; } else if (funcName == "galls") { ftype = TableExprFuncNode::gallsFUNC; } else if (funcName == "gntrue") { ftype = TableExprFuncNode::gntrueFUNC; } else if (funcName == "gntrues") { ftype = TableExprFuncNode::gntruesFUNC; } else if (funcName == "gnfalse") { ftype = TableExprFuncNode::gnfalseFUNC; } else if (funcName == "gnfalses") { ftype = TableExprFuncNode::gnfalsesFUNC; } else if (funcName == "ghist" || funcName == "ghistogram") { ftype = TableExprFuncNode::ghistFUNC; } else if (funcName == "gaggr" || funcName == "gstack") { ftype = TableExprFuncNode::gaggrFUNC; } else if (funcName == "growid") { ftype = TableExprFuncNode::growidFUNC; } else if (funcName == "gmedian") { ftype = TableExprFuncNode::gmedianFUNC; } else if (funcName == "gfractile") { ftype = TableExprFuncNode::gfractileFUNC; } else { // unknown name can be a user-defined function. ftype = TableExprFuncNode::NRFUNC; } // Functions to be ignored are incorrect. Bool found; linearSearch (found, ignoreFuncs, Int(ftype), ignoreFuncs.size()); if (found || (!ignoreFuncs.empty() && ftype >= TableExprFuncNode::FirstAggrFunc)) { throw (TableInvExpr ("Function '" + funcName + "' can only be used in TaQL")); } return ftype; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/TableParseFunc.h000066400000000000000000000076511476623553700204170ustar00rootroot00000000000000//# TableParseFunc.h: Class handling TaQL functions //# Copyright (C) 1994-2022 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABLEPARSEFUNC_H #define TABLES_TABLEPARSEFUNC_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations class TableParseQuery; class TaQLStyle; // // Class containing static functions to handle TaQL functions. // // // // // // This class contains a few functions to recognize TaQL functions. // A function name can be preceded by "shorthand.udflib." telling the // table to operate on and the UDF shared library containing the function. // Note that most functions are table agnostic, but a few (such as rowid()) // are not. Also User Defined Functions (UDFs) can be table aware. // Both shorthand and udflib are optional. If a single part is given, it is // first tried as a shorthand. // // It looks up the function name and creates a TableExprFuncNode object for it. // If the function name is not standard, it is tried to create a TableExprUDFNode // object. Otherwise an exception is thrown. // class TableParseFunc { public: // Make a function node. // The name is split into shorthand, udflib and function as explained // in the synopsis. static TableExprNode makeFuncNode (TableParseQuery*, const String& name, const TableExprNodeSet& arguments, const Vector& ignoreFuncs, const TableExprInfo& tabInfo, const TaQLStyle&); // Try to make a UDF function node for the given function name and arguments. // The function name can contain "shorthand." and must contain "udflib.". static TableExprNode makeUDFNode (TableParseQuery*, const String& name, const TableExprNodeSet& arguments, const TableExprInfo& tabInfo, const TaQLStyle&); // Find the function code belonging to a function name. // Functions to be ignored can be given (as function type values). // If the function name is unknown, NRFUNC is returned. static TableExprFuncNode::FunctionType findFunc (const String& name, uInt narguments, const Vector& ignoreFuncs); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/TableParseGroupby.cc000066400000000000000000000260321476623553700213030ustar00rootroot00000000000000//# TableParseGroupby.cc: Class handling GROUPBY and aggregate functions //# Copyright (C) 1994-2022 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include using namespace std; namespace casacore { //# NAMESPACE CASACORE - BEGIN void TableParseGroupby::handleGroupby (const std::vector& nodes, Bool rollup) { itsGroupbyNodes = nodes; itsGroupbyRollup = rollup; if (rollup) { throw TableInvExpr ("ROLLUP is not supported yet in the GROUPBY"); } for (uInt i=0; i& columnNodes, Bool isSelect) { itsGroupAggrUsed = 0; // Make sure main (where) node does not have aggregate functions. // This has been checked before, but use defensive programming. std::vector aggr; // Get possible aggregate functions used in column nodes and HAVING. // Note that column nodes are also used by UPDATE and INSERT commands. for (uInt i=0; i(aggr[0]); // Note that the cast fails for a TableExprAggrNodeArray (as it should). if (node && node->funcType() == TableExprFuncNode::countallFUNC) { itsGroupAggrUsed += ONLY_COUNTALL; } } itsAggrNodes = aggr; } uInt TableParseGroupby::disableApplySelection() { // Column nodes used in aggregate functions should not adhere applySelection. uInt ndis = 0; for (uInt i=0; i colNodes = TableExprNodeUtil::getColumnNodes (itsAggrNodes[i]); for (uInt j=0; jdisableApplySelection(); ndis++; } } return ndis; } void TableParseGroupby::getAggrNodes (const TableExprNode& node, std::vector& aggrNodes) const { std::vector aggr = TableExprNodeUtil::getAggrNodes (node.getRep().get()); aggrNodes.insert (aggrNodes.end(), aggr.begin(), aggr.end()); } void TableParseGroupby::checkAggrFuncs (const TableExprNode& node) { if (! node.isNull()) { TableExprNodeUtil::checkAggrFuncs (node.getRep().get()); } } std::shared_ptr TableParseGroupby::execGroupAggr (Vector& rownrs) const { // If only 'select count(*)' was given, get the size of the WHERE, // thus the size of rownrs_p. if ((itsGroupAggrUsed & ONLY_COUNTALL) != 0 && (itsGroupAggrUsed & GROUPBY) == 0) { return countAll (rownrs); } return aggregate (rownrs); } Bool TableParseGroupby::execHaving (Vector& rownrs, const std::shared_ptr& groups) { if (itsHavingNode.isNull()) { return False; } // Find the rows matching the HAVING expression. Vector resRownrs(rownrs.size()); rownr_t nr = 0; TableExprIdAggr rowid(groups); for (rownr_t i=0; i TableParseGroupby::aggregate (Vector& rownrs) const { // Get the aggregate functions to be evaluated lazily. std::vector immediateNodes; std::vector lazyNodes; for (uInt i=0; imakeGroupAggrFunc(); if (itsAggrNodes[i]->isLazyAggregate()) { lazyNodes.push_back (itsAggrNodes[i]); } else { immediateNodes.push_back (itsAggrNodes[i]); } } uInt nimmediate = immediateNodes.size(); // For lazy nodes a vector of TableExprId-s needs to be filled per group. // So add a node collecting the ids. // Note that this node must be alive after the if, so define outside if. TableExprAggrNode expridNode(TableExprFuncNode::gexpridFUNC, TableExprNodeRep::NTInt, TableExprNodeRep::VTArray, TableExprNodeSet(), std::vector(), Block()); if (! lazyNodes.empty()) { immediateNodes.push_back (&expridNode); } std::vector> funcSets; // Use a faster way for a single groupby key. if (itsGroupbyNodes.size() == 1 && itsGroupbyNodes[0].dataType() == TpDouble) { funcSets = singleKey (immediateNodes, rownrs); } else if (itsGroupbyNodes.size() == 1 && itsGroupbyNodes[0].dataType() == TpInt) { funcSets = singleKey (immediateNodes, rownrs); } else { funcSets = multiKey (immediateNodes, rownrs); } // Let the function nodes finish their operation. // Form the rownr vector from the rows kept in the aggregate objects. // Similarly, form the TableExprId vector if there are lazy nodes. Vector resRownrs(funcSets.size()); std::vector>> ids; ids.reserve (funcSets.size()); rownr_t n=0; for (uInt i=0; i>& funcs = funcSets[i]->getFuncs(); for (uInt j=0; jfinish(); } resRownrs[n++] = funcSets[i]->getId().rownr(); if (! lazyNodes.empty()) { ids.push_back (funcSets[i]->getFuncs()[nimmediate]->getIds()); } } rownrs.reference (resRownrs); // Save the aggregation results in a result object. return std::make_shared(funcSets, ids); } std::shared_ptr TableParseGroupby::countAll (Vector& rownrs) const { // This function is a special case because it does not need to // step though the table. Only its size is of interest. Furthermore, // some other columns can also be listed which will be those of the // last row. // Make a set containing the count(*) aggregate function object. std::vector> funcSets (1, std::make_shared()); std::shared_ptr funcb = itsAggrNodes[0]->makeGroupAggrFunc(); TableExprGroupCountAll& func = dynamic_cast(*funcb); funcSets[0]->add (funcb); // The nr of rows is the result of count(*), so simply set it. func.setResult (rownrs.size()); // The resulting table has only 1 group; use the last row with it. if (! rownrs.empty()) { rownrs.reference (Vector(1, rownrs[rownrs.size()-1])); } // Save the aggregation results in a result object. return std::make_shared(funcSets); } std::vector> TableParseGroupby::multiKey (const std::vector& nodes, const Vector& rownrs) const { // Group the data according to the (maybe empty) groupby. // Step through the table in the normal order which may not be the // groupby order. // A map is used to keep track of the results where the int // is the index in a vector of a set of aggregate function objects. std::vector> funcSets; std::map keyFuncMap; // Create the set of groupby key objects. TableExprGroupKeySet keySet(itsGroupbyNodes); // Loop through all rows. // For each row generate the key to get the right entry. TableExprId rowid(0); for (rownr_t i=0; i::iterator iter=keyFuncMap.find (keySet); if (iter == keyFuncMap.end()) { keyFuncMap[keySet] = groupnr; funcSets.push_back (std::make_shared(nodes)); } else { groupnr = iter->second; } funcSets[groupnr]->apply (rowid); } return funcSets; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/TableParseGroupby.h000066400000000000000000000160741476623553700211520ustar00rootroot00000000000000//# TableParseGroupby.h: Class handling GROUPBY and aggregate functions //# Copyright (C) 1994-2022 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABLEPARSEGROUPBY_H #define TABLES_TABLEPARSEGROUPBY_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations class TableParseQuery; // // Class handling GROUPBY and aggregate functions // // // // // // This class is used by TableParseQuery to handle TaQL's GROUPBY and HAVING // clauses and to setup and evaluate aggregate functions. // It checks that the commands and functions are given in a valid way. //
        Note that some hooks are present for the ROLLUP keyword, but it is not // possible to use it yet. //
        class TableParseGroupby { public: enum GroupAggrType { GROUPBY=1, AGGR_FUNCS=2, ONLY_COUNTALL=4 }; // Keep the groupby expressions. // It checks if they are all scalar expressions and do not contain // aggregate functions.. void handleGroupby (const std::vector&, Bool rollup); // Keep the having expression. // It checks if the node results in a bool scalar value. void handleHaving (const TableExprNode&); // Find if groupby and/or aggregate functions are given. // The column nodes can only contain aggregate functions if SELECT is used. // Finally it checks that HAVING is only used if a column node contains // an aggregate function (it makes no sense otherwise). void findGroupAggr (const Block& columnNodes, Bool isSelect); // Is GROUPBY and/or aggregation used? Bool isUsed() const { return itsGroupAggrUsed != 0; } // Is only aggregation used? Bool isOnlyAggr() const { return itsGroupAggrUsed != 0 && (itsGroupAggrUsed & GROUPBY) == 0; } // Get the number of aggregation ndes. uInt size() const { return itsAggrNodes.size(); } // Disable applySelection for the column nodes of aggregate functions. uInt disableApplySelection(); // An exception is thrown if the node uses an aggregate function. static void checkAggrFuncs (const TableExprNode& node); // Execute the grouping and aggregation and return the results. // The rownrs are adapted to the resulting rownrs consisting of the // first row of each group. std::shared_ptr execGroupAggr (Vector& rownrs) const; // Execute the HAVING clause (if present). // Return False in no HAVING. Bool execHaving (Vector& rownrs, const std::shared_ptr& groups); private: // Do the grouping and aggregation and return the results. // It distinguishes the immediate and lazy aggregate functions. // The rownrs are adapted to the resulting rownrs consisting of the // first row of each group. std::shared_ptr aggregate (Vector& rownrs) const; // Do the grouping and aggregation and return the results. // It consists of a single COUNTALL operation. // The rownrs are adapted to the resulting rownrs consisting of the // first row of each group. std::shared_ptr countAll (Vector& rownrs) const; // Create the set of aggregate functions and groupby keys. std::vector> multiKey (const std::vector&, const Vector& rownrs) const; // Create the set of aggregate functions and groupby keys in case // a single groupby key is given. // This offers much faster map access then the general multipleKeys. template std::vector> singleKey (const std::vector& nodes, const Vector& rownrs) const { // We have to group the data according to the (possibly empty) groupby. // We step through the table in the normal order which may not be the // groupby order. // A map is used to keep track of the results where the int // is the index in a vector of a set of aggregate function objects. std::vector> funcSets; std::map keyFuncMap; T lastKey = std::numeric_limits::max(); int groupnr = -1; // Loop through all rows. // For each row generate the key to get the right entry. TableExprId rowid(0); T key; for (rownr_t i=0; i::iterator iter = keyFuncMap.find (key); if (iter == keyFuncMap.end()) { groupnr = funcSets.size(); keyFuncMap[key] = groupnr; funcSets.push_back (std::shared_ptr (new TableExprGroupFuncSet (nodes))); } else { groupnr = iter->second; } } rowid.setRownr (rownrs[i]); funcSets[groupnr]->apply (rowid); } return funcSets; } // Get pointers to the aggregate nodes in the node expression. void getAggrNodes (const TableExprNode& node, std::vector& aggrNodes) const; //# Data members. // The possible GROUPBY expressions. std::vector itsGroupbyNodes; Bool itsGroupbyRollup; //# use ROLLUP in GROUPBY? // The possible HAVING expression. TableExprNode itsHavingNode; // Pointers to the aggregate function nodes. std::vector itsAggrNodes; Int itsGroupAggrUsed; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/TableParseJoin.cc000066400000000000000000000243311476623553700205530ustar00rootroot00000000000000//# TableParseJoin.cc: Class handling the join clause //# Copyright (C) 2022 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableParseJoin::TableParseJoin (TableParseQuery* parent) : itsParent (parent), itsParentJoinIndex (-1), itsLastMainRow (-1), itsLastJoinRow (0) { // Get the FROM tables which are all FROM tables before this JOIN clause. for (const auto& tabp : itsParent->tableList().fromTables()) { itsFromTables.push_back (tabp.table()); } } Int64 TableParseJoin::findRow (const TableExprId& id) const { // In the initialization phase of TaQLJoin, the itsJoin pointer // is not set. In that case the given row id is already the original // rownr in the join table and should be returned as such. if (! itsJoin) { return id.rownr(); } if (id.rownr() != itsLastMainRow) { itsLastMainRow = id.rownr(); itsLastJoinRow = itsJoin->findRow(id); } return itsLastJoinRow; } void TableParseJoin::addTable (Int tabnr, const String& name, const Table& ftab, const String& shorthand, const std::vector& tempTables, const std::vector& stack) { // First add the table to the FROM tables in the parent which gives // the Table object. // Link it to the index of this TableParseJoin object in the parent. Table tab = itsParent->tableList().addTable (tabnr, name, ftab, shorthand, True, tempTables, stack, itsParent->joins().size() - 1); itsJoinTables.push_back (tab); } void TableParseJoin::handleCondition (const TableExprNode& expr) { // Check that no aggregate functions are used. if (! TableExprNodeUtil::getAggrNodes(expr.getRep().get()).empty()) { throw TableInvExpr("Aggregate functions cannot be used in a JOIN condition"); } // The join condition can contain multiple ANDs. // Split the condition recursively into its AND parts and handle them. std::vector parts; splitAnd (expr.getRep(), parts); handleConditionParts (parts); } void TableParseJoin::splitAnd (const TENShPtr& node, std::vector& parts) { // Split recursively on the AND operator. if (node->operType() == TableExprNodeRep::OtAND) { const TableExprNodeBinary& bnode = dynamic_cast(*node); splitAnd (bnode.getLeftChild(), parts); splitAnd (bnode.getRightChild(), parts); } else { // No more ANDs, so add the part to the vector. parts.push_back (node); } } void TableParseJoin::handleConditionParts (std::vector& parts) { std::vector
      • mainTables; std::vector
        joinTables; std::vector eqParts; std::vector inParts; std::vector eqMainParts; std::vector inMainParts; for (size_t i=0; ivalueType() != TableExprNodeRep::VTScalar) { throw TableInvExpr ("JOIN condition must be a scalar expression"); } // Check the comparison operator (must be == or IN). if (part->operType() != TableExprNodeRep::OtEQ && part->operType() != TableExprNodeRep::OtIN) { throw TableInvExpr ("JOIN condition must be == or IN"); } // Both sides must be variable expressions. const TableExprNodeBinary& bnode = dynamic_cast(*part); TENShPtr leftChild = bnode.getLeftChild(); TENShPtr rightChild = bnode.getRightChild(); if (leftChild->isConstant() || rightChild->isConstant()) { throw TableInvExpr ("Join expressions cannot have constant comparison operands"); } // Get the tables used in left and right expression. std::vector
        leftTables = TableExprNodeUtil::getNodeTables (leftChild.get(), False); std::vector
        rightTables = TableExprNodeUtil::getNodeTables (rightChild.get(), False); // One side must be main tables and the other join tables. // For IN main tables must be on the left. // For ==, swap left and right if no join tables on the right. if (part->operType() == TableExprNodeRep::OtEQ) { if (findMatchingTables (rightTables, itsJoinTables) == 0) { leftTables.swap (rightTables); // Always have join tables on the right leftChild = bnode.getRightChild(); rightChild = bnode.getLeftChild(); } } // Test if all tables used in the condition exist at the right place. uInt nmatchLeft = findMatchingTables (leftTables, itsFromTables); uInt nmatchRight = findMatchingTables (rightTables, itsJoinTables); if (nmatchLeft == 0) { throw TableInvExpr ("When using an IN condition in a join, " "the main tables must be on the left side"); } if (nmatchRight == 0) { throw TableInvExpr ("Join tables must be on the right side of a join condition"); } if (nmatchLeft != leftTables.size() || nmatchRight != rightTables.size()) { throw TableInvExpr ("Mix of from and join tables on one side of a join condition"); } // Add the unique main and join ables to the overall vectors. addUniqueTables (mainTables, leftTables); addUniqueTables (joinTables, rightTables); if (part->operType() == TableExprNodeRep::OtEQ) { eqParts.push_back (rightChild); eqMainParts.push_back (leftChild); } else { inParts.push_back (rightChild); inMainParts.push_back (leftChild); } } // Check if all main and join tables have the same size. rownr_t nrow = checkNrow (itsJoinTables); // See if one or more main tables are join tables at a previous join. // In that case this join is basically a nested join. // Note that this TableParseJoin object is already part of the vector, // but it is innocent to test it. const std::vector& joins = itsParent->joins(); for (Int i=joins.size()-1; i>=0; --i) { uInt nmatch = findMatchingTables (mainTables, joins[i].itsJoinTables); if (nmatch > 0) { // Keep the index of the parent (not its pointer). // Note that a pointer might be invalidated if the vector gets extended. itsParentJoinIndex = i; break; } } // Append the IN parts to the EQ parts, so the faster EQ lookups are done first. eqParts.insert (eqParts.end(), inParts.begin(), inParts.end()); eqMainParts.insert (eqMainParts.end(), inMainParts.begin(), inMainParts.end()); // Everything seems to be fine. // Now read the join data for each part. // Joins can only be done on Int, Double, String and DateTime (handled as Double). std::vector rows(nrow); for (size_t i=0; i nodes; tnode.getRep()->flattenTree (nodes); for (auto node : nodes) { TaQLJoinColumn* colNode = dynamic_cast(node); if (colNode) { colNode->clear(); } } } } void TableParseJoin::addUniqueTables (std::vector
        & tables, const std::vector
        & other) { for (const Table& tab : other) { Bool add = True; for (const Table& t2 : tables) { if (tab.isSameTable (t2)) { add = False; break; } } if (add) { tables.insert (tables.end(), tab); } } } uInt TableParseJoin::findMatchingTables (const std::vector
        & exprTables, const std::vector
        & tables) const { uInt nmatch = 0; for (const Table& exprTab : exprTables) { for (const Table& tab : tables) { if (tab.isSameTable (exprTab)) { nmatch++; break; } } } return nmatch; } rownr_t TableParseJoin::checkNrow (const std::vector
        & tables) const { rownr_t nrow = 0; if (! tables.empty()) { nrow = tables[0].nrow(); for (const Table& tab : tables) { if (tab.nrow() != nrow) { throw TableInvExpr("Join tables in a join must have the same nr of rows"); } } } return nrow; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/TableParseJoin.h000066400000000000000000000117131476623553700204150ustar00rootroot00000000000000//# TableParseJoin.h: Class handling the join clause //# Copyright (C) 2022 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABLEPARSEJOIN_H #define TABLES_TABLEPARSEJOIN_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations. class TableParse; class TableParseQuery; class TableExprNode; class Table; // // Class handling a join clause in a TaQL command // // // // // // TableParseQuery holds a vector of TableParseJoin objects, one for each // join clause in a TaQL command. //
        TableParseJoin holds the join condition expression and the from and // join tables involved in the expression. The from tables are all tables in // the TaQL command prior to this join clause. The join tables are the tables // mentioned in the join clause. // TableParseJoin also tells if the join is nested in another join by keeping // the index of the parent TableParseJoin object in the vector in TableParseQuery. // // The ON condition expression is split in its AND parts; basically each part // involves a column comparison. Each part must contain an = for an exact match // or IN comparison for matching an interval. // A tree, consisting of TaQLJoinBase objects, is built to execute the condition. // It finds the matching row in the join table given a row in the main table. // Each level in the tree is an AND part in the condition. //
        class TableParseJoin { public: explicit TableParseJoin (TableParseQuery*); // Add a join table nr, name, or object to the container. void addTable (Int tabnr, const String& name, const Table& table, const String& shorthand, const std::vector& tempTables, const std::vector& stack); // Handle the ON condition of a join. void handleCondition (const TableExprNode& expr); // Find the row in the join table for the given main table row. //# In the initialization phase of TaQLJoin, the itsJoin pointer //# is not set. In that case the given row id is already the original //# rownr in the join table and should be returned as such. Int64 findRow (const TableExprId& id) const; private: // Split the ON condition recursively into its AND parts. void splitAnd (const TENShPtr& node, std::vector& parts); // Handle all AND parts of the join condition. void handleConditionParts (std::vector& parts); // Tell how many tables in the exprTables vector are the same as those // in the tables vector. uInt findMatchingTables (const std::vector
        & exprTables, const std::vector
        & tables) const; // Check if all join tables in the vector have the same number of rows. rownr_t checkNrow (const std::vector
        &) const; // Add the tables in the other vector to the tables vector. // Only do that for tables not occurring in the tables vector yet. void addUniqueTables (std::vector
        & tables, const std::vector
        & other); //# Data members. TableParseQuery* itsParent; std::vector
        itsFromTables; std::vector
        itsJoinTables; //# Index in TableParseQuery's vector of joins; <0 is no parent join. Int itsParentJoinIndex; std::shared_ptr itsJoin; mutable Int64 itsLastMainRow; // Last main table row looked for mutable Int64 itsLastJoinRow; // Join table row matching itsLastMainRow }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/TableParseProject.cc000066400000000000000000001064401476623553700212640ustar00rootroot00000000000000//# TableParseProject.cc: Class holding the info of a TaQL projection command //# Copyright (C) 1994-2022 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableParseProject::TableParseProject (const TableParseTableList& tableList) : tableList_p (tableList), tableDesc_p (new TableDesc()), nrSelExprUsed_p (0) {} //# Add a column name to the block of column names. //# Only take the part beyond the period. //# Extend the block each time. Since there are only a few column names, //# this will not be too expensive. void TableParseProject::handleColumn (Int stringType, const String& name, const TableExprNode& expr, const String& newName, const String& newNameMask, const String& newDtype, TableParseQuery& tpq) { if (expr.isNull() && stringType >= 0) { // A wildcarded column name is given. handleWildColumn (stringType, name); } else { // A single column is given. Int nrcol = columnNames_p.size(); columnNames_p.resize (nrcol+1); columnNameMasks_p.resize (nrcol+1); columnExpr_p.resize (nrcol+1); columnOldNames_p.resize (nrcol+1); columnDtypes_p.resize (nrcol+1); columnKeywords_p.resize (nrcol+1); if (expr.isNull()) { // A true column name is given. String oldName; String str = name; Int inx = str.index('.'); if (inx < 0) { oldName = str; } else { oldName = str.after(inx); } // Make an expression of the column or keyword name. columnExpr_p[nrcol] = handleKeyCol (str, True, tpq); if (columnExpr_p[nrcol].getTableInfo().table().isNull()) { // A keyword was given which is returned as a constant. nrSelExprUsed_p++; } else { // If a data type or shorthand is given, the column must be handled // as an expression. // The same is true if the same column is already used. In such a case // the user likely wants to duplicate the column with a different name. columnOldNames_p[nrcol] = oldName; if (!newDtype.empty() || inx >= 0) { nrSelExprUsed_p++; } else { for (Int i=0; i= 0) { shorthand = str.before(shInx); str = str.after(shInx); } } regex = Regex::fromPattern (str); } else { if (!negate) { shInx = str.index("\\."); if (shInx >= 0) { shorthand = str.before(shInx); str = str.after(shInx+1); } } if (name[0] == 'f') { regex = Regex(str); } else { // For regex type m prepend and append .* unless begin or end regex is given. if (str.size() > 0 && str[0] != '^') { str = ".*" + str; } if (str.size() > 0 && str[str.size()-1] != '$') { str = str + ".*"; } regex = Regex(str); } } if (!negate) { // Find all matching columns. Table tab = tableList_p.findTable(shorthand, False).table(); if (tab.isNull()) { throw TableInvExpr("Shorthand " + shorthand + " in wildcarded column " + name + " not defined in FROM clause"); } Vector columns = tab.tableDesc().columnNames(); // Add back the delimiting . if a shorthand is given. if (shInx >= 0) { shorthand += '.'; } Int nr = 0; for (uInt i=0; i 0) { --nrcol; if (! columnExpr_p[nrcol].isNull()) { break; } String col = columnNames_p[nrcol]; if (!col.empty()) { if (caseInsensitive) { col.downcase(); } if (col.matches(regex)) { columnNames_p[nrcol] = String(); } } } } } //# Finish the additions to the block of column names //# by removing the deleted empty names and creating Expr objects as needed. Table TableParseProject::handleColumnFinish (Bool distinct, Bool hasResultSet, TableParseQuery& tpq) { // Remove the deleted column names. // Create Expr objects for the wildcarded names. Int nrcol = columnNames_p.size(); if (nrcol > 0) { if (hasResultSet) { throw TableInvExpr("Expressions can be given in SELECT or GIVING, " "not both"); } Block names(nrcol); Block nameMasks(nrcol); Block oldNames(nrcol); Block exprs(nrcol); Block dtypes(nrcol); Block keywords(nrcol); Int nr = 0; for (Int i=0; i= 0) { name = name.after(j); } // Make an expression of the column name. exprs[nr] = handleKeyCol (name, False, tpq); names[nr] = name; oldNames[nr] = name; // Get the keywords for this column (to copy unit, etc.) TableColumn tabcol(exprs[nr].getTableInfo().table(), name); keywords[nr] = tabcol.keywordSet(); } ++nr; } } names.resize (nr, True); oldNames.resize (nr, True); exprs.resize (nr, True); dtypes.resize (nr, True); keywords.resize (nr, True); columnNames_p = names; columnNameMasks_p = nameMasks; columnOldNames_p = oldNames; columnExpr_p = exprs; columnDtypes_p = dtypes; columnKeywords_p = keywords; } if (distinct && columnNames_p.empty()) { throw TableInvExpr ("SELECT DISTINCT can only be given with at least " "one column name"); } // Make (empty) new table if select expressions were given. // This table is used when output columns are used in ORDERBY or HAVING. if (nrSelExprUsed_p > 0) { return makeProjectExprTable (tpq); } return Table(); } //# Add a column specification. void TableParseProject::handleColSpec (const String& colName, const String& likeColName, const String& dtstr, const Record& spec, Bool isCOrder) { // Check if specific column info is given. DataType dtype = TpOther; Int options = 0; Int ndim = -1; IPosition shape; String dmType; String dmGroup; String comment; Vector unit; TableRecord keywords; // See if the column is like another column. if (likeColName.empty()) { AlwaysAssert (! dtstr.empty(), AipsError); } else { // Use the description of the LIKE column. std::pair cdr = findColumnInfo (likeColName, colName); const ColumnDesc& cd = cdr.first; dtype = cd.dataType(); options = cd.options(); if (cd.isArray()) { ndim = cd.ndim(); } shape = cd.shape(); dmType = cd.dataManagerType(); dmGroup = cd.dataManagerGroup(); comment = cd.comment(); keywords = cd.keywordSet(); if (keywords.isDefined ("QuantumUnits")) { unit.reference (cd.keywordSet().asArrayString ("QuantumUnits")); } // Merge its dminfo into the overall one. DataManInfo::mergeInfo (dminfo_p, cdr.second); } if (! dtstr.empty()) { dtype = makeDataType (TpOther, dtstr, colName); } // Get the possible specifications (which override the LIKE column). for (uInt i=0; i ivec(spec.toArrayInt(i)); Int nd = ivec.size(); shape.resize (nd); if (isCOrder) { for (Int i=0; i(1, spec.asString(i))); } else { unit.reference (spec.asArrayString(i)); } } else { throw TableInvExpr ("TableParseProject::handleColSpec - " "column specification field name " + name + " is unknown"); } } // Now add the scalar or array column description. addColumnDesc (*tableDesc_p, dtype, colName, options, ndim, shape, dmType, dmGroup, comment, keywords, unit, Record()); Int nrcol = columnNames_p.size(); columnNames_p.resize (nrcol+1); columnNames_p[nrcol] = colName; } void TableParseProject::handleAddCol (const Record& dmInfo, Table& table) { // Merge the given dminfo into the dataman-info of the columns. DataManInfo::mergeInfo (dminfo_p, dmInfo); DataManInfo::finalizeMerge (*tableDesc_p, dminfo_p); DataManInfo::adaptNames (dminfo_p, table); if (dminfo_p.empty()) { StandardStMan ssm; table.addColumn (*tableDesc_p, ssm); } else { table.addColumn (*tableDesc_p, dminfo_p); } } Table TableParseProject::makeProjectExprTable (TableParseQuery& tpq) { // Make a column description for all expressions. // Check if all tables involved have the same nr of rows as the first one. TableDesc td; for (uInt i=0; i(1, columnExpr_p[i].unit().getName()), columnExpr_p[i].attributes()); if (! columnNameMasks_p[i].empty()) { addColumnDesc (td, TpBool, columnNameMasks_p[i], 0, columnExpr_p[i].isScalar() ? -1:0, //ndim IPosition(), "", "", "", TableRecord(), Vector(), Record()); } } // Create the table. return tpq.createTable (td, 0, dminfo_p, std::vector(), std::vector()); } void TableParseProject::makeProjectExprSel() { // Create/initialize the block of indices of projected columns used // elsewhere. projectExprSelColumn_p.resize (columnNames_p.size()); std::fill (projectExprSelColumn_p.begin(), projectExprSelColumn_p.end(), False); // Set to True for the used columns. uInt ncol = 0; for (uInt i=0; i(desc); dminfo_p = dminfo; } DataType TableParseProject::makeDataType (DataType dtype, const String& dtstr, const String& colName) { if (! dtstr.empty()) { if (dtstr == "B") { if (dtype != TpOther && dtype != TpBool) { throw TableInvExpr ("Expression of column " + colName + " does not have data type Bool"); } return TpBool; } if (dtstr == "S") { if (dtype != TpOther && dtype != TpString) { throw TableInvExpr ("Expression of column " + colName + " does not have data type String"); } return TpString; } if (dtype == TpBool || dtype == TpString) { throw TableInvExpr ("Expression of column " + colName + " does not have a numeric data type"); } // Any numeric data type can be converted to Complex. if (dtstr == "C4") { return TpComplex; } else if (dtstr == "C8") { return TpDComplex; } // Real numeric data types cannot have a complex value. if (dtype == TpComplex || dtype == TpDComplex) { throw TableInvExpr ("Expression of column " + colName + " does not have a real numeric data type"); } if (dtstr == "U1") { return TpUChar; } else if (dtstr == "I2") { return TpShort; } else if (dtstr == "U2") { return TpUShort; } else if (dtstr == "I4") { return TpInt; } else if (dtstr == "U4") { return TpUInt; } else if (dtstr == "I8") { return TpInt64; } else if (dtstr == "R4") { return TpFloat; } else if (dtstr == "R8") { return TpDouble; } else if (dtstr == "EPOCH") { return TpQuantity; } throw TableInvExpr ("Datatype " + dtstr + " of column " + colName + " is invalid"); } if (dtype == TpOther) { throw TableInvExpr ("Datatype " + dtstr + " of column " + colName + " is invalid (maybe a set with incompatible units)"); } return dtype; } void TableParseProject::addColumnDesc (TableDesc& td, DataType dtype, const String& colName, Int options, Int ndim, const IPosition& shape, const String& dmType, const String& dmGroup, const String& comment, const TableRecord& keywordSet, const Vector& unitName, const Record& attributes) { if (ndim < 0) { switch (dtype) { case TpBool: td.addColumn (ScalarColumnDesc (colName, comment, dmType, dmGroup, options)); break; case TpUChar: td.addColumn (ScalarColumnDesc (colName, comment, dmType, dmGroup, 0, options)); break; case TpShort: td.addColumn (ScalarColumnDesc (colName, comment, dmType, dmGroup, 0, options)); break; case TpUShort: td.addColumn (ScalarColumnDesc (colName, comment, dmType, dmGroup, 0, options)); break; case TpInt: td.addColumn (ScalarColumnDesc (colName, comment, dmType, dmGroup, 0, options)); break; case TpUInt: td.addColumn (ScalarColumnDesc (colName, comment, dmType, dmGroup, 0, options)); break; case TpInt64: td.addColumn (ScalarColumnDesc (colName, comment, dmType, dmGroup, 0, options)); break; case TpFloat: td.addColumn (ScalarColumnDesc (colName, comment, dmType, dmGroup, options)); break; case TpDouble: case TpQuantity: td.addColumn (ScalarColumnDesc (colName, comment, dmType, dmGroup, options)); break; case TpComplex: td.addColumn (ScalarColumnDesc (colName, comment, dmType, dmGroup, options)); break; case TpDComplex: td.addColumn (ScalarColumnDesc (colName, comment, dmType, dmGroup, options)); break; case TpString: td.addColumn (ScalarColumnDesc (colName, comment, dmType, dmGroup, options)); break; default: AlwaysAssert (False, AipsError); } } else { // Giving a shape means fixed shape arrays. if (shape.size() > 0) { options |= ColumnDesc::FixedShape; } switch (dtype) { case TpBool: td.addColumn (ArrayColumnDesc (colName, comment, dmType, dmGroup, shape, options, ndim)); break; case TpUChar: td.addColumn (ArrayColumnDesc (colName, comment, dmType, dmGroup, shape, options, ndim)); break; case TpShort: td.addColumn (ArrayColumnDesc (colName, comment, dmType, dmGroup, shape, options, ndim)); break; case TpUShort: td.addColumn (ArrayColumnDesc (colName, comment, dmType, dmGroup, shape, options, ndim)); break; case TpInt: td.addColumn (ArrayColumnDesc (colName, comment, dmType, dmGroup, shape, options, ndim)); break; case TpUInt: td.addColumn (ArrayColumnDesc (colName, comment, dmType, dmGroup, shape, options, ndim)); break; case TpInt64: td.addColumn (ArrayColumnDesc (colName, comment, dmType, dmGroup, shape, options, ndim)); break; case TpFloat: td.addColumn (ArrayColumnDesc (colName, comment, dmType, dmGroup, shape, options, ndim)); break; case TpDouble: case TpQuantity: td.addColumn (ArrayColumnDesc (colName, comment, dmType, dmGroup, shape, options, ndim)); break; case TpComplex: td.addColumn (ArrayColumnDesc (colName, comment, dmType, dmGroup, shape, options, ndim)); break; case TpDComplex: td.addColumn (ArrayColumnDesc (colName, comment, dmType, dmGroup, shape, options, ndim)); break; case TpString: td.addColumn (ArrayColumnDesc (colName, comment, dmType, dmGroup, shape, options, ndim)); break; default: AlwaysAssert (False, AipsError); } } // Write the keywords. ColumnDesc& cd = td.rwColumnDesc(colName); TableRecord keys (keywordSet); keys.merge (TableRecord(attributes), RecordInterface::OverwriteDuplicates); // If no keys defined for this column, define Epoch measure for dates. // This is done in the same way as the TableMeasures do. if (dtype == TpQuantity && keys.empty()) { TableRecord r; r.define ("type", "epoch"); r.define ("Ref", "UTC"); keys.defineRecord ("MEASINFO", r); } cd.rwKeywordSet() = keys; // Write unit in column keywords (in TableMeasures compatible way). // Check if it is valid by constructing the Unit object. Vector unit(unitName); if (dtype == TpQuantity && (unit.empty() || unit[0].empty())) { unit.reference (Vector(1, "d")); } if (! unit.empty() && ! unit[0].empty()) { if (! shape.empty()) { if (! (unit.size() == 1 || unit.size() == uInt(shape[0]))) { throw AipsError("Nr of units must be 1 or match the first axis"); } } cd.rwKeywordSet().define ("QuantumUnits", unit); } } std::pair TableParseProject::findColumnInfo (const String& colName, const String& newColName) const { String columnName, shorthand; Vector fieldNames; if (TableParseUtil::splitName (shorthand, columnName, fieldNames, colName, True, False, True)) { throw TableInvExpr ("Column name " + colName + " is a keyword, no column"); } Table tab = tableList_p.findTable (shorthand, True).table(); if (tab.isNull()) { throw TableInvExpr("Shorthand " + shorthand + " has not been defined"); } Record dminfo = tab.dataManagerInfo(); // Try to find the column in the info. // If found, create a dminfo record for this column only. Record dmrec; for (uInt i=0; i cols(dm.asArrayString("COLUMNS")); if (std::find(cols.begin(), cols.end(), columnName) != cols.end()) { dm.define ("COLUMNS", Vector(1, newColName)); dmrec.defineRecord (0, dm); break; } } } return std::make_pair (tab.tableDesc().columnDesc(columnName), dmrec); } void TableParseProject::checkTableProjSizes() const { // Check if all main tables used in non-constant select expressions // have the same size as the first table. // Note: the first table is a main table (not a join table). rownr_t nrow = tableList_p.firstTable().nrow(); for (uInt i=0; iisConstant()) { std::vector
        tabs = TableExprNodeUtil::getNodeTables (columnExpr_p[i].getRep().get(), True); for (const Table& tab : tabs) { if (tab.nrow() != nrow) { throw TableInvExpr("Nr of rows of tables used in select " "expressions must be equal to first table"); } } } } } //# Lookup a field name in the table for which the shorthand is given. //# If no shorthand is given, use the first table. //# The shorthand and name are separated by a period. TableExprNode TableParseProject::handleKeyCol (const String& name, Bool tryProj, TableParseQuery& tpq) { //# Split the name into optional shorthand, column, and optional keyword. String shand, columnName; Vector fieldNames; Bool hasKey = TableParseUtil::splitName (shand, columnName, fieldNames, name, True, False, False); //# Use first table if there is no shorthand given. //# Otherwise find the table at the current level (no WITH tables). TableParsePair tabPair = tableList_p.findTable (shand, False); Table tab = tabPair.table(); if (tab.isNull()) { throw (TableInvExpr("Shorthand " + shand + " has not been defined in FROM clause")); return 0; } //# If :: is not given, we have a column or keyword. if (!hasKey) { if (tryProj && shand.empty() && fieldNames.empty()) { // Only the column name is given; so first try if the column is // a new name of a projected column. It can also be a column created // from the mask of a masked array. Bool found; Int inx = linearSearchBrackets (found, columnNames_p, columnName, columnNames_p.size()); if (!found) { inx = linearSearchBrackets (found, columnNameMasks_p, columnName, columnNameMasks_p.size()); } if (found) { // If a table resulting from projection is used, take column from it // if it exists in it. const Table& projectExprTable = tpq.projectExprTable(); if (!projectExprTable.isNull() && projectExprTable.tableDesc().isColumn (columnName)) { uInt nc = projectExprSubset_p.size(); projectExprSubset_p.resize (nc+1); projectExprSubset_p[nc] = inx; return projectExprTable.col (columnName); } else if (!columnOldNames_p.empty() && !columnOldNames_p[inx].empty()) { // Possibly the column is renamed, so use the old name. columnName = columnOldNames_p[inx]; } } } // If it is a column, check if all tables used have the same size. // Note: the projected table (used above) should not be checked. // Don't do that for join tables. if (tab.tableDesc().isColumn (columnName) && tabPair.joinIndex() < 0) { if (firstColTable_p.isNull()) { firstColTable_p = tab; firstColName_p = name; } else { if (tab.nrow() != firstColTable_p.nrow()) { throw TableInvExpr ("Nr of rows (" + String::toString(tab.nrow()) + ") in table column " + name + " differs from column "+ firstColName_p + " (" + String::toString(firstColTable_p.nrow()) + ')'); } } } // Create column or keyword node. try { TableExprNode node(TableExprNode::keyCol (tabPair.getTableInfo(), columnName, fieldNames)); Bool isJoin = False; if (tabPair.joinIndex() >= 0) { // See if it is a column expr; it could have been a keyword. if (node.getRep()->exprType() == TableExprNodeRep::Variable) { node = TaQLJoinColumn::makeColumnNode (node.getRep(), tpq.joins()[tabPair.joinIndex()]); isJoin = True; } } if (!isJoin) { tpq.addApplySelNode (node); } return node; } catch (const TableError&) { throw TableInvExpr(name + " is an unknown column (or keyword) in table " + tab.tableName()); } } //# If no column name, we have a table keyword. if (columnName.empty()) { return tab.key (fieldNames); } //# Otherwise we have a column keyword. TableColumn col (tab, columnName); return TableExprNode::newKeyConst (col.keywordSet(), fieldNames); } Table TableParseProject::project (const Table& tab) { // First do projection using the original column names. Table tabp = tab.project (columnOldNames_p); for (uInt i=0; i (columnNames_p[i], columnNameMasks_p[i], columnExpr_p[i], False)); } } } } void TableParseProject::getAggrNodes (std::vector& aggr) const { for (uInt i=0; i nodes = TableExprNodeUtil::getAggrNodes (columnExpr_p[i].getRep().get()); aggr.insert (aggr.end(), nodes.begin(), nodes.end()); } } void TableParseProject::setUpdateNames (std::vector>& upd) { for (uInt i=0; isetColumnName (columnNames_p[i]); upd[i]->setColumnNameMask (columnNameMasks_p[i]); } } void TableParseProject::setColumnNames (const std::vector>& upd) { columnNames_p.resize (upd.size()); for (uInt i=0; icolumnName(); } columnNameMasks_p.resize (columnNames_p.size()); } void TableParseProject::setStoredColumns() { columnNames_p = TableParseUtil::getStoredColumns (tableList_p.firstTable()); columnNameMasks_p.resize (columnNames_p.size()); } void TableParseProject::checkCountColumns() const { if (columnExpr_p.empty()) { throw TableInvExpr ("No COUNT columns given"); } for (uInt i=0; i #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableParseUpdate; class TableExprGroupResult; // // Class holding the info of a TaQL projection command // // // // // // Table projection is the selection of columns in a TaQL command. // This class gets the results of the parser of all columns in the projection. // Those results are column name, new name, data type and expression. // Furthermore, it holds info which projected columns are used in other // parts of a TaQL command (HAVING and ORDERBY). // Once the TaQL is fully parsed and all parameters are stored, it is executed. // This class creates the table description and data manager info and finally // creates the resulting table. First projected columns used in other parts // are filled. After a possible HAVING clause and ORDERBY clause is executed, // the remaining columns are filled. // class TableParseProject { public: // Constructor fills a reference to the tableList objkect. TableParseProject (const TableParseTableList& tableList); // Get access to the datamanager info. Record& dminfo() { return dminfo_p; } // Get the table description. const TableDesc& tableDesc() const { return *tableDesc_p; } // Get the projected column names. const Block& getColumnNames() const { return columnNames_p; } // Get the projected column expressions. const Block& getColumnExpr() const { return columnExpr_p; } // Are expressions used in the column projection? Bool hasExpressions() const { return nrSelExprUsed_p > 0; } // Return the number of projected columns used in other clauses such as HAVING // which need to be precalculated. uInt nColumnsPreCalc() const { return projectExprSubset_p.size(); } // Set the column names to the ones to be updated. void setColumnNames (const std::vector>&); // Put the column name and mask name into the update objects. void setUpdateNames (std::vector>&); // Set the names to the stored columns of the first table. // Resize columnNameMasks_p accordingly. void setStoredColumns(); // Add a column to the list of column names. void handleColumn (Int stringType, const String& name, const TableExprNode& expr, const String& newName, const String& newNameMask, const String& newDtype, TableParseQuery&); // Handle the selection of a wildcarded column name. void handleWildColumn (Int stringType, const String& name); // Finish the additions to the block of column names // by removing the deleted empty names and creating Expr objects as needed. // An exception is thrown if there is a resultset and if columns are selected. Table handleColumnFinish (Bool distinct, Bool hasResultSet, TableParseQuery&); // Keep the column specification in a create table command. void handleColSpec (const String& columnName, const String& likeColName, const String& dataType, const Record& spec, Bool isCOrder); // Add columns to the table of ALTER TABLE. // The column descriptions have already been added to tableDesc_p. void handleAddCol (const Record& dmInfo, Table&); // Initialize the table and data manager descriptions. void initDescriptions (const TableDesc& desc, const Record& dminfo); // Set the DataManager info for a new table. void setDMInfo (const Record& dminfo) { dminfo_p = dminfo;} // Find the keyword or column name and create a TableExprNode from it. // If tryProj=True it is first tried if the column is a coluymn // in the projected table (i.e., result from the SELECT part). TableExprNode handleKeyCol (const String& name, Bool tryProj, TableParseQuery&); // Make the table projection using the selected columns. // The columns in the resulting table are renamed if a new name was given. Table project (const Table& tab); // Create TableParseUpdate objects for the selected column expressions. void makeUpdate (Bool useSel, TableParseQuery& tpq); // Fill projectExprSelColumn_p telling the columns to be projected // at the first stage. void makeProjectExprSel(); // Check if the tables used in selection columns have the same // size as the first table given in FROM. void checkTableProjSizes() const; // Add possible aggregation functions used in projection to the vector. void getAggrNodes (std::vector& aggr) const; // Check if the COUNT columns are given correctly. void checkCountColumns() const; private: // Make the (empty) table for the expression in the SELECT clause. Table makeProjectExprTable (TableParseQuery&); // Make a data type from the string. // It checks if it is compatible with the given (expression) data type. DataType makeDataType (DataType dtype, const String& dtstr, const String& colName); // Add the description of a column to the table description. // ndim < 0 means a scalar column. void addColumnDesc (TableDesc& td, DataType dtype, const String& colName, Int options, Int ndim, const IPosition& shape, const String& dmType, const String& dmGroup, const String& comment, const TableRecord& keywordSet, const Vector& unitName, const Record& attributes); // Find the ColumnDesc and data manager info of another column (a LIKE column). // The LIKE column name can be qualified to use another table. // It sets the new column name in the data manager info. // An exception is thrown if colName is invalid or unknown. std::pair findColumnInfo (const String& colName, const String& newColName) const; //# Data members. const TableParseTableList& tableList_p; //# Table and data manager description for a series of column descriptions. //# TableDesc has no copy ctor, so use a shared_ptr. std::shared_ptr tableDesc_p; Record dminfo_p; //# Block of selected column names (new name in case of select). Block columnNames_p; //# Block of selected mask column names (for masked arrays). Block columnNameMasks_p; //# Block of selected column expressions. Block columnExpr_p; //# The old name for a selected column. Block columnOldNames_p; //# The new data type for a column. Block columnDtypes_p; //# The keywords used in a column. Block columnKeywords_p; //# Number of real expressions used in selected columns. uInt nrSelExprUsed_p; //# The projected columns used in the HAVING and ORDERBY clauses. Block projectExprSubset_p; Block projectExprSelColumn_p; //# The first table used when creating a column object. //# All other tables used for them should have the same size. Table firstColTable_p; String firstColName_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/TableParseQuery.cc000066400000000000000000001457361476623553700207760ustar00rootroot00000000000000//# TableParseQuery.cc: Class getting the parser results and executing a query //# Copyright (C) 1994-2022 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableParseQuery::TableParseQuery (CommandType commandType) : commandType_p (commandType), tableProject_p (tableList_p), resultType_p (0), resultCreated_p (False), endianFormat_p (Table::AipsrcEndian), overwrite_p (True), resultSet_p (0), distinct_p (False), limit_p (0), endrow_p (0), offset_p (0), stride_p (1), insSel_p (0), noDupl_p (False), order_p (Sort::Ascending) {} TableParseQuery::~TableParseQuery() { // Note that insSel_p is simply a pointer to another object, // so no delete should be done. delete resultSet_p; } TableParseJoin& TableParseQuery::addJoin() { // Add a TableParseJoin object. joins_p.emplace (joins_p.end(), this); return joins_p.back(); } void TableParseQuery::replaceTable (const Table& table) { AlwaysAssert (! tableList_p.empty(), AipsError); tableList_p.replaceTable (table); } //# Lookup a field name in the table for which the shorthand is given. //# If no shorthand is given, use the first table. //# The shorthand and name are separated by a period. TableExprNode TableParseQuery::handleKeyCol (const String& name, Bool tryProj) { return tableProject_p.handleKeyCol (name, tryProj, *this); } TableExprNode TableParseQuery::handleSlice (const TableExprNode& array, const TableExprNodeSet& indices, const TaQLStyle& style) { // Create a masked array if a single bool element is given. if (indices.dataType() == TableExprNodeRep::NTBool) { if (! (indices.isSingle() && indices.size() == 1 && indices.hasArrays())) { throw TableInvExpr ("Second argument of a masked array must be an array; maybe extra brackets are needed like [1,2][[T,F]]"); } return marray (array, TableExprNode(indices[0]->start())); } return TableExprNode::newArrayPartNode (array, indices, style); } //# Parse the name of a function. TableExprNode TableParseQuery::handleFunc (const String& name, const TableExprNodeSet& arguments, const TaQLStyle& style) { //# No functions have to be ignored. Vector ignoreFuncs; // Use a default table if no one available. if (tableList_p.empty()) { return TableParseFunc::makeFuncNode (this, name, arguments, ignoreFuncs, TableExprInfo(), style); } TableExprNode node = TableParseFunc::makeFuncNode (this, name, arguments, ignoreFuncs, tableList_p.first(), style); // A rowid function node needs to be added to applySelNodes_p. const TENShPtr& rep = node.getRep(); if (dynamic_cast(rep.get())) { addApplySelNode (node); } return node; } //# Add a column name to the block of column names. //# Only take the part beyond the period. //# Extend the block each time. Since there are only a few column names, //# this will not be too expensive. void TableParseQuery::handleColumn (Int stringType, const String& name, const TableExprNode& expr, const String& newName, const String& newNameMask, const String& newDtype) { tableProject_p.handleColumn (stringType, name, expr, newName, newNameMask, newDtype, *this); } //# Finish the additions to the block of column names //# by removing the deleted empty names and creating Expr objects as needed. void TableParseQuery::handleColumnFinish (Bool distinct) { distinct_p = distinct; projectExprTable_p = tableProject_p.handleColumnFinish (distinct, resultSet_p, *this); } Table TableParseQuery::createTable (const TableDesc& td, Int64 nrow, const Record& dmInfo, const std::vector& tempTables, const std::vector& stack) { // If the table name contains ::, a subtable has to be created. // Split the name at the last ::. Vector parts = stringToVector(resultName_p, std::regex("::")); if (parts.size() > 1) { return createSubTable (parts[parts.size()-1], td, nrow, dmInfo, tempTables, stack); } // Create the table. // The types are defined in function handleGiving. Table::TableType ttype = Table::Plain; Table::TableOption topt = Table::New; if (!overwrite_p) topt = Table::NewNoReplace; // Use default Memory if no name or 'memory' has been given. if (resultName_p.empty()) { ttype = Table::Memory; } else if (resultType_p == 1) { ttype = Table::Memory; } else if (resultType_p == 2) { topt = Table::Scratch; } SetupNewTable newtab(resultName_p, td, topt, storageOption_p); newtab.bindCreate (dmInfo); Table tab(newtab, ttype, nrow, False, endianFormat_p); resultCreated_p = True; return tab; } Table TableParseQuery::createSubTable (const String& subtableName, const TableDesc& td, Int64 nrow, const Record& dmInfo, const std::vector& tempTables, const std::vector& stack) { Table parent (TableParseUtil::openParentTable(resultName_p, subtableName, tempTables, stack)); return TableUtil::createSubTable (parent, subtableName, td, overwrite_p ? Table::New : Table::NewNoReplace, storageOption_p, dmInfo, TableLock(), nrow, False, endianFormat_p, TSMOption()); } //# Add a column specification. void TableParseQuery::handleColSpec (const String& colName, const String& likeColName, const String& dtstr, const Record& spec, Bool isCOrder) { tableProject_p.handleColSpec (colName, likeColName, dtstr, spec, isCOrder); } void TableParseQuery::handleGroupby (const std::vector& nodes, Bool rollup) { groupby_p.handleGroupby (nodes, rollup); } void TableParseQuery::handleHaving (const TableExprNode& node) { groupby_p.handleHaving (node); } void TableParseQuery::handleDropTab(const std::vector& tempTables, const std::vector& stack) { // Delete all tables. It has already been checked they exist. for (TableParsePair& tab : tableList_p.fromTablesNC()) { // Split the name on :: to see if a subtable has to be deleted. Vector parts = stringToVector(tab.name(), std::regex("::")); if (parts.size() > 1) { // There is a subtable, so delete the keyword in its parent. // First get the size of the parent name. const String& subName(parts[parts.size() - 1]); size_t sz = tab.name().size() - subName.size() - 2; Table parent(TableParseUtil::getTable (tab.tabnr(), tab.name().substr(0,sz), Table(), tempTables, stack)); // Make sure subtable is closed, otherwise cannot be deleted. tab.table() = Table(); TableUtil::deleteSubTable (parent, subName); } else { tab.table().markForDelete(); } } } void TableParseQuery::handleCreTab (const Record& dmInfo, const std::vector& tempTables, const std::vector& stack) { DataManInfo::mergeInfo (tableProject_p.dminfo(), dmInfo); DataManInfo::finalizeMerge (tableProject_p.tableDesc(), tableProject_p.dminfo()); table_p = createTable (tableProject_p.tableDesc(), limit_p, tableProject_p.dminfo(), tempTables, stack); } void TableParseQuery::handleAltTab() { // The first table has to be altered. AlwaysAssert (! tableList_p.empty(), AipsError); table_p = tableList_p.firstTable(); table_p.reopenRW(); if (! table_p.isWritable()) { throw TableInvExpr ("Table " + table_p.tableName() + " is not writable"); } } void TableParseQuery::handleAddCol (const Record& dmInfo) { tableProject_p.handleAddCol (dmInfo, table_p); } void TableParseQuery::initDescriptions (const TableDesc& desc, const Record& dminfo) { tableProject_p.initDescriptions (desc, dminfo); } ValueHolder TableParseQuery::getRecFld (const String& name) { String keyName; const TableRecord& keyset = findKeyword (name, keyName, False); Int fieldnr = keyset.fieldNumber (keyName); if (fieldnr < 0) { throw (TableInvExpr ("Keyword " + name + " does not exist")); } return keyset.asValueHolder (fieldnr); } TableRecord& TableParseQuery::findKeyword (const String& name, String& keyName, Bool update) { //# Split the name into optional shorthand, column, and keyword. String shand, columnName; Vector fieldNames; TableParseUtil::splitName (shand, columnName, fieldNames, name, True, True, False); Table tab = tableList_p.findTable (shand, False).table(); if (tab.isNull()) { throw (TableInvExpr("Shorthand " + shand + " not defined in FROM clause")); } TableRecord* rec; String fullName; if (columnName.empty()) { if (update) { rec = TableExprNode::findLastKeyRec (tab.rwKeywordSet(), fieldNames, fullName); } else { rec = TableExprNode::findLastKeyRec (tab.keywordSet(), fieldNames, fullName); } } else { if (update) { TableRecord& colkeys (TableColumn(tab, columnName).rwKeywordSet()); rec = TableExprNode::findLastKeyRec (colkeys, fieldNames, fullName); } else { const TableRecord& colkeys (TableColumn(tab, columnName).keywordSet()); rec = TableExprNode::findLastKeyRec (colkeys, fieldNames, fullName); } } keyName = fieldNames[fieldNames.size() -1 ]; return *rec; } void TableParseQuery::handleSetKey (const String& name, const String& dtype, const ValueHolder& value) { String keyName; TableRecord& keyset = findKeyword (name, keyName); if (value.dataType() == TpString || value.dataType() == TpRecord) { keyset.defineFromValueHolder (keyName, value); } else { TableParseUtil::setRecFld (keyset, keyName, dtype, value); } } void TableParseQuery::handleCopyCol (Bool showTimings) { // Note that table_p, tableDesc_p and dminfo_p have already been set. Timer timer; handleAddCol (Record()); doUpdate (False, Table(), table_p, table_p.rowNumbers()); if (showTimings) { timer.show (" Copy Column "); } } void TableParseQuery::handleRenameKey (const String& oldName, const String& newName) { String keyName; TableRecord& keyset = findKeyword (oldName, keyName); keyset.renameField (newName, keyName); } void TableParseQuery::handleRemoveKey (const String& name) { String keyName; TableRecord& keyset = findKeyword (name, keyName); keyset.removeField (keyName); } void TableParseQuery::handleWhere (const TableExprNode& node) { TableParseGroupby::checkAggrFuncs (node); node_p = node; } void TableParseQuery::handleSort (const std::vector& sort, Bool noDuplicates, Sort::Order order) { noDupl_p = noDuplicates; order_p = order; sort_p = sort; } void TableParseQuery::handleCalcComm (const TableExprNode& node) { TableParseGroupby::checkAggrFuncs (node); node_p = node; } //# Execute a query in the FROM clause and return the resulting table. Table TableParseQuery::doFromQuery (Bool showTimings) { Timer timer; // Execute the nested command. execute (False, False, True, 0); if (showTimings) { timer.show (" From query "); } return table_p; } //# Execute a subquery for an EXISTS operator. TableExprNode TableParseQuery::doExists (Bool notexists, Bool showTimings) { Timer timer; // Execute the nested command. // Default limit_p is 1. execute (False, True, True, 1); if (showTimings) { timer.show (" Exists query"); } // Flag notexists tells if NOT EXISTS or EXISTS was given. return TableExprNode (notexists == (Int64(table_p.nrow()) < limit_p)); } //# Execute a subquery and create the correct node object for it. TableExprNode TableParseQuery::doSubQuery (Bool showTimings) { Timer timer; // Execute the nested command. execute (False, True, True, 0); TableExprNode result; if (resultSet_p != 0) { // A set specification was given, so make the set. result = makeSubSet(); } else { // A single column was given, so get its data. result = TableParseUtil::getColSet (table_p); } if (showTimings) { timer.show (" Subquery "); } return result; } TableExprNode TableParseQuery::makeSubSet() const { // Perform some checks on the given set. if (resultSet_p->hasArrays()) { throw (TableInvExpr ("Set in GIVING clause should contain scalar" " elements")); } resultSet_p->checkEqualDataTypes(); // Link to set to make sure that TableExprNode hereafter does not delete // the object. TableExprNodeSet set(rownrs_p, *resultSet_p); return set.setOrArray(); } void TableParseQuery::handleLimit (const TableExprNodeSetElem& expr) { if (expr.start()) { offset_p = evalIntScaExpr (TableExprNode(expr.start())); } if (expr.increment()) { stride_p = evalIntScaExpr (TableExprNode(expr.increment())); if (stride_p <= 0) { throw TableInvExpr ("in the LIMIT clause stride " + String::toString(stride_p) + " must be positive"); } } if (expr.end()) { endrow_p = evalIntScaExpr (TableExprNode(expr.end())); } } void TableParseQuery::handleLimit (const TableExprNode& expr) { limit_p = evalIntScaExpr (expr); } void TableParseQuery::handleOffset (const TableExprNode& expr) { offset_p = evalIntScaExpr (expr); } void TableParseQuery::handleTableNoFrom() { if (limit_p < 0 || offset_p < 0 || endrow_p < 0) { throw TableInvExpr("LIMIT and OFFSET values cannot be negative if no " "tables are given in the FROM clause"); } // Use 1 row if limit_p nor endrow_p is given. Int64 nrow = 1; if (limit_p > 0) { nrow = limit_p + offset_p; } else if (endrow_p > 0) { nrow = endrow_p; } // Add a temp table with no columns and some rows to the FROM list. Table tab(Table::Memory); tab.addRow(nrow); tableList_p.addTable (-1, String(), tab, String(), True, std::vector(), std::vector()); } void TableParseQuery::handleAddRow (const TableExprNode& expr) { table_p.addRow (evalIntScaExpr (expr)); } Int64 TableParseQuery::evalIntScaExpr (const TableExprNode& expr) const { TableParseGroupby::checkAggrFuncs (expr); if (! TableExprNodeUtil::getNodeTables (expr.getRep().get(), False).empty()) { throw TableInvExpr ("LIMIT or OFFSET expression cannot contain columns"); } // Get the value as a double, because some expressions result in double. // Round it to an integer. TableExprId rowid(0); Double val; expr.get (rowid, val); if (val >= 0) { return static_cast(val+0.5); } return -static_cast(-val+0.5); } void TableParseQuery::handleUpdate() { // Set the column names, so they can be returned by tableCommand(). tableProject_p.setColumnNames (update_p); } void TableParseQuery::handleInsert() { // If no columns were given, all stored columns in the first table // are the target columns. if (tableProject_p.getColumnNames().empty()) { tableProject_p.setStoredColumns(); } // Check if #columns and values match. // Copy the names to the update objects. const Block& colNames = tableProject_p.getColumnNames(); if (update_p.size() != colNames.size()) { throw TableInvExpr ("Error in INSERT command; nr of columns (=" + String::toString(colNames.size()) + ") mismatches " "number of VALUES expressions (=" + String::toString(Int(update_p.size())) + ")"); } tableProject_p.setUpdateNames (update_p); } void TableParseQuery::handleInsert (TableParseQuery* sel) { insSel_p = sel; } void TableParseQuery::handleCount() { tableProject_p.checkCountColumns(); } //# Execute the updates. void TableParseQuery::doUpdate (Bool showTimings, const Table& origTable, Table& updTable, const Vector& rownrs, const std::shared_ptr& groups) { Timer timer; AlwaysAssert (updTable.nrow() == rownrs.size(), AipsError); //# If no rows to be updated, return immediately. //# (the code below will fail for empty tables) if (rownrs.empty()) { return; } // Reopen the table for write. updTable.reopenRW(); if (! updTable.isWritable()) { throw TableInvExpr ("Table " + updTable.tableName() + " is not writable"); } //# First check if the update columns and values are correct. uInt nrkey = update_p.size(); Block cols(nrkey); Block > maskCols(nrkey); for (uInt i=0; iupdateColumn (cols[i], maskCols[i], row, rowid); } } if (showTimings) { timer.show (" Update "); } } //# Execute the inserts. Table TableParseQuery::doInsert (Bool showTimings, Table& table) { Timer timer; // Reopen the table for write. table.reopenRW(); if (! table.isWritable()) { throw TableInvExpr ("Table " + table.tableName() + " is not writable"); } // Add rows if the inserts are given as expressions. // Select rows and use update to put the expressions into the rows. if (update_p.size() > 0) { uInt nexpr = insertExprs_p.size(); Int64 nrowex = nexpr / update_p.size(); AlwaysAssert (nrowex*update_p.size() == nexpr, AipsError); Int64 nrow = nrowex; if (limit_p > 0) { // See if #rows is given explicitly. nrow = limit_p; } else if (limit_p < 0) { nrow = table.nrow() + limit_p; } Vector newRownrs(nrow); indgen (newRownrs, table.nrow()); Vector selRownrs(1, table.nrow() + nrow); // Add new rows to TableExprNodeRowid. // It works because NodeRowid does not obey disableApplySelection. for (std::vector::iterator iter=applySelNodes_p.begin(); iter!=applySelNodes_p.end(); ++iter) { iter->disableApplySelection(); iter->applySelection (selRownrs); } // Add one row at a time, because an insert expression might use // the table itself. Int64 inx = 0; for (Int64 i=0; isetNode (insertExprs_p[inx*update_p.size() + j]); } doUpdate (False, Table(), sel, selRownrs); inx++; if (inx == nrowex) inx = 0; } return table(newRownrs); } // Handle the inserts from another selection. // Do the selection. insSel_p->execute (False, False, False, 0); Table sel = insSel_p->getTable(); if (sel.nrow() == 0) { return Table(); } // Get the target columns if not given. if (tableProject_p.getColumnNames().empty()) { tableProject_p.setStoredColumns(); } // Get the source columns. Block sourceNames; sourceNames = insSel_p->getColumnNames(); if (sourceNames.size() == 0) { sourceNames = TableParseUtil::getStoredColumns (sel); } // Check if the number of columns match. const Block& colNames = tableProject_p.getColumnNames(); if (sourceNames.size() != colNames.size()) { throw TableInvExpr ("Error in INSERT command; nr of columns (=" + String::toString(colNames.size()) + ") mismatches " "number of columns in selection (=" + String::toString(sourceNames.size()) + ")"); } // Check if the data types match. const TableDesc& tdesc1 = table.tableDesc(); const TableDesc& tdesc2 = sel.tableDesc(); for (uInt i=0; i rownrs(sel.nrow()); indgen (rownrs, rownr); // fill with rownr, rownr+1, etc. Table tab = table(rownrs); TableRow rowto (tab, Vector(colNames.begin(), colNames.end())); ROTableRow rowfrom (sel, Vector(sourceNames.begin(), sourceNames.end())); for (rownr_t i=0; i countDesc ("_COUNT_"); tab.addColumn (countDesc); ScalarColumn countCol(tab, "_COUNT_"); // Iterate for all columns through the input table. Vector colNames = intab.tableDesc().columnNames(); Block bcolNames(colNames.size()); std::copy (colNames.begin(), colNames.end(), bcolNames.begin()); TableIterator iter (intab, bcolNames); while (!iter.pastEnd()) { Table tabfrom = iter.table(); // Add one row containing the column values. rownr_t rownr = tab.nrow(); tab.addRow(); Table tabto = tab.project (bcolNames); TableCopy::copyRows (tabto, tabfrom, rownr, 0, 1); // Put the count. countCol.put (rownr, tabfrom.nrow()); iter++; } if (showTimings) { timer.show (" Count "); } return tab; } //# Execute the groupby. std::shared_ptr TableParseQuery::doGroupby (Bool showTimings) { Timer timer; std::shared_ptr result = groupby_p.execGroupAggr(rownrs_p); if (showTimings) { timer.show (" Groupby "); } return result; } Table TableParseQuery::adjustApplySelNodes (const Table& table) { for (std::vector::iterator iter=applySelNodes_p.begin(); iter!=applySelNodes_p.end(); ++iter) { iter->applySelection (rownrs_p); } // Create the subset. Table tab(table(rownrs_p)); // From now on use row numbers 0..n. indgen (rownrs_p); return tab; } Bool TableParseQuery::doHaving (Bool showTimings, const std::shared_ptr& groups) { Timer timer; // Find the rows matching the HAVING expression. Bool done = groupby_p.execHaving (rownrs_p, groups); if (showTimings) { timer.show (" Having "); } return done; } //# Execute the sort. void TableParseQuery::doSort (Bool showTimings) { //# If no rows, return immediately. //# (the code below will fail if empty) if (rownrs_p.empty()) { return; } Timer timer; // Create and fill a Sort object for all keys. // The data are kept in vector Arrays and are automatically deleted at the end. std::vector> arrays; Sort sort; for (auto& sortKey : sort_p) { arrays.push_back (sortKey.addSortValues (sort, order_p, rownrs_p)); } rownr_t nrrow = rownrs_p.size(); Vector newRownrs (nrrow); int sortOpt = Sort::HeapSort; if (noDupl_p) { sortOpt += Sort::NoDuplicates; } sort.sort (newRownrs, nrrow, sortOpt); if (showTimings) { timer.show (" Orderby "); } // Convert index to rownr. for (rownr_t i=0; i newRownrs; // Negative values mean from the end (a la Python indexing). Int64 nrow = rownrs_p.size(); if (offset_p < 0) { offset_p += nrow; if (offset_p < 0) offset_p = 0; } // A limit (i.e. nr of rows) or an endrow can be given (not both). // Convert a limit to endrow. if (limit_p != 0) { if (limit_p < 0) limit_p += nrow; endrow_p = offset_p + limit_p*stride_p; } else if (endrow_p != 0) { if (endrow_p < 0) endrow_p += nrow; } else { endrow_p = nrow; } if (endrow_p > nrow) endrow_p = nrow; if (offset_p < endrow_p) { Int64 nr = 1 + (endrow_p - offset_p - 1) / stride_p; newRownrs.reference (rownrs_p(Slice(offset_p, nr, stride_p)).copy()); } rownrs_p.reference (newRownrs); if (showTimings) { timer.show (" Limit/Offset"); } } Table TableParseQuery::doLimOff (Bool showTimings, const Table& table) { Timer timer; rownrs_p.resize (table.nrow()); indgen (rownrs_p); doLimOff (False); return table(rownrs_p); if (showTimings) { timer.show (" Limit/Offset"); } } Table TableParseQuery::doProject (Bool showTimings, const Table& table, const std::shared_ptr& groups) { Timer timer; Table tabp; // doProjectExpr might have been done for some columns, so clear first to avoid // they are calculated twice. update_p.clear(); if (tableProject_p.hasExpressions()) { // Expressions used, so make a real table. tabp = doProjectExpr (False, groups); } else { // Only column names used, so make a reference table. tabp = table(rownrs_p); tabp = tableProject_p.project (tabp); } if (showTimings) { timer.show (" Projection "); } if (distinct_p) { tabp = doDistinct (showTimings, tabp); } return tabp; } Table TableParseQuery::doProjectExpr (Bool useSel, const std::shared_ptr& groups) { if (! rownrs_p.empty()) { // Add the rows if not done yet. if (projectExprTable_p.nrow() == 0) { projectExprTable_p.addRow (rownrs_p.size()); } // Turn the expressions of the selected columns into update objects. tableProject_p.makeUpdate (useSel, *this); // Fill the columns in the table. doUpdate (False, Table(), projectExprTable_p, rownrs_p, groups); projectExprTable_p.flush(); } return projectExprTable_p; } Table TableParseQuery::doFinish (Bool showTimings, Table& table, const std::vector& tempTables, const std::vector& stack) { Timer timer; Table result(table); // If the table name contains ::, a subtable has to be created. // Split the name at the last ::. Vector parts = stringToVector(resultName_p, std::regex("::")); Table parent; String fullName (resultName_p); if (parts.size() > 1) { parent = TableParseUtil::openParentTable (resultName_p, parts[parts.size()-1], tempTables, stack); fullName = parent.tableName() + '/' + parts[parts.size()-1]; } if (resultType_p == 1) { if (table.tableType() != Table::Memory) { result = table.copyToMemoryTable (fullName); } } else if (! resultCreated_p) { if (resultType_p > 0) { table.deepCopy (fullName, tableProject_p.dminfo(), storageOption_p, overwrite_p ? Table::New : Table::NewNoReplace, True, endianFormat_p); result = Table(fullName); } else { // Normal reference table. table.rename (fullName, overwrite_p ? Table::New : Table::NewNoReplace); table.flush(); } } // Create a subtable keyword if needed. if (parts.size() > 1) { parent.reopenRW(); parent.rwKeywordSet().defineTable (parts[parts.size()-1], table); } if (showTimings) { timer.show (" Giving "); } return result; } Table TableParseQuery::doDistinct (Bool showTimings, const Table& table) { Timer timer; Table result; // Sort the table uniquely on all columns. Table tabs = table.sort (tableProject_p.getColumnNames(), Sort::Ascending, Sort::QuickSort|Sort::NoDuplicates); if (tabs.nrow() == table.nrow()) { // Everything was already unique. result = table; } else { // Get the rownumbers. // Make sure it does not reference an internal array. Vector rownrs(tabs.rowNumbers(table)); rownrs.unique(); // Put the rownumbers back in the original order. GenSort::sort (rownrs); result = table(rownrs); rownrs_p.reference (rownrs); } if (showTimings) { timer.show (" Distinct "); } return result; } //# Keep the name of the resulting table. void TableParseQuery::handleGiving (const String& name, const Record& rec) { resultName_p = name; for (uInt i=0; i 2) { throw TableParseError ("output table name can only be omitted if " "AS MEMORY or AS SCRATCH is given"); } } //# Keep the resulting set expression. void TableParseQuery::handleGiving (const TableExprNodeSet& set) { // In principle GIVING is handled before SELECT, so below is always false. // But who knows what future brings us. if (! tableProject_p.getColumnNames().empty()) { throw TableInvExpr("Expressions can be given in SELECT or GIVING, " "not both"); } resultSet_p = new TableExprNodeSet (set); TableExprNodeUtil::checkAggrFuncs (resultSet_p); } //# Execute all parts of a TaQL command doing some selection. void TableParseQuery::execute (Bool showTimings, Bool setInGiving, Bool mustSelect, rownr_t maxRow, Bool doTracing, const std::vector& tempTables, const std::vector& stack) { //# A selection query consists of: //# - SELECT to do projection //# can only refer to columns in FROM or can be constants //# can contain aggregate functions //# - FROM to tell the tables to use //# - WHERE to select rows from tables //# can only refer to columns in FROM //# cannot contain aggregate functions //# - GROUPBY to group result of WHERE //# can refer to columns in FROM or expressions of FROM //# (in SQL92 only to columns in FROM) //# cannot contain aggregate functions //# - HAVING to select groups //# can refer to column in SELECT or FROM //# HAVING is possible without GROUPBY (-> one group only) //# usually refers to aggregate functions/columns //# if non-aggregate function is used, glast is implied //# - apply combination (UNION, INTERSECTION, DIFFERENCE) //# must have equal SELECT result (with equal column names) //# - ORDERBY to sort result of HAVING //# can refer to columns in SELECT or FROM //# (in SQL92 only to columns in SELECT), thus look in SELECT first //# can contain aggregate functions if aggregation or GROUPBY is used //# - LIMIT to skip latest results of ORDERBY //# - OFFSET to ignore first results of ORDERBY //# If GROUPBY/aggr is used, all clauses can contain other columns than //# aggregate or GROUPBY columns. The last row in a group is used for them. //# Set limit if not given. if (limit_p == 0) { limit_p = maxRow; if (doTracing && limit_p) { cerr << "LIMIT not given; set to " << limit_p << endl; } } //# Give an error if no command part has been given. if (mustSelect && commandType_p == PSELECT && node_p.isNull() && sort_p.size() == 0 && tableProject_p.getColumnNames().empty() && resultSet_p == 0 && limit_p == 0 && endrow_p == 0 && stride_p == 1 && offset_p == 0) { throw (TableInvExpr ("TableParse error: no projection, selection, sorting, " "limit, offset, or giving-set given in SELECT command")); } // Test if a "giving set" is possible. if (resultSet_p != 0 && !setInGiving) { throw TableInvExpr ("A query in a FROM can only have " "'GIVING tablename'"); } //# Set the project expressions to be filled in first stage. tableProject_p.makeProjectExprSel(); // Make sure WHERE expression does not use aggregate functions. // It has been checked before, but use defensive programming. TableParseGroupby::checkAggrFuncs (node_p); //# Get nodes representing aggregate functions. //# Test if aggregate, groupby, or having is used. groupby_p.findGroupAggr (tableProject_p.getColumnExpr(), commandType_p == PSELECT); if (! groupby_p.isUsed()) { // Check if tables used in projection have the same size. tableProject_p.checkTableProjSizes(); } else if (doTracing) { cerr << "GROUPBY to be done using " << groupby_p.size() << " aggregate nodes" << endl; } // Column nodes used in aggregate functions should not adhere applySelection. uInt ndisabled = groupby_p.disableApplySelection(); if (doTracing) { cerr << " disableApplySelection done in " << ndisabled << " column nodes of aggregate nodes" << endl; } // Select distinct makes no sense if aggregate and no groupby is given. if (groupby_p.isOnlyAggr()) { distinct_p = False; } //# The first table in the list is the source table. Table table = tableList_p.firstTable(); //# Set endrow_p if positive limit and positive or no offset. if (offset_p >= 0 && limit_p > 0) { endrow_p = offset_p + limit_p * stride_p; } //# Determine if we can pre-empt the selection loop. //# That is possible if a positive limit and offset are given //# without sorting, select distinct, groupby, or aggregation. rownr_t nrmax=0; if (endrow_p > 0 && sort_p.size() == 0 && !distinct_p && !groupby_p.isUsed()) { nrmax = endrow_p; if (doTracing) { cerr << "pre-empt WHERE at " << nrmax << " rows" << endl; } } //# First do the where selection. Table resultTable(table); if (! node_p.isNull()) { //#// cout << "Showing TableExprRange values ..." << endl; //#// Block rang; //#// node_p->ranges(rang); //#// for (Int i=0; i groupResult; if (groupby_p.isUsed() != 0) { groupResult = doGroupby (showTimings); // Aggregate results and normal table rows need to have the same rownrs, // so set the selected rows in the table column objects. resultTable = adjustApplySelNodes(table); table = resultTable; if (doTracing) { cerr << "GROUPBY resulted in " << table.nrow() << " groups" << endl; cerr << " applySelection called for " << applySelNodes_p.size() << " nodes" << endl; } } // Do the projection of SELECT columns used in HAVING or ORDERBY. // Thereafter the column nodes need to use rownrs 0..n. if (tableProject_p.nColumnsPreCalc() > 0) { doProjectExpr (True, groupResult); resultTable = adjustApplySelNodes(table); table = resultTable; if (doTracing) { cerr << "Pre-projected " << tableProject_p.nColumnsPreCalc() << " columns" << endl; cerr << " applySelection called for " << applySelNodes_p.size() << " nodes" << endl; } } // Do the possible HAVING step. if (doHaving (showTimings, groupResult)) { if (doTracing) { cerr << "HAVING resulted in " << rownrs_p.size() << " rows" << endl; } } //# Then do the sort. if (sort_p.size() > 0) { doSort (showTimings); if (doTracing) { cerr << "ORDERBY resulted in " << rownrs_p.size() << " rows" << endl; } } // If select distinct is given, limit/offset can only be done thereafter // because duplicate rows will be removed. if (!distinct_p && (offset_p != 0 || limit_p != 0 || endrow_p != 0 || stride_p != 1)) { doLimOff (showTimings); if (doTracing) { cerr << "LIMIT/OFFSET resulted in " << rownrs_p.size() << " rows" << endl; } } // Take the correct rows of the projected table (if not empty). resultTable = table(rownrs_p); if (projectExprTable_p.nrow() > 0) { if (rownrs_p.size() < projectExprTable_p.nrow() || sort_p.size() > 0) { projectExprTable_p = projectExprTable_p(rownrs_p); // Make deep copy if stored in a table. if (resultType_p == 3) { projectExprTable_p.rename (resultName_p + "_tmpproject", Table::New); projectExprTable_p.deepCopy (resultName_p, tableProject_p.dminfo(), storageOption_p, overwrite_p ? Table::New : Table::NewNoReplace, True, endianFormat_p); projectExprTable_p = Table(resultName_p); TableUtil::deleteTable (resultName_p + "_tmpproject"); // Indicate it does not have to be created anymore. resultCreated_p = True; } resultTable = projectExprTable_p; } } //# Then do the update, delete, insert, or projection and so. if (commandType_p == PUPDATE) { doUpdate (showTimings, table, resultTable, rownrs_p); table.flush(); } else if (commandType_p == PINSERT) { Table tabNewRows = doInsert (showTimings, table); table.flush(); resultTable = tabNewRows; } else if (commandType_p == PDELETE) { doDelete (showTimings, table); table.flush(); } else if (commandType_p == PCOUNT) { resultTable = doCount (showTimings, table); } else { //# Then do the projection. if (tableProject_p.getColumnNames().size() > 0) { resultTable = doProject (showTimings, table, groupResult); if (doTracing) { cerr << "Final projection done of " << tableProject_p.getColumnNames().size() - tableProject_p.nColumnsPreCalc() << " columns resulting in " << resultTable.nrow() << " rows" << endl; } } // If select distinct is given, limit/offset must be done at the end. if (distinct_p && (offset_p != 0 || limit_p != 0 || endrow_p != 0 || stride_p != 1)) { resultTable = doLimOff (showTimings, resultTable); if (doTracing) { cerr << "LIMIT/OFFSET resulted in " << resultTable.nrow() << " rows" << endl; } } //# Finally rename or copy using the given name (and flush it). if (resultType_p != 0 || ! resultName_p.empty()) { resultTable = doFinish (showTimings, resultTable, tempTables, stack); if (doTracing) { cerr << "Finished the GIVING command" << endl; } } } //# Keep the table for later. table_p = resultTable; } String TableParseQuery::getTableStructure (const Vector& parts, const TaQLStyle& style) { Bool showdm = False; Bool showcol = True; Bool showsub = False; Bool sortcol = False; Bool tabkey = False; Bool colkey = False; for (uInt i=2; i 2 && opt.substr(0,2) == "no") { fop = False; opt = opt.substr(2); } if (opt == "dm") { showdm = fop; } else if (opt == "col") { showcol = fop; } else if (opt == "sort") { sortcol = fop; } else if (opt == "key") { tabkey = fop; colkey = fop; } else if (opt == "tabkey") { tabkey = fop; } else if (opt == "colkey") { colkey = fop; } else if (opt == "recur") { showsub = fop; } else { throw TableInvExpr (parts[i] + " is an unknown show table option; use: " "dm col sort key tabkey colkey recur"); } } std::ostringstream os; tableList_p.firstTable().showStructure (os, showdm, showcol, showsub, sortcol, style.isCOrder()); tableList_p.firstTable().showKeywords (os, showsub, tabkey, colkey); return os.str(); } void TableParseQuery::show (ostream& os) const { if (! node_p.isNull()) { node_p.show (os); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/TableParseQuery.h000066400000000000000000000423031476623553700206220ustar00rootroot00000000000000//# TableParseQuery.h: Class getting the parser results and executing a query //# Copyright (C) 1994-2022 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABLEPARSEQUERY_H #define TABLES_TABLEPARSEQUERY_H //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableExprNodeSet; class TableExprNodeSetElem; class TableExprNodeIndex; class TableDesc; class TableColumn; class AipsIO; class Record; class TableRecord; template class ArrayColumn; // // Class getting the parser results and executing a query // // // // // //# Classes you should understand before using this one. //
      • TableGram.ll and .yy (flex and bison grammar) //
      • TaQLNodeHandler // // // The results of the bison parser TableGram.yy and flex scanner TableGram.ll // are stored in a tree of TaQLNode objects. When the parsing of a TaQL command // is fully done, the tree is traversed by TaQLNodeHandler which creates and // fills a stack of TableParseQuery objects, one object per (nested) query. // A nested query is executed once it is fully handled. // // // It is necessary to be able to give a table select command in ASCII. // This can be used in a CLI or in the table browser to get a subset // of a table or to sort a table. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class TableParseQuery { public: enum CommandType { PSELECT, PUPDATE, PINSERT, PDELETE, PCOUNT, PCALC, PCRETAB, PALTTAB, PDROPTAB, PSHOW }; // Construct. TableParseQuery (CommandType type); // Destructor. ~TableParseQuery(); // Return the command type. CommandType commandType() const { return commandType_p; } // Return the expression node. TableExprNode getNode() const { return node_p; } // Return the table name list. TableParseTableList& tableList() { return tableList_p; } Table& projectExprTable() { return projectExprTable_p; } // Execute the select command (select/sort/projection/groupby/having/giving). // The setInGiving flag tells if a set in the GIVING part is allowed. // The mustSelect flag tells if a SELECT command must do something. // Usually that is required, but not for a SELECT in an INSERT command. // Optionally the maximum nr of rows to be selected can be given. // It will be used as the default value for the LIMIT clause. // 0 = no maximum. void execute (Bool showTimings, Bool setInGiving, Bool mustSelect, rownr_t maxRow, Bool doTracing=False, const std::vector& tempTables = std::vector(), const std::vector& stack = std::vector()); // Execute a query in a FROM clause resulting in a Table. Table doFromQuery (Bool showTimings); // Execute a subquery and create an appropriate node for the result. TableExprNode doSubQuery (Bool showTimings); // Test if a subquery has sufficient elements. // It uses default LIMIT=1, but that can be overidden in the subquery. // The flag tells if NOT EXISTS or EXISTS was given. TableExprNode doExists (Bool noexists, Bool showTimings); // Show the expression tree. void show (ostream& os) const; // Create a temporary table if no tables are given in FROM. void handleTableNoFrom(); // Keep the selection expression. void handleWhere (const TableExprNode&); // Keep the groupby expressions. // It checks if they are all scalar expressions. void handleGroupby (const std::vector&, Bool rollup); // Keep the having expression. void handleHaving (const TableExprNode&); // Keep the expression of a calculate command. void handleCalcComm (const TableExprNode&); // Handle the DROP TABLE command. void handleDropTab (const std::vector& tempTables, const std::vector& stack); // Keep the create table command. void handleCreTab (const Record& dmInfo, const std::vector& tempTables, const std::vector& stack); // Keep the column specification in a create table command. void handleColSpec (const String& columnName, const String& likeColName, const String& dataType, const Record& spec, Bool isCOrder=False); // Reopen the table (for update) used in the ALTER TABLE command. void handleAltTab(); // Add columns to the table of ALTER TABLE. // The column descriptions have already been added to tableDesc_p. void handleAddCol (const Record& dmInfo); // Handle copying of columns. void handleCopyCol (Bool showTimings); // Add a keyword or replace a keyword with a value. // The keyword can be a table or column keyword (col::key). // The data type string can be empty leaving the data type unchanged. void handleSetKey (const String& name, const String& dtype, const ValueHolder& value); // Rename a table or column keyword. void handleRenameKey (const String& oldName, const String& newName); // Remove a table or column keyword. void handleRemoveKey (const String& name); // Keep the update expressions. void handleUpdate(); // Make ready for the insert expression. // The first one uses values (added via addUpdate), // the second one a subquery. // void handleInsert(); void handleInsert (TableParseQuery* sel); // // Make ready for a COUNT command. // It checks if all column expressions are scalar. void handleCount(); // Keep the sort expressions. void handleSort (const std::vector& sortList, Bool noDuplicates, Sort::Order defaultSortOrder); // Evaluate and keep limit/offset/stride given as start:end:incr void handleLimit (const TableExprNodeSetElem& expr); // Evaluate and keep the limit value. void handleLimit (const TableExprNode& expr); // Evaluate and keep the offset value. void handleOffset (const TableExprNode& expr); // Evaluate and add the rows. void handleAddRow (const TableExprNode& expr); // Add a join object. TableParseJoin& addJoin(); // Find the keyword or column name and create a TableExprNode from it. // If tryProj=True it is first tried if the column is a column // in the projected table (i.e., result from the SELECT part). TableExprNode handleKeyCol (const String& name, Bool tryProj); // Handle a slice operator. static TableExprNode handleSlice (const TableExprNode& array, const TableExprNodeSet& indices, const TaQLStyle&); // Handle a function. TableExprNode handleFunc (const String& name, const TableExprNodeSet& arguments, const TaQLStyle&); // Add a column to the list of column names. void handleColumn (Int type, const String& name, const TableExprNode& expr, const String& newName, const String& nameMask, const String& newDtype); // Finish the addition of columns to the list of column names. void handleColumnFinish (Bool distinct); // Handle the name and type given in a GIVING clause. void handleGiving (const String& name, const Record& type); // Handle the set given in a GIVING clause. void handleGiving (const TableExprNodeSet&); // Get the TableParseJoin objects. const std::vector& joins() const { return joins_p; } // Initialize the table and data manager descriptions. void initDescriptions (const TableDesc&, const Record& dminfo); // Add a keyword or replace a keyword with the value of another keyword. // The keywords can be table or column keywords (col::key). ValueHolder getRecFld (const String& name); // Split the given name into optional shorthand, column and fields. // Find the keywordset for it and fill in the final keyword name. // It is a helper function for handleSetKey, etc. // If update=True, rwKeywordSet() is used to ensure the table is updated. TableRecord& findKeyword (const String& name, String& keyName, Bool update=True); // Add an update object. void addUpdate (const std::shared_ptr& upd) { update_p.push_back (upd); } // Set the insert expressions for all rows. void setInsertExprs (const std::vector exprs) { insertExprs_p = exprs; } // Replace the first table (used by CALC command). void replaceTable (const Table& table); // Set the DataManager info for a new table. void setDMInfo (const Record& dminfo) { tableProject_p.setDMInfo (dminfo); } // Get the projected column names. const Block& getColumnNames() const { return tableProject_p.getColumnNames(); } // Get the resulting table. const Table& getTable() const { return table_p; } // Show the structure of fromTables_p[0] using the options given in parts[2:]. String getTableStructure (const Vector& parts, const TaQLStyle& style); // Add a column node to applySelNodes_p. void addApplySelNode (const TableExprNode& node) { applySelNodes_p.push_back (node); } // Create a table using the given parameters. // The variables set by handleGiven are used for name and type. Table createTable (const TableDesc& td, Int64 nrow, const Record& dmInfo, const std::vector& tempTables, const std::vector& stack); private: // Do the update step. // Rows 0,1,2,.. in UpdTable are updated from the expression result // for the rows in the given rownrs vector. void doUpdate (Bool showTimings, const Table& origTable, Table& updTable, const Vector& rownrs, const std::shared_ptr& groups = std::shared_ptr()); // Do the insert step and return a selection containing the new rows. Table doInsert (Bool showTimings, Table& table); // Do the delete step. void doDelete (Bool showTimings, Table& table); // Do the count step returning a memory table containing the unique // column values and the counts of the column values. Table doCount (Bool showTimings, const Table&); // Do the projection step returning a table containing the projection. Table doProject (Bool showTimings, const Table&, const std::shared_ptr& groups = std::shared_ptr()); // Do the projection containing column expressions. // Use the selected or unselected columns depending on useSel. Table doProjectExpr (Bool useSel, const std::shared_ptr& groups); // Create a subtable (used by createTable). Table createSubTable (const String& subtableName, const TableDesc& td, Int64 nrow, const Record& dmInfo, const std::vector& tempTables, const std::vector& stack); // Set the selected rows for the column objects in applySelNodes_p. // These nodes refer the original table. They requires different row // numbers than the selected groups and projected columns. // rownrs_p is changed to use row 0..n. // It returns the Table containing the subset of rows in the input Table. Table adjustApplySelNodes (const Table&); // Do the groupby/aggregate step and return its result. std::shared_ptr doGroupby (bool showTimings); // Do the HAVING step. // It returns False if no HAVING step was given. Bool doHaving (Bool showTimings, const std::shared_ptr& groups); // Do the sort step. void doSort (Bool showTimings); // Do the limit/offset step. void doLimOff (Bool showTimings); Table doLimOff (Bool showTimings, const Table& table); // Do the 'select distinct' step. Table doDistinct (Bool showTimings, const Table& table); // Finish the table (rename, copy, and/or flush). Table doFinish (Bool showTimings, Table& table, const std::vector& tempTables, const std::vector& stack); // Make an array from the contents of a column in a subquery. TableExprNode getColSet(); // Make a set from the results of the subquery. TableExprNode makeSubSet() const; // Evaluate an int scalar expression. Int64 evalIntScaExpr (const TableExprNode& expr) const; //# Data mambers. //# Command type. CommandType commandType_p; //# List of TableParsePair objects (from WITH and FROM clause). TableParseTableList tableList_p; //# A join object per join clause. std::vector joins_p; //# Object holding the info of table projection (i.e., column selection). TableParseProject tableProject_p; //# Name and type of the resulting table (from GIVING part). String resultName_p; uInt resultType_p; //# 0-unknown 1=memory 2=scratch 3=plain Bool resultCreated_p; //# Has the result table been created? StorageOption storageOption_p; Table::EndianFormat endianFormat_p; Bool overwrite_p; //# Resulting set (from GIVING part). TableExprNodeSet* resultSet_p; //# The WHERE expression tree. TableExprNode node_p; //# The GROUPBY, aggregate and HAVING info. TableParseGroupby groupby_p; //# Distinct values in output? Bool distinct_p; //# The possible limit (= max nr of selected rows) (0 means no limit). Int64 limit_p; //# The possible last row (0 means no end; can be <0). //# limit_p and endrow_p cannot be both !=0. Int64 endrow_p; //# The possible offset (= nr of selected rows to skip). Int64 offset_p; //# The possible stride in offset:endrow:stride. Int64 stride_p; //# The update and insert list. std::vector> update_p; //# The insert expressions (possibly for multiple rows). std::vector insertExprs_p; //# The table selection to be inserted. TableParseQuery* insSel_p; //# The sort list. std::vector sort_p; //# The noDuplicates sort switch. Bool noDupl_p; //# The default sort order. Sort::Order order_p; //# All nodes that need to be adjusted for a selection of rownrs. //# It can consist of column nodes and the rowid function node. //# Some nodes (in aggregate functions) can later be disabled for adjustment. std::vector applySelNodes_p; //# The resulting table. Table table_p; //# The table resulting from a projection with expressions. Table projectExprTable_p; //# The resulting row numbers. Vector rownrs_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/TableParseSortKey.cc000066400000000000000000000152251476623553700212560ustar00rootroot00000000000000//# TableParseSortKey.cc: Class to manage a key in a TaQL SORT command //# Copyright (C) 1994-2022 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableParseSortKey::TableParseSortKey() : order_p (Sort::Ascending), given_p (False) {} TableParseSortKey::TableParseSortKey (const TableExprNode& node) : node_p (node), order_p (Sort::Ascending), given_p (False) { checkNode(); } TableParseSortKey::TableParseSortKey (const TableExprNode& node, Sort::Order order) : node_p (node), order_p (order), given_p (True) { checkNode(); } void TableParseSortKey::checkNode() const { if (! node_p.isScalar()) { throw TableInvExpr("ORDERBY column/expression must be a scalar"); } TableParseGroupby::checkAggrFuncs (node_p); } std::shared_ptr TableParseSortKey::addSortValues (Sort& sort, Sort::Order mainOrder, const Vector& rownrs) const { // First check if the sort key has a valid data type. // This throws an exception for unknown data types (datetime, regex). node_p.getColumnDataType(); // If an order is given for this key, use it. Otherwise the main order. Sort::Order order = (given_p ? order_p : mainOrder); std::shared_ptr arrPtr; switch (node_p.getColumnDataType()) { case TpBool: { auto array = std::make_shared>(node_p.getColumnBool(rownrs)); if (! array->contiguousStorage()) { array = std::make_shared>(array->copy()); } arrPtr = array; sort.sortKey (array->data(), TpBool, 0, order); } break; case TpUChar: { auto array = std::make_shared>(node_p.getColumnuChar(rownrs)); if (! array->contiguousStorage()) { array = std::make_shared>(array->copy()); } arrPtr = array; sort.sortKey (array->data(), TpUChar, 0, order); } break; case TpShort: { auto array = std::make_shared>(node_p.getColumnShort(rownrs)); if (! array->contiguousStorage()) { array = std::make_shared>(array->copy()); } arrPtr = array; sort.sortKey (array->data(), TpShort, 0, order); } break; case TpUShort: { auto array = std::make_shared>(node_p.getColumnuShort(rownrs)); if (! array->contiguousStorage()) { array = std::make_shared>(array->copy()); } arrPtr = array; sort.sortKey (array->data(), TpUShort, 0, order); } break; case TpInt: { auto array = std::make_shared>(node_p.getColumnInt(rownrs)); if (! array->contiguousStorage()) { array = std::make_shared>(array->copy()); } arrPtr = array; sort.sortKey (array->data(), TpInt, 0, order); } break; case TpUInt: { auto array = std::make_shared>(node_p.getColumnuInt(rownrs)); if (! array->contiguousStorage()) { array = std::make_shared>(array->copy()); } arrPtr = array; sort.sortKey (array->data(), TpUInt, 0, order); } break; case TpInt64: { auto array = std::make_shared>(node_p.getColumnInt64(rownrs)); if (! array->contiguousStorage()) { array = std::make_shared>(array->copy()); } arrPtr = array; sort.sortKey (array->data(), TpInt64, 0, order); } break; case TpFloat: { auto array = std::make_shared>(node_p.getColumnFloat(rownrs)); if (! array->contiguousStorage()) { array = std::make_shared>(array->copy()); } arrPtr = array; sort.sortKey (array->data(), TpFloat, 0, order); } break; case TpDouble: { auto array = std::make_shared>(node_p.getColumnDouble(rownrs)); if (! array->contiguousStorage()) { array = std::make_shared>(array->copy()); } arrPtr = array; sort.sortKey (array->data(), TpDouble, 0, order); } break; case TpComplex: { auto array = std::make_shared>(node_p.getColumnComplex(rownrs)); if (! array->contiguousStorage()) { array = std::make_shared>(array->copy()); } arrPtr = array; sort.sortKey (array->data(), TpComplex, 0, order); } break; case TpDComplex: { auto array = std::make_shared>(node_p.getColumnDComplex(rownrs)); if (! array->contiguousStorage()) { array = std::make_shared>(array->copy()); } arrPtr = array; sort.sortKey (array->data(), TpDComplex, 0, order); } break; case TpString: { auto array = std::make_shared>(node_p.getColumnString(rownrs)); if (! array->contiguousStorage()) { array = std::make_shared>(array->copy()); } arrPtr = array; sort.sortKey (array->data(), TpString, 0, order); } break; default: AlwaysAssert (False, AipsError); } return arrPtr; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/TableParseSortKey.h000066400000000000000000000060751476623553700211230ustar00rootroot00000000000000//# TableParseSortKey.h: Class to manage a key in a TaQL SORT command //# Copyright (C) 1994-2022 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABLEPARSESORTKEY_H #define TABLES_TABLEPARSESORTKEY_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Helper class for sort keys in TableParse // // // // // // An object of this class is used to hold the sort expression // and sort order of a single sort key. // class TableParseSortKey { public: // Construct from a given expression. // The order is not given. TableParseSortKey(); // Construct from a given expression. // The order is not given. explicit TableParseSortKey (const TableExprNode&); // Construct from a given expression and order. TableParseSortKey (const TableExprNode&, Sort::Order); // Get the expression node. const TableExprNode& node() const { return node_p; } // Get the sort order. Sort::Order order() const { return order_p; } // Is the order given? Bool orderGiven() const { return given_p; } // Add the values of the sort key to the Sort object by reading the // values of the expression into an array. // The array has to be deletedlater after the full sort is done. std::shared_ptr addSortValues (Sort& sort, Sort::Order mainOrder, const Vector&) const; private: // Check if the node results in a scalar and does not contain // aggregate functions. void checkNode() const; TableExprNode node_p; Sort::Order order_p; Bool given_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/TableParseTableList.cc000066400000000000000000000112271476623553700215370ustar00rootroot00000000000000//# TableParseTableList.cc: Lists of tables used in a TaQL query //# Copyright (C) 1994-2022 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableParsePair::TableParsePair (const Table& table, Int tabnr, const String& name, const String& shorthand, Int joinIndex) : tabnr_p (tabnr), joinIndex_p (joinIndex), name_p (name), shorthand_p (shorthand), table_p (table) {} TableExprInfo TableParsePair::getTableInfo() const { return TableExprInfo (table_p, shorthand_p, joinIndex_p>=0); } //# Construct a TableParse object and add it to the container. Table TableParseTableList::addTable (Int tabnr, const String& name, const Table& ftab, const String& shorthand, Bool addToFromList, const std::vector& tempTables, const std::vector& stack, Int joinIndex) { Table table = TableParseUtil::getTable (tabnr, name, ftab, tempTables, stack); // Check that a shorthand is used only once, except for an empty one. // Don't take the WITH tables into account, otherwise it will complain // about a WITH shorthand used in a FROM. if (! shorthand.empty()) { TableParsePair tablePair = findTable (shorthand, False); if (! tablePair.table().isNull()) { throw TableInvExpr("Shorthand '" + shorthand + "' has already been used"); } } if (addToFromList) { itsFromTables.push_back (TableParsePair(table, tabnr, name, shorthand, joinIndex)); } else { itsWithTables.push_back (TableParsePair(table, tabnr, name, shorthand)); } return table; } void TableParseTableList::replaceTable (const Table& table) { AlwaysAssert (!itsFromTables.empty(), AipsError); itsFromTables[0].replaceTable (table); } TableParsePair TableParseTableList::findTable (const String& shorthand, Bool doWith, const std::vector& stack) { TableParsePair tab; for (Int i=stack.size()-1; i>=0; i--) { tab = stack[i]->tableList().findTable (shorthand, doWith); if (! tab.table().isNull()) { break; } } return tab; } TableParsePair TableParseTableList::findTable (const String& shorthand, Bool doWith) const { //# If no shorthand given, first table is taken (if there). for (uInt i=0; i #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations class TableParseQuery; // // Class binding a shorthand to a table name. // // // // // // This class is used by TableParse to associate a Table object and its // shorthand name (as used in TaQL). // class TableParsePair { public: TableParsePair() {} // Associate the table and the shorthand. // The full name and the table number (from $i) can also be given. TableParsePair (const Table& table, Int tabnr, const String& name, const String& shorthand, Int joinIndex=-1); // Test if shorthand matches. If also matches if the given shorthand is empty. Bool test (const String& str) const { return (str.empty() || shorthand_p == str); } // Get the given table number (of $i tables in TempTables) Int tabnr() const { return tabnr_p; } // Get the given table name. const String& name() const { return name_p; } // Get the shorthand. const String& shorthand() const { return shorthand_p; } // Get table object. const Table& table() const { return table_p; } Table& table() { return table_p; } // Get it as a TableExprInfo object. TableExprInfo getTableInfo() const; // Get the index of the table in the list of join objects. // <0 means that it is no join table. Int joinIndex() const { return joinIndex_p; } // Replace the Table object. void replaceTable (const Table& table) { table_p = table; } private: Int tabnr_p = -1; Int joinIndex_p = -1; String name_p; String shorthand_p; Table table_p; }; // // Class containing two lists of TableParsePair objects. // // // // // // This class is used by TableParse to hold two lists of TableParsePair objects. // One list is for the tables given in the WITH clause, the other list is for // the other tables given in e.g. FROM or UPDATE. // It has functions to operate on the lists, usually by means of the // shorthand name. // class TableParseTableList { public: // Is the FROM table list empty? Bool empty() const { return itsFromTables.empty(); } // Get the FROM tables. const std::vector& fromTables() const { return itsFromTables; } std::vector& fromTablesNC() { return itsFromTables; } // Return the first FROM table (which is usually the table to operate on). TableExprInfo first() const { return itsFromTables.at(0).getTableInfo(); } // Return the first FROM table (which is usually the table to operate on). const Table& firstTable() const { return itsFromTables.at(0).table(); } // Add a table to the list of tables with the given shorthand name. // The table can be given in a few ways: //
        - As a sequence number to be taken from tempTables. //
        - As a string giving the table name path. //
        - As a subtable name (starting with ::) which will be looked up in // the stack of query objects. //
        - As a temporary table (from a nested query) given in ttab. //
        - As the shorthand name of another table which will be looked up in // the stack of query objects. Table addTable (Int tabnr, const String& name, const Table& ttab, const String& shorthand, Bool addToFromList, const std::vector& tempTables, const std::vector& stack, Int joinsIndex = -1); // Replace the first Table object in the FROM list with the given one. void replaceTable (const Table& table); // Find a table for the given shorthand. // Optionally the WITH tables are searched as well. // If no shorthand is given, the first FROM table is returned (if there). // If not found, a TableParsePair with a null Table object is returned. static TableParsePair findTable (const String& shorthand, Bool doWith, const std::vector& stack); // Try to find the Table for the given shorthand in the table list. TableParsePair findTable (const String& shorthand, Bool doWith) const; // Find the keyword given in the name parameter which is // split into its shorthand, column and/or keyword parts. // It fills parameter keyName with the last keyword part // and returns the TableRecord containing that keyword. // It is a helper function for handleSetKey, etc. // If update=True, rwKeywordSet() is used to ensure the table is updated. // An exception is thrown in case a name is not found. TableRecord& findKeyword (const String& name, String& keyName, Bool update=True); private: //# Data members std::vector itsFromTables; std::vector itsWithTables; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/TableParseUpdate.cc000066400000000000000000000542151476623553700211020ustar00rootroot00000000000000//# TableParseUpdate.cc: Class to manage TaQL UPDATE command handling //# Copyright (C) 1994-2022 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableParseUpdate::TableParseUpdate (const String& columnName, const String& columnNameMask, const TableExprNode& node, Bool checkAggr) : columnName_p (columnName), columnNameMask_p (columnNameMask), maskFirst_p (False), indexPtr_p (0), node_p (node) { if (checkAggr) { TableParseGroupby::checkAggrFuncs (node); } } TableParseUpdate::TableParseUpdate (const String& columnName, const String& columnNameMask, const TableExprNodeSet& indices, const TableExprNode& node, const TaQLStyle& style) : columnName_p (columnName), columnNameMask_p (columnNameMask), maskFirst_p (False), indexPtr_p (0), node_p (node) { TableParseGroupby::checkAggrFuncs (node); handleIndices (indices, style); if (indexPtr_p == 0) { if (! columnNameMask_p.empty()) { throw TableInvExpr ("No mask column name can be given if the update " "data column is masked"); } maskFirst_p = True; } } TableParseUpdate::TableParseUpdate (const String& columnName, const String& columnNameMask, const TableExprNodeSet& indices1, const TableExprNodeSet& indices2, const TableExprNode& node, const TaQLStyle& style) : columnName_p (columnName), columnNameMask_p (columnNameMask), maskFirst_p (False), indexPtr_p (0), node_p (node) { // The grammar does not allow a column mask name, but you can never tell. AlwaysAssert (columnNameMask.empty(), AipsError); TableParseGroupby::checkAggrFuncs (node); handleIndices (indices1, style); maskFirst_p = indexPtr_p==0; handleIndices (indices2, style); } void TableParseUpdate::handleIndices (const TableExprNodeSet& indices, const TaQLStyle& style) { // Create a mask if a single bool element is given. if (indices.isSingle() && indices.size() == 1 && indices.dataType() == TableExprNodeRep::NTBool) { if (! mask_p.isNull()) { throw TableInvExpr ("A double indexed update array cannot contain " "two masks"); } if (! indices.hasArrays()) { throw TableInvExpr ("A mask in an update must be an array"); } mask_p = TableExprNode(indices[0]->start()); } else { if (indexPtr_p) { throw TableInvExpr ("A double indexed update array cannot contain " "two index ranges"); } indexPtr_p = new TableExprNodeIndex (indices, style); indexNode_p = TableExprNode(indexPtr_p); } } //# The following are helper functions for updateColumn. //# Due to their templated nature, they have to be properly ordered //# (thus the implementation before being used). template void TableParseUpdate::updateScalar (rownr_t row, const TableExprId& rowid, const TableExprNode& node, TableColumn& col) { AlwaysAssert (node.isScalar(), AipsError); TNODE val; node.get (rowid, val); TCOL value(static_cast(val)); col.putScalar (row, value); } template void TableParseUpdate::updateArray (rownr_t row, const TableExprId& rowid, const TableExprNode& node, const Array& res, ArrayColumn& col) { if (node.isScalar() && col.isDefined (row)) { TNODE val; node.get (rowid, val); Array arr(col.shape(row)); arr = static_cast(val); col.put (row, arr); } else { Array arr(res.shape()); convertArray (arr, res); col.put (row, arr); } } template void TableParseUpdate::updateSlice (rownr_t row, const TableExprId& rowid, const TableExprNode& node, const Array& res, const Slicer& slice, ArrayColumn& col) { if (col.isDefined(row)) { if (node.isScalar()) { TNODE val; node.get (rowid, val); Array arr; if (slice.isFixed()) { arr.resize (slice.length()); } else { // Unbound slicer, so derive from array shape. IPosition blc, trc, inc; arr.resize (slice.inferShapeFromSource (col.shape(row), blc, trc, inc)); } arr = static_cast(val); col.putSlice (row, slice, arr); } else { // Note that the calling function tests if the MArray is null. Array arr(res.shape()); convertArray (arr, res); col.putSlice (row, slice, arr); } } } template void TableParseUpdate::copyMaskedValue (rownr_t row, ArrayColumn& acol, const Slicer* slicerPtr, const TNODE* val, size_t incr, const Array& mask) { // Get the array from the table. Array res(mask.shape()); if (slicerPtr) { acol.getSlice (row, *slicerPtr, res); } else { acol.get (row, res); } // Copy values where masked. typename Array::iterator ito = res.begin(); Array::const_iterator imask = mask.begin(); size_t n = res.size(); for (size_t i=0; i(*val); } ++ito; ++imask; val += incr; } // Put the array (slice). if (slicerPtr) { acol.putSlice (row, *slicerPtr, res); } else { acol.put (row, res); } } template void TableParseUpdate::updateValue (rownr_t row, const TableExprId& rowid, Bool isScalarCol, const TableExprNode& node, const Array& mask, TableColumn& col, const Slicer* slicerPtr, ArrayColumn& maskCol) { if (isScalarCol) { updateScalar (row, rowid, node, col); } else { MArray aval; if (! node.isScalar()) { node.get (rowid, aval); if (aval.isNull()) { return; } } checkMaskColumn (aval.hasMask(), maskCol, col); ArrayColumn acol(col); if (mask.empty()) { if (slicerPtr) { updateSlice (row, rowid, node, aval.array(), *slicerPtr, acol); if (! maskCol.isNull()) { updateSlice (row, rowid, node, aval.mask(), *slicerPtr, maskCol); } } else { updateArray (row, rowid, node, aval.array(), acol); if (! maskCol.isNull()) { updateArray (row, rowid, node, aval.mask(), maskCol); } } } else { // A mask is used; can only be done if the column cell // contains an array. if (acol.isDefined(row)) { IPosition shapeCol = acol.shape (row); // Check shapes, get possible slice from mask. Array smask(makeMaskSlice (mask, shapeCol, slicerPtr)); // Get the expression data (scalar or array). TNODE sval; const TNODE* ptr = &sval; size_t incr = 0; Bool deleteIt = False; if (node.isScalar()) { node.get (rowid, sval); } else { if (! aval.shape().isEqual (smask.shape())) { throw TableInvExpr ("Array shapes in update of column " + col.columnDesc().name() + " mismatch"); } ptr = aval.array().getStorage (deleteIt); incr = 1; } // Put the array into the column (slice). // Copy values where masked. copyMaskedValue (row, acol, slicerPtr, ptr, incr, smask); if (! node.isScalar()) { aval.array().freeStorage (ptr, deleteIt); if (! maskCol.isNull()) { const Bool* bptr = aval.mask().getStorage (deleteIt); copyMaskedValue (row, maskCol, slicerPtr, bptr, 1, smask); aval.mask().freeStorage (bptr, deleteIt); } } } } } } Array TableParseUpdate::makeMaskSlice (const Array& mask, const IPosition& shapeCol, const Slicer* slicerPtr) { if (! slicerPtr || maskFirst_p) { if (! mask.shape().isEqual (shapeCol)) { throw TableInvExpr ("Update mask must conform the column's array shape"); } } if (slicerPtr) { IPosition length; if (slicerPtr->isFixed()) { length = slicerPtr->length(); } else { IPosition blc, trc, inc; length = slicerPtr->inferShapeFromSource (shapeCol, blc, trc, inc); } if (maskFirst_p) { // Mask before section, so apply the section to the mask. return mask(*slicerPtr); } else { if (! mask.shape().isEqual (length)) { throw TableInvExpr ("Update mask must conform the column's array section"); } } } return mask; } void TableParseUpdate::checkMaskColumn (Bool hasMask, const ArrayColumn& maskCol, const TableColumn& col) { // If a mask column is given, the expression must have a mask. // But if the expression has a mask, a mask column is not needed. // In that case the mask is ignored. This is necessary for a mask in // a select expression, otherwise function ARRAYDATA is always needed. if (! maskCol.isNull()) { if (! hasMask) { throw TableInvExpr ("No update mask column can be given for an " "unmasked expression in update of column " + col.columnDesc().name()); } } } void TableParseUpdate::updateColumn (TableColumn& col, ArrayColumn& maskCol, rownr_t row, const TableExprId& rowid) { // Get possible subscripts. const Slicer* slicerPtr = 0; if (indexPtr_p != 0) { slicerPtr = &(indexPtr_p->getSlicer(rowid)); } // Evaluate a possible mask. MArray mask; if (! mask_p.isNull()) { mask_p.get (rowid, mask); } // The expression node type determines how to get the data. // The column data type determines how to put it. // The node data type should be convertible to the column data type. // The updateValue function does the actual work. // We simply switch on the types. Bool isScalarCol = col.columnDesc().isScalar(); switch (node_p.getNodeRep()->dataType()) { case TableExprNodeRep::NTBool: switch (col.columnDesc().dataType()) { case TpBool: updateValue (row, rowid, isScalarCol, node_p, mask.array(), col, slicerPtr, maskCol); break; default: throw TableInvExpr ("Column " + columnName_p + " has an invalid data type for an" " UPDATE with a bool value"); } break; case TableExprNodeRep::NTString: switch (col.columnDesc().dataType()) { case TpString: updateValue (row, rowid, isScalarCol, node_p, mask.array(), col, slicerPtr, maskCol); break; default: throw TableInvExpr ("Column " + columnName_p + " has an invalid data type for an" " UPDATE with a string value"); } break; case TableExprNodeRep::NTInt: switch (col.columnDesc().dataType()) { case TpUChar: updateValue (row, rowid, isScalarCol, node_p, mask.array(), col, slicerPtr, maskCol); break; case TpShort: updateValue (row, rowid, isScalarCol, node_p, mask.array(), col, slicerPtr, maskCol); break; case TpUShort: updateValue (row, rowid, isScalarCol, node_p, mask.array(), col, slicerPtr, maskCol); break; case TpInt: updateValue (row, rowid, isScalarCol, node_p, mask.array(), col, slicerPtr, maskCol); break; case TpUInt: updateValue (row, rowid, isScalarCol, node_p, mask.array(), col, slicerPtr, maskCol); break; case TpInt64: updateValue (row, rowid, isScalarCol, node_p, mask.array(), col, slicerPtr, maskCol); break; case TpFloat: updateValue (row, rowid, isScalarCol, node_p, mask.array(), col, slicerPtr, maskCol); break; case TpDouble: updateValue (row, rowid, isScalarCol, node_p, mask.array(), col, slicerPtr, maskCol); break; case TpComplex: updateValue (row, rowid, isScalarCol, node_p, mask.array(), col, slicerPtr, maskCol); break; case TpDComplex: updateValue (row, rowid, isScalarCol, node_p, mask.array(), col, slicerPtr, maskCol); break; default: throw TableInvExpr ("Column " + columnName_p + " has an invalid data type for an" " UPDATE with an integer value"); } break; case TableExprNodeRep::NTDouble: case TableExprNodeRep::NTDate: switch (col.columnDesc().dataType()) { case TpUChar: updateValue (row, rowid, isScalarCol, node_p, mask.array(), col, slicerPtr, maskCol); break; case TpShort: updateValue (row, rowid, isScalarCol, node_p, mask.array(), col, slicerPtr, maskCol); break; case TpUShort: updateValue (row, rowid, isScalarCol, node_p, mask.array(), col, slicerPtr, maskCol); break; case TpInt: updateValue (row, rowid, isScalarCol, node_p, mask.array(), col, slicerPtr, maskCol); break; case TpUInt: updateValue (row, rowid, isScalarCol, node_p, mask.array(), col, slicerPtr, maskCol); break; case TpInt64: updateValue (row, rowid, isScalarCol, node_p, mask.array(), col, slicerPtr, maskCol); break; case TpFloat: updateValue (row, rowid, isScalarCol, node_p, mask.array(), col, slicerPtr, maskCol); break; case TpDouble: updateValue (row, rowid, isScalarCol, node_p, mask.array(), col, slicerPtr, maskCol); break; case TpComplex: updateValue (row, rowid, isScalarCol, node_p, mask.array(), col, slicerPtr, maskCol); break; case TpDComplex: updateValue (row, rowid, isScalarCol, node_p, mask.array(), col, slicerPtr, maskCol); break; default: throw TableInvExpr ("Column " + columnName_p + " has an invalid data type for an" " UPDATE with a double value"); } break; case TableExprNodeRep::NTComplex: switch (col.columnDesc().dataType()) { case TpComplex: updateValue (row, rowid, isScalarCol, node_p, mask.array(), col, slicerPtr, maskCol); break; case TpDComplex: updateValue (row, rowid, isScalarCol, node_p, mask.array(), col, slicerPtr, maskCol); break; default: throw TableInvExpr ("Column " + columnName() + " has an invalid data type for an" " UPDATE with a complex value"); } break; default: throw TableInvExpr ("Unknown UPDATE expression data type"); } } void TableParseUpdate::check (const Table& origTable, const Table& updTable) const { // Check if the correct table is used in the update and index expression. // A constant expression can be given. if (! node_p.checkTableSize (origTable, True)) { throw TableInvExpr ("Table(s) with incorrect size used in the " "UPDATE expr of column " + columnName_p + " (mismatches first table)"); } if (indexPtr_p != 0) { if (! indexNode_p.checkTableSize (updTable, True)) { throw TableInvExpr ("Table(s) with incorrect size used in the " "index expr in UPDATE of column " + columnName_p + " (mismatches first table)"); } } // This throws an exception for unknown data types (datetime, regex). node_p.getColumnDataType(); // Check if the column exists and is writable. const TableDesc& tabdesc = updTable.tableDesc(); if (! tabdesc.isColumn (columnName_p)) { throw TableInvExpr ("Update column " + columnName_p + " does not exist in table " + updTable.tableName()); } if (! updTable.isColumnWritable (columnName_p)) { throw TableInvExpr ("Update column " + columnName_p + " is not writable in table " + updTable.tableName()); } const ColumnDesc& coldesc = tabdesc[columnName_p]; Bool isScalar = coldesc.isScalar(); if (! columnNameMask_p.empty()) { if (! tabdesc.isColumn (columnNameMask_p)) { throw TableInvExpr ("Update column " + columnNameMask_p + " does not exist in table " + updTable.tableName()); } if (! updTable.isColumnWritable (columnNameMask_p)) { throw TableInvExpr ("Update column " + columnNameMask_p + " is not writable in table " + updTable.tableName()); } const ColumnDesc& coldescMask = tabdesc[columnNameMask_p]; if (node_p.isScalar()) { throw TableInvExpr ("Update mask column " + columnNameMask_p + " cannot be given for a scalar expression"); } if (coldescMask.dataType() != TpBool) { throw TableInvExpr ("Update mask column " + columnNameMask_p + " must have data type Bool"); } } // An index expression can only be given for an array column. if (indexPtr_p != 0) { if (isScalar) { throw TableInvExpr ("Index value cannot be given in UPDATE of " " scalar column " + columnName_p); } if (indexPtr_p->isSingle()) { isScalar = True; } } // Check if the value type matches. if (isScalar && !node_p.isScalar()) { throw TableInvExpr ("An array value cannot be used in UPDATE of " " scalar element of column " + columnName_p + " in table " + updTable.tableName()); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/TableParseUpdate.h000066400000000000000000000161351476623553700207430ustar00rootroot00000000000000//# TableParseUpdate.h: Class to manage TaQL UPDATE command handling //# Copyright (C) 1994-2022 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABLEPARSEUPDATE_H #define TABLES_TABLEPARSEUPDATE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableExprNodeSet; class TableExprNodeIndex; class TaQLStyle; class TableExprId; class TableColumn; template class ArrayColumn; // // Manage TaQL UPDATE command handling // // // // // // TableParseUpdate holds the column name, optional indices, optional mask, // and new value expression of a column to be updated. // Furthermore, it has functions to perform updates in the column and possibly // mask column using the expression and possible slice. // class TableParseUpdate { public: TableParseUpdate() : indexPtr_p(0) {} // Construct from a column name and expression. // By default it checks if no aggregate functions are used. TableParseUpdate (const String& columnName, const String& columnNameMask, const TableExprNode&, Bool checkAggr=True); // Construct from a column name, subscripts or mask, and expression. // It checks if no aggregate functions are used. TableParseUpdate (const String& columnName, const String& columnNameMask, const TableExprNodeSet& indices, const TableExprNode&, const TaQLStyle&); // Construct from a column name, subscripts and mask, and expression. // It checks if no aggregate functions are used. // It checks if one of the indices represents subscripts, the other a mask. TableParseUpdate (const String& columnName, const String& columnNameMask, const TableExprNodeSet& indices1, const TableExprNodeSet& indices2, const TableExprNode&, const TaQLStyle&); // Handle the subscripts or mask. // It checks if subscripts or mask is not already used. void handleIndices (const TableExprNodeSet& indices, const TaQLStyle& style); // Set the node expression (used by TableParseQuery::doInsert). void setNode (const TableExprNode& node) { node_p = node; } // Set the column name. void setColumnName (const String& name) { columnName_p = name; } // Set the column name for the mask. void setColumnNameMask (const String& name) { columnNameMask_p = name; } // Get the column name. const String& columnName() const { return columnName_p; } // Get the possible column name for the mask. const String& columnNameMask() const { return columnNameMask_p; } // Adapt the possible unit of the expression to the possible unit // of the column. void adaptUnit (const Unit& columnUnit) { node_p.adaptUnit (columnUnit); } // Check if the update column and expression specifications match. // Also check if the number of rows is correct. void check (const Table& origTable, const Table& updTable) const; // Update the values in the column with the values of the node_p expression. // The column can contain scalars of arrays. Possible only array slices // are updated. // The mask column values are also updated if a mask is used. void updateColumn (TableColumn& col, ArrayColumn& maskCol, rownr_t row, const TableExprId& rowid); private: // Update the values in the columns (helpers of updateColumn). // It converts the data type of the expression to that opf the column. // template void updateValue (rownr_t row, const TableExprId& rowid, Bool isScalarCol, const TableExprNode& node, const Array& mask, TableColumn& col, const Slicer* slicerPtr, ArrayColumn& maskCol); template void updateScalar (rownr_t row, const TableExprId& rowid, const TableExprNode& node, TableColumn& col); template void updateArray (rownr_t row, const TableExprId& rowid, const TableExprNode& node, const Array& res, ArrayColumn& col); template void updateSlice (rownr_t row, const TableExprId& rowid, const TableExprNode& node, const Array& res, const Slicer& slice, ArrayColumn& col); template void copyMaskedValue (rownr_t row, ArrayColumn& acol, const Slicer* slicerPtr, const TNODE* val, size_t incr, const Array& mask); Array makeMaskSlice (const Array& mask, const IPosition& shapeCol, const Slicer* slicerPtr); void checkMaskColumn (Bool hasMask, const ArrayColumn& maskCol, const TableColumn& col); // //# Data members String columnName_p; String columnNameMask_p; Bool maskFirst_p; //# True = mask is given before slice TableExprNodeIndex* indexPtr_p; //# copy of pointer in indexNode_p; no need to delete TableExprNode indexNode_p; TableExprNode mask_p; TableExprNode node_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/TableParseUtil.cc000066400000000000000000000555461476623553700206050ustar00rootroot00000000000000//# TableParseUtil.cc: Convenience functions for TableParse classes //# Copyright (C) 1994-2022 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN namespace TableParseUtil { // Handle a table name and create a Table object for it as needed. // This is quite complex, because a table name can be given in many ways: // 1. an ordinary name such as 'my.tab' or '../my.tab'. // 2. a wildcarded name (for table concatenation) such as 'my*.tab'. Note that for // such a case the alwaysOpen=False, so no Table object is created. // 3. a table number in the temporary table list such as $1 // 4. a shorthand referring to another table at this or a higher query level // 5. :: or . indicating the first available table at this or a higher query level // 6. a Table object resulting from a nested query // 7. a subtable indicated by a keyword such as tabspec::sub or tabspec::sub1::sub2 // where tabspec can be a table name as in 1, 3, 4 or 5 above. // - the subtable can be a table keyword as above, but also a column keyword // such as shorthand.column::key. Note that a column can only be given after // a shorthand to distinguish it from an ordinary table name. // The first part before a . is tried as a shorthand and can be empty indicating // the first available table as in 5. // - keywords can be nested thus tab::key1.key2.key3 // It means that sh.col::a1.a2.s1::b1.s2::c1.c2.c3.s4 is a valid specification // and indicates subtable s4 in subtable s3 in subtable s1 using nested // keywords in column col. But this example is very esoteric. // In practice column keywords and nested keywords will hardly ever be used, // so usually something like my.ms::ANTENNA is the only 'complicated' spec used. // Note that splitting on a dot as in option 7) is done after trying that part as an // ordinary table name to handle .. in a case such as '../my.tab::SUB' correctly. Table getTable (Int tabnr, const String& name, const Table& ftab, const std::vector& tempTables, const std::vector& stack, Bool alwaysOpen) { // A table from a nested query. if (! ftab.isNull()) { return ftab; } // Split the name into its subtable parts using :: as separator. Table table; uInt stSub = 0; uInt stPart = 0; Vector subs = stringToVector(name, std::regex("::")); // No part, except first one, can be empty (unless :: is given). if (name != "::") { if (subs.size() == 0 || (subs.size() > 1 && anyEQ(subs(Slice(1, subs.size()-1)), String()))) { throw TableInvExpr("'"+ name + "' is an invalid table name specification"); } } // Split the first subtable name into parts using a dot as separator. // The first part can be empty, a shorthand or a temporary table number. // An empty part means that an ordinary table name such as ../tab might be given. // If not, it must indicate the first available table in a previous query. stPart = 1; // indicate first part is handled. Vector parts = stringToVector(subs[0], '.'); if (parts.size() == 0 || parts[0].empty()) { if (! subs[0].empty()) { if (Table::isReadable (subs[0])) { table = Table(subs[0]); stSub = 1; stPart = 0; } } if (table.isNull()) { // Not a table name. Try it as first table in previous query. table = TableParseTableList::findTable (String(), True, stack).table(); if (table.isNull()) { throw TableInvExpr(":: or . is invalid in table name " + name + ": no previous table available"); } } } else { if (tabnr >= 0) { // Temporary table number (1-based) given. if (tabnr < 1 || tabnr > Int(tempTables.size()) || tempTables[tabnr-1] == 0) { throw (TableInvExpr ("Invalid temporary table number given in " + name)); } table = *(tempTables[tabnr-1]); } else { // See if the first part is a shorthand. table = TableParseTableList::findTable (parts[0], True, stack).table(); if (table.isNull()) { // It was not something like shorthand.column, thus try as a full name. // However, do not open if alwaysOpen=False. // In that case the table is opened when needed // (because the name can contain a wildcard as well). if (!alwaysOpen) { return table; } stPart = 0; stSub = 1; table = Table (subs[0]); if (table.isNull()) { throw TableInvExpr("Table " + subs[0] + " is unknown"); } } } } // Okay; we have found the first table. AlwaysAssert (!table.isNull(), AipsError); // Now process all parts in all subtable names, where the first name or // first part might need to be skipped because already processed. const TableRecord* keywords = &(table.keywordSet()); for (uInt k=stSub; k parts = stringToVector(subs[k], '.'); for (uInt p=stPart; pfieldNumber(parts[p]); if (fieldNr < 0) { throw TableInvExpr(parts[p] + " is an unknown keyword/subtable" + (p==1 && kdataType (fieldNr); if (p == parts.size()-1) { if (dtype != TpTable) { throw TableInvExpr(parts[p] + " is no table keyword in " + name); } table = keywords->asTable (fieldNr); keywords = &(table.keywordSet()); } else { if (dtype != TpRecord) { throw TableInvExpr(parts[p] + " is no record keyword in " + name); } keywords = &(keywords->subRecord (fieldNr)); } } } } stPart = 0; } return table; } // This function can split a name. // The name can consist of an optional shorthand, a column or keyword name, // followed by zero or more subfield names (separated by dots). // In the future it should also be possible to have a subfield name // followed by a keyword name, etc. to cater for something like: // shorthand::key.subtable::key.subsubtable::key. // If that gets possible, TableGram.ll should also be changed to accept // such a string in the scanner. // It is a question whether :: should be part of the scanner or grammar. // For columns one can go a bit further by accepting something like: // col.subtable[select expression resulting in scalar] // which is something for the far away future. Bool splitName (String& shorthand, String& columnName, Vector& fieldNames, const String& name, Bool checkError, Bool isKeyword, Bool allowNoKey) { //# Make a copy, because some String functions are non-const. //# Usually the name consists of a columnName only, so use that. //# A keyword is given if :: is part of the name or if isKeyword is set. shorthand = ""; columnName = name; String restName; Bool isKey = isKeyword; int j = columnName.index("::"); Vector fldNam; uInt stfld = 0; if (j >= 0) { // The name contains ::, thus represents a keyword name. isKey = True; } else if (isKey) { // It is a keyword, but no ::. j = -2; } if (isKey) { // There should be something after the :: // which can be multiple names separated by dots. // They represent the keyword name and possible subfields in case // the keyword is a record. restName = columnName.after(j+1); if (!allowNoKey && restName.empty()) { if (checkError) { throw (TableInvExpr ("No keyword given in name " + name)); } return False; } fldNam = stringToVector (restName, '.'); // The part before the :: can be empty, an optional shorthand, // and an optional column name (separated by a dot). if (j <= 0) { columnName = ""; } else { Vector scNames = stringToVector(columnName.before(j), '.'); switch (scNames.size()) { case 2: shorthand = scNames(0); columnName = scNames(1); break; case 1: columnName = scNames(0); break; default: if (checkError) { throw TableInvExpr ("Name " + name + " is invalid: More" " than 2 name parts given before ::"); } return False; } } } else { // The name is a column name optionally preceeded by a shorthand // and optionally followed by subfields in case the column contains // records. The separator is a dot. // A name like a.b is in principle ambiguous because: // - it can be shorthand.column // - it can be column.subfield // It is assumed to be a shorthand. // Users can use column.subfield by preceeding it with a dot // (.a.b always means column.subfield). fldNam = stringToVector (columnName, '.'); if (fldNam.size() == 1) { stfld = 0; // one part simply means column } else if (fldNam(0).empty()) { stfld = 1; // .column was used } else { shorthand = fldNam(0); // a known shorthand is used stfld = 1; } columnName = fldNam(stfld++); if (columnName.empty()) { if (checkError) { throw (TableInvExpr ("No column given in name " + name)); } return False; } } fieldNames.resize (fldNam.size() - stfld); for (uInt i=stfld; i& tempTables, const std::vector& stack) { // Remove ::subtableName from the full table name to get the parent's name. String tableName (fullName.substr(0, fullName.size() - subTableName.size() - 2)); // Open the parent table. Table parent = getTable (-1, tableName, Table(), tempTables, stack, True); // Create the subtable and define the keyword in the parent referring it. String parentName = parent.tableName(); if (parentName.empty()) { throw TableInvExpr("Parent table in " + fullName + " seems to be transient"); } return parent; } void setRecFld (RecordInterface& rec, const String& name, const String& dtype, const ValueHolder& vh) { String type = getTypeString (dtype, vh.dataType()); if (isScalar(vh.dataType())) { if (type == "B") { rec.define (name, vh.asBool()); } else if (type == "U1") { rec.define (name, vh.asuChar()); } else if (type == "U4") { rec.define (name, vh.asuInt()); } else if (type == "I2") { rec.define (name, vh.asShort()); } else if (type == "I4") { rec.define (name, vh.asInt()); } else if (type == "I8") { rec.define (name, vh.asInt64()); } else if (type == "R4") { rec.define (name, vh.asFloat()); } else if (type == "R8") { rec.define (name, vh.asDouble()); } else if (type == "C4") { rec.define (name, vh.asComplex()); } else if (type == "C8") { rec.define (name, vh.asDComplex()); } else if (type == "S") { rec.define (name, vh.asString()); } else { throw TableInvExpr ("TableParse::setRecFld - " "unknown data type " + type); } } else { if (type == "B") { rec.define (name, vh.asArrayBool()); } else if (type == "U1") { rec.define (name, vh.asArrayuChar()); } else if (type == "U4") { rec.define (name, vh.asArrayuInt()); } else if (type == "I2") { rec.define (name, vh.asArrayShort()); } else if (type == "I4") { rec.define (name, vh.asArrayInt()); } else if (type == "I8") { rec.define (name, vh.asArrayInt64()); } else if (type == "R4") { rec.define (name, vh.asArrayFloat()); } else if (type == "R8") { rec.define (name, vh.asArrayDouble()); } else if (type == "C4") { rec.define (name, vh.asArrayComplex()); } else if (type == "C8") { rec.define (name, vh.asArrayDComplex()); } else if (type == "S") { rec.define (name, vh.asArrayString()); } else { throw TableInvExpr ("TableParse::setRecFld - " "unknown data type " + type); } } } String getTypeString (const String& typeStr, DataType type) { String out = typeStr; if (out.empty()) { switch (type) { case TpBool: case TpArrayBool: out = "B"; break; case TpUChar: case TpArrayUChar: out = "U1"; break; case TpUShort: case TpArrayUShort: out = "U2"; // github.com/ICRAR/skuareview break; case TpUInt: case TpArrayUInt: out = "U4"; break; case TpShort: case TpArrayShort: out = "I2"; break; case TpInt: case TpArrayInt: out = "I4"; break; case TpInt64: case TpArrayInt64: out = "I8"; break; case TpFloat: case TpArrayFloat: out = "R4"; break; case TpDouble: case TpArrayDouble: out = "R8"; break; case TpComplex: case TpArrayComplex: out = "C4"; break; case TpDComplex: case TpArrayDComplex: out = "C8"; break; case TpString: case TpArrayString: out = "S"; break; default: throw TableInvExpr ("TableParse::getTypeString - " "value has an unknown data type " + String::toString(type)); } } return out; } Block getStoredColumns (const Table& tab) { Block names; const TableDesc& tdesc = tab.tableDesc(); for (uInt i=0; i (ScalarColumn(tabcol).getColumn()); break; case TpUChar: tsnptr = std::make_shared (ScalarColumn(tabcol).getColumn()); break; case TpShort: tsnptr = std::make_shared (ScalarColumn(tabcol).getColumn()); break; case TpUShort: tsnptr = std::make_shared (ScalarColumn(tabcol).getColumn()); break; case TpInt: tsnptr = std::make_shared (ScalarColumn(tabcol).getColumn()); break; case TpUInt: tsnptr = std::make_shared (ScalarColumn(tabcol).getColumn()); break; case TpInt64: tsnptr = std::make_shared (ScalarColumn(tabcol).getColumn()); break; case TpFloat: tsnptr = std::make_shared (ScalarColumn(tabcol).getColumn()); break; case TpDouble: tsnptr = std::make_shared (ScalarColumn(tabcol).getColumn()); break; case TpComplex: tsnptr = std::make_shared (ScalarColumn(tabcol).getColumn()); break; case TpDComplex: tsnptr = std::make_shared (ScalarColumn(tabcol).getColumn()); break; case TpString: tsnptr = std::make_shared (ScalarColumn(tabcol).getColumn()); break; default: throw (TableInvExpr ("Nested query column " + colDesc.name() + " has unknown data type")); } } else { switch (colDesc.dataType()) { case TpBool: tsnptr = std::make_shared (ArrayColumn(tabcol).getColumn()); break; case TpUChar: tsnptr = std::make_shared (ArrayColumn(tabcol).getColumn()); break; case TpShort: tsnptr = std::make_shared (ArrayColumn(tabcol).getColumn()); break; case TpUShort: tsnptr = std::make_shared (ArrayColumn(tabcol).getColumn()); break; case TpInt: tsnptr = std::make_shared (ArrayColumn(tabcol).getColumn()); break; case TpUInt: tsnptr = std::make_shared (ArrayColumn(tabcol).getColumn()); break; case TpInt64: tsnptr = std::make_shared (ArrayColumn(tabcol).getColumn()); break; case TpFloat: tsnptr = std::make_shared (ArrayColumn(tabcol).getColumn()); break; case TpDouble: tsnptr = std::make_shared (ArrayColumn(tabcol).getColumn()); break; case TpComplex: tsnptr = std::make_shared (ArrayColumn(tabcol).getColumn()); break; case TpDComplex: tsnptr = std::make_shared (ArrayColumn(tabcol).getColumn()); break; case TpString: tsnptr = std::make_shared (ArrayColumn(tabcol).getColumn()); break; default: throw (TableInvExpr ("Nested query column " + colDesc.name() + " has unknown data type")); } } //# Fill in the column unit (if defined). tsnptr->setUnit (TableExprNodeColumn::getColumnUnit (tabcol)); return tsnptr; } } // end namespace TableParseUtil } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/TaQL/TableParseUtil.h000066400000000000000000000112611476623553700204310ustar00rootroot00000000000000//# TableParseUtil.h: Convenience functions for TableParse classes //# Copyright (C) 1994-2022 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABLEPARSEUTIL_H #define TABLES_TABLEPARSEUTIL_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward declarations class TableParseQuery; // // Convenience functions for TableParse classes // // // // // // This file contains several static helper functions for TableParse. // They handle splitting a name into its shorthand, column and/or keyword // parts. A name can be given as // shorthand.column::key.subkey1.subkey2... // where each part is optional. // It also has functions to find the given table, column and keyword. // namespace TableParseUtil { // Make a Table object for given name, seqnr or so. // If alwaysOpen=False the table will only be looked up, // but not opened if not found. This is meant for concatenated tables // in TaQLNodeHandler. Table getTable (Int tabnr, const String& name, const Table& ftab, const std::vector& tempTables, const std::vector& stack, Bool alwaysOpen=True); // Open the parent table of a subtable. Table openParentTable (const String& fullName, const String& subTableName, const std::vector& tempTables, const std::vector& stack); // Split a name into its parts (shorthand, column and field names). // A name can be given as // [shorthand][column][::key1.key2.key3...] where the // square brackets indicate optional parts. Note that a single name given // before :: is interpreted as a shorthand unless preceded by a period. //
        True is returned if the name contains a keyword part. // In that case fieldNames contains the keyword name and the possible // subfields. The possible shorthand and the column name are // filled in if it is a column keyword. // If the name contains a column, fieldNames is filled with the subfields // of the column (for the case where the column contains records). //
        If isKeyword is True, the first part of name is a keyword, // even if no :: is given. // If allowNoKey is True, a single :: is allowed, otherwise the name is invalid. // If the name is invalid, exceptions are only thrown if checkError=True. // Otherwise the name is treated as a normal name without keyword. Bool splitName (String& shorthand, String& columnName, Vector& fieldNames, const String& name, Bool checkError, Bool isKeyword, Bool allowNoKey); // Define a field with the given data type in the Record. void setRecFld (RecordInterface& rec, const String& name, const String& dtype, const ValueHolder& vh); // Get the type string. If empty, it is made from the given // data type. String getTypeString (const String& typeStr, DataType type); // Find the names of all stored columns in a table. Block getStoredColumns (const Table& tab); // Make an array from the contents of a column in a subquery. TableExprNode getColSet (const Table& table); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/TaQL/UDFBase.cc000066400000000000000000000212121476623553700171150ustar00rootroot00000000000000//# UDFBase.cc: Abstract base class for a user-defined TaQL function //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include namespace casacore { // Define the static objects. // Use a recursive mutex, because loading from a shared library can cause // a nested lock. map UDFBase::theirRegistry; std::recursive_mutex UDFBase::theirMutex; UDFBase::UDFBase() : itsDataType (TableExprNodeRep::NTAny), itsNDim (-2), itsIsConstant (False), itsIsAggregate (False), itsApplySelection (True) {} UDFBase::~UDFBase() {} void UDFBase::init (const vector& operands, const TableExprInfo& tableInfo, const TaQLStyle& style) { // Link to the operands. itsOperands.resize (operands.size()); for (uInt i=0; i& nodes) { for (uInt i=0; iflattenTree (nodes); } } void UDFBase::setDataType (TableExprNodeRep::NodeDataType dataType) { itsDataType = dataType; } void UDFBase::setNDim (Int ndim) { AlwaysAssert (ndim >= -1, AipsError); if (itsShape.size() > 0) { AlwaysAssert (ndim == Int(itsShape.size()), AipsError); } itsNDim = ndim; } void UDFBase::setShape (const IPosition& shape) { if (itsNDim >= 0) { AlwaysAssert (Int(shape.size()) == itsNDim, AipsError); } itsShape = shape; itsNDim = itsShape.size(); } void UDFBase::setUnit (const String& unit) { itsUnit = unit; } void UDFBase::setAttributes (const Record& attributes) { itsAttributes = attributes; } void UDFBase::setConstant (Bool isConstant) { itsIsConstant = isConstant; } void UDFBase::setAggregate (Bool isAggregate) { itsIsAggregate = isAggregate; } Bool UDFBase::getBool (const TableExprId&) { throw TableInvExpr ("UDFBase::getBool not implemented"); } Int64 UDFBase::getInt (const TableExprId&) { throw TableInvExpr ("UDFBase::getInt not implemented"); } Double UDFBase::getDouble (const TableExprId&) { throw TableInvExpr ("UDFBase::getDouble not implemented"); } DComplex UDFBase::getDComplex (const TableExprId&) { throw TableInvExpr ("UDFBase::getDComplex not implemented"); } String UDFBase::getString (const TableExprId&) { throw TableInvExpr ("UDFBase::getString not implemented"); } TaqlRegex UDFBase::getRegex (const TableExprId&) { throw TableInvExpr ("UDFBase::getRegex not implemented"); } MVTime UDFBase::getDate (const TableExprId&) { throw TableInvExpr ("UDFBase::getDate not implemented"); } MArray UDFBase::getArrayBool (const TableExprId&) { throw TableInvExpr ("UDFBase::getArrayBool not implemented"); } MArray UDFBase::getArrayInt (const TableExprId&) { throw TableInvExpr ("UDFBase::getArrayInt not implemented"); } MArray UDFBase:: getArrayDouble (const TableExprId&) { throw TableInvExpr ("UDFBase::getArrayDouble not implemented"); } MArray UDFBase::getArrayDComplex (const TableExprId&) { throw TableInvExpr ("UDFBase::getArrayDComplex not implemented"); } MArray UDFBase:: getArrayString (const TableExprId&) { throw TableInvExpr ("UDFBase::getArrayString not implemented"); } MArray UDFBase:: getArrayDate (const TableExprId&) { throw TableInvExpr ("UDFBase::getArrayDate not implemented"); } void UDFBase::recreateColumnObjects (const Vector&) {} void UDFBase::applySelection (const Vector& rownrs) { if (itsApplySelection) { recreateColumnObjects (rownrs); // Clear switch in case called for a second time. itsApplySelection = False; } } void UDFBase::registerUDF (const String& name, MakeUDFObject* func) { String fname(name); fname.downcase(); // The library name is the first part. Int j = fname.index('.'); String libname; if (j > 0 && j < Int(fname.size())-1) { libname = fname.substr(0,j); } else { throw TableInvExpr("UDF " + name + " has an invalid name (no dot)"); } std::lock_guard lock(theirMutex); map::iterator iter = theirRegistry.find (fname); if (iter == theirRegistry.end()) { theirRegistry[fname] = func; } else { // Already defined, but allow double definition of the same. if (iter->second != func) { throw TableInvExpr ("User defined TaQL function " + fname + " already exists"); } } // Also register the library with null pointer (if not done yet). // Note that a libname is different from a function name because // it does not contain dots. iter = theirRegistry.find (libname); if (iter == theirRegistry.end()) { theirRegistry[libname] = 0; } } UDFBase* UDFBase::createUDF (const String& name, const TaQLStyle& style) { String fname(name); fname.downcase(); map::iterator iter; { std::lock_guard lock(theirMutex); // Try to find the function. iter = theirRegistry.find (fname); if (iter != theirRegistry.end()) { return iter->second (fname); } } String sfname(fname); // Split name in library and function name. // Require that a . is found and is not the first or last character. Int j = fname.index('.'); String libname; if (j > 0 && j < Int(fname.size())-1) { // Replace a possible synonym for the library name. libname = fname.substr(0,j); libname = style.findSynonym (libname); fname = libname + fname.substr(j); // Try to find the function with the synonym. iter = theirRegistry.find (fname); if (iter != theirRegistry.end()) { return iter->second (fname); } std::lock_guard lock(theirMutex); // See if the library is already loaded. iter = theirRegistry.find (libname); if (iter == theirRegistry.end()) { // Try to load the dynamic library. DynLib dl(libname, string("libcasa_"), CASACORE_STRINGIFY(SOVERSION), "register_"+libname, False); if (dl.getHandle()) { // Add to map to indicate library has been loaded. theirRegistry[libname] = 0; } } // Try to find the function. iter = theirRegistry.find (fname); if (iter != theirRegistry.end()) { return iter->second (fname); } } String unk; if (fname != sfname) { unk = " (=" + fname + ')'; } throw TableInvExpr ("TaQL function " + sfname + unk + " is unknown" + "\n Library " + libname + " was successfully loaded; " "taql 'show func " + libname + "' shows its functions" "\n Maybe check if (DY)LD_LIBRARY_PATH and " "CASACORE_LDPATH match the" " libraries used during the build of " + libname); } } // end namespace casacore-3.7.1/tables/TaQL/UDFBase.h000066400000000000000000000410421476623553700167620ustar00rootroot00000000000000//# UDFBase.h: Abstract base class for a user-defined TaQL function //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_UDFBASE_H #define TABLES_UDFBASE_H //# Includes #include #include #include #include #include #include #include namespace casacore { // // Abstract base class for a user-defined TaQL function // // // // This class makes it possible to add user-defined functions (UDF) to TaQL. // A UDF has to be implemented in a class derived from this class and can // contain one or more user-defined functions. //
        A few functions have to be implemented in the class as described below. // In this way TaQL can be extended with arbitrary functions, which can be // normal functions as well as aggregate functions (often used with GROUPBY). // // A UDF is a class derived from this base class. It must contain the // following member functions. See also the example below. //
      • // // // // // // // // // // // // //
        makeObjecta static function to create an object of the UDF class. This function // needs to be registered. //
        setupthis virtual function is called after the object has been created. // It should initialize the object using the function arguments that // can be obtained using the function operands(). The setup // function should perform the following: //
          //
        • Define the data type of the result using setDataType. // The data type should be derived from the data types of the function // arguments. The possible data types are defined in class // TableExprNodeRep. // Note that a UDF can support multiple data types. For example, a // function like min can be used for Int, Double, or a mix. // Function 'checkDT' in class TableExprNodeMulti can be used to // check the data types of the operands and determine the result // data type. //
        • Define if the function is an aggregate function calculating // an aggregated value in a group (e.g., minimum or mean). // setAggregate can be used to tell so. //
        • Define the dimensionality of the result using setNDim. // A value of 0 means a scalar. A value of -1 means an array with // a dimensionality that can vary from row to row. //
        • Optionally use setShape to define the shape if the // results are arrays with a shape that is the same for all rows. // It will also set ndim if setNDim was not used yet, otherwise // it checks if it ndim matches. //
        • Optionally set the unit of the result using setUnit. // TaQL has full support of units, so UDFs should behave the same. // It is possible to change the unit of the function arguments. // For example: //
            //
          • a function like 'sin' can force its argument to be // in radians; TaQL will scale the argument as needed. This can be // done like // TableExprNodeUnit::adaptUnit (operands()[i], "rad"); //
          • A function like 'asin' will have a result in radians. // Such a UDF should set its result unit to rad. //
          • A function like 'min' wants its arguments to have the same // unit and will set its result unit to it. It can be done like: // setUnit (TableExprFuncNode::makeEqualUnits // (operands(), 0, operands().size())); //
          // See class TableExprFuncNode for more info about these functions. //
        • Optionally define attributes as a Record object. They can be used // by UDFs to tell something more about the type of value. //
        • Optionally define if the result is a constant value using // setConstant. It means that the function is not // dependent on the row number in the table being queried. // This is usually the case if all UDF arguments are constant. //
        //
        getXXXthese are virtual get functions for each possible data type. The // get functions matching the data types set by the setup // function need to be implemented. // The get functions have an argument TableExprId // defining the table row (or record) for which the function has // to be evaluated. // If the UDF is an aggregate functions the TableExprId has to be // upcasted to an TableExprIdAggr object from which all TableExprId // objects in an aggregation group can be retrieved. // // const TableExprIdAggr& aid = TableExprIdAggr::cast (id); // const vector& ids = aid.result().ids(id.rownr()); // //
        // // A UDF has to be made known to TaQL by adding it to the UDF registry with // its name and 'makeObject' function. // UDFs will usually reside in a shared library that is loaded dynamically. // TaQL will load a UDF in the following way: //
          //
        • The UDF name used in TaQL consists of two parts: a library name // and a function name separated by a dot. Both parts need to be given. // Note that the library name can also be seen as a UDF scope, so // different UDFs with equal names can be used from different libraries. // A UDF should be registered with this full name. //
          The "USING STYLE" clause can be used to define a synonym for // a (long) library name in the TaQLStyle object. The library part // of the UDF will always be looked up in this synonym map. //
        • If a UDF is not found in the registry, it will be tried to load // a shared library using the library name part. The libraries tried // to be loaded are lib.so and libcasa_.so. // On Mac .dylib will be tried. If loaded successfully, a special // function 'register_libname' will be called first. It should // register each UDF in the shared library using UDFBase::register. //
        //
        // // // The following examples show a normal UDF function. //
        It returns True if the function argument matches 1. // It can be seen that it checks if the argument is an integer scalar. // // class TestUDF: public UDFBase // { // public: // TestUDF() {} // // Registered function to create the UDF object. // // The name of the function is not important here. // static UDFBase* makeObject (const String&) // { return new TestUDF(); } // // Setup and check the details; result is a bool scalar value. // virtual void setup (const Table&, const TaQLStyle&) // { // AlwaysAssert (operands().size() == 1, AipsError); // AlwaysAssert (operands()[0]->dataType() == TableExprNodeRep::NTInt, // AipsError); // AlwaysAssert (operands()[0]->valueType() == TableExprNodeRep::VTScalar, // AipsError); // setDataType (TableExprNodeRep::NTBool); // setNDim (0); // scalar result // setConstant (operands()[0].isConstant()); // constant result? // } // // Get the value for the given id. // // It gets the value of the operand and checks if it is 1. // Bool getBool (const TableExprId& id) // { return operands()[0]->getInt(id) == 1; } // }; // //
        // // The following example shows an aggregate UDF function. // It calculates the sum of the cubes of the values in a group. // // class TestUDFAggr: public UDFBase // { // public: // TestUDFAggr() {} // // Registered function to create the UDF object. // // The name of the function is not important here. // static UDFBase* makeObject (const String&) { return new TestUDFAggr(); } // // Setup and check the details; result is an integer scalar value. // // It aggregates the values of multiple rows. // virtual void setup (const Table&, const TaQLStyle&) // { // AlwaysAssert (operands().size() == 1, AipsError); // AlwaysAssert (operands()[0]->dataType() == TableExprNodeRep::NTInt, AipsError); // AlwaysAssert (operands()[0]->valueType() == TableExprNodeRep::VTScalar, AipsError); // setDataType (TableExprNodeRep::NTInt); // setNDim (0); // scalar // setAggregate (True); // aggregate function // } // // Get the value of a group. // // It aggregates the values of multiple rows. // Int64 getInt (const TableExprId& id) // { // // Cast the id to a TableExprIdAggr object. // const TableExprIdAggr& aid = TableExprIdAggr::cast (id); // // Get the vector of ids for this group. // const vector& ids = aid.result().ids(id.rownr()); // // Get the values for all ids and accumulate them. // Int64 sum3 = 0; // for (vector::const_iterator it=ids.begin(); // it!=ids.end(); ++it){ // Int64 v = operands()[0]->getInt(*it); // sum3 += v*v*v; // } // return sum3; // } // }; // // // More examples of UDF functions can be found in classes UDFMSCal // and DirectionUDF. class UDFBase { public: // The signature of a global or static member function creating an object // of the UDF. typedef UDFBase* MakeUDFObject (const String& functionName); // Only default constructor is needed. UDFBase(); // Destructor. virtual ~UDFBase(); // Evaluate the function and return the result. // Their default implementations throw a "not implemented" exception. // virtual Bool getBool (const TableExprId& id); virtual Int64 getInt (const TableExprId& id); virtual Double getDouble (const TableExprId& id); virtual DComplex getDComplex (const TableExprId& id); virtual String getString (const TableExprId& id); virtual TaqlRegex getRegex (const TableExprId& id); virtual MVTime getDate (const TableExprId& id); virtual MArray getArrayBool (const TableExprId& id); virtual MArray getArrayInt (const TableExprId& id); virtual MArray getArrayDouble (const TableExprId& id); virtual MArray getArrayDComplex (const TableExprId& id); virtual MArray getArrayString (const TableExprId& id); virtual MArray getArrayDate (const TableExprId& id); // // Get the unit. const String& getUnit() const { return itsUnit; } // Get the attributes. const Record& getAttributes() const { return itsAttributes; } // Flatten the node tree by adding the node and its children to the vector. virtual void flattenTree (std::vector&); private: // Set up the function object. virtual void setup (const Table& table, const TaQLStyle&) = 0; protected: // Get the operands. std::vector& operands() { return itsOperands; } // Set the data type. // This function must be called by the setup function of the derived class. void setDataType (TableExprNodeRep::NodeDataType); // Set the dimensionality of the results. //
        0 means that the results are scalars. //
        -1 means that the results are arrays with unknown dimensionality. //
        >0 means that the results are arrays with that dimensionality. // This function must be called by the setup function of the derived class. void setNDim (Int ndim); // Set the shape of the results if it is fixed and known. void setShape (const IPosition& shape); // Set the unit of the result. // If this function is not called by the setup function of the derived // class, the result has no unit. void setUnit (const String& unit); // Set the attributes of the result. // If this function is not called by the setup function of the derived // class, the result has no attributes. void setAttributes (const Record& attributes); // Define if the result is constant (e.g. if all arguments are constant). // If this function is not called by the setup function of the derived // class, the result is not constant. void setConstant (Bool isConstant); // Define if the UDF is an aggregate function (usually used in GROUPBY). void setAggregate (Bool isAggregate); // Let a derived class recreate its column objects in case a selection // has to be applied. // The default implementation does nothing. virtual void recreateColumnObjects (const Vector& rownrs); public: // Register the name and construction function of a UDF (thread-safe). // An exception is thrown if this name already exists with a different // construction function. static void registerUDF (const String& name, MakeUDFObject* func); // Initialize the function object. void init (const std::vector& arg, const TableExprInfo& tableInfo, const TaQLStyle&); // Get the data type. TableExprNodeRep::NodeDataType dataType() const { return itsDataType; } // Get the dimensionality of the results. // (0=scalar, -1=array with variable ndim, >0=array with fixed ndim Int ndim() const { return itsNDim; } // Get the result shape if the same for all results. const IPosition& shape() const { return itsShape; } // Tell if the UDF gives a constant result. Bool isConstant() const { return itsIsConstant; } // Tell if the UDF is an aggregate function. Bool isAggregate() const { return itsIsAggregate; } // Do not apply the selection. void disableApplySelection() { itsApplySelection = False; } // If needed, let the UDF re-create column objects for a selection of rows. // It calls the function recreateColumnObjects. void applySelection (const Vector& rownrs); // Create a UDF object (thread-safe). // It looks in the map with fixed function names. If unknown, // it looks if a wildcarded function name is supported (for PyTaQL). static UDFBase* createUDF (const String& name, const TaQLStyle& style); private: //# Data members. std::vector itsOperands; TableExprNodeRep::NodeDataType itsDataType; Int itsNDim; IPosition itsShape; String itsUnit; Record itsAttributes; Bool itsIsConstant; Bool itsIsAggregate; Bool itsApplySelection; //# The registry is used for two purposes: //# 1. It is a map of known function names (lib.func) to funcptr. //# Function name * means that the library can contain any function, //# which is intended for python functions (through PyTaQL). //# 2. The loaded libraries are kept in the map (with 0 funcptr). static map theirRegistry; static std::recursive_mutex theirMutex; }; } // end namespace #endif casacore-3.7.1/tables/TaQL/test/000077500000000000000000000000001476623553700163565ustar00rootroot00000000000000casacore-3.7.1/tables/TaQL/test/CMakeLists.txt000066400000000000000000000027061476623553700211230ustar00rootroot00000000000000# Define the table files used in tests. set (datafiles ../../Tables/test/tTable_2.data_v0/table.dat ../../Tables/test/tTable_2.data_v0/table.f0 ../../Tables/test/tTable_2.data_v0/table.f0i0 ../../Tables/test/tTable_2.data_v0/table.f1 ../../Tables/test/tTable_2.data_v0/table.f2 ../../Tables/test/tTable_2.data_v0/table.info ) # Copying is not needed, thus outcommented. #foreach (file ${datafiles}) # configure_file (${CMAKE_CURRENT_SOURCE_DIR}/${file} ${CMAKE_CURRENT_BINARY_DIR}/${file} COPYONLY) #endforeach (file) set (tests tExprGroup tExprGroupArray tExprNode tExprNodeSet tExprNodeSetElem tExprNodeSetOpt tExprUnitNode tExprNodeUDF tMArray tMArrayMath tMArrayUtil tRecordExpr tRecordGram tRecordGramTable tTableExprData tTableGram tTableGramError tTableGramFunc tTaQLNode ) # Only test scripts, no test programs. set (testscripts ttaql tTableGramAlttab tTableGramCretab tTableGramGroupAggr tTableGramGroupAggrAll tTableGramGroupAggrMaskAll tTableGramJoin tTableGramMasked tTableGramNull tTableGramUpdate ) # Some test sources include a test .h file. include_directories ( . ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_tables) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) foreach (test ${testscripts}) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) endforeach (test) casacore-3.7.1/tables/TaQL/test/tExprGroup.cc000066400000000000000000000475761476623553700210270ustar00rootroot00000000000000//# tExprGroup.cc: Test program for the grouping aggregate scalar functions //# Copyright (C) 2013 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include // // Test program for class TableExprAggrNode. // // Keeps track if errors occurred. Bool foundError = False; #define checkFailure(STR,EXPR)\ {\ bool failed = False;\ try {\ TableExprNode n(EXPR);\ } catch (std::exception&) {\ failed = True;\ }\ if (!failed) {\ cout << STR << ": was expected to fail, but did not" << endl;\ }\ } void check (const TableExprNode& expr, const vector& recs, Bool expVal, const String& str) { cout << "Test " << str << endl; // Get the aggregation node. TableExprAggrNode& aggr = const_cast (dynamic_cast(*expr.getRep().get())); std::shared_ptr func = aggr.makeGroupAggrFunc(); for (uInt i=0; iapply (id); } func->finish(); Bool val = func->getBool(); if (val != expVal) { foundError = True; cout << str << ": found value " << val << "; expected " << expVal << endl; } } void check (const TableExprNode& expr, const vector& recs, Int expVal, const String& str) { cout << "Test " << str << endl; // Get the aggregation node. TableExprAggrNode& aggr = const_cast (dynamic_cast(*expr.getRep().get())); std::shared_ptr func = aggr.makeGroupAggrFunc(); for (uInt i=0; iapply (id); } func->finish(); Int val = func->getInt(); if (val != expVal) { foundError = True; cout << str << ": found value " << val << "; expected " << expVal << endl; } } void check (const TableExprNode& expr, const vector& recs, Double expVal, const String& str) { cout << "Test " << str << endl; // Get the aggregation node. TableExprAggrNode& aggr = const_cast (dynamic_cast(*expr.getRep().get())); std::shared_ptr func = aggr.makeGroupAggrFunc(); for (uInt i=0; iapply (id); } func->finish(); Double val = func->getDouble(); if (!near (val, expVal, 1.e-10)) { foundError = True; cout << str << ": found value " << val << "; expected " << expVal << endl; } } void check (const TableExprNode& expr, const vector& recs, const DComplex& expVal, const String& str) { cout << "Test " << str << endl; // Get the aggregation node. TableExprAggrNode& aggr = const_cast (dynamic_cast(*expr.getRep().get())); std::shared_ptr func = aggr.makeGroupAggrFunc(); for (uInt i=0; iapply (id); } func->finish(); DComplex val = func->getDComplex(); if (!near (val, expVal, 1.e-10)) { foundError = True; cout << str << ": found value " << val << "; expected " << expVal << endl; } } void checkLazy (const TableExprNode& expr, const vector& recs, Double expVal, const String& str) { cout << "Test lazy " << str << endl; // Get the aggregation node. TableExprAggrNode& aggr = const_cast (dynamic_cast(*expr.getRep().get())); TableExprGroupExprId funcid(0); for (uInt i=0; i func = aggr.makeGroupAggrFunc(); Double val = func->getDouble (*funcid.getIds()); if (val != expVal) { foundError = True; cout << str << ": found value " << val << "; expected " << expVal << endl; } } void doBool() { // Define a Vector with values. // Use odd length (so median behaves fine). Vector vecb(9); vecb = False; vecb[3] = True; vecb[4] = True; // Define records containing the vector elements. vector recs(vecb.size()); for (uInt i=0; i veci(9); indgen(veci); veci[1]=-4; veci[6] = 20; Vector vecd(9); indgen(vecd); vecd[1]=-4; vecd[6] = 20; // Define records containing the vector elements. vector recs(veci.size()); for (uInt i=0; i vecd(40); indgen(vecd); vecd[1]=-40; vecd[6] = 20; // Define records containing the vector elements. vector recs(vecd.size()); for (uInt i=0; i vecd(40); indgen(vecd, DComplex(0.1,0.2), DComplex(-0.015,0.025)); // Define records containing the vector elements. vector recs(vecd.size()); for (uInt i=0; i vecb(9); vecb = False; vecb[3] = True; vecb[4] = True; // Define two records containing part of the vector. // The aggregate functions will evaluate all reocrds, thus full vector. for (int i=0; i<2; ++i) { vector recs(2); if (i == 0) { recs[0].define ("fld", vecb(Slice(0,5))); recs[1].define ("fld", vecb(Slice(5,4))); } else { recs[0].define ("fld", vecb(Slice(0,2))); recs[1].define ("fld", vecb(Slice(2,7))); } // Form the expression node from the record field. TableExprNode expr = makeRecordExpr (recs[0], "fld"); // Create and check aggregation expressions. check (TableExprNode::newFunctionNode(TableExprFuncNode::gallFUNC, expr), recs, allTrue(vecb), "all"); check (TableExprNode::newFunctionNode(TableExprFuncNode::ganyFUNC, expr), recs, anyTrue(vecb), "any"); check (TableExprNode::newFunctionNode(TableExprFuncNode::gntrueFUNC, expr), recs, Int(ntrue(vecb)), "ntrue"); check (TableExprNode::newFunctionNode(TableExprFuncNode::gnfalseFUNC, expr), recs, Int(nfalse(vecb)), "nfalse"); } } void doIntArr() { // Define a Vector with values. // Use odd length (so median behaves fine). Vector veci(9); indgen(veci); veci[1]=-4; veci[6] = 20; Vector vecd(9); indgen(vecd); vecd[1]=-4; vecd[6] = 20; // Define two records containing part of the vector. // The aggregate functions will evaluate all reocrds, thus full vector. vector recs(2); recs[0].define ("fld", veci(Slice(0,4))); recs[1].define ("fld", veci(Slice(4,5))); // Form the expression node from the record field. TableExprNode expr = makeRecordExpr (recs[0], "fld"); // Create and check aggregation expressions. check (TableExprNode::newFunctionNode(TableExprFuncNode::gminFUNC, expr), recs, min(veci), "minInt"); check (TableExprNode::newFunctionNode(TableExprFuncNode::gmaxFUNC, expr), recs, max(veci), "maxInt"); check (TableExprNode::newFunctionNode(TableExprFuncNode::gsumFUNC, expr), recs, sum(veci), "sumInt"); check (TableExprNode::newFunctionNode(TableExprFuncNode::gproductFUNC, expr), recs, product(veci), "productInt"); check (TableExprNode::newFunctionNode(TableExprFuncNode::gsumsqrFUNC, expr), recs, sum(veci*veci), "sumsqrInt"); check (TableExprNode::newFunctionNode(TableExprFuncNode::gmeanFUNC, expr), recs, mean(vecd), "meanInt"); check (TableExprNode::newFunctionNode(TableExprFuncNode::gvariance0FUNC, expr), recs, pvariance(vecd), "varianceInt"); check (TableExprNode::newFunctionNode(TableExprFuncNode::gvariance1FUNC, expr), recs, variance(vecd), "varianceInt"); check (TableExprNode::newFunctionNode(TableExprFuncNode::gstddev0FUNC, expr), recs, pstddev(vecd), "stddevInt"); check (TableExprNode::newFunctionNode(TableExprFuncNode::gstddev1FUNC, expr), recs, stddev(vecd), "stddevInt"); check (TableExprNode::newFunctionNode(TableExprFuncNode::grmsFUNC, expr), recs, rms(vecd), "rmsInt"); checkLazy (TableExprNode::newFunctionNode(TableExprFuncNode::gmedianFUNC, expr), recs, median(vecd), "medianInt"); checkLazy (TableExprNode::newFunctionNode(TableExprFuncNode::gfractileFUNC, expr, 0.3), recs, fractile(vecd, 0.3), "fractileInt"); } void doDoubleArr() { // Define a Vector with values. // Use odd length (so median behaves fine). Vector vecd(41); indgen(vecd); vecd[1]=-40; vecd[6] = 20; Vector vec2; //# test empty array // Define records containing part of the vector. // The aggregate functions will evaluate all reocrds, thus full vector. vector recs(5); recs[0].define ("fld", vecd(Slice(0,13))); recs[1].define ("fld", vecd(Slice(13,3))); recs[2].define ("fld", vecd(Slice(16,10))); recs[3].define ("fld", vec2); recs[4].define ("fld", vecd(Slice(26,15))); // Form the expression node from the record field. TableExprNode expr = makeRecordExpr (recs[0], "fld"); // Create and check aggregation expressions. check (TableExprNode::newFunctionNode(TableExprFuncNode::gminFUNC, expr), recs, min(vecd), "minDouble"); check (TableExprNode::newFunctionNode(TableExprFuncNode::gmaxFUNC, expr), recs, max(vecd), "maxDouble"); check (TableExprNode::newFunctionNode(TableExprFuncNode::gsumFUNC, expr), recs, sum(vecd), "sumDouble"); check (TableExprNode::newFunctionNode(TableExprFuncNode::gproductFUNC, expr), recs, product(vecd), "productDouble"); check (TableExprNode::newFunctionNode(TableExprFuncNode::gsumsqrFUNC, expr), recs, sum(vecd*vecd), "sumsqrDouble"); check (TableExprNode::newFunctionNode(TableExprFuncNode::gmeanFUNC, expr), recs, mean(vecd), "meanDouble"); check (TableExprNode::newFunctionNode(TableExprFuncNode::gvariance0FUNC, expr), recs, pvariance(vecd), "varianceDouble"); check (TableExprNode::newFunctionNode(TableExprFuncNode::gvariance1FUNC, expr), recs, variance(vecd), "varianceDouble"); check (TableExprNode::newFunctionNode(TableExprFuncNode::gstddev0FUNC, expr), recs, pstddev(vecd), "stddevDouble"); check (TableExprNode::newFunctionNode(TableExprFuncNode::gstddev1FUNC, expr), recs, stddev(vecd), "stddevDouble"); check (TableExprNode::newFunctionNode(TableExprFuncNode::grmsFUNC, expr), recs, rms(vecd), "rmsDouble"); checkLazy (TableExprNode::newFunctionNode(TableExprFuncNode::gmedianFUNC, expr), recs, median(vecd), "medianDouble"); checkLazy (TableExprNode::newFunctionNode(TableExprFuncNode::gfractileFUNC, expr, 0.65), recs, fractile(vecd, 0.65), "fractileDouble"); } void doDComplexArr() { // Define a Vector with values. Vector vecd(40); indgen(vecd, DComplex(0.1,0.2), DComplex(-0.015,0.025)); Vector vec2; //# test empty array // Define records containing part of the vector. // The aggregate functions will evaluate all reocrds, thus full vector. vector recs(5); recs[0].define ("fld", vecd(Slice(0,22))); recs[1].define ("fld", vecd(Slice(22,3))); recs[2].define ("fld", vecd(Slice(25,1))); recs[3].define ("fld", vec2); recs[4].define ("fld", vecd(Slice(26,14))); // Form the expression node from the record field. TableExprNode expr = makeRecordExpr (recs[0], "fld"); // Create and check aggregation expressions. check (TableExprNode::newFunctionNode(TableExprFuncNode::gsumFUNC, expr), recs, sum(vecd), "sumDComplex"); check (TableExprNode::newFunctionNode(TableExprFuncNode::gproductFUNC, expr), recs, product(vecd), "productDComplex"); check (TableExprNode::newFunctionNode(TableExprFuncNode::gsumsqrFUNC, expr), recs, sum(vecd*vecd), "sumsqrDComplex"); check (TableExprNode::newFunctionNode(TableExprFuncNode::gmeanFUNC, expr), recs, mean(vecd), "meanDComplex"); check (TableExprNode::newFunctionNode(TableExprFuncNode::gvariance0FUNC, expr), recs, real(pvariance(vecd)), "varianceDComplex"); check (TableExprNode::newFunctionNode(TableExprFuncNode::gvariance1FUNC, expr), recs, real(variance(vecd)), "varianceDComplex"); check (TableExprNode::newFunctionNode(TableExprFuncNode::gstddev0FUNC, expr), recs, real(pstddev(vecd)), "stddevDComplex"); check (TableExprNode::newFunctionNode(TableExprFuncNode::gstddev1FUNC, expr), recs, real(stddev(vecd)), "stddevDComplex"); } int main() { try { cout << "test Scalar aggregation ..." << endl; doBool(); doInt(); doDouble(); doDComplex(); cout << "test Array aggregation ..." << endl; doBoolArr(); doIntArr(); doDoubleArr(); doDComplexArr(); } catch (std::exception& x) { cout << "Unexpected exception: " << x.what() << endl; return 1; } catch (...) { cout << "Unexpected unknown exception" << endl; return 1; } if (foundError) { cout << "Some unexpected results were found" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/tables/TaQL/test/tExprGroupArray.cc000066400000000000000000000246751476623553700220210ustar00rootroot00000000000000//# tExprGroupArray.cc: Test program for the grouping aggregate array functions //# Copyright (C) 2013 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Test program for class TableExprAggrNodeArray. // // Keeps track if errors occurred. Bool foundError = False; #define checkFailure(STR,EXPR)\ {\ bool failed = False;\ try {\ TableExprNode n(EXPR);\ } catch (std::exception&) {\ failed = True;\ }\ if (!failed) {\ cout << STR << ": was expected to fail, but did not" << endl;\ }\ } void checkLazy (const TableExprNode& expr, const std::vector& recs, const Array& expVal, const String& str) { cout << "Test Bool " << str << endl; // Get the aggregation node. TableExprAggrNodeArray& aggr = const_cast (dynamic_cast(*expr.getRep().get())); TableExprGroupExprId funcid(0); for (uInt i=0; i func = aggr.makeGroupAggrFunc(); MArray val = func->getArrayBool(*funcid.getIds()); if (!allEQ (val.array(), expVal)) { foundError = True; cout << str << ": found value " << val.array() << "; expected " << expVal << endl; } } void checkLazy (const TableExprNode& expr, const std::vector& recs, const Array& expVal, const String& str) { cout << "Test Int " << str << endl; // Get the aggregation node. TableExprAggrNodeArray& aggr = const_cast (dynamic_cast(*expr.getRep().get())); TableExprGroupExprId funcid(0); for (uInt i=0; i> funcSets; funcSets.push_back (std::shared_ptr(new TableExprGroupFuncSet())); std::shared_ptr func = aggr.makeGroupAggrFunc(); funcSets[0]->add (func); MArray val = func->getArrayInt(*funcid.getIds()); if (!allEQ (val.array(), expVal)) { foundError = True; cout << str << ": found value " << val.array() << "; expected " << expVal << endl; } std::vector>> ids(1, funcid.getIds()); std::shared_ptr groupResult(new TableExprGroupResult(funcSets, ids)); TableExprIdAggr aid(groupResult); aid.setRownr (0); MArray val2 = aggr.getArrayInt (aid); if (!allEQ (val2.array(), expVal)) { foundError = True; cout << str << ": found value " << val2.array() << "; expected " << expVal << endl; } } void checkLazy (const TableExprNode& expr, const std::vector& recs, const Array& expVal, const String& str) { cout << "Test Double " << str << endl; // Get the aggregation node. TableExprAggrNodeArray& aggr = const_cast (dynamic_cast(*expr.getRep().get())); TableExprGroupExprId funcid(0); for (uInt i=0; i func = aggr.makeGroupAggrFunc(); MArray val = func->getArrayDouble(*funcid.getIds()); if (!allNear (val.array(), expVal, 1.e-10)) { foundError = True; cout << str << ": found value " << val.array() << "; expected " << expVal << endl; } } void checkLazy (const TableExprNode& expr, const std::vector& recs, const Array& expVal, const String& str) { cout << "Test DComplex " << str << endl; // Get the aggregation node. TableExprAggrNodeArray& aggr = const_cast (dynamic_cast(*expr.getRep().get())); TableExprGroupExprId funcid(0); for (uInt i=0; i func = aggr.makeGroupAggrFunc(); MArray val = func->getArrayDComplex(*funcid.getIds()); if (!allNear (val.array(), expVal, 1.e-10)) { foundError = True; cout << str << ": found value " << val.array() << "; expected " << expVal << endl; } } void checkHist (const TableExprNode& expr, const std::vector& recs, const Array& expVal) { cout << "Test Double ghist " << endl; // Get the aggregation node. TableExprAggrNodeArray& aggr = const_cast (dynamic_cast(*expr.getRep().get())); std::shared_ptr func = aggr.makeGroupAggrFunc(); for (uInt i=0; iapply (id); } func->finish(); MArray val = func->getArrayInt(std::vector()); if (!allEQ (val.array(), expVal)) { foundError = True; cout << "ghist: found value " << val.array() << "; expected " << expVal << endl; } } void doBoolArr() { // Define an Array with values. Cube arr(2,3,4); arr = False; arr(0,1,3) = True; arr(0,2,2) = True; // Define records containing equal parts of the array. std::vector recs(arr.shape()[2]); MatrixIterator iter(arr); int i=0; while (!iter.pastEnd()) { recs[i++].define ("fld", iter.matrix()); iter.next(); } // Form the expression node from the record field. TableExprNode expr = makeRecordExpr (recs[0], "fld"); checkLazy (TableExprNode::newFunctionNode(TableExprFuncNode::gaggrFUNC, expr), recs, arr, "gaggr"); } void doIntArr() { // Define an Array with values. Cube arr(20,30,40); indgen (arr); // Define records containing equal parts of the array. std::vector recs(arr.shape()[2]); MatrixIterator iter(arr); int i=0; while (!iter.pastEnd()) { recs[i++].define ("fld", iter.matrix()); iter.next(); } // Form the expression node from the record field. TableExprNode expr = makeRecordExpr (recs[0], "fld"); checkLazy (TableExprNode::newFunctionNode(TableExprFuncNode::gaggrFUNC, expr), recs, arr, "gaggr"); } void doDoubleArr() { // Define an Array with values. Cube arr(5,3,1); indgen (arr, 10., 2.); // Define records containing equal parts of the array. std::vector recs(arr.shape()[2]); MatrixIterator iter(arr); int i=0; while (!iter.pastEnd()) { recs[i++].define ("fld", iter.matrix()); iter.next(); } // Form the expression node from the record field. TableExprNode expr = makeRecordExpr (recs[0], "fld"); checkLazy (TableExprNode::newFunctionNode(TableExprFuncNode::gaggrFUNC, expr), recs, arr, "gaggr"); // Do a test of the histogram function (8 bins between 12 and 36). Vector hist(10, 0); for (uInt i=0; i 36) { hist[9]++; } else { hist[1 + int((v-12)/3)]++; } } TableExprNodeSet set; set.add (TableExprNodeSetElem(expr)); set.add (TableExprNodeSetElem(8)); set.add (TableExprNodeSetElem(12.)); set.add (TableExprNodeSetElem(36.)); checkHist (TableExprNode::newFunctionNode(TableExprFuncNode::ghistFUNC, set, TableExprInfo()), recs, hist); } void doDComplexArr() { // Define an Array with values. Cube arr(5,3,8); indgen (arr, DComplex(0.1, -0.3), DComplex(-1.3, 3.5)); // Define records containing equal parts of the array. std::vector recs(arr.shape()[2]); MatrixIterator iter(arr); int i=0; while (!iter.pastEnd()) { recs[i++].define ("fld", iter.matrix()); iter.next(); } // Form the expression node from the record field. TableExprNode expr = makeRecordExpr (recs[0], "fld"); checkLazy (TableExprNode::newFunctionNode(TableExprFuncNode::gaggrFUNC, expr), recs, arr, "gaggr"); } int main() { try { doBoolArr(); doIntArr(); doDoubleArr(); doDComplexArr(); } catch (std::exception& x) { cout << "Unexpected exception: " << x.what() << endl; return 1; } catch (...) { cout << "Unexpected unknown exception" << endl; return 1; } if (foundError) { cout << "Some unexpected results were found" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/tables/TaQL/test/tExprNode.cc000066400000000000000000001550471476623553700206110ustar00rootroot00000000000000//# tExprNode.cc: Test program for the selection classes //# Copyright (C) 2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Test program for class TableExprNode. // Bool foundError = False; void checkScaBool (const String& str, TableExprId& exprid, const TableExprNode& expr, const Bool& value) { cout << "checkScaBool " << str << endl; AlwaysAssertExit (expr.dataType() == TpBool); Bool val; expr.get (exprid, val); if (val != value) { foundError = True; cout << str << ": found value " << val << "; expected " << value << endl; } } void checkScaInt (const String& str, TableExprId& exprid, const TableExprNode& expr, const Int& value) { cout << "checkScaInt " << str << endl; AlwaysAssertExit (expr.dataType() == TpInt64); Int64 val; expr.get (exprid, val); if (val != value) { foundError = True; cout << str << ": found value " << val << "; expected " << value << endl; } } void checkScaDouble (const String& str, TableExprId& exprid, const TableExprNode& expr, const Double& value) { cout << "checkScaDouble " << str << endl; AlwaysAssertExit (expr.dataType() == TpDouble); Double val; expr.get (exprid, val); if (!near (val, value, 1.e-10)) { foundError = True; cout << str << ": found value " << val << "; expected " << value << endl; } } void checkScaDComplex (const String& str, TableExprId& exprid, const TableExprNode& expr, const DComplex& value) { cout << "checkScaDComplex " << str << endl; AlwaysAssertExit (expr.dataType() == TpDComplex); DComplex val; expr.get (exprid, val); if (!near (val, value, 1.e-10)) { foundError = True; cout << str << ": found value " << val << "; expected " << value << endl; } } void checkScaString (const String& str, TableExprId& exprid, const TableExprNode& expr, const String& value) { cout << "checkScaString " << str << endl; AlwaysAssertExit (expr.dataType() == TpString); String val; expr.get (exprid, val); if (val != value) { foundError = True; cout << str << ": found value " << val << "; expected " << value << endl; } } void checkScaDate (const String& str, TableExprId& exprid, const TableExprNode& expr, const MVTime& value) { cout << "checkScaDate " << str << endl; AlwaysAssertExit (expr.dataType() == TpQuantity); MVTime val; expr.get (exprid, val); if (!near (Double(val), Double(value), 1.e-10)) { foundError = True; cout << str << ": found value " << Double(val) << "; expected " << Double(value) << endl; } } void checkArrBool (const String& str, TableExprId& exprid, const TableExprNode& expr, const Array& value) { cout << "checkArrBool " << str << endl; AlwaysAssertExit (expr.dataType() == TpBool); MArray val; expr.get (exprid, val); if (! allEQ (val.array(), value)) { foundError = True; cout << str << ": found value " << val.array() << "; expected " << value << endl; } } void checkArrInt (const String& str, TableExprId& exprid, const TableExprNode& expr, const Array& value) { cout << "checkArrInt " << str << endl; AlwaysAssertExit (expr.dataType() == TpInt64); MArray val64; expr.get (exprid, val64); Array val(val64.shape()); convertArray (val, val64.array()); if (! allEQ (val, value)) { foundError = True; cout << str << ": found value " << val << "; expected " << value << endl; } } void checkArrDouble (const String& str, TableExprId& exprid, const TableExprNode& expr, const Array& value) { cout << "checkArrDouble " << str << endl; AlwaysAssertExit (expr.dataType() == TpDouble); MArray val; expr.get (exprid, val); if (! allNear (val.array(), value, 1.e-10)) { foundError = True; cout << str << ": found value " << val.array() << "; expected " << value << endl; } } void checkArrDComplex (const String& str, TableExprId& exprid, const TableExprNode& expr, const Array& value) { cout << "checkArrDComplex " << str << endl; AlwaysAssertExit (expr.dataType() == TpDComplex); MArray val; expr.get (exprid, val); if (! allNear (val.array(), value, 1.e-10)) { foundError = True; cout << str << ": found value " << val.array() << "; expected " << value << endl; } } void checkArrString (const String& str, TableExprId& exprid, const TableExprNode& expr, const Array& value) { cout << "checkArrString " << str << endl; AlwaysAssertExit (expr.dataType() == TpString); MArray val; expr.get (exprid, val); if (! allEQ (val.array(), value)) { foundError = True; cout << str << ": found value " << val.array() << "; expected " << value << endl; } } #define checkFailure(STR,EXPR)\ {\ bool failed = False;\ try {\ TableExprNode n(EXPR);\ } catch (std::exception&) {\ failed = True;\ }\ if (!failed) {\ cout << STR << ": was expected to fail, but did not" << endl;\ }\ } void doIt() { // Define various arrays for the record. // Define arrays for various data types with the same values, // so they can be used when checking results. IPosition shp(2,4,5); Vector shpVec(2); convertArray (shpVec, shp.asVector()); Matrix arrb1(shp); arrb1 = True; arrb1(2,3) = False; Matrix arrb2(shp); arrb2 = True; arrb2(2,3) = False; arrb2(1,4) = False; Matrix arrbi(shp); arrbi=0; arrbi(2,3) = 1; Matrix arrbd(shp); arrbd=1.; arrbd(2,3)=0.; Matrix arri1(shp); Matrix arrid1(shp); Matrix arriz1(shp); indgen (arri1, -1); indgen (arrid1, -1.); indgen (arriz1, DComplex(-1.,0)); Matrix arrisign(shp); Matrix arridsign(shp); Matrix arridarg(shp); arrisign = 1; arrisign.data()[0] = -1; convertArray (arridarg, arrisign); arridarg = (arridarg-1.)*C::pi/-2.; arrisign.data()[1] = 0; convertArray (arridsign, arrisign); Matrix arri2(shp); Matrix arrid2(shp); Matrix arriz2(shp); indgen (arri2, 100, 2); indgen (arrid2, 100., 2.); indgen (arriz2, DComplex(100.,0.), DComplex(2.,0.)); Matrix arrd1(shp); Matrix arrdz1(shp); Matrix arrdsign(shp); Matrix arrdi1(shp); indgen (arrd1, -222.1, 20.); indgen (arrdz1, DComplex(-222.1,0.), DComplex(20.,0.)); arrdsign = 1.; arrdsign(IPosition(2,0,0),IPosition(2,3,2)) = -1.; convertArray (arrdi1, arrd1); Matrix arrdzero(shp); arrdzero = 0.; Matrix arrd2(shp); Matrix arrdz2(shp); indgen (arrd2, 300., 2.); indgen (arrdz2, DComplex(300.,0.), DComplex(2.,0.)); Matrix arrc1(shp); Matrix arrcz1(shp); indgen (arrc1, Complex(1,2), Complex(1,1)); indgen (arrcz1, DComplex(1,2), DComplex(1,1)); Matrix arrc2(shp); Matrix arrcz2(shp); indgen (arrc2, Complex(100,200), Complex(2,2)); indgen (arrcz2, DComplex(100,200), DComplex(2,2)); Matrix arrz1(shp); indgen (arrz1, DComplex(-222,-333), DComplex(20,30)); Matrix arrz2(shp); indgen (arrz2, DComplex(300,400), DComplex(2,2)); Vector arrs1 = stringToVector("a1, a12, a123, a1234"); AlwaysAssertExit (arrs1.size() == 4); Vector arrs2 = stringToVector("s1, s12, s123, s1234"); Matrix mats1(shp); for (Int i=0; i sd1arr(shp); sd1arr = sd1; DComplex sdz1(sd1); Array sdz1arr(shp); sdz1arr = sdz1; Double sd2 = -4; Array sd2arr(shp); sd2arr = sd2; DComplex sdz2(sd2); Array sdz2arr(shp); sdz2arr = sdz2; Complex sc1(1,2); DComplex scz1(sc1); Complex sc2(3,4); DComplex scz2(sc2); DComplex sz1(4,5); DComplex sz2(5,6); Array sz1arr(shp); Array sz2arr(shp); sz1arr = sz1; sz2arr = sz2; String ss1 ("ss1"); String ss2 ("ss2"); // Define all kind of fields in a record. // These fields are used for the selection. Record rec; rec.define ("sb1", sb1); rec.define ("sb2", sb2); rec.define ("si1", si1); rec.define ("si2", si2); rec.define ("sd1", sd1); rec.define ("sd2", sd2); rec.define ("sc1", sc1); rec.define ("sc2", sc2); rec.define ("sz1", sz1); rec.define ("sz2", sz2); rec.define ("ss1", ss1); rec.define ("ss2", ss2); rec.define ("arrb1", arrb1); rec.define ("arrb2", arrb2); rec.define ("arri1", arri1); rec.define ("arri2", arri2); rec.define ("arrd1", arrd1); rec.define ("arrd2", arrd2); rec.define ("arrc1", arrc1); rec.define ("arrc2", arrc2); rec.define ("arrz1", arrz1); rec.define ("arrz2", arrz2); rec.define ("arrs1", arrs1); rec.define ("arrs2", arrs2); // Now form expression nodes from the record fields. TableExprNode esb1 = makeRecordExpr (rec, "sb1"); TableExprNode esb2 = makeRecordExpr (rec, "sb2"); TableExprNode esi1 = makeRecordExpr (rec, "si1"); TableExprNode esi2 = makeRecordExpr (rec, "si2"); TableExprNode esd1 = makeRecordExpr (rec, "sd1"); TableExprNode esd2 = makeRecordExpr (rec, "sd2"); TableExprNode esc1 = makeRecordExpr (rec, "sc1"); TableExprNode esc2 = makeRecordExpr (rec, "sc2"); TableExprNode esz1 = makeRecordExpr (rec, "sz1"); TableExprNode esz2 = makeRecordExpr (rec, "sz2"); TableExprNode ess1 = makeRecordExpr (rec, "ss1"); TableExprNode ess2 = makeRecordExpr (rec, "ss2"); TableExprNode earrb1 = makeRecordExpr (rec, "arrb1"); TableExprNode earrb2 = makeRecordExpr (rec, "arrb2"); TableExprNode earri1 = makeRecordExpr (rec, "arri1"); TableExprNode earri2 = makeRecordExpr (rec, "arri2"); TableExprNode earrd1 = makeRecordExpr (rec, "arrd1"); TableExprNode earrd2 = makeRecordExpr (rec, "arrd2"); TableExprNode earrc1 = makeRecordExpr (rec, "arrc1"); TableExprNode earrc2 = makeRecordExpr (rec, "arrc2"); TableExprNode earrz1 = makeRecordExpr (rec, "arrz1"); TableExprNode earrz2 = makeRecordExpr (rec, "arrz2"); TableExprNode earrs1 = makeRecordExpr (rec, "arrs1"); TableExprNode earrs2 = makeRecordExpr (rec, "arrs2"); // Use the record as the expression id. TableExprId exprid(rec); // Check if getting the data is correct. checkScaBool ("sb1", exprid, esb1, sb1); checkScaBool ("sb2", exprid, esb2, sb2); checkScaInt ("si1", exprid, esi1, si1); checkScaInt ("si2", exprid, esi2, si2); checkScaDouble ("sd1", exprid, esd1, sd1); checkScaDouble ("sd2", exprid, esd2, sd2); checkScaDComplex ("sz1", exprid, esz1, sz1); checkScaDComplex ("sz2", exprid, esz2, sz2); checkScaDComplex ("sc1", exprid, esc1, scz1); checkScaDComplex ("sc2", exprid, esc2, scz2); checkScaString ("ss1", exprid, ess1, ss1); checkScaString ("ss2", exprid, ess2, ss2); checkArrBool ("ab1", exprid, earrb1, arrb1); checkArrBool ("ab2", exprid, earrb2, arrb2); checkArrInt ("ai1", exprid, earri1, arri1); checkArrInt ("ai2", exprid, earri2, arri2); checkArrDouble ("ad1", exprid, earrd1, arrd1); checkArrDouble ("ad2", exprid, earrd2, arrd2); checkArrDComplex ("az1", exprid, earrz1, arrz1); checkArrDComplex ("az2", exprid, earrz2, arrz2); checkArrDComplex ("ac1", exprid, earrc1, arrcz1); checkArrDComplex ("ac2", exprid, earrc2, arrcz2); checkArrString ("as1", exprid, earrs1, arrs1); checkArrString ("as2", exprid, earrs2, arrs2); // Check the logical operators. // Form combinations of scalars and arrays (and constants). checkScaBool ("|| sb-sb", exprid, esb1||esb2, sb1||sb2); checkScaBool ("&& sb-sb", exprid, esb1&&esb2, sb1&&sb2); checkScaBool ("&& sb", exprid, esb1&&True, sb1&&True); checkScaBool ("! sb1", exprid, !esb1, !sb1); checkScaBool ("! sb2", exprid, !esb2, !sb2); checkArrBool ("|| ab-ab", exprid, earrb1||earrb2, arrb1||arrb2); checkArrBool ("|| sb-ab", exprid, earrb1||esb2, arrb1||sb2); checkArrBool ("|| ab-sb", exprid, esb1||earrb2, sb1||arrb2); checkArrBool ("&& ab-ab", exprid, earrb1&&earrb2, arrb1&&arrb2); checkArrBool ("&& sb-ab", exprid, earrb1&&esb2, arrb1&&sb2); checkArrBool ("&& ab-sb", exprid, esb1&&earrb2, sb1&&arrb2); checkArrBool ("! ab", exprid, !earrb2, !arrb2); // Check the unary mathematical operators. // Form combinations of scalars and arrays. checkScaInt ("+ si", exprid, +esi1, si1); checkScaInt ("- si", exprid, -esi1, -si1); checkScaInt ("~ si", exprid, ~esi1, ~si1); checkScaDouble ("+ sd", exprid, +esd1, sd1); checkScaDouble ("- sd", exprid, -esd1, -sd1); checkArrInt ("+ ai", exprid, +earri1, arri1); checkArrInt ("- ai", exprid, -earri1, -arri1); checkArrInt ("~ ai", exprid, ~earri1, ~arri1); checkArrDouble ("+ ad", exprid, +earrd1, arrd1); checkArrDouble ("- ad", exprid, -earrd1, -arrd1); checkScaDComplex ("+ sc", exprid, +esc1, scz1); checkScaDComplex ("- sc", exprid, -esc1, -scz1); checkScaDComplex ("+ sz", exprid, +esz1, sz1); checkScaDComplex ("- sz", exprid, -esz1, -sz1); checkArrDComplex ("+ ac", exprid, +earrc1, arrcz1); checkArrDComplex ("- ac", exprid, -earrc1, -arrcz1); checkArrDComplex ("+ az", exprid, +earrz1, arrz1); checkArrDComplex ("- az", exprid, -earrz1, -arrz1); // Check the binary arithmetic operators. // Form combinations of different data types. // Form combinations of scalars and arrays (and constants). checkScaInt ("+ si-ci", exprid, esi1-2, si1-2); checkScaInt ("* si-si", exprid, esi1*esi2, si1*si2); checkScaInt ("& si-si", exprid, esi1&esi2, si1&si2); checkScaInt ("| si-si", exprid, esi1|esi2, si1|si2); checkScaInt ("^ si-si", exprid, esi1^esi2, si1^si2); checkScaInt ("% si-si", exprid, esi1%esi2, floormod(si1,si2)); checkScaDouble ("/ si-si", exprid, esi1/esi2, sid1/si2); checkScaDouble ("+ sd-si", exprid, esd1+esi2, sd1+sid2); checkScaDouble ("/ sd-sd", exprid, esd1/esd2, sd1/sd2); checkScaDouble ("% sd-sd", exprid, esd1%esd2, floormod(sd1,sd2)); checkScaDouble ("**sd-sd", exprid, pow(esd1,esd2), pow(sd1,sd2)); checkArrDouble ("+ ad-ad", exprid, earrd1+earri2, arrd1+arrid2); checkArrDouble ("+ ad-ci", exprid, earrd1+20, arrd1+20.); checkArrDouble ("+ sd-ai", exprid, esd1+earri2, sd1+arrid2); checkArrDouble ("- ai-ad", exprid, earri1-earrd2, arrid1-arrd2); checkArrInt ("- ai-ci", exprid, earri1-1, arri1-1); checkArrInt ("* ai-ai", exprid, earri1*earri2, arri1*arri2); checkArrInt ("* ai-si", exprid, earri1*esi2, arri1*si2); checkArrInt ("& ai-si", exprid, earri1&esi2, arri1&si2); checkArrInt ("| si-ai", exprid, esi2|earri1, arri1|si2); checkArrInt ("^ ai-ai", exprid, earri1^earri2, arri1^arri2); checkArrInt ("% ai-ai", exprid, earri1%earri2, floormod(arri1,arri2)); checkArrDouble ("/ ai-ai", exprid, earri1/earri2, arrid1/arrid2); checkArrDouble ("- ci-ad", exprid, 12-earrd2, 12.-arrd2); checkArrDouble ("* sd-ai", exprid, esd1*earri2, sd1*arrid2); checkArrDouble ("/ ad-ad", exprid, earrd1/earrd2, arrd1/arrd2); checkArrDouble ("/ ad-sd", exprid, earrd1/esd2, arrd1/sd2); checkArrDouble ("/ sd-ad", exprid, esd1/earrd2, sd1/arrd2); checkArrDouble ("% ad-ad", exprid, earrd1%earrd2, floormod(arrd1,arrd2)); checkArrDouble ("% ad-sd", exprid, earrd1%esd2, floormod(arrd1,sd2)); checkArrDouble ("% sd-ad", exprid, esd1%earrd2, floormod(sd1,arrd2)); checkArrDouble ("**ad-ad", exprid, pow(earrd1,earrd2), pow(arrd1,arrd2)); checkArrDouble ("**ad-sd", exprid, pow(earrd1,esd2), pow(arrd1,sd2)); checkArrDouble ("**sd-ad", exprid, pow(esd1,earrd2), pow(sd1,arrd2)); checkScaDComplex ("+ sc-si", exprid, esc1+esi2, scz1+siz2); checkScaDComplex ("- si-sz", exprid, esi1-esz2, siz1-sz2); checkScaDComplex ("* sd-sc", exprid, esd1*esc2, sdz1*scz2); checkScaDComplex ("/ sz-sc", exprid, esz1/esc2, sz1/scz2); checkScaDComplex ("**sz-sz", exprid, pow(esz1,esz2), pow(sz1,sz2)); checkArrDComplex ("+ az-ai", exprid, earrz1+earri2, arrz1+arriz2); checkArrDComplex ("+ az-cc", exprid, earrz1+Complex(10,20), arrz1+DComplex(10,20)); checkArrDComplex ("+ sz-ai", exprid, esz1+earri2, sz1+arriz2); checkArrDComplex ("- ai-ac", exprid, earri1-earrc2, arriz1-arrcz2); checkArrDComplex ("- ai-sc", exprid, earri1-esc2, arriz1-scz2); checkArrDComplex ("- ci-ac", exprid, 100-earrc2, DComplex(100,0)-arrcz2); checkArrDComplex ("* az-az", exprid, earrz1*earrz2, arrz1*arrz2); checkArrDComplex ("* az-sz", exprid, earrz1*esz2, arrz1*sz2); checkArrDComplex ("* sz-az", exprid, esz1*earrz2, sz1*arrz2); checkArrDComplex ("/ ad-ac", exprid, earrd1/earrc2, arrdz1/arrcz2); checkArrDComplex ("/ ad-sc", exprid, earrd1/esc2, arrdz1/scz2); checkArrDComplex ("/ sd-ac", exprid, esd1/earrc2, sdz1/arrcz2); // Check the comparison operators. // Form combinations of different data types. // Form combinations of scalars and arrays (and constants). checkScaBool ("== sb-sb", exprid, esb1==esb2, sb1==sb2); checkScaBool ("!= sb-sb", exprid, esb1!=esb2, sb1!=sb2); checkArrBool ("== ab-ab", exprid, earrb1==earrb2, arrb1==arrb2); checkArrBool ("== ab-t", exprid, earrb1==True, arrb1==True); checkArrBool ("== sb-ab", exprid, esb1==earrb2, sb1==arrb2); checkArrBool ("!= ab-ab", exprid, earrb1!=earrb2, arrb1!=arrb2); checkArrBool ("!= ", exprid, earrb1!=esb2, arrb1!=sb2); checkArrBool ("!= sb-ab", exprid, esb1!=earrb2, sb1!=arrb2); checkScaBool ("== sd-si", exprid, esd1==esi2, sd1==sid2); checkScaBool ("> si-ci", exprid, esi1>2, si1>2); checkScaBool (">= si-si", exprid, esi1>=esi2, si1>=si2); checkScaBool ("< sd-sd", exprid, esd1 ai-ad", exprid, earri1>earrd2, arrid1>arrd2); checkArrBool ("> ai-ci", exprid, earri1>1, arri1>1); checkArrBool ("> ci-ad", exprid, 12>earrd2, 12.>arrd2); checkArrBool (">= ai-ai", exprid, earri1>=earri2, arri1>=arri2); checkArrBool (">= ai-si", exprid, earri1>=esi2, arri1>=si2); checkArrBool (">= sd-ai", exprid, esd1>=earri2, si1>=arri2); checkArrBool ("< ad-ad", exprid, earrd1 si-sz", exprid, esi1>esz2, siz1>sz2); checkScaBool (">= sd-sc", exprid, esd1>=esc2, sdz1>=scz2); checkScaBool ("< sz-sc", exprid, esz1 ai-ac", exprid, earri1>earrc2, arriz1>arrcz2); checkArrBool ("> ai-sc", exprid, earri1>esc2, arriz1>scz2); checkArrBool ("> si-ac", exprid, 100>earrc2, DComplex(100,0)>arrcz2); checkArrBool (">= az-az", exprid, earrz1>=earrz2, arrz1>=arrz2); checkArrBool (">= az-sz", exprid, earrz1>=esz2, arrz1>=sz2); checkArrBool (">= sz-az", exprid, esz1>=earrz2, sz1>=arrz2); checkArrBool ("< ad-ac", exprid, earrd1 ss-ss", exprid, ess1>ess2, ss1>ss2); checkScaBool (">= ss-ss", exprid, ess1>=ess2, ss1>=ss2); checkScaBool ("< ss-ss", exprid, ess1 as-as", exprid, earrs1>earrs2, arrs1>arrs2); checkArrBool ("> as-ss", exprid, earrs1>ess2, arrs1>ss2); checkArrBool ("> ss-as", exprid, "s123">earrs2, String("s123")>arrs2); checkArrBool (">= as-as", exprid, earrs1>=earrs2, arrs1>=arrs2); checkArrBool (">= as-ss", exprid, earrs1>=ess2, arrs1>=ss2); checkArrBool (">= ss-as", exprid, ess1>=earrs2, ss1>=arrs2); checkArrBool ("< as-as", exprid, earrs1scalar functions (they also operate on scalars). checkScaInt ("sum(si)", exprid, sum(esi1), si1); checkScaInt ("product(si)", exprid, product(esi1), si1); checkScaInt ("sumSquare(si)", exprid, sumSquare(esi1), si1*si1); checkScaInt ("min(si)", exprid, min(esi1), si1); checkScaInt ("max(si)", exprid, max(esi1), si1); checkScaDouble ("mean(si)", exprid, mean(esi1), si1); checkScaDouble ("variance(si)", exprid, variance(esi1), 0); checkScaDouble ("stddev(si)", exprid, stddev(esi1), 0); checkScaDouble ("avdev(si)", exprid, avdev(esi1), 0); checkScaDouble ("rms(si)", exprid, rms(esi1), si1); checkScaDouble ("median(si)", exprid, median(esi1), si1); checkScaDouble ("fractile(si, 0.3)", exprid, fractile(esi1, 0.3), si1); checkScaDouble ("sum(sd)", exprid, sum(esd1), sd1); checkScaDouble ("product(sd)", exprid, product(esd1), sd1); checkScaDouble ("sumSquare(sd)", exprid, sumSquare(esd1), sd1*sd1); checkScaDouble ("min(sd)", exprid, min(esd1), sd1); checkScaDouble ("max(sd)", exprid, max(esd1), sd1); checkScaDouble ("mean(sd)", exprid, mean(esd1), sd1); checkScaDouble ("variance(sd)", exprid, variance(esd1), 0); checkScaDouble ("stddev(sd)", exprid, stddev(esd1), 0); checkScaDouble ("avdev(sd)", exprid, avdev(esd1), 0); checkScaDouble ("rms(sd)", exprid, rms(esd1), sd1); checkScaDouble ("median(sd)", exprid, median(esd1), sd1); checkScaDouble ("fractile(sd, 0.3)", exprid, fractile(esd1, 0.3), sd1); checkScaDComplex ("sum(sz)", exprid, sum(esz1), sz1); checkScaDComplex ("product(sz)", exprid, product(esz1), sz1); checkScaDComplex ("sumSquare(sz)", exprid, sumSquare(esz1), sz1*sz1); checkScaInt ("sum(arri)", exprid, sum(earri1), 170); checkScaInt ("product(arri)", exprid, product(earri1), 0); checkScaInt ("sumSquare(arri)", exprid, sumSquare(earri1), 2110); checkScaInt ("min(arri)", exprid, min(earri1), -1); checkScaInt ("max(arri)", exprid, max(earri1), 18); checkScaDouble ("mean(arri)", exprid, mean(earri1), 170./20); checkScaDouble ("variance(arri)", exprid, variance(earri1), 665./19.); checkScaDouble ("stddev(arri)", exprid, stddev(earri1), sqrt(665./19.)); checkScaDouble ("avdev(arri)", exprid, avdev(earri1), 100./20.); checkScaDouble ("rms(arri)", exprid, rms(earri1), sqrt(2110./20.)); checkScaDouble ("median(arri)", exprid, median(earri1), 8.5); checkScaDouble ("fractile(arri, 0.3)", exprid, fractile(earri1, 0.3), 4.); checkScaDouble ("sum(arrd)", exprid, sum(arrdsign), -4.); checkScaDouble ("product(arrd)", exprid, product(arrdsign), 1.); checkScaDouble ("sumSquare(arrd)", exprid, sumSquare(arrdsign), 20.); checkScaDouble ("min(arrd)", exprid, min(arrdsign), -1.); checkScaDouble ("max(arrd)", exprid, max(arrdsign), 1.); checkScaDouble ("mean(arrd)", exprid, mean(arrdsign), -4./20); checkScaDouble ("variance(arrd)", exprid, variance(arrdsign), 19.2/19); checkScaDouble ("stddev(arrd)", exprid, stddev(arrdsign), sqrt(19.2/19)); checkScaDouble ("avdev(arrd)", exprid, avdev(arrdsign), 19.2/20.); checkScaDouble ("rms(arrd)", exprid, rms(arrdsign), 1.); checkScaDouble ("median(arrd)", exprid, median(arrdsign), -1.); checkScaDouble ("fractile(arrd, 0.3)", exprid, fractile(arrdsign, 0.3), -1.); checkScaDComplex ("sum(arrz)", exprid, sum(earrz1), DComplex(-640,-960)); checkScaDComplex ("product(arrz)", exprid, product(earrz1), Complex(1.55851e42, 1.62512e42)); checkScaDComplex ("sumSquare(arrz)", exprid, sumSquare(earrz1), Complex(-358100,859440)); // Check the functions operating on Bool arrays (and scalars). checkScaBool ("any(sb)", exprid, any(esb1), sb1); checkScaBool ("all(sb)", exprid, all(esb1), sb1); checkScaInt ("ntrue(sb)", exprid, ntrue(esb1), 1); checkScaInt ("nfalse(sb)", exprid, nfalse(esb1), 0); checkScaBool ("any(ab)", exprid, any(earrb1), True); checkScaBool ("any(ab)", exprid, any(earrb1==earrb1), True); checkScaBool ("any(ab)", exprid, any(earrb1!=earrb1), False); checkScaBool ("all(ab)", exprid, all(earrb1), False); checkScaBool ("all(ab)", exprid, all(earrb1==earrb1), True); checkScaBool ("all(ab)", exprid, all(earrb1!=earrb1), False); checkScaInt ("ntrue(ab)", exprid, ntrue(earrb1), 19); checkScaInt ("nfalse(ab)", exprid, nfalse(earrb1), 1); checkScaBool ("isnan(si)", exprid, isNaN(esi1), False); checkScaBool ("isnan(sd)", exprid, isNaN(esd1), False); checkScaBool ("isnan(sz)", exprid, isNaN(esz1), False); checkScaBool ("isnan(ai)", exprid, any(isNaN(earri1)), False); checkScaBool ("isnan(ad)", exprid, any(isNaN(earrd1)), False); checkScaBool ("isnan(az)", exprid, any(isNaN(earrz1)), False); // Check array functions. checkScaInt ("array(ab2)", exprid, ntrue(array(esb1,shp)), 20); checkScaInt ("array(ab1)", exprid, ntrue(array(esb1,shp) == earrb1), 19); checkArrDouble ("array(ad)", exprid, array(earrd1,shp), arrd1); checkArrInt ("array(ai)", exprid, array(earri1,shp), arri1); checkArrDComplex ("array(az)", exprid, array(earrz1,shp), arrz1); checkArrString ("array(as)", exprid, array(earrs1,shp), mats1); checkScaInt ("ndim(si)", exprid, ndim(esi1), 0); checkScaInt ("ndim(ad)", exprid, ndim(earrd1), shp.size()); checkArrInt ("shape(az)", exprid, shape(earrz1), shpVec); // Check string and pattern functions. checkScaInt ("strlength(ss)", exprid, strlength(ess1), ss1.size()); checkScaString ("upcase(ss)", exprid, upcase(ess1), "SS1"); checkScaString ("downcase(ss)", exprid, downcase(upcase(ess1)), "ss1"); checkScaString ("trim(ss)", exprid, trim(TableExprNode(" a b ")), "a b"); checkScaString ("trim(ss)", exprid, trim(TableExprNode("")), ""); checkScaString ("trim(ss)", exprid, trim(TableExprNode("a b")), "a b"); checkScaString ("ltrim(ss)", exprid, rtrim(TableExprNode(" a b ")), " a b"); checkScaString ("rtrim(ss)", exprid, ltrim(TableExprNode(" a b ")), "a b "); checkScaString ("substr(ss,2)", exprid, substr(TableExprNode("abcdef"),2), "cdef"); checkScaString ("substr(ss,-4,10)", exprid, substr(TableExprNode("abcdef"),-4,10), "cdef"); checkScaString ("substr(ss,3,2)", exprid, substr(TableExprNode("abcdef"),3,2), "de"); checkArrString ("substr(trim(as),-10,2)", exprid, substr(trim(earrs1),-10,2), Vector(arrs1.size(), "a1")); checkArrString ("substr(as,-100,-1)", exprid, substr(earrs1,-100,-1), Vector(arrs1.size(), "")); checkScaString ("replace(ss,ss1)", exprid, replace(TableExprNode("abcdef"),"ab"), "cdef"); checkScaString ("replace(ss,ss2)", exprid, replace(TableExprNode("abcdefab"),"ab"), "cdef"); checkScaString ("replace(ss,ss2,ss)", exprid, replace(TableExprNode("abcdefab"),"ab", "xyz"), "xyzcdefxyz"); checkScaString ("replace(ss,rg2)", exprid, replace(TableExprNode("abcdefab"),Regex("a.")), "cdef"); checkScaString ("replace(ss,rg2,ss)", exprid, replace(TableExprNode("abcdefab"),Regex("a."), "xaz"), "xazcdefxaz"); checkScaString ("replace(ss,rg1,ss)", exprid, replace(TableExprNode("abcdefab"),Regex("a.$"), "xaz"), "abcdefxaz"); checkArrString ("replace(as,Regex(.*)", exprid, replace(earrs1,Regex(".*")), Vector(arrs1.size(), "")); checkScaBool ("ss==regex", exprid, ess1==regex(TableExprNode("s.*")), True); checkScaBool ("ss==regex", exprid, ess1==regex(TableExprNode("as.*")), False); checkScaBool ("ss==patt", exprid, ess1==pattern(TableExprNode("s*")), True); checkScaBool ("ss==patt", exprid, ess1==pattern(TableExprNode("as*")), False); checkScaBool ("ss==sqlpatt", exprid, ess1==sqlpattern(TableExprNode("s%")), True); checkScaBool ("ss==sqlpatt", exprid, ess1==sqlpattern(TableExprNode("as%")), False); // Check date functions. checkScaDouble ("time", exprid, time("5Apr09/12:"), C::pi); checkScaDate ("datetime", exprid, datetime("5Apr09/12:"), MVTime(54926.5)); checkScaDouble ("mjd", exprid, mjd(datetime("5Apr09/12:")), 54926.5); checkScaInt ("day", exprid, day(datetime("5Apr09/12:")), 5); checkScaInt ("month", exprid, month(datetime("5Apr09/12:")), 4); checkScaInt ("year", exprid, year(datetime("5Apr09/12:")), 2009); checkScaInt ("weekday", exprid, weekday(datetime("5Apr09/12:")), 7); checkScaInt ("weekday", exprid, weekday(datetime("6Apr09/12:")), 1); checkScaInt ("week", exprid, week(datetime("5Apr09/12:")), 14); checkScaInt ("week", exprid, week(datetime("6Apr09/12:")), 15); checkScaInt ("week", exprid, week(datetime("1Jan09/12:")), 1); checkScaString ("cdow", exprid, cdow(datetime("1Jan09/12:")), "Thu"); checkScaString ("cmonth", exprid, cmonth(datetime("1Jan09/12:")), "Jan"); // Check indexing and slicing; also negative. Slicer sl1(IPosition(1,1), IPosition(1,2), Slicer::endIsLast); Slicer sl2(IPosition(1,0), IPosition(1,3), IPosition(1,2), Slicer::endIsLast); Slicer sl3(IPosition(1,-2), IPosition(1,-1), Slicer::endIsLast); checkScaString ("[2]", exprid, earrs1(IPosition(1,2)), arrs1[2]); checkScaString ("[-3]", exprid, earrs1(IPosition(1,-3)), arrs1[1]); checkArrString ("[1:2]", exprid, earrs1(sl1), arrs1(sl1)); checkArrString ("[0:3:2]", exprid, earrs1(sl2), arrs1(sl2)); checkArrString ("[-2:-1]", exprid, earrs1(sl3), arrs1(sl3)); // Check various kind of failures. checkFailure ("& si-sd", esi1&esd1); checkFailure ("| ad-si", earrd1|esi1); checkFailure ("sin ss", sin(ess1)); checkFailure ("sin ss", sin(earrb1)); checkFailure ("integer sz", integer(esz1)); checkFailure ("asin ss", asin(ess1)); checkFailure ("tan ss", tan(ess1)); checkFailure ("ceil sz", ceil(esz1)); checkFailure ("sign sz", sign(esz1)); checkFailure ("round sz", round(esz1)); checkFailure ("complex ab", formComplex(ess1,esd1)); checkFailure ("complex ab", formComplex(ess1,ess1)); checkFailure ("complex sz-sz", formComplex(esz1,esz2)); checkFailure ("near2 ss-ss", near(ess1,ess1)); checkFailure ("near2 ai-ss", near(earri1,ess1)); checkFailure ("nearabs2 ss-ss", nearAbs(ess1,ess1)); checkFailure ("nearabs2 as-si", nearAbs(earrs1,esi1)); checkFailure ("near3 ss-ss", near(ess1,ess1, 1e-10)); checkFailure ("near3 ai-ss", near(earri1,ess1, 1e-10)); checkFailure ("nearabs2 ss-ss", nearAbs(ess1,ess1)); checkFailure ("nearabs2 as-si", nearAbs(earrs1,esi1)); checkFailure ("fmod sz-sd", fmod(esz1,esd1)); checkFailure ("min sz", min(esz1)); } void doShow() { // Make some expressions where constants should have been pre-evaluated. TableExprNode expr1(TableExprNode(1)*2+3); cout << "1*2+3 = " << expr1.getInt(0) << " "; expr1.show (cout); TableExprNode expr2(ndim(array(3, IPosition(1,5)))); cout << "ndim(array(3,[5])) = " << expr2.getInt(0) << " "; expr2.show (cout); } int main() { try { doIt(); doShow(); } catch (std::exception& x) { cout << "Unexpected exception: " << x.what() << endl; return 1; } catch (...) { cout << "Unexpected unknown exception" << endl; return 1; } if (foundError) { cout << "Some unexpected results were found" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/tables/TaQL/test/tExprNodeSet.cc000066400000000000000000000322401476623553700212520ustar00rootroot00000000000000//# tExprNodeSet.cc: Test program for the ExprNodeSet selection classes //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include // // Test program for the ExprNodeSet selection classes. // void doSetBool() { TableExprNodeSetElem elem((TableExprNode(True))); TableExprNodeSet set; set.add (elem); set.add (elem); AlwaysAssertExit (set.isSingle()); AlwaysAssertExit (set.isDiscrete()); AlwaysAssertExit (set.isBounded()); AlwaysAssertExit (set.size() == 2); AlwaysAssertExit (!set.hasArrays()); AlwaysAssertExit (set.contains (0, True)); AlwaysAssertExit (!set.contains (0, False)); } void doSetInt() { TableExprNodeSetElem elem((TableExprNode(1))); TableExprNodeSet set; set.add (elem); AlwaysAssertExit (set.isSingle()); AlwaysAssertExit (set.isDiscrete()); AlwaysAssertExit (set.isBounded()); AlwaysAssertExit (set.size() == 1); AlwaysAssertExit (!set.hasArrays()); AlwaysAssertExit (set.contains (0, 1)); AlwaysAssertExit (!set.contains (0, 2)); TableExprNode st(3); TableExprNode end(10); TableExprNode incr(3); set.add (TableExprNodeSetElem(&st, &end, &incr, True)); AlwaysAssertExit (!set.isSingle()); AlwaysAssertExit (set.isDiscrete()); AlwaysAssertExit (set.isBounded()); AlwaysAssertExit (set.size() == 2); AlwaysAssertExit (!set.hasArrays()); AlwaysAssertExit (set.contains (0, 1)); AlwaysAssertExit (!set.contains (0, 2)); AlwaysAssertExit (set.contains (0, 3)); AlwaysAssertExit (!set.contains (0, 4)); set.add (TableExprNodeSetElem(&st, 0, 0, True)); AlwaysAssertExit (!set.isSingle()); AlwaysAssertExit (set.isDiscrete()); AlwaysAssertExit (!set.isBounded()); AlwaysAssertExit (set.size() == 3); AlwaysAssertExit (!set.hasArrays()); AlwaysAssertExit (set.contains (0, 1)); AlwaysAssertExit (!set.contains (0, 2)); AlwaysAssertExit (set.contains (0, 3)); AlwaysAssertExit (set.contains (0, 4)); } void doSetDouble() { TableExprNodeSetElem elem((TableExprNode(1.))); TableExprNodeSet set; set.add (elem); AlwaysAssertExit (set.isSingle()); AlwaysAssertExit (set.isDiscrete()); AlwaysAssertExit (set.isBounded()); AlwaysAssertExit (set.size() == 1); AlwaysAssertExit (!set.hasArrays()); AlwaysAssertExit (set.contains (0, 1.)); AlwaysAssertExit (!set.contains (0, 2.)); TableExprNode st(3.); TableExprNode end(10); TableExprNode incr(3); set.add (TableExprNodeSetElem(&st, &end, &incr, True)); AlwaysAssertExit (!set.isSingle()); AlwaysAssertExit (set.isDiscrete()); AlwaysAssertExit (set.isBounded()); AlwaysAssertExit (set.size() == 2); AlwaysAssertExit (!set.hasArrays()); AlwaysAssertExit (set.contains (0, 1.)); AlwaysAssertExit (!set.contains (0, 2.)); AlwaysAssertExit (set.contains (0, 3.)); AlwaysAssertExit (!set.contains (0, 4.)); set.add (TableExprNodeSetElem(&st, 0, 0, True)); AlwaysAssertExit (!set.isSingle()); AlwaysAssertExit (set.isDiscrete()); AlwaysAssertExit (!set.isBounded()); AlwaysAssertExit (set.size() == 3); AlwaysAssertExit (!set.hasArrays()); AlwaysAssertExit (set.contains (0, 1.)); AlwaysAssertExit (!set.contains (0, 2.)); AlwaysAssertExit (set.contains (0, 3.)); AlwaysAssertExit (set.contains (0, 4.)); } void doSetDComplex() { { // Test using a scalar as a single element. TableExprNodeSetElem elem((TableExprNode(DComplex(1,2)))); TableExprNodeSet set; set.add (elem); set.add (elem); AlwaysAssertExit (set.isSingle()); AlwaysAssertExit (set.isDiscrete()); AlwaysAssertExit (set.isBounded()); AlwaysAssertExit (set.size() == 2); AlwaysAssertExit (!set.hasArrays()); AlwaysAssertExit (set.contains (0, DComplex(1,2))); AlwaysAssertExit (!set.contains (0, DComplex(1,3))); } { // Test using an array as a single element. // This can only be done by converting the set to an array. Vector vec({DComplex(1,2), DComplex(3,4)}); TableExprNodeSetElem elem(vec); TableExprNodeSet set; set.add (elem); set.add (TableExprNodeSetElem (TableExprNode(DComplex(1,4)))); AlwaysAssertExit (set.isDiscrete()); AlwaysAssertExit (set.isBounded()); AlwaysAssertExit (set.size() == 2); AlwaysAssertExit (set.hasArrays()); AlwaysAssertExit (set.contains (0, DComplex(1,2))); AlwaysAssertExit (!set.contains (0, DComplex(1,3))); AlwaysAssertExit (set.contains (0, DComplex(3,4))); AlwaysAssertExit (set.contains (0, DComplex(1,4))); } } void doSetString() { TableExprNodeSetElem elem((TableExprNode("ger"))); TableExprNodeSet set; set.add (elem); AlwaysAssertExit (set.isSingle()); AlwaysAssertExit (set.isDiscrete()); AlwaysAssertExit (set.isBounded()); AlwaysAssertExit (set.size() == 1); AlwaysAssertExit (!set.hasArrays()); AlwaysAssertExit (set.contains (0, "ger")); AlwaysAssertExit (!set.contains (0, "Ger")); TableExprNode st("ger1"); TableExprNode end("ger9"); set.add (TableExprNodeSetElem(True, st, end, True)); AlwaysAssertExit (!set.isSingle()); AlwaysAssertExit (!set.isDiscrete()); AlwaysAssertExit (!set.isBounded()); AlwaysAssertExit (set.size() == 2); AlwaysAssertExit (!set.hasArrays()); AlwaysAssertExit (set.contains (0, "ger")); AlwaysAssertExit (!set.contains (0, "Ger")); AlwaysAssertExit (set.contains (0, "ger1")); AlwaysAssertExit (!set.contains (0, "ger99")); set.add (TableExprNodeSetElem(True, st)); AlwaysAssertExit (!set.isSingle()); AlwaysAssertExit (!set.isDiscrete()); AlwaysAssertExit (!set.isBounded()); AlwaysAssertExit (set.size() == 3); AlwaysAssertExit (!set.hasArrays()); AlwaysAssertExit (set.contains (0, "ger")); AlwaysAssertExit (!set.contains (0, "Ger")); AlwaysAssertExit (set.contains (0, "ger1")); AlwaysAssertExit (set.contains (0, "ger99")); } void doSetDate() { TableExprNodeSetElem elem(datetime("5Apr09/12:")); // MJD 54926.5 TableExprNodeSet set; set.add (elem); AlwaysAssertExit (set.isSingle()); AlwaysAssertExit (set.isDiscrete()); AlwaysAssertExit (set.isBounded()); AlwaysAssertExit (set.size() == 1); AlwaysAssertExit (!set.hasArrays()); AlwaysAssertExit (set.contains (0, 54926.5)); AlwaysAssertExit (!set.contains (0, 2.)); TableExprNode st(datetime("5Apr09/12:")); // MJD 54926.5 TableExprNode end(datetime("7May09/12:")); // MJD 54958.5 TableExprNode incr(3); set.add (TableExprNodeSetElem(&st, &end, &incr, True)); AlwaysAssertExit (!set.isSingle()); AlwaysAssertExit (set.isDiscrete()); AlwaysAssertExit (set.isBounded()); AlwaysAssertExit (set.size() == 2); AlwaysAssertExit (!set.hasArrays()); AlwaysAssertExit (set.contains (0, 54926.5)); AlwaysAssertExit (!set.contains (0, 54927.5)); AlwaysAssertExit (set.contains (0, 54929.5)); AlwaysAssertExit (!set.contains (0, 54957.5)); set.add (TableExprNodeSetElem(&st, 0, 0, True)); AlwaysAssertExit (!set.isSingle()); AlwaysAssertExit (set.isDiscrete()); AlwaysAssertExit (!set.isBounded()); AlwaysAssertExit (set.size() == 3); AlwaysAssertExit (!set.hasArrays()); AlwaysAssertExit (set.contains (0, 54926.5)); AlwaysAssertExit (set.contains (0, 54927.5)); AlwaysAssertExit (set.contains (0, 54929.5)); AlwaysAssertExit (set.contains (0, 54957.5)); } void doIPosition() { TableExprNodeSet set(IPosition(3,4,5,6)); AlwaysAssertExit (set.isSingle()); AlwaysAssertExit (set.isDiscrete()); AlwaysAssertExit (set.isBounded()); AlwaysAssertExit (set.dataType() == TableExprNodeRep::NTInt); AlwaysAssertExit (set.size() == 3); AlwaysAssertExit (!set.hasArrays()); AlwaysAssertExit (set.contains (0, 4)); AlwaysAssertExit (set.contains (0, 5)); AlwaysAssertExit (set.contains (0, 6)); AlwaysAssertExit (!set.contains (0, 3)); // Form an index node. TableExprNodeIndex inx(set); AlwaysAssertExit (inx.isSingle()); AlwaysAssertExit (inx.getSlicer(0).start() == IPosition(3,4,5,6)); } void doSlicer() { { Slicer sl(IPosition(2,1,2), IPosition(2,10,12), IPosition(2,2,3), Slicer::endIsLast); TableExprNodeSet set(sl); AlwaysAssertExit (!set.isSingle()); AlwaysAssertExit (set.isDiscrete()); AlwaysAssertExit (set.isBounded()); AlwaysAssertExit (set.dataType() == TableExprNodeRep::NTInt); AlwaysAssertExit (set.size() == 2); AlwaysAssertExit (!set.hasArrays()); AlwaysAssertExit (set[0]->start()->getInt(0) == 1); AlwaysAssertExit (set[0]->end()->getInt(0) == 10); AlwaysAssertExit (set[0]->increment()->getInt(0) == 2); AlwaysAssertExit (set[1]->start()->getInt(0) == 2); AlwaysAssertExit (set[1]->end()->getInt(0) == 12); AlwaysAssertExit (set[1]->increment()->getInt(0) == 3); // Form an index node. TableExprNodeIndex inx(set); AlwaysAssertExit (!inx.isSingle()); AlwaysAssertExit (inx.getSlicer(0).start() == IPosition(2,1,2)); AlwaysAssertExit (inx.getSlicer(0).end() == IPosition(2,10,12)); AlwaysAssertExit (inx.getSlicer(0).stride() == IPosition(2,2,3)); } { Slicer sl(IPosition(2,Slicer::MimicSource,2), IPosition(2,10,Slicer::MimicSource), Slicer::endIsLast); TableExprNodeSet set(sl); AlwaysAssertExit (!set.isSingle()); AlwaysAssertExit (set.isDiscrete()); AlwaysAssertExit (set.isBounded()); AlwaysAssertExit (set.dataType() == TableExprNodeRep::NTInt); AlwaysAssertExit (set.size() == 2); AlwaysAssertExit (!set.hasArrays()); AlwaysAssertExit (! set[0]->start()); AlwaysAssertExit (set[0]->end()->getInt(0) == 10); AlwaysAssertExit (set[0]->increment()->getInt(0) == 1); AlwaysAssertExit (set[1]->start()->getInt(0) == 2); AlwaysAssertExit (! set[1]->end()); AlwaysAssertExit (set[1]->increment()->getInt(0) == 1); } } void doEmpty() { TableExprNodeSet set; AlwaysAssertExit (set.isSingle()); AlwaysAssertExit (set.isDiscrete()); AlwaysAssertExit (set.isBounded()); AlwaysAssertExit (set.dataType() == TableExprNodeRep::NTNumeric); AlwaysAssertExit (set.size() == 0); AlwaysAssertExit (!set.hasArrays()); } #define tryError(CMD) \ { \ Bool ok = True; \ try { \ CMD; \ ok = False; \ } catch (const std::exception& x) { \ cout << "Expected: " << x.what() << endl; \ } \ AlwaysAssertExit (ok); \ } void doErrors() { TableExprNode intNode(1); TableExprNode dblNode(1.); dblNode.useUnit ("m"); TableExprNode db2Node(1.); db2Node.useUnit ("Hz"); TableExprNode strNode("a"); TableExprNodeSetElem intElem(intNode); TableExprNodeSetElem rangeElem(nullptr, &intNode, nullptr); TableExprNodeSetElem dblElem(dblNode); TableExprNodeSetElem db2Elem(db2Node); TableExprNodeSetElem strElem(strNode); TableExprNodeSet set; set.add (intElem, True); set.add (dblElem, True); tryError (intNode.in (set)); // mismatching data types TableExprNode setarr(set.setOrArray()); AlwaysAssertExit (setarr.getRep()->valueType() == TableExprNodeRep::VTArray); intNode.in (setarr); tryError (set.add (strElem, True)); // data type cannot be coerced { TableExprNodeSet set2(set); set2.add (db2Elem, True); TableExprNode setarr2(set2.setOrArray()); AlwaysAssertExit (setarr2.getRep()->valueType() == TableExprNodeRep::VTSet); } { TableExprNodeSet set2(set); set2.add (rangeElem, True); tryError (set2.setOrArray()); // no start in rangeElem } } int main() { try { doSetBool(); doSetInt(); doSetDouble(); doSetDComplex(); doSetString(); doSetDate(); doIPosition(); doSlicer(); doEmpty(); doErrors(); } catch (std::exception& x) { cout << "Unexpected exception: " << x.what() << endl; return 1; } catch (...) { cout << "Unexpected unknown exception" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/tables/TaQL/test/tExprNodeSetElem.cc000066400000000000000000000725051476623553700220650ustar00rootroot00000000000000//# tExprNodeSet.cc: Test program for the ExprNodeSetElem selection classes //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include // // Test program for the ExprNodeSetElem selection classes. // void checkMatchBool (const TableExprNodeSetElem& tset, Bool val, Bool result) { cout << "checkMatchBool " << val << ' ' << result << endl; Bool res = False; tset.getElem()->matchBool (&res, &val, 1, 0); AlwaysAssertExit (res == result); // Evaluate the element and test if result still matches. TENSEBShPtr newElem (tset.getElem()->evaluate (TableExprId(0))); res = False; newElem->matchBool (&res, &val, 1, 0); AlwaysAssertExit (res == result); } void checkMatchInt (const TableExprNodeSetElem& tset, Int64 val, Bool result) { cout << "checkMatchInt " << val << ' ' << result << endl; Bool res = False; tset.getElem()->matchInt (&res, &val, 1, 0); AlwaysAssertExit (res == result); // Evaluate the element and test if result still matches. TENSEBShPtr newElem (tset.getElem()->evaluate (TableExprId(0))); res = False; newElem->matchInt (&res, &val, 1, 0); AlwaysAssertExit (res == result); } void checkMatchDouble (const TableExprNodeSetElem& tset, Double val, Bool result) { cout << "checkMatchDouble " << val << ' ' << result << endl; Bool res = False; tset.getElem()->matchDouble (&res, &val, 1, 0); AlwaysAssertExit (res == result); // Evaluate the element and test if result still matches. TENSEBShPtr newElem (tset.getElem()->evaluate (TableExprId(0))); res = False; newElem->matchDouble (&res, &val, 1, 0); AlwaysAssertExit (res == result); } void checkMatchDComplex (const TableExprNodeSetElem& tset, const DComplex& val, Bool result) { cout << "checkMatchDComplex " << val << ' ' << result << endl; Bool res = False; tset.getElem()->matchDComplex (&res, &val, 1, 0); AlwaysAssertExit (res == result); // Evaluate the element and test if result still matches. TENSEBShPtr newElem (tset.getElem()->evaluate (TableExprId(0))); res = False; newElem->matchDComplex (&res, &val, 1, 0); AlwaysAssertExit (res == result); } void checkMatchString (const TableExprNodeSetElem& tset, const String& val, Bool result) { cout << "checkMatchString " << val << ' ' << result << endl; Bool res = False; tset.getElem()->matchString (&res, &val, 1, 0); AlwaysAssertExit (res == result); // Evaluate the element and test if result still matches. TENSEBShPtr newElem (tset.getElem()->evaluate (TableExprId(0))); res = False; newElem->matchString (&res, &val, 1, 0); AlwaysAssertExit (res == result); } void checkMatchDate (const TableExprNodeSetElem& tset, MVTime val, Bool result) { cout << "checkMatchDate " << Double(val) << ' ' << result << endl; Bool res = False; tset.getElem()->matchDate (&res, &val, 1, 0); AlwaysAssertExit (res == result); // Evaluate the element and test if result still matches. TENSEBShPtr newElem (tset.getElem()->evaluate (TableExprId(0))); res = False; newElem->matchDate (&res, &val, 1, 0); AlwaysAssertExit (res == result); } Bool testAttr (const TableExprNodeSetElem& elem, TableExprNodeRep::NodeDataType dt, Bool hasStart, Bool hasEnd, Bool hasIncrement, Bool isDiscrete, Bool isSingle, const String& unit=String(), Bool isLeftClosed=False, Bool isRightClosed=False, Bool isMidWidth=False) { Bool res = (elem.getElem()->dataType() == dt && Bool(elem.getElem()->start()) == hasStart && Bool(elem.getElem()->end()) == hasEnd && Bool(elem.getElem()->increment()) == hasIncrement && elem.getElem()->isDiscrete() == isDiscrete && elem.getElem()->isSingle() == isSingle && elem.getElem()->unit() == unit && elem.getElem()->isLeftClosed() == isLeftClosed && elem.getElem()->isRightClosed() == isRightClosed && elem.getElem()->isMidWidth() == isMidWidth && elem.getElem()->isConstant()); // Evaluate the element and test if attributes still match. // Note that mid-width is evaluated as a normal interval. TENSEBShPtr newElem(elem.getElem()->evaluate (TableExprId(0))); Bool res1 = (newElem->dataType() == dt && Bool(newElem->start()) == hasStart && Bool(newElem->end()) == hasEnd && Bool(newElem->increment()) == hasIncrement && newElem->isDiscrete() == isDiscrete && newElem->isSingle() == isSingle && newElem->unit()== unit && newElem->isLeftClosed() == isLeftClosed && newElem->isRightClosed() == isRightClosed && newElem->isMidWidth() == False && newElem->isConstant()); return res && res1; } void doDiscreteBool() { TableExprNode st(True); bool failed= False; try { TableExprNodeSetElem tset(&st, 0, 0, False); } catch (std::exception& x) { cout <<"Expected: " << x.what() << endl; failed = True; } AlwaysAssertExit (failed); { TableExprNodeSetElem tset(st); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTBool, True, False, False, True, True)); Vector vec; Int64 cnt=0; tset.getElem()->fillVector (vec, cnt, 0); vec.resize (cnt, True); AlwaysAssertExit (allEQ(Vector(1,True), vec)); checkMatchBool (tset, True, True); checkMatchBool (tset, False, False); } } void doDiscreteInt() { TableExprNode st(1); TableExprNode end(99); TableExprNode incr(2); TableExprNode stn(-1); TableExprNode endn(-99); TableExprNode incrn(-2); { TableExprNodeSetElem tset(&st, &end, &incr, False); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTInt, True, True, True, True, False)); Vector vec; Int64 cnt=0; tset.getElem()->fillVector (vec, cnt, 0); vec.resize (cnt, True); Vector exp(50); indgen (exp, Int64(1), Int64(2)); AlwaysAssertExit (allEQ(exp, vec)); checkMatchInt (tset, 1, True); checkMatchInt (tset, 2, False); } { TableExprNodeSetElem tset(&stn, &endn, &incrn, False); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTInt, True, True, True, True, False)); Vector vec; Int64 cnt=0; tset.getElem()->fillVector (vec, cnt, 0); vec.resize (cnt, True); Vector exp(50); indgen (exp, Int64(-1), Int64(-2)); AlwaysAssertExit (allEQ(exp, vec)); checkMatchInt (tset, -7, True); checkMatchInt (tset, -10, False); } { TableExprNodeSetElem tset(&stn, 0, &incrn, False); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTInt, True, False, True, True, False)); Vector vec; Int64 cnt=0; tset.getElem()->fillVector (vec, cnt, 0); AlwaysAssertExit (cnt == 1 && vec[0] == -1); checkMatchInt (tset, -1, True); checkMatchInt (tset, -2, False); } { TableExprNodeSetElem tset(&st, &end, &incr, True); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTInt, True, True, True, True, False)); Vector vec(51); vec[0] = -3; vec[1] = -1; Int64 cnt=2; tset.getElem()->fillVector (vec, cnt, 0); vec.resize (cnt, True); Vector exp(51); indgen (exp, Int64(-3), Int64(2)); AlwaysAssertExit (allEQ(exp, vec)); } { TableExprNodeSetElem tset(&st, &end, 0, True); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTInt, True, True, False, True, False)); Vector vec(98); Int64 cnt=0; tset.getElem()->fillVector (vec, cnt, 0); vec.resize (cnt, True); Vector exp(98); indgen (exp, Int64(1)); AlwaysAssertExit (allEQ(exp, vec)); } { TableExprNodeSetElem tset(&st, 0, 0, True); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTInt, True, False, False, True, False)); Vector vec; Int64 cnt=0; tset.getElem()->fillVector (vec, cnt, 0); vec.resize (cnt, True); AlwaysAssertExit (allEQ(Vector(), vec)); } { TableExprNodeSetElem tset(0, &end, 0, False); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTInt, False, True, False, True, False)); Vector vec; Int64 cnt=0; tset.getElem()->fillVector (vec, cnt, 0); vec.resize (cnt, True); Vector exp(100); indgen (exp); AlwaysAssertExit (allEQ(exp, vec)); } { TableExprNodeSetElem tset(0, 0, 0, True); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTInt, False, False, False, True, False)); Vector vec; Int64 cnt=0; tset.getElem()->fillVector (vec, cnt, 0); vec.resize (cnt, True); AlwaysAssertExit (allEQ(Vector(), vec)); } { TableExprNodeSetElem tset(st); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTInt, True, False, False, True, True)); Vector vec; Int64 cnt=0; tset.getElem()->fillVector (vec, cnt, 0); vec.resize (cnt, True); AlwaysAssertExit (allEQ(Vector(1,1), vec)); checkMatchInt (tset, 1, True); checkMatchInt (tset, 2, False); } } void doDiscreteDouble() { TableExprNode st(1); st.useUnit ("dm"); TableExprNode end(1.001); end.useUnit ("m"); TableExprNode incr(2); incr.useUnit ("cm"); TableExprNode stn(100.); TableExprNode endn(10.); TableExprNode incrn(-2.5); { TableExprNodeSetElem tset(&st, &end, &incr, False); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTDouble, True, True, True, True, False, "dm")); Vector vec; Int64 cnt=0; tset.getElem()->fillVector (vec, cnt, 0); vec.resize (cnt, True); Vector exp(46); indgen (exp, 1., 0.2); AlwaysAssertExit (allNear(exp, vec, 1e-13)); checkMatchDouble (tset, 3., True); checkMatchDouble (tset, 3.1, False); } { TableExprNodeSetElem tset(&stn, &endn, &incrn, True); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTDouble, True, True, True, True, False)); Vector vec; Int64 cnt=0; tset.getElem()->fillVector (vec, cnt, 0); vec.resize (cnt, True); Vector exp(36); indgen (exp, 100., -2.5); AlwaysAssertExit (allNear(exp, vec, 1e-13)); checkMatchDouble (tset, 100., True); checkMatchDouble (tset, 12.51, False); checkMatchDouble (tset, 12.5, True); checkMatchDouble (tset, 10., False); } { TableExprNodeSetElem tset(&stn, 0, &incrn, True); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTDouble, True, False, True, True, False)); Vector vec; Int64 cnt=0; tset.getElem()->fillVector (vec, cnt, 0); AlwaysAssertExit (cnt == 0); checkMatchDouble (tset, 100., True); checkMatchDouble (tset, -100., True); checkMatchDouble (tset, -102., False); checkMatchDouble (tset, 102.5, False); } { TableExprNodeSetElem tset(&st, &end, &incr, True); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTDouble, True, True, True, True, False, "dm")); Vector vec(25); vec[0] = 0.6; vec[1] = 0.8; Int64 cnt=2; tset.getElem()->fillVector (vec, cnt, 0); vec.resize (cnt, True); Vector exp(48); indgen (exp, 0.6, 0.2); AlwaysAssertExit (allNear(exp, vec, 1e-13)); } { TableExprNodeSetElem tset(&st, &end, 0, True); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTDouble, True, True, False, True, False, "dm")); Vector vec; Int64 cnt=0; tset.getElem()->fillVector (vec, cnt, 0); vec.resize (cnt, True); Vector exp(10); indgen (exp, 1., 1.); AlwaysAssertExit (allNear(exp, vec, 1e-13)); } { TableExprNodeSetElem tset(&st, 0, 0, True); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTDouble, True, False, False, True, False, "dm")); Vector vec; Int64 cnt=0; tset.getElem()->fillVector (vec, cnt, 0); vec.resize (cnt, True); AlwaysAssertExit (allNear(Vector(), vec, 1e-13)); } { TableExprNodeSetElem tset(st); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTDouble, True, False, False, True, True, "dm")); Vector vec; Int64 cnt=0; tset.getElem()->fillVector (vec, cnt, 0); vec.resize (cnt, True); AlwaysAssertExit (allNear(Vector(1, 1.), vec, 1e-13)); checkMatchDouble (tset, 1., True); checkMatchDouble (tset, -2., False); } } void doDiscreteDComplex() { TableExprNode st(DComplex(1,2)); bool failed= False; try { TableExprNodeSetElem tset(&st, 0, 0, False); } catch (std::exception& x) { cout <<"Expected: " << x.what() << endl; failed = True; } AlwaysAssertExit (failed); { TableExprNodeSetElem tset(st); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTComplex, True, False, False, True, True)); Vector vec; Int64 cnt=0; tset.getElem()->fillVector (vec, cnt, 0); vec.resize (cnt, True); AlwaysAssertExit (allEQ(Vector(1,DComplex(1,2)), vec)); checkMatchDComplex (tset, DComplex(1,2), True); checkMatchDComplex (tset, DComplex(1,2.1), False); } } void doDiscreteString() { TableExprNode st("abcd"); bool failed= False; try { TableExprNodeSetElem tset(&st, 0, 0, False); } catch (std::exception& x) { cout <<"Expected: " << x.what() << endl; failed = True; } AlwaysAssertExit (failed); { TableExprNodeSetElem tset(st); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTString, True, False, False, True, True)); Vector vec; Int64 cnt=0; tset.getElem()->fillVector (vec, cnt, 0); vec.resize (cnt, True); AlwaysAssertExit (allEQ(Vector(1,"abcd"), vec)); checkMatchString (tset, "abcd", True); checkMatchString (tset, "abc", False); checkMatchString (tset, "abcde", False); } } void doDiscreteDate() { TableExprNode st(datetime("5Apr09/12:")); TableExprNode end(datetime("7May09/12:")); TableExprNode incr(7); { TableExprNodeSetElem tset(&st, &end, &incr, False); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTDate, True, True, True, True, False)); Vector vect; Int64 cnt=0; tset.getElem()->fillVector (vect, cnt, 0); vect.resize (cnt, True); Vector vec(vect.size()); convertArray (vec, vect); Vector exp(5); indgen (exp, 54926.5, 7.); AlwaysAssertExit (allNear(exp, vec, 1e-13)); checkMatchDate (tset, 54926.5, True); checkMatchDate (tset, 3.1, False); } { TableExprNodeSetElem tset(&st, &end, &incr, True); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTDate, True, True, True, True, False)); Vector vect; Int64 cnt=0; tset.getElem()->fillVector (vect, cnt, 0); vect.resize (cnt, True); Vector vec(vect.size()); convertArray (vec, vect); Vector exp(5); indgen (exp, 54926.5, 7.); AlwaysAssertExit (allNear(exp, vec, 1e-13)); } { TableExprNodeSetElem tset(&st, &end, 0, True); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTDate, True, True, False, True, False)); Vector vect; Int64 cnt=0; tset.getElem()->fillVector (vect, cnt, 0); vect.resize (cnt, True); Vector vec(vect.size()); convertArray (vec, vect); Vector exp(32); indgen (exp, 54926.5); AlwaysAssertExit (allNear(exp, vec, 1e-13)); } { TableExprNodeSetElem tset(&st, 0, 0, True); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTDate, True, False, False, True, False)); Vector vect; Int64 cnt=0; tset.getElem()->fillVector (vect, cnt, 0); vect.resize (cnt, True); Vector vec(vect.size()); convertArray (vec, vect); AlwaysAssertExit (allNear(Vector(), vec, 1e-13)); } { TableExprNodeSetElem tset(st); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTDate, True, False, False, True, True)); Vector vect; Int64 cnt=0; tset.getElem()->fillVector (vect, cnt, 0); vect.resize (cnt, True); Vector vec(vect.size()); convertArray (vec, vect); AlwaysAssertExit (allNear(Vector(1, 54926.5), vec, 1e-13)); checkMatchDate (tset, 54926.5, True); checkMatchDate (tset, 54926.6, False); } } void doIntervalBool() { TableExprNode st(True); TableExprNode end(False); bool failed= False; try { TableExprNodeSetElem tset(False, st, end, False); } catch (std::exception& x) { cout <<"Expected: " << x.what() << endl; failed = True; } AlwaysAssertExit (failed); } void doIntervalInt() { { TableExprNode st(1); TableExprNode end(99); TableExprNodeSetElem tset(False, st, end, False); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTDouble, True, True, False, False, False, "", False, False)); } { TableExprNode st(1); TableExprNode end(99.); TableExprNodeSetElem tset(False, st, end, True); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTDouble, True, True, False, False, False, "", False, True)); } } void doIntervalDouble() { TableExprNode st(1); st.useUnit ("m"); TableExprNode end(989.5); end.useUnit ("cm"); { TableExprNodeSetElem tset(False, st, end, False); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTDouble, True, True, False, False, False, "m", False, False)); checkMatchDouble (tset, -1., False); checkMatchDouble (tset, 1., False); checkMatchDouble (tset, 9., True); checkMatchDouble (tset, 9.9, False); // unit is m checkMatchDouble (tset, 10., False); } { TableExprNodeSetElem tset(True, st, end, True); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTDouble, True, True, False, False, False, "m", True, True)); checkMatchDouble (tset, -1., False); checkMatchDouble (tset, 1., True); checkMatchDouble (tset, 9., True); checkMatchDouble (tset, 9.9, False); checkMatchDouble (tset, 10., False); } { TableExprNodeSetElem tset(True, st); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTDouble, True, False, False, False, False, "m", True, False)); checkMatchDouble (tset, -1., False); checkMatchDouble (tset, 1., True); checkMatchDouble (tset, 9., True); checkMatchDouble (tset, 9.9, True); checkMatchDouble (tset, 10., True); } { TableExprNodeSetElem tset(end, True); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTDouble, False, True, False, False, False, "cm", False, True)); checkMatchDouble (tset, -1., True); checkMatchDouble (tset, 1., True); checkMatchDouble (tset, 989., True); checkMatchDouble (tset, 990, False); // unit is cm checkMatchDouble (tset, 1000., False); } } void doIntervalDComplex() { TableExprNode st(1); TableExprNode end(DComplex(1,0)); bool failed= False; try { TableExprNodeSetElem tset(False, st, end, False); } catch (std::exception& x) { cout <<"Expected: " << x.what() << endl; failed = True; } AlwaysAssertExit (failed); } void doIntervalString() { TableExprNode st("abcd"); TableExprNode end("cde"); { TableExprNodeSetElem tset(False, st, end, False); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTString, True, True, False, False, False, "", False, False)); checkMatchString (tset, "abc", False); checkMatchString (tset, "abcd", False); checkMatchString (tset, "abcde", True); checkMatchString (tset, "cde", False); checkMatchString (tset, "cdef", False); } { TableExprNodeSetElem tset(True, st, end, True); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTString, True, True, False, False, False, "", True, True)); checkMatchString (tset, "abc", False); checkMatchString (tset, "abcd", True); checkMatchString (tset, "abcde", True); checkMatchString (tset, "cde", True); checkMatchString (tset, "cdef", False); } { TableExprNodeSetElem tset(False, st); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTString, True, False, False, False, False, "", False, False)); checkMatchString (tset, "abc", False); checkMatchString (tset, "abcd", False); checkMatchString (tset, "abcde", True); checkMatchString (tset, "cde", True); checkMatchString (tset, "cdef", True); } { TableExprNodeSetElem tset(end, False); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTString, False, True, False, False, False, "", False, False)); checkMatchString (tset, "abc", True); checkMatchString (tset, "abcd", True); checkMatchString (tset, "abcde", True); checkMatchString (tset, "cde", False); checkMatchString (tset, "cdef", False); } } void doIntervalDate() { TableExprNode st(datetime("5Apr09/12:")); TableExprNode end(datetime("7May09/12:")); { TableExprNodeSetElem tset(False, st, end, False); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTDate, True, True, False, False, False, "", False, False)); checkMatchDate (tset, 54926.0, False); checkMatchDate (tset, 54926.5, False); checkMatchDate (tset, 54940.0, True); checkMatchDate (tset, 54958.5, False); checkMatchDate (tset, 54959.0, False); } { TableExprNodeSetElem tset(True, st, end, True); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTDate, True, True, False, False, False, "", True, True)); checkMatchDate (tset, 54926.0, False); checkMatchDate (tset, 54926.5, True); checkMatchDate (tset, 54940.0, True); checkMatchDate (tset, 54958.5, True); checkMatchDate (tset, 54959.0, False); } { TableExprNodeSetElem tset(False, st); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTDate, True, False, False, False, False, "", False, False)); checkMatchDate (tset, 54926.0, False); checkMatchDate (tset, 54926.5, False); checkMatchDate (tset, 54940.0, True); checkMatchDate (tset, 54958.5, True); checkMatchDate (tset, 54959.0, True); } { TableExprNodeSetElem tset(end, False); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTDate, False, True, False, False, False, "", False, False)); checkMatchDate (tset, 54926.0, True); checkMatchDate (tset, 54926.5, True); checkMatchDate (tset, 54940.0, True); checkMatchDate (tset, 54958.5, False); checkMatchDate (tset, 54959.0, False); } } void doMidWidthDouble() { TableExprNode mid(1); mid.useUnit ("m"); TableExprNode width(200); width.useUnit ("cm"); { TableExprNodeSetElem tset(mid, width); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTDouble, True, True, False, False, False, "m", True, True, True)); checkMatchDouble (tset, -0.005, False); checkMatchDouble (tset, 0., True); checkMatchDouble (tset, 1., True); checkMatchDouble (tset, 2., True); // unit is m checkMatchDouble (tset, 2.005, False); } } void doMidWidthDate() { TableExprNode mid(datetime("5Apr09/12:")); // is MJD 54926.5 TableExprNode width(12); width.useUnit ("h"); { TableExprNodeSetElem tset(mid, width); AlwaysAssertExit (testAttr (tset, TableExprNodeRep::NTDate, True, True, False, False, False, "", True, True, True)); checkMatchDate (tset, 54926.24, False); checkMatchDate (tset, 54926.25, True); checkMatchDate (tset, 54926.5, True); checkMatchDate (tset, 54926.75, True); checkMatchDate (tset, 54926.76, False); } } #define tryError(CMD) \ { \ Bool ok = True; \ try { \ CMD; \ ok = False; \ } catch (const std::exception& x) { \ cout << "Expected: " << x.what() << endl; \ } \ AlwaysAssertExit (ok); \ } void doErrors() { cout << "Trying erroneous commands ..." << endl; Vector vec({1,2}); TableExprNode nullNode; TableExprNode boolNode(True); TableExprNode arrNode(vec); TableExprNode intNode(3); intNode.useUnit("m"); TableExprNode dblNode(3.); dblNode.useUnit("Hz"); TableExprNode cmplNode((Complex())); TableExprNode strNode("aa"); TableExprNode timeNode((MVTime())); tryError (TableExprNodeSetElemDiscrete(arrNode, nullNode, nullNode)); tryError (TableExprNodeSetElemDiscrete(nullNode, arrNode, nullNode)); tryError (TableExprNodeSetElemDiscrete(intNode, nullNode, arrNode)); tryError (TableExprNodeSetElemDiscrete(strNode, nullNode, nullNode)); tryError (TableExprNodeSetElemDiscrete(nullNode, strNode, nullNode)); tryError (TableExprNodeSetElemDiscrete(nullNode, intNode, strNode)); tryError (TableExprNodeSetElemDiscrete(intNode, timeNode, nullNode)); tryError (TableExprNodeSetElemDiscrete(nullNode, nullNode, timeNode)); tryError (TableExprNodeSetElemDiscrete(intNode, dblNode, nullNode)); tryError (TableExprNodeSetElemCont(True, arrNode)); tryError (TableExprNodeSetElemCont(arrNode, True)); tryError (TableExprNodeSetElemCont(True, intNode, arrNode, True)); tryError (TableExprNodeSetElemCont(True, boolNode)); tryError (TableExprNodeSetElemCont(cmplNode, True)); tryError (TableExprNodeSetElemCont(True, intNode, timeNode, True)); tryError (TableExprNodeSetElemCont(True, intNode, dblNode, True)); tryError (TableExprNodeSetElemMidWidth(intNode, strNode)); tryError (TableExprNodeSetElemMidWidth(strNode, intNode)); tryError (TableExprNodeSetElemMidWidth(intNode, nullNode)); tryError (TableExprNodeSetElemMidWidth(nullNode, intNode)); tryError (TableExprNodeSetElemMidWidth(dblNode, intNode)); } int main() { try { doDiscreteBool(); doDiscreteInt(); doDiscreteDouble(); doDiscreteDComplex(); doDiscreteString(); doDiscreteDate(); doIntervalBool(); doIntervalInt(); doIntervalDouble(); doIntervalDComplex(); doIntervalString(); doIntervalDate(); doMidWidthDouble(); doMidWidthDate(); doErrors(); } catch (std::exception& x) { cout << "Unexpected exception: " << x.what() << endl; return 1; } catch (...) { cout << "Unexpected unknown exception" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/tables/TaQL/test/tExprNodeSetOpt.cc000066400000000000000000000314541476623553700217430ustar00rootroot00000000000000//# tExprNodeSetOpt.cc: Test program for the ExprNodeSeOpt classes //# Copyright (C) 2022 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include // // Test program for the ExprNodeSetOpt classes. // // Execute the test for all values in the test vector. template void doTest (TableExprNodeSetOptBase& set, const Vector& testVec, const Vector& expFind) { TableExprId id(0); for (size_t i=0; i= 0)); } // Do the test for the full array. MArray res = set.contains (id, MArray(testVec)); AlwaysAssertExit (allEQ(res.array(), expFind>=Int64(0))); } // Original and transformed set should give the same results. template void doTestOrig (TableExprNodeSet& orig, TableExprNodeSetOptBase& set, const Vector& testVec) { TableExprId id(0); for (T v : testVec) { AlwaysAssertExit (orig.contains(id,v) == set.contains(id,v)); } } void doDoubleContSet() { { // Test closed-closed intervals std::vector st ({1,3,5,7,9,11,13,15,17,19,21}); std::vector end({2,4,6,8,10,12,14,16,18,20,22}); TableExprNodeSetOptContSet,std::less_equal> set (TableExprNodeSet(), st, end, std::less_equal(), std::less_equal(), "CC"); set.show(cout, 2); // Vectors of test values and expected index. Vector vec({-0.5, 1, 1.5, 2, 2.5, 13, 13.5, 14, 14.5, 21, 21.5, 22, 22.5}); Vector exp({-1, 0, 0, 0, -1, 6, 6, 6, -1, 10, 10, 10, -1}); doTest (set, vec, exp); } { // Test open-closed intervals std::vector st ({1,3,5,7,9,11,13,15,17,19,21}); std::vector end({2,4,6,8,10,12,14,16,18,20,22}); TableExprNodeSetOptContSet,std::less_equal> set (TableExprNodeSet(), st, end, std::less(), std::less_equal(), "OC"); set.show(cout, 2); // Vectors of test values and expected index. Vector vec({-0.5, 1, 1.5, 2, 2.5, 13, 13.5, 14, 14.5, 21, 21.5, 22, 22.5}); Vector exp({-1, -1, 0, 0, -1, -1, 6, 6, -1, -1, 10, 10, -1}); doTest (set, vec, exp); } { // Test closed-open intervals std::vector st ({1,3,5,7,9,11,13,15,17,19,21}); std::vector end({2,4,6,8,10,12,14,16,18,20,22}); TableExprNodeSetOptContSet,std::less> set (TableExprNodeSet(), st, end, std::less_equal(), std::less(), "CO"); set.show(cout, 2); // Vectors of test values and expected index. Vector vec({-0.5, 1, 1.5, 2, 2.5, 13, 13.5, 14, 14.5, 21, 21.5, 22, 22.5}); Vector exp({-1, 0, 0, -1, -1, 6, 6, -1, -1, 10, 10, -1, -1}); doTest (set, vec, exp); } { // Test open-open intervals std::vector st ({1,3,5,7,9,11,13,15,17,19,21}); std::vector end({2,4,6,8,10,12,14,16,18,20,22}); TableExprNodeSetOptContSet,std::less> set (TableExprNodeSet(), st, end, std::less(), std::less(), "OO"); set.show(cout, 2); // Vectors of test values and expected index. Vector vec({-0.5, 1, 1.5, 2, 2.5, 13, 13.5, 14, 14.5, 21, 21.5, 22, 22.5}); Vector exp({-1, -1, 0, -1, -1, -1, 6, -1, -1, -1, 10, -1, -1}); doTest (set, vec, exp); } { // Test intervals with mix of open and closed std::vector st ({1, 19, 3, 21}); std::vector end ({2, 20, 5, 22}); std::vector leftC ({false,false,true,true}); std::vector rightC({false,true,true,false}); TableExprNodeSetOptContSetMixOC set (TableExprNodeSet(), st, end, leftC, rightC); set.show(cout, 2); // Vectors of test values and expected index. Vector vec({-0.5, 1, 1.5, 2, 2.5, 3, 4, 5, 5.5, 19, 19.5, 20, 20.5, 21, 21.5, 22, 22.5}); Vector exp ({-1, -1, 0, -1, -1, 2, 2, 2, -1, -1, 1, 1, -1, 3, 3, -1, -1}); doTest (set, vec, exp); } } void doStringContSet() { std::vector st ({"a1"}); std::vector end({"a5"}); { // Test closed-closed intervals TableExprNodeSetOptContSet,std::less_equal> set (TableExprNodeSet(), st, end, std::less_equal(), std::less_equal(), "CC"); set.show(cout, 2); // Vectors of test values and expected index. Vector vec({"a0", "a1", "a3", "a5", "a6"}); Vector exp({ -1, 0, 0, 0, -1}); doTest (set, vec, exp); } { // Test closed-open intervals TableExprNodeSetOptContSet,std::less> set (TableExprNodeSet(), st, end, std::less_equal(), std::less(), "CO"); set.show(cout, 2); // Vectors of test values and expected index. Vector vec({"a0", "a1", "a3", "a5", "a6"}); Vector exp({ -1, 0, 0, -1, -1}); doTest (set, vec, exp); } { // Test open-closed intervals TableExprNodeSetOptContSet,std::less_equal> set (TableExprNodeSet(), st, end, std::less(), std::less_equal(), "OC"); set.show(cout, 2); // Vectors of test values and expected index. Vector vec({"a0", "a1", "a3", "a5", "a6"}); Vector exp({ -1, -1, 0, 0, -1}); doTest (set, vec, exp); } { // Test open-open intervals TableExprNodeSetOptContSet,std::less> set (TableExprNodeSet(), st, end, std::less(), std::less(), "OO"); set.show(cout, 2); // Vectors of test values and expected index. Vector vec({"a0", "a1", "a3", "a5", "a6"}); Vector exp({ -1, -1, 0, -1, -1}); doTest (set, vec, exp); } } void doIntSet() { Vector vecset({1,3,8,10,5}); TableExprNodeSetOptUSet set(TableExprNodeSet(), vecset); set.show(cout, 2); // Vectors of test values and expected index. Vector vec({1, 3, 5, 8, 10, 0, 2, 11}); Vector exp({0, 1, 4, 2, 3, -1,-1, -1}); doTest (set, vec, exp); } void doStringSet() { Vector vecset({"a", "d", "b"}); TableExprNodeSetOptUSet set(TableExprNodeSet(), vecset); set.show(cout, 2); // Vectors of test values and expected index. Vector vec({"aa", "a", "b", "d", "bd", "e"}); Vector exp({ -1, 0, 2, 1, -1, -1}); doTest (set, vec, exp); } void doDoubleTransform() { TableExprNode st1(1), st2(6), st3(25), st4(30), st5(33); TableExprNode end1(8), end2(20), end3(30), end4(33), end5(34); // Test with different leftC and rightC. // Also such that the 3rd interval is combined with the 4th, // but not 4th with 5th (because end4 and st5 are open). { TableExprNodeSet set; set.add (TableExprNodeSetElem(False, st1, end1, False)); set.add (TableExprNodeSetElem(True, st3, end3, True)); set.add (TableExprNodeSetElem(False, st2, end2, True)); set.add (TableExprNodeSetElem(True, st1+1, end2+1, False)); set.add (TableExprNodeSetElem(False, st4, end4, False)); set.add (TableExprNodeSetElem(False, st5, end5, True)); // Vectors of test values and expected index. Vector vec({ 0, 1, 2, 6, 19, 21, 23, 25, 26, 31, 33, 33.5, 34, 34.1}); Vector exp({-1,-1, 0, 0, 0, -1, -1, 1, 1, 1, -1, 2, 2, -1}); { // No combine, thus 6 intervals with different leftC/rightC. TENShPtr trSet = TableExprNodeSetOptContSetBase::transform (set, False); TableExprNodeSetOptContSetMixOC* p = dynamic_cast*>(trSet.get()); AlwaysAssertExit (p); AlwaysAssertExit (p->size() == 6); trSet->show (cout, 0); doTestOrig (set, *p, vec); } // Should result in (1,21) [25,33) (33,34] TENShPtr trSet = TableExprNodeSetOptContSetBase::transform (set); TableExprNodeSetOptContSetMixOC* p = dynamic_cast*>(trSet.get()); AlwaysAssertExit (p); AlwaysAssertExit (p->size() == 3); trSet->show (cout, 0); doTest (*p, vec, exp); doTestOrig (set, *p, vec); } // Test with equal leftC and rightC (all combinations). for (int i=0; i<4; ++i) { TableExprNodeSet set; set.add (TableExprNodeSetElem(i/2==0, st1, end1, i%2==0)); set.add (TableExprNodeSetElem(i/2==0, st3, end3, i%2==0)); set.add (TableExprNodeSetElem(i/2==0, st2, end2, i%2==0)); set.add (TableExprNodeSetElem(i/2==0, st4, end4, i%2==0)); set.add (TableExprNodeSetElem(i/2==0, st5, end5, i%2==0)); TENShPtr trSet = TableExprNodeSetOptContSetBase::transform (set); TableExprNodeSetOptContSetBase* p = dynamic_cast*>(trSet.get()); // Results in 2 elements, but 4 when left and right side are open. trSet->show (cout, 0); AlwaysAssertExit (p->size() == (i==3 ? 4:2)); // Vectors of test values and expected index (depending on open/closed). Int64 i3 = (i==3 ? 2 : 1); Int64 i4 = (i==3 ? 3 : 1); Int64 l1 = (i/2==0 ? 0 : -1); Int64 l2 = (i/2==0 ? 1 : -1); Int64 r1 = (i%2==0 ? 0 : -1); Int64 r2 = (i!=3 ? 1 : -1); Int64 r3 = (i%2==0 ? i3 : -1); Vector vec({ 0, 1, 2, 6, 19, 20, 23, 25, 26, 30, 31, 33, 33.5, 34, 34.1}); Vector exp({-1,l1, 0, 0, 0, r1, -1, l2, 1, r2, i3, r2, i4, r3, -1}); doTest (*p, vec, exp); doTestOrig (set, *p, vec); } } void doDateTransform() { TableExprNode st(datetime("5Apr09/12:")); // MJD 54926.5 TableExprNode end(datetime("7May09/12:")); // MJD 54958.5 TableExprNode width(4); // 4 days { TableExprNodeSet set; set.add (TableExprNodeSetElem(False, st, end, False)); TENShPtr trSet = TableExprNodeSetOptContSetBase::transform (set); TableExprNodeSetOptContSetBase* p = dynamic_cast*>(trSet.get()); AlwaysAssertExit (p); AlwaysAssertExit (p->size() == 1); trSet->show (cout, 0); Vector vec({54926.5, 54926.51, 54934, 54958.49,54958.5}); Vector exp({ -1, 0, 0, 0, -1}); doTest (*p, vec, exp); doTestOrig (set, *p, vec); } { TableExprNodeSet set; set.add (TableExprNodeSetElem(st, width)); TENShPtr trSet = TableExprNodeSetOptContSetBase::transform (set); TableExprNodeSetOptContSetBase* p = dynamic_cast*>(trSet.get()); AlwaysAssertExit (p); AlwaysAssertExit (p->size() == 1); trSet->show (cout, 0); Vector vec({54924.49, 54924.5, 54926, 54928.5, 54928.51}); Vector exp({ -1, 0, 0, 0, -1}); doTest (*p, vec, exp); doTestOrig (set, *p, vec); } } int main() { try { doDoubleContSet(); doStringContSet(); doIntSet(); doStringSet(); doDoubleTransform(); doDateTransform(); } catch (std::exception& x) { cout << "Unexpected exception: " << x.what() << endl; return 1; } catch (...) { cout << "Unexpected unknown exception" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/tables/TaQL/test/tExprNodeUDF.cc000066400000000000000000000133711476623553700211410ustar00rootroot00000000000000//# tExprUDFNode.cc: Test program for class TableExprUDFNode //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include using namespace casacore; class TestUDF: public UDFBase { public: TestUDF() {} static UDFBase* makeObject (const String&) { return new TestUDF(); } virtual void setup (const Table&, const TaQLStyle&) { AlwaysAssert (operands().size() == 1, AipsError); AlwaysAssert (operands()[0]->dataType() == TableExprNodeRep::NTInt, AipsError); AlwaysAssert (operands()[0]->valueType() == TableExprNodeRep::VTScalar, AipsError); setDataType (TableExprNodeRep::NTBool); setNDim (0); //scalar } Bool getBool (const TableExprId& id) {return operands()[0]->getInt(id) == 1;} }; class TestUDFAggr: public UDFBase { public: TestUDFAggr() {} static UDFBase* makeObject (const String&) { return new TestUDFAggr(); } virtual void setup (const Table&, const TaQLStyle&) { AlwaysAssert (operands().size() == 1, AipsError); AlwaysAssert (operands()[0]->dataType() == TableExprNodeRep::NTInt, AipsError); AlwaysAssert (operands()[0]->valueType() == TableExprNodeRep::VTScalar, AipsError); setDataType (TableExprNodeRep::NTInt); setNDim (0); // scalar setAggregate (True); // aggregate function } Int64 getInt (const TableExprId& id) { const TableExprIdAggr& aid = TableExprIdAggr::cast (id); const std::vector& ids = aid.result().ids(id.rownr()); Int64 sum3 = 0; for (std::vector::const_iterator it=ids.begin(); it!=ids.end(); ++it){ Int64 v = operands()[0]->getInt(*it); sum3 += v*v*v; } return sum3; } }; void makeTable() { TableDesc td; td.addColumn (ScalarColumnDesc("ANTENNA1")); SetupNewTable newtab("tExprNodeUDF_tmp.tab", td, Table::New); Table tab(newtab); ScalarColumn ant1(tab, "ANTENNA1"); tab.addRow (10); for (uInt i=0; i(node2.getRep().get()); std::vector aggrNodes = TableExprNodeUtil::getAggrNodes (rep); AlwaysAssertExit (aggrNodes.size() == 1); AlwaysAssertExit (aggrNodes[0]->isLazyAggregate()); std::shared_ptr> ids(new std::vector()); for (uInt i=0; ipush_back (TableExprId(i)); } std::vector>> idVec(1, ids); std::vector> funcVec(1); std::shared_ptr res (new TableExprGroupResult(funcVec, idVec)); TableExprIdAggr aid(res); aid.setRownr (0); Int64 val = node2.getInt(aid); cout << "aggregated value=" << val << endl; Vector colval (ScalarColumn(tab, "ANTENNA1").getColumn()); AlwaysAssertExit (val == sum(colval*colval*colval)); } } catch (std::exception& x) { cout << "Unexpected exception " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/tables/TaQL/test/tExprUnitNode.cc000066400000000000000000000206031476623553700214360ustar00rootroot00000000000000//# tExprUnitNode.cc: Test program for unit handling in selection expressions //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include // // Test program for unit handling in selection expressions. // Bool foundError = False; void checkScaBool (const String& str, const TableExprId& exprid, const TableExprNode& expr, const Bool& value) { cout << "checkScaBool " << str << endl; AlwaysAssertExit (expr.dataType() == TpBool); AlwaysAssertExit (expr.unit().getName().empty()); Bool val; expr.get (exprid, val); if (val != value) { foundError = True; cout << str << ": found value " << val << "; expected " << value << endl; } } void checkScaInt (const String& str, const TableExprId& exprid, const TableExprNode& expr, const Int& value) { cout << "checkScaInt " << str << endl; AlwaysAssertExit (expr.dataType() == TpInt64); AlwaysAssertExit (expr.unit().getName().empty()); Int64 val; expr.get (exprid, val); if (val != value) { foundError = True; cout << str << ": found value " << val << "; expected " << value << endl; } } void checkScaDouble (const String& str, const TableExprId& exprid, const TableExprNode& expr, const Double& value, const String& unit) { cout << "checkScaDouble " << str << ' ' << expr.unit().getName() << endl; AlwaysAssertExit (expr.dataType() == TpDouble); AlwaysAssertExit (expr.unit().getName() == unit); Double val; expr.get (exprid, val); if (!near (val, value, 1.e-10)) { foundError = True; cout << str << ": found value " << val << "; expected " << value << endl; } } #define checkFailure(STR,EXPR)\ {\ bool failed = False;\ try {\ TableExprNode n(EXPR);\ } catch (std::exception&) {\ failed = True;\ }\ if (!failed) {\ cout << STR << ": was expected to fail, but did not" << endl;\ }\ } void doIt() { TableExprNode e30(30); e30 = e30.useUnit("deg"); TableExprNode e1(2); checkScaInt ("int 2", 0, e1, 2); TableExprNode e2 = e1.useUnit (Unit("cm")); checkScaDouble ("2 cm 1", 0, e1, 2, "cm"); checkScaDouble ("2 cm 2", 0, e2, 2, "cm"); TableExprNode e3 = e2.useUnit (Unit("m")); checkScaDouble ("2 cm 3", 0, e2, 2, "cm"); checkScaDouble ("2 cm m", 0, e3, 0.02, "m"); // Math checkScaDouble ("1+e3", 0, 1+e3, 1.02, "m"); checkScaDouble ("e2+e3", 0, e2+e3, 4, "cm"); checkScaDouble ("e3-2*e2", 0, e3-2*e2, -0.02, "m"); checkScaDouble ("e2*e3", 0, e2*e3, 0.04, "cm.m"); checkScaDouble ("e3*e2", 0, e3*e2, 0.04, "m.cm"); checkScaDouble ("e3/e2", 0, e3/e2, 1., ""); checkScaDouble ("e3%e2", 0, e3%e2, 0., "m"); // Comparison checkScaBool ("e3*e2==e2*e3", 0, e3*e2==e2*e3, True); checkScaBool ("e2==e3", 0, e2+0.00001==e3, False); checkScaBool ("e2>=e3", 0, e2+0.00001>=e3, True); checkScaBool ("e2>e3", 0, e2+0.00001>e3, True); checkScaBool ("e2<=e3", 0, e2-0.00001<=e3, True); checkScaBool ("e2 #include #include #include #include #include #include using namespace casacore; using namespace std; void testExcp() { cout << "Testing MArray exceptions ..." << endl; Array arr(Array(IPosition(1,1))); Bool err = False; try { MArray a1(arr, Array(IPosition(1,2))); } catch (const std::exception& x) { err = True; cout << x.what() << endl; } AlwaysAssertExit (err); err = False; try { MArray a1(arr, Array(IPosition(1,1)), True); } catch (const std::exception& x) { err = True; cout << x.what() << endl; } AlwaysAssertExit (err); err = False; try { MArray a1(arr); a1.setMask (Array(IPosition(1,2))); } catch (const std::exception& x) { err = True; cout << x.what() << endl; } } void testNull() { cout << "Testing null MArray ..." << endl; // Create null array. MArray a1; AlwaysAssertExit (a1.isNull()); AlwaysAssertExit (a1.empty()); AlwaysAssertExit (! a1.hasMask()); AlwaysAssertExit (a1.shape().empty()); AlwaysAssertExit (a1.ndim() == 0); AlwaysAssertExit (a1.size() == 0); // Resize it. a1.resize (IPosition(2,3,4), False); AlwaysAssertExit (! a1.isNull()); AlwaysAssertExit (! a1.hasMask()); AlwaysAssertExit (! a1.shape().empty()); AlwaysAssertExit (a1.ndim() == 2); AlwaysAssertExit (a1.size() == 12); AlwaysAssertExit (a1.nelements() == 12); // Copy constructor. MArray a2(a1); AlwaysAssertExit (! a2.isNull()); AlwaysAssertExit (! a2.hasMask()); AlwaysAssertExit (! a2.shape().empty()); AlwaysAssertExit (a2.ndim() == 2); AlwaysAssertExit (a2.size() == 12); AlwaysAssertExit (a2.array().data() == a1.array().data()); // Assignment. MArray a3(Array(IPosition(2,3,4), 1), Array(IPosition(2,3,4), True)); a1 = a3; AlwaysAssertExit (! a1.isNull()); AlwaysAssertExit (a1.hasMask()); AlwaysAssertExit (a1.ndim() == 2); AlwaysAssertExit (a1.size() == 12); AlwaysAssertExit (allEQ (a1.array(), 1)); AlwaysAssertExit (allTrue (a1.mask())); AlwaysAssertExit (a2.array().data() == a1.array().data()); AlwaysAssertExit (allEQ (a2.array(), 1)); AlwaysAssertExit (! a2.hasMask()); // Reference. a1.reference (MArray()); AlwaysAssertExit (a1.isNull()); AlwaysAssertExit (! a1.hasMask()); AlwaysAssertExit (a1.shape().empty()); AlwaysAssertExit (a1.ndim() == 0); AlwaysAssertExit (a1.size() == 0); AlwaysAssertExit (allEQ (a2.array(), 1)); // Empty array. MArray a4(Array(), Array(), False); AlwaysAssertExit (! a4.isNull()); AlwaysAssertExit (a4.empty()); AlwaysAssertExit (! a4.hasMask()); AlwaysAssertExit (a4.shape().empty()); AlwaysAssertExit (a4.ndim() == 0); AlwaysAssertExit (a4.size() == 0); } void testMask() { cout << "Testing masked MArray ..." << endl; IPosition shp(3,5,4,3); Array arr(shp); indgen(arr); Array maskArr(arr%3 == 0); Array maskArr2(arr%5 == 0); // Create with a mask. MArray a1(arr, maskArr); AlwaysAssertExit (! a1.isNull()); AlwaysAssertExit (a1.hasMask()); AlwaysAssertExit (a1.shape() == shp); AlwaysAssertExit (a1.ndim() == 3); AlwaysAssertExit (a1.size() == 60); AlwaysAssertExit (a1.nvalid() == 40); AlwaysAssertExit (a1.array().data() == arr.data()); AlwaysAssertExit (a1.mask().data() == maskArr.data()); // Create another array. MArray a2(arr.copy(), maskArr2.copy()); indgen (a2.array(), 100); AlwaysAssertExit (a2.array().data() != arr.data()); AlwaysAssertExit (a2.mask().data() != maskArr2.data()); // Test copy constructor. MArray a3(a2); AlwaysAssertExit (! a3.isNull()); AlwaysAssertExit (a3.hasMask()); AlwaysAssertExit (a3.shape() == shp); AlwaysAssertExit (a3.ndim() == 3); AlwaysAssertExit (a3.size() == 60); AlwaysAssertExit (a3.nvalid() == 48); AlwaysAssertExit (a3.array().data() == a2.array().data()); AlwaysAssertExit (a3.mask().data() == a2.mask().data()); // Test assignment to empty array. MArray a4; AlwaysAssertExit (a4.isNull()); AlwaysAssertExit (! a4.hasMask()); a4 = a1; AlwaysAssertExit (! a4.isNull()); AlwaysAssertExit (a4.hasMask()); AlwaysAssertExit (a4.shape() == shp); AlwaysAssertExit (a4.ndim() == 3); AlwaysAssertExit (a4.size() == 60); AlwaysAssertExit (a4.nvalid() == 40); AlwaysAssertExit (allEQ (a4.array(), a1.array())); AlwaysAssertExit (allEQ (a4.mask(), a1.mask())); // Test assignment to non-empty array. a4 = a3; AlwaysAssertExit (allEQ (a4.array(), a2.array())); AlwaysAssertExit (allEQ (a4.mask(), a2.mask())); AlwaysAssertExit (a4.array().data() != a2.array().data()); AlwaysAssertExit (a4.mask().data() != a2.mask().data()); // Test reference. a4.reference (a3); AlwaysAssertExit (! a4.isNull()); AlwaysAssertExit (a4.hasMask()); AlwaysAssertExit (a4.shape() == shp); AlwaysAssertExit (a4.ndim() == 3); AlwaysAssertExit (a4.size() == 60); AlwaysAssertExit (a4.nvalid() == 48); AlwaysAssertExit (a4.array().data() == a2.array().data()); AlwaysAssertExit (a4.mask().data() == a2.mask().data()); a4.reference (MArray()); AlwaysAssertExit (a4.isNull()); AlwaysAssertExit (! a4.hasMask()); AlwaysAssertExit (a4.shape() == IPosition()); AlwaysAssertExit (a4.ndim() == 0); AlwaysAssertExit (a4.size() == 0); AlwaysAssertExit (a4.nvalid() == 0); // Test flatten. Vector flat = a1.flatten(); AlwaysAssertExit (flat.size() == 40); AlwaysAssertExit (sum(flat) == (1+58+2+59)*20/2); // Test combineMask Array cmask(a1.combineMask(a2)); AlwaysAssertExit (allEQ(cmask, arr%3==0 || arr%5==0)); // Test removeMask. a3.removeMask(); AlwaysAssertExit (! a3.isNull()); AlwaysAssertExit (! a3.hasMask()); AlwaysAssertExit (a3.shape() == shp); AlwaysAssertExit (a3.ndim() == 3); AlwaysAssertExit (a3.size() == 60); AlwaysAssertExit (a3.nvalid() == 60); } void testFill() { cout << "Testing MArray fill ..." << endl; IPosition shp1(3,5,4,3); Array arr1(shp1); indgen(arr1); IPosition shp2(2,6,2); Array arr2(shp2); indgen(arr2, 100); Array maskArr(arr1%3 == 0); // Create. MArray a1(arr1, maskArr); MArray a2(arr2); MArray a3; AlwaysAssertExit (a3.isNull()); AlwaysAssertExit (! a3.hasMask()); // Test fill from another MArray with another type. a3.fill (a1); AlwaysAssertExit (! a3.isNull()); AlwaysAssertExit (a3.hasMask()); AlwaysAssertExit (a3.shape() == shp1); AlwaysAssertExit (a3.mask().data() == a1.mask().data()); AlwaysAssertExit (sum(a3) == sum(a1)); // Test fill from another MArray with the same type. a1.fill (a2); AlwaysAssertExit (! a1.isNull()); AlwaysAssertExit (! a1.hasMask()); AlwaysAssertExit (a1.shape() == shp2); AlwaysAssertExit (sum(a1) == sum(a2)); AlwaysAssertExit (a1.array().data() != a2.array().data()); AlwaysAssertExit (allEQ(a1.array(), a2.array())); AlwaysAssertExit (a1.mask().data() == a2.mask().data()); a1.fill (Array()); AlwaysAssertExit (! a1.isNull()); AlwaysAssertExit (! a1.hasMask()); AlwaysAssertExit (a1.shape().empty()); AlwaysAssertExit (a1.empty()); } void testSlice() { cout << "Testing MArray slice ..." << endl; IPosition shp(3,5,4,3); Array arr(shp); indgen(arr); Array maskArr(arr%3 == 0); MArray a1(arr, maskArr); // Create slice. IPosition st(3,1,1,1); IPosition end(3,4,2,1); IPosition incr(3,2,1,1); MArray a2(a1(st, end, incr)); AlwaysAssertExit (a2.hasMask()); AlwaysAssertExit (a2.size() == 4); AlwaysAssertExit (a2.nvalid() == 3); AlwaysAssertExit (allEQ(a2.array(), arr(st, end, incr))); AlwaysAssertExit (allEQ(a2.mask(), maskArr(st, end, incr))); } int main() { try { testExcp(); testNull(); testMask(); testFill(); testSlice(); } catch (std::exception& x) { cout << "Unexpected exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/tables/TaQL/test/tMArrayMath.cc000066400000000000000000001001531476623553700210560ustar00rootroot00000000000000//# tMArrayMath.cc: test program for MArrayMath and MArrayLogical //# Copyright (C) 2012 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include using namespace casacore; using namespace std; template void check (const MArray& ma, const T& v, Bool m, Bool unmasked=False) { // Check if data and mask have the expected value. AlwaysAssertExit (allEQ(ma.array(), v)); AlwaysAssertExit (ma.hasMask() == !unmasked); if (!unmasked) { AlwaysAssertExit (allEQ(ma.mask(), m)); } } template void checkNear (const MArray& ma, const T& v, Bool m, Bool empty=False) { // Check if data is near the expected value. AlwaysAssertExit (allNear(ma.array(), v, 1e-5)); AlwaysAssertExit (empty == ma.mask().empty()); if (!empty) { AlwaysAssertExit (allEQ(ma.mask(), m)); } } template void doTestAll() { // Test most arithmetic and logical operators as array-array, // array-scalar, and scalar-array (with and without mask). MArray m1 (Vector(2,16), Vector(2,True)); MArray m2 (Vector(2,8)); check (m1+m2, T(16+8), True); check (m1-m2, T(16-8), True); check (m1*m2, T(16*8), True); check (m1/m2, T(16)/T(8), True); check (m1==m2, T(16)==T(8), True); check (m1>=m2, T(16)>=T(8), True); check (m1>m2, T(16)>T(8), True); check (m1<=m2, T(16)<=T(8), True); check (m1=T(4), T(16)>=T(4), True); check (m1>T(4), T(16)>T(4), True); check (m1<=T(4), T(16)<=T(4), True); check (m1=m2, T(4)>=T(8), True, True); check (T(4)>m2, T(4)>T(8), True, True); check (T(4)<=m2, T(4)<=T(8), True, True); check (T(4) void doTestInt() { // Test operators only useful for int. MArray m1 (Vector(2,11), Vector(2,True)); MArray m2 (Vector(2,7)); check (m1%m2, T(11%7), True); check (m1&m2, T(11&7), True); check (m1|m2, T(11|7), True); check (m1^m2, T(11^7), True); check (m1%T(6), T(11%6), True); check (m1&T(6), T(11&6), True); check (m1|T(6), T(11|6), True); check (m1^T(6), T(11^6), True); check (T(5)%m2, T(5%7), True, True); check (T(5)&m2, T(5&7), True, True); check (T(5)|m2, T(5|7), True, True); check (T(5)^m2, T(5^7), True, True); check (~m1, T(~11), True); } void doTestBool() { // Test operators only useful for bool. MArray m1 (Vector(2,True), Vector(2,True)); MArray m2 (Vector(2,False)); check (m1||m2, True, True); check (m1&&m2, False, True); check (m1||True, True, True); check (m1&&True, True, True); check (False||m2, False, True, True); check (False&&m2, False, True, True); } template void doTestFloat() { // Test all functions for real values. MArray m1 (Vector(2,0.7), Vector(2,False)); MArray m2 (Vector(2,0.3)); checkNear (sqrt(m1), T(sqrt(0.7)), False); checkNear (square(m1), T(square(0.7)), False); checkNear (cube(m1), T(cube(0.7)), False); checkNear (sin(m1), T(sin(0.7)), False); checkNear (cos(m1), T(cos(0.7)), False); checkNear (tan(m1), T(tan(0.7)), False); checkNear (asin(m1), T(asin(0.7)), False); checkNear (acos(m1), T(acos(0.7)), False); checkNear (atan(m1), T(atan(0.7)), False); checkNear (sinh(m1), T(sinh(0.7)), False); checkNear (cosh(m1), T(cosh(0.7)), False); checkNear (tanh(m1), T(tanh(0.7)), False); checkNear (exp(m1), T(exp(0.7)), False); checkNear (log(m1), T(log(0.7)), False); checkNear (log10(m1), T(log10(0.7)), False); checkNear (abs(m1), T(abs(0.7)), False); checkNear (sign(m1), T(sign(0.7)), False); checkNear (round(m1), T(round(0.7)), False); checkNear (floor(m1), T(floor(0.7)), False); checkNear (ceil(m1), T(ceil(0.7)), False); checkNear (atan2(m2,m1), T(atan2(0.3,0.7)), False); checkNear (atan2(m2,T(5)), T(atan2(0.3,5.)), False, True); checkNear (atan2(T(6),m1), T(atan2(6.,0.7)), False); checkNear (pow(m2,m1), T(pow(0.3,0.7)), False); checkNear (pow(m2,5.), T(pow(0.3,5.)), False, True); checkNear (pow(T(6),m1), T(pow(6.,0.7)), False); checkNear (fmod(m2,m1), T(fmod(0.3,0.7)), False); checkNear (fmod(m2,T(5)), T(fmod(0.3,5.)), False, True); checkNear (fmod(T(6),m1), T(fmod(6.,0.7)), False); check (near(m1,m2,1e-5), near(T(0.7),T(0.3),1e-5), False); check (nearAbs(m1,m2,1e-5), nearAbs(T(0.7),T(0.3),1e-5), False); check (near(m1,T(4),1e-5), near(T(0.7),T(4),1e-5), False); check (nearAbs(m1,T(4),1e-5), nearAbs(T(0.7),T(4),1e-5), False); check (near(T(4),m2,1e-5), near(T(4),T(0.3),1e-5), False, True); check (nearAbs(T(4),m2,1e-5), nearAbs(T(4),T(0.3),1e-5), False, True); check (isNaN(m2), False, False, True); check (isInf(m1), False, False); check (isFinite(m2), True, False, True); } template void doTestComplex() { // Test functions for complex values. T val(0.7, -0.3); MArray m1 (Vector(2,val)); for (int i=0; i<2; ++i) { checkNear (sqrt(m1), T(sqrt(val)), False, i==0); checkNear (square(m1), T(square(val)), False, i==0); checkNear (sin(m1), T(sin(val)), False, i==0); checkNear (cos(m1), T(cos(val)), False, i==0); checkNear (tan(m1), T(tan(val)), False, i==0); checkNear (asin(m1), T(asin(val)), False, i==0); checkNear (acos(m1), T(acos(val)), False, i==0); checkNear (atan(m1), T(atan(val)), False, i==0); checkNear (sinh(m1), T(sinh(val)), False, i==0); checkNear (cosh(m1), T(cosh(val)), False, i==0); checkNear (tanh(m1), T(tanh(val)), False, i==0); checkNear (conj(m1), conj(val), False, i==0); checkNear (real(m1), real(val), False, i==0); checkNear (imag(m1), imag(val), False, i==0); checkNear (amplitude(m1), abs(val), False, i==0); checkNear (phase(m1), arg(val), False, i==0); m1.setMask (Vector(2,False)); } } template void doTestComplexReal() { // Test pow function for mix of complex array and real scalar. T val(1.7); std::complex valc(1,2); MArray> m1 (Vector>(2, valc)); for (int i=0; i<2; ++i) { checkNear (pow(m1,val), std::pow(valc,val), False, i==0); m1.setMask (Vector(2,False)); } } void doTestMixed() { // Test masking more thoroughly. Vector m1(4); m1[0]=False; m1[1]=True; m1[2]=True; m1[3]=False; Vector m2(4); m2[0]=True; m2[1]=False; m2[2]=True; m2[3]=False; Vector v1(4); v1[0]=3; v1[1]=5; v1[2]=-2; v1[3]=-5; Vector v2(4); v2[0]=-4; v2[1]=8; v2[2]=9; v2[3]=-3; MArray ma1(v1, m1); MArray ma2(v2, m2); MArray ma3 = ma1+ma2; Vector m3(ma3.mask()); Vector v3(ma3.array()); AlwaysAssertExit (m3[0] && m3[1] && m3[2] && !m3[3]); AlwaysAssertExit (v3[0]==-1 && v3[1]==13 && v3[2]==7 && v3[3]==-8); AlwaysAssertExit (sum(ma1) == -2); AlwaysAssertExit (sum(ma2) == 5); AlwaysAssertExit (sumsqr(ma1) == 34); AlwaysAssertExit (sumsqr(ma2) == 73); AlwaysAssertExit (product(ma1) == -15); AlwaysAssertExit (product(ma2) == -24); AlwaysAssertExit (min(ma1) == -5); AlwaysAssertExit (min(ma2) == -3); AlwaysAssertExit (max(ma1) == 3); AlwaysAssertExit (max(ma2) == 8); } void doTestReduce() { // Test the full reduction functions. Vector v(20); indgen(v); Vector m(20, False); MArray ma(v, m); Double mn = mean(ma); AlwaysAssertExit (near(mean(ma), mean(v))); AlwaysAssertExit (near(variance(ma, 1), variance(v))); AlwaysAssertExit (near(variance(ma, mn, 1), variance(v, mn))); AlwaysAssertExit (near(stddev(ma, 1), stddev(v))); AlwaysAssertExit (near(stddev(ma, mn, 1), stddev(v, mn))); AlwaysAssertExit (near(avdev(ma), avdev(v))); AlwaysAssertExit (near(avdev(ma, mn), avdev(v, mn))); AlwaysAssertExit (near(rms(ma), rms(v))); AlwaysAssertExit (near(median(ma), median(v))); AlwaysAssertExit (near(fractile(ma, 0.4), fractile(v, 0.4))); Vector vec = ma.flatten(); AlwaysAssertExit (allEQ(v, vec)); m[0] = m[18] = True; ma.setMask (m); Vector v1(18); indgen (v1, 1.); v1[17] = 19; mn = mean(ma); AlwaysAssertExit (near(mean(ma), mean(v1))); AlwaysAssertExit (near(variance(ma, 1), variance(v1))); AlwaysAssertExit (near(variance(ma, mn, 1), variance(v1, mn))); AlwaysAssertExit (near(stddev(ma, 1), stddev(v1))); AlwaysAssertExit (near(stddev(ma, mn, 1), stddev(v1, mn))); AlwaysAssertExit (near(avdev(ma), avdev(v1))); AlwaysAssertExit (near(avdev(ma, mn), avdev(v1, mn))); AlwaysAssertExit (near(rms(ma), rms(v1))); AlwaysAssertExit (near(median(ma), median(v1))); AlwaysAssertExit (near(fractile(ma, 0.4), fractile(v1, 0.4))); Vector vec1 = ma.flatten(); AlwaysAssertExit (allEQ(v1, vec1)); } void doTestPartial() { // Test the partial reduction functions. Cube arr(2,3,4); Cube mask(2,3,4); arr = 1; mask = False; arr(1,1,1) = 101; // First do MArray tests without a a mask. // The result must be equal to the Array counterpart. AlwaysAssertExit (allEQ(partialSums(MArray(arr), IPosition(1,0)).array(), partialSums (arr, IPosition(1,0)))); AlwaysAssertExit (allEQ(partialSums(MArray(arr), IPosition(1,1)).array(), partialSums (arr, IPosition(1,1)))); AlwaysAssertExit (allEQ(partialSums(MArray(arr), IPosition(1,2)).array(), partialSums (arr, IPosition(1,2)))); AlwaysAssertExit (allEQ(partialSums(MArray(arr,mask), IPosition(2,0,1)).array(), partialSums(arr, IPosition(2,0,1)))); AlwaysAssertExit (allEQ(partialSums(MArray(arr,mask), IPosition(2,0,2)).array(), partialSums(arr, IPosition(2,0,2)))); AlwaysAssertExit (allEQ(partialSums(MArray(arr,mask), IPosition(2,1,2)).array(), partialSums(arr, IPosition(2,1,2)))); AlwaysAssertExit (allEQ(partialSums(MArray(arr,mask), IPosition(3,0,1,2)).array(), partialSums(arr, IPosition(3,0,1,2)))); AlwaysAssertExit (allEQ(partialSumSqrs(MArray(arr), IPosition(1,1)).array(), partialSumSqrs (arr, IPosition(1,1)))); AlwaysAssertExit (allEQ(partialProducts(MArray(arr), IPosition(1,1)).array(), partialProducts (arr, IPosition(1,1)))); AlwaysAssertExit (allEQ(partialMins(MArray(arr), IPosition(1,1)).array(), partialMins (arr, IPosition(1,1)))); AlwaysAssertExit (allEQ(partialMaxs(MArray(arr), IPosition(1,1)).array(), partialMaxs (arr, IPosition(1,1)))); AlwaysAssertExit (allEQ(partialMeans(MArray(arr), IPosition(1,1)).array(), partialMeans (arr, IPosition(1,1)))); AlwaysAssertExit (allEQ(partialVariances(MArray(arr), IPosition(1,1), 1).array(), partialVariances (arr, IPosition(1,1)))); AlwaysAssertExit (allEQ(partialStddevs(MArray(arr), IPosition(1,1), 1).array(), partialStddevs (arr, IPosition(1,1), 1))); AlwaysAssertExit (allEQ(partialAvdevs(MArray(arr), IPosition(1,1)).array(), partialAvdevs (arr, IPosition(1,1)))); AlwaysAssertExit (allEQ(partialRmss(MArray(arr), IPosition(1,1)).array(), partialRmss (arr, IPosition(1,1)))); AlwaysAssertExit (allEQ(partialMedians(MArray(arr), IPosition(1,1)).array(), partialMedians (arr, IPosition(1,1)))); AlwaysAssertExit (allEQ(partialFractiles(MArray(arr), IPosition(1,1), 0.6).array(), partialFractiles (arr, IPosition(1,1), 0.6))); // Now do MArray tests with a mask. mask(0,2,3) = mask(1,2,3) = mask(0,1,2) = True; Matrix ares(3,4); ares = 2; ares(1,1) = 102; ares(1,2) = 1; ares(2,3) = 0; Matrix mres(3,4); mres = False; mres(2,3) = True; MArray ma(partialSums(MArray(arr,mask), IPosition(1,0))); AlwaysAssertExit (allEQ(ma.array(), ares) && allEQ(ma.mask(), mres)); ares = 2; ares(1,1) = 1+101*101; ares(1,2) = 1; ares(2,3) = 0; ma = partialSumSqrs(MArray(arr,mask), IPosition(1,0)); AlwaysAssertExit (allEQ(ma.array(), ares) && allEQ(ma.mask(), mres)); ares = 1; ares(2,3) = 0; ma = partialMins(MArray(arr,mask), IPosition(1,0)); AlwaysAssertExit (allEQ(ma.array(), ares) && allEQ(ma.mask(), mres)); ares = 1; ares(1,1) = 101; ares(2,3) = 0; ma = partialMaxs(MArray(arr,mask), IPosition(1,0)); AlwaysAssertExit (allEQ(ma.array(), ares) && allEQ(ma.mask(), mres)); ares = 1; ares(1,1) = 51; ares(2,3) = 0; ma = partialMeans(MArray(arr,mask), IPosition(1,0)); AlwaysAssertExit (allEQ(ma.array(), ares) && allEQ(ma.mask(), mres)); ares = 1; ares(1,1) = 51; ares(2,3) = 0; ma = partialMedians(MArray(arr,mask), IPosition(1,0), True); AlwaysAssertExit (allEQ(ma.array(), ares) && allEQ(ma.mask(), mres)); ares(1,1) = 1; ma = partialFractiles(MArray(arr,mask), IPosition(1,0), 0.5); AlwaysAssertExit (allEQ(ma.array(), ares) && allEQ(ma.mask(), mres)); // Do other tests with doubles. Array arrd(arr.shape()); convertArray (arrd, arr); Matrix aresd(3,4); MArray mad; aresd = 0.; aresd(1,1) = 5000.; mad = partialVariances(MArray(arrd,mask), IPosition(1,0), 1); AlwaysAssertExit (allNear(mad.array(), aresd, 1e-5) && allEQ(mad.mask(), mres)); aresd(1,1) = 70.7107; mad = partialStddevs(MArray(arrd,mask), IPosition(1,0), 1); AlwaysAssertExit (allNear(mad.array(), aresd, 1e-5) && allEQ(mad.mask(), mres)); aresd(1,1) = 50.; mad = partialAvdevs(MArray(arrd,mask), IPosition(1,0)); AlwaysAssertExit (allNear(mad.array(), aresd, 1e-5) && allEQ(mad.mask(), mres)); aresd = 1.; aresd(1,1) = 71.4213; aresd(2,3) = 0.; mad = partialRmss(MArray(arrd,mask), IPosition(1,0)); AlwaysAssertExit (allNear(mad.array(), aresd, 1e-5) && allEQ(mad.mask(), mres)); } void doTestBoxed() { // Test the boxed reduction functions. Cube arr(2,3,4); Cube mask(2,3,4); arr = 1; mask = False; arr(1,1,1) = 101; AlwaysAssertExit (allEQ(boxedSums(MArray(arr), IPosition(1,1)).array(), boxedArrayMath (arr, IPosition(1,1), SumFunc()))); AlwaysAssertExit (allEQ(boxedSums(MArray(arr), IPosition(1,2)).array(), boxedArrayMath (arr, IPosition(1,2), SumFunc()))); AlwaysAssertExit (allEQ(boxedSums(MArray(arr), IPosition(2,2,2,2)).array(), boxedArrayMath (arr, IPosition(2,2,2,2), SumFunc()))); AlwaysAssertExit (allEQ(boxedSumSqrs(MArray(arr), IPosition(2,2,2,2)).array(), boxedArrayMath (arr, IPosition(2,2,2,2), SumSqrFunc()))); AlwaysAssertExit (allEQ(boxedProducts(MArray(arr), IPosition(2,2,2,2)).array(), boxedArrayMath (arr, IPosition(2,2,2,2), ProductFunc()))); AlwaysAssertExit (allEQ(boxedMins(MArray(arr), IPosition(2,2,2,2)).array(), boxedArrayMath (arr, IPosition(2,2,2,2), MinFunc()))); AlwaysAssertExit (allEQ(boxedMaxs(MArray(arr), IPosition(2,2,2,2)).array(), boxedArrayMath (arr, IPosition(2,2,2,2), MaxFunc()))); AlwaysAssertExit (allEQ(boxedMeans(MArray(arr), IPosition(2,2,2,2)).array(), boxedArrayMath (arr, IPosition(2,2,2,2), MeanFunc()))); AlwaysAssertExit (allEQ(boxedVariances(MArray(arr), IPosition(2,2,2,2), 1).array(), boxedArrayMath (arr, IPosition(2,2,2,2), VarianceFunc(1)))); AlwaysAssertExit (allEQ(boxedStddevs(MArray(arr), IPosition(2,2,2,2), 1).array(), boxedArrayMath (arr, IPosition(2,2,2,2), StddevFunc(1)))); AlwaysAssertExit (allEQ(boxedAvdevs(MArray(arr), IPosition(2,2,2,2)).array(), boxedArrayMath (arr, IPosition(2,2,2,2), AvdevFunc()))); AlwaysAssertExit (allEQ(boxedRmss(MArray(arr), IPosition(2,2,2,2)).array(), boxedArrayMath (arr, IPosition(2,2,2,2), RmsFunc()))); AlwaysAssertExit (allEQ(boxedMedians(MArray(arr), IPosition(2,2,2,2)).array(), boxedArrayMath (arr, IPosition(2,2,2,2), MedianFunc()))); AlwaysAssertExit (allEQ(boxedFractiles(MArray(arr), IPosition(2,2,2,2), 0.6).array(), boxedArrayMath (arr, IPosition(2,2,2,2), FractileFunc(0.6)))); // Now do MArray tests with a mask. mask(0,2,3) = mask(1,2,3) = mask(0,1,2) = True; Cube ares(1,3,4); ares = 2; ares(0,1,1) = 102; ares(0,1,2) = 1; ares(0,2,3) = 0; Cube mres(1,3,4); mres = False; mres(0,2,3) = True; MArray ma(boxedSums(MArray(arr,mask), IPosition(1,2))); AlwaysAssertExit (allEQ(ma.array(), ares) && allEQ(ma.mask(), mres)); ares = 2; ares(0,1,1) = 1+101*101; ares(0,1,2) = 1; ares(0,2,3) = 0; ma = boxedSumSqrs(MArray(arr,mask), IPosition(1,2)); AlwaysAssertExit (allEQ(ma.array(), ares) && allEQ(ma.mask(), mres)); ares = 1; ares(0,2,3) = 0; ma = boxedMins(MArray(arr,mask), IPosition(1,2)); AlwaysAssertExit (allEQ(ma.array(), ares) && allEQ(ma.mask(), mres)); ares = 1; ares(0,1,1) = 101; ares(0,2,3) = 0; ma = boxedMaxs(MArray(arr,mask), IPosition(1,2)); AlwaysAssertExit (allEQ(ma.array(), ares) && allEQ(ma.mask(), mres)); ares = 1; ares(0,1,1) = 51; ares(0,2,3) = 0; ma = boxedMeans(MArray(arr,mask), IPosition(1,2)); AlwaysAssertExit (allEQ(ma.array(), ares) && allEQ(ma.mask(), mres)); ares = 1; ares(0,1,1) = 51; ares(0,2,3) = 0; ma = boxedMedians(MArray(arr,mask), IPosition(1,2), True); AlwaysAssertExit (allEQ(ma.array(), ares) && allEQ(ma.mask(), mres)); ares(0,1,1) = 1; ma = boxedFractiles(MArray(arr,mask), IPosition(1,2), 0.5); AlwaysAssertExit (allEQ(ma.array(), ares) && allEQ(ma.mask(), mres)); // Do other tests with doubles. Array arrd(arr.shape()); convertArray (arrd, arr); Cube aresd(1,3,4); MArray mad; aresd = 0.; aresd(0,1,1) = 5000.; mad = boxedVariances(MArray(arrd,mask), IPosition(1,2), 1); AlwaysAssertExit (allNear(mad.array(), aresd, 1e-5) && allEQ(mad.mask(), mres)); aresd(0,1,1) = 70.7107; mad = boxedStddevs(MArray(arrd,mask), IPosition(1,2), 1); AlwaysAssertExit (allNear(mad.array(), aresd, 1e-5) && allEQ(mad.mask(), mres)); aresd(0,1,1) = 50.; mad = boxedAvdevs(MArray(arrd,mask), IPosition(1,2)); AlwaysAssertExit (allNear(mad.array(), aresd, 1e-5) && allEQ(mad.mask(), mres)); aresd = 1.; aresd(0,1,1) = 71.4213; aresd(0,2,3) = 0.; mad = boxedRmss(MArray(arrd,mask), IPosition(1,2)); AlwaysAssertExit (allNear(mad.array(), aresd, 1e-5) && allEQ(mad.mask(), mres)); } void doTestSliding() { // Test the sliding reduction functions. Cube arr(4,5,6); Cube mask(4,5,6); indgen (arr); mask = False; // An empty box results in the array itself. AlwaysAssertExit (allEQ (slidingSums(MArray(arr,mask), IPosition(), False).array(), arr)); // But with a mask the result is 0. mask = True; MArray ma1 = slidingSums(MArray(arr,mask), IPosition(), False); AlwaysAssertExit (allEQ (ma1.array(), 0)); AlwaysAssertExit (allEQ (ma1.mask(), mask)); // Test without filling the edge (with and without mask). mask = False; MArray a1(slidingSums(MArray(arr,mask), IPosition(2,1,2), False)); Array a2 (slidingArrayMath (arr, IPosition(2,1,2), SumFunc(), False)); AlwaysAssertExit (a1.shape() == IPosition(3,2,1,6)); AlwaysAssertExit (allEQ(a1.array(), a2)); AlwaysAssertExit (allEQ(a1.mask(), False)); // Test with filling the edge (with and without mask). a1.reference (slidingSums(MArray(arr,mask), IPosition(2,1,2), True)); a2.reference (slidingArrayMath (arr, IPosition(2,1,2), SumFunc(), True)); AlwaysAssertExit (a1.shape() == IPosition(3,4,5,6)); AlwaysAssertExit (allEQ(a1.array(), a2)); Cube expMask(4,5,6); expMask = True; expMask(IPosition(3,1,2,0), IPosition(3,2,2,5)) = False; AlwaysAssertExit (allEQ(a1.mask(), expMask)); // Test with some mask bits set. mask(0,2,3) = mask(1,2,3) = mask(2,2,3) = mask(1,1,2) = True; Cube ares = slidingSums(MArray(arr), IPosition(1,1), False).array(); ares(0,2,3) = 0; ares(1,2,3) = 71; ares(0,1,2) = 44+46; ares(1,1,2) = 46+47; Cube mres(2,5,6); mres = False; mres(0,2,3) = True; MArray ma(slidingSums(MArray(arr,mask), IPosition(1,1), False)); AlwaysAssertExit (allEQ(ma.array(), ares) && allEQ(ma.mask(), mres)); // Now test the various functions (with and without a mask). ares = slidingArrayMath(arr, IPosition(1,1), SumSqrFunc(), False); AlwaysAssertExit (allEQ(slidingSumSqrs(MArray(arr), IPosition(1,1), False).array(), ares)); ares(0,2,3) = 0; ares(1,2,3) = 71*71; ares(0,1,2) = 44*44+46*46; ares(1,1,2) = 46*46+47*47; ma = slidingSumSqrs(MArray(arr,mask), IPosition(1,1), False); AlwaysAssertExit (allEQ(ma.array(), ares) && allEQ(ma.mask(), mres)); ares = slidingArrayMath(arr, IPosition(1,1), ProductFunc(), False); AlwaysAssertExit (allEQ(slidingProducts(MArray(arr), IPosition(1,1), False).array(), ares)); ares(0,2,3) = 0; ares(1,2,3) = 71; ares(0,1,2) = 44*46; ares(1,1,2) = 46*47; ma = slidingProducts(MArray(arr,mask), IPosition(1,1), False); AlwaysAssertExit (allEQ(ma.array(), ares) && allEQ(ma.mask(), mres)); ares = slidingArrayMath(arr, IPosition(1,1), MinFunc(), False); AlwaysAssertExit (allEQ(slidingMins(MArray(arr), IPosition(1,1), False).array(), ares)); ares(0,2,3) = 0; ares(1,2,3) = 71; ares(0,1,2) = 44; ares(1,1,2) = 46; ma = slidingMins(MArray(arr,mask), IPosition(1,1), False); AlwaysAssertExit (allEQ(ma.array(), ares) && allEQ(ma.mask(), mres)); ares = slidingArrayMath(arr, IPosition(1,1), MaxFunc(), False); AlwaysAssertExit (allEQ(slidingMaxs(MArray(arr), IPosition(1,1), False).array(), ares)); ares(0,2,3) = 0; ares(1,2,3) = 71; ares(0,1,2) = 46; ares(1,1,2) = 47; ma = slidingMaxs(MArray(arr,mask), IPosition(1,1), False); AlwaysAssertExit (allEQ(ma.array(), ares) && allEQ(ma.mask(), mres)); ares = slidingArrayMath(arr, IPosition(1,1), MeanFunc(), False); AlwaysAssertExit (allEQ(slidingMeans(MArray(arr), IPosition(1,1), False).array(), ares)); ares(0,2,3) = 0; ares(1,2,3) = 71; ares(0,1,2) = 45; ares(1,1,2) = 46; ma = slidingMeans(MArray(arr,mask), IPosition(1,1), False); AlwaysAssertExit (allEQ(ma.array(), ares) && allEQ(ma.mask(), mres)); // Do other tests with doubles. Array arrd(arr.shape()); convertArray (arrd, arr); Cube aresd(2,5,6); MArray mad; aresd = slidingArrayMath(arrd, IPosition(1,1), VarianceFunc(1), False); AlwaysAssertExit (allNear(slidingVariances(MArray(arrd), IPosition(1,1), 1, False).array(), aresd, 1e-5)); aresd(0,2,3) = 0; aresd(1,2,3) = 0; aresd(0,1,2) = 2; aresd(1,1,2) = 0.5; mad = slidingVariances(MArray(arrd,mask), IPosition(1,1), 1, False); AlwaysAssertExit (allNear(mad.array(), aresd, 1e-5) && allEQ(mad.mask(), mres)); aresd = slidingArrayMath(arrd, IPosition(1,1), StddevFunc(1), False); AlwaysAssertExit (allNear(slidingStddevs(MArray(arrd), IPosition(1,1), 1, False).array(), aresd, 1e-5)); aresd(0,2,3) = 0; aresd(1,2,3) = 0; aresd(0,1,2) = sqrt(2.); aresd(1,1,2) = sqrt(0.5); mad = slidingStddevs(MArray(arrd,mask), IPosition(1,1), 1, False); AlwaysAssertExit (allNear(mad.array(), aresd, 1e-5) && allEQ(mad.mask(), mres)); aresd = slidingArrayMath(arrd, IPosition(1,1), AvdevFunc(), False); AlwaysAssertExit (allNear(slidingAvdevs(MArray(arrd), IPosition(1,1), False).array(), aresd, 1e-5)); aresd(0,2,3) = 0; aresd(1,2,3) = 0; aresd(0,1,2) = 1; aresd(1,1,2) = 0.5; mad = slidingAvdevs(MArray(arrd,mask), IPosition(1,1), False); AlwaysAssertExit (allNear(mad.array(), aresd, 1e-5) && allEQ(mad.mask(), mres)); aresd = slidingArrayMath(arrd, IPosition(1,1), RmsFunc(), False); AlwaysAssertExit (allNear(slidingRmss(MArray(arrd), IPosition(1,1), False).array(), aresd, 1e-5)); aresd(0,2,3) = 0; aresd(1,2,3) = 71; aresd(0,1,2) = 45.01110974; aresd(1,1,2) = 46.5026881; mad = slidingRmss(MArray(arrd,mask), IPosition(1,1), False); AlwaysAssertExit (allNear(mad.array(), aresd, 1e-5) && allEQ(mad.mask(), mres)); aresd = slidingArrayMath(arrd, IPosition(1,1), MedianFunc(False, True), False); AlwaysAssertExit (allNear(slidingMedians(MArray(arrd), IPosition(1,1), True, False, False).array(), aresd, 1e-5)); aresd(0,2,3) = 0; aresd(1,2,3) = 71; aresd(0,1,2) = 45; aresd(1,1,2) = 46.5; mad = slidingMedians(MArray(arrd,mask), IPosition(1,1), True, False, False); AlwaysAssertExit (allNear(mad.array(), aresd, 1e-5) && allEQ(mad.mask(), mres)); aresd = slidingArrayMath(arrd, IPosition(1,1), FractileFunc(0.6), False); AlwaysAssertExit (allNear(slidingFractiles(MArray(arrd), IPosition(1,1), 0.6, False, False).array(), aresd, 1e-5)); aresd(0,2,3) = 0; aresd(1,2,3) = 71; aresd(0,1,2) = 44; aresd(1,1,2) = 46; mad = slidingFractiles(MArray(arrd,mask), IPosition(1,1), 0.6, False, False); AlwaysAssertExit (allNear(mad.array(), aresd, 1e-5) && allEQ(mad.mask(), mres)); } void doTestNull() { MArray m1 (Vector(2,16), Vector(2,True)); MArray m2; AlwaysAssertExit ((m1+m2).isNull()); AlwaysAssertExit ((m2-m1).isNull()); AlwaysAssertExit (sin(m2).isNull()); AlwaysAssertExit ((-m2).isNull()); AlwaysAssertExit (sum(m2) == 0); AlwaysAssertExit (partialSums(m2, IPosition()).isNull()); AlwaysAssertExit (boxedMedians(m2, IPosition()).isNull()); AlwaysAssertExit (slidingMeans(m2, IPosition()).isNull()); } void doPerf() { // Do a bit of performance testing. Cube a(200,200,200); a = 1; Timer timer; partialArrayMath(a, IPosition(1,1), SumFunc()); timer.show("unmasked"); Cube mask(200,200,200); mask = False; timer.mark(); partialSums (MArray(a,mask), IPosition(1,1)); timer.show("masked "); timer.mark(); partialSums (a, IPosition(1,1)); timer.show("sums unm"); } int main() { try { cout << "doTestAll" << endl; doTestAll(); cout << "doTestAll" << endl; doTestAll(); cout << "doTestInt" << endl; doTestInt(); cout << "doTestBool" << endl; doTestBool(); cout << "doTestFloat" << endl; doTestFloat(); cout << "doTestComplex" << endl; doTestComplex(); cout << "doTestComplex" << endl; doTestComplex(); cout << "doTestComplexReal" << endl; doTestComplexReal(); cout << "doTestComplexReal" << endl; doTestComplexReal(); cout << "doTestMixed" << endl; doTestMixed(); cout << "doTestReduce" << endl; doTestReduce(); cout << "doTestPartial" << endl; doTestPartial(); cout << "doTestBoxed" << endl; doTestBoxed(); cout << "doTestSliding" << endl; doTestSliding(); cout << "doTestNull" << endl; doTestNull(); cout << "doPerf" << endl; doPerf(); } catch (std::exception& x) { cout << "Unexpected exception: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/tables/TaQL/test/tMArrayUtil.cc000066400000000000000000000131301476623553700211000ustar00rootroot00000000000000//# tMArrayUtil.cc: test program for MArrayUtil //# Copyright (C) 2012 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include using namespace casacore; using namespace std; void testReorder() { // Create and fill an array and mask. Array arr(IPosition(4,2,3,4,5)); indgen(arr); Array mask(arr.shape()); mask = True; mask(arr%7==0) = False; // Create an unmasked array from it, reorder it, and check the result. MArray marr1(arr); MArray res1 = reorderArray (marr1, IPosition(2,1,3)); AlwaysAssertExit (! res1.hasMask()); for (int i4=0; i4<5; ++i4) { for (int i3=0; i3<4; ++i3) { for (int i2=0; i2<3; ++i2) { for (int i1=0; i1<2; ++i1) { Int v = res1.array()(IPosition(4,i2,i4,i1,i3)); AlwaysAssertExit (v == arr(IPosition(4,i1,i2,i3,i4))); } } } } // Create a masked array from it, reorder it, and check the result. MArray marr2(arr, mask); MArray res2 = reorderArray (marr2, IPosition(2,3,1)); AlwaysAssertExit (res2.hasMask()); for (int i4=0; i4<5; ++i4) { for (int i3=0; i3<4; ++i3) { for (int i2=0; i2<3; ++i2) { for (int i1=0; i1<2; ++i1) { Int v = res2.array()(IPosition(4,i4,i2,i1,i3)); AlwaysAssertExit (v == arr(IPosition(4,i1,i2,i3,i4))); Bool m = res2.mask()(IPosition(4,i4,i2,i1,i3)); AlwaysAssertExit (m == mask(IPosition(4,i1,i2,i3,i4))); } } } } // Now reorder without actually changing the order. // Check that no copy is made if told so. MArray res3 = reorderArray (marr2, IPosition(4,0,1,2,3), False); AlwaysAssertExit (res3.hasMask()); AlwaysAssertExit (arr.data() == res3.array().data()); AlwaysAssertExit (mask.data() == res3.mask().data()); // The same but, now with a copy. MArray res4 = reorderArray (marr2, IPosition(4,0,1,2,3), True); AlwaysAssertExit (res4.hasMask()); AlwaysAssertExit (arr.data() != res4.array().data()); AlwaysAssertExit (mask.data() != res4.mask().data()); AlwaysAssertExit (allEQ(res4.array(), arr)); AlwaysAssertExit (allEQ(res4.mask(), mask)); // Check if a reordered null array is null. AlwaysAssertExit (reorderArray (MArray(), IPosition()).isNull()); } void testReverse() { // Create and fill an array and mask. Array arr(IPosition(4,2,3,4,5)); indgen(arr); Array mask(arr.shape()); mask = True; mask(arr%7==0) = False; // Create an unmasked array from it, reverse it, and check the result. MArray marr1(arr); MArray res1 = reverseArray (marr1, IPosition(2,1,3)); AlwaysAssertExit (! res1.hasMask()); for (int i4=0; i4<5; ++i4) { for (int i3=0; i3<4; ++i3) { for (int i2=0; i2<3; ++i2) { for (int i1=0; i1<2; ++i1) { Int v = res1.array()(IPosition(4,i1,2-i2,i3,4-i4)); AlwaysAssertExit (v == arr(IPosition(4,i1,i2,i3,i4))); } } } } // Create a masked array from it, reverse it, and check the result. MArray marr2(arr, mask); MArray res2 = reverseArray (marr2, IPosition(2,3,1)); AlwaysAssertExit (res2.hasMask()); for (int i4=0; i4<5; ++i4) { for (int i3=0; i3<4; ++i3) { for (int i2=0; i2<3; ++i2) { for (int i1=0; i1<2; ++i1) { Int v = res2.array()(IPosition(4,i1,2-i2,i3,4-i4)); AlwaysAssertExit (v == arr(IPosition(4,i1,i2,i3,i4))); Bool m = res2.mask()(IPosition(4,i1,2-i2,i3,4-i4)); AlwaysAssertExit (m == mask(IPosition(4,i1,i2,i3,i4))); } } } } // Now reverse without actually changing the order. // Check that no copy is made if told so. MArray res3 = reverseArray (marr2, IPosition(), False); AlwaysAssertExit (res3.hasMask()); AlwaysAssertExit (arr.data() == res3.array().data()); AlwaysAssertExit (mask.data() == res3.mask().data()); // The same but, now with a copy. MArray res4 = reverseArray (marr2, IPosition(), True); AlwaysAssertExit (res4.hasMask()); AlwaysAssertExit (arr.data() != res4.array().data()); AlwaysAssertExit (mask.data() != res4.mask().data()); AlwaysAssertExit (allEQ(res4.array(), arr)); AlwaysAssertExit (allEQ(res4.mask(), mask)); // Check if a reverseed null array is null. AlwaysAssertExit (reverseArray (MArray(), IPosition()).isNull()); } int main() { testReorder(); testReverse(); return 0; } casacore-3.7.1/tables/TaQL/test/tRecordExpr.cc000066400000000000000000000125031476623553700211270ustar00rootroot00000000000000//# tRecordExpr.cc: Test program for the record selection //# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include // // Test program for record selection. // // This program tests the class RecordExpr to select records from a group // of records. void doIt() { // Check if it handles a normal record field. TableRecord rec; rec.define ("fld1", Int(1)); TableExprNode expr (makeRecordExpr(rec, "fld1") == 1.); Bool result; expr.get (rec, result); AlwaysAssertExit (result); // Check if it can also handle a record where fld1 is e.g. a float. rec.removeField ("fld1"); rec.define ("fld1", Float(2)); expr.get (rec, result); AlwaysAssertExit (!result); // Check if it handles fields in subrecords. TableRecord subrec1, subrec2; subrec2.define ("fld1", 1); subrec1.defineRecord ("sub2", subrec2); rec.defineRecord ("sub1", subrec1); expr.get (rec, result); AlwaysAssertExit (!result); TableExprNode expr1 (makeRecordExpr(rec, "sub1.sub2.fld1") == 1); expr1.get (rec, result); AlwaysAssertExit (result); TableExprNode expr2 (makeRecordExpr(rec, "sub1.sub2.fld1") == 2); expr2.get (rec, result); AlwaysAssertExit (!result); TableExprNode expr3 (makeRecordExpr(rec, "sub1.sub2.fld1") == 1 && makeRecordExpr(rec, "fld1") > 1); expr3.get (rec, result); AlwaysAssertExit (result); rec.define ("fld1", Float(1)); expr3.get (rec, result); AlwaysAssertExit (!result); // Check if ifDefined behaves correctly. TableExprNode expr4a (isdefined (makeRecordExpr(rec, "sub1.sub2.fld1"))); expr4a.get (rec, result); AlwaysAssertExit (result); TableExprNode expr4b (isdefined (makeRecordExpr(rec, "fld1"))); expr4b.get (rec, result); AlwaysAssertExit (result); // Undefined when used on an empty record. TableRecord rect; TableRecord subrect1, subrect2; expr4a.get (rect, result); AlwaysAssertExit (!result); // Still undefined. rect.define ("fld2", True); rect.defineRecord ("sub1", subrect1); expr4a.get (rect, result); AlwaysAssertExit (!result); // Still undefined because field has incorrect type. subrect2.define ("fld1", True); subrect1.defineRecord ("sub2", subrect2); rect.defineRecord ("sub1", subrect1); expr4a.get (rect, result); AlwaysAssertExit (!result); // Now it should be defined. subrect2.removeField ("fld1"); subrect2.define ("fld1", 1); subrect1.defineRecord ("sub2", subrect2); rect.defineRecord ("sub1", subrect1); expr4a.get (rect, result); AlwaysAssertExit (result); // Now undefined again (because sub1 has not correct fieldNumber anymore). rect.removeField ("fld2"); expr4a.get (rect, result); AlwaysAssertExit (!result); // Check ndim and shape function for a scalar. TableExprNode expr5a (ndim (makeRecordExpr(rec, "fld1")) == 0); expr5a.get (rec, result); AlwaysAssertExit (result); TableExprNode expr5b (nelements (shape (makeRecordExpr(rec, "fld1"))) == 0); expr5b.get (rec, result); AlwaysAssertExit (result); // Check if array fields are handled correctly. Array arr(IPosition(3,6,8,12)); indgen (arr); rec.define ("arr1", arr); TableExprNode expr6a (max (makeRecordExpr(rec, "arr1")) > 6*8*12); expr6a.get (rec, result); AlwaysAssertExit (!result); TableExprNode expr6b (max (makeRecordExpr(rec, "arr1")) >= 6*8*12-1); expr6b.get (rec, result); AlwaysAssertExit (result); // Check shape and ndim function. TableExprNode expr6c (TableExprNode(7).in (shape (makeRecordExpr(rec, "arr1")))); expr6c.get (rec, result); AlwaysAssertExit (!result); TableExprNode expr6d (TableExprNode(7).in (shape (makeRecordExpr(rec, "arr1")) - 1)); expr6d.get (rec, result); AlwaysAssertExit (result); } int main() { try { doIt(); } catch (std::exception& x) { cout << "Unexpected exception: " << x.what() << endl; return 1; } catch (...) { cout << "Unexpected unknown exception" << endl; return 1; } return 0; } casacore-3.7.1/tables/TaQL/test/tRecordGram.cc000066400000000000000000000245121476623553700211020ustar00rootroot00000000000000//# tRecordGram.cc: Test program for the record selection grsmmar //# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include // // Test program for record selection. // // This program tests the class RecordGram to select records from a group // of records. void doIt() { // Check if it handles a normal record field. TableRecord rec; rec.define ("fld1", Int(1)); TableExprNode expr (RecordGram::parse(rec, "fld1 == 1.")); Bool result; expr.get (rec, result); AlwaysAssertExit (result); // Check if it can also handle a record where fld1 is e.g. a float. rec.removeField ("fld1"); rec.define ("fld1", Float(2)); expr.get (rec, result); AlwaysAssertExit (!result); // Check if it handles fields in subrecords. TableRecord subrec1, subrec2; subrec2.define ("fld1", 1); subrec1.defineRecord ("sub2", subrec2); rec.defineRecord ("sub1", subrec1); expr.get (rec, result); AlwaysAssertExit (!result); TableExprNode expr1 (RecordGram::parse(rec, "sub1.sub2.fld1 == 1")); expr1.get (rec, result); AlwaysAssertExit (result); TableExprNode expr2 (RecordGram::parse(rec, "sub1.sub2.fld1 == 2")); expr2.get (rec, result); AlwaysAssertExit (!result); TableExprNode expr3 (RecordGram::parse(rec, "sub1.sub2.fld1 == 1 && fld1 > 1")); expr3.get (rec, result); AlwaysAssertExit (result); rec.define ("fld1", Float(1)); expr3.get (rec, result); AlwaysAssertExit (!result); // Check if ifDefined behaves correctly. TableExprNode expr4a (RecordGram::parse (rec, "isdefined (sub1.sub2.fld1)")); expr4a.get (rec, result); AlwaysAssertExit (result); TableExprNode expr4b (RecordGram::parse (rec, "isdefined (fld1)")); expr4b.get (rec, result); AlwaysAssertExit (result); // Undefined when used on an empty record. TableRecord rect; TableRecord subrect1, subrect2; expr4a.get (rect, result); AlwaysAssertExit (!result); // Still undefined. rect.define ("fld2", True); rect.defineRecord ("sub1", subrect1); expr4a.get (rect, result); AlwaysAssertExit (!result); // Still undefined because field has incorrect type. subrect2.define ("fld1", True); subrect1.defineRecord ("sub2", subrect2); rect.defineRecord ("sub1", subrect1); expr4a.get (rect, result); AlwaysAssertExit (!result); // Now it should be defined. subrect2.removeField ("fld1"); subrect2.define ("fld1", 1); subrect1.defineRecord ("sub2", subrect2); rect.defineRecord ("sub1", subrect1); expr4a.get (rect, result); AlwaysAssertExit (result); // Now undefined again (because sub1 has not correct fieldNumber anymore). rect.removeField ("fld2"); expr4a.get (rect, result); AlwaysAssertExit (!result); // Check ndim and shape function for a scalar. TableExprNode expr5a (RecordGram::parse (rec, "ndim (fld1) == 0")); expr5a.get (rec, result); AlwaysAssertExit (result); TableExprNode expr5b (RecordGram::parse (rec, "nelements (shape (fld1)) == 0")); expr5b.get (rec, result); AlwaysAssertExit (result); // Check if array fields are handled correctly. Array arr(IPosition(3,6,8,12)); indgen (arr); rec.define ("arr1", arr); TableExprNode expr6a (RecordGram::parse (rec, "max (arr1) > 6*8*12")); expr6a.get (rec, result); AlwaysAssertExit (!result); TableExprNode expr6b (RecordGram::parse (rec, "max (arr1) >= 6*8*12-1")); expr6b.get (rec, result); AlwaysAssertExit (result); // Check shape and ndim function. TableExprNode expr6c (RecordGram::parse (rec, "7 in shape (arr1)")); expr6c.get (rec, result); AlwaysAssertExit (!result); TableExprNode expr6d (RecordGram::parse (rec, "7 in shape(arr1) - 1")); expr6d.get (rec, result); AlwaysAssertExit (result); // Check if an array in an array works fine. TableExprNode expr7a (RecordGram::parse (rec, "all(shape(arr1) in shape(arr1))")); expr7a.get (rec, result); AlwaysAssertExit (result); TableExprNode expr7b (RecordGram::parse (rec, "any(shape(arr1) in shape(arr1)-1)")); expr7b.get (rec, result); AlwaysAssertExit (!result); // Check if an array in a set works fine. TableExprNode expr7c (RecordGram::parse (rec, "all(shape(arr1) in [6,8,12])")); expr7c.get (rec, result); AlwaysAssertExit (result); TableExprNode expr7d (RecordGram::parse (rec, "any(shape(arr1) in [5,7,11])")); expr7d.get (rec, result); AlwaysAssertExit (!result); // Check if an array in a set works fine. TableExprNode expr7e (RecordGram::parse (rec, "all([6,8,12] in shape(arr1))")); expr7e.get (rec, result); AlwaysAssertExit (result); TableExprNode expr7f (RecordGram::parse (rec, "any([5,7,11] in shape(arr1))")); expr7f.get (rec, result); AlwaysAssertExit (!result); // Check if a set in a set works fine. TableExprNode expr7g (RecordGram::parse (rec, "all([6,8,12] in [6,8,12])")); expr7g.get (rec, result); AlwaysAssertExit (result); TableExprNode expr7h (RecordGram::parse (rec, "any([5,7,11] in [6,8,12])")); expr7h.get (rec, result); AlwaysAssertExit (!result); // Check if rownumber is indeed an invalid function. { Bool err = False; try { TableExprNode expr8 (RecordGram::parse (rec, "rownumber() > 3")); } catch (std::exception& x) { cout << "Expected exception:\n" << x.what() << endl; err = True; } AlwaysAssertExit (err); } } // Test the expr2 functions. void testExpr2() { Record vars; AlwaysAssertExit (RecordGram::expr2Bool("T||F") == True); AlwaysAssertExit (RecordGram::expr2Int("2*2") == 4); AlwaysAssertExit (RecordGram::expr2Double("4") == 4); AlwaysAssertExit (RecordGram::expr2Complex("4") == DComplex(4,0)); AlwaysAssertExit (RecordGram::expr2String("'ab'+'cd'") == "abcd"); AlwaysAssertExit (near (RecordGram::expr2Date("12Mar2017/12:34:56.7").second(), MVTime(Time(2017,3,12,12,34,56.7)).second())); AlwaysAssertExit (RecordGram::expr2Double("4 kHz", vars, "Hz") == 4000); AlwaysAssertExit (RecordGram::expr2Double("1.2m", vars, "m") == 1.2); Array arrb; Array arri; Array arrd; Array arrc; Array arrs; Array arrm; vars.define ("i", 10); vars.define ("s", "xy"); arrb = RecordGram::expr2ArrayBool("T", vars); arri = RecordGram::expr2ArrayInt("i", vars); arrd = RecordGram::expr2ArrayDouble("i cm", vars, "m"); arrc = RecordGram::expr2ArrayComplex("i + i*1i", vars); arrs = RecordGram::expr2ArrayString("'str'", vars); arrm = RecordGram::expr2ArrayDate("12Mar2017/12:34:56.7", vars); AlwaysAssertExit (arrb.shape() == IPosition(1,1) && arrb.data()[0] == True); AlwaysAssertExit (arri.shape() == IPosition(1,1) && arri.data()[0] == 10); AlwaysAssertExit (arrd.shape() == IPosition(1,1) && arrd.data()[0] == 0.1); AlwaysAssertExit (arrc.shape() == IPosition(1,1) && arrc.data()[0] == DComplex(10,10)); AlwaysAssertExit (arrs.shape() == IPosition(1,1) && arrs.data()[0] == "str"); AlwaysAssertExit (arrm.shape() == IPosition(1,1) && near(arrm.data()[0].second(), MVTime(Time(2017,3,12,12,34,56.7)).second())); arrb.reference (RecordGram::expr2ArrayBool("[T,F]", vars)); arri.reference (RecordGram::expr2ArrayInt("[i,i+1]", vars)); arrd.reference (RecordGram::expr2ArrayDouble("[10cm, 10+2dm]", vars, "m")); arrc.reference (RecordGram::expr2ArrayComplex("[i, i*1i]", vars)); arrs.reference (RecordGram::expr2ArrayString("['str', s]", vars)); arrm.reference (RecordGram::expr2ArrayDate("[12Mar2017/12:34:56.7, " "12Mar2017/12:34:56.7 + 2d]")); AlwaysAssertExit (arrb.shape() == IPosition(1,2) && arrb.data()[0] == True && arrb.data()[1] == False); AlwaysAssertExit (arri.shape() == IPosition(1,2) && arri.data()[0] == 10 && arri.data()[1] == 11); AlwaysAssertExit (arrd.shape() == IPosition(1,2) && arrd.data()[0] == 0.1 && arrd.data()[1] == 1.2); AlwaysAssertExit (arrc.shape() == IPosition(1,2) && arrc.data()[0] == DComplex(10,0) && arrc.data()[1] == DComplex(0,10)); AlwaysAssertExit (arrs.shape() == IPosition(1,2) && arrs.data()[0] == "str" && arrs.data()[1] == "xy"); AlwaysAssertExit (arrm.shape() == IPosition(1,2) && near(arrm.data()[0].second(), MVTime(Time(2017,3,12,12,34,56.7)).second()) && near(arrm.data()[1].second(), MVTime(Time(2017,3,14,12,34,56.7)).second())); } int main() { try { doIt(); testExpr2(); } catch (std::exception& x) { cout << "Unexpected exception: " << x.what() << endl; return 1; } catch (...) { cout << "Unexpected unknown exception" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/tables/TaQL/test/tRecordGramTable.cc000066400000000000000000000105071476623553700220510ustar00rootroot00000000000000//# tRecordGram.cc: Test program for the expression grammar on a table //# Copyright (C) 2004 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include // // Test program for the expression grammar on a table. // // This program tests the class RecordGram to do expressions on a table. void doIt (const String& str) { String name = "$testsrcdir/../../Tables/test/tTable_2.data_v0"; Table tab(name); TableExprNode expr = RecordGram::parse (tab, str); cout << str << ": "; if (expr.isScalar()) { Vector rownrs(expr.nrow()); indgen (rownrs); switch (expr.getColumnDataType()) { case TpBool: cout << expr.getColumnBool (rownrs); break; case TpUChar: cout << expr.getColumnuChar (rownrs); break; case TpShort: cout << expr.getColumnShort (rownrs); break; case TpUShort: cout << expr.getColumnuShort (rownrs); break; case TpInt: cout << expr.getColumnInt (rownrs); break; case TpUInt: cout << expr.getColumnuInt (rownrs); break; case TpInt64: cout << expr.getColumnInt64 (rownrs); break; case TpFloat: cout << expr.getColumnFloat (rownrs); break; case TpDouble: cout << expr.getColumnDouble (rownrs); break; case TpComplex: cout << expr.getColumnComplex (rownrs); break; case TpDComplex: cout << expr.getColumnDComplex (rownrs); break; case TpString: cout << expr.getColumnString (rownrs); break; default: cout << "Unknown expression scalar type " << expr.getColumnDataType(); } cout << endl; } else { for (rownr_t i=0; i arr; expr.get (i, arr); cout << arr.array(); break; } case TpDouble: { MArray arr; expr.get (i, arr); cout << arr.array(); break; } case TpDComplex: { MArray arr; expr.get (i, arr); cout << arr.array(); break; } case TpString: { MArray arr; expr.get (i, arr); cout << arr.array(); break; } default: cout << "Unknown expression array type " << expr.dataType(); } } } } // Ask and execute command till empty string is given. void docomm() { char comm[1025]; while (True) { cout << "Table command (q=quit): "; cin.getline (comm, 1024); String str(comm); if (str.empty() || str == "q") break; try { doIt (str); } catch (std::exception& x) { cout << x.what() << endl; } } } int main (int argc, const char* argv[]) { if (argc < 2) { docomm(); return 0; } try { doIt(argv[1]); } catch (std::exception& x) { cout << "Unexpected exception: " << x.what() << endl; return 1; } catch (...) { cout << "Unexpected unknown exception" << endl; return 1; } return 0; } casacore-3.7.1/tables/TaQL/test/tRecordGramTable.out000066400000000000000000000061441476623553700222750ustar00rootroot00000000000000ab: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] ac: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ad: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11] ae: [3, 4, 5, 6, 7, 8, 9, 10, 11, 12] af: [V0, V1, V2, V3, V4, V5, V6, V7, V8, V9] ag: [(2,0), (3,0), (4,0), (5,0), (6,0), (7,0), (8,0), (9,0), (10,0), (11,0)] ab>4: [0, 0, 0, 0, 0, 1, 1, 1, 1, 1] ab+ac: [1, 3, 5, 7, 9, 11, 13, 15, 17, 19] arr1[1,1,1]+1: [1, 25, 49, 73, 97, 121, 145, 169, 193, 217] max(arr1): [23, 47, 71, 95, 119, 143, 167, 191, 215, 239] arr1: row 0: Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][0, 1] [0, 1, 0][2, 3] [0, 2, 0][4, 5] [0, 0, 1][6, 7] [0, 1, 1][8, 9] [0, 2, 1][10, 11] [0, 0, 2][12, 13] [0, 1, 2][14, 15] [0, 2, 2][16, 17] [0, 0, 3][18, 19] [0, 1, 3][20, 21] [0, 2, 3][22, 23] row 1: Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][24, 25] [0, 1, 0][26, 27] [0, 2, 0][28, 29] [0, 0, 1][30, 31] [0, 1, 1][32, 33] [0, 2, 1][34, 35] [0, 0, 2][36, 37] [0, 1, 2][38, 39] [0, 2, 2][40, 41] [0, 0, 3][42, 43] [0, 1, 3][44, 45] [0, 2, 3][46, 47] row 2: Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][48, 49] [0, 1, 0][50, 51] [0, 2, 0][52, 53] [0, 0, 1][54, 55] [0, 1, 1][56, 57] [0, 2, 1][58, 59] [0, 0, 2][60, 61] [0, 1, 2][62, 63] [0, 2, 2][64, 65] [0, 0, 3][66, 67] [0, 1, 3][68, 69] [0, 2, 3][70, 71] row 3: Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][72, 73] [0, 1, 0][74, 75] [0, 2, 0][76, 77] [0, 0, 1][78, 79] [0, 1, 1][80, 81] [0, 2, 1][82, 83] [0, 0, 2][84, 85] [0, 1, 2][86, 87] [0, 2, 2][88, 89] [0, 0, 3][90, 91] [0, 1, 3][92, 93] [0, 2, 3][94, 95] row 4: Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][96, 97] [0, 1, 0][98, 99] [0, 2, 0][100, 101] [0, 0, 1][102, 103] [0, 1, 1][104, 105] [0, 2, 1][106, 107] [0, 0, 2][108, 109] [0, 1, 2][110, 111] [0, 2, 2][112, 113] [0, 0, 3][114, 115] [0, 1, 3][116, 117] [0, 2, 3][118, 119] row 5: Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][120, 121] [0, 1, 0][122, 123] [0, 2, 0][124, 125] [0, 0, 1][126, 127] [0, 1, 1][128, 129] [0, 2, 1][130, 131] [0, 0, 2][132, 133] [0, 1, 2][134, 135] [0, 2, 2][136, 137] [0, 0, 3][138, 139] [0, 1, 3][140, 141] [0, 2, 3][142, 143] row 6: Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][144, 145] [0, 1, 0][146, 147] [0, 2, 0][148, 149] [0, 0, 1][150, 151] [0, 1, 1][152, 153] [0, 2, 1][154, 155] [0, 0, 2][156, 157] [0, 1, 2][158, 159] [0, 2, 2][160, 161] [0, 0, 3][162, 163] [0, 1, 3][164, 165] [0, 2, 3][166, 167] row 7: Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][168, 169] [0, 1, 0][170, 171] [0, 2, 0][172, 173] [0, 0, 1][174, 175] [0, 1, 1][176, 177] [0, 2, 1][178, 179] [0, 0, 2][180, 181] [0, 1, 2][182, 183] [0, 2, 2][184, 185] [0, 0, 3][186, 187] [0, 1, 3][188, 189] [0, 2, 3][190, 191] row 8: Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][192, 193] [0, 1, 0][194, 195] [0, 2, 0][196, 197] [0, 0, 1][198, 199] [0, 1, 1][200, 201] [0, 2, 1][202, 203] [0, 0, 2][204, 205] [0, 1, 2][206, 207] [0, 2, 2][208, 209] [0, 0, 3][210, 211] [0, 1, 3][212, 213] [0, 2, 3][214, 215] row 9: Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][216, 217] [0, 1, 0][218, 219] [0, 2, 0][220, 221] [0, 0, 1][222, 223] [0, 1, 1][224, 225] [0, 2, 1][226, 227] [0, 0, 2][228, 229] [0, 1, 2][230, 231] [0, 2, 2][232, 233] [0, 0, 3][234, 235] [0, 1, 3][236, 237] [0, 2, 3][238, 239] casacore-3.7.1/tables/TaQL/test/tRecordGramTable.run000077500000000000000000000015211476623553700222670ustar00rootroot00000000000000#!/bin/sh #----------------------------------------------------------------------------- # Script to test the RecordGram class for a table #============================================================================= # Set default testsrcdir if undefined. It is used in tRecordGramTable.cc. if test "$testsrcdir" = ""; then testsrcdir=../../../../../casacore/tables/TaQL/test fi $casa_checktool ./tRecordGramTable "ab" $casa_checktool ./tRecordGramTable "ac" $casa_checktool ./tRecordGramTable "ad" $casa_checktool ./tRecordGramTable "ae" $casa_checktool ./tRecordGramTable "af" $casa_checktool ./tRecordGramTable "ag" $casa_checktool ./tRecordGramTable "ab>4" $casa_checktool ./tRecordGramTable "ab+ac" $casa_checktool ./tRecordGramTable "arr1[1,1,1]+1" $casa_checktool ./tRecordGramTable "max(arr1)" $casa_checktool ./tRecordGramTable "arr1" casacore-3.7.1/tables/TaQL/test/tTaQLNode.cc000066400000000000000000000070301476623553700204600ustar00rootroot00000000000000//# tTaQLNode.cc: This program tests parsing of table commands using TaQLNode //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include // // Test program for parsing of table commands using TaQLNode // // This program tests table commands with an SQL-like grammar. // The grammar is scanned and parsed using the flex/bison file TableGram.ll/yy // and with the help of the class TaQLNode. // It ask for commands until a "q" is given. void seltab (const String&); void docomm (); int main (int argc, const char* argv[]) { try { if (argc > 1) { seltab(argv[1]); }else{ docomm(); } } catch (std::exception& x) { cout << "\nCaught an exception: " << x.what() << endl; return 1; } return 0; // successfully executed } // Ask and execute command till empty string is given. void docomm() { char comm[1025]; while (True) { cout << "Table command (q=quit): "; cin.getline (comm, 1024); String str(comm); if (str == "q") break; try { seltab (str); } catch (std::exception& x) { cout << x.what() << endl; } } } // Sort and select data. void seltab (const String& str) { cout << str << endl; TaQLNode node; try { // Parse the command. node = TaQLNode::parse (str); } catch (std::exception& x) { cout << x.what() << endl; return; } ostringstream oss; node.show (oss); cout << oss.str() << endl; // Now see if parsing the result gives the same result. TaQLNode node1; try { // Parse the command. node1 = TaQLNode::parse (oss.str()); } catch (std::exception& x) { cout << x.what() << endl; return; } ostringstream oss1; node1.show (oss1); if (oss.str() != oss1.str()) { cout << "Error: different parse result" << endl; cout << oss1.str() << endl; } // Save and restore the parse tree. // See if it gives the same result. AipsIO aio(std::make_shared()); node.save (aio); aio.setpos (0); TaQLNode node2 = TaQLNode::restore (aio); ostringstream oss2; node2.show (oss2); if (oss.str() != oss2.str()) { cout << "Error: different save/restore result" << endl; cout << oss2.str() << endl; } cout << endl; } casacore-3.7.1/tables/TaQL/test/tTaQLNode.out000066400000000000000000000517621476623553700207150ustar00rootroot00000000000000select 1*3 SELECT (1)*(3) select 1*3 giving a.b; SELECT (1)*(3) GIVING a.b select 1*3,5+7 into a.b where a>2 groupby c1,c2 having e1<0 and e2>3 orderby d1,d2 limit 2:5:2 SELECT (1)*(3),(5)+(7) WHERE (a)>(2) GROUPBY c1,c2 HAVING ((e1)<(0))&&((e2)>(3)) ORDERBY d1,d2 LIMIT 2:5:2 GIVING a.b with "a."tab2 select from "a.tab" #comment WITH a.tab2 SELECT FROM a.tab select from [a1.tab,a2.tab,[a3a.tab,a3b.tab]] SELECT FROM [a1.tab,a2.tab,[a3a.tab,a3b.tab]] select ab,ac,ad,ae,af,ag into tTaQLNode_tmp\".data2 from "tTaQLNode tmp.tab" sh where all(ab>2) && (ae<10 || ae>11.0) && ag!= 10 + 1i orderby ac desc,ab SELECT ab,ac,ad,ae,af,ag FROM tTaQLNode\ tmp.tab AS sh WHERE ((ALL((ab)>(2)))&&(((ae)<(10))||((ae)>(11))))&&((ag)<>((10)+(1i))) ORDERBY ac DESC,ab GIVING tTaQLNode_tmp\".data2 select ab,ac,ad,ae,af,ag into tTaQLNode_tmp.data2 as PLAIN_LOCAL from tTaQLNode_tmp.tab sh where all(ab>2) && (ae<10 || ae>11.0) && ag!= 10 + 1i orderby ac desc,ab SELECT ab,ac,ad,ae,af,ag FROM tTaQLNode_tmp.tab AS sh WHERE ((ALL((ab)>(2)))&&(((ae)<(10))||((ae)>(11))))&&((ag)<>((10)+(1i))) ORDERBY ac DESC,ab GIVING tTaQLNode_tmp.data2 AS [PLAIN_LOCAL=T] select from tTaQLNode_tmp.tab sh giving tTaQLNode_tmp.data2 as [type="PLAIN",endian="local",storage="multifile",blocksize=32768] dminfo [name="ISM1",type="IncrementalStMan"], [name="SSM1",type="StandardStMan", bucketsize=1000] SELECT FROM tTaQLNode_tmp.tab AS sh GIVING tTaQLNode_tmp.data2 AS [type='PLAIN',endian='local',storage='multifile',blocksize=32768] DMINFO [name='ISM1',type='IncrementalStMan'],[name='SSM1',type='StandardStMan',bucketsize=1000] select distinct ab+1 as ab1,ac,ad,ae,af,ag from tTaQLNode_tmp.data2 SELECT DISTINCT (ab)+(1) AS ab1,ac,ad,ae,af,ag FROM tTaQLNode_tmp.data2 select all ab as ab1,ac as ac1,ad,ae,af,ag from tTaQLNode_tmp.data2 orderby af SELECT ab AS ab1,ac AS ac1,ad,ae,af,ag FROM tTaQLNode_tmp.data2 ORDERBY af select ab from tTaQLNode_tmp.tab where ab==2**1**2 || ab==-2**-1*0x0A1f/-2*3 SELECT ab FROM tTaQLNode_tmp.tab WHERE ((ab)=((2)**((1)**(2))))||((ab)=((((-((2)**(-(1))))*(2591))/(-(2)))*(3))) >>> select ab from tTaQLNode_tmp.tab where ab==2^1^2 || ab==-2^-1*8/-2*3 SELECT ab FROM tTaQLNode_tmp.tab WHERE ((ab)=(((2)^(1))^(2)))||((ab)=((-(2))^((((-(1))*(8))/(-(2)))*(3)))) <<< select ab,ac,af from tTaQLNode_tmp.tab where lower(af) == regex("v[01279]") SELECT ab,ac,af FROM tTaQLNode_tmp.tab WHERE (lower(af))=(regex('v[01279]')) select ab,ac,af from tTaQLNode_tmp.tab where lower(af)!~m/v[01279]/ SELECT ab,ac,af FROM tTaQLNode_tmp.tab WHERE (lower(af))!~m/v[01279]/ select ab,ac,af from tTaQLNode_tmp.tab where af ~ p/?{3,5,8}/ SELECT ab,ac,af FROM tTaQLNode_tmp.tab WHERE (af)~p/?{3,5,8}/ select ab,ac,af from tTaQLNode_tmp.tab where af != pattern("?{3,5,8}") SELECT ab,ac,af FROM tTaQLNode_tmp.tab WHERE (af)<>(pattern('?{3,5,8}')) select ab,ac,af from tTaQLNode_tmp.tab where af == sqlpattern("_3%") SELECT ab,ac,af FROM tTaQLNode_tmp.tab WHERE (af)=(sqlpattern('_3%')) select ab,ac,af from tTaQLNode_tmp.tab where af like "_3%" SELECT ab,ac,af FROM tTaQLNode_tmp.tab WHERE (af)=(SQLPATTERN('_3%')) select ab,ac,af from tTaQLNode_tmp.tab where af not like "_3%" SELECT ab,ac,af FROM tTaQLNode_tmp.tab WHERE (af)<>(SQLPATTERN('_3%')) select from tTaQLNode_tmp.tab where af ~ d/abc/ SELECT FROM tTaQLNode_tmp.tab WHERE (af)~d/abc/ select from tTaQLNode_tmp.tab where af ~ d@abc@ib3 SELECT FROM tTaQLNode_tmp.tab WHERE (af)~d@abc@ib3 select from tTaQLNode_tmp.tab where af ~ d/abc/03bi SELECT FROM tTaQLNode_tmp.tab WHERE (af)~d/abc/ib3 select from tTaQLNode_tmp.tab where af ~ d%abc%04 SELECT FROM tTaQLNode_tmp.tab WHERE (af)~d%abc%4 select ab,ac from tTaQLNode_tmp.tab where ab%1.5==0 SELECT ab,ac FROM tTaQLNode_tmp.tab WHERE ((ab)%(1.5))=(0) select ab,(ac)mm from tTaQLNode_tmp.tab where ab%1.5==0 "m/s" SELECT ab,(ac)'mm' FROM tTaQLNode_tmp.tab WHERE ((ab)%(1.5))=((0)'m/s') select ab,ac from tTaQLNode_tmp.tab where arr1[1,1,1]>=10 && arr2[1,1,1]<120 SELECT ab,ac FROM tTaQLNode_tmp.tab WHERE (((arr1)[1,1,1])>=(10))&&(((arr2)[1,1,1])<(120)) select * from tTaQLNode_tmp.tab where arr1[1,1,1]>=10 && arr2[1,1,1]<120 SELECT ~p/*/ FROM tTaQLNode_tmp.tab WHERE (((arr1)[1,1,1])>=(10))&&(((arr2)[1,1,1])<(120)) select ab,ac from tTaQLNode_tmp.tab where arr1[1,1,1+ab%1]>=192 orderby ad desc SELECT ab,ac FROM tTaQLNode_tmp.tab WHERE ((arr1)[1,1,(1)+((ab)%(1))])>=(192) ORDERBY ad DESC select ab,ac from tTaQLNode_tmp.tab where cos(0.01rad) <= sin(-0.02rad)*sin(-ab/180*pi()) + cos(-0.5rad)*cos(-ab/180*pi())*cos(0.02rad - ac/180*pi()) SELECT ab,ac FROM tTaQLNode_tmp.tab WHERE (cos((0.01)'rad'))<=(((sin(-((0.02)'rad')))*(sin(((-(ab))/(180))*(pi()))))+(((cos(-((0.5)'rad')))*(cos(((-(ab))/(180))*(pi()))))*(cos(((0.02)'rad')-(((ac)/(180))*(pi())))))) select ab,ac,ad,ae,af,ag from tTaQLNode_tmp.tab where ab+ac+ad+ae+real(ag) >= year(31-12-1960) + year("31Dec60") + month(1990/05/12) + day(date(1990/01/30/12h14m33.3)) - 3910 SELECT ab,ac,ad,ae,af,ag FROM tTaQLNode_tmp.tab WHERE (((((ab)+(ac))+(ad))+(ae))+(real(ag)))>=(((((year(1960/12/31/00:00:00.0000))+(year('31Dec60')))+(month(1990/05/12/00:00:00.0000)))+(day(date(1990/01/30/12:14:33.3000))))-(3910)) select ab,ac,af from tTaQLNode_tmp.tab where ab>5 orderby af desc, ac SELECT ab,ac,af FROM tTaQLNode_tmp.tab WHERE (ab)>(5) ORDERBY af DESC,ac select ab,ac,af from tTaQLNode_tmp.tab orderby arr1[1,1,1] SELECT ab,ac,af FROM tTaQLNode_tmp.tab ORDERBY (arr1)[1,1,1] select ab,ac from tTaQLNode_tmp.tab orderby round(2*sin(ab)),ac desc SELECT ab,ac FROM tTaQLNode_tmp.tab ORDERBY round((2)*(sin(ab))),ac DESC select ab,ac from tTaQLNode_tmp.tab where ab < mean([3:6,ab]) SELECT ab,ac FROM tTaQLNode_tmp.tab WHERE (ab)<(mean([3:6,ab])) select ab,ac from tTaQLNode_tmp.tab where ab < 4 && EXISTS (select from tTaQLNode_tmp.tab) SELECT ab,ac FROM tTaQLNode_tmp.tab WHERE ((ab)<(4))&&(EXISTS [SELECT FROM tTaQLNode_tmp.tab]) select ab,ac from tTaQLNode_tmp.tab where ab < 4 && EXISTS (select from tTaQLNode_tmp.tab LIMIT 11) SELECT ab,ac FROM tTaQLNode_tmp.tab WHERE ((ab)<(4))&&(EXISTS [SELECT FROM tTaQLNode_tmp.tab LIMIT 11]) select ab,ac from tTaQLNode_tmp.tab where ab IN (select ac from tTaQLNode_tmp.tab where ab>4) SELECT ab,ac FROM tTaQLNode_tmp.tab WHERE (ab) IN [SELECT ac FROM tTaQLNode_tmp.tab WHERE (ab)>(4)] select ab,ac from tTaQLNode_tmp.tab where ab BETWEEN 2 AND 4 SELECT ab,ac FROM tTaQLNode_tmp.tab WHERE (ab) IN {2,4} select ab,ac from tTaQLNode_tmp.tab where ab NOT BETWEEN 2 AND 4 SELECT ab,ac FROM tTaQLNode_tmp.tab WHERE NOT((ab) IN {2,4}) select ab,ac from tTaQLNode_tmp.tab where ab AROUND 2 IN 4 SELECT ab,ac FROM tTaQLNode_tmp.tab WHERE (ab) IN (2)<:>(4) select ab,ac from tTaQLNode_tmp.tab where ab NOT AROUND 2 IN 4 SELECT ab,ac FROM tTaQLNode_tmp.tab WHERE NOT((ab) IN (2)<:>(4)) select ab,ac from tTaQLNode_tmp.tab where ab IN [BETWEEN 2 AND 4] SELECT ab,ac FROM tTaQLNode_tmp.tab WHERE (ab) IN [<2,4>] select ab,ac from tTaQLNode_tmp.tab where ab IN AROUND 2 IN 4 SELECT ab,ac FROM tTaQLNode_tmp.tab WHERE (ab) IN (2)<:>(4) select ab,ac from tTaQLNode_tmp.tab where ab IN [:=2,4=:<6,7<:] SELECT ab,ac FROM tTaQLNode_tmp.tab WHERE (ab) IN [<,2},{4,6>,<7,>] select ab,ac from tTaQLNode_tmp.tab where ab IN [2,(3)] SELECT ab,ac FROM tTaQLNode_tmp.tab WHERE (ab) IN [2,3] select ab,ac from tTaQLNode_tmp.tab where ab NOT IN [2,(3)] SELECT ab,ac FROM tTaQLNode_tmp.tab WHERE NOT((ab) IN [2,3]) select ab,ac from tTaQLNode_tmp.tab where ab IN [select from tTaQLNode_tmp.tab where ab>4 giving [ac=:=ac+0.5]] SELECT ab,ac FROM tTaQLNode_tmp.tab WHERE (ab) IN [SELECT FROM tTaQLNode_tmp.tab WHERE (ab)>(4) GIVING [{ac,(ac)+(0.5)}]] select ab from tTaQLNode_tmp.tab where ab IN [with a.b as t1, c.d t2,t3 in e.f select from tTaQLNode_tmp.tab where ab>7 giving [ab-1=:=ab]] SELECT ab FROM tTaQLNode_tmp.tab WHERE (ab) IN [WITH a.b AS t1,c.d AS t2,e.f AS t3 SELECT FROM tTaQLNode_tmp.tab WHERE (ab)>(7) GIVING [{(ab)-(1),ab}]] select ab from tTaQLNode_tmp.tab where ab IN [select from tTaQLNode_tmp.tab where ab>7 giving [ab-1=:(7) GIVING [{(ab)-(1),ab>]] select ab from tTaQLNode_tmp.tab where ab IN [select from tTaQLNode_tmp.tab where ab>7 giving [ab-1<:=ab]] SELECT ab FROM tTaQLNode_tmp.tab WHERE (ab) IN [SELECT FROM tTaQLNode_tmp.tab WHERE (ab)>(7) GIVING [<(ab)-(1),ab}]] select ab from tTaQLNode_tmp.tab where ab IN [select from tTaQLNode_tmp.tab where ab>7 giving [ab-1<:(7) GIVING [<(ab)-(1),ab>]] select ab,ac from tTaQLNode_tmp.tab where any(isnan(arr1)) || isnan(ab) SELECT ab,ac FROM tTaQLNode_tmp.tab WHERE (any(isnan(arr1)))||(isnan(ab)) select ab,ac from tTaQLNode_tmp.tab where ab IN arr1 SELECT ab,ac FROM tTaQLNode_tmp.tab WHERE (ab) IN arr1 select ab,ac from tTaQLNode_tmp.tab where any(arr1-array(100,shape(arr1)) > 0 && arr1<200) SELECT ab,ac FROM tTaQLNode_tmp.tab WHERE any((((arr1)-(array(100,shape(arr1))))>(0))&&((arr1)<(200))) select ab,ac from tTaQLNode_tmp.tab where count(shape(arr1))==3 && count(ab)==1 && ndim(ac)==0 && isdefined(arr2) SELECT ab,ac FROM tTaQLNode_tmp.tab WHERE ((((COUNT(shape(arr1)))=(3))&&((COUNT(ab))=(1)))&&((ndim(ac))=(0)))&&(isdefined(arr2)) select ab,ac from tTaQLNode_tmp.tab where ab in ab SELECT ab,ac FROM tTaQLNode_tmp.tab WHERE (ab) IN ab select ab,ac from tTaQLNode_tmp.tab where any(arr1 in ab) SELECT ab,ac FROM tTaQLNode_tmp.tab WHERE any((arr1) IN ab) select ab,ac from tTaQLNode_tmp.tab where (ab=ab)=T SELECT ab,ac FROM tTaQLNode_tmp.tab WHERE ((ab)=(ab))=(T) select ab,ac from tTaQLNode_tmp.tab where (ab=ab)=F SELECT ab,ac FROM tTaQLNode_tmp.tab WHERE ((ab)=(ab))=(F) select ab,ac from tTaQLNode_tmp.tab where rownumber()==rowid()+1 SELECT ab,ac FROM tTaQLNode_tmp.tab WHERE (rownumber())=((rowid())+(1)) select ab,ac from [select from tTaQLNode_tmp.tab where ab > 4] where ab < 6 SELECT ab,ac FROM [SELECT FROM tTaQLNode_tmp.tab WHERE (ab)>(4)] WHERE (ab)<(6) select ab,ac from [select from tTaQLNode_tmp.tab where ab > 4] TEMPTAB, tTaQLNode_tmp.tab where any([ab,ac] in [select ac from TEMPTAB]) SELECT ab,ac FROM [SELECT FROM tTaQLNode_tmp.tab WHERE (ab)>(4)] AS TEMPTAB,tTaQLNode_tmp.tab WHERE any(([ab,ac]) IN [SELECT ac FROM TEMPTAB AS TEMPTAB]) select ab,ac from tTaQLNode_tmp.tab where ac in [select from tTaQLNode_tmp.tab where ac in 4:6:2 giving [rowid()]] SELECT ab,ac FROM tTaQLNode_tmp.tab WHERE (ac) IN [SELECT FROM tTaQLNode_tmp.tab WHERE (ac) IN 4:6:2 GIVING [rowid()]] select ab from tTaQLNode_tmp.tab where min(maxs(arr1,[1+arr1[1,1,1]%2,3])) == 19 SELECT ab FROM tTaQLNode_tmp.tab WHERE (min(maxs(arr1,[(1)+(((arr1)[1,1,1])%(2)),3])))=(19) select ab from tTaQLNode_tmp.tab where min(1+maxs(arr1-1,1,3)) == 19 SELECT ab FROM tTaQLNode_tmp.tab WHERE (min((1)+(maxs((arr1)-(1),1,3))))=(19) select ab from tTaQLNode_tmp.tab where sum(fractiles(arr1,0.5,[2:3])) == 21+shape(arr1)[1]*count(arr1) SELECT ab FROM tTaQLNode_tmp.tab WHERE (sum(fractiles(arr1,0.5,[2:3])))=((21)+(((shape(arr1))[1])*(COUNT(arr1)))) select ab from tTaQLNode_tmp.tab where sum(ntrues(arr1%5==0,[1])) < 5 SELECT ab FROM tTaQLNode_tmp.tab WHERE (sum(ntrues(((arr1)%(5))=(0),[1])))<(5) select ab from tTaQLNode_tmp.tab where all(anys(fmod(sums(arr1,1),5)==0,[2:4])) SELECT ab FROM tTaQLNode_tmp.tab WHERE ALL(anys((fmod(sums(arr1,1),5))=(0),[2:4])) select ab from $1 SELECT ab FROM $1 select ab from $1::ANTENNA SELECT ab FROM $1::ANTENNA select ab from $1::key SELECT ab FROM $1::key select ab from $1.col::key.kb.kc SELECT ab FROM $1.col::key.kb.kc select ab from tTaQLNode_tmp.tab where [ab,ab] incone [2 rad,2rad,1rad] SELECT ab FROM tTaQLNode_tmp.tab WHERE anyCone([ab,ab],[(2)'rad',(2)'rad',(1)'rad']) select ab from tTaQLNode_tmp.tab where anycone([ab,ab],[2rad,2rad],1rad) SELECT ab FROM tTaQLNode_tmp.tab WHERE anycone([ab,ab],[(2)'rad',(2)'rad'],(1)'rad') select ab from tTaQLNode_tmp.tab where cones([ab,ab],[4rad,4rad,1rad]) SELECT ab FROM tTaQLNode_tmp.tab WHERE cones([ab,ab],[(4)'rad',(4)'rad',(1)'rad']) select ab from tTaQLNode_tmp.tab where any(cones([ab,ab],array([2rad,2rad,4rad,4rad],[2,2]),1rad)) SELECT ab FROM tTaQLNode_tmp.tab WHERE any(cones([ab,ab],array([(2)'rad',(2)'rad',(4)'rad',(4)'rad'],[2,2]),(1)'rad')) select ab from tTaQLNode_tmp.tab where [ab,ab] incone [2rad,2rad,1rad,4rad,4rad,1rad] SELECT ab FROM tTaQLNode_tmp.tab WHERE anyCone([ab,ab],[(2)'rad',(2)'rad',(1)'rad',(4)'rad',(4)'rad',(1)'rad']) select *, !~p/ab*/i, *, !~m%b%, !~ m%c%i, abc, ~p/AB*/ from tTaQLNode_tmp.tab SELECT ~p/*/,!~p/ab*/i,~p/*/,!~m%b%,!~m%c%i,abc,~p/AB*/ FROM tTaQLNode_tmp.tab count TIME from tTaQLNode_tmp.tab COUNT TIME FROM tTaQLNode_tmp.tab count ANTENNA+1,ANTENNA2+3 from tTaQLNode_tmp.tab where ab COUNT (ANTENNA)+(1),(ANTENNA2)+(3) FROM tTaQLNode_tmp.tab WHERE ab calc from tTaQLNode_tmp.tab calc findcone([ab,ab],array([2rad,2rad,4rad,4rad],[2,2]),[1rad,2rad]) CALC findcone([ab,ab],array([(2)'rad',(2)'rad',(4)'rad',(4)'rad'],[2,2]),[(1)'rad',(2)'rad']) FROM tTaQLNode_tmp.tab calc from tTaQLNode_tmp.tab calc findcone([ab,ab],[select from tTaQLNode_tmp.tab giving [ab,ab]],[1rad,2rad]) CALC findcone([ab,ab],[SELECT FROM tTaQLNode_tmp.tab GIVING [ab,ab]],[(1)'rad',(2)'rad']) FROM tTaQLNode_tmp.tab calc sum([select from tTaQLNode_tmp.tab giving [ab+1]]) CALC sum([SELECT FROM tTaQLNode_tmp.tab GIVING [(ab)+(1)]]) calc sum([select from tTaQLNode_tmp.tab giving [ab,ac,ab:ac]]) CALC sum([SELECT FROM tTaQLNode_tmp.tab GIVING [ab,ac,ab:ac]]) with x.y calc from $1 calc sum([select ab from $1]) WITH x.y CALC sum([SELECT ab FROM $1]) FROM $1 calc from tTaQLNode_tmp.tab calc ab CALC ab FROM tTaQLNode_tmp.tab calc from tTaQLNode_tmp.tab calc arr1[2,1,1] CALC (arr1)[2,1,1] FROM tTaQLNode_tmp.tab calc from tTaQLNode_tmp.tab calc arr1[1+ab%2,1,1] CALC (arr1)[(1)+((ab)%(2)),1,1] FROM tTaQLNode_tmp.tab calc from $1 calc ab+1 CALC (ab)+(1) FROM $1 with SOME.tab update tTaQLNode_tmp.tab set ab=sum(arr1)+ac*2, arr1=arr1+2 where ac>3 WITH SOME.tab UPDATE tTaQLNode_tmp.tab SET ab=(sum(arr1))+((ac)*(2)),arr1=(arr1)+(2) WHERE (ac)>(3) select ab from tTaQLNode_tmp.tab SELECT ab FROM tTaQLNode_tmp.tab update tTaQLNode_tmp.tab set ab=sum(arr1)+ac*2, arr1=arr1+2 from tTaQLNode_tmp.tabc where ac>3 orderby ac limit 5 UPDATE tTaQLNode_tmp.tab SET ab=(sum(arr1))+((ac)*(2)),arr1=(arr1)+(2) FROM tTaQLNode_tmp.tabc WHERE (ac)>(3) ORDERBY ac LIMIT 5 update tTaQLNode_tmp.tab set arr1=2, ab=sum(arr1) limit 1 offset 3 UPDATE tTaQLNode_tmp.tab SET arr1=2,ab=sum(arr1) LIMIT 1 OFFSET 3 update tTaQLNode_tmp.tab set arr1[1,1,1]=3, arr1[2,2,2]=arr1[1,1,1], ab=sum(arr1) limit 1 offset 3 UPDATE tTaQLNode_tmp.tab SET arr1[1,1,1]=3,arr1[2,2,2]=(arr1)[1,1,1],ab=sum(arr1) LIMIT 1 OFFSET 3 update tTaQLNode_tmp.tab set arr1[1,,]=4, ab=sum(arr1) limit 1 offset 3 UPDATE tTaQLNode_tmp.tab SET arr1[1,,]=4,ab=sum(arr1) LIMIT 1 OFFSET 3 update tTaQLNode_tmp.tab set arr1[arr1>0][1,,]=4, ab=sum(arr1) limit 1 offset 3 UPDATE tTaQLNode_tmp.tab SET arr1[(arr1)>(0)][1,,]=4,ab=sum(arr1) LIMIT 1 OFFSET 3 delete from tTaQLNode_tmp.tab limit 3 offset 2 DELETE FROM tTaQLNode_tmp.tab LIMIT 3 OFFSET 2 with some.tab delete from tTaQLNode_tmp.tab orderby desc ab limit 1 offset 2 WITH some.tab DELETE FROM tTaQLNode_tmp.tab ORDERBY DESC ab LIMIT 1 OFFSET 2 select ab from tTaQLNode_tmp.tab SELECT ab FROM tTaQLNode_tmp.tab select ab[am] as (c1,c2) from tTaQLNode_tmp.tab SELECT (ab)[am] AS (c1,c2) FROM tTaQLNode_tmp.tab delete from tTaQLNode_tmp.tab DELETE FROM tTaQLNode_tmp.tab delete from tTaQLNode_tmp.tab where ab%2==0 DELETE FROM tTaQLNode_tmp.tab WHERE ((ab)%(2))=(0) insert into tTaQLNode_tmp.tab select from tTaQLNode_tmp.tabc INSERT INTO tTaQLNode_tmp.tab SELECT FROM tTaQLNode_tmp.tabc with s.tab insert into tTaQLNode_tmp.tab ((ab1,ab2),ac) values (1+2,3*ab + sum([select ab from tTaQLNode_tmp.tab])) WITH s.tab INSERT INTO tTaQLNode_tmp.tab [(ab1,ab2),ac] VALUES [(1)+(2),((3)*(ab))+(sum([SELECT ab FROM tTaQLNode_tmp.tab]))] insert into tTaQLNode_tmp.tab values (1+2),(8),(9),(10) INSERT INTO tTaQLNode_tmp.tab VALUES [(1)+(2)],[8],[9],[10] insert limit 10 into tTaQLNode_tmp.tab values (1) INSERT LIMIT 10 INTO tTaQLNode_tmp.tab VALUES [1] insert into tTaQLNode_tmp.tab set ab1=1, ab2="str" INSERT INTO tTaQLNode_tmp.tab [ab1,ab2] VALUES [1,'str'] with $1 count ab,ac+1 from tTaQLnode_tmp.tab WITH $1 COUNT ab,(ac)+(1) FROM tTaQLnode_tmp.tab count min(ab),ac+1 from tTaQLnode_tmp.tab where ac>1 COUNT min(ab),(ac)+(1) FROM tTaQLnode_tmp.tab WHERE (ac)>(1) update a.tab set (c1,cm)=arr UPDATE a.tab SET (c1,cm)=arr update a.tab set (c1,cm)[1:5]=arr UPDATE a.tab SET (c1,cm)[1:5]=arr create table tTaQLNode_tmp.tab col1 i4, col2 r4 ndim=1, c3 r8 [ndim=2, shape=[3,4]] dminfo [name="ISM1",type="IncrementalStMan"], [name="SSM1",type="StandardStMan", bucketsize=1000] CREATE TABLE tTaQLNode_tmp.tab col1 I4,col2 R4 ndim=1,c3 R8 [ndim=2,shape=[3,4]] DMINFO [name='ISM1',type='IncrementalStMan'],[name='SSM1',type='StandardStMan',bucketsize=1000] with s.t create table tTaQLNode_tmp.tab limit 10*10 WITH s.t CREATE TABLE tTaQLNode_tmp.tab LIMIT (10)*(10) create table a.b like a.c CREATE TABLE a.b LIKE a.c create table a.b like a.c as a CREATE TABLE a.b LIKE a.c AS a create table a.b as plain_big like a.c drop column col1,col2 add column [] CREATE TABLE a.b AS [plain_big=T] LIKE a.c DROP COLUMN col1,col2 create table a.b as plain_big like a.c drop column col1,col2 add column (col1 i4, col2 float [unit="s", ndim=3]) CREATE TABLE a.b AS [plain_big=T] LIKE a.c DROP COLUMN col1,col2 ADD COLUMN (col1 I4,col2 R4 [unit='s',ndim=3]) with a.1,a.2 create table a.b (col1 I4) WITH a.1,a.2 CREATE TABLE a.b (col1 I4) select gcount() from [tTableGram_tmp.tst as t1,tTableGram_tmp.tst as t2,[[tTableGram_tmp.tst,tTableGram_tmp.tst] as t3] as t4] as t5 SELECT COUNTALL() FROM [tTableGram_tmp.tst AS t1,tTableGram_tmp.tst AS t2,[[tTableGram_tmp.tst,tTableGram_tmp.tst] AS t3] AS t4] AS t5 select from [t_tmp.tst1,t_tmp.tst2 subtables a,b giving a.tab] SELECT FROM [t_tmp.tst1,t_tmp.tst2 SUBTABLES a,b GIVING a.tab] alter table a.b add column col1 like t.col2 ALTER TABLE a.b ADD COLUMN col1 LIKE t.col2 with tab1 t, tab2 alter table a.b add column col1 like t.col2 i4 [ndim=2, shape=[3,4]] WITH tab1 AS t,tab2 AS tab2 ALTER TABLE a.b ADD COLUMN col1 LIKE t.col2 I4 [ndim=2,shape=[3,4]] alter table a.b add column col1 i4, col2 r4 ndim=1, c3 r8 [ndim=2, shape=[3,4]] dminfo [name="ISM1",type="IncrementalStMan"], [name="SSM1",type="StandardStMan", bucketsize=1000] ALTER TABLE a.b ADD COLUMN col1 I4,col2 R4 ndim=1,c3 R8 [ndim=2,shape=[3,4]] DMINFO [name='ISM1',type='IncrementalStMan'],[name='SSM1',type='StandardStMan',bucketsize=1000] alter table a.b from a.c, a.d copy column col1=t.col2 ALTER TABLE a.b FROM a.c,a.d COPY COLUMN col1=t.col2 with [$1,$2] alter table a.b rename column old to new WITH [$1,$2] ALTER TABLE a.b RENAME COLUMN old TO new alter table a.b rename column old to new, old1 to new1, old2 to new2 ALTER TABLE a.b RENAME COLUMN old TO new,old1 TO new1,old2 TO new2 alter table x.y drop column col ALTER TABLE x.y DROP COLUMN col alter table x.y drop column col, col1,col2 ALTER TABLE x.y DROP COLUMN col,col1,col2 alter table x.y drop column col rename column old to new ALTER TABLE x.y DROP COLUMN col RENAME COLUMN old TO new alter table x.y set keyword xyz=14 ALTER TABLE x.y SET KEYWORD xyz=14 alter table x.y set keyword xy.z=14 AS uint ALTER TABLE x.y SET KEYWORD xy.z=14 AS U4 alter table x.y set keyword xyz=[14,15,16] as float ALTER TABLE x.y SET KEYWORD xyz=[14,15,16] AS R4 alter table x.y set keyword xyz=[f1=14 as uint, f2=[f2a=4]] ALTER TABLE x.y SET KEYWORD xyz=[f1=14 AS U4,f2=[f2a=4]] alter table x.y set keyword x=[] as bool, y=[y1=2,y2=[3+4/2, a+b/c], y3=[] as s, y4=[=]] ALTER TABLE x.y SET KEYWORD x=[] AS B,y=[y1=2,y2=[(3)+((4)/(2)),(a)+((b)/(c))],y3=[] AS S,y4=[=]] drop table a.b, a.c DROP TABLE a.b,a.c show SHOW show a b c d e SHOW a b c d e with [select abs(phase(t1.DATA) - phase(t2.DATA)) as D from a.ms t1, /home/ger/WSRTA190521040_B001_original.MS t2] t select gmax(iif(D>pi(),D-3.14,D)) from t WITH [SELECT abs((phase(t1.DATA))-(phase(t2.DATA))) AS D FROM a.ms AS t1,/home/ger/WSRTA190521040_B001_original.MS AS t2] AS t SELECT gmax(iif((D)>(pi()),(D)-(3.14),D)) FROM t AS t with ~a select 1+2 from [~b,[select 3+4 from [[select 5+6],~c]], ~d giving ~e] where 3+4 giving ~f WITH ~a SELECT (1)+(2) FROM [~b,[SELECT (3)+(4) FROM [[SELECT (5)+(6)],~c]],~d GIVING ~e] WHERE (3)+(4) GIVING ~f select from a join b on a.b == b.b join c on a.c BETWEEN c.m AND c.w SELECT FROM a AS a JOIN b AS b ON (a.b)=(b.b) JOIN c AS c ON (a.c) IN {c.m,c.w} SELECT t1.rowid() as r1, t2.pol, t3.ci FROM tTableGramJoin_tmp.tab t1 JOIN ::DD t2 ON t1.cdd=t2.rowid() JOIN ::SC t3 ON t2.spw=t3.spw AND t1.ct AROUND t3.tod IN t3.w SELECT t1.rowid() AS r1,t2.pol,t3.ci FROM tTableGramJoin_tmp.tab AS t1 JOIN ::DD AS t2 ON (t1.cdd)=(t2.rowid()) JOIN ::SC AS t3 ON ((t2.spw)=(t3.spw))&&((t1.ct) IN (t3.tod)<:>(t3.w)) SELECT t1.rowid() as r1, t2.pol, t3.ci FROM tTableGramJoin_tmp.tab t1 JOIN ::DD t2 ON t1.cdd=t2.rowid() JOIN ::SC t3 ON t2.spw=t3.spw AND t1.ct AROUND t3.tod IN t3.w WHERE t3.ci%3!=0 SELECT t1.rowid() AS r1,t2.pol,t3.ci FROM tTableGramJoin_tmp.tab AS t1 JOIN ::DD AS t2 ON (t1.cdd)=(t2.rowid()) JOIN ::SC AS t3 ON ((t2.spw)=(t3.spw))&&((t1.ct) IN (t3.tod)<:>(t3.w)) WHERE ((t3.ci)%(3))<>(0) casacore-3.7.1/tables/TaQL/test/tTaQLNode.run000077500000000000000000000351271476623553700207120ustar00rootroot00000000000000#!/bin/sh #----------------------------------------------------------------------------- # Script to test the TableGram and TaQLNode class.# be deleted on exit. #============================================================================= # Whitespace around * had to be removed to avoid file name expansion by shell. $casa_checktool ./tTaQLNode 'select 1*3' $casa_checktool ./tTaQLNode 'select 1*3 giving a.b;' $casa_checktool ./tTaQLNode 'select 1*3,5+7 into a.b where a>2 groupby c1,c2 having e1<0 and e2>3 orderby d1,d2 limit 2:5:2' $casa_checktool ./tTaQLNode 'with "a."tab2 select from "a.tab" #comment' $casa_checktool ./tTaQLNode 'select from [a1.tab,a2.tab,[a3a.tab,a3b.tab]]' $casa_checktool ./tTaQLNode 'select ab,ac,ad,ae,af,ag into tTaQLNode_tmp\".data2 from "tTaQLNode tmp.tab" sh where all(ab>2) && (ae<10 || ae>11.0) && ag!= 10 + 1i orderby ac desc,ab' $casa_checktool ./tTaQLNode 'select ab,ac,ad,ae,af,ag into tTaQLNode_tmp.data2 as PLAIN_LOCAL from tTaQLNode_tmp.tab sh where all(ab>2) && (ae<10 || ae>11.0) && ag!= 10 + 1i orderby ac desc,ab' $casa_checktool ./tTaQLNode 'select from tTaQLNode_tmp.tab sh giving tTaQLNode_tmp.data2 as [type="PLAIN",endian="local",storage="multifile",blocksize=32768] dminfo [name="ISM1",type="IncrementalStMan"], [name="SSM1",type="StandardStMan", bucketsize=1000]' $casa_checktool ./tTaQLNode 'select distinct ab+1 as ab1,ac,ad,ae,af,ag from tTaQLNode_tmp.data2' $casa_checktool ./tTaQLNode 'select all ab as ab1,ac as ac1,ad,ae,af,ag from tTaQLNode_tmp.data2 orderby af' $casa_checktool ./tTaQLNode 'select ab from tTaQLNode_tmp.tab where ab==2**1**2 || ab==-2**-1*0x0A1f/-2*3' # $casa_checktool ./tTaQLNode 'select ab from tTaQLNode_tmp.tab where ab==2^1^2 || ab==-2^-1*8/-2*3' $casa_checktool ./tTaQLNode 'select ab,ac,af from tTaQLNode_tmp.tab where lower(af) == regex("v[01279]")' $casa_checktool ./tTaQLNode 'select ab,ac,af from tTaQLNode_tmp.tab where lower(af)!~m/v[01279]/' $casa_checktool ./tTaQLNode 'select ab,ac,af from tTaQLNode_tmp.tab where af ~ p/?{3,5,8}/' $casa_checktool ./tTaQLNode 'select ab,ac,af from tTaQLNode_tmp.tab where af != pattern("?{3,5,8}")' $casa_checktool ./tTaQLNode 'select ab,ac,af from tTaQLNode_tmp.tab where af == sqlpattern("_3%")' $casa_checktool ./tTaQLNode 'select ab,ac,af from tTaQLNode_tmp.tab where af like "_3%"' $casa_checktool ./tTaQLNode 'select ab,ac,af from tTaQLNode_tmp.tab where af not like "_3%"' $casa_checktool ./tTaQLNode 'select from tTaQLNode_tmp.tab where af ~ d/abc/' $casa_checktool ./tTaQLNode 'select from tTaQLNode_tmp.tab where af ~ d@abc@ib3' $casa_checktool ./tTaQLNode 'select from tTaQLNode_tmp.tab where af ~ d/abc/03bi' $casa_checktool ./tTaQLNode 'select from tTaQLNode_tmp.tab where af ~ d%abc%04' $casa_checktool ./tTaQLNode 'select ab,ac from tTaQLNode_tmp.tab where ab%1.5==0' $casa_checktool ./tTaQLNode 'select ab,(ac)mm from tTaQLNode_tmp.tab where ab%1.5==0 "m/s"' $casa_checktool ./tTaQLNode 'select ab,ac from tTaQLNode_tmp.tab where arr1[1,1,1]>=10 && arr2[1,1,1]<120' $casa_checktool ./tTaQLNode 'select * from tTaQLNode_tmp.tab where arr1[1,1,1]>=10 && arr2[1,1,1]<120' $casa_checktool ./tTaQLNode 'select ab,ac from tTaQLNode_tmp.tab where arr1[1,1,1+ab%1]>=192 orderby ad desc' $casa_checktool ./tTaQLNode 'select ab,ac from tTaQLNode_tmp.tab where cos(0.01rad) <= sin(-0.02rad)*sin(-ab/180*pi()) + cos(-0.5rad)*cos(-ab/180*pi())*cos(0.02rad - ac/180*pi())' $casa_checktool ./tTaQLNode 'select ab,ac,ad,ae,af,ag from tTaQLNode_tmp.tab where ab+ac+ad+ae+real(ag) >= year(31-12-1960) + year("31Dec60") + month(1990/05/12) + day(date(1990/01/30/12h14m33.3)) - 3910' $casa_checktool ./tTaQLNode 'select ab,ac,af from tTaQLNode_tmp.tab where ab>5 orderby af desc, ac' $casa_checktool ./tTaQLNode 'select ab,ac,af from tTaQLNode_tmp.tab orderby arr1[1,1,1]' $casa_checktool ./tTaQLNode 'select ab,ac from tTaQLNode_tmp.tab orderby round(2*sin(ab)),ac desc' $casa_checktool ./tTaQLNode 'select ab,ac from tTaQLNode_tmp.tab where ab < mean([3:6,ab])' $casa_checktool ./tTaQLNode 'select ab,ac from tTaQLNode_tmp.tab where ab < 4 && EXISTS (select from tTaQLNode_tmp.tab)' $casa_checktool ./tTaQLNode 'select ab,ac from tTaQLNode_tmp.tab where ab < 4 && EXISTS (select from tTaQLNode_tmp.tab LIMIT 11)' $casa_checktool ./tTaQLNode 'select ab,ac from tTaQLNode_tmp.tab where ab IN (select ac from tTaQLNode_tmp.tab where ab>4)' $casa_checktool ./tTaQLNode 'select ab,ac from tTaQLNode_tmp.tab where ab BETWEEN 2 AND 4' $casa_checktool ./tTaQLNode 'select ab,ac from tTaQLNode_tmp.tab where ab NOT BETWEEN 2 AND 4' $casa_checktool ./tTaQLNode 'select ab,ac from tTaQLNode_tmp.tab where ab AROUND 2 IN 4' $casa_checktool ./tTaQLNode 'select ab,ac from tTaQLNode_tmp.tab where ab NOT AROUND 2 IN 4' $casa_checktool ./tTaQLNode 'select ab,ac from tTaQLNode_tmp.tab where ab IN [BETWEEN 2 AND 4]' $casa_checktool ./tTaQLNode 'select ab,ac from tTaQLNode_tmp.tab where ab IN AROUND 2 IN 4' $casa_checktool ./tTaQLNode 'select ab,ac from tTaQLNode_tmp.tab where ab IN [:=2,4=:<6,7<:]' $casa_checktool ./tTaQLNode 'select ab,ac from tTaQLNode_tmp.tab where ab IN [2,(3)]' $casa_checktool ./tTaQLNode 'select ab,ac from tTaQLNode_tmp.tab where ab NOT IN [2,(3)]' $casa_checktool ./tTaQLNode 'select ab,ac from tTaQLNode_tmp.tab where ab IN [select from tTaQLNode_tmp.tab where ab>4 giving [ac=:=ac+0.5]]' $casa_checktool ./tTaQLNode 'select ab from tTaQLNode_tmp.tab where ab IN [with a.b as t1, c.d t2,t3 in e.f select from tTaQLNode_tmp.tab where ab>7 giving [ab-1=:=ab]]' $casa_checktool ./tTaQLNode 'select ab from tTaQLNode_tmp.tab where ab IN [select from tTaQLNode_tmp.tab where ab>7 giving [ab-1=:7 giving [ab-1<:=ab]]' $casa_checktool ./tTaQLNode 'select ab from tTaQLNode_tmp.tab where ab IN [select from tTaQLNode_tmp.tab where ab>7 giving [ab-1<: 0 && arr1<200)' $casa_checktool ./tTaQLNode 'select ab,ac from tTaQLNode_tmp.tab where count(shape(arr1))==3 && count(ab)==1 && ndim(ac)==0 && isdefined(arr2)' $casa_checktool ./tTaQLNode 'select ab,ac from tTaQLNode_tmp.tab where ab in ab' $casa_checktool ./tTaQLNode 'select ab,ac from tTaQLNode_tmp.tab where any(arr1 in ab)' $casa_checktool ./tTaQLNode 'select ab,ac from tTaQLNode_tmp.tab where (ab=ab)=T' $casa_checktool ./tTaQLNode 'select ab,ac from tTaQLNode_tmp.tab where (ab=ab)=F' $casa_checktool ./tTaQLNode 'select ab,ac from tTaQLNode_tmp.tab where rownumber()==rowid()+1' $casa_checktool ./tTaQLNode 'select ab,ac from [select from tTaQLNode_tmp.tab where ab > 4] where ab < 6' $casa_checktool ./tTaQLNode 'select ab,ac from [select from tTaQLNode_tmp.tab where ab > 4] TEMPTAB, tTaQLNode_tmp.tab where any([ab,ac] in [select ac from TEMPTAB])' $casa_checktool ./tTaQLNode 'select ab,ac from tTaQLNode_tmp.tab where ac in [select from tTaQLNode_tmp.tab where ac in 4:6:2 giving [rowid()]]' $casa_checktool ./tTaQLNode 'select ab from tTaQLNode_tmp.tab where min(maxs(arr1,[1+arr1[1,1,1]%2,3])) == 19' $casa_checktool ./tTaQLNode 'select ab from tTaQLNode_tmp.tab where min(1+maxs(arr1-1,1,3)) == 19' $casa_checktool ./tTaQLNode 'select ab from tTaQLNode_tmp.tab where sum(fractiles(arr1,0.5,[2:3])) == 21+shape(arr1)[1]*count(arr1)' $casa_checktool ./tTaQLNode 'select ab from tTaQLNode_tmp.tab where sum(ntrues(arr1%5==0,[1])) < 5' $casa_checktool ./tTaQLNode 'select ab from tTaQLNode_tmp.tab where all(anys(fmod(sums(arr1,1),5)==0,[2:4]))' $casa_checktool ./tTaQLNode 'select ab from $1' $casa_checktool ./tTaQLNode 'select ab from $1::ANTENNA' $casa_checktool ./tTaQLNode 'select ab from $1::key' $casa_checktool ./tTaQLNode 'select ab from $1.col::key.kb.kc' $casa_checktool ./tTaQLNode 'select ab from tTaQLNode_tmp.tab where [ab,ab] incone [2 rad,2rad,1rad]' $casa_checktool ./tTaQLNode 'select ab from tTaQLNode_tmp.tab where anycone([ab,ab],[2rad,2rad],1rad)' $casa_checktool ./tTaQLNode 'select ab from tTaQLNode_tmp.tab where cones([ab,ab],[4rad,4rad,1rad])' $casa_checktool ./tTaQLNode 'select ab from tTaQLNode_tmp.tab where any(cones([ab,ab],array([2rad,2rad,4rad,4rad],[2,2]),1rad))' $casa_checktool ./tTaQLNode 'select ab from tTaQLNode_tmp.tab where [ab,ab] incone [2rad,2rad,1rad,4rad,4rad,1rad]' $casa_checktool ./tTaQLNode 'select *, !~p/ab*/i, *, !~m%b%, !~ m%c%i, abc, ~p/AB*/ from tTaQLNode_tmp.tab' $casa_checktool ./tTaQLNode 'count TIME from tTaQLNode_tmp.tab' $casa_checktool ./tTaQLNode 'count ANTENNA+1,ANTENNA2+3 from tTaQLNode_tmp.tab where ab' $casa_checktool ./tTaQLNode 'calc from tTaQLNode_tmp.tab calc findcone([ab,ab],array([2rad,2rad,4rad,4rad],[2,2]),[1rad,2rad])' $casa_checktool ./tTaQLNode 'calc from tTaQLNode_tmp.tab calc findcone([ab,ab],[select from tTaQLNode_tmp.tab giving [ab,ab]],[1rad,2rad])' echo "" $casa_checktool ./tTaQLNode 'calc sum([select from tTaQLNode_tmp.tab giving [ab+1]])' $casa_checktool ./tTaQLNode 'calc sum([select from tTaQLNode_tmp.tab giving [ab,ac,ab:ac]])' $casa_checktool ./tTaQLNode 'with x.y calc from $1 calc sum([select ab from $1])' $casa_checktool ./tTaQLNode 'calc from tTaQLNode_tmp.tab calc ab' $casa_checktool ./tTaQLNode 'calc from tTaQLNode_tmp.tab calc arr1[2,1,1]' $casa_checktool ./tTaQLNode 'calc from tTaQLNode_tmp.tab calc arr1[1+ab%2,1,1]' $casa_checktool ./tTaQLNode 'calc from $1 calc ab+1' echo "" $casa_checktool ./tTaQLNode 'with SOME.tab update tTaQLNode_tmp.tab set ab=sum(arr1)+ac*2, arr1=arr1+2 where ac>3' $casa_checktool ./tTaQLNode 'select ab from tTaQLNode_tmp.tab' $casa_checktool ./tTaQLNode 'update tTaQLNode_tmp.tab set ab=sum(arr1)+ac*2, arr1=arr1+2 from tTaQLNode_tmp.tabc where ac>3 orderby ac limit 5' $casa_checktool ./tTaQLNode 'update tTaQLNode_tmp.tab set arr1=2, ab=sum(arr1) limit 1 offset 3' $casa_checktool ./tTaQLNode 'update tTaQLNode_tmp.tab set arr1[1,1,1]=3, arr1[2,2,2]=arr1[1,1,1], ab=sum(arr1) limit 1 offset 3' $casa_checktool ./tTaQLNode 'update tTaQLNode_tmp.tab set arr1[1,,]=4, ab=sum(arr1) limit 1 offset 3' $casa_checktool ./tTaQLNode 'update tTaQLNode_tmp.tab set arr1[arr1>0][1,,]=4, ab=sum(arr1) limit 1 offset 3' $casa_checktool ./tTaQLNode 'delete from tTaQLNode_tmp.tab limit 3 offset 2' $casa_checktool ./tTaQLNode 'with some.tab delete from tTaQLNode_tmp.tab orderby desc ab limit 1 offset 2' $casa_checktool ./tTaQLNode 'select ab from tTaQLNode_tmp.tab' $casa_checktool ./tTaQLNode 'select ab[am] as (c1,c2) from tTaQLNode_tmp.tab' $casa_checktool ./tTaQLNode 'delete from tTaQLNode_tmp.tab' $casa_checktool ./tTaQLNode 'delete from tTaQLNode_tmp.tab where ab%2==0' $casa_checktool ./tTaQLNode 'insert into tTaQLNode_tmp.tab select from tTaQLNode_tmp.tabc' $casa_checktool ./tTaQLNode 'with s.tab insert into tTaQLNode_tmp.tab ((ab1,ab2),ac) values (1+2,3*ab + sum([select ab from tTaQLNode_tmp.tab]))' $casa_checktool ./tTaQLNode 'insert into tTaQLNode_tmp.tab values (1+2),(8),(9),(10)' $casa_checktool ./tTaQLNode 'insert limit 10 into tTaQLNode_tmp.tab values (1)' $casa_checktool ./tTaQLNode 'insert into tTaQLNode_tmp.tab set ab1=1, ab2="str"' $casa_checktool ./tTaQLNode 'with $1 count ab,ac+1 from tTaQLnode_tmp.tab' $casa_checktool ./tTaQLNode 'count min(ab),ac+1 from tTaQLnode_tmp.tab where ac>1' $casa_checktool ./tTaQLNode 'update a.tab set (c1,cm)=arr' $casa_checktool ./tTaQLNode 'update a.tab set (c1,cm)[1:5]=arr' $casa_checktool ./tTaQLNode 'create table tTaQLNode_tmp.tab col1 i4, col2 r4 ndim=1, c3 r8 [ndim=2, shape=[3,4]] dminfo [name="ISM1",type="IncrementalStMan"], [name="SSM1",type="StandardStMan", bucketsize=1000]' $casa_checktool ./tTaQLNode 'with s.t create table tTaQLNode_tmp.tab limit 10*10' $casa_checktool ./tTaQLNode 'create table a.b like a.c' $casa_checktool ./tTaQLNode 'create table a.b like a.c as a' $casa_checktool ./tTaQLNode 'create table a.b as plain_big like a.c drop column col1,col2 add column []' $casa_checktool ./tTaQLNode 'create table a.b as plain_big like a.c drop column col1,col2 add column (col1 i4, col2 float [unit="s", ndim=3])' $casa_checktool ./tTaQLNode 'with a.1,a.2 create table a.b (col1 I4)' $casa_checktool ./tTaQLNode 'select gcount() from [tTableGram_tmp.tst as t1,tTableGram_tmp.tst as t2,[[tTableGram_tmp.tst,tTableGram_tmp.tst] as t3] as t4] as t5' $casa_checktool ./tTaQLNode 'select from [t_tmp.tst1,t_tmp.tst2 subtables a,b giving a.tab]' $casa_checktool ./tTaQLNode 'alter table a.b add column col1 like t.col2' $casa_checktool ./tTaQLNode 'with tab1 t, tab2 alter table a.b add column col1 like t.col2 i4 [ndim=2, shape=[3,4]]' $casa_checktool ./tTaQLNode 'alter table a.b add column col1 i4, col2 r4 ndim=1, c3 r8 [ndim=2, shape=[3,4]] dminfo [name="ISM1",type="IncrementalStMan"], [name="SSM1",type="StandardStMan", bucketsize=1000]' $casa_checktool ./tTaQLNode 'alter table a.b from a.c, a.d copy column col1=t.col2' $casa_checktool ./tTaQLNode 'with [$1,$2] alter table a.b rename column old to new' $casa_checktool ./tTaQLNode 'alter table a.b rename column old to new, old1 to new1, old2 to new2' $casa_checktool ./tTaQLNode 'alter table x.y drop column col' $casa_checktool ./tTaQLNode 'alter table x.y drop column col, col1,col2' $casa_checktool ./tTaQLNode 'alter table x.y drop column col rename column old to new' $casa_checktool ./tTaQLNode 'alter table x.y set keyword xyz=14' $casa_checktool ./tTaQLNode 'alter table x.y set keyword xy.z=14 AS uint' $casa_checktool ./tTaQLNode 'alter table x.y set keyword xyz=[14,15,16] as float' $casa_checktool ./tTaQLNode 'alter table x.y set keyword xyz=[f1=14 as uint, f2=[f2a=4]]' $casa_checktool ./tTaQLNode 'alter table x.y set keyword x=[] as bool, y=[y1=2,y2=[3+4/2, a+b/c], y3=[] as s, y4=[=]]' $casa_checktool ./tTaQLNode 'drop table a.b, a.c' $casa_checktool ./tTaQLNode 'show' $casa_checktool ./tTaQLNode 'show a b c d e' $casa_checktool ./tTaQLNode 'with [select abs(phase(t1.DATA) - phase(t2.DATA)) as D from a.ms t1, /home/ger/WSRTA190521040_B001_original.MS t2] t select gmax(iif(D>pi(),D-3.14,D)) from t' $casa_checktool ./tTaQLNode "with ~a select 1+2 from [~b,[select 3+4 from [[select 5+6],~c]], ~d giving ~e] where 3+4 giving ~f" $casa_checktool ./tTaQLNode "select from a join b on a.b == b.b join c on a.c BETWEEN c.m AND c.w" $casa_checktool ./tTaQLNode "SELECT t1.rowid() as r1, t2.pol, t3.ci FROM tTableGramJoin_tmp.tab t1 JOIN ::DD t2 ON t1.cdd=t2.rowid() JOIN ::SC t3 ON t2.spw=t3.spw AND t1.ct AROUND t3.tod IN t3.w" $casa_checktool ./tTaQLNode "SELECT t1.rowid() as r1, t2.pol, t3.ci FROM tTableGramJoin_tmp.tab t1 JOIN ::DD t2 ON t1.cdd=t2.rowid() JOIN ::SC t3 ON t2.spw=t3.spw AND t1.ct AROUND t3.tod IN t3.w WHERE t3.ci%3!=0" casacore-3.7.1/tables/TaQL/test/tTableExprData.cc000066400000000000000000000117371476623553700215420ustar00rootroot00000000000000//# tTableExprData.cc: Test program for class tTableExprData //# Copyright (C) 2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include // // Test program for class TableExprData. // // This program tests the class TableExprData. // This example shows how a data set consisting of two vectors // of scalars can be used. // Write a class derived from TableExprData to handle the vectors. class MyTestClass : public TableExprData { public: // Constructor checks if both vectors have equal length. MyTestClass (const Vector& fld1, const Vector& fld2) : itsFld1(fld1), itsFld2(fld2), itsEntry(0) { AlwaysAssert (fld1.nelements() == fld2.nelements(), AipsError); } virtual ~MyTestClass() {} void next() { itsEntry++; } // Note that only the get functions for the possible types are needed. // The exception should never be thrown unless things are screwed up. virtual Int64 getInt (const Block& fieldNrs) const { switch (fieldNrs[0]) { case 0: return itsFld1(itsEntry); default: throw AipsError(); } } virtual String getString (const Block& fieldNrs) const { switch (fieldNrs[0]) { case 1: return itsFld2(itsEntry); default: throw AipsError(); } } virtual DataType dataType (const Block& fieldNrs) const { switch (fieldNrs[0]) { case 0: return TpInt; case 1: return TpString; default: throw AipsError(); } } // Make a Record to give to vectors a name. // The order in which the fields are defined determines the fieldnrs // passed to the get functions. static Record makeRecord() { RecordDesc desc; desc.addField ("fld1", TpInt); desc.addField ("fld2", TpString); return Record(desc); } private: Vector itsFld1; Vector itsFld2; uInt itsEntry; }; Vector findMatches (const Vector& fld1, const Vector& fld2) { // Make some expression. // First create a Record to make the names and types known. Record rec(MyTestClass::makeRecord()); TableExprNode expr (makeRecordExpr(rec,"fld1") > 10 && makeRecordExpr(rec,"fld2") != pattern("*xxx*")); // Now evaluate the expression for each entry in the vector. // Make a MyTestClass object to handle the vectors and put it in // a TableExprId object for the TaQL evaluator. // Note that TableExprId holds a pointer to the original MyTestClass // object, so the TaQL evaluator 'sees' the changes we make by // using the its next() function. MyTestClass subj(fld1, fld2); TableExprId eid(subj); // The matching entry numbers are stored in a vector. Vector result(fld1.nelements()); uInt nr=0; Bool valb; for (uInt i=0; i fld1(4); fld1(0) = 4; fld1(1) = 10; fld1(2) = 11; fld1(3) = 20; Vector fld2(4); fld2(0) = "xxx"; fld2(1) = ""; fld2(2) = "axxxa"; fld2(3) = "axxax"; Vector m = findMatches (fld1, fld2); AlwaysAssertExit (m.nelements() == 1); AlwaysAssertExit (m(0) == 3); } catch (std::exception& x) { cout << "Unexpected exception: " << x.what() << endl; return 1; } catch (...) { cout << "Unexpected unknown exception" << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/tables/TaQL/test/tTableGram.cc000066400000000000000000000276211476623553700207170ustar00rootroot00000000000000//# tTableGram.cc: This program tests table commands using TableGram/Parse //# Copyright (C) 1994,1995,1996,1998,2000,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Test program for table commands from user interface // // This program tests table commands with an SQL-like grammar. // The grammar is scanned and parsed using the flex/bison file TableGram.l/y // and with the help of the class TableParse. // It ask for commands until a "q" is given. // When columns are selected, it will show their contents. void testUnit (const String& comm, double expResult, const String& expUnit) { TaQLResult result = tableCommand (comm); AlwaysAssert (!result.isTable(), AipsError); TableExprNode node = result.node(); if (!near (node.getDouble(0), expResult) || node.unit().getName() != expUnit) { cout << "Error in evaluating: " + comm << endl; cout << " expected " << expResult << ' ' << expUnit << endl; cout << " found " << node.getDouble(0) << ' ' << node.unit().getName() << endl; } } void testUnit (const String& comm, Bool expResult) { TaQLResult result = tableCommand (comm); AlwaysAssert (!result.isTable(), AipsError); TableExprNode node = result.node(); if (node.getBool(0) != expResult || node.unit().getName() != "") { cout << "Error in evaluating: " + comm << endl; cout << " expected " << expResult << endl; cout << " found " << node.getBool(0) << ' ' << node.unit().getName() << endl; } } void checkUnits() { testUnit ("calc 100mm", 100., "mm"); testUnit ("calc 100mm cm", 10., "cm"); testUnit ("calc (100mm cm)dm", 1., "dm"); testUnit ("calc sin(90 deg)", 1., ""); testUnit ("calc asin(1) deg", 90., "deg"); testUnit ("calc min(1cm, 2mm)", 0.2, "cm"); testUnit ("calc min(1cm, 2mm)mm", 2., "mm"); testUnit ("calc min([2mm, 0.1cm])", 1., "mm"); testUnit ("calc iif(T, 1mm, 2cm)", 1., "mm"); testUnit ("calc iif(F, 1mm, 2cm)", 20., "mm"); testUnit ("calc 20mm+3cm", 50., "mm"); testUnit ("calc 2 cm + 30 'mm'", 5., "cm"); testUnit ("calc 2km/20s", 0.1, "km/(s)"); testUnit ("calc 2km/20", 0.1, "km"); testUnit ("calc 2 'km/s' * 20s", 40., "km/s.s"); testUnit ("calc 2 'km/h' + 1 'm/s'", 5.6, "km/h"); testUnit ("calc 2m*3m", 6., "m.m"); testUnit ("calc sumsqr([3.m,10dm])", 10., "m.m"); testUnit ("calc sqrt(9 'm2')", 3., "m"); testUnit ("calc 20Aug06 - 13Aug06", 7., "d"); testUnit ("calc 20Aug06 +86400s + 12*60min - 13Aug06", 8.5, "d"); testUnit ("calc sum([2mm,0.1cm] + [3cm,2mm])", 35, "mm"); testUnit ("calc 1mm in [2mm,0.1cm]", True); testUnit ("calc 0.02dm in [2mm,0.1cm]", True); testUnit ("calc 0.025dm in [2mm<:<0.3cm]", True); testUnit ("calc 0.02dm in [2mm<:<0.3cm]", False); testUnit ("calc !near(2cm,20mm)", False); testUnit ("calc 0.002km == 2m", True); testUnit ("calc [180deg/pi(),180deg/pi()] incone [2rad,2rad,1rad]", True); testUnit ("calc [90deg/pi(),90deg/pi()] incone [2rad,2rad,1rad]", False); testUnit ("calc [1rad,1.rad] incone [1rad,1rad,1arcsec]", True); testUnit ("calc [1rad,1.0001rad] incone [1rad,1rad,1arcsec]", False); testUnit ("calc [1h0m,15d0m] incone [15deg,15deg,1arcsec]", True); testUnit ("calc near(4.67312e+09s-3200, mjd('2006/12/18'))", True); testUnit ("calc 172800s / 86400", 2., "d"); testUnit ("calc 172800m / 86400", 2., "m"); } void seltab (const String&); void docomm (); int main (int argc, const char* argv[]) { try { if (argc > 1) { if (String(argv[1]) == "0") { // Check the unit handling in TaQL. checkUnits(); } else { // Execute the given command. seltab(argv[1]); } } else { // Do some interactive tests. docomm(); } } catch (std::exception& x) { cout << "\nCaught an exception: " << x.what() << endl; return 1; } return 0; // successfully executed } // Ask and execute command till empty string is given. void docomm() { char comm[1025]; while (True) { cout << "Table command (q=quit): "; cin.getline (comm, 1024); String str(comm); if (str == "q") break; try { seltab (str); } catch (std::exception& x) { cout << x.what() << endl; } } } // Show the required columns. // First test if they exist and contain scalars or arrays. void showtab (const Table& tab, const Vector& colnam) { uInt nrcol = 0; PtrBlock tableColumns(colnam.nelements()); for (uInt i=0; icolumnDesc().isScalar() && ! tableColumns[nrcol]->columnDesc().isArray()) { cout << "Column " << colnam(i) << " contains scalars nor arrays" << endl; delete tableColumns[nrcol]; }else{ nrcol++; } } } if (nrcol == 0) { return; } for (uInt i=0; icolumnDesc().isArray()) { cout << " shape=" << tableColumns[j]->shape (i); }else{ switch (tableColumns[j]->columnDesc().dataType()) { case TpBool: cout << " " << tableColumns[j]->asBool (i); break; case TpString: cout << " " << tableColumns[j]->asString (i); break; case TpComplex: case TpDComplex: cout << " " << tableColumns[j]->asDComplex (i); break; default: cout << " " << tableColumns[j]->asdouble (i); } } } cout << endl; } for (uInt i=0; i(expr.getRep().get()); if (nodePtr != 0) { // The node represents a part of an array; get its index node. const TableExprNodeIndex* inxNode = nodePtr->getIndexNode(); // If a constant index accessing a single element, // get the Slicer defining the index. if (inxNode->isConstant() && inxNode->isSingle()) { const Slicer& indices = inxNode->getConstantSlicer(); // Extract the index from it. cout << "Index: " << indices.start() << endl; } } const Unit& unit = expr.unit(); if (! unit.empty()) { cout << "Unit: " << unit.getName() << endl; } if (expr.isScalar()) { Vector rownrs(expr.nrow()); indgen (rownrs); switch (expr.getColumnDataType()) { case TpBool: cout << expr.getColumnBool (rownrs); break; case TpUChar: cout << expr.getColumnuChar (rownrs); break; case TpShort: cout << expr.getColumnShort (rownrs); break; case TpUShort: cout << expr.getColumnuShort (rownrs); break; case TpInt: cout << expr.getColumnInt (rownrs); break; case TpUInt: cout << expr.getColumnuInt (rownrs); break; case TpInt64: cout << expr.getColumnInt64 (rownrs); break; case TpFloat: cout << expr.getColumnFloat (rownrs); break; case TpDouble: cout << expr.getColumnDouble (rownrs); break; case TpComplex: cout << expr.getColumnComplex (rownrs); break; case TpDComplex: cout << expr.getColumnDComplex (rownrs); break; case TpString: cout << expr.getColumnString (rownrs); break; default: cout << "Unknown expression scalar type " << expr.getColumnDataType(); } cout << endl; } else { for (rownr_t i=0; i arr; expr.get (i, arr); cout << arr.array(); break; } case TpInt64: { MArray arr; expr.get (i, arr); cout << arr.array(); break; } case TpDouble: { MArray arr; expr.get (i, arr); cout << arr.array(); break; } case TpDComplex: { MArray arr; expr.get (i, arr); cout << arr.array(); break; } case TpString: { MArray arr; expr.get (i, arr); cout << arr.array(); break; } default: cout << "Unknown expression array type " << expr.dataType(); } cout << endl; } } } // Sort and select data. void seltab (const String& str) { // If no command is given, assume it is CALC. String::size_type spos = str.find_first_not_of (' '); Bool addCalc = False; String s; if (spos != String::npos) { String::size_type epos = str.find (' ', spos); if (epos == String::npos) { addCalc = True; } else { s = str.substr(spos, epos-spos); s.downcase(); addCalc = !(s=="with" || s=="select" || s=="update" || s=="insert" || s=="calc" || s=="delete" || s=="count" || s=="create" || s=="createtable" || s=="drop" || s=="droptable" || s=="alter" || s=="altertable" || s=="using" || s=="usingstyle" || s=="time" || s=="show" || s=="help"); } } String strc(str); if (addCalc) { strc = "CALC " + str; } cout << strc << endl; // Parse and execute the command. TaQLResult result; Table* tabp = 0; Vector vecstr; String cmd; // A semicolon can be used to specify a possible table after it (for $1). String::size_type semipos = strc.find(';'); if (semipos == String::npos) { result = tableCommand (strc, vecstr, cmd); } else { Table tab(strc.after(semipos)); std::vector tabblock(1, &tab); result = tableCommand (strc.before(semipos), tabblock, vecstr, cmd); } cout << " has been executed" << endl; if (result.isTable()) { tabp = new Table(result.table()); cout << " " << cmd << " result of " << tabp->nrow() << " rows" << endl; // Show the selected column names. // Add _COUNT_ column if counting is done. if (s == "count") { uInt nrcol = vecstr.size(); vecstr.resize (nrcol+1, True); vecstr[nrcol] = "_COUNT_"; } cout << vecstr.nelements() << " selected columns: "; for (uInt i=0; i 0) { showtab (*tabp, vecstr); } } else { showExpr (result.node()); } delete tabp; } casacore-3.7.1/tables/TaQL/test/tTableGram.out000066400000000000000000000734411476623553700211420ustar00rootroot00000000000000testing select ... select 3+4 has been executed select result of 1 rows 1 selected columns: Col_1 7 select 3+4 where 1>2 has been executed select result of 0 rows 1 selected columns: Col_1 select gsum(3+4) as C having C > 12 has been executed select result of 0 rows 1 selected columns: C select gsum(3+4) as C where 1<2 having C<12 orderby C giving as memory has been executed select result of 1 rows 1 selected columns: C 7 select ab,ac,ad,ae,af,ag into tTableGram_tmp.data2 from tTableGram_tmp.tab sh where all(ab>2) && (ae<10 || ae>11.0) && ag!= 10 + 1i orderby ac desc,ab has been executed select result of 5 rows 6 selected columns: ab ac ad ae af ag 9 10 11 12 V9 (11,0) 6 7 8 9 V6 (8,0) 5 6 7 8 V5 (7,0) 4 5 6 7 V4 (6,0) 3 4 5 6 V3 (5,0) select distinct ab+1,ac as ac2 from tTableGram_tmp.tab has been executed select result of 10 rows 2 selected columns: Col_1 ac2 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 select distinct ab,ac,ad,ae,af,ag from tTableGram_tmp.data2 has been executed select result of 5 rows 6 selected columns: ab ac ad ae af ag 9 10 11 12 V9 (11,0) 6 7 8 9 V6 (8,0) 5 6 7 8 V5 (7,0) 4 5 6 7 V4 (6,0) 3 4 5 6 V3 (5,0) select all ab,ac,ad,ae,af,ag from tTableGram_tmp.data2 orderby af has been executed select result of 5 rows 6 selected columns: ab ac ad ae af ag 3 4 5 6 V3 (5,0) 4 5 6 7 V4 (6,0) 5 6 7 8 V5 (7,0) 6 7 8 9 V6 (8,0) 9 10 11 12 V9 (11,0) select ab from tTableGram_tmp.tab where ab==2**1**2 || ab==-2**-1*8/-2*3 has been executed select result of 2 rows 1 selected columns: ab 2 6 select ab,ac,af from ../../TaQL/./test/tTableGram_tmp.tab where lower(af) == regex("v[01279]") has been executed select result of 5 rows 3 selected columns: ab ac af 0 1 V0 1 2 V1 2 3 V2 7 8 V7 9 10 V9 select ab,ac,af from tTableGram_tmp.tab where af!~m/V[01279]/i has been executed select result of 5 rows 3 selected columns: ab ac af 3 4 V3 4 5 V4 5 6 V5 6 7 V6 8 9 V8 select ab,ac,af from tTableGram_tmp.tab where af ~ p/?{3,5,8}/ has been executed select result of 3 rows 3 selected columns: ab ac af 3 4 V3 5 6 V5 8 9 V8 select ab,ac,af from tTableGram_tmp.tab where af != pattern("?{3,5,8}") has been executed select result of 7 rows 3 selected columns: ab ac af 0 1 V0 1 2 V1 2 3 V2 4 5 V4 6 7 V6 7 8 V7 9 10 V9 select ab,ac,af from tTableGram_tmp.tab where af == sqlpattern("_3%") has been executed select result of 1 rows 3 selected columns: ab ac af 3 4 V3 select ab,ac,af from tTableGram_tmp.tab where af like "_3%" has been executed select result of 1 rows 3 selected columns: ab ac af 3 4 V3 select ab,ac,af from tTableGram_tmp.tab where af not like "_3%" has been executed select result of 9 rows 3 selected columns: ab ac af 0 1 V0 1 2 V1 2 3 V2 4 5 V4 5 6 V5 6 7 V6 7 8 V7 8 9 V8 9 10 V9 select ab,ac from tTableGram_tmp.tab where ab%1.5==0 has been executed select result of 4 rows 2 selected columns: ab ac 0 1 3 4 6 7 9 10 select ab,ac from tTableGram_tmp.tab where arr1[1,1,1]>=10 && arr2[1,1,1]<120 has been executed select result of 4 rows 2 selected columns: ab ac 1 2 2 3 3 4 4 5 select *, !~ p/ar*/ from tTableGram_tmp.tab where arr1[1,1,1]>=10 && arr2[1,1,1]<120 has been executed select result of 4 rows 6 selected columns: ab ad ag ac ae af 1 3 (3,0) 2 4 V1 2 4 (4,0) 3 5 V2 3 5 (5,0) 4 6 V3 4 6 (6,0) 5 7 V4 select arr1,~ f/t1\..*/,!~f/t1\.ar.*/ from tTableGram_tmp.tab t1 where arr1[1,1,1]>=10 && arr2[1,1,1]<120 has been executed select result of 4 rows 7 selected columns: arr1 ab ad ag ac ae af shape=[2, 3, 4] 1 3 (3,0) 2 4 V1 shape=[2, 3, 4] 2 4 (4,0) 3 5 V2 shape=[2, 3, 4] 3 5 (5,0) 4 6 V3 shape=[2, 3, 4] 4 6 (6,0) 5 7 V4 select ab,ac from tTableGram_tmp.tab where arr1[1,1,1+ab%1]>=192 orderby ad desc has been executed select result of 2 rows 2 selected columns: ab ac 9 10 8 9 select ab,ac from tTableGram_tmp.tab where cos(2d0m) <= sin(-2d0m)*sin(-ab/180*pi()) + cos(-2deg)*cos(-ab/180*pi())*cos(3d0m - ac/180*pi()) has been executed select result of 3 rows 2 selected columns: ab ac 1 2 2 3 3 4 select ab,ac,ad,ae,af,ag from tTableGram_tmp.tab where ab+ac+ad+ae+real(ag) >= year(31-12-1960) + year("31Dec60") + month(1990/05/12) + day(date(1990/01/30/12h14m33.3)) - 3910 has been executed select result of 2 rows 6 selected columns: ab ac ad ae af ag 8 9 10 11 V8 (10,0) 9 10 11 12 V9 (11,0) select ab,ac,af from tTableGram_tmp.tab where ab>5 orderby af desc, ac has been executed select result of 4 rows 3 selected columns: ab ac af 9 10 V9 8 9 V8 7 8 V7 6 7 V6 select ab,ac,af from tTableGram_tmp.tab orderby arr1[1,1,1] has been executed select result of 10 rows 3 selected columns: ab ac af 0 1 V0 1 2 V1 2 3 V2 3 4 V3 4 5 V4 5 6 V5 6 7 V6 7 8 V7 8 9 V8 9 10 V9 select ab,ac from tTableGram_tmp.tab orderby round(2*sin(ab)),ac desc has been executed select result of 10 rows 2 selected columns: ab ac 5 6 4 5 6 7 3 4 0 1 9 10 7 8 8 9 2 3 1 2 select ab,ac from tTableGram_tmp.tab where ab < mean([3:6,ab]) has been executed select result of 5 rows 2 selected columns: ab ac 0 1 1 2 2 3 3 4 4 5 select ab from tTableGram_tmp.tab where ab in [select ab+5 from tTableGram_tmp.tab] has been executed select result of 5 rows 1 selected columns: ab 5 6 7 8 9 select rowid() from tTableGram_tmp.tab where ab+235 in [select arr1 from tTableGram_tmp.tab] has been executed select result of 5 rows 1 selected columns: Col_1 0 1 2 3 4 select ab,ac from tTableGram_tmp.tab where ab < 4 && EXISTS (select from tTableGram_tmp.tab) has been executed select result of 4 rows 2 selected columns: ab ac 0 1 1 2 2 3 3 4 select ab,ac from tTableGram_tmp.tab where ab < 4 && EXISTS (select from tTableGram_tmp.tab LIMIT 11) has been executed select result of 0 rows 2 selected columns: ab ac select ab,ac from tTableGram_tmp.tab where ab IN (select ac from tTableGram_tmp.tab where ab>4) has been executed select result of 4 rows 2 selected columns: ab ac 6 7 7 8 8 9 9 10 select ab,ac from tTableGram_tmp.tab where ab AROUND 3 IN 2 has been executed select result of 3 rows 2 selected columns: ab ac 2 3 3 4 4 5 select ab,ac from tTableGram_tmp.tab where ab NOT AROUND 3 IN 2 has been executed select result of 7 rows 2 selected columns: ab ac 0 1 1 2 5 6 6 7 7 8 8 9 9 10 select ab,ac from tTableGram_tmp.tab where ab BETWEEN 2 AND 4 has been executed select result of 3 rows 2 selected columns: ab ac 2 3 3 4 4 5 select ab,ac from tTableGram_tmp.tab where ab NOT BETWEEN 2 AND 4 has been executed select result of 7 rows 2 selected columns: ab ac 0 1 1 2 5 6 6 7 7 8 8 9 9 10 select ab,ac from tTableGram_tmp.tab where ab BETWEEN 2 AND 4 limit -1 has been executed select result of 2 rows 2 selected columns: ab ac 2 3 3 4 select ab,ac from tTableGram_tmp.tab where ab BETWEEN 2 AND 4 offset -1 has been executed select result of 1 rows 2 selected columns: ab ac 4 5 select distinct max(ab,3) from tTableGram_tmp.tab limit 4 has been executed select result of 4 rows 1 selected columns: Col_1 3 4 5 6 select ab,ac from tTableGram_tmp.tab where ab IN [:=2,4=:<6,7<:] has been executed select result of 7 rows 2 selected columns: ab ac 0 1 1 2 2 3 4 5 5 6 8 9 9 10 select ab,ac from tTableGram_tmp.tab where ab IN [2,(3)] has been executed select result of 2 rows 2 selected columns: ab ac 2 3 3 4 select ab,ac from tTableGram_tmp.tab where ab NOT IN [2,(3)] has been executed select result of 8 rows 2 selected columns: ab ac 0 1 1 2 4 5 5 6 6 7 7 8 8 9 9 10 select ab,ac from tTableGram_tmp.tab where ab IN [select from tTableGram_tmp.tab where ab>4 giving [ac=:=ac+0.5]] has been executed select result of 4 rows 2 selected columns: ab ac 6 7 7 8 8 9 9 10 select ab from tTableGram_tmp.tab where ab IN [select from tTableGram_tmp.tab where ab>7 giving [ab-1=:=ab]] has been executed select result of 3 rows 1 selected columns: ab 7 8 9 select ab from tTableGram_tmp.tab where ab IN [select from tTableGram_tmp.tab where ab>7 giving [ab-1=:7 giving [ab-1<:=ab]] has been executed select result of 2 rows 1 selected columns: ab 8 9 select ab from tTableGram_tmp.tab where ab IN [select from tTableGram_tmp.tab where ab>7 giving [ab-1<: 0 && arr1<200) has been executed select result of 5 rows 2 selected columns: ab ac 4 5 5 6 6 7 7 8 8 9 select ab,ac from tTableGram_tmp.tab where count(shape(arr1))==3 && count(ab)==1 && ndim(ac)==0 && isdefined(arr2) has been executed select result of 10 rows 2 selected columns: ab ac 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 select ab,ac from tTableGram_tmp.tab where ab in ab has been executed select result of 10 rows 2 selected columns: ab ac 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 select ab,ac from tTableGram_tmp.tab where any(arr1 in ab) has been executed select result of 1 rows 2 selected columns: ab ac 0 1 select ab,ac from tTableGram_tmp.tab where (ab=ab)=True has been executed select result of 10 rows 2 selected columns: ab ac 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 select ab,ac from tTableGram_tmp.tab where (ab=ab)=false has been executed select result of 0 rows 2 selected columns: ab ac select ab,ac from tTableGram_tmp.tab where rownumber()==rowid()+1 has been executed select result of 10 rows 2 selected columns: ab ac 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 select ab,ac from [select from tTableGram_tmp.tab where ab > 4] where ab < 6 has been executed select result of 1 rows 2 selected columns: ab ac 5 6 select ab,ac from [select from tTableGram_tmp.tab where ab > 4] TEMPTAB, tTableGram_tmp.tab where any([ab,ac] in [select ac from TEMPTAB]) has been executed select result of 5 rows 2 selected columns: ab ac 5 6 6 7 7 8 8 9 9 10 select ab,ac from tTableGram_tmp.tab where ac in [select from tTableGram_tmp.tab where ac in 4:6:2 giving [rowid()]] has been executed select result of 2 rows 2 selected columns: ab ac 2 3 4 5 select ab from tTableGram_tmp.tab where min(maxs(arr1,[1+int(arr1[1,1,1]%2),3])) == 19 has been executed select result of 1 rows 1 selected columns: ab 0 select ab from tTableGram_tmp.tab where min(1+maxs(arr1-1,1,3)) == 19 has been executed select result of 1 rows 1 selected columns: ab 0 select ab from tTableGram_tmp.tab where sum(fractiles(arr1,0.5,[2:3])) == 21+shape(arr1)[1]*count(arr1) has been executed select result of 1 rows 1 selected columns: ab 1 select ab from tTableGram_tmp.tab where sum(ntrues(arr1%5==0,[1])) < 5 has been executed select result of 2 rows 1 selected columns: ab 4 9 select ab from tTableGram_tmp.tab where all(anys(fmod(sums(arr1,1),5)==0,[2:4])) has been executed select result of 4 rows 1 selected columns: ab 0 3 5 8 select ab from $1;tTableGram_tmp.tab has been executed select result of 10 rows 1 selected columns: ab 0 1 2 3 4 5 6 7 8 9 select ab from $1::subtab;tTableGram_tmp.tab Caught an exception: Error in TaQL command: 'select ab from $1::subtab' Error in select expression: subtab is an unknown keyword/subtable in $1::subtab select ab from tTableGram_tmp.tab where [ab,ab] incone [2rad,2rad,1rad] has been executed select result of 4 rows 1 selected columns: ab 1 2 8 9 select ab from tTableGram_tmp.tab where anycone([ab,ab],[2rad,2rad],1rad) has been executed select result of 4 rows 1 selected columns: ab 1 2 8 9 select ab from tTableGram_tmp.tab where cones([ab,ab],[4rad,4rad,1rad]) has been executed select result of 2 rows 1 selected columns: ab 4 5 select ab from tTableGram_tmp.tab where any(cones([ab,ab],array([2rad,2rad,4rad,4rad],[2,2]),1rad)) has been executed select result of 6 rows 1 selected columns: ab 1 2 4 5 8 9 select ab from tTableGram_tmp.tab where [ab,ab] incone [2rad,2rad,1rad,4rad,4rad,1rad] has been executed select result of 6 rows 1 selected columns: ab 1 2 4 5 8 9 calc from tTableGram_tmp.tab calc findcone([ab,ab],array([2rad,2rad,4rad,4rad],[2,2]),[1rad,2rad]) has been executed [2, 1, 1, 2, 3, 3, 2, 2, 1, 1] calc from tTableGram_tmp.tab calc findcone([ab,ab],[select from tTableGram_tmp.tab giving [ab,ab]],[1rad,2rad]) has been executed [1, 2, 2, 1, 2, 2, 1, 1, 2, 1] Test transpose and axes orderings ... using style glish calc all(shape([[[1],[2]],[[3],[4]],[[5],[6]]]) = [1,2,3]) has been executed [1] using style python calc all(shape([[[1],[2]],[[3],[4]],[[5],[6]]]) = [3,2,1]) has been executed [1] using style glish calc all(shape(transpose([[[1],[2]],[[3],[4]],[[5],[6]]],[2])) = [2,1,3]) has been executed [1] using style python calc all(shape(transpose([[[1],[2]],[[3],[4]],[[5],[6]]],[1])) = [2,3,1]) has been executed [1] using style glish calc all(shape(transpose([[[1],[2]],[[3],[4]],[[5],[6]]],[2,3,1])) = [2,3,1]) has been executed [1] using style python calc all(shape(transpose([[[1],[2]],[[3],[4]],[[5],[6]]],[1,2,0])) = [2,1,3]) has been executed [1] testing some erroneous commands ... select abcd from tTableGram_tmp.tab Caught an exception: Error in TaQL command: 'select abcd from tTableGram_tmp.tab' Error in select expression: abcd is an unknown column (or keyword) in table tTableGram_tmp.tab select t1.ab from tTableGram_tmp.tab t0, [select from t0 limit 5] t1 Caught an exception: Error in TaQL command: 'select t1.ab from tTableGram_tmp.tab t0, [select from t0 limit 5] t1' Error in select expression: Nr of rows of tables used in select expressions must be equal to first table select from tTableGram_tmp.tab t0, [select from tTableGram_tmp.tab limit 5] t1 where t0.ab = t1.ab Caught an exception: Error in TaQL command: 'select from tTableGram_tmp.tab t0, [select from tTableGram_tmp.tab limit 5] t1 where t0.ab = t1.ab' Error in select expression: Nr of rows (5) in table column t1.ab differs from column t0.ab (10) testing calc ... calc mean([1:6][[1:6] > 3][[1:6]>1]) has been executed [1] calc 1 km m cm has been executed Unit: cm [100000] calc sum([select from tTableGram_tmp.tab giving [ab+1]]) has been executed [55] calc sum([select from tTableGram_tmp.tab giving [ab,ac,ab:ac]]) has been executed [200] calc from $1 calc sum([select ab from $1]);tTableGram_tmp.tab has been executed [45] calc from tTableGram_tmp.tab calc ab has been executed [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] calc from tTableGram_tmp.tab calc arr1[2,1,1] has been executed Index: [1, 0, 0] Ndim=4 Axis Lengths: [1, 1, 1, 10] [0, 0, 0, 0][1] [0, 0, 0, 1][25] [0, 0, 0, 2][49] [0, 0, 0, 3][73] [0, 0, 0, 4][97] [0, 0, 0, 5][121] [0, 0, 0, 6][145] [0, 0, 0, 7][169] [0, 0, 0, 8][193] [0, 0, 0, 9][217] calc from tTableGram_tmp.tab calc arr1[1+ab%2,1,1] has been executed [0, 25, 48, 73, 96, 121, 144, 169, 192, 217] calc from $1 calc ab+1;tTableGram_tmp.tab has been executed [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] calc from tTableGram_tmp.tab calc ab&0x000b has been executed [0, 1, 2, 3, 0, 1, 2, 3, 8, 9] calc from tTableGram_tmp.tab calc (ab|2)&~0x0001 has been executed [2, 2, 2, 2, 6, 6, 6, 6, 10, 10] testing count ... count ab from tTableGram_tmp.tab has been executed count result of 10 rows 2 selected columns: ab _COUNT_ 0 1 1 1 2 1 3 1 4 1 5 1 6 1 7 1 8 1 9 1 count af from tTableGram_tmp.tab where ab<6 has been executed count result of 6 rows 2 selected columns: af _COUNT_ V0 1 V1 1 V2 1 V3 1 V4 1 V5 1 count ab,af from tTableGram_tmp.tab where ac<4 has been executed count result of 3 rows 3 selected columns: ab af _COUNT_ 0 V0 1 1 V1 1 2 V2 1 count ab*2 as ab1,ac from [count ab,ac from tTableGram_tmp.tab] where _COUNT_<2 && ab<6 has been executed count result of 6 rows 3 selected columns: ab1 ac _COUNT_ 0 1 1 2 2 1 4 3 1 6 4 1 8 5 1 10 6 1 testing update ... update tTableGram_tmp.tab set ab=sum(arr1)+ac*2, arr1=arr1+2 where ac>3 has been executed update result of 7 rows 2 selected columns: ab arr1 2012 shape=[2, 3, 4] 2590 shape=[2, 3, 4] 3168 shape=[2, 3, 4] 3746 shape=[2, 3, 4] 4324 shape=[2, 3, 4] 4902 shape=[2, 3, 4] 5480 shape=[2, 3, 4] select ab from tTableGram_tmp.tab has been executed select result of 10 rows 1 selected columns: ab 0 1 2 2012 2590 3168 3746 4324 4902 5480 update tTableGram_tmp.tab set ab=sum(arr1)+ac*2, arr1=arr1+2 from tTableGram_tmp.tabc where ac>3 orderby ac limit 5 has been executed update result of 5 rows 2 selected columns: ab arr1 2060 shape=[2, 3, 4] 2638 shape=[2, 3, 4] 3216 shape=[2, 3, 4] 3794 shape=[2, 3, 4] 4372 shape=[2, 3, 4] update tTableGram_tmp.tab set arr1=2, ab=sum(arr1) limit 1 offset 3 has been executed update result of 1 rows 2 selected columns: arr1 ab shape=[2, 3, 4] 48 update tTableGram_tmp.tab set arr1[1,1,1]=3, arr1[2,2,2]=arr1[1,1,1], ab=sum(arr1) limit 1 offset 3 has been executed update result of 1 rows 3 selected columns: arr1 arr1 ab shape=[2, 3, 4] shape=[2, 3, 4] 50 update tTableGram_tmp.tab set arr1[1,,]=4, ab=sum(arr1) limit 1 offset 3 has been executed update result of 1 rows 2 selected columns: arr1 ab shape=[2, 3, 4] 73 testing insert/delete ... delete from tTableGram_tmp.tab limit 3 offset 2 has been executed delete result of 3 rows 0 selected columns: delete from tTableGram_tmp.tab orderby desc ab limit 1 offset 2 has been executed delete result of 1 rows 0 selected columns: select ab from tTableGram_tmp.tab has been executed select result of 6 rows 1 selected columns: ab 0 1 3216 3794 4902 5480 delete from tTableGram_tmp.tab has been executed delete result of 6 rows 0 selected columns: select ab from tTableGram_tmp.tab has been executed select result of 0 rows 1 selected columns: ab insert into tTableGram_tmp.tab select from tTableGram_tmp.tabc has been executed insert result of 10 rows 9 selected columns: ab ad ag arr1 arr2 arr3 ac ae af 0 2 (2,0) shape=[2, 3, 4] shape=[2, 3, 4] shape=[2, 3, 4] 1 3 V0 1 3 (3,0) shape=[2, 3, 4] shape=[2, 3, 4] shape=[2, 3, 4] 2 4 V1 2 4 (4,0) shape=[2, 3, 4] shape=[2, 3, 4] shape=[2, 3, 4] 3 5 V2 3 5 (5,0) shape=[2, 3, 4] shape=[2, 3, 4] shape=[2, 3, 4] 4 6 V3 4 6 (6,0) shape=[2, 3, 4] shape=[2, 3, 4] shape=[2, 3, 4] 5 7 V4 5 7 (7,0) shape=[2, 3, 4] shape=[2, 3, 4] shape=[2, 3, 4] 6 8 V5 6 8 (8,0) shape=[2, 3, 4] shape=[2, 3, 4] shape=[2, 3, 4] 7 9 V6 7 9 (9,0) shape=[2, 3, 4] shape=[2, 3, 4] shape=[2, 3, 4] 8 10 V7 8 10 (10,0) shape=[2, 3, 4] shape=[2, 3, 4] shape=[2, 3, 4] 9 11 V8 9 11 (11,0) shape=[2, 3, 4] shape=[2, 3, 4] shape=[2, 3, 4] 10 12 V9 select ab from tTableGram_tmp.tab has been executed select result of 10 rows 1 selected columns: ab 0 1 2 3 4 5 6 7 8 9 insert into tTableGram_tmp.tab (ab) select ab*2 as col1 i4 from tTableGram_tmp.tabc has been executed insert result of 10 rows 1 selected columns: ab 0 2 4 6 8 10 12 14 16 18 select ab from tTableGram_tmp.tab has been executed select result of 20 rows 1 selected columns: ab 0 1 2 3 4 5 6 7 8 9 0 2 4 6 8 10 12 14 16 18 delete from tTableGram_tmp.tab where ab%2==0 has been executed delete result of 15 rows 0 selected columns: select ab from tTableGram_tmp.tab has been executed select result of 5 rows 1 selected columns: ab 1 3 5 7 9 insert into tTableGram_tmp.tab set ab=1+2, ac=3*ab + sum([select ab from tTableGram_tmp.tab]) has been executed insert result of 1 rows 2 selected columns: ab ac 3 34 select ab,ac from tTableGram_tmp.tab has been executed select result of 6 rows 2 selected columns: ab ac 1 2 3 4 5 6 7 8 9 10 3 34 insert into [createtable tTableGram_tmp.tab2 (ab I4, ac U2, ad I4)] values (10,11,1),(12,13,2),(14,15,4) has been executed insert result of 3 rows 3 selected columns: ab ac ad 10 11 1 12 13 2 14 15 4 insert top 4 into tTableGram_tmp.tab2 values (rowid(), ab+10, rownumber()) has been executed insert result of 4 rows 3 selected columns: ab ac ad 3 13 4 4 14 5 5 15 6 6 16 7 select ab,ac,ad from tTableGram_tmp.tab2 has been executed select result of 7 rows 3 selected columns: ab ac ad 10 11 1 12 13 2 14 15 4 3 13 4 4 14 5 5 15 6 6 16 7 select [select ab from ::][rownr()] from tTableGram_tmp.tab limit 1 has been executed select result of 1 rows 1 selected columns: Col_1 1 testing create ... create table tTableGram_tmp.tab2 (col1 i4 [shape=[2,3], unit="m", dmtype="IncrementalStMan"], col2 B) dminfo [TYPE="IncrementalStMan",NAME="ISM1",SPEC=[BUCKETSIZE=16384],COLUMNS=["col1"]] has been executed cretab result of 0 rows 2 selected columns: col1 col2 select col1,col2 from tTableGram_tmp.tab2 has been executed select result of 0 rows 2 selected columns: col1 col2 insert into tTableGram_tmp.tab2 (col1,col2) VALUES (array(1,[2,3])dam, F) has been executed insert result of 1 rows 2 selected columns: col1 col2 shape=[2, 3] 0 insert into tTableGram_tmp.tab2 (col1,col2) VALUES (array(5,[2,3]), T) has been executed insert result of 1 rows 2 selected columns: col1 col2 shape=[2, 3] 1 calc sum([select sum(col1) from tTableGram_tmp.tab2]) has been executed Unit: m [90] testing styles ... using style python update tTableGram_tmp.tab set arr1 = array(1,4,3,2) where rownumber()==5 has been executed update result of 1 rows 1 selected columns: arr1 shape=[2, 3, 4] using style python calc [3:6][1] has been executed Index: [1] [4] using style glish calc [3:6][3] has been executed Index: [2] [5] using style base0 calc [3:6][3] has been executed Index: [3] [6] using style base1 calc [3:6][3] has been executed Index: [2] [5] using style python calc 6 in [3:6] has been executed [0] using style glish calc 6 in [3:6] has been executed [1] using style python calc array([3:7],3,4)[0,3] has been executed Index: [3, 0] [6] using style glish calc array([3:7],3,4)[3,1] has been executed Index: [2, 0] [5] using style python select ab,ac from tTableGram_tmp.tab where all(shape(arr1) == [4,3,2]) has been executed select result of 6 rows 2 selected columns: ab ac 1 2 3 4 5 6 7 8 9 10 3 34 using style glish select ab,ac from tTableGram_tmp.tab where all(shape(arr1) == [2,3,4]) has been executed select result of 6 rows 2 selected columns: ab ac 1 2 3 4 5 6 7 8 9 10 3 34 using style python select ab from tTableGram_tmp.tab where all(anys(fmod(sums(arr1,2),5)==0,[0])) has been executed select result of 2 rows 1 selected columns: ab 3 5 using style glish select ab from tTableGram_tmp.tab where all(anys(fmod(sums(arr1,1),5)==0,[2:4])) has been executed select result of 2 rows 1 selected columns: ab 3 5 using style python select ab from tTableGram_tmp.tab where rownumber() < 2 has been executed select result of 2 rows 1 selected columns: ab 1 3 using style glish select ab from tTableGram_tmp.tab where rownumber() < 2 has been executed select result of 1 rows 1 selected columns: ab 1 calc runningMedian(array([0:24],5,5),1,1) has been executed row 0: Axis Lengths: [5, 5] (NB: Matrix in Row/Column order) [0, 0, 0, 0, 0 0, 6, 11, 16, 0 0, 7, 12, 17, 0 0, 8, 13, 18, 0 0, 0, 0, 0, 0] calc boxedMedian(array([0:24],5,5),1,1) has been executed row 0: Axis Lengths: [5, 5] (NB: Matrix in Row/Column order) [0, 5, 10, 15, 20 1, 6, 11, 16, 21 2, 7, 12, 17, 22 3, 8, 13, 18, 23 4, 9, 14, 19, 24] calc boxedMax(array([0:24],5,5),[2,2]) has been executed row 0: Axis Lengths: [3, 3] (NB: Matrix in Row/Column order) [6, 16, 21 8, 18, 23 9, 19, 24] calc 5.1//2 has been executed [2] calc -4.1//2 has been executed [-3] calc -4**2 has been executed [-16] calc angdist([10,12],[10,12,13,14]) has been executed Unit: rad row 0: Axis Lengths: [1, 2] (NB: Matrix in Row/Column order) [0, 2.27282] Testing indexing and nested arrays ... calc [1:10][3] == 3 has been executed [1] calc [1:10][-3] == 8 has been executed [1] calc all([1:10][:5] == [1:5]) has been executed [1] calc all([1:10][::3] == [1,4,7,10]) has been executed [1] calc all([1:10][-7:-3:2] == [4,6,8]) has been executed [1] calc all([[[1,2,3],[4,5,6]]] = array([1:6],3,2,1)) has been executed [1] calc all([[[1,2,3],[4,5,6]],array([7:12],3,2)] = array([1:12],3,2,2)) has been executed [1] testing units ... calc sum([select ab d as ABDAY from tTableGram_tmp.tab]) has been executed Unit: d [28] calc sum([select from tTableGram_tmp.tab giving [ab \in]]) has been executed Unit: in [28] select ab s AS ab1, ac mm AS ac1 INTO AS MEMORY from tTableGram_tmp.tab where rownumber() < 4 has been executed select result of 3 rows 2 selected columns: ab1 ac1 1 2 3 4 5 6 select ab s AS ab1, ac mm AS ac1 INTO tTableGram_tmp.tab_abac AS PLAIN from tTableGram_tmp.tab where rownumber() < 4 has been executed select result of 3 rows 2 selected columns: ab1 ac1 1 2 3 4 5 6 update tTableGram_tmp.tab_abac set ab1=1min+ab1, ac1=1.5cm+ac1 has been executed update result of 3 rows 2 selected columns: ab1 ac1 61 17 63 19 65 21 select ab1,ac1 from tTableGram_tmp.tab_abac has been executed select result of 3 rows 2 selected columns: ab1 ac1 61 17 63 19 65 21 Testing multiple tables ... select from tTableGram_tmp.tab orderby ab desc giving tTableGram_tmp.rev as plain has been executed select result of 10 rows 0 selected columns: select t1.ab as ab1, t2.ab as ab2 from tTableGram_tmp.tab t1, tTableGram_tmp.rev t2 has been executed select result of 10 rows 2 selected columns: ab1 ab2 0 9 1 8 2 7 3 6 4 5 5 4 6 3 7 2 8 1 9 0 select t1.ab as ab1, t2.ab as ab2 from tTableGram_tmp.tab t1, tTableGram_tmp.rev t2 where abs(t1.ab - t2.ab) < 4 has been executed select result of 4 rows 2 selected columns: ab1 ab2 3 6 4 5 5 4 6 3 select t1.ab as ab1, t1.ac, t2.ab as ab2 from tTableGram_tmp.tab t1, tTableGram_tmp.rev t2 where t1.ab > t2.ab has been executed select result of 5 rows 3 selected columns: ab1 ac ab2 5 6 4 6 7 3 7 8 2 8 9 1 9 10 0 select ab1,ab2 from [select t1.ab as ab1, t2.ab as ab2 from tTableGram_tmp.tab t1, tTableGram_tmp.rev t2 where abs(t1.ab - t2.ab) < 4] has been executed select result of 4 rows 2 selected columns: ab1 ab2 3 6 4 5 5 4 6 3 select ab from tTableGram_tmp.tab where ab in [select t2.ab from tTableGram_tmp.tab t1, tTableGram_tmp.rev t2 where t1.ab > t2.ab] has been executed select result of 5 rows 1 selected columns: ab 0 1 2 3 4 select ab from tTableGram_tmp.tab where ac in [select ab from tTableGram_tmp.tab t1, tTableGram_tmp.rev t2 where t1.ab > t2.ab] has been executed select result of 5 rows 1 selected columns: ab 4 5 6 7 8 update tTableGram_tmp.tab t1, tTableGram_tmp.rev t2 set ab=ab+1, ac=t2.ac has been executed update result of 10 rows 2 selected columns: ab ac 1 10 2 9 3 8 4 7 5 6 6 5 7 4 8 3 9 2 10 1 select ab,ac from tTableGram_tmp.tab has been executed select result of 10 rows 2 selected columns: ab ac 1 10 2 9 3 8 4 7 5 6 6 5 7 4 8 3 9 2 10 1 update tTableGram_tmp.tab t1, tTableGram_tmp.rev t2 set ac=t2.ac+10 where ab=t2.ab has been executed update result of 1 rows 1 selected columns: ac 16 select ab,ac from tTableGram_tmp.tab has been executed select result of 10 rows 2 selected columns: ab ac 1 10 2 9 3 8 4 7 5 16 6 5 7 4 8 3 9 2 10 1 delete from tTableGram_tmp.tab t1, tTableGram_tmp.rev t2 where ab=t2.ab has been executed delete result of 1 rows 0 selected columns: select ab,ac from tTableGram_tmp.tab has been executed select result of 9 rows 2 selected columns: ab ac 1 10 2 9 3 8 4 7 6 5 7 4 8 3 9 2 10 1 update [create table tTableGram_tmp.tst as plain_big col1 int limit 3 ] set col1=rowid() has been executed update result of 3 rows 1 selected columns: col1 0 1 2 select * from tTableGram_tmp.tst has been executed select result of 3 rows 1 selected columns: col1 0 1 2 select gcount() from [tTableGram_tmp.tst,"tTableGram_*.tst" subtables a,b] Caught an exception: Error in TaQL command: 'select gcount() from [tTableGram_tmp.tst,"tTableGram_*.tst" subtables a,b]' RecordInterface: field a is unknown select gcount() from [tTableGram_tmp.tst,tTableGram_tmp.tst giving tTableGram_tmp.tst2] has been executed select result of 1 rows 1 selected columns: Col_1 6 select col1 from tTableGram_tmp.tst2 has been executed select result of 6 rows 1 selected columns: col1 0 1 2 0 1 2 select gcount() from [tTableGram_tmp.tst,tTableGram_tmp.tst],[tTableGram_tmp.tst,tTableGram_tmp.tst,tTableGram_tmp.tst] has been executed select result of 1 rows 1 selected columns: Col_1 6 select gcount() from [tTableGram_tmp.tst,tTableGram_tmp.tst] has been executed select result of 1 rows 1 selected columns: Col_1 6 select gcount() from [tTableGram_tmp.tst as t1,tTableGram_tmp.tst as t2,[[tTableGram_tmp.tst,tTableGram_tmp.tst] as t3] as t4] as t5 has been executed select result of 1 rows 1 selected columns: Col_1 12 with [tTableGram_tmp.tst,tTableGram_tmp.tst] t1 calc ([select gcount() from t1] + [select gcount() from tTableGram_tmp.tst] + [with [t1,t1] as t2 select gcount() from t2])[0] has been executed Index: [-1] [21] casacore-3.7.1/tables/TaQL/test/tTableGram.run000066400000000000000000000502401476623553700211270ustar00rootroot00000000000000#!/bin/sh #----------------------------------------------------------------------------- # Script to test the TableGram and TableParse class. All files generated will # be deleted on exit. #============================================================================= # If the first argument given to the script is 1, it will only create the # tTableGram_tmp.tab tables and not execute the tTableGram commands. # Set default testsrcdir if undefined. if test "$testsrcdir" = ""; then testsrcdir=../../../../../casacore/tables/TaQL/test fi # Use table tTable_2.data_v0 as the input by copying it (twice). rm -rf tTableGram_tmp.tab* mkdir tTableGram_tmp.tab cp -r $testsrcdir/../../Tables/test/tTable_2.data_v0/table.* tTableGram_tmp.tab chmod 644 tTableGram_tmp.tab/* cp -r tTableGram_tmp.tab tTableGram_tmp.tabc if test "$1" = 1; then exit 0; fi # Whitespace around * cannot be used to avoid file name expansion by shell. # First do the tests of unit handling. $casa_checktool ./tTableGram 0 # Now execute all kind of commands. echo "testing select ..." $casa_checktool ./tTableGram 'select 3+4' $casa_checktool ./tTableGram 'select 3+4 where 1>2' $casa_checktool ./tTableGram 'select gsum(3+4) as C having C > 12' $casa_checktool ./tTableGram 'select gsum(3+4) as C where 1<2 having C<12 orderby C giving as memory' $casa_checktool ./tTableGram 'select ab,ac,ad,ae,af,ag into tTableGram_tmp.data2 from tTableGram_tmp.tab sh where all(ab>2) && (ae<10 || ae>11.0) && ag!= 10 + 1i orderby ac desc,ab' $casa_checktool ./tTableGram 'select distinct ab+1,ac as ac2 from tTableGram_tmp.tab' $casa_checktool ./tTableGram 'select distinct ab,ac,ad,ae,af,ag from tTableGram_tmp.data2' $casa_checktool ./tTableGram 'select all ab,ac,ad,ae,af,ag from tTableGram_tmp.data2 orderby af' $casa_checktool ./tTableGram 'select ab from tTableGram_tmp.tab where ab==2**1**2 || ab==-2**-1*8/-2*3' $casa_checktool ./tTableGram 'select ab,ac,af from ../../TaQL/./test/tTableGram_tmp.tab where lower(af) == regex("v[01279]")' $casa_checktool ./tTableGram 'select ab,ac,af from tTableGram_tmp.tab where af!~m/V[01279]/i' $casa_checktool ./tTableGram 'select ab,ac,af from tTableGram_tmp.tab where af ~ p/?{3,5,8}/' $casa_checktool ./tTableGram 'select ab,ac,af from tTableGram_tmp.tab where af != pattern("?{3,5,8}")' $casa_checktool ./tTableGram 'select ab,ac,af from tTableGram_tmp.tab where af == sqlpattern("_3%")' $casa_checktool ./tTableGram 'select ab,ac,af from tTableGram_tmp.tab where af like "_3%"' $casa_checktool ./tTableGram 'select ab,ac,af from tTableGram_tmp.tab where af not like "_3%"' $casa_checktool ./tTableGram 'select ab,ac from tTableGram_tmp.tab where ab%1.5==0' $casa_checktool ./tTableGram 'select ab,ac from tTableGram_tmp.tab where arr1[1,1,1]>=10 && arr2[1,1,1]<120' $casa_checktool ./tTableGram 'select *, !~ p/ar*/ from tTableGram_tmp.tab where arr1[1,1,1]>=10 && arr2[1,1,1]<120' $casa_checktool ./tTableGram 'select arr1,~ f/t1\..*/,!~f/t1\.ar.*/ from tTableGram_tmp.tab t1 where arr1[1,1,1]>=10 && arr2[1,1,1]<120' $casa_checktool ./tTableGram 'select ab,ac from tTableGram_tmp.tab where arr1[1,1,1+ab%1]>=192 orderby ad desc' $casa_checktool ./tTableGram 'select ab,ac from tTableGram_tmp.tab where cos(2d0m) <= sin(-2d0m)*sin(-ab/180*pi()) + cos(-2deg)*cos(-ab/180*pi())*cos(3d0m - ac/180*pi())' $casa_checktool ./tTableGram 'select ab,ac,ad,ae,af,ag from tTableGram_tmp.tab where ab+ac+ad+ae+real(ag) >= year(31-12-1960) + year("31Dec60") + month(1990/05/12) + day(date(1990/01/30/12h14m33.3)) - 3910' $casa_checktool ./tTableGram 'select ab,ac,af from tTableGram_tmp.tab where ab>5 orderby af desc, ac' $casa_checktool ./tTableGram 'select ab,ac,af from tTableGram_tmp.tab orderby arr1[1,1,1]' $casa_checktool ./tTableGram 'select ab,ac from tTableGram_tmp.tab orderby round(2*sin(ab)),ac desc' $casa_checktool ./tTableGram 'select ab,ac from tTableGram_tmp.tab where ab < mean([3:6,ab])' $casa_checktool ./tTableGram 'select ab from tTableGram_tmp.tab where ab in [select ab+5 from tTableGram_tmp.tab]' $casa_checktool ./tTableGram 'select rowid() from tTableGram_tmp.tab where ab+235 in [select arr1 from tTableGram_tmp.tab]' $casa_checktool ./tTableGram 'select ab,ac from tTableGram_tmp.tab where ab < 4 && EXISTS (select from tTableGram_tmp.tab)' $casa_checktool ./tTableGram 'select ab,ac from tTableGram_tmp.tab where ab < 4 && EXISTS (select from tTableGram_tmp.tab LIMIT 11)' $casa_checktool ./tTableGram 'select ab,ac from tTableGram_tmp.tab where ab IN (select ac from tTableGram_tmp.tab where ab>4)' $casa_checktool ./tTableGram 'select ab,ac from tTableGram_tmp.tab where ab AROUND 3 IN 2' $casa_checktool ./tTableGram 'select ab,ac from tTableGram_tmp.tab where ab NOT AROUND 3 IN 2' $casa_checktool ./tTableGram 'select ab,ac from tTableGram_tmp.tab where ab BETWEEN 2 AND 4' $casa_checktool ./tTableGram 'select ab,ac from tTableGram_tmp.tab where ab NOT BETWEEN 2 AND 4' $casa_checktool ./tTableGram 'select ab,ac from tTableGram_tmp.tab where ab BETWEEN 2 AND 4 limit -1' $casa_checktool ./tTableGram 'select ab,ac from tTableGram_tmp.tab where ab BETWEEN 2 AND 4 offset -1' # Check that distinct is done before limit. $casa_checktool ./tTableGram 'select distinct max(ab,3) from tTableGram_tmp.tab limit 4' $casa_checktool ./tTableGram 'select ab,ac from tTableGram_tmp.tab where ab IN [:=2,4=:<6,7<:]' $casa_checktool ./tTableGram 'select ab,ac from tTableGram_tmp.tab where ab IN [2,(3)]' $casa_checktool ./tTableGram 'select ab,ac from tTableGram_tmp.tab where ab NOT IN [2,(3)]' $casa_checktool ./tTableGram 'select ab,ac from tTableGram_tmp.tab where ab IN [select from tTableGram_tmp.tab where ab>4 giving [ac=:=ac+0.5]]' $casa_checktool ./tTableGram 'select ab from tTableGram_tmp.tab where ab IN [select from tTableGram_tmp.tab where ab>7 giving [ab-1=:=ab]]' $casa_checktool ./tTableGram 'select ab from tTableGram_tmp.tab where ab IN [select from tTableGram_tmp.tab where ab>7 giving [ab-1=:7 giving [ab-1<:=ab]]' $casa_checktool ./tTableGram 'select ab from tTableGram_tmp.tab where ab IN [select from tTableGram_tmp.tab where ab>7 giving [ab-1<: 0 && arr1<200)' $casa_checktool ./tTableGram 'select ab,ac from tTableGram_tmp.tab where count(shape(arr1))==3 && count(ab)==1 && ndim(ac)==0 && isdefined(arr2)' $casa_checktool ./tTableGram 'select ab,ac from tTableGram_tmp.tab where ab in ab' $casa_checktool ./tTableGram 'select ab,ac from tTableGram_tmp.tab where any(arr1 in ab)' $casa_checktool ./tTableGram 'select ab,ac from tTableGram_tmp.tab where (ab=ab)=True' $casa_checktool ./tTableGram 'select ab,ac from tTableGram_tmp.tab where (ab=ab)=false' $casa_checktool ./tTableGram 'select ab,ac from tTableGram_tmp.tab where rownumber()==rowid()+1' $casa_checktool ./tTableGram 'select ab,ac from [select from tTableGram_tmp.tab where ab > 4] where ab < 6' $casa_checktool ./tTableGram 'select ab,ac from [select from tTableGram_tmp.tab where ab > 4] TEMPTAB, tTableGram_tmp.tab where any([ab,ac] in [select ac from TEMPTAB])' $casa_checktool ./tTableGram 'select ab,ac from tTableGram_tmp.tab where ac in [select from tTableGram_tmp.tab where ac in 4:6:2 giving [rowid()]]' $casa_checktool ./tTableGram 'select ab from tTableGram_tmp.tab where min(maxs(arr1,[1+int(arr1[1,1,1]%2),3])) == 19' $casa_checktool ./tTableGram 'select ab from tTableGram_tmp.tab where min(1+maxs(arr1-1,1,3)) == 19' $casa_checktool ./tTableGram 'select ab from tTableGram_tmp.tab where sum(fractiles(arr1,0.5,[2:3])) == 21+shape(arr1)[1]*count(arr1)' $casa_checktool ./tTableGram 'select ab from tTableGram_tmp.tab where sum(ntrues(arr1%5==0,[1])) < 5' $casa_checktool ./tTableGram 'select ab from tTableGram_tmp.tab where all(anys(fmod(sums(arr1,1),5)==0,[2:4]))' # Note that the table after ; serves as temptable for $1. $casa_checktool ./tTableGram 'select ab from $1;tTableGram_tmp.tab' $casa_checktool ./tTableGram 'select ab from $1::subtab;tTableGram_tmp.tab' $casa_checktool ./tTableGram 'select ab from tTableGram_tmp.tab where [ab,ab] incone [2rad,2rad,1rad]' $casa_checktool ./tTableGram 'select ab from tTableGram_tmp.tab where anycone([ab,ab],[2rad,2rad],1rad)' $casa_checktool ./tTableGram 'select ab from tTableGram_tmp.tab where cones([ab,ab],[4rad,4rad,1rad])' $casa_checktool ./tTableGram 'select ab from tTableGram_tmp.tab where any(cones([ab,ab],array([2rad,2rad,4rad,4rad],[2,2]),1rad))' $casa_checktool ./tTableGram 'select ab from tTableGram_tmp.tab where [ab,ab] incone [2rad,2rad,1rad,4rad,4rad,1rad]' $casa_checktool ./tTableGram 'calc from tTableGram_tmp.tab calc findcone([ab,ab],array([2rad,2rad,4rad,4rad],[2,2]),[1rad,2rad])' $casa_checktool ./tTableGram 'calc from tTableGram_tmp.tab calc findcone([ab,ab],[select from tTableGram_tmp.tab giving [ab,ab]],[1rad,2rad])' echo "" echo "Test transpose and axes orderings ..." ./tTableGram 'using style glish calc all(shape([[[1],[2]],[[3],[4]],[[5],[6]]]) = [1,2,3])' ./tTableGram 'using style python calc all(shape([[[1],[2]],[[3],[4]],[[5],[6]]]) = [3,2,1])' ./tTableGram 'using style glish calc all(shape(transpose([[[1],[2]],[[3],[4]],[[5],[6]]],[2])) = [2,1,3])' ./tTableGram 'using style python calc all(shape(transpose([[[1],[2]],[[3],[4]],[[5],[6]]],[1])) = [2,3,1])' ./tTableGram 'using style glish calc all(shape(transpose([[[1],[2]],[[3],[4]],[[5],[6]]],[2,3,1])) = [2,3,1])' ./tTableGram 'using style python calc all(shape(transpose([[[1],[2]],[[3],[4]],[[5],[6]]],[1,2,0])) = [2,1,3])' echo "" echo "testing some erroneous commands ..." # Unknown column ./tTableGram 'select abcd from tTableGram_tmp.tab' # Different table sizes ./tTableGram 'select t1.ab from tTableGram_tmp.tab t0, [select from t0 limit 5] t1' ./tTableGram 'select from tTableGram_tmp.tab t0, [select from tTableGram_tmp.tab limit 5] t1 where t0.ab = t1.ab' echo "" echo "testing calc ..." $casa_checktool ./tTableGram 'calc mean([1:6][[1:6] > 3][[1:6]>1])' #chained subscripts $casa_checktool ./tTableGram 'calc 1 km m cm' # chained units $casa_checktool ./tTableGram 'calc sum([select from tTableGram_tmp.tab giving [ab+1]])' $casa_checktool ./tTableGram 'calc sum([select from tTableGram_tmp.tab giving [ab,ac,ab:ac]])' $casa_checktool ./tTableGram 'calc from $1 calc sum([select ab from $1]);tTableGram_tmp.tab' $casa_checktool ./tTableGram 'calc from tTableGram_tmp.tab calc ab' $casa_checktool ./tTableGram 'calc from tTableGram_tmp.tab calc arr1[2,1,1]' $casa_checktool ./tTableGram 'calc from tTableGram_tmp.tab calc arr1[1+ab%2,1,1]' $casa_checktool ./tTableGram 'calc from $1 calc ab+1;tTableGram_tmp.tab' $casa_checktool ./tTableGram 'calc from tTableGram_tmp.tab calc ab&0x000b' $casa_checktool ./tTableGram 'calc from tTableGram_tmp.tab calc (ab|2)&~0x0001' echo "" echo "testing count ..." $casa_checktool ./tTableGram 'count ab from tTableGram_tmp.tab' $casa_checktool ./tTableGram 'count af from tTableGram_tmp.tab where ab<6' $casa_checktool ./tTableGram 'count ab,af from tTableGram_tmp.tab where ac<4' $casa_checktool ./tTableGram 'count ab*2 as ab1,ac from [count ab,ac from tTableGram_tmp.tab] where _COUNT_<2 && ab<6' echo "" echo "testing update ..." $casa_checktool ./tTableGram 'update tTableGram_tmp.tab set ab=sum(arr1)+ac*2, arr1=arr1+2 where ac>3' $casa_checktool ./tTableGram 'select ab from tTableGram_tmp.tab' $casa_checktool ./tTableGram 'update tTableGram_tmp.tab set ab=sum(arr1)+ac*2, arr1=arr1+2 from tTableGram_tmp.tabc where ac>3 orderby ac limit 5' $casa_checktool ./tTableGram 'update tTableGram_tmp.tab set arr1=2, ab=sum(arr1) limit 1 offset 3' $casa_checktool ./tTableGram 'update tTableGram_tmp.tab set arr1[1,1,1]=3, arr1[2,2,2]=arr1[1,1,1], ab=sum(arr1) limit 1 offset 3' $casa_checktool ./tTableGram 'update tTableGram_tmp.tab set arr1[1,,]=4, ab=sum(arr1) limit 1 offset 3' echo "" echo "testing insert/delete ..." $casa_checktool ./tTableGram 'delete from tTableGram_tmp.tab limit 3 offset 2' $casa_checktool ./tTableGram 'delete from tTableGram_tmp.tab orderby desc ab limit 1 offset 2' $casa_checktool ./tTableGram 'select ab from tTableGram_tmp.tab' $casa_checktool ./tTableGram 'delete from tTableGram_tmp.tab' $casa_checktool ./tTableGram 'select ab from tTableGram_tmp.tab' $casa_checktool ./tTableGram 'insert into tTableGram_tmp.tab select from tTableGram_tmp.tabc' $casa_checktool ./tTableGram 'select ab from tTableGram_tmp.tab' $casa_checktool ./tTableGram 'insert into tTableGram_tmp.tab (ab) select ab*2 as col1 i4 from tTableGram_tmp.tabc' $casa_checktool ./tTableGram 'select ab from tTableGram_tmp.tab' $casa_checktool ./tTableGram 'delete from tTableGram_tmp.tab where ab%2==0' $casa_checktool ./tTableGram 'select ab from tTableGram_tmp.tab' $casa_checktool ./tTableGram 'insert into tTableGram_tmp.tab set ab=1+2, ac=3*ab + sum([select ab from tTableGram_tmp.tab])' $casa_checktool ./tTableGram 'select ab,ac from tTableGram_tmp.tab' $casa_checktool ./tTableGram 'insert into [createtable tTableGram_tmp.tab2 (ab I4, ac U2, ad I4)] values (10,11,1),(12,13,2),(14,15,4)' $casa_checktool ./tTableGram 'insert top 4 into tTableGram_tmp.tab2 values (rowid(), ab+10, rownumber())' $casa_checktool ./tTableGram 'select ab,ac,ad from tTableGram_tmp.tab2' $casa_checktool ./tTableGram 'select [select ab from ::][rownr()] from tTableGram_tmp.tab limit 1' # Test create and insert with a unit. echo "" echo "testing create ..." $casa_checktool ./tTableGram 'create table tTableGram_tmp.tab2 (col1 i4 [shape=[2,3], unit="m", dmtype="IncrementalStMan"], col2 B) dminfo [TYPE="IncrementalStMan",NAME="ISM1",SPEC=[BUCKETSIZE=16384],COLUMNS=["col1"]]' $casa_checktool ./tTableGram 'select col1,col2 from tTableGram_tmp.tab2' $casa_checktool ./tTableGram 'insert into tTableGram_tmp.tab2 (col1,col2) VALUES (array(1,[2,3])dam, F)' $casa_checktool ./tTableGram 'insert into tTableGram_tmp.tab2 (col1,col2) VALUES (array(5,[2,3]), T)' $casa_checktool ./tTableGram 'calc sum([select sum(col1) from tTableGram_tmp.tab2])' # Some tests of styles. echo "" echo "testing styles ..." $casa_checktool ./tTableGram 'using style python update tTableGram_tmp.tab set arr1 = array(1,4,3,2) where rownumber()==5' $casa_checktool ./tTableGram 'using style python calc [3:6][1]' $casa_checktool ./tTableGram 'using style glish calc [3:6][3]' $casa_checktool ./tTableGram 'using style base0 calc [3:6][3]' $casa_checktool ./tTableGram 'using style base1 calc [3:6][3]' $casa_checktool ./tTableGram 'using style python calc 6 in [3:6]' $casa_checktool ./tTableGram 'using style glish calc 6 in [3:6]' $casa_checktool ./tTableGram 'using style python calc array([3:7],3,4)[0,3]' $casa_checktool ./tTableGram 'using style glish calc array([3:7],3,4)[3,1]' $casa_checktool ./tTableGram 'using style python select ab,ac from tTableGram_tmp.tab where all(shape(arr1) == [4,3,2])' $casa_checktool ./tTableGram 'using style glish select ab,ac from tTableGram_tmp.tab where all(shape(arr1) == [2,3,4])' $casa_checktool ./tTableGram 'using style python select ab from tTableGram_tmp.tab where all(anys(fmod(sums(arr1,2),5)==0,[0]))' $casa_checktool ./tTableGram 'using style glish select ab from tTableGram_tmp.tab where all(anys(fmod(sums(arr1,1),5)==0,[2:4]))' $casa_checktool ./tTableGram 'using style python select ab from tTableGram_tmp.tab where rownumber() < 2' $casa_checktool ./tTableGram 'using style glish select ab from tTableGram_tmp.tab where rownumber() < 2' $casa_checktool ./tTableGram 'calc runningMedian(array([0:24],5,5),1,1)' $casa_checktool ./tTableGram 'calc boxedMedian(array([0:24],5,5),1,1)' $casa_checktool ./tTableGram 'calc boxedMax(array([0:24],5,5),[2,2])' $casa_checktool ./tTableGram 'calc 5.1//2' $casa_checktool ./tTableGram 'calc -4.1//2' $casa_checktool ./tTableGram 'calc -4**2' $casa_checktool ./tTableGram 'calc angdist([10,12],[10,12,13,14])' # Some tests of indexing, also negative (from the end). # It uses Glish style, thus 1:10 is 10 inclusive. echo echo "Testing indexing and nested arrays ..." $casa_checktool ./tTableGram 'calc [1:10][3] == 3' $casa_checktool ./tTableGram 'calc [1:10][-3] == 8' $casa_checktool ./tTableGram 'calc all([1:10][:5] == [1:5])' $casa_checktool ./tTableGram 'calc all([1:10][::3] == [1,4,7,10])' $casa_checktool ./tTableGram 'calc all([1:10][-7:-3:2] == [4,6,8])' $casa_checktool ./tTableGram 'calc all([[[1,2,3],[4,5,6]]] = array([1:6],3,2,1))' $casa_checktool ./tTableGram 'calc all([[[1,2,3],[4,5,6]],array([7:12],3,2)] = array([1:12],3,2,2))' # Some tests of units. echo "" echo "testing units ..." $casa_checktool ./tTableGram 'calc sum([select ab d as ABDAY from tTableGram_tmp.tab])' $casa_checktool ./tTableGram 'calc sum([select from tTableGram_tmp.tab giving [ab \in]])' $casa_checktool ./tTableGram 'select ab s AS ab1, ac mm AS ac1 INTO AS MEMORY from tTableGram_tmp.tab where rownumber() < 4' $casa_checktool ./tTableGram 'select ab s AS ab1, ac mm AS ac1 INTO tTableGram_tmp.tab_abac AS PLAIN from tTableGram_tmp.tab where rownumber() < 4' $casa_checktool ./tTableGram 'update tTableGram_tmp.tab_abac set ab1=1min+ab1, ac1=1.5cm+ac1' $casa_checktool ./tTableGram 'select ab1,ac1 from tTableGram_tmp.tab_abac' echo echo "Testing multiple tables ..." # Now do some test that uses two tables. # Use table tTable_2.data_v0 again. rm -rf tTableGram_tmp.tab* mkdir tTableGram_tmp.tab cp -r $testsrcdir/../../Tables/test/tTable_2.data_v0/table.* tTableGram_tmp.tab chmod 644 tTableGram_tmp.tab/* # Make a deep copy in reversed order. $casa_checktool ./tTableGram 'select from tTableGram_tmp.tab orderby ab desc giving tTableGram_tmp.rev as plain' # Do a select from these two tables. $casa_checktool ./tTableGram 'select t1.ab as ab1, t2.ab as ab2 from tTableGram_tmp.tab t1, tTableGram_tmp.rev t2' $casa_checktool ./tTableGram 'select t1.ab as ab1, t2.ab as ab2 from tTableGram_tmp.tab t1, tTableGram_tmp.rev t2 where abs(t1.ab - t2.ab) < 4' $casa_checktool ./tTableGram 'select t1.ab as ab1, t1.ac, t2.ab as ab2 from tTableGram_tmp.tab t1, tTableGram_tmp.rev t2 where t1.ab > t2.ab' $casa_checktool ./tTableGram 'select ab1,ab2 from [select t1.ab as ab1, t2.ab as ab2 from tTableGram_tmp.tab t1, tTableGram_tmp.rev t2 where abs(t1.ab - t2.ab) < 4]' $casa_checktool ./tTableGram 'select ab from tTableGram_tmp.tab where ab in [select t2.ab from tTableGram_tmp.tab t1, tTableGram_tmp.rev t2 where t1.ab > t2.ab]' $casa_checktool ./tTableGram 'select ab from tTableGram_tmp.tab where ac in [select ab from tTableGram_tmp.tab t1, tTableGram_tmp.rev t2 where t1.ab > t2.ab]' $casa_checktool ./tTableGram 'update tTableGram_tmp.tab t1, tTableGram_tmp.rev t2 set ab=ab+1, ac=t2.ac' $casa_checktool ./tTableGram 'select ab,ac from tTableGram_tmp.tab' $casa_checktool ./tTableGram 'update tTableGram_tmp.tab t1, tTableGram_tmp.rev t2 set ac=t2.ac+10 where ab=t2.ab' $casa_checktool ./tTableGram 'select ab,ac from tTableGram_tmp.tab' $casa_checktool ./tTableGram 'delete from tTableGram_tmp.tab t1, tTableGram_tmp.rev t2 where ab=t2.ab' $casa_checktool ./tTableGram 'select ab,ac from tTableGram_tmp.tab' # Do an update of a nested create. $casa_checktool ./tTableGram 'update [create table tTableGram_tmp.tst as plain_big col1 int limit 3 ] set col1=rowid()' $casa_checktool ./tTableGram 'select * from tTableGram_tmp.tst' # Do a count of a concatenated table. echo $casa_checktool ./tTableGram 'select gcount() from [tTableGram_tmp.tst,"tTableGram_*.tst" subtables a,b]' $casa_checktool ./tTableGram 'select gcount() from [tTableGram_tmp.tst,tTableGram_tmp.tst giving tTableGram_tmp.tst2]' $casa_checktool ./tTableGram 'select col1 from tTableGram_tmp.tst2' $casa_checktool ./tTableGram 'select gcount() from [tTableGram_tmp.tst,tTableGram_tmp.tst],[tTableGram_tmp.tst,tTableGram_tmp.tst,tTableGram_tmp.tst]' $casa_checktool ./tTableGram 'select gcount() from [tTableGram_tmp.tst,tTableGram_tmp.tst]' $casa_checktool ./tTableGram 'select gcount() from [tTableGram_tmp.tst as t1,tTableGram_tmp.tst as t2,[[tTableGram_tmp.tst,tTableGram_tmp.tst] as t3] as t4] as t5' # Test the WITH clause. $casa_checktool ./tTableGram 'with [tTableGram_tmp.tst,tTableGram_tmp.tst] t1 calc ([select gcount() from t1] + [select gcount() from tTableGram_tmp.tst] + [with [t1,t1] as t2 select gcount() from t2])[0]' casacore-3.7.1/tables/TaQL/test/tTableGramAlttab.out000066400000000000000000000256561476623553700222770ustar00rootroot00000000000000 create table tTableGramAlttab_tmp.tab2 (col1 i4 [shape=[2,3], unit="m"], col2 B) dminfo [TYPE="IncrementalStMan",NAME="ISM1",SPEC=[BUCKETSIZE=16384],COLUMNS=["col1"]] has been executed cretab result of 0 rows 2 selected columns: col1 col2 create table tTableGramAlttab_tmp.tab2/subtab has been executed cretab result of 0 rows 0 selected columns: Structure of table tTableGramAlttab_tmp.tab2 ------------------ 0 rows, 2 columns in an endian format (using 2 data managers) IncrementalStMan file=table.f0 name=ISM1 bucketsize=16384 MaxCacheSize=1 PERSCACHESIZE=1 col1 Int shape=[2,3] unit=[m] StandardStMan file=table.f1 name=StandardStMan bucketsize=128 MaxCacheSize=2 PERSCACHESIZE=2 IndexLength=118 col2 Bool scalar alter table tTableGramAlttab_tmp.tab2/subtab ADD COLUMN colxyz S SET KEYWORD colxyz::skey="newval", tabk=4 as I2 has been executed alttab result of 0 rows 0 selected columns: Structure of table tTableGramAlttab_tmp.tab2/subtab ------------------ 0 rows, 1 columns in an endian format (using 1 data managers) StandardStMan file=table.f0 name=SSM bucketsize=384 MaxCacheSize=2 PERSCACHESIZE=2 IndexLength=118 colxyz String scalar Keywords of main table ---------------------- Table Keywords tabk: Short 4 Column colxyz skey: String "newval" alter table tTableGramAlttab_tmp.tab2 SET KEYWORD subtab="Table: tTableGramAlttab_tmp.tab2/subtab", ::key1=3 AS I2, col1::subrec=[=], col1::subrec.k1="3x", key2=[2+3,4+5.] AS R4 has been executed alttab result of 0 rows 0 selected columns: Structure of table tTableGramAlttab_tmp.tab2 ------------------ 0 rows, 2 columns in an endian format (using 2 data managers) IncrementalStMan file=table.f0 name=ISM1 bucketsize=16384 MaxCacheSize=1 PERSCACHESIZE=1 col1 Int shape=[2,3] unit=[m] StandardStMan file=table.f1 name=StandardStMan bucketsize=128 MaxCacheSize=2 PERSCACHESIZE=2 IndexLength=118 col2 Bool scalar SubTables: tTableGramAlttab_tmp.tab2/subtab Keywords of main table ---------------------- Table Keywords subtab: Table tTableGramAlttab_tmp.tab2/subtab key1: Short 3 key2: Float array with shape [2] [5, 9] Column col1 QuantumUnits: String array with shape [1] [m] subrec: { k1: String "3x" } alter table tTableGramAlttab_tmp.tab2 SET KEYWORD keyrec=[sub1=[k1=1 as U1, k2=2 as I2, k3=3 as U4, k4=4 as I2, k5=5 as I4, k6=6 as I8, k7=7 as R4, k8=8 as R8, k9=9 as C4, k10=10 as C8, k11=T as B, K12="s" as S, subtab="Table: tTableGramAlttab_tmp.tab2/subtab"], sub2=[sub2a=[k1=1, k2=2., k3=3+3j, k4=T, k5="s"], sub2b=[k1=1. as R4, k2=2. as R8, k3=3. as C4, k4=4. as C8], sub2c=[k1=1+2i as C4]]] has been executed alttab result of 0 rows 0 selected columns: Structure of table tTableGramAlttab_tmp.tab2 ------------------ 0 rows, 2 columns in an endian format (using 2 data managers) IncrementalStMan file=table.f0 name=ISM1 bucketsize=16384 MaxCacheSize=1 PERSCACHESIZE=1 col1 Int shape=[2,3] unit=[m] StandardStMan file=table.f1 name=StandardStMan bucketsize=128 MaxCacheSize=2 PERSCACHESIZE=2 IndexLength=118 col2 Bool scalar SubTables: tTableGramAlttab_tmp.tab2/subtab Keywords of main table ---------------------- Table Keywords subtab: Table tTableGramAlttab_tmp.tab2/subtab key1: Short 3 key2: Float array with shape [2] [5, 9] keyrec: { sub1: { k1: uChar 1 k2: Short 2 k3: uInt 3 k4: Short 4 k5: Int 5 k6: Int64 6 k7: Float 7 k8: Double 8 k9: Complex (9,0) k10: DComplex (10,0) k11: Bool 1 K12: String "s" subtab: Table tTableGramAlttab_tmp.tab2/subtab } sub2: { sub2a: { k1: Int64 1 k2: Double 2 k3: DComplex (3,3) k4: Bool 1 k5: String "s" } sub2b: { k1: Float 1 k2: Double 2 k3: Complex (3,0) k4: DComplex (4,0) } sub2c: { k1: Complex (1,2) } } } Column col1 QuantumUnits: String array with shape [1] [m] subrec: { k1: String "3x" } alter table tTableGramAlttab_tmp.tab2 FROM ::subtab t1 COPY KEYWORD col1::subrec.k2 = t1.::tabk, col1::subrec.k1=t1.colxyz::skey, col1::subrec.k3=t1.::tabk as R4, keyrecc=keyrec RENAME KEYWORDS key1 TO key1n DROp keyword subtab, keyrec has been executed alttab result of 0 rows 0 selected columns: Structure of table tTableGramAlttab_tmp.tab2 ------------------ 0 rows, 2 columns in an endian format (using 2 data managers) IncrementalStMan file=table.f0 name=ISM1 bucketsize=16384 MaxCacheSize=1 PERSCACHESIZE=1 col1 Int shape=[2,3] unit=[m] StandardStMan file=table.f1 name=StandardStMan bucketsize=128 MaxCacheSize=2 PERSCACHESIZE=2 IndexLength=118 col2 Bool scalar Keywords of main table ---------------------- Table Keywords key1n: Short 3 key2: Float array with shape [2] [5, 9] keyrecc: { sub1: { k1: uChar 1 k2: Short 2 k3: uInt 3 k4: Short 4 k5: Int 5 k6: Int64 6 k7: Float 7 k8: Double 8 k9: Complex (9,0) k10: DComplex (10,0) k11: Bool 1 K12: String "s" subtab: Table tTableGramAlttab_tmp.tab2/subtab } sub2: { sub2a: { k1: Int64 1 k2: Double 2 k3: DComplex (3,3) k4: Bool 1 k5: String "s" } sub2b: { k1: Float 1 k2: Double 2 k3: Complex (3,0) k4: DComplex (4,0) } sub2c: { k1: Complex (1,2) } } } Column col1 QuantumUnits: String array with shape [1] [m] subrec: { k1: String "newval" k2: Short 4 k3: Float 4 } alter table tTableGramAlttab_tmp.tab2 ADD COLUMN col1a i4 [shape=[2,3], unit="m", dmtype="IncrementalStMan"], col2a B has been executed alttab result of 0 rows 0 selected columns: Structure of table tTableGramAlttab_tmp.tab2 ------------------ 0 rows, 4 columns in an endian format (using 3 data managers) IncrementalStMan file=table.f0 name=ISM1 bucketsize=16384 MaxCacheSize=1 PERSCACHESIZE=1 col1 Int shape=[2,3] unit=[m] StandardStMan file=table.f1 name=StandardStMan bucketsize=128 MaxCacheSize=2 PERSCACHESIZE=2 IndexLength=118 col2 Bool scalar StandardStMan file=table.f2 name=SSM bucketsize=260 MaxCacheSize=2 PERSCACHESIZE=2 IndexLength=118 col1a Int shape=[2,3] unit=[m] col2a Bool scalar alter table tTableGramAlttab_tmp.tab2 RENAME COLUMN col1a to col1b, col2a to col2b has been executed alttab result of 0 rows 0 selected columns: Structure of table tTableGramAlttab_tmp.tab2 ------------------ 0 rows, 4 columns in an endian format (using 3 data managers) IncrementalStMan file=table.f0 name=ISM1 bucketsize=16384 MaxCacheSize=1 PERSCACHESIZE=1 col1 Int shape=[2,3] unit=[m] StandardStMan file=table.f1 name=StandardStMan bucketsize=128 MaxCacheSize=2 PERSCACHESIZE=2 IndexLength=118 col2 Bool scalar StandardStMan file=table.f2 name=SSM bucketsize=260 MaxCacheSize=2 PERSCACHESIZE=2 IndexLength=118 col1b Int shape=[2,3] unit=[m] col2b Bool scalar alter table tTableGramAlttab_tmp.tab2 DELETE COLUMN col1b,col2 ,ADDrows 4 +5, set keyword col1::emvec=[] as R4 has been executed alttab result of 9 rows 0 selected columns: Structure of table tTableGramAlttab_tmp.tab2 ------------------ 9 rows, 2 columns in an endian format (using 2 data managers) IncrementalStMan file=table.f0 name=ISM1 bucketsize=16384 MaxCacheSize=1 PERSCACHESIZE=1 col1 Int shape=[2,3] unit=[m] StandardStMan file=table.f2 name=SSM bucketsize=260 MaxCacheSize=2 PERSCACHESIZE=2 IndexLength=134 col2b Bool scalar Keywords of main table ---------------------- Column col1 QuantumUnits: String array with shape [1] [m] subrec: { k1: String "newval" k2: Short 4 k3: Float 4 } emvec: Float array with shape [0] [] alter table [create table tTableGramAlttab_tmp.tab3 (ab R4)] set keyword ac=3 has been executed alttab result of 0 rows 0 selected columns: select iscolumn("col1b"), iskeyword("ac"), iscolumn("col2b"), iskeyword("col1::subrec"), iskeyword("col1::subrec.k2"), iskeyword("key1n"), t2.iscolumn("ab"), t2.iskeyword("ac"), t2.iscolumn("col2b"), t2.iskeyword("key1n") from tTableGramAlttab_tmp.tab2, tTableGramAlttab_tmp.tab3 as t2 limit 1 has been executed select result of 1 rows 10 selected columns: Col_1 Col_2 Col_3 Col_4 Col_5 Col_6 Col_7 Col_8 Col_9 Col_10 0 0 1 1 1 1 1 1 0 0 create table tTableGramAlttab_tmp.tab4 LIKE tTableGramAlttab_tmp.tab2 limit 1 has been executed cretab result of 1 rows 0 selected columns: Structure of table tTableGramAlttab_tmp.tab4 ------------------ 1 rows, 2 columns in an endian format (using 2 data managers) IncrementalStMan file=table.f0 name=ISM1 bucketsize=16384 MaxCacheSize=1 PERSCACHESIZE=1 col1 Int shape=[2,3] unit=[m] StandardStMan file=table.f1 name=SSM bucketsize=260 MaxCacheSize=2 PERSCACHESIZE=2 IndexLength=126 col2b Bool scalar select iscolumn("col1b"), iskeyword("ac"), iscolumn("col2b"), iskeyword("col1::subrec"), iskeyword("col1::subrec.k2"), iskeyword("key1n") from tTableGramAlttab_tmp.tab4 limit 1 has been executed select result of 1 rows 6 selected columns: Col_1 Col_2 Col_3 Col_4 Col_5 Col_6 0 0 1 1 1 1 create table tTableGramAlttab_tmp.tab4 LIKE tTableGramAlttab_tmp.tab2 drop column col1 has been executed cretab result of 0 rows 0 selected columns: Structure of table tTableGramAlttab_tmp.tab4 ------------------ 0 rows, 1 columns in an endian format (using 1 data managers) StandardStMan file=table.f0 name=SSM bucketsize=260 MaxCacheSize=2 PERSCACHESIZE=2 IndexLength=118 col2b Bool scalar create table tTableGramAlttab_tmp.tab4 LIKE tTableGramAlttab_tmp.tab2 t1 drop column col1 add column (col1 LIKE t1.col1 complex) has been executed cretab result of 0 rows 1 selected columns: col1 Structure of table tTableGramAlttab_tmp.tab4 ------------------ 0 rows, 2 columns in an endian format (using 2 data managers) StandardStMan file=table.f0 name=SSM bucketsize=260 MaxCacheSize=2 PERSCACHESIZE=2 IndexLength=118 col2b Bool scalar IncrementalStMan file=table.f1 name=ISM1 bucketsize=16384 MaxCacheSize=1 PERSCACHESIZE=1 col1 Complex shape=[2,3] unit=[m] insert into [create table tTableGramAlttab_tmp.tab5 as [storage="multifile"] col1 I4, col2 S] (col1,col2) VALUES (0,"v1"),(1,"val2") has been executed insert result of 2 rows 2 selected columns: col1 col2 0 v1 1 val2 select * from [alter table tTableGramAlttab_tmp.tab5 copy column col1c=col1,col2c=col2] has been executed select result of 2 rows 4 selected columns: col1 col2 col1c col2c 0 v1 0 v1 1 val2 1 val2 casacore-3.7.1/tables/TaQL/test/tTableGramAlttab.run000066400000000000000000000140331476623553700222570ustar00rootroot00000000000000#!/bin/sh #----------------------------------------------------------------------------- # Script to test the TableGram and TableParse class. All files generated will # be deleted on exit. #============================================================================= # Create a symlink to achieve that casacore_memcheck (if used) will use # that name rm -f tTableGramAlttab ln -s tTableGram tTableGramAlttab # Execute all kind of table creation commands. # Check the result using showtableinfo (leave out version line and endianness). # First create the tables to use. # - a table with columns col1 and col2 # - an empty subtable # - add column xyz, column keyword skey and table keyword tabk to the subtable echo "" $casa_checktool ./tTableGramAlttab 'create table tTableGramAlttab_tmp.tab2 (col1 i4 [shape=[2,3], unit="m"], col2 B) dminfo [TYPE="IncrementalStMan",NAME="ISM1",SPEC=[BUCKETSIZE=16384],COLUMNS=["col1"]]' $casa_checktool ./tTableGramAlttab 'create table tTableGramAlttab_tmp.tab2/subtab' ../../apps/showtableinfo in=tTableGramAlttab_tmp.tab2 | grep -v 'showtableinfo:' | sed -e 's/ in .* endian format/ in an endian format/' $casa_checktool ./tTableGramAlttab 'alter table tTableGramAlttab_tmp.tab2/subtab ADD COLUMN colxyz S SET KEYWORD colxyz::skey="newval", tabk=4 as I2' ../../apps/showtableinfo in=tTableGramAlttab_tmp.tab2/subtab tabkey=T colkey=T | grep -v 'showtableinfo:' | sed -e 's/ in .* endian format/ in an endian format/' # Add the subtable as a keyword # Also add other keywords (some nested) echo "" $casa_checktool ./tTableGramAlttab 'alter table tTableGramAlttab_tmp.tab2 SET KEYWORD subtab="Table: tTableGramAlttab_tmp.tab2/subtab", ::key1=3 AS I2, col1::subrec=[=], col1::subrec.k1="3x", key2=[2+3,4+5.] AS R4' ../../apps/showtableinfo in=tTableGramAlttab_tmp.tab2 tabkey=T colkey=T | grep -v 'showtableinfo:' | sed -e 's/ in .* endian format/ in an endian format/' # Add a complex nested keyword echo "" $casa_checktool ./tTableGramAlttab 'alter table tTableGramAlttab_tmp.tab2 SET KEYWORD keyrec=[sub1=[k1=1 as U1, k2=2 as I2, k3=3 as U4, k4=4 as I2, k5=5 as I4, k6=6 as I8, k7=7 as R4, k8=8 as R8, k9=9 as C4, k10=10 as C8, k11=T as B, K12="s" as S, subtab="Table: tTableGramAlttab_tmp.tab2/subtab"], sub2=[sub2a=[k1=1, k2=2., k3=3+3j, k4=T, k5="s"], sub2b=[k1=1. as R4, k2=2. as R8, k3=3. as C4, k4=4. as C8], sub2c=[k1=1+2i as C4]]]' ../../apps/showtableinfo in=tTableGramAlttab_tmp.tab2 tabkey=T colkey=T | grep -v 'showtableinfo:' | sed -e 's/ in .* endian format/ in an endian format/' # Copy some keywords from the subtable to the main table echo "" $casa_checktool ./tTableGramAlttab 'alter table tTableGramAlttab_tmp.tab2 FROM ::subtab t1 COPY KEYWORD col1::subrec.k2 = t1.::tabk, col1::subrec.k1=t1.colxyz::skey, col1::subrec.k3=t1.::tabk as R4, keyrecc=keyrec RENAME KEYWORDS key1 TO key1n DROp keyword subtab, keyrec' ../../apps/showtableinfo in=tTableGramAlttab_tmp.tab2 tabkey=T colkey=T | grep -v 'showtableinfo:' | sed -e 's/ in .* endian format/ in an endian format/' # Add a column to the main table echo "" $casa_checktool ./tTableGramAlttab 'alter table tTableGramAlttab_tmp.tab2 ADD COLUMN col1a i4 [shape=[2,3], unit="m", dmtype="IncrementalStMan"], col2a B' ../../apps/showtableinfo in=tTableGramAlttab_tmp.tab2 | grep -v 'showtableinfo:' | sed -e 's/ in .* endian format/ in an endian format/' # Rename 2 columns echo "" $casa_checktool ./tTableGramAlttab 'alter table tTableGramAlttab_tmp.tab2 RENAME COLUMN col1a to col1b, col2a to col2b' ../../apps/showtableinfo in=tTableGramAlttab_tmp.tab2 | grep -v 'showtableinfo:' | sed -e 's/ in .* endian format/ in an endian format/' # Delete a column and add a column keyword $casa_checktool ./tTableGramAlttab 'alter table tTableGramAlttab_tmp.tab2 DELETE COLUMN col1b,col2 ,ADDrows 4 +5, set keyword col1::emvec=[] as R4' ../../apps/showtableinfo in=tTableGramAlttab_tmp.tab2 colkey=T | grep -v 'showtableinfo:' | sed -e 's/ in .* endian format/ in an endian format/' # Add a keyword and get the select result of testing on column and keyword $casa_checktool ./tTableGramAlttab 'alter table [create table tTableGramAlttab_tmp.tab3 (ab R4)] set keyword ac=3' $casa_checktool ./tTableGramAlttab 'select iscolumn("col1b"), iskeyword("ac"), iscolumn("col2b"), iskeyword("col1::subrec"), iskeyword("col1::subrec.k2"), iskeyword("key1n"), t2.iscolumn("ab"), t2.iskeyword("ac"), t2.iscolumn("col2b"), t2.iskeyword("key1n") from tTableGramAlttab_tmp.tab2, tTableGramAlttab_tmp.tab3 as t2 limit 1' # Create a table similar to the other one using LIKE. echo "" $casa_checktool ./tTableGramAlttab 'create table tTableGramAlttab_tmp.tab4 LIKE tTableGramAlttab_tmp.tab2 limit 1' ../../apps/showtableinfo in=tTableGramAlttab_tmp.tab4 | grep -v 'showtableinfo:' | sed -e 's/ in .* endian format/ in an endian format/' # See if the keywords are copied. $casa_checktool ./tTableGramAlttab 'select iscolumn("col1b"), iskeyword("ac"), iscolumn("col2b"), iskeyword("col1::subrec"), iskeyword("col1::subrec.k2"), iskeyword("key1n") from tTableGramAlttab_tmp.tab4 limit 1' # Create a table similar to the other one, but drop a column. $casa_checktool ./tTableGramAlttab 'create table tTableGramAlttab_tmp.tab4 LIKE tTableGramAlttab_tmp.tab2 drop column col1' ../../apps/showtableinfo in=tTableGramAlttab_tmp.tab4 | grep -v 'showtableinfo:' | sed -e 's/ in .* endian format/ in an endian format/' # As above, but add col1 again with another data type. $casa_checktool ./tTableGramAlttab 'create table tTableGramAlttab_tmp.tab4 LIKE tTableGramAlttab_tmp.tab2 t1 drop column col1 add column (col1 LIKE t1.col1 complex)' ../../apps/showtableinfo in=tTableGramAlttab_tmp.tab4 | grep -v 'showtableinfo:' | sed -e 's/ in .* endian format/ in an endian format/' # Copy columns from one table to another. First create and fill a table. $casa_checktool ./tTableGramAlttab 'insert into [create table tTableGramAlttab_tmp.tab5 as [storage="multifile"] col1 I4, col2 S] (col1,col2) VALUES (0,"v1"),(1,"val2")' $casa_checktool ./tTableGramAlttab 'select * from [alter table tTableGramAlttab_tmp.tab5 copy column col1c=col1,col2c=col2]' # Remove the symlink rm -f tTableGramAlttab casacore-3.7.1/tables/TaQL/test/tTableGramCretab.out000066400000000000000000000161121476623553700222530ustar00rootroot00000000000000 create table tTableGramCretab_tmp.tab1 (col1 i4 [shape=[2,3], unit="m"], col2 B) dminfo [TYPE="IncrementalStMan",NAME="ISM1",SPEC=[BUCKETSIZE=16384],COLUMNS=["col1"]] has been executed cretab result of 0 rows 2 selected columns: col1 col2 Structure of table tTableGramCretab_tmp.tab1 ------------------ 0 rows, 2 columns in an endian format (using 2 data managers) IncrementalStMan file=table.f0 name=ISM1 bucketsize=16384 MaxCacheSize=1 PERSCACHESIZE=1 col1 Int shape=[2,3] unit=[m] StandardStMan file=table.f1 name=StandardStMan bucketsize=128 MaxCacheSize=2 PERSCACHESIZE=2 IndexLength=118 col2 Bool scalar create table tTableGramCretab_tmp.tab2 as [plain_big=T,storage="multifile",blocksize=32768] [col1 i4 [shape=[2,3], unit="m", dmtype="IncrementalStMan"], col2 B] limit 4 has been executed cretab result of 4 rows 2 selected columns: col1 col2 shape=[2, 3] 0 shape=[2, 3] 0 shape=[2, 3] 0 shape=[2, 3] 0 Structure of table tTableGramCretab_tmp.tab2 ------------------ 4 rows, 2 columns in big endian format (using 2 data managers) Stored as MultiFile with blocksize 32768 IncrementalStMan file=table.f0 name=IncrementalStMan bucketsize=32768 MaxCacheSize=1 PERSCACHESIZE=1 col1 Int shape=[2,3] unit=[m] StandardStMan file=table.f1 name=StandardStMan bucketsize=128 MaxCacheSize=2 PERSCACHESIZE=2 IndexLength=126 col2 Bool scalar create table tTableGramCretab_tmp.tab3 as [endian="little", blocksize=2048, type="plain", storage="multifile"] [col1x i4 [shape=[3,4]], col2y B] has been executed cretab result of 0 rows 2 selected columns: col1x col2y Structure of table tTableGramCretab_tmp.tab3 ------------------ 0 rows, 2 columns in little endian format (using 1 data managers) Stored as MultiFile with blocksize 2048 StandardStMan file=table.f0 name=StandardStMan bucketsize=260 MaxCacheSize=2 PERSCACHESIZE=2 IndexLength=118 col1x Int shape=[3,4] col2y Bool scalar create table tTableGramCretab_tmp.tab3 as [endian="little", blocksize=2048, type="plain", storage="multifile", overwrite=F] [col1x i4 [shape=[3,4]], col2y B] Caught an exception: Error in TaQL command: 'create table tTableGramCretab_tmp.tab3 as [endian="little", blocksize=2048, type="plain", storage="multifile", overwrite=F] [col1x i4 [shape=[3,4]], col2y B]' Table tTableGramCretab_tmp.tab3 already exists create table tTableGramCretab_tmp.tab1 as [endian="little", blocksize=2048, type="plain", storage="multifil"] [col1x i4 [shape=[3,4]], col2y B] Caught an exception: Error in TaQL command: 'create table tTableGramCretab_tmp.tab1 as [endian="little", blocksize=2048, type="plain", storage="multifil"] [col1x i4 [shape=[3,4]], col2y B]' Error in TaQL command: storage must have a string value multifile, multihdf5, sepfile, default or aipsrc create table tTableGramCretab_tmp.tab1 as [blocksize=2048.] [col1x i4 [shape=[3,4]], col2y B] Caught an exception: Error in TaQL command: 'create table tTableGramCretab_tmp.tab1 as [blocksize=2048.] [col1x i4 [shape=[3,4]], col2y B]' Error in TaQL command: blocksize must have an integer value create table tTableGramCretab_tmp.tab1 as blocksize [col1x i4 [shape=[3,4]], col2y B] Caught an exception: Error in TaQL command: 'create table tTableGramCretab_tmp.tab1 as blocksize [col1x i4 [shape=[3,4]], col2y B]' Error in TaQL command: blocksize must have an integer value create table tTableGramCretab_tmp.tab1 as blocksize=2048 [col1x i4 [shape=[3,4]], col2y B] Caught an exception: Error in TaQL command: create table tTableGramCretab_tmp.tab1 as blocksize=2048 [col1x i4 [shape=[3,4]], col2y B] parse error at or near position 52 '=' select from tTableGramCretab_tmp.tab3 where col2y giving tTableGramCretab_tmp.tab4 as [plain=T,storage="multifile",blocksize=32768] dminfo [NAME="ISM1",TYPE="IncrementalStMan",COLUMNS=["col1x","col2y"]] has been executed select result of 0 rows 0 selected columns: Structure of table tTableGramCretab_tmp.tab4 ------------------ 0 rows, 2 columns in an endian format (using 1 data managers) Stored as MultiFile with blocksize 32768 IncrementalStMan file=table.f0 name=ISM1 bucketsize=32768 MaxCacheSize=1 PERSCACHESIZE=1 col1x Int shape=[3,4] col2y Bool scalar drop table tTableGramCretab_tmp.tab4 has been executed result of 0 rows 0 selected columns: DROP TABLE tab4 succeeded with tTableGramCretab_tmp.tab3 t1 createtable t1::subtab1 [c1 i4] has been executed cretab result of 0 rows 1 selected columns: c1 create table tTableGramCretab_tmp.tab3::subtab1::subtab2 [c2 i2] has been executed cretab result of 0 rows 1 selected columns: c2 show table tTableGramCretab_tmp.tab3 tabkey has been executed [ Structure of table tTableGramCretab_tmp.tab3 ------------------ 0 rows, 2 columns in little endian format (using 1 data managers) Stored as MultiFile with blocksize 2048 col1x Int shape=[3,4] col2y Bool scalar SubTables: tTableGramCretab_tmp.tab3/subtab1 Keywords of main table ---------------------- Table Keywords subtab1: Table tTableGramCretab_tmp.tab3/subtab1 ] show table tTableGramCretab_tmp.tab3::subtab1 tabkey has been executed [ Structure of table tTableGramCretab_tmp.tab3/subtab1 ------------------ 0 rows, 1 columns in little endian format (using 1 data managers) c1 Int scalar SubTables: tTableGramCretab_tmp.tab3/subtab1/subtab2 Keywords of main table ---------------------- Table Keywords subtab2: Table tTableGramCretab_tmp.tab3/subtab1/subtab2 ] show table tTableGramCretab_tmp.tab3::subtab1::subtab2 has been executed [ Structure of table tTableGramCretab_tmp.tab3/subtab1/subtab2 ------------------ 0 rows, 1 columns in little endian format (using 1 data managers) c2 Short scalar ] with tTableGramCretab_tmp.tab3::subtab1 t1 droptable t1::subtab2 has been executed result of 0 rows 0 selected columns: show table tTableGramCretab_tmp.tab3::subtab1 tabkey has been executed [ Structure of table tTableGramCretab_tmp.tab3/subtab1 ------------------ 0 rows, 1 columns in little endian format (using 1 data managers) c1 Int scalar Keywords of main table ---------------------- ] select from tTableGramCretab_tmp.tab2 limit 3 giving tTableGramCretab_tmp.tab3::subtab1::subtab3 has been executed select result of 3 rows 0 selected columns: show table tTableGramCretab_tmp.tab3::subtab1 tabkey has been executed [ Structure of table tTableGramCretab_tmp.tab3/subtab1 ------------------ 0 rows, 1 columns in little endian format (using 1 data managers) c1 Int scalar SubTables: tTableGramCretab_tmp.tab3/subtab1/subtab3 Keywords of main table ---------------------- Table Keywords subtab3: Table tTableGramCretab_tmp.tab3/subtab1/subtab3 ] show table tTableGramCretab_tmp.tab3::subtab1::subtab3 has been executed [ Structure of table tTableGramCretab_tmp.tab3/subtab1/subtab3 ------------------ 3 rows, 2 columns in big endian format (using 2 data managers) Stored as MultiFile with blocksize 32768 out of tTableGramCretab_tmp.tab2 (4 rows, 2 columns) col1 Int shape=[2,3] unit=[m] col2 Bool scalar ] casacore-3.7.1/tables/TaQL/test/tTableGramCretab.run000066400000000000000000000077601476623553700222610ustar00rootroot00000000000000#!/bin/sh #----------------------------------------------------------------------------- # Script to test the TableGram and TableParse class. All files generated will # be deleted on exit. #============================================================================= # Create a symlink to achieve that casacore_memcheck (if used) will use # that name rm -f tTableGramCretab ln -s tTableGram tTableGramCretab # Execute all kind of table creation commands. # Check the result using showtableinfo (leave out version line and endianness). # Test create and insert with a unit. echo $casa_checktool ./tTableGramCretab 'create table tTableGramCretab_tmp.tab1 (col1 i4 [shape=[2,3], unit="m"], col2 B) dminfo [TYPE="IncrementalStMan",NAME="ISM1",SPEC=[BUCKETSIZE=16384],COLUMNS=["col1"]]' ../../apps/showtableinfo in=tTableGramCretab_tmp.tab1 | grep -v 'showtableinfo:' | sed -e 's/ in .* endian format/ in an endian format/' # Create as MultiFile. echo $casa_checktool ./tTableGramCretab 'create table tTableGramCretab_tmp.tab2 as [plain_big=T,storage="multifile",blocksize=32768] [col1 i4 [shape=[2,3], unit="m", dmtype="IncrementalStMan"], col2 B] limit 4' ../../apps/showtableinfo in=tTableGramCretab_tmp.tab2 | grep -v 'showtableinfo:' echo $casa_checktool ./tTableGramCretab 'create table tTableGramCretab_tmp.tab3 as [endian="little", blocksize=2048, type="plain", storage="multifile"] [col1x i4 [shape=[3,4]], col2y B]' ../../apps/showtableinfo in=tTableGramCretab_tmp.tab3 | grep -v 'showtableinfo:' # Some erroneous commands. echo $casa_checktool ./tTableGramCretab 'create table tTableGramCretab_tmp.tab3 as [endian="little", blocksize=2048, type="plain", storage="multifile", overwrite=F] [col1x i4 [shape=[3,4]], col2y B]' $casa_checktool ./tTableGramCretab 'create table tTableGramCretab_tmp.tab1 as [endian="little", blocksize=2048, type="plain", storage="multifil"] [col1x i4 [shape=[3,4]], col2y B]' $casa_checktool ./tTableGramCretab 'create table tTableGramCretab_tmp.tab1 as [blocksize=2048.] [col1x i4 [shape=[3,4]], col2y B]' $casa_checktool ./tTableGramCretab 'create table tTableGramCretab_tmp.tab1 as blocksize [col1x i4 [shape=[3,4]], col2y B]' $casa_checktool ./tTableGramCretab 'create table tTableGramCretab_tmp.tab1 as blocksize=2048 [col1x i4 [shape=[3,4]], col2y B]' # A table created using a selection while changing the dminfo. echo $casa_checktool ./tTableGramCretab 'select from tTableGramCretab_tmp.tab3 where col2y giving tTableGramCretab_tmp.tab4 as [plain=T,storage="multifile",blocksize=32768] dminfo [NAME="ISM1",TYPE="IncrementalStMan",COLUMNS=["col1x","col2y"]]' ../../apps/showtableinfo in=tTableGramCretab_tmp.tab4 | grep -v 'showtableinfo:' | sed -e 's/ in .* endian format/ in an endian format/' # Delete the last table. echo $casa_checktool ./tTableGramCretab 'drop table tTableGramCretab_tmp.tab4' ls -l tTableGramCretab_tmp.tab4 > /dev/null 2>&1 || echo "DROP TABLE tab4 succeeded" # Create a subtable and a subtable in there. echo $casa_checktool ./tTableGramCretab 'with tTableGramCretab_tmp.tab3 t1 createtable t1::subtab1 [c1 i4]' $casa_checktool ./tTableGramCretab 'create table tTableGramCretab_tmp.tab3::subtab1::subtab2 [c2 i2]' $casa_checktool ./tTableGramCretab 'show table tTableGramCretab_tmp.tab3 tabkey' $casa_checktool ./tTableGramCretab 'show table tTableGramCretab_tmp.tab3::subtab1 tabkey' $casa_checktool ./tTableGramCretab 'show table tTableGramCretab_tmp.tab3::subtab1::subtab2' # Drop the subtables. echo $casa_checktool ./tTableGramCretab 'with tTableGramCretab_tmp.tab3::subtab1 t1 droptable t1::subtab2' $casa_checktool ./tTableGramCretab 'show table tTableGramCretab_tmp.tab3::subtab1 tabkey' # Create a table using a SELECT GIVING. $casa_checktool ./tTableGramCretab 'select from tTableGramCretab_tmp.tab2 limit 3 giving tTableGramCretab_tmp.tab3::subtab1::subtab3' $casa_checktool ./tTableGramCretab 'show table tTableGramCretab_tmp.tab3::subtab1 tabkey' $casa_checktool ./tTableGramCretab 'show table tTableGramCretab_tmp.tab3::subtab1::subtab3' # Remove the symlink rm -f tTableGramCretab casacore-3.7.1/tables/TaQL/test/tTableGramEpoch.out000066400000000000000000000112101476623553700221030ustar00rootroot00000000000000select 4mar53 giving tTableGramEpoch_tmp.tab has been executed select result of 1 rows 1 selected columns: Col_1 34440 using style python select * from tTableGramEpoch_tmp.tab has been executed select result of 1 rows 1 selected columns: Col_1 04-Mar-1953 Structure of table /Users/diepen/casa/masktql/build/dbg/tables/TaQL/test/tTableGramEpoch_tmp.tab ------------------ 1 rows, 1 columns in little endian format (using 1 data managers) StandardStMan file=table.f0 name=StandardStMan bucketsize=256 ActualCacheSize=2 PERSCACHESIZE=2 IndexLength=126 Col_1 double scalar unit=[d] measure=epoch,UTC Keywords of main table ---------------------- Column Col_1 MEASINFO: { type: String "epoch" Ref: String "UTC" } QuantumUnits: String array with shape [1] [d] select 4mar53 s giving tTableGramEpoch_tmp.tab has been executed select result of 1 rows 1 selected columns: Col_1 2.97562e+09 using style python select * from tTableGramEpoch_tmp.tab has been executed select result of 1 rows 1 selected columns: Col_1 04-Mar-1953 Structure of table /Users/diepen/casa/masktql/build/dbg/tables/TaQL/test/tTableGramEpoch_tmp.tab ------------------ 1 rows, 1 columns in little endian format (using 1 data managers) StandardStMan file=table.f0 name=StandardStMan bucketsize=256 ActualCacheSize=2 PERSCACHESIZE=2 IndexLength=126 Col_1 double scalar unit=[s] measure=epoch,UTC Keywords of main table ---------------------- Column Col_1 MEASINFO: { type: String "epoch" Ref: String "UTC" } QuantumUnits: String array with shape [1] [s] insert into tTableGramEpoch_tmp.tab values (04-03-1963) has been executed insert result of 1 rows 1 selected columns: Col_1 3.29115e+09 select * from tTableGramEpoch_tmp.tab has been executed select result of 2 rows 1 selected columns: Col_1 2.97562e+09 3.29115e+09 using style python select * from tTableGramEpoch_tmp.tab has been executed select result of 2 rows 1 selected columns: Col_1 04-Mar-1953 04-Mar-1963 update tTableGramEpoch_tmp.tab set Col_1=1958-05-11 where Col_1=4-mar-1963 has been executed update result of 1 rows 1 selected columns: Col_1 3.13926e+09 select * from tTableGramEpoch_tmp.tab has been executed select result of 2 rows 1 selected columns: Col_1 2.97562e+09 3.13926e+09 using style python select * from tTableGramEpoch_tmp.tab has been executed select result of 2 rows 1 selected columns: Col_1 04-Mar-1953 11-May-1958 select 34447 as TIME TIME giving tTableGramEpoch_tmp.tab has been executed select result of 1 rows 1 selected columns: TIME 34447 using style python select * from tTableGramEpoch_tmp.tab has been executed select result of 1 rows 1 selected columns: TIME 11-Mar-1953 Structure of table /Users/diepen/casa/masktql/build/dbg/tables/TaQL/test/tTableGramEpoch_tmp.tab ------------------ 1 rows, 1 columns in little endian format (using 1 data managers) StandardStMan file=table.f0 name=StandardStMan bucketsize=256 ActualCacheSize=2 PERSCACHESIZE=2 IndexLength=126 TIME double scalar unit=[d] measure=epoch,UTC Keywords of main table ---------------------- Column TIME MEASINFO: { type: String "epoch" Ref: String "UTC" } QuantumUnits: String array with shape [1] [d] create table tTableGramEpoch_tmp.tab TIME TIME has been executed cretab result of 0 rows 1 selected columns: TIME Structure of table /Users/diepen/casa/masktql/build/dbg/tables/TaQL/test/tTableGramEpoch_tmp.tab ------------------ 0 rows, 1 columns in little endian format (using 1 data managers) StandardStMan file=table.f0 name=StandardStMan bucketsize=256 ActualCacheSize=2 PERSCACHESIZE=2 IndexLength=118 TIME double scalar unit=[d] measure=epoch,UTC Keywords of main table ---------------------- Column TIME MEASINFO: { type: String "epoch" Ref: String "UTC" } QuantumUnits: String array with shape [1] [d] create table tTableGramEpoch_tmp.tab TIME EPOCH [UNIT="s"] has been executed cretab result of 0 rows 1 selected columns: TIME Structure of table /Users/diepen/casa/masktql/build/dbg/tables/TaQL/test/tTableGramEpoch_tmp.tab ------------------ 0 rows, 1 columns in little endian format (using 1 data managers) StandardStMan file=table.f0 name=StandardStMan bucketsize=256 ActualCacheSize=2 PERSCACHESIZE=2 IndexLength=118 TIME double scalar unit=[s] measure=epoch,UTC Keywords of main table ---------------------- Column TIME MEASINFO: { type: String "epoch" Ref: String "UTC" } QuantumUnits: String array with shape [1] [s] casacore-3.7.1/tables/TaQL/test/tTableGramEpoch.run000066400000000000000000000043021476623553700221040ustar00rootroot00000000000000#!/bin/sh #----------------------------------------------------------------------------- # Script to test the TableGram and TableParse class. All files generated will # be deleted on exit. #============================================================================= # Create a symlink to achieve that casacore_memcheck (if used) will use # that name rm -f tTableGramEpoch ln -s tTableGram tTableGramEpoch # Execute all kind of TaQL commands involving epochs. # Check the result using showtableinfo (leave out version line). # Create a table with an Epoch column. $casa_checktool ./tTableGramEpoch 'select 4mar53 giving tTableGramEpoch_tmp.tab' ../../apps/taql -pc 'select * from tTableGramEpoch_tmp.tab' ../../apps/showtableinfo in=tTableGramEpoch_tmp.tab colkey=1 | grep -v 'showtableinfo:' # Same with unit seconds. $casa_checktool ./tTableGramEpoch 'select 4mar53 s giving tTableGramEpoch_tmp.tab' ../../apps/taql -pc 'select * from tTableGramEpoch_tmp.tab' ../../apps/showtableinfo in=tTableGramEpoch_tmp.tab colkey=1 | grep -v 'showtableinfo:' # Insert another row. $casa_checktool ./tTableGramEpoch 'insert into tTableGramEpoch_tmp.tab values (04-03-1963)' ./tTableGram 'select * from tTableGramEpoch_tmp.tab' ../../apps/taql -pc 'select * from tTableGramEpoch_tmp.tab' # Update a row. $casa_checktool ./tTableGramEpoch 'update tTableGramEpoch_tmp.tab set Col_1=1958-05-11 where Col_1=4-mar-1963' ./tTableGram 'select * from tTableGramEpoch_tmp.tab' ../../apps/taql -pc 'select * from tTableGramEpoch_tmp.tab' # Create using SELECT with a given data type. $casa_checktool ./tTableGramEpoch 'select 34447 as TIME TIME giving tTableGramEpoch_tmp.tab' ../../apps/taql -pc 'select * from tTableGramEpoch_tmp.tab' ../../apps/showtableinfo in=tTableGramEpoch_tmp.tab colkey=1 | grep -v 'showtableinfo:' # Create using CREATE TABLE $casa_checktool ./tTableGramEpoch 'create table tTableGramEpoch_tmp.tab TIME TIME' ../../apps/showtableinfo in=tTableGramEpoch_tmp.tab colkey=1 | grep -v 'showtableinfo:' $casa_checktool ./tTableGramEpoch 'create table tTableGramEpoch_tmp.tab TIME EPOCH [UNIT="s"]' ../../apps/showtableinfo in=tTableGramEpoch_tmp.tab colkey=1 | grep -v 'showtableinfo:' # Remove the symlink rm -f tTableGramEpoch casacore-3.7.1/tables/TaQL/test/tTableGramError.cc000066400000000000000000000144501476623553700217250ustar00rootroot00000000000000//# tTableGramError.cc: This program tests erroneous table commands //# Copyright (C) 2022 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include // Run a table command which should fail. // Return 1 if not failing. int errCmd (const String& cmd) { cout << cmd << endl; try { tableCommand (cmd); cout << " Above command succeeded, but was expected to fail" << endl; return 1; } catch (const AipsError& x) { cout << " " << x.what() << endl; } return 0; } int main() { // Start with creating the table to use in the tests. tableCommand ("CREATE TABLE tTableGramError_tmp.tab1 [cb bool, ci int, cd double [unit='Hz'], cs string, cc complex, ct date, cai int [shape=[3,4]]] limit 1"); tableCommand ("CREATE TABLE tTableGramError_tmp.tab2 [cb bool, ci int, cd double, cs string, cc complex, ct date] limit 2"); int n = 0; // Incomplete commands n += errCmd ("select from"); n += errCmd ("select from tTableGramError_tmp.tab1 where"); n += errCmd ("select from tTableGramError_tmp.tab1 where ci>1 groupby"); n += errCmd ("select from tTableGramError_tmp.tab1 where ci>1 orderby"); // Invalid clause n += errCmd ("select from tTableGramError_tmp.tab1 where ci>1 group ci"); n += errCmd ("select from tTableGramError_tmp.tab1 where ci>1 order ci"); n += errCmd ("select from tTableGramError_tmp.tab1 where ci>1 order ci groupby ci"); // Shorthand already used or not defined n += errCmd ("select from tTableGramError_tmp.tab1 t1, tTableGramError_tmp.tab2 t1"); n += errCmd ("select t2.ci from tTableGramError_tmp.tab1 t1"); // Table does not exist n += errCmd ("select from tTableGramError_tmp.tabx"); n += errCmd ("select from tTableGramError_tmp.tabx::SUBTAB"); n += errCmd ("select from tTableGramError_tmp.tab1::SUBTAB"); n += errCmd ("select from ['tTableGramError_tmp.tabx*']"); n += errCmd ("select from $1"); // Column does not exist n += errCmd ("select cx from tTableGramError_tmp.tab1"); n += errCmd ("select from tTableGramError_tmp.tab1 where cx>1"); n += errCmd ("select from tTableGramError_tmp.tab1 orderby cx"); n += errCmd ("select from tTableGramError_tmp.tab1 groupby cx"); n += errCmd ("create table a.b like tTableGramError_tmp.tab1 drop column cx"); // Mismatching unit n += errCmd ("select cd kg from tTableGramError_tmp.tab1"); n += errCmd ("select from tTableGramError_tmp.tab1 where cd>1kg"); n += errCmd ("select from tTableGramError_tmp.tab1 orderby cd kg"); n += errCmd ("select from tTableGramError_tmp.tab1 groupby cd kg"); // Mismatching nr of rows n += errCmd ("select t1.cb, t2.ci from tTableGramError_tmp.tab1 t1, tTableGramError_tmp.tab2 t2"); n += errCmd ("select from tTableGramError_tmp.tab1 t1, tTableGramError_tmp.tab2 t2 where t1.ci!=t2.ci"); // Unknown shorthand or column n += errCmd ("select from tTableGramError_tmp.tab1 t1 where t2.ci=0"); n += errCmd ("select from tTableGramError_tmp.tab1 t1 where t1.cx=0"); // Invalid axes specification n += errCmd ("select means(cai,0,[1]) from tTableGramError_tmp.tab1"); // Invalid function name n += errCmd ("select sqrtt(cai) from tTableGramError_tmp.tab1"); cout << ">>>" << endl; // error messages are system dependent n += errCmd ("select m.sqrtt(cai) from tTableGramError_tmp.tab1"); // libm exists n += errCmd ("select mxy.sqrtt(cai) from tTableGramError_tmp.tab1"); cout << "<<<" << endl; // Invalid aggregation n += errCmd ("select gsum(gmin(ci)) from tTableGramError_tmp.tab1"); n += errCmd ("select from tTableGramError_tmp.tab1 groupby ci having gsum(gmin(ci)>0"); n += errCmd ("select from tTableGramError_tmp.tab1 where gsum(ci) > 0"); n += errCmd ("select from tTableGramError_tmp.tab1 orderby gsum(ci)"); n += errCmd ("select from tTableGramError_tmp.tab1 groupby gsum(ci)"); // Mismatching data types n += errCmd ("select from tTableGramError_tmp.tab1 where ci>cb"); n += errCmd ("select from tTableGramError_tmp.tab1 where ci+cs>0"); n += errCmd ("select from tTableGramError_tmp.tab1 where cc+cs>0"); n += errCmd ("select ct+cs from tTableGramError_tmp.tab1"); n += errCmd ("calc date() + date()"); // Invalid INSERT. n+= errCmd ("INSERT INTO tTableGramError_tmp.tab1 (ci,cd) VALUES (1,2),(1,2,3)"); n+= errCmd ("INSERT INTO tTableGramError_tmp.tab1 (ci,cd) VALUES (1,2,3)"); n+= errCmd ("INSERT INTO tTableGramError_tmp.tab1 (ci,cs) VALUES (1,2)"); // Invalid multi-dim array or mask n += errCmd ("calc sum([[1,2],[3,4,5]])"); n += errCmd ("calc sum([1,2][T,F])"); cout << ">>>" << endl; n += errCmd ("calc sum([1,2][[T,F,F]])"); // gives system-dependent message cout << "<<<" << endl; // Invalid LIMIT n += errCmd ("select ci from tTableGramError_tmp.tab1 limit ::0"); n += errCmd ("select ci from tTableGramError_tmp.tab1 limit ci"); n += errCmd ("select ci limit -1"); n += errCmd ("select ci offset -1"); // Invalid GIVING (cannot use array). n += errCmd ("select ci from tTableGramError_tmp.tab1 where ci>max([select from tTableGramError_tmp.tab1 giving [cai]])"); if (n > 0) { cerr << "***Error*** " << n << " commands succeeded, but were expected to fail." << endl; return 1; } return 0; } casacore-3.7.1/tables/TaQL/test/tTableGramError.out000066400000000000000000000273151476623553700221530ustar00rootroot00000000000000select from Error in TaQL command: select from parse error at or near position 12 '' select from tTableGramError_tmp.tab1 where Error in TaQL command: select from tTableGramError_tmp.tab1 where parse error at or near position 43 '' select from tTableGramError_tmp.tab1 where ci>1 groupby Error in TaQL command: select from tTableGramError_tmp.tab1 where ci>1 groupby parse error at or near position 56 '' select from tTableGramError_tmp.tab1 where ci>1 orderby Error in TaQL command: select from tTableGramError_tmp.tab1 where ci>1 orderby parse error at or near position 56 '' select from tTableGramError_tmp.tab1 where ci>1 group ci Error in TaQL command: 'select from tTableGramError_tmp.tab1 where ci>1 group ci' Unit::check Illegal unit string 'group' select from tTableGramError_tmp.tab1 where ci>1 order ci Error in TaQL command: 'select from tTableGramError_tmp.tab1 where ci>1 order ci' Unit::check Illegal unit string 'order' select from tTableGramError_tmp.tab1 where ci>1 order ci groupby ci Error in TaQL command: 'select from tTableGramError_tmp.tab1 where ci>1 order ci groupby ci' Unit::check Illegal unit string 'order' select from tTableGramError_tmp.tab1 t1, tTableGramError_tmp.tab2 t1 Error in TaQL command: 'select from tTableGramError_tmp.tab1 t1, tTableGramError_tmp.tab2 t1' Error in select expression: Shorthand 't1' has already been used select t2.ci from tTableGramError_tmp.tab1 t1 Error in TaQL command: 'select t2.ci from tTableGramError_tmp.tab1 t1' Error in select expression: Shorthand t2 has not been defined in FROM clause select from tTableGramError_tmp.tabx Error in TaQL command: 'select from tTableGramError_tmp.tabx' Table tTableGramError_tmp.tabx does not exist select from tTableGramError_tmp.tabx::SUBTAB Error in TaQL command: 'select from tTableGramError_tmp.tabx::SUBTAB' Table tTableGramError_tmp.tabx does not exist select from tTableGramError_tmp.tab1::SUBTAB Error in TaQL command: 'select from tTableGramError_tmp.tab1::SUBTAB' Error in select expression: SUBTAB is an unknown keyword/subtable in tTableGramError_tmp.tab1::SUBTAB select from ['tTableGramError_tmp.tabx*'] Error in TaQL command: 'select from ['tTableGramError_tmp.tabx*']' Error in select expression: No matching tables found for tTableGramError_tmp.tabx* select from $1 Error in TaQL command: 'select from $1' Error in select expression: Invalid temporary table number given in $1 select cx from tTableGramError_tmp.tab1 Error in TaQL command: 'select cx from tTableGramError_tmp.tab1' Error in select expression: cx is an unknown column (or keyword) in table tTableGramError_tmp.tab1 select from tTableGramError_tmp.tab1 where cx>1 Error in TaQL command: 'select from tTableGramError_tmp.tab1 where cx>1' Error in select expression: cx is an unknown column (or keyword) in table tTableGramError_tmp.tab1 select from tTableGramError_tmp.tab1 orderby cx Error in TaQL command: 'select from tTableGramError_tmp.tab1 orderby cx' Error in select expression: cx is an unknown column (or keyword) in table tTableGramError_tmp.tab1 select from tTableGramError_tmp.tab1 groupby cx Error in TaQL command: 'select from tTableGramError_tmp.tab1 groupby cx' Error in select expression: cx is an unknown column (or keyword) in table tTableGramError_tmp.tab1 create table a.b like tTableGramError_tmp.tab1 drop column cx Error in TaQL command: 'create table a.b like tTableGramError_tmp.tab1 drop column cx' Error in select expression: Column cx to be dropped does not exist in the LIKE table select cd kg from tTableGramError_tmp.tab1 Error in TaQL command: 'select cd kg from tTableGramError_tmp.tab1' Error in select expression: Units kg and Hz do not conform select from tTableGramError_tmp.tab1 where cd>1kg Error in TaQL command: 'select from tTableGramError_tmp.tab1 where cd>1kg' Error in select expression: Units Hz and kg do not conform select from tTableGramError_tmp.tab1 orderby cd kg Error in TaQL command: 'select from tTableGramError_tmp.tab1 orderby cd kg' Error in select expression: Units kg and Hz do not conform select from tTableGramError_tmp.tab1 groupby cd kg Error in TaQL command: 'select from tTableGramError_tmp.tab1 groupby cd kg' Error in select expression: Units kg and Hz do not conform select t1.cb, t2.ci from tTableGramError_tmp.tab1 t1, tTableGramError_tmp.tab2 t2 Error in TaQL command: 'select t1.cb, t2.ci from tTableGramError_tmp.tab1 t1, tTableGramError_tmp.tab2 t2' Error in select expression: Nr of rows (2) in table column t2.ci differs from column t1.cb (1) select from tTableGramError_tmp.tab1 t1, tTableGramError_tmp.tab2 t2 where t1.ci!=t2.ci Error in TaQL command: 'select from tTableGramError_tmp.tab1 t1, tTableGramError_tmp.tab2 t2 where t1.ci!=t2.ci' Error in select expression: Nr of rows (2) in table column t2.ci differs from column t1.ci (1) select from tTableGramError_tmp.tab1 t1 where t2.ci=0 Error in TaQL command: 'select from tTableGramError_tmp.tab1 t1 where t2.ci=0' Error in select expression: Shorthand t2 has not been defined in FROM clause select from tTableGramError_tmp.tab1 t1 where t1.cx=0 Error in TaQL command: 'select from tTableGramError_tmp.tab1 t1 where t1.cx=0' Error in select expression: t1.cx is an unknown column (or keyword) in table tTableGramError_tmp.tab1 select means(cai,0,[1]) from tTableGramError_tmp.tab1 Error in TaQL command: 'select means(cai,0,[1]) from tTableGramError_tmp.tab1' Error in select expression: Erroneous use of function means - Axes/shape arguments 3 are not one or more scalars or a single bounded range select sqrtt(cai) from tTableGramError_tmp.tab1 Error in TaQL command: 'select sqrtt(cai) from tTableGramError_tmp.tab1' Error in select expression: TaQL function sqrtt is unknown; use 'show func' to see all functions >>> select m.sqrtt(cai) from tTableGramError_tmp.tab1 Error in TaQL command: 'select m.sqrtt(cai) from tTableGramError_tmp.tab1' Found dynamic library libm.dylib, but not its register_m function dlsym(0x1179b8f00, register_m): symbol not found select mxy.sqrtt(cai) from tTableGramError_tmp.tab1 Error in TaQL command: 'select mxy.sqrtt(cai) from tTableGramError_tmp.tab1' Shared library mxy not found in CASACORE_LDPATH or (DY)LD_LIBRARY_PATH dlopen(/Users/diepen/lib/libcasa_mxy.7.dylib, 10): image not found dlopen(/Users/diepen/lib/libcasa_mxy.dylib, 10): image not found dlopen(/Users/diepen/lib/libmxy.7.dylib, 10): image not found dlopen(/Users/diepen/lib/libmxy.dylib, 10): image not found dlopen(/Users/diepen/sim/build/clang_opt/installed/lib/libcasa_mxy.7.dylib, 10): image not found dlopen(/Users/diepen/sim/build/clang_opt/installed/lib/libcasa_mxy.dylib, 10): image not found dlopen(/Users/diepen/sim/build/clang_opt/installed/lib/libmxy.7.dylib, 10): image not found dlopen(/Users/diepen/sim/build/clang_opt/installed/lib/libmxy.dylib, 10): image not found dlopen(libcasa_mxy.7.dylib, 10): image not found dlopen(libcasa_mxy.dylib, 10): image not found dlopen(libmxy.7.dylib, 10): image not found dlopen(libmxy.dylib, 10): image not found <<< select gsum(gmin(ci)) from tTableGramError_tmp.tab1 Error in TaQL command: 'select gsum(gmin(ci)) from tTableGramError_tmp.tab1' Error in select expression: Erroneous use of function gsum - The argument of an aggregate function cannot use an aggregate function select from tTableGramError_tmp.tab1 groupby ci having gsum(gmin(ci)>0 Error in TaQL command: select from tTableGramError_tmp.tab1 groupby ci having gsum(gmin(ci)>0 parse error at or near position 71 '' select from tTableGramError_tmp.tab1 where gsum(ci) > 0 Error in TaQL command: 'select from tTableGramError_tmp.tab1 where gsum(ci) > 0' Error in select expression: Invalid use of an aggregate function (only use in SELECT or HAVING clause) select from tTableGramError_tmp.tab1 orderby gsum(ci) Error in TaQL command: 'select from tTableGramError_tmp.tab1 orderby gsum(ci)' Error in select expression: Invalid use of an aggregate function (only use in SELECT or HAVING clause) select from tTableGramError_tmp.tab1 groupby gsum(ci) Error in TaQL command: 'select from tTableGramError_tmp.tab1 groupby gsum(ci)' Error in select expression: Invalid use of an aggregate function (only use in SELECT or HAVING clause) select from tTableGramError_tmp.tab1 where ci>cb Error in TaQL command: 'select from tTableGramError_tmp.tab1 where ci>cb' Error in select expression: invalid operand data type; TableExprNodeBinary::getDT cannot combine arguments with data type Double and Bool select from tTableGramError_tmp.tab1 where ci+cs>0 Error in TaQL command: 'select from tTableGramError_tmp.tab1 where ci+cs>0' Error in select expression: invalid operand data type; TableExprNodeBinary::getDT cannot combine arguments with data type Double and String select from tTableGramError_tmp.tab1 where cc+cs>0 Error in TaQL command: 'select from tTableGramError_tmp.tab1 where cc+cs>0' Error in select expression: invalid operand data type; TableExprNodeBinary::getDT cannot combine arguments with data type Complex and String select ct+cs from tTableGramError_tmp.tab1 Error in TaQL command: 'select ct+cs from tTableGramError_tmp.tab1' Error in select expression: invalid operand data type; TableExprNodeBinary::getDT cannot combine arguments with data type Double and String calc date() + date() Error in TaQL command: 'calc date() + date()' Error in select expression: invalid operand data type; TableExprNodeBinary::getDT cannot combine arguments with data type DateTime and DateTime INSERT INTO tTableGramError_tmp.tab1 (ci,cd) VALUES (1,2),(1,2,3) Error in TaQL command: 'INSERT INTO tTableGramError_tmp.tab1 (ci,cd) VALUES (1,2),(1,2,3)' Error in select expression: Different nr of values given in INSERT INSERT INTO tTableGramError_tmp.tab1 (ci,cd) VALUES (1,2,3) Error in TaQL command: 'INSERT INTO tTableGramError_tmp.tab1 (ci,cd) VALUES (1,2,3)' Error in select expression: Error in INSERT command; nr of columns (=2) mismatches number of VALUES expressions (=3) INSERT INTO tTableGramError_tmp.tab1 (ci,cs) VALUES (1,2) Error in TaQL command: 'INSERT INTO tTableGramError_tmp.tab1 (ci,cs) VALUES (1,2)' Error in select expression: Column cs has an invalid data type for an UPDATE with an integer value calc sum([[1,2],[3,4,5]]) Error in TaQL command: 'calc sum([[1,2],[3,4,5]])' Error in select expression: Shapes of nested arrays do not match calc sum([1,2][T,F]) Error in TaQL command: 'calc sum([1,2][T,F])' Error in select expression: Second argument of a masked array must be an array; maybe extra brackets are needed like [1,2][[T,F]] >>> calc sum([1,2][[T,F,F]]) Error in TaQL command: 'calc sum([1,2][[T,F,F]])'>>> (/Users/diepen/casa/github/casacore/tables/TaQL/MArrayBase.cc : 113) Failed AlwaysAssert itsShape.isEqual (mask.shape()) <<< select ci from tTableGramError_tmp.tab1 limit ::0 Error in TaQL command: 'select ci from tTableGramError_tmp.tab1 limit ::0' Error in select expression: in the LIMIT clause stride 0 must be positive select ci from tTableGramError_tmp.tab1 limit ci Error in TaQL command: 'select ci from tTableGramError_tmp.tab1 limit ci' Error in select expression: Shorthand has not been defined in FROM clause select ci limit -1 Error in TaQL command: 'select ci limit -1' Error in select expression: LIMIT and OFFSET values cannot be negative if no tables are given in the FROM clause select ci offset -1 Error in TaQL command: 'select ci offset -1' Error in select expression: LIMIT and OFFSET values cannot be negative if no tables are given in the FROM clause select ci from tTableGramError_tmp.tab1 where ci>max([select from tTableGramError_tmp.tab1 giving [cai]]) Error in TaQL command: 'select ci from tTableGramError_tmp.tab1 where ci>max([select from tTableGramError_tmp.tab1 giving [cai]])' Error in select expression: Set in GIVING clause should contain scalar elements casacore-3.7.1/tables/TaQL/test/tTableGramFunc.cc000066400000000000000000003006241476623553700215300ustar00rootroot00000000000000//# tTableGramFunc.cc: This program tests TaQL functions //# Copyright (C) 2018 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include // // Test program for all TaQL functions. // They are tested in two ways: // - The correct way where it is checked if the result is correct and has the // correct data type and unit. // - Various incorrect ways where it is checked if the expected exception is thrown. // The functions are called for scalars and arrays and combinations where applicable. // At the end it prints the total number of tests done. // int ntest=0; int checkScaBool (const String& func, const String& arg, Bool expResult) { ntest++; String comm = "using style python calc " + func + '(' + arg + ')'; try { TaQLResult result = tableCommand (comm); TableExprNode node = result.node(); if (node.dataType() != TpBool) { cout << "Bool error in evaluating: " + comm << endl; cout << " expected data type Bool, found " << ValType::getTypeStr(node.dataType()) << endl; return 1; } if (node.getBool(0) != expResult || node.unit().getName() != "") { cout << "Bool error in evaluating: " + comm << endl; cout << " expected " << expResult << endl; cout << " found " << node.getBool(0) << ' ' << node.unit().getName() << endl; return 1; } } catch (const std::exception& x) { cout << x.what() << endl; cout << " Unexpected exception in: " << comm << endl; return 1; } return 0; } int checkScaInt (const String& func, const String& arg, Int expResult) { ntest++; String comm = "using style python calc " + func + '(' + arg + ')'; try { TaQLResult result = tableCommand (comm); TableExprNode node = result.node(); if (node.dataType() != TpInt64) { cout << "Int error in evaluating: " + comm << endl; cout << " expected data type Int, found " << ValType::getTypeStr(node.dataType()) << endl; return 1; } if (node.getInt(0) != expResult || node.unit().getName() != "") { cout << "Int error in evaluating: " + comm << endl; cout << " expected " << expResult << endl; cout << " found " << node.getInt(0) << ' ' << node.unit().getName() << endl; return 1; } } catch (const std::exception& x) { cout << x.what() << endl; cout << " Unexpected exception in: " << comm << endl; return 1; } return 0; } int checkScaDouble (const String& func, const String& arg, Double expResult, const String& unit = String(), double tol=1e-5) { ntest++; String comm = "using style python calc " + func + '(' + arg + ')'; try { TaQLResult result = tableCommand (comm); TableExprNode node = result.node(); if (node.dataType() != TpDouble) { cout << "Double error in evaluating: " + comm << endl; cout << " expected data type Double, found " << ValType::getTypeStr(node.dataType()) << endl; return 1; } if (!near(node.getDouble(0), expResult, tol) || node.unit().getName() != unit) { cout << "Double error in evaluating: " + comm << endl; cout << " expected " << expResult << endl; cout << " found " << node.getDouble(0) << ' ' << node.unit().getName() << endl; return 1; } } catch (const std::exception& x) { cout << x.what() << endl; cout << " Unexpected exception in: " << comm << endl; return 1; } return 0; } int checkScaDComplex (const String& func, const String& arg, DComplex expResult) { ntest++; String comm = "using style python calc " + func + '(' + arg + ')'; try { TaQLResult result = tableCommand (comm); TableExprNode node = result.node(); if (node.dataType() != TpDComplex) { cout << "DComplex error in evaluating: " + comm << endl; cout << " expected data type DComplex, found " << ValType::getTypeStr(node.dataType()) << endl; return 1; } if (!near(node.getDComplex(0), expResult) || node.unit().getName() != "") { cout << "DComplex error in evaluating: " + comm << endl; cout << " expected " << expResult << endl; cout << " found " << node.getDComplex(0) << ' ' << node.unit().getName() << endl; return 1; } } catch (const std::exception& x) { cout << x.what() << endl; cout << " Unexpected exception in: " << comm << endl; return 1; } return 0; } int checkScaDateTime (const String& func, const String& arg, MVTime expResult) { ntest++; String comm = "using style python calc " + func + '(' + arg + ')'; try { TaQLResult result = tableCommand (comm); TableExprNode node = result.node(); if (node.dataType() != TpQuantity) { cout << "DateTime error in evaluating: " + comm << endl; cout << " expected data type Quantity, found " << ValType::getTypeStr(node.dataType()) << endl; return 1; } if (!(node.getDate(0) == expResult) || node.unit().getName() != "") { MVTime::setFormat (MVTime::YMD); cout << "DateTime error in evaluating: " + comm << endl; cout << " expected " << expResult << endl; cout << " found " << node.getDate(0) << ' ' << node.unit().getName() << endl; return 1; } } catch (const std::exception& x) { cout << x.what() << endl; cout << " Unexpected exception in: " << comm << endl; return 1; } return 0; } int checkScaString (const String& func, const String& arg, String expResult) { ntest++; String comm = "using style python calc " + func + '(' + arg + ')'; try { TaQLResult result = tableCommand (comm); TableExprNode node = result.node(); if (node.dataType() != TpString) { cout << "String error in evaluating: " + comm << endl; cout << " expected data type String, found " << ValType::getTypeStr(node.dataType()) << endl; return 1; } if (node.getString(0) != expResult || node.unit().getName() != "") { cout << "String error in evaluating: " + comm << endl; cout << " expected " << expResult << endl; cout << " found " << node.getString(0) << ' ' << node.unit().getName() << endl; return 1; } } catch (const std::exception& x) { cout << x.what() << endl; cout << " Unexpected exception in: " << comm << endl; return 1; } return 0; } int checkArrBool (const String& func, const String& arg, const String& expResult) { ntest++; String comm1 = "using style python calc " + func + '(' + arg + ')'; String comm2 = "using style python calc " + expResult; try { TaQLResult result1 = tableCommand (comm1); TableExprNode node1 = result1.node(); TaQLResult result2 = tableCommand (comm2); TableExprNode node2 = result2.node(); if (node1.dataType() != TpBool) { cout << "Bool Array error in evaluating: " + comm1 << endl; cout << " expected data type Bool, found " << ValType::getTypeStr(node1.dataType()) << endl; return 1; } if (! allEQ (node1.getArrayBool(0), node2.getArrayBool(0))) { cout << "Bool Array error in evaluating: " + comm1 << endl; cout << " expected " << node2.getArrayBool(0) << endl; cout << " found " << node1.getArrayBool(0) << endl; return 1; } } catch (const std::exception& x) { cout << x.what() << endl; cout << " Unexpected exception in: " << comm1 << endl; return 1; } return 0; } int checkArrInt (const String& func, const String& arg, const String& expResult) { ntest++; String comm1 = "using style python calc " + func + '(' + arg + ')'; String comm2 = "using style python calc " + expResult; try { TaQLResult result1 = tableCommand (comm1); TableExprNode node1 = result1.node(); TaQLResult result2 = tableCommand (comm2); TableExprNode node2 = result2.node(); if (node1.dataType() != TpInt64) { cout << "Int Array error in evaluating: " + comm1 << endl; cout << " expected data type Int, found " << ValType::getTypeStr(node1.dataType()) << endl; return 1; } if (! allEQ (node1.getArrayInt(0), node2.getArrayInt(0))) { cout << "Int Array error in evaluating: " + comm1 << endl; cout << " expected " << node2.getArrayInt(0) << endl; cout << " found " << node1.getArrayInt(0) << endl; return 1; } } catch (const std::exception& x) { cout << x.what() << endl; cout << " Unexpected exception in: " << comm1 << endl; return 1; } return 0; } int checkArrDouble (const String& func, const String& arg, const String& expResult) { ntest++; String comm1 = "using style python calc " + func + '(' + arg + ')'; String comm2 = "using style python calc " + expResult; try { TaQLResult result1 = tableCommand (comm1); TableExprNode node1 = result1.node(); TaQLResult result2 = tableCommand (comm2); TableExprNode node2 = result2.node(); if (node1.dataType() != TpDouble) { cout << "Double Array error in evaluating: " + comm1 << endl; cout << " expected data type Double, found " << ValType::getTypeStr(node1.dataType()) << endl; return 1; } if (! allNear (node1.getArrayDouble(0), node2.getArrayDouble(0), 1e-5)) { cout << "Double Array error in evaluating: " + comm1 << endl; cout << " expected " << node2.getArrayDouble(0) << endl; cout << " found " << node1.getArrayDouble(0) << endl; return 1; } } catch (const std::exception& x) { cout << x.what() << endl; cout << " Unexpected exception in: " << comm1 << endl; return 1; } return 0; } int checkArrDComplex (const String& func, const String& arg, const String& expResult) { ntest++; String comm1 = "using style python calc " + func + '(' + arg + ')'; String comm2 = "using style python calc " + expResult; try { TaQLResult result1 = tableCommand (comm1); TableExprNode node1 = result1.node(); TaQLResult result2 = tableCommand (comm2); TableExprNode node2 = result2.node(); if (node1.dataType() != TpDComplex) { cout << "DComplex Array error in evaluating: " + comm1 << endl; cout << " expected data type DComplex, found " << ValType::getTypeStr(node1.dataType()) << endl; return 1; } if (! allNear (node1.getArrayDComplex(0), node2.getArrayDComplex(0), 1e-10)) { cout << "DComplex Array error in evaluating: " + comm1 << endl; cout << " expected " << node2.getArrayDComplex(0) << endl; cout << " found " << node1.getArrayDComplex(0) << endl; return 1; } } catch (const std::exception& x) { cout << x.what() << endl; cout << " Unexpected exception in: " << comm1 << endl; return 1; } return 0; } int checkArrString (const String& func, const String& arg, const String& expResult) { ntest++; String comm1 = "using style python calc " + func + '(' + arg + ')'; String comm2 = "using style python calc " + expResult; try { TaQLResult result1 = tableCommand (comm1); TableExprNode node1 = result1.node(); TaQLResult result2 = tableCommand (comm2); TableExprNode node2 = result2.node(); if (node1.dataType() != TpString) { cout << "String Array error in evaluating: " + comm1 << endl; cout << " expected data type String, found " << ValType::getTypeStr(node1.dataType()) << endl; return 1; } if (! allEQ (node1.getArrayString(0), node2.getArrayString(0))) { cout << "String Array error in evaluating: " + comm1 << endl; cout << " expected " << node2.getArrayString(0) << endl; cout << " found " << node1.getArrayString(0) << endl; return 1; } } catch (const std::exception& x) { cout << x.what() << endl; cout << " Unexpected exception in: " << comm1 << endl; return 1; } return 0; } int checkArrDouble1 (const String& func, const String& arg1, const String& arg2) { // Check an array function by comparing with its scalar counterpart. // E.g., cos([1,2]) and [cos(1),cos(2)] return checkArrDouble (func, '[' + arg1 + ',' + arg2 + ']', '[' + func + '(' + arg1 + ")," + func + '(' + arg2 + ")]"); } int checkArrDComplex1 (const String& func, const String& arg1, const String& arg2) { return checkArrDComplex (func, '[' + arg1 + ',' + arg2 + ']', '[' + func + '(' + arg1 + ")," + func + '(' + arg2 + ")]"); } int checkArrInt2 (const String& func, const String& arg1, const String& arg2, const String& arg3) { // Check an array function with 2 arguments by comparing with its scalar counterpart. int nfail = 0; // arr-arr nfail += checkArrInt (func, '[' + arg1 + ',' + arg2 + "],[" + arg2 + ',' + arg3 + ']', '[' + func + '(' + arg1 + ',' + arg2 + ")," + func + '(' + arg2 + ',' + arg3 + ")]"); // sca-arr nfail += checkArrInt (func, arg1 + ",[" + arg2 + ',' + arg3 + ']', '[' + func + '(' + arg1 + ',' + arg2 + ")," + func + '(' + arg1 + ',' + arg3 + ")]"); // arr-sca nfail += checkArrInt (func, '[' + arg1 + ',' + arg2 + "]," + arg3, '[' + func + '(' + arg1 + ',' + arg3 + ")," + func + '(' + arg2 + ',' + arg3 + ")]"); return nfail; } int checkArrDouble2 (const String& func, const String& arg1, const String& arg2, const String& arg3) { // Check an array function with 2 arguments by comparing with its scalar counterpart. int nfail = 0; // arr-arr nfail += checkArrDouble (func, '[' + arg1 + ',' + arg2 + "],[" + arg2 + ',' + arg3 + ']', '[' + func + '(' + arg1 + ',' + arg2 + ")," + func + '(' + arg2 + ',' + arg3 + ")]"); // sca-arr nfail += checkArrDouble (func, arg1 + ",[" + arg2 + ',' + arg3 + ']', '[' + func + '(' + arg1 + ',' + arg2 + ")," + func + '(' + arg1 + ',' + arg3 + ")]"); // arr-sca nfail += checkArrDouble (func, '[' + arg1 + ',' + arg2 + "]," + arg3, '[' + func + '(' + arg1 + ',' + arg3 + ")," + func + '(' + arg2 + ',' + arg3 + ")]"); return nfail; } int checkExcp (const String& func, const String& arg, const String& msgPart=String()) { ntest++; String comm = "using style python calc " + func + '(' + arg + ')'; try { tableCommand (comm); } catch (const std::exception& x) { if (msgPart.empty()) { cout << x.what() << endl; } else if (String(x.what()).find (msgPart) == String::npos) { cout << x.what() << endl; cout << " Expected another exception in: " << comm << endl; return 1; } return 0; } cout << "Expected exception in: " << comm << endl; return 1; } int testScaBool() { cout<<" testing scalar Bool functions ..."< shape 3,1,2 nfail += checkArrInt ("areverse", "[[1,2,3],[4,5,6],[7,8,9]]", "[[9,8,7],[6,5,4],[3,2,1]]"); nfail += checkArrInt ("areverse", "[[1,2,3],[4,5,6],[7,8,9]],0,1", "[[9,8,7],[6,5,4],[3,2,1]]"); nfail += checkArrInt ("areverse", "[[1,2,3],[4,5,6],[7,8,9]], [1]", "[[3,2,1],[6,5,4],[9,8,7]]"); nfail += checkArrInt ("areverse", "[[1,2,3],[4,5,6],[7,8,9]], 0", "[[7,8,9],[4,5,6],[1,2,3]]"); nfail += checkArrInt ("resize", "[[1,2,3],[4,5,6]], [3,2]", "[[1,2],[4,5],[0,0]]"); nfail += checkArrInt ("resize", "[[1,2,3],[4,5,6]], [2,6], 0", "[[1,1,2,2,3,3],[4,4,5,5,6,6]]"); nfail += checkArrInt ("resize", "[[1,2,3],[4,5,6]], [2,6], 1", "[[1,2,3,1,2,3],[4,5,6,4,5,6]]"); nfail += checkArrInt ("sums", "[[1,2,3],[4,5,6]], 1", "[6,15]"); nfail += checkArrInt ("sums", "[[1,2,3],[4,5,6]], 0", "[5,7,9]"); nfail += checkArrInt ("products", "[[1,2,3],[4,5,6]], 0", "[4,10,18]"); nfail += checkArrInt ("sumsqrs", "[[1,2,3],[4,5,6]], 0", "[17,29,45]"); nfail += checkArrInt ("mins", "[[[1,2,3],[4,5,6]]], 1", "[[1,2,3]]"); nfail += checkArrInt ("maxs", "[[[1,2,3],[4,5,6]]], 0", "[[1,2,3],[4,5,6]]"); nfail += checkArrInt ("shape", "[1,2]", "[2]"); nfail += checkArrInt ("shape", "[[date(),date()]]", "[1,2]"); return nfail; } int testArrDouble() { cout<<" testing array Double functions ..."<1)", "[F,T]"); nfail += checkArrInt ("flatten", "marray([[1,2,3],[4,5,6]],[[T,F,F],[F,T,F]])", "[2,3,4,6]"); nfail += checkArrInt ("flatten", "[[1,2,3],[4,5,6]][[[T,F,F],[F,T,F]]]", "[2,3,4,6]"); nfail += checkArrBool ("arraymask", "negatemask(marray([1,2],[1,2]>1))", "[T,F]"); nfail += checkArrInt ("arraydata", "replacemasked([[1,2,3],[4,5,6]][[[T,F,F],[F,T,F]]], 10)", "[[10,2,3],[4,10,6]]"); nfail += checkArrInt ("arraydata", "replacemasked([[1,2,3],[4,5,6]][[[T,F,F],[F,T,F]]], [[-1,-2,-3],[-4,-5,-6]])", "[[-1,2,3],[4,-5,6]]"); nfail += checkArrInt ("arraydata", "replaceunmasked([[1,2,3],[4,5,6]][[[T,F,F],[F,T,F]]], 10)", "[[1,10,10],[10,5,10]]"); nfail += checkArrInt ("arraydata", "replaceunmasked([[1,2,3],[4,5,6]][[[T,F,F],[F,T,F]]], [[-1,-2,-3],[-4,-5,-6]])", "[[1,-2,-3],[-4,5,-6]]"); // Check without a mask. nfail += checkArrInt ("arraydata", "[[1,2,3],[4,5,6]]", "[[1,2,3],[4,5,6]]"); nfail += checkArrBool ("arraymask", "[1,2]", "[F,F]"); nfail += checkArrBool ("arraymask", "negatemask([1,2])", "[T,T]"); nfail += checkArrBool ("arraymask", "negatemask(2)", "[T]"); nfail += checkArrInt ("flatten", "[[1,2,3],[4,5,6]]", "[1,2,3,4,5,6]"); nfail += checkArrInt ("arraydata", "replacemasked([[1,2,3],[4,5,6]], 10)", "[[1,2,3],[4,5,6]]"); nfail += checkArrInt ("arraydata", "replacemasked([[1,2,3],[4,5,6]], [[-1,-2,-3],[-4,-5,-6]])", "[[1,2,3],[4,5,6]]"); nfail += checkArrInt ("arraydata", "replaceunmasked([[1,2,3],[4,5,6]], 10)", "[[10,10,10],[10,10,10]]"); nfail += checkArrInt ("arraydata", "replaceunmasked([[1,2,3],[4,5,6]], [[-1,-2,-3],[-4,-5,-6]])", "[[-1,-2,-3],[-4,-5,-6]]"); // Check with a scalar argument. nfail += checkArrInt ("arraydata", "1", "[1]"); nfail += checkArrInt ("arraydata", "marray(1, True)", "[1]"); nfail += checkArrBool ("arraymask", "marray(1, True)", "[T]"); nfail += checkArrBool ("arraymask", "1", "[F]"); nfail += checkArrBool ("arraymask", "negatemask(1)", "[T]"); nfail += checkArrInt ("arraydata", "replacemasked(1, 2)", "[1]"); nfail += checkArrInt ("arraydata", "replaceunmasked(1, 2)", "[2]"); nfail += checkArrInt ("flatten", "1", "[1]"); return nfail; } int testLessArg() { cout<<" testing functions with too few arguments ..."<2 groupby ab has been executed select result of 7 rows 1 selected columns: ab 3 4 5 6 7 8 9 select ab from tTableGramGroupAggr_tmp.tab where ab>2 groupby ab having ab<8 has been executed select result of 5 rows 1 selected columns: ab 3 4 5 6 7 select ab from tTableGramGroupAggr_tmp.tab groupby ab//2 has been executed select result of 5 rows 1 selected columns: ab 1 3 5 7 9 select gsum(ab) from tTableGramGroupAggr_tmp.tab has been executed select result of 1 rows 1 selected columns: Col_1 45 select gsum(ab), gfirst(ab), glast(ab) from tTableGramGroupAggr_tmp.tab groupby ab//2 has been executed select result of 5 rows 3 selected columns: Col_1 Col_2 Col_3 1 0 1 5 2 3 9 4 5 13 6 7 17 8 9 select gsum(ab), gfirst(ab), ab from tTableGramGroupAggr_tmp.tab groupby ab//2 has been executed select result of 5 rows 3 selected columns: Col_1 Col_2 ab 1 0 1 5 2 3 9 4 5 13 6 7 17 8 9 select gsum(ab), gfirst(ab), ab from tTableGramGroupAggr_tmp.tab groupby ab//2 having ab>3 has been executed select result of 3 rows 3 selected columns: Col_1 Col_2 ab 9 4 5 13 6 7 17 8 9 select gsum(ab) as SAB, gfirst(ab) as FAB, ab from tTableGramGroupAggr_tmp.tab groupby ab//2 having (SAB-1)%8==0 orderby desc FAB has been executed select result of 3 rows 3 selected columns: SAB FAB ab 17 8 9 9 4 5 1 0 1 select gsum(arr1) from tTableGramGroupAggr_tmp.tab has been executed select result of 1 rows 1 selected columns: Col_1 28680 select gsum(arr1) from tTableGramGroupAggr_tmp.tab groupby sum(arr1) has been executed select result of 10 rows 1 selected columns: Col_1 276 852 1428 2004 2580 3156 3732 4308 4884 5460 select gmedian(arr1) from tTableGramGroupAggr_tmp.tab groupby sum(arr1)//3 has been executed select result of 10 rows 1 selected columns: Col_1 11 35 59 83 107 131 155 179 203 227 select gsum(arr1), gmean(arr1), gmedian(arr2) as MED, gfractile(arr3,0.5) from tTableGramGroupAggr_tmp.tab groupby sum(arr1)//1000 has been executed select result of 6 rows 4 selected columns: Col_1 Col_2 MED Col_4 1128 23.5 23 23 1428 59.5 59 59 4584 95.5 95 95 6888 143.5 143 143 9192 191.5 191 191 5460 227.5 227 227 select gaggr(arr1), sum(gaggr(arr1)), sum(growid()) from tTableGramGroupAggr_tmp.tab has been executed select result of 1 rows 3 selected columns: Col_1 Col_2 Col_3 shape=[2, 3, 4, 10] 28680 45 select sum(gaggr(arr1)), sum(growid()) from tTableGramGroupAggr_tmp.tab groupby ab has been executed select result of 10 rows 2 selected columns: Col_1 Col_2 276 0 852 1 1428 2 2004 3 2580 4 3156 5 3732 6 4308 7 4884 8 5460 9 select sum(gaggr(arr1)), sum(growid()) from tTableGramGroupAggr_tmp.tab groupby ab//4 has been executed select result of 3 rows 2 selected columns: Col_1 Col_2 4560 6 13776 22 10344 17 select gaggr(arr1), sum(gaggr(arr1)), sum(growid()) from tTableGramGroupAggr_tmp.tab where ab between 1 and 8 has been executed select result of 1 rows 3 selected columns: Col_1 Col_2 Col_3 shape=[2, 3, 4, 8] 22944 36 select sum(gaggr(arr1)), sum(growid()) from tTableGramGroupAggr_tmp.tab where ab between 1 and 8 groupby ab has been executed select result of 8 rows 2 selected columns: Col_1 Col_2 852 1 1428 2 2004 3 2580 4 3156 5 3732 6 4308 7 4884 8 select sum(gaggr(arr1)), sum(growid()) from tTableGramGroupAggr_tmp.tab where ab between 1 and 8 groupby ab//4 has been executed select result of 3 rows 2 selected columns: Col_1 Col_2 4284 6 13776 22 4884 8 select gaggr(arr1), sum(gaggr(arr1)), sum(growid()) from tTableGramGroupAggr_tmp.tab having all(shape(gaggr(ab)) = [10]) has been executed select result of 1 rows 3 selected columns: Col_1 Col_2 Col_3 shape=[2, 3, 4, 10] 28680 45 select gsum(ab) from tTableGramGroupAggr_tmp.tab having gsum(ab)>0 has been executed select result of 1 rows 1 selected columns: Col_1 45 select gsum(arr1), gmedian(arr2) as MED, gfractile(arr3,0.5), ab from tTableGramGroupAggr_tmp.tab where ab>1 and ac<10 groupby ab having MED%48==11 and ab>=0 orderby desc MED%96, ab asc has been executed select result of 4 rows 4 selected columns: Col_1 MED Col_3 ab 1428 59 59 2 3732 155 155 6 2580 107 107 4 4884 203 203 8 select gmin(ab) + gsum(ag) from tTableGramGroupAggr_tmp.tab has been executed select result of 1 rows 1 selected columns: Col_1 (65,0) select gmin(ae) + gmax(ab) from tTableGramGroupAggr_tmp.tab has been executed select result of 1 rows 1 selected columns: Col_1 12 select gsum(ag) + gmax(ae) from tTableGramGroupAggr_tmp.tab has been executed select result of 1 rows 1 selected columns: Col_1 (77,0) casacore-3.7.1/tables/TaQL/test/tTableGramGroupAggr.run000066400000000000000000000143411476623553700227470ustar00rootroot00000000000000#!/bin/sh #----------------------------------------------------------------------------- # Script to test the GROUPBY and aggregate functionality of # TableGram and TableParse class. All files generated will # be deleted on exit. #============================================================================= # If the first argument given to the script is 1, it will only create the # tTableGramGroupAggr_tmp.tab tables and not execute the tTableGram commands. # Set default testsrcdir if undefined. if test "$testsrcdir" = ""; then testsrcdir=../../../../../casacore/tables/TaQL/test fi # Use table tTable_2.data_v0 as the input by copying it (twice). rm -rf tTableGramGroupAggr_tmp.tab* mkdir tTableGramGroupAggr_tmp.tab cp -r $testsrcdir/../../Tables/test/tTable_2.data_v0/table.* tTableGramGroupAggr_tmp.tab chmod 644 tTableGramGroupAggr_tmp.tab/* cp -r tTableGramGroupAggr_tmp.tab tTableGramGroupAggr_tmp.tabc if test "$1" = 1; then exit 0; fi # Whitespace around * is not allowed to avoid file name expansion by shell. # Execute all kind of groupby/aggregate commands. echo "testing groupby/aggregate ..." # Create a symlink to achieve that casacore_memcheck (if used) will use # that name rm -f tTableGramGroupAggr ln -s tTableGram tTableGramGroupAggr # Simple count of all rows. $casa_checktool ./tTableGramGroupAggr 'select gcount(*) from tTableGramGroupAggr_tmp.tab' # Test immediate aggregate. $casa_checktool ./tTableGramGroupAggr 'select gsum(ab) from tTableGramGroupAggr_tmp.tab' # Test lazy aggregate. $casa_checktool ./tTableGramGroupAggr 'select gaggr(ab) from tTableGramGroupAggr_tmp.tab' # Use a mix of it all. $casa_checktool ./tTableGramGroupAggr 'select ab, gfirst(ab), glast(ab), gsum(ab), gaggr(ab) from tTableGramGroupAggr_tmp.tab' # Count nr of rows per group (1 row per group). $casa_checktool ./tTableGramGroupAggr 'select gcount() from tTableGramGroupAggr_tmp.tab groupby ab,ac,ad,ae,af' # Use a non-aggregate column per group. $casa_checktool ./tTableGramGroupAggr 'select ab from tTableGramGroupAggr_tmp.tab groupby ab' # Use an immediate aggregate per group. $casa_checktool ./tTableGramGroupAggr 'select gsum(ab) from tTableGramGroupAggr_tmp.tab groupby ab' # Use a lazy aggregate per group. $casa_checktool ./tTableGramGroupAggr 'select gaggr(ab) from tTableGramGroupAggr_tmp.tab groupby ab' # Use a mix of it all. $casa_checktool ./tTableGramGroupAggr 'select ab, gfirst(ab), glast(ab), gsum(ab), gaggr(ab) from tTableGramGroupAggr_tmp.tab where ab%2 = 0 groupby ab//4' # Do the same for a selected subset. $casa_checktool ./tTableGramGroupAggr 'select ab from tTableGramGroupAggr_tmp.tab where ab>2 groupby ab' # Do the same and select the groups. $casa_checktool ./tTableGramGroupAggr 'select ab from tTableGramGroupAggr_tmp.tab where ab>2 groupby ab having ab<8' # Have multiple rows per group (non-aggregate uses the last value of a group). $casa_checktool ./tTableGramGroupAggr 'select ab from tTableGramGroupAggr_tmp.tab groupby ab//2' # Aggregate all rows. $casa_checktool ./tTableGramGroupAggr 'select gsum(ab) from tTableGramGroupAggr_tmp.tab' # Use aggregates for multiple groups. $casa_checktool ./tTableGramGroupAggr 'select gsum(ab), gfirst(ab), glast(ab) from tTableGramGroupAggr_tmp.tab groupby ab//2' # Combine aggregate and non-aggregate per group. $casa_checktool ./tTableGramGroupAggr 'select gsum(ab), gfirst(ab), ab from tTableGramGroupAggr_tmp.tab groupby ab//2' # Similar with a selection on group. $casa_checktool ./tTableGramGroupAggr 'select gsum(ab), gfirst(ab), ab from tTableGramGroupAggr_tmp.tab groupby ab//2 having ab>3' # Use SELECT columns in the other clauses. $casa_checktool ./tTableGramGroupAggr 'select gsum(ab) as SAB, gfirst(ab) as FAB, ab from tTableGramGroupAggr_tmp.tab groupby ab//2 having (SAB-1)%8==0 orderby desc FAB' # Do aggregation of arrays. $casa_checktool ./tTableGramGroupAggr 'select gsum(arr1) from tTableGramGroupAggr_tmp.tab' # Use a function in groupby. $casa_checktool ./tTableGramGroupAggr 'select gsum(arr1) from tTableGramGroupAggr_tmp.tab groupby sum(arr1)' # Use a lazy aggregate function. $casa_checktool ./tTableGramGroupAggr 'select gmedian(arr1) from tTableGramGroupAggr_tmp.tab groupby sum(arr1)//3' # Use immediate and lazy aggregate functions $casa_checktool ./tTableGramGroupAggr 'select gsum(arr1), gmean(arr1), gmedian(arr2) as MED, gfractile(arr3,0.5) from tTableGramGroupAggr_tmp.tab groupby sum(arr1)//1000' # Use aggregate that combines all arrays and gets row numbers. $casa_checktool ./tTableGramGroupAggr 'select gaggr(arr1), sum(gaggr(arr1)), sum(growid()) from tTableGramGroupAggr_tmp.tab' $casa_checktool ./tTableGramGroupAggr 'select sum(gaggr(arr1)), sum(growid()) from tTableGramGroupAggr_tmp.tab groupby ab' $casa_checktool ./tTableGramGroupAggr 'select sum(gaggr(arr1)), sum(growid()) from tTableGramGroupAggr_tmp.tab groupby ab//4' $casa_checktool ./tTableGramGroupAggr 'select gaggr(arr1), sum(gaggr(arr1)), sum(growid()) from tTableGramGroupAggr_tmp.tab where ab between 1 and 8' $casa_checktool ./tTableGramGroupAggr 'select sum(gaggr(arr1)), sum(growid()) from tTableGramGroupAggr_tmp.tab where ab between 1 and 8 groupby ab' $casa_checktool ./tTableGramGroupAggr 'select sum(gaggr(arr1)), sum(growid()) from tTableGramGroupAggr_tmp.tab where ab between 1 and 8 groupby ab//4' $casa_checktool ./tTableGramGroupAggr 'select gaggr(arr1), sum(gaggr(arr1)), sum(growid()) from tTableGramGroupAggr_tmp.tab having all(shape(gaggr(ab)) = [10])' # Use an aggregate in a HAVING. $casa_checktool ./tTableGramGroupAggr 'select gsum(ab) from tTableGramGroupAggr_tmp.tab having gsum(ab)>0' # Use a mix of referred and non-referred columns, lazy and immediate aggregates # in all clauses. $casa_checktool ./tTableGramGroupAggr 'select gsum(arr1), gmedian(arr2) as MED, gfractile(arr3,0.5), ab from tTableGramGroupAggr_tmp.tab where ab>1 and ac<10 groupby ab having MED%48==11 and ab>=0 orderby desc MED%96, ab asc' # Test mixed aggregate arithmetic (ab=Int, ag=DComplex, ae=Float) $casa_checktool ./tTableGramGroupAggr "select gmin(ab) + gsum(ag) from tTableGramGroupAggr_tmp.tab" $casa_checktool ./tTableGramGroupAggr "select gmin(ae) + gmax(ab) from tTableGramGroupAggr_tmp.tab" $casa_checktool ./tTableGramGroupAggr "select gsum(ag) + gmax(ae) from tTableGramGroupAggr_tmp.tab" # Remove the symlink rm -f tTableGramGroupAggr casacore-3.7.1/tables/TaQL/test/tTableGramGroupAggrAll.out000066400000000000000000000625541476623553700234140ustar00rootroot00000000000000*** Testing aggregate functions for all data types ... select min(gaggr(ab)) as A, gmin(ab) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select max(gaggr(ab)) as A, gmax(ab) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select sum(gaggr(ab)) as A, gsum(ab) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select product(gaggr(ab)) as A, gproduct(ab) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select sumsqr(gaggr(ab)) as A, gsumsqr(ab) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select mean(gaggr(ab)) as A, gmean(ab) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select variance(gaggr(ab)) as A, gvariance(ab) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select samplevariance(gaggr(ab)) as A, gsamplevariance(ab) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select stddev(gaggr(ab)) as A, gstddev(ab) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select samplestddev(gaggr(ab)) as A, gsamplestddev(ab) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select rms(gaggr(ab)) as A, grms(ab) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select fractile(gaggr(ab), 0.4) as A, gfractile(ab,0.4) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select min(gaggr(ag)) as A, gmin(ag) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B Caught an exception: Error in TaQL command: 'select min(gaggr(ag)) as A, gmin(ag) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B' Error in select expression: Erroneous use of function min - invalid operand data type; function argument is not real select max(gaggr(ag)) as A, gmax(ag) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B Caught an exception: Error in TaQL command: 'select max(gaggr(ag)) as A, gmax(ag) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B' Error in select expression: Erroneous use of function max - invalid operand data type; function argument is not real select sum(gaggr(ag)) as A, gsum(ag) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select product(gaggr(ag)) as A, gproduct(ag) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select sumsqr(gaggr(ag)) as A, gsumsqr(ag) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select mean(gaggr(ag)) as A, gmean(ag) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select variance(gaggr(ag)) as A, gvariance(ag) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select samplevariance(gaggr(ag)) as A, gsamplevariance(ag) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select stddev(gaggr(ag)) as A, gstddev(ag) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select samplestddev(gaggr(ag)) as A, gsamplestddev(ag) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select rms(gaggr(ag)) as A, grms(ag) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B Caught an exception: Error in TaQL command: 'select rms(gaggr(ag)) as A, grms(ag) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B' Error in select expression: Erroneous use of function rms - invalid operand data type; function argument is not real select fractile(gaggr(ag), 0.4) as A, gfractile(ag,0.4) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B Caught an exception: Error in TaQL command: 'select fractile(gaggr(ag), 0.4) as A, gfractile(ag,0.4) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B' Error in select expression: Erroneous use of function fractile - invalid operand data type; function argument is not real select min(gaggr(ae)) as A, gmin(ae) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select max(gaggr(ae)) as A, gmax(ae) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select sum(gaggr(ae)) as A, gsum(ae) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select product(gaggr(ae)) as A, gproduct(ae) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select sumsqr(gaggr(ae)) as A, gsumsqr(ae) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select mean(gaggr(ae)) as A, gmean(ae) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select variance(gaggr(ae)) as A, gvariance(ae) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select samplevariance(gaggr(ae)) as A, gsamplevariance(ae) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select stddev(gaggr(ae)) as A, gstddev(ae) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select samplestddev(gaggr(ae)) as A, gsamplestddev(ae) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select rms(gaggr(ae)) as A, grms(ae) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select fractile(gaggr(ae), 0.4) as A, gfractile(ae,0.4) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select min(gaggr(arr1)) as A, gmin(arr1) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select max(gaggr(arr1)) as A, gmax(arr1) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select sum(gaggr(arr1)) as A, gsum(arr1) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select product(gaggr(arr1)) as A, gproduct(arr1) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select sumsqr(gaggr(arr1)) as A, gsumsqr(arr1) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select mean(gaggr(arr1)) as A, gmean(arr1) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select variance(gaggr(arr1)) as A, gvariance(arr1) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select samplevariance(gaggr(arr1)) as A, gsamplevariance(arr1) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select stddev(gaggr(arr1)) as A, gstddev(arr1) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select samplestddev(gaggr(arr1)) as A, gsamplestddev(arr1) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select rms(gaggr(arr1)) as A, grms(arr1) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select fractile(gaggr(arr1), 0.4) as A, gfractile(arr1,0.4) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select min(gaggr(int(arr1))) as A, gmin(int(arr1)) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select max(gaggr(int(arr1))) as A, gmax(int(arr1)) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select sum(gaggr(int(arr1))) as A, gsum(int(arr1)) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select product(gaggr(int(arr1))) as A, gproduct(int(arr1)) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select sumsqr(gaggr(int(arr1))) as A, gsumsqr(int(arr1)) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select mean(gaggr(int(arr1))) as A, gmean(int(arr1)) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select variance(gaggr(int(arr1))) as A, gvariance(int(arr1)) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select samplevariance(gaggr(int(arr1))) as A, gsamplevariance(int(arr1)) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select stddev(gaggr(int(arr1))) as A, gstddev(int(arr1)) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select samplestddev(gaggr(int(arr1))) as A, gsamplestddev(int(arr1)) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select rms(gaggr(int(arr1))) as A, grms(int(arr1)) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select fractile(gaggr(int(arr1)), 0.4) as A, gfractile(int(arr1),0.4) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select min(gaggr(complex(arr1/10.,rownumber()/100.))) as A, gmin(complex(arr1/10.,rownumber()/100.)) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B Caught an exception: Error in TaQL command: 'select min(gaggr(complex(arr1/10.,rownumber()/100.))) as A, gmin(complex(arr1/10.,rownumber()/100.)) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B' Error in select expression: Erroneous use of function min - invalid operand data type; function argument is not real select max(gaggr(complex(arr1/10.,rownumber()/100.))) as A, gmax(complex(arr1/10.,rownumber()/100.)) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B Caught an exception: Error in TaQL command: 'select max(gaggr(complex(arr1/10.,rownumber()/100.))) as A, gmax(complex(arr1/10.,rownumber()/100.)) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B' Error in select expression: Erroneous use of function max - invalid operand data type; function argument is not real select sum(gaggr(complex(arr1/10.,rownumber()/100.))) as A, gsum(complex(arr1/10.,rownumber()/100.)) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select product(gaggr(complex(arr1/10.,rownumber()/100.))) as A, gproduct(complex(arr1/10.,rownumber()/100.)) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select sumsqr(gaggr(complex(arr1/10.,rownumber()/100.))) as A, gsumsqr(complex(arr1/10.,rownumber()/100.)) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select mean(gaggr(complex(arr1/10.,rownumber()/100.))) as A, gmean(complex(arr1/10.,rownumber()/100.)) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select variance(gaggr(complex(arr1/10.,rownumber()/100.))) as A, gvariance(complex(arr1/10.,rownumber()/100.)) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select samplevariance(gaggr(complex(arr1/10.,rownumber()/100.))) as A, gsamplevariance(complex(arr1/10.,rownumber()/100.)) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select stddev(gaggr(complex(arr1/10.,rownumber()/100.))) as A, gstddev(complex(arr1/10.,rownumber()/100.)) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select samplestddev(gaggr(complex(arr1/10.,rownumber()/100.))) as A, gsamplestddev(complex(arr1/10.,rownumber()/100.)) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select rms(gaggr(complex(arr1/10.,rownumber()/100.))) as A, grms(complex(arr1/10.,rownumber()/100.)) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B Caught an exception: Error in TaQL command: 'select rms(gaggr(complex(arr1/10.,rownumber()/100.))) as A, grms(complex(arr1/10.,rownumber()/100.)) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B' Error in select expression: Erroneous use of function rms - invalid operand data type; function argument is not real select fractile(gaggr(complex(arr1/10.,rownumber()/100.)), 0.4) as A, gfractile(complex(arr1/10.,rownumber()/100.),0.4) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B Caught an exception: Error in TaQL command: 'select fractile(gaggr(complex(arr1/10.,rownumber()/100.)), 0.4) as A, gfractile(complex(arr1/10.,rownumber()/100.),0.4) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B' Error in select expression: Erroneous use of function fractile - invalid operand data type; function argument is not real using style python select mins(gaggr(arr1),0) as A, gmins(arr1) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select maxs(gaggr(arr1),0) as A, gmaxs(arr1) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select sums(gaggr(arr1),0) as A, gsums(arr1) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select products(gaggr(arr1),0) as A, gproducts(arr1) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select sumsqrs(gaggr(arr1),0) as A, gsumsqrs(arr1) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select means(gaggr(arr1),0) as A, gmeans(arr1) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select variances(gaggr(arr1),0) as A, gvariances(arr1) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select samplevariances(gaggr(arr1),0) as A, gsamplevariances(arr1) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select stddevs(gaggr(arr1),0) as A, gstddevs(arr1) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select samplestddevs(gaggr(arr1),0) as A, gsamplestddevs(arr1) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select rmss(gaggr(arr1),0) as A, grmss(arr1) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select mins(gaggr(int(arr1)),0) as A, gmins(int(arr1)) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select maxs(gaggr(int(arr1)),0) as A, gmaxs(int(arr1)) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select sums(gaggr(int(arr1)),0) as A, gsums(int(arr1)) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select products(gaggr(int(arr1)),0) as A, gproducts(int(arr1)) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select sumsqrs(gaggr(int(arr1)),0) as A, gsumsqrs(int(arr1)) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select means(gaggr(int(arr1)),0) as A, gmeans(int(arr1)) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select variances(gaggr(int(arr1)),0) as A, gvariances(int(arr1)) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select samplevariances(gaggr(int(arr1)),0) as A, gsamplevariances(int(arr1)) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select stddevs(gaggr(int(arr1)),0) as A, gstddevs(int(arr1)) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select samplestddevs(gaggr(int(arr1)),0) as A, gsamplestddevs(int(arr1)) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select rmss(gaggr(int(arr1)),0) as A, grmss(int(arr1)) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select mins(gaggr(complex(arr1/10.,rownumber()/100.)),0) as A, gmins(complex(arr1/10.,rownumber()/100.)) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) Caught an exception: Error in TaQL command: 'using style python select mins(gaggr(complex(arr1/10.,rownumber()/100.)),0) as A, gmins(complex(arr1/10.,rownumber()/100.)) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B)' Error in select expression: Erroneous use of function mins - invalid operand data type; function argument is not real using style python select maxs(gaggr(complex(arr1/10.,rownumber()/100.)),0) as A, gmaxs(complex(arr1/10.,rownumber()/100.)) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) Caught an exception: Error in TaQL command: 'using style python select maxs(gaggr(complex(arr1/10.,rownumber()/100.)),0) as A, gmaxs(complex(arr1/10.,rownumber()/100.)) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B)' Error in select expression: Erroneous use of function maxs - invalid operand data type; function argument is not real using style python select sums(gaggr(complex(arr1/10.,rownumber()/100.)),0) as A, gsums(complex(arr1/10.,rownumber()/100.)) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select products(gaggr(complex(arr1/10.,rownumber()/100.)),0) as A, gproducts(complex(arr1/10.,rownumber()/100.)) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select sumsqrs(gaggr(complex(arr1/10.,rownumber()/100.)),0) as A, gsumsqrs(complex(arr1/10.,rownumber()/100.)) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select means(gaggr(complex(arr1/10.,rownumber()/100.)),0) as A, gmeans(complex(arr1/10.,rownumber()/100.)) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select variances(gaggr(complex(arr1/10.,rownumber()/100.)),0) as A, gvariances(complex(arr1/10.,rownumber()/100.)) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select samplevariances(gaggr(complex(arr1/10.,rownumber()/100.)),0) as A, gsamplevariances(complex(arr1/10.,rownumber()/100.)) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select stddevs(gaggr(complex(arr1/10.,rownumber()/100.)),0) as A, gstddevs(complex(arr1/10.,rownumber()/100.)) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select samplestddevs(gaggr(complex(arr1/10.,rownumber()/100.)),0) as A, gsamplestddevs(complex(arr1/10.,rownumber()/100.)) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select rmss(gaggr(complex(arr1/10.,rownumber()/100.)),0) as A, grmss(complex(arr1/10.,rownumber()/100.)) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B) Caught an exception: Error in TaQL command: 'using style python select rmss(gaggr(complex(arr1/10.,rownumber()/100.)),0) as A, grmss(complex(arr1/10.,rownumber()/100.)) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B)' Error in select expression: Erroneous use of function rmss - invalid operand data type; function argument is not real select any(gaggr(ab>5)) as A, gany(ab>5) as B from tTableGramGroupAggrAll_tmp.tab having A!=B has been executed select result of 0 rows 2 selected columns: A B select all(gaggr(ab>5)) as A, gall(ab>5) as B from tTableGramGroupAggrAll_tmp.tab having A!=B has been executed select result of 0 rows 2 selected columns: A B select ntrue(gaggr(ab>5)) as A, gntrue(ab>5) as B from tTableGramGroupAggrAll_tmp.tab having A!=B has been executed select result of 0 rows 2 selected columns: A B select nfalse(gaggr(ab>5)) as A, gnfalse(ab>5) as B from tTableGramGroupAggrAll_tmp.tab having A!=B has been executed select result of 0 rows 2 selected columns: A B select any(gaggr(arr1>5)) as A, gany(arr1>5) as B from tTableGramGroupAggrAll_tmp.tab having A!=B has been executed select result of 0 rows 2 selected columns: A B select all(gaggr(arr1>5)) as A, gall(arr1>5) as B from tTableGramGroupAggrAll_tmp.tab having A!=B has been executed select result of 0 rows 2 selected columns: A B select ntrue(gaggr(arr1>5)) as A, gntrue(arr1>5) as B from tTableGramGroupAggrAll_tmp.tab having A!=B has been executed select result of 0 rows 2 selected columns: A B select nfalse(gaggr(arr1>5)) as A, gnfalse(arr1>5) as B from tTableGramGroupAggrAll_tmp.tab having A!=B has been executed select result of 0 rows 2 selected columns: A B using style python select anys(gaggr(arr1>5),0) as A, ganys(arr1>5) as B from tTableGramGroupAggrAll_tmp.tab having not all(A=B) has been executed select result of 0 rows 2 selected columns: A B using style python select alls(gaggr(arr1>5),0) as A, galls(arr1>5) as B from tTableGramGroupAggrAll_tmp.tab having not all(A=B) has been executed select result of 0 rows 2 selected columns: A B using style python select ntrues(gaggr(arr1>5),0) as A, gntrues(arr1>5) as B from tTableGramGroupAggrAll_tmp.tab having not all(A=B) has been executed select result of 0 rows 2 selected columns: A B using style python select nfalses(gaggr(arr1>5),0) as A, gnfalses(arr1>5) as B from tTableGramGroupAggrAll_tmp.tab having not all(A=B) has been executed select result of 0 rows 2 selected columns: A B casacore-3.7.1/tables/TaQL/test/tTableGramGroupAggrAll.run000066400000000000000000000054061476623553700234020ustar00rootroot00000000000000#!/bin/sh #----------------------------------------------------------------------------- # Script to test the GROUPBY and aggregate functionality of # TableGram and TableParse class. All files generated will # be deleted on exit. #============================================================================= # If the first argument given to the script is 1, it will only create the # tTableGramGroupAggrAll_tmp.tab tables and not execute the tTableGram commands. # Set default testsrcdir if undefined. if test "$testsrcdir" = ""; then testsrcdir=../../../../../casacore/tables/TaQL/test fi # Use table tTable_2.data_v0 as the input by copying it (twice). rm -rf tTableGramGroupAggrAll_tmp.tab* mkdir tTableGramGroupAggrAll_tmp.tab cp -r $testsrcdir/../../Tables/test/tTable_2.data_v0/table.* tTableGramGroupAggrAll_tmp.tab chmod 644 tTableGramGroupAggrAll_tmp.tab/* cp -r tTableGramGroupAggrAll_tmp.tab tTableGramGroupAggrAll_tmp.tabc if test "$1" = 1; then exit 0; fi # Create a symlink to achieve that casacore_memcheck (if used) will use # that name rm -f tTableGramGroupAggrAll ln -s tTableGram tTableGramGroupAggrAll # Test all aggregate functions for all possible data and value types. # Some will give an 'invalid data type' exception. # Note that $casa_checktool is not used for valgrind because it takes long. # This could be changed in the future. echo "*** Testing aggregate functions for all data types ..." # Testing numeric ones. for COL in ab ag ae arr1 'int(arr1)' 'complex(arr1/10.,rownumber()/100.)' do # Check gsum against sum, etc. for FUNC in min max sum product sumsqr mean variance samplevariance stddev samplestddev rms do ./tTableGramGroupAggrAll "select $FUNC(gaggr($COL)) as A, g$FUNC($COL) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B" done ./tTableGramGroupAggrAll "select fractile(gaggr($COL), 0.4) as A, gfractile($COL,0.4) as B from tTableGramGroupAggrAll_tmp.tab having A!~=B" done # Check gsums against sums, etc. for COL in arr1 'int(arr1)' 'complex(arr1/10.,rownumber()/100.)' do for FUNC in min max sum product sumsqr mean variance samplevariance stddev samplestddev rms do ./tTableGramGroupAggrAll "using style python select ${FUNC}s(gaggr($COL),0) as A, g${FUNC}s($COL) as B from tTableGramGroupAggrAll_tmp.tab having not all(A~=B)" done done # Testing boolean ones. for COL in ab arr1 do for FUNC in any all ntrue nfalse do ./tTableGramGroupAggrAll "select $FUNC(gaggr($COL>5)) as A, g$FUNC($COL>5) as B from tTableGramGroupAggrAll_tmp.tab having A!=B" done done for FUNC in any all ntrue nfalse do ./tTableGramGroupAggrAll "using style python select ${FUNC}s(gaggr(arr1>5),0) as A, g${FUNC}s(arr1>5) as B from tTableGramGroupAggrAll_tmp.tab having not all(A=B)" done # Remove the symlink rm -f tTableGramGroupAggrAll casacore-3.7.1/tables/TaQL/test/tTableGramGroupAggrMaskAll.out000066400000000000000000000455531476623553700242300ustar00rootroot00000000000000*** Testing aggregate functions for all data types ... select min(gaggr(arr1[arr1%3!=0])) as A, gmin(arr1[arr1%3!=0]) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select max(gaggr(arr1[arr1%3!=0])) as A, gmax(arr1[arr1%3!=0]) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select sum(gaggr(arr1[arr1%3!=0])) as A, gsum(arr1[arr1%3!=0]) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select product(gaggr(arr1[arr1%3!=0])) as A, gproduct(arr1[arr1%3!=0]) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select sumsqr(gaggr(arr1[arr1%3!=0])) as A, gsumsqr(arr1[arr1%3!=0]) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select mean(gaggr(arr1[arr1%3!=0])) as A, gmean(arr1[arr1%3!=0]) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select variance(gaggr(arr1[arr1%3!=0])) as A, gvariance(arr1[arr1%3!=0]) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select samplevariance(gaggr(arr1[arr1%3!=0])) as A, gsamplevariance(arr1[arr1%3!=0]) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select stddev(gaggr(arr1[arr1%3!=0])) as A, gstddev(arr1[arr1%3!=0]) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select samplestddev(gaggr(arr1[arr1%3!=0])) as A, gsamplestddev(arr1[arr1%3!=0]) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select rms(gaggr(arr1[arr1%3!=0])) as A, grms(arr1[arr1%3!=0]) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select fractile(gaggr(arr1[arr1%3!=0]), 0.4) as A, gfractile(arr1[arr1%3!=0],0.4) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select min(gaggr(int(arr1[arr1%2=0]))) as A, gmin(int(arr1[arr1%2=0])) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select max(gaggr(int(arr1[arr1%2=0]))) as A, gmax(int(arr1[arr1%2=0])) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select sum(gaggr(int(arr1[arr1%2=0]))) as A, gsum(int(arr1[arr1%2=0])) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select product(gaggr(int(arr1[arr1%2=0]))) as A, gproduct(int(arr1[arr1%2=0])) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select sumsqr(gaggr(int(arr1[arr1%2=0]))) as A, gsumsqr(int(arr1[arr1%2=0])) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select mean(gaggr(int(arr1[arr1%2=0]))) as A, gmean(int(arr1[arr1%2=0])) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select variance(gaggr(int(arr1[arr1%2=0]))) as A, gvariance(int(arr1[arr1%2=0])) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select samplevariance(gaggr(int(arr1[arr1%2=0]))) as A, gsamplevariance(int(arr1[arr1%2=0])) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select stddev(gaggr(int(arr1[arr1%2=0]))) as A, gstddev(int(arr1[arr1%2=0])) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select samplestddev(gaggr(int(arr1[arr1%2=0]))) as A, gsamplestddev(int(arr1[arr1%2=0])) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select rms(gaggr(int(arr1[arr1%2=0]))) as A, grms(int(arr1[arr1%2=0])) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select fractile(gaggr(int(arr1[arr1%2=0])), 0.4) as A, gfractile(int(arr1[arr1%2=0]),0.4) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select min(gaggr(complex(arr1[arr1%100=50],0))) as A, gmin(complex(arr1[arr1%100=50],0)) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B Caught an exception: Error in TaQL command: 'select min(gaggr(complex(arr1[arr1%100=50],0))) as A, gmin(complex(arr1[arr1%100=50],0)) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B' Error in select expression: Erroneous use of function min - invalid operand data type; function argument is not real select max(gaggr(complex(arr1[arr1%100=50],0))) as A, gmax(complex(arr1[arr1%100=50],0)) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B Caught an exception: Error in TaQL command: 'select max(gaggr(complex(arr1[arr1%100=50],0))) as A, gmax(complex(arr1[arr1%100=50],0)) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B' Error in select expression: Erroneous use of function max - invalid operand data type; function argument is not real select sum(gaggr(complex(arr1[arr1%100=50],0))) as A, gsum(complex(arr1[arr1%100=50],0)) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select product(gaggr(complex(arr1[arr1%100=50],0))) as A, gproduct(complex(arr1[arr1%100=50],0)) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select sumsqr(gaggr(complex(arr1[arr1%100=50],0))) as A, gsumsqr(complex(arr1[arr1%100=50],0)) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select mean(gaggr(complex(arr1[arr1%100=50],0))) as A, gmean(complex(arr1[arr1%100=50],0)) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select variance(gaggr(complex(arr1[arr1%100=50],0))) as A, gvariance(complex(arr1[arr1%100=50],0)) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select samplevariance(gaggr(complex(arr1[arr1%100=50],0))) as A, gsamplevariance(complex(arr1[arr1%100=50],0)) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select stddev(gaggr(complex(arr1[arr1%100=50],0))) as A, gstddev(complex(arr1[arr1%100=50],0)) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select samplestddev(gaggr(complex(arr1[arr1%100=50],0))) as A, gsamplestddev(complex(arr1[arr1%100=50],0)) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B has been executed select result of 0 rows 2 selected columns: A B select rms(gaggr(complex(arr1[arr1%100=50],0))) as A, grms(complex(arr1[arr1%100=50],0)) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B Caught an exception: Error in TaQL command: 'select rms(gaggr(complex(arr1[arr1%100=50],0))) as A, grms(complex(arr1[arr1%100=50],0)) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B' Error in select expression: Erroneous use of function rms - invalid operand data type; function argument is not real select fractile(gaggr(complex(arr1[arr1%100=50],0)), 0.4) as A, gfractile(complex(arr1[arr1%100=50],0),0.4) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B Caught an exception: Error in TaQL command: 'select fractile(gaggr(complex(arr1[arr1%100=50],0)), 0.4) as A, gfractile(complex(arr1[arr1%100=50],0),0.4) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B' Error in select expression: Erroneous use of function fractile - invalid operand data type; function argument is not real using style python select mins(gaggr(arr1[arr1%3!=0]),0) as A, gmins(arr1[arr1%3!=0]) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select maxs(gaggr(arr1[arr1%3!=0]),0) as A, gmaxs(arr1[arr1%3!=0]) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select sums(gaggr(arr1[arr1%3!=0]),0) as A, gsums(arr1[arr1%3!=0]) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select products(gaggr(arr1[arr1%3!=0]),0) as A, gproducts(arr1[arr1%3!=0]) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select sumsqrs(gaggr(arr1[arr1%3!=0]),0) as A, gsumsqrs(arr1[arr1%3!=0]) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select means(gaggr(arr1[arr1%3!=0]),0) as A, gmeans(arr1[arr1%3!=0]) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select variances(gaggr(arr1[arr1%3!=0]),0) as A, gvariances(arr1[arr1%3!=0]) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select samplevariances(gaggr(arr1[arr1%3!=0]),0) as A, gsamplevariances(arr1[arr1%3!=0]) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select stddevs(gaggr(arr1[arr1%3!=0]),0) as A, gstddevs(arr1[arr1%3!=0]) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select samplestddevs(gaggr(arr1[arr1%3!=0]),0) as A, gsamplestddevs(arr1[arr1%3!=0]) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select rmss(gaggr(arr1[arr1%3!=0]),0) as A, grmss(arr1[arr1%3!=0]) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select mins(gaggr(int(arr1[arr1%2=0])),0) as A, gmins(int(arr1[arr1%2=0])) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select maxs(gaggr(int(arr1[arr1%2=0])),0) as A, gmaxs(int(arr1[arr1%2=0])) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select sums(gaggr(int(arr1[arr1%2=0])),0) as A, gsums(int(arr1[arr1%2=0])) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select products(gaggr(int(arr1[arr1%2=0])),0) as A, gproducts(int(arr1[arr1%2=0])) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select sumsqrs(gaggr(int(arr1[arr1%2=0])),0) as A, gsumsqrs(int(arr1[arr1%2=0])) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select means(gaggr(int(arr1[arr1%2=0])),0) as A, gmeans(int(arr1[arr1%2=0])) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select variances(gaggr(int(arr1[arr1%2=0])),0) as A, gvariances(int(arr1[arr1%2=0])) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select samplevariances(gaggr(int(arr1[arr1%2=0])),0) as A, gsamplevariances(int(arr1[arr1%2=0])) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select stddevs(gaggr(int(arr1[arr1%2=0])),0) as A, gstddevs(int(arr1[arr1%2=0])) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select samplestddevs(gaggr(int(arr1[arr1%2=0])),0) as A, gsamplestddevs(int(arr1[arr1%2=0])) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select rmss(gaggr(int(arr1[arr1%2=0])),0) as A, grmss(int(arr1[arr1%2=0])) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select mins(gaggr(complex(arr1[arr1%100=50],0)),0) as A, gmins(complex(arr1[arr1%100=50],0)) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B) Caught an exception: Error in TaQL command: 'using style python select mins(gaggr(complex(arr1[arr1%100=50],0)),0) as A, gmins(complex(arr1[arr1%100=50],0)) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B)' Error in select expression: Erroneous use of function mins - invalid operand data type; function argument is not real using style python select maxs(gaggr(complex(arr1[arr1%100=50],0)),0) as A, gmaxs(complex(arr1[arr1%100=50],0)) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B) Caught an exception: Error in TaQL command: 'using style python select maxs(gaggr(complex(arr1[arr1%100=50],0)),0) as A, gmaxs(complex(arr1[arr1%100=50],0)) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B)' Error in select expression: Erroneous use of function maxs - invalid operand data type; function argument is not real using style python select sums(gaggr(complex(arr1[arr1%100=50],0)),0) as A, gsums(complex(arr1[arr1%100=50],0)) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select products(gaggr(complex(arr1[arr1%100=50],0)),0) as A, gproducts(complex(arr1[arr1%100=50],0)) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select sumsqrs(gaggr(complex(arr1[arr1%100=50],0)),0) as A, gsumsqrs(complex(arr1[arr1%100=50],0)) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select means(gaggr(complex(arr1[arr1%100=50],0)),0) as A, gmeans(complex(arr1[arr1%100=50],0)) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select variances(gaggr(complex(arr1[arr1%100=50],0)),0) as A, gvariances(complex(arr1[arr1%100=50],0)) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select samplevariances(gaggr(complex(arr1[arr1%100=50],0)),0) as A, gsamplevariances(complex(arr1[arr1%100=50],0)) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select stddevs(gaggr(complex(arr1[arr1%100=50],0)),0) as A, gstddevs(complex(arr1[arr1%100=50],0)) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select samplestddevs(gaggr(complex(arr1[arr1%100=50],0)),0) as A, gsamplestddevs(complex(arr1[arr1%100=50],0)) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B) has been executed select result of 0 rows 2 selected columns: A B using style python select rmss(gaggr(complex(arr1[arr1%100=50],0)),0) as A, grmss(complex(arr1[arr1%100=50],0)) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B) Caught an exception: Error in TaQL command: 'using style python select rmss(gaggr(complex(arr1[arr1%100=50],0)),0) as A, grmss(complex(arr1[arr1%100=50],0)) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B)' Error in select expression: Erroneous use of function rmss - invalid operand data type; function argument is not real select any(gaggr(arr1[arr1%3!=0]>5)) as A, gany(arr1[arr1%3!=0]>5) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!=B has been executed select result of 0 rows 2 selected columns: A B select all(gaggr(arr1[arr1%3!=0]>5)) as A, gall(arr1[arr1%3!=0]>5) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!=B has been executed select result of 0 rows 2 selected columns: A B select ntrue(gaggr(arr1[arr1%3!=0]>5)) as A, gntrue(arr1[arr1%3!=0]>5) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!=B has been executed select result of 0 rows 2 selected columns: A B select nfalse(gaggr(arr1[arr1%3!=0]>5)) as A, gnfalse(arr1[arr1%3!=0]>5) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!=B has been executed select result of 0 rows 2 selected columns: A B using style python select anys(gaggr(arr1>5),0) as A, ganys(arr1>5) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A=B) has been executed select result of 0 rows 2 selected columns: A B using style python select alls(gaggr(arr1>5),0) as A, galls(arr1>5) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A=B) has been executed select result of 0 rows 2 selected columns: A B using style python select ntrues(gaggr(arr1>5),0) as A, gntrues(arr1>5) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A=B) has been executed select result of 0 rows 2 selected columns: A B using style python select nfalses(gaggr(arr1>5),0) as A, gnfalses(arr1>5) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A=B) has been executed select result of 0 rows 2 selected columns: A B casacore-3.7.1/tables/TaQL/test/tTableGramGroupAggrMaskAll.run000066400000000000000000000056541476623553700242230ustar00rootroot00000000000000#!/bin/sh #----------------------------------------------------------------------------- # Script to test the GROUPBY and masked aggregate functionality of # TableGram and TableParse class. All files generated will # be deleted on exit. #============================================================================= # If the first argument given to the script is 1, it will only create the # tTableGramGroupAggrMaskAll_tmp.tab tables and not execute the tTableGram commands. # Set default testsrcdir if undefined. if test "$testsrcdir" = ""; then testsrcdir=../../../../../casacore/tables/TaQL/test fi # Use table tTable_2.data_v0 as the input by copying it (twice). rm -rf tTableGramGroupAggrMaskAll_tmp.tab* mkdir tTableGramGroupAggrMaskAll_tmp.tab cp -r $testsrcdir/../../Tables/test/tTable_2.data_v0/table.* tTableGramGroupAggrMaskAll_tmp.tab chmod 644 tTableGramGroupAggrMaskAll_tmp.tab/* cp -r tTableGramGroupAggrMaskAll_tmp.tab tTableGramGroupAggrMaskAll_tmp.tabc if test "$1" = 1; then exit 0; fi # Create a symlink to achieve that casacore_memcheck (if used) will use # that name rm -f tTableGramGroupAggrMaskAll ln -s tTableGram tTableGramGroupAggrMaskAll # Test all aggregate functions for all possible data and value types. # Some will give an 'invalid data type' exception. # Note that $casa_checktool is not used for valgrind because it takes long. # This could be changed in the future. echo "*** Testing aggregate functions for all data types ..." # Testing numeric ones. for COL in 'arr1[arr1%3!=0]' 'int(arr1[arr1%2=0])' 'complex(arr1[arr1%100=50],0)' do # Check gsum against sum, etc. for FUNC in min max sum product sumsqr mean variance samplevariance stddev samplestddev rms do ./tTableGramGroupAggrMaskAll "select $FUNC(gaggr($COL)) as A, g$FUNC($COL) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B" done ./tTableGramGroupAggrMaskAll "select fractile(gaggr($COL), 0.4) as A, gfractile($COL,0.4) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!~=B" done # Check gsums against sums, etc. for COL in 'arr1[arr1%3!=0]' 'int(arr1[arr1%2=0])' 'complex(arr1[arr1%100=50],0)' do for FUNC in min max sum product sumsqr mean variance samplevariance stddev samplestddev rms do ./tTableGramGroupAggrMaskAll "using style python select ${FUNC}s(gaggr($COL),0) as A, g${FUNC}s($COL) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A~=B)" done done # Testing boolean ones. for COL in 'arr1[arr1%3!=0]' do for FUNC in any all ntrue nfalse do ./tTableGramGroupAggrMaskAll "select $FUNC(gaggr($COL>5)) as A, g$FUNC($COL>5) as B from tTableGramGroupAggrMaskAll_tmp.tab having A!=B" done done for COL in 'arr1[arr1%3!=0]' do for FUNC in any all ntrue nfalse do ./tTableGramGroupAggrMaskAll "using style python select ${FUNC}s(gaggr(arr1>5),0) as A, g${FUNC}s(arr1>5) as B from tTableGramGroupAggrMaskAll_tmp.tab having not all(A=B)" done done # Remove the symlink rm -f tTableGramGroupAggrMaskAll casacore-3.7.1/tables/TaQL/test/tTableGramJoin.out000066400000000000000000000241661476623553700217620ustar00rootroot00000000000000Creating the test tables ... update result of 20 rows update result of 5 rows select result of 5 rows 3 selected columns: ci2 cd2 cd Unit: d 0 4 1 0 5 2 1 4 3 1 5 4 2 4 5 testing joins ... SELECT t1.cmi, t1.cmi2, t1.cmd, t2.ci, t2.cd, t2.cd2 FROM tTableGramJoin_tmp.tab t1 JOIN ::SUB t2 ON t1.cmi=msid(t2.ci) has been executed select result of 20 rows 6 selected columns: cmi cmi2 cmd ci cd cd2 0 0 4.3 0 1 4 1 1 5.3 1 2 5 2 2 4.3 2 3 4 3 0 5.3 3 4 5 4 1 4.3 4 5 4 5 2 5.3 9.22337e+18 nan nan 0 0 4.3 0 1 4 1 1 5.3 1 2 5 2 2 4.3 2 3 4 3 0 5.3 3 4 5 4 1 4.3 4 5 4 5 2 5.3 9.22337e+18 nan nan 0 0 4.3 0 1 4 1 1 5.3 1 2 5 2 2 4.3 2 3 4 3 0 5.3 3 4 5 4 1 4.3 4 5 4 5 2 5.3 9.22337e+18 nan nan 0 0 4.3 0 1 4 1 1 5.3 1 2 5 SELECT t1.cmi, t1.cmi2, t1.cmd, t2.ci, t2.cd, t2.cd2, msid(cmixx), t2.msid(ci), t2.rowid() FROM tTableGramJoin_tmp.tab t1 JOIN ::SUB t2 ON t1.cmi=t2.rowid()+1 has been executed select result of 20 rows 9 selected columns: cmi cmi2 cmd ci cd cd2 Col_7 Col_8 Col_9 0 0 4.3 9.22337e+18 nan nan 0 9.22337e+18 -1 1 1 5.3 0 1 4 1 0 0 2 2 4.3 1 2 5 2 1 1 3 0 5.3 2 3 4 3 2 2 4 1 4.3 3 4 5 4 3 3 5 2 5.3 4 5 4 5 4 4 0 0 4.3 9.22337e+18 nan nan 6 9.22337e+18 -1 1 1 5.3 0 1 4 7 0 0 2 2 4.3 1 2 5 8 1 1 3 0 5.3 2 3 4 9 2 2 4 1 4.3 3 4 5 10 3 3 5 2 5.3 4 5 4 11 4 4 0 0 4.3 9.22337e+18 nan nan 12 9.22337e+18 -1 1 1 5.3 0 1 4 13 0 0 2 2 4.3 1 2 5 14 1 1 3 0 5.3 2 3 4 15 2 2 4 1 4.3 3 4 5 16 3 3 5 2 5.3 4 5 4 17 4 4 0 0 4.3 9.22337e+18 nan nan 18 9.22337e+18 -1 1 1 5.3 0 1 4 19 0 0 SELECT t1.cmi, t1.cmi2, t1.cmd, t2.ci, t2.cd, t2.cd2, msid(cmixx), msid(t2.ci), t2.rowid()+1 FROM tTableGramJoin_tmp.tab t1 JOIN ::SUB t2 ON t1.cmi=t2.rowid()+1 where t2.ci%5 > 2 has been executed select result of 6 rows 9 selected columns: cmi cmi2 cmd ci cd cd2 Col_7 Col_8 Col_9 4 1 4.3 3 4 5 4 3 4 5 2 5.3 4 5 4 5 4 5 4 1 4.3 3 4 5 10 3 4 5 2 5.3 4 5 4 11 4 5 4 1 4.3 3 4 5 16 3 4 5 2 5.3 4 5 4 17 4 5 SELECT t1.cmi, t1.cmi2, t1.cmd, t2.ci, t2.cd, t2.cd2 FROM tTableGramJoin_tmp.tab t1 JOIN ::SUB t2 ON t1.cmi=msid(t2.cirow) has been executed select result of 20 rows 6 selected columns: cmi cmi2 cmd ci cd cd2 0 0 4.3 0 1 4 1 1 5.3 1 2 5 2 2 4.3 2 3 4 3 0 5.3 3 4 5 4 1 4.3 4 5 4 5 2 5.3 9.22337e+18 nan nan 0 0 4.3 0 1 4 1 1 5.3 1 2 5 2 2 4.3 2 3 4 3 0 5.3 3 4 5 4 1 4.3 4 5 4 5 2 5.3 9.22337e+18 nan nan 0 0 4.3 0 1 4 1 1 5.3 1 2 5 2 2 4.3 2 3 4 3 0 5.3 3 4 5 4 1 4.3 4 5 4 5 2 5.3 9.22337e+18 nan nan 0 0 4.3 0 1 4 1 1 5.3 1 2 5 SELECT t1.cmi, gsum(t2.ci), gsum(t2.cd) FROM tTableGramJoin_tmp.tab t1 JOIN ::SUB t2 ON t1.cmi=msid(t2.ci) GROUPBY t2.ci has been executed select result of 6 rows 3 selected columns: cmi Col_2 Col_3 0 0 4 1 4 8 2 6 9 3 9 12 4 12 15 5 9.22337e+18 nan SELECT t1.cmi2, t1.cmd, t2.ci2, t2.cd2, t2.cd FROM tTableGramJoin_tmp.tab t1 JOIN ::SUB t2 ON t1.cmd around t2.cd2 in t2.cw has been executed select result of 20 rows 5 selected columns: cmi2 cmd ci2 cd2 cd 0 4.3 0 4 1 1 5.3 0 5 2 2 4.3 0 4 1 0 5.3 0 5 2 1 4.3 0 4 1 2 5.3 0 5 2 0 4.3 0 4 1 1 5.3 0 5 2 2 4.3 0 4 1 0 5.3 0 5 2 1 4.3 0 4 1 2 5.3 0 5 2 0 4.3 0 4 1 1 5.3 0 5 2 2 4.3 0 4 1 0 5.3 0 5 2 1 4.3 0 4 1 2 5.3 0 5 2 0 4.3 0 4 1 1 5.3 0 5 2 SELECT t1.cmi2, t1.cmd, t2.ci2, t2.cd2, t2.cd FROM tTableGramJoin_tmp.tab t1 JOIN ::SUB t2 ON t1.cmi2=t2.ci2 && t1.cmd around t2.cd2 in t2.cw has been executed select result of 20 rows 5 selected columns: cmi2 cmd ci2 cd2 cd 0 4.3 0 4 1 1 5.3 1 5 4 2 4.3 2 4 5 0 5.3 0 5 2 1 4.3 1 4 3 2 5.3 9.22337e+18 nan nan 0 4.3 0 4 1 1 5.3 1 5 4 2 4.3 2 4 5 0 5.3 0 5 2 1 4.3 1 4 3 2 5.3 9.22337e+18 nan nan 0 4.3 0 4 1 1 5.3 1 5 4 2 4.3 2 4 5 0 5.3 0 5 2 1 4.3 1 4 3 2 5.3 9.22337e+18 nan nan 0 4.3 0 4 1 1 5.3 1 5 4 SELECT t1.cmi, t1.cmi2, t1.cmd, t2.ci, t2.cd, t2.cd2 FROM tTableGramJoin_tmp.tab t1 JOIN ::SUB t2 ON t1.cmi=t2.ci and t2.ci=t1.cmi has been executed select result of 20 rows 6 selected columns: cmi cmi2 cmd ci cd cd2 0 0 4.3 0 1 4 1 1 5.3 1 2 5 2 2 4.3 2 3 4 3 0 5.3 3 4 5 4 1 4.3 4 5 4 5 2 5.3 9.22337e+18 nan nan 0 0 4.3 0 1 4 1 1 5.3 1 2 5 2 2 4.3 2 3 4 3 0 5.3 3 4 5 4 1 4.3 4 5 4 5 2 5.3 9.22337e+18 nan nan 0 0 4.3 0 1 4 1 1 5.3 1 2 5 2 2 4.3 2 3 4 3 0 5.3 3 4 5 4 1 4.3 4 5 4 5 2 5.3 9.22337e+18 nan nan 0 0 4.3 0 1 4 1 1 5.3 1 2 5 SELECT t1.cmi2, t1.cmd, t2.ci2, t2.cd2, t2.cd, t2.cad FROM tTableGramJoin_tmp.tab t1 JOIN ::SUB t2 ON t1.cmd around t2.cd2 in t2.cw and t1.cmd around t2.cd2 in t2.cw has been executed select result of 20 rows 6 selected columns: cmi2 cmd ci2 cd2 cd cad 0 4.3 0 4 1 shape=[2, 3] 1 5.3 0 5 2 shape=[2, 3] 2 4.3 0 4 1 shape=[2, 3] 0 5.3 0 5 2 shape=[2, 3] 1 4.3 0 4 1 shape=[2, 3] 2 5.3 0 5 2 shape=[2, 3] 0 4.3 0 4 1 shape=[2, 3] 1 5.3 0 5 2 shape=[2, 3] 2 4.3 0 4 1 shape=[2, 3] 0 5.3 0 5 2 shape=[2, 3] 1 4.3 0 4 1 shape=[2, 3] 2 5.3 0 5 2 shape=[2, 3] 0 4.3 0 4 1 shape=[2, 3] 1 5.3 0 5 2 shape=[2, 3] 2 4.3 0 4 1 shape=[2, 3] 0 5.3 0 5 2 shape=[2, 3] 1 4.3 0 4 1 shape=[2, 3] 2 5.3 0 5 2 shape=[2, 3] 0 4.3 0 4 1 shape=[2, 3] 1 5.3 0 5 2 shape=[2, 3] Updating the tables for nested joins ... update result of 9 rows update result of 9 rows select result of 9 rows 2 selected columns: spw pol 0 0 1 0 2 0 0 1 1 1 2 1 0 2 1 2 2 2 select result of 9 rows 4 selected columns: spw tod w ci Unit: d s 0 02-Nov-2022/12:00:00.000 86400 0 1 02-Nov-2022/12:00:00.000 86400 1 2 02-Nov-2022/12:00:00.000 86400 2 0 03-Nov-2022/12:00:00.000 86400 3 1 03-Nov-2022/12:00:00.000 86400 4 2 03-Nov-2022/12:00:00.000 86400 5 0 04-Nov-2022/12:00:00.000 86400 6 1 04-Nov-2022/12:00:00.000 86400 7 2 04-Nov-2022/12:00:00.000 86400 8 select result of 20 rows 5 selected columns: cmi cmi2 cmd cdd ct Unit: d 0 0 4.3 0 02-Nov-2022/12:00:00.000 1 1 5.3 1 02-Nov-2022/12:00:00.000 2 2 4.3 2 02-Nov-2022/12:00:00.000 3 0 5.3 3 02-Nov-2022/12:00:00.000 4 1 4.3 4 02-Nov-2022/12:00:00.000 5 2 5.3 5 02-Nov-2022/12:00:00.000 0 0 4.3 6 02-Nov-2022/12:00:00.000 1 1 5.3 7 03-Nov-2022/12:00:00.000 2 2 4.3 8 03-Nov-2022/12:00:00.000 3 0 5.3 0 03-Nov-2022/12:00:00.000 4 1 4.3 1 03-Nov-2022/12:00:00.000 5 2 5.3 2 03-Nov-2022/12:00:00.000 0 0 4.3 3 03-Nov-2022/12:00:00.000 1 1 5.3 4 03-Nov-2022/12:00:00.000 2 2 4.3 5 04-Nov-2022/12:00:00.000 3 0 5.3 6 04-Nov-2022/12:00:00.000 4 1 4.3 7 04-Nov-2022/12:00:00.000 5 2 5.3 8 04-Nov-2022/12:00:00.000 0 0 4.3 0 04-Nov-2022/12:00:00.000 1 1 5.3 1 04-Nov-2022/12:00:00.000 SELECT t1.rowid() as r1, t2.rowid() as r2, t3.rowid() as r3, t2.pol, t3.ci FROM tTableGramJoin_tmp.tab t1 JOIN ::DD t2 ON t1.cdd=t2.rowid() JOIN ::SC t3 ON t2.spw=t3.spw AND t1.ct AROUND t3.tod IN t3.w has been executed select result of 20 rows 5 selected columns: r1 r2 r3 pol ci 0 0 0 0 0 1 1 1 0 1 2 2 2 0 2 3 3 0 1 0 4 4 1 1 1 5 5 2 1 2 6 6 0 2 0 7 7 4 2 4 8 8 5 2 5 9 0 3 0 3 10 1 4 0 4 11 2 5 0 5 12 3 3 1 3 13 4 4 1 4 14 5 8 1 8 15 6 6 2 6 16 7 7 2 7 17 8 8 2 8 18 0 6 0 6 19 1 7 0 7 SELECT t1.rowid() as r1, t2.pol, t3.ci FROM tTableGramJoin_tmp.tab t1 JOIN ::DD t2 ON t1.cdd=t2.rowid() JOIN ::SC t3 ON t2.spw=t3.spw AND t1.ct AROUND t3.tod IN t3.w WHERE t3.ci%3!=0 has been executed select result of 13 rows 3 selected columns: r1 pol ci 1 0 1 2 0 2 4 1 1 5 1 2 7 2 4 8 2 5 10 0 4 11 0 5 13 1 4 14 1 8 16 2 7 17 2 8 19 0 7 Testing erroneous commands ... select from tTableGramJoin_tmp.tab t1 JOIN ::DD t2 on gsum(t1.cdd)=t2.rowid() Caught an exception: Error in TaQL command: 'select from tTableGramJoin_tmp.tab t1 JOIN ::DD t2 on gsum(t1.cdd)=t2.rowid()' Error in select expression: Aggregate functions cannot be used in a JOIN condition select from tTableGramJoin_tmp.tab t1 JOIN ::SUB t2, ::SC t3 on t1.cdd=t2.rowid()+t3.rowid() Caught an exception: Error in TaQL command: 'select from tTableGramJoin_tmp.tab t1 JOIN ::SUB t2, ::SC t3 on t1.cdd=t2.rowid()+t3.rowid()' Error in select expression: Join tables in a join must have the same nr of rows select from tTableGramJoin_tmp.tab t1 JOIN ::SUB t2 on t1.cmd=t2.ci Caught an exception: Error in TaQL command: 'select from tTableGramJoin_tmp.tab t1 JOIN ::SUB t2 on t1.cmd=t2.ci' Error in select expression: In a equality join condition only Int and String data types are possible select t2.rownr() from tTableGramJoin_tmp.tab t1 JOIN ::DD t2 on t1.cdd=t2.rowid() Caught an exception: Error in TaQL command: 'select t2.rownr() from tTableGramJoin_tmp.tab t1 JOIN ::DD t2 on t1.cdd=t2.rowid()' Error in select expression: Function rownr cannot be used on a join table select from tTableGramJoin_tmp.tab t1 JOIN ::DD t2 on t1.cdd=3 Caught an exception: Error in TaQL command: 'select from tTableGramJoin_tmp.tab t1 JOIN ::DD t2 on t1.cdd=3' Error in select expression: Join expressions cannot have constant comparison operands select from tTableGramJoin_tmp.tab t1 JOIN ::DD t2 on t1.cmi!=t2.rowid() Caught an exception: Error in TaQL command: 'select from tTableGramJoin_tmp.tab t1 JOIN ::DD t2 on t1.cmi!=t2.rowid()' Error in select expression: JOIN condition must be == or IN select from tTableGramJoin_tmp.tab t1 JOIN ::DD t2 on [t1.cmi,t1.cmi]=t2.rowid() Caught an exception: Error in TaQL command: 'select from tTableGramJoin_tmp.tab t1 JOIN ::DD t2 on [t1.cmi,t1.cmi]=t2.rowid()' Error in select expression: JOIN condition must be a scalar expression select from tTableGramJoin_tmp.tab t1 JOIN ::DD t2 on t2.rowid() in [t1.cmi,t1.cmi] Caught an exception: Error in TaQL command: 'select from tTableGramJoin_tmp.tab t1 JOIN ::DD t2 on t2.rowid() in [t1.cmi,t1.cmi]' Error in select expression: When using an IN condition in a join, the main tables must be on the left side select from tTableGramJoin_tmp.tab t1 JOIN ::DD t2 on t1.cmi+t2.rowid()=t2.rowid() Caught an exception: Error in TaQL command: 'select from tTableGramJoin_tmp.tab t1 JOIN ::DD t2 on t1.cmi+t2.rowid()=t2.rowid()' Error in select expression: Mix of from and join tables on one side of a join condition select from tTableGramJoin_tmp.tab t1 JOIN ::DD t2 on t1.cmi=t2.rowid()+t1.cmi Caught an exception: Error in TaQL command: 'select from tTableGramJoin_tmp.tab t1 JOIN ::DD t2 on t1.cmi=t2.rowid()+t1.cmi' Error in select expression: Mix of from and join tables on one side of a join condition casacore-3.7.1/tables/TaQL/test/tTableGramJoin.run000066400000000000000000000142111476623553700217450ustar00rootroot00000000000000#!/bin/sh #----------------------------------------------------------------------------- # Script to test the JOIN functionality of # TableGram and TableParse class. All files generated will # be deleted on exit. #============================================================================= # If the first argument given to the script is 1, it will only create the # tTableGramJoin_tmp.tab tables and not execute the tTableGram commands. # Set default testsrcdir if undefined. if test "$testsrcdir" = ""; then testsrcdir=../../../../../casacore/tables/TaQL/test fi # Create tables to do the join. echo "Creating the test tables ..." rm -rf tTableGramJoin_tmp.tab* ../../apps/taql "UPDATE [CREATE TABLE tTableGramJoin_tmp.tab [cmi int, cmi2 int, cmd double, cdd int, ct time] limit 20] set cmi=rowid()%6, cmi2=rowid()%3, cmd=rowid()%2+4.3, cdd=rowid()%9, ct=2nov2022/12h0m0+rowid()//7" ../../apps/taql "UPDATE [CREATE TABLE tTableGramJoin_tmp.tab::SUB [cb bool, ci int, ci2 int, cd double, cd2 double unit='d', cw double unit='s', cs string, ct time, cc complex, cab Bool shape=[3,2], cai int [shape=[3,2]], cad double [shape=[3,2]], cas string shape=[3,2]] limit 5] set ci=rowid(), ci2=rowid()//2, cai=[[ci,ci+1],[ci+2,ci+3],[ci+4,ci+5]], cb=ci%2=0, cd=ci+1, cs=string(ci+2,'x%dy'), ct=31Oct2022/12:0:0+ci, cd2=rowid()%2+4, cw=86400, cc=ci%2+ci%3*1i, cab=cai%3=1, cas=string(cai+3,'x%dyy')" ../../apps/taql "select ci2, cd2, cd from tTableGramJoin_tmp.tab::SUB" if test "$1" = 1; then exit 0; fi # Create a symlink to achieve that casacore_memcheck (if used) will use # that name rm -f tTableGramJoin ln -s tTableGram tTableGramJoin # Execute all kind of join commands. echo echo "testing joins ..." # Join on a single integer column. echo $casa_checktool ./tTableGramJoin "SELECT t1.cmi, t1.cmi2, t1.cmd, t2.ci, t2.cd, t2.cd2 FROM tTableGramJoin_tmp.tab t1 JOIN ::SUB t2 ON t1.cmi=msid(t2.ci)" # Join on rowid (column 'cirow' in msid does not exist) echo $casa_checktool ./tTableGramJoin "SELECT t1.cmi, t1.cmi2, t1.cmd, t2.ci, t2.cd, t2.cd2, msid(cmixx), t2.msid(ci), t2.rowid() FROM tTableGramJoin_tmp.tab t1 JOIN ::SUB t2 ON t1.cmi=t2.rowid()+1" echo $casa_checktool ./tTableGramJoin "SELECT t1.cmi, t1.cmi2, t1.cmd, t2.ci, t2.cd, t2.cd2, msid(cmixx), msid(t2.ci), t2.rowid()+1 FROM tTableGramJoin_tmp.tab t1 JOIN ::SUB t2 ON t1.cmi=t2.rowid()+1 where t2.ci%5 > 2" echo $casa_checktool ./tTableGramJoin "SELECT t1.cmi, t1.cmi2, t1.cmd, t2.ci, t2.cd, t2.cd2 FROM tTableGramJoin_tmp.tab t1 JOIN ::SUB t2 ON t1.cmi=msid(t2.cirow)" # Use aggregation and GROUPBY on a join column. echo $casa_checktool ./tTableGramJoin "SELECT t1.cmi, gsum(t2.ci), gsum(t2.cd) FROM tTableGramJoin_tmp.tab t1 JOIN ::SUB t2 ON t1.cmi=msid(t2.ci) GROUPBY t2.ci" # Join on a single interval (which is not unique; first one is taken). echo $casa_checktool ./tTableGramJoin "SELECT t1.cmi2, t1.cmd, t2.ci2, t2.cd2, t2.cd FROM tTableGramJoin_tmp.tab t1 JOIN ::SUB t2 ON t1.cmd around t2.cd2 in t2.cw" # Join on an integer and an interval (where units are adjusted). echo $casa_checktool ./tTableGramJoin "SELECT t1.cmi2, t1.cmd, t2.ci2, t2.cd2, t2.cd FROM tTableGramJoin_tmp.tab t1 JOIN ::SUB t2 ON t1.cmi2=t2.ci2 && t1.cmd around t2.cd2 in t2.cw" # Join on two integers. echo $casa_checktool ./tTableGramJoin "SELECT t1.cmi, t1.cmi2, t1.cmd, t2.ci, t2.cd, t2.cd2 FROM tTableGramJoin_tmp.tab t1 JOIN ::SUB t2 ON t1.cmi=t2.ci and t2.ci=t1.cmi" # Join on two intervals. echo $casa_checktool ./tTableGramJoin "SELECT t1.cmi2, t1.cmd, t2.ci2, t2.cd2, t2.cd, t2.cad FROM tTableGramJoin_tmp.tab t1 JOIN ::SUB t2 ON t1.cmd around t2.cd2 in t2.cw and t1.cmd around t2.cd2 in t2.cw" # Do a MS-SYSCAL like join (see at the end). echo echo "Updating the tables for nested joins ..." ../../apps/taql "UPDATE [CREATE TABLE tTableGramJoin_tmp.tab::DD [spw int, pol int] limit 9] set spw=rowid()%3, pol=rowid()//3" ../../apps/taql "UPDATE [CREATE TABLE tTableGramJoin_tmp.tab::SC [spw int, tod TIME, w double unit='s', ci int] limit 9] set spw=rowid()%3, w=1d, tod=2nov2022/12h0m0+rowid()//3, ci=rowid()" ../../apps/taql "select * from tTableGramJoin_tmp.tab::DD" ../../apps/taql "select * from tTableGramJoin_tmp.tab::SC" ../../apps/taql "select * from tTableGramJoin_tmp.tab" echo $casa_checktool ./tTableGramJoin "SELECT t1.rowid() as r1, t2.rowid() as r2, t3.rowid() as r3, t2.pol, t3.ci FROM tTableGramJoin_tmp.tab t1 JOIN ::DD t2 ON t1.cdd=t2.rowid() JOIN ::SC t3 ON t2.spw=t3.spw AND t1.ct AROUND t3.tod IN t3.w" echo $casa_checktool ./tTableGramJoin "SELECT t1.rowid() as r1, t2.pol, t3.ci FROM tTableGramJoin_tmp.tab t1 JOIN ::DD t2 ON t1.cdd=t2.rowid() JOIN ::SC t3 ON t2.spw=t3.spw AND t1.ct AROUND t3.tod IN t3.w WHERE t3.ci%3!=0" # Some erroneous commands. echo echo "Testing erroneous commands ..." $casa_checktool ./tTableGramJoin "select from tTableGramJoin_tmp.tab t1 JOIN ::DD t2 on gsum(t1.cdd)=t2.rowid()" $casa_checktool ./tTableGramJoin "select from tTableGramJoin_tmp.tab t1 JOIN ::SUB t2, ::SC t3 on t1.cdd=t2.rowid()+t3.rowid()" $casa_checktool ./tTableGramJoin "select from tTableGramJoin_tmp.tab t1 JOIN ::SUB t2 on t1.cmd=t2.ci" $casa_checktool ./tTableGramJoin "select t2.rownr() from tTableGramJoin_tmp.tab t1 JOIN ::DD t2 on t1.cdd=t2.rowid()" $casa_checktool ./tTableGramJoin "select from tTableGramJoin_tmp.tab t1 JOIN ::DD t2 on t1.cdd=3" $casa_checktool ./tTableGramJoin "select from tTableGramJoin_tmp.tab t1 JOIN ::DD t2 on t1.cmi!=t2.rowid()" $casa_checktool ./tTableGramJoin "select from tTableGramJoin_tmp.tab t1 JOIN ::DD t2 on [t1.cmi,t1.cmi]=t2.rowid()" $casa_checktool ./tTableGramJoin "select from tTableGramJoin_tmp.tab t1 JOIN ::DD t2 on t2.rowid() in [t1.cmi,t1.cmi]" $casa_checktool ./tTableGramJoin "select from tTableGramJoin_tmp.tab t1 JOIN ::DD t2 on t1.cmi+t2.rowid()=t2.rowid()" $casa_checktool ./tTableGramJoin "select from tTableGramJoin_tmp.tab t1 JOIN ::DD t2 on t1.cmi=t2.rowid()+t1.cmi" # Remove the symlink rm -f tTableGramJoin # Command for a join on the SYSCAL table of an MSv2: # using style python select from ~/data/GER.MS t1 # join ::DATA_DESCRIPTION t2 ON t1.DATA_DESC_ID==t2.rowid() # join ::SYSCAL t3 ON t1.ANTENNA1==t3.ANTENNA_ID and t2.SPECTRAL_WINDOW_ID==t3.SPECTRAL_WINDOW_ID and t1.TIME in t3.TIME<:>t3.INTERVAL casacore-3.7.1/tables/TaQL/test/tTableGramMasked.out000066400000000000000000000113071476623553700222600ustar00rootroot00000000000000 Test simple masking ... select sum(arr1),sum(arr1[arr1%3>0]),sum(arr1[arr1%3==0]) from tTableGramMasked_tmp.tab has been executed select result of 10 rows 3 selected columns: Col_1 Col_2 Col_3 276 84 192 852 276 576 1428 468 960 2004 660 1344 2580 852 1728 3156 1044 2112 3732 1236 2496 4308 1428 2880 4884 1620 3264 5460 1812 3648 Test replace(un)masked ... select sum(arraydata(replacemasked(arr1[arr1%3>0], 1))) from tTableGramMasked_tmp.tab has been executed select result of 10 rows 1 selected columns: Col_1 100 292 484 676 868 1060 1252 1444 1636 1828 select sum(arraydata(replaceunmasked(arr1[arr1%3>0], 1))) from tTableGramMasked_tmp.tab has been executed select result of 10 rows 1 selected columns: Col_1 200 584 968 1352 1736 2120 2504 2888 3272 3656 Test (col,mask) in SELECT ... select arr1, sums(arr1[arr1%2=0],1,2) as (a1,m1) R4 from tTableGramMasked_tmp.tab limit 2 giving tTableGramMasked_tmp.tab1 has been executed select result of 2 rows 2 selected columns: arr1 a1 shape=[2, 3, 4] shape=[4] shape=[2, 3, 4] shape=[4] select result of 2 rows 2 selected columns: a1 m1 [9, 27, 45, 63] [0, 0, 0, 0] [81, 99, 117, 135] [0, 0, 0, 0] select sums(gaggr(arr1[arr1%2=0]),[1,4]) as (a1,m1) R4 from tTableGramMasked_tmp.tab groupby ab%2 giving tTableGramMasked_tmp.tab1 has been executed select result of 2 rows 1 selected columns: a1 shape=[3, 4] shape=[3, 4] select result of 2 rows 2 selected columns: a1 m1 Axis Lengths: [3, 4] (NB: Matrix in Row/Column order) [485, 515, 545, 575 495, 525, 555, 585 505, 535, 565, 595] Axis Lengths: [3, 4] (NB: Matrix in Row/Column order) [0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0] Axis Lengths: [3, 4] (NB: Matrix in Row/Column order) [605, 635, 665, 695 615, 645, 675, 705 625, 655, 685, 715] Axis Lengths: [3, 4] (NB: Matrix in Row/Column order) [0, 0, 0, 0 0, 0, 0, 0 0, 0, 0, 0] select gsums(arr1[array(rowid()%2=0,shape(arr1))]) as (a1,m1) R4 from tTableGramMasked_tmp.tab groupby ab%2 giving tTableGramMasked_tmp.tab2 has been executed select result of 2 rows 1 selected columns: a1 shape=[2, 3, 4] shape=[2, 3, 4] select result of 2 rows 2 selected columns: a1 m1 Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][0, 0] [0, 1, 0][0, 0] [0, 2, 0][0, 0] [0, 0, 1][0, 0] [0, 1, 1][0, 0] [0, 2, 1][0, 0] [0, 0, 2][0, 0] [0, 1, 2][0, 0] [0, 2, 2][0, 0] [0, 0, 3][0, 0] [0, 1, 3][0, 0] [0, 2, 3][0, 0] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][1, 1] [0, 1, 0][1, 1] [0, 2, 0][1, 1] [0, 0, 1][1, 1] [0, 1, 1][1, 1] [0, 2, 1][1, 1] [0, 0, 2][1, 1] [0, 1, 2][1, 1] [0, 2, 2][1, 1] [0, 0, 3][1, 1] [0, 1, 3][1, 1] [0, 2, 3][1, 1] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][600, 605] [0, 1, 0][610, 615] [0, 2, 0][620, 625] [0, 0, 1][630, 635] [0, 1, 1][640, 645] [0, 2, 1][650, 655] [0, 0, 2][660, 665] [0, 1, 2][670, 675] [0, 2, 2][680, 685] [0, 0, 3][690, 695] [0, 1, 3][700, 705] [0, 2, 3][710, 715] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][0, 0] [0, 1, 0][0, 0] [0, 2, 0][0, 0] [0, 0, 1][0, 0] [0, 1, 1][0, 0] [0, 2, 1][0, 0] [0, 0, 2][0, 0] [0, 1, 2][0, 0] [0, 2, 2][0, 0] [0, 0, 3][0, 0] [0, 1, 3][0, 0] [0, 2, 3][0, 0] Test HAVING on mask in (col,mask) ... using style trace select sums(gaggr(arr1[array(ab%2=1,shape(arr1))]),4) as (a1,m1) from tTableGramMasked_tmp.tab groupby ab%2 having !any(m1) giving tTableGramMasked_tmp.tab3 has been executed select result of 1 rows 1 selected columns: a1 shape=[2, 3, 4] select result of 2 rows 2 selected columns: a1 m1 Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][480, 485] [0, 1, 0][490, 495] [0, 2, 0][500, 505] [0, 0, 1][510, 515] [0, 1, 1][520, 525] [0, 2, 1][530, 535] [0, 0, 2][540, 545] [0, 1, 2][550, 555] [0, 2, 2][560, 565] [0, 0, 3][570, 575] [0, 1, 3][580, 585] [0, 2, 3][590, 595] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][0, 0] [0, 1, 0][0, 0] [0, 2, 0][0, 0] [0, 0, 1][0, 0] [0, 1, 1][0, 0] [0, 2, 1][0, 0] [0, 0, 2][0, 0] [0, 1, 2][0, 0] [0, 2, 2][0, 0] [0, 0, 3][0, 0] [0, 1, 3][0, 0] [0, 2, 3][0, 0] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][0, 0] [0, 1, 0][0, 0] [0, 2, 0][0, 0] [0, 0, 1][0, 0] [0, 1, 1][0, 0] [0, 2, 1][0, 0] [0, 0, 2][0, 0] [0, 1, 2][0, 0] [0, 2, 2][0, 0] [0, 0, 3][0, 0] [0, 1, 3][0, 0] [0, 2, 3][0, 0] Ndim=3 Axis Lengths: [2, 3, 4] [0, 0, 0][1, 1] [0, 1, 0][1, 1] [0, 2, 0][1, 1] [0, 0, 1][1, 1] [0, 1, 1][1, 1] [0, 2, 1][1, 1] [0, 0, 2][1, 1] [0, 1, 2][1, 1] [0, 2, 2][1, 1] [0, 0, 3][1, 1] [0, 1, 3][1, 1] [0, 2, 3][1, 1] Test (col,mask) in INSERT ... insert into [create table tTableGramMasked_tmp.tab1 a1 I4 ndim=1, m1 B ndim=1] ((a1,m1)) VALUES (array([1:2],2)[array([T,F],2)]) has been executed insert result of 1 rows 1 selected columns: a1 shape=[2] select result of 1 rows 2 selected columns: a1 m1 [1, 2] [1, 0] casacore-3.7.1/tables/TaQL/test/tTableGramMasked.run000066400000000000000000000052641476623553700222620ustar00rootroot00000000000000#!/bin/sh #----------------------------------------------------------------------------- # Script to test the masked array in TableGram and TableParse. # All files generated will be deleted on exit. #============================================================================= # Create a symlink to achieve that casacore_memcheck (if used) will use # that name rm -f tTableGramMasked ln -s tTableGram tTableGramMasked # Set default testsrcdir if undefined. if test "$testsrcdir" = ""; then testsrcdir=../../../../../casacore/tables/TaQL/test fi # Use table tTable_2.data_v0 as the input by copying it. rm -rf tTableGramMasked_tmp.tab* mkdir tTableGramMasked_tmp.tab cp -r $testsrcdir/../../Tables/test/tTable_2.data_v0/table.* tTableGramMasked_tmp.tab chmod 644 tTableGramMasked_tmp.tab/* echo echo "Test simple masking ..." $casa_checktool ./tTableGramMasked 'select sum(arr1),sum(arr1[arr1%3>0]),sum(arr1[arr1%3==0]) from tTableGramMasked_tmp.tab' echo echo "Test replace(un)masked ..." $casa_checktool ./tTableGramMasked 'select sum(arraydata(replacemasked(arr1[arr1%3>0], 1))) from tTableGramMasked_tmp.tab' $casa_checktool ./tTableGramMasked 'select sum(arraydata(replaceunmasked(arr1[arr1%3>0], 1))) from tTableGramMasked_tmp.tab' echo echo "Test (col,mask) in SELECT ..." $casa_checktool ./tTableGramMasked 'select arr1, sums(arr1[arr1%2=0],1,2) as (a1,m1) R4 from tTableGramMasked_tmp.tab limit 2 giving tTableGramMasked_tmp.tab1' $casa_checktool ../../apps/taql -ps -d ' ' 'select a1,m1 from tTableGramMasked_tmp.tab1' $casa_checktool ./tTableGramMasked 'select sums(gaggr(arr1[arr1%2=0]),[1,4]) as (a1,m1) R4 from tTableGramMasked_tmp.tab groupby ab%2 giving tTableGramMasked_tmp.tab1' $casa_checktool ../../apps/taql -ps -d ' ' 'select a1,m1 from tTableGramMasked_tmp.tab1' $casa_checktool ./tTableGramMasked 'select gsums(arr1[array(rowid()%2=0,shape(arr1))]) as (a1,m1) R4 from tTableGramMasked_tmp.tab groupby ab%2 giving tTableGramMasked_tmp.tab2' $casa_checktool ../../apps/taql -ps -d ' ' 'select a1,m1 from tTableGramMasked_tmp.tab2' echo echo "Test HAVING on mask in (col,mask) ..." $casa_checktool ./tTableGramMasked 'using style trace select sums(gaggr(arr1[array(ab%2=1,shape(arr1))]),4) as (a1,m1) from tTableGramMasked_tmp.tab groupby ab%2 having !any(m1) giving tTableGramMasked_tmp.tab3' $casa_checktool ../../apps/taql -ps -d ' ' 'select a1,m1 from tTableGramMasked_tmp.tab3' echo echo "Test (col,mask) in INSERT ..." $casa_checktool ./tTableGramMasked 'insert into [create table tTableGramMasked_tmp.tab1 a1 I4 ndim=1, m1 B ndim=1] ((a1,m1)) VALUES (array([1:2],2)[array([T,F],2)])' ../../apps/taql -ps -d ' ' 'select * from tTableGramMasked_tmp.tab1' # Remove the symlink rm -f tTableGramMasked casacore-3.7.1/tables/TaQL/test/tTableGramNull.out000066400000000000000000000025271476623553700217720ustar00rootroot00000000000000CALC isnull(sums(nullarray(1),1)) has been executed [1] CALC isnull(nullarray(1.)+1) has been executed [1] select from tTableGramNull_tmp.tab where isnull(cola) has been executed select result of 5 rows 0 selected columns: select from tTableGramNull_tmp.tab where isnull(colv) has been executed select result of 5 rows 0 selected columns: select from tTableGramNull_tmp.tab where isnull(cols) has been executed select result of 0 rows 0 selected columns: update result of 5 rows select rowid() from tTableGramNull_tmp.tab where isnull(cola) has been executed select result of 3 rows 1 selected columns: Col_1 0 2 4 select sum(cola) from tTableGramNull_tmp.tab has been executed select result of 5 rows 1 selected columns: Col_1 0 16 0 16 0 select result of 5 rows 1 selected columns: Col_1 no_array Axis Lengths: [3, 2] (NB: Matrix in Row/Column order) [2, 5 3, 6 4, 2] no_array Axis Lengths: [3, 2] (NB: Matrix in Row/Column order) [2, 5 3, 6 4, 2] no_array insert result of 3 rows select rowid() from tTableGramNull_tmp.tab where isnull(cola) has been executed select result of 4 rows 1 selected columns: Col_1 0 2 4 6 select sum(cola) from tTableGramNull_tmp.tab has been executed select result of 8 rows 1 selected columns: Col_1 0 16 0 16 0 0 0 3 casacore-3.7.1/tables/TaQL/test/tTableGramNull.run000066400000000000000000000033101476623553700217560ustar00rootroot00000000000000#!/bin/sh #----------------------------------------------------------------------------- # Script to test the TableGram and TableParse class. All files generated will # be deleted on exit. #============================================================================= # Create a symlink to achieve that casacore_memcheck (if used) will use # that name rm -f tTableGramNull ln -s tTableGram tTableGramNull # Test null array basics. $casa_checktool ./tTableGramNull 'isnull(sums(nullarray(1),1))' $casa_checktool ./tTableGramNull 'isnull(nullarray(1.)+1)' # Create and print a table. ../../apps/taql -nopr "create table tTableGramNull_tmp.tab (cola R4 [ndim=0], colv I2 [ndim=1], cols I2) limit 5" $casa_checktool ./tTableGramNull 'select from tTableGramNull_tmp.tab where isnull(cola)' $casa_checktool ./tTableGramNull 'select from tTableGramNull_tmp.tab where isnull(colv)' $casa_checktool ./tTableGramNull 'select from tTableGramNull_tmp.tab where isnull(cols)' # Update the table. $casa_checktool ../../apps/taql 'update tTableGramNull_tmp.tab set cola=iif(rowid()%2==0, nullarray(1), array([1:6],[2,3]))' $casa_checktool ./tTableGramNull 'select rowid() from tTableGramNull_tmp.tab where isnull(cola)' $casa_checktool ./tTableGramNull 'select sum(cola) from tTableGramNull_tmp.tab' $casa_checktool ../../apps/taql -ps 'select cola+1 from tTableGramNull_tmp.tab' # Insert 3 rows. $casa_checktool ../../apps/taql 'insert into tTableGramNull_tmp.tab (cola) values (array(5,0)), (nullarray(1)), (array(3,1))' $casa_checktool ./tTableGramNull 'select rowid() from tTableGramNull_tmp.tab where isnull(cola)' $casa_checktool ./tTableGramNull 'select sum(cola) from tTableGramNull_tmp.tab' # Remove the symlink rm -f tTableGramNull casacore-3.7.1/tables/TaQL/test/tTableGramUpdate.out000066400000000000000000000642021476623553700223000ustar00rootroot00000000000000using style python select * from tTableGramUpdate_tmp.ref1 has been executed select result of 5 rows 3 selected columns: cola colv cols Ndim=3 Axis Lengths: [5, 2, 1] [0, 0, 0][-2, -2, -2, -2, -2] [0, 1, 0][-2, -2, -2, -2, -2] [7, 7, 7, 7, 7, 7] 0 Ndim=3 Axis Lengths: [5, 2, 1] [0, 0, 0][-1, -1, -1, -1, -1] [0, 1, 0][-1, -1, -1, -1, -1] [7, 7, 7, 7, 7, 7] 1 Ndim=3 Axis Lengths: [5, 2, 1] [0, 0, 0][0, 0, 0, 0, 0] [0, 1, 0][0, 0, 0, 0, 0] [7, 7, 7, 7, 7, 7] 2 Ndim=3 Axis Lengths: [5, 2, 1] [0, 0, 0][1, 1, 1, 1, 1] [0, 1, 0][1, 1, 1, 1, 1] [7, 7, 7, 7, 7, 7] 3 Ndim=3 Axis Lengths: [5, 2, 1] [0, 0, 0][2, 2, 2, 2, 2] [0, 1, 0][2, 2, 2, 2, 2] [7, 7, 7, 7, 7, 7] 4 using style python select * from tTableGramUpdate_tmp.ref2 has been executed select result of 5 rows 3 selected columns: cola colv cols Ndim=3 Axis Lengths: [5, 2, 1] [0, 0, 0][-2, -2, -2, -2, -2] [0, 1, 0][-2, -2, -2, -2, -2] [7, 7, 7, 7, 7, 7] 0 Ndim=3 Axis Lengths: [5, 2, 1] [0, 0, 0][-11, -11, -11, -1, -1] [0, 1, 0][-11, -11, -11, -1, -1] [7, 7, 8, 8, 8, 7] -1 Ndim=3 Axis Lengths: [5, 2, 1] [0, 0, 0][-10, -10, -10, 0, 0] [0, 1, 0][-10, -10, -10, 0, 0] [7, 7, 8, 8, 8, 7] 0 Ndim=3 Axis Lengths: [5, 2, 1] [0, 0, 0][-9, -9, -9, 1, 1] [0, 1, 0][-9, -9, -9, 1, 1] [7, 7, 8, 8, 8, 7] 1 Ndim=3 Axis Lengths: [5, 2, 1] [0, 0, 0][2, 2, 2, 2, 2] [0, 1, 0][2, 2, 2, 2, 2] [7, 7, 7, 7, 7, 7] 4 using style python select * from tTableGramUpdate_tmp.ref3 has been executed select result of 5 rows 3 selected columns: cola colv cols Ndim=3 Axis Lengths: [5, 2, 1] [0, 0, 0][-1, -1, -1, -1, -1] [0, 1, 0][-1, -1, -1, -1, -1] [10, 10, 10, 10, 10, 10] 5 Ndim=3 Axis Lengths: [5, 2, 1] [0, 0, 0][-8, -8, -8, 0, 0] [0, 1, 0][-8, -8, -8, 0, 0] [10, 10, 8, 8, 8, 10] 4 Ndim=3 Axis Lengths: [5, 2, 1] [0, 0, 0][-7, -7, -7, 1, 1] [0, 1, 0][-7, -7, -7, 1, 1] [10, 10, 8, 8, 8, 10] 5 Ndim=3 Axis Lengths: [5, 2, 1] [0, 0, 0][-8, -8, -8, 2, 2] [0, 1, 0][-8, -8, -8, 2, 2] [10, 10, 8, 8, 8, 10] 6 Ndim=3 Axis Lengths: [5, 2, 1] [0, 0, 0][3, 3, 3, 3, 3] [0, 1, 0][3, 3, 3, 3, 3] [10, 10, 10, 10, 10, 10] 9 using style python select * from tTableGramUpdate_tmp.ref4 has been executed select result of 5 rows 3 selected columns: cola colv cols Ndim=3 Axis Lengths: [5, 2, 1] [0, 0, 0][-1, -1, -1, -1, -1] [0, 1, 0][-1, -1, -1, -1, -1] [10, 12, 12, 12, 10, 10] 5 Ndim=3 Axis Lengths: [5, 2, 1] [0, 0, 0][-8, -8, -8, 0, 0] [0, 1, 0][-8, -8, -8, 1, 1] [10, 13, 8, 8, 8, 10] 4 Ndim=3 Axis Lengths: [5, 2, 1] [0, 0, 0][-7, -7, -7, 1, 1] [0, 1, 0][-7, -7, -7, 1, 1] [10, 14, 8, 8, 8, 10] 5 Ndim=3 Axis Lengths: [5, 2, 1] [0, 0, 0][-8, -8, -8, 2, 2] [0, 1, 0][-8, -8, -8, 2, 2] [10, 15, 8, 8, 8, 10] 6 Ndim=3 Axis Lengths: [5, 2, 1] [0, 0, 0][3, 3, 3, 3, 3] [0, 1, 0][3, 3, 3, 3, 3] [10, 16, 16, 16, 10, 10] 9 using style python select * from tTableGramUpdate_tmp.ref5 has been executed select result of 5 rows 3 selected columns: cola colv cols Ndim=3 Axis Lengths: [5, 2, 1] [0, 0, 0][-1, -1, -1, -1, -1] [0, 1, 0][-1, -1, -1, -1, -1] [3, 3, 3, 3, 3, 10] 5 Ndim=3 Axis Lengths: [5, 2, 1] [0, 0, 0][-8, -8, -8, 0, 0] [0, 1, 0][-8, -8, -8, 1, 1] [3, 13, 3, 3, 3, 10] 4 Ndim=3 Axis Lengths: [5, 2, 1] [0, 0, 0][-7, -7, -7, 1, 1] [0, 1, 0][-7, -7, -7, 1, 1] [3, 3, 3, 3, 3, 10] 5 Ndim=3 Axis Lengths: [5, 2, 1] [0, 0, 0][-8, -8, -8, 4, 4] [0, 1, 0][-8, -8, -8, 2, 2] [3, 15, 3, 3, 3, 10] 6 Ndim=3 Axis Lengths: [5, 2, 1] [0, 0, 0][4, 4, 4, 4, 4] [0, 1, 0][3, 3, 3, 3, 3] [3, 3, 3, 3, 3, 10] 9 Testing datatype I2 ... cretab result of 5 rows update tTableGramUpdate_tmp.tab1 set cols=0+rowid(), cola=array(0+rowid()-2,5,2,1), colv=array(7,6) has been executed update result of 5 rows 3 selected columns: cols cola colv 0 shape=[5, 2, 1] shape=[6] 1 shape=[5, 2, 1] shape=[6] 2 shape=[5, 2, 1] shape=[6] 3 shape=[5, 2, 1] shape=[6] 4 shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cols=cols-2, cola[1:3,,]=cola[2:4,,]-10, colv[3:5]=8 where cols>=0+1 and cols<=0+3 has been executed update result of 3 rows 3 selected columns: cols cola colv -1 shape=[5, 2, 1] shape=[6] 0 shape=[5, 2, 1] shape=[6] 1 shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cols=cols+10, cola[real(cola)<0-9]=cola+2, cols=cols-5, cola=cola+1, colv[real(colv)==7]=10 has been executed update result of 5 rows 5 selected columns: cols cola cols cola colv 5 shape=[5, 2, 1] 5 shape=[5, 2, 1] shape=[6] 4 shape=[5, 2, 1] 4 shape=[5, 2, 1] shape=[6] 5 shape=[5, 2, 1] 5 shape=[5, 2, 1] shape=[6] 6 shape=[5, 2, 1] 6 shape=[5, 2, 1] shape=[6] 9 shape=[5, 2, 1] 9 shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cola[,2:,][real(cola)[,2:,]==0]=array(0+rowid(),[5,1,1]), colv[2:4][(real(colv)>9)[2:4]]=12+rowid() has been executed update result of 5 rows 2 selected columns: cola colv shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cola[real(cola)>0+1.1][,1:1,]=array(0+4,5,1,1), colv[int(real(colv))%2=0][1:5]=3 has been executed update result of 5 rows 2 selected columns: cola colv shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 Testing datatype I4 ... cretab result of 5 rows update tTableGramUpdate_tmp.tab1 set cols=0+rowid(), cola=array(0+rowid()-2,5,2,1), colv=array(7,6) has been executed update result of 5 rows 3 selected columns: cols cola colv 0 shape=[5, 2, 1] shape=[6] 1 shape=[5, 2, 1] shape=[6] 2 shape=[5, 2, 1] shape=[6] 3 shape=[5, 2, 1] shape=[6] 4 shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cols=cols-2, cola[1:3,,]=cola[2:4,,]-10, colv[3:5]=8 where cols>=0+1 and cols<=0+3 has been executed update result of 3 rows 3 selected columns: cols cola colv -1 shape=[5, 2, 1] shape=[6] 0 shape=[5, 2, 1] shape=[6] 1 shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cols=cols+10, cola[real(cola)<0-9]=cola+2, cols=cols-5, cola=cola+1, colv[real(colv)==7]=10 has been executed update result of 5 rows 5 selected columns: cols cola cols cola colv 5 shape=[5, 2, 1] 5 shape=[5, 2, 1] shape=[6] 4 shape=[5, 2, 1] 4 shape=[5, 2, 1] shape=[6] 5 shape=[5, 2, 1] 5 shape=[5, 2, 1] shape=[6] 6 shape=[5, 2, 1] 6 shape=[5, 2, 1] shape=[6] 9 shape=[5, 2, 1] 9 shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cola[,2:,][real(cola)[,2:,]==0]=array(0+rowid(),[5,1,1]), colv[2:4][(real(colv)>9)[2:4]]=12+rowid() has been executed update result of 5 rows 2 selected columns: cola colv shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cola[real(cola)>0+1.1][,1:1,]=array(0+4,5,1,1), colv[int(real(colv))%2=0][1:5]=3 has been executed update result of 5 rows 2 selected columns: cola colv shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 Testing datatype R4 ... cretab result of 5 rows update tTableGramUpdate_tmp.tab1 set cols=0+rowid(), cola=array(0+rowid()-2,5,2,1), colv=array(7,6) has been executed update result of 5 rows 3 selected columns: cols cola colv 0 shape=[5, 2, 1] shape=[6] 1 shape=[5, 2, 1] shape=[6] 2 shape=[5, 2, 1] shape=[6] 3 shape=[5, 2, 1] shape=[6] 4 shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cols=cols-2, cola[1:3,,]=cola[2:4,,]-10, colv[3:5]=8 where cols>=0+1 and cols<=0+3 has been executed update result of 3 rows 3 selected columns: cols cola colv -1 shape=[5, 2, 1] shape=[6] 0 shape=[5, 2, 1] shape=[6] 1 shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cols=cols+10, cola[real(cola)<0-9]=cola+2, cols=cols-5, cola=cola+1, colv[real(colv)==7]=10 has been executed update result of 5 rows 5 selected columns: cols cola cols cola colv 5 shape=[5, 2, 1] 5 shape=[5, 2, 1] shape=[6] 4 shape=[5, 2, 1] 4 shape=[5, 2, 1] shape=[6] 5 shape=[5, 2, 1] 5 shape=[5, 2, 1] shape=[6] 6 shape=[5, 2, 1] 6 shape=[5, 2, 1] shape=[6] 9 shape=[5, 2, 1] 9 shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cola[,2:,][real(cola)[,2:,]==0]=array(0+rowid(),[5,1,1]), colv[2:4][(real(colv)>9)[2:4]]=12+rowid() has been executed update result of 5 rows 2 selected columns: cola colv shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cola[real(cola)>0+1.1][,1:1,]=array(0+4,5,1,1), colv[int(real(colv))%2=0][1:5]=3 has been executed update result of 5 rows 2 selected columns: cola colv shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 Testing datatype R8 ... cretab result of 5 rows update tTableGramUpdate_tmp.tab1 set cols=0+rowid(), cola=array(0+rowid()-2,5,2,1), colv=array(7,6) has been executed update result of 5 rows 3 selected columns: cols cola colv 0 shape=[5, 2, 1] shape=[6] 1 shape=[5, 2, 1] shape=[6] 2 shape=[5, 2, 1] shape=[6] 3 shape=[5, 2, 1] shape=[6] 4 shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cols=cols-2, cola[1:3,,]=cola[2:4,,]-10, colv[3:5]=8 where cols>=0+1 and cols<=0+3 has been executed update result of 3 rows 3 selected columns: cols cola colv -1 shape=[5, 2, 1] shape=[6] 0 shape=[5, 2, 1] shape=[6] 1 shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cols=cols+10, cola[real(cola)<0-9]=cola+2, cols=cols-5, cola=cola+1, colv[real(colv)==7]=10 has been executed update result of 5 rows 5 selected columns: cols cola cols cola colv 5 shape=[5, 2, 1] 5 shape=[5, 2, 1] shape=[6] 4 shape=[5, 2, 1] 4 shape=[5, 2, 1] shape=[6] 5 shape=[5, 2, 1] 5 shape=[5, 2, 1] shape=[6] 6 shape=[5, 2, 1] 6 shape=[5, 2, 1] shape=[6] 9 shape=[5, 2, 1] 9 shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cola[,2:,][real(cola)[,2:,]==0]=array(0+rowid(),[5,1,1]), colv[2:4][(real(colv)>9)[2:4]]=12+rowid() has been executed update result of 5 rows 2 selected columns: cola colv shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cola[real(cola)>0+1.1][,1:1,]=array(0+4,5,1,1), colv[int(real(colv))%2=0][1:5]=3 has been executed update result of 5 rows 2 selected columns: cola colv shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 Testing datatype C4 ... cretab result of 5 rows update tTableGramUpdate_tmp.tab1 set cols=0+rowid(), cola=array(0+rowid()-2,5,2,1), colv=array(7,6) has been executed update result of 5 rows 3 selected columns: cols cola colv (0,0) shape=[5, 2, 1] shape=[6] (1,0) shape=[5, 2, 1] shape=[6] (2,0) shape=[5, 2, 1] shape=[6] (3,0) shape=[5, 2, 1] shape=[6] (4,0) shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cols=cols-2, cola[1:3,,]=cola[2:4,,]-10, colv[3:5]=8 where cols>=0+1 and cols<=0+3 has been executed update result of 3 rows 3 selected columns: cols cola colv (-1,0) shape=[5, 2, 1] shape=[6] (0,0) shape=[5, 2, 1] shape=[6] (1,0) shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cols=cols+10, cola[real(cola)<0-9]=cola+2, cols=cols-5, cola=cola+1, colv[real(colv)==7]=10 has been executed update result of 5 rows 5 selected columns: cols cola cols cola colv (5,0) shape=[5, 2, 1] (5,0) shape=[5, 2, 1] shape=[6] (4,0) shape=[5, 2, 1] (4,0) shape=[5, 2, 1] shape=[6] (5,0) shape=[5, 2, 1] (5,0) shape=[5, 2, 1] shape=[6] (6,0) shape=[5, 2, 1] (6,0) shape=[5, 2, 1] shape=[6] (9,0) shape=[5, 2, 1] (9,0) shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cola[,2:,][real(cola)[,2:,]==0]=array(0+rowid(),[5,1,1]), colv[2:4][(real(colv)>9)[2:4]]=12+rowid() has been executed update result of 5 rows 2 selected columns: cola colv shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cola[real(cola)>0+1.1][,1:1,]=array(0+4,5,1,1), colv[int(real(colv))%2=0][1:5]=3 has been executed update result of 5 rows 2 selected columns: cola colv shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 Testing datatype C8 ... cretab result of 5 rows update tTableGramUpdate_tmp.tab1 set cols=0+rowid(), cola=array(0+rowid()-2,5,2,1), colv=array(7,6) has been executed update result of 5 rows 3 selected columns: cols cola colv (0,0) shape=[5, 2, 1] shape=[6] (1,0) shape=[5, 2, 1] shape=[6] (2,0) shape=[5, 2, 1] shape=[6] (3,0) shape=[5, 2, 1] shape=[6] (4,0) shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cols=cols-2, cola[1:3,,]=cola[2:4,,]-10, colv[3:5]=8 where cols>=0+1 and cols<=0+3 has been executed update result of 3 rows 3 selected columns: cols cola colv (-1,0) shape=[5, 2, 1] shape=[6] (0,0) shape=[5, 2, 1] shape=[6] (1,0) shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cols=cols+10, cola[real(cola)<0-9]=cola+2, cols=cols-5, cola=cola+1, colv[real(colv)==7]=10 has been executed update result of 5 rows 5 selected columns: cols cola cols cola colv (5,0) shape=[5, 2, 1] (5,0) shape=[5, 2, 1] shape=[6] (4,0) shape=[5, 2, 1] (4,0) shape=[5, 2, 1] shape=[6] (5,0) shape=[5, 2, 1] (5,0) shape=[5, 2, 1] shape=[6] (6,0) shape=[5, 2, 1] (6,0) shape=[5, 2, 1] shape=[6] (9,0) shape=[5, 2, 1] (9,0) shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cola[,2:,][real(cola)[,2:,]==0]=array(0+rowid(),[5,1,1]), colv[2:4][(real(colv)>9)[2:4]]=12+rowid() has been executed update result of 5 rows 2 selected columns: cola colv shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cola[real(cola)>0+1.1][,1:1,]=array(0+4,5,1,1), colv[int(real(colv))%2=0][1:5]=3 has been executed update result of 5 rows 2 selected columns: cola colv shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 Testing datatype U1 ... cretab result of 5 rows update tTableGramUpdate_tmp.tab1 set cols=20+rowid(), cola=array(20+rowid()-2,5,2,1), colv=array(7,6) has been executed update result of 5 rows 3 selected columns: cols cola colv 20 shape=[5, 2, 1] shape=[6] 21 shape=[5, 2, 1] shape=[6] 22 shape=[5, 2, 1] shape=[6] 23 shape=[5, 2, 1] shape=[6] 24 shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cols=cols-2, cola[1:3,,]=cola[2:4,,]-10, colv[3:5]=8 where cols>=20+1 and cols<=20+3 has been executed update result of 3 rows 3 selected columns: cols cola colv 19 shape=[5, 2, 1] shape=[6] 20 shape=[5, 2, 1] shape=[6] 21 shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cols=cols+10, cola[real(cola)<20-9]=cola+2, cols=cols-5, cola=cola+1, colv[real(colv)==7]=10 has been executed update result of 5 rows 5 selected columns: cols cola cols cola colv 25 shape=[5, 2, 1] 25 shape=[5, 2, 1] shape=[6] 24 shape=[5, 2, 1] 24 shape=[5, 2, 1] shape=[6] 25 shape=[5, 2, 1] 25 shape=[5, 2, 1] shape=[6] 26 shape=[5, 2, 1] 26 shape=[5, 2, 1] shape=[6] 29 shape=[5, 2, 1] 29 shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cola[,2:,][real(cola)[,2:,]==20]=array(20+rowid(),[5,1,1]), colv[2:4][(real(colv)>9)[2:4]]=12+rowid() has been executed update result of 5 rows 2 selected columns: cola colv shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cola[real(cola)>20+1.1][,1:1,]=array(20+4,5,1,1), colv[int(real(colv))%2=0][1:5]=3 has been executed update result of 5 rows 2 selected columns: cola colv shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 Testing datatype U2 ... cretab result of 5 rows update tTableGramUpdate_tmp.tab1 set cols=20+rowid(), cola=array(20+rowid()-2,5,2,1), colv=array(7,6) has been executed update result of 5 rows 3 selected columns: cols cola colv 20 shape=[5, 2, 1] shape=[6] 21 shape=[5, 2, 1] shape=[6] 22 shape=[5, 2, 1] shape=[6] 23 shape=[5, 2, 1] shape=[6] 24 shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cols=cols-2, cola[1:3,,]=cola[2:4,,]-10, colv[3:5]=8 where cols>=20+1 and cols<=20+3 has been executed update result of 3 rows 3 selected columns: cols cola colv 19 shape=[5, 2, 1] shape=[6] 20 shape=[5, 2, 1] shape=[6] 21 shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cols=cols+10, cola[real(cola)<20-9]=cola+2, cols=cols-5, cola=cola+1, colv[real(colv)==7]=10 has been executed update result of 5 rows 5 selected columns: cols cola cols cola colv 25 shape=[5, 2, 1] 25 shape=[5, 2, 1] shape=[6] 24 shape=[5, 2, 1] 24 shape=[5, 2, 1] shape=[6] 25 shape=[5, 2, 1] 25 shape=[5, 2, 1] shape=[6] 26 shape=[5, 2, 1] 26 shape=[5, 2, 1] shape=[6] 29 shape=[5, 2, 1] 29 shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cola[,2:,][real(cola)[,2:,]==20]=array(20+rowid(),[5,1,1]), colv[2:4][(real(colv)>9)[2:4]]=12+rowid() has been executed update result of 5 rows 2 selected columns: cola colv shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cola[real(cola)>20+1.1][,1:1,]=array(20+4,5,1,1), colv[int(real(colv))%2=0][1:5]=3 has been executed update result of 5 rows 2 selected columns: cola colv shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 Testing datatype U4 ... cretab result of 5 rows update tTableGramUpdate_tmp.tab1 set cols=20+rowid(), cola=array(20+rowid()-2,5,2,1), colv=array(7,6) has been executed update result of 5 rows 3 selected columns: cols cola colv 20 shape=[5, 2, 1] shape=[6] 21 shape=[5, 2, 1] shape=[6] 22 shape=[5, 2, 1] shape=[6] 23 shape=[5, 2, 1] shape=[6] 24 shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cols=cols-2, cola[1:3,,]=cola[2:4,,]-10, colv[3:5]=8 where cols>=20+1 and cols<=20+3 has been executed update result of 3 rows 3 selected columns: cols cola colv 19 shape=[5, 2, 1] shape=[6] 20 shape=[5, 2, 1] shape=[6] 21 shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cols=cols+10, cola[real(cola)<20-9]=cola+2, cols=cols-5, cola=cola+1, colv[real(colv)==7]=10 has been executed update result of 5 rows 5 selected columns: cols cola cols cola colv 25 shape=[5, 2, 1] 25 shape=[5, 2, 1] shape=[6] 24 shape=[5, 2, 1] 24 shape=[5, 2, 1] shape=[6] 25 shape=[5, 2, 1] 25 shape=[5, 2, 1] shape=[6] 26 shape=[5, 2, 1] 26 shape=[5, 2, 1] shape=[6] 29 shape=[5, 2, 1] 29 shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cola[,2:,][real(cola)[,2:,]==20]=array(20+rowid(),[5,1,1]), colv[2:4][(real(colv)>9)[2:4]]=12+rowid() has been executed update result of 5 rows 2 selected columns: cola colv shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tab1 set cola[real(cola)>20+1.1][,1:1,]=array(20+4,5,1,1), colv[int(real(colv))%2=0][1:5]=3 has been executed update result of 5 rows 2 selected columns: cola colv shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 Testing datatype B ... using style python select * from tTableGramUpdate_tmp.refb1 has been executed select result of 3 rows 3 selected columns: cola colv cols Ndim=3 Axis Lengths: [5, 2, 1] [0, 0, 0][0, 0, 0, 0, 0] [0, 1, 0][0, 0, 0, 0, 0] [1, 1, 1, 1, 1, 1] true Ndim=3 Axis Lengths: [5, 2, 1] [0, 0, 0][1, 1, 1, 1, 1] [0, 1, 0][1, 1, 1, 1, 1] [1, 1, 1, 1, 1, 1] false Ndim=3 Axis Lengths: [5, 2, 1] [0, 0, 0][1, 1, 1, 1, 1] [0, 1, 0][1, 1, 1, 1, 1] [1, 1, 1, 1, 1, 1] true using style python select * from tTableGramUpdate_tmp.refb2 has been executed select result of 3 rows 3 selected columns: cola colv cols Ndim=3 Axis Lengths: [5, 2, 1] [0, 0, 0][1, 1, 1, 0, 0] [0, 1, 0][1, 1, 1, 0, 0] [1, 1, 0, 0, 0, 1] false Ndim=3 Axis Lengths: [5, 2, 1] [0, 0, 0][0, 0, 0, 1, 1] [0, 1, 0][0, 0, 0, 1, 1] [1, 1, 0, 0, 0, 1] true Ndim=3 Axis Lengths: [5, 2, 1] [0, 0, 0][0, 0, 0, 1, 1] [0, 1, 0][0, 0, 0, 1, 1] [1, 1, 0, 0, 0, 1] false cretab result of 3 rows update tTableGramUpdate_tmp.tabb set cols=rowid()%2==0, cola=array(rowid()%3!=0,[5,2,1]), colv=array(T,6) has been executed update result of 3 rows 3 selected columns: cols cola colv 1 shape=[5, 2, 1] shape=[6] 0 shape=[5, 2, 1] shape=[6] 1 shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tabb set cols=!cols, cola[1:3,,]=!cola[2:4,,], colv[3:5]=F has been executed update result of 3 rows 3 selected columns: cols cola colv 0 shape=[5, 2, 1] shape=[6] 1 shape=[5, 2, 1] shape=[6] 0 shape=[5, 2, 1] shape=[6] select result of 0 rows 2 selected columns: colv cols2 Testing datatype S ... using style python select * from tTableGramUpdate_tmp.refs1 has been executed select result of 2 rows 3 selected columns: cola colv cols Ndim=3 Axis Lengths: [3, 1, 1] [0, 0, 0][1, 1, 1] [xy, xy, xy] 0 Ndim=3 Axis Lengths: [3, 1, 1] [0, 0, 0][2, 2, 2] [xy, xy, xy] 1 using style python select * from tTableGramUpdate_tmp.refs2 has been executed select result of 2 rows 3 selected columns: cola colv cols Ndim=3 Axis Lengths: [3, 1, 1] [0, 0, 0][1g, 1g, 1] [xy, bcd, bcd] 0abc Ndim=3 Axis Lengths: [3, 1, 1] [0, 0, 0][2g, 2g, 2] [xy, bcd, bcd] 1abc cretab result of 2 rows update tTableGramUpdate_tmp.tabs set cols=str(rowid()), cola=array(str(rowid()+1),[3,1,1]), colv=array('xy',3) has been executed update result of 2 rows 3 selected columns: cols cola colv 0 shape=[3, 1, 1] shape=[3] 1 shape=[3, 1, 1] shape=[3] select result of 0 rows 2 selected columns: colv cols2 update tTableGramUpdate_tmp.tabs set cols=cols+'abc', cola[1:2,,]=cola[2:3,,]+'g', colv[2:3]='bcd' has been executed update result of 2 rows 3 selected columns: cols cola colv 0abc shape=[3, 1, 1] shape=[3] 1abc shape=[3, 1, 1] shape=[3] select result of 0 rows 2 selected columns: colv cols2 Testing erroneous commands ... update tTableGramUpdate_tmp.tab1 set cola[,2:,][real(cola)[,1:,]==0]=array(rowid(),[5,1,1]) Caught an exception: Error in TaQL command: 'update tTableGramUpdate_tmp.tab1 set cola[,2:,][real(cola)[,1:,]==0]=array(rowid(),[5,1,1])' Error in select expression: Update mask must conform the column's array section update tTableGramUpdate_tmp.tab1 set cola[,1:,][real(cola)[,1:,]==0]=array(rowid(),[5,1,1]) Caught an exception: Error in TaQL command: 'update tTableGramUpdate_tmp.tab1 set cola[,1:,][real(cola)[,1:,]==0]=array(rowid(),[5,1,1])' Error in select expression: Array shapes in update of column cola mismatch update tTableGramUpdate_tmp.tab1 set cola[real(cola)>1.1][,1:1,]=array(4,5,2,1) Caught an exception: Error in TaQL command: 'update tTableGramUpdate_tmp.tab1 set cola[real(cola)>1.1][,1:1,]=array(4,5,2,1)' Error in select expression: Array shapes in update of column cola mismatch casacore-3.7.1/tables/TaQL/test/tTableGramUpdate.run000066400000000000000000000270341476623553700222770ustar00rootroot00000000000000#!/bin/sh #----------------------------------------------------------------------------- # Script to test the TableGram and TableParse class. All files generated will # be deleted on exit. #============================================================================= # Create a symlink to achieve that casacore_memcheck (if used) will use # that name rm -f tTableGramUpdate ln -s tTableGram tTableGramUpdate # Execute all kind of table update commands, especially a mix of # array slices and masks and various data types. # Check the result using a select command. # Create and print reference tables. ../../apps/taql -nopr "create table tTableGramUpdate_tmp.ref1 (cola I2 [shape=[1,2,5]], colv I2 [ndim=1], cols I2)" for val in 0 1 2 3 4 do ../../apps/taql -nopr "insert into tTableGramUpdate_tmp.ref1 (cols,cola,colv) VALUES($val, array($val-2,1,2,5), array(7,6))" done ../../apps/taql -d ' ' -p 'select * from tTableGramUpdate_tmp.ref1' ../../apps/taql -nopr "create table tTableGramUpdate_tmp.ref2 (cola I2 [shape=[1,2,5]], colv I2 [ndim=1], cols I2)" ../../apps/taql -nopr "insert into tTableGramUpdate_tmp.ref2 (cols,cola,colv) VALUES(0, array(-2,1,2,5), array(7,6))" ../../apps/taql -nopr "insert into tTableGramUpdate_tmp.ref2 (cols,cola,colv) VALUES(-1, array([-11,-11,-11,-1,-1,-11,-11,-11,-1,-1],1,2,5), [7,7,8,8,8,7])" ../../apps/taql -nopr "insert into tTableGramUpdate_tmp.ref2 (cols,cola,colv) VALUES(0, array([-10,-10,-10,0,0,-10,-10,-10,0,0],1,2,5), [7,7,8,8,8,7])" ../../apps/taql -nopr "insert into tTableGramUpdate_tmp.ref2 (cols,cola,colv) VALUES(1, array([-9,-9,-9,1,1,-9,-9,-9,1,1],1,2,5), [7,7,8,8,8,7])" ../../apps/taql -nopr "insert into tTableGramUpdate_tmp.ref2 (cols,cola,colv) VALUES(4, array(2,1,2,5), array(7,6))" ../../apps/taql -d ' ' -p 'select * from tTableGramUpdate_tmp.ref2' ../../apps/taql -nopr "create table tTableGramUpdate_tmp.ref3 (cola I2 [shape=[1,2,5]], colv I2 [ndim=1], cols I2)" ../../apps/taql -nopr "insert into tTableGramUpdate_tmp.ref3 (cols,cola,colv) VALUES(5, array(-1,1,2,5), array(10,6))" ../../apps/taql -nopr "insert into tTableGramUpdate_tmp.ref3 (cols,cola,colv) VALUES(4, array([-8,-8,-8,0,0,-8,-8,-8,0,0],1,2,5), [10,10,8,8,8,10])" ../../apps/taql -nopr "insert into tTableGramUpdate_tmp.ref3 (cols,cola,colv) VALUES(5, array([-7,-7,-7,1,1,-7,-7,-7,1,1],1,2,5), [10,10,8,8,8,10])" ../../apps/taql -nopr "insert into tTableGramUpdate_tmp.ref3 (cols,cola,colv) VALUES(6, array([-8,-8,-8,2,2,-8,-8,-8,2,2],1,2,5), [10,10,8,8,8,10])" ../../apps/taql -nopr "insert into tTableGramUpdate_tmp.ref3 (cols,cola,colv) VALUES(9, array(3,1,2,5), array(10,6))" ../../apps/taql -d ' ' -p 'select * from tTableGramUpdate_tmp.ref3' ../../apps/taql -nopr "create table tTableGramUpdate_tmp.ref4 (cola I2 [shape=[1,2,5]], colv I2 [ndim=1], cols I2)" ../../apps/taql -nopr "insert into tTableGramUpdate_tmp.ref4 (cols,cola,colv) VALUES(5, array(-1,1,2,5), [10,12,12,12,10,10])" ../../apps/taql -nopr "insert into tTableGramUpdate_tmp.ref4 (cols,cola,colv) VALUES(4, array([-8,-8,-8,0,0,-8,-8,-8,1,1],1,2,5), [10,13,8,8,8,10])" ../../apps/taql -nopr "insert into tTableGramUpdate_tmp.ref4 (cols,cola,colv) VALUES(5, array([-7,-7,-7,1,1,-7,-7,-7,1,1],1,2,5), [10,14,8,8,8,10])" ../../apps/taql -nopr "insert into tTableGramUpdate_tmp.ref4 (cols,cola,colv) VALUES(6, array([-8,-8,-8,2,2,-8,-8,-8,2,2],1,2,5), [10,15,8,8,8,10])" ../../apps/taql -nopr "insert into tTableGramUpdate_tmp.ref4 (cols,cola,colv) VALUES(9, array(3,1,2,5), [10,16,16,16,10,10])" ../../apps/taql -d ' ' -p 'select * from tTableGramUpdate_tmp.ref4' ../../apps/taql -nopr "create table tTableGramUpdate_tmp.ref5 (cola I2 [shape=[1,2,5]], colv I2 [ndim=1], cols I2)" ../../apps/taql -nopr "insert into tTableGramUpdate_tmp.ref5 (cols,cola,colv) VALUES(5, array(-1,1,2,5), [3,3,3,3,3,10])" ../../apps/taql -nopr "insert into tTableGramUpdate_tmp.ref5 (cols,cola,colv) VALUES(4, array([-8,-8,-8,0,0,-8,-8,-8,1,1],1,2,5), [3,13,3,3,3,10])" ../../apps/taql -nopr "insert into tTableGramUpdate_tmp.ref5 (cols,cola,colv) VALUES(5, array([-7,-7,-7,1,1,-7,-7,-7,1,1],1,2,5), [3,3,3,3,3,10])" ../../apps/taql -nopr "insert into tTableGramUpdate_tmp.ref5 (cols,cola,colv) VALUES(6, array([-8,-8,-8,4,4,-8,-8,-8,2,2],1,2,5), [3,15,3,3,3,10])" ../../apps/taql -nopr "insert into tTableGramUpdate_tmp.ref5 (cols,cola,colv) VALUES(9, array([4,4,4,4,4,3,3,3,3,3],1,2,5), [3,3,3,3,3,10])" ../../apps/taql -d ' ' -p 'select * from tTableGramUpdate_tmp.ref5' # Create tables and do updates for various numeric data taypes. add=0 for dtype in I2 I4 R4 R8 C4 C8 U1 U2 U4 do echo echo "Testing datatype $dtype ..." if [ $dtype = U1 ]; then add=20 # add 20 for unsigned to make everything positive fi # Create a new table with various columns and some rows. # Update the table and show the result. echo "" ../../apps/taql "create table tTableGramUpdate_tmp.tab1 (cola $dtype [shape=[1,2,5]], colv $dtype [ndim=1], cols $dtype) limit 5" $casa_checktool ./tTableGramUpdate "update tTableGramUpdate_tmp.tab1 set cols=$add+rowid(), cola=array($add+rowid()-2,5,2,1), colv=array(7,6)" ../../apps/taql -d ' ' -p -nopc "select t1.colv, t2.colv as cols2 from tTableGramUpdate_tmp.tab1 t1, tTableGramUpdate_tmp.ref1 t2 where t1.cols!=$add+t2.cols or any(t1.cola!=$add+t2.cola) or any(t1.colv!=t2.colv)" # Update part of an array using a slice for a few rows only. echo "" $casa_checktool ./tTableGramUpdate "update tTableGramUpdate_tmp.tab1 set cols=cols-2, cola[1:3,,]=cola[2:4,,]-10, colv[3:5]=8 where cols>=$add+1 and cols<=$add+3" ../../apps/taql -d ' ' -p -nopc "select t1.colv, t2.colv as cols2 from tTableGramUpdate_tmp.tab1 t1, tTableGramUpdate_tmp.ref2 t2 where t1.cols!=$add+t2.cols or any(t1.cola!=$add+t2.cola) or any(t1.colv!=t2.colv)" # Update part of an array using a mask; also do multiple updates. echo "" $casa_checktool ./tTableGramUpdate "update tTableGramUpdate_tmp.tab1 set cols=cols+10, cola[real(cola)<$add-9]=cola+2, cols=cols-5, cola=cola+1, colv[real(colv)==7]=10" ../../apps/taql -d ' ' -p -nopc "select t1.colv, t2.colv as cols2 from tTableGramUpdate_tmp.tab1 t1, tTableGramUpdate_tmp.ref3 t2 where t1.cols!=$add+t2.cols or any(t1.cola!=$add+t2.cola) or any(t1.colv!=t2.colv)" # Update using a slice and mask. echo "" $casa_checktool ./tTableGramUpdate "update tTableGramUpdate_tmp.tab1 set cola[,2:,][real(cola)[,2:,]==$add]=array($add+rowid(),[5,1,1]), colv[2:4][(real(colv)>9)[2:4]]=12+rowid()" ../../apps/taql -d ' ' -p -nopc "select t1.colv, t2.colv as cols2 from tTableGramUpdate_tmp.tab1 t1, tTableGramUpdate_tmp.ref4 t2 where t1.cols!=$add+t2.cols or any(t1.cola!=$add+t2.cola) or any(t1.colv!=t2.colv)" # Update using a mask and slice. echo "" $casa_checktool ./tTableGramUpdate "update tTableGramUpdate_tmp.tab1 set cola[real(cola)>$add+1.1][,1:1,]=array($add+4,5,1,1), colv[int(real(colv))%2=0][1:5]=3" ../../apps/taql -d ' ' -p -nopc "select t1.colv, t2.colv as cols2 from tTableGramUpdate_tmp.tab1 t1, tTableGramUpdate_tmp.ref5 t2 where t1.cols!=$add+t2.cols or any(t1.cola!=$add+t2.cola) or any(t1.colv!=t2.colv)" done # Do tests for Bool. echo echo "Testing datatype B ..." # Create the reference tables. ../../apps/taql -nopr "create table tTableGramUpdate_tmp.refb1 (cola B [shape=[1,2,5]], colv B [ndim=1], cols B)" ../../apps/taql -nopr "insert into tTableGramUpdate_tmp.refb1 (cols,cola,colv) VALUES(T, array(F,1,2,5), array(T,6))" ../../apps/taql -nopr "insert into tTableGramUpdate_tmp.refb1 (cols,cola,colv) VALUES(F, array(T,1,2,5), array(T,6))" ../../apps/taql -nopr "insert into tTableGramUpdate_tmp.refb1 (cols,cola,colv) VALUES(T, array(T,1,2,5), array(T,6))" ../../apps/taql -d ' ' -p 'select * from tTableGramUpdate_tmp.refb1' ../../apps/taql -nopr "create table tTableGramUpdate_tmp.refb2 (cola B [shape=[1,2,5]], colv B [ndim=1], cols B)" ../../apps/taql -nopr "insert into tTableGramUpdate_tmp.refb2 (cols,cola,colv) VALUES(F, array([T,T,T,F,F,T,T,T,F,F],1,2,5), [T,T,F,F,F,T])" ../../apps/taql -nopr "insert into tTableGramUpdate_tmp.refb2 (cols,cola,colv) VALUES(T, array([F,F,F,T,T,F,F,F,T,T],1,2,5), [T,T,F,F,F,T])" ../../apps/taql -nopr "insert into tTableGramUpdate_tmp.refb2 (cols,cola,colv) VALUES(F, array([F,F,F,T,T,F,F,F,T,T],1,2,5), [T,T,F,F,F,T])" ../../apps/taql -d ' ' -p 'select * from tTableGramUpdate_tmp.refb2' # Create a new table with various columns and some rows. # Update the table and show the result. echo "" ../../apps/taql "create table tTableGramUpdate_tmp.tabb (cola B [shape=[1,2,5]], colv B [ndim=1], cols B) limit 3" $casa_checktool ./tTableGramUpdate "update tTableGramUpdate_tmp.tabb set cols=rowid()%2==0, cola=array(rowid()%3!=0,[5,2,1]), colv=array(T,6)" ../../apps/taql -d ' ' -p -nopc "select t1.colv, t2.colv as cols2 from tTableGramUpdate_tmp.tabb t1, tTableGramUpdate_tmp.refb1 t2 where t1.cols!=t2.cols or any(t1.cola!=t2.cola) or any(t1.colv!=t2.colv)" # Update part of an array using a slice for a few rows only. echo "" $casa_checktool ./tTableGramUpdate "update tTableGramUpdate_tmp.tabb set cols=!cols, cola[1:3,,]=!cola[2:4,,], colv[3:5]=F" ../../apps/taql -d ' ' -p -nopc "select t1.colv, t2.colv as cols2 from tTableGramUpdate_tmp.tabb t1, tTableGramUpdate_tmp.refb2 t2 where t1.cols!=t2.cols or any(t1.cola!=t2.cola) or any(t1.colv!=t2.colv)" # Do tests for String. echo echo "Testing datatype S ..." # Create the reference tables. ../../apps/taql -nopr "create table tTableGramUpdate_tmp.refs1 (cola S [shape=[1,1,3]], colv S [ndim=1], cols S)" ../../apps/taql -nopr "insert into tTableGramUpdate_tmp.refs1 (cols,cola,colv) VALUES('0', array('1',1,1,3), array('xy',3))" ../../apps/taql -nopr "insert into tTableGramUpdate_tmp.refs1 (cols,cola,colv) VALUES('1', array('2',1,1,3), array('xy',3))" ../../apps/taql -d ' ' -p 'select * from tTableGramUpdate_tmp.refs1' ../../apps/taql -nopr "create table tTableGramUpdate_tmp.refs2 (cola S [shape=[1,1,3]], colv S [ndim=1], cols S)" ../../apps/taql -nopr "insert into tTableGramUpdate_tmp.refs2 (cols,cola,colv) VALUES('0abc', array(['1g','1g','1'],1,1,3), ['xy','bcd','bcd'])" ../../apps/taql -nopr "insert into tTableGramUpdate_tmp.refs2 (cols,cola,colv) VALUES('1abc', array(['2g','2g','2'],1,1,3), ['xy','bcd','bcd'])" ../../apps/taql -d ' ' -p 'select * from tTableGramUpdate_tmp.refs2' # Create a new table with various columns and some rows. # Update the table and show the result. echo "" ../../apps/taql "create table tTableGramUpdate_tmp.tabs (cola S [shape=[1,1,3]], colv S [ndim=1], cols S) limit 2" $casa_checktool ./tTableGramUpdate "update tTableGramUpdate_tmp.tabs set cols=str(rowid()), cola=array(str(rowid()+1),[3,1,1]), colv=array('xy',3)" ../../apps/taql -d ' ' -p -nopc "select t1.colv, t2.colv as cols2 from tTableGramUpdate_tmp.tabs t1, tTableGramUpdate_tmp.refs1 t2 where t1.cols!=t2.cols or any(t1.cola!=t2.cola) or any(t1.colv!=t2.colv)" # Update part of an array using a slice for a few rows only. echo "" $casa_checktool ./tTableGramUpdate "update tTableGramUpdate_tmp.tabs set cols=cols+'abc', cola[1:2,,]=cola[2:3,,]+'g', colv[2:3]='bcd'" ../../apps/taql -d ' ' -p -nopc "select t1.colv, t2.colv as cols2 from tTableGramUpdate_tmp.tabs t1, tTableGramUpdate_tmp.refs2 t2 where t1.cols!=t2.cols or any(t1.cola!=t2.cola) or any(t1.colv!=t2.colv)" # Do some erroneous updates. echo echo "Testing erroneous commands ..." $casa_checktool ./tTableGramUpdate 'update tTableGramUpdate_tmp.tab1 set cola[,2:,][real(cola)[,1:,]==0]=array(rowid(),[5,1,1])' $casa_checktool ./tTableGramUpdate 'update tTableGramUpdate_tmp.tab1 set cola[,1:,][real(cola)[,1:,]==0]=array(rowid(),[5,1,1])' $casa_checktool ./tTableGramUpdate 'update tTableGramUpdate_tmp.tab1 set cola[real(cola)>1.1][,1:1,]=array(4,5,2,1)' # Remove the symlink rm -f tTableGramUpdate casacore-3.7.1/tables/TaQL/test/ttaql.in000066400000000000000000000001651476623553700200350ustar00rootroot00000000000000# comment1 sum([1,2, # comment2 3, 4]) ; # 3+4; 'ab"# '" cd;e'f" ; 8 ; ; ;;;; 7 ; 1;2;3;4; 5+6 ; 18#1 casacore-3.7.1/tables/TaQL/test/ttaql.run000066400000000000000000000003641476623553700202340ustar00rootroot00000000000000#!/bin/sh # This script tests the taql program. # Direct command. ../../apps/taql '1+2' echo # Commands in 'interactive' mode. ../../apps/taql < TableBaseTable- nrrow_p: rownr_t- name_p: StringPlainTable- tsmOption_p: TSMOptionTableDescColumnDescSetBaseColumnColumnSetBaseColumnDescPlainColumn- originalName_p: String- rtraceColumn_p: Bool- wtraceColumn_p: BoolScalarColumnData<T>- undefVal_p: TArrayColumnData<T>- shapeCol_p: IPosition- checkValueLength_p: BoolScalarColumnDesc<T>ArrayColumnDescBase
        0..n
        0..n
        Use
        Use
        ArrayColumnDesc<T>
        0..n
        0..n
        RefTable- rows_p: Vector<rownr_t> - nameMap_pConcatTable- rows_p: ConcatRows- subTableNames_pConcatColumn- cache_p: ColumnCache- keywordSet_p: TableRecord
        0..n
        0..n
        1
        1
        0..n
        0..n
        0..n
        0..n
        0..n
        0..n
        Use
        Use
        0..n
        0..n
        1
        1
        ConcatScalarColumn
        0..n
        0..n
        1
        1
        Use
        Use
        RefColumn- cache_p: ColumnCacheMemoryTable
        Text is not SVG - cannot display
        casacore-3.7.1/tables/Table.drawio.svg.extrahtml000066400000000000000000000004061476623553700216410ustar00rootroot00000000000000
        See the TableOverview diagram for a global overview diagram.
        See the PlainTable diagram and TableDesc diagram for more details.
        casacore-3.7.1/tables/TableDesc.drawio.svg000066400000000000000000000437211476623553700204400ustar00rootroot00000000000000 TableDesc- name_p: StringColumnDescSet- colSeq_p: Block<void*>- cols_p: map<String,ColumnDesc>ColumnDesc- colPtr_p: BaseColumnDesc*- allocated_p: BoolBaseColumnDesc- colName_p: String- dtype_p: DataType- shape_p: IPosition
        0..n
        0..n
        1
        1

        TableRecordpublic table keywordsTableRecordprivate table keywordsTableRecordpublic column keywordsScalarColumnDesc<T>- defaultVal_p: TArrayColumnDesc<T>SubTableDesc
        Text is not SVG - cannot display
        casacore-3.7.1/tables/TableOverview.drawio.svg000066400000000000000000001101671476623553700213670ustar00rootroot00000000000000 TableBaseTable- nrrow_p: rownr_t- name_p: StringTableInfo- type_p: String- subType_p: String- readme_p: StringTableDescBaseColumnTableColumn- canChangeShape_p: Bool- isColWritable_p: BoolScalarColumn<T>+ get functions+ put functionsArrayColumn<T>+ get functions+ put functionsTableProxy
        Use
        Use
        Python
        Python
        ArrayColumnBase- shape_p: IPositionTableRowAccess to a table rowColumnsIndexIndex on scalar columnsTableRowProxyTableIndexProxyTableIteratorProxyTableIterator
        Iteration over table rows
        Iteration over table ro...
        BaseTableIteratorColumnsIndexArrayIndex on array columns
        Use
        Use
        Use
        Use
        Use
        Use
        0..n
        0..n
        1
        1
        0..n
        0..n
        BaseCompareIteration key comparison
        1..n
        1..n
        0..n
        0..n
        0..n
        0..n
        Text is not SVG - cannot display
        casacore-3.7.1/tables/TableOverview.drawio.svg.extrahtml000066400000000000000000000005241476623553700233710ustar00rootroot00000000000000
        The following diagrams show more detailed information:
        Table
        PlainTable
        TableDesc
        TableRecord
        DataManager
        casacore-3.7.1/tables/TableRecord.drawio.svg000066400000000000000000000620751476623553700210030ustar00rootroot00000000000000

        <<RecordInterface>>
        Interface


        - type_p: RecordType



        <<RecordInterface>>...
        Record- rep_p: COWPtr<RecordRep>- parent_p: RecordRep*TableRecord- rep_p: COWPtr<TableRecordRep>- parent_p: TableRecordRep*RecordRep- desc_p: RecordDesc- data_p: Block<void*>TableRecordRep- desc_p: RecordDesc
        parent
        parent
        0..n
        0..n
        1
        1
        parent
        parent
        0..n
        0..n
        1
        1
        RecordDescRep- types_p: Block<Int>- names_p: Block<String>- sub_records_p: PtrBlock<RecordDesc*>RecordDesc- desc_p: COWPtr<RecordDescRep>
        0..n
        0..n
        1
        1
        TableTableAttr- name_p: String- openWritable_p: Bool- lockOptions_p: TableLockTableKeyword- attr_p: TableAttr- table_p: Table*
        Text is not SVG - cannot display
        casacore-3.7.1/tables/Tables.h000066400000000000000000002525601476623553700161730ustar00rootroot00000000000000//# Tables.h: The Tables module - Casacore data storage //# Copyright (C) 1994-2010 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABLES_H #define TABLES_TABLES_H //# Includes //# table description #include #include #include #include #include #include //# table access #include #include #include #include #include #include #include #include #include #include #include //# keywords #include #include //# table lookup #include #include //# table vectors #include #include #include //# data managers #include //# table expressions (for selection of rows) #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // // CTDS (Casacore Table Data System) is the data storage mechanism for Casacore // // // // // //
      • Record class // // // "Table" is a formal term from relational database theory: // "The organizing principle in a relational database is the TABLE, // a rectangular, row/column arrangement of data values." // Casacore tables are extensions to traditional tables, but are similar // enough that we use the same name. There is also a strong resemblance // between the uses of Casacore tables, and FITS binary tables, which // provides another reason to use "Tables" to describe the Casacore data // storage mechanism. // // // Tables are the fundamental storage mechanism for Casacore. This document // explains why they had to be made, // what their properties are, and // how to use them. The last subject is // discussed and illustrated in a sequence of sections: // // A few applications exist to inspect // and manipulate a table. // // Several UML diagrams describe the class structure of the Tables module. // // // // // The Casacore tables are mainly based upon the ideas of Allen Farris, // as laid out in the // // AIPS++ Database document, from where the following paragraph is taken: // //

        // Traditional relational database tables have two features that // decisively limit their applicability to scientific data. First, an item of // data in a column of a table must be atomic -- it must have no internal // structure. A consequence of this restriction is that relational // databases are unable to deal with arrays of data items. Second, an // item of data in a column of a table must not have any direct or // implied linkages to other items of data or data aggregates. This // restriction makes it difficult to model complex relationships between // collections of data. While these restrictions may make it easy to // define a mathematically complete set of data manipulation operations, // they are simply intolerable in a scientific data-handling context. // Multi-dimensional arrays are frequently the most natural modes in // which to discuss and think about scientific data. In addition, // scientific data often requires complex calibration operations that // must draw on large bodies of data about equipment and its performance // in various states. The restrictions imposed by the relational model // make it very difficult to deal with complex problems of this nature. //

        // // In response to these limitations, and other needs, the Casacore tables were // designed. // // //

        Table Properties

        // // Casacore tables have the following properties: //
          //
        • A table consists of a number of rows and columns. // Keyword/value pairs may be defined // for the table as a whole and for individual columns. A keyword/value // pair for a column could, for instance, define its unit. //
        • Each table has a description // which specifies the number and type of columns, and maybe initial // keyword sets and default values for the columns. //
        • A cell in a column may contain //
            //
          • a scalar; //
          • a "direct" array -- which must have the same shape in all // cells of a column, is usually small, and is stored in the // table itself; //
          • an "indirect" array -- which may have different shapes in // different cells of the same column, is arbitrarily large, // and is stored in a separate file; //
          //
        • A column may be //
            //
          • "filled" -- containing actual data, or //
          • "virtual" -- containing a recipe telling how the data will // be generated dynamically //
          //
        • Only the standard Casacore data types can be used in filled // columns, be they scalars or arrays: Bool, uChar, Short, uShort, // Int, uInt, Int64, float, double, Complex, DComplex and String. // Furthermore scalars containing // record values are possible //
        • A column can have a default value, which will automatically be stored // in a cell of the column, when a row is added to the table. //
        • Data managers handle the // reading, writing and generation of data. Each column in a table can // be assigned its own data manager, which allows for optimization of // the data storage per column. The choice of data manager determines // whether a column is filled or virtual. //
        • Table data are stored in a canonical format, so they can be read // on any machine. To avoid needless swapping of bytes, the data can // be stored in big endian (as used on e.g. SUN) or little endian // (as used on Intel PC-s) canonical format. // By default it uses the format specified in the aipsrc variable // table.endianformat which defaults to // Table::LocalEndian (the endian format of the // machine being used when creating the table). //
        • The SQL-like // Table Query Language (TaQL) // can be used to do operations on tables like // select, sort, update, insert, delete, and create. //
        // // Tables can be in one of four forms: //
          //
        • A plain table is a table stored on disk. // It can be shared by multiple processes. //
        • A memory table is a table held in memory. // It is a process specific table, thus not sharable. // The Table::copy function can be used // to turn a memory table into a plain table. //
        • A reference table is a table referencing a plain or memory table. // It is the result of a selection or sort on another table. // A reference table references the data in the other table, thus // changing data in a reference table means that the data in the // original table are changed. // The Table::deepCopy function can be // used to turn a reference table into a plain table. //
        • a concatenated table // is a union of tables (of any form) with the same description. // They are concatenated in a virtual way, thus no copy is made. //
        // Concurrent access from different processes to the same plain table is // fully supported by means of a // locking/synchronization mechanism. Concurrent access over NFS is also // supported. //

        // A (somewhat primitive) mechanism is available to do a // table lookup based on the contents // of a key. // //

        Opening an Existing Table

        // // To open an existing table you just create a // Table object giving // the name of the table, like: // // // Table readonly_table ("tableName"); // // or // Table read_and_write_table ("tableName", Table::Update); // // // The constructor option determines whether the table will be opened as // readonly or as read/write. A readonly table file must be opened // as readonly, otherwise an exception is thrown. The functions // Table::isWritable(...) // can be used to determine if a table is writable. // // When the table is opened, the data managers are reinstantiated // according to their definition at table creation. //

        // // The static function TableUtil::openTable can be used to open a table, // in particular a subtable, in a simple way by means of the :: notation like // maintable::subtable. The :: notation is much better than specifying // an explicit path (such as maintable/subtable, because it also works // fine if the main table is a reference table (e.g. the result of a selection). // //

        Reading from a Table

        // // You can read data from a table column with the "get" functions // in the classes // ScalarColumn<T> // and // ArrayColumn<T>. // For scalars of a standard data type (i.e. Bool, uChar, Int, Short, // uShort, uInt, float, double, Complex, DComplex and String) you could // instead use // TableColumn::getScalar(...) or // TableColumn::asXXX(...). // These functions offer an extra: they do automatic data type promotion; // so that you can, for example, get a double value from a float column. // // These "get" functions are used in the same way as the simple "put" // functions described in the previous section. //

        // ScalarColumn<T> // can be constructed for a non-writable column. However, an exception // is thrown if the put function is used for it. // The same is true for // ArrayColumn<T> and // TableColumn. //

        // A typical program could look like: // // #include // #include // #include // #include // #include // #include // #include // // main() // { // // Open the table (readonly). // Table tab ("some.name"); // // // Construct the various column objects. // // Their data type has to match the data type in the table description. // ScalarColumn acCol (tab, "ac"); // ArrayColumn arr2Col (tab, "arr2"); // // // Loop through all rows in the table. // uInt nrrow = tab.nrow(); // for (uInt i=0; i //

        Creating a Table

        // // The creation of a table is a multi-step process: //
          //
        1. // Create a table description. //
        2. // Create a SetupNewTable // object with the name of the new table. //
        3. // Create the necessary data managers. //
        4. // Bind each column to the appropriate data manager. // The system will bind unbound columns to data managers which // are created internally using the default data manager name // defined in the column description. //
        5. // Define the shape of direct columns (if that was not already done in the // column description). //
        6. // Create the Table // object from the SetupNewTable object. Here, a final check is performed // and the necessary files are created. //
        // The recipe above is meant for the creation a plain table, but the // creation of a memory table is exactly the same. The only difference // is that in call to construct the Table object the Table::Memory // type has to be given. Note that in the SetupNewTable object the columns // can be bound to any data manager. MemoryTable will rebind // stored columns to the MemoryStMan // storage manager, but virtual columns bindings are not changed. // // The following example shows how you can create a table. An example // specifically illustrating the creation of the // table description is given // in that section. Other sections discuss the access to the table. // // // #include // #include // #include // #include // #include // #include // #include // #include // // main() // { // // Step1 -- Build the table description. // TableDesc td("tTableDesc", "1", TableDesc::Scratch); // td.comment() = "A test of class SetupNewTable"; // td.addColumn (ScalarColumnDesc ("ab" ,"Comment for column ab")); // td.addColumn (ScalarColumnDesc ("ac")); // td.addColumn (ScalarColumnDesc ("ad","comment for ad")); // td.addColumn (ScalarColumnDesc ("ae")); // td.addColumn (ScalarRecordColumnDesc ("arec")); // td.addColumn (ArrayColumnDesc ("arr1",3,ColumnDesc::Direct)); // td.addColumn (ArrayColumnDesc ("arr2",0)); // td.addColumn (ArrayColumnDesc ("arr3",0,ColumnDesc::Direct)); // // // Step 2 -- Setup a new table from the description. // SetupNewTable newtab("newtab.data", td, Table::New); // // // Step 3 -- Create storage managers for it. // StandardStMan stmanStand_1; // StandardStMan stmanStand_2; // IncrementalStMan stmanIncr; // // // Step 4 -- First, bind all columns to the first storage // // manager. Then, bind a few columns to another storage manager // // (which will overwrite the previous bindings). // newtab.bindAll (stmanStand_1); // newtab.bindColumn ("ab", stmanStand_2); // newtab.bindColumn ("ae", stmanIncr); // newtab.bindColumn ("arr3", stmanIncr); // // // Step 5 -- Define the shape of the direct columns. // // (this could have been done in the column description). // newtab.setShapeColumn( "arr1", IPosition(3,2,3,4)); // newtab.setShapeColumn( "arr3", IPosition(3,3,4,5)); // // // Step 6 -- Finally, create the table consisting of 10 rows. // Table tab(newtab, 10); // // // Now we can fill the table, which is shown in a next section. // // The Table destructor will flush the table to the files. // } // // To create a table in memory, only step 6 has to be modified slightly to: // // Table tab(newtab, Table::Memory, 10); // // // Note that the function TableUtil::createTable can be used to create a table // in a simpler way. It can also be used to create a subtable using the :: notation // similar to the Tableutil::openTable // function described above. // //

        Writing into a Table

        // // Once a table has been created or has been opened for read/write, // you want to write data into it. Before doing that you may have // to add one or more rows to the table. // If a table was created with a given number of rows, you // do not need to add rows; you may not even be able to do so. // // // When adding new rows to the table, either via the // Table(...) constructor // or via the // Table::addRow(...) // function, you can choose to have those rows initialized with the // default values given in the description. // // To actually write the data into the table you need the classes // ScalarColumn<T> and // ArrayColumn<T>. // For each column you can construct one or // more of these objects. Their put(...) functions // let you write a value at a time or the entire column in one go. // For arrays you can "put" subsections of the arrays. // // As an alternative for scalars of a standard data type (i.e. Bool, // uChar, Int, Short, uShort, uInt, float, double, Complex, DComplex // and String) you could use the functions // TableColumn::putScalar(...). // These functions offer an extra: automatic data type promotion; so that // you can, for example, put a float value in a double column. // // A typical program could look like: // // #include // #include // #include // #include // #include // #include // #include // #include // #include // #include // #include // // main() // { // // First build the table description. // TableDesc td("tTableDesc", "1", TableDesc::Scratch); // td.comment() = "A test of class SetupNewTable"; // td.addColumn (ScalarColumnDesc ("ac")); // td.addColumn (ArrayColumnDesc ("arr2",0)); // // // Setup a new table from the description, // // and create the (still empty) table. // // Note that since we do not explicitly bind columns to // // data managers, all columns will be bound to the default // // standard storage manager StandardStMan. // SetupNewTable newtab("newtab.data", td, Table::New); // Table tab(newtab); // // // Construct the various column objects. // // Their data type has to match the data type in the description. // ScalarColumn ac (tab, "ac"); // ArrayColumn arr2 (tab, "arr2"); // Vector vec2(100); // // // Write the data into the columns. // // In each cell arr2 will be a vector of length 100. // // Since its shape is not set explicitly, it is done implicitly. // for (uInt i=0; i<10; i++) { // tab.addRow(); // First add a row. // ac.put (i, i+10); // value is i+10 in row i // indgen (vec2, float(i+20)); // vec2 gets i+20, i+21, ..., i+119 // arr2.put (i, vec2); // } // // // Finally, show the entire column ac, // // and show the 10th element of arr2. // cout << ac.getColumn(); // cout << arr2.getColumn (Slicer(Slice(10))); // // // The Table destructor writes the table. // } // // // In this example we added rows in the for loop, but we could also have // created 10 rows straightaway by constructing the Table object as: // // Table tab(newtab, 10); // // in which case we would not include // // tab.addRow() // // // The classes // TableColumn, // ScalarColumn<T>, and // ArrayColumn<T> // contain several functions to put values into a single cell or into the // whole column. This may look confusing, but is actually quite simple. // The functions can be divided in two groups: //
          //
        1. // Put the given value into the column cell(s). //
            //
          • // The simplest put functions, // ScalarColumn::put(...) and // ArrayColumn::put(...), // put a value into the given column cell. For convenience, there is an // ArrayColumn::putSlice(...) // to put only a part of the array. //
          • // ScalarColumn::fillColumn(...) and // ArrayColumn::fillColumn(...) // fill an entire column by putting the given value into all the cells // of the column. //
          • // The simplest putColumn functions, // ScalarColumn::putColumn(...) and // ArrayColumn::putColumn(...), // put an array of values into the column. There is a special // ArrayColumn::putColumn(...) // version which puts only a part of the arrays. //
          // //
        2. // Copy values from another column to this column.
          // These functions have the advantage that the // data type of the input and/or output column can be unknown. // The generic TableColumn objects can be used for this purpose. // The put(Column) function checks the data types and, if possible, // converts them. If the conversion is not possible, it throws an // exception. //
            //
          • // The put functions copy the value in a cell of the input column // to a cell in the output column. The row numbers of the cells // in the columns can be different. //
          • // The putColumn functions copy the entire contents of the input column // to the output column. The lengths of the columns must be equal. //
          // Each class has its own set of these functions. //
            //
          • // TableColumn::put(...) and // TableColumn::putColumn(...) and // are the most generic. They can be // used if the data types of both input and output column are unknown. // Note that these functions are virtual. //
          • // ScalarColumn::put(...), // ArrayColumn::put(...), // ScalarColumn::putColumn(...), and // ArrayColumn::putColumn(...) // are less generic and therefore potentially more efficient. // The most efficient variants are the ones taking a // Scalar/ArrayColumn<T>, because they require no data type // conversion. //
          //
        // //

        Accessing rows in a Table

        // // Apart from accessing a table column-wise as described in the // previous two sections, it is also possible to access a table row-wise. // The TableRow class makes it possible // to access multiple fields in a table row as a whole. Note that like the // XXColumn classes described above, there is also an ROTableRow class // for access to readonly tables. //

        // On construction of a TableRow object it has to be specified which // fields (i.e. columns) are part of the row. For these fields a // fixed structured TableRecord // object is constructed as part of the TableRow object. The TableRow::get // function will fill this record with the table data for the given row. // The user has access to the record and can use // RecordFieldPtr objects for // speedier access to the record. //

        // The class could be used as shown in the following example. // // // Open the table as readonly and define a row object to contain // // the given columns. // // Note that the function stringToVector is a very convenient // // way to construct a Vector. // // Show the description of the fields in the row. // Table table("Some.table"); // ROTableRow row (table, stringToVector("col1,col2,col3")); // cout << row.record().description(); // // Since the structure of the record is known, the RecordFieldPtr // // objects could be used to allow for easy and fast access to // // the record which is refilled for each get. // RORecordFieldPtr col1(row.record(), "col1"); // RORecordFieldPtr col2(row.record(), "col2"); // RORecordFieldPtr > col3(row.record(), "col3"); // for (uInt i=0; i // The description of TableRow contains some more extensive examples. // //

        Table Selection and Sorting

        // // The result of a select and sort of a table is another table, // which references the original table. This means that an update // of a sorted or selected table results in the update of the original // table. The result is, however, a table in itself, so all table // functions (including select and sort) can be used with it. // Note that a true copy of such a reference table can be made with // the Table::deepCopy function. //

        // Rows or columns can be selected from a table. Columns can be selected // by the // Table::project(...) // function, while rows can be selected by the various // Table operator() functions. // Usually a row is selected by giving a select expression with // TableExprNode // objects. These objects represent the various nodes // in an expression, e.g. a constant, a column, or a subexpression. // The Table function // Table::col(...) // creates a TableExprNode object for a column. The function // Table::key(...) // does the same for a keyword by reading // the keyword value and storing it as a constant in an expression node. // All column nodes in an expression must belong to the same table, // otherwise an exception is thrown. // In the following example we select all rows with RA>10: // // #include // Table table ("Table.name"); // Table result = table (table.col("RA") > 10); // // while in the next one we select rows with RA and DEC in the given // intervals: // // Table result = table (table.col("RA") > 10 // && table.col("RA") < 14 // && table.col("DEC") >= -10 // && table.col("DEC") <= 10); // // The following operators can be used to form arbitrarily // complex expressions: //

          //
        • Relational operators ==, !=, >, >=, < and <=. //
        • Logical operators &&, || and !. //
        • Arithmetic operators +, -, *, /, %, and unary + and -. //
        • Bit operators ^, &, |, and unary ~. //
        • Operator() to take a subsection of an array. //
        // Many functions (like sin, max, conj) can be used in an expression. // Class TableExprNode shows // the available functions. // E.g. // // Table result = table (sin (table.col("RA")) > 0.5); // // Function in can be used to select from a set of values. // A value set can be constructed using class // TableExprNodeSet. // // TableExprNodeSet set; // set.add (TableExprNodeSetElem ("abc")); // set.add (TableExprNodeSetElem ("defg")); // set.add (TableExprNodeSetElem ("h")); // Table result = table (table.col("NAME).in (set)); // // select rows with a NAME equal to abc, // defg, or h. // //

        // You can sort a table on one or more columns containing scalars. // In this example we simply sort on column RA (default is ascending): // // Table table ("Table.name"); // Table result = table.sort ("RA"); // // Multiple // Table::sort(...) // functions exist which allow for more flexible control over the sort order. // In the next example we sort first on RA in descending order // and then on DEC in ascending order: // // Table table ("Table.name"); // Block sortKeys(2); // Block sortOrders(2); // sortKeys(0) = "RA"; // sortOrders(0) = Sort::Descending; // sortKeys(1) = "DEC"; // sortOrders(1) = Sort::Ascending; // Table result = table.sort (sortKeys, sortOrders); // // // Tables stemming from the same root, can be combined in several // ways with the help of the various logical // Table operators (operator|, etc.). //

        Table Query Language

        // The selection and sorting mechanism described above can only be used // in a hard-coded way in a C++ program. // There is, however, another way. Strings containing selection and // sorting commands can be used. // The syntax of these commands is based on SQL and is described in the // Table Query Language (TaQL) note 199. // The language supports UDFs (User Defined Functions) in dynamically // loadable libraries as explained in the note. //
        A TaQL command can be executed with the static function // tableCommand defined in class // TableParse. // //

        Table Concatenation

        // Tables with identical descriptions can be concatenated in a virtual way // using the Table concatenation constructor. Such a Table object behaves // as any other Table object, thus any operation can be performed on it. // An identical description means that the number of columns, the column names, // and their data types of the columns must be the same. The columns do not // need to be ordered in the same way nor to be stored in the same way. //
        Note that if tables have different column names, it is possible // to form a projection (as described in the previous section) first // to make them appear identical. // // Sometimes a MeasurementSet is partitioned, for instance in chunks of // one hour. All those chunks can be virtually concatenated this way. // Note that all tables in the concatenation will be opened, thus one might // run out of file descriptors if there are many chunks. // // Similar to reference tables, it is possible to make a concatenated Table // persistent by using the rename function. It will not copy the // data; only the names of the tables used are written. // // The keywords of a concatenated table are taken from the first table. // It is possible to change or add keywords, but that is not persistent, // not even if the concatenated table is made persistent. //
        The keywords holding subtables can be handled in a special way. // Normally the subtables of the concatenation are the subtables of the first // table are used, but is it possible to concatenate subtables as well by // giving their names in the constructor. // In this way the, say, SYSCAL subtable of a MeasurementSet can be // concatenated as well. // // // Create virtual concatenation of ms0 and ms1. // Block names(2); // names[0] = "ms0"; // names[1] = "ms1"; // // Also concatenate their SYSCAL subtables. // Block subNames(1, "SYSCAL"); // Table concTab (names, subNames); // // //

        Table Iterators

        // // You can iterate through a table in an arbitrary order by getting // a subset of the table consisting of the rows in which the iteration // columns have the same value. // An iterator object is created by constructing a // TableIterator // object with the appropriate column names. // // In the next example we define an iteration on the columns Time and // Baseline. Each iteration step returns a table subset in which Time and // Baseline have the same value. // // // // Iterate over Time and Baseline (by default in ascending order). // // Time is the main iteration order, thus the first column specified. // Table t; // Table tab ("UV_Table.data"); // Block iv0(2); // iv0[0] = "Time"; // iv0[1] = "Baseline"; // // // // Create the iterator. This will prepare the first subtable. // TableIterator iter(tab, iv0); // Int nr = 0; // while (!iter.pastEnd()) { // // Get the first subtable. // // This will contain rows with equal Time and Baseline. // t = iter.table(); // cout << t.nrow() << " "; // nr++; // // Prepare the next subtable with the next Time,Baseline value. // iter.next(); // } // cout << endl << nr << " iteration steps" << endl; // // // You can define more than one iterator on the same table; they operate // independently. // // Note that the result of each iteration step is a table in itself which // references the original table, just as in the case of a sort or select. // This means that the resulting table can be used again in a sort, select, // iteration, etc.. // //

        Table Vectors

        // // A table vector makes it possible to treat a column in a table // as a vector. Almost all operators and functions defined for normal // vectors, are also defined for table vectors. So it is, for instance, // possible to add a constant to a table vector. This has the effect // that the underlying column gets changed. // // You can use the templated class // TableVector // to make a scalar column appear as a (table) vector. // Columns containing arrays or tables are not supported. // The data type of the TableVector object must match the // data type of the column. // A table vector can also hold a normal vector so that (temporary) // results of table vector operations can be handled. // // In the following example we double the data in column COL1 and // store the result in a temporary table vector. // // // Create a table vector for column COL1. // // Note that if the table is readonly, putting data in the table vector // // results in an exception. // Table tab ("Table.data"); // TableVector tabvec(tab, "COL1"); // // Multiply it by a constant. Result is kept in a Vector in memory. // TableVector temp = 2 * tabvec; // // // In the next example we double the data in COL1 and put the result back // in the column. // // // Create a table vector for column COL1. // // It has to be a TableVector to be able to change the column. // Table tab ("Table.data", Table::Update); // TableVector tabvec(tab, "COL1"); // // Multiply it by a constant. // tabvec *= 2; // // //

        Table Keywords

        // // Any number of keyword/value pairs may be attached to the table as a whole, // or to any individual column. They may be freely added, retrieved, // re-assigned, or deleted. They are, in essence, a self-resizing list of // values (any of the primitive types) indexed by Strings (the keyword). // // A table keyword/value pair might be // // Observer = Grote Reber // Date = 10 october 1942 // // Column keyword/value pairs might be // // Units = mJy // Reference Pixel = 320 // // The class // TableRecord // represents the keywords in a table. // It is (indirectly) derived from the standard record classes in the class // Record // //

        Table Description

        // // A table contains a description of itself, which defines the layout of the // columns and the keyword sets for the table and for the individual columns. // It may also define initial keyword sets and default values for the columns. // Such a default value is automatically stored in a cell in the table column, // whenever a row is added to the table. // // The creation of the table descriptor is the first step in the creation of // a new table. The description is part of the table itself, but may also // exist in a separate file. This is useful if you need to create a number // of tables with the same structure; in other circumstances it probably // should be avoided. // // The public classes to set up a table description are: //
          //
        • TableDesc // -- holds the table description. //
        • ColumnDesc // -- holds a generic column description. //
        • ScalarColumnDesc<T> // // -- defines a column containing a scalar value. //
        • ScalarRecordColumnDesc; // // -- defines a column containing a scalar record value. //
        • ArrayColumnDesc<T> // // -- defines a column containing an (in)direct array. //
        // // Here follows a typical example of the construction of a table // description. For more specialized things -- like the definition of a // default data manager -- we refer to the descriptions of the above // mentioned classes. // // // #include // #include // #include // #include // #include // #include // #include // // main() // { // // Create a new table description // // Define a comment for the table description. // // Define some keywords. // ColumnDesc colDesc1, colDesc2; // TableDesc td("tTableDesc", "1", TableDesc::New); // td.comment() = "A test of class TableDesc"; // td.rwKeywordSet().define ("ra" float(3.14)); // td.rwKeywordSet().define ("equinox", double(1950)); // td.rwKeywordSet().define ("aa", Int(1)); // // // Define an integer column ab. // td.addColumn (ScalarColumnDesc ("ab", "Comment for column ab")); // // // Add a scalar integer column ac, define keywords for it // // and define a default value 0. // // Overwrite the value of keyword unit. // ScalarColumnDesc acColumn("ac"); // acColumn.rwKeywordSet().define ("scale" Complex(0,0)); // acColumn.rwKeywordSet().define ("unit", ""); // acColumn.setDefault (0); // td.addColumn (acColumn); // td.rwColumnDesc("ac").rwKeywordSet().define ("unit", "DEG"); // // // Add a scalar string column ad and define its comment string. // td.addColumn (ScalarColumnDesc ("ad","comment for ad")); // // // Now define array columns. // // This one is indirect and has no dimensionality mentioned yet. // td.addColumn (ArrayColumnDesc ("Arr1","comment for Arr1")); // // This one is indirect and has 3-dim arrays. // td.addColumn (ArrayColumnDesc ("A2r1","comment for Arr1",3)); // // This one is direct and has 2-dim arrays with axes length 4 and 7. // td.addColumn (ArrayColumnDesc ("Arr3","comment for Arr1", // IPosition(2,4,7), // ColumnDesc::Direct)); // // // Add columns containing records. // td.addColumn (ScalarRecordColumnDesc ("Rec1")); // } // // //

        Data Managers

        // // Data managers take care of the actual access to the data in a column. // There are two kinds of data managers: //
          //
        1. Storage managers -- // which store the data as such. They can only handle the standard // data types (Bool,...,String) as discussed in the section about the // table properties). //
        2. Virtual column engines // -- which manipulate the data. // An engine could be a simple thing like scaling the data (as done // in classic AIPS to reduce data storage), but it could also be an // elaborate thing like applying corrections on-the-fly. //
          A special engine is VirtualTaQLColumn which can be used to define // the contents of a column by means of a TaQL expression. In particular, // it can be used to define a constant value for the entire column. // But it can also be used to calculate the UVW-coordinates on-the-fly. //
          An engine must be used when storing data objects with a non-standard type. // It has to break down the object into items with standard data types // which can be stored with a storage manager. //
        // In general the user of a table does not need to be aware which // data managers are being used underneath. Only when the table is created // data managers have to be bound to the columns. Thereafter it is // completely transparent. // // Data managers needs to be registered, so they can be found when a table is // opened. All data managers mentioned below are part of the system and // pre-registered. // It is, however, also possible to load data managers on demand. If a data // manager is not registered it is tried to load a shared library with the // part of the data manager name (in lowercase) before a dot or left arrow. // The dot makes it possible to have multiple data managers in a shared library, // while the left arrow is meant for templated data manager classes. //
        E.g. if BitFlagsEngine was not registered, the shared // library libbitflagsengine.so (or .dylib) will be loaded. If // successful, its function register_bitflagsengine() will be // executed which should register the data manager(s). Thereafter it is known // and will be used. For example in a file Register.h and Register.cc: // // // Declare in .h file as C function, so no name mangling is done. // extern "C" { // void register_bitflagsengine(); // } // // Implement in .cc file. // void register_bitflagsengine() // { // BitFlagsEngine::registerClass(); // BitFlagsEngine::registerClass(); // BitFlagsEngine::registerClass(); // } // // There are several functions that can give information which data managers // are used for which columns and to obtain the characteristics and properties // of them. Class RODataManAccessor and derived classes can be used for it // as well as the functions dataManagerInfo and // showStructure in class Table. // //

        Storage Managers

        // // Storage managers are used to store the data contained in the column cells. // At table construction time the binding of columns to storage managers is done. //
        Each storage manager uses one or more files (usually called table.fi_xxx // where i is a sequence number and _xxx is some kind of extension). // Typically several file are used to store the data of the columns of a table. //
        In order to reduce the number of files (and to support large block sizes), // it is possible to have a single container file (a MultiFile) containing all // data files used by the storage managers. Such a file is called table.mf. // Note that the program lsmf can be used to see which // files are contained in a MultiFile. The program tomf can // convert the files in a MultiFile to regular files. //
        At table creation time it is decided if a MultiFile will be used. It // can be done by means of the StorageOption object given to the SetupNewTable // constructor and/or by the aipsrc variables: //
          //
        • table.storage.option which can have the value // 'multifile', 'sepfile' (meaning separate files), or 'default'. // Currently the default is to use separate files. //
        • table.storage.blocksize defines the block size to be // used by a MultiFile. If 0 is given, the file system's block size // will be used. //
        // About all standard storage managers support the MultiFile. // The exception is StManAipsIO, because it is hardly ever used. // // Several storage managers exist, each with its own storage characteristics. // The default and preferred storage manager is StandardStMan. // Other storage managers should only be used if they pay off in // file space (like IncrementalStMan for slowly varying data) // or access speed (like the tiled storage managers for large data arrays). //
        The storage managers store the data in a big or little endian // canonical format. The format can be specified when the table is created. // By default it uses the endian format as specified in the aipsrc variable // table.endianformat which can have the value local, big, // or little. The default is local. //
          //
        1. // StandardStMan // stores all the values in so-called buckets (equally sized chunks // in the file). It requires little memory. //
          It replaces the old StManAipsIO. // //
        2. // IncrementalStMan // uses a storage mechanism resembling "incremental backups". A value // is only stored if it is different from the previous row. It is // very well suited for slowly varying data. //
          The class // ROIncrementalStManAccessor can be used to tune the // behaviour of the IncrementalStMan. It contains functions // to deal with the cache size and to show the behaviour of the cache. // //
        3. // The Tiled Storage Managers // store the data as a tiled hypercube allowing for more or less equally // efficient data access along all main axes. It can be used for // UV-data as well as for image data. // //
        4. // StManAipsIO // uses AipsIO to store the data in the columns. // It supports all table functionality, but its I/O is probably not // as efficient as other storage managers. It also requires that // a large part of the table fits in memory. //
          It should not be used anymore, because it uses a lot of memory // for larger tables and because it is not very robust in case an // application or system crashes. // //
        5. // MemoryStMan // holds the data in memory. It means that data 'stored' with this // storage manager are NOT persistent. //
          This storage manager is primarily meant for tables held in // memory, but it can also be useful for temporary columns in // normal tables. Note, however, that if a table is accessed // concurrently from multiple processes, MemoryStMan data cannot be // synchronized. // //
        6. // @ref dyscostman.DyscoStMan is a class that stores data with lossy // compression. It combines non-linear least-squares quantization and // different kinds of normalizaton. With the typical factor of 4 // compression, the loss in accuracy from lossy compression is // negligable. It should only be used for real (non-simulated) data // that is in a Measurement Set. // The method is described in this article: // https://arxiv.org/abs/1609.02019. // //
        7. // Adios2StMan uses the // ADIOS2 framework to // store and load column data. //
          ADIOS2 has several configurable storage backend itself, and this // flexibility is also available via Adios2StMan. This includes, among other // things, storing compressed data, or choosing a different on-disk formats. //
          This storage manager is also special in that it provides parallel // writing capabilities for MPI processes, so that multiple processes can // write into different sections of the same column concurrently. //
        // // The storage manager framework makes it possible to support arbitrary files // as tables. This has been used in a case where a file is filled // by the data acquisition system of a telescope. The file is simultaneously // used as a table using a dedicated storage manager. The table // system and storage manager provide a sync function to synchronize // the processes, i.e. to make CTDS aware of changes // in the file size (thus in the table size) by the filling process. // // // Not all data managers support all the table functionality. So, the choice // of a data manager can greatly influence the type of operations you can do // on the table as a whole. // For example, if a column uses the tiled storage manager, // it is not possible to delete rows from the table, because that storage // manager will not support deletion of rows. // However, it is always possible to delete all columns of a data // manager in one single call. // // //

        Tiled Storage Manager

        // The Tiled Storage Managers allow one to store the data of // one or more columns in a tiled way. Tiling means // that the data are stored without a preferred order to make access // along the different main axes equally efficient. This is done by // storing the data in so-called tiles (i.e. equally shaped subsets of an // array) to increase data locality. The user can define the tile shape // to optimize for the most frequently used access. //

        // The Tiled Storage Manager has the following properties: //

          //
        • There can be more than one Tiled Storage Manager in // a table; each with its own (unique) name. //
        • Each Tiled Storage Manager can store an // N-dimensional so-called hypercolumn. // Elaborate hypercolumns can be defined using // // TableDesc::defineHypercolumn). //
          Note that defining a hypercolumn is only necessary if it // contains multiple columns or if the TiledDataStMan is used. // It means that in practice it is hardly ever needed to define a // hypercolumn. //
          A hypercolumn consists of up to three types of columns: //
          //
          Data columns //
          contain the data to be stored in a tiled way. This will // be done in tiled hypercubes. // There must be at least one data column. //
          For example: a table contains UV-data with // data columns "Visibility" and "Weight". //
          Coordinate columns //
          define the world coordinates of the pixels in the data columns. // Coordinate columns are optional, but if given there must // be N coordinate columns for an N-dimensional hypercolumn. //
          // For example: the data in the example above is 4-dimensional // and has coordinate columns "Time", "Baseline", "Frequency", // and "Polarization". //
          Id columns //
          are needed if TiledDataStMan is used. // Different rows in the data columns can be stored in different // hypercubes. The values in the id column(s) uniquely identify // the hypercube a row is stored in. //
          // For example: the line and continuum data in a MeasurementSet // table need to be stored in 2 different hypercubes (because // their shapes are different (see below)). A column containing // the type (line or continuum) has to be used as an id column. //
          //
        • If multiple data columns are used, the shape of their data // must be conforming in each individual row. // If data in different rows have different shapes, they must be // stored in different hypercubes, because a hypercube can only hold // data with conforming shapes. //
          // Thus in the example above, rows with line data will have conforming // shapes and can be stored in one hypercube. The continuum data // will have another shape and can be stored in another hypercube. //
          // The storage manager keeps track of the mapping of rows to/from // hypercubes. //
        • Each hypercube can be tiled in its own way. It is not required // that an integer number of tiles fits in the hypercube. The last // tiles will be padded as needed. //
        • The last axis of a hypercube can be extensible. This means that // the size of that axis does not need to be defined when the // hypercube is defined in the storage manager. Instead, the hypercube // can be extended when another chunk of data has to be stored. // This can be very useful in, for example, a (quasi-)realtime // environment where the size of the time axis is not known. //
        • If coordinate columns are defined, they describe the coordinates // of the axes of the hypercubes. Each hypercube has its own set of // coordinates. //
        • Data and id columns have to be stored with the Tiled // Storage Manager. However, coordinate columns do not need to be // stored with the Tiled Storage Manager. // Especially in the case where the coordinates for a hypercube axis // are varying (i.e. dependent on other axes), another storage manager // has to be used (because the Tiled Storage Manager can only // hold constant coordinates). //
        //

        // The following Tiled Storage Managers are available: //

        //
        TiledShapeStMan //
        can be seen as a specialization of TiledDataStMan // by using the array shape as the id value. // Similarly to TiledDataStMan it can maintain multiple // hypercubes and store multiple rows in a hypercube, but it is // easier to use, because the special addHypercube and // extendHypercube functions are not needed. // An hypercube is automatically added when a new array shape is // encountered. //
        // This storage manager could be used for a table with a column // containing line and continuum data, which will result // in 2 hypercubes. //
        TiledCellStMan //
        creates (automatically) a new hypercube for each row. // Thus each row of the hypercolumn is stored in a separate hypercube. // Note that the row number serves as the id value. So an id column // is not needed, although there are multiple hypercubes. //
        // This storage manager is meant for tables where the data arrays // in the different rows are not accessed together. One can think // of a column containing images. Each row contains an image and // only one image is shown at a time. //
        TiledColumnStMan //
        creates one hypercube for the entire hypercolumn. Thus all cells // in the hypercube have to have the same shape and therefore this // storage manager is only possible if all columns in the hypercolumn // have the attribute FixedShape. //
        // This storage manager could be used for a table with a column // containing images for the Stokes parameters I, Q, U, and V. // By storing them in one hypercube, it is possible to retrieve // the 4 Stokes values for a subset of the image or for an individual // pixel in a very efficient way. //
        TiledDataStMan //
        allows one to control the creation and extension of hypercubes. // This is done by means of the class // // TiledDataStManAccessor. // It makes it possible to store, say, row 0-9 in hypercube A, // row 10-34 in hypercube B, row 35-54 in hypercube A again, etc.. //
        // The drawback of this storage manager is that its hypercubes are not // automatically extended when adding new rows. The special functions // addHypercube and extendHypercube have to be // used making it somewhat tedious to use. // Therefore this storage manager may become obsolete in the near future. //
        // The Tiled Storage Managers have 3 ways to access and cache the data. // Class TSMOption can be used to setup an // access choice and use it in a Table constructor. //
          //
        • The old way (the only way until January 2010) uses a cache // of its own to keep tiles that might need to be reused. It will always // access entire tiles, even if only a small part is needed. // It is possible to define a maximum cache size. The description of class // ROTiledStManAccessor // contains a discussion about the effect of defining a maximum cache // size. //
        • Memory-mapping the data files. In this way the operating system // takes care of the IO and caching. However, the limited address space // may preclude using it for large tables on 32-bit systems. //
        • Use buffered IO and let the kernel's file cache take care of caching. // It will access the data in chunks of the given buffer size, so the // entire tile does not need to be accessed if only a small part is // needed. //
        // Apart from reading, all access ways described above can also handle writing // and extending tables. They create fully equal files. Both little and big // endian data can be read or written. // //

        Virtual Column Engines

        // // Virtual column engines are used to implement the virtual (i.e. // calculated-on-the-fly) columns. CTDS provides // an abstract base class (or "interface class") // VirtualColumnEngine // that specifies the protocol for these engines. // The programmer must derive a concrete class to implement // the application-specific virtual column. //

        // For example: the programmer // needs a column in a table which is the difference between two other // columns. (Perhaps these two other columns are updated periodically // during the execution of a program.) A good way to handle this would // be to have a virtual column in the table, and write a virtual column // engine which knows how to calculate the difference between corresponding // cells of the two other columns. So the result is that accessing a // particular cell of the virtual column invokes the virtual column engine, // which then gets the values from the other two columns, and returns their // difference. This particular example could be done using // VirtualTaQLColumn. //

        // Several virtual column engines exist: //

          //
        1. The class // VirtualTaQLColumn // makes it possible to define a column as an arbitrary expression of // other columns. It uses the TaQL // CALC command. The virtual column can be a scalar or an array and // can have one of the standard data types supported by CTDS. //
        2. The class // BitFlagsEngine // maps an integer bit flags column to a Bool column. A read and write mask // can be defined telling which bits to take into account when mapping // to and from Bool (thus when reading or writing the Bool). //
        3. The class // CompressFloat // compresses a single precision floating point array by scaling the // values to shorts (16-bit integer). //
        4. The class // CompressComplex // compresses a single precision complex array by scaling the // values to shorts (16-bit integer). In fact, the 2 parts of the complex // number are combined to an 32-bit integer. //
        5. The class // CompressComplexSD // does the same as CompressComplex, but optimizes for the case where the // imaginary part is zero (which is often the case for Single Dish data). //
        6. The double templated class // ScaledArrayEngine // scales the data in an array from, for example, // float to short before putting it. //
        7. The double templated class // MappedArrayEngine // converts the data from one data type to another. Sometimes it might be // needed to store the residual data in an MS in double precision. // Because the imaging task can only handle single precision, this enigne // can be used to map the data from double to single precision. //
        8. The double templated class // RetypedArrayEngine // converts the data from one data type to another with the possibility // to reduce the number of dimensions. For example, it can be used to // store an 2-d array of StokesVector objects as a 3-d array of floats // by treating the 4 data elements as an extra array axis. If the // StokesVector class is simple, it can be done very efficiently. //
        9. The class // // ForwardColumnEngine // forwards the gets and puts on a row in a column to the same row // in a column with the same name in another table. This provides // a virtual copy of the referenced column. //
        10. The class // // ForwardColumnIndexedRowEngine // is similar to ForwardColumnEngine.. // However, instead of forwarding it to the same row it uses a // a column to map its row number to a row number in the referenced // table. In this way multiple rows can share the same data. // This data manager only allows for get operations. //
        11. The calibration module has implemented a virtual column engine // to do on-the-fly calibration in a transparent way. //
        // To handle arbitrary data types the templated abstract base class // VSCEngine // has been written. An example of how to use this class can be // found in the demo program dVSCEngine.cc. // //

        Table locking and synchronization

        // // Multiple concurrent readers and writers (also via NFS) of a // table are supported by means of a locking/synchronization mechanism. // This mechanism is not very sophisticated in the sense that it is // very coarsely grained. When locking, the entire table gets locked. // A special lock file is used to lock the table. This lock file also // contains some synchronization data. //

        // Five ways of locking are supported (see class // TableLock): //

        //
        TableLock::PermanentLocking(Wait) //
        locks the table permanently (from open till close). This means // that one writer OR multiple readers are possible. //
        TableLock::AutoLocking //
        does the locking automatically. This is the default mode. // This mode makes it possible that a table is shared amongst // processes without the user needing to write any special code. // It also means that a lock is only released when needed. //
        TableLock::AutoNoReadLocking //
        is similar to AutoLocking. However, no lock is acquired when // reading the table making it possible to read the table while // another process holds a write-lock. It also means that for read // purposes no automatic synchronization is done when the table is // updated in another process. // Explicit synchronization can be done by means of the function // Table::resync. //
        TableLock::UserLocking //
        requires that the programmer explicitly acquires and releases // a lock on the table. This makes some kind of transaction // processing possible. E.g. set a write lock, add a row, // write all data into the row and release the lock. // The Table functions lock and unlock // have to be used to acquire and release a (read or write) lock. //
        TableLock::UserNoReadLocking //
        is similar to UserLocking. However, similarly to AutoNoReadLocking // no lock is needed to read the table. //
        TableLock::NoLocking //
        does not use table locking. It is the responsibility of the // user to ensure that no concurrent access is done on the same // bucket or tile in a storage manager, otherwise a table might // get corrupted. //
        This mode is always used if Casacore is built with // -DAIPS_TABLE_NOLOCKING. //
        // Synchronization of the processes accessing the same table is done // by means of the lock file. When a lock is released, the storage // managers flush their data into the table files. Some synchronization data // is written into the lock file telling the new number of table rows // and telling which storage managers have written data. // This information is read when another process acquires the lock // and is used to determine which storage managers have to refresh // their internal caches. //
        Note that for the NoReadLocking modes (see above) explicit // synchronization might be needed using Table::resync. //

        // The function Table::hasDataChanged can be used to check // if a table is (being) changed by another process. In this way // a program can react on it. E.g. the table browser can refresh its // screen when the underlying table is changed. //

        // In general the default locking option will do. // From the above it should be clear that heavy concurrent access // results in a lot of flushing, thus will have a negative impact on // performance. If uninterrupted access to a table is needed, // the PermanentLocking option should be used. // If transaction-like processing is done (e.g. updating a table // containing an observation catalogue), the UserLocking // option is probably best. //

        // Creation or deletion of a table is not possible if that table // is still open in another process. The function // Table::isMultiUsed() can be used to check if a table // is open in other processes. //
        // The function TableUtil::deleteTable should be used to delete // a table. Before deleting the table it ensures that it is writable // and that it is not open in the current or another process. //

        // The following example wants to read the table uninterrupted, thus it uses // the PermanentLocking option. It also wants to wait // until the lock is actually acquired. // Note that the destructor closes the table and releases the lock. // // // Open the table (readonly). // // Acquire a permanent (read) lock. // // It waits until the lock is acquired. // Table tab ("some.name", // TableLock(TableLock::PermanentLockingWait)); // // // The following example uses the automatic locking.. // It tells the system to check about every 20 seconds if another // process wants access to the table. // // // Open the table (readonly). // Table tab ("some.name", // TableLock(TableLock::AutoLocking, 20)); // // // The following example gets data (say from a GUI) and writes it // as a row into the table. The lock the table as little as possible // the lock is acquired just before writing and released immediately // thereafter. // // // Open the table (writable). // Table tab ("some.name", // TableLock(TableLock::UserLocking), // Table::Update); // while (True) { // get input data // tab.lock(); // Acquire a write lock and wait for it. // tab.addRow(); // write data into the row // tab.unlock(); // Release the lock. // } // // // The following example deletes a table if it is not used in // another process. // // Table tab ("some.name"); // if (! tab.isMultiUsed()) { // tab.markForDelete(); // } // // //

        Table lookup based on a key

        // // Class ColumnsIndex offers the // user a means to find the rows matching a given key or key range. // It is a somewhat primitive replacement of a B-tree index and in the // future it may be replaced by a proper B+-tree implementation. //

        // The ColumnsIndex class makes it possible to build an // in-core index on one or more columns. Looking a key or key range // is done using a binary search on that index. It returns a vector // containing the row numbers of the rows matching the key (range). //

        // The class is not capable of tracing changes in the underlying column(s). // It detects a change in the number of rows and updates the index // accordingly. However, it has to be told explicitly when a value // in the underlying column(s) changes. //

        // The following example shows how the class can be used. // // Suppose one has an antenna table with key ANTENNA. // // // Open the table and make an index for column ANTENNA. // Table tab("antenna.tab") // ColumnsIndex colInx(tab, "ANTENNA"); // // Make a RecordFieldPtr for the ANTENNA field in the index key record. // // Its data type has to match the data type of the column. // RecordFieldPtr antFld(colInx.accessKey(), "ANTENNA"); // // Now loop in some way and find the row for the antenna // // involved in that loop. // Bool found; // while (...) { // // Fill the key field and get the row number. // // ANTENNA is a unique key, so only one row number matches. // // Otherwise function getRowNumbers had to be used. // *antFld = antenna; // uInt antRownr = colInx.getRowNumber (found); // if (!found) { // cout << "Antenna " << antenna << " is unknown" << endl; // } else { // // antRownr can now be used to get data from that row in // // the antenna table. // } // } // // // ColumnsIndex itself contains a more // advanced example. It shows how to use a private compare function // to adjust the lookup if the index does not contain single // key values, but intervals instead. This is useful if a row in // a (sub)table is valid for, say, a time range instead of a single // timestamp. // //

        Performance and robustness considerations

        // // CTDS resembles a database system, but it is not as robust. // It lacks the transaction and logging facilities common to data base systems. // It means that in case of a crash data might be lost. // To reduce the risk of data loss to // a minimum, it is advisable to regularly do a flush, optionally // with an fsync to ensure that all data are really written. // However, that can degrade the performance because it involves extra writes. // So one should find the right balance between robustness and performance. // // To get a good feeling for the performance issues, it is important to // understand some of the internals of CTDS. //
        The storage managers drive the performance. All storage managers use // buckets (called tiles for the TiledStMan) which contain the data. // All IO is done by bucket. The bucket/tile size is defined when creating // the storage manager objects. Sometimes the default will do, but usually // it is better to set it explicitly. // // It is best to do a flush when a tile is full. // For example:
        // When creating a MeasurementSet containing N antennae (thus N*(N-1) baselines // or N*(N+1) if auto-correlations are stored as well) it makes sense to // store, say, N/2 rows in a tile and do a flush each time all baselines // are written. In that way tiles are fully filled when doing the flush, so // no extra IO is involved. //
        Here is some code showing this when creating a MeasurementSet. // The code should speak for itself. // // MS* createMS (const String& msName, int nrchan, int nrant) // { // // Get the MS main default table description. // TableDesc td = MS::requiredTableDesc(); // // Add the data column and its unit. // MS::addColumnToDesc(td, MS::DATA, 2); // td.rwColumnDesc(MS::columnName(MS::DATA)).rwKeywordSet(). // define("UNIT","Jy"); // // Store the DATA and FLAG column in two separate files. // // In this way accessing FLAG only is much cheaper than // // when combining DATA and FLAG. // // All data have the same shape, thus use TiledColumnStMan. // // Also store UVW with TiledColumnStMan. // Vector tsmNames(1); // tsmNames[0] = MS::columnName(MS::DATA); // td.rwColumnDesc(tsmNames[0]).setShape (IPosition(2,itsNrCorr,itsNrFreq)); // td.defineHypercolumn("TiledData", 3, tsmNames); // tsmNames[0] = MS::columnName(MS::FLAG); // td.rwColumnDesc(tsmNames[0]).setShape (IPosition(2,itsNrCorr,itsNrFreq)); // td.defineHypercolumn("TiledFlag", 3, tsmNames); // tsmNames[0] = MS::columnName(MS::UVW); // td.defineHypercolumn("TiledUVW", 2, tsmNames); // // Setup the new table. // SetupNewTable newTab(msName, td, Table::New); // // Most columns vary slowly and use the IncrStMan. // IncrementalStMan incrStMan("ISMData"); // // A few columns use he StandardStMan (set an appropriate bucket size). // StandardStMan stanStMan("SSMData", 32768); // // Store all pol and freq and some rows in a single tile. // // autocorrelations are written, thus in total there are // // nrant*(nrant+1)/2 baselines. Ensure a baseline takes up an // // integer number of tiles. // TiledColumnStMan tiledData("TiledData", // IPosition(3,4,nchan,(nrant+1)/2)); // TiledColumnStMan tiledFlag("TiledFlag", // IPosition(3,4,nchan,8*(nrant+1)/2)); // TiledColumnStMan tiledUVW("TiledUVW", IPosition(2,3,)); // IPosition(2,3,nrant*(nrant+1)/2)); // newTab.bindAll (incrStMan); // newTab.bindColumn(MS::columnName(MS::ANTENNA1),stanStMan); // newTab.bindColumn(MS::columnName(MS::ANTENNA2),stanStMan); // newTab.bindColumn(MS::columnName(MS::DATA),tiledData); // newTab.bindColumn(MS::columnName(MS::FLAG),tiledFlag); // newTab.bindColumn(MS::columnName(MS::UVW),tiledUVW); // // Create the MS and its subtables. // // Get access to its columns. // MS* msp = new MeasurementSet(newTab); // // Create all subtables. // // Do this after the creation of optional subtables, // // so the MS will know about those optional sutables. // msp->createDefaultSubtables (Table::New); // return msp; // } // //

        Some more performance considerations

        // Which storage managers to use and how to use them depends heavily on // the type of data and the access patterns to the data. Here follow some // guidelines: //
          //
        1. Scalar data can be stored with the StandardStMan (SSM) or // IncrementalStMan (ISM). For slowly varying data (e.g. the TIME column // in a MeasurementSet) it is best to use the ISM. Otherwise the SSM. // Note that very long strings (longer than the bucketsize) can only // be stored with the SSM. //
        2. Any number of storage managers can be used. In fact, each column // can have a storage manager of its own resulting in column-wise // stored data which is more and more used in data base systems. // In that way a query or sort on that column is very fast, because // the buckets to read only contain data of that column. // In practice one can decide to combine a few frequently used columns // in a storage manager. //
        3. Array data can be stored with any column manager. Small fixed size // arrays can be stored directly with the SSM // (or ISM if not changing much). // However, they can also be stored with a TiledStMan (TSM) as shown // for the UVW column in the example above. //
          Large arrays should usually be stored with a TSM. However, // if it must be possible to change the shape of an array after it // was stored, the SSM (or ISM) must be used. Note that in that // case a lot of disk space can be wasted, because the SSM and ISM // store the array data at the end of the file if the array got // bigger and do not reuse the old space. The only way to // reclaim it is by making a deep copy of the entire table. //
        4. If an array is stored with a TSM, it is important to decide // which TSM to use. //
            //
          1. The TiledColumnStMan is the most efficient, but only suitable // for arrays having the same shape in the entire column. //
          2. The TiledShapeStMan is suitable for columns where the arrays // can have a few shapes. //
          3. The TiledCellStMan is suitable for columns where the arrays // can have many different shapes. //
          // This is discussed in more detail // above. //
        5. If storing an array with a TSM, it can be very important to // choose the right tile shape. Not only does this define the size // of a tile, but it also defines if access in other directions // than the natural direction can be fast. It is also discussed in // more detail above. //
        6. Columns can be combined in a single TiledStMan. For instance, combining DATA // and FLAG is advantageous if FLAG is always used with DATA. However, if FLAG // is used on its own (e.g. in combination with CORRECTED_DATA), it is better // to separate them, otherwise tiles containing FLAG also contain DATA making the // tiles much bigger, thus more expensive to access. //
        // // //

        IO Tracing

        // // Several forms of tracing can be done to see how the Table I/O performs. //
          //
        • On Linux/UNIX systems the strace command can be used to // collect trace information about the physical IO. //
        • The function showCacheStatistics in class // TiledStManAccessor can be used to show the number of actual reads // and writes and the percentage of cache hits. //
        • The software has some options to trace the operations done on // tables. It is possible to specify the columns and/or the operations // to be traced. The following aipsrc variables can be used. //
            //
          • table.trace.filename specifies the file to write the // trace output to. If not given or empty, no tracing will be done. // The file name can contain environment variables or a tilde. //
          • table.trace.operation specifies the operations to be // traced. It is a string containing s, r, and/or w where // s means tracing RefTable construction (selection/sort), // r means column reads, and w means column writes. // If empty, only the high level table operations (open, create, close) // will be traced. //
          • table.trace.columntype specifies the types of columns to // be traced. It is a string containing the characters s, a, and/or r. // s means all scalar columns, a all array columns, and r all record // columns. If empty and if table.trace.column is empty, // its default value is a. //
          • table.trace.column specifies names of columns to be // traced. Its value can be one or more glob-like patterns separated // by commas without any whitespace. The default is empty. // For example: // // table.trace.column: *DATA,FLAG,WEIGHT* // // to trace all DATA, the FLAG, and all WEIGHT columns. //
          // The trace output is a text file with the following columns // separated by a space. //
            //
          • The UTC time the trace line was written (with msec accuracy). //
          • The operation: n(ew), o(pen), c(lose), t(able), r(ead), w(rite), // s(election/sort/iter), p(rojection). // t means an arbitrary table operation as given in the name column. //
          • The table-id (as t=i) given at table creation (new) or open. //
          • The table name, column name, or table operation // (as *oper*). // *reftable* means that the operation is on a RefTable // (thus result of selection, sort, projection, or iteration). //
          • The row or rows to access (* means all rows). // Multiple rows are given as a series of ranges like s:e:i,s:e:i,... // where e and i are only given if applicable (default i is 1). // Note that e is inclusive and defaults to s. //
          • The optional array shape to access (none means scalar). // In case multiple rows are accessed, the last shape value is the // number of rows. //
          • The optional slice of the array in each row as [start][end][stride]. //
          // Shape, start, end, and stride are given in Fortran-order as // [n1,n2,...]. //
        // //

        Applications to inspect/manipulate a table

        //
          //
        • showtableinfo shows the structure of a table. It can show: //
            //
          • the columns and their format (optionally sorted on name) //
          • the data managers used to store the column data //
          • the table and/or column keywords and their values //
          • recursively the same info of the subtables //
          //
        • showtablelock if a table is locked or opened and by // which process. //
        • lsmf shows the virtual files contained in a MultiFile. //
        • tomf copies the given files to a MultiFile. //
        • taql can be used to query a table using the // Table Query Language (TaQL). //
        // //
        // } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/000077500000000000000000000000001476623553700160105ustar00rootroot00000000000000casacore-3.7.1/tables/Tables/ArrColData.cc000066400000000000000000000276431476623553700203070ustar00rootroot00000000000000//# ArrColData.cc: Access to a table column containing arrays //# Copyright (C) 1994,1995,1996,1997,1998,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ArrayColumnData::ArrayColumnData (const ArrayColumnDescBase* cd, ColumnSet* csp) : PlainColumn (cd, csp), shapeColDef_p(False), shapeCol_p () { if (cd->shape().nelements() > 0) { ArrayColumnData::setShapeColumn (cd->shape()); } checkValueLength_p = (columnDesc().dataType() == TpString && columnDesc().maxLength() > 0); } ArrayColumnData::~ArrayColumnData() {} //# Create the data manager column for this column. //# The shape of a fixed shape array has to be defined in advance. void ArrayColumnData::createDataManagerColumn() { //# Create the data manager column (direct or indirect). if ((colDescPtr_p->options() & ColumnDesc::Direct) == ColumnDesc::Direct) { dataColPtr_p = dataManPtr_p->createDirArrColumn (colDescPtr_p->name(), colDescPtr_p->dataType(), colDescPtr_p->dataTypeId()); }else{ dataColPtr_p = dataManPtr_p->createIndArrColumn (colDescPtr_p->name(), colDescPtr_p->dataType(), colDescPtr_p->dataTypeId()); } //# Check if the shape is defined in case fixed. //# Pass it to the created data manager column. if ((colDescPtr_p->options() & ColumnDesc::FixedShape) == ColumnDesc::FixedShape) { if (!shapeColDef_p) { throw (TableInvOper ("ArrayColumnData::createDataManagerColumn; " "shape of FixedShape array in column " + colDescPtr_p->name() + " not defined")); } dataColPtr_p->setFixedShapeColumn (shapeCol_p); } //# Set the maximum length of an item. dataColPtr_p->setMaxLength (colDescPtr_p->maxLength()); } //# Initialize the array in the given rows. //# This removes an array if present. void ArrayColumnData::initialize (rownr_t, rownr_t) {} uInt ArrayColumnData::ndimColumn() const { Int ndim = columnDesc().ndim(); return (ndim > 0 ? ndim : shapeCol_p.nelements()); } IPosition ArrayColumnData::shapeColumn() const { return shapeCol_p; } void ArrayColumnData::setShapeColumn (const IPosition& shp) { if (shapeColDef_p) { if (shp != shapeCol_p) { throw (TableInvOper ("ArrayColumnData: change in shape of FixedShape array" " of column " + colDescPtr_p->name())); } } if (columnDesc().ndim() > 0) { if (Int(shp.nelements()) != columnDesc().ndim()) { throw (TableInvOper ("ArrayColumnData: mismatch in #dim of FixedShape array shape" " of column " + colDescPtr_p->name())); } } shapeCol_p = shp; shapeColDef_p = True; } Bool ArrayColumnData::isDefined (rownr_t rownr) const { return dataColPtr_p->isShapeDefined(rownr); } uInt ArrayColumnData::ndim (rownr_t rownr) const { return dataColPtr_p->ndim(rownr); } IPosition ArrayColumnData::shape (rownr_t rownr) const { return dataColPtr_p->shape(rownr); } IPosition ArrayColumnData::tileShape (rownr_t rownr) const { return dataColPtr_p->tileShape(rownr); } void ArrayColumnData::setShape (rownr_t rownr, const IPosition& shp) { checkShape (shp); checkWriteLock (True); dataColPtr_p->setShape (rownr, shp); autoReleaseLock(); } void ArrayColumnData::setShape (rownr_t rownr, const IPosition& shp, const IPosition& tileShp) { checkShape (shp); checkWriteLock (True); dataColPtr_p->setShapeTiled (rownr, shp, tileShp); autoReleaseLock(); } Bool ArrayColumnData::canChangeShape() const { return dataColPtr_p->canChangeShape(); } void ArrayColumnData::getArray (rownr_t rownr, ArrayBase& array) const { if (rtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'r', rownr, array.shape()); } checkReadLock (True); dataColPtr_p->getArrayV (rownr, array); autoReleaseLock(); } void ArrayColumnData::getSlice (rownr_t rownr, const Slicer& ns, ArrayBase& array) const { if (rtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'r', rownr, array.shape(), ns.start(), ns.end(), ns.stride()); } checkReadLock (True); dataColPtr_p->getSliceV (rownr, ns, array); autoReleaseLock(); } void ArrayColumnData::putArray (rownr_t rownr, const ArrayBase& array) { if (wtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'w', rownr, array.shape()); } if (checkValueLength_p) { checkValueLength (static_cast*>(&array)); } checkWriteLock (True); dataColPtr_p->putArrayV (rownr, array); autoReleaseLock(); } void ArrayColumnData::putSlice (rownr_t rownr, const Slicer& ns, const ArrayBase& array) { if (wtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'w', rownr, array.shape(), ns.start(), ns.end(), ns.stride()); } if (checkValueLength_p) { checkValueLength (static_cast*>(&array)); } checkWriteLock (True); dataColPtr_p->putSliceV (rownr, ns, array); autoReleaseLock(); } //# Get or put the column by iterating through the array and getting the //# column array for each row. void ArrayColumnData::getArrayColumn (ArrayBase& array) const { if (rtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'r', array.shape()); } checkReadLock (True); dataColPtr_p->getArrayColumnV (array); autoReleaseLock(); } void ArrayColumnData::getArrayColumnCells (const RefRows& rownrs, ArrayBase& array) const { if (rtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'r', rownrs, array.shape()); } checkReadLock (True); dataColPtr_p->getArrayColumnCellsV (rownrs, array); autoReleaseLock(); } void ArrayColumnData::getColumnSlice (const Slicer& ns, ArrayBase& array) const { if (rtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'r', array.shape(), ns.start(), ns.end(), ns.stride()); } checkReadLock (True); dataColPtr_p->getColumnSliceV (ns, array); autoReleaseLock(); } void ArrayColumnData::getColumnSliceCells (const RefRows& rownrs, const Slicer& ns, ArrayBase& array) const { if (rtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'r', rownrs, array.shape(), ns.start(), ns.end(), ns.stride()); } checkReadLock (True); dataColPtr_p->getColumnSliceCellsV (rownrs, ns, array); autoReleaseLock(); } void ArrayColumnData::putArrayColumn (const ArrayBase& array) { if (wtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'w', array.shape()); } if (checkValueLength_p) { checkValueLength (static_cast*>(&array)); } checkWriteLock (True); dataColPtr_p->putArrayColumnV (array); autoReleaseLock(); } void ArrayColumnData::putArrayColumnCells (const RefRows& rownrs, const ArrayBase& array) { if (wtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'w', rownrs, array.shape()); } if (checkValueLength_p) { checkValueLength (static_cast*>(&array)); } checkWriteLock (True); dataColPtr_p->putArrayColumnCellsV (rownrs, array); autoReleaseLock(); } void ArrayColumnData::putColumnSlice (const Slicer& ns, const ArrayBase& array) { if (wtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'w', array.shape(), ns.start(), ns.end(), ns.stride()); } if (checkValueLength_p) { checkValueLength (static_cast*>(&array)); } checkWriteLock (True); dataColPtr_p->putColumnSliceV (ns, array); autoReleaseLock(); } void ArrayColumnData::putColumnSliceCells (const RefRows& rownrs, const Slicer& ns, const ArrayBase& array) { if (wtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'w', rownrs, array.shape(), ns.start(), ns.end(), ns.stride()); } if (checkValueLength_p) { checkValueLength (static_cast*>(&array)); } checkWriteLock (True); dataColPtr_p->putColumnSliceCellsV (rownrs, ns, array); autoReleaseLock(); } //# Check if the array shape is set correctly. //# It is only possible for non-FixedShape arrays. //# Its dimensionality must match the possible #dim in the column description. void ArrayColumnData::checkShape (const IPosition& shape) const { if ((columnDesc().options() & ColumnDesc::FixedShape) != ColumnDesc::FixedShape) { if (columnDesc().ndim() > 0) { if (Int(shape.nelements()) != columnDesc().ndim()) { throw (TableInvOper ("ArrayColumn::setShape: mismatch in #dim of array" " of column " + colDescPtr_p->name())); } } } } //# It was felt that putstart takes too much space, so therefore //# the version is put "manually". void ArrayColumnData::putFileDerived (AipsIO& ios) { ios << (uInt)1; // class version 1 ios << dataManPtr_p->sequenceNr(); ios << shapeColDef_p; if (shapeColDef_p) { ios << shapeCol_p; } } void ArrayColumnData::getFileDerived (AipsIO& ios, const ColumnSet& colset) { uInt version; ios >> version; uInt seqnr; ios >> seqnr; ios >> shapeColDef_p; if (shapeColDef_p) { ios >> shapeCol_p; } dataManPtr_p = colset.getDataManager (seqnr); createDataManagerColumn(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/ArrColData.h000066400000000000000000000247641476623553700201520ustar00rootroot00000000000000//# ArrColData.h: Access to a table column containing arrays //# Copyright (C) 1994,1995,1996,1998,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_ARRCOLDATA_H #define TABLES_ARRCOLDATA_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class ColumnSet; class ArrayColumnDescBase; class AipsIO; // // Access to a table column containing arrays // // // // // //# Classes you should understand before using this one. //
      • PlainColumn //
      • ArrayColumnDesc //
      • Table // // // ArrayColumnData represents a table column containing array data. // // // The class ArrayColumnData is derived from PlainColumn. // It implements the virtual functions accessing a table column // containing arrays with an arbitrary data type. // Both direct and indirect arrays are supported. // // It is possible to access an array or a subsection of it in an // individual cell (i.e. table row) or in the entire column. // The functions accessing the entire column are implemented by // looping over the individual cells. // // The main task of this class is to communicate with the data manager // column object. This consists of: //
          //
        • Binding itself to a data manager. //
        • Letting the data manager create its column object and // setting the shape for direct arrays. //
        • Closing the data manager column object (in putFileDerived). //
        • Reconstructing the data manager object for an existing table // (in getFileDerived). //
        • Transferring get/put calls to the data manager column object. //
        // // The class is hidden from the user by the envelope class ArrayColumn. // It used directly, it should be done with care. It assumes that the // arrays in the various get and put functions have the correct length. // ArrayColumn does that check. //
        // //# A List of bugs, limitations, extensions or planned refinements. //
      • support tiling // class ArrayColumnData : public PlainColumn { public: // Construct an array column object from the given description // in the given column set. // This constructor is used by ArrayColumnDescBase::makeColumn. ArrayColumnData (const ArrayColumnDescBase*, ColumnSet*); ~ArrayColumnData(); // Copy constructor cannot be used. ArrayColumnData (const ArrayColumnData&) = delete; // Assignment cannot be used. ArrayColumnData& operator= (const ArrayColumnData&) = delete; // Ask the data manager if the shape of an existing array can be changed. virtual Bool canChangeShape() const; // Initialize the rows from startRownr till endRownr (inclusive) // with the default value defined in the column description (if defined). void initialize (rownr_t startRownr, rownr_t endRownr); // Get the global #dimensions of an array (ie. for all rows). uInt ndimColumn() const; // Get the global shape of an array (ie. for all rows). IPosition shapeColumn() const; // Set shape of all arrays in the column. // It can only be used for direct arrays. void setShapeColumn (const IPosition& shape); // Get the #dimensions of an array in a particular cell. // If the cell does not contain an array, 0 is returned. uInt ndim (rownr_t rownr) const; // Get the shape of an array in a particular cell. // If the cell does not contain an array, an empty IPosition is returned. IPosition shape(rownr_t rownr) const; // Get the tile shape of an array in a particular cell. // If the cell does not contain an array, an empty IPosition is returned. IPosition tileShape(rownr_t rownr) const; // Set dimensions of array in a particular cell. // void setShape (rownr_t rownr, const IPosition& shape); // The shape of tiles in the array can also be defined. void setShape (rownr_t rownr, const IPosition& shape, const IPosition& tileShape); // // Test if the given cell contains an array. Bool isDefined (rownr_t rownr) const; // Get the array from a particular cell. // The length of the array given by ArrayBase must match // the actual length. This is checked by ArrayColumn. void getArray (rownr_t rownr, ArrayBase& arrayPtr) const; // Get a slice of an N-dimensional array in a particular cell. // The length of the array given by ArrayBase must match // the actual length. This is checked by ArrayColumn. void getSlice (rownr_t rownr, const Slicer&, ArrayBase& arrayPtr) const; // Get the array of all values in a column. // If the column contains n-dim arrays, the resulting array is (n+1)-dim. // The arrays in the column have to have the same shape in all cells. // The length of the array given by ArrayBase must match // the actual length. This is checked by ArrayColumn. void getArrayColumn (ArrayBase& arrayPtr) const; // Get the array of some values in a column. // If the column contains n-dim arrays, the resulting array is (n+1)-dim. // The arrays in the column have to have the same shape in all cells. // The length of the array given by ArrayBase must match // the actual length. This is checked by ArrayColumn. void getArrayColumnCells (const RefRows& rownrs, ArrayBase& arrayPtr) const; // Get subsections from all arrays in the column. // If the column contains n-dim arrays, the resulting array is (n+1)-dim. // The arrays in the column have to have the same shape in all cells. // The length of the array given by ArrayBase must match // the actual length. This is checked by ArrayColumn. void getColumnSlice (const Slicer&, ArrayBase& arrayPtr) const; // Get subsections from some arrays in the column. // If the column contains n-dim arrays, the resulting array is (n+1)-dim. // The arrays in the column have to have the same shape in all cells. // The length of the array given by ArrayBase must match // the actual length. This is checked by ArrayColumn. void getColumnSliceCells (const RefRows& rownrs, const Slicer&, ArrayBase& arrayPtr) const; // Put the value in a particular cell. // The length of the array given by ArrayBase must match // the actual length. This is checked by ArrayColumn. void putArray (rownr_t rownr, const ArrayBase& arrayPtr); // Put a slice of an N-dimensional array in a particular cell. // The length of the array given by ArrayBase must match // the actual length. This is checked by ArrayColumn. void putSlice (rownr_t rownr, const Slicer&, const ArrayBase& arrayPtr); // Put the array of all values in the column. // If the column contains n-dim arrays, the source array is (n+1)-dim. // The arrays in the column have to have the same shape in all cells. // The length of the array given by ArrayBase must match // the actual length. This is checked by ArrayColumn. void putArrayColumn (const ArrayBase& arrayPtr); // Put the array of some values in the column. // If the column contains n-dim arrays, the source array is (n+1)-dim. // The arrays in the column have to have the same shape in all cells. // The length of the array given by ArrayBase must match // the actual length. This is checked by ArrayColumn. void putArrayColumnCells (const RefRows& rownrs, const ArrayBase& arrayPtr); // Put into subsections of all table arrays in the column. // If the column contains n-dim arrays, the source array is (n+1)-dim. // The arrays in the column have to have the same shape in all cells. // The length of the array given by ArrayBase must match // the actual length. This is checked by ArrayColumn. void putColumnSlice (const Slicer&, const ArrayBase& arrayPtr); // Put into subsections of some table arrays in the column. // If the column contains n-dim arrays, the source array is (n+1)-dim. // The arrays in the column have to have the same shape in all cells. // The length of the array given by ArrayBase must match // the actual length. This is checked by ArrayColumn. void putColumnSliceCells (const RefRows& rownrs, const Slicer&, const ArrayBase& arrayPtr); // Create a data manager column object for this column. void createDataManagerColumn(); private: // Is the shape for all arrays in the columns defined. Bool shapeColDef_p; // Shape for all arrays in the column. IPosition shapeCol_p; // Does the length of a string has to be checked? Bool checkValueLength_p; // Check if the shape of an array can be set and if it is set // correctly (i.e. if matching possible #dim in column description). void checkShape (const IPosition& shape) const; // Write the column data. // The control information is written into the given AipsIO object, // while the data is written/flushed by the data manager. void putFileDerived (AipsIO&); // Read the column data back. // The control information is read from the given AipsIO object. // This is used to bind the column to the appropriate data manager. // Thereafter the data manager gets opened. void getFileDerived (AipsIO&, const ColumnSet&); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/ArrColDesc.cc000066400000000000000000000101531476623553700203000ustar00rootroot00000000000000//# ArrColDesc.cc: Templated class to describe columns of arrays in tables //# Copyright (C) 1994,1995,1996,1997,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ArrayColumnDescBase::ArrayColumnDescBase (const String& name, const String& comment, const String& dataManagerType, const String& dataManagerGroup, DataType dt, const String& dataTypeId, Int options, uInt ndim, const IPosition& shape) : BaseColumnDesc (name, comment, dataManagerType, dataManagerGroup, dt, dataTypeId, options, ndim, shape, False, True, False) { if (nrdim_p <= 0) { nrdim_p = -1; } } ArrayColumnDescBase::ArrayColumnDescBase (const ArrayColumnDescBase& that) : BaseColumnDesc (that) {} ArrayColumnDescBase::~ArrayColumnDescBase() {} ArrayColumnDescBase& ArrayColumnDescBase::operator= (const ArrayColumnDescBase& that) { BaseColumnDesc::operator= (that); return *this; } //# Return the class name. String ArrayColumnDescBase::className() const { return "ArrayColumnDesc<" + dataTypeId(); } //# Put the object. //# The data is read by the ctor taking AipsIO. //# It was felt that putstart takes too much space, so therefore //# the version is put "manually". void ArrayColumnDescBase::putDesc (AipsIO& ios) const { ios << (uInt)1; // class version 1 // Formerly a switch was written to determine if a default existed. // This switch was always false. // Keep writing this switch (which is not used anymore). ios << False; } void ArrayColumnDescBase::getDesc (AipsIO& ios) { uInt version; ios >> version; // Formerly a switch was written to determine if a default existed. // This switch was always false. // So read it in and do not do anything with it. Bool sw; ios >> sw; } //# Show the column. void ArrayColumnDescBase::show (ostream& os) const { os << " Name=" << name(); os << " DataType=" << dataType(); if (dataType() == TpOther) { os << ", " << dataTypeId(); } if (maxLength() > 0) { os << " MaxLength=" << maxLength(); } os << " Nrdim=" << ndim(); os << " Shape=" << shape(); os << endl; os << " DataManager=" << dataManagerType(); os << "/" << dataManagerGroup(); os << endl; os << " Comment = " << comment() << endl; } //# Create a column object from the description. PlainColumn* ArrayColumnDescBase::makeColumn (ColumnSet* csp) const { PlainColumn* bcp = new ArrayColumnData (this, csp); return bcp; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/ArrColDesc.h000066400000000000000000000326271476623553700201540ustar00rootroot00000000000000//# ArrColDesc.h: Templated class to describe columns of arrays in tables //# Copyright (C) 1994,1995,1996,1997,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_ARRCOLDESC_H #define TABLES_ARRCOLDESC_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class PlainColumn; class ColumnSet; // // Abstract base class for description of table array columns // // // // // //
      • BaseColumnDesc (and its prerequisites) //
      • TableDesc // // // This class contains the common functionality for the templated class // ArrayColumnDesc which describes an array column. // class ArrayColumnDescBase : public BaseColumnDesc { public: // Construct with given parameters. ArrayColumnDescBase (const String& name, const String& comment, const String& dataManagerType, const String& dataManagerGroup, DataType, const String& dataTypeId, Int options, uInt ndim, const IPosition& shape); // Copy constructor (copy semantics); ArrayColumnDescBase (const ArrayColumnDescBase&); ~ArrayColumnDescBase(); // Assignment (copy semantics); ArrayColumnDescBase& operator= (const ArrayColumnDescBase&); // Get the name of this class. It is used by the registration process. // The template argument gets part of the name. String className() const; // Create a Column object out of this. // This is used by class ColumnSet to construct a table column object. virtual PlainColumn* makeColumn (ColumnSet*) const; // Show the column. void show (ostream& os) const; protected: // Put the object. virtual void putDesc (AipsIO&) const; // Get the object. virtual void getDesc (AipsIO&); }; // // Templated class for description of table array columns // // // // // //
      • ArrayColumnDescBase (and its prerequisites) //
      • TableDesc // // // This class builds descriptions of table columns where each cell (which // may also be called a row) will hold an array. // // // ArrayColumnDesc is a templated class for defining a table column // containing arrays. // // The table values are handled by a data manager. This can be // a storage manager to store the values in a file or it can be // a virtual column engine to calculate them on-the-fly. // Only the basic data types are allowed when storing in a file. These are: // Bool, uChar, Short, uShort, Int, uInt, Int64, float, double, // Complex, DComplex and String. // // At table creation time (when a table gets created from a table // description), each column needs to be bound to a data manager. // If not done explicitly, the table system will bind a column to the // default manager defined in the column description. // // An array column description consists of the following attributes: //
          //
        • Name, which has to be unique and must also be different // from possible table keyword names. //
        • Data type, which is determined by the template parameter // (e.g. ArrayColumnDesc). //
        • A data type id, which tells the unique name of non-standard // data types (i.e. for data type == TpOther). //
        • Comment, which defaults to the empty string. // This serves purely as an informational string for the user. //
        • Dimensionality. If given, all arrays in the column need // to have that dimensionality. //
        • Shape. If given, all arrays in the column need to have // that shape. //
        • Default data manager, which will be used if a column // for a newly created table is not explicitly bound to a // datamanager. //
        • Data manager group, which serves 2 purposes. // Firstly it can be used in class SetupNewTable to bind a group // of columns. // Secondly, when the default data managers are used, it // allows, for example, to have 2 AipsIO storage managers. // One for one group of columns and one for another group of columns. //
        • Options. These are defined in ColumnDesc.h and can be combined // by logically or-ing them. //
            //
          1. // ColumnDesc::FixedShape says that the arrays in all cells // of a column have the same shape. This shape must be defined // before a table is created. It does not tell if // the array is direct or indirect. // A FixedShape array is defined in every cell, while for // non-FixedShape arrays a cell can be empty. //
          2. // ColumnDesc::Direct determines if an array is directly // stored in the table or if it is stored indirectly in a separate // file. Direct arrays enforce the FixedShape option. // Usually indirect arrays are only read in on command, while // direct arrays are held in memory. So the size of the // arrays is an important factor. //
          //
        • Default keyword set, which defaults to an empty set. // When a table column gets created from the description, it gets // a copy of this keyword set as its initial keyword set. //
        // // There are several constructors, which allow the definition of most // of the above mentioned attributes. Others, like the default keyword // set, have to be defined explicitly. // // This class is derived from ArrayColumnDescBase, thus the functions // in there also apply to this class. // // Once a column description is set up satisfactorily, it must be added // to a table description before it can be used by the table system. //
        // // // TableDesc tabDesc("tTableDesc", "1", TableDesc::New); // // // Now define array columns. // // This one is indirect and has no dimensionality mentioned yet. // // Define the keyword UNIT in it. // ArrayColumnDesc arr1Column("Arr1", "comment for Arr1"); // arr1Column.rwKeywordSet().define ("UNIT", "Jy"); // tabDesc.addColumn (arr1Column); // // // This one is indirect and has 3-dim arrays. // tabDesc.addColumn (ArrayColumnDesc("Arr2", // "comment for Arr2", // 3)); // // This one is direct and has 2-dim arrays with axis lengths 4 and 7. // tabDesc.addColumn (ArrayColumnDesc("Arr3", // "comment for Arr1", // IPosition(2,4,7), // ColumnDesc::Direct)); // // // // Several column description classes are needed to allow the user // to define attributes which are special for each column type. // For scalars the special attribute is the default value. // They all have to be templated to support arbitrary data types. // // //
      • Default constructor //
      • Copy constructor //
      • Assignment operator //
      • static String dataTypeId(); // (not needed for builtin types) // This should return the unique "name" of the class. // //# //# A List of bugs, limitations, extensions or planned refinements. //# template class ArrayColumnDesc : public ArrayColumnDescBase { friend class ColumnDesc; public: // Construct the column with the given name and dimensionality. // The data manager type defaults to the StandardStman storage manager. // The data manager group defaults to the data manager type. // Ndim <=0 means that the number of dimensions is free and will // be defined when creating the table (rows). Ndim>0 means that // the arrays in this column must have the given dimensionality. // The possible options are defined in ColumnDesc.h. explicit ArrayColumnDesc (const String& name, Int ndim = -1, int options = 0); // Construct the column with the given name, dimensionality, and comment. // The data manager type defaults to the StandardStman storage manager. // The data manager group defaults to the data manager type. // Ndim <=0 means that the number of dimensions is free and will // be defined when creating the table (rows). Ndim>0 means that // the arrays in this column must have the given dimensionality. // The possible options are defined in ColumnDesc.h. ArrayColumnDesc (const String& name, const String& comment, Int ndim = -1, int options = 0); // Construct the column with the given name, dimensionality, comment, // and default data manager type and group. // A blank data manager group defaults to the data manager type. // Ndim <=0 means that the number of dimensions is free and will // be defined when creating the table (rows). Ndim>0 means that // the arrays in this column must have the given dimensionality. // The possible options are defined in ColumnDesc.h. ArrayColumnDesc (const String& name, const String& comment, const String& dataManName, const String& dataManGroup, Int ndim = -1, int options = 0); // Construct the column with the given name and shape. // The data manager type defaults to the StandardStman storage manager. // The data manager group defaults to the data manager type. // The possible options are defined in ColumnDesc.h. // This constructor can only be used for FixedShape arrays, because the // shape of other arrays can only be set per row. ArrayColumnDesc (const String& name, const IPosition& shape, int options = 0); // Construct the column with the given name, shape, and comment. // The data manager type defaults to the StandardStman storage manager. // The data manager group defaults to the data manager type. // The possible options are defined in ColumnDesc.h. // This constructor can only be used for FixedShape arrays, because the // shape of other arrays can only be set per row. ArrayColumnDesc (const String& name, const String& comment, const IPosition& shape, int options = 0); // Construct the column with the given name, shape, comment, // and default data manager type and group. // A blank data manager group defaults to the data manager type. // The possible options are defined in ColumnDesc.h. // This constructor can only be used for FixedShape arrays, because the // shape of other arrays can only be set per row. // If both ndim and shape are given as > 0, ndim should match the length // of shape. ArrayColumnDesc (const String& name, const String& comment, const String& dataManName, const String& dataManGroup, const IPosition& shape, int options = 0, int ndim=-1); // Copy constructor (copy semantics); ArrayColumnDesc (const ArrayColumnDesc&); ~ArrayColumnDesc(); // Assignment (copy semantics); ArrayColumnDesc& operator= (const ArrayColumnDesc&); // Clone this column description to another. BaseColumnDesc* clone() const; // Register the construction function of this class. void registerClass() const; // Create the object from AipsIO (this function is registered). static BaseColumnDesc* makeDesc(const String& name); }; //# Explicitly instantiate these templates in ArrColDesc_tmpl.cc extern template class ArrayColumnDesc; extern template class ArrayColumnDesc; extern template class ArrayColumnDesc; extern template class ArrayColumnDesc; extern template class ArrayColumnDesc; extern template class ArrayColumnDesc; extern template class ArrayColumnDesc; extern template class ArrayColumnDesc; extern template class ArrayColumnDesc; extern template class ArrayColumnDesc; extern template class ArrayColumnDesc; extern template class ArrayColumnDesc; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/tables/Tables/ArrColDesc.tcc000066400000000000000000000117271476623553700204740ustar00rootroot00000000000000//# ArrColDesc.tcc: Templated class to describe columns of arrays in tables //# Copyright (C) 1994,1995,1996,1997,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_ARRCOLDESC_TCC #define TABLES_ARRCOLDESC_TCC #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template ArrayColumnDesc::ArrayColumnDesc (const String& name, Int ndim, int opt) : ArrayColumnDescBase (name, "", "", "", ValType::getType(static_cast(0)), valDataTypeId(static_cast(0)), opt, ndim, IPosition()) {} template ArrayColumnDesc::ArrayColumnDesc (const String& name, const String& comment, Int ndim, int opt) : ArrayColumnDescBase (name, comment, "", "", ValType::getType(static_cast(0)), valDataTypeId(static_cast(0)), opt, ndim, IPosition()) {} template ArrayColumnDesc::ArrayColumnDesc (const String& name, const String& comment, const String& dataManName, const String& dataManGroup, Int ndim, int opt) : ArrayColumnDescBase (name, comment, dataManName, dataManGroup, ValType::getType(static_cast(0)), valDataTypeId(static_cast(0)), opt, ndim, IPosition()) {} template ArrayColumnDesc::ArrayColumnDesc (const String& name, const IPosition& shp, int opt) : ArrayColumnDescBase (name, "", "", "", ValType::getType(static_cast(0)), valDataTypeId(static_cast(0)), opt, shp.nelements(), shp) {} template ArrayColumnDesc::ArrayColumnDesc (const String& name, const String& comment, const IPosition& shp, int opt) : ArrayColumnDescBase (name, comment, "", "", ValType::getType(static_cast(0)), valDataTypeId(static_cast(0)), opt, shp.nelements(), shp) {} template ArrayColumnDesc::ArrayColumnDesc (const String& name, const String& comment, const String& dataManName, const String& dataManGroup, const IPosition& shp, int opt, int ndim) : ArrayColumnDescBase (name, comment, dataManName, dataManGroup, ValType::getType(static_cast(0)), valDataTypeId(static_cast(0)), opt, shp.nelements(), shp) { if (ndim > 0) { if (nrdim_p > 0 && ndim != nrdim_p) { throw (TableInvColumnDesc (name, "Shape length mismatches ndim")); } else { nrdim_p = ndim; } } } template ArrayColumnDesc::ArrayColumnDesc (const ArrayColumnDesc& that) : ArrayColumnDescBase (that) {} //# Make a new object. template BaseColumnDesc* ArrayColumnDesc::makeDesc(const String&) { BaseColumnDesc* ptr = new ArrayColumnDesc(String()); return ptr; } template ArrayColumnDesc::~ArrayColumnDesc() {} template ArrayColumnDesc& ArrayColumnDesc::operator= (const ArrayColumnDesc& that) { ArrayColumnDescBase::operator= (that); return *this; } //# Clone this column description to another. template BaseColumnDesc* ArrayColumnDesc::clone() const { BaseColumnDesc* ptr = new ArrayColumnDesc(*this); return ptr; } //# Register the makeDesc function. template void ArrayColumnDesc::registerClass() const { ColumnDesc::registerCtor (className(), makeDesc); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/ArrColDesc_tmpl.cc000066400000000000000000000035241476623553700213400ustar00rootroot00000000000000//# ArrColDesc_tmpl.cc: Explicit ArrayColumnDesc template instantiations //# Copyright (C) 2015 //# Associated Universities, Inc. Washington DC, USA, //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include //# Instantiate extern templates for often used types. namespace casacore { template class ArrayColumnDesc; template class ArrayColumnDesc; template class ArrayColumnDesc; template class ArrayColumnDesc; template class ArrayColumnDesc; template class ArrayColumnDesc; template class ArrayColumnDesc; template class ArrayColumnDesc; template class ArrayColumnDesc; template class ArrayColumnDesc; template class ArrayColumnDesc; template class ArrayColumnDesc; } casacore-3.7.1/tables/Tables/ArrayColumn.h000066400000000000000000000512141476623553700204200ustar00rootroot00000000000000//# ArrayColumn.h: access to an array table column with arbitrary data type //# Copyright (C) 1994,1995,1996,1997,1998,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_ARRAYCOLUMN_H #define TABLES_ARRAYCOLUMN_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class ColumnSlicer; // // Read and write access to an array table column with arbitrary data type // // // // // //
      • Table //
      • ArrayColumnBase // // // ArrayColumn gives read and write access to an column in a table // containing an array with data type T. // // // The class ArrayColumn allows readonly access to a column // containing arrays with an arbitrary data type. It can handle direct // as well as indirect arrays. // It is possible to get the data in an individual cell (i.e. table row); // either the whole array or a slice of the array can be accessed. // It is also possible to get the column as a whole if the arrays // in all cells of the column have the same shape (which is always true // for direct arrays). As in the case of individual cells it is possible // to get the entire arrays or a slice of the arrays. // // A default constructor is defined to allow construction of an array // of ArrayColumn objects. However, this constructs an object not // referencing a column. Functions like get, etc. will fail (i.e. result // in a segmentation fault) when used on such objects. The functions // isNull and throwIfNull can be used to test on this. // The functions attach and reference can fill in the object. // // The assignment operator is not defined for this class, because it was // felt it would be too confusing. Instead the function reference can // be used to do assignment with reference semantics. An assignment // with copy semantics makes no sense for a readonly column. // // //
      • Default constructor //
      • Copy constructor //
      • Assignment operator // // // See module Tables. // template class ArrayColumn : public ArrayColumnBase { public: // The default constructor creates a null object, i.e. it // does not reference a table column. // The sole purpose of this constructor is to allow construction // of an array of ArrayColumn objects. // The functions reference and attach can be used to make a null object // reference a column. // Note that get functions, etc. will cause a segmentation fault // when operating on a null object. It was felt it was too expensive // to test on null over and over again. The user should use the isNull // or throwIfNull function in case of doubt. ArrayColumn(); // Construct for the given column in the given table. ArrayColumn (const Table&, const String& columnName); // Construct from the given table column. // This constructor is useful if first a table column was constructed, // its type is determined and thereafter used to construct the // correct column object. explicit ArrayColumn (const TableColumn&); // Copy constructor (reference semantics). ArrayColumn (const ArrayColumn&); ~ArrayColumn(); // Clone the object. virtual TableColumn* clone() const; // Assignment uses reference semantics, thus works the same // as function reference. ArrayColumn& operator= (const ArrayColumn&); // Change the reference to another column. // This is in fact an assignment operator with reference semantics. // It removes the reference to the current column and creates // a reference to the column referenced in the other object. // It will handle null objects correctly. void reference (const ArrayColumn&); // Attach a column to the object. // This is in fact only a shorthand for //
        reference (ArrayColumn (table, columnName)); void attach (const Table& table, const String& columnName) { reference (ArrayColumn (table, columnName)); } // Get the #dimensions of an array in a particular cell. // If the cell does not contain an array, 0 is returned. // Use the function isDefined to test if the cell contains an array. uInt ndim (rownr_t rownr) const { TABLECOLUMNCHECKROW(rownr); return baseColPtr_p->ndim (rownr); } // Get the shape of an array in a particular cell. // If the cell does not contain an array, a 0-dim shape is returned. // Use the function isDefined to test if the cell contains an array. IPosition shape (rownr_t rownr) const { TABLECOLUMNCHECKROW(rownr); return baseColPtr_p->shape (rownr); } // Get the array value in a particular cell (i.e. table row). // The row numbers count from 0 until #rows-1. // // According to the assignment rules of class Array, the destination // array must be empty or its shape must conform the table array shape. // However, if the resize flag is set the destination array will be // resized if not conforming. void get (rownr_t rownr, Array& array, Bool resize = False) const; Array get (rownr_t rownr) const; Array operator() (rownr_t rownr) const; // // Get a slice of an N-dimensional array in a particular cell // (i.e. table row). // The row numbers count from 0 until #rows-1. // The dimensionality of the slice must match the dimensionality // of the table array and the slice definition should not exceed // the shape of the table array. // // According to the assignment rules of class Array, the destination // array must be empty or its shape must conform the shape of the // table array slice. // However, if the resize flag is set the destination array will be // resized if not conforming. void getSlice (rownr_t rownr, const Slicer& arraySection, Array& array, Bool resize = False) const; Array getSlice (rownr_t rownr, const Slicer& arraySection) const; // // Get an irregular slice of an N-dimensional array in a particular cell // (i.e. table row) as given by the vectors of Slice objects. // The outer vector represents the array axes. // A missing or empty axis means the entire axis. // The inner vector represents the slices to take for each axis. // For example, to get slices from 2-dim arrays: // // Vector > slices(2); // 2-dim // slices[1].resize (3); // 3 slices in 2nd dim // slices[1][0] = Slice(100,20); // slices[1][1] = Slice(200,18); // slices[1][2] = Slice(538,30,2); // // Get data. Vector of first axis is empty, thus entire axis is read. // Array data = dataCol.getColumn (slices); // // If the column contains n-dim arrays, the resulting array is (n+1)-dim. // with the last dimension representing the number of rows and the // other dimensions representing the shape of the slice. // The arrays in the column must have the same shape in all cells. // // According to the assignment rules of class Array, the destination // array must be empty or its shape must conform the resulting (n+1)-dim // array. // However, if the resize flag is set the destination array will be // resized if not conforming. void getSlice (rownr_t rownr, const Vector >& arraySlices, Array& arr, Bool resize = False) const; Array getSlice (rownr_t rownr, const Vector >& arraySlices) const; // // Get the array of all values in a column. // If the column contains n-dim arrays, the resulting array is (n+1)-dim // with the last dimension representing the number of rows. // The arrays in the column must have the same shape in all cells. // // According to the assignment rules of class Array, the destination // array must be empty or its shape must conform the resulting (n+1)-dim // array. // However, if the resize flag is set the destination array will be // resized if not conforming. void getColumn (Array& array, Bool resize = False) const; Array getColumn() const; // // Get regular slices from all arrays in the column. // If the column contains n-dim arrays, the resulting array is (n+1)-dim. // with the last dimension representing the number of rows and the // other dimensions representing the shape of the slice. // The arrays in the column must have the same shape in all cells. // // According to the assignment rules of class Array, the destination // array must be empty or its shape must conform the resulting (n+1)-dim // array. // However, if the resize flag is set the destination array will be // resized if not conforming. void getColumn (const Slicer& arraySection, Array& array, Bool resize = False) const; Array getColumn (const Slicer& arraySection) const; // // Get irregular slices from all arrays in the column as given by the // vectors of Slice objects. The outer vector represents the array axes. // A missing or empty axis means the entire axis. // The inner vector represents the slices to take for each axis. // For example, to get slices from 2-dim arrays: // // Vector > slices(2); // 2-dim // slices[1].resize (3); // 3 slices in 2nd dim // slices[1][0] = Slice(100,20); // slices[1][1] = Slice(200,18); // slices[1][2] = Slice(538,30,2); // // Get data. Vector of first axis is empty, thus entire axis is read. // Array data = dataCol.getColumn (slices); // // If the column contains n-dim arrays, the resulting array is (n+1)-dim. // with the last dimension representing the number of rows and the // other dimensions representing the shape of the slice. // The arrays in the column must have the same shape in all cells. // // According to the assignment rules of class Array, the destination // array must be empty or its shape must conform the resulting (n+1)-dim // array. // However, if the resize flag is set the destination array will be // resized if not conforming. void getColumn (const Vector >& arraySection, Array& array, Bool resize = False) const; Array getColumn (const Vector >& arraySection) const; // // Get the array of some values in a column. // The Slicer object can be used to specify start, end (or length), // and stride of the rows to get. // If the column contains n-dim arrays, the resulting array is (n+1)-dim // with the last dimension representing the number of rows in the slicer. // The arrays in the column must have the same shape in all those cells. // According to the assignment rules of class Array, the destination // array must be empty or its shape must conform the resulting (n+1)-dim // array. // However, if the resize flag is set the destination array will be // resized if not conforming. // void getColumnRange (const Slicer& rowRange, Array& arr, Bool resize = False) const; Array getColumnRange (const Slicer& rowRange) const; void getColumnCells (const RefRows& rownrs, Array& arr, Bool resize = False) const; Array getColumnCells (const RefRows& rownrs) const; // // Get slices from some arrays in a column. // The first Slicer object can be used to specify start, end (or length), // and stride of the rows to get. The second Slicer object can be // used to specify the slice to take from each array. // If the column contains n-dim arrays, the resulting array is (n+1)-dim // with the last dimension representing the number of rows in the slicer. // The arrays in the column must have the same shape in all those cells. // According to the assignment rules of class Array, the destination // array must be empty or its shape must conform the resulting (n+1)-dim // array. // However, if the resize flag is set the destination array will be // resized if not conforming. // void getColumnRange (const Slicer& rowRange, const Slicer& arraySection, Array& arr, Bool resize = False) const; Array getColumnRange (const Slicer& rowRange, const Slicer& arraySection) const; void getColumnCells (const RefRows& rownrs, const Slicer& arraySection, Array& arr, Bool resize = False) const; Array getColumnCells (const RefRows& rownrs, const Slicer& arraySection) const; // // Similar to getColumn (arraySlices, arr, resize) except it // gets the slices for the given rows instead of all rows. void getColumnCells (const RefRows& rows, const ColumnSlicer & slicerSet, Array& destination, Bool resize = False) const; // Set the shape of the array in the given row. // Setting the shape is needed if the array is put in slices, // otherwise the table system would not know the shape. // void setShape (rownr_t rownr, const IPosition& shape); // Try to store the array in a tiled way using the given tile shape. void setShape (rownr_t rownr, const IPosition& shape, const IPosition& tileShape); // // Put the array in a particular cell (i.e. table row). // The row numbers count from 0 until #rows-1. // If the shape of the table array in that cell has not already been // defined, it will be defined implicitly. void put (rownr_t rownr, const Array& array); // Copy the value of a cell of that column to a cell of this column. // This function uses a generic TableColumn object as input. // The data types of both columns must be the same, otherwise an // exception is thrown. // // Use the same row numbers for both cells. void put (rownr_t rownr, const TableColumn& that, Bool preserveTileShape=False) { put (rownr, that, rownr, preserveTileShape); } // Use possibly different row numbers for that (i.e. input) and // and this (i.e. output) cell. void put (rownr_t thisRownr, const TableColumn& that, rownr_t thatRownr, Bool preserveTileShape=False); // For backward compatibility (otherwise ambigious with put taking Bool). void put (uInt thisRownr, const TableColumn& that, uInt thatRownr, Bool preserveTileShape=False) { put (rownr_t(thisRownr), that, rownr_t(thatRownr), preserveTileShape); } // // Put into a slice of an N-dimensional array in a particular cell. // The row numbers count from 0 until #rows-1. // The shape of the table array must have been defined. // The dimensionality of the slice must match the dimensionality // of the table array and the slice definition should not exceed // the shape of the table array. void putSlice (rownr_t rownr, const Slicer& arraySection, const Array& array); void putSlice (rownr_t rownr, const Vector >& arraySlices, const Array& arr); // Put the array of all values in the column. // If the column contains n-dim arrays, the source array must be (n+1)-dim // with the last dimension representing the number of rows. void putColumn (const Array& array); // Put into subsections of the table arrays in the entire column. // If the column contains n-dim arrays, the source array is (n+1)-dim // with the last dimension representing the number of rows and // other dimensions representing the shape of the slice. // The dimensionality of the slice must match the dimensionality // of the table array, thus must be n-dim. Also the slice definition // should not exceed the shape of the table arrays. void putColumn (const Slicer& arraySection, const Array& array); void putColumn (const Vector >& arraySlices, const Array& arr); // Put the array of some values in the column. // The Slicer object can be used to specify start, end (or length), // and stride of the rows to put. // If the column contains n-dim arrays, the source array must be (n+1)-dim // with the last dimension representing the number of rows in the slicer. // void putColumnRange (const Slicer& rowRange, const Array& arr); void putColumnCells (const RefRows& rownrs, const Array& arr); // // Put into subsection of the table arrays in some rows of the column. // The first Slicer object can be used to specify start, end (or length), // and stride of the rows to put. The second Slicer object can be // used to specify the slice to take from each array. // If the column contains n-dim arrays, the source array must be (n+1)-dim // with the last dimension representing the number of rows in the slicer. // void putColumnRange (const Slicer& rowRange, const Slicer& arraySection, const Array& arr); void putColumnCells (const RefRows& rownrs, const Slicer& arraySection, const Array& arr); // // Same as putColumn(arraySlices, arr) except that it puts for the given // rows instead of all rows. // void putColumnCells (const RefRows& rows, const Vector >& arraySlices, const Array& arr); void putSliceFromRows (const RefRows& rows, const Vector >& arraySlices, const Array& source) { putColumnCells (rows, arraySlices, source); } void putColumnCells (const RefRows& rows, const ColumnSlicer & columnSlicer, const Array& source); // // Put the same value in all cells of the column. void fillColumn (const Array& value); // Put the contents of a column with the same data type into this column. // To put the contents of a column with a different data type into // this column, the function TableColumn::putColumn can be used // (provided the data type promotion is possible). // In fact, this function is an assignment operator with copy semantics. void putColumn (const ArrayColumn& that); private: // Check if the data type matches the column data type. void checkDataType() const; }; //# Explicitly instantiate these templates in ArrayColumn_tmpl.cc extern template class ArrayColumn; extern template class ArrayColumn; extern template class ArrayColumn; extern template class ArrayColumn; extern template class ArrayColumn; extern template class ArrayColumn; extern template class ArrayColumn; extern template class ArrayColumn; extern template class ArrayColumn; extern template class ArrayColumn; extern template class ArrayColumn; extern template class ArrayColumn; } //# NAMESPACE CASACORE - END //# Make old name ROArrayColumn still available. #define ROArrayColumn ArrayColumn #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/tables/Tables/ArrayColumn.tcc000066400000000000000000000274041476623553700207460ustar00rootroot00000000000000//# ArrayColumn.cc: Access to an array table column with arbitrary data type //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_ARRAYCOLUMN_TCC #define TABLES_ARRAYCOLUMN_TCC #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template ArrayColumn::ArrayColumn() : ArrayColumnBase () {} template ArrayColumn::ArrayColumn (const Table& tab, const String& columnName) : ArrayColumnBase (tab, columnName) { checkDataType(); } template ArrayColumn::ArrayColumn (const TableColumn& column) : ArrayColumnBase (column) { checkDataType(); } template ArrayColumn::ArrayColumn (const ArrayColumn& that) : ArrayColumnBase (that) {} template TableColumn* ArrayColumn::clone() const { return new ArrayColumn (*this); } template ArrayColumn& ArrayColumn::operator= (const ArrayColumn& that) { reference (that); return (*this); } template void ArrayColumn::reference (const ArrayColumn& that) { TableColumn::reference (that); } template ArrayColumn::~ArrayColumn() {} template void ArrayColumn::checkDataType() const { //# Check if the data type matches. const ColumnDesc& cd = baseColPtr_p->columnDesc(); DataType dtype = cd.dataType(); if (dtype != ValType::getType(static_cast(0)) || !cd.isArray()) { throw (TableInvDT (" in ArrayColumn ctor for column " + cd.name())); } if (dtype == TpOther) { if (cd.dataTypeId() != valDataTypeId(static_cast(0))) { throw (TableInvDT (" in ArrayColumn ctor for column " + cd.name() + "; using data type id " + valDataTypeId(static_cast(0)) + ", expected " + cd.dataTypeId())); } } } template Array ArrayColumn::operator() (rownr_t rownr) const { Array arr; get (rownr, arr); return arr; } template Array ArrayColumn::get (rownr_t rownr) const { Array arr; get (rownr, arr); return arr; } template void ArrayColumn::get (rownr_t rownr, Array& arr, Bool resize) const { acbGet (rownr, arr, resize); } template Array ArrayColumn::getSlice (rownr_t rownr, const Slicer& arraySection) const { Array arr; getSlice (rownr, arraySection, arr); return arr; } template void ArrayColumn::getSlice (rownr_t rownr, const Slicer& arraySection, Array& arr, Bool resize) const { acbGetSlice (rownr, arraySection, arr, resize); } template Array ArrayColumn::getSlice (rownr_t rownr, const Vector >& arraySlices) const { Array arr; getSlice (rownr, arraySlices, arr); return arr; } template void ArrayColumn::getSlice (rownr_t rownr, const Vector >& arraySlices, Array& arr, Bool resize) const { acbGetSlice (rownr, arraySlices, arr, resize); } template void ArrayColumn::getColumnCells (const RefRows& rows, const ColumnSlicer& columnSlicer, Array& destination, Bool resize) const { acbGetColumnCells (rows, columnSlicer, destination, resize); } //template //void //ArrayColumn::getColumnCellsSlicers (const Vector > & arraySlices, // uInt axis, // Vector selections, // vector result) const //{ // if (axis == arraySlices.size()){ // // IPosition start (axis), increment (axis), length (axis); // // for (uInt i = 0; i < axis; i++){ // // const Slice & slice = arraySlices [i] [selections [i]]; // start [i] = slice.start(); // increment [i] = slice.inc(); // length [i] = slice.length(); // // result.push_back = new Slicer (start, length, increment); // // } // // return; // } // // const Vector & thisAxis = arraySlices [axis]; // // for (uInt i = 0; i < thisAxis.size(); i++){ // // selections [axis] = i; // getColumnCellsSlicers (arraySlices, axis + 1, selections, result); // } // //} template Array ArrayColumn::getColumn() const { Array arr; getColumn (arr); return arr; } template void ArrayColumn::getColumn (Array& arr, Bool resize) const { acbGetColumn (arr, resize); } template Array ArrayColumn::getColumn (const Slicer& arraySection) const { Array arr; getColumn (arraySection, arr); return arr; } template void ArrayColumn::getColumn (const Slicer& arraySection, Array& arr, Bool resize) const { acbGetColumn (arraySection, arr, resize); } template Array ArrayColumn::getColumn (const Vector >& arraySlices) const { Array arr; getColumn (arraySlices, arr); return arr; } template void ArrayColumn::getColumn (const Vector >& arraySlices, Array& arr, Bool resize) const { acbGetColumn (arraySlices, arr, resize); } template Array ArrayColumn::getColumnRange (const Slicer& rowRange) const { Array arr; getColumnRange (rowRange, arr); return arr; } template void ArrayColumn::getColumnRange (const Slicer& rowRange, Array& arr, Bool resize) const { acbGetColumnRange (rowRange, arr, resize); } template Array ArrayColumn::getColumnCells (const RefRows& rownrs) const { Array arr; getColumnCells (rownrs, arr); return arr; } template void ArrayColumn::getColumnCells (const RefRows& rownrs, Array& arr, Bool resize) const { acbGetColumnCells (rownrs, arr, resize); } template Array ArrayColumn::getColumnRange (const Slicer& rowRange, const Slicer& arraySection) const { Array arr; getColumnRange (rowRange, arraySection, arr); return arr; } template void ArrayColumn::getColumnRange (const Slicer& rowRange, const Slicer& arraySection, Array& arr, Bool resize) const { acbGetColumnRange (rowRange, arraySection, arr, resize); } template Array ArrayColumn::getColumnCells (const RefRows& rownrs, const Slicer& arraySection) const { Array arr; getColumnCells (rownrs, arraySection, arr); return arr; } template void ArrayColumn::getColumnCells (const RefRows& rownrs, const Slicer& arraySection, Array& arr, Bool resize) const { acbGetColumnCells (rownrs, arraySection, arr, resize); } template void ArrayColumn::setShape (rownr_t rownr, const IPosition& shape) { ArrayColumnBase::setShape (rownr, shape); } template void ArrayColumn::setShape (rownr_t rownr, const IPosition& shape, const IPosition& tileShape) { ArrayColumnBase::setShape (rownr, shape, tileShape); } template void ArrayColumn::put (rownr_t rownr, const Array& arr) { acbPut (rownr, arr); } template void ArrayColumn::putSlice (rownr_t rownr, const Slicer& arraySection, const Array& arr) { acbPutSlice (rownr, arraySection, arr); } template void ArrayColumn::putSlice (rownr_t rownr, const Vector >& arraySlices, const Array& arr) { acbPutSlice (rownr, arraySlices, arr); } template void ArrayColumn::putColumnCells (const RefRows& rows, const Vector >& arraySlices, const Array& source) { acbPutColumnCells (rows, arraySlices, source); } template void ArrayColumn::putColumnCells (const RefRows& rows, const ColumnSlicer& columnSlicer, const Array& source) { acbPutColumnCells (rows, columnSlicer, source); } template void ArrayColumn::put (rownr_t thisRownr, const TableColumn& that, rownr_t thatRownr, Bool preserveTileShape) { TableColumn::put (thisRownr, that, thatRownr, preserveTileShape); } template void ArrayColumn::putColumn (const Array& arr) { acbPutColumn (arr); } template void ArrayColumn::putColumn (const Slicer& arraySection, const Array& arr) { acbPutColumn (arraySection, arr); } template void ArrayColumn::putColumn (const Vector >& arraySlices, const Array& arr) { acbPutColumn (arraySlices, arr); } template void ArrayColumn::putColumnRange (const Slicer& rowRange, const Array& arr) { acbPutColumnRange (rowRange, arr); } template void ArrayColumn::putColumnCells (const RefRows& rownrs, const Array& arr) { acbPutColumnCells (rownrs, arr); } template void ArrayColumn::putColumnRange (const Slicer& rowRange, const Slicer& arraySection, const Array& arr) { acbPutColumnRange (rowRange, arraySection, arr); } template void ArrayColumn::putColumnCells (const RefRows& rownrs, const Slicer& arraySection, const Array& arr) { acbPutColumnCells (rownrs, arraySection, arr); } //# This is a very simple implementation. //# However, it does not need to be more fancy, since an array operation //# is already much more expensive than the virtual function calls //# involved in each loop iteration. template void ArrayColumn::fillColumn (const Array& value) { acbFillColumn (value); } template void ArrayColumn::putColumn (const ArrayColumn& that) { acbPutColumn (that); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/ArrayColumnBase.cc000066400000000000000000000671131476623553700213560ustar00rootroot00000000000000//# ArrayColumnBase.cc: Base classfor access to an array table column //# Copyright (C) 2013 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ArrayColumnBase::ArrayColumnBase() : TableColumn () {} ArrayColumnBase::ArrayColumnBase (const Table& tab, const String& columnName) : TableColumn (tab, columnName) {} ArrayColumnBase::ArrayColumnBase (const TableColumn& column) : TableColumn (column) {} ArrayColumnBase::ArrayColumnBase (const ArrayColumnBase& that) : TableColumn (that) {} void ArrayColumnBase::reference (const ArrayColumnBase& that) { TableColumn::reference (that); } ArrayColumnBase::~ArrayColumnBase() {} Bool ArrayColumnBase::checkShape (const IPosition& expShape, const IPosition& arrShape, Bool noSlicing, Int64 rownr, const String& where) const { if (! expShape.isEqual (arrShape)) { if (noSlicing && canChangeShape_p) { return False; } String msg(where); if (rownr >= 0) { msg += " for row " + String::toString(rownr); } throw TableArrayConformanceError(msg + " in column " + baseColPtr_p->columnDesc().name(), arrShape, expShape); } return True; } void ArrayColumnBase::adaptShape (const IPosition& shp, ArrayBase& arr, Bool resize, Int64 rownr, const String& where) const { if (! shp.isEqual (arr.shape())) { if (resize || arr.nelements() == 0) { arr.resize (shp); } else { checkShape (shp, arr.shape(), False, rownr, where); } } } void ArrayColumnBase::acbGet (rownr_t rownr, ArrayBase& arr, Bool resize) const { TABLECOLUMNCHECKROW(rownr); // Check array conformance and resize if needed and possible. adaptShape (shape(rownr), arr, resize, rownr, "ArrayColumn::get"); baseColPtr_p->getArray (rownr, arr); } void ArrayColumnBase::acbGetSlice (rownr_t rownr, const Slicer& arraySection, ArrayBase& arr, Bool resize) const { TABLECOLUMNCHECKROW(rownr); // Check array conformance and resize if needed and possible. IPosition arrayShape (shape(rownr)); IPosition blc,trc,inc; IPosition shp = arraySection.inferShapeFromSource (arrayShape, blc, trc, inc); adaptShape (shp, arr, resize, rownr, "ArrayColumn::getSlice"); // Get the slice. //# Creating a Slicer is somewhat expensive, so use the slicer //# itself if it contains no undefined values. if (arraySection.isFixed()) { baseColPtr_p->getSlice (rownr, arraySection, arr); } else { baseColPtr_p->getSlice (rownr, Slicer(blc, trc, inc, Slicer::endIsLast), arr); } } void ArrayColumnBase::acbGetSlice (rownr_t rownr, const Vector >& arraySlices, ArrayBase& arr, Bool resize) const { TABLECOLUMNCHECKROW(rownr); // Use shape of row. IPosition colShp = shape(rownr); // Check the slices. Make a copy because a Slice is inserted if empty. Vector > slices(arraySlices); Slicer slicer; IPosition shp = Slice::checkSlices (slices, slicer, colShp); // Check array conformance and resize if needed and possible. adaptShape (shp, arr, resize, rownr, "ArrayColumn::getSlice"); // Now loop through all the slices and fill the array in parts. GetCellSlices functor(*this, rownr); handleSlices (slices, functor, slicer, arr); } void ArrayColumnBase::acbGetColumn (ArrayBase& arr, Bool resize) const { rownr_t nrrow = nrow(); //# Take shape of array in first row. IPosition shp; if (nrrow > 0) { shp = shape(0); } //# Total shape is array shape plus nr of table rows. shp.append (IPosition(1,nrrow)); // Check array conformance and resize if needed and possible. adaptShape (shp, arr, resize, -1, "ArrayColumn::getColumn"); if (!arr.empty()) { //# Get the column. baseColPtr_p->getArrayColumn (arr); } } void ArrayColumnBase::acbGetColumn (const Slicer& arraySection, ArrayBase& arr, Bool resize) const { rownr_t nrrow = nrow(); //# Use shape of array in first row. IPosition shp, blc,trc,inc; if (nrrow > 0) { shp = arraySection.inferShapeFromSource (shape(0), blc, trc, inc); } //# Total shape is slice shape plus nr of table rows. shp.append (IPosition(1,nrrow)); // Check array conformance and resize if needed and possible. adaptShape (shp, arr, resize, -1, "ArrayColumn::getColumn"); if (!arr.empty()) { //# Get the column slice. Slicer defSlicer (blc, trc, inc, Slicer::endIsLast); baseColPtr_p->getColumnSlice (defSlicer, arr); } } void ArrayColumnBase::acbGetColumn (const Vector >& arraySlices, ArrayBase& arr, Bool resize) const { rownr_t nrrow = nrow(); // Get total shape. // Use shape of first row (if there) as overall array shape. IPosition colShp; if (nrrow > 0) { colShp = shape(0); } Vector > slices(arraySlices); Slicer slicer; IPosition shp = Slice::checkSlices (slices, slicer, colShp); // Total shape is slice shape plus nr of table rows. shp.append (IPosition(1,nrrow)); // Check array conformance and resize if needed and possible. adaptShape (shp, arr, resize, -1, "ArrayColumn::getColumn"); // Now loop through all the slices and fill the array in parts. GetColumnSlices functor(*this); handleSlices (slices, functor, slicer, arr); } void ArrayColumnBase::acbGetColumnRange (const Slicer& rowRange, ArrayBase& arr, Bool resize) const { rownr_t nrrow = nrow(); IPosition shp, blc, trc, inc; shp = rowRange.inferShapeFromSource (IPosition(1,nrrow), blc, trc, inc); //# If the entire column is accessed, use that function. if (blc(0) == 0 && shp(0) == Int(nrrow) && inc(0) == 1) { acbGetColumn (arr, resize); } else { acbGetColumnCells (RefRows(blc(0), trc(0), inc(0)), arr, resize); } } void ArrayColumnBase::acbGetColumnCells (const RefRows& rownrs, ArrayBase& arr, Bool resize) const { rownr_t nrrow = rownrs.nrow(); //# Take shape of array in first row. IPosition arrshp; if (nrrow > 0) { arrshp = shape(rownrs.firstRow()); } //# Total shape is array shape plus nr of table rows. arrshp.append (IPosition(1,nrrow)); // Check array conformance and resize if needed and possible. adaptShape (arrshp, arr, resize, -1, "ArrayColumn::getColumnCells"); baseColPtr_p->getArrayColumnCells (rownrs, arr); } void ArrayColumnBase::acbGetColumnRange (const Slicer& rowRange, const Slicer& arraySection, ArrayBase& arr, Bool resize) const { rownr_t nrrow = nrow(); IPosition shp, blc, trc, inc; shp = rowRange.inferShapeFromSource (IPosition(1,nrrow), blc, trc, inc); //# If the entire column is accessed, use that function. if (blc(0) == 0 && shp(0) == Int(nrrow) && inc(0) == 1) { acbGetColumn (arraySection, arr, resize); } else { acbGetColumnCells (RefRows(blc(0), trc(0), inc(0)), arraySection, arr, resize); } } void ArrayColumnBase::acbGetColumnCells (const RefRows& rownrs, const Slicer& arraySection, ArrayBase& arr, Bool resize) const { rownr_t nrrow = rownrs.nrow(); IPosition arrshp, arrblc, arrtrc, arrinc; if (nrrow > 0) { arrshp = arraySection.inferShapeFromSource (shape(rownrs.firstRow()), arrblc, arrtrc, arrinc); } //# Total shape is slice shape plus nr of table rows. arrshp.append (IPosition(1,nrrow)); // Check array conformance and resize if needed and possible. adaptShape (arrshp, arr, resize, -1, "ArrayColumn::getColumnCells"); if (!arr.empty()) { //# Get the column slice. Slicer defSlicer (arrblc, arrtrc, arrinc, Slicer::endIsLast); baseColPtr_p->getColumnSliceCells (rownrs, defSlicer, arr); } } void ArrayColumnBase::setShape (rownr_t rownr, const IPosition& shape) { checkWritable(); TABLECOLUMNCHECKROW(rownr); //# Set shape if not defined yet or if changed (if possible). //# Throw exception if already defined with a different shape. if (canChangeShape_p || !isDefined(rownr)) { baseColPtr_p->setShape (rownr, shape); } else { if (! shape.isEqual (baseColPtr_p->shape (rownr))) { throw TableInvOper ("ArrayColumn::setShape; shape cannot be changed for row " + String::toString(rownr) + " in column " + baseColPtr_p->columnDesc().name()); } } } void ArrayColumnBase::setShape (rownr_t rownr, const IPosition& shape, const IPosition& tileShape) { checkWritable(); TABLECOLUMNCHECKROW(rownr); //# Only set shape if not defined yet. //# Throw exception if already defined with a different shape. if (canChangeShape_p || !isDefined(rownr)) { baseColPtr_p->setShape (rownr, shape, tileShape); } else { if (! shape.isEqual (baseColPtr_p->shape (rownr))) { throw TableInvOper ("ArrayColumn::setShape; shape cannot be changed for row " + String::toString(rownr) + " column " + baseColPtr_p->columnDesc().name()); } } } void ArrayColumnBase::acbPut (rownr_t rownr, const ArrayBase& arr) { checkWritable(); TABLECOLUMNCHECKROW(rownr); //# Define the shape if not defined yet. //# If defined, check if shape conforms. if (!isDefined(rownr)) { baseColPtr_p->setShape (rownr, arr.shape()); }else{ if (! checkShape (baseColPtr_p->shape(rownr), arr.shape(), True, rownr, "ArrayColumn::put")) { baseColPtr_p->setShape (rownr, arr.shape()); } } baseColPtr_p->putArray (rownr, arr); } void ArrayColumnBase::acbPutSlice (rownr_t rownr, const Slicer& arraySection, const ArrayBase& arr) { checkWritable(); TABLECOLUMNCHECKROW(rownr); //# Check the array conformance. IPosition arrayShape (shape(rownr)); IPosition blc,trc,inc; IPosition shp = arraySection.inferShapeFromSource (arrayShape, blc,trc,inc); checkShape (shp, arr.shape(), False, rownr, "ArrayColumn::putSlice"); //# Put the slice. baseColPtr_p->putSlice (rownr, arraySection, arr); } void ArrayColumnBase::acbPutSlice (rownr_t rownr, const Vector >& arraySlices, const ArrayBase& arr) { checkWritable(); TABLECOLUMNCHECKROW(rownr); // Use shape of the row. IPosition colShp = shape(rownr); Vector > slices(arraySlices); Slicer slicer; IPosition shp = Slice::checkSlices (slices, slicer, colShp); checkShape (shp, arr.shape(), False, rownr, "ArrayColumn::putSlice(slices)"); // Now loop through all the slices and fill the array in parts. PutCellSlices functor(*this, rownr); handleSlices (slices, functor, slicer, arr); } void ArrayColumnBase::acbPutColumn (const ArrayBase& arr) { checkWritable(); //# First check if number of rows matches. rownr_t nrrow = nrow(); IPosition shp = arr.shape(); uInt last = shp.nelements() - 1; if (shp[last] != Int(nrrow)) { throw TableArrayConformanceError ("ArrayColumn::putColumn - column " + baseColPtr_p->columnDesc().name() + " has " + String::toString(nrrow) + ", array has " + String::toString(shp[last]) + " rows"); } //# Remove #rows from shape to get the shape of each cell. shp.resize (last); //# If the array is fixed shape, check if the shape matches. if ((columnDesc().options() & ColumnDesc::FixedShape) == ColumnDesc::FixedShape) { checkShape (shapeColumn(), shp, False, -1, "ArrayColumn::putColumn"); } else { //# Otherwise set the shape of each cell (as far as needed). for (rownr_t i=0; iputArrayColumn (arr); } void ArrayColumnBase::acbPutColumn (const Slicer& arraySection, const ArrayBase& arr) { checkWritable(); rownr_t nrrow = nrow(); //# First check if number of rows matches. IPosition arrshp = arr.shape(); uInt last = arrshp.nelements() - 1; if (arrshp(last) != Int(nrrow)) { throw TableArrayConformanceError ("ArrayColumn::putColumn(slicer) - column " + baseColPtr_p->columnDesc().name() + " has " + String::toString(nrrow) + ", but array has " + String::toString(arrshp[last]) + " rows"); } //# If the array is fixed shape, check if the shape matches. if ((columnDesc().options() & ColumnDesc::FixedShape) == ColumnDesc::FixedShape) { //# Remove #rows from shape to get the shape of each cell. arrshp.resize (last); IPosition blc,trc,inc; IPosition shp = arraySection.inferShapeFromSource (shapeColumn(), blc,trc,inc); checkShape (shp, arrshp, False, -1, "ArrayColumn::putColumn(slicer)"); } //# Put the column slice. baseColPtr_p->putColumnSlice (arraySection, arr); } void ArrayColumnBase::acbPutColumn (const Vector >& arraySlices, const ArrayBase& arr) { checkWritable(); rownr_t nrrow = nrow(); // Get total shape. // Use shape of first row (if there) as overall array shape. IPosition colShp; if (nrrow > 0) { colShp = shape(0); } Vector > slices(arraySlices); Slicer slicer; IPosition shp = Slice::checkSlices (slices, slicer, colShp); // Total shape is slice shape plus nr of table rows. shp.append (IPosition(1,nrrow)); // Check array conformance. if (! shp.isEqual (arr.shape())) { throw (TableArrayConformanceError ("ArrayColumn::putColumn for column " + baseColPtr_p->columnDesc().name())); } // Now loop through all the slices and fill the array in parts. PutColumnSlices functor(*this); handleSlices (slices, functor, slicer, arr); } void ArrayColumnBase::acbPutColumnRange (const Slicer& rowRange, const ArrayBase& arr) { rownr_t nrrow = nrow(); IPosition shp, blc, trc, inc; shp = rowRange.inferShapeFromSource (IPosition(1,nrrow), blc, trc, inc); //# If the entire column is accessed, use that function. if (blc(0) == 0 && shp(0) == Int(nrrow) && inc(0) == 1) { acbPutColumn (arr); } else { acbPutColumnCells (RefRows(blc(0), trc(0), inc(0)), arr); } } void ArrayColumnBase::acbPutColumnCells (const RefRows& rownrs, const ArrayBase& arr) { checkWritable(); //# First check if number of rows matches. rownr_t nrrow = rownrs.nrow(); IPosition arrshp = arr.shape(); uInt last = arrshp.nelements() - 1; if (arrshp(last) != Int(nrrow)) { throw (TableArrayConformanceError ("ArrayColumn::putColumnCells for column " + baseColPtr_p->columnDesc().name())); } //# Remove #rows from shape to get the shape of each cell. arrshp.resize (last); //# If the array is fixed shape, check if the shape matches. if ((columnDesc().options() & ColumnDesc::FixedShape) == ColumnDesc::FixedShape) { if (! arrshp.isEqual (shapeColumn())) { throw (TableArrayConformanceError ("ArrayColumn::putColumnCells for column " + baseColPtr_p->columnDesc().name())); } } else { //# Otherwise set the shape of each cell (as far as needed). RefRowsSliceIter iter(rownrs); while (! iter.pastEnd()) { rownr_t rownr = iter.sliceStart(); rownr_t end = iter.sliceEnd(); rownr_t incr = iter.sliceIncr(); while (rownr <= end) { setShape (rownr, arrshp); rownr += incr; } iter++; } } //# Put the entire array. baseColPtr_p->putArrayColumnCells (rownrs, arr); } void ArrayColumnBase::acbPutColumnRange (const Slicer& rowRange, const Slicer& arraySection, const ArrayBase& arr) { rownr_t nrrow = nrow(); IPosition shp, blc, trc, inc; shp = rowRange.inferShapeFromSource (IPosition(1,nrrow), blc, trc, inc); //# If the entire column is accessed, use that function. if (blc(0) == 0 && shp(0) == Int(nrrow) && inc(0) == 1) { acbPutColumn (arraySection, arr); } else { acbPutColumnCells (RefRows(blc(0), trc(0), inc(0)), arraySection, arr); } } void ArrayColumnBase::acbPutColumnCells (const RefRows& rownrs, const Slicer& arraySection, const ArrayBase& arr) { checkWritable(); //# First check if number of rows matches. rownr_t nrrow = rownrs.nrow(); IPosition arrshp = arr.shape(); uInt last = arrshp.nelements() - 1; if (arrshp(last) != Int(nrrow)) { throw (TableArrayConformanceError ("ArrayColumn::putColumnCells for column " + baseColPtr_p->columnDesc().name())); } //# If the array is fixed shape, check if the shape matches. if ((columnDesc().options() & ColumnDesc::FixedShape) == ColumnDesc::FixedShape) { //# Remove #rows from shape to get the shape of each cell. arrshp.resize (last); IPosition arrshp2,arrblc,arrtrc,arrinc; arrshp2 = arraySection.inferShapeFromSource (shapeColumn(), arrblc, arrtrc, arrinc); if (! arrshp.isEqual(arrshp2)) { throw (TableArrayConformanceError ("ArrayColumn::putColumnCells for column " + baseColPtr_p->columnDesc().name())); } } //# Put the entire array. baseColPtr_p->putColumnSliceCells (rownrs, arraySection, arr); } void ArrayColumnBase::acbPutColumnCells (const RefRows& rows, const Vector >& arraySlices, const ArrayBase& source) { checkWritable(); // Check if the nr of rows in the array matches. if (Int64(rows.nrows()) != source.shape()[source.ndim()-1]) { throw TableArrayConformanceError("ArrayColumn::putColumnCells - number of " "rows in RefRows and Array mismatches"); } // Put the destination array one row at a time. RefRowsSliceIter rowIter(rows); std::unique_ptr arrIter = source.makeIterator (source.ndim()-1); while (! rowIter.pastEnd()) { for (rownr_t rownr = rowIter.sliceStart(); rownr <= rowIter.sliceEnd(); rownr += rowIter.sliceIncr()) { acbPutSlice (rownr, arraySlices, arrIter->getArray()); arrIter->next(); } // Go to next slice of rows. rowIter++; } } void ArrayColumnBase::acbGetColumnCells (const RefRows& rows, const ColumnSlicer& columnSlicer, ArrayBase& destination, Bool resize) const { // Calculate the shape of the destination data. This will be // [s1, s2, ..., nR] where sI are the sum of the slice elements for // that axis as contained in arraySlices [i]. nR is the number of rows // in the RefRows object rows. Resize the destination array to match. const Vector& dataSlicers = columnSlicer.getDataSlicers(); const Vector& destSlicers = columnSlicer.getDestinationSlicers(); IPosition destShape (columnSlicer.shape()); destShape.append (IPosition (1, rows.nrows())); adaptShape (destShape, destination, resize, -1, "ArrayColumn::getColumnCells (rows, columnSlicer, ...)"); // Fill the destination array one row at a time. RefRowsSliceIter rowIter(rows); std::unique_ptr arrIter = destination.makeIterator (destination.ndim()-1); while (! rowIter.pastEnd()) { for (rownr_t rownr = rowIter.sliceStart(); rownr <= rowIter.sliceEnd(); rownr += rowIter.sliceIncr()) { ArrayBase& destArray = arrIter->getArray(); // Iterate through the slicers. for (uInt j=0; j destPart = destArray.getSection (*destSlicers[j]); baseGetSlice (rownr, *dataSlicers[j], *destPart); } arrIter->next(); } // Go to next slice or rows. rowIter++; } } void ArrayColumnBase::acbPutColumnCells (const RefRows& rows, const ColumnSlicer& columnSlicer, const ArrayBase& source) { // Calculate the shape of the source data. This will be // [s1, s2, ..., nR] where sI are the sum of the slice elements for // that axis as contained in arraySlices [i]. nR is the number of rows // in the RefRows object rows. const Vector& dataSlicers = columnSlicer.getDataSlicers(); const Vector& destSlicers = columnSlicer.getDestinationSlicers(); IPosition destShape (columnSlicer.shape()); destShape.append (IPosition (1, rows.nrows())); checkShape (destShape, source.shape(), False, -1, "ArrayColumn::putColumnCells (rows, columnSlicer, ...)"); // Fill the source array one row at a time. RefRowsSliceIter rowIter(rows); std::unique_ptr arrIter = source.makeIterator (source.ndim()-1); while (! rowIter.pastEnd()) { for (rownr_t rownr = rowIter.sliceStart(); rownr <= rowIter.sliceEnd(); rownr += rowIter.sliceIncr()) { ArrayBase& destArray = arrIter->getArray(); // Iterate through the slicers. for (uInt j=0; j destPart = destArray.getSection (*destSlicers[j]); basePutSlice (rownr, *dataSlicers[j], *destPart); } arrIter->next(); } // Go to next slice or rows. rowIter++; } } //# This is a very simple implementation. //# However, it does not need to be more fancy, since an array operation //# is already much more expensive than the virtual function calls //# involved in each loop iteration. void ArrayColumnBase::acbFillColumn (const ArrayBase& value) { rownr_t nrrow = nrow(); for (rownr_t i=0; icolumnDesc().name() + " (from column " + that.baseColPtr_p->columnDesc().name() + ')'); } for (rownr_t i=0; i >& slices, BaseSlicesFunctor& functor, const Slicer& slicer, const ArrayBase& arr) const { // Set start and end position in Array for first Slice. IPosition arrStart (arr.ndim(), 0); IPosition arrEnd (slicer.length() - 1); if (arrStart.size() > arrEnd.size()) { arrEnd.append (IPosition(1, nrow()-1)); // for get/putColumn funcs } IPosition colStart (slicer.start()); IPosition colLen (slicer.length()); IPosition colIncr (slicer.stride()); uInt nrdim = slicer.ndim(); IPosition pos(nrdim, 0); while (True) { std::shared_ptr refArr (arr.getSection (Slicer(arrStart, arrEnd, Slicer::endIsLast))); functor.apply (Slicer(colStart, colLen, colIncr), *refArr); uInt i; for (i=0; i& dataSlicers, const Vector& destinationSlicers) : dataSlicers_p (dataSlicers), destinationSlicers_p (destinationSlicers), shape_p (shape) { String message = validateParameters (); if (! message.empty()){ freeSlicers(); // Call gave them to us; set them free. throw AipsError ("ColumnSlicer (ctor):: " + message); } } ColumnSlicer::~ColumnSlicer () { freeSlicers(); } void ColumnSlicer::freeSlicers () { // The two Vectors contain pointers to objects so they need to be freed. // They should have the same length normally, but during validation it's // possible that they have different lengths. for (uInt i = 0; i < dataSlicers_p.size(); i++){ delete dataSlicers_p [i]; } for (uInt i = 0; i < destinationSlicers_p.size(); i++){ delete destinationSlicers_p [i]; } } String ColumnSlicer::validateParameters() const { // Validate the contruction parameters to see if they are consistent. if (dataSlicers_p.size() != destinationSlicers_p.size()){ return String::format ("Number of data slicers (%d) and destination slicers (%d) " "must match", dataSlicers_p.size(), destinationSlicers_p.size()); } if (dataSlicers_p.size() == 0){ return String::format ("At least one destination and one data slicer required."); } for (uInt i = 0; i < dataSlicers_p.size(); i++) { if (dataSlicers_p[i]->length() != destinationSlicers_p[i]->length()){ return String::format ("Length of data slicer[%d] (%s) and " "destination slicer [%d] (%s) must be equal", i, dataSlicers_p[i]->length().toString().c_str(), i, destinationSlicers_p[i]->length().toString().c_str()); } } return String(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/ArrayColumnBase.h000066400000000000000000000430671476623553700212220ustar00rootroot00000000000000//# ArrayColumnBase.h: base class for access to an array table column //# Copyright (C) 2013 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_ARRAYCOLUMNBASE_H #define TABLES_ARRAYCOLUMNBASE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class BaseSlicesFunctor; class RefRows; class ColumnSlicer; class IPosition; class Slice; class Slicer; class String; // // Read and write access to an array table column with arbitrary data type // // // // // //
      • Table //
      • TableColumn // // // ArrayColumn gives read and write access to an column in a table // containing an array with data type T. // // // ArrayColumnBase is the base class of the templated class ArrayColumn // which allows readonly access to a column containing arrays with an // arbitrary data type. It can handle direct as well as indirect arrays. // // All get and put functions are implemented in this base class as // non-templated functions. Type-specific operations are done by means // of virtual functions in the Array classes. // // // See module Tables. // class ArrayColumnBase : public TableColumn { public: // The default constructor creates a null object, i.e. it // does not reference a table column. ArrayColumnBase(); // Construct for the given column in the given table. ArrayColumnBase (const Table&, const String& columnName); // Construct from the given table column. // This constructor is useful if first a table column was constructed, // its type is determined and thereafter used to construct the // correct column object. ArrayColumnBase (const TableColumn& column); // Copy constructor (reference semantics). ArrayColumnBase (const ArrayColumnBase&); ~ArrayColumnBase(); // Assignment uses reference semantics, thus works the same // as function reference. ArrayColumnBase& operator= (const ArrayColumnBase&); // Change the reference to another column. // This is in fact an assignment operator with reference semantics. // It removes the reference to the current column and creates // a reference to the column referenced in the other object. // It will handle null objects correctly. void reference (const ArrayColumnBase&); // Get the #dimensions of an array in a particular cell. // If the cell does not contain an array, 0 is returned. // Use the function isDefined to test if the cell contains an array. uInt ndim (rownr_t rownr) const { TABLECOLUMNCHECKROW(rownr); return baseColPtr_p->ndim (rownr); } // Get the shape of an array in a particular cell. // If the cell does not contain an array, a 0-dim shape is returned. // Use the function isDefined to test if the cell contains an array. IPosition shape (rownr_t rownr) const { TABLECOLUMNCHECKROW(rownr); return baseColPtr_p->shape (rownr); } // Counterparts of the acbGet() functions below not checking shapes, etc. // They are faster and can be used for performance reasons if one // knows for sure that the arguments are correct. // E.g., they are used internally in virtual column engines. // void baseGet (rownr_t rownr, ArrayBase& array) const { baseColPtr_p->getArray (rownr, array); } void baseGetSlice (rownr_t rownr, const Slicer& arraySection, ArrayBase& array) const { baseColPtr_p->getSlice (rownr, arraySection, array); } // // Get the array value in a particular cell (i.e. table row). // The row numbers count from 0 until #rows-1. void acbGet (rownr_t rownr, ArrayBase& array, Bool resize) const; // Get a slice of an N-dimensional array in a particular cell // (i.e. table row). // The row numbers count from 0 until #rows-1. // The dimensionality of the slice must match the dimensionality // of the table array and the slice definition should not exceed // the shape of the table array. void acbGetSlice (rownr_t rownr, const Slicer& arraySection, ArrayBase& array, Bool resize) const; // Get an irregular slice of an N-dimensional array in a particular cell // (i.e. table row) as given by the vectors of Slice objects. // The outer vector represents the array axes. // A missing or empty axis means the entire axis. // The inner vector represents the slices to take for each axis. // For example, to get slices from 2-dim arrays: // // Vector > slices(2); // 2-dim // slices[1].resize (3); // 3 slices in 2nd dim // slices[1][0] = Slice(100,20); // slices[1][1] = Slice(200,18); // slices[1][2] = Slice(538,30,2); // // Get data. Vector of first axis is empty, thus entire axis is read. // Array data = dataCol.getColumn (slices); // // If the column contains n-dim arrays, the resulting array is (n+1)-dim. // with the last dimension representing the number of rows and the // other dimensions representing the shape of the slice. // The arrays in the column must have the same shape in all cells. void acbGetSlice (rownr_t rownr, const Vector >& arraySlices, ArrayBase& arr, Bool resize) const; // Get the array of all values in a column. // If the column contains n-dim arrays, the resulting array is (n+1)-dim // with the last dimension representing the number of rows. // The arrays in the column must have the same shape in all cells. void acbGetColumn (ArrayBase& array, Bool resize) const; // Get regular slices from all arrays in the column. // If the column contains n-dim arrays, the resulting array is (n+1)-dim. // with the last dimension representing the number of rows and the // other dimensions representing the shape of the slice. // The arrays in the column must have the same shape in all cells. void acbGetColumn (const Slicer& arraySection, ArrayBase& array, Bool resize) const; // Get irregular slices from all arrays in the column as given by the // vectors of Slice objects. The outer vector represents the array axes. // A missing or empty axis means the entire axis. // The inner vector represents the slices to take for each axis. // For example, to get slices from 2-dim arrays: // // Vector > slices(2); // 2-dim // slices[1].resize (3); // 3 slices in 2nd dim // slices[1][0] = Slice(100,20); // slices[1][1] = Slice(200,18); // slices[1][2] = Slice(538,30,2); // // Get data. Vector of first axis is empty, thus entire axis is read. // Array data = dataCol.getColumn (slices); // // If the column contains n-dim arrays, the resulting array is (n+1)-dim. // with the last dimension representing the number of rows and the // other dimensions representing the shape of the slice. // The arrays in the column must have the same shape in all cells. void acbGetColumn (const Vector >& arraySection, ArrayBase& array, Bool resize) const; // Get the array of some values in a column. // The Slicer object can be used to specify start, end (or length), // and stride of the rows to get. // If the column contains n-dim arrays, the resulting array is (n+1)-dim // with the last dimension representing the number of rows in the slicer. // The arrays in the column must have the same shape in all those cells. void acbGetColumnRange (const Slicer& rowRange, ArrayBase& arr, Bool resize) const; void acbGetColumnCells (const RefRows& rownrs, ArrayBase& arr, Bool resize) const; // Get slices from some arrays in a column. // The first Slicer object can be used to specify start, end (or length), // and stride of the rows to get. The second Slicer object can be // used to specify the slice to take from each array. // If the column contains n-dim arrays, the resulting array is (n+1)-dim // with the last dimension representing the number of rows in the slicer. // The arrays in the column must have the same shape in all those cells. // void acbGetColumnRange (const Slicer& rowRange, const Slicer& arraySection, ArrayBase& arr, Bool resize) const; void acbGetColumnCells (const RefRows& rownrs, const Slicer& arraySection, ArrayBase& arr, Bool resize) const; // // Get various slices from the given rows. void acbGetColumnCells (const RefRows& rows, const ColumnSlicer& columnSlicer, ArrayBase& destination, Bool resize) const; // Set the shape of the array in the given row. // Setting the shape is needed if the array is put in slices, // otherwise the table system would not know the shape. // void setShape (rownr_t rownr, const IPosition& shape); // Try to store the array in a tiled way using the given tile shape. void setShape (rownr_t rownr, const IPosition& shape, const IPosition& tileShape); // // Counterparts of the acbPut() functions below not checking shapes, etc. // They are faster and can be used for performance reasons if one // knows for sure that the arguments are correct. // E.g., they are used internally in virtual column engines. // void basePut (rownr_t rownr, const ArrayBase& array) { baseColPtr_p->putArray (rownr, array); } void basePutSlice (rownr_t rownr, const Slicer& arraySection, const ArrayBase& array) { baseColPtr_p->putSlice (rownr, arraySection, array); } // // Put the array in a particular cell (i.e. table row). // The row numbers count from 0 until #rows-1. // If the shape of the table array in that cell has not already been // defined, it will be defined implicitly. void acbPut (rownr_t rownr, const ArrayBase& array); // Put into a slice of an N-dimensional array in a particular cell. // The row numbers count from 0 until #rows-1. // The shape of the table array must have been defined. // The dimensionality of the slice must match the dimensionality // of the table array and the slice definition should not exceed // the shape of the table array. void acbPutSlice (rownr_t rownr, const Slicer& arraySection, const ArrayBase& array); void acbPutSlice (rownr_t rownr, const Vector >& arraySlices, const ArrayBase& arr); // Put the array of all values in the column. // If the column contains n-dim arrays, the source array must be (n+1)-dim // with the last dimension representing the number of rows. void acbPutColumn (const ArrayBase& array); // Put into subsections of the table arrays in the entire column. // If the column contains n-dim arrays, the source array is (n+1)-dim // with the last dimension representing the number of rows and // other dimensions representing the shape of the slice. // The dimensionality of the slice must match the dimensionality // of the table array, thus must be n-dim. Also the slice definition // should not exceed the shape of the table arrays. void acbPutColumn (const Slicer& arraySection, const ArrayBase& array); void acbPutColumn (const Vector >& arraySlices, const ArrayBase& arr); // Put the array of some values in the column. // The Slicer object can be used to specify start, end (or length), // and stride of the rows to put. // If the column contains n-dim arrays, the source array must be (n+1)-dim // with the last dimension representing the number of rows in the slicer. // void acbPutColumnRange (const Slicer& rowRange, const ArrayBase& arr); void acbPutColumnCells (const RefRows& rownrs, const ArrayBase& arr); // // Put into subsection of the table arrays in some rows of the column. // The first Slicer object can be used to specify start, end (or length), // and stride of the rows to put. The second Slicer object can be // used to specify the slice to take from each array. // If the column contains n-dim arrays, the source array must be (n+1)-dim // with the last dimension representing the number of rows in the slicer. // void acbPutColumnRange (const Slicer& rowRange, const Slicer& arraySection, const ArrayBase& arr); void acbPutColumnCells (const RefRows& rownrs, const Slicer& arraySection, const ArrayBase& arr); // // Put various slices in the given rows. // void acbPutColumnCells (const RefRows& rows, const Vector >& arraySlices, const ArrayBase& source); void acbPutColumnCells (const RefRows& rows, const ColumnSlicer& columnSlicer, const ArrayBase& source); // // Put the same value in all cells of the column. void acbFillColumn (const ArrayBase& value); // Put the contents of that column into this one. void acbPutColumn (const ArrayColumnBase& that); // Adapt the shape of the array if possible. If the array is empty or // if resize=True, the array is resized if needed. // Otherwise checkShape is used to throw an exception if not conforming. void adaptShape (const IPosition& shp, ArrayBase& arr, Bool resize, Int64 rownr, const String& where) const; // Throw an exception if the array does not have the expected shape. // However, False is returned if noSlicing and canChangeShape_p are True // (meaning no slices are put and the shape of a full row can change). // The column name is made part of the error message, as well as the rownr // if it is not negative (meaning a put of a column). Bool checkShape (const IPosition& expShape, const IPosition& arrShape, Bool noSlicing, Int64 rownr, const String& where) const; // A common function used by all functions that can get or put irregular // array slices. The functor performs the get or put operation. void handleSlices (const Vector >& slices, BaseSlicesFunctor& functor, const Slicer& slicer, const ArrayBase& array) const; }; // // ColumnSlicer is used in one of the ArrayColumn::getColumnCells functions. // That method takes a potentially complex/ selection of data out of a // column cell (e.g., multiple slices along each axis) and then puts them // into a selection of a destination array. // This is most easily represented as a set of source,destination slicers // where one is applied to the cell and the other to the destination array. // class ColumnSlicer { public: // Construct the object. // It takes over the pointers to the Slicer objects and deletes them // in the destructor. // The shape parameter is the shape of the destination array excluding // the row axis. ColumnSlicer (const IPosition& shape, const Vector& dataSlicers, const Vector& destinationSlicers); // The destructor deletes all Slicer objects. ~ColumnSlicer(); // Get the data slicers. const Vector& getDataSlicers() const { return dataSlicers_p; } // Get the desintation slicers. const Vector& getDestinationSlicers() const { return destinationSlicers_p; } // Get the shape. const IPosition& shape() const { return shape_p; } private: // Delete all Slicer objects. void freeSlicers(); // Check if the slicers match the array shape. String validateParameters() const; //# Data members. Vector dataSlicers_p; Vector destinationSlicers_p; IPosition shape_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/ArrayColumnFunc.h000066400000000000000000000071061476623553700212350ustar00rootroot00000000000000//# ArrayColumnFunbc.h: Functors to operate on an ArrayColumn //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_ARRAYCOLUMNFUNC_H #define TABLES_ARRAYCOLUMNFUNC_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Abstract baseclass for slices functors // // There are several ArrayColumn functions to get or put irregular array // slices. ArrayColumnBase::handleSlices is used to perform all common // operations using a functor derived from this base class. // class BaseSlicesFunctor { public: virtual ~BaseSlicesFunctor() {} virtual void apply (const Slicer& slicer, ArrayBase& arr) = 0; }; // Functor to get irregular array slices from a cell class GetCellSlices : public BaseSlicesFunctor { public: GetCellSlices (const ArrayColumnBase& col, rownr_t rownr) : itsCol(col), itsRow(rownr) {} virtual void apply (const Slicer& slicer, ArrayBase& arr) { itsCol.baseGetSlice (itsRow, slicer, arr); } private: const ArrayColumnBase& itsCol; rownr_t itsRow; }; // Functor to get irregular array slices from a column class GetColumnSlices : public BaseSlicesFunctor { public: GetColumnSlices (const ArrayColumnBase& col) : itsCol(col) {} virtual void apply (const Slicer& slicer, ArrayBase& arr) { itsCol.acbGetColumn (slicer, arr, False); } private: const ArrayColumnBase& itsCol; }; // Functor to put irregular array slices into a cell class PutCellSlices : public BaseSlicesFunctor { public: PutCellSlices (ArrayColumnBase& col, rownr_t rownr) : itsCol(col), itsRow(rownr) {} virtual void apply (const Slicer& slicer, ArrayBase& arr) { itsCol.basePutSlice (itsRow, slicer, arr); } private: ArrayColumnBase& itsCol; rownr_t itsRow; }; // Functor to get irregular array slices from a column class PutColumnSlices : public BaseSlicesFunctor { public: PutColumnSlices (ArrayColumnBase& col) : itsCol(col) {} virtual void apply (const Slicer& slicer, ArrayBase& arr) { itsCol.acbPutColumn (slicer, arr); } private: ArrayColumnBase& itsCol; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/ArrayColumn_tmpl.cc000066400000000000000000000034421476623553700216120ustar00rootroot00000000000000//# ArrayColumn_tmpl.cc: Explicit ArrayColumn template instantiations //# Copyright (C) 2015 //# Associated Universities, Inc. Washington DC, USA, //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include //# Instantiate extern templates for often used types. namespace casacore { template class ArrayColumn; template class ArrayColumn; template class ArrayColumn; template class ArrayColumn; template class ArrayColumn; template class ArrayColumn; template class ArrayColumn; template class ArrayColumn; template class ArrayColumn; template class ArrayColumn; template class ArrayColumn; template class ArrayColumn; } casacore-3.7.1/tables/Tables/BaseColDesc.cc000066400000000000000000000231321476623553700204270ustar00rootroot00000000000000//# BaseColDesc.cc: Abstract base class for table column descriptions //# Copyright (C) 1994,1995,1996,1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN BaseColumnDesc::BaseColumnDesc (const String& name, const String& comment, const String& dataManType, const String& dataManGroup, DataType dt, const String& dtId, Int opt, uInt ndim, const IPosition& shape, Bool isScalar, Bool isArray, Bool isTable) : colName_p (name), comment_p (comment), dataManType_p (dataManType), dataManGroup_p(dataManGroup), dtype_p (dt), dtypeId_p (dtId), option_p (opt), nrdim_p (ndim), shape_p (shape), maxLength_p (0), isScalar_p (isScalar), isArray_p (isArray), isTable_p (isTable) { //# Option Direct forces FixedShape. if ((option_p & ColumnDesc::Direct) == ColumnDesc::Direct) { option_p |= ColumnDesc::FixedShape; } // A shape can only be given for a FixedShape array. if (shape_p.nelements() > 0) { option_p |= ColumnDesc::FixedShape; } // Option Undefined can only be set for standard types. if (dtype_p == TpOther) { if ((option_p & ColumnDesc::Undefined) == ColumnDesc::Undefined) { throw (TableInvColumnDesc (name, "Option Undefined only allowed for standard data types")); } } // Set the default data manager type and group (if empty). setDefaultDataManager (False); // Create the keyword set. keySetPtr_p = new TableRecord(); } BaseColumnDesc::BaseColumnDesc (const BaseColumnDesc& that) : colName_p (that.colName_p), comment_p (that.comment_p), dataManType_p (that.dataManType_p), dataManGroup_p(that.dataManGroup_p), dtype_p (that.dtype_p), dtypeId_p (that.dtypeId_p), option_p (that.option_p), nrdim_p (that.nrdim_p), shape_p (that.shape_p), maxLength_p (that.maxLength_p), isScalar_p (that.isScalar_p), isArray_p (that.isArray_p), isTable_p (that.isTable_p) { keySetPtr_p = new TableRecord(*that.keySetPtr_p); } BaseColumnDesc::~BaseColumnDesc () { delete keySetPtr_p; } BaseColumnDesc& BaseColumnDesc::operator= (const BaseColumnDesc& that) { colName_p = that.colName_p; comment_p = that.comment_p; dataManType_p = that.dataManType_p; dataManGroup_p = that.dataManGroup_p; dtype_p = that.dtype_p; dtypeId_p = that.dtypeId_p; option_p = that.option_p; nrdim_p = that.nrdim_p; shape_p.resize (that.shape_p.nelements()); shape_p = that.shape_p; maxLength_p = that.maxLength_p; *keySetPtr_p = *that.keySetPtr_p; isScalar_p = that.isScalar_p; isArray_p = that.isArray_p; isTable_p = that.isTable_p; return *this; } //# Derived classes can implement their own versions //# and call this basic implementation when needed. void BaseColumnDesc::checkAdd (const ColumnDescSet&) const {} void BaseColumnDesc::checkRename (const ColumnDescSet&, const String&) const {} void BaseColumnDesc::handleAdd (ColumnDescSet&) {} void BaseColumnDesc::handleRename (ColumnDescSet&, const String&) {} void BaseColumnDesc::handleRemove (ColumnDescSet&) {} void BaseColumnDesc::renameAction (const String&, const String&) {} void BaseColumnDesc::setDefaultDataManager (Bool always) { // The default data manager for standard types is StandardStMan. // For other types it is the virtual column engine handling // that type. if (always || dataManType_p.empty()) { if (dtype_p == TpOther) { if (isScalar()) { dataManType_p = dtypeId_p + "VSCEngine"; } else { dataManType_p = dtypeId_p + "VACEngine"; } } else { dataManType_p = "StandardStMan"; } } // The default data manager group for standard types is data manager type. // For other types it is the column name to make the group unique. if (always || dataManGroup_p.empty()) { if (dtype_p == TpOther) { dataManGroup_p = colName_p; } else { dataManGroup_p = dataManType_p; } } } // Dimensionality can only be changed if not set yet. void BaseColumnDesc::setNdim (uInt ndim) { if (!isArray()) { throw (TableInvOper ("setNdim: column " + colName_p + " is no array")); } if (ndim > 0 && nrdim_p > 0) { throw (TableInvOper ("setNdim(): dimensionality of column " + colName_p + " already defined")); } nrdim_p = ndim; if (nrdim_p <= 0) { nrdim_p = -1; shape_p.resize (0); } } void BaseColumnDesc::setShape (const IPosition& shape) { if (!isArray()) { throw (TableInvOper ("setShape: column " + colName_p + " is no array")); } if (shape_p.nelements() > 0) { throw (TableInvOper ("setShape(): shape of column " + colName_p + " already defined")); } if (nrdim_p > 0 && Int(shape.nelements()) != nrdim_p) { throw (TableInvOper ("setShape(): dimensionality of column " + colName_p + " mismatches new shape")); } shape_p = shape; nrdim_p = shape.nelements(); if (nrdim_p <= 0) { nrdim_p = -1; } option_p |= ColumnDesc::FixedShape; } void BaseColumnDesc::setShape (const IPosition& shape, Bool directOption) { setShape (shape); if (directOption) { option_p |= ColumnDesc::Direct; } else { option_p &= ~ColumnDesc::Direct; } } void BaseColumnDesc::setOptions (Int options) { option_p = options; //# Option Direct forces FixedShape. if ((option_p & ColumnDesc::Direct) == ColumnDesc::Direct) { option_p |= ColumnDesc::FixedShape; } // Option Undefined can only be set for standard types. if (dtype_p == TpOther) { if ((option_p & ColumnDesc::Undefined) == ColumnDesc::Undefined) { throw (TableInvColumnDesc (colName_p, "setOptions: Undefined only allowed for standard data types")); } } if ((option_p & ColumnDesc::FixedShape) != ColumnDesc::FixedShape) { nrdim_p = -1; shape_p.resize (0); } } void BaseColumnDesc::setMaxLength (uInt maxLength) { if (dtype_p != TpString) { throw (TableInvOper ("setMaxLength: column " + colName_p + " contains no string values")); } maxLength_p = maxLength; } //# By default no table description gets returned. TableDesc* BaseColumnDesc::tableDesc() { throw (TableInvOper ("tableDesc(): column " + colName_p + " is no subtable")); return 0; } //# Put the XXXColumnDesc object. //# First the base class variables are written, then the virtual //# function putDesc is called to write the specific variables. //# Note that the data is read back by the ctor taking AipsIO. //# It was felt that putstart takes too much space, so therefore //# the version is put "manually". void BaseColumnDesc::putFile (AipsIO& ios, const TableAttr& parentAttr) const { ios << (uInt)1; // class version 1 ios << colName_p; ios << comment_p; ios << dataManType_p; ios << dataManGroup_p; Int dt = dtype_p; ios << dt; ios << option_p; ios << nrdim_p; if (!isScalar_p) { ios << shape_p; } ios << maxLength_p; keySetPtr_p->putRecord (ios, parentAttr); putDesc(ios); } void BaseColumnDesc::getFile (AipsIO& ios, const TableAttr& parentAttr) { uInt version; ios >> version; ios >> colName_p; ios >> comment_p; ios >> dataManType_p; ios >> dataManGroup_p; Int dtype; ios >> dtype; if (dtype != dtype_p) { throw (TableInternalError ("BaseColumnDesc: data type read mismatch" " for column " + colName_p)); } ios >> option_p; ios >> nrdim_p; if (!isScalar_p > 0) { ios >> shape_p; } ios >> maxLength_p; keySetPtr_p->getRecord (ios, parentAttr); getDesc (ios); } //# Create a RefColumn object from the description. RefColumn* BaseColumnDesc::makeRefColumn (RefTable* rtp, BaseColumn* bcp) const { return new RefColumn (this, rtp, bcp); } ConcatColumn* BaseColumnDesc::makeConcatColumn (ConcatTable* ctp) const { return new ConcatColumn (this, ctp); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/BaseColDesc.h000066400000000000000000000263241476623553700202770ustar00rootroot00000000000000//# BaseColDesc.h: an abstract base class for column descriptions //# Copyright (C) 1994,1995,1996,1997,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_BASECOLDESC_H #define TABLES_BASECOLDESC_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class AipsIO; class ColumnDescSet; class TableRecord; class TableAttr; class BaseColumn; class PlainColumn; class RefTable; class RefColumn; class ConcatTable; class ConcatColumn; class TableDesc; class ColumnSet; // // An abstract base class for table column descriptions // // // // // //
      • Tables module (see especially Tables.h, the module header file) //
      • The description of letter/envelope class design, and its // application to the family of classes of which BaseColumnDesc // is a part, in ColumnDesc.h //
      • TableDesc // // // "Base" indicates that this is a base class for the specialized // column description classes. // // // BaseColumnDesc is an abstract class describing a column in a table. // Various XXXXColumnDesc classes are derived from it to describe // the various types of columns, among them ArrayColumnDesc // and ScalarcolumnDesc. These derived classes are used to // construct a column description which can be added to the TableDesc. // // BaseColumnDesc contains functions common to all kinds of column // descriptions. // It contains a TableRecord to attach simple keywords to the // column description (e.g. to define a scale-factor). This keyword set // serves as the initial keyword set for the column when a table // gets instantiated from a table description. // // The ColumnDesc class is an envelope around BaseColumnDesc, which // can be used to get information about existing column descriptions. // // // This abstract base class defines the common features required of all // concrete column description classes. It also provides the hook (for // letter objects) required by ColumnDesc, the envelope class. // // //# A List of bugs, limitations, extensions or planned refinements. // class BaseColumnDesc { //# Allow ColumnDesc to call the private functions checkAdd, etc.. friend class ColumnDesc; public: // Construct the column base object. BaseColumnDesc (const String& name, const String& comment, const String& dataManagerType, const String& dataManagerGroup, DataType, const String& dataTypeId, Int options, uInt ndim, const IPosition& shape, Bool isScalar, Bool isArray, Bool isTable); // Copy constructor (copy semantics). BaseColumnDesc (const BaseColumnDesc&); virtual ~BaseColumnDesc (); // Get access to the set of keywords. // TableRecord& rwKeywordSet() { return *keySetPtr_p; } const TableRecord& keywordSet() const { return *keySetPtr_p; } // // Show the column. virtual void show (ostream& os) const = 0; // Get the name of the column. const String& name() const { return colName_p; } // Get the data type of the column. DataType dataType() const { return dtype_p; } // Get the type id for non-standard data types (i.e. for TpOther). // For standard data types the returned string is empty. const String& dataTypeId() const { return dtypeId_p; } // Get the type name of the default data manager. const String& dataManagerType() const { return dataManType_p; } // Get the type name of the default data manager. // (allowing it to be changed) String& dataManagerType() { return dataManType_p; } // Get the data manager group. const String& dataManagerGroup() const { return dataManGroup_p; } // Get the data manager group. // (allowing it to be changed) String& dataManagerGroup() { return dataManGroup_p; } // Set the data manager type and group to the default. // If always==True they are always set, otherwise only if empty. void setDefaultDataManager (Bool always); // Get comment string. const String& comment() const { return comment_p; } // Get comment string (allowing it to be changed). String& comment() { return comment_p; } // Get the options. Int options() const { return option_p; } // Test if column is scalar, array or table. // Bool isScalar() const { return isScalar_p; } Bool isArray() const { return isArray_p; } Bool isTable() const { return isTable_p; } // // Get the number of dimensions. Int ndim() const { return nrdim_p; } // Get the predefined shape. // If not defined, a zero shape will be returned. const IPosition& shape() const { return shape_p; } // Set the number of dimensions. // This is only allowed for arrays. // ndim can be zero to clear the number of dimensions // and the shape. // Otherwise it can only be used if the dimensionality has not been // defined yet. void setNdim (uInt ndim); // Set the predefined shape. // This is only allowed for arrays, for which the shape // has not been defined yet. // If the dimensionality has already been defined, it must match. // It will set the option FixedShape if not set yet. //
        The first version leaves the Direct option as is. // The second version sets the Direct option as given. // void setShape (const IPosition& shape); void setShape (const IPosition& shape, Bool directOption); // // Set the options to the given value. // Option ColumnDesc::Direct forces FixedShape. // If FixedShape is not given (implicitly or explicitly), // the column can have no shape, so its shape is cleared. void setOptions (Int options); // Get the maximum value length. uInt maxLength() const { return maxLength_p; } // Set the maximum value length. // So far, this is only possible for columns containing String values. // An exception is thrown if the column data type is not TpString. // Some storage managers support fixed length strings and can store // them more efficiently than variable length strings. void setMaxLength (uInt maxLength); // Get table description (in case column contains subtables). // //# Use the non-const version to implement the const one. //# The cast is fully safe, because it returns a const object. const TableDesc* tableDesc() const { return ((BaseColumnDesc*)this)->tableDesc(); } virtual TableDesc* tableDesc(); // protected: String colName_p; //# column name String comment_p; //# comment String dataManType_p; //# default data manager type String dataManGroup_p; //# data manager group DataType dtype_p; //# datatype String dtypeId_p; //# datatype id for TpOther Int option_p; //# column options Int nrdim_p; //# #dimensions (<0 = unknown) IPosition shape_p; //# table array shape uInt maxLength_p; //# maximum value length (for strings) TableRecord* keySetPtr_p; //# set of keywords Bool isScalar_p; //# True = column contains scalars Bool isArray_p; //# True = column contains arrays Bool isTable_p; //# True = column contains tables // Assignment (copy semantics). BaseColumnDesc& operator= (const BaseColumnDesc&); // Put the object. // It uses putDesc to put the derived object. void putFile (AipsIO&, const TableAttr&) const; // Get the object. // It uses getDesc to get the derived object. void getFile (AipsIO&, const TableAttr&); // Put the derived object. virtual void putDesc (AipsIO&) const = 0; // Get the derived object. virtual void getDesc (AipsIO&) = 0; // Make a PlainColumn object out of the description. virtual PlainColumn* makeColumn (ColumnSet*) const = 0; // Make a RefColumn object out of the description. RefColumn* makeRefColumn (RefTable*, BaseColumn*) const; // Make a ConcatColumn object out of the description. // The default makes a ConcatColumn object. // Derived classes (ScalarColumnDesc) can make more specialized objects. virtual ConcatColumn* makeConcatColumn (ConcatTable*) const; private: // Check if a column can be handled by ColumnDescSet. // This gives, for instance, the virtual column class the opportunity // to check if the used columns exist. // virtual void checkAdd (const ColumnDescSet& cds) const; virtual void checkRename (const ColumnDescSet& cds, const String& newName) const; // // Take action after a column has been handled by ColumnDescSet. // This gives, for instance, the virtual column class the opportunity // to update the virtual column list. // virtual void handleAdd (ColumnDescSet& cds); virtual void handleRename (ColumnDescSet& cds, const String& oldName); virtual void handleRemove (ColumnDescSet& cds); // // This function allows each column to act upon a rename of another column. // If the old name is used internally, the column can update itself. virtual void renameAction (const String& newName, const String& oldName); public: // Clone a column description (creating a new column description object). virtual BaseColumnDesc* clone() const = 0; // Get the underlying class name. virtual String className() const = 0; // Set the name of the column (for a rename). void setName (const String& name) { colName_p = name; } }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/BaseColumn.cc000066400000000000000000000565041476623553700203610ustar00rootroot00000000000000//# BaseColumn.cc: Abstract base class for a table column //# Copyright (C) 1994,1995,1996,1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN BaseColumn::BaseColumn (const BaseColumnDesc* cdp) : colDescPtr_p(cdp) {} BaseColumn::~BaseColumn() {} //# By default all functions throw an exception //# to ensure they are called correctly. void BaseColumn::setShape (rownr_t, const IPosition&) { throw (TableInvOper ("invalid setShape() for column " + colDescPtr_p->name() + "; only valid for an array")); } void BaseColumn::setShape (rownr_t, const IPosition&, const IPosition&) { throw (TableInvOper ("invalid setShape() for column " + colDescPtr_p->name() + "; only valid for an array")); } uInt BaseColumn::ndimColumn() const { throw (TableInvOper ("invalid ndimColumn() for column " + colDescPtr_p->name() + "; only valid for an array")); return 0; } IPosition BaseColumn::shapeColumn() const { throw (TableInvOper ("invalid shapeColumn() for column " + colDescPtr_p->name() + "; only valid for an array")); return IPosition(0); } uInt BaseColumn::ndim (rownr_t) const { throw (TableInvOper ("invalid ndim() for column " + colDescPtr_p->name() + "; only valid for an array")); return 0; } IPosition BaseColumn::shape (rownr_t) const { throw (TableInvOper ("invalid shape() for column " + colDescPtr_p->name() + "; only valid for an array")); return IPosition(0); } IPosition BaseColumn::tileShape (rownr_t) const { throw (TableInvOper ("invalid tileShape() for column " + colDescPtr_p->name() + "; only valid for an array")); } Bool BaseColumn::canChangeShape() const { return False; // can not be changed } void BaseColumn::get (rownr_t, void*) const { throw (TableInvOper ("get() not implemented for column " + colDesc_p.name() + "; only valid for a scalar")); } void BaseColumn::getArray (rownr_t, ArrayBase&) const { throw (TableInvOper ("getArray() not implemented for column " + colDesc_p.name() + "; only valid for an array")); } void BaseColumn::getSlice (rownr_t, const Slicer&, ArrayBase&) const { throw (TableInvOper ("getSlice() not implemented for column " + colDescPtr_p->name() + "; only valid for an array")); } void BaseColumn::getScalarColumn (ArrayBase&) const { throw (TableInvOper ("getScalarColumn() not implemented for column " + colDescPtr_p->name() + "; only valid for a scalar")); } void BaseColumn::getArrayColumn (ArrayBase&) const { throw (TableInvOper ("getArrayColumn() not implemented for column " + colDescPtr_p->name() + "; only valid for an array")); } void BaseColumn::getScalarColumnCells (const RefRows&, ArrayBase&) const { throw (TableInvOper ("getScalarColumnCells() not implemented for column " + colDescPtr_p->name() + "; only valid for a scalar")); } void BaseColumn::getArrayColumnCells (const RefRows&, ArrayBase&) const { throw (TableInvOper ("getArrayColumnCells() not implemented for column " + colDescPtr_p->name() + "; only valid for an array")); } void BaseColumn::getColumnSliceCells (const RefRows&, const Slicer&, ArrayBase&) const { throw (TableInvOper ("getColumnCells(Slicer&) not implemented for column " + colDescPtr_p->name() + "; only valid for an array")); } void BaseColumn::getColumnSlice (const Slicer&, ArrayBase&) const { throw (TableInvOper ("getColumn(Slicer&) not implemented for column " + colDescPtr_p->name() + "; only valid for an array")); } void BaseColumn::put (rownr_t, const void*) { throw (TableInvOper ("put() not implemented for column " + colDesc_p.name() + "; only valid for a scalar")); } void BaseColumn::putArray (rownr_t, const ArrayBase&) { throw (TableInvOper ("putArray() not implemented for column " + colDesc_p.name() + "; only valid for an array")); } void BaseColumn::putSlice (rownr_t, const Slicer&, const ArrayBase&) { throw (TableInvOper ("putSlice() not implemented for column " + colDescPtr_p->name() + "; only valid for an array")); } void BaseColumn::putScalarColumn (const ArrayBase&) { throw (TableInvOper ("putScalarColumn() not implemented for column " + colDescPtr_p->name() + "; only valid for a scalar")); } void BaseColumn::putArrayColumn (const ArrayBase&) { throw (TableInvOper ("putArrayColumn() not implemented for column " + colDescPtr_p->name() + "; only valid for an array")); } void BaseColumn::putScalarColumnCells (const RefRows&, const ArrayBase&) { throw (TableInvOper ("putScalarColumnCells() not implemented for column " + colDescPtr_p->name() + "; only valid for a scalar")); } void BaseColumn::putArrayColumnCells (const RefRows&, const ArrayBase&) { throw (TableInvOper ("putArrayColumnCells() not implemented for column " + colDescPtr_p->name() + "; only valid for an array")); } void BaseColumn::putColumnSlice (const Slicer&, const ArrayBase&) { throw (TableInvOper ("putColumn(Slicer&) not implemented for column " + colDescPtr_p->name() + "; only valid for an array")); } void BaseColumn::putColumnSliceCells (const RefRows&, const Slicer&, const ArrayBase&) { throw (TableInvOper ("putColumnCells(Slicer&) not implemented for column " + colDescPtr_p->name() + "; only valid for an array")); } void BaseColumn::makeSortKey (Sort&, std::shared_ptr&, Int, std::shared_ptr&) { throw (TableInvOper ("makeSortKey() for column " + colDescPtr_p->name() + " is only valid for a scalar")); } void BaseColumn::makeRefSortKey (Sort&, std::shared_ptr&, Int, const Vector&, std::shared_ptr&) { throw (TableInvOper ("makeSortKey(rownrs) for column " + colDescPtr_p->name() + " is only valid for a scalar")); } void BaseColumn::allocIterBuf (void*&, void*&, std::shared_ptr&) { throw (TableInvOper ("allocIterBuf() for column " + colDescPtr_p->name() + " is only valid for a scalar")); } void BaseColumn::freeIterBuf (void*&, void*&) { throw (TableInvOper ("freeIterBuf() for column " + colDescPtr_p->name() + " is only valid for a scalar")); } void BaseColumn::getScalar (rownr_t rownr, Bool& value) const { if (!colDescPtr_p->isScalar()) { throwGetScalar(); } switch (colDescPtr_p->dataType()) { case TpBool: get (rownr, &value); return; default: throwGetType("Bool"); } } void BaseColumn::getScalar (rownr_t rownr, uChar& value) const { if (!colDescPtr_p->isScalar()) { throwGetScalar(); } switch (colDescPtr_p->dataType()) { case TpUChar: get (rownr, &value); return; default: throwGetType("uChar"); } } void BaseColumn::getScalar (rownr_t rownr, Short& value) const { if (!colDescPtr_p->isScalar()) { throwGetScalar(); } switch (colDescPtr_p->dataType()) { case TpShort: get (rownr, &value); return; default: throwGetType("Short"); } } void BaseColumn::getScalar (rownr_t rownr, uShort& value) const { if (!colDescPtr_p->isScalar()) { throwGetScalar(); } switch (colDescPtr_p->dataType()) { case TpUChar: uChar valuc; get (rownr, &valuc); value = valuc; return; case TpUShort: get (rownr, &value); return; default: throwGetType("uShort"); } } void BaseColumn::getScalar (rownr_t rownr, Int& value) const { if (!colDescPtr_p->isScalar()) { throwGetScalar(); } switch (colDescPtr_p->dataType()) { case TpUChar: uChar valuc; get (rownr, &valuc); value = valuc; return; case TpShort: Short vals; get (rownr, &vals); value = vals; return; case TpUShort: uShort valus; get (rownr, &valus); value = valus; return; case TpInt: get (rownr, &value); return; default: throwGetType("Int"); } } void BaseColumn::getScalar (rownr_t rownr, uInt& value) const { if (!colDescPtr_p->isScalar()) { throwGetScalar(); } switch (colDescPtr_p->dataType()) { case TpUChar: uChar valuc; get (rownr, &valuc); value = valuc; return; case TpUShort: uShort valus; get (rownr, &valus); value = valus; return; case TpUInt: get (rownr, &value); return; default: throwGetType("uInt"); } } void BaseColumn::getScalar (rownr_t rownr, Int64& value) const { if (!colDescPtr_p->isScalar()) { throwGetScalar(); } switch (colDescPtr_p->dataType()) { case TpUChar: uChar valuc; get (rownr, &valuc); value = valuc; return; case TpShort: Short vals; get (rownr, &vals); value = vals; return; case TpUShort: uShort valus; get (rownr, &valus); value = valus; return; case TpInt: Int vali; get (rownr, &vali); value = vali; return; case TpUInt: uInt valui; get (rownr, &valui); value = valui; return; case TpInt64: get (rownr, &value); return; default: throwGetType("Int64"); } } void BaseColumn::getScalar (rownr_t rownr, float& value) const { if (!colDescPtr_p->isScalar()) { throwGetScalar(); } switch (colDescPtr_p->dataType()) { case TpUChar: uChar valuc; get (rownr, &valuc); value = valuc; return; case TpShort: Short vals; get (rownr, &vals); value = vals; return; case TpUShort: uShort valus; get (rownr, &valus); value = valus; return; case TpInt: Int vali; get (rownr, &vali); value = vali; return; case TpUInt: uInt valui; get (rownr, &valui); value = valui; return; case TpInt64: Int64 vali64; get (rownr, &vali64); value = vali64; return; case TpFloat: get (rownr, &value); return; case TpDouble: double vald; get (rownr, &vald); value = vald; return; default: throwGetType("float"); } } void BaseColumn::getScalar (rownr_t rownr, double& value) const { if (!colDescPtr_p->isScalar()) { throwGetScalar(); } switch (colDescPtr_p->dataType()) { case TpUChar: uChar valuc; get (rownr, &valuc); value = valuc; return; case TpShort: Short vals; get (rownr, &vals); value = vals; return; case TpUShort: uShort valus; get (rownr, &valus); value = valus; return; case TpInt: Int vali; get (rownr, &vali); value = vali; return; case TpUInt: uInt valui; get (rownr, &valui); value = valui; return; case TpInt64: Int64 vali64; get (rownr, &vali64); value = vali64; return; case TpFloat: float valf; get (rownr, &valf); value = valf; return; case TpDouble: get (rownr, &value); return; default: throwGetType("double"); } } void BaseColumn::getScalar (rownr_t rownr, Complex& value) const { if (!colDescPtr_p->isScalar()) { throwGetScalar(); } switch (colDescPtr_p->dataType()) { case TpUChar: uChar valuc; get (rownr, &valuc); value = Complex ((float) valuc); return; case TpShort: Short vals; get (rownr, &vals); value = Complex ((float) vals); return; case TpUShort: uShort valus; get (rownr, &valus); value = Complex ((float) valus); return; case TpInt: Int vali; get (rownr, &vali); value = Complex ((float) vali); return; case TpUInt: uInt valui; get (rownr, &valui); value = Complex ((float) valui); return; case TpInt64: Int64 vali64; get (rownr, &vali64); value = vali64; return; case TpFloat: float valf; get (rownr, &valf); value = Complex (valf); return; case TpDouble: double vald; get (rownr, &vald); value = Complex ((float) vald); return; case TpComplex: get (rownr, &value); return; case TpDComplex: { DComplex valdc; get (rownr, &valdc); value = Complex(valdc.real(), valdc.imag()); } return; default: throwGetType("Complex"); } } void BaseColumn::getScalar (rownr_t rownr, DComplex& value) const { if (!colDescPtr_p->isScalar()) { throwGetScalar(); } switch (colDescPtr_p->dataType()) { case TpUChar: uChar valuc; get (rownr, &valuc); value = DComplex ((double) valuc); return; case TpShort: Short vals; get (rownr, &vals); value = DComplex ((double) vals); return; case TpUShort: uShort valus; get (rownr, &valus); value = DComplex ((double) valus); return; case TpInt: Int vali; get (rownr, &vali); value = DComplex ((double) vali); return; case TpUInt: uInt valui; get (rownr, &valui); value = DComplex ((double) valui); return; case TpInt64: Int64 vali64; get (rownr, &vali64); value = vali64; return; case TpFloat: float valf; get (rownr, &valf); value = DComplex ((double) valf); return; case TpDouble: double vald; get (rownr, &vald); value = DComplex (vald); return; case TpComplex: { Complex valc; get (rownr, &valc); value = valc; } return; case TpDComplex: get (rownr, &value); return; default: throwGetType("DComplex"); } } void BaseColumn::getScalar (rownr_t rownr, String& value) const { if (!colDescPtr_p->isScalar()) { throwGetScalar(); } switch (colDescPtr_p->dataType()) { case TpString: get (rownr, &value); return; default: throwGetType("String"); } } void BaseColumn::getScalar (rownr_t rownr, TableRecord& value) const { if (!colDescPtr_p->isScalar()) { throwGetScalar(); } switch (colDescPtr_p->dataType()) { case TpRecord: get (rownr, &value); return; default: throwGetType("TableRecord"); } } void BaseColumn::getScalar (rownr_t rownr, void* value, const String& dataTypeId) const { if (!colDescPtr_p->isScalar()) { throwGetScalar(); } if (colDescPtr_p->dataType() != TpOther || colDescPtr_p->dataTypeId() != dataTypeId) { throwGetType("void*"); } get (rownr, value); } void BaseColumn::putScalar (rownr_t rownr, const Bool& value) { if (!colDescPtr_p->isScalar()) { throwPutScalar(); } switch (colDescPtr_p->dataType()) { case TpBool: put (rownr, &value); return; default: throwPutType("Bool"); } } void BaseColumn::putScalar (rownr_t rownr, const uChar& value) { if (!colDescPtr_p->isScalar()) { throwPutScalar(); } switch (colDescPtr_p->dataType()) { case TpUChar: put (rownr, &value); return; case TpShort: Short vals; vals = value; put (rownr, &vals); return; case TpUShort: uShort valus; valus = value; put (rownr, &valus); return; case TpInt: Int vali; vali = value; put (rownr, &vali); return; case TpUInt: uInt valui; valui = value; put (rownr, &valui); return; case TpInt64: Int64 vali64; vali64 = value; put (rownr, &vali64); return; case TpFloat: float valf; valf = value; put (rownr, &valf); return; case TpDouble: double vald; vald = value; put (rownr, &vald); return; case TpComplex: { Complex valc(value); put (rownr, &valc); } return; case TpDComplex: { DComplex valdc(value); put (rownr, &valdc); } return; default: throwPutType("uChar"); } } void BaseColumn::putScalar (rownr_t rownr, const Short& value) { if (!colDescPtr_p->isScalar()) { throwPutScalar(); } switch (colDescPtr_p->dataType()) { case TpShort: put (rownr, &value); return; case TpInt: Int vali; vali = value; put (rownr, &vali); return; case TpInt64: Int64 vali64; vali64 = value; put (rownr, &vali64); return; case TpFloat: float valf; valf = value; put (rownr, &valf); return; case TpDouble: double vald; vald = value; put (rownr, &vald); return; case TpComplex: { Complex valc(value); put (rownr, &valc); } return; case TpDComplex: { DComplex valdc(value); put (rownr, &valdc); } return; default: throwPutType("Short"); } } void BaseColumn::putScalar (rownr_t rownr, const uShort& value) { if (!colDescPtr_p->isScalar()) { throwPutScalar(); } switch (colDescPtr_p->dataType()) { case TpUShort: put (rownr, &value); return; case TpInt: Int vali; vali = value; put (rownr, &vali); return; case TpUInt: uInt valui; valui = value; put (rownr, &valui); return; case TpInt64: Int64 vali64; vali64 = value; put (rownr, &vali64); return; case TpFloat: float valf; valf = value; put (rownr, &valf); return; case TpDouble: double vald; vald = value; put (rownr, &vald); return; case TpComplex: { Complex valc(value); put (rownr, &valc); } return; case TpDComplex: { DComplex valdc(value); put (rownr, &valdc); } return; default: throwPutType("uShort"); } } void BaseColumn::putScalar (rownr_t rownr, const Int& value) { if (!colDescPtr_p->isScalar()) { throwPutScalar(); } switch (colDescPtr_p->dataType()) { case TpInt: put (rownr, &value); return; case TpInt64: Int64 vali64; vali64 = value; put (rownr, &vali64); return; case TpFloat: float valf; valf = value; put (rownr, &valf); return; case TpDouble: double vald; vald = value; put (rownr, &vald); return; case TpComplex: { Complex valc(value); put (rownr, &valc); } return; case TpDComplex: { DComplex valdc(value); put (rownr, &valdc); } return; default: throwPutType("Int"); } } void BaseColumn::putScalar (rownr_t rownr, const uInt& value) { if (!colDescPtr_p->isScalar()) { throwPutScalar(); } switch (colDescPtr_p->dataType()) { case TpUInt: put (rownr, &value); return; case TpInt64: Int64 vali64; vali64 = value; put (rownr, &vali64); return; case TpFloat: float valf; valf = value; put (rownr, &valf); return; case TpDouble: double vald; vald = value; put (rownr, &vald); return; case TpComplex: { Complex valc(value); put (rownr, &valc); } return; case TpDComplex: { DComplex valdc(value); put (rownr, &valdc); } return; default: throwPutType("uInt"); } } void BaseColumn::putScalar (rownr_t rownr, const Int64& value) { if (!colDescPtr_p->isScalar()) { throwPutScalar(); } switch (colDescPtr_p->dataType()) { case TpInt64: put (rownr, &value); return; case TpFloat: float valf; valf = value; put (rownr, &valf); return; case TpDouble: double vald; vald = value; put (rownr, &vald); return; case TpComplex: { Complex valc(value); put (rownr, &valc); } return; case TpDComplex: { DComplex valdc(value); put (rownr, &valdc); } return; default: throwPutType("Int64"); } } void BaseColumn::putScalar (rownr_t rownr, const float& value) { if (!colDescPtr_p->isScalar()) { throwPutScalar(); } switch (colDescPtr_p->dataType()) { case TpFloat: put (rownr, &value); return; case TpDouble: double vald; vald = value; put (rownr, &vald); return; case TpComplex: { Complex valc(value); put (rownr, &valc); } return; case TpDComplex: { DComplex valdc(value); put (rownr, &valdc); } return; default: throwPutType("float"); } } void BaseColumn::putScalar (rownr_t rownr, const double& value) { if (!colDescPtr_p->isScalar()) { throwPutScalar(); } switch (colDescPtr_p->dataType()) { case TpFloat: float valf; valf = value; put (rownr, &valf); return; case TpDouble: put (rownr, &value); return; case TpComplex: { Complex valc(value); put (rownr, &valc); } return; case TpDComplex: { DComplex valdc(value); put (rownr, &valdc); } return; default: throwPutType("double"); } } void BaseColumn::putScalar (rownr_t rownr, const Complex& value) { if (!colDescPtr_p->isScalar()) { throwPutScalar(); } switch (colDescPtr_p->dataType()) { case TpComplex: put (rownr, &value); return; case TpDComplex: { DComplex valdc(value.real(), value.imag()); put (rownr, &valdc); } return; default: throwPutType("Complex"); } } void BaseColumn::putScalar (rownr_t rownr, const DComplex& value) { if (!colDescPtr_p->isScalar()) { throwPutScalar(); } switch (colDescPtr_p->dataType()) { case TpComplex: { Complex valc (value.real(), value.imag()); put (rownr, &valc); } return; case TpDComplex: put (rownr, &value); return; default: throwPutType("DComplex"); } } void BaseColumn::putScalar (rownr_t rownr, const String& value) { if (!colDescPtr_p->isScalar()) { throwPutScalar(); } switch (colDescPtr_p->dataType()) { case TpString: put (rownr, &value); return; default: throwPutType("String"); } } void BaseColumn::putScalar (rownr_t rownr, const TableRecord& value) { if (!colDescPtr_p->isScalar()) { throwPutScalar(); } switch (colDescPtr_p->dataType()) { case TpRecord: put (rownr, &value); return; default: throwPutType("TableRecord"); } } void BaseColumn::throwGetScalar() const { throw (TableInvOper ("invalid getScalar() for column " + colDescPtr_p->name() + "; only possible for a scalar")); } void BaseColumn::throwPutScalar() const { throw (TableInvOper ("invalid putScalar() for column " + colDescPtr_p->name() + "; only possible for a scalar")); } void BaseColumn::throwGetType (const String& type) const { throw (TableInvDT ("invalid type promotion in getScalar(" + type + ") for column " + colDescPtr_p->name() + " with type " + ValType::getTypeStr(colDescPtr_p->dataType()))); } void BaseColumn::throwPutType (const String& type) const { throw (TableInvDT ("invalid type promotion in putScalar(" + type + ") for column " + colDescPtr_p->name() + " with type " + ValType::getTypeStr(colDescPtr_p->dataType()))); } const ColumnDesc& BaseColumn::columnDesc() const { colDesc_p = ColumnDesc(const_cast(colDescPtr_p)); return colDesc_p; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/BaseColumn.h000066400000000000000000000313071476623553700202150ustar00rootroot00000000000000//# BaseColumn.h: Abstract base class for a table column //# Copyright (C) 1994,1995,1996,1997,1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_BASECOLUMN_H #define TABLES_BASECOLUMN_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class ArrayBase; class BaseColumnDesc; class ColumnCache; class TableRecord; class RefRows; class IPosition; class Slicer; class Sort; // // Abstract base class for a table column // // // // // //# Classes you should understand before using this one. //
      • ColumnDesc //
      • Table // // // This is the (abstract) base class to access a column in a table. // // // This class is the base class for the derived column classes. // It is a private class in the sense that the user cannot get // access to it. All user access to a column is done via the // classes TableColumn, ScalarColumn and ArrayColumn. They call // the corresponding functions in this class and its derived classes. // // // This class serves a the base for the more specialized column classes // like FilledScalarColumn and RefColumn. It defines many virtual // functions, which are implemented in the derived classes. // Some of these functions are purely virtual, some have a default // implementation throwing an "invalid operation" exception. In that // way those latter functions only have to be implemented in the // classes which handle those cases. // The class RefColumn is in fact implemented in terms of // this class. Almost every function in RefColumn calls the corresponding // function in BaseColumn with the correct row number. // // // //# A List of bugs, limitations, extensions or planned refinements. // class BaseColumn { public: // Construct it using the given column description. BaseColumn (const BaseColumnDesc*); virtual ~BaseColumn(); // Test if the column is writable. virtual Bool isWritable() const = 0; // Test if the column is stored (otherwise it is virtual). virtual Bool isStored() const = 0; // Get access to the column keyword set. // virtual TableRecord& rwKeywordSet() = 0; virtual TableRecord& keywordSet() = 0; // // Get const access to the column description. const ColumnDesc& columnDesc() const; // Get nr of rows in the column. virtual rownr_t nrow() const = 0; // Test if the given cell contains a defined value. virtual Bool isDefined (rownr_t rownr) const = 0; // Set the shape of the array in the given row. virtual void setShape (rownr_t rownr, const IPosition& shape); // Set the shape and tile shape of the array in the given row. virtual void setShape (rownr_t rownr, const IPosition& shape, const IPosition& tileShape); // Get the global #dimensions of an array (ie. for all rows). virtual uInt ndimColumn() const; // Get the global shape of an array (ie. for all rows). virtual IPosition shapeColumn() const; // Get the #dimensions of an array in a particular cell. virtual uInt ndim (rownr_t rownr) const; // Get the shape of an array in a particular cell. virtual IPosition shape (rownr_t rownr) const; // Get the tile shape of an array in a particular cell. virtual IPosition tileShape (rownr_t rownr) const; // Ask the data manager if the shape of an existing array can be changed. // Default is no. virtual Bool canChangeShape() const; // Initialize the rows from startRow till endRow (inclusive) // with the default value defined in the column description. virtual void initialize (rownr_t startRownr, rownr_t endRownr) = 0; // Get a scalar value from a particular cell. virtual void get (rownr_t rownr, void* dataPtr) const; // Get an array from a particular cell. virtual void getArray (rownr_t rownr, ArrayBase& dataPtr) const; // Get a slice of an N-dimensional array in a particular cell. virtual void getSlice (rownr_t rownr, const Slicer&, ArrayBase& dataPtr) const; // Get the vector of all scalar values in a column. virtual void getScalarColumn (ArrayBase& dataPtr) const; // Get the array of all array values in a column. // If the column contains n-dim arrays, the resulting array is (n+1)-dim. // The arrays in the column have to have the same shape in all cells. virtual void getArrayColumn (ArrayBase& dataPtr) const; // Get subsections from all arrays in the column. // If the column contains n-dim arrays, the resulting array is (n+1)-dim. // The arrays in the column have to have the same shape in all cells. virtual void getColumnSlice (const Slicer&, ArrayBase& dataPtr) const; // Get the vector of some scalar values in a column. virtual void getScalarColumnCells (const RefRows& rownrs, ArrayBase& dataPtr) const; // Get the array of some array values in a column. // If the column contains n-dim arrays, the resulting array is (n+1)-dim. // The arrays in the column have to have the same shape in all cells. virtual void getArrayColumnCells (const RefRows& rownrs, ArrayBase& dataPtr) const; // Get subsections from some arrays in the column. // If the column contains n-dim arrays, the resulting array is (n+1)-dim. // The arrays in the column have to have the same shape in all cells. virtual void getColumnSliceCells (const RefRows& rownrs, const Slicer&, ArrayBase& dataPtr) const; // Put the scalar value in a particular cell. virtual void put (rownr_t rownr, const void* dataPtr); // Put the array value in a particular cell. virtual void putArray (rownr_t rownr, const ArrayBase& dataPtr); // Put a slice of an N-dimensional array in a particular cell. virtual void putSlice (rownr_t rownr, const Slicer&, const ArrayBase& dataPtr); // Put the vector of all scalar values in the column. virtual void putScalarColumn (const ArrayBase& dataPtr); // Put the array of all array values in the column. // If the column contains n-dim arrays, the source array is (n+1)-dim. // The arrays in the column have to have the same shape in all cells. virtual void putArrayColumn (const ArrayBase& dataPtr); // Put into subsections of all table arrays in the column. // If the column contains n-dim arrays, the source array is (n+1)-dim. // The arrays in the column have to have the same shape in all cells. virtual void putColumnSlice (const Slicer&, const ArrayBase& dataPtr); // Get the vector of some scalar values in a column. virtual void putScalarColumnCells (const RefRows& rownrs, const ArrayBase& dataPtr); // Get the array of some array values in a column. // If the column contains n-dim arrays, the resulting array is (n+1)-dim. // The arrays in the column have to have the same shape in all cells. virtual void putArrayColumnCells (const RefRows& rownrs, const ArrayBase& dataPtr); // Put subsections of some arrays in the column. // If the column contains n-dim arrays, the source array is (n+1)-dim. // The arrays in the column have to have the same shape in all cells. virtual void putColumnSliceCells (const RefRows& rownrs, const Slicer&, const ArrayBase& dataPtr); // Get the value from the row and convert it to the required type. // This can only be used for scalar columns with a standard data type. // Note that an unsigned integer cannot be converted to a signed integer // with the same length. So only Int64 can handle all integer values. // void getScalar (rownr_t rownr, Bool& value) const; void getScalar (rownr_t rownr, uChar& value) const; void getScalar (rownr_t rownr, Short& value) const; void getScalar (rownr_t rownr, uShort& value) const; void getScalar (rownr_t rownr, Int& value) const; void getScalar (rownr_t rownr, uInt& value) const; void getScalar (rownr_t rownr, Int64& value) const; void getScalar (rownr_t rownr, float& value) const; void getScalar (rownr_t rownr, double& value) const; void getScalar (rownr_t rownr, Complex& value) const; void getScalar (rownr_t rownr, DComplex& value) const; void getScalar (rownr_t rownr, String& value) const; void getScalar (rownr_t rownr, TableRecord& value) const; // // Get a scalar for the other data types. // The given data type id must match the data type id of this column. void getScalar (rownr_t rownr, void* value, const String& dataTypeId) const; // Put the value into the row and convert it from the given type. // This can only be used for scalar columns with a standard data type. // void putScalar (rownr_t rownr, const Bool& value); void putScalar (rownr_t rownr, const uChar& value); void putScalar (rownr_t rownr, const Short& value); void putScalar (rownr_t rownr, const uShort& value); void putScalar (rownr_t rownr, const Int& value); void putScalar (rownr_t rownr, const uInt& value); void putScalar (rownr_t rownr, const Int64& value); void putScalar (rownr_t rownr, const float& value); void putScalar (rownr_t rownr, const double& value); void putScalar (rownr_t rownr, const Complex& value); void putScalar (rownr_t rownr, const DComplex& value); void putScalar (rownr_t rownr, const String& value); void putScalar (rownr_t rownr, const Char* value) { putScalar (rownr, String(value)); } void putScalar (rownr_t rownr, const TableRecord& value); // // Get a pointer to the underlying column cache. virtual ColumnCache& columnCache() = 0; // Set the maximum cache size (in bytes) to be used by a storage manager. virtual void setMaximumCacheSize (uInt nbytes) = 0; // Add this column and its data to the Sort object. // It may allocate some storage on the heap, which will be saved // in the argument dataSave. // The function freeSortKey must be called to free this storage. // virtual void makeSortKey (Sort&, std::shared_ptr& cmpObj, Int order, std::shared_ptr& dataSave); // Do it only for the given row numbers. virtual void makeRefSortKey (Sort&, std::shared_ptr& cmpObj, Int order, const Vector& rownrs, std::shared_ptr& dataSave); // // Allocate value buffers for the table iterator. // Also get a comparison object if undefined. // The function freeIterBuf must be called to free the buffers. virtual void allocIterBuf (void*& lastVal, void*& curVal, std::shared_ptr& cmpObj); // Free the value buffers allocated by allocIterBuf. virtual void freeIterBuf (void*& lastVal, void*& curVal); protected: // Throw exceptions for invalid scalar get or put. // void throwGetScalar() const; void throwPutScalar() const; void throwGetType (const String& type) const; void throwPutType (const String& type) const; // //# Data members const BaseColumnDesc* colDescPtr_p; private: //# This ColumnDesc object is created to be able to return //# a const ColumnDesc& by function columnDesc(). mutable ColumnDesc colDesc_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/BaseTabIter.cc000066400000000000000000000236021476623553700204470ustar00rootroot00000000000000//# BaseTabIter.cc: Base class for table iterator //# Copyright (C) 1994,1995,1996,1997 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // BaseTableIterator is the base class for the table iterators. // It is a letter class of the envelope TableIterator. // // BaseTableIterator iterates by sorting the table in the required // order and then creating a RefTable for each step containing the row // numbers of the rows for that iteration step. BaseTableIterator::BaseTableIterator (const std::shared_ptr& btp, const Block& keys, const Block>& cmp, const Block& order, int option, bool cacheIterationBoundaries) : lastRow_p (0), nrkeys_p (keys.nelements()), keyChangeAtLastNext_p(""), colPtr_p (keys.nelements()), cmpObj_p (cmp), lastVal_p (keys.nelements()), curVal_p (keys.nelements()), sortIterBoundaries_p (nullptr), sortIterKeyIdxChange_p (nullptr), aRefTable_p(nullptr) { // If needed sort the table in order of the iteration keys. // The passed in compare functions are for the iteration. if (option == TableIterator::NoSort) { sortTab_p = btp; }else{ Sort::Option sortopt = Sort::QuickSort; if (option == TableIterator::HeapSort) { sortopt = Sort::HeapSort; } else if (option == TableIterator::ParSort) { sortopt = Sort::ParSort; } else if (option == TableIterator::InsSort) { sortopt = Sort::InsSort; } Block ord(nrkeys_p, Sort::Ascending); for (uInt i=0; i>(); sortIterKeyIdxChange_p = std::make_shared>(); } sortTab_p = btp->sort (keys, cmpObj_p, ord, sortopt, sortIterBoundaries_p, sortIterKeyIdxChange_p); } // Get the pointers to the BaseColumn object. // Get a buffer to hold the current and last value per column. for (uInt i=0; igetColumn (keys[i]); colPtr_p[i]->allocIterBuf (lastVal_p[i], curVal_p[i], cmpObj_p[i]); } if (cacheIterationBoundaries) { sortIterBoundariesIt_p = sortIterBoundaries_p->begin(); sortIterKeyIdxChangeIt_p = sortIterKeyIdxChange_p->begin(); aBaseTable_p = sortTab_p->makeRefTable (False, 0); aRefTable_p = dynamic_cast(aBaseTable_p.get()); DebugAssert (aRefTable_p, AipsError); } } BaseTableIterator* BaseTableIterator::clone() const { BaseTableIterator* newbti = new BaseTableIterator (*this); return newbti; } BaseTableIterator::BaseTableIterator (const BaseTableIterator& that) : lastRow_p (0), nrkeys_p (that.nrkeys_p), colPtr_p (that.colPtr_p), cmpObj_p (that.cmpObj_p), lastVal_p (that.nrkeys_p), curVal_p (that.nrkeys_p), sortIterBoundaries_p (that.sortIterBoundaries_p), sortIterKeyIdxChange_p (that.sortIterKeyIdxChange_p), aRefTable_p(nullptr) { // Get the pointers to the BaseColumn object. // Get a buffer to hold the current and last value per column. for (uInt i=0; iallocIterBuf (lastVal_p[i], curVal_p[i], cmpObj_p[i]); } sortTab_p = that.sortTab_p; if (sortIterBoundaries_p) { sortIterBoundariesIt_p = sortIterBoundaries_p->begin(); } if (sortIterKeyIdxChange_p) { sortIterKeyIdxChangeIt_p = sortIterKeyIdxChange_p->begin(); } if (sortIterBoundaries_p && sortIterKeyIdxChange_p) { aBaseTable_p = sortTab_p->makeRefTable (False, 0); aRefTable_p = dynamic_cast(aBaseTable_p.get()); DebugAssert (aRefTable_p, AipsError); } } BaseTableIterator::~BaseTableIterator() { // Delete the value buffers. for (uInt i=0; ifreeIterBuf (lastVal_p[i], curVal_p[i]); } } void BaseTableIterator::reset() { lastRow_p = 0; if (sortIterBoundaries_p) { sortIterBoundariesIt_p = sortIterBoundaries_p->begin(); } if (sortIterKeyIdxChange_p) { sortIterKeyIdxChangeIt_p = sortIterKeyIdxChange_p->begin(); } } std::shared_ptr BaseTableIterator::next() { // If there are no group boundaries precomputed do an expensive // walk to check where a new boundary happens by calling the comparison // functions if (!sortIterBoundaries_p || !sortIterKeyIdxChange_p) { return noCachedIterBoundariesNext(); } // Allocate a RefTable to represent the rows in the iteration group. aRefTable_p->removeAllRow(); if (lastRow_p >= sortTab_p->nrow()) { return aBaseTable_p; // the end of the table } // Go to the next group boundary (the one after this), which will be // one past the end of the current group. ++sortIterBoundariesIt_p; rownr_t startNextGroup; if (sortIterBoundariesIt_p == sortIterBoundaries_p->end()) { startNextGroup = sortTab_p->nrow(); } else { startNextGroup = *sortIterBoundariesIt_p; } // lastRow_p contains the starting point for this group rownr_t startThisGroup = lastRow_p; aRefTable_p->addRownrRange (startThisGroup, startNextGroup - 1); // Set lastRow_p to the starting point of next group lastRow_p = startNextGroup; // If we've reached the end of the table, clear the keyCh_p if (lastRow_p==sortTab_p->nrow()) { keyChangeAtLastNext_p=String(); } else { // If not, get the name of the column from the sorting column ID keyChangeAtLastNext_p=colPtr_p[*sortIterKeyIdxChangeIt_p]->columnDesc().name(); } ++sortIterKeyIdxChangeIt_p; //# Adjust rownrs in case source table is already a RefTable. Vector& rownrs = aRefTable_p->rowStorage(); sortTab_p->adjustRownrs (aRefTable_p->nrow(), rownrs, False); return aBaseTable_p; } std::shared_ptr BaseTableIterator::noCachedIterBoundariesNext() { // This is an expensive way to find the next group boundary by calling // the sorting function for each individual row. // Allocate a RefTable to represent the rows in the iteration group. std::shared_ptr baseTabPtr = sortTab_p->makeRefTable (False, 0); RefTable* itp = dynamic_cast(baseTabPtr.get()); DebugAssert (itp, AipsError); if (lastRow_p >= sortTab_p->nrow()) { return baseTabPtr; // the end of the table } // Add the last found rownr to this iteration group. itp->addRownr (lastRow_p); for (uInt i=0; iget (lastRow_p, lastVal_p[i]); } Bool match; rownr_t nr = sortTab_p->nrow(); while (++lastRow_p < nr) { match = True; for (uInt i=0; iget (lastRow_p, curVal_p[i]); if (cmpObj_p[i]->comp (curVal_p[i], lastVal_p[i]) != 0) { match = False; // update so users can see which key changed keyChangeAtLastNext_p=colPtr_p[i]->columnDesc().name(); break; } } if (!match) { break; } itp->addRownr (lastRow_p); } // If we've reached the end of the table, clear the keyCh_p if (lastRow_p == nr) { keyChangeAtLastNext_p=String(); } //# Adjust rownrs in case source table is already a RefTable. Vector& rownrs = itp->rowStorage(); sortTab_p->adjustRownrs (itp->nrow(), rownrs, False); return baseTabPtr; } void BaseTableIterator::copyState(const BaseTableIterator &other) { lastRow_p = other.lastRow_p; keyChangeAtLastNext_p = other.keyChangeAtLastNext_p; if (sortIterBoundaries_p) { sortIterBoundariesIt_p = sortIterBoundaries_p->begin(); std::advance(sortIterBoundariesIt_p, std::distance(other.sortIterBoundaries_p->begin(), other.sortIterBoundariesIt_p)); } if (sortIterKeyIdxChange_p) { sortIterKeyIdxChangeIt_p = sortIterKeyIdxChange_p->begin(); std::advance(sortIterKeyIdxChangeIt_p, std::distance(other.sortIterKeyIdxChange_p->begin(), other.sortIterKeyIdxChangeIt_p)); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/BaseTabIter.h000066400000000000000000000134421476623553700203120ustar00rootroot00000000000000//# BaseTabIter.h: Base class for table iterator //# Copyright (C) 1994,1995,1996,1997,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_BASETABITER_H #define TABLES_BASETABITER_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableColumn; class RefTable; class String; // // Base class for table iterator // // // // // //# Classes you should understand before using this one. //
      • BaseTable //
      • TableIterator // // // BaseTableIterator is the base class for the classes doing // the actual iterating through a table. // // // BaseTableIterator is the base class for the table iterators. // It is a letter class of the envelope TableIterator. // Currently there are no classes derived from BaseTableIterator, // since it can do all the work itself. However, in the future // this need not to be true anymore. // // BaseTableIterator iterates by sorting the table in the required // order and then creating a RefTable for each step containing the // rows for that iteration step. Each iteration step assembles the // rows with equal key values. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class BaseTableIterator { public: // Create the table iterator to iterate through the given // columns in the given order. The given compare objects // will be used for the sort and to compare if values are equal. // If a compare object in cmpObjs is null, the default ObjCompare // will be used. // If cacheIterationBoundaries is set to true then the iteration // boundaries computed at construction time while sorting the table // are used when advancing with next(). Otherwise, for each next() // call the comparison functions are reevaluated again to get the // iteration boundary. This improves performance in general but will // break existing applications that change the comparison objects // (cmpObjs) between iterations. BaseTableIterator (const std::shared_ptr&, const Block& columnNames, const Block>& cmpObjs, const Block& orders, int option, bool cacheIterationBoundaries = false); // Clone this iterator. BaseTableIterator* clone() const; virtual ~BaseTableIterator(); // Assignment is not needed, because the assignment operator in // the envelope class TableIterator has reference semantics. BaseTableIterator& operator= (const BaseTableIterator&) = delete; // Reset the iterator (i.e. restart iteration). virtual void reset(); // Return the next group. virtual std::shared_ptr next(); virtual void copyState(const BaseTableIterator &); // Report Name of slowest sort column that changed (according to the // comparison function) to terminate the most recent call to next() // Enables clients to sense iteration boundary properties // and organize associated iterations inline const String& keyChangeAtLastNext() const { return keyChangeAtLastNext_p; } protected: std::shared_ptr sortTab_p; //# Table sorted in iteration order rownr_t lastRow_p; //# last row used from reftab uInt nrkeys_p; //# nr of columns in group String keyChangeAtLastNext_p; //# name of column that terminated most recent next() PtrBlock colPtr_p; //# pointer to column objects Block> cmpObj_p; //# comparison object per column // Copy constructor (to be used by clone) BaseTableIterator (const BaseTableIterator&); std::shared_ptr noCachedIterBoundariesNext(); private: Block lastVal_p; //# last value per column Block curVal_p; //# current value per column std::shared_ptr> sortIterBoundaries_p; std::shared_ptr> sortIterKeyIdxChange_p; Vector::iterator sortIterBoundariesIt_p; Vector::iterator sortIterKeyIdxChangeIt_p; RefTable* aRefTable_p; //# RefTable returned in each iteration std::shared_ptr aBaseTable_p; //# Same as above for automatic deletion }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/BaseTable.cc000066400000000000000000001225101476623553700201420ustar00rootroot00000000000000//# BaseTable.cc: Abstract base class for tables //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // The constructor of the derived class should call unmarkForDelete // when the construction ended succesfully. BaseTable::BaseTable (const String& name, int option, rownr_t nrrow) { BaseTableCommon(name, option, nrrow); } #ifdef HAVE_MPI BaseTable::BaseTable (MPI_Comm mpiComm, const String& name, int option, rownr_t nrrow) :itsMpiComm (mpiComm) { BaseTableCommon(name, option, nrrow); } #endif void BaseTable::BaseTableCommon (const String& name, int option, rownr_t nrrow) { nrrow_p = nrrow; nrrowToAdd_p = 0; name_p = name; option_p = option; noWrite_p = False; delete_p = False; madeDir_p = True; itsTraceId = -1; if (name_p.empty()) { name_p = File::newUniqueName ("", "tab").originalName(); } // Make name absolute in case a chdir is done in e.g. Python. name_p = makeAbsoluteName (name_p); if (option_p == Table::Scratch) { option_p = Table::New; } // Mark initially a new table for delete. // When the construction ends successfully, it can be unmarked. if (option_p == Table::New || option_p == Table::NewNoReplace) { markForDelete (False, ""); madeDir_p = False; } } BaseTable::~BaseTable() { //# Delete the table files (if there) if marked for delete. if (isMarkedForDelete()) { if (madeDir_p) { // The table may be a subtable already deleted when // the parent was deleted. So test if it still exists. File file(name_p); if (file.exists()) { Directory directory(file); directory.removeRecursive(); } //# Do callback indicating that table has been deleted. scratchCallback (False, name_p); } } } Bool BaseTable::isNull() const { return False; } void BaseTable::scratchCallback (Bool isScratch, const String& oldName) const { if (Table::scratchCallback_p != 0) { if (isScratch) { if (oldName == name_p) { Table::scratchCallback_p (name_p, isScratch, ""); }else{ Table::scratchCallback_p (name_p, isScratch, oldName); } }else{ if (oldName.empty()) { Table::scratchCallback_p (name_p, isScratch, ""); }else{ Table::scratchCallback_p (oldName, isScratch, ""); } } } } #ifdef HAVE_MPI template void safe_mpi_invoke(Func &&mpi_func, const char *errmsg, Args && ... args) { if (mpi_func(std::forward(args)...)) { throw std::runtime_error(errmsg); } } static bool is_mpi_initialized() { int mpi_initialized; safe_mpi_invoke(MPI_Initialized, "Error when checking for initialized MPI", &mpi_initialized); return mpi_initialized != 0; } static bool is_mpi_finalized() { int mpi_finalized; safe_mpi_invoke(MPI_Finalized, "Error when checking for finalized MPI", &mpi_finalized); return mpi_finalized != 0; } static std::once_flag ensure_mpi_flag; static void ensure_mpi() { std::call_once(ensure_mpi_flag, [&]{ if (is_mpi_initialized() || is_mpi_finalized()) { return; } #ifdef USE_THREADS int provided; safe_mpi_invoke(MPI_Init_thread, "Error when initializing MPI", nullptr, nullptr, MPI_THREAD_MULTIPLE, &provided); // Checking if provided == MPI_THREAD_MULTIPLE is not necessarily // the best option. Down here we have no idea how users are intending // to use these objects, and therefore throwing an exception is not // correct. For instance, with OpenMPI @ Ubuntu 16.04.05 lots // of unit tests fail. #else safe_mpi_invoke(MPI_Init, "Error when initializing MPI", nullptr, nullptr); #endif // USE_THREADS }); } static bool is_rank_0(MPI_Comm comm) { ensure_mpi(); if (is_mpi_finalized()) { return false; } int rank; safe_mpi_invoke(MPI_Comm_rank, "Error when getting MPI rank", comm, &rank); return rank == 0; } #endif // HAVE_MPI Bool BaseTable::makeTableDir() { #ifdef HAVE_MPI if (!is_rank_0(itsMpiComm)) { return false; } #endif //# Exit if the table directory has already been created. if (madeDir_p) { return False; } //# Check option. if (!openedForWrite()) { #ifndef HAVE_MPI throw (TableInvOpt ("BaseTable::makeTableDir", "must be Table::New, NewNoReplace or Update")); #endif } //# Check if the table directory name already exists //# and is a directory indeed. File file(name_p); if (file.exists()) { if (!file.isDirectory()) { throw (TableDuplFile(name_p, " (and is not a true table directory)")); } Directory tdir(file); if (! tdir.isEmpty()) { //# Check if file table.dat exist in it. File mfile (Table::fileName(name_p)); if (! mfile.exists()) { throw (TableDuplFile(name_p, " (and is not a true table directory)")); } if (option_p == Table::NewNoReplace) { throw (TableDuplFile(name_p)); // table file already exists } //# Remove the directory and possible files in it. //# In this way overwriting an existing table does not leave //# old files. //# Keep the directory, so existing properties (like placement on //# Lustre file system is kept. Directory dir(name_p); dir.removeRecursive (True); } } else { //# Create the table directory. Directory dir(name_p); dir.create(); } //# Create table.dat. //# First do a scratch callback that a table is getting created. //# If the file creation fails, the user sees it as a scratch //# table, so it can be deleted. scratchCallback (True, ""); RegularFile dfile (Table::fileName(name_p)); dfile.create(); madeDir_p = True; return True; } Bool BaseTable::openedForWrite() const { #ifdef HAVE_MPI if (!is_rank_0(itsMpiComm)) { return false; } #endif AlwaysAssert (!isNull(), AipsError); return (option_p==Table::Old || option_p==Table::Delete ? False : True); } int BaseTable::tableType() const { return Table::Plain; } void BaseTable::getPartNames (Block& names, Bool) const { uInt inx = names.size(); names.resize (inx + 1); names[inx] = name_p; } TableInfo BaseTable::tableInfo (const String& tableName) { return TableInfo (tableName + "/table.info"); } void BaseTable::getTableInfo() { AlwaysAssert (!isNull(), AipsError); info_p = TableInfo (name_p + "/table.info"); } void BaseTable::flushTableInfo() { AlwaysAssert (!isNull(), AipsError); // Create table directory if needed. Bool made = makeTableDir(); info_p.flush (name_p + "/table.info"); if (made && !isMarkedForDelete()) { scratchCallback (False, name_p); } } void BaseTable::writeStart (AipsIO& ios, Bool bigEndian) { // Check option. if (!openedForWrite()) { throw (TableInvOpt ("BaseTable::writeStart", "must be Table::New, NewNoReplace or Update")); } // Create table directory when needed. Bool made = makeTableDir(); // Create the file. It is a temporary file that will be later renamed // to the final name. This is so because, in case other process // tries to create a Table object with this table, there is a small // window in which the table.dat is truncated to lenght 0 (ByteIO::New) by // this process while the constructor of Table in the other process // will fail because table.dat is empty. Note that the constructor reads // reads table.dat before doing any locking ios.open (Table::fileName(name_p)+"_tmp", ByteIO::New); // Start the object as Table, so class Table can read it back. // Version 2 (of PlainTable) does not have its own TableRecord anymore. // Use old version if nr of rows fit in an Int, otherwise use new version. if (nrrow_p > rownr_t(std::numeric_limits::max())) { ios.putstart ("Table", 3); ios << nrrow_p; } else { ios.putstart ("Table", 2); ios << uInt(nrrow_p); } // Write endianity as a uInt, because older tables contain a uInt 0 here. uInt endian = 0; if (!bigEndian) { endian = 1; } ios << endian; // 0=bigendian; 1=littleendian if (made && !isMarkedForDelete()) { scratchCallback (False, name_p); } } //# End writing a table file. void BaseTable::writeEnd (AipsIO& ios) { ios.putend (); // Ensure that the buffers have been written before renaming ios.close(); // Now rename the file to the final name (table.dat). Not that the // rename is function (used by RegularFile::move) is atomic // (https://pubs.opengroup.org/onlinepubs/9699919799/functions/rename.html) RegularFile(Table::fileName(name_p)+"_tmp").move (Table::fileName(name_p), true); } void BaseTable::setTableChanged() {} void BaseTable::markForDelete (Bool callback, const String& oldName) { //# Do not use virtual isNull as it can be called from the constructor. AlwaysAssert (!BaseTable::isNull(), AipsError); Bool prev = delete_p; delete_p = True; //# Do callback if changed from non-scratch to scratch or if name changed. if (callback) { if (!prev) { scratchCallback (True, ""); } else if (!oldName.empty() && oldName != name_p) { scratchCallback (True, oldName); } } } void BaseTable::unmarkForDelete (Bool callback, const String& oldName) { AlwaysAssert (!BaseTable::isNull(), AipsError); Bool prev = delete_p; delete_p = False; //# Do callback if changed from scratch to non-scratch. if (callback && prev) { scratchCallback (False, oldName); } } //# Prepare for copying or renaming a table. void BaseTable::prepareCopyRename (const String& newName, int tableOption) const { // Options Delete and Old are wrong. if (tableOption == Table::Old || tableOption == Table::Delete) { throw (TableInvOpt ("BaseTable::rename", "must be Table::New, NewNoReplace, Scratch or Update")); } // Do not do anything if the new name is the same as the old name. if (newName == name_p) { return; } // Copy and rename is not allowed if the target table is open. Path path(newName); PlainTable* ptr = PlainTable::tableCache()(path.absoluteName()); if (ptr) { throw (TableInvOper ("Cannot copy/rename; target table " + newName + " is still open (is in the table cache)")); } // Test if the table already exists. // Throw an exception if a file (but not a table) exists. File fileNew(newName); if (fileNew.exists()) { if (!fileNew.isDirectory()) { throw (TableDuplFile(newName, " (and is not a true table directory)")); } // The table should not exist for NewNoReplace. if (tableOption == Table::NewNoReplace) { throw (TableDuplFile(newName)); } Directory directory(fileNew); directory.removeRecursive(); }else{ // The table must exist for Update. if (tableOption == Table::Update) { throw (TableNoFile(newName)); } } } //# Rename a table. void BaseTable::rename (const String& newName, int tableOption) { AlwaysAssert (!isNull(), AipsError); // Make the name absolute. String absNewName = makeAbsoluteName (newName); // The table can be renamed if: // - it is not created yet // - it exists and its file is writable if (madeDir_p) { File file(name_p); if (!file.isWritable()) { throw (TableInvOper ("Table file " + name_p + " is readonly and cannot be renamed")); } } String oldName = name_p; // Do not rename physically when the new name is the same as the old name. if (absNewName != oldName) { prepareCopyRename (absNewName, tableOption); //# Do the actual renaming when the table exists. //# It is possible that the files do not exist yet if rename //# is used very early. if (madeDir_p) { Directory fileOld(oldName); fileOld.move (absNewName); } //# Rename the names of the subtables in the keywords. renameSubTables (absNewName, oldName); //# Okay, the table file has been renamed. //# Now rename in the cache (if there) and internally. PlainTable::tableCache().rename (absNewName, oldName); name_p = absNewName; } //# (Un)mark for delete when necessary. if (tableOption == Table::Scratch) { markForDelete (True, oldName); }else{ unmarkForDelete (True, oldName); } } void BaseTable::renameSubTables (const String&, const String&) {} void BaseTable::deepCopy (const String& newName, const Record& dataManagerInfo, const StorageOption& stopt, int tableOption, Bool valueCopy, int endianFormat, Bool noRows) const { if (valueCopy || dataManagerInfo.nfields() > 0 || noRows) { trueDeepCopy (newName, dataManagerInfo, stopt, tableOption, endianFormat, noRows); } else { copy (newName, tableOption); } } void BaseTable::trueDeepCopy (const String& newName, const Record& dataManagerInfo, const StorageOption& stopt, int tableOption, int endianFormat, Bool noRows) const { AlwaysAssert (!isNull(), AipsError); // Make the name absolute. String absNewName = makeAbsoluteName (newName); // Throw exception if new name is same as old one. if (absNewName == name_p) { throw TableError ("Table::deepCopy: new name equal to old name " + name_p); } //# Flush the data and subtables. //# The cast is necessary to bypass the constness of trueDeepCopy //# while flush is const. Changing this requires changing of functions //# copy and deepCopy as well, which could be done in the future. BaseTable* ncThis = const_cast(this); ncThis->flush (True, True); //# Prepare the copy (do some extra checks). prepareCopyRename (absNewName, tableOption); // Create the new table and copy everything. Table oldtab(ncThis); Table newtab = TableCopy::makeEmptyTable (absNewName, dataManagerInfo, oldtab, Table::New, Table::EndianFormat(endianFormat), True, noRows, stopt); if (!noRows) { TableCopy::copyRows (newtab, oldtab); } TableCopy::copyInfo (newtab, oldtab); TableCopy::copySubTables (newtab, oldtab, noRows); } void BaseTable::copy (const String& newName, int tableOption) const { AlwaysAssert (!isNull(), AipsError); // Make the name absolute. String absNewName = makeAbsoluteName (newName); // Do not copy when the new name is the same as the old name. if (absNewName != name_p) { //# Throw an exception when directories do not exist yet. if (!madeDir_p) { throw (TableError ("BaseTable::copy: no input table files exist for " + name_p)); } //# Flush the data and subtables. //# (cast is necessary to bypass non-constness). ((BaseTable*)this)->flush (True, True); //# Copy the files (thus recursively the entire directory). //# Set user write permission after the copy. prepareCopyRename (absNewName, tableOption); Directory fileOld(name_p); fileOld.copy (absNewName); //# Renaming of subtables is not needed, because their names in //# the table directory (the ones copiued) are all relative. } } //# A column is writable if the table and column are writable. Bool BaseTable::isColumnWritable (const String& columnName) const { AlwaysAssert (!isNull(), AipsError); if (! isWritable()) { return False; // table is not writable } return getColumn(columnName)->isWritable(); } Bool BaseTable::isColumnWritable (uInt columnIndex) const { AlwaysAssert (!isNull(), AipsError); if (! isWritable()) { return False; // table is not writable } return getColumn(columnIndex)->isWritable(); } Bool BaseTable::isColumnStored (const String& columnName) const { AlwaysAssert (!isNull(), AipsError); return getColumn(columnName)->isStored(); } Bool BaseTable::isColumnStored (uInt columnIndex) const { AlwaysAssert (!isNull(), AipsError); return getColumn(columnIndex)->isStored(); } //# By default adding, etc. of rows and columns is not possible. Bool BaseTable::canAddRow() const { return False; } Bool BaseTable::canRemoveRow() const { return False; } void BaseTable::addRow (rownr_t, Bool) { throw (TableInvOper ("Table: cannot add a row to table " + name_p)); } void BaseTable::removeRow (rownr_t) { throw (TableInvOper ("Table: cannot remove a row from table " + name_p)); } void BaseTable::removeRow (const Vector& rownrs) { //# Copy the rownrs and sort them. //# Loop through them from end to start. In that way we are sure //# that the deletion of a row does not affect later rows. Vector rownrsCopy; rownrsCopy = rownrs; genSort (rownrsCopy); for (Int64 i=rownrsCopy.nelements()-1; i>=0; i--) { removeRow (rownrsCopy(i)); } } void BaseTable::addColumn (const ColumnDesc&, Bool) { throw (TableInvOper ("Table: cannot add a column to table " + name_p)); } void BaseTable::addColumn (const ColumnDesc&, const String&, Bool, Bool) { throw (TableInvOper ("Table: cannot add a column to table " + name_p)); } void BaseTable::addColumn (const ColumnDesc&, const DataManager&, Bool) { throw (TableInvOper ("Table: cannot add a column to table " + name_p)); } void BaseTable::addColumn (const TableDesc&, const DataManager&, Bool) { throw (TableInvOper ("Table: cannot add a column to table " + name_p)); } void BaseTable::addColumns (const TableDesc& desc, const Record& dmInfo, Bool addToParent) { // Create the correct data manager using the record. // The record can be the dminfo description itself or contain a // single subrecord with the dminfo. Record rec(dmInfo); if (dmInfo.nfields() == 1 && dmInfo.dataType(0) == TpRecord) { rec = dmInfo.subRecord(0); } if (rec.isDefined("TYPE") && rec.isDefined("NAME")) { String dmType = rec.asString ("TYPE"); String dmGroup = rec.asString ("NAME"); Record sp; if (rec.isDefined("SPEC")) { sp = rec.subRecord ("SPEC"); } DataManager* dataMan = DataManager::getCtor(dmType) (dmGroup, sp); addColumn (desc, *dataMan, addToParent); delete dataMan; } else { throw TableError ("Invalid dmInfo record given in Table::addColumn" " for table " + name_p); } } //# Get a vector of row numbers. Vector BaseTable::rowNumbers() const { AlwaysAssert (!isNull(), AipsError); Vector vec(nrow()); indgen (vec, (rownr_t)0); // store 0,1,... in it return vec; } //# By default root table is table itself. BaseTable* BaseTable::root() { return this; } //# By default table is in row order. Bool BaseTable::rowOrder() const { return True; } //# By the default the table cannot return the storage of rownrs. Vector& BaseTable::rowStorage() { throw (TableInvOper ("rowStorage() not possible; table " + name_p + " is no RefTable")); } //# Sort a table. std::shared_ptr BaseTable::sort (const Block& names, const Block>& cmpObj, const Block& order, int option, std::shared_ptr> sortIterBoundaries, std::shared_ptr> sortIterKeyIdxChange) { AlwaysAssert (!isNull(), AipsError); //# Check if the vectors have equal length. uInt nrkey = names.nelements(); if (nrkey != order.nelements()) { throw (TableInvSort ("Length of column sort names and order vectors mismatch" " for table " + name_p)); } //# Get the Column pointers. //# Check if a sort key is indeed a column of scalars. PtrBlock sortCol(nrkey); for (uInt i=0; icolumnDesc().isScalar()) { throw (TableInvSort ("Sort column " + names[i] + " in table " + name_p + " is not a scalar")); } } // Return the result as a table. return doSort (sortCol, cmpObj, order, option, sortIterBoundaries, sortIterKeyIdxChange); } //# Do the actual sort. std::shared_ptr BaseTable::doSort (PtrBlock& sortCol, const Block>& cmpObj, const Block& order, int option, std::shared_ptr> sortIterBoundaries, std::shared_ptr> sortIterKeyIdxChange) { uInt nrkey = sortCol.nelements(); //# Create a sort object. //# Pass all keys (and their data) to it. Sort sortobj; Block> data(nrkey); // to remember data blocks Block> cmp(cmpObj); for (uInt i=0; imakeSortKey (sortobj, cmp[i], order[i], data[i]); } //# Create a reference table. //# This table will NOT be in row order. rownr_t nrrow = nrow(); std::shared_ptr resultTable = makeRefTable (False, nrrow); DebugAssert (static_cast(resultTable), AipsError); //# Now sort the table storing the row-numbers in the RefTable. //# Adjust rownrs in case source table is already a RefTable. //# Then delete possible allocated data blocks. Vector& rows = resultTable->rowStorage(); //# Note that nrrow can change in case Sort::NoDuplicates was given. nrrow = sortobj.sort (rows, nrrow, option); if(sortIterBoundaries && sortIterKeyIdxChange) { sortobj.unique(*sortIterBoundaries, *sortIterKeyIdxChange, rows); } adjustRownrs (nrrow, rows, False); resultTable->setNrrow (nrrow); return resultTable; } std::shared_ptr BaseTable::makeRefTable (Bool rowOrder, rownr_t initialNrrow) { return std::make_shared(this, rowOrder, initialNrrow); } //# No rownrs have to be adjusted and they are by default in ascending order. Bool BaseTable::adjustRownrs (rownr_t, Vector&, Bool) const { return True; } std::shared_ptr BaseTable::select (rownr_t maxRow, rownr_t offset) { if (offset > nrow()) { offset = nrow(); } if (maxRow == 0 || maxRow > nrow() ) { maxRow = nrow() - offset; } if (offset == 0 && maxRow == nrow()) { return shared_from_this(); // pointer to itself } Vector rownrs(maxRow); indgen(rownrs, rownr_t(offset)); return select(rownrs); } // Do the row selection. std::shared_ptr BaseTable::select (const TableExprNode& node, rownr_t maxRow, rownr_t offset) { // Check we don't deal with a null table. AlwaysAssert (!isNull(), AipsError); // If it is a null expression, return maxrows. if (node.isNull()) { return select (maxRow, offset); } //# First check if the node is a Bool. if (node.dataType() != TpBool || !node.isScalar()) { throw (TableInvExpr ("select expression result on table " + name_p + " is not Bool scalar")); } // Accept a const bool expression. if (node.getNodeRep()->isConstant()) { if (node.getBool(0)) { // Select maxRow rows. return select (maxRow, offset); } // Select no rows. return select(Vector()); } // Now check if this table has been used for all columns. // Accept that the expression has no table, which can be the case for // UDFs in derivedmscal (since they have no function arguments). std::vector tables (TableExprNodeUtil::getNodeTables (node.getRep().get(), True)); if (! tables.empty()) { if (TableExprNodeUtil::getCheckNRow(tables) != this->nrow()) { throw (TableInvExpr ("select expression for table " + tables[0].tableName() + " is used on a differently sized table " + name_p)); } } //# Create a reference table, which will be in row order. //# Loop through all rows and add to reference table if true. //# Add the rownr of the root table (one may search a reference table). //# Adjust the row numbers to reflect row numbers in the root table. std::shared_ptr resultTable = makeRefTable (True, 0); DebugAssert (static_cast(resultTable), AipsError); Bool val; rownr_t nrrow = nrow(); TableExprId id; for (rownr_t i=0; iaddRownr (i); // add row // Stop if max #rows reached (note that maxRow==0 means no limit). if (resultTable->nrow() == maxRow) { break; } } else { // Skip first offset matching rows. offset--; } } } adjustRownrs (resultTable->nrow(), resultTable->rowStorage(), False); return resultTable; } std::shared_ptr BaseTable::select (const Vector& rownrs) { AlwaysAssert (!isNull(), AipsError); return std::make_shared(this, rownrs); } std::shared_ptr BaseTable::select (const Block& mask) { AlwaysAssert (!isNull(), AipsError); return std::make_shared(this, Vector(mask.begin(), mask.end())); } std::shared_ptr BaseTable::project (const Block& names) { AlwaysAssert (!isNull(), AipsError); return std::make_shared(this, Vector(names.begin(), names.end())); } //# And (intersect) 2 tables and return a new table. std::shared_ptr BaseTable::tabAnd (BaseTable* that) { AlwaysAssert (!isNull(), AipsError); //# Check if both table have the same root. logicCheck (that); //# Anding a table with the (possibly sorted) root table gives the table. if (this->nrow() == this->root()->nrow()) { return that->shared_from_this(); // this is root } if (that->nrow() == that->root()->nrow()) { return this->shared_from_this(); // that is root } //# There is no root table involved, so we have to deal with RefTables. //# Get both rownr arrays which are sorted if not in row order. Vector r1 = this->logicRows(); Vector r2 = that->logicRows(); // Create RefTable which will be in row order. std::shared_ptr rtp = makeRefTable (True, 0); DebugAssert (static_cast(rtp), AipsError); // Store rownrs in new RefTable. rtp->refAnd (r1.size(), r1.data(), r2.size(), r2.data()); return rtp; } //# Or (union) 2 tables and return a new table. std::shared_ptr BaseTable::tabOr (BaseTable* that) { AlwaysAssert (!isNull(), AipsError); //# Check if both tables have the same root. logicCheck (that); //# Oring a table with the (possibly sorted) root table gives the root. if (this->nrow() == this->root()->nrow() || that->nrow() == that->root()->nrow()) { return root()->shared_from_this(); } //# There is no root table involved, so we have to deal with RefTables. //# Get both rownr arrays which are sorted if not in row order. Vector r1 = this->logicRows(); Vector r2 = that->logicRows(); // Create RefTable which will be in row order. std::shared_ptr rtp = makeRefTable (True, 0); DebugAssert (static_cast(rtp), AipsError); // Store rownrs in new RefTable. rtp->refOr (r1.size(), r1.data(), r2.size(), r2.data()); return rtp; } //# Subtract (difference) 2 tables and return a new table. std::shared_ptr BaseTable::tabSub (BaseTable* that) { AlwaysAssert (!isNull(), AipsError); //# Check if both table have the same root. logicCheck (that); //# Subtracting the root table from a table results in an empty table. if (that->nrow() == that->root()->nrow()) { return makeRefTable (True, 0); } //# Subtracting a table from the root is negating the table. if (this->nrow() == this->root()->nrow()) { return that->tabNot(); } //# There is no root table involved, so we have to deal with RefTables. //# Get both rownr arrays which are sorted if not in row order. Vector r1 = this->logicRows(); Vector r2 = that->logicRows(); // Create RefTable which will be in row order. std::shared_ptr rtp = makeRefTable (True, 0); DebugAssert (static_cast(rtp), AipsError); // Store rownrs in new RefTable. rtp->refSub (r1.size(), r1.data(), r2.size(), r2.data()); return rtp; } //# Xor 2 tables and return a new table. std::shared_ptr BaseTable::tabXor (BaseTable* that) { AlwaysAssert (!isNull(), AipsError); //# Check if both table have the same root. logicCheck (that); //# Xoring a table with the (possibly sorted) root table is negating. if (this->nrow() == this->root()->nrow()) { return that->tabNot(); } if (that->nrow() == that->root()->nrow()) { return tabNot(); } //# There is no root table involved, so we have to deal with RefTables. //# Get both rownr arrays which are sorted if not in row order. Vector r1 = this->logicRows(); Vector r2 = that->logicRows(); // Create RefTable which will be in row order. std::shared_ptr rtp = makeRefTable (True, 0); DebugAssert (static_cast(rtp), AipsError); // Store rownrs in new RefTable. rtp->refXor (r1.size(), r1.data(), r2.size(), r2.data()); return rtp; } //# Negate a table (i.e. take all rows from the root not in table). std::shared_ptr BaseTable::tabNot() { AlwaysAssert (!isNull(), AipsError); //# Negating the (possibly sorted) root results in an empty table, if (nrow() == root()->nrow()) { return makeRefTable (True, 0); } //# There is no root table involved, so we have to deal with RefTables. //# Get rownr array which is sorted if not in row order. Vector r1 = this->logicRows(); // Create RefTable which will be in row order. std::shared_ptr rtp = makeRefTable (True, 0); DebugAssert (static_cast(rtp), AipsError); // Store rownrs in new RefTable. rtp->refNot (r1.size(), r1.data(), root()->nrow()); return rtp; } //# Check if both tables have the same root. void BaseTable::logicCheck (BaseTable* that) { if (root() != that->root()) { throw TableInvLogic(); } } //# Get the rownrs from the reference table. //# Note that rowStorage() throws an exception if it is not a RefTable. //# Sort them if not in row order. Vector BaseTable::logicRows() { AlwaysAssert (!isNull(), AipsError); Vector rows (rowStorage()); if (rowOrder()) { return rows; } //# rows are not in order, so sort them. //# They have to be copied, because the original should not be changed. Vector rowscp (rows.copy()); GenSort::sort (rowscp); return rowscp; } BaseTableIterator* BaseTable::makeIterator (const Block& names, const Block>& cmpObj, const Block& order, int option, bool cacheIterationBoundaries) { AlwaysAssert (!isNull(), AipsError); if (names.nelements() != order.nelements() || names.nelements() != cmpObj.nelements()) { throw (TableInvOper ("TableIterator: Unequal block lengths")); } BaseTableIterator* bti = new BaseTableIterator (shared_from_this(), names, cmpObj, order, option, cacheIterationBoundaries); return bti; } const TableDesc& BaseTable::makeEmptyTableDesc() const { if (!tdescPtr_p) { const_cast(this)->tdescPtr_p = std::make_shared(); } return *tdescPtr_p; } Bool BaseTable::checkRemoveColumn (const Vector& columnNames, Bool throwException) const { for (uInt i=0; iisColumn (columnNames(i))) { if (throwException) { throw TableInvOper ("Table::removeColumn - column " + columnNames(i) + " does not exist" " in table " + name_p); } return False; } // Check if the column is specified only once. for (uInt j=i+1; j maxl) { maxl = tdesc[i].name().size(); } } if (!showDataMans) { if (showColumns) { os << endl; showColumnInfo (os, tdesc, maxl, tdesc.columnNames(), sortColumns, cOrder); } } else { for (uInt i=0; i #include #include #include #include #include #include #include #include #ifdef HAVE_MPI #include #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class RefTable; // class TableDesc; !Forward declaration not recognized SGI compiler class TableLock; class BaseColumn; class ColumnDesc; class TableRecord; class Record; class TableExprNode; class BaseTableIterator; class DataManager; class IPosition; template class Block; template class PtrBlock; class AipsIO; // // Abstract base class for tables // // // // // //# Classes you should understand before using this one. //
      • Table //
      • Sort //
      • TableExprNode // // // BaseTable is the (abstract) base class for different kind of tables. // // // BaseTables defines many virtual functions, which are actually // implemented in the underlying table classes like PlainTable and // RefTable. Other functions like sort and select are implemented // in BaseTable itself. // // The functions in BaseTable and its derived classes can only be // used by the table system classes. All user access is via the // envelope class Table, which references (counted) BaseTable. // // //# A List of bugs, limitations, extensions or planned refinements. //
      • Implement function renameColumn, removeColumn. // class BaseTable: public std::enable_shared_from_this { public: // Initialize the object. BaseTable (const String& tableName, int tableOption, rownr_t nrrow); #ifdef HAVE_MPI // MPI version of the constructor BaseTable (MPI_Comm mpiComm, const String& tableName, int tableOption, rownr_t nrrow); #endif // Common code shared by the MPI constructor and non-MPI constructor void BaseTableCommon (const String& tableName, int tableOption, rownr_t nrrow); // The destructor will delete the table if needed. virtual ~BaseTable(); // Copy constructor is forbidden, because copying a table requires // some more knowledge (like table name of result). BaseTable (const BaseTable&) = delete; // Assignment is forbidden, because copying a table requires // some more knowledge (like table name of result). BaseTable& operator= (const BaseTable&) = delete; // Is the table a null table? // By default it is not. virtual Bool isNull() const; // Reopen the table for read/write. virtual void reopenRW() = 0; // Is the table stored in big or little endian format? virtual Bool asBigEndian() const = 0; // Get the storage option used for the table. virtual const StorageOption& storageOption() const = 0; // Is the table in use (i.e. open) in another process? // If checkSubTables is set, it is also checked if // a subtable is used in another process. virtual Bool isMultiUsed(Bool checkSubTables) const = 0; // Get the locking info. virtual const TableLock& lockOptions() const = 0; // Merge the given lock info with the existing one. virtual void mergeLock (const TableLock& lockOptions) = 0; // Has this process the read or write lock, thus can the table // be read or written safely? virtual Bool hasLock (FileLocker::LockType) const = 0; // Try to lock the table for read or write access. virtual Bool lock (FileLocker::LockType, uInt nattempts) = 0; // Unlock the table. This will also synchronize the table data, // thus force the data to be written to disk. virtual void unlock() = 0; // Flush the table, i.e. write it to disk. virtual void flush (Bool fsync, Bool recursive) = 0; // Resync the Table object with the table file. virtual void resync() = 0; // Get the modify counter. virtual uInt getModifyCounter() const = 0; // Set the table to being changed. By default it does nothing. virtual void setTableChanged(); // Do not write the table (used in in case of exceptions). void doNotWrite() { noWrite_p = True; } // Test if this table is writable. // This tells if values can be put into a column. virtual Bool isWritable() const = 0; // Test if the given column is writable. // Bool isColumnWritable (const String& columnName) const; Bool isColumnWritable (uInt columnIndex) const; // // Test if the given column is stored (otherwise it is virtual). // Bool isColumnStored (const String& columnName) const; Bool isColumnStored (uInt columnIndex) const; // // Get the table name. const String& tableName() const { return name_p; } // Get the names of the tables this table consists of. // The default implementation adds the name of this table to the block. virtual void getPartNames (Block& names, Bool recursive) const; // Rename the table. // The following options can be given: //
        //
        Table::Update //
        A table with this name must already exists, which will be // overwritten. When succesfully renamed, the table is unmarked // for delete (if necessary). //
        Table::New //
        When a table with this name exists, it will be overwritten. // When succesfully renamed, the table is unmarked // for delete (if necessary). //
        Table::NewNoReplace //
        When a table with this name already exists, an exception // is thrown. When succesfully renamed, the table // is unmarked for delete (if necessary). //
        Table::Scratch //
        Same as Table::New, but followed by markForDelete(). //
        // The rename function in this base class renames the table file. // In a derived class (e.g. PlainTable) the function should also // be implemented to rename subtables in its keywords. virtual void rename (const String& newName, int tableOption); // Copy the table and all its subtables. // The default implementation of deepCopy is to call copy. // The following options can be given: //
        //
        Table::New //
        When a table with this name exists, it will be overwritten. //
        Table::NewNoReplace //
        When a table with this name already exists, an exception // is thrown. //
        Table::Scratch //
        Same as Table::New, but followed by markForDelete(). //
        // virtual void copy (const String& newName, int tableOption) const; virtual void deepCopy (const String& newName, const Record& dataManagerInfo, const StorageOption&, int tableOption, Bool valueCopy, int endianFormat, Bool noRows) const; // // Get the table type. // By default it returns Table::Plain. virtual int tableType() const; // Get the table option. int tableOption() const { return option_p; } // Mark the table for delete. // This means that the underlying table gets deleted when it is // actually destructed. // The scratchCallback function is called when needed. void markForDelete (Bool callback, const String& oldName); // Unmark the table for delete. // This means the underlying table does not get deleted when destructed. // The scratchCallback function is called when needed. void unmarkForDelete (Bool callback, const String& oldName); // Test if the table is marked for delete. Bool isMarkedForDelete() const { return delete_p; } // Get the table description. const TableDesc& tableDesc() const { return (!tdescPtr_p ? makeEmptyTableDesc() : *tdescPtr_p); } // Get the actual table description. virtual TableDesc actualTableDesc() const = 0; // Get the data manager info. virtual Record dataManagerInfo() const = 0; // Show the table structure (implementation of Table::showStructure). void showStructure (std::ostream&, Bool showDataMan, Bool showColumns, Bool showSubTables, Bool sortColumns, Bool cOrder); // Get readonly access to the table keyword set. virtual TableRecord& keywordSet() = 0; // Get read/write access to the table keyword set. // This requires that the table is locked (or it gets locked // when using AutoLocking mode). virtual TableRecord& rwKeywordSet() = 0; // Get access to the TableInfo object. TableInfo& tableInfo() { return info_p; } // Get the table info of the table with the given name. // An empty object is returned when the table is unknown. static TableInfo tableInfo (const String& tableName); // Write the TableInfo object. virtual void flushTableInfo(); // Get number of rows. rownr_t nrow() const { return nrrow_p; } // Get a column object using its index. virtual BaseColumn* getColumn (uInt columnIndex) const = 0; // Get a column object using its name. virtual BaseColumn* getColumn (const String& columnName) const = 0; // Test if it is possible to add a row to this table. virtual Bool canAddRow() const; // Add one or more rows and possibly initialize them. // This will fail for tables not supporting addition of rows. virtual void addRow (rownr_t nrrow = 1, Bool initialize = True); // Test if it is possible to remove a row from this table. virtual Bool canRemoveRow() const; // Remove rows. // This will fail for tables not supporting removal of rows. // // The following code fragments do NOT have the same result: // // tab.removeRow (10); // remove row 10 // tab.removeRow (20); // remove row 20, which was 21 // // Vector vec(2); // vec(0) = 10; // vec(1) = 20; // tab.removeRow (vec); // remove row 10 and 20 // // because in the first fragment removing row 10 turns the former // row 21 into row 20. // // virtual void removeRow (rownr_t rownr); void removeRow (const Vector& rownrs); void removeRow (const Vector& rownrs); // // Find the data manager with the given name or for the given column. virtual DataManager* findDataManager (const String& name, Bool byColumn) const = 0; // Select rows using the given expression (which can be null). // Skip first offset matching rows. // Return at most maxRow matching rows. std::shared_ptr select (const TableExprNode&, rownr_t maxRow, rownr_t offset); // Select maxRow rows and skip first offset rows. maxRow=0 means all. std::shared_ptr select (rownr_t maxRow, rownr_t offset); // Select rows using a vector of row numbers. std::shared_ptr select (const Vector& rownrs); // Select rows using a mask block. // The length of the block must match the number of rows in the table. // If True, the corresponding row will be selected. std::shared_ptr select (const Block& mask); // Project the given columns (i.e. select the columns). std::shared_ptr project (const Block& columnNames); // Do logical operations on a table. // // intersection with another table std::shared_ptr tabAnd (BaseTable*); // union with another table std::shared_ptr tabOr (BaseTable*); // subtract another table std::shared_ptr tabSub (BaseTable*); // xor with another table std::shared_ptr tabXor (BaseTable*); // take complement std::shared_ptr tabNot (); // // Sort a table on one or more columns of scalars. std::shared_ptr sort (const Block& columnNames, const Block>& compareObjects, const Block& sortOrder, int sortOption, std::shared_ptr> sortIterBoundaries = nullptr, std::shared_ptr> sortIterKeyIdxChange = nullptr); // Create an iterator. BaseTableIterator* makeIterator (const Block& columnNames, const Block>&, const Block& orders, int option, bool cacheIterationBoundaries = false); // Add one or more columns to the table. // The default implementation throws an "invalid operation" exception. // virtual void addColumn (const ColumnDesc& columnDesc, Bool addToParent); virtual void addColumn (const ColumnDesc& columnDesc, const String& dataManager, Bool byName, Bool addToParent); virtual void addColumn (const ColumnDesc& columnDesc, const DataManager& dataManager, Bool addToParent); virtual void addColumn (const TableDesc& tableDesc, const DataManager& dataManager, Bool addToParent); // // Add one or more columns to the table. // The data manager to use is described in the record. void addColumns (const TableDesc& tableDesc, const Record& dmInfo, Bool addToParent); // Test if columns can be removed. virtual Bool canRemoveColumn (const Vector& columnNames) const = 0; // Remove columns. virtual void removeColumn (const Vector& columnNames) = 0; // Check if the set of columns can be removed. // It checks if columns have not been specified twice and it // checks if they exist. // If the flag is set an exception is thrown if errors are found. Bool checkRemoveColumn (const Vector& columnNames, Bool throwException) const; // Test if a column can be renamed. virtual Bool canRenameColumn (const String& columnName) const = 0; // Rename a column. virtual void renameColumn (const String& newName, const String& oldName) = 0; // Rename a hypercolumn. virtual void renameHypercolumn (const String& newName, const String& oldName) = 0; // Get a vector of row numbers. // By default it returns the row numbers 0..nrrow()-1. // It needs to be implemented for RefTable only. virtual Vector rowNumbers() const; // Get pointer to root table (i.e. parent of a RefTable). // Default it is this table. // It is meant for the reference tables after a select or sort which // can then still name their parent as the root. virtual BaseTable* root(); // Tell if the table is in row order. // By default it is, since normally a table is always in row order. // It is meant for RefTable-s, where the rows can be in // another (sorted) order. virtual Bool rowOrder() const; // By the default the table cannot return the storage of rownrs. // That can only be done by a RefTable, where it is implemented. virtual Vector& rowStorage(); // Adjust the row numbers to be the actual row numbers in the // root table. This is, for instance, used when a RefTable is sorted. // Optionally it also determines if the resulting rows are in order. virtual Bool adjustRownrs (rownr_t nrrow, Vector& rownrs, Bool determineOrder) const; // Do the actual sort. // The default implementation is suitable for almost all cases. // Only in RefTable a smarter implementation is provided. virtual std::shared_ptr doSort (PtrBlock&, const Block>&, const Block& sortOrder, int sortOption, std::shared_ptr> sortIterBoundaries, std::shared_ptr> sortIterKeyIdxChange); // Create a RefTable object. std::shared_ptr makeRefTable (Bool rowOrder, rownr_t initialNrrow); // Check if the row number is valid. // It throws an exception if out of range. void checkRowNumber (rownr_t rownr) const { if (rownr >= nrrow_p + nrrowToAdd_p) checkRowNumberThrow (rownr); } // Get the table's trace-id. int traceId() const { return itsTraceId; } protected: std::weak_ptr thisPtr_p; //# pointer to itself (to make shared_ptr) rownr_t nrrow_p; //# #rows in this table rownr_t nrrowToAdd_p; //# #rows to be added std::shared_ptr tdescPtr_p; //# Pointer to table description String name_p; //# table name int option_p; //# Table constructor option Bool noWrite_p; //# False = do not write the table Bool delete_p; //# True = delete when destructed TableInfo info_p; //# Table information (type, etc.) Bool madeDir_p; //# True = table dir has been created int itsTraceId; //# table-id for TableTrace tracing // Do the callback for scratch tables (if callback is set). void scratchCallback (Bool isScratch, const String& oldName) const; // Create the table directory when needed (and possible). // When the file already exists, check if it is a directory. // It returns True when it actually created the directory. Bool makeTableDir(); // Make a true deep copy of the table. // The table is flushed before making the copy. void trueDeepCopy (const String& newName, const Record& dataManagerInfo, const StorageOption&, int tableOption, int endianFormat, Bool noRows) const; // Prepare for copying or renaming a table. // It checks if the target table already exists and removes it // when necessary. void prepareCopyRename (const String& newName, int tableOption) const; // Rename the subtables (used by rename function). virtual void renameSubTables (const String& newName, const String& oldName); // Check if the table already exists. // Throw an exception if so. void throwIfTableExists(); // Test if the table is opened for write. Bool openedForWrite() const; // Start writing a table. It does a putstart and writes nrrow_p. // It should be ended by calling writeEnd. void writeStart (AipsIO&, Bool bigEndian); // End writing a table. void writeEnd (AipsIO&); // Should the table be written. // This flag is False if an exception was thrown. Bool shouldNotWrite() const { return noWrite_p; } // Read the TableInfo object. void getTableInfo(); private: // Show a possible extra table structure header. // It is used by e.g. RefTable to show which table is referenced. virtual void showStructureExtra (std::ostream&) const; // Show the info of the given columns. // Sort the columns if needed. void showColumnInfo (ostream& os, const TableDesc&, uInt maxNameLength, const Array& columnNames, Bool sort, Bool cOrder) const; // Throw an exception for checkRowNumber. void checkRowNumberThrow (rownr_t rownr) const; // Check if the tables combined in a logical operation have the // same root. void logicCheck (BaseTable* that); // Get the rownrs of the table in ascending order to be // used in the logical operation on the table. Vector logicRows(); // Make an empty table description. // This is used if one asks for the description of a NullTable. // Creating an empty TableDesc in the NullTable takes too much time. // Furthermore it causes static initialization order problems. const TableDesc& makeEmptyTableDesc() const; // Make the name absolute. // It first checks if the name contains valid characters (not only . and /). String makeAbsoluteName (const String& name) const; #ifdef HAVE_MPI // MPI communicator for parallel I/O. // When using an MPI-disabled casacore, MPI applications have always been // able to create Tables from each rank independently. Defaulting this // communicator to MPI_COMM_SELF preserves that expectation. MPI_Comm itsMpiComm = MPI_COMM_SELF; #endif }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/ColDescSet.cc000066400000000000000000000207621476623553700203160ustar00rootroot00000000000000//# ColDescSet.cc: This class defines a set of column descriptions //# Copyright (C) 1994,1995,1996,1997,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ColumnDescSet::ColumnDescSet() : colSeq_p (0) { } ColumnDescSet::ColumnDescSet (const ColumnDescSet& that) : colSeq_p (0) { operator= (that); } ColumnDescSet::~ColumnDescSet() {} ColumnDescSet& ColumnDescSet::operator= (const ColumnDescSet& that) { if (this != &that) { uInt nrcol = that.cols_p.size(); colSeq_p.resize (nrcol); cols_p.clear(); //# Now we have to fill in the column order, which is the //# same as the order in the source. //# Make a copy of the ColumnDesc object and keep a pointer to it. for (uInt i=0; i col = that.cols_p.at(colName); cols_p.insert (std::make_pair (colName, std::shared_ptr(new ColumnDesc(*col)))); colSeq_p[i] = cols_p.at(colName).get(); } } return *this; } ColumnDesc& ColumnDescSet::operator[] (const String& name) { // Throw an exception if the column is undefined. std::map>::iterator iter = cols_p.find (name); if (iter == cols_p.end()) { throw (TableError ("Table column " + name + " is unknown")); } return *(iter->second); } //# Add a column to the set with another name. ColumnDesc& ColumnDescSet::addColumn (const ColumnDesc& cd, const String& newname) { //# First make a copy to be able to change the name. ColumnDesc coldes (cd); coldes.setName (newname); return addColumn (coldes); } //# Add a column to the set. ColumnDesc& ColumnDescSet::addColumn (const ColumnDesc& cd) { //# First check if the column name already exists. if (isDefined (cd.name())) { throw (TableInvColumnDesc (cd.name(), "column already exists")); } cd.checkAdd (*this); cols_p.insert (std::make_pair(cd.name(), std::shared_ptr(new ColumnDesc(cd)))); //# Get actual column description object. ColumnDesc& coldes = *(cols_p.at(cd.name())); //# Add the new column to the sequence block. uInt nrcol = ncolumn(); if (nrcol > colSeq_p.nelements()) { colSeq_p.resize (nrcol + 63); } colSeq_p[nrcol-1] = &coldes; coldes.handleAdd (*this); return coldes; } //# Remove a column. //# Let the column first act upon its removal. void ColumnDescSet::remove (const String& name) { ColumnDesc& cd = (*this)[name]; cd.handleRemove (*this); //# Remove it first from the sequence block. uInt nrcol = ncolumn(); for (uInt i=0; i(colSeq_p[inx])->name() == oldname) { break; } } AlwaysAssert (inx < colSeq_p.size(), AipsError); std::shared_ptr cdesc = cols_p.at(oldname); cdesc->checkRename (*this, newname); cols_p.erase (oldname); cols_p.insert (std::make_pair(newname, cdesc)); ColumnDesc& cd = *(cols_p.at(newname)); colSeq_p[inx] = &cd; //# Actually rename in BaseColDesc object. cd.setName (newname); //# Handle rename for other things. cd.handleRename (*this, oldname); for (auto& x : cols_p) { x.second->renameAction (newname, oldname); } } //# Check recursevily if the descriptions of all subtables are known. void ColumnDescSet::checkSubTableDesc() const { uInt nrcol = ncolumn(); for (uInt i=0; icheckSubTableDesc(); // check recursively } } } Bool ColumnDescSet::isEqual (const ColumnDescSet& other, Bool& equalDataTypes) const { equalDataTypes = False; if (ncolumn() != other.ncolumn()) { return False; } return allExist (other, equalDataTypes); } Bool ColumnDescSet::isSubset (const ColumnDescSet& other, Bool& equalDataTypes) const { equalDataTypes = False; if (ncolumn() > other.ncolumn()) { return False; } return allExist (other, equalDataTypes); } Bool ColumnDescSet::isStrictSubset (const ColumnDescSet& other, Bool& equalDataTypes) const { equalDataTypes = False; if (ncolumn() >= other.ncolumn()) { return False; } return allExist (other, equalDataTypes); } Bool ColumnDescSet::allExist (const ColumnDescSet& other, Bool& equalDataTypes) const { equalDataTypes = True; uInt nrcol = ncolumn(); for (uInt i=0; i> nrcol; for (uInt i=0; i #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Set of table column descriptions // // // // // //# Classes you should understand before using this one. //
      • TableDesc //
      • BaseColumnDesc //
      • Keyword module // // // ColumnDescSet is the set of column descriptions in a table description. // // // ColumnDescSet is used by // TableDesc // to store all column descriptions. // // In principle this class is only used internally by the table system. // However, there is a function in TableDesc which gives const access // to this class. This can be used by the user to call functions // like isDisjoint. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class ColumnDescSet { friend class TableDesc; public: // Construct an empty column set. ColumnDescSet(); // Copy constructor (copy semantics). ColumnDescSet (const ColumnDescSet&); ~ColumnDescSet(); // Assignment (copy semantics). ColumnDescSet& operator= (const ColumnDescSet&); // Get a column by its name. // ColumnDesc& operator[] (const String& name); const ColumnDesc& operator[] (const String& name) const { return (*(ColumnDescSet*)this)[name]; } // // Get a column by its index. // ColumnDesc& operator[] (uInt index) { return *(ColumnDesc*)(colSeq_p[index]); } const ColumnDesc& operator[] (uInt index) const { return *(ColumnDesc*)(colSeq_p[index]); } // // Get nr of columns in this set. uInt ncolumn() const { return cols_p.size(); } // Test if a column is defined in this set. Bool isDefined (const String& name) const { return (cols_p.find(name) != cols_p.end()); } // Test if this set equals another one. // It is equal if the number of columns is equal and all field names in // this set occur in the other too. The order of the columns // is not important. //
        The flag equalDataTypes is set to True if the data types // of all columns match. Bool isEqual (const ColumnDescSet& other, Bool& equalDataTypes) const; // Test if this set is a subset of another one. // It is similar to isEqual above. Bool isSubset (const ColumnDescSet& other, Bool& equalDataTypes) const; // Test if this set is a strict subset of another one, thus // if it is a subset and not equal. Bool isStrictSubset (const ColumnDescSet& other, Bool& equalDataTypes) const; // Test if this set is a superset of another one. Bool isSuperset (const ColumnDescSet& other, Bool& equalDataTypes) const { return other.isSubset (*this, equalDataTypes); } // Test if this set is a strict superset of another one, thus // if it is a superset and not equal. Bool isStrictSuperset (const ColumnDescSet& other, Bool& equalDataTypes) const { return other.isStrictSubset (*this, equalDataTypes); } // Test if this and the other column set are disjoint. Bool isDisjoint (const ColumnDescSet& other) const; // Get const access to the column descriptions. //#// const TypedKeywords& columns() const //#// { return cols_p; } // Show the columns in the set. void show (ostream& os) const; // Check recursevily if the descriptions of all subtables are known. void checkSubTableDesc() const; private: // Add a column. // An exception is thrown if a column with this name already exists. ColumnDesc& addColumn (const ColumnDesc&); // Add a column with another name. // An exception is thrown if a column with this name already exists. ColumnDesc& addColumn (const ColumnDesc&, const String& newname); // Remove a column. // An exception is thrown if the column with this name does not exist. void remove (const String& name); // Rename a column in the set. // An exception is thrown if the new name already exists or if // the old name does not exist. void rename (const String& newname, const String& oldname); // Test if all columns are part of the other set. // The flag equalDataTypes is set to True if the data types of the // columns in both sets are the same. Bool allExist (const ColumnDescSet&, Bool& equalDataTypes) const; // Add another (disjoint) column set. // If the sets are not disjoint (i.e. the other set contains a column // with an already existing name, an exception is thrown and nothing // of the other set is added. void add (const ColumnDescSet& set); // Put the object. void putFile (AipsIO& ios, const TableAttr&) const; // Get the object void getFile (AipsIO&, const TableAttr&); // The set of all columns. std::map> cols_p; // The order of addition of column descriptions. //# This is in fact a Block, but a void* is used //# to reduce the number of template instantiations. Block colSeq_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/ColumnCache.cc000066400000000000000000000030721476623553700205020ustar00rootroot00000000000000//# ColumnCache.cc: A caching object for a table column //# Copyright (C) 1997 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ColumnCache::ColumnCache() : itsIncr (1) { invalidate(); } void ColumnCache::set (rownr_t startRow, rownr_t endRow, const void* dataPtr) { itsStart = startRow; itsEnd = endRow; itsData = dataPtr; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/ColumnCache.h000066400000000000000000000113771476623553700203530ustar00rootroot00000000000000//# ColumnCache.h: A caching object for a table column //# Copyright (C) 1997 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_COLUMNCACHE_H #define TABLES_COLUMNCACHE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A caching object for a table column. // // // // // //# Classes you should understand before using this one. //
      • ScalarColumn // // // ColumnCache acts as a cache for a table column. // It contains a pointer to data and the start and end row number // for which these data are valid. An increment is part of the object // and is usually 0 or 1. The value 0 is used for data which is // valid for multiple rows (as used in // IncrementalStMan). // The value 1 is used for data stored consecutevily in a buffer for // each row (as used in StManAipsIO). //

        // The ColumnCache object is created and updated by the data manager. // The top level ScalarColumn object // contains a pointer to the cache object. In this way the // ScalarColumn::get can often be executed by a few inlined // statements which improves performance considerably. //

        // The invalidate function can be used to invalidate the // cache. This is for instance needed when a table lock is acquired // or released to be sure that the cache gets refreshed. // // // This class was developed to improve the performance for getting a scalar. // // //

      • For ConcatColumn add the ability to have other ColumnCache objects // using this one and invalidate them as well. // class ColumnCache { public: // Constructor. // It sets the increment to 1 and calls invalidate. ColumnCache(); // Set the increment to the given value. void setIncrement (rownr_t increment); // Set the start and end row number for which the given data pointer // is valid. void set (rownr_t startRow, rownr_t endRow, const void* dataPtr); // Invalidate the cache. // This clears the data pointer and sets startRow>endRow. void invalidate(); // Calculate the offset in the cached data for the given row. // -1 is returned if the row is not within the cached rows. Int64 offset (rownr_t rownr) const; // Give a pointer to the data. // The calling function has to do a proper cast after which the // calculated offset can be added to get the proper data. const void* dataPtr() const; // Give the start, end (including), and increment row number // of the cached column values. rownr_t start() const { return itsStart; } rownr_t end() const { return itsEnd; } rownr_t incr() const { return itsIncr; } private: rownr_t itsStart; rownr_t itsEnd; rownr_t itsIncr; const void* itsData; }; inline void ColumnCache::setIncrement (rownr_t increment) { itsIncr = increment; } inline void ColumnCache::invalidate() { set (1, 0, 0); } inline Int64 ColumnCache::offset (rownr_t rownr) const { if (rownr < itsStart || rownr > itsEnd) { return -1; } const rownr_t offset = (rownr - itsStart) * itsIncr; assert(offset <= static_cast(std::numeric_limits::max())); return Int64(offset); } inline const void* ColumnCache::dataPtr() const { return itsData; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/ColumnDesc.cc000066400000000000000000000230621476623553700203560ustar00rootroot00000000000000//# ColumnDesc.cc: Envelope class for description of a table column //# Copyright (C) 1994,1995,1996,1997,1998,2001,2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Initialize the statics. std::mutex ColumnDesc::theirMutex; ColumnDesc::ColumnDesc (const BaseColumnDesc& cold) : colPtr_p (cold.clone()), allocated_p(True) {} ColumnDesc::ColumnDesc (const ColumnDesc& that) : colPtr_p (that.colPtr_p), allocated_p(True) { if (colPtr_p != 0) { colPtr_p = colPtr_p->clone(); } } ColumnDesc::ColumnDesc (BaseColumnDesc* bcdp) : colPtr_p (bcdp), allocated_p(False) {} ColumnDesc::~ColumnDesc() { if (allocated_p) { delete colPtr_p; } } ColumnDesc& ColumnDesc::operator= (const ColumnDesc& that) { if (this != &that) { if (allocated_p) { delete colPtr_p; } colPtr_p = that.colPtr_p; if (colPtr_p != 0) { colPtr_p = colPtr_p->clone(); } allocated_p = True; } return *this; } Bool ColumnDesc::operator== (const ColumnDesc& that) const { if (dataType() != that.dataType()) return False; if (options() != that.options()) return False; if (ndim() != that.ndim()) return False; if (isScalar() && that.isScalar()) return True; if (isArray() && that.isArray()) return True; if (isTable() && that.isTable()) return True; return False; } Bool ColumnDesc::operator!= (const ColumnDesc& that) const { return !(*this == that); } Bool ColumnDesc::isFixedShape() const { if (isScalar()) { return True; } if ((options() & ColumnDesc::FixedShape) == ColumnDesc::FixedShape) { return True; } return False; } DataType ColumnDesc::trueDataType() const { DataType dtype = dataType(); if (! isArray()) { return dtype; } switch (dtype) { case TpBool: return TpArrayBool; case TpChar: return TpArrayChar; case TpUChar: return TpArrayUChar; case TpShort: return TpArrayShort; case TpUShort: return TpArrayUShort; case TpInt: return TpArrayInt; case TpUInt: return TpArrayUInt; case TpInt64: return TpArrayInt64; case TpFloat: return TpArrayFloat; case TpDouble: return TpArrayDouble; case TpComplex: return TpArrayComplex; case TpDComplex: return TpArrayDComplex; case TpString: return TpArrayString; default: AlwaysAssert (False, AipsError); } return TpOther; } // Return the column name. const String& ColumnDesc::name() const { return colPtr_p->name(); } AipsIO& operator<< (AipsIO& ios, const ColumnDesc& cd) { cd.putFile (ios, TableAttr()); return ios; } AipsIO& operator>> (AipsIO& ios, ColumnDesc& cd) { cd.getFile(ios, TableAttr()); return ios; } //# Put into AipsIO. //# It was felt that putstart takes too much space, so therefore //# the version is put "manually". void ColumnDesc::putFile (AipsIO& ios, const TableAttr& parentAttr) const { ios << (uInt)1; // class version 1 //# First write the exact column type, then its data. ios << colPtr_p->className(); colPtr_p->putFile (ios, parentAttr); } //# Get from AipsIO. void ColumnDesc::getFile (AipsIO& ios, const TableAttr& parentAttr) { uInt version; ios >> version; String tp; ios >> tp; if (allocated_p) { delete colPtr_p; } // If tp is not in the map, (tp, unknownColumnDesc) is added and called (throws). ColumnDesc::ColumnDescCtor* cdFunc = getCtor(tp); colPtr_p = (*cdFunc)(tp); allocated_p = True; colPtr_p->getFile (ios, parentAttr); } ostream& operator<< (ostream& ios, const ColumnDesc& cd) { cd.show (ios); return ios; } void ColumnDesc::show() const { show (cout); } void ColumnDesc::show (ostream& os) const { if (colPtr_p) { colPtr_p->show (os); os << " #keywords=" << keywordSet().nfields() << endl; os << keywordSet().description(); } else { os << "ColumnDesc is empty" << endl; } } //# Register a mapping. void ColumnDesc::registerCtor (const String& name, ColumnDesc::ColumnDescCtor* func) { std::lock_guard lock(theirMutex); getRegisterMap().insert (std::make_pair(name, func)); } //# Get a ColumnDesc constructor. //# Return default function if undefined. ColumnDesc::ColumnDescCtor* ColumnDesc::getCtor (const String& name) { std::map& regMap = getRegisterMap(); std::map::iterator iter = regMap.find (name); if (iter == regMap.end()) { throw; } return iter->second; } std::map& ColumnDesc::getRegisterMap() { static std::map regMap(initRegisterMap()); return regMap; } //# Register the main "static constructors" of all XColumnDesc classes. // No locking since private and only called by ctor of static member init. std::map ColumnDesc::initRegisterMap() { std::map regMap; ScalarColumnDesc scdb("x"); regMap.insert (std::make_pair(scdb.className(), &scdb.makeDesc)); ScalarColumnDesc scduc("x"); regMap.insert (std::make_pair(scduc.className(), &scduc.makeDesc)); ScalarColumnDesc scds("x"); regMap.insert (std::make_pair(scds.className(), &scds.makeDesc)); ScalarColumnDesc scdus("x"); regMap.insert (std::make_pair(scdus.className(), &scdus.makeDesc)); ScalarColumnDesc scdi("x"); regMap.insert (std::make_pair(scdi.className(), &scdi.makeDesc)); ScalarColumnDesc scdui("x"); regMap.insert (std::make_pair(scdui.className(), &scdui.makeDesc)); ScalarColumnDesc scdi64("x"); regMap.insert (std::make_pair(scdi64.className(), &scdi64.makeDesc)); ScalarColumnDesc scdf("x"); regMap.insert (std::make_pair(scdf.className(), &scdf.makeDesc)); ScalarColumnDesc scdd("x"); regMap.insert (std::make_pair(scdd.className(), &scdd.makeDesc)); ScalarColumnDesc scdcx("x"); regMap.insert (std::make_pair(scdcx.className(), &scdcx.makeDesc)); ScalarColumnDesc scddx("x"); regMap.insert (std::make_pair(scddx.className(), &scddx.makeDesc)); ScalarColumnDesc scdst("x"); regMap.insert (std::make_pair(scdst.className(), &scdst.makeDesc)); ScalarRecordColumnDesc srcd ("x"); regMap.insert (std::make_pair(srcd.className(), &srcd.makeDesc)); ArrayColumnDesc acdb("x"); regMap.insert (std::make_pair(acdb.className(), &acdb.makeDesc)); ArrayColumnDesc acduc("x"); regMap.insert (std::make_pair(acduc.className(), &acduc.makeDesc)); ArrayColumnDesc acds("x"); regMap.insert (std::make_pair(acds.className(), &acds.makeDesc)); ArrayColumnDesc acdus("x"); regMap.insert (std::make_pair(acdus.className(), &acdus.makeDesc)); ArrayColumnDesc acdi("x"); regMap.insert (std::make_pair(acdi.className(), &acdi.makeDesc)); ArrayColumnDesc acdui("x"); regMap.insert (std::make_pair(acdui.className(), &acdui.makeDesc)); ArrayColumnDesc acdi64("x"); regMap.insert (std::make_pair(acdi64.className(), &acdi64.makeDesc)); ArrayColumnDesc acdf("x"); regMap.insert (std::make_pair(acdf.className(), &acdf.makeDesc)); ArrayColumnDesc acdd("x"); regMap.insert (std::make_pair(acdd.className(), &acdd.makeDesc)); ArrayColumnDesc acdcx("x"); regMap.insert (std::make_pair(acdcx.className(), &acdcx.makeDesc)); ArrayColumnDesc acddx("x"); regMap.insert (std::make_pair(acddx.className(), &acddx.makeDesc)); ArrayColumnDesc acdst("x"); regMap.insert (std::make_pair(acdst.className(), &acdst.makeDesc)); SubTableDesc std("x", "", TableDesc()); regMap.insert (std::make_pair(std.className(), &std.makeDesc)); return regMap; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/ColumnDesc.h000066400000000000000000000361701476623553700202240ustar00rootroot00000000000000//# ColumnDesc.h: an envelope class for column descriptions in tables //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001,2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_COLUMNDESC_H #define TABLES_COLUMNDESC_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Envelope class for the description of a table column // // // // // //
      • Tables module (see especially Tables.h, the module header file) //
      • Envelope/Letter class design (see J. Coplien, Advanced C++) // // // Class ColumnDesc is an envelope for the letter class BaseColDesc // and its derivations like // ScalarColumnDesc, // // ScalarRecordColumnDesc. // ArrayColumnDesc, and // SubTableDesc. // ColumnDesc is meant to examine or slightly modify already existing // column descriptions. // It allows the retrieval of attributes like name, data type, etc.. // For non-const ColumnDesc objects it is possible to modify the // attributes comment and keyword set. // // Since there are several types of columns, the class ColumnDesc // cannot handle all details of those column types. Therefore, // to create a column description, an instance of the specialized // classes ArrayColumnDesc, etc. has to be constructed. // In there column type dependent things like array shape and // default value can be defined. // // This class also enumerates the possible options which can be used // when defining a column via classes like ScalarColumnDesc. // These options are: //
        //
        FixedShape //
        // This is only useful for columns containing arrays and tables. // FixedShape means that the shape of the array or table must // be the same in each cell of the column. // If not given, the array or table shape may vary. // Option Direct forces FixedShape. //
        Direct //
        // This is only useful for columns containing arrays and tables. // Direct means that the data is directly stored in the table. // Direct forces option FixedShape. // If not given, the array or table is indirect, which implies // that the data will be stored in a separate file. //
        Undefined //
        // Undefined is only useful for scalars. If not given, all possible // values of the scalar have a meaning. If given, a value equal to // the default value in the column description is an undefined value. // The function TableColumn::isDefined will return False for such // values. //
        //
        // // // TableDesc tableDesc("theTableDesc", TableDesc::New); // // Add a float scalar column. // tableDesc.addColumn (ScalarColumnDesc ("NAME"); // // Get the description of a column and change the comments. // // In order to change the comments, a reference must be used // // (because the ColumnDesc copy constructor and assign have copy // // semantics). // ColumnDesc& myColDesc = tableDesc.columnDesc ("aName"); // myColDesc.comment() += "some more comments"; // // // // When getting the description of an arbitrary column, a pointer to // that description is needed to allow proper execution of virtual // functions. // An envelope class is needed to hide this from the user. // // //# A List of bugs, limitations, extensions or planned refinements. // class ColumnDesc { friend class ColumnDescSet; friend class ColumnSet; friend class BaseColumn; public: // Enumerate the possible column options. // They can be combined by adding (logical or-ing) them. enum Option { // direct table or array Direct=1, // undefined values are possible Undefined=2, // fixed array/table shape FixedShape=4 }; // Construct from a column description. // This constructor is merely for the purpose of the automatic // conversion of an object like ScalarColumnDesc to // ColumnDesc when adding a column to the table description // using the function TableDesc::addColumn. ColumnDesc (const BaseColumnDesc&); // Copy constructor (copy semantics). ColumnDesc (const ColumnDesc& that); // Default constructor (needed for ColumnDescSet). ColumnDesc() : colPtr_p(0), allocated_p (False) {} ~ColumnDesc(); // Assignment (copy semantics). ColumnDesc& operator= (const ColumnDesc& that); // Comparison. // Two descriptions are equal when their data types, value types // (scalar, array or table) and possible dimensionalities are equal. // Bool operator== (const ColumnDesc&) const; Bool operator!= (const ColumnDesc&) const; // // Get access to the set of keywords. // TableRecord& rwKeywordSet() { return colPtr_p->rwKeywordSet(); } const TableRecord& keywordSet() const { return colPtr_p->keywordSet(); } // // Get the name of the column. //# Maybe it can be inlined. const String& name() const; // Get the data type of the column. // This always returns the type of a scalar, even when the column // contains arrays. DataType dataType() const { return colPtr_p->dataType(); } // Get the true data type of the column. // Unlike dataType, it returns an array data type (e.g. TpArrayInt) // when the column contains arrays. DataType trueDataType() const; // Get the type id for non-standard data types (i.e. for TpOther). // For standard data types the returned string is empty. const String& dataTypeId() const { return colPtr_p->dataTypeId(); } // Get the type name of the default data manager. const String& dataManagerType() const { return colPtr_p->dataManagerType(); } // Get the type name of the default data manager // (allowing it to be changed). String& dataManagerType() { return colPtr_p->dataManagerType(); } // Get the data manager group. const String& dataManagerGroup() const { return colPtr_p->dataManagerGroup(); } // Get the data manager group. // (allowing it to be changed). String& dataManagerGroup() { return colPtr_p->dataManagerGroup(); } // If always==True they are always set, otherwise only if empty. void setDefaultDataManager (Bool always=True) { colPtr_p->setDefaultDataManager (always); } // Get comment string. const String& comment() const { return colPtr_p->comment(); } // Get comment string (allowing it to be changed). String& comment() { return colPtr_p->comment(); } // Get the options. The possible options are defined by the enum Option. // E.g. // // const ColumnDesc& coldesc = tableDesc.getColumn ("column_name"); // if (coldesc.option() & ColumnDesc::Direct == ColumnDesc::Direct) { // // the column has the Direct flag set // } // int options() const { return colPtr_p->options(); } // Check if the column is defined with a fixed shape. // This is always true for scalars. For arrays it is true when // the FixedShape flag was set when the column was defined. Bool isFixedShape() const; // Test if column is a scalar. Bool isScalar() const { return colPtr_p->isScalar(); } // Test if column is an array. Bool isArray() const { return colPtr_p->isArray(); } // Test if column is a table. Bool isTable() const { return colPtr_p->isTable(); } // Get the number of dimensions. Int ndim() const { return colPtr_p->ndim(); } // Get the predefined shape. // If not defined, a zero shape will be returned. const IPosition& shape() const { return colPtr_p->shape(); } // Set the number of dimensions. // This is only allowed for arrays. // ndim can be zero to clear the number of dimensions // and the shape. // Otherwise it can only be used if the dimensionality has not been // defined yet. void setNdim (uInt ndim) { colPtr_p->setNdim (ndim); } // Set the predefined shape. // This is only allowed for arrays, for which the shape // has not been defined yet. // If the dimensionality has already been defined, it must match. // It will set the option FixedShape if not set yet. //
        The first version leaves the Direct option as is. // The second version sets the Direct option as given. // void setShape (const IPosition& shape) { colPtr_p->setShape (shape); } void setShape (const IPosition& shape, Bool directOption) { colPtr_p->setShape (shape, directOption); } // // Set the options to the given value. // Option ColumnDesc::Direct forces FixedShape. // If FixedShape is not given (implicitly or explicitly), // the column can have no shape, so its shape is cleared. void setOptions (int options) { colPtr_p->setOptions (options); } // Get the maximum value length. uInt maxLength() const { return colPtr_p->maxLength(); } // Set the maximum value length. // So far, this is only possible for columns containing String values. // An exception is thrown if the column data type is not TpString. // Some storage managers support fixed length strings and can store // them more efficiently than variable length strings. void setMaxLength (uInt maxLength) { colPtr_p->setMaxLength (maxLength); } // Get table description (in case column contains subtables). // const TableDesc* tableDesc() const { return colPtr_p->tableDesc(); } TableDesc* tableDesc() { return colPtr_p->tableDesc(); } // // Show the column on cout. void show() const; // Show the column. void show (ostream& os) const; // Write into AipsIO. friend AipsIO& operator<< (AipsIO& ios, const ColumnDesc& cd); // Read from AipsIO. friend AipsIO& operator>> (AipsIO& ios, ColumnDesc& cd); // Show on ostream. friend ostream& operator<< (ostream& ios, const ColumnDesc& cd); // Set the name of the column. void setName (const String& name) { colPtr_p->setName(name); } // Create a RefColumn column object out of this column description. RefColumn* makeRefColumn (RefTable* rtp, BaseColumn* bcp) const { return colPtr_p->makeRefColumn (rtp, bcp); } // Create a ConcatColumn column object out of this column description. ConcatColumn* makeConcatColumn (ConcatTable* rtp) const { return colPtr_p->makeConcatColumn (rtp); } // Define the type of a XXColumnDesc construction function. typedef BaseColumnDesc* ColumnDescCtor (const String& className); // Get a construction function for a XXColumnDesc object (thread-safe). static ColumnDescCtor* getCtor (const String& name); // Register a "XXColumnDesc" constructor (thread-safe). static void registerCtor (const String& name, ColumnDescCtor* func); private: // A mutex for additions to the constructor map. static std::mutex theirMutex; // Define a map which maps the name of the various XXColumnDesc // classes to a static function constructing them. // This is used when reading a column description back; it in fact // determines the exact column type and is an easier thing to do // than an enormous switch statement. // The map is filled with the main XXColumnDesc construction functions // by the function registerColumnDesc upon the first call of // ColumnDesc::getFile. static std::map& getRegisterMap(); // Register the main data managers. static std::map initRegisterMap(); // Construct from a pointer (for class BaseColumn). ColumnDesc (BaseColumnDesc*); // Check if a column can be handled by ColumnDescSet. // It is called before the column gets actually added, etc.. // // Check if the column can be added to the table description. // It is implemented for a virtual column to check if the columns // it uses really exist. void checkAdd (const ColumnDescSet& cds) const { colPtr_p->checkAdd (cds); } // Check when a column gets renamed in a table description. // It is not used. void checkRename (const ColumnDescSet& cds, const String& newName) const { colPtr_p->checkRename (cds, newName); } // // Take action after a column has been handled by ColumnDescSet. // It is called after the column has been actually added, etc.. // This gives, for instance, the virtual column class the opportunity // to update the virtual column list. // void handleAdd (ColumnDescSet& cds) { colPtr_p->handleAdd (cds); } void handleRename (ColumnDescSet& cds, const String& oldName) { colPtr_p->handleRename (cds, oldName); } void handleRemove (ColumnDescSet& cds) { colPtr_p->handleRemove (cds); } // // This function allows each column to act upon a rename of another column. // If the old name is used internally, the column can update itself. // It is called after handleRename has been called. void renameAction (const String& newName, const String& oldName) { colPtr_p->renameAction (newName, oldName); } // Create a PlainColumn column object out of this column description. PlainColumn* makeColumn (ColumnSet* csp) const { return colPtr_p->makeColumn (csp); } // Store the object in AipsIO. void putFile (AipsIO& ios, const TableAttr&) const; // Get the object from AipsIO. void getFile (AipsIO&, const TableAttr&); protected: BaseColumnDesc* colPtr_p; Bool allocated_p; //# False = not allocated -> do not delete }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/ColumnSet.cc000066400000000000000000000766271476623553700202520ustar00rootroot00000000000000//# ColumnSet.cc: Class to manage a set of table columns //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN #define BLOCKDATAMANVAL(I) (static_cast(blockDataMan_p[I])) #define COLMAPNAME(NAME) (static_cast(colMap_p.at(NAME))) #define COLMAPCAST(PTR) (static_cast(PTR)) ColumnSet::ColumnSet (TableDesc* tdesc, const StorageOption& opt) : tdescPtr_p (tdesc), storageOpt_p (opt), baseTablePtr_p (0), lockPtr_p (0), seqCount_p (0), blockDataMan_p (0) { //# Loop through all columns in the description and create //# a column out of them. for (uInt i=0; incolumn(); i++) { const ColumnDesc& cd = tdescPtr_p->columnDesc(i); colMap_p.insert (std::make_pair(cd.name(), cd.makeColumn(this))); } } ColumnSet::~ColumnSet() { for (auto& x : colMap_p) { delete COLMAPCAST(x.second); } for (uInt i=0; icolumnDesc(columnName); // check if column exists return COLMAPNAME(columnName); } //# First get the column name and use that as key. PlainColumn* ColumnSet::getColumn (uInt columnIndex) const { const String& name = tdescPtr_p->columnDesc(columnIndex).name(); return COLMAPNAME(name); } void ColumnSet::addDataManager (DataManager* dmPtr) { uInt nr = blockDataMan_p.nelements(); blockDataMan_p.resize (nr + 1); blockDataMan_p[nr] = dmPtr; dmPtr->setSeqnr (seqCount_p++); } void ColumnSet::removeLastDataManager() { uInt nr = blockDataMan_p.nelements() - 1; delete BLOCKDATAMANVAL(nr); blockDataMan_p.resize (nr, True); seqCount_p--; } void ColumnSet::initDataManagers (rownr_t nrrow, Bool bigEndian, const TSMOption& tsmOption, Table& tab) { for (uInt i=0; isetEndian (bigEndian); BLOCKDATAMANVAL(i)->setTsmOption (tsmOption); } for (uInt i=0; icreateDataManagerColumn(); } //# Delete data managers without columns. uInt nr = 0; for (uInt i=0; incolumn() > 0) { blockDataMan_p[nr++] = blockDataMan_p[i]; }else{ delete BLOCKDATAMANVAL(i); } } //# Remove possible trailing elements by resizing the block. blockDataMan_p.resize (nr, True); //# Set the number of rows. nrrow_p = nrrow; //# Initialize all data managers further. initSomeDataManagers (0, tab); } void ColumnSet::initSomeDataManagers (uInt from, Table& tab) { openMultiFile (from, tab, ByteIO::New); //# Link the data managers to the table. for (uInt i=from; ilinkToTable (tab); } //# Now give the data managers the opportunity to create files as needed. //# Thereafter to prepare things. for (uInt i=from; icreate64 (nrrow_p); } prepareSomeDataManagers (from); } void ColumnSet::prepareSomeDataManagers (uInt from) { for (uInt i=from; icanReallocateColumns()) { for (uInt j=0; jdataManagerColumn(); column = BLOCKDATAMANVAL(i)->reallocateColumn (column); } } } for (uInt i=from; iprepare(); } } void ColumnSet::openMultiFile (uInt from, const Table& tab, ByteIO::OpenOption opt) { // Exit if MultiFile/HDF5 should not be used. if (storageOpt_p.option() != StorageOption::MultiFile && storageOpt_p.option() != StorageOption::MultiHDF5) { return; } // See if any data manager can use MultiFile/HDF5. Bool useMultiFile = False; for (uInt i=from; ihasMultiFileSupport(); } // If anyone does, use the MultiFile. if (useMultiFile) { // Create the object if not created yet. if (! multiFile_p) { if (storageOpt_p.option() == StorageOption::MultiFile) { multiFile_p = std::make_shared(tab.tableName() + "/table.mf", opt, storageOpt_p.blockSize(), storageOpt_p.useODirect()); } else { multiFile_p = std::make_shared(tab.tableName() + "/table.mfh5", opt, storageOpt_p.blockSize()); } } // Pass it to the data managers. for (uInt i=from; isetMultiFile (multiFile_p); } } } rownr_t ColumnSet::resync (rownr_t nrrow, Bool forceSync) { //# There may be no sync data (when new table locked for first time). if (dataManChanged_p.nelements() > 0) { AlwaysAssert (dataManChanged_p.nelements() == blockDataMan_p.nelements(), AipsError); for (uInt i=0; iresync64 (nrrow); if (nrr > nrrow) { nrrow = nrr; } dataManChanged_p[i] = False; } } nrrow_p = nrrow; } return nrrow_p; } void ColumnSet::invalidateColumnCaches() { for (auto& x : colMap_p) { COLMAPCAST(x.second)->columnCache().invalidate(); } } //# Do all data managers allow to add and remove rows and columns? Bool ColumnSet::canAddRow() const { for (uInt i=0; icanAddRow()) { return False; } } return True; } Bool ColumnSet::canRemoveRow() const { for (uInt i=0; icanRemoveRow()) { return False; } } return True; } Bool ColumnSet::canRemoveColumn (const Vector& columnNames) const { // Cannot be removed if column is unknown. for (uInt i=0; iisColumn (columnNames(i))) { return False; } if (! getColumn(columnNames(i))->dataManager()->canRemoveColumn()) { return False; } } return True; } Bool ColumnSet::canRenameColumn (const String& columnName) const { // Cannot be renamed if column is unknown. if (! tdescPtr_p->isColumn (columnName)) { return False; } return getColumn(columnName)->dataManager()->canRenameColumn(); } //# Add rows to all data managers. void ColumnSet::addRow (rownr_t nrrow) { // First add row to storage managers, thereafter to virtual engines. for (uInt i=0; iisStorageManager()) { BLOCKDATAMANVAL(i)->addRow64 (nrrow); } } for (uInt i=0; iisStorageManager()) { BLOCKDATAMANVAL(i)->addRow64 (nrrow); } } nrrow_p += nrrow; } //# Remove a row from all data managers. void ColumnSet::removeRow (rownr_t rownr) { if (!canRemoveRow()) { throw (TableInvOper ("Rows cannot be removed from table " + baseTablePtr_p->tableName() + "; its storage managers do not support it")); } if (rownr >= nrrow_p) { throw (TableInvOper ("removeRow: rownr " + String::toString(rownr) + " too high in table " + baseTablePtr_p->tableName() + " (#rows=" + String::toString(nrrow_p) + ")")); } for (uInt i=0; iremoveRow64 (rownr); } nrrow_p--; } void ColumnSet::addColumn (const ColumnDesc& columnDesc, Bool bigEndian, const TSMOption& tsmOption, Table& tab) { // Find a storage manager allowing addition of columns. // If found, add the column to it and exit. DataManager* dmptr; for (uInt i=0; iisStorageManager() && dmptr->canAddColumn()) { doAddColumn (columnDesc, dmptr); return; } } // No suitable data manager found. // Create the default storage manager and add the column to it. // Make sure the data manager name is not already used. dmptr = DataManager::getCtor(columnDesc.dataManagerType()) (uniqueDataManagerName (columnDesc.dataManagerGroup()), Record()); addColumn (columnDesc, *dmptr, bigEndian, tsmOption, tab); delete dmptr; } void ColumnSet::addColumn (const ColumnDesc& columnDesc, const String& dataManager, Bool byName, Bool bigEndian, const TSMOption& tsmOption, Table& tab) { // Give an error when no data manager name/type given. if (dataManager.empty()) { throw (TableInvOper ("Table::addColumn: no datamanager name/type given " "when adding column " + columnDesc.name() + " to table " + baseTablePtr_p->tableName())); } // When given by name, find the data manager and add the column to it. // findDataManager throws an exception when the data manager is unknown. if (byName) { doAddColumn (columnDesc, findDataManager (dataManager)); return; } // Find the first data manager with the given type allowing addition // of columns. If found, add the column and exit. DataManager* dmptr; for (uInt i=0; idataManagerType()) { if (dmptr->canAddColumn()) { doAddColumn (columnDesc, dmptr); return; } } } // No suitable data manager found. // Create one of this type and add the column to it. // Use the data manager as the data manager name. dmptr = DataManager::getCtor(dataManager) (uniqueDataManagerName(dataManager), Record()); addColumn (columnDesc, *dmptr, bigEndian, tsmOption, tab); delete dmptr; } void ColumnSet::doAddColumn (const ColumnDesc& columnDesc, DataManager* dataManPtr) { if (! dataManPtr->canAddColumn()) { throw TableError ("Table::addColumn - DataManager " + dataManPtr->dataManagerName() + " (" + dataManPtr->dataManagerType() + ") does not support column addition to table " + baseTablePtr_p->tableName()); } checkWriteLock (True); //# When the column already exists, TableDesc::addColumn throws //# an exception. //# The creation and binding of a column will always succeed. const String& name = columnDesc.name(); ColumnDesc& cd = tdescPtr_p->addColumn (columnDesc); PlainColumn* col = cd.makeColumn (this); colMap_p.insert (std::make_pair(name, col)); col->bind (dataManPtr); //# The creation of a column may fail as well as adding the //# column to the storage manager. //# Take care of this by catching an exception and deleting the stuff. //# Rethrow the exception by getting the message and throwing it. Bool error = False; String msg; DataManagerColumn* dmcol = 0; uInt nrcol = dataManPtr->ncolumn(); try { col->createDataManagerColumn(); dmcol = col->dataManagerColumn(); dataManPtr->addColumn (dmcol); } catch (const std::exception& x) { error = True; msg = x.what(); //# Get the column pointer (it may not have been filled yet). //# When #columns has grown, the column has been already added. //# In that case remove it, which will also delete the column. //# Otherwise delete the column directly. dmcol = col->dataManagerColumn(); if (dataManPtr->ncolumn() > nrcol) { dataManPtr->removeColumn (dmcol); }else{ delete dmcol; } //# Delete the PlainColumn, remove from map and delete its description. delete col; colMap_p.erase (name); tdescPtr_p->removeColumn (name); } // Rethrow if there was an exception. if (error) { throw (AipsError (msg)); } autoReleaseLock(); } void ColumnSet::addColumn (const ColumnDesc& columnDesc, const DataManager& dataManager, Bool bigEndian, const TSMOption& tsmOption, Table& tab) { TableDesc td; td.addColumn (columnDesc); addColumn (td, dataManager, bigEndian, tsmOption, tab); } void ColumnSet::addColumn (const TableDesc& tableDesc, const DataManager& dataManager, Bool bigEndian, const TSMOption& tsmOption, Table& tab) { checkWriteLock (True); // Check if the data manager name has not been used already. checkDataManagerName (dataManager.dataManagerName(), 0, baseTablePtr_p->tableName()); // Add the new table description to the current one. // This adds column and possible hypercolumn descriptions. // When failing, nothing will have been added. tdescPtr_p->add (tableDesc, False); // Clone the data manager (to get our own copy) and add it to the list. DataManager* dmptr = dataManager.clone(); dmptr->setEndian (bigEndian); dmptr->setTsmOption (tsmOption); addDataManager (dmptr); // Loop through all new columns and construct column objects for them. // We have to use the column description in our table description. // Bind the column to the data manager and create a column in there. // An exception may be thrown in this loop, so things have to be cleaned. Bool error = False; String msg; try { for (uInt i=0; icolumnDesc(tableDesc[i].name()); PlainColumn* col = cd.makeColumn (this); colMap_p.insert (std::make_pair(cd.name(), col)); col->bind (dmptr); col->createDataManagerColumn(); } // Let the new data manager create space, etc. for its columns. initSomeDataManagers (blockDataMan_p.nelements() - 1, tab); } catch (const std::exception& x) { error = True; msg = x.what(); for (uInt i=0; iremoveColumn (name); } removeLastDataManager(); } // Rethrow if there was an exception. if (error) { throw (AipsError (msg)); } autoReleaseLock(); } void ColumnSet::removeColumn (const Vector& columnNames) { // Check if the columns can be removed. // Also find out about the data managers. std::map dmCounts = checkRemoveColumn (columnNames); // Write lock table. checkWriteLock (True); // Remove all data managers possible. for (auto& x : dmCounts) { if (x.second < 0) { DataManager* dmPtr = static_cast(const_cast(x.first)); dmPtr->deleteManager(); Bool found = False; for (uInt j=0; j 0) { objmove (&blockDataMan_p[j], &blockDataMan_p[j+1], nr); } blockDataMan_p.resize (nrb - 1, True, True); uInt nrc = dataManChanged_p.nelements(); if (j < nrc) { nr = nrc - j - 1; if (nr > 0) { objmove (&dataManChanged_p[j], &dataManChanged_p[j+1], nr); } dataManChanged_p.resize (nrc - 1, True, True); } break; } } AlwaysAssert (found, AipsError); } } // Remove all columns from description, data managers, and maps. for (uInt i=0; iremoveColumn (name); PlainColumn* colPtr = COLMAPNAME(name); DataManager* dmPtr = colPtr->dataManager(); if (dmCounts.at(dmPtr) >= 0) { DataManagerColumn* dmcolPtr = colPtr->dataManagerColumn(); dmPtr->removeColumn (dmcolPtr); } delete colPtr; colMap_p.erase (name); } autoReleaseLock(); } std::map ColumnSet::checkRemoveColumn (const Vector& columnNames) { // Check if the column names are valid. baseTablePtr_p->checkRemoveColumn (columnNames, True); // Count how many columns in each data manager are to be deleted. std::map dmCounts; for (uInt i=0; idataManager(); std::map::iterator iter = dmCounts.find(dmPtr); if (iter == dmCounts.end()) { dmCounts.insert (std::make_pair(dmPtr, 1)); } else { iter->second++; } } // If all columns in a data manager are to be deleted, set count to -1. for (auto& iter : dmCounts) { DataManager* dmPtr = static_cast(const_cast(iter.first)); if (iter.second == Int(dmPtr->ncolumn())) { iter.second = -1; } } // Now we have to check if a column can be deleted. // It can if all columns of its data manager are deleted or // if the data manager can handle column deletion. // Set a flag for the columns for which the entire data manager // cannot be deleted, thus the column has to be deleted explicitly. for (uInt i=0; idataManager(); if (dmCounts.at(dmPtr) >= 0 && ! dmPtr->canRemoveColumn()) { throw TableInvOper ("Table::removeColumn - column " + columnNames(i) + " cannot be removed from table " + baseTablePtr_p->tableName()); } } return dmCounts; } void ColumnSet::renameColumn (const String& newName, const String& oldName) { if (! tdescPtr_p->isColumn (oldName)) { throw (TableInvOper ("Table::renameColumn; column " + oldName + " does not exist in table " + baseTablePtr_p->tableName())); } if (tdescPtr_p->isColumn (newName)) { throw (TableInvOper ("Table::renameColumn; new column " + newName + " already exists in table " + baseTablePtr_p->tableName())); } checkWriteLock (True); tdescPtr_p->renameColumn (newName, oldName); void* ptr = colMap_p.at(oldName); colMap_p.erase (oldName); colMap_p.insert (std::make_pair(newName, ptr)); autoReleaseLock(); } DataManager* ColumnSet::findDataManager (const String& name, Bool byColumn) const { if (byColumn) { return COLMAPNAME(name)->dataManager(); } for (uInt i=0; idataManagerName()) { return dmp; } } throw (TableInvOper ("Data manager " + name + " is unknown in table " + baseTablePtr_p->tableName())); } void ColumnSet::checkDataManagerNames (const String& tableName) const { // Loop through all data managers. // A name can appear only once (except a blank name). String name; for (uInt i=0; idataManagerName(), i+1, tableName); } } Bool ColumnSet::checkDataManagerName (const String& name, uInt from, const String& tableName, Bool doTthrow) const { // Loop through all data managers. // A name can appear only once (except a blank name). if (! name.empty()) { for (uInt j=from; jdataManagerName()) { if (doTthrow) { throw TableInvOper ("Data manager name " + name + " is already used in table " + tableName); } return False; } } } return True; } String ColumnSet::uniqueDataManagerName (const String& name) const { String dmName = name; Int nr = 0; while (! checkDataManagerName (dmName, 0, String(), False)) { nr++; dmName = name + '_' + String::toString(nr); } return dmName; } TableDesc ColumnSet::actualTableDesc() const { TableDesc td = *tdescPtr_p; for (uInt i=0; idataManager()->dataManagerType(); cd.dataManagerGroup() = pc->dataManager()->dataManagerName(); if (cd.isArray() && cd.isFixedShape()) { if (cd.shape().nelements() == 0) { cd.setShape (pc->shapeColumn()); } } } return td; } Record ColumnSet::dataManagerInfo (Bool virtualOnly) const { Record rec; uInt nrec=0; // Loop through all data managers. for (uInt i=0; iisStorageManager()) { Record subrec; subrec.define ("TYPE", dmPtr->dataManagerType()); subrec.define ("NAME", dmPtr->dataManagerName()); // Add info of the data manager to the record. dmPtr->dataManagerInfo (subrec); // Loop through all columns with this data manager and add // its name to the vector. uInt ncol = colMap_p.size(); Vector columns(ncol); uInt nc=0; for (auto& x : colMap_p) { if (COLMAPCAST(x.second)->dataManager() == dmPtr) { columns(nc++) = x.first; } } if (nc > 0) { columns.resize (nc, True); subrec.define ("COLUMNS", columns); rec.defineRecord (nrec, subrec); nrec++; } } } return rec; } //# Initialize rows. void ColumnSet::initialize (rownr_t startRow, rownr_t endRow) { for (uInt i=0; iinitialize (startRow, endRow); } } void ColumnSet::reopenRW() { if (multiFile_p) { multiFile_p->reopenRW(); } // Reopen all data managers. for (uInt i=0; ireopenRW(); } // Reopen tables in all column keyword sets. for (uInt i=0; ikeywordSet().reopenRW(); } } void ColumnSet::renameTables (const String& newName, const String& oldName) { for (uInt i=0; irwKeywordSet().renameTables (newName, oldName); } } Bool ColumnSet::areTablesMultiUsed() const { for (uInt i=0; ikeywordSet().areTablesMultiUsed()) { return True; } } return False; } Bool ColumnSet::putFile (Bool writeTable, AipsIO& ios, const TableAttr& attr, Bool fsync) { Bool written = False; //# Only write the table data when the flag is set. uInt nrold = dataManChanged_p.nelements(); dataManChanged_p.resize (blockDataMan_p.nelements(), True); for (uInt i=nrold; i rownr_t(std::numeric_limits::max())) { ios << Int(-3); // version (must be negative !!!) ios << nrrow_p; ios << Int(storageOpt_p.option()) << storageOpt_p.blockSize(); } else { ios << Int(-2); ios << uInt(nrrow_p); } ios << seqCount_p; //# Start with writing the data manager types. //# Only write with columns in them (thus count first). uInt nr=0; for (uInt i=0; incolumn() > 0) { nr++; } } ios << nr; for (uInt i=0; incolumn() > 0) { ios << BLOCKDATAMANVAL(i)->dataManagerType(); ios << BLOCKDATAMANVAL(i)->sequenceNr(); } } //# Now write all columns. for (uInt i=0; iputFile (ios, attr); } } //# Now write out the data in all data managers. //# Keep track if a data manager indeed wrote something. auto memio = std::make_shared(); AipsIO aio(memio); for (uInt i=0; iflush (aio, fsync)) { dataManChanged_p[i] = True; written = True; } if (writeTable) { ios.put (uInt(memio->length()), memio->getBuffer()); } memio->clear(); } if (multiFile_p) { multiFile_p->flush(); } return written; } rownr_t ColumnSet::getFile (AipsIO& ios, Table& tab, rownr_t nrrow, Bool bigEndian, const TSMOption& tsmOption) { //# If the first value is negative, it is the version. //# Otherwise it is nrrow_p. Int version; uInt i, nr, seqnr, nrman; String str; ios >> version; if (version < 0) { version = -version; if (version <= 2) { // In older versions nrrow was an unsigned 4-byte integer. ios >> nr; nrrow_p = nr; } else { ios >> nrrow_p; } }else{ nrrow_p = version; version = 1; } //# Use nrrow from caller, since that is most accurate. nrrow_p = nrrow; // Read StorageOption for newer versions. if (version >= 3) { Int opt, bufsz; ios >> opt >> bufsz; storageOpt_p = StorageOption (StorageOption::Option(opt), bufsz); } else { storageOpt_p = StorageOption (StorageOption::SepFile); } ios >> nrman; ios >> nr; //# Construct the various data managers. for (i=0; i> str; ios >> seqnr; DataManager* dmp = DataManager::getCtor(str)(str, Record()); addDataManager (dmp); dmp->setSeqnr (seqnr); dmp->setEndian (bigEndian); dmp->setTsmOption (tsmOption); } // Open the MultiFile if used. openMultiFile (0, tab, tab.isWritable() ? ByteIO::Update : ByteIO::Old); //# Now set seqCount_p (because that was changed by addDataManager). seqCount_p = nrman; //# Now read in the columns and create the data manager columns. //# In the first version the columns were written in order of //# name. In the newer versions they are written in order of addition //# (which was needed to support addColumn properly and is better anyway). if (version == 1) { for (auto& x : colMap_p) { COLMAPCAST(x.second)->getFile (ios, *this, TableAttr(tab)); } }else{ for (i=0; igetFile (ios, *this, TableAttr(tab)); } } //# Link the data managers to the table. for (i=0; ilinkToTable (tab); } //# Finally open the data managers and let them prepare themselves. for (i=0; i(data, leng); AipsIO aio(memio); rownr_t nrrow = BLOCKDATAMANVAL(i)->open64 (nrrow_p, aio); if (nrrow > nrrow_p) { nrrow_p = nrrow; } delete [] data; } prepareSomeDataManagers (0); return nrrow_p; } //# Find the data manager with the given sequence number. DataManager* ColumnSet::getDataManager (uInt seqnr) const { DataManager* dmp = 0; for (uInt i=0; isequenceNr()) { return dmp; } } throw (TableInternalError ("ColumnSet::getDataManager")); return 0; } Bool ColumnSet::userLock (FileLocker::LockType type, Bool wait) { // Acquire automatically a lock when: // - Userlocking // - not locked yet // - not NoReadLocking if (lockPtr_p->option() == TableLock::UserLocking) { if (! baseTablePtr_p->hasLock (type)) { if (type != FileLocker::Read || lockPtr_p->readLocking()) { uInt nattempts = (wait ? 0 : 1); baseTablePtr_p->lock (type, nattempts); return True; } } } return False; } void ColumnSet::doLock (FileLocker::LockType type, Bool wait) { if (lockPtr_p->option() != TableLock::AutoLocking) { String str = "PermanentLocking"; if (lockPtr_p->option() == TableLock::UserLocking) { str = "UserLocking"; } throw (TableError ("ColumnSet::doLock: table " + baseTablePtr_p->tableName() + " should be locked when using " + str)); } uInt nattempts = (wait ? baseTablePtr_p->lockOptions().maxWait() : 1); baseTablePtr_p->lock (type, nattempts); } void ColumnSet::syncColumns (const ColumnSet& other, const TableAttr& defaultAttr) { uInt ncol = colMap_p.size(); if (other.colMap_p.size() != ncol) { throw (TableError ("ColumnSet::syncColumns; another process " "changed the number of columns of table " + baseTablePtr_p->tableName())); } for (uInt i=0; icolumnDesc() != othercol->columnDesc()) { throw (TableError ("ColumnSet::syncColumns; another process " "changed the description of column " + thiscol->columnDesc().name() + " in table " + baseTablePtr_p->tableName())); } // Adjust the attributes of subtables. // Update the table keywords. TableRecord& oldKeySet = thiscol->keywordSet(); TableRecord& newKeySet = othercol->keywordSet(); newKeySet.setTableAttr (oldKeySet, defaultAttr); oldKeySet = newKeySet; } } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/ColumnSet.h000066400000000000000000000322161476623553700200760ustar00rootroot00000000000000//# ColumnSet.h: Class to manage a set of table columns //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_COLUMNSET_H #define TABLES_COLUMNSET_H //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class SetupNewTable; class Table; class TableDesc; class TSMOption; class BaseTable; class TableAttr; class ColumnDesc; class PlainColumn; class DataManager; class MultiFile; class Record; class IPosition; class AipsIO; // // Class to manage a set of table columns // // // // // //# Classes you should understand before using this one. //
      • PlainTable //
      • DataManager // // // ColumnSet represent the set of columns in a table. // // // ColumnSet contains all columns in a plain table (thus not in a RefTable). // Furthermore it contains the set of data managers used by the columns // in the table. // // The main purpose of the class is to deal with constructing, writing // and reading the column objects. It is used by classes SetupNewTable // and Table. // // //# A List of bugs, limitations, extensions or planned refinements. // class ColumnSet { public: // Construct from the table description. // This creates all underlying filled and virtual column objects. ColumnSet (TableDesc*, const StorageOption& = StorageOption()); ~ColumnSet(); // Reopen the data managers for read/write. void reopenRW(); // Rename the necessary subtables in the column keywords. void renameTables (const String& newName, const String& oldName); // Get the storage option. const StorageOption& storageOption() const { return storageOpt_p; } // Get the possible MultiFileBase object used to combine files. std::shared_ptr getMultiFile() const { return multiFile_p; } // Are subtables used in other processes. Bool areTablesMultiUsed() const; // Get a column by name. PlainColumn* getColumn (const String& columnName) const; // Get a column by index. PlainColumn* getColumn (uInt columnIndex) const; // Add a data manager. // It increments seqCount_p and returns that as a unique sequence number. // This can, for instance, be used to create a unique file name. void addDataManager (DataManager*); // Initialize the data managers for a new table. // It creates the data manager column objects for each column // and it allows the data managers to link themselves to the // Table object and to initialize themselves. void initDataManagers (rownr_t nrrow, Bool bigEndian, const TSMOption& tsmOption, Table& tab); // Link the ColumnSet object to the BaseTable object. void linkToTable (BaseTable* baseTableObject); // Link the ColumnSet object to the TableLockData object. void linkToLockObject (TableLockData* lockObject); // Check if the table is locked for read or write. // If manual or permanent locking is in effect, it checks if the // table is properly locked. // If autolocking is in effect, it locks the table when needed. // void checkReadLock (Bool wait); void checkWriteLock (Bool wait); // // Inspect the auto lock when the inspection interval has expired and // release it when another process needs the lock. void autoReleaseLock(); // If needed, get a temporary user lock. // It returns False if the lock was already there. Bool userLock (FileLocker::LockType, Bool wait); // Release a temporary user lock if the given release flag is True. void userUnlock (Bool releaseFlag); // Do all data managers and engines allow to add rows? Bool canAddRow() const; // Do all data managers and engines allow to remove rows? Bool canRemoveRow() const; // Can the given columns be removed from the data manager? Bool canRemoveColumn (const Vector& columnNames) const; // Can a column be renamed in the data manager? Bool canRenameColumn (const String& columnName) const; // Add rows to all data managers. void addRow (rownr_t nrrow); // Remove a row from all data managers. // It will throw an exception if not possible. void removeRow (rownr_t rownr); // Remove the columns from the map and the data manager. void removeColumn (const Vector& columnNames); // Rename the column in the map. void renameColumn (const String& newName, const String& oldName); // Add a column to the table. // The default implementation throws an "invalid operation" exception. // void addColumn (const ColumnDesc& columnDesc, Bool bigEndian, const TSMOption& tsmOption, Table& tab); void addColumn (const ColumnDesc& columnDesc, const String& dataManager, Bool byName, Bool bigEndian, const TSMOption& tsmOption, Table& tab); void addColumn (const ColumnDesc& columnDesc, const DataManager& dataManager, Bool bigEndian, const TSMOption& tsmOption, Table& tab); void addColumn (const TableDesc& tableDesc, const DataManager& dataManager, Bool bigEndian, const TSMOption& tsmOption, Table& tab); // // Get nr of rows. rownr_t nrow() const; // Get the actual table description. TableDesc actualTableDesc() const; // Get the data manager info. // Optionally only the virtual engines are retrieved. Record dataManagerInfo (Bool virtualOnly=False) const; // Get the trace-id of the table. int traceId() const { return baseTablePtr_p->traceId(); } // Initialize rows startRownr till endRownr (inclusive). void initialize (rownr_t startRownr, rownr_t endRownr); // Write all the data and let the data managers flush their data. // This function is called when a table gets written (i.e. flushed). // It returns True if any data manager wrote something. Bool putFile (Bool writeTable, AipsIO&, const TableAttr&, Bool fsync); // Read the data, reconstruct the data managers, and link those to // the table object. // This function gets called when an existing table is read back. // It returns the number of rows in case a data manager thinks there are // more. That is in particular used by LofarStMan. rownr_t getFile (AipsIO&, Table& tab, rownr_t nrrow, Bool bigEndian, const TSMOption& tsmOption); // Set the table to being changed. void setTableChanged(); // Get the data manager change flags (used by PlainTable). Block& dataManChanged(); // Synchronize the data managers when data in them have changed. // It returns the number of rows it think it has, which is needed for // storage managers like LofarStMan. // forceSync=True means that the data managers are forced // to do a sync. Otherwise the contents of the lock file tell if a data // manager has to sync. rownr_t resync (rownr_t nrrow, Bool forceSync); // Invalidate the column caches for all columns. void invalidateColumnCaches(); // Get the correct data manager. // This is used by the column objects to link themselves to the // correct datamanagers when they are read back. DataManager* getDataManager (uInt seqnr) const; // Check if no double data manager names have been given. void checkDataManagerNames (const String& tableName) const; // Find the data manager with the given name or for the given column. // If the data manager or column is unknown, an exception is thrown. // A blank name means the data manager is unknown. DataManager* findDataManager (const String& name, Bool byColumn=False) const; // Make a unique data manager name by appending a suffix _n if needed // where n is a number that makes the name unique. String uniqueDataManagerName (const String& name) const; // Synchronize the columns after it appeared that data in the // main table file have changed. // It cannot deal with changes in number of columns, so it throws an // exception when they have changed. // Keywords in all columns are updated. // The other ColumnSet gives the new data. void syncColumns (const ColumnSet& other, const TableAttr& defaultAttr); private: // Remove the last data manager (used by addColumn after an exception). // It does the opposite of addDataManager. void removeLastDataManager(); // Let the data managers (from the given index on) initialize themselves. void initSomeDataManagers (uInt from, Table& tab); // Let the data managers (from the given index on) prepare themselves. void prepareSomeDataManagers (uInt from); // Open or create the MultiFile if needed. void openMultiFile (uInt from, const Table& tab, ByteIO::OpenOption); // Check if a data manager name has not already been used. // Start checking at the given index in the array. // It returns False if the name has already been used. // By default an exception is thrown if the name has already been used. Bool checkDataManagerName (const String& name, uInt from, const String& tableName, Bool doTthrow=True) const; // Do the actual addition of a column. void doAddColumn (const ColumnDesc& columnDesc, DataManager* dataManPtr); // Check if columns to be removed can be removed. // It returns a map of DataManager* telling how many columns for // a data manager have to be removed. A count of -1 means that all // columns have to be removed. For such columns the flag in the // returned Block is False, otherwise True. std::map checkRemoveColumn (const Vector& columnNames); // Check if the table is locked for read or write. // If manual or permanent locking is in effect, it checks if the // table is properly locked. // If autolocking is in effect, it locks the table when needed. void doLock (FileLocker::LockType, Bool wait); //# Declare the variables. TableDesc* tdescPtr_p; StorageOption storageOpt_p; std::shared_ptr multiFile_p; rownr_t nrrow_p; //# #rows BaseTable* baseTablePtr_p; TableLockData* lockPtr_p; //# lock object std::map colMap_p; //# list of PlainColumns uInt seqCount_p; //# sequence number count //# (used for unique seqnr) Block blockDataMan_p; //# list of data managers Block dataManChanged_p; //# data has changed }; inline rownr_t ColumnSet::nrow() const { return nrrow_p; } inline void ColumnSet::linkToTable (BaseTable* baseTableObject) { baseTablePtr_p = baseTableObject; } inline void ColumnSet::setTableChanged() { baseTablePtr_p->setTableChanged(); } inline void ColumnSet::linkToLockObject (TableLockData* lockObject) { lockPtr_p = lockObject; } inline void ColumnSet::checkReadLock (Bool wait) { if (lockPtr_p->readLocking() && ! lockPtr_p->hasLock (FileLocker::Read)) { doLock (FileLocker::Read, wait); } } inline void ColumnSet::checkWriteLock (Bool wait) { if (! lockPtr_p->hasLock (FileLocker::Write)) { doLock (FileLocker::Write, wait); } } inline void ColumnSet::userUnlock (Bool releaseFlag) { if (releaseFlag) { lockPtr_p->release(); } } inline void ColumnSet::autoReleaseLock() { lockPtr_p->autoRelease(); } inline Block& ColumnSet::dataManChanged() { return dataManChanged_p; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/ColumnsIndex.cc000066400000000000000000000553661476623553700207460ustar00rootroot00000000000000//# ColumnsIndex.cc: Index to a table //# Copyright (C) 1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ColumnsIndex::ColumnsIndex (const Table& table, const String& columnName, Compare* compareFunction, Bool noSort) : itsLowerKeyPtr (0), itsUpperKeyPtr (0) { Vector columnNames(1); columnNames(0) = columnName; create (table, columnNames, compareFunction, noSort); } ColumnsIndex::ColumnsIndex (const Table& table, const Vector& columnNames, Compare* compareFunction, Bool noSort) { create (table, columnNames, compareFunction, noSort); } ColumnsIndex::ColumnsIndex (const ColumnsIndex& that) : itsLowerKeyPtr (0), itsUpperKeyPtr (0) { copy (that); } ColumnsIndex::~ColumnsIndex() { deleteObjects(); } ColumnsIndex& ColumnsIndex::operator= (const ColumnsIndex& that) { copy (that); return *this; } void ColumnsIndex::copy (const ColumnsIndex& that) { if (this != &that) { deleteObjects(); itsTable = that.itsTable; itsNrrow = itsTable.nrow(); itsNoSort = that.itsNoSort; itsCompare = that.itsCompare; makeObjects (that.itsLowerKeyPtr->description()); } } Vector ColumnsIndex::columnNames() const { const RecordDesc& desc = itsLowerKeyPtr->description(); const uInt nrfield = desc.nfields(); Vector names(nrfield); for (uInt i=0; i*)(itsLowerFields[i]); delete (RecordFieldPtr*)(itsUpperFields[i]); delete (Vector*)(itsDataVectors[i]); break; case TpUChar: delete (RecordFieldPtr*)(itsLowerFields[i]); delete (RecordFieldPtr*)(itsUpperFields[i]); delete (Vector*)(itsDataVectors[i]); break; case TpShort: delete (RecordFieldPtr*)(itsLowerFields[i]); delete (RecordFieldPtr*)(itsUpperFields[i]); delete (Vector*)(itsDataVectors[i]); break; case TpInt: delete (RecordFieldPtr*)(itsLowerFields[i]); delete (RecordFieldPtr*)(itsUpperFields[i]); delete (Vector*)(itsDataVectors[i]); break; case TpUInt: delete (RecordFieldPtr*)(itsLowerFields[i]); delete (RecordFieldPtr*)(itsUpperFields[i]); delete (Vector*)(itsDataVectors[i]); break; case TpInt64: delete (RecordFieldPtr*)(itsLowerFields[i]); delete (RecordFieldPtr*)(itsUpperFields[i]); delete (Vector*)(itsDataVectors[i]); break; case TpFloat: delete (RecordFieldPtr*)(itsLowerFields[i]); delete (RecordFieldPtr*)(itsUpperFields[i]); delete (Vector*)(itsDataVectors[i]); break; case TpDouble: delete (RecordFieldPtr*)(itsLowerFields[i]); delete (RecordFieldPtr*)(itsUpperFields[i]); delete (Vector*)(itsDataVectors[i]); break; case TpComplex: delete (RecordFieldPtr*)(itsLowerFields[i]); delete (RecordFieldPtr*)(itsUpperFields[i]); delete (Vector*)(itsDataVectors[i]); break; case TpDComplex: delete (RecordFieldPtr*)(itsLowerFields[i]); delete (RecordFieldPtr*)(itsUpperFields[i]); delete (Vector*)(itsDataVectors[i]); break; case TpString: delete (RecordFieldPtr*)(itsLowerFields[i]); delete (RecordFieldPtr*)(itsUpperFields[i]); delete (Vector*)(itsDataVectors[i]); break; default: throw (TableError ("ColumnsIndex: unknown data type")); } itsLowerFields[i] = 0; itsUpperFields[i] = 0; itsDataVectors[i] = 0; itsData[i] = 0; } delete itsLowerKeyPtr; delete itsUpperKeyPtr; itsLowerKeyPtr = 0; itsUpperKeyPtr = 0; } void ColumnsIndex::addColumnToDesc (RecordDesc& description, const TableColumn& column) { const ColumnDesc& columnDesc = column.columnDesc(); DataType dataType = columnDesc.dataType(); if (! columnDesc.isScalar()) { throw (TableError ("ColumnsIndex: column " + columnDesc.name() + " should be a scalar column")); } description.addField (columnDesc.name(), dataType); } void ColumnsIndex::create (const Table& table, const Vector& columnNames, Compare* compareFunction, Bool noSort) { itsTable = table; itsNrrow = itsTable.nrow(); itsCompare = (compareFunction == 0 ? compare : compareFunction); itsNoSort = noSort; // Loop through all column names. // Always add it to the RecordDesc. RecordDesc description; uInt nrfields = columnNames.nelements(); for (uInt i=0; i(0)); itsData.resize (nrfield, False, False); itsData.set (static_cast(0)); itsLowerFields.resize (nrfield, False, False); itsLowerFields.set (static_cast(0)); itsUpperFields.resize (nrfield, False, False); itsUpperFields.set (static_cast(0)); itsColumnChanged.resize (nrfield, False, False); itsColumnChanged.set (True); itsChanged = True; // Create the correct column object for each field. // Also create a RecordFieldPtr object for each Key. // This makes a fast data copy possible. for (uInt i=0; i(*itsLowerKeyPtr, i); itsUpperFields[i] = new RecordFieldPtr(*itsUpperKeyPtr, i); itsDataVectors[i] = new Vector; break; } case TpUChar: { itsLowerFields[i] = new RecordFieldPtr(*itsLowerKeyPtr, i); itsUpperFields[i] = new RecordFieldPtr(*itsUpperKeyPtr, i); itsDataVectors[i] = new Vector; break; } case TpShort: { itsLowerFields[i] = new RecordFieldPtr(*itsLowerKeyPtr, i); itsUpperFields[i] = new RecordFieldPtr(*itsUpperKeyPtr, i); itsDataVectors[i] = new Vector; break; } case TpInt: { itsLowerFields[i] = new RecordFieldPtr(*itsLowerKeyPtr, i); itsUpperFields[i] = new RecordFieldPtr(*itsUpperKeyPtr, i); itsDataVectors[i] = new Vector; break; } case TpUInt: { itsLowerFields[i] = new RecordFieldPtr(*itsLowerKeyPtr, i); itsUpperFields[i] = new RecordFieldPtr(*itsUpperKeyPtr, i); itsDataVectors[i] = new Vector; break; } case TpInt64: { itsLowerFields[i] = new RecordFieldPtr(*itsLowerKeyPtr, i); itsUpperFields[i] = new RecordFieldPtr(*itsUpperKeyPtr, i); itsDataVectors[i] = new Vector; break; } case TpFloat: { itsLowerFields[i] = new RecordFieldPtr(*itsLowerKeyPtr, i); itsUpperFields[i] = new RecordFieldPtr(*itsUpperKeyPtr, i); itsDataVectors[i] = new Vector; break; } case TpDouble: { itsLowerFields[i] = new RecordFieldPtr(*itsLowerKeyPtr, i); itsUpperFields[i] = new RecordFieldPtr(*itsUpperKeyPtr, i); itsDataVectors[i] = new Vector; break; } case TpComplex: { itsLowerFields[i] = new RecordFieldPtr(*itsLowerKeyPtr, i); itsUpperFields[i] = new RecordFieldPtr(*itsUpperKeyPtr, i); itsDataVectors[i] = new Vector; break; } case TpDComplex: { itsLowerFields[i] = new RecordFieldPtr(*itsLowerKeyPtr, i); itsUpperFields[i] = new RecordFieldPtr(*itsUpperKeyPtr, i); itsDataVectors[i] = new Vector; break; } case TpString: { itsLowerFields[i] = new RecordFieldPtr(*itsLowerKeyPtr, i); itsUpperFields[i] = new RecordFieldPtr(*itsUpperKeyPtr, i); itsDataVectors[i] = new Vector; break; } default: throw (TableError ("ColumnsIndex: unknown data type")); } } } void ColumnsIndex::readData() { // Acquire a lock if needed. TableLocker locker(itsTable, FileLocker::Read); rownr_t nrrow = itsTable.nrow(); if (nrrow != itsNrrow) { itsColumnChanged.set (True); itsChanged = True; itsNrrow = nrrow; } if (!itsChanged) { return; } Sort sort; Bool deleteIt; const RecordDesc& desc = itsLowerKeyPtr->description(); uInt nrfield = itsDataTypes.nelements(); for (uInt i=0; i* vecptr = (Vector*)itsDataVectors[i]; if (itsColumnChanged[i]) { ScalarColumn(itsTable, name).getColumn (*vecptr, True); } itsData[i] = vecptr->getStorage (deleteIt); sort.sortKey (itsData[i], desc.type(i)); break; } case TpUChar: { Vector* vecptr = (Vector*)itsDataVectors[i]; if (itsColumnChanged[i]) { ScalarColumn(itsTable, name).getColumn (*vecptr, True); } itsData[i] = vecptr->getStorage (deleteIt); sort.sortKey (itsData[i], desc.type(i)); break; } case TpShort: { Vector* vecptr = (Vector*)itsDataVectors[i]; if (itsColumnChanged[i]) { ScalarColumn(itsTable, name).getColumn (*vecptr, True); } itsData[i] = vecptr->getStorage (deleteIt); sort.sortKey (itsData[i], desc.type(i)); break; } case TpInt: { Vector* vecptr = (Vector*)itsDataVectors[i]; if (itsColumnChanged[i]) { ScalarColumn(itsTable, name).getColumn (*vecptr, True); } itsData[i] = vecptr->getStorage (deleteIt); sort.sortKey (itsData[i], desc.type(i)); break; } case TpUInt: { Vector* vecptr = (Vector*)itsDataVectors[i]; if (itsColumnChanged[i]) { ScalarColumn(itsTable, name).getColumn (*vecptr, True); } itsData[i] = vecptr->getStorage (deleteIt); sort.sortKey (itsData[i], desc.type(i)); break; } case TpInt64: { Vector* vecptr = (Vector*)itsDataVectors[i]; if (itsColumnChanged[i]) { ScalarColumn(itsTable, name).getColumn (*vecptr, True); } itsData[i] = vecptr->getStorage (deleteIt); sort.sortKey (itsData[i], desc.type(i)); break; } case TpFloat: { Vector* vecptr = (Vector*)itsDataVectors[i]; if (itsColumnChanged[i]) { ScalarColumn(itsTable, name).getColumn (*vecptr, True); } itsData[i] = vecptr->getStorage (deleteIt); sort.sortKey (itsData[i], desc.type(i)); break; } case TpDouble: { Vector* vecptr = (Vector*)itsDataVectors[i]; if (itsColumnChanged[i]) { ScalarColumn(itsTable, name).getColumn (*vecptr, True); } itsData[i] = vecptr->getStorage (deleteIt); sort.sortKey (itsData[i], desc.type(i)); break; } case TpComplex: { Vector* vecptr = (Vector*)itsDataVectors[i]; if (itsColumnChanged[i]) { ScalarColumn(itsTable, name).getColumn (*vecptr, True); } itsData[i] = vecptr->getStorage (deleteIt); sort.sortKey (itsData[i], desc.type(i)); break; } case TpDComplex: { Vector* vecptr = (Vector*)itsDataVectors[i]; if (itsColumnChanged[i]) { ScalarColumn(itsTable, name).getColumn (*vecptr, True); } itsData[i] = vecptr->getStorage (deleteIt); sort.sortKey (itsData[i], desc.type(i)); break; } case TpString: { Vector* vecptr = (Vector*)itsDataVectors[i]; if (itsColumnChanged[i]) { ScalarColumn(itsTable, name).getColumn (*vecptr, True); } itsData[i] = vecptr->getStorage (deleteIt); sort.sortKey (itsData[i], desc.type(i)); break; } default: throw (TableError ("ColumnsIndex: unknown data type")); } itsColumnChanged[i] = False; } // Sort the data if needed. // Otherwise fill the index vector with 0..n. itsDataIndex.resize (itsNrrow); if (!itsNoSort) { sort.sort (itsDataIndex, itsNrrow); } else { indgen (itsDataIndex); } // Determine all unique keys (itsUniqueIndex will contain the index of // each first unique entry in itsDataIndex). sort.unique (itsUniqueIndex, itsDataIndex); itsDataInx = itsDataIndex.getStorage (deleteIt); itsUniqueInx = itsUniqueIndex.getStorage (deleteIt); itsChanged = False; } rownr_t ColumnsIndex::bsearch (Bool& found, const Block& fieldPtrs) const { found = False; Int64 lower = 0; Int64 upper = itsUniqueIndex.nelements(); upper--; Int64 middle = 0; while (lower <= upper) { middle = (upper + lower) / 2; Int cmp = itsCompare (fieldPtrs, itsData, itsDataTypes, itsDataInx[itsUniqueInx[middle]]); if (cmp < 0) { upper = middle - 1; // go to left } else if (cmp > 0) { middle++; lower = middle; // go to right } else { found = True; break; } } return middle; } Int ColumnsIndex::compare (const Block& fieldPtrs, const Block& dataPtrs, const Block& dataTypes, rownr_t index) { uInt nfield = fieldPtrs.nelements(); for (uInt i=0; i*)(fieldPtrs[i])); const Bool right = ((const Bool*)(dataPtrs[i]))[index]; if (left < right) { return -1; } else if (left > right) { return 1; } break; } case TpUChar: { const uChar left = *(*(RecordFieldPtr*)(fieldPtrs[i])); const uChar right = ((const uChar*)(dataPtrs[i]))[index]; if (left < right) { return -1; } else if (left > right) { return 1; } break; } case TpShort: { const Short left = *(*(RecordFieldPtr*)(fieldPtrs[i])); const Short right = ((const Short*)(dataPtrs[i]))[index]; if (left < right) { return -1; } else if (left > right) { return 1; } break; } case TpInt: { const Int left = *(*(RecordFieldPtr*)(fieldPtrs[i])); const Int right = ((const Int*)(dataPtrs[i]))[index]; if (left < right) { return -1; } else if (left > right) { return 1; } break; } case TpUInt: { const uInt left = *(*(RecordFieldPtr*)(fieldPtrs[i])); const uInt right = ((const uInt*)(dataPtrs[i]))[index]; if (left < right) { return -1; } else if (left > right) { return 1; } break; } case TpInt64: { const Int64 left = *(*(RecordFieldPtr*)(fieldPtrs[i])); const Int64 right = ((const Int64*)(dataPtrs[i]))[index]; if (left < right) { return -1; } else if (left > right) { return 1; } break; } case TpFloat: { const Float left = *(*(RecordFieldPtr*)(fieldPtrs[i])); const Float right = ((const Float*)(dataPtrs[i]))[index]; if (left < right) { return -1; } else if (left > right) { return 1; } break; } case TpDouble: { const Double left = *(*(RecordFieldPtr*)(fieldPtrs[i])); const Double right = ((const Double*)(dataPtrs[i]))[index]; if (left < right) { return -1; } else if (left > right) { return 1; } break; } case TpComplex: { const Complex& left = *(*(RecordFieldPtr*)(fieldPtrs[i])); const Complex& right = ((const Complex*)(dataPtrs[i]))[index]; if (left < right) { return -1; } else if (left > right) { return 1; } break; } case TpDComplex: { const DComplex& left = *(*(RecordFieldPtr*)(fieldPtrs[i])); const DComplex& right = ((const DComplex*)(dataPtrs[i]))[index]; if (left < right) { return -1; } else if (left > right) { return 1; } break; } case TpString: { const String& left = *(*(RecordFieldPtr*)(fieldPtrs[i])); const String& right = ((const String*)(dataPtrs[i]))[index]; if (left < right) { return -1; } else if (left > right) { return 1; } break; } default: throw (TableError ("ColumnsIndex: unknown data type")); } } return 0; } rownr_t ColumnsIndex::getRowNumber (Bool& found, const Record& key) { copyKey (itsLowerFields, key); return getRowNumber (found); } rownr_t ColumnsIndex::getRowNumber (Bool& found) { if (!isUnique()) { throw (TableError ("ColumnsIndex::getRowNumber only possible " "when the index keys are unique")); } // Read the data (if needed). readData(); rownr_t inx = bsearch (found, itsLowerFields); if (found) { inx = itsDataInx[inx]; } return inx; } RowNumbers ColumnsIndex::getRowNumbers (const Record& key) { copyKey (itsLowerFields, key); return getRowNumbers(); } RowNumbers ColumnsIndex::getRowNumbers() { // Read the data (if needed). readData(); Bool found; rownr_t inx = bsearch (found, itsLowerFields); RowNumbers rows; if (found) { fillRowNumbers (rows, inx, inx+1); } return rows; } RowNumbers ColumnsIndex::getRowNumbers (const Record& lowerKey, const Record& upperKey, Bool lowerInclusive, Bool upperInclusive) { copyKey (itsLowerFields, lowerKey); copyKey (itsUpperFields, upperKey); return getRowNumbers (lowerInclusive, upperInclusive); } RowNumbers ColumnsIndex::getRowNumbers (Bool lowerInclusive, Bool upperInclusive) { // Read the data (if needed). readData(); Bool found; // Try to find the lower key. If not found, bsearch is giving the // index of the next higher key. // So increment the start index if found and is not to be included. rownr_t start = bsearch (found, itsLowerFields); if (found && !lowerInclusive) { start++; } // Try to find the upper key. // Increment the end index such that it is not inclusive // (thus increment if the found end index is to be included). rownr_t end = bsearch (found, itsUpperFields); if (found && upperInclusive) { end++; } RowNumbers rows; if (start < end) { fillRowNumbers (rows, start, end); } return rows; } void ColumnsIndex::fillRowNumbers (Vector& rows, rownr_t start, rownr_t end) const { start = itsUniqueInx[start]; if (end < itsUniqueIndex.nelements()) { end = itsUniqueInx[end]; } else { end = itsDataIndex.nelements(); } rownr_t nr = end-start; rows.resize (nr); Bool deleteIt; rownr_t* rowStorage = rows.getStorage (deleteIt); objcopy (rowStorage, itsDataInx+start, nr); rows.putStorage (rowStorage, deleteIt); } void ColumnsIndex::setChanged() { itsColumnChanged.set (True); itsChanged = True; } void ColumnsIndex::setChanged (const String& columnName) { const RecordDesc& desc = itsLowerKeyPtr->description(); uInt nrfield = itsColumnChanged.nelements(); for (uInt i=0; i*)(fieldPtr), key); break; case TpUChar: copyKeyField(*(RecordFieldPtr*)(fieldPtr), key); break; case TpShort: copyKeyField(*(RecordFieldPtr*)(fieldPtr), key); break; case TpInt: copyKeyField(*(RecordFieldPtr*)(fieldPtr), key); break; case TpUInt: copyKeyField(*(RecordFieldPtr*)(fieldPtr), key); break; case TpInt64: copyKeyField(*(RecordFieldPtr*)(fieldPtr), key); break; case TpFloat: copyKeyField(*(RecordFieldPtr*)(fieldPtr), key); break; case TpDouble: copyKeyField(*(RecordFieldPtr*)(fieldPtr), key); break; case TpComplex: copyKeyField(*(RecordFieldPtr*)(fieldPtr), key); break; case TpDComplex: copyKeyField(*(RecordFieldPtr*)(fieldPtr), key); break; case TpString: copyKeyField(*(RecordFieldPtr*)(fieldPtr), key); break; default: throw (TableError ("ColumnsIndex: unknown data type")); } } void ColumnsIndex::copyKey (Block fields, const Record& key) { for (uInt i=0; i #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class String; class TableColumn; template class RecordFieldPtr; // // Index to one or more columns in a table. // // // // // //
      • Table //
      • Record //
      • RecordFieldPtr // // // This class makes it possible to use transient indices on top // of tables in order to speed up the process of finding rows // based on a given key or key range. // When constructing a ColumnsIndex object, one has to define // which columns form the key for this index on the given // table object. // Only scalar columns are supported. The data in the given columns // will be read, sorted (if needed), and stored in memory. // When looking up a key or key range, the class will use a fast binary // search on the data held in memory. //

        // The ColumnsIndex object contains a // Record object which can be used // to define the key to be looked up. The record contains a field for // each column in the index (with the same name and data type). // The fastest way to fill the key is by creating a // RecordFieldPtr object for // each field in the record (see the example) and fill it as needed. // However, one can also use the Record::define function, // but that is slower. //
        // A second record is available to define the upper key // when a key range has to be looked up. The keys can be accessed // using the various accessKey functions. //

        // When a key is defined, the getRowNumbers function can be // used to find the table rows containing the given key (range). // Function getRowNumber can be used if all keys in the index // are unique (which can be tested with the isUnique function). //

        // Instead of using the internal records holding the keys, one can also // pass its own Record object to getRowNumbers. // However, it will be slower. //

        // When constructing the object, the user can supply his own compare // function. The default compare function compares each field of the // key in the normal way. A user's compare function makes it possible // to compare in a special way. E.g. one could use near instead of == // on floating point fields. Another example (which is shown in one // of the examples below) makes it possible to find a key in an // index consisting of a time and width. //

        // After an index is created, it is possible to change the data // in the underlying columns. However, the ColumnsIndex can // not detect if the column data have changed. It can only detect if // the number of rows has changed. If the column data have changed, // the user has to use the setChanged function to indicate // that all columns or a particular column has changed. //
        If data have changed, the entire index will be recreated by // rereading and optionally resorting the data. This will be deferred // until the next key lookup. // // // Suppose one has an antenna table with key ANTENNA. // // // Open the table and make an index for column ANTENNA. // Table tab("antenna.tab") // ColumnsIndex colInx(tab, "ANTENNA"); // // Make a RecordFieldPtr for the ANTENNA field in the index key record. // // Its data type has to match the data type of the column. // RecordFieldPtr antFld(colInx.accessKey(), "ANTENNA"); // // Now loop in some way and find the row for the antenna // // involved in that loop. // Bool found; // while (...) { // // Fill the key field and get the row number. // // ANTENNA is a unique key, so only one row number matches. // // Otherwise function getRowNumbers had to be used. // *antFld = antenna; // rownr_t antRownr = colInx.getRowNumber (found); // if (!found) { // cout << "Antenna " << antenna << " is unknown" << endl; // } else { // // antRownr can now be used to get data from that row in // // the antenna table. // } // } // // // The following example shows how multiple keys can be used and how // a search on a range can be done. // // Table tab("sometable") // // Note that TIME is the main key. // // Also note that stringToVector (in ArrayUtil.h) is a handy // // way to convert a String to a Vector. // ColumnsIndex colInx(tab, stringToVector("TIME,ANTENNA")); // // Make a RecordFieldPtr for the fields in lower and upper key records. // RecordFieldPtr timeLow(colInx.accessLowerKey(), "TIME"); // RecordFieldPtr antLow(colInx.accessLowerKey(), "ANTENNA"); // RecordFieldPtr timeUpp(colInx.accessUpperKey(), "TIME"); // RecordFieldPtr antUpp(colInx.accessUpperKey(), "ANTENNA"); // while (...) { // // Fill the key fields. // *timeLow = ...; // *antLow = ...; // *timeUpp = ...; // *antUpp = ...; // // Find the row numbers for keys between low and upp (inclusive). // RowNumbers rows = colInx.getRowNumbers (True, True); // } // // // The following example shows how a specific compare function // could look like. A function like this will actually be used in the // calibration software. //
        // The table for which the index is built, has rows with the TIME as its key. // However, each row is valid for a given interval, where TIME gives // the middle of the interval and WIDTH the length of the interval. // This means that the compare function has to test whether the key // is part of the interval. // // Int myCompare (const Block& fieldPtrs, // const Block& dataPtrs, // const Block& dataTypes, // rownr_t index) // { // // Assert (for performance only in debug mode) that the correct // // fields are used. // DebugAssert (dataTypes.nelements() == 2, AipsError); // DebugAssert (dataTypes[0] == TpDouble && dataTypes[1] == TpDouble, // AipsError); // // Now get the key to be looked up // // (an awfully looking cast has to be used). // const Double key = *(*(const RecordFieldPtr*)(fieldPtrs[0])); // // Get the time and width of the entry to be compared. // const Double time = ((const Double*)(dataPtrs[0]))[index]; // const Double width = ((const Double*)(dataPtrs[1]))[index]; // const Double start = time - width/2; // const Double end = time + width/2; // // Test if the key is before, after, or in the interval // // (representing less, greater, equal). // if (key < start) { // return -1; // } else if (key > end) { // return 1; // } // return 0; // } // // // Now use this compare function in an actual index. // Table tab("sometable") // ColumnsIndex colInx(tab, stringToVector("TIME,WIDTH"), myCompare); // // Make a RecordFieldPtr for the TIME field in the key record. // // Note that although the WIDTH is part of the index, it is // // not an actual key. So it does not need to be filled in. // RecordFieldPtr time(colInx.accessLowerKey(), "TIME"); // Bool found; // while (...) { // // Fill the key field. // *time = ...; // // Find the row number for this time. // rownr_t rownr = colInx.getRowNumber (found); // } // //
        // // The calibration software needs to lookup keys in calibration tables // very frequently. This class makes that process much easier and faster. // class ColumnsIndex { public: // Define the signature of a comparison function. // The first block contains pointers to RecordFieldPtr // objects holding the key to be looked up. // The second block contains pointers to the column data. // The index argument gives the index in the column data. // The third block contains data types of those blocks (TpBool, etc.). // The function should return -1 if key is less than data, // 0 if equal, 1 if greater. //
        An example above shows how a compare function can be used. typedef Int Compare (const Block& fieldPtrs, const Block& dataPtrs, const Block& dataTypes, rownr_t index); // Create an index on the given table for the given column. // The column has to be a scalar column. // If noSort==True, the table is already in order of that // column and the sort step will not be done. // The default compare function is provided by this class. It simply // compares each field in the key. ColumnsIndex (const Table&, const String& columnName, Compare* compareFunction = 0, Bool noSort = False); // Create an index on the given table for the given columns, thus // the key is formed by multiple columns. // The columns have to be scalar columns. // If noSort==True, the table is already in order of those // columns and the sort step will not be done. // The default compare function is provided by this class. It simply // compares each field in the key. ColumnsIndex (const Table&, const Vector& columnNames, Compare* compareFunction = 0, Bool noSort = False); // Copy constructor (copy semantics). ColumnsIndex (const ColumnsIndex& that); ~ColumnsIndex(); // Assignment (copy semantics). ColumnsIndex& operator= (const ColumnsIndex& that); // Are all keys in the index unique? Bool isUnique() const; // Return the names of the columns forming the index. Vector columnNames() const; // Get the table for which this index is created. const Table& table() const; // Something has changed in the table, so the index has to be recreated. // The 2nd version indicates that a specific column has changed, // so only that column is reread. If that column is not part of the // index, nothing will be done. //
        Note that the class itself is keeping track if the number of // rows in the table changes. // void setChanged(); void setChanged (const String& columnName); // // Access the key values. // These functions allow you to create RecordFieldPtr objects // for each field in the key. In this way you can quickly fill in // the key. //
        The records have a fixed type, so you cannot add or delete fields. // Record& accessKey(); Record& accessLowerKey(); Record& accessUpperKey(); // // Find the row number matching the key. All keys have to be unique, // otherwise an exception is thrown. // If no match is found, found is set to False. // The 2nd version makes it possible to pass in your own Record // instead of using the internal record via the accessKey // functions. Note that the given Record will be copied to the internal // record, thus overwrites it. // rownr_t getRowNumber (Bool& found); rownr_t getRowNumber (Bool& found, const Record& key); // // Find the row numbers matching the key. It should be used instead // of getRowNumber if the same key can exist multiple times. // The 2nd version makes it possible to pass in your own Record // instead of using the internal record via the accessKey // functions. Note that the given Record will be copied to the internal // record, thus overwrites it. // RowNumbers getRowNumbers(); RowNumbers getRowNumbers (const Record& key); // // Find the row numbers matching the key range. The boolean arguments // tell if the lower and upper key are part of the range. // The 2nd version makes it possible to pass in your own Records // instead of using the internal records via the // accessLower/UpperKey functions. // Note that the given Records will be copied to the internal // records, thus overwrite them. // RowNumbers getRowNumbers (Bool lowerInclusive, Bool upperInclusive); RowNumbers getRowNumbers (const Record& lower, const Record& upper, Bool lowerInclusive, Bool upperInclusive); // // Fill the internal key field from the corresponding external key. // The data type may differ. static void copyKeyField (void* field, int dtype, const Record& key); protected: // Copy that object to this. void copy (const ColumnsIndex& that); // Delete all data in the object. void deleteObjects(); // Add a column to the record description for the keys. void addColumnToDesc (RecordDesc& description, const TableColumn& column); // Create the various members in the object. void create (const Table& table, const Vector& columnNames, Compare* compareFunction, Bool noSort); // Make the various internal RecordFieldPtr objects. void makeObjects (const RecordDesc& description); // Read the data of the columns forming the index, sort them and // form the index. void readData(); // Do a binary search on itsUniqueIndex for the key in // fieldPtrs. // If the key is found, found is set to True and the index // in itsUniqueIndex is returned. // If not found, found is set to False and the index // of the next higher key is returned. rownr_t bsearch (Bool& found, const Block& fieldPtrs) const; // Compare the key in fieldPtrs with the given index entry. // -1 is returned when less, 0 when equal, 1 when greater. static Int compare (const Block& fieldPtrs, const Block& dataPtrs, const Block& dataTypes, rownr_t index); // Fill the row numbers vector for the given start till end in the // itsUniqueIndex vector (end is not inclusive). void fillRowNumbers (Vector& rows, rownr_t start, rownr_t end) const; private: // Fill the internal key fields from the corresponding external key. void copyKey (Block fields, const Record& key); // Fill the internal key field from the corresponding external key. // The data type may differ. template static void copyKeyField (RecordFieldPtr& field, const Record& key) { key.get (field.name(), *field); } Table itsTable; rownr_t itsNrrow; Record* itsLowerKeyPtr; Record* itsUpperKeyPtr; Block itsDataTypes; Block itsDataVectors; Block itsData; //# pointer to data in itsDataVectors //# The following 2 blocks are actually blocks of RecordFieldPtr*. //# They are used for fast access to the records. Block itsLowerFields; Block itsUpperFields; Block itsColumnChanged; Bool itsChanged; Bool itsNoSort; //# True = sort is not needed Compare* itsCompare; //# Compare function Vector itsDataIndex; //# Row numbers of all keys //# Indices in itsDataIndex for each unique key Vector itsUniqueIndex; rownr_t* itsDataInx; //# pointer to data in itsDataIndex rownr_t* itsUniqueInx; //# pointer to data in itsUniqueIndex }; inline Bool ColumnsIndex::isUnique() const { return (itsDataIndex.nelements() == itsUniqueIndex.nelements()); } inline const Table& ColumnsIndex::table() const { return itsTable; } inline Record& ColumnsIndex::accessKey() { return *itsLowerKeyPtr; } inline Record& ColumnsIndex::accessLowerKey() { return *itsLowerKeyPtr; } inline Record& ColumnsIndex::accessUpperKey() { return *itsUpperKeyPtr; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/ColumnsIndexArray.cc000066400000000000000000000461131476623553700217330ustar00rootroot00000000000000//# ColumnsIndexArray.cc: Index to a table //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ColumnsIndexArray::ColumnsIndexArray (const Table& table, const String& columnName) : itsLowerKeyPtr (0), itsUpperKeyPtr (0) { itsTable = table; itsNrrow = itsTable.nrow(); // Add column to the RecordDesc. RecordDesc description; addColumnToDesc (description, TableColumn (itsTable, columnName)); makeObjects (description); readData(); } ColumnsIndexArray::ColumnsIndexArray (const ColumnsIndexArray& that) : itsLowerKeyPtr (0), itsUpperKeyPtr (0) { copy (that); } ColumnsIndexArray::~ColumnsIndexArray() { deleteObjects(); } ColumnsIndexArray& ColumnsIndexArray::operator= (const ColumnsIndexArray& that) { copy (that); return *this; } void ColumnsIndexArray::copy (const ColumnsIndexArray& that) { if (this != &that) { deleteObjects(); itsTable = that.itsTable; itsNrrow = itsTable.nrow(); makeObjects (that.itsLowerKeyPtr->description()); } } const String& ColumnsIndexArray::columnName() const { const RecordDesc& desc = itsLowerKeyPtr->description(); return desc.name(0); } void ColumnsIndexArray::deleteObjects() { switch (itsDataType) { case TpUChar: delete (RecordFieldPtr*)(itsLowerField); delete (RecordFieldPtr*)(itsUpperField); delete (Vector*)(itsDataVector); break; case TpShort: delete (RecordFieldPtr*)(itsLowerField); delete (RecordFieldPtr*)(itsUpperField); delete (Vector*)(itsDataVector); break; case TpInt: delete (RecordFieldPtr*)(itsLowerField); delete (RecordFieldPtr*)(itsUpperField); delete (Vector*)(itsDataVector); break; case TpUInt: delete (RecordFieldPtr*)(itsLowerField); delete (RecordFieldPtr*)(itsUpperField); delete (Vector*)(itsDataVector); break; case TpInt64: delete (RecordFieldPtr*)(itsLowerField); delete (RecordFieldPtr*)(itsUpperField); delete (Vector*)(itsDataVector); break; case TpString: delete (RecordFieldPtr*)(itsLowerField); delete (RecordFieldPtr*)(itsUpperField); delete (Vector*)(itsDataVector); break; default: throw (TableError ("ColumnsIndexArray: unsupported data type")); } itsLowerField = 0; itsUpperField = 0; itsDataVector = 0; itsData = 0; delete itsLowerKeyPtr; delete itsUpperKeyPtr; itsLowerKeyPtr = 0; itsUpperKeyPtr = 0; } void ColumnsIndexArray::addColumnToDesc (RecordDesc& description, const TableColumn& column) { const ColumnDesc& columnDesc = column.columnDesc(); DataType dataType = columnDesc.dataType(); itsDataType = dataType; if (! columnDesc.isArray()) { throw (TableError ("ColumnsIndexArray: column " + columnDesc.name() + " should be an array column")); } description.addField (columnDesc.name(), dataType); } void ColumnsIndexArray::makeObjects (const RecordDesc& description) { // Create the Record from the description. itsLowerKeyPtr = new Record (description); itsUpperKeyPtr = new Record (description); // Initialize the column and field block. itsDataVector = 0; itsData = 0; itsLowerField = 0; itsUpperField = 0; itsChanged = True; // Create the correct column object for each field. // Also create a RecordFieldPtr object for each Key. // This makes a fast data copy possible. itsDataType = description.type(0); switch (itsDataType) { case TpUChar: { itsLowerField = new RecordFieldPtr(*itsLowerKeyPtr, 0); itsUpperField = new RecordFieldPtr(*itsUpperKeyPtr, 0); itsDataVector = new Vector; break; } case TpShort: { itsLowerField = new RecordFieldPtr(*itsLowerKeyPtr, 0); itsUpperField = new RecordFieldPtr(*itsUpperKeyPtr, 0); itsDataVector = new Vector; break; } case TpInt: { itsLowerField = new RecordFieldPtr(*itsLowerKeyPtr, 0); itsUpperField = new RecordFieldPtr(*itsUpperKeyPtr, 0); itsDataVector = new Vector; break; } case TpUInt: { itsLowerField = new RecordFieldPtr(*itsLowerKeyPtr, 0); itsUpperField = new RecordFieldPtr(*itsUpperKeyPtr, 0); itsDataVector = new Vector; break; } case TpInt64: { itsLowerField = new RecordFieldPtr(*itsLowerKeyPtr, 0); itsUpperField = new RecordFieldPtr(*itsUpperKeyPtr, 0); itsDataVector = new Vector; break; } case TpString: { itsLowerField = new RecordFieldPtr(*itsLowerKeyPtr, 0); itsUpperField = new RecordFieldPtr(*itsUpperKeyPtr, 0); itsDataVector = new Vector; break; } default: throw (TableError ("ColumnsIndexArray: unsupported data type")); } } void ColumnsIndexArray::readData() { // Acquire a lock if needed. TableLocker locker(itsTable, FileLocker::Read); rownr_t nrrow = itsTable.nrow(); if (nrrow != itsNrrow) { itsChanged = True; itsNrrow = nrrow; } if (!itsChanged) { return; } Sort sort; Bool deleteIt; const RecordDesc& desc = itsLowerKeyPtr->description(); const String& name = desc.name(0); switch (itsDataType) { case TpUChar: { Vector* vecptr = (Vector*)itsDataVector; getArray (*vecptr, name); itsData = vecptr->getStorage (deleteIt); sort.sortKey (itsData, desc.type(0)); break; } case TpShort: { Vector* vecptr = (Vector*)itsDataVector; getArray (*vecptr, name); itsData = vecptr->getStorage (deleteIt); sort.sortKey (itsData, desc.type(0)); break; } case TpInt: { Vector* vecptr = (Vector*)itsDataVector; getArray (*vecptr, name); itsData = vecptr->getStorage (deleteIt); sort.sortKey (itsData, desc.type(0)); break; } case TpUInt: { Vector* vecptr = (Vector*)itsDataVector; getArray (*vecptr, name); itsData = vecptr->getStorage (deleteIt); sort.sortKey (itsData, desc.type(0)); break; } case TpInt64: { Vector* vecptr = (Vector*)itsDataVector; getArray (*vecptr, name); itsData = vecptr->getStorage (deleteIt); sort.sortKey (itsData, desc.type(0)); break; } case TpString: { Vector* vecptr = (Vector*)itsDataVector; getArray (*vecptr, name); itsData = vecptr->getStorage (deleteIt); sort.sortKey (itsData, desc.type(0)); break; } default: throw (TableError ("ColumnsIndexArray: unsupported data type")); } // Sort the data if needed. // Otherwise fill the index vector with 0..n. sort.sort (itsDataIndex, itsRownrs.nelements()); // Determine all unique keys (itsUniqueIndex will contain the index of // each first unique entry in itsDataIndex). sort.unique (itsUniqueIndex, itsDataIndex); itsDataInx = itsDataIndex.getStorage (deleteIt); itsUniqueInx = itsUniqueIndex.getStorage (deleteIt); itsChanged = False; } rownr_t ColumnsIndexArray::bsearch (Bool& found, void* fieldPtr) const { found = False; Int64 lower = 0; Int64 upper = itsUniqueIndex.nelements() - 1; Int64 middle = 0; while (lower <= upper) { middle = (upper + lower) / 2; Int cmp = compare (fieldPtr, itsData, itsDataType, itsDataInx[itsUniqueInx[middle]]); if (cmp < 0) { upper = middle - 1; // go to left } else if (cmp > 0) { middle++; lower = middle; // go to right } else { found = True; break; } } return middle; } Int ColumnsIndexArray::compare (void* fieldPtr, void* dataPtr, Int dataType, rownr_t index) { switch (dataType) { case TpUChar: { const uChar left = *(*(RecordFieldPtr*)(fieldPtr)); const uChar right = ((const uChar*)(dataPtr))[index]; if (left < right) { return -1; } else if (left > right) { return 1; } break; } case TpShort: { const Short left = *(*(RecordFieldPtr*)(fieldPtr)); const Short right = ((const Short*)(dataPtr))[index]; if (left < right) { return -1; } else if (left > right) { return 1; } break; } case TpInt: { const Int left = *(*(RecordFieldPtr*)(fieldPtr)); const Int right = ((const Int*)(dataPtr))[index]; if (left < right) { return -1; } else if (left > right) { return 1; } break; } case TpUInt: { const uInt left = *(*(RecordFieldPtr*)(fieldPtr)); const uInt right = ((const uInt*)(dataPtr))[index]; if (left < right) { return -1; } else if (left > right) { return 1; } break; } case TpInt64: { const Int64 left = *(*(RecordFieldPtr*)(fieldPtr)); const Int64 right = ((const Int64*)(dataPtr))[index]; if (left < right) { return -1; } else if (left > right) { return 1; } break; } case TpString: { const String& left = *(*(RecordFieldPtr*)(fieldPtr)); const String& right = ((const String*)(dataPtr))[index]; if (left < right) { return -1; } else if (left > right) { return 1; } break; } default: throw (TableError ("ColumnsIndexArray: unsupported data type")); } return 0; } rownr_t ColumnsIndexArray::getRowNumber (Bool& found, const Record& key) { ColumnsIndex::copyKeyField (itsLowerField, itsDataType, key); return getRowNumber (found); } rownr_t ColumnsIndexArray::getRowNumber (Bool& found) { if (!isUnique()) { throw (TableError ("ColumnsIndexArray::getRowNumber only possible " "when the index keys are unique")); } // Read the data (if needed). readData(); rownr_t inx = bsearch (found, itsLowerField); if (found) { inx = itsRownrs[itsDataInx[inx]]; } return inx; } RowNumbers ColumnsIndexArray::getRowNumbers (const Record& key, Bool unique) { ColumnsIndex::copyKeyField (itsLowerField, itsDataType, key); return getRowNumbers (unique); } RowNumbers ColumnsIndexArray::getRowNumbers (Bool unique) { // Read the data (if needed). readData(); Bool found; rownr_t inx = bsearch (found, itsLowerField); RowNumbers rows; if (found) { fillRowNumbers (rows, inx, inx+1, unique); } return rows; } RowNumbers ColumnsIndexArray::getRowNumbers (const Record& lowerKey, const Record& upperKey, Bool lowerInclusive, Bool upperInclusive, Bool unique) { ColumnsIndex::copyKeyField (itsLowerField, itsDataType, lowerKey); ColumnsIndex::copyKeyField (itsUpperField, itsDataType, upperKey); return getRowNumbers (lowerInclusive, upperInclusive, unique); } RowNumbers ColumnsIndexArray::getRowNumbers (Bool lowerInclusive, Bool upperInclusive, Bool unique) { // Read the data (if needed). readData(); Bool found; // Try to find the lower key. If not found, bsearch is giving the // index of the next higher key. // So increment the start index if found and is not to be included. rownr_t start = bsearch (found, itsLowerField); if (found && !lowerInclusive) { start++; } // Try to find the upper key. // Increment the end index such that it is not inclusive // (thus increment if the found end index is to be included). rownr_t end = bsearch (found, itsUpperField); if (found && upperInclusive) { end++; } RowNumbers rows; if (start < end) { fillRowNumbers (rows, start, end, unique); } return rows; } void ColumnsIndexArray::fillRowNumbers (Vector& rows, rownr_t start, rownr_t end, Bool unique) const { start = itsUniqueInx[start]; if (end < itsUniqueIndex.nelements()) { end = itsUniqueInx[end]; } else { end = itsDataIndex.nelements(); } rownr_t nr = end-start; rows.resize (nr); Bool deleteIt; rownr_t* rowStorage = rows.getStorage (deleteIt); for (rownr_t i=0; i::sort (rows, Sort::Ascending, Sort::NoDuplicates); rows.resize (nrrow, True); } } void ColumnsIndexArray::setChanged() { itsChanged = True; } void ColumnsIndexArray::setChanged (const String& columnName) { const RecordDesc& desc = itsLowerKeyPtr->description(); if (desc.name(0) == columnName) { itsChanged = True; } } void ColumnsIndexArray::getArray (Vector& result, const String& name) { ArrayColumn arrCol (itsTable, name); rownr_t nrrow = arrCol.nrow(); if (nrrow > 0) { Block nrel(nrrow, rownr_t(0)); Array arr = arrCol(0); rownr_t npts = arr.nelements(); nrel[0] = npts; result.resize (nrrow*npts); objmove (result.data(), arr.data(), npts); for (rownr_t i=1; i arr = arrCol(i); rownr_t n = arr.nelements(); nrel[i] = n; if (npts+n > result.nelements()) { result.resize (npts+n, True); } objmove (result.data()+npts, arr.data(), n); npts += n; } } result.resize (npts, True); fillRownrs (npts, nrel); } } void ColumnsIndexArray::getArray (Vector& result, const String& name) { ArrayColumn arrCol (itsTable, name); rownr_t nrrow = arrCol.nrow(); if (nrrow > 0) { Block nrel(nrrow, rownr_t(0)); Array arr = arrCol(0); rownr_t npts = arr.nelements(); nrel[0] = npts; result.resize (nrrow*npts); objmove (result.data(), arr.data(), npts); for (rownr_t i=1; i arr = arrCol(i); rownr_t n = arr.nelements(); nrel[i] = n; if (npts+n > result.nelements()) { result.resize (npts+n, True); } objmove (result.data()+npts, arr.data(), n); npts += n; } } result.resize (npts, True); fillRownrs (npts, nrel); } } void ColumnsIndexArray::getArray (Vector& result, const String& name) { ArrayColumn arrCol (itsTable, name); rownr_t nrrow = arrCol.nrow(); if (nrrow > 0) { Block nrel(nrrow, rownr_t(0)); Array arr = arrCol(0); rownr_t npts = arr.nelements(); nrel[0] = npts; result.resize (nrrow*npts); objmove (result.data(), arr.data(), npts); for (rownr_t i=1; i arr = arrCol(i); rownr_t n = arr.nelements(); nrel[i] = n; if (npts+n > result.nelements()) { result.resize (npts+n, True); } objmove (result.data()+npts, arr.data(), n); npts += n; } } result.resize (npts, True); fillRownrs (npts, nrel); } } void ColumnsIndexArray::getArray (Vector& result, const String& name) { ArrayColumn arrCol (itsTable, name); rownr_t nrrow = arrCol.nrow(); if (nrrow > 0) { Block nrel(nrrow, rownr_t(0)); Array arr = arrCol(0); rownr_t npts = arr.nelements(); nrel[0] = npts; result.resize (nrrow*npts); objmove (result.data(), arr.data(), npts); for (rownr_t i=1; i arr = arrCol(i); rownr_t n = arr.nelements(); nrel[i] = n; if (npts+n > result.nelements()) { result.resize (npts+n, True); } objmove (result.data()+npts, arr.data(), n); npts += n; } } result.resize (npts, True); fillRownrs (npts, nrel); } } void ColumnsIndexArray::getArray (Vector& result, const String& name) { ArrayColumn arrCol (itsTable, name); rownr_t nrrow = arrCol.nrow(); if (nrrow > 0) { Block nrel(nrrow, rownr_t(0)); Array arr = arrCol(0); rownr_t npts = arr.nelements(); nrel[0] = npts; result.resize (nrrow*npts); objmove (result.data(), arr.data(), npts); for (rownr_t i=1; i arr = arrCol(i); rownr_t n = arr.nelements(); nrel[i] = n; if (npts+n > result.nelements()) { result.resize (npts+n, True); } objmove (result.data()+npts, arr.data(), n); npts += n; } } result.resize (npts, True); fillRownrs (npts, nrel); } } void ColumnsIndexArray::getArray (Vector& result, const String& name) { ArrayColumn arrCol (itsTable, name); rownr_t nrrow = arrCol.nrow(); if (nrrow > 0) { Block nrel(nrrow, rownr_t(0)); Array arr = arrCol(0); rownr_t npts = arr.nelements(); nrel[0] = npts; result.resize (nrrow*npts); objmove (result.data(), arr.data(), npts); for (rownr_t i=1; i arr = arrCol(i); rownr_t n = arr.nelements(); nrel[i] = n; if (npts+n > result.nelements()) { result.resize (npts+n, True); } objmove (result.data()+npts, arr.data(), n); npts += n; } } result.resize (npts, True); fillRownrs (npts, nrel); } } void ColumnsIndexArray::fillRownrs (rownr_t npts, const Block& nrel) { itsRownrs.resize (npts); rownr_t* data = itsRownrs.storage(); for (rownr_t i=0; i #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class String; class TableColumn; //

        // Index to an array column in a table. // // // // // //
      • Table //
      • Record //
      • RecordFieldPtr // // // This class makes it possible to use transient indices on top // of an array column in a table in order to speed up the process of // finding rows based on a given key or key range. // It is similar to class ColumnsIndex // which is meant for one or more scalar columns. //

        // When constructing a ColumnsIndexArray object, one has to define // which column forms the key for this index on the given // table object. // Not every data type is supported; only uChar, Short, Int, uInt, Int64 and // String array columns are supported. // The column can contain arrays of any shape and it can also contain // empty cells. The class will probably mostly be used for vectors, as // they seem to be the most logical way to hold multiple keys. //
        The data in the given column will be read, sorted, // and stored in memory. When looking up a key or key range, the class // will use a fast binary search on the data held in memory. //

        // The ColumnsIndexArray object contains a // Record object which can be used // to define the key to be looked up. The record contains a field for // the column in the index (with the same name and data type). // The fastest way to fill the key is by creating a // RecordFieldPtr object for // the field in the record (see the example) and fill it as needed. // However, one can also use the Record::define function, // but that is slower. //
        // A second record is available to define the upper key // in case a key range has to be looked up. The keys can be accessed // using the various accessKey functions. //

        // When a key is defined, the getRowNumbers function can be // used to find the table rows containing the given key (range). // Function getRowNumber can be used to lookup a single key // if all keys in the index are unique (which can be tested with the // isUnique function). //

        // Instead of using the internal records holding the keys, one can also // pass its own Record object to getRowNumbers. // However, it will be slower. //

        // After an index is created, it is possible to change the data // in the underlying columns. However, the ColumnsIndexArray can // not detect if the column data have changed. It can only detect if // the number of rows has changed. If the column data have changed, // the user has to use the setChanged function to indicate // that the column has changed. //
        If data have changed, the entire index will be recreated by // rereading and resorting the data. This will be deferred // until the next key lookup. // // // Suppose one has table with a column NAME containing vectors. // // // Open the table and make an index for the column. // Table tab("my.tab") // ColumnsIndexArray colInx(tab, "NAME"); // // Make a RecordFieldPtr for the NAME field in the index key record. // // Its data type has to match the data type of the column. // RecordFieldPtr nameFld(colInx.accessKey(), "NAME"); // // Find the row for a given name. // Bool found; // // Fill the key field and get the row number. // // NAME is a unique key, so only one row number matches. // // Otherwise function getRowNumbers had to be used. // *nameFld = "MYNAME"; // rownr_t rownr = colInx.getRowNumber (found); // if (!found) { // cout << "Name MYNAME is unknown" << endl; // } // // Now get a range of names and return the row numbers in ascending order. // // This uses the fact that the 'unique' argument also sorts the data. // RecordFieldPtr nameUpp(colInx.accessUpperKey(), "NAME"); // *nameFld = "LOWER"; // *nameUpp = "UPPER"; // RowNumbers rownrs = colInx.getRowNumbers (True, True, True); // // // Bob Garwood needed such a class. // class ColumnsIndexArray { public: // Create an index on the given table for the given column. // The column can be a scalar or an array column. // If noSort==True, the table is already in order of that // column and the sort step will not be done. // It only supports String and integer columns. ColumnsIndexArray (const Table&, const String& columnName); // Copy constructor (copy semantics). ColumnsIndexArray (const ColumnsIndexArray& that); ~ColumnsIndexArray(); // Assignment (copy semantics). ColumnsIndexArray& operator= (const ColumnsIndexArray& that); // Are all keys in the index unique? Bool isUnique() const; // Return the names of the columns forming the index. const String& columnName() const; // Get the table for which this index is created. const Table& table() const; // Something has changed in the table, so the index has to be recreated. // The 2nd version indicates that a specific column has changed, // so only that column might need to be reread. If that column is not // part of the index, nothing will be done. //
        Note that the class itself is keeping track if the number of // rows in the table changes. // void setChanged(); void setChanged (const String& columnName); // // Access the key values. // These functions allow you to create RecordFieldPtr objects // for each field in the key. In this way you can quickly fill in // the key. //
        The records have a fixed type, so you cannot add or delete fields. //
        Note that accessKey and accessLowerKey // are synonyms; they return the same underlying record. // Record& accessKey(); Record& accessLowerKey(); Record& accessUpperKey(); // // Find the row number matching the key. All keys have to be unique, // otherwise an exception is thrown. // If no match is found, found is set to False. // The 2nd version makes it possible to pass in your own Record // instead of using the internal record via the accessKey // functions. Note that the given Record will be copied to the internal // record, thus overwrites it. // rownr_t getRowNumber (Bool& found); rownr_t getRowNumber (Bool& found, const Record& key); // // Find the row numbers matching the key. It should be used instead // of getRowNumber if the same key can exist multiple times. // The 2nd version makes it possible to pass in your own Record // instead of using the internal record via the accessKey // functions. Note that the given Record will be copied to the internal // record, thus overwrites it. //
        A row can contain multiple equal values. In such a case the // same row number can occur multiple times in the output vector, // unless unique is set to True. Note that making the row // numbers unique implies a sort, so it can also be used to get the // row numbers in ascending order. // RowNumbers getRowNumbers (Bool unique=False); RowNumbers getRowNumbers (const Record& key, Bool unique=False); // // Find the row numbers matching the key range. The boolean arguments // tell if the lower and upper key are part of the range. // The 2nd version makes it possible to pass in your own Records // instead of using the internal records via the // accessLower/UpperKey functions. // Note that the given Records will be copied to the internal // records, thus overwrite them. //
        A row can contain multiple matching values. In such a case the // same row number can occur multiple times in the output vector, // unless unique is set to True. Note that making the row // numbers unique implies a sort, so it can also be used to get the // row numbers in ascending order. // RowNumbers getRowNumbers (Bool lowerInclusive, Bool upperInclusive, Bool unique=False); RowNumbers getRowNumbers (const Record& lower, const Record& upper, Bool lowerInclusive, Bool upperInclusive, Bool unique=False); // protected: // Copy that object to this. void copy (const ColumnsIndexArray& that); // Delete all data in the object. void deleteObjects(); // Add a column to the record description for the keys. // If the switch arrayPossible is True, the column can // be an array. Otherwise it has to be a scalar. void addColumnToDesc (RecordDesc& description, const TableColumn& column); // Make the various internal RecordFieldPtr objects. void makeObjects (const RecordDesc& description); // Read the data of the columns forming the index, sort them and // form the index. void readData(); // Do a binary search on itsUniqueIndexArray for the key in // fieldPtrs. // If the key is found, found is set to True and the index // in itsUniqueIndexArray is returned. // If not found, found is set to False and the index // of the next higher key is returned. rownr_t bsearch (Bool& found, void* fieldPtr) const; // Compare the key in fieldPtr with the given index entry. // -1 is returned when less, 0 when equal, 1 when greater. static Int compare (void* fieldPtr, void* dataPtr, Int dataType, rownr_t index); // Fill the row numbers vector for the given start till end in the // itsUniqueIndexArray vector (end is not inclusive). // If unique is True, the row numbers will be made unique. void fillRowNumbers (Vector& rows, rownr_t start, rownr_t end, Bool unique) const; // Get the data if the column is an array. // void getArray (Vector& result, const String& name); void getArray (Vector& result, const String& name); void getArray (Vector& result, const String& name); void getArray (Vector& result, const String& name); void getArray (Vector& result, const String& name); void getArray (Vector& result, const String& name); // // Fill the rownrs belonging to each array value. void fillRownrs (rownr_t npts, const Block& nrel); private: Table itsTable; rownr_t itsNrrow; Record* itsLowerKeyPtr; Record* itsUpperKeyPtr; Int itsDataType; void* itsDataVector; void* itsData; //# pointer to data in itsDataVector //# The following 2 blocks are actually blocks of RecordFieldPtr*. //# They are used for fast access to the records. void* itsLowerField; void* itsUpperField; Bool itsChanged; Vector itsDataIndex; //# Row numbers of all keys //# Indices in itsDataIndex for each unique key Vector itsUniqueIndex; Block itsRownrs; //# rownr for each value rownr_t* itsDataInx; //# pointer to data in itsDataIndex rownr_t* itsUniqueInx; //# pointer to data in itsUniqueIndex }; inline Bool ColumnsIndexArray::isUnique() const { return (itsDataIndex.nelements() == itsUniqueIndex.nelements()); } inline const Table& ColumnsIndexArray::table() const { return itsTable; } inline Record& ColumnsIndexArray::accessKey() { return *itsLowerKeyPtr; } inline Record& ColumnsIndexArray::accessLowerKey() { return *itsLowerKeyPtr; } inline Record& ColumnsIndexArray::accessUpperKey() { return *itsUpperKeyPtr; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/ConcatColumn.cc000066400000000000000000000276021476623553700207130ustar00rootroot00000000000000//# ConcatColumn.cc: A column in a concatenated table //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ConcatColumn::ConcatColumn (const BaseColumnDesc* bcdp, ConcatTable* reftab) : BaseColumn (bcdp), refTabPtr_p (reftab), refColPtr_p (reftab->getRefColumns (bcdp->name())) { keywordSet_p = refColPtr_p[0]->keywordSet(); } ConcatColumn::~ConcatColumn() {} Bool ConcatColumn::isWritable() const { return refTabPtr_p->isWritable() && refColPtr_p[0]->isWritable(); } Bool ConcatColumn::isStored() const { return refColPtr_p[0]->isStored(); } TableRecord& ConcatColumn::keywordSet() { return keywordSet_p; } TableRecord& ConcatColumn::rwKeywordSet() { return keywordSet_p; } rownr_t ConcatColumn::nrow() const { return refTabPtr_p->nrow(); } void ConcatColumn::initialize (rownr_t startRow, rownr_t endRow) { uInt tableNr; rownr_t tabRownr; for (rownr_t i=startRow; irows().mapRownr (tableNr, tabRownr, i); refColPtr_p[tableNr]->initialize (tabRownr, tabRownr); } } void ConcatColumn::setShape (rownr_t rownr, const IPosition& shape) { uInt tableNr; rownr_t tabRownr; refTabPtr_p->rows().mapRownr (tableNr, tabRownr, rownr); refColPtr_p[tableNr]->setShape (tabRownr, shape); } void ConcatColumn::setShape (rownr_t rownr, const IPosition& shape, const IPosition& tileShape) { uInt tableNr; rownr_t tabRownr; refTabPtr_p->rows().mapRownr (tableNr, tabRownr, rownr); refColPtr_p[tableNr]->setShape (tabRownr, shape, tileShape); } uInt ConcatColumn::ndimColumn() const { return refColPtr_p[0]->ndimColumn(); } IPosition ConcatColumn::shapeColumn() const { return refColPtr_p[0]->shapeColumn(); } uInt ConcatColumn::ndim (rownr_t rownr) const { uInt tableNr; rownr_t tabRownr; refTabPtr_p->rows().mapRownr (tableNr, tabRownr, rownr); return refColPtr_p[tableNr]->ndim (tabRownr); } IPosition ConcatColumn::shape(rownr_t rownr) const { uInt tableNr; rownr_t tabRownr; refTabPtr_p->rows().mapRownr (tableNr, tabRownr, rownr); return refColPtr_p[tableNr]->shape (tabRownr); } IPosition ConcatColumn::tileShape(rownr_t rownr) const { uInt tableNr; rownr_t tabRownr; refTabPtr_p->rows().mapRownr (tableNr, tabRownr, rownr); return refColPtr_p[tableNr]->tileShape (tabRownr); } Bool ConcatColumn::isDefined (rownr_t rownr) const { uInt tableNr; rownr_t tabRownr; refTabPtr_p->rows().mapRownr (tableNr, tabRownr, rownr); return refColPtr_p[tableNr]->isDefined (tabRownr); } Bool ConcatColumn::canChangeShape() const { return refColPtr_p[0]->canChangeShape(); } void ConcatColumn::get (rownr_t rownr, void* dataPtr) const { uInt tableNr; rownr_t tabRownr; refTabPtr_p->rows().mapRownr (tableNr, tabRownr, rownr); refColPtr_p[tableNr]->get (tabRownr, dataPtr); // Set the column cache to the table used. ///setColumnCache (tableNr, refColPtr_p[tableNr]->columnCache()); } void ConcatColumn::getArray (rownr_t rownr, ArrayBase& arr) const { uInt tableNr; rownr_t tabRownr; refTabPtr_p->rows().mapRownr (tableNr, tabRownr, rownr); refColPtr_p[tableNr]->getArray (tabRownr, arr); } void ConcatColumn::getSlice (rownr_t rownr, const Slicer& ns, ArrayBase& arr) const { uInt tableNr; rownr_t tabRownr; refTabPtr_p->rows().mapRownr (tableNr, tabRownr, rownr); refColPtr_p[tableNr]->getSlice (tabRownr, ns, arr); } void ConcatColumn::put (rownr_t rownr, const void* dataPtr) { uInt tableNr; rownr_t tabRownr; refTabPtr_p->rows().mapRownr (tableNr, tabRownr, rownr); refColPtr_p[tableNr]->put (tabRownr, dataPtr); // Set the column cache to the table used. ///setColumnCache (tableNr, refColPtr_p[tableNr]->columnCache()); } void ConcatColumn::putArray (rownr_t rownr, const ArrayBase& arr) { uInt tableNr; rownr_t tabRownr; refTabPtr_p->rows().mapRownr (tableNr, tabRownr, rownr); refColPtr_p[tableNr]->putArray (tabRownr, arr); } void ConcatColumn::putSlice (rownr_t rownr, const Slicer& ns, const ArrayBase& arr) { uInt tableNr; rownr_t tabRownr; refTabPtr_p->rows().mapRownr (tableNr, tabRownr, rownr); refColPtr_p[tableNr]->putSlice (tabRownr, ns, arr); } void ConcatColumn::setMaximumCacheSize (uInt nbytes) { for (uInt i=0; isetMaximumCacheSize (nbytes); } } void ConcatColumn::allocIterBuf (void*& lastVal, void*& curVal, std::shared_ptr& cmpObj) { refColPtr_p[0]->allocIterBuf (lastVal, curVal, cmpObj); } void ConcatColumn::freeIterBuf (void*& lastVal, void*& curVal) { refColPtr_p[0]->freeIterBuf (lastVal, curVal); } void ConcatColumn::getArrayColumn (ArrayBase& arr) const { accessColumn (0, arr, &getColumnPart); } void ConcatColumn::getColumnSlice (const Slicer& ns, ArrayBase& arr) const { accessColumn (&ns, arr, &getColumnSlicePart); } void ConcatColumn::getArrayColumnCells (const RefRows& rownrs, ArrayBase& arr) const { accessRows (rownrs, 0, arr, &getRowsPart); } void ConcatColumn::getColumnSliceCells (const RefRows& rownrs, const Slicer& ns, ArrayBase& arr) const { accessRows (rownrs, &ns, arr, &getRowsSlicePart); } void ConcatColumn::putArrayColumn (const ArrayBase& arr) { accessColumn (0, const_cast(arr), &putColumnPart); } void ConcatColumn::putColumnSlice (const Slicer& ns, const ArrayBase& arr) { accessColumn (&ns, const_cast(arr), &putColumnSlicePart); } void ConcatColumn::putArrayColumnCells (const RefRows& rownrs, const ArrayBase& arr) { accessRows (rownrs, 0, const_cast(arr), &putRowsPart); } void ConcatColumn::putColumnSliceCells (const RefRows& rownrs, const Slicer& ns, const ArrayBase& arr) { accessRows (rownrs, &ns, const_cast(arr), &putRowsSlicePart); } void ConcatColumn::accessColumn (const Slicer* ns, ArrayBase& arr, AccessColumnFunc* accessFunc) const { IPosition st(arr.ndim(), 0); IPosition sz(arr.shape()); uInt nlast = arr.ndim() - 1; for (uInt i=0; inrow(); sz[nlast] = nr; std::unique_ptr part (arr.getSection (Slicer(st, sz))); accessFunc (refColPtr_p[i], ns, *part); st[nlast] += nr; } } void ConcatColumn::accessRows (const RefRows& rownrs, const Slicer* ns, ArrayBase& arr, AccessRowsFunc* accessFunc) const { // The rows to access. Vector rows = rownrs.convert(); // We have one or more slices of rows. // Try to access them also in a sliced way, because that is faster. // First make resources. // The row number mapping. const ConcatRows& ccRows = refTabPtr_p->rows(); // The RefRows vector for the rownrs to be handled in an underlying table. // Make it as large as needed to avoid resizes. Vector tabRowNrs(rows.nelements()); // The rows are handled by combining them as much as possible in a RefRows // slice. This is possible until a different underlying table needs to // be accessed. // First setup the various loop variables. uInt rowAxis = arr.ndim() - 1; // row axis in array IPosition st(arr.ndim(), 0); // start of array part IPosition sz(arr.shape()); // size of array part Int lastTabNr = -1; uInt tableNr; // Step through all concat rownrs. for (rownr_t i=0; i= 0) { rownr_t nrrow = i - st[rowAxis]; sz[rowAxis] = nrrow; Vector rowPart(tabRowNrs(Slice(st[rowAxis], nrrow))); std::unique_ptr part (arr.getSection (Slicer(st, sz))); accessFunc (refColPtr_p[lastTabNr], RefRows(rowPart), ns, *part); } st[rowAxis] = i; lastTabNr = tableNr; } } if (lastTabNr >= 0) { rownr_t nrrow = rows.nelements() - st[rowAxis]; sz[rowAxis] = nrrow; Vector rowPart(tabRowNrs(Slice(st[rowAxis], nrrow))); std::unique_ptr part (arr.getSection (Slicer(st, sz))); accessFunc (refColPtr_p[lastTabNr], RefRows(rowPart), ns, *part); } } void ConcatColumn::getColumnPart (BaseColumn* col, const Slicer*, ArrayBase& arr) { col->getArrayColumn (arr); } void ConcatColumn::putColumnPart (BaseColumn* col, const Slicer*, ArrayBase& arr) { col->putArrayColumn (arr); } void ConcatColumn::getColumnSlicePart (BaseColumn* col, const Slicer* ns, ArrayBase& arr) { col->getColumnSlice (*ns, arr); } void ConcatColumn::putColumnSlicePart (BaseColumn* col, const Slicer* ns, ArrayBase& arr) { col->putColumnSlice (*ns, arr); } void ConcatColumn::getRowsPart (BaseColumn* col, const RefRows& rows, const Slicer*, ArrayBase& arr) { col->getArrayColumnCells (rows, arr); } void ConcatColumn::putRowsPart (BaseColumn* col, const RefRows& rows, const Slicer*, ArrayBase& arr) { col->putArrayColumnCells (rows, arr); } void ConcatColumn::getRowsSlicePart (BaseColumn* col, const RefRows& rows, const Slicer* ns, ArrayBase& arr) { col->getColumnSliceCells (rows, *ns, arr); } void ConcatColumn::putRowsSlicePart (BaseColumn* col, const RefRows& rows, const Slicer* ns, ArrayBase& arr) { col->putColumnSliceCells (rows, *ns, arr); } ColumnCache& ConcatColumn::columnCache() { return colCache_p; } void ConcatColumn::setColumnCache (uInt tableNr, const ColumnCache& colCache) const { // Please note that his is not fully safe, because if the cache in the // underlying table gets changed, it is not reflected in this cache. // There should be some kind of callback or this cache should point // to the other one. // So for the time being this function is not used. const ConcatRows& ccRows = refTabPtr_p->rows(); colCache_p.set (ccRows.offset(tableNr) + colCache.start(), ccRows.offset(tableNr) + colCache.end(), colCache.dataPtr()); colCache_p.setIncrement (colCache.incr()); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/ConcatColumn.h000066400000000000000000000251511476623553700205520ustar00rootroot00000000000000//# ConcatColumn.h: A column in a concatenated table //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_CONCATCOLUMN_H #define TABLES_CONCATCOLUMN_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class ConcatTable; class BaseColumnDesc; class TableRecord; class Slicer; class IPosition; //

        // A column in a concatenated table // // // // // //# Classes you should understand before using this one. //
      • ConcatTable //
      • BaseColumn // // // ConcatTable represents a column in a ConcatTable. A ConcatTable is a table // referencing another table, usually as the result of a select, etc.. // // // ConcatColumn handles the access of a column in a ConcatTable. // It calls the corresponding function in the referenced column // while converting the given row number to the row number in the // referenced table. // // // This class is untyped, i.e. not templated. // Every call is sent to the underlying referenced BaseColumn which // is typed by the virtual function mechanism. // A ConcatColumn can never be used directly. A user always has to // construct a typed ArrayColumn or ScalarColumn object to access a column. // This means everyting is fully type safe. // // //# A List of bugs, limitations, extensions or planned refinements. //
      • Act upon removal of rows or the underlying column // class ConcatColumn : public BaseColumn { public: // Construct the ConcatColumn. It will point to the given column // description, ConcatTable and referenced column. // The ConcatTable will be used to convert the rownr to the rownr // in the referenced column. ConcatColumn (const BaseColumnDesc*, ConcatTable*); ~ConcatColumn(); // Test if the column is writable in the parent table. virtual Bool isWritable() const; // Test if the column is stored (otherwise it is virtual). virtual Bool isStored() const; // Get access to the column keyword set. // The initial keyword set is a copy of the keyword set of the first table. // virtual TableRecord& rwKeywordSet(); virtual TableRecord& keywordSet(); // // Get nr of rows in the column. virtual rownr_t nrow() const; // Test if a value in a particular cell has been defined. virtual Bool isDefined (rownr_t rownr) const; // Set the shape of the array in the given row. virtual void setShape (rownr_t rownr, const IPosition& shape); // Set the shape and tile shape of the array in the given row. virtual void setShape (rownr_t rownr, const IPosition& shape, const IPosition& tileShape); // Get the global #dimensions of an array (i.e. for all rows). virtual uInt ndimColumn() const; // Get the global shape of an array (i.e. for all rows). virtual IPosition shapeColumn() const; // Get the #dimensions of an array in a particular cell. virtual uInt ndim (rownr_t rownr) const; // Get the shape of an array in a particular cell. virtual IPosition shape (rownr_t rownr) const; // Get the tile shape of an array in a particular cell. virtual IPosition tileShape (rownr_t rownr) const; // It can change shape if the underlying column can. virtual Bool canChangeShape() const; // Initialize the rows from startRownr till endRownr (inclusive) // with the default value defined in the column description (if defined). virtual void initialize (rownr_t startRownr, rownr_t endRownr); // Get a value from a particular cell. virtual void get (rownr_t rownr, void* dataPtr) const; // Get a slice of an N-dimensional array in a particular cell. virtual void getSlice (rownr_t rownr, const Slicer&, ArrayBase& dataPtr) const; // Get the array of all array values in a column. // If the column contains n-dim arrays, the resulting array is (n+1)-dim. // The arrays in the column have to have the same shape in all cells. virtual void getArrayColumn (ArrayBase& dataPtr) const; // Get subsections from all arrays in the column. // If the column contains n-dim arrays, the resulting array is (n+1)-dim. // The arrays in the column have to have the same shape in all cells. virtual void getColumnSlice (const Slicer&, ArrayBase& dataPtr) const; // Get an array from a particular cell. virtual void getArray (rownr_t rownr, ArrayBase& dataPtr) const; // Get the array of some array values in a column. // If the column contains n-dim arrays, the resulting array is (n+1)-dim. // The arrays in the column have to have the same shape in all cells. virtual void getArrayColumnCells (const RefRows& rownrs, ArrayBase& dataPtr) const; // Get subsections from some arrays in the column. // If the column contains n-dim arrays, the resulting array is (n+1)-dim. // The arrays in the column have to have the same shape in all cells. virtual void getColumnSliceCells (const RefRows& rownrs, const Slicer&, ArrayBase& dataPtr) const; // Put the scalar value in a particular cell. virtual void put (rownr_t rownr, const void* dataPtr); // Put the array value in a particular cell. virtual void putArray (rownr_t rownr, const ArrayBase& dataPtr); // Put a slice of an N-dimensional array in a particular cell. virtual void putSlice (rownr_t rownr, const Slicer&, const ArrayBase& dataPtr); // Put the array of all array values in the column. // If the column contains n-dim arrays, the source array is (n+1)-dim. // The arrays in the column have to have the same shape in all cells. virtual void putArrayColumn (const ArrayBase& dataPtr); // Put into subsections of all table arrays in the column. // If the column contains n-dim arrays, the source array is (n+1)-dim. // The arrays in the column have to have the same shape in all cells. virtual void putColumnSlice (const Slicer&, const ArrayBase& dataPtr); // Get the array of some array values in a column. // If the column contains n-dim arrays, the resulting array is (n+1)-dim. // The arrays in the column have to have the same shape in all cells. virtual void putArrayColumnCells (const RefRows& rownrs, const ArrayBase& dataPtr); // Put subsections of some arrays in the column. // If the column contains n-dim arrays, the source array is (n+1)-dim. // The arrays in the column have to have the same shape in all cells. virtual void putColumnSliceCells (const RefRows& rownrs, const Slicer&, const ArrayBase& dataPtr); // Get the underlying column cache. virtual ColumnCache& columnCache(); // Set the maximum cache size (in bytes) to be used by a storage manager. virtual void setMaximumCacheSize (uInt nbytes); // Allocate value buffers for the table iterator. // Also get a comparison function if undefined. // The function freeIterBuf must be called to free the buffers. virtual void allocIterBuf (void*& lastVal, void*& curVal, std::shared_ptr& cmpObj); // Free the value buffers allocated by allocIterBuf. virtual void freeIterBuf (void*& lastVal, void*& curVal); private: // Define the function to handle access to an entire column. typedef void AccessColumnFunc (BaseColumn* col, const Slicer*, ArrayBase& array); // Define the function to handle access to a number of rows. typedef void AccessRowsFunc (BaseColumn* col, const RefRows& rows, const Slicer*, ArrayBase& array); // Access the data for an entire column. void accessColumn (const Slicer* ns, ArrayBase& dataPtr, AccessColumnFunc*) const; // Access the data with multiple rows combined. void accessRows (const RefRows& rownrs, const Slicer* ns, ArrayBase& dataPtr, AccessRowsFunc*) const; // Define the access functions. static void getColumnPart (BaseColumn* col, const Slicer*, ArrayBase& arr); static void putColumnPart (BaseColumn* col, const Slicer*, ArrayBase& arr); static void getColumnSlicePart (BaseColumn* col, const Slicer* ns, ArrayBase& arr); static void putColumnSlicePart (BaseColumn* col, const Slicer* ns, ArrayBase& arr); static void getRowsPart (BaseColumn* col, const RefRows& rows, const Slicer*, ArrayBase& array); static void putRowsPart (BaseColumn* col, const RefRows& rows, const Slicer*, ArrayBase& array); static void getRowsSlicePart (BaseColumn* col, const RefRows& rows, const Slicer*, ArrayBase& array); static void putRowsSlicePart (BaseColumn* col, const RefRows& rows, const Slicer*, ArrayBase& array); // protected: // Set the column cache to the cache of the given table. // The row numbers will be adjusted as needed. void setColumnCache (uInt tableNr, const ColumnCache&) const; //# Data members ConcatTable* refTabPtr_p; Block refColPtr_p; mutable ColumnCache colCache_p; TableRecord keywordSet_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/ConcatRows.cc000066400000000000000000000071771476623553700204150ustar00rootroot00000000000000//# ConcatRows.cc: Class holding the row numbers in a ConcatTable //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN void ConcatRows::add (rownr_t nrow) { if (Int64(nrow) + itsRows[itsNTable] >= Int64(65536)*65536) { throw TableError ("Concatenation of tables exceeds 2**32 rows"); } itsNTable++; itsRows.resize (itsNTable+1); itsRows[itsNTable] = itsRows[itsNTable-1] + nrow; } void ConcatRows::findRownr (rownr_t rownr) const { if (rownr >= itsRows[itsNTable]) { throw TableError ("ConcatTable: rownr " + String::toString(rownr) + " past nr of rows (=" + String::toString(itsRows[itsNTable]) + ')'); } Bool found; Int inx = binarySearchBrackets (found, itsRows, rownr, itsNTable); if (!found) { inx--; } DebugAssert (inx>=0 && static_cast(inx)0 ? rows[0]-1 : 0; itsChunk[2] = 1; } // Construct the iterator on a ConcatRows object for the given row range. ConcatRowsIter::ConcatRowsIter (const ConcatRows& rows, rownr_t start, rownr_t end, rownr_t incr) : itsRows (&rows), itsChunk (3), itsStart (start), itsEnd (std::min(end+1, rows.nrow())), itsIncr (incr), itsTabNr (0) { if (itsStart >= itsEnd) { itsPastEnd = True; } else { itsPastEnd = False; rows.mapRownr (itsTabNr, itsChunk[0], start); itsChunk[1] = std::min(rows[itsTabNr], itsEnd) - 1 - rows[itsTabNr-1]; itsChunk[2] = itsIncr; } } void ConcatRowsIter::next() { if (!itsPastEnd) { if (itsTabNr+1 >= itsRows->ntable() || (*itsRows)[itsTabNr] >= itsEnd) { itsPastEnd = True; } else { itsChunk[0] = 0; if (itsIncr != 1) { rownr_t rem = ((*itsRows)[itsTabNr] - itsStart) % itsIncr; if (rem != 0) { itsChunk[0] = itsIncr - rem; } } itsChunk[1] = std::min((*itsRows)[itsTabNr+1], itsEnd) - 1 - (*itsRows)[itsTabNr]; ++itsTabNr; } } } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/ConcatRows.h000066400000000000000000000163631476623553700202540ustar00rootroot00000000000000//# ConcatRows.h: Class holding the row numbers in a ConcatTable //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_CONCATROWS_H #define TABLES_CONCATROWS_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Class holding the row numbers in a ConcatTable // // // // // //# Classes you should understand before using this one. //
      • Block // // // ConcatRows is used to hold the row numbers forming the concatenation // of oher tables. // table. It contains a vector which can hold the row numbers in 2 ways: //
          //
        1. As a normal series of row numbers. This is used by e.g. class // ConcatTable //
        2. As a series of Slices. In this case 3 subsequent entries // in the vector are used to represent start, end, and increment. // This is used by a function like ScalarColumn::getColumnRange. //
        // Class ConcatRowsIter can be // used to iterate through a ConcatRows object. Each step in the iteration // goes to the next slice. If the ConcatRows object contains a simple series // of row numbers, each slice contains only one row number. // This can degrade performance, so it is possible to use shortcuts by // testing if the object contains slices (using isSliced()) // and getting the row number vector directly (using rowVector()). //
        // // ConcatRows is meant to have one class representing the various ways // of picking row numbers. This simplifies the interface of the table // and data manager classes dealing with getting/putting the data. // //# //# A List of bugs, limitations, extensions or planned concatinements. //# class ConcatRows { public: // Construct an empty block. ConcatRows() : itsRows (1,0), itsNTable (0), itsLastStRow (1), itsLastEndRow (0) {} // Reserve the block for the given nr of tables. void reserve (uInt ntable) { itsRows.resize (ntable+1); } // Add a table with the given nr of rows. void add (rownr_t nrow); // Give the nr of tables. uInt ntable() const { return itsNTable; } // Get the total nr of rows. rownr_t nrow() const { return itsRows[itsNTable]; } // Give the nr of rows for the i-th table. rownr_t operator[] (uInt i) const { return itsRows[i+1]; } // Give the offset for the i-th table. rownr_t offset (uInt i) const { return itsRows[i]; } // Map an overall row number to a table and row number. void mapRownr (uInt& tableNr, rownr_t& tabRownr, rownr_t rownr) const { if (rownr < itsLastStRow || rownr >= itsLastEndRow) { findRownr (rownr); } tableNr = itsLastTableNr; tabRownr = rownr - itsLastStRow; } private: // Find the row number and fill in the lastXX_p values. void findRownr (rownr_t rownr) const; //# Data members. Block itsRows; uInt itsNTable; mutable rownr_t itsLastStRow; //# Cached variables to spped up mutable rownr_t itsLastEndRow; //# function mapRownr(). mutable uInt itsLastTableNr; }; // // Class to iterate through a ConcatRows object. // // // // // //# Classes you should understand before using this one. //
      • ConcatRows // // // ConcatRowsSliceIter is useful to iterate through a // ConcatRows object. // It is possible to define for which row // especially if the ConcatRows object contains slices. // Each step in the iteration returns a Slice object containing // the next slice in the ConcatRows object. //
        // It is used in Table and data manager classes (e.g. StManColumn). //
        // // This example shows how to iterate through a ConcatRows object // (giving a slice) and through each of the slices. // // void somefunc (const ConcatRows& rownrs) // // Iterate through all slices. // ConcatRowsSliceIter rowiter(rownrs); // while (! rowiter.pastEnd()) { // // Get start, end, and increment for this slice. // rownr_t rownr = rowiter.sliceStart(); // rownr_t end = rowiter.sliceEnd(); // rownr_t incr = rowiter.sliceIncr(); // // Iterate through the row numbers in the slice. // while (rownr <= end) { // rownr += incr; // } // // Go to next slice. // rowiter++; // } // } // // //# //# A List of bugs, limitations, extensions or planned concatinements. //# class ConcatRowsIter { public: // Construct the iterator on a ConcatRows object. // It is set to the full range. explicit ConcatRowsIter (const ConcatRows&); // Construct the iterator on a ConcatRows object for the given row range. ConcatRowsIter (const ConcatRows&, rownr_t start, rownr_t end, rownr_t incr=1); // Is the iterator past the end? Bool pastEnd() const { return itsPastEnd; } // Go the next chunk. // void operator++() { next(); } void operator++(int) { next(); } void next(); // // Get the current chunk. RefRows getChunk() const { return RefRows(itsChunk, True); } // Get the nr of the table the current chunk is in. uInt tableNr() const { return itsTabNr; } private: const ConcatRows* itsRows; Vector itsChunk; rownr_t itsStart; rownr_t itsEnd; rownr_t itsIncr; uInt itsTabNr; Bool itsPastEnd; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/ConcatScalarColumn.h000066400000000000000000000107541476623553700217030ustar00rootroot00000000000000//# ConcatScalarColumn.h: A typed scalar column in a concatenated table //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_CONCATSCALARCOLUMN_H #define TABLES_CONCATSCALARCOLUMN_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // A typed column in a concatenated table // // // // // //# Classes you should understand before using this one. //
      • ConcatTable //
      • BaseColumn // // // ConcatTable represents a column in a ConcatTable. A ConcatTable is a table // referencing another table, usually as the result of a select, etc.. // // // ConcatColumn handles the access of a column in a ConcatTable. // It calls the corresponding function in the referenced column // while converting the given row number to the row number in the // referenced table. // // // This class is untyped, i.e. not templated. // Every call is sent to the underlying referenced BaseColumn which // is typed by the virtual function mechanism. // A ConcatColumn can never be used directly. A user always has to // construct a typed ArrayColumn or ScalarColumn object to access a column. // This means everyting is fully type safe. // // //# A List of bugs, limitations, extensions or planned refinements. //
      • Act upon removal of rows or the underlying column // template class ConcatScalarColumn : public ConcatColumn { public: // Construct the ConcatColumn. It will point to the given column // description, ConcatTable and referenced column. // The ConcatTable will be used to convert the rownr to the rownr // in the apprpriate table. ConcatScalarColumn (const BaseColumnDesc*, ConcatTable*); ~ConcatScalarColumn(); // Get the vector of all scalar values in a column. virtual void getScalarColumn (ArrayBase& dataPtr) const; // Get the vector of some scalar values in a column. virtual void getScalarColumnCells (const RefRows& rownrs, ArrayBase& dataPtr) const; // Put the vector of all scalar values in the column. virtual void putScalarColumn (const ArrayBase& dataPtr); // Get the vector of some scalar values in a column. virtual void putScalarColumnCells (const RefRows& rownrs, const ArrayBase& dataPtr); // Handle the creation and deletion of sort keys. // virtual void makeSortKey (Sort& sortobj, std::shared_ptr& cmpObj, Int order, std::shared_ptr& dataSave); virtual void makeRefSortKey (Sort& sortobj, std::shared_ptr& cmpObj, Int order, const Vector& rownrs, std::shared_ptr& dataSave); virtual void fillSortKey (const Vector* vecPtr, Sort& sortobj, std::shared_ptr& cmpObj, Int order); // }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/tables/Tables/ConcatScalarColumn.tcc000066400000000000000000000144541476623553700222260ustar00rootroot00000000000000//# ConcatScalarColumn.cc: A typed scalar column in a concatenated table //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_CONCATSCALARCOLUMN_TCC #define TABLES_CONCATSCALARCOLUMN_TCC #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template ConcatScalarColumn::ConcatScalarColumn (const BaseColumnDesc* bcdp, ConcatTable* reftab) : ConcatColumn (bcdp, reftab) {} template ConcatScalarColumn::~ConcatScalarColumn() {} template void ConcatScalarColumn::getScalarColumn (ArrayBase& arr) const { Vector& vec = static_cast&>(arr); rownr_t st = 0; for (uInt i=0; inrow(); Vector part = vec(Slice(st, nr)); refColPtr_p[i]->getScalarColumn (part); st += nr; } // Set the column cache to the first table. ///setColumnCache (0, refColPtr_p[0]->columnCache()); } template void ConcatScalarColumn::getScalarColumnCells (const RefRows& rownrs, ArrayBase& arr) const { Vector& vec = static_cast&>(arr); // Get the rownrs as a vector and sort it. // In this way the data will be read in sequential order. Vector rows = rownrs.convert(); Vector inx; GenSortIndirect::sort (inx, rows); const ConcatRows& ccRows = refTabPtr_p->rows(); rownr_t tabRownr; uInt tableNr=0; // Map each row to rownr and tablenr. // Note this is pretty fast because it is done in row order. for (rownr_t i=0; iget (tabRownr, &(vec[row])); } // Set the column cache to the last table used. ///setColumnCache (tableNr, refColPtr_p[tableNr]->columnCache()); } template void ConcatScalarColumn::putScalarColumn (const ArrayBase& arr) { Vector vec (static_cast&>(arr)); rownr_t st = 0; for (uInt i=0; inrow(); Vector part = vec(Slice(st, nr)); refColPtr_p[i]->putScalarColumn (part); st += nr; } // Set the column cache to the first table. ///setColumnCache (0, refColPtr_p[0]->columnCache()); } template void ConcatScalarColumn::putScalarColumnCells (const RefRows& rownrs, const ArrayBase& arr) { const Vector& vec = static_cast&>(arr); // Get the rownrs as a vector and sort it. // In this way the data will be read in sequential order. Vector rows = rownrs.convert(); Vector inx; GenSortIndirect::sort (inx, rows); const ConcatRows& ccRows = refTabPtr_p->rows(); rownr_t tabRownr; uInt tableNr=0; // Map each row to rownr and tablenr. // Note this is pretty fast because it is done in row order. for (rownr_t i=0; iput (tabRownr, &(vec[row])); } // Set the column cache to the last table used. ///setColumnCache (tableNr, refColPtr_p[tableNr]->columnCache()); } template void ConcatScalarColumn::makeSortKey (Sort& sortobj, std::shared_ptr& cmpObj, Int order, std::shared_ptr& dataSave) { //# Get the data as a column. Vector* vecPtr = new Vector(nrow()); dataSave.reset (vecPtr); getScalarColumn (*vecPtr); fillSortKey (vecPtr, sortobj, cmpObj, order); } template void ConcatScalarColumn::makeRefSortKey (Sort& sortobj, std::shared_ptr& cmpObj, Int order, const Vector& rownrs, std::shared_ptr& dataSave) { //# Get the data as a column. Vector* vecPtr = new Vector(rownrs.size()); dataSave.reset (vecPtr); getScalarColumnCells (rownrs, *vecPtr); fillSortKey (vecPtr, sortobj, cmpObj, order); } template void ConcatScalarColumn::fillSortKey (const Vector* vecPtr, Sort& sortobj, std::shared_ptr& cmpObj, Int order) { //# Pass the real vector storage as the sort data. //# Use the compare object if given, otherwise pass data type. //# Throw an exception if no compare function is given for //# an unknown data type. Bool deleteIt; const T* datap = vecPtr->getStorage (deleteIt); if (!cmpObj) { cmpObj = std::make_shared>(); } sortobj.sortKey (datap, cmpObj, sizeof(T), order == Sort::Descending ? Sort::Descending : Sort::Ascending); vecPtr->freeStorage (datap, deleteIt); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/ConcatTable.cc000066400000000000000000000445711476623553700205110ustar00rootroot00000000000000//# ConcatTable.cc: Class to view a concatenation of tables as a single table //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ConcatTable::ConcatTable (AipsIO& ios, const String& name, rownr_t nrrow, int option, const TableLock& lockOptions, const TSMOption& tsmOption) : BaseTable (name, option, nrrow), changed_p (False) { //# Read the file in. // Set initially to no write in destructor. // At the end it is reset. In this way nothing is written if // an exception is thrown during initialization. noWrite_p = True; getConcat (ios, option, lockOptions, tsmOption); noWrite_p = False; } ConcatTable::ConcatTable (const Block
      • & tables, const Block& subTables, const String& subDirName) : BaseTable ("", Table::Scratch, 0), subTableNames_p (subTables), subDirName_p (subDirName), tables_p (tables), changed_p (True) { ///cout<<"cctab1="<& tableNames, const Block& subTables, const String& subDirName, int option, const TableLock& lockOptions, const TSMOption& tsmOption) : BaseTable ("", Table::Scratch, 0), subTableNames_p (subTables), subDirName_p (subDirName), changed_p (True) { ///cout<<"cctab1="<& names, Bool recursive) const { if (recursive) { for (uInt i=0; igetPartNames (names, recursive); } } else { uInt inx = names.size(); names.resize (inx + tables_p.nelements()); for (uInt i=0; iasBigEndian(); } const StorageOption& ConcatTable::storageOption() const { return tables_p[0].storageOption(); } Bool ConcatTable::isMultiUsed (Bool) const { return False; } const TableLock& ConcatTable::lockOptions() const { return tables_p[0].lockOptions(); } void ConcatTable::mergeLock (const TableLock& lockOptions) { for (uInt i=0; imergeLock (lockOptions); } } Bool ConcatTable::hasLock (FileLocker::LockType type) const { for (uInt i=0; igetModifyCounter(); } //# Write a concatenate table into a file. void ConcatTable::writeConcatTable (Bool) { //# Write name and type of root and write object data. //# Do this only when something has changed. if (changed_p) { AipsIO ios; writeStart (ios, True); // writeStart has made the table directory. // Create the subDir directory if given. String sdName; if (! subDirName_p.empty()) { sdName = tableName() + '/' + subDirName_p + '/'; Directory dir(sdName); dir.create(); } ios << "ConcatTable"; ios.putstart ("ConcatTable", 0); // Make the name of the base tables relative to this table. // First move a table if subDirName_p is set. ios << uInt(tables_p.nelements()); for (uInt i=0; i rootNames; Int version = ios.getstart ("ConcatTable"); AlwaysAssert (version==0, AipsError); ios >> nrtab; rootNames.resize(nrtab); for (uInt i=0; i> rootNames[i]; rootNames[i] = Path::addDirectory (rootNames[i], tableName()); } ios >> subTableNames_p; ios.getend(); openTables (rootNames, option, lockOptions, tsmOption); initialize(); //# Read the TableInfo object. getTableInfo(); } void ConcatTable::openTables (const Block& tableNames, int option, const TableLock& lockOptions, const TSMOption& tsmOption) { //# Open the tables referenced to. tables_p.resize (tableNames.nelements()); rows_p.reserve (tableNames.nelements() + 1); for (uInt i=0; i> actualDesc(tables_p.nelements());; Bool equalDataTypes; for (uInt i=0; i(tables_p[i].actualTableDesc()); if (actualDesc[i]->columnDescSet().isEqual (actualDesc[0]->columnDescSet(), equalDataTypes)) { if (equalDataTypes) { continue; } } throw TableError("All tables in ConCatTable must have same description"); } // For fixed shaped arrays check if all tables have the same shape. // If not, clear dimensionality and options. for (uInt i=0; incolumn(); ++i) { ColumnDesc& colDesc = actualDesc[0]->rwColumnDesc(i); if (colDesc.isArray() && (colDesc.options() & ColumnDesc::FixedShape) != 0) { Bool sameShape = true; for (uInt j=1; jcolumnDesc(i); if ((cd.options() & ColumnDesc::FixedShape) == 0 || ! colDesc.shape().isEqual (cd.shape())) { sameShape = False; break; } } if (!sameShape) { colDesc.setNdim (0); colDesc.setOptions (0); } } } //# Use the table description. tdescPtr_p = std::make_shared(*(actualDesc[0]), TableDesc::Scratch); keywordSet_p = tables_p[0].keywordSet(); // Handle the possible concatenated subtables. handleSubTables(); // Create the concatColumns. // Do this last, to avoid leaks in case of exceptions above. makeConcatCol(); } void ConcatTable::handleSubTables() { // Check for each subtable if it exists in all tables. // If fine, create a ConcatTable for each subtable. Block
        subtables(tables_p.nelements()); for (uInt i=0; i rootNames, subNames; Int version = ios.getstart ("ConcatTable"); AlwaysAssert (version==0, AipsError); ios >> nrtab; rootNames.resize(nrtab); for (uInt i=0; i> rootNames[i]; } ios >> subNames; ios.getend(); TableUtil::getLayout (desc, rootNames[0]); } //# Create a ConcatColumn object for all columns in the description. //# Insert it with the name in the column map. void ConcatTable::makeConcatCol() { for (uInt i=0; incolumn(); i++) { const ColumnDesc& cd = tdescPtr_p->columnDesc(i); colMap_p.insert (std::make_pair(cd.name(), cd.makeConcatColumn (this))); } } Block ConcatTable::getRefColumns (const String& columnName) { Block cols(tables_p.nelements()); for (uInt i=0; igetColumn (columnName); } return cols; } //# Test if the table is writable. Bool ConcatTable::isWritable() const { for (uInt i=0; icolumnDesc(columnName); // check if column exists return colMap_p.at(columnName); } BaseColumn* ConcatTable::getColumn (uInt columnIndex) const { const String& name = tdescPtr_p->columnDesc(columnIndex).name(); return colMap_p.at(name); } void ConcatTable::addConcatCol (const ColumnDesc& columnDesc) { ColumnDesc& cd = tdescPtr_p->addColumn(columnDesc); colMap_p.insert (std::make_pair(cd.name(), cd.makeConcatColumn(this))); changed_p = True; } void ConcatTable::addConcatCol (const TableDesc& tdesc) { for (uInt i=0; iisColumn(name)) { throw TableInvOper ("Table::addColumn; column " + name + " already exists"); } if (!addToParent) { throw TableInvOper ("ConcatTable::addColumn; column " + name + " does not exist in parent table, but must not be added" " (addToParent=False)"); } } void ConcatTable::addColumn (const ColumnDesc& columnDesc, Bool addToParent) { checkAddColumn (columnDesc.name(), addToParent); for (uInt i=0; i&) const { return False; } Bool ConcatTable::canRenameColumn (const String&) const { return False; } void ConcatTable::removeRow (rownr_t) { throw TableInvOper("ConcatTable cannot remove rows"); } void ConcatTable::removeColumn (const Vector&) { throw TableInvOper("ConcatTable cannot remove columns"); } void ConcatTable::renameColumn (const String&, const String&) { throw TableInvOper("ConcatTable cannot rename columns"); } void ConcatTable::renameHypercolumn (const String&, const String&) { throw TableInvOper("ConcatTable cannot rename hypercolumns"); } DataManager* ConcatTable::findDataManager (const String& name, Bool byColumn) const { return tables_p[0].findDataManager (name, byColumn); } void ConcatTable::showStructureExtra (std::ostream& os) const { for (uInt i=0; i #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TSMOption; class ConcatColumn; class AipsIO; // // Class to view a concatenation of tables as a single table. // // // // // //# Classes you should understand before using this one. //
      • BaseTable //
      • ConcatColumn // // // ConcatTable represents the concatenation of one or more tables. // // // ConcatTable is used to virtually concatenate one or more tables. // Those tables must have the same description. // // It acts to the user as a normal table. All gets and puts are // handled by ConcatColumn which directs them to the referenced columns // while (if needed) converting the given row number to the row number // in the referenced tables. For that purpose ConcatTable keeps the // number of rows in the referenced tables. // Currently it cannot handle changes in the number of rows in the // underlying tables. // // It is possible to specify the keyword names of the subtables that have // to be concatenated as well. The other subtables are assumed to be // identical for all tables, so only the subtable of the first table is used. // // The ConcatTable maintains its own keyword set, which is initially a copy // of the keyword set of the first table. It replaces the keywords of the // subtables to be concatenated. // The keyword set is not persistent. One can add or change keywords, but // these changes are not kept when the ConcatTable object is made persistent. // // // Sometimes a very large MeasurementSet is split into multiple smaller ones // using the time axis. Using ConcatTable they can still be viewed as a // single MS. The SYSCAL subtable is split in time as well, thus it has // to be possible to concatenate that one as well. // An MS split in subband could be concatenated as well provided that // at least the first part contains the full SPECTRAL_WINDOW subtable and // that unique SPWids are used. // // // //# A List of bugs, limitations, extensions or planned refinements. //
      • Maybe not allocating the row number vector for a projection. // This saves space and time, but each rownr conversion will // take a bit more time because it has to test if there is a vector. //
      • Maybe maintain a Vector telling on which columns // the table is ordered. This may speed up selection, but // it is hard to check if the order is changed by a put. //
      • Allow to remove a row or column from the ConcatTable //
      • Allow to rename a column in the ConcatTable //
      • Maybe implement doSort one time for a more efficient sort. // (now everything is handled by BaseTable). // class ConcatTable : public BaseTable { public: // Create a virtual table as the concatenation of the given tables. // It checks if the table descriptions of the tables are the same. // Subtables with the given names will be concatenated as well. // It is assumed that the other subtables are the same for all tables, // so the ones of the first table are used. //
        The option can be Table::Old or Table::Update. //
        If a non-empty subdirectory name is given, the tables will // be moved to that subdirectory when the concatenated table is written // (by writeConcatTable). // ConcatTable (const Block
      • & tables, const Block& subTables, const String& subDirName); ConcatTable (const Block& tableNames, const Block& subTables, const String& subDirName, int option, const TableLock& lockOptions, const TSMOption& tsmOption); // // Create a concat table out of a file (written by writeConcatTable). // The referenced tables will also be opened (if not stored in the cache). ConcatTable (AipsIO&, const String& name, rownr_t nrrow, int option, const TableLock& lockOptions, const TSMOption& tsmOption); // The destructor flushes (i.e. writes) the table if it is opened // for output and not marked for delete. virtual ~ConcatTable(); // Copy constructor is forbidden, because copying a table requires // some more knowledge (like table name of result). ConcatTable (const ConcatTable&) = delete; // Assignment is forbidden, because copying a table requires // some more knowledge (like table name of result). ConcatTable& operator= (const ConcatTable&) = delete; // Get the names of the tables this table consists of. virtual void getPartNames (Block& names, Bool recursive) const; // Return the layout of a table (i.e. description and #rows). // This function has the advantage that only the minimal amount of // information required is read from the table, thus it is much // faster than a normal table open. //
        The number of rows is returned. The description of the table // is stored in desc (its contents will be overwritten). static void getLayout (TableDesc& desc, AipsIO& ios); // Try to reopen the table (the underlying ones) for read/write access. // An exception is thrown if the table is not writable. // Nothing is done if the table is already open for read/write. virtual void reopenRW(); // Is the table stored in big or little endian format? // It returns the endianness of the first underlying table. virtual Bool asBigEndian() const; // Get the storage option used for the table. // It returns the storage option of the first underlying table. virtual const StorageOption& storageOption() const; // Is the table in use (i.e. open) in another process? // It always returns False. virtual Bool isMultiUsed (Bool checkSubTable) const; // Get the locking info. // All underlying tables have the same lock option. virtual const TableLock& lockOptions() const; // Merge the given lock info with the existing one. virtual void mergeLock (const TableLock& lockOptions); // Has this process the read or write lock, thus can the table // be read or written safely? virtual Bool hasLock (FileLocker::LockType) const; // Try to lock the table for read or write access. virtual Bool lock (FileLocker::LockType, uInt nattempts); // Unlock the table. This will also synchronize the table data, // thus force the data to be written to disk. virtual void unlock(); // Flush the table, i.e. write it to disk. // Nothing will be done if the table is not writable. // A flush can be executed at any time. // When a table is marked for delete, the destructor will remove // files written by intermediate flushes. // Note that if necessary the destructor will do an implicit flush, // unless it is executed due to an exception. virtual void flush (Bool fsync, Bool recursive); // Resync the Table object with the table files. virtual void resync(); // Get the modify counter. virtual uInt getModifyCounter() const; // Test if all underlying tables are opened as writable. virtual Bool isWritable() const; // Read a concat table from a file. // The underlying tables will be opened (if not stored in the cache). void getConcat (AipsIO&, int option, const TableLock& lockOptions, const TSMOption& tsmOption); // This is doing a shallow copy. // It gives an error if the ConcatTable has not been stored yet. virtual void copy (const String& newName, int tableOption) const; // Copy the table and all its subtables. // It copies the contents of each row to get a real copy. virtual void deepCopy (const String& newName, const Record& dataManagerInfo, const StorageOption&, int tableOption, Bool, int endianFormat, Bool noRows) const; // It returns the type of the parent table. virtual int tableType() const; // Get the actual table description. virtual TableDesc actualTableDesc() const; // Get the data manager info (of the first underlying table). virtual Record dataManagerInfo() const; // Get readonly access to the table keyword set. virtual TableRecord& keywordSet(); // Get read/write access to the table keyword set. // This requires that the table is locked (or it gets locked // when using AutoLocking mode). virtual TableRecord& rwKeywordSet(); // Get a column object using its index. virtual BaseColumn* getColumn (uInt columnIndex) const; // Get a column object using its name. virtual BaseColumn* getColumn (const String& columnName) const; // Test if it is possible to remove a row from this table (no). virtual Bool canRemoveRow() const; // Remove the given row. virtual void removeRow (rownr_t rownr); // Test if columns can be removed (no). virtual Bool canRemoveColumn (const Vector& columnNames) const; // Add one or more columns to the table. // The column is added to the parent tables if told so and if not existing. // virtual void addColumn (const ColumnDesc& columnDesc, Bool addToParent); virtual void addColumn (const ColumnDesc& columnDesc, const String& dataManager, Bool byName, Bool addToParent); virtual void addColumn (const ColumnDesc& columnDesc, const DataManager& dataManager, Bool addToParent); virtual void addColumn (const TableDesc& tableDesc, const DataManager& dataManager, Bool addToParent); // // Remove a column. virtual void removeColumn (const Vector& columnNames); // Test if a column can be renamed (no). virtual Bool canRenameColumn (const String& columnName) const; // Rename a column. virtual void renameColumn (const String& newName, const String& oldName); // Rename a hypercolumn. virtual void renameHypercolumn (const String& newName, const String& oldName); // Find the data manager with the given name or for the given column. virtual DataManager* findDataManager (const String& name, Bool byColumn) const; // Get the rows object. const ConcatRows& rows() const { return rows_p; } // Get the column objects in the referenced tables. Block getRefColumns (const String& columnName); private: // Show the extra table structure info (names of used tables). void showStructureExtra (std::ostream&) const; // Open all tables in the required way. void openTables (const Block& tableNames, int option, const TableLock& lockOptions, const TSMOption& tsmOption); // Initialize. // It checks if the descriptions of all tables are equal. // It creates the keyword setfor which it concatenates subtables as needed. void initialize(); // Setup the main parts of the object. //
        First create the name map (mapping column name in ConcatTable to // the column in the original table). // If the BaseTable is a ConcatTable, use its name map. // Otherwise create the initial name map from the table description. // A rename might change the map. //
        Create the ConcatColumn objects. //
        Create the initial TableInfo as a copy of the original BaseTable. void setup (BaseTable* btp, const Vector& columnNames); // Add lines containing the concatenated tables to the info. void addInfo(); // Create the ConcatColumn objects for all columns in the description. void makeConcatCol(); // Handle the subtales that have to be concatenated. void handleSubTables(); // Write a reference table. void writeConcatTable (Bool fsync); // Check if the column can be added, thus does not exist yet. void checkAddColumn (const String& name, Bool addToParent); // Add a column, with internal bookeeping (columns map). void addConcatCol (const ColumnDesc& cd); // Add multiple columns, with internal bookeeping (columns map). void addConcatCol (const TableDesc& tdesc); //# Data members Block subTableNames_p; String subDirName_p; Block
        tables_p; //# Tables forming the concat std::map colMap_p; //# map name to column TableRecord keywordSet_p; Bool changed_p; //# True = changed since last write ConcatRows rows_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/ExternalLockSync.cc000066400000000000000000000042311476623553700215470ustar00rootroot00000000000000//# ExternalLockSync.cc: Class to hold table lock data //# Copyright (C) 1997,1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ExternalLockSync::ExternalLockSync (const TableLock& lockOptions) : itsLock (lockOptions, releaseCallBack, this), itsNrrow (0) {} ExternalLockSync::~ExternalLockSync() {} void ExternalLockSync::makeLock (const String& tableName, Bool create, FileLocker::LockType type) { itsLock.makeLock (tableName, create, type); } Bool ExternalLockSync::acquire (FileLocker::LockType type, uInt nattempts) { if (! itsLock.acquire (&(itsSync.memoryIO()), type, nattempts)) { return False; } uInt nrcol; Bool tableChanged; Block dataManChanged; itsSync.read (itsNrrow, nrcol, tableChanged, dataManChanged); return True; } MemoryIO* ExternalLockSync::releaseCallBack (void* lockSyncObject, Bool always) { return (*(ExternalLockSync*)lockSyncObject).doReleaseCallBack (always); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/ExternalLockSync.h000066400000000000000000000113251476623553700214130ustar00rootroot00000000000000//# ExternalLockSync.h: Class to hold table lock data //# Copyright (C) 1997,1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_EXTERNALLOCKSYNC_H #define TABLES_EXTERNALLOCKSYNC_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Class to hold table lock data. // // // // // //
      • class TableLock // // // This class keeps the LockFile object used to do the // actual locking/unlocking. // It also keeps the synchronization information. // // // Encapsulate Table locking data. // class ExternalLockSync { public: // Construct from the given TableLock object. ExternalLockSync (const TableLock& lockOptions); ~ExternalLockSync(); // Copy constructor is forbidden. ExternalLockSync (const ExternalLockSync& that) = delete; // Assignment is forbidden. ExternalLockSync& operator= (const ExternalLockSync& that) = delete; // Create the LockFile object and acquire a read or write // lock when permanent locking is in effect. // It throws an exception when acquiring the lock failed. void makeLock (const String& tableName, Bool create, FileLocker::LockType); // Acquire a read or write lock (when needed). // Nattempts==0 indicates that it has to wait until the lock is acquired. // Nattempts>0 indicates that it gives up acquiring the lock when // nattempts have been done (with 1 second intervals). // It throws an exception when acquire failed while it had to wait. // It returns a false status when acquiring the lock failed // while it does not have to wait. //
        When a lock is successfully acquired, the number of rows // (see function nrrow() below) is reset as a result of // synchronizing the access to the table. Bool acquire (FileLocker::LockType = FileLocker::Write, uInt nattempts = 0); // Get the current number of rows in this object. rownr_t nrow() const; // Release the lock and synchronize the table access. // When autolocking is in effect, the lock is only released when // the inspection-interval (see class // TableLockData) has expired. // It does nothing when permanent locking is used. // It throws an exception when the release failed. void release (rownr_t nrrow); // Check if the table has a read or write lock, thus if the table can // be read or written safely. Bool hasLock (FileLocker::LockType) const; private: // The callback function when releasing a lock. static MemoryIO* releaseCallBack (void* lockSyncObject, Bool always); // The member function executing the callback functionality. MemoryIO* doReleaseCallBack (Bool always); //# Define the lock and sync data objects. TableLockData itsLock; TableSyncData itsSync; rownr_t itsNrrow; }; inline Bool ExternalLockSync::hasLock (FileLocker::LockType type) const { return itsLock.hasLock (type); } inline void ExternalLockSync::release (rownr_t nrrow) { itsNrrow = nrrow; itsLock.release(); } inline MemoryIO* ExternalLockSync::doReleaseCallBack (Bool) { itsSync.write (itsNrrow); return &(itsSync.memoryIO()); } inline rownr_t ExternalLockSync::nrow() const { return itsNrrow; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/MemoryTable.cc000066400000000000000000000216451476623553700205470ustar00rootroot00000000000000//# MemoryTable.cc: Class for a table held in memory //# Copyright (C) 2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN MemoryTable::MemoryTable (SetupNewTable& newtab, rownr_t nrrow, Bool initialize) : BaseTable (newtab.name(), newtab.option(), 0), colSetPtr_p (0), lockPtr_p (0) { //# Check if another Table was already constructed using this //# SetupNewTable (which is invalid). if (newtab.isUsed()) { throw (TableInvOper ("SetupNewTable object already used for another Table")); } //# Use MemoryStMan for stored and unbound columns. std::shared_ptr tdescPtr = newtab.tableDescPtr(); std::shared_ptr colSetPtr = newtab.columnSetPtr(); MemoryStMan stman(colSetPtr->uniqueDataManagerName("MSMTAB")); for (uInt i=0; incolumn(); i++) { PlainColumn* col = colSetPtr->getColumn(i); if (!col->isBound() || col->isStored()) { newtab.bindColumn (tdescPtr->columnDesc(i).name(), stman); } } //# Check if there are no data managers with equal names. newtab.columnSetPtr()->checkDataManagerNames ("MemoryTable"); //# Get the data from the SetupNewTable object. //# Set SetupNewTable object to in use. tdescPtr_p = tdescPtr; colSetPtr_p = colSetPtr; colSetPtr_p->linkToTable (this); newtab.setInUse(); //# Create the lock object. lockPtr_p = new TableLockData (TableLock(TableLock::PermanentLocking), 0, this); colSetPtr_p->linkToLockObject (lockPtr_p); //# Initialize the data managers. Table tab(this); nrrowToAdd_p = nrrow; colSetPtr_p->initDataManagers (nrrow, False, TSMOption(TSMOption::Cache,0,0), tab); //# Initialize the columns if needed. if (initialize && nrrow > 0) { colSetPtr_p->initialize (0, nrrow-1); } //# Nrrow_p has to be set here, otherwise data managers may use the //# incorrect number of rows (similar behaviour as in function addRow). nrrowToAdd_p = 0; nrrow_p = nrrow; // The table is transient, thus deleted when destructed. // It is set, so Table::isMarkedForDelete() returns True. markForDelete (False, ""); } MemoryTable::~MemoryTable() { delete lockPtr_p; } void MemoryTable::reopenRW() {} Bool MemoryTable::asBigEndian() const { return HostInfo::bigEndian(); } const StorageOption& MemoryTable::storageOption() const { return colSetPtr_p->storageOption(); } Bool MemoryTable::isMultiUsed (Bool) const { return False; } const TableLock& MemoryTable::lockOptions() const { return *lockPtr_p; } void MemoryTable::mergeLock (const TableLock&) {} Bool MemoryTable::hasLock (FileLocker::LockType) const { return True; } Bool MemoryTable::lock (FileLocker::LockType, uInt) { return True; } void MemoryTable::unlock() {} void MemoryTable::flush (Bool, Bool) {} void MemoryTable::resync() {} uInt MemoryTable::getModifyCounter() const { return 0; } Bool MemoryTable::isWritable() const { return True; } void MemoryTable::copy (const String& newName, int tableOption) const { Record dmInfo = colSetPtr_p->dataManagerInfo(); deepCopy (newName, dmInfo, StorageOption(), tableOption, True, Table::AipsrcEndian, False); } void MemoryTable::deepCopy (const String& newName, const Record& dataManagerInfo, const StorageOption& stopt, int tableOption, Bool, int endianFormat, Bool noRows) const { trueDeepCopy (newName, dataManagerInfo, stopt, tableOption, endianFormat, noRows); } void MemoryTable::rename (const String& newName, int) { //# Rename the names of the subtables in the keywords. String oldName = name_p; renameSubTables (newName, oldName); name_p = newName; } int MemoryTable::tableType() const { return Table::Memory; } TableDesc MemoryTable::actualTableDesc() const { return *tdescPtr_p; } Record MemoryTable::dataManagerInfo() const { return colSetPtr_p->dataManagerInfo(); } TableRecord& MemoryTable::keywordSet() { return tdescPtr_p->rwKeywordSet(); } TableRecord& MemoryTable::rwKeywordSet() { return tdescPtr_p->rwKeywordSet(); } void MemoryTable::flushTableInfo() {} BaseColumn* MemoryTable::getColumn (uInt columnIndex) const { return colSetPtr_p->getColumn (columnIndex); } BaseColumn* MemoryTable::getColumn (const String& columnName) const { return colSetPtr_p->getColumn (columnName); } Bool MemoryTable::canAddRow() const { return True; } void MemoryTable::addRow (rownr_t nrrw, Bool initialize) { if (nrrw > 0) { nrrowToAdd_p = nrrw; colSetPtr_p->addRow (nrrw); if (initialize) { colSetPtr_p->initialize (nrrow_p, nrrow_p+nrrw-1); } nrrowToAdd_p = 0; nrrow_p += nrrw; } } Bool MemoryTable::canRemoveRow() const { return True; } void MemoryTable::removeRow (rownr_t rownr) { colSetPtr_p->removeRow (rownr); nrrow_p--; } void MemoryTable::addColumn (const ColumnDesc& columnDesc, Bool) { Table tab(this); ColumnDesc cold(columnDesc); // Make sure the MemoryStMan is used. cold.dataManagerType() = "MemoryStMan"; cold.dataManagerGroup() = "MSMTAB"; colSetPtr_p->addColumn (cold, False, TSMOption(TSMOption::Cache,0,0), tab); } void MemoryTable::addColumn (const ColumnDesc& columnDesc, const String& dataManager, Bool byName, Bool) { Table tab(this); if (byName) { colSetPtr_p->addColumn (columnDesc, dataManager, byName, False, TSMOption(TSMOption::Cache,0,0), tab); } else { // Make sure the MemoryStMan is used if no virtual engine is used. DataManager* dmptr = DataManager::getCtor(dataManager) (dataManager, Record()); addColumn (columnDesc, *dmptr, False); delete dmptr; } } void MemoryTable::addColumn (const ColumnDesc& columnDesc, const DataManager& dataManager, Bool) { Table tab(this); // a temporary Table object // Make sure the MemoryStMan is used if no virtual engine is used. if (dataManager.isStorageManager()) { addColumn (columnDesc, False); } else { colSetPtr_p->addColumn (columnDesc, dataManager, False, TSMOption(TSMOption::Cache,0,0), tab); } } void MemoryTable::addColumn (const TableDesc& tableDesc, const DataManager& dataManager, Bool) { Table tab(this); // Make sure the MemoryStMan is used if no virtual engine is used. if (dataManager.isStorageManager()) { MemoryStMan stman(dataManager.dataManagerName()); colSetPtr_p->addColumn (tableDesc, stman, False, TSMOption(TSMOption::Cache,0,0), tab); } else { colSetPtr_p->addColumn (tableDesc, dataManager, False, TSMOption(TSMOption::Cache,0,0), tab); } } Bool MemoryTable::canRemoveColumn (const Vector&) const { return True; } void MemoryTable::removeColumn (const Vector& columnNames) { colSetPtr_p->removeColumn (columnNames); } Bool MemoryTable::canRenameColumn (const String&) const { return True; } void MemoryTable::renameColumn (const String& newName, const String& oldName) { colSetPtr_p->renameColumn (newName, oldName); } void MemoryTable::renameHypercolumn (const String& newName, const String& oldName) { tdescPtr_p->renameHypercolumn (newName, oldName); } DataManager* MemoryTable::findDataManager (const String& name, Bool byColumn) const { return colSetPtr_p->findDataManager (name, byColumn); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/MemoryTable.h000066400000000000000000000206261476623553700204070ustar00rootroot00000000000000//# MemoryTable.h: Class for a table held in memory //# Copyright (C) 2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_MEMORYTABLE_H #define TABLES_MEMORYTABLE_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class SetupNewTable; class ColumnSet; class TableLockData; // // Class for a table held in memory // // // // // //# Classes you should understand before using this one. //
      • BaseTable // // // MemoryTable holds all its data in memory. // It means that the data is not persistent. However, it can be copied to // another table to make the data persistent. // Furthermore it is a table as all other tables, so all table functions // can be applied to it. Some functions (e.g. lock) won't do anything. // Also all table operations like sorting, selecting, and iterating can // be used. // // The constructor accepts a SetupNewTable object which can contain // bindings of columns to any data manager. All bindings to storage // managers will be replaced by a binding to the memory based storage // manager MemoryStMan. Also all // unbound columns will be bound to MemoryStMan. // Thus it is still possible that a column is bound to a virtual column // engine like CompressComplex. // //# //# class MemoryTable : public BaseTable { public: // Create the table in memory using the definitions in the // SetupNewTable object. MemoryTable (SetupNewTable&, rownr_t nrrow, Bool initialize); // The destructor deletes all data. virtual ~MemoryTable(); // Copy constructor is forbidden, because copying a table requires // some more knowledge (like table name of result). MemoryTable (const MemoryTable&) = delete; // Assignment is forbidden, because copying a table requires // some more knowledge (like table name of result). MemoryTable& operator= (const MemoryTable&) = delete; // Try to reopen the table (the underlying one) for read/write access. // It does nothing. virtual void reopenRW(); // Is the table stored in big or little endian format? // It returns the endian format of the machine. virtual Bool asBigEndian() const; // Get the storage option used for the table. virtual const StorageOption& storageOption() const; // Is the table in use (i.e. open) in another process? // It always returns False. virtual Bool isMultiUsed (Bool checkSubTable) const; // Get the locking info. // It returns PermanentLocking. virtual const TableLock& lockOptions() const; // Merge the given lock info with the existing one. // It does nothing. virtual void mergeLock (const TableLock& lockOptions); // Has this process the read or write lock, thus can the table // be read or written safely? // It always returns True. virtual Bool hasLock (FileLocker::LockType) const; // Locking the table is a no-op. virtual Bool lock (FileLocker::LockType, uInt nattempts); // Unlocking the table is a no-op. virtual void unlock(); // Flushing the table is a no-op. virtual void flush (Bool fsync, Bool recursive); // Resyncing the Table is a no-op. virtual void resync(); // Get the modify counter. It always returns 0. virtual uInt getModifyCounter() const; // Test if the table is opened as writable. It always returns True. virtual Bool isWritable() const; // Copy the table and all its subtables. // It copies the contents of each row to get a real copy. // virtual void copy (const String& newName, int tableOption) const; virtual void deepCopy (const String& newName, const Record& dataManagerInfo, const StorageOption&, int tableOption, Bool, int endianFormat, Bool noRows) const; // // Rename the table. The tableOption is ignored. virtual void rename (const String& newName, int tableOption); // Get the table type (Table::Memory). virtual int tableType() const; // Get the actual table description. virtual TableDesc actualTableDesc() const; // Get the data manager info. virtual Record dataManagerInfo() const; // Get readonly access to the table keyword set. virtual TableRecord& keywordSet(); // Get read/write access to the table keyword set. virtual TableRecord& rwKeywordSet(); // Write the TableInfo object. It does not do anything. virtual void flushTableInfo(); // Get a column object using its index. virtual BaseColumn* getColumn (uInt columnIndex) const; // Get a column object using its name. virtual BaseColumn* getColumn (const String& columnName) const; // Test if it is possible to add a row to this table (yes). virtual Bool canAddRow() const; // Add one or more rows and possibly initialize them. // This will fail for tables not supporting addition of rows. virtual void addRow (rownr_t nrrow = 1, Bool initialize = True); // Test if it is possible to remove a row from this table (yes). virtual Bool canRemoveRow() const; // Remove the given row. virtual void removeRow (rownr_t rownr); // Add a column to the table. // If the DataManager is not a virtual engine, MemoryStMan will be used. // The last Bool argument is not used in MemoryTable, but can be used in // other classes derived from BaseTable. // virtual void addColumn (const ColumnDesc& columnDesc, Bool addToParent); virtual void addColumn (const ColumnDesc& columnDesc, const String& dataManager, Bool byName, Bool addToParent); virtual void addColumn (const ColumnDesc& columnDesc, const DataManager& dataManager, Bool addToParent); virtual void addColumn (const TableDesc& tableDesc, const DataManager& dataManager, Bool addToParent); // // Test if columns can be removed (yes). virtual Bool canRemoveColumn (const Vector& columnNames) const; // Remove columns. virtual void removeColumn (const Vector& columnNames); // Test if a column can be renamed (yes). virtual Bool canRenameColumn (const String& columnName) const; // Rename a column. virtual void renameColumn (const String& newName, const String& oldName); // Rename a hypercolumn. virtual void renameHypercolumn (const String& newName, const String& oldName); // Find the data manager with the given name or for the given column. // There is only one storage manager (MemoryStMan) with name MSM. virtual DataManager* findDataManager (const String& name, Bool byColumn) const; private: std::shared_ptr colSetPtr_p; //# pointer to set of columns TableLockData* lockPtr_p; //# pointer to lock object // Setup the main parts of the object. //
        Create the initial name map from the table description. // This map maps a name to the name in the original table. // A rename might change the map. //
        Create the RefColumn objects. //
        Create the initial TableInfo as a copy of the original BaseTable. void setup (BaseTable* btp); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/NullTable.cc000066400000000000000000000135711476623553700202100ustar00rootroot00000000000000//# NullTable.cc: Class indicating a null Table object //# Copyright (C) 2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN NullTable::NullTable() : BaseTable ("Null table object", Table::Old, 0) { delete_p = False; } NullTable::~NullTable() {} Bool NullTable::isNull() const { return True; } void NullTable::reopenRW() { throw makeError ("reopenRW"); } Bool NullTable::asBigEndian() const { throw makeError ("asBigEndian"); } Bool NullTable::isMultiUsed (Bool) const { throw makeError ("isMultiUsed"); } const StorageOption& NullTable::storageOption() const { throw makeError ("storageOption"); } const TableLock& NullTable::lockOptions() const { throw makeError ("lockOptions"); } void NullTable::mergeLock (const TableLock&) { throw makeError ("mergeLoc"); } Bool NullTable::hasLock (FileLocker::LockType) const { throw makeError ("hasLock"); } Bool NullTable::lock (FileLocker::LockType, uInt) { throw makeError ("lock"); } void NullTable::unlock() { throw makeError ("unlock"); } void NullTable::flush (Bool, Bool) { throw makeError ("flush"); } void NullTable::resync() { throw makeError ("resync"); } uInt NullTable::getModifyCounter() const { throw makeError ("getModifyCounter"); } Bool NullTable::isWritable() const { throw makeError ("isWritable"); } void NullTable::deepCopy (const String&, const Record&, const StorageOption&, int, Bool, int, Bool) const { throw makeError ("deepCopy"); } TableDesc NullTable::actualTableDesc() const { throw makeError ("actualTableDesc"); } Record NullTable::dataManagerInfo() const { throw makeError ("dataManagerInfo"); } TableRecord& NullTable::keywordSet() { throw makeError ("keywordSet"); } TableRecord& NullTable::rwKeywordSet() { throw makeError ("rwKeywordSet"); } BaseColumn* NullTable::getColumn (uInt) const { throw makeError ("getColumn"); } BaseColumn* NullTable::getColumn (const String&) const { throw makeError ("getColumn"); } Bool NullTable::canAddRow() const { throw makeError ("canAddRow"); } void NullTable::addRow (rownr_t, Bool) { throw makeError ("addRow"); } Bool NullTable::canRemoveRow() const { throw makeError ("canRemoveRow"); } void NullTable::removeRow (rownr_t) { throw makeError ("removeRow"); } DataManager* NullTable::findDataManager (const String&, Bool) const { throw makeError ("findDataManager"); } void NullTable::addColumn (const ColumnDesc&, Bool) { throw makeError ("addColumn"); } void NullTable::addColumn (const ColumnDesc&, const String&, Bool, Bool) { throw makeError ("addColumn"); } void NullTable::addColumn (const ColumnDesc&, const DataManager&, Bool) { throw makeError ("addColumn"); } void NullTable::addColumn (const TableDesc& , const DataManager&, Bool) { throw makeError ("addColumn"); } Bool NullTable::canRemoveColumn (const Vector&) const { throw makeError ("canRemoveColumn"); } void NullTable::removeColumn (const Vector&) { throw makeError ("removeColumn"); } Bool NullTable::canRenameColumn (const String&) const { throw makeError ("canRenameColumn"); } void NullTable::renameColumn (const String&, const String&) { throw makeError ("renameColumn"); } void NullTable::renameHypercolumn (const String&, const String&) { throw makeError ("renameHypercolumn"); } Vector NullTable::rowNumbers() const { throw makeError ("rowNumbers"); } BaseTable* NullTable::root() { throw makeError ("root"); } Bool NullTable::rowOrder() const { throw makeError ("rowOrder"); } Vector& NullTable::rowStorage() { throw makeError ("rowStorage"); } Bool NullTable::adjustRownrs (rownr_t, Vector&, Bool) const { throw makeError ("adjustRownrs"); } std::shared_ptr NullTable::doSort (PtrBlock&, const Block>&, const Block&, int, std::shared_ptr>, std::shared_ptr>) { throw makeError ("doSort"); } void NullTable::renameSubTables (const String&, const String&) { throw makeError ("renameSubTables"); } TableError NullTable::makeError (const String& name) const { return TableError ("NullTable::" + name + " - Table object is empty"); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/NullTable.h000066400000000000000000000140171476623553700200460ustar00rootroot00000000000000//# NullTable.h: Class indicating a null Table object //# Copyright (C) 2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_NULLTABLE_H #define TABLES_NULLTABLE_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Class indicating a null Table object // // // // // //# Classes you should understand before using this one. //
      • BaseTable // // // NullTable represents a null table object, i.e. a Table object without // an underlying table. // // // NullTable is used to represent a null table. // The default Table constructor used to a create a null pointer // which resulted in core dumps when the Table object was actually used. // The NullTable object makes it possible to catch such cases // and throw an appropriate exception. // class NullTable : public BaseTable { public: // Default constructor. NullTable(); virtual ~NullTable(); // Copy constructor is forbidden. NullTable (const NullTable&) = delete; // Assignment is forbidden. NullTable& operator= (const NullTable&) = delete; // The table is a null table. virtual Bool isNull() const override; // All functions throw a "null table" exception. // virtual void reopenRW() override; virtual Bool asBigEndian() const override; virtual const StorageOption& storageOption() const override; virtual Bool isMultiUsed (Bool checkSubTable) const override; virtual const TableLock& lockOptions() const override; virtual void mergeLock (const TableLock& lockOptions) override; virtual Bool hasLock (FileLocker::LockType) const override; virtual Bool lock (FileLocker::LockType, uInt nattempts) override; virtual void unlock() override; virtual void flush (Bool fsync, Bool recursive) override; virtual void resync() override; virtual uInt getModifyCounter() const override; virtual Bool isWritable() const override; virtual void deepCopy (const String& newName, const Record& dataManagerInfo, const StorageOption&, int tableOption, Bool valueCopy, int endianFormat, Bool noRows) const override; virtual TableDesc actualTableDesc() const override; virtual Record dataManagerInfo() const override; virtual TableRecord& keywordSet() override; virtual TableRecord& rwKeywordSet() override; virtual BaseColumn* getColumn (uInt columnIndex) const override; virtual BaseColumn* getColumn (const String& columnName) const override; virtual Bool canAddRow() const override; virtual void addRow (rownr_t nrrow, Bool initialize) override; virtual Bool canRemoveRow() const override; virtual void removeRow (rownr_t rownr) override; virtual DataManager* findDataManager (const String& name, Bool byColumn) const override; virtual void addColumn (const ColumnDesc& columnDesc, Bool addToParent) override; virtual void addColumn (const ColumnDesc& columnDesc, const String& dataManager, Bool byName, Bool addToParent) override; virtual void addColumn (const ColumnDesc& columnDesc, const DataManager& dataManager, Bool addToParent) override; virtual void addColumn (const TableDesc& tableDesc, const DataManager& dataManager, Bool addToParent) override; virtual Bool canRemoveColumn (const Vector& columnNames) const override; virtual void removeColumn (const Vector& columnNames) override; virtual Bool canRenameColumn (const String& columnName) const override; virtual void renameColumn (const String& newName, const String& oldName) override; virtual void renameHypercolumn (const String& newName, const String& oldName) override; virtual Vector rowNumbers() const override; virtual BaseTable* root() override; virtual Bool rowOrder() const override; virtual Vector& rowStorage() override; virtual Bool adjustRownrs (rownr_t nrrow, Vector& rownrs, Bool determineOrder) const override; virtual std::shared_ptr doSort (PtrBlock&, const Block>&, const Block&, int, std::shared_ptr>, std::shared_ptr>) override; virtual void renameSubTables (const String& newName, const String& oldName) override; // private: // Make an exception message with the name of the function. TableError makeError (const String& name) const; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/PlainColumn.cc000066400000000000000000000122101476623553700205340ustar00rootroot00000000000000//# PlainColumn.cc: Base class for a column in a plain table //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN PlainColumn::PlainColumn (const BaseColumnDesc* cdp, ColumnSet* csp) : BaseColumn (cdp), dataManPtr_p (0), dataColPtr_p (0), colSetPtr_p (csp), originalName_p(cdp->name()) { int trace = TableTrace::traceColumn (columnDesc()); rtraceColumn_p = (trace&TableTrace::READ) != 0; wtraceColumn_p = (trace&TableTrace::WRITE) != 0; } PlainColumn::~PlainColumn() {} rownr_t PlainColumn:: nrow() const { return colSetPtr_p->nrow(); } TableRecord& PlainColumn::rwKeywordSet() { Bool hasLocked = colSetPtr_p->userLock (FileLocker::Write, True); checkWriteLock (True); TableRecord& rec = const_cast(colDescPtr_p)->rwKeywordSet(); colSetPtr_p->setTableChanged(); colSetPtr_p->userUnlock (hasLocked); return rec; } TableRecord& PlainColumn::keywordSet() { Bool hasLocked = colSetPtr_p->userLock (FileLocker::Read, True); checkReadLock (True); TableRecord& rec = const_cast(colDescPtr_p)->rwKeywordSet(); colSetPtr_p->userUnlock (hasLocked); return rec; } //# By default defining the array shape is invalid. void PlainColumn::setShapeColumn (const IPosition&) { throw (TableInvOper ("setShapeColumn not allowed for column " + colDescPtr_p->name())); } Bool PlainColumn::isBound() const { return (dataManPtr_p == 0 ? False : True); } void PlainColumn::bind (DataManager* dataManPtr) { dataManPtr_p = dataManPtr; } Bool PlainColumn::isWritable() const { return dataColPtr_p->isWritable(); } Bool PlainColumn::isStored() const { return dataManPtr_p->isStorageManager(); } ColumnCache& PlainColumn::columnCache() { return dataColPtr_p->columnCache(); } void PlainColumn::setMaximumCacheSize (uInt nbytes) { dataManPtr_p->setMaximumCacheSize (nbytes); } //# Read/write the column. //# Its data will be read/written by the appropriate storage manager. //# It was felt that putstart takes too much space, so therefore //# the version is put "manually". void PlainColumn::putFile (AipsIO& ios, const TableAttr&) { ios << (uInt)2; // class version 2 ios << originalName_p; putFileDerived (ios); } void PlainColumn::getFile (AipsIO& ios, const ColumnSet& colset, const TableAttr& attr) { uInt version; ios >> version; // In the older Table files the keyword set was written separately // and was not part of the TableDesc. // So read it for those and merge it into the TableDesc keywords. if (version == 1) { TableRecord tmp; tmp.getRecord (ios, attr); keywordSet().merge (tmp, RecordInterface::OverwriteDuplicates); } ios >> originalName_p; getFileDerived (ios, colset); } void PlainColumn::checkValueLength (const String* value) const { uInt maxlen = colDescPtr_p->maxLength(); if (maxlen > 0 && value->length() > maxlen) { throw (TableError ("ScalarColumn::put: string value '" + *value + "' exceeds maximum length")); } } void PlainColumn::checkValueLength (const Array* value) const { uInt maxlen = colDescPtr_p->maxLength(); if (maxlen == 0) { return; } ReadOnlyArrayIterator iter (*value, 1); while (! iter.pastEnd()) { Vector vec (iter.array()); for (size_t i=0; i maxlen) { throw (TableError ("Scalar/ArrayColumn::put: string value '" + vec(i) + "' exceeds maximum length")); } } iter.next(); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/PlainColumn.h000066400000000000000000000147531476623553700204140ustar00rootroot00000000000000//# PlainColumn.h: Base class for a column in a plain table //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_PLAINCOLUMN_H #define TABLES_PLAINCOLUMN_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableAttr; class BaseColumnDesc; class DataManager; class DataManagerColumn; class AipsIO; class IPosition; // // Base class for a column in a plain table // // // // // //# Classes you should understand before using this one. //
      • BaseColumn //
      • PlainTable // // // PlainColumn represents any column in a plain table. // A plain table is a regular table, i.e. not a table like a // RefTable which is a view on a plain table. // // // Abstract base class for all types of columns in a plain table. // It implements the common functionality for all columns in a plain // table. Furthermore it defines some virtual functions (on top of // the virtual functions defined in BaseColumn) which are specific for // plain columns. // // //# A List of bugs, limitations, extensions or planned refinements. // class PlainColumn : public BaseColumn { public: PlainColumn (const BaseColumnDesc*, ColumnSet*); virtual ~PlainColumn(); // Test if the column is in principle writable. // This does not test if the table itself is writable. // That has to be done by the caller. virtual Bool isWritable() const; // Test if the column is stored (otherwise it is virtual). virtual Bool isStored() const; // Get access to the column keyword set. // TableRecord& rwKeywordSet(); TableRecord& keywordSet(); // // Get nr of rows in the column. rownr_t nrow() const; // Define the shape of all arrays in the column. virtual void setShapeColumn (const IPosition& shape); // Test if the column is bound to a storage manager or // virtual column engine. virtual Bool isBound() const; // Bind the column to a data manager. virtual void bind (DataManager*); // Create a data manager column for a filled column. virtual void createDataManagerColumn() = 0; // Get the pointer to the data manager. DataManager* dataManager() const; // Get the pointer to the data manager column. DataManagerColumn*& dataManagerColumn(); // Get a pointer to the underlying column cache. virtual ColumnCache& columnCache(); // Set the maximum cache size (in bytes) to be used by a storage manager. virtual void setMaximumCacheSize (uInt nbytes); // Write the column. void putFile (AipsIO&, const TableAttr&); // Read the column. void getFile (AipsIO&, const ColumnSet&, const TableAttr&); protected: DataManager* dataManPtr_p; //# Pointer to data manager. DataManagerColumn* dataColPtr_p; //# Pointer to column in data manager. ColumnSet* colSetPtr_p; String originalName_p; //# Column name before any rename Bool rtraceColumn_p; //# trace reads of the column? Bool wtraceColumn_p; //# trace writes of the column? // Get the trace-id of the table. int traceId() const { return colSetPtr_p->traceId(); } // Write the column. // The control information is written into the given AipsIO object, // while the data is written by the storage manager. virtual void putFileDerived (AipsIO&) = 0; // Read the column back. // The control information is read from the given AipsIO object. // This is used to bind the column to the appropriate data manager. virtual void getFileDerived (AipsIO&, const ColumnSet&) = 0; // Check the length of a value. // This a meant for String values for which a maximum length is defined. // The void* version is a no-op for other values. // void checkValueLength (const void*) const; void checkValueLength (const String* value) const; void checkValueLength (const Array* value) const; // // Lock the table before reading or writing. // If manual or permanent locking is in effect, it checks if // the table is locked. // void checkReadLock (Bool wait) const; void checkWriteLock (Bool wait) const; // // Inspect the auto lock when the inspection interval has expired and // release it when another process needs the lock. void autoReleaseLock() const; }; inline DataManager* PlainColumn::dataManager() const { return dataManPtr_p; } inline DataManagerColumn*& PlainColumn::dataManagerColumn() { return dataColPtr_p; } inline void PlainColumn::checkValueLength (const void*) const {} inline void PlainColumn::checkReadLock (Bool wait) const { colSetPtr_p->checkReadLock (wait); } inline void PlainColumn::checkWriteLock (Bool wait) const { colSetPtr_p->checkWriteLock (wait); } inline void PlainColumn::autoReleaseLock() const { colSetPtr_p->autoReleaseLock(); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/PlainTable.cc000066400000000000000000000636441476623553700203470ustar00rootroot00000000000000//# PlainTable.cc: Class defining a regular table //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //# for nanosleep namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Initialize the static TableCache object. TableCache PlainTable::theirTableCache; PlainTable::PlainTable (SetupNewTable& newtab, rownr_t nrrow, Bool initialize, const TableLock& lockOptions, int endianFormat, const TSMOption& tsmOption) : BaseTable (newtab.name(), newtab.option(), 0) { PlainTableCommon(newtab, nrrow, initialize, lockOptions, endianFormat, tsmOption); } #ifdef HAVE_MPI PlainTable::PlainTable (MPI_Comm mpiComm, SetupNewTable& newtab, rownr_t nrrow, Bool initialize, const TableLock& lockOptions, int endianFormat, const TSMOption& tsmOption) : BaseTable (mpiComm, newtab.name(), newtab.option(), 0) { PlainTableCommon(newtab, nrrow, initialize, lockOptions, endianFormat, tsmOption); } #endif void PlainTable::PlainTableCommon (SetupNewTable& newtab, rownr_t nrrow, Bool initialize, const TableLock& lockOptions, int endianFormat, const TSMOption& tsmOption) { colSetPtr_p = 0; tableChanged_p = True; addToCache_p = True; lockPtr_p = 0; tsmOption_p = tsmOption; try { // Determine and set the endian option. setEndian (endianFormat); // Replace default TSM option for new table. tsmOption_p.fillOption (True); // Set initially to no write in destructor. // At the end it is reset. In this way nothing is written if // an exception is thrown during initialization. noWrite_p = True; //# Check if another Table was already constructed using this //# SetupNewTable (which is invalid). if (newtab.isUsed()) { throw (TableInvOper ("SetupNewTable object already used for another Table")); } //# Check if a table with this name is not in the table cache. if (tableCache()(name_p) != 0) { // OK it's in the cache but is it really there? if(File(name_p).exists()){ throw (TableInvOper ("SetupNewTable " + name_p + " is already opened (is in the table cache)")); } else { tableCache().remove (name_p); } } //# If the table already exists, exit if it is in use. if (Table::isReadable (name_p)) { TableLockData tlock (TableLock (TableLock::UserLocking, 0)); tlock.makeLock (name_p, False, FileLocker::Write); if (tlock.isMultiUsed()) { throw (TableError ("Table " + name_p + " cannot be created; " "it is in use in another process")); } } //# Create the data managers for unbound columns. //# Check if there are no data managers with equal names. newtab.handleUnbound(); newtab.columnSetPtr()->checkDataManagerNames (name_p); //# Get the data from the SetupNewTable object. //# Set SetupNewTable object to in use. tdescPtr_p = newtab.tableDescPtr(); colSetPtr_p = newtab.columnSetPtr(); colSetPtr_p->linkToTable (this); newtab.setInUse(); //# Create the table directory (and possibly delete existing files) //# as needed. makeTableDir(); //# Create the lock object. //# When needed, it sets a permanent write lock. //# Acquire a write lock. lockPtr_p = new TableLockData (lockOptions, releaseCallBack, this); lockPtr_p->makeLock (name_p, True, FileLocker::Write); lockPtr_p->acquire (0, FileLocker::Write, 1); colSetPtr_p->linkToLockObject (lockPtr_p); //# Initialize the data managers. Table tab(this); nrrowToAdd_p = nrrow; colSetPtr_p->initDataManagers (nrrow, bigEndian_p, tsmOption_p, tab); //# Initialize the columns if needed. if (initialize && nrrow > 0) { colSetPtr_p->initialize (0, nrrow-1); } //# Nrrow_p has to be set here, otherwise data managers may use the //# incorrect number of rows (similar behaviour as in function addRow). nrrowToAdd_p = 0; nrrow_p = nrrow; //# Release the write lock if UserLocking is used. if (lockPtr_p->option() == TableLock::UserLocking) { lockPtr_p->release(); } //# Unmark for delete when needed. if (! newtab.isMarkedForDelete()) { unmarkForDelete (True, ""); } //# The destructor can (in principle) write. noWrite_p = False; //# Add it to the table cache. tableCache().define (name_p, this); //# Trace if needed. itsTraceId = TableTrace::traceTable (name_p, 'n'); } catch (std::exception&) { delete lockPtr_p; lockPtr_p = 0; throw; } } PlainTable::PlainTable (AipsIO&, uInt version, const String& tabname, const String& type, rownr_t nrrow, int opt, const TableLock& lockOptions, const TSMOption& tsmOption, Bool addToCache, uInt locknr) : BaseTable (tabname, opt, nrrow), tableChanged_p (False), addToCache_p (addToCache), lockPtr_p (0), tsmOption_p (tsmOption) { // Replace default TSM option for existing table. tsmOption_p.fillOption (False); //# Set initially to no write in destructor. //# At the end it is reset. In this way nothing is written if //# an exception is thrown during initialization. noWrite_p = True; //# Create the lock object. //# When needed, it sets a permanent (read or write) lock. //# Otherwise acquire a read lock (when needed) to read in the table //# or get the sync info. lockPtr_p = new TableLockData (lockOptions, releaseCallBack, this); lockPtr_p->makeLock (name_p, False, opt == Table::Old ? FileLocker::Read : FileLocker::Write, locknr); if (lockPtr_p->readLocking()) { lockPtr_p->acquire (&(lockSync_p.memoryIO()), FileLocker::Read, 0); } else { lockPtr_p->getInfo (lockSync_p.memoryIO()); } uInt ncolumn; Bool tableChanged; Block dmChanged; lockSync_p.read (nrrow_p, ncolumn, tableChanged, dmChanged); tdescPtr_p = std::make_shared("", TableDesc::Scratch); //# Reopen the file to be sure that the internal stdio buffer is not reused. //# This is a terrible hack, but it works. //# However, One time a better solution is needed. //# Probably stdio should not be used, but class RegularFileIO or //# FilebufIO should do its own buffering and have a sync function. AipsIO ios (Table::fileName(tabname), ByteIO::Old); String tp; version = ios.getstart ("Table"); if (version > 3) { throw TableError ("PlainTable version " + String::toString(version) + " not supported by this version of Casacore"); } if (version > 2) { ios >> nrrow; } else { uInt n; ios >> n; nrrow = n; } uInt format; ios >> format; bigEndian_p = (format==0); ios >> tp; // If locking is not used, nrrow_p might be 0. Use nrrow in that case. if (nrrow_p == 0) { nrrow_p = nrrow; } #if defined(TABLEREPAIR) cerr << "tableRepair: found " << nrrow << " rows; give new number: "; cin >> nrrow_p; if (nrrow != nrrow_p) { cerr << "Number of rows set to " << nrrow_p << endl; tableChanged_p = True; } #endif TableAttr attr (tableName(), PlainTable::isWritable(), lockOptions); tdescPtr_p->getFile (ios, attr); // read description // Check if the given table type matches the type in the file. if ((! type.empty()) && type != tdescPtr_p->getType()) { throw (TableInvType (tableName(), type, tdescPtr_p->getType())); return; } // In the older Table files the keyword set was written separately // and was not part of the TableDesc. // So read it for those and merge it into the TableDesc keywords. // Merging is done after attaching the lock to the ColumnSet, // because function keywordSet() uses the lock. TableRecord tmp; if (version == 1) { tmp.getRecord (ios, attr); } //# Construct and read the ColumnSet object. //# This will also construct the various DataManager objects. colSetPtr_p = std::make_shared(tdescPtr_p.get()); colSetPtr_p->linkToTable (this); colSetPtr_p->linkToLockObject (lockPtr_p); if (version == 1) { PlainTable::keywordSet().merge (tmp, RecordInterface::OverwriteDuplicates); } //# Create a Table object to be used internally by the data managers. //# Do not count it, otherwise a mutual dependency exists. Table tab(this); nrrow_p = colSetPtr_p->getFile (ios, tab, nrrow_p, bigEndian_p, tsmOption_p); //# Read the TableInfo object. getTableInfo(); //# Release the read lock if UserLocking is used. if (lockPtr_p->option() == TableLock::UserLocking) { lockPtr_p->release(); } //# The destructor can (in principle) write. noWrite_p = False; //# Add it to the table cache. if (addToCache) { tableCache().define (name_p, this); } //# Trace if needed. itsTraceId = TableTrace::traceTable (name_p, 'o'); } PlainTable::~PlainTable() { // If destructed during an exception, catch possible other exceptions to // avoid termination. if (std::uncaught_exception() ) { try { closeObject(); } catch (std::exception& x) { try { cerr << "Exception in ~PlainTable during exception unwind:" << endl << " " << x.what() << endl; } catch (...) { } } } else { closeObject(); } } void PlainTable::closeObject() { //# When needed, write and sync the table files if not marked for delete if (!isMarkedForDelete()) { if (openedForWrite() && !shouldNotWrite()) { lockPtr_p->release (True); } }else{ //# Check if table can indeed be deleted. //# If not, set delete flag to False. //# It only checks if the main table is multi-used. //# File locking support in Lustre (maybe other file systems too) //# seems to be asynchronous to some degree, so try a few times. int nTrys = 5; timespec timet; timet.tv_sec = 1; timet.tv_nsec = 0; while (PlainTable::isMultiUsed(False)) { if (nTrys == 0) { unmarkForDelete (False, ""); throw (TableError ("Table " + name_p + " cannot be deleted;" " the table or a subtable is still used" " in another process")); } nanosleep (&timet, 0); // nanosleep works well with signals --nTrys; } } //# Remove it from the table cache (if added). if (addToCache_p) { tableCache().remove (name_p); } //# Trace if needed. TableTrace::traceClose (name_p); //# Delete everything. delete lockPtr_p; } //# Read description and #rows. void PlainTable::getLayout (TableDesc& desc, AipsIO& ios) { desc.getFile (ios, TableAttr()); // read description } void PlainTable::reopenRW() { // Exit if already open for write. if (isWritable()) { return; } // Exception when readonly table. if (! Table::isWritable (tableName())) { throw (TableError ("Table " + tableName() + " cannot be opened for read/write")); } // When a permanent lock is in use, turn it into a write lock. lockPtr_p->makeLock (name_p, False, FileLocker::Write); // Set table to opened for read/write. // Do this before reopening subtables, because that might cause // recursion (e.g. SORTED_TABLE in the MS). option_p = Table::Update; // Reopen the storage managers and the subtables in all keyword sets. colSetPtr_p->reopenRW(); keywordSet().reopenRW(); TableTrace::traceFile (itsTraceId, "reopenrw"); } void PlainTable::renameSubTables (const String& newName, const String& oldName) { rwKeywordSet().renameTables (newName, oldName); colSetPtr_p->renameTables (newName, oldName); } Bool PlainTable::asBigEndian() const { return bigEndian_p; } const StorageOption& PlainTable::storageOption() const { return colSetPtr_p->storageOption(); } Bool PlainTable::isMultiUsed (Bool checkSubTables) const { if (lockPtr_p->isMultiUsed()) { return True; } if (checkSubTables) { if (const_cast(this)-> keywordSet().areTablesMultiUsed()) { return True; } return colSetPtr_p->areTablesMultiUsed(); } return False; } const TableLock& PlainTable::lockOptions() const { return *lockPtr_p; } void PlainTable::mergeLock (const TableLock& lockOptions) { Bool isPerm = lockPtr_p->isPermanent(); lockPtr_p->merge (lockOptions); // Acquire if needed a permanent lock. if (lockPtr_p->isPermanent() && !isPerm) { lockPtr_p->makeLock (name_p, False, isWritable() ? FileLocker::Write : FileLocker::Read); } } Bool PlainTable::hasLock (FileLocker::LockType type) const { return lockPtr_p->hasLock (type); } Bool PlainTable::lock (FileLocker::LockType type, uInt nattempts) { //# When the table is already locked (read locked is sufficient), //# no synchronization is needed (other processes could not write). Bool noSync = hasLock (FileLocker::Read); //# Acquire the required lock. //# Synchronize the table when it has changed. //# When table data has changed, a temporary PlainTable object is created //# to get the new keyword values, etc.. Deleting it causes all locks //# held by this process on the table to be released. So reacquire //# them when that happens. Bool tableChanged = True; while (tableChanged) { tableChanged = False; if (! lockPtr_p->acquire (&(lockSync_p.memoryIO()), type, nattempts)) { return False; } if (!noSync) { // Older readonly table files may have empty locksync data. // Skip the sync-ing in that case. uInt ncolumn; rownr_t nrrow; if (! lockSync_p.read (nrrow, ncolumn, tableChanged, colSetPtr_p->dataManChanged())) { tableChanged = False; } else { if (ncolumn != tableDesc().ncolumn()) { throw (TableError ("Table::lock cannot sync table " + tableName() + "; another process " "changed the number of columns")); } nrrow_p = colSetPtr_p->resync (nrrow, False); if (tableChanged && ncolumn > 0) { syncTable(); } } } } return True; } void PlainTable::syncTable() { // Something changed in the table file itself. // Reread it into a PlainTable object (don't add it to the cache). // Use a different locknr for it to preserve possible existing locks. std::shared_ptr btab = Table::makeBaseTable (tableName(), "", Table::Old, TableLock(TableLock::PermanentLocking), TSMOption(TSMOption::Buffer,0,0), False, 1); PlainTable* tab = dynamic_cast(btab.get()); AlwaysAssert (tab, AipsError); TableAttr defaultAttr (tableName(), isWritable(), lockOptions()); // Now check if all columns are the same. // Update the column keywords. colSetPtr_p->syncColumns (*tab->colSetPtr_p, defaultAttr); // Adjust the attributes of subtables. // Update the table keywords. TableRecord& oldKeySet = keywordSet(); TableRecord& newKeySet = tab->keywordSet(); newKeySet.setTableAttr (oldKeySet, defaultAttr); oldKeySet = newKeySet; } void PlainTable::unlock() { lockPtr_p->release(); } void PlainTable::autoReleaseLock (Bool always) { lockPtr_p->autoRelease (always); } void PlainTable::setTableChanged() { tableChanged_p = True; } uInt PlainTable::getModifyCounter() const { return lockSync_p.getModifyCounter(); } void PlainTable::flush (Bool fsync, Bool recursive) { if (openedForWrite()) { putFile (False); // Flush subtables if wanted. if (recursive) { keywordSet().flushTables (fsync); } } } void PlainTable::resync() { TableTrace::traceFile (itsTraceId, "resync"); Bool tableChanged = True; lockPtr_p->getInfo (lockSync_p.memoryIO()); // Older readonly table files may have empty locksync data. // Skip the sync-ing in that case. uInt ncolumn; rownr_t nrrow; if (lockSync_p.read (nrrow, ncolumn, tableChanged, colSetPtr_p->dataManChanged())) { if (ncolumn != tableDesc().ncolumn()) { throw (TableError ("Table::resync cannot sync table " + tableName() + "; another process " "changed the number of columns")); } nrrow_p = colSetPtr_p->resync (nrrow, True); if (tableChanged && ncolumn > 0) { syncTable(); } } } Bool PlainTable::putFile (Bool always) { TableTrace::traceFile (itsTraceId, "flush"); Bool writeTab = always || tableChanged_p; Bool written = writeTab; { // use scope to ensure AipsIO is closed (thus flushed) before lockfile AipsIO ios; TableAttr attr(tableName()); if (writeTab) { #ifdef AIPS_TRACE cout << " full PlainTable::putFile" << endl; #endif writeStart (ios, bigEndian_p); ios << "PlainTable"; tdescPtr_p->putFile (ios, attr); // write description colSetPtr_p->putFile (True, ios, attr, False); // write column data writeEnd (ios); //# Write the TableInfo. flushTableInfo(); } else { //# Tell the data managers to write their data only. if (colSetPtr_p->putFile (False, ios, attr, False)) { written = True; #ifdef AIPS_TRACE cout << " data PlainTable::putFile on " << tableName() << endl; #endif } } } // Write the change info if anything has been written. if (written) { lockSync_p.write (nrrow_p, tdescPtr_p->ncolumn(), tableChanged_p, colSetPtr_p->dataManChanged()); lockPtr_p->putInfo (lockSync_p.memoryIO()); } // Clear the change-flags for the next round. tableChanged_p = False; colSetPtr_p->dataManChanged() = False; return writeTab; } MemoryIO* PlainTable::releaseCallBack (void* plainTableObject, Bool always) { return (*(PlainTable*)plainTableObject).doReleaseCallBack (always); } MemoryIO* PlainTable::doReleaseCallBack (Bool always) { //# Invalidate the caches in the columns to be sure //# that the next get on a column reacquires a lock. colSetPtr_p->invalidateColumnCaches(); //# Data does not need to be written when not opened for write. if (!openedForWrite()) { return 0; } putFile (always); return 0; } //# Test if the table is writable. Bool PlainTable::isWritable() const { if (option_p == Table::Old || option_p == Table::Delete) { return False; } return True; } // Get the actual table description. TableDesc PlainTable::actualTableDesc() const { return colSetPtr_p->actualTableDesc(); } // Get the data manager info. Record PlainTable::dataManagerInfo() const { return colSetPtr_p->dataManagerInfo(); } //# Get access to the keyword set. TableRecord& PlainTable::keywordSet() { Bool hasLocked = colSetPtr_p->userLock (FileLocker::Read, True); colSetPtr_p->checkReadLock (True); TableRecord& rec = tdescPtr_p->rwKeywordSet(); colSetPtr_p->userUnlock (hasLocked); return rec; } TableRecord& PlainTable::rwKeywordSet() { colSetPtr_p->checkWriteLock (True); TableRecord& rec = tdescPtr_p->rwKeywordSet(); tableChanged_p = True; return rec; } //# Get a column object. BaseColumn* PlainTable::getColumn (uInt columnIndex) const { return colSetPtr_p->getColumn (columnIndex); } BaseColumn* PlainTable::getColumn (const String& columnName) const { return colSetPtr_p->getColumn (columnName); } //# The data managers have to be inspected to tell if adding and removing //# of rows and columns is possible. Bool PlainTable::canAddRow() const { return colSetPtr_p->canAddRow(); } Bool PlainTable::canRemoveRow() const { return colSetPtr_p->canRemoveRow(); } Bool PlainTable::canRemoveColumn (const Vector& columnNames) const { if (!checkRemoveColumn (columnNames, False)) { return False; } return colSetPtr_p->canRemoveColumn (columnNames); } //# Renaming a column is possible. Bool PlainTable::canRenameColumn (const String& columnName) const { return colSetPtr_p->canRenameColumn (columnName); } //# Add rows. void PlainTable::addRow (rownr_t nrrw, Bool initialize) { if (nrrw > 0) { checkWritable("addRow"); //# Locking has to be done here, otherwise nrrow_p is not up-to-date //# when autoReleaseLock releases the lock and writes the data. nrrowToAdd_p = nrrw; colSetPtr_p->checkWriteLock (True); colSetPtr_p->addRow (nrrw); if (initialize) { colSetPtr_p->initialize (nrrow_p, nrrow_p+nrrw-1); } nrrowToAdd_p = 0; nrrow_p += nrrw; colSetPtr_p->autoReleaseLock(); } } void PlainTable::removeRow (rownr_t rownr) { checkWritable("rowmoveRow"); //# Locking has to be done here, otherwise nrrow_p is not up-to-date //# when autoReleaseLock releases the lock and writes the data. colSetPtr_p->checkWriteLock (True); colSetPtr_p->removeRow (rownr); nrrow_p--; colSetPtr_p->autoReleaseLock(); } void PlainTable::addColumn (const ColumnDesc& columnDesc, Bool) { checkWritable("addColumn"); Table tab(this); colSetPtr_p->addColumn (columnDesc, bigEndian_p, tsmOption_p, tab); tableChanged_p = True; } void PlainTable::addColumn (const ColumnDesc& columnDesc, const String& dataManager, Bool byName, Bool) { checkWritable("addColumn"); Table tab(this); colSetPtr_p->addColumn (columnDesc, dataManager, byName, bigEndian_p, tsmOption_p, tab); tableChanged_p = True; } void PlainTable::addColumn (const ColumnDesc& columnDesc, const DataManager& dataManager, Bool) { checkWritable("addColumn"); Table tab(this); colSetPtr_p->addColumn (columnDesc, dataManager, bigEndian_p, tsmOption_p, tab); tableChanged_p = True; } void PlainTable::addColumn (const TableDesc& tableDesc, const DataManager& dataManager, Bool) { checkWritable("addColumn"); Table tab(this); colSetPtr_p->addColumn (tableDesc, dataManager, bigEndian_p, tsmOption_p, tab); tableChanged_p = True; } void PlainTable::removeColumn (const Vector& columnNames) { checkWritable("removeColumn"); colSetPtr_p->removeColumn (columnNames); tableChanged_p = True; } void PlainTable::renameColumn (const String& newName, const String& oldName) { checkWritable("renameColumn"); colSetPtr_p->renameColumn (newName, oldName); tableChanged_p = True; } void PlainTable::renameHypercolumn (const String& newName, const String& oldName) { checkWritable("renameHyperColumn"); tdescPtr_p->renameHypercolumn (newName, oldName); tableChanged_p = True; } DataManager* PlainTable::findDataManager (const String& name, Bool byColumn) const { return colSetPtr_p->findDataManager (name, byColumn); } ByteIO::OpenOption PlainTable::toAipsIOFoption (int tabOpt) { switch (tabOpt) { case Table::Old: case Table::Delete: return ByteIO::Old; case Table::Update: return ByteIO::Update; case Table::New: return ByteIO::New; case Table::NewNoReplace: return ByteIO::NewNoReplace; case Table::Scratch: return ByteIO::Scratch; } //# This statement is only there to satisfy strict compilers. return ByteIO::Scratch; } void PlainTable::setEndian (int endianFormat) { int endOpt = endianFormat; if (endOpt == Table::AipsrcEndian) { String opt; // Default "big" was used until version 10.1203.00. AipsrcValue::find (opt, "table.endianformat", "local"); opt.downcase(); if (opt == "big") { endOpt = Table::BigEndian; } else if (opt == "little") { endOpt = Table::LittleEndian; } else { endOpt = Table::LocalEndian; } } if (endOpt == Table::LocalEndian) { bigEndian_p = HostInfo::bigEndian(); } else { bigEndian_p = True; if (endOpt == Table::LittleEndian) { bigEndian_p = False; } } } void PlainTable::checkWritable (const char* func) const { if (! isWritable()) { throw (TableInvOper ("Table::" + String(func) + "; table " + tableName() + " is not writable")); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/PlainTable.h000066400000000000000000000274711476623553700202070ustar00rootroot00000000000000//# PlainTable.h: Class defining a plain regular table //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_PLAINTABLE_H #define TABLES_PLAINTABLE_H //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class SetupNewTable; class TableLock; class TableLockData; class ColumnSet; class IPosition; class AipsIO; class MemoryIO; // // Class defining a plain regular table // // // // // //# Classes you should understand before using this one. //
      • BaseTable //
      • BaseColumn // // // PlainTable represents a plain regular table. This is opposed to a // RefTable, which is a view on a PlainTable. // // // PlainTable is a table consisting of a keyword set and a number of // filled and virtual columns. The table control information and the // keyword set is stored in an AipsIO file. The data in the filled columns // are stored separately by storage managers. //
        The PlainTable class structure is shown in this // UML diagram. //
        // //# A List of bugs, limitations, extensions or planned refinements. //
      • notify RefTable's when deleting rows // class PlainTable : public BaseTable { public: // Construct the object for a new table. // It creates storage manager(s) for unbound columns and initializes // all storage managers. The given number of rows is stored in // the table and initialized if the flag is set. PlainTable (SetupNewTable&, rownr_t nrrow, Bool initialize, const TableLock& lockOptions, int endianFormat, const TSMOption& tsmOption); #ifdef HAVE_MPI // MPI version of the constructor PlainTable (MPI_Comm mpiComm, SetupNewTable&, rownr_t nrrow, Bool initialize, const TableLock& lockOptions, int endianFormat, const TSMOption& tsmOption); #endif // Common part of the constructor shared by MPI and non-MPI void PlainTableCommon (SetupNewTable&, rownr_t nrrow, Bool initialize, const TableLock& lockOptions, int endianFormat, const TSMOption& tsmOption); // Construct the object for an existing table. // It opens the table file, reads the table control information // and creates and initializes the required storage managers. PlainTable (AipsIO&, uInt version, const String& name, const String& type, rownr_t nrrow, int option, const TableLock& lockOptions, const TSMOption& tsmOption, Bool addToCache, uInt locknr); // The destructor flushes (i.e. writes) the table if it is opened // for output and not marked for delete. virtual ~PlainTable(); // Copy constructor is forbidden, because copying a table requires // some more knowledge (like table name of result). PlainTable (const PlainTable&) = delete; // Assignment is forbidden, because copying a table requires // some more knowledge (like table name of result). PlainTable& operator= (const PlainTable&) = delete; // Return the layout of a table (i.e. description and #rows). // This function has the advantage that only the minimal amount of // information required is read from the table, thus it is much // faster than a normal table open. //
        The number of rows is returned. The description of the table // is stored in desc (its contents will be overwritten). static void getLayout (TableDesc& desc, AipsIO& ios); // Try to reopen the table for read/write access. // An exception is thrown if the table is not writable. // Nothing is done if the table is already open for read/write. virtual void reopenRW(); // Is the table stored in big or little endian format? virtual Bool asBigEndian() const; // Get the storage option used for the table. virtual const StorageOption& storageOption() const; // Is the table in use (i.e. open) in another process? // If checkSubTables is set, it is also checked if // a subtable is used in another process. virtual Bool isMultiUsed (Bool checkSubTables) const; // Get the locking info. virtual const TableLock& lockOptions() const; // Merge the given lock info with the existing one. virtual void mergeLock (const TableLock& lockOptions); // Has this process the read or write lock, thus can the table // be read or written safely? virtual Bool hasLock (FileLocker::LockType) const; // Try to lock the table for read or write access. virtual Bool lock (FileLocker::LockType, uInt nattempts); // Unlock the table. This will also synchronize the table data, // thus force the data to be written to disk. virtual void unlock(); // Do a release of an AutoLock when the inspection interval has expired. // always=True means that the inspection is always done, // thus not every 25th call or so. void autoReleaseLock (Bool always = False); // Flush the table, i.e. write it to disk. // Nothing will be done if the table is not writable. // A flush can be executed at any time. // When a table is marked for delete, the destructor will remove // files written by intermediate flushes. // Note that if necessary the destructor will do an implicit flush, // unless it is executed due to an exception. virtual void flush (Bool fsync, Bool recursive); // Resync the Table object with the table file. virtual void resync(); // Get the modify counter. virtual uInt getModifyCounter() const; // Set the table to being changed. virtual void setTableChanged(); // Convert a Table option to an AipsIO file option. // This is used by storage managers. static ByteIO::OpenOption toAipsIOFoption (int tableOption); // Test if the table is opened as writable. virtual Bool isWritable() const; // Get the actual table description. virtual TableDesc actualTableDesc() const; // Get the data manager info. virtual Record dataManagerInfo() const; // Get readonly access to the table keyword set. virtual TableRecord& keywordSet(); // Get read/write access to the table keyword set. // This requires that the table is locked (or it gets locked // when using AutoLocking mode). virtual TableRecord& rwKeywordSet(); // Get a column object using its index. virtual BaseColumn* getColumn (uInt columnIndex) const; // Get a column object using its name. virtual BaseColumn* getColumn (const String& columnName) const; // Test if it is possible to add a row to this table. virtual Bool canAddRow() const; // Add one or more rows and possibly initialize them. // This will fail for tables not supporting addition of rows. virtual void addRow (rownr_t nrrow, Bool initialize); // Test if it is possible to remove a row from this table. virtual Bool canRemoveRow() const; // Remove the given row. // This will fail for tables not supporting removal of rows. virtual void removeRow (rownr_t rownr); // Add a column to the table. // The last Bool argument is not used in PlainTable, but can be used in // other classes derived from BaseTable. // virtual void addColumn (const ColumnDesc& columnDesc, Bool); virtual void addColumn (const ColumnDesc& columnDesc, const String& dataManager, Bool byName, Bool); virtual void addColumn (const ColumnDesc& columnDesc, const DataManager& dataManager, Bool); virtual void addColumn (const TableDesc& tableDesc, const DataManager& dataManager, Bool); // // Test if columns can be removed. virtual Bool canRemoveColumn (const Vector& columnNames) const; // Remove columns. virtual void removeColumn (const Vector& columnNames); // Test if a column can be renamed (yes). virtual Bool canRenameColumn (const String& columnName) const; // Rename a column. virtual void renameColumn (const String& newName, const String& oldName); // Rename a hypercolumn. virtual void renameHypercolumn (const String& newName, const String& oldName); // Find the data manager with the given name or for the given column. virtual DataManager* findDataManager (const String& name, Bool byColumn) const; // Get access to the TableCache. static TableCache& tableCache() { return theirTableCache; } private: // Close the object which is called by the destructor. void closeObject(); // Rename the subtables (used by rename function). virtual void renameSubTables (const String& newName, const String& oldName); // The callback function when a lock is released. // This flushes the table data, writes the synchronization data // into the MemoryIO object, and returns a pointer to it. // static MemoryIO* releaseCallBack (void* plainTableObject, Bool always); MemoryIO* doReleaseCallBack (Bool always); // // When needed, write the table control information in an AipsIO file. // Tell the storage managers to flush and close their files. // It returns a switch to tell if the table control information has // been written. Bool putFile (Bool always); // Synchronize the table after having acquired a lock which says // that main table data has changed. // It check if the columns did not change. // It updates the table and column keywords. void syncTable(); // Determine and set the endian format (big or little). void setEndian (int endianFormat); // Throw an exception if the table is not writable. void checkWritable (const char* func) const; std::shared_ptr colSetPtr_p; //# pointer to set of columns Bool tableChanged_p; //# Has the main data changed? Bool addToCache_p; //# Is table added to cache? TableLockData* lockPtr_p; //# pointer to lock object TableSyncData lockSync_p; //# table synchronization Bool bigEndian_p; //# True = big endian canonical //# False = little endian canonical TSMOption tsmOption_p; //# cache of open (plain) tables static TableCache theirTableCache; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/ReadAsciiTable.cc000066400000000000000000001310131476623553700211120ustar00rootroot00000000000000//# ReadAsciiTable.cc: Filling a table from an Ascii file //# Copyright (C) 1993,1994,1995,1996,1997,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // needed for file IO #include // needed for internal IO namespace casacore { //# NAMESPACE CASACORE - BEGIN const Int lineSize = 32768; //# Helper function. //# Read a line and ignore lines to be skipped. Bool ReadAsciiTable::getLine (ifstream& file, Int& lineNumber, char* line, Int lineSize, Bool testComment, const Regex& commentMarker, Int firstLine, Int lastLine) { Int dummy; while (True) { if (! file.getline (line, lineSize)) { return False; } Int nch = file.gcount(); // Remove linefeed or newline. if (nch > 0) nch--; // Remove possible carriage return. if (nch > 1 && line[nch-1] == '\r') { nch--; } line[nch] = '\0'; lineNumber++; if (lineNumber >= firstLine) { if (lastLine <= 0 || lineNumber <= lastLine) { if (! testComment) { return True; } if (commentMarker.find (line, nch, dummy) != 0) { return True; } } } } } //# Helper function //# It gets the next value from a line and stores it in result. //# It updates at and returns the length of the value retrieved. //# Quotes around strings are removed //# -1 is returned if no more values are found. Int ReadAsciiTable::getNext (const Char* string, Int strlen, Char* result, Int& at, Char separator) { Int i = 0; Bool found = False; Bool quoted = False; Char ihave; // The next few lines are needed to treat e.g. a trailing comma as // a value. Bool hasNext = False; if (at < 0) { at = -at; hasNext = True; } for (; at= 0) { if (string2[0] == '\0') { string1[0] = 'A'; } else { str = string2; if (str.matches (RXint)) { string1[0] = 'I'; } else if (str.matches (RXdouble)) { string1[0] = 'D'; } else { string1[0] = 'A'; } } string1++; char name[24]; i++; snprintf (name, sizeof(name), " Column%i", i); strcpy (string2, name); string2 += strlen(name); string2[0] = '\0'; if (shape.nelements() > 0) { ostringstream ostr; for (uInt i=0; i 0) { ostr << ','; } ostr << shape(i); } // There is probably a way to attach the char * to the ostringstream // but I'm not going to worry about it. wky 2003/02/27 strcpy(string1, ostr.str().data()); break; } string1[0] = ' '; string1++; string1[0] = '\0'; } } //# Convert a string to a Bool Bool ReadAsciiTable::makeBool (const String& str) { if (str.length() == 0 || str == "0" || str[0] == 'F' || str[0] == 'f' || str[0] == 'N' || str[0] == 'n') { return False; } return True; } //# Read a keyword set and add it to keysets. void ReadAsciiTable::handleKeyset (Int lineSize, char* string1, char* first, char* second, TableRecord& keysets, LogIO& logger, const String& fileName, ifstream& jFile, Int& lineNumber, Char separator, Bool testComment, const Regex& commentMarker, Int firstLine, Int lastLine) { TableRecord keyset; // Get the column name in case it is a column keywordset. String colName; Int atl = 0; getNext (string1, lineSize, first, atl, ' '); Int d4 = getNext (string1, lineSize, second, atl, ' '); if (d4 > 0) { colName = second; } while (True) { // Read the next line(s) if (!getLine (jFile, lineNumber, string1, lineSize, testComment, commentMarker, firstLine, lastLine)) { throw AipsError ("ReadAsciiTable: no .endkey line in " + fileName); } // If we are at END of KEYWORDS read the next line to get NAMES OF COLUMNS // or to get next keyword group. if (strncmp(string1, ".endkey", 7) == 0) { if (!getLine (jFile, lineNumber, string1, lineSize, testComment, commentMarker, firstLine, lastLine)) { string1[0] = '\0'; } break; } // Read the first two fields (name and type) of a KEYWORD line Int at3=0; Int done3 = getNext (string1, lineSize, first, at3, ' '); Int done4 = getNext (string1, lineSize, second, at3, ' '); if (done3<=0 || done4<=0) { throw AipsError ("ReadAsciiTable: no keyword name or type in line " + String::toString(lineNumber) + " of " + fileName); } String keyName = String(first); String keyType = String(second); keyType.upcase(); if (keyset.isDefined (keyName)) { logger << LogIO::WARN << "Keyword " << keyName << " skipped because defined twice in " << fileName << LogIO::POST; } else { // Convert the type string to shape and type. IPosition keyShape; Int keyRAT; Int varAxis = getTypeShape (keyType, keyShape, keyRAT); // If no shape is given, the keyword can be a vector. Bool shpDefined = keyShape.nelements() > 0; if (!shpDefined) { keyShape = IPosition(1,1); varAxis = 0; } // Get the keyword values from the line and store them in the set. switch (keyRAT) { case RATBool: { Block values; IPosition shp = getArray (string1, lineSize, first, at3, separator, keyShape, varAxis, keyRAT, &values); if (!shpDefined && shp(0) == 1) { keyset.define (keyName, values[0]); } else { Array array(shp, values.storage(), SHARE); keyset.define (keyName, array); } } break; case RATShort: { Block values; IPosition shp = getArray (string1, lineSize, first, at3, separator, keyShape, varAxis, keyRAT, &values); if (!shpDefined && shp(0) == 1) { keyset.define (keyName, values[0]); } else { Array array(shp, values.storage(), SHARE); keyset.define (keyName, array); } } break; case RATInt: { Block values; IPosition shp = getArray (string1, lineSize, first, at3, separator, keyShape, varAxis, keyRAT, &values); if (!shpDefined && shp(0) == 1) { keyset.define (keyName, values[0]); } else { Array array(shp, values.storage(), SHARE); keyset.define (keyName, array); } } break; case RATFloat: { Block values; IPosition shp = getArray (string1, lineSize, first, at3, separator, keyShape, varAxis, keyRAT, &values); if (!shpDefined && shp(0) == 1) { keyset.define (keyName, values[0]); } else { Array array(shp, values.storage(), SHARE); keyset.define (keyName, array); } } break; case RATDouble: case RATDMS: case RATHMS: { Block values; IPosition shp = getArray (string1, lineSize, first, at3, separator, keyShape, varAxis, keyRAT, &values); if (!shpDefined && shp(0) == 1) { keyset.define (keyName, values[0]); } else { Array array(shp, values.storage(), SHARE); keyset.define (keyName, array); } } break; case RATString: { Block values; IPosition shp = getArray (string1, lineSize, first, at3, separator, keyShape, varAxis, keyRAT, &values); if (!shpDefined && shp(0) == 1) { keyset.define (keyName, values[0]); } else { Array array(shp, values.storage(), SHARE); keyset.define (keyName, array); } } break; case RATComX: case RATComZ: { Block values; IPosition shp = getArray (string1, lineSize, first, at3, separator, keyShape, varAxis, keyRAT, &values); if (!shpDefined && shp(0) == 1) { keyset.define (keyName, values[0]); } else { Array array(shp, values.storage(), SHARE); keyset.define (keyName, array); } } break; case RATDComX: case RATDComZ: { Block values; IPosition shp = getArray (string1, lineSize, first, at3, separator, keyShape, varAxis, keyRAT, &values); if (!shpDefined && shp(0) == 1) { keyset.define (keyName, values[0]); } else { Array array(shp, values.storage(), SHARE); keyset.define (keyName, array); } } break; } } } if (keysets.isDefined (colName)) { logger << LogIO::WARN << "Keywordset of column " << colName << " skipped because defined twice in " << fileName << LogIO::POST; } else { keysets.defineRecord (colName, keyset); } } Int ReadAsciiTable::getTypeShape (const String& typestr, IPosition& shape, Int& type) { shape.resize (0); Int varAxis = -1; // Split at each comma. Vector vec = stringToVector (typestr); // The first value can be something like I10, so find first digit. // It should have a type before the first digit. uInt pos = vec(0).find (Regex("[0-9]")); if (pos == 0) { throw AipsError ("ReadAsciiTable: no type info in type string '" + typestr + "'"); } // Get type without shape info. // Note: need to convert pos to an Int because some compilers are more picky // about type safety, i.e. the native compilers for SGI and SUN. String tp = vec(0).before (Int(pos)); if (pos >= vec(0).length()) { vec(0) = String(); // Clear vector if no shape given at all. if (vec.nelements() == 1) { vec.resize (0); } } else { // Keep only length in first value. vec(0) = vec(0).from(Int(pos)); } shape.resize (vec.nelements()); Regex num("[0-9]+"); // Check value and convert to integers. // One variable shaped axis is possible. for (uInt i=0; i> shape(i); if (shape(i) <= 0) { if (varAxis >= 0) { throw AipsError ("ReadAsciiTable: multiple variable axes in " "type string '" + typestr + "'"); } varAxis = i; shape(i) = 1; } } if (tp == "B") { type = RATBool; } else if (tp == "S") { type = RATShort; } else if (tp == "I") { type = RATInt; } else if (tp == "R") { type = RATFloat; } else if (tp == "D") { type = RATDouble; } else if (tp == "DMS") { type = RATDMS; } else if (tp == "HMS") { type = RATHMS; } else if (tp == "A") { type = RATString; } else if (tp == "X") { type = RATComX; } else if (tp == "Z") { type = RATComZ; } else if (tp == "DX") { type = RATDComX; } else if (tp == "DZ") { type = RATDComZ; } else { throw AipsError ("ReadAsciiTable: invalid type specifier '" + tp + "'"); } return varAxis; } double ReadAsciiTable::stringToPos (const String& str, Bool isDMS) { // This function is a bit more relaxed than MVAngle::read. // It allows whitespace. Furthermore it allows whitespace as separator. String strc(str); strc.downcase(); // Remove blanks and insert : if only blanks. // Insert 0 if nothing between separators. String pos; Bool foundBlanks = False; Bool needSep = False; Bool needNum = True; pos.reserve (strc.size()); for (uInt i=0; i 0) { istringstream(dum) >> *(Short*)value; } else { *(Short*)value = 0; } break; case RATInt: if (done1 > 0) { istringstream(dum) >> *(Int*)value; } else { *(Int*)value = 0; } break; case RATFloat: if (done1 > 0) { istringstream(dum) >> *(Float*)value; } else { *(Float*)value = 0; } break; case RATDouble: if (done1 > 0) { istringstream(dum) >> *(Double*)value; } else { *(Double*)value = 0; } break; case RATString: *(String*)value = String(first, done1); break; case RATDMS: *(Double*)value = stringToPos (String(first, done1), True); break; case RATHMS: *(Double*)value = stringToPos (String(first, done1), False); break; case RATComX: if (done1 > 0) { istringstream(dum) >> f1; } done1 = getNext (string1, lineSize, first, at1, separator); if (done1 > 0) { String dum2(first, done1); istringstream(dum2) >> f2; } *(Complex*)value = Complex(f1, f2); break; case RATDComX: if (done1 > 0) { istringstream(dum) >> d1; } done1 = getNext (string1, lineSize, first, at1, separator); if (done1 > 0) { String dum2(first, done1); istringstream(dum2) >> d2; } *(DComplex*)value = DComplex(d1, d2); break; case RATComZ: if (done1 > 0) { istringstream(dum) >> f1; } done1 = getNext (string1, lineSize, first, at1, separator); if (done1 > 0) { String dum2(first, done1); istringstream(dum2) >> f2; } f2 *= 3.14159265/180.0; *(Complex*)value = Complex(f1*cos(f2), f1*sin(f2)); break; case RATDComZ: if (done1 > 0) { istringstream(dum) >> d1; } done1 = getNext (string1, lineSize, first, at1, separator); if (done1 > 0) { String dum2(first, done1); istringstream(dum2) >> d2; } d2 *= 3.14159265/180.0; *(DComplex*)value = DComplex(d1*cos(d2), d1*sin(d2)); break; } } return more; } void ReadAsciiTable::handleScalar (char* string1, Int lineSize, char* first, Int& at1, Char separator, Int type, TableColumn& tabcol, rownr_t rownr) { switch (type) { case RATBool: { Bool value = False; getValue (string1, lineSize, first, at1, separator, type, &value); tabcol.putScalar (rownr, value); } break; case RATShort: { Short value = 0; getValue (string1, lineSize, first, at1, separator, type, &value); tabcol.putScalar (rownr, value); } break; case RATInt: { Int value = 0; getValue (string1, lineSize, first, at1, separator, type, &value); tabcol.putScalar (rownr, value); } break; case RATFloat: { Float value = 0; getValue (string1, lineSize, first, at1, separator, type, &value); tabcol.putScalar (rownr, value); } break; case RATDouble: case RATDMS: case RATHMS: { Double value = 0; getValue (string1, lineSize, first, at1, separator, type, &value); tabcol.putScalar (rownr, value); } break; case RATString: { String value; getValue (string1, lineSize, first, at1, separator, type, &value); tabcol.putScalar (rownr, value); } break; case RATComX: case RATComZ: { Complex value; getValue (string1, lineSize, first, at1, separator, type, &value); tabcol.putScalar (rownr, value); } break; case RATDComX: case RATDComZ: { DComplex value; getValue (string1, lineSize, first, at1, separator, type, &value); tabcol.putScalar (rownr, value); } break; } } IPosition ReadAsciiTable::getArray (char* string1, Int lineSize, char* first, Int& at1, Char separator, const IPosition& shape, Int varAxis, Int type, void* valueBlock) { IPosition shp(shape); uInt nelem = shp.product(); uInt nfound = 0; switch (type) { case RATBool: { Block& data = *(Block*)valueBlock; data.resize (nelem); data = False; Bool value; while (getValue (string1, lineSize, first, at1, separator, type, &value)) { if (nfound == data.nelements()) { data.resize (2*nfound, True, True); } data[nfound++] = value; if (varAxis < 0 && nfound == data.nelements()) { break; } } if (varAxis >= 0) { shp(varAxis) = (nfound + nelem - 1) / nelem; nelem = shp.product(); if (nelem > nfound) { if (nelem > data.nelements()) { data.resize (nelem, True, True); } objset (&data[nfound], False, nelem-nfound); } } } break; case RATShort: { Block& data = *(Block*)valueBlock; data.resize (nelem); data = Short(0); Short value; while (getValue (string1, lineSize, first, at1, separator, type, &value)) { if (nfound == data.nelements()) { data.resize (2*nfound, True, True); } data[nfound++] = value; if (varAxis < 0 && nfound == data.nelements()) { break; } } if (varAxis >= 0) { shp(varAxis) = (nfound + nelem - 1) / nelem; nelem = shp.product(); if (nelem > nfound) { if (nelem > data.nelements()) { data.resize (nelem, True, True); } objset (&data[nfound], Short(0), nelem-nfound); } } } break; case RATInt: { Block& data = *(Block*)valueBlock; data.resize (nelem); data = False; Int value; while (getValue (string1, lineSize, first, at1, separator, type, &value)) { if (nfound == data.nelements()) { data.resize (2*nfound, True, True); } data[nfound++] = value; if (varAxis < 0 && nfound == data.nelements()) { break; } } if (varAxis >= 0) { shp(varAxis) = (nfound + nelem - 1) / nelem; nelem = shp.product(); if (nelem > nfound) { if (nelem > data.nelements()) { data.resize (nelem, True, True); } objset (&data[nfound], 0, nelem-nfound); } } } break; case RATFloat: { Block& data = *(Block*)valueBlock; data.resize (nelem); data = Float(0); Float value; while (getValue (string1, lineSize, first, at1, separator, type, &value)) { if (nfound == data.nelements()) { data.resize (2*nfound, True, True); } data[nfound++] = value; if (varAxis < 0 && nfound == data.nelements()) { break; } } if (varAxis >= 0) { shp(varAxis) = (nfound + nelem - 1) / nelem; nelem = shp.product(); if (nelem > nfound) { if (nelem > data.nelements()) { data.resize (nelem, True, True); } objset (&data[nfound], Float(0), nelem-nfound); } } } break; case RATDouble: case RATDMS: case RATHMS: { Block& data = *(Block*)valueBlock; data.resize (nelem); data = Double(0); Double value; while (getValue (string1, lineSize, first, at1, separator, type, &value)) { if (nfound == data.nelements()) { data.resize (2*nfound, True, True); } data[nfound++] = value; if (varAxis < 0 && nfound == data.nelements()) { break; } } if (varAxis >= 0) { shp(varAxis) = (nfound + nelem - 1) / nelem; nelem = shp.product(); if (nelem > nfound) { if (nelem > data.nelements()) { data.resize (nelem, True, True); } objset (&data[nfound], Double(0), nelem-nfound); } } } break; case RATString: { Block& data = *(Block*)valueBlock; data.resize (nelem); data = String(); String value; while (getValue (string1, lineSize, first, at1, separator, type, &value)) { if (nfound == data.nelements()) { data.resize (2*nfound, True, True); } data[nfound++] = value; if (varAxis < 0 && nfound == data.nelements()) { break; } } if (varAxis >= 0) { shp(varAxis) = (nfound + nelem - 1) / nelem; nelem = shp.product(); if (nelem > nfound) { if (nelem > data.nelements()) { data.resize (nelem, True, True); } objset (&data[nfound], String(), nelem-nfound); } } } break; case RATComX: case RATComZ: { Block& data = *(Block*)valueBlock; data.resize (nelem); data = Complex(); Complex value; while (getValue (string1, lineSize, first, at1, separator, type, &value)) { if (nfound == data.nelements()) { data.resize (2*nfound, True, True); } data[nfound++] = value; if (varAxis < 0 && nfound == data.nelements()) { break; } } if (varAxis >= 0) { shp(varAxis) = (nfound + nelem - 1) / nelem; nelem = shp.product(); if (nelem > nfound) { if (nelem > data.nelements()) { data.resize (nelem, True, True); } objset (&data[nfound], Complex(), nelem-nfound); } } } break; case RATDComX: case RATDComZ: { Block& data = *(Block*)valueBlock; data.resize (nelem); data = DComplex(); DComplex value; while (getValue (string1, lineSize, first, at1, separator, type, &value)) { if (nfound == data.nelements()) { data.resize (2*nfound, True, True); } data[nfound++] = value; if (varAxis < 0 && nfound == data.nelements()) { break; } } if (varAxis >= 0) { shp(varAxis) = (nfound + nelem - 1) / nelem; nelem = shp.product(); if (nelem > nfound) { if (nelem > data.nelements()) { data.resize (nelem, True, True); } objset (&data[nfound], DComplex(), nelem-nfound); } } } break; } return shp; } void ReadAsciiTable::handleArray (char* string1, Int lineSize, char* first, Int& at1, Char separator, const IPosition& shape, Int varAxis, Int type, TableColumn& tabcol, rownr_t rownr) { switch (type) { case RATBool: { Block data; IPosition shp = getArray (string1, lineSize, first, at1, separator, shape, varAxis, type, &data); Array array(shp, data.storage(), SHARE); ArrayColumn(tabcol).put (rownr, array); } break; case RATShort: { Block data; IPosition shp = getArray (string1, lineSize, first, at1, separator, shape, varAxis, type, &data); Array array(shp, data.storage(), SHARE); ArrayColumn(tabcol).put (rownr, array); } break; case RATInt: { Block data; IPosition shp = getArray (string1, lineSize, first, at1, separator, shape, varAxis, type, &data); Array array(shp, data.storage(), SHARE); ArrayColumn(tabcol).put (rownr, array); } break; case RATFloat: { Block data; IPosition shp = getArray (string1, lineSize, first, at1, separator, shape, varAxis, type, &data); Array array(shp, data.storage(), SHARE); ArrayColumn(tabcol).put (rownr, array); } break; case RATDouble: case RATDMS: case RATHMS: { Block data; IPosition shp = getArray (string1, lineSize, first, at1, separator, shape, varAxis, type, &data); Array array(shp, data.storage(), SHARE); ArrayColumn(tabcol).put (rownr, array); } break; case RATString: { Block data; IPosition shp = getArray (string1, lineSize, first, at1, separator, shape, varAxis, type, &data); Array array(shp, data.storage(), SHARE); ArrayColumn(tabcol).put (rownr, array); } break; case RATComX: case RATComZ: { Block data; IPosition shp = getArray (string1, lineSize, first, at1, separator, shape, varAxis, type, &data); Array array(shp, data.storage(), SHARE); ArrayColumn(tabcol).put (rownr, array); } break; case RATDComX: case RATDComZ: { Block data; IPosition shp = getArray (string1, lineSize, first, at1, separator, shape, varAxis, type, &data); Array array(shp, data.storage(), SHARE); ArrayColumn(tabcol).put (rownr, array); } break; } } Table ReadAsciiTable::makeTab (String& formatString, Table::TableType tableType, const String& headerfile, const String& filein, const String& tableproto, const String& tablename, Bool autoHeader, const IPosition& autoShape, const Vector& columnNames, const Vector& dataTypes, Char separator, Bool testComment, const Regex& commentMarker, Int firstLine, Int lastLine) { char string1[lineSize], string2[lineSize], stringsav[lineSize]; char first[lineSize], second[lineSize]; Block nameOfColumn(100); Block tstrOfColumn(100); String keyName; LogIO logger(LogOrigin("readAsciiTable", WHERE)); // Determine if column names are already given. Bool hdrGiven = False; if (columnNames.nelements() > 0 || dataTypes.nelements() > 0) { if (columnNames.nelements() != dataTypes.nelements()) { throw AipsError ("ReadAsciiTable: vector of columnNames and " "dataTypes should have equal length"); } hdrGiven = True; autoHeader = False; } // Determine if header and data are in one file. Bool oneFile = (headerfile == filein); Int firstHeaderLine = 1; Int lastHeaderLine = -1; if (oneFile) { firstHeaderLine = firstLine; lastHeaderLine = lastLine; } // PART ONE // Define the TABLE description, i.e. define its columns. // Create the description as scratch if no name is given. TableDesc td (tableproto, (tableproto.empty() ? TableDesc::Scratch : TableDesc::New)); ifstream jFile; Path headerPath(headerfile); String hdrName = headerPath.expandedName(); jFile.open(hdrName.chars(), ios::in); if (! jFile) { throw AipsError ("ReadAsciiTable: file " + hdrName + " not found or unreadable" ); } // Read the first line. It will be KEYWORDS or NAMES OF COLUMNS Int lineNumber = 0; if (!getLine (jFile, lineNumber, string1, lineSize, testComment, commentMarker, firstHeaderLine, lastHeaderLine)) { throw AipsError ("ReadAsciiTable: cannot read first header line of " + headerfile); } // If the first line shows that we have KEYWORDS read until the // end of keywords while assembling the keywords. TableRecord keysets; while (strncmp(string1, ".key", 4) == 0) { handleKeyset (lineSize, string1, first, second, keysets, logger, headerfile, jFile, lineNumber, separator, testComment, commentMarker, firstHeaderLine, lastHeaderLine); } // Okay, all keywords have been read. // string1 contains the next line (if any). // Read the column definition lines from header file (if needed). // Determine the types if autoheader is given. // Previous line should be NAMES OF COLUMNS; now get TYPE OF COLUMNS line if (!autoHeader && !hdrGiven) { if (string1[0] == '\0') { throw AipsError ("ReadAsciiTable: no COLUMN NAMES line in " + headerfile); } if (!getLine (jFile, lineNumber, string2, lineSize, testComment, commentMarker, firstHeaderLine, lastHeaderLine)) { throw AipsError ("ReadAsciiTable: no COLUMN TYPES line in " + headerfile); } } // Now open the actual data file (if not the same as header file). // Read the first line if auto header. if (!oneFile) { jFile.close(); Path filePath(filein); String fileName = filePath.expandedName(); jFile.open(fileName.chars(), ios::in); if (! jFile) { throw AipsError ("ReadAsciiTable: input file " + fileName + " not found or unreadable"); } lineNumber = 0; if (autoHeader) { if (!getLine (jFile, lineNumber, string1, lineSize, testComment, commentMarker, firstLine, lastLine)) { string1[0] = '\0'; } } } // Process the auto header. // Save string, because it'll be overwritten. stringsav[0] = '\0'; if (autoHeader) { strcpy (stringsav, string1); getTypes (autoShape, string1, lineSize, string2, first, separator); strcpy (string1, first); } else if (hdrGiven) { strcpy (stringsav, string1); } // Break up the NAME OF COLUMNS line and the TYPE OF COLUMNS line // Place the results in the two arrays. // Also put in in a single string to be returned to the caller. // The separator in a header line is the given separator if found in it. // Otherwise it is a blank. int nrcol = 0; if (hdrGiven) { nrcol = columnNames.size(); nameOfColumn.resize (nrcol); tstrOfColumn.resize (nrcol); for (int i=0; i= 0) { done1 = getNext (string1, lineSize, first, at1, sep1); done2 = getNext (string2, lineSize, second, at2, sep2); if (done1>0 && done2>0) { if (nrcol >= Int(nameOfColumn.nelements())) { nameOfColumn.resize (2*nrcol, True, True); tstrOfColumn.resize (2*nrcol, True, True); } nameOfColumn[nrcol] = String(first); tstrOfColumn[nrcol] = String(second); nrcol++; } else if (done1>=0 || done2>=0) { throw AipsError ("ReadAsciiTable: mismatching COLUMN NAMES " "and TYPES lines in " + headerfile); } } } // Generate a format string. String formStr; for (int i=0; i 0) { IPosition shape; Int option = 0; if (varAxis < 0) { shape = shapeOfColumn[i5]; option = ColumnDesc::Direct | ColumnDesc::FixedShape; } switch (typeOfColumn[i5]) { case RATBool: td.addColumn (ArrayColumnDesc (nameOfColumn[i5], shape, option)); break; case RATShort: td.addColumn (ArrayColumnDesc (nameOfColumn[i5], shape, option)); break; case RATInt: td.addColumn (ArrayColumnDesc (nameOfColumn[i5], shape, option)); break; case RATFloat: td.addColumn (ArrayColumnDesc (nameOfColumn[i5], shape, option)); break; case RATDouble: td.addColumn (ArrayColumnDesc (nameOfColumn[i5], shape, option)); break; case RATDMS: case RATHMS: { td.addColumn (ArrayColumnDesc (nameOfColumn[i5], shape, option)); ColumnDesc& cd = td.rwColumnDesc(nameOfColumn[i5]); cd.rwKeywordSet().define ("QuantumUnits", Vector(1, "rad")); } break; case RATString: td.addColumn (ArrayColumnDesc (nameOfColumn[i5], shape, option)); break; case RATComX: case RATComZ: td.addColumn (ArrayColumnDesc (nameOfColumn[i5], shape, option)); break; case RATDComX: case RATDComZ: td.addColumn (ArrayColumnDesc (nameOfColumn[i5], shape, option)); break; } } else { switch (typeOfColumn[i5]) { case RATBool: td.addColumn (ScalarColumnDesc (nameOfColumn[i5])); break; case RATShort: td.addColumn (ScalarColumnDesc (nameOfColumn[i5])); break; case RATInt: td.addColumn (ScalarColumnDesc (nameOfColumn[i5])); break; case RATFloat: td.addColumn (ScalarColumnDesc (nameOfColumn[i5])); break; case RATDouble: case RATDMS: case RATHMS: td.addColumn (ScalarColumnDesc (nameOfColumn[i5])); break; case RATString: td.addColumn (ScalarColumnDesc (nameOfColumn[i5])); break; case RATComX: case RATComZ: td.addColumn (ScalarColumnDesc (nameOfColumn[i5])); break; case RATDComX: case RATDComZ: td.addColumn (ScalarColumnDesc (nameOfColumn[i5])); break; } } } // PART TWO // The TableDesc has now been created. Start filling in the Table. // Use the default storage manager. SetupNewTable newtab(tablename, td, Table::New); Table tab(newtab, tableType); // Write keywordsets. for (uInt i=0; i 0) { Int varAx = (i6 == nrcol-1 ? varAxis : -1); handleArray (string1, lineSize, first, at1, separator, shapeOfColumn[i6], varAx, typeOfColumn[i6], tabcol[i6], rownr); } else { handleScalar (string1, lineSize, first, at1, separator, typeOfColumn[i6], tabcol[i6], rownr); } } rownr++; cont = getLine (jFile, lineNumber, string1, lineSize, testComment, commentMarker, firstLine, lastLine); } delete [] tabcol; jFile.close(); formatString = formStr; return tab; } String ReadAsciiTable::doRun (const String& headerfile, const String& filein, const String& tableproto, const String& tablename, Bool autoHeader, const IPosition& autoShape, const Vector& columnNames, const Vector& dataTypes, Char separator, Bool testComment, const Regex& commentMarker, Int firstLine, Int lastLine) { String formatString; Table tab = makeTab (formatString, Table::Plain, headerfile, filein, tableproto, tablename, autoHeader, autoShape, columnNames, dataTypes, separator, testComment, commentMarker, firstLine, lastLine); return formatString; } String ReadAsciiTable::run (const String& headerfile, const String& filein, const String& tableproto, const String& tablename, Bool autoHeader, const IPosition& autoShape, const Vector& columnNames, const Vector& dataTypes, Char separator, const String& commentMarkerRegex, Int firstLine, Int lastLine) { if (firstLine < 1) { firstLine = 1; } //# The Regex is made here (instead of creating a temporary Regex //# in the doRun call). //# For one reason or another the temporary gives a bus error with gcc-3.3 //# on Solaris in the tryerror calls in tReadAsciiTable. Regex regex; if (commentMarkerRegex.empty()) { return doRun (headerfile, filein, tableproto, tablename, autoHeader, autoShape, columnNames, dataTypes, separator, False, regex, firstLine, lastLine); } else { regex = Regex(commentMarkerRegex); return doRun (headerfile, filein, tableproto, tablename, autoHeader, autoShape, columnNames, dataTypes, separator, True, regex, firstLine, lastLine); } } Table ReadAsciiTable::runt (String& formatString, Table::TableType tableType, const String& headerfile, const String& filein, const String& tableproto, const String& tablename, Bool autoHeader, const IPosition& autoShape, const Vector& columnNames, const Vector& dataTypes, Char separator, const String& commentMarkerRegex, Int firstLine, Int lastLine) { if (firstLine < 1) { firstLine = 1; } //# The Regex is made here (instead of creating a temporary Regex //# in the doRun call). //# For one reason or another the temporary gives a bus error with gcc-3.3 //# on Solaris in the tryerror calls in tReadAsciiTable. Regex regex; if (commentMarkerRegex.empty()) { return makeTab (formatString, tableType, headerfile, filein, tableproto, tablename, autoHeader, autoShape, columnNames, dataTypes, separator, False, regex, firstLine, lastLine); } else { regex = Regex(commentMarkerRegex); return makeTab (formatString, tableType, headerfile, filein, tableproto, tablename, autoHeader, autoShape, columnNames, dataTypes, separator, True, regex, firstLine, lastLine); } } String readAsciiTable (const String& filein, const String& tableproto, const String& tablename, Bool autoHeader, Char separator, const String& commentMarkerRegex, Int firstLine, Int lastLine, const IPosition& autoShape) { Vector dumvec; return ReadAsciiTable::run (filein, filein, tableproto, tablename, autoHeader, autoShape, dumvec, dumvec, separator, commentMarkerRegex, firstLine, lastLine); } String readAsciiTable (const String& filein, const String& tableproto, const String& tablename, const Vector& columnNames, const Vector& dataTypes, Char separator, const String& commentMarkerRegex, Int firstLine, Int lastLine) { return ReadAsciiTable::run (filein, filein, tableproto, tablename, False, IPosition(), columnNames, dataTypes, separator, commentMarkerRegex, firstLine, lastLine); } String readAsciiTable (const String& headerfile, const String& filein, const String& tableproto, const String& tablename, Char separator, const String& commentMarkerRegex, Int firstLine, Int lastLine) { Vector dumvec; return ReadAsciiTable::run (headerfile, filein, tableproto, tablename, False, IPosition(), dumvec, dumvec, separator, commentMarkerRegex, firstLine, lastLine); } String readAsciiTable (const String& headerfile, const String& filein, const String& tableproto, const char* tablename, Char separator, const String& commentMarkerRegex, Int firstLine, Int lastLine) { Vector dumvec; return ReadAsciiTable::run (headerfile, filein, tableproto, String(tablename), False, IPosition(), dumvec, dumvec, separator, commentMarkerRegex, firstLine, lastLine); } Table readAsciiTable (String& formatString, Table::TableType tableType, const String& filein, const String& tableproto, const String& tablename, Bool autoHeader, Char separator, const String& commentMarkerRegex, Int firstLine, Int lastLine, const IPosition& autoShape) { Vector dumvec; return ReadAsciiTable::runt (formatString, tableType, filein, filein, tableproto, tablename, autoHeader, autoShape, dumvec, dumvec, separator, commentMarkerRegex, firstLine, lastLine); } Table readAsciiTable (String& formatString, Table::TableType tableType, const String& filein, const String& tableproto, const String& tablename, const Vector& columnNames, const Vector& dataTypes, Char separator, const String& commentMarkerRegex, Int firstLine, Int lastLine) { return ReadAsciiTable::runt (formatString, tableType, filein, filein, tableproto, tablename, False, IPosition(), columnNames, dataTypes, separator, commentMarkerRegex, firstLine, lastLine); } Table readAsciiTable (String& formatString, Table::TableType tableType, const String& headerfile, const String& filein, const String& tableproto, const String& tablename, Char separator, const String& commentMarkerRegex, Int firstLine, Int lastLine) { Vector dumvec; return ReadAsciiTable::runt (formatString, tableType, headerfile, filein, tableproto, tablename, False, IPosition(), dumvec, dumvec, separator, commentMarkerRegex, firstLine, lastLine); } Table readAsciiTable (String& formatString, Table::TableType tableType, const String& headerfile, const String& filein, const String& tableproto, const char* tablename, Char separator, const String& commentMarkerRegex, Int firstLine, Int lastLine) { Vector dumvec; return ReadAsciiTable::runt (formatString, tableType, headerfile, filein, tableproto, String(tablename), False, IPosition(), dumvec, dumvec, separator, commentMarkerRegex, firstLine, lastLine); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/ReadAsciiTable.h000066400000000000000000000444161476623553700207660ustar00rootroot00000000000000//# ReadAsciiTable.h: Filling a table from an Ascii file //# Copyright (C) 1993,1994,1995,1999,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_READASCIITABLE_H #define TABLES_READASCIITABLE_H //# Includes #include #include #include #include //# Forward Declarations #include namespace casacore { //# NAMESPACE CASACORE - BEGIN class Regex; class IPosition; class LogIO; class TableRecord; class TableColumn; // // Filling a table from an Ascii file. // // // // // //
      • Table // // // Global functions to fill a table from an Ascii file. // // The table columns are filled from a file containing the data values // separated by a separator (optionally followed by whitespace). The // default separator is a comma. Non-given values default to 0, False, or // blank string (depending on data type). A value is not given between 2 // consecutive separators or if less values are given than needed. // One line per table row should be given. // The following two header lines define the columns in the table: //
          //
        1. The first line contains the names of the variables in each column. // These names may be enclosed in double quotes. //
        2. The second line contains the data types of each column. // Valid types are: //
            //
          • S for Short Integer data //
          • I for Integer data //
          • R for Real data //
          • D for Double Precision data //
          • X for Complex data (Real, Imaginary) //
          • DX for Double Precision Complex data (R,I) //
          • Z for Complex data (Amplitude, Phase) //
          • DZ for Double Precision Complex data (A,P) //
          • A for ASCII data (must be enclosed in double // quotes if it contains one or more blanks) //
          • DMS for MVAngle-format position in DMS (converted to radians) // In this case a colon separated position is seen as // degrees and not as hours. // Blanks instead of : can be used as separator. //
          • HMS for MVAngle-format position in HMS (converted to radians) // Blanks instead of : can be used as separator. //
          // The type can optionally be followed by one or more positive numbers // (separated by commas without whitespace) indicating that the column // contains an array. The numbers give the shape of the array. // E.g. D2,4 defines a column containing arrays with // shape [2,4]. It "consumes" 8 numbers in each input data line. // The last column can contain a 0 in one of the shape numbers. // It indicates that the arrays are variable shaped; it "consumes" // all remaining numbers in each input data line. If needed, // the arrays are filled with default values (0, False, or blank). // E.g. I0 indicates a variable shaped vector. // I0,4 with a line with remaining input // 1 2 3 4 5 6 7 8 9 results in an array with shape [3,4] // (filled with with 3 zeroes). //
        // If the autoHeader argument is True, the column definition // lines should not be given. It recognizes the types from the first data // line. It gives the names 'column0', etc. to the columns. // It can recognize integer, double, and string types. // It is possible to give a shape argument which has the same function // as the shape values discussed above. //

        // There are two forms of the readAsciiTable function: //

          //
        1. The simplest form has two input files. // The second input file contains the column data. // The first input file contains the keywords (if any) // and the column definitions. // The keywords in the first file, if there are any, must be enclosed // between a line that starts with ".keywords" and a line that starts // with ".endkeywords". To define column keywords, .keywords should be // followed by whitespace and the column name. // Between these two lines each line should contain the following: //
            //
          • The keyword name, e.g., ANYKEY //
          • The datatype of the keyword (cf. list of valid types above) //
          • The value or values for the keyword (the keyword may contain a // scalar or a vector of values). e.g., 3.14159 21.78945 //
          // After the keywords definitions, the two column definition lines // should follow (unless autoHeader=True is given). //
          For example: // // .keywords // KEYI I 10 // KEYIV I 11 12 13 14 // KEYF R 1.2 // KEYFV R -3.2 0 5.6 // KEYD D 1.23456789 // KEYDV D 1 2 3 4 5 6 7 8 9 // KEYX X -1.5 -3 // KEYXC X 0 1 2 3 4 5 6 7 8 9 // KEYZ Z -3 -1.5 // KEYZV Z 0 0.1 0.2 0.3 0.4 0.5 // KEYS A "1 2 3 4 5" // KEYSV A " 1 2 " "AAA" BBB bbb CCc C "@#$%^&*()" // .endkeywords // .keywords COLDX // IKEYS A "coldx ikey" // DKEYS A "coldx dkey" // .endkeywords // COLI COLF COLD COLX COLZ COLS // I R D X Z A // // defines a table with 12 table keywords (of which 6 contain vector // values), 2 keywords for column COLDX, and and 6 columns. // The number of rows is determined by the number of // lines in the second input file. //
        2. The other form is to combine the two files in one file. // In that case the data lines must be preceeded by the optional // keyword and column definitions (without an intermediate blank line). //
        //
        // // // readAsciiTable ("file.in", "", "table.test"); // // creates a table with name table.test from the text file // file.in. The text file could look like: // // COLI COLF COLD COLX COLZ COLS // I R D X Z A // 1 1.1 1.11 1.12 1.13 1.14 1.15 Str1 // 10 11 12 13 14 15 16 String17 // // resulting in a table with 6 columns and 2 rows. // // // Create a table with name as given by tableName. // If autoHeader==True, the format is automatically derived from the // first data line. It can recognize integer, double, and String types. // The columns will be named column1, column2, etc.. // If the autoShape argument is given with 1 or more axes, all values are // treated as a single column with the given shape. Note that one of the // can have length 0 indicating a variable shaped array. // If autoHeader==False, the layout of the table has to be defined in // the first 2 lines of the input file. The remaining lines in the // input file contain the data. // // When the tableDescName is not blank, the table description will // be stored in a table description file with the given name. //
        It returns a string containing the format of the columns in // the form COL1=R, COL2=D, ... // // The separator gives the character separating the values. The default // is a blank. Note that irrespective of the separator, blanks between // values are always ignored. A string value has to be enclosed in // double quotes if it has to contain blanks or the separator value. // // Header and data lines starting with the regular expression given in the // commentMarker are ignored. By default no comment marker is present. // E.g. "#" ignores all lines starting with the #-sign. // " *#" does the same, but the lines to ignore can start with whitespace. // // The first and last line argument give the 1-relative number of the // first and last line to read from the file. firstLine <= 0 is the // same as 1. lastLine <= 0 means until end-of-file. // Note that lines matching the comment marker are also counted. String readAsciiTable (const String& filein, const String& tableDescName, const String& tableName, Bool autoHeader = False, Char separator = ' ', const String& commentMarkerRegex = "", Int firstLine = 1, Int lastLine = -1, const IPosition& autoShape = IPosition()); // This form gets the header info in the given vectors. // Each element in the dataTypes vector has to be of the form as would // be given in a header line. String readAsciiTable (const String& filein, const String& tableproto, const String& tablename, const Vector& columnNames, const Vector& dataTypes, Char separator, const String& commentMarkerRegex, Int firstLine, Int lastLine); // This form reads TWO Ascii files. The first file may contain // keywords and their values as well as the two lines described above for // the names and type of variables. The second file is intended for data only. // // When the tableDescName is not blank, the table description will // be stored in a table description file with the given name. //
        It returns a string containing the format of the columns in // the form COL1=R, COL2=D, ... // // The separator gives the character separating the values. The default // is a blank. Note that irrespective of the separator, blanks between // values are always ignored. A string value has to be enclosed in // double quotes if it has to contain blanks or the separator value. // // Header and data lines starting with the regular expression given in the // commentMarker are ignored. By default no comment marker is present. // E.g. "#" ignores all lines starting with the #-sign. // " *#" does the same, but the lines to ignore can start with whitespace. // // The first and last line argument give the 1-relative number of the // first and last line to read from the data file. firstLine <= 0 is the // same as 1. lastLine <= 0 means until end-of-file. // Note that lines matching the comment marker are also counted. // String readAsciiTable (const String& headerFile, const String& dataFile, const String& tableDescName, const String& tablename, Char separator = ' ', const String& commentMarkerRegex = "", Int firstLine = 1, Int lastLine = -1); //# Note that this char* version is needed, because of the first version //# Taking a Bool as the 4th argument. String readAsciiTable (const String& headerFile, const String& dataFile, const String& tableDescName, const char* tablename, Char separator = ' ', const String& commentMarkerRegex = "", Int firstLine = 1, Int lastLine = -1); // // Similar versions as above, but returning a Table object. // The format string is returned in the first argument. // The type of Table can be given (Plain or Memory). // Table readAsciiTable (String& formatString, Table::TableType tableType, const String& filein, const String& tableDescName, const String& tableName, Bool autoHeader = False, Char separator = ' ', const String& commentMarkerRegex = "", Int firstLine = 1, Int lastLine = -1, const IPosition& autoShape = IPosition()); Table readAsciiTable (String& formatString, Table::TableType tableType, const String& filein, const String& tableproto, const String& tablename, const Vector& columnNames, const Vector& dataTypes, Char separator, const String& commentMarkerRegex, Int firstLine, Int lastLine); Table readAsciiTable (String& formatString, Table::TableType tableType, const String& headerFile, const String& dataFile, const String& tableDescName, const String& tablename, Char separator = ' ', const String& commentMarkerRegex = "", Int firstLine = 1, Int lastLine = -1); Table readAsciiTable (String& formatString, Table::TableType tableType, const String& headerFile, const String& dataFile, const String& tableDescName, const char* tablename, Char separator = ' ', const String& commentMarkerRegex = "", Int firstLine = 1, Int lastLine = -1); // //
        // // Helper class for readAsciiTable // // // // // // This class contains static functions as helpers for readAsciiTable. // class ReadAsciiTable { public: // Run the readAsciiTable. static String run (const String& headerfile, const String& filein, const String& tableproto, const String& tablename, Bool autoHeader, const IPosition& autoShape, const Vector& columnNames, const Vector& dataTypes, Char separator, const String& commentMarkerRegex, Int firstLine, Int lastLine); static Table runt (String& formatString, Table::TableType tableType, const String& headerfile, const String& filein, const String& tableproto, const String& tablename, Bool autoHeader, const IPosition& autoShape, const Vector& columnNames, const Vector& dataTypes, Char separator, const String& commentMarkerRegex, Int firstLine, Int lastLine); // Read a position using MVAngle. // If isDMS is True, a position with : is treated as DMS instead of HMS. // This function is a bit more relaxed than MVAngle::read. // It allows whitespace. Furthermore it allows whitespace as separator :. static double stringToPos (const String& pos, Bool isDMS); private: // Define types. enum RATType {RATBool, RATShort, RATInt, RATFloat, RATDouble, RATString, RATComX, RATComZ, RATDComX, RATDComZ, RATDMS, RATHMS}; // Do the actual run. static String doRun (const String& headerfile, const String& filein, const String& tableproto, const String& tablename, Bool autoHeader, const IPosition& autoShape, const Vector& columnNames, const Vector& dataTypes, Char separator, Bool testComment, const Regex& commentMarker, Int firstLine, Int lastLine); // Do the actual work of making and filling the table. static Table makeTab (String& formatString, Table::TableType tableType, const String& headerfile, const String& filein, const String& tableproto, const String& tablename, Bool autoHeader, const IPosition& autoShape, const Vector& columnNames, const Vector& dataTypes, Char separator, Bool testComment, const Regex& commentMarker, Int firstLine, Int lastLine); // Get the next line. Skip lines to be ignored. // It returns False when no more lines are available. static Bool getLine (ifstream& file, Int& lineNumber, char* line, Int lineSize, Bool testComment, const Regex& commentMarker, Int firstLine, Int lastLine); // Get the next part of the line using the separator as delimiter. // Leading blanks are ignored. static Int getNext (const Char* string, Int strlen, Char* result, Int& at, Char separator); // Derive the types from the values in the first data line. static void getTypes (const IPosition& shape, const Char* in, Int leng, Char* string1, Char* string2, Char separator); // Turn the string into a Bool value. // Empty string, value 0 and any value starting with f, F, n or N are False. static Bool makeBool (const String& str); // Handle a keyword set. static void handleKeyset (Int lineSize, char* string1, char* first, char* second, TableRecord& keysets, LogIO& logger, const String& fileName, ifstream& jFile, Int& lineNumber, Char separator, Bool testComment, const Regex& commentMarker, Int firstLine, Int lastLine); // Get the shape and type from the type string. static Int getTypeShape (const String& typestr, IPosition& shape, Int& type); // Get the next scalar value with the given type from string1. static Bool getValue (char* string1, Int lineSize, char* first, Int& at1, Char separator, Int type, void* value); // Handle the next scalar with the given type from the data line and // put it into the table column. static void handleScalar (char* string1, Int lineSize, char* first, Int& at1, Char separator, Int type, TableColumn& tabcol, rownr_t rownr); // Get the next array with the given type from string1. // It returns the shape (for variable shaped arrays). static IPosition getArray (char* string1, Int lineSize, char* first, Int& at1, Char separator, const IPosition& shape, Int varAxis, Int type, void* valueBlock); // Get the next array with the given type from the data line and // put it into the table column. static void handleArray (char* string1, Int lineSize, char* first, Int& at1, Char separator, const IPosition& shape, Int varAxis, Int type, TableColumn& tabcol, rownr_t rownr); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/RefColumn.cc000066400000000000000000000151331476623553700202140ustar00rootroot00000000000000//# RefColumn.cc: Abstract base class for a table column //# Copyright (C) 1994,1995,1996,1997,1998,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN RefColumn::RefColumn (const BaseColumnDesc* bcdp, RefTable* reftab, BaseColumn* bcp) : BaseColumn (bcdp), refTabPtr_p(reftab), colPtr_p (bcp) {} RefColumn::~RefColumn() {} Bool RefColumn::isWritable() const { return colPtr_p->isWritable(); } Bool RefColumn::isStored() const { return colPtr_p->isStored(); } TableRecord& RefColumn::rwKeywordSet() { return colPtr_p->rwKeywordSet(); } TableRecord& RefColumn::keywordSet() { return colPtr_p->keywordSet(); } rownr_t RefColumn::nrow() const { return refTabPtr_p->nrow(); } void RefColumn::initialize (rownr_t startRow, rownr_t endRow) { rownr_t rownr; for (rownr_t i=startRow; irootRownr(i); colPtr_p->initialize (rownr, rownr); } } void RefColumn::setShape (rownr_t rownr, const IPosition& shape) { colPtr_p->setShape (refTabPtr_p->rootRownr(rownr), shape); } void RefColumn::setShape (rownr_t rownr, const IPosition& shape, const IPosition& tileShape) { colPtr_p->setShape (refTabPtr_p->rootRownr(rownr), shape, tileShape); } uInt RefColumn::ndimColumn() const { return colPtr_p->ndimColumn(); } IPosition RefColumn::shapeColumn() const { return colPtr_p->shapeColumn(); } uInt RefColumn::ndim (rownr_t rownr) const { return colPtr_p->ndim (refTabPtr_p->rootRownr(rownr)); } IPosition RefColumn::shape(rownr_t rownr) const { return colPtr_p->shape (refTabPtr_p->rootRownr(rownr)); } Bool RefColumn::isDefined (rownr_t rownr) const { return colPtr_p->isDefined (refTabPtr_p->rootRownr(rownr)); } Bool RefColumn::canChangeShape() const { return colPtr_p->canChangeShape(); } void RefColumn::get (rownr_t rownr, void* dataPtr) const { colPtr_p->get (refTabPtr_p->rootRownr(rownr), dataPtr); } void RefColumn::getArray (rownr_t rownr, ArrayBase& data) const { colPtr_p->getArray (refTabPtr_p->rootRownr(rownr), data); } void RefColumn::getSlice (rownr_t rownr, const Slicer& ns, ArrayBase& data) const { colPtr_p->getSlice (refTabPtr_p->rootRownr(rownr), ns, data); } void RefColumn::put (rownr_t rownr, const void* dataPtr) { colPtr_p->put (refTabPtr_p->rootRownr(rownr), dataPtr); } void RefColumn::putArray (rownr_t rownr, const ArrayBase& data) { colPtr_p->putArray (refTabPtr_p->rootRownr(rownr), data); } void RefColumn::putSlice (rownr_t rownr, const Slicer& ns, const ArrayBase& data) { colPtr_p->putSlice (refTabPtr_p->rootRownr(rownr), ns, data); } void RefColumn::getScalarColumn (ArrayBase& data) const { colPtr_p->getScalarColumnCells (refTabPtr_p->rowNumbers(), data); } void RefColumn::getArrayColumn (ArrayBase& data) const { colPtr_p->getArrayColumnCells (refTabPtr_p->rowNumbers(), data); } void RefColumn::getColumnSlice (const Slicer& ns, ArrayBase& data) const { colPtr_p->getColumnSliceCells (refTabPtr_p->rowNumbers(), ns, data); } void RefColumn::getScalarColumnCells (const RefRows& rownrs, ArrayBase& data) const { colPtr_p->getScalarColumnCells (rownrs.convert(refTabPtr_p->rowNumbers()), data); } void RefColumn::getArrayColumnCells (const RefRows& rownrs, ArrayBase& data) const { colPtr_p->getArrayColumnCells (rownrs.convert(refTabPtr_p->rowNumbers()), data); } void RefColumn::getColumnSliceCells (const RefRows& rownrs, const Slicer& ns, ArrayBase& data) const { colPtr_p->getColumnSliceCells (rownrs.convert(refTabPtr_p->rowNumbers()), ns, data); } void RefColumn::putScalarColumn (const ArrayBase& data) { colPtr_p->putScalarColumnCells (refTabPtr_p->rowNumbers(), data); } void RefColumn::putArrayColumn (const ArrayBase& data) { colPtr_p->putArrayColumnCells (refTabPtr_p->rowNumbers(), data); } void RefColumn::putColumnSlice (const Slicer& ns, const ArrayBase& data) { colPtr_p->putColumnSliceCells (refTabPtr_p->rowNumbers(), ns, data); } void RefColumn::putScalarColumnCells (const RefRows& rownrs, const ArrayBase& data) { colPtr_p->putScalarColumnCells (rownrs.convert(refTabPtr_p->rowNumbers()), data); } void RefColumn::putArrayColumnCells (const RefRows& rownrs, const ArrayBase& data) { colPtr_p->putArrayColumnCells (rownrs.convert(refTabPtr_p->rowNumbers()), data); } void RefColumn::putColumnSliceCells (const RefRows& rownrs, const Slicer& ns, const ArrayBase& data) { colPtr_p->putColumnSliceCells (rownrs.convert(refTabPtr_p->rowNumbers()), ns, data); } ColumnCache& RefColumn::columnCache() { return colCache_p; } void RefColumn::setMaximumCacheSize (uInt nbytes) { colPtr_p->setMaximumCacheSize (nbytes); } void RefColumn::makeSortKey (Sort& sortobj, std::shared_ptr& cmpObj, Int order, std::shared_ptr& dataSave) { colPtr_p->makeRefSortKey (sortobj, cmpObj, order, refTabPtr_p->rowNumbers(), dataSave); } void RefColumn::allocIterBuf (void*& lastVal, void*& curVal, std::shared_ptr& cmpObj) { colPtr_p->allocIterBuf (lastVal, curVal, cmpObj); } void RefColumn::freeIterBuf (void*& lastVal, void*& curVal) { colPtr_p->freeIterBuf (lastVal, curVal); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/RefColumn.h000066400000000000000000000230351476623553700200560ustar00rootroot00000000000000//# RefColumn.h: A column in a reference table //# Copyright (C) 1994,1995,1996,1997,1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_REFCOLUMN_H #define TABLES_REFCOLUMN_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class RefTable; class BaseColumnDesc; class TableRecord; class Slicer; class IPosition; // // A column in a reference table // // // // // //# Classes you should understand before using this one. //
      • RefTable //
      • BaseColumn // // // RefTable represents a column in a RefTable. A RefTable is a table // referencing another table, usually as the result of a select, etc.. // // // RefColumn handles the access of a column in a RefTable. // It calls the corresponding function in the referenced column // while converting the given row number to the row number in the // referenced table. // // // This class is untyped, i.e. not templated. // Every call is sent to the underlying referenced BaseColumn which // is typed by the virtual function mechanism. // A RefColumn can never be used directly. A user always has to // construct a typed ArrayColumn or ScalarColumn object to access a column. // This means everyting is fully type safe. // // //# A List of bugs, limitations, extensions or planned refinements. //
      • Act upon removal of rows or the underlying column // class RefColumn : public BaseColumn { public: // Construct the RefColumn. It will point to the given column // description, RefTable and referenced column. // The RefTable will be used to convert the rownr to the rownr // in the referenced column. RefColumn (const BaseColumnDesc*, RefTable*, BaseColumn* referencedColumn); ~RefColumn(); // Test if the column is writable in the parent table. virtual Bool isWritable() const; // Test if the column is stored (otherwise it is virtual). virtual Bool isStored() const; // Get access to the column keyword set. // This is the keyword set in the referenced column. // virtual TableRecord& rwKeywordSet(); virtual TableRecord& keywordSet(); // // Get nr of rows in the column. virtual rownr_t nrow() const; // Test if a value in a particular cell has been defined. virtual Bool isDefined (rownr_t rownr) const; // Set the shape of the array in the given row. virtual void setShape (rownr_t rownr, const IPosition& shape); // Set the shape and tile shape of the array in the given row. virtual void setShape (rownr_t rownr, const IPosition& shape, const IPosition& tileShape); // Get the global #dimensions of an array (i.e. for all rows). virtual uInt ndimColumn() const; // Get the global shape of an array (i.e. for all rows). virtual IPosition shapeColumn() const; // Get the #dimensions of an array in a particular cell. virtual uInt ndim (rownr_t rownr) const; // Get the shape of an array in a particular cell. virtual IPosition shape (rownr_t rownr) const; // It can change shape if the underlying column can. virtual Bool canChangeShape() const; // Initialize the rows from startRownr till endRownr (inclusive) // with the default value defined in the column description (if defined). void initialize (rownr_t startRownr, rownr_t endRownr); // Get the value from a particular cell. // This can be a scalar or an array. virtual void get (rownr_t rownr, void* dataPtr) const; // Get an array from a particular cell. virtual void getArray (rownr_t rownr, ArrayBase& dataPtr) const; // Get a slice of an N-dimensional array in a particular cell. virtual void getSlice (rownr_t rownr, const Slicer&, ArrayBase& dataPtr) const; // Get the vector of all scalar values in a column. virtual void getScalarColumn (ArrayBase& dataPtr) const; // Get the array of all array values in a column. // If the column contains n-dim arrays, the resulting array is (n+1)-dim. // The arrays in the column have to have the same shape in all cells. virtual void getArrayColumn (ArrayBase& dataPtr) const; // Get subsections from all arrays in the column. // If the column contains n-dim arrays, the resulting array is (n+1)-dim. // The arrays in the column have to have the same shape in all cells. virtual void getColumnSlice (const Slicer&, ArrayBase& dataPtr) const; // Get the vector of some scalar values in a column. virtual void getScalarColumnCells (const RefRows& rownrs, ArrayBase& dataPtr) const; // Get the array of some array values in a column. // If the column contains n-dim arrays, the resulting array is (n+1)-dim. // The arrays in the column have to have the same shape in all cells. virtual void getArrayColumnCells (const RefRows& rownrs, ArrayBase& dataPtr) const; // Get subsections from some arrays in the column. // If the column contains n-dim arrays, the resulting array is (n+1)-dim. // The arrays in the column have to have the same shape in all cells. virtual void getColumnSliceCells (const RefRows& rownrs, const Slicer&, ArrayBase& dataPtr) const; // Put the value in a particular cell. // This can be a scalar or an array. virtual void put (rownr_t rownr, const void* dataPtr); // Put the array value in a particular cell. virtual void putArray (rownr_t rownr, const ArrayBase& dataPtr); // Put a slice of an N-dimensional array in a particular cell. virtual void putSlice (rownr_t rownr, const Slicer&, const ArrayBase& dataPtr); // Put the vector of all scalar values in the column. virtual void putScalarColumn (const ArrayBase& dataPtr); // Put the array of all array values in the column. // If the column contains n-dim arrays, the source array is (n+1)-dim. // The arrays in the column have to have the same shape in all cells. virtual void putArrayColumn (const ArrayBase& dataPtr); // Put into subsections of all table arrays in the column. // If the column contains n-dim arrays, the source array is (n+1)-dim. // The arrays in the column have to have the same shape in all cells. virtual void putColumnSlice (const Slicer&, const ArrayBase& dataPtr); // Get the vector of some scalar values in a column. virtual void putScalarColumnCells (const RefRows& rownrs, const ArrayBase& dataPtr); // Get the array of some array values in a column. // If the column contains n-dim arrays, the resulting array is (n+1)-dim. // The arrays in the column have to have the same shape in all cells. virtual void putArrayColumnCells (const RefRows& rownrs, const ArrayBase& dataPtr); // Put subsections of some arrays in the column. // If the column contains n-dim arrays, the source array is (n+1)-dim. // The arrays in the column have to have the same shape in all cells. virtual void putColumnSliceCells (const RefRows& rownrs, const Slicer&, const ArrayBase& dataPtr); // Get the underlying column cache. virtual ColumnCache& columnCache(); // Set the maximum cache size (in bytes) to be used by a storage manager. virtual void setMaximumCacheSize (uInt nbytes); // Add this column and its data to the Sort object. // It may allocate some storage on the heap, which will be saved // in the argument dataSave. // The function freeSortKey must be called to free this storage. virtual void makeSortKey (Sort&, std::shared_ptr& cmpObj, Int order, std::shared_ptr& dataSave); // Allocate value buffers for the table iterator. // Also get a comparison functiuon if undefined. // The function freeIterBuf must be called to free the buffers. virtual void allocIterBuf (void*& lastVal, void*& curVal, std::shared_ptr& cmpObj); // Free the value buffers allocated by allocIterBuf. virtual void freeIterBuf (void*& lastVal, void*& curVal); protected: RefTable* refTabPtr_p; BaseColumn* colPtr_p; ColumnCache colCache_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/RefRows.cc000066400000000000000000000145321476623553700177130ustar00rootroot00000000000000//# RefRows.cc: Class holding the row numbers in a RefTable //# Copyright (C) 1998,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN RefRows::RefRows (const Vector& rowNumbers, Bool isSliced, Bool collapse) { init (rowNumbers, isSliced, collapse); } RefRows::RefRows (const Vector& rowNumbers, Bool isSliced, Bool collapse) { init (RowNumbers(rowNumbers), isSliced, collapse); } void RefRows::init (const Vector& rowNumbers, Bool isSliced, Bool collapse) { itsRows = rowNumbers; itsNrows = rowNumbers.nelements(); itsSliced = isSliced; if (itsSliced) { AlwaysAssert (itsNrows%3 == 0, AipsError); itsNrows = 0; } else if (collapse) { //# Try to turn individual row numbers into slices. //# Stop doing that when the number of elements in the //# resulting array would exceed the input length, because //# in that case we gain not anything at all. Vector rows(itsNrows+3); rownr_t start = 0; rownr_t end = 0; rownr_t incr = 0; rownr_t nv = 0; rownr_t nr = 0; for (rownr_t i=0; i 2) { rows(nr++) = end; rows(nr++) = incr; start = value; nv = 1; } else { rows(nr++) = start; rows(nr++) = 1; start = end; end = value; incr = end - start; nv = 2; } } } // Great, our result is smaller than the input. So use the result // after filling in the last slice. if (nr < itsNrows) { rows(nr++) = start; if (nv == 1) { rows(nr++) = start; rows(nr++) = 1; } else { rows(nr++) = end; rows(nr++) = incr; } rows.resize (nr, True); itsRows.reference (rows); itsSliced = True; } } } RefRows::RefRows (rownr_t start, rownr_t end, rownr_t incr) : itsRows (3), itsNrows (1 + (end-start)/incr), itsSliced (True) { AlwaysAssert (start<=end, AipsError); itsRows(0) = start; itsRows(1) = end; itsRows(2) = incr; } RefRows::RefRows (const RefRows& other) : itsRows (other.itsRows), itsNrows (other.itsNrows), itsSliced (other.itsSliced) {} // Assignment (copy semantics). RefRows& RefRows::operator= (const RefRows& other) { if (this != &other) { itsRows.resize (other.itsRows.nelements()); itsRows = other.itsRows; itsNrows = other.itsNrows; itsSliced = other.itsSliced; } return *this; } RefRows::~RefRows() {} Bool RefRows::operator== (const RefRows& other) const { return (itsSliced == other.itsSliced && itsRows.nelements() == other.itsRows.nelements() && allEQ (itsRows, other.itsRows)); } rownr_t RefRows::fillNrows() const { rownr_t nr = 0; rownr_t n = itsRows.nelements(); for (rownr_t i=0; iitsNrows = nr; return nr; } RowNumbers RefRows::convert (const RowNumbers& rootRownrs) const { rownr_t n = nrow(); Vector rownrs(n); if (itsSliced) { rownr_t nr = 0; RefRowsSliceIter iter(*this); while (! iter.pastEnd()) { rownr_t rownr = iter.sliceStart(); rownr_t end = iter.sliceEnd(); rownr_t incr = iter.sliceIncr(); while (rownr <= end) { DebugAssert (rownr <= rootRownrs.nelements(), AipsError); rownrs(nr++) = rootRownrs(rownr); rownr += incr; } iter++; } } else { for (rownr_t i=0; i rownrs(n); rownr_t nr = 0; RefRowsSliceIter iter(*this); while (! iter.pastEnd()) { rownr_t rownr = iter.sliceStart(); rownr_t end = iter.sliceEnd(); rownr_t incr = iter.sliceIncr(); while (rownr <= end) { rownrs(nr++) = rownr; rownr += incr; } iter++; } return rownrs; } RefRowsSliceIter::RefRowsSliceIter (const RefRows& rows) : itsRows (rows.rowVector()), itsSliced (rows.isSliced()) { reset(); } void RefRowsSliceIter::reset() { itsPos = 0; itsPastEnd = True; if (itsPos < itsRows.nelements()) { itsPastEnd = False; next(); } } void RefRowsSliceIter::next() { if (itsPastEnd) { throw (AipsError ("RefRowsSliceIter::next - past end")); } if (itsPos >= itsRows.nelements()) { itsPastEnd = True; } else { itsStart = itsRows(itsPos++); if (itsSliced) { itsEnd = itsRows(itsPos++); itsIncr = itsRows(itsPos++); } else { itsEnd = itsStart; itsIncr = 1; } } } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/RefRows.h000066400000000000000000000204341476623553700175530ustar00rootroot00000000000000//# RefRows.h: Class holding the row numbers in a RefTable //# Copyright (C) 1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_REFROWS_H #define TABLES_REFROWS_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class Slicer; // // Class holding the row numbers in a RefTable // // // // // //# Classes you should understand before using this one. //
      • Vector // // // RefRows is used to hold the row numbers forming a view on another // table. It contains a vector which can hold the row numbers in 2 ways: //
          //
        1. As a normal series of row numbers. This is used by e.g. class // RefTable //
        2. As a series of Slices. In this case 3 subsequent entries // in the vector are used to represent start, end, and increment. // This is used by a function like ScalarColumn::getColumnRange. //
        // Class RefRowsSliceIter can be // used to iterate through a RefRows object. Each step in the iteration // goes to the next a slice. If the RefRows objct contains a simple series // of row numbers, each slice contains only one row number. // This can degrade performance, so it is possible to use shortcuts by // testing if the object contains slices (using isSliced()) // and getting the row number vector directly (using rowVector()). //
        // // RefRows is meant to have one class representing the various ways // of picking row numbers. This simplifies the interface of the table // and data manager classes dealing with getting/putting the data. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class RefRows { public: // Create the object from a Vector containing the row numbers. // When isSliced==False, the vector is treated as // containing individual row numbers, otherwise as containing // (possibly multiple) slices in the form start,end,incr. // When collapse==True, it will try to collapse the // individual row numbers to the slice form (to save memory). RefRows (const Vector& rowNumbers, Bool isSliced = False, Bool collapse = False); #ifdef IMPLICIT_CTDS_32BIT RefRows (const Vector& rowNumbers, Bool isSliced = False, Bool collapse = False); #else explicit RefRows (const Vector& rowNumbers, Bool isSliced = False, Bool collapse = False); #endif // Create the object from a single start,end,incr slice. RefRows (rownr_t start, rownr_t end, rownr_t incr=1); // Copy constructor (reference semantics). RefRows (const RefRows& other); // Assignment (copy semantics). RefRows& operator= (const RefRows& other); ~RefRows(); // Do this and the other object reference the same rows? Bool operator== (const RefRows& other) const; // Convert this object to a RowNumbers object by applying the given row numbers. // It is used to convert the RefRows object with row numbers in a // RefTable to row numbers in the original root table. RowNumbers convert (const RowNumbers& rootRownrs) const; // Convert this object to a RowNumbers object by de-slicing it. // I.e. it linearizes the row numbers. RowNumbers convert() const; // Return the number of rows given by this object. // If the object contains slices, it counts the number of rows // represented by each slice. // rownr_t nrows() const { return (itsNrows == 0 ? fillNrows() : itsNrows); } rownr_t nrow() const { return (itsNrows == 0 ? fillNrows() : itsNrows); } // // Return the first row in the object. rownr_t firstRow() const { return itsRows(0); } // Represents the vector a slice? Bool isSliced() const { return itsSliced; } // Get the row vector as is (thus sliced if the object contains slices). // It is mainly useful to get all row numbers when the object does not // contain slices. const Vector& rowVector() const { return itsRows; } private: // Initialize the object. void init (const Vector& rowNumbers, Bool isSliced, Bool collapse); // Fill the itsNrows variable. rownr_t fillNrows() const; Vector itsRows; rownr_t itsNrows; //# 0 = still unknown Bool itsSliced; //# True = vector contains slices }; // // Class to iterate through a RefRows object. // // // // // //# Classes you should understand before using this one. //
      • RefRows // // // RefRowsSliceIter is useful to iterate through a // RefRows object, // especially if the RefRows object contains slices. // Each step in the iteration returns a Slice object containing // the next slice in the RefRows object. //
        // It is used in Table and data manager classes (e.g. StManColumn). //
        // // This example shows how to iterate through a RefRows object // (giving a slice) and through each of the slices. // // void somefunc (const RefRows& rownrs) // // Iterate through all slices. // RefRowsSliceIter rowiter(rownrs); // while (! rowiter.pastEnd()) { // // Get start, end, and increment for this slice. // rownr_t rownr = rowiter.sliceStart(); // rownr_t end = rowiter.sliceEnd(); // rownr_t incr = rowiter.sliceIncr(); // // Iterate through the row numbers in the slice. // while (rownr <= end) { // rownr += incr; // } // // Go to next slice. // rowiter++; // } // } // // //# //# A List of bugs, limitations, extensions or planned refinements. //# class RefRowsSliceIter { public: // Construct the iterator on a RefRows object. // It is set to the beginning. RefRowsSliceIter (const RefRows&); // Reset the iterator to the beginning. void reset(); // Is the iterator past the end? Bool pastEnd() const { return itsPastEnd; } // Go the next slice. // void operator++() { next(); } void operator++(int) { next(); } void next(); // // Get the current slice start, end, or increment. // rownr_t sliceStart() const { return itsStart; } rownr_t sliceEnd() const { return itsEnd; } rownr_t sliceIncr() const { return itsIncr; } // private: Vector itsRows; Bool itsSliced; rownr_t itsStart; rownr_t itsEnd; rownr_t itsIncr; rownr_t itsPos; Bool itsPastEnd; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/RefTable.cc000066400000000000000000001017051476623553700200070ustar00rootroot00000000000000//# RefTable.cc: Class for a table as a view of another table //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN RefTable::RefTable (AipsIO& ios, const String& name, rownr_t nrrow, int opt, const TableLock& lockOptions, const TSMOption& tsmOption) : BaseTable (name, opt, nrrow), rowStorage_p (0), // initially empty vector of rownrs changed_p (False) { //# Read the file in. // Set initially to no write in destructor. // At the end it is reset. In this way nothing is written if // an exception is thrown during initialization. noWrite_p = True; getRef (ios, opt, lockOptions, tsmOption); noWrite_p = False; TableTrace::traceRefTable (baseTabPtr_p->tableName(), 'o'); } RefTable::RefTable (BaseTable* btp, Bool order, rownr_t nrall) : BaseTable ("", Table::Scratch, nrall), baseTabPtr_p (btp->root()->shared_from_this()), rowOrd_p (order), rowStorage_p (nrall), // allocate vector of rownrs changed_p (True) { AlwaysAssert (rowStorage_p.contiguousStorage(), AipsError); //# Copy the table description and create the columns. tdescPtr_p = std::make_shared(btp->tableDesc(), TableDesc::Scratch); setup (btp, Vector()); //# Get root table (will be parent if btp is an reference table). TableTrace::traceRefTable (baseTabPtr_p->tableName(), 's'); } RefTable::RefTable (BaseTable* btp, const Vector& rownrs) : BaseTable ("", Table::Scratch, rownrs.nelements()), baseTabPtr_p (btp->root()->shared_from_this()), rowOrd_p (True), rowStorage_p (0), changed_p (True) { //# Copy the table description and create the columns. tdescPtr_p = std::make_shared(btp->tableDesc(), TableDesc::Scratch); setup (btp, Vector()); rowStorage_p = rownrs; AlwaysAssert (rowStorage_p.contiguousStorage(), AipsError); const rownr_t* rows = rowStorage_p.data(); //# Check if the row numbers do not exceed #rows. rownr_t nmax = btp->nrow(); for (rownr_t i=0; i= nmax) { throw (indexError (rows[i], "RefTable Row vector")); } } //# Adjust rownrs in case input table is a reference table. rowOrd_p = btp->adjustRownrs (nrrow_p, rowStorage_p, True); TableTrace::traceRefTable (baseTabPtr_p->tableName(), 's'); } RefTable::RefTable (BaseTable* btp, const Vector& mask) : BaseTable ("", Table::Scratch, 0), baseTabPtr_p (btp->root()->shared_from_this()), rowOrd_p (btp->rowOrder()), rowStorage_p (0), // initially empty vector of rownrs changed_p (True) { //# Copy the table description and create the columns. tdescPtr_p = std::make_shared(btp->tableDesc(), TableDesc::Scratch); setup (btp, Vector()); //# Store the rownr if the mask is set. rownr_t nr = std::min (mask.nelements(), btp->nrow()); for (rownr_t i=0; iadjustRownrs (nrrow_p, rowStorage_p, True); TableTrace::traceRefTable (baseTabPtr_p->tableName(), 's'); } RefTable::RefTable (BaseTable* btp, const Vector& columnNames) : BaseTable ("", Table::Scratch, btp->nrow()), baseTabPtr_p (btp->root()->shared_from_this()), rowOrd_p (btp->rowOrder()), rowStorage_p (0), changed_p (True) { //# Create table description by copying the selected columns. //# Create the columns. const TableDesc& td = btp->tableDesc(); //# Copy the keywords from the root tabledesc. tdescPtr_p = std::make_shared(td, "", "", TableDesc::Scratch, False); for (uInt i=0; iaddColumn (td.columnDesc (columnNames(i))); } setup (btp, columnNames); //# Get the row numbers from the input table. //# Copy them to this table. rowStorage_p = btp->rowNumbers(); AlwaysAssert (rowStorage_p.contiguousStorage(), AipsError); TableTrace::traceRefTable (baseTabPtr_p->tableName(), 'p'); } RefTable::~RefTable() { //# When needed, write the table files if not marked for delete if (!isMarkedForDelete()) { if (openedForWrite() && !shouldNotWrite()) { writeRefTable (True); } } TableTrace::traceRefTable (baseTabPtr_p->tableName(), 'c'); //# Delete all RefColumn objects. for (auto& x : colMap_p) { delete x.second; } } void RefTable::getPartNames (Block& names, Bool recursive) const { if (recursive) { baseTabPtr_p->getPartNames (names, recursive); } else { uInt inx = names.size(); names.resize (inx + 1); names[inx] = baseTabPtr_p->tableName(); } } void RefTable::reopenRW() { baseTabPtr_p->reopenRW(); option_p = Table::Update; } Bool RefTable::asBigEndian() const { return baseTabPtr_p->asBigEndian(); } const StorageOption& RefTable::storageOption() const { return baseTabPtr_p->storageOption(); } Bool RefTable::isMultiUsed (Bool) const { return False; } const TableLock& RefTable::lockOptions() const { return baseTabPtr_p->lockOptions(); } void RefTable::mergeLock (const TableLock& lockOptions) { baseTabPtr_p->mergeLock (lockOptions); } Bool RefTable::hasLock (FileLocker::LockType type) const { return baseTabPtr_p->hasLock (type); } Bool RefTable::lock (FileLocker::LockType type, uInt nattempts) { return baseTabPtr_p->lock (type, nattempts); } void RefTable::unlock() { baseTabPtr_p->unlock(); } void RefTable::flush (Bool fsync, Bool recursive) { if (!isMarkedForDelete()) { if (openedForWrite()) { writeRefTable (fsync); } } // Flush the underlying table. baseTabPtr_p->flush (fsync, recursive); } void RefTable::resync() { baseTabPtr_p->resync(); } uInt RefTable::getModifyCounter() const { return baseTabPtr_p->getModifyCounter(); } //# Adjust the input rownrs to the actual rownrs in the root table. Bool RefTable::adjustRownrs (rownr_t nr, Vector& rowStorage, Bool determineOrder) const { // Note that rowStorage can be the same as rowStorage_p. AlwaysAssert (nr <= rowStorage.size(), AipsError); rowStorage.resize (nr, True); AlwaysAssert (rowStorage.contiguousStorage(), AipsError); const rownr_t* rows = rowStorage_p.data(); rownr_t* rownrs = rowStorage.data(); Bool rowOrder = True; for (rownr_t i=0; itableName(), 'w'); // Write old version if all row numbers fit in 32 bits. Int version = 3; if (nrrow_p < std::numeric_limits::max() && baseTabPtr_p->nrow() < std::numeric_limits::max() && allLT (rowStorage_p, rownr_t(std::numeric_limits::max()))) { version = 2; } AipsIO ios; writeStart (ios, True); ios << "RefTable"; ios.putstart ("RefTable", version); // Make the name of the base table relative to this table. ios << Path::stripDirectory (baseTabPtr_p->tableName(), tableName()); ios << nameMap_p; // Write the column names in order of appearance. Vector names(tdescPtr_p->ncolumn()); for (uInt i=0; icolumnDesc(i).name(); } ios << names; if (version == 2) { ios << uInt(baseTabPtr_p->nrow()); ios << rowOrd_p; ios << uInt(nrrow_p); } else { ios << baseTabPtr_p->nrow(); ios << rowOrd_p; ios << nrrow_p; } // Do not write more than 2**20 rownrs at once (CAS-7020). Vector rows32; if (version == 2) { rows32.resize (nrrow_p); convertArray (rows32, rowStorage_p(Slice(0, nrrow_p))); } const uInt* rows32p = rows32.data(); rownr_t done = 0; while (done < nrrow_p) { rownr_t todo = std::min(nrrow_p-done, rownr_t(1048576)); if (version == 2) { ios.put (todo, rows32p+done, False); } else { ios.put (todo, rowStorage_p.data()+done, False); } done += todo; } ios.putend(); writeEnd (ios); changed_p = False; } //# Write the TableInfo. flushTableInfo(); } //# Read a reference table from a file and read the associated table. void RefTable::getRef (AipsIO& ios, int opt, const TableLock& lockOptions, const TSMOption& tsmOption) { //# Open the file, read name and type of root and read object data. String rootName; rownr_t rootNrow, nrrow; Int version = ios.getstart ("RefTable"); if (version > 3) { throw TableError ("RefTable version " + String::toString(version) + " not supported by this version of Cassacore"); } ios >> rootName; rootName = Path::addDirectory (rootName, tableName()); ios >> nameMap_p; Vector names; if (version > 1) { ios >> names; } if (version > 2) { ios >> rootNrow >> rowOrd_p >> nrrow; } else { uInt n1, n2; ios >> n1 >> rowOrd_p >> n2; rootNrow = n1; nrrow = n2; } DebugAssert (nrrow == nrrow_p, AipsError); //# Resize the block of rownrs and read them in. rowStorage_p.resize (nrrow); AlwaysAssert (rowStorage_p.contiguousStorage(), AipsError); rownr_t* rows = rowStorage_p.data(); rownr_t done = 0; // Do not read more than 2**20 rows at once (CAS-7020). if (version > 2) { while (done < nrrow) { rownr_t todo = std::min(nrrow_p-done, rownr_t(1048576)); ios.get (todo, rows+done); done += todo; } } else { Vector rows(nrrow); uInt* p = rows.data(); while (done < nrrow) { rownr_t todo = std::min(nrrow_p-done, rownr_t(1048576)); ios.get (todo, p+done); done += todo; } convertArray (rowStorage_p, rows); } ios.getend(); //# Now read in the root table referenced to. //# Check if #rows has not decreased, which is about the only thing //# we can do to make sure the referenced rows are still the same. Table tab; if (opt == Table::Old) { tab = Table(rootName, lockOptions, Table::Old, tsmOption); }else{ tab = Table(rootName, lockOptions, Table::Update, tsmOption); } baseTabPtr_p = tab.baseTablePtr()->shared_from_this(); if (rootNrow > baseTabPtr_p->nrow()) { throw (TableInvOper ("RefTable::getRef, #rows in referenced table decreased")); } //# Build up the table description from the name map and the //# description of the root table. const TableDesc& rootDesc = baseTabPtr_p->tableDesc(); //# Copy the keywords from the root tabledesc. tdescPtr_p = std::make_shared(rootDesc, "", "", TableDesc::Scratch, False); makeDesc (*tdescPtr_p, rootDesc, nameMap_p, names); //# Create the refColumns. makeRefCol(); //# Read the TableInfo object. getTableInfo(); //# Great, everything is done. } //# Read description and #rows. void RefTable::getLayout (TableDesc& desc, AipsIO& ios) { String rootName; std::map nameMap; Int version = ios.getstart ("RefTable"); ios >> rootName; ios >> nameMap; Vector names; if (version > 1) { ios >> names; } // Get description of the parent table. TableDesc pdesc; TableUtil::getLayout (pdesc, rootName); makeDesc (desc, pdesc, nameMap, names); } void RefTable::makeDesc (TableDesc& desc, const TableDesc& rootDesc, std::map& nameMap, Vector& names) { //# The names block contains the column names in order of appearance. //# For older versions it can be empty. If so, fill it with the //# names from the map. if (names.nelements() == 0) { names.resize (nameMap.size()); uInt i = 0; for (auto& x : nameMap) { names[i++] = x.first; } } //# Build up the table description. //# It is possible that columns have disappeared from the root table. //# Remember these columns, so they are removed later from the map. //# The nameMap maps column names in this table to the names in the //# root table, so a rename is needed if names are different. std::map unknownCol; for (uInt i=0; i(0))); } } //# Remove the unknown columns from the map. for (const auto& x : unknownCol) { nameMap.erase (x.first); } } //# Build the name map from the description. //# Old and new name are (initially) equal. //# Make RefColumn objects and initialize TableInfo. void RefTable::setup (BaseTable* btp, const Vector& columnNames) { RefTable* rtp = dynamic_cast(btp); if (rtp != 0) { // The table is already a RefTable, so copy its nameMap. if (columnNames.nelements() == 0) { nameMap_p = rtp->nameMap_p; } else { // Some columns are selected, so copy those only. // Make the map const, so operator() throws an exception // if the key does not exist. const std::map& nm = rtp->nameMap_p; for (uInt i=0; incolumn(); i++) { nameMap_p.insert (std::make_pair(tdescPtr_p->columnDesc(i).name(), tdescPtr_p->columnDesc(i).name())); } } makeRefCol(); //# The initial table info is a copy of the original. tableInfo() = btp->tableInfo(); } //# Create a RefColumn object for all columns in the description. //# Insert it with the name in the column map. void RefTable::makeRefCol() { for (uInt i=0; incolumn(); i++) { const ColumnDesc& cd = tdescPtr_p->columnDesc(i); colMap_p.insert (std::make_pair(cd.name(), cd.makeRefColumn (this, baseTabPtr_p->getColumn(nameMap_p.at(cd.name()))))); } } //# Add column to this object for an addColumn. void RefTable::addRefCol (const ColumnDesc& columnDesc) { ColumnDesc& cd = tdescPtr_p->addColumn (columnDesc); nameMap_p.insert (std::make_pair(cd.name(), cd.name())); // Use cd (and not columnDesc) because underneath a pointer to its // BaseColumnDesc is kept which is disastrous for the temporary columnDesc. colMap_p.insert (std::make_pair(cd.name(), cd.makeRefColumn (this, baseTabPtr_p->getColumn(nameMap_p.at(cd.name()))))); changed_p = True; } void RefTable::addRefCol (const TableDesc& tdesc) { for (uInt i=0; i= nrow) { nrow = max ( nrow + 1024, rownr_t(1.2f * nrow)); rowStorage_p.resize (nrow, True); AlwaysAssert (rowStorage_p.contiguousStorage(), AipsError); } rowStorage_p[nrrow_p++] = rnr; changed_p = True; } //# Add a row number range of the root table. void RefTable::addRownrRange (rownr_t startRownr, rownr_t endRownr) { rownr_t nrow = rowStorage_p.nelements(); rownr_t new_nrrow_p = nrrow_p + endRownr - startRownr + 1; if (new_nrrow_p > nrow) { rowStorage_p.resize (new_nrrow_p, True); AlwaysAssert (rowStorage_p.contiguousStorage(), AipsError); } rownr_t* rows = rowStorage_p.data(); // Fill with increasing rownr std::iota(rows + nrrow_p, rows + new_nrrow_p, startRownr); nrrow_p = new_nrrow_p; changed_p = True; } //# Set exact number of rows. void RefTable::setNrrow (rownr_t nrrow) { if (nrrow > nrrow_p) { throw (TableError ("RefTable::setNrrow: exceeds current nrrow")); } AlwaysAssert (rowStorage_p.contiguousStorage(), AipsError); nrrow_p = nrrow; changed_p = True; } //# Test if the parent table is writable. Bool RefTable::isWritable() const { return baseTabPtr_p->isWritable(); } void RefTable::copyRefTable (const String& newName, int tableOption) { prepareCopyRename (newName, tableOption); // Save state, write, and restore state. Bool changed = changed_p; Int option = option_p; String name = name_p; changed_p = True; option_p = tableOption; name_p = newName; writeRefTable (False); changed_p = changed; option_p = option; name_p = name; madeDir_p = False; } void RefTable::copy (const String& newName, int tableOption) const { // If a memory table, make a deep copy. if (tableType() == Table::Memory) { deepCopy (newName, Record(), StorageOption(), tableOption, True, Table::AipsrcEndian, False); // If not persistent, make the copy by writing the table. } else if (!madeDir_p) { const_cast(this)->copyRefTable (newName, tableOption); } else { BaseTable::copy (newName, tableOption); } } void RefTable::deepCopy (const String& newName, const Record& dataManagerInfo, const StorageOption& stopt, int tableOption, Bool, int endianFormat, Bool noRows) const { trueDeepCopy (newName, dataManagerInfo, stopt, tableOption, endianFormat, noRows); } int RefTable::tableType() const { return baseTabPtr_p->tableType(); } TableDesc RefTable::actualTableDesc() const { // Get the table description of reftable. const TableDesc& refDesc = tableDesc(); // Get actual table desc of parent. // Create new tabledesc and copy keywords from parent. TableDesc rootDesc = baseTabPtr_p->actualTableDesc(); TableDesc actualDesc(rootDesc, "", "", TableDesc::Scratch, False); // Copy the relevant columns and rename (because reftable // can have renamed columns). for (uInt i=0; i nmap; for (const auto& x : nameMap_p) { nmap.insert (std::make_pair(x.second, x.first)); } actualDesc.adjustHypercolumns (nmap); return actualDesc; } Record RefTable::dataManagerInfo() const { // Get the info of the parent table. // We only have to have this info for the columns in this table. Record dmi = baseTabPtr_p->dataManagerInfo(); // Invert the map to get map of old to new name. std::map nmap; for (const auto& x : nameMap_p) { nmap.insert (std::make_pair(x.second, x.first)); } // Now remove all columns not part of it. // Use the new name. // Remove data managers without columns left. // Iterate in reverse order because of the remove we do. for (uInt i=dmi.nfields(); i>0;) { i--; Record& rec = dmi.rwSubRecord(i); Vector vec (rec.asArrayString ("COLUMNS")); Vector newVec(vec.nelements()); uInt nc=0; for (uInt j=0; j::iterator iter = nmap.find (vec(j)); if (iter != nmap.end()) { newVec(nc++) = iter->second; } } // Remove field if no columns are left. // Otherwise store new names in subrecord. if (nc == 0) { dmi.removeField(i); } else { rec.define ("COLUMNS", newVec(Slice(0,nc))); } } return dmi; } void RefTable::showStructureExtra (ostream& os) const { os << "out of " << baseTabPtr_p->tableName() << " (" << baseTabPtr_p->nrow() << " rows, " << baseTabPtr_p->tableDesc().ncolumn() << " columns)" << endl; } //# Get the keyword set. TableRecord& RefTable::keywordSet() { return baseTabPtr_p->keywordSet(); } //# Get the keyword set. TableRecord& RefTable::rwKeywordSet() { return baseTabPtr_p->rwKeywordSet(); } BaseColumn* RefTable::getColumn (const String& columnName) const { tdescPtr_p->columnDesc(columnName); // check if column exists return colMap_p.at(columnName); } //# We cannot simply return colMap_p.getVal(columnIndex), because the order of //# the columns in the description is important. So first get the column //# name and use that as key. BaseColumn* RefTable::getColumn (uInt columnIndex) const { const String& name = tdescPtr_p->columnDesc(columnIndex).name(); return colMap_p.at(name); } Vector& RefTable::rowStorage() { return rowStorage_p; } //# Convert a vector of row numbers to row numbers in this table. Vector RefTable::rootRownr (const Vector& rownrs) const { const rownr_t* rows = rowStorage_p.data(); rownr_t nrow = rownrs.nelements(); Vector rnr(nrow); for (rownr_t i=0; i RefTable::rowNumbers() const { if (nrrow_p == rowStorage_p.nelements()) { return rowStorage_p; } Vector vec (rowStorage_p); return vec(Slice(0, nrrow_p)); } Bool RefTable::checkAddColumn (const String& name, Bool addToParent) { if (! isWritable()) { throw TableInvOper ("Table::addColumn; table is not writable"); } if (tdescPtr_p->isColumn(name)) { throw TableInvOper ("Table::addColumn; column " + name + " already exists"); } if (baseTabPtr_p->tableDesc().isColumn(name)) { return False; } if (!addToParent) { throw TableInvOper ("RefTable::addColumn; column " + name + " does not exist in parent table, but must not be added" " (addToParent=False)"); } return True; } void RefTable::addColumn (const ColumnDesc& columnDesc, Bool addToParent) { if (checkAddColumn (columnDesc.name(), addToParent)) { baseTabPtr_p->addColumn (columnDesc, addToParent); } addRefCol (columnDesc); } void RefTable::addColumn (const ColumnDesc& columnDesc, const String& dataManager, Bool byName, Bool addToParent) { if (checkAddColumn (columnDesc.name(), addToParent)) { baseTabPtr_p->addColumn (columnDesc, dataManager, byName, addToParent); } addRefCol (columnDesc); } void RefTable::addColumn (const ColumnDesc& columnDesc, const DataManager& dataManager, Bool addToParent) { if (checkAddColumn (columnDesc.name(), addToParent)) { baseTabPtr_p->addColumn (columnDesc,dataManager, addToParent); } addRefCol (columnDesc); } void RefTable::addColumn (const TableDesc& tableDesc, const DataManager& dataManager, Bool addToParent) { // First check if all columns exist and can be added or not. // Collect all columns to be added to the parent. TableDesc addTabDesc; for (uInt i=0; i 0) { baseTabPtr_p->addColumn (addTabDesc, dataManager, addToParent); } addRefCol (tableDesc); } //# Rows and columns can be removed and renamed. Bool RefTable::canRemoveRow() const { return True; } Bool RefTable::canRemoveColumn (const Vector& columnNames) const { return checkRemoveColumn (columnNames, False); } Bool RefTable::canRenameColumn (const String& columnName) const { return tdescPtr_p->isColumn (columnName); } void RefTable::removeRow (rownr_t rownr) { if (rownr >= nrrow_p) { throw (TableInvOper ("removeRow: rownr out of bounds")); } rownr_t* rows = rowStorage_p.data(); if (rownr < nrrow_p - 1) { objmove (rows+rownr, rows+rownr+1, nrrow_p-rownr-1); } nrrow_p--; changed_p = True; } void RefTable::removeAllRow () { nrrow_p=0; changed_p = True; } void RefTable::removeColumn (const Vector& columnNames) { checkRemoveColumn (columnNames, True); for (uInt i=0; iremoveColumn (name); nameMap_p.erase (name); delete colMap_p.at(name); colMap_p.erase (name); } changed_p = True; } void RefTable::renameColumn (const String& newName, const String& oldName) { tdescPtr_p->renameColumn (newName, oldName); RefColumn* colval = colMap_p.at(oldName); colMap_p.erase (oldName); colMap_p.insert (std::make_pair(newName, colval)); const String nmval = nameMap_p.at(oldName); nameMap_p.erase (oldName); nameMap_p.insert (std::make_pair(newName, nmval)); changed_p = True; } void RefTable::renameHypercolumn (const String& newName, const String& oldName) { tdescPtr_p->renameHypercolumn (newName, oldName); changed_p = True; } DataManager* RefTable::findDataManager (const String& name, Bool byColumn) const { String origName(name); if (byColumn) { // A column can be renamed, so use the original name. origName = nameMap_p.at(name); } return baseTabPtr_p->findDataManager (origName, byColumn); } // And 2 index arrays, which are both in ascending order. void RefTable::refAnd (rownr_t nr1, const rownr_t* inx1, rownr_t nr2, const rownr_t* inx2) { rownr_t allrow = (nr1 < nr2 ? nr1 : nr2); // max #output rows rowStorage_p.resize (allrow); // allocate output storage AlwaysAssert (rowStorage_p.contiguousStorage(), AipsError); rownr_t* rows = rowStorage_p.data(); rownr_t i1, i2, row1, row2; i1 = i2 = 0; while (True) { if (i1 >= nr1) { row1 = std::numeric_limits::max(); // end of inx1 }else{ row1 = inx1[i1]; // next element in inx1 } if (i2 >= nr2) { row2 = std::numeric_limits::max(); // end of inx2 }else{ row2 = inx2[i2]; // next element in inx2 } if (row1 == row2) { if (row1 == std::numeric_limits::max()) break; // end of both inx rows[nrrow_p++] = row1; i1++; i2++; }else{ if (row1 < row2) { i1++; // next inx1 }else{ i2++; // next inx2 } } } changed_p = True; } // Or 2 index arrays, which are both in ascending order. void RefTable::refOr (rownr_t nr1, const rownr_t* inx1, rownr_t nr2, const rownr_t* inx2) { rownr_t allrow = nr1 + nr2; // max #output rows rowStorage_p.resize (allrow); // allocate output storage AlwaysAssert (rowStorage_p.contiguousStorage(), AipsError); rownr_t* rows = rowStorage_p.data(); rownr_t i1, i2, row1, row2; i1 = i2 = 0; while (True) { if (i1 >= nr1) { row1 = std::numeric_limits::max(); // end of inx1 }else{ row1 = inx1[i1]; // next element in inx1 } if (i2 >= nr2) { row2 = std::numeric_limits::max(); // end of inx2 }else{ row2 = inx2[i2]; // next element in inx2 } if (row1 == row2) { if (row1 == std::numeric_limits::max()) break; // end of both inx rows[nrrow_p++] = row1; i1++; i2++; }else{ if (row1 < row2) { rows[nrrow_p++] = row1; i1++; // next inx1 }else{ rows[nrrow_p++] = row2; i2++; // next inx2 } } } changed_p = True; } // Subtract 2 index arrays, which are both in ascending order. void RefTable::refSub (rownr_t nr1, const rownr_t* inx1, rownr_t nr2, const rownr_t* inx2) { rownr_t allrow = nr1; // max #output rows rowStorage_p.resize (allrow); // allocate output storage AlwaysAssert (rowStorage_p.contiguousStorage(), AipsError); rownr_t* rows = rowStorage_p.data(); rownr_t i1, i2, row1, row2; i1 = i2 = 0; while (True) { if (i1 >= nr1) { row1 = std::numeric_limits::max(); // end of inx1 }else{ row1 = inx1[i1]; // next element in inx1 } if (i2 >= nr2) { row2 = std::numeric_limits::max(); // end of inx2 }else{ row2 = inx2[i2]; // next element in inx2 } if (row1 == row2) { if (row1 == std::numeric_limits::max()) break; // end of both inx i1++; i2++; }else{ if (row1 < row2) { rows[nrrow_p++] = row1; i1++; // next inx1 }else{ i2++; // next inx2 } } } changed_p = True; } // Xor 2 index arrays, which are both in ascending order. void RefTable::refXor (rownr_t nr1, const rownr_t* inx1, rownr_t nr2, const rownr_t* inx2) { rownr_t allrow = nr1 + nr2; // max #output rows rowStorage_p.resize (allrow); // allocate output storage AlwaysAssert (rowStorage_p.contiguousStorage(), AipsError); rownr_t* rows = rowStorage_p.data(); rownr_t i1, i2, row1, row2; i1 = i2 = 0; while (True) { if (i1 >= nr1) { row1 = std::numeric_limits::max(); // end of inx1 }else{ row1 = inx1[i1]; // next element in inx1 } if (i2 >= nr2) { row2 = std::numeric_limits::max(); // end of inx2 }else{ row2 = inx2[i2]; // next element in inx2 } if (row1 == row2) { if (row1 == std::numeric_limits::max()) break; // end of both inx i1++; i2++; }else{ if (row1 < row2) { rows[nrrow_p++] = row1; i1++; // next inx1 }else{ rows[nrrow_p++] = row2; i2++; // next inx2 } } } changed_p = True; } // Negate a table. void RefTable::refNot (rownr_t nr, const rownr_t* inx, rownr_t nrtot) { // All rows not in the original table must be "selected". // The original table has NRTOT rows. // So loop through the inx-array and store all rownrs not in the array. rownr_t allrow = nrtot - nr; // #output rows rowStorage_p.resize (allrow); // allocate output storage AlwaysAssert (rowStorage_p.contiguousStorage(), AipsError); rownr_t* rows = rowStorage_p.data(); rownr_t start = 0; rownr_t i, j; for (i=0; i #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TSMOption; class RefColumn; class AipsIO; // // Class for a table as a view of another table // // // // // //# Classes you should understand before using this one. //
      • BaseTable //
      • RefColumn // // // RefTable represents a table which is a view on another table, // thus which references another table. // // // RefTable is used to make a view on another table. // Usually it is a view on a subset of the table, either in vertical // or horizontal direction. Thus a subset of rows and/or columns. // It will be the result of a select, sort, project or iterate function. // // It acts to the user as a normal table. All gets and puts are // handled by RefColumn which directs them to the referenced column // while (if needed) converting the given row number to the row number // in the referenced table. For that purpose RefTable maintains a // Vector of the row numbers in the referenced table. // // The RefTable constructor acts in a way that it will always reference // the original table. This means that if a select is done on a RefTable, // the resulting RefTable will also reference the original PlainTable. // This is done to avoid long chains of RefTables. // However, if ever some other kind of table views are introduced // (like a join or a concatenation of similar tables), this cannot be // used anymore. Most software already anticipates on that. The only // exception is the code anding, oring tables (refAnd, etc.). // // //# A List of bugs, limitations, extensions or planned refinements. //
      • Maybe not allocating the row number vector for a projection. // This saves space and time, but each rownr conversion will // take a bit more time because it has to test if there is a vector. //
      • Maybe maintain a Vector telling on which columns // the table is ordered. This may speed up selection, but // it is hard to check if the order is changed by a put. //
      • Allow to remove a row or column from the RefTable //
      • Allow to rename a column in the RefTable //
      • Maybe implement doSort one time for a more efficient sort. // (now everything is handled by BaseTable). // class RefTable : public BaseTable { public: // Create a reference table object referencing the // given BaseTable object. // If the BaseTable is actually another RefTable, it will reference // its referenced table (thus the original table) and it will // take its vector of row numbers and projected column names // into account. Thus if a select is done on a projected table, // the resulting RefTable will have the same projection. // // Construct a RefTable with an empty row number vector. // rowOrder=True indicates that the order of the rows will not // be disturbed (as will be the case for a sort). // A row number vector of the given size is initially allocated. // Later this RefTable will be filled in by the select, etc.. RefTable (BaseTable*, Bool rowOrder, rownr_t initialNrrow); // A RefTable with the given row numbers is constructed. RefTable (BaseTable*, const Vector& rowNumbers); // Create a reference table object out of a mask. // The row number vector will consist of the rows for which the // mask has a True value. // The length of the mask must be the number of rows in the BaseTable. RefTable (BaseTable*, const Vector& rowMask); // Create a reference table object via projection (i.e. column selection). // The row number vector is a copy of the given table. RefTable (BaseTable*, const Vector& columnNames); // // Create a reference table out of a file (written by writeRefTable). // The referenced table will also be created (if not stored in the cache). RefTable (AipsIO&, const String& name, rownr_t nrrow, int option, const TableLock& lockOptions, const TSMOption& tsmOption); // The destructor flushes (i.e. writes) the table if it is opened // for output and not marked for delete. virtual ~RefTable(); // Copy constructor is forbidden, because copying a table requires // some more knowledge (like table name of result). RefTable (const RefTable&) = delete; // Assignment is forbidden, because copying a table requires // some more knowledge (like table name of result). RefTable& operator= (const RefTable&) = delete; // Return the layout of a table (i.e. description and #rows). // This function has the advantage that only the minimal amount of // information required is read from the table, thus it is much // faster than a normal table open. //
        The number of rows is returned. The description of the table // is stored in desc (its contents will be overwritten). static void getLayout (TableDesc& desc, AipsIO& ios); // Try to reopen the table (the underlying one) for read/write access. // An exception is thrown if the table is not writable. // Nothing is done if the table is already open for read/write. virtual void reopenRW(); // Is the table stored in big or little endian format? virtual Bool asBigEndian() const; // Get the storage option used for the table. virtual const StorageOption& storageOption() const; // Is the table in use (i.e. open) in another process? // It always returns False. virtual Bool isMultiUsed (Bool checkSubTable) const; // Get the locking info. virtual const TableLock& lockOptions() const; // Merge the given lock info with the existing one. virtual void mergeLock (const TableLock& lockOptions); // Has this process the read or write lock, thus can the table // be read or written safely? virtual Bool hasLock (FileLocker::LockType) const; // Try to lock the table for read or write access. virtual Bool lock (FileLocker::LockType, uInt nattempts); // Unlock the table. This will also synchronize the table data, // thus force the data to be written to disk. virtual void unlock(); // Flush the table, i.e. write it to disk. // Nothing will be done if the table is not writable. // A flush can be executed at any time. // When a table is marked for delete, the destructor will remove // files written by intermediate flushes. // Note that if necessary the destructor will do an implicit flush, // unless it is executed due to an exception. virtual void flush (Bool fsync, Bool recursive); // Resync the Table object with the table file. virtual void resync(); // Get the modify counter. virtual uInt getModifyCounter() const; // Test if the parent table is opened as writable. virtual Bool isWritable() const; // Read a reference table from a file. // The referenced table will also be created (if not stored in the cache). void getRef (AipsIO&, int option, const TableLock& lockOptions, const TSMOption& tsmOption); // This is doing a shallow copy. // It gives an error if the RefTable has not been stored yet. virtual void copy (const String& newName, int tableOption) const; // Copy the table and all its subtables. // It copies the contents of each row to get a real copy. virtual void deepCopy (const String& newName, const Record& dataManagerInfo, const StorageOption&, int tableOption, Bool, int endianFormat, Bool noRows) const; // It returns the type of the parent table. virtual int tableType() const; // Get the actual table description. virtual TableDesc actualTableDesc() const; // Get the data manager info. virtual Record dataManagerInfo() const; // Get readonly access to the table keyword set. virtual TableRecord& keywordSet(); // Get read/write access to the table keyword set. // This requires that the table is locked (or it gets locked // when using AutoLocking mode). virtual TableRecord& rwKeywordSet(); // Get a column object using its index. virtual BaseColumn* getColumn (uInt columnIndex) const; // Get a column object using its name. virtual BaseColumn* getColumn (const String& columnName) const; // Test if it is possible to remove a row from this table. virtual Bool canRemoveRow() const; // Remove the given row. virtual void removeRow (rownr_t rownr); // Remove the given row. virtual void removeAllRow (); // Add one or more columns to the table. // The column is added to the parent table if told so and if not existing. // virtual void addColumn (const ColumnDesc& columnDesc, Bool addToParent); virtual void addColumn (const ColumnDesc& columnDesc, const String& dataManager, Bool byName, Bool addToParent); virtual void addColumn (const ColumnDesc& columnDesc, const DataManager& dataManager, Bool addToParent); virtual void addColumn (const TableDesc& tableDesc, const DataManager& dataManager, Bool addToParent); // // Test if columns can be removed (yes). virtual Bool canRemoveColumn (const Vector& columnNames) const; // Remove columns. virtual void removeColumn (const Vector& columnNames); // Test if a column can be renamed (yes). virtual Bool canRenameColumn (const String& columnName) const; // Rename a column. virtual void renameColumn (const String& newName, const String& oldName); // Rename a hypercolumn. virtual void renameHypercolumn (const String& newName, const String& oldName); // Find the data manager with the given name or for the given column. virtual DataManager* findDataManager (const String& name, Bool byColumn) const; // Get a vector of row numbers. virtual Vector rowNumbers() const; // Get parent of this table. virtual BaseTable* root(); // Get rownr in root table. // This converts the given row number to the row number in the root table. rownr_t rootRownr (rownr_t rownr) const; // Get vector of rownrs in root table. // This converts the given row numbers to row numbers in the root table. Vector rootRownr (const Vector& rownrs) const; // Tell if the table is in row order. virtual Bool rowOrder() const; // Get row number vector. // This is used by the BaseTable logic and sort routines. virtual Vector& rowStorage(); // Add a rownr to reference table. void addRownr (rownr_t rownr); void addRownrRange (rownr_t startRownr, rownr_t endRownr); // Set the exact number of rows in the table. // An exception is thrown if more than current nrrow. void setNrrow (rownr_t nrrow); // Adjust the row numbers to be the actual row numbers in the // root table. This is, for instance, used when a RefTable is sorted. // Optionally it also determines if the resulting rows are in row order. virtual Bool adjustRownrs (rownr_t nrrow, Vector& rownrs, Bool determineOrder) const; // And, or, subtract or xor the row numbers of 2 tables. void refAnd (rownr_t nr1, const rownr_t* rows1, rownr_t nr2, const rownr_t* rows2); void refOr (rownr_t nr1, const rownr_t* rows1, rownr_t nr2, const rownr_t* rows2); void refSub (rownr_t nr1, const rownr_t* rows1, rownr_t nr2, const rownr_t* rows2); void refXor (rownr_t nr1, const rownr_t* rows1, rownr_t nr2, const rownr_t* rows2); void refNot (rownr_t nr1, const rownr_t* rows1, rownr_t nrmain); private: std::shared_ptr baseTabPtr_p;//# pointer to parent table Bool rowOrd_p; //# True = table is in row order Vector rowStorage_p; //# row numbers in parent table std::map nameMap_p; //# map to column name in parent std::map colMap_p; //# map name to column Bool changed_p; //# True = changed since last write // Get the names of the tables this table consists of. virtual void getPartNames (Block& names, Bool recursive) const; // Show the extra table structure info (name of root table). void showStructureExtra (std::ostream&) const; // Make a table description for the given columns. static void makeDesc (TableDesc& desc, const TableDesc& rootDesc, std::map& nameMap, Vector& names); // Setup the main parts of the object. //
        First create the name map (mapping column name in RefTable to // the column in the original table). // If the BaseTable is a RefTable, use its name map. // Otherwise create the initial name map from the table description. // A rename might change the map. //
        Create the RefColumn objects. //
        Create the initial TableInfo as a copy of the original BaseTable. void setup (BaseTable* btp, const Vector& columnNames); // Create the RefColumn objects for all columns in the description. void makeRefCol(); // Write a reference table. void writeRefTable (Bool fsync); // Copy a RefTable that is not persistent. It requires some special logic. void copyRefTable (const String& newName, int tableOption); // Check if a column can be added. Return True if it can and must be // added to the parent table first. Bool checkAddColumn (const String& name, Bool addToParent); // Add a column. void addRefCol (const ColumnDesc& cd); // Add multiple columns. void addRefCol (const TableDesc& tdesc); }; inline rownr_t RefTable::rootRownr (rownr_t rnr) const { return rowStorage_p[rnr]; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/RowCopier.cc000066400000000000000000000122721476623553700202340ustar00rootroot00000000000000//# RowCopier.cc: RowCopier copies part or all of a row from one table to another. //# Copyright (C) 1995,1996,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // this class is used internally by RowCopier and is what really does the work. class ColumnHolder { public: ColumnHolder(Table &inTab, const Table &outTab); ~ColumnHolder(); //# The following constructors and operator don't seem to be useful ColumnHolder(const ColumnHolder &other) = delete; ColumnHolder &operator=(const ColumnHolder &other) = delete; void attach(const String &outCol, const String &inCol); Bool copy(rownr_t toRow, rownr_t fromRow); private: // The tables involved in the copying Table in; Table out; // Blocks of pointers to the TableColumns that will be involved in copying PtrBlock inTabCol; PtrBlock outTabCol; }; ColumnHolder::ColumnHolder(Table &outTab, const Table &inTab) : in(inTab), out(outTab) {} ColumnHolder::~ColumnHolder() { for (uInt colNum=0; colNum < inTabCol.nelements(); colNum++) { delete inTabCol[colNum]; delete outTabCol[colNum]; inTabCol[colNum] = 0; outTabCol[colNum] = 0; } } void ColumnHolder::attach(const String &outCol, const String &inCol) { if (!out.tableDesc().isColumn(outCol) || !in.tableDesc().isColumn(inCol)) { throw(TableError("RowCopier: " + inCol + " or " + outCol + " is not a column")); } // out isWritable() but individual columns may not be writable // for now, we throw an exception of outCol is not writable if (! out.isColumnWritable(outCol)) { throw(TableError("RowCopier: output table must be writable")); } // TBF The block resizes are a quadratic behaviour; we should make them // larger in powers of (say) two instead. if (out.tableDesc()[outCol].isScalar() == in.tableDesc()[inCol].isScalar() && out.tableDesc()[outCol].dataType() == in.tableDesc()[inCol].dataType()) { inTabCol.resize(inTabCol.nelements() + 1); outTabCol.resize(outTabCol.nelements() + 1); inTabCol[inTabCol.nelements() - 1] = new TableColumn(in,inCol); outTabCol[outTabCol.nelements() - 1] = new TableColumn(out,outCol); } else { throw(TableError("RowCopier: " + inCol + " and " + outCol + " are not conformant")); } } Bool ColumnHolder::copy(rownr_t toRow, rownr_t fromRow) { if (fromRow >= in.nrow() || toRow >= out.nrow()) { return False; } // loop over all columns for (uInt i=0; i < inTabCol.nelements(); i++) { outTabCol[i]->put(toRow, (*inTabCol[i]), (fromRow)); } return True; } RowCopier::RowCopier(Table &out, const Table &in) { if (! out.isWritable()) { throw(TableError("RowCopier: output table must be writable")); } columns_p = std::make_shared(out,in); for (uInt i=0; i < out.tableDesc().ncolumn(); i++) { TableColumn outCol(out, i); String name (outCol.columnDesc().name()); if (in.tableDesc().isColumn(name)) { TableColumn inCol(in, name); columns_p->attach(name, inCol.columnDesc().name()); } } } RowCopier::RowCopier(Table &out, const Table &in, const Vector& outNames, const Vector& inNames) { if (! out.isWritable()) { throw(TableError("RowCopier: output table must be writable")); } columns_p = std::make_shared(out,in); if (inNames.nelements() != outNames.nelements()) { throw(TableError("RowCopier: Non-conformant column name vectors")); } for (uInt i=0; iattach(outNames(i), inNames(i)); } } Bool RowCopier::copy(rownr_t toRow, rownr_t fromRow) { return columns_p->copy(toRow, fromRow); } RowCopier::~RowCopier() {} } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/RowCopier.h000066400000000000000000000172261476623553700201020ustar00rootroot00000000000000//# RowCopier.h: RowCopier copies part or all of a row from one table to another. //# Copyright (C) 1995,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_ROWCOPIER_H #define TABLES_ROWCOPIER_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class Table; class String; class ColumnHolder; //# Only in the .cc file // // RowCopier copies all or part of a row from one table to another. // // // // // //
      • Table // // // RowCopier is a class that copies rows from one table to another, hence // the name. // // // The primary organization in a Table is the TableColumn. However, data // is often organized primarily by rows, at least initially (e.g. for an // on-line system, the data will arrive in chunks that are likely to be // individual rows rather than individual columns). RowCopier is used // to copy values in a row from all or some of the columns of one table to // another table. // // Some things to keep in mind: //
          //
        1. For each column to be copied, the data types and dimensionality // must match. //
        2. The input row number need not be the same as the output row number. //
        3. The output row number must already exist (i.e. no new rows are created). //
        4. The output column name need not be the same as the input column name. //
        5. The output column name and input column name, when specified, must // already exist. //
        6. The output table and each output column must be writable. //
        //
        // // In the FITS Binary Table extension to Table conversion class, BinTable, // the input FITS file is a stream that must be read sequentially, so the // input arrives row-by-row. Internally, there is a single row table that // is used to hold the values for the current row. To fill a Casacore table // with the data from each row, one creates the output table using the // table descriptor from the input, single-row table and uses RowCopier to // copy the single-row table to the appropriate row of the full table, // refilling the single-row table at each step. This is how that looks // (leaving out some details not important to this example): // // Background: // singleRowTab is a table constisting of a single row. It is filled // from the input FITS classes using the fillRow() member function. // The nrows() member function returns the total number of FITS binary // table rows and currrow() returns the current row number. // // // // Create an empty Table able to hold all remaining FITS rows, including // // the current one and having the same descriptor as singleRowTab // SetupNewTable newTab("FullTable", singleRowTab.getDescriptor(), // Table:New); // Table full(newTab, (nrows() - currrow() + 1)); // // create the copier to copy all columns // RowCopier copier(full, singleRowTab); // // loop over all remaining rows // // since full was just created, we start filling it at row 0. // for (rownr_t outRow = 0, fitsRow = currrow(); fitsRow < nrows(); // outRow++, fitsRow++) { // // copy the only row from currRowTab (row 0) to the outRow of full // copier.copy(outRow, 0); // // fill the next row of currRowTab // fillRow(); // } // // // This example shows how to copy some of the values from one table to // another. This is a contrived example. The input table // has columns named "HSource" and "VSource" along with other columns. // This example places the values from these columns to columns named // "RA (1950)" and "DEC (1950)" in the output table (which also has other // columns). Note that each input column must have the same type and // dimensionality as the corresponding output column. // // // // construct a vector of the input column names to copy and the // // associated output column names // Vector inColNames(2), outColNames(2); // inColNames(0) = "HSource"; outColNames(0) = "RA (1950)" // inColNames(1) = "VSource"; outColNames(1) = "DEC (1950)" // // // construct the copier // RowCopier copier(inTable, outTable, inColNames, outColNames); // // // Copy a row from in to out, obviously a typical use would do // // more than just one row. // copier.copy(outRownr, outRownr-1); // // // // See the comments in the synopsis. // // //
      • resize should probably happen in powers of 2 //
      • is throwing exceptions really what we want to happen? // class RowCopier { public: // This constructs a copier which will copy all columns which have the // same name in both tables from in to out. // An exception is thrown if the columns having the same name in both // tables are not conformant (not the same type and not both scalar of both // array columns) // //
      • TableError // RowCopier (Table &out, const Table &in); // This constructs a copier which will copy innames columns to outnames // columns, outnames and innames must be conformant. Columns are // matched up element-by-element in innames and outnames. // An exception is thrown if an element of innames or outnames is not // present in the corresponding table, if innames and outnames are // not conformant and if the corresponding columns are not conformant // (not the same type and not both scalar or both array columns) // //
      • TableError // RowCopier (Table &out, const Table &in, const Vector& outNames, const Vector& inNames); ~RowCopier(); //# The following constructors and operator don't seem to be useful // RowCopier(const RowCopier &other) = delete; RowCopier &operator=(const RowCopier &other) = delete; // // The things that actually do the copying when requested. // // Copy different row numbers. Bool copy (rownr_t toRow, rownr_t fromRow); // Copy to and from the same row number Bool copy (rownr_t rownr); // private: // The ColumnHolder class exists only in the .cc file, it is what // ultimately does the work. std::shared_ptr columns_p; }; inline Bool RowCopier::copy (rownr_t rownr) { return copy (rownr, rownr); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/RowNumbers.cc000066400000000000000000000047611476623553700204320ustar00rootroot00000000000000//# RowNumbers.cc: Vector of row numbers //# Copyright (C) 2019 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include namespace casacore { //#Begin namespace casacore RowNumbers::RowNumbers (const std::vector& rows) { // Resize the Vector in the parent class and convert to it. resize (rows.size()); std::copy (rows.begin(), rows.end(), this->cbegin()); } RowNumbers::RowNumbers (const Vector& rows) { // Resize the Vector in the parent class and convert to it. resize (rows.size()); convertArray (*this, rows); } RowNumbers::RowNumbers (const std::vector& rows) { // Resize the Vector in the parent class and convert to it. resize (rows.size()); std::copy (rows.begin(), rows.end(), this->cbegin()); } Array& RowNumbers::operator= (const Array& other) { Vector::operator= (other); return *this; } Vector RowNumbers::convertRownrVector (const Vector& rows64) { AlwaysAssert (allLE (rows64, rownr_t(std::numeric_limits::max())), AipsError); Vector rows(rows64.size()); convertArray (rows, rows64); return rows; } } //#End namespace casacore casacore-3.7.1/tables/Tables/RowNumbers.h000066400000000000000000000055231476623553700202710ustar00rootroot00000000000000//# RowNumbers.h: Vector of row numbers //# Copyright (C) 2019 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_ROWNUMBERS_H #define TABLES_ROWNUMBERS_H //# Includes #include #include namespace casacore { //#Begin namespace casacore class RowNumbers: public Vector { public: // Construct an empty RowNumbers Vector. RowNumbers() {} // Construct with the given length. explicit RowNumbers (size_t n) : Vector (n) {} // Construct from a Vector of row numbers. RowNumbers (const Vector& rows) : Vector (rows) {} // Construct from a std::vector of row numbers. RowNumbers (const std::vector& rows); // Array has this virtual function, so also define in this // class to avoid 'virtual function override' warning. virtual Array& operator= (const Array& other); // Construct from a Vector or std::vector of old style row numbers. #ifdef IMPLICIT_CTDS_32BIT RowNumbers (const Vector& rows); RowNumbers (const std::vector& rows); #else explicit RowNumbers (const Vector& rows); explicit RowNumbers (const std::vector& rows); #endif // Conversion operator to convert Vector to Vector. // This is for backward compatibility of Table::rowNumbers. #ifdef IMPLICIT_CTDS_32BIT operator Vector() const #else explicit operator Vector() const #endif { return convertRownrVector (*this); } // Do the actual conversion. // An exception is thrown if a row number exceeds 32 bits. static Vector convertRownrVector (const Vector&); }; } //#End namespace casacore #endif casacore-3.7.1/tables/Tables/ScaColData.h000066400000000000000000000170441476623553700201250ustar00rootroot00000000000000//# ScaColData.h: Access to a table column containing scalars //# Copyright (C) 1994,1995,1996,1998,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_SCACOLDATA_H #define TABLES_SCACOLDATA_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class ColumnSet; template class ScalarColumnDesc; class AipsIO; // // Access to a table column containing scalars // // // // // //# Classes you should understand before using this one. //
      • PlainColumn //
      • ScalarColumnDesc //
      • Table // // // ScalarColumnData represents a table column containing scalars. // // // The class ScalarColumnData is derived from PlainColumn. // It implements the virtual functions accessing a table column // containing scalars with an arbitrary data type. // // It is possible to access an scalar an individual cell (i.e. table row) // or in the entire column. // // The main task of this class is to communicate with the data manager // column object. This consists of: //
          //
        • Binding itself to a data manager. //
        • Letting the data manager create its column object. //
        • Closing the data manager column object (in putFileDerived). //
        • Reconstructing the data manager object for an existing table // (in getFileDerived). //
        • Transferring get/put calls to the data manager column object. //
        // // The class is hidden from the user by the envelope class ScalarColumn. // It used directly, it should be done with care. It assumes that the // arrays in the various get and put functions have the correct length. // ScalarColumn does that check. //
        // //# A List of bugs, limitations, extensions or planned refinements. // template class ScalarColumnData : public PlainColumn { public: // Construct a scalar column object from the given description // in the given column set. // This constructor is used by ScalarColumnDesc::makeColumn. ScalarColumnData (const ScalarColumnDesc*, ColumnSet*); ~ScalarColumnData(); // Initialize the rows from startRownr till endRownr (inclusive) // with the default value defined in the column description. virtual void initialize (rownr_t startRownr, rownr_t endRownr); // Test if the given cell contains a defined value. virtual Bool isDefined (rownr_t rownr) const; // Get the value from a particular cell. virtual void get (rownr_t rownr, void*) const; // Get the array of all values in the column. // The length of the buffer pointed to by dataPtr must match // the actual length. This is checked by ScalarColumn. virtual void getScalarColumn (ArrayBase& dataPtr) const; // Get the array of some values in the column (on behalf of RefColumn). // The length of the buffer pointed to by dataPtr must match // the actual length. This is checked by ScalarColumn. virtual void getScalarColumnCells (const RefRows& rownrs, ArrayBase& dataPtr) const; // Put the value in a particular cell. // The length of the buffer pointed to by dataPtr must match // the actual length. This is checked by ScalarColumn. virtual void put (rownr_t rownr, const void* dataPtr); // Put the array of all values in the column. // The length of the buffer pointed to by dataPtr must match // the actual length. This is checked by ScalarColumn. virtual void putScalarColumn (const ArrayBase& dataPtr); // Put the array of some values in the column (on behalf on RefColumn). // The length of the buffer pointed to by dataPtr must match // the actual length. This is checked by ScalarColumn. virtual void putScalarColumnCells (const RefRows& rownrs, const ArrayBase& dataPtr); // Add the sort key to the Sort object on behalf of the Table sort function. // virtual void makeSortKey (Sort&, std::shared_ptr& cmpFunc, Int order, std::shared_ptr& dataSave); // Do it only for the given row numbers. void makeRefSortKey (Sort&, std::shared_ptr& cmpFunc, Int order, const Vector& rownrs, std::shared_ptr& dataSave); // // Allocate value buffers for the table iterator. // Also get a comparison object if undefined. // The function freeIterBuf must be called to free the buffers. void allocIterBuf (void*& lastVal, void*& curVal, std::shared_ptr& cmpObj); // Free the value buffers allocated by allocIterBuf. void freeIterBuf (void*& lastVal, void*& curVal); // Create a data manager column object for this column. void createDataManagerColumn(); private: // Pointer to column description. const ScalarColumnDesc* scaDescPtr_p; // Undefined value can exist? Bool undefFlag_p; // Undefined value. T undefVal_p; // Copy constructor cannot be used. ScalarColumnData (const ScalarColumnData&); // Assignment cannot be used. ScalarColumnData& operator= (const ScalarColumnData&); // Write the column data. // The control information is written into the given AipsIO object, // while the data is written/flushed by the data manager. void putFileDerived (AipsIO&); // Read the column data back. // The control information is read from the given AipsIO object. // This is used to bind the column to the appropriate data manager. // Thereafter the data manager gets opened. void getFileDerived (AipsIO&, const ColumnSet&); // Fill in the sort key on behalf of the Table sort function. // The pointer to the data (which can be allocated on the heap) // is stored in dataPtr. This is used by freeSortKey to release it. // It checks if a compare function is given when needed. // //
      • TableInvSort // void fillSortKey (const Vector* dataPtr, Sort&, std::shared_ptr& cmpObj, Int order); }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/tables/Tables/ScaColData.tcc000066400000000000000000000201331476623553700204400ustar00rootroot00000000000000//# ScaColData.cc: Access to a table column containing scalars //# Copyright (C) 1994,1995,1996,1997,1998,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_SCACOLDATA_TCC #define TABLES_SCACOLDATA_TCC #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template ScalarColumnData::ScalarColumnData (const ScalarColumnDesc* cd, ColumnSet* csp) : PlainColumn (cd, csp), scaDescPtr_p (cd), undefFlag_p (False), undefVal_p (cd->defaultValue()) { if ((cd->options() & ColumnDesc::Undefined) == ColumnDesc::Undefined) { undefFlag_p = True; } } template ScalarColumnData::~ScalarColumnData() {} template void ScalarColumnData::createDataManagerColumn() { dataColPtr_p = dataManPtr_p->createScalarColumn (colDescPtr_p->name(), colDescPtr_p->dataType(), colDescPtr_p->dataTypeId()); //# Set the maximum length of an item. dataColPtr_p->setMaxLength (colDescPtr_p->maxLength()); } template void ScalarColumnData::initialize (rownr_t startRow, rownr_t endRow) { if (colDescPtr_p->dataType() != TpOther) { for (rownr_t i=startRow; i<=endRow; i++) { dataColPtr_p->put (i, &(scaDescPtr_p->defaultValue())); } } } template Bool ScalarColumnData::isDefined (rownr_t rownr) const { if (!undefFlag_p) { return True; } T val; dataColPtr_p->get (rownr, &val); return ( (!(val == undefVal_p))); } template void ScalarColumnData::get (rownr_t rownr, void* val) const { if (rtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'r', rownr); } checkReadLock (True); dataColPtr_p->get (rownr, static_cast(val)); autoReleaseLock(); } template void ScalarColumnData::getScalarColumn (ArrayBase& val) const { if (rtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'r'); } if (val.ndim() != 1 || val.nelements() != nrow()) { throw (TableArrayConformanceError("ScalarColumnData::getScalarColumn")); } checkReadLock (True); dataColPtr_p->getScalarColumnV (val); autoReleaseLock(); } template void ScalarColumnData::getScalarColumnCells (const RefRows& rownrs, ArrayBase& val) const { if (rtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'r', rownrs); } if (val.ndim() != 1 || val.nelements() != rownrs.nrow()) { throw (TableArrayConformanceError("ScalarColumnData::getScalarColumnCells")); } checkReadLock (True); dataColPtr_p->getScalarColumnCellsV (rownrs, val); autoReleaseLock(); } template void ScalarColumnData::put (rownr_t rownr, const void* val) { if (wtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'w', rownr); } checkValueLength (static_cast(val)); checkWriteLock (True); dataColPtr_p->put (rownr, static_cast(val)); autoReleaseLock(); } template void ScalarColumnData::putScalarColumn (const ArrayBase& val) { if (wtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'w'); } if (val.ndim() != 1 || val.nelements() != nrow()) { throw (TableArrayConformanceError("ScalarColumnData::putColumn")); } checkValueLength (static_cast*>(&val)); checkWriteLock (True); dataColPtr_p->putScalarColumnV (val); autoReleaseLock(); } template void ScalarColumnData::putScalarColumnCells (const RefRows& rownrs, const ArrayBase& val) { if (wtraceColumn_p) { TableTrace::trace (traceId(), columnDesc().name(), 'w', rownrs); } if (val.ndim() != 1 || val.nelements() != rownrs.nrow()) { throw (TableArrayConformanceError("ScalarColumnData::putColumn")); } checkValueLength (static_cast*>(&val)); checkWriteLock (True); dataColPtr_p->putScalarColumnCellsV (rownrs, val); autoReleaseLock(); } template void ScalarColumnData::makeSortKey (Sort& sortobj, std::shared_ptr& cmpObj, Int order, std::shared_ptr& dataSave) { //# Get the data as a column. //# Save the pointer to the vector for deletion by freeSortKey(). Vector* vecPtr = new Vector(nrow()); dataSave.reset (vecPtr); getScalarColumn (*vecPtr); fillSortKey (vecPtr, sortobj, cmpObj, order); } template void ScalarColumnData::makeRefSortKey (Sort& sortobj, std::shared_ptr& cmpObj, Int order, const Vector& rownrs, std::shared_ptr& dataSave) { //# Get the data as a column. Vector* vecPtr = new Vector(rownrs.size()); dataSave.reset (vecPtr); getScalarColumnCells (rownrs, *vecPtr); fillSortKey (vecPtr, sortobj, cmpObj, order); } template void ScalarColumnData::fillSortKey (const Vector* vecPtr, Sort& sortobj, std::shared_ptr& cmpObj, Int order) { //# Pass the real vector storage as the sort data. //# Use the compare function if given, otherwise pass data type. //# Throw an exception if no compare function is given for //# an unknown data type. AlwaysAssert (vecPtr->contiguousStorage(), AipsError); if (!cmpObj) { cmpObj = std::make_shared>(); } sortobj.sortKey (vecPtr->data(), cmpObj, sizeof(T), order == Sort::Descending ? Sort::Descending : Sort::Ascending); } template void ScalarColumnData::allocIterBuf (void*& lastVal, void*& curVal, std::shared_ptr& cmpObj) { T* valp = new T[2]; lastVal = valp; curVal = valp + 1; if (!cmpObj) { cmpObj = std::make_shared>(); } } template void ScalarColumnData::freeIterBuf (void*& lastVal, void*& curVal) { delete [] static_cast(lastVal); lastVal = 0; curVal = 0; } //# It was felt that putstart takes too much space, so therefore //# the version is put "manually". template void ScalarColumnData::putFileDerived (AipsIO& ios) { ios << (uInt)1; // class version 1 ios << dataManPtr_p->sequenceNr(); } template void ScalarColumnData::getFileDerived (AipsIO& ios, const ColumnSet& colset) { uInt version; ios >> version; uInt seqnr; ios >> seqnr; dataManPtr_p = colset.getDataManager (seqnr); createDataManagerColumn(); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/ScaColDesc.h000066400000000000000000000240331476623553700201260ustar00rootroot00000000000000//# ScaColDesc.h: Templated class for description of table scalar columns //# Copyright (C) 1994,1995,1996,1997,1998,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_SCACOLDESC_H #define TABLES_SCACOLDESC_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class PlainColumn; class ColumnSet; // // Templated class to define columns of scalars in tables // // // // // //
      • BaseColumnDesc //
      • TableDesc // // // This class builds descriptions of table columns where each cell (which // may also be called a row) will hold a scalar value. // // // ScalarColumnDesc is a templated class for defining a // table column containing scalar values. // Note that class // ScalarRecordColumnDesc // has to be used to define the description of a column containing records. //

        // The table values are handled by a data manager. This can be // a storage manager to store the values in a file or it can be // a virtual column engine to calculate them on-the-fly. // Only the basic data types are allowed when storing in a file. These are: // Bool, uChar, Short, uShort, Int, uInt, Int64, float, double, // Complex, DComplex and String. //

        // At table creation time (when a table gets created from a table // description), each column needs to be bound to a data manager. // If not done explicitly, the table system will bind a column to the // default data manager defined in the column description. //

        // A scalar column description consists of the following attributes: //

          //
        • Name, which has to be unique and must also be different // from possible table keyword names. //
        • Data type, which is determined by the template parameter // (e.g. ArrayColumnDesc). //
        • A data type id, which tells the unique name of non-standard // data types (i.e. for data type == TpOther). //
        • Comment, which defaults to an empty string. // This serves purely as an informational string for the user. //
        • Default value, which is only possible for the standard data types. // It defaults to the undefined value defined in ValType.h. // When a row gets added to a table, it is possible to // initialize the column fields in the row with this default value. //
        • Default data manager, which will be used if a column // for a newly created table is not explicitly bound to a // data manager. //
        • Data manager group, which serves 2 purposes. // Firstly it can be used in class SetupNewTable to bind a group // of columns. // Secondly, when the default data managers are used, it // allows, for example, to have 2 StandardStMan storage managers. // One for one group of columns and one for another group of columns. //
        • Options. These are defined in ColumnDesc.h and can be combined // by or-ing them. // Currently only the Undefined flag applies to scalars. //
        • Default keyword set, which defaults to an empty set. // When a table column gets created from the description, it gets // a copy of this keyword set as its initial keyword set. //
        // // There are several constructors, which allow to define most // of the above mentioned attributes. Others, like the default keyword // set, have to be defined explicitly. //

        // This class is derived from BaseColumnDesc, thus the functions // in there also apply to this class. //
        // Once a column description is setup satisfactorily, it must be added // to a table description before it can be used by the table system. // // // // TableDesc tabDesc("tTableDesc", "1", TableDesc::New); // // // Add a scalar integer column ac, define keywords for it // // and define a default value 0. // ScalarColumnDesc acColumn("ac"); // acColumn.rwKeywordSet().define ("scale", Complex(0)); // acColumn.rwKeywordSet().define ("unit", ""); // acColumn.setDefault (0); // tabDesc.addColumn (acColumn); // // // Add another column, now with data type String.. // // This can be added directly, because no special things like // // keywords or default values have to be set. // tabDesc.addColumn (ScalarColumnDesc("name", "comments")); // // // // Several column description classes are needed to allow the user // to define attributes which are special for each column type. // For scalars the special attribute is the default value. // They all have to be templated to support arbitrary data types. // // //

      • Default constructor //
      • Copy constructor //
      • Assignment operator //
      • static String dataTypeId(); // (not needed for builtin types) // This should return the unique "name" of the class. // // //# A List of bugs, limitations, extensions or planned refinements. // template class ScalarColumnDesc : public BaseColumnDesc { friend class ColumnDesc; public: // Construct the column with the given name. // The data manager type defaults to the StandardStMan storage manager. // The data manager group defaults to the data manager type. // The possible options are defined in ColumnDesc.h. explicit ScalarColumnDesc (const String& name, int options = 0); // Construct the column with the given name and comment. // The data manager type defaults to the StandardStMan storage manager. // The data manager group defaults to the data manager type. // The possible options are defined in ColumnDesc.h. ScalarColumnDesc (const String& name, const String& comment, int options = 0); // Construct the column with the given name, comment, and // default data manager type and group. // A blank data manager group defaults to the data manager type. // The possible options are defined in ColumnDesc.h. ScalarColumnDesc (const String& name, const String& comment, const String& dataManName, const String& dataManGroup, int options = 0); // Construct the column with the given name, comment, default // data manager type and group, and default value. // A blank data manager group defaults to the data manager type. // The possible options are defined in ColumnDesc.h. ScalarColumnDesc (const String& name, const String& comment, const String& dataManName, const String& dataManGroup, const T& defaultValue, int options = 0); // Copy constructor (copy semantics); ScalarColumnDesc (const ScalarColumnDesc&); ~ScalarColumnDesc(); // Assignment (copy semantics); ScalarColumnDesc& operator= (const ScalarColumnDesc&); // Clone this column description. BaseColumnDesc* clone() const; // Get the name of this class. It is used by the registration process. // The template argument gets part of the name. String className() const; // Set the default value. void setDefault (const T& defaultValue) { defaultVal_p = defaultValue; } // Get the default value. const T& defaultValue() const { return defaultVal_p; } // Create a Column object out of this. // This is used by class ColumnSet to construct a table column object. virtual PlainColumn* makeColumn (ColumnSet*) const; // Make a ConcatColumn object out of the description. virtual ConcatColumn* makeConcatColumn (ConcatTable*) const; // Show the column. void show (ostream& os) const; // Register the construction function of this class. void registerClass() const; // Create the object from AipsIO (this function is registered). static BaseColumnDesc* makeDesc (const String& name); private: T defaultVal_p; //# default value // Put the object. virtual void putDesc (AipsIO&) const; // Get the object. virtual void getDesc (AipsIO&); }; //# Explicitly instantiate these templates in ScaColDesc_tmpl.cc extern template class ScalarColumnDesc; extern template class ScalarColumnDesc; extern template class ScalarColumnDesc; extern template class ScalarColumnDesc; extern template class ScalarColumnDesc; extern template class ScalarColumnDesc; extern template class ScalarColumnDesc; extern template class ScalarColumnDesc; extern template class ScalarColumnDesc; extern template class ScalarColumnDesc; extern template class ScalarColumnDesc; extern template class ScalarColumnDesc; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/tables/Tables/ScaColDesc.tcc000066400000000000000000000130471476623553700204530ustar00rootroot00000000000000//# ScaColDesc.cc: Templated class for description of table scalar columns //# Copyright (C) 1994,1995,1996,1997,1998,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_SCACOLDESC_TCC #define TABLES_SCACOLDESC_TCC #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template ScalarColumnDesc::ScalarColumnDesc (const String& name, int opt) : BaseColumnDesc (name, "", "", "", ValType::getType(&defaultVal_p), valDataTypeId(&defaultVal_p), opt, 0, IPosition(), True, False, False) { defaultVal_p = T(); } template ScalarColumnDesc::ScalarColumnDesc (const String& name, const String& comment, int opt) : BaseColumnDesc (name, comment, "", "", ValType::getType(&defaultVal_p), valDataTypeId(&defaultVal_p), opt, 0, IPosition(), True, False, False) { defaultVal_p = T(); } template ScalarColumnDesc::ScalarColumnDesc (const String& name, const String& comment, const String& dataManName, const String& dataManGroup, int opt) : BaseColumnDesc (name, comment, dataManName, dataManGroup, ValType::getType(&defaultVal_p), valDataTypeId(&defaultVal_p), opt, 0, IPosition(), True, False, False) { defaultVal_p = T(); } template ScalarColumnDesc::ScalarColumnDesc (const String& name, const String& comment, const String& dataManName, const String& dataManGroup, const T& defaultVal, int opt) : BaseColumnDesc (name, comment, dataManName, dataManGroup, ValType::getType(&defaultVal_p), valDataTypeId(&defaultVal_p), opt, 0, IPosition(), True, False, False), defaultVal_p (defaultVal) {} template ScalarColumnDesc::ScalarColumnDesc (const ScalarColumnDesc& that) : BaseColumnDesc (that), defaultVal_p (that.defaultVal_p) {} // Make a new object. template BaseColumnDesc* ScalarColumnDesc::makeDesc (const String&) { return new ScalarColumnDesc(String()); } template ScalarColumnDesc::~ScalarColumnDesc() {} template ScalarColumnDesc& ScalarColumnDesc::operator= ( const ScalarColumnDesc& that) { BaseColumnDesc::operator= (that); defaultVal_p = that.defaultVal_p; return *this; } // Clone this column description to another. template BaseColumnDesc* ScalarColumnDesc::clone() const { BaseColumnDesc* ptr = new ScalarColumnDesc(*this); return ptr; } // Return the class name. template String ScalarColumnDesc::className() const { return "ScalarColumnDesc<" + dataTypeId(); } //# Register the makeDesc function. template void ScalarColumnDesc::registerClass() const { ColumnDesc::registerCtor (className(), makeDesc); } // Put the object. // The data is read by the ctor taking AipsIO. // It was felt that putstart takes too much space, so therefore // the version is put "manually". template void ScalarColumnDesc::putDesc (AipsIO& ios) const { ios << (uInt)1; // class version 1 ValType::put (ios, &defaultVal_p); } template void ScalarColumnDesc::getDesc (AipsIO& ios) { uInt version; ios >> version; ValType::get (ios, &defaultVal_p); } // Show the column. template void ScalarColumnDesc::show (ostream& os) const { os << " Name=" << name(); os << " DataType=" << dataType(); if (dataType() == TpOther) { os << ", " << dataTypeId(); } if (maxLength() > 0) { os << " MaxLength=" << maxLength(); } os << endl; os << " DataManager=" << dataManagerType(); os << "/" << dataManagerGroup(); os << " Default="; ValType::put (os, &defaultVal_p); os << endl; os << " Comment = " << comment() << endl; } // Create a column object from the description. template PlainColumn* ScalarColumnDesc::makeColumn (ColumnSet* csp) const { PlainColumn* bcp = new ScalarColumnData (this, csp); return bcp; } template ConcatColumn* ScalarColumnDesc::makeConcatColumn (ConcatTable* ct) const { return new ConcatScalarColumn (this, ct); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/ScaColDesc_tmpl.cc000066400000000000000000000035411476623553700213210ustar00rootroot00000000000000//# ScaColDesc_tmpl.cc: Explicit ScalarColumnDesc template instantiations //# Copyright (C) 2015 //# Associated Universities, Inc. Washington DC, USA, //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include //# Instantiate extern templates for often used types. namespace casacore { template class ScalarColumnDesc; template class ScalarColumnDesc; template class ScalarColumnDesc; template class ScalarColumnDesc; template class ScalarColumnDesc; template class ScalarColumnDesc; template class ScalarColumnDesc; template class ScalarColumnDesc; template class ScalarColumnDesc; template class ScalarColumnDesc; template class ScalarColumnDesc; template class ScalarColumnDesc; } casacore-3.7.1/tables/Tables/ScaRecordColData.cc000066400000000000000000000167141476623553700214250ustar00rootroot00000000000000//# ScaRecordColData.cc: Access to a table column containing scalar records //# Copyright (C) 1998,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ScalarRecordColumnData::ScalarRecordColumnData (const ScalarRecordColumnDesc* cd, ColumnSet* csp) : PlainColumn (cd, csp) {} ScalarRecordColumnData::~ScalarRecordColumnData() {} void ScalarRecordColumnData::createDataManagerColumn() { // The records are stored as an array. // Because their lengths can vary, it has to be indirect. dataColPtr_p = dataManPtr_p->createIndArrColumn (colDescPtr_p->name(), TpUChar, ""); } void ScalarRecordColumnData::initialize (rownr_t, rownr_t) {} Bool ScalarRecordColumnData::isDefined (rownr_t) const { return True; } void ScalarRecordColumnData::get (rownr_t rownr, void* val) const { checkReadLock (True); getRecord (rownr, *(TableRecord*)val); autoReleaseLock(); } void ScalarRecordColumnData::getScalarColumn (ArrayBase& val) const { Vector& vec = static_cast&>(val); rownr_t nr = nrow(); if (vec.nelements() != nr) { throw (TableArrayConformanceError ("ScalarRecordColumnData::getScalarColumn")); } checkReadLock (True); for (rownr_t i=0; i& vec = static_cast&>(val); if (vec.nelements() != rownrs.nrow()) { throw (TableArrayConformanceError ("ScalarRecordColumnData::getColumnCells")); } checkReadLock (True); RefRowsSliceIter iter(rownrs); rownr_t i=0; while (! iter.pastEnd()) { rownr_t rownr = iter.sliceStart(); rownr_t end = iter.sliceEnd(); rownr_t incr = iter.sliceIncr(); while (rownr <= end) { getRecord (rownr, vec(i++)); rownr += incr; } iter++; } autoReleaseLock(); } void ScalarRecordColumnData::put (rownr_t rownr, const void* val) { checkWriteLock (True); putRecord (rownr, *(const TableRecord*)val); autoReleaseLock(); } void ScalarRecordColumnData::putScalarColumn (const ArrayBase& val) { const Vector& vec = static_cast&>(val); rownr_t nr = nrow(); if (vec.nelements() != nr) { throw (TableArrayConformanceError ("ScalarRecordColumnData::putScalarColumn")); } checkWriteLock (True); for (rownr_t i=0; i& vec = static_cast&>(val); if (vec.nelements() != rownrs.nrow()) { throw (TableArrayConformanceError ("ScalarRecordColumnData::putColumnCells")); } checkWriteLock (True); RefRowsSliceIter iter(rownrs); rownr_t i=0; while (! iter.pastEnd()) { rownr_t rownr = iter.sliceStart(); rownr_t end = iter.sliceEnd(); rownr_t incr = iter.sliceIncr(); while (rownr <= end) { putRecord (rownr, vec(i++)); rownr += incr; } iter++; } autoReleaseLock(); } void ScalarRecordColumnData::getRecord (rownr_t rownr, TableRecord& rec) const { if (! dataColPtr_p->isShapeDefined (rownr)) { rec = TableRecord(); } else { IPosition shape = dataColPtr_p->shape (rownr); AlwaysAssert (shape.nelements() == 1, AipsError); Array data(shape); dataColPtr_p->getArrayV (rownr, data); Bool deleteIt; const uChar* buf = data.getStorage (deleteIt); auto memio = std::make_shared(buf, shape(0)); AipsIO aio(memio); rec.getRecord (aio, TableAttr(dataManager()->table())); data.freeStorage (buf, deleteIt); } } void ScalarRecordColumnData::putRecord (rownr_t rownr, const TableRecord& rec) { auto memio = std::make_shared(); AipsIO aio(memio); rec.putRecord (aio, TableAttr(dataManager()->table().tableName())); IPosition shape (1, Int(memio->length())); Vector data(shape, (uChar*)(memio->getBuffer()), SHARE); dataColPtr_p->setShape (rownr, shape); dataColPtr_p->putArrayV (rownr, data); } void ScalarRecordColumnData::makeSortKey (Sort&, std::shared_ptr&, Int, std::shared_ptr&) { throw (TableError ("Sorting on a column containing records " "is not possible")); } void ScalarRecordColumnData::makeRefSortKey (Sort&, std::shared_ptr&, Int, const Vector&, std::shared_ptr&) { throw (TableError ("Sorting on a column containing records " "is not possible")); } void ScalarRecordColumnData::allocIterBuf (void*&, void*&, std::shared_ptr&) { throw (TableError ("Iterating on a column containing records " "is not possible")); } void ScalarRecordColumnData::freeIterBuf (void*& lastVal, void*& curVal) { lastVal = 0; curVal = 0; } //# It was felt that putstart takes too much space, so therefore //# the version is put "manually". void ScalarRecordColumnData::putFileDerived (AipsIO& ios) { ios << (uInt)1; // class version 1 ios << dataManPtr_p->sequenceNr(); } void ScalarRecordColumnData::getFileDerived (AipsIO& ios, const ColumnSet& colset) { uInt version; ios >> version; uInt seqnr; ios >> seqnr; dataManPtr_p = colset.getDataManager (seqnr); createDataManagerColumn(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/ScaRecordColData.h000066400000000000000000000166231476623553700212660ustar00rootroot00000000000000//# ScaRecordColData.h: Access to a table column containing scalar records //# Copyright (C) 1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_SCARECORDCOLDATA_H #define TABLES_SCARECORDCOLDATA_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class ColumnSet; class ScalarRecordColumnDesc; class AipsIO; // // Access to a table column containing scalar records. // // // // // //# Classes you should understand before using this one. //
      • PlainColumn //
      • ScalarRecordColumnDesc //
      • Table // // // ScalarRecordColumnData represents a table column containing scalars. // // // The class ScalarRecordColumnData is derived from PlainColumn. // It implements the virtual functions accessing a table column // containing scalars holding records. //
        // It is possible to access an scalar in an individual cell (i.e. table row) // or in the entire column. //

        // The main task of this class is to communicate with the data manager // column object. This consists of: //

          //
        • Binding itself to a data manager. //
        • Letting the data manager create its column object. //
        • Closing the data manager column object (in putFileDerived). //
        • Reconstructing the data manager object for an existing table // (in getFileDerived). //
        • Transferring get/put calls to the data manager column object. //
        // // The class is hidden from the user by the envelope class ScalarColumn. // If used directly by other Table classes, it should be done with care. // It assumes that the arrays in the various get and put functions have // the correct length. ScalarColumn does that check. //
        // //# A List of bugs, limitations, extensions or planned refinements. //
      • Introduce a class ArrayRecordColumnData to support arrays of records. // class ScalarRecordColumnData : public PlainColumn { public: // Construct a scalar column object from the given description // in the given column set. // This constructor is used by ScalarRecordColumnDesc::makeColumn. ScalarRecordColumnData (const ScalarRecordColumnDesc*, ColumnSet*); ~ScalarRecordColumnData(); // Copy constructor cannot be used. ScalarRecordColumnData (const ScalarRecordColumnData&) = delete; // Assignment cannot be used. ScalarRecordColumnData& operator= (const ScalarRecordColumnData&) = delete; // Initialize the rows from startRownr till endRownr (inclusive) // with the default value defined in the column description. virtual void initialize (rownr_t startRownr, rownr_t endRownr); // Test if the given cell contains a defined value. virtual Bool isDefined (rownr_t rownr) const; // Get the value from a particular cell. virtual void get (rownr_t rownr, void*) const; // Get the array of all values in the column. // The length of the buffer pointed to by dataPtr must match // the actual length. This is checked by ScalarColumn. virtual void getScalarColumn (ArrayBase& dataPtr) const; // Get the array of some values in the column (on behalf of RefColumn). // The length of the buffer pointed to by dataPtr must match // the actual length. This is checked by ScalarColumn. virtual void getScalarColumnCells (const RefRows& rownrs, ArrayBase& dataPtr) const; // Put the value in a particular cell. // The length of the buffer pointed to by dataPtr must match // the actual length. This is checked by ScalarColumn. virtual void put (rownr_t rownr, const void* dataPtr); // Put the array of all values in the column. // The length of the buffer pointed to by dataPtr must match // the actual length. This is checked by ScalarColumn. virtual void putScalarColumn (const ArrayBase& dataPtr); // Put the array of some values in the column (on behalf on RefColumn). // The length of the buffer pointed to by dataPtr must match // the actual length. This is checked by ScalarColumn. virtual void putScalarColumnCells (const RefRows& rownrs, const ArrayBase& dataPtr); // Add this column and its data to the Sort object. // Sorting on records is not supported, so an exception is thrown. // virtual void makeSortKey (Sort&, std::shared_ptr& cmpObj, Int order, std::shared_ptr& dataSave); // Do it only for the given row numbers. virtual void makeRefSortKey (Sort&, std::shared_ptr& cmpObj, Int order, const Vector& rownrs, std::shared_ptr& dataSave); // // Allocate value buffers for the table iterator. // Iteration based on records is not supported, so an exception is thrown. virtual void allocIterBuf (void*& lastVal, void*& curVal, std::shared_ptr& cmpObj); // Free the value buffers allocated by allocIterBuf. virtual void freeIterBuf (void*& lastVal, void*& curVal); // Create a data manager column object for this column. virtual void createDataManagerColumn(); private: // Write the column data. // The control information is written into the given AipsIO object, // while the data is written/flushed by the data manager. virtual void putFileDerived (AipsIO&); // Read the column data back. // The control information is read from the given AipsIO object. // This is used to bind the column to the appropriate data manager. // Thereafter the data manager gets opened. virtual void getFileDerived (AipsIO&, const ColumnSet&); // Handle getting and putting a record. // It is stored as a Vector of uChar. // void getRecord (rownr_t rownr, TableRecord& rec) const; void putRecord (rownr_t rownr, const TableRecord& rec); // }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/ScaRecordColDesc.cc000066400000000000000000000075471476623553700214360ustar00rootroot00000000000000//# ScaRecordColDesc.cc: Class for description of table scalar record columns //# Copyright (C) 1998,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ScalarRecordColumnDesc::ScalarRecordColumnDesc (const String& name) : BaseColumnDesc (name, "", "", "", TpRecord, "TableRecord", 0, 0, IPosition(), True, False, False) {} ScalarRecordColumnDesc::ScalarRecordColumnDesc (const String& name, const String& comment) : BaseColumnDesc (name, comment, "", "", TpRecord, "TableRecord", 0, 0, IPosition(), True, False, False) {} ScalarRecordColumnDesc::ScalarRecordColumnDesc (const String& name, const String& comment, const String& dataManName, const String& dataManGroup) : BaseColumnDesc (name, comment, dataManName, dataManGroup, TpRecord, "TableRecord", 0, 0, IPosition(), True, False, False) {} ScalarRecordColumnDesc::ScalarRecordColumnDesc (const ScalarRecordColumnDesc& that) : BaseColumnDesc (that) {} // Make a new object. BaseColumnDesc* ScalarRecordColumnDesc::makeDesc (const String&) { return new ScalarRecordColumnDesc(""); } ScalarRecordColumnDesc::~ScalarRecordColumnDesc() {} ScalarRecordColumnDesc& ScalarRecordColumnDesc::operator= (const ScalarRecordColumnDesc& that) { BaseColumnDesc::operator= (that); return *this; } // Clone this column description to another. BaseColumnDesc* ScalarRecordColumnDesc::clone() const { return new ScalarRecordColumnDesc(*this); } // Return the class name. String ScalarRecordColumnDesc::className() const { return "ScalarRecordColumnDesc"; } // Put the object. // The data is read by the ctor taking AipsIO. // It was felt that putstart takes too much space, so therefore // the version is put "manually". void ScalarRecordColumnDesc::putDesc (AipsIO& ios) const { ios << (uInt)1; // class version 1 } void ScalarRecordColumnDesc::getDesc (AipsIO& ios) { uInt version; ios >> version; } // Show the column. void ScalarRecordColumnDesc::show (ostream& os) const { os << " Name=" << name(); os << " DataType=" << dataType(); os << endl; os << " DataManager=" << dataManagerType(); os << "/" << dataManagerGroup(); os << endl; os << " Comment = " << comment() << endl; } // Create a column object from the description. PlainColumn* ScalarRecordColumnDesc::makeColumn (ColumnSet* csp) const { return new ScalarRecordColumnData (this, csp); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/ScaRecordColDesc.h000066400000000000000000000141611476623553700212660ustar00rootroot00000000000000//# ScaRecordColDesc.h: Class for description of table scalar record columns //# Copyright (C) 1998,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_SCARECORDCOLDESC_H #define TABLES_SCARECORDCOLDESC_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class PlainColumn; class ColumnSet; // // Class to define columns of scalar records in tables // // // // // //
      • ColumnDesc //
      • TableDesc //
      • TableRecord // // // This class builds descriptions of table columns where each cell (which // may also be called a row) will hold a scalar record value. // // // ScalarRecordColumnDesc is the class for defining a // table column containing scalar record values. The only record class // supported is TableRecord. //
        // This class is similar to the templated class // ScalarColumnDesc used // to define column descriptions for scalars with a standard data type. //

        // The data managers handle a record as an indirect Vector of uChar, // because class // ScalarRecordColumnData // converts a record to such a vector before passing it to the data manager. //

        // This class is derived from // BaseColumnDesc, thus the functions // in there also apply to this class. //
        // Once a column description is setup satisfactorily, it must be added // to a table description before it can be used by the table system. // // // // TableDesc tabDesc("tTableDesc", "1", TableDesc::New); // // // Add a scalar integer column ac, define keywords for it // // and define a default value 0. // ScalarRecordColumnDesc acColumn("ac"); // acColumn.rwKeywordSet().define ("scale", Complex(0)); // acColumn.rwKeywordSet().define ("unit", ""); // acColumn.setDefault (0); // tabDesc.addColumn (acColumn); // // // Add another column, now with data type String.. // // This can be added directly, because no special things like // // keywords or default values have to be set. // tabDesc.addColumn (ScalarRecordColumnDesc("name", "comments")); // // // // This class resembles the templated class // ScalarColumnDesc // a lot, but is different enough to make that templated class not usable // for records. //
        In principle it could have been a template specialization, // but not all compilers support specializations so well. //
        // //# A List of bugs, limitations, extensions or planned refinements. //

      • Introduce a class ArrayRecordColumnDesc to support arrays of records. // class ScalarRecordColumnDesc : public BaseColumnDesc { friend class ColumnDesc; public: // Construct the column with the given name. // The data manager type defaults to the StandardStMan storage manager. // The data manager group defaults to the data manager type. explicit ScalarRecordColumnDesc (const String& name); // Construct the column with the given name and comment. // The data manager type defaults to the StandardStMan storage manager. // The data manager group defaults to the data manager type. ScalarRecordColumnDesc (const String& name, const String& comment); // Construct the column with the given name, comment, and // default data manager type and group. // A blank data manager group defaults to the data manager type. ScalarRecordColumnDesc (const String& name, const String& comment, const String& dataManName, const String& dataManGroup); // Copy constructor (copy semantics); ScalarRecordColumnDesc (const ScalarRecordColumnDesc&); ~ScalarRecordColumnDesc(); // Assignment (copy semantics); ScalarRecordColumnDesc& operator= (const ScalarRecordColumnDesc&); // Clone this column description. virtual BaseColumnDesc* clone() const; // Get the name of this class. It is used by the registration process. virtual String className() const; // Create a Column object out of this. // This is used by class ColumnSet to construct a table column object. virtual PlainColumn* makeColumn (ColumnSet*) const; // Show the column. virtual void show (ostream& os) const; // Create the object from AipsIO (this function is registered // by ColumnDesc.cc). static BaseColumnDesc* makeDesc (const String& name); private: // Put the object. virtual void putDesc (AipsIO&) const; // Get the object. virtual void getDesc (AipsIO&); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/ScalarColumn.h000066400000000000000000000246671476623553700205630ustar00rootroot00000000000000//# SclarColumn.h: access to a scalar table column with arbitrary data type //# Copyright (C) 1994,1995,1996,1997,1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_SCALARCOLUMN_H #define TABLES_SCALARCOLUMN_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class BaseColumn; class RefRows; class String; // // Access to a scalar table column with arbitrary data type // // // // // //
      • Table //
      • TableColumn // // // ScalarColumn gives read and write access to a column in a table // containing a scalar with data type T. // // // The class ScalarColumn allows read and write access to a column // containing scalar values with an arbitrary data type. // It is possible to get the data in an individual cell (i.e. table row) // and to get the column as a whole. // // A default constructor is defined to allow construction of an array // of ScalarColumn objects. However, this constructs an object not // referencing a column. Functions like get, etc. will fail (i.e. result // in a segmentation fault) when used on such objects. The functions // isNull and throwIfNull can be used to test on this. // The functions attach and reference can fill in the object. // // // See module Tables. // template class ScalarColumn : public TableColumn { public: // The default constructor creates a null object, i.e. it // does not reference a table column. // The sole purpose of this constructor is to allow construction // of an array of ScalarColumn objects. // The functions reference and attach can be used to make a null object // reference a column. // Note that get functions, etc. will cause a segmentation fault // when operating on a null object. It was felt it was too expensive // to test on null over and over again. The user should use the isNull // or throwIfNull function in case of doubt. ScalarColumn(); // Construct for the given column in the given table. ScalarColumn (const Table&, const String& columnName); // Construct from the given table column. // This constructor is useful if first a table column was constructed, // its type is determined and thereafter used to construct the // correct column object. explicit ScalarColumn (const TableColumn&); // Copy constructor (reference semantics). ScalarColumn (const ScalarColumn&); ~ScalarColumn(); // Clone the object. virtual TableColumn* clone() const; // Assignment uses reference semantics, thus works the same // as function reference. ScalarColumn& operator= (const ScalarColumn&); // Change the reference to another column. // This is in fact an assignment operator with reference semantics. // It removes the reference to the current column and creates // a reference to the column referenced in the other object. // It will handle null objects correctly. void reference (const ScalarColumn&); // Attach a column to the object. // This is in fact only a shorthand for //
        reference (ScalarColumn (table, columnName)); void attach (const Table& table, const String& columnName) { reference (ScalarColumn (table, columnName)); } // Get the data from a particular cell (i.e. table row). // The row numbers count from 0 until #rows-1. // void get (rownr_t rownr, T& value) const { TABLECOLUMNCHECKROW(rownr); Int off = colCachePtr_p->offset(rownr); if (off >= 0) { value = ((T*)(colCachePtr_p->dataPtr()))[off]; }else{ baseColPtr_p->get (rownr, &value); } } T get (rownr_t rownr) const { T value; get (rownr, value); return value; } T operator() (rownr_t rownr) const { T value; get (rownr, value); return value; } // // Get the vector of all values in the column. // According to the assignment rules of class Array, the destination // vector must be empty or its length must be the number of cells // in the column (i.e. the number of rows in the table). void getColumn (Vector& vec, Bool resize = False) const; // Get the vector of all values in the column. Vector getColumn() const; // Get the vector of a range of values in the column. // The Slicer object can be used to specify start, end (or length), // and stride of the rows to get. // According to the assignment rules of class Array, the destination // vector must be empty or its length must be the number of cells // in the column (i.e. the number of rows in the slicer). void getColumnRange (const Slicer& rowRange, Vector& vec, Bool resize = False) const; // Get the vector of a range of values in the column. // The Slicer object can be used to specify start, end (or length), // and stride of the rows to get.. Vector getColumnRange (const Slicer& rowRange) const; // Get the vector of some values in the column. // The Slicer object can be used to specify start, end (or length), // and stride of the rows to get. // According to the assignment rules of class Array, the destination // vector must be empty or its length must be the number of cells // in the column (i.e. the number of rows in the RefRows object). void getColumnCells (const RefRows& rownrs, Vector& vec, Bool resize = False) const; // Get the vector of some values in the column. Vector getColumnCells (const RefRows& rownrs) const; // Put the value in a particular cell (i.e. table row). // The row numbers count from 0 until #rows-1. void put (rownr_t rownr, const T& value) { TABLECOLUMNCHECKROW(rownr); checkWritable(); baseColPtr_p->put (rownr, &value); } // Copy the value of a cell of that column to a cell of this column. // The data types of both columns must be the same. // // Use the same row numbers for both cells. void put (rownr_t rownr, const ScalarColumn& that) { put (rownr, that, rownr); } // Use possibly different row numbers for that (i.e. input) and // and this (i.e. output) cell. void put (rownr_t thisRownr, const ScalarColumn& that, rownr_t thatRownr); // // Copy the value of a cell of that column to a cell of this column. // This function uses a generic TableColumn object as input. // If possible the data will be promoted to the data type of this column. // Otherwise an exception is thrown. // // Use the same row numbers for both cells. void put (rownr_t rownr, const TableColumn& that, Bool=False) { put (rownr, that, rownr); } // Use possibly different row numbers for that (i.e. input) and // and this (i.e. output) cell. void put (rownr_t thisRownr, const TableColumn& that, rownr_t thatRownr, Bool=False); // // Put the vector of all values in the column. // The length of the vector must be the number of cells in the column // (i.e. the number of rows in the table). void putColumn (const Vector& vec); // Put the vector of a range of values in the column. // The Slicer object can be used to specify start, end (or length), // and stride of the rows to put. // The length of the vector must be the number of cells in the slice. void putColumnRange (const Slicer& rowRange, const Vector& vec); // Put the vector of some values in the column. // The length of the vector must be the number of cells in the RefRows // object. void putColumnCells (const RefRows& rownrs, const Vector& vec); // Put the same value in all cells of the column. void fillColumn (const T& value); // Put the contents of a column with the same data type into this column. // To put the contents of a column with a different data type into // this column, the function TableColumn::putColumn can be used // (provided the data type promotion is possible). // In fact, this function is an assignment operator with copy semantics. void putColumn (const ScalarColumn& that); private: // Check if the data type matches the column data type. void checkDataType() const; }; //# Explicitly instantiate these templates in ScalarColumn_tmpl.cc extern template class ScalarColumn; extern template class ScalarColumn; extern template class ScalarColumn; extern template class ScalarColumn; extern template class ScalarColumn; extern template class ScalarColumn; extern template class ScalarColumn; extern template class ScalarColumn; extern template class ScalarColumn; extern template class ScalarColumn; extern template class ScalarColumn; extern template class ScalarColumn; } //# NAMESPACE CASACORE - END //# Make old name ROScalarColumn still available. #define ROScalarColumn ScalarColumn #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/tables/Tables/ScalarColumn.tcc000066400000000000000000000165131476623553700210740ustar00rootroot00000000000000//# ScalarColumn.cc: Access to a scalar table column with arbitrary data type //# Copyright (C) 1994,1995,1996,1997,1998,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_SCALARCOLUMN_TCC #define TABLES_SCALARCOLUMN_TCC #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template ScalarColumn::ScalarColumn() : TableColumn() {} template ScalarColumn::ScalarColumn (const Table& tab, const String& columnName) : TableColumn (tab, columnName) { checkDataType(); } template ScalarColumn::ScalarColumn (const TableColumn& column) : TableColumn (column) { checkDataType(); } template ScalarColumn::ScalarColumn (const ScalarColumn& that) : TableColumn (that) {} template TableColumn* ScalarColumn::clone() const { return new ScalarColumn (*this); } template ScalarColumn& ScalarColumn::operator= (const ScalarColumn& that) { reference (that); return (*this); } template void ScalarColumn::reference (const ScalarColumn& that) { if (this != &that) { TableColumn::reference (that); } } template ScalarColumn::~ScalarColumn() {} template void ScalarColumn::checkDataType() const { //# Check if the data type matches. const ColumnDesc& cd = baseColPtr_p->columnDesc(); DataType dtype = cd.dataType(); if (dtype != ValType::getType(static_cast(0)) || !cd.isScalar()) { throw (TableInvDT (" in ScalarColumn ctor for column " + cd.name())); } if (dtype == TpOther) { if (cd.dataTypeId() != valDataTypeId(static_cast(0))) { throw (TableInvDT (" in ScalarColumn ctor for column " + cd.name() + "; using data type id " + valDataTypeId(static_cast(0)) + ", expected " + cd.dataTypeId())); } } } template Vector ScalarColumn::getColumn() const { Vector vec; getColumn (vec); return vec; } template void ScalarColumn::getColumn (Vector& vec, Bool resize) const { rownr_t nrrow = nrow(); //# Resize the vector if empty; otherwise check its length. if (vec.nelements() != nrrow) { if (resize || vec.nelements() == 0) { vec.resize (nrrow); }else{ throw (TableConformanceError("ScalarColumn::getColumn")); } } // Get the column. baseColPtr_p->getScalarColumn (vec); } template Vector ScalarColumn::getColumnRange (const Slicer& rowRange) const { Vector vec; getColumnRange (rowRange, vec); return vec; } template void ScalarColumn::getColumnRange (const Slicer& rowRange, Vector& vec, Bool resize) const { rownr_t nrrow = nrow(); IPosition shp, blc, trc, inc; shp = rowRange.inferShapeFromSource (IPosition(1,nrrow), blc, trc, inc); //# When the entire column is accessed, use that function. if (blc(0) == 0 && shp(0) == Int(nrrow) && inc(0) == 1) { getColumn (vec, resize); } else { getColumnCells (RefRows(blc(0), trc(0), inc(0)), vec, resize); } } template Vector ScalarColumn::getColumnCells (const RefRows& rownrs) const { Vector vec; getColumnCells (rownrs, vec); return vec; } template void ScalarColumn::getColumnCells (const RefRows& rownrs, Vector& vec, Bool resize) const { //# Resize the vector if needed; otherwise check its length. rownr_t nrrow = rownrs.nrow(); if (vec.nelements() != nrrow) { if (resize || vec.nelements() == 0) { vec.resize (nrrow); }else{ throw (TableConformanceError("ScalarColumn::getColumnCells")); } } baseColPtr_p->getScalarColumnCells (rownrs, vec); } template void ScalarColumn::put (rownr_t thisRownr, const ScalarColumn& that, rownr_t thatRownr) { put (thisRownr, that(thatRownr)); } template void ScalarColumn::put (rownr_t thisRownr, const TableColumn& that, rownr_t thatRownr, Bool) { T value; that.getScalarValue (thatRownr, &value, columnDesc().dataTypeId()); put (thisRownr, value); } template void ScalarColumn::putColumn (const Vector& vec) { checkWritable(); rownr_t nrrow = nrow(); //# Check the vector length. if (vec.nelements() != nrrow) { throw (TableConformanceError("ScalarColumn::putColumn(Vector&)")); } // Put the column. baseColPtr_p->putScalarColumn (vec); } template void ScalarColumn::putColumnRange (const Slicer& rowRange, const Vector& vec) { rownr_t nrrow = nrow(); IPosition shp, blc, trc, inc; shp = rowRange.inferShapeFromSource (IPosition(1,nrrow), blc, trc, inc); //# When the entire column is accessed, use that function. if (blc(0) == 0 && shp(0) == Int(nrrow) && inc(0) == 1) { putColumn (vec); } else { putColumnCells (RefRows(blc(0), trc(0), inc(0)), vec); } } template void ScalarColumn::putColumnCells (const RefRows& rownrs, const Vector& vec) { checkWritable(); //# Check the vector length. rownr_t nrrow = rownrs.nrow(); if (vec.nelements() != nrrow) { throw (TableConformanceError("ScalarColumn::putColumnCells")); } baseColPtr_p->putScalarColumnCells (rownrs, vec); } //#// This is a very simple implementation. //#// Ultimately this must be done more directly via the data manager. template void ScalarColumn::fillColumn (const T& value) { rownr_t nrrow = nrow(); for (rownr_t i=0; i void ScalarColumn::putColumn (const ScalarColumn& that) { //# Check the column lengths. rownr_t nrrow = nrow(); if (nrrow != that.nrow()) { throw (TableConformanceError ("ScalarColumn::putColumn")); } for (rownr_t i=0; i //# Instantiate extern templates for often used types. namespace casacore { template class ScalarColumn; template class ScalarColumn; template class ScalarColumn; template class ScalarColumn; template class ScalarColumn; template class ScalarColumn; template class ScalarColumn; template class ScalarColumn; template class ScalarColumn; template class ScalarColumn; template class ScalarColumn; template class ScalarColumn; } casacore-3.7.1/tables/Tables/SetupNewTab.cc000066400000000000000000000253651476623553700205330ustar00rootroot00000000000000//# SetupNewTab.cc: Class to construct a new or scratch table //# Copyright (C) 1994,1995,1996,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN SetupNewTable::SetupNewTable (const String& tableName, const String& tableDescName, Table::TableOption opt, const StorageOption& storageOpt) { newTable_p = std::make_shared(tableName, tableDescName, opt, storageOpt); } SetupNewTable::SetupNewTable (const String& tableName, const TableDesc& tableDesc, Table::TableOption opt, const StorageOption& storageOpt) { newTable_p = std::make_shared(tableName, tableDesc, opt, storageOpt); } SetupNewTable::SetupNewTable (const SetupNewTable& that) { operator= (that); } SetupNewTable::~SetupNewTable() {} SetupNewTable& SetupNewTable::operator= (const SetupNewTable& that) { newTable_p = that.newTable_p; return *this; } SetupNewTableRep::SetupNewTableRep (const String& tableName, const String& tableDescName, Table::TableOption opt, const StorageOption& storageOpt) : tabName_p (tableName), option_p (opt), storageOpt_p(storageOpt), delete_p (False), tdescPtr_p (0), colSetPtr_p (0) { //# Copy the table description. tdescPtr_p = std::make_shared(tableDescName); //# Setup the new table. setup(); } SetupNewTableRep::SetupNewTableRep (const String& tableName, const TableDesc& tableDesc, Table::TableOption opt, const StorageOption& storageOpt) : tabName_p (tableName), option_p (opt), storageOpt_p(storageOpt), delete_p (False), tdescPtr_p (0), colSetPtr_p (0) { //# Read the table description. tdescPtr_p = std::make_shared(tableDesc, "", "", TableDesc::Scratch); //# Setup the new table. setup(); } SetupNewTableRep::~SetupNewTableRep() {} void SetupNewTableRep::setup() { //# If no name is given, create a unique name. if (tabName_p.empty()) { tabName_p = File::newUniqueName ("", "tab").originalName(); } //# A scratch table is new, but marked for delete. if (option_p == Table::Scratch) { option_p = Table::New; delete_p = True; } //# Check the table option. //# Check if the table exists and can be overwritten if new. if (option_p == Table::NewNoReplace) { File file(tabName_p); if (file.exists()) { throw (TableDuplFile(tabName_p)); // table file already exists } }else{ if (option_p != Table::New) { throw (TableInvOpt ("SetupNewTable", "must be Table::New, NewNoReplace or Scratch")); } } // Complete the storage option. storageOpt_p.fillOption(); //# Check if all subtable descriptions exist. tdescPtr_p->checkSubTableDesc(); //# Create a column set. colSetPtr_p = std::make_shared(tdescPtr_p.get(), storageOpt_p); } DataManager* SetupNewTableRep::getDataManager (const DataManager& dataMan) { //# Clone if this DataManager has not been cloned yet. //# The map maintains a mapping of an original DataManager object //# and its clone. //# However, it is possible that the original was a temporary and //# that another original is allocated at the same address. //# So also test if the original is indeed cloned. DataManager* dmp = 0; std::map::iterator iter = dataManMap_p.find((void*)&dataMan); if (iter != dataManMap_p.end()) { dmp = static_cast(iter->second); } if (dmp == 0 || dataMan.getClone() == 0) { //# Not cloned yet, so clone it. //# Add it to the map in the ColumnSet object. //# Tell the original object that it has been cloned. dmp = dataMan.clone(); colSetPtr_p->addDataManager (dmp); dataManMap_p[(void*)&dataMan] = dmp; dataMan.setClone (dmp); } return dmp; } void SetupNewTableRep::bindCreate (const Record& spec) { //# Test if object is already in use for a table. if (isUsed()) { throw (TableInvOper ("SetupNewTable::bindCreate, object already used by Table")); } for (uInt i=0; i cols (rec.asArrayString ("COLUMNS")); std::shared_ptr dataMan (DataManager::getCtor(dmType) (dmGroup, sp)); // Bind the columns to this data manager. for (uInt j=0; jncolumn(); i++) { PlainColumn* col = colSetPtr_p->getColumn(i); if (rebind || !col->isBound()) { //# Great, bind the data manager to the column. col->bind (dataManPtr); } } } void SetupNewTableRep::bindGroup (const String& groupName, const DataManager& dataMan, Bool rebind) { //# Test if object is already in use for a table. if (isUsed()) { throw (TableInvOper ("SetupNewTable::bindGroup, object already used by Table")); } //# Add DataManager object if not used yet. DataManager* dataManPtr = getDataManager (dataMan); //# Loop through all columns and bind those matching the group name. for (uInt i=0; incolumn(); i++) { PlainColumn* col = colSetPtr_p->getColumn(i); const ColumnDesc& cd = col->columnDesc(); if (cd.dataManagerGroup() == groupName) { if (rebind || !col->isBound()) { //# Great, bind the column to the data manager. col->bind (dataManPtr); } } } } void SetupNewTableRep::bindColumn (const String& columnName, const DataManager& dataMan) { //# Test if object is already in use for a table. if (isUsed()) { throw (TableInvOper ("SetupNewTable::bindColumn, object already used by Table")); } //# Add DataManager object if not used yet. DataManager* dataManPtr = getDataManager (dataMan); //# Bind data manager to the given column. //# Rebind if already bound. PlainColumn* col = colSetPtr_p->getColumn(columnName); col->bind (dataManPtr); } void SetupNewTableRep::bindColumn (const String& columnName, const String& otherColumn) { //# Test if object is already in use for a table. if (isUsed()) { throw (TableInvOper ("SetupNewTable::bindColumn, object already used by Table")); } //# Bind if other column has a data manager. //# Rebind if already bound. PlainColumn* col = colSetPtr_p->getColumn(columnName); PlainColumn* ocol = colSetPtr_p->getColumn(otherColumn); if (ocol->isBound()) { col->bind (ocol->dataManager()); } } void SetupNewTableRep::handleUnbound() { //# Loop through all columns and find unbound columns. for (uInt i=0; incolumn(); i++) { PlainColumn* col = colSetPtr_p->getColumn(i); const ColumnDesc& coldes = col->columnDesc(); if (!col->isBound()) { //# Create a data manager object for this column. //# Do this by executing the appropriate "constructor" //# in the static DataManager map. //# Clone and add DataManager object. String dmType = coldes.dataManagerType(); String dmGroup = coldes.dataManagerGroup(); DataManager* dataMan = DataManager::getCtor(dmType) (dmGroup, Record()); DataManager* dataManPtr = getDataManager (*dataMan); delete dataMan; //# Bind the column. col->bind (dataManPtr); //# Bind this data manager to all other unbound columns with //# the same group and default data manager type name. for (uInt j=i+1; jncolumn(); j++) { PlainColumn* cp = colSetPtr_p->getColumn(j); const ColumnDesc& cd = cp->columnDesc(); if (!cp->isBound() && cd.dataManagerGroup() == coldes.dataManagerGroup() && cd.dataManagerType() == coldes.dataManagerType()) { //# Great, bind the column to the data manager. cp->bind (dataManPtr); } } } } } void SetupNewTableRep::setShapeColumn (const String& columnName, const IPosition& shape) { //# Test if object is already in use for a table. if (isUsed()) { throw (TableInvOper ("SetupNewTable::setShapeColumn, object already used by Table")); } PlainColumn* col = colSetPtr_p->getColumn(columnName); if (! (col->columnDesc().isFixedShape())) { throw (TableInvOper ("SetupNewTable::setShapeColumn, column " + columnName + " is not fixed shape")); } col->setShapeColumn (shape); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/SetupNewTab.h000066400000000000000000000430671476623553700203740ustar00rootroot00000000000000//# SetupNewTab.h: Create a new table - define shapes, data managers, etc. //# Copyright (C) 1994,1995,1996,1999,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_SETUPNEWTAB_H #define TABLES_SETUPNEWTAB_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableDesc; class ColumnSet; class VirtualColumnEngine; class DataManager; class IPosition; // // Representation for handle class SetupNewTable // // // // // //
      • TableDesc and related classes like ArrayColumnDesc //
      • DataManager //
      • Table // // // SetupNewTableRep is the representation of class SetupNewTable. // // // SetupNewTableRep is the representation of class // SetupNewTable. // Its functionality is described there. // // // Copying a SetupNewTable object as such is very difficult, if not // impossible. However, being able to use a SetupNewTable copy constructor // was required to be able to have (static) functions constructing a // SetupNewTable object and return it by value (as done for example // by ForwardColumn::setupNewTable). // Therefore SetupNewTable is implemented using the handle idiom. // SetupNewTable is the interface (i.e. the handle) for the user, // while underneath SetupNewTableRep is doing all the work. // The SetupNewTable copy constructor can simply copy yhe pointer // to the underlying SetupNewTableRep object. // // //# A List of bugs, limitations, extensions or planned refinements. //
      • full implementation of tiling // class SetupNewTableRep { public: // Create a new table using the table description with the given name. // The description will be read from a file. SetupNewTableRep (const String& tableName, const String& tableDescName, Table::TableOption, const StorageOption&); // Create a new table using the given table description. SetupNewTableRep (const String& tableName, const TableDesc&, Table::TableOption, const StorageOption&); ~SetupNewTableRep(); // Copy constructor is forbidden, because copying a table requires // some more knowledge (like table name of result). SetupNewTableRep (const SetupNewTableRep&) = delete; // Assignment is forbidden, because copying a table requires // some more knowledge (like table name of result). SetupNewTableRep& operator= (const SetupNewTableRep&) = delete; // Get the name of the table. const String& name() const { return tabName_p; } // Get the table create option. int option() const { return option_p; } // Get the storage option. const StorageOption& storageOption() const { return storageOpt_p; } // Test if the table is marked for delete. Bool isMarkedForDelete() const { return delete_p; } // Get the table description. const TableDesc& tableDesc() const { return *tdescPtr_p; } // Bind a column to the given data manager. // If already bound, the binding will be overwritten. // It cannot be used anymore once the SetupNewTableRep object is used to // construct a Table object. void bindColumn (const String& columnName, const DataManager&); // Bind a column to the given data manager of the other column. // If the other column is not bound, nothing will be done. // If columnName is already bound, the binding will be overwritten. // It cannot be used anymore once the SetupNewTableRep object is used to // construct a Table object. void bindColumn (const String& columnName, const String& otherColumn); // Bind a group of columns to the given data manager. // The flag rebind tells if the binding of an already bound column // will be overwritten. // It cannot be used anymore once the SetupNewTableRep object is used to // construct a Table object. void bindGroup (const String& columnGroup, const DataManager&, Bool rebind=False); // Bind all columns to the given data manager. // The flag rebind tells if the binding of an already bound column // will be overwritten. // It cannot be used anymore once the SetupNewTableRep object is used to // construct a Table object. void bindAll (const DataManager&, Bool rebind=False); // Create data managers and bind the columns using the specifications // in the given record (which is obtained using Table::dataManagerInfo()). void bindCreate (const Record& spec); // Define the shape of fixed shaped arrays in a column. // The shape of those arrays has to be known before the table // can be constructed. It has to be defined via this function, // if it was not already defined in the column description. // If only the dimensionality was defined in the column // description, the shape's dimensionality must match it. // Calling this function for an non-fixed shaped array results in // an exception. // It cannot be used anymore once the SetupNewTableRep object is used to // construct a Table object. void setShapeColumn (const String& columnName, const IPosition& shape); // Test if object is already in use. Bool isUsed() const { return !colSetPtr_p; } // Get pointer to column set. // This function is used by PlainTable. const std::shared_ptr& columnSetPtr() const { return colSetPtr_p; } // Get pointer to table description. // This function is used by PlainTable. const std::shared_ptr& tableDescPtr() const { return tdescPtr_p; } // Set object to in use by a (Plain)Table object. // This function is used by PlainTable. void setInUse() { colSetPtr_p.reset(); } // Make a data manager for all unbound columns. void handleUnbound(); private: // Table name. String tabName_p; // Constructor options. int option_p; StorageOption storageOpt_p; // Marked for delete? Bool delete_p; std::shared_ptr tdescPtr_p; std::shared_ptr colSetPtr_p; //# null = object is already used by a Table std::map dataManMap_p; // Setup the new table. // This checks various things and creates the set of columns. void setup(); // Get the internal data manager object for the given data manager. // If it does not exist yet, it will be cloned and stored internally. DataManager* getDataManager (const DataManager& dataMan); }; // // Create a new table - define shapes, data managers, etc. // // // // // //
      • TableDesc and related classes like ArrayColumnDesc //
      • DataManager //
      • Table // // // SetupNewTable is a class to setup a new table. // // // Constructing a new table is a two stage process. // First a SetupNewTable object has to be created. Thereafter its columns // have to be bound defining how they have to be stored or calculated. // Columns have to be bound to a data manager (e.g. a storage manager // or a virtual column engine).. // Once the required columns are bound, the actual Table object can // be created. At this stage, still unbound columns will be bound // to the default data managers. // The Table object can be used to write data, etc. // // The construct options for SetupNewTable are defined in class Table. // The possible options are: //
          //
        • New // creates a new table file. // The Table destructor will write the table into the file. //
        • NewNoReplace // as option New, but an exception will be thrown if the table // file already exists. //
        • Scratch // creates a temporary table. // It will be lost when the Table object gets destructed. //
        // More information is provided in the Tables module documentation. //
        // // // // Table makeIt(const TableDesc &td) { // 1 // SetupNewTable maker("test.table", td, Table::New); // 2 // maker.setShapeColumn("SomeArray", IPosition(2,10,10)); // 3 // maker.setShapeColumn("AnotherArray", IPosition(1,100)); // 4 // StManAipsIO sm1; // 5 // StManKarma sm2; // 6 // maker.bindAll(sm1); // 7 // maker.bindColumn("SomeCol", sm2); // 8 // maker.bindColumn("AnotherCol", sm2); // 9 // return Table(maker, 1000); // 1000 row table // 10 // } // 11 // // This code illustrates a simple function that creates a Table starting // from a Table descriptor. I //
          //
        1. Declare the function makeIt which, given a TableDesc, returns // a table. //
        2. Create the SetupNewTable object "maker". We want the new table // to be named "test.table", its rows columns and keywords come // from the TableDesc "td", and this table is to be created // unconditionally, that is, it will overwrite an existing table // of the same name. Alternative options are given in the synopsis. //
        3. //
        4. Give direct arrays declared in the table descriptor (but not // necessarily given a shape) a defined shape; 10x10 for the first // array, 100 long vector for the second. If all direct arrays // do not have a shape, an error will occur when the table is // actually constructed. //
        5. //
        6. Declare two data (storage) managers. AipsIO keeps a whole column // in memory, Karma does I/O to keep a subsection in memory at once. // A powerful feature of Casacore tables is that different columns // may be bound to different data managers, which have different // properties. //
        7. Define the default data manager. AipsIO in this case. // Note that this statement and statement 5 are actually not // needed. When the Table constructor finds some unbound columns, // it will construct the default data manager for them and // bind them. A default data manager can be defined in the // column description and defaults to AipsIO. //
        8. //
        9. Override the default for some particular columns. //
        10. Create and return a 1000 row table. With the Karma storage manager // the table size must be defined at construction since new rows // can't be added or deleted. If AipsIO was the only storage manager, // the size wouldn't need to be defined since rows can be added with // AipsIO. //
        //
        // // In principle, SetupNewTab isn't necessary as what we are doing is logically // just constructing a Table, so it could be done in the Table constructor. // However such a process can be an involved one - binding multiple data // managers and filling in the shapes of direct arrays - so separating // the process makes it much clearer what is going on. // // //# A List of bugs, limitations, extensions or planned refinements. //
      • full implementation of tiling // class SetupNewTable { friend class PlainTable; friend class MemoryTable; public: // Create a new table using the table description with the given name. // The description will be read from a file. SetupNewTable (const String& tableName, const String& tableDescName, Table::TableOption, const StorageOption& = StorageOption()); // Create a new table using the given table description. SetupNewTable (const String& tableName, const TableDesc&, Table::TableOption, const StorageOption& = StorageOption()); // Copy constructor (reference semantics). SetupNewTable (const SetupNewTable&); ~SetupNewTable(); // Assignment (reference semantics). SetupNewTable& operator= (const SetupNewTable&); // Get the name of the table. const String& name() const { return newTable_p->name(); } // Get the table create option. int option() const { return newTable_p->option(); } // Get the storage option. const StorageOption& storageOption() const { return newTable_p->storageOption(); } // Test if the table is marked for delete. Bool isMarkedForDelete() const { return newTable_p->isMarkedForDelete(); } // Get the table description. const TableDesc& tableDesc() const { return newTable_p->tableDesc(); } // Adjust the hypercolumn definitions. // It renames and/or removes columns as necessary. void adjustHypercolumns (const std::map& old2new, Bool keepUnknown) { newTable_p->tableDescPtr()->adjustHypercolumns(old2new,keepUnknown); } // Bind a column to the given data manager. // If already bound, the binding will be overwritten. // It cannot be used anymore once the SetupNewTable object is used to // construct a Table object. void bindColumn (const String& columnName, const DataManager& dm) { newTable_p->bindColumn (columnName, dm); } // Bind a column to the given data manager of the other column. // If the other column is not bound, nothing will be done. // If columnName is already bound, the binding will be overwritten. // It cannot be used anymore once the SetupNewTableRep object is used to // construct a Table object. void bindColumn (const String& columnName, const String& otherColumn) { newTable_p->bindColumn (columnName, otherColumn); } // Bind a group of columns to the given data manager. // The flag rebind tells if the binding of an already bound column // will be overwritten. // It cannot be used anymore once the SetupNewTable object is used to // construct a Table object. void bindGroup (const String& columnGroup, const DataManager& dm, Bool rebind=False) { newTable_p->bindGroup (columnGroup, dm, rebind); } // Bind all columns to the given data manager. // The flag rebind tells if the binding of an already bound column // will be overwritten. // It cannot be used anymore once the SetupNewTable object is used to // construct a Table object. void bindAll (const DataManager& dm, Bool rebind=False) { newTable_p->bindAll (dm, rebind); } // Create data managers and bind the columns using the specifications // in the given record (which is obtained using Table::dataManagerInfo()). void bindCreate (const Record& spec) { newTable_p->bindCreate (spec); } // Define the shape of fixed shaped arrays in a column. // The shape of those arrays has to be known before the table // can be constructed. It has to be defined via this function, // if it was not already defined in the column description. // If only the dimensionality was defined in the column // description, the shape's dimensionality must match it. // Calling this function for an non-fixed shaped array results in // an exception. // It cannot be used anymore once the SetupNewTable object is used to // construct a Table object. void setShapeColumn (const String& columnName, const IPosition& shape) { newTable_p->setShapeColumn (columnName, shape); } // Test if object is already in use. Bool isUsed() const { return newTable_p->isUsed(); } private: // Actual object. std::shared_ptr newTable_p; // Get pointer to column set. // This function is used by PlainTable. const std::shared_ptr& columnSetPtr() const { return newTable_p->columnSetPtr(); } // Get pointer to table description. // This function is used by PlainTable. const std::shared_ptr& tableDescPtr() const { return newTable_p->tableDescPtr(); } // Set object to in use by a (Plain)Table object. // This function is used by PlainTable. void setInUse() { newTable_p->setInUse(); } // Make a data manager for all unbound columns. void handleUnbound() { newTable_p->handleUnbound(); } }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/StorageOption.cc000066400000000000000000000055121476623553700211170ustar00rootroot00000000000000//# StorageOption.cc: Options defining how table files are organized //# Copyright (C) 2014 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have receied a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN StorageOption::StorageOption (StorageOption::Option option, Int blockSize, Int useODirect) : itsOption (option), itsBlockSize (blockSize), itsUseODirect (useODirect>0), itsUseAipsrcODirect (useODirect<0) {} void StorageOption::fillOption() { // Get variables from aipsrc if needed. if (itsOption == StorageOption::Aipsrc) { String opt; AipsrcValue::find (opt, "table.storage.option", "default"); opt.downcase(); if (opt == "multifile") { itsOption = StorageOption::MultiFile; } else if (opt == "multihdf5") { itsOption = StorageOption::MultiHDF5; } else if (opt == "sepfile") { itsOption = StorageOption::SepFile; } else { itsOption = StorageOption::Default; } } // Default block size is 4MB. if (itsBlockSize <= -2) { AipsrcValue::find (itsBlockSize, "table.storage.blocksize", 0); } if (itsBlockSize <= 0) { itsBlockSize = 4*1024*1024; } // Default O_DIRECT support is False. if (itsUseAipsrcODirect) { AipsrcValue::find (itsUseODirect, "table.storage.odirect", False); } // Default is to use separate files. if (itsOption == StorageOption::Default) { itsOption = StorageOption::SepFile; } } void StorageOption::setUseODirect (Bool useODirect) { itsUseODirect = useODirect; itsUseAipsrcODirect = False; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/StorageOption.h000066400000000000000000000113771476623553700207670ustar00rootroot00000000000000//# StorageOption.h: Options defining how table files are organized //# Copyright (C) 2014 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have receied a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_STORAGEOPTION_H #define TABLES_STORAGEOPTION_H //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Options defining how table files are organized // // // // // // This class can be used to define how the files of a table are organized. // There are two ways: //
          //
        1. The old way where each storage manager has its own file(s). //
        2. Using MultiFile that storage managers can use to combine about all // table files in a single file. This mode is particularly useful // for new file systems (like Lustre) requiring large block sizes. //
          The block size to be used in a MultiFile can be defined in // this class. Default is 4 MByte. //
        3. Using MultiHDF5 which behaves similar to MultiFile but uses an // HDF5 file instead of a regular file. Note that it requires that // support for HDF5 has been used in the build system. //
        // It is possible to specify the storage type and block size using aipsrc. // The aipsrc variables are: //
          //
        • table.storage.option. The (case-insensitive) value can be // 'multifile' or 'multihdf5'. // Another value means the old way (separate files). //
        • table.storage.blocksize gives the default blocksize to be // used for the multifile and multihdf5 option. //
        • table.storage.odirect can be true or false. It tells if the // O_DIRECT option has to be used to let the kernel bypass its filecache // for more predictable I/O behaviour. It's only used for MultiFile and // only if the OS supports O_DIRECT. //
        //
        class StorageOption { public: // Define the possible options how table files are organized. enum Option { // Let storage managers use a combined MultiFile. MultiFile, // Let storage managers use a combined MultiHDF5. MultiHDF5, // Let storage managers use separate files. SepFile, // Use default (currently SepFile). Default, // Use as defined in the aipsrc file. Aipsrc }; // Create an option object. // The parameter values are described in the synopsis. // The blocksize has to be given in bytes. // A size value -2 means reading that size from the aipsrc file. // A size value -1 means use the default of 4*1024*1024. //
        useODirect<0 means reading the option from the aipsrc file. // It is only set if the OS supports O_DIRECT. StorageOption (Option option=Aipsrc, Int blockSize=-2, Int useODirect=-3); // Fill the option in case Aipsrc or Default was given. // It is done as explained in the synopsis. void fillOption(); // Get the option. Option option() const { return itsOption; } // Set the option. void setOption (Option option) { itsOption = option; } // Get the block size (in bytes). uInt blockSize() const { return itsBlockSize; } // Set the block size (in bytes). void setBlockSize (Int blockSize) { itsBlockSize = blockSize; } // Get the O_DIRECT option. Bool useODirect() const { return itsUseODirect; } // Set the O_DIRECT option. // It is only set if the OS supports O_DIRECT. void setUseODirect (Bool useODirect); private: Option itsOption; Int itsBlockSize; Bool itsUseODirect; Bool itsUseAipsrcODirect; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/SubTabDesc.cc000066400000000000000000000135541476623553700203060ustar00rootroot00000000000000//# SubTabDesc.cc: Description of columns containing tables //# Copyright (C) 1994,1995,1996,1997,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN SubTableDesc::SubTableDesc (const String& name, const String& comment, const String& descname, int opt) : BaseColumnDesc(name, comment, "", "", TpTable, "", opt, 1, IPosition(), False, False, True), tabDescPtr_p (0), tabDescTyp_p (descname), byName_p (True), allocSelf_p (True), shallowCopy_p (False) { readTableDesc(); } SubTableDesc::SubTableDesc (const String& name, const String& comment, const TableDesc& desc, int opt) : BaseColumnDesc(name, comment, "", "", TpTable, "", opt, 1, IPosition(), False, False, True), tabDescPtr_p (new TableDesc(desc, "", "", TableDesc::Scratch)), tabDescTyp_p (desc.getType()), byName_p (False), allocSelf_p (True), shallowCopy_p (False) {} SubTableDesc::SubTableDesc (const String& name, const String& comment, TableDesc* descptr, int opt) : BaseColumnDesc(name, comment, "", "", TpTable, "", opt, 1, IPosition(), False, False, True), tabDescPtr_p (descptr), tabDescTyp_p (descptr->getType()), byName_p (False), allocSelf_p (False), shallowCopy_p (True) {} SubTableDesc::SubTableDesc (const SubTableDesc& that) : BaseColumnDesc(that), tabDescPtr_p (0), tabDescTyp_p (""), allocSelf_p (False) { operator= (that); } //# Make a new object. BaseColumnDesc* SubTableDesc::makeDesc (const String&) { BaseColumnDesc* ptr = new SubTableDesc("", "", TableDesc()); return ptr; } SubTableDesc::~SubTableDesc() { if (allocSelf_p) { delete tabDescPtr_p; } } SubTableDesc& SubTableDesc::operator= (const SubTableDesc& that) { BaseColumnDesc::operator= (that); if (allocSelf_p) { delete tabDescPtr_p; } tabDescPtr_p = 0; tabDescTyp_p = that.tabDescTyp_p; byName_p = that.byName_p; allocSelf_p = True; shallowCopy_p = that.shallowCopy_p; if (shallowCopy_p) { tabDescPtr_p = that.tabDescPtr_p; allocSelf_p = False; }else if (byName_p) { readTableDesc(); }else if (that.tabDescPtr_p != 0) { tabDescPtr_p = new TableDesc (*that.tabDescPtr_p, "", "", TableDesc::Scratch); } return *this; } //# Clone this column description to another. BaseColumnDesc* SubTableDesc::clone() const { SubTableDesc* ptr = new SubTableDesc(*this); return ptr; } //# Return the class name. String SubTableDesc::className() const { return "SubTableDesc"; } //# Put the object. //# The data is read by the ctor taking AipsIO. //# It was felt that putstart takes too much space, so therefore //# the version is put "manually". void SubTableDesc::putDesc (AipsIO& ios) const { ios << (uInt)1; // class version 1 ios << tabDescTyp_p; ios << byName_p; if (!byName_p) { tabDescPtr_p->putFile (ios, TableAttr()); } } void SubTableDesc::getDesc (AipsIO& ios) { uInt version; ios >> version; ios >> tabDescTyp_p; ios >> byName_p; //# If referenced by name, read the table description. //# Otherwise get it from the file itself. if (allocSelf_p) { delete tabDescPtr_p; } tabDescPtr_p = 0; if (byName_p) { readTableDesc(); }else{ tabDescPtr_p = new TableDesc; tabDescPtr_p->getFile (ios, TableAttr()); // get nested table desc. } } //# Get the table description. //# Throw exception if not there. TableDesc* SubTableDesc::tableDesc() { if (tabDescPtr_p == 0) { throw (TableNoFile("desc. " + tabDescTyp_p)); } return tabDescPtr_p; } //# Reread the table description if referenced by name. Bool SubTableDesc::readTableDesc() { Bool success = True; if (byName_p) { if (allocSelf_p) { delete tabDescPtr_p; } tabDescPtr_p = 0; if (TableDesc::isReadable (tabDescTyp_p)) { tabDescPtr_p = new TableDesc(tabDescTyp_p); }else{ success = False; } } return success; } //# Once the column is added, a deep copy has to be made. void SubTableDesc::handleAdd (ColumnDescSet&) { shallowCopy_p = False; } //# Show the column. void SubTableDesc::show (ostream& os) const { os << " Name=" << name(); os << " Subtable type=" << tabDescTyp_p; if (byName_p) { os << " (by name)"; }else{ if (!allocSelf_p) { os << " (directly)"; } } os << endl; os << " Comment = " << comment() << endl; } PlainColumn* SubTableDesc::makeColumn (ColumnSet*) const { return 0; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/SubTabDesc.h000066400000000000000000000204271476623553700201450ustar00rootroot00000000000000//# SubTabDesc.h: Description of columns containing tables //# Copyright (C) 1994,1995,1996,1997,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_SUBTABDESC_H #define TABLES_SUBTABDESC_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class PlainColumn; class ColumnSet; class TableDesc; class String; class AipsIO; // // Description of columns containing tables // // // // // //# Classes you should understand before using this one. //
      • TableDesc //
      • BaseColumnDesc // // // SubTableDesc holds a description of a subtable contained in the // columns of the parent table. // // // SubTableDesc describes a table column containing subtables. // The semantics of subtables are described below. // The column description is constructed using a table description // describing the subtable. This subtable decription or its name is // stored with the column description. // When a table containing this column gets created, the subtable // description gets copied and this copy is thereafter frozen. // Constructing a column description for a subtable can be done // in 3 ways: //
          //
        • It can be constructed with the name of a table description // kept in a file. Only this name will be stored with the column // description. Only when the table column gets created, // it will read the newest version of this table description. // This is a completely dynamic way of defining the column. // When the subtable description in the file changes, this column // in newly created tables gets the latest version. //
        • It can be constructed with a given table description. // This means that a copy of that description will be made. // The frozen subtable description will be stored with the // column description. // This is a completely static way of defining the column. //
        • It can be constructed with a pointer to a table description. // This means that a copy will only be made when the column // description gets written. Thus changes to the subtable // description will as long as possible be reflected in the // column description. // This is a mix of the first two ways. //
        // // A column can be direct or indirect. // Direct columns will be written directly in the table file. All cells // in the column must have the same description and it is therefore not // possible to change a description. // The subtables in indirect columns will be stored in separate files. // The cells in indirect columns can contain different tables. //
        // // // // First build the new description of a subtable. // // Define keyword subkey (integer) having value 10. // // Define columns ra and dec (double). // TableDesc subTableDesc("tTableDesc_sub", "1", TableDesc::New); // subTableDesc.keywordSet().keysInt()("subkey") = 10; // subTableDesc.addColumn (TpDouble, "ra"); // subTableDesc.addColumn (TpDouble, "dec"); // // // Now create a new table description // TableDesc td("tTableDesc", "1", TableDesc::New); // // // Add columns containing subtables. // // This is done in 3 slighty different ways, which all have // // their own (dis)advantages. // // This is described in detail at the SubTableDesc constructors. // td.addColumn (SubTableDesc("sub1", "subtable by name","tTableDesc_sub")); // td.addColumn (SubTableDesc("sub2", "subtable copy", subTableDesc)); // td.addColumn (SubTableDesc("sub3", "subtable pointer", &subTableDesc)); // // // // Several column description classes are needed to allow the user // to define attributes which are special for each column type. // For columns containing a table this is the table description. // // //# A List of bugs, limitations, extensions or planned refinements. //
      • Probably only direct table descriptions should be allowed. // Indirect arrays can have a shape in the description // (although they can have #dim), so tables should behave // similarly. // class SubTableDesc : public BaseColumnDesc { public: friend class ColumnDesc; public: // Construct from a table description with the given name. // The description does not need to exist yet. Only when the // table gets created, the description will be read and must exist. // This means that the table description is not frozen; the most // recent description will be used when creating the column. SubTableDesc (const String& columnName, const String& comment, const String& tableDescName, int options = 0); // Construct from the given table description, which will be copied // and frozen. SubTableDesc (const String& columnName, const String& comment, const TableDesc&, int options = 0); // Construct from the given table description, which will be used // directly. The description gets frozen when the column is written. // Care should be taken, because the given table description must // not be deleted before the column description gets destructed. SubTableDesc (const String& columnName, const String& comment, TableDesc*, int options = 0); // Copy constructor (copy semantics). SubTableDesc (const SubTableDesc&); ~SubTableDesc(); // Assignment (copy semantics). SubTableDesc& operator= (const SubTableDesc&); // Clone this column description to another. BaseColumnDesc* clone() const; // Get the table description. // //
      • TableNoFile // TableDesc* tableDesc(); // Get the name of this class. String className() const; // Create a Column column object out of this. // This is used by class ColumnSet to construct a table column object. PlainColumn* makeColumn (ColumnSet*) const; // Show the column. void show (ostream& os) const; // Create the object from AipsIO (this function is registered). static BaseColumnDesc* makeDesc(const String& name); protected: // Put the object. virtual void putDesc (AipsIO&) const; // Get the object. virtual void getDesc (AipsIO&); private: TableDesc* tabDescPtr_p; //# pointer to Table Description String tabDescTyp_p; //# type of table description Bool byName_p; //# True = TableDesc name is given Bool allocSelf_p; //# True = allocated tdptr itself Bool shallowCopy_p; //# True = make shallow copy //# (is only set when !allocSelf) // Read table description (if passed by name). // If the table description is not found, a False value is returned. Bool readTableDesc(); // Handle the addition of the subtable description (clear the flag). void handleAdd (ColumnDescSet&); }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/TVec.h000066400000000000000000000143371476623553700170320ustar00rootroot00000000000000//# TVec.h: Templated base class for table vectors //# Copyright (C) 1994,1995,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TVEC_H #define TABLES_TVEC_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Enumeration of possible table vectors // // // // // // Define the type of table vectors. // Alas, this enum has to be defined outside the class, because // some compilers do not support an enum in a templated class. // // enum TabVecTag { // Table Vector is a scalar column TagScaCol = 1, // Table Vector is a temporary vector (i.e. a regular vector). TagTemp = 2 }; // // // Templated base class for table vectors // // // // // //# Classes you should understand before using this one. //
      • TableVector // // // TabVecRep is the representation of a table vector. // // // TabVecRep is the counted referenced letter class for the envelope // class TableVector. It is an abstract base class for the actual // table vector classes TabVecScaCol and TabVecTemp. // // All operations defined for TableVector are immediately passed to // the corresponding virtual TabVecRep function. // The header files TVecMath.h and TVecLogic.h declare all the // mathematical and logical functions for TabVecRep. // // // A virtual function call only works when used with an object // pointer or reference. To allow the use of virtual functions // in value objects, an extra level of indirection is used. // This is called the letter/envelope idiom and is described in // "Advanced C++" by J. Coplien. // Class TableVector is the envelope to the letters TabVecRep and // its derivations. // // //# A List of bugs, limitations, extensions or planned refinements. //
      • put the TabVecTag enum inside the class definition //
      • support array columns // template class TabVecRep { public: // Create empty table vector. // TabVecRep cannot be contructed by the user, because it is an // abstract base class (it contains pure virtual functions). TabVecRep(); // Destruct the object. virtual ~TabVecRep(); // Get nr of dimensions. inline uInt ndim() const; // Get nr of elements (ie. vector length). inline rownr_t nelements() const; // Test if vector shape conforms another table vector. inline Bool conform(const TabVecRep&) const; // Test if vector shape conforms another vector. inline Bool conform(const Vector&) const; // Check internal consistency. Bool ok() const; // Increments the reference count. inline TabVecRep* link(); // Decrements the reference count and returns the resulting count. inline uInt unlink(); // Get the tag (the type of vector). inline TabVecTag getTag() const; // Get a value. virtual T value (rownr_t index) const = 0; // Get a value. virtual void getVal (rownr_t index, T&) const = 0; // Put a value. virtual void putVal (rownr_t index, const T&) = 0; // Set entire vector to a value. virtual void set (const T&) = 0; // Set to another table vector. virtual void assign (const TabVecRep&); protected: uInt count_p; //# reference count TabVecTag tag_p; Int64 nrel_p; //# #elements (<0 = ask derived class) // Get nr of elements. virtual rownr_t nelem() const; public: // Check if vectors are comformant. void validateConformance (rownr_t) const; // Create a new temporary vector (for result of math operations). // TabVecTemp& cannot be used, because the template instantiation // mechanism instantiates TabVecTemp, which depends on TabVecRep and // therefore gives errors. void* newVec() const; }; template inline uInt TabVecRep::ndim() const { return 1; } template inline rownr_t TabVecRep::nelements() const { return (nrel_p<0 ? nelem() : nrel_p); } //# Check if 2 table vectors are conformant. template inline Bool TabVecRep::conform (const TabVecRep& vec) const { return (nelements() == vec.nelements() ? True : False); } template inline Bool TabVecRep::conform (const Vector& vec) const { return (nelements() == vec.nelements() ? True : False); } //# Maintain reference count. template inline TabVecRep* TabVecRep::link() { count_p++; return this; } template inline uInt TabVecRep::unlink() { return --count_p; } //# Return the tag. template inline TabVecTag TabVecRep::getTag() const { return tag_p; } } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/tables/Tables/TVec.tcc000066400000000000000000000044441476623553700173520ustar00rootroot00000000000000//# TVec.cc: Template table column or memory vectors //# Copyright (C) 1994,1995,1996,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TVEC_TCC #define TABLES_TVEC_TCC #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Construct template TabVecRep::TabVecRep() : count_p(0), nrel_p (0) { ; } //# Destructor. template TabVecRep::~TabVecRep() { ; } template rownr_t TabVecRep::nelem() const { return nrel_p; } template void TabVecRep::validateConformance (rownr_t leng) const { if (nelements() != leng) { throw TableVectorNonConform(); } } //# Create a new vector (in memory). template void* TabVecRep::newVec() const { rownr_t nr = nelements(); TabVecTemp* tmvp = new TabVecTemp(nr); return tmvp; } template void TabVecRep::assign (const TabVecRep& that) { rownr_t nr = that.nelements(); validateConformance (nr); for (rownr_t i=0; i namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class TabVecRep; // // Comparison between two table vectors // // // // // // Element by element comparisons between two table vectors // or between a table vector and a scalar. // The result is true only if the comparison is true for every element // of the table vectors. // At some point operators will be available that return masks where the // comparison is true. // The left and right operands must be conformant (i.e. have equal length). // The functions are the implementation of the wrapper functions // defined in TabVecLogic.h. // // template Bool tabVecReptvLE (const TabVecRep& left, const TabVecRep& right); template Bool tabVecReptvLT (const TabVecRep& left, const TabVecRep& right); template Bool tabVecReptvGE (const TabVecRep& left, const TabVecRep& right); template Bool tabVecReptvGT (const TabVecRep& left, const TabVecRep& right); template Bool tabVecReptvEQ (const TabVecRep& left, const TabVecRep& right); template Bool tabVecReptvNE (const TabVecRep& left, const TabVecRep& right); // // // Comparison between a table vector and a scalar // // // // // // Element by element comparisons between a table vector and a scalar, // which behaves as if it were a conformant table vector filled with the // scalar value. // At some point operators will be available that return masks where the // comparison is true. // The functions are the implementation of the wrapper functions // defined in TabVecLogic.h. // // template Bool tabVecRepvalrLE (const TabVecRep& left, const T& right); template Bool tabVecRepvallLE (const T& left, const TabVecRep& right); template Bool tabVecRepvalrLT (const TabVecRep& left, const T& right); template Bool tabVecRepvallLT (const T& left, const TabVecRep& right); template Bool tabVecRepvalrGE (const TabVecRep& left, const T& right); template Bool tabVecRepvallGE (const T& left, const TabVecRep& right); template Bool tabVecRepvalrGT (const TabVecRep& left, const T& right); template Bool tabVecRepvallGT (const T& left, const TabVecRep& right); template Bool tabVecRepvalrEQ (const TabVecRep& left, const T& right); template Bool tabVecRepvallEQ (const T& left, const TabVecRep& right); template Bool tabVecRepvalrNE (const TabVecRep& left, const T& right); template Bool tabVecRepvallNE (const T& left, const TabVecRep& right); // } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/tables/Tables/TVecLogic.tcc000066400000000000000000000052171476623553700203270ustar00rootroot00000000000000//# TVecLogic.cc: Global functions for table vector logical operations //# Copyright (C) 1994,1995,1997 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TVECLOGIC_TCC #define TABLES_TVECLOGIC_TCC #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN #define TVECLOGICOPER(NAME,OP) \ template \ Bool aips_name2(tabVecReptv,NAME) (const TabVecRep& l, const TabVecRep& r) \ { \ rownr_t nr = r.nelements(); \ l.validateConformance(nr); \ Bool retval = True; \ for (rownr_t i=0; i \ Bool aips_name2(tabVecRepvalr,NAME) (const TabVecRep& tv, const T& val) \ { \ rownr_t nr = tv.nelements(); \ Bool retval = True; \ for (rownr_t i=0; i \ Bool aips_name2(tabVecRepvall,NAME) (const T& val, const TabVecRep& tv) \ { \ rownr_t nr = tv.nelements(); \ Bool retval = True; \ for (rownr_t i=0; i=) TVECLOGICOPER(GT,>) TVECLOGICOPER(EQ,==) TVECLOGICOPER(NE,!=) } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/TVecMath.h000066400000000000000000000230461476623553700176410ustar00rootroot00000000000000//# TVecMath.h: Global helper functions for table vector mathematics //# Copyright (C) 1994,1995,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TVECMATH_H #define TABLES_TVECMATH_H //# Includes #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations template class TabVecRep; // // Basic math for table vectors. // // // // // // These global functions do the basic math for table vectors. // This means addition, subtraction, multiplication, division // and negation. // In case two table vectors are used, the left and right operand // must be conformant (i.e. have equal length). // // // Add 2 table vectors storing result in first one. template void tabVecReptvassadd (TabVecRep&, const TabVecRep&); // Subtract 2 table vectors storing result in first one. template void tabVecReptvasssub (TabVecRep&, const TabVecRep&); // Multiple 2 table vectors storing result in first one. template void tabVecReptvasstim (TabVecRep&, const TabVecRep&); // Divide 2 table vectors storing result in first one. template void tabVecReptvassdiv (TabVecRep&, const TabVecRep&); // Add a scalar to each element in the table vector. template void tabVecRepvalassadd (TabVecRep&, const T&); // Subtract a scalar from each element in the table vector. template void tabVecRepvalasssub (TabVecRep&, const T&); // Multiple each element in the table vector with a scalar. template void tabVecRepvalasstim (TabVecRep&, const T&); // Divide each element in the table vector by a scalar. template void tabVecRepvalassdiv (TabVecRep&, const T&); // Unary minus - store result in a new vector. // // (unary plus is already handled in TabVecMath). // // template TabVecRep& tabVecRepnegate (const TabVecRep&); // Add 2 table vectors storing result in a new one. template TabVecRep& tabVecReptvadd (const TabVecRep&, const TabVecRep&); // Subtract 2 table vectors storing result in a new one. template TabVecRep& tabVecReptvsub (const TabVecRep&, const TabVecRep&); // Multiple 2 table vectors storing result in a new one. template TabVecRep& tabVecReptvtim (const TabVecRep&, const TabVecRep&); // Divide 2 table vectors storing result in a new one. template TabVecRep& tabVecReptvdiv (const TabVecRep&, const TabVecRep&); // Add a scalar to each element in the table vector storing result // in a new table vector. template TabVecRep& tabVecRepvalradd (const TabVecRep&, const T&); // Subtract a scalar from each element in the table vector storing result // in a new table vector. template TabVecRep& tabVecRepvalrsub (const TabVecRep&, const T&); // Multiple each element in the table vector with a scalar storing result // in a new table vector. template TabVecRep& tabVecRepvalrtim (const TabVecRep&, const T&); // Divide each element in the table vector by a scalar storing result // in a new table vector. template TabVecRep& tabVecRepvalrdiv (const TabVecRep&, const T&); // Add a scalar to each element in the table vector storing result // in a new table vector. template TabVecRep& tabVecRepvalladd (const T&, const TabVecRep&); // Subtract a scalar from each element in the table vector storing result // in a new table vector. template TabVecRep& tabVecRepvallsub (const T&, const TabVecRep&); // Multiple each element in the table vector with a scalar storing result // in a new table vector. template TabVecRep& tabVecRepvalltim (const T&, const TabVecRep&); // Divide each element in the table vector by a scalar storing result // in a new table vector. template TabVecRep& tabVecRepvalldiv (const T&, const TabVecRep&); // // // Transcendental math for table vectors. // // // // // // These global functions do the transcendental math for table vectors // for essentially all numeric types. // The functions are sin, sinh, exp, log, pow, etc.. // In case two table vectors are used, the left and right operand // must be conformant (i.e. have equal length). // // template TabVecRep& tabVecRepcos (const TabVecRep&); template TabVecRep& tabVecRepcosh (const TabVecRep&); template TabVecRep& tabVecRepexp (const TabVecRep&); template TabVecRep& tabVecReplog (const TabVecRep&); template TabVecRep& tabVecReplog10(const TabVecRep&); template TabVecRep& tabVecReppow (const TabVecRep&, const TabVecRep&); template TabVecRep& tabVecRepsin (const TabVecRep&); template TabVecRep& tabVecRepsinh (const TabVecRep&); template TabVecRep& tabVecRepsqrt (const TabVecRep&); // // // Further transcendental math for table vectors. // // // // // // These global functions do the transcendental math for table vectors // for a limited set of numeric types. // The functions are asin, ceil, etc.. // In case two table vectors are used, the left and right operand // must be conformant (i.e. have equal length). // // template TabVecRep& tabVecRepacos (const TabVecRep&); template TabVecRep& tabVecRepasin (const TabVecRep&); template TabVecRep& tabVecRepatan (const TabVecRep&); template TabVecRep& tabVecRepatan2(const TabVecRep&, const TabVecRep&); template TabVecRep& tabVecRepceil (const TabVecRep&); template TabVecRep& tabVecRepfabs (const TabVecRep&); template TabVecRep& tabVecRepfloor(const TabVecRep&); template TabVecRep& tabVecRepfmod (const TabVecRep&, const TabVecRep&); template TabVecRep& tabVecReppow (const TabVecRep&, const double&); template TabVecRep& tabVecReptan (const TabVecRep&); template TabVecRep& tabVecReptanh (const TabVecRep&); // // // Miscellaneous table vector operations. // // // // // // Fill a table vector or calculate the sum, product, minimum or // maximum of its elements. // // // Determine minimum and maximum value in a table vector. // Requires that the type "T" has comparison operators. template void tabVecRepminmax (T& min, T& max, const TabVecRep&); // Fills all elements of the table vector with a sequence starting with // "start" and incrementing by "inc" for each element. template void tabVecRepindgen (TabVecRep&, T start, T inc); // Sum of all the elements of a table vector. template T tabVecRepsum (const TabVecRep&); // Product of all the elements of a table vector. // // product can easily overflow. // template T tabVecRepproduct (const TabVecRep&); // // // Vector operations on a table vector. // // // // // // Do vector operations on a table vector (like inner product). // // // The inner product of 2 table vectors. template T tabVecRepinnerproduct (const TabVecRep&, const TabVecRep&); // The norm of a table vector. template T tabVecRepnorm (const TabVecRep&); // The cross product of 2 table vectors containing 3 elements. template TabVecRep& tabVecRepcrossproduct (const TabVecRep&, const TabVecRep&); // } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/tables/Tables/TVecMath.tcc000066400000000000000000000170611476623553700201630ustar00rootroot00000000000000//# TVecMath.cc: Global helper functions for table vector mathematics //# Copyright (C) 1994,1995,1997 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TVECMATH_TCC #define TABLES_TVECMATH_TCC #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Add, subtract, multiply, divide table vector. //# Define it for a vector and scalar, 2 vectors, 2 vectors with assign. #define TVECMATHOPER(NAME,OP,OPA) \ template \ TabVecRep& aips_name2(tabVecRepvalr,NAME) (const TabVecRep& tv, \ const T& val) \ { \ rownr_t nr = tv.nelements(); \ TabVecTemp& vec = *(TabVecTemp*)tv.newVec(); \ for (rownr_t i=0; i \ TabVecRep& aips_name2(tabVecRepvall,NAME) (const T& val, \ const TabVecRep& tv) \ { \ rownr_t nr = tv.nelements(); \ TabVecTemp& vec = *(TabVecTemp*)tv.newVec(); \ T tmp; \ for (rownr_t i=0; i \ TabVecRep& aips_name2(tabVecReptv,NAME) (const TabVecRep& tvl, \ const TabVecRep& tvr) \ { \ rownr_t nr = tvr.nelements(); \ tvl.validateConformance(nr); \ TabVecTemp& vec = *(TabVecTemp*)tvl.newVec(); \ for (rownr_t i=0; i \ void aips_name2(tabVecRepvalass,NAME) (TabVecRep& tv, const T& val) \ { \ rownr_t nr = tv.nelements(); \ T tmp; \ for (rownr_t i=0; i \ void aips_name2(tabVecReptvass,NAME) (TabVecRep& tvl, \ const TabVecRep& tvr) \ { \ rownr_t nr = tvr.nelements(); \ tvl.validateConformance(nr); \ T tmp; \ for (rownr_t i=0; i TabVecRep& tabVecRepnegate(const TabVecRep& tv) { rownr_t nr = tv.nelements(); TabVecTemp& vec = *(TabVecTemp*)tv.newVec(); T tmp; for (rownr_t i=0; i void tabVecRepminmax (T& min, T& max, const TabVecRep& tv) { rownr_t nr = tv.nelements(); if (nr == 0) { throw(ArrayError("void minMax(T& min, T& max, const TabVecRep&) - " "TabVecRep has no elements")); } T tmp; tv.getVal (0, min); max = min; for (rownr_t i=1; i max) max = tmp; } } template void tabVecRepindgen(TabVecRep& tv, T start, T inc) { rownr_t nr = tv.nelements(); for (rownr_t i=0; i \ TabVecRep& aips_name2(tabVecRep,NAME) (const TabVecRep& tv) \ { \ rownr_t nr = tv.nelements(); \ TabVecTemp& vec = *(TabVecTemp*)tv.newVec(); \ T tmp; \ for (rownr_t i=0; i \ TabVecRep& aips_name2(tabVecRep,NAME) (const TabVecRep& tvl, \ const TabVecRep& tvr) \ { \ rownr_t nr = tvr.nelements(); \ tvl.validateConformance(nr); \ TabVecTemp& vec = *(TabVecTemp*)tvl.newVec(); \ T tmpl, tmpr; \ for (rownr_t i=0; i TabVecRep& tabVecReppowd (const TabVecRep& tv, const double& exp) { rownr_t nr = tv.nelements(); TabVecTemp& vec = *(TabVecTemp*)tv.newVec(); T tmp; for (rownr_t i=0; i T tabVecRepsum (const TabVecRep& tv) { rownr_t nr = tv.nelements(); if (nr == 0) { throw(ArrayError("T sum(const TabVecRep&) - " "TabVecRep has no elements")); } T tmp, res; tv.getVal (0, res); for (rownr_t i=1; i T tabVecRepproduct (const TabVecRep& tv) { rownr_t nr = tv.nelements(); if (nr == 0) { throw(ArrayError("T product(const TabVecRep&) - " "TabVecRep has no elements")); } T tmp, res; tv.getVal (0, res); for (rownr_t i=1; i T tabVecRepinnerproduct (const TabVecRep& tvl, const TabVecRep& tvr) { rownr_t nr = tvr.nelements(); tvl.validateConformance(nr); T res = 0; for (rownr_t i=0; i T tabVecRepnorm (const TabVecRep& tv) { rownr_t nr = tv.nelements(); T tmp; T res = 0; for (rownr_t i=0; i TabVecRep& tabVecRepcrossproduct (const TabVecRep& tvl, const TabVecRep& tvr) { tvl.validateConformance(3); tvr.validateConformance(3); TabVecTemp& vec = *(TabVecTemp*)tvl.newVec(); vec(0) = tvl.value(1) * tvr.value(2) - tvl.value(2) * tvr.value(1); vec(1) = tvl.value(2) * tvr.value(0) - tvl.value(0) * tvr.value(2); vec(2) = tvl.value(0) * tvr.value(1) - tvl.value(1) * tvr.value(0); return vec; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/TVecScaCol.h000066400000000000000000000067751476623553700201260ustar00rootroot00000000000000//# TVecScaCol.h: Templated table scalar column vectors //# Copyright (C) 1994,1995,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TVECSCACOL_H #define TABLES_TVECSCACOL_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableColumn; template class ScalarColumn; class String; // // Templated table scalar column vectors // // // // // //# Classes you should understand before using this one. //
      • TabVecRep // // // TabVecScaCol is the class dealing with a table vector representing // a column of scalars in a table. // // // TabVecScaCol objects are a view on a column of scalars in a table. // The semantics of these table vectors are the same as the normal // vectors. So for example, changing an element in the table vector // means changing the corresponding field in the underlying table. // // // TabVecScaCol is derived from TabVecRep and as such it is a letter for // the envelope class TableVector. // // //
      • Default constructor //
      • Copy constructor //
      • Assignment operator // // //# A List of bugs, limitations, extensions or planned refinements. // template class TabVecScaCol : public TabVecRep { //# Make members of parent class known. protected: using TabVecRep::tag_p; using TabVecRep::nrel_p; public: // Create a table vector from the given table column. // This constructor is for TableVector and allows elements to be changed. TabVecScaCol (const TableColumn& column); // Destruct the object. ~TabVecScaCol (); // Nr of elements (ie. #rows in table). rownr_t nelem() const; // Get a value. T value (rownr_t index) const; // Get a value. void getVal (rownr_t index, T&) const; // Put a value. void putVal (rownr_t index, const T&); // Set entire vector to a value. void set (const T&); protected: ScalarColumn* colPtr_p; }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/tables/Tables/TVecScaCol.tcc000066400000000000000000000051101476623553700204260ustar00rootroot00000000000000//# TVecScaCol.cc: Template table scalar column vectors //# Copyright (C) 1994,1995 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TVECSCACOL_TCC #define TABLES_TVECSCACOL_TCC #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Construct a table column vector. template TabVecScaCol::TabVecScaCol (const TableColumn& column) { //# Construct a scalar column. //# This will check the type, etc. and link to the BaseTable object. colPtr_p = new ScalarColumn (column); tag_p = TagScaCol; nrel_p = -1; // #rows is #nelements } //# Destructor. template TabVecScaCol::~TabVecScaCol () { delete colPtr_p; } template rownr_t TabVecScaCol::nelem() const { return colPtr_p->nrow(); } template T TabVecScaCol::value (rownr_t i) const { return (*colPtr_p)(i); } template void TabVecScaCol::getVal (rownr_t i, T& val) const { colPtr_p->get (i, val); } template void TabVecScaCol::putVal (rownr_t i, const T& val) { colPtr_p->put (i, val); } template void TabVecScaCol::set (const T& val) { rownr_t nrrow = colPtr_p->nrow(); for (rownr_t i=0; iput (i, val); } } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/TVecTemp.h000066400000000000000000000103771476623553700176600ustar00rootroot00000000000000//# TVecTemp.h: Templated table vectors held in memory as a temporary //# Copyright (C) 1994,1995,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TVECTEMP_H #define TABLES_TVECTEMP_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Templated table vectors held in memory as a temporary // // // // // //# Classes you should understand before using this one. //
      • TabVecRep // // // TabVecTemp is the class dealing with a table vector when used as // a temporary in math operations. // // // TabVecTemp objects enable the use of Vector objects as table vectors. // They are used for 2 purposes: //
          //
        1. To convert a Vector to a TableVector. This is used to // allow the use of Vectors in TableVector expressions. // The TabVecTemp object uses the Vector copy constructor, // which is very cheap due to its reference semantics. //
        2. To hold the result of an operation (like addition) on // two TableVector objects. //
        //
        // // TabVecTemp is derived from TabVecRep and as such a letter for // the envelope class TableVector. // // //
      • Default constructor //
      • Copy constructor //
      • Assignment operator // // //# A List of bugs, limitations, extensions or planned refinements. //
      • In the future temporary results may need to use a file, // because table vectors can potentially be very, very long. // template class TabVecTemp : public TabVecRep { //# Make members of parent class known. protected: using TabVecRep::tag_p; using TabVecRep::nrel_p; public: // Create table vector containing the given Vector (reference semantics). // It will set the origin to zero. TabVecTemp (const Vector&); // Create table vector containing a Vector with given length. TabVecTemp (rownr_t leng); // Destruct the object. ~TabVecTemp(); // Return a reference to a value. inline const T& operator() (rownr_t index) const; // Return a reference to a value. inline T& operator() (rownr_t index); // Get a value (virtual function). T value (rownr_t index) const; // Get a value (virtual function). void getVal (rownr_t index, T&) const; // Put a value (virtual function). void putVal (rownr_t index, const T&); // Set entire vector to a value. void set (const T&); protected: Vector* vecPtr_p; }; //# Return a reference to a value. template inline const T& TabVecTemp::operator() (rownr_t index) const { return (*vecPtr_p)(index); } template inline T& TabVecTemp::operator() (rownr_t index) { return (*vecPtr_p)(index); } } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/tables/Tables/TVecTemp.tcc000066400000000000000000000046371476623553700202040ustar00rootroot00000000000000//# TVecTemp.cc: Template table vectors held in memory as a temporary //# Copyright (C) 1994,1995,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TVECTEMP_TCC #define TABLES_TVECTEMP_TCC #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Construct from a vector. template TabVecTemp::TabVecTemp (const Vector& vec) { nrel_p = vec.nelements(); vecPtr_p = new Vector(vec); tag_p = TagTemp; } //# Construct a Vector. template TabVecTemp::TabVecTemp (rownr_t leng) { nrel_p = leng; vecPtr_p = new Vector(nrel_p); tag_p = TagTemp; } //# Destructor. template TabVecTemp::~TabVecTemp () { delete vecPtr_p; } //# Get or put a value. template T TabVecTemp::value (rownr_t i) const { return (*vecPtr_p)(i); } template void TabVecTemp::getVal (rownr_t i, T& val) const { val = (*vecPtr_p)(i); } template void TabVecTemp::putVal (rownr_t i, const T& val) { (*vecPtr_p)(i) = val; } template void TabVecTemp::set (const T& val) { vecPtr_p->set (val); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/TabPath.cc000066400000000000000000000045121476623553700176440ustar00rootroot00000000000000//# TabPath.cc: Search path for table files //# Copyright (C) 1993,1994,1995,1998 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include // for system call access namespace casacore { //# NAMESPACE CASACORE - BEGIN //# This is the implementation of the class TabPath. TabPath::TabPath() : tabDir_p (10) { tabDir_p[0] = "./"; tabDir_p[1] = "~/TabDir/"; nrDir_p = 2; } TabPath::TabPath (const String& dir) : tabDir_p (10) { tabDir_p[0] = dir; nrDir_p = 1; } TabPath::~TabPath () { ; } Bool TabPath::found (const String& name, String& dir) const { uInt dirnr; Bool sw = False; for (dirnr=0; dirnr //
      • indexError // const String& TabPath::dir (uInt dirnr) const { if (dirnr >= nrDir_p) { throw (indexError ((Int)dirnr, "TabPath")); } return tabDir_p[dirnr]; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/TabPath.h000066400000000000000000000050401476623553700175030ustar00rootroot00000000000000//# TabPath.h: Search path for table files //# Copyright (C) 1993,1994,1995 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABPATH_H #define TABLES_TABPATH_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Search path for table files // // // // // // // // TabPath is the class containing the search path for table files. // It is used by the TabDesc class to find the directory of a table // description. // // //
      • This class has to be replaced by a more general path class. // class TabPath { public: // Create a table file search path with a .COD{Directory} // Use default path ., ~/TabDir TabPath (); // Create a table file search path with given path name. TabPath (const String&); // Remove a table file search path. ~TabPath (); // Find a file in one of the directories. Bool found (const String&, String&) const; // Get the directory name. const String& dir (uInt dirnr) const; private: Block tabDir_p; // file directories uInt nrDir_p; // # directories }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/TabVecLogic.h000066400000000000000000000177301476623553700203130ustar00rootroot00000000000000//# TabVecLogic.h: Global functions for table vector logical operations //# Copyright (C) 1994,1995,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABVECLOGIC_H #define TABLES_TABVECLOGIC_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Comparison between two table vectors // // // // // // Element by element comparisons between the left and right table vectors. // The result is true only if the comparison is true for every element // of the table vectors. // At some point operators will be available that return masks where the // comparison is true. // The left and right operands must be conformant (i.e. have equal length). // // template inline Bool allLE (const TableVector& left, const TableVector& right); template inline Bool allLT (const TableVector& left, const TableVector& right); template inline Bool allGE (const TableVector& left, const TableVector& right); template inline Bool allGT (const TableVector& left, const TableVector& right); template inline Bool allEQ (const TableVector& left, const TableVector& right); template inline Bool allNE (const TableVector& left, const TableVector& right); // // // Comparison between a table vector and a scalar // // // // // // Element by element comparisons between a table vector and a scalar, // which behaves as if it were a conformant table vector filled with the // scalar value. // At some point operators will be available that return masks where the // comparison is true. // // template inline Bool allLE (const TableVector& left, const T& right); template inline Bool allLE (const T& left, const TableVector& right); template inline Bool allLT (const TableVector& left, const T& right); template inline Bool allLT (const T& left, const TableVector& right); template inline Bool allGE (const TableVector& left, const T& right); template inline Bool allGE (const T& left, const TableVector& right); template inline Bool allGT (const TableVector& left, const T& right); template inline Bool allGT (const T& left, const TableVector& right); template inline Bool allEQ (const TableVector& left, const T& right); template inline Bool allEQ (const T& left, const TableVector& right); template inline Bool allNE (const TableVector& left, const T& right); template inline Bool allNE (const T& left, const TableVector& right); // //# Implement all functions inline. //# The actual work is done in TVecLogic.cc. //# #define TABVECLOGICOPER(NAME) \ template inline \ Bool aips_name2(all,NAME) (const TableVector& l, \ const TableVector& r) \ { return aips_name2(tabVecReptv,NAME) (l.tabVec(), r.tabVec()); } \ template inline \ Bool aips_name2(all,NAME) (const T& val, const TableVector& tv) \ { return aips_name2(tabVecRepvall,NAME) (val, tv.tabVec()); } \ template inline \ Bool aips_name2(all,NAME) (const TableVector& tv, const T& val) \ { return aips_name2(tabVecRepvalr,NAME) (tv.tabVec(), val); } TABVECLOGICOPER(LE) TABVECLOGICOPER(LT) TABVECLOGICOPER(GE) TABVECLOGICOPER(GT) TABVECLOGICOPER(EQ) TABVECLOGICOPER(NE) // // Element by element comparisons between the "l" and "r" table vectors. The // result is true if the comparison is true for some element of the vectors. // At some point operators will be available that return masks where the // comparison is true. The vectors must conform or an exception is thrown. template inline Bool anyLE (const TableVector& l, const TableVector& r) { return (allGT (l, r) ? False : True); } template inline Bool anyLT (const TableVector& l, const TableVector& r) { return (allGE (l, r) ? False : True); } template inline Bool anyGE (const TableVector& l, const TableVector& r) { return (allLT (l, r) ? False : True); } template inline Bool anyGT (const TableVector& l, const TableVector& r) { return (allLE (l, r) ? False : True); } template inline Bool anyEQ (const TableVector& l, const TableVector& r) { return (allNE (l, r) ? False : True); } template inline Bool anyNE (const TableVector& l, const TableVector& r) { return (allEQ (l, r) ? False : True); } // // Element by element comparisons between a table vector and a scalar, which // behaves as if it were a conformant vector filled with the value "val." // The result is true if the comparison is true for some element of the vector. // At some point operators will be available that return masks where the // comparison is true. template inline Bool anyLE (const TableVector& tv, const T &val) { return (allGT (tv, val) ? False : True); } template inline Bool anyLE (const T &val, const TableVector& tv) { return (allGT (val, tv) ? False : True); } template inline Bool anyLT (const TableVector& tv, const T &val) { return (allGE (tv, val) ? False : True); } template inline Bool anyLT (const T &val, const TableVector& tv) { return (allGE (val, tv) ? False : True); } template inline Bool anyGE (const TableVector& tv, const T &val) { return (allLT (tv, val) ? False : True); } template inline Bool anyGE (const T &val, const TableVector& tv) { return (allLT (val, tv) ? False : True); } template inline Bool anyGT (const TableVector& tv, const T &val) { return (allLE (tv, val) ? False : True); } template inline Bool anyGT (const T &val, const TableVector& tv) { return (allLE (val, tv) ? False : True); } template inline Bool anyEQ (const TableVector& tv, const T &val) { return (allNE (tv, val) ? False : True); } template inline Bool anyEQ (const T &val, const TableVector& tv) { return (allNE (val, tv) ? False : True); } template inline Bool anyNE (const TableVector& tv, const T &val) { return (allEQ (tv, val) ? False : True); } template inline Bool anyNE (const T &val, const TableVector& tv) { return (allEQ (val, tv) ? False : True); } } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/tables/Tables/TabVecLogic.tcc000066400000000000000000000027501476623553700206310ustar00rootroot00000000000000//# TabVecLogic.cc: Global functions for table vector logical operations //# Copyright (C) 1994,1995 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABVECLOGIC_TCC #define TABLES_TABVECLOGIC_TCC #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# All functions are inlined, so there is no actual code. } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/TabVecMath.h000066400000000000000000000352541476623553700201500ustar00rootroot00000000000000//# TabVecMath.h: Global functions for table vector mathematics //# Copyright (C) 1994,1995,1996,1999,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABVECMATH_H #define TABLES_TABVECMATH_H //# Global functions similar to those defined in ArrayMath are defined for //# the table vectors. Furthermore vector functions like norm are defined. //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Basic math for table vectors. // // // // // // These global functions do the basic math for table vectors. // This means addition, subtraction, multiplication, division // and negation. // In case two table vectors are used, the left and right operand // must be conformant (i.e. have equal length). // // // Add 2 table vectors storing result in first one. template inline void operator+= (TableVector& left, const TableVector& right); // Subtract 2 table vectors storing result in first one. template inline void operator-= (TableVector& left, const TableVector& right); // Multiple 2 table vectors storing result in first one. template inline void operator*= (TableVector& left, const TableVector& right); // Divide 2 table vectors storing result in first one. template inline void operator/= (TableVector& left, const TableVector& right); // Add a scalar to each element in the table vector. template inline void operator+= (TableVector& left, const T& right); // Subtract a scalar from each element in the table vector. template inline void operator-= (TableVector& left, const T& right); // Multiple each element in the table vector with a scalar. template inline void operator*= (TableVector& left, const T& right); // Divide each element in the table vector by a scalar. template inline void operator/= (TableVector& left, const T& right); // Unary plus. template inline TableVector operator+ (const TableVector&); // Unary minus. template inline TableVector operator- (const TableVector&); // Add 2 table vectors storing result in a new one. template inline TableVector operator+ (const TableVector& left, const TableVector& right); // Subtract 2 table vectors storing result in a new one. template inline TableVector operator- (const TableVector& left, const TableVector& right); // Multiple 2 table vectors storing result in a new one. template inline TableVector operator* (const TableVector& left, const TableVector& right); // Divide 2 table vectors storing result in a new one. template inline TableVector operator/ (const TableVector& left, const TableVector& right); // Add a scalar to each element in the table vector storing result // in a new table vector. template inline TableVector operator+ (const TableVector& left, const T& right); // Subtract a scalar from each element in the table vector storing result // in a new table vector. template inline TableVector operator- (const TableVector& left, const T& right); // Multiple each element in the table vector with a scalar storing result // in a new table vector. template inline TableVector operator* (const TableVector& left, const T& right); // Divide each element in the table vector by a scalar storing result // in a new table vector. template inline TableVector operator/ (const TableVector& left, const T& right); // Add a scalar to each element in the table vector storing result // in a new table vector. template inline TableVector operator+ (const T& left, const TableVector& right); // Subtract a scalar from each element in the table vector storing result // in a new table vector. template inline TableVector operator- (const T& left, const TableVector& right); // Multiple each element in the table vector with a scalar storing result // in a new table vector. template inline TableVector operator* (const T& left, const TableVector& right); // Divide each element in the table vector by a scalar storing result // in a new table vector. template inline TableVector operator/ (const T& left, const TableVector& right); // // // Transcendental math for table vectors. // // // // // // These global functions do the transcendental math for table vectors // for essentially all numeric types. // The functions are sin, sinh, exp, log, pow, etc.. // In case two table vectors are used, the left and right operand // must be conformant (i.e. have equal length). // // template inline TableVector cos (const TableVector&); template inline TableVector cosh (const TableVector&); template inline TableVector exp (const TableVector&); template inline TableVector log (const TableVector&); template inline TableVector log10(const TableVector&); template inline TableVector pow (const TableVector& value, const TableVector& exponent); template inline TableVector sin (const TableVector&); template inline TableVector sinh (const TableVector&); template inline TableVector sqrt (const TableVector&); // // // Further transcendental math for table vectors. // // // // // // These global functions do the transcendental math for table vectors // for a limited set of numeric types. // The functions are asin, ceil, etc.. // In case two table vectors are used, the left and right operand // must be conformant (i.e. have equal length). // // template inline TableVector acos (const TableVector&); template inline TableVector asin (const TableVector&); template inline TableVector atan (const TableVector&); template inline TableVector atan2(const TableVector& y, const TableVector& x); template inline TableVector ceil (const TableVector&); template inline TableVector fabs (const TableVector&); template inline TableVector floor(const TableVector&); template inline TableVector fmod (const TableVector& value, const TableVector& modulo); template inline TableVector pow (const TableVector& value, const double& exponent); template inline TableVector tan (const TableVector&); template inline TableVector tanh (const TableVector&); // // // Miscellaneous table vector operations. // // // // // // Fill a table vector or calculate the sum, product, minimum or // maximum of its elements. // // // This sets min and max to the min and max of the vector to avoid having // to do two passes with max() and min() separately. // Requires that the type "T" has comparison operators. template inline void minMax (T& min, T& max, const TableVector&); // The minimum element of the table vector. // Requires that the type "T" has comparison operators. template inline T min (const TableVector&); // The maximum element of the table vector. // Requires that the type "T" has comparison operators. template inline T max (const TableVector&); // Fills all elements of the table vector with a sequence starting with // "start" and incrementing by "inc" for each element. template inline void indgen (TableVector&, T start, T inc); // Fills all elements of the table vector with a sequence starting with // "start" incremented by one for each position in the table vector. template inline void indgen (TableVector&, T start); // Fills all elements of the table vector with a sequence starting with // 0 and ending with nelements() - 1. template inline void indgen (TableVector&); // Sum of all the elements of a table vector. template inline T sum (const TableVector&); // Product of all the elements of a table vector. // // product can easily overflow. // template inline T product (const TableVector&); // // // Vector operations on a table vector. // // // // // // Do vector operations on a table vector (like inner product). // // // The inner product of 2 table vectors. // The left and right operands must be conformant (i.e. have equal length). template inline T innerProduct (const TableVector& left, const TableVector& right); // The norm of a table vector. template inline T norm (const TableVector&); // The cross product of 2 table vectors containing 3 elements. template inline TableVector crossProduct (const TableVector& left, const TableVector& right); // //# Inline all these functions. //# The actual work is done by functions (tabVecRep...) operating on TabVecRep. //# Because the preprocessor of gcc-3 gives warnings when using the macro as //# e.g. TABVECMATHOPER(add,+,+=), the r is removed from the function name and //# put befroe the + in the macro call. #define TABVECMATHOPER(NAME,OP,OPA) \ template inline \ TableVector aips_name2(operato,OP) (const TableVector& tv, \ const T& v) \ { return TableVector (aips_name2(tabVecRepvalr,NAME) (tv.tabVec(), \ v)); } \ template inline \ TableVector aips_name2(operato,OP) (const T& v, \ const TableVector& tv) \ { return TableVector (aips_name2(tabVecRepvall,NAME) (v, \ tv.tabVec())); } \ template inline \ TableVector aips_name2(operato,OP) (const TableVector& l, \ const TableVector& r) \ { return TableVector (aips_name2(tabVecReptv,NAME) (l.tabVec(), \ r.tabVec())); } \ template inline \ void aips_name2(operato,OPA) (TableVector& tv, const T& v) \ { aips_name2(tabVecRepvalass,NAME) (tv.tabVec(), v); } \ template inline \ void aips_name2(operato,OPA) (TableVector& l, \ const TableVector& r) \ { aips_name2(tabVecReptvass,NAME) (l.tabVec(), r.tabVec()); } TABVECMATHOPER(add,r+,r+=) TABVECMATHOPER(sub,r-,r-=) TABVECMATHOPER(tim,r*,r*=) TABVECMATHOPER(div,r/,r/=) #define TABVECMATHFUNC(NAME) \ template inline \ TableVector NAME (const TableVector& tv) \ { return TableVector (aips_name2(tabVecRep,NAME) (tv.tabVec())); } #define TABVECMATHFUNC2(NAME) \ template inline \ TableVector NAME (const TableVector& l, \ const TableVector& r) \ { return TableVector (aips_name2(tabVecRep,NAME) (l.tabVec(), \ r.tabVec())); } TABVECMATHFUNC (cos) TABVECMATHFUNC (cosh) TABVECMATHFUNC (exp) TABVECMATHFUNC (log) TABVECMATHFUNC (log10) TABVECMATHFUNC2(pow) TABVECMATHFUNC (sin) TABVECMATHFUNC (sinh) TABVECMATHFUNC (sqrt) TABVECMATHFUNC (acos) TABVECMATHFUNC (asin) TABVECMATHFUNC (atan) TABVECMATHFUNC2(atan2) TABVECMATHFUNC (ceil) TABVECMATHFUNC (fabs) TABVECMATHFUNC (floor) TABVECMATHFUNC2(fmod) TABVECMATHFUNC (tan) TABVECMATHFUNC (tanh) template inline TableVector pow (const TableVector& tv, const double& exp) { return TableVector (tabVecReppowd (tv.tabVec(), exp)); } template inline T sum (const TableVector& tv) { return tabVecRepsum (tv.tabVec()); } template inline T product (const TableVector& tv) { return tabVecRepproduct (tv.tabVec()); } template inline void minMax (T& min, T& max, const TableVector& tv) { tabVecRepminmax (min, max, tv.tabVec()); } template inline T min (const TableVector& tv) { T Min,Max; tabVecRepminmax (Min, Max, tv.tabVec()); return Min; } template inline T max (const TableVector& tv) { T Min,Max; tabVecRepminmax (Min, Max, tv.tabVec()); return Max; } template inline void indgen (TableVector& tv, T start, T inc) { tabVecRepindgen (tv.tabVec(), start, inc); } template inline void indgen (TableVector& tv, T start) { tabVecRepindgen (tv.tabVec(), start, T(1)); } template inline void indgen (TableVector& tv) { tabVecRepindgen (tv.tabVec(), T(0), T(1)); } template inline T innerProduct (const TableVector& l, const TableVector& r) { return tabVecRepinnerproduct (l.tabVec(), r.tabVec()); } template inline T norm (const TableVector& tv) { return tabVecRepnorm (tv.tabVec()); } template inline TableVector crossProduct (const TableVector& l, const TableVector& r) { return TableVector (tabVecRepcrossproduct (l.tabVec(), r.tabVec())); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/TabVecMath.tcc000066400000000000000000000027351476623553700204700ustar00rootroot00000000000000//# TabVecMath.cc: Global functions for table vector mathematics //# Copyright (C) 1994,1995 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABVECMATH_TCC #define TABLES_TABVECMATH_TCC #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# All functions are inlined, so there is no actual code. } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/Table.cc000066400000000000000000000761111476623553700173540ustar00rootroot00000000000000//# Table.cc: Main interface class to table data //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Table::ScratchCallback* Table::scratchCallback_p = 0; Table::ScratchCallback* Table::setScratchCallback (Table::ScratchCallback* fptr) { Table::ScratchCallback* cur = scratchCallback_p; scratchCallback_p = fptr; return cur; } Table::Table() : baseTabPtr_p (0), lastModCounter_p (0) { countedTabPtr_p = std::make_shared(); baseTabPtr_p = countedTabPtr_p.get(); } Table::Table (const String& name, TableOption option, const TSMOption& tsmOpt) : baseTabPtr_p (0), lastModCounter_p (0) { open (name, "", option, TableLock(), tsmOpt); } Table::Table (const String& name, const TableLock& lockOptions, TableOption option, const TSMOption& tsmOpt) : baseTabPtr_p (0), lastModCounter_p (0) { open (name, "", option, lockOptions, tsmOpt); } Table::Table (const String& name, const String& type, TableOption option, const TSMOption& tsmOpt) : baseTabPtr_p (0), lastModCounter_p (0) { open (name, type, option, TableLock(), tsmOpt); } Table::Table (const String& name, const String& type, const TableLock& lockOptions, TableOption option, const TSMOption& tsmOpt) : baseTabPtr_p (0), lastModCounter_p (0) { open (name, type, option, lockOptions, tsmOpt); } Table::Table (Table::TableType type, Table::EndianFormat endianFormat, const TSMOption& tsmOpt) : baseTabPtr_p (0), lastModCounter_p (0) { SetupNewTable newtab("", TableDesc(), Table::Scratch); BaseTable* ptr; if (type == Table::Memory) { ptr = new MemoryTable (newtab, 0, False); } else { ptr = new PlainTable (newtab, 0, False, TableLock(), endianFormat, tsmOpt); } initBasePtr (ptr); } Table::Table (SetupNewTable& newtab, rownr_t nrrow, Bool initialize, Table::EndianFormat endianFormat, const TSMOption& tsmOpt) : baseTabPtr_p (0), lastModCounter_p (0) { initBasePtr (new PlainTable (newtab, nrrow, initialize, TableLock(), endianFormat, tsmOpt)); } Table::Table (SetupNewTable& newtab, Table::TableType type, rownr_t nrrow, Bool initialize, Table::EndianFormat endianFormat, const TSMOption& tsmOpt) : baseTabPtr_p (0), lastModCounter_p (0) { BaseTable* ptr; if (type == Table::Memory) { ptr = new MemoryTable (newtab, nrrow, initialize); } else { ptr = new PlainTable (newtab, nrrow, initialize, TableLock(), endianFormat, tsmOpt); } initBasePtr (ptr); } Table::Table (SetupNewTable& newtab, Table::TableType type, const TableLock& lockOptions, rownr_t nrrow, Bool initialize, Table::EndianFormat endianFormat, const TSMOption& tsmOpt) : baseTabPtr_p (0), lastModCounter_p (0) { BaseTable* ptr; if (type == Table::Memory) { ptr = new MemoryTable (newtab, nrrow, initialize); } else { ptr = new PlainTable (newtab, nrrow, initialize, lockOptions, endianFormat, tsmOpt); } initBasePtr (ptr); } Table::Table (SetupNewTable& newtab, TableLock::LockOption lockOption, rownr_t nrrow, Bool initialize, Table::EndianFormat endianFormat, const TSMOption& tsmOpt) : baseTabPtr_p (0), lastModCounter_p (0) { initBasePtr (new PlainTable (newtab, nrrow, initialize, TableLock(lockOption), endianFormat, tsmOpt)); } Table::Table (SetupNewTable& newtab, const TableLock& lockOptions, rownr_t nrrow, Bool initialize, Table::EndianFormat endianFormat, const TSMOption& tsmOpt) : baseTabPtr_p (0), lastModCounter_p (0) { initBasePtr (new PlainTable (newtab, nrrow, initialize, lockOptions, endianFormat, tsmOpt)); } #ifdef HAVE_MPI Table::Table (MPI_Comm mpiComm, Table::TableType type, Table::EndianFormat endianFormat, const TSMOption& tsmOpt) : baseTabPtr_p (0), lastModCounter_p (0) { SetupNewTable newtab("", TableDesc(), Table::Scratch); BaseTable* ptr; if (type == Table::Memory) { ptr = new MemoryTable (newtab, 0, False); } else { ptr = new PlainTable (mpiComm, newtab, 0, False, TableLock(), endianFormat, tsmOpt); } initBasePtr (ptr); } Table::Table (MPI_Comm mpiComm, SetupNewTable& newtab, rownr_t nrrow, Bool initialize, Table::EndianFormat endianFormat, const TSMOption& tsmOpt) : baseTabPtr_p (0), lastModCounter_p (0) { initBasePtr (new PlainTable (mpiComm, newtab, nrrow, initialize, TableLock(), endianFormat, tsmOpt)); } Table::Table (MPI_Comm mpiComm, SetupNewTable& newtab, Table::TableType type, rownr_t nrrow, Bool initialize, Table::EndianFormat endianFormat, const TSMOption& tsmOpt) : baseTabPtr_p (0), lastModCounter_p (0) { BaseTable* ptr; if (type == Table::Memory) { ptr = new MemoryTable (newtab, nrrow, initialize); } else { ptr = new PlainTable (mpiComm, newtab, nrrow, initialize, TableLock(), endianFormat, tsmOpt); } initBasePtr (ptr); } Table::Table (MPI_Comm mpiComm, SetupNewTable& newtab, Table::TableType type, const TableLock& lockOptions, rownr_t nrrow, Bool initialize, Table::EndianFormat endianFormat, const TSMOption& tsmOpt) : baseTabPtr_p (0), lastModCounter_p (0) { BaseTable* ptr; if (type == Table::Memory) { ptr = new MemoryTable (newtab, nrrow, initialize); } else { ptr = new PlainTable (mpiComm, newtab, nrrow, initialize, lockOptions, endianFormat, tsmOpt); } initBasePtr (ptr); } Table::Table (MPI_Comm mpiComm, SetupNewTable& newtab, TableLock::LockOption lockOption, rownr_t nrrow, Bool initialize, Table::EndianFormat endianFormat, const TSMOption& tsmOpt) : baseTabPtr_p (0), lastModCounter_p (0) { initBasePtr (new PlainTable (mpiComm, newtab, nrrow, initialize, TableLock(lockOption), endianFormat, tsmOpt)); } Table::Table (MPI_Comm mpiComm, SetupNewTable& newtab, const TableLock& lockOptions, rownr_t nrrow, Bool initialize, Table::EndianFormat endianFormat, const TSMOption& tsmOpt) : baseTabPtr_p (0), lastModCounter_p (0) { initBasePtr (new PlainTable (mpiComm, newtab, nrrow, initialize, lockOptions, endianFormat, tsmOpt)); } #endif Table::Table (const Block
      • & tables, const Block& subTables, const String& subDirName) : baseTabPtr_p (0), lastModCounter_p (0) { initBasePtr (new ConcatTable (tables, subTables, subDirName)); } Table::Table (const Block& tableNames, const Block& subTables, TableOption option, const TSMOption& tsmOpt, const String& subDirName) : baseTabPtr_p (0), lastModCounter_p (0) { initBasePtr (new ConcatTable (tableNames, subTables, subDirName, option, TableLock(), tsmOpt)); } Table::Table (const Block& tableNames, const Block& subTables, const TableLock& lockOptions, TableOption option, const TSMOption& tsmOpt) : baseTabPtr_p (0), lastModCounter_p (0) { initBasePtr (new ConcatTable (tableNames, subTables, String(), option, lockOptions, tsmOpt)); } Table::Table (BaseTable* btp) : baseTabPtr_p (btp), lastModCounter_p (0) {} Table::Table (const std::shared_ptr& shptr) : baseTabPtr_p (0), lastModCounter_p (0) { baseTabPtr_p = shptr.get(); AlwaysAssert (baseTabPtr_p, AipsError); countedTabPtr_p = shptr; } Table::Table (const Table& that) : countedTabPtr_p (that.countedTabPtr_p), baseTabPtr_p (that.baseTabPtr_p), lastModCounter_p (that.lastModCounter_p) {} Table::~Table() { if (countedTabPtr_p) { #ifdef CASACORE_UNLOCK_TABLE_ON_DESTRUCT unlock(); #endif } } Table& Table::operator= (const Table& that) { countedTabPtr_p = that.countedTabPtr_p; baseTabPtr_p = that.baseTabPtr_p; lastModCounter_p = that.lastModCounter_p; return *this; } void Table::initBasePtr (BaseTable* ptr) { AlwaysAssert (!baseTabPtr_p, AipsError); baseTabPtr_p = ptr; countedTabPtr_p.reset (baseTabPtr_p); } Block Table::getPartNames (Bool recursive) const { Block names; baseTabPtr_p->getPartNames (names, recursive); return names; } void Table::closeSubTables() const { return keywordSet().closeTables(); } Vector Table::nonWritableFiles (const String& tableName) { String tabName = Path(tableName).absoluteName(); if (! isReadable (tabName)) { throw (TableError ("Table::nonWritableFiles: Table " + tabName + " does not exist")); } uInt n=0; Vector names; DirectoryIterator iter(tabName); while (! iter.pastEnd()) { if (! iter.file().isWritable()) { names.resize (n+1, True); names(n++) = iter.name(); } iter++; } return names; } Table::EndianFormat Table::endianFormat() const { return baseTabPtr_p->asBigEndian() ? Table::BigEndian : Table::LittleEndian; } Bool Table::isNativeDataType (DataType dtype) { return StManColumnBase::isNativeDataType (dtype); } void Table::copy (const String& newName, TableOption option, Bool noRows) const { if (noRows) { baseTabPtr_p->deepCopy (newName, Record(), StorageOption(), option, False, AipsrcEndian, noRows); } else { baseTabPtr_p->copy (newName, option); } } void Table::deepCopy (const String& newName, TableOption option, Bool valueCopy, EndianFormat endianFormat, Bool noRows) const { baseTabPtr_p->deepCopy (newName, Record(), StorageOption(), option, valueCopy, endianFormat, noRows); } Table Table::copyToMemoryTable (const String& newName, Bool noRows) const { Table newtab = TableCopy::makeEmptyMemoryTable (newName, *this, noRows); if (!noRows) { TableCopy::copyRows (newtab, *this); } TableCopy::copyInfo (newtab, *this); TableCopy::copySubTables (newtab, *this, noRows); return newtab; } //# Open the table file and read it in if necessary. void Table::open (const String& name, const String& type, int tableOption, const TableLock& lockOptions, const TSMOption& tsmOpt) { //# Option Delete is effectively the same as Old followed by a //# markForDelete. Bool deleteOpt = False; if (tableOption == Table::Delete) { tableOption = Table::Old; deleteOpt = True; } // Make name absolute in case a chdir is done in e.g. Python. String absName = Path(name).absoluteName(); //# Look if the table is already in the cache. //# If so, link to it. BaseTable* btp = lookCache (absName, tableOption, lockOptions); if (btp != 0) { countedTabPtr_p = btp->shared_from_this(); }else{ //# Check if the table directory exists. File dir(absName); if (!dir.exists()) { throw TableNoFile(absName); } if (!dir.isDirectory()) { throw TableNoDir(absName); } //# Check if the table.dat file exists. String desc = Table::fileName(absName); File file (desc); if (!file.exists()) { throw TableNoDatFile(desc); } //# Read the file type and verify that it is a table AipsIO ios (desc); String t = ios.getNextType(); if (t != "Table") { throw TableInvType(absName, "Table", t); } //# Check if the table exists. if (! Table::isReadable (absName)) { throw (TableNoFile (absName)); } // Create the BaseTable object and add a PlainTable to the cache. countedTabPtr_p = makeBaseTable (absName, type, tableOption, lockOptions, tsmOpt, True, 0); } baseTabPtr_p = countedTabPtr_p.get(); if (deleteOpt) { markForDelete(); } } // NOTE: When changing this function because of new Table versions, also change // TableUtil::getLayout !!!!! std::shared_ptr Table::makeBaseTable (const String& name, const String& type, int tableOption, const TableLock& lockOptions, const TSMOption& tsmOpt, Bool addToCache, uInt locknr) { std::shared_ptr baseTabPtr; //# Determine the file option for the table. //# Only existing tables can be opened. //# This is guaranteed by the calling functions. ByteIO::OpenOption fopt = PlainTable::toAipsIOFoption (tableOption); //# Open the file. AipsIO ios (Table::fileName(name), fopt); //# Determine the kind of table by reading the type. String tp; uInt version = ios.getstart ("Table"); if (version > 3) { throw TableError ("Table version " + String::toString(version) + " not supported by this version of Casacore"); } uInt format; rownr_t nrrow; if (version > 2) { ios >> nrrow; } else { uInt n; ios >> n; nrrow = n; } ios >> format; ios >> tp; if (tp == "PlainTable") { baseTabPtr = std::make_shared(ios, version, name, type, nrrow, tableOption, lockOptions, tsmOpt, addToCache, locknr); } else if (tp == "RefTable") { baseTabPtr = std::make_shared(ios, name, nrrow, tableOption, lockOptions, tsmOpt); } else if (tp == "ConcatTable") { baseTabPtr = std::make_shared(ios, name, nrrow, tableOption, lockOptions, tsmOpt); } else { throw (TableInternalError("Table::open: unknown table kind " + tp)); } return baseTabPtr; } BaseTable* Table::lookCache (const String& name, int tableOption, const TableLock& lockOptions) { return PlainTable::tableCache().lookCache (name, tableOption, lockOptions); } void Table::throwIfNull() const { if (isNull()) { throw (TableInvOper ("Table is null")); } } Bool Table::isOpened (const String& tableName) { return (PlainTable::tableCache()(Path(tableName).absoluteName()) != 0); } // Check if the table data has changed. Bool Table::hasDataChanged() { // If the table is not read locked try to get one (without waiting). // If not succeeding, another process is writing, thus data is changing. // Otherwise unlock immediately. if (! hasLock (FileLocker::Read)) { if (! lock (FileLocker::Read, 1)) { return True; } unlock(); } // Get the modify counter. If different, data have changed. uInt counter = baseTabPtr_p->getModifyCounter(); if (counter != lastModCounter_p) { lastModCounter_p = counter; return True; } return False; } uInt Table::nAutoLocks() { return PlainTable::tableCache().nAutoLocks(); } void Table::relinquishAutoLocks (Bool all) { PlainTable::tableCache().relinquishAutoLocks (all); } Vector Table::getLockedTables (FileLocker::LockType lockType, int lockOption) { return PlainTable::tableCache().getLockedTables (lockType, lockOption); } TableRecord& Table::rwKeywordSet() { if (! isWritable()) { throw (TableError ("Table::rwKeywordSet cannot be used: table " + tableName() + " is not writable")); } return baseTabPtr_p->rwKeywordSet(); } Bool Table::canRemoveColumn (const String& columnName) const { return baseTabPtr_p->canRemoveColumn (Vector(1, columnName)); } void Table::removeColumn (const String& columnName) { baseTabPtr_p->removeColumn (Vector(1, columnName)); } RowNumbers Table::rowNumbers () const { return baseTabPtr_p->rowNumbers(); } RowNumbers Table::rowNumbers (const Table& that, Bool tryFast) const { Vector thisRows(rowNumbers()); const rownr_t highValue = std::numeric_limits::max(); // If that is the root table of this, we can simply use rowNumbers(). // The same is true if empty. if (that.baseTabPtr_p == baseTabPtr_p->root() || nrow() == 0) { return thisRows; } // Get the rowNumbers of that. Vector thatRows(that.rowNumbers()); // Try if a fast conversion can be done. // That is the case if this is not a superset of that and if orders match. if (tryFast) { Vector outRows; if (fastRowNumbers (thisRows, thatRows, outRows)) { return outRows; } } // Alas, we have to do it the hard way. // Transform the rowNumbers of that to a vector // mapping rownr in root to rownr in that. rownr_t nrthat = thatRows.nelements(); rownr_t maxv = nrthat; Vector rownrs(thatRows); // That mapping only needs to be done if that is not a root table. // Non-used rownrs are initialized with a very high value. if (! that.isRootTable()) { maxv = max(thatRows); Vector tmp(maxv+1, highValue); rownrs.reference (tmp); } Bool deleteIt; rownr_t* rownrsData = rownrs.getStorage (deleteIt); // Now make the mapping. // thatRows is not needed anymore, so resize at the end to reclaim memory. if (! that.isRootTable()) { Bool deleteThat; const rownr_t* thatRowData = thatRows.getStorage (deleteThat); for (rownr_t i=0; i maxv) { thisRowData[i] = highValue; } else { thisRowData[i] = rownrsData[thisRowData[i]]; } } thisRows.putStorage (thisRowData, deleteThis); // rownrsData is not used, so don't need to be put. // freeStorage requires const pointer though. const rownr_t *dummy(rownrsData); rownrs.freeStorage (dummy, deleteIt); return thisRows; } Bool Table::fastRowNumbers (const Vector& v1, const Vector& v2, Vector& rows) const { // v1 cannot be a superset of v2. if (v1.size() > v2.size()) { return False; } rows.resize (v1.size()); if (v1.empty()) { return True; } Bool d1,d2,d3; const rownr_t* r1 = v1.getStorage (d1); const rownr_t* r2 = v2.getStorage (d2); rownr_t* routc = rows.getStorage (d3); rownr_t* rout = routc; rownr_t i1=0; rownr_t i2=0; Bool ok = True; while (ok) { if (r1[i1] == r2[i2]) { *rout++ = i2; if (++i1 >= v1.size()) { break; } } if (++i2 >= v2.size()) { ok = False; } } v1.freeStorage (r1, d1); v2.freeStorage (r2, d2); rows.putStorage (routc, d3); return ok; } //# Sort on a single column. //# This is converted to a sort on a vector of column names. Table Table::sort (const String& name, int order, int option) const { //# Turn the name argument into a block. return sort (Block(1, name), order, option); } //# Sort on multiple columns, where a global order is given. //# This is converted to a sort with mixed orders. Table Table::sort (const Block& names, int order, int option) const { //# Expand the order argument into a block. return sort (names, Block(names.nelements(), order), option); } //# Sort on multiple columns and orders. Table Table::sort (const Block& names, const Block& orders, int option) const { //# Insert a block with null compare objects. return sort (names, Block>(names.nelements()), orders, option); } //# Sort on multiple columns and orders with given functions. Table Table::sort (const Block& names, const Block>& cmpObjs, const Block& orders, int option) const { return Table(baseTabPtr_p->sort (names, cmpObjs, orders, option)); } //# Create an expression node to handle a keyword. //# The code to handle this is in TableExprNode, because there the //# differentation between data types is being made. TableExprNode Table::key (const String& keywordName) const { Vector names(1); names(0) = keywordName; return TableExprNode::newKeyConst (keywordSet(), names); } TableExprNode Table::key (const Vector& fieldNames) const { return TableExprNode::newKeyConst (keywordSet(), fieldNames); } TableExprNode Table::col (const String& columnName) const { Vector fieldNames; return TableExprNode::newColumnNode (TableExprInfo(*this), columnName, fieldNames); } TableExprNode Table::col (const String& columnName, const Vector& fieldNames) const { return TableExprNode::newColumnNode (TableExprInfo(*this), columnName, fieldNames); } TableExprNode Table::nodeRownr (rownr_t origin) const { return TableExprNode::newRownrNode (TableExprInfo(*this), origin); } TableExprNode Table::nodeRandom() const { return TableExprNode::newRandomNode (TableExprInfo(*this)); } //# Select rows based on an expression. Table Table::operator() (const TableExprNode& expr, rownr_t maxRow, rownr_t offset) const { return Table (baseTabPtr_p->select (expr, maxRow, offset)); } //# Select rows based on row numbers. Table Table::operator() (const RowNumbers& rownrs) const { return Table (baseTabPtr_p->select (rownrs)); } //# Select rows based on a mask. Table Table::operator() (const Block& mask) const { return Table (baseTabPtr_p->select (mask)); } //# Select columns. Table Table::project (const Block& names) const { return Table (baseTabPtr_p->project (names)); } //# Combine tables. Table Table::operator& (const Table& that) const { return Table(baseTabPtr_p->tabAnd (that.baseTabPtr_p)); } Table Table::operator| (const Table& that) const { return Table(baseTabPtr_p->tabOr (that.baseTabPtr_p)); } Table Table::operator- (const Table& that) const { return Table(baseTabPtr_p->tabSub (that.baseTabPtr_p)); } Table Table::operator^ (const Table& that) const { return Table(baseTabPtr_p->tabXor (that.baseTabPtr_p)); } Table Table::operator! () const { return Table(baseTabPtr_p->tabNot()); } //# Test if table exists and is readable. Bool Table::isReadable (const String& tableName, Bool throwIf) { String tabName = Path(tableName).absoluteName(); // First see if it is in the table cache. By doing so a new table // does not need to exist on disk yet. if (PlainTable::tableCache()(tabName)) { return True; } //# Check if the table directory exists. File dir(tabName); if (!dir.exists()) { if (throwIf) { throw TableNoFile(tabName); } return False; } if (!dir.isDirectory()) { if (throwIf) { throw TableNoDir(tabName); } return False; } //# Test if the table.dat file exists. String datFile = Table::fileName(tabName); File file (datFile); if (!file.exists()) { if (throwIf) { throw TableNoDatFile(tabName); } return False; } //# Open the table file and get its type. //# An exception might be thrown, but chances are very low. AipsIO ios (Table::fileName(tabName)); Bool valid = True; try { if (ios.getNextType() != "Table") { if (throwIf) { throw TableInvType(tabName, "Table", tabName); } valid = False; } } catch (std::exception& x) { if (throwIf) { throw; } valid = False; } return valid; } //# Test if table exists and is writable. Bool Table::isWritable (const String& tableName, Bool throwIf) { String tabName = Path(tableName).absoluteName(); if (! isReadable (tabName, throwIf)) { return False; } File file (Table::fileName(tabName)); Bool wb = file.isWritable(); if (throwIf && !wb) { throw TableError("Table " + tableName + " is not writable"); } return wb; } TableDesc Table::actualTableDesc() const { return baseTabPtr_p->actualTableDesc(); } Record Table::dataManagerInfo() const { return baseTabPtr_p->dataManagerInfo(); } //# Make the table file name. String Table::fileName (const String& tableName) { return tableName + "/table.dat"; } //# Write a table to AipsIO (for TypedKeywords
        ). AipsIO& operator<< (AipsIO& ios, const Table& tab) { ios << tab.tableName(); return ios; } //# Read a table from AipsIO (for TypedKeywords
        ). //#// There are 2 things to be done: //#// 1. Now the table is opened as Update if it is writable. //#// It's better to do that if the Table is opened as Update. //#// 2. Only read in the table when needed (i.e. when a get for //#// the table is done). AipsIO& operator>> (AipsIO& ios, Table& tab) { tab.getTableKeyword (ios, True); return ios; } void Table::getTableKeyword (AipsIO& ios, Bool openWritable) { String name; ios >> name; TableOption opt = Table::Old; if (openWritable && Table::isWritable (name)) { opt = Table::Update; } Table table(name, opt); operator= (table); } //# Write a table to ostream (for TypedKeywords
        ). ostream& operator<< (ostream& ios, const Table& tab) { ios << "Table "; ios << tab.tableName(); ios << " ("; ios << tab.tableDesc().ncolumn() << " columns, "; ios << uInt(tab.nrow()) << " rows)"; ios << endl; return ios; } void Table::showKeywords (ostream& ios, Bool showSubTables, Bool showTabKey, Bool showColKey, Int maxVal) const { if (showTabKey || showColKey) { // Show table and/or column keywords. ios << endl << "Keywords of main table " << endl << "----------------------" << endl; showKeywordSets (ios, showTabKey, showColKey, maxVal); if (showSubTables) { // Also show them in the subtables. TableRecord keyset (keywordSet()); for (uInt i=0; i 0) { ios << " Table Keywords" << endl; keywordSet().print (ios, maxVal, " "); ios << endl; shown = True; } } if (showColKey) { Vector colNames (tableDesc().columnNames()); for (uInt i=0; i 0) { ios << " Column " << colNames[i] << endl; keys.print (ios, maxVal, " "); ios << endl; shown = True; } } } if (!shown) { ios << endl; } } // Deprecated functions; now in TableUtil.h. Table Table::openTable (const String& tableName, TableOption tabOpt, const TSMOption& tsmOpt) { return TableUtil::openTable (tableName, tabOpt, tsmOpt); } Table Table::openTable (const String& tableName, const TableLock& lockOptions, TableOption tabOpt, const TSMOption& tsmOpt) { return TableUtil::openTable (tableName, lockOptions, tabOpt, tsmOpt); } Bool Table::canDeleteTable (const String& tableName, Bool checkSubTables) { return TableUtil::canDeleteTable (tableName, checkSubTables); } Bool Table::canDeleteTable (String& message, const String& tableName, Bool checkSubTables) { return TableUtil::canDeleteTable (message, tableName, checkSubTables); } void Table::deleteTable (const String& tableName, Bool checkSubTables) { TableUtil::deleteTable (tableName, checkSubTables); } rownr_t Table::getLayout (TableDesc& desc, const String& tableName) { return TableUtil::getLayout (desc, tableName); } TableInfo Table::tableInfo (const String& tableName) { return TableUtil::tableInfo (tableName); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/Table.h000066400000000000000000001536161476623553700172240ustar00rootroot00000000000000//# Table.h: Main interface classes to tables //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have receied a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABLE_H #define TABLES_TABLE_H //# Includes #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_MPI #include #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class SetupNewTable; class TableDesc; class ColumnDesc; class TableRecord; class Record; class TableExprNode; class DataManager; class IPosition; class TableExprInfo; template class Block; // // Main interface class to a read/write table // // // // // //# Classes you should understand before using this one. //
      • SetupNewTable //
      • TableDesc //
      • TableColumn //
      • ScalarColumn //
      • ArrayColum //
      • TableLock // // // Class Table can be used to create a new table or to access an existing // table in read/write or readonly mode. // // To access the data in a Table, objects have to be created // to access the columns. These objects are TableColumn, // ScalarColumn and ArrayColumn, which can be created // via their constructors. // Furthermore the Table has a TableRecord object for holding keywords // which can be read or written using the appropriate functions. //
        The Table class structure is shown in this // UML diagram. // // To open an existing table, a simple Table constructor can be used. // The possible construct options are: //
          //
        • Old readonly table (default option) //
        • Update update existing table //
        • Delete delete table //
        // // Creating a new table requires more work, because columns have // to be bound to storage managers or virtual column engines. // Class SetupNewTable is needed for this purpose. The Tables module // documentation explains in more detail how to create a table. // When creating a table, it can be specified which endian format to use. // By default it uses the format specified in the aipsrc variable // table.endianformat which defaults to // Table::LocalEndian (thus the endian format of the // machine being used). // // Note that TableUtil contains convenience function to open, create or delete a table. // They make it possible to use the :: notation to denote subtables. //

        // It is possible to create a Table object as the virtual concatenation of // Tables having identical table descriptions. Subtables of those tables // can optionally be concatenated as well. // E.g. if a MeasurementSet is partioned in time, this mechanism makes it // possible to view it as a single table. Furthermore, a subtable like // SYSCAL can be concatenated as well, while the other subtables are identical // in all partitions and are taken from the first table only. // // Other Table objects can be created from a Table using // the select, project and sort functions. The result in so-called // reference tables. In this way a subset of a table can be created and // can be read/written in the same way as a normal Table. Writing has the // effect that the underlying table gets written. // // // // // Open a table to be updated. // Table myTable ("theTable", Table::Update); // // Write the column containing the scalar RA. // ScalarColumn raColumn(myTable, "RA"); // rownr_t nrrow = myTable.nrow(); // for (rownr_t i=0; i // // // Table is the envelope for the underlying counted referenced // classes derived from BaseTable. In this way no pointers have // to be used to get polymorphism. // // //# A List of bugs, limitations, extensions or planned refinements. //

      • add, remove, rename columns. //
      • virtual concatenation of tables (if still necessary). //
      • maybe an isAttached function. // class Table { friend class TableColumn; friend class BaseTable; friend class PlainTable; friend class MemoryTable; friend class RefTable; friend class ConcatTable; friend class TableIterator; friend class RODataManAccessor; friend class TableExprNode; friend class TableExprNodeRep; public: // Define the possible options how a table can be opened. enum TableOption { // existing table Old=1, // create table New, // create table (may not exist) NewNoReplace, // new table, which gets marked for delete Scratch, // update existing table Update, // delete table Delete }; // Define the possible table types. enum TableType { // plain table (stored on disk) Plain, // table held in memory Memory }; // Define the possible endian formats in which table data can be stored. enum EndianFormat { // store table data in big endian (e.g. SUN) format BigEndian=1, // store table data in little endian (e.g. Intel) format LittleEndian, // store data in the endian format of the machine used LocalEndian, // use endian format defined in the aipsrc variable table.endianformat // If undefined, it defaults to LocalEndian. AipsrcEndian }; // Define the signature of the function being called when the state // of a scratch table changes (i.e. created, closed, renamed, // (un)markForDelete). //
        - isScratch=True indicates that a scratch table // is created (oldName is empty) or renamed // (oldName is not empty). //
        - isScratch=False indicates that a scratch table // with name name is not scratch anymore (because it is // closed or because its state is set to non-scratch). typedef void ScratchCallback (const String& name, Bool isScratch, const String& oldName); // Set the pointer to the ScratchCallback function. // It returns the current value of the pointer. // This function is called when changing the state of a table // (i.e. create, close, rename, (un)markForDelete). static ScratchCallback* setScratchCallback (ScratchCallback*); // Create a null Table object (i.e. a NullTable is attached). // The sole purpose of this constructor is to allow construction // of an array of Table objects. // The assignment operator can be used to make a null object // reference a proper table. Table(); // Create a table object for an existing table. // The only options allowed are Old, Update, and Delete. // If the name of a table description is given, it is checked // if the table has that description. // Locking options can be given (see class // TableLock. // If the table with this name was already opened in this process, // the existing and new locking options are merged using // TableLock::merge. // The default locking mechanism is DefaultLocking. If the table // is not open yet, it comes to AutoLocking with an inspection interval // of 5 seconds. Otherwise DefaultLocking keeps the locking options // of the already open table. // explicit Table (const String& tableName, TableOption = Table::Old, const TSMOption& = TSMOption()); Table (const String& tableName, const TableLock& lockOptions, TableOption = Table::Old, const TSMOption& = TSMOption()); Table (const String& tableName, const String& tableDescName, TableOption = Table::Old, const TSMOption& = TSMOption()); Table (const String& tableName, const String& tableDescName, const TableLock& lockOptions, TableOption = Table::Old, const TSMOption& = TSMOption()); // // Make a new empty table (plain (scratch) or memory type). // Columns should be added to make it a real one. // Note that the endian format is only relevant for plain tables. explicit Table (TableType, EndianFormat = Table::AipsrcEndian, const TSMOption& = TSMOption()); // Make a table object for a new table, which can thereafter be used // for reading and writing. // If there are unbound columns, default storage managers an/ord virtual // column engines will be created and bound to those columns. // Create the table with the given nr of rows. If a storage manager // is used which does not allow addition of rows, the number of rows // in the table must already be given here. // Optionally the rows can be initialized with the default // values as defined in the column descriptions. // Locking options can be given (see class // TableLock. // The default locking mechanism is AutoLocking with a default // inspection interval of 5 seconds. //
        The data will be stored in the given endian format. // explicit Table (SetupNewTable&, rownr_t nrrow = 0, Bool initialize = False, EndianFormat = Table::AipsrcEndian, const TSMOption& = TSMOption()); Table (SetupNewTable&, TableType, rownr_t nrrow = 0, Bool initialize = False, EndianFormat = Table::AipsrcEndian, const TSMOption& = TSMOption()); Table (SetupNewTable&, TableType, const TableLock& lockOptions, rownr_t nrrow = 0, Bool initialize = False, EndianFormat = Table::AipsrcEndian, const TSMOption& = TSMOption()); Table (SetupNewTable&, TableLock::LockOption, rownr_t nrrow = 0, Bool initialize = False, EndianFormat = Table::AipsrcEndian, const TSMOption& = TSMOption()); Table (SetupNewTable&, const TableLock& lockOptions, rownr_t nrrow = 0, Bool initialize = False, EndianFormat = Table::AipsrcEndian, const TSMOption& = TSMOption()); #ifdef HAVE_MPI explicit Table (MPI_Comm mpiComm, TableType, EndianFormat = Table::AipsrcEndian, const TSMOption& = TSMOption()); explicit Table (MPI_Comm mpiComm, SetupNewTable&, rownr_t nrrow = 0, Bool initialize = False, EndianFormat = Table::AipsrcEndian, const TSMOption& = TSMOption()); Table (MPI_Comm mpiComm, SetupNewTable&, TableType, rownr_t nrrow = 0, Bool initialize = False, EndianFormat = Table::AipsrcEndian, const TSMOption& = TSMOption()); Table (MPI_Comm mpiComm, SetupNewTable&, TableType, const TableLock& lockOptions, rownr_t nrrow = 0, Bool initialize = False, EndianFormat = Table::AipsrcEndian, const TSMOption& = TSMOption()); Table (MPI_Comm mpiComm, SetupNewTable&, TableLock::LockOption, rownr_t nrrow = 0, Bool initialize = False, EndianFormat = Table::AipsrcEndian, const TSMOption& = TSMOption()); Table (MPI_Comm mpiComm, SetupNewTable&, const TableLock& lockOptions, rownr_t nrrow = 0, Bool initialize = False, EndianFormat = Table::AipsrcEndian, const TSMOption& = TSMOption()); #endif // // Create a table object as the virtual concatenation of // one or more of existing tables. The descriptions of all those tables // must be exactly the same. //
        The keywordset of the virtual table is the set of the first table // including its subtables. However, it is possible to specify the names // of the subtables that have to be concantenated as well. //
        In this way a concatenation of multiple MS-s can be made, where it // can be specified that, say, the SYSCAL table has to be concatenated too. //
        When a concatenated table is written and if a non-empty // subDirName is given, the tables to be concatenated will be // moved to that subdirectory in the directory of the concatenated table. // This option is mainly used by the MSS structure used in CASA. //
        // The only open options allowed are Old and Update. // Locking options can be given (see class // TableLock. // They apply to all underlying tables. // If a table was already opened in this process, // the existing and new locking options are merged using // TableLock::merge. // The default locking mechanism is DefaultLocking. If the table // is not open yet, it comes to AutoLocking with an inspection interval // of 5 seconds. Otherwise DefaultLocking keeps the locking options // of the already open table. // explicit Table (const Block
      • & tables, const Block& subTables = Block(), const String& subDirName = String()); explicit Table (const Block& tableNames, const Block& subTables = Block(), TableOption = Table::Old, const TSMOption& = TSMOption(), const String& subDirName = String()); Table (const Block& tableNames, const Block& subTables, const TableLock& lockOptions, TableOption = Table::Old, const TSMOption& = TSMOption()); // // Copy constructor (reference semantics). Table (const Table&); // The destructor flushes (i.e. writes) the table if it is opened // for output and not marked for delete. // It will flush if the destructor is called due to an exception, // because the Table object may not be correct. // Of course, in that case the flush function could be called explicitly. //
        It is virtual, so an object of a derived class like MeasurementSet // is destructed correctly through a Table pointer. virtual ~Table(); // Assignment (reference semantics). Table& operator= (const Table&); // Get the names of the tables this table consists of. // For a plain table it returns its name, // for a RefTable the name of the parent, and // for a ConcatTable the names of all its parts. //
        Note that a part can be any type of table (e.g. a ConcatTable). // The recursive switch tells how to deal with that. Block getPartNames (Bool recursive=False) const; // Is this table the same as the other? Bool isSameTable (const Table& other) const { return baseTabPtr_p == other.baseTabPtr_p; } // Is the root table of this table the same as that of the other one? Bool isSameRoot (const Table& other) const; // Close all open subtables. void closeSubTables() const; // Try to reopen the table for read/write access. // An exception is thrown if the table is not writable. // Nothing is done if the table is already open for read/write. void reopenRW(); // Get the endian format in which the table is stored. Table::EndianFormat endianFormat() const; // Get the storage option used for the table. const StorageOption& storageOption() const; // Is the table used (i.e. open) in this process. static Bool isOpened (const String& tableName); // Is the table used (i.e. open) in another process. // If checkSubTables is set, it is also checked if // a subtable is used in another process. Bool isMultiUsed (Bool checkSubTables=False) const; // Get the locking options. const TableLock& lockOptions() const; // Has this process the read or write lock, thus can the table // be read or written safely? // Bool hasLock (FileLocker::LockType = FileLocker::Write) const; Bool hasLock (Bool write) const; // // Try to lock the table for read or write access (default is write). // The number of attempts (default = forever) can be specified when // acquiring the lock does not succeed immediately. If nattempts>1, // the system waits 1 second between each attempt, so nattempts // is more or less equal to a wait period in seconds. // The return value is false if acquiring the lock failed. // If PermanentLocking is in effect, a lock is already // present, so nothing will be done. // Bool lock (FileLocker::LockType = FileLocker::Write, uInt nattempts = 0); Bool lock (Bool write, uInt nattempts = 0); // // Unlock the table. This will also synchronize the table data, // thus force the data to be written to disk. // If PermanentLocking is in effect, nothing will be done. void unlock(); // Determine the number of locked tables opened with the AutoLock option // (Locked table means locked for read and/or write). static uInt nAutoLocks(); // Unlock locked tables opened with the AutoLock option. // If all=True all such tables will be unlocked. // If all=False only tables requested by another process // will be unlocked. static void relinquishAutoLocks (Bool all = False); // Get the names of tables locked in this process. // By default all locked tables are given (note that a write lock // implies a read lock), but it is possible to select on lock type // FileLocker::Write and on option (TableLock::AutoLocking, // TableLock::ReadLocking, or TableLock::PermanentLocking). static Vector getLockedTables(FileLocker::LockType=FileLocker::Read, int lockOption=-1); // Determine if column or keyword table data have changed // (or is being changed) since the last time this function was called. Bool hasDataChanged(); // Flush the table, i.e. write out the buffers. If sync=True, // it is ensured that all data are physically written to disk. // Nothing will be done if the table is not writable. // At any time a flush can be executed, even if the table is marked // for delete. // If the table is marked for delete, the destructor will remove // files written by intermediate flushes. // Note that if necessary the destructor will do an implicit flush, // unless it is executed due to an exception. //
        If fsync=True the file contents are fsync-ed to disk, // thus ensured that the system buffers are actually written to disk. //
        If recursive=True all subtables are flushed too. void flush (Bool fsync=False, Bool recursive=False); // Resynchronize the Table object with the table file. // This function is only useful if no read-locking is used, ie. // if the table lock option is UserNoReadLocking or AutoNoReadLocking. // In that cases the table system does not acquire a read-lock, thus // does not synchronize itself automatically. void resync(); // Test if the object is null, i.e. does not reference a proper table. // This is the case if the default constructor is used. Bool isNull() const { return (baseTabPtr_p == 0 ? True : baseTabPtr_p->isNull()); } // Throw an exception if the object is null, i.e. // if function isNull() is True. void throwIfNull() const; // Test if the given data type is native to the table system. // If not, a virtual column engine is needed to store data with that type. // With the function DataType::whatType it can be used in a templated // function like: // // if (Table::isNativeDataType (whatType(static_cast(0)))) { // static Bool isNativeDataType (DataType dtype); // Make the table file name. static String fileName (const String& tableName); // Test if a table with the given name exists and is readable. // If not, an exception is thrown if throwIf==True. static Bool isReadable (const String& tableName, bool throwIf=False); // Show the structure of the table. // It shows the columns (with types), the data managers, and the subtables. // Optionally the columns can be sorted alphabetically. void showStructure (std::ostream&, Bool showDataMans=True, Bool showColumns=True, Bool showSubTables=False, Bool sortColumns=False, Bool cOrder=False) const; // Show the table and/or column keywords, possibly also of all subtables. // Maximum maxVal values of Arrays will be shown. void showKeywords (std::ostream&, Bool showSubTables=False, Bool showTabKey=True, Bool showColKey=False, Int maxVal=25) const; // Show the table and/or column keywords of this table. // Maximum maxVal values of Arrays will be shown. void showKeywordSets (std::ostream&, Bool showTabKey, Bool showColKey, Int maxVal) const; // Test if a table with the given name exists and is writable. static Bool isWritable (const String& tableName, bool throwIf=False); // Find the non-writable files in a table. static Vector nonWritableFiles (const String& tableName); // Test if this table is the root table (ie. if it is not the subset // of another table). Bool isRootTable() const; // Test if this table is opened as writable. Bool isWritable() const; // Test if the given column is writable. // Bool isColumnWritable (const String& columnName) const; Bool isColumnWritable (uInt columnIndex) const; // // Test if the given column is stored (otherwise it is virtual). // Bool isColumnStored (const String& columnName) const; Bool isColumnStored (uInt columnIndex) const; // // Get readonly access to the table keyword set. // If UserLocking is used, it will automatically acquire // and release a read lock if the table is not locked. const TableRecord& keywordSet() const; // Get read/write access to the table keyword set. // This requires that the table is locked (or it gets locked // if using AutoLocking mode). TableRecord& rwKeywordSet(); // Get access to the TableInfo object. // const TableInfo& tableInfo() const; TableInfo& tableInfo(); // // Write the TableInfo object. // Usually this is not necessary, because it is done automatically // when the table gets written (by table destructor or flush function). // This function is only useful if the table info has to be written // before the table gets written (e.g. when another process reads // the table while it gets filled). void flushTableInfo() const; // Get the table description. // This can be used to get nr of columns, etc.. // tableDesc() gives the table description used when // constructing the table, while actualTableDesc() gives the // actual description, thus with the actual data managers used. // const TableDesc& tableDesc() const; TableDesc actualTableDesc() const; // // Return all data managers used and the columns served by them. // The info is returned in a record. It contains a subrecord per // data manager. Each subrecord contains the following fields: //
        //
        TYPE //
        a string giving the type of the data manager. //
        NAME //
        a string giving the name of the data manager. //
        COLUMNS //
        a vector of strings giving the columns served by the data manager. //
        // Data managers may return some additional fields (e.g. BUCKETSIZE). Record dataManagerInfo() const; // Get the table name. const String& tableName() const; // Rename the table and all its subtables. // The following options can be given: //
        //
        Table::Update //
        A table with this name must already exists, which will be // overwritten. When succesfully renamed, the table is unmarked // for delete (if necessary). //
        Table::New //
        If a table with this name exists, it will be overwritten. // When succesfully renamed, the table is unmarked // for delete (if necessary). //
        Table::NewNoReplace //
        If a table with this name already exists, an exception // is thrown. When succesfully renamed, the table // is unmarked for delete (if necessary). //
        Table::Scratch //
        Same as Table::New, but followed by markForDelete(). //
        // The scratchCallback function is called when needed. void rename (const String& newName, TableOption); // Copy the table and all its subtables. // Especially for RefTables copy and deepCopy behave // differently. copy makes a bitwise copy of the table, thus // the result is still a RefTable. On the other hand deepCopy // makes a physical copy of all referenced table rows and columns, thus // the result is a PlainTable. //
        For PlainTables deepCopy is the same as copy // unless valueCopy==True is given. In that case the values // are copied which takes longer, but reorganizes the data files to get // rid of gaps in the data. Also if specific DataManager info is given // or if no rows have to be copied, a deep copy is made. //
        The following options can be given: //
        //
        Table::New //
        If a table with this name exists, it will be overwritten. //
        Table::NewNoReplace //
        If a table with this name already exists, an exception // is thrown. //
        Table::Scratch //
        Same as Table::New, but followed by markForDelete(). //
        // // The new table gets the given endian format. Note that the endian option // is only used if a true deep copy of a table is made. //
        When making a deep copy, it is possible to specify the data managers // using the dataManagerInfo argument. // See getDataManagerInfo for more info about that record. //
        If noRows=True no rows are copied. Also no rows are // copied in all subtables. It is useful if one wants to make a copy // of only the Table structure. void copy (const String& newName, TableOption, Bool noRows=False) const; void deepCopy (const String& newName, TableOption, Bool valueCopy=False, EndianFormat=AipsrcEndian, Bool noRows=False) const; void deepCopy (const String& newName, const Record& dataManagerInfo, TableOption, Bool valueCopy=False, EndianFormat=AipsrcEndian, Bool noRows=False) const; void deepCopy (const String& newName, const Record& dataManagerInfo, const StorageOption&, TableOption, Bool valueCopy=False, EndianFormat=AipsrcEndian, Bool noRows=False) const; //
        // Make a copy of a table to a MemoryTable object. // Use the given name for the memory table. Table copyToMemoryTable (const String& name, Bool noRows=False) const; // Get the table type. TableType tableType() const; // Get the table option. int tableOption() const; // Mark the table for delete. // This means that the underlying table gets deleted when it is // actually destructed. // The scratchCallback function is called when needed. void markForDelete(); // Unmark the table for delete. // This means the underlying table does not get deleted when destructed. // The scratchCallback function is called when needed. void unmarkForDelete(); // Test if the table is marked for delete. Bool isMarkedForDelete() const; // Get the number of rows. // It is unsynchronized meaning that it will not check if another // process updated the table, thus possible increased the number of rows. // If one wants to take that into account, he should acquire a // read-lock (using the lock function) before using nrow(). rownr_t nrow() const; // Test if it is possible to add a row to this table. // It is possible if all storage managers used for the table // support it. Bool canAddRow() const; // Add one or more rows at the end of the table. // This will fail for tables not supporting addition of rows. // Optionally the rows can be initialized with the default // values as defined in the column descriptions. void addRow (rownr_t nrrow = 1, Bool initialize = False); // Test if it is possible to remove a row from this table. // It is possible if all storage managers used for the table // support it. Bool canRemoveRow() const; // Remove the given row(s). // The latter form can be useful with the select and rowNumbers functions // to remove some selected rows from the table. //
        It will fail for tables not supporting removal of rows. // // The following code fragments do NOT have the same result: // // tab.removeRow (10); // remove row 10 // tab.removeRow (20); // remove row 20, which was 21 // Vector vec(2); // vec(0) = 10; // vec(1) = 20; // tab.removeRow (vec); // remove row 10 and 20 // // because in the first fragment removing row 10 turns the former // row 21 into row 20. // // void removeRow (rownr_t rownr); void removeRow (const RowNumbers& rownrs); // // Create a TableExprNode object for a column or for a keyword // in the table keyword set. // This can be used in selecting rows from a table using // operator() described below. //
        The functions taking the fieldNames vector are meant for // the cases where the keyword or column contains records. // The fieldNames indicate which field to take from that record // (which can be a record again, etc.). // TableExprNode key (const String& keywordName) const; TableExprNode key (const Vector& fieldNames) const; TableExprNode col (const String& columnName) const; TableExprNode col (const String& columnName, const Vector& fieldNames) const; // // Create a TableExprNode object for the rownumber function. // 'origin' Indicates which rownumber is the first. // C++ uses origin = 0 (default) // Glish and TaQL both use origin = 1 TableExprNode nodeRownr (rownr_t origin=0) const; // Create a TableExprNode object for the rand function. TableExprNode nodeRandom () const; // Select rows from a table using an select expression consisting // of TableExprNode objects. // Basic TableExprNode objects can be created with the functions // key and especially // col. // Composite TableExprNode objects, representing an expression, // can be created by applying operations (like == and +) // to the basic ones. This is described in class // TableExprNode. // For example: // // Table result = tab(tab.col("columnName") > 10); // // All rows for which the expression is true, will be selected and // "stored" in the result. // You need to include ExprNode.h for this purpose. //
        The first offset matching rows will be skipped. //
        If maxRow>0, the selection process will stop // when maxRow rows are selected. //
        The TableExprNode argument can be empty (null) meaning that only // the maxRow/offset arguments are taken into account. Table operator() (const TableExprNode&, rownr_t maxRow=0, rownr_t offset=0) const; // Select rows using a vector of row numbers. // This can, for instance, be used to select the same rows as // were selected in another table (using the rowNumbers function). // // Table result = thisTable (otherTable.rowNumbers()); // Table operator() (const RowNumbers& rownrs) const; // Select rows using a mask block. // The length of the block must match the number of rows in the table. // If an element in the mask is True, the corresponding row will be // selected. Table operator() (const Block& mask) const; // Project the given columns (i.e. select the columns). Table project (const Block& columnNames) const; //# Virtually concatenate all tables in this column. //# The column cells must contain tables with the same description. //#// Table concatenate (const String& columnName) const; // Do logical operations on a table. // It can be used for row-selected or projected (i.e. column-selected) // tables. The tables involved must come from the same root table or // be the root table themselves. // // Intersection with another table. Table operator& (const Table&) const; // Union with another table. Table operator| (const Table&) const; // Subtract another table. Table operator- (const Table&) const; // Xor with another table. Table operator^ (const Table&) const; // Take complement. Table operator! () const; // // Sort a table on one or more columns of scalars. // Per column a compare function can be provided. By default // the standard compare function defined in Compare.h will be used. // Default sort order is ascending. // Default sorting algorithm is the parallel sort. // // Sort on one column. Table sort (const String& columnName, int = Sort::Ascending, int = Sort::ParSort) const; // Sort on multiple columns. The principal column has to be the // first element in the Block of column names. Table sort (const Block& columnNames, int = Sort::Ascending, int = Sort::ParSort) const; // Sort on multiple columns. The principal column has to be the // first element in the Block of column names. // The order can be given per column. Table sort (const Block& columnNames, const Block& sortOrders, int = Sort::ParSort) const; // Sort on multiple columns. The principal column has to be the // first element in the Block of column names. // The order can be given per column. // Provide some special comparisons via std::shared_ptrs of compare objects. // A null std::shared_ptr means using the standard compare object // from class ObjCompare. Table sort (const Block& columnNames, const Block>& compareObjects, const Block& sortOrders, int = Sort::ParSort) const; // // Get a vector of row numbers in the root table of rows in this table. // In case the table is a subset of the root table, this tells which // rows of the root table are part of the subset. // In case the table is the root table itself, the result is a vector // containing the row numbers 0 .. #rows-1. //
        Note that in general it is better to use the next // rowNumbers(Table) function. RowNumbers rowNumbers() const; // Get a vector of row numbers in that table of rows in this table. // In case the table is a subset of that table, this tells which // rows of that table are part of the subset. // In case the table is that table itself, the result is a vector // containing the row numbers 0 .. #rows-1. // This function is in principle meant for cases // where this table is a subset of that table. However, it can be used // for any table. In that case the returned vector contains a very high // number (max_uint) for rows in this table not part of that table. // In that way they are invalid if used elsewhere. //
        In the general case creating the row number vector can be slowish, // because it has to do two mappings. However, if this table is a subset // of that table and if they are in the same order, the mapping can be done // in a more efficient way. The argument tryFast can be used to // tell the function to try a fast conversion first. If that cannot be done, // it reverts to the slower way at the expense of an unsuccessful fast // attempt. //
        // // Table tab("somename"); // Table subset = tab(some_select_expression); // RowNumbers rownrs = subset.rowNumbers(tab); // // Note that one cannot be sure that table "somename" is the root // (i.e. original) table. It may also be a subset of another table. // In the latter case doing //
        RowNumbers rownrs = subset.rowNumbers() // does not give the row numbers in tab, but in the root table // (which is probably not what you want). RowNumbers rowNumbers (const Table& that, Bool tryFast=False) const; // Add a column to the table. // The data manager used for the column depend on the function used. // Exceptions are thrown if the column already exist or if the // table is not writable. //
        If this table is a reference table (result of selection) and if // addToParent=True the column is also added to the parent // table. // // Use the first appropriate existing storage manager. // If there is none, a data manager is created using the default // data manager in the column description. void addColumn (const ColumnDesc& columnDesc, Bool addToParent = True); // Use an existing data manager with the given name or type. // If the flag byName is True, a name is given, otherwise a type. // If a name is given, an exception is thrown if the data manager is // unknown or does not allow addition of columns. // If a type is given, a storage manager of the given type will be // created if there is no such data manager allowing addition of rows. void addColumn (const ColumnDesc& columnDesc, const String& dataManager, Bool byName, Bool addToParent = True); // Use the given data manager (which is a new one). void addColumn (const ColumnDesc& columnDesc, const DataManager& dataManager, Bool addToParent = True); // // Add a bunch of columns using the given new data manager. // All columns and possible hypercolumn definitions in the given table // description will be copied and added to the table. // This can be used in case of specific data managers which need to // be created with more than one column (e.g. the tiled hypercube // storage managers). //
        The data manager can be given directly or by means of a record // describing the data manager in the standard way with the fields // TYPE, NAME, and SPEC. The record can contain those fields itself // or it can contain a single subrecord with those fields. //
        If this table is a reference table (result of selection) and if // addToParent=True the columns are also added to the parent // table. // void addColumn (const TableDesc& tableDesc, const DataManager& dataManager, Bool addToParent = True); void addColumn (const TableDesc& tableDesc, const Record& dataManagerInfo, Bool addToParent = True); // // Test if columns can be removed. // It can if the columns exist and if the data manager it is using // supports removal of columns or if all columns from a data manager // would be removed.. //
        You can always remove columns from a reference table. // Bool canRemoveColumn (const String& columnName) const; Bool canRemoveColumn (const Vector& columnNames) const; // // Remove columns. //
        When removing columns from a reference table, the columns // are NOT removed from the underlying table. // void removeColumn (const String& columnName); void removeColumn (const Vector& columnName); // // Test if a column can be renamed. Bool canRenameColumn (const String& columnName) const; // Rename a column. // An exception is thrown if the old name does not exist or // if the name already exists. // // Renaming a column should be done with care, because other // columns may be referring this column. Also a hypercolumn definition // might be using the old name. // Finally if may also invalidate persistent selections of a table, // because the reference table cannot find the column anymore. // void renameColumn (const String& newName, const String& oldName); void renameHypercolumn (const String& newName, const String& oldName); // Write a table to AipsIO (for TypedKeywords
        ). // This will only write the table name. friend AipsIO& operator<< (AipsIO&, const Table&); // Read a table from AipsIO (for TypedKeywords
        ). // This will read the table name and open the table as writable // if the table file is writable, otherwise as readonly. friend AipsIO& operator>> (AipsIO&, Table&); // Read a table from AipsIO (for TableKeywords). // This will read the table name and open the table as writable // if the switch is set and if the table file is writable. // otherwise it is opened as readonly. void getTableKeyword (AipsIO&, Bool openWritable); // Write a table to ostream (for TypedKeywords
        ). // This only shows its name and number of columns and rows. friend ostream& operator<< (ostream&, const Table&); // Find the data manager with the given name or for the given column name. DataManager* findDataManager (const String& name, Bool byColumn=False) const; // Some deprecated functions for backward compatibility, now in TableUtil.h. // Use old way of indicating deprecate to avoid -Wc++14-extensions warnings. // //# [[deprecated ("Now use TableUtil::openTable")]] static Table openTable (const String& tableName, TableOption = Table::Old, const TSMOption& = TSMOption()) __attribute__ ((deprecated ("Now use TableUtil::openTable"))); //# [[deprecated ("Now use TableUtil::openTable")]] static Table openTable (const String& tableName, const TableLock& lockOptions, TableOption = Table::Old, const TSMOption& = TSMOption()) __attribute__ ((deprecated ("Now use TableUtil::openTable"))); //# [[deprecated ("Now use TableUtil::canDeleteTable")]] static Bool canDeleteTable (const String& tableName, Bool checkSubTables=False) __attribute__ ((deprecated ("Now use TableUtil::canDeleteTable"))); //# [[deprecated ("Now use TableUtil::canDeleteTable")]] static Bool canDeleteTable (String& message, const String& tableName, Bool checkSubTables=False) __attribute__ ((deprecated ("Now use TableUtil::canDeleteTable"))); //# [[deprecated ("Now use TableUtil::deleteTable")]] static void deleteTable (const String& tableName, Bool checkSubTables=False) __attribute__ ((deprecated ("Now use TableUtil::deleteTable"))); //# [[deprecated ("Now use TableUtil::getLayout")]] static rownr_t getLayout (TableDesc& desc, const String& tableName) __attribute__ ((deprecated ("Now use TableUtil::getLayout"))); //# [[deprecated ("Now use TableUtil::tableInfo")]] static TableInfo tableInfo (const String& tableName) __attribute__ ((deprecated ("Now use TableUtil::tableInfo"))); // protected: // Shared pointer to count the references to the BaseTable object. // The shared pointer can be null, so it is not counted which is necessary for // the Table object in the DataManager. Otherwise mutual referencing would occur. // Note that the BaseTable object contains a weak_ptr to itself which is the // basis for all shared pointer counting. std::shared_ptr countedTabPtr_p; // Pointer to BaseTable object which is always filled and always used. // The shared_ptr above is only for reference counting. BaseTable* baseTabPtr_p; // Counter of last call to hasDataChanged. uInt lastModCounter_p; // Pointer to the ScratchCallback function. static ScratchCallback* scratchCallback_p; // Construct a Table object from a pointer to BaseTable. // It is meant for internal Table objects, so the BaseTable is not counted. // Thus the internal shared_ptr is null. Table (BaseTable*); // Construct a Table object from a shared pointer to BaseTable. Table (const std::shared_ptr&); // Open an existing table. void open (const String& name, const String& type, int tableOption, const TableLock& lockOptions, const TSMOption& tsmOpt); private: // Construct a BaseTable object from the table file. static std::shared_ptr makeBaseTable (const String& name, const String& type, int tableOption, const TableLock& lockOptions, const TSMOption& tsmOpt, Bool addToCache, uInt locknr); // Get the pointer to the underlying BaseTable. // This is needed for some friend classes. BaseTable* baseTablePtr() const; // Initialize the BaseTable pointers in this Table object. void initBasePtr (BaseTable* ptr); // Look in the cache if the table is already open. // If so, check if the table option matches. // If needed reopen the table for read/write and merge the lock options. BaseTable* lookCache (const String& name, int tableOption, const TableLock& tableInfo); // Try if v1 is a subset of v2 and fill rows with its indices in v2. // Return False if not a proper subset. Bool fastRowNumbers (const Vector& v1, const Vector& v2, Vector& rows) const; // Show the info of the given columns. // Sort the columns if needed. void showColumnInfo (ostream& os, const TableDesc&, uInt maxNameLength, const Array& columnNames, Bool sort) const; }; inline Bool Table::isSameRoot (const Table& other) const { return baseTabPtr_p->root() == other.baseTabPtr_p->root(); } inline void Table::reopenRW() { baseTabPtr_p->reopenRW(); } inline void Table::flush (Bool fsync, Bool recursive) { baseTabPtr_p->flush (fsync, recursive); } inline void Table::resync() { baseTabPtr_p->resync(); } inline const StorageOption& Table::storageOption() const { return baseTabPtr_p->storageOption(); } inline Bool Table::isMultiUsed(Bool checkSubTables) const { return baseTabPtr_p->isMultiUsed(checkSubTables); } inline const TableLock& Table::lockOptions() const { return baseTabPtr_p->lockOptions(); } inline Bool Table::lock (FileLocker::LockType type, uInt nattempts) { return baseTabPtr_p->lock (type, nattempts); } inline Bool Table::lock (Bool write, uInt nattempts) { return baseTabPtr_p->lock (write ? FileLocker::Write : FileLocker::Read, nattempts); } inline void Table::unlock() { baseTabPtr_p->unlock(); } inline Bool Table::hasLock (FileLocker::LockType type) const { return baseTabPtr_p->hasLock (type); } inline Bool Table::hasLock (Bool write) const { return baseTabPtr_p->hasLock (write ? FileLocker::Write : FileLocker::Read); } inline Bool Table::isRootTable() const { return baseTabPtr_p == baseTabPtr_p->root(); } inline Bool Table::isWritable() const { return baseTabPtr_p->isWritable(); } inline Bool Table::isColumnWritable (const String& columnName) const { return baseTabPtr_p->isColumnWritable (columnName); } inline Bool Table::isColumnWritable (uInt columnIndex) const { return baseTabPtr_p->isColumnWritable (columnIndex); } inline Bool Table::isColumnStored (const String& columnName) const { return baseTabPtr_p->isColumnStored (columnName); } inline Bool Table::isColumnStored (uInt columnIndex) const { return baseTabPtr_p->isColumnStored (columnIndex); } inline void Table::rename (const String& newName, TableOption option) { baseTabPtr_p->rename (newName, option); } inline void Table::deepCopy (const String& newName, const Record& dataManagerInfo, TableOption option, Bool valueCopy, EndianFormat endianFormat, Bool noRows) const { baseTabPtr_p->deepCopy (newName, dataManagerInfo, StorageOption(), option, valueCopy, endianFormat, noRows); } inline void Table::deepCopy (const String& newName, const Record& dataManagerInfo, const StorageOption& stopt, TableOption option, Bool valueCopy, EndianFormat endianFormat, Bool noRows) const { baseTabPtr_p->deepCopy (newName, dataManagerInfo, stopt, option, valueCopy, endianFormat, noRows); } inline void Table::markForDelete() { baseTabPtr_p->markForDelete (True, ""); } inline void Table::unmarkForDelete() { baseTabPtr_p->unmarkForDelete(True, ""); } inline Bool Table::isMarkedForDelete() const { return baseTabPtr_p->isMarkedForDelete(); } inline rownr_t Table::nrow() const { return baseTabPtr_p->nrow(); } inline BaseTable* Table::baseTablePtr() const { return baseTabPtr_p; } inline const TableDesc& Table::tableDesc() const { return baseTabPtr_p->tableDesc(); } inline const TableRecord& Table::keywordSet() const { return baseTabPtr_p->keywordSet(); } inline const TableInfo& Table::tableInfo() const { return baseTabPtr_p->tableInfo(); } inline TableInfo& Table::tableInfo() { return baseTabPtr_p->tableInfo(); } inline void Table::flushTableInfo() const { baseTabPtr_p->flushTableInfo(); } inline const String& Table::tableName() const { return baseTabPtr_p->tableName(); } inline Table::TableType Table::tableType() const { return TableType(baseTabPtr_p->tableType()); } inline int Table::tableOption() const { return baseTabPtr_p->tableOption(); } inline Bool Table::canAddRow() const { return baseTabPtr_p->canAddRow(); } inline Bool Table::canRemoveRow() const { return baseTabPtr_p->canRemoveRow(); } inline Bool Table::canRemoveColumn (const Vector& columnNames) const { return baseTabPtr_p->canRemoveColumn (columnNames); } inline Bool Table::canRenameColumn (const String& columnName) const { return baseTabPtr_p->canRenameColumn (columnName); } inline void Table::addRow (rownr_t nrrow, Bool initialize) { baseTabPtr_p->addRow (nrrow, initialize); } inline void Table::removeRow (rownr_t rownr) { baseTabPtr_p->removeRow (rownr); } inline void Table::removeRow (const RowNumbers& rownrs) { baseTabPtr_p->removeRow (rownrs); } inline void Table::addColumn (const ColumnDesc& columnDesc, Bool addToParent) { baseTabPtr_p->addColumn (columnDesc, addToParent); } inline void Table::addColumn (const ColumnDesc& columnDesc, const String& dataManager, Bool byName, Bool addToParent) { baseTabPtr_p->addColumn (columnDesc, dataManager, byName, addToParent); } inline void Table::addColumn (const ColumnDesc& columnDesc, const DataManager& dataManager, Bool addToParent) { baseTabPtr_p->addColumn (columnDesc, dataManager, addToParent); } inline void Table::addColumn (const TableDesc& tableDesc, const DataManager& dataManager, Bool addToParent) { baseTabPtr_p->addColumn (tableDesc, dataManager, addToParent); } inline void Table::addColumn (const TableDesc& tableDesc, const Record& dataManagerInfo, Bool addToParent) { baseTabPtr_p->addColumns (tableDesc, dataManagerInfo, addToParent); } inline void Table::removeColumn (const Vector& columnNames) { baseTabPtr_p->removeColumn (columnNames); } inline void Table::renameColumn (const String& newName, const String& oldName) { baseTabPtr_p->renameColumn (newName, oldName); } inline void Table::renameHypercolumn (const String& newName, const String& oldName) { baseTabPtr_p->renameHypercolumn (newName, oldName); } inline DataManager* Table::findDataManager (const String& name, Bool byColumn) const { return baseTabPtr_p->findDataManager (name, byColumn); } inline void Table::showStructure (std::ostream& os, Bool showDataMans, Bool showColumns, Bool showSubTables, Bool sortColumns, Bool cOrder) const { baseTabPtr_p->showStructure (os, showDataMans, showColumns, showSubTables, sortColumns, cOrder); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/TableAttr.cc000066400000000000000000000047301476623553700202050ustar00rootroot00000000000000//# TableAttr.cc: Some attributes of a table //# Copyright (C) 2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableAttr::TableAttr() : openWritable_p (False) {} TableAttr::TableAttr (const Table& table) : name_p (table.tableName()), openWritable_p (table.isWritable()), lockOptions_p (table.lockOptions()) {} TableAttr::TableAttr (const String& name, Bool openWritable) : name_p (name), openWritable_p (openWritable) {} TableAttr::TableAttr (const String& name, Bool openWritable, const TableLock& lockOptions) : name_p (name), openWritable_p (openWritable), lockOptions_p (lockOptions) {} TableAttr::TableAttr (const TableAttr& that) : name_p (that.name_p), openWritable_p (that.openWritable_p), lockOptions_p (that.lockOptions_p) {} TableAttr& TableAttr::operator= (const TableAttr& that) { if (this != &that) { name_p = that.name_p; openWritable_p = that.openWritable_p; lockOptions_p = that.lockOptions_p; } return *this; } TableAttr::~TableAttr() {} void TableAttr::set (const Table& table) { name_p = table.tableName(); openWritable_p = table.isWritable(); lockOptions_p = table.lockOptions(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/TableAttr.h000066400000000000000000000073411476623553700200500ustar00rootroot00000000000000//# TableAttr.h: Some attributes of a table //# Copyright (C) 2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABLEATTR_H #define TABLES_TABLEATTR_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class Table; // // Some attributes of a table. // // // // // //# Classes you should understand before using this one. //
      • TableRecord //
      • Table // // // This class holds some attributes of a table. // These attributes are name, readable/writable, and lock options. //
        // The primary use of the class is to be able to pass the various // attributes of the parent table to its subtables (e.g. in classes // like TableRecord and // TableKeyword). //
        // // This class makes it possible to have more attributes without // having to alter the classes using subtables. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class TableAttr { public: // Default constructor results in empty name. TableAttr(); // Construct the attributes from the table. explicit TableAttr (const Table& table); // Construct with given values. // explicit TableAttr (const String& name, Bool openWritable = False); TableAttr (const String& name, Bool openWritable, const TableLock&); // // Copy constructor (copy semantics). TableAttr (const TableAttr& that); // Assignment (copy semantics). // TableAttr& operator= (const TableAttr& that); TableAttr& operator= (const Table& table); // ~TableAttr(); // Set the object to for another table. void set (const Table& table); // Set the keyword to read/write access. void setRW() { openWritable_p = True; } void setName (const String& name) { name_p = name; } // Get info. // const String& name() const { return name_p; } Bool openWritable() const { return openWritable_p; } const TableLock& lockOptions() const { return lockOptions_p; } // private: String name_p; Bool openWritable_p; TableLock lockOptions_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/TableCache.cc000066400000000000000000000142541476623553700203000ustar00rootroot00000000000000//# TableCache.cc: Cache of open tables //# Copyright (C) 1994,1995,1997,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableCache::TableCache() {} TableCache::~TableCache() {} PlainTable* TableCache::operator() (const String& tableName) const { std::lock_guard sc(itsMutex); return getTable (tableName); } PlainTable* TableCache::getTable (const String& tableName) const { std::map::const_iterator iter = tableMap_p.find (tableName); if (iter == tableMap_p.end()) { return 0; } return static_cast(iter->second); } void TableCache::define (const String& tableName, PlainTable* tab) { std::lock_guard sc(itsMutex); tableMap_p.insert (std::make_pair(tableName, tab)); } void TableCache::remove (const String& tableName) { // For static Table objects it is possible that the cache is // deleted before the Table. // Therefore do not delete if the map is already empty // (otherwise an exception is thrown). std::lock_guard sc(itsMutex); if (tableMap_p.size() > 0) { try { tableMap_p.erase (tableName); } catch (std::exception&) { // Something strange has happened. // Throwing an exception causes an immediate crash (probably by // Table destructors). // So write a message on stderr; std::cerr << "Cannot remove table " << tableName << " from the table cache; suggest restarting" << std::endl; } } } void TableCache::rename (const String& newName, const String& oldName) { std::lock_guard sc(itsMutex); if (tableMap_p.find (oldName) != tableMap_p.end()) { void* ptr = tableMap_p.at(oldName); tableMap_p.erase (oldName); tableMap_p.insert (std::make_pair(newName, ptr)); } } uInt TableCache::nAutoLocks() { std::lock_guard sc(itsMutex); uInt n=0; for (const auto& x : tableMap_p) { PlainTable& table = *static_cast(x.second); if (table.lockOptions().option() == TableLock::AutoLocking) { //# Having a read lock is enough. if (table.hasLock (FileLocker::Read)) { n++; } } } return n; } void TableCache::relinquishAutoLocks (Bool all) { std::lock_guard sc(itsMutex); for (const auto& x : tableMap_p) { PlainTable& table = *static_cast(x.second); if (table.lockOptions().option() == TableLock::AutoLocking) { //# Having a read lock is enough. if (table.hasLock (FileLocker::Read)) { if (all) { table.unlock(); }else{ table.autoReleaseLock (True); } } } } } Vector TableCache::getTableNames() const { std::lock_guard sc(itsMutex); uInt ntab = tableMap_p.size(); Vector names(ntab); ntab = 0; for (const auto& x : tableMap_p) { PlainTable& table = *static_cast(x.second); names[ntab++] = table.tableName(); } return names; } Vector TableCache::getLockedTables (FileLocker::LockType lockType, int lockOption) { std::lock_guard sc(itsMutex); vector names; for (const auto& x : tableMap_p) { PlainTable& table = *static_cast(x.second); if (lockOption < 0 || table.lockOptions().option() == lockOption) { if (table.hasLock (lockType)) { names.push_back (table.tableName()); } } } return Vector(names); } void TableCache::flushTable (const String& name, Bool fsync, Bool recursive) { std::lock_guard sc(itsMutex); PlainTable* tab = getTable(name); if (tab) { tab->flush (fsync, recursive); } } PlainTable* TableCache::lookCache (const String& name, int tableOption, const TableLock& lockOptions) { //# Exit if table is not in cache yet. PlainTable* btp = this->operator()(name); if (btp == 0) { return btp; } //# Check if option matches. It does if equal. //# Otherwise it does if option in cached table is "more". //# Note that class PlainTable already throws an exception if //# a new table is created with the same name as an open table. int cachedTableOption = btp->tableOption(); if ((tableOption == cachedTableOption) || ((cachedTableOption == Table::New || cachedTableOption == Table::NewNoReplace || cachedTableOption == Table::Update) && (tableOption == Table::Update || tableOption == Table::Old))) { btp->mergeLock (lockOptions); return btp; } if (cachedTableOption == Table::Old && tableOption == Table::Update) { btp->mergeLock (lockOptions); btp->reopenRW(); return btp; } throw (TableInvOper ("Table " + name + " cannot be opened/created (already in cache)")); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/TableCache.h000066400000000000000000000135131476623553700201370ustar00rootroot00000000000000//# TableCache.h: Cache of open tables //# Copyright (C) 1994,1995,1997,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABLECACHE_H #define TABLES_TABLECACHE_H //# Includes #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class PlainTable; class TableLock; // // Cache of open tables // // // // // //# Classes you should understand before using this one. // // // TableCache represents a cache of open tables. // // // A TableCache object keeps track of the tables which have already // been opened in a program. It maps the name of a table to its // PlainTable object. // In principle only one TableCache object (statically defined in // class PlainTable) exists in a process. // The cache is used to prevent a table from being opened more than // once, which is not only a waste of space, but more importantly, // may give rise to synchronization problems. // Synchronization between the same table in multiple processes must // be done by a locking mechanism. // // TableCache is used by class Table and PlainTable. // Before opening a table, Table will first look in the cache. // Newly opened or created tables will be added to the cache. // When a table is actually closed, it will be removed from the cache. // // // When a RefTable is read back, it will also read back the table it // references. However, that table may have been opened before and // it is bad to have a table open more than once in the same program. // The TableCache class catches this and will not reopen the table. // // //# A List of bugs, limitations, extensions or planned refinements. //
      • Currently only PlainTables are taken into account. // Maybe RefTables should be too. // class TableCache { public: // Construct an empty cache of open tables. TableCache(); ~TableCache(); // The copy constructor is forbidden. TableCache (const TableCache&) = delete; // The assignment operator is forbidden. TableCache& operator= (const TableCache&) = delete; // Try to find a table with the given name in the cache. // Return a pointer to a table if found (thus if already open). // Return a zero pointer if not found. PlainTable* operator() (const String& tableName) const; // Add an open table to the cache. void define (const String& tableName, PlainTable*); // Remove an open table. void remove (const String& tableName); // Rename an open table. // If oldName is not in the cache, nothing will be done. void rename (const String& newName, const String& oldName); // Determine the number of locked tables opened with the AutoLock option // (Locked table means locked for read and/or write). uInt nAutoLocks(); // Unlock locked tables opened with the AutoLock option. // If all=True all such tables will be unlocked. // If all=False only tables requested by another process // will be unlocked. void relinquishAutoLocks (Bool all); // Get the names of the tables in the cache. Vector getTableNames() const; // Get the names of tables locked in this process. // By default all locked tables are given (note that a write lock // implies a read lock), but it is possible to select on lock type // FileLocker::Write and on option (TableLock::AutoLocking, // TableLock::ReadLocking, or TableLock::PermanentLocking). Vector getLockedTables (FileLocker::LockType, int lockOption); // Flush a possibly cached Table. void flushTable (const String& tableName, Bool fsync, Bool recursive); // Look in the cache if the table is already open. // If so, check if table option matches. // If needed reopen the table for read/write and merge the lock options. PlainTable* lookCache (const String& name, int tableOption, const TableLock& tableInfo); private: // Get the table without doing a mutex lock (for operator()). PlainTable* getTable (const String& tableName) const; //# void* iso. PlainTable* is used in the map declaration //# to reduce the number of template instantiations. //# The .cc file will use (fully safe) casts. std::map tableMap_p; //# A mutex to synchronize access to the cache. mutable std::mutex itsMutex; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/TableColumn.cc000066400000000000000000000330231476623553700205250ustar00rootroot00000000000000//# TableColumn.cc: Const access to a table column //# Copyright (C) 1994,1995,1996,1997,1999,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableColumn::TableColumn () : baseTabPtr_p (0), baseColPtr_p (0), colCachePtr_p (0), canChangeShape_p (False), isColWritable_p (False) {} TableColumn::TableColumn (const Table& tab, const String& columnName) : baseColPtr_p(0) { //# Get base table and base column. baseTabPtr_p = tab.baseTablePtr(); if (baseTabPtr_p == 0) { throw (TableInvOper ("TableColumn: no table in Table object")); } baseColPtr_p = baseTabPtr_p->getColumn (columnName); colCachePtr_p = &(baseColPtr_p->columnCache()); canChangeShape_p = baseColPtr_p->canChangeShape(); isColWritable_p = baseColPtr_p->isWritable(); } TableColumn::TableColumn (const Table& tab, uInt columnIndex) : baseColPtr_p(0) { //# Get base table and base column. baseTabPtr_p = tab.baseTablePtr(); if (baseTabPtr_p == 0) { throw (TableInvOper ("TableColumn: no table in Table object")); } baseColPtr_p = baseTabPtr_p->getColumn (columnIndex); colCachePtr_p = &(baseColPtr_p->columnCache()); canChangeShape_p = baseColPtr_p->canChangeShape(); isColWritable_p = baseColPtr_p->isWritable(); } TableColumn::TableColumn (const TableColumn& that) : baseTabPtr_p (that.baseTabPtr_p), baseColPtr_p (that.baseColPtr_p), colCachePtr_p (that.colCachePtr_p), canChangeShape_p (that.canChangeShape_p), isColWritable_p (that.isColWritable_p) {} TableColumn* TableColumn::clone() const { return new TableColumn (*this); } TableColumn& TableColumn::operator= (const TableColumn& that) { reference (that); return *this; } void TableColumn::reference (const TableColumn& that) { baseTabPtr_p = that.baseTabPtr_p; baseColPtr_p = that.baseColPtr_p; colCachePtr_p = that.colCachePtr_p; canChangeShape_p = that.canChangeShape_p; isColWritable_p = that.isColWritable_p; } TableColumn::~TableColumn() {} void TableColumn::throwIfNull() const { if (isNull()) { throw (TableInvOper ("TableColumn is null")); } } TableRecord& TableColumn::rwKeywordSet() { if (! baseTabPtr_p->isWritable()) { throw (TableError ("TableColumn::rwKeywordSet cannot be used: table " + baseTabPtr_p->tableName() + " is not writable")); } return baseColPtr_p->rwKeywordSet(); } const ColumnDesc& TableColumn::columnDesc() const { return baseColPtr_p->columnDesc(); } Table TableColumn::table() const { return Table (baseTabPtr_p); } Bool TableColumn::asBool (rownr_t rownr) const { TABLECOLUMNCHECKROW(rownr); Bool value; baseColPtr_p->getScalar (rownr, value); return value; } uChar TableColumn::asuChar (rownr_t rownr) const { TABLECOLUMNCHECKROW(rownr); uChar value; baseColPtr_p->getScalar (rownr, value); return value; } Short TableColumn::asShort (rownr_t rownr) const { TABLECOLUMNCHECKROW(rownr); Short value; baseColPtr_p->getScalar (rownr, value); return value; } uShort TableColumn::asuShort (rownr_t rownr) const { TABLECOLUMNCHECKROW(rownr); uShort value; baseColPtr_p->getScalar (rownr, value); return value; } Int TableColumn::asInt (rownr_t rownr) const { TABLECOLUMNCHECKROW(rownr); Int value; baseColPtr_p->getScalar (rownr, value); return value; } uInt TableColumn::asuInt (rownr_t rownr) const { TABLECOLUMNCHECKROW(rownr); uInt value; baseColPtr_p->getScalar (rownr, value); return value; } Int64 TableColumn::asInt64 (rownr_t rownr) const { TABLECOLUMNCHECKROW(rownr); Int64 value; baseColPtr_p->getScalar (rownr, value); return value; } float TableColumn::asfloat (rownr_t rownr) const { TABLECOLUMNCHECKROW(rownr); float value; baseColPtr_p->getScalar (rownr, value); return value; } double TableColumn::asdouble (rownr_t rownr) const { TABLECOLUMNCHECKROW(rownr); double value; baseColPtr_p->getScalar (rownr, value); return value; } Complex TableColumn::asComplex (rownr_t rownr) const { TABLECOLUMNCHECKROW(rownr); Complex value; baseColPtr_p->getScalar (rownr, value); return value; } DComplex TableColumn::asDComplex (rownr_t rownr) const { TABLECOLUMNCHECKROW(rownr); DComplex value; baseColPtr_p->getScalar (rownr, value); return value; } String TableColumn::asString (rownr_t rownr) const { TABLECOLUMNCHECKROW(rownr); String value; baseColPtr_p->getScalar (rownr, value); return value; } void TableColumn::put (rownr_t thisRownr, const TableColumn& that, rownr_t thatRownr, Bool preserveTileShape) { TABLECOLUMNCHECKROW(thisRownr); checkWritable(); if (columnDesc().isScalar()) { switch (columnDesc().dataType()) { case TpBool: putScalar (thisRownr, that.asBool (thatRownr)); break; case TpUChar: putScalar (thisRownr, that.asuChar (thatRownr)); break; case TpShort: putScalar (thisRownr, that.asShort (thatRownr)); break; case TpUShort: putScalar (thisRownr, that.asuShort (thatRownr)); break; case TpInt: putScalar (thisRownr, that.asInt (thatRownr)); break; case TpUInt: putScalar (thisRownr, that.asuInt (thatRownr)); break; case TpInt64: putScalar (thisRownr, that.asInt64 (thatRownr)); break; case TpFloat: putScalar (thisRownr, that.asfloat (thatRownr)); break; case TpDouble: putScalar (thisRownr, that.asdouble (thatRownr)); break; case TpComplex: putScalar (thisRownr, that.asComplex (thatRownr)); break; case TpDComplex: putScalar (thisRownr, that.asDComplex (thatRownr)); break; case TpString: putScalar (thisRownr, that.asString (thatRownr)); break; default: throw (TableInvDT ("TableColumn::put; invalid type promotion")); } }else{ if (! columnDesc().isArray()) { throw (TableInvDT ("TableColumn::put; no scalar or array")); } if (! that.columnDesc().isArray()) { throw (TableInvDT ("TableColumn::put; array types mismatch")); } if (that.isDefined (thatRownr)) { //#// If not defined, the this-value should be unset (if there is one). //#// However, this requires an undefine function, which is not there yet. //# Get the shape and define it for non-FixedShape arrays. //# Then get the data and put it depending on the type. IPosition shape = that.shape (thatRownr); if (preserveTileShape) { IPosition tileShape = that.tileShape (thatRownr); if (tileShape.empty()) { baseColPtr_p->setShape (thisRownr, shape); } else { baseColPtr_p->setShape (thisRownr, shape, tileShape); } } else if ((columnDesc().options() & ColumnDesc::FixedShape) != ColumnDesc::FixedShape) { baseColPtr_p->setShape (thisRownr, shape); } ValueHolder vh; switch (that.columnDesc().dataType()) { case TpBool: { Array array(shape); baseColPtr(that)->getArray (thatRownr, array); vh = ValueHolder (array); } break; case TpUChar: { Array array(shape); baseColPtr(that)->getArray (thatRownr, array); vh = ValueHolder (array); } break; case TpShort: { Array array(shape); baseColPtr(that)->getArray (thatRownr, array); vh = ValueHolder (array); } break; case TpUShort: { Array array(shape); baseColPtr(that)->getArray (thatRownr, array); vh = ValueHolder (array); } break; case TpInt: { Array array(shape); baseColPtr(that)->getArray (thatRownr, array); vh = ValueHolder (array); } break; case TpUInt: { Array array(shape); baseColPtr(that)->getArray (thatRownr, array); vh = ValueHolder (array); } break; case TpInt64: { Array array(shape); baseColPtr(that)->get (thatRownr, &array); vh = ValueHolder (array); } break; case TpFloat: { Array array(shape); baseColPtr(that)->getArray (thatRownr, array); vh = ValueHolder (array); } break; case TpDouble: { Array array(shape); baseColPtr(that)->getArray (thatRownr, array); vh = ValueHolder (array); } break; case TpComplex: { Array array(shape); baseColPtr(that)->getArray (thatRownr, array); vh = ValueHolder (array); } break; case TpDComplex: { Array array(shape); baseColPtr(that)->getArray (thatRownr, array); vh = ValueHolder (array); } break; case TpString: { Array array(shape); baseColPtr(that)->getArray (thatRownr, array); vh = ValueHolder (array); } break; default: throw (TableInvDT ("TableColumn::put of that column")); } switch (columnDesc().dataType()) { case TpBool: { Array arr (vh.asArrayBool()); baseColPtr_p->putArray (thisRownr, arr); } break; case TpUChar: { Array arr (vh.asArrayuChar()); baseColPtr_p->putArray (thisRownr, arr); } break; case TpShort: { Array arr (vh.asArrayShort()); baseColPtr_p->putArray (thisRownr, arr); } break; case TpUShort: { Array arr (vh.asArrayuShort()); baseColPtr_p->putArray (thisRownr, arr); } break; case TpInt: { Array arr (vh.asArrayInt()); baseColPtr_p->putArray (thisRownr, arr); } break; case TpUInt: { Array arr (vh.asArrayuInt()); baseColPtr_p->putArray (thisRownr, arr); } break; case TpInt64: { Array arr (vh.asArrayInt64()); baseColPtr_p->put (thisRownr, &arr); } break; case TpFloat: { Array arr (vh.asArrayFloat()); baseColPtr_p->putArray (thisRownr, arr); } break; case TpDouble: { Array arr (vh.asArrayDouble()); baseColPtr_p->putArray (thisRownr, arr); } break; case TpComplex: { Array arr (vh.asArrayComplex()); baseColPtr_p->putArray (thisRownr, arr); } break; case TpDComplex: { Array arr (vh.asArrayDComplex()); baseColPtr_p->putArray (thisRownr, arr); } break; case TpString: { Array arr (vh.asArrayString()); baseColPtr_p->putArray (thisRownr, arr); } break; default: throw (TableInvDT ("TableColumn::put of this column")); } } } } //#// Currently this is a very dumb implementation. //# It should check if types are equal and take advantage of that. void TableColumn::putColumn (const TableColumn& that) { checkWritable(); rownr_t nrrow = nrow(); if (nrrow != that.nrow()) { throw (TableConformanceError ("TableColumn::putColumn")); } for (rownr_t i=0; itableName() + " is not writable"); } Bool TableColumn::hasContent (rownr_t rownr) const { Bool retval = !isNull() && isDefined(rownr); if (retval && columnDesc().isArray()) { // The first cell seems to have something, but check for // degenerate Arrays. IPosition shp(shape(rownr)); if (shp.empty()) { retval = False; } else { for (uInt i=0; i #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class Table; class BaseTable; //# Check the number of rows in debug mode. #if defined(AIPS_DEBUG) # define TABLECOLUMNCHECKROW(ROWNR) \ (checkRowNumber (ROWNR)) #else # define TABLECOLUMNCHECKROW(ROWNR) #endif // // Read/write access to a table column // // // // // //
      • Table //
      • ColumnDesc // // // The class TableColumn gives read and write access to a column // in a table. In particular access to the column description // (for name, data type, etc.) and to the column keyword set // can be obtained. // Another important function is isDefined, which tests if a // cell (i.e. table row) in a column contains a value. // // The classes ScalarColumn and ArrayColumn have to be // used to get/put the data in the column cells. // However, TableColumn has get functions for the basic data types // (Bool, uChar, Short, uSort, Int, uInt, Int64, float, double, // Complex, DComplex and String). // Opposite to the get functions in ScalarColumn, the // TableColumn get functions support data type promotion. // // A default constructor is defined to allow construction of an array // of TableColumn objects. However, this constructs an object not // referencing a column. Functions like get, etc. will fail (i.e. result // in a segmentation fault) when used on such objects. The functions // isNull and throwIfNull can be used to test on this. // The functions attach and reference can fill in the object. // // // See module Tables. // class TableColumn { friend class ForwardColumn; //# for function baseColPtr() public: // The default constructor creates a null object, i.e. it // does not reference a table column. // The sole purpose of this constructor is to allow construction // of an array of TableColumn objects. // The functions reference and attach can be used to make a null object // reference a column. // Note that get functions, etc. will cause a segmentation fault // when operating on a null object. It was felt it was too expensive // to test on null over and over again. The user should use the isNull // or throwIfNull function in case of doubt. TableColumn(); // Construct the object for a column in the table using its name. TableColumn (const Table&, const String& columnName); // Construct the object for a column in the table using its index. // This allows to loop through all columns in a table as: // // for (uInt=0; i TableColumn (const Table&, uInt columnIndex); // Copy constructor (reference semantics). TableColumn (const TableColumn&); virtual ~TableColumn(); // Assignment has reference semantics. // It copies the object, not the data of that column to this column. // Function putColumn can be used to copy the data of a column. //
        It does the same as the reference function. TableColumn& operator= (const TableColumn&); // Clone the object. virtual TableColumn* clone() const; // Change the reference to another column. // This is in fact an assignment operator with reference semantics. // It removes the reference to the current column and creates // a reference to the column referenced in the other object. // It will handle null objects correctly. void reference (const TableColumn&); // Attach a column to the object. // This is in fact only a shorthand for // <
        reference (TableColumn (table, columnName)); // void attach (const Table& table, const String& columnName) { reference (TableColumn (table, columnName)); } void attach (const Table& table, uInt columnIndex) { reference (TableColumn (table, columnIndex)); } // // Test if the object is null, i.e. does not reference a column. Bool isNull() const { return (baseColPtr_p == 0 ? True : False); } // Throw an exception if the object is null, i.e. // if function isNull() is True. void throwIfNull() const; // Test if the column can be written to, thus if the column and // the underlying table can be written to. Bool isWritable() const { return baseTabPtr_p->isWritable() && isColWritable_p; } // Test if the column is writable at all (virtual columns might not be). // Note that keywords can always be written, even for virtual columns. Bool isWritableAtAll() const { return isColWritable_p; } // Check if the column is writable and throw an exception if not. void checkWritable() const { if (!isWritable()) throwNotWritable(); } // Get readonly access to the column keyword set. const TableRecord& keywordSet() const { return baseColPtr_p->keywordSet(); } // Get read/write access to the column keyword set. // An exception is thrown if the table is not writable. TableRecord& rwKeywordSet(); // Get const access to the column description. // ColumnDesc functions have to be used to get the data type, etc.. const ColumnDesc& columnDesc() const; // Get the Table object this column belongs to. Table table() const; // Get the number of rows in the column. rownr_t nrow() const { return baseColPtr_p->nrow(); } // Can the shape of an already existing non-FixedShape array be changed? // This depends on the storage manager. Most storage managers // can handle it, but TiledDataStMan and TiledColumnStMan can not. Bool canChangeShape() const { return canChangeShape_p; } // Get the global #dimensions of an array (ie. for all cells in column). // This is always set for fixed shape arrays. // Otherwise, 0 will be returned. uInt ndimColumn() const { return baseColPtr_p->ndimColumn(); } // Get the global shape of an array (ie. for all cells in the column). // This is always set for fixed shape arrays. // Otherwise, a 0-dim shape will be returned. IPosition shapeColumn() const { return baseColPtr_p->shapeColumn(); } // Test if the given cell contains a defined value. Bool isDefined (rownr_t rownr) const { TABLECOLUMNCHECKROW(rownr); return baseColPtr_p->isDefined (rownr); } // Does the column has content in the given row (default is the first row)? // It has if it is defined and does not contain an empty array. Bool hasContent (rownr_t rownr=0) const; // Get the #dimensions of an array in a particular cell. uInt ndim (rownr_t rownr) const { TABLECOLUMNCHECKROW(rownr); return baseColPtr_p->ndim (rownr); } // Get the shape of an array in a particular cell. IPosition shape (rownr_t rownr) const { TABLECOLUMNCHECKROW(rownr); return baseColPtr_p->shape (rownr); } // Get the tile shape of an array in a particular cell. IPosition tileShape (rownr_t rownr) const { TABLECOLUMNCHECKROW(rownr); return baseColPtr_p->tileShape (rownr); } // Get the value of a scalar in the given row. // Data type promotion is possible. // These functions only work for the standard data types. // void getScalar (rownr_t rownr, Bool& value) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr, value); } void getScalar (rownr_t rownr, uChar& value) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr, value); } void getScalar (rownr_t rownr, Short& value) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr, value); } void getScalar (rownr_t rownr, uShort& value) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr, value); } void getScalar (rownr_t rownr, Int& value) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr, value); } void getScalar (rownr_t rownr, uInt& value) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr, value); } void getScalar (rownr_t rownr, Int64& value) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr, value); } void getScalar (rownr_t rownr, float& value) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr, value); } void getScalar (rownr_t rownr, double& value) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr, value); } void getScalar (rownr_t rownr, Complex& value) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr, value); } void getScalar (rownr_t rownr, DComplex& value) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr, value); } void getScalar (rownr_t rownr, String& value) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr, value); } // // Get the value from the row and convert it to the required type. // This can only be used for scalar columns with a standard data type. // Bool asBool (rownr_t rownr) const; uChar asuChar (rownr_t rownr) const; Short asShort (rownr_t rownr) const; uShort asuShort (rownr_t rownr) const; Int asInt (rownr_t rownr) const; uInt asuInt (rownr_t rownr) const; Int64 asInt64 (rownr_t rownr) const; float asfloat (rownr_t rownr) const; double asdouble (rownr_t rownr) const; Complex asComplex (rownr_t rownr) const; DComplex asDComplex (rownr_t rownr) const; String asString (rownr_t rownr) const; // // Get the value of a scalar in the given row. // These functions work for all data types. // Data type promotion is possible for the standard data types. // The functions are primarily meant for ScalarColumn. // void getScalarValue (rownr_t rownr, Bool* value, const String&) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr,*value); } void getScalarValue (rownr_t rownr, uChar* value, const String&) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr,*value); } void getScalarValue (rownr_t rownr, Short* value, const String&) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr,*value); } void getScalarValue (rownr_t rownr, uShort* value, const String&) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr,*value); } void getScalarValue (rownr_t rownr, Int* value, const String&) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr,*value); } void getScalarValue (rownr_t rownr, uInt* value, const String&) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr,*value); } void getScalarValue (rownr_t rownr, Int64* value, const String&) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr,*value); } void getScalarValue (rownr_t rownr, float* value, const String&) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr,*value); } void getScalarValue (rownr_t rownr, double* value, const String&) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr,*value); } void getScalarValue (rownr_t rownr, Complex* value, const String&) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr,*value); } void getScalarValue (rownr_t rownr, DComplex* value, const String&) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr,*value); } void getScalarValue (rownr_t rownr, String* value, const String&) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr,*value); } void getScalarValue (rownr_t rownr, void* value, const String& dataTypeId) const { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->getScalar (rownr,value,dataTypeId); } // // Copy the value of a cell of that column to a cell of this column. // This function only works for the standard data types. // Data type promotion will be done if needed. // An exception is thrown if this column is not writable or if // the data cannot be converted. // // Use the same row numbers for both cells. void put (rownr_t rownr, const TableColumn& that, Bool preserveTileShape=False) { put (rownr, that, rownr, preserveTileShape); } // Use possibly different row numbers for that (i.e. input) and // and this (i.e. output) cell. virtual void put (rownr_t thisRownr, const TableColumn& that, rownr_t thatRownr, Bool preserveTileShape=False); // // Copy the values of that column to this column. // The numbers of rows in both columns must be equal. // Data type promotion is possible. // An exception is thrown if the data cannot be converted. // This function is useful to copy one column to another without // knowing their data types. // In fact, this function is an assignment operator with copy semantics. void putColumn (const TableColumn& that); // Put the value of a scalar in the given row. // Data type promotion is possible. // These functions only work for the standard data types. // void putScalar (rownr_t rownr, const Bool& value) { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->putScalar (rownr, value); } void putScalar (rownr_t rownr, const uChar& value) { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->putScalar (rownr, value); } void putScalar (rownr_t rownr, const Short& value) { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->putScalar (rownr, value); } void putScalar (rownr_t rownr, const uShort& value) { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->putScalar (rownr, value); } void putScalar (rownr_t rownr, const Int& value) { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->putScalar (rownr, value); } void putScalar (rownr_t rownr, const uInt& value) { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->putScalar (rownr, value); } void putScalar (rownr_t rownr, const Int64& value) { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->putScalar (rownr, value); } void putScalar (rownr_t rownr, const float& value) { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->putScalar (rownr, value); } void putScalar (rownr_t rownr, const double& value) { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->putScalar (rownr, value); } void putScalar (rownr_t rownr, const Complex& value) { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->putScalar (rownr, value); } void putScalar (rownr_t rownr, const DComplex& value) { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->putScalar (rownr, value); } void putScalar (rownr_t rownr, const String& value) { TABLECOLUMNCHECKROW(rownr); baseColPtr_p->putScalar (rownr, value); } void putScalar (rownr_t rownr, const Char* value) { putScalar (rownr, String(value)); } // // Check if the row number is valid. // It throws an exception if out of range. void checkRowNumber (rownr_t rownr) const { baseTabPtr_p->checkRowNumber (rownr); } // Set the maximum cache size (in bytes) to be used by a storage manager. void setMaximumCacheSize (uInt nbytes) const { baseColPtr_p->setMaximumCacheSize (nbytes); } protected: BaseTable* baseTabPtr_p; BaseColumn* baseColPtr_p; //# pointer to real column object const ColumnCache* colCachePtr_p; Bool canChangeShape_p; Bool isColWritable_p; //# is the column writable at all? // Get the baseColPtr_p of this TableColumn object. BaseColumn* baseColPtr () const { return baseColPtr_p; } // Get the baseColPtr_p of another TableColumn object. // This is needed for function put, because baseColPtr_p is a // protected member of TableColumn. Another TableColumn has // no access to that. BaseColumn* baseColPtr (const TableColumn& that) const { return that.baseColPtr_p; } private: // Throw the exception that the column is not writable. void throwNotWritable() const; }; // Define ROTableColumn for backward compatibility. typedef TableColumn ROTableColumn; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/TableCopy.cc000066400000000000000000000217041476623553700202050ustar00rootroot00000000000000//# TableCopy.h: Class with static functions for copying a table //# Copyright (C) 2001,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN Table TableCopy::makeEmptyTable (const String& newName, const Record& dataManagerInfo, const Table& tab, Table::TableOption option, Table::EndianFormat endianFormat, Bool replaceTSM, Bool noRows, const StorageOption& stopt) { TableDesc tabDesc = tab.actualTableDesc(); Record dminfo (dataManagerInfo); if (dminfo.nfields() == 0) { // No new dminfo given, so use existing. dminfo = tab.dataManagerInfo(); } else { // Set data manager group in description to actual group. // Also remove possible obsolete hypercolumn definitions. DataManInfo::adjustDesc (tabDesc, dminfo); } if (replaceTSM) { // Replace possible usage of TiledDataStMan by TiledShapeStMan. DataManInfo::adjustTSM (tabDesc, dminfo); } // Replace non-writable storage managers by StandardStMan. // This is for instance needed for LofarStMan. dminfo = DataManInfo::adjustStMan (dminfo, "StandardStMan", True); SetupNewTable newtab (newName, tabDesc, option, stopt); newtab.bindCreate (dminfo); return Table(newtab, (noRows ? 0 : tab.nrow()), False, endianFormat); } Table TableCopy::makeEmptyMemoryTable (const String& newName, const Table& tab, Bool noRows) { TableDesc tabDesc = tab.actualTableDesc(); Record dminfo = tab.dataManagerInfo(); SetupNewTable newtab (newName, tabDesc, Table::New); newtab.bindCreate (dminfo); return Table(newtab, Table::Memory, (noRows ? 0 : tab.nrow())); } void TableCopy::copyRows (Table& out, const Table& in, rownr_t startout, rownr_t startin, rownr_t nrrow, Bool flush) { // Check if startin and nrrow are correct for input. if (startin + nrrow > in.nrow()) { throw TableError ("TableCopy: startin+nrrow exceed nr of input rows"); } // Get all columns in the output table. // If there are multiple columns, only take the stored ones. TableRow outrow(out, out.tableDesc().ncolumn() > 1); Vector columns = outrow.columnNames(); const TableDesc& tdesc = in.tableDesc(); // Only copy the columns that exist in the input table. Vector cols(columns.nelements()); uInt nrcol = 0; for (uInt i=0; i 0) { cols.resize (nrcol, True); // Add rows as needed. if (startout + nrrow > out.nrow()) { out.addRow (startout + nrrow - out.nrow()); } ROTableRow inrow(in, cols); outrow = TableRow(out, cols); for (rownr_t i=0; i& omit) { copySubTables (out.rwKeywordSet(), in.keywordSet(), out.tableName(), out.tableType(), in, noRows, omit); const TableDesc& outDesc = out.tableDesc(); const TableDesc& inDesc = in.tableDesc(); for (uInt i=0; i& omit) { for (uInt i=0; i= 0) { continue; } // Lock the subtable in case not locked yet. // Note it will keep the lock if already locked. TableLocker locker(inTab, FileLocker::Read); // If the table to be copied has the same root as the main input table, // we do not make a copy. This is needed to avoid the recursive copy // of SORTED_TABLE in a MeasurementSet. if (inTab.isSameRoot (in)) { String keyName = inKeys.name(i); if (outKeys.isDefined (keyName)) { outKeys.removeField (keyName); } } else { String newName = outName + '/' + Path(inTab.tableName()).baseName(); Table outTab; if (outType == Table::Memory) { outTab = inTab.copyToMemoryTable (newName, noRows); } else { inTab.deepCopy (newName, Table::New, False, Table::AipsrcEndian, noRows); outTab = Table(newName); } outKeys.defineTable (inKeys.name(i), outTab); } } } } void TableCopy::cloneColumn (const Table& fromTable, const String& fromColumn, Table& toTable, const String& newColumn, const String& dataManagerName, const Record& newdmInfo) { // Use existing column description and give it the new name. ColumnDesc cd(fromTable.tableDesc()[fromColumn]); cd.setName (newColumn); doCloneColumn (fromTable, fromColumn, toTable, cd, dataManagerName, newdmInfo); } void TableCopy::doCloneColumn (const Table& fromTable, const String& fromColumn, Table& toTable, const ColumnDesc& newColumn, const String& dataManagerName, const Record& newdmInfo) { // Use existing column description and give it the new name. TableDesc td; td.addColumn (newColumn); // Get datamanager info of source column. Block selcol(1); selcol[0] = fromColumn; // Use the given datamanager info; if empty use that of input column. Record dminfo(newdmInfo); if (dminfo.nfields() == 0) { // Do the selection to only get dminfo of source column. Table t1(fromTable.project (selcol)); dminfo = t1.dataManagerInfo(); // Set the datamanager name if not given. String dmName (dataManagerName); if (dmName.empty()) { dmName = newColumn.name(); } // Adjust the dminfo. // It has a subrecord per dataman, thus in this case only 1. Record& rec = dminfo.rwSubRecord(0); rec.define ("COLUMNS", Vector(1, newColumn.name())); rec.define ("NAME", dmName); } // Now add the column. toTable.addColumn (td, dminfo); } void TableCopy::copyColumnData (const Table& tabFrom, const String& colFrom, Table& tabTo, const String& colTo, Bool preserveTileShape) { AlwaysAssert (tabFrom.nrow() == tabTo.nrow(), AipsError); TableColumn incol(tabFrom, colFrom); TableColumn outcol(tabTo, colTo); for (rownr_t i=0; i #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Class with static functions for copying a table. // // // // // //# Classes you should understand before using this one. //
      • Table // // // TableCopy is a class for making a deep copy of a table. // The table can be a PlainTable or a RefTable. // It contains the following static functions: //
          //
        1. makeEmptyTable creates a new table using the // description and storage managers of the input table. // By default TiledDataStMan (which is more or less obsolete) will // be replaced by TiledShapeStMan. // By default the new table contains the same number of rows as the // existing table. //
        2. copyRows copies the data of one to another table. // It is possible to specify where to start in the input and output. //
        3. CopyInfo copies the table info data. //
        4. copySubTables copies all the subtables in table and // column keywords. It is done recursively. //
        //
        //# //# class TableCopy { public: // Make an (empty) table with the given description. // If the description contains no columns, the description of the input // table is used, so it has the same keywords and columns as the input one. // The data managers can be given in the dataManagerInfo record. // If it is empty, the info is taken from the input table. //
        Non-writable storage managers (like LofarStMan) are by default replaced // by StandardStMan. If replaceMSM is set, MemoryStMan is also // replaced by StandardStMan. //
        By default, the TiledDataStMan will be replaced by the TiledShapeStMan. //
        By default, the new table has the same nr of rows as the input table. // If noRows=True is given, it does not contain any row. static Table makeEmptyTable (const String& newName, const Record& dataManagerInfo, const Table& tab, Table::TableOption option, Table::EndianFormat endianFormat, Bool replaceTSM = True, Bool noRows = False, const StorageOption& = StorageOption()); // Make an (empty) memory table with the same layout as the input one. // It has the same keywords and columns as the input one. // By default, the new table has the same nr of rows as the input table. // If noRows=True is given, it does not contain any row. static Table makeEmptyMemoryTable (const String& newName, const Table& tab, Bool noRows = False); // Copy rows from the input to the output. // By default all rows will be copied starting at row 0 of the output. // Rows will be added to the output table as needed. // The output table will by default be flushed after the rows are copied. //
        All columns in Table out will be filled from the // column with the same name in table in. In principle only // stored columns will be filled; however if the output table has only // one column, it can also be a virtual one. // static void copyRows (Table& out, const Table& in, Bool flush=True) { copyRows (out, in, 0, 0, in.nrow(), flush); } static void copyRows (Table& out, const Table& in, rownr_t startout, rownr_t startin, rownr_t nrrow, Bool flush=True); // // Copy the table info block from input to output table. static void copyInfo (Table& out, const Table& in); // Copy all subtables (in table and column keywords) from input to // output table. // Subtables of which the keyword name matches an omit value are skipped. // Optionally the row contents are not copied. static void copySubTables (Table& out, const Table& in, Bool noRows=False, const Block& omit=Block()); // Copy the subtables in the given keywordset to the output keywordset // in the table with the given name. // Subtables of which the keyword name matches an omit value are skipped. // Optionally the row contents are not copied. static void copySubTables (TableRecord& outKeys, const TableRecord& inKeys, const String& outName, Table::TableType outType, const Table& in, Bool noRows=False, const Block& omit=Block()); // Clone a column in the from table to a new column in the to table. // The new column gets the same table description as the source column. // If newdmInfo is empty, the same data manager type as the source column is used. // It has to have a unique data manager name. If not given, it is the new column name. static void cloneColumn (const Table& fromTable, const String& fromColumn, Table& toTable, const String& newColumn, const String& dataManagerName = String(), const Record& newdmInfo = Record()); // Cloning as above, but the data type is set to the template parameter. template static void cloneColumnTyped (const Table& fromTable, const String& fromColumn, Table& toTable, const String& newColumn, const String& dataManagerName = String(), const Record& newdmInfo = Record()); // Copy the data from one column to another. // It can be used after function cloneColumn to populate the new column. // Note that the data types of the column do not need to match; data type // promotion is done if needed. //
        The preserveTileShape argument tells if the original // tile shape is kept if a tiled data manager is used. If False, the // default tile shape of the data manager is used. // // Note that a TaQL command can be used to fill a column in any way. // For example, fill toColumn with the real part of a complex fromColumn: // // Block
      • tables(2); // tables[0] = toTable; // tables[1] = fromTable; // tableCommand ("update $1 set toColumn=real(t2.fromColumn) from $2 t2", // tables); // // When copying a column in a straightforward way, the TaQL way is about 25% // slower than using the function copyColumnData. // static void copyColumnData (const Table& fromTable, const String& fromColumn, Table& toTable, const String& toColumn, Bool preserveTileShape=True); // Fill the table column with the given array. // The template type must match the column data type. template static void fillArrayColumn (Table& table, const String& column, const Array& value); // Fill the table column with the given value. // If the column contains arrays, the arrays are filled with the value. // The template type must match the column data type. template static void fillColumnData (Table& table, const String& column, const T& value); // Specialization to handle a C-string correctly. static void fillColumnData (Table& table, const String& column, const char* value) { fillColumnData (table, column, String(value)); } // Fill the table column with the given value. // The column must contain arrays. The arrays get the shape of the // corresponding row in the fromColumn in the fromTable. // It can be used after function cloneColumn to initialize the new column. // The template type must match the column data type. template static void fillColumnData (Table& table, const String& column, const T& value, const Table& fromTable, const String& fromColumn, Bool preserveTileShape=True); // Specialization to handle a C-string correctly. static void fillColumnData (Table& table, const String& column, const char* value, const Table& fromTable, const String& fromColumn, Bool preserveTileShape=True) { fillColumnData (table, column, String(value), fromTable, fromColumn, preserveTileShape); } private: static void doCloneColumn (const Table& fromTable, const String& fromColumn, Table& toTable, const ColumnDesc& newColumn, const String& dataManagerName, const Record& newdmInfo); }; } //# NAMESPACE CASACORE - END #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/tables/Tables/TableCopy.tcc000066400000000000000000000106061476623553700203700ustar00rootroot00000000000000//# TableCopy.tcc: Class with static functions for copying a table //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABLECOPY_TCC #define TABLES_TABLECOPY_TCC //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN template void TableCopy::cloneColumnTyped (const Table& fromTable, const String& fromColumn, Table& toTable, const String& newColumn, const String& dataManagerName, const Record& newdmInfo) { // Get existing column description. ColumnDesc cd(fromTable.tableDesc()[fromColumn]); if (cd.isScalar()) { ScalarColumnDesc scd(newColumn, cd.comment(), cd.dataManagerType(), cd.dataManagerGroup(), T(), cd.options()); cd = ColumnDesc(scd); } else { ArrayColumnDesc acd(newColumn, cd.comment(), cd.dataManagerType(), cd.dataManagerGroup(), cd.shape(), cd.options(), cd.ndim()); cd = ColumnDesc(acd); } doCloneColumn (fromTable, fromColumn, toTable, cd, dataManagerName, newdmInfo); } template void TableCopy::fillArrayColumn (Table& table, const String& column, const Array& value) { ArrayColumn acol(table, column); acol.fillColumn (value); } template void TableCopy::fillColumnData (Table& table, const String& column, const T& value) { TableColumn col(table, column); if (col.columnDesc().isScalar()) { ScalarColumn scol(col); scol.fillColumn (value); } else { // Fill the array in each row with the value. TableCopy::fillColumnData (table, column, value, table, column); } } template void TableCopy::fillColumnData (Table& table, const String& column, const T& value, const Table& fromTable, const String& fromColumn, Bool preserveTileShape) { TableColumn fromCol(fromTable, fromColumn); AlwaysAssert (fromCol.columnDesc().isArray(), AipsError); Array arr; ArrayColumn toCol(table, column); for (rownr_t i=0; i #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# This is the implementation of the class TableDesc. //# //# It uses the class TabPath to find which directory contains //# the table description file. //# Define the prefix to use for keywords representing hypercolumns. static const String theHyperPrefix ("Hypercolumn_"); TableDesc::TableDesc() : option_p (Scratch) { init (TabPath()); // use default search path } TableDesc::TableDesc (const String& nam, TDOption opt) : name_p (nam), option_p (opt) { init (TabPath()); // use default search path } TableDesc::TableDesc (const String& nam, const String& version, TDOption opt) : name_p (nam), vers_p (version), option_p (opt) { init (TabPath()); // use default search path } TableDesc::TableDesc (const String& nam, const String& version, const TabPath& tdpath, TDOption opt) : name_p (nam), vers_p (version), option_p (opt) { init (tdpath); // use given search path } TableDesc::TableDesc (const TableDesc& td, const String& nam, const String& version, TDOption opt, Bool copyColumns) : name_p (nam), vers_p (version), option_p (opt) { copy (td, TabPath(), copyColumns); // use default search path } TableDesc::TableDesc (const TableDesc& td, const String& nam, const String& version, const TabPath& tdpath, TDOption opt, Bool copyColumns) : name_p (nam), vers_p (version), option_p (opt) { copy (td, tdpath, copyColumns); // use given search path } TableDesc::TableDesc (const TableDesc& td, TDOption opt) : option_p (opt) { copy (td, TabPath(), True); // use default search path } TableDesc::~TableDesc () { //# Write if the description if opened for output and if the //# description can be written. //# Delete will be done by destructor of AipsIO. if (swwrite_p) { if (option_p == Update || option_p == New || option_p == NewNoReplace) { putFile (iofil_p, TableAttr()); // write the description } } iofil_p.close (); delete key_p; delete privKey_p; } // //
      • TableDescNoName //
      • TableDuplFile //
      • TableNoFile //
      • TableInvOpt // void TableDesc::init (const TabPath& tdpath) { //# Initialize some variables. swwrite_p = False; // writing is not possible yet //# If non-scratch, check if name is not blank and look if the //# description already exists. if (option_p == Scratch) { dir_p = "**SCRATCH**"; }else{ if (name_p.empty()) { throw TableDescNoName(); } Bool exsw = tdpath.found (name_p + ".tabdsc", dir_p); if (option_p == NewNoReplace) { if (exsw) { throw (TableDuplFile("desc. " + name_p));// table already exists } }else{ if (option_p != New && !exsw) { throw (TableNoFile("desc." + name_p)); // table does not exist } } } //# Determine the AipsIO option for this file. ByteIO::OpenOption fopt = ByteIO::Old; switch (option_p) { case TableDesc::Old: fopt = ByteIO::Old; break; case TableDesc::New: fopt = ByteIO::New; break; case TableDesc::NewNoReplace: fopt = ByteIO::NewNoReplace; break; case TableDesc::Scratch: fopt = ByteIO::Scratch; break; case TableDesc::Update: fopt = ByteIO::Update; break; case TableDesc::Delete: fopt = ByteIO::Delete; break; default: throw (TableInvOpt ("TableDesc", "must be Old, New, NewNoReplace, Scratch, Update or Delete")); } //# Allocate the keyword sets. key_p = new TableRecord(); privKey_p = new TableRecord(); //# If non-scratch, open the file. Read it for new, update and delete. //# It can be closed immediately when old (i.e readonly). if (option_p != Scratch) { iofil_p.open (dir_p + name_p + ".tabdsc", fopt); if (option_p == Old || option_p == Update || option_p == Delete) { getFile (iofil_p, TableAttr()); // read file } if (option_p == Old || option_p == Update) { iofil_p.close (); if (option_p == Update) { iofil_p.open (dir_p + name_p + ".tabdsc", fopt); // reposition } } } swwrite_p = True; // writing is possible now } // //
      • TableInvOpt // void TableDesc::copy (const TableDesc& td, const TabPath& tdpath, Bool copyColumns) { //# Check the options; it has to be a new description. if (option_p != New && option_p != NewNoReplace && option_p != Scratch) { throw (TableInvOpt ("TableDesc", "must be New, NewNoReplace or Scratch")); } init (tdpath); if (name_p.empty()) { name_p = td.name_p; } if (vers_p.empty()) { vers_p = td.vers_p; } comm_p = td.comm_p; *key_p = *(td.key_p); *privKey_p = *(td.privKey_p); if (copyColumns) { col_p = td.col_p; } } // Test if a description exists. Bool TableDesc::isReadable (const String& tableDescName) { File file(tableDescName + ".tabdsc"); return file.isReadable(); } // Get a vector with all column names. Vector TableDesc::columnNames() const { Vector names(ncolumn()); for (uInt i=0; idescription().isDisjoint (that.privKey_p->description())){ throw (TableInvOper ("TableDesc::add; hypercolumns not disjoint")); } if (addKeywordSet) { if (! key_p->description().isDisjoint (that.key_p->description())) { throw (TableInvOper ("TableDesc::add; keywords not disjoint")); } } col_p.add (that.col_p); privKey_p->merge (*that.privKey_p, RecordInterface::ThrowOnDuplicates); if (addKeywordSet) { key_p->merge (*that.key_p, RecordInterface::ThrowOnDuplicates); } } //# Show the table. void TableDesc::show () const { show (cout); } void TableDesc::show (ostream& os) const { os << endl; os << "TableDesc " << name_p; os << " version " << vers_p; os << " (Directory " << dir_p << ")"; os << endl; os << "---------" << endl; os << " Comment: " << comm_p << endl; os << " #Keywords = " << key_p->nfields() << endl;; os << key_p->description(); os << " #Columns = " << ncolumn() << endl;; os << privKey_p->description(); col_p.show (os); } void TableDesc::putFile (AipsIO& ios, const TableAttr& parentAttr) const { ios.putstart ("TableDesc", 2); ios << name_p; ios << vers_p; ios << comm_p; key_p->putRecord (ios, parentAttr); ios << *privKey_p; col_p.putFile (ios, parentAttr); ios.putend (); } void TableDesc::getFile (AipsIO& ios, const TableAttr& parentAttr) { uInt tvers = ios.getstart ("TableDesc"); ios >> name_p; ios >> vers_p; ios >> comm_p; key_p->getRecord (ios, parentAttr); // Version 1 does not contain privKey_p. if (tvers != 1) { ios >> *privKey_p; } col_p.getFile (ios, parentAttr); ios.getend (); } //# Rename a column. void TableDesc::renameColumn (const String& newname, const String& oldname) { // First rename the column itself. col_p.rename (newname, oldname); // Now adjust the hypercolumn descriptions. std::map old2new; // First fill the map with all columns and replace it for the new name. for (uInt i=0; i& dataColumnNames) { Vector columnNames; defineHypercolumn (hypercolumnName, ndim, dataColumnNames, columnNames, columnNames); } void TableDesc::defineHypercolumn (const String& hypercolumnName, uInt ndim, const Vector& dataColumnNames, const Vector& coordColumnNames) { Vector columnNames; defineHypercolumn (hypercolumnName, ndim, dataColumnNames, coordColumnNames, columnNames); } void TableDesc::defineHypercolumn (const String& hypercolumnName, uInt ndim, const Vector& dataColumnNames, const Vector& coordColumnNames, const Vector& idColumnNames) { // Check if data and coordinate columns have been given. if (dataColumnNames.nelements() < 1) { throwHypercolumn (hypercolumnName, "dataColumnNames is empty"); } if (ndim < 1) { throwHypercolumn (hypercolumnName, "ndim < 1"); } uInt ncoord = coordColumnNames.nelements(); if (ncoord != 0 && ncoord != ndim) { throwHypercolumn (hypercolumnName, "#coordColumnNames mismatches ndim"); } uInt i, j; // Check if the coordinate columns exist and are numeric // scalars or vectors. An empty coordinate name is allowed meaning // that the axis has no coordinate. // Get the number of vectors. uInt firstCoordSca = 0; uInt lastCoordVec = 0; for (i=0; i 0 && lastCoordVec > firstCoordSca) { throwHypercolumn (hypercolumnName, "coordinate vectors have to describe the first axes"); } // Check if the data columns exist and have fixed length // (i.e. no String, Table or Other type). // Also check if their dimensionality matches the number of // coordinate vectors (if coordinates are defined). // Find out if all data columns have FixedShape. Bool fixedShape = True; uInt cellNdim = 0; for (i=0; i 0 && firstCoordSca <= cellNdim) || lastCoordVec > cellNdim) { throwHypercolumn (hypercolumnName, "the dimensionality of the data columns" " mismatches nr of coordinate columns with a" " vector value"); } // Check if the ID columns exist and are scalars (not type Other). // Type (u)Char and (u)Short are not possible. for (i=0; i names(nr); names(Slice(0,dataColumnNames.nelements())) = dataColumnNames; nr = dataColumnNames.nelements(); names(Slice(nr,coordColumnNames.nelements())) = coordColumnNames; nr += coordColumnNames.nelements(); names(Slice(nr,idColumnNames.nelements())) = idColumnNames; nr += idColumnNames.nelements(); for (i=0; idefineRecord (keyName, set); // Set the default data manager to TiledShapeStMan or TiledColumnStMan. // (use TiledColumnStMan if all data columns have FixedShape). // Set default data manager group to hypercolumn name. String dmName = (fixedShape ? "TiledColumnStMan" : "TiledShapeStMan"); for (i=0; iisDefined (theHyperPrefix + name); } Vector TableDesc::hypercolumnNames() const { uInt i; uInt nhyp = 0; uInt nkey = privKey_p->nfields(); for (i=0; itype(i) == TpRecord) { const String& key = privKey_p->description().name (i); if (key.index (theHyperPrefix) == 0) { nhyp++; } } } Vector result(nhyp); if (nhyp > 0) { nhyp = 0; for (i=0; itype(i) == TpRecord) { const String& key = privKey_p->description().name (i); if (key.index (theHyperPrefix) == 0) { result(nhyp) = String(key).from (int(theHyperPrefix.length())); nhyp++; } } } } return result; } uInt TableDesc::hypercolumnDesc (const String& name, Vector& dataColumnNames, Vector& coordColumnNames, Vector& idColumnNames) const { const TableRecord& set = privKey_p->subRecord (theHyperPrefix + name); // Make vectors empty, so assignment will be possible. dataColumnNames.resize (0); coordColumnNames.resize (0); idColumnNames.resize (0); dataColumnNames = set.asArrayString ("data"); coordColumnNames = set.asArrayString ("coord"); idColumnNames = set.asArrayString ("id"); return set.asuInt ("ndim"); } void TableDesc::adjustHypercolumns (const std::map& old2new, Bool keepUnknownData, Bool keepUnknownCoord, Bool keepUnknownId) { Vector hcNames = hypercolumnNames(); Vector dataNames, coordNames, idNames; for (uInt i=0; iremoveField (theHyperPrefix + hcNames(i)); // Rename/remove columns in the hypercolumn description. uInt nr = 0; for (uInt j=0; j::const_iterator iter = old2new.find (dataNames(j)); if (iter != old2new.end()) { dataNames(nr++) = iter->second; } else if (keepUnknownData) { nr++; } } // If no data columns left, there is no need to recreate the hypercolumn. if (nr > 0) { dataNames.resize (nr, True); nr = 0; for (uInt j=0; j::const_iterator iter = old2new.find (dataNames(j)); if (iter != old2new.end()) { coordNames(nr++) = iter->second; } else if (keepUnknownCoord) { nr++; } } // All coordinate columns are needed, so removal of one means // that they cannot be used anymore. // That also means their default storage manager has to be reset. if (nr != ndim) { for (uInt j=0; j::const_iterator iter = old2new.find (dataNames(j)); if (iter != old2new.end()) { idNames(nr++) = iter->second; } else if (keepUnknownId) { nr++; } } idNames.resize (nr, True); // Add the hypercolumn again. defineHypercolumn (hcNames(i), ndim, dataNames, coordNames, idNames); } } } void TableDesc::removeIDhypercolumns (const Vector& hcNames) { Vector dataNames, coordNames, idNames; for (uInt i=0; i 0) { for (uInt j=0; jremoveField (theHyperPrefix + hcNames(i)); defineHypercolumn (hcNames(i), ndim, dataNames, coordNames); } } } void TableDesc::removeHypercolumnDesc (const String& hypercolumnName) { if (! isHypercolumn(hypercolumnName)) { throw TableError ("Hypercolumn " + hypercolumnName + " does not exist, thus cannot be removed"); } // Remove all hypercolumns; start at the end to avoid invalid indices. privKey_p->removeField (theHyperPrefix + hypercolumnName); } void TableDesc::renameHypercolumn (const String& newHypercolumnName, const String& hypercolumnName) { if (! isHypercolumn(hypercolumnName)) { throw TableError ("Hypercolumn " + hypercolumnName + " does not exist, thus cannot be renamed"); } if (newHypercolumnName == "") { throw TableError ("New hypercolumn name must be non-empty"); } // Get hypercolumn description Vector dataNames, coordNames, idNames; uInt ndim = hypercolumnDesc (hypercolumnName, dataNames, coordNames, idNames); // delete the hypercolumn privKey_p->removeField (theHyperPrefix + hypercolumnName); // recreate it under new name (will also change the column descriptions) defineHypercolumn (newHypercolumnName, ndim, dataNames, coordNames, idNames); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/TableDesc.h000066400000000000000000000574251476623553700200240ustar00rootroot00000000000000//# TableDesc.h: specify structure of Casacore tables //# Copyright (C) 1994,1995,1996,1997,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABLEDESC_H #define TABLES_TABLEDESC_H //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableRecord; class TableAttr; class TabPath; // // Define the structure of a Casacore table // // // // // //
      • column description classes //
      • TableRecord // // // A TableDesc object contains the description, or structure, of a table. // This description is required for the creation of a new table. // Descriptions are subsequently associated with every table and // embedded in them. //
        The TableDesc class structure is shown in this // UML diagram. // // A table description consists of the following items: //
          //
        • Name, which cannot be blank if the description is saved in a file. // The file name will be this name followed by .tabdsc. //
        • Version, which defaults to a blank string. // It serves merely as information for the user. //
        • Comment, which defaults to an empty string. // This serves purely as an informational string for the user. //
        • A set of column descriptions which has to be added to the // table description. A column description can be created using // the classes ScalarColumnDesc, etc.. // At table creation it is determined by the user if a column // has to be stored using a storage manager or calculated // on-the-fly using a so-called virtual column engine. //
        • A keyword set, which is by default empty. // When a table is created from the description, it gets // a copy of this keyword set as its initial keyword set. //
        // // A TableDesc object can be constructed with one of the following // options: //
          //
        • Old // Open an existing table description file as readonly. //
        • Update // Open an existing table description file as read/write // The TableDesc destructor will rewrite the possibly changed // description. //
        • New // Create a new table description file. // The TableDesc destructor will write the table description into the file. //
        • NewNoReplace // As option New, but an exception will be thrown if the table // description file already exists. //
        • Scratch // Create a temporary table description. The table description will // be lost when the TableDesc object is destructed. // This is useful to create a Table object without storing the // description separately. // Note that the Table object maintains its own description (i.e. it // copies the description when being constructed). //
        • Delete // Delete the table description file. This gets done by the destructor. //
        // // More information is provided in the Tables module documentation. //
        // // // // First build the new description of a subtable. // // Define columns ra and dec (double). // TableDesc subTableDesc("tTableDesc_sub", "1", TableDesc::New); // subTableDesc.addColumn (ScalarColumnDesc("ra")); // subTableDesc.addColumn (ScalarColumnDesc("dec")); // // // Now create a new table description // // Define a comment for the table description. // // Define a double keyword. // ColumnDesc colDesc1, colDesc2; // TableDesc td("tTableDesc", "1", TableDesc::New); // td.comment() = "A test of class TableDesc"; // td.rwKeywordSet().define ("equinox", 1950.0); // // // Define an integer column ab using the TableDesc::addColumn // // function which creates a scalar column description. // td.addColumn (ScalarColumnDesc("ab", "Comment for column ab")); // // // Add a scalar integer column ac, define keywords for it // // and define a default value 0. // // Overwrite the value of keyword unit. // ScalarColumnDesc acColumn("ac"); // acColumn.rwKeywordSet().define ("scale", Complex(0.0f)); // acColumn.rwKeywordSet().define ("unit", ""); // acColumn.setDefault (0); // td.addColumn (acColumn); // td["ac"].rwKeywordSet().define ("unit", "DEG"); // // // Add a scalar string column ad and define its comment string. // td.addColumn (ScalarColumnDesc("ad","comment for ad")); // // // Now define array columns. // // This one is indirect and has no dimensionality mentioned yet. // td.addColumn (ArrayColumnDesc("Arr1","comment for Arr1")); // // This one is indirect and has 3-dim arrays. // td.addColumn (ArrayColumnDesc("A2r1","comment for Arr1",3)); // // This one is direct and has 2-dim arrays with axes length 4 and 7. // td.addColumn (ArrayColumnDesc("Arr3","comment for Arr1", // IPosition(2,4,7), // ColumnDesc::Direct)); // // // Add a columns containing tables. // td.addColumn (SubTableDesc("sub1", "subtable by name", // "tTableDesc_sub")); // // // Define hypercolumn "dataCube". // td.addColumn (ArrayColumnDesc("data",2)); // td.addColumn (ArrayColumnDesc("pol",1)); // td.addColumn (ArrayColumnDesc("freq",1)); // td.addColumn (ScalarColumnDesc("time")); // td.addColumn (ScalarColumnDesc("baseline")); // td.defineHypercolumn ("dataCube", 4, // stringToVector ("data"), // stringToVector ("pol,freq,time,baseline")); // } // // // // A table description specifies the structure, but not the contents, // of a Casacore table. Since many tables will have identical structure // and different content, it makes good sense to separate structure // ("description") from content. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class TableDesc { public: //# Enumerate the possible options for TableDesc. enum TDOption {Old=1, New, NewNoReplace, Scratch, Update, Delete}; // The default constructor creates a table description with // option = Scratch and a blank name. TableDesc(); // Create a table description object with the given name. // This name can be seen as the table type in the same way as a // class name is the data type of an object. // The name can only be blank when option=Scratch. // The default table description path is used for the description file. TableDesc (const String& type, TDOption = Old); // Create a table description object with the given name (i.e. table type) // and version. // The name can only be blank when option=Scratch. // The default table description path is used for the description file. TableDesc (const String& type, const String& version, TDOption = Old); // Create a table description object. // The given table description path is used for the description file. // The name can only be blank with option=Scratch. TableDesc (const String& type, const String& version, const TabPath&, TDOption = Old); // Create a table description object with the given name (i.e. table type) // and version by copying the input table description. // If the given name or version is blank, it will be copied from // the input table description. // The default table description path is used for the description file. // The only options allowed are New, NewNoReplace and Scratch. TableDesc (const TableDesc&, const String& type, const String& version, TDOption, Bool copyColumns=True); // Create a table description object with the given name (i.e. table type) // and version by copying the input table description. // If the given name or version is blank, it will be copied from // the input table description. // The given table description path is used for the description file. // The only options allowed are New, NewNoReplace and Scratch. TableDesc (const TableDesc&, const String& type, const String& version, const TabPath&, TDOption, Bool copyColumns=True); // This copy constructor makes a copy of the table description // maintaining its name and version. By default a Scratch copy is made. // It serves as a shorthand for the constructor: //
        TableDesc (const TableDesc&, "", "", TDOption); TableDesc (const TableDesc&, TDOption = Scratch); // The destructor writes the table description if changed. ~TableDesc(); // Assignment is not supported, because it is impossible to define // its semantics. Does the data need to be written into a file // before being overwritten? TableDesc& operator= (const TableDesc&) = delete; // Test if a description file exists (i.e. isReadable). static Bool isReadable (const String& tableDescName); // Get access to the set of column descriptions. // In this way const ColumnDescSet // functions (e.g. isDisjoint) can be used. const ColumnDescSet& columnDescSet() const; // Add another table description to this table description. // It merges the column descriptions, the special keywordSet // (containing hypercolumn definitions) and the user keywordSet // (this last one is not added if the flag is False). // The two table descriptions have to be disjoint, i.e. no column // nor keyword should already exist. Otherwise an TableInvOper // exception is thrown and nothing gets added. void add (const TableDesc& other, Bool addKeywordSet = True); // Get access to the keyword set. // TableRecord& rwKeywordSet(); const TableRecord& keywordSet() const; // // Get readonly access to the private set of keywords. const TableRecord& privateKeywordSet() const; // Add a column to the table description. // An exception is thrown if a keyword or column with this name // already exists. // Although this function has a ColumnDesc as argument, // it is usually needed to construct a more specialized object like // ArrayColumnDesc. A ColumnDesc // constructor converts that automatically to a ColumnDesc // object. // // tableDesc.addColumn (ArrayColumnDesc ("NAME")); // // On the other hand this function can also be used to add a // column description from another table as in: // // tableDesc.addColumn (otherTableDesc.columnDesc("NAME")); // ColumnDesc& addColumn (const ColumnDesc&); // Add a column to the table description and give it another name. // This may be useful to use a description of another column. ColumnDesc& addColumn (const ColumnDesc&, const String& newname); // Remove a column. // An exception is thrown if the column does not exist. void removeColumn (const String& name); // Rename a column. // An exception is thrown if the old name does not exist or // if the name already exists. // // Renaming a column should be done with care, because other // columns may be referring this column. Also a hypercolumn definition // might be using the old name. // void renameColumn (const String& newname, const String& oldname); // Get number of columns. uInt ncolumn() const; // Test if a column with this name exists. Bool isColumn (const String& name) const; // Get a vector containing all column names. Vector columnNames() const; // Get the column description by name or by index. // An exception is thrown if the column does not exist. // Function isColumn should be used to test if a column exists. // const ColumnDesc& columnDesc (const String& name) const; const ColumnDesc& operator[] (const String& name) const; const ColumnDesc& columnDesc (uInt index) const; const ColumnDesc& operator[] (uInt index) const; ColumnDesc& rwColumnDesc (const String& name); ColumnDesc& rwColumnDesc (uInt index); // // Get comment string. const String& comment() const; // Get comment string (allowing it to be changed). String& comment(); // Show the table description on cout. void show() const; // Show the table description. void show (ostream& os) const; // Get the table type (i.e. name of table description). const String& getType() const; // Get the table description version. const String& version() const; // Define a hypercolumn. // A hypercolumn is a group of one or more data columns of which // the data is treated as one or more (regular) hypercubes. // The hypercolumn has coordinate axes (e.g. time, frequency) // which are columns in the table. // When the entire hypercolumn consists of multiple hypercubes, // ID-columns can be defined, which uniquely determine the // hypercube to be used. // Note that only TiledDataStMan // requires the use of ID-columns. // A hypercolumn definition is needed to be able to use a Tiled // Storage Manager. // // The following has to be specified: //
        //
        Hypercolumn name //
        which is the name used to refer to the hypercolumn. //
        ndim //
        defining the dimensionality of the hypercolumn (and // of its hypercube(s)). //
        Data column names //
        which are the columns containing the hypercube data. // When multiple columns are used, the shapes of the data // in their cells must be the same in the same row. // All data columns must contain numeric or Bool scalars or arrays. //
        //
        array: //
        Its dimensionality has to be less than or equal to the // dimensionality of the hypercolumn. If equal, the // array itself already forms the hypercube. That would // mean that each row is a hypercube. // If less, the arrays from multiple rows form a hypercube, // adding one or more dimensions to the array dimensionality. //
        scalar: //
        The data from multiple rows form a hypercube. // Not all tiled storage managers support scalars. //
        //
        Coordinate column names (optional) //
        which are the columns containing the coordinates of the // hypercubes. They must be (u)Int, float, double or (D)Complex. // When given, the number of coordinate columns must match the // dimensionality of the hypercolumn. //
        // When the data column cells contain arrays, the first N coordinate // columns must contain vector values, where N is the dimensionality // of the data arrays. // The remaining coordinate columns must contain scalar values. //
        Id column names (optional) //
        have to be given when a hypercolumn can consist of multiple // hypercubes. They define the column(s) determining which // hypercube has to be used for a data array. // The id columns must contain scalar values ((u)Int, float, // double, (D)Complex, String and/or Bool). //
        // It will be checked if the given columns exists and have // an appropriate type. //
        // The default data manager type of the columns involved will be set // to TiledColumnStMan if all data columns have a fixed shape. // Otherwise they are set to TiledShapeStMan. // The storage manager group of all columns involved will be set to // the hypercolumn name. In that way binding columns to storage managers // during the table creation process is easier because a simple // bindGroup can be used. //

        // For example:
        // A table contains data matrices with axes pol and freq. // Those axes are defined in columns pol and freq containing // vectors with the same length as the corresponding axis. // The table also contains scalar columns time and baseline, which // superimpose dimensions upon the data. So the data will be stored // in a 4-d hypercube with axes pol,freq,time,baseline. // It would be defined as follows: // // tableDesc.defineHypercolumn ("dataCube", 4, // stringToVector ("data"), // stringToVector ("pol,freq,time,baseline")); // // Note that the function // stringToVector is very convenient for creating a vector // of Strings. // void defineHypercolumn (const String& hypercolumnName, uInt ndim, const Vector& dataColumnNames); void defineHypercolumn (const String& hypercolumnName, uInt ndim, const Vector& dataColumnNames, const Vector& coordColumnNames); void defineHypercolumn (const String& hypercolumnName, uInt ndim, const Vector& dataColumnNames, const Vector& coordColumnNames, const Vector& idColumnNames); // // Test if the given hypercolumn exists. Bool isHypercolumn (const String& hypercolumnName) const; // Get the names of all hypercolumns. Vector hypercolumnNames() const; // Get the columns involved in a hypercolumn. // It returns the dimensionality of the hypercolumn. // An exception is thrown if the hypercolumn does not exist. uInt hypercolumnDesc (const String& hypercolumnName, Vector& dataColumnNames, Vector& coordColumnNames, Vector& idColumnNames) const; // Adjust the hypercolumn definitions (for a RefTable). // It removes and/or renames columns as necessary. // Column names which are not part of the map are removed if // keepUnknown==False. // If all data columns of a hypercolumn are removed, the entire // hypercolumn is removed. void adjustHypercolumns (const std::map& old2new, Bool keepUnknownData = False, Bool keepUnknownCoord = False, Bool keppUnknownId = False); // Remove ID-columns from the given hypercolumn definitions // and set their default data manager type to IncrementalStMan // and group to ISM_TSM. void removeIDhypercolumns (const Vector& hcNames); // Remove given hypercolumn definition. // An exception is thrown if it is not a hypercolumn. void removeHypercolumnDesc (const String& hypercolumnName); // Check recursively if the descriptions of all subtables are known. void checkSubTableDesc() const; void renameHypercolumn (const String& newHypercolumnName, const String& hypercolumnName); private: String name_p; //# name of table description String vers_p; //# version of table description String dir_p; //# directory String comm_p; //# comment //# Note: the TableRecords are done as pointer, otherwise TableRecord.h //# needs to be included leading to a mutual include. TableRecord* key_p; //# user set of keywords TableRecord* privKey_p; //# Private set of keywords ColumnDescSet col_p; //# set of column names + indices Bool swwrite_p; //# True = description can be written TDOption option_p; //# Table desc. open option AipsIO iofil_p; //# File // Initialize the table description. void init (const TabPath&); // Initialize and copy a table description. void copy (const TableDesc&, const TabPath&, Bool copyColumns); // Throw an invalid hypercolumn exception. void throwHypercolumn (const String& hyperColumnName, const String& message); public: // Put the table description into the file. // The name can be used to write the TableDesc from a Table and // is used to set the names of subtables correctly. void putFile (AipsIO&, const TableAttr&) const; // Get the table description from the file. void getFile (AipsIO&, const TableAttr&); }; //# Get number of columns. inline uInt TableDesc::ncolumn () const { return col_p.ncolumn(); } //# Test if column exists. inline Bool TableDesc::isColumn (const String& name) const { return col_p.isDefined(name); } //# Get a column description. inline const ColumnDesc& TableDesc::columnDesc (const String& name) const { return col_p[name]; } inline const ColumnDesc& TableDesc::operator[] (const String& name) const { return col_p[name]; } inline const ColumnDesc& TableDesc::columnDesc (uInt index) const { return col_p[index]; } inline const ColumnDesc& TableDesc::operator[] (uInt index) const { return col_p[index]; } inline ColumnDesc& TableDesc::rwColumnDesc (const String& name) { return col_p[name]; } inline ColumnDesc& TableDesc::rwColumnDesc (uInt index) { return col_p[index]; } //# Return the name (ie. type) of the table description. inline const String& TableDesc::getType () const { return name_p; } //# Return the version of the table description. inline const String& TableDesc::version () const { return vers_p; } //# Get access to the sets of keywords. inline TableRecord& TableDesc::rwKeywordSet () { return *key_p; } inline const TableRecord& TableDesc::keywordSet () const { return *key_p; } inline const TableRecord& TableDesc::privateKeywordSet () const { return *privKey_p; } //# Get the set of columns. inline const ColumnDescSet& TableDesc::columnDescSet() const { return col_p; } //# Add a column. inline ColumnDesc& TableDesc::addColumn (const ColumnDesc& column) { return col_p.addColumn (column); } inline ColumnDesc& TableDesc::addColumn (const ColumnDesc& column, const String& newname) { return col_p.addColumn (column, newname); } //# Remove a column. inline void TableDesc::removeColumn (const String& name) { col_p.remove (name); } //# Access the comment. inline const String& TableDesc::comment () const { return comm_p; } inline String& TableDesc::comment () { return comm_p; } inline void TableDesc::checkSubTableDesc () const { col_p.checkSubTableDesc(); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/TableError.cc000066400000000000000000000152401476623553700203620ustar00rootroot00000000000000//# TableError.cc: Error classes for the table descriptor classes //# Copyright (C) 1994,1995,1996,1997,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableError::TableError (Category c) : AipsError("Table error",c) {} TableError::TableError (const String& str,Category c) : AipsError(str,c) {} TableError::~TableError () noexcept {} TableInternalError::TableInternalError (const String& str,Category c) : TableError("Internal Table error: " + str,c) {} TableInternalError::~TableInternalError () noexcept {} TableDuplFile::TableDuplFile (const String& name,Category c) : TableError("Table " + name + " already exists",c) {} TableDuplFile::TableDuplFile (const String& name, const String& msg,Category c) : TableError("Table " + name + " already exists" + msg,c) {} TableDuplFile::~TableDuplFile () noexcept {} TableNoFile::TableNoFile (const String& name,Category c) : TableError(name.empty() ? String("No table name given at open") : "Table " + name + " does not exist",c) {} TableNoFile::~TableNoFile () noexcept {} TableNoDir::TableNoDir (const String& name,Category c) : TableError(name + " is not a directory",c) {} TableNoDir::~TableNoDir () noexcept {} TableNoDatFile::TableNoDatFile (const String& filename,Category c) : TableError(filename.empty() ? String("No table name given at open") : "Table file " + filename + " does not exist",c) {} TableNoDatFile::~TableNoDatFile () noexcept {} TableDescNoName::TableDescNoName (Category c) : TableError ("No name for table description",c) {} TableDescNoName::~TableDescNoName () noexcept {} TableInvOpt::TableInvOpt (const String& cl, const String& str, Category c) : TableError ("Invalid " + cl + " option: " + str,c) {} TableInvOpt::~TableInvOpt () noexcept {} TableInvType::TableInvType (const String& tableName, const String& tpin, const String& tpfil,Category c) : TableError ("Table file " + tableName + " is incorrect: Expected type " + tpin + ", found " + tpfil, c) {} TableInvType::~TableInvType () noexcept {} TableInvColumnDesc::TableInvColumnDesc (const String& name, const String& msg,Category c) : TableError("Invalid description of column " + name + ": " + msg,c) {} TableInvColumnDesc::~TableInvColumnDesc () noexcept {} TableInvHyperDesc::TableInvHyperDesc (const String& name, const String& msg,Category c) : TableError("Invalid description of hypercolumn " + name + ": " + msg,c) {} TableInvHyperDesc::~TableInvHyperDesc () noexcept {} TableUnknownDesc::TableUnknownDesc (const String& name,Category c) : TableError("ColumnDesc class " + name + " unknown to ColumnDesc::register",c) {} TableUnknownDesc::~TableUnknownDesc () noexcept {} TableInvDT::TableInvDT (Category c) : TableError ("Invalid Table data type",c) {} TableInvDT::TableInvDT (const String& name,Category c) : TableError ("Invalid Table data type when accessing column " + name,c) {} TableInvDT::~TableInvDT () noexcept {} TableInvOper::TableInvOper (Category c) : TableError ("Invalid Table operation",c) {} TableInvOper::TableInvOper (const String& s,Category c) : TableError ("Invalid Table operation: " + s,c) {} TableInvOper::~TableInvOper () noexcept {} TableArrayConformanceError::TableArrayConformanceError (const String& s,Category c) : TableError (s + ": Table array conformance error",c) {} TableArrayConformanceError::TableArrayConformanceError (const String& s, const IPosition& shape, const IPosition& exp, Category c) : TableError (std::string(s) + ": Table array conformance error (shape=" + shape.toString() + ", expected " + exp.toString() + ')', c) {} TableArrayConformanceError::~TableArrayConformanceError () noexcept {} TableConformanceError::TableConformanceError (const String& s,Category c) : TableError (s + ": Table conformance error (#rows mismatch)",c) {} TableConformanceError::~TableConformanceError () noexcept {} TableInvSort::TableInvSort (Category c) : TableError ("Invalid table sort",c) {} TableInvSort::TableInvSort (const String& s,Category c) : TableError ("Invalid table sort: " + s,c) {} TableInvSort::~TableInvSort () noexcept {} TableInvLogic::TableInvLogic (Category c) : TableError ("Tables in logical operation have different roots",c) {} TableInvLogic::~TableInvLogic () noexcept {} TableInvExpr::TableInvExpr (const String& str,Category c) : TableError ("Error in select expression: " + str,c) {} TableInvExpr::TableInvExpr (const String& name, const String& str,Category c) : TableError ("Error in select expression: column " + name + " is invalid; " + str,c) {} TableInvExpr::~TableInvExpr () noexcept {} TableVectorNonConform::TableVectorNonConform (Category c) : TableError ("Shapes of table vectors are not conformant",c) {} TableVectorNonConform::~TableVectorNonConform () noexcept {} TableParseError::TableParseError (const String& s, int pos, const String& token, Category c) : TableError ("Error in TaQL command: " + s, c), itsPos (pos), itsToken (token) {} TableParseError::~TableParseError () noexcept {} TableGramError::TableGramError (int pos, const String& token, Category c) : TableError ("parse error at or near position " + String::toString(pos) + " '" + token + "'", c), itsPos (pos), itsToken (token) {} TableGramError::~TableGramError () noexcept {} } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/TableError.h000066400000000000000000000401031476623553700202200ustar00rootroot00000000000000//# TableError.h: Table error classes //# Copyright (C) 1994,1995,1996,1997,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABLEERROR_H #define TABLES_TABLEERROR_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations. class IPosition; //# This header file defines the error classes belonging to the table //# descriptor class and its associated classes. //

        // Base error class for storage manager // // // // // // This is the generic StMan exception; catching this one means catching // all Table* exceptions. // Note that you have to catch AipsError to catch all possible exceptions. // class TableError : public AipsError { public: // The default constructor generates the message "Table error". TableError (Category c=GENERAL); // Construct with given message. TableError (const String& message,Category c=GENERAL); ~TableError () noexcept; }; // // Internal table error // // // // // // Internal table error (should never be thrown). // If this is thrown, something is terribly wrong. // class TableInternalError : public TableError { public: // Add given message to string "Internal Table error: ". TableInternalError (const String& message,Category c=GENERAL); ~TableInternalError () noexcept; }; // // Table error; table (description) already exists // // // // // // Table (description) with this name already exists. // class TableDuplFile : public TableError { public: // This constructor generates a message telling that the a table // or description with the given name already exists. TableDuplFile (const String& name, Category c=INVALID_ARGUMENT); // This constructor generates a message telling that the a table // or description with the given name already exists. // The given message is appended to it. TableDuplFile (const String& name, const String& message,Category c=INVALID_ARGUMENT); ~TableDuplFile () noexcept; }; // // Table error; table (description) not found // // // // // // Table (description) with this name could not be found. // class TableNoFile : public TableError { public: // This constructor generates a message telling that the a table // or description with the given name does not exist. TableNoFile (const String& name,Category c=INVALID_ARGUMENT); ~TableNoFile () noexcept; }; // // Table error; no name given to table description // // // // // // No name given for the table description. // Only scratch descriptions can have no name (i.e. a blank name). // class TableDescNoName : public TableError { public: // The default constructor generates the message. TableDescNoName (Category c=INITIALIZATION); ~TableDescNoName () noexcept; }; // // Table error; invalid table (description) option // // // // // // Invalid Table(Desc) option given for the table (description). // class TableInvOpt : public TableError { public: // This constructor generates a message that an invalid option // has been given. The class name is either Table or TableDesc. // The given message will be appended to the total message. TableInvOpt (const String& className, const String& message,Category c=INVALID_ARGUMENT); ~TableInvOpt () noexcept; }; // Table error; path is not a directory // // // // // // Table directory with this name could not be found. // class TableNoDir : public TableError { public: // This constructor generates a message telling that the // table directory with the given name does not exist. TableNoDir (const String& name,Category c=INVALID_ARGUMENT); ~TableNoDir () noexcept; }; // // Table error; table.dat file not found // // // // // // The table.dat file for this table could not be found. // class TableNoDatFile : public TableError { public: // This constructor generates a message telling that the a table // or datription file does not exist. TableNoDatFile (const String& filename,Category c=INVALID_ARGUMENT); ~TableNoDatFile () noexcept; }; // // Table error; table type mismatch // // // // // // The given table type (i.e. name of the table description) does // not match the type as stored in the table file. // class TableInvType : public TableError { public: // This constructor generates a message that the in table type // mismatches the table type in the file. TableInvType (const String& tablename, const String& typeIn, const String& typeFile, Category c=CONFORMANCE); ~TableInvType () noexcept; }; // // Table error; invalid column description // // // // // // The description of a column is invalid. // The given default manager is unknown // (i.e. not registered in DataManReg.cc). // class TableInvColumnDesc : public TableError { public: // This constructor generates a message that the column // with the given name has an invalid description. TableInvColumnDesc (const String& columnName, const String& message,Category c=INVALID_ARGUMENT); ~TableInvColumnDesc () noexcept; }; // // Table error; invalid hypercolumn description // // // // // // The description of a hypercolumn is invalid. // The referenced columns are unknown or invalid. // The message explains the reason. // class TableInvHyperDesc : public TableError { public: // This constructor generates a message that the hypercolumn // with the given name has an invalid description. TableInvHyperDesc (const String& hypercolumnName, const String& message,Category c=INVALID_ARGUMENT); ~TableInvHyperDesc () noexcept; }; // // Table error; unknown column description // // // // // // To be able to reconstruct the correct column description object // from a stored table description, each column description type // must register itself (see ColumnDesc.h and ColumnReg.cc). // class TableUnknownDesc : public TableError { public: // This constructor generates a message that the class with the // given name is unknown (not registered). TableUnknownDesc (const String& name,Category c=INITIALIZATION); ~TableUnknownDesc () noexcept; }; // // Table error; invalid data type // // // // // // Checking of the data type of a column is done at runtime. // This error results from non-matching data types when constructing // a ScalarColumn or ArrayColumn or from invalid data type promotions // when doing a get or put. // class TableInvDT : public TableError { public: // The default constructor generates a generic "invalid data type" message. TableInvDT (Category c=CONFORMANCE); // Put the name of the offending column in the "invalid data type" message. TableInvDT (const String& columName,Category c=CONFORMANCE); ~TableInvDT () noexcept; }; // // Table error; invalid operation // // // // // // Invalid operation on a table. // A request was done that could not be handled by the table system // (e.g. sorting on a column containing arrays). // The message tells what is wrong. // // Invalid operation on a table. class TableInvOper : public TableError { public: // The default constructor generates a generic "invalid operation" message. TableInvOper (Category c=INVALID_ARGUMENT); // Add given message to string "Invalid Table operation: ". TableInvOper (const String& message,Category c=INVALID_ARGUMENT); ~TableInvOper () noexcept; }; // // Table error; non-conformant array // // // // // // When putting a direct array, the shape of the array must conform // the shape as defined for the table array. // When getting an array, the receiving array must be zero-length // or it must conform the shape of the table array. // class TableArrayConformanceError : public TableError { public: // This constructor appends ": Table array conformance error" // to the given message. TableArrayConformanceError (const String& message,Category c=CONFORMANCE); // This constructor appends ": Table array conformance error" // to the given message with the given and expected shape. TableArrayConformanceError (const String& message, const IPosition& shape, const IPosition& expectedShape, Category c=CONFORMANCE); ~TableArrayConformanceError () noexcept; }; // // Table error; table length conformance error // // // // // // When putting a column, the length of the vector must match the // length of the table (i.e. its number of rows). // When getting a column, the length of the vector must be zero or // it must match the length of the table. // class TableConformanceError : public TableError { public: // This constructor appends ": Table conformance error (#rows mismatch)" // to the given message. TableConformanceError (const String& message,Category c=CONFORMANCE); ~TableConformanceError () noexcept; }; // // Table error; invalid sort // // // // // // Invalid sort operation on a table. // A sort can only be done on a scalar column. // class TableInvSort : public TableError { public: // The default constructor generates a generic "invalid sort" message. TableInvSort (Category c=INVALID_ARGUMENT); // This constructor appends the given message to the "invalid sort" // message. TableInvSort (const String& message,Category c=INVALID_ARGUMENT); ~TableInvSort () noexcept; }; // // Table error; invalid logical operation // // // // // // Invalid logical table operation. // When combining tables using a union, difference, etc., the // tables involved have to stem from the same root. I.e. they // should all refer to the same underlying table. // class TableInvLogic : public TableError { public: // The default constructor generates the message. TableInvLogic (Category c=INVALID_ARGUMENT); ~TableInvLogic () noexcept; }; // // Table error; invalid select expression // // // // // // Invalid table select expression. // A column is not a scalar or belongs to another table than // the table on which the selection will be done. // class TableInvExpr : public TableError { public: TableInvExpr (const String& message,Category c=INVALID_ARGUMENT); // This constructor generates a message containing the name of // the offending column. It appends the given message. TableInvExpr (const String& columnName, const String& message,Category c=INVALID_ARGUMENT); ~TableInvExpr () noexcept; }; // // Table error; non-conformant table vectors // // // // // // Table vectors are not conformant (have different lengths) // class TableVectorNonConform : public TableError { public: // The default constructor generates the message. TableVectorNonConform (Category c=CONFORMANCE); ~TableVectorNonConform () noexcept; }; // // Table error; invalid table command // // // // // // The parser in TableGram/TableParse found an error in // the given table command. // class TableParseError : public TableError { public: // This constructor generates a message containing the table command. TableParseError (const String& commandString, int pos=-1, const String& token=String(), Category c=INVALID_ARGUMENT); ~TableParseError () noexcept; // Get error position or token. int pos() const {return itsPos; } const String& token() const { return itsToken; } private: int itsPos; String itsToken; }; // // Table grammar error; invalid table command // // // // // // The parser in TableGram/TableParse found an error in // the given table command. // class TableGramError : public TableError { public: // This constructor generates a message containing the table command. TableGramError (int pos, const String& token, Category c=INVALID_ARGUMENT); ~TableGramError () noexcept; // Get error position or token. int pos() const {return itsPos; } const String& token() const { return itsToken; } private: int itsPos; String itsToken; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/TableIndexProxy.cc000066400000000000000000000104341476623553700214020ustar00rootroot00000000000000//# TableIndexProxy.cc: Holder of table index for the table glish client //# Copyright (C) 2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableIndexProxy::TableIndexProxy (const TableProxy& tablep, const Vector& columnNames, Bool noSort) : scaIndex_p (0), arrIndex_p (0) { if (columnNames.nelements() == 1) { const String& colName = columnNames(0); const TableDesc& td = tablep.table().tableDesc(); if (td.isColumn(colName) && td[colName].isArray()) { arrIndex_p = new ColumnsIndexArray (tablep.table(), colName); return; } } scaIndex_p = new ColumnsIndex (tablep.table(), columnNames, 0, noSort); } TableIndexProxy::TableIndexProxy (const TableIndexProxy& that) : scaIndex_p (0), arrIndex_p (0) { if (that.scaIndex_p != 0) { scaIndex_p = new ColumnsIndex (*that.scaIndex_p); } if (that.arrIndex_p != 0) { arrIndex_p = new ColumnsIndexArray (*that.arrIndex_p); } } TableIndexProxy::~TableIndexProxy() { delete scaIndex_p; delete arrIndex_p; } Bool TableIndexProxy::isUnique() const { if (scaIndex_p != 0) { return scaIndex_p->isUnique(); } return arrIndex_p->isUnique(); } Vector TableIndexProxy::columnNames() const { if (scaIndex_p != 0) { return scaIndex_p->columnNames(); } Vector names(1); names(0) = arrIndex_p->columnName(); return names; } void TableIndexProxy::setChanged (const Vector& columnNames) { if (columnNames.nelements() == 0) { if (scaIndex_p != 0) { scaIndex_p->setChanged(); } else { arrIndex_p->setChanged(); } } else { for (uInt i=0; isetChanged (columnNames(i)); } else { arrIndex_p->setChanged (columnNames(i)); } } } } Int64 TableIndexProxy::getRowNumber (const Record& key) { Bool found; Int64 rownr; if (scaIndex_p != 0) { rownr = scaIndex_p->getRowNumber (found, key); } else { rownr = arrIndex_p->getRowNumber (found, key); } if (!found) { rownr = -1; } return rownr; } Vector TableIndexProxy::getRowNumbers (const Record& key) { RowNumbers rows; if (scaIndex_p != 0) { rows = scaIndex_p->getRowNumbers (key); } else { rows = arrIndex_p->getRowNumbers (key); } Vector rownrs(rows.shape()); convertArray (rownrs, rows); return rownrs; } Vector TableIndexProxy::getRowNumbersRange (const Record& lower, const Record& upper, Bool lowerInclusive, Bool upperInclusive) { RowNumbers rows; if (scaIndex_p != 0) { rows = scaIndex_p->getRowNumbers (lower, upper, lowerInclusive, upperInclusive); } else { rows = arrIndex_p->getRowNumbers (lower, upper, lowerInclusive, upperInclusive); } Vector rownrs(rows.shape()); convertArray (rownrs, rows); return rownrs; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/TableIndexProxy.h000066400000000000000000000074471476623553700212560ustar00rootroot00000000000000//# TableIndexProxy.h: Proxy for table index access //# Copyright (C) 2002,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABLEINDEXPROXY_H #define TABLES_TABLEINDEXPROXY_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableProxy; // // Proxy for table index access. // // // // // //# Classes you should understand before using this one. //
      • class ColumnsIndex //
      • class ColumnsIndexArray // // // TableIndexProxy gives access to indexed access to tables, both for // scalar columns and array columns. // It is primarily meant to be used in classes that wrap access to it // from scripting languages (like Glish and Python). // However, it can also be used directly from other C++ code. // // A TableIndexProxy object is usually created by class // TableProxy. // class TableIndexProxy { public: // Construct for the given columns in the table. TableIndexProxy (const TableProxy& table, const Vector& columnNames, Bool noSort); // Copy constructor. TableIndexProxy (const TableIndexProxy&); ~TableIndexProxy(); // Assignment is forbidden. TableIndexProxy& operator= (const TableIndexProxy&) = delete; // Are all keys in the index unique? Bool isUnique() const; // Return the names of the columns forming the index. Vector columnNames() const; // Something has changed in the table, so the index has to be recreated. // An empty vector means that all columns have changed, otherwise // only the given columns. void setChanged (const Vector& columnNames); // Find the row number matching the key. All keys have to be unique, // otherwise an exception is thrown. // If no match is found, -1 is returned. Int64 getRowNumber (const Record& key); // Find the row numbers matching the key. It should be used instead // of getRowNumber if the same key can exist multiple times. Vector getRowNumbers (const Record& key); // Find the row numbers matching the key range. The boolean arguments // tell if the lower and upper key are part of the range. Vector getRowNumbersRange (const Record& lower, const Record& upper, Bool lowerInclusive, Bool upperInclusive); private: ColumnsIndex* scaIndex_p; ColumnsIndexArray* arrIndex_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/TableInfo.cc000066400000000000000000000130671476623553700201710ustar00rootroot00000000000000//# TableInfo.cc: Table type, subtype and further info //# Copyright (C) 1996,1997,1999,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableInfo::TableInfo() : writeIt_p (True) {} TableInfo::TableInfo (const String& fileName) : writeIt_p (True) { String absName = Path(fileName).absoluteName(); // check cache first, table may not be flushed yet PlainTable * tb = PlainTable::tableCache()(Path(absName).dirName()); if (tb) { *this = tb->tableInfo(); return; } File file (absName); if (! file.exists()) { return; } ifstream os(file.path().absoluteName().chars(), ios::in); char buf[1025]; int len; if (! os.getline (buf, 1024)) { // Type = string return; } len = os.gcount(); if (len > 7) { type_p = String (buf + 7); } if (! os.getline (buf, 1024)) { // SubType = string return; } len = os.gcount(); if (len > 10) { subType_p = String (buf + 10); } if (! os.getline (buf, 1024)) { // blank string return; } // Read the readme lines until a newline. // Add a newline at the end of each line when a newline was read // (in that case gcount gives length+1). while (os.getline (buf, 1024, '\n')) { len = os.gcount(); if (buf[len-1] == '\0') { buf[len-1] = '\n'; buf[len] = '\0'; } readme_p += String (buf, len); } // The info file existed and is normal. // So it does not need to be written. writeIt_p = False; } // Create a TableInfo object of one of the predefined types. // This is a centralised way of setting the Table type/subType. TableInfo::TableInfo (Type tableType) :type_p (type(tableType)), subType_p (subType(tableType)), readme_p (), writeIt_p (True) {} TableInfo::TableInfo (const TableInfo& that) : type_p (that.type_p), subType_p (that.subType_p), readme_p (that.readme_p), writeIt_p (True) {} TableInfo& TableInfo::operator= (const TableInfo& that) { if (this != &that) { type_p = that.type_p; subType_p = that.subType_p; readme_p = that.readme_p; writeIt_p = True; } return *this; } TableInfo::~TableInfo() {} void TableInfo::flush (const String& fileName) { if (writeIt_p) { ofstream os(Path(fileName).absoluteName().chars(), ios::out); os << "Type = " << type_p << endl; os << "SubType = " << subType_p << endl; os << endl; os << readme_p; writeIt_p = False; } } void TableInfo::setType (const String& type) { type_p = type; writeIt_p = True; } void TableInfo::setSubType (const String& subType) { subType_p = subType; writeIt_p = True; } void TableInfo::readmeClear() { readme_p = ""; writeIt_p = True; } void TableInfo::readmeAddLine (const String& readmeLine) { readme_p += readmeLine; readme_p += '\n'; writeIt_p = True; } String TableInfo::type(Type tableType) { switch (tableType) { case PAGEDIMAGE: return "Image"; case PAGEDARRAY: return "Paged Array"; case MEASUREMENTSET: return "Measurement Set"; case ANTENNA: return "Antenna"; case ARRAY: return "Telescope Array"; case FEED: return "Feed Characteristics"; case FIELD: return "Field"; case OBSERVATION: return "Observation Information"; case OBSLOG: return "Observation Log"; case SOURCE: return "Source"; case SPECTRALWINDOW: return "Spectral Window"; case SYSCAL: return "System Calibration"; case WEATHER: return "Weather"; case ME_CALIBRATION: return "Calibration"; case LOG: return "Log message"; case COMPONENTLIST: return "Component List"; default: return ""; }; } String TableInfo::subType(Type tableType) { switch (tableType) { case PAGEDIMAGE: return ""; case PAGEDARRAY: return ""; case MEASUREMENTSET: return ""; case ANTENNA: return ""; case ARRAY: return ""; case FEED: return ""; case FIELD: return ""; case OBSERVATION: return ""; case OBSLOG: return ""; case SOURCE: return ""; case SPECTRALWINDOW: return ""; case SYSCAL: return ""; case WEATHER: return ""; case ME_CALIBRATION: return ""; case LOG: return ""; case COMPONENTLIST: return ""; default: return ""; }; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/TableInfo.h000066400000000000000000000164021476623553700200270ustar00rootroot00000000000000//# TableInfo.h: Table type, subtype and further info //# Copyright (C) 1996,1997,1999 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABLEINFO_H #define TABLES_TABLEINFO_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Table type, subtype and further info // // // // //# //# Classes you should understand before using this one. //# // // TableInfo holds information (like type) about a table. // // // TableInfo holds information about a table. It contains the following // information: //
        //
        Type //
        the type of a table (e.g. IMAGE, LOG). //
        SubType //
        the subtype of a table (e.g. UVDATA, MAP or ANTENNAPATTERN for // type IMAGE). //
        Readme //
        An arbitrary number of lines containing ancillary text // describing the table (or maybe its history). //
        // This information is stored // in the file table.info in the table directory. // Regular tables as well as reference tables (results of sort/select) // have their own table.info file. //
        // The initial table-info of a regular table is blank. It has to be set // explicitly by the user. //
        // The initial table-info of a reference table // is a copy of the table-info of its parent table. The user can add // lines to the readme information to describe the table in more detail. // Of course, the type and/or subtype can be changed at will. //

        // The type and subtype information are stored at the beginning of // the first two lines of the file as: // // Type = TypeString // SubType = SubTypeString // // These lines in the table.info file can be used by external programs // (like the filebrowser) to determine the type of table being handled. //

        // The third line in the file is blank. The line(s) thereafter contain // the possible readme information (note that multiple readme lines are // possible). They can be added using the function readmeAddLine. //

        // Existing tables do not have a table.info file yet. The table system // will handle them correctly and use a blank type, subtype // and readme string. A table.info file will be created when the // table is opened for update. //

        // To be sure that different table types have unique names, it can be // useful to use enum's and to define them in one common file. For // Casacore tables this enum is defined in this file. // // // // Open a table for update. // Table table("name", Table::Update); // // Get its TableInfo object. // TableInfo& info = table.tableInfo(); // // Set type and subtype. // info.setType ("IMAGE"); // info.setSubType ("SubTypeString"); // // Add a few readme lines. The second one adds 2 lines. // info.readmeAddLine ("the first readme string"); // info.readmeAddLine ("the second readme string\nthe third readme string"); // // Display the type, etc.. // cout << info.type() << " " << info.subType() << endl; // cout << info.readme(); // // // // External programs need to be able to determine the type of a table. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class TableInfo { public: // enum for various standard Table types. // Underscores in the enumerator indicate different sub-types enum Type { // a PagedImage is a PagedArray with coordinates and Masking (opt.) PAGEDIMAGE, // a PagedArray (.../Lattices/PagedArray.h) PAGEDARRAY, // MeasurementSet main Table MEASUREMENTSET, // MeasurementSet Antenna table ANTENNA, // MeasurementSet Array table ARRAY, // MeasurementSet Feed characteristics table FEED, // MeasurementSet Field table FIELD, // MeasurementSet Observation information table OBSERVATION, // MeasurementSet Oserving Log table OBSLOG, // MeasurementSet Source table SOURCE, // MeasurementSet Spectral Window table SPECTRALWINDOW, // MeasurementSet System Calibration table SYSCAL, // MeasurementSet Weather table WEATHER, // Measurement Equation Calibration table ME_CALIBRATION, // Casacore Log table LOG, // A ComponentList table contains parameterised representations of the // sky brightness. COMPONENTLIST }; // Create an empty object. TableInfo(); // Create the object reading it from the given file name. // If the file does not exist, type, subtype and readme are // initialized to a blank string. explicit TableInfo (const String& fileName); // Create a TableInfo object of one of the predefined types. // This is a centralised way of setting the Table type only. TableInfo (Type which); // Copy constructor (copy semantics). TableInfo (const TableInfo& that); // Assignment (copy semantics). TableInfo& operator= (const TableInfo& that); ~TableInfo(); // Get the table (sub)type. // const String& type() const; const String& subType() const; // // Get the readme. const String& readme() const; // Set the table (sub)type. void setType (const String& type); void setSubType (const String& subType); // Convert the Type enumerator to a type and subType string // static String type(Type tableType); static String subType(Type tableType); // // Clear the readme. void readmeClear(); // Add a line to the readme. // It will itself add a newline character ('\n') to the end of the line. void readmeAddLine (const String& readmeLine); // Write the TableInfo object. void flush (const String& fileName); private: String type_p; String subType_p; String readme_p; Bool writeIt_p; // True = object has changed, so has to be written }; inline const String& TableInfo::type() const { return type_p; } inline const String& TableInfo::subType() const { return subType_p; } inline const String& TableInfo::readme() const { return readme_p; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/TableIter.cc000066400000000000000000000107561476623553700202030ustar00rootroot00000000000000//# TableIter.cc: Iterate through a Table //# Copyright (C) 1994,1995,1996,1997,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableIterator::TableIterator() : tabIterPtr_p (0) {} TableIterator::TableIterator (const Table& tab, const String& key, Order order, Option option) : tabIterPtr_p (0) { Block keys(1, key); Block ord(1, order); Block> cmpObj(1); tabIterPtr_p = tab.baseTablePtr()->makeIterator (keys, cmpObj, ord, option); next(); // get first subtable } TableIterator::TableIterator (const Table& tab, const Block& keys, Order order, Option option) : tabIterPtr_p (0) { Block ord(keys.nelements(), order); Block> cmpObj(keys.nelements()); tabIterPtr_p = tab.baseTablePtr()->makeIterator (keys, cmpObj, ord, option); next(); // get first subtable } TableIterator::TableIterator (const Table& tab, const Block& keys, const Block& orders, Option option) : tabIterPtr_p (0) { Block> cmpObj(keys.nelements()); tabIterPtr_p = tab.baseTablePtr()->makeIterator (keys, cmpObj, orders, option); next(); // get first subtable } TableIterator::TableIterator (const Table& tab, const Block& keys, const Block>& cmpObjs, const Block& orders, Option option, bool cacheIterationBoundaries) : tabIterPtr_p (0) { tabIterPtr_p = tab.baseTablePtr()->makeIterator (keys, cmpObjs, orders, option, cacheIterationBoundaries); next(); // get first subtable } TableIterator::TableIterator (const TableIterator& iter) : tabIterPtr_p (0) { operator= (iter); } TableIterator& TableIterator::operator= (const TableIterator& iter) { delete tabIterPtr_p; tabIterPtr_p = 0; subTable_p = Table(); if (iter.tabIterPtr_p != 0) { tabIterPtr_p = iter.tabIterPtr_p->clone(); subTable_p = iter.table(); next(); // Get first subtable, as in constructor } return *this; } TableIterator::~TableIterator() { delete tabIterPtr_p; } void TableIterator::reset() { tabIterPtr_p->reset(); next(); } void TableIterator::throwIfNull() const { if (isNull()) { throw (TableInvOper ("TableIterator is null")); } } void TableIterator::next() { subTable_p = Table(tabIterPtr_p->next()); } void TableIterator::copyState(const TableIterator &other) { subTable_p = other.subTable_p; tabIterPtr_p->copyState(*other.tabIterPtr_p); } // Report Name of slowest column that changes at end of current iteration const String& TableIterator::keyChangeAtLastNext() const { return tabIterPtr_p->keyChangeAtLastNext(); } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/TableIter.h000066400000000000000000000214651476623553700200440ustar00rootroot00000000000000//# TableIter.h: Iterate through a Table //# Copyright (C) 1994,1995,1996,1997,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABLEITER_H #define TABLES_TABLEITER_H //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class BaseTableIterator; class String; template class Block; //

        // Iterate through a Table // // // // // //# Classes you should understand before using this one. //
      • Table //
      • Sort // // // TableIterator is a class allowing one to iterate in an arbitrary // way through a table. Each iteration step returns a Table // containing the result of the iteration step. // It is possible to have more than one iterator on a table. // // An iteration is defined by giving the columns over which to iterate. // For example, take a UV data set with "axes" frequency, baseline and // time. Getting all frequencies per time and baseline can be done // by iterating over columns time and baseline (as shown in the example). // The main iteration column must be given first. // It is possible to define an iteration order per column. //
        It is also possible to define a compare object per column. // For example, CompareIntervalReal can be used to iterate in intervals // over, say, the TIME column by treating a range of values as equal // (e.g. iterate in 60 seconds time intervals). // // The table is sorted before doing the iteration unless TableIterator::NoSort // is given. //
        // // // // Iterate over time and baseline (by default in ascending order). // // Time is the main iteration order. // Table t; // Table tab ("UV_Table.data"); // Block iv0(2); // iv0[0] = "time"; // iv0[1] = "baseline"; // // Create the iterator. This will prepare the first subtable. // TableIterator iter(tab, iv0); // Int nr = 0; // while (!iter.pastEnd()) { // // Get the first subtable. // // This will contain rows with equal time and baseline. // t = iter.table(); // cout << t.nrow() << " "; // nr++; // // Prepare the next subtable with the next time,baseline value. // iter.next(); // } // cout << endl << nr << " iteration steps" << endl; // // // // It is sometimes needed to access all data in a table in a grouped // way; for example, all frequencies per time and baseline. // This can perfectly be done with an iterator. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class TableIterator { public: // Define the possible iteration orders. enum Order {Ascending=Sort::Ascending, Descending=Sort::Descending}; // Define the possible sorts. enum Option {QuickSort= Sort::QuickSort, HeapSort = Sort::HeapSort, InsSort = Sort::InsSort, ParSort = Sort::ParSort, NoSort = 64}; // Create a null TableIterator object (i.e. no iterator is attached yet). // The sole purpose of this constructor is to allow construction // of an array of TableIterator objects. // The assignment operator can be used to make a null object // reference a column. // Note that sort functions, etc. will cause a segmentation fault // when operating on a null object. It was felt it was too expensive // to test on null over and over again. The user should use the isNull // or throwIfNull function in case of doubt. TableIterator(); // Create a table iterator on the given column(s) for the given table. // Each iteration step results in a Table containing all // rows in which the values in each given column is equal. // The column vector can be empty, resulting in a single iteration step // giving the entire table. // An iteration order can be given; it defaults to Ascending. // Per column a compare object can be given to use other compare // functions than the standard ones defined in Compare.h. // The compare functions are used for both the sort and the iteration. // The option argument makes it possible to choose from various // sorting algorithms. Usually ParSort is the fastest, but for // a single core machine QuickSort usually performs better. // InsSort (insertion sort) should only be used if the input // is almost in order. // If it is known that the table is already in order, the sort step can be // bypassed by giving the option TableIterator::NoSort. // The default option is ParSort. // TableIterator (const Table&, const String& columnName, Order = Ascending, Option = ParSort); TableIterator (const Table&, const Block& columnNames, Order = Ascending, Option = ParSort); // Give the iteration order per column. // If an interval comparison object like CompareIntervalReal // is used, the data are sorted on the interval, not on the value. // One should consider to do an explicitsort on value and no iteration sort. // TableIterator (const Table&, const Block& columnNames, const Block& orders, Option = ParSort); // Give the iteration order per column. // Give an optional compare object per column. // A zero pointer means that the default compare function will be used. // If cacheIterationBoundaries is set to true then the iteration // boundaries computed at construction time while sorting the table // are used when advancing with next(). Otherwise, for each next() // call the comparison functions are reevaluated again to get the // iteration boundary. This improves performance in general but will // break existing applications that change the comparison objects // (cmpObjs) between iterations. TableIterator (const Table&, const Block& columnNames, const Block>& cmpObjs, const Block& orders, Option = ParSort, bool cacheIterationBoundaries = false); // // Copy constructor (copy semantics). TableIterator (const TableIterator&); ~TableIterator(); // Assignment (copy semantics). TableIterator& operator= (const TableIterator&); // Test if the object is null, i.e. does not reference a table yet. // This is the case if the default constructor is used. Bool isNull() const { return !tabIterPtr_p; } // Throw an exception if the object is null, i.e. // if function isNull() is True. void throwIfNull() const; // Reset the iterator (i.e. restart iteration). void reset(); // Test if at the end. Bool pastEnd() const; // Go to the next group. // void next(); void operator++(); void operator++(int); // void copyState(const TableIterator &); // Report Name of slowest column that changes at end of current iteration const String& keyChangeAtLastNext() const; // Get the current group. Table table() const; protected: BaseTableIterator* tabIterPtr_p; Table subTable_p; }; //# Iterator is at the end if the subtable is empty. inline Bool TableIterator::pastEnd() const { return (subTable_p.nrow() == 0 ? True : False); } inline Table TableIterator::table() const { return subTable_p; } inline void TableIterator::operator++() { next(); } inline void TableIterator::operator++(int) { next(); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/TableIterProxy.cc000066400000000000000000000167151476623553700212460ustar00rootroot00000000000000//# TableIterProxy.cc: Holder of table iterators for the table glish client. //# Copyright (C) 1994,1995,1996 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableIterProxy::TableIterProxy() : firstTime_p (True) {} TableIterProxy::TableIterProxy (const TableProxy& tab, const Vector& columns, const String& order, const String& sortType, const Vector& iterSteps) : firstTime_p (True) { Block names(columns.nelements()); for (uInt i=0; i& columns, const Vector& iterSteps, TableIterator::Order order, TableIterator::Option option) { // First determine if all columns are scalar and have a valid data type. // Also find out if a case-insenstive string comparison is needed. Block> comps(columns.size()); Block orders (columns.size(), order); for (uInt i=0; i 0) { const ColumnDesc& colDesc = tab.tableDesc()[columns[i]]; if (! colDesc.isScalar()) { throw TableError ("Only scalar columns can be used in table " "iteration"); } DataType dtype = colDesc.dataType(); switch (dtype) { // The following data types are valid. case TpUChar: case TpShort: case TpUShort: case TpInt: case TpUInt: case TpInt64: case TpFloat: case TpDouble: break; case TpString: comps[i] = std::make_shared(); break; default: throw TableError ("No iteration step can be given for column " + columns[i]); } } } // First sort the table to fully order the columns with an interval. Table sortab(tab); if (option != TableIterator::NoSort) { Table sortab = tab.sort (columns, comps, orders, option); } // Now see if an interval comparison has to be done when iterating. for (uInt i=0; i 0) { DataType dtype = sortab.tableDesc()[columns[i]].dataType(); switch (dtype) { case TpUChar: { uChar start = ScalarColumn(sortab, columns[i])(0); comps[i] = std::make_shared>(iterSteps[i], start); } break; case TpShort: { Short start = ScalarColumn(sortab, columns[i])(0); comps[i] = std::make_shared>(iterSteps[i], start); } break; case TpUShort: { uShort start = ScalarColumn(sortab, columns[i])(0); comps[i] = std::make_shared>(iterSteps[i], start); } break; case TpInt: { Int start = ScalarColumn(sortab, columns[i])(0); comps[i] = std::make_shared>(iterSteps[i], start); } break; case TpUInt: { uInt start = ScalarColumn(sortab, columns[i])(0); comps[i] = std::make_shared>(iterSteps[i], start); } break; case TpInt64: { Int64 start = ScalarColumn(sortab, columns[i])(0); comps[i] = std::make_shared>(iterSteps[i], start); } break; case TpFloat: { Float start = ScalarColumn(sortab, columns[i])(0); comps[i] = std::make_shared>(iterSteps[i], start); } break; case TpDouble: { Double start = ScalarColumn(sortab, columns[i])(0); comps[i] = std::make_shared>(iterSteps[i], start); } break; default: break; } } } // Iterate over the sorted table (and don't sort again). iter_p = TableIterator(sortab, columns, comps, orders, TableIterator::NoSort); } TableIterProxy::TableIterProxy (const TableIterProxy& that) : iter_p (that.iter_p), firstTime_p (that.firstTime_p) {} TableIterProxy::~TableIterProxy() {} TableIterProxy& TableIterProxy::operator= (const TableIterProxy& that) { if (this != &that) { iter_p = that.iter_p; firstTime_p = that.firstTime_p; } return *this; } Bool TableIterProxy::nextPart (TableProxy& table) { // The first iteration is already done by the TableIterator constructor. if (firstTime_p) { firstTime_p = False; } else { iter_p.next(); } // Exit when no more subtables. if (iter_p.pastEnd()) { return False; } table = TableProxy (iter_p.table()); return True; } TableProxy TableIterProxy::next() { TableProxy tp; Bool ok = nextPart (tp); if (ok) { return tp; } throw IterError(); } void TableIterProxy::reset() { iter_p.reset(); firstTime_p = True; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/TableIterProxy.h000066400000000000000000000117371476623553700211070ustar00rootroot00000000000000//# TableIterProxy.h: Proxy for table iterator access //# Copyright (C) 1994,1995,1996,1999,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABLEITERPROXY_H #define TABLES_TABLEITERPROXY_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableProxy; // // Proxy for table iterator access. // // // // // //# Classes you should understand before using this one. //
      • class TableIterator // // // TableIterProxy holds a TableIterator object for the table // glish client. // // // TableIterProxy gives access to the table iterator functionality. // It is primarily meant to be used in classes that wrap access to it // from scripting languages (like Glish and Python). // However, it can also be used directly from other C++ code. // // A TableIterProxy object is usually created by class // TableProxy. // // // // // Get a table proxy. // TableProxy proxy("sometable"); // Vector columns(1, "SOMECOL"); // TableIterProxy tgi (proxy, columns, "a", "q"); // TableProxy subTable; // // Iterate through the table. // while (tgi.next (subTable)) { // ..use Table object subTable.table() // } // // class TableIterProxy { public: // Default constructor initializes to not open. // This constructor is only needed for the Block container. TableIterProxy(); // Construct iterator for the given table column(s). // Order and sortType are case-insentive strings and only the first // character in it is important. //
        order[0]=a means ascending; d means descending. //
        sortType[0]=q means quicksort, i means insertion sort, // n means nosort, h means heapsort, otherwise parsort //
        For each column an iteration interval can be given making it possible // to iterate in e.g. time chunks of 1 minute. Not given or zero means // no interval is given for that column, thus a normal comparison is done. // It can only be used for numerical columns (not complex). // However, if for a string column the interbval is set to non-zero, it // means that case-insensitive comparison will be used. TableIterProxy (const TableProxy& tab, const Vector& columns, const String& order, const String& sortType, const Vector& intervals = Vector()); // Copy constructor (copy semantics). TableIterProxy (const TableIterProxy&); ~TableIterProxy(); // Assignment (copy semantics). TableIterProxy& operator= (const TableIterProxy&); // Is the internal iterator object null? Bool isNull() const { return iter_p.isNull(); } // Get the TableIterator object. const TableIterator& iterator() const { return iter_p; } // Get the next subtable and return it in the TableProxy argument. // When no more subtables are available, it returns False. Bool nextPart (TableProxy& table); // Iterate to the next part (for Python use). // An IterError exception is thrown at the end of the loop. TableProxy next(); // Reset the iterator (for Python use). void reset(); private: // Make an iterator where iteration intervals may have been given. void makeStepIter (const Table& tab, const Block& columns, const Vector& iterSteps, TableIterator::Order order, TableIterator::Option sortType); //# Data members TableIterator iter_p; Bool firstTime_p; //# True = first time }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/TableKeyword.cc000066400000000000000000000134311476623553700207150ustar00rootroot00000000000000//# TableKeyword.cc: A keyword value representing a table //# Copyright (C) 1996,1997,1998,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableKeyword::TableKeyword (const String& tableDescName) : table_p (new Table), tableDescName_p (tableDescName) {} TableKeyword::TableKeyword (const Table& table, const String& tableDescName) : table_p (new Table), attr_p (table), tableDescName_p (tableDescName) { // Only keep the Table object if not persistent. if (table.isMarkedForDelete()) { *table_p = table; } else { *table_p = Table(); } } TableKeyword::TableKeyword (const TableKeyword& that) : table_p (new Table(*that.table_p)), attr_p (that.attr_p), tableDescName_p (that.tableDescName_p) {} TableKeyword& TableKeyword::operator= (const TableKeyword& that) { if (this != &that) { *table_p = *that.table_p; attr_p = that.attr_p; tableDescName_p = that.tableDescName_p; } return *this; } TableKeyword& TableKeyword::operator= (const Table& table) { if (!conform (table)) { throw (TableError ("TableKeyword::operator=; non-conforming table")); } attr_p.set (table); // Only keep the Table object is not persistent. if (table.isMarkedForDelete()) { *table_p = table; } else { *table_p = Table(); } return *this; } TableKeyword::~TableKeyword() { delete table_p; } void TableKeyword::set (const String& name, const TableAttr& parentAttr) { attr_p = parentAttr; // If a directory was stripped off, add the directory of the parent. // Otherwise use the name as such. attr_p.setName (Path::addDirectory (name, parentAttr.name())); } void TableKeyword::setRW() { attr_p.setRW(); if (! table_p->isNull()) { if (Table::isWritable (attr_p.name())) { table_p->reopenRW(); } } } Bool TableKeyword::isMultiUsed (Bool checkSubTables) const { if (! table_p->isNull()) { return table_p->isMultiUsed (checkSubTables); } // The Table is closed immediately (thus not left open unnecessarily). Table tab(attr_p.name(), Table::Old); return tab.isMultiUsed (checkSubTables); } void TableKeyword::renameTable (const String& newParentName, const String& oldParentName) { // Remove common part of old name from subtable name. String old = tableName (oldParentName); if (old != attr_p.name()) { attr_p.setName (Path::addDirectory (old, newParentName)); } // Note that renaming subtables of a subtable is not necessary, // because they are always relative to the subtable (which // is not really renamed). } String TableKeyword::tableName (const String& parentName) const { // Get the directory of the parent (with the trailing /). // If it is contained in this name, return name without it. return Path::stripDirectory (attr_p.name(), parentName); } Table TableKeyword::table (const TableLock* lockOptions) const { // Return the table object if already open. if (! table_p->isNull()) { return *table_p; } // Open for write when needed and possible. Table::TableOption option = Table::Old; if (attr_p.openWritable() && Table::isWritable (attr_p.name())) { option = Table::Update; } // Note that the opened table is not kept to avoid possible leaks // if a table keyword refers to the table itself (like the SORTED_TABLE // in an MS). return Table(attr_p.name(), lockOptions ? *lockOptions : attr_p.lockOptions(), option); } void TableKeyword::close() const { *table_p = Table(); } void TableKeyword::flush (Bool fsync) const { if (attr_p.openWritable()) { if (!table_p->isNull()) { table_p->flush (fsync, True); } else { // The table is not open here, but might be open elsewhere. // So only flush if open elsewhere, thus in the TableCache. PlainTable::tableCache().flushTable (attr_p.name(), fsync, True); } } } Bool TableKeyword::conform (const TableKeyword& that) const { // Only check for conformance if a description is fixed. if (isFixed()) { return conform (that.table()); } return True; } Bool TableKeyword::conform (const Table& that) const { if (isFixed() && tableDescName_p != that.tableDesc().getType()) { return False; } return True; } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/TableKeyword.h000066400000000000000000000177271476623553700205730ustar00rootroot00000000000000//# TableKeyword.h: A keyword value representing a table //# Copyright (C) 1996,1997,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABLEKEYWORD_H #define TABLES_TABLEKEYWORD_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class Table; // // Keyword value representing a table // // // // // //# Classes you should understand before using this one. //
      • TableRecord //
      • Table // // // TableKeyword represents a record keyword field containing a table. // It is used by class TableRecord, which in its turn is meant to be // used by the Table class. // It serves the following purposes: //
          //
        • A table is only opened on demand, i.e. when the keyword // is accessed for the first time. When opened, the function // closeTable makes it possible to close a table when not // needed anymore (provided the table is not used elsewhere). // It will automatically be reopened when used again. //
        • A switch is maintained which indicates if the table // should be opened as readonly or read/write. // A table is opened as read/write when the switch is read/write and // when the table is writable. Otherwise it is opened as readonly. // When a parent table is read back, its TableKeyword's will be // read back and the switch will be set to the access-mode // (readonly or read/write) of the parent table. // When a new table is inserted, the access-mode is taken from the table. //
        • When the parent table is reopened as read/write, the table in // this object will also be reopened as read/write (if the table is // writable). //
        • When a TableKeyword gets written, only the table name will be // written. Reading it back will set the correct access-mode, while // the table will not be opened until necessary. // However, when reading a parent table back it is possible that it // is done from a different directory than where it was created. // Therefore the directory of the parent table is prepended to the // TableKeyword subtable name. Similarly, when written it is stripped off. //
          E.g. parent table XX and subtable SUB are created in the working // directory WD. Reading back is done from another directory by // specifying WD/XX. WD will be prepended to SUB. //
        //
        // // This class provides the extra functionality for keywords containing // tables. This is needed because tables are much more complex entities // than scalars or arrays. // // // // // Store a table in the keyword set. // void someFunc (const Table& subTable) // { // // Open the table and get access to the table keyword set. // Table table("table.data", Table::Update); // TableRecord& keyset = table.rwKeywordSet(); // keyset.defineTable ("KeyTab", subTable); // } // // // Open the table and get the table from keyword KeyTab. // // It shows that this can be done in one statement. // Table table("table.data"); // Table subTab = table.keywordSet().asTable ("KeyTab"); // // //# //# A List of bugs, limitations, extensions or planned refinements. //# class TableKeyword { public: // Construct a TableKeyword with the given tableDescName. // When the tableDescName is empty the keyword is variable structured. // Otherwise it is fixed structured, meaning that only tables with a // description of that name can be assigned to this keyword. TableKeyword (const String& tableDescName); // Construct a TableKeyword from a Table. //
        // When the tableDescName is empty the keyword is variable structured. // Otherwise it is fixed structured, meaning that only tables with a // description of that name can be assigned to this keyword. TableKeyword (const Table& table, const String& tableDescName); // Copy constructor (full copy semantics). TableKeyword (const TableKeyword& that); // Assignment (leaves tableDescName_p untouched). // This is only possible when both objects conform. // TableKeyword& operator= (const TableKeyword& that); TableKeyword& operator= (const Table& table); // ~TableKeyword(); // Set the name of the table and the writable switch. // This is used when reading back a keyword. void set (const String& name, const TableAttr& parentAttr); // Set the keyword to read/write access. // If the table is already open, it will be reopened with read/write // access if the table is writable. void setRW(); // Is the table in use in another process? // If checkSubTables is set, it is also checked if // a subtable is used in another process. Bool isMultiUsed (Bool checkSubTables) const; // Get the name of the table. const String& tableName() const; // Get the name of the table relative to parent table. // String tableName (const String& parentName) const; String tableName (const TableAttr& parentAttr) const { return tableName (parentAttr.name()); } // // Get the table. // It will be opened when necessary. // If given, the lockOptions will be used instead of the ones in // the table attributes. Table table (const TableLock* lockOptions = 0) const; // Get the table attributes. const TableAttr& tableAttributes() const { return attr_p; } // Set the table attributes. void setTableAttributes (const TableAttr& attr) { attr_p = attr; } // Close the table. void close() const; // Flush and optionally fsync the table. void flush (Bool fsync) const; // Rename the table if its path contains the old parent table name. void renameTable (const String& newParentName, const String& oldParentName); // Test if the table in other conforms this table keyword. // It conforms when this description name is blank or matches the // table description name of the other. // Bool conform (const TableKeyword& that) const; Bool conform (const Table& that) const; // // Has the table a fixed description name? // It has when its description name is not empty. Bool isFixed() const; private: Table* table_p; TableAttr attr_p; String tableDescName_p; }; inline const String& TableKeyword::tableName() const { return attr_p.name(); } inline Bool TableKeyword::isFixed() const { return (! tableDescName_p.empty()); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/TableLock.cc000066400000000000000000000100431476623553700201550ustar00rootroot00000000000000//# TableLock.cc: Class to hold table lock options //# Copyright (C) 1997,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableLock::TableLock (LockOption option) : itsOption (option), itsReadLocking (True), itsMaxWait (0), itsInterval (5), itsIsDefaultLocking (False), itsIsDefaultInterval (True) { init(); } TableLock::TableLock (LockOption option, double inspectionInterval, uInt maxWait) : itsOption (option), itsReadLocking (True), itsMaxWait (maxWait), itsInterval (inspectionInterval), itsIsDefaultLocking (False), itsIsDefaultInterval (False) { init(); } TableLock::TableLock (const TableLock& that) : itsOption (that.itsOption), itsReadLocking (that.itsReadLocking), itsMaxWait (that.itsMaxWait), itsInterval (that.itsInterval), itsIsDefaultLocking (that.itsIsDefaultLocking), itsIsDefaultInterval (that.itsIsDefaultInterval) {} TableLock& TableLock::operator= (const TableLock& that) { if (this != &that) { itsOption = that.itsOption; itsReadLocking = that.itsReadLocking; itsMaxWait = that.itsMaxWait; itsInterval = that.itsInterval; itsIsDefaultLocking = that.itsIsDefaultLocking; itsIsDefaultInterval = that.itsIsDefaultInterval; } return *this; } void TableLock::init() { #ifdef AIPS_TABLE_NOLOCKING itsOption = NoLocking; #else Bool opt; AipsrcValue::find (opt, "table.nolocking", False); if (opt) { itsOption = NoLocking; } else { if (itsOption == DefaultLocking) { itsOption = AutoLocking; itsIsDefaultLocking = True; } else if (itsOption == AutoNoReadLocking) { itsOption = AutoLocking; itsReadLocking = False; } else if (itsOption == UserNoReadLocking) { itsOption = UserLocking; itsReadLocking = False; } } #endif if (itsOption == NoLocking) { itsReadLocking = False; } } void TableLock::merge (const TableLock& that) { if (! that.itsIsDefaultLocking) { if (itsIsDefaultLocking || that.itsOption <= itsOption) { itsOption = that.itsOption; itsMaxWait = that.itsMaxWait; itsIsDefaultLocking = that.itsIsDefaultLocking; } if (itsIsDefaultLocking) { itsReadLocking = that.itsReadLocking; } else if (that.itsReadLocking) { itsReadLocking = True; } if (! that.itsIsDefaultInterval) { if (itsIsDefaultInterval || itsInterval > that.itsInterval) { itsInterval = that.itsInterval; } } } } Bool TableLock::lockingDisabled() { #ifdef AIPS_TABLE_NOLOCKING return True; #else Bool opt; AipsrcValue::find (opt, "table.nolocking", False); return opt; #endif } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/TableLock.h000066400000000000000000000155631476623553700200330ustar00rootroot00000000000000//# TableLock.h: Class to hold table lock options //# Copyright (C) 1997,1998,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABLELOCK_H #define TABLES_TABLELOCK_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Class to hold table lock options. // // // // // //
      • class Table //
      • class LockFile // // // This class keeps the Table lock options. // Currently these are the LockingOption and the inspection interval. //

        // It also keeps the LockFile object used to do the // actual locking/unlocking. // // It is possible to disable locking by building casacore with -DAIPS_TABLE_NOLOCKING // or by setting the aipsrc variable table.nolocking=true. // // Encapsulate Table locking info. // class TableLock { public: // Define the possible table locking options. // They offer the user the possibility to lock and synchronize access // to the table. A lot of locking degrades table performance; not only // because acquiring/releasing locks takes time, but especially // because table data has to be synchronized (thus written to disk) // when a lock is released. Otherwise the other processes see data // which is not up-to-date. // // Building Casacore with -DTABLE_NOLOCKING or setting aipsrc variable // table.nolocking=1 forces lock option NoLocking. enum LockOption { // The table is permanently locked. // A lock is set at the beginning and only released when // the table is closed. A read lock is used when the table is // opened for readonly; otherwise a write lock is used. // This means that multiple readers are possible. // The Table constructor exits with an exception when the // lock cannot be acquired. PermanentLocking, // The same as above, but the table constructor waits // until the lock gets available. PermanentLockingWait, // The system takes care of acquiring/releasing locks. // In principle it keeps the table locked, but from time to // time (defined by the inspection interval) it is checked whether // another process wants to access the table. If so, the lock // is released and probably re-acquired later. // This mode is the default mode. AutoLocking, // The user is taking care of locking the table by means // of the Table functions lock and unlock. // In this way transaction processing can be implemented. UserLocking, // The system takes care of acquiring/releasing locks. // It is similar to AutoLocking, but no locks are needed for // reading. AutoNoReadLocking, // The user is taking care of locking the table by means // of the Table functions lock and unlock. // It is similar to UserLocking, but no locks are needed for // reading. UserNoReadLocking, // Do not do any locking at all. This should be used with care // because concurrent access might result in table corruption. NoLocking, // This is the default locking option. // It means that AutoLocking will be used if the table is not // opened yet. Otherwise the locking options of the PlainTable // object already in use will be used. DefaultLocking }; // Construct with given option and interval. // The default LockOption is AutoLocking. // In case of AutloLocking the inspection interval defines how often // the table system checks if another process needs a lock on the table. // It defaults to 5 seconds. // The maxWait defines the maximum number of seconds the table system // waits when acquiring a lock in AutoLocking mode. The default // is 0 seconds meaning indefinitely. // TableLock (LockOption option = DefaultLocking); TableLock (LockOption option, double inspectionInterval, uInt maxWait = 0); // // Copy constructor. TableLock (const TableLock& that); // Assignment. TableLock& operator= (const TableLock& that); // Merge that TableLock with this TableLock object by taking the // maximum option and minimum inspection interval. // The option order (ascending) is UserLocking, AutoLocking, // PermanentLocking. // When an interval was defaulted, it is not taken into account. // An option DefaultLocking is not taken into account. void merge (const TableLock& that); // Get the locking option. LockOption option() const; // Is read locking needed? Bool readLocking() const; // Is permanent locking used? Bool isPermanent() const; // Get the inspection interval. double interval() const; // Get the maximum wait period in AutoLocking mode. uInt maxWait() const; // Is table locking disabled (because AIPS_TABLE_NOLOCKING or table.nolocking is set)? static Bool lockingDisabled(); private: LockOption itsOption; Bool itsReadLocking; uInt itsMaxWait; double itsInterval; Bool itsIsDefaultLocking; Bool itsIsDefaultInterval; // Set itsOption and itsReadLocking when needed. void init(); }; inline TableLock::LockOption TableLock::option() const { return itsOption; } inline Bool TableLock::readLocking() const { return itsReadLocking; } inline Bool TableLock::isPermanent() const { return (itsOption == PermanentLocking || itsOption == PermanentLockingWait); } inline double TableLock::interval() const { return itsInterval; } inline uInt TableLock::maxWait() const { return itsMaxWait; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/TableLockData.cc000066400000000000000000000106741476623553700207610ustar00rootroot00000000000000//# TableLockData.cc: Class to hold table lock data //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableLockData::TableLockData (const TableLock& lockOptions, TableLockData::ReleaseCallBack* releaseCallBack, void* releaseParentObject) : TableLock (lockOptions), itsLock (0), itsReleaseCallBack (releaseCallBack), itsReleaseParent (releaseParentObject) {} TableLockData::~TableLockData() { delete itsLock; } void TableLockData::makeLock (const String& name, Bool create, FileLocker::LockType type, uInt locknr) { //# Create lock file object only when not created yet. //# It is acceptable that no lock file exists for a readonly table //# (to be able to read older tables). if (itsLock == 0) { itsLock = new LockFile (name + "/table.lock", interval(), create, True, False, locknr, isPermanent(), option() == NoLocking); } //# Acquire a lock when permanent locking is in use. if (isPermanent()) { uInt nattempts = 1; if (option() == PermanentLockingWait) { nattempts = 0; // wait } if (! itsLock->acquire (type, nattempts)) { throw (TableError ("Permanent lock on table " + name + " could not be acquired (" + itsLock->lastMessage() + ")")); } } } Bool TableLockData::acquire (MemoryIO* info, FileLocker::LockType type, uInt nattempts) { //# Try to acquire a lock. //# Show a message when we have to wait for a long time. //# Start with n attempts, show a message and continue thereafter. uInt n = 30; if (nattempts > 0 && nattempts < n) { n = nattempts; } Bool status = itsLock->acquire (info, type, n); if (!status && n != nattempts) { String s = "read"; if (type == FileLocker::Write) { s = "write"; } LogIO os; os << "Process " << uInt(getpid()) << ": waiting for " << s << "-lock on file " << itsLock->name(); os.post(); if (nattempts > 0) { nattempts -= n; } status = itsLock->acquire (info, type, nattempts); if (status) { os << "Process " << uInt(getpid()) << ": acquired " << s << "-lock on file " << itsLock->name(); os.post(); }else{ if (nattempts > 0) { os << "Process " << uInt(getpid()) << ": gave up acquiring " << s << "-lock on file " << itsLock->name() << " after " << nattempts << " seconds"; os.post(); } } } //# Throw exception when error while we had to wait forever. if (!status) { if (nattempts == 0) { throw (TableError ("Error (" + itsLock->lastMessage() + ") when acquiring lock on " + itsLock->name())); } } return status; } void TableLockData::release (Bool always) { //# Only release if not permanently locked. if (always || !isPermanent()) { MemoryIO* memIO = 0; if (hasLock (FileLocker::Write)) { if (itsReleaseCallBack != 0) { memIO = itsReleaseCallBack (itsReleaseParent, always); } } if (! itsLock->release (memIO)) { throw (TableError ("Error (" + itsLock->lastMessage() + ") when releasing lock on " + itsLock->name())); } } } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/TableLockData.h000066400000000000000000000122631476623553700206170ustar00rootroot00000000000000//# TableLockData.h: Class to hold table lock data //# Copyright (C) 1997,1998,1999,2000,2002,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABLELOCKDATA_H #define TABLES_TABLELOCKDATA_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //

        // Class to hold table lock data. // // // // // //
      • class TableLock // // // This class keeps the LockFile object used to do the // actual locking/unlocking. // It also keeps the synchronization information. // // // Encapsulate Table locking data. // class TableLockData : public TableLock { public: // Define the signature of the callback function when a lock is released. // The flag always tells if the callback function should // always write its main data (meant for case that table gets closed). // The callback function has to write the synchronization data // (preferably in canonical format) in a MemoryIO object. // A pointer to this MemoryIO object has to be returned. A zero pointer // can be returned when no synchronization data is available. typedef MemoryIO* ReleaseCallBack (void* parentObject, Bool always); // Construct from the given TableLock object. TableLockData (const TableLock& lockOptions, ReleaseCallBack* = 0, void* releaseParentObject = 0); ~TableLockData(); // Copy constructor is forbidden. TableLockData (const TableLockData& that) = delete; // Assignment is forbidden. TableLockData& operator= (const TableLockData& that) = delete; // Create the LockFile object and acquire a read or write // lock when permanent locking is in effect. // It throws an exception when acquiring the lock failed. void makeLock (const String& name, Bool create, FileLocker::LockType, uInt locknr = 0); // Acquire a read or write lock. // It throws an exception when acquire failed while it had to wait. Bool acquire (MemoryIO* info, FileLocker::LockType, uInt nattempts); // Release the lock. When always==False, the lock is not released // when a permanent lock is used. // It does nothing when permanent locking is used. // It throws an exception when the release failed. // When the lock is released, the release callback function (if defined) // is called to write the synchronization data. void release (Bool always = False); // When the inspection interval has expired, inspect if another process // needs the lock. If so, release the lock. // always=True means that the inspection is always done, // thus not every 25th call or so. void autoRelease (Bool always=False); // Has this process the read or write lock, thus can the table // be read or written safely? Bool hasLock (FileLocker::LockType) const; // Is the table in use (i.e. open) in another process? Bool isMultiUsed() const; // Get or put the info in the lock file. // void getInfo (MemoryIO& info); void putInfo (const MemoryIO& info); // private: //# Define the lock file. LockFile* itsLock; //# Define if the file is already read or write locked. ReleaseCallBack* itsReleaseCallBack; void* itsReleaseParent; }; inline Bool TableLockData::hasLock (FileLocker::LockType type) const { return (itsLock == 0 ? True : itsLock->hasLock (type)); } inline void TableLockData::autoRelease (Bool always) { if (option() == AutoLocking && itsLock->inspect(always)) { release(); } } inline Bool TableLockData::isMultiUsed() const { return itsLock->isMultiUsed(); } inline void TableLockData::getInfo (MemoryIO& info) { itsLock->getInfo (info); } inline void TableLockData::putInfo (const MemoryIO& info) { itsLock->putInfo (info); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/TableLocker.cc000066400000000000000000000040751476623553700205140ustar00rootroot00000000000000//# TableLocker.cc: Class to hold a (user) lock on a table //# Copyright (C) 1998,2000,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableLocker::TableLocker (Table& table, FileLocker::LockType type, uInt nattempts) : itsTable (table), itsHadLock (table.hasLock(type)) { if (!itsHadLock) { if (type == FileLocker::Read && !table.lockOptions().readLocking()) { // Read lock not needed if NoReadLocking. itsHadLock = True; } else { // Acquire the lock. if (! itsTable.lock (type, nattempts)) { String str = "write"; if (type == FileLocker::Read) { str = "read"; } throw (TableError ("No " + str + " lock could be acquired on table " + itsTable.tableName())); } } } } TableLocker::~TableLocker() { if (!itsHadLock) { itsTable.unlock(); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/TableLocker.h000066400000000000000000000120411476623553700203460ustar00rootroot00000000000000//# TableLocker.h: Class to hold a (user) lock on a table //# Copyright (C) 1998,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABLELOCKER_H #define TABLES_TABLELOCKER_H //# Includes #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Class to hold a (user) lock on a table. // // // // // //# Classes you should understand before using this one. //
      • Table //
      • TableLock // // // Class TableLocker can be used to acquire a (user) lock on a table. // The lock can be a read or write lock. // The destructor only releases the lock if the lock was acquired by the // constructor. //

        // TableLocker simply uses the lock and unlock // function of class Table. // The advantage of TableLocker over these functions is that the // destructor of TableLocker is called automatically by the system, // so unlocking the table does not need to be done explicitly and // cannot be forgotten. Especially in case of exception handling this // can be quite an adavantage. //

        // This class is meant to be used with the UserLocking option. // It can, however, also be used with the other locking options. // In case of PermanentLocking(Wait) it won't do anything at all. // In case of AutoLocking it will acquire and release the lock when // needed. However, it is possible that the system releases an // auto lock before the TableLocker destructor is called. // // // // // Open a table to be updated. // Table myTable ("theTable", TableLock::UserLocking, Table::Update); // // Start of some critical section requiring a lock. // { // TableLocker lock1 (myTable); // ... write the data // } // // The TableLocker destructor invoked by } unlocked the table. // // // // TableLocker makes it easier to unlock a table. // //# //# A List of bugs, limitations, extensions or planned refinements. //# class TableLocker { public: // The constructor acquires a read or write lock on a table // which is released by the destructor. // If the table was already locked, the destructor will // not unlock the table. //
        // The number of attempts (default = forever) can be specified when // acquiring the lock does not succeed immediately. When nattempts>1, // the system waits 1 second between each attempt, so nattempts // is more or less equal to a wait period in seconds. // An exception is thrown when the lock cannot be acquired. explicit TableLocker (Table& table, FileLocker::LockType = FileLocker::Write, uInt nattempts = 0); // If locked, the destructor releases the lock and flushes the data. ~TableLocker(); // The copy constructor and assignment are not possible. // Note that only one lock can be held on a table, so copying a // TableLocker object imposes great difficulties which objects should // release the lock. // It can be solved by turning TableLocker into a handle class // with a reference counted body class. // However, that will only be done when the need arises. // TableLocker (const TableLocker&) = delete; TableLocker& operator= (const TableLocker&) = delete; // // Has this process the read or write lock, thus can the table // be read or written safely? Bool hasLock (FileLocker::LockType = FileLocker::Write) const; private: //# Variables. Table itsTable; bool itsHadLock; }; inline Bool TableLocker::hasLock (FileLocker::LockType type) const { return itsTable.hasLock (type); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/TableProxy.cc000066400000000000000000002761011476623553700204170ustar00rootroot00000000000000//# TableProxy.cc: High-level interface to tables //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001,2002,2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // needed for snprintf namespace casacore { //# NAMESPACE CASACORE - BEGIN TableProxy::TableProxy() {} TableProxy::TableProxy (const String& tableName, const Record& lockOptions, int option) { table_p = TableUtil::openTable (tableName, makeLockOptions(lockOptions), Table::TableOption(option)); } TableProxy::TableProxy (const String& tableName, const Record& lockOptions, const String& endianFormat, const String& memType, Int64 nrow, const Record& tableDesc, const Record& dmInfo) { // Interpret the endian option. Table::EndianFormat endOpt = makeEndianFormat (endianFormat); // Get the type. Table::TableType type = Table::Plain; if (memType == "memory") { type = Table::Memory; } // Get nr of rows. if (nrow < 0) { nrow = 0; } TableDesc tabdesc; String message; if (!makeTableDesc (tableDesc, tabdesc, message)) { throw TableError (tableName + " failed: " + message); } // Try to create the table (scratch if no table name given). table_p = TableUtil::createTable (tableName, tabdesc, Table::New, type, StorageOption(), dmInfo, makeLockOptions(lockOptions), nrow, False, endOpt); } TableProxy::TableProxy (const Vector& tableNames, const Vector& concatenateSubTableNames, const Record& lockOptions, int option) { // Open the tables here (and not by Table ctor) to make it // possible to use the :: syntax for subtables. TableLock lockOpt = makeLockOptions(lockOptions); Block

      • tabs(tableNames.size()); for (uInt i=0; i subNames(concatenateSubTableNames.size()); std::copy (concatenateSubTableNames.begin(), concatenateSubTableNames.end(), subNames.begin()); table_p = Table (tabs, subNames); } TableProxy::TableProxy (const std::vector& tables, const Vector& concatenateSubTableNames, int, int, int) { Block
        tabs(tables.size()); for (uInt i=0; i subNames(concatenateSubTableNames.size()); std::copy (concatenateSubTableNames.begin(), concatenateSubTableNames.end(), subNames.begin()); table_p = Table (tabs, subNames); } TableProxy::TableProxy (const String& command, const std::vector& tables) { std::vector tabs(tables.size()); for (uInt i=0; i& columnNames, const Vector& dataTypes) { if (separator.length() != 1) { throw AipsError ("tablefromascii : separator must be 1 char"); } Char sep = separator[0]; // Create the table String inputFormat; if (headerName.empty()) { if (columnNames.size() == 0 && dataTypes.size() == 0) { asciiFormat_p = readAsciiTable(fileName, "", tableName, autoHeader, sep, commentMarker, firstLine, lastLine, IPosition(autoShape)); } else { asciiFormat_p = readAsciiTable(fileName, "", tableName, columnNames, dataTypes, sep, commentMarker, firstLine, lastLine); } } else { asciiFormat_p = readAsciiTable(headerName, fileName, "", tableName, sep, commentMarker, firstLine, lastLine); } // Open the table. table_p = Table(tableName); } TableProxy::TableProxy (const TableProxy& that) : table_p (that.table_p) {} TableProxy::~TableProxy() {} TableProxy& TableProxy::operator= (const TableProxy& that) { if (this != &that) { table_p = that.table_p; } return *this; } String TableProxy::endianFormat() const { // Return the endian format as a string. if (table_p.endianFormat() == Table::BigEndian) { return "big"; } return "little"; } void TableProxy::lock (Bool mode, Int nattempts) { table_p.lock (mode, nattempts); } void TableProxy::unlock() { table_p.unlock(); } Bool TableProxy::hasDataChanged() { return table_p.hasDataChanged(); } Bool TableProxy::hasLock (Bool mode) { return table_p.hasLock (mode); } Record TableProxy::lockOptions() { // Return the lock options as a record. const TableLock& lock = table_p.lockOptions(); Record rec; String option; switch (lock.option()) { case TableLock::PermanentLocking: option = "permanent"; break; case TableLock::PermanentLockingWait: option = "permanentwait"; break; case TableLock::UserLocking: if (lock.readLocking()) { option = "user"; } else { option = "usernoread"; } break; case TableLock::AutoLocking: if (lock.readLocking()) { option = "auto"; } else { option = "autonoread"; } break; default: option = "unknown"; } rec.define ("option", option); rec.define ("interval", lock.interval()); rec.define ("maxwait", Int(lock.maxWait())); return rec; } Bool TableProxy::isMultiUsed (Bool checkSubTables) { return table_p.isMultiUsed (checkSubTables); } String TableProxy::toAscii (const String& asciiFile, const String& headerFile, const Vector& columns, const String& sep, const Vector& precision, Bool useBrackets) { // Possible warning message. String message; // Determine separator String theSep(sep); if (sep.empty()) { theSep = " "; } // Determine names of columns to write. Vector colNames(columns); if (columns.empty() || columns(0).empty()) { // No columns given, so use all. colNames.assign (columnNames()); } Int ncols = colNames.size(); // Analyse the columns. vector col_is_good(ncols); vector col_type(ncols); Int last_good_col = 0; for (Int j=0; j 0) { colShape = TableColumn(table_p, colName).shape(0); } } for (uInt i=0; i 0) { oss << ","; } oss << colShape[i]; } if (useBrackets) { oss << "]"; } } colType = oss.str(); } return good; } void TableProxy::printValueHolder (const ValueHolder& vh, ostream& os, const String& sep, Int prec, bool useBrackets) const { Int defPrec = 18; switch (vh.dataType()) { case TpBool: os << vh.asBool(); break; case TpUChar: case TpShort: case TpUShort: case TpInt: case TpUInt: case TpInt64: os << vh.asInt64(); break; case TpFloat: defPrec = 9; CASACORE_FALLTHROUGH; case TpDouble: { // set precision; set it back at the end. if (prec <= 0) prec = defPrec; streamsize oldPrec = os.precision(prec); os << vh.asDouble(); os.precision (oldPrec); } break; case TpComplex: defPrec = 9; CASACORE_FALLTHROUGH; case TpDComplex: { // set precision; set it back at the end. if (prec <= 0) prec = defPrec; streamsize oldPrec = os.precision(prec); os << vh.asDComplex(); os.precision (oldPrec); } break; case TpString: os << '"' << vh.asString() << '"'; break; case TpArrayBool: { Array arr = vh.asArrayBool(); if (useBrackets) { printArray (arr, os, sep); } else { Array::const_iterator iterend = arr.end(); for (Array::const_iterator iter=arr.begin(); iter!=iterend; ++iter) { if (iter != arr.begin()) { os << sep; } os << *iter; } } } break; case TpArrayUChar: case TpArrayShort: case TpArrayUShort: case TpArrayInt: case TpArrayUInt: case TpArrayInt64: { Array arr = vh.asArrayInt64(); if (useBrackets) { printArray (arr, os, sep); } else { Array::const_iterator iterend = arr.end(); for (Array::const_iterator iter=arr.begin(); iter!=iterend; ++iter) { if (iter != arr.begin()) { os << sep; } os << *iter; } } } break; case TpArrayFloat: defPrec = 9; CASACORE_FALLTHROUGH; case TpArrayDouble: { // set precision; set it back at the end. if (prec <= 0) prec = defPrec; streamsize oldPrec = os.precision(prec); Array arr = vh.asArrayDouble(); if (useBrackets) { printArray (arr, os, sep); } else { Array::const_iterator iterend = arr.end(); for (Array::const_iterator iter=arr.begin(); iter!=iterend; ++iter) { if (iter != arr.begin()) { os << sep; } os << *iter; } } os.precision (oldPrec); } break; case TpArrayComplex: defPrec = 9; CASACORE_FALLTHROUGH; case TpArrayDComplex: { // set precision; set it back at the end. if (prec <= 0) prec = defPrec; streamsize oldPrec = os.precision(prec); Array arr = vh.asArrayDComplex(); if (useBrackets) { printArray (arr, os, sep); } else { Array::const_iterator iterend = arr.end(); for (Array::const_iterator iter=arr.begin(); iter!=iterend; ++iter) { if (iter != arr.begin()) { os << sep; } os << iter->real() << sep << iter->imag(); } } os.precision (oldPrec); } break; case TpArrayString: { Array arr = vh.asArrayString(); if (useBrackets) { printArray (arr, os, sep); } else { Array::const_iterator iterend = arr.end(); for (Array::const_iterator iter=arr.begin(); iter!=iterend; ++iter) { if (iter != arr.begin()) { os << sep; } os << '"' << *iter << '"'; } } } break; case TpRecord: os << '{' << vh.asRecord() << '}'; break; default: throw AipsError ("ValueHolder::write - unknown data type"); break; } } template void TableProxy::printArray (const Array& arr, ostream& os, const String& sep) const { if (arr.empty()) { cout << "[]"; } else { const IPosition& shp = arr.shape(); uInt ndim = shp.size(); IPosition pos(shp.size(), 0); typename Array::const_iterator iter = arr.begin(); uInt i = ndim; while (True) { for (uInt j=0; j 0 || noRows) { valueCopy = True; } Table outtab; if (toMemory) { outtab = table_p.copyToMemoryTable (newTableName, noRows); } else { if (deepCopy || valueCopy) { table_p.deepCopy (newTableName, dminfo, Table::New, valueCopy, endOpt, noRows); } else { table_p.copy (newTableName, Table::New); } outtab = Table(newTableName); } return TableProxy(outtab); } void TableProxy::copyRows (TableProxy& out, Int64 startIn, Int64 startOut, Int64 nrow) { Table tableOut = out.table(); if (startOut < 0) { startOut = tableOut.nrow(); } nrow = checkRowColumn (table_p, "", startIn, nrow, 1, "TableProxy::copyRows"); if (startOut > Int64(tableOut.nrow())) { throw TableError ("TableProxy::copyRows: start output row too high"); } TableCopy::copyRows (tableOut, table_p, startOut, startIn, nrow); } void TableProxy::deleteTable (Bool checkSubTables) { if (table_p.isMultiUsed (False)) { throw TableError ("Table " + table_p.tableName() + " cannot be deleted; it is used by another process"); } if (checkSubTables) { if (table_p.isMultiUsed (True)) { throw TableError ("Table " + table_p.tableName() + " cannot be deleted;" " one of its subtables is used by another process"); } } table_p.markForDelete(); } TableProxy TableProxy::selectRows (const Vector& rownrs, const String& outName) { // If needed, synchronize table to get up-to-date number of rows. syncTable (table_p); if (anyLT (rownrs, Int64(0)) || anyGE (rownrs, Int64(table_p.nrow()))) { throw TableError("rownumbers should be >= 1 and <= nrow"); } table_p.unlock(); // Create a table from the selected rows. // Rename it and make it non-scratch if a name is given. Vector rows(rownrs.nelements()); convertArray (rows, rownrs); Table ntable = table_p(rows); if (! outName.empty()) { ntable.rename (outName, Table::New); } // Command succeeded. return ntable; } void TableProxy::stillSameShape (Int& same, IPosition& shape, const IPosition& newShape) { if (same == 0) { same = 1; // not first time anymore shape = newShape; } else if (! newShape.isEqual(shape)) { same = 2; // varying shape } } void TableProxy::calcValues (Record& rec, const TableExprNode& expr) { if (expr.isScalar()) { Vector rownrs(expr.nrow()); indgen (rownrs); switch (expr.getColumnDataType()) { case TpBool: rec.define ("values", expr.getColumnBool (rownrs)); break; case TpUChar: rec.define ("values", expr.getColumnuChar (rownrs)); break; case TpShort: rec.define ("values", expr.getColumnShort (rownrs)); break; case TpUShort: { Vector vs = expr.getColumnuShort (rownrs); Vector vi(vs.nelements()); convertArray (vi, vs); rec.define ("values", vi); break; } case TpInt: rec.define ("values", expr.getColumnInt (rownrs)); break; case TpUInt: { Vector vs = expr.getColumnuInt (rownrs); Vector vi(vs.nelements()); convertArray (vi, vs); rec.define ("values", vi); break; } case TpInt64: rec.define ("values", expr.getColumnInt64 (rownrs)); break; case TpFloat: rec.define ("values", expr.getColumnFloat (rownrs)); break; case TpDouble: rec.define ("values", expr.getColumnDouble (rownrs)); break; case TpComplex: rec.define ("values", expr.getColumnComplex (rownrs)); break; case TpDComplex: rec.define ("values", expr.getColumnDComplex (rownrs)); break; case TpString: rec.define ("values", expr.getColumnString (rownrs)); break; default: throw AipsError("Unknown calc expression scalar type"); } } else { // Array result. Check if shape is always the same. Int sameShape = 0; IPosition resShape; Record res; switch (expr.dataType()) { case TpBool: for (rownr_t i=0; i arr; expr.get (i, arr); res.define (String::toString(i), arr.array()); stillSameShape (sameShape, resShape, arr.shape()); } break; case TpInt64: for (rownr_t i=0; i arr; expr.get (i, arr); res.define (String::toString(i), arr.array()); stillSameShape (sameShape, resShape, arr.shape()); } break; case TpDouble: for (rownr_t i=0; i arr; expr.get (i, arr); res.define (String::toString(i), arr.array()); stillSameShape (sameShape, resShape, arr.shape()); } break; case TpDComplex: for (rownr_t i=0; i arr; expr.get (i, arr); res.define (String::toString(i), arr.array()); stillSameShape (sameShape, resShape, arr.shape()); } break; case TpString: for (rownr_t i=0; i arr; expr.get (i, arr); res.define (String::toString(i), arr.array()); stillSameShape (sameShape, resShape, arr.shape()); } break; default: throw AipsError("Unknown calc expression array type"); } if (sameShape == 2) { // Varying shape, so define as Records. rec.defineRecord ("values", res); // All the same shape, so define as a single array. } else { switch (expr.dataType()) { case TpBool: rec.define ("values", record2Array(res)); break; case TpInt64: rec.define ("values", record2Array(res)); break; case TpDouble: rec.define ("values", record2Array(res)); break; case TpDComplex: rec.define ("values", record2Array(res)); break; case TpString: rec.define ("values", record2Array(res)); break; default: throw AipsError("Unknown calc expression array type"); } } } } Record TableProxy::getDataManagerInfo() { return table_p.dataManagerInfo(); } Record TableProxy::getProperties (const String& name, Bool byColumn) { RODataManAccessor acc (table_p, name, byColumn); return acc.getProperties(); } void TableProxy::setProperties (const String& name, const Record& properties, Bool byColumn) { RODataManAccessor acc (table_p, name, byColumn); acc.setProperties (properties); } Record TableProxy::getTableDescription (Bool actual, Bool cOrder) { // Get the table description. std::unique_ptr tableDescPtr; if (actual) { tableDescPtr.reset (new TableDesc(table_p.actualTableDesc())); } else { tableDescPtr.reset (new TableDesc(table_p.tableDesc())); } Record rec = getTableDesc(*tableDescPtr, cOrder); return rec; } Record TableProxy::getTableDesc(const TableDesc & tabdesc, Bool cOrder) { Record rec; // Convert columns for (uInt i=0; i tableDescPtr; if (actual) { tableDescPtr.reset (new TableDesc(table_p.actualTableDesc())); } else { tableDescPtr.reset (new TableDesc(table_p.tableDesc())); } // Return the column description as a record. const ColumnDesc& columnDescription = (*tableDescPtr) [columnName]; return recordColumnDesc (columnDescription, cOrder); } String TableProxy::tableName() { return table_p.tableName(); } Vector TableProxy::getPartNames (Bool recursive) { Block partNames(table_p.getPartNames (recursive)); return Vector(partNames.begin(), partNames.end()); } String TableProxy::getAsciiFormat() const { return asciiFormat_p; } Record TableProxy::getCalcResult() const { return calcResult_p; } String TableProxy::showStructure (Bool showDataMan, Bool showColumns, Bool showSubTables, Bool sortColumns) const { ostringstream ostr; table_p.showStructure (ostr, showDataMan, showColumns, showSubTables, sortColumns); return ostr.str(); } Int64 TableProxy::nrows() { // If needed synchronize table to get up-to-date number of rows. syncTable (table_p); return table_p.nrow(); } Int TableProxy::ncolumns() { return table_p.tableDesc().ncolumn(); } Vector TableProxy::shape() { // If needed synchronize table to get up-to-date number of rows. syncTable (table_p); Vector result(2); result(0) = table_p.tableDesc().ncolumn(); result(1) = table_p.nrow(); return result; } Vector TableProxy::rowNumbers (TableProxy& other) { // If needed synchronize table to get up-to-date number of rows. syncTable (table_p); table_p.unlock(); Vector result(table_p.nrow()); if (other.table().isNull()) { convertArray (result, table_p.rowNumbers()); } else { convertArray (result, table_p.rowNumbers(other.table())); } return result; } Vector TableProxy::columnNames() { // Put the column names into a vector. const TableDesc& tabdesc = table_p.tableDesc(); Vector result (tabdesc.ncolumn()); for (uInt i=0; i< result.nelements(); i++) { result(i) = tabdesc.columnDesc(i).name(); } return result; } void TableProxy::setMaximumCacheSize (const String& columnName, Int nbytes) { TableColumn col (table_p, columnName); col.setMaximumCacheSize (nbytes); } Bool TableProxy::isScalarColumn (const String& columnName) { const TableDesc& tabdesc = table_p.tableDesc(); return tabdesc.columnDesc(columnName).isScalar(); } String TableProxy::columnDataType (const String& columnName) { const TableDesc& tabdesc = table_p.tableDesc(); DataType type = tabdesc.columnDesc(columnName).dataType(); return getTypeStr(type); } String TableProxy::columnArrayType (const String& columnName) { const TableDesc& tableDesc = table_p.tableDesc(); const ColumnDesc& coldesc = tableDesc.columnDesc (columnName); if (coldesc.isScalar()) { throw TableError("column " + columnName + " is a scalar column"); } String result; int columnOption = coldesc.options (); if (columnOption & ColumnDesc::Direct) { result = "Direct"; } else { result = "Indirect"; } if (columnOption & ColumnDesc::FixedShape) { result += ", fixed"; } else { result += ", variable"; } result += " sized arrays"; return result; } ValueHolder TableProxy::getCell (const String& columnName, Int64 row) { Int64 nrow = getRowsCheck (columnName, row, 1, 1, "getCell"); return getValueFromTable (columnName, row, nrow, 1, True); } void TableProxy::getCellVH (const String& columnName, Int64 row, const ValueHolder& vh) { Int64 nrow = getRowsCheck (columnName, row, 1, 1, "getCellVH"); getValueFromTable (columnName, row, nrow, 1, True, vh); } ValueHolder TableProxy::getCellSlice (const String& columnName, Int64 row, const Vector& blc, const Vector& trc, const Vector& inc) { return getCellSliceIP (columnName, row, blc, trc, inc); } void TableProxy::getCellSliceVH (const String& columnName, Int64 row, const Vector& blc, const Vector& trc, const Vector& inc, const ValueHolder& vh) { return getCellSliceVHIP (columnName, row, blc, trc, inc, vh); } ValueHolder TableProxy::getCellSliceIP (const String& columnName, Int64 row, const IPosition& blc, const IPosition& trc, const IPosition& inc) { Slicer slicer; Int64 nrow = getRowsSliceCheck (slicer, columnName, row, 1, 1, blc, trc, inc, "getCellSlice"); return getValueSliceFromTable (columnName, slicer, row, nrow, 1, True); } void TableProxy::getCellSliceVHIP (const String& columnName, Int64 row, const IPosition& blc, const IPosition& trc, const IPosition& inc, const ValueHolder& vh) { Slicer slicer; Int64 nrow = getRowsSliceCheck (slicer, columnName, row, 1, 1, blc, trc, inc, "getCellSliceVH"); getValueSliceFromTable (columnName, slicer, row, nrow, 1, True, vh); } ValueHolder TableProxy::getColumn (const String& columnName, Int64 row, Int64 nrow, Int64 incr) { Int64 nrows = getRowsCheck (columnName, row, nrow, incr, "getColumn"); return getValueFromTable (columnName, row, nrows, incr, False); } void TableProxy::getColumnVH (const String& columnName, Int64 row, Int64 nrow, Int64 incr, const ValueHolder& vh) { Int64 nrows = getRowsCheck (columnName, row, nrow, incr, "getColumnVH"); return getValueFromTable (columnName, row, nrows, incr, False, vh); } Record TableProxy::getVarColumn (const String& columnName, Int64 row, Int64 nrow, Int64 incr) { Int64 nrows = getRowsCheck (columnName, row, nrow, incr, "getVarColumn"); TableColumn tabcol (table_p, columnName); Record rec; char namebuf[22]; for (Int64 i=0; i& blc, const Vector& trc, const Vector& inc) { return getColumnSliceIP (columnName, blc, trc, inc, row, nrow, incr); } ValueHolder TableProxy::getColumnSliceIP (const String& columnName, const IPosition& blc, const IPosition& trc, const IPosition& inc, Int64 row, Int64 nrow, Int64 incr) { Slicer slicer; Int64 nrows = getRowsSliceCheck (slicer, columnName, row, nrow, incr, blc, trc, inc, "getColumnSlice"); return getValueSliceFromTable (columnName, slicer, row, nrows, incr, False); } void TableProxy::getColumnSliceVH (const String& columnName, Int64 row, Int64 nrow, Int64 incr, const Vector& blc, const Vector& trc, const Vector& inc, const ValueHolder& vh) { getColumnSliceVHIP (columnName, blc, trc, inc, row, nrow, incr, vh); } void TableProxy::getColumnSliceVHIP (const String& columnName, const IPosition& blc, const IPosition& trc, const IPosition& inc, Int64 row, Int64 nrow, Int64 incr, const ValueHolder& vh) { Slicer slicer; Int64 nrows = getRowsSliceCheck (slicer, columnName, row, nrow, incr, blc, trc, inc, "getColumnSliceVH"); getValueSliceFromTable (columnName, slicer, row, nrows, incr, False, vh); } void TableProxy::putColumn (const String& columnName, Int64 row, Int64 nrow, Int64 incr, const ValueHolder& value) { // Synchronize table to get up-to-date #rows. // Check that the row number is within the table bounds. syncTable (table_p); nrow = checkRowColumn (table_p, columnName, row, nrow, incr, "TableProxy::putColumn"); putValueInTable (columnName, row, nrow, incr, False, value); } void TableProxy::putVarColumn (const String& columnName, Int64 row, Int64 nrow, Int64 incr, const Record& values) { // Synchronize table to get up-to-date #rows. // Check that the row number is within the table bounds. syncTable (table_p); nrow = checkRowColumn (table_p, columnName, row, nrow, incr, "TableProxy::putVarColumn"); if (Int(values.nfields()) != nrow) { throw TableError("TableProxy::putVarColumn: " "#rows mismatches #elem in value"); } for (Int64 i=0; i& blc, const Vector& trc, const Vector& inc, const ValueHolder& value) { putColumnSliceIP (columnName, value, blc, trc, inc, row, nrow, incr); } void TableProxy::putColumnSliceIP (const String& columnName, const ValueHolder& value, const IPosition& blc, const IPosition& trc, const IPosition& inc, Int64 row, Int64 nrow, Int64 incr) { IPosition cblc, ctrc; cblc = blc; ctrc = trc; setDefaultForSlicer (cblc); setDefaultForSlicer (ctrc); Slicer slicer; if (inc.nelements() > 0) { slicer = Slicer (cblc, ctrc, inc, Slicer::endIsLast); }else{ slicer = Slicer (cblc, ctrc, Slicer::endIsLast); } // Synchronize table to get up-to-date #rows. // Check that the row number is within the table bounds. syncTable (table_p); nrow = checkRowColumn (table_p, columnName, row, nrow, incr, "TableProxy::putColumn"); putValueSliceInTable (columnName, slicer, row, nrow, incr, False, value); } void TableProxy::putCell (const String& columnName, const Vector& rownrs, const ValueHolder& value) { // Synchronize table to get up-to-date #rows. syncTable (table_p); for (rownr_t i=0; i& blc, const Vector& trc, const Vector& inc, const ValueHolder& value) { putCellSliceIP (columnName, row, value, blc, trc, inc); } void TableProxy::putCellSliceIP (const String& columnName, Int64 row, const ValueHolder& value, const IPosition& blc, const IPosition& trc, const IPosition& inc) { IPosition cblc, ctrc; cblc = blc; ctrc = trc; setDefaultForSlicer (cblc); setDefaultForSlicer (ctrc); Slicer slicer; if (inc.nelements() > 0) { slicer = Slicer (cblc, ctrc, inc, Slicer::endIsLast); }else{ slicer = Slicer (cblc, ctrc, Slicer::endIsLast); } // Synchronize table to get up-to-date #rows. // Check that the row number is within the table bounds. syncTable (table_p); Int64 nrow = checkRowColumn (table_p, columnName, row, 1, 1, "TableProxy::putColumn"); putValueSliceInTable (columnName, slicer, row, nrow, 1, True, value); } Vector TableProxy::getColumnShapeString (const String& columnName, Int64 rownr, Int64 nrow, Int64 incr, Bool cOrder) { // If needed synchronize table to get up-to-date number of rows. syncTable (table_p); // Check that the row number is within the table bounds. // However, accept a row number equal to nrow when no rows are needed. Int64 tabnrow = table_p.nrow(); if (rownr < 0 || rownr > tabnrow || (rownr==tabnrow && nrow>0)) { throw TableError("TableProxy::getColumnShapeString: no such row"); } if (incr <= 0) { throw TableError("TableProxy::getColumnShapeString: rowincr<=0"); } if (! table_p.tableDesc().isColumn(columnName)) { throw TableError("TableProxy::getColumnShapeString: column " + columnName + " does not exist"); } Int64 maxnrow = (tabnrow - rownr + incr - 1) / incr; if (nrow < 0 || nrow > maxnrow) { nrow = maxnrow; } Vector result; TableColumn col (table_p, columnName); IPosition shape = col.shapeColumn(); if (shape.nelements() > 0) { //# This is a fixed shape, so return immediately. ostringstream os; os << fillAxes (shape, cOrder); result.resize(1); result(0) = os.str(); } else { result.resize (nrow); Int64 lastRow(nrow+rownr); for (Int64 i=0; iasValueHolder(fieldid); } Record TableProxy::getKeywordSet (const String& columnName) { const TableRecord* keySet; if (columnName.empty()) { keySet = &(table_p.keywordSet()); }else{ TableColumn tabColumn (table_p, columnName); keySet = &(tabColumn.keywordSet()); } return keySet->toRecord(); } void TableProxy::putKeyword (const String& columnName, const String& keywordName, Int keywordIndex, Bool makeSubRecord, const ValueHolder& value) { TableRecord* keySet; if (columnName.empty()) { keySet = &(table_p.rwKeywordSet()); } else { TableColumn tabColumn (table_p, columnName); keySet = &(tabColumn.rwKeywordSet()); } RecordFieldId fieldid(0); if (keywordName.empty()) { fieldid = RecordFieldId(keywordIndex-1); } else { findKeyId (fieldid, keySet, keywordName, columnName, False, True, makeSubRecord); } keySet->defineFromValueHolder (fieldid, value); } void TableProxy::putKeywordSet (const String& columnName, const Record& valueSet) { TableRecord* keySet; if (columnName.empty()) { keySet = &(table_p.rwKeywordSet()); } else { TableColumn tabColumn (table_p, columnName); keySet = &(tabColumn.rwKeywordSet()); } keySet->fromRecord (valueSet); } void TableProxy::removeKeyword (const String& columnName, const String& keywordName, Int keywordIndex) { TableRecord* keySet; if (columnName.empty()) { keySet = &(table_p.rwKeywordSet()); }else{ TableColumn tabColumn (table_p, columnName); keySet = &(tabColumn.rwKeywordSet()); } RecordFieldId fieldid(0); if (keywordName.empty()) { fieldid = RecordFieldId(keywordIndex); } else { findKeyId (fieldid, keySet, keywordName, columnName, True, True, False); } keySet->removeField (fieldid); } Vector TableProxy::getFieldNames (const String& columnName, const String& keywordName, Int keywordIndex) { const TableRecord* keySet; if (columnName.empty()) { keySet = &(table_p.keywordSet()); }else{ TableColumn tabColumn (table_p, columnName); keySet = &(tabColumn.keywordSet()); } RecordFieldId fieldid(0); if (keywordIndex < 0) { if (! keywordName.empty()) { findKeyId (fieldid, keySet, keywordName, columnName); } } const RecordDesc* desc; if (keywordName.empty()) { desc = &(keySet->description()); } else { if (keySet->dataType (fieldid) != TpRecord) { throw TableError("Keyword does not contain a subrecord"); } desc = &(keySet->subRecord(fieldid).description()); } Vector result(desc->nfields()); for (uInt i=0; iname(i); } return result; } void TableProxy::flush (Bool recursive) { table_p.flush (False, recursive); } void TableProxy::close() { if (! table_p.isNull()) { flush(True); unlock(); table_p = Table(); } } void TableProxy::reopenRW() { table_p.reopenRW(); } void TableProxy::resync() { table_p.resync(); } Record TableProxy::tableInfo() { const TableInfo& info = table_p.tableInfo(); Record rec; rec.define ("type", info.type()); rec.define ("subType", info.subType()); rec.define ("readme", info.readme()); return rec; } void TableProxy::putTableInfo (const Record& value) { if (! table_p.isWritable()) { throw TableError("Table " + table_p.tableName() + " is not writable"); } TableInfo& info = table_p.tableInfo(); // Loop through all fields in the value and check if they are valid. for (uInt i=0; i 0) { table_p.addColumn (tabdesc, dminfo, addToParent); } else { for (uInt i=0; i& columnNames) { table_p.removeColumn (columnNames); } void TableProxy::addRow (Int64 nrow) { table_p.addRow (nrow); } void TableProxy::removeRow (const Vector& rownrs) { // If needed synchronize table to get up-to-date number of rows. syncTable (table_p); Vector rows(rownrs.nelements()); convertArray (rows, rownrs); table_p.removeRow (rows); } Bool TableProxy::makeHC (const Record& gdesc, TableDesc& tabdesc, String& message) { for (uInt i=0; i dataNames; Vector coordNames; Vector idNames; if (! cold.isDefined("HCdatanames")) { message = "No HCdatanames for hypercolumn " + name; return False; } dataNames = cold.asArrayString("HCdatanames"); if (cold.isDefined("HCcoordnames")) { coordNames = cold.asArrayString("HCcoordnames"); } if (cold.isDefined("HCidnames")) { idNames = cold.asArrayString("HCidnames"); } tabdesc.defineHypercolumn (name, ndim, dataNames, coordNames, idNames); } return True; } Bool TableProxy::makeTableDesc (const Record& gdesc, TableDesc& tabdesc, String& message) { for(uInt nrdone=0, nrcols=0; nrdone < gdesc.nfields(); ++nrdone) { String name = gdesc.name(nrdone); const Record& cold (gdesc.asRecord(nrdone)); // Avoid special records for now if(name == "_define_hypercolumn_") { // Ignore, for now, handled later continue; } else if(name == "_define_dminfo_") { // Ignore, this is obsolete continue; } else if(name == "_keywords_") { // Unpack keywords into TableDesc tabdesc.rwKeywordSet().fromRecord(cold); continue; } else if(name == "_private_keywords_") { // Ignore, private keywords are not // publicly accessable on TableDesc continue; } else if(!cold.isDefined("valueType")) { // Assume it is a column and complain as // no value type exists to describe it message = "No value type for column " + name; return False; } String valtype = cold.asString("valueType"); valtype.downcase(); int option = 0; if (cold.isDefined("option")) { option = cold.asInt("option"); } int maxlen = 0; if (cold.isDefined("maxlen")) { maxlen = cold.asInt("maxlen"); } String comment, dmtype, dmgrp; if (cold.isDefined("comment")) { comment = cold.asString ("comment"); } if (cold.isDefined("dataManagerType")) { dmtype = cold.asString("dataManagerType"); } if (cold.isDefined("dataManagerGroup")) { dmgrp = cold.asString("dataManagerGroup"); } Bool isArray = cold.isDefined("ndim"); Int ndim; Vector shape; if (isArray) { ndim = cold.asInt("ndim"); if (cold.isDefined("shape")) { shape = cold.toArrayInt64 ("shape"); } Bool cOrder = False; if (cold.isDefined("_c_order")) { cOrder = cold.asBool ("_c_order"); } if (! addArrayColumnDesc (tabdesc, valtype, name, comment, dmtype, dmgrp, option, ndim, shape, cOrder, message)) { return False; } } else { if (valtype == "boolean" || valtype == "bool") { tabdesc.addColumn (ScalarColumnDesc (name, comment, dmtype, dmgrp, option)); } else if (valtype == "byte" || valtype == "uchar") { tabdesc.addColumn (ScalarColumnDesc (name, comment, dmtype, dmgrp, 0, option)); } else if (valtype == "short") { tabdesc.addColumn (ScalarColumnDesc (name, comment, dmtype, dmgrp, 0, option)); } else if (valtype == "ushort") { tabdesc.addColumn (ScalarColumnDesc (name, comment, dmtype, dmgrp, 0, option)); } else if (valtype == "integer" || valtype == "int") { tabdesc.addColumn (ScalarColumnDesc (name, comment, dmtype, dmgrp, 0, option)); } else if (valtype == "uint") { tabdesc.addColumn (ScalarColumnDesc (name, comment, dmtype, dmgrp, 0, option)); } else if (valtype == "int64") { tabdesc.addColumn (ScalarColumnDesc (name, comment, dmtype, dmgrp, 0, option)); } else if (valtype == "float") { tabdesc.addColumn (ScalarColumnDesc (name, comment, dmtype, dmgrp, option)); } else if (valtype == "double") { tabdesc.addColumn (ScalarColumnDesc (name, comment, dmtype, dmgrp, option)); } else if (valtype == "complex") { tabdesc.addColumn (ScalarColumnDesc (name, comment, dmtype, dmgrp, option)); } else if (valtype == "dcomplex") { tabdesc.addColumn (ScalarColumnDesc (name, comment, dmtype, dmgrp, option)); } else if (valtype == "string") { tabdesc.addColumn (ScalarColumnDesc (name, comment, dmtype, dmgrp, option)); } else if (valtype == "record") { tabdesc.addColumn (ScalarRecordColumnDesc (name, comment, dmtype, dmgrp)); }else{ message = "Unknown data type " + valtype + " for scalar column " + name; return False; } } // Set maximum string length. if (maxlen > 0) { tabdesc.rwColumnDesc(nrcols).setMaxLength (maxlen); } // Define the keywords if needed. if (cold.isDefined ("keywords")) { TableRecord& keySet (tabdesc.rwColumnDesc(nrcols).rwKeywordSet()); keySet.fromRecord (cold.asRecord("keywords")); } ++nrcols; } if (gdesc.isDefined ("_define_hypercolumn_")) { if (! makeHC (gdesc.asRecord("_define_hypercolumn_"), tabdesc, message)) { return False; } } return True; } Bool TableProxy::addArrayColumnDesc (TableDesc& tabdesc, const String& valtype, const String& name, const String& comment, const String& dmtype, const String& dmgrp, int option, Int ndim, const Vector& shape, Bool cOrder, String& message) { if (ndim <= 0 && shape.nelements() > 0) { message = "arrayColumnDesc: shape should not be given when ndim <= 0"; return False; } if (ndim > 0 && shape.nelements() != 0 && uInt(ndim) != shape.nelements()) { message = "arrayColumnDesc: ndim and shape mismatch"; return False; } IPosition shp; if (shape.nelements() > 0) { if (anyLE (shape, Int64(0))) { message = "arrayColumnDesc: shape < 0"; return False; } shp = fillAxes (IPosition(shape), cOrder); option |= ColumnDesc::FixedShape; } if (valtype == "boolean" || valtype == "bool") { if (shp.nelements() > 0) { tabdesc.addColumn (ArrayColumnDesc (name, comment, dmtype, dmgrp, shp, option)); }else{ tabdesc.addColumn (ArrayColumnDesc (name, comment, dmtype, dmgrp, ndim, option)); } } else if (valtype == "byte" || valtype == "uchar") { if (shp.nelements() > 0) { tabdesc.addColumn (ArrayColumnDesc (name, comment, dmtype, dmgrp, shp, option)); }else{ tabdesc.addColumn (ArrayColumnDesc (name, comment, dmtype, dmgrp, ndim, option)); } } else if (valtype == "short") { if (shp.nelements() > 0) { tabdesc.addColumn (ArrayColumnDesc (name, comment, dmtype, dmgrp, shp, option)); }else{ tabdesc.addColumn (ArrayColumnDesc (name, comment, dmtype, dmgrp, ndim, option)); } } else if (valtype == "ushort") { if (shp.nelements() > 0) { tabdesc.addColumn (ArrayColumnDesc (name, comment, dmtype, dmgrp, shp, option)); }else{ tabdesc.addColumn (ArrayColumnDesc (name, comment, dmtype, dmgrp, ndim, option)); } } else if (valtype == "integer" || valtype == "int") { if (shp.nelements() > 0) { tabdesc.addColumn (ArrayColumnDesc (name, comment, dmtype, dmgrp, shp, option)); }else{ tabdesc.addColumn (ArrayColumnDesc (name, comment, dmtype, dmgrp, ndim, option)); } } else if (valtype == "uint") { if (shp.nelements() > 0) { tabdesc.addColumn (ArrayColumnDesc (name, comment, dmtype, dmgrp, shp, option)); }else{ tabdesc.addColumn (ArrayColumnDesc (name, comment, dmtype, dmgrp, ndim, option)); } } else if (valtype == "int64") { if (shp.nelements() > 0) { tabdesc.addColumn (ArrayColumnDesc (name, comment, dmtype, dmgrp, shp, option)); }else{ tabdesc.addColumn (ArrayColumnDesc (name, comment, dmtype, dmgrp, ndim, option)); } } else if (valtype == "float") { if (shp.nelements() > 0) { tabdesc.addColumn (ArrayColumnDesc (name, comment, dmtype, dmgrp, shp, option)); }else{ tabdesc.addColumn (ArrayColumnDesc (name, comment, dmtype, dmgrp, ndim, option)); } } else if (valtype == "double") { if (shp.nelements() > 0) { tabdesc.addColumn (ArrayColumnDesc (name, comment, dmtype, dmgrp, shp, option)); }else{ tabdesc.addColumn (ArrayColumnDesc (name, comment, dmtype, dmgrp, ndim, option)); } } else if (valtype == "complex") { if (shp.nelements() > 0) { tabdesc.addColumn (ArrayColumnDesc (name, comment, dmtype, dmgrp, shp, option)); }else{ tabdesc.addColumn (ArrayColumnDesc (name, comment, dmtype, dmgrp, ndim, option)); } } else if (valtype == "dcomplex") { if (shp.nelements() > 0) { tabdesc.addColumn (ArrayColumnDesc (name, comment, dmtype, dmgrp, shp, option)); }else{ tabdesc.addColumn (ArrayColumnDesc (name, comment, dmtype, dmgrp, ndim, option)); } } else if (valtype == "string") { if (shp.nelements() > 0) { tabdesc.addColumn (ArrayColumnDesc (name, comment, dmtype, dmgrp, shp, option)); }else{ tabdesc.addColumn (ArrayColumnDesc (name, comment, dmtype, dmgrp, ndim, option)); } }else{ message = "Unknown data type " + valtype + " for array column " + name; return False; } return True; } String TableProxy::getTypeStr (DataType dtype) { switch (dtype) { case TpBool: return "boolean"; case TpUChar: return "uchar"; case TpShort: return "short"; case TpUShort: return "ushort"; case TpInt: return "int"; case TpUInt: return "uint"; case TpInt64: return "int64"; case TpFloat: return "float"; case TpDouble: return "double"; case TpComplex: return "complex"; case TpDComplex: return "dcomplex"; case TpString: return "string"; case TpRecord: return "record"; default: break; } return "int"; } Record TableProxy::recordColumnDesc (const ColumnDesc& cold, Bool cOrder) { Record cdesc; cdesc.define ("valueType", getTypeStr(cold.dataType())); cdesc.define ("dataManagerType", cold.dataManagerType()); cdesc.define ("dataManagerGroup", cold.dataManagerGroup()); cdesc.define ("option", Int(cold.options())); cdesc.define ("maxlen", Int(cold.maxLength())); cdesc.define ("comment", cold.comment()); if (cold.isArray()) { cdesc.define ("ndim", Int(cold.ndim())); IPosition shape = fillAxes (cold.shape(), cOrder); if (shape.nelements() > 0) { Vector vec(shape.nelements()); for (uInt i=0; i hcNames = tableDesc.hypercolumnNames(); for (uInt i=0; i dataNames; Vector coordNames; Vector idNames; Int ndim = tableDesc.hypercolumnDesc (hcNames(i), dataNames, coordNames, idNames); Record hrec; hrec.define ("HCndim", ndim); hrec.define ("HCdatanames", dataNames); hrec.define ("HCcoordnames", coordNames); hrec.define ("HCidnames", idNames); rec.defineRecord (hcNames(i), hrec); } return rec; } Int64 TableProxy::getRowsCheck (const String& columnName, Int64 row, Int64 nrow, Int64 incr, const String& caller) { // Synchronize table to get up-to-date #rows. // Check that the row number is within the table bounds. syncTable (table_p); return checkRowColumn (table_p, columnName, row, nrow, incr, caller); } Int64 TableProxy::getRowsSliceCheck (Slicer& slicer, const String& columnName, Int64 row, Int64 nrow, Int64 incr, const IPosition& blc, const IPosition& trc, const IPosition& inc, const String& caller) { IPosition cblc, ctrc; cblc = blc; ctrc = trc; setDefaultForSlicer (cblc); setDefaultForSlicer (ctrc); if (inc.nelements() > 0) { slicer = Slicer (cblc, ctrc, inc, Slicer::endIsLast); }else{ slicer = Slicer (cblc, ctrc, Slicer::endIsLast); } return getRowsCheck (columnName, row, nrow, incr, caller); } Int64 TableProxy::checkRowColumn (Table& table, const String& colName, Int64 rownr, Int64 nrow, Int64 incr, const String& caller) { // Check that the row number is within the table bounds. // However, accept a row number equal to nrow when no rows are needed. Int64 tabnrow = table.nrow(); if (rownr < 0 || rownr > tabnrow || (rownr==tabnrow && nrow>0)) { throw TableError ("TableProxy::" + caller + ": no such row"); } else if (incr <= 0) { throw TableError (String(caller) + ": rowincr<=0"); } else { if (!colName.empty() && !table.tableDesc().isColumn(colName)) { throw TableError ("TableProxy::" + caller + ": column " + colName + " does not exist"); } } Int64 maxnrow = (tabnrow - rownr + incr - 1) / incr; if (nrow < 0 || nrow > maxnrow) { nrow = maxnrow; } return nrow; } ValueHolder TableProxy::makeEmptyArray (DataType dtype) { IPosition shape(1,0); switch (dtype) { case TpBool: return ValueHolder(Array(shape)); case TpUChar: return ValueHolder(Array(shape)); case TpShort: return ValueHolder(Array(shape)); case TpUShort: return ValueHolder(Array(shape)); case TpInt: return ValueHolder(Array(shape)); case TpUInt: return ValueHolder(Array(shape)); case TpInt64: return ValueHolder(Array(shape)); case TpFloat: return ValueHolder(Array(shape)); case TpDouble: return ValueHolder(Array(shape)); case TpComplex: return ValueHolder(Array(shape)); case TpDComplex: return ValueHolder(Array(shape)); case TpString: return ValueHolder(Array(shape)); default: throw TableError ("TableProxy::getCell/Column: Unknown scalar type"); } } ValueHolder TableProxy::getValueFromTable (const String& colName, Int64 rownr, Int64 nrow, Int64 incr, Bool isCell) { // Exit immediately if no rows have to be done. const ColumnDesc& cdesc = table_p.tableDesc().columnDesc(colName); Bool isScalar = cdesc.isScalar(); DataType dtype = cdesc.dataType(); if (nrow == 0) { return makeEmptyArray (dtype); } if (isScalar) { switch (dtype) { case TpBool: { ScalarColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac(rownr)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr))); } } break; case TpUChar: { ScalarColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac(rownr)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr))); } } break; case TpShort: { ScalarColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac(rownr)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr))); } } break; case TpUShort: { ScalarColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac(rownr)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr))); } } break; case TpInt: { ScalarColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac(rownr)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr))); } } break; case TpUInt: { ScalarColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac(rownr)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr))); } } break; case TpInt64: { ScalarColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac(rownr)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr))); } } break; case TpFloat: { ScalarColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac(rownr)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr))); } } break; case TpDouble: { ScalarColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac(rownr)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr))); } } break; case TpComplex: { ScalarColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac(rownr)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr))); } } break; case TpDComplex: { ScalarColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac(rownr)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr))); } } break; case TpString: { ScalarColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac(rownr)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr))); } } break; case TpRecord: { ScalarColumn ac(table_p,colName); if (isCell) { // Transform a TableRecord into a Record. return ValueHolder (ac(rownr).toRecord()); } else { throw TableError ("TableProxy::getColumn not possible for a column" " containing records"); } } break; default: throw TableError ("TableProxy::getCell/Column: Unknown scalar type"); } } else { switch (dtype) { case TpBool: { ArrayColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac(rownr)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr))); } } break; case TpUChar: { ArrayColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac(rownr)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr))); } } break; case TpShort: { ArrayColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac(rownr)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr))); } } break; case TpUShort: { ArrayColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac(rownr)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr))); } } break; case TpInt: { ArrayColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac(rownr)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr))); } } break; case TpUInt: { ArrayColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac(rownr)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr))); } } break; case TpInt64: { ArrayColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac(rownr)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr))); } } break; case TpFloat: { ArrayColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac(rownr)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr))); } } break; case TpDouble: { ArrayColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac(rownr)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr))); } } break; case TpComplex: { ArrayColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac(rownr)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr))); } } break; case TpDComplex: { ArrayColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac(rownr)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr))); } } break; case TpString: { ArrayColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac(rownr)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr))); } } break; default: break; } } throw TableError ("TableProxy::getCell/Column: Unknown array type"); } void TableProxy::getValueFromTable (const String& colName, Int64 rownr, Int64 nrow, Int64 incr, Bool isCell, const ValueHolder& vh) { const ColumnDesc& cdesc = table_p.tableDesc().columnDesc(colName); Bool isScalar = cdesc.isScalar(); DataType dtype = cdesc.dataType(); if (isScalar && isCell) { throw TableError("A scalar value cannot be read into a python variable"); } if (nrow == 0) { return; } switch (vh.dataType()) { case TpArrayBool: { Array arr(vh.asArrayBool()); if (dtype != TpBool) { // Data has to be converted; cannot be read directly into the array. arr = getValueFromTable (colName, rownr, nrow, incr, isCell). asArrayBool(); } else if (isScalar) { // Read directly into the array (thus into Python ndarray object). ScalarColumn ac(table_p, colName); Vector vec(arr); ac.getColumnRange(Slice(rownr, nrow, incr), vec); } else { ArrayColumn ac(table_p, colName); if (isCell) { ac.get (rownr, arr); } else { ac.getColumnRange (Slice(rownr, nrow, incr), arr); } } } break; case TpArrayInt: { Array arr(vh.asArrayInt()); if (dtype != TpInt) { // Data has to be converted, so cannot be read directly into the array. arr = getValueFromTable (colName, rownr, nrow, incr, isCell). asArrayInt(); } else if (isScalar) { ScalarColumn ac(table_p, colName); Vector vec(arr); ac.getColumnRange(Slice(rownr, nrow, incr), vec); } else { ArrayColumn ac(table_p, colName); if (isCell) { ac.get (rownr, arr); } else { ac.getColumnRange (Slice(rownr, nrow, incr), arr); } } } break; case TpArrayFloat: { Array arr(vh.asArrayFloat()); if (dtype != TpFloat) { // Data has to be converted, so cannot be read directly into the array. arr = getValueFromTable (colName, rownr, nrow, incr, isCell). asArrayFloat(); } else if (isScalar) { ScalarColumn ac(table_p, colName); Vector vec(arr); ac.getColumnRange(Slice(rownr, nrow, incr), vec); } else { ArrayColumn ac(table_p, colName); if (isCell) { ac.get (rownr, arr); } else { ac.getColumnRange (Slice(rownr, nrow, incr), arr); } } } break; case TpArrayDouble: { Array arr(vh.asArrayDouble()); if (dtype != TpDouble) { // Data has to be converted, so cannot be read directly into the array. arr = getValueFromTable (colName, rownr, nrow, incr, isCell). asArrayDouble(); } else if (isScalar) { ScalarColumn ac(table_p, colName); Vector vec(arr); ac.getColumnRange(Slice(rownr, nrow, incr), vec); } else { ArrayColumn ac(table_p, colName); if (isCell) { ac.get (rownr, arr); } else { ac.getColumnRange (Slice(rownr, nrow, incr), arr); } } } break; case TpArrayComplex: { Array arr(vh.asArrayComplex()); if (dtype != TpComplex) { // Data has to be converted, so cannot be read directly into the array. arr = getValueFromTable (colName, rownr, nrow, incr, isCell). asArrayComplex(); } else if (isScalar) { ScalarColumn ac(table_p, colName); Vector vec(arr); ac.getColumnRange(Slice(rownr, nrow, incr), vec); } else { ArrayColumn ac(table_p, colName); if (isCell) { ac.get (rownr, arr); } else { ac.getColumnRange (Slice(rownr, nrow, incr), arr); } } } break; case TpArrayDComplex: { Array arr(vh.asArrayDComplex()); if (dtype != TpDComplex) { // Data has to be converted, so cannot be read directly into the array. arr = getValueFromTable (colName, rownr, nrow, incr, isCell). asArrayDComplex(); } else if (isScalar) { ScalarColumn ac(table_p, colName); Vector vec(arr); ac.getColumnRange(Slice(rownr, nrow, incr), vec); } else { ArrayColumn ac(table_p, colName); if (isCell) { ac.get (rownr, arr); } else { ac.getColumnRange (Slice(rownr, nrow, incr), arr); } } } break; default: throw TableError ("TableProxy::getCell/Column: Unknown data type " + ValType::getTypeStr(vh.dataType())); } } ValueHolder TableProxy::getValueSliceFromTable (const String& colName, const Slicer& slicer, Int64 rownr, Int64 nrow, Int64 incr, Bool isCell) { // Check that the column is an array. const ColumnDesc& cdesc = table_p.tableDesc().columnDesc(colName); if (! cdesc.isArray()) { throw TableError ("TableProxy::getColumnSlice: column " + String(colName) + " is not an array column"); } // Exit immediately if no rows have to be done. if (nrow == 0) { return makeEmptyArray (cdesc.dataType()); } switch(cdesc.dataType()) { case TpBool: { ArrayColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac.getSlice(rownr, slicer)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr), slicer)); } } break; case TpUChar: { ArrayColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac.getSlice(rownr, slicer)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr), slicer)); } } break; case TpShort: { ArrayColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac.getSlice(rownr, slicer)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr), slicer)); } } break; case TpUShort: { ArrayColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac.getSlice(rownr, slicer)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr), slicer)); } } break; case TpInt: { ArrayColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac.getSlice(rownr, slicer)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr), slicer)); } } break; case TpUInt: { ArrayColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac.getSlice(rownr, slicer)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr), slicer)); } } break; case TpInt64: { ArrayColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac.getSlice(rownr, slicer)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr), slicer)); } } case TpFloat: { ArrayColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac.getSlice(rownr, slicer)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr), slicer)); } } break; case TpDouble: { ArrayColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac.getSlice(rownr, slicer)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr), slicer)); } } break; case TpComplex: { ArrayColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac.getSlice(rownr, slicer)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr), slicer)); } } break; case TpDComplex: { ArrayColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac.getSlice(rownr, slicer)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr), slicer)); } } break; case TpString: { ArrayColumn ac(table_p,colName); if (isCell) { return ValueHolder (ac.getSlice(rownr, slicer)); }else{ return ValueHolder (ac.getColumnRange(Slice(rownr, nrow, incr), slicer)); } } break; default: break; } throw TableError ("TableProxy::getColumnSlice: Unknown array type"); } void TableProxy::getValueSliceFromTable (const String& colName, const Slicer& slicer, Int64 rownr, Int64 nrow, Int64 incr, Bool isCell, const ValueHolder& vh) { // Check that the column is an array. const ColumnDesc& cdesc = table_p.tableDesc().columnDesc(colName); if (! cdesc.isArray()) { throw TableError ("TableProxy::getColumnSlice: column " + String(colName) + " is not an array column"); } if (nrow == 0) { return; } DataType dtype = cdesc.dataType(); switch (vh.dataType()) { case TpArrayBool: { Array arr(vh.asArrayBool()); if (dtype != TpBool) { // Data has to be converted; cannot be read directly into the array. arr = getValueSliceFromTable (colName, slicer, rownr, nrow, incr, isCell).asArrayBool(); } else { ArrayColumn ac(table_p, colName); if (isCell) { ac.getSlice (rownr, slicer, arr); } else { ac.getColumnRange (Slice(rownr, nrow, incr), slicer, arr); } } } break; case TpArrayInt: { Array arr(vh.asArrayInt()); if (dtype != TpInt) { // Data has to be converted; cannot be read directly into the array. arr = getValueSliceFromTable (colName, slicer, rownr, nrow, incr, isCell).asArrayInt(); } else { ArrayColumn ac(table_p, colName); if (isCell) { ac.getSlice (rownr, slicer, arr); } else { ac.getColumnRange (Slice(rownr, nrow, incr), slicer, arr); } } } break; case TpArrayFloat: { Array arr(vh.asArrayFloat()); if (dtype != TpFloat) { // Data has to be converted; cannot be read directly into the array. arr = getValueSliceFromTable (colName, slicer, rownr, nrow, incr, isCell).asArrayFloat(); } else { ArrayColumn ac(table_p, colName); if (isCell) { ac.getSlice (rownr, slicer, arr); } else { ac.getColumnRange (Slice(rownr, nrow, incr), slicer, arr); } } } break; case TpArrayDouble: { Array arr(vh.asArrayDouble()); if (dtype != TpDouble) { // Data has to be converted; cannot be read directly into the array. arr = getValueSliceFromTable (colName, slicer, rownr, nrow, incr, isCell).asArrayDouble(); } else { ArrayColumn ac(table_p, colName); if (isCell) { ac.getSlice (rownr, slicer, arr); } else { ac.getColumnRange (Slice(rownr, nrow, incr), slicer, arr); } } } break; case TpArrayComplex: { Array arr(vh.asArrayComplex()); if (dtype != TpComplex) { // Data has to be converted; cannot be read directly into the array. arr = getValueSliceFromTable (colName, slicer, rownr, nrow, incr, isCell).asArrayComplex(); } else { ArrayColumn ac(table_p, colName); if (isCell) { ac.getSlice (rownr, slicer, arr); } else { ac.getColumnRange (Slice(rownr, nrow, incr), slicer, arr); } } } break; case TpArrayDComplex: { Array arr(vh.asArrayDComplex()); if (dtype != TpDComplex) { // Data has to be converted; cannot be read directly into the array. arr = getValueSliceFromTable (colName, slicer, rownr, nrow, incr, isCell).asArrayDComplex(); } else { ArrayColumn ac(table_p, colName); if (isCell) { ac.getSlice (rownr, slicer, arr); } else { ac.getColumnRange (Slice(rownr, nrow, incr), slicer, arr); } } } break; default: throw TableError ("TableProxy::getCell/Column: Unknown data type " + ValType::getTypeStr(vh.dataType())); } } void TableProxy::putValueInTable (const String& colName, Int64 rownr, Int64 nrow, Int64 incr, Bool isCell, const ValueHolder& value) { // Exit immediately if no rows have to be done. if (nrow == 0) { return; } Bool isScalar = table_p.tableDesc().columnDesc(colName).isScalar(); DataType type = table_p.tableDesc().columnDesc(colName).dataType(); if (isScalar) { switch (type) { case TpBool: { ScalarColumn col(table_p, colName); if (isCell) { col.put (rownr, value.asBool()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), value.asArrayBool()); } } break; case TpUChar: { ScalarColumn col(table_p, colName); if (isCell) { col.put (rownr, value.asuChar()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), value.asArrayuChar()); } } break; case TpShort: { ScalarColumn col(table_p, colName); if (isCell) { col.put (rownr, value.asShort()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), value.asArrayShort()); } } break; case TpUShort: { ScalarColumn col(table_p, colName); if (isCell) { col.put (rownr, value.asuShort()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), value.asArrayuShort()); } } break; case TpInt: { ScalarColumn col(table_p, colName); if (isCell) { col.put (rownr, value.asInt()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), value.asArrayInt()); } } break; case TpUInt: { ScalarColumn col(table_p, colName); if (isCell) { col.put (rownr, value.asuInt()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), value.asArrayuInt()); } } break; case TpInt64: { ScalarColumn col(table_p, colName); if (isCell) { col.put (rownr, value.asInt64()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), value.asArrayInt64()); } } break; case TpFloat: { ScalarColumn col(table_p, colName); if (isCell) { col.put (rownr, value.asFloat()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), value.asArrayFloat()); } } break; case TpDouble: { ScalarColumn col(table_p, colName); if (isCell) { col.put (rownr, value.asDouble()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), value.asArrayDouble()); } } break; case TpComplex: { ScalarColumn col(table_p, colName); if (isCell) { col.put (rownr, value.asComplex()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), value.asArrayComplex()); } } break; case TpDComplex: { ScalarColumn col(table_p, colName); if (isCell) { col.put (rownr, value.asDComplex()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), value.asArrayDComplex()); } } break; case TpString: { ScalarColumn col(table_p, colName); if (isCell) { col.put (rownr, value.asString()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), value.asArrayString()); } } break; case TpRecord: { ScalarColumn col(table_p, colName); if (isCell) { // Transform a Record into a TableRecord. TableRecord rec; rec.fromRecord (value.asRecord()); col.put (rownr, rec); } else { throw TableError ("TableProxy::putColumn not possible for a column" " containing records"); } } break; default: throw TableError ("TableProxy::put: unknown scalar data type"); } }else{ switch (type) { case TpBool: { ArrayColumn col(table_p, colName); if (isCell) { col.put (rownr, value.asArrayBool()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), value.asArrayBool()); } } break; case TpUChar: { ArrayColumn col(table_p, colName); if (isCell) { col.put (rownr, value.asArrayuChar()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), value.asArrayuChar()); } } break; case TpShort: { ArrayColumn col(table_p, colName); if (isCell) { col.put (rownr, value.asArrayShort()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), value.asArrayShort()); } } break; case TpUShort: { ArrayColumn col(table_p, colName); if (isCell) { col.put (rownr, value.asArrayuShort()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), value.asArrayuShort()); } } break; case TpInt: { ArrayColumn col(table_p, colName); if (isCell) { col.put (rownr, value.asArrayInt()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), value.asArrayInt()); } } break; case TpUInt: { ArrayColumn col(table_p, colName); if (isCell) { col.put (rownr, value.asArrayuInt()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), value.asArrayuInt()); } } break; case TpInt64: { ArrayColumn col(table_p, colName); if (isCell) { col.put (rownr, value.asArrayInt64()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), value.asArrayInt64()); } } break; case TpFloat: { ArrayColumn col(table_p, colName); if (isCell) { col.put (rownr, value.asArrayFloat()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), value.asArrayFloat()); } } break; case TpDouble: { ArrayColumn col(table_p, colName); if (isCell) { col.put (rownr, value.asArrayDouble()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), value.asArrayDouble()); } } break; case TpComplex: { ArrayColumn col(table_p, colName); if (isCell) { col.put (rownr, value.asArrayComplex()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), value.asArrayComplex()); } } break; case TpDComplex: { ArrayColumn col(table_p, colName); if (isCell) { col.put (rownr, value.asArrayDComplex()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), value.asArrayDComplex()); } } break; case TpString: { ArrayColumn col(table_p, colName); if (isCell) { col.put (rownr, value.asArrayString()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), value.asArrayString()); } } break; default: throw TableError("TableProxy::put: unknown array data type"); } } } void TableProxy::putValueSliceInTable (const String& colName, const Slicer& slicer, Int64 rownr, Int64 nrow, Int64 incr, Bool isCell, const ValueHolder& value) { // Exit immediately if no rows have to be done. if (nrow == 0) { return; } switch (table_p.tableDesc().columnDesc(colName).dataType()) { case TpBool: { ArrayColumn col(table_p, colName); if (isCell) { col.putSlice (rownr, slicer, value.asArrayBool()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), slicer, value.asArrayBool()); } } break; case TpUChar: { ArrayColumn col(table_p, colName); if (isCell) { col.putSlice (rownr, slicer, value.asArrayuChar()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), slicer, value.asArrayuChar()); } } break; case TpShort: { ArrayColumn col(table_p, colName); if (isCell) { col.putSlice (rownr, slicer, value.asArrayShort()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), slicer, value.asArrayShort()); } } break; case TpUShort: { ArrayColumn col(table_p, colName); if (isCell) { col.putSlice (rownr, slicer, value.asArrayuShort()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), slicer, value.asArrayuShort()); } } break; case TpInt: { ArrayColumn col(table_p, colName); if (isCell) { col.putSlice (rownr, slicer, value.asArrayInt()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), slicer, value.asArrayInt()); } } break; case TpUInt: { ArrayColumn col(table_p, colName); if (isCell) { col.putSlice (rownr, slicer, value.asArrayuInt()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), slicer, value.asArrayuInt()); } } break; case TpInt64: { ArrayColumn col(table_p, colName); if (isCell) { col.putSlice (rownr, slicer, value.asArrayInt64()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), slicer, value.asArrayInt64()); } } break; case TpFloat: { ArrayColumn col(table_p, colName); if (isCell) { col.putSlice (rownr, slicer, value.asArrayFloat()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), slicer, value.asArrayFloat()); } } break; case TpDouble: { ArrayColumn col(table_p, colName); if (isCell) { col.putSlice (rownr, slicer, value.asArrayDouble()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), slicer, value.asArrayDouble()); } } break; case TpComplex: { ArrayColumn col(table_p, colName); if (isCell) { col.putSlice (rownr, slicer, value.asArrayComplex()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), slicer, value.asArrayComplex()); } } break; case TpDComplex: { ArrayColumn col(table_p, colName); if (isCell) { col.putSlice (rownr, slicer, value.asArrayDComplex()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), slicer, value.asArrayDComplex()); } } break; case TpString: { ArrayColumn col(table_p, colName); if (isCell) { col.putSlice (rownr, slicer, value.asArrayString()); }else{ col.putColumnRange (Slice(rownr, nrow, incr), slicer, value.asArrayString()); } } break; default: throw TableError ("TableProxy::putColumnSlice: unknown array data type"); } } void TableProxy::findKeyId (RecordFieldId& fieldid, const TableRecord*& keySet, const String& keyname, const String& column) { TableRecord* ksPtr = const_cast(keySet); findKeyId (fieldid, ksPtr, keyname, column, True, False, False); keySet = ksPtr; } void TableProxy::findKeyId (RecordFieldId& fieldid, TableRecord*& keySet, const String& keyname, const String& column, Bool mustExist, Bool change, Bool makeSubRecord) { if (keyname.empty()) { throw TableError ("Empty keyword name given"); } // A keyword name can consist of multiple names for a keyword hierarchy. Vector keys = stringToVector (keyname, '.'); String usedName; for (uInt i=0; iisDefined (keys(i))) { if (mustExist) { if (column.empty()) { throw TableError ("Table keyword " + usedName + " does not exist"); } else { throw TableError ("Keyword " + usedName + " in column " + column + " does not exist"); } } else { if (makeSubRecord && i+1 < keys.nelements()) { keySet->defineRecord (keys(i), TableRecord()); } } } fieldid = RecordFieldId(keys(i)); if (i+1 < keys.nelements()) { if (keySet->dataType (fieldid) != TpRecord) { if (column.empty()) { throw TableError ("Table keyword " + usedName + " does not have a record-value"); } else { throw TableError ("Keyword " + usedName + " in column " + column + " does not have a record-value"); } } if (change) { keySet = &(keySet->rwSubRecord (fieldid)); } else { keySet = (TableRecord*)(&(keySet->subRecord (fieldid))); } } usedName += '.'; } } void TableProxy::syncTable (Table& table) { if (table.lockOptions().readLocking()) { table.lock (FileLocker::Read); } } void TableProxy::setDefaultForSlicer (IPosition& vec) const { for (uInt i=0; i 1) { for (Int i=0; i #include #include #include #include //# Forward Declarations namespace casacore { //# NAMESPACE CASACORE - BEGIN class ValueHolder; class RecordFieldId; class Table; class TableLock; class ColumnDesc; class TableExprNode; class Slicer; // // High-level interface to tables // // // // // //# Classes you should understand before using this one. //
      • class Table //
      • python script table.py // // // TableProxy is a proxy for access to tables from any script. // // // TableProxy gives access to most of the functionality in the Table System. // It is primarily meant to be used in classes that wrap access to it // from scripting languages (like Glish and Python). // However, it can also be used directly from other C++ code. // // It has functions to open, create, read, write, and query tables. // Accompying proxy classes give access to other functionality. They are: //
          //
        • TableIterProxy for iteration // through a table using class // TableIterator. //
        • TableRowProxy for access to // table rows using class TableRow. //
        • TableIterProxy for faster // indexed access to using classes // ColumnsIndex and // ColumnsIndexArray. //
        // // TableProxy does not have the TableRecord type in its interface, because // such a type cannot be handled by e.g. Glish or Python. Instead it // converts TableRecords to/from Records. If a TableRecord contains a field // with a Table object, it is represented in the Record as a string // with the value "Table: NAME" where NAME is the table name. //
        // // TableProxy is the Tasking-independent high-level table interface. // Different front-ends (e.g. GlishTableProxy) can be put on top of it. // class TableProxy { public: // Default constructor initializes to not open. // This constructor is only needed for containers. TableProxy(); // Create the object from an existing table (used by some methods). TableProxy (const Table& table) : table_p (table) {} // Open the table with a given name. TableProxy (const String& tableName, const Record& lockOptions, int option); // Create a table with given name and description, etc. TableProxy (const String& tableName, const Record& lockOptions, const String& endianFormat, const String& memType, Int64 nrow, const Record& tableDesc, const Record& dmInfo); // Create a table object to concatenate a number of similar tables. // The keyword set of the first table is take as the keyword set of the // entire concatenation. However, it can be specified which subtables // have to be concatenated as well which means that for each subtable name // the subtable in the keywordsets are concatenated. // For Boost-Python the constructors must have different nr of arguments. // Hence some dummy arguments are added. // // TableProxy (const Vector& tableNames, const Vector& concatenateSubTableNames, const Record& lockOptions, int option); TableProxy (const std::vector& tables, const Vector& concatenateSubTableNames, int dummy1=0, int dummy2=0, int dummy3=0); // // Create a table object from a table command (as defined in TableGram). //
        If a CALC command was given, the resulting values are stored in // the a record and a null TableProxy object is returned. // The result can be obtained using getCalcResult. // // If the command string contains no GIVING part, the resulting // table is temporary and its name is blank. // TableProxy (const String& command, const std::vector& tables); // Create a table from an Ascii file. // It fills a string containing the names and types // of the columns (in the form COL1=R, COL2=D, ...). // The string can be obtained using getAsciiFormat. TableProxy (const String& fileName, const String& headerName, const String& tableName, Bool autoHeader, const IPosition& autoShape, const String& separator, const String& commentMarker, Int64 firstLine, Int64 lastLine, const Vector& columnNames = Vector(), const Vector& dataTypes = Vector()); // Copy constructor. TableProxy (const TableProxy&); // Close the table. ~TableProxy(); // Assignment. TableProxy& operator= (const TableProxy&); // Select the given rows from the table and create a new (reference) table. // If outName is not empty, the new table is made persistent with that name. TableProxy selectRows (const Vector& rownrs, const String& outName); // Reopen the table for read/write. void reopenRW(); // Resync the table. void resync(); // Flush the table and optionally all its subtables. void flush (Bool recursive); // Flush and close the table and all its subtables. void close(); // Get the endian format of the table. // It fills the result with value "big" or "little". String endianFormat() const; // Acquire a (read or write) lock on the table. void lock (Bool mode, Int nattempts); // Release a lock on the table. void unlock(); // Determine if data in the table has changed. Bool hasDataChanged(); // Determine if the process has a read or write lock on the table. Bool hasLock (Bool mode); // Get the lock options of the table. // It fills the record with the fields option, interval and maxwait. Record lockOptions(); // Determine if the table (and optionally its subtables) are in use // in another process. Bool isMultiUsed (Bool checkSubTables); // Write the table to an ASCII file // (approximately the inverse of the from-ASCII-contructor). // If headerFile is empty or equal to asciiFile, the // headers are written in the same file as the data, otherwise in a separate // file. // If no columns are given (or if the first column name is empty), all // table columns are written. Columns containing records are also printed // (enclosed in {}), but a warning message is returned. //
        Argument sep is used as separator between columns and // array values. If it is empty, a blank is used. //
        For each column the precision can be given. It is only used for // columns containing floating point numbers. A value <=0 means using the // default which is 9 for single and 18 for double precision. //
        If useBrackets=True, arrays are enclosed in [] (for each // dimension), so variable shaped arrays can be read back unambiguously. // The type in the header will be something like D[4,64]. If the column is // variable shaped, the type is like D[]. // If useBracket=False, arrays are written linearly where a // shape [4,64] is given in the header like D4,64. If the column is variable // shaped, the shape of the first cell is used and a warning message is // returned. String toAscii (const String& asciiFile, const String& headerFile, const Vector& columns, const String& sep, const Vector& precision, Bool useBrackets); // Rename the table void rename (const String& newTableName); // Copy the table (possibly a deep copy). // If noRows=True, an empty table is created. TableProxy copy (const String& newTableName, Bool toMemoryTable, Bool deepCopy, Bool valueCopy, const String& endianFormat, const Record& dminfo, Bool noRows); // Copy rows from one table to another. // If startOut<0, it is set to the end of the output table. void copyRows (TableProxy& out, Int64 startIn, Int64 startOut, Int64 nrow); // Close and delete the table. void deleteTable (Bool checkSubTables); // Get the table info of the table. Record tableInfo(); // Put the table info of the table. void putTableInfo (const Record& value); // Add a line to the TableInfo readme. void addReadmeLine (const String& line); // Test if a table is readable. Bool isReadable() const; // Test if a table is writable. Bool isWritable() const; // Set the maximum cache size for the given column in the table. void setMaximumCacheSize (const String& columnName, Int nbytes); // Add one or more columns to the table. void addColumns (const Record& tableDesc, const Record& dminfo, Bool addToParent); // Rename a column in the table. void renameColumn (const String& nameOld, const String& nameNew); // Remove one or more columns from the table. void removeColumns (const Vector& columnNames); // Add rows to the table. void addRow (Int64 nrow); // Remove rows from the table. void removeRow (const Vector& rownrs); // Get some or all values from a column in the table. // row is the starting row number (0-relative). // nrow=-1 means until the end of the table. // incr is the step in row number. // ValueHolder getColumn (const String& columnName, Int64 row, Int64 nrow, Int64 incr); void getColumnVH (const String& columnName, Int64 row, Int64 nrow, Int64 incr, const ValueHolder& vh); Record getVarColumn (const String& columnName, Int64 row, Int64 nrow, Int64 incr); // // Get some or all value slices from a column in the table. // If the inc vector is empty, it defaults to all 1. // ValueHolder getColumnSlice (const String& columnName, Int64 row, Int64 nrow, Int64 incr, const Vector& blc, const Vector& trc, const Vector& inc); ValueHolder getColumnSliceIP (const String& columnName, const IPosition& blc, const IPosition& trc, const IPosition& inc, Int64 row, Int64 nrow, Int64 incr); void getColumnSliceVH (const String& columnName, Int64 row, Int64 nrow, Int64 incr, const Vector& blc, const Vector& trc, const Vector& inc, const ValueHolder& vh); void getColumnSliceVHIP (const String& columnName, const IPosition& blc, const IPosition& trc, const IPosition& inc, Int64 row, Int64 nrow, Int64 incr, const ValueHolder& vh); // // Put some or all values into a column in the table. // row is the starting row number (0-relative). // nrow=-1 means until the end of the table. // incr is the step in row number. // void putColumn (const String& columnName, Int64 row, Int64 nrow, Int64 incr, const ValueHolder&); void putVarColumn (const String& columnName, Int64 row, Int64 nrow, Int64 incr, const Record& values); // // Put some or all value slices into a column in the table. // void putColumnSlice (const String& columnName, Int64 row, Int64 nrow, Int64 incr, const Vector& blc, const Vector& trc, const Vector& inc, const ValueHolder&); void putColumnSliceIP (const String& columnName, const ValueHolder&, const IPosition& blc, const IPosition& trc, const IPosition& inc, Int64 row, Int64 nrow, Int64 incr); // // Tests if the contents of a cell are defined. // Only a column with variable shaped arrays can have an empty cell. Bool cellContentsDefined (const String& columnName, Int64 rownr); // Get a value from a column in the table. ValueHolder getCell (const String& columnName, Int64 row); void getCellVH (const String& columnName, Int64 row, const ValueHolder& vh); // Get a value slice from a column in the table. // If the inc vector is empty, it defaults to all 1. // ValueHolder getCellSlice (const String& columnName, Int64 row, const Vector& blc, const Vector& trc, const Vector& inc); ValueHolder getCellSliceIP (const String& columnName, Int64 row, const IPosition& blc, const IPosition& trc, const IPosition& inc); void getCellSliceVH (const String& columnName, Int64 row, const Vector& blc, const Vector& trc, const Vector& inc, const ValueHolder& vh); void getCellSliceVHIP (const String& columnName, Int64 row, const IPosition& blc, const IPosition& trc, const IPosition& inc, const ValueHolder& vh); // // Put a value into a column in the table. void putCell (const String& columnName, const Vector& rownrs, const ValueHolder&); // Put a value slice into a column in the table. // If the inc vector is empty, it defaults to all 1. // void putCellSlice (const String& columnName, Int64 row, const Vector& blc, const Vector& trc, const Vector& inc, const ValueHolder&); void putCellSliceIP (const String& columnName, Int64 row, const ValueHolder&, const IPosition& blc, const IPosition& trc, const IPosition& inc); // // Get the shape of one or more cells in a column as a vector of Strings // containing the shapes as [a,b,c]. // If the shape is fixed, a single String is returned. Vector getColumnShapeString (const String& columnName, Int64 rownr, Int64 nrow, Int64 incr, Bool cOrder = False); // Get a table or column keyword value in the table. // If the columnName is empty, a given keyword is a table keyword. // The keyword can be given as a name or a 0-based index. ValueHolder getKeyword (const String& columnName, const String& keywordName, Int keywordIndex); // Get the table or column keyword values in the table. // If the columnName is empty, the table keyword values are returned. Record getKeywordSet (const String& columnName); // Define a table or column keyword in the table. // If the column name is empty, a table keyword is defined. // The keyword can be given as a name or a 0-based number. // The value should be a record containing the value of the keyword. // The value can be any type (including a record). void putKeyword (const String& columnName, const String& keywordName, Int keywordIndex, Bool makeSubRecord, const ValueHolder&); // Define multiple table or column keywords in the table. // If the column name is empty, a table keywords are defined. // The value should be a record containing the values of the keywords. // The values can be any type (including a record). // The field names are the keyword names. void putKeywordSet (const String& columnName, const Record& valueSet); // Remove a table or column keyword from the table. // If the column name is empty, a table keyword is removed. void removeKeyword (const String& columnName, const String& keywordName, Int keywordIndex); // Get the names of all field in a record in the table. // If the column name is empty, the table keywords are used. // If the keyword name is empty, the names of all keywords are returned. // Otherwise the names of all fields in the keyword value are returned. // In that case the value has to be a record. Vector getFieldNames (const String& columnName, const String& keywordName, Int keywordIndex); // Get table name. String tableName(); // Get the names of the parts the table consists of (e.g. for a ConcatTable). Vector getPartNames (Bool recursive); // Get #columns of the table. Int ncolumns(); // Get #rows of the table. Int64 nrows(); // Get the shape (#columns, #rows) of the table. Vector shape(); // Get the row numbers of the table. Vector rowNumbers (TableProxy& other); // Get all column names in the table. Vector columnNames(); // Return in result if the column contains scalars. Bool isScalarColumn (const String& columnName); // Return the data type of the column as: // Bool, UChar, Short, UShort, Int, UInt, Int64, // Float, Double, Complex, DComplex, String, Table, or unknown. String columnDataType (const String& columnName); // Return the type of array in the column as: // Direct // Undefined // FixedShape // Direct,Undefined // Direct,FixedShape // Undefined,FixedShape // Direct,Undefined,FixedShape // or Error -- unexpected column type String columnArrayType (const String& columnName); // Get the data manager info of the table. Record getDataManagerInfo(); // Get the properties of a data manager given by column or data manager name. Record getProperties (const String& name, Bool byColumn); // Set the properties of a data manager given by column or data manager name. void setProperties (const String& name, const Record& properties, Bool byColumn); // Get the table description of the table. // It returns a record containing the description. Record getTableDescription (Bool actual, //# use actual description? Bool cOrder=False); // Create a Record table description from a TableDesc object static Record getTableDesc(const TableDesc & tabdesc, Bool cOrder=False); // Get the column description of a column in the table. // It returns a record containing the description. Record getColumnDescription (const String& columnName, Bool actual, //# use actual description? Bool cOrder=False); // Get ascii format string. String getAsciiFormat() const; // Get result of possible CALC statement. Record getCalcResult() const; // Show the structure of a table. String showStructure (Bool showDataMan=True, Bool showColumns=True, Bool showSubTables=False, Bool sortColumns=False) const; // Return the table object. // Table& table() { return table_p; } const Table& table() const { return table_p; } // // Get or put the values of all keywords. // Thus convert from TableRecord to/from Record. // Keywords containing a table are converted to a string containing // the table name preceeded by 'Table: '. // static Record getKeyValues (const TableRecord& keySet); static void putKeyValues (TableRecord& keySet, const Record& valueSet); // // Get the lock options from the fields in the record. // If the record or lockoption is invalid, an exception is thrown. static TableLock makeLockOptions (const Record& options); // Turn the string into the endian format option. // An exception is thrown if the string is invalid. static Table::EndianFormat makeEndianFormat (const String& endianFormat); // Make hypercolumn definitions for the given hypercolumns. static Bool makeHC (const Record& gdesc, TableDesc& tabdesc, String& message); // Get the value of a keyword. static ValueHolder getKeyValue (const TableRecord& keySet, const RecordFieldId& fieldId); // Put the value of a keyword. static void putKeyValue (TableRecord& keySet, const RecordFieldId& fieldId, const ValueHolder& value); // Make a real table description from a table description in a record. // An exception is thrown if the record table description is invalid. // A record table description is a Record object as returned by // getDesc. static Bool makeTableDesc (const Record& gdesc, TableDesc& tabdesc, String& message); // Add an array column description to the table description. // It is used by the function makeDesc. static Bool addArrayColumnDesc (TableDesc& tableDesc, const String& valueType, const String& columnName, const String& comment, const String& dataManagerType, const String& dataManagerGroup, int options, Int ndim, const Vector& shape, Bool cOrder, String& message); // Make a record containing the column description. static Record recordColumnDesc (const ColumnDesc&, Bool cOrder); // Make a record containing the description of all hypercolumns. static Record recordHCDesc (const TableDesc& tableDesc); // Calculate the values of a CALC expression and store them in field // 'values' in rec. static void calcValues (Record& rec, const TableExprNode& expr); // Get the type string as used externally (in e.g. glish). static String getTypeStr (DataType); // Optionally reverse the axes. static IPosition fillAxes (const IPosition&, Bool cOrder); // Check if the new shape is still the same. //
        same: 0=first time; 1=still the same; 2=different static void stillSameShape (Int& same, IPosition& shape, const IPosition& newShape); // Copy the array contents of the record fields to a single array. // This can only be done if the shape is constant. template static Array record2Array (const Record& rec) { if (rec.empty()) { return Array(); } Array tmp; rec.get (0, tmp); IPosition shp(tmp.shape()); shp.append (IPosition(1, rec.size())); Array arr(shp); ArrayIterator iter(arr, tmp.ndim()); for (uInt i=0; i void printValueHolder (const ValueHolder& vh, ostream& os, const String& sep, Int prec, Bool useBrackets) const; template void printArray (const Array& arr, ostream& os, const String& sep) const; void printArrayValue (ostream& os, Bool v, const String&) const {os << v;} void printArrayValue (ostream& os, Int v, const String&) const {os << v;} void printArrayValue (ostream& os, Int64 v, const String&) const {os << v;} void printArrayValue (ostream& os, Double v, const String&) const {os << v;} void printArrayValue (ostream& os, const DComplex& v, const String&) const {os << v;} void printArrayValue (ostream& os, const String& v, const String&) const {os << '"' << v << '"';} // // Sync table to get correct nr of rows and check the row number. // It returns the nr of table rows. Int64 getRowsCheck (const String& columnName, Int64 row, Int64 nrow, Int64 incr, const String& caller); // Sync table to get correct nr of rows and check the row number. // Fill the slicer with the possibly expanded blc,trc,inc. // It returns the nr of table rows. Int64 getRowsSliceCheck (Slicer& slicer, const String& columnName, Int64 row, Int64 nrow, Int64 incr, const IPosition& blc, const IPosition& trc, const IPosition& inc, const String& caller); // Check if the column name and row numbers are valid. // Return the recalculated nrow so that it does not exceed #rows. Int64 checkRowColumn (Table& table, const String& colName, Int64 rownr, Int64 nrow, Int64 incr, const String& caller); // Make an empty array (with 1 axis) of the correct datatype. ValueHolder makeEmptyArray (DataType dtype); // Get values from the column. // Nrow<0 means till the end of the column. ValueHolder getValueFromTable (const String& colName, Int64 rownr, Int64 nrow, Int64 incr, Bool isCell); void getValueFromTable (const String& colName, Int64 rownr, Int64 nrow, Int64 incr, Bool isCell, const ValueHolder& vh); // Get value slices from the column. // Nrow<0 means till the end of the column. ValueHolder getValueSliceFromTable(const String& colName, const Slicer& slicer, Int64 rownr, Int64 nrow, Int64 incr, Bool isCell); void getValueSliceFromTable(const String& colName, const Slicer& slicer, Int64 rownr, Int64 nrow, Int64 incr, Bool isCell, const ValueHolder& vh); // Put values into the column. // Nrow<0 means till the end of the column. void putValueInTable (const String& colName, Int64 rownr, Int64 nrow, Int64 incr, Bool isCell, const ValueHolder&); // Put value slices into the column. // Nrow<0 means till the end of the column. void putValueSliceInTable (const String& colName, const Slicer& slicer, Int64 rownr, Int64 nrow, Int64 incr, Bool isCell, const ValueHolder&); // Split the keyname into its separate parts (separator is .). // Check if each part exists and is a subrecord (except last part). // When putting, subrecords are created if undefined and if // makeSubRecord is set. // On return it fills in the fieldid with the latest keyword part. // KeySet is set to the last subrecord. // void findKeyId (RecordFieldId& fieldid, const TableRecord*& keySet, const String& keyname, const String& column); void findKeyId (RecordFieldId& fieldid, TableRecord*& keySet, const String& keyname, const String& column, Bool mustExist, Bool change, Bool makeSubRecord); // // Replace the user-given default value (<0) by the default value // used by Slicer (i.e. by Slicer::MimicSource). void setDefaultForSlicer (IPosition& vec) const; // Synchronize table if readlocking is in effect. // In this way the number of rows is up-to-date. void syncTable (Table& table); //# The data members. Table table_p; String asciiFormat_p; Record calcResult_p; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/TableRecord.cc000066400000000000000000000340131476623553700205060ustar00rootroot00000000000000//# TableRecord.cc: A hierarchical collection of named fields of various types //# Copyright (C) 1995,1996,1997,1998,1999,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableRecord::TableRecord() : RecordInterface (), rep_p (new TableRecordRep), parent_p (0) {} TableRecord::TableRecord (RecordType type, CheckFieldFunction* func, const void* checkArgument) : RecordInterface (type, func, checkArgument), rep_p (new TableRecordRep), parent_p (0) {} TableRecord::TableRecord (const RecordDesc& description, RecordType type, CheckFieldFunction* func, const void* checkArgument) : RecordInterface (type, func, checkArgument), rep_p (new TableRecordRep (description)), parent_p (0) {} // When description is empty, TableRecord is not fixed. TableRecord::TableRecord (TableRecordRep* parent, const RecordDesc& description) : RecordInterface (description.nfields()==0 ? Variable : Fixed, 0, 0), rep_p (new TableRecordRep (description)), parent_p (parent) {} TableRecord::TableRecord (TableRecordRep* parent, RecordType type) : RecordInterface (type, 0, 0), rep_p (new TableRecordRep), parent_p (parent) {} TableRecord::TableRecord (const TableRecord& other) : RecordInterface (other), rep_p (other.rep_p), parent_p (other.parent_p) {} TableRecord::TableRecord (const RecordInterface& other) : RecordInterface (other), parent_p (0) { // If the RecordInterface is a TableRecord, assign it immediately. const TableRecord* trecp = dynamic_cast(&other); if (trecp != 0) { rep_p = trecp->rep_p; } else { rep_p.set (new TableRecordRep (other.description())); uInt n = other.nfields(); const RecordDesc& desc = description(); for (uInt i=0; icopyDataField (dtype, i, other.get_pointer (i, dtype)); } } } } TableRecord& TableRecord::operator= (const TableRecord& other) { // Assignment is only possible when the Record is empty or // when their layout match or when the Record is non-fixed. // When non-fixed or empty, we simply replace the representation. // Otherwise we replace all values (in which case we do not need // to replace the RecordFieldPtr pointers). if (this != &other) { if (! isFixed() || nfields() == 0) { rep_p = other.rep_p; }else{ AlwaysAssert (conform (other), AipsError); rwRef().copyData (other.ref()); } } return *this; } TableRecord::~TableRecord() {} RecordInterface* TableRecord::clone() const { return new TableRecord (*this); } void TableRecord::assign (const RecordInterface& that) { // We want the subrecords to be variable if the main record // is variable and empty. // Operator= does not always preserve the type of subrecords, // so we do a hack by setting the type explicitly. Bool var = (nfields() == 0 && !isFixed()); *this = TableRecord (that); if (var) { setRecordType (Variable); } } void TableRecord::print (ostream& os, Int maxNrValues, const String& indent) const { rep_p.ref().print (os, maxNrValues, indent); } void TableRecord::makeUnique() { rwRef(); } TableRecordRep& TableRecord::rwRef() { TableRecordRep& newRep = rep_p.rwRef(); return newRep; } const String& TableRecord::comment (const RecordFieldId& id) const { Int whichField = idToNumber (id); return ref().comment (whichField); } void TableRecord::setComment (const RecordFieldId& id, const String& comment) { Int whichField = idToNumber (id); rwRef().setComment (whichField, comment); } RecordDesc TableRecord::getDescription() const { return ref().description(); } void TableRecord::restructure (const RecordDesc& newDescription, Bool recursive) { // Restructure is not possible for fixed records. throwIfFixed(); rwRef().restructure (newDescription, recursive); } void TableRecord::setRecordType (RecordType rtype) { recordType() = rtype; // Iterate through all fields to make the subrecords the required type. uInt nf = nfields(); for (uInt i=0; itable(); } Table TableRecord::asTable (const RecordFieldId& id, const TableLock& lockOptions) const { Int whichField = idToNumber (id); const Table& tab = ((const TableKeyword*)get_pointer (whichField, TpTable))->table(&lockOptions); /* String name = tab.tableName(); int option = tab.tableOption(); if (option == Table::New || option == Table::NewNoReplace) { option = Table::Update; } // Close the table in the record, otherwise the new lock options // may have no effect. // Only do this for a plain table. if (tab.tableType() == Table::Plain) { closeTable (id); return Table (name, lockOptions, Table::TableOption(option)); } */ return tab; } const TableAttr& TableRecord::tableAttributes (const RecordFieldId& id) const { Int whichField = idToNumber (id); return ((const TableKeyword*)get_pointer (whichField, TpTable))-> tableAttributes(); } void TableRecord::closeTable (const RecordFieldId& id) const { Int whichField = idToNumber (id); ref().closeTable (whichField); } void TableRecord::mergeField (const TableRecord& other, const RecordFieldId& id, DuplicatesFlag flag) { throwIfFixed(); Int whichField = other.idToNumber (id); rwRef().mergeField (other.ref(), whichField, flag); } void TableRecord::merge (const TableRecord& other, DuplicatesFlag flag) { AlwaysAssert (this != &other, AipsError); throwIfFixed(); rwRef().merge (other.ref(), flag); } AipsIO& operator<< (AipsIO& os, const TableRecord& rec) { rec.putRecord (os, TableAttr()); return os; } AipsIO& operator>> (AipsIO& os, TableRecord& rec) { rec.getRecord (os, TableAttr()); return os; } void TableRecord::putRecord (AipsIO& os, const TableAttr& parentAttr) const { ref().putRecord (os, recordType(), parentAttr); } void TableRecord::getRecord (AipsIO& os, const TableAttr& parentAttr) { // Get is only possible when the Record is empty or when // the Record is non-fixed. AlwaysAssert ((! isFixed() || nfields() == 0), AipsError); // Reading the record type back means casting it from an int // to the correct type. Int type; rwRef().getRecord (os, type, parentAttr); recordType() = (RecordInterface::RecordType)type; } void TableRecord::setTableAttr (const TableRecord& other, const TableAttr& defaultAttr) { uInt n = nfields(); const RecordDesc& desc = description(); for (uInt i=0; i #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class IPosition; class AipsIO; class TableLock; // // A hierarchical collection of named fields of various types // // // // // //
      • RecordDesc. //
      • RecordFieldPtr. // // // // TableRecord is a Record to be used in the Table system. // // // // Class RecordInterface describes // the fundamental properties of records. //
        // The TableRecord class is a particular type of a record class. //
        The TableRecord class structure is shown in this // UML diagram. //
        // The fields in TableRecord may be of scalar type, array type, a Table // or a TableRecord. // The types are chosen to be compatible with the native // types of the Table system, viz: Bool, uChar, Short, Int, uInt, Int64, Float, // Double, Complex, DComplex, String. // Arrays of all these types are also available. // Note that a TableRecord is not a space-efficient way of storing // small objects. //

        // The structure of a TableRecord is defined by the // RecordDesc class. // The structure of the TableRecord can be defined at // construction time. It can thereafter be restructured. This has the // effect, however, that any existing RecordFieldPtr objects become // invalid. //
        // It is possible to add or remove fields once a TableRecord is constructed. // However, this is not possible when the TableRecord is constructed with a // fixed structure (i.e. with the fixedStructure flag set). //

        // A TableRecord is an hierarchical structure, because it can have fields // containing TableRecord's (as layed out in the RecordDesc). A subrecord // has a variable structure, when its RecordDesc is empty (i.e. contains // no fields). It is fixed when its RecordDesc contains fields. //

        // A TableRecord may be assigned to another only if they conform; that is if // their fields have the identical type in the identical order. // The field names do not need to be identical however, only the types. // That is, the structure needs to be identical, but // not the labels. Note that field order is significant, // [ifield(type=Int),ffield(type=Float)] // is not the same as [ffield(type=Float),ifield(type=Int)] //
        // Conformance is checked recursively for fixed subrecords. That is, a // variable structured subrecord is not checked, because any record // can be assigned to it. A fixed structured subrecord has to // conform the corresponding subrecord in the source. //
        A Table field is conforming when the name of the table // description of the source table matches the table description name // defined in the RecordDesc field. When that name is blank, every // table matches. In fact, defining a table description name is identical // to defining an array shape.. //

        // When a TableRecord is read back, possible Tables contained in fields // are only opended and read back when they are accessed for the first time. // In that way no needless table opens are done. // When a table has been opened, it is possible to close it. This // can be useful to save memory usage. //

        // TableRecord uses copy-on-write semantics. This means that when a // TableRecord is copied, only the pointer to the underlying // TableRecordRep object is copied. // Only when the TableRecord gets changed (i.e. when a non-const // TableRecord member function is called), the TableRecordRep object is copied. // This results in a cheap copy behaviour. // // // // // { // TableDesc td ("td", TableDesc::Scratch); // td.addColumn (ScalarColumnDesc ("col1")); // td.addColumn (ScalarColumnDesc ("col2")); // SetupNewTable newtab ("tTableRecord_tmp.tab1", td1, Table::New); // Table tab (newtab, 10); // RecordDesc rd; // rd.addTable ("tab1", "td"); // with description name // rd.addField ("tab2", TpTable); // without description name // TableRecord rec (rd, RecordInterface::Variable); // // Both define's are possible. // // The first one because the table description name matches. // // The second one because that field has no table description name, // // thus every table description matches. // rec.defineTable (rec.fieldNumber("tab1"), tab1); // rec.defineTable (rec.fieldNumber("tab2"), tab1); // Table t1 = rec.asTable ("tab1"); // AlwaysAssertExit (t1.nrow() == 10 && t1.tableDesc().ncolumn() == 2); // Table t2 = rec.asTable ("tab2"); // AlwaysAssertExit (t2.nrow() == 10 && t2.tableDesc().ncolumn() == 2); // AipsIO aos ("file.name", ByteIO::New); // aos << rec; // } // // Note that he above is put in a separate scope to be sure that // // all objects are deleted and tables are written. // { // TableRecord rec; // AipsIO aos ("file.name"); // aos >> rec; // // At this point the record is read back, but the tables are not opened. // // The next statement accesses the table resulting in its open. // Table t1 = rec.asTable ("tab1"); // // The following statement closes it again. // rec.closeTable ("tab1"); // // // // // In principle the class Record could also support data type Table. // However, this would have had the big disadvantage that all the // Table code would have be linked in when only a simple Record is needed. // It was decided that for that reason it was better to support tables // in a separate class. // // // //

      • A record reference class, which contains some fields from another // record, would likely be useful. This would be analagous to a // subarray sliced from an existing array. // class TableRecord : public RecordInterface { friend class TableRecordRep; public: // Create a record with no fields. // The record has a variable structure. TableRecord(); // Create a record with no fields. // The type determines if the record has a fixed or variable structure. // The callback function is called when a field is added to the Record. // That function can check the name and of data type of the new field // (for instance, the Table system uses it to ensure that table columns // and keywords have different names). explicit TableRecord (RecordType type, CheckFieldFunction* = 0, const void* checkArgument = 0); // Create a record with the given description. If it is not possible to // create all fields (for example, if a field with an unsupported data // type is requested), an exception is thrown. // The type determines if the record has a fixed or variable structure. // All fields are checked by the field checking function (if defined) // (for instance, the Table system uses it to ensure that table columns // and keywords have different names). explicit TableRecord (const RecordDesc& description, RecordType type = Fixed, CheckFieldFunction* = 0, const void* checkArgument = 0); // Create a copy of other using copy semantics. TableRecord (const TableRecord& other); // Create a TableRecord from another type of record. // It uses copy-on-write semantics if possible (i.e. if // other is a TableRecord), otherwise each field is copied. // Subrecords are also copied and converted to TableRecords if needed. TableRecord (const RecordInterface& other); // Copy the data in the other record to this record. // It can operate in 2 ways depending on the TableRecord structure flag. //
          //
        • For variable structured records the existing fields are // thrown away and replaced by the new fields. // This means that RecordFieldPtr's using this record get invalidated. // Because copy-on-write semantics are used, this kind of // assignment is a very efficient operation. //
        • For fixed structured records the existing values are replaced // by the new values. This means that RecordFieldPtr's using this // record remain valid. // The structure of the other record has to conform this record // or this record has to be empty, otherwise an exception is thrown. // This assignment is less efficient, because it has to check the // conformance and because each value has to be copied. //
        // // Attributes like fixed structure flag and check function will not // be copied. // TableRecord& operator= (const TableRecord& other); // Release resources associated with this object. ~TableRecord(); // Make a copy of this object. virtual RecordInterface* clone() const; // Assign that RecordInterface object to this one. // If that is a TableRecord, copy-on-write is used. // Otherwise each individual field is copied. virtual void assign (const RecordInterface& that); // Convert the TableRecord to a Record (recursively). // A possible Table object is converted to a string containing // the table name preceeded by 'Table: ' (as used by TableProxy). Record toRecord() const; // Fill the TableRecord from the given Record. // The fields are appended to the TableRecord. // It is the opposite of toRecord, so a String containing 'Table: ' // is handled as a Table (if it exists). void fromRecord (const Record& rec); // Get or define the value as a ValueHolder. // This is useful to pass around a value of any supported type. // virtual ValueHolder asValueHolder (const RecordFieldId&) const; virtual void defineFromValueHolder (const RecordFieldId&, const ValueHolder&); // // Get the comment for this field. virtual const String& comment (const RecordFieldId&) const; // Set the comment for this field. virtual void setComment (const RecordFieldId&, const String& comment); // Describes the current structure of this TableRecord. const RecordDesc& description() const; // Change the structure of this TableRecord to contain the fields in // newDescription. After calling restructure, description() == // newDescription. Any existing RecordFieldPtr objects are // invalidated (their isAttached() members return False) after // this call. //
        When the new description contains subrecords, those subrecords // will be restructured if recursive=True is given. // Otherwise the subrecord is a variable empty record. // Subrecords will be variable if their description is empty (i.e. does // not contain any field), otherwise they are fixed. //
        Restructuring is not possible and an exception is thrown // if the Record has a fixed structure. virtual void restructure (const RecordDesc& newDescription, Bool recursive=True); // Returns True if this and other have the same RecordDesc, other // than different names for the fields. That is, the number, type and the // order of the fields must be identical (recursively for fixed // structured sub-Records in this). // // thisRecord.conform(thatRecord) == True does not imply //
        thatRecord.conform(thisRecord) == True, because // a variable record in one conforms a fixed record in that, but // not vice-versa. //
        Bool conform (const TableRecord& other) const; // How many fields does this structure have? A convenient synonym for // description().nfields(). virtual uInt nfields() const; // Get the field number from the field name. // -1 is returned if the field name is unknown. virtual Int fieldNumber (const String& fieldName) const; // Get the data type of this field. virtual DataType type (Int whichField) const; // Remove a field from the record. // // Removing a field means that the field number of the fields following // it will be decremented. Only the RecordFieldPtr's // pointing to the removed field will be invalidated. // void removeField (const RecordFieldId&); // Rename the given field. void renameField (const String& newName, const RecordFieldId&); // Define a value for the given field. // When the field is unknown, it will be added to the record. // The second version is meant for any type of record (e.g. Record, // TableRecord, GlishRecord). It is converted to a TableRecord using the // TableRecord constructor taking a RecordInterface object. // void defineRecord (const RecordFieldId&, const TableRecord& value, RecordType type = Variable); virtual void defineRecord (const RecordFieldId&, const RecordInterface& value, RecordType = Variable); void defineTable (const RecordFieldId&, const Table& value, RecordType type = Variable); // // Get the subrecord or table from the given field. // // The non-const version has a different name to prevent that the // copy-on-write mechanism makes a copy when not necessary. // // const TableRecord& subRecord (const RecordFieldId&) const; TableRecord& rwSubRecord (const RecordFieldId&); virtual const RecordInterface& asRecord (const RecordFieldId&) const; virtual RecordInterface& asrwRecord (const RecordFieldId&); // // Get the table from the given field. // By default the read/write option and lock options are inherited // from the parent table. // If openWritable=True, the table is still opened as readonly if the file // permissions do not permit write access. // Table asTable (const RecordFieldId&) const; Table asTable (const RecordFieldId&, const TableLock& lockOptions) const; // // Get the attributes of a table field. const TableAttr& tableAttributes (const RecordFieldId&) const; // Merge a field from another record into this record. // The DuplicatesFlag (as described in // RecordInterface) determines // what will be done in case the field name already exists. void mergeField (const TableRecord& other, const RecordFieldId&, DuplicatesFlag = ThrowOnDuplicates); // Merge all fields from the other record into this record. // The DuplicatesFlag (as described in // RecordInterface) determines // what will be done in case a field name already exists. // An exception will be thrown if other is the same as this // (i.e. if merging the record itself). void merge (const TableRecord& other, DuplicatesFlag = ThrowOnDuplicates); // Close the table in the given field. // When accessed again, it will be opened automatically. // This can be useful to save memory usage. void closeTable (const RecordFieldId&) const; // Close all open tables. // When accessed again, it will be opened automatically. // This can be useful to save memory usage. void closeTables() const; // Flush all open subtables. void flushTables (Bool fsync=False) const; // Rename the subtables with a path containing the old parent table name. void renameTables (const String& newParentName, const String& oldParentName); // Are subtables used in other processes. Bool areTablesMultiUsed() const; // Write the TableRecord to an output stream. friend AipsIO& operator<< (AipsIO& os, const TableRecord& rec); // Read the TableRecord from an input stream. friend AipsIO& operator>> (AipsIO& os, TableRecord& rec); // Put the data of a record. // This is used to write a subrecord, whose description has // not been written. void putRecord (AipsIO& os, const TableAttr&) const; // Read a record. // This is used to read a subrecord, whose description has // not been read. void getRecord (AipsIO& os, const TableAttr&); // Put the data of a record. // This is used to write a subrecord, whose description has // already been written. void putData (AipsIO& os, const TableAttr&) const; // Read the data of a record. // This is used to read a subrecord, whose description has // already been read. void getData (AipsIO& os, uInt version, const TableAttr&); // Print the contents of the record. // Only the first maxNrValues of an array will be printed. // A value < 0 means the entire array. virtual void print (std::ostream&, Int maxNrValues = 25, const String& indent="") const; // Reopen possible tables in keywords as read/write. // Tables are not reopened if they are not writable. void reopenRW(); // Recursively set the attributes of subtables to the ones in the other // record for matching subtable field names. Otherwise set it to defaultAttr. // The name attribute is not changed. // It is primarily a helper function for PlainTable::syncTable // and ColumnSet::syncColumns. //
        However, it can also be used to achieve that all subtables of a // read/write table are opened as readonly. E.g.: // // TableAttr newAttr(String(), False, mainTable.lockOptions()); // mainTable.keywordSet().setTableAttr (TableRecord(), newAttr); // void setTableAttr (const TableRecord& other, const TableAttr& defaultAttr); // Make a unique record representation // (to do copy-on-write in RecordFieldPtr). virtual void makeUnique(); protected: // Used by the RecordField classes to attach in a type-safe way to the // correct field. // virtual void* get_pointer (Int whichField, DataType type) const; virtual void* get_pointer (Int whichField, DataType type, const String& recordType) const; // // Return a const reference to the underlying TableRecordRep. const TableRecordRep& ref() const; // Return a non-const reference to the underlying TableRecordRep. // When needed, the TableRecordRep will be copied and all RecordField // objects will be notified. TableRecordRep& rwRef(); // Add a field to the record. virtual void addDataField (const String& name, DataType type, const IPosition& shape, Bool fixedShape, const void* value); // Define a value in the given field. virtual void defineDataField (Int whichField, DataType type, const void* value); private: // Get the description of this record. virtual RecordDesc getDescription() const; // Create TableRecord as a subrecord. // When the description is empty, the record has a variable structure. // Otherwise it is fixed. // TableRecord (TableRecordRep* parent, const RecordDesc& description); TableRecord (TableRecordRep* parent, RecordType type); // // Set the recordtype of this record and all its subrecords (recursively). void setRecordType (RecordType type); // The TableRecord representation. COWPtr rep_p; // The parent TableRecord. TableRecordRep* parent_p; }; inline const TableRecordRep& TableRecord::ref() const { return rep_p.ref(); } inline const RecordDesc& TableRecord::description() const { return ref().description(); } inline Bool TableRecord::conform (const TableRecord& other) const { return ref().conform (other.ref()); } inline void TableRecord::putData (AipsIO& os, const TableAttr& parentAttr) const { ref().putData (os, parentAttr); } inline void TableRecord::getData (AipsIO& os, uInt version, const TableAttr& parentAttr) { rwRef().getData (os, version, parentAttr); } inline void TableRecord::reopenRW() { rwRef().reopenRW(); } inline void TableRecord::closeTables() const { ref().closeTables(); } inline void TableRecord::flushTables (Bool fsync) const { ref().flushTables (fsync); } inline void TableRecord::renameTables (const String& newParentName, const String& oldParentName) { rwRef().renameTables (newParentName, oldParentName); } inline Bool TableRecord::areTablesMultiUsed() const { return ref().areTablesMultiUsed(); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/TableRecordRep.cc000066400000000000000000000374401476623553700211640ustar00rootroot00000000000000//# TableRecordRep.cc: A hierarchical collection of named fields of various types //# Copyright (C) 1996,1997,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableRecordRep::TableRecordRep () : RecordRep() {} TableRecordRep::TableRecordRep (const RecordDesc& description) : RecordRep(), desc_p (description) { restructure (desc_p, True); } TableRecordRep::TableRecordRep (const TableRecordRep& other) : RecordRep(), desc_p (other.desc_p) { restructure (desc_p, False); copy_other (other); } TableRecordRep& TableRecordRep::operator= (const TableRecordRep& other) { if (this != &other) { restructure (other.desc_p, False); copy_other (other); } return *this; } TableRecordRep::~TableRecordRep() { delete_myself (desc_p.nfields()); } void TableRecordRep::restructure (const RecordDesc& newDescription, Bool recursive) { delete_myself (desc_p.nfields()); desc_p = newDescription; nused_p = desc_p.nfields(); datavec_p.resize (nused_p); datavec_p = static_cast(0); data_p.resize (nused_p); for (uInt i=0; i(ptr); } else if (type == TpTable) { delete static_cast(ptr); }else{ deleteDataField (type, ptr, vecptr); } } void TableRecordRep::addFieldToDesc (const String& name, DataType type, const IPosition& shape, Bool fixedShape) { if (fixedShape) { desc_p.addField (name, type, shape); }else{ desc_p.addField (name, type); } } void TableRecordRep::removeFieldFromDesc (Int whichField) { desc_p.removeField (whichField); } void TableRecordRep::addField (const String& name, const TableRecord& value, RecordInterface::RecordType type) { // When the record is empty, it is variable structured. if (value.nfields() == 0) { type = RecordInterface::Variable; } // When the new field is fixed, add its description too. if (type == RecordInterface::Fixed) { desc_p.addField (name, value.description()); }else{ desc_p.addField (name, TpRecord); } // Use default ctor and assignment to be sure that the // new record is variable structured. TableRecord* ptr = new TableRecord (this, type); *ptr = value; addDataPtr (ptr); } void TableRecordRep::addField (const String& name, const Table& value, RecordInterface::RecordType type) { String tableDescName; // When the new field is fixed, add its description name too. if (type == RecordInterface::Fixed) { tableDescName = value.tableDesc().getType(); } desc_p.addTable (name, tableDescName); addDataPtr (new TableKeyword(value, tableDescName)); } void TableRecordRep::defineDataField (Int whichField, DataType type, const void* value) { AlwaysAssert (whichField >= 0 && whichField < Int(nused_p), AipsError); DataType descDtype = desc_p.type(whichField); if (type == descDtype) { if (type == TpRecord) { *static_cast(data_p[whichField]) = *static_cast(value); } else if (type == TpTable) { *static_cast(data_p[whichField]) = *static_cast(value); }else{ if (desc_p.isArray(whichField)) { const IPosition& shape = desc_p.shape(whichField); if (shape.nelements() > 0 && shape(0) > 0) { checkShape (type, shape, value, desc_p.name(whichField)); } } copyDataField (type, data_p[whichField], value); } } else if (isArray(type) && asScalar(type) == descDtype) { // A scalar can be defined using a single element vector. checkShape (type, IPosition(1,1), value, desc_p.name(whichField)); // Make sure there is a datavec entry. get_pointer (whichField, type); copyDataField (type, datavec_p[whichField], value); } else { throw (AipsError ("TableRecordRep::defineDataField - " "incorrect data type used for field " + desc_p.name(whichField))); } } Bool TableRecordRep::conform (const TableRecordRep& other) const { // First check (non-recursively) if the descriptions conform. if (! desc_p.conform (other.desc_p)) { return False; } // Now check for each fixed sub-record and table if it conforms. for (Int i=0; i(const_cast(data_p[i])); if (thisRecord.isFixed()) { const TableRecord& thatRecord = *static_cast(const_cast(other.data_p[i])); if (! thisRecord.conform (thatRecord)) { return False; } } } else if (desc_p.type(i) == TpTable) { const TableKeyword& thisKey = *static_cast(const_cast(data_p[i])); if (thisKey.isFixed()) { const TableKeyword& thatKey = *static_cast(const_cast(other.data_p[i])); if (! thisKey.conform (thatKey)) { return False; } } } } return True; } void TableRecordRep::copyData (const TableRecordRep& other) { // Assume conform has already been called DebugAssert (conform (other), AipsError); copy_other (other); } void TableRecordRep::copy_other (const TableRecordRep& other) { for (uInt i=0; i(data_p[i]) = *static_cast(const_cast(other.data_p[i])); } else if (desc_p.type(i) == TpTable) { *static_cast(data_p[i]) = *static_cast(const_cast(other.data_p[i])); }else{ copyDataField (desc_p.type(i), data_p[i], other.data_p[i]); } } } void* TableRecordRep::get_pointer (Int whichField, DataType type, const String& recordType) const { if (recordType != "TableRecord") { throw (AipsError ("TableRecordRep::get_pointer - field " + desc_p.name(whichField) + " is not of type TableRecord")); } return get_pointer (whichField, type); } void* TableRecordRep::get_pointer (Int whichField, DataType type) const { AlwaysAssert (whichField >= 0 && whichField < Int(nused_p), AipsError); DataType descDtype = desc_p.type(whichField); if (type == descDtype) { return data_p[whichField]; } // A scalar can be returned as an array. if (! (isArray(type) && asScalar(type) == descDtype)) { throw (AipsError ("TableRecordRep::get_pointer - " "incorrect data type used for field " + desc_p.name(whichField))); } if (datavec_p[whichField] == nullptr) { const_cast(this)->makeDataVec (whichField, descDtype); } return datavec_p[whichField]; } void TableRecordRep::closeTable (Int whichField) const { AlwaysAssert (whichField >= 0 && whichField < Int(desc_p.nfields()) && desc_p.type(whichField) == TpTable, AipsError); static_cast(const_cast(data_p[whichField]))->close(); } void TableRecordRep::mergeField (const TableRecordRep& other, Int whichFieldFromOther, RecordInterface::DuplicatesFlag flag) { // If the field exists and if flag tells to overwrite, // the field is removed first. if (flag == RecordInterface::OverwriteDuplicates) { Int fld = desc_p.fieldNumber (other.desc_p.name(whichFieldFromOther)); if (fld >= 0) { removeField (fld); } } // Try to add the field to the description. Int nr = desc_p.nfields(); Int nrnew = desc_p.mergeField (other.desc_p, whichFieldFromOther, flag); // It succeeded if nfields increased. // Then the value can be defined. if (nrnew > nr) { DataType type = desc_p.type (nr); void* otherPtr = other.get_pointer (whichFieldFromOther, type); void* ptr; if (type == TpRecord) { ptr = new TableRecord (*static_cast(otherPtr)); } else if (type == TpTable) { ptr = new TableKeyword (*static_cast(otherPtr)); }else{ ptr = createDataField (type, desc_p.shape(nr)); copyDataField (type, ptr, otherPtr); } addDataPtr (ptr); } } void TableRecordRep::merge (const TableRecordRep& other, RecordInterface::DuplicatesFlag flag) { Int n = other.desc_p.nfields(); for (Int i=0; i(data_p[i])->renameTable (newParentName, oldParentName); } } } void TableRecordRep::closeTables() const { for (uInt i=0; i(const_cast(data_p[i]))->close(); } } } void TableRecordRep::flushTables (Bool fsync) const { for (uInt i=0; i(const_cast(data_p[i]))->flush(fsync); } } } Bool TableRecordRep::areTablesMultiUsed() const { for (uInt i=0; i(const_cast(data_p[i]))->isMultiUsed(True)) { return True; } } } return False; } void TableRecordRep::print (std::ostream& os, Int maxNrValues, const String& indent) const { for (uInt i=0; i(data_p[i])->print(os, maxNrValues, indent+" "); os << indent << '}' << endl; } else if (desc_p.type(i) == TpTable) { os << "Table " << static_cast(data_p[i])->tableName() << endl; } else { printDataField (os, desc_p.type(i), indent, maxNrValues, data_p[i]); os << endl; } } } void TableRecordRep::putRecord (AipsIO& os, Int recordType, const TableAttr& parentAttr) const { os.putstart ("TableRecord", 1); // version 1 os << desc_p; os << recordType; putData (os, parentAttr); os.putend(); } void TableRecordRep::putData (AipsIO& os, const TableAttr& parentAttr) const { for (uInt i=0; i(const_cast(data_p[i]))->putRecord (os, parentAttr); }else{ static_cast(const_cast(data_p[i]))->putData (os, parentAttr); } } else if (desc_p.type(i) == TpTable) { os << static_cast(const_cast(data_p[i]))->tableName (parentAttr); }else{ putDataField (os, desc_p.type(i), data_p[i]); } } } void TableRecordRep::getRecord (AipsIO& os, Int& recordType, const TableAttr& parentAttr) { // Support reading scalar, array, and table keyword sets as records. uInt version; String type = os.getNextType(); if (type == "ScalarKeywordSet") { version = os.getstart ("ScalarKeywordSet"); getTableKeySet (os, version, parentAttr, 0); } else if (type == "ArrayKeywordSet") { version = os.getstart ("ArrayKeywordSet"); getTableKeySet (os, version, parentAttr, 1); } else if (type == "TableKeywordSet") { version = os.getstart ("TableKeywordSet"); getTableKeySet (os, version, parentAttr, 2); recordType = RecordInterface::Variable; }else{ uInt version = os.getstart ("TableRecord"); // Get the description and restructure the record. RecordDesc desc; os >> desc; os >> recordType; restructure (desc, True); // Read the data. getData (os, version, parentAttr); } os.getend(); } void TableRecordRep::getData (AipsIO& os, uInt version, const TableAttr& parentAttr) { for (uInt i=0; i(data_p[i])->getRecord (os, parentAttr); }else{ static_cast(data_p[i])->getData (os, version, parentAttr); } }else if (type == TpTable) { String name; os >> name; static_cast(data_p[i])->set (name, parentAttr); }else{ getDataField (os, desc_p.type(i), data_p[i]); } } } void TableRecordRep::reopenRW() { for (uInt i=0; i(data_p[i])->reopenRW(); }else if (type == TpTable) { static_cast(data_p[i])->setRW(); } } } void TableRecordRep::getTableKeySet (AipsIO& os, uInt version, const TableAttr& parentAttr, uInt type) { // First build the description from the map of keyword names and // attributes. RecordDesc desc; getKeyDesc (os, desc); // Define the record from the description. // Read the keyword values and define the corresponding record value. restructure (desc, True); getScalarKeys (os); if (type > 0) { getArrayKeys (os); } if (type > 1) { String key, name; uInt i, n; os >> n; for (i=0; i> key; // keyword name os >> name; // table name static_cast(data_p[desc_p.fieldNumber(key)])->set (name, parentAttr); } } // Newer keyword sets may contain nested keyword sets. // We do not support reading those, so throw an exception when they exist. if (version > 1) { uInt n; os >> n; AlwaysAssert (n==0, AipsError); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/TableRecordRep.h000066400000000000000000000241431476623553700210220ustar00rootroot00000000000000//# TableRecordRep.h: The representation of a TableRecord //# Copyright (C) 1996,1997,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABLERECORDREP_H #define TABLES_TABLERECORDREP_H #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableRecord; class TableAttr; // // The representation of a TableRecord // // // // // //
      • TableRecord. //
      • RecordRep. // // // // TableRecordRep is the REPresentation of a TableRecord. // // // // TableRecordRep is the actual implementation of a TableRecord object. // It contains the description and the data. The data is stored as // a collection of void* pointers to the actual data. By storing // it in this indirect way, it is easier to extend the data block. // It also means that RecordFieldPtr objects always have the correct // pointer and do not need to be adjusted when the data block is extended. //

        // Despite the fact that the data pointers have type void*, the // functions are completely type safe. This is done by passing the // type around using the DataType enumeration. The downpart is that // only types from that enumeration are supported (but that is also // required by the RecordDesc mechanics). //

        // Note that TableRecordRep does not know anything about RecordFieldPtr // objects pointing to its data. Only its mother class TableRecord // knows about them and handles all cases where the RecordFieldPtr's // have to be notified. //

        // Fields containing tables are not directly handled using class Table. // Instead the class TableKeyword // is used to map a table name to a table and to take care of // opening a table on demand. // // // // TableRecordRep mirrors all functions in TableRecord. // // // // Having a separate TableRecordRep class makes copy-on-write possible. // It also allows derivation from RecordRep. // // //# //# class TableRecordRep : public RecordRep { public: // Create a record with no fields. TableRecordRep(); // Create a record with the given description. If it is not possible to // create all fields (for example, if a field of an unsupported type is // requested), an exception is thrown. // All fields are checked by the field checking function (if defined). TableRecordRep (const RecordDesc& description); // Create a copy of other using copy semantics. TableRecordRep (const TableRecordRep& other); // Copy all the data over. TableRecordRep& operator= (const TableRecordRep& other); // Delete all data. ~TableRecordRep(); // Get the comment for this field. const String& comment (Int whichField) const; // Set the comment for this field. void setComment (Int whichField, const String& comment); // Describes the current structure of this Record. const RecordDesc& description() const; // Change the structure of this Record to contain the fields in // newDescription. After calling restructure, description() == // newDescription. void restructure (const RecordDesc& newDescription, Bool recursive); // Returns True if this and other have the same RecordDesc, other // than different names for the fields. That is, the number, type and the // order of the fields must be identical (recursively for fixed // structured sub-Records in this). // // thisRecord.conform(thatRecord) == True does not imply //
        thatRecord.conform(thisRecord) == True, because // a variable record in one conforms a fixed record in that, but // not vice-versa. //
        Bool conform (const TableRecordRep& other) const; // Rename the given field. void renameField (const String& newName, Int whichField); // Copy all data of the TableRecord. void copyData (const TableRecordRep& other); // Add a field with the given name and value to the record. // The data type of the field is determined by the data type of the value. // void addField (const String& name, const TableRecord& value, RecordInterface::RecordType type); void addField (const String& name, const Table& value, RecordInterface::RecordType type); // // Define a value for the given field. // Array conformance rules will not be applied for variable shaped arrays. // When the field and value data type mismatch, type promotion // of scalars will be done if possible. If not possible, an exception // is thrown. void defineDataField (Int whichField, DataType type, const void* value); // Close the table in the given field. // When accessed again, it will be opened automatically. // This can be useful to save memory usage. void closeTable (Int whichField) const; // Close all open tables. // When accessed again, it will be opened automatically. // This can be useful to save memory usage. void closeTables() const; // Flush all open subtables. void flushTables (Bool fsync) const; // Rename the subtables with a path containing the old parent table name. void renameTables (const String& newParentName, const String& oldParentName); // Are subtables used in other processes. Bool areTablesMultiUsed() const; // Put the description and data of the Record. // It also puts the fixedFlag attribute (of the mother object). void putRecord (AipsIO& os, Int recordType, const TableAttr&) const; // Get the description and data of the Record. // It also gets the fixedFlag attribute (of the mother object). void getRecord (AipsIO& os, Int& recordType, const TableAttr&); // Put the data of a record. // This is used to write a subrecord, whose description has // already been written. void putData (AipsIO& os, const TableAttr&) const; // Read the data of a record. // This is used to read a subrecord, whose description has // already been read. void getData (AipsIO& os, uInt version, const TableAttr&); // Reopen possible tables in keywords as read/write. // Tables are not reopened if they are not writable. void reopenRW(); // Used by the RecordFieldPtr classes to attach in a type-safe way to the // correct field. // void* get_pointer (Int whichField, DataType type) const; void* get_pointer (Int whichField, DataType type, const String& recordType) const; // // Merge a field from another record into this record. void mergeField (const TableRecordRep& other, Int whichFieldFromOther, RecordInterface::DuplicatesFlag); // Merge all fields from the other record into this record. void merge (const TableRecordRep& other, RecordInterface::DuplicatesFlag); // Print a record. // Print the contents of the record. // Only the first maxNrValues of an array will be printed. // A value < 0 means the entire array. void print (std::ostream&, Int maxNrValues = 25, const String& indent="") const; protected: // Utility function to avoid code duplication in the public member // functions. void copy_other (const TableRecordRep& other); // Get the field number for a given name. virtual Int fieldNumber (const String& name) const; // Add a field to the description. virtual void addFieldToDesc (const String& name, DataType type, const IPosition& shape, Bool fixedShape); // Remove a data field. virtual void removeData (Int whichField, void* ptr, void* vecptr); // Remove a field from the description. virtual void removeFieldFromDesc (Int whichField); // Get a KeywordSet object as a TableRecord. // (type: 0=ScalarKeywordSet, 1=ArrayKeywordSet, 2=TableKeywordSet) void getTableKeySet (AipsIO& os, uInt version, const TableAttr&, uInt type); // Holds the description. //# Although we could use the RecordDesc object from RecordRep, //# it is better to use an own RecordDesc object in case a dedicated //# TableRecordDesc is needed in the future. In this way it //# is sure that inherited functions do not use the RecordDesc object //# in RecordRep. RecordDesc desc_p; }; inline const String& TableRecordRep::comment (Int whichField) const { return desc_p.comment (whichField); } inline void TableRecordRep::setComment (Int whichField, const String& comment) { desc_p.setComment (whichField, comment); } inline const RecordDesc& TableRecordRep::description() const { return desc_p; } inline void TableRecordRep::renameField (const String& newName, Int whichField) { desc_p.renameField (newName, whichField); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/TableRow.cc000066400000000000000000000742521476623553700200500ustar00rootroot00000000000000//# TableRow.cc: Access to a table row //# Copyright (C) 1996,1997,1998,1999,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN ROTableRow::ROTableRow() : itsRecord (0) { init(); } ROTableRow::ROTableRow (const Table& table, Bool storedColumnsOnly) : itsRecord (0) { init(); create (table, storedColumnsOnly, False); } ROTableRow::ROTableRow (const Table& table, const Vector& columnNames, Bool exclude) : itsRecord (0) { init(); create (table, columnNames, exclude, False); } ROTableRow::ROTableRow (const ROTableRow& that) : itsRecord (0) { init(); copy (that); } void ROTableRow::init() { itsLastRow = -1; itsReread = True; } ROTableRow::~ROTableRow() { deleteObjects(); } ROTableRow& ROTableRow::operator= (const ROTableRow& that) { copy (that); return *this; } void ROTableRow::copy (const ROTableRow& that) { if (this != &that) { deleteObjects(); itsTable = that.itsTable; itsNrused = that.itsNrused; itsLastRow = that.itsLastRow; itsReread = that.itsReread; if (that.itsRecord != 0) { makeObjects (that.itsRecord->description()); } } } Vector ROTableRow::columnNames() const { const RecordDesc& desc = itsRecord->description(); uInt nfield = desc.nfields(); Vector names(nfield); for (uInt i=0; idescription(); for (uInt i=0; i*)(itsColumns[i]); delete (RecordFieldPtr*)(itsFields[i]); break; case TpArrayBool: delete (ArrayColumn*)(itsColumns[i]); delete (RecordFieldPtr >*)(itsFields[i]); break; case TpUChar: delete (ScalarColumn*)(itsColumns[i]); delete (RecordFieldPtr*)(itsFields[i]); break; case TpArrayUChar: delete (ArrayColumn*)(itsColumns[i]); delete (RecordFieldPtr >*)(itsFields[i]); break; case TpShort: delete (ScalarColumn*)(itsColumns[i]); delete (RecordFieldPtr*)(itsFields[i]); break; case TpArrayShort: delete (ArrayColumn*)(itsColumns[i]); delete (RecordFieldPtr >*)(itsFields[i]); break; case TpInt: delete (ScalarColumn*)(itsColumns[i]); delete (RecordFieldPtr*)(itsFields[i]); break; case TpArrayInt: delete (ArrayColumn*)(itsColumns[i]); delete (RecordFieldPtr >*)(itsFields[i]); break; case TpUInt: delete (ScalarColumn*)(itsColumns[i]); delete (RecordFieldPtr*)(itsFields[i]); break; case TpArrayUInt: delete (ArrayColumn*)(itsColumns[i]); delete (RecordFieldPtr >*)(itsFields[i]); break; case TpInt64: delete (ScalarColumn*)(itsColumns[i]); delete (RecordFieldPtr*)(itsFields[i]); break; case TpArrayInt64: delete (ArrayColumn*)(itsColumns[i]); delete (RecordFieldPtr >*)(itsFields[i]); break; case TpFloat: delete (ScalarColumn*)(itsColumns[i]); delete (RecordFieldPtr*)(itsFields[i]); break; case TpArrayFloat: delete (ArrayColumn*)(itsColumns[i]); delete (RecordFieldPtr >*)(itsFields[i]); break; case TpDouble: delete (ScalarColumn*)(itsColumns[i]); delete (RecordFieldPtr*)(itsFields[i]); break; case TpArrayDouble: delete (ArrayColumn*)(itsColumns[i]); delete (RecordFieldPtr >*)(itsFields[i]); break; case TpComplex: delete (ScalarColumn*)(itsColumns[i]); delete (RecordFieldPtr*)(itsFields[i]); break; case TpArrayComplex: delete (ArrayColumn*)(itsColumns[i]); delete (RecordFieldPtr >*)(itsFields[i]); break; case TpDComplex: delete (ScalarColumn*)(itsColumns[i]); delete (RecordFieldPtr*)(itsFields[i]); break; case TpArrayDComplex: delete (ArrayColumn*)(itsColumns[i]); delete (RecordFieldPtr >*)(itsFields[i]); break; case TpString: delete (ScalarColumn*)(itsColumns[i]); delete (RecordFieldPtr*)(itsFields[i]); break; case TpArrayString: delete (ArrayColumn*)(itsColumns[i]); delete (RecordFieldPtr >*)(itsFields[i]); break; case TpRecord: delete (ScalarColumn*)(itsColumns[i]); delete (RecordFieldPtr*)(itsFields[i]); break; default: throw (TableError ("TableRow: unknown data type")); } itsTabCols[i] = 0; itsColumns[i] = 0; itsFields[i] = 0; } delete itsRecord; itsRecord = 0; } void ROTableRow::addColumnToDesc (RecordDesc& description, const TableColumn& column, Bool skipOther) { const ColumnDesc& columnDesc = column.columnDesc(); DataType dataType = columnDesc.dataType(); if (! (skipOther && dataType == TpOther)) { if (columnDesc.isArray()) { IPosition shape = column.shapeColumn(); if (shape.nelements() == 0) { shape = IPosition(1,-1); } description.addField (columnDesc.name(), dataType, shape); }else{ description.addField (columnDesc.name(), dataType); } itsNrused++; } } void ROTableRow::create (const Table& table, Bool storedColumnsOnly, Bool writable) { itsTable = table; // Loop through all columns in the table. // Add it to the RecordDesc when the column is writable or // when we do not need to write and when the column is stored // or no stored columns are asked for.. itsNrused = 0; RecordDesc description; uInt nrcol = itsTable.tableDesc().ncolumn(); for (uInt i=0; i& columnNames, Bool exclude, Bool writable) { itsTable = table; // Loop through all column names. // Always add it to the RecordDesc. itsNrused = 0; RecordDesc description; if (exclude) { makeDescExclude (description, columnNames, writable); }else{ uInt nrcol = columnNames.nelements(); for (uInt i=0; i& columnNames, Bool writable) { // Loop through all columns in the table. // Add it to the RecordDesc when the column is writable or // when we do not need to write. Skip the columns to exclude. uInt nrcol = itsTable.tableDesc().ncolumn(); uInt nrexcl = columnNames.nelements(); const TableDesc& tableDesc = itsTable.tableDesc(); for (uInt i=0; i(0)); itsColumns.resize (itsNrused, False, False); itsColumns.set (static_cast(0)); itsFields.resize (itsNrused, False, False); itsFields.set (static_cast(0)); itsDefined.resize (itsNrused, False, False); itsDefined.set (True); // Create the correct column object for each field. // (if not writing, an RO version is sufficient). // Also create a RecordFieldPtr object for each column. // This makes a fast data copy possible. uInt nrfield = description.nfields(); for (uInt i=0; i (itsTable, name); itsFields[i] = new RecordFieldPtr(*itsRecord, i); break; case TpUChar: itsColumns[i] = new ScalarColumn (itsTable, name); itsFields[i] = new RecordFieldPtr(*itsRecord, i); break; case TpShort: itsColumns[i] = new ScalarColumn (itsTable, name); itsFields[i] = new RecordFieldPtr(*itsRecord, i); break; case TpInt: itsColumns[i] = new ScalarColumn (itsTable, name); itsFields[i] = new RecordFieldPtr(*itsRecord, i); break; case TpUInt: itsColumns[i] = new ScalarColumn (itsTable, name); itsFields[i] = new RecordFieldPtr(*itsRecord, i); break; case TpInt64: itsColumns[i] = new ScalarColumn (itsTable, name); itsFields[i] = new RecordFieldPtr(*itsRecord, i); break; case TpFloat: itsColumns[i] = new ScalarColumn (itsTable, name); itsFields[i] = new RecordFieldPtr(*itsRecord, i); break; case TpDouble: itsColumns[i] = new ScalarColumn (itsTable, name); itsFields[i] = new RecordFieldPtr(*itsRecord, i); break; case TpComplex: itsColumns[i] = new ScalarColumn (itsTable, name); itsFields[i] = new RecordFieldPtr(*itsRecord, i); break; case TpDComplex: itsColumns[i] = new ScalarColumn (itsTable, name); itsFields[i] = new RecordFieldPtr(*itsRecord, i); break; case TpString: itsColumns[i] = new ScalarColumn (itsTable, name); itsFields[i] = new RecordFieldPtr(*itsRecord, i); break; case TpRecord: itsColumns[i] = new ScalarColumn (itsTable, name); itsFields[i] = new RecordFieldPtr(*itsRecord, i); break; case TpArrayBool: itsColumns[i] = new ArrayColumn (itsTable, name); itsFields[i] = new RecordFieldPtr >(*itsRecord, i); break; case TpArrayUChar: itsColumns[i] = new ArrayColumn (itsTable, name); itsFields[i] = new RecordFieldPtr >(*itsRecord, i); break; case TpArrayShort: itsColumns[i] = new ArrayColumn (itsTable, name); itsFields[i] = new RecordFieldPtr >(*itsRecord, i); break; case TpArrayInt: itsColumns[i] = new ArrayColumn (itsTable, name); itsFields[i] = new RecordFieldPtr >(*itsRecord, i); break; case TpArrayUInt: itsColumns[i] = new ArrayColumn (itsTable, name); itsFields[i] = new RecordFieldPtr >(*itsRecord, i); break; case TpArrayInt64: itsColumns[i] = new ArrayColumn (itsTable, name); itsFields[i] = new RecordFieldPtr >(*itsRecord, i); break; case TpArrayFloat: itsColumns[i] = new ArrayColumn (itsTable, name); itsFields[i] = new RecordFieldPtr >(*itsRecord, i); break; case TpArrayDouble: itsColumns[i] = new ArrayColumn (itsTable, name); itsFields[i] = new RecordFieldPtr >(*itsRecord, i); break; case TpArrayComplex: itsColumns[i] = new ArrayColumn (itsTable, name); itsFields[i] = new RecordFieldPtr >(*itsRecord, i); break; case TpArrayDComplex: itsColumns[i] = new ArrayColumn (itsTable, name); itsFields[i] = new RecordFieldPtr >(*itsRecord, i); break; case TpArrayString: itsColumns[i] = new ArrayColumn (itsTable, name); itsFields[i] = new RecordFieldPtr >(*itsRecord, i); break; default: throw (TableError ("TableRow: cannot handle Table " "and TableRecord yet")); } } } const TableRecord& ROTableRow::get (rownr_t rownr, Bool alwaysRead) const { // Only read when needed. if (Int64(rownr) == itsLastRow && !itsReread && !alwaysRead) { return *itsRecord; } const RecordDesc& desc = itsRecord->description(); Int ndim = 0; uInt nrfield = desc.nfields(); for (uInt i=0; i*)(itsColumns[i])).get ( rownr, *(*(RecordFieldPtr*) itsFields[i])); break; case TpArrayBool: if (isDefined) { (*(const ArrayColumn*)(itsColumns[i])).get ( rownr, *(*(RecordFieldPtr >*) itsFields[i]), True); }else{ (*(RecordFieldPtr >*)(itsFields[i])).define ( Array (IPosition(ndim, 0))); } break; case TpUChar: (*(const ScalarColumn*)(itsColumns[i])).get ( rownr, *(*(RecordFieldPtr*) itsFields[i])); break; case TpArrayUChar: if (isDefined) { (*(const ArrayColumn*)(itsColumns[i])).get ( rownr, *(*(RecordFieldPtr >*) itsFields[i]), True); }else{ (*(RecordFieldPtr >*)(itsFields[i])).define ( Array (IPosition(ndim, 0))); } break; case TpShort: (*(const ScalarColumn*)(itsColumns[i])).get ( rownr, *(*(RecordFieldPtr*) itsFields[i])); break; case TpArrayShort: if (isDefined) { (*(const ArrayColumn*)(itsColumns[i])).get ( rownr, *(*(RecordFieldPtr >*) itsFields[i]), True); }else{ (*(RecordFieldPtr >*)(itsFields[i])).define ( Array (IPosition(ndim, 0))); } break; case TpInt: (*(const ScalarColumn*)(itsColumns[i])).get ( rownr, *(*(RecordFieldPtr*) itsFields[i])); break; case TpArrayInt: if (isDefined) { (*(const ArrayColumn*)(itsColumns[i])).get ( rownr, *(*(RecordFieldPtr >*) itsFields[i]), True); }else{ (*(RecordFieldPtr >*)(itsFields[i])).define ( Array (IPosition(ndim, 0))); } break; case TpUInt: (*(const ScalarColumn*)(itsColumns[i])).get ( rownr, *(*(RecordFieldPtr*) itsFields[i])); break; case TpArrayUInt: if (isDefined) { (*(const ArrayColumn*)(itsColumns[i])).get ( rownr, *(*(RecordFieldPtr >*) itsFields[i]), True); }else{ (*(RecordFieldPtr >*)(itsFields[i])).define ( Array (IPosition(ndim, 0))); } break; case TpInt64: (*(const ScalarColumn*)(itsColumns[i])).get ( rownr, *(*(RecordFieldPtr*) itsFields[i])); break; case TpArrayInt64: if (isDefined) { (*(const ArrayColumn*)(itsColumns[i])).get ( rownr, *(*(RecordFieldPtr >*) itsFields[i]), True); }else{ (*(RecordFieldPtr >*)(itsFields[i])).define ( Array (IPosition(ndim, 0))); } break; case TpFloat: (*(const ScalarColumn*)(itsColumns[i])).get ( rownr, *(*(RecordFieldPtr*) itsFields[i])); break; case TpArrayFloat: if (isDefined) { (*(const ArrayColumn*)(itsColumns[i])).get ( rownr, *(*(RecordFieldPtr >*) itsFields[i]), True); }else{ (*(RecordFieldPtr >*)(itsFields[i])).define ( Array (IPosition(ndim, 0))); } break; case TpDouble: (*(const ScalarColumn*)(itsColumns[i])).get ( rownr, *(*(RecordFieldPtr*) itsFields[i])); break; case TpArrayDouble: if (isDefined) { (*(const ArrayColumn*)(itsColumns[i])).get ( rownr, *(*(RecordFieldPtr >*) itsFields[i]), True); }else{ (*(RecordFieldPtr >*)(itsFields[i])).define ( Array (IPosition(ndim, 0))); } break; case TpComplex: (*(const ScalarColumn*)(itsColumns[i])).get ( rownr, *(*(RecordFieldPtr*) itsFields[i])); break; case TpArrayComplex: if (isDefined) { (*(const ArrayColumn*)(itsColumns[i])).get ( rownr, *(*(RecordFieldPtr >*) itsFields[i]), True); }else{ (*(RecordFieldPtr >*)(itsFields[i])).define ( Array (IPosition(ndim, 0))); } break; case TpDComplex: (*(const ScalarColumn*)(itsColumns[i])).get ( rownr, *(*(RecordFieldPtr*) itsFields[i])); break; case TpArrayDComplex: if (isDefined) { (*(const ArrayColumn*)(itsColumns[i])).get ( rownr, *(*(RecordFieldPtr >*) itsFields[i]), True); }else{ (*(RecordFieldPtr >*)(itsFields[i])).define ( Array (IPosition(ndim, 0))); } break; case TpString: (*(const ScalarColumn*)(itsColumns[i])).get ( rownr, *(*(RecordFieldPtr*) itsFields[i])); break; case TpArrayString: if (isDefined) { (*(const ArrayColumn*)(itsColumns[i])).get ( rownr, *(*(RecordFieldPtr >*) itsFields[i]), True); }else{ (*(RecordFieldPtr >*)(itsFields[i])).define ( Array (IPosition(ndim, 0))); } break; case TpRecord: (*(const ScalarColumn*)(itsColumns[i])).get ( rownr, *(*(RecordFieldPtr*) itsFields[i])); break; default: throw (TableError ("TableRow: unknown data type")); } } itsLastRow = rownr; itsReread = False; return *itsRecord; } // The values (might) have changed, which is not reflected in the // internal record. Be sure to reread when the same row is asked for. void ROTableRow::setReread (rownr_t rownr) { if (Int64(rownr) == itsLastRow) { itsReread = True; } } // put into column, convert if necessary #define PUTFIELD_ARRAY(type) \ do { \ try { \ (*(ArrayColumn*)(itsColumns[whichColumn])).put \ (rownr, record.asArray##type (whichField)); \ } \ catch (const AipsError & e) { \ (*(ArrayColumn*)(itsColumns[whichColumn])).put \ (rownr, record.toArray##type (whichField)); \ } \ } while (0) void ROTableRow::putField (rownr_t rownr, const TableRecord& record, Int whichColumn, Int whichField) { switch (itsRecord->description().type(whichColumn)) { case TpBool: (*(ScalarColumn*)(itsColumns[whichColumn])).put (rownr, record.asBool (whichField)); break; case TpArrayBool: PUTFIELD_ARRAY(Bool); break; case TpUChar: (*(ScalarColumn*)(itsColumns[whichColumn])).put (rownr, record.asuChar (whichField)); break; case TpArrayUChar: PUTFIELD_ARRAY(uChar); break; case TpShort: (*(ScalarColumn*)(itsColumns[whichColumn])).put (rownr, record.asShort (whichField)); break; case TpArrayShort: PUTFIELD_ARRAY(Short); break; case TpInt: (*(ScalarColumn*)(itsColumns[whichColumn])).put (rownr, record.asInt (whichField)); break; case TpArrayInt: PUTFIELD_ARRAY(Int); break; case TpUInt: (*(ScalarColumn*)(itsColumns[whichColumn])).put (rownr, record.asuInt (whichField)); break; case TpArrayUInt: PUTFIELD_ARRAY(uInt); break; case TpInt64: (*(ScalarColumn*)(itsColumns[whichColumn])).put (rownr, record.asInt64 (whichField)); break; case TpArrayInt64: PUTFIELD_ARRAY(Int64); break; case TpFloat: (*(ScalarColumn*)(itsColumns[whichColumn])).put (rownr, record.asfloat (whichField)); break; case TpArrayFloat: PUTFIELD_ARRAY(Float); break; case TpDouble: (*(ScalarColumn*)(itsColumns[whichColumn])).put (rownr, record.asdouble (whichField)); break; case TpArrayDouble: PUTFIELD_ARRAY(Double); break; case TpComplex: (*(ScalarColumn*)(itsColumns[whichColumn])).put (rownr, record.asComplex (whichField)); break; case TpArrayComplex: PUTFIELD_ARRAY(Complex); break; case TpDComplex: (*(ScalarColumn*)(itsColumns[whichColumn])).put (rownr, record.asDComplex (whichField)); break; case TpArrayDComplex: PUTFIELD_ARRAY(DComplex); break; case TpString: (*(ScalarColumn*)(itsColumns[whichColumn])).put (rownr, record.asString (whichField)); break; case TpArrayString: PUTFIELD_ARRAY(String); break; case TpRecord: (*(ScalarColumn*)(itsColumns[whichColumn])).put (rownr, record.subRecord (whichField)); break; default: throw (TableError ("TableRow: unknown data type")); } } void ROTableRow::putRecord (rownr_t rownr) { const RecordDesc& desc = itsRecord->description(); uInt nrfield = desc.nfields(); for (uInt i=0; i*)(itsColumns[i])).put (rownr, (*(RecordFieldPtr*) itsFields[i]).get()); break; case TpArrayBool: (*(ArrayColumn*)(itsColumns[i])).put (rownr, (*(RecordFieldPtr >*) itsFields[i]).get()); break; case TpUChar: (*(ScalarColumn*)(itsColumns[i])).put (rownr, (*(RecordFieldPtr*) itsFields[i]).get()); break; case TpArrayUChar: (*(ArrayColumn*)(itsColumns[i])).put (rownr, (*(RecordFieldPtr >*) itsFields[i]).get()); break; case TpShort: (*(ScalarColumn*)(itsColumns[i])).put (rownr, (*(RecordFieldPtr*) itsFields[i]).get()); break; case TpArrayShort: (*(ArrayColumn*)(itsColumns[i])).put (rownr, (*(RecordFieldPtr >*) itsFields[i]).get()); break; case TpInt: (*(ScalarColumn*)(itsColumns[i])).put (rownr, (*(RecordFieldPtr*) itsFields[i]).get()); break; case TpArrayInt: (*(ArrayColumn*)(itsColumns[i])).put (rownr, (*(RecordFieldPtr >*) itsFields[i]).get()); break; case TpUInt: (*(ScalarColumn*)(itsColumns[i])).put (rownr, (*(RecordFieldPtr*) itsFields[i]).get()); break; case TpArrayUInt: (*(ArrayColumn*)(itsColumns[i])).put (rownr, (*(RecordFieldPtr >*) itsFields[i]).get()); break; case TpInt64: (*(ScalarColumn*)(itsColumns[i])).put (rownr, (*(RecordFieldPtr*) itsFields[i]).get()); break; case TpArrayInt64: (*(ArrayColumn*)(itsColumns[i])).put (rownr, (*(RecordFieldPtr >*) itsFields[i]).get()); break; case TpFloat: (*(ScalarColumn*)(itsColumns[i])).put (rownr, (*(RecordFieldPtr*) itsFields[i]).get()); break; case TpArrayFloat: (*(ArrayColumn*)(itsColumns[i])).put (rownr, (*(RecordFieldPtr >*) itsFields[i]).get()); break; case TpDouble: (*(ScalarColumn*)(itsColumns[i])).put (rownr, (*(RecordFieldPtr*) itsFields[i]).get()); break; case TpArrayDouble: (*(ArrayColumn*)(itsColumns[i])).put (rownr, (*(RecordFieldPtr >*) itsFields[i]).get()); break; case TpComplex: (*(ScalarColumn*)(itsColumns[i])).put (rownr, (*(RecordFieldPtr*) itsFields[i]).get()); break; case TpArrayComplex: (*(ArrayColumn*)(itsColumns[i])).put (rownr, (*(RecordFieldPtr >*) itsFields[i]).get()); break; case TpDComplex: (*(ScalarColumn*)(itsColumns[i])).put (rownr, (*(RecordFieldPtr*) itsFields[i]).get()); break; case TpArrayDComplex: (*(ArrayColumn*)(itsColumns[i])).put (rownr, (*(RecordFieldPtr >*) itsFields[i]).get()); break; case TpString: (*(ScalarColumn*)(itsColumns[i])).put (rownr, (*(RecordFieldPtr*) itsFields[i]).get()); break; case TpArrayString: (*(ArrayColumn*)(itsColumns[i])).put (rownr, (*(RecordFieldPtr >*) itsFields[i]).get()); break; case TpRecord: (*(ScalarColumn*)(itsColumns[i])).put (rownr, (*(RecordFieldPtr*) itsFields[i]).get()); break; default: throw (TableError ("TableRow: unknown data type")); } } // The values (might) have changed, which is not reflected in the // internal record. Be sure to reread when the same row is asked for. setReread (rownr); } TableRow::TableRow() : ROTableRow() {} TableRow::TableRow (const Table& table, Bool storedColumnsOnly) : ROTableRow() { if (! table.isWritable()) { throw (TableError ("TableRow cannot be used: table is not writable")); } create (table, storedColumnsOnly, True); } TableRow::TableRow (const Table& table, const Vector& columnNames, Bool exclude) : ROTableRow() { if (! table.isWritable()) { throw (TableError ("TableRow cannot be used: table is not writable")); } create (table, columnNames, exclude, True); } TableRow::TableRow (const TableRow& that) : ROTableRow() { copy (that); } TableRow::~TableRow() {} TableRow& TableRow::operator= (const TableRow& that) { copy (that); return *this; } void TableRow::putMatchingFields (rownr_t rownr, const TableRecord& record) { const RecordDesc& thisDesc = itsRecord->description(); const RecordDesc& thatDesc = record.description(); uInt nrfield = thatDesc.nfields(); Int field; for (uInt i=0; i= 0) { putField (rownr, record, field, i); } } // The values (might) have changed, which is not reflected in the // internal record. Be sure to reread when the same row is asked for. setReread (rownr); } void TableRow::put() { if (rowNumber() < 0) { throw (TableError ("TableRow::put(): no row read yet")); } put (rowNumber()); } void TableRow::put (rownr_t rownr, const TableRecord& record, Bool checkConformance) { if (checkConformance) { if (! namesConform (record)) { throw (TableError ("TableRow::put; names not conforming")); } } const RecordDesc& thisDesc = itsRecord->description(); uInt nrfield = thisDesc.nfields(); for (uInt i=0; i& valuesDefined, Bool checkConformance) { if (checkConformance) { if (! namesConform (record)) { throw (TableError ("TableRow::put; names not conforming")); } } const RecordDesc& thisDesc = itsRecord->description(); uInt nrfield = thisDesc.nfields(); AlwaysAssert (valuesDefined.nelements() >= nrfield, AipsError); for (uInt i=0; idescription(); const RecordDesc& thatDesc = that.description(); for (uInt i=0; i #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableColumn; //

        // Readonly access to a table row // // // // // //
      • Table //
      • TableRecord // // // This class provides easy access to the contents of a table, // one row at a time. 'Normal' access to a table is by columns, each of // which contains values of the same type. // A table row, by contrast, will be a collection // of heterogeneous data, similar to a C struct. For // this reason, the TableRow classes (ROTableRow and TableRow) are built // around and provide access to the class // TableRecord . // The TableRow delegates much of its behaviour to the TableRecord class. // For example: // // Table table ("some.table"); // ROTableRow row (table); // construct TableRow object // cout << row.record().description(); // show its description // // Get the values in row 17. // const TableRecord& record = row.get (17); // // column name is "Title", and automatically becomes the record // // key for this field of the record: // String row17title = record.asString ("Title"); // Int row17count = record.asInt ("Count"); // // The simplest constructor will include all columns in the TableRow object // (although columns with a non-standard data type will be excluded, // because they cannot be represented in a TableRecord). // However, it is possible to be more selective and to include only // some columns in the TableRow object. The various constructors show // how this can be done. //

        // It is possible to have multiple TableRow objects for the same table. // They can contain different columns or they can share columns. // //

        // On construction an internal TableRecord // object is created containing the required fields. The contents of this // record will be changed with each get call, but the structure of it is // fixed. This means that RORecordFieldPtr // objects can be constructed once and used many times. // This results in potentially faster access to the record, because it avoids // unnecessary name lookups. // // // // // Open the table as readonly and define a row object containing // // the given columns. // // Note that the function stringToVector is a very convenient // // way to construct a Vector. // // Show the description of the fields in the row. // Table table("Some.table"); // ROTableRow row (table, stringToVector("col1,col2,col3")); // cout << row.record().description(); // // Loop through all rows and get their values. // for (rownr_t i=0; i col1(row.record(), "col1"); // RORecordFieldPtr col2(row.record(), "col2"); // RORecordFieldPtr > col3(row.record(), "col3"); // for (rownr_t i=0; i // Please note that the TableRecord& returned by the get() function is the // same as returned by the record() function. Therefore the RORecordField // objects can be created in advance. // class ROTableRow { public: // Create a detached ROTableRow object. // This means that no Table, etc. is contained in it. // Function isAttached will return False for it. //
        // This constructor should normally not be used, because it does not // result in a valid object. It should only be used when really needed // (e.g. when an array of objects has to be used). ROTableRow(); // Create a ROTableRow object for the given Table. // Its TableRecord will contain all columns except columns with // datatype TpOther (i.e. non-standard data types). //
        // If the flag storedColumnsOnly is True, only the // columns actually stored by a storage manager will be selected. // This is useful when the contents of an entire row have to be copied. // Virtual columns are calculated on-the-fly (often using stored columns), // thus it makes no sense to copy their data. // // If the table contains columns with large arrays, it may // be better not to use this constructor. Each get will read in // all data in the row, thus also the large data array(s). // In that case it is better to use the constructor which // includes selected columns only. // explicit ROTableRow (const Table& table, Bool storedColumnsOnly = True); // Create a ROTableRow object for the given Table. // Its TableRecord will contain all columns given in the Vector. // An exception is thrown if an unknown column name is given. //
        // When exclude=True, all columns except the given columns are taken. // In that case an unknown name does not result in an exception. ROTableRow (const Table& table, const Vector& columnNames, Bool exclude = False); // Copy constructor (copy semantics). ROTableRow (const ROTableRow&); ~ROTableRow(); // Assignment (copy semantics). ROTableRow& operator= (const ROTableRow&); // Test if a Table is attached to this object. Bool isAttached() const; // Get the Table used for this object. const Table& table() const; // Get the record containing all fields. const TableRecord& record() const; // Get the number of the last row read. // -1 is returned when no Table is attached or no row has been read yet. Int64 rowNumber() const; // Get a vector consisting of all columns names. // This can, for instance, be used to construct a TableRow object // with the same columns in another table. Vector columnNames() const; // Get the values of all columns used from the given row. // When the given row number equals the current one, nothing // will be read unless the alwaysRead flag is set to True. //
        The TableRecord& returned is the same one as returned by the // record() function. So one can ignore the return value of get(). const TableRecord& get (rownr_t rownr, Bool alwaysRead = False) const; // Get the block telling for each column if its value in the row // was indefined in the table. // Note that array values might be undefined in the table, but in // the record they will be represented as empty arrays. const Block& getDefined() const; protected: // Copy that object to this object. // The writable flag determines if writable or readonly // TableColumn objects will be created. void copy (const ROTableRow& that); // Create the record, column, and field objects // for all columns in the table. // The writable flag determines if writable or readonly // TableColumn objects will be created. void create (const Table& table, Bool storedColumnsOnly, Bool writable); // Create the record, column, and field objects for the given columns. // The writable flag determines if writable or readonly // TableColumn objects will be created. void create (const Table& table, const Vector& columnNames, Bool exclude, Bool writable); // Put the values found in the internal TableRecord at the given row. // This is a helper function for class TableRow. void putRecord (rownr_t rownr); // Put a value in the given field in the TableRecord into the // given row and column. // This is a helper function for class TableRow. void putField (rownr_t rownr, const TableRecord& record, Int whichColumn, Int whichField); // Set the switch to reread when the current row has been put. void setReread (rownr_t rownr); //# The record of all fields. TableRecord* itsRecord; //# The table used. Table itsTable; //# The following block is actually a Block. //# However, using void* (and appropriate casts) saves on template //# instantiations. Block itsTabCols; //# The following block is actually a Block>. Block itsColumns; //# The following block is actually a block of RecordFieldPtr*. //# These are used for fast access to the record. Block itsFields; //# Block to tell if the corresponding column value is defined. mutable Block itsDefined; //# A cache for itsRecord.nfields() uInt itsNrused; //# The last rownr read (-1 is nothing read yet). mutable Int64 itsLastRow; //# A switch to indicate that the last row has to be reread. //# This is the case when it has been put after being read. mutable Bool itsReread; private: // Initialize the object. void init(); // Make a RecordDesc from the table with some excluded column names. void makeDescExclude (RecordDesc& description, const Vector& columnNames, Bool writable); // Add a column to the record. // When skipOther is True, columns with a non-standard data type // will be silently skipped. void addColumnToDesc (RecordDesc& description, const TableColumn& column, Bool skipOther); // Make the required objects. These are the TableRecord and for // each column a TableColumn and RecordFieldPtr. void makeObjects (const RecordDesc& description); // Delete all objects. void deleteObjects(); }; //

        // Read/write access to a table row // // // // // //
      • ROTableRow // // // The class TableRow is derived from ROTableRow and as an extra it // provides write-access to a row in a table. // With the put function, all values in the TableRecord object will // be put in the corresponding columns in the table row. // There is, however, an extra consideration: //
          //
        • Constructing a TableRow object fails if the table is not // writable. Non-writable columns will not be part of the object. // If an explicitly given column is non-writable, the construction // will also fail. //
        // There are effectively 3 ways of writing data. //
          //
        1. The function // // put (rownr, tableRecord); // // can be used to put all values from the given TableRecord, // which has to be conforming (i.e. matching order and names). // Optionally the conformance is checked. // This put function is capable of data type promotion. // For instance, if column COL1 is float, the corresponding // field in the TableRecord can be Int. //
        2. A faster way is using the functions record // and put. It is possible to use // RecordFieldPtr objects to get direct access to the // fields in the record (provided the structure of the record // is known). // E.g. // // TableRow row (someTable, stringToVector("col1,col2,col3")); // RecordFieldPtr col1(row.record(), "col1"); // RecordFieldPtr col2(row.record(), "col2"); // RecordFieldPtr > col3(row.record(), "col3"); // for (rownr_t i=0; i //
        3. //
        4. The function // // putMatchingFields (rownr, tableRecord); // // can be used to put some fields from the given TableRecord. // Only fields having a corresponding name in the TableRow object // will be put. Similar to the first way data type promotion will // be applied for numeric values. //
          E.g.: Suppose the TableRow object has columns A, C, and B, // and the given TableRecord has fields B, D, and C. Only fields B and C // will be put. As the example shows, the order of the fields is not // important. //
          // This way is (much) slower than the other 2, because a name // lookup is involved for each field. It can, however, be more // convenient to use. //
        //
        // // // // Open the new table (with 10 rows) and define a row object containing // // values from the given column. // // Note that the function stringToVector is a very convenient // // way to construct a Vector. // SetupNewTable newtab(tableDesc, Table::new); // Table table(newtab, 10); // TableRow row (table, stringToVector("col1,col2,col3,col4")); // // Loop through all rows and get their values. // for (rownr_t i=0; i // class TableRow : public ROTableRow { public: // Create a detached TableRow object. // This means that no Table, etc. is contained in it. // Function isAttached (in the base class) will return False for it. //
        // This constructor should normally not be used, because it does not // result in a valid object. It should only be used when really needed // (e.g. when an array of objects has to be used). TableRow(); // Create a TableRow object for the given Table. // Its TableRecord will contain all columns except columns with // datatype TpOther and columns which are not writable. //
        // If the flag storedColumnsOnly is True, only the // columns actually stored by a storage manager will be selected. // This is useful when the contents of an entire row have to be copied. // Virtual columns are calculated on-the-fly (often using stored columns), // thus it makes no sense to copy their data. // // If the table contains columns with large arrays, it may // be better not to use this constructor. Each get will read in // all data in the row, thus also the large data array(s). // In that case it is better to use the next constructor which // works selectively. // explicit TableRow (const Table& table, Bool storedColumnsOnly = True); // Create a TableRow object for the given Table. // Its TableRecord will contain all columns given in the Vector. // An exception is thrown if an unknown column name is given // or if a column is given which is not writable. //
        // When exclude=True, all columns except the given columns are taken. // In that case an unknown name does not result in an exception // and non-writable columns are simply skipped. TableRow (const Table& table, const Vector& columnNames, Bool exclude = False); // Copy constructor (copy semantics). TableRow (const TableRow&); ~TableRow(); // Assignment (copy semantics). TableRow& operator= (const TableRow&); // Get non-const access to the TableRecord in this object. // This can be used to change values in it which can thereafter // be put using the function put(rownr). // The returned TableRecord has a fixed structure, so it is // not possible to add or remove fields. It is only possible // to change values. // TableRecord& record(); // Put into the last row read. // An exception is thrown if no row has been read yet. // The values in the TableRecord contained in this object are put. // This TableRecord can be accessed and updated using the // function record. void put(); // Put into the given row. // The values in the TableRecord contained in this object are put. // This TableRecord can be accessed and updated using the // function record. void put (rownr_t rownr); // Put the values found in the TableRecord in the appropriate columns // in the given row. // The names and order of the fields in the TableRecord must conform // those of the description of the TableRow. The data types of numeric // values do not need to conform exactly; they can be promoted // (e.g. an Int value in the record may correspond to a float column). // If not conforming, an exception is thrown. // For performance reasons it is optional to check // the name order conformance. // // The valuesDefined block tells if the value in the // corresponding field in the record is actually defined. // If not, nothing will be written. // It is meant for array values which might be undefined in a table. // void put (rownr_t rownr, const TableRecord& record, Bool checkConformance = True); void put (rownr_t rownr, const TableRecord& record, const Block& valuesDefined, Bool checkConformance = True); // // Put the values found in the TableRecord. Only fields with a matching // name in the TableRow object will be put. // This makes it possible to put fields in a selective way. //
        E.g.: If the TableRow contains columns A and B, and the // record contains fields B and C, only field B will be put. //
        In principle the data types of the matching fields must match, // but data type promotion of numeric values will be applied. void putMatchingFields (rownr_t rownr, const TableRecord& record); private: // Check if the names of the given record match this row. Bool namesConform (const TableRecord& that) const; }; inline Bool ROTableRow::isAttached() const { return (itsRecord != 0); } inline const Table& ROTableRow::table() const { return itsTable; } inline Int64 ROTableRow::rowNumber() const { return itsLastRow; } inline const TableRecord& ROTableRow::record() const { return *itsRecord; } inline const Block& ROTableRow::getDefined() const { return itsDefined; } inline TableRecord& TableRow::record() { return *itsRecord; } inline void TableRow::put (rownr_t rownr) { putRecord (rownr); } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/TableRowProxy.cc000066400000000000000000000061071476623553700211040ustar00rootroot00000000000000//# TableRowProxy.cc: Holder of table rows for the table glish client. //# Copyright (C) 1994,1995,1996,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableRowProxy::TableRowProxy() : isWritable_p (False) {} TableRowProxy::TableRowProxy (const TableProxy& tablep, const Vector& columnNames, Bool exclude) : isWritable_p (tablep.isWritable()) { if (columnNames.nelements() == 0) { rorow_p = ROTableRow (tablep.table(), False); if (isWritable_p) { rwrow_p = TableRow (tablep.table(), False); } } else { rorow_p = ROTableRow (tablep.table(), columnNames, exclude); if (isWritable_p) { rwrow_p = TableRow (tablep.table(), columnNames, exclude); } } } TableRowProxy::TableRowProxy (const TableRowProxy& that) : isWritable_p (that.isWritable_p), rorow_p (that.rorow_p), rwrow_p (that.rwrow_p) {} TableRowProxy::~TableRowProxy() {} TableRowProxy& TableRowProxy::operator= (const TableRowProxy& that) { if (this != &that) { isWritable_p = that.isWritable_p; rorow_p = that.rorow_p; rwrow_p = that.rwrow_p; } return *this; } Bool TableRowProxy::isNull() const { return (rorow_p.isAttached() ? False : True); } Record TableRowProxy::get (Int64 rownr) const { return rorow_p.get(rownr, True).toRecord(); } void TableRowProxy::put (Int64 rownr, const Record& record, Bool matchingFields) { if (!isWritable_p) { throw TableError ("TableRowProxy: the given TableRow is not writable"); } TableRecord trec; trec.fromRecord (record); if (matchingFields) { rwrow_p.putMatchingFields (rownr, trec); } else { rwrow_p.put (rownr, trec); } } } //# NAMESPACE CASACORE - END casacore-3.7.1/tables/Tables/TableRowProxy.h000066400000000000000000000064141476623553700207470ustar00rootroot00000000000000//# TableRowProxy.h: Proxy for table row access //# Copyright (C) 1994,1995,1996,1999,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABLEROWPROXY_H #define TABLES_TABLEROWPROXY_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class TableProxy; class Record; // // Proxy for table row access. // // // // // //# Classes you should understand before using this one. //
      • class TableRow // // // TableRowProxy holds a TableRow object for the table // glish client. // // // TableRowProxy gives access to row-based table accessor functions. // It is primarily meant to be used in classes that wrap access to it // from scripting languages (like Glish and Python). // However, it can also be used directly from other C++ code. // // A TableRowProxy object is usually created by class // TableProxy. // class TableRowProxy { public: // Default constructor is only needed for the Block container. TableRowProxy(); // Construct for the given columns in the table. TableRowProxy (const TableProxy& table, const Vector& columnNames, Bool exclude); // Copy constructor (copy semantics). TableRowProxy (const TableRowProxy&); ~TableRowProxy(); // Assignment (copy semantics). TableRowProxy& operator= (const TableRowProxy&); // Test if the underlying TableRow object is invalid. Bool isNull() const; // Test if values can be written. Bool isWritable() const; // Get values for the given row. Record get (Int64 rownr) const; // Put values for the given row. // The given record has to conform the fields in the table row. void put (Int64 rownr, const Record& values, Bool matchingFields); private: Bool isWritable_p; ROTableRow rorow_p; TableRow rwrow_p; }; inline Bool TableRowProxy::isWritable() const { return isWritable_p; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/TableSyncData.cc000066400000000000000000000137231476623553700210030ustar00rootroot00000000000000//# TableSyncData.cc: Class to hold table synchronization data //# Copyright (C) 1997,1999,2001,2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN TableSyncData::TableSyncData() : itsNrrow (0), itsNrcolumn (-1), itsModifyCounter (0), itsTableChangeCounter (0), itsMemIO (new MemoryIO) { itsAipsIO.open (itsMemIO); } TableSyncData::~TableSyncData() { itsAipsIO.close(); } void TableSyncData::write (rownr_t nrrow, uInt nrcolumn, Bool tableChanged, const Block& dataManChanged) { // Increment change counter when the table has changed. Bool changed = False; itsNrrow = nrrow; itsNrcolumn = nrcolumn; if (tableChanged) { itsTableChangeCounter++; changed = True; } // Increment a counter when a data manager has changed. // Resize and initialize the block when needed. uInt ndmOld = itsDataManChangeCounter.nelements(); uInt ndmNew = dataManChanged.nelements(); if (ndmNew != ndmOld) { itsDataManChangeCounter.resize (ndmNew, True, True); for (uInt i=ndmOld; iclear(); if (itsNrrow > DataManager::MAXROWNR32) { itsAipsIO.putstart ("sync", 2); itsAipsIO << itsNrrow; } else { itsAipsIO.putstart ("sync", 1); itsAipsIO << uInt(itsNrrow); } itsAipsIO << itsNrcolumn; itsAipsIO << itsModifyCounter; if (itsNrcolumn >= 0) { itsAipsIO << itsTableChangeCounter; itsAipsIO << itsDataManChangeCounter; } itsAipsIO.putend(); } void TableSyncData::write (rownr_t nrrow) { itsModifyCounter++; itsNrrow = nrrow; itsNrcolumn = -1; // Now write the data into the memoryIO object. // Use 32-bit for the row number if it fits. // First clear it. itsMemIO->clear(); if (itsNrrow > DataManager::MAXROWNR32) { itsAipsIO.putstart ("sync", 2); itsAipsIO << itsNrrow; } else { itsAipsIO.putstart ("sync", 1); itsAipsIO << uInt(itsNrrow); } itsAipsIO << itsNrcolumn; itsAipsIO << itsModifyCounter; itsAipsIO.putend(); } Bool TableSyncData::read (rownr_t& nrrow, uInt& nrcolumn, Bool& tableChanged, Block& dataManChanged) { // Read the data into the memoryIO object. // When no columns, don't read the remaining part (then it is used // by an external filler). Int nrcol = -1; if (itsMemIO->length() > 0) { uint version = itsAipsIO.getstart ("sync"); if (version > 2) { throw TableError ("TableSyncData version " + String::toString(version) + " not supported by this version of Casacore"); } if (version == 1) { uInt n; itsAipsIO >> n; nrrow = n; } else { itsAipsIO >> nrrow; } itsAipsIO >> nrcol; itsAipsIO >> itsModifyCounter; } if (nrcol < 0) { tableChanged = True; dataManChanged.set (True); if (itsMemIO->length() > 0) { itsAipsIO.getend(); return True; // not empty } nrcolumn = 0; return False; // empty MemoryIO object } nrcolumn = nrcol; // The table has changed when the change counter has changed. uInt tableChangeCounter; Block dataManChangeCounter; itsAipsIO >> tableChangeCounter; itsAipsIO >> dataManChangeCounter; itsAipsIO.getend(); tableChanged = (tableChangeCounter != itsTableChangeCounter); itsTableChangeCounter = tableChangeCounter; // A data manager has changed when its change counter has changed. // Increment a change counter when a data manager has changed. // Resize and initialize the array when needed. uInt ndmOld = itsDataManChangeCounter.nelements(); uInt ndmNew = dataManChangeCounter.nelements(); dataManChanged.resize (ndmNew, True, False); dataManChanged.set (False); if (ndmNew != ndmOld) { itsDataManChangeCounter.resize (ndmNew, True, True); for (uInt i=ndmOld; i #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // // Class to hold table synchronization data. // // // // // //
      • class TableLockData // // // This class keeps counters to synchronize the table data when a table // is locked or unlocked. //
        // A few counters are kept by this class: //
          //
        • The numbers of rows in the table. //
        • The number of columns in the table. //
        • The table change counter. //
        • A change counter per data manager. //
        // When a lock on the table is acquired, it reads the sync data from the // lock file and determines if anything has changed. If so, the necessary // steps are taken to reread the table data when needed. //
        // When a lock on the table is released, it updates and writes the sync data // which tells if table data have changed. //

        // This class can also be used for the synchronization of tables and // external fillers (see class // ExternalLockSync). For this // purpose it is sufficient to store the number of rows. // class TableSyncData { public: TableSyncData(); ~TableSyncData(); // Copy constructor is forbidden. TableSyncData (const TableSyncData& that) = delete; // Assignment is forbidden. TableSyncData& operator= (const TableSyncData& that) = delete; // Update the synchronization data and write it into the MemoryIO object. // This function is called when a table flush is done to reflect // if anything has changed compared to the previous flush. void write (rownr_t nrrow, uInt nrcolumn, Bool tableChanged, const Block& dataManChanged); // Update the synchronization data and write it into the MemoryIO object. // This function should be used by an external filler when it flushes // its data. void write (rownr_t nrrow); // Read the synchronization data from the MemoryIO object. // This function is called when a lock is acquired to see if // table data has to be reread. //
        It returns False when the MemoryIO object is empty. Bool read (rownr_t& nrrow, uInt& nrcolumn, Bool& tableChanged, Block& dataManChanged); // Get the MemoryIO object. // This is used to let LockFile read or write the // synchronization data into it. MemoryIO& memoryIO(); // Get the modify counter. uInt getModifyCounter() const; private: //# Member variables. rownr_t itsNrrow; Int itsNrcolumn; uInt itsModifyCounter; uInt itsTableChangeCounter; Block itsDataManChangeCounter; std::shared_ptr itsMemIO; AipsIO itsAipsIO; }; inline MemoryIO& TableSyncData::memoryIO() { return *itsMemIO; } inline uInt TableSyncData::getModifyCounter() const { return itsModifyCounter; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/TableTrace.cc000066400000000000000000000256651476623553700203430ustar00rootroot00000000000000//# TableTrace.cc: Class with static functions for tracing column IO //# Copyright (C) 2014 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN // Define the statics. std::once_flag TableTrace::theirCallOnceFlag; std::mutex TableTrace::theirMutex; std::ofstream TableTrace::theirTraceFile; std::ostream* TableTrace::theirStream = 0; int TableTrace::theirDoTrace = 0; int TableTrace::theirOper = 0; int TableTrace::theirColType = 0; std::vector TableTrace::theirColumns; std::vector TableTrace::theirTables; int TableTrace::traceTable (const String& tableName, char oper) { // Open trace file if not done yet. std::call_once(theirCallOnceFlag, initTracing); int tabid = -1; if (theirDoTrace > 0) { std::lock_guard locker(theirMutex); // Table should not be found, but who knows ... tabid = findTable (tableName); int id = tabid; if (tabid < 0) { // Find a free table entry. If none, append. tabid = findTable (String()); if (tabid < 0) { tabid = theirTables.size(); theirTables.push_back (tableName); } else { theirTables[tabid] = tableName; } } writeTraceFirst (tabid, tableName, oper); if (id >= 0) { *theirStream << "**ERROR** table already in use"; } *theirStream << endl; } return tabid; } void TableTrace::traceClose (const String& tableName) { std::call_once(theirCallOnceFlag, initTracing); if (theirDoTrace > 0) { std::lock_guard locker(theirMutex); int tabid = findTable (tableName); writeTraceFirst (tabid, tableName, 'c'); if (tabid < 0) { *theirStream << "**ERROR** unknown table"; } else { // Free entry. theirTables[tabid] = String(); } *theirStream << endl; } } void TableTrace::traceFile (int tabid, const String& oper) { std::call_once(theirCallOnceFlag, initTracing); if (theirDoTrace > 0) { writeTraceFirst (tabid, '*'+oper+'*', 't'); *theirStream << endl; } } void TableTrace::traceRefTable (const String& parentName, char oper) { std::call_once(theirCallOnceFlag, initTracing); if (theirDoTrace > 1) { int tabid = findTable (parentName); writeTraceFirst (tabid, "*reftable*", oper); *theirStream << endl; } } int TableTrace::traceColumn (const ColumnDesc& cd) { std::call_once(theirCallOnceFlag, initTracing); int traceCol = 0; if (theirOper > 0) { // First test if all scalar, array, or record columns are traced. if ((cd.isScalar() && (theirColType&SCALAR) != 0) || (cd.isArray() && (theirColType&ARRAY) != 0) || ((theirColType&RECORD) != 0)) { traceCol = theirOper; } else { // Otherwise see if this column is traced. for (size_t i=0; i::find (fname, "table.trace.filename", ""); if (! fname.empty()) { if (fname == "stdout") { theirStream = &std::cout; } else if (fname == "stderr") { theirStream = &std::cerr; } else { String expName = Path(fname).expandedName(); theirTraceFile.open (fname.c_str()); if (! theirTraceFile) { throw TableError ("Could not open table column trace file " + fname); } theirStream = &theirTraceFile; } *theirStream << "# time oper tabid name row(s) shape blc/trc/inc" << endl; *theirStream << "# Note: shapes are in Fortran order" << endl << endl; theirDoTrace = 1; initOper(); initColumn(); } } void TableTrace::initOper() { // Get the operations to trace. String operStr; AipsrcValue::find (operStr, "table.trace.operation", ""); if (! operStr.empty()) { operStr.downcase(); for (uInt i=0; i::find (typeStr, "table.trace.columntype", ""); String colStr; AipsrcValue::find (colStr, "table.trace.column", ""); if (! typeStr.empty()) { typeStr.downcase(); for (uInt i=0; i cols = stringToVector (colStr, ','); theirColumns.reserve (cols.size()); for (uInt i=0; i #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations. class ColumnDesc; class RefRows; class IPosition; //

        // Class with static functions for tracing column IO // // // // // // This class contains some static functions to enable table and column tracing. // It maintains a map of table name to table-id. //
        // The following aipsrc variables variables determine if tracing will be done, // and if so, which columns and operations will be traced. //
          //
        • table.trace.filename gives the name of the file in which // the trace will be written. If empty (default), no tracing will be done. // If 'stdout' is given, tracing is done to stdout. // If 'stderr' is given, tracing is done to stderr. //
        • table.trace.operation gives the operation to trace. // be traced. It can be one or more of: //
          s: creation of RefTable (selection/sort/iter) //
          r: reads //
          w: writes //
          The default is ''. Note that opening and closing a PlainTable // are always traced. //
        • table.trace.columntype gives the types of columns to trace // for read and/or write. // It can be one or more of: //
          s: scalar columns //
          a: array columns //
          r: record columns //
          The default is ''. //
        • table.trace.column gives names of additional columns to // trace for read and/or write. // The names are separated by commas without any whitespace. // Each name can be a glob-like pattern. //
          The default is ''. //
        // If both table.trace.columntype and table.trace.column // have an empty value, all array columns are traced. class TableTrace { public: enum ColType { SCALAR = 1, ARRAY = 2, RECORD = 4 }; enum Oper { READ = 1, WRITE = 2 }; // Does the given column have to be traced for read and/or write? // bit 0 set means read tracing; bit 1 write tracing. static int traceColumn (const ColumnDesc&); // If needed, write a trace message for table open or create. // It adds the table to the map and returns the table-id. static int traceTable (const String& tableName, char oper); // If needed, trace closing a table. // It removes the table from the map. static void traceClose (const String& tableName); // If needed, trace an operation on a table. static void traceFile (int tabid, const String& oper); // If needed, write a trace message for reftable open, create, or close. static void traceRefTable (const String& parentName, char oper); // If needed, write a trace message // Write a trace message for a scalar column. static void trace (int tabid, const String& columnName, char oper); // Write a trace message for a scalar row. static void trace (int tabid, const String& columnName, char oper, Int64 row); // Write a trace message for ranges of scalar rows. static void trace (int tabid, const String& columnName, char oper, const RefRows& rownrs); // Write a trace message for an array column. static void trace (int tabid, const String& columnName, char oper, const IPosition& shape); // Write a trace message for an array row. static void trace (int tabid, const String& columnName, char oper, Int64 row, const IPosition& shape); // Write a trace message for ranges of array rows. static void trace (int tabid, const String& columnName, char oper, const RefRows& rownrs, const IPosition& shape); // Write a trace message for an array column slice. static void trace (int tabid, const String& columnName, char oper, const IPosition& shape, const IPosition& blc, const IPosition& trc, const IPosition& inc); // Write a trace message for an array row slice. static void trace (int tabid, const String& columnName, char oper, Int64 row, const IPosition& shape, const IPosition& blc, const IPosition& trc, const IPosition& inc); // Write a trace message for ranges of array rows slice. static void trace (int tabid, const String& columnName, char oper, const RefRows& rownrs, const IPosition& shape, const IPosition& blc, const IPosition& trc, const IPosition& inc); private: // Initialize the tracing mechanism which should be done only once. static void initTracing(); // always called using theirCallOnce static void initOper(); static void initColumn(); // Find the table name in the vector. -1 is returned if not found. static int findTable (const String& name); // Write the first part of the trace message. static void writeTraceFirst (int tabid, const String& name, char oper); // Write the RefRows as vector of rows or slices. static void writeRefRows (const RefRows& rownrs); // Write the blc, trc, and inc of an array slice. static void writeSlice (const IPosition& blc, const IPosition& trc, const IPosition& inc); //# Data members static std::once_flag theirCallOnceFlag; //# for thread-safe lazy init static std::mutex theirMutex; static std::ofstream theirTraceFile; static std::ostream* theirStream; static int theirDoTrace; //# 0=init -1=no 1=yes 2=reftable static int theirOper; //# 1=rtrace 2=wtrace static int theirColType; //# 1=scalar 2=array 4=record static std::vector theirColumns; static std::vector theirTables; }; } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/TableUtil.cc000066400000000000000000000306401476623553700202070ustar00rootroot00000000000000//# TableUtil.cc: Utility functions for tables //# Copyright (C) 2022 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have receied a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include namespace casacore { namespace TableUtil { Table openTable (const String& tableName, Table::TableOption option, const TSMOption& tsmOption) { return openTable (tableName, TableLock(), option, tsmOption); } Table openTable (const String& tableName, const TableLock& lockOpt, Table::TableOption tabOpt, const TSMOption& tsmOpt) { // See if the table can be opened as such. if (Table::isReadable(tableName)) { return Table(tableName, lockOpt, tabOpt, tsmOpt); } // Try to find and open the last subtable by splitting at :: std::pair t(findParentTable(tableName, lockOpt, tabOpt, tsmOpt)); if (t.first.isNull()) { // Note that tableName was already tried to be opened at beginning. throw TableError ("Table " + tableName + " does not exist"); } // Find the last subtable in the parent. if (! t.first.keywordSet().isDefined(t.second)) { throw TableError ("Table name " + tableName + " is invalid (" + "subtable " + t.second + " is unknown)"); } return t.first.keywordSet().asTable (t.second); } Table createTable (const String& tableName, const TableDesc& desc, Table::TableOption tabOpt, Table::TableType tabType, const StorageOption& storageOption, const Record& dmInfo, const TableLock& lockOptions, rownr_t nrrow, Bool initialize, Table::EndianFormat endian, const TSMOption& tsmOpt) { // Find and open the one but last subtable and get the name of the last one. // An empty name results in a Scratch table. std::pair t; if (! tableName.empty()) { t = findParentTable (tableName); } if (! t.second.empty()) { return createSubTable (t.first, t.second, desc, tabOpt, storageOption, dmInfo, lockOptions, nrrow, initialize, endian, tsmOpt); } else { // No subtable given, so create a main table. SetupNewTable newtab(tableName, desc, tableName.empty() ? Table::Scratch : tabOpt, storageOption); newtab.bindCreate (dmInfo); // Create the table. return Table(newtab, tabType, lockOptions, nrrow, initialize, endian, tsmOpt); } } Table createSubTable (Table& parent, const String& subName, const TableDesc& desc, Table::TableOption tabOpt, const StorageOption& storageOption, const Record& dmInfo, const TableLock& lockOptions, rownr_t nrrow, Bool initialize, Table::EndianFormat endian, const TSMOption& tsmOpt) { // See if the subtable and its keyword already exist. Int inx = parent.keywordSet().fieldNumber(subName); if (inx >= 0) { if (parent.keywordSet().type(inx) != TpTable) { throw TableError("Subtable " + subName + " cannot be created in " + parent.tableName() + "; a keyword with that name already exists"); } if (tabOpt == Table::NewNoReplace) { throw TableError("Subtable " + parent.tableName() + '/' + subName + " already exists"); } // Remove the subtable. deleteSubTable (parent, subName); } // Setup creation of the subtable and attach data managers. SetupNewTable newtab(parent.tableName() + '/' + subName, desc, tabOpt, storageOption); newtab.bindCreate (dmInfo); // Create the table and define the keyword for it in the parent. Table subtab(newtab, lockOptions, nrrow, initialize, endian, tsmOpt); parent.reopenRW(); parent.rwKeywordSet().defineTable (subName, subtab); return subtab; } Bool canDeleteTable (const String& tableName, Bool checkSubTables) { String message; return canDeleteTable (message, tableName, checkSubTables); } Bool canDeleteTable (String& message, const String& tableName, Bool checkSubTables, Bool splitColons) { if (splitColons) { std::pair t = findParentTable (tableName); if (! t.first.isNull()) { return canDeleteSubTable (message, t.first, t.second, checkSubTables); } } String tabName = Path(tableName).absoluteName(); if (! Table::isWritable (tabName)) { message = "table is not writable"; return False; } if (Table::isOpened (tabName)) { message = "table is still open in this process"; return False; } Table table(tabName); if (table.isMultiUsed()) { message = "table is still open in another process"; return False; } if (checkSubTables && table.isMultiUsed(True)) { message = "a subtable of the table is still open in another process"; return False; } return True; } Bool canDeleteSubTable (String& message, const Table& parent, const String& subtableName, Bool checkSubTables) { // Get the full table name of the subtable. // Note: the temporary Table object is deleted before canDeleteTable. // Otherwise isOpened() used internally would be true. const String fullName (parent.keywordSet().asTable(subtableName).tableName()); return canDeleteTable (message, fullName, checkSubTables, False); } void deleteTable (const String& tableName, Bool checkSubTables) { // Check that the name is not empty, because making it absolute results in / if (tableName.empty()) { throw TableError ("Empty string provided for tableName; will not attempt delete."); } // See if the name represents a table. if (! Table::isReadable(tableName)) { // See if the name contains subtable names using :: std::pair t = findParentTable (tableName); if (! t.first.isNull()) { deleteSubTable (t.first, t.second, checkSubTables); return; } } // Delete the table (which fails if it is not a table or still in use). String tabName = Path(tableName).absoluteName(); String message; if (! canDeleteTable (message, tabName, checkSubTables)) { throw (TableError ("Table " + tabName + " cannot be deleted: " + message)); } Table table(tabName, Table::Delete); } void deleteSubTable (Table& parent, const String& subtableName, Bool checkSubTables) { String message; if (! canDeleteSubTable (message, parent, subtableName, checkSubTables)) { throw (TableError ("Subtable " + subtableName + " in " + parent.tableName() + " cannot be deleted: " + message)); } Table subtab = parent.keywordSet().asTable(subtableName); subtab.markForDelete(); // If there, remove the keyword referring the subtable. Int inx = parent.keywordSet().fieldNumber(subtableName); if (inx >= 0) { if (parent.keywordSet().type(inx) == TpTable) { parent.reopenRW(); parent.rwKeywordSet().removeField (subtableName); } } } //# The logic is similar to that in Table::open. rownr_t getLayout (TableDesc& desc, const String& tableName) { rownr_t nrow; uInt format; String tp; AipsIO ios (Table::fileName(getFullName(tableName))); uInt version = ios.getstart ("Table"); if (version > 3) { throw TableError ("Table version " + String::toString(version) + " not supported by TableUtil in this version of Casacore"); } if (version > 2) { ios >> nrow; } else { uInt n; ios >> n; nrow = n; } ios >> format; ios >> tp; if (tp == "PlainTable") { PlainTable::getLayout (desc, ios); } else if (tp == "RefTable") { RefTable::getLayout (desc, ios); } else if (tp == "ConcatTable") { ConcatTable::getLayout (desc, ios); } else { throw (TableInternalError ("TableUtil::getLayout: unknown table kind " + tp)); } ios.close(); return nrow; } TableInfo tableInfo (const String& tableName) { return BaseTable::tableInfo (getFullName(tableName)); } String getFullName (const String& tableName) { // See if a subtable is given using ::. String tabName; std::pair t = findParentTable (tableName); if (t.first.isNull()) { tabName = Path(tableName).absoluteName(); } else { tabName = t.first.keywordSet().tableAttributes(t.second).name(); } return tabName; } // Return Table and name of last part. std::pair findParentTable (const String& fullName, const TableLock& lockOpt, Table::TableOption tabOpt, const TSMOption& tsmOpt) { Table tab; String lastPart, msg; // Split the name on :: to get the main and subtable names. const Vector names = stringToVector(fullName, std::regex("::")); AlwaysAssert (!names.empty(), AipsError); // Check that no empty parts are given. if (anyEQ (names, String())) { msg = "empty name part given"; } else if (names.size() > 1) { // Subtable given; check if previous parts exist. // First check if main table exists. if (! Table::isReadable (names[0])) { msg = "main table " + names[0] + " does not exist"; } else { tab = Table(names[0], lockOpt, tabOpt, tsmOpt); // Get name of last subtable. lastPart = names[names.size()-1]; // Check if all subtables exist, except last one. for (uInt i=1; i #include #include #include #include #include namespace casacore { // The TableUtil namespace contains several convenience functions operating // on Table objects. They make it very convenient to open, close or delete // main tables and subtables. //

        // The function openTable makes it possible to open a subtable // of a table in a convenient way, even if the table is only a reference // to another table (e.g., a selection). The name can be given with colons as // 'maintab::subtab1::subtab2' meaning that subtab2 is opened and returned. // Of course, it can also be used to open a main table such as 'my.tab'. // // Similar to openTable, the function createTable // can be used to create a (sub)table, possibly using the :: notation. //
        deleteTable is similar to delete a (sub)table. namespace TableUtil { // Try to open a table. The name of the table can contain subtable names // using :: as separator. In this way it is possible to directly open a // subtable of a RefTable or ConcatTable, which is not possible if the // table name is specified with slashes. //
        The open process is as follows: //

          //
        • It is tried to open the table with the given name. //
        • If unsuccessful, the name is split into its parts using :: // The first part is the main table which will be opened temporarily. // The other parts are the successive subtable names (usually one). // Each subtable is opened by looking it up in the keywords of the // table above. The final subtable is returned. //
        //
        An exception is thrown if the table cannot be opened. // // Open the ANTENNA subtable of an MS which might be a selection of // a real MS. // // Table tab(Table::openTable ("sel.ms::ANTENNA"); // // // Table openTable (const String& tableName, Table::TableOption = Table::Old, const TSMOption& = TSMOption()); Table openTable (const String& tableName, const TableLock& lockOptions, Table::TableOption = Table::Old, const TSMOption& = TSMOption()); // // Create a table with the given name and description. // Datamanager information can be given in the Record. // The table name can be given with the :: notation meaning that a subtable // of the previous part is created. Depending on the TableOption, that subtable // can or cannot exist yet. // It defines the subtable keyword in the parent table. //
        An exception is thrown if one of the parts cannot be opened. // // Create the ANT subtable of an MS with some description and create the // table keyword ANT in sel.ms referring to the subtable. // It is replaced if already existing (not if TableOption::NewNoReplace is given). // // Table tab(Table::createTable ("sel.ms::ANT", someDesc, TableOption::New)); // // Table createTable (const String& tableName, const TableDesc&, Table::TableOption, Table::TableType = Table::Plain, const StorageOption& = StorageOption(), const Record& dmInfo = Record(), const TableLock& lockOptions = TableLock(), rownr_t nrrow = 0, Bool initialize = False, Table::EndianFormat = Table::AipsrcEndian, const TSMOption& = TSMOption()); Table createSubTable (Table& parent, const String& subtableName, const TableDesc& desc, Table::TableOption, const StorageOption& = StorageOption(), const Record& dmInfo = Record(), const TableLock& lockOptions = TableLock(), rownr_t nrrow = 0, Bool initialize = False, Table::EndianFormat = Table::AipsrcEndian, const TSMOption& = TSMOption()); // Can the table be deleted? // If true, function deleteTable can safely be called. // If not, message contains the reason why (e.g. 'table is not writable'). // It checks if the table is writable, is not open in this process // and is not open in another process. // If splitColons=True the table name can contain :: to // denote subtables. //
        If checkSubTables is set, it also checks if // a subtable is not open in another process. //
        canDeleteSubTable can be used to check a subtable of the // given parent. // Bool canDeleteTable (const String& tableName, Bool checkSubTables=False); Bool canDeleteTable (String& message, const String& tableName, Bool checkSubTables=False, Bool splitColons=True); Bool canDeleteSubTable (String& message, const Table& parent, const String& subtableName, Bool checkSubTables=False); // // Delete the table. // An exception is thrown if the table cannot be deleted because // its is not writable or because it is still open in this or // another process. //
        If checkSubTables is set, it is also checked if // a subtable is used in another process. //
        deleteSubTable can be used to delete a subtable of the // given parent. void deleteTable (const String& tableName, Bool checkSubTables=False); void deleteSubTable (Table& parent, const String& subtableName, Bool checkSubTables = False); // Return the layout of a table (i.e. description and #rows). // This function has the advantage that only the minimal amount of // information required is read from the table, thus it is // faster than a normal table open. The table name can be a subtable using ::. //
        The number of rows is returned. The description of the table // is stored in desc (its contents will be overwritten). //
        An exception is thrown if the table does not exist. rownr_t getLayout (TableDesc& desc, const String& tableName); // Get the table info of the table with the given name. // An empty object is returned if the table is unknown. // The table name can be a subtable using ::. TableInfo tableInfo (const String& tableName); // Get the full name (absolute path) of the given table name, which can // be a subtable specification using ::. String getFullName (const String& tableName); // Find the parent table of the last subtable in a table name containing // :: to indicate subtables. // It returns the Table object of that parent table and the name of // the last subtable. An empty Table is returned if the table name does // not contain subtable names. // In case of an error, an exception is thrown. std::pair findParentTable (const String& fullName, const TableLock& lockOptions=TableLock(), Table::TableOption option=Table::Old, const TSMOption& tsmOption=TSMOption()); } //# NAMESPACE TableUtil - END } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/TableVector.h000066400000000000000000000227721476623553700204050ustar00rootroot00000000000000//# TableVector.h: Templated read/write table column vectors //# Copyright (C) 1994,1995,1996,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABLEVECTOR_H #define TABLES_TABLEVECTOR_H //# Includes #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Forward Declarations class Table; class TableColumn; template class TableVectorHelper; class String; // // Templated readonly table column vectors // // // // // //# Classes you should understand before using this one. //
      • Vector //
      • Table // // // TableVector allows to operate on a column in a readonly table as a vector. // // // A TableVector object is a read/write view of data in a Table. // This means that the vector data can be changed if the underlying column // is writable. // // Table vectors can be used in the same way as the normal vectors. // They allow to handle a column in a table as a vector. // Many mathematical and logical operations are defined for them // in TabVecMath.h and TabVecLogic.h. In fact, constructors exist // to convert a TableColumn or a Vector object to a TableVector, // so they can often directly be used in a table vector expression. // There are 2 kinds of table vectors: //
          //
        • A table vector representing a scalar column in a table. // The data types of the vector and the column must conform. //
        • A temporary vector, which is held in memory. // These are usually the result of operations on table vectors. //
        // // TableVector is implemented by referencing the counted TabVecRep object. // A default constructor is defined to allow construction of an array // of TableVector objects. However, it constructs an object not // referencing anything. Functions like operator() will fail (i.e. result // in a segmentation fault) when used on such objects. The functions // isNull and throwIfNull can be used to test on this. //
        // // // // Create a table vector for column COL1. // Table tab ("Table.data"); // TableVector tabvec(tab, "COL1"); // // Multiply it by a constant. // // The result has to be stored in a TableVector, // // since a TableVector cannot be written. // TableVector temp = 2 * tabvec; // // // // It is very useful to be able to handle a column as a vector. // To handle a column in a readonly table, a TableVector class // is needed, otherwise output operations could not be forbidden. // // //# A List of bugs, limitations, extensions or planned refinements. //
      • derive from Lattice one day //
      • support slicing //
      • support table array columns //
      • do we ever need Row vectors? // template class TableVector { public: // The default constructor creates a null table vector. // This does not contain an actual vector and cannot be used until // it references an actual vector (using function reference). // Its sole purpose is to be able to construct an array of TableVectors. // Note that operator(), etc. will cause a segmentation fault // when operating on a null object. It was felt it was too expensive // to test on null over and over again. The user should use the isNull // or throwIfNull function in case of doubt. TableVector(); // Create a read/write table vector from the given table column name. // Only scalar columns are supported. TableVector (const Table&, const String& columnName); // Create a read/write table vector from the given table column. // Only scalar columns are supported. // This constructor converts a TableColumn to a TableVector and // allows the use of TableColumn objects in table vector expressions. TableVector (const TableColumn& column); // Create a table vector from another one (reference semantics) TableVector (const TableVector&); // Create a table vector containing the given Vector (reference semantics). // This constructor converts a Vector to a TableVector and // allows the use of Vector objects in table vector expressions. TableVector (const Vector&); // Create a table vector containing a Vector with the given length. TableVector (rownr_t leng); // Destruct the object. ~TableVector(); // Assign a table vector to another one (copy semantics). // The vectors must have equal length. TableVector& operator= (const TableVector&); // Test if the table vector is null, i.e. has no actual vector. // This is the case if the default constructor has been used. Bool isNull() const; // Throw an exception if the table vector is null, i.e. // if function isNull() is true. void throwIfNull() const; // Make a reference to the table vector of the other TableVector. // It will replace an already existing reference. // It handles null objects correctly. void reference (const TableVector&); // Make a (normal) Vector from a TableVector (copy semantics). Vector makeVector() const; // Get the value of a single pixel. T operator() (rownr_t index) const; //# Get a slice. //# TableVector operator() (const NSlice&) const; // Set all elements to a value. // TableVector& operator= (const T&); void set (const T& value); // // Put a value into a single pixel. //
        tabvec(i) = value; void set (rownr_t index, const T& value); // Get nr of dimensions (is always 1). uInt ndim() const; // Get nr of elements (ie. vector length). rownr_t nelements() const; // Test if the shape of the given table vector conforms. Bool conform (const TableVector&) const; // Test if the shape of the given vector conforms. Bool conform (const Vector&) const; // Test if internal state is correct. Bool ok() const; protected: TabVecRep* tabVecPtr_p; // Destruct the object. It decreases the reference count in the // underlying object. void destruct(); public: // Return the TabVecRep reference. TabVecRep& tabVec(); const TabVecRep& tabVec() const; // Create a TableVector from a TabVecRep as result of an operation. TableVector (TabVecRep&); }; template inline Bool TableVector::isNull() const { return (tabVecPtr_p == 0 ? True : False); } template inline uInt TableVector::ndim () const { return tabVecPtr_p->ndim(); } template inline rownr_t TableVector::nelements() const { return tabVecPtr_p->nelements(); } //# Check if 2 table vectors are conformant. template inline Bool TableVector::conform (const TableVector& vec) const { return tabVecPtr_p->conform (*vec.tabVecPtr_p); } template inline Bool TableVector::conform (const Vector& vec) const { return tabVecPtr_p->conform (vec); } //# Get the ith pixel. template inline T TableVector::operator() (rownr_t index) const { return tabVecPtr_p->value (index); } //# Return the TabVecRep (for TabVecMath and Logic). template inline const TabVecRep& TableVector::tabVec() const { return *tabVecPtr_p; } template inline TabVecRep& TableVector::tabVec() { return *tabVecPtr_p; } //# Create a new object as a result of an addition, etc.. template inline TableVector::TableVector (TabVecRep& vec) { tabVecPtr_p = vec.link(); } //# Assign a table vector to this one. template inline TableVector& TableVector::operator= (const TableVector& that) { tabVecPtr_p->assign (that.tabVec()); return *this; } template inline void TableVector::set (rownr_t index, const T& value) { tabVecPtr_p->putVal (index, value); } template inline void TableVector::set (const T& value) { tabVecPtr_p->set (value); } template inline TableVector& TableVector::operator= (const T& value) { tabVecPtr_p->set (value); return *this; } } //# NAMESPACE CASACORE - END //# Make old name ROTableVector still available. #define ROTableVector TableVector #ifndef CASACORE_NO_AUTO_TEMPLATES #include #endif //# CASACORE_NO_AUTO_TEMPLATES #endif casacore-3.7.1/tables/Tables/TableVector.tcc000066400000000000000000000070551476623553700207240ustar00rootroot00000000000000//# TableVector.cc: Templated readonly table column vectors //# Copyright (C) 1994,1995,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #ifndef TABLES_TABLEVECTOR_TCC #define TABLES_TABLEVECTOR_TCC #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN //# Construct an empty table vector. template TableVector::TableVector() : tabVecPtr_p(0) {} //# Construct from a vector (reference semantics). template TableVector::TableVector (const Vector& vec) { tabVecPtr_p = new TabVecTemp(vec); tabVecPtr_p->link(); } //# Construct a vector with a given length. template TableVector::TableVector (rownr_t leng) { tabVecPtr_p = new TabVecTemp(leng); tabVecPtr_p->link(); } //# Copy constructor (reference semantics). template TableVector::TableVector (const TableVector& that) : tabVecPtr_p (that.tabVecPtr_p) { if (tabVecPtr_p != 0) { tabVecPtr_p->link(); } } //# Construct a readonly table column vector. template TableVector::TableVector (const Table& tab, const String& columnName) { tabVecPtr_p = new TabVecScaCol (TableColumn (tab, columnName)); tabVecPtr_p->link(); } template TableVector::TableVector (const TableColumn& column) { tabVecPtr_p = new TabVecScaCol (column); tabVecPtr_p->link(); } template TableVector::~TableVector() { destruct(); } template void TableVector::destruct () { if (tabVecPtr_p->unlink() == 0) { delete tabVecPtr_p; } } template void TableVector::throwIfNull() const { if (isNull()) { throw (TableInvOper ("TableVector is null")); } } template void TableVector::reference (const TableVector& that) { //# First destruct current vector. destruct(); //# Now reference the other table vector. tabVecPtr_p = that.tabVecPtr_p; if (tabVecPtr_p != 0) { tabVecPtr_p->link(); } } //# Make a Vector from the (RO)TableVector. template Vector TableVector::makeVector() const { Vector vect(nelements()); TableVector tvec(vect); tvec = *this; return vect; } } //# NAMESPACE CASACORE - END #endif casacore-3.7.1/tables/Tables/test/000077500000000000000000000000001476623553700167675ustar00rootroot00000000000000casacore-3.7.1/tables/Tables/test/CMakeLists.txt000066400000000000000000000024731476623553700215350ustar00rootroot00000000000000set (datafiles tTable_2.data_v0/table.dat tTable_2.data_v0/table.f0 tTable_2.data_v0/table.f0i0 tTable_2.data_v0/table.f1 tTable_2.data_v0/table.f2 tTable_2.data_v0/table.info tTable_2.data_v1/table.dat tTable_2.data_v1/table.f0 tTable_2.data_v1/table.f0i0 tTable_2.data_v1/table.f1 tTable_2.data_v1/table.f2 tTable_2.data_v1/table.info ) foreach (file ${datafiles}) configure_file (${CMAKE_CURRENT_SOURCE_DIR}/${file} ${CMAKE_CURRENT_BINARY_DIR}/${file} COPYONLY) endforeach (file) set (tests ascii2Table tArrayColumnSlices tArrayColumnCellSlices tColumnsIndex tColumnsIndexArray tConcatRows tConcatTable tConcatTable2 tConcatTable3 tMemoryTable tReadAsciiTable tReadAsciiTable2 tRefRows tRefTable tRowCopier tScalarRecordColumn tTable tTableAccess tTableCopy tTableCopyPerf tTableDesc tTableDescHyper tTableInfo tTableIter tTableKeywords tTableLock tTableLockSync tTableLockSync_2 tTableRecord tTableRow tTableTrace tTableUtil tTableVector tTable_1 tTable_2 tTable_3 tTable_4 tVeryBigTable ) # Some test sources include a test .h file. include_directories ( . ) foreach (test ${tests}) add_executable (${test} ${test}.cc) add_pch_support(${test}) target_link_libraries (${test} casa_tables) add_test (${test} ${CMAKE_SOURCE_DIR}/cmake/cmake_assay ./${test}) add_dependencies(check ${test}) endforeach (test) casacore-3.7.1/tables/Tables/test/ascii2Table.cc000066400000000000000000000111461476623553700214230ustar00rootroot00000000000000//# ascii2Table.cc: This program loads an ASCII file into a table //# Copyright (C) 1993,1994,1995,1996,1997,1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include #include #include // // program to load an ASCII file into a table // // This programs fills a table with data from an ASCII catalogue. // It calculates mean, median, etc., of the given column, shows them // and stores them as keywords. void calc(Table&, const String&); int main (int argc, const char* argv[]) { try { if (argc < 3) { cout << ">>>" << endl; cout << "Needs arguments: filein tablename [column]" << endl; cout << "filein: input file in AsciiFileIO format" << endl; cout << "tablename: name of resulting table" << endl; cout << "column: optional column for which to compute mean, etc." << endl; cout << "<<<" << endl; return 0; } // Convert the ASCII file into a table with name argv[2] cout << "Loading " << argv[1] << " into table " << argv[2] << endl; readAsciiTable (argv[1], "", argv[2]); // Compute the minimum, maximum, mean, stddev and median // show them and store into the table. if (argc < 4) { Table tab(argv[2], Table::Old); cout << "Loaded " << tab.nrow() << " rows" << endl; }else{ Table tab(argv[2], Table::Update); cout << "Loaded " << tab.nrow() << " rows" << endl; calc (tab, argv[3]); } } catch (std::exception& x) { cout << "\nCaught an exception: " << x.what() << endl; return 1; } return 0; // successfully executed } void calc(Table& tab, const String& name) { cout << "Computing mean " << name << ", etc." << endl; if (! tab.tableDesc().isColumn(name)) { cout << "Column " << name << " does not exist" << endl; return; } TableColumn tabcol (tab, name); const ColumnDesc& cd = tabcol.columnDesc(); if (cd.dataType() == TpBool || cd.dataType() == TpString || cd.dataType() == TpComplex || cd.dataType() == TpDComplex || cd.ndim() != 0) { cout << "Column " << name << " should be a numeric scalar" << endl; return; } uInt nrrow = tab.nrow(); Vector vec(nrrow); for (uInt i=0; i #include #include #include #include #include #include #include #include #include #include #include #include using namespace casacore; uInt nRows=7; uInt nChannels=5; uInt nCorrelations=3; Array referenceArray (IPosition (3, nCorrelations, nChannels, nRows)); // Create the table. void createTable (DataManager& dataMan, const Array array, Bool useDirect) { IPosition shape = array.shape(); int nCorrelations = shape(0); int nChannels = shape (1); int nRows = shape(2); // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.addColumn (ArrayColumnDesc("testArrayColumn",IPosition(2, nCorrelations, nChannels), useDirect ? ColumnDesc::Direct : ColumnDesc::FixedShape)); // Now create a new table from the description. SetupNewTable newtab("tArrayColumnCellSlices_tmp.data", td, Table::New); newtab.bindAll (dataMan); Table tab(newtab, nRows, False, Table::LocalEndian); ArrayColumn arrayColumn (tab, "testArrayColumn"); //indgen (arrf); RefRows rows (0, nRows-1); // all rows arrayColumn.putColumnCells (rows, referenceArray); } void clearValues (ArrayColumn & arrayColumn, Int value = 0) { int nRows = referenceArray.shape().last(); Array array (referenceArray.shape(), value); RefRows rows (0, nRows-1); // all rows arrayColumn.putColumnCells (rows, array); } void createReferenceArray (int nCorrelations, int nChannels, int nRows) { for (Int i=0; i other) { AlwaysAssertExit (other.shape().isEqual (referenceArray.shape())); for (uInt i=0; i & arrayColumn) { Array array (referenceArray.shape(), -2); RefRows rows (0, referenceArray.shape().last()-1); arrayColumn.getColumnCells (rows, array); compareToReferenceArray (array); } void readCellSlices() { Table tab("tArrayColumnCellSlices_tmp.data"); ArrayColumn arrayColumn(tab, "testArrayColumn"); { // Check ColumnSlicer validation logic // // No slicers Vector dataSlicer; Vector destinationSlicer; IPosition shape (1, 0); try { ColumnSlicer columnSlicer (shape, dataSlicer, destinationSlicer); AlwaysAssertExit (False); // shouldn't get here } catch (std::exception & e){ } } { // Check ColumnSlicer validation logic II // // Slicer lists must be same length Vector dataSlicer (3, nullptr); Vector destinationSlicer (2, nullptr); IPosition shape (1, 0); try { ColumnSlicer columnSlicer (shape, dataSlicer, destinationSlicer); AlwaysAssertExit (False); // shouldn't get here } catch (std::exception & e){ } } { // Check ColumnSlicer validation logic III // // Slicer elements must have same length Vector dataSlicer (1, nullptr); Vector destinationSlicer (1, nullptr); IPosition shape (1, 0); dataSlicer (0) = new Slicer (IPosition (1, 0), IPosition (1, 2), IPosition (1, 1)); destinationSlicer (0) = new Slicer (IPosition (1, 0), IPosition (1, 3), IPosition (1, 1)); try { ColumnSlicer columnSlicer (shape, dataSlicer, destinationSlicer); AlwaysAssertExit (False); // shouldn't get here } catch (std::exception & e){ } } { // Check the actual I/O now // // Read the whole thing at once. Vector dataSlicer (1, nullptr); Vector destinationSlicer (1, nullptr); IPosition shape (2, nCorrelations, nChannels); dataSlicer (0) = new Slicer (IPosition (2, 0, 0), IPosition (2, nCorrelations, nChannels), IPosition (2, 1)); destinationSlicer (0) = new Slicer (IPosition (2, 0), IPosition (2, nCorrelations, nChannels), IPosition (2, 1)); ColumnSlicer columnSlicer (shape, dataSlicer, destinationSlicer); RefRows refRows (0, nRows - 1); Array destination (IPosition (3, nCorrelations, nChannels, nRows)); arrayColumn.getColumnCells (refRows, columnSlicer, destination); compareToReferenceArray (destination); } { // Check the actual I/O now // // Do read in two halves Vector dataSlicer (2, nullptr); Vector destinationSlicer (2, nullptr); IPosition shape (2, nCorrelations, nChannels); uInt n1 = nChannels / 2; uInt n2 = nChannels / 2 + nChannels % 2; dataSlicer (0) = new Slicer (IPosition (2, 0, 0), IPosition (2, nCorrelations, n1), IPosition (2, 1, 1)); destinationSlicer (0) = new Slicer (IPosition (2, 0, 0), IPosition (2, nCorrelations, n1), IPosition (2, 1, 1)); dataSlicer (1) = new Slicer (IPosition (2, 0, n1), IPosition (2, nCorrelations, n2), IPosition (2, 1, 1)); destinationSlicer (1) = new Slicer (IPosition (2, 0, n1), IPosition (2, nCorrelations, n2), IPosition (2, 1, 1)); ColumnSlicer columnSlicer (shape, dataSlicer, destinationSlicer); RefRows refRows (0, nRows - 1); Array destination (IPosition (3, nCorrelations, nChannels, nRows)); arrayColumn.getColumnCells (refRows, columnSlicer, destination); compareToReferenceArray (destination); } { // Check the actual I/O now // // Do read in two halves the other way. Vector dataSlicer (2, nullptr); Vector destinationSlicer (2, nullptr); IPosition shape (2, nCorrelations, nChannels); uInt n1 = nCorrelations / 2; uInt n2 = nCorrelations / 2 + nCorrelations % 2; dataSlicer (0) = new Slicer (IPosition (2, 0, 0), IPosition (2, n1, nChannels), IPosition (2, 1, 1)); destinationSlicer (0) = new Slicer (IPosition (2, 0, 0), IPosition (2, n1, nChannels), IPosition (2, 1, 1)); dataSlicer (1) = new Slicer (IPosition (2, n1, 0), IPosition (2, n2, nChannels), IPosition (2, 1, 1)); destinationSlicer (1) = new Slicer (IPosition (2, n1, 0), IPosition (2, n2, nChannels), IPosition (2, 1, 1)); ColumnSlicer columnSlicer (shape, dataSlicer, destinationSlicer); RefRows refRows (0, nRows - 1); Array destination (IPosition (3, nCorrelations, nChannels, nRows)); arrayColumn.getColumnCells (refRows, columnSlicer, destination); compareToReferenceArray (destination); } { // Check the actual I/O now // // Do it with two interleaving slices. Vector dataSlicer (2, nullptr); Vector destinationSlicer (2, nullptr); IPosition shape (2, nCorrelations, nChannels); uInt nChannels1 = nChannels / 2; uInt nChannels2 = nChannels / 2 + nChannels % 2; dataSlicer (0) = new Slicer (IPosition (2, 0, 0), IPosition (2, nCorrelations, nChannels2), IPosition (2, 1, 2)); destinationSlicer (0) = new Slicer (IPosition (2, 0, 0), IPosition (2, nCorrelations, nChannels2), IPosition (2, 1, 2)); dataSlicer (1) = new Slicer (IPosition (2, 0, 1), IPosition (2, nCorrelations, nChannels1), IPosition (2, 1, 2)); destinationSlicer (1) = new Slicer (IPosition (2, 0, 1), IPosition (2, nCorrelations, nChannels1), IPosition (2, 1, 2)); ColumnSlicer columnSlicer (shape, dataSlicer, destinationSlicer); RefRows refRows (0, nRows - 1); Array destination (IPosition (3, nCorrelations, nChannels, nRows)); arrayColumn.getColumnCells (refRows, columnSlicer, destination); compareToReferenceArray (destination); } { // Check the actual I/O now // // Do it with two interleaving slices doing it the other way Vector dataSlicer (2, nullptr); Vector destinationSlicer (2, nullptr); IPosition shape (2, nCorrelations, nChannels); uInt nCorrelations1 = nCorrelations / 2; uInt nCorrelations2 = nCorrelations / 2 + nCorrelations % 2; dataSlicer (0) = new Slicer (IPosition (2, 0, 0), IPosition (2, nCorrelations2, nChannels), IPosition (2, 2, 1)); destinationSlicer (0) = new Slicer (IPosition (2, 0, 0), IPosition (2, nCorrelations2, nChannels), IPosition (2, 2, 1)); dataSlicer (1) = new Slicer (IPosition (2, 1, 0), IPosition (2, nCorrelations1, nChannels), IPosition (2, 2, 1)); destinationSlicer (1) = new Slicer (IPosition (2, 1, 0), IPosition (2, nCorrelations1, nChannels), IPosition (2, 2, 1)); ColumnSlicer columnSlicer (shape, dataSlicer, destinationSlicer); RefRows refRows (0, nRows - 1); Array destination (IPosition (3, nCorrelations, nChannels, nRows)); arrayColumn.getColumnCells (refRows, columnSlicer, destination); compareToReferenceArray (destination); } } void writeCellSlices() { Table tab("tArrayColumnCellSlices_tmp.data", Table::Update); ArrayColumn arrayColumn(tab, "testArrayColumn"); { // Check the actual I/O now // // Read the whole thing at once. Vector dataSlicer (1, nullptr); Vector sourceSlicer (1, nullptr); IPosition shape (2, nCorrelations, nChannels); dataSlicer (0) = new Slicer (IPosition (2, 0, 0), IPosition (2, nCorrelations, nChannels), IPosition (2, 1)); sourceSlicer (0) = new Slicer (IPosition (2, 0), IPosition (2, nCorrelations, nChannels), IPosition (2, 1)); ColumnSlicer columnSlicer (shape, dataSlicer, sourceSlicer); RefRows refRows (0, nRows - 1); Array source = referenceArray.copy(); arrayColumn.putColumnCells (refRows, columnSlicer, source); readAndCompareArray (arrayColumn); } { // Check the actual I/O now // // Do read in two halves Vector dataSlicer (2, nullptr); Vector sourceSlicer (2, nullptr); IPosition shape (2, nCorrelations, nChannels); uInt n1 = nChannels / 2; uInt n2 = nChannels / 2 + nChannels % 2; dataSlicer (0) = new Slicer (IPosition (2, 0, 0), IPosition (2, nCorrelations, n1), IPosition (2, 1, 1)); sourceSlicer (0) = new Slicer (IPosition (2, 0, 0), IPosition (2, nCorrelations, n1), IPosition (2, 1, 1)); dataSlicer (1) = new Slicer (IPosition (2, 0, n1), IPosition (2, nCorrelations, n2), IPosition (2, 1, 1)); sourceSlicer (1) = new Slicer (IPosition (2, 0, n1), IPosition (2, nCorrelations, n2), IPosition (2, 1, 1)); ColumnSlicer columnSlicer (shape, dataSlicer, sourceSlicer); RefRows refRows (0, nRows - 1); Array source = referenceArray.copy(); arrayColumn.putColumnCells (refRows, columnSlicer, source); readAndCompareArray (arrayColumn); } { // Check the actual I/O now // // Do read in two halves the other way. Vector dataSlicer (2, nullptr); Vector sourceSlicer (2, nullptr); IPosition shape (2, nCorrelations, nChannels); uInt n1 = nCorrelations / 2; uInt n2 = nCorrelations / 2 + nCorrelations % 2; dataSlicer (0) = new Slicer (IPosition (2, 0, 0), IPosition (2, n1, nChannels), IPosition (2, 1, 1)); sourceSlicer (0) = new Slicer (IPosition (2, 0, 0), IPosition (2, n1, nChannels), IPosition (2, 1, 1)); dataSlicer (1) = new Slicer (IPosition (2, n1, 0), IPosition (2, n2, nChannels), IPosition (2, 1, 1)); sourceSlicer (1) = new Slicer (IPosition (2, n1, 0), IPosition (2, n2, nChannels), IPosition (2, 1, 1)); ColumnSlicer columnSlicer (shape, dataSlicer, sourceSlicer); RefRows refRows (0, nRows - 1); Array source = referenceArray.copy(); arrayColumn.putColumnCells (refRows, columnSlicer, source); readAndCompareArray (arrayColumn); } { // Check the actual I/O now // // Do it with two interleaving slices. Vector dataSlicer (2, nullptr); Vector sourceSlicer (2, nullptr); IPosition shape (2, nCorrelations, nChannels); uInt nChannels1 = nChannels / 2; uInt nChannels2 = nChannels / 2 + nChannels % 2; dataSlicer (0) = new Slicer (IPosition (2, 0, 0), IPosition (2, nCorrelations, nChannels2), IPosition (2, 1, 2)); sourceSlicer (0) = new Slicer (IPosition (2, 0, 0), IPosition (2, nCorrelations, nChannels2), IPosition (2, 1, 2)); dataSlicer (1) = new Slicer (IPosition (2, 0, 1), IPosition (2, nCorrelations, nChannels1), IPosition (2, 1, 2)); sourceSlicer (1) = new Slicer (IPosition (2, 0, 1), IPosition (2, nCorrelations, nChannels1), IPosition (2, 1, 2)); ColumnSlicer columnSlicer (shape, dataSlicer, sourceSlicer); RefRows refRows (0, nRows - 1); Array source = referenceArray.copy(); arrayColumn.putColumnCells (refRows, columnSlicer, source); readAndCompareArray (arrayColumn); } { // Check the actual I/O now // // Do it with two interleaving slices doing it the other way Vector dataSlicer (2, nullptr); Vector sourceSlicer (2, nullptr); IPosition shape (2, nCorrelations, nChannels); uInt nCorrelations1 = nCorrelations / 2; uInt nCorrelations2 = nCorrelations / 2 + nCorrelations % 2; dataSlicer (0) = new Slicer (IPosition (2, 0, 0), IPosition (2, nCorrelations2, nChannels), IPosition (2, 2, 1)); sourceSlicer (0) = new Slicer (IPosition (2, 0, 0), IPosition (2, nCorrelations2, nChannels), IPosition (2, 2, 1)); dataSlicer (1) = new Slicer (IPosition (2, 1, 0), IPosition (2, nCorrelations1, nChannels), IPosition (2, 2, 1)); sourceSlicer (1) = new Slicer (IPosition (2, 1, 0), IPosition (2, nCorrelations1, nChannels), IPosition (2, 2, 1)); ColumnSlicer columnSlicer (shape, dataSlicer, sourceSlicer); RefRows refRows (0, nRows - 1); Array source = referenceArray.copy(); arrayColumn.putColumnCells (refRows, columnSlicer, source); readAndCompareArray (arrayColumn); } } int main() { try { createReferenceArray (nCorrelations, nChannels, nRows); { StandardStMan dataMan; createTable (dataMan, referenceArray, True); readCellSlices(); writeCellSlices(); } { StandardStMan dataMan; createTable (dataMan, referenceArray, False); readCellSlices(); writeCellSlices(); } { IncrementalStMan dataMan; createTable (dataMan, referenceArray, True); readCellSlices(); writeCellSlices(); } { IncrementalStMan dataMan; createTable (dataMan, referenceArray, False); readCellSlices(); writeCellSlices(); } } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } casacore-3.7.1/tables/Tables/test/tArrayColumnSlices.cc000066400000000000000000000151111476623553700230600ustar00rootroot00000000000000//# tArrayColumnSlices.cc: Test program for the ArrayColumn slices functions //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include using namespace casacore; // Create the table. void createTab() { // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.addColumn (ArrayColumnDesc("arr1",IPosition(2,20,30), ColumnDesc::FixedShape)); // Now create a new table from the description. SetupNewTable newtab("tArrayColumnSlices_tmp.data", td, Table::New); Table tab(newtab, 10, False, Table::LocalEndian); ArrayColumn arr1(tab, "arr1"); Array arrf(IPosition(2,20,30)); indgen (arrf); for (uInt i=0; i<10; i++) { arr1.put(i, arrf); arrf += (float)(arrf.nelements()); } } void readCellSlices() { Table tab("tArrayColumnSlices_tmp.data"); ArrayColumn arr1(tab, "arr1"); { // No axes, thus all entire axes. Vector > slices; for (uInt i=0; i > slices(2); for (uInt i=0; i > slices(2); slices[0].resize(2); slices[0][0] = Slice(0,5); slices[0][1] = Slice(5,15); slices[1].resize(3); slices[1][0] = Slice(0,10); slices[1][1] = Slice(10,4); slices[1][2] = Slice(14,16); for (uInt i=0; i > slices(2); slices[0].resize(2); slices[0][0] = Slice(2,5,2); slices[0][1] = Slice(12,3,2); slices[1].resize(3); slices[1][0] = Slice(1,3,3); slices[1][1] = Slice(10,2,3); slices[1][2] = Slice(16,1); for (uInt i=0; i arr1(tab, "arr1"); { // No axes, thus all entire axes. Vector > slices; AlwaysAssertExit (allEQ (arr1.getColumn(), arr1.getColumn(slices))); } { // Axes parts with strides. Vector > slices(2); slices[0].resize(2); slices[0][0] = Slice(2,5,2); slices[0][1] = Slice(12,3,2); slices[1].resize(3); slices[1][0] = Slice(1,3,3); slices[1][1] = Slice(10,2,3); slices[1][2] = Slice(16,1); AlwaysAssertExit (allEQ (arr1.getColumn(Slicer(IPosition(2,2,1), IPosition(2,8,6), IPosition(2,2,3))), arr1.getColumn(slices))); } } void writeCellSlices() { Table tab("tArrayColumnSlices_tmp.data", Table::Update); ArrayColumn arr1(tab, "arr1"); { // Axes parts with strides. Vector > slices(2); slices[0].resize(2); slices[0][0] = Slice(2,5,2); slices[0][1] = Slice(12,3,2); slices[1].resize(3); slices[1][0] = Slice(1,3,3); slices[1][1] = Slice(10,2,3); slices[1][2] = Slice(16,1); for (uInt i=0; i arr = float(1) + arr1.getSlice(i, Slicer(IPosition(2,2,1), IPosition(2,8,6), IPosition(2,2,3))); arr1.putSlice (i, slices, arr); AlwaysAssertExit (allEQ (arr1.getSlice(i, Slicer(IPosition(2,2,1), IPosition(2,8,6), IPosition(2,2,3))), arr)); } } } void writeColumnSlices() { Table tab("tArrayColumnSlices_tmp.data", Table::Update); ArrayColumn arr1(tab, "arr1"); { // Entire axes, but in parts. Vector > slices(2); slices[0].resize(2); slices[0][0] = Slice(0,5); slices[0][1] = Slice(5,15); slices[1].resize(3); slices[1][0] = Slice(0,10); slices[1][1] = Slice(10,4); slices[1][2] = Slice(14,16); Array arr = float(12) + arr1.getColumn(); arr1.putColumn (slices, arr); AlwaysAssertExit (allEQ (arr1.getColumn(), arr)); } } int main() { try { createTab(); readCellSlices(); readColumnSlices(); writeCellSlices(); writeColumnSlices(); readColumnSlices(); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } casacore-3.7.1/tables/Tables/test/tColumnsIndex.cc000066400000000000000000000277241476623553700221060ustar00rootroot00000000000000//# tColumnsIndex.cc: Test program for the ColumnsIndex class //# Copyright (C) 1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Test program for the ColumnsIndex class // // First build a description. void a() { // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.comment() = "A test of class Table"; td.addColumn (ScalarColumnDesc("abool")); td.addColumn (ScalarColumnDesc("auchar")); td.addColumn (ScalarColumnDesc("ashort")); td.addColumn (ScalarColumnDesc("aint")); td.addColumn (ScalarColumnDesc("auint")); td.addColumn (ScalarColumnDesc("afloat")); td.addColumn (ScalarColumnDesc("adouble")); td.addColumn (ScalarColumnDesc("acomplex")); td.addColumn (ScalarColumnDesc("adcomplex")); td.addColumn (ScalarColumnDesc("astring")); // Now create a new table from the description. const Int nrrow = 10; SetupNewTable newtab("tColumnsIndex_tmp.data", td, Table::New); Table tab(newtab, nrrow); ScalarColumn abool(tab, "abool"); ScalarColumn auchar(tab, "auchar"); ScalarColumn ashort(tab, "ashort"); ScalarColumn aint(tab, "aint"); ScalarColumn auint(tab, "auint"); ScalarColumn afloat(tab, "afloat"); ScalarColumn adouble(tab,"adouble"); ScalarColumn acomplex(tab, "acomplex"); ScalarColumn adcomplex(tab, "adcomplex"); ScalarColumn astring(tab, "astring"); char str[8]; for (Int i=0; i abool (colInx0.accessKey(), "abool"); RecordFieldPtr auchar (colInx1.accessKey(), "auchar"); RecordFieldPtr ashort (colInx2.accessKey(), "ashort"); RecordFieldPtr aint (colInx3.accessKey(), "aint"); RecordFieldPtr auint (colInx4.accessKey(), "auint"); RecordFieldPtr afloat (colInx5.accessKey(), "afloat"); RecordFieldPtr adouble (colInx6.accessKey(), "adouble"); RecordFieldPtr acomplex (colInx7.accessKey(), "acomplex"); RecordFieldPtr adcomplex (colInx8.accessKey(), "adcomplex"); RecordFieldPtr astring (colInx9.accessKey(), "astring"); Record rec; RowNumbers rows; Bool found; char str[8]; // Test each individual type. uInt i; for (i=0; i& fieldPtrs, const Block& dataPtrs, const Block& dataTypes, rownr_t index) { AlwaysAssert (dataTypes.nelements() == 2, AipsError); AlwaysAssert (dataTypes[0] == TpDouble && dataTypes[1] == TpFloat, AipsError); const Double keyTime = *(*(const RecordFieldPtr*)(fieldPtrs[0])); const Double time = ((const Double*)(dataPtrs[0]))[index]; const Double width = ((const Float*)(dataPtrs[1]))[index]; const Double start = time - width/2; const Double end = time + width/2; if (keyTime < start) { return -1; } else if (keyTime > end) { return 1; } return 0; } void c() { Table tab("tColumnsIndex_tmp.data", Table::Update); // Create the index with the special compare function. ColumnsIndex colInx0 (tab, stringToVector("adouble,afloat"), tcompare); RecordFieldPtr keydouble (colInx0.accessKey(), "adouble"); const Int nrrow = tab.nrow(); ScalarColumn aint(tab, "aint"); ScalarColumn auint(tab, "auint"); ScalarColumn afloat(tab, "afloat"); // Change a the values of a few columns. Int i; for (i=0; i aint (colInx3.accessKey(), "aint"); RecordFieldPtr auint (colInx4.accessKey(), "auint"); Bool found; Int i; for (i=0; i abool1 (colInx5.accessKey(), "abool"); RecordFieldPtr auint1 (colInx5.accessKey(), "auint"); for (i=0; i<(nrrow+2)/3; i++) { *auint1 = 1+2*i; *abool1 = True; cout << colInx5.getRowNumbers() << ' '; *abool1 = False; cout << colInx5.getRowNumbers() << endl; } // Now test a range of multiple columns. RecordFieldPtr abool1l (colInx5.accessLowerKey(), "abool"); RecordFieldPtr auint1l (colInx5.accessLowerKey(), "auint"); RecordFieldPtr abool1u (colInx5.accessUpperKey(), "abool"); RecordFieldPtr auint1u (colInx5.accessUpperKey(), "auint"); *abool1l = True; *abool1u = True; *auint1l = 3; *auint1u = 10; cout << colInx5.getRowNumbers (True, True) << ' '; cout << colInx5.getRowNumbers (False, False) << endl; *auint1l = 0; *auint1u = 10; cout << colInx5.getRowNumbers (True, True) << ' '; cout << colInx5.getRowNumbers (False, False) << endl; *auint1l = 2; *auint1u = 6; cout << colInx5.getRowNumbers (True, True) << ' '; cout << colInx5.getRowNumbers (False, False) << endl; *abool1l = False; *abool1u = False; cout << colInx5.getRowNumbers (True, True) << ' '; cout << colInx5.getRowNumbers (False, False) << endl; // Now test extending the table. // The index should be updated automatically, so create that first. ColumnsIndex colInx6 (tab, "adouble"); RecordFieldPtr adouble (colInx6.accessKey(), "adouble"); if (nrrow < 1000) { tab.addRow (1000-nrrow); nrrow = 1000; } ScalarColumn cdouble(tab, "adouble"); // Change a the values of a few columns. for (i=0; i>>" << endl; Timer timer; for (i=0; i<100*nrrow; i++) { *adouble = i/100; AlwaysAssertExit ( (Int(colInx6.getRowNumber(found)) == i/100 && found)); } timer.show ("100000*find"); cout << "<<<" << endl; } int main() { try { a(); b(); c(); d(); } catch (std::exception& x) { cout << "Exception caught: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/tables/Tables/test/tColumnsIndex.out000066400000000000000000000004471476623553700223210ustar00rootroot00000000000000ColumnsIndex::getRowNumber only possible when the index keys are unique [0, 1, 2] [0, 1, 2] [0, 1, 2] [3, 4, 5] [3, 4, 5] [3, 4, 5] [6, 7, 8] [6, 7, 8] [6, 7, 8] [9] [0, 2] [1] [4] [3, 5] [6, 8] [7] [] [9] [4, 6, 8] [6, 8] [0, 2, 4, 6, 8] [0, 2, 4, 6, 8] [4, 6, 8] [4, 6, 8] [3, 5, 7] [3, 5, 7] casacore-3.7.1/tables/Tables/test/tColumnsIndexArray.cc000066400000000000000000000143561476623553700231020ustar00rootroot00000000000000//# tColumnsIndexArray.cc: Test program for the ColumnsIndexArray class //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Test program for the ColumnsIndexArray class // // First build a description. void a() { // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.comment() = "A test of class Table"; td.addColumn (ArrayColumnDesc("aint")); td.addColumn (ArrayColumnDesc("auint")); td.addColumn (ArrayColumnDesc("astring")); // Now create a new table from the description. const Int nrrow = 10; SetupNewTable newtab("tColumnsIndexArray_tmp.data", td, Table::New); Table tab(newtab, nrrow); ArrayColumn aint(tab, "aint"); ArrayColumn auint(tab, "auint"); ArrayColumn astring(tab, "astring"); Array arri(IPosition(1,3)); indgen(arri); Array arrui(IPosition(1,3)); indgen(arrui); Array arrstr(IPosition(1,3)); arrstr = "aa"; for (Int i=0; i aint (colInx3.accessKey(), "aint"); RecordFieldPtr auint (colInx4.accessKey(), "auint"); RecordFieldPtr astring (colInx9.accessKey(), "astring"); Record rec; Bool found; // Find the 15 values. for (uInt i=0; i<15; i++) { rec.define ("auint", i); AlwaysAssertExit ( (colInx4.getRowNumber(found, rec) == 2*(i/3) && found)); } // The 16th one should not be found. *auint = 15; colInx4.getRowNumber(found); AlwaysAssertExit (!found); // Find the values in the other index. They are not unique. for (Int i=0; i<12; i++) { *aint = i; cout << colInx3.getRowNumbers() << endl; } *astring = "a"; cout << colInx9.getRowNumbers() << endl; cout << colInx9.getRowNumbers(True) << endl; *astring = "aa"; cout << colInx9.getRowNumbers() << endl; cout << colInx9.getRowNumbers(True) << endl; // Test a not unique index in an erroneous way. try { colInx9.getRowNumber(found); } catch (std::exception& x) { cout << x.what() << endl; // values are not unique } // Test a range. Record lower, upper; lower.define ("auint", uInt(2)); upper.define ("auint", uInt(6)); cout << colInx4.getRowNumbers (lower, upper, False, False) << endl; cout << colInx4.getRowNumbers (lower, upper, True, False) << endl; cout << colInx4.getRowNumbers (lower, upper, False, True) << endl; cout << colInx4.getRowNumbers (lower, upper, True, True) << endl; cout << colInx4.getRowNumbers (lower, upper, False, False, True) << endl; cout << colInx4.getRowNumbers (lower, upper, True, False, True) << endl; cout << colInx4.getRowNumbers (lower, upper, False, True, True) << endl; cout << colInx4.getRowNumbers (lower, upper, True, True, True) << endl; upper.define ("auint", uInt(3)); cout << colInx4.getRowNumbers (lower, upper, True, True) << endl; cout << colInx4.getRowNumbers (lower, upper, False, False) << endl; cout << colInx4.getRowNumbers (lower, upper, True, True, True) << endl; cout << colInx4.getRowNumbers (lower, upper, False, False, True) << endl; } void c() { Table tab("tColumnsIndexArray_tmp.data", Table::Update); // Create the index with the special compare function. ColumnsIndexArray colInx0 (tab, "aint"); RecordFieldPtr keyint (colInx0.accessKey(), "aint"); ArrayColumn aint(tab, "aint"); ArrayColumn auint(tab, "auint"); // Change the values of a few columns. Array arri(IPosition(2,2,4)); Array arrui(IPosition(3,2,3,3)); indgen(arri); indgen(arrui, uInt(15)); aint.put (3, arri); auint.put (7, arrui); // Tell the index that some columns have changed. colInx0.setChanged ("auint"); *keyint = 2; // Should not change anything yet. cout << colInx0.getRowNumbers() << endl; colInx0.setChanged ("aint"); // Now the index knows it is changed. cout << colInx0.getRowNumbers() << endl; } int main() { try { a(); b(); c(); } catch (std::exception& x) { cout << "Exception caught: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/tables/Tables/test/tColumnsIndexArray.out000066400000000000000000000005011476623553700233070ustar00rootroot00000000000000[0] [0] [0, 2] [2] [2, 4] [4] [4, 6] [6] [6, 8] [8] [8] [] [] [] [0, 0, 0, 2, 2, 2, 4, 4, 4, 6, 6, 6, 8, 8, 8] [0, 2, 4, 6, 8] ColumnsIndexArray::getRowNumber only possible when the index keys are unique [2, 2, 2] [0, 2, 2, 2] [2, 2, 2, 4] [0, 2, 2, 2, 4] [2] [0, 2] [2, 4] [0, 2, 4] [0, 2] [] [0, 2] [] [0, 2] [0, 2, 3] casacore-3.7.1/tables/Tables/test/tConcatRows.cc000066400000000000000000000155251476623553700215540ustar00rootroot00000000000000//# tConcatRows.cc: This program tests class ConcatRows //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include // // Test program for class ConcatRows. // void doIt() { // Construct from 2 tables. ConcatRows rows; AlwaysAssertExit (rows.nrow() == 0); AlwaysAssertExit (rows.ntable() == 0); rows.add (10); AlwaysAssertExit (rows.nrow() == 10); AlwaysAssertExit (rows.ntable() == 1); rows.add (15); AlwaysAssertExit (rows.nrow() == 25); AlwaysAssertExit (rows.ntable() == 2); // Check if rownr mapping is fine. uInt tabnr; rownr_t rownr; for (uInt i=0; i<10; ++i) { rows.mapRownr (tabnr, rownr, i); AlwaysAssertExit (tabnr == 0); AlwaysAssertExit (rownr == i); } for (uInt i=10; i<25; ++i) { rows.mapRownr (tabnr, rownr, i); AlwaysAssertExit (tabnr == 1); AlwaysAssertExit (rownr == i-10); } // Check if it fails if rownr out of bounds. Bool ok = True; try { rows.mapRownr (tabnr, rownr, rows.nrow()); } catch (std::exception& x) { ok = False; } AlwaysAssertExit (!ok); // Check if iteration is fine. { // Check for an empty object. ConcatRowsIter iter ((ConcatRows())); AlwaysAssertExit (iter.pastEnd()); iter.next(); AlwaysAssertExit (iter.pastEnd()); } { // Check for an empty object. ConcatRowsIter iter ((ConcatRows()), 0, 1000); AlwaysAssertExit (iter.pastEnd()); iter.next(); AlwaysAssertExit (iter.pastEnd()); } { // Check for the full iteration. ConcatRowsIter iter (rows); AlwaysAssertExit (! iter.pastEnd()); RefRows chunk = iter.getChunk(); AlwaysAssertExit (chunk.firstRow() == 0); AlwaysAssertExit (chunk.nrow() == 10); iter.next(); chunk = iter.getChunk(); AlwaysAssertExit (chunk.firstRow() == 0); AlwaysAssertExit (chunk.nrow() == 15); iter.next(); AlwaysAssertExit (iter.pastEnd()); } { // Check for the full iteration. ConcatRowsIter iter (rows, 0, 1000); AlwaysAssertExit (! iter.pastEnd()); RefRows chunk = iter.getChunk(); AlwaysAssertExit (chunk.firstRow() == 0); AlwaysAssertExit (chunk.nrow() == 10); AlwaysAssertExit (iter.tableNr() == 0); iter.next(); chunk = iter.getChunk(); AlwaysAssertExit (chunk.firstRow() == 0); AlwaysAssertExit (chunk.nrow() == 15); AlwaysAssertExit (iter.tableNr() == 1); iter.next(); AlwaysAssertExit (iter.pastEnd()); } { // Check for the empty iteration. ConcatRowsIter iter (rows, 2, 1); AlwaysAssertExit (iter.pastEnd()); } { // Check for partial iteration. ConcatRowsIter iter (rows, 2, 8); AlwaysAssertExit (! iter.pastEnd()); RefRows chunk = iter.getChunk(); AlwaysAssertExit (chunk.firstRow() == 2); AlwaysAssertExit (chunk.nrow() == 7); AlwaysAssertExit (iter.tableNr() == 0); iter.next(); AlwaysAssertExit (iter.pastEnd()); } { // Check for partial iteration. ConcatRowsIter iter (rows, 12, 18); AlwaysAssertExit (! iter.pastEnd()); RefRows chunk = iter.getChunk(); AlwaysAssertExit (chunk.firstRow() == 2); AlwaysAssertExit (chunk.nrow() == 7); AlwaysAssertExit (iter.tableNr() == 1); iter.next(); AlwaysAssertExit (iter.pastEnd()); } { // Check for partial iteration. ConcatRowsIter iter (rows, 2, 18); AlwaysAssertExit (! iter.pastEnd()); RefRows chunk = iter.getChunk(); AlwaysAssertExit (chunk.firstRow() == 2); AlwaysAssertExit (chunk.nrow() == 8); AlwaysAssertExit (iter.tableNr() == 0); iter.next(); chunk = iter.getChunk(); AlwaysAssertExit (chunk.firstRow() == 0); AlwaysAssertExit (chunk.nrow() == 9); AlwaysAssertExit (iter.tableNr() == 1); iter.next(); AlwaysAssertExit (iter.pastEnd()); } { // Check for iteration with steps. ConcatRowsIter iter (rows, 2, 25, 4); AlwaysAssertExit (! iter.pastEnd()); RefRows chunk = iter.getChunk(); AlwaysAssertExit (chunk.firstRow() == 2); AlwaysAssertExit (chunk.nrow() == 2); AlwaysAssertExit (iter.tableNr() == 0); iter.next(); chunk = iter.getChunk(); AlwaysAssertExit (chunk.firstRow() == 0); AlwaysAssertExit (chunk.nrow() == 4); AlwaysAssertExit (iter.tableNr() == 1); iter.next(); AlwaysAssertExit (iter.pastEnd()); } { // Check for iteration with steps. ConcatRowsIter iter (rows, 2, 18, 5); AlwaysAssertExit (! iter.pastEnd()); RefRows chunk = iter.getChunk(); AlwaysAssertExit (chunk.firstRow() == 2); AlwaysAssertExit (chunk.nrow() == 2); AlwaysAssertExit (iter.tableNr() == 0); iter.next(); chunk = iter.getChunk(); AlwaysAssertExit (chunk.firstRow() == 2); AlwaysAssertExit (chunk.nrow() == 2); AlwaysAssertExit (iter.tableNr() == 1); iter.next(); AlwaysAssertExit (iter.pastEnd()); } { // Check for iteration with steps. ConcatRowsIter iter (rows, 1, 22, 7); AlwaysAssertExit (! iter.pastEnd()); RefRows chunk = iter.getChunk(); AlwaysAssertExit (chunk.firstRow() == 1); AlwaysAssertExit (chunk.nrow() == 2); AlwaysAssertExit (iter.tableNr() == 0); iter.next(); chunk = iter.getChunk(); AlwaysAssertExit (chunk.firstRow() == 5); AlwaysAssertExit (chunk.nrow() == 2); AlwaysAssertExit (iter.tableNr() == 1); iter.next(); AlwaysAssertExit (iter.pastEnd()); } } int main() { try { doIt(); } catch (std::exception& x) { cout << "\nCaught an exception: " << x.what() << endl; return 1; } return 0; // successfully executed } casacore-3.7.1/tables/Tables/test/tConcatTable.cc000066400000000000000000000264231476623553700216500ustar00rootroot00000000000000//# tConcatTable.cc: Test program for class ConcatTable //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Test program for class tConcatTable // void doAddColumn(const Table &tab) { // Add a column. Table tabrw(tab); tabrw.reopenRW(); rownr_t nrows = tab.nrow(); AlwaysAssertExit(!tabrw.canAddRow()); auto newName = "new_col"; tabrw.addColumn (ScalarColumnDesc(newName)); AlwaysAssertExit(tab.tableDesc().isColumn(newName)); AlwaysAssertExit(nullptr != tabrw.findDataManager(newName, true)); AlwaysAssertExit(!tabrw.hasDataChanged()); // this is not currently supported in ConcatTable AlwaysAssertExit(!tabrw.canRenameColumn(newName)); AlwaysAssertExit(!tabrw.canRemoveColumn(newName)); AlwaysAssertExit(!tabrw.canRemoveRow()); // fillColumnData will trigger TableColumn constructors, getColumn(), shape() etc. // operations which might fail if the ConcatTable, ConcatColumn internal state is not // consistent. Int val = 44; TableCopy::fillColumnData(tabrw, newName, val); AlwaysAssertExit(!tabrw.hasDataChanged()); AlwaysAssertExit(nrows == tabrw.nrow()); ScalarColumn filled(tabrw, newName); AlwaysAssertExit(val == filled(0)); auto newArrName = "new_arr_col"; auto srcName = "arr3"; tabrw.addColumn(ArrayColumnDesc(newArrName)); float fval = 0.3f; TableCopy::fillColumnData(tabrw, newArrName, fval, tab, srcName, false); AlwaysAssertExit(!tabrw.hasDataChanged()); AlwaysAssertExit(nrows == tabrw.nrow()); ArrayColumn filledArr(tabrw, newArrName); AlwaysAssertExit(filledArr.shape(0) == TableColumn(tab, srcName).shape(0)); Cube arrval(IPosition(3,2,3,4)); filledArr.get(0, arrval); AlwaysAssertExit(allEQ(fval, arrval)); } void doIt (const Table& tab) { cout << ">>> -------------------" << endl; cout << "partNamesF " << tab.getPartNames() << endl; cout << "partNamesT " << tab.getPartNames(True) << endl; cout << "<<<" << endl; ScalarColumn ab2(tab,"ab"); ScalarColumn ac (tab,"ac"); ScalarColumn ad(tab,"ad"); ScalarColumn ae(tab,"ae"); ScalarColumn af(tab,"af"); ScalarColumn ag(tab,"ag"); ArrayColumn arr1(tab,"arr1"); ArrayColumn arr2(tab,"arr2"); ArrayColumn arr3(tab,"arr3"); uInt i; Vector names = tab.tableDesc().columnNames(); for (i=0; i arrf(IPosition(3,2,3,4)); Cube arrval(IPosition(3,2,3,4)); Cube arrvalslice(arrval(Slice(0,1),Slice(0,1,2),Slice(0,2,2))); Slice tmp; Slicer nslice (tmp, tmp, tmp, Slicer::endIsLength); Slicer nslice2(Slice(0,1), Slice(0,1,2), Slice(0,2,2), Slicer::endIsLength); indgen (arrf); for (i=0; i<10; i++) { cout << "get scalar row " << i << endl; ab2.get (i, abval); ac.get (i, acval); ad.get (i, adval); ae.get (i, aeval); af.get (i, afval); ag.get (i, agval); snprintf (str, sizeof(str), "V%i", i); if (abval != Int(i) || acval != Int(i+1) || adval != i+2 || aeval != i+3 || afval != str || agval != DComplex(i+2)) { cout << "error in row " << i << ": " << abval << ", " << acval << ", " << adval << ", " << aeval << ", " << afval << ", " << agval << endl; } arr1.get (i, arrval); if (!allEQ (arrval, arrf)) { cout << "error in arr1 in row " << i << endl; } arr2.get (i, arrval); if (!allEQ (arrval, arrf)) { cout << "error in arr2 in row " << i << endl; } cout << "get array row " << i << endl; arr3.get (i, arrval); if (!allEQ (arrval, arrf)) { cout << "error in arr3 in row " << i << endl; } arr2.getSlice (i, nslice, arrval); if (!allEQ (arrval, arrf)) { cout << "error in arr2 (entire slice) in row " << i << endl; } arr2.getSlice (i, nslice2, arrvalslice); if (!allEQ (arrval, arrf)) { cout << "error in arr2 (partial slice) in row " << i << endl; } arrf += (float)(arrf.nelements()); } Vector abvec = ab2.getColumn(); cout << tab.nrow() << " " << abvec.nelements() << endl; for (i=0; i<10; i++) { if (abvec(i) != Int(i)) { cout << "error in getColumn " << i << ": " << abvec(i) << endl; } } Array arr1a = arr1.getColumn(); if (arr1a.ndim() != 4) { cout << "arr1a not 4-dim" << endl; } i=0; uInt j0,j1,j2,j3; for (j3=0; j3<10; j3++) for (j2=0; j2<4; j2++) for (j1=0; j1<3; j1++) for (j0=0; j0<2; j0++) { if (arr1a(IPosition(4,j0,j1,j2,j3)) != i++) { cout <<"arr1a error at " < arr1b = arr1.getColumn(nslice); if (arr1b.ndim() != 4) { cout << "arr1b not 4-dim" << endl; } i=0; for (j3=0; j3<10; j3++) for (j2=0; j2<4; j2++) for (j1=0; j1<3; j1++) for (j0=0; j0<2; j0++) { if (arr1b(IPosition(4,j0,j1,j2,j3)) != i++) { cout <<"arr1b error at " < sorae(sortab, "ae"); cout << sorae.getColumn() << endl; cout << "#columns in sortab: " << sortab.tableDesc().ncolumn() << endl; Table sortab2 = sortab.sort ("ad"); if (sortab2.nrow() != 10) { cout << "sortab2 does not contain 10 rows" << endl; } ScalarColumn sorad(sortab2, "ad"); cout << sorad.getColumn() << endl; cout << "#columns in sortab2: " << sortab2.tableDesc().ncolumn() << endl; // Get a subset of the table via row numbers. Vector rownrs(4); rownrs(0)=3; rownrs(1)=1; rownrs(2)=9; rownrs(3)=6; Table seltab1 = sortab(rownrs); if (seltab1.nrow() != 4) { cout << "seltab1 does not contain 4 rows" << endl; } ScalarColumn sel1ab(seltab1, "ab"); cout << sel1ab.getColumn() << endl; cout << "#columns in seltab1: " << seltab1.tableDesc().ncolumn() << endl; // Project the table. Block projname(3); projname[0] = "ae"; projname[1] = "ab"; projname[2] = "arr1"; Table seltab2 = seltab1.project (projname); if (seltab2.nrow() != 4) { cout << "seltab2 does not contain 4 rows" << endl; } ScalarColumn sel2ab(seltab2, "ab"); cout << sel2ab.getColumn() << endl; cout << "#columns in seltab2: " << seltab2.tableDesc().ncolumn() << endl; // Get a subset via a mask. Block mask(4,True); mask[0] = False; mask[3] = False; Table seltab3 = seltab2(mask); if (seltab3.nrow() != 2) { cout << "seltab3 does not contain 2 rows" << endl; } ScalarColumn sel3ab(seltab3, "ab"); cout << sel3ab.getColumn() << endl; cout << "#columns in seltab3: " << seltab3.tableDesc().ncolumn() << endl; cout << seltab3.tableDesc().columnNames() << endl; Table xortab = sortab ^ seltab1; if (xortab.nrow() != 6) { cout << "xortab does not contain 6 rows" << endl; } ScalarColumn xorab(xortab, "ab"); cout << xorab.getColumn() << endl; cout << "#columns in xortab: " << xortab.tableDesc().ncolumn() << endl; Table or1tab = xortab | seltab3; if (or1tab.nrow() != 8) { cout << "or1tab does not contain 8 rows" << endl; } ScalarColumn or1ab(or1tab, "ab"); cout << or1ab.getColumn() << endl; cout << "#columns in or1tab: " << or1tab.tableDesc().ncolumn() << endl; Table or2tab = seltab3 | xortab; if (or2tab.nrow() != 8) { cout << "or2tab does not contain 8 rows" << endl; } ScalarColumn or2ab(or2tab, "ab"); cout << or2ab.getColumn() << endl; cout << "#columns in or2tab: " << or2tab.tableDesc().ncolumn() << endl; Table exprtab = sortab(sortab.col("ab") >= 5); if (exprtab.nrow() != 5) { cout << "exprtab does not contain 5 rows" << endl; } ScalarColumn exprab(exprtab, "ab"); cout << exprab.getColumn() << endl; Table expr2tab = tab(tab.col("af") == "V3" || (tab.col("ab") >= 5 && tab.col("ab") < 8)); if (expr2tab.nrow() != 4) { cout << "expr2tab does not contain 4 rows" << endl; } ScalarColumn expr2ab(expr2tab, "ab"); cout << expr2ab.getColumn() << endl; doAddColumn(tab); } void doIt1 (const String& tableName) { cout << ">>> -------------------" << endl; cout << "start reading Tables: " << tableName << endl; cout << "<<<" << endl; Table tab(tableName); cout << "end reading Tables" << endl; rownr_t nr = tab.nrow(); if (nr < 3) { cout << "Table must have at least 3 rows" << endl; return; } Block
      • tabs(2); tabs[0] = tab(tab.nodeRownr() < nr/3); tabs[1] = tab(tab.nodeRownr() >= nr/3); Table ctab(tabs); doIt (ctab); } void doItMult (const Block& tableNames) { cout << ">>> -------------------" << endl; cout << "start reading Tables: " << tableNames << endl; cout << "<<<" << endl; Table tab(tableNames); cout << "end reading Tables" << endl; doIt (tab); } int main (int argc, const char* argv[]) { // Only execute when table names have been given. try { if (argc > 1) { if (argc == 2) { doIt1 (argv[1]); } else { Block names(argc-1); for (int i=1; i>> ------------------- start reading Tables: tTable_2.data_v0 <<< end reading Tables ab ad ag arr1 arr2 arr3 ac ae af get scalar row 0 get array row 0 get scalar row 1 get array row 1 get scalar row 2 get array row 2 get scalar row 3 get array row 3 get scalar row 4 get array row 4 get scalar row 5 get array row 5 get scalar row 6 get array row 6 get scalar row 7 get array row 7 get scalar row 8 get array row 8 get scalar row 9 get array row 9 10 10 [12, 11, 10, 9, 8, 7, 6, 5, 4, 3] #columns in sortab: 9 [2, 3, 4, 5, 6, 7, 8, 9, 10, 11] #columns in sortab2: 9 [6, 8, 0, 3] #columns in seltab1: 9 [6, 8, 0, 3] #columns in seltab2: 3 [8, 0] #columns in seltab3: 3 [ae, ab, arr1] [1, 2, 4, 5, 7, 9] #columns in xortab: 9 [0, 1, 2, 4, 5, 7, 8, 9] #columns in or1tab: 9 [0, 1, 2, 4, 5, 7, 8, 9] #columns in or2tab: 3 [9, 8, 7, 6, 5] [3, 5, 6, 7] casacore-3.7.1/tables/Tables/test/tConcatTable.run000077500000000000000000000002401476623553700220570ustar00rootroot00000000000000#!/bin/sh # The table gets changed, so use a copy. cp -r $testsrcdir/tTable_2.data_v0 tConcatTable_tmp.tab $casa_checktool ./tConcatTable tConcatTable_tmp.tab casacore-3.7.1/tables/Tables/test/tConcatTable2.cc000066400000000000000000000223031476623553700217230ustar00rootroot00000000000000//# tConcatTable2.cc: Test program for the ConcatTable class //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Test program for the ConcatTable class // // First build a description. void fill(const String& name, const String& name2, Int stval) { const Int nrrow = 10; { // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.addColumn (ScalarColumnDesc("abool")); td.addColumn (ScalarColumnDesc("auchar")); td.addColumn (ScalarColumnDesc("ashort")); td.addColumn (ScalarColumnDesc("aint")); td.addColumn (ScalarColumnDesc("auint")); td.addColumn (ScalarColumnDesc("afloat")); td.addColumn (ScalarColumnDesc("adouble")); td.addColumn (ScalarColumnDesc("acomplex")); td.addColumn (ScalarColumnDesc("adcomplex")); td.addColumn (ScalarColumnDesc("astring")); // Now create a new table from the description. SetupNewTable newtab(name, td, Table::New); Table tab(newtab, nrrow); tab.rwKeywordSet().define ("key1", 1); tab.rwKeywordSet().define ("key2", "abc"); } // Fill the table by means of a concatenated table. // Note the parts are not consecutive in the original table. Block
        tabs(3); { Table tab(name, Table::Update); tabs[0] = tab(tab.nodeRownr() < 3); tabs[1] = tab(tab.nodeRownr() >= 8); tabs[2] = tab(tab.nodeRownr() >=3 && tab.nodeRownr() < 8); } // Now create the ConcatTable and fill it. Table tab(tabs); AlwaysAssertExit (tab.nrow() == 10); AlwaysAssertExit (tab.keywordSet().nfields() == 2); ScalarColumn abool(tab, "abool"); ScalarColumn auchar(tab, "auchar"); ScalarColumn ashort(tab, "ashort"); ScalarColumn aint(tab, "aint"); ScalarColumn auint(tab, "auint"); ScalarColumn afloat(tab, "afloat"); ScalarColumn adouble(tab,"adouble"); ScalarColumn acomplex(tab, "acomplex"); ScalarColumn adcomplex(tab, "adcomplex"); ScalarColumn astring(tab, "astring"); char str[16]; for (Int i=0; i(1,name)); AlwaysAssertExit (tab.keywordSet().nfields() == 2); tab.rwKeywordSet().define ("key3", "def"); tab.rwKeywordSet().define ("key4", 3.14); tab.rename (name2, Table::New); AlwaysAssertExit (tab.keywordSet().nfields() == 4); AlwaysAssertExit (tab.keywordSet().asInt("key1") == 1); AlwaysAssertExit (tab.keywordSet().asString("key2") == "abc"); AlwaysAssertExit (tab.keywordSet().asString("key3") == "def"); AlwaysAssertExit (tab.keywordSet().asDouble("key4") == 3.14); } } } void checkTable (const Table& tab, uInt nkey, uInt nsubrow, Int stval, Bool reorder=True, uInt nrow=10) { AlwaysAssertExit (tab.nrow() == nrow); AlwaysAssertExit (tab.keywordSet().nfields() == nkey); AlwaysAssertExit (tab.keywordSet().asInt("key1") == 1); AlwaysAssertExit (tab.keywordSet().asString("key2") == "abc"); if (nkey == 3) { AlwaysAssertExit (tab.keywordSet().asTable("keysub").nrow() == nsubrow); } ScalarColumn abool(tab, "abool"); ScalarColumn auchar(tab, "auchar"); ScalarColumn ashort(tab, "ashort"); ScalarColumn aint(tab, "aint"); ScalarColumn auint(tab, "auint"); ScalarColumn afloat(tab, "afloat"); ScalarColumn adouble(tab,"adouble"); ScalarColumn acomplex(tab, "acomplex"); ScalarColumn adcomplex(tab, "adcomplex"); ScalarColumn astring(tab, "astring"); char str[8]; // Values are stored as: 0 1 2 5 6 7 8 9 3 4 for (uInt i=0; i=5) { row-=2; } else if (row>=3) { row+=5; } } row += rowd; AlwaysAssertExit (abool(row) == (stval%2==0)); AlwaysAssertExit (auchar(row) == stval); AlwaysAssertExit (ashort(row) == stval); AlwaysAssertExit (aint(row) == stval); AlwaysAssertExit (auint(row) == uInt(stval)); AlwaysAssertExit (afloat(row) == stval); AlwaysAssertExit (adouble(row) == stval); AlwaysAssertExit (acomplex(row) == Complex(stval,0)); AlwaysAssertExit (adcomplex(row) == DComplex(0,stval)); snprintf (str, sizeof(str), "V%i", stval); AlwaysAssertExit (astring(row) == str); ++stval; } } void check (const String& name, uInt nkey, Int stval) { checkTable (Table(name), nkey, 10, stval); } void checkComb (const String& name1, const String& name2, uInt nkey, Int stval) { Block
        tabs(2); tabs[0] = Table(name1); tabs[1] = Table(name2); Table tab(tabs); checkTable (tab, nkey, 10, stval, True, 20); } void checkSplit (const String& name, uInt nkey, Int stval) { Table tab(name); // Split and concatenate the table such that we get the original order. // Values are stored as: 0 1 2 5 6 7 8 9 3 4 Block
        tabs(3); tabs[0] = tab(tab.nodeRownr() < 3); tabs[1] = tab(tab.nodeRownr() >= 8); tabs[2] = tab(tab.nodeRownr() >=3 && tab.nodeRownr() < 8); checkTable (Table(tabs), nkey, 10, stval, False); } void checkFull (const String& name, Int stval) { Table tab(name); // Make a ConcatTable of a table for each row. Block
        tabs(10); for (uInt i=0; i<10; ++i) { tabs[i] = tab(tab.nodeRownr() == i); } // Concatenate the subtable, so we get a subtable of 10*10 rows. Table ctab(tabs, Block(1,"keysub")); checkTable (ctab, 3, 100, stval); // Check each subtable. Table subtab = ctab.keywordSet().asTable("keysub"); // Check if each part of 10 rows is correct. for (Int i=0; i<10; ++i) { checkTable(subtab(subtab.nodeRownr()>=10*i && subtab.nodeRownr()<10*(i+1)), 2, 10, 21); } } void fillSub(const String& name, const String& subname) { Table tab(name, Table::Update); fill (subname, String(), 21); Table subtab(subname); tab.rwKeywordSet().defineTable("keysub", subtab); } int main() { try { fill("tConcatTable2_tmp.data", "tConcatTable2_tmp.data2", 0); cout<< "done fill 0" << endl; fill("tConcatTable2_tmp.datb", "tConcatTable2_tmp.datb2", 10); cout<< "done fill 10" << endl; check("tConcatTable2_tmp.data", 2, 0); cout<< "done check 0" << endl; check("tConcatTable2_tmp.datb", 2, 10); cout<< "done check 10" << endl; checkComb("tConcatTable2_tmp.data", "tConcatTable2_tmp.datb", 2, 0); cout << "done checkComb" << endl; checkComb("tConcatTable2_tmp.data2", "tConcatTable2_tmp.datb2", 2, 0); cout << "done checkComb2" << endl; check("tConcatTable2_tmp.data2", 2, 0); cout<< "done check2" << endl; checkSplit("tConcatTable2_tmp.data", 2, 0); cout<< "done checkSplit" << endl; checkSplit("tConcatTable2_tmp.data2", 2, 0); cout<< "done checkSplit2" << endl; fillSub("tConcatTable2_tmp.data", "tConcatTable2_tmp.datasub"); cout<< "done fillSub" << endl; check("tConcatTable2_tmp.data", 3, 0); cout<< "done check" << endl; checkFull("tConcatTable2_tmp.data", 0); cout<< "done checkFull" << endl; } catch (std::exception& x) { cout << "Exception caught: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/tables/Tables/test/tConcatTable3.cc000066400000000000000000000074171476623553700217350ustar00rootroot00000000000000//# tConcatTable3.cc: Test program for the ConcatTable class //# Copyright (C) 2008 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Test program for the ConcatTable class // // First build a description. void createTable(const String& name, Int stval, Int nrrow) { // Build the table description. TableDesc td; td.addColumn (ScalarColumnDesc("aint")); td.addColumn (ScalarColumnDesc("afloat")); // Now create a new table from the description. SetupNewTable newtab(name, td, Table::New); Table tab(newtab, nrrow); // Fill the table. ScalarColumn icol(tab, "aint"); ScalarColumn fcol(tab, "afloat"); for (Int i=0; i aint(tab, "aint"); ScalarColumn afloat(tab, "afloat"); for (uInt i=0; i names(3); names[0] = "tConcatTable3_tmp.tab1"; names[1] = "tConcatTable3_tmp.tab2"; names[2] = "tConcatTable3_tmp.tab3"; Table concTab (names, Block(), Table::Old, TSMOption(), "SUBDIR"); concTab.rename ("tConcatTable3_tmp.conctab", Table::New); } int main() { try { createTable ("tConcatTable3_tmp.tab1", 0, 10); createTable ("tConcatTable3_tmp.tab2", 10, 20); createTable ("tConcatTable3_tmp.tab3", 30, 5); concatTables(); checkTable (0, 35); } catch (std::exception& x) { cout << "Exception caught: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/tables/Tables/test/tIncrementalStMan2.cc000066400000000000000000000111411476623553700227460ustar00rootroot00000000000000//# tIncrementalStMan2.cc: Test program for the IncrementalStMan storage manager //# Copyright (C) 2014 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Test program for the IncrementalStMan storage manager // // This program tests the IncrementalStMan storage manager, especially the // get and put functions. // The results are written to stdout. The script executing this program, // compares the results with the reference output file. // First build a description. uInt makeTab (uInt bucketSize) { Table tab; DataManager::registerCtor ("IncrementalStMan", IncrementalStMan::makeObject); // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.addColumn (ScalarColumnDesc("c1")); td.addColumn (ScalarColumnDesc("c2")); // Now create a new table from the description. SetupNewTable newtab("tIncrementalStMan2_tmp.data", td, Table::New); // Create a storage manager for it. IncrementalStMan sm1 ("ISM", bucketSize, False); StandardStMan sm2 ("SSM"); newtab.bindColumn ("c1", sm1); newtab.bindColumn ("c2", sm2); tab = Table (newtab, 100000); ScalarColumn c1(tab,"c1"); ScalarColumn c2(tab,"c2"); for (uInt i=0; i c1(tab,"c1"); ScalarColumn c2(tab,"c2"); Vector a1 = c1.getColumn(); Vector a2 = c2.getColumn(); for (uInt i=0; i 0) { Table tab("tIncrementalStMan2_tmp.data", Table::Update); ROIncrementalStManAccessor acc(tab, "ISM"); ScalarColumn c1(tab,"c1"); ScalarColumn c2(tab,"c2"); cout << "updateTab step=" << step << endl; for (uInt i=step; i 1) { istringstream istr(argv[1]); istr >> bucketSize; } try { uInt nrow = makeTab (bucketSize); checkTab(); // Now update some rows. // Do it in the middle, so ISM has to split buckets. uInt step = 2; for (uInt i=0; i<16; ++i) { updateTab (nrow/step); step *= 2; } } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } casacore-3.7.1/tables/Tables/test/tMemoryTable.cc000066400000000000000000000424561476623553700217150ustar00rootroot00000000000000//# tMemoryTable.cc: Test program for the MemoryTable class //# Copyright (C) 2003,2004 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Test program for the MemoryTable class. // // (re) open and write a few rows // aMode == 0 open new // aMode == 1 use existing void init (uInt aMode, Table&); // reopen table, and throw away a row void deleteRow(const uInt aRow, Table&); // reopen table, and throw away a few rows void deleteRows(const Vector& aNrRows, Table&); // delete a Column void deleteColumn(const String aColumn, Table&); // delete a column && put it back again void deleteAndRestore(Table&); // add a Column void addColumn(DataType aDataType, Table&); // add a few direct arrays void addDirectArrays(Table&); // add an indirect string array void addIndStringArray(Table&); // add an indirect array void addIndArray(Table&); // add a keyword and subtable void addKeys(Table&); // show table info void info(const Table& aTable); // put/putColumn cache test void putColumnTest(Table&); // Copy the table to a plain table void copyTable(const Table&); // Copy a reftable to the memory table to a plain table void copyRefTable(const Table&); // Copy the table to a memory table void copyMemoryTable(const Table&); // Copy a subset of a table to a memory table void copyMemoryTableSubSet(const Table&); int main () { try { Table tab; init (0, tab); init (1, tab); deleteRow (0, tab); deleteRow (17, tab); // delete and restore Column 1 (should use perfect fit) deleteAndRestore(tab); // putColumnTest(tab); // delete middle Column deleteColumn ("Col-2", tab); // add a Bool Column Should fit in freed space addColumn (TpBool, tab); // add a DComplex Column Should use new index && space addColumn (TpDComplex, tab); // Test the copy table stuff. { cout << endl << "Test copying ..." << endl; Table tabc = tab.copyToMemoryTable ("tmtestc"); // Add a keyword and a subtable. addKeys (tabc); // copy to a plain table copyTable(tabc); // copy a reference to a plain table /// copyRefTable(tabc); // copy to a memory table copyMemoryTable(tabc); // copy a subset to a memory table copyMemoryTableSubSet(tabc); // copy from a plain table { Table tab2("tMemoryTable_tmp.tabcp"); copyMemoryTable(tab2); copyMemoryTableSubSet(tab2); } cout << endl; } // delete first Column deleteColumn ("Col-1", tab); // delete last Column deleteColumn ("Col-3", tab); addDirectArrays (tab); addIndStringArray(tab); addIndArray (tab); Vector aNrRows(3); for (uInt i=0; i< 3; i++) { aNrRows(i) = i+3; } deleteRows (aNrRows, tab); deleteColumn ("Col-7", tab); addColumn (TpString, tab); // remove all remaining rows to check freebucket performance Vector aNewNrRows(15); for (uInt i=0; i< 15; i++) { aNewNrRows(i) = i; } deleteRows (aNewNrRows, tab); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } void initArrays (Cube& arrf, Vector& arrdc, Cube& arrb) { // The static_cast is a workaround for an SGI compiler bug indgen (static_cast< Cube &>(arrf)); arrdc(0) = DComplex(1.2, 3.4); arrdc(1) = DComplex(-2.3, 5.6); IPosition shape(arrb.shape()); uInt n = 0; for (Int i=0; i ad(aTable,cdesc.name()); cout << ad.getColumn() << endl; } else { ScalarColumn aa(aTable,cdesc.name()); cout << aa.getColumn() << endl; } break; case TpBool: if (cdesc.isArray()) { ArrayColumn ad(aTable,cdesc.name()); cout << ad.getColumn() << endl; } else { ScalarColumn ab(aTable,cdesc.name()); cout << ab.getColumn() << endl; } break; case TpDComplex: if (cdesc.isArray()) { ArrayColumn ad(aTable,cdesc.name()); cout << ad.getColumn() << endl; } else { ScalarColumn ac(aTable,cdesc.name()); cout << ac.getColumn() << endl; } break; case TpFloat: if (cdesc.isArray()) { ArrayColumn ad(aTable,cdesc.name()); cout << ad.getColumn() << endl; } else { ScalarColumn ad(aTable,cdesc.name()); cout << ad.getColumn() << endl; } break; case TpDouble: if (cdesc.isArray()) { ArrayColumn ad(aTable,cdesc.name()); cout << ad.getColumn() << endl; } else { ScalarColumn ad(aTable,cdesc.name()); cout << ad.getColumn() << endl; } break; case TpString: if (cdesc.isArray()) { ArrayColumn ad(aTable,cdesc.name()); cout << ad.getColumn() << endl; } else { ScalarColumn ad(aTable,cdesc.name()); cout << ad.getColumn() << endl; } break; default: cout << "Sorry, datatype not implemented yet." << endl; } } const TableRecord& rec = aTable.keywordSet(); for (uInt i=0; i("Col-5")); if (aTable.tableDesc().isColumn("Col-5")) { ae.attach(aTable,"Col-5"); } // fill new column with data for (uInt i=0; i("Col-10")); if (aTable.tableDesc().isColumn("Col-10")) { aj.attach(aTable,"Col-10"); } String aString("String-1"); // fill new column with data for (uInt i=0; i af; ArrayColumn ag; ArrayColumn ah; cout << "Trying to add a few Direct Array Columns." << endl; aTable.addColumn(ArrayColumnDesc("Col-6", IPosition(3,2,3,1), ColumnDesc::Direct)); aTable.addColumn(ArrayColumnDesc("Col-7", IPosition(1,2), ColumnDesc::Direct)); aTable.addColumn(ArrayColumnDesc("Col-8", IPosition(3,5,7,1), ColumnDesc::Direct)); Cube arrf(IPosition(3,2,3,1)); Vector arrdc(2); Cube arrb(IPosition(3,5,7,1)); initArrays (arrf, arrdc, arrb); if (aTable.tableDesc().isColumn("Col-6")) { af.attach(aTable,"Col-6"); } if (aTable.tableDesc().isColumn("Col-7")) { ag.attach(aTable,"Col-7"); } if (aTable.tableDesc().isColumn("Col-8")) { ah.attach(aTable,"Col-8"); } for (uInt i=0; i ai; cout << "Trying to add an indirect String Array Column." << endl; aTable.addColumn(ArrayColumnDesc("Col-9")); Vector arrs(5); arrs(0)="Start-1"; arrs(1)="Start-2"; arrs(2)="Start-3"; arrs(3)="Start-4"; arrs(4)="Start-5"; if (aTable.tableDesc().isColumn("Col-9")) { ai.attach(aTable,"Col-9"); } for (uInt i=0; i ak; cout << "Trying to add an indirect Array Column." << endl; aTable.addColumn(ArrayColumnDesc("Col-11")); Vector arrs(5); arrs(0)=1; arrs(1)=2; arrs(2)=3; arrs(3)=4; arrs(4)=5; if (aTable.tableDesc().isColumn("Col-11")) { ak.attach(aTable,"Col-11"); } for (uInt i=0; i ("str")); SetupNewTable aNewTab("tmtest", td, Table::New); Table aTable (aNewTab, Table::Memory, 2); ScalarColumn sc(aTable, "str"); sc.put (0, "str1"); sc.put (0, "s1"); sc.put (1, "sstr2"); tab.rwKeywordSet().defineTable ("Subtab", aTable); } void putColumnTest (Table& aTable) { ScalarColumn ab(aTable,"Col-2"); // put value 3 in rownr 5 ab.put(5,3); AlwaysAssertExit(ab(5) == 3); // put value 4 in column ab.putColumn(ab.getColumn() + 1); AlwaysAssertExit (ab(5) == 4); } void copyTable (const Table& aTable) { cout << "Try to rename and copy the table" << endl; Table tab(aTable); cout << "old name = " << Path(tab.tableName()).baseName(); tab.rename ("mt_newname", Table::Scratch); AlwaysAssertExit (!tab.isColumnStored ("Colvirt")); cout << " new name = " << Path(tab.tableName()).baseName() << endl; aTable.copy ("tMemoryTable_tmp.tabcp", Table::New); Table tabc("tMemoryTable_tmp.tabcp"); cout << "copy name = " << Path(tabc.tableName()).baseName() << endl; AlwaysAssertExit (!tabc.isColumnStored ("Colvirt")); info(tabc); } void copyRefTable (const Table& aTable) { cout << "Try to copy a referenced memory table" << endl; Table tab (aTable(aTable.col("Colvirt") < 10)); AlwaysAssertExit (!tab.isColumnStored ("Colvirt")); tab.deepCopy ("tMemoryTable_tmp.tabcpr", Table::New); Table tabc("tMemoryTable_tmp.tabcpr"); cout << "copy name = " << Path(tabc.tableName()).baseName() << endl; AlwaysAssertExit (!tabc.isColumnStored ("Colvirt")); info(tabc); cout << tab.dataManagerInfo() << endl; cout << tabc.dataManagerInfo() << endl; } void copyMemoryTable (const Table& aTable) { cout << "Try to copy the table to a MemoryTable" << endl; Table tab(aTable); AlwaysAssertExit (!tab.isColumnStored ("Colvirt")); cout << "name = " << Path(tab.tableName()).baseName() << endl; Table tabc = tab.copyToMemoryTable ("tMemoryTable.dat"); cout << "copy name = " << Path(tabc.tableName()).baseName() << endl; AlwaysAssertExit (!tabc.isColumnStored ("Colvirt")); info(tabc); } void copyMemoryTableSubSet (const Table& aTable) { cout << "Try to copy a table subset to a MemoryTable" << endl; Table tab = aTable(aTable.col("Col-3")); Table tabc = tab.copyToMemoryTable ("tMemoryTable.subdat"); cout << "copy name = " << Path(tabc.tableName()).baseName() << endl; info(tabc); } casacore-3.7.1/tables/Tables/test/tMemoryTable.out000066400000000000000000001465221476623553700221360ustar00rootroot00000000000000after creation TableType = 1 Col-1: DComplex [(0,0), (1,2), (2,4), (3,6), (4,8), (5,10), (6,12), (7,14), (8,16), (9,18)] Col-2: Int [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Col-3: Bool [1, 0, 1, 0, 1, 0, 1, 0, 1, 0] Colvirt: Int [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] after reopening and adding 10 rows TableType = 1 Col-1: DComplex [(0,0), (1,2), (2,4), (3,6), (4,8), (5,10), (6,12), (7,14), (8,16), (9,18), (10,20), (11,22), (12,24), (13,26), (14,28), (15,30), (16,32), (17,34), (18,36), (19,38)] Col-2: Int [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] Col-3: Bool [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0] Colvirt: Int [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40] after removing row: 0 TableType = 1 Col-1: DComplex [(1,2), (2,4), (3,6), (4,8), (5,10), (6,12), (7,14), (8,16), (9,18), (10,20), (11,22), (12,24), (13,26), (14,28), (15,30), (16,32), (17,34), (18,36), (19,38)] Col-2: Int [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] Col-3: Bool [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0] Colvirt: Int [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38] after removing row: 17 TableType = 1 Col-1: DComplex [(1,2), (2,4), (3,6), (4,8), (5,10), (6,12), (7,14), (8,16), (9,18), (10,20), (11,22), (12,24), (13,26), (14,28), (15,30), (16,32), (17,34), (19,38)] Col-2: Int [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 19] Col-3: Bool [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0] Colvirt: Int [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36] Try to remove Column 1 after saving the contents After removing Column 1 TableType = 1 Col-2: Int [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 19] Col-3: Bool [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0] Colvirt: Int [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36] Try to restore Column 1 After restoring Column1: TableType = 1 Col-2: Int [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 19] Col-3: Bool [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0] Colvirt: Int [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36] Col-1: DComplex [(1,2), (2,4), (3,6), (4,8), (5,10), (6,12), (7,14), (8,16), (9,18), (10,20), (11,22), (12,24), (13,26), (14,28), (15,30), (16,32), (17,34), (19,38)] Try to remove Column:Col-2 After removing Column: Col-2 TableType = 1 Col-3: Bool [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0] Colvirt: Int [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36] Col-1: DComplex [(1,2), (2,4), (3,6), (4,8), (5,10), (6,12), (7,14), (8,16), (9,18), (10,20), (11,22), (12,24), (13,26), (14,28), (15,30), (16,32), (17,34), (19,38)] Try to add Column: Col-4 and fill it. It Should be using space just freed up TableType = 1 Col-3: Bool [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0] Colvirt: Int [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36] Col-1: DComplex [(1,2), (2,4), (3,6), (4,8), (5,10), (6,12), (7,14), (8,16), (9,18), (10,20), (11,22), (12,24), (13,26), (14,28), (15,30), (16,32), (17,34), (19,38)] Col-4: Bool [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] Try to add Column: Col-5 and fill it. It should make a new index because there's not enough free space TableType = 1 Col-3: Bool [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0] Colvirt: Int [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36] Col-1: DComplex [(1,2), (2,4), (3,6), (4,8), (5,10), (6,12), (7,14), (8,16), (9,18), (10,20), (11,22), (12,24), (13,26), (14,28), (15,30), (16,32), (17,34), (19,38)] Col-4: Bool [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] Col-5: DComplex [(18,0), (17,1), (16,2), (15,3), (14,4), (13,5), (12,6), (11,7), (10,8), (9,9), (8,10), (7,11), (6,12), (5,13), (4,14), (3,15), (2,16), (1,17)] Test copying ... Try to rename and copy the table old name = tmtestc new name = mt_newname copy name = tMemoryTable_tmp.tabcp TableType = 0 Col-3: Bool [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0] Colvirt: Int [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36] Col-1: DComplex [(1,2), (2,4), (3,6), (4,8), (5,10), (6,12), (7,14), (8,16), (9,18), (10,20), (11,22), (12,24), (13,26), (14,28), (15,30), (16,32), (17,34), (19,38)] Col-4: Bool [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] Col-5: DComplex [(18,0), (17,1), (16,2), (15,3), (14,4), (13,5), (12,6), (11,7), (10,8), (9,9), (8,10), (7,11), (6,12), (5,13), (4,14), (3,15), (2,16), (1,17)] Key1=abc Subtab=Record TableType = 0 str: String [s1, sstr2] Try to copy the table to a MemoryTable name = mt_newname copy name = tMemoryTable.dat TableType = 1 Col-3: Bool [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0] Colvirt: Int [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36] Col-1: DComplex [(1,2), (2,4), (3,6), (4,8), (5,10), (6,12), (7,14), (8,16), (9,18), (10,20), (11,22), (12,24), (13,26), (14,28), (15,30), (16,32), (17,34), (19,38)] Col-4: Bool [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] Col-5: DComplex [(18,0), (17,1), (16,2), (15,3), (14,4), (13,5), (12,6), (11,7), (10,8), (9,9), (8,10), (7,11), (6,12), (5,13), (4,14), (3,15), (2,16), (1,17)] Key1=abc Subtab=Record TableType = 1 str: String [s1, sstr2] Try to copy a table subset to a MemoryTable copy name = tMemoryTable.subdat TableType = 1 Col-3: Bool [1, 1, 1, 1, 1, 1, 1, 1] Colvirt: Int [2, 4, 6, 8, 10, 12, 14, 16] Col-1: DComplex [(2,4), (4,8), (6,12), (8,16), (10,20), (12,24), (14,28), (16,32)] Col-4: Bool [1, 1, 1, 1, 1, 0, 0, 0] Col-5: DComplex [(17,1), (15,3), (13,5), (11,7), (9,9), (7,11), (5,13), (3,15)] Key1=abc Subtab=Record TableType = 1 str: String [s1, sstr2] Try to copy the table to a MemoryTable name = tMemoryTable_tmp.tabcp copy name = tMemoryTable.dat TableType = 1 Col-3: Bool [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0] Colvirt: Int [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36] Col-1: DComplex [(1,2), (2,4), (3,6), (4,8), (5,10), (6,12), (7,14), (8,16), (9,18), (10,20), (11,22), (12,24), (13,26), (14,28), (15,30), (16,32), (17,34), (19,38)] Col-4: Bool [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] Col-5: DComplex [(18,0), (17,1), (16,2), (15,3), (14,4), (13,5), (12,6), (11,7), (10,8), (9,9), (8,10), (7,11), (6,12), (5,13), (4,14), (3,15), (2,16), (1,17)] Key1=abc Subtab=Record TableType = 1 str: String [s1, sstr2] Try to copy a table subset to a MemoryTable copy name = tMemoryTable.subdat TableType = 1 Col-3: Bool [1, 1, 1, 1, 1, 1, 1, 1] Colvirt: Int [2, 4, 6, 8, 10, 12, 14, 16] Col-1: DComplex [(2,4), (4,8), (6,12), (8,16), (10,20), (12,24), (14,28), (16,32)] Col-4: Bool [1, 1, 1, 1, 1, 0, 0, 0] Col-5: DComplex [(17,1), (15,3), (13,5), (11,7), (9,9), (7,11), (5,13), (3,15)] Key1=abc Subtab=Record TableType = 1 str: String [s1, sstr2] Try to remove Column:Col-1 After removing Column: Col-1 TableType = 1 Col-3: Bool [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0] Colvirt: Int [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36] Col-4: Bool [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] Col-5: DComplex [(18,0), (17,1), (16,2), (15,3), (14,4), (13,5), (12,6), (11,7), (10,8), (9,9), (8,10), (7,11), (6,12), (5,13), (4,14), (3,15), (2,16), (1,17)] Try to remove Column:Col-3 After removing Column: Col-3 TableType = 1 Colvirt: Int [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36] Col-4: Bool [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] Col-5: DComplex [(18,0), (17,1), (16,2), (15,3), (14,4), (13,5), (12,6), (11,7), (10,8), (9,9), (8,10), (7,11), (6,12), (5,13), (4,14), (3,15), (2,16), (1,17)] Trying to add a few Direct Array Columns. TableType = 1 Colvirt: Int [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36] Col-4: Bool [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] Col-5: DComplex [(18,0), (17,1), (16,2), (15,3), (14,4), (13,5), (12,6), (11,7), (10,8), (9,9), (8,10), (7,11), (6,12), (5,13), (4,14), (3,15), (2,16), (1,17)] Col-6: float Ndim=4 Axis Lengths: [2, 3, 1, 18] [0, 0, 0, 0][0, 1] [0, 1, 0, 0][2, 3] [0, 2, 0, 0][4, 5] [0, 0, 0, 1][6, 7] [0, 1, 0, 1][8, 9] [0, 2, 0, 1][10, 11] [0, 0, 0, 2][12, 13] [0, 1, 0, 2][14, 15] [0, 2, 0, 2][16, 17] [0, 0, 0, 3][18, 19] [0, 1, 0, 3][20, 21] [0, 2, 0, 3][22, 23] [0, 0, 0, 4][24, 25] [0, 1, 0, 4][26, 27] [0, 2, 0, 4][28, 29] [0, 0, 0, 5][30, 31] [0, 1, 0, 5][32, 33] [0, 2, 0, 5][34, 35] [0, 0, 0, 6][36, 37] [0, 1, 0, 6][38, 39] [0, 2, 0, 6][40, 41] [0, 0, 0, 7][42, 43] [0, 1, 0, 7][44, 45] [0, 2, 0, 7][46, 47] [0, 0, 0, 8][48, 49] [0, 1, 0, 8][50, 51] [0, 2, 0, 8][52, 53] [0, 0, 0, 9][54, 55] [0, 1, 0, 9][56, 57] [0, 2, 0, 9][58, 59] [0, 0, 0, 10][60, 61] [0, 1, 0, 10][62, 63] [0, 2, 0, 10][64, 65] [0, 0, 0, 11][66, 67] [0, 1, 0, 11][68, 69] [0, 2, 0, 11][70, 71] [0, 0, 0, 12][72, 73] [0, 1, 0, 12][74, 75] [0, 2, 0, 12][76, 77] [0, 0, 0, 13][78, 79] [0, 1, 0, 13][80, 81] [0, 2, 0, 13][82, 83] [0, 0, 0, 14][84, 85] [0, 1, 0, 14][86, 87] [0, 2, 0, 14][88, 89] [0, 0, 0, 15][90, 91] [0, 1, 0, 15][92, 93] [0, 2, 0, 15][94, 95] [0, 0, 0, 16][96, 97] [0, 1, 0, 16][98, 99] [0, 2, 0, 16][100, 101] [0, 0, 0, 17][102, 103] [0, 1, 0, 17][104, 105] [0, 2, 0, 17][106, 107] Col-7: DComplex Axis Lengths: [2, 18] (NB: Matrix in Row/Column order) [(1.2,3.4), (3.2,6.4), (5.2,9.4), (7.2,12.4), (9.2,15.4), (11.2,18.4), (13.2,21.4), (15.2,24.4), (17.2,27.4), (19.2,30.4), (21.2,33.4), (23.2,36.4), (25.2,39.4), (27.2,42.4), (29.2,45.4), (31.2,48.4), (33.2,51.4), (35.2,54.4) (-2.3,5.6), (-0.3,8.6), (1.7,11.6), (3.7,14.6), (5.7,17.6), (7.7,20.6), (9.7,23.6), (11.7,26.6), (13.7,29.6), (15.7,32.6), (17.7,35.6), (19.7,38.6), (21.7,41.6), (23.7,44.6), (25.7,47.6), (27.7,50.6), (29.7,53.6), (31.7,56.6)] Col-8: Bool Ndim=4 Axis Lengths: [5, 7, 1, 18] [0, 0, 0, 0][0, 0, 1, 0, 0] [0, 1, 0, 0][1, 0, 0, 1, 0] [0, 2, 0, 0][0, 1, 0, 0, 1] [0, 3, 0, 0][0, 0, 1, 0, 0] [0, 4, 0, 0][1, 0, 0, 1, 0] [0, 5, 0, 0][0, 1, 0, 0, 1] [0, 6, 0, 0][0, 0, 1, 0, 0] [0, 0, 0, 1][0, 0, 1, 0, 0] [0, 1, 0, 1][1, 0, 0, 1, 0] [0, 2, 0, 1][0, 1, 0, 0, 1] [0, 3, 0, 1][0, 0, 1, 0, 0] [0, 4, 0, 1][1, 0, 0, 1, 0] [0, 5, 0, 1][0, 1, 0, 0, 1] [0, 6, 0, 1][0, 0, 1, 0, 0] [0, 0, 0, 2][0, 0, 1, 0, 0] [0, 1, 0, 2][1, 0, 0, 1, 0] [0, 2, 0, 2][0, 1, 0, 0, 1] [0, 3, 0, 2][0, 0, 1, 0, 0] [0, 4, 0, 2][1, 0, 0, 1, 0] [0, 5, 0, 2][0, 1, 0, 0, 1] [0, 6, 0, 2][0, 0, 1, 0, 0] [0, 0, 0, 3][0, 0, 1, 0, 0] [0, 1, 0, 3][1, 0, 0, 1, 0] [0, 2, 0, 3][0, 1, 0, 0, 1] [0, 3, 0, 3][0, 0, 1, 0, 0] [0, 4, 0, 3][1, 0, 0, 1, 0] [0, 5, 0, 3][0, 1, 0, 0, 1] [0, 6, 0, 3][0, 0, 1, 0, 0] [0, 0, 0, 4][0, 0, 1, 0, 0] [0, 1, 0, 4][1, 0, 0, 1, 0] [0, 2, 0, 4][0, 1, 0, 0, 1] [0, 3, 0, 4][0, 0, 1, 0, 0] [0, 4, 0, 4][1, 0, 0, 1, 0] [0, 5, 0, 4][0, 1, 0, 0, 1] [0, 6, 0, 4][0, 0, 1, 0, 0] [0, 0, 0, 5][0, 0, 1, 0, 0] [0, 1, 0, 5][1, 0, 0, 1, 0] [0, 2, 0, 5][0, 1, 0, 0, 1] [0, 3, 0, 5][0, 0, 1, 0, 0] [0, 4, 0, 5][1, 0, 0, 1, 0] [0, 5, 0, 5][0, 1, 0, 0, 1] [0, 6, 0, 5][0, 0, 1, 0, 0] [0, 0, 0, 6][0, 0, 1, 0, 0] [0, 1, 0, 6][1, 0, 0, 1, 0] [0, 2, 0, 6][0, 1, 0, 0, 1] [0, 3, 0, 6][0, 0, 1, 0, 0] [0, 4, 0, 6][1, 0, 0, 1, 0] [0, 5, 0, 6][0, 1, 0, 0, 1] [0, 6, 0, 6][0, 0, 1, 0, 0] [0, 0, 0, 7][0, 0, 1, 0, 0] [0, 1, 0, 7][1, 0, 0, 1, 0] [0, 2, 0, 7][0, 1, 0, 0, 1] [0, 3, 0, 7][0, 0, 1, 0, 0] [0, 4, 0, 7][1, 0, 0, 1, 0] [0, 5, 0, 7][0, 1, 0, 0, 1] [0, 6, 0, 7][0, 0, 1, 0, 0] [0, 0, 0, 8][0, 0, 1, 0, 0] [0, 1, 0, 8][1, 0, 0, 1, 0] [0, 2, 0, 8][0, 1, 0, 0, 1] [0, 3, 0, 8][0, 0, 1, 0, 0] [0, 4, 0, 8][1, 0, 0, 1, 0] [0, 5, 0, 8][0, 1, 0, 0, 1] [0, 6, 0, 8][0, 0, 1, 0, 0] [0, 0, 0, 9][0, 0, 1, 0, 0] [0, 1, 0, 9][1, 0, 0, 1, 0] [0, 2, 0, 9][0, 1, 0, 0, 1] [0, 3, 0, 9][0, 0, 1, 0, 0] [0, 4, 0, 9][1, 0, 0, 1, 0] [0, 5, 0, 9][0, 1, 0, 0, 1] [0, 6, 0, 9][0, 0, 1, 0, 0] [0, 0, 0, 10][0, 0, 1, 0, 0] [0, 1, 0, 10][1, 0, 0, 1, 0] [0, 2, 0, 10][0, 1, 0, 0, 1] [0, 3, 0, 10][0, 0, 1, 0, 0] [0, 4, 0, 10][1, 0, 0, 1, 0] [0, 5, 0, 10][0, 1, 0, 0, 1] [0, 6, 0, 10][0, 0, 1, 0, 0] [0, 0, 0, 11][0, 0, 1, 0, 0] [0, 1, 0, 11][1, 0, 0, 1, 0] [0, 2, 0, 11][0, 1, 0, 0, 1] [0, 3, 0, 11][0, 0, 1, 0, 0] [0, 4, 0, 11][1, 0, 0, 1, 0] [0, 5, 0, 11][0, 1, 0, 0, 1] [0, 6, 0, 11][0, 0, 1, 0, 0] [0, 0, 0, 12][0, 0, 1, 0, 0] [0, 1, 0, 12][1, 0, 0, 1, 0] [0, 2, 0, 12][0, 1, 0, 0, 1] [0, 3, 0, 12][0, 0, 1, 0, 0] [0, 4, 0, 12][1, 0, 0, 1, 0] [0, 5, 0, 12][0, 1, 0, 0, 1] [0, 6, 0, 12][0, 0, 1, 0, 0] [0, 0, 0, 13][0, 0, 1, 0, 0] [0, 1, 0, 13][1, 0, 0, 1, 0] [0, 2, 0, 13][0, 1, 0, 0, 1] [0, 3, 0, 13][0, 0, 1, 0, 0] [0, 4, 0, 13][1, 0, 0, 1, 0] [0, 5, 0, 13][0, 1, 0, 0, 1] [0, 6, 0, 13][0, 0, 1, 0, 0] [0, 0, 0, 14][0, 0, 1, 0, 0] [0, 1, 0, 14][1, 0, 0, 1, 0] [0, 2, 0, 14][0, 1, 0, 0, 1] [0, 3, 0, 14][0, 0, 1, 0, 0] [0, 4, 0, 14][1, 0, 0, 1, 0] [0, 5, 0, 14][0, 1, 0, 0, 1] [0, 6, 0, 14][0, 0, 1, 0, 0] [0, 0, 0, 15][0, 0, 1, 0, 0] [0, 1, 0, 15][1, 0, 0, 1, 0] [0, 2, 0, 15][0, 1, 0, 0, 1] [0, 3, 0, 15][0, 0, 1, 0, 0] [0, 4, 0, 15][1, 0, 0, 1, 0] [0, 5, 0, 15][0, 1, 0, 0, 1] [0, 6, 0, 15][0, 0, 1, 0, 0] [0, 0, 0, 16][0, 0, 1, 0, 0] [0, 1, 0, 16][1, 0, 0, 1, 0] [0, 2, 0, 16][0, 1, 0, 0, 1] [0, 3, 0, 16][0, 0, 1, 0, 0] [0, 4, 0, 16][1, 0, 0, 1, 0] [0, 5, 0, 16][0, 1, 0, 0, 1] [0, 6, 0, 16][0, 0, 1, 0, 0] [0, 0, 0, 17][0, 0, 1, 0, 0] [0, 1, 0, 17][1, 0, 0, 1, 0] [0, 2, 0, 17][0, 1, 0, 0, 1] [0, 3, 0, 17][0, 0, 1, 0, 0] [0, 4, 0, 17][1, 0, 0, 1, 0] [0, 5, 0, 17][0, 1, 0, 0, 1] [0, 6, 0, 17][0, 0, 1, 0, 0] Trying to add an indirect String Array Column. TableType = 1 Colvirt: Int [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36] Col-4: Bool [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] Col-5: DComplex [(18,0), (17,1), (16,2), (15,3), (14,4), (13,5), (12,6), (11,7), (10,8), (9,9), (8,10), (7,11), (6,12), (5,13), (4,14), (3,15), (2,16), (1,17)] Col-6: float Ndim=4 Axis Lengths: [2, 3, 1, 18] [0, 0, 0, 0][0, 1] [0, 1, 0, 0][2, 3] [0, 2, 0, 0][4, 5] [0, 0, 0, 1][6, 7] [0, 1, 0, 1][8, 9] [0, 2, 0, 1][10, 11] [0, 0, 0, 2][12, 13] [0, 1, 0, 2][14, 15] [0, 2, 0, 2][16, 17] [0, 0, 0, 3][18, 19] [0, 1, 0, 3][20, 21] [0, 2, 0, 3][22, 23] [0, 0, 0, 4][24, 25] [0, 1, 0, 4][26, 27] [0, 2, 0, 4][28, 29] [0, 0, 0, 5][30, 31] [0, 1, 0, 5][32, 33] [0, 2, 0, 5][34, 35] [0, 0, 0, 6][36, 37] [0, 1, 0, 6][38, 39] [0, 2, 0, 6][40, 41] [0, 0, 0, 7][42, 43] [0, 1, 0, 7][44, 45] [0, 2, 0, 7][46, 47] [0, 0, 0, 8][48, 49] [0, 1, 0, 8][50, 51] [0, 2, 0, 8][52, 53] [0, 0, 0, 9][54, 55] [0, 1, 0, 9][56, 57] [0, 2, 0, 9][58, 59] [0, 0, 0, 10][60, 61] [0, 1, 0, 10][62, 63] [0, 2, 0, 10][64, 65] [0, 0, 0, 11][66, 67] [0, 1, 0, 11][68, 69] [0, 2, 0, 11][70, 71] [0, 0, 0, 12][72, 73] [0, 1, 0, 12][74, 75] [0, 2, 0, 12][76, 77] [0, 0, 0, 13][78, 79] [0, 1, 0, 13][80, 81] [0, 2, 0, 13][82, 83] [0, 0, 0, 14][84, 85] [0, 1, 0, 14][86, 87] [0, 2, 0, 14][88, 89] [0, 0, 0, 15][90, 91] [0, 1, 0, 15][92, 93] [0, 2, 0, 15][94, 95] [0, 0, 0, 16][96, 97] [0, 1, 0, 16][98, 99] [0, 2, 0, 16][100, 101] [0, 0, 0, 17][102, 103] [0, 1, 0, 17][104, 105] [0, 2, 0, 17][106, 107] Col-7: DComplex Axis Lengths: [2, 18] (NB: Matrix in Row/Column order) [(1.2,3.4), (3.2,6.4), (5.2,9.4), (7.2,12.4), (9.2,15.4), (11.2,18.4), (13.2,21.4), (15.2,24.4), (17.2,27.4), (19.2,30.4), (21.2,33.4), (23.2,36.4), (25.2,39.4), (27.2,42.4), (29.2,45.4), (31.2,48.4), (33.2,51.4), (35.2,54.4) (-2.3,5.6), (-0.3,8.6), (1.7,11.6), (3.7,14.6), (5.7,17.6), (7.7,20.6), (9.7,23.6), (11.7,26.6), (13.7,29.6), (15.7,32.6), (17.7,35.6), (19.7,38.6), (21.7,41.6), (23.7,44.6), (25.7,47.6), (27.7,50.6), (29.7,53.6), (31.7,56.6)] Col-8: Bool Ndim=4 Axis Lengths: [5, 7, 1, 18] [0, 0, 0, 0][0, 0, 1, 0, 0] [0, 1, 0, 0][1, 0, 0, 1, 0] [0, 2, 0, 0][0, 1, 0, 0, 1] [0, 3, 0, 0][0, 0, 1, 0, 0] [0, 4, 0, 0][1, 0, 0, 1, 0] [0, 5, 0, 0][0, 1, 0, 0, 1] [0, 6, 0, 0][0, 0, 1, 0, 0] [0, 0, 0, 1][0, 0, 1, 0, 0] [0, 1, 0, 1][1, 0, 0, 1, 0] [0, 2, 0, 1][0, 1, 0, 0, 1] [0, 3, 0, 1][0, 0, 1, 0, 0] [0, 4, 0, 1][1, 0, 0, 1, 0] [0, 5, 0, 1][0, 1, 0, 0, 1] [0, 6, 0, 1][0, 0, 1, 0, 0] [0, 0, 0, 2][0, 0, 1, 0, 0] [0, 1, 0, 2][1, 0, 0, 1, 0] [0, 2, 0, 2][0, 1, 0, 0, 1] [0, 3, 0, 2][0, 0, 1, 0, 0] [0, 4, 0, 2][1, 0, 0, 1, 0] [0, 5, 0, 2][0, 1, 0, 0, 1] [0, 6, 0, 2][0, 0, 1, 0, 0] [0, 0, 0, 3][0, 0, 1, 0, 0] [0, 1, 0, 3][1, 0, 0, 1, 0] [0, 2, 0, 3][0, 1, 0, 0, 1] [0, 3, 0, 3][0, 0, 1, 0, 0] [0, 4, 0, 3][1, 0, 0, 1, 0] [0, 5, 0, 3][0, 1, 0, 0, 1] [0, 6, 0, 3][0, 0, 1, 0, 0] [0, 0, 0, 4][0, 0, 1, 0, 0] [0, 1, 0, 4][1, 0, 0, 1, 0] [0, 2, 0, 4][0, 1, 0, 0, 1] [0, 3, 0, 4][0, 0, 1, 0, 0] [0, 4, 0, 4][1, 0, 0, 1, 0] [0, 5, 0, 4][0, 1, 0, 0, 1] [0, 6, 0, 4][0, 0, 1, 0, 0] [0, 0, 0, 5][0, 0, 1, 0, 0] [0, 1, 0, 5][1, 0, 0, 1, 0] [0, 2, 0, 5][0, 1, 0, 0, 1] [0, 3, 0, 5][0, 0, 1, 0, 0] [0, 4, 0, 5][1, 0, 0, 1, 0] [0, 5, 0, 5][0, 1, 0, 0, 1] [0, 6, 0, 5][0, 0, 1, 0, 0] [0, 0, 0, 6][0, 0, 1, 0, 0] [0, 1, 0, 6][1, 0, 0, 1, 0] [0, 2, 0, 6][0, 1, 0, 0, 1] [0, 3, 0, 6][0, 0, 1, 0, 0] [0, 4, 0, 6][1, 0, 0, 1, 0] [0, 5, 0, 6][0, 1, 0, 0, 1] [0, 6, 0, 6][0, 0, 1, 0, 0] [0, 0, 0, 7][0, 0, 1, 0, 0] [0, 1, 0, 7][1, 0, 0, 1, 0] [0, 2, 0, 7][0, 1, 0, 0, 1] [0, 3, 0, 7][0, 0, 1, 0, 0] [0, 4, 0, 7][1, 0, 0, 1, 0] [0, 5, 0, 7][0, 1, 0, 0, 1] [0, 6, 0, 7][0, 0, 1, 0, 0] [0, 0, 0, 8][0, 0, 1, 0, 0] [0, 1, 0, 8][1, 0, 0, 1, 0] [0, 2, 0, 8][0, 1, 0, 0, 1] [0, 3, 0, 8][0, 0, 1, 0, 0] [0, 4, 0, 8][1, 0, 0, 1, 0] [0, 5, 0, 8][0, 1, 0, 0, 1] [0, 6, 0, 8][0, 0, 1, 0, 0] [0, 0, 0, 9][0, 0, 1, 0, 0] [0, 1, 0, 9][1, 0, 0, 1, 0] [0, 2, 0, 9][0, 1, 0, 0, 1] [0, 3, 0, 9][0, 0, 1, 0, 0] [0, 4, 0, 9][1, 0, 0, 1, 0] [0, 5, 0, 9][0, 1, 0, 0, 1] [0, 6, 0, 9][0, 0, 1, 0, 0] [0, 0, 0, 10][0, 0, 1, 0, 0] [0, 1, 0, 10][1, 0, 0, 1, 0] [0, 2, 0, 10][0, 1, 0, 0, 1] [0, 3, 0, 10][0, 0, 1, 0, 0] [0, 4, 0, 10][1, 0, 0, 1, 0] [0, 5, 0, 10][0, 1, 0, 0, 1] [0, 6, 0, 10][0, 0, 1, 0, 0] [0, 0, 0, 11][0, 0, 1, 0, 0] [0, 1, 0, 11][1, 0, 0, 1, 0] [0, 2, 0, 11][0, 1, 0, 0, 1] [0, 3, 0, 11][0, 0, 1, 0, 0] [0, 4, 0, 11][1, 0, 0, 1, 0] [0, 5, 0, 11][0, 1, 0, 0, 1] [0, 6, 0, 11][0, 0, 1, 0, 0] [0, 0, 0, 12][0, 0, 1, 0, 0] [0, 1, 0, 12][1, 0, 0, 1, 0] [0, 2, 0, 12][0, 1, 0, 0, 1] [0, 3, 0, 12][0, 0, 1, 0, 0] [0, 4, 0, 12][1, 0, 0, 1, 0] [0, 5, 0, 12][0, 1, 0, 0, 1] [0, 6, 0, 12][0, 0, 1, 0, 0] [0, 0, 0, 13][0, 0, 1, 0, 0] [0, 1, 0, 13][1, 0, 0, 1, 0] [0, 2, 0, 13][0, 1, 0, 0, 1] [0, 3, 0, 13][0, 0, 1, 0, 0] [0, 4, 0, 13][1, 0, 0, 1, 0] [0, 5, 0, 13][0, 1, 0, 0, 1] [0, 6, 0, 13][0, 0, 1, 0, 0] [0, 0, 0, 14][0, 0, 1, 0, 0] [0, 1, 0, 14][1, 0, 0, 1, 0] [0, 2, 0, 14][0, 1, 0, 0, 1] [0, 3, 0, 14][0, 0, 1, 0, 0] [0, 4, 0, 14][1, 0, 0, 1, 0] [0, 5, 0, 14][0, 1, 0, 0, 1] [0, 6, 0, 14][0, 0, 1, 0, 0] [0, 0, 0, 15][0, 0, 1, 0, 0] [0, 1, 0, 15][1, 0, 0, 1, 0] [0, 2, 0, 15][0, 1, 0, 0, 1] [0, 3, 0, 15][0, 0, 1, 0, 0] [0, 4, 0, 15][1, 0, 0, 1, 0] [0, 5, 0, 15][0, 1, 0, 0, 1] [0, 6, 0, 15][0, 0, 1, 0, 0] [0, 0, 0, 16][0, 0, 1, 0, 0] [0, 1, 0, 16][1, 0, 0, 1, 0] [0, 2, 0, 16][0, 1, 0, 0, 1] [0, 3, 0, 16][0, 0, 1, 0, 0] [0, 4, 0, 16][1, 0, 0, 1, 0] [0, 5, 0, 16][0, 1, 0, 0, 1] [0, 6, 0, 16][0, 0, 1, 0, 0] [0, 0, 0, 17][0, 0, 1, 0, 0] [0, 1, 0, 17][1, 0, 0, 1, 0] [0, 2, 0, 17][0, 1, 0, 0, 1] [0, 3, 0, 17][0, 0, 1, 0, 0] [0, 4, 0, 17][1, 0, 0, 1, 0] [0, 5, 0, 17][0, 1, 0, 0, 1] [0, 6, 0, 17][0, 0, 1, 0, 0] Col-9: String Axis Lengths: [5, 18] (NB: Matrix in Row/Column order) [Start-1, Start-1 0, Start-1 0 1, Start-1 0 1 2, Start-1 0 1 2 3, Start-1 0 1 2 3 4, Start-1 0 1 2 3 4 5, Start-1 0 1 2 3 4 5 6, Start-1 0 1 2 3 4 5 6 7, Start-1 0 1 2 3 4 5 6 7 8, Start-1 0 1 2 3 4 5 6 7 8 9, Start-1 0 1 2 3 4 5 6 7 8 9 10, Start-1 0 1 2 3 4 5 6 7 8 9 10 11, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-2, Start-2 0, Start-2 0 1, Start-2 0 1 2, Start-2 0 1 2 3, Start-2 0 1 2 3 4, Start-2 0 1 2 3 4 5, Start-2 0 1 2 3 4 5 6, Start-2 0 1 2 3 4 5 6 7, Start-2 0 1 2 3 4 5 6 7 8, Start-2 0 1 2 3 4 5 6 7 8 9, Start-2 0 1 2 3 4 5 6 7 8 9 10, Start-2 0 1 2 3 4 5 6 7 8 9 10 11, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-3, Start-3 0, Start-3 0 1, Start-3 0 1 2, Start-3 0 1 2 3, Start-3 0 1 2 3 4, Start-3 0 1 2 3 4 5, Start-3 0 1 2 3 4 5 6, Start-3 0 1 2 3 4 5 6 7, Start-3 0 1 2 3 4 5 6 7 8, Start-3 0 1 2 3 4 5 6 7 8 9, Start-3 0 1 2 3 4 5 6 7 8 9 10, Start-3 0 1 2 3 4 5 6 7 8 9 10 11, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-4, Start-4 0, Start-4 0 1, Start-4 0 1 2, Start-4 0 1 2 3, Start-4 0 1 2 3 4, Start-4 0 1 2 3 4 5, Start-4 0 1 2 3 4 5 6, Start-4 0 1 2 3 4 5 6 7, Start-4 0 1 2 3 4 5 6 7 8, Start-4 0 1 2 3 4 5 6 7 8 9, Start-4 0 1 2 3 4 5 6 7 8 9 10, Start-4 0 1 2 3 4 5 6 7 8 9 10 11, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-5, Start-5 0, Start-5 0 1, Start-5 0 1 2, Start-5 0 1 2 3, Start-5 0 1 2 3 4, Start-5 0 1 2 3 4 5, Start-5 0 1 2 3 4 5 6, Start-5 0 1 2 3 4 5 6 7, Start-5 0 1 2 3 4 5 6 7 8, Start-5 0 1 2 3 4 5 6 7 8 9, Start-5 0 1 2 3 4 5 6 7 8 9 10, Start-5 0 1 2 3 4 5 6 7 8 9 10 11, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16] Trying to add an indirect Array Column. TableType = 1 Colvirt: Int [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36] Col-4: Bool [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] Col-5: DComplex [(18,0), (17,1), (16,2), (15,3), (14,4), (13,5), (12,6), (11,7), (10,8), (9,9), (8,10), (7,11), (6,12), (5,13), (4,14), (3,15), (2,16), (1,17)] Col-6: float Ndim=4 Axis Lengths: [2, 3, 1, 18] [0, 0, 0, 0][0, 1] [0, 1, 0, 0][2, 3] [0, 2, 0, 0][4, 5] [0, 0, 0, 1][6, 7] [0, 1, 0, 1][8, 9] [0, 2, 0, 1][10, 11] [0, 0, 0, 2][12, 13] [0, 1, 0, 2][14, 15] [0, 2, 0, 2][16, 17] [0, 0, 0, 3][18, 19] [0, 1, 0, 3][20, 21] [0, 2, 0, 3][22, 23] [0, 0, 0, 4][24, 25] [0, 1, 0, 4][26, 27] [0, 2, 0, 4][28, 29] [0, 0, 0, 5][30, 31] [0, 1, 0, 5][32, 33] [0, 2, 0, 5][34, 35] [0, 0, 0, 6][36, 37] [0, 1, 0, 6][38, 39] [0, 2, 0, 6][40, 41] [0, 0, 0, 7][42, 43] [0, 1, 0, 7][44, 45] [0, 2, 0, 7][46, 47] [0, 0, 0, 8][48, 49] [0, 1, 0, 8][50, 51] [0, 2, 0, 8][52, 53] [0, 0, 0, 9][54, 55] [0, 1, 0, 9][56, 57] [0, 2, 0, 9][58, 59] [0, 0, 0, 10][60, 61] [0, 1, 0, 10][62, 63] [0, 2, 0, 10][64, 65] [0, 0, 0, 11][66, 67] [0, 1, 0, 11][68, 69] [0, 2, 0, 11][70, 71] [0, 0, 0, 12][72, 73] [0, 1, 0, 12][74, 75] [0, 2, 0, 12][76, 77] [0, 0, 0, 13][78, 79] [0, 1, 0, 13][80, 81] [0, 2, 0, 13][82, 83] [0, 0, 0, 14][84, 85] [0, 1, 0, 14][86, 87] [0, 2, 0, 14][88, 89] [0, 0, 0, 15][90, 91] [0, 1, 0, 15][92, 93] [0, 2, 0, 15][94, 95] [0, 0, 0, 16][96, 97] [0, 1, 0, 16][98, 99] [0, 2, 0, 16][100, 101] [0, 0, 0, 17][102, 103] [0, 1, 0, 17][104, 105] [0, 2, 0, 17][106, 107] Col-7: DComplex Axis Lengths: [2, 18] (NB: Matrix in Row/Column order) [(1.2,3.4), (3.2,6.4), (5.2,9.4), (7.2,12.4), (9.2,15.4), (11.2,18.4), (13.2,21.4), (15.2,24.4), (17.2,27.4), (19.2,30.4), (21.2,33.4), (23.2,36.4), (25.2,39.4), (27.2,42.4), (29.2,45.4), (31.2,48.4), (33.2,51.4), (35.2,54.4) (-2.3,5.6), (-0.3,8.6), (1.7,11.6), (3.7,14.6), (5.7,17.6), (7.7,20.6), (9.7,23.6), (11.7,26.6), (13.7,29.6), (15.7,32.6), (17.7,35.6), (19.7,38.6), (21.7,41.6), (23.7,44.6), (25.7,47.6), (27.7,50.6), (29.7,53.6), (31.7,56.6)] Col-8: Bool Ndim=4 Axis Lengths: [5, 7, 1, 18] [0, 0, 0, 0][0, 0, 1, 0, 0] [0, 1, 0, 0][1, 0, 0, 1, 0] [0, 2, 0, 0][0, 1, 0, 0, 1] [0, 3, 0, 0][0, 0, 1, 0, 0] [0, 4, 0, 0][1, 0, 0, 1, 0] [0, 5, 0, 0][0, 1, 0, 0, 1] [0, 6, 0, 0][0, 0, 1, 0, 0] [0, 0, 0, 1][0, 0, 1, 0, 0] [0, 1, 0, 1][1, 0, 0, 1, 0] [0, 2, 0, 1][0, 1, 0, 0, 1] [0, 3, 0, 1][0, 0, 1, 0, 0] [0, 4, 0, 1][1, 0, 0, 1, 0] [0, 5, 0, 1][0, 1, 0, 0, 1] [0, 6, 0, 1][0, 0, 1, 0, 0] [0, 0, 0, 2][0, 0, 1, 0, 0] [0, 1, 0, 2][1, 0, 0, 1, 0] [0, 2, 0, 2][0, 1, 0, 0, 1] [0, 3, 0, 2][0, 0, 1, 0, 0] [0, 4, 0, 2][1, 0, 0, 1, 0] [0, 5, 0, 2][0, 1, 0, 0, 1] [0, 6, 0, 2][0, 0, 1, 0, 0] [0, 0, 0, 3][0, 0, 1, 0, 0] [0, 1, 0, 3][1, 0, 0, 1, 0] [0, 2, 0, 3][0, 1, 0, 0, 1] [0, 3, 0, 3][0, 0, 1, 0, 0] [0, 4, 0, 3][1, 0, 0, 1, 0] [0, 5, 0, 3][0, 1, 0, 0, 1] [0, 6, 0, 3][0, 0, 1, 0, 0] [0, 0, 0, 4][0, 0, 1, 0, 0] [0, 1, 0, 4][1, 0, 0, 1, 0] [0, 2, 0, 4][0, 1, 0, 0, 1] [0, 3, 0, 4][0, 0, 1, 0, 0] [0, 4, 0, 4][1, 0, 0, 1, 0] [0, 5, 0, 4][0, 1, 0, 0, 1] [0, 6, 0, 4][0, 0, 1, 0, 0] [0, 0, 0, 5][0, 0, 1, 0, 0] [0, 1, 0, 5][1, 0, 0, 1, 0] [0, 2, 0, 5][0, 1, 0, 0, 1] [0, 3, 0, 5][0, 0, 1, 0, 0] [0, 4, 0, 5][1, 0, 0, 1, 0] [0, 5, 0, 5][0, 1, 0, 0, 1] [0, 6, 0, 5][0, 0, 1, 0, 0] [0, 0, 0, 6][0, 0, 1, 0, 0] [0, 1, 0, 6][1, 0, 0, 1, 0] [0, 2, 0, 6][0, 1, 0, 0, 1] [0, 3, 0, 6][0, 0, 1, 0, 0] [0, 4, 0, 6][1, 0, 0, 1, 0] [0, 5, 0, 6][0, 1, 0, 0, 1] [0, 6, 0, 6][0, 0, 1, 0, 0] [0, 0, 0, 7][0, 0, 1, 0, 0] [0, 1, 0, 7][1, 0, 0, 1, 0] [0, 2, 0, 7][0, 1, 0, 0, 1] [0, 3, 0, 7][0, 0, 1, 0, 0] [0, 4, 0, 7][1, 0, 0, 1, 0] [0, 5, 0, 7][0, 1, 0, 0, 1] [0, 6, 0, 7][0, 0, 1, 0, 0] [0, 0, 0, 8][0, 0, 1, 0, 0] [0, 1, 0, 8][1, 0, 0, 1, 0] [0, 2, 0, 8][0, 1, 0, 0, 1] [0, 3, 0, 8][0, 0, 1, 0, 0] [0, 4, 0, 8][1, 0, 0, 1, 0] [0, 5, 0, 8][0, 1, 0, 0, 1] [0, 6, 0, 8][0, 0, 1, 0, 0] [0, 0, 0, 9][0, 0, 1, 0, 0] [0, 1, 0, 9][1, 0, 0, 1, 0] [0, 2, 0, 9][0, 1, 0, 0, 1] [0, 3, 0, 9][0, 0, 1, 0, 0] [0, 4, 0, 9][1, 0, 0, 1, 0] [0, 5, 0, 9][0, 1, 0, 0, 1] [0, 6, 0, 9][0, 0, 1, 0, 0] [0, 0, 0, 10][0, 0, 1, 0, 0] [0, 1, 0, 10][1, 0, 0, 1, 0] [0, 2, 0, 10][0, 1, 0, 0, 1] [0, 3, 0, 10][0, 0, 1, 0, 0] [0, 4, 0, 10][1, 0, 0, 1, 0] [0, 5, 0, 10][0, 1, 0, 0, 1] [0, 6, 0, 10][0, 0, 1, 0, 0] [0, 0, 0, 11][0, 0, 1, 0, 0] [0, 1, 0, 11][1, 0, 0, 1, 0] [0, 2, 0, 11][0, 1, 0, 0, 1] [0, 3, 0, 11][0, 0, 1, 0, 0] [0, 4, 0, 11][1, 0, 0, 1, 0] [0, 5, 0, 11][0, 1, 0, 0, 1] [0, 6, 0, 11][0, 0, 1, 0, 0] [0, 0, 0, 12][0, 0, 1, 0, 0] [0, 1, 0, 12][1, 0, 0, 1, 0] [0, 2, 0, 12][0, 1, 0, 0, 1] [0, 3, 0, 12][0, 0, 1, 0, 0] [0, 4, 0, 12][1, 0, 0, 1, 0] [0, 5, 0, 12][0, 1, 0, 0, 1] [0, 6, 0, 12][0, 0, 1, 0, 0] [0, 0, 0, 13][0, 0, 1, 0, 0] [0, 1, 0, 13][1, 0, 0, 1, 0] [0, 2, 0, 13][0, 1, 0, 0, 1] [0, 3, 0, 13][0, 0, 1, 0, 0] [0, 4, 0, 13][1, 0, 0, 1, 0] [0, 5, 0, 13][0, 1, 0, 0, 1] [0, 6, 0, 13][0, 0, 1, 0, 0] [0, 0, 0, 14][0, 0, 1, 0, 0] [0, 1, 0, 14][1, 0, 0, 1, 0] [0, 2, 0, 14][0, 1, 0, 0, 1] [0, 3, 0, 14][0, 0, 1, 0, 0] [0, 4, 0, 14][1, 0, 0, 1, 0] [0, 5, 0, 14][0, 1, 0, 0, 1] [0, 6, 0, 14][0, 0, 1, 0, 0] [0, 0, 0, 15][0, 0, 1, 0, 0] [0, 1, 0, 15][1, 0, 0, 1, 0] [0, 2, 0, 15][0, 1, 0, 0, 1] [0, 3, 0, 15][0, 0, 1, 0, 0] [0, 4, 0, 15][1, 0, 0, 1, 0] [0, 5, 0, 15][0, 1, 0, 0, 1] [0, 6, 0, 15][0, 0, 1, 0, 0] [0, 0, 0, 16][0, 0, 1, 0, 0] [0, 1, 0, 16][1, 0, 0, 1, 0] [0, 2, 0, 16][0, 1, 0, 0, 1] [0, 3, 0, 16][0, 0, 1, 0, 0] [0, 4, 0, 16][1, 0, 0, 1, 0] [0, 5, 0, 16][0, 1, 0, 0, 1] [0, 6, 0, 16][0, 0, 1, 0, 0] [0, 0, 0, 17][0, 0, 1, 0, 0] [0, 1, 0, 17][1, 0, 0, 1, 0] [0, 2, 0, 17][0, 1, 0, 0, 1] [0, 3, 0, 17][0, 0, 1, 0, 0] [0, 4, 0, 17][1, 0, 0, 1, 0] [0, 5, 0, 17][0, 1, 0, 0, 1] [0, 6, 0, 17][0, 0, 1, 0, 0] Col-9: String Axis Lengths: [5, 18] (NB: Matrix in Row/Column order) [Start-1, Start-1 0, Start-1 0 1, Start-1 0 1 2, Start-1 0 1 2 3, Start-1 0 1 2 3 4, Start-1 0 1 2 3 4 5, Start-1 0 1 2 3 4 5 6, Start-1 0 1 2 3 4 5 6 7, Start-1 0 1 2 3 4 5 6 7 8, Start-1 0 1 2 3 4 5 6 7 8 9, Start-1 0 1 2 3 4 5 6 7 8 9 10, Start-1 0 1 2 3 4 5 6 7 8 9 10 11, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-2, Start-2 0, Start-2 0 1, Start-2 0 1 2, Start-2 0 1 2 3, Start-2 0 1 2 3 4, Start-2 0 1 2 3 4 5, Start-2 0 1 2 3 4 5 6, Start-2 0 1 2 3 4 5 6 7, Start-2 0 1 2 3 4 5 6 7 8, Start-2 0 1 2 3 4 5 6 7 8 9, Start-2 0 1 2 3 4 5 6 7 8 9 10, Start-2 0 1 2 3 4 5 6 7 8 9 10 11, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-3, Start-3 0, Start-3 0 1, Start-3 0 1 2, Start-3 0 1 2 3, Start-3 0 1 2 3 4, Start-3 0 1 2 3 4 5, Start-3 0 1 2 3 4 5 6, Start-3 0 1 2 3 4 5 6 7, Start-3 0 1 2 3 4 5 6 7 8, Start-3 0 1 2 3 4 5 6 7 8 9, Start-3 0 1 2 3 4 5 6 7 8 9 10, Start-3 0 1 2 3 4 5 6 7 8 9 10 11, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-4, Start-4 0, Start-4 0 1, Start-4 0 1 2, Start-4 0 1 2 3, Start-4 0 1 2 3 4, Start-4 0 1 2 3 4 5, Start-4 0 1 2 3 4 5 6, Start-4 0 1 2 3 4 5 6 7, Start-4 0 1 2 3 4 5 6 7 8, Start-4 0 1 2 3 4 5 6 7 8 9, Start-4 0 1 2 3 4 5 6 7 8 9 10, Start-4 0 1 2 3 4 5 6 7 8 9 10 11, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-5, Start-5 0, Start-5 0 1, Start-5 0 1 2, Start-5 0 1 2 3, Start-5 0 1 2 3 4, Start-5 0 1 2 3 4 5, Start-5 0 1 2 3 4 5 6, Start-5 0 1 2 3 4 5 6 7, Start-5 0 1 2 3 4 5 6 7 8, Start-5 0 1 2 3 4 5 6 7 8 9, Start-5 0 1 2 3 4 5 6 7 8 9 10, Start-5 0 1 2 3 4 5 6 7 8 9 10 11, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16] Col-11: Int Axis Lengths: [5, 18] (NB: Matrix in Row/Column order) [1, 1, 2, 4, 7, 11, 16, 22, 29, 37, 46, 56, 67, 79, 92, 106, 121, 137 2, 2, 3, 5, 8, 12, 17, 23, 30, 38, 47, 57, 68, 80, 93, 107, 122, 138 3, 3, 4, 6, 9, 13, 18, 24, 31, 39, 48, 58, 69, 81, 94, 108, 123, 139 4, 4, 5, 7, 10, 14, 19, 25, 32, 40, 49, 59, 70, 82, 95, 109, 124, 140 5, 5, 6, 8, 11, 15, 20, 26, 33, 41, 50, 60, 71, 83, 96, 110, 125, 141] after removing several rows at once: TableType = 1 Colvirt: Int [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30] Col-4: Bool [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] Col-5: DComplex [(18,0), (17,1), (16,2), (12,6), (11,7), (10,8), (9,9), (8,10), (7,11), (6,12), (5,13), (4,14), (3,15), (2,16), (1,17)] Col-6: float Ndim=4 Axis Lengths: [2, 3, 1, 15] [0, 0, 0, 0][0, 1] [0, 1, 0, 0][2, 3] [0, 2, 0, 0][4, 5] [0, 0, 0, 1][6, 7] [0, 1, 0, 1][8, 9] [0, 2, 0, 1][10, 11] [0, 0, 0, 2][12, 13] [0, 1, 0, 2][14, 15] [0, 2, 0, 2][16, 17] [0, 0, 0, 3][36, 37] [0, 1, 0, 3][38, 39] [0, 2, 0, 3][40, 41] [0, 0, 0, 4][42, 43] [0, 1, 0, 4][44, 45] [0, 2, 0, 4][46, 47] [0, 0, 0, 5][48, 49] [0, 1, 0, 5][50, 51] [0, 2, 0, 5][52, 53] [0, 0, 0, 6][54, 55] [0, 1, 0, 6][56, 57] [0, 2, 0, 6][58, 59] [0, 0, 0, 7][60, 61] [0, 1, 0, 7][62, 63] [0, 2, 0, 7][64, 65] [0, 0, 0, 8][66, 67] [0, 1, 0, 8][68, 69] [0, 2, 0, 8][70, 71] [0, 0, 0, 9][72, 73] [0, 1, 0, 9][74, 75] [0, 2, 0, 9][76, 77] [0, 0, 0, 10][78, 79] [0, 1, 0, 10][80, 81] [0, 2, 0, 10][82, 83] [0, 0, 0, 11][84, 85] [0, 1, 0, 11][86, 87] [0, 2, 0, 11][88, 89] [0, 0, 0, 12][90, 91] [0, 1, 0, 12][92, 93] [0, 2, 0, 12][94, 95] [0, 0, 0, 13][96, 97] [0, 1, 0, 13][98, 99] [0, 2, 0, 13][100, 101] [0, 0, 0, 14][102, 103] [0, 1, 0, 14][104, 105] [0, 2, 0, 14][106, 107] Col-7: DComplex Axis Lengths: [2, 15] (NB: Matrix in Row/Column order) [(1.2,3.4), (3.2,6.4), (5.2,9.4), (13.2,21.4), (15.2,24.4), (17.2,27.4), (19.2,30.4), (21.2,33.4), (23.2,36.4), (25.2,39.4), (27.2,42.4), (29.2,45.4), (31.2,48.4), (33.2,51.4), (35.2,54.4) (-2.3,5.6), (-0.3,8.6), (1.7,11.6), (9.7,23.6), (11.7,26.6), (13.7,29.6), (15.7,32.6), (17.7,35.6), (19.7,38.6), (21.7,41.6), (23.7,44.6), (25.7,47.6), (27.7,50.6), (29.7,53.6), (31.7,56.6)] Col-8: Bool Ndim=4 Axis Lengths: [5, 7, 1, 15] [0, 0, 0, 0][0, 0, 1, 0, 0] [0, 1, 0, 0][1, 0, 0, 1, 0] [0, 2, 0, 0][0, 1, 0, 0, 1] [0, 3, 0, 0][0, 0, 1, 0, 0] [0, 4, 0, 0][1, 0, 0, 1, 0] [0, 5, 0, 0][0, 1, 0, 0, 1] [0, 6, 0, 0][0, 0, 1, 0, 0] [0, 0, 0, 1][0, 0, 1, 0, 0] [0, 1, 0, 1][1, 0, 0, 1, 0] [0, 2, 0, 1][0, 1, 0, 0, 1] [0, 3, 0, 1][0, 0, 1, 0, 0] [0, 4, 0, 1][1, 0, 0, 1, 0] [0, 5, 0, 1][0, 1, 0, 0, 1] [0, 6, 0, 1][0, 0, 1, 0, 0] [0, 0, 0, 2][0, 0, 1, 0, 0] [0, 1, 0, 2][1, 0, 0, 1, 0] [0, 2, 0, 2][0, 1, 0, 0, 1] [0, 3, 0, 2][0, 0, 1, 0, 0] [0, 4, 0, 2][1, 0, 0, 1, 0] [0, 5, 0, 2][0, 1, 0, 0, 1] [0, 6, 0, 2][0, 0, 1, 0, 0] [0, 0, 0, 3][0, 0, 1, 0, 0] [0, 1, 0, 3][1, 0, 0, 1, 0] [0, 2, 0, 3][0, 1, 0, 0, 1] [0, 3, 0, 3][0, 0, 1, 0, 0] [0, 4, 0, 3][1, 0, 0, 1, 0] [0, 5, 0, 3][0, 1, 0, 0, 1] [0, 6, 0, 3][0, 0, 1, 0, 0] [0, 0, 0, 4][0, 0, 1, 0, 0] [0, 1, 0, 4][1, 0, 0, 1, 0] [0, 2, 0, 4][0, 1, 0, 0, 1] [0, 3, 0, 4][0, 0, 1, 0, 0] [0, 4, 0, 4][1, 0, 0, 1, 0] [0, 5, 0, 4][0, 1, 0, 0, 1] [0, 6, 0, 4][0, 0, 1, 0, 0] [0, 0, 0, 5][0, 0, 1, 0, 0] [0, 1, 0, 5][1, 0, 0, 1, 0] [0, 2, 0, 5][0, 1, 0, 0, 1] [0, 3, 0, 5][0, 0, 1, 0, 0] [0, 4, 0, 5][1, 0, 0, 1, 0] [0, 5, 0, 5][0, 1, 0, 0, 1] [0, 6, 0, 5][0, 0, 1, 0, 0] [0, 0, 0, 6][0, 0, 1, 0, 0] [0, 1, 0, 6][1, 0, 0, 1, 0] [0, 2, 0, 6][0, 1, 0, 0, 1] [0, 3, 0, 6][0, 0, 1, 0, 0] [0, 4, 0, 6][1, 0, 0, 1, 0] [0, 5, 0, 6][0, 1, 0, 0, 1] [0, 6, 0, 6][0, 0, 1, 0, 0] [0, 0, 0, 7][0, 0, 1, 0, 0] [0, 1, 0, 7][1, 0, 0, 1, 0] [0, 2, 0, 7][0, 1, 0, 0, 1] [0, 3, 0, 7][0, 0, 1, 0, 0] [0, 4, 0, 7][1, 0, 0, 1, 0] [0, 5, 0, 7][0, 1, 0, 0, 1] [0, 6, 0, 7][0, 0, 1, 0, 0] [0, 0, 0, 8][0, 0, 1, 0, 0] [0, 1, 0, 8][1, 0, 0, 1, 0] [0, 2, 0, 8][0, 1, 0, 0, 1] [0, 3, 0, 8][0, 0, 1, 0, 0] [0, 4, 0, 8][1, 0, 0, 1, 0] [0, 5, 0, 8][0, 1, 0, 0, 1] [0, 6, 0, 8][0, 0, 1, 0, 0] [0, 0, 0, 9][0, 0, 1, 0, 0] [0, 1, 0, 9][1, 0, 0, 1, 0] [0, 2, 0, 9][0, 1, 0, 0, 1] [0, 3, 0, 9][0, 0, 1, 0, 0] [0, 4, 0, 9][1, 0, 0, 1, 0] [0, 5, 0, 9][0, 1, 0, 0, 1] [0, 6, 0, 9][0, 0, 1, 0, 0] [0, 0, 0, 10][0, 0, 1, 0, 0] [0, 1, 0, 10][1, 0, 0, 1, 0] [0, 2, 0, 10][0, 1, 0, 0, 1] [0, 3, 0, 10][0, 0, 1, 0, 0] [0, 4, 0, 10][1, 0, 0, 1, 0] [0, 5, 0, 10][0, 1, 0, 0, 1] [0, 6, 0, 10][0, 0, 1, 0, 0] [0, 0, 0, 11][0, 0, 1, 0, 0] [0, 1, 0, 11][1, 0, 0, 1, 0] [0, 2, 0, 11][0, 1, 0, 0, 1] [0, 3, 0, 11][0, 0, 1, 0, 0] [0, 4, 0, 11][1, 0, 0, 1, 0] [0, 5, 0, 11][0, 1, 0, 0, 1] [0, 6, 0, 11][0, 0, 1, 0, 0] [0, 0, 0, 12][0, 0, 1, 0, 0] [0, 1, 0, 12][1, 0, 0, 1, 0] [0, 2, 0, 12][0, 1, 0, 0, 1] [0, 3, 0, 12][0, 0, 1, 0, 0] [0, 4, 0, 12][1, 0, 0, 1, 0] [0, 5, 0, 12][0, 1, 0, 0, 1] [0, 6, 0, 12][0, 0, 1, 0, 0] [0, 0, 0, 13][0, 0, 1, 0, 0] [0, 1, 0, 13][1, 0, 0, 1, 0] [0, 2, 0, 13][0, 1, 0, 0, 1] [0, 3, 0, 13][0, 0, 1, 0, 0] [0, 4, 0, 13][1, 0, 0, 1, 0] [0, 5, 0, 13][0, 1, 0, 0, 1] [0, 6, 0, 13][0, 0, 1, 0, 0] [0, 0, 0, 14][0, 0, 1, 0, 0] [0, 1, 0, 14][1, 0, 0, 1, 0] [0, 2, 0, 14][0, 1, 0, 0, 1] [0, 3, 0, 14][0, 0, 1, 0, 0] [0, 4, 0, 14][1, 0, 0, 1, 0] [0, 5, 0, 14][0, 1, 0, 0, 1] [0, 6, 0, 14][0, 0, 1, 0, 0] Col-9: String Axis Lengths: [5, 15] (NB: Matrix in Row/Column order) [Start-1, Start-1 0, Start-1 0 1, Start-1 0 1 2 3 4 5, Start-1 0 1 2 3 4 5 6, Start-1 0 1 2 3 4 5 6 7, Start-1 0 1 2 3 4 5 6 7 8, Start-1 0 1 2 3 4 5 6 7 8 9, Start-1 0 1 2 3 4 5 6 7 8 9 10, Start-1 0 1 2 3 4 5 6 7 8 9 10 11, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-2, Start-2 0, Start-2 0 1, Start-2 0 1 2 3 4 5, Start-2 0 1 2 3 4 5 6, Start-2 0 1 2 3 4 5 6 7, Start-2 0 1 2 3 4 5 6 7 8, Start-2 0 1 2 3 4 5 6 7 8 9, Start-2 0 1 2 3 4 5 6 7 8 9 10, Start-2 0 1 2 3 4 5 6 7 8 9 10 11, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-3, Start-3 0, Start-3 0 1, Start-3 0 1 2 3 4 5, Start-3 0 1 2 3 4 5 6, Start-3 0 1 2 3 4 5 6 7, Start-3 0 1 2 3 4 5 6 7 8, Start-3 0 1 2 3 4 5 6 7 8 9, Start-3 0 1 2 3 4 5 6 7 8 9 10, Start-3 0 1 2 3 4 5 6 7 8 9 10 11, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-4, Start-4 0, Start-4 0 1, Start-4 0 1 2 3 4 5, Start-4 0 1 2 3 4 5 6, Start-4 0 1 2 3 4 5 6 7, Start-4 0 1 2 3 4 5 6 7 8, Start-4 0 1 2 3 4 5 6 7 8 9, Start-4 0 1 2 3 4 5 6 7 8 9 10, Start-4 0 1 2 3 4 5 6 7 8 9 10 11, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-5, Start-5 0, Start-5 0 1, Start-5 0 1 2 3 4 5, Start-5 0 1 2 3 4 5 6, Start-5 0 1 2 3 4 5 6 7, Start-5 0 1 2 3 4 5 6 7 8, Start-5 0 1 2 3 4 5 6 7 8 9, Start-5 0 1 2 3 4 5 6 7 8 9 10, Start-5 0 1 2 3 4 5 6 7 8 9 10 11, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16] Col-11: Int Axis Lengths: [5, 15] (NB: Matrix in Row/Column order) [1, 1, 2, 16, 22, 29, 37, 46, 56, 67, 79, 92, 106, 121, 137 2, 2, 3, 17, 23, 30, 38, 47, 57, 68, 80, 93, 107, 122, 138 3, 3, 4, 18, 24, 31, 39, 48, 58, 69, 81, 94, 108, 123, 139 4, 4, 5, 19, 25, 32, 40, 49, 59, 70, 82, 95, 109, 124, 140 5, 5, 6, 20, 26, 33, 41, 50, 60, 71, 83, 96, 110, 125, 141] Try to remove Column:Col-7 After removing Column: Col-7 TableType = 1 Colvirt: Int [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30] Col-4: Bool [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] Col-5: DComplex [(18,0), (17,1), (16,2), (12,6), (11,7), (10,8), (9,9), (8,10), (7,11), (6,12), (5,13), (4,14), (3,15), (2,16), (1,17)] Col-6: float Ndim=4 Axis Lengths: [2, 3, 1, 15] [0, 0, 0, 0][0, 1] [0, 1, 0, 0][2, 3] [0, 2, 0, 0][4, 5] [0, 0, 0, 1][6, 7] [0, 1, 0, 1][8, 9] [0, 2, 0, 1][10, 11] [0, 0, 0, 2][12, 13] [0, 1, 0, 2][14, 15] [0, 2, 0, 2][16, 17] [0, 0, 0, 3][36, 37] [0, 1, 0, 3][38, 39] [0, 2, 0, 3][40, 41] [0, 0, 0, 4][42, 43] [0, 1, 0, 4][44, 45] [0, 2, 0, 4][46, 47] [0, 0, 0, 5][48, 49] [0, 1, 0, 5][50, 51] [0, 2, 0, 5][52, 53] [0, 0, 0, 6][54, 55] [0, 1, 0, 6][56, 57] [0, 2, 0, 6][58, 59] [0, 0, 0, 7][60, 61] [0, 1, 0, 7][62, 63] [0, 2, 0, 7][64, 65] [0, 0, 0, 8][66, 67] [0, 1, 0, 8][68, 69] [0, 2, 0, 8][70, 71] [0, 0, 0, 9][72, 73] [0, 1, 0, 9][74, 75] [0, 2, 0, 9][76, 77] [0, 0, 0, 10][78, 79] [0, 1, 0, 10][80, 81] [0, 2, 0, 10][82, 83] [0, 0, 0, 11][84, 85] [0, 1, 0, 11][86, 87] [0, 2, 0, 11][88, 89] [0, 0, 0, 12][90, 91] [0, 1, 0, 12][92, 93] [0, 2, 0, 12][94, 95] [0, 0, 0, 13][96, 97] [0, 1, 0, 13][98, 99] [0, 2, 0, 13][100, 101] [0, 0, 0, 14][102, 103] [0, 1, 0, 14][104, 105] [0, 2, 0, 14][106, 107] Col-8: Bool Ndim=4 Axis Lengths: [5, 7, 1, 15] [0, 0, 0, 0][0, 0, 1, 0, 0] [0, 1, 0, 0][1, 0, 0, 1, 0] [0, 2, 0, 0][0, 1, 0, 0, 1] [0, 3, 0, 0][0, 0, 1, 0, 0] [0, 4, 0, 0][1, 0, 0, 1, 0] [0, 5, 0, 0][0, 1, 0, 0, 1] [0, 6, 0, 0][0, 0, 1, 0, 0] [0, 0, 0, 1][0, 0, 1, 0, 0] [0, 1, 0, 1][1, 0, 0, 1, 0] [0, 2, 0, 1][0, 1, 0, 0, 1] [0, 3, 0, 1][0, 0, 1, 0, 0] [0, 4, 0, 1][1, 0, 0, 1, 0] [0, 5, 0, 1][0, 1, 0, 0, 1] [0, 6, 0, 1][0, 0, 1, 0, 0] [0, 0, 0, 2][0, 0, 1, 0, 0] [0, 1, 0, 2][1, 0, 0, 1, 0] [0, 2, 0, 2][0, 1, 0, 0, 1] [0, 3, 0, 2][0, 0, 1, 0, 0] [0, 4, 0, 2][1, 0, 0, 1, 0] [0, 5, 0, 2][0, 1, 0, 0, 1] [0, 6, 0, 2][0, 0, 1, 0, 0] [0, 0, 0, 3][0, 0, 1, 0, 0] [0, 1, 0, 3][1, 0, 0, 1, 0] [0, 2, 0, 3][0, 1, 0, 0, 1] [0, 3, 0, 3][0, 0, 1, 0, 0] [0, 4, 0, 3][1, 0, 0, 1, 0] [0, 5, 0, 3][0, 1, 0, 0, 1] [0, 6, 0, 3][0, 0, 1, 0, 0] [0, 0, 0, 4][0, 0, 1, 0, 0] [0, 1, 0, 4][1, 0, 0, 1, 0] [0, 2, 0, 4][0, 1, 0, 0, 1] [0, 3, 0, 4][0, 0, 1, 0, 0] [0, 4, 0, 4][1, 0, 0, 1, 0] [0, 5, 0, 4][0, 1, 0, 0, 1] [0, 6, 0, 4][0, 0, 1, 0, 0] [0, 0, 0, 5][0, 0, 1, 0, 0] [0, 1, 0, 5][1, 0, 0, 1, 0] [0, 2, 0, 5][0, 1, 0, 0, 1] [0, 3, 0, 5][0, 0, 1, 0, 0] [0, 4, 0, 5][1, 0, 0, 1, 0] [0, 5, 0, 5][0, 1, 0, 0, 1] [0, 6, 0, 5][0, 0, 1, 0, 0] [0, 0, 0, 6][0, 0, 1, 0, 0] [0, 1, 0, 6][1, 0, 0, 1, 0] [0, 2, 0, 6][0, 1, 0, 0, 1] [0, 3, 0, 6][0, 0, 1, 0, 0] [0, 4, 0, 6][1, 0, 0, 1, 0] [0, 5, 0, 6][0, 1, 0, 0, 1] [0, 6, 0, 6][0, 0, 1, 0, 0] [0, 0, 0, 7][0, 0, 1, 0, 0] [0, 1, 0, 7][1, 0, 0, 1, 0] [0, 2, 0, 7][0, 1, 0, 0, 1] [0, 3, 0, 7][0, 0, 1, 0, 0] [0, 4, 0, 7][1, 0, 0, 1, 0] [0, 5, 0, 7][0, 1, 0, 0, 1] [0, 6, 0, 7][0, 0, 1, 0, 0] [0, 0, 0, 8][0, 0, 1, 0, 0] [0, 1, 0, 8][1, 0, 0, 1, 0] [0, 2, 0, 8][0, 1, 0, 0, 1] [0, 3, 0, 8][0, 0, 1, 0, 0] [0, 4, 0, 8][1, 0, 0, 1, 0] [0, 5, 0, 8][0, 1, 0, 0, 1] [0, 6, 0, 8][0, 0, 1, 0, 0] [0, 0, 0, 9][0, 0, 1, 0, 0] [0, 1, 0, 9][1, 0, 0, 1, 0] [0, 2, 0, 9][0, 1, 0, 0, 1] [0, 3, 0, 9][0, 0, 1, 0, 0] [0, 4, 0, 9][1, 0, 0, 1, 0] [0, 5, 0, 9][0, 1, 0, 0, 1] [0, 6, 0, 9][0, 0, 1, 0, 0] [0, 0, 0, 10][0, 0, 1, 0, 0] [0, 1, 0, 10][1, 0, 0, 1, 0] [0, 2, 0, 10][0, 1, 0, 0, 1] [0, 3, 0, 10][0, 0, 1, 0, 0] [0, 4, 0, 10][1, 0, 0, 1, 0] [0, 5, 0, 10][0, 1, 0, 0, 1] [0, 6, 0, 10][0, 0, 1, 0, 0] [0, 0, 0, 11][0, 0, 1, 0, 0] [0, 1, 0, 11][1, 0, 0, 1, 0] [0, 2, 0, 11][0, 1, 0, 0, 1] [0, 3, 0, 11][0, 0, 1, 0, 0] [0, 4, 0, 11][1, 0, 0, 1, 0] [0, 5, 0, 11][0, 1, 0, 0, 1] [0, 6, 0, 11][0, 0, 1, 0, 0] [0, 0, 0, 12][0, 0, 1, 0, 0] [0, 1, 0, 12][1, 0, 0, 1, 0] [0, 2, 0, 12][0, 1, 0, 0, 1] [0, 3, 0, 12][0, 0, 1, 0, 0] [0, 4, 0, 12][1, 0, 0, 1, 0] [0, 5, 0, 12][0, 1, 0, 0, 1] [0, 6, 0, 12][0, 0, 1, 0, 0] [0, 0, 0, 13][0, 0, 1, 0, 0] [0, 1, 0, 13][1, 0, 0, 1, 0] [0, 2, 0, 13][0, 1, 0, 0, 1] [0, 3, 0, 13][0, 0, 1, 0, 0] [0, 4, 0, 13][1, 0, 0, 1, 0] [0, 5, 0, 13][0, 1, 0, 0, 1] [0, 6, 0, 13][0, 0, 1, 0, 0] [0, 0, 0, 14][0, 0, 1, 0, 0] [0, 1, 0, 14][1, 0, 0, 1, 0] [0, 2, 0, 14][0, 1, 0, 0, 1] [0, 3, 0, 14][0, 0, 1, 0, 0] [0, 4, 0, 14][1, 0, 0, 1, 0] [0, 5, 0, 14][0, 1, 0, 0, 1] [0, 6, 0, 14][0, 0, 1, 0, 0] Col-9: String Axis Lengths: [5, 15] (NB: Matrix in Row/Column order) [Start-1, Start-1 0, Start-1 0 1, Start-1 0 1 2 3 4 5, Start-1 0 1 2 3 4 5 6, Start-1 0 1 2 3 4 5 6 7, Start-1 0 1 2 3 4 5 6 7 8, Start-1 0 1 2 3 4 5 6 7 8 9, Start-1 0 1 2 3 4 5 6 7 8 9 10, Start-1 0 1 2 3 4 5 6 7 8 9 10 11, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-2, Start-2 0, Start-2 0 1, Start-2 0 1 2 3 4 5, Start-2 0 1 2 3 4 5 6, Start-2 0 1 2 3 4 5 6 7, Start-2 0 1 2 3 4 5 6 7 8, Start-2 0 1 2 3 4 5 6 7 8 9, Start-2 0 1 2 3 4 5 6 7 8 9 10, Start-2 0 1 2 3 4 5 6 7 8 9 10 11, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-3, Start-3 0, Start-3 0 1, Start-3 0 1 2 3 4 5, Start-3 0 1 2 3 4 5 6, Start-3 0 1 2 3 4 5 6 7, Start-3 0 1 2 3 4 5 6 7 8, Start-3 0 1 2 3 4 5 6 7 8 9, Start-3 0 1 2 3 4 5 6 7 8 9 10, Start-3 0 1 2 3 4 5 6 7 8 9 10 11, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-4, Start-4 0, Start-4 0 1, Start-4 0 1 2 3 4 5, Start-4 0 1 2 3 4 5 6, Start-4 0 1 2 3 4 5 6 7, Start-4 0 1 2 3 4 5 6 7 8, Start-4 0 1 2 3 4 5 6 7 8 9, Start-4 0 1 2 3 4 5 6 7 8 9 10, Start-4 0 1 2 3 4 5 6 7 8 9 10 11, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-5, Start-5 0, Start-5 0 1, Start-5 0 1 2 3 4 5, Start-5 0 1 2 3 4 5 6, Start-5 0 1 2 3 4 5 6 7, Start-5 0 1 2 3 4 5 6 7 8, Start-5 0 1 2 3 4 5 6 7 8 9, Start-5 0 1 2 3 4 5 6 7 8 9 10, Start-5 0 1 2 3 4 5 6 7 8 9 10 11, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16] Col-11: Int Axis Lengths: [5, 15] (NB: Matrix in Row/Column order) [1, 1, 2, 16, 22, 29, 37, 46, 56, 67, 79, 92, 106, 121, 137 2, 2, 3, 17, 23, 30, 38, 47, 57, 68, 80, 93, 107, 122, 138 3, 3, 4, 18, 24, 31, 39, 48, 58, 69, 81, 94, 108, 123, 139 4, 4, 5, 19, 25, 32, 40, 49, 59, 70, 82, 95, 109, 124, 140 5, 5, 6, 20, 26, 33, 41, 50, 60, 71, 83, 96, 110, 125, 141] Try to add a string Column: Col-10 and fill it. TableType = 1 Colvirt: Int [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30] Col-4: Bool [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] Col-5: DComplex [(18,0), (17,1), (16,2), (12,6), (11,7), (10,8), (9,9), (8,10), (7,11), (6,12), (5,13), (4,14), (3,15), (2,16), (1,17)] Col-6: float Ndim=4 Axis Lengths: [2, 3, 1, 15] [0, 0, 0, 0][0, 1] [0, 1, 0, 0][2, 3] [0, 2, 0, 0][4, 5] [0, 0, 0, 1][6, 7] [0, 1, 0, 1][8, 9] [0, 2, 0, 1][10, 11] [0, 0, 0, 2][12, 13] [0, 1, 0, 2][14, 15] [0, 2, 0, 2][16, 17] [0, 0, 0, 3][36, 37] [0, 1, 0, 3][38, 39] [0, 2, 0, 3][40, 41] [0, 0, 0, 4][42, 43] [0, 1, 0, 4][44, 45] [0, 2, 0, 4][46, 47] [0, 0, 0, 5][48, 49] [0, 1, 0, 5][50, 51] [0, 2, 0, 5][52, 53] [0, 0, 0, 6][54, 55] [0, 1, 0, 6][56, 57] [0, 2, 0, 6][58, 59] [0, 0, 0, 7][60, 61] [0, 1, 0, 7][62, 63] [0, 2, 0, 7][64, 65] [0, 0, 0, 8][66, 67] [0, 1, 0, 8][68, 69] [0, 2, 0, 8][70, 71] [0, 0, 0, 9][72, 73] [0, 1, 0, 9][74, 75] [0, 2, 0, 9][76, 77] [0, 0, 0, 10][78, 79] [0, 1, 0, 10][80, 81] [0, 2, 0, 10][82, 83] [0, 0, 0, 11][84, 85] [0, 1, 0, 11][86, 87] [0, 2, 0, 11][88, 89] [0, 0, 0, 12][90, 91] [0, 1, 0, 12][92, 93] [0, 2, 0, 12][94, 95] [0, 0, 0, 13][96, 97] [0, 1, 0, 13][98, 99] [0, 2, 0, 13][100, 101] [0, 0, 0, 14][102, 103] [0, 1, 0, 14][104, 105] [0, 2, 0, 14][106, 107] Col-8: Bool Ndim=4 Axis Lengths: [5, 7, 1, 15] [0, 0, 0, 0][0, 0, 1, 0, 0] [0, 1, 0, 0][1, 0, 0, 1, 0] [0, 2, 0, 0][0, 1, 0, 0, 1] [0, 3, 0, 0][0, 0, 1, 0, 0] [0, 4, 0, 0][1, 0, 0, 1, 0] [0, 5, 0, 0][0, 1, 0, 0, 1] [0, 6, 0, 0][0, 0, 1, 0, 0] [0, 0, 0, 1][0, 0, 1, 0, 0] [0, 1, 0, 1][1, 0, 0, 1, 0] [0, 2, 0, 1][0, 1, 0, 0, 1] [0, 3, 0, 1][0, 0, 1, 0, 0] [0, 4, 0, 1][1, 0, 0, 1, 0] [0, 5, 0, 1][0, 1, 0, 0, 1] [0, 6, 0, 1][0, 0, 1, 0, 0] [0, 0, 0, 2][0, 0, 1, 0, 0] [0, 1, 0, 2][1, 0, 0, 1, 0] [0, 2, 0, 2][0, 1, 0, 0, 1] [0, 3, 0, 2][0, 0, 1, 0, 0] [0, 4, 0, 2][1, 0, 0, 1, 0] [0, 5, 0, 2][0, 1, 0, 0, 1] [0, 6, 0, 2][0, 0, 1, 0, 0] [0, 0, 0, 3][0, 0, 1, 0, 0] [0, 1, 0, 3][1, 0, 0, 1, 0] [0, 2, 0, 3][0, 1, 0, 0, 1] [0, 3, 0, 3][0, 0, 1, 0, 0] [0, 4, 0, 3][1, 0, 0, 1, 0] [0, 5, 0, 3][0, 1, 0, 0, 1] [0, 6, 0, 3][0, 0, 1, 0, 0] [0, 0, 0, 4][0, 0, 1, 0, 0] [0, 1, 0, 4][1, 0, 0, 1, 0] [0, 2, 0, 4][0, 1, 0, 0, 1] [0, 3, 0, 4][0, 0, 1, 0, 0] [0, 4, 0, 4][1, 0, 0, 1, 0] [0, 5, 0, 4][0, 1, 0, 0, 1] [0, 6, 0, 4][0, 0, 1, 0, 0] [0, 0, 0, 5][0, 0, 1, 0, 0] [0, 1, 0, 5][1, 0, 0, 1, 0] [0, 2, 0, 5][0, 1, 0, 0, 1] [0, 3, 0, 5][0, 0, 1, 0, 0] [0, 4, 0, 5][1, 0, 0, 1, 0] [0, 5, 0, 5][0, 1, 0, 0, 1] [0, 6, 0, 5][0, 0, 1, 0, 0] [0, 0, 0, 6][0, 0, 1, 0, 0] [0, 1, 0, 6][1, 0, 0, 1, 0] [0, 2, 0, 6][0, 1, 0, 0, 1] [0, 3, 0, 6][0, 0, 1, 0, 0] [0, 4, 0, 6][1, 0, 0, 1, 0] [0, 5, 0, 6][0, 1, 0, 0, 1] [0, 6, 0, 6][0, 0, 1, 0, 0] [0, 0, 0, 7][0, 0, 1, 0, 0] [0, 1, 0, 7][1, 0, 0, 1, 0] [0, 2, 0, 7][0, 1, 0, 0, 1] [0, 3, 0, 7][0, 0, 1, 0, 0] [0, 4, 0, 7][1, 0, 0, 1, 0] [0, 5, 0, 7][0, 1, 0, 0, 1] [0, 6, 0, 7][0, 0, 1, 0, 0] [0, 0, 0, 8][0, 0, 1, 0, 0] [0, 1, 0, 8][1, 0, 0, 1, 0] [0, 2, 0, 8][0, 1, 0, 0, 1] [0, 3, 0, 8][0, 0, 1, 0, 0] [0, 4, 0, 8][1, 0, 0, 1, 0] [0, 5, 0, 8][0, 1, 0, 0, 1] [0, 6, 0, 8][0, 0, 1, 0, 0] [0, 0, 0, 9][0, 0, 1, 0, 0] [0, 1, 0, 9][1, 0, 0, 1, 0] [0, 2, 0, 9][0, 1, 0, 0, 1] [0, 3, 0, 9][0, 0, 1, 0, 0] [0, 4, 0, 9][1, 0, 0, 1, 0] [0, 5, 0, 9][0, 1, 0, 0, 1] [0, 6, 0, 9][0, 0, 1, 0, 0] [0, 0, 0, 10][0, 0, 1, 0, 0] [0, 1, 0, 10][1, 0, 0, 1, 0] [0, 2, 0, 10][0, 1, 0, 0, 1] [0, 3, 0, 10][0, 0, 1, 0, 0] [0, 4, 0, 10][1, 0, 0, 1, 0] [0, 5, 0, 10][0, 1, 0, 0, 1] [0, 6, 0, 10][0, 0, 1, 0, 0] [0, 0, 0, 11][0, 0, 1, 0, 0] [0, 1, 0, 11][1, 0, 0, 1, 0] [0, 2, 0, 11][0, 1, 0, 0, 1] [0, 3, 0, 11][0, 0, 1, 0, 0] [0, 4, 0, 11][1, 0, 0, 1, 0] [0, 5, 0, 11][0, 1, 0, 0, 1] [0, 6, 0, 11][0, 0, 1, 0, 0] [0, 0, 0, 12][0, 0, 1, 0, 0] [0, 1, 0, 12][1, 0, 0, 1, 0] [0, 2, 0, 12][0, 1, 0, 0, 1] [0, 3, 0, 12][0, 0, 1, 0, 0] [0, 4, 0, 12][1, 0, 0, 1, 0] [0, 5, 0, 12][0, 1, 0, 0, 1] [0, 6, 0, 12][0, 0, 1, 0, 0] [0, 0, 0, 13][0, 0, 1, 0, 0] [0, 1, 0, 13][1, 0, 0, 1, 0] [0, 2, 0, 13][0, 1, 0, 0, 1] [0, 3, 0, 13][0, 0, 1, 0, 0] [0, 4, 0, 13][1, 0, 0, 1, 0] [0, 5, 0, 13][0, 1, 0, 0, 1] [0, 6, 0, 13][0, 0, 1, 0, 0] [0, 0, 0, 14][0, 0, 1, 0, 0] [0, 1, 0, 14][1, 0, 0, 1, 0] [0, 2, 0, 14][0, 1, 0, 0, 1] [0, 3, 0, 14][0, 0, 1, 0, 0] [0, 4, 0, 14][1, 0, 0, 1, 0] [0, 5, 0, 14][0, 1, 0, 0, 1] [0, 6, 0, 14][0, 0, 1, 0, 0] Col-9: String Axis Lengths: [5, 15] (NB: Matrix in Row/Column order) [Start-1, Start-1 0, Start-1 0 1, Start-1 0 1 2 3 4 5, Start-1 0 1 2 3 4 5 6, Start-1 0 1 2 3 4 5 6 7, Start-1 0 1 2 3 4 5 6 7 8, Start-1 0 1 2 3 4 5 6 7 8 9, Start-1 0 1 2 3 4 5 6 7 8 9 10, Start-1 0 1 2 3 4 5 6 7 8 9 10 11, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-2, Start-2 0, Start-2 0 1, Start-2 0 1 2 3 4 5, Start-2 0 1 2 3 4 5 6, Start-2 0 1 2 3 4 5 6 7, Start-2 0 1 2 3 4 5 6 7 8, Start-2 0 1 2 3 4 5 6 7 8 9, Start-2 0 1 2 3 4 5 6 7 8 9 10, Start-2 0 1 2 3 4 5 6 7 8 9 10 11, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-2 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-3, Start-3 0, Start-3 0 1, Start-3 0 1 2 3 4 5, Start-3 0 1 2 3 4 5 6, Start-3 0 1 2 3 4 5 6 7, Start-3 0 1 2 3 4 5 6 7 8, Start-3 0 1 2 3 4 5 6 7 8 9, Start-3 0 1 2 3 4 5 6 7 8 9 10, Start-3 0 1 2 3 4 5 6 7 8 9 10 11, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-3 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-4, Start-4 0, Start-4 0 1, Start-4 0 1 2 3 4 5, Start-4 0 1 2 3 4 5 6, Start-4 0 1 2 3 4 5 6 7, Start-4 0 1 2 3 4 5 6 7 8, Start-4 0 1 2 3 4 5 6 7 8 9, Start-4 0 1 2 3 4 5 6 7 8 9 10, Start-4 0 1 2 3 4 5 6 7 8 9 10 11, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Start-5, Start-5 0, Start-5 0 1, Start-5 0 1 2 3 4 5, Start-5 0 1 2 3 4 5 6, Start-5 0 1 2 3 4 5 6 7, Start-5 0 1 2 3 4 5 6 7 8, Start-5 0 1 2 3 4 5 6 7 8 9, Start-5 0 1 2 3 4 5 6 7 8 9 10, Start-5 0 1 2 3 4 5 6 7 8 9 10 11, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15, Start-5 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16] Col-11: Int Axis Lengths: [5, 15] (NB: Matrix in Row/Column order) [1, 1, 2, 16, 22, 29, 37, 46, 56, 67, 79, 92, 106, 121, 137 2, 2, 3, 17, 23, 30, 38, 47, 57, 68, 80, 93, 107, 122, 138 3, 3, 4, 18, 24, 31, 39, 48, 58, 69, 81, 94, 108, 123, 139 4, 4, 5, 19, 25, 32, 40, 49, 59, 70, 82, 95, 109, 124, 140 5, 5, 6, 20, 26, 33, 41, 50, 60, 71, 83, 96, 110, 125, 141] Col-10: String [String-1, String-1 0, String-1 0 1, String-1 0 1 2, String-1 0 1 2 3, String-1 0 1 2 3 4, String-1 0 1 2 3 4 5, String-1 0 1 2 3 4 5 6, String-1 0 1 2 3 4 5 6 7, String-1 0 1 2 3 4 5 6 7 8, String-1 0 1 2 3 4 5 6 7 8 9, String-1 0 1 2 3 4 5 6 7 8 9 10, String-1 0 1 2 3 4 5 6 7 8 9 10 11, String-1 0 1 2 3 4 5 6 7 8 9 10 11 12, String-1 0 1 2 3 4 5 6 7 8 9 10 11 12 13] after removing several rows at once: TableType = 1 Colvirt: Int [] Col-4: Bool [] Col-5: DComplex [] Col-6: float [] Col-8: Bool [] Col-9: String [] Col-11: Int [] Col-10: String [] casacore-3.7.1/tables/Tables/test/tReadAsciiTable.cc000066400000000000000000000504071476623553700222640ustar00rootroot00000000000000//# tReadAsciiTable.cc: Test program for the ReadAsciiTable functions //# Copyright (C) 1994,1995,1996,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include // Test program for the ReadAsciiTable functions // This program tests the functions in ReadAsciiTable.h. // It uses some files in the test directory. The directory of those // files is given in argv[1]. // The results are written to stdout. The script executing this program, // compares the results with the reference output file. void a (const String& dir); void aa (const String& dir); void ab (const String& dir); void a1 (const String& dir, const String& commentMarker, Int firstLine, Int lastLine); void a2 (const String& dir, const String& commentMarker, Int firstLine, Int lastLine); void b (const String& dir, const String& suffix, Char separator, const String& commentMarker, Int firstLine, Int lastLine); void b1 (const String& dir); void b2 (const String& dir); void b3 (const String& dir, const IPosition& autoShape); void erroneous(); int main (int argc, const char* argv[]) { try { String dir; if (argc > 1) { dir = argv[1]; } a (dir); aa (dir); ab (dir); a1 (dir, "", -1, -1); a1 (dir, "1 ", -1, -1); a1 (dir, "", 1, 2); a1 (dir, "", 2, -1); a2 (dir, "", -1, -1); a2 (dir, "1 ", -1, -1); a2 (dir, "", 1, 2); a2 (dir, "", 2, -1); b (dir, "", ' ', " *#", 1, -1); b (dir, "", ' ', " #", 2, 3); b (dir, "c", ',', "", -1, -1); b (dir, "c", ',', "K", -1, -1); b1 (dir); b2 (dir); b3 (dir, IPosition(1,0)); b3 (dir, IPosition(2,1,10)); b3 (dir, IPosition(1,10)); b3 (dir, IPosition(1,5)); b3 (dir, IPosition(1,15)); b3 (dir, IPosition(2,2,5)); b3 (dir, IPosition(2,3,5)); b3 (dir, IPosition(2,0,5)); erroneous(); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } void a (const String& dir) { cout << ">>>" << endl; String formStr = readAsciiTable (dir + "tReadAsciiTable.in_tab", "", "tReadAsciiTable_tmp.data_tab"); cout << "<<<" << endl; cout << "Input format: [" << formStr << ']' << endl; Table tab("tReadAsciiTable_tmp.data_tab"); cout << endl; cout << tab.nrow() << " rows, " << tab.tableDesc().ncolumn() << " columns" << endl; ScalarColumn coli (tab,"COLI"); ScalarColumn colf (tab,"COLF"); ScalarColumn cold (tab,"COLD"); ScalarColumn colx (tab,"COLX"); ScalarColumn colz (tab,"COLZ"); ScalarColumn cols (tab,"COLS"); ScalarColumn colra (tab,"COLRA"); ScalarColumn coldec (tab,"COLDEC"); for (uInt i=0; i>>" << endl; String formStr; Table tab = readAsciiTable (formStr, Table::Plain, dir + "tReadAsciiTable.in_tab", "", "tReadAsciiTable_tmp.data_tab1"); AlwaysAssertExit (tab.tableType() == Table::Plain); cout << "<<<" << endl; cout << "Input format: [" << formStr << ']' << endl; cout << endl; cout << tab.nrow() << " rows, " << tab.tableDesc().ncolumn() << " columns" << endl; ScalarColumn coli (tab,"COLI"); ScalarColumn colf (tab,"COLF"); ScalarColumn cold (tab,"COLD"); ScalarColumn colx (tab,"COLX"); ScalarColumn colz (tab,"COLZ"); ScalarColumn cols (tab,"COLS"); ScalarColumn colra (tab,"COLRA"); ScalarColumn coldec (tab,"COLDEC"); for (uInt i=0; i>>" << endl; String formStr; Table tab = readAsciiTable (formStr, Table::Memory, dir + "tReadAsciiTable.in_tab", "", ""); AlwaysAssertExit (tab.tableType() == Table::Memory); cout << "<<<" << endl; cout << "Input format: [" << formStr << ']' << endl; cout << endl; cout << tab.nrow() << " rows, " << tab.tableDesc().ncolumn() << " columns" << endl; ScalarColumn coli (tab,"COLI"); ScalarColumn colf (tab,"COLF"); ScalarColumn cold (tab,"COLD"); ScalarColumn colx (tab,"COLX"); ScalarColumn colz (tab,"COLZ"); ScalarColumn cols (tab,"COLS"); ScalarColumn colra (tab,"COLRA"); ScalarColumn coldec (tab,"COLDEC"); for (uInt i=0; i>>" << endl; String formStr = readAsciiTable (dir + "tReadAsciiTable.in_tah", "", "tReadAsciiTable_tmp.data_tah", True, ' ', commentMarker, firstLine, lastLine); cout << "<<<" << endl; cout << "Input format: [" << formStr << ']' << endl; Table tab("tReadAsciiTable_tmp.data_tah"); cout << endl; cout << tab.nrow() << " rows, " << tab.tableDesc().ncolumn() << " columns" << endl; ScalarColumn col1 (tab,"Column1"); ScalarColumn col2 (tab,"Column2"); ScalarColumn col3 (tab,"Column3"); ScalarColumn col4 (tab,"Column4"); ScalarColumn col5 (tab,"Column5"); ScalarColumn col6 (tab,"Column6"); ScalarColumn col7 (tab,"Column7"); ScalarColumn col8 (tab,"Column8"); for (uInt i=0; i>>" << endl; Vector names(7); Vector types(7); names[0] = "COLI"; names[1] = "COLF"; names[2] = "COLD"; names[3] = "COLX"; names[4] = "COLZ1"; names[5] = "COLZ2"; names[6] = "COLS"; types[0] = "I"; types[1] = "R"; types[2] = "D"; types[3] = "X"; types[4] = "R"; types[5] = "D"; types[6] = "A"; String formStr = readAsciiTable (dir + "tReadAsciiTable.in_tah", "", "tReadAsciiTable_tmp.data_tah", names, types, ' ', commentMarker, firstLine, lastLine); cout << "<<<" << endl; cout << "Input format: [" << formStr << ']' << endl; Table tab("tReadAsciiTable_tmp.data_tah"); cout << endl; cout << tab.nrow() << " rows, " << tab.tableDesc().ncolumn() << " columns" << endl; ScalarColumn coli (tab,"COLI"); ScalarColumn colf (tab,"COLF"); ScalarColumn cold (tab,"COLD"); ScalarColumn colx (tab,"COLX"); ScalarColumn colz1 (tab,"COLZ1"); ScalarColumn colz2 (tab,"COLZ2"); ScalarColumn cols (tab,"COLS"); for (uInt i=0; i>>" << endl; String formStr = readAsciiTable (dir + "tReadAsciiTable.in_tkh" + suffix, dir + "tReadAsciiTable.in_tkd" + suffix, "tReadAsciiTable_tmp", "tReadAsciiTable_tmp.data_tk", separator, commentMarker, firstLine, lastLine); cout << "<<<" << endl; cout << "Input format: [" << formStr << ']' << endl; cout << endl; TableDesc tabdesc("tReadAsciiTable_tmp"); tabdesc.show(); cout << endl; Table tab("tReadAsciiTable_tmp.data_tk"); const TableRecord& keys = tab.keywordSet(); cout << keys.description(); if (commentMarker != "K") { cout << "KEYS " << keys.asShort ("KEYS") << endl; cout << "KEYI " << keys.asInt ("KEYI") << endl; cout << "KEYF " << keys.asfloat ("KEYF") << endl; cout << "KEYD " << keys.asdouble ("KEYD") << endl; cout << "KEYX " << keys.asComplex ("KEYX") << endl; cout << "KEYZ " << keys.asComplex ("KEYZ") << endl; cout << "KEYDX " << keys.asDComplex ("KEYDX") << endl; cout << "KEYDZ " << keys.asDComplex ("KEYDZ") << endl; cout << "KEYA " << keys.asString ("KEYA") << endl; cout << "KEYB " << keys.asBool ("KEYB") << endl; cout << "KEYSV " << keys.asArrayShort ("KEYSV") << endl; cout << "KEYIV " << keys.asArrayInt ("KEYIV") << endl; cout << "KEYFV " << keys.asArrayfloat ("KEYFV") << endl; cout << "KEYDV " << keys.asArraydouble ("KEYDV") << endl; cout << "KEYXC " << keys.asArrayComplex ("KEYXC") << endl; cout << "KEYZV " << keys.asArrayComplex ("KEYZV") << endl; cout << "KEYDXC " << keys.asArrayDComplex ("KEYDXC") << endl; cout << "KEYDZV " << keys.asArrayDComplex ("KEYDZV") << endl; cout << "KEYAV " << keys.asArrayString ("KEYAV") << endl; cout << "KEYBV " << keys.asArrayBool ("KEYBV") << endl; cout << endl; } { TableColumn tabcol (tab, "COLI"); const TableRecord& keycol = tabcol.keywordSet(); cout << keycol.description(); cout << "IKEYS " << keycol.asString ("IKEYS") << endl; } { TableColumn tabcol (tab, "COLDX"); const TableRecord& keycol = tabcol.keywordSet(); cout << keycol.description(); cout << "IKEYS " << keycol.asString ("IKEYS") << endl; cout << "DKEYS " << keycol.asString ("DKEYS") << endl; } cout << tab.nrow() << " rows, " << tab.tableDesc().ncolumn() << " columns" << endl; ScalarColumn cols (tab,"COLS"); ScalarColumn coli (tab,"COLI"); ScalarColumn colf (tab,"COLF"); ScalarColumn cold (tab,"COLD"); ScalarColumn colx (tab,"COLX"); ScalarColumn colz (tab,"COLZ"); ScalarColumn coldx (tab,"COLDX"); ScalarColumn coldz (tab,"COLDZ"); ScalarColumn cola (tab,"COLA"); ScalarColumn colb (tab,"COLB"); for (uInt i=0; i>>" << endl; String formStr = readAsciiTable (dir + "tReadAsciiTable.in_tkh", "", "tReadAsciiTable_tmp.data_tk", False, ' ', " #"); cout << "<<<" << endl; cout << "Input format: [" << formStr << ']' << endl; cout << endl; Table tab("tReadAsciiTable_tmp.data_tk"); const TableRecord& keys = tab.keywordSet(); cout << keys.description(); cout << "KEYS " << keys.asShort ("KEYS") << endl; cout << "KEYI " << keys.asInt ("KEYI") << endl; cout << "KEYF " << keys.asfloat ("KEYF") << endl; cout << "KEYD " << keys.asdouble ("KEYD") << endl; cout << "KEYX " << keys.asComplex ("KEYX") << endl; cout << "KEYZ " << keys.asComplex ("KEYZ") << endl; cout << "KEYDX " << keys.asDComplex ("KEYDX") << endl; cout << "KEYDZ " << keys.asDComplex ("KEYDZ") << endl; cout << "KEYA " << keys.asString ("KEYA") << endl; cout << "KEYB " << keys.asBool ("KEYB") << endl; cout << "KEYSV " << keys.asArrayShort ("KEYSV") << endl; cout << "KEYIV " << keys.asArrayInt ("KEYIV") << endl; cout << "KEYFV " << keys.asArrayfloat ("KEYFV") << endl; cout << "KEYDV " << keys.asArraydouble ("KEYDV") << endl; cout << "KEYXC " << keys.asArrayComplex ("KEYXC") << endl; cout << "KEYZV " << keys.asArrayComplex ("KEYZV") << endl; cout << "KEYDXC " << keys.asArrayDComplex ("KEYDXC") << endl; cout << "KEYDZV " << keys.asArrayDComplex ("KEYDZV") << endl; cout << "KEYAV " << keys.asArrayString ("KEYAV") << endl; cout << "KEYBV " << keys.asArrayBool ("KEYBV") << endl; cout << endl; { TableColumn tabcol (tab, "COLI"); const TableRecord& keycol = tabcol.keywordSet(); cout << keycol.description(); cout << "IKEYS " << keycol.asString ("IKEYS") << endl; } { TableColumn tabcol (tab, "COLDX"); const TableRecord& keycol = tabcol.keywordSet(); cout << keycol.description(); cout << "IKEYS " << keycol.asString ("IKEYS") << endl; cout << "DKEYS " << keycol.asString ("DKEYS") << endl; } cout << tab.nrow() << " rows, " << tab.tableDesc().ncolumn() << " columns" << endl; ScalarColumn cols (tab,"COLS"); ScalarColumn coli (tab,"COLI"); ScalarColumn colf (tab,"COLF"); ScalarColumn cold (tab,"COLD"); ScalarColumn colx (tab,"COLX"); ScalarColumn colz (tab,"COLZ"); ScalarColumn coldx (tab,"COLDX"); ScalarColumn coldz (tab,"COLDZ"); ScalarColumn cola (tab,"COLA"); ScalarColumn colb (tab,"COLB"); for (uInt i=0; i>>" << endl; String formStr = readAsciiTable (dir + "tReadAsciiTable.in_tkh", "", "tReadAsciiTable_tmp.data_tk", True, ' ', " #"); cout << "<<<" << endl; cout << "Input format: [" << formStr << ']' << endl; cout << endl; Table tab("tReadAsciiTable_tmp.data_tk"); const TableRecord& keys = tab.keywordSet(); cout << keys.description(); cout << "KEYS " << keys.asShort ("KEYS") << endl; cout << "KEYI " << keys.asInt ("KEYI") << endl; cout << "KEYF " << keys.asfloat ("KEYF") << endl; cout << "KEYD " << keys.asdouble ("KEYD") << endl; cout << "KEYX " << keys.asComplex ("KEYX") << endl; cout << "KEYZ " << keys.asComplex ("KEYZ") << endl; cout << "KEYDX " << keys.asDComplex ("KEYDX") << endl; cout << "KEYDZ " << keys.asDComplex ("KEYDZ") << endl; cout << "KEYA " << keys.asString ("KEYA") << endl; cout << "KEYB " << keys.asBool ("KEYB") << endl; cout << "KEYSV " << keys.asArrayShort ("KEYSV") << endl; cout << "KEYIV " << keys.asArrayInt ("KEYIV") << endl; cout << "KEYFV " << keys.asArrayfloat ("KEYFV") << endl; cout << "KEYDV " << keys.asArraydouble ("KEYDV") << endl; cout << "KEYXC " << keys.asArrayComplex ("KEYXC") << endl; cout << "KEYZV " << keys.asArrayComplex ("KEYZV") << endl; cout << "KEYDXC " << keys.asArrayDComplex ("KEYDXC") << endl; cout << "KEYDZV " << keys.asArrayDComplex ("KEYDZV") << endl; cout << "KEYAV " << keys.asArrayString ("KEYAV") << endl; cout << "KEYBV " << keys.asArrayBool ("KEYBV") << endl; cout << endl; cout << tab.nrow() << " rows, " << tab.tableDesc().ncolumn() << " columns" << endl; ScalarColumn col1 (tab,"Column1"); ScalarColumn col2 (tab,"Column2"); ScalarColumn col3 (tab,"Column3"); ScalarColumn col4 (tab,"Column4"); ScalarColumn col5 (tab,"Column5"); ScalarColumn col6 (tab,"Column6"); ScalarColumn col7 (tab,"Column7"); ScalarColumn col8 (tab,"Column8"); ScalarColumn col9 (tab,"Column9"); ScalarColumn col10 (tab,"Column10"); for (uInt i=0; i>>" << endl; String formStr = readAsciiTable (dir + "tReadAsciiTable.in_tkh", "", "tReadAsciiTable_tmp.data_tk", True, ' ', " #", 1, -1, autoShape); cout << "<<<" << endl; cout << "Input format: [" << formStr << ']' << endl; cout << "shape=" << autoShape << endl;; Table tab("tReadAsciiTable_tmp.data_tk"); cout << tab.nrow() << " rows, " << tab.tableDesc().ncolumn() << " columns" << endl; ArrayColumn col1 (tab,"Column1"); for (uInt i=0; i>> <<< Input format: [COLI=I, COLF=R, COLD=D, COLX=X, COLZ=Z, COLS=A, COLRA=HMS, COLDEC=DMS] 2 rows, 8 columns 1 1.1 1.11 (1.12,1.13) (1.13977,0.0228797) Str1 0.261799 0.261799 10 11 12 (13,14) (14.4189,4.13456) String17 6.28319 6.28319 >>> <<< Input format: [COLI=I, COLF=R, COLD=D, COLX=X, COLZ=Z, COLS=A, COLRA=HMS, COLDEC=DMS] 2 rows, 8 columns 1 1.1 1.11 (1.12,1.13) (1.13977,0.0228797) Str1 0.261799 0.261799 10 11 12 (13,14) (14.4189,4.13456) String17 6.28319 6.28319 >>> <<< Input format: [COLI=I, COLF=R, COLD=D, COLX=X, COLZ=Z, COLS=A, COLRA=HMS, COLDEC=DMS] 2 rows, 8 columns 1 1.1 1.11 (1.12,1.13) (1.13977,0.0228797) Str1 0.261799 0.261799 10 11 12 (13,14) (14.4189,4.13456) String17 6.28319 6.28319 >>> <<< Input format: [Column1=I, Column2=D, Column3=D, Column4=D, Column5=D, Column6=D, Column7=D, Column8=A] 3 rows, 8 columns 1 1.1 1.11 1.12 1.13 1.14 1.15 11 11.1 12.1 13.1 14.1 15.1 16.1 str2a 10 11 12 13 14 15 16 str2 >>> <<< Input format: [Column1=I, Column2=D, Column3=D, Column4=D, Column5=D, Column6=D, Column7=D, Column8=A] 2 rows, 8 columns 11 11.1 12.1 13.1 14.1 15.1 16.1 str2a 10 11 12 13 14 15 16 str2 >>> <<< Input format: [Column1=I, Column2=D, Column3=D, Column4=D, Column5=D, Column6=D, Column7=D, Column8=A] 2 rows, 8 columns 1 1.1 1.11 1.12 1.13 1.14 1.15 11 11.1 12.1 13.1 14.1 15.1 16.1 str2a >>> <<< Input format: [Column1=I, Column2=D, Column3=D, Column4=D, Column5=D, Column6=D, Column7=D, Column8=A] 2 rows, 8 columns 11 11.1 12.1 13.1 14.1 15.1 16.1 str2a 10 11 12 13 14 15 16 str2 >>> <<< Input format: [COLI=I, COLF=R, COLD=D, COLX=X, COLZ1=R, COLZ2=D, COLS=A] 3 rows, 7 columns 1 1.1 1.11 (1.12,1.13) 1.14 1.15 11 11.1 12.1 (13.1,14.1) 15.1 16.1 str2a 10 11 12 (13,14) 15 16 str2 >>> <<< Input format: [COLI=I, COLF=R, COLD=D, COLX=X, COLZ1=R, COLZ2=D, COLS=A] 2 rows, 7 columns 11 11.1 12.1 (13.1,14.1) 15.1 16.1 str2a 10 11 12 (13,14) 15 16 str2 >>> <<< Input format: [COLI=I, COLF=R, COLD=D, COLX=X, COLZ1=R, COLZ2=D, COLS=A] 2 rows, 7 columns 1 1.1 1.11 (1.12,1.13) 1.14 1.15 11 11.1 12.1 (13.1,14.1) 15.1 16.1 str2a >>> <<< >>> <<< Input format: [COLI=I, COLF=R, COLD=D, COLX=X, COLZ1=R, COLZ2=D, COLS=A] 2 rows, 7 columns 11 11.1 12.1 (13.1,14.1) 15.1 16.1 str2a 10 11 12 (13,14) 15 16 str2 >>> <<< Input format: [COLS=S, COLI=I, COLF=R, COLD=D, COLX=X, COLDX=DX, COLZ=Z, COLDZ=DZ, COLA=A, COLB=B] TableDesc tReadAsciiTable_tmp version (Directory ./) --------- Comment: #Keywords = 0 #Columns = 10 Name=COLS DataType=Short DataManager=StandardStMan/StandardStMan Default=0 Comment = #keywords=0 Name=COLI DataType=Int DataManager=StandardStMan/StandardStMan Default=0 Comment = #keywords=0 Name=COLF DataType=float DataManager=StandardStMan/StandardStMan Default=0 Comment = #keywords=0 Name=COLD DataType=double DataManager=StandardStMan/StandardStMan Default=0 Comment = #keywords=0 Name=COLX DataType=Complex DataManager=StandardStMan/StandardStMan Default=(0,0) Comment = #keywords=0 Name=COLDX DataType=DComplex DataManager=StandardStMan/StandardStMan Default=(0,0) Comment = #keywords=0 Name=COLZ DataType=Complex DataManager=StandardStMan/StandardStMan Default=(0,0) Comment = #keywords=0 Name=COLDZ DataType=DComplex DataManager=StandardStMan/StandardStMan Default=(0,0) Comment = #keywords=0 Name=COLA DataType=String DataManager=StandardStMan/StandardStMan Default= Comment = #keywords=0 Name=COLB DataType=Bool DataManager=StandardStMan/StandardStMan Default=0 Comment = #keywords=0 0 KEYS : Short 1 KEYSV : Array[-1] 2 KEYI : Int 3 KEYIV : Array[-1] 4 KEYF : float 5 KEYFV : Array[-1] 6 KEYD : double 7 KEYDV : Array[-1] 8 KEYX : Complex 9 KEYXC : Array[-1] 10 KEYZ : Complex 11 KEYZV : Array[-1] 12 KEYDX : DComplex 13 KEYDXC : Array[-1] 14 KEYDZ : DComplex 15 KEYDZV : Array[-1] 16 KEYA : String 17 KEYAV : Array[-1] 18 KEYB : Bool 19 KEYBV : Array[-1] KEYS 20 KEYI 10 KEYF 1.2 KEYD 1.23457 KEYX (-1.5,-3) KEYZ (-2.99897,0.0785308) KEYDX (-2.5,-4) KEYDZ (-3.99619,0.174478) KEYA 1 2 3 4 5 KEYB 1 KEYSV [10, 11, 12] KEYIV [11, 12, 13, 14] KEYFV [-3.2, 0, 5.6] KEYDV [1, 2, 3, 4, 5, 6, 7, 8, 9] KEYXC [(0,1), (2,3), (4,5), (6,7), (8,9)] KEYZV [(0,0), (0.199997,0.00104719), (0.399985,0.00349061)] KEYDXC [(1,2), (3,4), (5,6), (7,8), (9,10)] KEYDZV [(0.0999994,0.000349065), (0.299993,0.00209438), (0.499973,0.00523589)] KEYAV [ 1 2 , AAA, BBB, bbb, CCc, C, @#$%^&*()] KEYBV [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1] 0 IKEYS : String IKEYS coli key 0 IKEYS : String 1 DKEYS : String IKEYS coldx ikey DKEYS coldx dkey 3 rows, 10 columns 0 1 1.1 1.11 (1.12,1.13) (2.12,2.13) (1.13977,0.0228797) (2.13849,0.0802838) Str1 1 -2 -1 -1.1 -1.11 (-1.2,-1.3) (-2.2,-2.3) (-1.39952,0.0366477) (-2.39772,0.104687) Str1 3 1 123 10 11 12 (13,14) (23,-24) (14.4189,4.13456) (22.4699,-10.9593) 0 >>> <<< Input format: [COLS=S, COLI=I, COLF=R, COLD=D, COLX=X, COLDX=DX, COLZ=Z, COLDZ=DZ, COLA=A, COLB=B] TableDesc tReadAsciiTable_tmp version (Directory ./) --------- Comment: #Keywords = 0 #Columns = 10 Name=COLS DataType=Short DataManager=StandardStMan/StandardStMan Default=0 Comment = #keywords=0 Name=COLI DataType=Int DataManager=StandardStMan/StandardStMan Default=0 Comment = #keywords=0 Name=COLF DataType=float DataManager=StandardStMan/StandardStMan Default=0 Comment = #keywords=0 Name=COLD DataType=double DataManager=StandardStMan/StandardStMan Default=0 Comment = #keywords=0 Name=COLX DataType=Complex DataManager=StandardStMan/StandardStMan Default=(0,0) Comment = #keywords=0 Name=COLDX DataType=DComplex DataManager=StandardStMan/StandardStMan Default=(0,0) Comment = #keywords=0 Name=COLZ DataType=Complex DataManager=StandardStMan/StandardStMan Default=(0,0) Comment = #keywords=0 Name=COLDZ DataType=DComplex DataManager=StandardStMan/StandardStMan Default=(0,0) Comment = #keywords=0 Name=COLA DataType=String DataManager=StandardStMan/StandardStMan Default= Comment = #keywords=0 Name=COLB DataType=Bool DataManager=StandardStMan/StandardStMan Default=0 Comment = #keywords=0 0 KEYS : Short 1 KEYSV : Array[-1] 2 KEYI : Int 3 KEYIV : Array[-1] 4 KEYF : float 5 KEYFV : Array[-1] 6 KEYD : double 7 KEYDV : Array[-1] 8 KEYX : Complex 9 KEYXC : Array[-1] 10 KEYZ : Complex 11 KEYZV : Array[-1] 12 KEYDX : DComplex 13 KEYDXC : Array[-1] 14 KEYDZ : DComplex 15 KEYDZV : Array[-1] 16 KEYA : String 17 KEYAV : Array[-1] 18 KEYB : Bool 19 KEYBV : Array[-1] KEYS 20 KEYI 10 KEYF 1.2 KEYD 1.23457 KEYX (-1.5,-3) KEYZ (-2.99897,0.0785308) KEYDX (-2.5,-4) KEYDZ (-3.99619,0.174478) KEYA 1 2 3 4 5 KEYB 1 KEYSV [10, 11, 12] KEYIV [11, 12, 13, 14] KEYFV [-3.2, 0, 5.6] KEYDV [1, 2, 3, 4, 5, 6, 7, 8, 9] KEYXC [(0,1), (2,3), (4,5), (6,7), (8,9)] KEYZV [(0,0), (0.199997,0.00104719), (0.399985,0.00349061)] KEYDXC [(1,2), (3,4), (5,6), (7,8), (9,10)] KEYDZV [(0.0999994,0.000349065), (0.299993,0.00209438), (0.499973,0.00523589)] KEYAV [ 1 2 , AAA, BBB, bbb, CCc, C, @#$%^&*()] KEYBV [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1] 0 IKEYS : String IKEYS coli key 0 IKEYS : String 1 DKEYS : String IKEYS coldx ikey DKEYS coldx dkey 2 rows, 10 columns 0 1 1.1 1.11 (1.12,1.13) (2.12,2.13) (1.13977,0.0228797) (2.13849,0.0802838) Str1 1 -2 -1 -1.1 -1.11 (-1.2,-1.3) (-2.2,-2.3) (-1.39952,0.0366477) (-2.39772,0.104687) Str1 3 1 >>> <<< Input format: [COLS=S, COLI=I, COLF=R, COLD=D, COLX=X, COLDX=DX, COLZ=Z, COLDZ=DZ, COLA=A, COLB=B] TableDesc tReadAsciiTable_tmp version (Directory ./) --------- Comment: #Keywords = 0 #Columns = 10 Name=COLS DataType=Short DataManager=StandardStMan/StandardStMan Default=0 Comment = #keywords=0 Name=COLI DataType=Int DataManager=StandardStMan/StandardStMan Default=0 Comment = #keywords=0 Name=COLF DataType=float DataManager=StandardStMan/StandardStMan Default=0 Comment = #keywords=0 Name=COLD DataType=double DataManager=StandardStMan/StandardStMan Default=0 Comment = #keywords=0 Name=COLX DataType=Complex DataManager=StandardStMan/StandardStMan Default=(0,0) Comment = #keywords=0 Name=COLDX DataType=DComplex DataManager=StandardStMan/StandardStMan Default=(0,0) Comment = #keywords=0 Name=COLZ DataType=Complex DataManager=StandardStMan/StandardStMan Default=(0,0) Comment = #keywords=0 Name=COLDZ DataType=DComplex DataManager=StandardStMan/StandardStMan Default=(0,0) Comment = #keywords=0 Name=COLA DataType=String DataManager=StandardStMan/StandardStMan Default= Comment = #keywords=0 Name=COLB DataType=Bool DataManager=StandardStMan/StandardStMan Default=0 Comment = #keywords=0 0 KEYS : Short 1 KEYSV : Array[-1] 2 KEYI : Int 3 KEYIV : Array[-1] 4 KEYF : float 5 KEYFV : Array[-1] 6 KEYD : double 7 KEYDV : Array[-1] 8 KEYX : Complex 9 KEYXC : Array[-1] 10 KEYZ : Complex 11 KEYZV : Array[-1] 12 KEYDX : DComplex 13 KEYDXC : Array[-1] 14 KEYDZ : DComplex 15 KEYDZV : Array[-1] 16 KEYA : String 17 KEYAV : Array[-1] 18 KEYB : Bool 19 KEYBV : Array[-1] KEYS 20 KEYI 10 KEYF 1.2 KEYD 1.23457 KEYX (-1.5,-3) KEYZ (-2.99897,0.0785308) KEYDX (-2.5,-4) KEYDZ (-3.99619,0.174478) KEYA 1, 2, 3, 4, 5 KEYB 1 KEYSV [10, 0, 12] KEYIV [11, 12, 13, 14] KEYFV [-3.2, 0, 0, 5.6] KEYDV [1, 2, 3, 4, 5, 6, 7, 8, 0] KEYXC [(0,1), (2,3), (4,5), (6,7), (8,0)] KEYZV [(0,0), (0.199997,0.00104719), (0.399985,0.00349061)] KEYDXC [(1,2), (3,4), (5,6), (7,8), (9,10)] KEYDZV [(0.0999994,0.000349065), (0.299993,0.00209438), (0.499973,0.00523589)] KEYAV [ 1 2 , AAA, BBB, bbb, , , CCc, C, @#$%^&*()] KEYBV [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1] 0 IKEYS : String IKEYS coli key 0 IKEYS : String 1 DKEYS : String IKEYS coldx ikey DKEYS coldx dkey 3 rows, 10 columns 0 1 1.1 1.11 (1.12,1.13) (2.12,2.13) (1.13977,0.0228797) (2.13849,0.0802838) Str1 1 -2 -1 0 -1.11 (0,0) (-2.3,0) (-1.39952,0.0366477) (-2.39772,0.104687) Str1 3 0 123 0 0 0 (0,0) (0,0) (0,0) (0,0) 0 >>> <<< Input format: [COLS=S, COLI=I, COLF=R, COLD=D, COLX=X, COLDX=DX, COLZ=Z, COLDZ=DZ, COLA=A, COLB=B] TableDesc tReadAsciiTable_tmp version (Directory ./) --------- Comment: #Keywords = 0 #Columns = 10 Name=COLS DataType=Short DataManager=StandardStMan/StandardStMan Default=0 Comment = #keywords=0 Name=COLI DataType=Int DataManager=StandardStMan/StandardStMan Default=0 Comment = #keywords=0 Name=COLF DataType=float DataManager=StandardStMan/StandardStMan Default=0 Comment = #keywords=0 Name=COLD DataType=double DataManager=StandardStMan/StandardStMan Default=0 Comment = #keywords=0 Name=COLX DataType=Complex DataManager=StandardStMan/StandardStMan Default=(0,0) Comment = #keywords=0 Name=COLDX DataType=DComplex DataManager=StandardStMan/StandardStMan Default=(0,0) Comment = #keywords=0 Name=COLZ DataType=Complex DataManager=StandardStMan/StandardStMan Default=(0,0) Comment = #keywords=0 Name=COLDZ DataType=DComplex DataManager=StandardStMan/StandardStMan Default=(0,0) Comment = #keywords=0 Name=COLA DataType=String DataManager=StandardStMan/StandardStMan Default= Comment = #keywords=0 Name=COLB DataType=Bool DataManager=StandardStMan/StandardStMan Default=0 Comment = #keywords=0 0 IKEYS : String IKEYS coli key 0 IKEYS : String 1 DKEYS : String IKEYS coldx ikey DKEYS coldx dkey 3 rows, 10 columns 0 1 1.1 1.11 (1.12,1.13) (2.12,2.13) (1.13977,0.0228797) (2.13849,0.0802838) Str1 1 -2 -1 0 -1.11 (0,0) (-2.3,0) (-1.39952,0.0366477) (-2.39772,0.104687) Str1 3 0 123 0 0 0 (0,0) (0,0) (0,0) (0,0) 0 >>> <<< Input format: [COLS=S, COLI=I, COLF=R, COLD=D, COLX=X, COLDX=DX, COLZ=Z, COLDZ=DZ, COLA=A, COLB=B] 0 KEYS : Short 1 KEYSV : Array[-1] 2 KEYI : Int 3 KEYIV : Array[-1] 4 KEYF : float 5 KEYFV : Array[-1] 6 KEYD : double 7 KEYDV : Array[-1] 8 KEYX : Complex 9 KEYXC : Array[-1] 10 KEYZ : Complex 11 KEYZV : Array[-1] 12 KEYDX : DComplex 13 KEYDXC : Array[-1] 14 KEYDZ : DComplex 15 KEYDZV : Array[-1] 16 KEYA : String 17 KEYAV : Array[-1] 18 KEYB : Bool 19 KEYBV : Array[-1] KEYS 20 KEYI 10 KEYF 1.2 KEYD 1.23457 KEYX (-1.5,-3) KEYZ (-2.99897,0.0785308) KEYDX (-2.5,-4) KEYDZ (-3.99619,0.174478) KEYA 1 2 3 4 5 KEYB 1 KEYSV [10, 11, 12] KEYIV [11, 12, 13, 14] KEYFV [-3.2, 0, 5.6] KEYDV [1, 2, 3, 4, 5, 6, 7, 8, 9] KEYXC [(0,1), (2,3), (4,5), (6,7), (8,9)] KEYZV [(0,0), (0.199997,0.00104719), (0.399985,0.00349061)] KEYDXC [(1,2), (3,4), (5,6), (7,8), (9,10)] KEYDZV [(0.0999994,0.000349065), (0.299993,0.00209438), (0.499973,0.00523589)] KEYAV [ 1 2 , AAA, BBB, bbb, CCc, C, @#$%^&*()] KEYBV [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1] 0 IKEYS : String IKEYS coli key 0 IKEYS : String 1 DKEYS : String IKEYS coldx ikey DKEYS coldx dkey 0 rows, 10 columns >>> <<< Input format: [Column1=A, Column2=A, Column3=A, Column4=A, Column5=A, Column6=A, Column7=A, Column8=A, Column9=A, Column10=A] 0 KEYS : Short 1 KEYSV : Array[-1] 2 KEYI : Int 3 KEYIV : Array[-1] 4 KEYF : float 5 KEYFV : Array[-1] 6 KEYD : double 7 KEYDV : Array[-1] 8 KEYX : Complex 9 KEYXC : Array[-1] 10 KEYZ : Complex 11 KEYZV : Array[-1] 12 KEYDX : DComplex 13 KEYDXC : Array[-1] 14 KEYDZ : DComplex 15 KEYDZV : Array[-1] 16 KEYA : String 17 KEYAV : Array[-1] 18 KEYB : Bool 19 KEYBV : Array[-1] KEYS 20 KEYI 10 KEYF 1.2 KEYD 1.23457 KEYX (-1.5,-3) KEYZ (-2.99897,0.0785308) KEYDX (-2.5,-4) KEYDZ (-3.99619,0.174478) KEYA 1 2 3 4 5 KEYB 1 KEYSV [10, 11, 12] KEYIV [11, 12, 13, 14] KEYFV [-3.2, 0, 5.6] KEYDV [1, 2, 3, 4, 5, 6, 7, 8, 9] KEYXC [(0,1), (2,3), (4,5), (6,7), (8,9)] KEYZV [(0,0), (0.199997,0.00104719), (0.399985,0.00349061)] KEYDXC [(1,2), (3,4), (5,6), (7,8), (9,10)] KEYDZV [(0.0999994,0.000349065), (0.299993,0.00209438), (0.499973,0.00523589)] KEYAV [ 1 2 , AAA, BBB, bbb, CCc, C, @#$%^&*()] KEYBV [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1] 2 rows, 10 columns COLS COLI COLF COLD COLX COLDX COLZ COLDZ COLA COLB s i R d X dx z DZ A B >>> <<< Input format: [Column1=A0] shape=[0] 2 rows, 1 columns [COLS, COLI, COLF, COLD, COLX, COLDX, COLZ, COLDZ, COLA, COLB] [s, i, R, d, X, dx, z, DZ, A, B] >>> <<< Input format: [Column1=A1,10] shape=[1, 10] 2 rows, 1 columns Axis Lengths: [1, 10] (NB: Matrix in Row/Column order) [COLS, COLI, COLF, COLD, COLX, COLDX, COLZ, COLDZ, COLA, COLB] Axis Lengths: [1, 10] (NB: Matrix in Row/Column order) [s, i, R, d, X, dx, z, DZ, A, B] >>> <<< Input format: [Column1=A10] shape=[10] 2 rows, 1 columns [COLS, COLI, COLF, COLD, COLX, COLDX, COLZ, COLDZ, COLA, COLB] [s, i, R, d, X, dx, z, DZ, A, B] >>> <<< Input format: [Column1=A5] shape=[5] 2 rows, 1 columns [COLS, COLI, COLF, COLD, COLX] [s, i, R, d, X] >>> <<< Input format: [Column1=A15] shape=[15] 2 rows, 1 columns [COLS, COLI, COLF, COLD, COLX, COLDX, COLZ, COLDZ, COLA, COLB, , , , , ] [s, i, R, d, X, dx, z, DZ, A, B, , , , , ] >>> <<< Input format: [Column1=A2,5] shape=[2, 5] 2 rows, 1 columns Axis Lengths: [2, 5] (NB: Matrix in Row/Column order) [COLS, COLF, COLX, COLZ, COLA COLI, COLD, COLDX, COLDZ, COLB] Axis Lengths: [2, 5] (NB: Matrix in Row/Column order) [s, R, X, z, A i, d, dx, DZ, B] >>> <<< Input format: [Column1=A3,5] shape=[3, 5] 2 rows, 1 columns Axis Lengths: [3, 5] (NB: Matrix in Row/Column order) [COLS, COLD, COLZ, COLB, COLI, COLX, COLDZ, , COLF, COLDX, COLA, , ] Axis Lengths: [3, 5] (NB: Matrix in Row/Column order) [s, d, z, B, i, X, DZ, , R, dx, A, , ] >>> <<< Input format: [Column1=A0,5] shape=[0, 5] 2 rows, 1 columns Axis Lengths: [2, 5] (NB: Matrix in Row/Column order) [COLS, COLF, COLX, COLZ, COLA COLI, COLD, COLDX, COLDZ, COLB] Axis Lengths: [2, 5] (NB: Matrix in Row/Column order) [s, R, X, z, A i, d, dx, DZ, B] ReadAsciiTable: mismatching COLUMN NAMES and TYPES lines in tReadAsciiTable_tmp.header ReadAsciiTable: mismatching COLUMN NAMES and TYPES lines in tReadAsciiTable_tmp.header ReadAsciiTable: invalid type specifier 'F' ReadAsciiTable: only last column can have variable shaped arrays ReadAsciiTable: multiple variable axes in type string 'I0,0' ReadAsciiTable: invalid shape value '' in type string 'I,1' ReadAsciiTable: invalid shape value '' in type string 'I1,' ReadAsciiTable: invalid shape value '1.' in type string 'I1.' ReadAsciiTable: no type info in type string '1' ReadAsciiTable: cannot read first header line of tReadAsciiTable_tmp.header ReadAsciiTable: no COLUMN TYPES line in tReadAsciiTable_tmp.header ReadAsciiTable: no .endkey line in tReadAsciiTable_tmp.header ReadAsciiTable: no keyword name or type in line 2 of tReadAsciiTable_tmp.header casacore-3.7.1/tables/Tables/test/tReadAsciiTable2.cc000066400000000000000000000031571476623553700223460ustar00rootroot00000000000000//# tReadAsciiTable.cc: Test program for ReadAsciiTable::stringToPos //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include using namespace casacore; int main() { char buf[32768]; while (cin.getline (buf, sizeof(buf))) { if (buf[0] == 'd') { std::cout << ReadAsciiTable::stringToPos (String(buf+1), True) << std::endl; } else { std::cout << ReadAsciiTable::stringToPos (String(buf), False) << std::endl; } } return 0; } casacore-3.7.1/tables/Tables/test/tReadAsciiTable2.in000066400000000000000000000001611476623553700223570ustar00rootroot0000000000000012. 12. : 11:60 - 11 : 60 11 . 59 . 60 + 11 59 60 d12 d 12: d 11 : 60 d -11:60 d 11.59.60 d 11 59 60 casacore-3.7.1/tables/Tables/test/tReadAsciiTable2.out000066400000000000000000000001301476623553700225540ustar00rootroot0000000000000012 3.14159 3.14159 -3.14159 0.20944 3.14159 12 0.20944 0.20944 -0.20944 0.20944 0.20944 casacore-3.7.1/tables/Tables/test/tRefRows.cc000066400000000000000000000076221476623553700210600ustar00rootroot00000000000000//# tRefRows.cc: This program tests class RefRows //# Copyright (C) 1998,1999,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include // // Test program for class RefRows. // void doIt() { Vector vec(18); vec(0) = 1; vec(1) = 1; vec(2) = 2; vec(3) = 3; vec(4) = 4; vec(5) = 6; vec(6) = 7; vec(7) = 9; vec(8) = 11; vec(9) = 5; vec(10)= 10; vec(11)= 15; vec(12)= 20; vec(13)= 25; vec(14)= 30; vec(15)= 35; vec(16)= 40; vec(17)= 4; { RefRows ref(vec); AlwaysAssertExit (ref.nrows() == 18); AlwaysAssertExit (!ref.isSliced()); RefRowsSliceIter iter1(ref); cout << "unSliced,unCollapse" << endl; while (!iter1.pastEnd()) { cout << iter1.sliceStart() << ' ' << iter1.sliceEnd() << ' ' << iter1.sliceIncr() << endl; iter1++; } } { RefRows ref(vec, True); AlwaysAssertExit (ref.nrows() == 7); AlwaysAssertExit (ref.isSliced()); RefRowsSliceIter iter1(ref); cout << "sliced" << endl; while (!iter1.pastEnd()) { cout << iter1.sliceStart() << ' ' << iter1.sliceEnd() << ' ' << iter1.sliceIncr() << endl; iter1++; } } { RefRows ref(vec, False, True); AlwaysAssertExit (ref.nrows() == 18); AlwaysAssertExit (ref.isSliced()); RefRowsSliceIter iter1(ref); cout << "unSliced,collapse" << endl; while (!iter1.pastEnd()) { cout << iter1.sliceStart() << ' ' << iter1.sliceEnd() << ' ' << iter1.sliceIncr() << endl; iter1++; } } { RefRows ref(3,9,2); AlwaysAssertExit (ref.nrows() == 4); AlwaysAssertExit (ref.isSliced()); RefRowsSliceIter iter1(ref); cout << "one slice" << endl; while (!iter1.pastEnd()) { cout << iter1.sliceStart() << ' ' << iter1.sliceEnd() << ' ' << iter1.sliceIncr() << endl; iter1++; } } { Vector rows(18); indgen (rows, rownr_t(1)); rows(17) = 0; RefRows ref(rows); AlwaysAssertExit (ref.nrows() == 18); AlwaysAssertExit (!ref.isSliced()); cout << ref.convert(vec) << endl; } { Vector rows(18); indgen (rows, rownr_t(1)); rows(17) = 0; RefRows ref(rows, False, True); AlwaysAssertExit (ref.nrows() == 18); AlwaysAssertExit (ref.isSliced()); RefRowsSliceIter iter1(ref); while (!iter1.pastEnd()) { cout << iter1.sliceStart() << ' ' << iter1.sliceEnd() << ' ' << iter1.sliceIncr() << endl; iter1++; } cout << ref.convert(vec) << endl;; } } int main() { try { doIt(); } catch (std::exception& x) { cout << "\nCaught an exception: " << x.what() << endl; return 1; } return 0; // successfully executed } casacore-3.7.1/tables/Tables/test/tRefRows.out000066400000000000000000000006261476623553700212770ustar00rootroot00000000000000unSliced,unCollapse 1 1 1 1 1 1 2 2 1 3 3 1 4 4 1 6 6 1 7 7 1 9 9 1 11 11 1 5 5 1 10 10 1 15 15 1 20 20 1 25 25 1 30 30 1 35 35 1 40 40 1 4 4 1 sliced 1 1 2 3 4 6 7 9 11 5 10 15 20 25 30 35 40 4 unSliced,collapse 1 1 1 1 4 1 6 6 1 7 11 2 5 40 5 4 4 1 one slice 3 9 2 [1, 2, 3, 4, 6, 7, 9, 11, 5, 10, 15, 20, 25, 30, 35, 40, 4, 1] 1 17 1 0 0 1 [1, 2, 3, 4, 6, 7, 9, 11, 5, 10, 15, 20, 25, 30, 35, 40, 4, 1] casacore-3.7.1/tables/Tables/test/tRefTable.cc000066400000000000000000000103301476623553700211430ustar00rootroot00000000000000//# tRefTable.cc: Test program for RefTable::addColumn //# Copyright (C) 2010 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include // // Test program for RefTable::addColumn // void readTab (const String& tabName, uInt nrow, uInt ncol) { cout << "read " << tabName << endl; Table tab(tabName); AlwaysAssertExit (tab.tableDesc().ncolumn() == ncol); AlwaysAssertExit (tab.nrow() == nrow); ScalarColumn ab(tab,"ab"); ScalarColumn ac(tab,"ac"); ScalarColumn ad(tab,"ad"); ScalarColumn ax(tab,"ax"); for (uInt i=0; i("ab","Comment for column ab")); td.addColumn (ScalarColumnDesc("ad","comment for ad")); td.addColumn (ScalarColumnDesc("ag")); // Now create a new table from the description. SetupNewTable newtab("tRefTable_tmp.data", td, Table::New); Table tab(newtab, 10); // Add a column. tab.addColumn (ScalarColumnDesc ("ac")); ScalarColumn ab(tab,"ab"); ScalarColumn ac(tab,"ac"); ScalarColumn ad(tab,"ad"); TableColumn ag(tab,"ag"); for (Int i=0; i<10; i++) { ab.put (i, i); ac.put (i, i+1); ad.put (i, i+2); ag.put (i, ad); } } void makeRef() { Table tab("tRefTable_tmp.data", Table::Update); Table reftab(tab.project(Block(1, "ab"))); AlwaysAssertExit (tab.tableDesc().ncolumn() == 4); AlwaysAssertExit (reftab.tableDesc().ncolumn() == 1); reftab.addColumn (ScalarColumnDesc ("ac"), False); AlwaysAssertExit (tab.tableDesc().ncolumn() == 4); AlwaysAssertExit (reftab.tableDesc().ncolumn() == 2); reftab.addColumn (ScalarColumnDesc ("ad"), True); AlwaysAssertExit (tab.tableDesc().ncolumn() == 4); AlwaysAssertExit (reftab.tableDesc().ncolumn() == 3); reftab.addColumn (ScalarColumnDesc ("ax"), True); AlwaysAssertExit (tab.tableDesc().ncolumn() == 5); AlwaysAssertExit (reftab.tableDesc().ncolumn() == 4); reftab.rename ("tRefTable_tmp.dataref", Table::New); reftab.flush(); ScalarColumn ax(reftab, "ax"); for (uInt i=0; i #include #include #include #include #include #include #include #include int main() { // first, create a short table with a few columns to use during testing // Do the descriptor first cout << "Building some test tables." << endl; TableDesc td; td.addColumn(ScalarColumnDesc("ICol1")); td.addColumn(ScalarColumnDesc("ICol2")); td.addColumn(ScalarColumnDesc("FCol")); td.addColumn(ScalarColumnDesc("DCol")); td.addColumn(ArrayColumnDesc("IACol", "", IPosition(1,5), ColumnDesc::Direct)); // Ok, now a table with 5 rows SetupNewTable newtab("tRowCopier_tmp_0",td, Table::Scratch); Table maintab(newtab, 5); // fill the above ScalarColumn ic1(maintab,"ICol1"), ic2(maintab,"ICol2"); ScalarColumn fc(maintab,"FCol"); ScalarColumn dc(maintab,"DCol"); ArrayColumn ac(maintab,"IACol"); Vector vtmp(5); for (Int i=0; i<5; i++) { ic1.put(i,i); ic2.put(i, i*10); fc.put(i, Float(i)/10.); dc.put(i, Double(i*i)); indgen(vtmp, i*10, 10); ac.put(i, vtmp); } // Create 2 tables to be used by RowCopier testing // first, one exactly the same size and columns as maintab // we can't simply reuse newtab because each table needs a unique name SetupNewTable newtab1("tRowCopier_tmp_1",td,Table::Scratch); Table exacttab(newtab1, 5); // second, one with some of the same columns as maintab, plus others TableDesc td2; td2.addColumn(ScalarColumnDesc("ICol1")); td2.addColumn(ScalarColumnDesc("ICol3")); td2.addColumn(ScalarColumnDesc("uICol")); td2.addColumn(ArrayColumnDesc("IACol","", IPosition(1,5),ColumnDesc::Direct)); td2.addColumn(ArrayColumnDesc("IACol2","", IPosition(1,3),ColumnDesc::Direct)); SetupNewTable newtab2("tRowCopier_tmp_2",td2, Table::Scratch); Table partialtab(newtab2, 5); ScalarColumn ic3col(partialtab,"ICol3"); for (uInt j=0; j ic1main(maintab, "ICol1"), ic1copy(exacttab, "ICol1"); if (anyNE(ic1main, ic1copy)) { cout << "An exact copy was not made of ICol1" << endl; cout << "tRowCopier fails!" << endl; return 1; } TableVector ic2main(maintab, "ICol2"), ic2copy(exacttab, "ICol2"); if (anyNE(ic2main, ic2copy)) { cout << "An exact copy was not made of ICol2" << endl; cout << "tRowCopier fails!" << endl; return 1; } TableVector fcmain(maintab, "FCol"), fccopy(exacttab, "FCol"); if (anyNE(fcmain, fccopy)) { cout << "An exact copy was not made of FCol" << endl; cout << "tRowCopier fails!" << endl; return 1; } TableVector dcmain(maintab, "DCol"), dccopy(exacttab, "DCol"); if (anyNE(dcmain, dccopy)) { cout << "An exact copy was not made of DCol" << endl; cout << "tRowCopier fails!" << endl; return 1; } // and check each Vector in IACol ArrayColumn iamain(maintab, "IACol"), iacopy(exacttab, "IACol"); for (rownr = 0; rownr < maintab.nrow(); rownr++) { if (anyNE(iamain(rownr), iacopy(rownr))) { cout << "An exact copy was not made of the array column " << "at row number " << rownr << endl; cout << "tRowCopier fails!" << endl; return 1; } } cout << "Exact copy passes." << endl; } // copy as much of maintab to partialtab as allowed // That pretty much amounts to ICol1 and IACol { cout << "\nCopy as much as allowed between two tables" << endl; RowCopier limited(partialtab, maintab); uInt rownr; for (rownr=0; rownr ic1main(maintab, "ICol1"); TableVector ic1part(partialtab, "ICol1"); if (anyNE(ic1main, ic1part)) { cout << "ICol1 copy differs!" << endl; cout << "tRowCopier fails!" << endl; return 1; } // and check each Vector in IACol ArrayColumn mia(maintab, "IACol"), pia(partialtab, "IACol"); for (rownr = 0; rownr < maintab.nrow(); rownr++) { if (anyNE(mia(rownr), pia(rownr))) { cout << "The array columns do not match " << "at row number " << rownr << endl; cout << "tRowCopier fails!" << endl; return 1; } } cout << "limited copy passes." << endl; } // copy using named columns from ICol1 to ICol3 { cout << "\nNamed copy of ICol1 to ICol3" << endl; // first, verify that any of ICol1 and ICol3 are not already equal TableVector ic1main(maintab, "ICol1"); TableVector ic3part(partialtab, "ICol3"); if (anyEQ(ic1main, ic3part)) { cout << "Hmm, ICol1 and ICol3 are already equal in some values!" << endl; cout << "That should not happen yet" << endl; for (uInt rownr=0; rownr < maintab.nrow(); rownr++) { cout << rownr << " " << ic1main(rownr) << " " << ic3part(rownr) << endl; } cout << "tRowCopier fails!" << endl; return 1; } Vector inname(1), outname(1); inname(0) = "ICol1"; outname(0) = "ICol3"; RowCopier named(partialtab, maintab, outname, inname); for (uInt rownr=0; rownr inname(1), outname(1); uInt inrownr, outrownr; inname(0) = "ICol1"; outname(0) = "ICol3"; RowCopier named(partialtab, maintab, outname, inname); TableVector ic1main(maintab, "ICol1"); TableVector ic3part(partialtab, "ICol3"); for (inrownr=0, outrownr=maintab.nrow()-1; inrownr colname(1); colname(0) = "Garbage"; RowCopier rc(partialtab, maintab, colname, colname); } catch (TableError& x) { caught = True; } if (caught) { cout << "OK" << endl; caught = False; } else { cout << "FAILS!" << endl; return 1; } // non-conformant columns // different types try { cout << "Different column types : "; Vector inname(1), outname(1); inname(0) = "FCol"; outname(0) = "DCol"; RowCopier rc(maintab, maintab, inname, outname); } catch (TableError& x) { caught = True; } if (caught) { cout << "OK" << endl; caught = False; } else { cout << "FAILS!" << endl; return 1; } // different dimensionality, Scalar versus Array try { cout << "Different column dimensionality, scalar vs array : "; Vector inname(1), outname(1); inname(0) = "ICol1"; outname(0) = "IACol"; RowCopier rc(maintab, maintab, inname, outname); } catch (TableError& x) { caught = True; } if (caught) { cout << "OK" << endl; caught = False; } else { cout << "FAILS!" << endl; return 1; } // different dimensionality of input strings try { cout << "Different dimensionality of the two Vector : "; Vector inname(1), outname(2); inname(0) = "IACol"; outname(0) = "IACol"; outname(1) = "DCol"; RowCopier rc(maintab, maintab, inname, outname); } catch (TableError& x) { caught = True; } if (caught) { cout << "OK" << endl; caught = False; } else { cout << "FAILS!" << endl; return 1; } // and finally, some checks that False is returned when appropriate. cout << "\nChecking that copy() returns False when appropriate" << endl; { RowCopier rc(maintab, maintab); // inrow exceeds number of input rows cout << "Input row number exceeds number of rows in input column : "; if (rc.copy(maintab.nrow()+1), 0) { cout << "FAILS!" << endl; return 1; } else { cout << "OK" << endl; } // outrow exceeds number of output rows cout << "Output row number exceeds number of rows in output column : "; if (rc.copy(0,maintab.nrow()+1)) { cout << "FAILS!" << endl; return 1; } else { cout << "OK" << endl; } } cout << "\ntRowCopier finishes successfully" << endl; return 0; } casacore-3.7.1/tables/Tables/test/tRowCopier.out000066400000000000000000000014501476623553700216150ustar00rootroot00000000000000Building some test tables. Make an exact copy using rowcopier Exact copy passes. Copy as much as allowed between two tables limited copy passes. Named copy of ICol1 to ICol3 Named copy, same row number, passes. Named copy of ICol1 to ICol3, in reverse order named copy, reverse order, passes. Checking that exceptions are thrown properly. Note: since RowCopier is NOT derived from Cleanup, this section causes memory leaks. Invalid column name : OK Different column types : OK Different column dimensionality, scalar vs array : OK Different dimensionality of the two Vector : OK Checking that copy() returns False when appropriate Input row number exceeds number of rows in input column : OK Output row number exceeds number of rows in output column : OK tRowCopier finishes successfully casacore-3.7.1/tables/Tables/test/tScalarRecordColumn.cc000066400000000000000000000146671476623553700232220ustar00rootroot00000000000000//# tScalarRecordColumn.cc: Test program for the ScalarRecordColumn classes //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include // // Test program for the ScalarRecordColumn classes // void a() { // Test constructors and assignment. ScalarRecordColumnDesc abd1("ab1"); AlwaysAssertExit (abd1.comment() == ""); ScalarRecordColumnDesc abd2("ab2", "comment"); AlwaysAssertExit (abd2.comment() == "comment"); AlwaysAssertExit (abd2.name() == "ab2"); abd2 = abd1; AlwaysAssertExit (abd2.comment() == ""); AlwaysAssertExit (abd2.name() == "ab1"); AlwaysAssertExit (abd2.comment() == ""); ScalarRecordColumnDesc abd3a("ab3", "comm1", "IncrementalStMan", "A1"); ScalarRecordColumnDesc abd3(abd3a); AlwaysAssertExit (abd3.name() == "ab3"); AlwaysAssertExit (abd3.comment() == "comm1"); AlwaysAssertExit (abd3.dataManagerType() == "IncrementalStMan"); AlwaysAssertExit (abd3.dataManagerGroup() == "A1"); AlwaysAssertExit (abd3.dataType() == TpRecord); // Build and show the table description. TableDesc td("", "1", TableDesc::Scratch); td.addColumn (abd1); td.addColumn (abd3); td.show (cout); // Now create a new table from the description. SetupNewTable newtab("tScalarRecordColumn_tmp.data", td, Table::New); Table tab(newtab, 10); // Get the column ( consisting of empty records only). // Put the column. ScalarColumn ab1(tab,"ab1"); ScalarColumn ab3(tab,"ab3"); Vector vec1 = ab1.getColumn(); Vector vec3 = ab3.getColumn(); ab1.putColumn (vec1); TableRecord rec1; TableRecord rec3; uInt i; // Check if they are empty. // Fill each cell with a record (add a field for each row). AlwaysAssertExit (vec1.nelements() == 10); AlwaysAssertExit (vec3.nelements() == 10); for (i=0; i<10; i++) { AlwaysAssertExit (vec1(i).nfields() == 0); AlwaysAssertExit (vec3(i).nfields() == 0); AlwaysAssertExit (ab1.isDefined(i)); AlwaysAssertExit (ab1(i).nfields() == 0); AlwaysAssertExit (ab3.isDefined(i)); // In IncrementalStMan rows default to the previous one. if (i<2) { AlwaysAssertExit (ab3(i).nfields() == 0); } else { AlwaysAssertExit (ab3(i).nfields() == 1); } rec1.define (i, i); ab1.put (i, rec1); if (i%2 == 1) { rec3.define ("fld1", i/2); ab3.put (i, rec3); } } // Check if the column is correctly filled. for (i=0; i<10; i++) { ab1.get (i, rec1); AlwaysAssertExit (rec1.nfields() == i+1); for (uInt j=0; j<=i; j++) { AlwaysAssertExit (rec1.asuInt(j) == j); } ab3.get (i, rec1); if (i==0) { AlwaysAssertExit (rec1.nfields() == 0); } else { AlwaysAssertExit (rec1.nfields() == 1); AlwaysAssertExit (rec1.asuInt("fld1") == (i-1)/2); } } // Let us change rows 1, 5, and 9. Slicer cells(Slice(1,3,4)); Vector vec = ab3.getColumnRange (cells); AlwaysAssertExit (vec.nelements() == 3); for (i=0; i<3; i++) { vec(i).define ("fld2", i+10); } ab3.putColumnRange (cells, vec); for (i=0; i<10; i++) { rec1 = ab3(i); if (i==0) { AlwaysAssertExit (rec1.nfields() == 0); } else { if (i%4 == 1) { AlwaysAssertExit (rec1.nfields() == 2); AlwaysAssertExit (rec1.asuInt("fld2") == 10+i/4); } else { AlwaysAssertExit (rec1.nfields() == 1); } AlwaysAssertExit (rec1.asuInt("fld1") == (i-1)/2); } } // At this point the table is destructed, thus written. } void b() { // Open the table. Table tab("tScalarRecordColumn_tmp.data"); ScalarColumn ab1(tab,"ab1"); ScalarColumn ab3(tab,"ab3"); Vector vec = ab1.getColumn(); TableRecord rec; uInt i; // Check if the columns are written and read back correctly. for (i=0; i<10; i++) { ab1.get (i, rec); AlwaysAssertExit (rec.nfields() == i+1); AlwaysAssertExit (vec(i).nfields() == i+1); for (uInt j=0; j<=i; j++) { AlwaysAssertExit (rec.asuInt(j) == j); AlwaysAssertExit (vec(i).asuInt(j) == j); } rec = ab3(i); if (i==0) { AlwaysAssertExit (rec.nfields() == 0); } else { if (i%4 == 1) { AlwaysAssertExit (rec.nfields() == 2); AlwaysAssertExit (rec.asuInt("fld2") == 10+i/4); } else { AlwaysAssertExit (rec.nfields() == 1); } AlwaysAssertExit (rec.asuInt("fld1") == (i-1)/2); } } vec.resize(0); vec = ab1.getColumnRange (Slice(1,4)); AlwaysAssertExit (vec.nelements() == 4); for (i=0; i<4; i++) { AlwaysAssertExit (vec(i).nfields() == i+2); for (uInt j=0; j<=i+1; j++) { AlwaysAssertExit (vec(i).asuInt(j) == j); } } } int main() { try { a(); b(); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } casacore-3.7.1/tables/Tables/test/tScalarRecordColumn.out000066400000000000000000000004631476623553700234310ustar00rootroot00000000000000 TableDesc version 1 (Directory **SCRATCH**) --------- Comment: #Keywords = 0 #Columns = 2 Name=ab1 DataType=Record DataManager=StandardStMan/StandardStMan Comment = #keywords=0 Name=ab3 DataType=Record DataManager=IncrementalStMan/A1 Comment = comm1 #keywords=0 casacore-3.7.1/tables/Tables/test/tTable.cc000066400000000000000000000760371476623553700205260ustar00rootroot00000000000000//# tTable.cc: Test program for the Table classes //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Test program for the Table classes // // This program tests the class SetupNewTable and related classes. // The results are written to stdout. The script executing this program, // compares the results with the reference output file. // Define the callback for handling (scratch) tables. void cbFunc (const String& name, Bool isScratch, const String& oldname) { String nm1 = name.empty() ? name : Path(name).baseName(); String nm2 = oldname.empty() ? oldname : Path(oldname).baseName(); cout << "ScratchCallBack: name=" << nm1 << " isScratch=" << isScratch << " oldName=" << nm2 << endl; } // Remove the dirname from the table name in an error message. String removeDir (const String& msg) { String s = msg; s.gsub (Regex("/.*/t"), "t"); return s; } // Define the endian format. #ifdef AIPS_LITTLE_ENDIAN Table::EndianFormat theEndianFormat = Table::LittleEndian; #else Table::EndianFormat theEndianFormat = Table::BigEndian; #endif // First build a description. void a (const StorageOption& stopt, Bool doExcp) { // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.comment() = "A test of class Table"; td.addColumn (ScalarColumnDesc("ab","Comment for column ab")); td.addColumn (ScalarColumnDesc("ad","comment for ad")); td.addColumn (ScalarColumnDesc("ag")); td.addColumn (ArrayColumnDesc("arr1",IPosition(3,2,3,4), ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc("arr2",0)); td.addColumn (ArrayColumnDesc("arr3",0,ColumnDesc::Direct)); // Now create a new table from the description. // Use copy constructor to test if it works fine. // (newtab and newtabcp have the same underlying object). SetupNewTable newtab("tTable_tmp.data", td, Table::New, stopt); SetupNewTable newtabcp(newtab); // Create a storage manager for it. StManAipsIO sm1; StManAipsIO sm2; newtab.bindAll (sm1); newtab.bindColumn ("ab",sm2); if (doExcp) { try { newtab.setShapeColumn("arr2",IPosition(3,2,3,4)); } catch (std::exception& x) { // not FixedShape cout << "Expected exception: "<< x.what() << endl; } } newtab.setShapeColumn("arr3",IPosition(3,2,3,4)); Table tab(newtabcp, 10, False, Table::LocalEndian); AlwaysAssertExit (tab.endianFormat() == theEndianFormat); tab.tableInfo().setType ("testtype"); tab.tableInfo().setSubType ("testsubtype"); tab.tableInfo().readmeAddLine ("first readme line"); tab.tableInfo().readmeAddLine ("second test readme line"); // Determine if columns are stored. uInt i; cout << "stored columns: "; for (i=0; i ab1(tab,"ab"); ScalarColumn ab2(tab,"ab"); ScalarColumn ad(tab,"ad"); TableColumn ag1(tab,"ag"); ScalarColumn ag(tab,"ag"); ArrayColumn arr1(tab,"arr1"); ArrayColumn arr2(tab,"arr2"); ArrayColumn arr3(tab,"arr3"); Cube arrf(IPosition(3,2,3,4)); char str[8]; indgen (arrf); for (i=0; i<10; i++) { ab1.put (i, i); ad.put (i, i+2); arr1.put(i,arrf); arr2.put(i,arrf); arr3.put(i,arrf); arrf += (float)(arrf.nelements()); } ag1.putColumn (ad); Int abval; uInt adval; DComplex agval; Cube arrval(IPosition(3,2,3,4)); arrf -= (float)(arrf.nelements()*tab.nrow()); for (i=0; i<10; i++) { ab2.get (i, abval); ad.get (i, adval); ag.get (i, agval); if (abval != Int(i) || adval != i+2 || agval != DComplex(i+2)) { cout << "error in row " << i << ": " << abval << ", " << adval << ", " << agval << endl; } arr1.get (i, arrval); if (!allEQ (arrval, arrf)) { cout << "error in arr1 in row " << i << endl; } arr2.get (i, arrval); if (!allEQ (arrval, arrf)) { cout << "error in arr2 in row " << i << endl; } arr3.get (i, arrval); if (!allEQ (arrval, arrf)) { cout << "error in arr3 in row " << i << endl; } arrf += (float)(arrf.nelements()); } // Add a column. tab.addColumn (ScalarColumnDesc ("ac")); // Add a few columns with a new storage manager. TableDesc tempTD ("", "", TableDesc::Scratch); tempTD.addColumn (ScalarColumnDesc ("ae")); ScalarColumnDesc afcoldesc("af"); afcoldesc.setMaxLength (10); tempTD.addColumn (afcoldesc); StManAipsIO stmanAdd; tab.addColumn (tempTD, stmanAdd); tab.tableDesc().show(); ScalarColumn ac (tab,"ac"); ScalarColumn ae(tab,"ae"); ScalarColumn af(tab,"af"); for (i=0; i vec2; if (doExcp) { try { af.put (0, "12345678901"); } catch (std::exception& x) { // value too long cout << "Expected exception: " << x.what() << endl; } try { arr1.put (0, vec2); } catch (std::exception& x) { // shape cannot change cout << "Expected exception: " << x.what() << endl; } } } void b (Bool doExcp) { // Get the description and #rows of the Table. cout << "get layout in static way" << endl; TableDesc layout; cout << "TableUtil::getlayout #rows = " << TableUtil::getLayout (layout, "tTable_tmp.data"); layout.show (cout); cout << endl; TableInfo info(TableUtil::tableInfo ("tTable_tmp.data")); cout << "type = " << info.type() << endl; cout << "subtype = " << info.subType() << endl; cout << info.readme() << endl; // Check if some table files can be accessed. cout << Table::isReadable("tTable_tmp.data"); cout << Table::isWritable("tTable_tmp.data"); cout << Table::isReadable("tTablex.data"); cout << Table::isWritable("tTablex.data"); if (Table::nonWritableFiles("tTable_tmp.data").nelements() > 0) { cout << "There should be no non-writable table files" << endl; } cout << endl; // Read back the table. cout << "start reading Table" << endl; Table tab("tTable_tmp.data", TableLock(TableLock::PermanentLockingWait)); AlwaysAssertExit (tab.endianFormat() == theEndianFormat); cout << "end reading Table" << endl; cout << "type = " << tab.tableInfo().type() << endl; cout << "subtype = " << tab.tableInfo().subType() << endl; cout << tab.tableInfo().readme() << endl; if (doExcp) { try { tab.addColumn (ScalarColumnDesc("ab")); } catch (std::exception& x) { // table not writable cout << "Expected exception: " << removeDir(x.what()) << endl; } } ScalarColumn ab2(tab,"ab"); ScalarColumn ac (tab,"ac"); ScalarColumn ad(tab,"ad"); ScalarColumn ae(tab,"ae"); ScalarColumn af(tab,"af"); ScalarColumn ag(tab,"ag"); ArrayColumn arr1(tab,"arr1"); ArrayColumn arr2(tab,"arr2"); ArrayColumn arr3(tab,"arr3"); cout << arr1.columnDesc().isFixedShape() << " arr1.shapeColumn() = " << arr1.shapeColumn() << endl; cout << arr2.columnDesc().isFixedShape() << " arr2.shapeColumn() = " << arr2.shapeColumn() << endl; cout << arr3.columnDesc().isFixedShape() << " arr3.shapeColumn() = " << arr3.shapeColumn() << endl; cout << "datatypes ab = " << ab2.columnDesc().dataType() << " " << ab2.columnDesc().trueDataType() << endl; cout << "datatypes ac = " << ac.columnDesc().dataType() << " " << ac.columnDesc().trueDataType() << endl; cout << "datatypes ad = " << ad.columnDesc().dataType() << " " << ad.columnDesc().trueDataType() << endl; cout << "datatypes ae = " << ae.columnDesc().dataType() << " " << ae.columnDesc().trueDataType() << endl; cout << "datatypes af = " << af.columnDesc().dataType() << " " << af.columnDesc().trueDataType() << endl; cout << "datatypes ag = " << ag.columnDesc().dataType() << " " << ag.columnDesc().trueDataType() << endl; cout << "datatypes ar1 = " << arr1.columnDesc().dataType() << " " << arr1.columnDesc().trueDataType() << endl; cout << "datatypes arr2 = " << arr2.columnDesc().dataType() << " " << arr2.columnDesc().trueDataType() << endl; cout << "datatypes arr3 = " << arr3.columnDesc().dataType() << " " << arr3.columnDesc().trueDataType() << endl; uInt i; Int abval, acval; uInt adval; float aeval; String afval; DComplex agval; char str[8]; Cube arrf(IPosition(3,2,3,4)); Cube arrval(IPosition(3,2,3,4)); Cube arrvalslice(arrval(Slice(0,1),Slice(0,1,2),Slice(0,2,2))); Slice tmp; Slicer nslice (tmp, tmp, tmp, Slicer::endIsLength); Slicer nslice2(Slice(0,1), Slice(0,1,2), Slice(0,2,2), Slicer::endIsLength); indgen (arrf); for (i=0; i<10; i++) { cout << "get scalar row " << i << endl; ab2.get (i, abval); ac.get (i, acval); ad.get (i, adval); ae.get (i, aeval); af.get (i, afval); ag.get (i, agval); snprintf (str, sizeof(str), "V%i", i); if (abval != Int(i) || acval != Int(i+1) || adval != i+2 || aeval != i+3 || afval != str || agval != DComplex(i+2)) { cout << "error in row " << i << ": " << abval << ", " << acval << ", " << adval << ", " << aeval << ", " << afval << ", " << agval << endl; } arr1.get (i, arrval); if (!allEQ (arrval, arrf)) { cout << "error in arr1 in row " << i << endl; } arr2.get (i, arrval); if (!allEQ (arrval, arrf)) { cout << "error in arr2 in row " << i << endl; } cout << "get array row " << i << endl; arr3.get (i, arrval); if (!allEQ (arrval, arrf)) { cout << "error in arr3 in row " << i << endl; } arr2.getSlice (i, nslice, arrval); if (!allEQ (arrval, arrf)) { cout << "error in arr2 (entire slice) in row " << i << endl; } arr2.getSlice (i, nslice2, arrvalslice); if (!allEQ (arrval, arrf)) { cout << "error in arr2 (partial slice) in row " << i << endl; } arrf += (float)(arrf.nelements()); } Vector abvec = ab2.getColumn(); cout << tab.nrow() << " " << abvec.nelements() << endl; for (i=0; i<10; i++) { if (abvec(i) != Int(i)) { cout << "error in getColumn " << i << ": " << abvec(i) << endl; } } Array arr1a = arr1.getColumn(); if (arr1a.ndim() != 4) { cout << "arr1a not 4-dim" << endl; } i=0; uInt j0,j1,j2,j3; for (j3=0; j3<10; j3++) for (j2=0; j2<4; j2++) for (j1=0; j1<3; j1++) for (j0=0; j0<2; j0++) { if (arr1a(IPosition(4,j0,j1,j2,j3)) != i++) { cout <<"arr1a error at " < arr1b = arr1.getColumn(nslice); if (arr1b.ndim() != 4) { cout << "arr1b not 4-dim" << endl; } i=0; for (j3=0; j3<10; j3++) for (j2=0; j2<4; j2++) for (j1=0; j1<3; j1++) for (j0=0; j0<2; j0++) { if (arr1b(IPosition(4,j0,j1,j2,j3)) != i++) { cout <<"arr1b error at " < sorae(sortab, "ae"); cout << sorae.getColumn() << endl; cout << "#columns in sortab: " << sortab.tableDesc().ncolumn() << endl; Table sortab2 = sortab.sort ("ad"); if (sortab2.nrow() != 10) { cout << "sortab2 does not contain 10 rows" << endl; } ScalarColumn sorad(sortab2, "ad"); cout << sorad.getColumn() << endl; cout << "#columns in sortab2: " << sortab2.tableDesc().ncolumn() << endl; cout << "sortab2 type = " << sortab2.tableInfo().type() << endl; cout << "sortab2 subtype = " << sortab2.tableInfo().subType() << endl; cout << sortab2.tableInfo().readme() << endl; // Test using an empty selection. sortab = sortab(TableExprNode()); AlwaysAssertExit (sortab.nrow() == sortab2.nrow()); sortab2 = sortab2(TableExprNode(), 5); AlwaysAssertExit (sortab2.nrow() == 5); // Test using a const Bool expression. Table csortab = sortab(TableExprNode(2) + 3 == 5); AlwaysAssertExit (csortab.nrow() == sortab.nrow()); csortab = sortab(TableExprNode(False)); AlwaysAssertExit (csortab.nrow() == 0); csortab = sortab(TableExprNode(True), 5); AlwaysAssertExit (csortab.nrow() == 5); // Select using an empty set. // Select using the IN function. TableExprNodeSet set; Table seltabset = sortab (sortab.col("af").in (set)); AlwaysAssertExit (seltabset.nrow() == 0); set.add (TableExprNodeSetElem ("V3")); set.add (TableExprNodeSetElem ("V1")); set.add (TableExprNodeSetElem ("V9")); set.add (TableExprNodeSetElem ("V6")); seltabset = sortab (sortab.col("af").in (set)); if (seltabset.nrow() != 4) { cout << "seltabset does not contain 4 rows" << endl; } cout << seltabset.rowNumbers() << endl;; cout << seltabset.rowNumbers(tab) << endl;; cout << seltabset.rowNumbers(sortab) << endl; seltabset = sortab (sortab.col("arr1")(IPosition(3,0,0,0)) < 100); if (seltabset.nrow() != 5) { cout << "seltabset does not contain 5 rows" << endl; } seltabset = sortab (sortab.col("arr1")(IPosition(3,1,0,0)) == 97); if (seltabset.nrow() != 1) { cout << "seltabset does not contain 1 row" << endl; } // Select an empty table and use that as input for a select and sort. { Table selempty1 = sortab (sortab.col("ab") < -10); if (selempty1.nrow() != 0) { cout << "selempty1 is not empty" << endl; } Table selempty2 = selempty1 (selempty1.col("ab") < -10 || TableExprNode()); if (selempty2.nrow() != 0) { cout << "selempty2 is not empty" << endl; } Table sorempty1 = selempty1.sort ("ab"); if (sorempty1.nrow() != 0) { cout << "sorempty1 is not empty" << endl; } } // Get a subset of the table via row numbers. Vector rownrs(4); rownrs(0)=3; rownrs(1)=1; rownrs(2)=9; rownrs(3)=6; Table seltab1 = sortab(rownrs); if (seltab1.nrow() != 4) { cout << "seltab1 does not contain 4 rows" << endl; } ScalarColumn sel1ab(seltab1, "ab"); cout << sel1ab.getColumn() << endl; cout << "#columns in seltab1: " << seltab1.tableDesc().ncolumn() << endl; // Project the table. Block projname(3); projname[0] = "ae"; projname[1] = "ab"; projname[2] = "arr1"; Table seltab2 = seltab1.project (projname); if (seltab2.nrow() != 4) { cout << "seltab2 does not contain 4 rows" << endl; } ScalarColumn sel2ab(seltab2, "ab"); cout << sel2ab.getColumn() << endl; cout << "#columns in seltab2: " << seltab2.tableDesc().ncolumn() << endl; // Get a subset via a mask. Block mask(4,True); mask[0] = False; mask[3] = False; Table seltab3 = seltab2(mask); if (seltab3.nrow() != 2) { cout << "seltab3 does not contain 2 rows" << endl; } ScalarColumn sel3ab(seltab3, "ab"); cout << sel3ab.getColumn() << endl; cout << "#columns in seltab3: " << seltab3.tableDesc().ncolumn() << endl; seltab3.tableDesc().show(); Table xortab = sortab ^ seltab1; if (xortab.nrow() != 6) { cout << "xortab does not contain 6 rows" << endl; } ScalarColumn xorab(xortab, "ab"); cout << xorab.getColumn() << endl; cout << "#columns in xortab: " << xortab.tableDesc().ncolumn() << endl; Table or1tab = xortab | seltab3; if (or1tab.nrow() != 8) { cout << "or1tab does not contain 8 rows" << endl; } ScalarColumn or1ab(or1tab, "ab"); cout << or1ab.getColumn() << endl; cout << "#columns in or1tab: " << or1tab.tableDesc().ncolumn() << endl; Table or2tab = seltab3 | xortab; if (or2tab.nrow() != 8) { cout << "or2tab does not contain 8 rows" << endl; } ScalarColumn or2ab(or2tab, "ab"); cout << or2ab.getColumn() << endl; cout << "#columns in or2tab: " << or2tab.tableDesc().ncolumn() << endl; Table exprtab = sortab(TableExprNode() && sortab.col("ab") >= 5); if (exprtab.nrow() != 5) { cout << "exprtab does not contain 5 rows" << endl; } ScalarColumn exprab(exprtab, "ab"); cout << exprab.getColumn() << endl; Table expr2tab = tab(tab.col("af") == "V3" || (tab.col("ab") >= 5 && tab.col("ab") < 8)); if (expr2tab.nrow() != 4) { cout << "expr2tab does not contain 4 rows" << endl; } ScalarColumn expr2ab(expr2tab, "ab"); cout << expr2ab.getColumn() << endl; // Test persistency of reference tables. { Table ex1tab = tab(tab.col("ab") > 5); ex1tab.renameColumn ("abnew", "ab"); AlwaysAssertExit (ex1tab.tableDesc().isColumn ("abnew")); AlwaysAssertExit (! ex1tab.tableDesc().isColumn ("ab")); AlwaysAssertExit (tab.tableDesc().isColumn ("ab")); AlwaysAssertExit (! tab.tableDesc().isColumn ("abnew")); ScalarColumn abcol(ex1tab, "abnew"); cout << abcol.getColumn() << endl; cout << ">>>" << endl; ex1tab.rename ("tTable_tmp.ex1", Table::New); cout << "<<<" << endl; } { Table ex1tab ("tTable_tmp.ex1"); AlwaysAssertExit (! ex1tab.tableDesc().isColumn ("ab")); ScalarColumn abcol(ex1tab, "abnew"); Table ex2tab = ex1tab (ex1tab.col("abnew") > 6); AlwaysAssertExit (! ex2tab.tableDesc().isColumn ("ab")); ScalarColumn abcol2(ex2tab, "abnew"); ex1tab.renameColumn ("abnew1", "abnew"); ex2tab.renameColumn ("abnew2", "abnew"); AlwaysAssertExit (ex1tab.tableDesc().isColumn ("abnew1")); AlwaysAssertExit (! ex1tab.tableDesc().isColumn ("abnew")); AlwaysAssertExit (! ex1tab.tableDesc().isColumn ("abnew2")); AlwaysAssertExit (ex2tab.tableDesc().isColumn ("abnew2")); AlwaysAssertExit (! ex2tab.tableDesc().isColumn ("abnew")); AlwaysAssertExit (! ex2tab.tableDesc().isColumn ("abnew1")); ScalarColumn abcola(ex1tab, "abnew1"); ScalarColumn abcol2a(ex2tab, "abnew2"); } } //# Test deletion of rows, array of Strings, and some more. void c (const StorageOption& stopt, Bool doExcp) { TableDesc td("", "1", TableDesc::Scratch); td.addColumn (ScalarColumnDesc("ab","Comment for column ab")); td.addColumn (ScalarColumnDesc("ac")); td.addColumn (ScalarColumnDesc("ad","comment for ad")); td.addColumn (ScalarColumnDesc("ae")); td.addColumn (ScalarColumnDesc("af")); td.addColumn (ScalarColumnDesc("ag")); td.addColumn (ArrayColumnDesc("arr1",3,ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc("arr2",0)); td.addColumn (ArrayColumnDesc("arr3",0,ColumnDesc::Direct)); // Now create a new table from the description. SetupNewTable newtab("tTable_tmp.data1", td, Table::New, stopt); // Create a storage manager for it. StManAipsIO sm1; StManAipsIO sm2; newtab.bindAll (sm1); newtab.bindColumn ("ab",sm2); newtab.bindColumn ("ac",sm2); newtab.setShapeColumn("arr1",IPosition(3,2,3,4)); newtab.setShapeColumn("arr3",IPosition(1,2)); Table tab(newtab); tab.rename ("tTable_tmp.data2", Table::New); tab.rename ("tTable_tmp.data2", Table::New); tab.rename ("tTable_tmp.data2", Table::Scratch); tab.rename ("tTable_tmp.data2a", Table::Scratch); tab.rename ("tTable_tmp.data2a", Table::Scratch); tab.rename ("tTable_tmp.data2a", Table::New); tab.rename ("tTable_tmp.data2", Table::Scratch); tab.rename ("tTable_tmp.data2a", Table::New); if (doExcp) { try { // Create a normal file, so rename will fail. RegularFile file("tTable_tmp.file"); file.create(); tab.rename ("tTable_tmp.file", Table::NewNoReplace); } catch (std::exception& x) { // exists as file cout << "Expected exception: " << removeDir(x.what()) << endl; } try { tab.rename ("tTable_tmp.data", Table::NewNoReplace); } catch (std::exception& x) { // already exists cout << "Expected exception: " << removeDir(x.what()) << endl; } try { tab.rename ("tTable.datx", Table::Update); } catch (std::exception& x) { // does not exist cout << "Expected exception: " << removeDir(x.what()) << endl; } try { tab.addColumn (ScalarColumnDesc("ab")); } catch (std::exception& x) { // column already exists cout << "Expected exception: " << x.what() << endl; } } // Rename a column. It'll be renamed back later. tab.renameColumn ("acnew", "ac"); ScalarColumn ab1(tab,"ab"); ScalarColumn ab2(tab,"ab"); ScalarColumn ac (tab,"acnew"); ScalarColumn ad(tab,"ad"); ScalarColumn ae(tab,"ae"); ScalarColumn af(tab,"af"); TableColumn ag1(tab,"ag"); ScalarColumn ag(tab,"ag"); ArrayColumn arr1(tab,"arr1"); ArrayColumn arr2(tab,"arr2"); ArrayColumn arr3(tab,"arr3"); Cube arrf(IPosition(3,2,3,4)); Vector vecstr (stringToVector ("0,1,23,4,5,6,7,8,9,100," "1,2,34,5,6,7,8,9,0,101")); uInt i; char str[8]; indgen (arrf); for (i=0; i<10; i++) { tab.addRow(); ab1.put (i, i); ac.put (i, i+1); ad.put (i, i+2); ae.put (i, i+3); snprintf (str, sizeof(str), "V%i", i); af.put (i, str); arr1.put(i,arrf); arr2.put(i,arrf); arr3.put(i,vecstr(Slice(i*2,2))); arrf += (float)(arrf.nelements()); } ag1.putColumn (ad); arrf -= (float)(arrf.nelements()*tab.nrow()); // Rename the column back. tab.renameColumn ("ac", "acnew"); //# Select some rows from the table. Table expr2tab = tab(tab.col("af") == "V3" || (tab.col("ab") >= 5 && tab.col("ab") < 8)); if (expr2tab.nrow() != 4) { cout << "expr2tab does not contain 4 rows" << endl; } ScalarColumn expr2ab(expr2tab, "ab"); cout << expr2ab.getColumn() << endl; if (!allEQ (expr2tab.rowNumbers(), expr2tab.rowNumbers(tab))) { cout << "error in expr2tab.rowNumbers()" << endl; } //# Remove 2 rows from the selected rows. expr2tab.removeRow (1); expr2tab.removeRow (2); if (expr2tab.nrow() != 2) { cout << "expr2tab does not contain 2 rows" << endl; } cout << expr2ab.getColumn() << endl; //# Remove those rows. tab.removeRow (expr2tab.rowNumbers()); if (tab.nrow() != 8) { cout << "tab does not contain 8 rows" << endl; } cout << ab2.getColumn() << endl; tab.removeRow (7); if (tab.nrow() != 7) { cout << "tab does not contain 7 rows" << endl; } if (doExcp) { try { tab.removeRow (7); } catch (std::exception& x) { // row does not exist cout << "Expected exception: " << removeDir(x.what()) << endl; } } cout << ab2.getColumn() << endl; //# Check if the values are still okay. Int abval; uInt adval; DComplex agval; Cube arrval(IPosition(3,2,3,4)); for (i=0; i matstr; arr3.getColumn (matstr); cout << matstr << endl; } void d (const StorageOption& stopt) { Vector arrf2(100); indgen (arrf2); { // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.addColumn (ScalarColumnDesc("ab","Comment for column ab")); td.addColumn (ScalarColumnDesc("ad","comment for ad")); td.addColumn (ScalarColumnDesc("ag")); td.addColumn (ArrayColumnDesc ("arr1",1,ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc("arr2",0)); td.addColumn (ArrayColumnDesc ("arr3",0,ColumnDesc::Direct)); // Now create a new table from the description. SetupNewTable newtab("tTable_tmp.data3", td, Table::New, stopt); // Create a storage manager for it. StManAipsIO sm1; newtab.bindAll (sm1); newtab.setShapeColumn ("arr1",IPosition(1,2)); newtab.setShapeColumn ("arr3",IPosition(2,2,2)); Table tab(newtab); uInt i; ScalarColumn ab(tab,"ab"); ScalarColumn ad(tab,"ad"); TableColumn ag1(tab,"ag"); ArrayColumn arr1(tab,"arr1"); ArrayColumn arr2(tab,"arr2"); ArrayColumn arr3(tab,"arr3"); Vector arrf(IPosition(1,3)); Matrix arri(IPosition(2,2,2)); Vector arrs (stringToVector ("aa,bbb")); indgen (arrf); indgen (arri); for (i=0; i<10000; i++) { tab.addRow(); ab.put (i, i); ad.put (i, i+2); arr1.put(i,arrs); arr2.put(i,arrf); arr3.put(i,arri); arrf += (Complex)(arrf.nelements()); arri += (Int)(arri.nelements()); } ag1.putColumn (ad); } { Table tab ("tTable_tmp.data3"); ScalarColumn ab(tab,"ab"); ScalarColumn ad(tab,"ad"); ScalarColumn ag(tab,"ag"); ArrayColumn arr1(tab,"arr1"); ArrayColumn arr2(tab,"arr2"); ArrayColumn arr3(tab,"arr3"); Int abval; uInt adval; Complex agval; Vector arrf(IPosition(1,3)); Matrix arri(IPosition(2,2,2)); Vector arrs (stringToVector ("aa,bbb")); indgen (arrf); indgen (arri); uInt i; for (i=0; i<10; i++) { ab.get (i, abval); ad.get (i, adval); ag.get (i, agval); if (abval != Int(i) || adval != i+2 || agval != Complex(i+2)) { cout << "error in row " << i << ": " << abval << ", " << adval << ", " << agval << endl; } if (!allEQ (arr1(i), arrs)) { cout << "error in arr1 in row " << i << endl; } if (!allEQ (arr2(i), arrf)) { cout << "error in arr2 in row " << i << endl; } if (!allEQ (arr3(i), arri)) { cout << "error in arr3 in row " << i << endl; } arrf += (Complex)(arrf.nelements()); arri += (Int)(arri.nelements()); } // Open the same table read/write. Table rwtab ("tTable_tmp.data3", Table::Update); AlwaysAssertExit (rwtab.isWritable()); for (i=0; i rwab(rwtab,"ab"); ArrayColumn rwarr2(rwtab,"arr2"); rwab.put (0,1); rwarr2.put (0,arrf); if (!allEQ( arr2(0), arrf)) { cout << "first error in arr2" << endl; } rwarr2.put (0,arrf2); if (!allEQ( arr2(0), arrf2)) { cout << "second error in arr2" << endl; } } { Table tab ("tTable_tmp.data3"); ScalarColumn ab(tab,"ab"); ScalarColumn ad(tab,"ad"); ScalarColumn ag(tab,"ag"); ArrayColumn arr1(tab,"arr1"); ArrayColumn arr2(tab,"arr2"); ArrayColumn arr3(tab,"arr3"); Int abval; uInt adval; Complex agval; Vector arrf(IPosition(1,3)); Matrix arri(IPosition(2,2,2)); Vector arrs (stringToVector ("aa,bbb")); indgen (arrf); indgen (arri); rownr_t nrow = tab.nrow(); for (rownr_t i=0; i 0) { if (!allEQ (arr2(i), arrf)) { cout << "error in arr2 in row " << i << endl; } } if (!allEQ (arr3(i), arri)) { cout << "error in arr3 in row " << i << endl; } arrf += (Complex)(arrf.nelements()); arri += (Int)(arri.nelements()); } if (!allEQ( arr2(0), arrf2)) { cout << "error in rereading arr2" << endl; } // Check the locked tables. Vector vec (Table::getLockedTables()); AlwaysAssertExit (vec.size() == 1); AlwaysAssertExit (removeDir(vec[0]) == "tTable_tmp.data3"); } // No locked tables should be left. AlwaysAssertExit (Table::getLockedTables().size() == 0); } int main (int argc,const char*[]) { try { Table::setScratchCallback (cbFunc); StorageOption stopt; a ( stopt, (argc<2)); b ( (argc<2)); c ( stopt, (argc<2)); d ( stopt); // Also test with MultiFile (with O_DIRECT if supported). cout< datatypes arr2 = float Array datatypes arr3 = float Array get scalar row 0 get array row 0 get scalar row 1 get array row 1 get scalar row 2 get array row 2 get scalar row 3 get array row 3 get scalar row 4 get array row 4 get scalar row 5 get array row 5 get scalar row 6 get array row 6 get scalar row 7 get array row 7 get scalar row 8 get array row 8 get scalar row 9 get array row 9 10 10 [12, 11, 10, 9, 8, 7, 6, 5, 4, 3] #columns in sortab: 9 [2, 3, 4, 5, 6, 7, 8, 9, 10, 11] #columns in sortab2: 9 sortab2 type = testtype sortab2 subtype = testsubtype first readme line second test readme line a sortab line [9, 6, 3, 1] [9, 6, 3, 1] [0, 3, 6, 8] [6, 8, 0, 3] #columns in seltab1: 9 [6, 8, 0, 3] #columns in seltab2: 3 [8, 0] #columns in seltab3: 3 TableDesc version 1 (Directory **SCRATCH**) --------- Comment: A test of class Table #Keywords = 0 #Columns = 3 Name=ae DataType=float DataManager=StandardStMan/StandardStMan Default=0 Comment = #keywords=0 Name=ab DataType=Int DataManager=StandardStMan/StandardStMan Default=0 Comment = Comment for column ab #keywords=0 Name=arr1 DataType=float Nrdim=3 Shape=[2, 3, 4] DataManager=StandardStMan/StandardStMan Comment = #keywords=0 [1, 2, 4, 5, 7, 9] #columns in xortab: 9 [0, 1, 2, 4, 5, 7, 8, 9] #columns in or1tab: 9 [0, 1, 2, 4, 5, 7, 8, 9] #columns in or2tab: 3 [9, 8, 7, 6, 5] [3, 5, 6, 7] [6, 7, 8, 9] >>> ScratchCallBack: name=tab17115_20 isScratch=0 oldName= <<< ScratchCallBack: name=tTable_tmp.ex1 isScratch=1 oldName= ScratchCallBack: name=tTable_tmp.ex1 isScratch=0 oldName= ScratchCallBack: name=tTable_tmp.data1 isScratch=1 oldName= ScratchCallBack: name=tTable_tmp.data1 isScratch=0 oldName= ScratchCallBack: name=tTable_tmp.data2 isScratch=1 oldName= ScratchCallBack: name=tTable_tmp.data2a isScratch=1 oldName=tTable_tmp.data2 ScratchCallBack: name=tTable_tmp.data2a isScratch=0 oldName= ScratchCallBack: name=tTable_tmp.data2 isScratch=1 oldName= ScratchCallBack: name=tTable_tmp.data2 isScratch=0 oldName= Expected exception: Table tTable_tmp.file already exists (and is not a true table directory) Expected exception: Table tTable_tmp.data already exists Expected exception: Table tTable.datx does not exist Expected exception: Invalid description of column ab: column already exists [3, 5, 6, 7] [3, 6] [0, 1, 2, 4, 5, 7, 8, 9] Expected exception: Invalid Table operation: removeRow: rownr 7 too high in table tTable_tmp.data2a (#rows=7) [0, 1, 2, 4, 5, 7, 8] [0, 1] [23, 4] [5, 6] [9, 100] [1, 2] [6, 7] [8, 9] Axis Lengths: [2, 7] (NB: Matrix in Row/Column order) [0, 23, 5, 9, 1, 6, 8 1, 4, 6, 100, 2, 7, 9] ScratchCallBack: name=tTable_tmp.data3 isScratch=1 oldName= ScratchCallBack: name=tTable_tmp.data3 isScratch=0 oldName= Test with MultiFile: Expected exception: Invalid Table operation: SetupNewTable::setShapeColumn, column arr2 is not fixed shape ScratchCallBack: name=tTable_tmp.data isScratch=1 oldName= ScratchCallBack: name=tTable_tmp.data isScratch=0 oldName= stored columns: 111111 TableDesc version 1 (Directory **SCRATCH**) --------- Comment: A test of class Table #Keywords = 0 #Columns = 9 Name=ab DataType=Int DataManager=StandardStMan/StandardStMan Default=0 Comment = Comment for column ab #keywords=0 Name=ad DataType=uInt DataManager=StandardStMan/StandardStMan Default=0 Comment = comment for ad #keywords=0 Name=ag DataType=DComplex DataManager=StandardStMan/StandardStMan Default=(0,0) Comment = #keywords=0 Name=arr1 DataType=float Nrdim=3 Shape=[2, 3, 4] DataManager=StandardStMan/StandardStMan Comment = #keywords=0 Name=arr2 DataType=float Nrdim=-1 Shape=[] DataManager=StandardStMan/StandardStMan Comment = #keywords=0 Name=arr3 DataType=float Nrdim=-1 Shape=[] DataManager=StandardStMan/StandardStMan Comment = #keywords=0 Name=ac DataType=Int DataManager=StandardStMan/StandardStMan Default=0 Comment = #keywords=0 Name=ae DataType=float DataManager=StandardStMan/StandardStMan Default=0 Comment = #keywords=0 Name=af DataType=String MaxLength=10 DataManager=StandardStMan/StandardStMan Default= Comment = #keywords=0 Expected exception: ScalarColumn::put: string value '12345678901' exceeds maximum length Expected exception: ArrayColumn::put for row 0 in column arr1: Table array conformance error (shape=[0], expected [2, 3, 4]) get layout in static way TableUtil::getlayout #rows = 10 TableDesc version 1 (Directory **SCRATCH**) --------- Comment: A test of class Table #Keywords = 0 #Columns = 9 Name=ab DataType=Int DataManager=StandardStMan/StandardStMan Default=0 Comment = Comment for column ab #keywords=0 Name=ad DataType=uInt DataManager=StandardStMan/StandardStMan Default=0 Comment = comment for ad #keywords=0 Name=ag DataType=DComplex DataManager=StandardStMan/StandardStMan Default=(0,0) Comment = #keywords=0 Name=arr1 DataType=float Nrdim=3 Shape=[2, 3, 4] DataManager=StandardStMan/StandardStMan Comment = #keywords=0 Name=arr2 DataType=float Nrdim=-1 Shape=[] DataManager=StandardStMan/StandardStMan Comment = #keywords=0 Name=arr3 DataType=float Nrdim=-1 Shape=[] DataManager=StandardStMan/StandardStMan Comment = #keywords=0 Name=ac DataType=Int DataManager=StandardStMan/StandardStMan Default=0 Comment = #keywords=0 Name=ae DataType=float DataManager=StandardStMan/StandardStMan Default=0 Comment = #keywords=0 Name=af DataType=String MaxLength=10 DataManager=StandardStMan/StandardStMan Default= Comment = #keywords=0 type = testtype subtype = testsubtype first readme line second test readme line 1100 start reading Table end reading Table type = testtype subtype = testsubtype first readme line second test readme line Expected exception: Invalid Table operation: Table::addColumn; table tTable_tmp.data is not writable 1 arr1.shapeColumn() = [2, 3, 4] 0 arr2.shapeColumn() = [] 1 arr3.shapeColumn() = [2, 3, 4] datatypes ab = Int Int datatypes ac = Int Int datatypes ad = uInt uInt datatypes ae = float float datatypes af = String String datatypes ag = DComplex DComplex datatypes ar1 = float Array datatypes arr2 = float Array datatypes arr3 = float Array get scalar row 0 get array row 0 get scalar row 1 get array row 1 get scalar row 2 get array row 2 get scalar row 3 get array row 3 get scalar row 4 get array row 4 get scalar row 5 get array row 5 get scalar row 6 get array row 6 get scalar row 7 get array row 7 get scalar row 8 get array row 8 get scalar row 9 get array row 9 10 10 [12, 11, 10, 9, 8, 7, 6, 5, 4, 3] #columns in sortab: 9 [2, 3, 4, 5, 6, 7, 8, 9, 10, 11] #columns in sortab2: 9 sortab2 type = testtype sortab2 subtype = testsubtype first readme line second test readme line a sortab line [9, 6, 3, 1] [9, 6, 3, 1] [0, 3, 6, 8] [6, 8, 0, 3] #columns in seltab1: 9 [6, 8, 0, 3] #columns in seltab2: 3 [8, 0] #columns in seltab3: 3 TableDesc version 1 (Directory **SCRATCH**) --------- Comment: A test of class Table #Keywords = 0 #Columns = 3 Name=ae DataType=float DataManager=StandardStMan/StandardStMan Default=0 Comment = #keywords=0 Name=ab DataType=Int DataManager=StandardStMan/StandardStMan Default=0 Comment = Comment for column ab #keywords=0 Name=arr1 DataType=float Nrdim=3 Shape=[2, 3, 4] DataManager=StandardStMan/StandardStMan Comment = #keywords=0 [1, 2, 4, 5, 7, 9] #columns in xortab: 9 [0, 1, 2, 4, 5, 7, 8, 9] #columns in or1tab: 9 [0, 1, 2, 4, 5, 7, 8, 9] #columns in or2tab: 3 [9, 8, 7, 6, 5] [3, 5, 6, 7] [6, 7, 8, 9] >>> ScratchCallBack: name=tab17115_20 isScratch=0 oldName= <<< ScratchCallBack: name=tTable_tmp.ex1 isScratch=1 oldName= ScratchCallBack: name=tTable_tmp.ex1 isScratch=0 oldName= ScratchCallBack: name=tTable_tmp.data1 isScratch=1 oldName= ScratchCallBack: name=tTable_tmp.data1 isScratch=0 oldName= ScratchCallBack: name=tTable_tmp.data2 isScratch=1 oldName= ScratchCallBack: name=tTable_tmp.data2a isScratch=1 oldName=tTable_tmp.data2 ScratchCallBack: name=tTable_tmp.data2a isScratch=0 oldName= ScratchCallBack: name=tTable_tmp.data2 isScratch=1 oldName= ScratchCallBack: name=tTable_tmp.data2 isScratch=0 oldName= Expected exception: Table tTable_tmp.file already exists (and is not a true table directory) Expected exception: Table tTable_tmp.data already exists Expected exception: Table tTable.datx does not exist Expected exception: Invalid description of column ab: column already exists [3, 5, 6, 7] [3, 6] [0, 1, 2, 4, 5, 7, 8, 9] Expected exception: Invalid Table operation: removeRow: rownr 7 too high in table tTable_tmp.data2a (#rows=7) [0, 1, 2, 4, 5, 7, 8] [0, 1] [23, 4] [5, 6] [9, 100] [1, 2] [6, 7] [8, 9] Axis Lengths: [2, 7] (NB: Matrix in Row/Column order) [0, 23, 5, 9, 1, 6, 8 1, 4, 6, 100, 2, 7, 9] ScratchCallBack: name=tTable_tmp.data3 isScratch=1 oldName= ScratchCallBack: name=tTable_tmp.data3 isScratch=0 oldName= casacore-3.7.1/tables/Tables/test/tTableAccess.cc000066400000000000000000000122621476623553700216360ustar00rootroot00000000000000//# tTable_4.cc: Interactive test program for adding/removing columns //# Copyright (C) 2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Interactive test program for adding/removing columns. // // Create a table with the given storage manager and data type. // Write 10 rows with the given value. template Table createSSM (T value, T step) { TableDesc desc; desc.addColumn (ScalarColumnDesc("scacol")); desc.addColumn (ArrayColumnDesc("arrcol")); SetupNewTable newtab ("tTableAccess_tmp.tab", desc, Table::New); int nrow = 2; Table tab(newtab, nrow); ScalarColumn scacol(tab, "scacol"); ArrayColumn arrcol(tab, "arrcol"); Array arr(IPosition(1,3)); for (int i=0; i Table createISM (T value, T step) { TableDesc desc; desc.addColumn (ScalarColumnDesc("scacol")); desc.addColumn (ArrayColumnDesc("arrcol")); SetupNewTable newtab ("tTableAccess_tmp.tab", desc, Table::New); IncrementalStMan ism; newtab.bindAll (ism); int nrow = 2; Table tab(newtab, nrow); ScalarColumn scacol(tab, "scacol"); ArrayColumn arrcol(tab, "arrcol"); Array arr(IPosition(1,3)); for (int i=0; i Table createForward (const Table& table) { TableDesc desc; desc.addColumn (ScalarColumnDesc("scacol")); desc.addColumn (ArrayColumnDesc("arrcol")); SetupNewTable newtab ("tTableAccess_tmp.tab1", desc, Table::New); ForwardColumnEngine fcsm(table); newtab.bindAll (fcsm); int nrow = 2; Table tab(newtab, nrow); return tab; } // Remove the dirname from the table name in an error message. String removeDir (const String& msg) { String s = msg; s.gsub (Regex("/.*/t"), "t"); return s; } template void readSca (const Table& tab) { ScalarColumn col(tab, "scacol"); uInt nrow = tab.nrow(); for (uInt i=0; i void readArr (const Table& tab) { ArrayColumn col(tab, "arrcol"); uInt nrow = tab.nrow(); for (uInt i=0; i (tab); readArr (tab); Vector rownrs(1,1); Table tab2 = tab(rownrs); readSca (tab2); readArr (tab2); Block
        tables(1); tables[0] = tab; Table tabc1(tables); readSca (tabc1); readArr (tabc1); tables[0] = tab2; Table tabc2(tables); readSca (tabc2); readArr (tabc2); } void doIt() { readTable (createSSM (1,1)); readTable (createISM (1,2)); readTable (createForward (Table("tTableAccess_tmp.tab"))); } int main() { try { doIt(); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } casacore-3.7.1/tables/Tables/test/tTableCopy.cc000066400000000000000000000140001476623553700213370ustar00rootroot00000000000000//# tTableCopy.cc: Test program for copying tables //# Copyright (C) 2006 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include using namespace casacore; using namespace std; // Remove the dirname from the table name in an error message. String removeDir (const String& msg) { String s = msg; s.gsub (Regex("/.*/t"), "t"); return s; } void testCloneColumn (const DataManager& tsm, Bool fixed) { cout << "testCloneColumn ..." << endl; // First create a table. TableDesc td; if (fixed) { td.addColumn (ArrayColumnDesc("DATA", IPosition(1,10))); } else { td.addColumn (ArrayColumnDesc("DATA", 1)); } td.addColumn (ScalarColumnDesc("SCALAR", 1)); td.addColumn (ArrayColumnDesc("ARRAY", 0)); SetupNewTable newtab("tTableCopy_tmp.data", td, Table::New); StandardStMan ssm; newtab.bindAll (ssm); newtab.bindColumn ("DATA", tsm); Table tab(newtab, 4); ArrayColumn col(tab, "DATA"); for (uInt row=0; row vec(10*row+1); if (fixed) vec.resize(10); indgen (vec); if (fixed || row != 2) { // Keep a row cell without an array. col.setShape (row, vec.shape(), IPosition(2, 4, 4)); col.put (row, vec); } } // Now clone the column and copy the data. TableCopy::cloneColumn (tab, "DATA", tab, "DATA1"); TableCopy::cloneColumn (tab, "DATA", tab, "DATA2", "Data2StMan"); TableCopy::cloneColumnTyped (tab, "DATA", tab, "DATA3"); TableCopy::cloneColumnTyped (tab, "SCALAR", tab, "SCALAR3"); TableCopy::copyColumnData (tab, "DATA", tab, "DATA1", False); TableCopy::copyColumnData (tab, "DATA", tab, "DATA3"); cout << tab.dataManagerInfo() << endl; // Check if the data are the same. ArrayColumn col1(tab, "DATA1"); ArrayColumn col3(tab, "DATA3"); for (uInt row=0; row vec(col(row)); Vector vecd(vec.shape()); convertArray (vecd, vec); AlwaysAssertExit (allEQ (col1(row), vec)); AlwaysAssertExit (allEQ (col3(row), vecd)); } else { AlwaysAssertExit (! col1.isDefined(row)); } } // Initialize the array. TableCopy::fillColumnData (tab, "DATA2", Complex(-1,-2), tab, "DATA"); // Initialize the scalar and other array. TableCopy::fillColumnData (tab, "SCALAR", "str"); TableCopy::fillColumnData (tab, "SCALAR3", 2); TableCopy::fillArrayColumn (tab, "ARRAY", Vector(3,2)); // Check if the data are correct. ArrayColumn col2(tab, "DATA2"); ScalarColumn cols(tab, "SCALAR"); ScalarColumn cols3(tab, "SCALAR3"); ArrayColumn cola(tab, "ARRAY"); for (uInt row=0; row(3,2))); } } void testCloneColumns() { TiledShapeStMan tsm1("DATA_stm", IPosition(2,8,2)); testCloneColumn (tsm1, False); TiledCellStMan tsm2("DATA_stm", IPosition(2,8,2)); testCloneColumn (tsm2, False); TiledColumnStMan tsm3("DATA_stm", IPosition(2,8,2)); testCloneColumn (tsm3, True); } int main (int argc, const char* argv[]) { Table::TableType ttyp = Table::Plain; if (argc > 1) { if (String(argv[1]) == String("m")) { ttyp = Table::Memory; } } Bool noRows = False; if (argc > 2 && String(argv[2]) == String("n")) { noRows = True; } try { TableDesc td("", "1", TableDesc::Scratch); td.comment() = "A SDMemTable"; td.addColumn(ScalarColumnDesc("Test")); // Now create a new table from the description. SetupNewTable aNewTab("tTableCopy_tmp.tbl", td, Table::New); Table tabl(aNewTab, ttyp, 0); tabl.addRow(); TableDesc std("", "1", TableDesc::Scratch); std.comment() = "A SubTable"; SetupNewTable newTab(tabl.tableName()+"/SUBTABLE", std, Table::New); Table stabl(newTab, ttyp, 0); stabl.addRow(); tabl.rwKeywordSet().defineTable("SUBTABLE", stabl); tabl.copy("tTableCopy_tmp.newtbl", Table::New, noRows); Table t("tTableCopy_tmp.newtbl"); cout << removeDir(tabl.tableName()) << endl; cout << removeDir(tabl.keywordSet().asTable("SUBTABLE").tableName()) << endl; cout << removeDir(t.tableName()) << endl; cout << removeDir(t.keywordSet().asTable("SUBTABLE").tableName()) << endl; cout << tabl.nrow() << ' ' << stabl.nrow() << ' ' << t.nrow() << ' ' << t.keywordSet().asTable("SUBTABLE").nrow() << ' ' << tabl.tableType() << ' ' << t.tableType() << endl; if (argc <= 1) { testCloneColumns(); } } catch (const exception& x) { cout << x.what() << endl; return 1; } return 0; } casacore-3.7.1/tables/Tables/test/tTableCopy.out000066400000000000000000000277141476623553700216010ustar00rootroot00000000000000tTableCopy_tmp.tbl tTableCopy_tmp.tbl/SUBTABLE tTableCopy_tmp.newtbl tTableCopy_tmp.newtbl/SUBTABLE 1 1 1 1 0 0 testCloneColumn ... *1: { TYPE: String "StandardStMan" NAME: String "SSM" SEQNR: uInt 0 SPEC: { MaxCacheSize: Int 2 BUCKETSIZE: Int 640 PERSCACHESIZE: Int 2 IndexLength: Int 0 } COLUMNS: String array with shape [2] [ARRAY, SCALAR] } *2: { TYPE: String "TiledShapeStMan" NAME: String "DATA_stm" SEQNR: uInt 1 SPEC: { MaxCacheSize: Int 0 DEFAULTTILESHAPE: Int array with shape [2] [8, 2] MAXIMUMCACHESIZE: Int64 0 HYPERCUBES: { *1: { CubeShape: Int array with shape [2] [1, 1] TileShape: Int array with shape [2] [1, 4] CellShape: Int array with shape [1] [1] BucketSize: Int 32 ID: { } } *2: { CubeShape: Int array with shape [2] [11, 1] TileShape: Int array with shape [2] [4, 4] CellShape: Int array with shape [1] [11] BucketSize: Int 128 ID: { } } *3: { CubeShape: Int array with shape [2] [31, 1] TileShape: Int array with shape [2] [4, 4] CellShape: Int array with shape [1] [31] BucketSize: Int 128 ID: { } } } SEQNR: uInt 1 IndexSize: uInt 4 } COLUMNS: String array with shape [1] [DATA] } *3: { TYPE: String "TiledShapeStMan" NAME: String "DATA1" SEQNR: uInt 2 SPEC: { MaxCacheSize: Int 0 DEFAULTTILESHAPE: Int array with shape [2] [8, 2] MAXIMUMCACHESIZE: Int64 0 HYPERCUBES: { *1: { CubeShape: Int array with shape [2] [1, 1] TileShape: Int array with shape [2] [1, 2] CellShape: Int array with shape [1] [1] BucketSize: Int 16 ID: { } } *2: { CubeShape: Int array with shape [2] [11, 1] TileShape: Int array with shape [2] [8, 2] CellShape: Int array with shape [1] [11] BucketSize: Int 128 ID: { } } *3: { CubeShape: Int array with shape [2] [31, 1] TileShape: Int array with shape [2] [8, 2] CellShape: Int array with shape [1] [31] BucketSize: Int 128 ID: { } } } SEQNR: uInt 2 IndexSize: uInt 4 } COLUMNS: String array with shape [1] [DATA1] } *4: { TYPE: String "TiledShapeStMan" NAME: String "Data2StMan" SEQNR: uInt 3 SPEC: { MaxCacheSize: Int 0 DEFAULTTILESHAPE: Int array with shape [2] [8, 2] MAXIMUMCACHESIZE: Int64 0 HYPERCUBES: { } SEQNR: uInt 3 IndexSize: uInt 0 } COLUMNS: String array with shape [1] [DATA2] } *5: { TYPE: String "TiledShapeStMan" NAME: String "DATA3" SEQNR: uInt 4 SPEC: { MaxCacheSize: Int 0 DEFAULTTILESHAPE: Int array with shape [2] [8, 2] MAXIMUMCACHESIZE: Int64 0 HYPERCUBES: { *1: { CubeShape: Int array with shape [2] [1, 1] TileShape: Int array with shape [2] [1, 4] CellShape: Int array with shape [1] [1] BucketSize: Int 64 ID: { } } *2: { CubeShape: Int array with shape [2] [11, 1] TileShape: Int array with shape [2] [4, 4] CellShape: Int array with shape [1] [11] BucketSize: Int 256 ID: { } } *3: { CubeShape: Int array with shape [2] [31, 1] TileShape: Int array with shape [2] [4, 4] CellShape: Int array with shape [1] [31] BucketSize: Int 256 ID: { } } } SEQNR: uInt 4 IndexSize: uInt 4 } COLUMNS: String array with shape [1] [DATA3] } *6: { TYPE: String "StandardStMan" NAME: String "SCALAR3" SEQNR: uInt 5 SPEC: { MaxCacheSize: Int 2 BUCKETSIZE: Int 640 PERSCACHESIZE: Int 2 IndexLength: Int 0 } COLUMNS: String array with shape [1] [SCALAR3] } testCloneColumn ... *1: { TYPE: String "StandardStMan" NAME: String "SSM" SEQNR: uInt 0 SPEC: { MaxCacheSize: Int 2 BUCKETSIZE: Int 640 PERSCACHESIZE: Int 2 IndexLength: Int 0 } COLUMNS: String array with shape [2] [ARRAY, SCALAR] } *2: { TYPE: String "TiledCellStMan" NAME: String "DATA_stm" SEQNR: uInt 1 SPEC: { MaxCacheSize: Int 0 DEFAULTTILESHAPE: Int array with shape [2] [8, 2] MAXIMUMCACHESIZE: Int64 0 HYPERCUBES: { *1: { CubeShape: Int array with shape [1] [1] TileShape: Int array with shape [1] [1] CellShape: Int array with shape [1] [1] BucketSize: Int 8 ID: { } } *2: { CubeShape: Int array with shape [1] [11] TileShape: Int array with shape [1] [4] CellShape: Int array with shape [1] [11] BucketSize: Int 32 ID: { } } *3: { CubeShape: Int array with shape [1] [31] TileShape: Int array with shape [1] [4] CellShape: Int array with shape [1] [31] BucketSize: Int 32 ID: { } } } SEQNR: uInt 1 } COLUMNS: String array with shape [1] [DATA] } *3: { TYPE: String "TiledCellStMan" NAME: String "DATA1" SEQNR: uInt 2 SPEC: { MaxCacheSize: Int 0 DEFAULTTILESHAPE: Int array with shape [2] [8, 2] MAXIMUMCACHESIZE: Int64 0 HYPERCUBES: { *1: { CubeShape: Int array with shape [1] [1] TileShape: Int array with shape [1] [1] CellShape: Int array with shape [1] [1] BucketSize: Int 8 ID: { } } *2: { CubeShape: Int array with shape [1] [11] TileShape: Int array with shape [1] [8] CellShape: Int array with shape [1] [11] BucketSize: Int 64 ID: { } } *3: { CubeShape: Int array with shape [1] [31] TileShape: Int array with shape [1] [8] CellShape: Int array with shape [1] [31] BucketSize: Int 64 ID: { } } } SEQNR: uInt 2 } COLUMNS: String array with shape [1] [DATA1] } *4: { TYPE: String "TiledCellStMan" NAME: String "Data2StMan" SEQNR: uInt 3 SPEC: { MaxCacheSize: Int 0 DEFAULTTILESHAPE: Int array with shape [2] [8, 2] MAXIMUMCACHESIZE: Int64 0 HYPERCUBES: { } SEQNR: uInt 3 } COLUMNS: String array with shape [1] [DATA2] } *5: { TYPE: String "TiledCellStMan" NAME: String "DATA3" SEQNR: uInt 4 SPEC: { MaxCacheSize: Int 0 DEFAULTTILESHAPE: Int array with shape [2] [8, 2] MAXIMUMCACHESIZE: Int64 0 HYPERCUBES: { *1: { CubeShape: Int array with shape [1] [1] TileShape: Int array with shape [1] [1] CellShape: Int array with shape [1] [1] BucketSize: Int 16 ID: { } } *2: { CubeShape: Int array with shape [1] [11] TileShape: Int array with shape [1] [4] CellShape: Int array with shape [1] [11] BucketSize: Int 64 ID: { } } *3: { CubeShape: Int array with shape [1] [31] TileShape: Int array with shape [1] [4] CellShape: Int array with shape [1] [31] BucketSize: Int 64 ID: { } } } SEQNR: uInt 4 } COLUMNS: String array with shape [1] [DATA3] } *6: { TYPE: String "StandardStMan" NAME: String "SCALAR3" SEQNR: uInt 5 SPEC: { MaxCacheSize: Int 2 BUCKETSIZE: Int 640 PERSCACHESIZE: Int 2 IndexLength: Int 0 } COLUMNS: String array with shape [1] [SCALAR3] } testCloneColumn ... *1: { TYPE: String "StandardStMan" NAME: String "SSM" SEQNR: uInt 0 SPEC: { MaxCacheSize: Int 2 BUCKETSIZE: Int 640 PERSCACHESIZE: Int 2 IndexLength: Int 0 } COLUMNS: String array with shape [2] [ARRAY, SCALAR] } *2: { TYPE: String "TiledColumnStMan" NAME: String "DATA_stm" SEQNR: uInt 1 SPEC: { MaxCacheSize: Int 0 DEFAULTTILESHAPE: Int array with shape [2] [8, 2] MAXIMUMCACHESIZE: Int64 0 HYPERCUBES: { *1: { CubeShape: Int array with shape [2] [10, 4] TileShape: Int array with shape [2] [8, 2] CellShape: Int array with shape [1] [10] BucketSize: Int 128 ID: { } } } SEQNR: uInt 1 } COLUMNS: String array with shape [1] [DATA] } *3: { TYPE: String "TiledColumnStMan" NAME: String "DATA1" SEQNR: uInt 2 SPEC: { MaxCacheSize: Int 0 DEFAULTTILESHAPE: Int array with shape [2] [8, 2] MAXIMUMCACHESIZE: Int64 0 HYPERCUBES: { *1: { CubeShape: Int array with shape [2] [10, 4] TileShape: Int array with shape [2] [8, 2] CellShape: Int array with shape [1] [10] BucketSize: Int 128 ID: { } } } SEQNR: uInt 2 } COLUMNS: String array with shape [1] [DATA1] } *4: { TYPE: String "TiledColumnStMan" NAME: String "Data2StMan" SEQNR: uInt 3 SPEC: { MaxCacheSize: Int 0 DEFAULTTILESHAPE: Int array with shape [2] [8, 2] MAXIMUMCACHESIZE: Int64 0 HYPERCUBES: { *1: { CubeShape: Int array with shape [2] [10, 4] TileShape: Int array with shape [2] [8, 2] CellShape: Int array with shape [1] [10] BucketSize: Int 128 ID: { } } } SEQNR: uInt 3 } COLUMNS: String array with shape [1] [DATA2] } *5: { TYPE: String "TiledColumnStMan" NAME: String "DATA3" SEQNR: uInt 4 SPEC: { MaxCacheSize: Int 0 DEFAULTTILESHAPE: Int array with shape [2] [8, 2] MAXIMUMCACHESIZE: Int64 0 HYPERCUBES: { *1: { CubeShape: Int array with shape [2] [10, 4] TileShape: Int array with shape [2] [8, 2] CellShape: Int array with shape [1] [10] BucketSize: Int 256 ID: { } } } SEQNR: uInt 4 } COLUMNS: String array with shape [1] [DATA3] } *6: { TYPE: String "StandardStMan" NAME: String "SCALAR3" SEQNR: uInt 5 SPEC: { MaxCacheSize: Int 2 BUCKETSIZE: Int 640 PERSCACHESIZE: Int 2 IndexLength: Int 0 } COLUMNS: String array with shape [1] [SCALAR3] } tTableCopy_tmp.tbl tTableCopy_tmp.tbl/SUBTABLE tTableCopy_tmp.newtbl tTableCopy_tmp.newtbl/SUBTABLE 1 1 1 1 1 0 tTableCopy_tmp.tbl tTableCopy_tmp.tbl/SUBTABLE tTableCopy_tmp.newtbl tTableCopy_tmp.newtbl/SUBTABLE 1 1 0 0 0 0 tTableCopy_tmp.tbl tTableCopy_tmp.tbl/SUBTABLE tTableCopy_tmp.newtbl tTableCopy_tmp.newtbl/SUBTABLE 1 1 0 0 1 0 casacore-3.7.1/tables/Tables/test/tTableCopy.run000077500000000000000000000005251476623553700215700ustar00rootroot00000000000000#!/bin/sh #----------------------------------------------------------------------------- # Script to test the tTableCopy test program. #============================================================================= $casa_checktool ./tTableCopy $casa_checktool ./tTableCopy m $casa_checktool ./tTableCopy p n $casa_checktool ./tTableCopy m n casacore-3.7.1/tables/Tables/test/tTableCopyPerf.cc000066400000000000000000000054151476623553700221660ustar00rootroot00000000000000//# tTableCopy.cc: Test program for column copy performance //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include using namespace casacore; using namespace std; void testPerf (Int nrowPerf) { cout << "testPerf with " << nrowPerf << " rows ..." << endl; // First create a table. TableDesc td; td.addColumn (ArrayColumnDesc("DATA", IPosition(2,4,256))); td.addColumn (ScalarColumnDesc("SCALAR", 1)); SetupNewTable newtab("tTableCopyPerf_tmp.data", td, Table::New); StandardStMan ssm; TiledShapeStMan tsm("DATA_stm", IPosition(3,4,256,4)); newtab.bindAll (ssm); newtab.bindColumn ("DATA", tsm); Table tab(newtab, nrowPerf); ArrayColumn col(tab, "DATA"); Array arr(IPosition(2,4,256)); indgen(arr); Timer timer; for (uInt row=0; row (tab, "DATA", tab, "DATA3"); timer.mark(); TableCopy::copyColumnData (tab, "DATA", tab, "DATA3"); timer.show ("copycold"); TableCopy::cloneColumnTyped (tab, "DATA", tab, "DATA4"); timer.mark(); tableCommand ("update $1 set DATA4=DATA", tab); timer.show ("copytaql"); } int main (int argc, const char* argv[]) { Int nrowPerf = 10; if (argc > 1) { nrowPerf = atoi(argv[1]); } try { testPerf (nrowPerf); } catch (const exception& x) { cout << x.what() << endl; return 1; } return 0; } casacore-3.7.1/tables/Tables/test/tTableDesc.cc000066400000000000000000000367761476623553700213330ustar00rootroot00000000000000//# tTableDesc.cc: Test program for the TableDesc class //# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Test program for the TableDesc class // This program tests the class TableDesc and related classes. // The results are written to stdout. The script executing this program, // compares the results with the reference output file. // First build a description. void a (Bool doExcp) { // Add Scalar/ArrayColumnDesc to column type map. ScalarColumnDesc("x").registerClass(); ArrayColumnDesc("x").registerClass(); // First build the description of a subtable. // Do it in separate scope to destruct it (thus to write it). { TableDesc subtd("tTableDesc_tmp_sub", "1", TableDesc::New); subtd.rwKeywordSet().define ("subint", Int(10)); subtd.addColumn (ScalarColumnDesc ("ra")); subtd.addColumn (ScalarColumnDesc ("dec")); } TableDesc subtd("tTableDesc_tmp_sub", "1", TableDesc::Update); // Now build the main table description. uInt i; ColumnDesc cd,cd2; Vector arr(4); for (i=0; i<4; i++) { arr(i) = i; } TableDesc td("tTableDesc_tmp", "1", TableDesc::New); td.comment() = "A test of class TableDesc"; td.rwKeywordSet().define ("ra", float(3.14)); td.rwKeywordSet().define ("equinox", double(1950)); td.rwKeywordSet().define ("aa", Int(1)); td.addColumn (ScalarColumnDesc ("ab","Comment for column ab")); if (doExcp) { try { td.addColumn (ScalarColumnDesc ("ab")); // already exists } catch (std::exception& x) { cout << x.what() << endl; } } td.addColumn (ScalarColumnDesc ("ac")); td.rwColumnDesc("ac").rwKeywordSet().define ("scale", Complex(0,0)); td.rwColumnDesc("ac").rwKeywordSet().define ("unit", ""); td.addColumn (ScalarColumnDesc ("ad","comment for ad")); td.rwColumnDesc("ac").rwKeywordSet().define ("unit", "DEG"); td.addColumn (ScalarColumnDesc ("ae")); td.addColumn (ArrayColumnDesc ("arr0")); td.addColumn (ScalarRecordColumnDesc ("rec0")); if (doExcp) { try { td.addColumn (ScalarColumnDesc ("af", ColumnDesc::Undefined)); } catch (std::exception& x) { cout << x.what() << endl; // undefined given } } td.addColumn (ArrayColumnDesc ("Arr1","comment for Arr1",0)); td.addColumn (ArrayColumnDesc ("A2r1","comment for Arr1",3)); ArrayColumnDesc coldes("Arr3","comment for Arr1", IPosition(2,3,4), ColumnDesc::Direct); td.addColumn (coldes); // Set the shape of some columns. if (doExcp) { try { td.rwColumnDesc("ab").setNdim (2); } catch (std::exception& x) { cout << x.what() << endl; // column is a scalar } try { td.rwColumnDesc("ab").setShape (IPosition()); } catch (std::exception& x) { cout << x.what() << endl; // column is a scalar } try { td.rwColumnDesc("Arr3").setNdim (2); } catch (std::exception& x) { cout << x.what() << endl; // ndim already defined } try { td.rwColumnDesc("Arr3").setShape (IPosition()); } catch (std::exception& x) { cout << x.what() << endl; // shape already defined } } td.addColumn (SubTableDesc("sub1", "subtable by name", "tTableDesc_tmp_sub")); td.addColumn (SubTableDesc("sub2", "subtable copy", subtd)); td.addColumn (SubTableDesc("sub3", "subtable pointer", &subtd)); td.addColumn (SubTableDesc("sub4", "subtable by name", "tTableDesc_tmp_sub1"), "sub4Renamed"); td.show(); cout << td.keywordSet().isDefined("aa") << td.isColumn("ab") << td.keywordSet().isDefined("ab") << td.isColumn("aa") << endl; // Not all subtables are known. cout << "checkSubTableDesc: "; if (doExcp) { try { td.checkSubTableDesc(); } catch (std::exception& x) { cout << x.what() << endl; // subtable sub1 does not exist } } // Add another column and keyword to the subtable to test // if they get reflected in the subtable. They should for sub3, // but not in sub1 and sub2. subtd.addColumn (ScalarColumnDesc ("Equinox")); subtd.rwKeywordSet().define ("CoordSys", "equatorial"); td["sub1"].tableDesc()->show(); // should have 2 columns td["sub3"].tableDesc()->show(); // should have 3 columns td["sub2"].tableDesc()->show(); // should have 2 columns // Test isDisjoint, etc.. TableDesc tdx(td); const ColumnDescSet& set1 = td["sub1"].tableDesc()->columnDescSet(); const ColumnDescSet& set2 = td["sub2"].tableDesc()->columnDescSet(); const ColumnDescSet& set3 = td["sub3"].tableDesc()->columnDescSet(); Bool equalDataTypes; AlwaysAssertExit (set2.isDisjoint (tdx.columnDescSet())); tdx.addColumn (ScalarColumnDesc ("ra")); AlwaysAssertExit (! set2.isDisjoint (tdx.columnDescSet())); AlwaysAssertExit (! set2.isSubset (tdx.columnDescSet(), equalDataTypes)); tdx.addColumn (ScalarColumnDesc ("dec")); AlwaysAssertExit (set2.isSubset (tdx.columnDescSet(), equalDataTypes)); AlwaysAssertExit (! equalDataTypes); AlwaysAssertExit (! set1.isDisjoint (set2)); AlwaysAssertExit (set1.isEqual (set2, equalDataTypes)); AlwaysAssertExit (equalDataTypes); AlwaysAssertExit (set1.isSubset (set2, equalDataTypes)); AlwaysAssertExit (set1.isSuperset (set2, equalDataTypes)); AlwaysAssertExit (! set1.isStrictSubset (set2, equalDataTypes)); AlwaysAssertExit (! set1.isStrictSuperset (set2, equalDataTypes)); AlwaysAssertExit (! set3.isEqual (set2, equalDataTypes)); AlwaysAssertExit (! set3.isSubset (set2, equalDataTypes)); AlwaysAssertExit (set3.isSuperset (set2, equalDataTypes)); AlwaysAssertExit (! set3.isStrictSubset (set2, equalDataTypes)); AlwaysAssertExit (set3.isStrictSuperset (set2, equalDataTypes)); // Try some ColumnDesc functions. // First add the column; remove it at the end. td.addColumn (ArrayColumnDesc ("ArrExtra")); ColumnDesc& cdesc = td.rwColumnDesc("ArrExtra"); AlwaysAssertExit (cdesc.ndim() == -1); AlwaysAssertExit (cdesc.shape() == IPosition()); AlwaysAssertExit (cdesc.options() == 0); cdesc.setShape (IPosition(1,4)); AlwaysAssertExit (cdesc.ndim() == 1); AlwaysAssertExit (cdesc.shape() == IPosition(1,4)); AlwaysAssertExit (cdesc.options() == ColumnDesc::FixedShape); cdesc.setNdim (0); AlwaysAssertExit (cdesc.ndim() == -1); AlwaysAssertExit (cdesc.shape() == IPosition()); AlwaysAssertExit (cdesc.options() == ColumnDesc::FixedShape); cdesc.setShape (IPosition(2,4,5), True); AlwaysAssertExit (cdesc.ndim() == 2); AlwaysAssertExit (cdesc.shape() == IPosition(2,4,5)); AlwaysAssertExit (cdesc.options() == (ColumnDesc::FixedShape|ColumnDesc::Direct)); cdesc.setNdim (0); AlwaysAssertExit (cdesc.ndim() == -1); AlwaysAssertExit (cdesc.shape() == IPosition()); AlwaysAssertExit (cdesc.options() == (ColumnDesc::FixedShape|ColumnDesc::Direct)); cdesc.setShape (IPosition(1,4)); AlwaysAssertExit (cdesc.ndim() == 1); AlwaysAssertExit (cdesc.shape() == IPosition(1,4)); AlwaysAssertExit (cdesc.options() == (ColumnDesc::FixedShape|ColumnDesc::Direct)); cdesc.setNdim (0); AlwaysAssertExit (cdesc.ndim() == -1); AlwaysAssertExit (cdesc.shape() == IPosition()); AlwaysAssertExit (cdesc.options() == (ColumnDesc::FixedShape|ColumnDesc::Direct)); cdesc.setShape (IPosition(2,4,5), False); AlwaysAssertExit (cdesc.ndim() == 2); AlwaysAssertExit (cdesc.shape() == IPosition(2,4,5)); AlwaysAssertExit (cdesc.options() == ColumnDesc::FixedShape); cdesc.setOptions (0); AlwaysAssertExit (cdesc.ndim() == -1); AlwaysAssertExit (cdesc.shape() == IPosition()); AlwaysAssertExit (cdesc.options() == 0); cdesc.setOptions (ColumnDesc::Direct); AlwaysAssertExit (cdesc.ndim() == -1); AlwaysAssertExit (cdesc.shape() == IPosition()); AlwaysAssertExit (cdesc.options() == (ColumnDesc::FixedShape|ColumnDesc::Direct)); td.removeColumn ("ArrExtra"); } // Remove some keywords/columns. // Do some tests of the options for the constructor. void b (Bool doExcp) { TableDesc td("tTableDesc_tmp", TableDesc::Update); cout << td.columnNames() << endl; cout << (td.columnDesc("ab") == td.columnDesc("ac")); cout << (td.columnDesc("ad") == td.columnDesc("ae")); cout << (td.columnDesc("ab") == td.columnDesc("Arr1")); cout << (td.columnDesc("Arr1") == td.columnDesc("Arr3")); cout << (td.columnDesc("Arr3") == td.columnDesc("ad")); cout << (td.columnDesc("A2r1") == td.columnDesc("ab")); cout << (td.columnDesc("sub1") != td.columnDesc("sub2")); cout << (td.columnDesc("sub1") != td.columnDesc("ab")); cout << endl; td.show(); td["sub1"].tableDesc()->show(); // should have 3 columns td["sub2"].tableDesc()->show(); // should have 2 columns td["sub3"].tableDesc()->show(); // should have 3 columns cout << "endshow" << endl; // Set the shape of Arr1. td.rwColumnDesc("Arr1").setNdim (2); if (doExcp) { try { td.rwColumnDesc("Arr1").setShape (IPosition(3,4,5,6)); } catch (std::exception& x) { cout << x.what() << endl; // shape mimatches ndim } } td.rwColumnDesc("Arr1").setShape (IPosition(2,4,5)); if (doExcp) { try { td["Arr1"].tableDesc(); // no subtable } catch (std::exception& x) { cout << x.what() << endl; } } // Define another descr. and add it to the first descr. TableDesc tdscr("TabSub",TableDesc::Scratch); tdscr.rwKeywordSet().define ("key1", Int(0)); ScalarColumnDesc colaDesc ("cola"); colaDesc.setMaxLength (32); tdscr.addColumn (colaDesc); td.addColumn (SubTableDesc("colsub","colsub comment",tdscr)); tdscr.rwKeywordSet().define ("key2", Int(0)); tdscr.show(); cout<("colint","comment")); // Define a keyword colint_key1 (=10) for that column. c1.rwKeywordSet().define ("colint_key1", Int(10)); // It can also be done the other way around. ScalarColumnDesc colint("colint2","comment2"); colint.rwKeywordSet().define ("colint_key1", Int(20)); td.addColumn(colint); // Add a third column. c1 = td.addColumn (ScalarColumnDesc ("colint3","comment")); // Extend the comment. td.rwColumnDesc("colint").comment() += " addition"; // Rename the column and back. td.renameColumn ("colintnew", "colint"); AlwaysAssertExit (td.isColumn ("colintnew")); AlwaysAssertExit (! td.isColumn ("colint")); td.renameColumn ("colint", "colintnew"); AlwaysAssertExit (! td.isColumn ("colintnew")); AlwaysAssertExit (td.isColumn ("colint")); // Rename to already existing column must fail. // Also a non-existing column cannot be renamed. if (doExcp) { try { td.renameColumn ("colint2", "colint"); } catch (std::exception& x) { cout << x.what() << endl; } try { td.renameColumn ("colintnew", "colintxxx"); } catch (std::exception& x) { cout << x.what() << endl; } } // Define a keyword for the table. td.rwKeywordSet().define ("tab_key1", "this is a string"); // Register engines. DataManager::registerCtor ("c1_engine",0); DataManager::registerCtor ("c2_engine",0); // Define a virtual column. td.addColumn(ScalarColumnDesc("c1", "c1-comment", "c1_engine", "")); // Show name and comment of all column descriptions. for (uInt jj=0; jj #include #include // // Example for a non-standard column in a table description // // // // //# Classes you should understand before using this one. //
      • TableDesc // // // ExampleDesc is an example showing how to use a column with // a non-standard data type in a table description. // class ExampleDesc { public: ExampleDesc(): x_p(0), y_p(0) {} ExampleDesc(Int x, float y) : x_p(x), y_p(y) {} static String dataTypeId() { return "ExampleDesc"; } Int x() const { return x_p; } float y() const { return y_p; } Int& x() { return x_p; } float& y() { return y_p; } int operator== (const ExampleDesc& that) const { return x_p==that.x_p && y_p==that.y_p; } int operator< (const ExampleDesc& that) const { return x_p #include #include #include #include #include #include #include #include #include // Test program for hypercolumns in TableDesc // This program tests the hypercolumn functionality in class TableDesc. // The results are written to stdout. The script executing this program, // compares the results with the reference output file. void a(); void b(); void excpDesc(); int main (int argc, const char*[]) { try { a(); b(); if (argc < 2) { excpDesc(); } } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } // First build a description. void a() { // Build the table description. TableDesc td ("tTableDescHyper_tmp", "1", TableDesc::New); td.addColumn (ScalarColumnDesc ("Time")); td.addColumn (ScalarColumnDesc ("Baseline")); td.addColumn (ScalarColumnDesc ("DataScalar")); td.addColumn (ArrayColumnDesc ("Pol", IPosition(1,16), ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc ("Freq", 1)); td.addColumn (ScalarColumnDesc ("Id")); td.addColumn (ArrayColumnDesc ("Data", 2)); td.addColumn (ArrayColumnDesc ("Weight", IPosition(2,16,25), ColumnDesc::FixedShape)); td.defineHypercolumn ("TSMExample1", 4, stringToVector ("Data,Weight"), stringToVector ("Pol,Freq,Baseline,Time"), stringToVector ("Id")); td.defineHypercolumn ("TSMExample2", 4, stringToVector ("Data")); td.defineHypercolumn ("TSMExample3", 3, stringToVector ("Data,Weight"), stringToVector ("Pol,Freq,Time")); td.defineHypercolumn ("TSMExample4", 2, stringToVector ("DataScalar"), stringToVector ("Baseline,Time")); td.defineHypercolumn ("TSMExample5", 4, stringToVector ("Data,Weight"), stringToVector (",,,")); td.defineHypercolumn ("TSMExample6", 4, stringToVector ("Data,Weight"), stringToVector ("Pol,,,")); td.defineHypercolumn ("TSMExample7", 4, stringToVector ("Data,Weight"), stringToVector (",Freq,,")); td.defineHypercolumn ("TSMExample8", 4, stringToVector ("Data,Weight"), stringToVector (",,Time,")); td.defineHypercolumn ("TSMExample9", 4, stringToVector ("Data,Weight"), stringToVector (",,,Baseline")); td.defineHypercolumn ("TSMExample10", 4, stringToVector ("Data,Weight"), stringToVector (",Freq,Time,")); td.defineHypercolumn ("TSMExample11", 4, stringToVector ("Data,Weight"), stringToVector ("Pol,,,Baseline")); } void showHyper (const TableDesc& td, const String& name) { Vector idNames; Vector coordNames; Vector dataNames; uInt ndim = td.hypercolumnDesc (name, dataNames, coordNames, idNames); cout << name << ": ndim=" << ndim << endl; cout << " Data=" << dataNames; cout << "Coord=" << coordNames; cout << "Id=" << idNames << endl; } void b() { TableDesc td ("tTableDescHyper_tmp"); cout << td.isHypercolumn ("TSMExamplea") << td.isHypercolumn ("TSMExample1") << td.isHypercolumn ("TSMExample2") << td.isHypercolumn ("TSMExample3") << td.isHypercolumn ("TSMExample4") << td.isHypercolumn ("TSMExampleb") << endl; cout << td.hypercolumnNames() << endl; showHyper (td, "TSMExample1"); showHyper (td, "TSMExample2"); showHyper (td, "TSMExample3"); showHyper (td, "TSMExample4"); showHyper (td, "TSMExample5"); showHyper (td, "TSMExample6"); showHyper (td, "TSMExample7"); showHyper (td, "TSMExample8"); showHyper (td, "TSMExample9"); showHyper (td, "TSMExample10"); showHyper (td, "TSMExample11"); td.show(); } void excpDesc() { // Build the table description. TableDesc td ("", "1", TableDesc::Scratch); td.addColumn (ScalarColumnDesc ("Time")); td.addColumn (ScalarColumnDesc ("TimeNotNum")); td.addColumn (ScalarColumnDesc ("TimeShort")); td.addColumn (ScalarColumnDesc ("Baseline")); td.addColumn (ArrayColumnDesc ("Pol", 1)); td.addColumn (ArrayColumnDesc ("Freq", 1)); td.addColumn (ScalarColumnDesc ("Id")); td.addColumn (ArrayColumnDesc ("Data", 2)); td.addColumn (ArrayColumnDesc ("Data0")); td.addColumn (ArrayColumnDesc ("Data1", 1)); td.addColumn (ArrayColumnDesc ("Weight", 2)); try { td.defineHypercolumn ("TSMExample", 4, stringToVector ("")); } catch (std::exception& x) { cout << x.what() << endl; // no data columns } try { td.defineHypercolumn ("TSMExample", 0, stringToVector ("Data,Weight")); } catch (std::exception& x) { cout << x.what() << endl; // ndim < 1 } try { td.defineHypercolumn ("TSMExample", 4, stringToVector ("Data,Weight"), stringToVector ("Pol,Freq,Baseline")); } catch (std::exception& x) { cout << x.what() << endl; // ndim != #coord } try { td.defineHypercolumn ("TSMExample", 4, stringToVector ("Data,Weight"), stringToVector ("Pol,Freq,Baseline,Timex")); } catch (std::exception& x) { cout << x.what() << endl; // Timex does not exist } try { td.defineHypercolumn ("TSMExample", 4, stringToVector ("Data,Weight,Datax")); } catch (std::exception& x) { cout << x.what() << endl; // Datax does not exist } try { td.defineHypercolumn ("TSMExample", 4, stringToVector ("Data,Weight"), stringToVector ("Pol,Freq,Baseline,Time"), stringToVector ("Idx")); } catch (std::exception& x) { cout << x.what() << endl; // Idx does not exist } try { td.defineHypercolumn ("TSMExample", 4, stringToVector ("Data,Weight"), stringToVector ("Pol,Freq,Baseline,TimeNotNum")); } catch (std::exception& x) { cout << x.what() << endl; // TimeNotNum not numeric } try { td.defineHypercolumn ("TSMExample", 4, stringToVector ("Data,Weight"), stringToVector ("Pol,Freq,Baseline,TimeShort")); } catch (std::exception& x) { cout << x.what() << endl; // Coord short not supported } try { td.defineHypercolumn ("TSMExample", 4, stringToVector ("Data,Weight"), stringToVector ("Pol,Freq,Baseline,Data")); } catch (std::exception& x) { cout << x.what() << endl; // Coord Data is array > 1-dim } try { td.defineHypercolumn ("TSMExample", 4, stringToVector ("Data,Weight"), stringToVector ("Pol,Freq,Baseline,Pol")); } catch (std::exception& x) { cout << x.what() << endl; // coord vectors not at start } try { td.defineHypercolumn ("TSMExample", 4, stringToVector ("Data0,Weight")); } catch (std::exception& x) { cout << x.what() << endl; // Data #dim undefined } try { td.defineHypercolumn ("TSMExample", 4, stringToVector ("Data1,Weight")); } catch (std::exception& x) { cout << x.what() << endl; // Data #dim != Weight #ndim } try { td.defineHypercolumn ("TSMExample", 4, stringToVector ("Data1"), stringToVector ("Pol,Freq,Baseline,Time")); } catch (std::exception& x) { cout << x.what() << endl; // Data #dim != #coordVector } try { td.defineHypercolumn ("TSMExample", 4, stringToVector ("Data"), stringToVector ("Pol,Time,Baseline,Time")); } catch (std::exception&x) { cout << x.what() << endl; // Data #dim != #coordVector } try { td.defineHypercolumn ("TSMExample", 4, stringToVector ("Data"), stringToVector (",,Pol,")); } catch (std::exception& x) { cout << x.what() << endl; // Data #dim != #coordVector } try { td.defineHypercolumn ("TSMExample", 4, stringToVector ("Data,Weight"), stringToVector ("Pol,Freq,Baseline,Time"), stringToVector ("TimeShort")); } catch (std::exception& x) { cout << x.what() << endl; // Id short not supported } try { td.defineHypercolumn ("TSMExample", 4, stringToVector ("Data,Weight"), stringToVector ("Pol,Freq,Baseline,Time"), stringToVector ("Data")); } catch (std::exception& x) { cout << x.what() << endl; // Id array not supported } try { td.defineHypercolumn ("TSMExample", 4, stringToVector ("Data,Weight"), stringToVector ("Pol,Freq,Baseline,Time"), stringToVector ("Time")); } catch (std::exception& x) { cout << x.what() << endl; // Time double used } } casacore-3.7.1/tables/Tables/test/tTableDescHyper.out000066400000000000000000000101111476623553700225340ustar00rootroot00000000000000011110 [TSMExample1, TSMExample2, TSMExample3, TSMExample4, TSMExample5, TSMExample6, TSMExample7, TSMExample8, TSMExample9, TSMExample10, TSMExample11] TSMExample1: ndim=4 Data=[Data, Weight]Coord=[Pol, Freq, Baseline, Time]Id=[Id] TSMExample2: ndim=4 Data=[Data]Coord=[]Id=[] TSMExample3: ndim=3 Data=[Data, Weight]Coord=[Pol, Freq, Time]Id=[] TSMExample4: ndim=2 Data=[DataScalar]Coord=[Baseline, Time]Id=[] TSMExample5: ndim=4 Data=[Data, Weight]Coord=[, , , ]Id=[] TSMExample6: ndim=4 Data=[Data, Weight]Coord=[Pol, , , ]Id=[] TSMExample7: ndim=4 Data=[Data, Weight]Coord=[, Freq, , ]Id=[] TSMExample8: ndim=4 Data=[Data, Weight]Coord=[, , Time, ]Id=[] TSMExample9: ndim=4 Data=[Data, Weight]Coord=[, , , Baseline]Id=[] TSMExample10: ndim=4 Data=[Data, Weight]Coord=[, Freq, Time, ]Id=[] TSMExample11: ndim=4 Data=[Data, Weight]Coord=[Pol, , , Baseline]Id=[] TableDesc tTableDescHyper_tmp version 1 (Directory ./) --------- Comment: #Keywords = 0 #Columns = 8 0 Hypercolumn_TSMExample1 : SUBRECORD 1 Hypercolumn_TSMExample2 : SUBRECORD 2 Hypercolumn_TSMExample3 : SUBRECORD 3 Hypercolumn_TSMExample4 : SUBRECORD 4 Hypercolumn_TSMExample5 : SUBRECORD 5 Hypercolumn_TSMExample6 : SUBRECORD 6 Hypercolumn_TSMExample7 : SUBRECORD 7 Hypercolumn_TSMExample8 : SUBRECORD 8 Hypercolumn_TSMExample9 : SUBRECORD 9 Hypercolumn_TSMExample10 : SUBRECORD 10 Hypercolumn_TSMExample11 : SUBRECORD Name=Time DataType=float DataManager=TiledShapeStMan/TSMExample10 Default=0 Comment = #keywords=0 Name=Baseline DataType=double DataManager=TiledShapeStMan/TSMExample11 Default=0 Comment = #keywords=0 Name=DataScalar DataType=Complex DataManager=TiledColumnStMan/TSMExample4 Default=(0,0) Comment = #keywords=0 Name=Pol DataType=float Nrdim=1 Shape=[16] DataManager=TiledShapeStMan/TSMExample11 Comment = #keywords=0 Name=Freq DataType=float Nrdim=1 Shape=[] DataManager=TiledShapeStMan/TSMExample10 Comment = #keywords=0 Name=Id DataType=String DataManager=TiledShapeStMan/TSMExample1 Default= Comment = #keywords=0 Name=Data DataType=float Nrdim=2 Shape=[] DataManager=TiledShapeStMan/TSMExample11 Comment = #keywords=0 Name=Weight DataType=float Nrdim=2 Shape=[16, 25] DataManager=TiledShapeStMan/TSMExample11 Comment = #keywords=0 Invalid description of hypercolumn TSMExample: dataColumnNames is empty Invalid description of hypercolumn TSMExample: ndim < 1 Invalid description of hypercolumn TSMExample: #coordColumnNames mismatches ndim Invalid description of hypercolumn TSMExample: coordColumn Timex does not exist Invalid description of hypercolumn TSMExample: dataColumn Datax does not exist Invalid description of hypercolumn TSMExample: idColumn Idx does not exist Invalid description of hypercolumn TSMExample: coordColumn TimeNotNum is not numeric Invalid description of hypercolumn TSMExample: coordColumn TimeShort: (u)Char, (u)Short and Int64 not supported Invalid description of hypercolumn TSMExample: coordColumn Data is not a scalar or vector Invalid description of hypercolumn TSMExample: coordinate vectors have to describe the first axes Invalid description of hypercolumn TSMExample: the dimensionality of data column Data0 is undefined Invalid description of hypercolumn TSMExample: the dimensionality of data column Weight mismatches that of previous data columns Invalid description of hypercolumn TSMExample: the dimensionality of the data columns mismatches nr of coordinate columns with a vector value Invalid description of hypercolumn TSMExample: the dimensionality of the data columns mismatches nr of coordinate columns with a vector value Invalid description of hypercolumn TSMExample: the dimensionality of the data columns mismatches nr of coordinate columns with a vector value Invalid description of hypercolumn TSMExample: idColumn TimeShort: (u)Char, (u)Short and Int64 not supported Invalid description of hypercolumn TSMExample: idColumn Data is not a scalar Invalid description of hypercolumn TSMExample: column name Time is multiply used casacore-3.7.1/tables/Tables/test/tTableInfo.cc000066400000000000000000000045001476623553700213240ustar00rootroot00000000000000//# tTableInfo.cc: Test program for class TableInfo //# Copyright (C) 1996,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include // // Test program for the Table classes // // This program tests the class TableInfo. // The results are written to stdout. The script executing this program, // compares the results with the reference output file. void writeInfo() { TableInfo info; info.setType ("infotype"); info.setSubType ("infosubtype"); info.readmeAddLine ("first line"); info.readmeAddLine ("second line"); info.readmeAddLine ("third line\nfourth line"); info.flush ("tTableInfo_tmp.data"); } void readInfo() { TableInfo info("tTableInfo_tmp.data"); cout << info.type() << endl; cout << info.subType() << endl; cout << info.readme() << endl; } int main() { try { writeInfo(); readInfo(); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } casacore-3.7.1/tables/Tables/test/tTableInfo.out000066400000000000000000000001041476623553700215420ustar00rootroot00000000000000infotype infosubtype first line second line third line fourth line casacore-3.7.1/tables/Tables/test/tTableIter.cc000066400000000000000000000254301476623553700213410ustar00rootroot00000000000000//# tTableIter.cc: Test program for table iterators //# Copyright (C) 1994,1995,1996,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include // Test program for table iterators // This program tests the table iterators.. // It creates a description and a table. // It reads back the table, sorts it and selects rows. // The data read is written to stdout to be checked. // The standard output file tTableIter.out is checked in as a reference. // The script tTableIter.run executes tTableIter and compares its output // with tTableIter.out. void credes(); void cretab(uInt); void doiter0(); void doiter1(); void doiter2(); void doiter3(); void test_cache_boundaries(); int main (int argc, const char* argv[]) { uInt nr = 5000; if (argc > 1) { istringstream istr(argv[1]); istr >> nr; } credes(); // make description cretab(nr); // create table (and write it) doiter0(); // do no column iteration doiter1(); // do single column iteration doiter2(); // do two column iteration doiter3(); // do interval iteration test_cache_boundaries(); // test option to cache group boundaries return 0; // successfully executed } // Create the description of the table and its subtable. // Define some defaults (as scalar and array) to test if they // are filled in correctly when a row is added to a table. void credes () { TableDesc txp("tTableIter_tmp", TableDesc::New); txp.addColumn (ScalarColumnDesc ("col1")); txp.addColumn (ScalarColumnDesc ("col2")); txp.addColumn (ScalarColumnDesc ("col3")); txp.addColumn (ScalarColumnDesc ("col4")); } // Write data into the table. void cretab(uInt nr) { Int i; SetupNewTable newtab ("tTableIter_tmp.data","tTableIter_tmp",Table::New); Table tab(newtab, nr); ScalarColumn col1 (tab, "col1"); ScalarColumn col2 (tab, "col2"); ScalarColumn col3 (tab, "col3"); ScalarColumn col4 (tab, "col4"); for (i=0; i iv0; TableIterator iter0(tab, iv0); Int nr = 0; while (!iter0.pastEnd()) { t = iter0.table(); AlwaysAssertExit (t.nrow() == tab.nrow()); nr++; iter0.next(); } cout << " #iter=" << nr << endl; iter0.reset(); nr = 0; while (!iter0.pastEnd()) { t = iter0.table(); AlwaysAssertExit (t.nrow() == tab.nrow()); nr++; iter0.next(); } cout << " #iter1=" << nr << endl; } void doiter1() { Table t; Table tab ("tTableIter_tmp.data"); Block iv0(1); iv0[0] = "col1"; TableIterator iter0(tab, iv0); Int nr = 0; while (!iter0.pastEnd()) { t = iter0.table(); ScalarColumn col1(t, "col1"); Vector vec; col1.getColumn (vec); cout << t.nrow() << " "; if (!allEQ(vec, nr)) { cout << "error in iter. " << nr << " " << vec(0) << " " << vec(vec.nelements()-1) << " " << vec.nelements() << endl; } cout << "(" << iter0.keyChangeAtLastNext() << ") "; if ( (nr<9 && iter0.keyChangeAtLastNext()!=iv0[0]) || (nr==9 && iter0.keyChangeAtLastNext()!="") ) { // last iter is empty string cout << "error in TableIter::keyChangeAtLastNext() " << iter0.keyChangeAtLastNext() << endl; } nr++; iter0.next(); } cout << " #iter=" << nr << endl; iter0.reset(); nr = 0; while (!iter0.pastEnd()) { t = iter0.table(); ScalarColumn col1(t, "col1"); Vector vec; col1.getColumn (vec); cout << t.nrow() << " "; if (!allEQ(vec, nr)) { cout << "error in iter. " << nr << " " << vec(0) << " " << vec(vec.nelements()-1) << " " << vec.nelements() << endl; } nr++; iter0.next(); } cout << " #iter1=" << nr << endl; } void doiter2() { Table tab1 ("tTableIter_tmp.data", Table::Update); Table t1; Block iv1(2); iv1[0] = "col2"; iv1[1] = "col1"; TableIterator iter1(tab1, iv1); Int nr = 0; Int l1 = -1; double l2 = -1; while (!iter1.pastEnd()) { t1 = iter1.table(); ScalarColumn col1(t1, "col1"); ScalarColumn col2(t1, "col2"); Vector vec1; col1.getColumn (vec1); Vector vec2; col2.getColumn (vec2); if (!(allEQ(vec1, vec1(0)) && allEQ(vec2, vec2(0)))) { cout << "error in iter. " << nr << " " << vec1(0) << " " << vec1(vec1.nelements()-1) << " " << vec2(0) << " " << vec2(vec2.nelements()-1) << endl; } if (vec2(0) < l2 || (vec2(0) == l2 && vec1(0) <= l1)) { cout << "order error " << vec2(0) << " " << l2 << ", " << vec1(0) << " " << l1 << endl; } l1 = vec1(0); l2 = vec2(0); nr++; iter1.next(); } cout << " #iter2=" << nr << endl; } void doiter3() { Table tab1 ("tTableIter_tmp.data"); Table t1; Block iv1(1); iv1[0] = "col3"; Block> compObj(1); compObj[0].reset (new CompareIntervalReal(10., 0.)); Block orders(1); orders[0] = TableIterator::Ascending; TableIterator iter1(tab1, iv1, compObj, orders); TableIterator iter2; iter2 = iter1; AlwaysAssertExit(iter2.table().nrow() == iter1.table().nrow()); // Test that copied new iterator gives the same number of rows int iter1count = 0; while(!iter1.pastEnd()) { iter1.next(); iter1count++; } int iter2count = 0; while(!iter2.pastEnd()) { iter2.next(); iter2count++; } AlwaysAssertExit(iter1count == iter2count); iter1.reset(); iter2.reset(); // Test that copied new iterator does not copy the state iter1.next(); iter1.next(); // Advance iter1 by two, so state changes iter1count = 0; iter2 = iter1; // iter2 should be 'clean' and iterate 2 more than iter1 TableIterator iter3 = iter1; iter3.copyState(iter1); // iter3 should iterate as much as iter1 while(!iter1.pastEnd()) { iter1.next(); iter1count++; } iter2count = 0; while(!iter2.pastEnd()) { iter2.next(); iter2count++; } AlwaysAssertExit(iter2count == iter1count + 2); int iter3count = 0; while(!iter3.pastEnd()) { iter3.next(); iter3count++; } AlwaysAssertExit(iter3count == iter1count); iter1.reset(); Int nr = 0; float l3 = -1; while (!iter1.pastEnd()) { t1 = iter1.table(); cout << t1.nrow() << " "; ScalarColumn col3(t1, "col3"); Vector vec3; col3.getColumn (vec3); if (max(vec3) - min(vec3) > 9.5) { cout << "Interval order error" << endl; } if (vec3(0) < l3) { cout << "order error " << vec3(0) << " " << l3 << endl; } l3 = vec3(0); nr++; iter1.next(); } cout << " #iter3=" << nr << endl; } void test_cache_boundaries() { // Create two iterators, one that cached the boundaries between iterations // and one that does not. Table tab1 ("tTableIter_tmp.data"); Table t1; Block sortCols(2); sortCols[0] = "col2"; sortCols[1] = "col1"; Block> compObj(2); // use default compares Block orders(2); orders[0] = TableIterator::Ascending; orders[1] = TableIterator::Ascending; TableIterator iter1(tab1, sortCols, compObj, orders); TableIterator iter2(tab1, sortCols, compObj, orders, TableIterator::ParSort, true); iter1.reset(); iter2.reset(); while (!iter1.pastEnd()) { auto iter1Table = iter1.table(); auto iter2Table = iter2.table(); AlwaysAssertExit(iter2Table.nrow() == iter1Table.nrow()); ScalarColumn iter1Col1 (iter1Table, "col1"); ScalarColumn iter1Col2 (iter1Table, "col2"); ScalarColumn iter1Col3 (iter1Table, "col3"); ScalarColumn iter1Col4 (iter1Table, "col4"); ScalarColumn iter2Col1 (iter2Table, "col1"); ScalarColumn iter2Col2 (iter2Table, "col2"); ScalarColumn iter2Col3 (iter2Table, "col3"); ScalarColumn iter2Col4 (iter2Table, "col4"); Vector iter1Vec1; Vector iter1Vec2; Vector iter1Vec3; Vector iter1Vec4; Vector iter2Vec1; Vector iter2Vec2; Vector iter2Vec3; Vector iter2Vec4; iter1Col1.getColumn (iter1Vec1); iter1Col2.getColumn (iter1Vec2); iter1Col3.getColumn (iter1Vec3); iter1Col4.getColumn (iter1Vec4); iter2Col1.getColumn (iter2Vec1); iter2Col2.getColumn (iter2Vec2); iter2Col3.getColumn (iter2Vec3); iter2Col4.getColumn (iter2Vec4); // Check that all the columns for this iteration have the same // content in both iterators. AlwaysAssertExit(allEQ(iter1Vec1, iter2Vec1)); AlwaysAssertExit(allEQ(iter1Vec2, iter2Vec2)); AlwaysAssertExit(allEQ(iter1Vec3, iter2Vec3)); AlwaysAssertExit(allEQ(iter1Vec4, iter2Vec4)); // Check that the changing key is the same in both iterators. AlwaysAssertExit(iter1.keyChangeAtLastNext() == iter2.keyChangeAtLastNext()); iter1.next(); iter2.next(); } } casacore-3.7.1/tables/Tables/test/tTableIter.out000066400000000000000000000004111476623553700215530ustar00rootroot00000000000000Filling done #iter=1 #iter1=1 500 (col1) 500 (col1) 500 (col1) 500 (col1) 500 (col1) 500 (col1) 500 (col1) 500 (col1) 500 (col1) 500 () #iter=10 500 500 500 500 500 500 500 500 500 500 #iter1=10 #iter2=210 668 670 670 670 670 662 660 330 #iter3=8 casacore-3.7.1/tables/Tables/test/tTableKeywords.cc000066400000000000000000000140721476623553700222450ustar00rootroot00000000000000//# tTableKeywords.cc Test program for the table keywords //# Copyright (C) 1994,1995,1996,1997,2000,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# Includes #include #include #include #include #include #include #include #include // This program tests some aspects of the table keywords, mainly // if writing them and reading them back works fine. // It was originally written by Mark Wieringa to track down a problem. // Create tables in a subdirectory which is created first. void createTables() { // Create subdirectory with this name, so assay will delete everything. Directory dir("tTableKeywords_tmp"); dir.create(); // Create a main table. TableDesc td("", "", TableDesc::Scratch); td.addColumn (ScalarColumnDesc("RowNr")); SetupNewTable newtab("tTableKeywords_tmp/maindata", td, Table::New); Table tab(newtab, 1); // Create 3 subtables (in different directories). TableDesc std("", "", TableDesc::Scratch); std.addColumn (ScalarColumnDesc("SourceNr")); SetupNewTable newtab2("tTableKeywords_tmp/submdata", std, Table::New); Table subtab2(newtab2, 2); SetupNewTable newtab3("tTableKeywords_tmp/maindata/subdata", std, Table::New); Table subtab3(newtab3, 3); // Store one subtable as a keyword in the main table, the // other as a column keyword. tab.rwKeywordSet().defineTable ("SubTab2", subtab2); ScalarColumn col(tab, "RowNr"); col.rwKeywordSet().defineTable ("SubTab3", subtab3); // Store another subtable as a keyword in SubTab3. SetupNewTable newtab4("tTableKeywords_tmp/maindata/subdata/sub4", std, Table::New); Table subtab4(newtab4, 4); subtab3.rwKeywordSet().defineTable ("SubTab4", subtab4); } void readTables (const String& name, Bool swap) { // Reconstruct the main table. // Get the sub table from the keyword . Table tab(name, Table::Update); AlwaysAssertExit (tab.nrow() == 1); AlwaysAssertExit (tab.keywordSet().nfields() == 1); AlwaysAssertExit (tab.keywordSet().isDefined ("SubTab2")); Table subtab2 = tab.keywordSet().asTable ("SubTab2"); AlwaysAssertExit (subtab2.nrow() == 2); ScalarColumn col(tab, "RowNr"); Table subtab3 = col.keywordSet().asTable ("SubTab3"); AlwaysAssertExit (subtab3.nrow() == 3); Table subtab4 = subtab3.keywordSet().asTable ("SubTab4"); AlwaysAssertExit (subtab4.nrow() == 4); // Now swap the keyword sets. if (swap) { tab.rwKeywordSet().defineTable ("SubTab2", subtab3); col.rwKeywordSet().defineTable ("SubTab3", subtab2); } } void renameTables (const String& newName, const String& oldName) { Table tab(oldName, Table::Update); tab.rename (newName, Table::New); tab.flush(); // Try to open the table with the old name (should fail). Bool excp = False; try { Table tab1(oldName); } catch (std::exception& x) { excp = True; } AlwaysAssertExit (excp); // Try to open the table with new name (should succeed). Table tab2(newName); } void copyTables (const String& newName, const String& oldName) { Table tab(oldName); tab.copy (newName, Table::New); // Try to open the table with the old name (should succeed). Table tab1(oldName); // Try to open the table with new name (should succeed). Table tab2(newName); } void readFromOtherDir() { { // Reconstruct the main table. // Get the sub table from the keyword . Table tab("main3data"); AlwaysAssertExit (tab.nrow() == 1); AlwaysAssertExit (tab.keywordSet().nfields() == 1); AlwaysAssertExit (tab.keywordSet().isDefined ("SubTab2")); Table subtab3 = tab.keywordSet().asTable ("SubTab2"); AlwaysAssertExit (subtab3.nrow() == 3); ScalarColumn col(tab, "RowNr"); Table subtab2 = col.keywordSet().asTable ("SubTab3"); AlwaysAssertExit (subtab2.nrow() == 2); Table subtab4 = subtab3.keywordSet().asTable ("SubTab4"); AlwaysAssertExit (subtab4.nrow() == 4); } } int main() { try { createTables(); readTables ("tTableKeywords_tmp/maindata", False); renameTables ("tTableKeywords_tmp/main2data", "tTableKeywords_tmp/maindata"); readTables ("tTableKeywords_tmp/main2data", False); copyTables ("tTableKeywords_tmp/main3data", "tTableKeywords_tmp/main2data"); readTables ("tTableKeywords_tmp/main3data", False); readTables ("tTableKeywords_tmp/main2data", False); // Go to the subdirectory to test if renaming and // reading back from there succeeds. AlwaysAssertExit (chdir ("tTableKeywords_tmp") == 0); readTables ("main2data", False); renameTables ("main4data", "main2data"); readTables ("main3data", True); readTables ("main4data", False); readFromOtherDir(); } catch (std::exception& x) { cout << "Caught an exception : " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; } casacore-3.7.1/tables/Tables/test/tTableKeywords.out000066400000000000000000000000031476623553700224540ustar00rootroot00000000000000OK casacore-3.7.1/tables/Tables/test/tTableLock.cc000066400000000000000000000120711476623553700213230ustar00rootroot00000000000000//# tTableLock.cc: Test TableLock class //# Copyright (C) 2001,2002 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include // // Test program for the TableLock class. // void checkLockOption (const TableLock& lock, TableLock::LockOption opt, Bool readLock, Bool permLock) { if (TableLock::lockingDisabled()) { AlwaysAssertExit (lock.option() == TableLock::NoLocking); AlwaysAssertExit (! lock.readLocking()); AlwaysAssertExit (! lock.isPermanent()); } else { AlwaysAssertExit (lock.option() == opt); AlwaysAssertExit (lock.readLocking() == readLock); AlwaysAssertExit (lock.isPermanent() == permLock); } } int main() { { TableLock lock; checkLockOption (lock, TableLock::AutoLocking, True, False); AlwaysAssertExit (lock.interval() == 5); AlwaysAssertExit (lock.maxWait() == 0); } { TableLock lock(TableLock::AutoLocking); checkLockOption (lock, TableLock::AutoLocking, True, False); AlwaysAssertExit (lock.interval() == 5); AlwaysAssertExit (lock.maxWait() == 0); } { TableLock lock(TableLock::AutoNoReadLocking, 10, 1); checkLockOption (lock, TableLock::AutoLocking, False, False); AlwaysAssertExit (lock.interval() == 10); AlwaysAssertExit (lock.maxWait() == 1); } { TableLock lock(TableLock::UserLocking); checkLockOption (lock, TableLock::UserLocking, True, False); } { TableLock lock(TableLock::UserNoReadLocking); checkLockOption (lock, TableLock::UserLocking, False, False); } { TableLock lock(TableLock::PermanentLocking); checkLockOption (lock, TableLock::PermanentLocking, True, True); } { TableLock lock(TableLock::PermanentLockingWait); checkLockOption (lock, TableLock::PermanentLockingWait, True, True); } { TableLock lock1(TableLock::AutoNoReadLocking, 10, 1); TableLock lock2(TableLock::PermanentLockingWait); TableLock lock3(lock2); checkLockOption (lock3, TableLock::PermanentLockingWait, True, True); lock2 = lock1; checkLockOption (lock2, TableLock::AutoLocking, False, False); AlwaysAssertExit (lock2.interval() == 10); AlwaysAssertExit (lock2.maxWait() == 1); } // Test merging. { TableLock lock; lock.merge (TableLock()); checkLockOption (lock, TableLock::AutoLocking, True, False); AlwaysAssertExit (lock.interval() == 5); AlwaysAssertExit (lock.maxWait() == 0); lock.merge (TableLock (TableLock::AutoNoReadLocking, 10, 1)); checkLockOption (lock, TableLock::AutoLocking, True, False); AlwaysAssertExit (lock.interval() == 10); AlwaysAssertExit (lock.maxWait() == 1); } { TableLock lock (TableLock::PermanentLockingWait); lock.merge (TableLock()); checkLockOption (lock, TableLock::PermanentLockingWait, True, True); lock.merge (TableLock(TableLock::AutoNoReadLocking)); checkLockOption (lock, TableLock::PermanentLockingWait, True, True); } { TableLock lock (TableLock::UserLocking); lock.merge (TableLock()); checkLockOption (lock, TableLock::UserLocking, True, False); ////checkLockOption (lock, TableLock::AutoLocking, True, False); lock.merge (TableLock(TableLock::AutoLocking, 20, 2)); checkLockOption (lock, TableLock::AutoLocking, True, False); AlwaysAssertExit (lock.interval() == 20); AlwaysAssertExit (lock.maxWait() == 2); lock.merge (TableLock(TableLock::PermanentLockingWait)); checkLockOption (lock, TableLock::PermanentLockingWait, True, True); lock.merge (TableLock(TableLock::PermanentLocking)); checkLockOption (lock, TableLock::PermanentLocking, True, True); } { TableLock lock (TableLock::NoLocking); lock.merge (TableLock()); checkLockOption (lock, TableLock::NoLocking, False, False); lock.merge (TableLock(TableLock::AutoLocking, 20, 2)); checkLockOption (lock, TableLock::AutoLocking, True, False); } return 0; // exit with success status } casacore-3.7.1/tables/Tables/test/tTableLockSync.cc000066400000000000000000000227131476623553700221640ustar00rootroot00000000000000//# tTableLockSync.cc: Interactive test program for concurrent access to tables //# Copyright (C) 1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Test program for the Table classes // // This program tests concurrent access to tables. // Create the table. void a() { // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.addColumn (ScalarColumnDesc("col1")); td.addColumn (ScalarColumnDesc("col2")); td.addColumn (ScalarColumnDesc("col3")); td.addColumn (ScalarColumnDesc("cols")); td.addColumn (ArrayColumnDesc ("Pol", IPosition(1,16), ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc ("Freq", 1, ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc ("Data", 2, ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc ("Data2", 2, ColumnDesc::FixedShape)); td.defineHypercolumn ("TSMExample", 2, stringToVector ("Data"), stringToVector ("Pol,Freq")); td.defineHypercolumn ("TSMExample2", 3, stringToVector ("Data2")); td.rwKeywordSet().define ("k0", Int(0)); // Now create a new table from the description. SetupNewTable newtab("tTableLockSync_tmp.tab", td, Table::New); StManAipsIO sm1; StandardStMan sm2(128); IncrementalStMan sm3; TiledCellStMan sm4 ("TSMExample", IPosition(2,5,6)); TiledShapeStMan sm5 ("TSMExample2", IPosition(2,5,6)); newtab.setShapeColumn ("Freq", IPosition(1,25)); newtab.setShapeColumn ("Data", IPosition(2,16,25)); newtab.setShapeColumn ("Data2", IPosition(2,16,25)); newtab.bindAll (sm4); newtab.bindColumn ("col1", sm1); newtab.bindColumn ("col2", sm2); newtab.bindColumn ("cols", sm2); newtab.bindColumn ("col3", sm3); newtab.bindColumn ("Data2", sm5); Table tab(newtab, 1); } void b (Bool noReadLocking, Bool permLocking) { // Open the table for update with UserLocking. TableLock lt(TableLock::UserLocking); if (permLocking) { lt = TableLock::PermanentLocking; } else if (noReadLocking) { lt = TableLock::UserNoReadLocking; } Table tab("tTableLockSync_tmp.tab", lt, Table::Update); try { TableLocker lock1 (tab, FileLocker::Write, 1); } catch (std::exception& x) { cout << "table is write-locked" << endl; } ScalarColumn col1 (tab, "col1"); ScalarColumn col2 (tab, "col2"); ScalarColumn col3 (tab, "col3"); ScalarColumn cols (tab, "cols"); ArrayColumn freq (tab, "Freq"); ArrayColumn pol (tab, "Pol"); ArrayColumn data (tab, "Data"); ArrayColumn data2 (tab, "Data2"); Vector freqValues(25); Vector polValues(16); Matrix dataValues(IPosition(2,16,25)); Matrix data2Values(IPosition(2,16,25)); Int opt, rownr, val; while (True) { cout << "0=quit, 1=quit/delete, 2=rdlock, 3=rdlockw, 4=wrlock, 5=wrlockw, 6=unlock" << endl; cout << "7=status, 8=get, 9=put, 10=rdkey, 11=wrkey, 12=flush, 13=resync" << endl; cout << "14=hasChanged: "; cin >> opt; if (opt <= 1) { break; } else if (opt == 2) { if (! tab.lock (False, 1)) { cout << "Could not acquire a read lock" << endl; } } else if (opt == 3) { if (! tab.lock (False, 0)) { cout << "Could not acquire a read lock" << endl; } } else if (opt == 4) { if (! tab.lock (True, 1)) { cout << "Could not acquire a write lock" << endl; } } else if (opt == 5) { if (! tab.lock (True, 0)) { cout << "Could not acquire a write lock" << endl; } } else if (opt == 6) { tab.unlock(); } else if (opt == 7) { cout << " hasReadLock=" << tab.hasLock (FileLocker::Read) << endl; cout << " hasWriteLock=" << tab.hasLock (FileLocker::Write) << endl; cout << " isMultiUsed = " << tab.isMultiUsed() << endl; } else if (opt == 12) { tab.flush(); } else if (opt == 13) { tab.resync(); cout << "nrows=" << tab.nrow() << endl; } else if (opt == 14) { cout << "hasDataChanged = " << tab.hasDataChanged() << endl; } else { if (opt == 8 || opt == 9) { // First test if get or put is possible (using row 0). Bool err = False; try { col1.get (0, val); if (opt == 9) { col1.put (0, val); } } catch (std::exception& x) { cout << x.what() << endl; err = True; } if (!err) { cout << "rownr: "; cin >> rownr; if (opt == 9) { cout << "value: "; cin >> val; if (rownr >= Int(tab.nrow())) { Int n = 1 + rownr - tab.nrow(); tab.addRow (n); cout << "added " << n << " rows" << endl; } col1.put (rownr, val); col2.put (rownr, val+1); col3.put (rownr, val+2); cols.put (rownr, "ARatherLongTestString" + String::toString(val)); indgen (freqValues, float(val+2)); indgen (polValues, float(val+3)); indgen (dataValues, float(val+4)); indgen (data2Values, float(val+5)); data.put (rownr, dataValues); freq.put (rownr, freqValues); pol.put (rownr, polValues); data2.put (rownr, data2Values); }else{ if (rownr >= Int(tab.nrow())) { cout << "Only " << tab.nrow() << " rows in table" << endl; }else{ cout << "Row " << rownr << " has value " << col1(rownr) << ' ' << col2(rownr) << ' ' << col3(rownr) << ' ' << cols(rownr) << ' ' << freq(rownr)(IPosition(1,0)) << '-' << freq(rownr)(IPosition(1,24)) - 24 << ' ' << pol(rownr)(IPosition(1,0)) << '-' << pol(rownr)(IPosition(1,15)) - 15 << ' ' << data(rownr)(IPosition(2,0,0)) << '-' << data(rownr)(IPosition(2,15,24)) - 399 << ' ' << data2(rownr)(IPosition(2,0,0)) << '-' << data2(rownr)(IPosition(2,15,24)) - 399 << endl; } } } } else { // First test if get or put is possible (using key k0). Bool err = False; try { val = tab.keywordSet().asInt ("k0"); if (opt == 11) { tab.rwKeywordSet().define ("k0", val); } } catch (std::exception& x) { cout << x.what() << endl; err = True; } if (!err) { cout << "keyword name: "; String name; cin >> name; if (opt == 10) { if (! tab.keywordSet().isDefined (name)) { cout << "Keyword " << name << " does not exist" << endl; } else { cout << tab.keywordSet().asInt (name) << endl; } } else { cout << "value: "; cin >> val; tab.rwKeywordSet().define(name, val); } } } } } if (opt == 1) { Table tabd ("tTableLockSync_tmp.tab", Table::Delete); } } int main (int argc, const char* argv[]) { if (argc < 2) { cout << "Execute as: tTableLockSync 1 x to create new table" << endl; cout << " tTableLockSync 0 x to update existing table" << endl; cout << "where x=1 means NoReadLocking and x=2 means PermanentLocking" << endl; }else{ try { Bool noReadLocking = False; Bool permLocking = False; if (argc >= 3) { if (*(argv[2]) == '1') { noReadLocking = True; } else if (*(argv[2]) == '2') { permLocking = True; } } if (*(argv[1]) == '1') { a(); } b (noReadLocking, permLocking); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } } return 0; // exit with success status } casacore-3.7.1/tables/Tables/test/tTableLockSync_2.cc000066400000000000000000000263071476623553700224100ustar00rootroot00000000000000//# tTableLockSync.cc: Test program for concurrent access to tables //# Copyright (C) 1997,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Test program for the Table classes // // This program tests concurrent access to tables. // It is doing this by running the program simultaneously in different // processes and with different options. // It can be run as follows: // tTableLockSync_2 option inspection_interval wait_period get_put nrrow/wait // option 1=Permanent 2=PermanentWait 3=Auto 4=User // inspection_interval only meaningful for Auto (in seconds) // wait_period period to sleep before next get or put // get_put 0=get 1=put // nrrow #rows to put void tlock (Table& tab, Bool write, Bool show) { if (show) { Time time; double sec = time.modifiedJulianDay() * 86400; cout << time << ' ' << sec - floor(sec) << ": "; cout << "Starting to lock" << endl; } tab.lock (write); if (show) { Time time1; double sec1 = time1.modifiedJulianDay() * 86400; cout << time1 << ' ' << sec1 - floor(sec1) << ": "; cout << "Lock acquired" << endl; } } void tunlock (Table& tab, Bool show) { if (show) { Time time; double sec = time.modifiedJulianDay() * 86400; cout << time << ' ' << sec - floor(sec) << ": "; cout << "Starting to unlock" << endl; } tab.unlock(); if (show) { Time time1; double sec1 = time1.modifiedJulianDay() * 86400; cout << time1 << ' ' << sec1 - floor(sec1) << ": "; cout << "Unlock ended" << endl; } } // Create the table. void a() { // Check if the table exists. // If not, create it. if (Table::isReadable ("tTableLockSync_2_tmp.tab")) { return; } // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.addColumn (ScalarColumnDesc("seq")); td.addColumn (ScalarColumnDesc("col1")); td.addColumn (ScalarColumnDesc("col2")); td.addColumn (ArrayColumnDesc ("Pol", IPosition(1,16), ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc ("Freq", 1, ColumnDesc::FixedShape)); td.addColumn (ArrayColumnDesc ("Data", 2, ColumnDesc::FixedShape)); td.defineHypercolumn ("TSMExample", // 2, 3, stringToVector ("Data"), // stringToVector ("Pol,Freq")); stringToVector ("Pol,Freq,seq")); td.rwKeywordSet().define ("seqnr", uInt(0)); // Now create a new table from the description. SetupNewTable newtab("tTableLockSync_2_tmp.tab", td, Table::New); StManAipsIO sm1; IncrementalStMan sm2; // TiledCellStMan sm3 ("TSMExample", IPosition(2,5,6)); TiledColumnStMan sm3 ("TSMExample", IPosition(3,5,6,1)); newtab.setShapeColumn ("Freq", IPosition(1,25)); newtab.setShapeColumn ("Data", IPosition(2,16,25)); newtab.bindAll (sm3); // newtab.bindColumn ("seq", sm1); newtab.bindColumn ("col1", sm1); // newtab.bindColumn ("seq", sm2); // newtab.bindColumn ("col1", sm2); newtab.bindColumn ("col2", sm2); Table tab(newtab); } void b (const TableLock& lockMode, uInt wait, uInt nrrow, Bool show) { // Check if user locking. Bool userLocking = (lockMode.option() == TableLock::UserLocking); // Open the table for update. Table tab ("tTableLockSync_2_tmp.tab", lockMode, Table::Update); ScalarColumn seq (tab, "seq"); ScalarColumn col1 (tab, "col1"); ScalarColumn col2 (tab, "col2"); ArrayColumn freq (tab, "Freq"); ArrayColumn pol (tab, "Pol"); ArrayColumn data (tab, "Data"); // Get and update the sequencenumber. if (userLocking) tlock(tab, True, show); TableRecord& keyset = tab.rwKeywordSet(); uInt seqnr = keyset.asuInt ("seqnr"); seqnr++; keyset.define ("seqnr", seqnr); if (userLocking) tunlock (tab, show); Vector freqValues(25); Vector polValues(16); Matrix dataValues(IPosition(2,16,25)); Int rownr, val; for (uInt i=0; i 0) { sleep (wait); } } } void c (const TableLock& lockMode, uInt wait, uInt lastWait, Bool show) { // Check if user locking. Bool userLocking = (lockMode.option() == TableLock::UserLocking); // Open the table for read. Table tab ("tTableLockSync_2_tmp.tab", lockMode); ScalarColumn seq (tab, "seq"); ScalarColumn col1 (tab, "col1"); ScalarColumn col2 (tab, "col2"); ArrayColumn freq (tab, "Freq"); ArrayColumn pol (tab, "Pol"); ArrayColumn data (tab, "Data"); Block count; Time* lastTime = 0; Vector freqValues; Vector polValues; Matrix dataValues; Int val; uInt oldNrrow = 0; uInt nrrow = 0; while (True) { if (userLocking) tlock (tab, False, show); nrrow = tab.nrow(); for (uInt rownr=oldNrrow; rownr= Int(nr)) { count.resize (result+1); for (Int i=nr; i<=result; i++) { count[i] = 0; } } count[result]++; } if (userLocking) tunlock (tab, show); if (wait > 0) { sleep (wait); } // When no more rows in last wait, stop the program. if (nrrow == oldNrrow) { if (lastTime != 0) { if (lastTime->age() > lastWait) { break; } }else{ lastTime = new Time(); } }else{ delete lastTime; lastTime = 0; oldNrrow = nrrow; } } delete lastTime; cout << "seqnr\t#rows" << endl; uInt nrread = 0; for (uInt i=0; i 0) { cout << i << '\t' << count[i] << endl; nrread += count[i]; } } cout << "total\t" << nrread << " rows read" << endl; if (nrread != nrrow) { cout << " *** but nrrow = " << nrrow << " in table" << endl; } } int main (int argc, const char* argv[]) { if (argc < 6) { cout << "Execute as:" << endl; cout << " tTableLockSync_2 option inspection_interval " "wait_period get_put nrrow/lastWait show" << endl; cout << " option 1=Permanent 2=PermanentWait 3=Auto 4=User" << endl; cout << " get_put 0=get else=put" << endl; cout << " When putting, last parameter is #rows to put" << endl; cout << " When getting, last parameter is 'no change' period" " to stop" << endl; return 0; } Bool show = (argc > 6); uInt var[5]; for (uInt i=0; i<5; i++) { istringstream str(argv[i+1]); str >> var[i]; } // Determine the correct locking mode. TableLock lockMode (TableLock::UserLocking); if (var[0] == 1) { lockMode = TableLock(TableLock::PermanentLocking); } else if (var[0] == 2) { lockMode = TableLock(TableLock::PermanentLockingWait); } else if (var[0] == 3) { lockMode = TableLock(TableLock::AutoLocking, var[1]); } Bool getsw = (var[3] == 0); try { if (!getsw) { a(); b (lockMode, var[2], var[4], show); }else{ c (lockMode, var[2], var[4], show); } } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } casacore-3.7.1/tables/Tables/test/tTableRecord.cc000066400000000000000000001062341476623553700216560ustar00rootroot00000000000000//# tTableRecord.cc: Test the TableRecord class //# Copyright (C) 1995,1996,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // This program tests the TableRecord and TableRecordRep classes. // Its expected output is stored in tTableRecord.out and can be checked // using assay. This will also delete the temporary output file. //

        // It can throw several exceptions, which may result in memory leaks. // To check if no real memory leaks occur, the program can be run // with an argument (e.g. tTableRecord 1). In that case no statements // resulting in exceptions are executed, so no memory leaks should occur. //

        // The ability to read old KeywordSet objects from a file has been tested // in a separate program. That program is not checked into the system, // because the KeywordSet classes are removed from it. void check (const TableRecord&, Int intValue, uInt nrField); // This function checks if a field name is correct. // A name has to be > 0 characters and start with an uppercase. // The extra argument should not be 10. Bool nameCallBack (const String& name, DataType, const void* extraArgument, String& message) { if (name.length() < 1) { message = "length<1"; return False; } if (name[0] < 'A' || name[0] > 'Z') { message = "no uppercase"; return False; } if (extraArgument != 0 && *(const Int*)extraArgument == 10) { message = "extra==10"; return False; } return True; } // This function is doing define's and assign's in several ways. // Many of them are incorrect and should result in an exception. // TpArrayString2 has a fixed shape, while TpArrayString3's shape is variable. // Note that assign always requires a matching shape, while a define // only requires a matching shape for a fixed shape. // It checks if the value is correct after each define/assign. void doDefineAssign (const TableRecord& inrecord) { TableRecord record (inrecord); RecordFieldPtr > rfstr2 (record, "TpArrayString2"); RecordFieldPtr > rfstr3 (record, "TpArrayString3"); Vector vec; record.get (record.fieldNumber ("TpArrayString2"), vec); AlwaysAssertExit (allEQ (vec, stringToVector ("abcd,ghi,jklmn"))); record.get (record.fieldNumber ("TpArrayString3"), vec); AlwaysAssertExit (allEQ (vec, stringToVector ("abc,dghij,klmn"))); try { *rfstr2 = stringToVector ("abc"); } catch (std::exception& x) { cout << x.what() << endl; // incorrect shape } try { *rfstr3 = stringToVector ("abc"); } catch (std::exception& x) { cout << x.what() << endl; // incorrect shape } record.get (record.fieldNumber ("TpArrayString2"), vec); AlwaysAssertExit (allEQ (vec, stringToVector ("abcd,ghi,jklmn"))); record.get (record.fieldNumber ("TpArrayString3"), vec); AlwaysAssertExit (allEQ (vec, stringToVector ("abc,dghij,klmn"))); try { *rfstr2 = stringToVector ("abc"); } catch (std::exception& x) { cout << x.what() << endl; // incorrect shape } try { *rfstr3 = stringToVector ("abc"); } catch (std::exception& x) { cout << x.what() << endl; // incorrect shape } record.get (record.fieldNumber ("TpArrayString2"), vec); AlwaysAssertExit (allEQ (vec, stringToVector ("abcd,ghi,jklmn"))); record.get (record.fieldNumber ("TpArrayString3"), vec); AlwaysAssertExit (allEQ (vec, stringToVector ("abc,dghij,klmn"))); try { rfstr2.define (stringToVector ("abc")); } catch (std::exception& x) { cout << x.what() << endl; // incorrect shape } rfstr3.define (stringToVector ("a")); record.get (record.fieldNumber ("TpArrayString2"), vec); AlwaysAssertExit (allEQ (vec, stringToVector ("abcd,ghi,jklmn"))); vec.resize (1); record.get (record.fieldNumber ("TpArrayString3"), vec); AlwaysAssertExit (allEQ (vec, stringToVector ("a"))); *rfstr2 = stringToVector ("a,b,c"); *rfstr3 = stringToVector ("d"); record.get (record.fieldNumber ("TpArrayString3"), vec); AlwaysAssertExit (allEQ (vec, stringToVector ("d"))); vec.resize (3); record.get (record.fieldNumber ("TpArrayString2"), vec); AlwaysAssertExit (allEQ (vec, stringToVector ("a,b,c"))); rfstr2.define (stringToVector ("g,h,i")); record.define (record.fieldNumber ("TpArrayString3"), stringToVector ("j,k,l")); record.get (record.fieldNumber ("TpArrayString2"), vec); AlwaysAssertExit (allEQ (vec, stringToVector ("g,h,i"))); record.get (record.fieldNumber ("TpArrayString3"), vec); AlwaysAssertExit (allEQ (vec, stringToVector ("j,k,l"))); try { record.define ("TpBool", Vector(2, False)); } catch (std::exception& x) { cout << x.what() << endl; } } void doSubRecord (Bool doExcp, const RecordDesc& desc) { Int subField = desc.fieldNumber ("SubRecord"); Int subField1 = desc.fieldNumber ("SubRecord1"); desc.subRecord (subField); TableRecord record(desc); RecordFieldPtr sub (record, subField); RecordFieldPtr sub1 (record, subField1); AlwaysAssertExit (! (*sub).conform (*sub1)); // Add 2 fields, so now they are conforming. (*sub1).define ("f1", float(3)); (*sub1).define ("i1", Int(2)); AlwaysAssertExit ((*sub).conform (*sub1)); // Create a copy of the record description and add the 2 fields // to SubRecord1. RecordDesc desc1(desc); RecordDesc& subDesc1 = desc1.rwSubRecord (subField1); subDesc1.addField ("fa", TpFloat); subDesc1.addField ("ia", TpInt); // Now create a record from that new description. // record conforms record1 and vice versa (because SubRecord1 // is variable in record, but fixed in record1). TableRecord record1(desc1); AlwaysAssertExit (record.conform (record1)); AlwaysAssertExit (record1.conform (record)); // Test if the assignment works fine. record1 = record; AlwaysAssertExit (record1.subRecord(subField1).asfloat(0) == 3); AlwaysAssertExit (record1.subRecord(subField1).asInt(1) == 2); // Add another field to SubRecord1 in record. // This results in record1 not conforming record. // record still conforms record1, because its SubRecord1 is non-fixed. (*sub1).define ("i2", Int(2)); AlwaysAssertExit (record.conform (record1)); AlwaysAssertExit (! record1.conform (record)); if (doExcp) { try { record1 = record; } catch (std::exception& x) { cout << ">>> Instance-specific assertion error message:" << endl; cout << x.what() << endl; // not conforming cout << "<<<" << endl; } } (*sub1).define (0, float(4)); AlwaysAssertExit (record.subRecord(subField1).asfloat(0) == 4); record = record1; AlwaysAssertExit (record.subRecord(subField1).asfloat(0) == 3); // Print the record. cout << record; // Convert to and from a Record. Record srec = record.toRecord(); TableRecord trec; trec.fromRecord (srec); cout << trec; } void doIt (Bool doExcp) { // Create a record description with all types. Int extraArgument=0; RecordDesc rd; rd.addField ("TpBool", TpBool); rd.setComment (0, "comment for field TpBool"); rd.addField ("TpUChar", TpUChar); rd.addField ("TpShort", TpShort); rd.addField ("TpInt", TpInt); rd.addField ("TpUInt", TpUInt); rd.addField ("TpFloat", TpFloat); rd.addField ("TpDouble", TpDouble); rd.addField ("TpComplex", TpComplex); rd.addField ("TpDComplex", TpDComplex); rd.addField ("TpString", TpString); rd.addField ("TpArrayBool", TpArrayBool, IPosition(1,1)); rd.addField ("TpArrayUChar", TpArrayUChar, IPosition(1,1)); rd.addField ("TpArrayShort", TpArrayShort, IPosition(1,1)); rd.addField ("TpArrayInt", TpArrayInt, IPosition(1,1)); rd.addField ("TpArrayUInt", TpArrayUInt, IPosition(1,1)); rd.addField ("TpArrayFloat", TpArrayFloat, IPosition(1,1)); rd.addField ("TpArrayDouble", TpArrayDouble, IPosition(1,1)); rd.addField ("TpArrayComplex", TpArrayComplex, IPosition(1,1)); rd.addField ("TpArrayDComplex", TpArrayDComplex, IPosition(1,1)); rd.addField ("TpArrayString", TpArrayString); RecordDesc subDesc; rd.addField ("SubRecord1", subDesc); // empty, thus arbitrary subrecord subDesc.addField ("SubFloat", TpFloat); subDesc.addField ("SubInt", TpInt); rd.addField ("SubRecord", subDesc); // not empty, thus fixed subrecord // TableRecord(const RecordDesc &description, ...); // uInt nfields() const; // const RecordDesc &description() const TableRecord record(rd, RecordInterface::Variable, nameCallBack, &extraArgument); AlwaysAssertExit (record.comment ("TpBool") == "comment for field TpBool"); record.setComment ("TpBool", "comment for TpBool"); AlwaysAssertExit (record.comment ("TpBool") == "comment for TpBool"); AlwaysAssertExit(record.nfields() == rd.nfields() && record.nfields() == 22); AlwaysAssertExit(record.description() == rd); // Test renameField. record.renameField ("newname", "TpInt"); AlwaysAssertExit (! record.isDefined ("TpInt")); AlwaysAssertExit (record.fieldNumber ("newname") == 3); record.renameField ("TpInt", "newname"); // Do some incorrect addField's. if (doExcp) { try { record.define ("", (Int)0); } catch (std::exception& x) { cout << x.what() << endl; // empty name } try { record.define ("aB", (Int)0); } catch (std::exception& x) { cout << x.what() << endl; // first no uppercase } extraArgument = 10; try { record.define ("A", (Int)0); } catch (std::exception& x) { cout << x.what() << endl; // extra argument = 10 } extraArgument = 0; try { record.define ("TpShort", (Int)0); } catch (std::exception& x) { cout << x.what() << endl; // invalid type } } AlwaysAssertExit(record.nfields() == rd.nfields() && record.nfields() == 22); AlwaysAssertExit(record.description() == rd); // void define (const String& name, value); // void define (const String& name, value, Bool fixedShape); record.define ("TpBool2", False); rd.addField ("TpBool2a", TpBool); record.define ("TpUChar2", uChar(1)); rd.addField ("TpUChar2a", TpUChar); record.define ("TpShort2", Short(2)); rd.addField ("TpShort2a", TpShort); record.define ("TpInt2", Int(3)); rd.addField ("TpInt2a", TpInt); record.define ("TpUInt2", uInt(4)); rd.addField ("TpUInt2a", TpUInt); record.define ("TpFloat2", Float(5)); rd.addField ("TpFloat2a", TpFloat); record.define ("TpDouble2", Double(6)); rd.addField ("TpDouble2a", TpDouble); record.define ("TpComplex2", Complex(7,8)); rd.addField ("TpComplex2a", TpComplex); record.define ("TpDComplex2", DComplex(9,10)); rd.addField ("TpDComplex2a", TpDComplex); record.define ("TpArrayString2", stringToVector("abcd,ghi,jklmn"), True); rd.addField ("TpArrayString2a", TpArrayString, IPosition(1,3)); record.define ("TpArrayString3", stringToVector("abc,dghij,klmn")); rd.addField ("TpArrayString3a", TpArrayString); record.define ("TpString2", "abc"); rd.addField ("TpString2a", TpString); // Define a scalar using an array. AlwaysAssertExit (record.asInt("TpInt2") == 3); AlwaysAssertExit (record.asuInt("TpUInt2") == 4); AlwaysAssertExit (allEQ (record.asArrayuInt("TpUInt2"), uInt(4))); record.define ("TpInt2", Vector(1,6)); AlwaysAssertExit (record.asInt("TpInt2") == 6); AlwaysAssertExit (allEQ (record.asArrayInt("TpInt2"), 6)); record.define ("TpUInt2", uInt(10)); AlwaysAssertExit (record.asuInt("TpUInt2") == 10); AlwaysAssertExit (allEQ (record.asArrayuInt("TpUInt2"), uInt(10))); record.define ("TpInt2", 3); record.define ("TpUInt2", Vector(1,4u)); AlwaysAssertExit (record.asInt("TpInt2") == 3); AlwaysAssertExit (allEQ (record.asArrayInt("TpInt2"), 3)); AlwaysAssertExit (record.asuInt("TpUInt2") == 4); AlwaysAssertExit (allEQ (record.asArrayuInt("TpUInt2"), uInt(4))); // Do some erroneous defines and assigns. if (doExcp) { doDefineAssign (record); } // TableRecord(); // void restructure(const RecordDesc &newDescription); // Also check that a RecordFieldPtr gets detached. TableRecord record2; record2.restructure(subDesc); // non-fixed -> possible RecordFieldPtr fld2(record2, 1); AlwaysAssertExit (fld2.isAttached()); record2.restructure(subDesc); // non-fixed -> possible and detaches // restructure and operator= fail on a non-empty, fixed record. TableRecord record2a(RecordInterface::Fixed); if (doExcp) { try { record2a.restructure(subDesc); } catch (std::exception& x) { cout << x.what() << endl; // fixed, not empty ->impossible } } record2 = record; // non-fixed -> possible record2a = record; // same structure -> possible TableRecord record2b (subDesc); if (doExcp) { try { record2a = record2b; } catch (std::exception& x) { cout << ">>> Instance-specific assertion error message:" << endl; cout << x.what() << endl; // fixed; non-conforming cout << "<<<" << endl; } } // TableRecord(const TableRecord &other); // Bool conform(const TableRecord &other); TableRecord record3(record2); TableRecord record4(record2.description()); record4 = record3; AlwaysAssertExit(rd == record2.description() && rd == record3.description() && rd == record4.description()); AlwaysAssertExit(record.conform(record2)); AlwaysAssertExit(record.conform(record2a)); TableRecord record5; AlwaysAssertExit(! record.conform(record5)); // Scalar fields RecordFieldPtr boolField(record, 0); RecordFieldPtr ucharField(record, 1); RecordFieldPtr shortField(record, 2); RecordFieldPtr intField(record, 3); RecordFieldPtr uintField(record, 4); RecordFieldPtr floatField(record, 5); RecordFieldPtr doubleField(record, 6); RecordFieldPtr complexField(record, 7); RecordFieldPtr dcomplexField(record, 8); RecordFieldPtr stringField(record, 9); // RecordFieldPtr(TableRecord &record, uInt whichField); // T &operator*() // const T &operator*() const // define (const T& value) *boolField = True; *ucharField = 255; AlwaysAssertExit(*((const RecordFieldPtr &)ucharField) == 255); *shortField = 32767; *intField = -1234567; uintField.define (1234567); *floatField = 7.0f; *doubleField = 9.0; *complexField = Complex(1.0f, 11.0f); *dcomplexField = Complex(5.0, 1.0); *stringField = "Hello"; // Array fields RecordFieldPtr > arrayboolField(record, 10); RecordFieldPtr > arrayucharField(record, 11); RecordFieldPtr > arrayshortField(record, 12); RecordFieldPtr > arrayintField(record, 13); RecordFieldPtr > arrayuintField(record, 14); RecordFieldPtr > arrayfloatField(record, 15); RecordFieldPtr > arraydoubleField(record, 16); RecordFieldPtr > arraycomplexField(record, 17); RecordFieldPtr > arraydcomplexField(record, 18); RecordFieldPtr > arraystringField(record, 19); arrayboolField.setComment ("comment for TpArrayBool"); *arrayboolField = True; *arrayucharField = 255; *arrayshortField = 32767; *arrayintField = -1234567; *arrayuintField = 1234567; *arrayfloatField = 7.0f; *arraydoubleField = 9.0; *arraycomplexField = Complex(1.0f, 11.0f); *arraydcomplexField = DComplex(5.0, 1.0); *arraystringField = stringToVector ("Hello,Goodbye"); // Sub-record fields if (doExcp) { try { RecordFieldPtr fld(record, "SubRecord"); } catch (std::exception& x) { cout << x.what() << endl; // invalid type } } RecordFieldPtr recordField(record, "SubRecord"); TableRecord& subrec = *recordField; AlwaysAssertExit(subrec.description() == subDesc); RecordFieldPtr subref (subrec, 0); *subref = 9.0; // TableRecord& rwSubRecord (Int whichField); // defineRecordField (const String& name, const TableRecord&); // RecordFieldPtr::define (const TableRecord&); // RecordFieldPtr::operator= (const TableRecord&); TableRecord& subrec1 = record.rwSubRecord (record.fieldNumber ("SubRecord1")); TableRecord subrec1a; subrec1.defineRecord ("sub", subrec, RecordInterface::Fixed); subrec1.defineRecord ("sub1", subrec1a); subrec1.defineRecord ("sub2", subrec1a); RecordFieldPtr sub1 (subrec1, 1); RecordFieldPtr sub2 (subrec1, 2); *subref = 6.0; sub1.define (subrec); *subref = 8.0; *sub2 = subrec; // Check if the entire record is correct. check (record, -1234567, 34); // Now make a copy of the record and assign a value via RecordFieldPtr. // This has to result in a copy(-on-write) operation. TableRecord savrec2(record); check (savrec2, -1234567, 34); *intField += 1; *arrayintField = -1234566; check (savrec2, -1234567, 34); check (record, -1234566, 34); savrec2 = record; check (savrec2, -1234566, 34); // Change some more fields and check if the original is kept intact // (thus if copy-on-write works fine). This also checks if // reacquiring the RecordFieldPtr pointers after a copy works fine. TableRecord savrec2a(savrec2); RecordFieldPtr savrf (savrec2, 3); RecordFieldPtr > savrfarray (savrec2, 13); savrf.define (savrf.get() + 11); *savrfarray = *savrf; check (savrec2, -1234555, 34); check (savrec2a, -1234566, 34); check (record, -1234566, 34); // Add some fields. // Check if removing a field results in deattaching and in // decrementing the field number. record.define ("TpString3", "abcd"); record.define ("TpString4", "efghij"); check (record, -1234566, 36); TableRecord savrec3(record); check (savrec3, -1234566, 36); RecordFieldPtr tpstring2 (record, "TpString2"); RecordFieldPtr tpstring3 (record, "TpString3"); RecordFieldPtr tpstring4 (record, "TpString4"); record.removeField (record.fieldNumber("TpString3")); AlwaysAssertExit (tpstring2.isAttached()); AlwaysAssertExit (tpstring4.isAttached()); AlwaysAssertExit (tpstring2.fieldNumber() == 33); record.removeField (record.fieldNumber("TpString4")); check (savrec3, -1234566, 36); check (record, -1234566, 34); // OK, we've tested the TableRecord members, now test the remaining // RecordFieldPtr members. // RecordFieldPtr(); // void attachToRecord(TableRecord &record, uInt whichField); // virtual Bool isAttached() RecordFieldPtr ucharField2; AlwaysAssertExit(! ucharField2.isAttached()); ucharField2.attachToRecord(record, 1); AlwaysAssertExit(*ucharField2 == *ucharField && ucharField2.isAttached()); *ucharField = 99; AlwaysAssertExit(*ucharField2 == 99); // RecordFieldPtr(const RecordFieldPtr &other); RecordFieldPtr ucharField3(ucharField); AlwaysAssertExit(*ucharField3 == *ucharField2 && ucharField3.isAttached()); // RecordFieldPtr &operator=(const RecordFieldPtr &other); RecordFieldPtr ucharField4; ucharField4 = ucharField; AlwaysAssertExit(*ucharField4 == *ucharField3 && ucharField4.isAttached()); *ucharField4 = 44; AlwaysAssertExit(*ucharField == 44); // void detach(); ucharField4.detach(); AlwaysAssertExit(! ucharField4.isAttached()); RecordDesc rd2; rd2.addField("foo", TpInt); TableRecord *record6 = new TableRecord(rd2); RecordFieldPtr(*record6, 0); delete record6; // Check subRecord conformance. doSubRecord (doExcp, rd); *ucharField= 255; AipsIO aos ("tTableRecord_tmp.data", ByteIO::New); aos << record; aos.close(); aos.open ("tTableRecord_tmp.data"); aos >> record5; AlwaysAssertExit(record5.conform(record)); check (record5, -1234566, 34); // Check removing a sub record. record5.defineRecord ("abcd", record); check (record5, -1234566, 35); record5.removeField (record5.fieldNumber("abcd")); check (record5, -1234566, 34); // ~TableRecord() // implicit // ~RecordFieldPtr(); // implicit } // Check if the values in the record and subrecord are correct. // The number of fields and the value of the Int fields can vary, // so they are given as arguments. void check (const TableRecord& record, Int intValue, uInt nrField) { AlwaysAssertExit (record.nfields() == nrField); RORecordFieldPtr boolField(record, 0); RORecordFieldPtr ucharField(record, 1); RORecordFieldPtr shortField(record, 2); RORecordFieldPtr intField(record, 3); RORecordFieldPtr uintField(record, 4); RORecordFieldPtr floatField(record, 5); RORecordFieldPtr doubleField(record, 6); RORecordFieldPtr complexField(record, 7); RORecordFieldPtr dcomplexField(record, 8); RORecordFieldPtr stringField(record, 9); // RORecordFieldPtr(TableRecord &record, uInt whichField); // const T &operator*() const {return *field_ptr_p;} AlwaysAssertExit(boolField.comment() == "comment for TpBool"); AlwaysAssertExit(*boolField == True); AlwaysAssertExit(*ucharField == 255); AlwaysAssertExit(*shortField == 32767); AlwaysAssertExit(intField.get() == intValue); AlwaysAssertExit(uintField.get() == 1234567); AlwaysAssertExit(*floatField == 7.0f); AlwaysAssertExit(*doubleField == 9.0); AlwaysAssertExit(*complexField == Complex(1.0f, 11.0f)); AlwaysAssertExit(*dcomplexField == DComplex(5.0, 1.0)); AlwaysAssertExit(*stringField == "Hello"); Bool bv; uChar ucv; Short sv; Int iv; uInt uiv; Float fv; Double dv; Complex cv; DComplex dcv; String strv; // TableRecord::get (T& value) const; record.get (22, bv); record.get (23, ucv); record.get (24, sv); record.get (25, iv); record.get (26, uiv); record.get (27, fv); record.get (28, dv); record.get (29, cv); record.get (30, dcv); record.get (33, strv); AlwaysAssertExit(bv == False); AlwaysAssertExit(ucv == 1); AlwaysAssertExit(sv == 2); AlwaysAssertExit(iv == 3); AlwaysAssertExit(uiv == 4); AlwaysAssertExit(fv == 5); AlwaysAssertExit(dv == 6); AlwaysAssertExit(cv == Complex(7,8)); AlwaysAssertExit(dcv == DComplex(9,10)); AlwaysAssertExit(strv == "abc"); AlwaysAssertExit (allEQ (record.asArrayBool(22), bv)); AlwaysAssertExit (allEQ (record.asArrayuChar(23), ucv)); AlwaysAssertExit (allEQ (record.asArrayShort(24), sv)); AlwaysAssertExit (allEQ (record.asArrayInt(25), iv)); AlwaysAssertExit (allEQ (record.asArrayuInt(26), uiv)); AlwaysAssertExit (allEQ (record.asArrayfloat(27), fv)); AlwaysAssertExit (allEQ (record.asArraydouble(28), dv)); AlwaysAssertExit (allEQ (record.asArrayComplex(29), cv)); AlwaysAssertExit (allEQ (record.asArrayDComplex(30), dcv)); AlwaysAssertExit (allEQ (record.asArrayString(33), strv)); // Scalars as Arrays. RORecordFieldPtr > boolFieldA(record, 0); RORecordFieldPtr > ucharFieldA(record, 1); RORecordFieldPtr > shortFieldA(record, 2); RORecordFieldPtr > intFieldA(record, 3); RORecordFieldPtr > uintFieldA(record, 4); RORecordFieldPtr > floatFieldA(record, 5); RORecordFieldPtr > doubleFieldA(record, 6); RORecordFieldPtr > complexFieldA(record, 7); RORecordFieldPtr > dcomplexFieldA(record, 8); RORecordFieldPtr > stringFieldA(record, 9); AlwaysAssertExit (allEQ (*boolFieldA, Vector(1, *boolField))); AlwaysAssertExit (allEQ (*ucharFieldA, Vector(1, *ucharField))); AlwaysAssertExit (allEQ (*shortFieldA, Vector(1, *shortField))); AlwaysAssertExit (allEQ (*intFieldA, Vector(1, *intField))); AlwaysAssertExit (allEQ (*uintFieldA, Vector(1, *uintField))); AlwaysAssertExit (allEQ (*floatFieldA, Vector(1, *floatField))); AlwaysAssertExit (allEQ (*doubleFieldA, Vector(1, *doubleField))); AlwaysAssertExit (allEQ (*complexFieldA, Vector(1, *complexField))); AlwaysAssertExit (allEQ (*dcomplexFieldA, Vector(1, *dcomplexField))); AlwaysAssertExit (allEQ (*stringFieldA, Vector(1, *stringField))); // Array fields RORecordFieldPtr > arrayboolField(record, 10); RORecordFieldPtr > arrayucharField(record, 11); RORecordFieldPtr > arrayshortField(record, 12); RORecordFieldPtr > arrayintField(record, 13); RORecordFieldPtr > arrayuintField(record, 14); RORecordFieldPtr > arrayfloatField(record, 15); RORecordFieldPtr > arraydoubleField(record, 16); RORecordFieldPtr > arraycomplexField(record, 17); RORecordFieldPtr > arraydcomplexField(record, 18); RORecordFieldPtr > arraystringField(record, 19); AlwaysAssertExit(arrayboolField.comment() == "comment for TpArrayBool"); AlwaysAssertExit(allEQ(*arrayboolField, *boolField)); AlwaysAssertExit(allEQ(*arrayucharField, *ucharField)); AlwaysAssertExit(allEQ(*arrayshortField, *shortField)); AlwaysAssertExit(allEQ(*arrayintField, *intField)); AlwaysAssertExit(allEQ(*arrayuintField, *uintField)); AlwaysAssertExit(allEQ(*arrayfloatField, *floatField)); AlwaysAssertExit(allEQ(*arraydoubleField, *doubleField)); AlwaysAssertExit(allEQ(*arraycomplexField, *complexField)); AlwaysAssertExit(allEQ(*arraydcomplexField, *dcomplexField)); AlwaysAssertExit(allEQ(*arraystringField, stringToVector("Hello,Goodbye"))); // Sub(-sub)-record fields RORecordFieldPtr recordField(record, "SubRecord"); const TableRecord& subrec = *recordField; AlwaysAssertExit(subrec.nfields() == 2); RORecordFieldPtr subref (subrec, 0); AlwaysAssertExit(*subref == 8.0); RORecordFieldPtr subref2 (record.subRecord (record.fieldNumber ("SubRecord")), 0); AlwaysAssertExit(*subref2 == 8.0); RORecordFieldPtr recordField1(record, "SubRecord1"); const TableRecord& subrec1 = *recordField1; AlwaysAssertExit(! subrec1.isFixed()); AlwaysAssertExit(subrec1.nfields() == 3); RORecordFieldPtr sub(subrec1, "sub"); AlwaysAssertExit((*sub).isFixed()); AlwaysAssertExit((*sub).nfields() == 2); RORecordFieldPtr subrefa (*sub, 0); AlwaysAssertExit(*subrefa == 9.0); RORecordFieldPtr sub1(subrec1, "sub1"); AlwaysAssertExit(! (*sub1).isFixed()); AlwaysAssertExit((*sub1).nfields() == 2); AlwaysAssertExit((*sub1).asfloat(0) == 6.0); RORecordFieldPtr sub2(subrec1, "sub2"); AlwaysAssertExit(! (*sub2).isFixed()); AlwaysAssertExit((*sub2).nfields() == 2); AlwaysAssertExit((*sub2).asdouble("SubFloat") == 8.0); } // Test Table specific things. void testTable (Bool doExcp) { // Create a table description and table. TableDesc td1 ("td1", TableDesc::Scratch); td1.addColumn (ScalarColumnDesc ("col1")); TableDesc td2 ("td2", TableDesc::Scratch); td2.addColumn (ScalarColumnDesc ("col1")); td2.addColumn (ScalarColumnDesc ("col2")); SetupNewTable newtab1 ("tTableRecord_tmp.tab1", td1, Table::New); Table tab1 (newtab1, 10); SetupNewTable newtab2 ("tTableRecord_tmp.tab2", td2, Table::New); Table tab2 (newtab2, 20); RecordDesc rd1; rd1.addTable ("tab1", "td1"); rd1.addField ("tab2", TpTable); TableRecord rec1 (rd1, RecordInterface::Variable); rec1.defineTable (rec1.fieldNumber("tab1"), tab1); rec1.defineTable (rec1.fieldNumber("tab2"), tab1); Table t1 = rec1.asTable (rec1.fieldNumber("tab1")); AlwaysAssertExit (t1.nrow() == 10 && t1.tableDesc().ncolumn() == 1); Table t2 = rec1.asTable (rec1.fieldNumber("tab2")); AlwaysAssertExit (t2.nrow() == 10 && t2.tableDesc().ncolumn() == 1); if (doExcp) { try { rec1.defineTable (rec1.fieldNumber("tab1"), tab2); } catch (std::exception& x) { cout << x.what() << endl; // non-conforming } try { RecordFieldPtr

      • fld1 (rec1, "tab1"); } catch (std::exception& x) { cout << x.what() << endl; // invalid type } } rec1.defineTable (rec1.fieldNumber("tab2"), tab2); Table t3 = rec1.asTable (rec1.fieldNumber("tab2")); AlwaysAssertExit (t3.nrow() == 20 && t3.tableDesc().ncolumn() == 2); AipsIO aos ("tTableRecord_tmp.data", ByteIO::New); aos << rec1; // Copy constructor // defineTable // conform TableRecord rec2(rec1); AlwaysAssertExit (rec1.conform(rec2)); rec2.defineTable ("tab1a", t3, RecordInterface::Fixed); AlwaysAssertExit (! rec1.conform(rec2)); // nfields() differ AlwaysAssertExit (! rec2.conform(rec1)); // nfields() differ rec2.removeField (1); AlwaysAssertExit (rec1.conform(rec2)); // second table type is empty AlwaysAssertExit (rec2.conform(rec1)); // second table type matches rec1.removeField(1); rec1.defineTable ("tab1a", t1); AlwaysAssertExit (rec1.conform(rec2)); // second table type is var. AlwaysAssertExit (! rec2.conform(rec1)); // second table type mismatches // Convert to and from a Record. // First flush, otherwise fromRecord won't find a table. tab1.flush(); tab2.flush(); Record srec = rec1.toRecord(); cout << "Record" << endl << srec; TableRecord trec; trec.fromRecord (srec); cout << "TableRecord" << endl << trec; // Constructor from RecordInterface // The first one should dynamic cast itself to a TableRecord, TableRecord rec3((const RecordInterface&)rec1); AlwaysAssertExit (rec1.conform(rec3)); Record rec4a; rec4a.define ("fld1", 1.); TableRecord rec4(rec4a); AlwaysAssertExit (rec4.nfields() == 1); AlwaysAssertExit (rec4.asDouble("fld1") == 1.); // Test that a Record constructed with a TableDesc // can be converted back into an equivalent TableDesc TableDesc td3 ("td3", TableDesc::Scratch); td3.addColumn (ScalarColumnDesc ("icol1")); td3.addColumn (ScalarColumnDesc ("icol2")); td3.addColumn (ScalarColumnDesc ("dcol1")); td3.addColumn (ScalarColumnDesc ("dcol2")); td3.addColumn (ScalarColumnDesc ("col1")); td3.addColumn (ScalarColumnDesc ("col2")); uInt keyvalue1 = 10; td3.rwKeywordSet().define("key1", keyvalue1); // Add a hypercolumn Vector dcnames(2); dcnames[0] = "dcol1"; dcnames[1] = "dcol2"; Vector ccnames(2); ccnames[0] = "col1"; ccnames[1] = "col2"; Vector icnames(2); icnames[0] = "icol1"; icnames[1] = "icol2"; td3.defineHypercolumn("hc1", 2, dcnames, ccnames, icnames); // Convert to Record Record rec5 = TableProxy::getTableDesc(td3); // Convert back to TableDesc TableDesc td4; String msg; TableProxy::makeTableDesc(rec5, td4, msg); // Check that the number of columns is equal AlwaysAssertExit (td3.ncolumn() == td4.ncolumn()); for(uInt i=0; i dcresult; Vector ccresult; Vector icresult; uInt dim = td4.hypercolumnDesc("hc1", dcresult, ccresult, icresult); AlwaysAssertExit (dim == 2); for(uInt i=0; i> rec1; { Table t1 = rec1.asTable (rec1.fieldNumber("tab1")); AlwaysAssertExit (t1.nrow() == 10 && t1.tableDesc().ncolumn() == 1); Table t3 = rec1.asTable (rec1.fieldNumber("tab2")); AlwaysAssertExit (t3.nrow() == 20 && t3.tableDesc().ncolumn() == 2); } rec1.closeTable ("tab1"); rec1.closeTable ("tab2"); rec1.closeTable ("tab2"); Table t1 = rec1.asTable (rec1.fieldNumber("tab1")); AlwaysAssertExit (t1.nrow() == 10 && t1.tableDesc().ncolumn() == 1); // defineTable() rec1.defineTable ("tab1a", t1); AlwaysAssertExit (rec1.nfields() == 3); } int main (int argc, const char*[]) { try { doIt ( (argc<2)); testTable ( (argc<2)); testTable2 ( (argc<2)); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; // exit with success status } casacore-3.7.1/tables/Tables/test/tTableRecord.out000066400000000000000000000070631476623553700221000ustar00rootroot00000000000000Record field cannot be added: length<1 Record field aB cannot be added: no uppercase Record field A cannot be added: extra==10 TableRecordRep::defineDataField - incorrect data type used for field TpShort ArrayBase::validateConformance shape [3] differs from [1] ArrayBase::validateConformance shape [3] differs from [1] ArrayBase::validateConformance shape [3] differs from [1] ArrayBase::validateConformance shape [3] differs from [1] Record::define - fixed array conformance error for field TpArrayString2 Record::define - fixed array conformance error for field TpBool Record cannot be changed (fixed structure) >>> Instance-specific assertion error message: (/aips++/code/aips/implement/Tables/TableRecord.cc : 121) Failed AlwaysAssertExit conform (other) <<< TableRecordRep::get_pointer - field SubRecord is not of type TableRecord >>> Instance-specific assertion error message: (/aips++/code/aips/implement/Tables/TableRecord.cc : 121) Failed AlwaysAssertExit conform (other) <<< TpBool: Bool 0 TpUChar: uChar 0 TpShort: Short 0 TpInt: Int 0 TpUInt: uInt 0 TpFloat: Float 0 TpDouble: Double 0 TpComplex: Complex (0,0) TpDComplex: DComplex (0,0) TpString: String "" TpArrayBool: Bool array with shape [1] [0] TpArrayUChar: uChar array with shape [1] [0] TpArrayShort: Short array with shape [1] [0] TpArrayInt: Int array with shape [1] [0] TpArrayUInt: uInt array with shape [1] [0] TpArrayFloat: Float array with shape [1] [0] TpArrayDouble: Double array with shape [1] [0] TpArrayComplex: Complex array with shape [1] [(0,0)] TpArrayDComplex: DComplex array with shape [1] [(0,0)] TpArrayString: String array with shape [] [] SubRecord1: { fa: Float 3 ia: Int 2 } SubRecord: { SubFloat: Float 0 SubInt: Int 0 } TpBool2a: Bool 0 TpUChar2a: uChar 0 TpShort2a: Short 0 TpInt2a: Int 0 TpUInt2a: uInt 0 TpFloat2a: Float 0 TpDouble2a: Double 0 TpComplex2a: Complex (0,0) TpDComplex2a: DComplex (0,0) TpArrayString2a: String array with shape [3] [, , ] TpArrayString3a: String array with shape [] [] TpString2a: String "" TpBool: Bool 0 TpUChar: uChar 0 TpShort: Short 0 TpInt: Int 0 TpUInt: uInt 0 TpFloat: Float 0 TpDouble: Double 0 TpComplex: Complex (0,0) TpDComplex: DComplex (0,0) TpString: String "" TpArrayBool: Bool array with shape [1] [0] TpArrayUChar: uChar array with shape [1] [0] TpArrayShort: Short array with shape [1] [0] TpArrayInt: Int array with shape [1] [0] TpArrayUInt: uInt array with shape [1] [0] TpArrayFloat: Float array with shape [1] [0] TpArrayDouble: Double array with shape [1] [0] TpArrayComplex: Complex array with shape [1] [(0,0)] TpArrayDComplex: DComplex array with shape [1] [(0,0)] TpArrayString: String array with shape [] [] SubRecord1: { fa: Float 3 ia: Int 2 } SubRecord: { SubFloat: Float 0 SubInt: Int 0 } TpBool2a: Bool 0 TpUChar2a: uChar 0 TpShort2a: Short 0 TpInt2a: Int 0 TpUInt2a: uInt 0 TpFloat2a: Float 0 TpDouble2a: Double 0 TpComplex2a: Complex (0,0) TpDComplex2a: DComplex (0,0) TpArrayString2a: String array with shape [3] [, , ] TpArrayString3a: String array with shape [] [] TpString2a: String "" TableKeyword::operator=; non-conforming table TableRecordRep::get_pointer - incorrect data type used for field tab1 Record tab1: String "Table: tTableRecord_tmp.tab1" tab1a: String "Table: tTableRecord_tmp.tab1" TableRecord tab1: Table tTableRecord_tmp.tab1 tab1a: Table tTableRecord_tmp.tab1 OK casacore-3.7.1/tables/Tables/test/tTableRow.cc000066400000000000000000000301261476623553700212030ustar00rootroot00000000000000//# tTableRow.cc: Test program for class (RO)TableRow //# Copyright (C) 1996,1997,1998,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Test program for the class (RO)TableRow // // This program tests the classes ROTableRow and TableRow. // The results are written to stdout. The script executing this program, // compares the results with the reference output file. // // In the second part of the program a table is created and read back. // It times accessing it as columns and as rows. // The number of rows can be specified as the first argument (default 500). // When an argument is given, no statements resulting in exceptions // will be executed. This gives the possibility to check for memory leaks // (because the emulated exceptions result in leaks). // First build a description. void a (Bool) { // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.comment() = "A test of class Table"; td.addColumn (ScalarColumnDesc("ab","Comment for column ab")); td.addColumn (ScalarColumnDesc("ad","comment for ad")); td.addColumn (ScalarColumnDesc("ag")); td.addColumn (ArrayColumnDesc("arr1",3,ColumnDesc::Direct)); td.addColumn (ArrayColumnDesc("arr2",0)); td.addColumn (ArrayColumnDesc("arr3",0)); td.addColumn (ScalarRecordColumnDesc("rec")); // Now create a new table from the description. SetupNewTable newtab("tTableRow_tmp.data", td, Table::New); newtab.setShapeColumn("arr1",IPosition(3,2,3,4)); Table tab(newtab, 10); Cube arrf(IPosition(3,2,3,4)); Vector arrs(stringToVector ("a,bc,def,ghij,klmno,qprstu,vxxyzab," "cdefghij,klmnopqrs,tuvwxyzabc")); indgen (arrf); TableRow row (tab, stringToVector("ab,ad,ag,arr1,arr2,rec")); AlwaysAssertExit (row.rowNumber() == -1); TableRecord rec (row.record().description(), RecordInterface::Variable); AlwaysAssertExit (row.record().nfields() == 6); RecordFieldPtr ab(rec, 0); RecordFieldPtr ad(rec, 1); RecordFieldPtr ag(rec, 2); RecordFieldPtr > arr1(rec, 3); RecordFieldPtr > arr2(rec, 4); RecordFieldPtr recfld(rec, 5); ArrayColumn arr3(tab, "arr3"); TableRecord r1; Int i; for (i=0; i<10; i++) { ab.define (i); ad.define (i+2); ag.define (DComplex(i+3,-i-1)); arr1.define (arrf); arr2.define (arrs(Slice(0,i))); r1.define (i, i); recfld.define (r1); row.put (i, rec); if (i%2 == 0) { arr3.put (i, arrf); } arrf += (float)(arrf.nelements()); } // Test if the record has an extra field. rec.define ("extraField", Int(1)); row.putMatchingFields (9, rec); AlwaysAssertExit (row.rowNumber() == -1); ScalarColumn colab (tab, "ab"); ScalarColumn colad (tab, "ad"); ScalarColumn colag (tab, "ag"); ScalarColumn colrec (tab, "rec"); ArrayColumn colarr1 (tab, "arr1"); ArrayColumn colarr2 (tab, "arr2"); ArrayColumn colarr3 (tab, "arr3"); Int abval; uInt adval; DComplex agval; TableRecord recval; Cube arrval(IPosition(3,2,3,4)); Cube arr3val; Array arrstr; arrf -= (float)(arrf.nelements()*tab.nrow()); for (i=0; i<10; i++) { colab.get (i, abval); colad.get (i, adval); colag.get (i, agval); if (abval != i || Int(adval) != i+2 || agval != DComplex(i+3,-i-1)) { cout << "error in row " << i << ": " << abval << ", " << adval << ", " << agval << endl; } colrec.get (i, recval); if (Int(recval.nfields()) != i+1) { cout << "error in row " << i << ": " << recval.nfields() << " fields; expected " << i+1 << endl; } else { for (Int j=0; j<=i; j++) { if (recval.asInt(j) != j) { cout << "error in row " << i << ": invalid record" << endl; } } } colarr1.get (i, arrval); if (!allEQ (arrval, arrf)) { cout << "error in arr1 in row " << i << endl; } colarr2.get (i, arrstr, True); AlwaysAssertExit (arrstr.ndim() == 1); AlwaysAssertExit (arrstr.shape()(0) == i); if (!allEQ (arrstr, arrs(Slice(0,i)))) { cout << "error in arr2 in row " << i << endl; } if (i%2 == 0) { colarr3.get (i, arr3val, True); AlwaysAssertExit (arr3val.ndim() == 3); if (!allEQ (arr3val, arrf)) { cout << "error in arr3 in row " << i << endl; } }else{ AlwaysAssertExit (! colarr3.isDefined (i)); } arrf += (float)(arrf.nelements()); } TableRow rowa; AlwaysAssertExit (! rowa.isAttached()); TableRow rowb(rowa); AlwaysAssertExit (! rowb.isAttached()); rowb = row; AlwaysAssertExit (rowb.isAttached()); AlwaysAssertExit (row.record().description() == rowb.record().description()); rowb = rowa; AlwaysAssertExit (! rowb.isAttached()); TableRow rowc(row); AlwaysAssertExit (row.record().description() == rowc.record().description()); } void b (Bool doExcp) { Table tab("tTableRow_tmp.data"); if (doExcp) { try { TableRow row (tab); } catch (std::exception& x) { cout << x.what() << endl; // not writable } try { ROTableRow row (tab, stringToVector("ab,abb")); } catch (std::exception& x) { cout << x.what() << endl; // abb not exists } } ROTableRow rowx (tab, stringToVector("ab,arr1")); ROTableRow rowy (tab, stringToVector("ab,bcd,arr1"), True); RORecordFieldPtr ab(rowx.record(), 0); RORecordFieldPtr ad(rowy.record(), 0); RORecordFieldPtr ag(rowy.record(), 1); RORecordFieldPtr > arr1(rowx.record(), 1); RORecordFieldPtr > arr2(rowy.record(), 2); RORecordFieldPtr > arr3(rowy.record(), 3); Cube arrf(IPosition(3,2,3,4)); Vector arrs(stringToVector ("a,bc,def,ghij,klmno,qprstu,vxxyzab," "cdefghij,klmnopqrs,tuvwxyzabc")); indgen (arrf); Int i; for (i=0; i<10; i++) { cout << "get scalar row " << i << endl; rowx.get (i); AlwaysAssertExit (rowx.rowNumber() == i); rowy.get (i); if (*ab != i || Int(*ad) != i+2 || *ag != DComplex(i+3,-i-1)) { cout << "error in row " << i << ": " << *ab << ", " << *ad << ", " << *ag << endl; } if (!allEQ (*arr1, arrf)) { cout << "error in arr1 in row " << i << endl; } if (!allEQ (*arr2, arrs(Slice(0,i)))) { cout << "error in arr2 in row " << i << endl; } if (i%2 == 0) { if (!allEQ (*arr3, arrf)) { cout << "error in arr3 in row " << i << endl; } }else{ if ((*arr3).ndim() != 0) { cout << "error in arr3 in row " << i << endl; } } arrf += (float)(arrf.nelements()); } // This get operation should not do anything. rowx.get (i-1); AlwaysAssertExit (rowx.rowNumber() == i-1); ROTableRow rowa; AlwaysAssertExit (! rowa.isAttached()); ROTableRow rowb(rowa); AlwaysAssertExit (! rowb.isAttached()); rowb = rowy; AlwaysAssertExit (rowb.isAttached()); AlwaysAssertExit (rowy.record().description() == rowb.record().description()); rowb = rowa; AlwaysAssertExit (! rowb.isAttached()); ROTableRow rowc(rowy); AlwaysAssertExit (rowy.record().description() == rowc.record().description()); } // This function times how fast it can read data back. void c (Int nrow) { // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.comment() = "A test of class Table"; td.addColumn (ScalarColumnDesc("ab","Comment for column ab")); td.addColumn (ScalarColumnDesc("ad","comment for ad")); td.addColumn (ScalarColumnDesc("ag")); td.addColumn (ArrayColumnDesc("arr1",3,ColumnDesc::Direct)); // Now create a new table from the description. SetupNewTable newtab("tTableRow_tmp.data1", td, Table::New); newtab.setShapeColumn("arr1",IPosition(3,2,3,4)); Table tab(newtab, nrow); Cube arrf(IPosition(3,2,3,4)); indgen (arrf); cout << ">>>" << endl; Timer timer; TableRow row (tab); RecordFieldPtr ab(row.record(), 0); RecordFieldPtr ad(row.record(), 1); RecordFieldPtr ag(row.record(), 2); RecordFieldPtr > arr1(row.record(), 3); Int i; for (i=0; i colab (tab, "ab"); ScalarColumn colad (tab, "ad"); ScalarColumn colag (tab, "ag"); Int abval; uInt adval; DComplex agval; for (i=0; i colarr1 (tab, "arr1"); Cube arrval(IPosition(3,2,3,4)); for (i=0; i abr(rowx.record(), 0); RORecordFieldPtr adr(rowx.record(), 1); RORecordFieldPtr agr(rowx.record(), 2); for (i=0; i > arr1r(rowy.record(), 0); for (i=0; i 1) { istringstream istr(argv[1]); istr >> nr; } try { a ( (argc<2)); b ( (argc<2)); c (nr); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } cout << "OK" << endl; return 0; // exit with success status } casacore-3.7.1/tables/Tables/test/tTableRow.out000066400000000000000000000011121476623553700214160ustar00rootroot00000000000000TableRow cannot be used: table is not writable Table column abb is unknown get scalar row 0 get scalar row 1 get scalar row 2 get scalar row 3 get scalar row 4 get scalar row 5 get scalar row 6 get scalar row 7 get scalar row 8 get scalar row 9 >>> put row 0.15 real 0.16 user 0 system scalar column 0 real 0 user 0 system array column 0.01 real 0.01 user 0 system scalar row 0.01 real 0.01 user 0 system array row 0.01 real 0.01 user 0 system <<< OK casacore-3.7.1/tables/Tables/test/tTableTrace.cc000066400000000000000000000074431476623553700215000ustar00rootroot00000000000000//# tTableTrace.cc: Test program for class TableTrace //# Copyright (C) 2016 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include // This program and script tTableTrace.run test the class TableTrace. void testTable (rownr_t nrrow) { { // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.comment() = "A test of class Table"; td.addColumn (ScalarColumnDesc("ab")); td.addColumn (ArrayColumnDesc("ad")); // Now create a new table from the description. SetupNewTable newtab("tTableTrace_tmp.tab", td, Table::New); IncrementalStMan stman2; newtab.bindColumn ("ab", stman2); Table tab(newtab, TableLock(TableLock::PermanentLocking), nrrow); tab.tableInfo().setType ("testtype"); tab.tableInfo().setSubType ("testsubtype"); tab.tableInfo().readmeAddLine ("first readme line"); tab.tableInfo().readmeAddLine ("second test readme line"); // Write some data. ScalarColumn ab1(tab, "ab"); ArrayColumn ad(tab, "ad"); for (rownr_t i=0; i(8,i/10)); } } // Read data back. Table tab("tTableTrace_tmp.tab"); ScalarColumn ab1(tab, "ab"); ArrayColumn ad(tab, "ad"); Vector abv = ab1.getColumn(); Array adv = ad.getColumn(); { // Get entire column (minus last cell). Vector abv1 = ab1.getColumnRange (Slicer(IPosition(1,0), IPosition(1,nrrow-1))); Array adv1 = ad.getColumnRange (Slicer(IPosition(1,0), IPosition(1,nrrow-1))); } { Vector abv64(abv.size()); convertArray (abv64, abv); Table rtab (tab(abv64)); ScalarColumn ab1(rtab,"ab"); ArrayColumn ad(rtab,"ad"); Vector abv = ab1.getColumn(); Array adv = ad.getColumn(); } } int main() { try { rownr_t nrrow = 5; testTable (nrrow); } catch (const std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } casacore-3.7.1/tables/Tables/test/tTableTrace.out000066400000000000000000000021661476623553700217170ustar00rootroot00000000000000# time oper tabid name row(s) shape blc/trc/inc # Note: shapes are in Fortran order time1 n t=0 tTableTrace_tmp.tab time1 t t=0 *flush* time1 c t=0 tTableTrace_tmp.tab time1 o t=0 tTableTrace_tmp.tab time1 c t=0 tTableTrace_tmp.tab # time oper tabid name row(s) shape blc/trc/inc # Note: shapes are in Fortran order time2 n t=0 tTableTrace_tmp.tab time2 w t=0 ad 0 [8] time2 w t=0 ad 1 [8] time2 w t=0 ad 2 [8] time2 w t=0 ad 3 [8] time2 w t=0 ad 4 [8] time2 t t=0 *flush* time2 c t=0 tTableTrace_tmp.tab time2 o t=0 tTableTrace_tmp.tab time2 r t=0 ad * [8,5] time2 r t=0 ad 0:3 [8,4] time2 s t=0 *reftable* time2 r t=0 ad 0,1,2,3,4 [8,5] time2 c t=0 *reftable* time2 c t=0 tTableTrace_tmp.tab # time oper tabid name row(s) shape blc/trc/inc # Note: shapes are in Fortran order time3 n t=0 tTableTrace_tmp.tab time3 w t=0 ab 0 time3 w t=0 ab 1 time3 w t=0 ab 2 time3 w t=0 ab 3 time3 w t=0 ab 4 time3 t t=0 *flush* time3 c t=0 tTableTrace_tmp.tab time3 o t=0 tTableTrace_tmp.tab time3 r t=0 ab * time3 r t=0 ab 0:3 time3 s t=0 *reftable* time3 r t=0 ab 0,1,2,3,4 time3 c t=0 *reftable* time3 c t=0 tTableTrace_tmp.tab casacore-3.7.1/tables/Tables/test/tTableTrace.run000077500000000000000000000014071476623553700217140ustar00rootroot00000000000000#!/bin/sh # Define the 'casarc' file to be used by Aipsrc. CASARCFILES=`pwd`/tTableTrace_tmp.rc export CASARCFILES # Do high level tracing to a file. # Remove the time from the log lines. cat > tTableTrace_tmp.rc < tTableTrace_tmp.rc < tTableTrace_tmp.rc < #include #include #include #include #include #include #include #include using namespace casacore; // This program tests some table utility functions. // Define the main table name. const String mainName("tTableUtil_tmp/maindata"); // Create a table and various subtables. void createTables() { // Create a main table. TableDesc td("", "", TableDesc::Scratch); td.addColumn (ScalarColumnDesc("RowNr")); Table tab(TableUtil::createTable(mainName, td, Table::New)); // Create 3 subtables (in different directories). TableDesc std("", "", TableDesc::Scratch); std.addColumn (ScalarColumnDesc("col1")); SetupNewTable newtab2(mainName + "/SubTab2a", std, Table::New); Table subtab2(newtab2, 2); std.addColumn (ScalarColumnDesc("col2")); SetupNewTable newtab3(mainName + "/subdata", std, Table::New); Table subtab3(newtab3, 3); // Store one subtable as a keyword in the main table, the // other as a column keyword. tab.rwKeywordSet().defineTable ("SubTab2", subtab2); ScalarColumn col(tab, "RowNr"); col.rwKeywordSet().defineTable ("SubTab3", subtab3); // Store another subtable as a keyword in SubTab3. std.addColumn (ScalarColumnDesc("col3")); SetupNewTable newtab4(mainName + "/subdata/sub4", std, Table::New); Table subtab4(newtab4, 4); subtab3.rwKeywordSet().defineTable ("SubTab4", subtab4); { // Create a scratch table. Table stab(TableUtil::createTable(String(), td, Table::New)); AlwaysAssertExit (stab.nrow() == 0); } } void readTables() { // Reconstruct the main table. // Get the sub table from the keyword. Table tab = TableUtil::openTable(mainName); AlwaysAssertExit (tab.nrow() == 0); AlwaysAssertExit (tab.keywordSet().nfields() == 1); AlwaysAssertExit (tab.tableDesc().ncolumn() == 1); AlwaysAssertExit (tab.keywordSet().isDefined ("SubTab2")); Table subtab2 = tab.keywordSet().asTable ("SubTab2"); AlwaysAssertExit (subtab2.nrow() == 2); AlwaysAssertExit (subtab2.tableDesc().ncolumn() == 1); ScalarColumn col(tab, "RowNr"); Table subtab3 = col.keywordSet().asTable ("SubTab3"); AlwaysAssertExit (subtab3.nrow() == 3); AlwaysAssertExit (subtab3.tableDesc().ncolumn() == 2); Table subtab4 = subtab3.keywordSet().asTable ("SubTab4"); AlwaysAssertExit (subtab4.nrow() == 4); AlwaysAssertExit (subtab4.tableDesc().ncolumn() == 3); } void testColons() { { // Open the table using the :: syntax. Table subtab2(TableUtil::openTable(mainName + "::SubTab2")); AlwaysAssertExit (subtab2.nrow() == 2); AlwaysAssertExit (subtab2.tableDesc().ncolumn() == 1); } { // Create a subtable of the subtable. It will have 1 column and 10 rows. TableDesc td; td.addColumn(ScalarColumnDesc("col2")); Table subtab4(TableUtil::createTable(mainName + "::SubTab2::SubSubTab4", td, Table::NewNoReplace, Table::Plain, StorageOption(), Record(), TableLock(), 10)); } { // Open the subtable just created. Table tab(TableUtil::openTable(mainName + "::SubTab2::SubSubTab4")); AlwaysAssertExit (tab.nrow() == 10); AlwaysAssertExit (tab.tableDesc().ncolumn() == 1); } { // Do the same, but use slashes. Table tab(TableUtil::openTable(mainName + "/SubTab2a/SubSubTab4")); AlwaysAssertExit (tab.nrow() == 10); AlwaysAssertExit (tab.tableDesc().ncolumn() == 1); } } void testDelete() { // Create and delete a subtable. { TableDesc td; Table tab(TableUtil::createTable(mainName + "::SubTab2::SubSubTab5", td, Table::NewNoReplace)); } TableUtil::deleteTable (mainName + "::SubTab2::SubSubTab5"); // Check it is deleted. Bool ok = False; try { Table tab(TableUtil::openTable(mainName + "::SubTab2::SubSubTab5")); } catch (const TableError& x) { ok = True; cout << "Expected exception: " << x.what() << endl; } AlwaysAssertExit (ok); { // Also check the keyword does not exist anymore. Table tab(TableUtil::openTable(mainName + "::SubTab2")); AlwaysAssertExit (! tab.keywordSet().isDefined("SubSubTab5")); } { // Do the same, but leave the created table open to make deletion fail. TableDesc td; Table tab(TableUtil::createTable(mainName + "::SubTab2::SubSubTab5", td, Table::NewNoReplace)); ok = False; try { TableUtil::deleteTable (mainName + "::SubTab2::SubSubTab5"); } catch (const TableError& x) { ok = True; cout << "Expected exception: " << x.what() << endl; } AlwaysAssertExit (ok); } } void testReplace() { // Create a subtable. { TableDesc td; td.addColumn(ScalarColumnDesc("col2")); Table tab(TableUtil::createTable(mainName + "::SubTab2::SubSubTab6", td, Table::NewNoReplace)); AlwaysAssertExit (tab.nrow() == 0); AlwaysAssertExit (tab.tableDesc().ncolumn() == 1); AlwaysAssertExit (tab.tableDesc()[0].name() == "col2"); } // Replace the subtable. { TableDesc td; td.addColumn(ScalarColumnDesc("col1")); td.addColumn(ScalarColumnDesc("col3")); Table tab(TableUtil::createTable(mainName + "::SubTab2::SubSubTab6", td, Table::New)); AlwaysAssertExit (tab.nrow() == 0); AlwaysAssertExit (tab.tableDesc().ncolumn() == 2); AlwaysAssertExit (tab.tableDesc()[0].name() == "col1"); AlwaysAssertExit (tab.tableDesc()[1].name() == "col3"); } } void testErrors() { // Do some erroneous createTable calls. TableDesc td; Bool ok = False; try { // SubTab22 does not exist TableUtil::createTable (mainName + "::SubTab22::SubSubTab4", td, Table::New); } catch (const TableError& x) { ok = True; cout << "Expected exception: " << x.what() << endl; } AlwaysAssertExit (ok); ok = False; try { // main33data does not exist. TableUtil::createTable ("tTableUtil_tmp/main33data::SubTab2::SubSubTab4", td, Table::NewNoReplace); } catch (const TableError& x) { ok = True; cout << "Expected exception: " << x.what() << endl; } AlwaysAssertExit (ok); ok = False; try { // SubSubTab4 already exists. TableUtil::createTable (mainName + "::SubTab2::SubSubTab4", td, Table::NewNoReplace); } catch (const TableError& x) { ok = True; cout << "Expected exception: " << x.what() << endl; } AlwaysAssertExit (ok); ok = False; try { // Empty part given. TableUtil::createTable ("tTableUtil_tmp/main3data::::SubTab2::SubSubTab4", td, Table::New); } catch (const TableError& x) { ok = True; cout << "Expected exception: " << x.what() << endl; } AlwaysAssertExit (ok); } void testLayoutInfo (const String& tableName) { cout << endl << "Test getLayout and tableInfo ..." << endl; // Get the description and #rows of the Table. TableDesc layout; cout << "TableUtil::getlayout #rows = " << TableUtil::getLayout (layout, tableName); layout.show (cout); cout << endl; TableInfo info(TableUtil::tableInfo (tableName)); cout << "type = " << info.type() << endl; cout << "subtype = " << info.subType() << endl; cout << info.readme() << endl; } int main() { try { // Create subdirectory with this name, which will be deleted by assay. Directory dir("tTableUtil_tmp"); dir.create(); createTables(); readTables(); testColons(); testDelete(); testReplace(); testErrors(); testLayoutInfo(mainName); testLayoutInfo(mainName + "::SubTab2"); } catch (const std::exception& x) { cout << "Caught an exception : " << x.what() << endl; return 1; } cout << "tTableUtil ended OK" << endl; return 0; } casacore-3.7.1/tables/Tables/test/tTableUtil.out000066400000000000000000000026721476623553700216000ustar00rootroot00000000000000Expected exception: Table name tTableUtil_tmp/maindata::SubTab2::SubSubTab5 is invalid (subtable SubSubTab5 is unknown) Expected exception: Subtable SubSubTab5 in tTableUtil_tmp/maindata/SubTab2a cannot be deleted: table is still open in this process Expected exception: Table name tTableUtil_tmp/maindata::SubTab22::SubSubTab4 is invalid (subtable SubTab22 is unknown) Expected exception: Table name tTableUtil_tmp/main33data::SubTab2::SubSubTab4 is invalid (main table tTableUtil_tmp/main33data does not exist) Expected exception: Subtable tTableUtil_tmp/maindata/SubTab2a/SubSubTab4 already exists Expected exception: Table name tTableUtil_tmp/main3data::::SubTab2::SubSubTab4 is invalid (empty name part given) Test getLayout and tableInfo ... TableUtil::getlayout #rows = 0 TableDesc version (Directory **SCRATCH**) --------- Comment: #Keywords = 1 0 SubTab2 : TableDesc #Columns = 1 Name=RowNr DataType=Int DataManager=StandardStMan/StandardStMan Default=0 Comment = #keywords=1 0 SubTab3 : TableDesc type = subtype = Test getLayout and tableInfo ... TableUtil::getlayout #rows = 2 TableDesc version (Directory **SCRATCH**) --------- Comment: #Keywords = 3 0 SubSubTab4 : TableDesc 1 SubSubTab5 : TableDesc 2 SubSubTab6 : TableDesc #Columns = 1 Name=col1 DataType=Int DataManager=StandardStMan/StandardStMan Default=0 Comment = #keywords=0 type = subtype = tTableUtil ended OK casacore-3.7.1/tables/Tables/test/tTableVector.cc000066400000000000000000000152141476623553700216770ustar00rootroot00000000000000//# tTableVector.cc: This program tests the table vectors //# Copyright (C) 1994,1995,1996,1999,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include // This program tests the table vectors.. // It creates a description and a table. // It creates various vectors, operates on them and checks the results. // It writes some timing data to stdout. void credes(); void cretab(uInt); void dovec (Int); int main (int argc, const char* argv[]) { uInt nr = 5000; if (argc > 1) { istringstream istr(argv[1]); istr >> nr; } credes(); // make description cretab(nr); // create table (and write it) dovec (nr); // do table vector operations return 0; // successfully executed } // Create the description of the table and its subtable. // Define some defaults (as scalar and array) to test if they // are filled in correctly when a row is added to a table. void credes () { TableDesc txp ("tTableVector_tmp", TableDesc::New); txp.addColumn (ScalarColumnDesc ("col1")); txp.addColumn (ScalarColumnDesc ("col2")); txp.addColumn (ScalarColumnDesc ("col3")); txp.addColumn (ScalarColumnDesc ("col4")); } // Write data into the table. void cretab (uInt nr) { uInt i; SetupNewTable newtab ("tTableVector_tmp.data", "tTableVector_tmp", Table::New); Table tab (newtab); ScalarColumn col1 (tab, "col1"); ScalarColumn col2 (tab, "col2"); ScalarColumn col3 (tab, "col3"); ScalarColumn col4 (tab, "col4"); Timer tim; for (i=0; i>>" << endl; tim.show(); cout << "<<<" << endl; cout << "Filling done" << endl; } void dovec (Int nr) { Int i; Vector vec(nr); Timer tim; for (i=0; i>>" << endl; tim.show ("Initializing Vector "); TableVector tvec(nr); tim.mark(); for (i=0; i tvec2 = tvec + 1; tim.show ("Adding constant to TVec "); cout << "<<<" << endl; cout << tvec2.nelements() << " elements" << endl; for (i=0; i tvec3 = tvec + tvec2; cout << ">>>" << endl; tim.show ("Adding TVec to TVec "); tim.mark(); tvec3 = tvec3 + tvec2; tvec3 += tvec; tim.show ("Adding TVec+TVec+TVec "); cout << "<<<" << endl; for (i=0; i tvec4(tab,"col1"); TableVector tvecFloat(tab,"col3"); //# tvecFloat = 3; // Should fail when compiling cout << "got tvec4" << endl; cout << tvec4(1) << " (must be 99999)" << endl; tim.mark(); tvec3 = tvec3 + tvec4; cout << ">>>" << endl; tim.show ("Adding ColVec to TVec "); cout << "<<<" << endl; for (i=0; i>>" << endl; tim.show ("Adding TVec to ColVec "); tvec4 = tvec4 + tvec2; Vector vec2; ScalarColumn col1 (tab, "col1"); tim.mark(); col1.getColumn (vec2); tim.show ("Getting a table column "); cout << "<<<" << endl; tim.mark(); for (i=0; i>>" << endl; tim.show ("Loop through TableVector"); cout << "<<<" << endl; tim.mark(); for (i=0; i>>" << endl; tim.show ("Loop through Vector "); cout << "<<<" << endl; // Get a vector from the table column vector. Vector vec3; vec3 = tvec4.makeVector(); for (i=0; i>> 0.06 real 0.04 user 0.01 system <<< Filling done >>> Initializing Vector 0.01 real 0.01 user 0 system Initializing TableVector 0 real 0 user 0 system Adding constant to TVec 0.01 real 0.01 user 0 system <<< 5000 elements >>> Adding TVec to TVec 0.01 real 0.01 user 0 system Adding TVec+TVec+TVec 0.03 real 0.03 user 0 system <<< got tvec4 99999 (must be 99999) >>> Adding ColVec to TVec 0.02 real 0.02 user 0 system <<< >>> Adding TVec to ColVec 0.04 real 0.03 user 0 system Getting a table column 0 real 0 user 0 system <<< >>> Loop through TableVector 0.01 real 0.01 user 0 system <<< >>> Loop through Vector 0.01 real 0.01 user 0 system <<< casacore-3.7.1/tables/Tables/test/tTable_1.cc000066400000000000000000000066501476623553700207400ustar00rootroot00000000000000//# tTable_1.cc: Test program for the SetupNewTable class //# Copyright (C) 1994,1995,1996,2000,2001,2003 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include // Test program for the SetupNewTable class // This program tests the class SetupNewTable and related classes. // The results are written to stdout. The script executing this program, // compares the results with the reference output file. void a(uInt); int main (int argc, const char* argv[]) { uInt nr = 500; if (argc > 1) { istringstream istr(argv[1]); istr >> nr; } try { a(nr); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } // First build a description. void a(uInt nrrow) { // Build the table description. TableDesc td("tTableDesc","1",TableDesc::Scratch); td.comment() = "A test of class TableDesc"; td.addColumn (ScalarColumnDesc ("ab","Comment for column ab")); td.addColumn (ScalarColumnDesc ("ac")); td.addColumn (ScalarColumnDesc ("ad","comment for ad")); td.addColumn (ScalarColumnDesc ("ae")); td.addColumn (ScalarColumnDesc ("af")); td.addColumn (ArrayColumnDesc ("arr2",0)); // Now create a new table from the description. SetupNewTable newtab("tTable_1_tmp.data", td, Table::New); // Create a storage manager for it. StManAipsIO sm1; StManAipsIO sm2; //#// StManKarma smk1(); newtab.bindAll (sm1); newtab.bindColumn ("ab",sm2); //#// newtab.bindColumn ("ae",smk1); //#// newtab.bindColumn ("arr3",smk1); Table tab(newtab, nrrow); //# Do some erroneous things. try { newtab.bindColumn ("ab", sm1); // newtab is already in use } catch (std::exception& x) { cout << x.what() << endl; } /// try { /// Table tab2(newtab, 10); // newtab is already in use /// } catch (std::exception x) { /// cout << x.what() << endl; /// } } casacore-3.7.1/tables/Tables/test/tTable_1.out000066400000000000000000000001211476623553700211450ustar00rootroot00000000000000Invalid Table operation: SetupNewTable::bindColumn, object already used by Table casacore-3.7.1/tables/Tables/test/tTable_2.cc000066400000000000000000000221671476623553700207420ustar00rootroot00000000000000//# tTable_2.cc: Test program for reading back old tables //# Copyright (C) 1995,1996,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Test program for reading back old tables // // This program tests if old tables (generated by tTable) can still // be read back when changes in the table file layout are made. // It can be executed as: // tTable_2 table0 table1 ... void doIt (const String& tableName); int main (int argc, const char* argv[]) { // Only execute when a table name has been given. try { for (int i=1; i>> -------------------" << endl; cout << "start reading Table " << tableName << endl; cout << "<<<" << endl; Table tab(tableName); cout << "end reading Table" << endl; ScalarColumn ab2(tab,"ab"); ScalarColumn ac (tab,"ac"); ScalarColumn ad(tab,"ad"); ScalarColumn ae(tab,"ae"); ScalarColumn af(tab,"af"); ScalarColumn ag(tab,"ag"); ArrayColumn arr1(tab,"arr1"); ArrayColumn arr2(tab,"arr2"); ArrayColumn arr3(tab,"arr3"); uInt i; Vector names = tab.tableDesc().columnNames(); for (i=0; i arrf(IPosition(3,2,3,4)); Cube arrval(IPosition(3,2,3,4)); Cube arrvalslice(arrval(Slice(0,1),Slice(0,1,2),Slice(0,2,2))); Slice tmp; Slicer nslice (tmp, tmp, tmp, Slicer::endIsLength); Slicer nslice2(Slice(0,1), Slice(0,1,2), Slice(0,2,2), Slicer::endIsLength); indgen (arrf); for (i=0; i<10; i++) { cout << "get scalar row " << i << endl; ab2.get (i, abval); ac.get (i, acval); ad.get (i, adval); ae.get (i, aeval); af.get (i, afval); ag.get (i, agval); snprintf (str, sizeof(str), "V%i", i); if (abval != Int(i) || acval != Int(i+1) || adval != i+2 || aeval != i+3 || afval != str || agval != DComplex(i+2)) { cout << "error in row " << i << ": " << abval << ", " << acval << ", " << adval << ", " << aeval << ", " << afval << ", " << agval << endl; } arr1.get (i, arrval); if (!allEQ (arrval, arrf)) { cout << "error in arr1 in row " << i << endl; } arr2.get (i, arrval); if (!allEQ (arrval, arrf)) { cout << "error in arr2 in row " << i << endl; } cout << "get array row " << i << endl; arr3.get (i, arrval); if (!allEQ (arrval, arrf)) { cout << "error in arr3 in row " << i << endl; } arr2.getSlice (i, nslice, arrval); if (!allEQ (arrval, arrf)) { cout << "error in arr2 (entire slice) in row " << i << endl; } arr2.getSlice (i, nslice2, arrvalslice); if (!allEQ (arrval, arrf)) { cout << "error in arr2 (partial slice) in row " << i << endl; } arrf += (float)(arrf.nelements()); } Vector abvec = ab2.getColumn(); cout << tab.nrow() << " " << abvec.nelements() << endl; for (i=0; i<10; i++) { if (abvec(i) != Int(i)) { cout << "error in getColumn " << i << ": " << abvec(i) << endl; } } Array arr1a = arr1.getColumn(); if (arr1a.ndim() != 4) { cout << "arr1a not 4-dim" << endl; } i=0; uInt j0,j1,j2,j3; for (j3=0; j3<10; j3++) for (j2=0; j2<4; j2++) for (j1=0; j1<3; j1++) for (j0=0; j0<2; j0++) { if (arr1a(IPosition(4,j0,j1,j2,j3)) != i++) { cout <<"arr1a error at " < arr1b = arr1.getColumn(nslice); if (arr1b.ndim() != 4) { cout << "arr1b not 4-dim" << endl; } i=0; for (j3=0; j3<10; j3++) for (j2=0; j2<4; j2++) for (j1=0; j1<3; j1++) for (j0=0; j0<2; j0++) { if (arr1b(IPosition(4,j0,j1,j2,j3)) != i++) { cout <<"arr1b error at " < sorae(sortab, "ae"); cout << sorae.getColumn() << endl; cout << "#columns in sortab: " << sortab.tableDesc().ncolumn() << endl; Table sortab2 = sortab.sort ("ad"); if (sortab2.nrow() != 10) { cout << "sortab2 does not contain 10 rows" << endl; } ScalarColumn sorad(sortab2, "ad"); cout << sorad.getColumn() << endl; cout << "#columns in sortab2: " << sortab2.tableDesc().ncolumn() << endl; // Get a subset of the table via row numbers. Vector rownrs(4); rownrs(0)=3; rownrs(1)=1; rownrs(2)=9; rownrs(3)=6; Table seltab1 = sortab(rownrs); if (seltab1.nrow() != 4) { cout << "seltab1 does not contain 4 rows" << endl; } ScalarColumn sel1ab(seltab1, "ab"); cout << sel1ab.getColumn() << endl; cout << "#columns in seltab1: " << seltab1.tableDesc().ncolumn() << endl; // Project the table. Block projname(3); projname[0] = "ae"; projname[1] = "ab"; projname[2] = "arr1"; Table seltab2 = seltab1.project (projname); if (seltab2.nrow() != 4) { cout << "seltab2 does not contain 4 rows" << endl; } ScalarColumn sel2ab(seltab2, "ab"); cout << sel2ab.getColumn() << endl; cout << "#columns in seltab2: " << seltab2.tableDesc().ncolumn() << endl; // Get a subset via a mask. Block mask(4,True); mask[0] = False; mask[3] = False; Table seltab3 = seltab2(mask); if (seltab3.nrow() != 2) { cout << "seltab3 does not contain 2 rows" << endl; } ScalarColumn sel3ab(seltab3, "ab"); cout << sel3ab.getColumn() << endl; cout << "#columns in seltab3: " << seltab3.tableDesc().ncolumn() << endl; cout << seltab3.tableDesc().columnNames() << endl; Table xortab = sortab ^ seltab1; if (xortab.nrow() != 6) { cout << "xortab does not contain 6 rows" << endl; } ScalarColumn xorab(xortab, "ab"); cout << xorab.getColumn() << endl; cout << "#columns in xortab: " << xortab.tableDesc().ncolumn() << endl; Table or1tab = xortab | seltab3; if (or1tab.nrow() != 8) { cout << "or1tab does not contain 8 rows" << endl; } ScalarColumn or1ab(or1tab, "ab"); cout << or1ab.getColumn() << endl; cout << "#columns in or1tab: " << or1tab.tableDesc().ncolumn() << endl; Table or2tab = seltab3 | xortab; if (or2tab.nrow() != 8) { cout << "or2tab does not contain 8 rows" << endl; } ScalarColumn or2ab(or2tab, "ab"); cout << or2ab.getColumn() << endl; cout << "#columns in or2tab: " << or2tab.tableDesc().ncolumn() << endl; Table exprtab = sortab(sortab.col("ab") >= 5); if (exprtab.nrow() != 5) { cout << "exprtab does not contain 5 rows" << endl; } ScalarColumn exprab(exprtab, "ab"); cout << exprab.getColumn() << endl; Table expr2tab = tab(tab.col("af") == "V3" || (tab.col("ab") >= 5 && tab.col("ab") < 8)); if (expr2tab.nrow() != 4) { cout << "expr2tab does not contain 4 rows" << endl; } ScalarColumn expr2ab(expr2tab, "ab"); cout << expr2ab.getColumn() << endl; } casacore-3.7.1/tables/Tables/test/tTable_2.data_v0/000077500000000000000000000000001476623553700217405ustar00rootroot00000000000000casacore-3.7.1/tables/Tables/test/tTable_2.data_v0/table.dat000066400000000000000000000050261476623553700235240ustar00rootroot00000000000000¾¾¾¾ Table PlainTable TableDesc1A test of class Table5 TableRecord RecordDesc5 TableRecord RecordDesc ScalarColumnDesc StManAipsIO  QStManColumnAipsIO  ÉStManColumnAipsIO @@@@@@@ @"@$@& StManColumnArrayAipsIOéStManColumnAipsIO ð?€@@@@€@ @À@àAAA A0A@APA`ApA€AˆAA˜A A¨A°A¸AÀAÈAÐAØAàAèAðAøBBBB BBBBB B$B(B,B0B4B8B<B@BDBHBLBPBTBXB\B`BdBhBlBpBtBxB|B€B‚B„B†BˆBŠBŒBŽBB’B”B–B˜BšBœBžB B¢B¤B¦B¨BªB¬B®B°B²B´B¶B¸BºB¼B¾BÀBÂBÄBÆBÈBÊBÌBÎBÐBÒBÔBÖBØBÚBÜBÞBàBâBäBæBèBêBìBîBðBòBôBöBøBúBüBþCCCCCCCCCC C C C C CCCCCCCCCCCCCCCCCCC C!C"C#C$C%C&C'C(C)C*C+C,C-C.C/C0C1C2C3C4C5C6C7C8C9C:C;C<C=C>C?C@CACBCCCDCECFCGCHCICJCKCLCMCNCOCPCQCRCSCTCUCVCWCXCYCZC[C\C]C^C_C`CaCbCcCdCeCfCgChCiCjCkClCmCnCozStManColumnIndArrayAipsIOMStManColumnAipsIO €ð`Ð@°  StManColumnArrayAipsIOéStManColumnAipsIO ð?€@@@@€@ @À@àAAA A0A@APA`ApA€AˆAA˜A A¨A°A¸AÀAÈAÐAØAàAèAðAøBBBB BBBBB B$B(B,B0B4B8B<B@BDBHBLBPBTBXB\B`BdBhBlBpBtBxB|B€B‚B„B†BˆBŠBŒBŽBB’B”B–B˜BšBœBžB B¢B¤B¦B¨BªB¬B®B°B²B´B¶B¸BºB¼B¾BÀBÂBÄBÆBÈBÊBÌBÎBÐBÒBÔBÖBØBÚBÜBÞBàBâBäBæBèBêBìBîBðBòBôBöBøBúBüBþCCCCCCCCCC C C C C CCCCCCCCCCCCCCCCCCC C!C"C#C$C%C&C'C(C)C*C+C,C-C.C/C0C1C2C3C4C5C6C7C8C9C:C;C<C=C>C?C@CACBCCCDCECFCGCHCICJCKCLCMCNCOCPCQCRCSCTCUCVCWCXCYCZC[C\C]C^C_C`CaCbCcCdCeCfCgChCiCjCkClCmCnCoQStManColumnAipsIO  casacore-3.7.1/tables/Tables/test/tTable_2.data_v0/table.f0i0000066400000000000000000000021601476623553700235060ustar00rootroot00000000000000p?€@@@@€@ @À@àAAA A0A@APA`ApA€AˆAA˜A A¨A°A¸AÀAÈAÐAØAàAèAðAøBBBB BBBBB B$B(B,B0B4B8B<B@BDBHBLBPBTBXB\B`BdBhBlBpBtBxB|B€B‚B„B†BˆBŠBŒBŽBB’B”B–B˜BšBœBžB B¢B¤B¦B¨BªB¬B®B°B²B´B¶B¸BºB¼B¾BÀBÂBÄBÆBÈBÊBÌBÎBÐBÒBÔBÖBØBÚBÜBÞBàBâBäBæBèBêBìBîBðBòBôBöBøBúBüBþCCCCCCCCCC C C C C CCCCCCCCCCCCCCCCCCC C!C"C#C$C%C&C'C(C)C*C+C,C-C.C/C0C1C2C3C4C5C6C7C8C9C:C;C<C=C>C?C@CACBCCCDCECFCGCHCICJCKCLCMCNCOCPCQCRCSCTCUCVCWCXCYCZC[C\C]C^C_C`CaCbCcCdCeCfCgChCiCjCkClCmCnCocasacore-3.7.1/tables/Tables/test/tTable_2.data_v0/table.f1000066400000000000000000000002041476623553700232530ustar00rootroot00000000000000¾¾¾¾€ StManAipsIO QStManColumnAipsIO  casacore-3.7.1/tables/Tables/test/tTable_2.data_v0/table.f2000066400000000000000000000003551476623553700232630ustar00rootroot00000000000000¾¾¾¾é StManAipsIO  QStManColumnAipsIO @@@€@ @À@àAAA A0A@eStManColumnAipsIO V0V1V2V3V4V5V6V7V8V9casacore-3.7.1/tables/Tables/test/tTable_2.data_v0/table.info000066400000000000000000000001211476623553700236760ustar00rootroot00000000000000Type = testtype SubType = testsubtype first readme line second test readme line casacore-3.7.1/tables/Tables/test/tTable_2.data_v1/000077500000000000000000000000001476623553700217415ustar00rootroot00000000000000casacore-3.7.1/tables/Tables/test/tTable_2.data_v1/table.dat000066400000000000000000000040041476623553700235200ustar00rootroot00000000000000¾¾¾¾Table PlainTable TableDesc1A test of class Table5 TableRecord RecordDesc5 TableRecord RecordDesc ScalarColumnDesc StManAipsIO  QStManColumnAipsIO  ÉStManColumnAipsIO @@@@@@@ @"@$@& StManColumnArrayAipsIOéStManColumnAipsIO ð?€@@@@€@ @À@àAAA A0A@APA`ApA€AˆAA˜A A¨A°A¸AÀAÈAÐAØAàAèAðAøBBBB BBBBB B$B(B,B0B4B8B<B@BDBHBLBPBTBXB\B`BdBhBlBpBtBxB|B€B‚B„B†BˆBŠBŒBŽBB’B”B–B˜BšBœBžB B¢B¤B¦B¨BªB¬B®B°B²B´B¶B¸BºB¼B¾BÀBÂBÄBÆBÈBÊBÌBÎBÐBÒBÔBÖBØBÚBÜBÞBàBâBäBæBèBêBìBîBðBòBôBöBøBúBüBþCCCCCCCCCC C C C C CCCCCCCCCCCCCCCCCCC C!C"C#C$C%C&C'C(C)C*C+C,C-C.C/C0C1C2C3C4C5C6C7C8C9C:C;C<C=C>C?C@CACBCCCDCECFCGCHCICJCKCLCMCNCOCPCQCRCSCTCUCVCWCXCYCZC[C\C]C^C_C`CaCbCcCdCeCfCgChCiCjCkClCmCnCozStManColumnIndArrayAipsIOMStManColumnAipsIO €ð`Ð@°  StManColumnArrayAipsIOéStManColumnAipsIO ð?€@@@@€@ @À@àAAA A0A@APA`ApA€AˆAA˜A A¨A°A¸AÀAÈAÐAØAàAèAðAøBBBB BBBBB B$B(B,B0B4B8B<B@BDBHBLBPBTBXB\B`BdBhBlBpBtBxB|B€B‚B„B†BˆBŠBŒBŽBB’B”B–B˜BšBœBžB B¢B¤B¦B¨BªB¬B®B°B²B´B¶B¸BºB¼B¾BÀBÂBÄBÆBÈBÊBÌBÎBÐBÒBÔBÖBØBÚBÜBÞBàBâBäBæBèBêBìBîBðBòBôBöBøBúBüBþCCCCCCCCCC C C C C CCCCCCCCCCCCCCCCCCC C!C"C#C$C%C&C'C(C)C*C+C,C-C.C/C0C1C2C3C4C5C6C7C8C9C:C;C<C=C>C?C@CACBCCCDCECFCGCHCICJCKCLCMCNCOCPCQCRCSCTCUCVCWCXCYCZC[C\C]C^C_C`CaCbCcCdCeCfCgChCiCjCkClCmCnCoQStManColumnAipsIO  casacore-3.7.1/tables/Tables/test/tTable_2.data_v1/table.f0i0000066400000000000000000000021601476623553700235070ustar00rootroot00000000000000p?€@@@@€@ @À@àAAA A0A@APA`ApA€AˆAA˜A A¨A°A¸AÀAÈAÐAØAàAèAðAøBBBB BBBBB B$B(B,B0B4B8B<B@BDBHBLBPBTBXB\B`BdBhBlBpBtBxB|B€B‚B„B†BˆBŠBŒBŽBB’B”B–B˜BšBœBžB B¢B¤B¦B¨BªB¬B®B°B²B´B¶B¸BºB¼B¾BÀBÂBÄBÆBÈBÊBÌBÎBÐBÒBÔBÖBØBÚBÜBÞBàBâBäBæBèBêBìBîBðBòBôBöBøBúBüBþCCCCCCCCCC C C C C CCCCCCCCCCCCCCCCCCC C!C"C#C$C%C&C'C(C)C*C+C,C-C.C/C0C1C2C3C4C5C6C7C8C9C:C;C<C=C>C?C@CACBCCCDCECFCGCHCICJCKCLCMCNCOCPCQCRCSCTCUCVCWCXCYCZC[C\C]C^C_C`CaCbCcCdCeCfCgChCiCjCkClCmCnCocasacore-3.7.1/tables/Tables/test/tTable_2.data_v1/table.f1000066400000000000000000000002041476623553700232540ustar00rootroot00000000000000¾¾¾¾€ StManAipsIO QStManColumnAipsIO  casacore-3.7.1/tables/Tables/test/tTable_2.data_v1/table.f2000066400000000000000000000003551476623553700232640ustar00rootroot00000000000000¾¾¾¾é StManAipsIO  QStManColumnAipsIO @@@€@ @À@àAAA A0A@eStManColumnAipsIO V0V1V2V3V4V5V6V7V8V9casacore-3.7.1/tables/Tables/test/tTable_2.data_v1/table.info000066400000000000000000000001211476623553700236770ustar00rootroot00000000000000Type = testtype SubType = testsubtype first readme line second test readme line casacore-3.7.1/tables/Tables/test/tTable_2.out000066400000000000000000000032321476623553700211540ustar00rootroot00000000000000>>> ------------------- start reading Table tTable_2.data_v0 <<< end reading Table ab ad ag arr1 arr2 arr3 ac ae af get scalar row 0 get array row 0 get scalar row 1 get array row 1 get scalar row 2 get array row 2 get scalar row 3 get array row 3 get scalar row 4 get array row 4 get scalar row 5 get array row 5 get scalar row 6 get array row 6 get scalar row 7 get array row 7 get scalar row 8 get array row 8 get scalar row 9 get array row 9 10 10 [12, 11, 10, 9, 8, 7, 6, 5, 4, 3] #columns in sortab: 9 [2, 3, 4, 5, 6, 7, 8, 9, 10, 11] #columns in sortab2: 9 [6, 8, 0, 3] #columns in seltab1: 9 [6, 8, 0, 3] #columns in seltab2: 3 [8, 0] #columns in seltab3: 3 [ae, ab, arr1] [1, 2, 4, 5, 7, 9] #columns in xortab: 9 [0, 1, 2, 4, 5, 7, 8, 9] #columns in or1tab: 9 [0, 1, 2, 4, 5, 7, 8, 9] #columns in or2tab: 3 [9, 8, 7, 6, 5] [3, 5, 6, 7] >>> ------------------- start reading Table tTable_2.data_v1 <<< end reading Table ab ad ag arr1 arr2 arr3 ac ae af get scalar row 0 get array row 0 get scalar row 1 get array row 1 get scalar row 2 get array row 2 get scalar row 3 get array row 3 get scalar row 4 get array row 4 get scalar row 5 get array row 5 get scalar row 6 get array row 6 get scalar row 7 get array row 7 get scalar row 8 get array row 8 get scalar row 9 get array row 9 10 10 [12, 11, 10, 9, 8, 7, 6, 5, 4, 3] #columns in sortab: 9 [2, 3, 4, 5, 6, 7, 8, 9, 10, 11] #columns in sortab2: 9 [6, 8, 0, 3] #columns in seltab1: 9 [6, 8, 0, 3] #columns in seltab2: 3 [8, 0] #columns in seltab3: 3 [ae, ab, arr1] [1, 2, 4, 5, 7, 9] #columns in xortab: 9 [0, 1, 2, 4, 5, 7, 8, 9] #columns in or1tab: 9 [0, 1, 2, 4, 5, 7, 8, 9] #columns in or2tab: 3 [9, 8, 7, 6, 5] [3, 5, 6, 7] casacore-3.7.1/tables/Tables/test/tTable_2.run000066400000000000000000000011411476623553700211460ustar00rootroot00000000000000#!/bin/sh #----------------------------------------------------------------------------- # Usage: tTable_2.run #----------------------------------------------------------------------------- # This scripts executes the program tTable_2 to test if new table # software can still read old tables. # The script supplies the names of all test tables found in the system. # It is meant to be run from assay, but can also be used standalone. #----------------------------------------------------------------------------- # Fundamentals. TABLES="$testsrcdir/tTable_2.data_v*" $casa_checktool ./tTable_2 $TABLES casacore-3.7.1/tables/Tables/test/tTable_3.cc000066400000000000000000000130541476623553700207360ustar00rootroot00000000000000//# tTable_3.cc: Program to test some performance aspects of the table system //# Copyright (C) 1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Program to test some performance aspects of the table system. // // First build a description. void a (uInt nrrow) { // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.comment() = "A test of class Table"; td.addColumn (ScalarColumnDesc("ab","Comment for column ab")); td.addColumn (ScalarColumnDesc("ad","comment for ad")); // Now create a new table from the description. // Use copy constructor to test if it works fine. // (newtab and newtabcp have the same underlying object). SetupNewTable newtab("tTable_3_tmp.data", td, Table::New); IncrementalStMan stman2; newtab.bindColumn ("ad", stman2); Table tab(newtab, TableLock(TableLock::PermanentLocking), nrrow); tab.tableInfo().setType ("testtype"); tab.tableInfo().setSubType ("testsubtype"); tab.tableInfo().readmeAddLine ("first readme line"); tab.tableInfo().readmeAddLine ("second test readme line"); ScalarColumn ab1(tab,"ab"); ScalarColumn ad(tab,"ad"); uInt i; for (i=0; i abv = ab1.getColumn(); timer.show(); timer.mark(); Vector adv = ad.getColumn(); timer.show(); timer.mark(); for (i=0; i abv1 = ab1.getColumnRange (Slicer(IPosition(1,0), IPosition(1,nrrow-1))); timer.show("range AIO"); timer.mark(); Vector adv1 = ad.getColumnRange (Slicer(IPosition(1,0), IPosition(1,nrrow-1))); timer.show("range ISM"); for (i=0; i abvcp(abv.size()); convertArray (abvcp, abv); Table rtab (tab(abvcp)); ScalarColumn ab1(rtab,"ab"); ScalarColumn ad(rtab,"ad"); Timer timer; { Vector abv = ab1.getColumn(); timer.show("cells AIO"); timer.mark(); Vector adv = ad.getColumn(); timer.show("cells ISM"); Bool del; uInt st = 0; const uInt* abvv = abv.getStorage(del); timer.mark(); for (i=0; ist && row abv1 = ab1.getColumnRange (Slicer(IPosition(1,0), IPosition(1,nrrow-1))); timer.show("cells/range AIO"); timer.mark(); Vector adv1 = ad.getColumnRange (Slicer(IPosition(1,0), IPosition(1,nrrow-1))); timer.show("cells/range ISM"); for (i=0; i 1) { nrrow = atoi (argv[1]); } cout << nrrow << " rows" << endl; a (nrrow); } catch (std::exception& x) { cout << "Caught an exception: " << x.what() << endl; return 1; } return 0; // exit with success status } casacore-3.7.1/tables/Tables/test/tTable_4.cc000066400000000000000000000242061476623553700207400ustar00rootroot00000000000000//# tTable_4.cc: Interactive test program for adding/removing columns //# Copyright (C) 2001 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Interactive test program for adding/removing columns. // // Remove the dirname from the table name in an error message. String removeDir (const String& msg) { String s = msg; s.gsub (Regex("/.*/t"), "t"); return s; } // First build a description. TableDesc makeDesc (Bool ask) { // Build the table description. TableDesc td("", "1", TableDesc::Scratch); String stman, stmanname; Int op; while (True) { try { if (ask) { cout << "0=end 1=scalar 2=dirarr 3=fixindarr 4=varindarr: "; } cin >> op; if (op >= 1 && op <= 4) { if (ask) { cout << "Column name,stman (a=aipsio s=ssm i=ism t=tsm),stmanname: "; } String str; cin >> str; Vector strs = stringToVector (str); AlwaysAssert (strs.nelements() >= 1 && strs.nelements() <= 3, AipsError); stman = "StandardStMan"; stmanname = ""; if (strs.nelements() > 1) { strs(1).downcase(); if (strs(1) == 'a') { stman = "StManAipsIO"; } else if (strs(1) == 'i') { stman = "IncrementalStMan"; } else if (strs(1) == 't') { stman = "TiledShapeStMan"; } else if (strs(1) != 's') { throw AipsError ("Invalid StMan given"); } stmanname = stman; if (strs.nelements() > 2) { stmanname = strs(2); } } if (op == 1) { td.addColumn (ScalarColumnDesc(strs(0), "", stman, stmanname)); } else if (op == 2) { td.addColumn (ArrayColumnDesc(strs(0), "", stman, stmanname, IPosition(1,10), ColumnDesc::Direct)); } else if (op == 3) { td.addColumn (ArrayColumnDesc(strs(0), "", stman, stmanname, IPosition(1,10), ColumnDesc::FixedShape)); } else if (op == 4) { td.addColumn (ArrayColumnDesc(strs(0), "", stman, stmanname)); } } else { break; } } catch (std::exception& x) { cout << x.what() << endl; } } // Create the hypercolumn descriptions for all tiled columns. std::map hcmap; for (uInt i=0; i::iterator iter = hcmap.find(cd.dataManagerGroup()); if (iter == hcmap.end()) { hcmap.insert (make_pair(cd.dataManagerGroup(), cd.name())); } else { iter->second += ("," + cd.name()); } } } for (auto& x : hcmap) { Vector vec = stringToVector(x.second); uInt ndim = 2; if (td.columnDesc(vec(0)).isScalar()) { ndim = 1; } td.defineHypercolumn (x.first, ndim, vec); } td.show (cout); return td; } void putData (Table& tab, const TableDesc& td, uInt startrow, uInt nrow) { for (uInt i=0; i col (tab, cdesc.name()); for (uInt i=0; i col (tab, cdesc.name()); Vector vec(10); for (uInt i=0; i col (tab, cdesc.name()); for (uInt i=0; i col (tab, cdesc.name()); Vector vec(10); for (uInt i=0; i 0, AipsError); const ColumnDesc& cdesc = tdn.columnDesc(0); if (tdn.ncolumn() == 1) { if (cdesc.dataManagerType() == cdesc.dataManagerGroup()) { tab.addColumn (cdesc, cdesc.dataManagerType(), False); } else { tab.addColumn (cdesc, cdesc.dataManagerGroup(), True); } } else { if (cdesc.dataManagerType() == "StManAipsIO") { StManAipsIO stman(cdesc.dataManagerGroup()); tab.addColumn (tdn, stman); } else if (cdesc.dataManagerType() == "IncrementalStMan") { IncrementalStMan stman(cdesc.dataManagerGroup()); tab.addColumn (tdn, stman); } else if (cdesc.dataManagerType() == "StandardStMan") { StandardStMan stman(cdesc.dataManagerGroup()); tab.addColumn (tdn, stman); } else if (cdesc.dataManagerType() == "TiledColumnStMan") { TiledColumnStMan stman(cdesc.dataManagerGroup(), IPosition(2,10,2)); tab.addColumn (tdn, stman); } else { TiledShapeStMan stman(cdesc.dataManagerGroup(), IPosition(2,10,2)); tab.addColumn (tdn, stman); } } putData (tab, tdn, 0, tab.nrow()); cout << " Added and initialized " << tdn.ncolumn() << " columns" << endl; } void doTable (Bool ask, const TableDesc& td) { // Now create a new table from the description. // Use copy constructor to test if it works fine. // (newtab and newtabcp have the same underlying object). SetupNewTable newtab("tTable_4_tmp.data", td, Table::New); Table tab(newtab); Int op; while (True) { try { if (ask) { cout << "0=end 1=reopen 2=addcols 3=removecols 4=addrow 5=show " << endl << "6=recreate, 7=check, 8=refcol, 9=deepcopy: "; } cin >> op; if (op == 1) { tab = Table(); tab = Table("tTable_4_tmp.data", Table::Update); cout << " Reopened table" << endl; } else if (op == 2) { addCols (ask, tab); } else if (op == 3) { String str; if (ask) { cout << "Column names: "; } cin >> str; tab.removeColumn (stringToVector(str)); cout << " Removed columns " << str << endl; } else if (op == 4) { uInt n = tab.nrow(); tab.addRow(); putData (tab, tab.tableDesc(), n, 1); cout << " Added and initialized 1 row" << endl; } else if (op == 8) { String str; if (ask) { cout << "Column names: "; } cin >> str; Block cols = makeBlock(stringToVector(str)); tab = tab.project (cols); } else if (op == 5) { tab.actualTableDesc().show (cout); Record rec = tab.dataManagerInfo(); cout << "Data Managers:" << endl; for (uInt i=0; i #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // // Test program for a very large table. // // This program tests a very large (i.e., with a large number of rows) // using the IncrementalStMan storage manager to keep the file sizes very small. rownr_t nrowStep = 500000000; /// 2**32 = 4294967296 void createTableISM() { cout << "Creating ISM table with 10*" << nrowStep << " rows" << endl; // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.addColumn (ScalarColumnDesc("ad")); // Now create a new table from the description. SetupNewTable newtab("tVeryBigTable_tmp.ism", td, Table::New); // Create a storage manager for it. IncrementalStMan sm1 ("ISM", 256, False); newtab.bindAll (sm1); Table tab(newtab, 0); ScalarColumn ad(tab,"ad"); rownr_t rownr = 0; for (Int i=0; i<10; i++) { tab.addRow (nrowStep); ad.put (rownr, i); rownr += nrowStep; } AlwaysAssertExit (tab.nrow() == 10 * nrowStep); } void createTableSSM() { cout << "Creating SSM table with 10*" << nrowStep << " rows" << endl; // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.addColumn (ScalarColumnDesc("ad")); // Now create a new table from the description. SetupNewTable newtab("tVeryBigTable_tmp.ssm", td, Table::New); // Create a storage manager for it. StandardStMan sm1 ("SSM", 4*1024*1024); newtab.bindAll (sm1); Table tab(newtab, 0); ScalarColumn ad(tab,"ad"); rownr_t rownr = 0; for (Int i=0; i<10; i++) { tab.addRow (nrowStep); ad.put (rownr, i); ad.put (rownr+1, i); rownr += nrowStep/2; ad.put (rownr-1, i); ad.put (rownr, i); ad.put (rownr+1, i); rownr += nrowStep/2; ad.put (rownr-2, i); ad.put (rownr-1, i); } AlwaysAssertExit (tab.nrow() == 10 * nrowStep); } void createTableTSM() { cout << "Creating TSM table with 10*" << nrowStep << " rows" << endl; // Build the table description. TableDesc td("", "1", TableDesc::Scratch); td.addColumn (ArrayColumnDesc("ad", IPosition(1,1))); // Now create a new table from the description. SetupNewTable newtab("tVeryBigTable_tmp.tsm", td, Table::New); // Create a storage manager for it. ///TiledColumnStMan sm1 ("TSM", IPosition(2,1,1024*1024)); TiledShapeStMan sm1 ("TSM", IPosition(2,1,1024*1024)); newtab.bindAll (sm1); Table tab(newtab, 0); ArrayColumn ad(tab,"ad"); Vector vec(1); rownr_t rownr = 0; for (Int i=0; i<10; i++) { tab.addRow (nrowStep); vec[0] = i; ad.put (rownr, vec); ad.put (rownr+1, vec); rownr += nrowStep/2; ad.put (rownr-1, vec); ad.put (rownr, vec); ad.put (rownr+1, vec); rownr += nrowStep/2; ad.put (rownr-2, vec); ad.put (rownr-1, vec); } AlwaysAssertExit (tab.nrow() == 10 * nrowStep); } void readTable (const String& name, Int add) { cout << "Reading some rows from ISM table with " << 10*nrowStep << " rows" << endl; Table tab(name); AlwaysAssertExit (tab.nrow() == 10 * nrowStep); ScalarColumn ad(tab,"ad"); rownr_t rownr = 0; for (Int i=0; i<10; i++) { AlwaysAssertExit (ad(rownr) == i+add); AlwaysAssertExit (ad(rownr+1) == i); rownr += nrowStep/2; AlwaysAssertExit (ad(rownr-1) == i); AlwaysAssertExit (ad(rownr) == i+add); AlwaysAssertExit (ad(rownr+1) == i); rownr += nrowStep/2; AlwaysAssertExit (ad(rownr-2) == i); AlwaysAssertExit (ad(rownr-1) == i+add); } } void testSelection() { cout << "Testing selection ..." << endl; { // Take a few rows as a RefTable and make it persistent. Vector rows(30); uInt inx = 0; rownr_t rownr = 0; for (uInt i=0; i<10; ++i) { rows[inx++] = rownr; rownr += nrowStep/2; rows[inx++] = rownr; rownr += nrowStep/2; rows[inx++] = rownr-1; } Table tab("tVeryBigTable_tmp.ism"); Table seltab = tab(rows); seltab.rename ("tVeryBigTable_tmp.sel", Table::New); } { // Open the RefTable and check the data. Table tab("tVeryBigTable_tmp.sel"); ScalarColumn ad(tab,"ad"); rownr_t rownr = 0; for (Int i=0; i<10; i++) { AlwaysAssertExit (ad(rownr) == i); rownr++; AlwaysAssertExit (ad(rownr) == i); rownr++; AlwaysAssertExit (ad(rownr) == i); rownr++; } } } void testUpdate() { Table tab("tVeryBigTable_tmp.ism", Table::Update); ScalarColumn ad(tab,"ad"); rownr_t rownr = 0; for (uInt i=0; i<10; ++i) { ad.put (rownr, i+100); rownr += nrowStep/2; ad.put (rownr, i+100); rownr += nrowStep/2; ad.put (rownr-1, i+100); } } int main() { try { createTableISM(); readTable("tVeryBigTable_tmp.ism", 0); ///createTableSSM(); ///readTable("tVeryBigTable_tmp.ssm", 0); ///createTableTSM(); testSelection(); testUpdate(); readTable("tVeryBigTable_tmp.ism", 100); } catch (AipsError& x) { cout << "Caught an exception: " << x.getMesg() << endl; return 1; } return 0; // exit with success status } casacore-3.7.1/tables/apps/000077500000000000000000000000001476623553700155415ustar00rootroot00000000000000casacore-3.7.1/tables/apps/CMakeLists.txt000066400000000000000000000003501476623553700202770ustar00rootroot00000000000000foreach(prog showtableinfo showtablelock taql lsmf tomf tablefromascii) add_executable (${prog} ${prog}.cc) add_pch_support(${prog}) target_link_libraries (${prog} casa_tables) install(TARGETS ${prog}) endforeach() casacore-3.7.1/tables/apps/lsmf.cc000066400000000000000000000067751476623553700170300ustar00rootroot00000000000000//# lsmf.cc: This program shows meta info of a multifile //# Copyright (C) 2014 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include using namespace casacore; using namespace std; void show (const std::shared_ptr& mfile, Bool showbl, const String& mftype) { cout << endl; cout << mftype << " = " << mfile->fileName() << endl; cout << " blocksize = " << mfile->blockSize() << " nfile = " << mfile->nfile() << " nfreeblocks = " << mfile->freeBlocks().size() << endl; if (showbl) { cout << " freeblocks = " << mfile->freeBlocks() << endl; } for (uInt i=0; infile(); ++i) { const MultiFileInfo& info = mfile->info()[i]; cout << ' ' << info.name << " size=" << info.fsize << " nblocks=" << (info.fsize+mfile->blockSize()-1) / mfile->blockSize() << endl; if (showbl) { cout << ' ' << info.blockNrs << endl; } } } int main (int argc, char* argv[]) { try { vector fname; Bool showbl = False; for (int argnr=1; argnr::const_iterator iter=fname.begin(); iter!=fname.end(); ++iter) { if (iter->empty()) { cerr << "lsmf: Empty file name given" << endl; } else { if (! File(*iter).exists()) { cerr << "lsmf: File " << *iter << " does not exist" << endl; } else if (HDF5File::isHDF5(*iter)) { std::shared_ptr mfile (new MultiHDF5(*iter, ByteIO::Old)); show (mfile, showbl, "MultiHDF5"); } else { std::shared_ptr mfile (new MultiFile(*iter, ByteIO::Old)); show (mfile, showbl, "MultiFile"); } } } cout << endl; } catch (const std::exception& x) { cerr << x.what() << endl; return 1; } return 0; } casacore-3.7.1/tables/apps/showtableinfo.cc000066400000000000000000000125731476623553700207240ustar00rootroot00000000000000//# showtableinfo.cc: This program shows table info and contents. //# Copyright (C) 2011 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include // for mkstemp #include // for strerror #include #include using namespace casacore; using namespace std; int main (int argc, char* argv[]) { try { // Read the input parameters. Input inputs(1); inputs.version("2016Feb17GvD"); inputs.create("in", "", "Input table", "string"); inputs.create("dm", "T", "Show data manager info?", "bool"); inputs.create("col", "T", "Show column info?", "bool"); inputs.create("tabkey", "F", "Show table keywords?", "bool"); inputs.create("colkey", "F", "Show column keywords?", "bool"); inputs.create("maxval", "25", "Max nr of array values to show", "int"); inputs.create("sub", "F", "Show info for all subtables?", "bool"); inputs.create("sort", "F", "Sort columns in alphabetical order?", "bool"); inputs.create("corder", "F", "Show shapes in C-order?", "bool"); inputs.create("browse", "F", "Browse contents of table?", "bool"); inputs.create("selcol", "", "TaQL column selection string", "string"); inputs.create("selrow", "", "TaQL row selection string", "string"); inputs.create("selsort", "", "TaQL sort string", "string"); inputs.readArguments(argc, argv); // Get and check the input specification. String in (inputs.getString("in")); if (in.empty()) { throw AipsError(" an input table name must be given"); } Bool showdm = inputs.getBool("dm"); Bool showcol = inputs.getBool("col"); Bool showtabkey = inputs.getBool("tabkey"); Bool showcolkey = inputs.getBool("colkey"); Int maxval = inputs.getInt ("maxval"); Bool showsub = inputs.getBool("sub"); Bool sortcol = inputs.getBool("sort"); Bool cOrder = inputs.getBool ("corder"); Bool browse = inputs.getBool("browse"); String selcol (inputs.getString("selcol")); String selrow (inputs.getString("selrow")); String selsort (inputs.getString("selsort")); // Do the selection if needed. Table table(in); Table seltab(table); if (! (selcol.empty() && selrow.empty() && selsort.empty())) { String command ("select "); if (!selcol.empty()) { command += selcol; } command += " from " + in; if (!selrow.empty()) { command += " where " + selrow; } if (!selsort.empty()) { command += " orderby " + selsort; } clog << "TaQL command = " << command << endl; seltab = tableCommand (command).table(); } // Show the table structure. table.showStructure (cout, showdm, showcol, showsub, sortcol, cOrder); table.showKeywords (cout, showsub, showtabkey, showcolkey, maxval); if (browse) { // Need to make table persistent for casabrowser. String tmpName; if (seltab.tableName() != table.tableName()) { // g++ gives a deprecated warning for tempnam. // Therefore we use mkstemp and close/unlink the file immediately. char tmpnm[] = "/tmp/shtabXXXXXX"; int fd = mkstemp(tmpnm); tmpName = tmpnm; ///cout << "tmpnm="< #include #include #include #include #include #include using namespace casacore; using namespace std; void showVerbose (const String& lockFileName) { LockFile lfile(lockFileName); TableSyncData data; // Read the lock info from the file into a buffer. // Thereafter interpret the data read. lfile.getInfo (data.memoryIO()); rownr_t nrrow; uInt nrcolumn; Bool tableChanged; Block dataManChanged; data.read (nrrow, nrcolumn, tableChanged, dataManChanged); // Show the data. cout << "Lock file info (of " << data.memoryIO().length() << " bytes)" << endl; cout << " nrows: " << nrrow << endl; cout << " ncolumns: " << nrcolumn << endl; cout << " table changed: " << tableChanged << endl; cout << " dataman changed: " << dataManChanged << endl; cout << " modify counter: " << data.getModifyCounter() << endl; int nrid = lfile.reqIds()[0]; cout << " " << nrid << " outstanding lock requests from other processes" << endl; } int main (int argc, char* argv[]) { if (TableLock::lockingDisabled()) { cerr << "Note: table locking is disabled because Casacore " << "was built with -DAIPS_TABLES_NOLOCKING" << endl; } int starg = 1; Bool verbose = False; if (argc > starg && String(argv[starg]) == "-v") { verbose = True; starg += 1; } if (argc <= starg) { cerr << "Use as: showtablelock [-v] tablename" << endl; cerr << " -v verbose" << endl; return 1; } try { String tablename(argv[starg]); tablename = Path(tablename).absoluteName(); if (! Table::isReadable (tablename)) { cerr << "Table " << tablename << " does not exist (or not readable)" << endl; return 1; } String lockFileName(tablename + "/table.lock"); uInt pid = 0; Bool permLocked = False; uInt type = LockFile::showLock (pid, permLocked, lockFileName); String perm; if (permLocked) { perm = "permanently "; } if (type == 1) { cout << "Table " << tablename << " is opened (but not locked) in process " << pid << endl; } else if (type == 2) { cout << "Table " << tablename << " is " << perm << "read-locked in process " << pid << endl; } else if (type == 3) { cout << "Table " << tablename << " is " << perm << "write-locked in process " << pid << endl; } else { cout << "Table " << tablename << " is neither opened nor locked in another process" << endl; } if (verbose) { showVerbose (lockFileName); } } catch (std::exception& x) { cerr << x.what() << endl; return 1; } return 0; } casacore-3.7.1/tables/apps/tablefromascii.cc000066400000000000000000000110211476623553700210270ustar00rootroot00000000000000//# tablefromascii.cc: Convert an ASCII file to a table //# Copyright (C) 2015 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include using namespace casacore; using namespace std; // Convert an ASCII file to a table // // This program converts an ASCII table to a table using the class // ReadAsciiTable. It can only handle scalar columns. // The separator between the fields in the ASCII file can be specified. // int main (int argc, char* argv[]) { try { Input inputs(1); inputs.version("2016Jan08GvD"); inputs.create("in", "", "Input ASCII file", "string"); inputs.create("out", "", "Output table name", "string"); inputs.create("headerfile", "", "Name of optional file containing headers", "string"); inputs.create("autoheader", "F", "Determine header automatically?", "bool"); inputs.create("autoshape", "[]", "Shape if all columns are treated as one array", "string"); inputs.create("columnnames", "", "Comma separated names of the columns", "string"); inputs.create("datatypes", "", "Comma separated data types of the columns", "string"); inputs.create("sep", " ", "One character separator between values", "string"); inputs.create("commentmarker", "", "Regex indicating comments", "string"); inputs.create("firstline", "1", "First line to process", "int"); inputs.create("lastline", "-1", "Last line to process (<0 is till end)", "int"); inputs.readArguments(argc, argv); // Get and check the input specification. String in (inputs.getString("in")); String out (inputs.getString("out")); String hdrfile (inputs.getString("headerfile")); Bool autohdr (inputs.getBool("autoheader")); String autoshp (inputs.getString("autoshape")); String colnm (inputs.getString("columnnames")); String dtype (inputs.getString("datatypes")); String sep (inputs.getString("sep")); String comm (inputs.getString("commentmarker")); Int first (inputs.getInt("firstline")); Int last (inputs.getInt("lastline")); if (in.empty()) { throw AipsError(" an input file name must be given"); } if (out.empty()) { throw AipsError(" an output table name must be given"); } if (sep.size() != 1) { throw AipsError(" separator must be a single character"); } // Split column names and datatypes. Vector cols (stringToVector(colnm)); Vector typs (stringToVector(dtype)); // Get the auto-shape (if given). Vector vec; std::istringstream is(autoshp); if (! read(is, vec, 0, False)) { throw AipsError (" '" + autoshp + "' is an invalid autoshape (maybe enclose in [])"); } IPosition shp(vec); // Convert the text file to a table and get table object. TableProxy tp(in, hdrfile, out, autohdr, shp, sep, comm, first, last, cols, typs); cout << "Created table " << tp.table().tableName() << " with " << tp.table().nrow() << " rows" << endl; cout << " Used format = " << tp.getAsciiFormat() << endl; } catch (const std::exception& x) { cerr << "Exception: " << x.what() << endl; return 1; } return 0; } casacore-3.7.1/tables/apps/taql.cc000066400000000000000000001302001476623553700170050ustar00rootroot00000000000000//# taql.cc: This program executes TaQL commands //# Copyright (C) 2009 //# Associated Universities, Inc. Washington DC, USA. //# //# 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., //# 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_READLINE # include # include #endif using namespace casacore; using namespace std; // // Execute any TaQL command from the shell. // If a command is given at the taql command, only that command is executed. // It is possible to execute command from a file using the -f option. // If no command is given, an interactive session is started where options and/or // commands can be given. In there the -f option can be used as well. // -h or --help gives help info for the taql program. // The command show or help gives help about the TaQL syntax and functionality. // //# The following functions are used (possibly recursively): //# - main calls 'executeArgs' //# - 'executeArgs' parses options and calls 'execCommand' if a command is given, calls //# 'execFileCommands' if -f is given or calls 'askCommands' if no command given, //# but only if called from main //# - 'execCommand' handles t=command and calls 'taqlComand' if a command is given //# - 'execFileCommands' executes commands given in a file; splits and calls 'executeArgs' //# - 'askCommands' ask commands, splits and calls 'executeArgs' //# - 'taqlCommand' executes a TaQL command //# //# Options can be given at the start of any command line. If only options are given, they are //# persistent. If a command is given as well, the options are only valid for that command. //# The options set at a lower level, are not used at higher levels. //# //# The tableMap (mapping of name to Table object) is global, thus can be used at any level. // Define the type for the map of name to (resulttable,command). typedef map > TableMap; // Define Deleter objects for a shared_ptr // cout and cerr should not be deleted. class Deleter { public: Deleter (Bool deleteIt) : itsDelete (deleteIt) {} void operator() (ostream* ptr) { if (itsDelete) delete ptr; } private: Bool itsDelete; }; struct Options { Bool printSelect; Bool printAuto; Bool printMeasure; Bool printHeader; Bool printCommand; Bool printNRows; rownr_t maxNRows; String separator; String fname; String style; String outName; std::shared_ptr stream; Options() : printSelect (False), // print explicitly select result? printAuto (True), // print automatically if printSelect=False? printMeasure (True), // print as measures when printing result? printHeader (True), // print column header when printing result? printCommand (False), // print command? printNRows (True), // print nr of rows handled? maxNRows (50), // max #rows to print for auto print separator ('\t'), // default separator between printed columns style ("python"), outName ("stdout"), stream (&cout, Deleter(False)) // default stdout {} }; //# Forward declare. // Parse and execute the arguments given. topLevel tells if this is the top level. // If so, an interactive session is started if no TaQL command is given. It also tells if // possible quotes are removed from option values. // The TableMap holds a map of name to temporary table. // It returns False if exit (or quit) is given. Bool executeArgs (const vector args, Bool topLevel, TableMap& tableMap, Options& options); void removeCR (String& line) { // Remove possible carriage-return (in DOS files). if (line.size() > 0 && line[line.size() - 1] == '\r') { line = line.substr(0, line.size()-1); } } uInt lskipws (const String& value, uInt st, uInt end) { for (; stst && isspace(value[end-1]); --end) ; return end; } uInt skipQuoted (const String& str, uInt st, uInt end) { // Skip until the matching end quote is found. char ch = str[st++]; for (; st splitLine (const String& line) { vector parts; // Skip leading and trailing whitespace. uInt st = lskipws (line, 0, line.size()); if (!line.empty() && line[st] != '#') { // skip if only comment uInt end = rskipws (line, st, line.size()); uInt stcmd = st; // first non-blank character while (st splitWS (const String& str) { vector parts; String part; int qpos = -1; for (uInt i=0; i 0) { parts.push_back (part); part = String(); } continue; } } else if (str[i] == str[qpos]) { // End of quoted string. qpos = -1; } part += str[i]; } if (qpos >= 0) { throw AipsError ("Unbalanced quoted string at position " + String::toString(qpos) + " in " + str); } if (part.size() > 0) { parts.push_back (part); // last part } return parts; } // Read a line, if possible using the readline library to make command // editing and history possible. #ifdef HAVE_READLINE Bool readLine (String& line, const String& prompt) { char* str = readline(prompt.c_str()); if (!str) return False; line = String(str); removeCR (line); free(str); return True; } #else Bool readLine (String& line, const String& prompt) { if (!prompt.empty()) cerr << prompt; getline (cin, line); removeCR (line); return cin.good(); } #endif // Read a line until a non-empty line or ^D or quit is read. Bool readLineSkip (String& line, const String& prompt) { Bool fnd = False; while (!fnd && readLine (line, prompt)) { fnd = !line.empty(); } #ifdef HAVE_READLINE if (fnd) add_history (line.c_str()); #endif return fnd; } // Show a date/time. Do not show time part if 0. void showTime (const MVTime& time, ostream& os) { Double val = time.day(); if (val == floor(val)) { time.print (os, MVTime::Format (MVTime::formatTypes(MVTime::DMY | MVTime::NO_TIME))); } else { time.print (os, MVTime::Format(MVTime::DMY, 9)); } } void showTime (double time, const String& unit, ostream& os) { showTime (MVTime (Quantity(time, unit)), os); } void showTime (const Array& times, const String& unit, ostream& os) { Quantity q(0., unit); Bool firstTime = True; cout << '['; Array::const_iterator endIter = times.end(); for (Array::const_iterator iter= times.begin(); iter != endIter; ++iter) { if (!firstTime) { os << ", "; } else { firstTime = False; } q.setValue (*iter); showTime (q, os); } os << ']'; } // Show values representing MPositions. void showPos (const Array& pos, const Vector& units, ostream& os) { AlwaysAssert (pos.size() % units.size() == 0, AipsError); Vector q(units.size()); for (uInt i=0; i< units.size(); ++i) { q[i] = Quantity(0., units[i]); } Bool firstTime = True; if (pos.size() != units.size()) { os << '['; } Array::const_iterator endIter = pos.end(); for (Array::const_iterator iter= pos.begin(); iter != endIter;) { if (!firstTime) { os << ", "; } else { firstTime = False; } for (uInt i=0; i& dir, const Vector& units, ostream& os) { AlwaysAssert (dir.size() % units.size() == 0, AipsError); Vector q(units.size()); for (uInt i=0; i< units.size(); ++i) { q[i] = Quantity(0., units[i]); } Bool firstTime = True; if (dir.size() != units.size()) { os << '['; } Array::const_iterator endIter = dir.end(); for (Array::const_iterator iter= dir.begin(); iter != endIter;) { if (!firstTime) { os << ", "; } else { firstTime = False; } os << '['; for (uInt i=0; i void showArray (const Array& arr, ostream& os) { if (arr.size() == 1) { os << arr.data()[0]; } else { os << arr; } } template<> void showArray (const Array& arr, ostream& os) { if (arr.size() == 1) { showTime (arr.data()[0], os); } else { Bool firstTime = True; os << '['; Array::const_iterator endIter = arr.end(); for (Array::const_iterator iter= arr.begin(); iter != endIter; ++iter) { if (!firstTime) { os << ", "; } else { firstTime = False; } showTime (*iter, os); } os << ']'; } } // Show the required columns of the table. // First test if they exist and contain scalars or arrays. void showTable (const Table& tab, const Vector& colnam, Bool printMeasure, const String& separator, ostream& os) { uInt nrcol = 0; PtrBlock tableColumns(colnam.nelements()); Block > timeUnit(colnam.nelements()); Block > posUnit(colnam.nelements()); Block > dirUnit(colnam.nelements()); Block colUnits(colnam.nelements()); Bool hasUnits = False; for (uInt i=0; icolumnDesc().isScalar() && ! tableColumns[nrcol]->columnDesc().isArray()) { os << "Column " << colnam(i) << " contains neither scalars nor arrays" << endl; delete tableColumns[nrcol]; tableColumns[nrcol] = 0; }else{ // Get possible units. const TableRecord& keys = tableColumns[nrcol]->keywordSet(); Vector units; if (keys.isDefined ("QuantumUnits")) { units = keys.asArrayString("QuantumUnits"); if (! units.empty()) { colUnits[nrcol] = units[0]; hasUnits = True; } } // If needed, see if it is a Measure type we know of. if (printMeasure) { if (keys.isDefined ("MEASINFO")) { const TableRecord& meas = keys.subRecord("MEASINFO"); if (meas.isDefined ("type")) { String type = meas.asString("type"); if (type == "epoch") { timeUnit[nrcol] = units; } else if (type == "position") { posUnit[nrcol] = units; } else if (type == "direction") { dirUnit[nrcol] = units; } } } } nrcol++; } } } if (nrcol == 0) { return; } // Show possible units. if (hasUnits) { os << "Unit: "; for (uInt j=0; j 0) { os << separator; } os << colUnits[j]; } os << endl; } // Use TableProxy, so we can be type-agnostic. TableProxy proxy(tab); for (rownr_t i=0; i 0) { os << separator; } if (! tableColumns[j]->isDefined (i)) { os << " no_array"; } else { ValueHolder vh(proxy.getCell (tableColumns[j]->columnDesc().name(), i)); if (! timeUnit[j].empty()) { if (tableColumns[j]->columnDesc().isScalar()) { showTime (vh.asDouble(), timeUnit[j][0], os); } else { showTime (vh.asArrayDouble(), timeUnit[j][0], os); } } else if (! posUnit[j].empty()) { showPos (vh.asArrayDouble(), posUnit[j], os); } else if (! dirUnit[j].empty()) { showDir (vh.asArrayDouble(), dirUnit[j], os); } else if (vh.dataType() == TpBool) { // std::boolalpha seems to persist. os << (vh.asBool() ? "true" : "false"); } else { os << vh; } } } os << endl; } for (uInt i=0; i(expr.getNodeRep()); if (nodePtr != 0) { // The node represents a part of an array; get its index node. const TableExprNodeIndex* inxNode = nodePtr->getIndexNode(); // If a constant index accessing a single element, // get the Slicer defining the index. if (inxNode->isConstant() && inxNode->isSingle()) { const Slicer& indices = inxNode->getConstantSlicer(); // Extract the index from it. os << "Index: " << indices.start() << endl; } } const Unit& unit = expr.unit(); if (! unit.empty()) { os << "Unit: " << unit.getName() << endl; } Vector rownrs (expr.nrow()); indgen (rownrs); if (expr.isScalar()) { switch (expr.getColumnDataType()) { case TpBool: showArray (expr.getColumnBool (rownrs), os); break; case TpUChar: showArray (expr.getColumnuChar (rownrs), os); break; case TpShort: showArray (expr.getColumnShort (rownrs), os); break; case TpUShort: showArray (expr.getColumnuShort (rownrs), os); break; case TpInt: showArray (expr.getColumnInt (rownrs), os); break; case TpUInt: showArray (expr.getColumnuInt (rownrs), os); break; case TpInt64: showArray (expr.getColumnInt64 (rownrs), os); break; case TpFloat: showArray (expr.getColumnFloat (rownrs), os); break; case TpDouble: showArray (expr.getColumnDouble (rownrs), os); break; case TpComplex: showArray (expr.getColumnComplex (rownrs), os); break; case TpDComplex: showArray (expr.getColumnDComplex (rownrs), os); break; case TpString: showArray (expr.getColumnString (rownrs), os); break; case TpQuantity: { AlwaysAssert (expr.getNodeRep()->dataType() == TableExprNodeRep::NTDate, AipsError); MVTime time; if (expr.nrow() != 1) os << '['; for (rownr_t i=0; i 0) os << ", "; expr.get (i, time); showTime (time, os); } if (expr.nrow() != 1) os << ']'; } break; default: os << "Unknown expression scalar type " << expr.getColumnDataType(); } os << endl; } else { for (rownr_t i=0; i 1) { os << " row " << i << ": "; } switch (expr.dataType()) { case TpBool: showArray (expr.getArrayBool(i), os); break; case TpInt64: showArray (expr.getArrayInt(i), os); break; case TpDouble: showArray (expr.getArrayDouble(i), os); break; case TpDComplex: showArray (expr.getArrayDComplex(i), os); break; case TpString: showArray (expr.getArrayString(i), os); break; case TpQuantity: { AlwaysAssert (expr.getNodeRep()->dataType() == TableExprNodeRep::NTDate, AipsError); showArray (expr.getArrayDate(i), os); } break; default: os << "Unknown expression array type " << expr.dataType(); } os << endl; } } } void showParseError (const TableParseError& x) { // Try to highlight a parse error on a tty. A color init //# string consists of one or more of the following numeric codes: //# Attribute codes: //# 00=none 01=bold 04=underscore 05=blink 07=reverse 08=concealed //# Text color codes: //# 30=black 31=red 32=green 33=yellow 34=blue 35=magenta 36=cyan 37=white //# Background color codes: //# 40=black 41=red 42=green 43=yellow 44=blue 45=magenta 46=cyan 47=white // cerr has fd 2 (per C++ standard) const String& msg(x.what()); if (isatty(2) && x.pos() >= 0) { // Cater for leading part of the message. int errLen = x.token().size(); int errPos = x.pos() - errLen + 23; if (msg[errPos + errLen -1] == '\n') { errLen--; } // For now use yellow background (43) to highlight the error. cerr << msg.substr(0, errPos) << "\033[1;43m" << msg.substr(errPos, errLen); cerr << "\033[0m" << msg.substr(errPos+errLen) << endl; if (errLen == 0) { cerr << "Probably a missing parenthesis or bracket" << endl; } } else { cerr << msg <& tempTables) { // If no command is given, assume it is SELECT. // Only show results for SELECT, COUNT and CALC. Bool addComm = False; Bool showHelp = False; Bool printSelect = options.printSelect; Bool printAuto = options.printAuto; Bool printMeasure = options.printMeasure; Bool printCommand = options.printCommand; Bool printNRows = options.printNRows; Bool printHeader = options.printHeader; rownr_t maxNRows = options.maxNRows; ostream& os = *(options.stream); String::size_type spos = command.find_first_not_of (' '); if (spos != String::npos) { String::size_type epos = command.find (' ', spos); if (epos == String::npos) { epos = command.size(); } String s = command.substr(spos, epos-spos); s.downcase(); showHelp = (s=="show" || s=="help"); addComm = !(s=="with" || s=="select" || s=="update" || s=="insert" || s=="calc" || s=="delete" || s=="count" || s=="create" || s=="createtable" || s=="drop" || s=="droptable" || s=="alter" || s=="altertable" || s=="using" || s=="usingstyle" || s=="time" || showHelp); } String strc(command); if (addComm) { strc = "SELECT " + command; printSelect = True; printHeader = False; printCommand = False; printNRows = False; } String style(options.style); if (style.empty()) { style = "glish"; } strc = "using style " + style + ' ' + strc; Vector colNames; String cmd; TaQLResult result = tableCommand (strc, tempTables, colNames, cmd); cmd.downcase(); // Show result of COUNT as well. if (cmd == "count") { colNames.resize (colNames.size() + 1, True); colNames[colNames.size() - 1] = "_COUNT_"; } if (printCommand && !showHelp) { if (!varName.empty()) { os << varName << " = "; } os << strc << endl; os << " has been executed" << endl; } Table tabp; if (result.isTable()) { tabp = result.table(); if (printNRows) { os << " " << cmd << " result of " << tabp.nrow() << " rows" << endl; } // Only show the selected table columns for SELECT and COUNT. if ((cmd == "select" || cmd == "count") && colNames.size() > 0) { // Only show if explicit print or possibly if auto. if (printSelect || (printAuto && maxNRows>0)) { if (printHeader) { // Show the selected column names. os << colNames.nelements() << " selected columns: "; for (uInt i=0; i maxNRows) { showSubset = True; Vector rownrs(maxNRows); indgen (rownrs); tabc = tabp(rownrs); } showTable (tabc, colNames, printMeasure, options.separator, os); if (showSubset) { os << " Note: only the first " << maxNRows << " rows are shown (out of " << tabp.nrow() << ')' << endl; } } } } else { showExpr (result.node(), os); } return tabp; } void showVersion() { cerr << "taql version " << getVersionCASA() << endl; } void showHelp() { cerr << endl; cerr << "TaQL is the query language for casacore tables and is described at" << endl; cerr << " http://casacore.github.io/casacore-notes/199.html" << endl; cerr << "taql can be started with multiple arguments containing options and" << endl; cerr << "an optional TaQL command as the last argument(s)." << endl; cerr << "Using the -f option commands are taken from a file where commands can be" << endl; cerr << "split over multiple lines and a ; has to be used to delimit a command" << endl; cerr << "After a # a line can contain comments." << endl; cerr << "taql will run interactively if no command nor file is given. If possible," << endl; cerr << "interactive commands are kept in $HOME/.taql_history for later reuse." << endl; cerr << "Use q, quit, exit, or ^D to exit." << endl; cerr << endl; cerr << "Any TaQL command can be used. If no command name is given, SELECT is assumed." << endl; cerr << "For example:" << endl; cerr << " date() + 107 #which date is 107 days after today" << endl; cerr << " select from my.ms where ANTENNA1=1 giving sel.ms" << endl; cerr << "The result of a CALC command will be printed." << endl; cerr << "The number of resulting rows and the values of possible selected" << endl; cerr << "columns can be printed (see options below)." << endl; cerr << endl; cerr << "It is possible to save the table resulting from a selection" << endl; cerr << "by assigning it like:" << endl; cerr << " var = taqlcommand" << endl; cerr << "Thereafter $var can be used as a table in another TaQL command like:" << endl; cerr << " t1 = select from my.ms where ANTENNA1=1" << endl; cerr << " t2 = select from $t1 where ANTENNA2=2" << endl; cerr << "A variable name followed by zero or more question marks gives info about" << endl; cerr << "the table. More info for more question marks (e.g. t1?)." << endl; cerr << " var =" << endl; cerr << "clears 'var' (removes it from the saved selections)." << endl; cerr << "Use command ? to show all saved selections." << endl; cerr << endl; cerr << "The 'show' command gives TaQL help; subcommands give more detailed help." << endl; cerr << endl; cerr << "taql can be started with a few options:" << endl; cerr << " -s or --style defines the TaQL style." << endl; cerr << " The default style is python; if no value is given after -s it defaults to glish" << endl; cerr << " -h or --help show this help and exit" << endl; cerr << " -v or --version show the taql version and exit" << endl; cerr << " -f filename name of file containing TaQL commands to execute" << endl; cerr << " -d separator separator used between printed values (default a tab (\t))" << endl; cerr << " -o outputfile name of output file (or stdout or stderr)" << endl; cerr << " -ps or --printselect always show the values of selected columns" << endl; cerr << " -pa or --printauto automatically show values (up to maxNRows)" << endl; cerr << " -pm or --printmeasure if possible, show values as formatted measures" << endl; cerr << " -ph or --printheader show the header of names of the selected columns" << endl; cerr << " -pc or --printcommand show the (expanded) TaQL command" << endl; cerr << " -pr or --printnrows show the number of rows selected, updated, etc." << endl; cerr << " -p or --printall sets -ps, -pm, -ph, -pc and -pr" << endl; cerr << " -m maxNRows max nr of rows to print automatically (default 50)" << endl; cerr << "A print option can be turned off by giving, for example, -nops or --noprintall" << endl; cerr << "The default is -nops, -pa, -pm, -ph, -nopc, -pr, -m 50, -o stdout" << endl; cerr << "Note that the result of CALC and an implicit SELECT is always printed" << endl; cerr << ""; cerr << "These options can also be given before any TaQL command. If followed by a command," << endl; cerr << "the setting is used for that command only, otherwise it is permanent." << endl; cerr << endl; } void showOptions (const Options& options) { const char* opts[] = {"--no", "--"}; cerr << endl; cerr << "Options settings:" << endl; cerr << ' ' << opts[options.printSelect] << "printselect" << endl; cerr << ' ' << opts[options.printAuto] << "printauto" << endl; cerr << ' ' << opts[options.printMeasure] << "printmeasure" << endl; cerr << ' ' << opts[options.printHeader] << "printheader" << endl; cerr << ' ' << opts[options.printCommand] << "printcommand" << endl; cerr << ' ' << opts[options.printNRows] << "printnrows" << endl; cerr << " -m " << options.maxNRows << " (max nr of rows to print automatically)" << endl; cerr << " -d " << "'" << options.separator << "' (separator between printed columns)" << endl; cerr << " -o " << "'" << options.outName << "' (output filename)" << endl; cerr << " --style " << "'" << options.style << "'" << endl; cerr << endl; } void showTableInfo (const String& name, const Table& tab, const String& command, Int level, ostream& os) { TableDesc tdesc(tab.actualTableDesc()); os << " " << name << " resulted from:"; if (level >= 0) { os << endl; os << " "; } os << ' ' << command << endl; if (level >= 0) { os << " " << tab.nrow() << " rows, " << tdesc.ncolumn() << " columns" << endl; } if (level > 0) { Vector colNames = tdesc.columnNames(); os << " " << colNames << endl; if (level > 1) { genSort (colNames); uInt maxLen = 0; for (uInt i=0; i maxLen) { maxLen = colNames[i].size(); } } for (uInt i=0; i 0) { os << " ndim=" << cdesc.ndim(); } if (! cdesc.shape().empty()) { os << " shape=" << cdesc.shape(); } } if (cdesc.comment().empty()) { os << " " << cdesc.comment(); } os << endl; } } } } // Show the variable names and tables associated to them. void showTableMap (const TableMap& tables, ostream& os) { if (tables.empty()) { os << " no saved selections; note: use h to get help info" << endl; } else { for (TableMap::const_iterator iter = tables.begin(); iter != tables.end(); ++iter) { showTableInfo (iter->first, iter->second.first, iter->second.second, -1, os); } } } // Substitute possible table variables given like $var. String substituteName (const String& name, const TableMap& tableMap, vector& tabs) { TableMap::const_iterator fnd = tableMap.find(name); if (fnd == tableMap.end()) { return name; // not found } tabs.push_back (&(fnd->second.first)); return String::toString (tabs.size()); // return seqnr as string } vector replaceVars (String& str, const TableMap& tableMap) { vector tabs; // Initialize some variables. Bool backslash = False; Bool dollar = False; Bool squote = False; Bool dquote = False; String name; String out; out.reserve (str.size()); // Loop through the entire string. for (uInt i=0; i='a' && tmp<='z') || (tmp>='A' && tmp<='Z')) { name += tmp; continue; } else if (tmp>='0' && tmp<='9' && !name.empty()) { // Numerics are only part if not first character. name += tmp; continue; } else { // End of name found. Try to substitute. dollar = False; out += substituteName(name, tableMap, tabs); } } // Handle possible single or double quotes. if (tmp == '"' && !squote) { dquote = !dquote; } else if (tmp == '\'' && !dquote) { squote = !squote; } else if (!dquote && !squote) { // Set a switch if we have a dollar (outside quoted) // that is not preceeded by a backslash. if (tmp == '$' && !backslash) { dollar = True; name = String(); } } // Set a switch if we have a backslash (not preceeded by a backslash). backslash = (!backslash && tmp == '\\'); // Add the character to output. out += tmp; } // The entire string has been handled. // Substitute a possible last name. // Insert a possible incomplete eval string as such. if (dollar) { out += substituteName(name, tableMap, tabs); } str = out; return tabs; } Bool execCommand (const String& command, TableMap& tableMap, const Options& options) { Regex varassRE("^[a-zA-Z_][a-zA-Z0-9_]*[ \t]*="); Regex assRE("[ \t]*="); Regex lwhiteRE("^[ \t]*"); Regex rwhiteRE("[ \t]*$"); try { ostream& os = *(options.stream); String strc(command); if (strc == "h") { showHelp(); } else if (strc == "v") { showVersion(); } else if (strc == "o") { showOptions (options); } else if (strc == "?") { showTableMap (tableMap, os); } else if (strc == "exit" || strc == "quit" || strc == "q") { return False; } else { String varName; String::size_type assLen = varassRE.match (strc.c_str(), strc.size()); if (assLen != String::npos) { // Assignment to variable; get its name and remove from command. varName = strc.before(assLen); strc = strc.from(assLen); varName.del (assRE); if (varName.empty()) { throw AipsError ("Variable name before =command is empty"); } } strc.del (lwhiteRE); if (strc.empty()) { // No command means that the variable will be removed. tableMap.erase (varName); } else { // No assignment, so it is a name or a command. // First try it as a name. // A name can be followed by question marks giving the level of // info to be printed. Int sz = strc.size(); while (sz > 0 && strc[sz-1] == '?') { --sz; } Int level = strc.size() - sz; String name = strc.substr(0, sz); name.del (rwhiteRE); TableMap::const_iterator it = tableMap.find (name); if (it != tableMap.end()) { // It exists, so it must be a name. showTableInfo (name, it->second.first, it->second.second, level, os); } else { // No name, so it must be a command. vector tabs = replaceVars (strc, tableMap); Table tab = taqlCommand (options, varName, strc, tabs); if (!varName.empty() && !tab.isNull()) { // Keep the resulting table if a variable was given. tableMap[varName] = make_pair(tab, command); } } } } } catch (const TableParseError& x) { showParseError (x); } catch (const std::exception& x) { cerr << x.what() << endl; } return True; } void execFileCommands (TableMap& tableMap, const Options& options) { // Reads all commands from the file and split them at ;. // A command can be continued on the next line. // An error in a command file is severe, so it exits on error. vector commands; Bool appendLast = False; std::ifstream ifs(options.fname.c_str()); if (! ifs.good()) { throw AipsError("Cannot open file " + options.fname); } String line; getline (ifs, line); while (ifs.good()) { removeCR (line); vector parts = splitLine(line); for (size_t i=0; i ") && executeArgs (splitWS(str), False, tableMap, options))) { cerr << endl; break; } } catch (const std::exception& x) { cout << x.what() << endl; } } #ifdef HAVE_READLINE if (! histFile.empty()) { write_history (histFile.c_str()); } #endif } // Remove quotes if necessary. // Note that it should not be done for options given in a shell command, // but has to be done for options given at the TaQL prompt. String removeQuotes (const String& s, Bool removeQuote) { if (removeQuote && s.size() >= 2) { if ((s[0] == '"' || s[0] == '\'') && s[0] == s[s.size()-1]) { return s.substr (1, s.size()-2); } } return s; } // Parse the given options and set flags accordingly. // Stop at first non-option (indicated by st). Bool parseArgs (const vector& args, uInt& st, Options& options, Bool removeQuote) { options.fname = String(); for (st=0; st 0 && options.style[0] == '-') { // no style value, thus ignore. options.style = String(); } else { // use style value. st++; } } } else if (arg == "-d") { if (st < args.size()-1) { st++; options.separator = removeQuotes (args[st], removeQuote); } else { throw AipsError("No separator given after -d"); } } else if (arg == "-m") { if (st < args.size()-1) { st++; options.maxNRows = atol(args[st].c_str()); } else { throw AipsError("No value given after -m"); } } else if (arg == "-f") { if (st < args.size()-1) { st++; // Expand possible ~ and env.vars in file name. options.fname = Path(removeQuotes (args[st], removeQuote)).absoluteName(); } else { throw AipsError("No file name given after -f"); } } else if (arg == "-o") { if (st < args.size()-1) { st++; String fname = removeQuotes (args[st], removeQuote); String outname(fname); outname.downcase(); if (outname == "stdout") { options.stream = std::shared_ptr(&cout, Deleter(False)); options.outName = "stdout"; } else if (outname == "stderr") { options.stream = std::shared_ptr(&cerr, Deleter(False)); options.outName = "stderr"; } else { try { outname = Path(fname).absoluteName(); options.stream = std::shared_ptr (new ofstream(outname), Deleter(True)); options.outName = outname; } catch (std::exception& x) { cerr << "Could not create output file " << fname << endl; cerr << " " << x.what() << endl; } } } else { throw AipsError("No output file name given after -o"); } } else if (arg == "-p" || arg == "--printall") { options.printCommand = True; options.printSelect = True; options.printMeasure = True; options.printNRows = True; options.printHeader = True; } else if (arg == "-nop" || arg == "--noprintall") { options.printCommand = False; options.printSelect = False; options.printMeasure = False; options.printNRows = False; options.printHeader = False; } else if (arg == "-pc" || arg == "--printcommand") { options.printCommand = True; } else if (arg == "-ps" || arg == "--printselect") { options.printSelect = True; } else if (arg == "-pa" || arg == "--printauto") { options.printAuto = True; options.printSelect = False; } else if (arg == "-pm" || arg == "--printmeasure") { options.printMeasure = True; } else if (arg == "-pr" || arg == "--printnrows") { options.printNRows = True; } else if (arg == "-ph" || arg == "--printheader") { options.printHeader = True; } else if (arg == "-nopc" || arg == "--noprintcommand") { options.printCommand = False; } else if (arg == "-nops" || arg == "--noprintselect") { options.printSelect = False; } else if (arg == "-nopa" || arg == "--noprintauto") { options.printAuto = False; } else if (arg == "-nopm" || arg == "--noprintmeasure") { options.printMeasure = False; } else if (arg == "-nopr" || arg == "--noprintnrows") { options.printNRows = False; } else if (arg == "-noph" || arg == "--noprintheader") { options.printHeader = False; } else if (arg == "-v" || arg == "--version") { showVersion(); return False; } else if (arg == "-h" || arg == "--help") { showHelp(); return False; } else if (arg == "--") { break; // -- signifies end of options } else if (arg[0] == '-') { cerr << arg << " is an invalid option; it will be tried as a command" << endl; cerr << " Note that -- indicates the end of options (e.g., use -- " << arg << ')' << endl; return True; } else { break; } } return True; } // Execute a given command. // Ask for commands if interactive and empty command. Bool executeArgs (const vector args, Bool topLevel, TableMap& tableMap, Options& options) { // Parse the options as given. Options localOptions(options); uInt st = 0; if (! parseArgs (args, st, localOptions, !topLevel)) { return True; } // Execute the command file if given. if (! localOptions.fname.empty()) { execFileCommands (tableMap, localOptions); } else if (st < args.size()) { // A command can be given as multiple parameters to make tab-completion // easier. Thus combine it all. String command(args[st]); while (++st < args.size()) { command += ' ' + String(args[st]); } // Execute the given command. return execCommand (command, tableMap, localOptions); } else if (topLevel) { // Ask the user for commands. cerr << "Using default TaQL style " << options.style << endl; askCommands (tableMap, localOptions); } else { // Make the options persistent. options = localOptions; } return True; } int main (int argc, const char* argv[]) { try { // Set default state options. TableMap tableMap; Options options; vector args; for (int i=1; i #include #include #include #include #include #include #include #include using namespace casacore; using namespace std; int main (int argc, char* argv[]) { try { vector fname; String outName; Int64 blockSize = 1048576; Bool useHDF5 = False; for (int argnr=1; argnr mfile; if (useHDF5) { mfile.reset (new MultiHDF5 (outName, ByteIO::New, blockSize)); } else { mfile.reset (new MultiFile (outName, ByteIO::New, blockSize)); } Block buffer (blockSize); for (vector::const_iterator iter=fname.begin(); iter!=fname.end(); ++iter) { if (iter->empty()) { cerr << "*** Empty file name given" << endl; } else { int fd = RegularFileIO::openCreate (*iter, ByteIO::Old); FiledesIO file(fd, *iter); Int64 todo = file.length(); cout << " copying " << todo << " bytes of " << *iter << " ..." << endl; MFFileIO outfile (mfile, *iter, ByteIO::New); while (todo > 0) { Int64 sz = file.read (std::min(todo, blockSize), buffer.storage()); outfile.write (sz, buffer.storage()); todo -= sz; } } } cout << endl; } catch (const std::exception& x) { cerr << x.what() << endl; return 1; } return 0; } casacore-3.7.1/tables/tables.dox000066400000000000000000000035401476623553700165660ustar00rootroot00000000000000//# tables.dox: doxygen description of tables package //# Copyright (C) 2005 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library 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 Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: casa-feedback@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ namespace casacore { // \defgroup tables tables package (libcasa_tables) // // The tables package contains the core modules for the Casacore Table System //
          //
        • Tables: // data base like tables supporting N-dim arrays. //
        • DataMan: // data managers for storage or on-the-fly calculation of the logically organized table data. //
        • TaQL: // an advanced SQL-like query language for the tables. //
        • LogTables: // store log messages in tables. //
        }